summaryrefslogtreecommitdiff
path: root/source4
diff options
context:
space:
mode:
authorbubulle <bubulle@alioth.debian.org>2009-07-06 18:17:09 +0000
committerbubulle <bubulle@alioth.debian.org>2009-07-06 18:17:09 +0000
commit1e4514a1edfdd1dde65a7567a7d7328de40e3493 (patch)
tree889204356442a0e13d8b3b2deaa1e0b310cea940 /source4
parent956e238a6ed95a84c85c14ac898ffee166c35287 (diff)
downloadsamba-1e4514a1edfdd1dde65a7567a7d7328de40e3493.tar.gz
merge upstream 3.4.0
git-svn-id: svn://svn.debian.org/svn/pkg-samba/trunk/samba@2936 fc4039ab-9d04-0410-8cac-899223bdd6b0
Diffstat (limited to 'source4')
-rw-r--r--source4/.valgrind_suppressions85
-rw-r--r--source4/BUGS24
-rw-r--r--source4/Makefile309
-rw-r--r--source4/NEWS503
-rw-r--r--source4/TODO278
-rw-r--r--source4/VERSION117
-rw-r--r--source4/aclocal.m465
-rw-r--r--source4/auth/auth.h291
-rw-r--r--source4/auth/auth_sam_reply.c288
-rw-r--r--source4/auth/config.m434
-rw-r--r--source4/auth/config.mk50
-rw-r--r--source4/auth/credentials/config.mk20
-rw-r--r--source4/auth/credentials/credentials.c761
-rw-r--r--source4/auth/credentials/credentials.h272
-rw-r--r--source4/auth/credentials/credentials_files.c433
-rw-r--r--source4/auth/credentials/credentials_krb5.c762
-rw-r--r--source4/auth/credentials/credentials_krb5.h46
-rw-r--r--source4/auth/credentials/credentials_ntlm.c269
-rw-r--r--source4/auth/credentials/pycredentials.c316
-rw-r--r--source4/auth/credentials/pycredentials.h30
-rw-r--r--source4/auth/credentials/tests/bindings.py98
-rw-r--r--source4/auth/credentials/tests/simple.c120
-rw-r--r--source4/auth/gensec/config.m42
-rw-r--r--source4/auth/gensec/config.mk87
-rw-r--r--source4/auth/gensec/cyrus_sasl.c432
-rw-r--r--source4/auth/gensec/gensec.c1331
-rw-r--r--source4/auth/gensec/gensec.h318
-rw-r--r--source4/auth/gensec/gensec.pc.in11
-rw-r--r--source4/auth/gensec/gensec_gssapi.c1525
-rw-r--r--source4/auth/gensec/gensec_gssapi.h68
-rw-r--r--source4/auth/gensec/gensec_krb5.c826
-rw-r--r--source4/auth/gensec/schannel.c291
-rw-r--r--source4/auth/gensec/schannel.h36
-rw-r--r--source4/auth/gensec/schannel_sign.c285
-rw-r--r--source4/auth/gensec/schannel_state.c349
-rw-r--r--source4/auth/gensec/socket.c534
-rw-r--r--source4/auth/gensec/spnego.c1172
-rw-r--r--source4/auth/gensec/spnego.h65
-rw-r--r--source4/auth/gensec/spnego_parse.c408
-rw-r--r--source4/auth/kerberos/clikrb5.c113
-rw-r--r--source4/auth/kerberos/config.m4540
-rw-r--r--source4/auth/kerberos/config.mk18
-rw-r--r--source4/auth/kerberos/gssapi_parse.c125
-rw-r--r--source4/auth/kerberos/kerberos-notes.txt466
-rw-r--r--source4/auth/kerberos/kerberos.c123
-rw-r--r--source4/auth/kerberos/kerberos.h153
-rw-r--r--source4/auth/kerberos/kerberos_heimdal.c102
-rw-r--r--source4/auth/kerberos/kerberos_pac.c776
-rw-r--r--source4/auth/kerberos/kerberos_util.c679
-rw-r--r--source4/auth/kerberos/krb5_init_context.c477
-rw-r--r--source4/auth/kerberos/krb5_init_context.h37
-rw-r--r--source4/auth/ntlm/auth.c576
-rw-r--r--source4/auth/ntlm/auth_anonymous.c78
-rw-r--r--source4/auth/ntlm/auth_developer.c207
-rw-r--r--source4/auth/ntlm/auth_proto.h50
-rw-r--r--source4/auth/ntlm/auth_sam.c500
-rw-r--r--source4/auth/ntlm/auth_server.c234
-rw-r--r--source4/auth/ntlm/auth_simple.c103
-rw-r--r--source4/auth/ntlm/auth_unix.c844
-rw-r--r--source4/auth/ntlm/auth_util.c261
-rw-r--r--source4/auth/ntlm/auth_winbind.c282
-rw-r--r--source4/auth/ntlm/config.mk85
-rw-r--r--source4/auth/ntlm/ntlm_check.c603
-rw-r--r--source4/auth/ntlm/ntlm_check.h76
-rw-r--r--source4/auth/ntlm/pam_errors.c126
-rw-r--r--source4/auth/ntlm/pam_errors.h34
-rw-r--r--source4/auth/ntlmssp/config.mk19
-rw-r--r--source4/auth/ntlmssp/ntlmssp.c445
-rw-r--r--source4/auth/ntlmssp/ntlmssp.h190
-rw-r--r--source4/auth/ntlmssp/ntlmssp_client.c379
-rw-r--r--source4/auth/ntlmssp/ntlmssp_parse.c368
-rw-r--r--source4/auth/ntlmssp/ntlmssp_server.c812
-rw-r--r--source4/auth/ntlmssp/ntlmssp_sign.c545
-rw-r--r--source4/auth/pyauth.c92
-rw-r--r--source4/auth/pyauth.h32
-rw-r--r--source4/auth/sam.c430
-rw-r--r--source4/auth/samba_server_gensec.c73
-rw-r--r--source4/auth/session.c203
-rw-r--r--source4/auth/session.h66
-rw-r--r--source4/auth/system_session.c305
-rw-r--r--source4/auth/tests/bindings.py34
-rwxr-xr-xsource4/autogen.sh79
-rw-r--r--source4/build/m4/ax_cflags_gcc_option.m4109
-rw-r--r--source4/build/m4/ax_cflags_irix_option.m4174
-rw-r--r--source4/build/m4/check_cc.m4160
-rw-r--r--source4/build/m4/check_doc.m41
-rw-r--r--source4/build/m4/check_ld.m4187
-rw-r--r--source4/build/m4/check_path.m4217
-rw-r--r--source4/build/m4/env.m490
-rw-r--r--source4/build/m4/public.m4277
-rwxr-xr-xsource4/build/make/lex_compile.sh60
-rw-r--r--source4/build/make/python.mk54
-rw-r--r--source4/build/make/rules.mk191
-rw-r--r--source4/build/make/templates.mk141
-rwxr-xr-xsource4/build/make/yacc_compile.sh45
-rw-r--r--source4/build/pasn1/Makefile5
-rw-r--r--source4/build/pasn1/asn1.yp306
-rwxr-xr-xsource4/build/pasn1/pasn1.pl93
-rw-r--r--source4/build/pasn1/util.pm379
-rw-r--r--source4/build/smb_build/README.txt83
-rw-r--r--source4/build/smb_build/TODO25
-rw-r--r--source4/build/smb_build/config_mk.pm284
-rwxr-xr-xsource4/build/smb_build/dot.pl63
-rw-r--r--source4/build/smb_build/input.pm277
-rw-r--r--source4/build/smb_build/main.pl104
-rw-r--r--source4/build/smb_build/makefile.pm281
-rw-r--r--source4/build/smb_build/output.pm172
-rw-r--r--source4/build/smb_build/summary.pm80
-rw-r--r--source4/cldap_server/cldap_server.c214
-rw-r--r--source4/cldap_server/cldap_server.h34
-rw-r--r--source4/cldap_server/config.mk18
-rw-r--r--source4/cldap_server/netlogon.c501
-rw-r--r--source4/cldap_server/rootdse.c181
-rw-r--r--source4/client/cifsdd.c629
-rw-r--r--source4/client/cifsdd.h110
-rw-r--r--source4/client/cifsddio.c530
-rw-r--r--source4/client/client.c3305
-rw-r--r--source4/client/config.mk36
-rw-r--r--source4/client/mount.cifs.c559
-rw-r--r--source4/client/smbmnt.c306
-rw-r--r--source4/client/smbmount.c942
-rw-r--r--source4/client/smbumount.c186
-rwxr-xr-xsource4/client/tests/test_cifsdd.sh73
-rwxr-xr-xsource4/client/tests/test_smbclient.sh160
-rw-r--r--source4/client/tree.c809
-rw-r--r--source4/cluster/cluster.c97
-rw-r--r--source4/cluster/cluster.h50
-rw-r--r--source4/cluster/cluster_private.h45
-rw-r--r--source4/cluster/config.mk4
-rw-r--r--source4/cluster/local.c113
-rwxr-xr-xsource4/config.guess1464
-rwxr-xr-xsource4/config.sub1577
-rw-r--r--source4/configure.ac199
-rwxr-xr-xsource4/configure.developer6
-rwxr-xr-xsource4/configure.nodebug.developer3
-rwxr-xr-xsource4/configure.tridge.opt2
-rw-r--r--source4/dsdb/common/flag_mapping.c145
-rw-r--r--source4/dsdb/common/flags.h137
-rw-r--r--source4/dsdb/common/sidmap.c612
-rw-r--r--source4/dsdb/common/util.c2036
-rw-r--r--source4/dsdb/config.mk63
-rw-r--r--source4/dsdb/repl/drepl_out_helpers.c444
-rw-r--r--source4/dsdb/repl/drepl_out_helpers.h26
-rw-r--r--source4/dsdb/repl/drepl_out_pull.c154
-rw-r--r--source4/dsdb/repl/drepl_partitions.c270
-rw-r--r--source4/dsdb/repl/drepl_periodic.c109
-rw-r--r--source4/dsdb/repl/drepl_service.c189
-rw-r--r--source4/dsdb/repl/drepl_service.h175
-rw-r--r--source4/dsdb/repl/replicated_objects.c417
-rw-r--r--source4/dsdb/samdb/cracknames.c1326
-rw-r--r--source4/dsdb/samdb/ldb_modules/anr.c370
-rw-r--r--source4/dsdb/samdb/ldb_modules/config.mk325
-rw-r--r--source4/dsdb/samdb/ldb_modules/dsdb_cache.c42
-rw-r--r--source4/dsdb/samdb/ldb_modules/extended_dn_in.c394
-rw-r--r--source4/dsdb/samdb/ldb_modules/extended_dn_out.c655
-rw-r--r--source4/dsdb/samdb/ldb_modules/extended_dn_store.c431
-rw-r--r--source4/dsdb/samdb/ldb_modules/instancetype.c154
-rw-r--r--source4/dsdb/samdb/ldb_modules/kludge_acl.c535
-rw-r--r--source4/dsdb/samdb/ldb_modules/linked_attributes.c1105
-rw-r--r--source4/dsdb/samdb/ldb_modules/local_password.c1098
-rw-r--r--source4/dsdb/samdb/ldb_modules/naming_fsmo.c122
-rw-r--r--source4/dsdb/samdb/ldb_modules/objectclass.c1073
-rw-r--r--source4/dsdb/samdb/ldb_modules/objectguid.c285
-rw-r--r--source4/dsdb/samdb/ldb_modules/partition.c1345
-rw-r--r--source4/dsdb/samdb/ldb_modules/password_hash.c2257
-rw-r--r--source4/dsdb/samdb/ldb_modules/password_modules.h3
-rw-r--r--source4/dsdb/samdb/ldb_modules/pdc_fsmo.c120
-rw-r--r--source4/dsdb/samdb/ldb_modules/proxy.c403
-rw-r--r--source4/dsdb/samdb/ldb_modules/ranged_results.c252
-rw-r--r--source4/dsdb/samdb/ldb_modules/repl_meta_data.c1466
-rw-r--r--source4/dsdb/samdb/ldb_modules/rootdse.c466
-rw-r--r--source4/dsdb/samdb/ldb_modules/samba3sam.c933
-rw-r--r--source4/dsdb/samdb/ldb_modules/samldb.c1416
-rw-r--r--source4/dsdb/samdb/ldb_modules/schema_fsmo.c499
-rw-r--r--source4/dsdb/samdb/ldb_modules/show_deleted.c159
-rw-r--r--source4/dsdb/samdb/ldb_modules/simple_ldap_map.c719
-rw-r--r--source4/dsdb/samdb/ldb_modules/subtree_delete.c162
-rw-r--r--source4/dsdb/samdb/ldb_modules/subtree_rename.c268
-rw-r--r--source4/dsdb/samdb/ldb_modules/tests/samba3sam.py1086
-rw-r--r--source4/dsdb/samdb/ldb_modules/update_keytab.c447
-rw-r--r--source4/dsdb/samdb/samdb.c296
-rw-r--r--source4/dsdb/samdb/samdb.h130
-rw-r--r--source4/dsdb/samdb/samdb_privilege.c121
-rw-r--r--source4/dsdb/schema/schema.h185
-rw-r--r--source4/dsdb/schema/schema_description.c414
-rw-r--r--source4/dsdb/schema/schema_init.c1471
-rw-r--r--source4/dsdb/schema/schema_query.c344
-rw-r--r--source4/dsdb/schema/schema_set.c407
-rw-r--r--source4/dsdb/schema/schema_syntax.c1538
-rw-r--r--source4/dynconfig/config.mk25
-rw-r--r--source4/dynconfig/dynconfig.c89
-rw-r--r--source4/dynconfig/dynconfig.h42
-rw-r--r--source4/headermap.txt108
-rw-r--r--source4/heimdal/HEIMDAL-LICENCE.txt45
-rw-r--r--source4/heimdal/README19
-rw-r--r--source4/heimdal/cf/check-var.m427
-rw-r--r--source4/heimdal/cf/find-func-no-libs.m49
-rw-r--r--source4/heimdal/cf/find-func-no-libs2.m463
-rw-r--r--source4/heimdal/cf/find-func.m49
-rw-r--r--source4/heimdal/cf/make-proto.pl351
-rw-r--r--source4/heimdal/cf/resolv.m4109
-rw-r--r--source4/heimdal/kdc/524.c400
-rw-r--r--source4/heimdal/kdc/default_config.c285
-rw-r--r--source4/heimdal/kdc/digest.c1458
-rw-r--r--source4/heimdal/kdc/headers.h109
-rw-r--r--source4/heimdal/kdc/kaserver.c951
-rw-r--r--source4/heimdal/kdc/kdc.h96
-rw-r--r--source4/heimdal/kdc/kdc_locl.h72
-rw-r--r--source4/heimdal/kdc/kerberos4.c794
-rw-r--r--source4/heimdal/kdc/kerberos5.c1871
-rw-r--r--source4/heimdal/kdc/krb5tgs.c2087
-rw-r--r--source4/heimdal/kdc/kx509.c460
-rw-r--r--source4/heimdal/kdc/log.c93
-rw-r--r--source4/heimdal/kdc/misc.c123
-rw-r--r--source4/heimdal/kdc/pkinit.c1665
-rw-r--r--source4/heimdal/kdc/process.c220
-rw-r--r--source4/heimdal/kdc/rx.h79
-rw-r--r--source4/heimdal/kdc/windc.c110
-rw-r--r--source4/heimdal/kdc/windc_plugin.h82
-rw-r--r--source4/heimdal/kpasswd/kpasswd.c247
-rw-r--r--source4/heimdal/kpasswd/kpasswd_locl.h104
-rw-r--r--source4/heimdal/kuser/kinit.c901
-rw-r--r--source4/heimdal/kuser/kuser_locl.h105
-rw-r--r--source4/heimdal/lib/asn1/CMS.asn1157
-rw-r--r--source4/heimdal/lib/asn1/asn1-common.h66
-rw-r--r--source4/heimdal/lib/asn1/asn1_err.et25
-rw-r--r--source4/heimdal/lib/asn1/asn1_gen.c187
-rw-r--r--source4/heimdal/lib/asn1/asn1_queue.h167
-rw-r--r--source4/heimdal/lib/asn1/canthandle.asn134
-rw-r--r--source4/heimdal/lib/asn1/der.c142
-rw-r--r--source4/heimdal/lib/asn1/der.h103
-rw-r--r--source4/heimdal/lib/asn1/der_cmp.c102
-rw-r--r--source4/heimdal/lib/asn1/der_copy.c145
-rw-r--r--source4/heimdal/lib/asn1/der_format.c170
-rw-r--r--source4/heimdal/lib/asn1/der_free.c119
-rw-r--r--source4/heimdal/lib/asn1/der_get.c546
-rw-r--r--source4/heimdal/lib/asn1/der_length.c232
-rw-r--r--source4/heimdal/lib/asn1/der_locl.h60
-rw-r--r--source4/heimdal/lib/asn1/der_put.c483
-rw-r--r--source4/heimdal/lib/asn1/digest.asn1164
-rw-r--r--source4/heimdal/lib/asn1/extra.c155
-rw-r--r--source4/heimdal/lib/asn1/gen.c802
-rw-r--r--source4/heimdal/lib/asn1/gen_copy.c249
-rw-r--r--source4/heimdal/lib/asn1/gen_decode.c720
-rw-r--r--source4/heimdal/lib/asn1/gen_encode.c557
-rw-r--r--source4/heimdal/lib/asn1/gen_free.c194
-rw-r--r--source4/heimdal/lib/asn1/gen_glue.c140
-rw-r--r--source4/heimdal/lib/asn1/gen_length.c283
-rw-r--r--source4/heimdal/lib/asn1/gen_locl.h89
-rw-r--r--source4/heimdal/lib/asn1/gen_seq.c119
-rw-r--r--source4/heimdal/lib/asn1/hash.c206
-rw-r--r--source4/heimdal/lib/asn1/hash.h87
-rw-r--r--source4/heimdal/lib/asn1/heim_asn1.h52
-rw-r--r--source4/heimdal/lib/asn1/k5.asn1671
-rw-r--r--source4/heimdal/lib/asn1/kx509.asn120
-rw-r--r--source4/heimdal/lib/asn1/lex.c2709
-rw-r--r--source4/heimdal/lib/asn1/lex.h42
-rw-r--r--source4/heimdal/lib/asn1/lex.l300
-rw-r--r--source4/heimdal/lib/asn1/main.c133
-rw-r--r--source4/heimdal/lib/asn1/parse.c2831
-rw-r--r--source4/heimdal/lib/asn1/parse.h249
-rw-r--r--source4/heimdal/lib/asn1/parse.y1015
-rw-r--r--source4/heimdal/lib/asn1/pkcs12.asn181
-rw-r--r--source4/heimdal/lib/asn1/pkcs8.asn130
-rw-r--r--source4/heimdal/lib/asn1/pkcs9.asn128
-rw-r--r--source4/heimdal/lib/asn1/pkinit.asn1195
-rw-r--r--source4/heimdal/lib/asn1/rfc2459.asn1506
-rw-r--r--source4/heimdal/lib/asn1/symbol.c110
-rw-r--r--source4/heimdal/lib/asn1/symbol.h161
-rw-r--r--source4/heimdal/lib/asn1/test.asn195
-rw-r--r--source4/heimdal/lib/asn1/test.gen14
-rw-r--r--source4/heimdal/lib/asn1/timegm.c86
-rw-r--r--source4/heimdal/lib/com_err/com_err.c172
-rw-r--r--source4/heimdal/lib/com_err/com_err.h66
-rw-r--r--source4/heimdal/lib/com_err/com_right.h58
-rw-r--r--source4/heimdal/lib/com_err/compile_et.c241
-rw-r--r--source4/heimdal/lib/com_err/compile_et.h80
-rw-r--r--source4/heimdal/lib/com_err/error.c104
-rw-r--r--source4/heimdal/lib/com_err/lex.c1912
-rw-r--r--source4/heimdal/lib/com_err/lex.h39
-rw-r--r--source4/heimdal/lib/com_err/lex.l128
-rw-r--r--source4/heimdal/lib/com_err/parse.c1716
-rw-r--r--source4/heimdal/lib/com_err/parse.h81
-rw-r--r--source4/heimdal/lib/com_err/parse.y173
-rw-r--r--source4/heimdal/lib/gssapi/gssapi/gssapi.h815
-rw-r--r--source4/heimdal/lib/gssapi/gssapi/gssapi_krb5.h253
-rw-r--r--source4/heimdal/lib/gssapi/gssapi/gssapi_spnego.h58
-rw-r--r--source4/heimdal/lib/gssapi/gssapi_mech.h361
-rw-r--r--source4/heimdal/lib/gssapi/krb5/8003.c248
-rw-r--r--source4/heimdal/lib/gssapi/krb5/accept_sec_context.c888
-rw-r--r--source4/heimdal/lib/gssapi/krb5/acquire_cred.c402
-rw-r--r--source4/heimdal/lib/gssapi/krb5/add_cred.c252
-rw-r--r--source4/heimdal/lib/gssapi/krb5/address_to_krb5addr.c77
-rw-r--r--source4/heimdal/lib/gssapi/krb5/arcfour.c760
-rw-r--r--source4/heimdal/lib/gssapi/krb5/canonicalize_name.c60
-rwxr-xr-xsource4/heimdal/lib/gssapi/krb5/cfx.c905
-rw-r--r--source4/heimdal/lib/gssapi/krb5/cfx.h65
-rw-r--r--source4/heimdal/lib/gssapi/krb5/compare_name.c55
-rw-r--r--source4/heimdal/lib/gssapi/krb5/compat.c128
-rw-r--r--source4/heimdal/lib/gssapi/krb5/context_time.c95
-rw-r--r--source4/heimdal/lib/gssapi/krb5/copy_ccache.c195
-rw-r--r--source4/heimdal/lib/gssapi/krb5/decapsulate.c209
-rw-r--r--source4/heimdal/lib/gssapi/krb5/delete_sec_context.c83
-rw-r--r--source4/heimdal/lib/gssapi/krb5/display_name.c74
-rw-r--r--source4/heimdal/lib/gssapi/krb5/display_status.c199
-rw-r--r--source4/heimdal/lib/gssapi/krb5/duplicate_name.c60
-rw-r--r--source4/heimdal/lib/gssapi/krb5/encapsulate.c155
-rw-r--r--source4/heimdal/lib/gssapi/krb5/export_name.c94
-rw-r--r--source4/heimdal/lib/gssapi/krb5/export_sec_context.c240
-rw-r--r--source4/heimdal/lib/gssapi/krb5/external.c479
-rw-r--r--source4/heimdal/lib/gssapi/krb5/get_mic.c317
-rw-r--r--source4/heimdal/lib/gssapi/krb5/gkrb5_err.et31
-rw-r--r--source4/heimdal/lib/gssapi/krb5/gsskrb5_locl.h143
-rw-r--r--source4/heimdal/lib/gssapi/krb5/import_name.c252
-rw-r--r--source4/heimdal/lib/gssapi/krb5/import_sec_context.c229
-rw-r--r--source4/heimdal/lib/gssapi/krb5/indicate_mechs.c57
-rw-r--r--source4/heimdal/lib/gssapi/krb5/init.c83
-rw-r--r--source4/heimdal/lib/gssapi/krb5/init_sec_context.c972
-rw-r--r--source4/heimdal/lib/gssapi/krb5/inquire_context.c112
-rw-r--r--source4/heimdal/lib/gssapi/krb5/inquire_cred.c182
-rw-r--r--source4/heimdal/lib/gssapi/krb5/inquire_cred_by_mech.c76
-rw-r--r--source4/heimdal/lib/gssapi/krb5/inquire_cred_by_oid.c83
-rw-r--r--source4/heimdal/lib/gssapi/krb5/inquire_mechs_for_name.c57
-rw-r--r--source4/heimdal/lib/gssapi/krb5/inquire_names_for_mech.c80
-rw-r--r--source4/heimdal/lib/gssapi/krb5/inquire_sec_context_by_oid.c561
-rw-r--r--source4/heimdal/lib/gssapi/krb5/prf.c143
-rw-r--r--source4/heimdal/lib/gssapi/krb5/process_context_token.c70
-rw-r--r--source4/heimdal/lib/gssapi/krb5/release_buffer.c48
-rw-r--r--source4/heimdal/lib/gssapi/krb5/release_cred.c80
-rw-r--r--source4/heimdal/lib/gssapi/krb5/release_name.c55
-rw-r--r--source4/heimdal/lib/gssapi/krb5/sequence.c294
-rw-r--r--source4/heimdal/lib/gssapi/krb5/set_cred_option.c264
-rw-r--r--source4/heimdal/lib/gssapi/krb5/set_sec_context_option.c263
-rw-r--r--source4/heimdal/lib/gssapi/krb5/unwrap.c437
-rw-r--r--source4/heimdal/lib/gssapi/krb5/verify_mic.c344
-rw-r--r--source4/heimdal/lib/gssapi/krb5/wrap.c569
-rw-r--r--source4/heimdal/lib/gssapi/mech/context.c163
-rw-r--r--source4/heimdal/lib/gssapi/mech/context.h41
-rw-r--r--source4/heimdal/lib/gssapi/mech/cred.h41
-rw-r--r--source4/heimdal/lib/gssapi/mech/gss_accept_sec_context.c294
-rw-r--r--source4/heimdal/lib/gssapi/mech/gss_acquire_cred.c169
-rw-r--r--source4/heimdal/lib/gssapi/mech/gss_add_cred.c187
-rw-r--r--source4/heimdal/lib/gssapi/mech/gss_add_oid_set_member.c67
-rw-r--r--source4/heimdal/lib/gssapi/mech/gss_buffer_set.c125
-rw-r--r--source4/heimdal/lib/gssapi/mech/gss_canonicalize_name.c88
-rw-r--r--source4/heimdal/lib/gssapi/mech/gss_compare_name.c77
-rw-r--r--source4/heimdal/lib/gssapi/mech/gss_context_time.c41
-rw-r--r--source4/heimdal/lib/gssapi/mech/gss_create_empty_oid_set.c52
-rw-r--r--source4/heimdal/lib/gssapi/mech/gss_decapsulate_token.c73
-rw-r--r--source4/heimdal/lib/gssapi/mech/gss_delete_sec_context.c58
-rw-r--r--source4/heimdal/lib/gssapi/mech/gss_display_name.c83
-rw-r--r--source4/heimdal/lib/gssapi/mech/gss_display_status.c210
-rw-r--r--source4/heimdal/lib/gssapi/mech/gss_duplicate_name.c95
-rw-r--r--source4/heimdal/lib/gssapi/mech/gss_duplicate_oid.c68
-rw-r--r--source4/heimdal/lib/gssapi/mech/gss_encapsulate_token.c67
-rw-r--r--source4/heimdal/lib/gssapi/mech/gss_export_name.c55
-rw-r--r--source4/heimdal/lib/gssapi/mech/gss_export_sec_context.c78
-rw-r--r--source4/heimdal/lib/gssapi/mech/gss_get_mic.c50
-rw-r--r--source4/heimdal/lib/gssapi/mech/gss_import_name.c218
-rw-r--r--source4/heimdal/lib/gssapi/mech/gss_import_sec_context.c83
-rw-r--r--source4/heimdal/lib/gssapi/mech/gss_indicate_mechs.c65
-rw-r--r--source4/heimdal/lib/gssapi/mech/gss_init_sec_context.c149
-rw-r--r--source4/heimdal/lib/gssapi/mech/gss_inquire_context.c106
-rw-r--r--source4/heimdal/lib/gssapi/mech/gss_inquire_cred.c196
-rw-r--r--source4/heimdal/lib/gssapi/mech/gss_inquire_cred_by_mech.c93
-rw-r--r--source4/heimdal/lib/gssapi/mech/gss_inquire_cred_by_oid.c87
-rw-r--r--source4/heimdal/lib/gssapi/mech/gss_inquire_mechs_for_name.c77
-rw-r--r--source4/heimdal/lib/gssapi/mech/gss_inquire_names_for_mech.c74
-rw-r--r--source4/heimdal/lib/gssapi/mech/gss_inquire_sec_context_by_oid.c71
-rw-r--r--source4/heimdal/lib/gssapi/mech/gss_krb5.c944
-rw-r--r--source4/heimdal/lib/gssapi/mech/gss_mech_switch.c337
-rw-r--r--source4/heimdal/lib/gssapi/mech/gss_names.c111
-rw-r--r--source4/heimdal/lib/gssapi/mech/gss_oid_equal.c45
-rw-r--r--source4/heimdal/lib/gssapi/mech/gss_oid_to_str.c68
-rw-r--r--source4/heimdal/lib/gssapi/mech/gss_process_context_token.c42
-rw-r--r--source4/heimdal/lib/gssapi/mech/gss_pseudo_random.c69
-rw-r--r--source4/heimdal/lib/gssapi/mech/gss_release_buffer.c43
-rw-r--r--source4/heimdal/lib/gssapi/mech/gss_release_cred.c52
-rw-r--r--source4/heimdal/lib/gssapi/mech/gss_release_name.c55
-rw-r--r--source4/heimdal/lib/gssapi/mech/gss_release_oid.c59
-rw-r--r--source4/heimdal/lib/gssapi/mech/gss_release_oid_set.c45
-rw-r--r--source4/heimdal/lib/gssapi/mech/gss_seal.c46
-rw-r--r--source4/heimdal/lib/gssapi/mech/gss_set_cred_option.c119
-rw-r--r--source4/heimdal/lib/gssapi/mech/gss_set_sec_context_option.c71
-rw-r--r--source4/heimdal/lib/gssapi/mech/gss_sign.c42
-rw-r--r--source4/heimdal/lib/gssapi/mech/gss_test_oid_set_member.c47
-rw-r--r--source4/heimdal/lib/gssapi/mech/gss_unseal.c44
-rw-r--r--source4/heimdal/lib/gssapi/mech/gss_unwrap.c46
-rw-r--r--source4/heimdal/lib/gssapi/mech/gss_utils.c79
-rw-r--r--source4/heimdal/lib/gssapi/mech/gss_verify.c43
-rw-r--r--source4/heimdal/lib/gssapi/mech/gss_verify_mic.c51
-rw-r--r--source4/heimdal/lib/gssapi/mech/gss_wrap.c55
-rw-r--r--source4/heimdal/lib/gssapi/mech/gss_wrap_size_limit.c51
-rw-r--r--source4/heimdal/lib/gssapi/mech/gssapi.asn112
-rw-r--r--source4/heimdal/lib/gssapi/mech/mech_locl.h66
-rw-r--r--source4/heimdal/lib/gssapi/mech/mech_switch.h42
-rw-r--r--source4/heimdal/lib/gssapi/mech/mechqueue.h101
-rw-r--r--source4/heimdal/lib/gssapi/mech/name.h48
-rw-r--r--source4/heimdal/lib/gssapi/mech/utils.h33
-rw-r--r--source4/heimdal/lib/gssapi/spnego/accept_sec_context.c1013
-rw-r--r--source4/heimdal/lib/gssapi/spnego/compat.c319
-rw-r--r--source4/heimdal/lib/gssapi/spnego/context_stubs.c949
-rw-r--r--source4/heimdal/lib/gssapi/spnego/cred_stubs.c356
-rw-r--r--source4/heimdal/lib/gssapi/spnego/external.c94
-rw-r--r--source4/heimdal/lib/gssapi/spnego/init_sec_context.c668
-rw-r--r--source4/heimdal/lib/gssapi/spnego/spnego.asn163
-rw-r--r--source4/heimdal/lib/gssapi/spnego/spnego_locl.h114
-rw-r--r--source4/heimdal/lib/hcrypto/aes.c124
-rw-r--r--source4/heimdal/lib/hcrypto/aes.h79
-rw-r--r--source4/heimdal/lib/hcrypto/bn.c445
-rw-r--r--source4/heimdal/lib/hcrypto/bn.h121
-rw-r--r--source4/heimdal/lib/hcrypto/camellia-ntt.c1464
-rw-r--r--source4/heimdal/lib/hcrypto/camellia-ntt.h58
-rw-r--r--source4/heimdal/lib/hcrypto/camellia.c120
-rw-r--r--source4/heimdal/lib/hcrypto/camellia.h72
-rw-r--r--source4/heimdal/lib/hcrypto/des-tables.h196
-rw-r--r--source4/heimdal/lib/hcrypto/des.c1185
-rw-r--r--source4/heimdal/lib/hcrypto/des.h144
-rw-r--r--source4/heimdal/lib/hcrypto/dh-imath.c251
-rw-r--r--source4/heimdal/lib/hcrypto/dh.c491
-rw-r--r--source4/heimdal/lib/hcrypto/dh.h141
-rw-r--r--source4/heimdal/lib/hcrypto/dsa.c125
-rw-r--r--source4/heimdal/lib/hcrypto/dsa.h140
-rw-r--r--source4/heimdal/lib/hcrypto/engine.c325
-rw-r--r--source4/heimdal/lib/hcrypto/engine.h103
-rw-r--r--source4/heimdal/lib/hcrypto/evp-aes-cts.c277
-rw-r--r--source4/heimdal/lib/hcrypto/evp-hcrypto.c176
-rw-r--r--source4/heimdal/lib/hcrypto/evp.c1701
-rw-r--r--source4/heimdal/lib/hcrypto/evp.h318
-rw-r--r--source4/heimdal/lib/hcrypto/hash.h71
-rw-r--r--source4/heimdal/lib/hcrypto/hmac.c161
-rw-r--r--source4/heimdal/lib/hcrypto/hmac.h82
-rw-r--r--source4/heimdal/lib/hcrypto/imath/LICENSE21
-rw-r--r--source4/heimdal/lib/hcrypto/imath/imath.c3352
-rw-r--r--source4/heimdal/lib/hcrypto/imath/imath.h231
-rw-r--r--source4/heimdal/lib/hcrypto/imath/iprime.c189
-rw-r--r--source4/heimdal/lib/hcrypto/imath/iprime.h51
-rw-r--r--source4/heimdal/lib/hcrypto/md2.c138
-rw-r--r--source4/heimdal/lib/hcrypto/md2.h63
-rw-r--r--source4/heimdal/lib/hcrypto/md4.c250
-rw-r--r--source4/heimdal/lib/hcrypto/md4.h62
-rw-r--r--source4/heimdal/lib/hcrypto/md5.c274
-rw-r--r--source4/heimdal/lib/hcrypto/md5.h62
-rw-r--r--source4/heimdal/lib/hcrypto/pkcs12.c159
-rw-r--r--source4/heimdal/lib/hcrypto/pkcs12.h57
-rw-r--r--source4/heimdal/lib/hcrypto/pkcs5.c132
-rw-r--r--source4/heimdal/lib/hcrypto/rand-egd.c264
-rw-r--r--source4/heimdal/lib/hcrypto/rand-fortuna.c612
-rw-r--r--source4/heimdal/lib/hcrypto/rand-timer.c206
-rw-r--r--source4/heimdal/lib/hcrypto/rand-unix.c163
-rw-r--r--source4/heimdal/lib/hcrypto/rand.c368
-rw-r--r--source4/heimdal/lib/hcrypto/rand.h108
-rw-r--r--source4/heimdal/lib/hcrypto/randi.h48
-rw-r--r--source4/heimdal/lib/hcrypto/rc2.c245
-rw-r--r--source4/heimdal/lib/hcrypto/rc2.h71
-rw-r--r--source4/heimdal/lib/hcrypto/rc4.c82
-rw-r--r--source4/heimdal/lib/hcrypto/rc4.h46
-rw-r--r--source4/heimdal/lib/hcrypto/resource.h18
-rw-r--r--source4/heimdal/lib/hcrypto/rijndael-alg-fst.c1231
-rw-r--r--source4/heimdal/lib/hcrypto/rijndael-alg-fst.h46
-rw-r--r--source4/heimdal/lib/hcrypto/rnd_keys.c142
-rw-r--r--source4/heimdal/lib/hcrypto/rsa-imath.c660
-rw-r--r--source4/heimdal/lib/hcrypto/rsa.c565
-rw-r--r--source4/heimdal/lib/hcrypto/rsa.h177
-rw-r--r--source4/heimdal/lib/hcrypto/sha.c300
-rw-r--r--source4/heimdal/lib/hcrypto/sha.h83
-rw-r--r--source4/heimdal/lib/hcrypto/sha256.c233
-rw-r--r--source4/heimdal/lib/hcrypto/ui.c166
-rw-r--r--source4/heimdal/lib/hcrypto/ui.h45
-rw-r--r--source4/heimdal/lib/hdb/db.c337
-rw-r--r--source4/heimdal/lib/hdb/dbinfo.c266
-rw-r--r--source4/heimdal/lib/hdb/ext.c421
-rw-r--r--source4/heimdal/lib/hdb/hdb.asn1127
-rw-r--r--source4/heimdal/lib/hdb/hdb.c431
-rw-r--r--source4/heimdal/lib/hdb/hdb.h150
-rw-r--r--source4/heimdal/lib/hdb/hdb_err.et28
-rw-r--r--source4/heimdal/lib/hdb/hdb_locl.h70
-rw-r--r--source4/heimdal/lib/hdb/keys.c401
-rw-r--r--source4/heimdal/lib/hdb/keytab.c272
-rw-r--r--source4/heimdal/lib/hdb/mkey.c604
-rw-r--r--source4/heimdal/lib/hdb/ndbm.c370
-rw-r--r--source4/heimdal/lib/hx509/ca.c1518
-rw-r--r--source4/heimdal/lib/hx509/cert.c3360
-rw-r--r--source4/heimdal/lib/hx509/cms.c1434
-rw-r--r--source4/heimdal/lib/hx509/collector.c329
-rw-r--r--source4/heimdal/lib/hx509/crmf.asn1113
-rw-r--r--source4/heimdal/lib/hx509/crypto.c2706
-rw-r--r--source4/heimdal/lib/hx509/env.c246
-rw-r--r--source4/heimdal/lib/hx509/error.c223
-rw-r--r--source4/heimdal/lib/hx509/file.c303
-rw-r--r--source4/heimdal/lib/hx509/hx509.h157
-rw-r--r--source4/heimdal/lib/hx509/hx509_err.et101
-rw-r--r--source4/heimdal/lib/hx509/hx_locl.h220
-rw-r--r--source4/heimdal/lib/hx509/keyset.c677
-rw-r--r--source4/heimdal/lib/hx509/ks_dir.c224
-rw-r--r--source4/heimdal/lib/hx509/ks_file.c645
-rw-r--r--source4/heimdal/lib/hx509/ks_keychain.c548
-rw-r--r--source4/heimdal/lib/hx509/ks_mem.c224
-rw-r--r--source4/heimdal/lib/hx509/ks_null.c98
-rw-r--r--source4/heimdal/lib/hx509/ks_p11.c1194
-rw-r--r--source4/heimdal/lib/hx509/ks_p12.c705
-rw-r--r--source4/heimdal/lib/hx509/lock.c248
-rw-r--r--source4/heimdal/lib/hx509/name.c991
-rw-r--r--source4/heimdal/lib/hx509/ocsp.asn1113
-rw-r--r--source4/heimdal/lib/hx509/peer.c202
-rw-r--r--source4/heimdal/lib/hx509/pkcs10.asn125
-rw-r--r--source4/heimdal/lib/hx509/print.c1037
-rw-r--r--source4/heimdal/lib/hx509/req.c325
-rw-r--r--source4/heimdal/lib/hx509/revoke.c1539
-rw-r--r--source4/heimdal/lib/hx509/sel-gram.c1714
-rw-r--r--source4/heimdal/lib/hx509/sel-gram.h83
-rw-r--r--source4/heimdal/lib/hx509/sel-gram.y115
-rw-r--r--source4/heimdal/lib/hx509/sel-lex.c1899
-rw-r--r--source4/heimdal/lib/hx509/sel-lex.l135
-rw-r--r--source4/heimdal/lib/hx509/sel.c232
-rw-r--r--source4/heimdal/lib/hx509/sel.h82
-rw-r--r--source4/heimdal/lib/hx509/test_name.c383
-rw-r--r--source4/heimdal/lib/krb5/acache.c1059
-rw-r--r--source4/heimdal/lib/krb5/add_et_list.c58
-rw-r--r--source4/heimdal/lib/krb5/addr_families.c1492
-rw-r--r--source4/heimdal/lib/krb5/appdefault.c142
-rw-r--r--source4/heimdal/lib/krb5/asn1_glue.c64
-rw-r--r--source4/heimdal/lib/krb5/auth_context.c521
-rw-r--r--source4/heimdal/lib/krb5/build_ap_req.c76
-rw-r--r--source4/heimdal/lib/krb5/build_auth.c202
-rw-r--r--source4/heimdal/lib/krb5/cache.c1489
-rw-r--r--source4/heimdal/lib/krb5/changepw.c862
-rw-r--r--source4/heimdal/lib/krb5/codec.c219
-rw-r--r--source4/heimdal/lib/krb5/config_file.c809
-rw-r--r--source4/heimdal/lib/krb5/config_file_netinfo.c180
-rw-r--r--source4/heimdal/lib/krb5/constants.c43
-rw-r--r--source4/heimdal/lib/krb5/context.c1228
-rw-r--r--source4/heimdal/lib/krb5/convert_creds.c206
-rw-r--r--source4/heimdal/lib/krb5/copy_host_realm.c79
-rw-r--r--source4/heimdal/lib/krb5/crc.c71
-rw-r--r--source4/heimdal/lib/krb5/creds.c292
-rw-r--r--source4/heimdal/lib/krb5/crypto.c4713
-rw-r--r--source4/heimdal/lib/krb5/data.c224
-rw-r--r--source4/heimdal/lib/krb5/eai_to_heim_errno.c114
-rw-r--r--source4/heimdal/lib/krb5/error_string.c277
-rw-r--r--source4/heimdal/lib/krb5/expand_hostname.c162
-rw-r--r--source4/heimdal/lib/krb5/fcache.c983
-rw-r--r--source4/heimdal/lib/krb5/free.c53
-rw-r--r--source4/heimdal/lib/krb5/free_host_realm.c54
-rw-r--r--source4/heimdal/lib/krb5/generate_seq_number.c62
-rw-r--r--source4/heimdal/lib/krb5/generate_subkey.c72
-rw-r--r--source4/heimdal/lib/krb5/get_addrs.c294
-rw-r--r--source4/heimdal/lib/krb5/get_cred.c1464
-rw-r--r--source4/heimdal/lib/krb5/get_default_principal.c116
-rw-r--r--source4/heimdal/lib/krb5/get_default_realm.c85
-rw-r--r--source4/heimdal/lib/krb5/get_for_creds.c462
-rw-r--r--source4/heimdal/lib/krb5/get_host_realm.c260
-rw-r--r--source4/heimdal/lib/krb5/get_in_tkt.c1078
-rw-r--r--source4/heimdal/lib/krb5/get_in_tkt_with_keytab.c99
-rw-r--r--source4/heimdal/lib/krb5/get_port.c54
-rw-r--r--source4/heimdal/lib/krb5/heim_err.et44
-rw-r--r--source4/heimdal/lib/krb5/heim_threads.h175
-rw-r--r--source4/heimdal/lib/krb5/init_creds.c447
-rw-r--r--source4/heimdal/lib/krb5/init_creds_pw.c1604
-rw-r--r--source4/heimdal/lib/krb5/k524_err.et20
-rw-r--r--source4/heimdal/lib/krb5/kcm.c1159
-rw-r--r--source4/heimdal/lib/krb5/keyblock.c133
-rw-r--r--source4/heimdal/lib/krb5/keytab.c536
-rw-r--r--source4/heimdal/lib/krb5/keytab_any.c262
-rw-r--r--source4/heimdal/lib/krb5/keytab_file.c779
-rw-r--r--source4/heimdal/lib/krb5/keytab_keyfile.c451
-rw-r--r--source4/heimdal/lib/krb5/keytab_memory.c237
-rw-r--r--source4/heimdal/lib/krb5/krb5-v4compat.h132
-rw-r--r--source4/heimdal/lib/krb5/krb5.h814
-rw-r--r--source4/heimdal/lib/krb5/krb5_ccapi.h230
-rw-r--r--source4/heimdal/lib/krb5/krb5_err.et268
-rw-r--r--source4/heimdal/lib/krb5/krb5_locl.h310
-rw-r--r--source4/heimdal/lib/krb5/krb_err.et63
-rw-r--r--source4/heimdal/lib/krb5/krbhst.c1019
-rw-r--r--source4/heimdal/lib/krb5/locate_plugin.h66
-rw-r--r--source4/heimdal/lib/krb5/log.c482
-rw-r--r--source4/heimdal/lib/krb5/mcache.c499
-rw-r--r--source4/heimdal/lib/krb5/misc.c86
-rw-r--r--source4/heimdal/lib/krb5/mit_glue.c373
-rw-r--r--source4/heimdal/lib/krb5/mk_error.c92
-rw-r--r--source4/heimdal/lib/krb5/mk_priv.c156
-rw-r--r--source4/heimdal/lib/krb5/mk_rep.c124
-rw-r--r--source4/heimdal/lib/krb5/mk_req.c116
-rw-r--r--source4/heimdal/lib/krb5/mk_req_ext.c165
-rw-r--r--source4/heimdal/lib/krb5/n-fold.c137
-rw-r--r--source4/heimdal/lib/krb5/pac.c1062
-rw-r--r--source4/heimdal/lib/krb5/padata.c67
-rw-r--r--source4/heimdal/lib/krb5/pkinit.c2154
-rw-r--r--source4/heimdal/lib/krb5/plugin.c270
-rw-r--r--source4/heimdal/lib/krb5/principal.c1376
-rw-r--r--source4/heimdal/lib/krb5/prog_setup.c66
-rw-r--r--source4/heimdal/lib/krb5/prompter_posix.c74
-rw-r--r--source4/heimdal/lib/krb5/rd_cred.c346
-rw-r--r--source4/heimdal/lib/krb5/rd_error.c127
-rw-r--r--source4/heimdal/lib/krb5/rd_priv.c186
-rw-r--r--source4/heimdal/lib/krb5/rd_rep.c124
-rw-r--r--source4/heimdal/lib/krb5/rd_req.c894
-rw-r--r--source4/heimdal/lib/krb5/replay.c321
-rw-r--r--source4/heimdal/lib/krb5/send_to_kdc.c670
-rw-r--r--source4/heimdal/lib/krb5/send_to_kdc_plugin.h58
-rw-r--r--source4/heimdal/lib/krb5/set_default_realm.c92
-rw-r--r--source4/heimdal/lib/krb5/store-int.h47
-rw-r--r--source4/heimdal/lib/krb5/store.c1035
-rw-r--r--source4/heimdal/lib/krb5/store_emem.c147
-rw-r--r--source4/heimdal/lib/krb5/store_fd.c98
-rw-r--r--source4/heimdal/lib/krb5/store_mem.c150
-rw-r--r--source4/heimdal/lib/krb5/ticket.c303
-rw-r--r--source4/heimdal/lib/krb5/time.c131
-rw-r--r--source4/heimdal/lib/krb5/transited.c518
-rw-r--r--source4/heimdal/lib/krb5/v4_glue.c953
-rw-r--r--source4/heimdal/lib/krb5/version.c43
-rw-r--r--source4/heimdal/lib/krb5/warn.c211
-rw-r--r--source4/heimdal/lib/ntlm/heimntlm.h124
-rw-r--r--source4/heimdal/lib/ntlm/ntlm.c1375
-rw-r--r--source4/heimdal/lib/roken/base64.c136
-rw-r--r--source4/heimdal/lib/roken/base64.h53
-rw-r--r--source4/heimdal/lib/roken/bswap.c61
-rw-r--r--source4/heimdal/lib/roken/cloexec.c60
-rw-r--r--source4/heimdal/lib/roken/closefrom.c60
-rw-r--r--source4/heimdal/lib/roken/copyhostent.c102
-rw-r--r--source4/heimdal/lib/roken/dumpdata.c99
-rw-r--r--source4/heimdal/lib/roken/ecalloc.c56
-rw-r--r--source4/heimdal/lib/roken/emalloc.c56
-rw-r--r--source4/heimdal/lib/roken/erealloc.c56
-rw-r--r--source4/heimdal/lib/roken/err.hin88
-rw-r--r--source4/heimdal/lib/roken/estrdup.c56
-rw-r--r--source4/heimdal/lib/roken/freeaddrinfo.c57
-rw-r--r--source4/heimdal/lib/roken/freehostent.c62
-rw-r--r--source4/heimdal/lib/roken/gai_strerror.c77
-rw-r--r--source4/heimdal/lib/roken/get_window_size.c102
-rw-r--r--source4/heimdal/lib/roken/getaddrinfo.c417
-rw-r--r--source4/heimdal/lib/roken/getarg.c625
-rw-r--r--source4/heimdal/lib/roken/getarg.h110
-rw-r--r--source4/heimdal/lib/roken/getipnodebyaddr.c74
-rw-r--r--source4/heimdal/lib/roken/getipnodebyname.c86
-rw-r--r--source4/heimdal/lib/roken/getnameinfo.c127
-rw-r--r--source4/heimdal/lib/roken/getprogname.c51
-rw-r--r--source4/heimdal/lib/roken/h_errno.c41
-rw-r--r--source4/heimdal/lib/roken/hex.c103
-rw-r--r--source4/heimdal/lib/roken/hex.h55
-rw-r--r--source4/heimdal/lib/roken/hostent_find_fqdn.c59
-rw-r--r--source4/heimdal/lib/roken/inet_aton.c49
-rw-r--r--source4/heimdal/lib/roken/inet_ntop.c133
-rw-r--r--source4/heimdal/lib/roken/inet_pton.c49
-rw-r--r--source4/heimdal/lib/roken/issuid.c59
-rw-r--r--source4/heimdal/lib/roken/net_read.c74
-rw-r--r--source4/heimdal/lib/roken/net_write.c72
-rw-r--r--source4/heimdal/lib/roken/parse_bytes.h56
-rw-r--r--source4/heimdal/lib/roken/parse_time.c78
-rw-r--r--source4/heimdal/lib/roken/parse_time.h59
-rw-r--r--source4/heimdal/lib/roken/parse_units.c330
-rw-r--r--source4/heimdal/lib/roken/parse_units.h79
-rw-r--r--source4/heimdal/lib/roken/resolve.c712
-rw-r--r--source4/heimdal/lib/roken/resolve.h298
-rw-r--r--source4/heimdal/lib/roken/rkpty.c336
-rw-r--r--source4/heimdal/lib/roken/roken-common.h418
-rw-r--r--source4/heimdal/lib/roken/roken.h.in702
-rw-r--r--source4/heimdal/lib/roken/roken_gethostby.c274
-rw-r--r--source4/heimdal/lib/roken/rtbl.c489
-rw-r--r--source4/heimdal/lib/roken/rtbl.h118
-rw-r--r--source4/heimdal/lib/roken/setprogname.c61
-rw-r--r--source4/heimdal/lib/roken/signal.c80
-rw-r--r--source4/heimdal/lib/roken/simple_exec.c335
-rw-r--r--source4/heimdal/lib/roken/socket.c302
-rw-r--r--source4/heimdal/lib/roken/strcollect.c96
-rw-r--r--source4/heimdal/lib/roken/strlwr.c53
-rw-r--r--source4/heimdal/lib/roken/strpool.c110
-rw-r--r--source4/heimdal/lib/roken/strsep.c61
-rw-r--r--source4/heimdal/lib/roken/strsep_copy.c69
-rw-r--r--source4/heimdal/lib/roken/strupr.c53
-rw-r--r--source4/heimdal/lib/roken/vis.c424
-rw-r--r--source4/heimdal/lib/roken/vis.hin125
-rw-r--r--source4/heimdal/lib/roken/xfree.c47
-rw-r--r--source4/heimdal/lib/vers/print_version.c55
-rw-r--r--source4/heimdal/lib/wind/CompositionExclusions-3.2.0.txt176
-rw-r--r--source4/heimdal/lib/wind/DerivedNormalizationProps.txt2574
-rw-r--r--source4/heimdal/lib/wind/NormalizationCorrections.txt43
-rw-r--r--source4/heimdal/lib/wind/NormalizationTest.txt17166
-rw-r--r--source4/heimdal/lib/wind/UnicodeData.py57
-rw-r--r--source4/heimdal/lib/wind/UnicodeData.txt15100
-rw-r--r--source4/heimdal/lib/wind/bidi.c92
-rw-r--r--source4/heimdal/lib/wind/combining.c62
-rw-r--r--source4/heimdal/lib/wind/errorlist.c77
-rw-r--r--source4/heimdal/lib/wind/gen-bidi.py101
-rw-r--r--source4/heimdal/lib/wind/gen-combining.py104
-rw-r--r--source4/heimdal/lib/wind/gen-errorlist.py120
-rw-r--r--source4/heimdal/lib/wind/gen-map.py158
-rw-r--r--source4/heimdal/lib/wind/gen-normalize.py210
-rw-r--r--source4/heimdal/lib/wind/generate.py81
-rw-r--r--source4/heimdal/lib/wind/ldap.c91
-rw-r--r--source4/heimdal/lib/wind/map.c87
-rw-r--r--source4/heimdal/lib/wind/normalize.c301
-rw-r--r--source4/heimdal/lib/wind/rfc3454.py60
-rw-r--r--source4/heimdal/lib/wind/rfc3454.txt5099
-rw-r--r--source4/heimdal/lib/wind/rfc3490.txt1235
-rw-r--r--source4/heimdal/lib/wind/rfc3491.txt395
-rw-r--r--source4/heimdal/lib/wind/rfc3492.txt1963
-rw-r--r--source4/heimdal/lib/wind/rfc4013.txt339
-rw-r--r--source4/heimdal/lib/wind/rfc4518.py150
-rw-r--r--source4/heimdal/lib/wind/rfc4518.txt787
-rw-r--r--source4/heimdal/lib/wind/stringprep.c141
-rw-r--r--source4/heimdal/lib/wind/stringprep.py90
-rw-r--r--source4/heimdal/lib/wind/utf8.c520
-rw-r--r--source4/heimdal/lib/wind/util.py48
-rw-r--r--source4/heimdal/lib/wind/wind.h85
-rw-r--r--source4/heimdal/lib/wind/wind_err.et23
-rw-r--r--source4/heimdal/lib/wind/windlocl.h66
-rwxr-xr-xsource4/heimdal_build/asn1_compile_wrapper.sh59
-rwxr-xr-xsource4/heimdal_build/asn1_deps.pl111
-rw-r--r--source4/heimdal_build/config.h30
-rw-r--r--source4/heimdal_build/config.m423
-rw-r--r--source4/heimdal_build/crypto-headers.h20
-rwxr-xr-xsource4/heimdal_build/et_compile_wrapper.sh65
-rwxr-xr-xsource4/heimdal_build/et_deps.pl17
-rw-r--r--source4/heimdal_build/external.m454
-rw-r--r--source4/heimdal_build/gssapi-glue.c28
-rw-r--r--source4/heimdal_build/internal.m4290
-rw-r--r--source4/heimdal_build/internal.mk797
-rw-r--r--source4/heimdal_build/kafs.h19
-rw-r--r--source4/heimdal_build/kpasswdd-glue.h6
-rw-r--r--source4/heimdal_build/krb5-glue.c47
-rw-r--r--source4/heimdal_build/krb5-types.h13
-rw-r--r--source4/heimdal_build/krb5/windc_plugin.h1
-rwxr-xr-xsource4/heimdal_build/perl_path_wrapper.sh7
-rw-r--r--source4/heimdal_build/print_version.h1
-rw-r--r--source4/heimdal_build/replace.c86
-rw-r--r--source4/heimdal_build/roken.h87
-rw-r--r--source4/heimdal_build/vis.h15
-rw-r--r--source4/include/includes.h72
-rwxr-xr-xsource4/install-sh238
-rw-r--r--source4/kdc/config.m41
-rw-r--r--source4/kdc/config.mk26
-rw-r--r--source4/kdc/hdb-samba4.c1558
-rw-r--r--source4/kdc/kdc.c795
-rw-r--r--source4/kdc/kdc.h62
-rw-r--r--source4/kdc/kpasswdd.c628
-rw-r--r--source4/kdc/pac-glue.c307
-rw-r--r--source4/ldap_server/config.mk22
-rw-r--r--source4/ldap_server/devdocs/AD-Syntaxes.txt79
-rw-r--r--source4/ldap_server/devdocs/Index34
-rw-r--r--source4/ldap_server/devdocs/draft-armijo-ldap-syntax-00.txt137
-rw-r--r--source4/ldap_server/devdocs/ldapext-ldapv3-vlv-04.txt655
-rw-r--r--source4/ldap_server/devdocs/rfc2307.txt1179
-rw-r--r--source4/ldap_server/devdocs/rfc2696.txt395
-rw-r--r--source4/ldap_server/devdocs/rfc2849.txt787
-rw-r--r--source4/ldap_server/devdocs/rfc2891.txt451
-rw-r--r--source4/ldap_server/devdocs/rfc3296.txt787
-rw-r--r--source4/ldap_server/devdocs/rfc4510.txt395
-rw-r--r--source4/ldap_server/devdocs/rfc4511.txt3811
-rw-r--r--source4/ldap_server/devdocs/rfc4512.txt2915
-rw-r--r--source4/ldap_server/devdocs/rfc4513.txt1907
-rw-r--r--source4/ldap_server/devdocs/rfc4514.txt843
-rw-r--r--source4/ldap_server/devdocs/rfc4515.txt675
-rw-r--r--source4/ldap_server/devdocs/rfc4516.txt843
-rw-r--r--source4/ldap_server/devdocs/rfc4517.txt2971
-rw-r--r--source4/ldap_server/devdocs/rfc4518.txt787
-rw-r--r--source4/ldap_server/devdocs/rfc4519.txt1963
-rw-r--r--source4/ldap_server/devdocs/rfc4520.txt1067
-rw-r--r--source4/ldap_server/devdocs/rfc4521.txt899
-rw-r--r--source4/ldap_server/devdocs/rfc4522.txt451
-rw-r--r--source4/ldap_server/devdocs/rfc4523.txt1347
-rw-r--r--source4/ldap_server/devdocs/rfc4524.txt1403
-rw-r--r--source4/ldap_server/devdocs/rfc4525.txt339
-rw-r--r--source4/ldap_server/devdocs/rfc4526.txt283
-rw-r--r--source4/ldap_server/devdocs/rfc4527.txt451
-rw-r--r--source4/ldap_server/devdocs/rfc4528.txt339
-rw-r--r--source4/ldap_server/devdocs/rfc4529.txt339
-rw-r--r--source4/ldap_server/devdocs/rfc4530.txt451
-rw-r--r--source4/ldap_server/devdocs/rfc4531.txt507
-rw-r--r--source4/ldap_server/devdocs/rfc4532.txt395
-rw-r--r--source4/ldap_server/devdocs/rfc4533.txt1795
-rw-r--r--source4/ldap_server/ldap_backend.c779
-rw-r--r--source4/ldap_server/ldap_bind.c313
-rw-r--r--source4/ldap_server/ldap_extended.c150
-rw-r--r--source4/ldap_server/ldap_server.c592
-rw-r--r--source4/ldap_server/ldap_server.h71
-rw-r--r--source4/lib/basic.mk26
-rw-r--r--source4/lib/cmdline/config.mk21
-rw-r--r--source4/lib/cmdline/credentials.c50
-rw-r--r--source4/lib/cmdline/popt_common.c213
-rw-r--r--source4/lib/cmdline/popt_common.h41
-rw-r--r--source4/lib/cmdline/popt_credentials.c139
-rw-r--r--source4/lib/com/README9
-rw-r--r--source4/lib/com/classes/simple.c122
-rw-r--r--source4/lib/com/com.h52
-rw-r--r--source4/lib/com/config.mk22
-rw-r--r--source4/lib/com/dcom/dcom.h85
-rw-r--r--source4/lib/com/dcom/main.c710
-rw-r--r--source4/lib/com/dcom/tables.c92
-rw-r--r--source4/lib/com/main.c90
-rw-r--r--source4/lib/com/pycom.c85
-rw-r--r--source4/lib/com/rot.c35
-rw-r--r--source4/lib/com/tables.c109
-rw-r--r--source4/lib/events/config.mk5
-rw-r--r--source4/lib/events/events.h7
-rw-r--r--source4/lib/events/tevent_s4.c90
-rw-r--r--source4/lib/ldb-samba/README7
-rw-r--r--source4/lib/ldb-samba/config.mk11
-rw-r--r--source4/lib/ldb-samba/ldif_handlers.c771
-rw-r--r--source4/lib/ldb-samba/ldif_handlers.h13
-rw-r--r--source4/lib/ldb/Doxyfile26
-rw-r--r--source4/lib/ldb/Makefile.in186
-rw-r--r--source4/lib/ldb/README_gcov.txt29
-rw-r--r--source4/lib/ldb/aclocal.m41
-rwxr-xr-xsource4/lib/ldb/autogen.sh16
-rw-r--r--source4/lib/ldb/build_macros.m415
-rw-r--r--source4/lib/ldb/common/attrib_handlers.c378
-rw-r--r--source4/lib/ldb/common/ldb.c1432
-rw-r--r--source4/lib/ldb/common/ldb_attributes.c275
-rw-r--r--source4/lib/ldb/common/ldb_controls.c597
-rw-r--r--source4/lib/ldb/common/ldb_debug.c103
-rw-r--r--source4/lib/ldb/common/ldb_dn.c1800
-rw-r--r--source4/lib/ldb/common/ldb_ldif.c761
-rw-r--r--source4/lib/ldb/common/ldb_match.c428
-rw-r--r--source4/lib/ldb/common/ldb_modules.c808
-rw-r--r--source4/lib/ldb/common/ldb_msg.c899
-rw-r--r--source4/lib/ldb/common/ldb_parse.c819
-rw-r--r--source4/lib/ldb/common/ldb_utf8.c136
-rw-r--r--source4/lib/ldb/common/qsort.c251
-rwxr-xr-xsource4/lib/ldb/config.guess1464
-rw-r--r--source4/lib/ldb/config.mk151
-rwxr-xr-xsource4/lib/ldb/config.sub1577
-rw-r--r--source4/lib/ldb/configure.ac102
-rwxr-xr-xsource4/lib/ldb/docs/builddocs.sh52
-rw-r--r--source4/lib/ldb/docs/design.txt41
-rwxr-xr-xsource4/lib/ldb/docs/installdocs.sh17
-rw-r--r--source4/lib/ldb/examples.dox16
-rw-r--r--source4/lib/ldb/examples/ldbreader.c122
-rw-r--r--source4/lib/ldb/examples/ldifreader.c125
-rw-r--r--source4/lib/ldb/external/libevents.m47
-rw-r--r--source4/lib/ldb/external/libpopt.m47
-rw-r--r--source4/lib/ldb/external/libtalloc.m47
-rw-r--r--source4/lib/ldb/external/libtdb.m47
-rw-r--r--source4/lib/ldb/external/pkg.m4156
-rw-r--r--source4/lib/ldb/include/dlinklist.h114
-rw-r--r--source4/lib/ldb/include/ldb.h1840
-rw-r--r--source4/lib/ldb/include/ldb_errors.h310
-rw-r--r--source4/lib/ldb/include/ldb_handlers.h67
-rw-r--r--source4/lib/ldb/include/ldb_includes.h18
-rw-r--r--source4/lib/ldb/include/ldb_module.h161
-rw-r--r--source4/lib/ldb/include/ldb_private.h165
-rwxr-xr-xsource4/lib/ldb/install-sh238
-rw-r--r--source4/lib/ldb/ldap.m490
-rw-r--r--source4/lib/ldb/ldb.mk81
-rw-r--r--source4/lib/ldb/ldb.pc.in16
-rw-r--r--source4/lib/ldb/ldb_ildap/config.mk13
-rw-r--r--source4/lib/ldb/ldb_ildap/ldb_ildap.c849
-rw-r--r--source4/lib/ldb/ldb_ldap/ldb_ldap.c909
-rw-r--r--source4/lib/ldb/ldb_map/ldb_map.c1135
-rw-r--r--source4/lib/ldb/ldb_map/ldb_map.h173
-rw-r--r--source4/lib/ldb/ldb_map/ldb_map_inbound.c826
-rw-r--r--source4/lib/ldb/ldb_map/ldb_map_outbound.c1378
-rw-r--r--source4/lib/ldb/ldb_map/ldb_map_private.h91
-rw-r--r--source4/lib/ldb/ldb_sqlite3/README7
-rw-r--r--source4/lib/ldb/ldb_sqlite3/base160.c154
-rw-r--r--source4/lib/ldb/ldb_sqlite3/ldb_sqlite3.c1907
-rw-r--r--source4/lib/ldb/ldb_sqlite3/schema328
-rw-r--r--source4/lib/ldb/ldb_sqlite3/trees.ps1760
-rw-r--r--source4/lib/ldb/ldb_tdb/ldb_cache.c477
-rw-r--r--source4/lib/ldb/ldb_tdb/ldb_index.c1617
-rw-r--r--source4/lib/ldb/ldb_tdb/ldb_pack.c291
-rw-r--r--source4/lib/ldb/ldb_tdb/ldb_search.c560
-rw-r--r--source4/lib/ldb/ldb_tdb/ldb_tdb.c1296
-rw-r--r--source4/lib/ldb/ldb_tdb/ldb_tdb.h135
-rw-r--r--source4/lib/ldb/ldb_tdb/ldb_tdb_wrap.c155
-rw-r--r--source4/lib/ldb/libldb.m47
-rw-r--r--source4/lib/ldb/mainpage.dox80
-rw-r--r--source4/lib/ldb/man/ad2oLschema.1.xml87
-rw-r--r--source4/lib/ldb/man/ldb.3.xml262
-rw-r--r--source4/lib/ldb/man/ldbadd.1.xml105
-rw-r--r--source4/lib/ldb/man/ldbdel.1.xml105
-rw-r--r--source4/lib/ldb/man/ldbedit.1.xml200
-rw-r--r--source4/lib/ldb/man/ldbmodify.1.xml93
-rw-r--r--source4/lib/ldb/man/ldbrename.1.xml107
-rw-r--r--source4/lib/ldb/man/ldbsearch.1.xml119
-rw-r--r--source4/lib/ldb/man/oLschema2ldif.1.xml79
-rw-r--r--source4/lib/ldb/modules/asq.c406
-rw-r--r--source4/lib/ldb/modules/operational.c314
-rw-r--r--source4/lib/ldb/modules/paged_results.c422
-rw-r--r--source4/lib/ldb/modules/paged_searches.c383
-rw-r--r--source4/lib/ldb/modules/rdn_name.c328
-rw-r--r--source4/lib/ldb/modules/skel.c138
-rw-r--r--source4/lib/ldb/modules/sort.c349
-rw-r--r--source4/lib/ldb/nssldb/README.txt34
-rw-r--r--source4/lib/ldb/nssldb/ldb-grp.c429
-rw-r--r--source4/lib/ldb/nssldb/ldb-nss.c395
-rw-r--r--source4/lib/ldb/nssldb/ldb-nss.h84
-rw-r--r--source4/lib/ldb/nssldb/ldb-pwd.c242
-rw-r--r--source4/lib/ldb/pyldb.c2175
-rw-r--r--source4/lib/ldb/pyldb.h101
-rw-r--r--source4/lib/ldb/python.mk6
-rw-r--r--source4/lib/ldb/rules.mk25
-rw-r--r--source4/lib/ldb/sqlite3.m462
-rwxr-xr-xsource4/lib/ldb/standalone.sh28
-rw-r--r--source4/lib/ldb/tests/init.ldif40
-rwxr-xr-xsource4/lib/ldb/tests/init_slapd.sh41
-rwxr-xr-xsource4/lib/ldb/tests/kill_slapd.sh12
-rwxr-xr-xsource4/lib/ldb/tests/ldapi_url.sh11
-rw-r--r--source4/lib/ldb/tests/photo.ldif5
-rwxr-xr-xsource4/lib/ldb/tests/python/api.py497
-rwxr-xr-xsource4/lib/ldb/tests/python/ldap.py1058
-rw-r--r--source4/lib/ldb/tests/samba4.pngbin0 -> 6239 bytes
-rw-r--r--source4/lib/ldb/tests/sample_module.c36
-rw-r--r--source4/lib/ldb/tests/schema-tests/schema-add-test.ldif66
-rw-r--r--source4/lib/ldb/tests/schema-tests/schema-mod-test-1.ldif5
-rw-r--r--source4/lib/ldb/tests/schema-tests/schema-mod-test-2.ldif5
-rw-r--r--source4/lib/ldb/tests/schema-tests/schema-mod-test-3.ldif5
-rw-r--r--source4/lib/ldb/tests/schema-tests/schema-mod-test-4.ldif5
-rw-r--r--source4/lib/ldb/tests/schema-tests/schema-mod-test-5.ldif5
-rw-r--r--source4/lib/ldb/tests/schema-tests/schema.ldif100
-rw-r--r--source4/lib/ldb/tests/slapd.conf26
-rwxr-xr-xsource4/lib/ldb/tests/start_slapd.sh14
-rw-r--r--source4/lib/ldb/tests/test-attribs.ldif6
-rw-r--r--source4/lib/ldb/tests/test-config.ldif67
-rw-r--r--source4/lib/ldb/tests/test-default-config.ldif17
-rwxr-xr-xsource4/lib/ldb/tests/test-extended.sh69
-rwxr-xr-xsource4/lib/ldb/tests/test-generic.sh128
-rw-r--r--source4/lib/ldb/tests/test-index.ldif7
-rwxr-xr-xsource4/lib/ldb/tests/test-ldap.sh54
-rw-r--r--source4/lib/ldb/tests/test-modify.ldif23
-rwxr-xr-xsource4/lib/ldb/tests/test-schema.sh34
-rwxr-xr-xsource4/lib/ldb/tests/test-soloading.sh32
-rwxr-xr-xsource4/lib/ldb/tests/test-sqlite3.sh25
-rw-r--r--source4/lib/ldb/tests/test-tdb-features.sh160
-rwxr-xr-xsource4/lib/ldb/tests/test-tdb.sh31
-rw-r--r--source4/lib/ldb/tests/test-wildcard.ldif5
-rw-r--r--source4/lib/ldb/tests/test-wrong_attributes.ldif3
-rw-r--r--source4/lib/ldb/tests/test.ldif411
-rw-r--r--source4/lib/ldb/tests/testdata.txt8
-rw-r--r--source4/lib/ldb/tests/testsearch.txt5
-rw-r--r--source4/lib/ldb/tools/cmdline.c398
-rw-r--r--source4/lib/ldb/tools/cmdline.h53
-rw-r--r--source4/lib/ldb/tools/config.mk90
-rw-r--r--source4/lib/ldb/tools/ldbadd.c126
-rw-r--r--source4/lib/ldb/tools/ldbdel.c115
-rw-r--r--source4/lib/ldb/tools/ldbedit.c339
-rw-r--r--source4/lib/ldb/tools/ldbmodify.c116
-rw-r--r--source4/lib/ldb/tools/ldbrename.c90
-rw-r--r--source4/lib/ldb/tools/ldbsearch.c329
-rw-r--r--source4/lib/ldb/tools/ldbtest.c419
-rw-r--r--source4/lib/ldb/web/index.html86
-rw-r--r--source4/lib/ldb_wrap.c192
-rw-r--r--source4/lib/ldb_wrap.h43
-rw-r--r--source4/lib/messaging/config.mk18
-rw-r--r--source4/lib/messaging/irpc.h132
-rw-r--r--source4/lib/messaging/messaging.c1118
-rw-r--r--source4/lib/messaging/messaging.h39
-rw-r--r--source4/lib/messaging/pymessaging.c578
-rw-r--r--source4/lib/messaging/tests/bindings.py57
-rw-r--r--source4/lib/messaging/tests/irpc.c271
-rw-r--r--source4/lib/messaging/tests/messaging.c145
-rw-r--r--source4/lib/registry/Doxyfile24
-rw-r--r--source4/lib/registry/README42
-rw-r--r--source4/lib/registry/TODO5
-rw-r--r--source4/lib/registry/config.mk110
-rw-r--r--source4/lib/registry/dir.c409
-rw-r--r--source4/lib/registry/hive.c180
-rw-r--r--source4/lib/registry/interface.c295
-rw-r--r--source4/lib/registry/ldb.c834
-rw-r--r--source4/lib/registry/local.c351
-rw-r--r--source4/lib/registry/man/regdiff.1.xml100
-rw-r--r--source4/lib/registry/man/regpatch.1.xml86
-rw-r--r--source4/lib/registry/man/regshell.1.xml186
-rw-r--r--source4/lib/registry/man/regtree.1.xml98
-rw-r--r--source4/lib/registry/patchfile.c496
-rw-r--r--source4/lib/registry/patchfile_dotreg.c265
-rw-r--r--source4/lib/registry/patchfile_preg.c358
-rw-r--r--source4/lib/registry/pyregistry.c443
-rw-r--r--source4/lib/registry/regf.c2164
-rw-r--r--source4/lib/registry/regf.idl167
-rw-r--r--source4/lib/registry/registry.h526
-rw-r--r--source4/lib/registry/registry.pc.in12
-rw-r--r--source4/lib/registry/rpc.c516
-rw-r--r--source4/lib/registry/samba.c97
-rw-r--r--source4/lib/registry/tests/bindings.py57
-rw-r--r--source4/lib/registry/tests/diff.c296
-rw-r--r--source4/lib/registry/tests/generic.c138
-rw-r--r--source4/lib/registry/tests/hive.c493
-rw-r--r--source4/lib/registry/tests/registry.c594
-rw-r--r--source4/lib/registry/tools/common.c88
-rw-r--r--source4/lib/registry/tools/regdiff.c149
-rw-r--r--source4/lib/registry/tools/regpatch.c74
-rw-r--r--source4/lib/registry/tools/regshell.c582
-rw-r--r--source4/lib/registry/tools/regtree.c167
-rw-r--r--source4/lib/registry/util.c250
-rw-r--r--source4/lib/registry/wine.c45
-rw-r--r--source4/lib/samba3/README8
-rw-r--r--source4/lib/samba3/STATUS68
-rw-r--r--source4/lib/samba3/config.mk8
-rw-r--r--source4/lib/samba3/samba3.h32
-rw-r--r--source4/lib/samba3/smbpasswd.c203
-rw-r--r--source4/lib/smbreadline/readline.m488
-rw-r--r--source4/lib/smbreadline/smbreadline.c170
-rw-r--r--source4/lib/smbreadline/smbreadline.h9
-rw-r--r--source4/lib/socket/access.c361
-rw-r--r--source4/lib/socket/config.m418
-rw-r--r--source4/lib/socket/config.mk43
-rw-r--r--source4/lib/socket/connect.c158
-rw-r--r--source4/lib/socket/connect_multi.c278
-rw-r--r--source4/lib/socket/interface.c334
-rw-r--r--source4/lib/socket/netif.c126
-rw-r--r--source4/lib/socket/netif.h36
-rw-r--r--source4/lib/socket/socket.c596
-rw-r--r--source4/lib/socket/socket.h218
-rw-r--r--source4/lib/socket/socket_ip.c1004
-rw-r--r--source4/lib/socket/socket_unix.c420
-rw-r--r--source4/lib/socket/testsuite.c198
-rw-r--r--source4/lib/stream/config.mk4
-rw-r--r--source4/lib/stream/packet.c604
-rw-r--r--source4/lib/stream/packet.h64
-rw-r--r--source4/lib/tdb_wrap.c117
-rw-r--r--source4/lib/tdb_wrap.h38
-rw-r--r--source4/lib/tdr/TODO1
-rw-r--r--source4/lib/tdr/config.mk9
-rw-r--r--source4/lib/tdr/tdr.c397
-rw-r--r--source4/lib/tdr/tdr.h67
-rw-r--r--source4/lib/tdr/testsuite.c186
-rw-r--r--source4/lib/tls/config.m445
-rw-r--r--source4/lib/tls/config.mk5
-rw-r--r--source4/lib/tls/tls.c695
-rw-r--r--source4/lib/tls/tls.h68
-rw-r--r--source4/lib/tls/tlscert.c166
-rw-r--r--source4/lib/wmi/config.mk69
-rw-r--r--source4/lib/wmi/tools/wmic.c221
-rw-r--r--source4/lib/wmi/tools/wmis.c221
-rw-r--r--source4/lib/wmi/wbemdata.c451
-rw-r--r--source4/lib/wmi/wmi.h48
-rw-r--r--source4/lib/wmi/wmi.i352
-rw-r--r--source4/lib/wmi/wmi.py95
-rw-r--r--source4/lib/wmi/wmi_wrap.c4310
-rw-r--r--source4/lib/wmi/wmicore.c252
-rw-r--r--source4/lib/zlib.mk16
-rw-r--r--source4/libcli/auth/config.mk17
-rw-r--r--source4/libcli/auth/credentials.c375
-rw-r--r--source4/libcli/auth/credentials.h46
-rw-r--r--source4/libcli/auth/libcli_auth.h24
-rw-r--r--source4/libcli/auth/session.c218
-rw-r--r--source4/libcli/auth/smbdes.c381
-rw-r--r--source4/libcli/auth/smbencrypt.c595
-rw-r--r--source4/libcli/cldap/cldap.c738
-rw-r--r--source4/libcli/cldap/cldap.h182
-rw-r--r--source4/libcli/cliconnect.c267
-rw-r--r--source4/libcli/clideltree.c137
-rw-r--r--source4/libcli/clifile.c702
-rw-r--r--source4/libcli/clilist.c356
-rw-r--r--source4/libcli/climessage.c95
-rw-r--r--source4/libcli/clireadwrite.c167
-rw-r--r--source4/libcli/clitrans2.c224
-rw-r--r--source4/libcli/composite/composite.c218
-rw-r--r--source4/libcli/composite/composite.h109
-rw-r--r--source4/libcli/config.mk169
-rw-r--r--source4/libcli/dgram/browse.c114
-rw-r--r--source4/libcli/dgram/dgramsocket.c245
-rw-r--r--source4/libcli/dgram/libdgram.h157
-rw-r--r--source4/libcli/dgram/mailslot.c226
-rw-r--r--source4/libcli/dgram/netlogon.c145
-rw-r--r--source4/libcli/finddcs.c276
-rw-r--r--source4/libcli/ldap/config.mk12
-rw-r--r--source4/libcli/ldap/ldap.h31
-rw-r--r--source4/libcli/ldap/ldap_bind.c413
-rw-r--r--source4/libcli/ldap/ldap_client.c832
-rw-r--r--source4/libcli/ldap/ldap_client.h140
-rw-r--r--source4/libcli/ldap/ldap_controls.c1237
-rw-r--r--source4/libcli/ldap/ldap_ildap.c129
-rw-r--r--source4/libcli/libcli.h70
-rw-r--r--source4/libcli/rap/rap.h358
-rw-r--r--source4/libcli/raw/README5
-rw-r--r--source4/libcli/raw/clierror.c73
-rw-r--r--source4/libcli/raw/clioplock.c62
-rw-r--r--source4/libcli/raw/clisession.c297
-rw-r--r--source4/libcli/raw/clisocket.c249
-rw-r--r--source4/libcli/raw/clitransport.c680
-rw-r--r--source4/libcli/raw/clitree.c216
-rw-r--r--source4/libcli/raw/interfaces.h2766
-rw-r--r--source4/libcli/raw/ioctl.h59
-rw-r--r--source4/libcli/raw/libcliraw.h384
-rw-r--r--source4/libcli/raw/rawacl.c163
-rw-r--r--source4/libcli/raw/rawdate.c82
-rw-r--r--source4/libcli/raw/raweas.c365
-rw-r--r--source4/libcli/raw/rawfile.c1017
-rw-r--r--source4/libcli/raw/rawfileinfo.c776
-rw-r--r--source4/libcli/raw/rawfsinfo.c336
-rw-r--r--source4/libcli/raw/rawioctl.c173
-rw-r--r--source4/libcli/raw/rawlpq.c49
-rw-r--r--source4/libcli/raw/rawnegotiate.c206
-rw-r--r--source4/libcli/raw/rawnotify.c168
-rw-r--r--source4/libcli/raw/rawreadwrite.c349
-rw-r--r--source4/libcli/raw/rawrequest.c1029
-rw-r--r--source4/libcli/raw/rawsearch.c841
-rw-r--r--source4/libcli/raw/rawsetfileinfo.c492
-rw-r--r--source4/libcli/raw/rawshadow.c82
-rw-r--r--source4/libcli/raw/rawtrans.c961
-rw-r--r--source4/libcli/raw/request.h78
-rw-r--r--source4/libcli/raw/signing.h43
-rw-r--r--source4/libcli/raw/smb.h622
-rw-r--r--source4/libcli/raw/smb_signing.c407
-rw-r--r--source4/libcli/raw/trans2.h476
-rw-r--r--source4/libcli/resolve/bcast.c106
-rw-r--r--source4/libcli/resolve/dns_ex.c541
-rw-r--r--source4/libcli/resolve/host.c60
-rw-r--r--source4/libcli/resolve/nbtlist.c224
-rw-r--r--source4/libcli/resolve/resolve.c283
-rw-r--r--source4/libcli/resolve/resolve.h53
-rw-r--r--source4/libcli/resolve/resolve_lp.c46
-rw-r--r--source4/libcli/resolve/testsuite.c90
-rw-r--r--source4/libcli/resolve/wins.c82
-rw-r--r--source4/libcli/security/access_check.c155
-rw-r--r--source4/libcli/security/config.mk10
-rw-r--r--source4/libcli/security/privilege.c241
-rw-r--r--source4/libcli/security/sddl.c598
-rw-r--r--source4/libcli/security/security.h35
-rw-r--r--source4/libcli/security/security_descriptor.c533
-rw-r--r--source4/libcli/security/security_token.c169
-rw-r--r--source4/libcli/security/tests/bindings.py97
-rw-r--r--source4/libcli/security/tests/sddl.c105
-rw-r--r--source4/libcli/smb2/break.c74
-rw-r--r--source4/libcli/smb2/cancel.c77
-rw-r--r--source4/libcli/smb2/close.c80
-rw-r--r--source4/libcli/smb2/config.mk10
-rw-r--r--source4/libcli/smb2/connect.c311
-rw-r--r--source4/libcli/smb2/create.c419
-rw-r--r--source4/libcli/smb2/find.c180
-rw-r--r--source4/libcli/smb2/flush.c70
-rw-r--r--source4/libcli/smb2/getinfo.c220
-rw-r--r--source4/libcli/smb2/ioctl.c109
-rw-r--r--source4/libcli/smb2/keepalive.c65
-rw-r--r--source4/libcli/smb2/lock.c82
-rw-r--r--source4/libcli/smb2/logoff.c69
-rw-r--r--source4/libcli/smb2/negprot.c113
-rw-r--r--source4/libcli/smb2/notify.c114
-rw-r--r--source4/libcli/smb2/read.c87
-rw-r--r--source4/libcli/smb2/request.c737
-rw-r--r--source4/libcli/smb2/session.c274
-rw-r--r--source4/libcli/smb2/setinfo.c122
-rw-r--r--source4/libcli/smb2/signing.c117
-rw-r--r--source4/libcli/smb2/smb2.h303
-rw-r--r--source4/libcli/smb2/smb2_calls.h111
-rw-r--r--source4/libcli/smb2/tcon.c112
-rw-r--r--source4/libcli/smb2/tdis.c65
-rw-r--r--source4/libcli/smb2/transport.c417
-rw-r--r--source4/libcli/smb2/util.c216
-rw-r--r--source4/libcli/smb2/write.c81
-rw-r--r--source4/libcli/smb_composite/appendacl.c313
-rw-r--r--source4/libcli/smb_composite/connect.c520
-rw-r--r--source4/libcli/smb_composite/fetchfile.c192
-rw-r--r--source4/libcli/smb_composite/fsinfo.c210
-rw-r--r--source4/libcli/smb_composite/loadfile.c293
-rw-r--r--source4/libcli/smb_composite/savefile.c288
-rw-r--r--source4/libcli/smb_composite/sesssetup.c570
-rw-r--r--source4/libcli/smb_composite/smb2.c370
-rw-r--r--source4/libcli/smb_composite/smb_composite.h195
-rw-r--r--source4/libcli/smbc/README1
-rw-r--r--source4/libcli/util/clilsa.c359
-rw-r--r--source4/libcli/util/errormap.c1408
-rw-r--r--source4/libcli/util/nterr.c898
-rw-r--r--source4/libcli/util/pyerrors.h45
-rw-r--r--source4/libcli/wbclient/config.mk5
-rw-r--r--source4/libcli/wbclient/wbclient.c210
-rw-r--r--source4/libcli/wbclient/wbclient.h50
-rw-r--r--source4/libcli/wrepl/winsrepl.c871
-rw-r--r--source4/libcli/wrepl/winsrepl.h162
-rw-r--r--source4/libnet/composite.h56
-rw-r--r--source4/libnet/config.mk18
-rw-r--r--source4/libnet/groupinfo.c364
-rw-r--r--source4/libnet/groupinfo.h54
-rw-r--r--source4/libnet/groupman.c314
-rw-r--r--source4/libnet/groupman.h46
-rw-r--r--source4/libnet/libnet.c56
-rw-r--r--source4/libnet/libnet.h78
-rw-r--r--source4/libnet/libnet_become_dc.c3032
-rw-r--r--source4/libnet/libnet_become_dc.h146
-rw-r--r--source4/libnet/libnet_domain.c1259
-rw-r--r--source4/libnet/libnet_domain.h70
-rw-r--r--source4/libnet/libnet_group.c745
-rw-r--r--source4/libnet/libnet_group.h74
-rw-r--r--source4/libnet/libnet_join.c1235
-rw-r--r--source4/libnet/libnet_join.h100
-rw-r--r--source4/libnet/libnet_lookup.c417
-rw-r--r--source4/libnet/libnet_lookup.h69
-rw-r--r--source4/libnet/libnet_passwd.c711
-rw-r--r--source4/libnet/libnet_passwd.h137
-rw-r--r--source4/libnet/libnet_rpc.c978
-rw-r--r--source4/libnet/libnet_rpc.h72
-rw-r--r--source4/libnet/libnet_samdump.c205
-rw-r--r--source4/libnet/libnet_samdump_keytab.c132
-rw-r--r--source4/libnet/libnet_samsync.c412
-rw-r--r--source4/libnet/libnet_samsync.h84
-rw-r--r--source4/libnet/libnet_samsync_ldb.c1251
-rw-r--r--source4/libnet/libnet_share.c208
-rw-r--r--source4/libnet/libnet_share.h70
-rw-r--r--source4/libnet/libnet_site.c270
-rw-r--r--source4/libnet/libnet_site.h35
-rw-r--r--source4/libnet/libnet_time.c123
-rw-r--r--source4/libnet/libnet_time.h46
-rw-r--r--source4/libnet/libnet_unbecome_dc.c736
-rw-r--r--source4/libnet/libnet_unbecome_dc.h31
-rw-r--r--source4/libnet/libnet_user.c1209
-rw-r--r--source4/libnet/libnet_user.h156
-rw-r--r--source4/libnet/libnet_vampire.c720
-rw-r--r--source4/libnet/libnet_vampire.h40
-rw-r--r--source4/libnet/prereq_domain.c142
-rw-r--r--source4/libnet/py_net.c88
-rw-r--r--source4/libnet/userinfo.c363
-rw-r--r--source4/libnet/userinfo.h54
-rw-r--r--source4/libnet/userman.c892
-rw-r--r--source4/libnet/userman.h106
-rw-r--r--source4/librpc/config.mk758
-rw-r--r--source4/librpc/dcerpc.pc.in11
-rw-r--r--source4/librpc/dcerpc_atsvc.pc.in11
-rw-r--r--source4/librpc/dcerpc_samr.pc.in11
-rw-r--r--source4/librpc/idl-deps.pl37
-rw-r--r--source4/librpc/idl/IDL_LICENSE.txt9
-rw-r--r--source4/librpc/idl/dcerpc.idl306
-rw-r--r--source4/librpc/idl/irpc.idl144
-rw-r--r--source4/librpc/idl/nfs4acl.idl42
-rw-r--r--source4/librpc/idl/notify.idl58
-rw-r--r--source4/librpc/idl/ntp_signd.idl44
-rw-r--r--source4/librpc/idl/opendb.idl46
-rw-r--r--source4/librpc/idl/sasl_helpers.idl22
-rw-r--r--source4/librpc/idl/schannel.idl44
-rw-r--r--source4/librpc/idl/server_id.idl12
-rw-r--r--source4/librpc/idl/winbind.idl68
-rw-r--r--source4/librpc/idl/wins.idl27
-rw-r--r--source4/librpc/idl/winsrepl.idl175
-rw-r--r--source4/librpc/idl/winstation.idl13
-rw-r--r--source4/librpc/ndr/ndr_string.c711
-rw-r--r--source4/librpc/ndr/py_security.c394
-rw-r--r--source4/librpc/rpc/dcerpc.c1697
-rw-r--r--source4/librpc/rpc/dcerpc.h390
-rw-r--r--source4/librpc/rpc/dcerpc.py20
-rw-r--r--source4/librpc/rpc/dcerpc_auth.c398
-rw-r--r--source4/librpc/rpc/dcerpc_connect.c946
-rw-r--r--source4/librpc/rpc/dcerpc_schannel.c413
-rw-r--r--source4/librpc/rpc/dcerpc_secondary.c336
-rw-r--r--source4/librpc/rpc/dcerpc_smb.c588
-rw-r--r--source4/librpc/rpc/dcerpc_smb2.c512
-rw-r--r--source4/librpc/rpc/dcerpc_sock.c672
-rw-r--r--source4/librpc/rpc/dcerpc_util.c748
-rw-r--r--source4/librpc/rpc/pyrpc.c493
-rw-r--r--source4/librpc/rpc/pyrpc.h74
-rwxr-xr-xsource4/librpc/scripts/build_idl.sh37
-rw-r--r--source4/librpc/tests/binding_string.c172
-rw-r--r--source4/librpc/tests/samr-CreateUser-in.datbin0 -> 60 bytes
-rw-r--r--source4/librpc/tests/samr-CreateUser-out.datbin0 -> 32 bytes
-rwxr-xr-xsource4/librpc/tests/test_ndrdump.sh20
-rw-r--r--source4/main.mk54
-rw-r--r--source4/min_versions.m46
-rw-r--r--source4/nbt_server/config.mk77
-rw-r--r--source4/nbt_server/defense.c79
-rw-r--r--source4/nbt_server/dgram/browse.c85
-rw-r--r--source4/nbt_server/dgram/netlogon.c210
-rw-r--r--source4/nbt_server/dgram/ntlogon.c121
-rw-r--r--source4/nbt_server/dgram/request.c153
-rw-r--r--source4/nbt_server/interfaces.c419
-rw-r--r--source4/nbt_server/irpc.c210
-rw-r--r--source4/nbt_server/nbt_server.c97
-rw-r--r--source4/nbt_server/nbt_server.h94
-rw-r--r--source4/nbt_server/nodestatus.c126
-rw-r--r--source4/nbt_server/packet.c343
-rw-r--r--source4/nbt_server/query.c102
-rw-r--r--source4/nbt_server/register.c293
-rw-r--r--source4/nbt_server/wins/wins_dns_proxy.c98
-rw-r--r--source4/nbt_server/wins/wins_hook.c94
-rw-r--r--source4/nbt_server/wins/wins_ldb.c121
-rw-r--r--source4/nbt_server/wins/winsclient.c259
-rw-r--r--source4/nbt_server/wins/winsdb.c1039
-rw-r--r--source4/nbt_server/wins/winsdb.h81
-rw-r--r--source4/nbt_server/wins/winsserver.c1065
-rw-r--r--source4/nbt_server/wins/winsserver.h66
-rw-r--r--source4/nbt_server/wins/winswack.c387
-rw-r--r--source4/ntp_signd/README7
-rw-r--r--source4/ntp_signd/config.mk14
-rw-r--r--source4/ntp_signd/ntp-dev-4.2.5p125.diff579
-rw-r--r--source4/ntp_signd/ntp_signd.c397
-rw-r--r--source4/ntptr/config.mk27
-rw-r--r--source4/ntptr/ntptr.h240
-rw-r--r--source4/ntptr/ntptr_base.c151
-rw-r--r--source4/ntptr/ntptr_interface.c604
-rw-r--r--source4/ntptr/simple_ldb/ntptr_simple_ldb.c891
-rw-r--r--source4/ntvfs/README26
-rw-r--r--source4/ntvfs/cifs/README40
-rw-r--r--source4/ntvfs/cifs/vfs_cifs.c1158
-rw-r--r--source4/ntvfs/cifs_posix_cli/README12
-rw-r--r--source4/ntvfs/cifs_posix_cli/cifsposix.h38
-rw-r--r--source4/ntvfs/cifs_posix_cli/svfs_util.c188
-rw-r--r--source4/ntvfs/cifs_posix_cli/vfs_cifs_posix.c1081
-rw-r--r--source4/ntvfs/common/brlock.c127
-rw-r--r--source4/ntvfs/common/brlock.h55
-rw-r--r--source4/ntvfs/common/brlock_tdb.c737
-rw-r--r--source4/ntvfs/common/config.mk11
-rw-r--r--source4/ntvfs/common/init.c32
-rw-r--r--source4/ntvfs/common/notify.c672
-rw-r--r--source4/ntvfs/common/ntvfs_common.h32
-rw-r--r--source4/ntvfs/common/opendb.c200
-rw-r--r--source4/ntvfs/common/opendb.h60
-rw-r--r--source4/ntvfs/common/opendb_tdb.c905
-rw-r--r--source4/ntvfs/config.mk103
-rw-r--r--source4/ntvfs/ipc/README5
-rw-r--r--source4/ntvfs/ipc/ipc.h0
-rw-r--r--source4/ntvfs/ipc/ipc_rap.c511
-rw-r--r--source4/ntvfs/ipc/np_echo.c0
-rw-r--r--source4/ntvfs/ipc/rap_server.c89
-rw-r--r--source4/ntvfs/ipc/vfs_ipc.c953
-rw-r--r--source4/ntvfs/nbench/README13
-rw-r--r--source4/ntvfs/nbench/vfs_nbench.c972
-rw-r--r--source4/ntvfs/ntvfs.h339
-rw-r--r--source4/ntvfs/ntvfs_base.c229
-rw-r--r--source4/ntvfs/ntvfs_generic.c1553
-rw-r--r--source4/ntvfs/ntvfs_interface.c716
-rw-r--r--source4/ntvfs/ntvfs_util.c202
-rw-r--r--source4/ntvfs/posix/config.m436
-rw-r--r--source4/ntvfs/posix/config.mk79
-rw-r--r--source4/ntvfs/posix/pvfs_acl.c851
-rw-r--r--source4/ntvfs/posix/pvfs_acl_nfs4.c212
-rw-r--r--source4/ntvfs/posix/pvfs_acl_xattr.c104
-rw-r--r--source4/ntvfs/posix/pvfs_aio.c166
-rw-r--r--source4/ntvfs/posix/pvfs_dirlist.c403
-rw-r--r--source4/ntvfs/posix/pvfs_fileinfo.c159
-rw-r--r--source4/ntvfs/posix/pvfs_flush.c80
-rw-r--r--source4/ntvfs/posix/pvfs_fsinfo.c208
-rw-r--r--source4/ntvfs/posix/pvfs_ioctl.c82
-rw-r--r--source4/ntvfs/posix/pvfs_lock.c408
-rw-r--r--source4/ntvfs/posix/pvfs_mkdir.c196
-rw-r--r--source4/ntvfs/posix/pvfs_notify.c285
-rw-r--r--source4/ntvfs/posix/pvfs_open.c2024
-rw-r--r--source4/ntvfs/posix/pvfs_oplock.c298
-rw-r--r--source4/ntvfs/posix/pvfs_qfileinfo.c450
-rw-r--r--source4/ntvfs/posix/pvfs_read.c112
-rw-r--r--source4/ntvfs/posix/pvfs_rename.c665
-rw-r--r--source4/ntvfs/posix/pvfs_resolve.c806
-rw-r--r--source4/ntvfs/posix/pvfs_search.c862
-rw-r--r--source4/ntvfs/posix/pvfs_seek.c65
-rw-r--r--source4/ntvfs/posix/pvfs_setfileinfo.c876
-rw-r--r--source4/ntvfs/posix/pvfs_shortname.c699
-rw-r--r--source4/ntvfs/posix/pvfs_streams.c542
-rw-r--r--source4/ntvfs/posix/pvfs_unlink.c276
-rw-r--r--source4/ntvfs/posix/pvfs_util.c202
-rw-r--r--source4/ntvfs/posix/pvfs_wait.c201
-rw-r--r--source4/ntvfs/posix/pvfs_write.c155
-rw-r--r--source4/ntvfs/posix/pvfs_xattr.c481
-rw-r--r--source4/ntvfs/posix/vfs_posix.c391
-rw-r--r--source4/ntvfs/posix/vfs_posix.h293
-rw-r--r--source4/ntvfs/posix/xattr_system.c146
-rw-r--r--source4/ntvfs/posix/xattr_tdb.c234
-rw-r--r--source4/ntvfs/print/README3
-rw-r--r--source4/ntvfs/print/vfs_print.c125
-rw-r--r--source4/ntvfs/simple/README10
-rw-r--r--source4/ntvfs/simple/svfs.h38
-rw-r--r--source4/ntvfs/simple/svfs_util.c185
-rw-r--r--source4/ntvfs/simple/vfs_simple.c1078
-rw-r--r--source4/ntvfs/smb2/vfs_smb2.c851
-rw-r--r--source4/ntvfs/sysdep/README5
-rw-r--r--source4/ntvfs/sysdep/config.m427
-rw-r--r--source4/ntvfs/sysdep/config.mk27
-rw-r--r--source4/ntvfs/sysdep/inotify.c422
-rw-r--r--source4/ntvfs/sysdep/sys_lease.c145
-rw-r--r--source4/ntvfs/sysdep/sys_lease.h66
-rw-r--r--source4/ntvfs/sysdep/sys_lease_linux.c213
-rw-r--r--source4/ntvfs/sysdep/sys_notify.c147
-rw-r--r--source4/ntvfs/sysdep/sys_notify.h53
-rw-r--r--source4/ntvfs/unixuid/config.m41
-rw-r--r--source4/ntvfs/unixuid/config.mk10
-rw-r--r--source4/ntvfs/unixuid/vfs_unixuid.c727
-rw-r--r--source4/param/README4
-rw-r--r--source4/param/config.mk64
-rw-r--r--source4/param/generic.c294
-rw-r--r--source4/param/loadparm.c2724
-rw-r--r--source4/param/loadparm.h99
-rw-r--r--source4/param/param.h442
-rw-r--r--source4/param/provision.c143
-rw-r--r--source4/param/provision.h51
-rw-r--r--source4/param/pyparam.c385
-rw-r--r--source4/param/pyparam.h25
-rw-r--r--source4/param/samba-hostconfig.pc.in10
-rw-r--r--source4/param/secrets.c196
-rw-r--r--source4/param/secrets.h46
-rw-r--r--source4/param/share.c156
-rw-r--r--source4/param/share.h137
-rw-r--r--source4/param/share_classic.c362
-rw-r--r--source4/param/share_ldb.c592
-rw-r--r--source4/param/tests/bindings.py52
-rw-r--r--source4/param/tests/loadparm.c167
-rw-r--r--source4/param/tests/share.c215
-rw-r--r--source4/param/util.c298
-rw-r--r--source4/rpc_server/browser/dcesrv_browser.c172
-rw-r--r--source4/rpc_server/common/common.h45
-rw-r--r--source4/rpc_server/common/server_info.c197
-rw-r--r--source4/rpc_server/common/share_info.c113
-rw-r--r--source4/rpc_server/config.mk226
-rw-r--r--source4/rpc_server/dcerpc_server.c1512
-rw-r--r--source4/rpc_server/dcerpc_server.h376
-rw-r--r--source4/rpc_server/dcesrv_auth.c539
-rw-r--r--source4/rpc_server/dcesrv_mgmt.c102
-rw-r--r--source4/rpc_server/drsuapi/dcesrv_drsuapi.c825
-rw-r--r--source4/rpc_server/drsuapi/dcesrv_drsuapi.h37
-rw-r--r--source4/rpc_server/echo/rpc_echo.c204
-rw-r--r--source4/rpc_server/epmapper/rpc_epmapper.c267
-rw-r--r--source4/rpc_server/handles.c90
-rw-r--r--source4/rpc_server/lsa/dcesrv_lsa.c3345
-rw-r--r--source4/rpc_server/lsa/lsa.h68
-rw-r--r--source4/rpc_server/lsa/lsa_init.c250
-rw-r--r--source4/rpc_server/lsa/lsa_lookup.c1007
-rw-r--r--source4/rpc_server/netlogon/dcerpc_netlogon.c1490
-rw-r--r--source4/rpc_server/remote/README38
-rw-r--r--source4/rpc_server/remote/dcesrv_remote.c327
-rw-r--r--source4/rpc_server/samr/dcesrv_samr.c4346
-rw-r--r--source4/rpc_server/samr/dcesrv_samr.h69
-rw-r--r--source4/rpc_server/samr/samr_password.c628
-rw-r--r--source4/rpc_server/service_rpc.c458
-rw-r--r--source4/rpc_server/spoolss/dcesrv_spoolss.c1594
-rw-r--r--source4/rpc_server/srvsvc/dcesrv_srvsvc.c2323
-rw-r--r--source4/rpc_server/srvsvc/srvsvc_ntvfs.c139
-rw-r--r--source4/rpc_server/unixinfo/dcesrv_unixinfo.c241
-rw-r--r--source4/rpc_server/winreg/README3
-rw-r--r--source4/rpc_server/winreg/rpc_winreg.c697
-rw-r--r--source4/rpc_server/wkssvc/dcesrv_wkssvc.c418
-rwxr-xr-xsource4/script/buildtree.pl40
-rwxr-xr-xsource4/script/configure_check_unused.pl124
-rwxr-xr-xsource4/script/depfilter.py50
-rwxr-xr-xsource4/script/extract_allparms.sh2
-rwxr-xr-xsource4/script/find_unused_macros.pl38
-rwxr-xr-xsource4/script/find_unused_makefilevars.pl55
-rwxr-xr-xsource4/script/find_unused_options.sh37
-rwxr-xr-xsource4/script/findstatic.pl70
-rwxr-xr-xsource4/script/installdat.sh23
-rwxr-xr-xsource4/script/installdirs.sh17
-rwxr-xr-xsource4/script/installheader.pl109
-rwxr-xr-xsource4/script/installlib.sh32
-rwxr-xr-xsource4/script/installman.sh30
-rwxr-xr-xsource4/script/installmisc.sh27
-rwxr-xr-xsource4/script/installpc.sh16
-rwxr-xr-xsource4/script/installswat.sh37
-rwxr-xr-xsource4/script/minimal_includes.pl150
-rwxr-xr-xsource4/script/mkinstalldirs38
-rwxr-xr-xsource4/script/mkproto.pl281
-rwxr-xr-xsource4/script/mkrelease.sh43
-rwxr-xr-xsource4/script/mkversion.sh133
-rwxr-xr-xsource4/script/pkg-config145
-rwxr-xr-xsource4/script/revert.sh18
-rwxr-xr-xsource4/script/uninstalllib.sh35
-rwxr-xr-xsource4/script/uninstallman.sh27
-rwxr-xr-xsource4/script/update-proto.pl242
-rwxr-xr-xsource4/scripting/bin/autoidl161
-rwxr-xr-xsource4/scripting/bin/epdump24
-rwxr-xr-xsource4/scripting/bin/minschema581
-rwxr-xr-xsource4/scripting/bin/mymachinepw59
-rwxr-xr-xsource4/scripting/bin/rpcclient305
-rwxr-xr-xsource4/scripting/bin/samba3dump167
-rwxr-xr-xsource4/scripting/bin/smbstatus83
-rwxr-xr-xsource4/scripting/bin/subunitrun47
-rw-r--r--source4/scripting/python/STATUS14
-rw-r--r--source4/scripting/python/config.mk35
-rw-r--r--source4/scripting/python/examples/netbios.py28
-rwxr-xr-xsource4/scripting/python/examples/samr.py117
-rwxr-xr-xsource4/scripting/python/examples/winreg.py87
-rw-r--r--source4/scripting/python/modules.c70
-rw-r--r--source4/scripting/python/modules.h28
-rw-r--r--source4/scripting/python/pyglue.c274
-rw-r--r--source4/scripting/python/samba/__init__.py242
-rw-r--r--source4/scripting/python/samba/getopt.py116
-rw-r--r--source4/scripting/python/samba/hostconfig.py33
-rw-r--r--source4/scripting/python/samba/idmap.py82
-rw-r--r--source4/scripting/python/samba/ndr.py28
-rw-r--r--source4/scripting/python/samba/provision.py1682
-rw-r--r--source4/scripting/python/samba/samba3.py792
-rw-r--r--source4/scripting/python/samba/samdb.py245
-rw-r--r--source4/scripting/python/samba/tests/__init__.py98
-rw-r--r--source4/scripting/python/samba/tests/dcerpc/__init__.py0
-rw-r--r--source4/scripting/python/samba/tests/dcerpc/bare.py48
-rw-r--r--source4/scripting/python/samba/tests/dcerpc/registry.py50
-rw-r--r--source4/scripting/python/samba/tests/dcerpc/rpcecho.py69
-rw-r--r--source4/scripting/python/samba/tests/dcerpc/sam.py46
-rw-r--r--source4/scripting/python/samba/tests/dcerpc/unix.py37
-rw-r--r--source4/scripting/python/samba/tests/provision.py124
-rw-r--r--source4/scripting/python/samba/tests/samba3.py232
-rw-r--r--source4/scripting/python/samba/tests/samdb.py97
-rw-r--r--source4/scripting/python/samba/tests/upgrade.py37
-rwxr-xr-xsource4/scripting/python/samba/torture/pytorture51
-rw-r--r--source4/scripting/python/samba/torture/spoolss.py437
-rwxr-xr-xsource4/scripting/python/samba/torture/torture_samr.py221
-rwxr-xr-xsource4/scripting/python/samba/torture/torture_tdb.py90
-rwxr-xr-xsource4/scripting/python/samba/torture/winreg.py165
-rw-r--r--source4/scripting/python/samba/upgrade.py437
-rw-r--r--source4/scripting/python/uuidmodule.c58
-rw-r--r--source4/selftest/config.mk89
-rw-r--r--source4/selftest/knownfail53
-rw-r--r--source4/selftest/quick38
-rw-r--r--source4/selftest/skip61
-rw-r--r--source4/selftest/slow7
-rwxr-xr-xsource4/selftest/test_w2k3.sh48
-rwxr-xr-xsource4/selftest/test_w2k3_file.sh44
-rwxr-xr-xsource4/selftest/test_win.sh42
-rwxr-xr-xsource4/selftest/tests.sh433
-rwxr-xr-xsource4/selftest/tests_win.sh30
-rwxr-xr-xsource4/selftest/tests_win2k3_dc.sh22
-rw-r--r--source4/selftest/win/README121
-rw-r--r--source4/selftest/win/VMHost.pm359
-rw-r--r--source4/selftest/win/common.exp521
-rw-r--r--source4/selftest/win/test_win.conf83
-rw-r--r--source4/selftest/win/vm_get_ip.pl48
-rw-r--r--source4/selftest/win/vm_load_snapshot.pl46
-rwxr-xr-xsource4/selftest/win/wintest_2k3_dc.sh114
-rwxr-xr-xsource4/selftest/win/wintest_base.sh67
-rw-r--r--source4/selftest/win/wintest_client.exp95
-rwxr-xr-xsource4/selftest/win/wintest_client.sh26
-rwxr-xr-xsource4/selftest/win/wintest_functions.sh54
-rwxr-xr-xsource4/selftest/win/wintest_net.sh62
-rwxr-xr-xsource4/selftest/win/wintest_raw.sh68
-rw-r--r--source4/selftest/win/wintest_remove.exp71
-rwxr-xr-xsource4/selftest/win/wintest_rpc.sh66
-rw-r--r--source4/selftest/win/wintest_setup.exp104
-rw-r--r--source4/setup/DB_CONFIG6
-rw-r--r--source4/setup/ad-schema/MS-AD_Schema_Attributes_v20080618.txt15681
-rw-r--r--source4/setup/ad-schema/MS-AD_Schema_Classes_v20080618.txt3418
-rw-r--r--source4/setup/ad-schema/licence.txt54
-rw-r--r--source4/setup/aggregate_schema.ldif3
-rw-r--r--source4/setup/cn=replicator.ldif12
-rw-r--r--source4/setup/cn=samba-admin.ldif12
-rw-r--r--source4/setup/cn=samba.ldif11
-rw-r--r--source4/setup/display_specifiers.ldif111
-rwxr-xr-xsource4/setup/enableaccount77
-rw-r--r--source4/setup/fedora-ds-init.ldif27
-rw-r--r--source4/setup/fedorads-partitions.ldif30
-rw-r--r--source4/setup/fedorads.inf29
-rw-r--r--source4/setup/idmap_init.ldif4
-rw-r--r--source4/setup/krb5.conf17
-rw-r--r--source4/setup/memberof.conf9
-rw-r--r--source4/setup/mmr_serverids.conf1
-rw-r--r--source4/setup/mmr_syncrepl.conf12
-rw-r--r--source4/setup/modules.conf1
-rw-r--r--source4/setup/named.conf67
-rw-r--r--source4/setup/named.txt46
-rwxr-xr-xsource4/setup/newuser60
-rw-r--r--source4/setup/olc_acl.conf4
-rw-r--r--source4/setup/olc_mmr.conf3
-rw-r--r--source4/setup/olc_pass.conf3
-rw-r--r--source4/setup/olc_seed.ldif16
-rw-r--r--source4/setup/olc_serverid.conf1
-rw-r--r--source4/setup/olc_syncrepl.conf13
-rw-r--r--source4/setup/olc_syncrepl_seed.conf5
-rw-r--r--source4/setup/phpldapadmin-config.php28
-rw-r--r--source4/setup/prefixMap.txt41
-rwxr-xr-xsource4/setup/provision192
-rwxr-xr-xsource4/setup/provision-backend114
-rw-r--r--source4/setup/provision.ldif84
-rw-r--r--source4/setup/provision.reg45
-rw-r--r--source4/setup/provision.smb.conf.dc15
-rw-r--r--source4/setup/provision.smb.conf.member7
-rw-r--r--source4/setup/provision.smb.conf.standalone7
-rw-r--r--source4/setup/provision.zone50
-rw-r--r--source4/setup/provision_basedn.ldif8
-rw-r--r--source4/setup/provision_basedn_modify.ldif85
-rw-r--r--source4/setup/provision_computers_add.ldif3
-rw-r--r--source4/setup/provision_computers_modify.ldif13
-rw-r--r--source4/setup/provision_configuration.ldif94
-rw-r--r--source4/setup/provision_configuration_basedn.ldif8
-rw-r--r--source4/setup/provision_configuration_basedn_modify.ldif7
-rw-r--r--source4/setup/provision_group_policy.ldif25
-rw-r--r--source4/setup/provision_init.ldif54
-rw-r--r--source4/setup/provision_partitions.ldif13
-rw-r--r--source4/setup/provision_rootdse_add.ldif17
-rw-r--r--source4/setup/provision_rootdse_modify.ldif5
-rw-r--r--source4/setup/provision_schema_basedn.ldif8
-rw-r--r--source4/setup/provision_schema_basedn_modify.ldif14
-rw-r--r--source4/setup/provision_self_join.ldif62
-rw-r--r--source4/setup/provision_templates.ldif43
-rw-r--r--source4/setup/provision_templates_init.ldif10
-rw-r--r--source4/setup/provision_users.ldif588
-rw-r--r--source4/setup/provision_users_add.ldif3
-rw-r--r--source4/setup/provision_users_modify.ldif13
-rw-r--r--source4/setup/refint.conf3
-rw-r--r--source4/setup/schema-map-fedora-ds-1.031
-rw-r--r--source4/setup/schema-map-openldap-2.354
-rw-r--r--source4/setup/schema.ldif10375
-rw-r--r--source4/setup/schema_samba4.ldif373
-rw-r--r--source4/setup/secrets.ldif10
-rw-r--r--source4/setup/secrets_dc.ldif39
-rw-r--r--source4/setup/secrets_init.ldif15
-rw-r--r--source4/setup/secrets_sasl_ldap.ldif9
-rw-r--r--source4/setup/secrets_simple_ldap.ldif6
-rwxr-xr-xsource4/setup/setexpiry44
-rwxr-xr-xsource4/setup/setpassword77
-rw-r--r--source4/setup/share.ldif46
-rw-r--r--source4/setup/slapd.conf155
-rwxr-xr-xsource4/setup/tests/blackbox_newuser.sh32
-rwxr-xr-xsource4/setup/tests/blackbox_provision-backend.sh26
-rwxr-xr-xsource4/setup/tests/blackbox_provision.sh29
-rwxr-xr-xsource4/setup/tests/blackbox_setpassword.sh21
-rwxr-xr-xsource4/setup/upgrade83
-rw-r--r--source4/smb_server/blob.c793
-rw-r--r--source4/smb_server/config.mk42
-rw-r--r--source4/smb_server/handle.c144
-rw-r--r--source4/smb_server/management.c137
-rw-r--r--source4/smb_server/session.c169
-rw-r--r--source4/smb_server/smb/config.mk22
-rw-r--r--source4/smb_server/smb/negprot.c542
-rw-r--r--source4/smb_server/smb/nttrans.c810
-rw-r--r--source4/smb_server/smb/receive.c689
-rw-r--r--source4/smb_server/smb/reply.c2337
-rw-r--r--source4/smb_server/smb/request.c778
-rw-r--r--source4/smb_server/smb/search.c283
-rw-r--r--source4/smb_server/smb/service.c216
-rw-r--r--source4/smb_server/smb/sesssetup.c448
-rw-r--r--source4/smb_server/smb/signing.c173
-rw-r--r--source4/smb_server/smb/srvtime.c82
-rw-r--r--source4/smb_server/smb/trans2.c1428
-rw-r--r--source4/smb_server/smb2/config.mk19
-rw-r--r--source4/smb_server/smb2/fileinfo.c378
-rw-r--r--source4/smb_server/smb2/fileio.c539
-rw-r--r--source4/smb_server/smb2/find.c171
-rw-r--r--source4/smb_server/smb2/keepalive.c76
-rw-r--r--source4/smb_server/smb2/negprot.c292
-rw-r--r--source4/smb_server/smb2/receive.c654
-rw-r--r--source4/smb_server/smb2/sesssetup.c276
-rw-r--r--source4/smb_server/smb2/smb2_server.h187
-rw-r--r--source4/smb_server/smb2/tcon.c459
-rw-r--r--source4/smb_server/smb_samba3.c175
-rw-r--r--source4/smb_server/smb_server.c259
-rw-r--r--source4/smb_server/smb_server.h521
-rw-r--r--source4/smb_server/tcon.c209
-rw-r--r--source4/smbd/config.mk42
-rw-r--r--source4/smbd/pidfile.c121
-rw-r--r--source4/smbd/process_model.c131
-rw-r--r--source4/smbd/process_model.h85
-rw-r--r--source4/smbd/process_model.m460
-rw-r--r--source4/smbd/process_model.mk52
-rw-r--r--source4/smbd/process_prefork.c222
-rw-r--r--source4/smbd/process_single.c124
-rw-r--r--source4/smbd/process_standard.c239
-rw-r--r--source4/smbd/process_thread.c572
-rw-r--r--source4/smbd/samba.8.xml178
-rw-r--r--source4/smbd/server.c369
-rw-r--r--source4/smbd/service.c104
-rw-r--r--source4/smbd/service.h30
-rw-r--r--source4/smbd/service_named_pipe.c368
-rw-r--r--source4/smbd/service_stream.c369
-rw-r--r--source4/smbd/service_stream.h73
-rw-r--r--source4/smbd/service_task.c110
-rw-r--r--source4/smbd/service_task.h38
-rw-r--r--source4/static_deps.mk15
-rw-r--r--source4/torture/auth/ntlmssp.c157
-rw-r--r--source4/torture/auth/pac.c752
-rw-r--r--source4/torture/basic/aliases.c397
-rw-r--r--source4/torture/basic/attr.c183
-rw-r--r--source4/torture/basic/base.c1780
-rw-r--r--source4/torture/basic/charset.c210
-rw-r--r--source4/torture/basic/delaywrite.c2892
-rw-r--r--source4/torture/basic/delete.c1558
-rw-r--r--source4/torture/basic/denytest.c2033
-rw-r--r--source4/torture/basic/dir.c165
-rw-r--r--source4/torture/basic/disconnect.c174
-rw-r--r--source4/torture/basic/locking.c817
-rw-r--r--source4/torture/basic/mangle_test.c206
-rw-r--r--source4/torture/basic/misc.c948
-rw-r--r--source4/torture/basic/properties.c119
-rw-r--r--source4/torture/basic/rename.c98
-rw-r--r--source4/torture/basic/scanner.c629
-rw-r--r--source4/torture/basic/secleak.c78
-rw-r--r--source4/torture/basic/unlink.c92
-rw-r--r--source4/torture/basic/utable.c191
-rw-r--r--source4/torture/config.mk364
-rw-r--r--source4/torture/gentest.c3262
-rw-r--r--source4/torture/ldap/basic.c239
-rw-r--r--source4/torture/ldap/cldap.c497
-rw-r--r--source4/torture/ldap/cldapbench.c204
-rw-r--r--source4/torture/ldap/common.c117
-rw-r--r--source4/torture/ldap/schema.c409
-rw-r--r--source4/torture/ldap/uptodatevector.c177
-rw-r--r--source4/torture/ldb/ldb.c735
-rw-r--r--source4/torture/libnet/domain.c113
-rw-r--r--source4/torture/libnet/groupinfo.c123
-rw-r--r--source4/torture/libnet/groupman.c91
-rw-r--r--source4/torture/libnet/grouptest.h20
-rw-r--r--source4/torture/libnet/libnet.c70
-rw-r--r--source4/torture/libnet/libnet_BecomeDC.c709
-rw-r--r--source4/torture/libnet/libnet_domain.c449
-rw-r--r--source4/torture/libnet/libnet_group.c399
-rw-r--r--source4/torture/libnet/libnet_lookup.c189
-rw-r--r--source4/torture/libnet/libnet_rpc.c226
-rw-r--r--source4/torture/libnet/libnet_share.c236
-rw-r--r--source4/torture/libnet/libnet_user.c741
-rw-r--r--source4/torture/libnet/userinfo.c202
-rw-r--r--source4/torture/libnet/userman.c461
-rw-r--r--source4/torture/libnet/usertest.h38
-rw-r--r--source4/torture/libnet/utils.c301
-rw-r--r--source4/torture/libnet/utils.h45
-rw-r--r--source4/torture/local/config.mk57
-rw-r--r--source4/torture/local/dbspeed.c258
-rw-r--r--source4/torture/local/local.c90
-rw-r--r--source4/torture/local/torture.c86
-rw-r--r--source4/torture/locktest.c657
-rw-r--r--source4/torture/locktest2.c578
-rw-r--r--source4/torture/man/gentest.1.xml160
-rw-r--r--source4/torture/man/locktest.1.xml157
-rw-r--r--source4/torture/man/masktest.1.xml139
-rw-r--r--source4/torture/man/smbtorture.1.xml259
-rw-r--r--source4/torture/masktest.c391
-rw-r--r--source4/torture/nbench/nbench.c293
-rw-r--r--source4/torture/nbench/nbio.c997
-rw-r--r--source4/torture/nbt/browse.c54
-rw-r--r--source4/torture/nbt/dgram.c665
-rw-r--r--source4/torture/nbt/nbt.c67
-rw-r--r--source4/torture/nbt/query.c115
-rw-r--r--source4/torture/nbt/register.c176
-rw-r--r--source4/torture/nbt/wins.c477
-rw-r--r--source4/torture/nbt/winsbench.c301
-rw-r--r--source4/torture/nbt/winsreplication.c9705
-rw-r--r--source4/torture/ndr/atsvc.c214
-rw-r--r--source4/torture/ndr/dfs.c114
-rw-r--r--source4/torture/ndr/drsuapi.c304
-rw-r--r--source4/torture/ndr/epmap.c77
-rw-r--r--source4/torture/ndr/lsa.c2041
-rw-r--r--source4/torture/ndr/ndr.c287
-rw-r--r--source4/torture/ndr/ndr.h44
-rw-r--r--source4/torture/ndr/netlogon.c120
-rw-r--r--source4/torture/ndr/samr.c211
-rw-r--r--source4/torture/ndr/spoolss.c483
-rw-r--r--source4/torture/ndr/winreg.c575
-rw-r--r--source4/torture/rap/rap.c563
-rw-r--r--source4/torture/raw/acls.c2011
-rw-r--r--source4/torture/raw/chkpath.c394
-rw-r--r--source4/torture/raw/close.c177
-rw-r--r--source4/torture/raw/composite.c430
-rw-r--r--source4/torture/raw/context.c917
-rw-r--r--source4/torture/raw/eas.c489
-rw-r--r--source4/torture/raw/ioctl.c171
-rw-r--r--source4/torture/raw/lock.c1899
-rw-r--r--source4/torture/raw/lockbench.c418
-rw-r--r--source4/torture/raw/lookuprate.c319
-rw-r--r--source4/torture/raw/missing.txt160
-rw-r--r--source4/torture/raw/mkdir.c174
-rw-r--r--source4/torture/raw/mux.c361
-rw-r--r--source4/torture/raw/notify.c1631
-rw-r--r--source4/torture/raw/offline.c513
-rw-r--r--source4/torture/raw/open.c1680
-rw-r--r--source4/torture/raw/openbench.c487
-rw-r--r--source4/torture/raw/oplock.c3099
-rwxr-xr-xsource4/torture/raw/pingpong.c266
-rw-r--r--source4/torture/raw/qfileinfo.c865
-rw-r--r--source4/torture/raw/qfsinfo.c296
-rw-r--r--source4/torture/raw/raw.c87
-rw-r--r--source4/torture/raw/read.c953
-rw-r--r--source4/torture/raw/rename.c624
-rw-r--r--source4/torture/raw/samba3hide.c339
-rw-r--r--source4/torture/raw/samba3misc.c974
-rw-r--r--source4/torture/raw/search.c1391
-rw-r--r--source4/torture/raw/seek.c243
-rw-r--r--source4/torture/raw/setfileinfo.c703
-rw-r--r--source4/torture/raw/streams.c1694
-rw-r--r--source4/torture/raw/tconrate.c204
-rw-r--r--source4/torture/raw/unlink.c449
-rw-r--r--source4/torture/raw/write.c724
-rw-r--r--source4/torture/rpc/alter_context.c86
-rw-r--r--source4/torture/rpc/async_bind.c90
-rw-r--r--source4/torture/rpc/atsvc.c139
-rw-r--r--source4/torture/rpc/autoidl.c277
-rw-r--r--source4/torture/rpc/bench.c152
-rw-r--r--source4/torture/rpc/bind.c82
-rw-r--r--source4/torture/rpc/browser.c124
-rw-r--r--source4/torture/rpc/countcalls.c129
-rw-r--r--source4/torture/rpc/dfs.c665
-rw-r--r--source4/torture/rpc/drsuapi.c817
-rw-r--r--source4/torture/rpc/drsuapi.h36
-rw-r--r--source4/torture/rpc/drsuapi_cracknames.c1012
-rw-r--r--source4/torture/rpc/dssetup.c64
-rw-r--r--source4/torture/rpc/dssync.c914
-rw-r--r--source4/torture/rpc/echo.c452
-rw-r--r--source4/torture/rpc/epmapper.c275
-rw-r--r--source4/torture/rpc/eventlog.c459
-rw-r--r--source4/torture/rpc/frsapi.c275
-rw-r--r--source4/torture/rpc/handles.c582
-rw-r--r--source4/torture/rpc/initshutdown.c114
-rw-r--r--source4/torture/rpc/join.c90
-rw-r--r--source4/torture/rpc/lsa.c2768
-rw-r--r--source4/torture/rpc/lsa_lookup.c414
-rw-r--r--source4/torture/rpc/mgmt.c270
-rw-r--r--source4/torture/rpc/netlogon.c2372
-rw-r--r--source4/torture/rpc/netlogon.h6
-rw-r--r--source4/torture/rpc/ntsvcs.c189
-rw-r--r--source4/torture/rpc/object_uuid.c89
-rw-r--r--source4/torture/rpc/oxidresolve.c237
-rw-r--r--source4/torture/rpc/remact.c89
-rw-r--r--source4/torture/rpc/remote_pac.c337
-rw-r--r--source4/torture/rpc/rpc.c454
-rw-r--r--source4/torture/rpc/rpc.h88
-rw-r--r--source4/torture/rpc/samba3rpc.c3542
-rw-r--r--source4/torture/rpc/samlogon.c1890
-rw-r--r--source4/torture/rpc/samr.c6861
-rw-r--r--source4/torture/rpc/samr_accessmask.c669
-rw-r--r--source4/torture/rpc/samsync.c1715
-rw-r--r--source4/torture/rpc/scanner.c151
-rw-r--r--source4/torture/rpc/schannel.c865
-rw-r--r--source4/torture/rpc/session_key.c233
-rw-r--r--source4/torture/rpc/spoolss.c2058
-rw-r--r--source4/torture/rpc/spoolss_notify.c338
-rw-r--r--source4/torture/rpc/spoolss_win.c627
-rw-r--r--source4/torture/rpc/srvsvc.c1181
-rw-r--r--source4/torture/rpc/svcctl.c563
-rw-r--r--source4/torture/rpc/testjoin.c699
-rw-r--r--source4/torture/rpc/unixinfo.c144
-rw-r--r--source4/torture/rpc/winreg.c1925
-rw-r--r--source4/torture/rpc/wkssvc.c1451
-rw-r--r--source4/torture/smb2/config.mk29
-rw-r--r--source4/torture/smb2/connect.c268
-rw-r--r--source4/torture/smb2/create.c468
-rw-r--r--source4/torture/smb2/dir.c93
-rw-r--r--source4/torture/smb2/find.c220
-rw-r--r--source4/torture/smb2/getinfo.c236
-rw-r--r--source4/torture/smb2/lock.c545
-rw-r--r--source4/torture/smb2/maxwrite.c132
-rw-r--r--source4/torture/smb2/notify.c203
-rw-r--r--source4/torture/smb2/oplocks.c177
-rw-r--r--source4/torture/smb2/persistent_handles.c183
-rw-r--r--source4/torture/smb2/read.c245
-rw-r--r--source4/torture/smb2/scan.c248
-rw-r--r--source4/torture/smb2/setinfo.c300
-rw-r--r--source4/torture/smb2/smb2.c150
-rw-r--r--source4/torture/smb2/util.c408
-rw-r--r--source4/torture/smbiconv.c236
-rw-r--r--source4/torture/smbtorture.c673
-rw-r--r--source4/torture/smbtorture.h40
-rwxr-xr-xsource4/torture/tests/test_gentest.sh34
-rwxr-xr-xsource4/torture/tests/test_locktest.sh28
-rwxr-xr-xsource4/torture/tests/test_masktest.sh28
-rw-r--r--source4/torture/torture.c73
-rw-r--r--source4/torture/unix/unix.c40
-rw-r--r--source4/torture/unix/unix_info2.c469
-rw-r--r--source4/torture/unix/whoami.c339
-rw-r--r--source4/torture/util.h97
-rw-r--r--source4/torture/util_smb.c911
-rw-r--r--source4/torture/winbind/config.mk16
-rw-r--r--source4/torture/winbind/struct_based.c1046
-rw-r--r--source4/torture/winbind/winbind.c35
-rw-r--r--source4/utils/ad2oLschema.c442
-rw-r--r--source4/utils/config.mk107
-rw-r--r--source4/utils/getntacl.c121
-rw-r--r--source4/utils/man/ad2oLschema.1.xml87
-rw-r--r--source4/utils/man/getntacl.1.xml45
-rw-r--r--source4/utils/man/ntlm_auth.1.xml269
-rw-r--r--source4/utils/man/oLschema2ldif.1.xml79
-rw-r--r--source4/utils/net/config.mk27
-rw-r--r--source4/utils/net/net.c219
-rw-r--r--source4/utils/net/net.h39
-rw-r--r--source4/utils/net/net_join.c170
-rw-r--r--source4/utils/net/net_machinepw.c91
-rw-r--r--source4/utils/net/net_password.c171
-rw-r--r--source4/utils/net/net_time.c78
-rw-r--r--source4/utils/net/net_user.c125
-rw-r--r--source4/utils/net/net_vampire.c181
-rw-r--r--source4/utils/ntlm_auth.c1174
-rw-r--r--source4/utils/oLschema2ldif.c604
-rw-r--r--source4/utils/setntacl.c28
-rw-r--r--source4/utils/setnttoken.c28
-rw-r--r--source4/utils/testparm.c257
-rwxr-xr-xsource4/utils/tests/test_net.sh40
-rwxr-xr-xsource4/utils/tests/test_nmblookup.sh39
-rw-r--r--source4/web_server/config.mk14
-rw-r--r--source4/web_server/swat/__init__.py39
-rw-r--r--source4/web_server/web_server.c364
-rw-r--r--source4/web_server/web_server.h65
-rw-r--r--source4/web_server/wsgi.c391
-rw-r--r--source4/winbind/config.mk79
-rw-r--r--source4/winbind/idmap.c714
-rw-r--r--source4/winbind/idmap.h39
-rw-r--r--source4/winbind/wb_async_helpers.c458
-rw-r--r--source4/winbind/wb_async_helpers.h34
-rw-r--r--source4/winbind/wb_cmd_getdcname.c125
-rw-r--r--source4/winbind/wb_cmd_getgrgid.c177
-rw-r--r--source4/winbind/wb_cmd_getgrnam.c168
-rw-r--r--source4/winbind/wb_cmd_getpwent.c128
-rw-r--r--source4/winbind/wb_cmd_getpwnam.c196
-rw-r--r--source4/winbind/wb_cmd_getpwuid.c205
-rw-r--r--source4/winbind/wb_cmd_list_groups.c200
-rw-r--r--source4/winbind/wb_cmd_list_trustdom.c196
-rw-r--r--source4/winbind/wb_cmd_list_users.c198
-rw-r--r--source4/winbind/wb_cmd_lookupname.c121
-rw-r--r--source4/winbind/wb_cmd_lookupsid.c119
-rw-r--r--source4/winbind/wb_cmd_setpwent.c142
-rw-r--r--source4/winbind/wb_cmd_userdomgroups.c147
-rw-r--r--source4/winbind/wb_cmd_usersids.c193
-rw-r--r--source4/winbind/wb_connect_lsa.c135
-rw-r--r--source4/winbind/wb_connect_sam.c164
-rw-r--r--source4/winbind/wb_dom_info.c126
-rw-r--r--source4/winbind/wb_dom_info_trusted.c242
-rw-r--r--source4/winbind/wb_gid2sid.c108
-rw-r--r--source4/winbind/wb_init_domain.c434
-rw-r--r--source4/winbind/wb_irpc.c155
-rw-r--r--source4/winbind/wb_name2domain.c121
-rw-r--r--source4/winbind/wb_pam_auth.c270
-rw-r--r--source4/winbind/wb_sam_logon.c171
-rw-r--r--source4/winbind/wb_samba3_cmd.c1219
-rw-r--r--source4/winbind/wb_samba3_protocol.c299
-rw-r--r--source4/winbind/wb_server.c223
-rw-r--r--source4/winbind/wb_server.h169
-rw-r--r--source4/winbind/wb_setup_domains.c42
-rw-r--r--source4/winbind/wb_sid2domain.c212
-rw-r--r--source4/winbind/wb_sid2gid.c109
-rw-r--r--source4/winbind/wb_sid2uid.c109
-rw-r--r--source4/winbind/wb_sids2xids.c80
-rw-r--r--source4/winbind/wb_uid2sid.c110
-rw-r--r--source4/winbind/wb_utils.c49
-rw-r--r--source4/winbind/wb_xids2sids.c80
-rw-r--r--source4/wrepl_server/config.mk24
-rw-r--r--source4/wrepl_server/wrepl_apply_records.c1480
-rw-r--r--source4/wrepl_server/wrepl_in_call.c575
-rw-r--r--source4/wrepl_server/wrepl_in_connection.c323
-rw-r--r--source4/wrepl_server/wrepl_out_helpers.c1123
-rw-r--r--source4/wrepl_server/wrepl_out_helpers.h37
-rw-r--r--source4/wrepl_server/wrepl_out_pull.c142
-rw-r--r--source4/wrepl_server/wrepl_out_push.c144
-rw-r--r--source4/wrepl_server/wrepl_periodic.c118
-rw-r--r--source4/wrepl_server/wrepl_scavenging.c565
-rw-r--r--source4/wrepl_server/wrepl_server.c512
-rw-r--r--source4/wrepl_server/wrepl_server.h314
1914 files changed, 713248 insertions, 0 deletions
diff --git a/source4/.valgrind_suppressions b/source4/.valgrind_suppressions
new file mode 100644
index 0000000000..a09a4dcf7d
--- /dev/null
+++ b/source4/.valgrind_suppressions
@@ -0,0 +1,85 @@
+# add valgrind suppressions for the build farm here. Get the format
+# from the build farm log
+
+{
+ samba_dlopen1
+ Memcheck:Cond
+ obj:/lib/ld-2.3.6.so
+ obj:/lib/ld-2.3.6.so
+ obj:/lib/tls/libc-2.3.6.so
+ obj:/lib/ld-2.3.6.so
+ fun:_dl_open
+}
+
+{
+ samba_dlopen2
+ Memcheck:Cond
+ obj:/lib/ld-2.3.6.so
+ fun:_dl_open
+}
+
+{
+ samba_dlopen3
+ Memcheck:Addr4
+ obj:/lib/ld-2.3.6.so
+ obj:/lib/ld-2.3.6.so
+ obj:/lib/ld-2.3.6.so
+ obj:/lib/ld-2.3.6.so
+ obj:/lib/ld-2.3.6.so
+ obj:/lib/ld-2.3.6.so
+ obj:/lib/tls/libc-2.3.6.so
+ obj:/lib/ld-2.3.6.so
+ fun:_dl_open
+}
+
+{
+ samba_dlopen4
+ Memcheck:Cond
+ obj:/lib/ld-2.3.6.so
+ obj:/lib/tls/libc-2.3.6.so
+ obj:/lib/ld-2.3.6.so
+ fun:_dl_open
+ }
+
+{
+ samba_dlopen5
+ Memcheck:Addr4
+ obj:/lib/ld-2.3.6.so
+ obj:/lib/ld-2.3.6.so
+ obj:/lib/ld-2.3.6.so
+ obj:/lib/tls/libc-2.3.6.so
+ obj:/lib/ld-2.3.6.so
+ fun:_dl_open
+}
+
+{
+ samba_dlopen6
+ Memcheck:Cond
+ obj:/lib/ld-2.3.6.so
+ obj:/lib/ld-2.3.6.so
+ obj:/lib/ld-2.3.6.so
+ obj:/lib/tls/libc-2.3.6.so
+ obj:/lib/ld-2.3.6.so
+ fun:_dl_open
+}
+
+{
+ samba_dlopen7
+ Memcheck:Addr4
+ obj:/lib/ld-2.3.6.so
+ obj:/lib/ld-2.3.6.so
+ obj:/lib/tls/libc-2.3.6.so
+ obj:/lib/ld-2.3.6.so
+ fun:_dl_open
+}
+
+{
+ samba_libc_dlsym1
+ Memcheck:Addr4
+ obj:/lib/ld-2.3.6.so
+ obj:/lib/ld-2.3.6.so
+ obj:/lib/ld-2.3.6.so
+ obj:/lib/tls/libc-2.3.6.so
+ obj:/lib/ld-2.3.6.so
+ fun:__libc_dlsym
+}
diff --git a/source4/BUGS b/source4/BUGS
new file mode 100644
index 0000000000..1a9790ddd9
--- /dev/null
+++ b/source4/BUGS
@@ -0,0 +1,24 @@
+Samba4 alpha4 is not a final Samba release. That is more a reference
+to Samba4's lack of the features we expect you will need than a
+statement of code quality, but clearly it hasn't seen a broad
+deployment yet. If you were to upgrade Samba3 (or indeed Windows) to
+Samba4, you would find many things work, but that other key features
+you may have relied on simply are not there yet.
+
+For example, while Samba 3.0 is an excellent member of a Active
+Directory domain, Samba4 is happier as a domain controller, and it is
+in this role where it has seen deployment into production.
+
+Samba4 is subjected to an awesome battery of tests on an
+automated basis, we have found Samba4 to be very stable in it's
+behaviour. We have to recommend against upgrading production servers
+from Samba 3 to Samba 4 at this stage, because there may be the features on
+which you may rely that are not present, or the mapping of
+your configuration and user database may not be complete.
+
+If you are upgrading, or looking to develop, test or deploy Samba4, you should
+backup all configuration and data.
+
+We welcome your testing, please file bug reports at
+https://bugzilla.samba.org/, product: Samba4. Please include as much
+information as possible, such as GIT revision number and backtraces.
diff --git a/source4/Makefile b/source4/Makefile
new file mode 100644
index 0000000000..15b1b8ba40
--- /dev/null
+++ b/source4/Makefile
@@ -0,0 +1,309 @@
+#!gmake
+# The Samba 4 Makefile.
+# This file is *NOT* autogenerated.
+#
+.DEFAULT_GOAL := all
+
+default: all
+
+include mkconfig.mk
+
+pidldir := $(srcdir)/../pidl
+
+basedir = $(prefix)
+torturedir = ../lib/torture
+swatdir = $(datadir)/swat
+setupdir = $(datadir)/setup
+ncalrpcdir = $(localstatedir)/ncalrpc
+shliboutputdir = $(builddir)/bin/shared
+
+BNLD = $(LD)
+BNLD_FLAGS = $(LDFLAGS) $(SYS_LDFLAGS)
+
+HOSTCC_FLAGS = -D_SAMBA_HOSTCC_ $(CFLAGS)
+HOSTLD_FLAGS = $(LDFLAGS) $(SYS_LDFLAGS)
+
+$(srcdir)/version.h: $(srcdir)/VERSION
+ @$(SHELL) script/mkversion.sh VERSION $(srcdir)/version.h $(srcdir)/
+
+ifneq ($(automatic_dependencies),yes)
+ALL_PREDEP = basics
+.NOTPARALLEL:
+endif
+
+regen_version::
+ @$(SHELL) script/mkversion.sh VERSION $(srcdir)/version.h $(srcdir)/
+
+clean_pch::
+ @echo "Removing precompiled headers"
+ @-rm -f include/includes.h.gch
+
+pch:: clean_pch include/includes.h.gch
+
+.DEFAULT_GOAL := all
+
+ifneq ($(automatic_dependencies),yes)
+ALL_PREDEP = basics
+.NOTPARALLEL:
+endif
+
+include $(srcdir)/build/make/rules.mk
+include $(srcdir)/build/make/python.mk
+zlibsrcdir := ../lib/zlib
+dynconfigsrcdir := dynconfig
+heimdalsrcdir := heimdal
+dsdbsrcdir := dsdb
+smbdsrcdir := smbd
+clustersrcdir := cluster
+libnetsrcdir := libnet
+authsrcdir := auth
+nsswitchsrcdir := ../nsswitch
+libsrcdir := lib
+libsocketsrcdir := lib/socket
+libcharsetsrcdir := ../lib/util/charset
+ldb_sambasrcdir := lib/ldb-samba
+tdbsrcdir := ../lib/tdb
+ldbsrcdir := lib/ldb
+libtlssrcdir := lib/tls
+libregistrysrcdir := lib/registry
+smbreadlinesrcdir := lib/smbreadline
+libmessagingsrcdir := lib/messaging
+libteventsrcdir := ../lib/tevent
+libeventssrcdir := lib/events
+libcmdlinesrcdir := lib/cmdline
+poptsrcdir := ../lib/popt
+socketwrappersrcdir := ../lib/socket_wrapper
+nsswrappersrcdir := ../lib/nss_wrapper
+appwebsrcdir := lib/appweb
+libstreamsrcdir := lib/stream
+libutilsrcdir := ../lib/util
+libtdrsrcdir := lib/tdr
+libcryptosrcdir := ../lib/crypto
+libtorturesrcdir := ../lib/torture
+smb_serversrcdir := smb_server
+libcompressionsrcdir := ../lib/compression
+libgencachesrcdir := lib
+paramsrcdir := param
+rpc_serversrcdir := rpc_server
+ldap_serversrcdir := ldap_server
+web_serversrcdir := web_server
+winbindsrcdir := winbind
+nbt_serversrcdir := nbt_server
+wrepl_serversrcdir := wrepl_server
+cldap_serversrcdir := cldap_server
+utilssrcdir := utils
+clientsrcdir := client
+torturesrcdir := torture
+ntvfssrcdir := ntvfs
+ntptrsrcdir := ntptr
+librpcsrcdir := librpc
+libclisrcdir := libcli
+libclicommonsrcdir := ../libcli
+libclinbtsrcdir := ../libcli/nbt
+pyscriptsrcdir := $(srcdir)/scripting/python
+kdcsrcdir := kdc
+ntp_signdsrcdir := ntp_signd
+wmisrcdir := lib/wmi
+tallocsrcdir := ../lib/talloc
+comsrcdir := $(srcdir)/lib/com
+
+include data.mk
+
+$(foreach SCRIPT,$(wildcard scripting/bin/*),$(eval $(call binary_install_template,$(SCRIPT))))
+
+$(DESTDIR)$(bindir)/%: scripting/bin/% installdirs
+ @mkdir -p $(@D)
+ @echo Installing $(@F) as $@
+ @if test -f $@; then echo -n ""; rm -f $@.old; mv $@ $@.old; fi
+ @cp $< $@
+ @chmod $(INSTALLPERMS) $@
+
+pythonmods:: $(PYTHON_PYS) $(PYTHON_SO)
+
+DEP_FILES = $(patsubst %.ho,%.hd,$(patsubst %.o,%.d,$(ALL_OBJS))) \
+ include/includes.d
+
+ifeq ($(automatic_dependencies),yes)
+ifneq ($(MAKECMDGOALS),clean)
+ifneq ($(MAKECMDGOALS),distclean)
+ifneq ($(MAKECMDGOALS),realdistclean)
+ifneq ($(SKIP_DEP_FILES),yes)
+-include $(DEP_FILES)
+endif
+endif
+endif
+endif
+
+ifneq ($(SKIP_DEP_FILES),yes)
+clean::
+ @echo Removing dependency files
+ @find . -name '*.d' -o -name '*.hd' | xargs rm -f
+endif
+else
+include $(srcdir)/static_deps.mk
+endif
+
+clean::
+ @find ../lib ../libcli ../librpc ../nsswitch -name '*.o' -o -name '*.ho' | xargs rm -f
+
+PUBLIC_HEADERS += ./version.h
+
+libraries:: $(STATIC_LIBS) $(SHARED_LIBS)
+modules:: $(PLUGINS)
+headers:: $(PUBLIC_HEADERS)
+manpages:: $(MANPAGES)
+all:: showflags $(ALL_PREDEP) binaries modules pythonmods libraries headers
+everything:: all
+
+LD_LIBPATH_OVERRIDE = $(LIB_PATH_VAR)=$(shliboutputdir)
+
+# 'make testsuite' creates all binaries which are
+# needed by samba3's 'make test' and the build-farm
+# scripts use that it as fallback in case
+# 'make everything' fails
+testsuite:: bin/smbclient bin/cifsdd bin/smbtorture bin/nmblookup
+
+showlayout::
+ @echo 'Samba will be installed into:'
+ @echo ' basedir: $(basedir)'
+ @echo ' bindir: $(bindir)'
+ @echo ' sbindir: $(sbindir)'
+ @echo ' libdir: $(libdir)'
+ @echo ' modulesdir: $(modulesdir)'
+ @echo ' includedir: $(includedir)'
+ @echo ' vardir: $(localstatedir)'
+ @echo ' privatedir: $(privatedir)'
+ @echo ' piddir: $(piddir)'
+ @echo ' lockdir: $(lockdir)'
+ @echo ' logfilebase: $(logfilebase)'
+ @echo ' setupdir: $(setupdir)'
+ @echo ' swatdir: $(swatdir)'
+ @echo ' mandir: $(mandir)'
+ @echo ' torturedir: $(torturedir)'
+ @echo ' datadir: $(datadir)'
+ @echo ' winbindd_socket_dir: $(winbindd_socket_dir)'
+ @echo ' ntp_signd_socket_dir: $(ntp_signd_socket_dir)'
+
+showflags::
+ @echo ' srcdir = $(srcdir)'
+ @echo ' builddir = $(builddir)'
+
+# The permissions to give the executables
+INSTALLPERMS = 0755
+
+install:: showlayout everything installbin installsbin installdat installmisc \
+ installlib installheader installpc installplugins
+
+# DESTDIR is used here to prevent packagers wasting their time
+# duplicating the Makefile. Remove it and you will have the privilege
+# of packaging each samba release for multiple versions of multiple
+# distributions and operating systems, or at least supplying patches
+# to all the packaging files required for this, prior to committing
+# the removal of DESTDIR. Do not remove it even though you think it
+# is not used.
+
+installdirs::
+ @$(SHELL) $(srcdir)/script/installdirs.sh \
+ $(DESTDIR)$(basedir) \
+ $(DESTDIR)$(bindir) \
+ $(DESTDIR)$(sbindir) \
+ $(DESTDIR)$(torturedir) \
+ $(DESTDIR)$(libdir) \
+ $(DESTDIR)$(modulesdir) \
+ $(DESTDIR)$(mandir) \
+ $(DESTDIR)$(localstatedir) \
+ $(DESTDIR)$(localstatedir)/lib \
+ $(DESTDIR)$(localstatedir)/run \
+ $(DESTDIR)$(privatedir) \
+ $(DESTDIR)$(datadir) \
+ $(DESTDIR)$(piddir) \
+ $(DESTDIR)$(lockdir) \
+ $(DESTDIR)$(logfilebase) \
+ $(DESTDIR)$(privatedir)/tls \
+ $(DESTDIR)$(includedir) \
+ $(DESTDIR)$(pkgconfigdir) \
+ $(DESTDIR)$(sysconfdir)
+
+installbin:: installdirs
+
+installplugins::
+
+installlib:: $(SHARED_LIBS) $(STATIC_LIBS) installdirs
+ @$(SHELL) $(srcdir)/script/installlib.sh $(DESTDIR)$(libdir) "$(SHLIBEXT)" $(SHARED_LIBS)
+ #@$(SHELL) $(srcdir)/script/installlib.sh $(DESTDIR)$(libdir) "$(STLIBEXT)" $(STATIC_LIBS)
+
+installheader:: headers installdirs
+ @srcdir=$(srcdir) builddir=$(builddir) $(PERL) $(srcdir)/script/installheader.pl $(DESTDIR)$(includedir) $(PUBLIC_HEADERS)
+
+installdat:: installdirs
+ @$(SHELL) $(srcdir)/script/installdat.sh $(DESTDIR)$(datadir) $(srcdir)
+
+installman:: manpages installdirs
+ @$(SHELL) $(srcdir)/script/installman.sh $(DESTDIR)$(mandir) $(MANPAGES)
+
+installmisc:: installdirs
+ @$(SHELL) $(srcdir)/script/installmisc.sh $(srcdir) $(DESTDIR)$(setupdir)
+
+installpc:: installdirs
+ @$(SHELL) $(srcdir)/script/installpc.sh $(builddir) $(DESTDIR)$(pkgconfigdir) $(PC_FILES)
+
+uninstall:: uninstallbin uninstallsbin uninstalldat uninstallmisc uninstalllib uninstallheader \
+ uninstallman uninstallpc uninstallplugins
+
+uninstallmisc::
+ @echo "Removing MISC files"
+ @rm -rf $(DESTDIR)$(setupdir)/*
+
+$(DESTDIR)$(bindir)/%: bin/% installdirs
+ @mkdir -p $(@D)
+ @echo Installing $(@F) as $@
+ @if test -f $@; then echo -n ""; rm -f $@.old; mv $@ $@.old; fi
+ @cp $< $@
+ @chmod $(INSTALLPERMS) $@
+
+$(DESTDIR)$(sbindir)/%: bin/% installdirs
+ @mkdir -p $(@D)
+ @echo Installing $(@F) as $@
+ @if test -f $@; then echo -n ""; rm -f $@.old; mv $@ $@.old; fi
+ @cp $< $@
+ @chmod $(INSTALLPERMS) $@
+
+uninstalldat::
+ @echo "Removing DAT files"
+ @rm -fr $(DESTDIR)$(datadir)/*
+
+uninstallbin::
+
+uninstalllib::
+ @echo "Removing libraries"
+ @$(SHELL) $(srcdir)/script/uninstalllib.sh $(DESTDIR)$(libdir) $(SHARED_LIBS)
+
+uninstallheader::
+ @echo "Removing headers"
+ @rm -fr $(DESTDIR)$(includedir)/*
+
+uninstallman::
+ @echo "Removing manpages"
+ @$(SHELL) $(srcdir)/script/uninstallman.sh $(DESTDIR)$(mandir) $(MANPAGES)
+
+uninstallplugins::
+
+uninstallpc::
+ @echo "Removing package configurations"
+ @cd $(DESTDIR)$(pkgconfigdir); rm -f $(notdir $(PC_FILES))
+
+config.status:
+ @echo "config.status does not exist. Please run ./configure."
+ @/bin/false
+
+data.mk: config.status $(MK_FILES)
+ ./config.status
+
+testcov-html::
+
+include $(pidldir)/config.mk
+selftestdir := $(srcdir)/../selftest
+include $(srcdir)/selftest/config.mk
+
+showflags::
+ @echo ' pwd = '`/bin/pwd`
diff --git a/source4/NEWS b/source4/NEWS
new file mode 100644
index 0000000000..b441bc784f
--- /dev/null
+++ b/source4/NEWS
@@ -0,0 +1,503 @@
+This file aims to document the major changes since the latest released version
+of Samba, 3.0. Samba 4.0 contains rewrites of several subsystems
+and uses a different internal format for most data. Since this
+file is an initial draft, please update missing items.
+
+One of the main goals of Samba 4 was Active Directory Domain Controller
+support. This means Samba now implements several protocols that are required
+by AD such as Kerberos and DNS.
+
+An (experimental) upgrade script that performs a one-way upgrade
+from Samba 3 is available in source/setup/upgrade.
+
+Removal of nmbd and introduction of process models
+==================================================
+smbd now implements several network protocols other than just CIFS and
+DCE/RPC. nmbd's functionality has been merged into smbd. smbd supports
+various 'process models' that specify how concurrent connections are
+handled (when to fork, use threads, etc).
+
+Introduction of LDB
+===================
+Samba now stores most of its persistent data in a LDAP-like database
+called LDB (see ldb(7) for more info).
+
+Removed SWAT
+==================
+Unlike previous versions, Samba4 does not provide a web interface at this time.
+
+Built-in KDC
+============
+Samba4 ships with an integrated KDC (Kerberos Key Distribution
+Center). Backed directly onto our main internal database, and
+integrated with custom code to handle the PAC, Samba4's KDC is an
+integral part of our support for AD logon protocols.
+
+Built-in LDAP Server
+====================
+Like the situation with the KDC, Samba4 ships with it's own LDAP
+server, included to provide simple, built-in LDAP services in an AD
+(rather than distinctly standards) matching manner. The database is
+LDB, and it shares that in common with the rest of Samba.
+
+Changed configuration options
+=============================
+Several configuration options have been removed in Samba4 while others have
+been introduced. This section contains a summary of changes to smb.conf and
+where these settings moved. Configuration options that have disappeared may be
+re-added later when the functionality that uses them gets reimplemented in
+Samba 4.
+
+The 'security' parameter has been split up. It is now only used to choose
+between the 'user' and 'share' security levels (the latter is not supported
+in Samba 4 yet). The other values of this option and the 'domain master' and
+'domain logons' parameters have been merged into a 'server role' parameter
+that can be either 'domain controller', 'member server' or 'standalone'. Note that
+member server support does not work yet.
+
+The following parameters have been removed:
+- passdb backend: accounts are now stored in a LDB-based SAM database,
+ see 'sam database' below.
+- update encrypted
+- public
+- guest ok
+- client schannel
+- server schannel
+- allow trusted domains
+- hosts equiv
+- map to guest
+- smb passwd file
+- algorithmic rid base
+- root directory
+- root dir
+- root
+- guest account
+- enable privileges
+- pam password change
+- passwd program
+- passwd chat debug
+- passwd chat timeout
+- check password script
+- username map
+- username level
+- unix password sync
+- restrict anonymous
+- username
+- user
+- users
+- invalid users
+- valid users
+- admin users
+- read list
+- write list
+- printer admin
+- force user
+- force group
+- group
+- write ok
+- writeable
+- writable
+- acl check permissions
+- acl group control
+- acl map full control
+- create mask
+- create mode
+- force create mode
+- security mask
+- force security mode
+- directory mask
+- directory mode
+- force directory mode
+- directory security mask
+- force directory security mode
+- force unknown acl user
+- inherit permissions
+- inherit acls
+- inherit owner
+- guest only
+- only guest
+- only user
+- allow hosts
+- deny hosts
+- preload modules
+- use kerberos keytab
+- syslog
+- syslog only
+- max log size
+- debug timestamp
+- timestamp logs
+- debug hires timestamp
+- debug pid
+- debug uid
+- allocation roundup size
+- aio read size
+- aio write size
+- aio write behind
+- large readwrite
+- protocol
+- read bmpx
+- reset on zero vc
+- acl compatibility
+- defer sharing violations
+- ea support
+- nt acl support
+- nt pipe support
+- profile acls
+- map acl inherit
+- afs share
+- max ttl
+- client use spnego
+- enable asu support
+- svcctl list
+- block size
+- change notify timeout
+- deadtime
+- getwd cache
+- keepalive
+- kernel change notify
+- lpq cache time
+- max smbd processes
+- max disk size
+- max open files
+- min print space
+- strict allocate
+- sync always
+- use mmap
+- use sendfile
+- hostname lookups
+- write cache size
+- name cache timeout
+- max reported print jobs
+- load printers
+- printcap cache time
+- printcap name
+- printcap
+- printing
+- cups options
+- cups server
+- iprint server
+- print command
+- disable spoolss
+- enable spoolss
+- lpq command
+- lprm command
+- lppause command
+- lpresume command
+- queuepause command
+- queueresume command
+- enumports command
+- addprinter command
+- deleteprinter command
+- show add printer wizard
+- os2 driver map
+- use client driver
+- default devmode
+- force printername
+- mangling method
+- mangle prefix
+- default case
+- case sensitive
+- casesignames
+- preserve case
+- short preserve case
+- mangling char
+- hide dot files
+- hide special files
+- hide unreadable
+- hide unwriteable files
+- delete veto files
+- veto files
+- hide files
+- veto oplock files
+- map readonly
+- mangled names
+- mangled map
+- max stat cache size
+- stat cache
+- store dos attributes
+- machine password timeout
+- add user script
+- rename user script
+- delete user script
+- add group script
+- delete group script
+- add user to group script
+- delete user from group script
+- set primary group script
+- add machine script
+- shutdown script
+- abort shutdown script
+- username map script
+- logon script
+- logon path
+- logon drive
+- logon home
+- domain logons
+- os level
+- lm announce
+- lm interval
+- domain master
+- browse list
+- enhanced browsing
+- wins proxy
+- wins hook
+- wins partners
+- blocking locks
+- fake oplocks
+- kernel oplocks
+- locking
+- lock spin count
+- lock spin time
+- level2 oplocks
+- oplock break wait time
+- oplock contention limit
+- posix locking
+- share modes
+- ldap server
+- ldap port
+- ldap admin dn
+- ldap delete dn
+- ldap group suffix
+- ldap idmap suffix
+- ldap machine suffix
+- ldap passwd sync
+- ldap password sync
+- ldap replication sleep
+- ldap suffix
+- ldap ssl
+- ldap timeout
+- ldap page size
+- ldap user suffix
+- add share command
+- change share command
+- delete share command
+- eventlog list
+- utmp directory
+- wtmp directory
+- utmp
+- default service
+- default
+- message command
+- dfree cache time
+- dfree command
+- get quota command
+- set quota command
+- remote announce
+- remote browse sync
+- homedir map
+- afs username map
+- afs token lifetime
+- log nt token command
+- time offset
+- NIS homedir
+- preexec
+- exec
+- preexec close
+- postexec
+- root preexec
+- root preexec close
+- root postexec
+- set directory
+- wide links
+- follow symlinks
+- dont descend
+- magic script
+- magic output
+- delete readonly
+- dos filemode
+- dos filetimes
+- dos filetime resolution
+- fake directory create times
+- panic action
+- vfs objects
+- vfs object
+- msdfs root
+- msdfs proxy
+- host msdfs
+- enable rid algorithm
+- passdb expand explicit
+- idmap backend
+- idmap uid
+- winbind uid
+- idmap gid
+- winbind gid
+- template homedir
+- template shell
+- winbind separator
+- winbind cache time
+- winbind enum users
+- winbind enum groups
+- winbind use default domain
+- winbind trusted domains only
+- winbind nested groups
+- winbind max idle children
+- winbind nss info
+
+The following parameters have been added:
++ rpc big endian (G)
+ Make Samba fake it is running on a bigendian machine when using DCE/RPC.
+ Useful for debugging.
+
+ Default: no
+
++ case insensitive filesystem (S)
+ Set to true if this share is located on a case-insensitive filesystem.
+ This disables looking for a filename by trying all possible combinations of
+ uppercase/lowercase characters and thus speeds up operations when a
+ file cannot be found.
+
+ Default: no
+
++ setup directory
+ Path to data used by provisioning script.
+
+ Default: Set at compile-time
+
++ ncalrpc dir
+ Directory to use for UNIX sockets used by the 'ncalrpc' DCE/RPC transport.
+
+ Default: Set at compile-time
+
++ ntvfs handler
+ Backend to the NT VFS to use (more than one can be specified). Available
+ backends include:
+
+ - posix:
+ Maps POSIX FS semantics to NT semantics
+
+ - simple:
+ Very simple backend (original testing backend).
+
+ - unixuid:
+ Sets up user credentials based on POSIX gid/uid.
+
+ - cifs:
+ Proxies a remote CIFS FS. Mainly useful for testing.
+
+ - nbench:
+ Filter module that saves data useful to the nbench benchmark suite.
+
+ - ipc:
+ Allows using SMB for inter process communication. Only used for
+ the IPC$ share.
+
+ - print:
+ Allows printing over SMB. This is LANMAN-style printing (?), not
+ the be confused with the spoolss DCE/RPC interface used by later
+ versions of Windows.
+
+ Default: unixuid default
+
++ ntptr providor
+ FIXME
+
++ dcerpc endpoint servers
+ What DCE/RPC servers to start.
+
+ Default: epmapper srvsvc wkssvc rpcecho samr netlogon lsarpc spoolss drsuapi winreg dssetup
+
++ server services
+ Services Samba should provide.
+
+ Default: smb rpc nbt wrepl ldap cldap web kdc
+
++ sam database
+ Location of the SAM (account database) database. This should be a
+ LDB URL.
+
+ Default: set at compile-time
+
++ spoolss database
+ Spoolss (printer) DCE/RPC server database. This should be a LDB URL.
+
+ Default: set at compile-time
+
++ wins config database
+ WINS configuration database location. This should be a LDB URL.
+
+ Default: set at compile-time
+
++ wins database
+ WINS database location. This should be a LDB URL.
+
+ Default: set at compile-time
+
++ client use spnego principal
+ Tells the client to use the Kerberos service principal specified by the
+ server during the security protocol negotation rather than
+ looking up the principal itself (cifs/hostname).
+
+ Default: false
+
++ nbt port
+ TCP/IP Port used by the NetBIOS over TCP/IP (NBT) implementation.
+
+ Default: 137
+
++ dgram port
+ UDP/IP port used by the NetBIOS over TCP/IP (NBT) implementation.
+
+ Default: 138
+
++ cldap port
+ UDP/IP port used by the CLDAP protocol.
+
+ Default: 389
+
++ krb5 port
+ IP port used by the kerberos KDC.
+
+ Default: 88
+
++ kpasswd port
+ IP port used by the kerberos password change protocol.
+
+ Default: 464
+
++ web port
+ TCP/IP port SWAT should listen on.
+
+ Default: 901
+
++ tls enabled
+ Enable TLS support for SWAT
+
+ Default: true
+
++ tls keyfile
+ Path to TLS key file (PEM format) to be used by SWAT. If no
+ path is specified, Samba will create a key.
+
+ Default: none
+
++ tls certfile
+ Path to TLS certificate file (PEM format) to be used by SWAT. If no
+ path is specified, Samba will create a certificate.
+
+ Default: none
+
++ tls cafile
+ Path to CA authority file Samba will use to sign TLS keys it generates. If
+ no path is specified, Samba will create a self-signed CA certificate.
+
+ Default: none
+
++ tls crlfile
+ Path to TLS certificate revocation lists file.
+
+ Default: none
+
++ swat directory
+ SWAT data directory.
+
+ Default: set at compile-time
+
++ large readwrite
+ Indicate the CIFS server is able to do large reads/writes.
+
+ Default: true
+
++ unicode
+ Enable/disable unicode support in the protocol.
+
+ Default: true
diff --git a/source4/TODO b/source4/TODO
new file mode 100644
index 0000000000..5efebed1b3
--- /dev/null
+++ b/source4/TODO
@@ -0,0 +1,278 @@
+build/smb_build/TODO
+lib/registry/TODO
+lib/tdr/TODO
+pidl/TODO
+
+- seperate adminlog mechanism (as opposed to the current DEBUG log,
+ which is not really aimed at administrators but more at developers)
+ Perhaps similar to eventlog so we can also use eventlog to retrieve the data?
+
+- testsuite for the 'net' tool
+
+- and a lot of other stuff
+
+Configuration options
+=====================
+
+The following options don't exist in Samba4 yet
+or are not converted by the upgrade script
+or will be removed:
+
+- update encrypted
+- public
+- guest ok
+- client schannel
+- server schannel
+- allow trusted domains
+- hosts equiv
+- map to guest
+- algorithmic rid base
+- root directory
+- root dir
+- root
+- guest account
+- enable privileges
+- pam password change
+- passwd program
+- passwd chat debug
+- passwd chat timeout
+- check password script
+- username map
+- username level
+- unix password sync
+- restrict anonymous
+- username
+- user
+- users
+- invalid users
+- valid users
+- admin users
+- read list
+- write list
+- printer admin
+- force user
+- force group
+- group
+- write ok
+- writeable
+- writable
+- acl check permissions
+- acl group control
+- acl map full control
+- create mask
+- create mode
+- force create mode
+- security mask
+- force security mode
+- directory mask
+- directory mode
+- force directory mode
+- directory security mask
+- force directory security mode
+- force unknown acl user
+- inherit permissions
+- inherit acls
+- inherit owner
+- guest only
+- only guest
+- only user
+- allow hosts
+- deny hosts
+- preload modules
+- use kerberos keytab
+- syslog
+- syslog only
+- max log size
+- debug timestamp
+- timestamp logs
+- debug hires timestamp
+- debug pid
+- debug uid
+- allocation roundup size
+- aio read size
+- aio write size
+- aio write behind
+- large readwrite
+- protocol
+- read bmpx
+- reset on zero vc
+- acl compatibility
+- defer sharing violations
+- ea support
+- nt acl support
+- nt pipe support
+- profile acls
+- map acl inherit
+- afs share
+- max ttl
+- client use spnego
+- enable asu support
+- svcctl list
+- block size
+- change notify timeout
+- deadtime
+- getwd cache
+- keepalive
+- kernel change notify
+- lpq cache time
+- max smbd processes
+- max disk size
+- max open files
+- min print space
+- strict allocate
+- sync always
+- use mmap
+- use sendfile
+- hostname lookups
+- write cache size
+- name cache timeout
+- max reported print jobs
+- load printers
+- printcap cache time
+- printcap name
+- printcap
+- printing
+- cups options
+- cups server
+- iprint server
+- print command
+- disable spoolss
+- enable spoolss
+- lpq command
+- lprm command
+- lppause command
+- lpresume command
+- queuepause command
+- queueresume command
+- enumports command
+- addprinter command
+- deleteprinter command
+- show add printer wizard
+- os2 driver map
+- use client driver
+- default devmode
+- force printername
+- mangling method
+- mangle prefix
+- default case
+- case sensitive
+- casesignames
+- preserve case
+- short preserve case
+- mangling char
+- hide dot files
+- hide special files
+- hide unreadable
+- hide unwriteable files
+- delete veto files
+- veto files
+- hide files
+- veto oplock files
+- map readonly
+- mangled names
+- mangled map
+- max stat cache size
+- stat cache
+- store dos attributes
+- machine password timeout
+- add user script
+- rename user script
+- delete user script
+- add group script
+- delete group script
+- add user to group script
+- delete user from group script
+- set primary group script
+- add machine script
+- shutdown script
+- abort shutdown script
+- username map script
+- logon script
+- logon path
+- logon drive
+- logon home
+- domain logons
+- os level
+- lm announce
+- lm interval
+- domain master
+- browse list
+- enhanced browsing
+- wins proxy
+- blocking locks
+- fake oplocks
+- kernel oplocks
+- locking
+- lock spin count
+- lock spin time
+- oplocks
+- level2 oplocks
+- oplock break wait time
+- oplock contention limit
+- posix locking
+- share modes
+- add share command
+- change share command
+- delete share command
+- eventlog list
+- utmp directory
+- wtmp directory
+- utmp
+- default service
+- default
+- message command
+- dfree cache time
+- dfree command
+- get quota command
+- set quota command
+- remote announce
+- remote browse sync
+- homedir map
+- afs username map
+- afs token lifetime
+- log nt token command
+- time offset
+- NIS homedir
+- preexec
+- exec
+- preexec close
+- postexec
+- root preexec
+- root preexec close
+- root postexec
+- set directory
+- wide links
+- follow symlinks
+- dont descend
+- magic script
+- magic output
+- delete readonly
+- dos filemode
+- dos filetimes
+- dos filetime resolution
+- fake directory create times
+- panic action
+- vfs objects
+- vfs object
+- msdfs root
+- msdfs proxy
+- host msdfs
+- enable rid algorithm
+- passdb expand explicit
+- idmap backend
+- idmap uid
+- winbind uid
+- idmap gid
+- winbind gid
+- template homedir
+- template shell
+- winbind separator
+- winbind cache time
+- winbind enum users
+- winbind enum groups
+- winbind use default domain
+- winbind trusted domains only
+- winbind nested groups
+- winbind max idle children
+- winbind nss info
+
diff --git a/source4/VERSION b/source4/VERSION
new file mode 100644
index 0000000000..58f8cc169d
--- /dev/null
+++ b/source4/VERSION
@@ -0,0 +1,117 @@
+########################################################
+# SAMBA Version #
+# #
+# Samba versions are as follows #
+# 3.0.x New production series #
+# 3.0.x{tp,pre,rc}y Preview/Testing & RC #
+# 3.0.x[a-z] Patch releases #
+# 3.0.x[a-z]-VENDOR-z Vendor patch releases #
+# #
+# script/mkversion.sh #
+# will use this file to create #
+# include/version.h #
+# #
+########################################################
+
+########################################################
+# This are the main SAMBA version numbers #
+# #
+# <MAJOR>.<MINOR>.<RELEASE> #
+# #
+# e.g. SAMBA_VERSION_MAJOR=3 #
+# SAMBA_VERSION_MINOR=0 #
+# SAMBA_VERSION_RELEASE=0 #
+# -> "3.0.0" #
+########################################################
+SAMBA_VERSION_MAJOR=4
+SAMBA_VERSION_MINOR=0
+SAMBA_VERSION_RELEASE=0
+
+########################################################
+# If a official release has a serious bug #
+# a security release will have 'a' sufffix #
+# #
+# so SAMBA's version will be #
+# <MAJOR>.<MINOR>.<RELEASE><REVISION> #
+# #
+# e.g. SAMBA_VERSION_REVISION=a #
+# -> "2.2.8a" #
+########################################################
+SAMBA_VERSION_REVISION=
+
+########################################################
+# For 'tp' releases the version will be #
+# #
+# <MAJOR>.<MINOR>.<RELEASE>tp<TP_RELEASE> #
+# #
+# e.g. SAMBA_VERSION_TP_RELEASE=1 #
+# -> "4.0.0tp1" #
+########################################################
+SAMBA_VERSION_TP_RELEASE=
+
+########################################################
+# For 'alpha' releases the version will be #
+# #
+# <MAJOR>.<MINOR>.<RELEASE>alpha<TP_RELEASE> #
+# #
+# e.g. SAMBA_VERSION_ALPHA_RELEASE=1 #
+# -> "4.0.0alpha1" #
+########################################################
+SAMBA_VERSION_ALPHA_RELEASE=8
+
+########################################################
+# For 'pre' releases the version will be #
+# #
+# <MAJOR>.<MINOR>.<RELEASE>pre<PRE_RELEASE> #
+# #
+# e.g. SAMBA_VERSION_PRE_RELEASE=1 #
+# -> "2.2.9pre1" #
+########################################################
+SAMBA_VERSION_PRE_RELEASE=
+
+########################################################
+# For 'rc' releases the version will be #
+# #
+# <MAJOR>.<MINOR>.<RELEASE>rc<RC_RELEASE> #
+# #
+# e.g. SAMBA_VERSION_RC_RELEASE=1 #
+# -> "3.0.0rc1" #
+########################################################
+SAMBA_VERSION_RC_RELEASE=
+
+########################################################
+# To mark SVN snapshots this should be set to 'yes' #
+# in the development BRANCH, and set to 'no' only in #
+# the SAMBA_X_X_RELEASE BRANCH #
+# #
+# <MAJOR>.<MINOR>.<RELEASE>[...]-SVN-build-xxx #
+# #
+# e.g. SAMBA_VERSION_IS_SVN_SNAPSHOT=yes #
+# -> "3.0.0-SVN-build-199" #
+########################################################
+SAMBA_VERSION_IS_GIT_SNAPSHOT=yes
+
+########################################################
+# This is for specifying a release nickname #
+# #
+# e.g. SAMBA_VERSION_RELEASE_NICKNAME=Nicky Nickname #
+# smbd --version will then give: #
+# -> "4.0.0-tp1-VendorVersion (Nicky Nickname)" #
+########################################################
+SAMBA_VERSION_RELEASE_NICKNAME=
+
+########################################################
+# This can be set by vendors if they want... #
+# This can be a string constant or a function which #
+# returns a string (const char *) #
+# #
+# <MAJOR>.<MINOR>.<RELEASE>[...]-<VENDOR_SUFFIX> #
+# #
+# Note the '-' is automaticaly added #
+# #
+# e.g. SAMBA_VERSION_VENDOR_SUFFIX=VendorVersion #
+# -> "3.0.0rc2-VendorVersion" #
+# #
+########################################################
+SAMBA_VERSION_VENDOR_SUFFIX=
+SAMBA_VERSION_VENDOR_PATCH=
diff --git a/source4/aclocal.m4 b/source4/aclocal.m4
new file mode 100644
index 0000000000..8ad8f47cd6
--- /dev/null
+++ b/source4/aclocal.m4
@@ -0,0 +1,65 @@
+
+dnl Copied from libtool.m4
+AC_DEFUN(AC_PROG_LD_GNU,
+[AC_CACHE_CHECK([if the linker ($LD) is GNU ld], ac_cv_prog_gnu_ld,
+[# I'd rather use --version here, but apparently some GNU ld's only accept -v.
+if $LD -v 2>&1 </dev/null | egrep '(GNU|with BFD)' 1>&5; then
+ ac_cv_prog_gnu_ld=yes
+else
+ ac_cv_prog_gnu_ld=no
+fi])
+])
+
+dnl Removes -I/usr/include/? from given variable
+AC_DEFUN(CFLAGS_REMOVE_USR_INCLUDE,[
+ ac_new_flags=""
+ for i in [$]$1; do
+ case [$]i in
+ -I/usr/include|-I/usr/include/) ;;
+ *) ac_new_flags="[$]ac_new_flags [$]i" ;;
+ esac
+ done
+ $1=[$]ac_new_flags
+])
+
+dnl Removes '-L/usr/lib[/]', '-Wl,-rpath,/usr/lib[/]'
+dnl and '-Wl,-rpath -Wl,/usr/lib[/]' from given variable
+AC_DEFUN(LIB_REMOVE_USR_LIB,[
+ ac_new_flags=""
+ l=""
+ for i in [$]$1; do
+ case [$]l[$]i in
+ -L/usr/lib) ;;
+ -L/usr/lib/) ;;
+ -L/usr/lib64) ;;
+ -L/usr/lib64/) ;;
+ -Wl,-rpath,/usr/lib) l="";;
+ -Wl,-rpath,/usr/lib/) l="";;
+ -Wl,-rpath,/usr/lib64) l="";;
+ -Wl,-rpath,/usr/lib64/) l="";;
+ -Wl,-rpath) l=[$]i;;
+ -Wl,-rpath-Wl,/usr/lib) l="";;
+ -Wl,-rpath-Wl,/usr/lib/) l="";;
+ -Wl,-rpath-Wl,/usr/lib64) l="";;
+ -Wl,-rpath-Wl,/usr/lib64/) l="";;
+ *)
+ s=" "
+ if test x"[$]ac_new_flags" = x""; then
+ s="";
+ fi
+ if test x"[$]l" = x""; then
+ ac_new_flags="[$]ac_new_flags[$]s[$]i";
+ else
+ ac_new_flags="[$]ac_new_flags[$]s[$]l [$]i";
+ fi
+ l=""
+ ;;
+ esac
+ done
+ $1=[$]ac_new_flags
+])
+
+m4_include(../lib/replace/libreplace.m4)
+m4_include(build/m4/ax_cflags_gcc_option.m4)
+m4_include(build/m4/ax_cflags_irix_option.m4)
+m4_include(build/m4/public.m4)
diff --git a/source4/auth/auth.h b/source4/auth/auth.h
new file mode 100644
index 0000000000..973102d842
--- /dev/null
+++ b/source4/auth/auth.h
@@ -0,0 +1,291 @@
+/*
+ Unix SMB/CIFS implementation.
+ Standardised Authentication types
+ Copyright (C) Andrew Bartlett 2001
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _SAMBA_AUTH_H
+#define _SAMBA_AUTH_H
+
+#include "librpc/gen_ndr/ndr_krb5pac.h"
+
+extern const char *user_attrs[];
+
+union netr_Validation;
+struct netr_SamBaseInfo;
+struct netr_SamInfo3;
+struct loadparm_context;
+
+/* modules can use the following to determine if the interface has changed
+ * please increment the version number after each interface change
+ * with a comment and maybe update struct auth_critical_sizes.
+ */
+/* version 1 - version from samba 3.0 - metze */
+/* version 2 - initial samba4 version - metze */
+/* version 3 - subsequent samba4 version - abartlet */
+/* version 4 - subsequent samba4 version - metze */
+/* version 0 - till samba4 is stable - metze */
+#define AUTH_INTERFACE_VERSION 0
+
+#define USER_INFO_CASE_INSENSITIVE_USERNAME 0x01 /* username may be in any case */
+#define USER_INFO_CASE_INSENSITIVE_PASSWORD 0x02 /* password may be in any case */
+#define USER_INFO_DONT_CHECK_UNIX_ACCOUNT 0x04 /* dont check unix account status */
+#define USER_INFO_INTERACTIVE_LOGON 0x08 /* dont check unix account status */
+
+enum auth_password_state {
+ AUTH_PASSWORD_RESPONSE,
+ AUTH_PASSWORD_HASH,
+ AUTH_PASSWORD_PLAIN
+};
+
+struct auth_usersupplied_info
+{
+ const char *workstation_name;
+ struct socket_address *remote_host;
+
+ uint32_t logon_parameters;
+
+ bool mapped_state;
+ /* the values the client gives us */
+ struct {
+ const char *account_name;
+ const char *domain_name;
+ } client, mapped;
+
+ enum auth_password_state password_state;
+
+ union {
+ struct {
+ DATA_BLOB lanman;
+ DATA_BLOB nt;
+ } response;
+ struct {
+ struct samr_Password *lanman;
+ struct samr_Password *nt;
+ } hash;
+
+ char *plaintext;
+ } password;
+ uint32_t flags;
+};
+
+struct auth_serversupplied_info
+{
+ struct dom_sid *account_sid;
+ struct dom_sid *primary_group_sid;
+
+ size_t n_domain_groups;
+ struct dom_sid **domain_groups;
+
+ DATA_BLOB user_session_key;
+ DATA_BLOB lm_session_key;
+
+ const char *account_name;
+ const char *domain_name;
+
+ const char *full_name;
+ const char *logon_script;
+ const char *profile_path;
+ const char *home_directory;
+ const char *home_drive;
+ const char *logon_server;
+
+ NTTIME last_logon;
+ NTTIME last_logoff;
+ NTTIME acct_expiry;
+ NTTIME last_password_change;
+ NTTIME allow_password_change;
+ NTTIME force_password_change;
+
+ uint16_t logon_count;
+ uint16_t bad_password_count;
+
+ uint32_t acct_flags;
+
+ bool authenticated;
+
+ struct PAC_SIGNATURE_DATA pac_srv_sig, pac_kdc_sig;
+};
+
+struct auth_method_context;
+struct auth_check_password_request;
+struct auth_context;
+
+struct auth_operations {
+ const char *name;
+
+ /* If you are using this interface, then you are probably
+ * getting something wrong. This interface is only for
+ * security=server, and makes a number of compromises to allow
+ * that. It is not compatible with being a PDC. */
+
+ NTSTATUS (*get_challenge)(struct auth_method_context *ctx, TALLOC_CTX *mem_ctx, DATA_BLOB *challenge);
+
+ /* Given the user supplied info, check if this backend want to handle the password checking */
+
+ NTSTATUS (*want_check)(struct auth_method_context *ctx, TALLOC_CTX *mem_ctx,
+ const struct auth_usersupplied_info *user_info);
+
+ /* Given the user supplied info, check a password */
+
+ NTSTATUS (*check_password)(struct auth_method_context *ctx, TALLOC_CTX *mem_ctx,
+ const struct auth_usersupplied_info *user_info,
+ struct auth_serversupplied_info **server_info);
+
+ /* Lookup a 'server info' return based only on the principal */
+ NTSTATUS (*get_server_info_principal)(TALLOC_CTX *mem_ctx,
+ struct auth_context *auth_context,
+ const char *principal,
+ struct auth_serversupplied_info **server_info);
+};
+
+struct auth_method_context {
+ struct auth_method_context *prev, *next;
+ struct auth_context *auth_ctx;
+ const struct auth_operations *ops;
+ int depth;
+ void *private_data;
+};
+
+struct auth_context {
+ struct {
+ /* Who set this up in the first place? */
+ const char *set_by;
+
+ bool may_be_modified;
+
+ DATA_BLOB data;
+ } challenge;
+
+ /* methods, in the order they should be called */
+ struct auth_method_context *methods;
+
+ /* the event context to use for calls that can block */
+ struct tevent_context *event_ctx;
+
+ /* the messaging context which can be used by backends */
+ struct messaging_context *msg_ctx;
+
+ /* loadparm context */
+ struct loadparm_context *lp_ctx;
+
+ NTSTATUS (*check_password)(struct auth_context *auth_ctx,
+ TALLOC_CTX *mem_ctx,
+ const struct auth_usersupplied_info *user_info,
+ struct auth_serversupplied_info **server_info);
+
+ NTSTATUS (*get_challenge)(struct auth_context *auth_ctx, const uint8_t **_chal);
+
+ bool (*challenge_may_be_modified)(struct auth_context *auth_ctx);
+
+ NTSTATUS (*set_challenge)(struct auth_context *auth_ctx, const uint8_t chal[8], const char *set_by);
+
+ NTSTATUS (*get_server_info_principal)(TALLOC_CTX *mem_ctx,
+ struct auth_context *auth_context,
+ const char *principal,
+ struct auth_serversupplied_info **server_info);
+
+};
+
+/* this structure is used by backends to determine the size of some critical types */
+struct auth_critical_sizes {
+ int interface_version;
+ int sizeof_auth_operations;
+ int sizeof_auth_methods;
+ int sizeof_auth_context;
+ int sizeof_auth_usersupplied_info;
+ int sizeof_auth_serversupplied_info;
+};
+
+ NTSTATUS encrypt_user_info(TALLOC_CTX *mem_ctx, struct auth_context *auth_context,
+ enum auth_password_state to_state,
+ const struct auth_usersupplied_info *user_info_in,
+ const struct auth_usersupplied_info **user_info_encrypted);
+
+#include "auth/session.h"
+#include "auth/system_session_proto.h"
+
+struct ldb_message;
+struct ldb_context;
+struct gensec_security;
+
+NTSTATUS auth_get_challenge(struct auth_context *auth_ctx, const uint8_t **_chal);
+NTSTATUS authsam_account_ok(TALLOC_CTX *mem_ctx,
+ struct ldb_context *sam_ctx,
+ uint32_t logon_parameters,
+ struct ldb_message *msg,
+ struct ldb_message *msg_domain_ref,
+ const char *logon_workstation,
+ const char *name_for_logs,
+ bool allow_domain_trust);
+struct auth_session_info *system_session(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx);
+NTSTATUS authsam_make_server_info(TALLOC_CTX *mem_ctx, struct ldb_context *sam_ctx,
+ const char *netbios_name,
+ struct ldb_message *msg,
+ struct ldb_message *msg_domain_ref,
+ DATA_BLOB user_sess_key, DATA_BLOB lm_sess_key,
+ struct auth_serversupplied_info **_server_info);
+NTSTATUS auth_system_session_info(TALLOC_CTX *parent_ctx,
+ struct loadparm_context *lp_ctx,
+ struct auth_session_info **_session_info) ;
+NTSTATUS auth_nt_status_squash(NTSTATUS nt_status);
+
+NTSTATUS auth_context_create_methods(TALLOC_CTX *mem_ctx, const char **methods,
+ struct tevent_context *ev,
+ struct messaging_context *msg,
+ struct loadparm_context *lp_ctx,
+ struct auth_context **auth_ctx);
+
+NTSTATUS auth_context_create(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct messaging_context *msg,
+ struct loadparm_context *lp_ctx,
+ struct auth_context **auth_ctx);
+
+NTSTATUS auth_check_password(struct auth_context *auth_ctx,
+ TALLOC_CTX *mem_ctx,
+ const struct auth_usersupplied_info *user_info,
+ struct auth_serversupplied_info **server_info);
+NTSTATUS auth_init(void);
+NTSTATUS auth_register(const struct auth_operations *ops);
+NTSTATUS authenticate_username_pw(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct messaging_context *msg,
+ struct loadparm_context *lp_ctx,
+ const char *nt4_domain,
+ const char *nt4_username,
+ const char *password,
+ struct auth_session_info **session_info);
+NTSTATUS auth_check_password_recv(struct auth_check_password_request *req,
+ TALLOC_CTX *mem_ctx,
+ struct auth_serversupplied_info **server_info);
+
+void auth_check_password_send(struct auth_context *auth_ctx,
+ const struct auth_usersupplied_info *user_info,
+ void (*callback)(struct auth_check_password_request *req, void *private_data),
+ void *private_data);
+NTSTATUS auth_context_set_challenge(struct auth_context *auth_ctx, const uint8_t chal[8], const char *set_by);
+
+NTSTATUS samba_server_gensec_start(TALLOC_CTX *mem_ctx,
+ struct tevent_context *event_ctx,
+ struct messaging_context *msg_ctx,
+ struct loadparm_context *lp_ctx,
+ struct cli_credentials *server_credentials,
+ const char *target_service,
+ struct gensec_security **gensec_context);
+
+#endif /* _SMBAUTH_H_ */
diff --git a/source4/auth/auth_sam_reply.c b/source4/auth/auth_sam_reply.c
new file mode 100644
index 0000000000..dfa76234b4
--- /dev/null
+++ b/source4/auth/auth_sam_reply.c
@@ -0,0 +1,288 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Convert a server info struct into the form for PAC and NETLOGON replies
+
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004
+ Copyright (C) Stefan Metzmacher <metze@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "auth/auth.h"
+#include "libcli/security/security.h"
+#include "librpc/gen_ndr/ndr_netlogon.h"
+#include "auth/auth_sam_reply.h"
+
+NTSTATUS auth_convert_server_info_sambaseinfo(TALLOC_CTX *mem_ctx,
+ struct auth_serversupplied_info *server_info,
+ struct netr_SamBaseInfo **_sam)
+{
+ struct netr_SamBaseInfo *sam = talloc_zero(mem_ctx, struct netr_SamBaseInfo);
+ NT_STATUS_HAVE_NO_MEMORY(sam);
+
+ sam->domain_sid = dom_sid_dup(mem_ctx, server_info->account_sid);
+ NT_STATUS_HAVE_NO_MEMORY(sam->domain_sid);
+ sam->domain_sid->num_auths--;
+
+ sam->last_logon = server_info->last_logon;
+ sam->last_logoff = server_info->last_logoff;
+ sam->acct_expiry = server_info->acct_expiry;
+ sam->last_password_change = server_info->last_password_change;
+ sam->allow_password_change = server_info->allow_password_change;
+ sam->force_password_change = server_info->force_password_change;
+
+ sam->account_name.string = server_info->account_name;
+ sam->full_name.string = server_info->full_name;
+ sam->logon_script.string = server_info->logon_script;
+ sam->profile_path.string = server_info->profile_path;
+ sam->home_directory.string = server_info->home_directory;
+ sam->home_drive.string = server_info->home_drive;
+
+ sam->logon_count = server_info->logon_count;
+ sam->bad_password_count = sam->bad_password_count;
+ sam->rid = server_info->account_sid->sub_auths[server_info->account_sid->num_auths-1];
+ sam->primary_gid = server_info->primary_group_sid->sub_auths[server_info->primary_group_sid->num_auths-1];
+
+ sam->groups.count = 0;
+ sam->groups.rids = NULL;
+
+ if (server_info->n_domain_groups > 0) {
+ int i;
+ sam->groups.rids = talloc_array(sam, struct samr_RidWithAttribute,
+ server_info->n_domain_groups);
+
+ if (sam->groups.rids == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ for (i=0; i<server_info->n_domain_groups; i++) {
+ struct dom_sid *group_sid = server_info->domain_groups[i];
+ if (!dom_sid_in_domain(sam->domain_sid, group_sid)) {
+ /* We handle this elsewhere */
+ continue;
+ }
+ sam->groups.rids[sam->groups.count].rid =
+ group_sid->sub_auths[group_sid->num_auths-1];
+
+ sam->groups.rids[sam->groups.count].attributes =
+ SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED;
+ sam->groups.count += 1;
+ }
+ }
+
+ sam->user_flags = 0; /* w2k3 uses NETLOGON_EXTRA_SIDS | NETLOGON_NTLMV2_ENABLED */
+ sam->acct_flags = server_info->acct_flags;
+ sam->logon_server.string = server_info->logon_server;
+ sam->domain.string = server_info->domain_name;
+
+ ZERO_STRUCT(sam->unknown);
+
+ ZERO_STRUCT(sam->key);
+ if (server_info->user_session_key.length == sizeof(sam->key.key)) {
+ memcpy(sam->key.key, server_info->user_session_key.data, sizeof(sam->key.key));
+ }
+
+ ZERO_STRUCT(sam->LMSessKey);
+ if (server_info->lm_session_key.length == sizeof(sam->LMSessKey.key)) {
+ memcpy(sam->LMSessKey.key, server_info->lm_session_key.data,
+ sizeof(sam->LMSessKey.key));
+ }
+
+ *_sam = sam;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS auth_convert_server_info_saminfo3(TALLOC_CTX *mem_ctx,
+ struct auth_serversupplied_info *server_info,
+ struct netr_SamInfo3 **_sam3)
+{
+ struct netr_SamBaseInfo *sam;
+ struct netr_SamInfo3 *sam3 = talloc_zero(mem_ctx, struct netr_SamInfo3);
+ NTSTATUS status;
+ int i;
+ NT_STATUS_HAVE_NO_MEMORY(sam3);
+
+ status = auth_convert_server_info_sambaseinfo(mem_ctx, server_info, &sam);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ sam3->base = *sam;
+ sam3->sidcount = 0;
+ sam3->sids = NULL;
+
+
+ sam3->sids = talloc_array(sam, struct netr_SidAttr,
+ server_info->n_domain_groups);
+ NT_STATUS_HAVE_NO_MEMORY(sam3->sids);
+
+ for (i=0; i<server_info->n_domain_groups; i++) {
+ if (dom_sid_in_domain(sam->domain_sid, server_info->domain_groups[i])) {
+ continue;
+ }
+ sam3->sids[sam3->sidcount].sid = talloc_reference(sam3->sids,server_info->domain_groups[i]);
+ sam3->sids[sam3->sidcount].attributes =
+ SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED;
+ sam3->sidcount += 1;
+ }
+ if (sam3->sidcount) {
+ sam3->base.user_flags |= NETLOGON_EXTRA_SIDS;
+ } else {
+ sam3->sids = NULL;
+ }
+ *_sam3 = sam3;
+
+ return NT_STATUS_OK;
+}
+
+/**
+ * Make a server_info struct from the info3 returned by a domain logon
+ */
+NTSTATUS make_server_info_netlogon_validation(TALLOC_CTX *mem_ctx,
+ const char *account_name,
+ uint16_t validation_level,
+ union netr_Validation *validation,
+ struct auth_serversupplied_info **_server_info)
+{
+ struct auth_serversupplied_info *server_info;
+ 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;
+ }
+
+ server_info = talloc(mem_ctx, struct auth_serversupplied_info);
+ NT_STATUS_HAVE_NO_MEMORY(server_info);
+
+ /*
+ Here is where we should check the list of
+ trusted domains, and verify that the SID
+ matches.
+ */
+ server_info->account_sid = dom_sid_add_rid(server_info, base->domain_sid, base->rid);
+ NT_STATUS_HAVE_NO_MEMORY(server_info->account_sid);
+
+
+ server_info->primary_group_sid = dom_sid_add_rid(server_info, base->domain_sid, base->primary_gid);
+ NT_STATUS_HAVE_NO_MEMORY(server_info->primary_group_sid);
+
+ server_info->n_domain_groups = base->groups.count;
+ if (base->groups.count) {
+ server_info->domain_groups = talloc_array(server_info, struct dom_sid*, base->groups.count);
+ NT_STATUS_HAVE_NO_MEMORY(server_info->domain_groups);
+ } else {
+ server_info->domain_groups = NULL;
+ }
+
+ for (i = 0; i < base->groups.count; i++) {
+ server_info->domain_groups[i] = dom_sid_add_rid(server_info, base->domain_sid, base->groups.rids[i].rid);
+ NT_STATUS_HAVE_NO_MEMORY(server_info->domain_groups[i]);
+ }
+
+ /* Copy 'other' sids. We need to do sid filtering here to
+ prevent possible elevation of privileges. See:
+
+ http://www.microsoft.com/windows2000/techinfo/administration/security/sidfilter.asp
+ */
+
+ if (validation_level == 3) {
+ struct dom_sid **dgrps = server_info->domain_groups;
+ size_t sidcount = server_info->n_domain_groups + validation->sam3->sidcount;
+ size_t n_dgrps = server_info->n_domain_groups;
+
+ if (validation->sam3->sidcount > 0) {
+ dgrps = talloc_realloc(server_info, dgrps, struct dom_sid*, sidcount);
+ NT_STATUS_HAVE_NO_MEMORY(dgrps);
+
+ for (i = 0; i < validation->sam3->sidcount; i++) {
+ dgrps[n_dgrps + i] = talloc_reference(dgrps, validation->sam3->sids[i].sid);
+ }
+ }
+
+ server_info->n_domain_groups = sidcount;
+ server_info->domain_groups = dgrps;
+
+ /* Where are the 'global' sids?... */
+ }
+
+ if (base->account_name.string) {
+ server_info->account_name = talloc_reference(server_info, base->account_name.string);
+ } else {
+ server_info->account_name = talloc_strdup(server_info, account_name);
+ NT_STATUS_HAVE_NO_MEMORY(server_info->account_name);
+ }
+
+ server_info->domain_name = talloc_reference(server_info, base->domain.string);
+ server_info->full_name = talloc_reference(server_info, base->full_name.string);
+ server_info->logon_script = talloc_reference(server_info, base->logon_script.string);
+ server_info->profile_path = talloc_reference(server_info, base->profile_path.string);
+ server_info->home_directory = talloc_reference(server_info, base->home_directory.string);
+ server_info->home_drive = talloc_reference(server_info, base->home_drive.string);
+ server_info->logon_server = talloc_reference(server_info, base->logon_server.string);
+ server_info->last_logon = base->last_logon;
+ server_info->last_logoff = base->last_logoff;
+ server_info->acct_expiry = base->acct_expiry;
+ server_info->last_password_change = base->last_password_change;
+ server_info->allow_password_change = base->allow_password_change;
+ server_info->force_password_change = base->force_password_change;
+ server_info->logon_count = base->logon_count;
+ server_info->bad_password_count = base->bad_password_count;
+ server_info->acct_flags = base->acct_flags;
+
+ server_info->authenticated = true;
+
+ /* ensure we are never given NULL session keys */
+
+ if (all_zero(base->key.key, sizeof(base->key.key))) {
+ server_info->user_session_key = data_blob(NULL, 0);
+ } else {
+ server_info->user_session_key = data_blob_talloc(server_info, base->key.key, sizeof(base->key.key));
+ NT_STATUS_HAVE_NO_MEMORY(server_info->user_session_key.data);
+ }
+
+ if (all_zero(base->LMSessKey.key, sizeof(base->LMSessKey.key))) {
+ server_info->lm_session_key = data_blob(NULL, 0);
+ } else {
+ server_info->lm_session_key = data_blob_talloc(server_info, base->LMSessKey.key, sizeof(base->LMSessKey.key));
+ NT_STATUS_HAVE_NO_MEMORY(server_info->lm_session_key.data);
+ }
+
+ ZERO_STRUCT(server_info->pac_srv_sig);
+ ZERO_STRUCT(server_info->pac_kdc_sig);
+
+ *_server_info = server_info;
+ return NT_STATUS_OK;
+}
+
diff --git a/source4/auth/config.m4 b/source4/auth/config.m4
new file mode 100644
index 0000000000..a271a9f6fe
--- /dev/null
+++ b/source4/auth/config.m4
@@ -0,0 +1,34 @@
+###############################
+# start SMB_EXT_LIB_PAM
+# check for security/pam_appl.h and -lpam
+AC_CHECK_HEADERS(security/pam_appl.h)
+AC_CHECK_LIB_EXT(pam, PAM_LIBS, pam_start)
+if test x"$ac_cv_header_security_pam_appl_h" = x"yes" -a x"$ac_cv_lib_ext_pam_pam_start" = x"yes";then
+ SMB_ENABLE(PAM,YES)
+fi
+SMB_EXT_LIB(PAM, $PAM_LIBS)
+# end SMB_EXT_LIB_PAM
+###############################
+
+################################################
+# test for where we get crypt() from
+AC_CHECK_LIB_EXT(crypt, CRYPT_LIBS, crypt)
+SMB_ENABLE(CRYPT,YES)
+SMB_EXT_LIB(CRYPT, $CRYPT_LIBS)
+
+AC_CHECK_FUNCS(crypt16 getauthuid getpwanam)
+
+AC_CHECK_HEADERS(sasl/sasl.h)
+AC_CHECK_LIB_EXT(sasl2, SASL_LIBS, sasl_client_init)
+
+if test x"$ac_cv_header_sasl_sasl_h" = x"yes" -a x"$ac_cv_lib_ext_sasl2_sasl_client_init" = x"yes";then
+ SMB_ENABLE(SASL,YES)
+ SMB_ENABLE(cyrus_sasl,YES)
+ SASL_CFLAGS="$CFLAGS"
+ SASL_CPPFLAGS="$CPPFLAGS"
+ SASL_LDFLAGS="$LDFLAGS"
+else
+ SMB_ENABLE(cyrus_sasl,NO)
+fi
+
+SMB_EXT_LIB(SASL, $SASL_LIBS, [${SASL_CFLAGS}], [${SASL_CPPFLAGS}], [${SASL_LDFLAGS}])
diff --git a/source4/auth/config.mk b/source4/auth/config.mk
new file mode 100644
index 0000000000..7d5050919e
--- /dev/null
+++ b/source4/auth/config.mk
@@ -0,0 +1,50 @@
+# auth server subsystem
+gensecsrcdir := $(authsrcdir)/gensec
+mkinclude gensec/config.mk
+mkinclude kerberos/config.mk
+mkinclude ntlmssp/config.mk
+mkinclude ntlm/config.mk
+mkinclude credentials/config.mk
+
+[SUBSYSTEM::auth_session]
+PUBLIC_DEPENDENCIES = CREDENTIALS
+
+PUBLIC_HEADERS += $(authsrcdir)/session.h
+
+auth_session_OBJ_FILES = $(addprefix $(authsrcdir)/, session.o)
+
+$(eval $(call proto_header_template,$(authsrcdir)/session_proto.h,$(auth_session_OBJ_FILES:.o=.c)))
+
+[SUBSYSTEM::samba_server_gensec]
+PUBLIC_DEPENDENCIES = CREDENTIALS GENSEC auth
+
+samba_server_gensec_OBJ_FILES = $(addprefix $(authsrcdir)/, samba_server_gensec.o)
+
+[SUBSYSTEM::auth_system_session]
+PUBLIC_DEPENDENCIES = CREDENTIALS
+PRIVATE_DEPENDENCIES = auth_session LIBSAMBA-UTIL LIBSECURITY
+
+auth_system_session_OBJ_FILES = $(addprefix $(authsrcdir)/, system_session.o)
+$(eval $(call proto_header_template,$(authsrcdir)/system_session_proto.h,$(auth_system_session_OBJ_FILES:.o=.c)))
+
+[SUBSYSTEM::auth_sam]
+PUBLIC_DEPENDENCIES = SAMDB UTIL_LDB LIBSECURITY
+PRIVATE_DEPENDENCIES = LDAP_ENCODE
+
+auth_sam_OBJ_FILES = $(addprefix $(authsrcdir)/, sam.o)
+
+$(eval $(call proto_header_template,$(authsrcdir)/auth_sam.h,$(auth_sam_OBJ_FILES:.o=.c)))
+
+[SUBSYSTEM::auth_sam_reply]
+
+auth_sam_reply_OBJ_FILES = $(addprefix $(authsrcdir)/, auth_sam_reply.o)
+
+$(eval $(call proto_header_template,$(authsrcdir)/auth_sam_reply.h,$(auth_sam_reply_OBJ_FILES:.o=.c)))
+
+[PYTHON::pyauth]
+LIBRARY_REALNAME = samba/auth.$(SHLIBEXT)
+PUBLIC_DEPENDENCIES = auth_system_session
+PRIVATE_DEPENDENCIES = SAMDB PYTALLOC param
+
+pyauth_OBJ_FILES = $(authsrcdir)/pyauth.o
+
diff --git a/source4/auth/credentials/config.mk b/source4/auth/credentials/config.mk
new file mode 100644
index 0000000000..2402c732b3
--- /dev/null
+++ b/source4/auth/credentials/config.mk
@@ -0,0 +1,20 @@
+#################################
+# Start SUBSYSTEM CREDENTIALS
+[SUBSYSTEM::CREDENTIALS]
+PUBLIC_DEPENDENCIES = \
+ LIBCLI_AUTH SECRETS LIBCRYPTO KERBEROS UTIL_LDB HEIMDAL_GSSAPI
+PRIVATE_DEPENDENCIES = \
+ SECRETS
+
+
+CREDENTIALS_OBJ_FILES = $(addprefix $(authsrcdir)/credentials/, credentials.o credentials_files.o credentials_ntlm.o credentials_krb5.o ../kerberos/kerberos_util.o)
+
+$(eval $(call proto_header_template,$(authsrcdir)/credentials/credentials_proto.h,$(CREDENTIALS_OBJ_FILES:.o=.c)))
+
+PUBLIC_HEADERS += $(authsrcdir)/credentials/credentials.h
+
+[PYTHON::pycredentials]
+LIBRARY_REALNAME = samba/credentials.$(SHLIBEXT)
+PUBLIC_DEPENDENCIES = CREDENTIALS LIBCMDLINE_CREDENTIALS PYTALLOC param
+
+pycredentials_OBJ_FILES = $(authsrcdir)/credentials/pycredentials.o
diff --git a/source4/auth/credentials/credentials.c b/source4/auth/credentials/credentials.c
new file mode 100644
index 0000000000..5fb180d7b1
--- /dev/null
+++ b/source4/auth/credentials/credentials.c
@@ -0,0 +1,761 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ User credentials handling
+
+ Copyright (C) Jelmer Vernooij 2005
+ Copyright (C) Tim Potter 2001
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "librpc/gen_ndr/samr.h" /* for struct samrPassword */
+#include "auth/credentials/credentials.h"
+#include "auth/credentials/credentials_krb5.h"
+#include "auth/credentials/credentials_proto.h"
+#include "libcli/auth/libcli_auth.h"
+#include "lib/events/events.h"
+#include "param/param.h"
+
+/**
+ * Create a new credentials structure
+ * @param mem_ctx TALLOC_CTX parent for credentials structure
+ */
+_PUBLIC_ struct cli_credentials *cli_credentials_init(TALLOC_CTX *mem_ctx)
+{
+ struct cli_credentials *cred = talloc(mem_ctx, struct cli_credentials);
+ if (!cred) {
+ return cred;
+ }
+
+ cred->netlogon_creds = NULL;
+ cred->machine_account_pending = false;
+ cred->workstation_obtained = CRED_UNINITIALISED;
+ cred->username_obtained = CRED_UNINITIALISED;
+ cred->password_obtained = CRED_UNINITIALISED;
+ cred->domain_obtained = CRED_UNINITIALISED;
+ cred->realm_obtained = CRED_UNINITIALISED;
+ cred->ccache_obtained = CRED_UNINITIALISED;
+ cred->client_gss_creds_obtained = CRED_UNINITIALISED;
+ cred->server_gss_creds_obtained = CRED_UNINITIALISED;
+ cred->keytab_obtained = CRED_UNINITIALISED;
+ cred->principal_obtained = CRED_UNINITIALISED;
+
+ cred->ccache_threshold = CRED_UNINITIALISED;
+ cred->client_gss_creds_threshold = CRED_UNINITIALISED;
+
+ cred->old_password = NULL;
+ cred->smb_krb5_context = NULL;
+ cred->salt_principal = NULL;
+ cred->machine_account = false;
+
+ cred->bind_dn = NULL;
+
+ cred->tries = 3;
+ cred->callback_running = false;
+
+ cli_credentials_set_kerberos_state(cred, CRED_AUTO_USE_KERBEROS);
+ cli_credentials_set_gensec_features(cred, 0);
+
+ return cred;
+}
+
+/**
+ * Create a new anonymous credential
+ * @param mem_ctx TALLOC_CTX parent for credentials structure
+ */
+_PUBLIC_ struct cli_credentials *cli_credentials_init_anon(TALLOC_CTX *mem_ctx)
+{
+ struct cli_credentials *anon_credentials;
+
+ anon_credentials = cli_credentials_init(mem_ctx);
+ cli_credentials_set_anonymous(anon_credentials);
+
+ return anon_credentials;
+}
+
+_PUBLIC_ void cli_credentials_set_kerberos_state(struct cli_credentials *creds,
+ enum credentials_use_kerberos use_kerberos)
+{
+ creds->use_kerberos = use_kerberos;
+}
+
+_PUBLIC_ enum credentials_use_kerberos cli_credentials_get_kerberos_state(struct cli_credentials *creds)
+{
+ return creds->use_kerberos;
+}
+
+_PUBLIC_ void cli_credentials_set_gensec_features(struct cli_credentials *creds, uint32_t gensec_features)
+{
+ creds->gensec_features = gensec_features;
+}
+
+_PUBLIC_ uint32_t cli_credentials_get_gensec_features(struct cli_credentials *creds)
+{
+ return creds->gensec_features;
+}
+
+
+/**
+ * Obtain the username for this credentials context.
+ * @param cred credentials context
+ * @retval The username set on this context.
+ * @note Return value will never be NULL except by programmer error.
+ */
+_PUBLIC_ const char *cli_credentials_get_username(struct cli_credentials *cred)
+{
+ if (cred->machine_account_pending) {
+ cli_credentials_set_machine_account(cred,
+ cred->machine_account_pending_lp_ctx);
+ }
+
+ if (cred->username_obtained == CRED_CALLBACK &&
+ !cred->callback_running) {
+ cred->callback_running = true;
+ cred->username = cred->username_cb(cred);
+ cred->callback_running = false;
+ cred->username_obtained = CRED_SPECIFIED;
+ cli_credentials_invalidate_ccache(cred, cred->username_obtained);
+ }
+
+ return cred->username;
+}
+
+_PUBLIC_ bool cli_credentials_set_username(struct cli_credentials *cred,
+ const char *val, enum credentials_obtained obtained)
+{
+ if (obtained >= cred->username_obtained) {
+ cred->username = talloc_strdup(cred, val);
+ cred->username_obtained = obtained;
+ cli_credentials_invalidate_ccache(cred, cred->username_obtained);
+ return true;
+ }
+
+ return false;
+}
+
+bool cli_credentials_set_username_callback(struct cli_credentials *cred,
+ const char *(*username_cb) (struct cli_credentials *))
+{
+ if (cred->username_obtained < CRED_CALLBACK) {
+ cred->username_cb = username_cb;
+ cred->username_obtained = CRED_CALLBACK;
+ return true;
+ }
+
+ return false;
+}
+
+_PUBLIC_ bool cli_credentials_set_bind_dn(struct cli_credentials *cred,
+ const char *bind_dn)
+{
+ cred->bind_dn = talloc_strdup(cred, bind_dn);
+ return true;
+}
+
+/**
+ * Obtain the BIND DN for this credentials context.
+ * @param cred credentials context
+ * @retval The username set on this context.
+ * @note Return value will be NULL if not specified explictly
+ */
+_PUBLIC_ const char *cli_credentials_get_bind_dn(struct cli_credentials *cred)
+{
+ return cred->bind_dn;
+}
+
+
+/**
+ * Obtain the client principal for this credentials context.
+ * @param cred credentials context
+ * @retval The username set on this context.
+ * @note Return value will never be NULL except by programmer error.
+ */
+_PUBLIC_ const char *cli_credentials_get_principal(struct cli_credentials *cred, TALLOC_CTX *mem_ctx)
+{
+ if (cred->machine_account_pending) {
+ cli_credentials_set_machine_account(cred,
+ cred->machine_account_pending_lp_ctx);
+ }
+
+ if (cred->principal_obtained == CRED_CALLBACK &&
+ !cred->callback_running) {
+ cred->callback_running = true;
+ cred->principal = cred->principal_cb(cred);
+ cred->callback_running = false;
+ cred->principal_obtained = CRED_SPECIFIED;
+ cli_credentials_invalidate_ccache(cred, cred->principal_obtained);
+ }
+
+ if (cred->principal_obtained < cred->username_obtained) {
+ if (cred->domain_obtained > cred->realm_obtained) {
+ return talloc_asprintf(mem_ctx, "%s@%s",
+ cli_credentials_get_username(cred),
+ cli_credentials_get_domain(cred));
+ } else {
+ return talloc_asprintf(mem_ctx, "%s@%s",
+ cli_credentials_get_username(cred),
+ cli_credentials_get_realm(cred));
+ }
+ }
+ return talloc_reference(mem_ctx, cred->principal);
+}
+
+bool cli_credentials_set_principal(struct cli_credentials *cred,
+ const char *val,
+ enum credentials_obtained obtained)
+{
+ if (obtained >= cred->principal_obtained) {
+ cred->principal = talloc_strdup(cred, val);
+ cred->principal_obtained = obtained;
+ cli_credentials_invalidate_ccache(cred, cred->principal_obtained);
+ return true;
+ }
+
+ return false;
+}
+
+/* Set a callback to get the principal. This could be a popup dialog,
+ * a terminal prompt or similar. */
+bool cli_credentials_set_principal_callback(struct cli_credentials *cred,
+ const char *(*principal_cb) (struct cli_credentials *))
+{
+ if (cred->principal_obtained < CRED_CALLBACK) {
+ cred->principal_cb = principal_cb;
+ cred->principal_obtained = CRED_CALLBACK;
+ return true;
+ }
+
+ return false;
+}
+
+/* Some of our tools are 'anonymous by default'. This is a single
+ * function to determine if authentication has been explicitly
+ * requested */
+
+_PUBLIC_ bool cli_credentials_authentication_requested(struct cli_credentials *cred)
+{
+ if (cred->bind_dn) {
+ return true;
+ }
+
+ if (cli_credentials_is_anonymous(cred)){
+ return false;
+ }
+
+ if (cred->principal_obtained >= CRED_SPECIFIED) {
+ return true;
+ }
+ if (cred->username_obtained >= CRED_SPECIFIED) {
+ return true;
+ }
+
+ if (cli_credentials_get_kerberos_state(cred) == CRED_MUST_USE_KERBEROS) {
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Obtain the password for this credentials context.
+ * @param cred credentials context
+ * @retval If set, the cleartext password, otherwise NULL
+ */
+_PUBLIC_ const char *cli_credentials_get_password(struct cli_credentials *cred)
+{
+ if (cred->machine_account_pending) {
+ cli_credentials_set_machine_account(cred,
+ cred->machine_account_pending_lp_ctx);
+ }
+
+ if (cred->password_obtained == CRED_CALLBACK &&
+ !cred->callback_running) {
+ cred->callback_running = true;
+ cred->password = cred->password_cb(cred);
+ cred->callback_running = false;
+ cred->password_obtained = CRED_CALLBACK_RESULT;
+ cli_credentials_invalidate_ccache(cred, cred->password_obtained);
+ }
+
+ return cred->password;
+}
+
+/* Set a password on the credentials context, including an indication
+ * of 'how' the password was obtained */
+
+_PUBLIC_ bool cli_credentials_set_password(struct cli_credentials *cred,
+ const char *val,
+ enum credentials_obtained obtained)
+{
+ if (obtained >= cred->password_obtained) {
+ cred->password = talloc_strdup(cred, val);
+ cred->password_obtained = obtained;
+ cli_credentials_invalidate_ccache(cred, cred->password_obtained);
+
+ cred->nt_hash = NULL;
+ cred->lm_response = data_blob(NULL, 0);
+ cred->nt_response = data_blob(NULL, 0);
+ return true;
+ }
+
+ return false;
+}
+
+_PUBLIC_ bool cli_credentials_set_password_callback(struct cli_credentials *cred,
+ const char *(*password_cb) (struct cli_credentials *))
+{
+ if (cred->password_obtained < CRED_CALLBACK) {
+ cred->password_cb = password_cb;
+ cred->password_obtained = CRED_CALLBACK;
+ cli_credentials_invalidate_ccache(cred, cred->password_obtained);
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Obtain the 'old' password for this credentials context (used for join accounts).
+ * @param cred credentials context
+ * @retval If set, the cleartext password, otherwise NULL
+ */
+const char *cli_credentials_get_old_password(struct cli_credentials *cred)
+{
+ if (cred->machine_account_pending) {
+ cli_credentials_set_machine_account(cred,
+ cred->machine_account_pending_lp_ctx);
+ }
+
+ return cred->old_password;
+}
+
+bool cli_credentials_set_old_password(struct cli_credentials *cred,
+ const char *val,
+ enum credentials_obtained obtained)
+{
+ cred->old_password = talloc_strdup(cred, val);
+ return true;
+}
+
+/**
+ * Obtain the password, in the form MD4(unicode(password)) for this credentials context.
+ *
+ * Sometimes we only have this much of the password, while the rest of
+ * the time this call avoids calling E_md4hash themselves.
+ *
+ * @param cred credentials context
+ * @retval If set, the cleartext password, otherwise NULL
+ */
+_PUBLIC_ const struct samr_Password *cli_credentials_get_nt_hash(struct cli_credentials *cred,
+ TALLOC_CTX *mem_ctx)
+{
+ const char *password = cli_credentials_get_password(cred);
+
+ if (password) {
+ struct samr_Password *nt_hash = talloc(mem_ctx, struct samr_Password);
+ if (!nt_hash) {
+ return NULL;
+ }
+
+ E_md4hash(password, nt_hash->hash);
+
+ return nt_hash;
+ } else {
+ return cred->nt_hash;
+ }
+}
+
+/**
+ * Obtain the 'short' or 'NetBIOS' domain for this credentials context.
+ * @param cred credentials context
+ * @retval The domain set on this context.
+ * @note Return value will never be NULL except by programmer error.
+ */
+_PUBLIC_ const char *cli_credentials_get_domain(struct cli_credentials *cred)
+{
+ if (cred->machine_account_pending) {
+ cli_credentials_set_machine_account(cred,
+ cred->machine_account_pending_lp_ctx);
+ }
+
+ if (cred->domain_obtained == CRED_CALLBACK &&
+ !cred->callback_running) {
+ cred->callback_running = true;
+ cred->domain = cred->domain_cb(cred);
+ cred->callback_running = false;
+ cred->domain_obtained = CRED_SPECIFIED;
+ cli_credentials_invalidate_ccache(cred, cred->domain_obtained);
+ }
+
+ return cred->domain;
+}
+
+
+_PUBLIC_ bool cli_credentials_set_domain(struct cli_credentials *cred,
+ const char *val,
+ enum credentials_obtained obtained)
+{
+ if (obtained >= cred->domain_obtained) {
+ /* it is important that the domain be in upper case,
+ * particularly for the sensitive NTLMv2
+ * calculations */
+ cred->domain = strupper_talloc(cred, val);
+ cred->domain_obtained = obtained;
+ cli_credentials_invalidate_ccache(cred, cred->domain_obtained);
+ return true;
+ }
+
+ return false;
+}
+
+bool cli_credentials_set_domain_callback(struct cli_credentials *cred,
+ const char *(*domain_cb) (struct cli_credentials *))
+{
+ if (cred->domain_obtained < CRED_CALLBACK) {
+ cred->domain_cb = domain_cb;
+ cred->domain_obtained = CRED_CALLBACK;
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Obtain the Kerberos realm for this credentials context.
+ * @param cred credentials context
+ * @retval The realm set on this context.
+ * @note Return value will never be NULL except by programmer error.
+ */
+_PUBLIC_ const char *cli_credentials_get_realm(struct cli_credentials *cred)
+{
+ if (cred->machine_account_pending) {
+ cli_credentials_set_machine_account(cred,
+ cred->machine_account_pending_lp_ctx);
+ }
+
+ if (cred->realm_obtained == CRED_CALLBACK &&
+ !cred->callback_running) {
+ cred->callback_running = true;
+ cred->realm = cred->realm_cb(cred);
+ cred->callback_running = false;
+ cred->realm_obtained = CRED_SPECIFIED;
+ cli_credentials_invalidate_ccache(cred, cred->realm_obtained);
+ }
+
+ return cred->realm;
+}
+
+/**
+ * Set the realm for this credentials context, and force it to
+ * uppercase for the sainity of our local kerberos libraries
+ */
+_PUBLIC_ bool cli_credentials_set_realm(struct cli_credentials *cred,
+ const char *val,
+ enum credentials_obtained obtained)
+{
+ if (obtained >= cred->realm_obtained) {
+ cred->realm = strupper_talloc(cred, val);
+ cred->realm_obtained = obtained;
+ cli_credentials_invalidate_ccache(cred, cred->realm_obtained);
+ return true;
+ }
+
+ return false;
+}
+
+bool cli_credentials_set_realm_callback(struct cli_credentials *cred,
+ const char *(*realm_cb) (struct cli_credentials *))
+{
+ if (cred->realm_obtained < CRED_CALLBACK) {
+ cred->realm_cb = realm_cb;
+ cred->realm_obtained = CRED_CALLBACK;
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Obtain the 'short' or 'NetBIOS' workstation name for this credentials context.
+ *
+ * @param cred credentials context
+ * @retval The workstation name set on this context.
+ * @note Return value will never be NULL except by programmer error.
+ */
+_PUBLIC_ const char *cli_credentials_get_workstation(struct cli_credentials *cred)
+{
+ if (cred->workstation_obtained == CRED_CALLBACK &&
+ !cred->callback_running) {
+ cred->callback_running = true;
+ cred->workstation = cred->workstation_cb(cred);
+ cred->callback_running = false;
+ cred->workstation_obtained = CRED_SPECIFIED;
+ }
+
+ return cred->workstation;
+}
+
+_PUBLIC_ bool cli_credentials_set_workstation(struct cli_credentials *cred,
+ const char *val,
+ enum credentials_obtained obtained)
+{
+ if (obtained >= cred->workstation_obtained) {
+ cred->workstation = talloc_strdup(cred, val);
+ cred->workstation_obtained = obtained;
+ return true;
+ }
+
+ return false;
+}
+
+bool cli_credentials_set_workstation_callback(struct cli_credentials *cred,
+ const char *(*workstation_cb) (struct cli_credentials *))
+{
+ if (cred->workstation_obtained < CRED_CALLBACK) {
+ cred->workstation_cb = workstation_cb;
+ cred->workstation_obtained = CRED_CALLBACK;
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Given a string, typically obtained from a -U argument, parse it into domain, username, realm and password fields
+ *
+ * The format accepted is [domain\\]user[%password] or user[@realm][%password]
+ *
+ * @param credentials Credentials structure on which to set the password
+ * @param data the string containing the username, password etc
+ * @param obtained This enum describes how 'specified' this password is
+ */
+
+_PUBLIC_ void cli_credentials_parse_string(struct cli_credentials *credentials, const char *data, enum credentials_obtained obtained)
+{
+ char *uname, *p;
+
+ if (strcmp("%",data) == 0) {
+ cli_credentials_set_anonymous(credentials);
+ return;
+ }
+
+ uname = talloc_strdup(credentials, data);
+ if ((p = strchr_m(uname,'%'))) {
+ *p = 0;
+ cli_credentials_set_password(credentials, p+1, obtained);
+ }
+
+ if ((p = strchr_m(uname,'@'))) {
+ cli_credentials_set_principal(credentials, uname, obtained);
+ *p = 0;
+ cli_credentials_set_realm(credentials, p+1, obtained);
+ return;
+ } else if ((p = strchr_m(uname,'\\')) || (p = strchr_m(uname, '/'))) {
+ *p = 0;
+ cli_credentials_set_domain(credentials, uname, obtained);
+ uname = p+1;
+ }
+ cli_credentials_set_username(credentials, uname, obtained);
+}
+
+/**
+ * Given a a credentials structure, print it as a string
+ *
+ * The format output is [domain\\]user[%password] or user[@realm][%password]
+ *
+ * @param credentials Credentials structure on which to set the password
+ * @param mem_ctx The memory context to place the result on
+ */
+
+_PUBLIC_ const char *cli_credentials_get_unparsed_name(struct cli_credentials *credentials, TALLOC_CTX *mem_ctx)
+{
+ const char *bind_dn = cli_credentials_get_bind_dn(credentials);
+ const char *domain;
+ const char *username;
+ const char *name;
+
+ if (bind_dn) {
+ name = talloc_reference(mem_ctx, bind_dn);
+ } else {
+ cli_credentials_get_ntlm_username_domain(credentials, mem_ctx, &username, &domain);
+ if (domain && domain[0]) {
+ name = talloc_asprintf(mem_ctx, "%s\\%s",
+ domain, username);
+ } else {
+ name = talloc_asprintf(mem_ctx, "%s",
+ username);
+ }
+ }
+ return name;
+}
+
+/**
+ * Specifies default values for domain, workstation and realm
+ * from the smb.conf configuration file
+ *
+ * @param cred Credentials structure to fill in
+ */
+_PUBLIC_ void cli_credentials_set_conf(struct cli_credentials *cred,
+ struct loadparm_context *lp_ctx)
+{
+ cli_credentials_set_username(cred, "", CRED_UNINITIALISED);
+ cli_credentials_set_domain(cred, lp_workgroup(lp_ctx), CRED_UNINITIALISED);
+ cli_credentials_set_workstation(cred, lp_netbios_name(lp_ctx), CRED_UNINITIALISED);
+ cli_credentials_set_realm(cred, lp_realm(lp_ctx), CRED_UNINITIALISED);
+}
+
+/**
+ * Guess defaults for credentials from environment variables,
+ * and from the configuration file
+ *
+ * @param cred Credentials structure to fill in
+ */
+_PUBLIC_ void cli_credentials_guess(struct cli_credentials *cred,
+ struct loadparm_context *lp_ctx)
+{
+ char *p;
+
+ if (lp_ctx != NULL) {
+ cli_credentials_set_conf(cred, lp_ctx);
+ }
+
+ if (getenv("LOGNAME")) {
+ cli_credentials_set_username(cred, getenv("LOGNAME"), CRED_GUESS_ENV);
+ }
+
+ if (getenv("USER")) {
+ cli_credentials_parse_string(cred, getenv("USER"), CRED_GUESS_ENV);
+ if ((p = strchr_m(getenv("USER"),'%'))) {
+ memset(p,0,strlen(cred->password));
+ }
+ }
+
+ if (getenv("PASSWD")) {
+ cli_credentials_set_password(cred, getenv("PASSWD"), CRED_GUESS_ENV);
+ }
+
+ if (getenv("PASSWD_FD")) {
+ cli_credentials_parse_password_fd(cred, atoi(getenv("PASSWD_FD")),
+ CRED_GUESS_FILE);
+ }
+
+ p = getenv("PASSWD_FILE");
+ if (p && p[0]) {
+ cli_credentials_parse_password_file(cred, p, CRED_GUESS_FILE);
+ }
+
+ if (cli_credentials_get_kerberos_state(cred) != CRED_DONT_USE_KERBEROS) {
+ cli_credentials_set_ccache(cred, event_context_find(cred), lp_ctx, NULL, CRED_GUESS_FILE);
+ }
+}
+
+/**
+ * Attach NETLOGON credentials for use with SCHANNEL
+ */
+
+_PUBLIC_ void cli_credentials_set_netlogon_creds(struct cli_credentials *cred,
+ struct creds_CredentialState *netlogon_creds)
+{
+ cred->netlogon_creds = talloc_reference(cred, netlogon_creds);
+}
+
+/**
+ * Return attached NETLOGON credentials
+ */
+
+struct creds_CredentialState *cli_credentials_get_netlogon_creds(struct cli_credentials *cred)
+{
+ return cred->netlogon_creds;
+}
+
+/**
+ * Set NETLOGON secure channel type
+ */
+
+_PUBLIC_ void cli_credentials_set_secure_channel_type(struct cli_credentials *cred,
+ enum netr_SchannelType secure_channel_type)
+{
+ cred->secure_channel_type = secure_channel_type;
+}
+
+/**
+ * Return NETLOGON secure chanel type
+ */
+
+_PUBLIC_ enum netr_SchannelType cli_credentials_get_secure_channel_type(struct cli_credentials *cred)
+{
+ return cred->secure_channel_type;
+}
+
+/**
+ * Fill in a credentials structure as the anonymous user
+ */
+_PUBLIC_ void cli_credentials_set_anonymous(struct cli_credentials *cred)
+{
+ cli_credentials_set_username(cred, "", CRED_SPECIFIED);
+ cli_credentials_set_domain(cred, "", CRED_SPECIFIED);
+ cli_credentials_set_password(cred, NULL, CRED_SPECIFIED);
+ cli_credentials_set_realm(cred, NULL, CRED_SPECIFIED);
+ cli_credentials_set_workstation(cred, "", CRED_UNINITIALISED);
+}
+
+/**
+ * Describe a credentials context as anonymous or authenticated
+ * @retval true if anonymous, false if a username is specified
+ */
+
+_PUBLIC_ bool cli_credentials_is_anonymous(struct cli_credentials *cred)
+{
+ const char *username;
+
+ if (cred->machine_account_pending) {
+ cli_credentials_set_machine_account(cred,
+ cred->machine_account_pending_lp_ctx);
+ }
+
+ username = cli_credentials_get_username(cred);
+
+ /* Yes, it is deliberate that we die if we have a NULL pointer
+ * here - anonymous is "", not NULL, which is 'never specified,
+ * never guessed', ie programmer bug */
+ if (!username[0]) {
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Mark the current password for a credentials struct as wrong. This will
+ * cause the password to be prompted again (if a callback is set).
+ *
+ * This will decrement the number of times the password can be tried.
+ *
+ * @retval whether the credentials struct is finished
+ */
+_PUBLIC_ bool cli_credentials_wrong_password(struct cli_credentials *cred)
+{
+ if (cred->password_obtained != CRED_CALLBACK_RESULT) {
+ return false;
+ }
+
+ cred->password_obtained = CRED_CALLBACK;
+
+ cred->tries--;
+
+ return (cred->tries > 0);
+}
diff --git a/source4/auth/credentials/credentials.h b/source4/auth/credentials/credentials.h
new file mode 100644
index 0000000000..3c2fb8f2e6
--- /dev/null
+++ b/source4/auth/credentials/credentials.h
@@ -0,0 +1,272 @@
+/*
+ samba -- Unix SMB/CIFS implementation.
+
+ Client credentials structure
+
+ Copyright (C) Jelmer Vernooij 2004-2006
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+#ifndef __CREDENTIALS_H__
+#define __CREDENTIALS_H__
+
+#include "../lib/util/data_blob.h"
+#include "librpc/gen_ndr/misc.h"
+
+struct ccache_container;
+struct tevent_context;
+
+/* In order of priority */
+enum credentials_obtained {
+ CRED_UNINITIALISED = 0, /* We don't even have a guess yet */
+ CRED_CALLBACK, /* Callback should be used to obtain value */
+ CRED_GUESS_ENV, /* Current value should be used, which was guessed */
+ CRED_GUESS_FILE, /* A guess from a file (or file pointed at in env variable) */
+ CRED_CALLBACK_RESULT, /* Value was obtained from a callback */
+ CRED_SPECIFIED /* Was explicitly specified on the command-line */
+};
+
+enum credentials_use_kerberos {
+ CRED_AUTO_USE_KERBEROS = 0, /* Default, we try kerberos if available */
+ CRED_DONT_USE_KERBEROS, /* Sometimes trying kerberos just does 'bad things', so don't */
+ CRED_MUST_USE_KERBEROS /* Sometimes administrators are parinoid, so always do kerberos */
+};
+
+#define CLI_CRED_NTLM2 0x01
+#define CLI_CRED_NTLMv2_AUTH 0x02
+#define CLI_CRED_LANMAN_AUTH 0x04
+#define CLI_CRED_NTLM_AUTH 0x08
+#define CLI_CRED_CLEAR_AUTH 0x10 /* TODO: Push cleartext auth with this flag */
+
+struct cli_credentials {
+ enum credentials_obtained workstation_obtained;
+ enum credentials_obtained username_obtained;
+ enum credentials_obtained password_obtained;
+ enum credentials_obtained domain_obtained;
+ enum credentials_obtained realm_obtained;
+ enum credentials_obtained ccache_obtained;
+ enum credentials_obtained client_gss_creds_obtained;
+ enum credentials_obtained principal_obtained;
+ enum credentials_obtained keytab_obtained;
+ enum credentials_obtained server_gss_creds_obtained;
+
+ /* Threshold values (essentially a MAX() over a number of the
+ * above) for the ccache and GSS credentials, to ensure we
+ * regenerate/pick correctly */
+
+ enum credentials_obtained ccache_threshold;
+ enum credentials_obtained client_gss_creds_threshold;
+
+ const char *workstation;
+ const char *username;
+ const char *password;
+ const char *old_password;
+ const char *domain;
+ const char *realm;
+ const char *principal;
+ const char *salt_principal;
+
+ const char *bind_dn;
+
+ /* Allows authentication from a keytab or similar */
+ struct samr_Password *nt_hash;
+
+ /* Allows NTLM pass-though authentication */
+ DATA_BLOB lm_response;
+ DATA_BLOB nt_response;
+
+ struct ccache_container *ccache;
+ struct gssapi_creds_container *client_gss_creds;
+ struct keytab_container *keytab;
+ struct gssapi_creds_container *server_gss_creds;
+
+ const char *(*workstation_cb) (struct cli_credentials *);
+ const char *(*password_cb) (struct cli_credentials *);
+ const char *(*username_cb) (struct cli_credentials *);
+ const char *(*domain_cb) (struct cli_credentials *);
+ const char *(*realm_cb) (struct cli_credentials *);
+ const char *(*principal_cb) (struct cli_credentials *);
+
+ /* Private handle for the callback routines to use */
+ void *priv_data;
+
+ struct creds_CredentialState *netlogon_creds;
+ enum netr_SchannelType secure_channel_type;
+ int kvno;
+
+ struct smb_krb5_context *smb_krb5_context;
+
+ /* We are flagged to get machine account details from the
+ * secrets.ldb when we are asked for a username or password */
+ bool machine_account_pending;
+ struct loadparm_context *machine_account_pending_lp_ctx;
+
+ /* Is this a machine account? */
+ bool machine_account;
+
+ /* Should we be trying to use kerberos? */
+ enum credentials_use_kerberos use_kerberos;
+
+ /* gensec features which should be used for connections */
+ uint32_t gensec_features;
+
+ /* Number of retries left before bailing out */
+ int tries;
+
+ /* Whether any callback is currently running */
+ bool callback_running;
+};
+
+struct ldb_context;
+struct loadparm_context;
+struct ccache_container;
+
+struct gssapi_creds_container;
+
+const char *cli_credentials_get_workstation(struct cli_credentials *cred);
+bool cli_credentials_set_workstation(struct cli_credentials *cred,
+ const char *val,
+ enum credentials_obtained obtained);
+bool cli_credentials_is_anonymous(struct cli_credentials *cred);
+struct cli_credentials *cli_credentials_init(TALLOC_CTX *mem_ctx);
+void cli_credentials_set_anonymous(struct cli_credentials *cred);
+bool cli_credentials_wrong_password(struct cli_credentials *cred);
+const char *cli_credentials_get_password(struct cli_credentials *cred);
+void cli_credentials_get_ntlm_username_domain(struct cli_credentials *cred, TALLOC_CTX *mem_ctx,
+ const char **username,
+ const char **domain);
+NTSTATUS cli_credentials_get_ntlm_response(struct cli_credentials *cred, TALLOC_CTX *mem_ctx,
+ int *flags,
+ DATA_BLOB challenge, DATA_BLOB target_info,
+ DATA_BLOB *_lm_response, DATA_BLOB *_nt_response,
+ DATA_BLOB *_lm_session_key, DATA_BLOB *_session_key);
+const char *cli_credentials_get_realm(struct cli_credentials *cred);
+const char *cli_credentials_get_username(struct cli_credentials *cred);
+int cli_credentials_get_krb5_context(struct cli_credentials *cred,
+ struct tevent_context *event_ctx,
+ struct loadparm_context *lp_ctx,
+ struct smb_krb5_context **smb_krb5_context);
+int cli_credentials_get_ccache(struct cli_credentials *cred,
+ struct tevent_context *event_ctx,
+ struct loadparm_context *lp_ctx,
+ struct ccache_container **ccc);
+int cli_credentials_get_keytab(struct cli_credentials *cred,
+ struct tevent_context *event_ctx,
+ struct loadparm_context *lp_ctx,
+ struct keytab_container **_ktc);
+const char *cli_credentials_get_domain(struct cli_credentials *cred);
+struct creds_CredentialState *cli_credentials_get_netlogon_creds(struct cli_credentials *cred);
+void cli_credentials_set_machine_account_pending(struct cli_credentials *cred,
+ struct loadparm_context *lp_ctx);
+void cli_credentials_set_conf(struct cli_credentials *cred,
+ struct loadparm_context *lp_ctx);
+const char *cli_credentials_get_principal(struct cli_credentials *cred, TALLOC_CTX *mem_ctx);
+int cli_credentials_get_server_gss_creds(struct cli_credentials *cred,
+ struct tevent_context *event_ctx,
+ struct loadparm_context *lp_ctx,
+ struct gssapi_creds_container **_gcc);
+int cli_credentials_get_client_gss_creds(struct cli_credentials *cred,
+ struct tevent_context *event_ctx,
+ struct loadparm_context *lp_ctx,
+ struct gssapi_creds_container **_gcc);
+void cli_credentials_set_kerberos_state(struct cli_credentials *creds,
+ enum credentials_use_kerberos use_kerberos);
+bool cli_credentials_set_domain(struct cli_credentials *cred,
+ const char *val,
+ enum credentials_obtained obtained);
+bool cli_credentials_set_domain_callback(struct cli_credentials *cred,
+ const char *(*domain_cb) (struct cli_credentials *));
+bool cli_credentials_set_username(struct cli_credentials *cred,
+ const char *val, enum credentials_obtained obtained);
+bool cli_credentials_set_username_callback(struct cli_credentials *cred,
+ const char *(*username_cb) (struct cli_credentials *));
+bool cli_credentials_set_principal(struct cli_credentials *cred,
+ const char *val,
+ enum credentials_obtained obtained);
+bool cli_credentials_set_principal_callback(struct cli_credentials *cred,
+ const char *(*principal_cb) (struct cli_credentials *));
+bool cli_credentials_set_password(struct cli_credentials *cred,
+ const char *val,
+ enum credentials_obtained obtained);
+struct cli_credentials *cli_credentials_init_anon(TALLOC_CTX *mem_ctx);
+void cli_credentials_parse_string(struct cli_credentials *credentials, const char *data, enum credentials_obtained obtained);
+const struct samr_Password *cli_credentials_get_nt_hash(struct cli_credentials *cred,
+ TALLOC_CTX *mem_ctx);
+bool cli_credentials_set_realm(struct cli_credentials *cred,
+ const char *val,
+ enum credentials_obtained obtained);
+void cli_credentials_set_secure_channel_type(struct cli_credentials *cred,
+ enum netr_SchannelType secure_channel_type);
+void cli_credentials_set_netlogon_creds(struct cli_credentials *cred,
+ struct creds_CredentialState *netlogon_creds);
+NTSTATUS cli_credentials_set_krb5_context(struct cli_credentials *cred,
+ struct smb_krb5_context *smb_krb5_context);
+NTSTATUS cli_credentials_set_stored_principal(struct cli_credentials *cred,
+ struct tevent_context *event_ctx,
+ struct loadparm_context *lp_ctx,
+ const char *serviceprincipal);
+NTSTATUS cli_credentials_set_machine_account(struct cli_credentials *cred,
+ struct loadparm_context *lp_ctx);
+bool cli_credentials_authentication_requested(struct cli_credentials *cred);
+void cli_credentials_guess(struct cli_credentials *cred,
+ struct loadparm_context *lp_ctx);
+bool cli_credentials_set_bind_dn(struct cli_credentials *cred,
+ const char *bind_dn);
+const char *cli_credentials_get_bind_dn(struct cli_credentials *cred);
+bool cli_credentials_parse_file(struct cli_credentials *cred, const char *file, enum credentials_obtained obtained);
+const char *cli_credentials_get_unparsed_name(struct cli_credentials *credentials, TALLOC_CTX *mem_ctx);
+bool cli_credentials_set_password_callback(struct cli_credentials *cred,
+ const char *(*password_cb) (struct cli_credentials *));
+enum netr_SchannelType cli_credentials_get_secure_channel_type(struct cli_credentials *cred);
+void cli_credentials_set_kvno(struct cli_credentials *cred,
+ int kvno);
+bool cli_credentials_set_nt_hash(struct cli_credentials *cred,
+ const struct samr_Password *nt_hash,
+ enum credentials_obtained obtained);
+bool cli_credentials_set_ntlm_response(struct cli_credentials *cred,
+ const DATA_BLOB *lm_response,
+ const DATA_BLOB *nt_response,
+ enum credentials_obtained obtained);
+int cli_credentials_set_keytab_name(struct cli_credentials *cred,
+ struct tevent_context *event_ctx,
+ struct loadparm_context *lp_ctx,
+ const char *keytab_name,
+ enum credentials_obtained obtained);
+int cli_credentials_update_keytab(struct cli_credentials *cred,
+ struct tevent_context *event_ctx,
+ struct loadparm_context *lp_ctx);
+void cli_credentials_set_gensec_features(struct cli_credentials *creds, uint32_t gensec_features);
+uint32_t cli_credentials_get_gensec_features(struct cli_credentials *creds);
+int cli_credentials_set_ccache(struct cli_credentials *cred,
+ struct tevent_context *event_ctx,
+ struct loadparm_context *lp_ctx,
+ const char *name,
+ enum credentials_obtained obtained);
+bool cli_credentials_parse_password_file(struct cli_credentials *credentials, const char *file, enum credentials_obtained obtained);
+bool cli_credentials_parse_password_fd(struct cli_credentials *credentials,
+ int fd, enum credentials_obtained obtained);
+void cli_credentials_invalidate_ccache(struct cli_credentials *cred,
+ enum credentials_obtained obtained);
+void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal);
+enum credentials_use_kerberos cli_credentials_get_kerberos_state(struct cli_credentials *creds);
+NTSTATUS cli_credentials_set_secrets(struct cli_credentials *cred,
+ struct tevent_context *event_ctx,
+ struct loadparm_context *lp_ctx,
+ struct ldb_context *ldb,
+ const char *base,
+ const char *filter);
+ int cli_credentials_get_kvno(struct cli_credentials *cred);
+
+#endif /* __CREDENTIALS_H__ */
diff --git a/source4/auth/credentials/credentials_files.c b/source4/auth/credentials/credentials_files.c
new file mode 100644
index 0000000000..3fe38d5cd1
--- /dev/null
+++ b/source4/auth/credentials/credentials_files.c
@@ -0,0 +1,433 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ User credentials handling (as regards on-disk files)
+
+ Copyright (C) Jelmer Vernooij 2005
+ Copyright (C) Tim Potter 2001
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/events/events.h"
+#include "lib/ldb/include/ldb.h"
+#include "librpc/gen_ndr/samr.h" /* for struct samrPassword */
+#include "param/secrets.h"
+#include "system/filesys.h"
+#include "../lib/util/util_ldb.h"
+#include "auth/credentials/credentials.h"
+#include "auth/credentials/credentials_krb5.h"
+#include "auth/credentials/credentials_proto.h"
+#include "param/param.h"
+#include "lib/events/events.h"
+
+/**
+ * Read a file descriptor, and parse it for a password (eg from a file or stdin)
+ *
+ * @param credentials Credentials structure on which to set the password
+ * @param fd open file descriptor to read the password from
+ * @param obtained This enum describes how 'specified' this password is
+ */
+
+_PUBLIC_ bool cli_credentials_parse_password_fd(struct cli_credentials *credentials,
+ int fd, enum credentials_obtained obtained)
+{
+ char *p;
+ char pass[128];
+
+ for(p = pass, *p = '\0'; /* ensure that pass is null-terminated */
+ p && p - pass < sizeof(pass);) {
+ switch (read(fd, p, 1)) {
+ case 1:
+ if (*p != '\n' && *p != '\0') {
+ *++p = '\0'; /* advance p, and null-terminate pass */
+ break;
+ }
+ /* fall through */
+ case 0:
+ if (p - pass) {
+ *p = '\0'; /* null-terminate it, just in case... */
+ p = NULL; /* then force the loop condition to become false */
+ break;
+ } else {
+ fprintf(stderr, "Error reading password from file descriptor %d: %s\n", fd, "empty password\n");
+ return false;
+ }
+
+ default:
+ fprintf(stderr, "Error reading password from file descriptor %d: %s\n",
+ fd, strerror(errno));
+ return false;
+ }
+ }
+
+ cli_credentials_set_password(credentials, pass, obtained);
+ return true;
+}
+
+/**
+ * Read a named file, and parse it for a password
+ *
+ * @param credentials Credentials structure on which to set the password
+ * @param file a named file to read the password from
+ * @param obtained This enum describes how 'specified' this password is
+ */
+
+_PUBLIC_ bool cli_credentials_parse_password_file(struct cli_credentials *credentials, const char *file, enum credentials_obtained obtained)
+{
+ int fd = open(file, O_RDONLY, 0);
+ bool ret;
+
+ if (fd < 0) {
+ fprintf(stderr, "Error opening password file %s: %s\n",
+ file, strerror(errno));
+ return false;
+ }
+
+ ret = cli_credentials_parse_password_fd(credentials, fd, obtained);
+
+ close(fd);
+
+ return ret;
+}
+
+/**
+ * Read a named file, and parse it for username, domain, realm and password
+ *
+ * @param credentials Credentials structure on which to set the password
+ * @param file a named file to read the details from
+ * @param obtained This enum describes how 'specified' this password is
+ */
+
+_PUBLIC_ bool cli_credentials_parse_file(struct cli_credentials *cred, const char *file, enum credentials_obtained obtained)
+{
+ uint16_t len = 0;
+ char *ptr, *val, *param;
+ char **lines;
+ int i, numlines;
+
+ lines = file_lines_load(file, &numlines, 0, NULL);
+
+ if (lines == NULL)
+ {
+ /* fail if we can't open the credentials file */
+ d_printf("ERROR: Unable to open credentials file!\n");
+ return false;
+ }
+
+ for (i = 0; i < numlines; i++) {
+ len = strlen(lines[i]);
+
+ if (len == 0)
+ continue;
+
+ /* break up the line into parameter & value.
+ * will need to eat a little whitespace possibly */
+ param = lines[i];
+ if (!(ptr = strchr_m (lines[i], '=')))
+ continue;
+
+ val = ptr+1;
+ *ptr = '\0';
+
+ /* eat leading white space */
+ while ((*val!='\0') && ((*val==' ') || (*val=='\t')))
+ val++;
+
+ if (strwicmp("password", param) == 0) {
+ cli_credentials_set_password(cred, val, obtained);
+ } else if (strwicmp("username", param) == 0) {
+ cli_credentials_set_username(cred, val, obtained);
+ } else if (strwicmp("domain", param) == 0) {
+ cli_credentials_set_domain(cred, val, obtained);
+ } else if (strwicmp("realm", param) == 0) {
+ cli_credentials_set_realm(cred, val, obtained);
+ }
+ memset(lines[i], 0, len);
+ }
+
+ talloc_free(lines);
+
+ return true;
+}
+
+
+/**
+ * Fill in credentials for the machine trust account, from the secrets database.
+ *
+ * @param cred Credentials structure to fill in
+ * @retval NTSTATUS error detailing any failure
+ */
+_PUBLIC_ NTSTATUS cli_credentials_set_secrets(struct cli_credentials *cred,
+ struct tevent_context *event_ctx,
+ struct loadparm_context *lp_ctx,
+ struct ldb_context *ldb,
+ const char *base,
+ const char *filter)
+{
+ TALLOC_CTX *mem_ctx;
+
+ int ldb_ret;
+ struct ldb_message **msgs;
+ const char *attrs[] = {
+ "secret",
+ "priorSecret",
+ "samAccountName",
+ "flatname",
+ "realm",
+ "secureChannelType",
+ "unicodePwd",
+ "msDS-KeyVersionNumber",
+ "saltPrincipal",
+ "privateKeytab",
+ "krb5Keytab",
+ "servicePrincipalName",
+ "ldapBindDn",
+ NULL
+ };
+
+ const char *machine_account;
+ const char *password;
+ const char *old_password;
+ const char *domain;
+ const char *realm;
+ enum netr_SchannelType sct;
+ const char *salt_principal;
+ const char *keytab;
+
+ /* ok, we are going to get it now, don't recurse back here */
+ cred->machine_account_pending = false;
+
+ /* some other parts of the system will key off this */
+ cred->machine_account = true;
+
+ mem_ctx = talloc_named(cred, 0, "cli_credentials fetch machine password");
+
+ if (!ldb) {
+ /* Local secrets are stored in secrets.ldb */
+ ldb = secrets_db_connect(mem_ctx, event_ctx, lp_ctx);
+ if (!ldb) {
+ /* set anonymous as the fallback, if the machine account won't work */
+ cli_credentials_set_anonymous(cred);
+ DEBUG(1, ("Could not open secrets.ldb\n"));
+ talloc_free(mem_ctx);
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ }
+ }
+
+ /* search for the secret record */
+ ldb_ret = gendb_search(ldb,
+ mem_ctx, ldb_dn_new(mem_ctx, ldb, base),
+ &msgs, attrs,
+ "%s", filter);
+ if (ldb_ret == 0) {
+ DEBUG(5, ("(normal if no LDAP backend required) Could not find entry to match filter: '%s' base: '%s'\n",
+ filter, base));
+ /* set anonymous as the fallback, if the machine account won't work */
+ cli_credentials_set_anonymous(cred);
+ talloc_free(mem_ctx);
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ } else if (ldb_ret != 1) {
+ DEBUG(5, ("Found more than one (%d) entry to match filter: '%s' base: '%s'\n",
+ ldb_ret, filter, base));
+ /* set anonymous as the fallback, if the machine account won't work */
+ cli_credentials_set_anonymous(cred);
+ talloc_free(mem_ctx);
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ }
+
+ password = ldb_msg_find_attr_as_string(msgs[0], "secret", NULL);
+ old_password = ldb_msg_find_attr_as_string(msgs[0], "priorSecret", NULL);
+
+ machine_account = ldb_msg_find_attr_as_string(msgs[0], "samAccountName", NULL);
+
+ if (!machine_account) {
+ machine_account = ldb_msg_find_attr_as_string(msgs[0], "servicePrincipalName", NULL);
+
+ if (!machine_account) {
+ const char *ldap_bind_dn = ldb_msg_find_attr_as_string(msgs[0], "ldapBindDn", NULL);
+ if (!ldap_bind_dn) {
+ DEBUG(1, ("Could not find 'samAccountName', 'servicePrincipalName' or 'ldapBindDn' in secrets record: filter: '%s' base: '%s'\n",
+ filter, base));
+ /* set anonymous as the fallback, if the machine account won't work */
+ cli_credentials_set_anonymous(cred);
+ talloc_free(mem_ctx);
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ }
+ }
+ }
+
+ salt_principal = ldb_msg_find_attr_as_string(msgs[0], "saltPrincipal", NULL);
+ cli_credentials_set_salt_principal(cred, salt_principal);
+
+ sct = ldb_msg_find_attr_as_int(msgs[0], "secureChannelType", 0);
+ if (sct) {
+ cli_credentials_set_secure_channel_type(cred, sct);
+ }
+
+ if (!password) {
+ const struct ldb_val *nt_password_hash = ldb_msg_find_ldb_val(msgs[0], "unicodePwd");
+ struct samr_Password hash;
+ ZERO_STRUCT(hash);
+ if (nt_password_hash) {
+ memcpy(hash.hash, nt_password_hash->data,
+ MIN(nt_password_hash->length, sizeof(hash.hash)));
+
+ cli_credentials_set_nt_hash(cred, &hash, CRED_SPECIFIED);
+ } else {
+ cli_credentials_set_password(cred, NULL, CRED_SPECIFIED);
+ }
+ } else {
+ cli_credentials_set_password(cred, password, CRED_SPECIFIED);
+ }
+
+
+ domain = ldb_msg_find_attr_as_string(msgs[0], "flatname", NULL);
+ if (domain) {
+ cli_credentials_set_domain(cred, domain, CRED_SPECIFIED);
+ }
+
+ realm = ldb_msg_find_attr_as_string(msgs[0], "realm", NULL);
+ if (realm) {
+ cli_credentials_set_realm(cred, realm, CRED_SPECIFIED);
+ }
+
+ if (machine_account) {
+ cli_credentials_set_username(cred, machine_account, CRED_SPECIFIED);
+ }
+
+ cli_credentials_set_kvno(cred, ldb_msg_find_attr_as_int(msgs[0], "msDS-KeyVersionNumber", 0));
+
+ /* If there was an external keytab specified by reference in
+ * the LDB, then use this. Otherwise we will make one up
+ * (chewing CPU time) from the password */
+ keytab = ldb_msg_find_attr_as_string(msgs[0], "krb5Keytab", NULL);
+ if (keytab) {
+ cli_credentials_set_keytab_name(cred, event_ctx, lp_ctx, keytab, CRED_SPECIFIED);
+ } else {
+ keytab = ldb_msg_find_attr_as_string(msgs[0], "privateKeytab", NULL);
+ if (keytab) {
+ keytab = talloc_asprintf(mem_ctx, "FILE:%s", private_path(mem_ctx, lp_ctx, keytab));
+ if (keytab) {
+ cli_credentials_set_keytab_name(cred, event_ctx, lp_ctx, keytab, CRED_SPECIFIED);
+ }
+ }
+ }
+ talloc_free(mem_ctx);
+
+ return NT_STATUS_OK;
+}
+
+/**
+ * Fill in credentials for the machine trust account, from the secrets database.
+ *
+ * @param cred Credentials structure to fill in
+ * @retval NTSTATUS error detailing any failure
+ */
+_PUBLIC_ NTSTATUS cli_credentials_set_machine_account(struct cli_credentials *cred,
+ struct loadparm_context *lp_ctx)
+{
+ NTSTATUS status;
+ char *filter;
+ /* Bleh, nasty recursion issues: We are setting a machine
+ * account here, so we don't want the 'pending' flag around
+ * any more */
+ cred->machine_account_pending = false;
+ filter = talloc_asprintf(cred, SECRETS_PRIMARY_DOMAIN_FILTER,
+ cli_credentials_get_domain(cred));
+ status = cli_credentials_set_secrets(cred, event_context_find(cred), lp_ctx, NULL,
+ SECRETS_PRIMARY_DOMAIN_DN,
+ filter);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Could not find machine account in secrets database: %s", nt_errstr(status)));
+ }
+ return status;
+}
+
+/**
+ * Fill in credentials for the machine trust account, from the secrets database.
+ *
+ * @param cred Credentials structure to fill in
+ * @retval NTSTATUS error detailing any failure
+ */
+NTSTATUS cli_credentials_set_krbtgt(struct cli_credentials *cred,
+ struct tevent_context *event_ctx,
+ struct loadparm_context *lp_ctx)
+{
+ NTSTATUS status;
+ char *filter;
+ /* Bleh, nasty recursion issues: We are setting a machine
+ * account here, so we don't want the 'pending' flag around
+ * any more */
+ cred->machine_account_pending = false;
+ filter = talloc_asprintf(cred, SECRETS_KRBTGT_SEARCH,
+ cli_credentials_get_realm(cred),
+ cli_credentials_get_domain(cred));
+ status = cli_credentials_set_secrets(cred, event_ctx, lp_ctx, NULL,
+ SECRETS_PRINCIPALS_DN,
+ filter);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Could not find krbtgt (master Kerberos) account in secrets database: %s", nt_errstr(status)));
+ }
+ return status;
+}
+
+/**
+ * Fill in credentials for a particular prinicpal, from the secrets database.
+ *
+ * @param cred Credentials structure to fill in
+ * @retval NTSTATUS error detailing any failure
+ */
+_PUBLIC_ NTSTATUS cli_credentials_set_stored_principal(struct cli_credentials *cred,
+ struct tevent_context *event_ctx,
+ struct loadparm_context *lp_ctx,
+ const char *serviceprincipal)
+{
+ NTSTATUS status;
+ char *filter;
+ /* Bleh, nasty recursion issues: We are setting a machine
+ * account here, so we don't want the 'pending' flag around
+ * any more */
+ cred->machine_account_pending = false;
+ filter = talloc_asprintf(cred, SECRETS_PRINCIPAL_SEARCH,
+ cli_credentials_get_realm(cred),
+ cli_credentials_get_domain(cred),
+ serviceprincipal);
+ status = cli_credentials_set_secrets(cred, event_ctx, lp_ctx, NULL,
+ SECRETS_PRINCIPALS_DN, filter);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Could not find %s principal in secrets database: %s", serviceprincipal, nt_errstr(status)));
+ }
+ return status;
+}
+
+/**
+ * Ask that when required, the credentials system will be filled with
+ * machine trust account, from the secrets database.
+ *
+ * @param cred Credentials structure to fill in
+ * @note This function is used to call the above function after, rather
+ * than during, popt processing.
+ *
+ */
+_PUBLIC_ void cli_credentials_set_machine_account_pending(struct cli_credentials *cred,
+ struct loadparm_context *lp_ctx)
+{
+ cred->machine_account_pending = true;
+ cred->machine_account_pending_lp_ctx = lp_ctx;
+}
+
+
diff --git a/source4/auth/credentials/credentials_krb5.c b/source4/auth/credentials/credentials_krb5.c
new file mode 100644
index 0000000000..bc3d05f529
--- /dev/null
+++ b/source4/auth/credentials/credentials_krb5.c
@@ -0,0 +1,762 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Handle user credentials (as regards krb5)
+
+ Copyright (C) Jelmer Vernooij 2005
+ Copyright (C) Tim Potter 2001
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/kerberos.h"
+#include "auth/kerberos/kerberos.h"
+#include "auth/credentials/credentials.h"
+#include "auth/credentials/credentials_proto.h"
+#include "auth/credentials/credentials_krb5.h"
+#include "param/param.h"
+
+_PUBLIC_ int cli_credentials_get_krb5_context(struct cli_credentials *cred,
+ struct tevent_context *event_ctx,
+ struct loadparm_context *lp_ctx,
+ struct smb_krb5_context **smb_krb5_context)
+{
+ int ret;
+ if (cred->smb_krb5_context) {
+ *smb_krb5_context = cred->smb_krb5_context;
+ return 0;
+ }
+
+ ret = smb_krb5_init_context(cred, event_ctx, lp_ctx, &cred->smb_krb5_context);
+ if (ret) {
+ cred->smb_krb5_context = NULL;
+ return ret;
+ }
+ *smb_krb5_context = cred->smb_krb5_context;
+ return 0;
+}
+
+/* This needs to be called directly after the cli_credentials_init(),
+ * otherwise we might have problems with the krb5 context already
+ * being here.
+ */
+_PUBLIC_ NTSTATUS cli_credentials_set_krb5_context(struct cli_credentials *cred,
+ struct smb_krb5_context *smb_krb5_context)
+{
+ if (!talloc_reference(cred, smb_krb5_context)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ cred->smb_krb5_context = smb_krb5_context;
+ return NT_STATUS_OK;
+}
+
+static int cli_credentials_set_from_ccache(struct cli_credentials *cred,
+ struct ccache_container *ccache,
+ enum credentials_obtained obtained)
+{
+
+ krb5_principal princ;
+ krb5_error_code ret;
+ char *name;
+ char **realm;
+
+ if (cred->ccache_obtained > obtained) {
+ return 0;
+ }
+
+ ret = krb5_cc_get_principal(ccache->smb_krb5_context->krb5_context,
+ ccache->ccache, &princ);
+
+ if (ret) {
+ char *err_mess = smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context,
+ ret, cred);
+ DEBUG(1,("failed to get principal from ccache: %s\n",
+ err_mess));
+ talloc_free(err_mess);
+ return ret;
+ }
+
+ ret = krb5_unparse_name(ccache->smb_krb5_context->krb5_context, princ, &name);
+ if (ret) {
+ char *err_mess = smb_get_krb5_error_message(ccache->smb_krb5_context->krb5_context, ret, cred);
+ DEBUG(1,("failed to unparse principal from ccache: %s\n",
+ err_mess));
+ talloc_free(err_mess);
+ return ret;
+ }
+
+ realm = krb5_princ_realm(ccache->smb_krb5_context->krb5_context, princ);
+
+ cli_credentials_set_principal(cred, name, obtained);
+
+ free(name);
+
+ krb5_free_principal(ccache->smb_krb5_context->krb5_context, princ);
+
+ /* set the ccache_obtained here, as it just got set to UNINITIALISED by the calls above */
+ cred->ccache_obtained = obtained;
+
+ return 0;
+}
+
+/* Free a memory ccache */
+static int free_mccache(struct ccache_container *ccc)
+{
+ krb5_cc_destroy(ccc->smb_krb5_context->krb5_context, ccc->ccache);
+
+ return 0;
+}
+
+/* Free a disk-based ccache */
+static int free_dccache(struct ccache_container *ccc) {
+ krb5_cc_close(ccc->smb_krb5_context->krb5_context, ccc->ccache);
+
+ return 0;
+}
+
+_PUBLIC_ int cli_credentials_set_ccache(struct cli_credentials *cred,
+ struct tevent_context *event_ctx,
+ struct loadparm_context *lp_ctx,
+ const char *name,
+ enum credentials_obtained obtained)
+{
+ krb5_error_code ret;
+ krb5_principal princ;
+ struct ccache_container *ccc;
+ if (cred->ccache_obtained > obtained) {
+ return 0;
+ }
+
+ ccc = talloc(cred, struct ccache_container);
+ if (!ccc) {
+ return ENOMEM;
+ }
+
+ ret = cli_credentials_get_krb5_context(cred, event_ctx, lp_ctx,
+ &ccc->smb_krb5_context);
+ if (ret) {
+ talloc_free(ccc);
+ return ret;
+ }
+ if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
+ talloc_free(ccc);
+ return ENOMEM;
+ }
+
+ if (name) {
+ ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, name, &ccc->ccache);
+ if (ret) {
+ DEBUG(1,("failed to read krb5 ccache: %s: %s\n",
+ name,
+ smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
+ talloc_free(ccc);
+ return ret;
+ }
+ } else {
+ ret = krb5_cc_default(ccc->smb_krb5_context->krb5_context, &ccc->ccache);
+ if (ret) {
+ DEBUG(3,("failed to read default krb5 ccache: %s\n",
+ smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
+ talloc_free(ccc);
+ return ret;
+ }
+ }
+
+ talloc_set_destructor(ccc, free_dccache);
+
+ ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context, ccc->ccache, &princ);
+
+ if (ret) {
+ DEBUG(3,("failed to get principal from default ccache: %s\n",
+ smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
+ talloc_free(ccc);
+ return ret;
+ }
+
+ krb5_free_principal(ccc->smb_krb5_context->krb5_context, princ);
+
+ ret = cli_credentials_set_from_ccache(cred, ccc, obtained);
+
+ if (ret) {
+ return ret;
+ }
+
+ cred->ccache = ccc;
+ cred->ccache_obtained = obtained;
+ talloc_steal(cred, ccc);
+
+ cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
+ return 0;
+}
+
+
+static int cli_credentials_new_ccache(struct cli_credentials *cred,
+ struct tevent_context *event_ctx,
+ struct loadparm_context *lp_ctx,
+ struct ccache_container **_ccc)
+{
+ krb5_error_code ret;
+ struct ccache_container *ccc = talloc(cred, struct ccache_container);
+ char *ccache_name;
+ if (!ccc) {
+ return ENOMEM;
+ }
+
+ ccache_name = talloc_asprintf(ccc, "MEMORY:%p",
+ ccc);
+
+ if (!ccache_name) {
+ talloc_free(ccc);
+ return ENOMEM;
+ }
+
+ ret = cli_credentials_get_krb5_context(cred, event_ctx, lp_ctx,
+ &ccc->smb_krb5_context);
+ if (ret) {
+ talloc_free(ccc);
+ return ret;
+ }
+ if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
+ talloc_free(ccc);
+ return ENOMEM;
+ }
+
+ ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, ccache_name,
+ &ccc->ccache);
+ if (ret) {
+ DEBUG(1,("failed to generate a new krb5 ccache (%s): %s\n",
+ ccache_name,
+ smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
+ talloc_free(ccache_name);
+ talloc_free(ccc);
+ return ret;
+ }
+
+ talloc_set_destructor(ccc, free_mccache);
+
+ talloc_free(ccache_name);
+
+ *_ccc = ccc;
+
+ return ret;
+}
+
+_PUBLIC_ int cli_credentials_get_ccache(struct cli_credentials *cred,
+ struct tevent_context *event_ctx,
+ struct loadparm_context *lp_ctx,
+ struct ccache_container **ccc)
+{
+ krb5_error_code ret;
+
+ if (cred->machine_account_pending) {
+ cli_credentials_set_machine_account(cred, lp_ctx);
+ }
+
+ if (cred->ccache_obtained >= cred->ccache_threshold &&
+ cred->ccache_obtained > CRED_UNINITIALISED) {
+ *ccc = cred->ccache;
+ return 0;
+ }
+ if (cli_credentials_is_anonymous(cred)) {
+ return EINVAL;
+ }
+
+ ret = cli_credentials_new_ccache(cred, event_ctx, lp_ctx, ccc);
+ if (ret) {
+ return ret;
+ }
+
+ ret = kinit_to_ccache(cred, cred, (*ccc)->smb_krb5_context, (*ccc)->ccache);
+ if (ret) {
+ return ret;
+ }
+
+ ret = cli_credentials_set_from_ccache(cred, *ccc,
+ (MAX(MAX(cred->principal_obtained,
+ cred->username_obtained),
+ cred->password_obtained)));
+
+ cred->ccache = *ccc;
+ cred->ccache_obtained = cred->principal_obtained;
+ if (ret) {
+ return ret;
+ }
+ cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
+ return ret;
+}
+
+void cli_credentials_invalidate_client_gss_creds(struct cli_credentials *cred,
+ enum credentials_obtained obtained)
+{
+ /* If the caller just changed the username/password etc, then
+ * any cached credentials are now invalid */
+ if (obtained >= cred->client_gss_creds_obtained) {
+ if (cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
+ talloc_unlink(cred, cred->client_gss_creds);
+ cred->client_gss_creds = NULL;
+ }
+ cred->client_gss_creds_obtained = CRED_UNINITIALISED;
+ }
+ /* Now that we know that the data is 'this specified', then
+ * don't allow something less 'known' to be returned as a
+ * ccache. Ie, if the username is on the commmand line, we
+ * don't want to later guess to use a file-based ccache */
+ if (obtained > cred->client_gss_creds_threshold) {
+ cred->client_gss_creds_threshold = obtained;
+ }
+}
+
+_PUBLIC_ void cli_credentials_invalidate_ccache(struct cli_credentials *cred,
+ enum credentials_obtained obtained)
+{
+ /* If the caller just changed the username/password etc, then
+ * any cached credentials are now invalid */
+ if (obtained >= cred->ccache_obtained) {
+ if (cred->ccache_obtained > CRED_UNINITIALISED) {
+ talloc_unlink(cred, cred->ccache);
+ cred->ccache = NULL;
+ }
+ cred->ccache_obtained = CRED_UNINITIALISED;
+ }
+ /* Now that we know that the data is 'this specified', then
+ * don't allow something less 'known' to be returned as a
+ * ccache. Ie, if the username is on the commmand line, we
+ * don't want to later guess to use a file-based ccache */
+ if (obtained > cred->ccache_threshold) {
+ cred->ccache_threshold = obtained;
+ }
+
+ cli_credentials_invalidate_client_gss_creds(cred,
+ obtained);
+}
+
+static int free_gssapi_creds(struct gssapi_creds_container *gcc)
+{
+ OM_uint32 min_stat, maj_stat;
+ maj_stat = gss_release_cred(&min_stat, &gcc->creds);
+ return 0;
+}
+
+_PUBLIC_ int cli_credentials_get_client_gss_creds(struct cli_credentials *cred,
+ struct tevent_context *event_ctx,
+ struct loadparm_context *lp_ctx,
+ struct gssapi_creds_container **_gcc)
+{
+ int ret = 0;
+ OM_uint32 maj_stat, min_stat;
+ struct gssapi_creds_container *gcc;
+ struct ccache_container *ccache;
+ gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
+ krb5_enctype *etypes = NULL;
+
+ if (cred->client_gss_creds_obtained >= cred->client_gss_creds_threshold &&
+ cred->client_gss_creds_obtained > CRED_UNINITIALISED) {
+ *_gcc = cred->client_gss_creds;
+ return 0;
+ }
+
+ ret = cli_credentials_get_ccache(cred, event_ctx, lp_ctx,
+ &ccache);
+ if (ret) {
+ DEBUG(1, ("Failed to get CCACHE for GSSAPI client: %s\n", error_message(ret)));
+ return ret;
+ }
+
+ gcc = talloc(cred, struct gssapi_creds_container);
+ if (!gcc) {
+ return ENOMEM;
+ }
+
+ maj_stat = gss_krb5_import_cred(&min_stat, ccache->ccache, NULL, NULL,
+ &gcc->creds);
+ if (maj_stat) {
+ talloc_free(gcc);
+ if (min_stat) {
+ ret = min_stat;
+ } else {
+ ret = EINVAL;
+ }
+ return ret;
+ }
+
+ /*
+ * transfer the enctypes from the smb_krb5_context to the gssapi layer
+ *
+ * We use 'our' smb_krb5_context to do the AS-REQ and it is possible
+ * to configure the enctypes via the krb5.conf.
+ *
+ * And the gss_init_sec_context() creates it's own krb5_context and
+ * the TGS-REQ had all enctypes in it and only the ones configured
+ * and used for the AS-REQ, so it wasn't possible to disable the usage
+ * of AES keys.
+ */
+ min_stat = krb5_get_default_in_tkt_etypes(ccache->smb_krb5_context->krb5_context,
+ &etypes);
+ if (min_stat == 0) {
+ OM_uint32 num_ktypes;
+
+ for (num_ktypes = 0; etypes[num_ktypes]; num_ktypes++);
+
+ maj_stat = gss_krb5_set_allowable_enctypes(&min_stat, gcc->creds,
+ num_ktypes, etypes);
+ krb5_xfree (etypes);
+ if (maj_stat) {
+ talloc_free(gcc);
+ if (min_stat) {
+ ret = min_stat;
+ } else {
+ ret = EINVAL;
+ }
+ return ret;
+ }
+ }
+
+ /* don't force GSS_C_CONF_FLAG and GSS_C_INTEG_FLAG */
+ maj_stat = gss_set_cred_option(&min_stat, &gcc->creds,
+ GSS_KRB5_CRED_NO_CI_FLAGS_X,
+ &empty_buffer);
+ if (maj_stat) {
+ talloc_free(gcc);
+ if (min_stat) {
+ ret = min_stat;
+ } else {
+ ret = EINVAL;
+ }
+ return ret;
+ }
+
+ cred->client_gss_creds_obtained = cred->ccache_obtained;
+ talloc_set_destructor(gcc, free_gssapi_creds);
+ cred->client_gss_creds = gcc;
+ *_gcc = gcc;
+ return 0;
+}
+
+/**
+ Set a gssapi cred_id_t into the credentials system. (Client case)
+
+ This grabs the credentials both 'intact' and getting the krb5
+ ccache out of it. This routine can be generalised in future for
+ the case where we deal with GSSAPI mechs other than krb5.
+
+ On sucess, the caller must not free gssapi_cred, as it now belongs
+ to the credentials system.
+*/
+
+ int cli_credentials_set_client_gss_creds(struct cli_credentials *cred,
+ struct tevent_context *event_ctx,
+ struct loadparm_context *lp_ctx,
+ gss_cred_id_t gssapi_cred,
+ enum credentials_obtained obtained)
+{
+ int ret;
+ OM_uint32 maj_stat, min_stat;
+ struct ccache_container *ccc;
+ struct gssapi_creds_container *gcc;
+ if (cred->client_gss_creds_obtained > obtained) {
+ return 0;
+ }
+
+ gcc = talloc(cred, struct gssapi_creds_container);
+ if (!gcc) {
+ return ENOMEM;
+ }
+
+ ret = cli_credentials_new_ccache(cred, event_ctx, lp_ctx, &ccc);
+ if (ret != 0) {
+ return ret;
+ }
+
+ maj_stat = gss_krb5_copy_ccache(&min_stat,
+ gssapi_cred, ccc->ccache);
+ if (maj_stat) {
+ if (min_stat) {
+ ret = min_stat;
+ } else {
+ ret = EINVAL;
+ }
+ }
+
+ if (ret == 0) {
+ ret = cli_credentials_set_from_ccache(cred, ccc, obtained);
+ }
+ cred->ccache = ccc;
+ cred->ccache_obtained = obtained;
+ if (ret == 0) {
+ gcc->creds = gssapi_cred;
+ talloc_set_destructor(gcc, free_gssapi_creds);
+
+ /* set the clinet_gss_creds_obtained here, as it just
+ got set to UNINITIALISED by the calls above */
+ cred->client_gss_creds_obtained = obtained;
+ cred->client_gss_creds = gcc;
+ }
+ return ret;
+}
+
+/* Get the keytab (actually, a container containing the krb5_keytab)
+ * attached to this context. If this hasn't been done or set before,
+ * it will be generated from the password.
+ */
+_PUBLIC_ int cli_credentials_get_keytab(struct cli_credentials *cred,
+ struct tevent_context *event_ctx,
+ struct loadparm_context *lp_ctx,
+ struct keytab_container **_ktc)
+{
+ krb5_error_code ret;
+ struct keytab_container *ktc;
+ struct smb_krb5_context *smb_krb5_context;
+ const char **enctype_strings;
+ TALLOC_CTX *mem_ctx;
+
+ if (cred->keytab_obtained >= (MAX(cred->principal_obtained,
+ cred->username_obtained))) {
+ *_ktc = cred->keytab;
+ return 0;
+ }
+
+ if (cli_credentials_is_anonymous(cred)) {
+ return EINVAL;
+ }
+
+ ret = cli_credentials_get_krb5_context(cred, event_ctx, lp_ctx,
+ &smb_krb5_context);
+ if (ret) {
+ return ret;
+ }
+
+ mem_ctx = talloc_new(cred);
+ if (!mem_ctx) {
+ return ENOMEM;
+ }
+
+ enctype_strings = cli_credentials_get_enctype_strings(cred);
+
+ ret = smb_krb5_create_memory_keytab(mem_ctx, cred,
+ smb_krb5_context,
+ enctype_strings, &ktc);
+ if (ret) {
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ cred->keytab_obtained = (MAX(cred->principal_obtained,
+ cred->username_obtained));
+
+ talloc_steal(cred, ktc);
+ cred->keytab = ktc;
+ *_ktc = cred->keytab;
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+/* Given the name of a keytab (presumably in the format
+ * FILE:/etc/krb5.keytab), open it and attach it */
+
+_PUBLIC_ int cli_credentials_set_keytab_name(struct cli_credentials *cred,
+ struct tevent_context *event_ctx,
+ struct loadparm_context *lp_ctx,
+ const char *keytab_name,
+ enum credentials_obtained obtained)
+{
+ krb5_error_code ret;
+ struct keytab_container *ktc;
+ struct smb_krb5_context *smb_krb5_context;
+ TALLOC_CTX *mem_ctx;
+
+ if (cred->keytab_obtained >= obtained) {
+ return 0;
+ }
+
+ ret = cli_credentials_get_krb5_context(cred, event_ctx, lp_ctx, &smb_krb5_context);
+ if (ret) {
+ return ret;
+ }
+
+ mem_ctx = talloc_new(cred);
+ if (!mem_ctx) {
+ return ENOMEM;
+ }
+
+ ret = smb_krb5_open_keytab(mem_ctx, smb_krb5_context,
+ keytab_name, &ktc);
+ if (ret) {
+ return ret;
+ }
+
+ cred->keytab_obtained = obtained;
+
+ talloc_steal(cred, ktc);
+ cred->keytab = ktc;
+ talloc_free(mem_ctx);
+
+ return ret;
+}
+
+_PUBLIC_ int cli_credentials_update_keytab(struct cli_credentials *cred,
+ struct tevent_context *event_ctx,
+ struct loadparm_context *lp_ctx)
+{
+ krb5_error_code ret;
+ struct keytab_container *ktc;
+ struct smb_krb5_context *smb_krb5_context;
+ const char **enctype_strings;
+ TALLOC_CTX *mem_ctx;
+
+ mem_ctx = talloc_new(cred);
+ if (!mem_ctx) {
+ return ENOMEM;
+ }
+
+ ret = cli_credentials_get_krb5_context(cred, event_ctx, lp_ctx, &smb_krb5_context);
+ if (ret) {
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ enctype_strings = cli_credentials_get_enctype_strings(cred);
+
+ ret = cli_credentials_get_keytab(cred, event_ctx, lp_ctx, &ktc);
+ if (ret != 0) {
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ ret = smb_krb5_update_keytab(mem_ctx, cred, smb_krb5_context, enctype_strings, ktc);
+
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+/* Get server gss credentials (in gsskrb5, this means the keytab) */
+
+_PUBLIC_ int cli_credentials_get_server_gss_creds(struct cli_credentials *cred,
+ struct tevent_context *event_ctx,
+ struct loadparm_context *lp_ctx,
+ struct gssapi_creds_container **_gcc)
+{
+ int ret = 0;
+ OM_uint32 maj_stat, min_stat;
+ struct gssapi_creds_container *gcc;
+ struct keytab_container *ktc;
+ struct smb_krb5_context *smb_krb5_context;
+ TALLOC_CTX *mem_ctx;
+ krb5_principal princ;
+
+ if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained,
+ MAX(cred->principal_obtained,
+ cred->username_obtained)))) {
+ *_gcc = cred->server_gss_creds;
+ return 0;
+ }
+
+ ret = cli_credentials_get_krb5_context(cred, event_ctx, lp_ctx, &smb_krb5_context);
+ if (ret) {
+ return ret;
+ }
+
+ ret = cli_credentials_get_keytab(cred, event_ctx, lp_ctx, &ktc);
+ if (ret) {
+ DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret)));
+ return ret;
+ }
+
+ mem_ctx = talloc_new(cred);
+ if (!mem_ctx) {
+ return ENOMEM;
+ }
+
+ ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ);
+ if (ret) {
+ DEBUG(1,("cli_credentials_get_server_gss_creds: makeing krb5 principal failed (%s)\n",
+ smb_get_krb5_error_message(smb_krb5_context->krb5_context,
+ ret, mem_ctx)));
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ gcc = talloc(cred, struct gssapi_creds_container);
+ if (!gcc) {
+ talloc_free(mem_ctx);
+ return ENOMEM;
+ }
+
+ /* This creates a GSSAPI cred_id_t with the principal and keytab set */
+ maj_stat = gss_krb5_import_cred(&min_stat, NULL, princ, ktc->keytab,
+ &gcc->creds);
+ if (maj_stat) {
+ if (min_stat) {
+ ret = min_stat;
+ } else {
+ ret = EINVAL;
+ }
+ }
+ if (ret == 0) {
+ cred->server_gss_creds_obtained = cred->keytab_obtained;
+ talloc_set_destructor(gcc, free_gssapi_creds);
+ cred->server_gss_creds = gcc;
+ *_gcc = gcc;
+ }
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+/**
+ * Set Kerberos KVNO
+ */
+
+_PUBLIC_ void cli_credentials_set_kvno(struct cli_credentials *cred,
+ int kvno)
+{
+ cred->kvno = kvno;
+}
+
+/**
+ * Return Kerberos KVNO
+ */
+
+_PUBLIC_ int cli_credentials_get_kvno(struct cli_credentials *cred)
+{
+ return cred->kvno;
+}
+
+
+const char **cli_credentials_get_enctype_strings(struct cli_credentials *cred)
+{
+ /* If this is ever made user-configurable, we need to add code
+ * to remove/hide the other entries from the generated
+ * keytab */
+ static const char *default_enctypes[] = {
+ "des-cbc-md5",
+ "aes256-cts-hmac-sha1-96",
+ "des3-cbc-sha1",
+ "arcfour-hmac-md5",
+ NULL
+ };
+ return default_enctypes;
+}
+
+const char *cli_credentials_get_salt_principal(struct cli_credentials *cred)
+{
+ return cred->salt_principal;
+}
+
+_PUBLIC_ void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal)
+{
+ cred->salt_principal = talloc_strdup(cred, principal);
+}
+
+
diff --git a/source4/auth/credentials/credentials_krb5.h b/source4/auth/credentials/credentials_krb5.h
new file mode 100644
index 0000000000..0d0e9f330f
--- /dev/null
+++ b/source4/auth/credentials/credentials_krb5.h
@@ -0,0 +1,46 @@
+/*
+ samba -- Unix SMB/CIFS implementation.
+
+ Client credentials structure
+
+ Copyright (C) Jelmer Vernooij 2004-2006
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __CREDENTIALS_KRB5_H__
+#define __CREDENTIALS_KRB5_H__
+
+#include <gssapi/gssapi.h>
+#include <krb5.h>
+
+struct gssapi_creds_container {
+ gss_cred_id_t creds;
+};
+
+/* Manually prototyped here to avoid needing gss headers in most callers */
+int cli_credentials_set_client_gss_creds(struct cli_credentials *cred,
+ struct tevent_context *event_ctx,
+ struct loadparm_context *lp_ctx,
+ gss_cred_id_t gssapi_cred,
+ enum credentials_obtained obtained);
+
+/* Manually prototyped here to avoid needing krb5 headers in most callers */
+krb5_error_code principal_from_credentials(TALLOC_CTX *parent_ctx,
+ struct cli_credentials *credentials,
+ struct smb_krb5_context *smb_krb5_context,
+ krb5_principal *princ);
+
+#endif /* __CREDENTIALS_KRB5_H__ */
diff --git a/source4/auth/credentials/credentials_ntlm.c b/source4/auth/credentials/credentials_ntlm.c
new file mode 100644
index 0000000000..ef41971462
--- /dev/null
+++ b/source4/auth/credentials/credentials_ntlm.c
@@ -0,0 +1,269 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ User credentials handling
+
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "librpc/gen_ndr/samr.h" /* for struct samrPassword */
+#include "../lib/crypto/crypto.h"
+#include "libcli/auth/libcli_auth.h"
+#include "auth/credentials/credentials.h"
+
+_PUBLIC_ void cli_credentials_get_ntlm_username_domain(struct cli_credentials *cred, TALLOC_CTX *mem_ctx,
+ const char **username,
+ const char **domain)
+{
+ if (cred->principal_obtained > cred->username_obtained) {
+ *domain = talloc_strdup(mem_ctx, "");
+ *username = cli_credentials_get_principal(cred, mem_ctx);
+ } else {
+ *domain = cli_credentials_get_domain(cred);
+ *username = cli_credentials_get_username(cred);
+ }
+}
+
+_PUBLIC_ NTSTATUS cli_credentials_get_ntlm_response(struct cli_credentials *cred, TALLOC_CTX *mem_ctx,
+ int *flags,
+ DATA_BLOB challenge, DATA_BLOB target_info,
+ DATA_BLOB *_lm_response, DATA_BLOB *_nt_response,
+ DATA_BLOB *_lm_session_key, DATA_BLOB *_session_key)
+{
+ const char *user, *domain;
+ DATA_BLOB lm_response, nt_response;
+ DATA_BLOB lm_session_key, session_key;
+ const struct samr_Password *nt_hash;
+ lm_session_key = data_blob(NULL, 0);
+
+ /* We may already have an NTLM response we prepared earlier.
+ * This is used for NTLM pass-though authentication */
+ if (cred->nt_response.data || cred->lm_response.data) {
+ *_nt_response = cred->nt_response;
+ *_lm_response = cred->lm_response;
+
+ if (!cred->lm_response.data) {
+ *flags = *flags & ~CLI_CRED_LANMAN_AUTH;
+ }
+ *_lm_session_key = data_blob(NULL, 0);
+ *_session_key = data_blob(NULL, 0);
+ return NT_STATUS_OK;
+ }
+
+ nt_hash = cli_credentials_get_nt_hash(cred, mem_ctx);
+
+ cli_credentials_get_ntlm_username_domain(cred, mem_ctx, &user, &domain);
+
+ /* If we are sending a username@realm login (see function
+ * above), then we will not send LM, it will not be
+ * accepted */
+ if (cred->principal_obtained > cred->username_obtained) {
+ *flags = *flags & ~CLI_CRED_LANMAN_AUTH;
+ }
+
+ /* Likewise if we are a machine account (avoid protocol downgrade attacks) */
+ if (cred->machine_account) {
+ *flags = *flags & ~CLI_CRED_LANMAN_AUTH;
+ }
+
+ if (cred->use_kerberos == CRED_MUST_USE_KERBEROS) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (!nt_hash) {
+ static const uint8_t zeros[16];
+ /* do nothing - blobs are zero length */
+
+ /* session key is all zeros */
+ session_key = data_blob_talloc(mem_ctx, zeros, 16);
+ lm_session_key = data_blob_talloc(mem_ctx, zeros, 16);
+
+ lm_response = data_blob(NULL, 0);
+ nt_response = data_blob(NULL, 0);
+
+ /* not doing NTLM2 without a password */
+ *flags &= ~CLI_CRED_NTLM2;
+ } else if (*flags & CLI_CRED_NTLMv2_AUTH) {
+
+ if (!target_info.length) {
+ /* be lazy, match win2k - we can't do NTLMv2 without it */
+ DEBUG(1, ("Server did not provide 'target information', required for NTLMv2\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* TODO: if the remote server is standalone, then we should replace 'domain'
+ with the server name as supplied above */
+
+ if (!SMBNTLMv2encrypt_hash(mem_ctx,
+ user,
+ domain,
+ nt_hash->hash, &challenge,
+ &target_info,
+ &lm_response, &nt_response,
+ NULL, &session_key)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* LM Key is incompatible... */
+ *flags &= ~CLI_CRED_LANMAN_AUTH;
+ } else if (*flags & CLI_CRED_NTLM2) {
+ struct MD5Context md5_session_nonce_ctx;
+ uint8_t session_nonce[16];
+ uint8_t session_nonce_hash[16];
+ uint8_t user_session_key[16];
+
+ lm_response = data_blob_talloc(mem_ctx, NULL, 24);
+ generate_random_buffer(lm_response.data, 8);
+ memset(lm_response.data+8, 0, 16);
+
+ memcpy(session_nonce, challenge.data, 8);
+ memcpy(&session_nonce[8], lm_response.data, 8);
+
+ MD5Init(&md5_session_nonce_ctx);
+ MD5Update(&md5_session_nonce_ctx, challenge.data, 8);
+ MD5Update(&md5_session_nonce_ctx, lm_response.data, 8);
+ MD5Final(session_nonce_hash, &md5_session_nonce_ctx);
+
+ DEBUG(5, ("NTLMSSP challenge set by NTLM2\n"));
+ DEBUG(5, ("challenge is: \n"));
+ dump_data(5, session_nonce_hash, 8);
+
+ nt_response = data_blob_talloc(mem_ctx, NULL, 24);
+ SMBOWFencrypt(nt_hash->hash,
+ session_nonce_hash,
+ nt_response.data);
+
+ session_key = data_blob_talloc(mem_ctx, NULL, 16);
+
+ SMBsesskeygen_ntv1(nt_hash->hash, user_session_key);
+ hmac_md5(user_session_key, session_nonce, sizeof(session_nonce), session_key.data);
+ dump_data_pw("NTLM2 session key:\n", session_key.data, session_key.length);
+
+ /* LM Key is incompatible... */
+ *flags &= ~CLI_CRED_LANMAN_AUTH;
+ } else {
+ uint8_t lm_hash[16];
+ nt_response = data_blob_talloc(mem_ctx, NULL, 24);
+ SMBOWFencrypt(nt_hash->hash, challenge.data,
+ nt_response.data);
+
+ session_key = data_blob_talloc(mem_ctx, NULL, 16);
+ SMBsesskeygen_ntv1(nt_hash->hash, session_key.data);
+ dump_data_pw("NT session key:\n", session_key.data, session_key.length);
+
+ /* lanman auth is insecure, it may be disabled.
+ We may also not have a password */
+ if (*flags & CLI_CRED_LANMAN_AUTH) {
+ const char *password;
+ password = cli_credentials_get_password(cred);
+ if (!password) {
+ lm_response = nt_response;
+ } else {
+ lm_response = data_blob_talloc(mem_ctx, NULL, 24);
+ if (!SMBencrypt(password,challenge.data,
+ lm_response.data)) {
+ /* If the LM password was too long (and therefore the LM hash being
+ of the first 14 chars only), don't send it.
+
+ We don't have any better options but to send the NT response
+ */
+ data_blob_free(&lm_response);
+ lm_response = nt_response;
+ /* LM Key is incompatible with 'long' passwords */
+ *flags &= ~CLI_CRED_LANMAN_AUTH;
+ } else {
+ E_deshash(password, lm_hash);
+ lm_session_key = data_blob_talloc(mem_ctx, NULL, 16);
+ memcpy(lm_session_key.data, lm_hash, 8);
+ memset(&lm_session_key.data[8], '\0', 8);
+
+ if (!(*flags & CLI_CRED_NTLM_AUTH)) {
+ session_key = lm_session_key;
+ }
+ }
+ }
+ } else {
+ const char *password;
+
+ /* LM Key is incompatible... */
+ lm_response = nt_response;
+ *flags &= ~CLI_CRED_LANMAN_AUTH;
+
+ password = cli_credentials_get_password(cred);
+ if (password) {
+ E_deshash(password, lm_hash);
+ lm_session_key = data_blob_talloc(mem_ctx, NULL, 16);
+ memcpy(lm_session_key.data, lm_hash, 8);
+ memset(&lm_session_key.data[8], '\0', 8);
+ }
+ }
+ }
+ if (_lm_response) {
+ *_lm_response = lm_response;
+ }
+ if (_nt_response) {
+ *_nt_response = nt_response;
+ }
+ if (_lm_session_key) {
+ *_lm_session_key = lm_session_key;
+ }
+ if (_session_key) {
+ *_session_key = session_key;
+ }
+ return NT_STATUS_OK;
+}
+
+_PUBLIC_ bool cli_credentials_set_nt_hash(struct cli_credentials *cred,
+ const struct samr_Password *nt_hash,
+ enum credentials_obtained obtained)
+{
+ if (obtained >= cred->password_obtained) {
+ cli_credentials_set_password(cred, NULL, obtained);
+ if (nt_hash) {
+ cred->nt_hash = talloc(cred, struct samr_Password);
+ *cred->nt_hash = *nt_hash;
+ } else {
+ cred->nt_hash = NULL;
+ }
+ return true;
+ }
+
+ return false;
+}
+
+_PUBLIC_ bool cli_credentials_set_ntlm_response(struct cli_credentials *cred,
+ const DATA_BLOB *lm_response,
+ const DATA_BLOB *nt_response,
+ enum credentials_obtained obtained)
+{
+ if (obtained >= cred->password_obtained) {
+ cli_credentials_set_password(cred, NULL, obtained);
+ if (nt_response) {
+ cred->nt_response = data_blob_talloc(cred, nt_response->data, nt_response->length);
+ talloc_steal(cred, cred->nt_response.data);
+ }
+ if (nt_response) {
+ cred->lm_response = data_blob_talloc(cred, lm_response->data, lm_response->length);
+ }
+ return true;
+ }
+
+ return false;
+}
+
diff --git a/source4/auth/credentials/pycredentials.c b/source4/auth/credentials/pycredentials.c
new file mode 100644
index 0000000000..3eacf9ada1
--- /dev/null
+++ b/source4/auth/credentials/pycredentials.c
@@ -0,0 +1,316 @@
+/*
+ Unix SMB/CIFS implementation.
+ Copyright (C) Jelmer Vernooij <jelmer@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include <Python.h>
+#include "pycredentials.h"
+#include "param/param.h"
+#include "lib/cmdline/credentials.h"
+#include "librpc/gen_ndr/samr.h" /* for struct samr_Password */
+#include "libcli/util/pyerrors.h"
+#include "param/pyparam.h"
+
+#ifndef Py_RETURN_NONE
+#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
+#endif
+
+struct cli_credentials *cli_credentials_from_py_object(PyObject *py_obj)
+{
+ if (py_obj == Py_None) {
+ return cli_credentials_init_anon(NULL);
+ }
+
+ return PyCredentials_AsCliCredentials(py_obj);
+}
+
+static PyObject *PyString_FromStringOrNULL(const char *str)
+{
+ if (str == NULL)
+ Py_RETURN_NONE;
+ return PyString_FromString(str);
+}
+
+static PyObject *py_creds_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
+{
+ return py_talloc_import(type, cli_credentials_init(NULL));
+}
+
+static PyObject *py_creds_get_username(py_talloc_Object *self)
+{
+ return PyString_FromStringOrNULL(cli_credentials_get_username(PyCredentials_AsCliCredentials(self)));
+}
+
+static PyObject *py_creds_set_username(py_talloc_Object *self, PyObject *args)
+{
+ char *newval;
+ enum credentials_obtained obt = CRED_SPECIFIED;
+ if (!PyArg_ParseTuple(args, "s|i", &newval, &obt))
+ return NULL;
+
+ return PyBool_FromLong(cli_credentials_set_username(PyCredentials_AsCliCredentials(self), newval, obt));
+}
+
+static PyObject *py_creds_get_password(py_talloc_Object *self)
+{
+ return PyString_FromStringOrNULL(cli_credentials_get_password(PyCredentials_AsCliCredentials(self)));
+}
+
+
+static PyObject *py_creds_set_password(py_talloc_Object *self, PyObject *args)
+{
+ char *newval;
+ enum credentials_obtained obt = CRED_SPECIFIED;
+ if (!PyArg_ParseTuple(args, "s|i", &newval, &obt))
+ return NULL;
+
+ return PyBool_FromLong(cli_credentials_set_password(PyCredentials_AsCliCredentials(self), newval, obt));
+}
+
+static PyObject *py_creds_get_domain(py_talloc_Object *self)
+{
+ return PyString_FromStringOrNULL(cli_credentials_get_domain(PyCredentials_AsCliCredentials(self)));
+}
+
+static PyObject *py_creds_set_domain(py_talloc_Object *self, PyObject *args)
+{
+ char *newval;
+ enum credentials_obtained obt = CRED_SPECIFIED;
+ if (!PyArg_ParseTuple(args, "s|i", &newval, &obt))
+ return NULL;
+
+ return PyBool_FromLong(cli_credentials_set_domain(PyCredentials_AsCliCredentials(self), newval, obt));
+}
+
+static PyObject *py_creds_get_realm(py_talloc_Object *self)
+{
+ return PyString_FromStringOrNULL(cli_credentials_get_realm(PyCredentials_AsCliCredentials(self)));
+}
+
+static PyObject *py_creds_set_realm(py_talloc_Object *self, PyObject *args)
+{
+ char *newval;
+ enum credentials_obtained obt = CRED_SPECIFIED;
+ if (!PyArg_ParseTuple(args, "s|i", &newval, &obt))
+ return NULL;
+
+ return PyBool_FromLong(cli_credentials_set_realm(PyCredentials_AsCliCredentials(self), newval, obt));
+}
+
+static PyObject *py_creds_get_bind_dn(py_talloc_Object *self)
+{
+ return PyString_FromStringOrNULL(cli_credentials_get_bind_dn(PyCredentials_AsCliCredentials(self)));
+}
+
+static PyObject *py_creds_set_bind_dn(py_talloc_Object *self, PyObject *args)
+{
+ char *newval;
+ if (!PyArg_ParseTuple(args, "s", &newval))
+ return NULL;
+
+ return PyBool_FromLong(cli_credentials_set_bind_dn(PyCredentials_AsCliCredentials(self), newval));
+}
+
+static PyObject *py_creds_get_workstation(py_talloc_Object *self)
+{
+ return PyString_FromStringOrNULL(cli_credentials_get_workstation(PyCredentials_AsCliCredentials(self)));
+}
+
+static PyObject *py_creds_set_workstation(py_talloc_Object *self, PyObject *args)
+{
+ char *newval;
+ enum credentials_obtained obt = CRED_SPECIFIED;
+ if (!PyArg_ParseTuple(args, "s|i", &newval, &obt))
+ return NULL;
+
+ return PyBool_FromLong(cli_credentials_set_workstation(PyCredentials_AsCliCredentials(self), newval, obt));
+}
+
+static PyObject *py_creds_is_anonymous(py_talloc_Object *self)
+{
+ return PyBool_FromLong(cli_credentials_is_anonymous(PyCredentials_AsCliCredentials(self)));
+}
+
+static PyObject *py_creds_set_anonymous(py_talloc_Object *self)
+{
+ cli_credentials_set_anonymous(PyCredentials_AsCliCredentials(self));
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_creds_authentication_requested(py_talloc_Object *self)
+{
+ return PyBool_FromLong(cli_credentials_authentication_requested(PyCredentials_AsCliCredentials(self)));
+}
+
+static PyObject *py_creds_wrong_password(py_talloc_Object *self)
+{
+ return PyBool_FromLong(cli_credentials_wrong_password(PyCredentials_AsCliCredentials(self)));
+}
+
+static PyObject *py_creds_set_cmdline_callbacks(py_talloc_Object *self)
+{
+ return PyBool_FromLong(cli_credentials_set_cmdline_callbacks(PyCredentials_AsCliCredentials(self)));
+}
+
+static PyObject *py_creds_parse_string(py_talloc_Object *self, PyObject *args)
+{
+ char *newval;
+ enum credentials_obtained obt = CRED_SPECIFIED;
+ if (!PyArg_ParseTuple(args, "s|i", &newval, &obt))
+ return NULL;
+
+ cli_credentials_parse_string(PyCredentials_AsCliCredentials(self), newval, obt);
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_creds_get_nt_hash(py_talloc_Object *self)
+{
+ const struct samr_Password *ntpw = cli_credentials_get_nt_hash(PyCredentials_AsCliCredentials(self), self->ptr);
+
+ return PyString_FromStringAndSize(discard_const_p(char, ntpw->hash), 16);
+}
+
+static PyObject *py_creds_set_kerberos_state(py_talloc_Object *self, PyObject *args)
+{
+ int state;
+ if (!PyArg_ParseTuple(args, "i", &state))
+ return NULL;
+
+ cli_credentials_set_kerberos_state(PyCredentials_AsCliCredentials(self), state);
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_creds_guess(py_talloc_Object *self, PyObject *args)
+{
+ PyObject *py_lp_ctx = Py_None;
+ struct loadparm_context *lp_ctx;
+ if (!PyArg_ParseTuple(args, "|O", &py_lp_ctx))
+ return NULL;
+
+ lp_ctx = lp_from_py_object(py_lp_ctx);
+ if (lp_ctx == NULL)
+ return NULL;
+
+ cli_credentials_guess(PyCredentials_AsCliCredentials(self), lp_ctx);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_creds_set_machine_account(py_talloc_Object *self, PyObject *args)
+{
+ PyObject *py_lp_ctx = Py_None;
+ struct loadparm_context *lp_ctx;
+ NTSTATUS status;
+ if (!PyArg_ParseTuple(args, "|O", &py_lp_ctx))
+ return NULL;
+
+ lp_ctx = lp_from_py_object(py_lp_ctx);
+ if (lp_ctx == NULL)
+ return NULL;
+
+ status = cli_credentials_set_machine_account(PyCredentials_AsCliCredentials(self), lp_ctx);
+ PyErr_NTSTATUS_IS_ERR_RAISE(status);
+
+ Py_RETURN_NONE;
+}
+
+static PyMethodDef py_creds_methods[] = {
+ { "get_username", (PyCFunction)py_creds_get_username, METH_NOARGS,
+ "S.get_username() -> username\nObtain username." },
+ { "set_username", (PyCFunction)py_creds_set_username, METH_VARARGS,
+ "S.set_username(name, obtained=CRED_SPECIFIED) -> None\n"
+ "Change username." },
+ { "get_password", (PyCFunction)py_creds_get_password, METH_NOARGS,
+ "S.get_password() -> password\n"
+ "Obtain password." },
+ { "set_password", (PyCFunction)py_creds_set_password, METH_VARARGS,
+ "S.set_password(password, obtained=CRED_SPECIFIED) -> None\n"
+ "Change password." },
+ { "get_domain", (PyCFunction)py_creds_get_domain, METH_NOARGS,
+ "S.get_domain() -> domain\n"
+ "Obtain domain name." },
+ { "set_domain", (PyCFunction)py_creds_set_domain, METH_VARARGS,
+ "S.set_domain(domain, obtained=CRED_SPECIFIED) -> None\n"
+ "Change domain name." },
+ { "get_realm", (PyCFunction)py_creds_get_realm, METH_NOARGS,
+ "S.get_realm() -> realm\n"
+ "Obtain realm name." },
+ { "set_realm", (PyCFunction)py_creds_set_realm, METH_VARARGS,
+ "S.set_realm(realm, obtained=CRED_SPECIFIED) -> None\n"
+ "Change realm name." },
+ { "get_bind_dn", (PyCFunction)py_creds_get_bind_dn, METH_NOARGS,
+ "S.get_bind_dn() -> bind dn\n"
+ "Obtain bind DN." },
+ { "set_bind_dn", (PyCFunction)py_creds_set_bind_dn, METH_VARARGS,
+ "S.set_bind_dn(bind_dn) -> None\n"
+ "Change bind DN." },
+ { "is_anonymous", (PyCFunction)py_creds_is_anonymous, METH_NOARGS,
+ NULL },
+ { "set_anonymous", (PyCFunction)py_creds_set_anonymous, METH_NOARGS,
+ "S.set_anonymous() -> None\n"
+ "Use anonymous credentials." },
+ { "get_workstation", (PyCFunction)py_creds_get_workstation, METH_NOARGS,
+ NULL },
+ { "set_workstation", (PyCFunction)py_creds_set_workstation, METH_VARARGS,
+ NULL },
+ { "authentication_requested", (PyCFunction)py_creds_authentication_requested, METH_NOARGS,
+ NULL },
+ { "wrong_password", (PyCFunction)py_creds_wrong_password, METH_NOARGS,
+ "S.wrong_password() -> bool\n"
+ "Indicate the returned password was incorrect." },
+ { "set_cmdline_callbacks", (PyCFunction)py_creds_set_cmdline_callbacks, METH_NOARGS,
+ "S.set_cmdline_callbacks() -> bool\n"
+ "Use command-line to obtain credentials not explicitly set." },
+ { "parse_string", (PyCFunction)py_creds_parse_string, METH_VARARGS,
+ "S.parse_string(text, obtained=CRED_SPECIFIED) -> None\n"
+ "Parse credentials string." },
+ { "get_nt_hash", (PyCFunction)py_creds_get_nt_hash, METH_NOARGS,
+ NULL },
+ { "set_kerberos_state", (PyCFunction)py_creds_set_kerberos_state, METH_VARARGS,
+ NULL },
+ { "guess", (PyCFunction)py_creds_guess, METH_VARARGS, NULL },
+ { "set_machine_account", (PyCFunction)py_creds_set_machine_account, METH_VARARGS, NULL },
+ { NULL }
+};
+
+PyTypeObject PyCredentials = {
+ .tp_name = "Credentials",
+ .tp_basicsize = sizeof(py_talloc_Object),
+ .tp_dealloc = py_talloc_dealloc,
+ .tp_new = py_creds_new,
+ .tp_flags = Py_TPFLAGS_DEFAULT,
+ .tp_methods = py_creds_methods,
+};
+
+void initcredentials(void)
+{
+ PyObject *m;
+
+ if (PyType_Ready(&PyCredentials) < 0)
+ return;
+
+ m = Py_InitModule3("credentials", NULL, "Credentials management.");
+ if (m == NULL)
+ return;
+
+ PyModule_AddObject(m, "AUTO_USE_KERBEROS", PyInt_FromLong(CRED_AUTO_USE_KERBEROS));
+ PyModule_AddObject(m, "DONT_USE_KERBEROS", PyInt_FromLong(CRED_DONT_USE_KERBEROS));
+ PyModule_AddObject(m, "MUST_USE_KERBEROS", PyInt_FromLong(CRED_MUST_USE_KERBEROS));
+
+ Py_INCREF(&PyCredentials);
+ PyModule_AddObject(m, "Credentials", (PyObject *)&PyCredentials);
+}
diff --git a/source4/auth/credentials/pycredentials.h b/source4/auth/credentials/pycredentials.h
new file mode 100644
index 0000000000..f2e15fad61
--- /dev/null
+++ b/source4/auth/credentials/pycredentials.h
@@ -0,0 +1,30 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Jelmer Vernooij <jelmer@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+#ifndef _PYCREDENTIALS_H_
+#define _PYCREDENTIALS_H_
+
+#include "auth/credentials/credentials.h"
+#include "pytalloc.h"
+
+PyAPI_DATA(PyTypeObject) PyCredentials;
+struct cli_credentials *cli_credentials_from_py_object(PyObject *py_obj);
+#define PyCredentials_Check(py_obj) PyObject_TypeCheck(py_obj, &PyCredentials)
+#define PyCredentials_AsCliCredentials(py_obj) py_talloc_get_type(py_obj, struct cli_credentials)
+
+#endif /* _PYCREDENTIALS_H_ */
diff --git a/source4/auth/credentials/tests/bindings.py b/source4/auth/credentials/tests/bindings.py
new file mode 100644
index 0000000000..30120b3a60
--- /dev/null
+++ b/source4/auth/credentials/tests/bindings.py
@@ -0,0 +1,98 @@
+#!/usr/bin/python
+
+# Unix SMB/CIFS implementation.
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 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 <http://www.gnu.org/licenses/>.
+#
+
+"""Tests for the Credentials Python bindings.
+
+Note that this just tests the bindings work. It does not intend to test
+the functionality, that's already done in other tests.
+"""
+
+import unittest
+from samba import credentials
+
+class CredentialsTests(unittest.TestCase):
+ def setUp(self):
+ self.creds = credentials.Credentials()
+
+ def test_set_username(self):
+ self.creds.set_username("somebody")
+ self.assertEquals("somebody", self.creds.get_username())
+
+ def test_set_password(self):
+ self.creds.set_password("S3CreT")
+ self.assertEquals("S3CreT", self.creds.get_password())
+
+ def test_set_domain(self):
+ self.creds.set_domain("ABMAS")
+ self.assertEquals("ABMAS", self.creds.get_domain())
+
+ def test_set_realm(self):
+ self.creds.set_realm("myrealm")
+ self.assertEquals("MYREALM", self.creds.get_realm())
+
+ def test_parse_string_anon(self):
+ self.creds.parse_string("%")
+ self.assertEquals("", self.creds.get_username())
+ self.assertEquals(None, self.creds.get_password())
+
+ def test_parse_string_user_pw_domain(self):
+ self.creds.parse_string("dom\\someone%secr")
+ self.assertEquals("someone", self.creds.get_username())
+ self.assertEquals("secr", self.creds.get_password())
+ self.assertEquals("DOM", self.creds.get_domain())
+
+ def test_bind_dn(self):
+ self.assertEquals(None, self.creds.get_bind_dn())
+ self.creds.set_bind_dn("dc=foo,cn=bar")
+ self.assertEquals("dc=foo,cn=bar", self.creds.get_bind_dn())
+
+ def test_is_anon(self):
+ self.creds.set_username("")
+ self.assertTrue(self.creds.is_anonymous())
+ self.creds.set_username("somebody")
+ self.assertFalse(self.creds.is_anonymous())
+ self.creds.set_anonymous()
+ self.assertTrue(self.creds.is_anonymous())
+
+ def test_workstation(self):
+ # FIXME: This is uninitialised, it should be None
+ #self.assertEquals(None, self.creds.get_workstation())
+ self.creds.set_workstation("myworksta")
+ self.assertEquals("myworksta", self.creds.get_workstation())
+
+ def test_get_nt_hash(self):
+ self.creds.set_password("geheim")
+ self.assertEquals('\xc2\xae\x1f\xe6\xe6H\x84cRE>\x81o*\xeb\x93',
+ self.creds.get_nt_hash())
+
+ def test_guess(self):
+ # Just check the method is there and doesn't raise an exception
+ self.creds.guess()
+
+ def test_set_cmdline_callbacks(self):
+ self.creds.set_cmdline_callbacks()
+
+ def test_authentication_requested(self):
+ self.creds.set_username("")
+ self.assertFalse(self.creds.authentication_requested())
+ self.creds.set_username("somebody")
+ self.assertTrue(self.creds.authentication_requested())
+
+ def test_wrong_password(self):
+ self.assertFalse(self.creds.wrong_password())
diff --git a/source4/auth/credentials/tests/simple.c b/source4/auth/credentials/tests/simple.c
new file mode 100644
index 0000000000..c059878318
--- /dev/null
+++ b/source4/auth/credentials/tests/simple.c
@@ -0,0 +1,120 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Jelmer Vernooij <jelmer@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "auth/credentials/credentials.h"
+#include "torture/torture.h"
+
+static bool test_init(struct torture_context *tctx)
+{
+ struct cli_credentials *creds = cli_credentials_init(tctx);
+
+ cli_credentials_set_domain(creds, "bla", CRED_SPECIFIED);
+
+ torture_assert_str_equal(tctx, "BLA", cli_credentials_get_domain(creds),
+ "domain");
+
+ cli_credentials_set_username(creds, "someuser", CRED_SPECIFIED);
+
+ torture_assert_str_equal(tctx, "someuser",
+ cli_credentials_get_username(creds),
+ "username");
+
+ cli_credentials_set_password(creds, "p4ssw0rd", CRED_SPECIFIED);
+
+ torture_assert_str_equal(tctx, "p4ssw0rd",
+ cli_credentials_get_password(creds),
+ "password");
+
+ return true;
+}
+
+static bool test_init_anonymous(struct torture_context *tctx)
+{
+ struct cli_credentials *creds = cli_credentials_init_anon(tctx);
+
+ torture_assert_str_equal(tctx, cli_credentials_get_domain(creds),
+ "", "domain");
+
+ torture_assert_str_equal(tctx, cli_credentials_get_username(creds),
+ "", "username");
+
+ torture_assert(tctx, cli_credentials_get_password(creds) == NULL,
+ "password");
+
+ return true;
+}
+
+static bool test_parse_string(struct torture_context *tctx)
+{
+ struct cli_credentials *creds = cli_credentials_init_anon(tctx);
+
+ /* anonymous */
+ cli_credentials_parse_string(creds, "%", CRED_SPECIFIED);
+
+ torture_assert_str_equal(tctx, cli_credentials_get_domain(creds),
+ "", "domain");
+
+ torture_assert_str_equal(tctx, cli_credentials_get_username(creds),
+ "", "username");
+
+ torture_assert(tctx, cli_credentials_get_password(creds) == NULL,
+ "password");
+
+ /* username + password */
+ cli_credentials_parse_string(creds, "somebody%secret",
+ CRED_SPECIFIED);
+
+ torture_assert_str_equal(tctx, cli_credentials_get_domain(creds),
+ "", "domain");
+
+ torture_assert_str_equal(tctx, cli_credentials_get_username(creds),
+ "somebody", "username");
+
+ torture_assert_str_equal(tctx, cli_credentials_get_password(creds),
+ "secret", "password");
+
+ /* principal */
+ cli_credentials_parse_string(creds, "prin@styx",
+ CRED_SPECIFIED);
+
+ torture_assert_str_equal(tctx, cli_credentials_get_realm(creds),
+ "STYX", "realm");
+
+ torture_assert_str_equal(tctx,
+ cli_credentials_get_principal(creds, tctx),
+ "prin@styx", "principal");
+
+ return true;
+}
+
+struct torture_suite *torture_local_credentials(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx,
+ "CREDENTIALS");
+
+ torture_suite_add_simple_test(suite, "init", test_init);
+ torture_suite_add_simple_test(suite, "init anonymous",
+ test_init_anonymous);
+ torture_suite_add_simple_test(suite, "parse_string",
+ test_parse_string);
+
+ return suite;
+}
+
diff --git a/source4/auth/gensec/config.m4 b/source4/auth/gensec/config.m4
new file mode 100644
index 0000000000..b945afeea0
--- /dev/null
+++ b/source4/auth/gensec/config.m4
@@ -0,0 +1,2 @@
+SMB_ENABLE(gensec_krb5, $HAVE_KRB5)
+SMB_ENABLE(gensec_gssapi, $HAVE_KRB5)
diff --git a/source4/auth/gensec/config.mk b/source4/auth/gensec/config.mk
new file mode 100644
index 0000000000..27cf442b68
--- /dev/null
+++ b/source4/auth/gensec/config.mk
@@ -0,0 +1,87 @@
+#################################
+# Start SUBSYSTEM gensec
+[LIBRARY::gensec]
+PUBLIC_DEPENDENCIES = \
+ CREDENTIALS LIBSAMBA-UTIL LIBCRYPTO ASN1_UTIL samba_socket LIBPACKET
+# End SUBSYSTEM gensec
+#################################
+
+PC_FILES += $(gensecsrcdir)/gensec.pc
+
+gensec_VERSION = 0.0.1
+gensec_SOVERSION = 0
+gensec_OBJ_FILES = $(addprefix $(gensecsrcdir)/, gensec.o socket.o)
+
+PUBLIC_HEADERS += $(gensecsrcdir)/gensec.h
+
+$(eval $(call proto_header_template,$(gensecsrcdir)/gensec_proto.h,$(gensec_OBJ_FILES:.o=.c)))
+
+################################################
+# Start MODULE gensec_krb5
+[MODULE::gensec_krb5]
+SUBSYSTEM = gensec
+INIT_FUNCTION = gensec_krb5_init
+PRIVATE_DEPENDENCIES = CREDENTIALS KERBEROS auth_session
+# End MODULE gensec_krb5
+################################################
+
+gensec_krb5_OBJ_FILES = $(addprefix $(gensecsrcdir)/, gensec_krb5.o)
+
+################################################
+# Start MODULE gensec_gssapi
+[MODULE::gensec_gssapi]
+SUBSYSTEM = gensec
+INIT_FUNCTION = gensec_gssapi_init
+PRIVATE_DEPENDENCIES = HEIMDAL_GSSAPI CREDENTIALS KERBEROS
+# End MODULE gensec_gssapi
+################################################
+
+gensec_gssapi_OBJ_FILES = $(addprefix $(gensecsrcdir)/, gensec_gssapi.o)
+
+################################################
+# Start MODULE cyrus_sasl
+[MODULE::cyrus_sasl]
+SUBSYSTEM = gensec
+INIT_FUNCTION = gensec_sasl_init
+PRIVATE_DEPENDENCIES = CREDENTIALS SASL
+# End MODULE cyrus_sasl
+################################################
+
+cyrus_sasl_OBJ_FILES = $(addprefix $(gensecsrcdir)/, cyrus_sasl.o)
+
+################################################
+# Start MODULE gensec_spnego
+[MODULE::gensec_spnego]
+SUBSYSTEM = gensec
+INIT_FUNCTION = gensec_spnego_init
+PRIVATE_DEPENDENCIES = ASN1_UTIL CREDENTIALS
+# End MODULE gensec_spnego
+################################################
+
+gensec_spnego_OBJ_FILES = $(addprefix $(gensecsrcdir)/, spnego.o spnego_parse.o)
+
+$(eval $(call proto_header_template,$(gensecsrcdir)/spnego_proto.h,$(gensec_spnego_OBJ_FILES:.o=.c)))
+
+################################################
+# Start MODULE gensec_schannel
+[MODULE::gensec_schannel]
+SUBSYSTEM = gensec
+INIT_FUNCTION = gensec_schannel_init
+PRIVATE_DEPENDENCIES = SCHANNELDB NDR_SCHANNEL CREDENTIALS LIBNDR
+OUTPUT_TYPE = MERGED_OBJ
+# End MODULE gensec_schannel
+################################################
+
+gensec_schannel_OBJ_FILES = $(addprefix $(gensecsrcdir)/, schannel.o schannel_sign.o)
+$(eval $(call proto_header_template,$(gensecsrcdir)/schannel_proto.h,$(gensec_schannel_OBJ_FILES:.o=.c)))
+
+################################################
+# Start SUBSYSTEM SCHANNELDB
+[SUBSYSTEM::SCHANNELDB]
+PRIVATE_DEPENDENCIES = LDB_WRAP
+# End SUBSYSTEM SCHANNELDB
+################################################
+
+SCHANNELDB_OBJ_FILES = $(addprefix $(gensecsrcdir)/, schannel_state.o)
+$(eval $(call proto_header_template,$(gensecsrcdir)/schannel_state.h,$(SCHANNELDB_OBJ_FILES:.o=.c)))
+
diff --git a/source4/auth/gensec/cyrus_sasl.c b/source4/auth/gensec/cyrus_sasl.c
new file mode 100644
index 0000000000..54d53965cc
--- /dev/null
+++ b/source4/auth/gensec/cyrus_sasl.c
@@ -0,0 +1,432 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Connect GENSEC to an external SASL lib
+
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "auth/auth.h"
+#include "auth/credentials/credentials.h"
+#include "auth/gensec/gensec.h"
+#include "auth/gensec/gensec_proto.h"
+#include "lib/socket/socket.h"
+#include <sasl/sasl.h>
+
+struct gensec_sasl_state {
+ sasl_conn_t *conn;
+ int step;
+};
+
+static NTSTATUS sasl_nt_status(int sasl_ret)
+{
+ switch (sasl_ret) {
+ case SASL_CONTINUE:
+ return NT_STATUS_MORE_PROCESSING_REQUIRED;
+ case SASL_NOMEM:
+ return NT_STATUS_NO_MEMORY;
+ case SASL_BADPARAM:
+ case SASL_NOMECH:
+ return NT_STATUS_INVALID_PARAMETER;
+ case SASL_BADMAC:
+ return NT_STATUS_ACCESS_DENIED;
+ case SASL_OK:
+ return NT_STATUS_OK;
+ default:
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+}
+
+static int gensec_sasl_get_user(void *context, int id,
+ const char **result, unsigned *len)
+{
+ struct gensec_security *gensec_security = talloc_get_type(context, struct gensec_security);
+ const char *username = cli_credentials_get_username(gensec_get_credentials(gensec_security));
+ if (id != SASL_CB_USER && id != SASL_CB_AUTHNAME) {
+ return SASL_FAIL;
+ }
+
+ *result = username;
+ return SASL_OK;
+}
+
+static int gensec_sasl_get_realm(void *context, int id,
+ const char **availrealms,
+ const char **result)
+{
+ struct gensec_security *gensec_security = talloc_get_type(context, struct gensec_security);
+ const char *realm = cli_credentials_get_realm(gensec_get_credentials(gensec_security));
+ int i;
+ if (id != SASL_CB_GETREALM) {
+ return SASL_FAIL;
+ }
+
+ for (i=0; availrealms && availrealms[i]; i++) {
+ if (strcasecmp_m(realm, availrealms[i]) == 0) {
+ result[i] = availrealms[i];
+ return SASL_OK;
+ }
+ }
+ /* None of the realms match, so lets not specify one */
+ *result = "";
+ return SASL_OK;
+}
+
+static int gensec_sasl_get_password(sasl_conn_t *conn, void *context, int id,
+ sasl_secret_t **psecret)
+{
+ struct gensec_security *gensec_security = talloc_get_type(context, struct gensec_security);
+ const char *password = cli_credentials_get_password(gensec_get_credentials(gensec_security));
+
+ sasl_secret_t *secret;
+ if (!password) {
+ *psecret = NULL;
+ return SASL_OK;
+ }
+ secret = talloc_size(gensec_security, sizeof(sasl_secret_t)+strlen(password));
+ if (!secret) {
+ return SASL_NOMEM;
+ }
+ secret->len = strlen(password);
+ safe_strcpy((char*)secret->data, password, secret->len+1);
+ *psecret = secret;
+ return SASL_OK;
+}
+
+static int gensec_sasl_dispose(struct gensec_sasl_state *gensec_sasl_state)
+{
+ sasl_dispose(&gensec_sasl_state->conn);
+ return SASL_OK;
+}
+
+static NTSTATUS gensec_sasl_client_start(struct gensec_security *gensec_security)
+{
+ struct gensec_sasl_state *gensec_sasl_state;
+ const char *service = gensec_get_target_service(gensec_security);
+ const char *target_name = gensec_get_target_hostname(gensec_security);
+ struct socket_address *local_socket_addr = gensec_get_my_addr(gensec_security);
+ struct socket_address *remote_socket_addr = gensec_get_peer_addr(gensec_security);
+ char *local_addr = NULL;
+ char *remote_addr = NULL;
+ int sasl_ret;
+
+ sasl_callback_t *callbacks;
+
+ gensec_sasl_state = talloc(gensec_security, struct gensec_sasl_state);
+ if (!gensec_sasl_state) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ callbacks = talloc_array(gensec_sasl_state, sasl_callback_t, 5);
+ callbacks[0].id = SASL_CB_USER;
+ callbacks[0].proc = gensec_sasl_get_user;
+ callbacks[0].context = gensec_security;
+
+ callbacks[1].id = SASL_CB_AUTHNAME;
+ callbacks[1].proc = gensec_sasl_get_user;
+ callbacks[1].context = gensec_security;
+
+ callbacks[2].id = SASL_CB_GETREALM;
+ callbacks[2].proc = gensec_sasl_get_realm;
+ callbacks[2].context = gensec_security;
+
+ callbacks[3].id = SASL_CB_PASS;
+ callbacks[3].proc = gensec_sasl_get_password;
+ callbacks[3].context = gensec_security;
+
+ callbacks[4].id = SASL_CB_LIST_END;
+ callbacks[4].proc = NULL;
+ callbacks[4].context = NULL;
+
+ gensec_security->private_data = gensec_sasl_state;
+
+ if (local_socket_addr) {
+ local_addr = talloc_asprintf(gensec_sasl_state,
+ "%s;%d",
+ local_socket_addr->addr,
+ local_socket_addr->port);
+ }
+
+ if (remote_socket_addr) {
+ remote_addr = talloc_asprintf(gensec_sasl_state,
+ "%s;%d",
+ remote_socket_addr->addr,
+ remote_socket_addr->port);
+ }
+ gensec_sasl_state->step = 0;
+
+ sasl_ret = sasl_client_new(service,
+ target_name,
+ local_addr, remote_addr, callbacks, 0,
+ &gensec_sasl_state->conn);
+
+ if (sasl_ret == SASL_OK || sasl_ret == SASL_CONTINUE) {
+ sasl_security_properties_t props;
+ talloc_set_destructor(gensec_sasl_state, gensec_sasl_dispose);
+
+ ZERO_STRUCT(props);
+ if (gensec_security->want_features & GENSEC_FEATURE_SIGN) {
+ props.min_ssf = 1;
+ }
+ if (gensec_security->want_features & GENSEC_FEATURE_SEAL) {
+ props.min_ssf = 40;
+ }
+
+ props.max_ssf = UINT_MAX;
+ props.maxbufsize = 65536;
+ sasl_ret = sasl_setprop(gensec_sasl_state->conn, SASL_SEC_PROPS, &props);
+ if (sasl_ret != SASL_OK) {
+ return sasl_nt_status(sasl_ret);
+ }
+
+ } else {
+ DEBUG(1, ("GENSEC SASL: client_new failed: %s\n", sasl_errdetail(gensec_sasl_state->conn)));
+ }
+ return sasl_nt_status(sasl_ret);
+}
+
+static NTSTATUS gensec_sasl_update(struct gensec_security *gensec_security,
+ TALLOC_CTX *out_mem_ctx,
+ const DATA_BLOB in, DATA_BLOB *out)
+{
+ struct gensec_sasl_state *gensec_sasl_state = talloc_get_type(gensec_security->private_data,
+ struct gensec_sasl_state);
+ int sasl_ret;
+ const char *out_data;
+ unsigned int out_len;
+
+ if (gensec_sasl_state->step == 0) {
+ const char *mech;
+ sasl_ret = sasl_client_start(gensec_sasl_state->conn, gensec_security->ops->sasl_name,
+ NULL, &out_data, &out_len, &mech);
+ } else {
+ sasl_ret = sasl_client_step(gensec_sasl_state->conn,
+ (char*)in.data, in.length, NULL,
+ &out_data, &out_len);
+ }
+ if (sasl_ret == SASL_OK || sasl_ret == SASL_CONTINUE) {
+ *out = data_blob_talloc(out_mem_ctx, out_data, out_len);
+ } else {
+ DEBUG(1, ("GENSEC SASL: step %d update failed: %s\n", gensec_sasl_state->step,
+ sasl_errdetail(gensec_sasl_state->conn)));
+ }
+ gensec_sasl_state->step++;
+ return sasl_nt_status(sasl_ret);
+}
+
+static NTSTATUS gensec_sasl_unwrap_packets(struct gensec_security *gensec_security,
+ TALLOC_CTX *out_mem_ctx,
+ const DATA_BLOB *in,
+ DATA_BLOB *out,
+ size_t *len_processed)
+{
+ struct gensec_sasl_state *gensec_sasl_state = talloc_get_type(gensec_security->private_data,
+ struct gensec_sasl_state);
+ const char *out_data;
+ unsigned int out_len;
+
+ int sasl_ret = sasl_decode(gensec_sasl_state->conn,
+ (char*)in->data, in->length, &out_data,
+ &out_len);
+ if (sasl_ret == SASL_OK) {
+ *out = data_blob_talloc(out_mem_ctx, out_data, out_len);
+ *len_processed = in->length;
+ } else {
+ DEBUG(1, ("GENSEC SASL: unwrap failed: %s\n", sasl_errdetail(gensec_sasl_state->conn)));
+ }
+ return sasl_nt_status(sasl_ret);
+
+}
+
+static NTSTATUS gensec_sasl_wrap_packets(struct gensec_security *gensec_security,
+ TALLOC_CTX *out_mem_ctx,
+ const DATA_BLOB *in,
+ DATA_BLOB *out,
+ size_t *len_processed)
+{
+ struct gensec_sasl_state *gensec_sasl_state = talloc_get_type(gensec_security->private_data,
+ struct gensec_sasl_state);
+ const char *out_data;
+ unsigned int out_len;
+
+ int sasl_ret = sasl_encode(gensec_sasl_state->conn,
+ (char*)in->data, in->length, &out_data,
+ &out_len);
+ if (sasl_ret == SASL_OK) {
+ *out = data_blob_talloc(out_mem_ctx, out_data, out_len);
+ *len_processed = in->length;
+ } else {
+ DEBUG(1, ("GENSEC SASL: wrap failed: %s\n", sasl_errdetail(gensec_sasl_state->conn)));
+ }
+ return sasl_nt_status(sasl_ret);
+}
+
+/* Try to figure out what features we actually got on the connection */
+static bool gensec_sasl_have_feature(struct gensec_security *gensec_security,
+ uint32_t feature)
+{
+ struct gensec_sasl_state *gensec_sasl_state = talloc_get_type(gensec_security->private_data,
+ struct gensec_sasl_state);
+ sasl_ssf_t ssf;
+ int sasl_ret = sasl_getprop(gensec_sasl_state->conn, SASL_SSF,
+ (const void**)&ssf);
+ if (sasl_ret != SASL_OK) {
+ return false;
+ }
+ if (feature & GENSEC_FEATURE_SIGN) {
+ if (ssf == 0) {
+ return false;
+ }
+ if (ssf >= 1) {
+ return true;
+ }
+ }
+ if (feature & GENSEC_FEATURE_SEAL) {
+ if (ssf <= 1) {
+ return false;
+ }
+ if (ssf > 1) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/* This could in theory work with any SASL mech */
+static const struct gensec_security_ops gensec_sasl_security_ops = {
+ .name = "sasl-DIGEST-MD5",
+ .sasl_name = "DIGEST-MD5",
+ .client_start = gensec_sasl_client_start,
+ .update = gensec_sasl_update,
+ .wrap_packets = gensec_sasl_wrap_packets,
+ .unwrap_packets = gensec_sasl_unwrap_packets,
+ .have_feature = gensec_sasl_have_feature,
+ .enabled = true,
+ .priority = GENSEC_SASL
+};
+
+static int gensec_sasl_log(void *context,
+ int sasl_log_level,
+ const char *message)
+{
+ int dl;
+ switch (sasl_log_level) {
+ case SASL_LOG_NONE:
+ dl = 0;
+ break;
+ case SASL_LOG_ERR:
+ dl = 1;
+ break;
+ case SASL_LOG_FAIL:
+ dl = 2;
+ break;
+ case SASL_LOG_WARN:
+ dl = 3;
+ break;
+ case SASL_LOG_NOTE:
+ dl = 5;
+ break;
+ case SASL_LOG_DEBUG:
+ dl = 10;
+ break;
+ case SASL_LOG_TRACE:
+ dl = 11;
+ break;
+#if DEBUG_PASSWORD
+ case SASL_LOG_PASS:
+ dl = 100;
+ break;
+#endif
+ default:
+ dl = 0;
+ break;
+ }
+ DEBUG(dl, ("gensec_sasl: %s\n", message));
+
+ return SASL_OK;
+}
+
+NTSTATUS gensec_sasl_init(void)
+{
+ NTSTATUS ret;
+ int sasl_ret;
+#if 0
+ int i;
+ const char **sasl_mechs;
+#endif
+
+ static const sasl_callback_t callbacks[] = {
+ {
+ .id = SASL_CB_LOG,
+ .proc = gensec_sasl_log,
+ .context = NULL,
+ },
+ {
+ .id = SASL_CB_LIST_END,
+ .proc = gensec_sasl_log,
+ .context = NULL,
+ }
+ };
+ sasl_ret = sasl_client_init(callbacks);
+
+ if (sasl_ret == SASL_NOMECH) {
+ /* Nothing to do here */
+ return NT_STATUS_OK;
+ }
+
+ if (sasl_ret != SASL_OK) {
+ return sasl_nt_status(sasl_ret);
+ }
+
+ /* For now, we just register DIGEST-MD5 */
+#if 1
+ ret = gensec_register(&gensec_sasl_security_ops);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("Failed to register '%s' gensec backend!\n",
+ gensec_sasl_security_ops.name));
+ return ret;
+ }
+#else
+ sasl_mechs = sasl_global_listmech();
+ for (i = 0; sasl_mechs && sasl_mechs[i]; i++) {
+ const struct gensec_security_ops *oldmech;
+ struct gensec_security_ops *newmech;
+ oldmech = gensec_security_by_sasl_name(NULL, sasl_mechs[i]);
+ if (oldmech) {
+ continue;
+ }
+ newmech = talloc(talloc_autofree_context(), struct gensec_security_ops);
+ if (!newmech) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ *newmech = gensec_sasl_security_ops;
+ newmech->sasl_name = talloc_strdup(newmech, sasl_mechs[i]);
+ newmech->name = talloc_asprintf(newmech, "sasl-%s", sasl_mechs[i]);
+ if (!newmech->sasl_name || !newmech->name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = gensec_register(newmech);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("Failed to register '%s' gensec backend!\n",
+ gensec_sasl_security_ops.name));
+ return ret;
+ }
+ }
+#endif
+ return NT_STATUS_OK;
+}
diff --git a/source4/auth/gensec/gensec.c b/source4/auth/gensec/gensec.c
new file mode 100644
index 0000000000..2feb545f53
--- /dev/null
+++ b/source4/auth/gensec/gensec.c
@@ -0,0 +1,1331 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Generic Authentication Interface
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "auth/auth.h"
+#include "lib/events/events.h"
+#include "librpc/rpc/dcerpc.h"
+#include "auth/credentials/credentials.h"
+#include "auth/gensec/gensec.h"
+#include "auth/gensec/gensec_proto.h"
+#include "param/param.h"
+
+/* the list of currently registered GENSEC backends */
+static struct gensec_security_ops **generic_security_ops;
+static int gensec_num_backends;
+
+/* Return all the registered mechs. Don't modify the return pointer,
+ * but you may talloc_reference it if convient */
+_PUBLIC_ struct gensec_security_ops **gensec_security_all(void)
+{
+ return generic_security_ops;
+}
+
+bool gensec_security_ops_enabled(struct gensec_security_ops *ops,
+ struct loadparm_context *lp_ctx)
+{
+ return lp_parm_bool(lp_ctx, NULL, "gensec", ops->name, ops->enabled);
+}
+
+/* Sometimes we want to force only kerberos, sometimes we want to
+ * force it's avoidance. The old list could be either
+ * gensec_security_all(), or from cli_credentials_gensec_list() (ie,
+ * an existing list we have trimmed down) */
+
+_PUBLIC_ struct gensec_security_ops **gensec_use_kerberos_mechs(TALLOC_CTX *mem_ctx,
+ struct gensec_security_ops **old_gensec_list,
+ struct cli_credentials *creds)
+{
+ struct gensec_security_ops **new_gensec_list;
+ int i, j, num_mechs_in;
+ enum credentials_use_kerberos use_kerberos = CRED_AUTO_USE_KERBEROS;
+
+ if (creds) {
+ use_kerberos = cli_credentials_get_kerberos_state(creds);
+ }
+
+ if (use_kerberos == CRED_AUTO_USE_KERBEROS) {
+ if (!talloc_reference(mem_ctx, old_gensec_list)) {
+ return NULL;
+ }
+ return old_gensec_list;
+ }
+
+ for (num_mechs_in=0; old_gensec_list && old_gensec_list[num_mechs_in]; num_mechs_in++) {
+ /* noop */
+ }
+
+ new_gensec_list = talloc_array(mem_ctx, struct gensec_security_ops *, num_mechs_in + 1);
+ if (!new_gensec_list) {
+ return NULL;
+ }
+
+ j = 0;
+ for (i=0; old_gensec_list && old_gensec_list[i]; i++) {
+ int oid_idx;
+
+ for (oid_idx = 0; old_gensec_list[i]->oid && old_gensec_list[i]->oid[oid_idx]; oid_idx++) {
+ if (strcmp(old_gensec_list[i]->oid[oid_idx], GENSEC_OID_SPNEGO) == 0) {
+ new_gensec_list[j] = old_gensec_list[i];
+ j++;
+ break;
+ }
+ }
+ switch (use_kerberos) {
+ case CRED_DONT_USE_KERBEROS:
+ if (old_gensec_list[i]->kerberos == false) {
+ new_gensec_list[j] = old_gensec_list[i];
+ j++;
+ }
+ break;
+ case CRED_MUST_USE_KERBEROS:
+ if (old_gensec_list[i]->kerberos == true) {
+ new_gensec_list[j] = old_gensec_list[i];
+ j++;
+ }
+ break;
+ default:
+ /* Can't happen or invalid parameter */
+ return NULL;
+ }
+ }
+ new_gensec_list[j] = NULL;
+
+ return new_gensec_list;
+}
+
+struct gensec_security_ops **gensec_security_mechs(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx)
+{
+ struct gensec_security_ops **backends;
+ backends = gensec_security_all();
+ if (!gensec_security) {
+ if (!talloc_reference(mem_ctx, backends)) {
+ return NULL;
+ }
+ return backends;
+ } else {
+ struct cli_credentials *creds = gensec_get_credentials(gensec_security);
+ if (!creds) {
+ if (!talloc_reference(mem_ctx, backends)) {
+ return NULL;
+ }
+ return backends;
+ }
+ return gensec_use_kerberos_mechs(mem_ctx, backends, creds);
+ }
+}
+
+static const struct gensec_security_ops *gensec_security_by_authtype(struct gensec_security *gensec_security,
+ uint8_t auth_type)
+{
+ int i;
+ struct gensec_security_ops **backends;
+ const struct gensec_security_ops *backend;
+ TALLOC_CTX *mem_ctx = talloc_new(gensec_security);
+ if (!mem_ctx) {
+ return NULL;
+ }
+ backends = gensec_security_mechs(gensec_security, mem_ctx);
+ for (i=0; backends && backends[i]; i++) {
+ if (!gensec_security_ops_enabled(backends[i],
+ gensec_security->settings->lp_ctx))
+ continue;
+ if (backends[i]->auth_type == auth_type) {
+ backend = backends[i];
+ talloc_free(mem_ctx);
+ return backend;
+ }
+ }
+ talloc_free(mem_ctx);
+
+ return NULL;
+}
+
+const struct gensec_security_ops *gensec_security_by_oid(struct gensec_security *gensec_security,
+ const char *oid_string)
+{
+ int i, j;
+ struct gensec_security_ops **backends;
+ const struct gensec_security_ops *backend;
+ TALLOC_CTX *mem_ctx = talloc_new(gensec_security);
+ if (!mem_ctx) {
+ return NULL;
+ }
+ backends = gensec_security_mechs(gensec_security, mem_ctx);
+ for (i=0; backends && backends[i]; i++) {
+ if (gensec_security != NULL &&
+ !gensec_security_ops_enabled(backends[i],
+ gensec_security->settings->lp_ctx))
+ continue;
+ if (backends[i]->oid) {
+ for (j=0; backends[i]->oid[j]; j++) {
+ if (backends[i]->oid[j] &&
+ (strcmp(backends[i]->oid[j], oid_string) == 0)) {
+ backend = backends[i];
+ talloc_free(mem_ctx);
+ return backend;
+ }
+ }
+ }
+ }
+ talloc_free(mem_ctx);
+
+ return NULL;
+}
+
+const struct gensec_security_ops *gensec_security_by_sasl_name(struct gensec_security *gensec_security,
+ const char *sasl_name)
+{
+ int i;
+ struct gensec_security_ops **backends;
+ const struct gensec_security_ops *backend;
+ TALLOC_CTX *mem_ctx = talloc_new(gensec_security);
+ if (!mem_ctx) {
+ return NULL;
+ }
+ backends = gensec_security_mechs(gensec_security, mem_ctx);
+ for (i=0; backends && backends[i]; i++) {
+ if (!gensec_security_ops_enabled(backends[i], gensec_security->settings->lp_ctx))
+ continue;
+ if (backends[i]->sasl_name
+ && (strcmp(backends[i]->sasl_name, sasl_name) == 0)) {
+ backend = backends[i];
+ talloc_free(mem_ctx);
+ return backend;
+ }
+ }
+ talloc_free(mem_ctx);
+
+ return NULL;
+}
+
+static const struct gensec_security_ops *gensec_security_by_name(struct gensec_security *gensec_security,
+ const char *name)
+{
+ int i;
+ struct gensec_security_ops **backends;
+ const struct gensec_security_ops *backend;
+ TALLOC_CTX *mem_ctx = talloc_new(gensec_security);
+ if (!mem_ctx) {
+ return NULL;
+ }
+ backends = gensec_security_mechs(gensec_security, mem_ctx);
+ for (i=0; backends && backends[i]; i++) {
+ if (gensec_security != NULL &&
+ !gensec_security_ops_enabled(backends[i], gensec_security->settings->lp_ctx))
+ continue;
+ if (backends[i]->name
+ && (strcmp(backends[i]->name, name) == 0)) {
+ backend = backends[i];
+ talloc_free(mem_ctx);
+ return backend;
+ }
+ }
+ talloc_free(mem_ctx);
+ return NULL;
+}
+
+/**
+ * Return a unique list of security subsystems from those specified in
+ * the list of SASL names.
+ *
+ * Use the list of enabled GENSEC mechanisms from the credentials
+ * attached to the gensec_security, and return in our preferred order.
+ */
+
+const struct gensec_security_ops **gensec_security_by_sasl_list(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ const char **sasl_names)
+{
+ const struct gensec_security_ops **backends_out;
+ struct gensec_security_ops **backends;
+ int i, k, sasl_idx;
+ int num_backends_out = 0;
+
+ if (!sasl_names) {
+ return NULL;
+ }
+
+ backends = gensec_security_mechs(gensec_security, mem_ctx);
+
+ backends_out = talloc_array(mem_ctx, const struct gensec_security_ops *, 1);
+ if (!backends_out) {
+ return NULL;
+ }
+ backends_out[0] = NULL;
+
+ /* Find backends in our preferred order, by walking our list,
+ * then looking in the supplied list */
+ for (i=0; backends && backends[i]; i++) {
+ if (gensec_security != NULL &&
+ !gensec_security_ops_enabled(backends[i], gensec_security->settings->lp_ctx))
+ continue;
+ for (sasl_idx = 0; sasl_names[sasl_idx]; sasl_idx++) {
+ if (!backends[i]->sasl_name ||
+ !(strcmp(backends[i]->sasl_name,
+ sasl_names[sasl_idx]) == 0)) {
+ continue;
+ }
+
+ for (k=0; backends_out[k]; k++) {
+ if (backends_out[k] == backends[i]) {
+ break;
+ }
+ }
+
+ if (k < num_backends_out) {
+ /* already in there */
+ continue;
+ }
+
+ backends_out = talloc_realloc(mem_ctx, backends_out,
+ const struct gensec_security_ops *,
+ num_backends_out + 2);
+ if (!backends_out) {
+ return NULL;
+ }
+
+ backends_out[num_backends_out] = backends[i];
+ num_backends_out++;
+ backends_out[num_backends_out] = NULL;
+ }
+ }
+ return backends_out;
+}
+
+/**
+ * Return a unique list of security subsystems from those specified in
+ * the OID list. That is, where two OIDs refer to the same module,
+ * return that module only once.
+ *
+ * Use the list of enabled GENSEC mechanisms from the credentials
+ * attached to the gensec_security, and return in our preferred order.
+ */
+
+const struct gensec_security_ops_wrapper *gensec_security_by_oid_list(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ const char **oid_strings,
+ const char *skip)
+{
+ struct gensec_security_ops_wrapper *backends_out;
+ struct gensec_security_ops **backends;
+ int i, j, k, oid_idx;
+ int num_backends_out = 0;
+
+ if (!oid_strings) {
+ return NULL;
+ }
+
+ backends = gensec_security_mechs(gensec_security, gensec_security);
+
+ backends_out = talloc_array(mem_ctx, struct gensec_security_ops_wrapper, 1);
+ if (!backends_out) {
+ return NULL;
+ }
+ backends_out[0].op = NULL;
+ backends_out[0].oid = NULL;
+
+ /* Find backends in our preferred order, by walking our list,
+ * then looking in the supplied list */
+ for (i=0; backends && backends[i]; i++) {
+ if (gensec_security != NULL &&
+ !gensec_security_ops_enabled(backends[i], gensec_security->settings->lp_ctx))
+ continue;
+ if (!backends[i]->oid) {
+ continue;
+ }
+ for (oid_idx = 0; oid_strings[oid_idx]; oid_idx++) {
+ if (strcmp(oid_strings[oid_idx], skip) == 0) {
+ continue;
+ }
+
+ for (j=0; backends[i]->oid[j]; j++) {
+ if (!backends[i]->oid[j] ||
+ !(strcmp(backends[i]->oid[j],
+ oid_strings[oid_idx]) == 0)) {
+ continue;
+ }
+
+ for (k=0; backends_out[k].op; k++) {
+ if (backends_out[k].op == backends[i]) {
+ break;
+ }
+ }
+
+ if (k < num_backends_out) {
+ /* already in there */
+ continue;
+ }
+
+ backends_out = talloc_realloc(mem_ctx, backends_out,
+ struct gensec_security_ops_wrapper,
+ num_backends_out + 2);
+ if (!backends_out) {
+ return NULL;
+ }
+
+ backends_out[num_backends_out].op = backends[i];
+ backends_out[num_backends_out].oid = backends[i]->oid[j];
+ num_backends_out++;
+ backends_out[num_backends_out].op = NULL;
+ backends_out[num_backends_out].oid = NULL;
+ }
+ }
+ }
+ return backends_out;
+}
+
+/**
+ * Return OIDS from the security subsystems listed
+ */
+
+const char **gensec_security_oids_from_ops(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ struct gensec_security_ops **ops,
+ const char *skip)
+{
+ int i;
+ int j = 0;
+ int k;
+ const char **oid_list;
+ if (!ops) {
+ return NULL;
+ }
+ oid_list = talloc_array(mem_ctx, const char *, 1);
+ if (!oid_list) {
+ return NULL;
+ }
+
+ for (i=0; ops && ops[i]; i++) {
+ if (gensec_security != NULL &&
+ !gensec_security_ops_enabled(ops[i], gensec_security->settings->lp_ctx)) {
+ continue;
+ }
+ if (!ops[i]->oid) {
+ continue;
+ }
+
+ for (k = 0; ops[i]->oid[k]; k++) {
+ if (skip && strcmp(skip, ops[i]->oid[k])==0) {
+ } else {
+ oid_list = talloc_realloc(mem_ctx, oid_list, const char *, j + 2);
+ if (!oid_list) {
+ return NULL;
+ }
+ oid_list[j] = ops[i]->oid[k];
+ j++;
+ }
+ }
+ }
+ oid_list[j] = NULL;
+ return oid_list;
+}
+
+
+/**
+ * Return OIDS from the security subsystems listed
+ */
+
+const char **gensec_security_oids_from_ops_wrapped(TALLOC_CTX *mem_ctx,
+ const struct gensec_security_ops_wrapper *wops)
+{
+ int i;
+ int j = 0;
+ int k;
+ const char **oid_list;
+ if (!wops) {
+ return NULL;
+ }
+ oid_list = talloc_array(mem_ctx, const char *, 1);
+ if (!oid_list) {
+ return NULL;
+ }
+
+ for (i=0; wops[i].op; i++) {
+ if (!wops[i].op->oid) {
+ continue;
+ }
+
+ for (k = 0; wops[i].op->oid[k]; k++) {
+ oid_list = talloc_realloc(mem_ctx, oid_list, const char *, j + 2);
+ if (!oid_list) {
+ return NULL;
+ }
+ oid_list[j] = wops[i].op->oid[k];
+ j++;
+ }
+ }
+ oid_list[j] = NULL;
+ return oid_list;
+}
+
+
+/**
+ * Return all the security subsystems currently enabled on a GENSEC context.
+ *
+ * This is taken from a list attached to the cli_credentials, and
+ * skips the OID in 'skip'. (Typically the SPNEGO OID)
+ *
+ */
+
+const char **gensec_security_oids(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ const char *skip)
+{
+ struct gensec_security_ops **ops
+ = gensec_security_mechs(gensec_security, mem_ctx);
+ return gensec_security_oids_from_ops(gensec_security, mem_ctx, ops, skip);
+}
+
+
+
+/**
+ Start the GENSEC system, returning a context pointer.
+ @param mem_ctx The parent TALLOC memory context.
+ @param gensec_security Returned GENSEC context pointer.
+ @note The mem_ctx is only a parent and may be NULL.
+*/
+static NTSTATUS gensec_start(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct gensec_settings *settings,
+ struct auth_context *auth_context,
+ struct gensec_security **gensec_security)
+{
+ if (ev == NULL) {
+ DEBUG(0, ("No event context available!\n"));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ (*gensec_security) = talloc(mem_ctx, struct gensec_security);
+ NT_STATUS_HAVE_NO_MEMORY(*gensec_security);
+
+ (*gensec_security)->ops = NULL;
+ (*gensec_security)->private_data = NULL;
+
+ ZERO_STRUCT((*gensec_security)->target);
+ ZERO_STRUCT((*gensec_security)->peer_addr);
+ ZERO_STRUCT((*gensec_security)->my_addr);
+
+ (*gensec_security)->subcontext = false;
+ (*gensec_security)->want_features = 0;
+
+ (*gensec_security)->event_ctx = ev;
+ SMB_ASSERT(settings->lp_ctx != NULL);
+ (*gensec_security)->settings = talloc_reference(*gensec_security, settings);
+ (*gensec_security)->auth_context = talloc_reference(*gensec_security, auth_context);
+
+ return NT_STATUS_OK;
+}
+
+/**
+ * Start a GENSEC subcontext, with a copy of the properties of the parent
+ * @param mem_ctx The parent TALLOC memory context.
+ * @param parent The parent GENSEC context
+ * @param gensec_security Returned GENSEC context pointer.
+ * @note Used by SPNEGO in particular, for the actual implementation mechanism
+ */
+
+_PUBLIC_ NTSTATUS gensec_subcontext_start(TALLOC_CTX *mem_ctx,
+ struct gensec_security *parent,
+ struct gensec_security **gensec_security)
+{
+ (*gensec_security) = talloc(mem_ctx, struct gensec_security);
+ NT_STATUS_HAVE_NO_MEMORY(*gensec_security);
+
+ (**gensec_security) = *parent;
+ (*gensec_security)->ops = NULL;
+ (*gensec_security)->private_data = NULL;
+
+ (*gensec_security)->subcontext = true;
+ (*gensec_security)->want_features = parent->want_features;
+ (*gensec_security)->event_ctx = parent->event_ctx;
+ (*gensec_security)->auth_context = talloc_reference(*gensec_security, parent->auth_context);
+ (*gensec_security)->settings = talloc_reference(*gensec_security, parent->settings);
+ (*gensec_security)->auth_context = talloc_reference(*gensec_security, parent->auth_context);
+
+ return NT_STATUS_OK;
+}
+
+/**
+ Start the GENSEC system, in client mode, returning a context pointer.
+ @param mem_ctx The parent TALLOC memory context.
+ @param gensec_security Returned GENSEC context pointer.
+ @note The mem_ctx is only a parent and may be NULL.
+*/
+_PUBLIC_ NTSTATUS gensec_client_start(TALLOC_CTX *mem_ctx,
+ struct gensec_security **gensec_security,
+ struct tevent_context *ev,
+ struct gensec_settings *settings)
+{
+ NTSTATUS status;
+
+ if (settings == NULL) {
+ DEBUG(0,("gensec_client_start: no settings given!\n"));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ status = gensec_start(mem_ctx, ev, settings, NULL, gensec_security);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ (*gensec_security)->gensec_role = GENSEC_CLIENT;
+
+ return status;
+}
+
+/**
+ Start the GENSEC system, in server mode, returning a context pointer.
+ @param mem_ctx The parent TALLOC memory context.
+ @param gensec_security Returned GENSEC context pointer.
+ @note The mem_ctx is only a parent and may be NULL.
+*/
+_PUBLIC_ NTSTATUS gensec_server_start(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct gensec_settings *settings,
+ struct auth_context *auth_context,
+ struct gensec_security **gensec_security)
+{
+ NTSTATUS status;
+
+ if (!ev) {
+ DEBUG(0,("gensec_server_start: no event context given!\n"));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ if (!settings) {
+ DEBUG(0,("gensec_server_start: no settings given!\n"));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ status = gensec_start(mem_ctx, ev, settings, auth_context, gensec_security);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ (*gensec_security)->gensec_role = GENSEC_SERVER;
+
+ return status;
+}
+
+static NTSTATUS gensec_start_mech(struct gensec_security *gensec_security)
+{
+ NTSTATUS status;
+ DEBUG(5, ("Starting GENSEC %smechanism %s\n",
+ gensec_security->subcontext ? "sub" : "",
+ gensec_security->ops->name));
+ switch (gensec_security->gensec_role) {
+ case GENSEC_CLIENT:
+ if (gensec_security->ops->client_start) {
+ status = gensec_security->ops->client_start(gensec_security);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(2, ("Failed to start GENSEC client mech %s: %s\n",
+ gensec_security->ops->name, nt_errstr(status)));
+ }
+ return status;
+ }
+ break;
+ case GENSEC_SERVER:
+ if (gensec_security->ops->server_start) {
+ status = gensec_security->ops->server_start(gensec_security);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to start GENSEC server mech %s: %s\n",
+ gensec_security->ops->name, nt_errstr(status)));
+ }
+ return status;
+ }
+ break;
+ }
+ return NT_STATUS_INVALID_PARAMETER;
+}
+
+/**
+ * Start a GENSEC sub-mechanism by DCERPC allocated 'auth type' number
+ * @param gensec_security GENSEC context pointer.
+ * @param auth_type DCERPC auth type
+ * @param auth_level DCERPC auth level
+ */
+
+_PUBLIC_ NTSTATUS gensec_start_mech_by_authtype(struct gensec_security *gensec_security,
+ uint8_t auth_type, uint8_t auth_level)
+{
+ gensec_security->ops = gensec_security_by_authtype(gensec_security, auth_type);
+ if (!gensec_security->ops) {
+ DEBUG(3, ("Could not find GENSEC backend for auth_type=%d\n", (int)auth_type));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ gensec_want_feature(gensec_security, GENSEC_FEATURE_DCE_STYLE);
+ gensec_want_feature(gensec_security, GENSEC_FEATURE_ASYNC_REPLIES);
+ if (auth_level == DCERPC_AUTH_LEVEL_INTEGRITY) {
+ gensec_want_feature(gensec_security, GENSEC_FEATURE_SIGN);
+ } else if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) {
+ gensec_want_feature(gensec_security, GENSEC_FEATURE_SIGN);
+ gensec_want_feature(gensec_security, GENSEC_FEATURE_SEAL);
+ } else if (auth_level == DCERPC_AUTH_LEVEL_CONNECT) {
+ /* Default features */
+ } else {
+ DEBUG(2,("auth_level %d not supported in DCE/RPC authentication\n",
+ auth_level));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ return gensec_start_mech(gensec_security);
+}
+
+_PUBLIC_ const char *gensec_get_name_by_authtype(struct gensec_security *gensec_security, uint8_t authtype)
+{
+ const struct gensec_security_ops *ops;
+ ops = gensec_security_by_authtype(gensec_security, authtype);
+ if (ops) {
+ return ops->name;
+ }
+ return NULL;
+}
+
+
+_PUBLIC_ const char *gensec_get_name_by_oid(struct gensec_security *gensec_security,
+ const char *oid_string)
+{
+ const struct gensec_security_ops *ops;
+ ops = gensec_security_by_oid(gensec_security, oid_string);
+ if (ops) {
+ return ops->name;
+ }
+ return oid_string;
+}
+
+
+/**
+ * Start a GENSEC sub-mechanism with a specifed mechansim structure, used in SPNEGO
+ *
+ */
+
+NTSTATUS gensec_start_mech_by_ops(struct gensec_security *gensec_security,
+ const struct gensec_security_ops *ops)
+{
+ gensec_security->ops = ops;
+ return gensec_start_mech(gensec_security);
+}
+
+/**
+ * Start a GENSEC sub-mechanism by OID, used in SPNEGO
+ *
+ * @note This should also be used when you wish to just start NLTMSSP (for example), as it uses a
+ * well-known #define to hook it in.
+ */
+
+_PUBLIC_ NTSTATUS gensec_start_mech_by_oid(struct gensec_security *gensec_security,
+ const char *mech_oid)
+{
+ SMB_ASSERT(gensec_security != NULL);
+
+ gensec_security->ops = gensec_security_by_oid(gensec_security, mech_oid);
+ if (!gensec_security->ops) {
+ DEBUG(3, ("Could not find GENSEC backend for oid=%s\n", mech_oid));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ return gensec_start_mech(gensec_security);
+}
+
+/**
+ * Start a GENSEC sub-mechanism by a well know SASL name
+ *
+ */
+
+_PUBLIC_ NTSTATUS gensec_start_mech_by_sasl_name(struct gensec_security *gensec_security,
+ const char *sasl_name)
+{
+ gensec_security->ops = gensec_security_by_sasl_name(gensec_security, sasl_name);
+ if (!gensec_security->ops) {
+ DEBUG(3, ("Could not find GENSEC backend for sasl_name=%s\n", sasl_name));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ return gensec_start_mech(gensec_security);
+}
+
+/**
+ * Start a GENSEC sub-mechanism with the preferred option from a SASL name list
+ *
+ */
+
+_PUBLIC_ NTSTATUS gensec_start_mech_by_sasl_list(struct gensec_security *gensec_security,
+ const char **sasl_names)
+{
+ NTSTATUS nt_status = NT_STATUS_INVALID_PARAMETER;
+ TALLOC_CTX *mem_ctx = talloc_new(gensec_security);
+ const struct gensec_security_ops **ops;
+ int i;
+ if (!mem_ctx) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ ops = gensec_security_by_sasl_list(gensec_security, mem_ctx, sasl_names);
+ if (!ops || !*ops) {
+ DEBUG(3, ("Could not find GENSEC backend for any of sasl_name = %s\n",
+ str_list_join(mem_ctx,
+ sasl_names, ' ')));
+ talloc_free(mem_ctx);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ for (i=0; ops[i]; i++) {
+ nt_status = gensec_start_mech_by_ops(gensec_security, ops[i]);
+ if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_INVALID_PARAMETER)) {
+ break;
+ }
+ }
+ talloc_free(mem_ctx);
+ return nt_status;
+}
+
+/**
+ * Start a GENSEC sub-mechanism by an internal name
+ *
+ */
+
+_PUBLIC_ NTSTATUS gensec_start_mech_by_name(struct gensec_security *gensec_security,
+ const char *name)
+{
+ gensec_security->ops = gensec_security_by_name(gensec_security, name);
+ if (!gensec_security->ops) {
+ DEBUG(3, ("Could not find GENSEC backend for name=%s\n", name));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ return gensec_start_mech(gensec_security);
+}
+
+/*
+ wrappers for the gensec function pointers
+*/
+_PUBLIC_ NTSTATUS gensec_unseal_packet(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ uint8_t *data, size_t length,
+ const uint8_t *whole_pdu, size_t pdu_length,
+ const DATA_BLOB *sig)
+{
+ if (!gensec_security->ops->unseal_packet) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ return gensec_security->ops->unseal_packet(gensec_security, mem_ctx,
+ data, length,
+ whole_pdu, pdu_length,
+ sig);
+}
+
+_PUBLIC_ NTSTATUS gensec_check_packet(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ const uint8_t *data, size_t length,
+ const uint8_t *whole_pdu, size_t pdu_length,
+ const DATA_BLOB *sig)
+{
+ if (!gensec_security->ops->check_packet) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ return gensec_security->ops->check_packet(gensec_security, mem_ctx, data, length, whole_pdu, pdu_length, sig);
+}
+
+_PUBLIC_ NTSTATUS gensec_seal_packet(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ uint8_t *data, size_t length,
+ const uint8_t *whole_pdu, size_t pdu_length,
+ DATA_BLOB *sig)
+{
+ if (!gensec_security->ops->seal_packet) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ return gensec_security->ops->seal_packet(gensec_security, mem_ctx, data, length, whole_pdu, pdu_length, sig);
+}
+
+_PUBLIC_ NTSTATUS gensec_sign_packet(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ const uint8_t *data, size_t length,
+ const uint8_t *whole_pdu, size_t pdu_length,
+ DATA_BLOB *sig)
+{
+ if (!gensec_security->ops->sign_packet) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ return gensec_security->ops->sign_packet(gensec_security, mem_ctx, data, length, whole_pdu, pdu_length, sig);
+}
+
+_PUBLIC_ size_t gensec_sig_size(struct gensec_security *gensec_security, size_t data_size)
+{
+ if (!gensec_security->ops->sig_size) {
+ return 0;
+ }
+ if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) {
+ return 0;
+ }
+
+ return gensec_security->ops->sig_size(gensec_security, data_size);
+}
+
+size_t gensec_max_wrapped_size(struct gensec_security *gensec_security)
+{
+ if (!gensec_security->ops->max_wrapped_size) {
+ return (1 << 17);
+ }
+
+ return gensec_security->ops->max_wrapped_size(gensec_security);
+}
+
+size_t gensec_max_input_size(struct gensec_security *gensec_security)
+{
+ if (!gensec_security->ops->max_input_size) {
+ return (1 << 17) - gensec_sig_size(gensec_security, 1 << 17);
+ }
+
+ return gensec_security->ops->max_input_size(gensec_security);
+}
+
+_PUBLIC_ NTSTATUS gensec_wrap(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *in,
+ DATA_BLOB *out)
+{
+ if (!gensec_security->ops->wrap) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return gensec_security->ops->wrap(gensec_security, mem_ctx, in, out);
+}
+
+_PUBLIC_ NTSTATUS gensec_unwrap(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *in,
+ DATA_BLOB *out)
+{
+ if (!gensec_security->ops->unwrap) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return gensec_security->ops->unwrap(gensec_security, mem_ctx, in, out);
+}
+
+_PUBLIC_ NTSTATUS gensec_session_key(struct gensec_security *gensec_security,
+ DATA_BLOB *session_key)
+{
+ if (!gensec_security->ops->session_key) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SESSION_KEY)) {
+ return NT_STATUS_NO_USER_SESSION_KEY;
+ }
+
+ return gensec_security->ops->session_key(gensec_security, session_key);
+}
+
+/**
+ * Return the credentials of a logged on user, including session keys
+ * etc.
+ *
+ * Only valid after a successful authentication
+ *
+ * May only be called once per authentication.
+ *
+ */
+
+_PUBLIC_ NTSTATUS gensec_session_info(struct gensec_security *gensec_security,
+ struct auth_session_info **session_info)
+{
+ if (!gensec_security->ops->session_info) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return gensec_security->ops->session_info(gensec_security, session_info);
+}
+
+/**
+ * Next state function for the GENSEC state machine
+ *
+ * @param gensec_security GENSEC State
+ * @param out_mem_ctx The TALLOC_CTX for *out to be allocated on
+ * @param in The request, as a DATA_BLOB
+ * @param out The reply, as an talloc()ed DATA_BLOB, on *out_mem_ctx
+ * @return Error, MORE_PROCESSING_REQUIRED if a reply is sent,
+ * or NT_STATUS_OK if the user is authenticated.
+ */
+
+_PUBLIC_ NTSTATUS gensec_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx,
+ const DATA_BLOB in, DATA_BLOB *out)
+{
+ return gensec_security->ops->update(gensec_security, out_mem_ctx, in, out);
+}
+
+static void gensec_update_async_timed_handler(struct tevent_context *ev, struct tevent_timer *te,
+ struct timeval t, void *ptr)
+{
+ struct gensec_update_request *req = talloc_get_type(ptr, struct gensec_update_request);
+ req->status = req->gensec_security->ops->update(req->gensec_security, req, req->in, &req->out);
+ req->callback.fn(req, req->callback.private_data);
+}
+
+/**
+ * Next state function for the GENSEC state machine async version
+ *
+ * @param gensec_security GENSEC State
+ * @param in The request, as a DATA_BLOB
+ * @param callback The function that will be called when the operation is
+ * finished, it should return gensec_update_recv() to get output
+ * @param private_data A private pointer that will be passed to the callback function
+ */
+
+_PUBLIC_ void gensec_update_send(struct gensec_security *gensec_security, const DATA_BLOB in,
+ void (*callback)(struct gensec_update_request *req, void *private_data),
+ void *private_data)
+{
+ struct gensec_update_request *req = NULL;
+ struct tevent_timer *te = NULL;
+
+ req = talloc(gensec_security, struct gensec_update_request);
+ if (!req) goto failed;
+ req->gensec_security = gensec_security;
+ req->in = in;
+ req->out = data_blob(NULL, 0);
+ req->callback.fn = callback;
+ req->callback.private_data = private_data;
+
+ te = event_add_timed(gensec_security->event_ctx, req,
+ timeval_zero(),
+ gensec_update_async_timed_handler, req);
+ if (!te) goto failed;
+
+ return;
+
+failed:
+ talloc_free(req);
+ callback(NULL, private_data);
+}
+
+/**
+ * Next state function for the GENSEC state machine
+ *
+ * @param req GENSEC update request state
+ * @param out_mem_ctx The TALLOC_CTX for *out to be allocated on
+ * @param out The reply, as an talloc()ed DATA_BLOB, on *out_mem_ctx
+ * @return Error, MORE_PROCESSING_REQUIRED if a reply is sent,
+ * or NT_STATUS_OK if the user is authenticated.
+ */
+_PUBLIC_ NTSTATUS gensec_update_recv(struct gensec_update_request *req, TALLOC_CTX *out_mem_ctx, DATA_BLOB *out)
+{
+ NTSTATUS status;
+
+ NT_STATUS_HAVE_NO_MEMORY(req);
+
+ *out = req->out;
+ talloc_steal(out_mem_ctx, out->data);
+ status = req->status;
+
+ talloc_free(req);
+ return status;
+}
+
+/**
+ * Set the requirement for a certain feature on the connection
+ *
+ */
+
+_PUBLIC_ void gensec_want_feature(struct gensec_security *gensec_security,
+ uint32_t feature)
+{
+ if (!gensec_security->ops || !gensec_security->ops->want_feature) {
+ gensec_security->want_features |= feature;
+ return;
+ }
+ gensec_security->ops->want_feature(gensec_security, feature);
+}
+
+/**
+ * Check the requirement for a certain feature on the connection
+ *
+ */
+
+_PUBLIC_ bool gensec_have_feature(struct gensec_security *gensec_security,
+ uint32_t feature)
+{
+ if (!gensec_security->ops->have_feature) {
+ return false;
+ }
+
+ /* We might 'have' features that we don't 'want', because the
+ * other end demanded them, or we can't neotiate them off */
+ return gensec_security->ops->have_feature(gensec_security, feature);
+}
+
+/**
+ * Associate a credentials structure with a GENSEC context - talloc_reference()s it to the context
+ *
+ */
+
+_PUBLIC_ NTSTATUS gensec_set_credentials(struct gensec_security *gensec_security, struct cli_credentials *credentials)
+{
+ gensec_security->credentials = talloc_reference(gensec_security, credentials);
+ NT_STATUS_HAVE_NO_MEMORY(gensec_security->credentials);
+ gensec_want_feature(gensec_security, cli_credentials_get_gensec_features(gensec_security->credentials));
+ return NT_STATUS_OK;
+}
+
+/**
+ * Return the credentials structure associated with a GENSEC context
+ *
+ */
+
+_PUBLIC_ struct cli_credentials *gensec_get_credentials(struct gensec_security *gensec_security)
+{
+ if (!gensec_security) {
+ return NULL;
+ }
+ return gensec_security->credentials;
+}
+
+/**
+ * Set the target service (such as 'http' or 'host') on a GENSEC context - ensures it is talloc()ed
+ *
+ */
+
+_PUBLIC_ NTSTATUS gensec_set_target_service(struct gensec_security *gensec_security, const char *service)
+{
+ gensec_security->target.service = talloc_strdup(gensec_security, service);
+ if (!gensec_security->target.service) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ return NT_STATUS_OK;
+}
+
+_PUBLIC_ const char *gensec_get_target_service(struct gensec_security *gensec_security)
+{
+ if (gensec_security->target.service) {
+ return gensec_security->target.service;
+ }
+
+ return "host";
+}
+
+/**
+ * Set the target hostname (suitable for kerberos resolutation) on a GENSEC context - ensures it is talloc()ed
+ *
+ */
+
+_PUBLIC_ NTSTATUS gensec_set_target_hostname(struct gensec_security *gensec_security, const char *hostname)
+{
+ gensec_security->target.hostname = talloc_strdup(gensec_security, hostname);
+ if (hostname && !gensec_security->target.hostname) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ return NT_STATUS_OK;
+}
+
+_PUBLIC_ const char *gensec_get_target_hostname(struct gensec_security *gensec_security)
+{
+ /* We allow the target hostname to be overriden for testing purposes */
+ if (gensec_security->settings->target_hostname) {
+ return gensec_security->settings->target_hostname;
+ }
+
+ if (gensec_security->target.hostname) {
+ return gensec_security->target.hostname;
+ }
+
+ /* We could add use the 'set sockaddr' call, and do a reverse
+ * lookup, but this would be both insecure (compromising the
+ * way kerberos works) and add DNS timeouts */
+ return NULL;
+}
+
+/**
+ * Set (and talloc_reference) local and peer socket addresses onto a socket context on the GENSEC context
+ *
+ * This is so that kerberos can include these addresses in
+ * cryptographic tokens, to avoid certain attacks.
+ */
+
+_PUBLIC_ NTSTATUS gensec_set_my_addr(struct gensec_security *gensec_security, struct socket_address *my_addr)
+{
+ gensec_security->my_addr = my_addr;
+ if (my_addr && !talloc_reference(gensec_security, my_addr)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ return NT_STATUS_OK;
+}
+
+_PUBLIC_ NTSTATUS gensec_set_peer_addr(struct gensec_security *gensec_security, struct socket_address *peer_addr)
+{
+ gensec_security->peer_addr = peer_addr;
+ if (peer_addr && !talloc_reference(gensec_security, peer_addr)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ return NT_STATUS_OK;
+}
+
+struct socket_address *gensec_get_my_addr(struct gensec_security *gensec_security)
+{
+ if (gensec_security->my_addr) {
+ return gensec_security->my_addr;
+ }
+
+ /* We could add a 'set sockaddr' call, and do a lookup. This
+ * would avoid needing to do system calls if nothing asks. */
+ return NULL;
+}
+
+_PUBLIC_ struct socket_address *gensec_get_peer_addr(struct gensec_security *gensec_security)
+{
+ if (gensec_security->peer_addr) {
+ return gensec_security->peer_addr;
+ }
+
+ /* We could add a 'set sockaddr' call, and do a lookup. This
+ * would avoid needing to do system calls if nothing asks.
+ * However, this is not appropriate for the peer addres on
+ * datagram sockets */
+ return NULL;
+}
+
+
+
+/**
+ * Set the target principal (assuming it it known, say from the SPNEGO reply)
+ * - ensures it is talloc()ed
+ *
+ */
+
+NTSTATUS gensec_set_target_principal(struct gensec_security *gensec_security, const char *principal)
+{
+ gensec_security->target.principal = talloc_strdup(gensec_security, principal);
+ if (!gensec_security->target.principal) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ return NT_STATUS_OK;
+}
+
+const char *gensec_get_target_principal(struct gensec_security *gensec_security)
+{
+ if (gensec_security->target.principal) {
+ return gensec_security->target.principal;
+ }
+
+ return NULL;
+}
+
+/*
+ register a GENSEC backend.
+
+ The 'name' can be later used by other backends to find the operations
+ structure for this backend.
+*/
+NTSTATUS gensec_register(const struct gensec_security_ops *ops)
+{
+ if (gensec_security_by_name(NULL, ops->name) != NULL) {
+ /* its already registered! */
+ DEBUG(0,("GENSEC backend '%s' already registered\n",
+ ops->name));
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ generic_security_ops = talloc_realloc(talloc_autofree_context(),
+ generic_security_ops,
+ struct gensec_security_ops *,
+ gensec_num_backends+2);
+ if (!generic_security_ops) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ generic_security_ops[gensec_num_backends] = discard_const_p(struct gensec_security_ops, ops);
+ gensec_num_backends++;
+ generic_security_ops[gensec_num_backends] = NULL;
+
+ DEBUG(3,("GENSEC backend '%s' registered\n",
+ ops->name));
+
+ return NT_STATUS_OK;
+}
+
+/*
+ return the GENSEC interface version, and the size of some critical types
+ This can be used by backends to either detect compilation errors, or provide
+ multiple implementations for different smbd compilation options in one module
+*/
+const struct gensec_critical_sizes *gensec_interface_version(void)
+{
+ static const struct gensec_critical_sizes critical_sizes = {
+ GENSEC_INTERFACE_VERSION,
+ sizeof(struct gensec_security_ops),
+ sizeof(struct gensec_security),
+ };
+
+ return &critical_sizes;
+}
+
+static int sort_gensec(struct gensec_security_ops **gs1, struct gensec_security_ops **gs2) {
+ return (*gs2)->priority - (*gs1)->priority;
+}
+
+int gensec_setting_int(struct gensec_settings *settings, const char *mechanism, const char *name, int default_value)
+{
+ return lp_parm_int(settings->lp_ctx, NULL, mechanism, name, default_value);
+}
+
+bool gensec_setting_bool(struct gensec_settings *settings, const char *mechanism, const char *name, bool default_value)
+{
+ return lp_parm_bool(settings->lp_ctx, NULL, mechanism, name, default_value);
+}
+
+/*
+ initialise the GENSEC subsystem
+*/
+_PUBLIC_ NTSTATUS gensec_init(struct loadparm_context *lp_ctx)
+{
+ static bool initialized = false;
+ extern NTSTATUS gensec_sasl_init(void);
+ extern NTSTATUS gensec_krb5_init(void);
+ extern NTSTATUS gensec_schannel_init(void);
+ extern NTSTATUS gensec_spnego_init(void);
+ extern NTSTATUS gensec_gssapi_init(void);
+ extern NTSTATUS gensec_ntlmssp_init(void);
+
+ init_module_fn static_init[] = { STATIC_gensec_MODULES };
+ init_module_fn *shared_init;
+
+ if (initialized) return NT_STATUS_OK;
+ initialized = true;
+
+ shared_init = load_samba_modules(NULL, lp_ctx, "gensec");
+
+ run_init_functions(static_init);
+ run_init_functions(shared_init);
+
+ talloc_free(shared_init);
+
+ qsort(generic_security_ops, gensec_num_backends, sizeof(*generic_security_ops), QSORT_CAST sort_gensec);
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/auth/gensec/gensec.h b/source4/auth/gensec/gensec.h
new file mode 100644
index 0000000000..c4e93ee97b
--- /dev/null
+++ b/source4/auth/gensec/gensec.h
@@ -0,0 +1,318 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Generic Authentication Interface
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __GENSEC_H__
+#define __GENSEC_H__
+
+#include "../lib/util/data_blob.h"
+#include "libcli/util/ntstatus.h"
+
+#define GENSEC_OID_NTLMSSP "1.3.6.1.4.1.311.2.2.10"
+#define GENSEC_OID_SPNEGO "1.3.6.1.5.5.2"
+#define GENSEC_OID_KERBEROS5 "1.2.840.113554.1.2.2"
+#define GENSEC_OID_KERBEROS5_OLD "1.2.840.48018.1.2.2"
+#define GENSEC_OID_KERBEROS5_USER2USER "1.2.840.113554.1.2.2.3"
+
+enum gensec_priority {
+ GENSEC_SPNEGO = 90,
+ GENSEC_GSSAPI = 80,
+ GENSEC_KRB5 = 70,
+ GENSEC_SCHANNEL = 60,
+ GENSEC_NTLMSSP = 50,
+ GENSEC_SASL = 20,
+ GENSEC_OTHER = 0
+};
+
+struct gensec_security;
+struct gensec_target {
+ const char *principal;
+ const char *hostname;
+ const char *service;
+};
+
+#define GENSEC_FEATURE_SESSION_KEY 0x00000001
+#define GENSEC_FEATURE_SIGN 0x00000002
+#define GENSEC_FEATURE_SEAL 0x00000004
+#define GENSEC_FEATURE_DCE_STYLE 0x00000008
+#define GENSEC_FEATURE_ASYNC_REPLIES 0x00000010
+#define GENSEC_FEATURE_DATAGRAM_MODE 0x00000020
+#define GENSEC_FEATURE_SIGN_PKT_HEADER 0x00000040
+#define GENSEC_FEATURE_NEW_SPNEGO 0x00000080
+
+/* GENSEC mode */
+enum gensec_role
+{
+ GENSEC_SERVER,
+ GENSEC_CLIENT
+};
+
+struct auth_session_info;
+struct cli_credentials;
+struct gensec_settings;
+struct tevent_context;
+
+struct gensec_update_request {
+ struct gensec_security *gensec_security;
+ void *private_data;
+ DATA_BLOB in;
+ DATA_BLOB out;
+ NTSTATUS status;
+ struct {
+ void (*fn)(struct gensec_update_request *req, void *private_data);
+ void *private_data;
+ } callback;
+};
+
+struct gensec_settings {
+ struct loadparm_context *lp_ctx;
+ struct smb_iconv_convenience *iconv_convenience;
+ const char *target_hostname;
+};
+
+struct gensec_security_ops {
+ const char *name;
+ const char *sasl_name;
+ uint8_t auth_type; /* 0 if not offered on DCE-RPC */
+ const char **oid; /* NULL if not offered by SPNEGO */
+ NTSTATUS (*client_start)(struct gensec_security *gensec_security);
+ NTSTATUS (*server_start)(struct gensec_security *gensec_security);
+ /**
+ Determine if a packet has the right 'magic' for this mechanism
+ */
+ NTSTATUS (*magic)(struct gensec_security *gensec_security,
+ const DATA_BLOB *first_packet);
+ NTSTATUS (*update)(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx,
+ const DATA_BLOB in, DATA_BLOB *out);
+ NTSTATUS (*seal_packet)(struct gensec_security *gensec_security, TALLOC_CTX *sig_mem_ctx,
+ uint8_t *data, size_t length,
+ const uint8_t *whole_pdu, size_t pdu_length,
+ DATA_BLOB *sig);
+ NTSTATUS (*sign_packet)(struct gensec_security *gensec_security, TALLOC_CTX *sig_mem_ctx,
+ const uint8_t *data, size_t length,
+ const uint8_t *whole_pdu, size_t pdu_length,
+ DATA_BLOB *sig);
+ size_t (*sig_size)(struct gensec_security *gensec_security, size_t data_size);
+ size_t (*max_input_size)(struct gensec_security *gensec_security);
+ size_t (*max_wrapped_size)(struct gensec_security *gensec_security);
+ NTSTATUS (*check_packet)(struct gensec_security *gensec_security, TALLOC_CTX *sig_mem_ctx,
+ const uint8_t *data, size_t length,
+ const uint8_t *whole_pdu, size_t pdu_length,
+ const DATA_BLOB *sig);
+ NTSTATUS (*unseal_packet)(struct gensec_security *gensec_security, TALLOC_CTX *sig_mem_ctx,
+ uint8_t *data, size_t length,
+ const uint8_t *whole_pdu, size_t pdu_length,
+ const DATA_BLOB *sig);
+ NTSTATUS (*wrap)(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *in,
+ DATA_BLOB *out);
+ NTSTATUS (*unwrap)(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *in,
+ DATA_BLOB *out);
+ NTSTATUS (*wrap_packets)(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *in,
+ DATA_BLOB *out,
+ size_t *len_processed);
+ NTSTATUS (*unwrap_packets)(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *in,
+ DATA_BLOB *out,
+ size_t *len_processed);
+ NTSTATUS (*packet_full_request)(struct gensec_security *gensec_security,
+ DATA_BLOB blob, size_t *size);
+ NTSTATUS (*session_key)(struct gensec_security *gensec_security, DATA_BLOB *session_key);
+ NTSTATUS (*session_info)(struct gensec_security *gensec_security,
+ struct auth_session_info **session_info);
+ void (*want_feature)(struct gensec_security *gensec_security,
+ uint32_t feature);
+ bool (*have_feature)(struct gensec_security *gensec_security,
+ uint32_t feature);
+ bool enabled;
+ bool kerberos;
+ enum gensec_priority priority;
+};
+
+struct gensec_security_ops_wrapper {
+ const struct gensec_security_ops *op;
+ const char *oid;
+};
+
+#define GENSEC_INTERFACE_VERSION 0
+
+struct gensec_security {
+ const struct gensec_security_ops *ops;
+ void *private_data;
+ struct cli_credentials *credentials;
+ struct gensec_target target;
+ enum gensec_role gensec_role;
+ bool subcontext;
+ uint32_t want_features;
+ struct tevent_context *event_ctx;
+ struct socket_address *my_addr, *peer_addr;
+ struct gensec_settings *settings;
+
+ /* When we are a server, this may be filled in to provide an
+ * NTLM authentication backend, and user lookup (such as if no
+ * PAC is found) */
+ struct auth_context *auth_context;
+};
+
+/* this structure is used by backends to determine the size of some critical types */
+struct gensec_critical_sizes {
+ int interface_version;
+ int sizeof_gensec_security_ops;
+ int sizeof_gensec_security;
+};
+
+/* Socket wrapper */
+
+struct gensec_security;
+struct socket_context;
+struct auth_context;
+
+NTSTATUS gensec_socket_init(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ struct socket_context *current_socket,
+ struct tevent_context *ev,
+ void (*recv_handler)(void *, uint16_t),
+ void *recv_private,
+ struct socket_context **new_socket);
+/* These functions are for use here only (public because SPNEGO must
+ * use them for recursion) */
+NTSTATUS gensec_wrap_packets(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *in,
+ DATA_BLOB *out,
+ size_t *len_processed);
+/* These functions are for use here only (public because SPNEGO must
+ * use them for recursion) */
+NTSTATUS gensec_unwrap_packets(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *in,
+ DATA_BLOB *out,
+ size_t *len_processed);
+
+/* These functions are for use here only (public because SPNEGO must
+ * use them for recursion) */
+NTSTATUS gensec_packet_full_request(struct gensec_security *gensec_security,
+ DATA_BLOB blob, size_t *size);
+
+struct loadparm_context;
+
+NTSTATUS gensec_subcontext_start(TALLOC_CTX *mem_ctx,
+ struct gensec_security *parent,
+ struct gensec_security **gensec_security);
+NTSTATUS gensec_client_start(TALLOC_CTX *mem_ctx,
+ struct gensec_security **gensec_security,
+ struct tevent_context *ev,
+ struct gensec_settings *settings);
+NTSTATUS gensec_start_mech_by_sasl_list(struct gensec_security *gensec_security,
+ const char **sasl_names);
+NTSTATUS gensec_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx,
+ const DATA_BLOB in, DATA_BLOB *out);
+void gensec_update_send(struct gensec_security *gensec_security, const DATA_BLOB in,
+ void (*callback)(struct gensec_update_request *req, void *private_data),
+ void *private_data);
+NTSTATUS gensec_update_recv(struct gensec_update_request *req, TALLOC_CTX *out_mem_ctx, DATA_BLOB *out);
+void gensec_want_feature(struct gensec_security *gensec_security,
+ uint32_t feature);
+bool gensec_have_feature(struct gensec_security *gensec_security,
+ uint32_t feature);
+NTSTATUS gensec_set_credentials(struct gensec_security *gensec_security, struct cli_credentials *credentials);
+NTSTATUS gensec_set_target_service(struct gensec_security *gensec_security, const char *service);
+const char *gensec_get_target_service(struct gensec_security *gensec_security);
+NTSTATUS gensec_set_target_hostname(struct gensec_security *gensec_security, const char *hostname);
+const char *gensec_get_target_hostname(struct gensec_security *gensec_security);
+NTSTATUS gensec_session_key(struct gensec_security *gensec_security,
+ DATA_BLOB *session_key);
+NTSTATUS gensec_start_mech_by_oid(struct gensec_security *gensec_security,
+ const char *mech_oid);
+const char *gensec_get_name_by_oid(struct gensec_security *gensec_security, const char *oid_string);
+struct cli_credentials *gensec_get_credentials(struct gensec_security *gensec_security);
+struct socket_address *gensec_get_peer_addr(struct gensec_security *gensec_security);
+NTSTATUS gensec_init(struct loadparm_context *lp_ctx);
+NTSTATUS gensec_unseal_packet(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ uint8_t *data, size_t length,
+ const uint8_t *whole_pdu, size_t pdu_length,
+ const DATA_BLOB *sig);
+NTSTATUS gensec_check_packet(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ const uint8_t *data, size_t length,
+ const uint8_t *whole_pdu, size_t pdu_length,
+ const DATA_BLOB *sig);
+size_t gensec_sig_size(struct gensec_security *gensec_security, size_t data_size);
+NTSTATUS gensec_seal_packet(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ uint8_t *data, size_t length,
+ const uint8_t *whole_pdu, size_t pdu_length,
+ DATA_BLOB *sig);
+NTSTATUS gensec_sign_packet(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ const uint8_t *data, size_t length,
+ const uint8_t *whole_pdu, size_t pdu_length,
+ DATA_BLOB *sig);
+NTSTATUS gensec_start_mech_by_authtype(struct gensec_security *gensec_security,
+ uint8_t auth_type, uint8_t auth_level);
+const char *gensec_get_name_by_authtype(struct gensec_security *gensec_security, uint8_t authtype);
+NTSTATUS gensec_server_start(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct gensec_settings *settings,
+ struct auth_context *auth_context,
+ struct gensec_security **gensec_security);
+NTSTATUS gensec_session_info(struct gensec_security *gensec_security,
+ struct auth_session_info **session_info);
+NTSTATUS auth_nt_status_squash(NTSTATUS nt_status);
+struct creds_CredentialState;
+NTSTATUS dcerpc_schannel_creds(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ struct creds_CredentialState **creds);
+NTSTATUS gensec_set_peer_addr(struct gensec_security *gensec_security, struct socket_address *peer_addr);
+NTSTATUS gensec_set_my_addr(struct gensec_security *gensec_security, struct socket_address *my_addr);
+
+NTSTATUS gensec_start_mech_by_name(struct gensec_security *gensec_security,
+ const char *name);
+
+NTSTATUS gensec_unwrap(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *in,
+ DATA_BLOB *out);
+NTSTATUS gensec_wrap(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *in,
+ DATA_BLOB *out);
+
+struct gensec_security_ops **gensec_security_all(void);
+bool gensec_security_ops_enabled(struct gensec_security_ops *ops, struct loadparm_context *lp_ctx);
+struct gensec_security_ops **gensec_use_kerberos_mechs(TALLOC_CTX *mem_ctx,
+ struct gensec_security_ops **old_gensec_list,
+ struct cli_credentials *creds);
+
+NTSTATUS gensec_start_mech_by_sasl_name(struct gensec_security *gensec_security,
+ const char *sasl_name);
+
+int gensec_setting_int(struct gensec_settings *settings, const char *mechanism, const char *name, int default_value);
+bool gensec_setting_bool(struct gensec_settings *settings, const char *mechanism, const char *name, bool default_value);
+
+#endif /* __GENSEC_H__ */
diff --git a/source4/auth/gensec/gensec.pc.in b/source4/auth/gensec/gensec.pc.in
new file mode 100644
index 0000000000..faf772ae73
--- /dev/null
+++ b/source4/auth/gensec/gensec.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+modulesdir=${prefix}/modules/gensec
+
+Name: gensec
+Description: Generic Security Library
+Version: 0.0.1
+Libs: -L${libdir} -lgensec
+Cflags: -I${includedir} -DHAVE_IMMEDIATE_STRUCTURES=1
diff --git a/source4/auth/gensec/gensec_gssapi.c b/source4/auth/gensec/gensec_gssapi.c
new file mode 100644
index 0000000000..aae04dffe2
--- /dev/null
+++ b/source4/auth/gensec/gensec_gssapi.c
@@ -0,0 +1,1525 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Kerberos backend for GENSEC
+
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
+ Copyright (C) Stefan Metzmacher <metze@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/events/events.h"
+#include "system/kerberos.h"
+#include "auth/kerberos/kerberos.h"
+#include "librpc/gen_ndr/krb5pac.h"
+#include "auth/auth.h"
+#include "lib/ldb/include/ldb.h"
+#include "auth/auth_sam.h"
+#include "librpc/rpc/dcerpc.h"
+#include "auth/credentials/credentials.h"
+#include "auth/credentials/credentials_krb5.h"
+#include "auth/gensec/gensec.h"
+#include "auth/gensec/gensec_proto.h"
+#include "param/param.h"
+#include "auth/session_proto.h"
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_krb5.h>
+#include "auth/gensec/gensec_gssapi.h"
+
+static size_t gensec_gssapi_max_input_size(struct gensec_security *gensec_security);
+static size_t gensec_gssapi_max_wrapped_size(struct gensec_security *gensec_security);
+
+static char *gssapi_error_string(TALLOC_CTX *mem_ctx,
+ OM_uint32 maj_stat, OM_uint32 min_stat,
+ const gss_OID mech)
+{
+ OM_uint32 disp_min_stat, disp_maj_stat;
+ gss_buffer_desc maj_error_message;
+ gss_buffer_desc min_error_message;
+ char *maj_error_string, *min_error_string;
+ OM_uint32 msg_ctx = 0;
+
+ char *ret;
+
+ maj_error_message.value = NULL;
+ min_error_message.value = NULL;
+ maj_error_message.length = 0;
+ min_error_message.length = 0;
+
+ disp_maj_stat = gss_display_status(&disp_min_stat, maj_stat, GSS_C_GSS_CODE,
+ mech, &msg_ctx, &maj_error_message);
+ disp_maj_stat = gss_display_status(&disp_min_stat, min_stat, GSS_C_MECH_CODE,
+ mech, &msg_ctx, &min_error_message);
+
+ maj_error_string = talloc_strndup(mem_ctx, (char *)maj_error_message.value, maj_error_message.length);
+
+ min_error_string = talloc_strndup(mem_ctx, (char *)min_error_message.value, min_error_message.length);
+
+ ret = talloc_asprintf(mem_ctx, "%s: %s", maj_error_string, min_error_string);
+
+ talloc_free(maj_error_string);
+ talloc_free(min_error_string);
+
+ gss_release_buffer(&disp_min_stat, &maj_error_message);
+ gss_release_buffer(&disp_min_stat, &min_error_message);
+
+ return ret;
+}
+
+
+static int gensec_gssapi_destructor(struct gensec_gssapi_state *gensec_gssapi_state)
+{
+ OM_uint32 maj_stat, min_stat;
+
+ if (gensec_gssapi_state->delegated_cred_handle != GSS_C_NO_CREDENTIAL) {
+ maj_stat = gss_release_cred(&min_stat,
+ &gensec_gssapi_state->delegated_cred_handle);
+ }
+
+ if (gensec_gssapi_state->gssapi_context != GSS_C_NO_CONTEXT) {
+ maj_stat = gss_delete_sec_context (&min_stat,
+ &gensec_gssapi_state->gssapi_context,
+ GSS_C_NO_BUFFER);
+ }
+
+ if (gensec_gssapi_state->server_name != GSS_C_NO_NAME) {
+ maj_stat = gss_release_name(&min_stat, &gensec_gssapi_state->server_name);
+ }
+ if (gensec_gssapi_state->client_name != GSS_C_NO_NAME) {
+ maj_stat = gss_release_name(&min_stat, &gensec_gssapi_state->client_name);
+ }
+
+ if (gensec_gssapi_state->lucid) {
+ gss_krb5_free_lucid_sec_context(&min_stat, gensec_gssapi_state->lucid);
+ }
+
+ return 0;
+}
+
+static NTSTATUS gensec_gssapi_init_lucid(struct gensec_gssapi_state *gensec_gssapi_state)
+{
+ OM_uint32 maj_stat, min_stat;
+
+ if (gensec_gssapi_state->lucid) {
+ return NT_STATUS_OK;
+ }
+
+ maj_stat = gss_krb5_export_lucid_sec_context(&min_stat,
+ &gensec_gssapi_state->gssapi_context,
+ 1,
+ (void **)&gensec_gssapi_state->lucid);
+ if (maj_stat != GSS_S_COMPLETE) {
+ DEBUG(0,("gensec_gssapi_init_lucid: %s\n",
+ gssapi_error_string(gensec_gssapi_state,
+ maj_stat, min_stat,
+ gensec_gssapi_state->gss_oid)));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ if (gensec_gssapi_state->lucid->version != 1) {
+ DEBUG(0,("gensec_gssapi_init_lucid: lucid version[%d] != 1\n",
+ gensec_gssapi_state->lucid->version));
+ gss_krb5_free_lucid_sec_context(&min_stat, gensec_gssapi_state->lucid);
+ gensec_gssapi_state->lucid = NULL;
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS gensec_gssapi_start(struct gensec_security *gensec_security)
+{
+ struct gensec_gssapi_state *gensec_gssapi_state;
+ krb5_error_code ret;
+ struct gsskrb5_send_to_kdc send_to_kdc;
+
+ gensec_gssapi_state = talloc(gensec_security, struct gensec_gssapi_state);
+ if (!gensec_gssapi_state) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ gensec_gssapi_state->gss_exchange_count = 0;
+ gensec_gssapi_state->max_wrap_buf_size
+ = gensec_setting_int(gensec_security->settings, "gensec_gssapi", "max wrap buf size", 65536);
+
+ gensec_gssapi_state->sasl = false;
+ gensec_gssapi_state->sasl_state = STAGE_GSS_NEG;
+
+ gensec_security->private_data = gensec_gssapi_state;
+
+ gensec_gssapi_state->gssapi_context = GSS_C_NO_CONTEXT;
+ gensec_gssapi_state->server_name = GSS_C_NO_NAME;
+ gensec_gssapi_state->client_name = GSS_C_NO_NAME;
+ gensec_gssapi_state->lucid = NULL;
+
+ /* TODO: Fill in channel bindings */
+ gensec_gssapi_state->input_chan_bindings = GSS_C_NO_CHANNEL_BINDINGS;
+
+ gensec_gssapi_state->want_flags = 0;
+ if (gensec_setting_bool(gensec_security->settings, "gensec_gssapi", "mutual", true)) {
+ gensec_gssapi_state->want_flags |= GSS_C_MUTUAL_FLAG;
+ }
+ if (gensec_setting_bool(gensec_security->settings, "gensec_gssapi", "delegation", true)) {
+ gensec_gssapi_state->want_flags |= GSS_C_DELEG_FLAG;
+ }
+ if (gensec_setting_bool(gensec_security->settings, "gensec_gssapi", "replay", true)) {
+ gensec_gssapi_state->want_flags |= GSS_C_REPLAY_FLAG;
+ }
+ if (gensec_setting_bool(gensec_security->settings, "gensec_gssapi", "sequence", true)) {
+ gensec_gssapi_state->want_flags |= GSS_C_SEQUENCE_FLAG;
+ }
+
+ gensec_gssapi_state->got_flags = 0;
+
+ gensec_gssapi_state->session_key = data_blob(NULL, 0);
+ gensec_gssapi_state->pac = data_blob(NULL, 0);
+
+ gensec_gssapi_state->delegated_cred_handle = GSS_C_NO_CREDENTIAL;
+ gensec_gssapi_state->sig_size = 0;
+
+ talloc_set_destructor(gensec_gssapi_state, gensec_gssapi_destructor);
+
+ if (gensec_security->want_features & GENSEC_FEATURE_SIGN) {
+ gensec_gssapi_state->want_flags |= GSS_C_INTEG_FLAG;
+ }
+ if (gensec_security->want_features & GENSEC_FEATURE_SEAL) {
+ gensec_gssapi_state->want_flags |= GSS_C_CONF_FLAG;
+ }
+ if (gensec_security->want_features & GENSEC_FEATURE_DCE_STYLE) {
+ gensec_gssapi_state->want_flags |= GSS_C_DCE_STYLE;
+ }
+
+ gensec_gssapi_state->gss_oid = GSS_C_NULL_OID;
+
+ send_to_kdc.func = smb_krb5_send_and_recv_func;
+ send_to_kdc.ptr = gensec_security->event_ctx;
+
+ ret = gsskrb5_set_send_to_kdc(&send_to_kdc);
+ if (ret) {
+ DEBUG(1,("gensec_krb5_start: gsskrb5_set_send_to_kdc failed\n"));
+ talloc_free(gensec_gssapi_state);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ if (lp_realm(gensec_security->settings->lp_ctx) && *lp_realm(gensec_security->settings->lp_ctx)) {
+ char *upper_realm = strupper_talloc(gensec_gssapi_state, lp_realm(gensec_security->settings->lp_ctx));
+ if (!upper_realm) {
+ DEBUG(1,("gensec_krb5_start: could not uppercase realm: %s\n", lp_realm(gensec_security->settings->lp_ctx)));
+ talloc_free(gensec_gssapi_state);
+ return NT_STATUS_NO_MEMORY;
+ }
+ ret = gsskrb5_set_default_realm(upper_realm);
+ talloc_free(upper_realm);
+ if (ret) {
+ DEBUG(1,("gensec_krb5_start: gsskrb5_set_default_realm failed\n"));
+ talloc_free(gensec_gssapi_state);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ }
+
+ /* don't do DNS lookups of any kind, it might/will fail for a netbios name */
+ ret = gsskrb5_set_dns_canonicalize(gensec_setting_bool(gensec_security->settings, "krb5", "set_dns_canonicalize", false));
+ if (ret) {
+ DEBUG(1,("gensec_krb5_start: gsskrb5_set_dns_canonicalize failed\n"));
+ talloc_free(gensec_gssapi_state);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ ret = smb_krb5_init_context(gensec_gssapi_state,
+ gensec_security->event_ctx,
+ gensec_security->settings->lp_ctx,
+ &gensec_gssapi_state->smb_krb5_context);
+ if (ret) {
+ DEBUG(1,("gensec_krb5_start: krb5_init_context failed (%s)\n",
+ error_message(ret)));
+ talloc_free(gensec_gssapi_state);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS gensec_gssapi_server_start(struct gensec_security *gensec_security)
+{
+ NTSTATUS nt_status;
+ int ret;
+ struct gensec_gssapi_state *gensec_gssapi_state;
+ struct cli_credentials *machine_account;
+ struct gssapi_creds_container *gcc;
+
+ nt_status = gensec_gssapi_start(gensec_security);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ gensec_gssapi_state = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state);
+
+ machine_account = gensec_get_credentials(gensec_security);
+
+ if (!machine_account) {
+ DEBUG(3, ("No machine account credentials specified\n"));
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ } else {
+ ret = cli_credentials_get_server_gss_creds(machine_account,
+ gensec_security->event_ctx,
+ gensec_security->settings->lp_ctx, &gcc);
+ if (ret) {
+ DEBUG(1, ("Aquiring acceptor credentials failed: %s\n",
+ error_message(ret)));
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ }
+ }
+
+ gensec_gssapi_state->server_cred = gcc;
+ return NT_STATUS_OK;
+
+}
+
+static NTSTATUS gensec_gssapi_sasl_server_start(struct gensec_security *gensec_security)
+{
+ NTSTATUS nt_status;
+ struct gensec_gssapi_state *gensec_gssapi_state;
+ nt_status = gensec_gssapi_server_start(gensec_security);
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ gensec_gssapi_state = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state);
+ gensec_gssapi_state->sasl = true;
+ }
+ return nt_status;
+}
+
+static NTSTATUS gensec_gssapi_client_start(struct gensec_security *gensec_security)
+{
+ struct gensec_gssapi_state *gensec_gssapi_state;
+ struct cli_credentials *creds = gensec_get_credentials(gensec_security);
+ krb5_error_code ret;
+ NTSTATUS nt_status;
+ gss_buffer_desc name_token;
+ gss_OID name_type;
+ OM_uint32 maj_stat, min_stat;
+ const char *hostname = gensec_get_target_hostname(gensec_security);
+ const char *principal;
+ struct gssapi_creds_container *gcc;
+
+ if (!hostname) {
+ DEBUG(1, ("Could not determine hostname for target computer, cannot use kerberos\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ if (is_ipaddress(hostname)) {
+ DEBUG(2, ("Cannot do GSSAPI to an IP address\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ if (strcmp(hostname, "localhost") == 0) {
+ DEBUG(2, ("GSSAPI to 'localhost' does not make sense\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ nt_status = gensec_gssapi_start(gensec_security);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ gensec_gssapi_state = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state);
+
+ gensec_gssapi_state->gss_oid = gss_mech_krb5;
+
+ principal = gensec_get_target_principal(gensec_security);
+ if (principal && lp_client_use_spnego_principal(gensec_security->settings->lp_ctx)) {
+ name_type = GSS_C_NULL_OID;
+ } else {
+ principal = talloc_asprintf(gensec_gssapi_state, "%s@%s",
+ gensec_get_target_service(gensec_security),
+ hostname);
+
+ name_type = GSS_C_NT_HOSTBASED_SERVICE;
+ }
+ name_token.value = discard_const_p(uint8_t, principal);
+ name_token.length = strlen(principal);
+
+
+ maj_stat = gss_import_name (&min_stat,
+ &name_token,
+ name_type,
+ &gensec_gssapi_state->server_name);
+ if (maj_stat) {
+ DEBUG(2, ("GSS Import name of %s failed: %s\n",
+ (char *)name_token.value,
+ gssapi_error_string(gensec_gssapi_state, maj_stat, min_stat, gensec_gssapi_state->gss_oid)));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ ret = cli_credentials_get_client_gss_creds(creds,
+ gensec_security->event_ctx,
+ gensec_security->settings->lp_ctx, &gcc);
+ switch (ret) {
+ case 0:
+ break;
+ case KRB5KDC_ERR_PREAUTH_FAILED:
+ return NT_STATUS_LOGON_FAILURE;
+ case KRB5_KDC_UNREACH:
+ DEBUG(3, ("Cannot reach a KDC we require to contact %s\n", principal));
+ return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */
+ default:
+ DEBUG(1, ("Aquiring initiator credentials failed\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ gensec_gssapi_state->client_cred = gcc;
+ if (!talloc_reference(gensec_gssapi_state, gcc)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS gensec_gssapi_sasl_client_start(struct gensec_security *gensec_security)
+{
+ NTSTATUS nt_status;
+ struct gensec_gssapi_state *gensec_gssapi_state;
+ nt_status = gensec_gssapi_client_start(gensec_security);
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ gensec_gssapi_state = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state);
+ gensec_gssapi_state->sasl = true;
+ }
+ return nt_status;
+}
+
+
+/**
+ * Check if the packet is one for this mechansim
+ *
+ * @param gensec_security GENSEC state
+ * @param in The request, as a DATA_BLOB
+ * @return Error, INVALID_PARAMETER if it's not a packet for us
+ * or NT_STATUS_OK if the packet is ok.
+ */
+
+static NTSTATUS gensec_gssapi_magic(struct gensec_security *gensec_security,
+ const DATA_BLOB *in)
+{
+ if (gensec_gssapi_check_oid(in, GENSEC_OID_KERBEROS5)) {
+ return NT_STATUS_OK;
+ } else {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+}
+
+
+/**
+ * Next state function for the GSSAPI GENSEC mechanism
+ *
+ * @param gensec_gssapi_state GSSAPI State
+ * @param out_mem_ctx The TALLOC_CTX for *out to be allocated on
+ * @param in The request, as a DATA_BLOB
+ * @param out The reply, as an talloc()ed DATA_BLOB, on *out_mem_ctx
+ * @return Error, MORE_PROCESSING_REQUIRED if a reply is sent,
+ * or NT_STATUS_OK if the user is authenticated.
+ */
+
+static NTSTATUS gensec_gssapi_update(struct gensec_security *gensec_security,
+ TALLOC_CTX *out_mem_ctx,
+ const DATA_BLOB in, DATA_BLOB *out)
+{
+ struct gensec_gssapi_state *gensec_gssapi_state
+ = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state);
+ NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE;
+ OM_uint32 maj_stat, min_stat;
+ OM_uint32 min_stat2;
+ gss_buffer_desc input_token, output_token;
+ gss_OID gss_oid_p = NULL;
+ input_token.length = in.length;
+ input_token.value = in.data;
+
+ switch (gensec_gssapi_state->sasl_state) {
+ case STAGE_GSS_NEG:
+ {
+ switch (gensec_security->gensec_role) {
+ case GENSEC_CLIENT:
+ {
+ maj_stat = gss_init_sec_context(&min_stat,
+ gensec_gssapi_state->client_cred->creds,
+ &gensec_gssapi_state->gssapi_context,
+ gensec_gssapi_state->server_name,
+ gensec_gssapi_state->gss_oid,
+ gensec_gssapi_state->want_flags,
+ 0,
+ gensec_gssapi_state->input_chan_bindings,
+ &input_token,
+ &gss_oid_p,
+ &output_token,
+ &gensec_gssapi_state->got_flags, /* ret flags */
+ NULL);
+ if (gss_oid_p) {
+ gensec_gssapi_state->gss_oid = gss_oid_p;
+ }
+ break;
+ }
+ case GENSEC_SERVER:
+ {
+ maj_stat = gss_accept_sec_context(&min_stat,
+ &gensec_gssapi_state->gssapi_context,
+ gensec_gssapi_state->server_cred->creds,
+ &input_token,
+ gensec_gssapi_state->input_chan_bindings,
+ &gensec_gssapi_state->client_name,
+ &gss_oid_p,
+ &output_token,
+ &gensec_gssapi_state->got_flags,
+ NULL,
+ &gensec_gssapi_state->delegated_cred_handle);
+ if (gss_oid_p) {
+ gensec_gssapi_state->gss_oid = gss_oid_p;
+ }
+ break;
+ }
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+
+ }
+
+ gensec_gssapi_state->gss_exchange_count++;
+
+ if (maj_stat == GSS_S_COMPLETE) {
+ *out = data_blob_talloc(out_mem_ctx, output_token.value, output_token.length);
+ gss_release_buffer(&min_stat2, &output_token);
+
+ if (gensec_gssapi_state->got_flags & GSS_C_DELEG_FLAG) {
+ DEBUG(5, ("gensec_gssapi: credentials were delegated\n"));
+ } else {
+ DEBUG(5, ("gensec_gssapi: NO credentials were delegated\n"));
+ }
+
+ /* We may have been invoked as SASL, so there
+ * is more work to do */
+ if (gensec_gssapi_state->sasl) {
+ /* Due to a very subtle interaction
+ * with SASL and the LDAP libs, we
+ * must ensure the data pointer is
+ * != NULL, but the length is 0.
+ *
+ * This ensures we send a 'zero
+ * length' (rather than NULL) response
+ */
+
+ if (!out->data) {
+ out->data = (uint8_t *)talloc_strdup(out_mem_ctx, "\0");
+ }
+
+ gensec_gssapi_state->sasl_state = STAGE_SASL_SSF_NEG;
+ return NT_STATUS_MORE_PROCESSING_REQUIRED;
+ } else {
+ gensec_gssapi_state->sasl_state = STAGE_DONE;
+
+ if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
+ DEBUG(5, ("GSSAPI Connection will be cryptographicly sealed\n"));
+ } else if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) {
+ DEBUG(5, ("GSSAPI Connection will be cryptographicly signed\n"));
+ } else {
+ DEBUG(5, ("GSSAPI Connection will have no cryptographic protection\n"));
+ }
+
+ return NT_STATUS_OK;
+ }
+ } else if (maj_stat == GSS_S_CONTINUE_NEEDED) {
+ *out = data_blob_talloc(out_mem_ctx, output_token.value, output_token.length);
+ gss_release_buffer(&min_stat2, &output_token);
+
+ return NT_STATUS_MORE_PROCESSING_REQUIRED;
+ } else if (gss_oid_equal(gensec_gssapi_state->gss_oid, gss_mech_krb5)) {
+ switch (min_stat) {
+ case KRB5_KDC_UNREACH:
+ DEBUG(3, ("Cannot reach a KDC we require: %s\n",
+ gssapi_error_string(gensec_gssapi_state, maj_stat, min_stat, gensec_gssapi_state->gss_oid)));
+ return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */
+ case KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN:
+ DEBUG(3, ("Server is not registered with our KDC: %s\n",
+ gssapi_error_string(gensec_gssapi_state, maj_stat, min_stat, gensec_gssapi_state->gss_oid)));
+ return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */
+ case KRB5KRB_AP_ERR_MSG_TYPE:
+ /* garbage input, possibly from the auto-mech detection */
+ return NT_STATUS_INVALID_PARAMETER;
+ default:
+ DEBUG(1, ("GSS Update(krb5)(%d) Update failed: %s\n",
+ gensec_gssapi_state->gss_exchange_count,
+ gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid)));
+ return nt_status;
+ }
+ } else {
+ DEBUG(1, ("GSS Update(%d) failed: %s\n",
+ gensec_gssapi_state->gss_exchange_count,
+ gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid)));
+ return nt_status;
+ }
+ break;
+ }
+
+ /* These last two stages are only done if we were invoked as SASL */
+ case STAGE_SASL_SSF_NEG:
+ {
+ switch (gensec_security->gensec_role) {
+ case GENSEC_CLIENT:
+ {
+ uint8_t maxlength_proposed[4];
+ uint8_t maxlength_accepted[4];
+ uint8_t security_supported;
+ int conf_state;
+ gss_qop_t qop_state;
+ input_token.length = in.length;
+ input_token.value = in.data;
+
+ /* As a client, we have just send a
+ * zero-length blob to the server (after the
+ * normal GSSAPI exchange), and it has replied
+ * with it's SASL negotiation */
+
+ maj_stat = gss_unwrap(&min_stat,
+ gensec_gssapi_state->gssapi_context,
+ &input_token,
+ &output_token,
+ &conf_state,
+ &qop_state);
+ if (GSS_ERROR(maj_stat)) {
+ DEBUG(1, ("gensec_gssapi_update: GSS UnWrap of SASL protection negotiation failed: %s\n",
+ gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid)));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (output_token.length < 4) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ memcpy(maxlength_proposed, output_token.value, 4);
+ gss_release_buffer(&min_stat, &output_token);
+
+ /* first byte is the proposed security */
+ security_supported = maxlength_proposed[0];
+ maxlength_proposed[0] = '\0';
+
+ /* Rest is the proposed max wrap length */
+ gensec_gssapi_state->max_wrap_buf_size = MIN(RIVAL(maxlength_proposed, 0),
+ gensec_gssapi_state->max_wrap_buf_size);
+ gensec_gssapi_state->sasl_protection = 0;
+ if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
+ if (security_supported & NEG_SEAL) {
+ gensec_gssapi_state->sasl_protection |= NEG_SEAL;
+ }
+ } else if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) {
+ if (security_supported & NEG_SIGN) {
+ gensec_gssapi_state->sasl_protection |= NEG_SIGN;
+ }
+ } else if (security_supported & NEG_NONE) {
+ gensec_gssapi_state->sasl_protection |= NEG_NONE;
+ } else {
+ DEBUG(1, ("Remote server does not support unprotected connections"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* Send back the negotiated max length */
+
+ RSIVAL(maxlength_accepted, 0, gensec_gssapi_state->max_wrap_buf_size);
+
+ maxlength_accepted[0] = gensec_gssapi_state->sasl_protection;
+
+ input_token.value = maxlength_accepted;
+ input_token.length = sizeof(maxlength_accepted);
+
+ maj_stat = gss_wrap(&min_stat,
+ gensec_gssapi_state->gssapi_context,
+ false,
+ GSS_C_QOP_DEFAULT,
+ &input_token,
+ &conf_state,
+ &output_token);
+ if (GSS_ERROR(maj_stat)) {
+ DEBUG(1, ("GSS Update(SSF_NEG): GSS Wrap failed: %s\n",
+ gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid)));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ *out = data_blob_talloc(out_mem_ctx, output_token.value, output_token.length);
+ gss_release_buffer(&min_stat, &output_token);
+
+ /* quirk: This changes the value that gensec_have_feature returns, to be that after SASL negotiation */
+ gensec_gssapi_state->sasl_state = STAGE_DONE;
+
+ if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
+ DEBUG(3, ("SASL/GSSAPI Connection to server will be cryptographicly sealed\n"));
+ } else if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) {
+ DEBUG(3, ("SASL/GSSAPI Connection to server will be cryptographicly signed\n"));
+ } else {
+ DEBUG(3, ("SASL/GSSAPI Connection to server will have no cryptographicly protection\n"));
+ }
+
+ return NT_STATUS_OK;
+ }
+ case GENSEC_SERVER:
+ {
+ uint8_t maxlength_proposed[4];
+ uint8_t security_supported = 0x0;
+ int conf_state;
+
+ /* As a server, we have just been sent a zero-length blob (note this, but it isn't fatal) */
+ if (in.length != 0) {
+ DEBUG(1, ("SASL/GSSAPI: client sent non-zero length starting SASL negotiation!\n"));
+ }
+
+ /* Give the client some idea what we will support */
+
+ RSIVAL(maxlength_proposed, 0, gensec_gssapi_state->max_wrap_buf_size);
+ /* first byte is the proposed security */
+ maxlength_proposed[0] = '\0';
+
+ gensec_gssapi_state->sasl_protection = 0;
+ if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
+ security_supported |= NEG_SEAL;
+ }
+ if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) {
+ security_supported |= NEG_SIGN;
+ }
+ if (security_supported == 0) {
+ /* If we don't support anything, this must be 0 */
+ RSIVAL(maxlength_proposed, 0, 0x0);
+ }
+
+ /* TODO: We may not wish to support this */
+ security_supported |= NEG_NONE;
+ maxlength_proposed[0] = security_supported;
+
+ input_token.value = maxlength_proposed;
+ input_token.length = sizeof(maxlength_proposed);
+
+ maj_stat = gss_wrap(&min_stat,
+ gensec_gssapi_state->gssapi_context,
+ false,
+ GSS_C_QOP_DEFAULT,
+ &input_token,
+ &conf_state,
+ &output_token);
+ if (GSS_ERROR(maj_stat)) {
+ DEBUG(1, ("GSS Update(SSF_NEG): GSS Wrap failed: %s\n",
+ gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid)));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ *out = data_blob_talloc(out_mem_ctx, output_token.value, output_token.length);
+ gss_release_buffer(&min_stat, &output_token);
+
+ gensec_gssapi_state->sasl_state = STAGE_SASL_SSF_ACCEPT;
+ return NT_STATUS_MORE_PROCESSING_REQUIRED;
+ }
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+
+ }
+ }
+ /* This is s server-only stage */
+ case STAGE_SASL_SSF_ACCEPT:
+ {
+ uint8_t maxlength_accepted[4];
+ uint8_t security_accepted;
+ int conf_state;
+ gss_qop_t qop_state;
+ input_token.length = in.length;
+ input_token.value = in.data;
+
+ maj_stat = gss_unwrap(&min_stat,
+ gensec_gssapi_state->gssapi_context,
+ &input_token,
+ &output_token,
+ &conf_state,
+ &qop_state);
+ if (GSS_ERROR(maj_stat)) {
+ DEBUG(1, ("gensec_gssapi_update: GSS UnWrap of SASL protection negotiation failed: %s\n",
+ gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid)));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (output_token.length < 4) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ memcpy(maxlength_accepted, output_token.value, 4);
+ gss_release_buffer(&min_stat, &output_token);
+
+ /* first byte is the proposed security */
+ security_accepted = maxlength_accepted[0];
+ maxlength_accepted[0] = '\0';
+
+ /* Rest is the proposed max wrap length */
+ gensec_gssapi_state->max_wrap_buf_size = MIN(RIVAL(maxlength_accepted, 0),
+ gensec_gssapi_state->max_wrap_buf_size);
+
+ gensec_gssapi_state->sasl_protection = 0;
+ if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
+ if (security_accepted & NEG_SEAL) {
+ gensec_gssapi_state->sasl_protection |= NEG_SEAL;
+ }
+ } else if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) {
+ if (security_accepted & NEG_SIGN) {
+ gensec_gssapi_state->sasl_protection |= NEG_SIGN;
+ }
+ } else if (security_accepted & NEG_NONE) {
+ gensec_gssapi_state->sasl_protection |= NEG_NONE;
+ } else {
+ DEBUG(1, ("Remote client does not support unprotected connections, but we failed to negotiate anything better"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* quirk: This changes the value that gensec_have_feature returns, to be that after SASL negotiation */
+ gensec_gssapi_state->sasl_state = STAGE_DONE;
+ if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
+ DEBUG(5, ("SASL/GSSAPI Connection from client will be cryptographicly sealed\n"));
+ } else if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) {
+ DEBUG(5, ("SASL/GSSAPI Connection from client will be cryptographicly signed\n"));
+ } else {
+ DEBUG(5, ("SASL/GSSAPI Connection from client will have no cryptographic protection\n"));
+ }
+
+ *out = data_blob(NULL, 0);
+ return NT_STATUS_OK;
+ }
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+}
+
+static NTSTATUS gensec_gssapi_wrap(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *in,
+ DATA_BLOB *out)
+{
+ struct gensec_gssapi_state *gensec_gssapi_state
+ = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state);
+ OM_uint32 maj_stat, min_stat;
+ gss_buffer_desc input_token, output_token;
+ int conf_state;
+ input_token.length = in->length;
+ input_token.value = in->data;
+
+ maj_stat = gss_wrap(&min_stat,
+ gensec_gssapi_state->gssapi_context,
+ gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL),
+ GSS_C_QOP_DEFAULT,
+ &input_token,
+ &conf_state,
+ &output_token);
+ if (GSS_ERROR(maj_stat)) {
+ DEBUG(1, ("gensec_gssapi_wrap: GSS Wrap failed: %s\n",
+ gssapi_error_string(mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid)));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ *out = data_blob_talloc(mem_ctx, output_token.value, output_token.length);
+ gss_release_buffer(&min_stat, &output_token);
+
+ if (gensec_gssapi_state->sasl) {
+ size_t max_wrapped_size = gensec_gssapi_max_wrapped_size(gensec_security);
+ if (max_wrapped_size < out->length) {
+ DEBUG(1, ("gensec_gssapi_wrap: when wrapped, INPUT data (%u) is grew to be larger than SASL negotiated maximum output size (%u > %u)\n",
+ (unsigned)in->length,
+ (unsigned)out->length,
+ (unsigned int)max_wrapped_size));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ }
+
+ if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)
+ && !conf_state) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS gensec_gssapi_unwrap(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *in,
+ DATA_BLOB *out)
+{
+ struct gensec_gssapi_state *gensec_gssapi_state
+ = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state);
+ OM_uint32 maj_stat, min_stat;
+ gss_buffer_desc input_token, output_token;
+ int conf_state;
+ gss_qop_t qop_state;
+ input_token.length = in->length;
+ input_token.value = in->data;
+
+ if (gensec_gssapi_state->sasl) {
+ size_t max_wrapped_size = gensec_gssapi_max_wrapped_size(gensec_security);
+ if (max_wrapped_size < in->length) {
+ DEBUG(1, ("gensec_gssapi_unwrap: WRAPPED data is larger than SASL negotiated maximum size\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ }
+
+ maj_stat = gss_unwrap(&min_stat,
+ gensec_gssapi_state->gssapi_context,
+ &input_token,
+ &output_token,
+ &conf_state,
+ &qop_state);
+ if (GSS_ERROR(maj_stat)) {
+ DEBUG(1, ("gensec_gssapi_unwrap: GSS UnWrap failed: %s\n",
+ gssapi_error_string(mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid)));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ *out = data_blob_talloc(mem_ctx, output_token.value, output_token.length);
+ gss_release_buffer(&min_stat, &output_token);
+
+ if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)
+ && !conf_state) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ return NT_STATUS_OK;
+}
+
+/* Find out the maximum input size negotiated on this connection */
+
+static size_t gensec_gssapi_max_input_size(struct gensec_security *gensec_security)
+{
+ struct gensec_gssapi_state *gensec_gssapi_state
+ = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state);
+ OM_uint32 maj_stat, min_stat;
+ OM_uint32 max_input_size;
+
+ maj_stat = gss_wrap_size_limit(&min_stat,
+ gensec_gssapi_state->gssapi_context,
+ gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL),
+ GSS_C_QOP_DEFAULT,
+ gensec_gssapi_state->max_wrap_buf_size,
+ &max_input_size);
+ if (GSS_ERROR(maj_stat)) {
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+ DEBUG(1, ("gensec_gssapi_max_input_size: determinaing signature size with gss_wrap_size_limit failed: %s\n",
+ gssapi_error_string(mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid)));
+ talloc_free(mem_ctx);
+ return 0;
+ }
+
+ return max_input_size;
+}
+
+/* Find out the maximum output size negotiated on this connection */
+static size_t gensec_gssapi_max_wrapped_size(struct gensec_security *gensec_security)
+{
+ struct gensec_gssapi_state *gensec_gssapi_state = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state);;
+ return gensec_gssapi_state->max_wrap_buf_size;
+}
+
+static NTSTATUS gensec_gssapi_seal_packet(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ uint8_t *data, size_t length,
+ const uint8_t *whole_pdu, size_t pdu_length,
+ DATA_BLOB *sig)
+{
+ struct gensec_gssapi_state *gensec_gssapi_state
+ = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state);
+ OM_uint32 maj_stat, min_stat;
+ gss_buffer_desc input_token, output_token;
+ int conf_state;
+ ssize_t sig_length;
+
+ input_token.length = length;
+ input_token.value = data;
+
+ maj_stat = gss_wrap(&min_stat,
+ gensec_gssapi_state->gssapi_context,
+ gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL),
+ GSS_C_QOP_DEFAULT,
+ &input_token,
+ &conf_state,
+ &output_token);
+ if (GSS_ERROR(maj_stat)) {
+ DEBUG(1, ("gensec_gssapi_seal_packet: GSS Wrap failed: %s\n",
+ gssapi_error_string(mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid)));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (output_token.length < input_token.length) {
+ DEBUG(1, ("gensec_gssapi_seal_packet: GSS Wrap length [%ld] *less* than caller length [%ld]\n",
+ (long)output_token.length, (long)length));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ sig_length = output_token.length - input_token.length;
+
+ memcpy(data, ((uint8_t *)output_token.value) + sig_length, length);
+ *sig = data_blob_talloc(mem_ctx, (uint8_t *)output_token.value, sig_length);
+
+ dump_data_pw("gensec_gssapi_seal_packet: sig\n", sig->data, sig->length);
+ dump_data_pw("gensec_gssapi_seal_packet: clear\n", data, length);
+ dump_data_pw("gensec_gssapi_seal_packet: sealed\n", ((uint8_t *)output_token.value) + sig_length, output_token.length - sig_length);
+
+ gss_release_buffer(&min_stat, &output_token);
+
+ if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)
+ && !conf_state) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS gensec_gssapi_unseal_packet(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ uint8_t *data, size_t length,
+ const uint8_t *whole_pdu, size_t pdu_length,
+ const DATA_BLOB *sig)
+{
+ struct gensec_gssapi_state *gensec_gssapi_state
+ = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state);
+ OM_uint32 maj_stat, min_stat;
+ gss_buffer_desc input_token, output_token;
+ int conf_state;
+ gss_qop_t qop_state;
+ DATA_BLOB in;
+
+ dump_data_pw("gensec_gssapi_unseal_packet: sig\n", sig->data, sig->length);
+
+ in = data_blob_talloc(mem_ctx, NULL, sig->length + length);
+
+ memcpy(in.data, sig->data, sig->length);
+ memcpy(in.data + sig->length, data, length);
+
+ input_token.length = in.length;
+ input_token.value = in.data;
+
+ maj_stat = gss_unwrap(&min_stat,
+ gensec_gssapi_state->gssapi_context,
+ &input_token,
+ &output_token,
+ &conf_state,
+ &qop_state);
+ if (GSS_ERROR(maj_stat)) {
+ DEBUG(1, ("gensec_gssapi_unseal_packet: GSS UnWrap failed: %s\n",
+ gssapi_error_string(mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid)));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (output_token.length != length) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ memcpy(data, output_token.value, length);
+
+ gss_release_buffer(&min_stat, &output_token);
+
+ if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)
+ && !conf_state) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS gensec_gssapi_sign_packet(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ const uint8_t *data, size_t length,
+ const uint8_t *whole_pdu, size_t pdu_length,
+ DATA_BLOB *sig)
+{
+ struct gensec_gssapi_state *gensec_gssapi_state
+ = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state);
+ OM_uint32 maj_stat, min_stat;
+ gss_buffer_desc input_token, output_token;
+
+ if (gensec_security->want_features & GENSEC_FEATURE_SIGN_PKT_HEADER) {
+ input_token.length = pdu_length;
+ input_token.value = discard_const_p(uint8_t *, whole_pdu);
+ } else {
+ input_token.length = length;
+ input_token.value = discard_const_p(uint8_t *, data);
+ }
+
+ maj_stat = gss_get_mic(&min_stat,
+ gensec_gssapi_state->gssapi_context,
+ GSS_C_QOP_DEFAULT,
+ &input_token,
+ &output_token);
+ if (GSS_ERROR(maj_stat)) {
+ DEBUG(1, ("GSS GetMic failed: %s\n",
+ gssapi_error_string(mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid)));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ *sig = data_blob_talloc(mem_ctx, (uint8_t *)output_token.value, output_token.length);
+
+ dump_data_pw("gensec_gssapi_seal_packet: sig\n", sig->data, sig->length);
+
+ gss_release_buffer(&min_stat, &output_token);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS gensec_gssapi_check_packet(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ const uint8_t *data, size_t length,
+ const uint8_t *whole_pdu, size_t pdu_length,
+ const DATA_BLOB *sig)
+{
+ struct gensec_gssapi_state *gensec_gssapi_state
+ = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state);
+ OM_uint32 maj_stat, min_stat;
+ gss_buffer_desc input_token;
+ gss_buffer_desc input_message;
+ gss_qop_t qop_state;
+
+ dump_data_pw("gensec_gssapi_seal_packet: sig\n", sig->data, sig->length);
+
+ if (gensec_security->want_features & GENSEC_FEATURE_SIGN_PKT_HEADER) {
+ input_message.length = pdu_length;
+ input_message.value = discard_const(whole_pdu);
+ } else {
+ input_message.length = length;
+ input_message.value = discard_const(data);
+ }
+
+ input_token.length = sig->length;
+ input_token.value = sig->data;
+
+ maj_stat = gss_verify_mic(&min_stat,
+ gensec_gssapi_state->gssapi_context,
+ &input_message,
+ &input_token,
+ &qop_state);
+ if (GSS_ERROR(maj_stat)) {
+ DEBUG(1, ("GSS VerifyMic failed: %s\n",
+ gssapi_error_string(mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid)));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/* Try to figure out what features we actually got on the connection */
+static bool gensec_gssapi_have_feature(struct gensec_security *gensec_security,
+ uint32_t feature)
+{
+ struct gensec_gssapi_state *gensec_gssapi_state
+ = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state);
+ if (feature & GENSEC_FEATURE_SIGN) {
+ /* If we are going GSSAPI SASL, then we honour the second negotiation */
+ if (gensec_gssapi_state->sasl
+ && gensec_gssapi_state->sasl_state == STAGE_DONE) {
+ return ((gensec_gssapi_state->sasl_protection & NEG_SIGN)
+ && (gensec_gssapi_state->got_flags & GSS_C_INTEG_FLAG));
+ }
+ return gensec_gssapi_state->got_flags & GSS_C_INTEG_FLAG;
+ }
+ if (feature & GENSEC_FEATURE_SEAL) {
+ /* If we are going GSSAPI SASL, then we honour the second negotiation */
+ if (gensec_gssapi_state->sasl
+ && gensec_gssapi_state->sasl_state == STAGE_DONE) {
+ return ((gensec_gssapi_state->sasl_protection & NEG_SEAL)
+ && (gensec_gssapi_state->got_flags & GSS_C_CONF_FLAG));
+ }
+ return gensec_gssapi_state->got_flags & GSS_C_CONF_FLAG;
+ }
+ if (feature & GENSEC_FEATURE_SESSION_KEY) {
+ /* Only for GSSAPI/Krb5 */
+ if (gss_oid_equal(gensec_gssapi_state->gss_oid, gss_mech_krb5)) {
+ return true;
+ }
+ }
+ if (feature & GENSEC_FEATURE_DCE_STYLE) {
+ return gensec_gssapi_state->got_flags & GSS_C_DCE_STYLE;
+ }
+ if (feature & GENSEC_FEATURE_NEW_SPNEGO) {
+ NTSTATUS status;
+
+ if (!(gensec_gssapi_state->got_flags & GSS_C_INTEG_FLAG)) {
+ return false;
+ }
+
+ if (gensec_setting_bool(gensec_security->settings, "gensec_gssapi", "force_new_spnego", false)) {
+ return true;
+ }
+ if (gensec_setting_bool(gensec_security->settings, "gensec_gssapi", "disable_new_spnego", false)) {
+ return false;
+ }
+
+ status = gensec_gssapi_init_lucid(gensec_gssapi_state);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ if (gensec_gssapi_state->lucid->protocol == 1) {
+ return true;
+ }
+
+ return false;
+ }
+ /* We can always do async (rather than strict request/reply) packets. */
+ if (feature & GENSEC_FEATURE_ASYNC_REPLIES) {
+ return true;
+ }
+ return false;
+}
+
+/*
+ * Extract the 'sesssion key' needed by SMB signing and ncacn_np
+ * (for encrypting some passwords).
+ *
+ * This breaks all the abstractions, but what do you expect...
+ */
+static NTSTATUS gensec_gssapi_session_key(struct gensec_security *gensec_security,
+ DATA_BLOB *session_key)
+{
+ struct gensec_gssapi_state *gensec_gssapi_state
+ = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state);
+ OM_uint32 maj_stat, min_stat;
+ krb5_keyblock *subkey;
+
+ if (gensec_gssapi_state->sasl_state != STAGE_DONE) {
+ return NT_STATUS_NO_USER_SESSION_KEY;
+ }
+
+ if (gensec_gssapi_state->session_key.data) {
+ *session_key = gensec_gssapi_state->session_key;
+ return NT_STATUS_OK;
+ }
+
+ maj_stat = gsskrb5_get_subkey(&min_stat,
+ gensec_gssapi_state->gssapi_context,
+ &subkey);
+ if (maj_stat != 0) {
+ DEBUG(1, ("NO session key for this mech\n"));
+ return NT_STATUS_NO_USER_SESSION_KEY;
+ }
+
+ DEBUG(10, ("Got KRB5 session key of length %d%s\n",
+ (int)KRB5_KEY_LENGTH(subkey),
+ (gensec_gssapi_state->sasl_state == STAGE_DONE)?" (done)":""));
+ *session_key = data_blob_talloc(gensec_gssapi_state,
+ KRB5_KEY_DATA(subkey), KRB5_KEY_LENGTH(subkey));
+ krb5_free_keyblock(gensec_gssapi_state->smb_krb5_context->krb5_context, subkey);
+ gensec_gssapi_state->session_key = *session_key;
+ dump_data_pw("KRB5 Session Key:\n", session_key->data, session_key->length);
+
+ return NT_STATUS_OK;
+}
+
+/* Get some basic (and authorization) information about the user on
+ * this session. This uses either the PAC (if present) or a local
+ * database lookup */
+static NTSTATUS gensec_gssapi_session_info(struct gensec_security *gensec_security,
+ struct auth_session_info **_session_info)
+{
+ NTSTATUS nt_status;
+ TALLOC_CTX *mem_ctx;
+ struct gensec_gssapi_state *gensec_gssapi_state
+ = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state);
+ struct auth_serversupplied_info *server_info = NULL;
+ struct auth_session_info *session_info = NULL;
+ OM_uint32 maj_stat, min_stat;
+ gss_buffer_desc pac;
+ DATA_BLOB pac_blob;
+
+ if ((gensec_gssapi_state->gss_oid->length != gss_mech_krb5->length)
+ || (memcmp(gensec_gssapi_state->gss_oid->elements, gss_mech_krb5->elements,
+ gensec_gssapi_state->gss_oid->length) != 0)) {
+ DEBUG(1, ("NO session info available for this mech\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ mem_ctx = talloc_named(gensec_gssapi_state, 0, "gensec_gssapi_session_info context");
+ NT_STATUS_HAVE_NO_MEMORY(mem_ctx);
+
+ maj_stat = gsskrb5_extract_authz_data_from_sec_context(&min_stat,
+ gensec_gssapi_state->gssapi_context,
+ KRB5_AUTHDATA_WIN2K_PAC,
+ &pac);
+
+
+ if (maj_stat == 0) {
+ pac_blob = data_blob_talloc(mem_ctx, pac.value, pac.length);
+ gss_release_buffer(&min_stat, &pac);
+
+ } else {
+ pac_blob = data_blob(NULL, 0);
+ }
+
+ /* IF we have the PAC - otherwise we need to get this
+ * data from elsewere - local ldb, or (TODO) lookup of some
+ * kind...
+ */
+ if (pac_blob.length) {
+ nt_status = kerberos_pac_blob_to_server_info(mem_ctx,
+ gensec_security->settings->iconv_convenience,
+ pac_blob,
+ gensec_gssapi_state->smb_krb5_context->krb5_context,
+ &server_info);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(mem_ctx);
+ return nt_status;
+ }
+ } else {
+ gss_buffer_desc name_token;
+ char *principal_string;
+
+ maj_stat = gss_display_name (&min_stat,
+ gensec_gssapi_state->client_name,
+ &name_token,
+ NULL);
+ if (GSS_ERROR(maj_stat)) {
+ DEBUG(1, ("GSS display_name failed: %s\n",
+ gssapi_error_string(mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid)));
+ talloc_free(mem_ctx);
+ return NT_STATUS_FOOBAR;
+ }
+
+ principal_string = talloc_strndup(mem_ctx,
+ (const char *)name_token.value,
+ name_token.length);
+
+ gss_release_buffer(&min_stat, &name_token);
+
+ if (!principal_string) {
+ talloc_free(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (gensec_security->auth_context &&
+ !gensec_setting_bool(gensec_security->settings, "gensec", "require_pac", false)) {
+ DEBUG(1, ("Unable to find PAC, resorting to local user lookup: %s\n",
+ gssapi_error_string(mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid)));
+ nt_status = gensec_security->auth_context->get_server_info_principal(mem_ctx,
+ gensec_security->auth_context,
+ principal_string,
+ &server_info);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(mem_ctx);
+ return nt_status;
+ }
+ } else {
+ DEBUG(1, ("Unable to find PAC in ticket from %s, failing to allow access: %s\n",
+ principal_string,
+ gssapi_error_string(mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid)));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ }
+
+ /* references the server_info into the session_info */
+ nt_status = auth_generate_session_info(mem_ctx, gensec_security->event_ctx,
+ gensec_security->settings->lp_ctx, server_info, &session_info);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(mem_ctx);
+ return nt_status;
+ }
+
+ nt_status = gensec_gssapi_session_key(gensec_security, &session_info->session_key);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(mem_ctx);
+ return nt_status;
+ }
+
+ if (!(gensec_gssapi_state->got_flags & GSS_C_DELEG_FLAG)) {
+ DEBUG(10, ("gensec_gssapi: NO delegated credentials supplied by client\n"));
+ } else {
+ krb5_error_code ret;
+ DEBUG(10, ("gensec_gssapi: delegated credentials supplied by client\n"));
+ session_info->credentials = cli_credentials_init(session_info);
+ if (!session_info->credentials) {
+ talloc_free(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ cli_credentials_set_conf(session_info->credentials, gensec_security->settings->lp_ctx);
+ /* Just so we don't segfault trying to get at a username */
+ cli_credentials_set_anonymous(session_info->credentials);
+
+ ret = cli_credentials_set_client_gss_creds(session_info->credentials,
+ gensec_security->event_ctx,
+ gensec_security->settings->lp_ctx,
+ gensec_gssapi_state->delegated_cred_handle,
+ CRED_SPECIFIED);
+ if (ret) {
+ talloc_free(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* This credential handle isn't useful for password authentication, so ensure nobody tries to do that */
+ cli_credentials_set_kerberos_state(session_info->credentials, CRED_MUST_USE_KERBEROS);
+
+ /* It has been taken from this place... */
+ gensec_gssapi_state->delegated_cred_handle = GSS_C_NO_CREDENTIAL;
+ }
+ talloc_steal(gensec_gssapi_state, session_info);
+ talloc_free(mem_ctx);
+ *_session_info = session_info;
+
+ return NT_STATUS_OK;
+}
+
+static size_t gensec_gssapi_sig_size(struct gensec_security *gensec_security, size_t data_size)
+{
+ struct gensec_gssapi_state *gensec_gssapi_state
+ = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state);
+ NTSTATUS status;
+
+ if (gensec_gssapi_state->sig_size) {
+ return gensec_gssapi_state->sig_size;
+ }
+
+ if (gensec_gssapi_state->got_flags & GSS_C_CONF_FLAG) {
+ gensec_gssapi_state->sig_size = 45;
+ } else {
+ gensec_gssapi_state->sig_size = 37;
+ }
+
+ status = gensec_gssapi_init_lucid(gensec_gssapi_state);
+ if (!NT_STATUS_IS_OK(status)) {
+ return gensec_gssapi_state->sig_size;
+ }
+
+ if (gensec_gssapi_state->lucid->protocol == 1) {
+ if (gensec_gssapi_state->got_flags & GSS_C_CONF_FLAG) {
+ /*
+ * TODO: windows uses 76 here, but we don't know
+ * gss_wrap works with aes keys yet
+ */
+ gensec_gssapi_state->sig_size = 76;
+ } else {
+ gensec_gssapi_state->sig_size = 28;
+ }
+ } else if (gensec_gssapi_state->lucid->protocol == 0) {
+ switch (gensec_gssapi_state->lucid->rfc1964_kd.ctx_key.type) {
+ case KEYTYPE_DES:
+ case KEYTYPE_ARCFOUR:
+ case KEYTYPE_ARCFOUR_56:
+ if (gensec_gssapi_state->got_flags & GSS_C_CONF_FLAG) {
+ gensec_gssapi_state->sig_size = 45;
+ } else {
+ gensec_gssapi_state->sig_size = 37;
+ }
+ break;
+ case KEYTYPE_DES3:
+ if (gensec_gssapi_state->got_flags & GSS_C_CONF_FLAG) {
+ gensec_gssapi_state->sig_size = 57;
+ } else {
+ gensec_gssapi_state->sig_size = 49;
+ }
+ break;
+ }
+ }
+
+ return gensec_gssapi_state->sig_size;
+}
+
+static const char *gensec_gssapi_krb5_oids[] = {
+ GENSEC_OID_KERBEROS5_OLD,
+ GENSEC_OID_KERBEROS5,
+ NULL
+};
+
+static const char *gensec_gssapi_spnego_oids[] = {
+ GENSEC_OID_SPNEGO,
+ NULL
+};
+
+/* As a server, this could in theory accept any GSSAPI mech */
+static const struct gensec_security_ops gensec_gssapi_spnego_security_ops = {
+ .name = "gssapi_spnego",
+ .sasl_name = "GSS-SPNEGO",
+ .auth_type = DCERPC_AUTH_TYPE_SPNEGO,
+ .oid = gensec_gssapi_spnego_oids,
+ .client_start = gensec_gssapi_client_start,
+ .server_start = gensec_gssapi_server_start,
+ .magic = gensec_gssapi_magic,
+ .update = gensec_gssapi_update,
+ .session_key = gensec_gssapi_session_key,
+ .session_info = gensec_gssapi_session_info,
+ .sign_packet = gensec_gssapi_sign_packet,
+ .check_packet = gensec_gssapi_check_packet,
+ .seal_packet = gensec_gssapi_seal_packet,
+ .unseal_packet = gensec_gssapi_unseal_packet,
+ .wrap = gensec_gssapi_wrap,
+ .unwrap = gensec_gssapi_unwrap,
+ .have_feature = gensec_gssapi_have_feature,
+ .enabled = false,
+ .kerberos = true,
+ .priority = GENSEC_GSSAPI
+};
+
+/* As a server, this could in theory accept any GSSAPI mech */
+static const struct gensec_security_ops gensec_gssapi_krb5_security_ops = {
+ .name = "gssapi_krb5",
+ .auth_type = DCERPC_AUTH_TYPE_KRB5,
+ .oid = gensec_gssapi_krb5_oids,
+ .client_start = gensec_gssapi_client_start,
+ .server_start = gensec_gssapi_server_start,
+ .magic = gensec_gssapi_magic,
+ .update = gensec_gssapi_update,
+ .session_key = gensec_gssapi_session_key,
+ .session_info = gensec_gssapi_session_info,
+ .sig_size = gensec_gssapi_sig_size,
+ .sign_packet = gensec_gssapi_sign_packet,
+ .check_packet = gensec_gssapi_check_packet,
+ .seal_packet = gensec_gssapi_seal_packet,
+ .unseal_packet = gensec_gssapi_unseal_packet,
+ .wrap = gensec_gssapi_wrap,
+ .unwrap = gensec_gssapi_unwrap,
+ .have_feature = gensec_gssapi_have_feature,
+ .enabled = true,
+ .kerberos = true,
+ .priority = GENSEC_GSSAPI
+};
+
+/* As a server, this could in theory accept any GSSAPI mech */
+static const struct gensec_security_ops gensec_gssapi_sasl_krb5_security_ops = {
+ .name = "gssapi_krb5_sasl",
+ .sasl_name = "GSSAPI",
+ .client_start = gensec_gssapi_sasl_client_start,
+ .server_start = gensec_gssapi_sasl_server_start,
+ .update = gensec_gssapi_update,
+ .session_key = gensec_gssapi_session_key,
+ .session_info = gensec_gssapi_session_info,
+ .max_input_size = gensec_gssapi_max_input_size,
+ .max_wrapped_size = gensec_gssapi_max_wrapped_size,
+ .wrap = gensec_gssapi_wrap,
+ .unwrap = gensec_gssapi_unwrap,
+ .have_feature = gensec_gssapi_have_feature,
+ .enabled = true,
+ .kerberos = true,
+ .priority = GENSEC_GSSAPI
+};
+
+_PUBLIC_ NTSTATUS gensec_gssapi_init(void)
+{
+ NTSTATUS ret;
+
+ ret = gensec_register(&gensec_gssapi_spnego_security_ops);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("Failed to register '%s' gensec backend!\n",
+ gensec_gssapi_spnego_security_ops.name));
+ return ret;
+ }
+
+ ret = gensec_register(&gensec_gssapi_krb5_security_ops);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("Failed to register '%s' gensec backend!\n",
+ gensec_gssapi_krb5_security_ops.name));
+ return ret;
+ }
+
+ ret = gensec_register(&gensec_gssapi_sasl_krb5_security_ops);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("Failed to register '%s' gensec backend!\n",
+ gensec_gssapi_sasl_krb5_security_ops.name));
+ return ret;
+ }
+
+ return ret;
+}
diff --git a/source4/auth/gensec/gensec_gssapi.h b/source4/auth/gensec/gensec_gssapi.h
new file mode 100644
index 0000000000..b55b4391e0
--- /dev/null
+++ b/source4/auth/gensec/gensec_gssapi.h
@@ -0,0 +1,68 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Kerberos backend for GENSEC
+
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
+ Copyright (C) Stefan Metzmacher <metze@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+/* This structure described here, so the RPC-PAC test can get at the PAC provided */
+
+enum gensec_gssapi_sasl_state
+{
+ STAGE_GSS_NEG,
+ STAGE_SASL_SSF_NEG,
+ STAGE_SASL_SSF_ACCEPT,
+ STAGE_DONE
+};
+
+#define NEG_SEAL 0x4
+#define NEG_SIGN 0x2
+#define NEG_NONE 0x1
+
+struct gensec_gssapi_state {
+ gss_ctx_id_t gssapi_context;
+ struct gss_channel_bindings_struct *input_chan_bindings;
+ gss_name_t server_name;
+ gss_name_t client_name;
+ OM_uint32 want_flags, got_flags;
+ gss_OID gss_oid;
+
+ DATA_BLOB session_key;
+ DATA_BLOB pac;
+
+ struct smb_krb5_context *smb_krb5_context;
+ struct gssapi_creds_container *client_cred;
+ struct gssapi_creds_container *server_cred;
+ gss_krb5_lucid_context_v1_t *lucid;
+
+ gss_cred_id_t delegated_cred_handle;
+
+ bool sasl; /* We have two different mechs in this file: One
+ * for SASL wrapped GSSAPI and another for normal
+ * GSSAPI */
+ enum gensec_gssapi_sasl_state sasl_state;
+ uint8_t sasl_protection; /* What was negotiated at the SASL
+ * layer, independent of the GSSAPI
+ * layer... */
+
+ size_t max_wrap_buf_size;
+ int gss_exchange_count;
+ size_t sig_size;
+};
+
diff --git a/source4/auth/gensec/gensec_krb5.c b/source4/auth/gensec/gensec_krb5.c
new file mode 100644
index 0000000000..b04abfc16c
--- /dev/null
+++ b/source4/auth/gensec/gensec_krb5.c
@@ -0,0 +1,826 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Kerberos backend for GENSEC
+
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Luke Howard 2002-2003
+ Copyright (C) Stefan Metzmacher 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/kerberos.h"
+#include "auth/kerberos/kerberos.h"
+#include "librpc/gen_ndr/krb5pac.h"
+#include "auth/auth.h"
+#include "lib/ldb/include/ldb.h"
+#include "auth/auth_sam.h"
+#include "lib/socket/socket.h"
+#include "librpc/rpc/dcerpc.h"
+#include "auth/credentials/credentials.h"
+#include "auth/credentials/credentials_krb5.h"
+#include "auth/gensec/gensec.h"
+#include "auth/gensec/gensec_proto.h"
+#include "param/param.h"
+#include "auth/session_proto.h"
+#include "auth/auth_sam_reply.h"
+
+enum GENSEC_KRB5_STATE {
+ GENSEC_KRB5_SERVER_START,
+ GENSEC_KRB5_CLIENT_START,
+ GENSEC_KRB5_CLIENT_MUTUAL_AUTH,
+ GENSEC_KRB5_DONE
+};
+
+struct gensec_krb5_state {
+ DATA_BLOB session_key;
+ DATA_BLOB pac;
+ enum GENSEC_KRB5_STATE state_position;
+ struct smb_krb5_context *smb_krb5_context;
+ krb5_auth_context auth_context;
+ krb5_data enc_ticket;
+ krb5_keyblock *keyblock;
+ krb5_ticket *ticket;
+ bool gssapi;
+};
+
+static int gensec_krb5_destroy(struct gensec_krb5_state *gensec_krb5_state)
+{
+ if (!gensec_krb5_state->smb_krb5_context) {
+ /* We can't clean anything else up unless we started up this far */
+ return 0;
+ }
+ if (gensec_krb5_state->enc_ticket.length) {
+ kerberos_free_data_contents(gensec_krb5_state->smb_krb5_context->krb5_context,
+ &gensec_krb5_state->enc_ticket);
+ }
+
+ if (gensec_krb5_state->ticket) {
+ krb5_free_ticket(gensec_krb5_state->smb_krb5_context->krb5_context,
+ gensec_krb5_state->ticket);
+ }
+
+ /* ccache freed in a child destructor */
+
+ krb5_free_keyblock(gensec_krb5_state->smb_krb5_context->krb5_context,
+ gensec_krb5_state->keyblock);
+
+ if (gensec_krb5_state->auth_context) {
+ krb5_auth_con_free(gensec_krb5_state->smb_krb5_context->krb5_context,
+ gensec_krb5_state->auth_context);
+ }
+
+ return 0;
+}
+
+static NTSTATUS gensec_krb5_start(struct gensec_security *gensec_security)
+{
+ krb5_error_code ret;
+ struct gensec_krb5_state *gensec_krb5_state;
+ struct cli_credentials *creds;
+ const struct socket_address *my_addr, *peer_addr;
+ krb5_address my_krb5_addr, peer_krb5_addr;
+
+ creds = gensec_get_credentials(gensec_security);
+ if (!creds) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ gensec_krb5_state = talloc(gensec_security, struct gensec_krb5_state);
+ if (!gensec_krb5_state) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ gensec_security->private_data = gensec_krb5_state;
+ gensec_krb5_state->smb_krb5_context = NULL;
+ gensec_krb5_state->auth_context = NULL;
+ gensec_krb5_state->ticket = NULL;
+ ZERO_STRUCT(gensec_krb5_state->enc_ticket);
+ gensec_krb5_state->keyblock = NULL;
+ gensec_krb5_state->session_key = data_blob(NULL, 0);
+ gensec_krb5_state->pac = data_blob(NULL, 0);
+ gensec_krb5_state->gssapi = false;
+
+ talloc_set_destructor(gensec_krb5_state, gensec_krb5_destroy);
+
+ if (cli_credentials_get_krb5_context(creds,
+ gensec_security->event_ctx,
+ gensec_security->settings->lp_ctx, &gensec_krb5_state->smb_krb5_context)) {
+ talloc_free(gensec_krb5_state);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ ret = krb5_auth_con_init(gensec_krb5_state->smb_krb5_context->krb5_context, &gensec_krb5_state->auth_context);
+ if (ret) {
+ DEBUG(1,("gensec_krb5_start: krb5_auth_con_init failed (%s)\n",
+ smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context,
+ ret, gensec_krb5_state)));
+ talloc_free(gensec_krb5_state);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ ret = krb5_auth_con_setflags(gensec_krb5_state->smb_krb5_context->krb5_context,
+ gensec_krb5_state->auth_context,
+ KRB5_AUTH_CONTEXT_DO_SEQUENCE);
+ if (ret) {
+ DEBUG(1,("gensec_krb5_start: krb5_auth_con_setflags failed (%s)\n",
+ smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context,
+ ret, gensec_krb5_state)));
+ talloc_free(gensec_krb5_state);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ my_addr = gensec_get_my_addr(gensec_security);
+ if (my_addr && my_addr->sockaddr) {
+ ret = krb5_sockaddr2address(gensec_krb5_state->smb_krb5_context->krb5_context,
+ my_addr->sockaddr, &my_krb5_addr);
+ if (ret) {
+ DEBUG(1,("gensec_krb5_start: krb5_sockaddr2address (local) failed (%s)\n",
+ smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context,
+ ret, gensec_krb5_state)));
+ talloc_free(gensec_krb5_state);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ }
+
+ peer_addr = gensec_get_peer_addr(gensec_security);
+ if (peer_addr && peer_addr->sockaddr) {
+ ret = krb5_sockaddr2address(gensec_krb5_state->smb_krb5_context->krb5_context,
+ peer_addr->sockaddr, &peer_krb5_addr);
+ if (ret) {
+ DEBUG(1,("gensec_krb5_start: krb5_sockaddr2address (local) failed (%s)\n",
+ smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context,
+ ret, gensec_krb5_state)));
+ talloc_free(gensec_krb5_state);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ }
+
+ ret = krb5_auth_con_setaddrs(gensec_krb5_state->smb_krb5_context->krb5_context,
+ gensec_krb5_state->auth_context,
+ my_addr ? &my_krb5_addr : NULL,
+ peer_addr ? &peer_krb5_addr : NULL);
+ if (ret) {
+ DEBUG(1,("gensec_krb5_start: krb5_auth_con_setaddrs failed (%s)\n",
+ smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context,
+ ret, gensec_krb5_state)));
+ talloc_free(gensec_krb5_state);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS gensec_krb5_server_start(struct gensec_security *gensec_security)
+{
+ NTSTATUS nt_status;
+ struct gensec_krb5_state *gensec_krb5_state;
+
+ nt_status = gensec_krb5_start(gensec_security);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
+ gensec_krb5_state->state_position = GENSEC_KRB5_SERVER_START;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS gensec_fake_gssapi_krb5_server_start(struct gensec_security *gensec_security)
+{
+ NTSTATUS nt_status = gensec_krb5_server_start(gensec_security);
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ struct gensec_krb5_state *gensec_krb5_state;
+ gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
+ gensec_krb5_state->gssapi = true;
+ }
+ return nt_status;
+}
+
+static NTSTATUS gensec_krb5_client_start(struct gensec_security *gensec_security)
+{
+ struct gensec_krb5_state *gensec_krb5_state;
+ krb5_error_code ret;
+ NTSTATUS nt_status;
+ struct ccache_container *ccache_container;
+ const char *hostname;
+ krb5_flags ap_req_options = AP_OPTS_USE_SUBKEY | AP_OPTS_MUTUAL_REQUIRED;
+
+ const char *principal;
+ krb5_data in_data;
+
+ hostname = gensec_get_target_hostname(gensec_security);
+ if (!hostname) {
+ DEBUG(1, ("Could not determine hostname for target computer, cannot use kerberos\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ if (is_ipaddress(hostname)) {
+ DEBUG(2, ("Cannot do krb5 to an IP address"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ if (strcmp(hostname, "localhost") == 0) {
+ DEBUG(2, ("krb5 to 'localhost' does not make sense"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ nt_status = gensec_krb5_start(gensec_security);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
+ gensec_krb5_state->state_position = GENSEC_KRB5_CLIENT_START;
+
+ principal = gensec_get_target_principal(gensec_security);
+
+ ret = cli_credentials_get_ccache(gensec_get_credentials(gensec_security),
+ gensec_security->event_ctx,
+ gensec_security->settings->lp_ctx, &ccache_container);
+ switch (ret) {
+ case 0:
+ break;
+ case KRB5KDC_ERR_PREAUTH_FAILED:
+ return NT_STATUS_LOGON_FAILURE;
+ case KRB5_KDC_UNREACH:
+ DEBUG(3, ("Cannot reach a KDC we require to contact %s\n", principal));
+ return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */
+ default:
+ DEBUG(1, ("gensec_krb5_start: Aquiring initiator credentials failed: %s\n", error_message(ret)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ in_data.length = 0;
+
+ if (principal && lp_client_use_spnego_principal(gensec_security->settings->lp_ctx)) {
+ krb5_principal target_principal;
+ ret = krb5_parse_name(gensec_krb5_state->smb_krb5_context->krb5_context, principal,
+ &target_principal);
+ if (ret == 0) {
+ ret = krb5_mk_req_exact(gensec_krb5_state->smb_krb5_context->krb5_context,
+ &gensec_krb5_state->auth_context,
+ ap_req_options,
+ target_principal,
+ &in_data, ccache_container->ccache,
+ &gensec_krb5_state->enc_ticket);
+ krb5_free_principal(gensec_krb5_state->smb_krb5_context->krb5_context,
+ target_principal);
+ }
+ } else {
+ ret = krb5_mk_req(gensec_krb5_state->smb_krb5_context->krb5_context,
+ &gensec_krb5_state->auth_context,
+ ap_req_options,
+ gensec_get_target_service(gensec_security),
+ hostname,
+ &in_data, ccache_container->ccache,
+ &gensec_krb5_state->enc_ticket);
+ }
+ switch (ret) {
+ case 0:
+ return NT_STATUS_OK;
+ case KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN:
+ DEBUG(3, ("Server [%s] is not registered with our KDC: %s\n",
+ hostname, smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state)));
+ return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */
+ case KRB5_KDC_UNREACH:
+ DEBUG(3, ("Cannot reach a KDC we require to contact host [%s]: %s\n",
+ hostname, smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state)));
+ return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */
+ case KRB5KDC_ERR_PREAUTH_FAILED:
+ case KRB5KRB_AP_ERR_TKT_EXPIRED:
+ case KRB5_CC_END:
+ /* Too much clock skew - we will need to kinit to re-skew the clock */
+ case KRB5KRB_AP_ERR_SKEW:
+ case KRB5_KDCREP_SKEW:
+ {
+ DEBUG(3, ("kerberos (mk_req) failed: %s\n",
+ smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state)));
+ /*fall through*/
+ }
+
+ /* just don't print a message for these really ordinary messages */
+ case KRB5_FCC_NOFILE:
+ case KRB5_CC_NOTFOUND:
+ case ENOENT:
+
+ return NT_STATUS_UNSUCCESSFUL;
+ break;
+
+ default:
+ DEBUG(0, ("kerberos: %s\n",
+ smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+}
+
+static NTSTATUS gensec_fake_gssapi_krb5_client_start(struct gensec_security *gensec_security)
+{
+ NTSTATUS nt_status = gensec_krb5_client_start(gensec_security);
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ struct gensec_krb5_state *gensec_krb5_state;
+ gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
+ gensec_krb5_state->gssapi = true;
+ }
+ return nt_status;
+}
+
+/**
+ * Check if the packet is one for this mechansim
+ *
+ * @param gensec_security GENSEC state
+ * @param in The request, as a DATA_BLOB
+ * @return Error, INVALID_PARAMETER if it's not a packet for us
+ * or NT_STATUS_OK if the packet is ok.
+ */
+
+static NTSTATUS gensec_fake_gssapi_krb5_magic(struct gensec_security *gensec_security,
+ const DATA_BLOB *in)
+{
+ if (gensec_gssapi_check_oid(in, GENSEC_OID_KERBEROS5)) {
+ return NT_STATUS_OK;
+ } else {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+}
+
+
+/**
+ * Next state function for the Krb5 GENSEC mechanism
+ *
+ * @param gensec_krb5_state KRB5 State
+ * @param out_mem_ctx The TALLOC_CTX for *out to be allocated on
+ * @param in The request, as a DATA_BLOB
+ * @param out The reply, as an talloc()ed DATA_BLOB, on *out_mem_ctx
+ * @return Error, MORE_PROCESSING_REQUIRED if a reply is sent,
+ * or NT_STATUS_OK if the user is authenticated.
+ */
+
+static NTSTATUS gensec_krb5_update(struct gensec_security *gensec_security,
+ TALLOC_CTX *out_mem_ctx,
+ const DATA_BLOB in, DATA_BLOB *out)
+{
+ struct gensec_krb5_state *gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
+ krb5_error_code ret = 0;
+ NTSTATUS nt_status;
+
+ switch (gensec_krb5_state->state_position) {
+ case GENSEC_KRB5_CLIENT_START:
+ {
+ DATA_BLOB unwrapped_out;
+
+ if (gensec_krb5_state->gssapi) {
+ unwrapped_out = data_blob_talloc(out_mem_ctx, gensec_krb5_state->enc_ticket.data, gensec_krb5_state->enc_ticket.length);
+
+ /* wrap that up in a nice GSS-API wrapping */
+ *out = gensec_gssapi_gen_krb5_wrap(out_mem_ctx, &unwrapped_out, TOK_ID_KRB_AP_REQ);
+ } else {
+ *out = data_blob_talloc(out_mem_ctx, gensec_krb5_state->enc_ticket.data, gensec_krb5_state->enc_ticket.length);
+ }
+ gensec_krb5_state->state_position = GENSEC_KRB5_CLIENT_MUTUAL_AUTH;
+ nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
+ return nt_status;
+ }
+
+ case GENSEC_KRB5_CLIENT_MUTUAL_AUTH:
+ {
+ DATA_BLOB unwrapped_in;
+ krb5_data inbuf;
+ krb5_ap_rep_enc_part *repl = NULL;
+ uint8_t tok_id[2];
+
+ if (gensec_krb5_state->gssapi) {
+ if (!gensec_gssapi_parse_krb5_wrap(out_mem_ctx, &in, &unwrapped_in, tok_id)) {
+ DEBUG(1,("gensec_gssapi_parse_krb5_wrap(mutual authentication) failed to parse\n"));
+ dump_data_pw("Mutual authentication message:\n", in.data, in.length);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ } else {
+ unwrapped_in = in;
+ }
+ /* TODO: check the tok_id */
+
+ inbuf.data = unwrapped_in.data;
+ inbuf.length = unwrapped_in.length;
+ ret = krb5_rd_rep(gensec_krb5_state->smb_krb5_context->krb5_context,
+ gensec_krb5_state->auth_context,
+ &inbuf, &repl);
+ if (ret) {
+ DEBUG(1,("krb5_rd_rep (mutual authentication) failed (%s)\n",
+ smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, out_mem_ctx)));
+ dump_data_pw("Mutual authentication message:\n", (uint8_t *)inbuf.data, inbuf.length);
+ nt_status = NT_STATUS_ACCESS_DENIED;
+ } else {
+ *out = data_blob(NULL, 0);
+ nt_status = NT_STATUS_OK;
+ gensec_krb5_state->state_position = GENSEC_KRB5_DONE;
+ }
+ if (repl) {
+ krb5_free_ap_rep_enc_part(gensec_krb5_state->smb_krb5_context->krb5_context, repl);
+ }
+ return nt_status;
+ }
+
+ case GENSEC_KRB5_SERVER_START:
+ {
+ DATA_BLOB unwrapped_in;
+ DATA_BLOB unwrapped_out = data_blob(NULL, 0);
+ krb5_data inbuf, outbuf;
+ uint8_t tok_id[2];
+ struct keytab_container *keytab;
+ krb5_principal server_in_keytab;
+
+ if (!in.data) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* Grab the keytab, however generated */
+ ret = cli_credentials_get_keytab(gensec_get_credentials(gensec_security),
+ gensec_security->event_ctx,
+ gensec_security->settings->lp_ctx, &keytab);
+ if (ret) {
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ }
+
+ /* This ensures we lookup the correct entry in that keytab */
+ ret = principal_from_credentials(out_mem_ctx, gensec_get_credentials(gensec_security),
+ gensec_krb5_state->smb_krb5_context,
+ &server_in_keytab);
+
+ if (ret) {
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ }
+
+ /* Parse the GSSAPI wrapping, if it's there... (win2k3 allows it to be omited) */
+ if (gensec_krb5_state->gssapi
+ && gensec_gssapi_parse_krb5_wrap(out_mem_ctx, &in, &unwrapped_in, tok_id)) {
+ inbuf.data = unwrapped_in.data;
+ inbuf.length = unwrapped_in.length;
+ } else {
+ inbuf.data = in.data;
+ inbuf.length = in.length;
+ }
+
+ ret = smb_rd_req_return_stuff(gensec_krb5_state->smb_krb5_context->krb5_context,
+ &gensec_krb5_state->auth_context,
+ &inbuf, keytab->keytab, server_in_keytab,
+ &outbuf,
+ &gensec_krb5_state->ticket,
+ &gensec_krb5_state->keyblock);
+
+ if (ret) {
+ return NT_STATUS_LOGON_FAILURE;
+ }
+ unwrapped_out.data = (uint8_t *)outbuf.data;
+ unwrapped_out.length = outbuf.length;
+ gensec_krb5_state->state_position = GENSEC_KRB5_DONE;
+ /* wrap that up in a nice GSS-API wrapping */
+ if (gensec_krb5_state->gssapi) {
+ *out = gensec_gssapi_gen_krb5_wrap(out_mem_ctx, &unwrapped_out, TOK_ID_KRB_AP_REP);
+ } else {
+ *out = data_blob_talloc(out_mem_ctx, outbuf.data, outbuf.length);
+ }
+ krb5_data_free(&outbuf);
+ return NT_STATUS_OK;
+ }
+
+ case GENSEC_KRB5_DONE:
+ default:
+ /* Asking too many times... */
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+}
+
+static NTSTATUS gensec_krb5_session_key(struct gensec_security *gensec_security,
+ DATA_BLOB *session_key)
+{
+ struct gensec_krb5_state *gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
+ krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context;
+ krb5_auth_context auth_context = gensec_krb5_state->auth_context;
+ krb5_keyblock *skey;
+ krb5_error_code err = -1;
+
+ if (gensec_krb5_state->state_position != GENSEC_KRB5_DONE) {
+ return NT_STATUS_NO_USER_SESSION_KEY;
+ }
+
+ if (gensec_krb5_state->session_key.data) {
+ *session_key = gensec_krb5_state->session_key;
+ return NT_STATUS_OK;
+ }
+
+ switch (gensec_security->gensec_role) {
+ case GENSEC_CLIENT:
+ err = krb5_auth_con_getlocalsubkey(context, auth_context, &skey);
+ break;
+ case GENSEC_SERVER:
+ err = krb5_auth_con_getremotesubkey(context, auth_context, &skey);
+ break;
+ }
+ if (err == 0 && skey != NULL) {
+ DEBUG(10, ("Got KRB5 session key of length %d\n",
+ (int)KRB5_KEY_LENGTH(skey)));
+ gensec_krb5_state->session_key = data_blob_talloc(gensec_krb5_state,
+ KRB5_KEY_DATA(skey), KRB5_KEY_LENGTH(skey));
+ *session_key = gensec_krb5_state->session_key;
+ dump_data_pw("KRB5 Session Key:\n", session_key->data, session_key->length);
+
+ krb5_free_keyblock(context, skey);
+ return NT_STATUS_OK;
+ } else {
+ DEBUG(10, ("KRB5 error getting session key %d\n", err));
+ return NT_STATUS_NO_USER_SESSION_KEY;
+ }
+}
+
+static NTSTATUS gensec_krb5_session_info(struct gensec_security *gensec_security,
+ struct auth_session_info **_session_info)
+{
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ struct gensec_krb5_state *gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
+ krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context;
+ struct auth_serversupplied_info *server_info = NULL;
+ struct auth_session_info *session_info = NULL;
+ struct PAC_LOGON_INFO *logon_info;
+
+ krb5_principal client_principal;
+ char *principal_string;
+
+ DATA_BLOB pac;
+ krb5_data pac_data;
+
+ krb5_error_code ret;
+
+ TALLOC_CTX *mem_ctx = talloc_new(gensec_security);
+ if (!mem_ctx) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = krb5_ticket_get_client(context, gensec_krb5_state->ticket, &client_principal);
+ if (ret) {
+ DEBUG(5, ("krb5_ticket_get_client failed to get cleint principal: %s\n",
+ smb_get_krb5_error_message(context,
+ ret, mem_ctx)));
+ talloc_free(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = krb5_unparse_name(gensec_krb5_state->smb_krb5_context->krb5_context,
+ client_principal, &principal_string);
+ if (ret) {
+ DEBUG(1, ("Unable to parse client principal: %s\n",
+ smb_get_krb5_error_message(context,
+ ret, mem_ctx)));
+ talloc_free(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = krb5_ticket_get_authorization_data_type(context, gensec_krb5_state->ticket,
+ KRB5_AUTHDATA_WIN2K_PAC,
+ &pac_data);
+
+ if (ret && gensec_setting_bool(gensec_security->settings, "gensec", "require_pac", false)) {
+ DEBUG(1, ("Unable to find PAC in ticket from %s, failing to allow access: %s \n",
+ principal_string,
+ smb_get_krb5_error_message(context,
+ ret, mem_ctx)));
+ krb5_free_principal(context, client_principal);
+ free(principal_string);
+ return NT_STATUS_ACCESS_DENIED;
+ } else if (ret) {
+ /* NO pac */
+ DEBUG(5, ("krb5_ticket_get_authorization_data_type failed to find PAC: %s\n",
+ smb_get_krb5_error_message(context,
+ ret, mem_ctx)));
+ if (gensec_security->auth_context &&
+ !gensec_setting_bool(gensec_security->settings, "gensec", "require_pac", false)) {
+ DEBUG(1, ("Unable to find PAC, resorting to local user lookup: %s"));
+ nt_status = gensec_security->auth_context->get_server_info_principal(mem_ctx,
+ gensec_security->auth_context,
+ principal_string,
+ &server_info);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(mem_ctx);
+ return nt_status;
+ }
+ } else {
+ DEBUG(1, ("Unable to find PAC in ticket from %s, failing to allow access\n",
+ principal_string));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ krb5_free_principal(context, client_principal);
+ free(principal_string);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(mem_ctx);
+ return nt_status;
+ }
+ } else {
+ /* Found pac */
+ union netr_Validation validation;
+ free(principal_string);
+
+ pac = data_blob_talloc(mem_ctx, pac_data.data, pac_data.length);
+ if (!pac.data) {
+ krb5_free_principal(context, client_principal);
+ talloc_free(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* decode and verify the pac */
+ nt_status = kerberos_pac_logon_info(gensec_krb5_state,
+ gensec_security->settings->iconv_convenience,
+ &logon_info, pac,
+ gensec_krb5_state->smb_krb5_context->krb5_context,
+ NULL, gensec_krb5_state->keyblock,
+ client_principal,
+ gensec_krb5_state->ticket->ticket.authtime, NULL);
+ krb5_free_principal(context, client_principal);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(mem_ctx);
+ return nt_status;
+ }
+
+ validation.sam3 = &logon_info->info3;
+ nt_status = make_server_info_netlogon_validation(mem_ctx,
+ NULL,
+ 3, &validation,
+ &server_info);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(mem_ctx);
+ return nt_status;
+ }
+ }
+
+ /* references the server_info into the session_info */
+ nt_status = auth_generate_session_info(mem_ctx, gensec_security->event_ctx, gensec_security->settings->lp_ctx, server_info, &session_info);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(mem_ctx);
+ return nt_status;
+ }
+
+ nt_status = gensec_krb5_session_key(gensec_security, &session_info->session_key);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(mem_ctx);
+ return nt_status;
+ }
+
+ *_session_info = session_info;
+
+ talloc_steal(gensec_krb5_state, session_info);
+ talloc_free(mem_ctx);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS gensec_krb5_wrap(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *in,
+ DATA_BLOB *out)
+{
+ struct gensec_krb5_state *gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
+ krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context;
+ krb5_auth_context auth_context = gensec_krb5_state->auth_context;
+ krb5_error_code ret;
+ krb5_data input, output;
+ input.length = in->length;
+ input.data = in->data;
+
+ if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
+ ret = krb5_mk_priv(context, auth_context, &input, &output, NULL);
+ if (ret) {
+ DEBUG(1, ("krb5_mk_priv failed: %s\n",
+ smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context,
+ ret, mem_ctx)));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ *out = data_blob_talloc(mem_ctx, output.data, output.length);
+
+ krb5_data_free(&output);
+ } else {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS gensec_krb5_unwrap(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *in,
+ DATA_BLOB *out)
+{
+ struct gensec_krb5_state *gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
+ krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context;
+ krb5_auth_context auth_context = gensec_krb5_state->auth_context;
+ krb5_error_code ret;
+ krb5_data input, output;
+ krb5_replay_data replay;
+ input.length = in->length;
+ input.data = in->data;
+
+ if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
+ ret = krb5_rd_priv(context, auth_context, &input, &output, &replay);
+ if (ret) {
+ DEBUG(1, ("krb5_rd_priv failed: %s\n",
+ smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context,
+ ret, mem_ctx)));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ *out = data_blob_talloc(mem_ctx, output.data, output.length);
+
+ krb5_data_free(&output);
+ } else {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ return NT_STATUS_OK;
+}
+
+static bool gensec_krb5_have_feature(struct gensec_security *gensec_security,
+ uint32_t feature)
+{
+ struct gensec_krb5_state *gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
+ if (feature & GENSEC_FEATURE_SESSION_KEY) {
+ return true;
+ }
+ if (!gensec_krb5_state->gssapi &&
+ (feature & GENSEC_FEATURE_SEAL)) {
+ return true;
+ }
+
+ return false;
+}
+
+static const char *gensec_krb5_oids[] = {
+ GENSEC_OID_KERBEROS5,
+ GENSEC_OID_KERBEROS5_OLD,
+ NULL
+};
+
+static const struct gensec_security_ops gensec_fake_gssapi_krb5_security_ops = {
+ .name = "fake_gssapi_krb5",
+ .auth_type = DCERPC_AUTH_TYPE_KRB5,
+ .oid = gensec_krb5_oids,
+ .client_start = gensec_fake_gssapi_krb5_client_start,
+ .server_start = gensec_fake_gssapi_krb5_server_start,
+ .update = gensec_krb5_update,
+ .magic = gensec_fake_gssapi_krb5_magic,
+ .session_key = gensec_krb5_session_key,
+ .session_info = gensec_krb5_session_info,
+ .have_feature = gensec_krb5_have_feature,
+ .enabled = false,
+ .kerberos = true,
+ .priority = GENSEC_KRB5
+};
+
+static const struct gensec_security_ops gensec_krb5_security_ops = {
+ .name = "krb5",
+ .client_start = gensec_krb5_client_start,
+ .server_start = gensec_krb5_server_start,
+ .update = gensec_krb5_update,
+ .session_key = gensec_krb5_session_key,
+ .session_info = gensec_krb5_session_info,
+ .have_feature = gensec_krb5_have_feature,
+ .wrap = gensec_krb5_wrap,
+ .unwrap = gensec_krb5_unwrap,
+ .enabled = true,
+ .kerberos = true,
+ .priority = GENSEC_KRB5
+};
+
+_PUBLIC_ NTSTATUS gensec_krb5_init(void)
+{
+ NTSTATUS ret;
+
+ ret = gensec_register(&gensec_krb5_security_ops);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("Failed to register '%s' gensec backend!\n",
+ gensec_krb5_security_ops.name));
+ return ret;
+ }
+
+ ret = gensec_register(&gensec_fake_gssapi_krb5_security_ops);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("Failed to register '%s' gensec backend!\n",
+ gensec_krb5_security_ops.name));
+ return ret;
+ }
+
+ return ret;
+}
diff --git a/source4/auth/gensec/schannel.c b/source4/auth/gensec/schannel.c
new file mode 100644
index 0000000000..e6d38c14a3
--- /dev/null
+++ b/source4/auth/gensec/schannel.c
@@ -0,0 +1,291 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ dcerpc schannel operations
+
+ Copyright (C) Andrew Tridgell 2004
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "librpc/gen_ndr/ndr_schannel.h"
+#include "auth/auth.h"
+#include "auth/credentials/credentials.h"
+#include "auth/gensec/gensec.h"
+#include "auth/gensec/gensec_proto.h"
+#include "auth/gensec/schannel.h"
+#include "auth/gensec/schannel_state.h"
+#include "auth/gensec/schannel_proto.h"
+#include "librpc/rpc/dcerpc.h"
+#include "param/param.h"
+#include "auth/session_proto.h"
+
+static size_t schannel_sig_size(struct gensec_security *gensec_security, size_t data_size)
+{
+ return 32;
+}
+
+static NTSTATUS schannel_session_key(struct gensec_security *gensec_security,
+ DATA_BLOB *session_key)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS schannel_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx,
+ const DATA_BLOB in, DATA_BLOB *out)
+{
+ struct schannel_state *state = (struct schannel_state *)gensec_security->private_data;
+ NTSTATUS status;
+ enum ndr_err_code ndr_err;
+ struct schannel_bind bind_schannel;
+ struct schannel_bind_ack bind_schannel_ack;
+ struct creds_CredentialState *creds;
+
+ const char *workstation;
+ const char *domain;
+ *out = data_blob(NULL, 0);
+
+ switch (gensec_security->gensec_role) {
+ case GENSEC_CLIENT:
+ if (state->state != SCHANNEL_STATE_START) {
+ /* we could parse the bind ack, but we don't know what it is yet */
+ return NT_STATUS_OK;
+ }
+
+ state->creds = talloc_reference(state, cli_credentials_get_netlogon_creds(gensec_security->credentials));
+
+ bind_schannel.unknown1 = 0;
+#if 0
+ /* to support this we'd need to have access to the full domain name */
+ bind_schannel.bind_type = 23;
+ bind_schannel.u.info23.domain = cli_credentials_get_domain(gensec_security->credentials);
+ bind_schannel.u.info23.workstation = cli_credentials_get_workstation(gensec_security->credentials);
+ bind_schannel.u.info23.dnsdomain = cli_credentials_get_realm(gensec_security->credentials);
+ /* w2k3 refuses us if we use the full DNS workstation?
+ why? perhaps because we don't fill in the dNSHostName
+ attribute in the machine account? */
+ bind_schannel.u.info23.dnsworkstation = cli_credentials_get_workstation(gensec_security->credentials);
+#else
+ bind_schannel.bind_type = 3;
+ bind_schannel.u.info3.domain = cli_credentials_get_domain(gensec_security->credentials);
+ bind_schannel.u.info3.workstation = cli_credentials_get_workstation(gensec_security->credentials);
+#endif
+
+ ndr_err = ndr_push_struct_blob(out, out_mem_ctx,
+ gensec_security->settings->iconv_convenience, &bind_schannel,
+ (ndr_push_flags_fn_t)ndr_push_schannel_bind);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ DEBUG(3, ("Could not create schannel bind: %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+
+ state->state = SCHANNEL_STATE_UPDATE_1;
+
+ return NT_STATUS_MORE_PROCESSING_REQUIRED;
+ case GENSEC_SERVER:
+
+ if (state->state != SCHANNEL_STATE_START) {
+ /* no third leg on this protocol */
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* parse the schannel startup blob */
+ ndr_err = ndr_pull_struct_blob(&in, out_mem_ctx,
+ gensec_security->settings->iconv_convenience,
+ &bind_schannel,
+ (ndr_pull_flags_fn_t)ndr_pull_schannel_bind);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ DEBUG(3, ("Could not parse incoming schannel bind: %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+
+ if (bind_schannel.bind_type == 23) {
+ workstation = bind_schannel.u.info23.workstation;
+ domain = bind_schannel.u.info23.domain;
+ } else {
+ workstation = bind_schannel.u.info3.workstation;
+ domain = bind_schannel.u.info3.domain;
+ }
+
+ /* pull the session key for this client */
+ status = schannel_fetch_session_key(out_mem_ctx, gensec_security->event_ctx,
+ gensec_security->settings->lp_ctx, workstation,
+ domain, &creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(3, ("Could not find session key for attempted schannel connection from %s: %s\n",
+ workstation, nt_errstr(status)));
+ if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_HANDLE)) {
+ return NT_STATUS_LOGON_FAILURE;
+ }
+ return status;
+ }
+
+ state->creds = talloc_reference(state, creds);
+
+ bind_schannel_ack.unknown1 = 1;
+ bind_schannel_ack.unknown2 = 0;
+ bind_schannel_ack.unknown3 = 0x6c0000;
+
+ ndr_err = ndr_push_struct_blob(out, out_mem_ctx,
+ gensec_security->settings->iconv_convenience, &bind_schannel_ack,
+ (ndr_push_flags_fn_t)ndr_push_schannel_bind_ack);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ DEBUG(3, ("Could not return schannel bind ack for client %s: %s\n",
+ workstation, nt_errstr(status)));
+ return status;
+ }
+
+ state->state = SCHANNEL_STATE_UPDATE_1;
+
+ return NT_STATUS_OK;
+ }
+ return NT_STATUS_INVALID_PARAMETER;
+}
+
+/**
+ * Return the struct creds_CredentialState.
+ *
+ * Make sure not to call this unless gensec is using schannel...
+ */
+
+/* TODO: make this non-public */
+_PUBLIC_ NTSTATUS dcerpc_schannel_creds(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ struct creds_CredentialState **creds)
+{
+ struct schannel_state *state = talloc_get_type(gensec_security->private_data, struct schannel_state);
+
+ *creds = talloc_reference(mem_ctx, state->creds);
+ if (!*creds) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ return NT_STATUS_OK;
+}
+
+
+/**
+ * Returns anonymous credentials for schannel, matching Win2k3.
+ *
+ */
+
+static NTSTATUS schannel_session_info(struct gensec_security *gensec_security,
+ struct auth_session_info **_session_info)
+{
+ struct schannel_state *state = talloc_get_type(gensec_security->private_data, struct schannel_state);
+ return auth_anonymous_session_info(state, gensec_security->event_ctx, gensec_security->settings->lp_ctx, _session_info);
+}
+
+static NTSTATUS schannel_start(struct gensec_security *gensec_security)
+{
+ struct schannel_state *state;
+
+ state = talloc(gensec_security, struct schannel_state);
+ if (!state) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ state->state = SCHANNEL_STATE_START;
+ state->seq_num = 0;
+ gensec_security->private_data = state;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS schannel_server_start(struct gensec_security *gensec_security)
+{
+ NTSTATUS status;
+ struct schannel_state *state;
+
+ status = schannel_start(gensec_security);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ state = (struct schannel_state *)gensec_security->private_data;
+ state->initiator = false;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS schannel_client_start(struct gensec_security *gensec_security)
+{
+ NTSTATUS status;
+ struct schannel_state *state;
+
+ status = schannel_start(gensec_security);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ state = (struct schannel_state *)gensec_security->private_data;
+ state->initiator = true;
+
+ return NT_STATUS_OK;
+}
+
+
+static bool schannel_have_feature(struct gensec_security *gensec_security,
+ uint32_t feature)
+{
+ if (feature & (GENSEC_FEATURE_SIGN |
+ GENSEC_FEATURE_SEAL)) {
+ return true;
+ }
+ if (feature & GENSEC_FEATURE_DCE_STYLE) {
+ return true;
+ }
+ if (feature & GENSEC_FEATURE_ASYNC_REPLIES) {
+ return true;
+ }
+ return false;
+}
+
+
+static const struct gensec_security_ops gensec_schannel_security_ops = {
+ .name = "schannel",
+ .auth_type = DCERPC_AUTH_TYPE_SCHANNEL,
+ .client_start = schannel_client_start,
+ .server_start = schannel_server_start,
+ .update = schannel_update,
+ .seal_packet = schannel_seal_packet,
+ .sign_packet = schannel_sign_packet,
+ .check_packet = schannel_check_packet,
+ .unseal_packet = schannel_unseal_packet,
+ .session_key = schannel_session_key,
+ .session_info = schannel_session_info,
+ .sig_size = schannel_sig_size,
+ .have_feature = schannel_have_feature,
+ .enabled = true,
+ .priority = GENSEC_SCHANNEL
+};
+
+_PUBLIC_ NTSTATUS gensec_schannel_init(void)
+{
+ NTSTATUS ret;
+ ret = gensec_register(&gensec_schannel_security_ops);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("Failed to register '%s' gensec backend!\n",
+ gensec_schannel_security_ops.name));
+ return ret;
+ }
+
+ return ret;
+}
diff --git a/source4/auth/gensec/schannel.h b/source4/auth/gensec/schannel.h
new file mode 100644
index 0000000000..2ddea29006
--- /dev/null
+++ b/source4/auth/gensec/schannel.h
@@ -0,0 +1,36 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ dcerpc schannel operations
+
+ Copyright (C) Andrew Tridgell 2004
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "libcli/auth/credentials.h"
+
+enum schannel_position {
+ SCHANNEL_STATE_START = 0,
+ SCHANNEL_STATE_UPDATE_1
+};
+
+struct schannel_state {
+ enum schannel_position state;
+ uint32_t seq_num;
+ bool initiator;
+ struct creds_CredentialState *creds;
+};
+
diff --git a/source4/auth/gensec/schannel_sign.c b/source4/auth/gensec/schannel_sign.c
new file mode 100644
index 0000000000..9862a029a4
--- /dev/null
+++ b/source4/auth/gensec/schannel_sign.c
@@ -0,0 +1,285 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ schannel library code
+
+ Copyright (C) Andrew Tridgell 2004
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "../lib/crypto/crypto.h"
+#include "auth/auth.h"
+#include "auth/gensec/schannel.h"
+#include "auth/credentials/credentials.h"
+#include "auth/gensec/gensec.h"
+#include "auth/gensec/schannel_proto.h"
+
+#define NETSEC_SIGN_SIGNATURE { 0x77, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00 }
+#define NETSEC_SEAL_SIGNATURE { 0x77, 0x00, 0x7a, 0x00, 0xff, 0xff, 0x00, 0x00 }
+
+/*******************************************************************
+ Encode or Decode the sequence number (which is symmetric)
+ ********************************************************************/
+static void netsec_deal_with_seq_num(struct schannel_state *state,
+ const uint8_t packet_digest[8],
+ uint8_t seq_num[8])
+{
+ static const uint8_t zeros[4];
+ uint8_t sequence_key[16];
+ uint8_t digest1[16];
+
+ hmac_md5(state->creds->session_key, zeros, sizeof(zeros), digest1);
+ hmac_md5(digest1, packet_digest, 8, sequence_key);
+ arcfour_crypt(seq_num, sequence_key, 8);
+
+ state->seq_num++;
+}
+
+
+/*******************************************************************
+ Calculate the key with which to encode the data payload
+ ********************************************************************/
+static void netsec_get_sealing_key(const uint8_t session_key[16],
+ const uint8_t seq_num[8],
+ uint8_t sealing_key[16])
+{
+ static const uint8_t zeros[4];
+ uint8_t digest2[16];
+ uint8_t sess_kf0[16];
+ int i;
+
+ for (i = 0; i < 16; i++) {
+ sess_kf0[i] = session_key[i] ^ 0xf0;
+ }
+
+ hmac_md5(sess_kf0, zeros, 4, digest2);
+ hmac_md5(digest2, seq_num, 8, sealing_key);
+}
+
+
+/*******************************************************************
+ Create a digest over the entire packet (including the data), and
+ MD5 it with the session key.
+ ********************************************************************/
+static void schannel_digest(const uint8_t sess_key[16],
+ const uint8_t netsec_sig[8],
+ const uint8_t *confounder,
+ const uint8_t *data, size_t data_len,
+ uint8_t digest_final[16])
+{
+ uint8_t packet_digest[16];
+ static const uint8_t zeros[4];
+ struct MD5Context ctx;
+
+ MD5Init(&ctx);
+ MD5Update(&ctx, zeros, 4);
+ MD5Update(&ctx, netsec_sig, 8);
+ if (confounder) {
+ MD5Update(&ctx, confounder, 8);
+ }
+ MD5Update(&ctx, data, data_len);
+ MD5Final(packet_digest, &ctx);
+
+ hmac_md5(sess_key, packet_digest, sizeof(packet_digest), digest_final);
+}
+
+
+/*
+ unseal a packet
+*/
+NTSTATUS schannel_unseal_packet(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ uint8_t *data, size_t length,
+ const uint8_t *whole_pdu, size_t pdu_length,
+ const DATA_BLOB *sig)
+{
+ struct schannel_state *state = talloc_get_type(gensec_security->private_data, struct schannel_state);
+
+ uint8_t digest_final[16];
+ uint8_t confounder[8];
+ uint8_t seq_num[8];
+ uint8_t sealing_key[16];
+ static const uint8_t netsec_sig[8] = NETSEC_SEAL_SIGNATURE;
+
+ if (sig->length != 32) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ memcpy(confounder, sig->data+24, 8);
+
+ RSIVAL(seq_num, 0, state->seq_num);
+ SIVAL(seq_num, 4, state->initiator?0:0x80);
+
+ netsec_get_sealing_key(state->creds->session_key, seq_num, sealing_key);
+ arcfour_crypt(confounder, sealing_key, 8);
+ arcfour_crypt(data, sealing_key, length);
+
+ schannel_digest(state->creds->session_key,
+ netsec_sig, confounder,
+ data, length, digest_final);
+
+ if (memcmp(digest_final, sig->data+16, 8) != 0) {
+ dump_data_pw("calc digest:", digest_final, 8);
+ dump_data_pw("wire digest:", sig->data+16, 8);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ netsec_deal_with_seq_num(state, digest_final, seq_num);
+
+ if (memcmp(seq_num, sig->data+8, 8) != 0) {
+ dump_data_pw("calc seq num:", seq_num, 8);
+ dump_data_pw("wire seq num:", sig->data+8, 8);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ check the signature on a packet
+*/
+NTSTATUS schannel_check_packet(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ const uint8_t *data, size_t length,
+ const uint8_t *whole_pdu, size_t pdu_length,
+ const DATA_BLOB *sig)
+{
+ struct schannel_state *state = talloc_get_type(gensec_security->private_data, struct schannel_state);
+
+ uint8_t digest_final[16];
+ uint8_t seq_num[8];
+ static const uint8_t netsec_sig[8] = NETSEC_SIGN_SIGNATURE;
+
+ /* w2k sends just 24 bytes and skip the confounder */
+ if (sig->length != 32 && sig->length != 24) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ RSIVAL(seq_num, 0, state->seq_num);
+ SIVAL(seq_num, 4, state->initiator?0:0x80);
+
+ dump_data_pw("seq_num:\n", seq_num, 8);
+ dump_data_pw("sess_key:\n", state->creds->session_key, 16);
+
+ schannel_digest(state->creds->session_key,
+ netsec_sig, NULL,
+ data, length, digest_final);
+
+ netsec_deal_with_seq_num(state, digest_final, seq_num);
+
+ if (memcmp(seq_num, sig->data+8, 8) != 0) {
+ dump_data_pw("calc seq num:", seq_num, 8);
+ dump_data_pw("wire seq num:", sig->data+8, 8);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (memcmp(digest_final, sig->data+16, 8) != 0) {
+ dump_data_pw("calc digest:", digest_final, 8);
+ dump_data_pw("wire digest:", sig->data+16, 8);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ seal a packet
+*/
+NTSTATUS schannel_seal_packet(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ uint8_t *data, size_t length,
+ const uint8_t *whole_pdu, size_t pdu_length,
+ DATA_BLOB *sig)
+{
+ struct schannel_state *state = talloc_get_type(gensec_security->private_data, struct schannel_state);
+
+ uint8_t digest_final[16];
+ uint8_t confounder[8];
+ uint8_t seq_num[8];
+ uint8_t sealing_key[16];
+ static const uint8_t netsec_sig[8] = NETSEC_SEAL_SIGNATURE;
+
+ generate_random_buffer(confounder, 8);
+
+ RSIVAL(seq_num, 0, state->seq_num);
+ SIVAL(seq_num, 4, state->initiator?0x80:0);
+
+ schannel_digest(state->creds->session_key,
+ netsec_sig, confounder,
+ data, length, digest_final);
+
+ netsec_get_sealing_key(state->creds->session_key, seq_num, sealing_key);
+ arcfour_crypt(confounder, sealing_key, 8);
+ arcfour_crypt(data, sealing_key, length);
+
+ netsec_deal_with_seq_num(state, digest_final, seq_num);
+
+ (*sig) = data_blob_talloc(mem_ctx, NULL, 32);
+
+ memcpy(sig->data, netsec_sig, 8);
+ memcpy(sig->data+8, seq_num, 8);
+ memcpy(sig->data+16, digest_final, 8);
+ memcpy(sig->data+24, confounder, 8);
+
+ dump_data_pw("signature:", sig->data+ 0, 8);
+ dump_data_pw("seq_num :", sig->data+ 8, 8);
+ dump_data_pw("digest :", sig->data+16, 8);
+ dump_data_pw("confound :", sig->data+24, 8);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ sign a packet
+*/
+NTSTATUS schannel_sign_packet(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ const uint8_t *data, size_t length,
+ const uint8_t *whole_pdu, size_t pdu_length,
+ DATA_BLOB *sig)
+{
+ struct schannel_state *state = talloc_get_type(gensec_security->private_data, struct schannel_state);
+
+ uint8_t digest_final[16];
+ uint8_t seq_num[8];
+ static const uint8_t netsec_sig[8] = NETSEC_SIGN_SIGNATURE;
+
+ RSIVAL(seq_num, 0, state->seq_num);
+ SIVAL(seq_num, 4, state->initiator?0x80:0);
+
+ schannel_digest(state->creds->session_key,
+ netsec_sig, NULL,
+ data, length, digest_final);
+
+ netsec_deal_with_seq_num(state, digest_final, seq_num);
+
+ (*sig) = data_blob_talloc(mem_ctx, NULL, 32);
+
+ memcpy(sig->data, netsec_sig, 8);
+ memcpy(sig->data+8, seq_num, 8);
+ memcpy(sig->data+16, digest_final, 8);
+ memset(sig->data+24, 0, 8);
+
+ dump_data_pw("signature:", sig->data+ 0, 8);
+ dump_data_pw("seq_num :", sig->data+ 8, 8);
+ dump_data_pw("digest :", sig->data+16, 8);
+ dump_data_pw("confound :", sig->data+24, 8);
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/auth/gensec/schannel_state.c b/source4/auth/gensec/schannel_state.c
new file mode 100644
index 0000000000..ca8537cac9
--- /dev/null
+++ b/source4/auth/gensec/schannel_state.c
@@ -0,0 +1,349 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ module to store/fetch session keys for the schannel server
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/ldb/include/ldb.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "ldb_wrap.h"
+#include "../lib/util/util_ldb.h"
+#include "libcli/auth/libcli_auth.h"
+#include "auth/auth.h"
+#include "param/param.h"
+#include "auth/gensec/schannel_state.h"
+
+static struct ldb_val *schannel_dom_sid_ldb_val(TALLOC_CTX *mem_ctx,
+ struct smb_iconv_convenience *smbiconv,
+ struct dom_sid *sid)
+{
+ enum ndr_err_code ndr_err;
+ struct ldb_val *v;
+
+ v = talloc(mem_ctx, struct ldb_val);
+ if (!v) return NULL;
+
+ ndr_err = ndr_push_struct_blob(v, mem_ctx, smbiconv, sid,
+ (ndr_push_flags_fn_t)ndr_push_dom_sid);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ talloc_free(v);
+ return NULL;
+ }
+
+ return v;
+}
+
+static struct dom_sid *schannel_ldb_val_dom_sid(TALLOC_CTX *mem_ctx,
+ const struct ldb_val *v)
+{
+ enum ndr_err_code ndr_err;
+ struct dom_sid *sid;
+
+ sid = talloc(mem_ctx, struct dom_sid);
+ if (!sid) return NULL;
+
+ ndr_err = ndr_pull_struct_blob(v, sid, NULL, sid,
+ (ndr_pull_flags_fn_t)ndr_pull_dom_sid);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ talloc_free(sid);
+ return NULL;
+ }
+ return sid;
+}
+
+
+/**
+ connect to the schannel ldb
+*/
+struct ldb_context *schannel_db_connect(TALLOC_CTX *mem_ctx, struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx)
+{
+ char *path;
+ struct ldb_context *ldb;
+ bool existed;
+ const char *init_ldif =
+ "dn: @ATTRIBUTES\n" \
+ "computerName: CASE_INSENSITIVE\n" \
+ "flatname: CASE_INSENSITIVE\n";
+
+ path = private_path(mem_ctx, lp_ctx, "schannel.ldb");
+ if (!path) {
+ return NULL;
+ }
+
+ existed = file_exist(path);
+
+ ldb = ldb_wrap_connect(mem_ctx, ev_ctx, lp_ctx, path,
+ system_session(mem_ctx, lp_ctx),
+ NULL, LDB_FLG_NOSYNC, NULL);
+ talloc_free(path);
+ if (!ldb) {
+ return NULL;
+ }
+
+ if (!existed) {
+ gendb_add_ldif(ldb, init_ldif);
+ }
+
+ return ldb;
+}
+
+/*
+ remember an established session key for a netr server authentication
+ use a simple ldb structure
+*/
+NTSTATUS schannel_store_session_key_ldb(TALLOC_CTX *mem_ctx,
+ struct ldb_context *ldb,
+ struct creds_CredentialState *creds)
+{
+ struct ldb_message *msg;
+ struct ldb_val val, seed, client_state, server_state;
+ struct smb_iconv_convenience *smbiconv;
+ struct ldb_val *sid_val;
+ char *f;
+ char *sct;
+ int ret;
+
+ f = talloc_asprintf(mem_ctx, "%u", (unsigned int)creds->negotiate_flags);
+
+ if (f == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ sct = talloc_asprintf(mem_ctx, "%u", (unsigned int)creds->secure_channel_type);
+
+ if (sct == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ msg = ldb_msg_new(ldb);
+ if (msg == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ msg->dn = ldb_dn_new_fmt(msg, ldb, "computerName=%s", creds->computer_name);
+ if ( ! msg->dn) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ smbiconv = lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm"));
+ sid_val = schannel_dom_sid_ldb_val(msg, smbiconv, creds->sid);
+ if (sid_val == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ val.data = creds->session_key;
+ val.length = sizeof(creds->session_key);
+
+ seed.data = creds->seed.data;
+ seed.length = sizeof(creds->seed.data);
+
+ client_state.data = creds->client.data;
+ client_state.length = sizeof(creds->client.data);
+ server_state.data = creds->server.data;
+ server_state.length = sizeof(creds->server.data);
+
+ ldb_msg_add_string(msg, "objectClass", "schannelState");
+ ldb_msg_add_value(msg, "sessionKey", &val, NULL);
+ ldb_msg_add_value(msg, "seed", &seed, NULL);
+ ldb_msg_add_value(msg, "clientState", &client_state, NULL);
+ ldb_msg_add_value(msg, "serverState", &server_state, NULL);
+ ldb_msg_add_string(msg, "negotiateFlags", f);
+ ldb_msg_add_string(msg, "secureChannelType", sct);
+ ldb_msg_add_string(msg, "accountName", creds->account_name);
+ ldb_msg_add_string(msg, "computerName", creds->computer_name);
+ ldb_msg_add_string(msg, "flatname", creds->domain);
+ ldb_msg_add_value(msg, "objectSid", sid_val, NULL);
+
+ ldb_delete(ldb, msg->dn);
+
+ ret = ldb_add(ldb, msg);
+
+ if (ret != 0) {
+ DEBUG(0,("Unable to add %s to session key db - %s\n",
+ ldb_dn_get_linearized(msg->dn), ldb_errstring(ldb)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS schannel_store_session_key(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx,
+ struct creds_CredentialState *creds)
+{
+ struct ldb_context *ldb;
+ NTSTATUS nt_status;
+ int ret;
+
+ ldb = schannel_db_connect(mem_ctx, ev_ctx, lp_ctx);
+ if (!ldb) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ ret = ldb_transaction_start(ldb);
+ if (ret != 0) {
+ talloc_free(ldb);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ nt_status = schannel_store_session_key_ldb(mem_ctx, ldb, creds);
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ ret = ldb_transaction_commit(ldb);
+ } else {
+ ret = ldb_transaction_cancel(ldb);
+ }
+
+ if (ret != 0) {
+ DEBUG(0,("Unable to commit adding credentials for %s to schannel key db - %s\n",
+ creds->computer_name, ldb_errstring(ldb)));
+ talloc_free(ldb);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ talloc_free(ldb);
+ return nt_status;
+}
+
+/*
+ read back a credentials back for a computer
+*/
+NTSTATUS schannel_fetch_session_key_ldb(TALLOC_CTX *mem_ctx,
+ struct ldb_context *ldb,
+ const char *computer_name,
+ const char *domain,
+ struct creds_CredentialState **creds)
+{
+ struct ldb_result *res;
+ int ret;
+ const struct ldb_val *val;
+
+ *creds = talloc_zero(mem_ctx, struct creds_CredentialState);
+ if (!*creds) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = ldb_search(ldb, mem_ctx, &res,
+ NULL, LDB_SCOPE_SUBTREE, NULL,
+ "(&(computerName=%s)(flatname=%s))", computer_name, domain);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(3,("schannel: Failed to find a record for client %s: %s\n", computer_name, ldb_errstring(ldb)));
+ return NT_STATUS_INVALID_HANDLE;
+ }
+ if (res->count != 1) {
+ DEBUG(3,("schannel: Failed to find a record for client: %s (found %d records)\n", computer_name, res->count));
+ talloc_free(res);
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ val = ldb_msg_find_ldb_val(res->msgs[0], "sessionKey");
+ if (val == NULL || val->length != 16) {
+ DEBUG(1,("schannel: record in schannel DB must contain a sessionKey of length 16, when searching for client: %s\n", computer_name));
+ talloc_free(res);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ memcpy((*creds)->session_key, val->data, 16);
+
+ val = ldb_msg_find_ldb_val(res->msgs[0], "seed");
+ if (val == NULL || val->length != 8) {
+ DEBUG(1,("schannel: record in schannel DB must contain a vaid seed of length 8, when searching for client: %s\n", computer_name));
+ talloc_free(res);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ memcpy((*creds)->seed.data, val->data, 8);
+
+ val = ldb_msg_find_ldb_val(res->msgs[0], "clientState");
+ if (val == NULL || val->length != 8) {
+ DEBUG(1,("schannel: record in schannel DB must contain a vaid clientState of length 8, when searching for client: %s\n", computer_name));
+ talloc_free(res);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ memcpy((*creds)->client.data, val->data, 8);
+
+ val = ldb_msg_find_ldb_val(res->msgs[0], "serverState");
+ if (val == NULL || val->length != 8) {
+ DEBUG(1,("schannel: record in schannel DB must contain a vaid serverState of length 8, when searching for client: %s\n", computer_name));
+ talloc_free(res);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ memcpy((*creds)->server.data, val->data, 8);
+
+ (*creds)->negotiate_flags = ldb_msg_find_attr_as_int(res->msgs[0], "negotiateFlags", 0);
+
+ (*creds)->secure_channel_type = ldb_msg_find_attr_as_int(res->msgs[0], "secureChannelType", 0);
+
+ (*creds)->account_name = talloc_strdup(*creds, ldb_msg_find_attr_as_string(res->msgs[0], "accountName", NULL));
+ if ((*creds)->account_name == NULL) {
+ talloc_free(res);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*creds)->computer_name = talloc_strdup(*creds, ldb_msg_find_attr_as_string(res->msgs[0], "computerName", NULL));
+ if ((*creds)->computer_name == NULL) {
+ talloc_free(res);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*creds)->domain = talloc_strdup(*creds, ldb_msg_find_attr_as_string(res->msgs[0], "flatname", NULL));
+ if ((*creds)->domain == NULL) {
+ talloc_free(res);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ val = ldb_msg_find_ldb_val(res->msgs[0], "objectSid");
+ if (val == NULL) {
+ DEBUG(1,("schannel: missing ObjectSid for client: %s\n", computer_name));
+ talloc_free(res);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ (*creds)->sid = schannel_ldb_val_dom_sid(*creds, val);
+ if ((*creds)->sid == NULL) {
+ talloc_free(res);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ talloc_free(res);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS schannel_fetch_session_key(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx,
+ const char *computer_name,
+ const char *domain,
+ struct creds_CredentialState **creds)
+{
+ NTSTATUS nt_status;
+ struct ldb_context *ldb;
+
+ ldb = schannel_db_connect(mem_ctx, ev_ctx, lp_ctx);
+ if (!ldb) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ nt_status = schannel_fetch_session_key_ldb(mem_ctx, ldb,
+ computer_name, domain,
+ creds);
+ talloc_free(ldb);
+ return nt_status;
+}
diff --git a/source4/auth/gensec/socket.c b/source4/auth/gensec/socket.c
new file mode 100644
index 0000000000..6a03f0bcec
--- /dev/null
+++ b/source4/auth/gensec/socket.c
@@ -0,0 +1,534 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ GENSEC socket interface
+
+ Copyright (C) Andrew Bartlett 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/events/events.h"
+#include "lib/socket/socket.h"
+#include "lib/stream/packet.h"
+#include "auth/gensec/gensec.h"
+#include "auth/gensec/gensec_proto.h"
+
+static const struct socket_ops gensec_socket_ops;
+
+struct gensec_socket {
+ struct gensec_security *gensec_security;
+ struct socket_context *socket;
+ struct tevent_context *ev;
+ struct packet_context *packet;
+ DATA_BLOB read_buffer; /* SASL packets are turned into liniarlised data here, for reading */
+ size_t orig_send_len;
+ bool eof;
+ NTSTATUS error;
+ bool interrupted;
+ void (*recv_handler)(void *, uint16_t);
+ void *recv_private;
+ int in_extra_read;
+ bool wrap; /* Should we be wrapping on this socket at all? */
+};
+
+static NTSTATUS gensec_socket_init_fn(struct socket_context *sock)
+{
+ switch (sock->type) {
+ case SOCKET_TYPE_STREAM:
+ break;
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ sock->backend_name = "gensec";
+
+ return NT_STATUS_OK;
+}
+
+/* These functions are for use here only (public because SPNEGO must
+ * use them for recursion) */
+_PUBLIC_ NTSTATUS gensec_wrap_packets(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *in,
+ DATA_BLOB *out,
+ size_t *len_processed)
+{
+ if (!gensec_security->ops->wrap_packets) {
+ NTSTATUS nt_status;
+ size_t max_input_size;
+ DATA_BLOB unwrapped, wrapped;
+ max_input_size = gensec_max_input_size(gensec_security);
+ unwrapped = data_blob_const(in->data, MIN(max_input_size, (size_t)in->length));
+
+ nt_status = gensec_wrap(gensec_security,
+ mem_ctx,
+ &unwrapped, &wrapped);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(mem_ctx);
+ return nt_status;
+ }
+
+ *out = data_blob_talloc(mem_ctx, NULL, 4);
+ if (!out->data) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ RSIVAL(out->data, 0, wrapped.length);
+
+ if (!data_blob_append(mem_ctx, out, wrapped.data, wrapped.length)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ *len_processed = unwrapped.length;
+ return NT_STATUS_OK;
+ }
+ return gensec_security->ops->wrap_packets(gensec_security, mem_ctx, in, out,
+ len_processed);
+}
+
+/* These functions are for use here only (public because SPNEGO must
+ * use them for recursion) */
+NTSTATUS gensec_unwrap_packets(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *in,
+ DATA_BLOB *out,
+ size_t *len_processed)
+{
+ if (!gensec_security->ops->unwrap_packets) {
+ DATA_BLOB wrapped;
+ NTSTATUS nt_status;
+ size_t packet_size;
+ if (in->length < 4) {
+ /* Missing the header we already had! */
+ DEBUG(0, ("Asked to unwrap packet of bogus length! How did we get the short packet?!\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ packet_size = RIVAL(in->data, 0);
+
+ wrapped = data_blob_const(in->data + 4, packet_size);
+
+ if (wrapped.length > (in->length - 4)) {
+ DEBUG(0, ("Asked to unwrap packed of bogus length %d > %d! How did we get this?!\n",
+ (int)wrapped.length, (int)(in->length - 4)));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ nt_status = gensec_unwrap(gensec_security,
+ mem_ctx,
+ &wrapped, out);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ *len_processed = packet_size + 4;
+ return nt_status;
+ }
+ return gensec_security->ops->unwrap_packets(gensec_security, mem_ctx, in, out,
+ len_processed);
+}
+
+/* These functions are for use here only (public because SPNEGO must
+ * use them for recursion) */
+NTSTATUS gensec_packet_full_request(struct gensec_security *gensec_security,
+ DATA_BLOB blob, size_t *size)
+{
+ if (gensec_security->ops->packet_full_request) {
+ return gensec_security->ops->packet_full_request(gensec_security,
+ blob, size);
+ }
+ if (gensec_security->ops->unwrap_packets) {
+ if (blob.length) {
+ *size = blob.length;
+ return NT_STATUS_OK;
+ }
+ return STATUS_MORE_ENTRIES;
+ }
+ return packet_full_request_u32(NULL, blob, size);
+}
+
+static NTSTATUS gensec_socket_full_request(void *private_data, DATA_BLOB blob, size_t *size)
+{
+ struct gensec_socket *gensec_socket = talloc_get_type(private_data, struct gensec_socket);
+ struct gensec_security *gensec_security = gensec_socket->gensec_security;
+ return gensec_packet_full_request(gensec_security, blob, size);
+}
+
+/* Try to figure out how much data is waiting to be read */
+static NTSTATUS gensec_socket_pending(struct socket_context *sock, size_t *npending)
+{
+ struct gensec_socket *gensec_socket = talloc_get_type(sock->private_data, struct gensec_socket);
+ if (!gensec_socket->wrap) {
+ return socket_pending(gensec_socket->socket, npending);
+ }
+
+ if (gensec_socket->read_buffer.length > 0) {
+ *npending = gensec_socket->read_buffer.length;
+ return NT_STATUS_OK;
+ }
+
+ /* This is a lie. We hope the decrypted data will always be
+ * less than this value, so the application just gets a short
+ * read. Without reading and decrypting it, we can't tell.
+ * If the SASL mech does compression, then we just need to
+ * manually trigger read events */
+ return socket_pending(gensec_socket->socket, npending);
+}
+
+/* Note if an error occours, so we can return it up the stack */
+static void gensec_socket_error_handler(void *private_data, NTSTATUS status)
+{
+ struct gensec_socket *gensec_socket = talloc_get_type(private_data, struct gensec_socket);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE)) {
+ gensec_socket->eof = true;
+ } else {
+ gensec_socket->error = status;
+ }
+}
+
+static void gensec_socket_trigger_read(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval t, void *private_data)
+{
+ struct gensec_socket *gensec_socket = talloc_get_type(private_data, struct gensec_socket);
+
+ gensec_socket->in_extra_read++;
+ gensec_socket->recv_handler(gensec_socket->recv_private, EVENT_FD_READ);
+ gensec_socket->in_extra_read--;
+
+ /* It may well be that, having run the recv handler, we still
+ * have even more data waiting for us!
+ */
+ if (gensec_socket->read_buffer.length && gensec_socket->recv_handler) {
+ /* Schedule this funcion to run again */
+ event_add_timed(gensec_socket->ev, gensec_socket, timeval_zero(),
+ gensec_socket_trigger_read, gensec_socket);
+ }
+}
+
+/* These two routines could be changed to use a circular buffer of
+ * some kind, or linked lists, or ... */
+static NTSTATUS gensec_socket_recv(struct socket_context *sock, void *buf,
+ size_t wantlen, size_t *nread)
+{
+ struct gensec_socket *gensec_socket = talloc_get_type(sock->private_data, struct gensec_socket);
+
+ if (!gensec_socket->wrap) {
+ return socket_recv(gensec_socket->socket, buf, wantlen, nread);
+ }
+
+ gensec_socket->error = NT_STATUS_OK;
+
+ if (gensec_socket->read_buffer.length == 0) {
+ /* Process any data on the socket, into the read buffer. At
+ * this point, the socket is not available for read any
+ * longer */
+ packet_recv(gensec_socket->packet);
+
+ if (gensec_socket->eof) {
+ *nread = 0;
+ return NT_STATUS_OK;
+ }
+
+ if (!NT_STATUS_IS_OK(gensec_socket->error)) {
+ return gensec_socket->error;
+ }
+
+ if (gensec_socket->read_buffer.length == 0) {
+ /* Clearly we don't have the entire SASL packet yet,
+ * so it has not been written into the buffer */
+ *nread = 0;
+ return STATUS_MORE_ENTRIES;
+ }
+ }
+
+
+ *nread = MIN(wantlen, gensec_socket->read_buffer.length);
+ memcpy(buf, gensec_socket->read_buffer.data, *nread);
+
+ if (gensec_socket->read_buffer.length > *nread) {
+ memmove(gensec_socket->read_buffer.data,
+ gensec_socket->read_buffer.data + *nread,
+ gensec_socket->read_buffer.length - *nread);
+ }
+
+ gensec_socket->read_buffer.length -= *nread;
+ gensec_socket->read_buffer.data = talloc_realloc(gensec_socket,
+ gensec_socket->read_buffer.data,
+ uint8_t,
+ gensec_socket->read_buffer.length);
+
+ if (gensec_socket->read_buffer.length &&
+ gensec_socket->in_extra_read == 0 &&
+ gensec_socket->recv_handler) {
+ /* Manually call a read event, to get this moving
+ * again (as the socket should be dry, so the normal
+ * event handler won't trigger) */
+ event_add_timed(gensec_socket->ev, gensec_socket, timeval_zero(),
+ gensec_socket_trigger_read, gensec_socket);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/* Completed SASL packet callback. When we have a 'whole' SASL
+ * packet, decrypt it, and add it to the read buffer
+ *
+ * This function (and anything under it) MUST NOT call the event system
+ */
+static NTSTATUS gensec_socket_unwrap(void *private_data, DATA_BLOB blob)
+{
+ struct gensec_socket *gensec_socket = talloc_get_type(private_data, struct gensec_socket);
+ DATA_BLOB unwrapped;
+ NTSTATUS nt_status;
+ TALLOC_CTX *mem_ctx;
+ size_t packet_size;
+
+ mem_ctx = talloc_new(gensec_socket);
+ if (!mem_ctx) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ nt_status = gensec_unwrap_packets(gensec_socket->gensec_security,
+ mem_ctx,
+ &blob, &unwrapped,
+ &packet_size);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(mem_ctx);
+ return nt_status;
+ }
+
+ if (packet_size != blob.length) {
+ DEBUG(0, ("gensec_socket_unwrap: Did not consume entire packet!\n"));
+ talloc_free(mem_ctx);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ /* We could change this into a linked list, and have
+ * gensec_socket_recv() and gensec_socket_pending() walk the
+ * linked list */
+
+ if (!data_blob_append(gensec_socket, &gensec_socket->read_buffer,
+ unwrapped.data, unwrapped.length)) {
+ talloc_free(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ talloc_free(mem_ctx);
+ return NT_STATUS_OK;
+}
+
+/* when the data is sent, we know we have not been interrupted */
+static void send_callback(void *private_data)
+{
+ struct gensec_socket *gensec_socket = talloc_get_type(private_data, struct gensec_socket);
+ gensec_socket->interrupted = false;
+}
+
+/*
+ send data, but only as much as we allow in one packet.
+
+ If this returns STATUS_MORE_ENTRIES, the caller must retry with
+ exactly the same data, or a NULL blob.
+*/
+static NTSTATUS gensec_socket_send(struct socket_context *sock,
+ const DATA_BLOB *blob, size_t *sendlen)
+{
+ NTSTATUS nt_status;
+ struct gensec_socket *gensec_socket = talloc_get_type(sock->private_data, struct gensec_socket);
+ DATA_BLOB wrapped;
+ TALLOC_CTX *mem_ctx;
+
+ if (!gensec_socket->wrap) {
+ return socket_send(gensec_socket->socket, blob, sendlen);
+ }
+
+ *sendlen = 0;
+
+ /* We have have been interupted, so the caller should be
+ * giving us the same data again. */
+ if (gensec_socket->interrupted) {
+ packet_queue_run(gensec_socket->packet);
+
+ if (!NT_STATUS_IS_OK(gensec_socket->error)) {
+ return gensec_socket->error;
+ } else if (gensec_socket->interrupted) {
+ return STATUS_MORE_ENTRIES;
+ } else {
+ *sendlen = gensec_socket->orig_send_len;
+ gensec_socket->orig_send_len = 0;
+ return NT_STATUS_OK;
+ }
+ }
+
+ mem_ctx = talloc_new(gensec_socket);
+ if (!mem_ctx) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ nt_status = gensec_wrap_packets(gensec_socket->gensec_security,
+ mem_ctx,
+ blob, &wrapped,
+ &gensec_socket->orig_send_len);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(mem_ctx);
+ return nt_status;
+ }
+
+ gensec_socket->interrupted = true;
+ gensec_socket->error = NT_STATUS_OK;
+
+ nt_status = packet_send_callback(gensec_socket->packet,
+ wrapped,
+ send_callback, gensec_socket);
+
+ talloc_free(mem_ctx);
+
+ packet_queue_run(gensec_socket->packet);
+
+ if (!NT_STATUS_IS_OK(gensec_socket->error)) {
+ return gensec_socket->error;
+ } else if (gensec_socket->interrupted) {
+ return STATUS_MORE_ENTRIES;
+ } else {
+ *sendlen = gensec_socket->orig_send_len;
+ gensec_socket->orig_send_len = 0;
+ return NT_STATUS_OK;
+ }
+}
+
+/* Turn a normal socket into a potentially GENSEC wrapped socket */
+/* CAREFUL: this function will steal 'current_socket' */
+
+NTSTATUS gensec_socket_init(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ struct socket_context *current_socket,
+ struct tevent_context *ev,
+ void (*recv_handler)(void *, uint16_t),
+ void *recv_private,
+ struct socket_context **new_socket)
+{
+ struct gensec_socket *gensec_socket;
+ struct socket_context *new_sock;
+ NTSTATUS nt_status;
+
+ nt_status = socket_create_with_ops(mem_ctx, &gensec_socket_ops, &new_sock,
+ SOCKET_TYPE_STREAM, current_socket->flags | SOCKET_FLAG_ENCRYPT);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ *new_socket = NULL;
+ return nt_status;
+ }
+
+ new_sock->state = current_socket->state;
+
+ gensec_socket = talloc(new_sock, struct gensec_socket);
+ if (gensec_socket == NULL) {
+ *new_socket = NULL;
+ talloc_free(new_sock);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ new_sock->private_data = gensec_socket;
+ gensec_socket->socket = current_socket;
+
+ /* Nothing to do here, if we are not actually wrapping on this socket */
+ if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL) &&
+ !gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) {
+
+ gensec_socket->wrap = false;
+ talloc_steal(gensec_socket, current_socket);
+ *new_socket = new_sock;
+ return NT_STATUS_OK;
+ }
+
+ gensec_socket->gensec_security = gensec_security;
+
+ gensec_socket->wrap = true;
+ gensec_socket->eof = false;
+ gensec_socket->error = NT_STATUS_OK;
+ gensec_socket->interrupted = false;
+ gensec_socket->in_extra_read = 0;
+
+ gensec_socket->read_buffer = data_blob(NULL, 0);
+
+ gensec_socket->recv_handler = recv_handler;
+ gensec_socket->recv_private = recv_private;
+ gensec_socket->ev = ev;
+
+ gensec_socket->packet = packet_init(gensec_socket);
+ if (gensec_socket->packet == NULL) {
+ *new_socket = NULL;
+ talloc_free(new_sock);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ packet_set_private(gensec_socket->packet, gensec_socket);
+ packet_set_socket(gensec_socket->packet, gensec_socket->socket);
+ packet_set_callback(gensec_socket->packet, gensec_socket_unwrap);
+ packet_set_full_request(gensec_socket->packet, gensec_socket_full_request);
+ packet_set_error_handler(gensec_socket->packet, gensec_socket_error_handler);
+ packet_set_serialise(gensec_socket->packet);
+
+ /* TODO: full-request that knows about maximum packet size */
+
+ talloc_steal(gensec_socket, current_socket);
+ *new_socket = new_sock;
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS gensec_socket_set_option(struct socket_context *sock, const char *option, const char *val)
+{
+ set_socket_options(socket_get_fd(sock), option);
+ return NT_STATUS_OK;
+}
+
+static char *gensec_socket_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx)
+{
+ struct gensec_socket *gensec = talloc_get_type(sock->private_data, struct gensec_socket);
+ return socket_get_peer_name(gensec->socket, mem_ctx);
+}
+
+static struct socket_address *gensec_socket_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
+{
+ struct gensec_socket *gensec = talloc_get_type(sock->private_data, struct gensec_socket);
+ return socket_get_peer_addr(gensec->socket, mem_ctx);
+}
+
+static struct socket_address *gensec_socket_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
+{
+ struct gensec_socket *gensec = talloc_get_type(sock->private_data, struct gensec_socket);
+ return socket_get_my_addr(gensec->socket, mem_ctx);
+}
+
+static int gensec_socket_get_fd(struct socket_context *sock)
+{
+ struct gensec_socket *gensec = talloc_get_type(sock->private_data, struct gensec_socket);
+ return socket_get_fd(gensec->socket);
+}
+
+static const struct socket_ops gensec_socket_ops = {
+ .name = "gensec",
+ .fn_init = gensec_socket_init_fn,
+ .fn_recv = gensec_socket_recv,
+ .fn_send = gensec_socket_send,
+ .fn_pending = gensec_socket_pending,
+
+ .fn_set_option = gensec_socket_set_option,
+
+ .fn_get_peer_name = gensec_socket_get_peer_name,
+ .fn_get_peer_addr = gensec_socket_get_peer_addr,
+ .fn_get_my_addr = gensec_socket_get_my_addr,
+ .fn_get_fd = gensec_socket_get_fd
+};
+
diff --git a/source4/auth/gensec/spnego.c b/source4/auth/gensec/spnego.c
new file mode 100644
index 0000000000..e51b215807
--- /dev/null
+++ b/source4/auth/gensec/spnego.c
@@ -0,0 +1,1172 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ RFC2478 Compliant SPNEGO implementation
+
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
+ Copyright (C) Stefan Metzmacher <metze@samba.org> 2004-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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "auth/gensec/spnego.h"
+#include "librpc/gen_ndr/ndr_dcerpc.h"
+#include "auth/credentials/credentials.h"
+#include "auth/gensec/gensec.h"
+#include "auth/gensec/gensec_proto.h"
+
+enum spnego_state_position {
+ SPNEGO_SERVER_START,
+ SPNEGO_CLIENT_START,
+ SPNEGO_SERVER_TARG,
+ SPNEGO_CLIENT_TARG,
+ SPNEGO_FALLBACK,
+ SPNEGO_DONE
+};
+
+struct spnego_state {
+ enum spnego_message_type expected_packet;
+ enum spnego_state_position state_position;
+ struct gensec_security *sub_sec_security;
+ bool no_response_expected;
+
+ const char *neg_oid;
+
+ DATA_BLOB mech_types;
+};
+
+
+static NTSTATUS gensec_spnego_client_start(struct gensec_security *gensec_security)
+{
+ struct spnego_state *spnego_state;
+
+ spnego_state = talloc(gensec_security, struct spnego_state);
+ if (!spnego_state) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ spnego_state->expected_packet = SPNEGO_NEG_TOKEN_INIT;
+ spnego_state->state_position = SPNEGO_CLIENT_START;
+ spnego_state->sub_sec_security = NULL;
+ spnego_state->no_response_expected = false;
+ spnego_state->mech_types = data_blob(NULL, 0);
+
+ gensec_security->private_data = spnego_state;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS gensec_spnego_server_start(struct gensec_security *gensec_security)
+{
+ struct spnego_state *spnego_state;
+
+ spnego_state = talloc(gensec_security, struct spnego_state);
+ if (!spnego_state) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ spnego_state->expected_packet = SPNEGO_NEG_TOKEN_INIT;
+ spnego_state->state_position = SPNEGO_SERVER_START;
+ spnego_state->sub_sec_security = NULL;
+ spnego_state->no_response_expected = false;
+ spnego_state->mech_types = data_blob(NULL, 0);
+
+ gensec_security->private_data = spnego_state;
+ return NT_STATUS_OK;
+}
+
+/*
+ wrappers for the spnego_*() functions
+*/
+static NTSTATUS gensec_spnego_unseal_packet(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ uint8_t *data, size_t length,
+ const uint8_t *whole_pdu, size_t pdu_length,
+ const DATA_BLOB *sig)
+{
+ struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
+
+ if (spnego_state->state_position != SPNEGO_DONE
+ && spnego_state->state_position != SPNEGO_FALLBACK) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ return gensec_unseal_packet(spnego_state->sub_sec_security,
+ mem_ctx,
+ data, length,
+ whole_pdu, pdu_length,
+ sig);
+}
+
+static NTSTATUS gensec_spnego_check_packet(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ const uint8_t *data, size_t length,
+ const uint8_t *whole_pdu, size_t pdu_length,
+ const DATA_BLOB *sig)
+{
+ struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
+
+ if (spnego_state->state_position != SPNEGO_DONE
+ && spnego_state->state_position != SPNEGO_FALLBACK) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ return gensec_check_packet(spnego_state->sub_sec_security,
+ mem_ctx,
+ data, length,
+ whole_pdu, pdu_length,
+ sig);
+}
+
+static NTSTATUS gensec_spnego_seal_packet(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ uint8_t *data, size_t length,
+ const uint8_t *whole_pdu, size_t pdu_length,
+ DATA_BLOB *sig)
+{
+ struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
+
+ if (spnego_state->state_position != SPNEGO_DONE
+ && spnego_state->state_position != SPNEGO_FALLBACK) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ return gensec_seal_packet(spnego_state->sub_sec_security,
+ mem_ctx,
+ data, length,
+ whole_pdu, pdu_length,
+ sig);
+}
+
+static NTSTATUS gensec_spnego_sign_packet(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ const uint8_t *data, size_t length,
+ const uint8_t *whole_pdu, size_t pdu_length,
+ DATA_BLOB *sig)
+{
+ struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
+
+ if (spnego_state->state_position != SPNEGO_DONE
+ && spnego_state->state_position != SPNEGO_FALLBACK) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ return gensec_sign_packet(spnego_state->sub_sec_security,
+ mem_ctx,
+ data, length,
+ whole_pdu, pdu_length,
+ sig);
+}
+
+static NTSTATUS gensec_spnego_wrap(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *in,
+ DATA_BLOB *out)
+{
+ struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
+
+ if (spnego_state->state_position != SPNEGO_DONE
+ && spnego_state->state_position != SPNEGO_FALLBACK) {
+ DEBUG(1, ("gensec_spnego_wrap: wrong state for wrap\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ return gensec_wrap(spnego_state->sub_sec_security,
+ mem_ctx, in, out);
+}
+
+static NTSTATUS gensec_spnego_unwrap(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *in,
+ DATA_BLOB *out)
+{
+ struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
+
+ if (spnego_state->state_position != SPNEGO_DONE
+ && spnego_state->state_position != SPNEGO_FALLBACK) {
+ DEBUG(1, ("gensec_spnego_unwrap: wrong state for unwrap\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ return gensec_unwrap(spnego_state->sub_sec_security,
+ mem_ctx, in, out);
+}
+
+static NTSTATUS gensec_spnego_wrap_packets(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *in,
+ DATA_BLOB *out,
+ size_t *len_processed)
+{
+ struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
+
+ if (spnego_state->state_position != SPNEGO_DONE
+ && spnego_state->state_position != SPNEGO_FALLBACK) {
+ DEBUG(1, ("gensec_spnego_wrap: wrong state for wrap\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ return gensec_wrap_packets(spnego_state->sub_sec_security,
+ mem_ctx, in, out,
+ len_processed);
+}
+
+static NTSTATUS gensec_spnego_packet_full_request(struct gensec_security *gensec_security,
+ DATA_BLOB blob, size_t *size)
+{
+ struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
+
+ if (spnego_state->state_position != SPNEGO_DONE
+ && spnego_state->state_position != SPNEGO_FALLBACK) {
+ DEBUG(1, ("gensec_spnego_unwrap: wrong state for unwrap\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ return gensec_packet_full_request(spnego_state->sub_sec_security,
+ blob, size);
+}
+
+static NTSTATUS gensec_spnego_unwrap_packets(struct gensec_security *gensec_security,
+ TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *in,
+ DATA_BLOB *out,
+ size_t *len_processed)
+{
+ struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
+
+ if (spnego_state->state_position != SPNEGO_DONE
+ && spnego_state->state_position != SPNEGO_FALLBACK) {
+ DEBUG(1, ("gensec_spnego_unwrap: wrong state for unwrap\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ return gensec_unwrap_packets(spnego_state->sub_sec_security,
+ mem_ctx, in, out,
+ len_processed);
+}
+
+static size_t gensec_spnego_sig_size(struct gensec_security *gensec_security, size_t data_size)
+{
+ struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
+
+ if (spnego_state->state_position != SPNEGO_DONE
+ && spnego_state->state_position != SPNEGO_FALLBACK) {
+ return 0;
+ }
+
+ return gensec_sig_size(spnego_state->sub_sec_security, data_size);
+}
+
+static size_t gensec_spnego_max_input_size(struct gensec_security *gensec_security)
+{
+ struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
+
+ if (spnego_state->state_position != SPNEGO_DONE
+ && spnego_state->state_position != SPNEGO_FALLBACK) {
+ return 0;
+ }
+
+ return gensec_max_input_size(spnego_state->sub_sec_security);
+}
+
+static size_t gensec_spnego_max_wrapped_size(struct gensec_security *gensec_security)
+{
+ struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
+
+ if (spnego_state->state_position != SPNEGO_DONE
+ && spnego_state->state_position != SPNEGO_FALLBACK) {
+ return 0;
+ }
+
+ return gensec_max_wrapped_size(spnego_state->sub_sec_security);
+}
+
+static NTSTATUS gensec_spnego_session_key(struct gensec_security *gensec_security,
+ DATA_BLOB *session_key)
+{
+ struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
+ if (!spnego_state->sub_sec_security) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ return gensec_session_key(spnego_state->sub_sec_security,
+ session_key);
+}
+
+static NTSTATUS gensec_spnego_session_info(struct gensec_security *gensec_security,
+ struct auth_session_info **session_info)
+{
+ struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
+ if (!spnego_state->sub_sec_security) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ return gensec_session_info(spnego_state->sub_sec_security,
+ session_info);
+}
+
+/** Fallback to another GENSEC mechanism, based on magic strings
+ *
+ * This is the 'fallback' case, where we don't get SPNEGO, and have to
+ * try all the other options (and hope they all have a magic string
+ * they check)
+*/
+
+static NTSTATUS gensec_spnego_server_try_fallback(struct gensec_security *gensec_security,
+ struct spnego_state *spnego_state,
+ TALLOC_CTX *out_mem_ctx,
+ const DATA_BLOB in, DATA_BLOB *out)
+{
+ int i,j;
+ struct gensec_security_ops **all_ops
+ = gensec_security_mechs(gensec_security, out_mem_ctx);
+ for (i=0; all_ops[i]; i++) {
+ bool is_spnego;
+ NTSTATUS nt_status;
+
+ if (gensec_security != NULL &&
+ !gensec_security_ops_enabled(all_ops[i], gensec_security->settings->lp_ctx))
+ continue;
+
+ if (!all_ops[i]->oid) {
+ continue;
+ }
+
+ is_spnego = false;
+ for (j=0; all_ops[i]->oid[j]; j++) {
+ if (strcasecmp(GENSEC_OID_SPNEGO,all_ops[i]->oid[j]) == 0) {
+ is_spnego = true;
+ }
+ }
+ if (is_spnego) {
+ continue;
+ }
+
+ if (!all_ops[i]->magic) {
+ continue;
+ }
+
+ nt_status = all_ops[i]->magic(gensec_security, &in);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ continue;
+ }
+
+ spnego_state->state_position = SPNEGO_FALLBACK;
+
+ nt_status = gensec_subcontext_start(spnego_state,
+ gensec_security,
+ &spnego_state->sub_sec_security);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+ /* select the sub context */
+ nt_status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
+ all_ops[i]);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+ nt_status = gensec_update(spnego_state->sub_sec_security,
+ out_mem_ctx, in, out);
+ return nt_status;
+ }
+ DEBUG(1, ("Failed to parse SPNEGO request\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+
+}
+
+/*
+ Parse the netTokenInit, either from the client, to the server, or
+ from the server to the client.
+*/
+
+static NTSTATUS gensec_spnego_parse_negTokenInit(struct gensec_security *gensec_security,
+ struct spnego_state *spnego_state,
+ TALLOC_CTX *out_mem_ctx,
+ const char **mechType,
+ const DATA_BLOB unwrapped_in, DATA_BLOB *unwrapped_out)
+{
+ int i;
+ NTSTATUS nt_status = NT_STATUS_INVALID_PARAMETER;
+ DATA_BLOB null_data_blob = data_blob(NULL,0);
+ bool ok;
+
+ const struct gensec_security_ops_wrapper *all_sec
+ = gensec_security_by_oid_list(gensec_security,
+ out_mem_ctx,
+ mechType,
+ GENSEC_OID_SPNEGO);
+
+ ok = spnego_write_mech_types(spnego_state,
+ mechType,
+ &spnego_state->mech_types);
+ if (!ok) {
+ DEBUG(1, ("SPNEGO: Failed to write mechTypes\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (spnego_state->state_position == SPNEGO_SERVER_START) {
+ for (i=0; all_sec && all_sec[i].op; i++) {
+ /* optomisitic token */
+ if (strcmp(all_sec[i].oid, mechType[0]) == 0) {
+ nt_status = gensec_subcontext_start(spnego_state,
+ gensec_security,
+ &spnego_state->sub_sec_security);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+ /* select the sub context */
+ nt_status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
+ all_sec[i].op);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(spnego_state->sub_sec_security);
+ spnego_state->sub_sec_security = NULL;
+ break;
+ }
+
+ nt_status = gensec_update(spnego_state->sub_sec_security,
+ out_mem_ctx,
+ unwrapped_in,
+ unwrapped_out);
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_INVALID_PARAMETER) ||
+ NT_STATUS_EQUAL(nt_status, NT_STATUS_CANT_ACCESS_DOMAIN_INFO)) {
+ /* Pretend we never started it (lets the first run find some incompatible demand) */
+
+ DEBUG(1, ("SPNEGO(%s) NEG_TOKEN_INIT failed to parse contents: %s\n",
+ spnego_state->sub_sec_security->ops->name, nt_errstr(nt_status)));
+ talloc_free(spnego_state->sub_sec_security);
+ spnego_state->sub_sec_security = NULL;
+ break;
+ }
+
+ spnego_state->neg_oid = all_sec[i].oid;
+ break;
+ }
+ }
+ }
+
+ /* Having tried any optomisitc token from the client (if we
+ * were the server), if we didn't get anywhere, walk our list
+ * in our preference order */
+
+ if (!spnego_state->sub_sec_security) {
+ for (i=0; all_sec && all_sec[i].op; i++) {
+ nt_status = gensec_subcontext_start(spnego_state,
+ gensec_security,
+ &spnego_state->sub_sec_security);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+ /* select the sub context */
+ nt_status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
+ all_sec[i].op);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(spnego_state->sub_sec_security);
+ spnego_state->sub_sec_security = NULL;
+ continue;
+ }
+
+ spnego_state->neg_oid = all_sec[i].oid;
+
+ /* only get the helping start blob for the first OID */
+ nt_status = gensec_update(spnego_state->sub_sec_security,
+ out_mem_ctx,
+ null_data_blob,
+ unwrapped_out);
+
+ /* it is likely that a NULL input token will
+ * not be liked by most server mechs, but if
+ * we are in the client, we want the first
+ * update packet to be able to abort the use
+ * of this mech */
+ if (spnego_state->state_position != SPNEGO_SERVER_START) {
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_INVALID_PARAMETER) ||
+ NT_STATUS_EQUAL(nt_status, NT_STATUS_CANT_ACCESS_DOMAIN_INFO)) {
+ /* Pretend we never started it (lets the first run find some incompatible demand) */
+
+ DEBUG(1, ("SPNEGO(%s) NEG_TOKEN_INIT failed to parse: %s\n",
+ spnego_state->sub_sec_security->ops->name, nt_errstr(nt_status)));
+ talloc_free(spnego_state->sub_sec_security);
+ spnego_state->sub_sec_security = NULL;
+ continue;
+ }
+ }
+
+ break;
+ }
+ }
+
+ if (spnego_state->sub_sec_security) {
+ /* it is likely that a NULL input token will
+ * not be liked by most server mechs, but this
+ * does the right thing in the CIFS client.
+ * just push us along the merry-go-round
+ * again, and hope for better luck next
+ * time */
+
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_INVALID_PARAMETER)) {
+ *unwrapped_out = data_blob(NULL, 0);
+ nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
+ }
+
+ if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_INVALID_PARAMETER)
+ && !NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)
+ && !NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(1, ("SPNEGO(%s) NEG_TOKEN_INIT failed: %s\n",
+ spnego_state->sub_sec_security->ops->name, nt_errstr(nt_status)));
+ talloc_free(spnego_state->sub_sec_security);
+ spnego_state->sub_sec_security = NULL;
+
+ /* We started the mech correctly, and the
+ * input from the other side was valid.
+ * Return the error (say bad password, invalid
+ * ticket) */
+ return nt_status;
+ }
+
+
+ return nt_status; /* OK, INVALID_PARAMETER ore MORE PROCESSING */
+ }
+
+ DEBUG(1, ("SPNEGO: Could not find a suitable mechtype in NEG_TOKEN_INIT\n"));
+ /* we could re-negotiate here, but it would only work
+ * if the client or server lied about what it could
+ * support the first time. Lets keep this code to
+ * reality */
+
+ return nt_status;
+}
+
+/** create a negTokenInit
+ *
+ * This is the same packet, no matter if the client or server sends it first, but it is always the first packet
+*/
+static NTSTATUS gensec_spnego_create_negTokenInit(struct gensec_security *gensec_security,
+ struct spnego_state *spnego_state,
+ TALLOC_CTX *out_mem_ctx,
+ const DATA_BLOB in, DATA_BLOB *out)
+{
+ int i;
+ NTSTATUS nt_status = NT_STATUS_INVALID_PARAMETER;
+ DATA_BLOB null_data_blob = data_blob(NULL,0);
+ const char **mechTypes = NULL;
+ DATA_BLOB unwrapped_out = data_blob(NULL, 0);
+ const struct gensec_security_ops_wrapper *all_sec;
+ const char *principal = NULL;
+
+ mechTypes = gensec_security_oids(gensec_security,
+ out_mem_ctx, GENSEC_OID_SPNEGO);
+
+ all_sec = gensec_security_by_oid_list(gensec_security,
+ out_mem_ctx,
+ mechTypes,
+ GENSEC_OID_SPNEGO);
+ for (i=0; all_sec && all_sec[i].op; i++) {
+ struct spnego_data spnego_out;
+ const char **send_mech_types;
+ bool ok;
+
+ nt_status = gensec_subcontext_start(spnego_state,
+ gensec_security,
+ &spnego_state->sub_sec_security);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+ /* select the sub context */
+ nt_status = gensec_start_mech_by_ops(spnego_state->sub_sec_security,
+ all_sec[i].op);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(spnego_state->sub_sec_security);
+ spnego_state->sub_sec_security = NULL;
+ continue;
+ }
+
+ /* In the client, try and produce the first (optimistic) packet */
+ if (spnego_state->state_position == SPNEGO_CLIENT_START) {
+ nt_status = gensec_update(spnego_state->sub_sec_security,
+ out_mem_ctx,
+ null_data_blob,
+ &unwrapped_out);
+
+ if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)
+ && !NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(1, ("SPNEGO(%s) creating NEG_TOKEN_INIT failed: %s\n",
+ spnego_state->sub_sec_security->ops->name, nt_errstr(nt_status)));
+ talloc_free(spnego_state->sub_sec_security);
+ spnego_state->sub_sec_security = NULL;
+ /* Pretend we never started it (lets the first run find some incompatible demand) */
+
+ continue;
+ }
+ }
+
+ spnego_out.type = SPNEGO_NEG_TOKEN_INIT;
+
+ send_mech_types = gensec_security_oids_from_ops_wrapped(out_mem_ctx,
+ &all_sec[i]);
+
+ ok = spnego_write_mech_types(spnego_state,
+ send_mech_types,
+ &spnego_state->mech_types);
+ if (!ok) {
+ DEBUG(1, ("SPNEGO: Failed to write mechTypes\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* List the remaining mechs as options */
+ spnego_out.negTokenInit.mechTypes = send_mech_types;
+ spnego_out.negTokenInit.reqFlags = 0;
+
+ if (spnego_state->state_position == SPNEGO_SERVER_START) {
+ /* server credentials */
+ struct cli_credentials *creds = gensec_get_credentials(gensec_security);
+ if (creds) {
+ principal = cli_credentials_get_principal(creds, out_mem_ctx);
+ }
+ }
+ if (principal) {
+ spnego_out.negTokenInit.mechListMIC
+ = data_blob_string_const(principal);
+ } else {
+ spnego_out.negTokenInit.mechListMIC = null_data_blob;
+ }
+
+ spnego_out.negTokenInit.mechToken = unwrapped_out;
+
+ if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
+ DEBUG(1, ("Failed to write NEG_TOKEN_INIT\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* set next state */
+ spnego_state->neg_oid = all_sec[i].oid;
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ spnego_state->no_response_expected = true;
+ }
+
+ return NT_STATUS_MORE_PROCESSING_REQUIRED;
+ }
+ talloc_free(spnego_state->sub_sec_security);
+ spnego_state->sub_sec_security = NULL;
+
+ DEBUG(1, ("Failed to setup SPNEGO negTokenInit request: %s\n", nt_errstr(nt_status)));
+ return NT_STATUS_INVALID_PARAMETER;
+}
+
+
+/** create a server negTokenTarg
+ *
+ * This is the case, where the client is the first one who sends data
+*/
+
+static NTSTATUS gensec_spnego_server_negTokenTarg(struct gensec_security *gensec_security,
+ struct spnego_state *spnego_state,
+ TALLOC_CTX *out_mem_ctx,
+ NTSTATUS nt_status,
+ const DATA_BLOB unwrapped_out,
+ DATA_BLOB mech_list_mic,
+ DATA_BLOB *out)
+{
+ struct spnego_data spnego_out;
+ DATA_BLOB null_data_blob = data_blob(NULL, 0);
+
+ /* compose reply */
+ spnego_out.type = SPNEGO_NEG_TOKEN_TARG;
+ spnego_out.negTokenTarg.responseToken = unwrapped_out;
+ spnego_out.negTokenTarg.mechListMIC = null_data_blob;
+ spnego_out.negTokenTarg.supportedMech = NULL;
+
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ spnego_out.negTokenTarg.supportedMech = spnego_state->neg_oid;
+ spnego_out.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
+ spnego_state->state_position = SPNEGO_SERVER_TARG;
+ } else if (NT_STATUS_IS_OK(nt_status)) {
+ if (unwrapped_out.data) {
+ spnego_out.negTokenTarg.supportedMech = spnego_state->neg_oid;
+ }
+ spnego_out.negTokenTarg.negResult = SPNEGO_ACCEPT_COMPLETED;
+ spnego_out.negTokenTarg.mechListMIC = mech_list_mic;
+ spnego_state->state_position = SPNEGO_DONE;
+ } else {
+ spnego_out.negTokenTarg.negResult = SPNEGO_REJECT;
+ DEBUG(2, ("SPNEGO login failed: %s\n", nt_errstr(nt_status)));
+ spnego_state->state_position = SPNEGO_DONE;
+ }
+
+ if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
+ DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
+
+ return nt_status;
+}
+
+
+static NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx,
+ const DATA_BLOB in, DATA_BLOB *out)
+{
+ struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
+ DATA_BLOB null_data_blob = data_blob(NULL, 0);
+ DATA_BLOB mech_list_mic = data_blob(NULL, 0);
+ DATA_BLOB unwrapped_out = data_blob(NULL, 0);
+ struct spnego_data spnego_out;
+ struct spnego_data spnego;
+
+ ssize_t len;
+
+ *out = data_blob(NULL, 0);
+
+ if (!out_mem_ctx) {
+ out_mem_ctx = spnego_state;
+ }
+
+ /* and switch into the state machine */
+
+ switch (spnego_state->state_position) {
+ case SPNEGO_FALLBACK:
+ return gensec_update(spnego_state->sub_sec_security,
+ out_mem_ctx, in, out);
+ case SPNEGO_SERVER_START:
+ {
+ NTSTATUS nt_status;
+ if (in.length) {
+
+ len = spnego_read_data(gensec_security, in, &spnego);
+ if (len == -1) {
+ return gensec_spnego_server_try_fallback(gensec_security, spnego_state,
+ out_mem_ctx, in, out);
+ }
+ /* client sent NegTargetInit, we send NegTokenTarg */
+
+ /* OK, so it's real SPNEGO, check the packet's the one we expect */
+ if (spnego.type != spnego_state->expected_packet) {
+ DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type,
+ spnego_state->expected_packet));
+ dump_data(1, in.data, in.length);
+ spnego_free_data(&spnego);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ nt_status = gensec_spnego_parse_negTokenInit(gensec_security,
+ spnego_state,
+ out_mem_ctx,
+ spnego.negTokenInit.mechTypes,
+ spnego.negTokenInit.mechToken,
+ &unwrapped_out);
+
+ nt_status = gensec_spnego_server_negTokenTarg(gensec_security,
+ spnego_state,
+ out_mem_ctx,
+ nt_status,
+ unwrapped_out,
+ null_data_blob,
+ out);
+
+ spnego_free_data(&spnego);
+
+ return nt_status;
+ } else {
+ nt_status = gensec_spnego_create_negTokenInit(gensec_security, spnego_state,
+ out_mem_ctx, in, out);
+ spnego_state->state_position = SPNEGO_SERVER_START;
+ spnego_state->expected_packet = SPNEGO_NEG_TOKEN_INIT;
+ return nt_status;
+ }
+ }
+
+ case SPNEGO_CLIENT_START:
+ {
+ /* The server offers a list of mechanisms */
+
+ const char *my_mechs[] = {NULL, NULL};
+ NTSTATUS nt_status = NT_STATUS_INVALID_PARAMETER;
+
+ if (!in.length) {
+ /* client to produce negTokenInit */
+ nt_status = gensec_spnego_create_negTokenInit(gensec_security, spnego_state,
+ out_mem_ctx, in, out);
+ spnego_state->state_position = SPNEGO_CLIENT_TARG;
+ spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
+ return nt_status;
+ }
+
+ len = spnego_read_data(gensec_security, in, &spnego);
+
+ if (len == -1) {
+ DEBUG(1, ("Invalid SPNEGO request:\n"));
+ dump_data(1, in.data, in.length);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* OK, so it's real SPNEGO, check the packet's the one we expect */
+ if (spnego.type != spnego_state->expected_packet) {
+ DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type,
+ spnego_state->expected_packet));
+ dump_data(1, in.data, in.length);
+ spnego_free_data(&spnego);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (spnego.negTokenInit.targetPrincipal) {
+ DEBUG(5, ("Server claims it's principal name is %s\n", spnego.negTokenInit.targetPrincipal));
+ gensec_set_target_principal(gensec_security, spnego.negTokenInit.targetPrincipal);
+ }
+
+ nt_status = gensec_spnego_parse_negTokenInit(gensec_security,
+ spnego_state,
+ out_mem_ctx,
+ spnego.negTokenInit.mechTypes,
+ spnego.negTokenInit.mechToken,
+ &unwrapped_out);
+
+ if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED) && !NT_STATUS_IS_OK(nt_status)) {
+ spnego_free_data(&spnego);
+ return nt_status;
+ }
+
+ my_mechs[0] = spnego_state->neg_oid;
+ /* compose reply */
+ spnego_out.type = SPNEGO_NEG_TOKEN_INIT;
+ spnego_out.negTokenInit.mechTypes = my_mechs;
+ spnego_out.negTokenInit.reqFlags = 0;
+ spnego_out.negTokenInit.mechListMIC = null_data_blob;
+ spnego_out.negTokenInit.mechToken = unwrapped_out;
+
+ if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
+ DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_INIT\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* set next state */
+ spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
+ spnego_state->state_position = SPNEGO_CLIENT_TARG;
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ spnego_state->no_response_expected = true;
+ }
+
+ spnego_free_data(&spnego);
+ return NT_STATUS_MORE_PROCESSING_REQUIRED;
+ }
+ case SPNEGO_SERVER_TARG:
+ {
+ NTSTATUS nt_status;
+ bool new_spnego = false;
+
+ if (!in.length) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ len = spnego_read_data(gensec_security, in, &spnego);
+
+ if (len == -1) {
+ DEBUG(1, ("Invalid SPNEGO request:\n"));
+ dump_data(1, in.data, in.length);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* OK, so it's real SPNEGO, check the packet's the one we expect */
+ if (spnego.type != spnego_state->expected_packet) {
+ DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type,
+ spnego_state->expected_packet));
+ dump_data(1, in.data, in.length);
+ spnego_free_data(&spnego);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!spnego_state->sub_sec_security) {
+ DEBUG(1, ("SPNEGO: Did not setup a mech in NEG_TOKEN_INIT\n"));
+ spnego_free_data(&spnego);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ nt_status = gensec_update(spnego_state->sub_sec_security,
+ out_mem_ctx,
+ spnego.negTokenTarg.responseToken,
+ &unwrapped_out);
+ if (NT_STATUS_IS_OK(nt_status) && spnego.negTokenTarg.mechListMIC.length > 0) {
+ new_spnego = true;
+ nt_status = gensec_check_packet(spnego_state->sub_sec_security,
+ out_mem_ctx,
+ spnego_state->mech_types.data,
+ spnego_state->mech_types.length,
+ spnego_state->mech_types.data,
+ spnego_state->mech_types.length,
+ &spnego.negTokenTarg.mechListMIC);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(2,("GENSEC SPNEGO: failed to verify mechListMIC: %s\n",
+ nt_errstr(nt_status)));
+ }
+ }
+ if (NT_STATUS_IS_OK(nt_status) && new_spnego) {
+ nt_status = gensec_sign_packet(spnego_state->sub_sec_security,
+ out_mem_ctx,
+ spnego_state->mech_types.data,
+ spnego_state->mech_types.length,
+ spnego_state->mech_types.data,
+ spnego_state->mech_types.length,
+ &mech_list_mic);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(2,("GENSEC SPNEGO: failed to sign mechListMIC: %s\n",
+ nt_errstr(nt_status)));
+ }
+ }
+
+ nt_status = gensec_spnego_server_negTokenTarg(gensec_security,
+ spnego_state,
+ out_mem_ctx,
+ nt_status,
+ unwrapped_out,
+ mech_list_mic,
+ out);
+
+ spnego_free_data(&spnego);
+
+ return nt_status;
+ }
+ case SPNEGO_CLIENT_TARG:
+ {
+ NTSTATUS nt_status;
+ if (!in.length) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ len = spnego_read_data(gensec_security, in, &spnego);
+
+ if (len == -1) {
+ DEBUG(1, ("Invalid SPNEGO request:\n"));
+ dump_data(1, in.data, in.length);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* OK, so it's real SPNEGO, check the packet's the one we expect */
+ if (spnego.type != spnego_state->expected_packet) {
+ DEBUG(1, ("Invalid SPNEGO request: %d, expected %d\n", spnego.type,
+ spnego_state->expected_packet));
+ dump_data(1, in.data, in.length);
+ spnego_free_data(&spnego);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (spnego.negTokenTarg.negResult == SPNEGO_REJECT) {
+ spnego_free_data(&spnego);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* Server didn't like our choice of mech, and chose something else */
+ if ((spnego.negTokenTarg.negResult == SPNEGO_ACCEPT_INCOMPLETE) &&
+ spnego.negTokenTarg.supportedMech &&
+ strcmp(spnego.negTokenTarg.supportedMech, spnego_state->neg_oid) != 0) {
+ DEBUG(3,("GENSEC SPNEGO: client preferred mech (%s) not accepted, server wants: %s\n",
+ gensec_get_name_by_oid(gensec_security, spnego.negTokenTarg.supportedMech),
+ gensec_get_name_by_oid(gensec_security, spnego_state->neg_oid)));
+
+ talloc_free(spnego_state->sub_sec_security);
+ nt_status = gensec_subcontext_start(spnego_state,
+ gensec_security,
+ &spnego_state->sub_sec_security);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ spnego_free_data(&spnego);
+ return nt_status;
+ }
+ /* select the sub context */
+ nt_status = gensec_start_mech_by_oid(spnego_state->sub_sec_security,
+ spnego.negTokenTarg.supportedMech);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ spnego_free_data(&spnego);
+ return nt_status;
+ }
+
+ nt_status = gensec_update(spnego_state->sub_sec_security,
+ out_mem_ctx,
+ spnego.negTokenTarg.responseToken,
+ &unwrapped_out);
+ spnego_state->neg_oid = talloc_strdup(spnego_state, spnego.negTokenTarg.supportedMech);
+ } else if (spnego_state->no_response_expected) {
+ if (spnego.negTokenTarg.negResult != SPNEGO_ACCEPT_COMPLETED) {
+ DEBUG(3,("GENSEC SPNEGO: client GENSEC accepted, but server rejected (bad password?)\n"));
+ nt_status = NT_STATUS_INVALID_PARAMETER;
+ } else if (spnego.negTokenTarg.responseToken.length) {
+ DEBUG(2,("GENSEC SPNEGO: client GENSEC accepted, but server continued negotiation!\n"));
+ nt_status = NT_STATUS_INVALID_PARAMETER;
+ } else {
+ nt_status = NT_STATUS_OK;
+ }
+ if (NT_STATUS_IS_OK(nt_status) && spnego.negTokenTarg.mechListMIC.length > 0) {
+ nt_status = gensec_check_packet(spnego_state->sub_sec_security,
+ out_mem_ctx,
+ spnego_state->mech_types.data,
+ spnego_state->mech_types.length,
+ spnego_state->mech_types.data,
+ spnego_state->mech_types.length,
+ &spnego.negTokenTarg.mechListMIC);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(2,("GENSEC SPNEGO: failed to verify mechListMIC: %s\n",
+ nt_errstr(nt_status)));
+ }
+ }
+ } else {
+ bool new_spnego = false;
+
+ nt_status = gensec_update(spnego_state->sub_sec_security,
+ out_mem_ctx,
+ spnego.negTokenTarg.responseToken,
+ &unwrapped_out);
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ new_spnego = gensec_have_feature(spnego_state->sub_sec_security,
+ GENSEC_FEATURE_NEW_SPNEGO);
+ }
+ if (NT_STATUS_IS_OK(nt_status) && new_spnego) {
+ nt_status = gensec_sign_packet(spnego_state->sub_sec_security,
+ out_mem_ctx,
+ spnego_state->mech_types.data,
+ spnego_state->mech_types.length,
+ spnego_state->mech_types.data,
+ spnego_state->mech_types.length,
+ &mech_list_mic);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(2,("GENSEC SPNEGO: failed to sign mechListMIC: %s\n",
+ nt_errstr(nt_status)));
+ }
+ }
+ if (NT_STATUS_IS_OK(nt_status)) {
+ spnego_state->no_response_expected = true;
+ }
+ }
+
+ spnego_free_data(&spnego);
+
+ if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)
+ && !NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(1, ("SPNEGO(%s) login failed: %s\n",
+ spnego_state->sub_sec_security->ops->name,
+ nt_errstr(nt_status)));
+ return nt_status;
+ }
+
+ if (unwrapped_out.length) {
+ /* compose reply */
+ spnego_out.type = SPNEGO_NEG_TOKEN_TARG;
+ spnego_out.negTokenTarg.negResult = SPNEGO_NONE_RESULT;
+ spnego_out.negTokenTarg.supportedMech = NULL;
+ spnego_out.negTokenTarg.responseToken = unwrapped_out;
+ spnego_out.negTokenTarg.mechListMIC = mech_list_mic;
+
+ if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
+ DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ spnego_state->state_position = SPNEGO_CLIENT_TARG;
+ nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
+ } else {
+
+ /* all done - server has accepted, and we agree */
+ *out = null_data_blob;
+
+ if (spnego.negTokenTarg.negResult != SPNEGO_ACCEPT_COMPLETED) {
+ /* unless of course it did not accept */
+ DEBUG(1,("gensec_update ok but not accepted\n"));
+ nt_status = NT_STATUS_INVALID_PARAMETER;
+ }
+
+ spnego_state->state_position = SPNEGO_DONE;
+ }
+
+ return nt_status;
+ }
+ case SPNEGO_DONE:
+ /* We should not be called after we are 'done' */
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ return NT_STATUS_INVALID_PARAMETER;
+}
+
+static void gensec_spnego_want_feature(struct gensec_security *gensec_security,
+ uint32_t feature)
+{
+ struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
+
+ if (!spnego_state || !spnego_state->sub_sec_security) {
+ gensec_security->want_features |= feature;
+ return;
+ }
+
+ gensec_want_feature(spnego_state->sub_sec_security,
+ feature);
+}
+
+static bool gensec_spnego_have_feature(struct gensec_security *gensec_security,
+ uint32_t feature)
+{
+ struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
+ if (!spnego_state->sub_sec_security) {
+ return false;
+ }
+
+ return gensec_have_feature(spnego_state->sub_sec_security,
+ feature);
+}
+
+static const char *gensec_spnego_oids[] = {
+ GENSEC_OID_SPNEGO,
+ NULL
+};
+
+static const struct gensec_security_ops gensec_spnego_security_ops = {
+ .name = "spnego",
+ .sasl_name = "GSS-SPNEGO",
+ .auth_type = DCERPC_AUTH_TYPE_SPNEGO,
+ .oid = gensec_spnego_oids,
+ .client_start = gensec_spnego_client_start,
+ .server_start = gensec_spnego_server_start,
+ .update = gensec_spnego_update,
+ .seal_packet = gensec_spnego_seal_packet,
+ .sign_packet = gensec_spnego_sign_packet,
+ .sig_size = gensec_spnego_sig_size,
+ .max_wrapped_size = gensec_spnego_max_wrapped_size,
+ .max_input_size = gensec_spnego_max_input_size,
+ .check_packet = gensec_spnego_check_packet,
+ .unseal_packet = gensec_spnego_unseal_packet,
+ .packet_full_request = gensec_spnego_packet_full_request,
+ .wrap = gensec_spnego_wrap,
+ .unwrap = gensec_spnego_unwrap,
+ .wrap_packets = gensec_spnego_wrap_packets,
+ .unwrap_packets = gensec_spnego_unwrap_packets,
+ .session_key = gensec_spnego_session_key,
+ .session_info = gensec_spnego_session_info,
+ .want_feature = gensec_spnego_want_feature,
+ .have_feature = gensec_spnego_have_feature,
+ .enabled = true,
+ .priority = GENSEC_SPNEGO
+};
+
+_PUBLIC_ NTSTATUS gensec_spnego_init(void)
+{
+ NTSTATUS ret;
+ ret = gensec_register(&gensec_spnego_security_ops);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("Failed to register '%s' gensec backend!\n",
+ gensec_spnego_security_ops.name));
+ return ret;
+ }
+
+ return ret;
+}
diff --git a/source4/auth/gensec/spnego.h b/source4/auth/gensec/spnego.h
new file mode 100644
index 0000000000..24e80ecb0b
--- /dev/null
+++ b/source4/auth/gensec/spnego.h
@@ -0,0 +1,65 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ RFC2478 Compliant SPNEGO implementation
+
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#define SPNEGO_DELEG_FLAG 0x01
+#define SPNEGO_MUTUAL_FLAG 0x02
+#define SPNEGO_REPLAY_FLAG 0x04
+#define SPNEGO_SEQUENCE_FLAG 0x08
+#define SPNEGO_ANON_FLAG 0x10
+#define SPNEGO_CONF_FLAG 0x20
+#define SPNEGO_INTEG_FLAG 0x40
+#define SPNEGO_REQ_FLAG 0x80
+
+enum spnego_negResult {
+ SPNEGO_ACCEPT_COMPLETED = 0,
+ SPNEGO_ACCEPT_INCOMPLETE = 1,
+ SPNEGO_REJECT = 2,
+ SPNEGO_NONE_RESULT = 3
+};
+
+struct spnego_negTokenInit {
+ const char **mechTypes;
+ int reqFlags;
+ DATA_BLOB mechToken;
+ DATA_BLOB mechListMIC;
+ char *targetPrincipal;
+};
+
+struct spnego_negTokenTarg {
+ uint8_t negResult;
+ const char *supportedMech;
+ DATA_BLOB responseToken;
+ DATA_BLOB mechListMIC;
+};
+
+struct spnego_data {
+ int type;
+ struct spnego_negTokenInit negTokenInit;
+ struct spnego_negTokenTarg negTokenTarg;
+};
+
+enum spnego_message_type {
+ SPNEGO_NEG_TOKEN_INIT = 0,
+ SPNEGO_NEG_TOKEN_TARG = 1,
+};
+
+#include "auth/gensec/spnego_proto.h"
diff --git a/source4/auth/gensec/spnego_parse.c b/source4/auth/gensec/spnego_parse.c
new file mode 100644
index 0000000000..a79f15b8ee
--- /dev/null
+++ b/source4/auth/gensec/spnego_parse.c
@@ -0,0 +1,408 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ RFC2478 Compliant SPNEGO implementation
+
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "auth/gensec/spnego.h"
+#include "auth/gensec/gensec.h"
+#include "../lib/util/asn1.h"
+
+static bool read_negTokenInit(struct asn1_data *asn1, TALLOC_CTX *mem_ctx,
+ struct spnego_negTokenInit *token)
+{
+ ZERO_STRUCTP(token);
+
+ asn1_start_tag(asn1, ASN1_CONTEXT(0));
+ asn1_start_tag(asn1, ASN1_SEQUENCE(0));
+
+ while (!asn1->has_error && 0 < asn1_tag_remaining(asn1)) {
+ int i;
+ uint8_t context;
+ if (!asn1_peek_uint8(asn1, &context)) {
+ asn1->has_error = true;
+ break;
+ }
+
+ switch (context) {
+ /* Read mechTypes */
+ case ASN1_CONTEXT(0):
+ asn1_start_tag(asn1, ASN1_CONTEXT(0));
+ asn1_start_tag(asn1, ASN1_SEQUENCE(0));
+
+ token->mechTypes = talloc(NULL, const char *);
+ for (i = 0; !asn1->has_error &&
+ 0 < asn1_tag_remaining(asn1); i++) {
+ token->mechTypes = talloc_realloc(NULL,
+ token->mechTypes,
+ const char *, i+2);
+ asn1_read_OID(asn1, token->mechTypes, token->mechTypes + i);
+ }
+ token->mechTypes[i] = NULL;
+
+ asn1_end_tag(asn1);
+ asn1_end_tag(asn1);
+ break;
+ /* Read reqFlags */
+ case ASN1_CONTEXT(1):
+ asn1_start_tag(asn1, ASN1_CONTEXT(1));
+ asn1_read_Integer(asn1, &token->reqFlags);
+ token->reqFlags |= SPNEGO_REQ_FLAG;
+ asn1_end_tag(asn1);
+ break;
+ /* Read mechToken */
+ case ASN1_CONTEXT(2):
+ asn1_start_tag(asn1, ASN1_CONTEXT(2));
+ asn1_read_OctetString(asn1, mem_ctx, &token->mechToken);
+ asn1_end_tag(asn1);
+ break;
+ /* Read mecListMIC */
+ case ASN1_CONTEXT(3):
+ {
+ uint8_t type_peek;
+ asn1_start_tag(asn1, ASN1_CONTEXT(3));
+ if (!asn1_peek_uint8(asn1, &type_peek)) {
+ asn1->has_error = true;
+ break;
+ }
+ if (type_peek == ASN1_OCTET_STRING) {
+ asn1_read_OctetString(asn1, mem_ctx,
+ &token->mechListMIC);
+ } else {
+ /* RFC 2478 says we have an Octet String here,
+ but W2k sends something different... */
+ char *mechListMIC;
+ asn1_push_tag(asn1, ASN1_SEQUENCE(0));
+ asn1_push_tag(asn1, ASN1_CONTEXT(0));
+ asn1_read_GeneralString(asn1, mem_ctx, &mechListMIC);
+ asn1_pop_tag(asn1);
+ asn1_pop_tag(asn1);
+
+ token->targetPrincipal = mechListMIC;
+ }
+ asn1_end_tag(asn1);
+ break;
+ }
+ default:
+ asn1->has_error = true;
+ break;
+ }
+ }
+
+ asn1_end_tag(asn1);
+ asn1_end_tag(asn1);
+
+ return !asn1->has_error;
+}
+
+static bool write_negTokenInit(struct asn1_data *asn1, struct spnego_negTokenInit *token)
+{
+ asn1_push_tag(asn1, ASN1_CONTEXT(0));
+ asn1_push_tag(asn1, ASN1_SEQUENCE(0));
+
+ /* Write mechTypes */
+ if (token->mechTypes && *token->mechTypes) {
+ int i;
+
+ asn1_push_tag(asn1, ASN1_CONTEXT(0));
+ asn1_push_tag(asn1, ASN1_SEQUENCE(0));
+ for (i = 0; token->mechTypes[i]; i++) {
+ asn1_write_OID(asn1, token->mechTypes[i]);
+ }
+ asn1_pop_tag(asn1);
+ asn1_pop_tag(asn1);
+ }
+
+ /* write reqFlags */
+ if (token->reqFlags & SPNEGO_REQ_FLAG) {
+ int flags = token->reqFlags & ~SPNEGO_REQ_FLAG;
+
+ asn1_push_tag(asn1, ASN1_CONTEXT(1));
+ asn1_write_Integer(asn1, flags);
+ asn1_pop_tag(asn1);
+ }
+
+ /* write mechToken */
+ if (token->mechToken.data) {
+ asn1_push_tag(asn1, ASN1_CONTEXT(2));
+ asn1_write_OctetString(asn1, token->mechToken.data,
+ token->mechToken.length);
+ asn1_pop_tag(asn1);
+ }
+
+ /* write mechListMIC */
+ if (token->mechListMIC.data) {
+ asn1_push_tag(asn1, ASN1_CONTEXT(3));
+#if 0
+ /* This is what RFC 2478 says ... */
+ asn1_write_OctetString(asn1, token->mechListMIC.data,
+ token->mechListMIC.length);
+#else
+ /* ... but unfortunately this is what Windows
+ sends/expects */
+ asn1_push_tag(asn1, ASN1_SEQUENCE(0));
+ asn1_push_tag(asn1, ASN1_CONTEXT(0));
+ asn1_push_tag(asn1, ASN1_GENERAL_STRING);
+ asn1_write(asn1, token->mechListMIC.data,
+ token->mechListMIC.length);
+ asn1_pop_tag(asn1);
+ asn1_pop_tag(asn1);
+ asn1_pop_tag(asn1);
+#endif
+ asn1_pop_tag(asn1);
+ }
+
+ asn1_pop_tag(asn1);
+ asn1_pop_tag(asn1);
+
+ return !asn1->has_error;
+}
+
+static bool read_negTokenTarg(struct asn1_data *asn1, TALLOC_CTX *mem_ctx,
+ struct spnego_negTokenTarg *token)
+{
+ ZERO_STRUCTP(token);
+
+ asn1_start_tag(asn1, ASN1_CONTEXT(1));
+ asn1_start_tag(asn1, ASN1_SEQUENCE(0));
+
+ while (!asn1->has_error && 0 < asn1_tag_remaining(asn1)) {
+ uint8_t context;
+ if (!asn1_peek_uint8(asn1, &context)) {
+ asn1->has_error = true;
+ break;
+ }
+
+ switch (context) {
+ case ASN1_CONTEXT(0):
+ asn1_start_tag(asn1, ASN1_CONTEXT(0));
+ asn1_start_tag(asn1, ASN1_ENUMERATED);
+ asn1_read_uint8(asn1, &token->negResult);
+ asn1_end_tag(asn1);
+ asn1_end_tag(asn1);
+ break;
+ case ASN1_CONTEXT(1):
+ asn1_start_tag(asn1, ASN1_CONTEXT(1));
+ asn1_read_OID(asn1, mem_ctx, &token->supportedMech);
+ asn1_end_tag(asn1);
+ break;
+ case ASN1_CONTEXT(2):
+ asn1_start_tag(asn1, ASN1_CONTEXT(2));
+ asn1_read_OctetString(asn1, mem_ctx, &token->responseToken);
+ asn1_end_tag(asn1);
+ break;
+ case ASN1_CONTEXT(3):
+ asn1_start_tag(asn1, ASN1_CONTEXT(3));
+ asn1_read_OctetString(asn1, mem_ctx, &token->mechListMIC);
+ asn1_end_tag(asn1);
+ break;
+ default:
+ asn1->has_error = true;
+ break;
+ }
+ }
+
+ asn1_end_tag(asn1);
+ asn1_end_tag(asn1);
+
+ return !asn1->has_error;
+}
+
+static bool write_negTokenTarg(struct asn1_data *asn1, struct spnego_negTokenTarg *token)
+{
+ asn1_push_tag(asn1, ASN1_CONTEXT(1));
+ asn1_push_tag(asn1, ASN1_SEQUENCE(0));
+
+ if (token->negResult != SPNEGO_NONE_RESULT) {
+ asn1_push_tag(asn1, ASN1_CONTEXT(0));
+ asn1_write_enumerated(asn1, token->negResult);
+ asn1_pop_tag(asn1);
+ }
+
+ if (token->supportedMech) {
+ asn1_push_tag(asn1, ASN1_CONTEXT(1));
+ asn1_write_OID(asn1, token->supportedMech);
+ asn1_pop_tag(asn1);
+ }
+
+ if (token->responseToken.data) {
+ asn1_push_tag(asn1, ASN1_CONTEXT(2));
+ asn1_write_OctetString(asn1, token->responseToken.data,
+ token->responseToken.length);
+ asn1_pop_tag(asn1);
+ }
+
+ if (token->mechListMIC.data) {
+ asn1_push_tag(asn1, ASN1_CONTEXT(3));
+ asn1_write_OctetString(asn1, token->mechListMIC.data,
+ token->mechListMIC.length);
+ asn1_pop_tag(asn1);
+ }
+
+ asn1_pop_tag(asn1);
+ asn1_pop_tag(asn1);
+
+ return !asn1->has_error;
+}
+
+ssize_t spnego_read_data(TALLOC_CTX *mem_ctx, DATA_BLOB data, struct spnego_data *token)
+{
+ struct asn1_data *asn1;
+ ssize_t ret = -1;
+ uint8_t context;
+
+ ZERO_STRUCTP(token);
+
+ if (data.length == 0) {
+ return ret;
+ }
+
+ asn1 = asn1_init(mem_ctx);
+ if (asn1 == NULL) {
+ return -1;
+ }
+
+ asn1_load(asn1, data);
+
+ if (!asn1_peek_uint8(asn1, &context)) {
+ asn1->has_error = true;
+ } else {
+ switch (context) {
+ case ASN1_APPLICATION(0):
+ asn1_start_tag(asn1, ASN1_APPLICATION(0));
+ asn1_check_OID(asn1, GENSEC_OID_SPNEGO);
+ if (read_negTokenInit(asn1, mem_ctx, &token->negTokenInit)) {
+ token->type = SPNEGO_NEG_TOKEN_INIT;
+ }
+ asn1_end_tag(asn1);
+ break;
+ case ASN1_CONTEXT(1):
+ if (read_negTokenTarg(asn1, mem_ctx, &token->negTokenTarg)) {
+ token->type = SPNEGO_NEG_TOKEN_TARG;
+ }
+ break;
+ default:
+ asn1->has_error = true;
+ break;
+ }
+ }
+
+ if (!asn1->has_error) ret = asn1->ofs;
+ asn1_free(asn1);
+
+ return ret;
+}
+
+ssize_t spnego_write_data(TALLOC_CTX *mem_ctx, DATA_BLOB *blob, struct spnego_data *spnego)
+{
+ struct asn1_data *asn1 = asn1_init(mem_ctx);
+ ssize_t ret = -1;
+
+ if (asn1 == NULL) {
+ return -1;
+ }
+
+ switch (spnego->type) {
+ case SPNEGO_NEG_TOKEN_INIT:
+ asn1_push_tag(asn1, ASN1_APPLICATION(0));
+ asn1_write_OID(asn1, GENSEC_OID_SPNEGO);
+ write_negTokenInit(asn1, &spnego->negTokenInit);
+ asn1_pop_tag(asn1);
+ break;
+ case SPNEGO_NEG_TOKEN_TARG:
+ write_negTokenTarg(asn1, &spnego->negTokenTarg);
+ break;
+ default:
+ asn1->has_error = true;
+ break;
+ }
+
+ if (!asn1->has_error) {
+ *blob = data_blob_talloc(mem_ctx, asn1->data, asn1->length);
+ ret = asn1->ofs;
+ }
+ asn1_free(asn1);
+
+ return ret;
+}
+
+bool spnego_free_data(struct spnego_data *spnego)
+{
+ bool ret = true;
+
+ if (!spnego) goto out;
+
+ switch(spnego->type) {
+ case SPNEGO_NEG_TOKEN_INIT:
+ if (spnego->negTokenInit.mechTypes) {
+ talloc_free(spnego->negTokenInit.mechTypes);
+ }
+ data_blob_free(&spnego->negTokenInit.mechToken);
+ data_blob_free(&spnego->negTokenInit.mechListMIC);
+ talloc_free(spnego->negTokenInit.targetPrincipal);
+ break;
+ case SPNEGO_NEG_TOKEN_TARG:
+ if (spnego->negTokenTarg.supportedMech) {
+ talloc_free(discard_const(spnego->negTokenTarg.supportedMech));
+ }
+ data_blob_free(&spnego->negTokenTarg.responseToken);
+ data_blob_free(&spnego->negTokenTarg.mechListMIC);
+ break;
+ default:
+ ret = false;
+ break;
+ }
+ ZERO_STRUCTP(spnego);
+out:
+ return ret;
+}
+
+bool spnego_write_mech_types(TALLOC_CTX *mem_ctx,
+ const char **mech_types,
+ DATA_BLOB *blob)
+{
+ struct asn1_data *asn1 = asn1_init(mem_ctx);
+
+ /* Write mechTypes */
+ if (mech_types && *mech_types) {
+ int i;
+
+ asn1_push_tag(asn1, ASN1_SEQUENCE(0));
+ for (i = 0; mech_types[i]; i++) {
+ asn1_write_OID(asn1, mech_types[i]);
+ }
+ asn1_pop_tag(asn1);
+ }
+
+ if (asn1->has_error) {
+ asn1_free(asn1);
+ return false;
+ }
+
+ *blob = data_blob_talloc(mem_ctx, asn1->data, asn1->length);
+ if (blob->length != asn1->length) {
+ asn1_free(asn1);
+ return false;
+ }
+
+ asn1_free(asn1);
+
+ return true;
+}
diff --git a/source4/auth/kerberos/clikrb5.c b/source4/auth/kerberos/clikrb5.c
new file mode 100644
index 0000000000..cf87d13cf2
--- /dev/null
+++ b/source4/auth/kerberos/clikrb5.c
@@ -0,0 +1,113 @@
+/*
+ Unix SMB/CIFS implementation.
+ simple kerberos5 routines for active directory
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Luke Howard 2002-2003
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/network.h"
+#include "system/kerberos.h"
+#include "system/time.h"
+#include "auth/kerberos/kerberos.h"
+
+#ifdef HAVE_KRB5
+
+#if defined(HAVE_KRB5_PRINCIPAL2SALT) && defined(HAVE_KRB5_USE_ENCTYPE) && defined(HAVE_KRB5_STRING_TO_KEY) && defined(HAVE_KRB5_ENCRYPT_BLOCK)
+ int create_kerberos_key_from_string(krb5_context context,
+ krb5_principal host_princ,
+ krb5_data *password,
+ krb5_keyblock *key,
+ krb5_enctype enctype)
+{
+ int ret;
+ krb5_data salt;
+ krb5_encrypt_block eblock;
+
+ ret = krb5_principal2salt(context, host_princ, &salt);
+ if (ret) {
+ DEBUG(1,("krb5_principal2salt failed (%s)\n", error_message(ret)));
+ return ret;
+ }
+ krb5_use_enctype(context, &eblock, enctype);
+ ret = krb5_string_to_key(context, &eblock, key, password, &salt);
+ SAFE_FREE(salt.data);
+ return ret;
+}
+#elif defined(HAVE_KRB5_GET_PW_SALT) && defined(HAVE_KRB5_STRING_TO_KEY_SALT)
+ int create_kerberos_key_from_string(krb5_context context,
+ krb5_principal host_princ,
+ krb5_data *password,
+ krb5_keyblock *key,
+ krb5_enctype enctype)
+{
+ int ret;
+ krb5_salt salt;
+
+ ret = krb5_get_pw_salt(context, host_princ, &salt);
+ if (ret) {
+ DEBUG(1,("krb5_get_pw_salt failed (%s)\n", error_message(ret)));
+ return ret;
+ }
+ ret = krb5_string_to_key_salt(context, enctype, password->data,
+ salt, key);
+ krb5_free_salt(context, salt);
+ return ret;
+}
+#else
+#error UNKNOWN_CREATE_KEY_FUNCTIONS
+#endif
+
+ void kerberos_free_data_contents(krb5_context context, krb5_data *pdata)
+{
+#if defined(HAVE_KRB5_FREE_DATA_CONTENTS)
+ if (pdata->data) {
+ krb5_free_data_contents(context, pdata);
+ }
+#else
+ SAFE_FREE(pdata->data);
+#endif
+}
+
+ krb5_error_code smb_krb5_kt_free_entry(krb5_context context, krb5_keytab_entry *kt_entry)
+{
+#if defined(HAVE_KRB5_KT_FREE_ENTRY)
+ return krb5_kt_free_entry(context, kt_entry);
+#elif defined(HAVE_KRB5_FREE_KEYTAB_ENTRY_CONTENTS)
+ return krb5_free_keytab_entry_contents(context, kt_entry);
+#else
+#error UNKNOWN_KT_FREE_FUNCTION
+#endif
+}
+
+ char *smb_get_krb5_error_message(krb5_context context, krb5_error_code code, TALLOC_CTX *mem_ctx)
+{
+ char *ret;
+
+#if defined(HAVE_KRB5_GET_ERROR_STRING) && defined(HAVE_KRB5_FREE_ERROR_STRING)
+ char *context_error = krb5_get_error_string(context);
+ if (context_error) {
+ ret = talloc_asprintf(mem_ctx, "%s: %s", error_message(code), context_error);
+ krb5_free_error_string(context, context_error);
+ return ret;
+ }
+#endif
+ ret = talloc_strdup(mem_ctx, error_message(code));
+ return ret;
+}
+
+#endif
diff --git a/source4/auth/kerberos/config.m4 b/source4/auth/kerberos/config.m4
new file mode 100644
index 0000000000..bf14ca0ee4
--- /dev/null
+++ b/source4/auth/kerberos/config.m4
@@ -0,0 +1,540 @@
+# NOTE! this whole m4 file is disabled in configure.in for now
+
+#################################################
+# KRB5 support
+KRB5_CFLAGS=""
+KRB5_CPPFLAGS=""
+KRB5_LDFLAGS=""
+KRB5_LIBS=""
+with_krb5_support=auto
+krb5_withval=auto
+AC_MSG_CHECKING([for KRB5 support])
+
+# Do no harm to the values of CFLAGS and LIBS while testing for
+# Kerberos support.
+AC_ARG_WITH(krb5,
+[ --with-krb5=base-dir Locate Kerberos 5 support (default=auto)],
+ [ case "$withval" in
+ no)
+ with_krb5_support=no
+ AC_MSG_RESULT(no)
+ krb5_withval=no
+ ;;
+ yes)
+ with_krb5_support=yes
+ AC_MSG_RESULT(yes)
+ krb5_withval=yes
+ ;;
+ auto)
+ with_krb5_support=auto
+ AC_MSG_RESULT(auto)
+ krb5_withval=auto
+ ;;
+ *)
+ with_krb5_support=yes
+ AC_MSG_RESULT(yes)
+ krb5_withval=$withval
+ KRB5CONFIG="$krb5_withval/bin/krb5-config"
+ ;;
+ esac ],
+ AC_MSG_RESULT($with_krb5_support)
+)
+
+if test x$with_krb5_support != x"no"; then
+ FOUND_KRB5=no
+ FOUND_KRB5_VIA_CONFIG=no
+
+ #################################################
+ # check for krb5-config from recent MIT and Heimdal kerberos 5
+ AC_MSG_CHECKING(for working specified location for krb5-config)
+ if test x$KRB5CONFIG != "x"; then
+ if test -x "$KRB5CONFIG"; then
+ ac_save_CFLAGS=$CFLAGS
+ CFLAGS="";export CFLAGS
+ ac_save_LDFLAGS=$LDFLAGS
+ LDFLAGS="";export LDFLAGS
+ KRB5_LIBS="`$KRB5CONFIG --libs gssapi`"
+ KRB5_CFLAGS="`$KRB5CONFIG --cflags | sed s/@INCLUDE_des@//`"
+ KRB5_CPPFLAGS="`$KRB5CONFIG --cflags | sed s/@INCLUDE_des@//`"
+ CFLAGS=$ac_save_CFLAGS;export CFLAGS
+ LDFLAGS=$ac_save_LDFLAGS;export LDFLAGS
+ FOUND_KRB5=yes
+ FOUND_KRB5_VIA_CONFIG=yes
+ AC_MSG_RESULT(yes. Found $KRB5CONFIG)
+ else
+ AC_MSG_RESULT(no. Fallback to specified directory)
+ fi
+ else
+ AC_MSG_RESULT(no. Fallback to finding krb5-config in path)
+ #################################################
+ # check for krb5-config from recent MIT and Heimdal kerberos 5
+ AC_PATH_PROG(KRB5CONFIG, krb5-config)
+ AC_MSG_CHECKING(for working krb5-config in path)
+ if test -x "$KRB5CONFIG"; then
+ ac_save_CFLAGS=$CFLAGS
+ CFLAGS="";export CFLAGS
+ ac_save_LDFLAGS=$LDFLAGS
+ LDFLAGS="";export LDFLAGS
+ KRB5_LIBS="`$KRB5CONFIG --libs gssapi`"
+ KRB5_CFLAGS="`$KRB5CONFIG --cflags | sed s/@INCLUDE_des@//`"
+ KRB5_CPPFLAGS="`$KRB5CONFIG --cflags | sed s/@INCLUDE_des@//`"
+ CFLAGS=$ac_save_CFLAGS;export CFLAGS
+ LDFLAGS=$ac_save_LDFLAGS;export LDFLAGS
+ FOUND_KRB5=yes
+ FOUND_KRB5_VIA_CONFIG=yes
+ AC_MSG_RESULT(yes. Found $KRB5CONFIG)
+ else
+ AC_MSG_RESULT(no. Fallback to previous krb5 detection strategy)
+ fi
+ fi
+
+ if test x$FOUND_KRB5 != x"yes"; then
+ #################################################
+ # check for location of Kerberos 5 install
+ AC_MSG_CHECKING(for kerberos 5 install path)
+ case "$krb5_withval" in
+ no)
+ AC_MSG_RESULT(no krb5-path given)
+ ;;
+ yes)
+ AC_MSG_RESULT(/usr)
+ FOUND_KRB5=yes
+ ;;
+ *)
+ AC_MSG_RESULT($krb5_withval)
+ KRB5_CFLAGS="-I$krb5_withval/include"
+ KRB5_CPPFLAGS="-I$krb5_withval/include"
+ KRB5_LDFLAGS="-L$krb5_withval/lib"
+ FOUND_KRB5=yes
+ ;;
+ esac
+ fi
+
+ if test x$FOUND_KRB5 != x"yes"; then
+ #################################################
+ # see if this box has the SuSE location for the heimdal krb implementation
+ AC_MSG_CHECKING(for /usr/include/heimdal)
+ if test -d /usr/include/heimdal; then
+ if test -f /usr/lib/heimdal/lib/libkrb5.a; then
+ KRB5_CFLAGS="-I/usr/include/heimdal"
+ KRB5_CPPFLAGS="-I/usr/include/heimdal"
+ KRB5_LDFLAGS="-L/usr/lib/heimdal/lib"
+ AC_MSG_RESULT(yes)
+ else
+ KRB5_CFLAGS="-I/usr/include/heimdal"
+ KRB5_CPPFLAGS="-I/usr/include/heimdal"
+ AC_MSG_RESULT(yes)
+ fi
+ else
+ AC_MSG_RESULT(no)
+ fi
+ fi
+
+ if test x$FOUND_KRB5 != x"yes"; then
+ #################################################
+ # see if this box has the RedHat location for kerberos
+ AC_MSG_CHECKING(for /usr/kerberos)
+ if test -d /usr/kerberos -a -f /usr/kerberos/lib/libkrb5.a; then
+ KRB5_LDFLAGS="-L/usr/kerberos/lib"
+ KRB5_CFLAGS="-I/usr/kerberos/include"
+ KRB5_CPPFLAGS="-I/usr/kerberos/include"
+ AC_MSG_RESULT(yes)
+ else
+ AC_MSG_RESULT(no)
+ fi
+ fi
+
+ ac_save_CFLAGS=$CFLAGS
+ ac_save_CPPFLAGS=$CPPFLAGS
+ ac_save_LDFLAGS=$LDFLAGS
+
+ #MIT needs this, to let us see 'internal' parts of the headers we use
+ KRB5_CFLAGS="${KRB5_CFLAGS} -DKRB5_PRIVATE -DKRB5_DEPRECATED"
+
+ #Heimdal needs this
+ #TODO: we need to parse KRB5_LIBS for -L path
+ # and set -Wl,-rpath -Wl,path
+
+ CFLAGS="$CFLAGS $KRB5_CFLAGS"
+ CPPFLAGS="$CPPFLAGS $KRB5_CPPFLAGS"
+ LDFLAGS="$LDFLAGS $KRB5_LDFLAGS"
+
+ KRB5_LIBS="$KRB5_LDFLAGS $KRB5_LIBS"
+
+ # now check for krb5.h. Some systems have the libraries without the headers!
+ # note that this check is done here to allow for different kerberos
+ # include paths
+ AC_CHECK_HEADERS(krb5.h)
+
+ if test x"$ac_cv_header_krb5_h" = x"no"; then
+ # Give a warning if KRB5 support was not explicitly requested,
+ # i.e with_krb5_support = auto, otherwise die with an error.
+ if test x"$with_krb5_support" = x"yes"; then
+ AC_MSG_ERROR([KRB5 cannot be supported without krb5.h])
+ else
+ AC_MSG_WARN([KRB5 cannot be supported without krb5.h])
+ fi
+ # Turn off AD support and restore CFLAGS and LIBS variables
+ with_krb5_support="no"
+ fi
+
+ CFLAGS=$ac_save_CFLAGS
+ CPPFLAGS=$ac_save_CPPFLAGS
+ LDFLAGS=$ac_save_LDFLAGS
+fi
+
+# Now we have determined whether we really want KRB5 support
+
+if test x"$with_krb5_support" != x"no"; then
+ ac_save_CFLAGS=$CFLAGS
+ ac_save_CPPFLAGS=$CPPFLAGS
+ ac_save_LDFLAGS=$LDFLAGS
+ ac_save_LIBS=$LIBS
+
+ CFLAGS="$CFLAGS $KRB5_CFLAGS"
+ CPPFLAGS="$CPPFLAGS $KRB5_CPPFLAGS"
+ LDFLAGS="$LDFLAGS $KRB5_LDFLAGS"
+
+ # now check for gssapi headers. This is also done here to allow for
+ # different kerberos include paths
+ AC_CHECK_HEADERS(gssapi.h gssapi_krb5.h gssapi/gssapi.h gssapi/gssapi_generic.h gssapi/gssapi_krb5.h com_err.h)
+
+
+ # Heimdal checks.
+ # But only if we didn't have a krb5-config to tell us this already
+ if test x"$FOUND_KRB5_VIA_CONFIG" != x"yes"; then
+ ##################################################################
+ # we might need the k5crypto and com_err libraries on some systems
+ AC_CHECK_LIB_EXT(com_err, KRB5_LIBS, _et_list)
+ AC_CHECK_LIB_EXT(k5crypto, KRB5_LIBS, krb5_encrypt_data)
+
+ AC_CHECK_LIB_EXT(crypto, KRB5_LIBS, des_set_key)
+ AC_CHECK_LIB_EXT(asn1, KRB5_LIBS, copy_Authenticator)
+ AC_CHECK_LIB_EXT(roken, KRB5_LIBS, roken_getaddrinfo_hostspec)
+ fi
+
+ # Heimdal checks. On static Heimdal gssapi must be linked before krb5.
+ AC_CHECK_LIB_EXT(gssapi, KRB5_LIBS, gss_display_status,[],[],
+ AC_DEFINE(HAVE_GSSAPI,1,[Whether GSSAPI is available]))
+
+ ########################################################
+ # now see if we can find the krb5 libs in standard paths
+ # or as specified above
+ AC_CHECK_LIB_EXT(krb5, KRB5_LIBS, krb5_mk_req_extended)
+ AC_CHECK_LIB_EXT(krb5, KRB5_LIBS, krb5_kt_compare)
+
+ ########################################################
+ # now see if we can find the gssapi libs in standard paths
+ if test x"$ac_cv_lib_ext_gssapi_gss_display_status" != x"yes"; then
+ AC_CHECK_LIB_EXT(gssapi_krb5, KRB5_LIBS,gss_display_status,[],[],
+ AC_DEFINE(HAVE_GSSAPI,1,[Whether GSSAPI is available]))
+ fi
+
+ AC_CHECK_FUNC_EXT(krb5_set_real_time, $KRB5_LIBS)
+ AC_CHECK_FUNC_EXT(krb5_set_default_in_tkt_etypes, $KRB5_LIBS)
+ AC_CHECK_FUNC_EXT(krb5_set_default_tgs_ktypes, $KRB5_LIBS)
+ AC_CHECK_FUNC_EXT(krb5_principal2salt, $KRB5_LIBS)
+ AC_CHECK_FUNC_EXT(krb5_use_enctype, $KRB5_LIBS)
+ AC_CHECK_FUNC_EXT(krb5_string_to_key, $KRB5_LIBS)
+ AC_CHECK_FUNC_EXT(krb5_get_pw_salt, $KRB5_LIBS)
+ AC_CHECK_FUNC_EXT(krb5_string_to_key_salt, $KRB5_LIBS)
+ AC_CHECK_FUNC_EXT(krb5_auth_con_setkey, $KRB5_LIBS)
+ AC_CHECK_FUNC_EXT(krb5_auth_con_setuseruserkey, $KRB5_LIBS)
+ AC_CHECK_FUNC_EXT(krb5_locate_kdc, $KRB5_LIBS)
+ AC_CHECK_FUNC_EXT(krb5_get_permitted_enctypes, $KRB5_LIBS)
+ AC_CHECK_FUNC_EXT(krb5_get_default_in_tkt_etypes, $KRB5_LIBS)
+ AC_CHECK_FUNC_EXT(krb5_free_ktypes, $KRB5_LIBS)
+ AC_CHECK_FUNC_EXT(krb5_free_data_contents, $KRB5_LIBS)
+ AC_CHECK_FUNC_EXT(krb5_principal_get_comp_string, $KRB5_LIBS)
+ AC_CHECK_FUNC_EXT(krb5_free_unparsed_name, $KRB5_LIBS)
+ AC_CHECK_FUNC_EXT(krb5_free_keytab_entry_contents, $KRB5_LIBS)
+ AC_CHECK_FUNC_EXT(krb5_kt_free_entry, $KRB5_LIBS)
+ AC_CHECK_FUNC_EXT(krb5_krbhst_get_addrinfo, $KRB5_LIBS)
+ AC_CHECK_FUNC_EXT(krb5_verify_checksum, $KRB5_LIBS)
+ AC_CHECK_FUNC_EXT(krb5_c_verify_checksum, $KRB5_LIBS)
+ AC_CHECK_FUNC_EXT(krb5_ticket_get_authorization_data_type, $KRB5_LIBS)
+ AC_CHECK_FUNC_EXT(krb5_krbhst_get_addrinfo, $KRB5_LIBS)
+ AC_CHECK_FUNC_EXT(krb5_c_enctype_compare, $KRB5_LIBS)
+ AC_CHECK_FUNC_EXT(krb5_enctypes_compatible_keys, $KRB5_LIBS)
+ AC_CHECK_FUNC_EXT(krb5_get_error_string, $KRB5_LIBS)
+ AC_CHECK_FUNC_EXT(krb5_free_error_string, $KRB5_LIBS)
+ AC_CHECK_FUNC_EXT(krb5_initlog, $KRB5_LIBS)
+ AC_CHECK_FUNC_EXT(krb5_addlog_func, $KRB5_LIBS)
+ AC_CHECK_FUNC_EXT(krb5_set_warn_dest, $KRB5_LIBS)
+
+ LIBS="$LIBS $KRB5_LIBS"
+
+ AC_CACHE_CHECK([for krb5_log_facility type],
+ samba_cv_HAVE_KRB5_LOG_FACILITY,[
+ AC_TRY_COMPILE([#include <krb5.h>],
+ [krb5_log_facility block;],
+ samba_cv_HAVE_KRB5_LOG_FACILITY=yes,
+ samba_cv_HAVE_KRB5_LOG_FACILITY=no)])
+
+ if test x"$samba_cv_HAVE_KRB5_LOG_FACILITY" = x"yes"; then
+ AC_DEFINE(HAVE_KRB5_LOG_FACILITY,1,
+ [Whether the type krb5_log_facility exists])
+ fi
+
+ AC_CACHE_CHECK([for krb5_encrypt_block type],
+ samba_cv_HAVE_KRB5_ENCRYPT_BLOCK,[
+ AC_TRY_COMPILE([#include <krb5.h>],
+ [krb5_encrypt_block block;],
+ samba_cv_HAVE_KRB5_ENCRYPT_BLOCK=yes,
+ samba_cv_HAVE_KRB5_ENCRYPT_BLOCK=no)])
+
+ if test x"$samba_cv_HAVE_KRB5_ENCRYPT_BLOCK" = x"yes"; then
+ AC_DEFINE(HAVE_KRB5_ENCRYPT_BLOCK,1,
+ [Whether the type krb5_encrypt_block exists])
+ fi
+
+ AC_CACHE_CHECK([for addrtype in krb5_address],
+ samba_cv_HAVE_ADDRTYPE_IN_KRB5_ADDRESS,[
+ AC_TRY_COMPILE([#include <krb5.h>],
+ [krb5_address kaddr; kaddr.addrtype = ADDRTYPE_INET;],
+ samba_cv_HAVE_ADDRTYPE_IN_KRB5_ADDRESS=yes,
+ samba_cv_HAVE_ADDRTYPE_IN_KRB5_ADDRESS=no)])
+ if test x"$samba_cv_HAVE_ADDRTYPE_IN_KRB5_ADDRESS" = x"yes"; then
+ AC_DEFINE(HAVE_ADDRTYPE_IN_KRB5_ADDRESS,1,
+ [Whether the krb5_address struct has a addrtype property])
+ fi
+
+ AC_CACHE_CHECK([for addr_type in krb5_address],
+ samba_cv_HAVE_ADDR_TYPE_IN_KRB5_ADDRESS,[
+ AC_TRY_COMPILE([#include <krb5.h>],
+ [krb5_address kaddr; kaddr.addr_type = KRB5_ADDRESS_INET;],
+ samba_cv_HAVE_ADDR_TYPE_IN_KRB5_ADDRESS=yes,
+ samba_cv_HAVE_ADDR_TYPE_IN_KRB5_ADDRESS=no)])
+ if test x"$samba_cv_HAVE_ADDR_TYPE_IN_KRB5_ADDRESS" = x"yes"; then
+ AC_DEFINE(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS,1,
+ [Whether the krb5_address struct has a addr_type property])
+ fi
+
+ AC_CACHE_CHECK([for enc_part2 in krb5_ticket],
+ samba_cv_HAVE_KRB5_TKT_ENC_PART2,[
+ AC_TRY_COMPILE([#include <krb5.h>],
+ [krb5_ticket tkt; tkt.enc_part2->authorization_data[0]->contents = NULL;],
+ samba_cv_HAVE_KRB5_TKT_ENC_PART2=yes,
+ samba_cv_HAVE_KRB5_TKT_ENC_PART2=no)])
+ if test x"$samba_cv_HAVE_KRB5_TKT_ENC_PART2" = x"yes"; then
+ AC_DEFINE(HAVE_KRB5_TKT_ENC_PART2,1,
+ [Whether the krb5_ticket struct has a enc_part2 property])
+ fi
+
+ AC_CACHE_CHECK([for keyblock in krb5_creds],
+ samba_cv_HAVE_KRB5_KEYBLOCK_IN_CREDS,[
+ AC_TRY_COMPILE([#include <krb5.h>],
+ [krb5_creds creds; krb5_keyblock kb; creds.keyblock = kb;],
+ samba_cv_HAVE_KRB5_KEYBLOCK_IN_CREDS=yes,
+ samba_cv_HAVE_KRB5_KEYBLOCK_IN_CREDS=no)])
+
+ if test x"$samba_cv_HAVE_KRB5_KEYBLOCK_IN_CREDS" = x"yes"; then
+ AC_DEFINE(HAVE_KRB5_KEYBLOCK_IN_CREDS,1,
+ [Whether the krb5_creds struct has a keyblock property])
+ fi
+
+ AC_CACHE_CHECK([for session in krb5_creds],
+ samba_cv_HAVE_KRB5_SESSION_IN_CREDS,[
+ AC_TRY_COMPILE([#include <krb5.h>],
+ [krb5_creds creds; krb5_keyblock kb; creds.session = kb;],
+ samba_cv_HAVE_KRB5_SESSION_IN_CREDS=yes,
+ samba_cv_HAVE_KRB5_SESSION_IN_CREDS=no)])
+
+ if test x"$samba_cv_HAVE_KRB5_SESSION_IN_CREDS" = x"yes"; then
+ AC_DEFINE(HAVE_KRB5_SESSION_IN_CREDS,1,
+ [Whether the krb5_creds struct has a session property])
+ fi
+
+ AC_CACHE_CHECK([for keyvalue in krb5_keyblock],
+ samba_cv_HAVE_KRB5_KEYBLOCK_KEYVALUE,[
+ AC_TRY_COMPILE([#include <krb5.h>],
+ [krb5_keyblock key; key.keyvalue.data = NULL;],
+ samba_cv_HAVE_KRB5_KEYBLOCK_KEYVALUE=yes,
+ samba_cv_HAVE_KRB5_KEYBLOCK_KEYVALUE=no)])
+ if test x"$samba_cv_HAVE_KRB5_KEYBLOCK_KEYVALUE" = x"yes"; then
+ AC_DEFINE(HAVE_KRB5_KEYBLOCK_KEYVALUE,1,
+ [Whether the krb5_keyblock struct has a keyvalue property])
+ fi
+
+ AC_CACHE_CHECK([for ENCTYPE_ARCFOUR_HMAC_MD5],
+ samba_cv_HAVE_ENCTYPE_ARCFOUR_HMAC_MD5,[
+ AC_TRY_COMPILE([#include <krb5.h>],
+ [krb5_enctype enctype; enctype = ENCTYPE_ARCFOUR_HMAC_MD5;],
+ samba_cv_HAVE_ENCTYPE_ARCFOUR_HMAC_MD5=yes,
+ samba_cv_HAVE_ENCTYPE_ARCFOUR_HMAC_MD5=no)])
+ AC_CACHE_CHECK([for KEYTYPE_ARCFOUR_56],
+ samba_cv_HAVE_KEYTYPE_ARCFOUR_56,[
+ AC_TRY_COMPILE([#include <krb5.h>],
+ [krb5_keytype keytype; keytype = KEYTYPE_ARCFOUR_56;],
+ samba_cv_HAVE_KEYTYPE_ARCFOUR_56=yes,
+ samba_cv_HAVE_KEYTYPE_ARCFOUR_56=no)])
+ # Heimdals with KEYTYPE_ARCFOUR but not KEYTYPE_ARCFOUR_56 are broken
+ # w.r.t. arcfour and windows, so we must not enable it here
+ if test x"$samba_cv_HAVE_ENCTYPE_ARCFOUR_HMAC_MD5" = x"yes" -a\
+ x"$samba_cv_HAVE_KEYTYPE_ARCFOUR_56" = x"yes"; then
+ AC_DEFINE(HAVE_ENCTYPE_ARCFOUR_HMAC_MD5,1,
+ [Whether the ENCTYPE_ARCFOUR_HMAC_MD5 key type is available])
+ fi
+
+ AC_CACHE_CHECK([for AP_OPTS_USE_SUBKEY],
+ samba_cv_HAVE_AP_OPTS_USE_SUBKEY,[
+ AC_TRY_COMPILE([#include <krb5.h>],
+ [krb5_flags ap_options; ap_options = AP_OPTS_USE_SUBKEY;],
+ samba_cv_HAVE_AP_OPTS_USE_SUBKEY=yes,
+ samba_cv_HAVE_AP_OPTS_USE_SUBKEY=no)])
+ if test x"$samba_cv_HAVE_AP_OPTS_USE_SUBKEY" = x"yes"; then
+ AC_DEFINE(HAVE_AP_OPTS_USE_SUBKEY,1,
+ [Whether the AP_OPTS_USE_SUBKEY ap option is available])
+ fi
+
+ AC_CACHE_CHECK([for KV5M_KEYTAB],
+ samba_cv_HAVE_KV5M_KEYTAB,[
+ AC_TRY_COMPILE([#include <krb5.h>],
+ [krb5_keytab_entry entry; entry.magic = KV5M_KEYTAB;],
+ samba_cv_HAVE_KV5M_KEYTAB=yes,
+ samba_cv_HAVE_KV5M_KEYTAB=no)])
+ if test x"$samba_cv_HAVE_KV5M_KEYTAB" = x"yes"; then
+ AC_DEFINE(HAVE_KV5M_KEYTAB,1,
+ [Whether the KV5M_KEYTAB option is available])
+ fi
+
+ AC_CACHE_CHECK([for the krb5_princ_component macro],
+ samba_cv_HAVE_KRB5_PRINC_COMPONENT,[
+ AC_TRY_LINK([#include <krb5.h>],
+ [const krb5_data *pkdata; krb5_context context; krb5_principal principal;
+ pkdata = krb5_princ_component(context, principal, 0);],
+ samba_cv_HAVE_KRB5_PRINC_COMPONENT=yes,
+ samba_cv_HAVE_KRB5_PRINC_COMPONENT=no)])
+ if test x"$samba_cv_HAVE_KRB5_PRINC_COMPONENT" = x"yes"; then
+ AC_DEFINE(HAVE_KRB5_PRINC_COMPONENT,1,
+ [Whether krb5_princ_component is available])
+ fi
+
+ AC_CACHE_CHECK([for key in krb5_keytab_entry],
+ samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEY,[
+ AC_TRY_COMPILE([#include <krb5.h>],
+ [krb5_keytab_entry entry; krb5_keyblock e; entry.key = e;],
+ samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEY=yes,
+ samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEY=no)])
+ if test x"$samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEY" = x"yes"; then
+ AC_DEFINE(HAVE_KRB5_KEYTAB_ENTRY_KEY,1,
+ [Whether krb5_keytab_entry has key member])
+ fi
+
+ AC_CACHE_CHECK([for keyblock in krb5_keytab_entry],
+ samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK,[
+ AC_TRY_COMPILE([#include <krb5.h>],
+ [krb5_keytab_entry entry; entry.keyblock.keytype = 0;],
+ samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK=yes,
+ samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK=no)])
+ if test x"$samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK" = x"yes"; then
+ AC_DEFINE(HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK,1,
+ [Whether krb5_keytab_entry has keyblock member])
+ fi
+
+ AC_CACHE_CHECK([for WRFILE: keytab support],
+ samba_cv_HAVE_WRFILE_KEYTAB,[
+ AC_TRY_RUN([
+ #include<krb5.h>
+ main()
+ {
+ krb5_context context;
+ krb5_keytab keytab;
+ krb5_init_context(&context);
+ return krb5_kt_resolve(context, "WRFILE:api", &keytab);
+ }],
+ samba_cv_HAVE_WRFILE_KEYTAB=yes,
+ samba_cv_HAVE_WRFILE_KEYTAB=no)])
+ if test x"$samba_cv_HAVE_WRFILE_KEYTAB" = x"yes"; then
+ AC_DEFINE(HAVE_WRFILE_KEYTAB,1,
+ [Whether the WRFILE:-keytab is supported])
+ fi
+
+ AC_CACHE_CHECK([for krb5_princ_realm returns krb5_realm or krb5_data],
+ samba_cv_KRB5_PRINC_REALM_RETURNS_REALM,[
+ AC_TRY_COMPILE([#include <krb5.h>],
+ [krb5_context context;krb5_principal principal;krb5_realm realm;
+ realm = *krb5_princ_realm(context, principal);],
+ samba_cv_KRB5_PRINC_REALM_RETURNS_REALM=yes,
+ samba_cv_KRB5_PRINC_REALM_RETURNS_REALM=no)])
+ if test x"$samba_cv_KRB5_PRINC_REALM_RETURNS_REALM" = x"yes"; then
+ AC_DEFINE(KRB5_PRINC_REALM_RETURNS_REALM,1,
+ [Whether krb5_princ_realm returns krb5_realm or krb5_data])
+ fi
+
+ # TODO: check all gssapi headers for this
+ AC_CACHE_CHECK([for GSS_C_DCE_STYLE in gssapi.h],
+ samba_cv_GSS_C_DCE_STYLE,[
+ AC_TRY_COMPILE([#include <gssapi.h>],
+ [int flags = GSS_C_DCE_STYLE;],
+ samba_cv_GSS_C_DCE_STYLE=yes,
+ samba_cv_GSS_C_DCE_STYLE=no)])
+
+ AC_CHECK_FUNC_EXT(gsskrb5_get_initiator_subkey, $KRB5_LIBS)
+ AC_CHECK_FUNC_EXT(gsskrb5_extract_authz_data_from_sec_context, $KRB5_LIBS)
+ AC_CHECK_FUNC_EXT(gsskrb5_register_acceptor_identity, $KRB5_LIBS)
+ AC_CHECK_FUNC_EXT(gss_krb5_ccache_name, $KRB5_LIBS)
+ if test x"$ac_cv_lib_ext_krb5_krb5_mk_req_extended" = x"yes"; then
+ AC_DEFINE(HAVE_KRB5,1,[Whether to have KRB5 support])
+ AC_MSG_CHECKING(whether KRB5 support is used)
+ SMB_ENABLE(KRB5,YES)
+ AC_MSG_RESULT(yes)
+ echo "KRB5_CFLAGS: ${KRB5_CFLAGS}"
+ echo "KRB5_CPPFLAGS: ${KRB5_CPPFLAGS}"
+ echo "KRB5_LDFLAGS: ${KRB5_LDFLAGS}"
+ echo "KRB5_LIBS: ${KRB5_LIBS}"
+ else
+ if test x"$with_krb5_support" = x"yes"; then
+ AC_MSG_ERROR(a working krb5 library is needed for KRB5 support)
+ else
+ AC_MSG_WARN(a working krb5 library is needed for KRB5 support)
+ fi
+ KRB5_CFLAGS=""
+ KRB5_CPPFLAGS=""
+ KRB5_LDFLAGS=""
+ KRB5_LIBS=""
+ with_krb5_support=no
+ fi
+
+ # checks if we have access to a libkdc
+ # and can use it for our builtin kdc server_service
+ KDC_CFLAGS=""
+ KDC_CPPFLAGS=""
+ KDC_DLFLAGS=""
+ KDC_LIBS=""
+ AC_CHECK_HEADERS(kdc.h)
+ AC_CHECK_LIB_EXT(kdc, KDC_LIBS, krb5_kdc_default_config)
+ AC_CHECK_LIB_EXT(hdb, KDC_LIBS, hdb_generate_key_set_password)
+
+ AC_MSG_CHECKING(whether libkdc is used)
+ if test x"$ac_cv_header_kdc_h" = x"yes"; then
+ if test x"$ac_cv_lib_ext_kdc_krb5_kdc_default_config" = x"yes"; then
+ if test x"$ac_cv_lib_ext_hdb_hdb_generate_key_set_password" = x"yes"; then
+ SMB_ENABLE(KDC,YES)
+ AC_MSG_RESULT(yes)
+ echo "KDC_LIBS: ${KDC_LIBS}"
+ else
+ AC_MSG_RESULT(no)
+ fi
+ else
+ AC_MSG_RESULT(no)
+ fi
+ else
+ AC_MSG_RESULT(no)
+ fi
+
+ CFLAGS=$ac_save_CFLAGS
+ CPPFLAGS=$ac_save_CPPFLAGS
+ LDFLAGS=$ac_save_LDFLAGS
+ LIBS="$ac_save_LIBS"
+
+ # as a nasty hack add the krb5 stuff to the global vars,
+ # at some point this should not be needed anymore when the build system
+ # can handle that alone
+ CFLAGS="$CFLAGS $KRB5_CFLAGS"
+ CPPFLAGS="$CPPFLAGS $KRB5_CPPFLAGS"
+ LDFLAGS="$LDFLAGS $KRB5_LDFLAGS"
+fi
+
+SMB_EXT_LIB(KRB5,[${KRB5_LIBS}],[${KRB5_CFLAGS}],[${KRB5_CPPFLAGS}],[${KRB5_LDFLAGS}])
+SMB_EXT_LIB(KDC,[${KDC_LIBS}],[${KDC_CFLAGS}],[${KDC_CPPFLAGS}],[${KDC_LDFLAGS}])
diff --git a/source4/auth/kerberos/config.mk b/source4/auth/kerberos/config.mk
new file mode 100644
index 0000000000..822bf398a7
--- /dev/null
+++ b/source4/auth/kerberos/config.mk
@@ -0,0 +1,18 @@
+#################################
+# Start SUBSYSTEM KERBEROS
+[SUBSYSTEM::KERBEROS]
+PUBLIC_DEPENDENCIES = HEIMDAL_KRB5 NDR_KRB5PAC samba_socket LIBCLI_RESOLVE
+PRIVATE_DEPENDENCIES = ASN1_UTIL auth_sam_reply LIBTEVENT LIBPACKET LIBNDR
+# End SUBSYSTEM KERBEROS
+#################################
+
+KERBEROS_OBJ_FILES = $(addprefix $(authsrcdir)/kerberos/, \
+ kerberos.o \
+ clikrb5.o \
+ kerberos_heimdal.o \
+ kerberos_pac.o \
+ gssapi_parse.o \
+ krb5_init_context.o)
+
+$(eval $(call proto_header_template,$(authsrcdir)/kerberos/proto.h,$(KERBEROS_OBJ_FILES:.o=.c)))
+
diff --git a/source4/auth/kerberos/gssapi_parse.c b/source4/auth/kerberos/gssapi_parse.c
new file mode 100644
index 0000000000..489ebcaa83
--- /dev/null
+++ b/source4/auth/kerberos/gssapi_parse.c
@@ -0,0 +1,125 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ simple GSSAPI wrappers
+
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
+ Copyright (C) Luke Howard 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "../lib/util/asn1.h"
+#include "auth/gensec/gensec.h"
+#include "system/kerberos.h"
+#include "auth/kerberos/kerberos.h"
+
+/*
+ generate a krb5 GSS-API wrapper packet given a ticket
+*/
+DATA_BLOB gensec_gssapi_gen_krb5_wrap(TALLOC_CTX *mem_ctx, const DATA_BLOB *ticket, const uint8_t tok_id[2])
+{
+ struct asn1_data *data;
+ DATA_BLOB ret;
+
+ if (!data || !ticket->data) {
+ return data_blob(NULL,0);
+ }
+
+ data = asn1_init(mem_ctx);
+ if (data == NULL) {
+ return data_blob(NULL,0);
+ }
+
+ asn1_push_tag(data, ASN1_APPLICATION(0));
+ asn1_write_OID(data, GENSEC_OID_KERBEROS5);
+
+ asn1_write(data, tok_id, 2);
+ asn1_write(data, ticket->data, ticket->length);
+ asn1_pop_tag(data);
+
+ if (data->has_error) {
+ DEBUG(1,("Failed to build krb5 wrapper at offset %d\n", (int)data->ofs));
+ asn1_free(data);
+ return data_blob(NULL,0);
+ }
+
+ ret = data_blob_talloc(mem_ctx, data->data, data->length);
+ asn1_free(data);
+
+ return ret;
+}
+
+/*
+ parse a krb5 GSS-API wrapper packet giving a ticket
+*/
+bool gensec_gssapi_parse_krb5_wrap(TALLOC_CTX *mem_ctx, const DATA_BLOB *blob, DATA_BLOB *ticket, uint8_t tok_id[2])
+{
+ bool ret;
+ struct asn1_data *data = asn1_init(mem_ctx);
+ int data_remaining;
+
+ if (!data) {
+ return false;
+ }
+
+ asn1_load(data, *blob);
+ asn1_start_tag(data, ASN1_APPLICATION(0));
+ asn1_check_OID(data, GENSEC_OID_KERBEROS5);
+
+ data_remaining = asn1_tag_remaining(data);
+
+ if (data_remaining < 3) {
+ data->has_error = true;
+ } else {
+ asn1_read(data, tok_id, 2);
+ data_remaining -= 2;
+ *ticket = data_blob_talloc(mem_ctx, NULL, data_remaining);
+ asn1_read(data, ticket->data, ticket->length);
+ }
+
+ asn1_end_tag(data);
+
+ ret = !data->has_error;
+
+ asn1_free(data);
+
+ return ret;
+}
+
+
+/*
+ check a GSS-API wrapper packet givin an expected OID
+*/
+bool gensec_gssapi_check_oid(const DATA_BLOB *blob, const char *oid)
+{
+ bool ret;
+ struct asn1_data *data = asn1_init(NULL);
+
+ if (!data) return false;
+
+ asn1_load(data, *blob);
+ asn1_start_tag(data, ASN1_APPLICATION(0));
+ asn1_check_OID(data, oid);
+
+ ret = !data->has_error;
+
+ asn1_free(data);
+
+ return ret;
+}
+
+
diff --git a/source4/auth/kerberos/kerberos-notes.txt b/source4/auth/kerberos/kerberos-notes.txt
new file mode 100644
index 0000000000..43881a20d3
--- /dev/null
+++ b/source4/auth/kerberos/kerberos-notes.txt
@@ -0,0 +1,466 @@
+AllowedWorkstationNames and Krb5
+--------------------------------
+
+Microsoft uses the clientAddresses *multiple value* field in the krb5
+protocol (particularly the AS_REQ) to communicate it's netbios name.
+This is (my guess) to permit the userWorkstations field to work.
+
+The KDC I imagine checks the netbios address against this value, in
+the same way that the Samba server does this.
+
+The checking of this implies a little of the next question:
+
+Is a DAL the layer we need?
+---------------------------
+
+Looking at what we need to pass around, I start to seriously wonder if
+the DAL is even the right layer - we seem to want to create an account
+authorization abstraction layer - is this account permitted to login to
+this computer, at this time?
+
+This information in AD is much richer than the Heimdal HDB, and it
+seems to make sense to do AD-specific access control checks in an
+AD-specific layer, not in the back-end agnostic server.
+
+Because the DAL only reads in the principalName as the key, it has
+trouble performing access control decisions on things other than the
+name.
+
+I'll be very interested if the DAL really works for eDirectory too.
+Perhaps all we need to do is add in the same kludges as we have in
+Samba 3.0 for eDirectory. Hmm...
+
+That said, the current layer provides us with a very good start, and
+any redefinition would occour from that basis.
+
+
+GSSAPI layer requirements
+-------------------------
+
+Welcome to the wonderful world of canonicalisation
+
+The MIT GSSAPI libs do not support kinit returning a different
+realm to what the client asked for, even just in case differences.
+
+Heimdal has the same problem, and this applies to the krb5 layer, not
+just gssapi.
+
+We need to test if the canonicalisation is controlled by the KDCOption
+flags, windows always sends the Canonicalize flags
+
+Old Clients (samba3 and HPUX clients) uses 'selfmade' gssapi/krb5
+for using it in the CIFS session setup. Because they use krb5_mk_req()
+they get a chksum field depending on the encryption type, but that's wrong
+for GSSAPI (see rfc 1964 section 1.1.1). The Cheksum type 8003
+should be used in the Authenticator of the AP-REQ! That allows the channel bindings,
+the GCC_C_* req_flags and optional delegation tickets to be passed from the client to the server.
+Hower windows doesn't seems to care about if the checksum is of the wrong type,
+for CIFS SessionSetups, it seems that the req_flags are just set to 0.
+So this can't work for LDAP connections with sign or seal, or for any DCERPC
+connection.
+
+So we need to also support old clients!
+
+Principal Names, long and short names
+-------------------------------------
+
+As far as servicePrincipalNames are concerned, these are not
+canonicalised, except as regards the realm in the reply. That is, the
+client gets back the principal it asked for, with the realm portion
+'fixed' to uppercase, long form.
+
+The short name of the realm seems to be accepted for at least AS_REQ
+operations, but because the server performs canonicalisation, this
+causes pain for current client libraries.
+
+The canonicalisation of names matters not only for the KDC, but also
+for code that has to deal with keytabs.
+
+We also need to handle type 10 names (NT-ENTERPRISE), which are a full
+principal name in the principal field, unrelated to the realm.
+
+HOST/ Aliases
+-------------
+
+There is another post somewhere (ref lost for the moment) that details
+where in active directory the list of stored aliases for HOST/ is.
+This should be read, parsed and used to allow any of these requests to
+use the HOST/ key.
+
+For example, this is how HTTP/, DNS/ and CIFS/ can use HOST/ without
+any explicit entry.
+
+
+Jean-Baptiste.Marchand@hsc.fr reminds me:
+
+> This is the SPNMappings attribute in Active Directory:
+
+> http://msdn.microsoft.com/library/en-us/adschema/adschema/a_spnmappings.asp
+
+We implement this in hdb-ldb.
+
+Implicit names for Win2000 Accounts
+-----------------------------------
+
+Despite not having a DNS name, nor a servicePrincipalName on accounts
+created by computers running win2000, it appears we are expected to
+have an implicit mapping from host/computer.full.name and
+host/computer to it's entry.
+
+Returned Salt for PreAuthentication
+-----------------------------------
+
+When the server replies for pre-authentication, it returns the Salt,
+which may be in the form of a principalName that is in no way
+connected with the current names. (ie, even if the userPrincipalName
+and samAccountName are renamed, the old salt is returned).
+
+This is probably the kerberos standard salt, kept in the 'Key'. The
+standard generation rules are found in a Mail from Luke Howard dated
+10 Nov 2004:
+
+
+From: Luke Howard <lukeh@padl.com>
+Message-Id: <200411100231.iAA2VLUW006101@au.padl.com>
+MIME-Version: 1.0
+Content-Type: text/plain; charset=US-ASCII
+Organization: PADL Software Pty Ltd
+To: lukeh@padl.com
+Date: Wed, 10 Nov 2004 13:31:21 +1100
+Versions: dmail (bsd44) 2.6d/makemail 2.10
+Cc: huaraz@moeller.plus.com, samba-technical@lists.samba.org
+Subject: Re: Samba-3.0.7-1.3E Active Directory Issues
+X-BeenThere: samba-technical@lists.samba.org
+X-Mailman-Version: 2.1.4
+Precedence: list
+Reply-To: lukeh@padl.com
+
+Did some more testing, it appears the behaviour has another
+explanation. It appears that the standard Kerberos password salt
+algorithm is applied in Windows 2003, just that the source principal
+name is different.
+
+Here is what I've been able to deduce from creating a bunch of
+different accounts:
+
+Type of account Principal for Salting
+========================================================================
+Computer Account host/<SAM-Name-Without-$>.realm@REALM
+User Account Without UPN <SAM-Name>@REALM
+User Account With UPN <LHS-Of-UPN>@REALM
+
+Note that if the computer account's SAM account name does not include
+the trailing '$', then the entire SAM account name is used as input to
+the salting principal. Setting a UPN for a computer account has no
+effect.
+
+It seems to me odd that the RHS of the UPN is not used in the salting
+principal. For example, a user with UPN foo@mydomain.com in the realm
+MYREALM.COM would have a salt of MYREALM.COMfoo. Perhaps this is to
+allow a user's UPN suffix to be changed without changing the salt. And
+perhaps using the UPN for salting signifies a move away SAM names and
+their associated constraints.
+
+For more information on how UPNs relate to the Kerberos protocol,
+see:
+
+http://www.ietf.org/proceedings/01dec/I-D/draft-ietf-krb-wg-kerberos-referrals-02.txt
+
+-- Luke
+
+--
+
+
+
+
+Heimdal oddities
+----------------
+
+Heimdal is built such that it should be able to serve multiple realms
+at the same time. This isn't relevant for Samba's use, but it shows
+up in a lot of generalisations throughout the code.
+
+Other odd things:
+ - Support for multiple passwords on a client account: we seem to
+ call hdb_next_enctype2key() in the pre-authentication routines to
+ allow multiple passwords per account in krb5. (I think this was
+ intened to allow multiple salts)
+
+State Machine safety
+--------------------
+
+Samba is a giant state machine, and as such have very different
+requirements to those traditionally expressed for kerberos and GSSAPI
+libraries.
+
+Samba requires all of the libraries it uses to be state machine safe in
+their use of internal data. This does not mean thread safe, and an
+application could be thread safe, but not state machine safe (if it
+instead used thread-local variables).
+
+So, what does it mean for a library to be state machine safe? This is
+mostly a question of context, and how the library manages whatever
+internal state machines it has. If the library uses a context
+variable, passed in by the caller, which contains all the information
+about the current state of the library, then it is safe. An example
+of this state is the sequence number and session keys for an ongoing
+encrypted session).
+
+The other issue affecting state machines is 'blocking' (waiting for a
+read on a network socket).
+
+Heimdal has this 'state machine safety' in parts, and we have modified
+the lorikeet branch to improve this behviour, when using a new,
+non-standard API.
+
+Heimdal uses a per-context variable for the 'krb5_auth_context', which
+controls the ongoing encrypted connection, but does use global
+variables for the ubiquitous krb5_context parameter.
+
+The modification that has added most to 'state machine safety' of
+GSSAPI is the addition of the gsskrb5_acquire_creds function. This
+allows the caller to specify a keytab and ccache, for use by the
+GSSAPI code. Therefore there is no need to use global variables to
+communicate this information.
+
+At a more theoritical level (simply counting static and global
+variables) Heimdal is not state machine safe for the GSSAPI layer.
+The Krb5 layer alone is much closer, as far as I can tell, blocking
+excepted. .
+
+To deal with blocking, we could have a fork()ed child per context,
+using the 'GSSAPI export context' function to transfer
+the GSSAPI state back into the main code for the wrap()/unwrap() part
+of the operation. This will still hit issues of static storage (one
+gss_krb5_context per process, and multiple GSSAPI encrypted sessions
+at a time) but these may not matter in practice.
+
+In the short-term, we deal with blocking by taking over the network
+send() and recv() functions, therefore making them 'semi-async'. This
+doens't apply to DNS yet.
+
+GSSAPI and Kerberos extensions
+------------------------------
+
+This is a general list of the other extensions we have made to / need from
+the kerberos libraries
+
+ - DCE_STYLE
+
+ - gsskrb5_get_initiator_subkey() (return the exact key that Samba3
+ has always asked for. gsskrb5_get_subkey() might do what we need
+ anyway)
+
+ - gsskrb5_acquire_creds() (takes keytab and/or ccache as input
+ parameters, see keytab and state machine discussion)
+
+ - gss_krb5_copy_service_keyblock() (get the key used to actually
+ encrypt the ticket to the server, because the same key is used for
+ the PAC validation).
+ - gsskrb5_extract_authtime_from_sec_context (get authtime from
+ kerberos ticket)
+ - gsskrb5_extract_authz_data_from_sec_context (get authdata from
+ ticket, ie the PAC. Must unwrap the data if in an AD-IFRELEVENT)
+ - gsskrb5_wrap_size (find out how big the wrapped packet will be,
+ given input length).
+
+Keytab requirements
+-------------------
+
+Because windows machine account handling is very different to the
+tranditional 'MIT' keytab operation. This starts when we look at the
+basis of the secrets handling:
+
+Traditional 'MIT' behaviour is to use a keytab, continaing salted key
+data, extracted from the KDC. (In this modal, there is no 'service
+password', instead the keys are often simply application of random
+bytes). Heimdal also implements this behaviour.
+
+The windows modal is very different - instead of sharing a keytab with
+each member server, a password is stored for the whole machine. The
+password is set with non-kerberos mechanisms (particularly SAMR, a
+DCE-RPC service) and when interacting on a kerberos basis, the
+password is salted by the client. (That is, no salt infromation
+appears to be convayed from the KDC to the member).
+
+In dealing with this modal, we leverage both the traditional file
+keytab and in-MEMORY keytabs.
+
+When dealing with a windows KDC, the behaviour regarding case
+sensitivity and canonacolisation must be accomidated. This means that
+an incoming request to a member server may have a wide variety of
+service principal names. These include:
+
+machine$@REALM (samba clients)
+HOST/foo.bar@realm (win2k clients)
+HOST/foo@realm (win2k clients, using netbios)
+cifs/foo.bar@realm (winxp clients)
+cifs/foo@realm (winxp clients, using netbios)
+
+as well as all case variations on the above.
+
+Because that all got 'too hard' to put into a keytab in the
+traditional way (with the client to specify the name), we either
+pre-compute the keys into a traditional keytab or make an in-MEMORY
+keytab at run time. In both cases we specifiy the principal name to
+GSSAPI, which avoids the need to store duplicate principals.
+
+We use a 'private' keytab in our private dir, referenced from the
+secrets.ldb by default.
+
+Extra Heimdal functions used
+----------------------------
+(an attempt to list some of the Heimdal-specific functions I know we use)
+
+krb5_free_keyblock_contents()
+
+also a raft of prinicpal manipulation functions:
+
+Prncipal Manipulation
+---------------------
+
+Samba makes extensive use of the principal manipulation functions in
+Heimdal, including the known structure behind krb_principal and
+krb5_realm (a char *).
+
+Authz data extraction
+---------------------
+
+We use krb5_ticket_get_authorization_data_type(), and expect it to
+return the correct authz data, even if wrapped in an AD-IFRELEVENT container.
+
+
+KDC/hdb Extensions
+--------------
+
+We have modified Heimdal's 'hdb' interface to specify the 'type' of
+Principal being requested. This allows us to correctly behave with
+the different 'classes' of Principal name.
+
+We currently define 2 classes:
+ - client (kinit)
+ - server (tgt)
+
+I also now specify the kerberos principal as an explict parameter, not
+an in/out value on the entry itself.
+
+Inside hdb-ldb, we add krbtgt as a special class of principal, because
+of particular special-case backend requirements.
+
+Callbacks:
+ In addition, I have added a new interface hdb_fetch_ex(), which
+ returns a structure including callbacks, which provide the hook for
+ the PAC, as well as a callback into the main access control routines.
+
+ A new callback should be added to increment the bad password counter
+ on failure.
+
+ Another possability for a callback is to obtain the keys. This would
+ allow the plaintext password to only be hashed into the encryption
+ types we need. This idea from the eDirectory/MIT DAL work.
+
+ This probably should be combined with storing the hashed passwords in
+ the supplementalCredentials attribute. If combined with a kvno
+ parameter, this could also allow changing of the krbtgt password
+ (valuable for security).
+
+libkdc
+------
+
+Samba4 needs to be built as a single binary (design requirement), and
+this should include the KDC. Samba also (and perhaps more
+importantly) needs to control the configuration environment of the
+KDC.
+
+The interface we have defined for libkdc allow for packet injection
+into the post-socket layer, with a defined krb5_context and
+kdb5_kdc_configuration structure. These effectively redirect the
+kerberos warnings, logging and database calls as we require.
+
+Using our socket lib
+--------------------
+
+An important detail in the use of libkdc is that we use our own socket
+lib. This allows the KDC code to be as portable as the rest of samba
+(this cuts both ways), but far more importantly it ensures a
+consistancy in the handling of requests, binding to sockets etc.
+
+To handle TCP, we use of our socket layer in much the same way as
+we deal with TCP for CIFS. Tridge created a generic packet handling
+layer for this.
+
+For the client, we likewise must take over the socket functions, so
+that our single thread smbd will not lock up talking to itself. (We
+allow processing while waiting for packets in our socket routines).
+
+Kerberos logging support
+------------------------
+
+Samba now (optionally in the main code, required for the KDC) uses the
+krb5_log_facility from Heimdal. This allows us to redirect the
+warnings and status from the KDC (and client/server kerberos code) to
+Samba's DEBUG() system.
+
+Similarly important is the Heimdal-specific krb5_get_error_string()
+function, which does a lot to reduce the 'administrator pain' level,
+by providing specific, english text-string error messages instead of
+just error code translations.
+
+
+Short name rules
+----------------
+
+Samba is highly likely to be misconfigured, in many weird and
+interesting ways. As such, we have a patch for Heimdal that avoids
+DNS lookups on names without a . in them. This should avoid some
+delay and root server load.
+
+PAC Correctness
+---------------
+
+We now put the PAC into the TGT, not just the service ticket.
+
+Forwarded tickets
+-----------------
+
+We extract forwarded tickets from the GSSAPI layer, and put
+them into the credentials. We can then use them for proxy work.
+
+
+Kerberos TODO
+=============
+
+(Feel free to contribute to any of these tasks, or ask
+abartlet@samba.org about them).
+
+Lockout Control
+--------------
+
+We need to get (either if PADL publishes their patch, or write our
+own) access control hooks in the Heimdal KDC. We need to lockout
+accounts, and perform other controls.
+
+Gssmonger
+---------
+
+Microsoft has released a testsuite called gssmonger, which tests
+interop. We should compile it against lorikeet-heimdal, MIT and see
+if we can build a 'Samba4' server for it.
+
+Kpasswd server
+--------------
+
+I have a partial kpasswd server which needs finishing, and a we need a
+client testsuite written, either via the krb5 API or directly against
+GENSEC and the ASN.1 routines.
+
+Currently it only works for Heimdal, not MIT clients. This may be due
+to call ordering constraints.
+
+
+Correct TCP support
+-------------------
+
+Our current TCP support does not send back 'too large' error messages
+if the high bit is set. This is needed for a proposed extension
+mechanism, but is likewise unsupported in both current Heimdal and MIT.
diff --git a/source4/auth/kerberos/kerberos.c b/source4/auth/kerberos/kerberos.c
new file mode 100644
index 0000000000..1889dcab4d
--- /dev/null
+++ b/source4/auth/kerberos/kerberos.c
@@ -0,0 +1,123 @@
+/*
+ Unix SMB/CIFS implementation.
+ kerberos utility library
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Remus Koos 2001
+ Copyright (C) Nalin Dahyabhai 2004.
+ Copyright (C) Jeremy Allison 2004.
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/kerberos.h"
+#include "auth/kerberos/kerberos.h"
+
+#ifdef HAVE_KRB5
+
+/*
+ simulate a kinit, putting the tgt in the given credentials cache.
+ Orignally by remus@snapserver.com
+
+ This version is built to use a keyblock, rather than needing the
+ original password.
+*/
+ krb5_error_code kerberos_kinit_keyblock_cc(krb5_context ctx, krb5_ccache cc,
+ krb5_principal principal, krb5_keyblock *keyblock,
+ time_t *expire_time, time_t *kdc_time)
+{
+ krb5_error_code code = 0;
+ krb5_creds my_creds;
+ krb5_get_init_creds_opt options;
+
+ krb5_get_init_creds_opt_init(&options);
+
+ krb5_get_init_creds_opt_set_default_flags(ctx, NULL, NULL, &options);
+
+ if ((code = krb5_get_init_creds_keyblock(ctx, &my_creds, principal, keyblock,
+ 0, NULL, &options))) {
+ return code;
+ }
+
+ if ((code = krb5_cc_initialize(ctx, cc, principal))) {
+ krb5_free_cred_contents(ctx, &my_creds);
+ return code;
+ }
+
+ if ((code = krb5_cc_store_cred(ctx, cc, &my_creds))) {
+ krb5_free_cred_contents(ctx, &my_creds);
+ return code;
+ }
+
+ if (expire_time) {
+ *expire_time = (time_t) my_creds.times.endtime;
+ }
+
+ if (kdc_time) {
+ *kdc_time = (time_t) my_creds.times.starttime;
+ }
+
+ krb5_free_cred_contents(ctx, &my_creds);
+
+ return 0;
+}
+
+/*
+ simulate a kinit, putting the tgt in the given credentials cache.
+ Orignally by remus@snapserver.com
+*/
+ krb5_error_code kerberos_kinit_password_cc(krb5_context ctx, krb5_ccache cc,
+ krb5_principal principal, const char *password,
+ time_t *expire_time, time_t *kdc_time)
+{
+ krb5_error_code code = 0;
+ krb5_creds my_creds;
+ krb5_get_init_creds_opt options;
+
+ krb5_get_init_creds_opt_init(&options);
+
+ krb5_get_init_creds_opt_set_default_flags(ctx, NULL, NULL, &options);
+
+ if ((code = krb5_get_init_creds_password(ctx, &my_creds, principal, password,
+ NULL,
+ NULL, 0, NULL, &options))) {
+ return code;
+ }
+
+ if ((code = krb5_cc_initialize(ctx, cc, principal))) {
+ krb5_free_cred_contents(ctx, &my_creds);
+ return code;
+ }
+
+ if ((code = krb5_cc_store_cred(ctx, cc, &my_creds))) {
+ krb5_free_cred_contents(ctx, &my_creds);
+ return code;
+ }
+
+ if (expire_time) {
+ *expire_time = (time_t) my_creds.times.endtime;
+ }
+
+ if (kdc_time) {
+ *kdc_time = (time_t) my_creds.times.starttime;
+ }
+
+ krb5_free_cred_contents(ctx, &my_creds);
+
+ return 0;
+}
+
+
+#endif
diff --git a/source4/auth/kerberos/kerberos.h b/source4/auth/kerberos/kerberos.h
new file mode 100644
index 0000000000..8585aa321b
--- /dev/null
+++ b/source4/auth/kerberos/kerberos.h
@@ -0,0 +1,153 @@
+/*
+ Unix SMB/CIFS implementation.
+ simple kerberos5 routines for active directory
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Luke Howard 2002-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 <http://www.gnu.org/licenses/>.
+*/
+
+#if defined(HAVE_KRB5)
+
+#include "auth/kerberos/krb5_init_context.h"
+#include "librpc/gen_ndr/krb5pac.h"
+
+struct auth_serversupplied_info;
+struct cli_credentials;
+
+struct ccache_container {
+ struct smb_krb5_context *smb_krb5_context;
+ krb5_ccache ccache;
+};
+
+struct keytab_container {
+ struct smb_krb5_context *smb_krb5_context;
+ krb5_keytab keytab;
+};
+
+/* not really ASN.1, but RFC 1964 */
+#define TOK_ID_KRB_AP_REQ ((const uint8_t *)"\x01\x00")
+#define TOK_ID_KRB_AP_REP ((const uint8_t *)"\x02\x00")
+#define TOK_ID_KRB_ERROR ((const uint8_t *)"\x03\x00")
+#define TOK_ID_GSS_GETMIC ((const uint8_t *)"\x01\x01")
+#define TOK_ID_GSS_WRAP ((const uint8_t *)"\x02\x01")
+
+#ifdef HAVE_KRB5_KEYBLOCK_KEYVALUE
+#define KRB5_KEY_TYPE(k) ((k)->keytype)
+#define KRB5_KEY_LENGTH(k) ((k)->keyvalue.length)
+#define KRB5_KEY_DATA(k) ((k)->keyvalue.data)
+#else
+#define KRB5_KEY_TYPE(k) ((k)->enctype)
+#define KRB5_KEY_LENGTH(k) ((k)->length)
+#define KRB5_KEY_DATA(k) ((k)->contents)
+#endif /* HAVE_KRB5_KEYBLOCK_KEYVALUE */
+
+#ifndef HAVE_KRB5_SET_REAL_TIME
+krb5_error_code krb5_set_real_time(krb5_context context, int32_t seconds, int32_t microseconds);
+#endif
+
+#ifndef HAVE_KRB5_SET_DEFAULT_TGS_KTYPES
+krb5_error_code krb5_set_default_tgs_ktypes(krb5_context ctx, const krb5_enctype *enc);
+#endif
+
+#if defined(HAVE_KRB5_AUTH_CON_SETKEY) && !defined(HAVE_KRB5_AUTH_CON_SETUSERUSERKEY)
+krb5_error_code krb5_auth_con_setuseruserkey(krb5_context context, krb5_auth_context auth_context, krb5_keyblock *keyblock);
+#endif
+
+#ifndef HAVE_KRB5_FREE_UNPARSED_NAME
+void krb5_free_unparsed_name(krb5_context ctx, char *val);
+#endif
+
+#if defined(HAVE_KRB5_PRINCIPAL_GET_COMP_STRING) && !defined(HAVE_KRB5_PRINC_COMPONENT)
+const krb5_data *krb5_princ_component(krb5_context context, krb5_principal principal, int i );
+#endif
+
+/* Samba wrapper function for krb5 functionality. */
+void setup_kaddr( krb5_address *pkaddr, struct sockaddr *paddr);
+int create_kerberos_key_from_string(krb5_context context, krb5_principal host_princ, krb5_data *password, krb5_keyblock *key, krb5_enctype enctype);
+int create_kerberos_key_from_string_direct(krb5_context context, krb5_principal host_princ, krb5_data *password, krb5_keyblock *key, krb5_enctype enctype);
+krb5_const_principal get_principal_from_tkt(krb5_ticket *tkt);
+krb5_error_code get_kerberos_allowed_etypes(krb5_context context, krb5_enctype **enctypes);
+void free_kerberos_etypes(krb5_context context, krb5_enctype *enctypes);
+bool get_krb5_smb_session_key(krb5_context context, krb5_auth_context auth_context, DATA_BLOB *session_key, bool remote);
+krb5_error_code ads_krb5_mk_req(krb5_context context,
+ krb5_auth_context *auth_context,
+ const krb5_flags ap_req_options,
+ const char *principal,
+ krb5_ccache ccache,
+ krb5_data *outbuf);
+bool get_auth_data_from_tkt(TALLOC_CTX *mem_ctx, DATA_BLOB *auth_data, krb5_ticket *tkt);
+int kerberos_kinit_password_cc(krb5_context ctx, krb5_ccache cc,
+ krb5_principal principal, const char *password,
+ time_t *expire_time, time_t *kdc_time);
+int kerberos_kinit_keyblock_cc(krb5_context ctx, krb5_ccache cc,
+ krb5_principal principal, krb5_keyblock *keyblock,
+ time_t *expire_time, time_t *kdc_time);
+krb5_principal kerberos_fetch_salt_princ_for_host_princ(krb5_context context,
+ krb5_principal host_princ,
+ int enctype);
+void kerberos_set_creds_enctype(krb5_creds *pcreds, int enctype);
+bool kerberos_compatible_enctypes(krb5_context context, krb5_enctype enctype1, krb5_enctype enctype2);
+void kerberos_free_data_contents(krb5_context context, krb5_data *pdata);
+krb5_error_code smb_krb5_kt_free_entry(krb5_context context, krb5_keytab_entry *kt_entry);
+char *smb_get_krb5_error_message(krb5_context context, krb5_error_code code, TALLOC_CTX *mem_ctx);
+ krb5_error_code kinit_to_ccache(TALLOC_CTX *parent_ctx,
+ struct cli_credentials *credentials,
+ struct smb_krb5_context *smb_krb5_context,
+ krb5_ccache ccache);
+krb5_error_code principal_from_credentials(TALLOC_CTX *parent_ctx,
+ struct cli_credentials *credentials,
+ struct smb_krb5_context *smb_krb5_context,
+ krb5_principal *princ);
+NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx,
+ struct smb_iconv_convenience *iconv_convenience,
+ struct PAC_DATA **pac_data_out,
+ DATA_BLOB blob,
+ krb5_context context,
+ const krb5_keyblock *krbtgt_keyblock,
+ const krb5_keyblock *service_keyblock,
+ krb5_const_principal client_principal,
+ time_t tgs_authtime,
+ krb5_error_code *k5ret);
+ NTSTATUS kerberos_pac_logon_info(TALLOC_CTX *mem_ctx,
+ struct smb_iconv_convenience *iconv_convenience,
+ struct PAC_LOGON_INFO **logon_info,
+ DATA_BLOB blob,
+ krb5_context context,
+ const krb5_keyblock *krbtgt_keyblock,
+ const krb5_keyblock *service_keyblock,
+ krb5_const_principal client_principal,
+ time_t tgs_authtime,
+ krb5_error_code *k5ret);
+ krb5_error_code kerberos_encode_pac(TALLOC_CTX *mem_ctx,
+ struct smb_iconv_convenience *iconv_convenience,
+ struct PAC_DATA *pac_data,
+ krb5_context context,
+ const krb5_keyblock *krbtgt_keyblock,
+ const krb5_keyblock *service_keyblock,
+ DATA_BLOB *pac);
+ krb5_error_code kerberos_create_pac(TALLOC_CTX *mem_ctx,
+ struct smb_iconv_convenience *iconv_convenience,
+ struct auth_serversupplied_info *server_info,
+ krb5_context context,
+ const krb5_keyblock *krbtgt_keyblock,
+ const krb5_keyblock *service_keyblock,
+ krb5_principal client_principal,
+ time_t tgs_authtime,
+ DATA_BLOB *pac);
+struct loadparm_context;
+
+#include "auth/kerberos/proto.h"
+
+#endif /* HAVE_KRB5 */
diff --git a/source4/auth/kerberos/kerberos_heimdal.c b/source4/auth/kerberos/kerberos_heimdal.c
new file mode 100644
index 0000000000..44cb39c518
--- /dev/null
+++ b/source4/auth/kerberos/kerberos_heimdal.c
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 1997 - 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* This file for code taken from the Heimdal code, to preserve licence */
+/* Modified by Andrew Bartlett <abartlet@samba.org> */
+
+#include "includes.h"
+#include "system/kerberos.h"
+#include "auth/kerberos/kerberos.h"
+
+/* Taken from accept_sec_context.c,v 1.65 */
+krb5_error_code smb_rd_req_return_stuff(krb5_context context,
+ krb5_auth_context *auth_context,
+ const krb5_data *inbuf,
+ krb5_keytab keytab,
+ krb5_principal acceptor_principal,
+ krb5_data *outbuf,
+ krb5_ticket **ticket,
+ krb5_keyblock **keyblock)
+{
+ krb5_rd_req_in_ctx in = NULL;
+ krb5_rd_req_out_ctx out = NULL;
+ krb5_error_code kret;
+
+ *keyblock = NULL;
+ *ticket = NULL;
+ outbuf->length = 0;
+ outbuf->data = NULL;
+
+ kret = krb5_rd_req_in_ctx_alloc(context, &in);
+ if (kret == 0)
+ kret = krb5_rd_req_in_set_keytab(context, in, keytab);
+ if (kret) {
+ if (in)
+ krb5_rd_req_in_ctx_free(context, in);
+ return kret;
+ }
+
+ kret = krb5_rd_req_ctx(context,
+ auth_context,
+ inbuf,
+ acceptor_principal,
+ in, &out);
+ krb5_rd_req_in_ctx_free(context, in);
+ if (kret) {
+ return kret;
+ }
+
+ /*
+ * We need to remember some data on the context_handle.
+ */
+ kret = krb5_rd_req_out_get_ticket(context, out,
+ ticket);
+ if (kret == 0) {
+ kret = krb5_rd_req_out_get_keyblock(context, out,
+ keyblock);
+ }
+ krb5_rd_req_out_ctx_free(context, out);
+
+ if (kret == 0) {
+ kret = krb5_mk_rep(context, *auth_context, outbuf);
+ }
+
+ if (kret) {
+ krb5_free_ticket(context, *ticket);
+ krb5_free_keyblock(context, *keyblock);
+ krb5_data_free(outbuf);
+ }
+
+ return kret;
+}
+
diff --git a/source4/auth/kerberos/kerberos_pac.c b/source4/auth/kerberos/kerberos_pac.c
new file mode 100644
index 0000000000..7a36c9ddea
--- /dev/null
+++ b/source4/auth/kerberos/kerberos_pac.c
@@ -0,0 +1,776 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Create and parse the krb5 PAC
+
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005,2008
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Luke Howard 2002-2003
+ Copyright (C) Stefan Metzmacher 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/kerberos.h"
+#include "auth/auth.h"
+#include "auth/kerberos/kerberos.h"
+#include "librpc/gen_ndr/ndr_krb5pac.h"
+#include "lib/ldb/include/ldb.h"
+#include "auth/auth_sam_reply.h"
+
+krb5_error_code check_pac_checksum(TALLOC_CTX *mem_ctx,
+ DATA_BLOB pac_data,
+ struct PAC_SIGNATURE_DATA *sig,
+ krb5_context context,
+ const krb5_keyblock *keyblock)
+{
+ krb5_error_code ret;
+ krb5_crypto crypto;
+ Checksum cksum;
+
+ cksum.cksumtype = (CKSUMTYPE)sig->type;
+ cksum.checksum.length = sig->signature.length;
+ cksum.checksum.data = sig->signature.data;
+
+ ret = krb5_crypto_init(context,
+ keyblock,
+ 0,
+ &crypto);
+ if (ret) {
+ DEBUG(0,("krb5_crypto_init() failed: %s\n",
+ smb_get_krb5_error_message(context, ret, mem_ctx)));
+ return ret;
+ }
+ ret = krb5_verify_checksum(context,
+ crypto,
+ KRB5_KU_OTHER_CKSUM,
+ pac_data.data,
+ pac_data.length,
+ &cksum);
+ krb5_crypto_destroy(context, crypto);
+
+ return ret;
+}
+
+ NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx,
+ struct smb_iconv_convenience *iconv_convenience,
+ struct PAC_DATA **pac_data_out,
+ DATA_BLOB blob,
+ krb5_context context,
+ const krb5_keyblock *krbtgt_keyblock,
+ const krb5_keyblock *service_keyblock,
+ krb5_const_principal client_principal,
+ time_t tgs_authtime,
+ krb5_error_code *k5ret)
+{
+ krb5_error_code ret;
+ NTSTATUS status;
+ enum ndr_err_code ndr_err;
+ struct PAC_SIGNATURE_DATA *srv_sig_ptr = NULL;
+ struct PAC_SIGNATURE_DATA *kdc_sig_ptr = NULL;
+ struct PAC_SIGNATURE_DATA *srv_sig_wipe = NULL;
+ struct PAC_SIGNATURE_DATA *kdc_sig_wipe = NULL;
+ struct PAC_LOGON_INFO *logon_info = NULL;
+ struct PAC_LOGON_NAME *logon_name = NULL;
+ struct PAC_DATA *pac_data;
+ struct PAC_DATA_RAW *pac_data_raw;
+
+ DATA_BLOB *srv_sig_blob = NULL;
+ DATA_BLOB *kdc_sig_blob = NULL;
+
+ DATA_BLOB modified_pac_blob;
+ NTTIME tgs_authtime_nttime;
+ krb5_principal client_principal_pac;
+ int i;
+
+ krb5_clear_error_string(context);
+
+ if (k5ret) {
+ *k5ret = KRB5_PARSE_MALFORMED;
+ }
+
+ pac_data = talloc(mem_ctx, struct PAC_DATA);
+ pac_data_raw = talloc(mem_ctx, struct PAC_DATA_RAW);
+ kdc_sig_wipe = talloc(mem_ctx, struct PAC_SIGNATURE_DATA);
+ srv_sig_wipe = talloc(mem_ctx, struct PAC_SIGNATURE_DATA);
+ if (!pac_data_raw || !pac_data || !kdc_sig_wipe || !srv_sig_wipe) {
+ if (k5ret) {
+ *k5ret = ENOMEM;
+ }
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ndr_err = ndr_pull_struct_blob(&blob, pac_data,
+ iconv_convenience, pac_data,
+ (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ DEBUG(0,("can't parse the PAC: %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+
+ if (pac_data->num_buffers < 4) {
+ /* we need logon_ingo, service_key and kdc_key */
+ DEBUG(0,("less than 4 PAC buffers\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ ndr_err = ndr_pull_struct_blob(&blob, pac_data_raw,
+ iconv_convenience, pac_data_raw,
+ (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA_RAW);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ DEBUG(0,("can't parse the PAC: %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+
+ if (pac_data_raw->num_buffers < 4) {
+ /* we need logon_ingo, service_key and kdc_key */
+ DEBUG(0,("less than 4 PAC buffers\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (pac_data->num_buffers != pac_data_raw->num_buffers) {
+ /* we need logon_ingo, service_key and kdc_key */
+ DEBUG(0,("misparse! PAC_DATA has %d buffers while PAC_DATA_RAW has %d\n",
+ pac_data->num_buffers, pac_data_raw->num_buffers));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ for (i=0; i < pac_data->num_buffers; i++) {
+ if (pac_data->buffers[i].type != pac_data_raw->buffers[i].type) {
+ DEBUG(0,("misparse! PAC_DATA buffer %d has type %d while PAC_DATA_RAW has %d\n",
+ i, pac_data->buffers[i].type, pac_data->buffers[i].type));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ switch (pac_data->buffers[i].type) {
+ case PAC_TYPE_LOGON_INFO:
+ if (!pac_data->buffers[i].info) {
+ break;
+ }
+ logon_info = pac_data->buffers[i].info->logon_info.info;
+ break;
+ case PAC_TYPE_SRV_CHECKSUM:
+ if (!pac_data->buffers[i].info) {
+ break;
+ }
+ srv_sig_ptr = &pac_data->buffers[i].info->srv_cksum;
+ srv_sig_blob = &pac_data_raw->buffers[i].info->remaining;
+ break;
+ case PAC_TYPE_KDC_CHECKSUM:
+ if (!pac_data->buffers[i].info) {
+ break;
+ }
+ kdc_sig_ptr = &pac_data->buffers[i].info->kdc_cksum;
+ kdc_sig_blob = &pac_data_raw->buffers[i].info->remaining;
+ break;
+ case PAC_TYPE_LOGON_NAME:
+ logon_name = &pac_data->buffers[i].info->logon_name;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (!logon_info) {
+ DEBUG(0,("PAC no logon_info\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!logon_name) {
+ DEBUG(0,("PAC no logon_name\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!srv_sig_ptr || !srv_sig_blob) {
+ DEBUG(0,("PAC no srv_key\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!kdc_sig_ptr || !kdc_sig_blob) {
+ DEBUG(0,("PAC no kdc_key\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* Find and zero out the signatures, as required by the signing algorithm */
+
+ /* We find the data blobs above, now we parse them to get at the exact portion we should zero */
+ ndr_err = ndr_pull_struct_blob(kdc_sig_blob, kdc_sig_wipe,
+ iconv_convenience, kdc_sig_wipe,
+ (ndr_pull_flags_fn_t)ndr_pull_PAC_SIGNATURE_DATA);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ DEBUG(0,("can't parse the KDC signature: %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+
+ ndr_err = ndr_pull_struct_blob(srv_sig_blob, srv_sig_wipe,
+ iconv_convenience, srv_sig_wipe,
+ (ndr_pull_flags_fn_t)ndr_pull_PAC_SIGNATURE_DATA);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ DEBUG(0,("can't parse the SRV signature: %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+
+ /* Now zero the decoded structure */
+ memset(kdc_sig_wipe->signature.data, '\0', kdc_sig_wipe->signature.length);
+ memset(srv_sig_wipe->signature.data, '\0', srv_sig_wipe->signature.length);
+
+ /* and reencode, back into the same place it came from */
+ ndr_err = ndr_push_struct_blob(kdc_sig_blob, pac_data_raw,
+ iconv_convenience,
+ kdc_sig_wipe,
+ (ndr_push_flags_fn_t)ndr_push_PAC_SIGNATURE_DATA);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ DEBUG(0,("can't repack the KDC signature: %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+ ndr_err = ndr_push_struct_blob(srv_sig_blob, pac_data_raw,
+ iconv_convenience,
+ srv_sig_wipe,
+ (ndr_push_flags_fn_t)ndr_push_PAC_SIGNATURE_DATA);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ DEBUG(0,("can't repack the SRV signature: %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+
+ /* push out the whole structure, but now with zero'ed signatures */
+ ndr_err = ndr_push_struct_blob(&modified_pac_blob, pac_data_raw,
+ iconv_convenience,
+ pac_data_raw,
+ (ndr_push_flags_fn_t)ndr_push_PAC_DATA_RAW);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ DEBUG(0,("can't repack the RAW PAC: %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+
+ /* verify by service_key */
+ ret = check_pac_checksum(mem_ctx,
+ modified_pac_blob, srv_sig_ptr,
+ context,
+ service_keyblock);
+ if (ret) {
+ DEBUG(1, ("PAC Decode: Failed to verify the service signature: %s\n",
+ smb_get_krb5_error_message(context, ret, mem_ctx)));
+ if (k5ret) {
+ *k5ret = ret;
+ }
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (krbtgt_keyblock) {
+ ret = check_pac_checksum(mem_ctx,
+ srv_sig_ptr->signature, kdc_sig_ptr,
+ context, krbtgt_keyblock);
+ if (ret) {
+ DEBUG(1, ("PAC Decode: Failed to verify the KDC signature: %s\n",
+ smb_get_krb5_error_message(context, ret, mem_ctx)));
+ if (k5ret) {
+ *k5ret = ret;
+ }
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ }
+
+ /* Convert to NT time, so as not to loose accuracy in comparison */
+ unix_to_nt_time(&tgs_authtime_nttime, tgs_authtime);
+
+ if (tgs_authtime_nttime != logon_name->logon_time) {
+ DEBUG(2, ("PAC Decode: Logon time mismatch between ticket and PAC!\n"));
+ DEBUG(2, ("PAC Decode: PAC: %s\n", nt_time_string(mem_ctx, logon_name->logon_time)));
+ DEBUG(2, ("PAC Decode: Ticket: %s\n", nt_time_string(mem_ctx, tgs_authtime_nttime)));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ ret = krb5_parse_name_flags(context, logon_name->account_name, KRB5_PRINCIPAL_PARSE_NO_REALM,
+ &client_principal_pac);
+ if (ret) {
+ DEBUG(2, ("Could not parse name from incoming PAC: [%s]: %s\n",
+ logon_name->account_name,
+ smb_get_krb5_error_message(context, ret, mem_ctx)));
+ if (k5ret) {
+ *k5ret = ret;
+ }
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!krb5_principal_compare_any_realm(context, client_principal, client_principal_pac)) {
+ DEBUG(2, ("Name in PAC [%s] does not match principal name in ticket\n",
+ logon_name->account_name));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+#if 0
+ if (strcasecmp(logon_info->info3.base.account_name.string,
+ "Administrator")== 0) {
+ file_save("tmp_pac_data-admin.dat",blob.data,blob.length);
+ }
+#endif
+
+ DEBUG(3,("Found account name from PAC: %s [%s]\n",
+ logon_info->info3.base.account_name.string,
+ logon_info->info3.base.full_name.string));
+ *pac_data_out = pac_data;
+
+ return NT_STATUS_OK;
+}
+
+_PUBLIC_ NTSTATUS kerberos_pac_logon_info(TALLOC_CTX *mem_ctx,
+ struct smb_iconv_convenience *iconv_convenience,
+ struct PAC_LOGON_INFO **logon_info,
+ DATA_BLOB blob,
+ krb5_context context,
+ const krb5_keyblock *krbtgt_keyblock,
+ const krb5_keyblock *service_keyblock,
+ krb5_const_principal client_principal,
+ time_t tgs_authtime,
+ krb5_error_code *k5ret)
+{
+ NTSTATUS nt_status;
+ struct PAC_DATA *pac_data;
+ int i;
+ nt_status = kerberos_decode_pac(mem_ctx,
+ iconv_convenience,
+ &pac_data,
+ blob,
+ context,
+ krbtgt_keyblock,
+ service_keyblock,
+ client_principal,
+ tgs_authtime,
+ k5ret);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ *logon_info = NULL;
+ for (i=0; i < pac_data->num_buffers; i++) {
+ if (pac_data->buffers[i].type != PAC_TYPE_LOGON_INFO) {
+ continue;
+ }
+ *logon_info = pac_data->buffers[i].info->logon_info.info;
+ }
+ if (!*logon_info) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ return NT_STATUS_OK;
+}
+
+static krb5_error_code make_pac_checksum(TALLOC_CTX *mem_ctx,
+ DATA_BLOB *pac_data,
+ struct PAC_SIGNATURE_DATA *sig,
+ krb5_context context,
+ const krb5_keyblock *keyblock)
+{
+ krb5_error_code ret;
+ krb5_crypto crypto;
+ Checksum cksum;
+
+
+ ret = krb5_crypto_init(context,
+ keyblock,
+ 0,
+ &crypto);
+ if (ret) {
+ DEBUG(0,("krb5_crypto_init() failed: %s\n",
+ smb_get_krb5_error_message(context, ret, mem_ctx)));
+ return ret;
+ }
+ ret = krb5_create_checksum(context,
+ crypto,
+ KRB5_KU_OTHER_CKSUM,
+ 0,
+ pac_data->data,
+ pac_data->length,
+ &cksum);
+ if (ret) {
+ DEBUG(2, ("PAC Verification failed: %s\n",
+ smb_get_krb5_error_message(context, ret, mem_ctx)));
+ }
+
+ krb5_crypto_destroy(context, crypto);
+
+ if (ret) {
+ return ret;
+ }
+
+ sig->type = cksum.cksumtype;
+ sig->signature = data_blob_talloc(mem_ctx, cksum.checksum.data, cksum.checksum.length);
+ free_Checksum(&cksum);
+
+ return 0;
+}
+
+ krb5_error_code kerberos_encode_pac(TALLOC_CTX *mem_ctx,
+ struct smb_iconv_convenience *iconv_convenience,
+ struct PAC_DATA *pac_data,
+ krb5_context context,
+ const krb5_keyblock *krbtgt_keyblock,
+ const krb5_keyblock *service_keyblock,
+ DATA_BLOB *pac)
+{
+ NTSTATUS nt_status;
+ krb5_error_code ret;
+ enum ndr_err_code ndr_err;
+ DATA_BLOB zero_blob = data_blob(NULL, 0);
+ DATA_BLOB tmp_blob = data_blob(NULL, 0);
+ struct PAC_SIGNATURE_DATA *kdc_checksum = NULL;
+ struct PAC_SIGNATURE_DATA *srv_checksum = NULL;
+ int i;
+
+ /* First, just get the keytypes filled in (and lengths right, eventually) */
+ for (i=0; i < pac_data->num_buffers; i++) {
+ if (pac_data->buffers[i].type != PAC_TYPE_KDC_CHECKSUM) {
+ continue;
+ }
+ kdc_checksum = &pac_data->buffers[i].info->kdc_cksum,
+ ret = make_pac_checksum(mem_ctx, &zero_blob,
+ kdc_checksum,
+ context, krbtgt_keyblock);
+ if (ret) {
+ DEBUG(2, ("making krbtgt PAC checksum failed: %s\n",
+ smb_get_krb5_error_message(context, ret, mem_ctx)));
+ talloc_free(pac_data);
+ return ret;
+ }
+ }
+
+ for (i=0; i < pac_data->num_buffers; i++) {
+ if (pac_data->buffers[i].type != PAC_TYPE_SRV_CHECKSUM) {
+ continue;
+ }
+ srv_checksum = &pac_data->buffers[i].info->srv_cksum;
+ ret = make_pac_checksum(mem_ctx, &zero_blob,
+ srv_checksum,
+ context, service_keyblock);
+ if (ret) {
+ DEBUG(2, ("making service PAC checksum failed: %s\n",
+ smb_get_krb5_error_message(context, ret, mem_ctx)));
+ talloc_free(pac_data);
+ return ret;
+ }
+ }
+
+ if (!kdc_checksum) {
+ DEBUG(2, ("Invalid PAC constructed for signing, no KDC checksum present!"));
+ return EINVAL;
+ }
+ if (!srv_checksum) {
+ DEBUG(2, ("Invalid PAC constructed for signing, no SRV checksum present!"));
+ return EINVAL;
+ }
+
+ /* But wipe out the actual signatures */
+ memset(kdc_checksum->signature.data, '\0', kdc_checksum->signature.length);
+ memset(srv_checksum->signature.data, '\0', srv_checksum->signature.length);
+
+ ndr_err = ndr_push_struct_blob(&tmp_blob, mem_ctx,
+ iconv_convenience,
+ pac_data,
+ (ndr_push_flags_fn_t)ndr_push_PAC_DATA);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ nt_status = ndr_map_error2ntstatus(ndr_err);
+ DEBUG(1, ("PAC (presig) push failed: %s\n", nt_errstr(nt_status)));
+ talloc_free(pac_data);
+ return EINVAL;
+ }
+
+ /* Then sign the result of the previous push, where the sig was zero'ed out */
+ ret = make_pac_checksum(mem_ctx, &tmp_blob, srv_checksum,
+ context, service_keyblock);
+
+ /* Then sign Server checksum */
+ ret = make_pac_checksum(mem_ctx, &srv_checksum->signature, kdc_checksum, context, krbtgt_keyblock);
+ if (ret) {
+ DEBUG(2, ("making krbtgt PAC checksum failed: %s\n",
+ smb_get_krb5_error_message(context, ret, mem_ctx)));
+ talloc_free(pac_data);
+ return ret;
+ }
+
+ /* And push it out again, this time to the world. This relies on determanistic pointer values */
+ ndr_err = ndr_push_struct_blob(&tmp_blob, mem_ctx,
+ iconv_convenience,
+ pac_data,
+ (ndr_push_flags_fn_t)ndr_push_PAC_DATA);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ nt_status = ndr_map_error2ntstatus(ndr_err);
+ DEBUG(1, ("PAC (final) push failed: %s\n", nt_errstr(nt_status)));
+ talloc_free(pac_data);
+ return EINVAL;
+ }
+
+ *pac = tmp_blob;
+
+ return ret;
+}
+
+
+ krb5_error_code kerberos_create_pac(TALLOC_CTX *mem_ctx,
+ struct smb_iconv_convenience *iconv_convenience,
+ struct auth_serversupplied_info *server_info,
+ krb5_context context,
+ const krb5_keyblock *krbtgt_keyblock,
+ const krb5_keyblock *service_keyblock,
+ krb5_principal client_principal,
+ time_t tgs_authtime,
+ DATA_BLOB *pac)
+{
+ NTSTATUS nt_status;
+ krb5_error_code ret;
+ struct PAC_DATA *pac_data = talloc(mem_ctx, struct PAC_DATA);
+ struct netr_SamInfo3 *sam3;
+ union PAC_INFO *u_LOGON_INFO;
+ struct PAC_LOGON_INFO *LOGON_INFO;
+ union PAC_INFO *u_LOGON_NAME;
+ struct PAC_LOGON_NAME *LOGON_NAME;
+ union PAC_INFO *u_KDC_CHECKSUM;
+ union PAC_INFO *u_SRV_CHECKSUM;
+
+ char *name;
+
+ enum {
+ PAC_BUF_LOGON_INFO = 0,
+ PAC_BUF_LOGON_NAME = 1,
+ PAC_BUF_SRV_CHECKSUM = 2,
+ PAC_BUF_KDC_CHECKSUM = 3,
+ PAC_BUF_NUM_BUFFERS = 4
+ };
+
+ if (!pac_data) {
+ return ENOMEM;
+ }
+
+ pac_data->num_buffers = PAC_BUF_NUM_BUFFERS;
+ pac_data->version = 0;
+
+ pac_data->buffers = talloc_array(pac_data,
+ struct PAC_BUFFER,
+ pac_data->num_buffers);
+ if (!pac_data->buffers) {
+ talloc_free(pac_data);
+ return ENOMEM;
+ }
+
+ /* LOGON_INFO */
+ u_LOGON_INFO = talloc_zero(pac_data->buffers, union PAC_INFO);
+ if (!u_LOGON_INFO) {
+ talloc_free(pac_data);
+ return ENOMEM;
+ }
+ pac_data->buffers[PAC_BUF_LOGON_INFO].type = PAC_TYPE_LOGON_INFO;
+ pac_data->buffers[PAC_BUF_LOGON_INFO].info = u_LOGON_INFO;
+
+ /* LOGON_NAME */
+ u_LOGON_NAME = talloc_zero(pac_data->buffers, union PAC_INFO);
+ if (!u_LOGON_NAME) {
+ talloc_free(pac_data);
+ return ENOMEM;
+ }
+ pac_data->buffers[PAC_BUF_LOGON_NAME].type = PAC_TYPE_LOGON_NAME;
+ pac_data->buffers[PAC_BUF_LOGON_NAME].info = u_LOGON_NAME;
+ LOGON_NAME = &u_LOGON_NAME->logon_name;
+
+ /* SRV_CHECKSUM */
+ u_SRV_CHECKSUM = talloc_zero(pac_data->buffers, union PAC_INFO);
+ if (!u_SRV_CHECKSUM) {
+ talloc_free(pac_data);
+ return ENOMEM;
+ }
+ pac_data->buffers[PAC_BUF_SRV_CHECKSUM].type = PAC_TYPE_SRV_CHECKSUM;
+ pac_data->buffers[PAC_BUF_SRV_CHECKSUM].info = u_SRV_CHECKSUM;
+
+ /* KDC_CHECKSUM */
+ u_KDC_CHECKSUM = talloc_zero(pac_data->buffers, union PAC_INFO);
+ if (!u_KDC_CHECKSUM) {
+ talloc_free(pac_data);
+ return ENOMEM;
+ }
+ pac_data->buffers[PAC_BUF_KDC_CHECKSUM].type = PAC_TYPE_KDC_CHECKSUM;
+ pac_data->buffers[PAC_BUF_KDC_CHECKSUM].info = u_KDC_CHECKSUM;
+
+ /* now the real work begins... */
+
+ LOGON_INFO = talloc_zero(u_LOGON_INFO, struct PAC_LOGON_INFO);
+ if (!LOGON_INFO) {
+ talloc_free(pac_data);
+ return ENOMEM;
+ }
+ nt_status = auth_convert_server_info_saminfo3(LOGON_INFO, server_info, &sam3);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(1, ("Getting Samba info failed: %s\n", nt_errstr(nt_status)));
+ talloc_free(pac_data);
+ return EINVAL;
+ }
+
+ u_LOGON_INFO->logon_info.info = LOGON_INFO;
+ LOGON_INFO->info3 = *sam3;
+
+ ret = krb5_unparse_name_flags(context, client_principal,
+ KRB5_PRINCIPAL_UNPARSE_NO_REALM, &name);
+ if (ret) {
+ return ret;
+ }
+ LOGON_NAME->account_name = talloc_strdup(LOGON_NAME, name);
+ free(name);
+ /*
+ this logon_time field is absolutely critical. This is what
+ caused all our PAC troubles :-)
+ */
+ unix_to_nt_time(&LOGON_NAME->logon_time, tgs_authtime);
+
+ ret = kerberos_encode_pac(mem_ctx,
+ iconv_convenience,
+ pac_data,
+ context,
+ krbtgt_keyblock,
+ service_keyblock,
+ pac);
+ talloc_free(pac_data);
+ return ret;
+}
+
+krb5_error_code kerberos_pac_to_server_info(TALLOC_CTX *mem_ctx,
+ struct smb_iconv_convenience *iconv_convenience,
+ krb5_pac pac,
+ krb5_context context,
+ struct auth_serversupplied_info **server_info)
+{
+ NTSTATUS nt_status;
+ enum ndr_err_code ndr_err;
+ krb5_error_code ret;
+
+ DATA_BLOB pac_logon_info_in, pac_srv_checksum_in, pac_kdc_checksum_in;
+ krb5_data k5pac_logon_info_in, k5pac_srv_checksum_in, k5pac_kdc_checksum_in;
+
+ union PAC_INFO info;
+ union netr_Validation validation;
+ struct auth_serversupplied_info *server_info_out;
+
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ ret = krb5_pac_get_buffer(context, pac, PAC_TYPE_LOGON_INFO, &k5pac_logon_info_in);
+ if (ret != 0) {
+ talloc_free(tmp_ctx);
+ return EINVAL;
+ }
+
+ pac_logon_info_in = data_blob_const(k5pac_logon_info_in.data, k5pac_logon_info_in.length);
+
+ ndr_err = ndr_pull_union_blob(&pac_logon_info_in, tmp_ctx, iconv_convenience, &info,
+ PAC_TYPE_LOGON_INFO,
+ (ndr_pull_flags_fn_t)ndr_pull_PAC_INFO);
+ krb5_data_free(&k5pac_logon_info_in);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err) || !info.logon_info.info) {
+ nt_status = ndr_map_error2ntstatus(ndr_err);
+ DEBUG(0,("can't parse the PAC LOGON_INFO: %s\n", nt_errstr(nt_status)));
+ talloc_free(tmp_ctx);
+ return EINVAL;
+ }
+
+ /* Pull this right into the normal auth sysstem structures */
+ validation.sam3 = &info.logon_info.info->info3;
+ nt_status = make_server_info_netlogon_validation(mem_ctx,
+ "",
+ 3, &validation,
+ &server_info_out);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(tmp_ctx);
+ return EINVAL;
+ }
+
+ ret = krb5_pac_get_buffer(context, pac, PAC_TYPE_SRV_CHECKSUM, &k5pac_srv_checksum_in);
+ if (ret != 0) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ pac_srv_checksum_in = data_blob_const(k5pac_srv_checksum_in.data, k5pac_srv_checksum_in.length);
+
+ ndr_err = ndr_pull_struct_blob(&pac_srv_checksum_in, server_info_out,
+ iconv_convenience, &server_info_out->pac_srv_sig,
+ (ndr_pull_flags_fn_t)ndr_pull_PAC_SIGNATURE_DATA);
+ krb5_data_free(&k5pac_srv_checksum_in);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ nt_status = ndr_map_error2ntstatus(ndr_err);
+ DEBUG(0,("can't parse the KDC signature: %s\n",
+ nt_errstr(nt_status)));
+ return EINVAL;
+ }
+
+ ret = krb5_pac_get_buffer(context, pac, PAC_TYPE_KDC_CHECKSUM, &k5pac_kdc_checksum_in);
+ if (ret != 0) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ pac_kdc_checksum_in = data_blob_const(k5pac_kdc_checksum_in.data, k5pac_kdc_checksum_in.length);
+
+ ndr_err = ndr_pull_struct_blob(&pac_kdc_checksum_in, server_info_out,
+ iconv_convenience, &server_info_out->pac_kdc_sig,
+ (ndr_pull_flags_fn_t)ndr_pull_PAC_SIGNATURE_DATA);
+ krb5_data_free(&k5pac_kdc_checksum_in);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ nt_status = ndr_map_error2ntstatus(ndr_err);
+ DEBUG(0,("can't parse the KDC signature: %s\n",
+ nt_errstr(nt_status)));
+ return EINVAL;
+ }
+
+ *server_info = server_info_out;
+
+ return 0;
+}
+
+
+NTSTATUS kerberos_pac_blob_to_server_info(TALLOC_CTX *mem_ctx,
+ struct smb_iconv_convenience *iconv_convenience,
+ DATA_BLOB pac_blob,
+ krb5_context context,
+ struct auth_serversupplied_info **server_info)
+{
+ krb5_error_code ret;
+ krb5_pac pac;
+ ret = krb5_pac_parse(context,
+ pac_blob.data, pac_blob.length,
+ &pac);
+ if (ret) {
+ return map_nt_error_from_unix(ret);
+ }
+
+
+ ret = kerberos_pac_to_server_info(mem_ctx, iconv_convenience, pac, context, server_info);
+ krb5_pac_free(context, pac);
+ if (ret) {
+ return map_nt_error_from_unix(ret);
+ }
+ return NT_STATUS_OK;
+}
diff --git a/source4/auth/kerberos/kerberos_util.c b/source4/auth/kerberos/kerberos_util.c
new file mode 100644
index 0000000000..0567565d33
--- /dev/null
+++ b/source4/auth/kerberos/kerberos_util.c
@@ -0,0 +1,679 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Kerberos utility functions for GENSEC
+
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/kerberos.h"
+#include "auth/kerberos/kerberos.h"
+#include "auth/credentials/credentials.h"
+#include "auth/credentials/credentials_proto.h"
+#include "auth/credentials/credentials_krb5.h"
+
+struct principal_container {
+ struct smb_krb5_context *smb_krb5_context;
+ krb5_principal principal;
+};
+
+static krb5_error_code free_principal(struct principal_container *pc)
+{
+ /* current heimdal - 0.6.3, which we need anyway, fixes segfaults here */
+ krb5_free_principal(pc->smb_krb5_context->krb5_context, pc->principal);
+
+ return 0;
+}
+
+static krb5_error_code salt_principal_from_credentials(TALLOC_CTX *parent_ctx,
+ struct cli_credentials *machine_account,
+ struct smb_krb5_context *smb_krb5_context,
+ krb5_principal *salt_princ)
+{
+ krb5_error_code ret;
+ char *machine_username;
+ char *salt_body;
+ char *lower_realm;
+ const char *salt_principal;
+ struct principal_container *mem_ctx = talloc(parent_ctx, struct principal_container);
+ if (!mem_ctx) {
+ return ENOMEM;
+ }
+
+ salt_principal = cli_credentials_get_salt_principal(machine_account);
+ if (salt_principal) {
+ ret = krb5_parse_name(smb_krb5_context->krb5_context, salt_principal, salt_princ);
+ } else {
+ machine_username = talloc_strdup(mem_ctx, cli_credentials_get_username(machine_account));
+
+ if (!machine_username) {
+ talloc_free(mem_ctx);
+ return ENOMEM;
+ }
+
+ if (machine_username[strlen(machine_username)-1] == '$') {
+ machine_username[strlen(machine_username)-1] = '\0';
+ }
+ lower_realm = strlower_talloc(mem_ctx, cli_credentials_get_realm(machine_account));
+ if (!lower_realm) {
+ talloc_free(mem_ctx);
+ return ENOMEM;
+ }
+
+ salt_body = talloc_asprintf(mem_ctx, "%s.%s", machine_username,
+ lower_realm);
+ if (!salt_body) {
+ talloc_free(mem_ctx);
+ return ENOMEM;
+ }
+
+ ret = krb5_make_principal(smb_krb5_context->krb5_context, salt_princ,
+ cli_credentials_get_realm(machine_account),
+ "host", salt_body, NULL);
+ }
+
+ if (ret == 0) {
+ /* This song-and-dance effectivly puts the principal
+ * into talloc, so we can't loose it. */
+ mem_ctx->smb_krb5_context = talloc_reference(mem_ctx, smb_krb5_context);
+ mem_ctx->principal = *salt_princ;
+ talloc_set_destructor(mem_ctx, free_principal);
+ }
+ return ret;
+}
+
+/* Obtain the principal set on this context. Requires a
+ * smb_krb5_context because we are doing krb5 principal parsing with
+ * the library routines. The returned princ is placed in the talloc
+ * system by means of a destructor (do *not* free). */
+
+ krb5_error_code principal_from_credentials(TALLOC_CTX *parent_ctx,
+ struct cli_credentials *credentials,
+ struct smb_krb5_context *smb_krb5_context,
+ krb5_principal *princ)
+{
+ krb5_error_code ret;
+ const char *princ_string;
+ struct principal_container *mem_ctx = talloc(parent_ctx, struct principal_container);
+ if (!mem_ctx) {
+ return ENOMEM;
+ }
+
+ princ_string = cli_credentials_get_principal(credentials, mem_ctx);
+
+ /* A NULL here has meaning, as the gssapi server case will
+ * then use the principal from the client */
+ if (!princ_string) {
+ talloc_free(mem_ctx);
+ princ = NULL;
+ return 0;
+ }
+
+ ret = krb5_parse_name(smb_krb5_context->krb5_context,
+ princ_string, princ);
+
+ if (ret == 0) {
+ /* This song-and-dance effectivly puts the principal
+ * into talloc, so we can't loose it. */
+ mem_ctx->smb_krb5_context = talloc_reference(mem_ctx, smb_krb5_context);
+ mem_ctx->principal = *princ;
+ talloc_set_destructor(mem_ctx, free_principal);
+ }
+ return ret;
+}
+
+/**
+ * Return a freshly allocated ccache (destroyed by destructor on child
+ * of parent_ctx), for a given set of client credentials
+ */
+
+ krb5_error_code kinit_to_ccache(TALLOC_CTX *parent_ctx,
+ struct cli_credentials *credentials,
+ struct smb_krb5_context *smb_krb5_context,
+ krb5_ccache ccache)
+{
+ krb5_error_code ret;
+ const char *password;
+ time_t kdc_time = 0;
+ krb5_principal princ;
+ int tries;
+ TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
+
+ if (!mem_ctx) {
+ return ENOMEM;
+ }
+
+ ret = principal_from_credentials(mem_ctx, credentials, smb_krb5_context, &princ);
+ if (ret) {
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ password = cli_credentials_get_password(credentials);
+
+ tries = 2;
+ while (tries--) {
+ if (password) {
+ ret = kerberos_kinit_password_cc(smb_krb5_context->krb5_context, ccache,
+ princ,
+ password, NULL, &kdc_time);
+ } else {
+ /* No password available, try to use a keyblock instead */
+
+ krb5_keyblock keyblock;
+ const struct samr_Password *mach_pwd;
+ mach_pwd = cli_credentials_get_nt_hash(credentials, mem_ctx);
+ if (!mach_pwd) {
+ talloc_free(mem_ctx);
+ DEBUG(1, ("kinit_to_ccache: No password available for kinit\n"));
+ return EINVAL;
+ }
+ ret = krb5_keyblock_init(smb_krb5_context->krb5_context,
+ ETYPE_ARCFOUR_HMAC_MD5,
+ mach_pwd->hash, sizeof(mach_pwd->hash),
+ &keyblock);
+
+ if (ret == 0) {
+ ret = kerberos_kinit_keyblock_cc(smb_krb5_context->krb5_context, ccache,
+ princ,
+ &keyblock, NULL, &kdc_time);
+ krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &keyblock);
+ }
+ }
+
+ if (ret == KRB5KRB_AP_ERR_SKEW || ret == KRB5_KDCREP_SKEW) {
+ /* Perhaps we have been given an invalid skew, so try again without it */
+ time_t t = time(NULL);
+ krb5_set_real_time(smb_krb5_context->krb5_context, t, 0);
+ } else {
+ /* not a skew problem */
+ break;
+ }
+ }
+
+ if (ret == KRB5KRB_AP_ERR_SKEW || ret == KRB5_KDCREP_SKEW) {
+ DEBUG(1,("kinit for %s failed (%s)\n",
+ cli_credentials_get_principal(credentials, mem_ctx),
+ smb_get_krb5_error_message(smb_krb5_context->krb5_context,
+ ret, mem_ctx)));
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ /* cope with ticket being in the future due to clock skew */
+ if ((unsigned)kdc_time > time(NULL)) {
+ time_t t = time(NULL);
+ int time_offset =(unsigned)kdc_time-t;
+ DEBUG(4,("Advancing clock by %d seconds to cope with clock skew\n", time_offset));
+ krb5_set_real_time(smb_krb5_context->krb5_context, t + time_offset + 1, 0);
+ }
+
+ if (ret == KRB5KDC_ERR_PREAUTH_FAILED && cli_credentials_wrong_password(credentials)) {
+ ret = kinit_to_ccache(parent_ctx,
+ credentials,
+ smb_krb5_context,
+ ccache);
+ }
+ if (ret) {
+ DEBUG(1,("kinit for %s failed (%s)\n",
+ cli_credentials_get_principal(credentials, mem_ctx),
+ smb_get_krb5_error_message(smb_krb5_context->krb5_context,
+ ret, mem_ctx)));
+ talloc_free(mem_ctx);
+ return ret;
+ }
+ talloc_free(mem_ctx);
+ return 0;
+}
+
+static krb5_error_code free_keytab(struct keytab_container *ktc)
+{
+ return krb5_kt_close(ktc->smb_krb5_context->krb5_context, ktc->keytab);
+}
+
+krb5_error_code smb_krb5_open_keytab(TALLOC_CTX *mem_ctx,
+ struct smb_krb5_context *smb_krb5_context,
+ const char *keytab_name, struct keytab_container **ktc)
+{
+ krb5_keytab keytab;
+ krb5_error_code ret;
+ ret = krb5_kt_resolve(smb_krb5_context->krb5_context, keytab_name, &keytab);
+ if (ret) {
+ DEBUG(1,("failed to open krb5 keytab: %s\n",
+ smb_get_krb5_error_message(smb_krb5_context->krb5_context,
+ ret, mem_ctx)));
+ return ret;
+ }
+
+ *ktc = talloc(mem_ctx, struct keytab_container);
+ if (!*ktc) {
+ return ENOMEM;
+ }
+
+ (*ktc)->smb_krb5_context = talloc_reference(*ktc, smb_krb5_context);
+ (*ktc)->keytab = keytab;
+ talloc_set_destructor(*ktc, free_keytab);
+
+ return 0;
+}
+
+static krb5_error_code keytab_add_keys(TALLOC_CTX *parent_ctx,
+ const char *princ_string,
+ krb5_principal princ,
+ krb5_principal salt_princ,
+ int kvno,
+ const char *password_s,
+ struct smb_krb5_context *smb_krb5_context,
+ const char **enctype_strings,
+ krb5_keytab keytab)
+{
+ int i;
+ krb5_error_code ret;
+ krb5_data password;
+ TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
+ if (!mem_ctx) {
+ return ENOMEM;
+ }
+
+ password.data = discard_const_p(char *, password_s);
+ password.length = strlen(password_s);
+
+ for (i=0; enctype_strings[i]; i++) {
+ krb5_keytab_entry entry;
+ krb5_enctype enctype;
+ ret = krb5_string_to_enctype(smb_krb5_context->krb5_context, enctype_strings[i], &enctype);
+ if (ret != 0) {
+ DEBUG(1, ("Failed to interpret %s as a krb5 encryption type: %s\n",
+ enctype_strings[i],
+ smb_get_krb5_error_message(smb_krb5_context->krb5_context,
+ ret, mem_ctx)));
+ talloc_free(mem_ctx);
+ return ret;
+ }
+ ret = create_kerberos_key_from_string(smb_krb5_context->krb5_context,
+ salt_princ, &password, &entry.keyblock, enctype);
+ if (ret != 0) {
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ entry.principal = princ;
+ entry.vno = kvno;
+ ret = krb5_kt_add_entry(smb_krb5_context->krb5_context, keytab, &entry);
+ if (ret != 0) {
+ DEBUG(1, ("Failed to add %s entry for %s(kvno %d) to keytab: %s\n",
+ enctype_strings[i],
+ princ_string,
+ kvno,
+ smb_get_krb5_error_message(smb_krb5_context->krb5_context,
+ ret, mem_ctx)));
+ talloc_free(mem_ctx);
+ krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock);
+ return ret;
+ }
+
+ DEBUG(5, ("Added %s(kvno %d) to keytab (%s)\n",
+ princ_string, kvno,
+ enctype_strings[i]));
+
+ krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock);
+ }
+ talloc_free(mem_ctx);
+ return 0;
+}
+
+static krb5_error_code create_keytab(TALLOC_CTX *parent_ctx,
+ struct cli_credentials *machine_account,
+ struct smb_krb5_context *smb_krb5_context,
+ const char **enctype_strings,
+ krb5_keytab keytab,
+ bool add_old)
+{
+ krb5_error_code ret;
+ const char *password_s;
+ const char *old_secret;
+ int kvno;
+ krb5_principal salt_princ;
+ krb5_principal princ;
+ const char *princ_string;
+
+ TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
+ if (!mem_ctx) {
+ return ENOMEM;
+ }
+
+ princ_string = cli_credentials_get_principal(machine_account, mem_ctx);
+ /* Get the principal we will store the new keytab entries under */
+ ret = principal_from_credentials(mem_ctx, machine_account, smb_krb5_context, &princ);
+ if (ret) {
+ DEBUG(1,("create_keytab: makeing krb5 principal failed (%s)\n",
+ smb_get_krb5_error_message(smb_krb5_context->krb5_context,
+ ret, mem_ctx)));
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ /* The salt used to generate these entries may be different however, fetch that */
+ ret = salt_principal_from_credentials(mem_ctx, machine_account,
+ smb_krb5_context,
+ &salt_princ);
+ if (ret) {
+ DEBUG(1,("create_keytab: makeing salt principal failed (%s)\n",
+ smb_get_krb5_error_message(smb_krb5_context->krb5_context,
+ ret, mem_ctx)));
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ /* Finally, do the dance to get the password to put in the entry */
+ password_s = cli_credentials_get_password(machine_account);
+ if (!password_s) {
+ krb5_keytab_entry entry;
+ const struct samr_Password *mach_pwd;
+
+ if (!str_list_check(enctype_strings, "arcfour-hmac-md5")) {
+ DEBUG(1, ("Asked to create keytab, but with only an NT hash supplied, "
+ "but not listing arcfour-hmac-md5 as an enc type to include in the keytab!\n"));
+ talloc_free(mem_ctx);
+ return EINVAL;
+ }
+
+ /* If we don't have the plaintext password, try for
+ * the MD4 password hash */
+ mach_pwd = cli_credentials_get_nt_hash(machine_account, mem_ctx);
+ if (!mach_pwd) {
+ /* OK, nothing to do here */
+ talloc_free(mem_ctx);
+ return 0;
+ }
+ ret = krb5_keyblock_init(smb_krb5_context->krb5_context,
+ ETYPE_ARCFOUR_HMAC_MD5,
+ mach_pwd->hash, sizeof(mach_pwd->hash),
+ &entry.keyblock);
+ if (ret) {
+ DEBUG(1, ("create_keytab: krb5_keyblock_init failed: %s\n",
+ smb_get_krb5_error_message(smb_krb5_context->krb5_context,
+ ret, mem_ctx)));
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ entry.principal = princ;
+ entry.vno = cli_credentials_get_kvno(machine_account);
+ ret = krb5_kt_add_entry(smb_krb5_context->krb5_context, keytab, &entry);
+ if (ret) {
+ DEBUG(1, ("Failed to add ARCFOUR_HMAC (only) entry for %s to keytab: %s",
+ cli_credentials_get_principal(machine_account, mem_ctx),
+ smb_get_krb5_error_message(smb_krb5_context->krb5_context,
+ ret, mem_ctx)));
+ talloc_free(mem_ctx);
+ krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock);
+ return ret;
+ }
+
+ DEBUG(5, ("Added %s(kvno %d) to keytab (arcfour-hmac-md5)\n",
+ cli_credentials_get_principal(machine_account, mem_ctx),
+ cli_credentials_get_kvno(machine_account)));
+
+ krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock);
+
+ /* Can't go any further, we only have this one key */
+ talloc_free(mem_ctx);
+ return 0;
+ }
+
+ kvno = cli_credentials_get_kvno(machine_account);
+ /* good, we actually have the real plaintext */
+ ret = keytab_add_keys(mem_ctx, princ_string, princ, salt_princ,
+ kvno, password_s, smb_krb5_context,
+ enctype_strings, keytab);
+ if (!ret) {
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ if (!add_old || kvno == 0) {
+ talloc_free(mem_ctx);
+ return 0;
+ }
+
+ old_secret = cli_credentials_get_old_password(machine_account);
+ if (!old_secret) {
+ talloc_free(mem_ctx);
+ return 0;
+ }
+
+ ret = keytab_add_keys(mem_ctx, princ_string, princ, salt_princ,
+ kvno - 1, old_secret, smb_krb5_context,
+ enctype_strings, keytab);
+ if (!ret) {
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ talloc_free(mem_ctx);
+ return 0;
+}
+
+
+/*
+ * Walk the keytab, looking for entries of this principal name, with KVNO other than current kvno -1.
+ *
+ * These entries are now stale, we only keep the current, and previous entries around.
+ *
+ * Inspired by the code in Samba3 for 'use kerberos keytab'.
+ *
+ */
+
+static krb5_error_code remove_old_entries(TALLOC_CTX *parent_ctx,
+ struct cli_credentials *machine_account,
+ struct smb_krb5_context *smb_krb5_context,
+ krb5_keytab keytab, bool *found_previous)
+{
+ krb5_error_code ret, ret2;
+ krb5_kt_cursor cursor;
+ krb5_principal princ;
+ int kvno;
+ TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
+ const char *princ_string;
+ if (!mem_ctx) {
+ return ENOMEM;
+ }
+
+ *found_previous = false;
+ princ_string = cli_credentials_get_principal(machine_account, mem_ctx);
+
+ /* Get the principal we will store the new keytab entries under */
+ ret = principal_from_credentials(mem_ctx, machine_account, smb_krb5_context, &princ);
+ if (ret) {
+ DEBUG(1,("update_keytab: makeing krb5 principal failed (%s)\n",
+ smb_get_krb5_error_message(smb_krb5_context->krb5_context,
+ ret, mem_ctx)));
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ kvno = cli_credentials_get_kvno(machine_account);
+
+ /* for each entry in the keytab */
+ ret = krb5_kt_start_seq_get(smb_krb5_context->krb5_context, keytab, &cursor);
+ switch (ret) {
+ case 0:
+ break;
+ case HEIM_ERR_OPNOTSUPP:
+ case ENOENT:
+ case KRB5_KT_END:
+ /* no point enumerating if there isn't anything here */
+ talloc_free(mem_ctx);
+ return 0;
+ default:
+ DEBUG(1,("failed to open keytab for read of old entries: %s\n",
+ smb_get_krb5_error_message(smb_krb5_context->krb5_context,
+ ret, mem_ctx)));
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ while (!ret) {
+ krb5_keytab_entry entry;
+ ret = krb5_kt_next_entry(smb_krb5_context->krb5_context, keytab, &entry, &cursor);
+ if (ret) {
+ break;
+ }
+ /* if it matches our principal */
+ if (!krb5_kt_compare(smb_krb5_context->krb5_context, &entry, princ, 0, 0)) {
+ /* Free the entry, it wasn't the one we were looking for anyway */
+ krb5_kt_free_entry(smb_krb5_context->krb5_context, &entry);
+ continue;
+ }
+
+ /* delete it, if it is not kvno -1 */
+ if (entry.vno != (kvno - 1 )) {
+ /* Release the enumeration. We are going to
+ * have to start this from the top again,
+ * because deletes during enumeration may not
+ * always be consistant.
+ *
+ * Also, the enumeration locks a FILE: keytab
+ */
+
+ krb5_kt_end_seq_get(smb_krb5_context->krb5_context, keytab, &cursor);
+
+ ret = krb5_kt_remove_entry(smb_krb5_context->krb5_context, keytab, &entry);
+ krb5_kt_free_entry(smb_krb5_context->krb5_context, &entry);
+
+ /* Deleted: Restart from the top */
+ ret2 = krb5_kt_start_seq_get(smb_krb5_context->krb5_context, keytab, &cursor);
+ if (ret2) {
+ krb5_kt_free_entry(smb_krb5_context->krb5_context, &entry);
+ DEBUG(1,("failed to restart enumeration of keytab: %s\n",
+ smb_get_krb5_error_message(smb_krb5_context->krb5_context,
+ ret, mem_ctx)));
+
+ talloc_free(mem_ctx);
+ return ret2;
+ }
+
+ if (ret) {
+ break;
+ }
+
+ } else {
+ *found_previous = true;
+ }
+
+ /* Free the entry, we don't need it any more */
+ krb5_kt_free_entry(smb_krb5_context->krb5_context, &entry);
+
+
+ }
+ krb5_kt_end_seq_get(smb_krb5_context->krb5_context, keytab, &cursor);
+
+ switch (ret) {
+ case 0:
+ break;
+ case ENOENT:
+ case KRB5_KT_END:
+ ret = 0;
+ break;
+ default:
+ DEBUG(1,("failed in deleting old entries for principal: %s: %s\n",
+ princ_string,
+ smb_get_krb5_error_message(smb_krb5_context->krb5_context,
+ ret, mem_ctx)));
+ }
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+krb5_error_code smb_krb5_update_keytab(TALLOC_CTX *parent_ctx,
+ struct cli_credentials *machine_account,
+ struct smb_krb5_context *smb_krb5_context,
+ const char **enctype_strings,
+ struct keytab_container *keytab_container)
+{
+ krb5_error_code ret;
+ bool found_previous;
+ TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
+ if (!mem_ctx) {
+ return ENOMEM;
+ }
+
+ ret = remove_old_entries(mem_ctx, machine_account,
+ smb_krb5_context, keytab_container->keytab, &found_previous);
+ if (ret != 0) {
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ /* Create a new keytab. If during the cleanout we found
+ * entires for kvno -1, then don't try and duplicate them.
+ * Otherwise, add kvno, and kvno -1 */
+
+ ret = create_keytab(mem_ctx, machine_account, smb_krb5_context,
+ enctype_strings,
+ keytab_container->keytab,
+ found_previous ? false : true);
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+krb5_error_code smb_krb5_create_memory_keytab(TALLOC_CTX *parent_ctx,
+ struct cli_credentials *machine_account,
+ struct smb_krb5_context *smb_krb5_context,
+ const char **enctype_strings,
+ struct keytab_container **keytab_container)
+{
+ krb5_error_code ret;
+ TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
+ const char *rand_string;
+ const char *keytab_name;
+ if (!mem_ctx) {
+ return ENOMEM;
+ }
+
+ *keytab_container = talloc(mem_ctx, struct keytab_container);
+
+ rand_string = generate_random_str(mem_ctx, 16);
+ if (!rand_string) {
+ talloc_free(mem_ctx);
+ return ENOMEM;
+ }
+
+ keytab_name = talloc_asprintf(mem_ctx, "MEMORY:%s",
+ rand_string);
+ if (!keytab_name) {
+ talloc_free(mem_ctx);
+ return ENOMEM;
+ }
+
+ ret = smb_krb5_open_keytab(mem_ctx, smb_krb5_context, keytab_name, keytab_container);
+ if (ret) {
+ return ret;
+ }
+
+ ret = smb_krb5_update_keytab(mem_ctx, machine_account, smb_krb5_context, enctype_strings, *keytab_container);
+ if (ret == 0) {
+ talloc_steal(parent_ctx, *keytab_container);
+ } else {
+ *keytab_container = NULL;
+ }
+ talloc_free(mem_ctx);
+ return ret;
+}
+
diff --git a/source4/auth/kerberos/krb5_init_context.c b/source4/auth/kerberos/krb5_init_context.c
new file mode 100644
index 0000000000..04f0718a62
--- /dev/null
+++ b/source4/auth/kerberos/krb5_init_context.c
@@ -0,0 +1,477 @@
+/*
+ Unix SMB/CIFS implementation.
+ Wrapper for krb5_init_context
+
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
+ Copyright (C) Andrew Tridgell 2005
+ Copyright (C) Stefan Metzmacher 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/kerberos.h"
+#include <tevent.h>
+#include "auth/kerberos/kerberos.h"
+#include "lib/socket/socket.h"
+#include "lib/stream/packet.h"
+#include "system/network.h"
+#include "param/param.h"
+#include "libcli/resolve/resolve.h"
+
+/*
+ context structure for operations on cldap packets
+*/
+struct smb_krb5_socket {
+ struct socket_context *sock;
+
+ /* the fd event */
+ struct tevent_fd *fde;
+
+ NTSTATUS status;
+ DATA_BLOB request, reply;
+
+ struct packet_context *packet;
+
+ size_t partial_read;
+
+ krb5_krbhst_info *hi;
+};
+
+static krb5_error_code smb_krb5_context_destroy_1(struct smb_krb5_context *ctx)
+{
+ krb5_free_context(ctx->krb5_context);
+ return 0;
+}
+
+static krb5_error_code smb_krb5_context_destroy_2(struct smb_krb5_context *ctx)
+{
+ /* Otherwise krb5_free_context will try and close what we have already free()ed */
+ krb5_set_warn_dest(ctx->krb5_context, NULL);
+ krb5_closelog(ctx->krb5_context, ctx->logf);
+ smb_krb5_context_destroy_1(ctx);
+ return 0;
+}
+
+/* We never close down the DEBUG system, and no need to unreference the use */
+static void smb_krb5_debug_close(void *private_data) {
+ return;
+}
+
+static void smb_krb5_debug_wrapper(const char *timestr, const char *msg, void *private_data)
+{
+ DEBUG(2, ("Kerberos: %s\n", msg));
+}
+
+/*
+ handle recv events on a smb_krb5 socket
+*/
+static void smb_krb5_socket_recv(struct smb_krb5_socket *smb_krb5)
+{
+ TALLOC_CTX *tmp_ctx = talloc_new(smb_krb5);
+ DATA_BLOB blob;
+ size_t nread, dsize;
+
+ smb_krb5->status = socket_pending(smb_krb5->sock, &dsize);
+ if (!NT_STATUS_IS_OK(smb_krb5->status)) {
+ talloc_free(tmp_ctx);
+ return;
+ }
+
+ blob = data_blob_talloc(tmp_ctx, NULL, dsize);
+ if (blob.data == NULL && dsize != 0) {
+ smb_krb5->status = NT_STATUS_NO_MEMORY;
+ talloc_free(tmp_ctx);
+ return;
+ }
+
+ smb_krb5->status = socket_recv(smb_krb5->sock, blob.data, blob.length, &nread);
+ if (!NT_STATUS_IS_OK(smb_krb5->status)) {
+ talloc_free(tmp_ctx);
+ return;
+ }
+ blob.length = nread;
+
+ if (nread == 0) {
+ smb_krb5->status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ talloc_free(tmp_ctx);
+ return;
+ }
+
+ DEBUG(2,("Received smb_krb5 packet of length %d\n",
+ (int)blob.length));
+
+ talloc_steal(smb_krb5, blob.data);
+ smb_krb5->reply = blob;
+ talloc_free(tmp_ctx);
+}
+
+static NTSTATUS smb_krb5_full_packet(void *private_data, DATA_BLOB data)
+{
+ struct smb_krb5_socket *smb_krb5 = talloc_get_type(private_data, struct smb_krb5_socket);
+ talloc_steal(smb_krb5, data.data);
+ smb_krb5->reply = data;
+ smb_krb5->reply.length -= 4;
+ smb_krb5->reply.data += 4;
+ return NT_STATUS_OK;
+}
+
+/*
+ handle request timeouts
+*/
+static void smb_krb5_request_timeout(struct tevent_context *event_ctx,
+ struct tevent_timer *te, struct timeval t,
+ void *private_data)
+{
+ struct smb_krb5_socket *smb_krb5 = talloc_get_type(private_data, struct smb_krb5_socket);
+ DEBUG(5,("Timed out smb_krb5 packet\n"));
+ smb_krb5->status = NT_STATUS_IO_TIMEOUT;
+}
+
+static void smb_krb5_error_handler(void *private_data, NTSTATUS status)
+{
+ struct smb_krb5_socket *smb_krb5 = talloc_get_type(private_data, struct smb_krb5_socket);
+ smb_krb5->status = status;
+}
+
+/*
+ handle send events on a smb_krb5 socket
+*/
+static void smb_krb5_socket_send(struct smb_krb5_socket *smb_krb5)
+{
+ NTSTATUS status;
+
+ size_t len;
+
+ len = smb_krb5->request.length;
+ status = socket_send(smb_krb5->sock, &smb_krb5->request, &len);
+
+ if (!NT_STATUS_IS_OK(status)) return;
+
+ TEVENT_FD_READABLE(smb_krb5->fde);
+
+ TEVENT_FD_NOT_WRITEABLE(smb_krb5->fde);
+ return;
+}
+
+
+/*
+ handle fd events on a smb_krb5_socket
+*/
+static void smb_krb5_socket_handler(struct tevent_context *ev, struct tevent_fd *fde,
+ uint16_t flags, void *private_data)
+{
+ struct smb_krb5_socket *smb_krb5 = talloc_get_type(private_data, struct smb_krb5_socket);
+ switch (smb_krb5->hi->proto) {
+ case KRB5_KRBHST_UDP:
+ if (flags & TEVENT_FD_READ) {
+ smb_krb5_socket_recv(smb_krb5);
+ return;
+ }
+ if (flags & TEVENT_FD_WRITE) {
+ smb_krb5_socket_send(smb_krb5);
+ return;
+ }
+ /* not reached */
+ return;
+ case KRB5_KRBHST_TCP:
+ if (flags & TEVENT_FD_READ) {
+ packet_recv(smb_krb5->packet);
+ return;
+ }
+ if (flags & TEVENT_FD_WRITE) {
+ packet_queue_run(smb_krb5->packet);
+ return;
+ }
+ /* not reached */
+ return;
+ case KRB5_KRBHST_HTTP:
+ /* can't happen */
+ break;
+ }
+}
+
+
+krb5_error_code smb_krb5_send_and_recv_func(krb5_context context,
+ void *data,
+ krb5_krbhst_info *hi,
+ time_t timeout,
+ const krb5_data *send_buf,
+ krb5_data *recv_buf)
+{
+ krb5_error_code ret;
+ NTSTATUS status;
+ struct socket_address *remote_addr;
+ const char *name;
+ struct addrinfo *ai, *a;
+ struct smb_krb5_socket *smb_krb5;
+
+ struct tevent_context *ev = talloc_get_type(data, struct tevent_context);
+
+ DATA_BLOB send_blob = data_blob_const(send_buf->data, send_buf->length);
+
+ ret = krb5_krbhst_get_addrinfo(context, hi, &ai);
+ if (ret) {
+ return ret;
+ }
+
+ for (a = ai; a; a = ai->ai_next) {
+ smb_krb5 = talloc(NULL, struct smb_krb5_socket);
+ if (!smb_krb5) {
+ return ENOMEM;
+ }
+ smb_krb5->hi = hi;
+
+ switch (a->ai_family) {
+ case PF_INET:
+ name = "ipv4";
+ break;
+#ifdef HAVE_IPV6
+ case PF_INET6:
+ name = "ipv6";
+ break;
+#endif
+ default:
+ talloc_free(smb_krb5);
+ return EINVAL;
+ }
+
+ status = NT_STATUS_INVALID_PARAMETER;
+ switch (hi->proto) {
+ case KRB5_KRBHST_UDP:
+ status = socket_create(name, SOCKET_TYPE_DGRAM, &smb_krb5->sock, 0);
+ break;
+ case KRB5_KRBHST_TCP:
+ status = socket_create(name, SOCKET_TYPE_STREAM, &smb_krb5->sock, 0);
+ break;
+ case KRB5_KRBHST_HTTP:
+ talloc_free(smb_krb5);
+ return EINVAL;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(smb_krb5);
+ continue;
+ }
+
+ talloc_steal(smb_krb5, smb_krb5->sock);
+
+ remote_addr = socket_address_from_sockaddr(smb_krb5, a->ai_addr, a->ai_addrlen);
+ if (!remote_addr) {
+ talloc_free(smb_krb5);
+ continue;
+ }
+
+ status = socket_connect_ev(smb_krb5->sock, NULL, remote_addr, 0, ev);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(smb_krb5);
+ continue;
+ }
+ talloc_free(remote_addr);
+
+ /* Setup the FDE, start listening for read events
+ * from the start (otherwise we may miss a socket
+ * drop) and mark as AUTOCLOSE along with the fde */
+
+ /* Ths is equivilant to EVENT_FD_READABLE(smb_krb5->fde) */
+ smb_krb5->fde = tevent_add_fd(ev, smb_krb5->sock,
+ socket_get_fd(smb_krb5->sock),
+ TEVENT_FD_READ,
+ smb_krb5_socket_handler, smb_krb5);
+ /* its now the job of the event layer to close the socket */
+ tevent_fd_set_close_fn(smb_krb5->fde, socket_tevent_fd_close_fn);
+ socket_set_flags(smb_krb5->sock, SOCKET_FLAG_NOCLOSE);
+
+ tevent_add_timer(ev, smb_krb5,
+ timeval_current_ofs(timeout, 0),
+ smb_krb5_request_timeout, smb_krb5);
+
+ smb_krb5->status = NT_STATUS_OK;
+ smb_krb5->reply = data_blob(NULL, 0);
+
+ switch (hi->proto) {
+ case KRB5_KRBHST_UDP:
+ TEVENT_FD_WRITEABLE(smb_krb5->fde);
+ smb_krb5->request = send_blob;
+ break;
+ case KRB5_KRBHST_TCP:
+
+ smb_krb5->packet = packet_init(smb_krb5);
+ if (smb_krb5->packet == NULL) {
+ talloc_free(smb_krb5);
+ return ENOMEM;
+ }
+ packet_set_private(smb_krb5->packet, smb_krb5);
+ packet_set_socket(smb_krb5->packet, smb_krb5->sock);
+ packet_set_callback(smb_krb5->packet, smb_krb5_full_packet);
+ packet_set_full_request(smb_krb5->packet, packet_full_request_u32);
+ packet_set_error_handler(smb_krb5->packet, smb_krb5_error_handler);
+ packet_set_event_context(smb_krb5->packet, ev);
+ packet_set_fde(smb_krb5->packet, smb_krb5->fde);
+
+ smb_krb5->request = data_blob_talloc(smb_krb5, NULL, send_blob.length + 4);
+ RSIVAL(smb_krb5->request.data, 0, send_blob.length);
+ memcpy(smb_krb5->request.data+4, send_blob.data, send_blob.length);
+ packet_send(smb_krb5->packet, smb_krb5->request);
+ break;
+ case KRB5_KRBHST_HTTP:
+ talloc_free(smb_krb5);
+ return EINVAL;
+ }
+ while ((NT_STATUS_IS_OK(smb_krb5->status)) && !smb_krb5->reply.length) {
+ if (tevent_loop_once(ev) != 0) {
+ talloc_free(smb_krb5);
+ return EINVAL;
+ }
+ }
+ if (NT_STATUS_EQUAL(smb_krb5->status, NT_STATUS_IO_TIMEOUT)) {
+ talloc_free(smb_krb5);
+ continue;
+ }
+
+ if (!NT_STATUS_IS_OK(smb_krb5->status)) {
+ DEBUG(2,("Error reading smb_krb5 reply packet: %s\n", nt_errstr(smb_krb5->status)));
+ talloc_free(smb_krb5);
+ continue;
+ }
+
+ ret = krb5_data_copy(recv_buf, smb_krb5->reply.data, smb_krb5->reply.length);
+ if (ret) {
+ talloc_free(smb_krb5);
+ return ret;
+ }
+ talloc_free(smb_krb5);
+
+ break;
+ }
+ if (a) {
+ return 0;
+ }
+ return KRB5_KDC_UNREACH;
+}
+
+krb5_error_code smb_krb5_init_context(void *parent_ctx,
+ struct tevent_context *ev,
+ struct loadparm_context *lp_ctx,
+ struct smb_krb5_context **smb_krb5_context)
+{
+ krb5_error_code ret;
+ TALLOC_CTX *tmp_ctx;
+ char **config_files;
+ const char *config_file;
+
+ initialize_krb5_error_table();
+
+ tmp_ctx = talloc_new(parent_ctx);
+ *smb_krb5_context = talloc(tmp_ctx, struct smb_krb5_context);
+
+ if (!*smb_krb5_context || !tmp_ctx) {
+ talloc_free(tmp_ctx);
+ return ENOMEM;
+ }
+
+ ret = krb5_init_context(&(*smb_krb5_context)->krb5_context);
+ if (ret) {
+ DEBUG(1,("krb5_init_context failed (%s)\n",
+ error_message(ret)));
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ talloc_set_destructor(*smb_krb5_context, smb_krb5_context_destroy_1);
+
+ config_file = config_path(tmp_ctx, lp_ctx, "krb5.conf");
+ if (!config_file) {
+ talloc_free(tmp_ctx);
+ return ENOMEM;
+ }
+
+ /* Use our local krb5.conf file by default */
+ ret = krb5_prepend_config_files_default(config_file == NULL?"":config_file, &config_files);
+ if (ret) {
+ DEBUG(1,("krb5_prepend_config_files_default failed (%s)\n",
+ smb_get_krb5_error_message((*smb_krb5_context)->krb5_context, ret, tmp_ctx)));
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ ret = krb5_set_config_files((*smb_krb5_context)->krb5_context,
+ config_files);
+ krb5_free_config_files(config_files);
+ if (ret) {
+ DEBUG(1,("krb5_set_config_files failed (%s)\n",
+ smb_get_krb5_error_message((*smb_krb5_context)->krb5_context, ret, tmp_ctx)));
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ if (lp_realm(lp_ctx) && *lp_realm(lp_ctx)) {
+ char *upper_realm = strupper_talloc(tmp_ctx, lp_realm(lp_ctx));
+ if (!upper_realm) {
+ DEBUG(1,("gensec_krb5_start: could not uppercase realm: %s\n", lp_realm(lp_ctx)));
+ talloc_free(tmp_ctx);
+ return ENOMEM;
+ }
+ ret = krb5_set_default_realm((*smb_krb5_context)->krb5_context, upper_realm);
+ if (ret) {
+ DEBUG(1,("krb5_set_default_realm failed (%s)\n",
+ smb_get_krb5_error_message((*smb_krb5_context)->krb5_context, ret, tmp_ctx)));
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+ }
+
+ /* TODO: Should we have a different name here? */
+ ret = krb5_initlog((*smb_krb5_context)->krb5_context, "Samba", &(*smb_krb5_context)->logf);
+
+ if (ret) {
+ DEBUG(1,("krb5_initlog failed (%s)\n",
+ smb_get_krb5_error_message((*smb_krb5_context)->krb5_context, ret, tmp_ctx)));
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ talloc_set_destructor(*smb_krb5_context, smb_krb5_context_destroy_2);
+
+ ret = krb5_addlog_func((*smb_krb5_context)->krb5_context, (*smb_krb5_context)->logf, 0 /* min */, -1 /* max */,
+ smb_krb5_debug_wrapper, smb_krb5_debug_close, NULL);
+ if (ret) {
+ DEBUG(1,("krb5_addlog_func failed (%s)\n",
+ smb_get_krb5_error_message((*smb_krb5_context)->krb5_context, ret, tmp_ctx)));
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+ krb5_set_warn_dest((*smb_krb5_context)->krb5_context, (*smb_krb5_context)->logf);
+
+ /* Set use of our socket lib */
+ ret = krb5_set_send_to_kdc_func((*smb_krb5_context)->krb5_context,
+ smb_krb5_send_and_recv_func,
+ ev);
+ if (ret) {
+ DEBUG(1,("krb5_set_send_recv_func failed (%s)\n",
+ smb_get_krb5_error_message((*smb_krb5_context)->krb5_context, ret, tmp_ctx)));
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ talloc_steal(parent_ctx, *smb_krb5_context);
+ talloc_free(tmp_ctx);
+
+ /* Set options in kerberos */
+
+ krb5_set_dns_canonicalize_hostname((*smb_krb5_context)->krb5_context,
+ lp_parm_bool(lp_ctx, NULL, "krb5", "set_dns_canonicalize", false));
+
+ return 0;
+}
+
diff --git a/source4/auth/kerberos/krb5_init_context.h b/source4/auth/kerberos/krb5_init_context.h
new file mode 100644
index 0000000000..3f1425ea92
--- /dev/null
+++ b/source4/auth/kerberos/krb5_init_context.h
@@ -0,0 +1,37 @@
+/*
+ Unix SMB/CIFS implementation.
+ simple kerberos5 routines for active directory
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+struct smb_krb5_context {
+ krb5_context krb5_context;
+ krb5_log_facility *logf;
+};
+
+struct tevent_context;
+struct loadparm_context;
+krb5_error_code smb_krb5_init_context(void *parent_ctx, struct tevent_context *ev,
+ struct loadparm_context *lp_ctx,
+ struct smb_krb5_context **smb_krb5_context);
+void smb_krb5_free_context(struct smb_krb5_context *smb_krb5_context);
+
+krb5_error_code smb_krb5_send_and_recv_func(krb5_context context,
+ void *data,
+ krb5_krbhst_info *hi,
+ time_t timeout,
+ const krb5_data *send_buf,
+ krb5_data *recv_buf);
diff --git a/source4/auth/ntlm/auth.c b/source4/auth/ntlm/auth.c
new file mode 100644
index 0000000000..5520c9d01f
--- /dev/null
+++ b/source4/auth/ntlm/auth.c
@@ -0,0 +1,576 @@
+/*
+ Unix SMB/CIFS implementation.
+ Password and authentication handling
+ Copyright (C) Andrew Bartlett 2001-2002
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "../lib/util/dlinklist.h"
+#include "auth/auth.h"
+#include "auth/ntlm/auth_proto.h"
+#include "lib/events/events.h"
+#include "param/param.h"
+
+/***************************************************************************
+ Set a fixed challenge
+***************************************************************************/
+_PUBLIC_ NTSTATUS auth_context_set_challenge(struct auth_context *auth_ctx, const uint8_t chal[8], const char *set_by)
+{
+ auth_ctx->challenge.set_by = talloc_strdup(auth_ctx, set_by);
+ NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.set_by);
+
+ auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8);
+ NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
+ Set a fixed challenge
+***************************************************************************/
+bool auth_challenge_may_be_modified(struct auth_context *auth_ctx)
+{
+ return auth_ctx->challenge.may_be_modified;
+}
+
+/****************************************************************************
+ Try to get a challenge out of the various authentication modules.
+ Returns a const char of length 8 bytes.
+****************************************************************************/
+_PUBLIC_ NTSTATUS auth_get_challenge(struct auth_context *auth_ctx, const uint8_t **_chal)
+{
+ NTSTATUS nt_status;
+ struct auth_method_context *method;
+
+ if (auth_ctx->challenge.data.length) {
+ DEBUG(5, ("auth_get_challenge: returning previous challenge by module %s (normal)\n",
+ auth_ctx->challenge.set_by));
+ *_chal = auth_ctx->challenge.data.data;
+ return NT_STATUS_OK;
+ }
+
+ for (method = auth_ctx->methods; method; method = method->next) {
+ DATA_BLOB challenge = data_blob(NULL,0);
+
+ nt_status = method->ops->get_challenge(method, auth_ctx, &challenge);
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NOT_IMPLEMENTED)) {
+ continue;
+ }
+
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+
+ if (challenge.length != 8) {
+ DEBUG(0, ("auth_get_challenge: invalid challenge (length %u) by mothod [%s]\n",
+ (unsigned)challenge.length, method->ops->name));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ auth_ctx->challenge.data = challenge;
+ auth_ctx->challenge.set_by = method->ops->name;
+
+ break;
+ }
+
+ if (!auth_ctx->challenge.set_by) {
+ uint8_t chal[8];
+ generate_random_buffer(chal, 8);
+
+ auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8);
+ NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
+ auth_ctx->challenge.set_by = "random";
+
+ auth_ctx->challenge.may_be_modified = true;
+ }
+
+ DEBUG(10,("auth_get_challenge: challenge set by %s\n",
+ auth_ctx->challenge.set_by));
+
+ *_chal = auth_ctx->challenge.data.data;
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Try to get a challenge out of the various authentication modules.
+ Returns a const char of length 8 bytes.
+****************************************************************************/
+_PUBLIC_ NTSTATUS auth_get_server_info_principal(TALLOC_CTX *mem_ctx,
+ struct auth_context *auth_ctx,
+ const char *principal,
+ struct auth_serversupplied_info **server_info)
+{
+ NTSTATUS nt_status;
+ struct auth_method_context *method;
+
+ for (method = auth_ctx->methods; method; method = method->next) {
+ if (!method->ops->get_server_info_principal) {
+ continue;
+ }
+
+ nt_status = method->ops->get_server_info_principal(mem_ctx, auth_ctx, principal, server_info);
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NOT_IMPLEMENTED)) {
+ continue;
+ }
+
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+
+ break;
+ }
+
+ return NT_STATUS_OK;
+}
+
+struct auth_check_password_sync_state {
+ bool finished;
+ NTSTATUS status;
+ struct auth_serversupplied_info *server_info;
+};
+
+static void auth_check_password_sync_callback(struct auth_check_password_request *req,
+ void *private_data)
+{
+ struct auth_check_password_sync_state *s = talloc_get_type(private_data,
+ struct auth_check_password_sync_state);
+
+ s->finished = true;
+ s->status = auth_check_password_recv(req, s, &s->server_info);
+}
+
+/**
+ * Check a user's Plaintext, LM or NTLM password.
+ * (sync version)
+ *
+ * Check a user's password, as given in the user_info struct and return various
+ * interesting details in the server_info struct.
+ *
+ * The return value takes precedence over the contents of the server_info
+ * struct. When the return is other than NT_STATUS_OK the contents
+ * of that structure is undefined.
+ *
+ * @param auth_ctx Supplies the challenges and some other data.
+ * Must be created with auth_context_create(), and the challenges should be
+ * filled in, either at creation or by calling the challenge geneation
+ * function auth_get_challenge().
+ *
+ * @param user_info Contains the user supplied components, including the passwords.
+ *
+ * @param mem_ctx The parent memory context for the server_info structure
+ *
+ * @param server_info If successful, contains information about the authentication,
+ * including a SAM_ACCOUNT struct describing the user.
+ *
+ * @return An NTSTATUS with NT_STATUS_OK or an appropriate error.
+ *
+ **/
+
+_PUBLIC_ NTSTATUS auth_check_password(struct auth_context *auth_ctx,
+ TALLOC_CTX *mem_ctx,
+ const struct auth_usersupplied_info *user_info,
+ struct auth_serversupplied_info **server_info)
+{
+ struct auth_check_password_sync_state *sync_state;
+ NTSTATUS status;
+
+ sync_state = talloc_zero(auth_ctx, struct auth_check_password_sync_state);
+ NT_STATUS_HAVE_NO_MEMORY(sync_state);
+
+ auth_check_password_send(auth_ctx, user_info, auth_check_password_sync_callback, sync_state);
+
+ while (!sync_state->finished) {
+ event_loop_once(auth_ctx->event_ctx);
+ }
+
+ status = sync_state->status;
+
+ if (NT_STATUS_IS_OK(status)) {
+ *server_info = talloc_steal(mem_ctx, sync_state->server_info);
+ }
+
+ talloc_free(sync_state);
+ return status;
+}
+
+struct auth_check_password_request {
+ struct auth_context *auth_ctx;
+ const struct auth_usersupplied_info *user_info;
+ struct auth_serversupplied_info *server_info;
+ struct auth_method_context *method;
+ NTSTATUS status;
+ struct {
+ void (*fn)(struct auth_check_password_request *req, void *private_data);
+ void *private_data;
+ } callback;
+};
+
+static void auth_check_password_async_timed_handler(struct tevent_context *ev, struct tevent_timer *te,
+ struct timeval t, void *ptr)
+{
+ struct auth_check_password_request *req = talloc_get_type(ptr, struct auth_check_password_request);
+ req->status = req->method->ops->check_password(req->method, req, req->user_info, &req->server_info);
+ req->callback.fn(req, req->callback.private_data);
+}
+
+/**
+ * Check a user's Plaintext, LM or NTLM password.
+ * async send hook
+ *
+ * Check a user's password, as given in the user_info struct and return various
+ * interesting details in the server_info struct.
+ *
+ * The return value takes precedence over the contents of the server_info
+ * struct. When the return is other than NT_STATUS_OK the contents
+ * of that structure is undefined.
+ *
+ * @param auth_ctx Supplies the challenges and some other data.
+ * Must be created with make_auth_context(), and the challenges should be
+ * filled in, either at creation or by calling the challenge geneation
+ * function auth_get_challenge().
+ *
+ * @param user_info Contains the user supplied components, including the passwords.
+ *
+ * @param callback A callback function which will be called when the operation is finished.
+ * The callback function needs to call auth_check_password_recv() to get the return values
+ *
+ * @param private_data A private pointer which will ba passed to the callback function
+ *
+ **/
+
+_PUBLIC_ void auth_check_password_send(struct auth_context *auth_ctx,
+ const struct auth_usersupplied_info *user_info,
+ void (*callback)(struct auth_check_password_request *req, void *private_data),
+ void *private_data)
+{
+ /* if all the modules say 'not for me' this is reasonable */
+ NTSTATUS nt_status;
+ struct auth_method_context *method;
+ const uint8_t *challenge;
+ struct auth_usersupplied_info *user_info_tmp;
+ struct auth_check_password_request *req = NULL;
+
+ DEBUG(3, ("auth_check_password_send: Checking password for unmapped user [%s]\\[%s]@[%s]\n",
+ user_info->client.domain_name, user_info->client.account_name, user_info->workstation_name));
+
+ req = talloc_zero(auth_ctx, struct auth_check_password_request);
+ if (!req) {
+ callback(NULL, private_data);
+ return;
+ }
+ req->auth_ctx = auth_ctx;
+ req->user_info = user_info;
+ req->callback.fn = callback;
+ req->callback.private_data = private_data;
+
+ if (!user_info->mapped_state) {
+ nt_status = map_user_info(req, lp_workgroup(auth_ctx->lp_ctx), user_info, &user_info_tmp);
+ if (!NT_STATUS_IS_OK(nt_status)) goto failed;
+ user_info = user_info_tmp;
+ req->user_info = user_info_tmp;
+ }
+
+ DEBUGADD(3,("auth_check_password_send: mapped user is: [%s]\\[%s]@[%s]\n",
+ user_info->mapped.domain_name, user_info->mapped.account_name, user_info->workstation_name));
+
+ nt_status = auth_get_challenge(auth_ctx, &challenge);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("auth_check_password_send: Invalid challenge (length %u) stored for this auth context set_by %s - cannot continue: %s\n",
+ (unsigned)auth_ctx->challenge.data.length, auth_ctx->challenge.set_by, nt_errstr(nt_status)));
+ goto failed;
+ }
+
+ if (auth_ctx->challenge.set_by) {
+ DEBUG(10, ("auth_check_password_send: auth_context challenge created by %s\n",
+ auth_ctx->challenge.set_by));
+ }
+
+ DEBUG(10, ("auth_check_password_send: challenge is: \n"));
+ dump_data(5, auth_ctx->challenge.data.data, auth_ctx->challenge.data.length);
+
+ nt_status = NT_STATUS_NO_SUCH_USER; /* If all the modules say 'not for me', then this is reasonable */
+ for (method = auth_ctx->methods; method; method = method->next) {
+ NTSTATUS result;
+ struct tevent_timer *te = NULL;
+
+ /* check if the module wants to chek the password */
+ result = method->ops->want_check(method, req, user_info);
+ if (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) {
+ DEBUG(11,("auth_check_password_send: %s had nothing to say\n", method->ops->name));
+ continue;
+ }
+
+ nt_status = result;
+ req->method = method;
+
+ if (!NT_STATUS_IS_OK(nt_status)) break;
+
+ te = event_add_timed(auth_ctx->event_ctx, req,
+ timeval_zero(),
+ auth_check_password_async_timed_handler, req);
+ if (!te) {
+ nt_status = NT_STATUS_NO_MEMORY;
+ goto failed;
+ }
+ return;
+ }
+
+failed:
+ req->status = nt_status;
+ req->callback.fn(req, req->callback.private_data);
+}
+
+/**
+ * Check a user's Plaintext, LM or NTLM password.
+ * async receive function
+ *
+ * The return value takes precedence over the contents of the server_info
+ * struct. When the return is other than NT_STATUS_OK the contents
+ * of that structure is undefined.
+ *
+ *
+ * @param req The async auth_check_password state, passes to the callers callback function
+ *
+ * @param mem_ctx The parent memory context for the server_info structure
+ *
+ * @param server_info If successful, contains information about the authentication,
+ * including a SAM_ACCOUNT struct describing the user.
+ *
+ * @return An NTSTATUS with NT_STATUS_OK or an appropriate error.
+ *
+ **/
+
+_PUBLIC_ NTSTATUS auth_check_password_recv(struct auth_check_password_request *req,
+ TALLOC_CTX *mem_ctx,
+ struct auth_serversupplied_info **server_info)
+{
+ NTSTATUS status;
+
+ NT_STATUS_HAVE_NO_MEMORY(req);
+
+ if (NT_STATUS_IS_OK(req->status)) {
+ DEBUG(5,("auth_check_password_recv: %s authentication for user [%s\\%s] succeeded\n",
+ req->method->ops->name, req->server_info->domain_name, req->server_info->account_name));
+
+ *server_info = talloc_steal(mem_ctx, req->server_info);
+ } else {
+ DEBUG(2,("auth_check_password_recv: %s authentication for user [%s\\%s] FAILED with error %s\n",
+ (req->method ? req->method->ops->name : "NO_METHOD"),
+ req->user_info->mapped.domain_name,
+ req->user_info->mapped.account_name,
+ nt_errstr(req->status)));
+ }
+
+ status = req->status;
+ talloc_free(req);
+ return status;
+}
+
+/***************************************************************************
+ Make a auth_info struct for the auth subsystem
+ - Allow the caller to specify the methods to use
+***************************************************************************/
+_PUBLIC_ NTSTATUS auth_context_create_methods(TALLOC_CTX *mem_ctx, const char **methods,
+ struct tevent_context *ev,
+ struct messaging_context *msg,
+ struct loadparm_context *lp_ctx,
+ struct auth_context **auth_ctx)
+{
+ int i;
+ struct auth_context *ctx;
+
+ auth_init();
+
+ if (!methods) {
+ DEBUG(0,("auth_context_create: No auth method list!?\n"));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ if (!ev) {
+ DEBUG(0,("auth_context_create: called with out event context\n"));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ if (!msg) {
+ DEBUG(0,("auth_context_create: called with out messaging context\n"));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ ctx = talloc(mem_ctx, struct auth_context);
+ NT_STATUS_HAVE_NO_MEMORY(ctx);
+ ctx->challenge.set_by = NULL;
+ ctx->challenge.may_be_modified = false;
+ ctx->challenge.data = data_blob(NULL, 0);
+ ctx->methods = NULL;
+ ctx->event_ctx = ev;
+ ctx->msg_ctx = msg;
+ ctx->lp_ctx = lp_ctx;
+
+ for (i=0; methods[i] ; i++) {
+ struct auth_method_context *method;
+
+ method = talloc(ctx, struct auth_method_context);
+ NT_STATUS_HAVE_NO_MEMORY(method);
+
+ method->ops = auth_backend_byname(methods[i]);
+ if (!method->ops) {
+ DEBUG(1,("auth_context_create: failed to find method=%s\n",
+ methods[i]));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ method->auth_ctx = ctx;
+ method->depth = i;
+ DLIST_ADD_END(ctx->methods, method, struct auth_method_context *);
+ }
+
+ if (!ctx->methods) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ ctx->check_password = auth_check_password;
+ ctx->get_challenge = auth_get_challenge;
+ ctx->set_challenge = auth_context_set_challenge;
+ ctx->challenge_may_be_modified = auth_challenge_may_be_modified;
+ ctx->get_server_info_principal = auth_get_server_info_principal;
+
+ *auth_ctx = ctx;
+
+ return NT_STATUS_OK;
+}
+/***************************************************************************
+ Make a auth_info struct for the auth subsystem
+ - Uses default auth_methods, depending on server role and smb.conf settings
+***************************************************************************/
+_PUBLIC_ NTSTATUS auth_context_create(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct messaging_context *msg,
+ struct loadparm_context *lp_ctx,
+ struct auth_context **auth_ctx)
+{
+ const char **auth_methods = NULL;
+ switch (lp_server_role(lp_ctx)) {
+ case ROLE_STANDALONE:
+ auth_methods = lp_parm_string_list(mem_ctx, lp_ctx, NULL, "auth methods", "standalone", NULL);
+ break;
+ case ROLE_DOMAIN_MEMBER:
+ auth_methods = lp_parm_string_list(mem_ctx, lp_ctx, NULL, "auth methods", "member server", NULL);
+ break;
+ case ROLE_DOMAIN_CONTROLLER:
+ auth_methods = lp_parm_string_list(mem_ctx, lp_ctx, NULL, "auth methods", "domain controller", NULL);
+ break;
+ }
+ return auth_context_create_methods(mem_ctx, auth_methods, ev, msg, lp_ctx, auth_ctx);
+}
+
+
+/* the list of currently registered AUTH backends */
+static struct auth_backend {
+ const struct auth_operations *ops;
+} *backends = NULL;
+static int num_backends;
+
+/*
+ register a AUTH backend.
+
+ The 'name' can be later used by other backends to find the operations
+ structure for this backend.
+*/
+_PUBLIC_ NTSTATUS auth_register(const struct auth_operations *ops)
+{
+ struct auth_operations *new_ops;
+
+ if (auth_backend_byname(ops->name) != NULL) {
+ /* its already registered! */
+ DEBUG(0,("AUTH backend '%s' already registered\n",
+ ops->name));
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ backends = talloc_realloc(talloc_autofree_context(), backends,
+ struct auth_backend, num_backends+1);
+ NT_STATUS_HAVE_NO_MEMORY(backends);
+
+ new_ops = (struct auth_operations *)talloc_memdup(backends, ops, sizeof(*ops));
+ NT_STATUS_HAVE_NO_MEMORY(new_ops);
+ new_ops->name = talloc_strdup(new_ops, ops->name);
+ NT_STATUS_HAVE_NO_MEMORY(new_ops->name);
+
+ backends[num_backends].ops = new_ops;
+
+ num_backends++;
+
+ DEBUG(3,("AUTH backend '%s' registered\n",
+ ops->name));
+
+ return NT_STATUS_OK;
+}
+
+/*
+ return the operations structure for a named backend of the specified type
+*/
+const struct auth_operations *auth_backend_byname(const char *name)
+{
+ int i;
+
+ for (i=0;i<num_backends;i++) {
+ if (strcmp(backends[i].ops->name, name) == 0) {
+ return backends[i].ops;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ return the AUTH interface version, and the size of some critical types
+ This can be used by backends to either detect compilation errors, or provide
+ multiple implementations for different smbd compilation options in one module
+*/
+const struct auth_critical_sizes *auth_interface_version(void)
+{
+ static const struct auth_critical_sizes critical_sizes = {
+ AUTH_INTERFACE_VERSION,
+ sizeof(struct auth_operations),
+ sizeof(struct auth_method_context),
+ sizeof(struct auth_context),
+ sizeof(struct auth_usersupplied_info),
+ sizeof(struct auth_serversupplied_info)
+ };
+
+ return &critical_sizes;
+}
+
+_PUBLIC_ NTSTATUS auth_init(void)
+{
+ static bool initialized = false;
+ extern NTSTATUS auth_developer_init(void);
+ extern NTSTATUS auth_winbind_init(void);
+ extern NTSTATUS auth_anonymous_init(void);
+ extern NTSTATUS auth_unix_init(void);
+ extern NTSTATUS auth_sam_init(void);
+ extern NTSTATUS auth_server_init(void);
+
+ init_module_fn static_init[] = { STATIC_auth_MODULES };
+
+ if (initialized) return NT_STATUS_OK;
+ initialized = true;
+
+ run_init_functions(static_init);
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS server_service_auth_init(void)
+{
+ return auth_init();
+}
diff --git a/source4/auth/ntlm/auth_anonymous.c b/source4/auth/ntlm/auth_anonymous.c
new file mode 100644
index 0000000000..c889071878
--- /dev/null
+++ b/source4/auth/ntlm/auth_anonymous.c
@@ -0,0 +1,78 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Anonymous Authentification
+
+ Copyright (C) Stefan Metzmacher 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "auth/auth.h"
+#include "auth/ntlm/auth_proto.h"
+#include "param/param.h"
+
+/**
+ * Return a anonymous logon for anonymous users (username = "")
+ *
+ * Typically used as the first module in the auth chain, this allows
+ * anonymou logons to be dealt with in one place. Non-anonymou logons 'fail'
+ * and pass onto the next module.
+ **/
+static NTSTATUS anonymous_want_check(struct auth_method_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ const struct auth_usersupplied_info *user_info)
+{
+ if (user_info->client.account_name && *user_info->client.account_name) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/**
+ * Return a anonymous logon for anonymous users (username = "")
+ *
+ * Typically used as the first module in the auth chain, this allows
+ * anonymou logons to be dealt with in one place. Non-anonymou logons 'fail'
+ * and pass onto the next module.
+ **/
+static NTSTATUS anonymous_check_password(struct auth_method_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ const struct auth_usersupplied_info *user_info,
+ struct auth_serversupplied_info **_server_info)
+{
+ return auth_anonymous_server_info(mem_ctx, lp_netbios_name(ctx->auth_ctx->lp_ctx), _server_info);
+}
+
+static const struct auth_operations anonymous_auth_ops = {
+ .name = "anonymous",
+ .get_challenge = auth_get_challenge_not_implemented,
+ .want_check = anonymous_want_check,
+ .check_password = anonymous_check_password
+};
+
+_PUBLIC_ NTSTATUS auth_anonymous_init(void)
+{
+ NTSTATUS ret;
+
+ ret = auth_register(&anonymous_auth_ops);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("Failed to register 'anonymous' auth backend!\n"));
+ return ret;
+ }
+
+ return ret;
+}
diff --git a/source4/auth/ntlm/auth_developer.c b/source4/auth/ntlm/auth_developer.c
new file mode 100644
index 0000000000..3b8c83c349
--- /dev/null
+++ b/source4/auth/ntlm/auth_developer.c
@@ -0,0 +1,207 @@
+/*
+ Unix SMB/CIFS implementation.
+ Generic authentication types
+ Copyright (C) Andrew Bartlett 2001-2002
+ Copyright (C) Jelmer Vernooij 2002
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "auth/auth.h"
+#include "auth/ntlm/auth_proto.h"
+#include "libcli/security/security.h"
+#include "librpc/gen_ndr/ndr_samr.h"
+
+static NTSTATUS name_to_ntstatus_want_check(struct auth_method_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ const struct auth_usersupplied_info *user_info)
+{
+ return NT_STATUS_OK;
+}
+
+/**
+ * Return an error based on username
+ *
+ * This function allows the testing of obsure errors, as well as the generation
+ * of NT_STATUS -> DOS error mapping tables.
+ *
+ * This module is of no value to end-users.
+ *
+ * The password is ignored.
+ *
+ * @return An NTSTATUS value based on the username
+ **/
+
+static NTSTATUS name_to_ntstatus_check_password(struct auth_method_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ const struct auth_usersupplied_info *user_info,
+ struct auth_serversupplied_info **_server_info)
+{
+ NTSTATUS nt_status;
+ struct auth_serversupplied_info *server_info;
+ uint32_t error_num;
+ const char *user;
+
+ user = user_info->client.account_name;
+
+ if (strncasecmp("NT_STATUS", user, strlen("NT_STATUS")) == 0) {
+ nt_status = nt_status_string_to_code(user);
+ } else {
+ error_num = strtoul(user, NULL, 16);
+ DEBUG(5,("name_to_ntstatus_check_password: Error for user %s was 0x%08X\n", user, error_num));
+ nt_status = NT_STATUS(error_num);
+ }
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+
+ server_info = talloc(mem_ctx, struct auth_serversupplied_info);
+ NT_STATUS_HAVE_NO_MEMORY(server_info);
+
+ server_info->account_sid = dom_sid_parse_talloc(server_info, SID_NT_ANONYMOUS);
+ NT_STATUS_HAVE_NO_MEMORY(server_info->account_sid);
+
+ /* is this correct? */
+ server_info->primary_group_sid = dom_sid_parse_talloc(server_info, SID_BUILTIN_GUESTS);
+ NT_STATUS_HAVE_NO_MEMORY(server_info->primary_group_sid);
+
+ server_info->n_domain_groups = 0;
+ server_info->domain_groups = NULL;
+
+ /* annoying, but the Anonymous really does have a session key,
+ and it is all zeros! */
+ server_info->user_session_key = data_blob_talloc(server_info, NULL, 16);
+ NT_STATUS_HAVE_NO_MEMORY(server_info->user_session_key.data);
+
+ server_info->lm_session_key = data_blob_talloc(server_info, NULL, 16);
+ NT_STATUS_HAVE_NO_MEMORY(server_info->lm_session_key.data);
+
+ data_blob_clear(&server_info->user_session_key);
+ data_blob_clear(&server_info->lm_session_key);
+
+ server_info->account_name = talloc_asprintf(server_info, "NAME TO NTSTATUS %s ANONYMOUS LOGON", user);
+ NT_STATUS_HAVE_NO_MEMORY(server_info->account_name);
+
+ server_info->domain_name = talloc_strdup(server_info, "NT AUTHORITY");
+ NT_STATUS_HAVE_NO_MEMORY(server_info->domain_name);
+
+ server_info->full_name = talloc_asprintf(server_info, "NAME TO NTSTATUS %s Anonymous Logon", user);
+ NT_STATUS_HAVE_NO_MEMORY(server_info->full_name);
+
+ server_info->logon_script = talloc_strdup(server_info, "");
+ NT_STATUS_HAVE_NO_MEMORY(server_info->logon_script);
+
+ server_info->profile_path = talloc_strdup(server_info, "");
+ NT_STATUS_HAVE_NO_MEMORY(server_info->profile_path);
+
+ server_info->home_directory = talloc_strdup(server_info, "");
+ NT_STATUS_HAVE_NO_MEMORY(server_info->home_directory);
+
+ server_info->home_drive = talloc_strdup(server_info, "");
+ NT_STATUS_HAVE_NO_MEMORY(server_info->home_drive);
+
+ server_info->last_logon = 0;
+ server_info->last_logoff = 0;
+ server_info->acct_expiry = 0;
+ server_info->last_password_change = 0;
+ server_info->allow_password_change = 0;
+ server_info->force_password_change = 0;
+
+ server_info->logon_count = 0;
+ server_info->bad_password_count = 0;
+
+ server_info->acct_flags = ACB_NORMAL;
+
+ server_info->authenticated = false;
+
+ *_server_info = server_info;
+
+ return nt_status;
+}
+
+static const struct auth_operations name_to_ntstatus_auth_ops = {
+ .name = "name_to_ntstatus",
+ .get_challenge = auth_get_challenge_not_implemented,
+ .want_check = name_to_ntstatus_want_check,
+ .check_password = name_to_ntstatus_check_password
+};
+
+/**
+ * Return a 'fixed' challenge instead of a variable one.
+ *
+ * The idea of this function is to make packet snifs consistant
+ * with a fixed challenge, so as to aid debugging.
+ *
+ * This module is of no value to end-users.
+ *
+ * This module does not actually authenticate the user, but
+ * just pretenteds to need a specified challenge.
+ * This module removes *all* security from the challenge-response system
+ *
+ * @return NT_STATUS_UNSUCCESSFUL
+ **/
+static NTSTATUS fixed_challenge_get_challenge(struct auth_method_context *ctx, TALLOC_CTX *mem_ctx, DATA_BLOB *_blob)
+{
+ DATA_BLOB blob;
+ const char *challenge = "I am a teapot";
+
+ blob = data_blob_talloc(mem_ctx, challenge, 8);
+ NT_STATUS_HAVE_NO_MEMORY(blob.data);
+
+ *_blob = blob;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS fixed_challenge_want_check(struct auth_method_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ const struct auth_usersupplied_info *user_info)
+{
+ /* don't handle any users */
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS fixed_challenge_check_password(struct auth_method_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ const struct auth_usersupplied_info *user_info,
+ struct auth_serversupplied_info **_server_info)
+{
+ /* don't handle any users */
+ return NT_STATUS_NO_SUCH_USER;
+}
+
+static const struct auth_operations fixed_challenge_auth_ops = {
+ .name = "fixed_challenge",
+ .get_challenge = fixed_challenge_get_challenge,
+ .want_check = fixed_challenge_want_check,
+ .check_password = fixed_challenge_check_password
+};
+
+_PUBLIC_ NTSTATUS auth_developer_init(void)
+{
+ NTSTATUS ret;
+
+ ret = auth_register(&name_to_ntstatus_auth_ops);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("Failed to register 'name_to_ntstatus' auth backend!\n"));
+ return ret;
+ }
+
+ ret = auth_register(&fixed_challenge_auth_ops);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("Failed to register 'fixed_challenge' auth backend!\n"));
+ return ret;
+ }
+
+ return ret;
+}
diff --git a/source4/auth/ntlm/auth_proto.h b/source4/auth/ntlm/auth_proto.h
new file mode 100644
index 0000000000..572c1a4ca7
--- /dev/null
+++ b/source4/auth/ntlm/auth_proto.h
@@ -0,0 +1,50 @@
+#ifndef __AUTH_NTLM_AUTH_PROTO_H__
+#define __AUTH_NTLM_AUTH_PROTO_H__
+
+#undef _PRINTF_ATTRIBUTE
+#define _PRINTF_ATTRIBUTE(a1, a2) PRINTF_ATTRIBUTE(a1, a2)
+/* This file was automatically generated by mkproto.pl. DO NOT EDIT */
+
+/* this file contains prototypes for functions that are private
+ * to this subsystem or library. These functions should not be
+ * used outside this particular subsystem! */
+
+
+/* The following definitions come from auth/ntlm/auth.c */
+
+
+/***************************************************************************
+ Set a fixed challenge
+***************************************************************************/
+bool auth_challenge_may_be_modified(struct auth_context *auth_ctx) ;
+const struct auth_operations *auth_backend_byname(const char *name);
+const struct auth_critical_sizes *auth_interface_version(void);
+NTSTATUS server_service_auth_init(void);
+
+/* The following definitions come from auth/ntlm/auth_util.c */
+
+NTSTATUS auth_get_challenge_not_implemented(struct auth_method_context *ctx, TALLOC_CTX *mem_ctx, DATA_BLOB *challenge);
+
+/****************************************************************************
+ Create an auth_usersupplied_data structure after appropriate mapping.
+****************************************************************************/
+NTSTATUS map_user_info(TALLOC_CTX *mem_ctx,
+ const char *default_domain,
+ const struct auth_usersupplied_info *user_info,
+ struct auth_usersupplied_info **user_info_mapped);
+
+/****************************************************************************
+ Create an auth_usersupplied_data structure after appropriate mapping.
+****************************************************************************/
+NTSTATUS encrypt_user_info(TALLOC_CTX *mem_ctx, struct auth_context *auth_context,
+ enum auth_password_state to_state,
+ const struct auth_usersupplied_info *user_info_in,
+ const struct auth_usersupplied_info **user_info_encrypted);
+
+/* The following definitions come from auth/ntlm/auth_simple.c */
+
+#undef _PRINTF_ATTRIBUTE
+#define _PRINTF_ATTRIBUTE(a1, a2)
+
+#endif /* __AUTH_NTLM_AUTH_PROTO_H__ */
+
diff --git a/source4/auth/ntlm/auth_sam.c b/source4/auth/ntlm/auth_sam.c
new file mode 100644
index 0000000000..96a13d5ed9
--- /dev/null
+++ b/source4/auth/ntlm/auth_sam.c
@@ -0,0 +1,500 @@
+/*
+ Unix SMB/CIFS implementation.
+ Password and authentication handling
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-2009
+ Copyright (C) Gerald Carter 2003
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "librpc/gen_ndr/ndr_netlogon.h"
+#include "system/time.h"
+#include "lib/ldb/include/ldb.h"
+#include "../lib/util/util_ldb.h"
+#include "auth/auth.h"
+#include "auth/ntlm/ntlm_check.h"
+#include "auth/ntlm/auth_proto.h"
+#include "auth/auth_sam.h"
+#include "dsdb/samdb/samdb.h"
+#include "libcli/security/security.h"
+#include "libcli/ldap/ldap_ndr.h"
+#include "param/param.h"
+
+extern const char *user_attrs[];
+extern const char *domain_ref_attrs[];
+
+/****************************************************************************
+ Look for the specified user in the sam, return ldb result structures
+****************************************************************************/
+
+static NTSTATUS authsam_search_account(TALLOC_CTX *mem_ctx, struct ldb_context *sam_ctx,
+ const char *account_name,
+ const char *domain_name,
+ struct ldb_message ***ret_msgs,
+ struct ldb_message ***ret_msgs_domain_ref)
+{
+ struct ldb_message **msgs_tmp;
+ struct ldb_message **msgs;
+ struct ldb_message **msgs_domain_ref;
+ struct ldb_dn *partitions_basedn = samdb_partitions_dn(sam_ctx, mem_ctx);
+
+ int ret;
+ int ret_domain;
+
+ struct ldb_dn *domain_dn = NULL;
+
+ if (domain_name) {
+ domain_dn = samdb_domain_to_dn(sam_ctx, mem_ctx, domain_name);
+ if (!domain_dn) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ }
+
+ /* pull the user attributes */
+ ret = gendb_search(sam_ctx, mem_ctx, domain_dn, &msgs, user_attrs,
+ "(&(sAMAccountName=%s)(objectclass=user))",
+ ldb_binary_encode_string(mem_ctx, account_name));
+ if (ret == -1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ if (ret == 0) {
+ DEBUG(3,("sam_search_user: Couldn't find user [%s\\%s] in samdb, under %s\n",
+ domain_name, account_name, ldb_dn_get_linearized(domain_dn)));
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ if (ret > 1) {
+ DEBUG(0,("Found %d records matching user [%s]\n", ret, account_name));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ if (!domain_dn) {
+ struct dom_sid *domain_sid;
+
+ domain_sid = samdb_result_sid_prefix(mem_ctx, msgs[0], "objectSid");
+ if (!domain_sid) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ /* find the domain's DN */
+ ret = gendb_search(sam_ctx, mem_ctx, NULL, &msgs_tmp, NULL,
+ "(&(objectSid=%s)(objectClass=domain))",
+ ldap_encode_ndr_dom_sid(mem_ctx, domain_sid));
+ if (ret == -1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ if (ret == 0) {
+ DEBUG(3,("check_sam_security: Couldn't find domain_sid [%s] in passdb file.\n",
+ dom_sid_string(mem_ctx, domain_sid)));
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ if (ret > 1) {
+ DEBUG(0,("Found %d records matching domain_sid [%s]\n",
+ ret, dom_sid_string(mem_ctx, domain_sid)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ domain_dn = msgs_tmp[0]->dn;
+ }
+
+ ret_domain = gendb_search(sam_ctx, mem_ctx, partitions_basedn, &msgs_domain_ref, domain_ref_attrs,
+ "(nCName=%s)", ldb_dn_get_linearized(domain_dn));
+ if (ret_domain == -1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ if (ret_domain == 0) {
+ DEBUG(3,("check_sam_security: Couldn't find domain [%s] in passdb file.\n",
+ ldb_dn_get_linearized(msgs_tmp[0]->dn)));
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ if (ret_domain > 1) {
+ DEBUG(0,("Found %d records matching domain [%s]\n",
+ ret_domain, ldb_dn_get_linearized(msgs_tmp[0]->dn)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ *ret_msgs = msgs;
+ *ret_msgs_domain_ref = msgs_domain_ref;
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Do a specific test for an smb password being correct, given a smb_password and
+ the lanman and NT responses.
+****************************************************************************/
+static NTSTATUS authsam_password_ok(struct auth_context *auth_context,
+ TALLOC_CTX *mem_ctx,
+ uint16_t acct_flags,
+ const struct samr_Password *lm_pwd,
+ const struct samr_Password *nt_pwd,
+ const struct auth_usersupplied_info *user_info,
+ DATA_BLOB *user_sess_key,
+ DATA_BLOB *lm_sess_key)
+{
+ NTSTATUS status;
+
+ if (acct_flags & ACB_PWNOTREQ) {
+ if (lp_null_passwords(auth_context->lp_ctx)) {
+ DEBUG(3,("Account for user '%s' has no password and null passwords are allowed.\n",
+ user_info->mapped.account_name));
+ *lm_sess_key = data_blob(NULL, 0);
+ *user_sess_key = data_blob(NULL, 0);
+ return NT_STATUS_OK;
+ } else {
+ DEBUG(3,("Account for user '%s' has no password and null passwords are NOT allowed.\n",
+ user_info->mapped.account_name));
+ return NT_STATUS_LOGON_FAILURE;
+ }
+ }
+
+ switch (user_info->password_state) {
+ case AUTH_PASSWORD_PLAIN:
+ {
+ const struct auth_usersupplied_info *user_info_temp;
+ status = encrypt_user_info(mem_ctx, auth_context,
+ AUTH_PASSWORD_HASH,
+ user_info, &user_info_temp);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to convert plaintext password to password HASH: %s\n", nt_errstr(status)));
+ return status;
+ }
+ user_info = user_info_temp;
+
+ /*fall through*/
+ }
+ case AUTH_PASSWORD_HASH:
+ *lm_sess_key = data_blob(NULL, 0);
+ *user_sess_key = data_blob(NULL, 0);
+ status = hash_password_check(mem_ctx,
+ lp_lanman_auth(auth_context->lp_ctx),
+ user_info->password.hash.lanman,
+ user_info->password.hash.nt,
+ user_info->mapped.account_name,
+ lm_pwd, nt_pwd);
+ NT_STATUS_NOT_OK_RETURN(status);
+ break;
+
+ case AUTH_PASSWORD_RESPONSE:
+ status = ntlm_password_check(mem_ctx,
+ lp_lanman_auth(auth_context->lp_ctx),
+ lp_ntlm_auth(auth_context->lp_ctx),
+ user_info->logon_parameters,
+ &auth_context->challenge.data,
+ &user_info->password.response.lanman,
+ &user_info->password.response.nt,
+ user_info->mapped.account_name,
+ user_info->client.account_name,
+ user_info->client.domain_name,
+ lm_pwd, nt_pwd,
+ user_sess_key, lm_sess_key);
+ NT_STATUS_NOT_OK_RETURN(status);
+ break;
+ }
+
+ if (user_sess_key && user_sess_key->data) {
+ talloc_steal(auth_context, user_sess_key->data);
+ }
+ if (lm_sess_key && lm_sess_key->data) {
+ talloc_steal(auth_context, lm_sess_key->data);
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+
+static NTSTATUS authsam_authenticate(struct auth_context *auth_context,
+ TALLOC_CTX *mem_ctx, struct ldb_context *sam_ctx,
+ struct ldb_message **msgs,
+ struct ldb_message **msgs_domain_ref,
+ const struct auth_usersupplied_info *user_info,
+ DATA_BLOB *user_sess_key, DATA_BLOB *lm_sess_key)
+{
+ struct samr_Password *lm_pwd, *nt_pwd;
+ NTSTATUS nt_status;
+ struct ldb_dn *domain_dn = samdb_result_dn(sam_ctx, mem_ctx, msgs_domain_ref[0], "nCName", NULL);
+
+ uint16_t acct_flags = samdb_result_acct_flags(sam_ctx, mem_ctx, msgs[0], domain_dn);
+
+ /* Quit if the account was locked out. */
+ if (acct_flags & ACB_AUTOLOCK) {
+ DEBUG(3,("check_sam_security: Account for user %s was locked out.\n",
+ user_info->mapped.account_name));
+ return NT_STATUS_ACCOUNT_LOCKED_OUT;
+ }
+
+ /* You can only do an interactive login to normal accounts */
+ if (user_info->flags & USER_INFO_INTERACTIVE_LOGON) {
+ if (!(acct_flags & ACB_NORMAL)) {
+ return NT_STATUS_NO_SUCH_USER;
+ }
+ }
+
+ nt_status = samdb_result_passwords(mem_ctx, auth_context->lp_ctx, msgs[0], &lm_pwd, &nt_pwd);
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+
+ nt_status = authsam_password_ok(auth_context, mem_ctx,
+ acct_flags, lm_pwd, nt_pwd,
+ user_info, user_sess_key, lm_sess_key);
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+
+ nt_status = authsam_account_ok(mem_ctx, sam_ctx,
+ user_info->logon_parameters,
+ msgs[0],
+ msgs_domain_ref[0],
+ user_info->workstation_name,
+ user_info->mapped.account_name,
+ false);
+
+ return nt_status;
+}
+
+
+
+static NTSTATUS authsam_check_password_internals(struct auth_method_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ const char *domain,
+ const struct auth_usersupplied_info *user_info,
+ struct auth_serversupplied_info **server_info)
+{
+ NTSTATUS nt_status;
+ const char *account_name = user_info->mapped.account_name;
+ struct ldb_message **msgs;
+ struct ldb_message **domain_ref_msgs;
+ struct ldb_context *sam_ctx;
+ DATA_BLOB user_sess_key, lm_sess_key;
+ TALLOC_CTX *tmp_ctx;
+
+ if (!account_name || !*account_name) {
+ /* 'not for me' */
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (!tmp_ctx) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ sam_ctx = samdb_connect(tmp_ctx, ctx->auth_ctx->event_ctx, ctx->auth_ctx->lp_ctx, system_session(mem_ctx, ctx->auth_ctx->lp_ctx));
+ if (sam_ctx == NULL) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_INVALID_SYSTEM_SERVICE;
+ }
+
+ nt_status = authsam_search_account(tmp_ctx, sam_ctx, account_name, domain, &msgs, &domain_ref_msgs);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(tmp_ctx);
+ return nt_status;
+ }
+
+ nt_status = authsam_authenticate(ctx->auth_ctx, tmp_ctx, sam_ctx, msgs, domain_ref_msgs, user_info,
+ &user_sess_key, &lm_sess_key);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(tmp_ctx);
+ return nt_status;
+ }
+
+ nt_status = authsam_make_server_info(tmp_ctx, sam_ctx, lp_netbios_name(ctx->auth_ctx->lp_ctx),
+ msgs[0], domain_ref_msgs[0],
+ user_sess_key, lm_sess_key,
+ server_info);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(tmp_ctx);
+ return nt_status;
+ }
+
+ talloc_steal(mem_ctx, *server_info);
+ talloc_free(tmp_ctx);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS authsam_ignoredomain_want_check(struct auth_method_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ const struct auth_usersupplied_info *user_info)
+{
+ if (!user_info->mapped.account_name || !*user_info->mapped.account_name) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS authsam_ignoredomain_check_password(struct auth_method_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ const struct auth_usersupplied_info *user_info,
+ struct auth_serversupplied_info **server_info)
+{
+ return authsam_check_password_internals(ctx, mem_ctx, NULL, user_info, server_info);
+}
+
+/****************************************************************************
+Check SAM security (above) but with a few extra checks.
+****************************************************************************/
+static NTSTATUS authsam_want_check(struct auth_method_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ const struct auth_usersupplied_info *user_info)
+{
+ bool is_local_name, is_my_domain;
+
+ if (!user_info->mapped.account_name || !*user_info->mapped.account_name) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ is_local_name = lp_is_myname(ctx->auth_ctx->lp_ctx,
+ user_info->mapped.domain_name);
+ is_my_domain = lp_is_mydomain(ctx->auth_ctx->lp_ctx,
+ user_info->mapped.domain_name);
+
+ /* check whether or not we service this domain/workgroup name */
+ switch (lp_server_role(ctx->auth_ctx->lp_ctx)) {
+ case ROLE_STANDALONE:
+ return NT_STATUS_OK;
+
+ case ROLE_DOMAIN_MEMBER:
+ if (!is_local_name) {
+ DEBUG(6,("authsam_check_password: %s is not one of my local names (DOMAIN_MEMBER)\n",
+ user_info->mapped.domain_name));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return NT_STATUS_OK;
+
+ case ROLE_DOMAIN_CONTROLLER:
+ if (!is_local_name && !is_my_domain) {
+ DEBUG(6,("authsam_check_password: %s is not one of my local names or domain name (DC)\n",
+ user_info->mapped.domain_name));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return NT_STATUS_OK;
+ }
+
+ DEBUG(6,("authsam_check_password: lp_server_role() has an undefined value\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/****************************************************************************
+Check SAM security (above) but with a few extra checks.
+****************************************************************************/
+static NTSTATUS authsam_check_password(struct auth_method_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ const struct auth_usersupplied_info *user_info,
+ struct auth_serversupplied_info **server_info)
+{
+ const char *domain;
+
+ /* check whether or not we service this domain/workgroup name */
+ switch (lp_server_role(ctx->auth_ctx->lp_ctx)) {
+ case ROLE_STANDALONE:
+ case ROLE_DOMAIN_MEMBER:
+ domain = lp_netbios_name(ctx->auth_ctx->lp_ctx);
+ break;
+
+ case ROLE_DOMAIN_CONTROLLER:
+ domain = lp_workgroup(ctx->auth_ctx->lp_ctx);
+ break;
+
+ default:
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ return authsam_check_password_internals(ctx, mem_ctx, domain, user_info, server_info);
+}
+
+
+/* Used in the gensec_gssapi and gensec_krb5 server-side code, where the PAC isn't available */
+NTSTATUS authsam_get_server_info_principal(TALLOC_CTX *mem_ctx,
+ struct auth_context *auth_context,
+ const char *principal,
+ struct auth_serversupplied_info **server_info)
+{
+ NTSTATUS nt_status;
+ DATA_BLOB user_sess_key = data_blob(NULL, 0);
+ DATA_BLOB lm_sess_key = data_blob(NULL, 0);
+
+ struct ldb_message **msgs;
+ struct ldb_message **msgs_domain_ref;
+ struct ldb_context *sam_ctx;
+
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+ if (!tmp_ctx) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ sam_ctx = samdb_connect(tmp_ctx, auth_context->event_ctx, auth_context->lp_ctx,
+ system_session(tmp_ctx, auth_context->lp_ctx));
+ if (sam_ctx == NULL) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_INVALID_SYSTEM_SERVICE;
+ }
+
+ nt_status = sam_get_results_principal(sam_ctx, tmp_ctx, principal,
+ &msgs, &msgs_domain_ref);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ nt_status = authsam_make_server_info(tmp_ctx, sam_ctx,
+ lp_netbios_name(auth_context->lp_ctx),
+ msgs[0], msgs_domain_ref[0],
+ user_sess_key, lm_sess_key,
+ server_info);
+ if (NT_STATUS_IS_OK(nt_status)) {
+ talloc_steal(mem_ctx, *server_info);
+ }
+ talloc_free(tmp_ctx);
+ return nt_status;
+}
+
+static const struct auth_operations sam_ignoredomain_ops = {
+ .name = "sam_ignoredomain",
+ .get_challenge = auth_get_challenge_not_implemented,
+ .want_check = authsam_ignoredomain_want_check,
+ .check_password = authsam_ignoredomain_check_password,
+ .get_server_info_principal = authsam_get_server_info_principal
+};
+
+static const struct auth_operations sam_ops = {
+ .name = "sam",
+ .get_challenge = auth_get_challenge_not_implemented,
+ .want_check = authsam_want_check,
+ .check_password = authsam_check_password,
+ .get_server_info_principal = authsam_get_server_info_principal
+};
+
+_PUBLIC_ NTSTATUS auth_sam_init(void)
+{
+ NTSTATUS ret;
+
+ ret = auth_register(&sam_ops);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("Failed to register 'sam' auth backend!\n"));
+ return ret;
+ }
+
+ ret = auth_register(&sam_ignoredomain_ops);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("Failed to register 'sam_ignoredomain' auth backend!\n"));
+ return ret;
+ }
+
+ return ret;
+}
diff --git a/source4/auth/ntlm/auth_server.c b/source4/auth/ntlm/auth_server.c
new file mode 100644
index 0000000000..fd0ef0fe4a
--- /dev/null
+++ b/source4/auth/ntlm/auth_server.c
@@ -0,0 +1,234 @@
+/*
+ Unix SMB/CIFS implementation.
+ Authenticate by using a remote server
+ Copyright (C) Andrew Bartlett 2001-2002, 2008
+ Copyright (C) Jelmer Vernooij 2002
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "auth/auth.h"
+#include "auth/ntlm/auth_proto.h"
+#include "auth/credentials/credentials.h"
+#include "libcli/security/security.h"
+#include "librpc/gen_ndr/ndr_samr.h"
+#include "libcli/smb_composite/smb_composite.h"
+#include "param/param.h"
+#include "libcli/resolve/resolve.h"
+
+/* This version of 'security=server' rewirtten from scratch for Samba4
+ * libraries in 2008 */
+
+
+static NTSTATUS server_want_check(struct auth_method_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ const struct auth_usersupplied_info *user_info)
+{
+ return NT_STATUS_OK;
+}
+/**
+ * The challenge from the target server, when operating in security=server
+ **/
+static NTSTATUS server_get_challenge(struct auth_method_context *ctx, TALLOC_CTX *mem_ctx, DATA_BLOB *_blob)
+{
+ struct smb_composite_connect io;
+ struct smbcli_options smb_options;
+ const char **host_list;
+ NTSTATUS status;
+
+ /* Make a connection to the target server, found by 'password server' in smb.conf */
+
+ lp_smbcli_options(ctx->auth_ctx->lp_ctx, &smb_options);
+
+ /* Make a negprot, WITHOUT SPNEGO, so we get a challenge nice an easy */
+ io.in.options.use_spnego = false;
+
+ /* Hope we don't get * (the default), as this won't work... */
+ host_list = lp_passwordserver(ctx->auth_ctx->lp_ctx);
+ if (!host_list) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ io.in.dest_host = host_list[0];
+ if (strequal(io.in.dest_host, "*")) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ io.in.dest_ports = lp_smb_ports(ctx->auth_ctx->lp_ctx);
+ io.in.socket_options = lp_socket_options(ctx->auth_ctx->lp_ctx);
+ io.in.gensec_settings = lp_gensec_settings(mem_ctx, ctx->auth_ctx->lp_ctx);
+
+ io.in.called_name = strupper_talloc(mem_ctx, io.in.dest_host);
+
+ /* We don't want to get as far as the session setup */
+ io.in.credentials = cli_credentials_init_anon(mem_ctx);
+ cli_credentials_set_workstation(io.in.credentials,
+ lp_netbios_name(ctx->auth_ctx->lp_ctx),
+ CRED_SPECIFIED);
+
+ io.in.service = NULL;
+
+ io.in.workgroup = ""; /* only used with SPNEGO, disabled above */
+
+ io.in.options = smb_options;
+
+ io.in.iconv_convenience = lp_iconv_convenience(ctx->auth_ctx->lp_ctx);
+ lp_smbcli_session_options(ctx->auth_ctx->lp_ctx, &io.in.session_options);
+
+ status = smb_composite_connect(&io, mem_ctx, lp_resolve_context(ctx->auth_ctx->lp_ctx),
+ ctx->auth_ctx->event_ctx);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ *_blob = io.out.tree->session->transport->negotiate.secblob;
+ ctx->private_data = talloc_steal(ctx, io.out.tree->session);
+ return NT_STATUS_OK;
+}
+
+/**
+ * Return an error based on username
+ *
+ * This function allows the testing of obsure errors, as well as the generation
+ * of NT_STATUS -> DOS error mapping tables.
+ *
+ * This module is of no value to end-users.
+ *
+ * The password is ignored.
+ *
+ * @return An NTSTATUS value based on the username
+ **/
+
+static NTSTATUS server_check_password(struct auth_method_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ const struct auth_usersupplied_info *user_info,
+ struct auth_serversupplied_info **_server_info)
+{
+ NTSTATUS nt_status;
+ struct auth_serversupplied_info *server_info;
+ struct cli_credentials *creds;
+ struct smb_composite_sesssetup session_setup;
+
+ struct smbcli_session *session = talloc_get_type(ctx->private_data, struct smbcli_session);
+
+ creds = cli_credentials_init(mem_ctx);
+
+ NT_STATUS_HAVE_NO_MEMORY(creds);
+
+ cli_credentials_set_username(creds, user_info->client.account_name, CRED_SPECIFIED);
+ cli_credentials_set_domain(creds, user_info->client.domain_name, CRED_SPECIFIED);
+
+ switch (user_info->password_state) {
+ case AUTH_PASSWORD_PLAIN:
+ cli_credentials_set_password(creds, user_info->password.plaintext,
+ CRED_SPECIFIED);
+ break;
+ case AUTH_PASSWORD_HASH:
+ cli_credentials_set_nt_hash(creds, user_info->password.hash.nt,
+ CRED_SPECIFIED);
+ break;
+
+ case AUTH_PASSWORD_RESPONSE:
+ cli_credentials_set_ntlm_response(creds, &user_info->password.response.lanman, &user_info->password.response.nt, CRED_SPECIFIED);
+ break;
+ }
+
+ session_setup.in.sesskey = session->transport->negotiate.sesskey;
+ session_setup.in.capabilities = session->transport->negotiate.capabilities;
+
+ session_setup.in.credentials = creds;
+ session_setup.in.workgroup = ""; /* Only used with SPNEGO, which we are not doing */
+ session_setup.in.gensec_settings = lp_gensec_settings(session, ctx->auth_ctx->lp_ctx);
+
+ /* Check password with remove server - this should be async some day */
+ nt_status = smb_composite_sesssetup(session, &session_setup);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ server_info = talloc(mem_ctx, struct auth_serversupplied_info);
+ NT_STATUS_HAVE_NO_MEMORY(server_info);
+
+ server_info->account_sid = dom_sid_parse_talloc(server_info, SID_NT_ANONYMOUS);
+ NT_STATUS_HAVE_NO_MEMORY(server_info->account_sid);
+
+ /* is this correct? */
+ server_info->primary_group_sid = dom_sid_parse_talloc(server_info, SID_BUILTIN_GUESTS);
+ NT_STATUS_HAVE_NO_MEMORY(server_info->primary_group_sid);
+
+ server_info->n_domain_groups = 0;
+ server_info->domain_groups = NULL;
+
+ /* annoying, but the Anonymous really does have a session key,
+ and it is all zeros! */
+ server_info->user_session_key = data_blob(NULL, 0);
+ server_info->lm_session_key = data_blob(NULL, 0);
+
+ server_info->account_name = talloc_strdup(server_info, user_info->client.account_name);
+ NT_STATUS_HAVE_NO_MEMORY(server_info->account_name);
+
+ server_info->domain_name = talloc_strdup(server_info, user_info->client.domain_name);
+ NT_STATUS_HAVE_NO_MEMORY(server_info->domain_name);
+
+ server_info->full_name = NULL;
+
+ server_info->logon_script = talloc_strdup(server_info, "");
+ NT_STATUS_HAVE_NO_MEMORY(server_info->logon_script);
+
+ server_info->profile_path = talloc_strdup(server_info, "");
+ NT_STATUS_HAVE_NO_MEMORY(server_info->profile_path);
+
+ server_info->home_directory = talloc_strdup(server_info, "");
+ NT_STATUS_HAVE_NO_MEMORY(server_info->home_directory);
+
+ server_info->home_drive = talloc_strdup(server_info, "");
+ NT_STATUS_HAVE_NO_MEMORY(server_info->home_drive);
+
+ server_info->last_logon = 0;
+ server_info->last_logoff = 0;
+ server_info->acct_expiry = 0;
+ server_info->last_password_change = 0;
+ server_info->allow_password_change = 0;
+ server_info->force_password_change = 0;
+
+ server_info->logon_count = 0;
+ server_info->bad_password_count = 0;
+
+ server_info->acct_flags = ACB_NORMAL;
+
+ server_info->authenticated = false;
+
+ *_server_info = server_info;
+
+ return nt_status;
+}
+
+static const struct auth_operations server_auth_ops = {
+ .name = "server",
+ .get_challenge = server_get_challenge,
+ .want_check = server_want_check,
+ .check_password = server_check_password
+};
+
+_PUBLIC_ NTSTATUS auth_server_init(void)
+{
+ NTSTATUS ret;
+
+ ret = auth_register(&server_auth_ops);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("Failed to register 'server' auth backend!\n"));
+ return ret;
+ }
+
+ return ret;
+}
diff --git a/source4/auth/ntlm/auth_simple.c b/source4/auth/ntlm/auth_simple.c
new file mode 100644
index 0000000000..ccf677bf51
--- /dev/null
+++ b/source4/auth/ntlm/auth_simple.c
@@ -0,0 +1,103 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ auth functions
+
+ Copyright (C) Simo Sorce 2005
+ Copyright (C) Andrew Tridgell 2005
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "auth/auth.h"
+#include "lib/events/events.h"
+#include "param/param.h"
+#include "auth/session_proto.h"
+
+/*
+ It's allowed to pass NULL as session_info,
+ when the caller doesn't need a session_info
+*/
+_PUBLIC_ NTSTATUS authenticate_username_pw(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct messaging_context *msg,
+ struct loadparm_context *lp_ctx,
+ const char *nt4_domain,
+ const char *nt4_username,
+ const char *password,
+ struct auth_session_info **session_info)
+{
+ struct auth_context *auth_context;
+ struct auth_usersupplied_info *user_info;
+ struct auth_serversupplied_info *server_info;
+ NTSTATUS nt_status;
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+
+ if (!tmp_ctx) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ nt_status = auth_context_create(tmp_ctx,
+ ev, msg,
+ lp_ctx,
+ &auth_context);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(tmp_ctx);
+ return nt_status;
+ }
+
+ user_info = talloc(tmp_ctx, struct auth_usersupplied_info);
+ if (!user_info) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ user_info->mapped_state = true;
+ user_info->client.account_name = nt4_username;
+ user_info->mapped.account_name = nt4_username;
+ user_info->client.domain_name = nt4_domain;
+ user_info->mapped.domain_name = nt4_domain;
+
+ user_info->workstation_name = NULL;
+
+ user_info->remote_host = NULL;
+
+ user_info->password_state = AUTH_PASSWORD_PLAIN;
+ user_info->password.plaintext = talloc_strdup(user_info, password);
+
+ user_info->flags = USER_INFO_CASE_INSENSITIVE_USERNAME |
+ USER_INFO_DONT_CHECK_UNIX_ACCOUNT;
+
+ user_info->logon_parameters = 0;
+
+ nt_status = auth_check_password(auth_context, tmp_ctx, user_info, &server_info);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(tmp_ctx);
+ return nt_status;
+ }
+
+ if (session_info) {
+ nt_status = auth_generate_session_info(tmp_ctx, ev, lp_ctx, server_info, session_info);
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ talloc_steal(mem_ctx, *session_info);
+ }
+ }
+
+ talloc_free(tmp_ctx);
+ return nt_status;
+}
+
diff --git a/source4/auth/ntlm/auth_unix.c b/source4/auth/ntlm/auth_unix.c
new file mode 100644
index 0000000000..1717b9d0e1
--- /dev/null
+++ b/source4/auth/ntlm/auth_unix.c
@@ -0,0 +1,844 @@
+/*
+ Unix SMB/CIFS implementation.
+ Password and authentication handling
+ Copyright (C) Andrew Bartlett 2001
+ Copyright (C) Jeremy Allison 2001
+ Copyright (C) Simo Sorce 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "auth/auth.h"
+#include "auth/ntlm/auth_proto.h"
+#include "system/passwd.h" /* needed by some systems for struct passwd */
+#include "lib/socket/socket.h"
+#include "auth/ntlm/pam_errors.h"
+#include "param/param.h"
+
+/* TODO: look at how to best fill in parms retrieveing a struct passwd info
+ * except in case USER_INFO_DONT_CHECK_UNIX_ACCOUNT is set
+ */
+static NTSTATUS authunix_make_server_info(TALLOC_CTX *mem_ctx,
+ const char *netbios_name,
+ const struct auth_usersupplied_info *user_info,
+ struct passwd *pwd,
+ struct auth_serversupplied_info **_server_info)
+{
+ struct auth_serversupplied_info *server_info;
+ NTSTATUS status;
+
+ /* This is a real, real hack */
+ if (pwd->pw_uid == 0) {
+ status = auth_system_server_info(mem_ctx, netbios_name, &server_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ server_info->account_name = talloc_steal(server_info, pwd->pw_name);
+ NT_STATUS_HAVE_NO_MEMORY(server_info->account_name);
+
+ server_info->domain_name = talloc_strdup(server_info, "unix");
+ NT_STATUS_HAVE_NO_MEMORY(server_info->domain_name);
+ } else {
+ server_info = talloc(mem_ctx, struct auth_serversupplied_info);
+ NT_STATUS_HAVE_NO_MEMORY(server_info);
+
+ server_info->authenticated = true;
+
+ server_info->account_name = talloc_steal(server_info, pwd->pw_name);
+ NT_STATUS_HAVE_NO_MEMORY(server_info->account_name);
+
+ server_info->domain_name = talloc_strdup(server_info, "unix");
+ NT_STATUS_HAVE_NO_MEMORY(server_info->domain_name);
+
+ /* This isn't in any way correct.. */
+ server_info->account_sid = NULL;
+ server_info->primary_group_sid = NULL;
+ server_info->n_domain_groups = 0;
+ server_info->domain_groups = NULL;
+ }
+ server_info->user_session_key = data_blob(NULL,0);
+ server_info->lm_session_key = data_blob(NULL,0);
+
+ server_info->full_name = talloc_steal(server_info, pwd->pw_gecos);
+ NT_STATUS_HAVE_NO_MEMORY(server_info->full_name);
+ server_info->logon_script = talloc_strdup(server_info, "");
+ NT_STATUS_HAVE_NO_MEMORY(server_info->logon_script);
+ server_info->profile_path = talloc_strdup(server_info, "");
+ NT_STATUS_HAVE_NO_MEMORY(server_info->profile_path);
+ server_info->home_directory = talloc_strdup(server_info, "");
+ NT_STATUS_HAVE_NO_MEMORY(server_info->home_directory);
+ server_info->home_drive = talloc_strdup(server_info, "");
+ NT_STATUS_HAVE_NO_MEMORY(server_info->home_drive);
+
+ server_info->last_logon = 0;
+ server_info->last_logoff = 0;
+ server_info->acct_expiry = 0;
+ server_info->last_password_change = 0;
+ server_info->allow_password_change = 0;
+ server_info->force_password_change = 0;
+ server_info->logon_count = 0;
+ server_info->bad_password_count = 0;
+ server_info->acct_flags = 0;
+
+ *_server_info = server_info;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS talloc_getpwnam(TALLOC_CTX *ctx, const char *username, struct passwd **pws)
+{
+ struct passwd *ret;
+ struct passwd *from;
+
+ *pws = NULL;
+
+ ret = talloc(ctx, struct passwd);
+ NT_STATUS_HAVE_NO_MEMORY(ret);
+
+ from = getpwnam(username);
+ if (!from) {
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ ret->pw_name = talloc_strdup(ctx, from->pw_name);
+ NT_STATUS_HAVE_NO_MEMORY(ret->pw_name);
+
+ ret->pw_passwd = talloc_strdup(ctx, from->pw_passwd);
+ NT_STATUS_HAVE_NO_MEMORY(ret->pw_passwd);
+
+ ret->pw_uid = from->pw_uid;
+ ret->pw_gid = from->pw_gid;
+ ret->pw_gecos = talloc_strdup(ctx, from->pw_gecos);
+ NT_STATUS_HAVE_NO_MEMORY(ret->pw_gecos);
+
+ ret->pw_dir = talloc_strdup(ctx, from->pw_dir);
+ NT_STATUS_HAVE_NO_MEMORY(ret->pw_dir);
+
+ ret->pw_shell = talloc_strdup(ctx, from->pw_shell);
+ NT_STATUS_HAVE_NO_MEMORY(ret->pw_shell);
+
+ *pws = ret;
+
+ return NT_STATUS_OK;
+}
+
+
+#ifdef HAVE_SECURITY_PAM_APPL_H
+#include <security/pam_appl.h>
+
+struct smb_pam_user_info {
+ const char *account_name;
+ const char *plaintext_password;
+};
+
+#define COPY_STRING(s) (s) ? strdup(s) : NULL
+
+/*
+ * Check user password
+ * Currently it uses PAM only and fails on systems without PAM
+ * Samba3 code located in pass_check.c is to ugly to be used directly it will
+ * need major rework that's why pass_check.c is still there.
+*/
+
+static int smb_pam_conv(int num_msg, const struct pam_message **msg,
+ struct pam_response **reply, void *appdata_ptr)
+{
+ struct smb_pam_user_info *info = (struct smb_pam_user_info *)appdata_ptr;
+ int num;
+
+ if (num_msg <= 0) {
+ *reply = NULL;
+ return PAM_CONV_ERR;
+ }
+
+ /*
+ * Apparantly HPUX has a buggy PAM that doesn't support the
+ * data pointer. Fail if this is the case. JRA.
+ */
+
+ if (info == NULL) {
+ *reply = NULL;
+ return PAM_CONV_ERR;
+ }
+
+ /*
+ * PAM frees memory in reply messages by itself
+ * so use malloc instead of talloc here.
+ */
+ *reply = malloc_array_p(struct pam_response, num_msg);
+ if (*reply == NULL) {
+ return PAM_CONV_ERR;
+ }
+
+ for (num = 0; num < num_msg; num++) {
+ switch (msg[num]->msg_style) {
+ case PAM_PROMPT_ECHO_ON:
+ (*reply)[num].resp_retcode = PAM_SUCCESS;
+ (*reply)[num].resp = COPY_STRING(info->account_name);
+ break;
+
+ case PAM_PROMPT_ECHO_OFF:
+ (*reply)[num].resp_retcode = PAM_SUCCESS;
+ (*reply)[num].resp = COPY_STRING(info->plaintext_password);
+ break;
+
+ case PAM_TEXT_INFO:
+ (*reply)[num].resp_retcode = PAM_SUCCESS;
+ (*reply)[num].resp = NULL;
+ DEBUG(4,("PAM Info message in conversation function: %s\n", (msg[num]->msg)));
+ break;
+
+ case PAM_ERROR_MSG:
+ (*reply)[num].resp_retcode = PAM_SUCCESS;
+ (*reply)[num].resp = NULL;
+ DEBUG(4,("PAM Error message in conversation function: %s\n", (msg[num]->msg)));
+ break;
+
+ default:
+ while (num > 0) {
+ SAFE_FREE((*reply)[num-1].resp);
+ num--;
+ }
+ SAFE_FREE(*reply);
+ *reply = NULL;
+ DEBUG(1,("Error: PAM subsystme sent an UNKNOWN message type to the conversation function!\n"));
+ return PAM_CONV_ERR;
+ }
+ }
+
+ return PAM_SUCCESS;
+}
+
+/*
+ * Start PAM authentication for specified account
+ */
+
+static NTSTATUS smb_pam_start(pam_handle_t **pamh, const char *account_name, const char *remote_host, struct pam_conv *pconv)
+{
+ int pam_error;
+
+ if (account_name == NULL || remote_host == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ DEBUG(4,("smb_pam_start: PAM: Init user: %s\n", account_name));
+
+ pam_error = pam_start("samba", account_name, pconv, pamh);
+ if (pam_error != PAM_SUCCESS) {
+ /* no valid pamh here, can we reliably call pam_strerror ? */
+ DEBUG(4,("smb_pam_start: pam_start failed!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+#ifdef PAM_RHOST
+ DEBUG(4,("smb_pam_start: PAM: setting rhost to: %s\n", remote_host));
+ pam_error = pam_set_item(*pamh, PAM_RHOST, remote_host);
+ if (pam_error != PAM_SUCCESS) {
+ NTSTATUS nt_status;
+
+ DEBUG(4,("smb_pam_start: setting rhost failed with error: %s\n",
+ pam_strerror(*pamh, pam_error)));
+ nt_status = pam_to_nt_status(pam_error);
+
+ pam_error = pam_end(*pamh, 0);
+ if (pam_error != PAM_SUCCESS) {
+ /* no vaild pamh here, can we reliably call pam_strerror ? */
+ DEBUG(4,("smb_pam_start: clean up failed, pam_end gave error %d.\n",
+ pam_error));
+ return pam_to_nt_status(pam_error);
+ }
+ return nt_status;
+ }
+#endif
+#ifdef PAM_TTY
+ DEBUG(4,("smb_pam_start: PAM: setting tty\n"));
+ pam_error = pam_set_item(*pamh, PAM_TTY, "samba");
+ if (pam_error != PAM_SUCCESS) {
+ NTSTATUS nt_status;
+
+ DEBUG(4,("smb_pam_start: setting tty failed with error: %s\n",
+ pam_strerror(*pamh, pam_error)));
+ nt_status = pam_to_nt_status(pam_error);
+
+ pam_error = pam_end(*pamh, 0);
+ if (pam_error != PAM_SUCCESS) {
+ /* no vaild pamh here, can we reliably call pam_strerror ? */
+ DEBUG(4,("smb_pam_start: clean up failed, pam_end gave error %d.\n",
+ pam_error));
+ return pam_to_nt_status(pam_error);
+ }
+ return nt_status;
+ }
+#endif
+ DEBUG(4,("smb_pam_start: PAM: Init passed for user: %s\n", account_name));
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS smb_pam_end(pam_handle_t *pamh)
+{
+ int pam_error;
+
+ if (pamh != NULL) {
+ pam_error = pam_end(pamh, 0);
+ if (pam_error != PAM_SUCCESS) {
+ /* no vaild pamh here, can we reliably call pam_strerror ? */
+ DEBUG(4,("smb_pam_end: clean up failed, pam_end gave error %d.\n",
+ pam_error));
+ return pam_to_nt_status(pam_error);
+ }
+ return NT_STATUS_OK;
+ }
+
+ DEBUG(2,("smb_pam_end: pamh is NULL, PAM not initialized ?\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+/*
+ * PAM Authentication Handler
+ */
+static NTSTATUS smb_pam_auth(pam_handle_t *pamh, bool allow_null_passwords, const char *user)
+{
+ int pam_error;
+
+ /*
+ * To enable debugging set in /etc/pam.d/samba:
+ * auth required /lib/security/pam_pwdb.so nullok shadow audit
+ */
+
+ DEBUG(4,("smb_pam_auth: PAM: Authenticate User: %s\n", user));
+
+ pam_error = pam_authenticate(pamh, PAM_SILENT | allow_null_passwords ? 0 : PAM_DISALLOW_NULL_AUTHTOK);
+ switch( pam_error ){
+ case PAM_AUTH_ERR:
+ DEBUG(2, ("smb_pam_auth: PAM: Authentication Error for user %s\n", user));
+ break;
+ case PAM_CRED_INSUFFICIENT:
+ DEBUG(2, ("smb_pam_auth: PAM: Insufficient Credentials for user %s\n", user));
+ break;
+ case PAM_AUTHINFO_UNAVAIL:
+ DEBUG(2, ("smb_pam_auth: PAM: Authentication Information Unavailable for user %s\n", user));
+ break;
+ case PAM_USER_UNKNOWN:
+ DEBUG(2, ("smb_pam_auth: PAM: Username %s NOT known to Authentication system\n", user));
+ break;
+ case PAM_MAXTRIES:
+ DEBUG(2, ("smb_pam_auth: PAM: One or more authentication modules reports user limit for user %s exceeeded\n", user));
+ break;
+ case PAM_ABORT:
+ DEBUG(0, ("smb_pam_auth: PAM: One or more PAM modules failed to load for user %s\n", user));
+ break;
+ case PAM_SUCCESS:
+ DEBUG(4, ("smb_pam_auth: PAM: User %s Authenticated OK\n", user));
+ break;
+ default:
+ DEBUG(0, ("smb_pam_auth: PAM: UNKNOWN ERROR while authenticating user %s\n", user));
+ break;
+ }
+
+ return pam_to_nt_status(pam_error);
+}
+
+/*
+ * PAM Account Handler
+ */
+static NTSTATUS smb_pam_account(pam_handle_t *pamh, const char * user)
+{
+ int pam_error;
+
+ DEBUG(4,("smb_pam_account: PAM: Account Management for User: %s\n", user));
+
+ pam_error = pam_acct_mgmt(pamh, PAM_SILENT); /* Is user account enabled? */
+ switch( pam_error ) {
+ case PAM_AUTHTOK_EXPIRED:
+ DEBUG(2, ("smb_pam_account: PAM: User %s is valid but password is expired\n", user));
+ break;
+ case PAM_ACCT_EXPIRED:
+ DEBUG(2, ("smb_pam_account: PAM: User %s no longer permitted to access system\n", user));
+ break;
+ case PAM_AUTH_ERR:
+ DEBUG(2, ("smb_pam_account: PAM: There was an authentication error for user %s\n", user));
+ break;
+ case PAM_PERM_DENIED:
+ DEBUG(0, ("smb_pam_account: PAM: User %s is NOT permitted to access system at this time\n", user));
+ break;
+ case PAM_USER_UNKNOWN:
+ DEBUG(0, ("smb_pam_account: PAM: User \"%s\" is NOT known to account management\n", user));
+ break;
+ case PAM_SUCCESS:
+ DEBUG(4, ("smb_pam_account: PAM: Account OK for User: %s\n", user));
+ break;
+ default:
+ DEBUG(0, ("smb_pam_account: PAM: UNKNOWN PAM ERROR (%d) during Account Management for User: %s\n", pam_error, user));
+ break;
+ }
+
+ return pam_to_nt_status(pam_error);
+}
+
+/*
+ * PAM Credential Setting
+ */
+
+static NTSTATUS smb_pam_setcred(pam_handle_t *pamh, const char * user)
+{
+ int pam_error;
+
+ /*
+ * This will allow samba to aquire a kerberos token. And, when
+ * exporting an AFS cell, be able to /write/ to this cell.
+ */
+
+ DEBUG(4,("PAM: Account Management SetCredentials for User: %s\n", user));
+
+ pam_error = pam_setcred(pamh, (PAM_ESTABLISH_CRED|PAM_SILENT));
+ switch( pam_error ) {
+ case PAM_CRED_UNAVAIL:
+ DEBUG(0, ("smb_pam_setcred: PAM: Credentials not found for user:%s\n", user ));
+ break;
+ case PAM_CRED_EXPIRED:
+ DEBUG(0, ("smb_pam_setcred: PAM: Credentials for user: \"%s\" EXPIRED!\n", user ));
+ break;
+ case PAM_USER_UNKNOWN:
+ DEBUG(0, ("smb_pam_setcred: PAM: User: \"%s\" is NOT known so can not set credentials!\n", user ));
+ break;
+ case PAM_CRED_ERR:
+ DEBUG(0, ("smb_pam_setcred: PAM: Unknown setcredentials error - unable to set credentials for %s\n", user ));
+ break;
+ case PAM_SUCCESS:
+ DEBUG(4, ("smb_pam_setcred: PAM: SetCredentials OK for User: %s\n", user));
+ break;
+ default:
+ DEBUG(0, ("smb_pam_setcred: PAM: UNKNOWN PAM ERROR (%d) during SetCredentials for User: %s\n", pam_error, user));
+ break;
+ }
+
+ return pam_to_nt_status(pam_error);
+}
+
+static NTSTATUS check_unix_password(TALLOC_CTX *ctx, struct loadparm_context *lp_ctx,
+ const struct auth_usersupplied_info *user_info, struct passwd **pws)
+{
+ struct smb_pam_user_info *info;
+ struct pam_conv *pamconv;
+ pam_handle_t *pamh;
+ NTSTATUS nt_status;
+
+ info = talloc(ctx, struct smb_pam_user_info);
+ if (info == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ info->account_name = user_info->mapped.account_name;
+ info->plaintext_password = user_info->password.plaintext;
+
+ pamconv = talloc(ctx, struct pam_conv);
+ if (pamconv == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ pamconv->conv = smb_pam_conv;
+ pamconv->appdata_ptr = (void *)info;
+
+ /* TODO:
+ * check for user_info->flags & USER_INFO_CASE_INSENSITIVE_USERNAME
+ * if true set up a crack name routine.
+ */
+
+ nt_status = smb_pam_start(&pamh, user_info->mapped.account_name, user_info->remote_host ? user_info->remote_host->addr : NULL, pamconv);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ nt_status = smb_pam_auth(pamh, lp_null_passwords(lp_ctx), user_info->mapped.account_name);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ smb_pam_end(pamh);
+ return nt_status;
+ }
+
+ if ( ! (user_info->flags & USER_INFO_DONT_CHECK_UNIX_ACCOUNT)) {
+
+ nt_status = smb_pam_account(pamh, user_info->mapped.account_name);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ smb_pam_end(pamh);
+ return nt_status;
+ }
+
+ nt_status = smb_pam_setcred(pamh, user_info->mapped.account_name);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ smb_pam_end(pamh);
+ return nt_status;
+ }
+ }
+
+ smb_pam_end(pamh);
+
+ nt_status = talloc_getpwnam(ctx, user_info->mapped.account_name, pws);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+#else
+
+/****************************************************************************
+core of password checking routine
+****************************************************************************/
+static NTSTATUS password_check(const char *username, const char *password,
+ const char *crypted, const char *salt)
+{
+ bool ret;
+
+#ifdef WITH_AFS
+ if (afs_auth(username, password))
+ return NT_STATUS_OK;
+#endif /* WITH_AFS */
+
+#ifdef WITH_DFS
+ if (dfs_auth(username, password))
+ return NT_STATUS_OK;
+#endif /* WITH_DFS */
+
+#ifdef OSF1_ENH_SEC
+
+ ret = (strcmp(osf1_bigcrypt(password, salt), crypted) == 0);
+
+ if (!ret) {
+ DEBUG(2,
+ ("OSF1_ENH_SEC failed. Trying normal crypt.\n"));
+ ret = (strcmp((char *)crypt(password, salt), crypted) == 0);
+ }
+ if (ret) {
+ return NT_STATUS_OK;
+ } else {
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+#endif /* OSF1_ENH_SEC */
+
+#ifdef ULTRIX_AUTH
+ ret = (strcmp((char *)crypt16(password, salt), crypted) == 0);
+ if (ret) {
+ return NT_STATUS_OK;
+ } else {
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+#endif /* ULTRIX_AUTH */
+
+#ifdef LINUX_BIGCRYPT
+ ret = (linux_bigcrypt(password, salt, crypted));
+ if (ret) {
+ return NT_STATUS_OK;
+ } else {
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+#endif /* LINUX_BIGCRYPT */
+
+#if defined(HAVE_BIGCRYPT) && defined(HAVE_CRYPT) && defined(USE_BOTH_CRYPT_CALLS)
+
+ /*
+ * Some systems have bigcrypt in the C library but might not
+ * actually use it for the password hashes (HPUX 10.20) is
+ * a noteable example. So we try bigcrypt first, followed
+ * by crypt.
+ */
+
+ if (strcmp(bigcrypt(password, salt), crypted) == 0)
+ return NT_STATUS_OK;
+ else
+ ret = (strcmp((char *)crypt(password, salt), crypted) == 0);
+ if (ret) {
+ return NT_STATUS_OK;
+ } else {
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+#else /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
+
+#ifdef HAVE_BIGCRYPT
+ ret = (strcmp(bigcrypt(password, salt), crypted) == 0);
+ if (ret) {
+ return NT_STATUS_OK;
+ } else {
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+#endif /* HAVE_BIGCRYPT */
+
+#ifndef HAVE_CRYPT
+ DEBUG(1, ("Warning - no crypt available\n"));
+ return NT_STATUS_LOGON_FAILURE;
+#else /* HAVE_CRYPT */
+ ret = (strcmp((char *)crypt(password, salt), crypted) == 0);
+ if (ret) {
+ return NT_STATUS_OK;
+ } else {
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+#endif /* HAVE_CRYPT */
+#endif /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
+}
+
+static NTSTATUS check_unix_password(TALLOC_CTX *ctx, struct loadparm_context *lp_ctx,
+ const struct auth_usersupplied_info *user_info, struct passwd **ret_passwd)
+{
+ char *username;
+ char *password;
+ char *pwcopy;
+ char *salt;
+ char *crypted;
+ struct passwd *pws;
+ NTSTATUS nt_status;
+ int level = lp_passwordlevel(lp_ctx);
+
+ *ret_passwd = NULL;
+
+ username = talloc_strdup(ctx, user_info->mapped.account_name);
+ password = talloc_strdup(ctx, user_info->password.plaintext);
+
+ nt_status = talloc_getpwnam(ctx, username, &pws);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ crypted = pws->pw_passwd;
+ salt = pws->pw_passwd;
+
+#ifdef HAVE_GETSPNAM
+ {
+ struct spwd *spass;
+
+ /* many shadow systems require you to be root to get
+ the password, in most cases this should already be
+ the case when this function is called, except
+ perhaps for IPC password changing requests */
+
+ spass = getspnam(pws->pw_name);
+ if (spass && spass->sp_pwdp) {
+ crypted = talloc_strdup(ctx, spass->sp_pwdp);
+ NT_STATUS_HAVE_NO_MEMORY(crypted);
+ salt = talloc_strdup(ctx, spass->sp_pwdp);
+ NT_STATUS_HAVE_NO_MEMORY(salt);
+ }
+ }
+#elif defined(IA_UINFO)
+ {
+ char *ia_password;
+ /* Need to get password with SVR4.2's ia_ functions
+ instead of get{sp,pw}ent functions. Required by
+ UnixWare 2.x, tested on version
+ 2.1. (tangent@cyberport.com) */
+ uinfo_t uinfo;
+ if (ia_openinfo(pws->pw_name, &uinfo) != -1) {
+ ia_get_logpwd(uinfo, &ia_password);
+ crypted = talloc_strdup(ctx, ia_password);
+ NT_STATUS_HAVE_NO_MEMORY(crypted);
+ }
+ }
+#endif
+
+#ifdef HAVE_GETPRPWNAM
+ {
+ struct pr_passwd *pr_pw = getprpwnam(pws->pw_name);
+ if (pr_pw && pr_pw->ufld.fd_encrypt) {
+ crypted = talloc_strdup(ctx, pr_pw->ufld.fd_encrypt);
+ NT_STATUS_HAVE_NO_MEMORY(crypted);
+ }
+ }
+#endif
+
+#ifdef HAVE_GETPWANAM
+ {
+ struct passwd_adjunct *pwret;
+ pwret = getpwanam(s);
+ if (pwret && pwret->pwa_passwd) {
+ crypted = talloc_strdup(ctx, pwret->pwa_passwd);
+ NT_STATUS_HAVE_NO_MEMORY(crypted);
+ }
+ }
+#endif
+
+#ifdef OSF1_ENH_SEC
+ {
+ struct pr_passwd *mypasswd;
+ DEBUG(5,("Checking password for user %s in OSF1_ENH_SEC\n", username));
+ mypasswd = getprpwnam(username);
+ if (mypasswd) {
+ username = talloc_strdup(ctx, mypasswd->ufld.fd_name);
+ NT_STATUS_HAVE_NO_MEMORY(username);
+ crypted = talloc_strdup(ctx, mypasswd->ufld.fd_encrypt);
+ NT_STATUS_HAVE_NO_MEMORY(crypted);
+ } else {
+ DEBUG(5,("OSF1_ENH_SEC: No entry for user %s in protected database !\n", username));
+ }
+ }
+#endif
+
+#ifdef ULTRIX_AUTH
+ {
+ AUTHORIZATION *ap = getauthuid(pws->pw_uid);
+ if (ap) {
+ crypted = talloc_strdup(ctx, ap->a_password);
+ endauthent();
+ NT_STATUS_HAVE_NO_MEMORY(crypted);
+ }
+ }
+#endif
+
+#if defined(HAVE_TRUNCATED_SALT)
+ /* crypt on some platforms (HPUX in particular)
+ won't work with more than 2 salt characters. */
+ salt[2] = 0;
+#endif
+
+ if (crypted[0] == '\0') {
+ if (!lp_null_passwords(lp_ctx)) {
+ DEBUG(2, ("Disallowing %s with null password\n", username));
+ return NT_STATUS_LOGON_FAILURE;
+ }
+ if (password == NULL) {
+ DEBUG(3, ("Allowing access to %s with null password\n", username));
+ *ret_passwd = pws;
+ return NT_STATUS_OK;
+ }
+ }
+
+ /* try it as it came to us */
+ nt_status = password_check(username, password, crypted, salt);
+ if (NT_STATUS_IS_OK(nt_status)) {
+ *ret_passwd = pws;
+ return nt_status;
+ }
+ else if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD)) {
+ /* No point continuing if its not the password thats to blame (ie PAM disabled). */
+ return nt_status;
+ }
+
+ if ( user_info->flags | USER_INFO_CASE_INSENSITIVE_PASSWORD) {
+ return nt_status;
+ }
+
+ /* if the password was given to us with mixed case then we don't
+ * need to proceed as we know it hasn't been case modified by the
+ * client */
+ if (strhasupper(password) && strhaslower(password)) {
+ return nt_status;
+ }
+
+ /* make a copy of it */
+ pwcopy = talloc_strdup(ctx, password);
+ if (!pwcopy)
+ return NT_STATUS_NO_MEMORY;
+
+ /* try all lowercase if it's currently all uppercase */
+ if (strhasupper(pwcopy)) {
+ strlower(pwcopy);
+ nt_status = password_check(username, pwcopy, crypted, salt);
+ if NT_STATUS_IS_OK(nt_status) {
+ *ret_passwd = pws;
+ return nt_status;
+ }
+ }
+
+ /* give up? */
+ if (level < 1) {
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /* last chance - all combinations of up to level chars upper! */
+ strlower(pwcopy);
+
+#if 0
+ if (NT_STATUS_IS_OK(nt_status = string_combinations(pwcopy, password_check, level))) {
+ *ret_passwd = pws;
+ return nt_status;
+ }
+#endif
+ return NT_STATUS_WRONG_PASSWORD;
+}
+
+#endif
+
+/** Check a plaintext username/password
+ *
+ **/
+
+static NTSTATUS authunix_want_check(struct auth_method_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ const struct auth_usersupplied_info *user_info)
+{
+ if (!user_info->mapped.account_name || !*user_info->mapped.account_name) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS authunix_check_password(struct auth_method_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ const struct auth_usersupplied_info *user_info,
+ struct auth_serversupplied_info **server_info)
+{
+ TALLOC_CTX *check_ctx;
+ NTSTATUS nt_status;
+ struct passwd *pwd;
+
+ if (user_info->password_state != AUTH_PASSWORD_PLAIN) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ check_ctx = talloc_named_const(mem_ctx, 0, "check_unix_password");
+ if (check_ctx == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ nt_status = check_unix_password(check_ctx, ctx->auth_ctx->lp_ctx, user_info, &pwd);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(check_ctx);
+ return nt_status;
+ }
+
+ nt_status = authunix_make_server_info(mem_ctx, lp_netbios_name(ctx->auth_ctx->lp_ctx),
+ user_info, pwd, server_info);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(check_ctx);
+ return nt_status;
+ }
+
+ talloc_free(check_ctx);
+ return NT_STATUS_OK;
+}
+
+static const struct auth_operations unix_ops = {
+ .name = "unix",
+ .get_challenge = auth_get_challenge_not_implemented,
+ .want_check = authunix_want_check,
+ .check_password = authunix_check_password
+};
+
+_PUBLIC_ NTSTATUS auth_unix_init(void)
+{
+ NTSTATUS ret;
+
+ ret = auth_register(&unix_ops);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("Failed to register unix auth backend!\n"));
+ return ret;
+ }
+
+ return ret;
+}
diff --git a/source4/auth/ntlm/auth_util.c b/source4/auth/ntlm/auth_util.c
new file mode 100644
index 0000000000..2f8ef10e05
--- /dev/null
+++ b/source4/auth/ntlm/auth_util.c
@@ -0,0 +1,261 @@
+/*
+ Unix SMB/CIFS implementation.
+ Authentication utility functions
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Andrew Bartlett 2001
+ Copyright (C) Jeremy Allison 2000-2001
+ Copyright (C) Rafal Szczesniak 2002
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "auth/auth.h"
+#include "auth/auth_proto.h"
+#include "libcli/security/security.h"
+#include "libcli/auth/libcli_auth.h"
+#include "dsdb/samdb/samdb.h"
+#include "auth/credentials/credentials.h"
+#include "param/param.h"
+
+/* this default function can be used by mostly all backends
+ * which don't want to set a challenge
+ */
+NTSTATUS auth_get_challenge_not_implemented(struct auth_method_context *ctx, TALLOC_CTX *mem_ctx, DATA_BLOB *challenge)
+{
+ /* we don't want to set a challenge */
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/****************************************************************************
+ Create an auth_usersupplied_data structure after appropriate mapping.
+****************************************************************************/
+
+NTSTATUS map_user_info(TALLOC_CTX *mem_ctx,
+ const char *default_domain,
+ const struct auth_usersupplied_info *user_info,
+ struct auth_usersupplied_info **user_info_mapped)
+{
+ const char *domain;
+ char *account_name;
+ char *d;
+ DEBUG(5,("map_user_info: Mapping user [%s]\\[%s] from workstation [%s]\n",
+ user_info->client.domain_name, user_info->client.account_name, user_info->workstation_name));
+
+ account_name = talloc_strdup(mem_ctx, user_info->client.account_name);
+ if (!account_name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* don't allow "" as a domain, fixes a Win9X bug
+ where it doesn't supply a domain for logon script
+ 'net use' commands. */
+
+ /* Split user@realm names into user and realm components. This is TODO to fix with proper userprincipalname support */
+ if (user_info->client.domain_name && *user_info->client.domain_name) {
+ domain = user_info->client.domain_name;
+ } else if (strchr_m(user_info->client.account_name, '@')) {
+ d = strchr_m(account_name, '@');
+ if (!d) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ d[0] = '\0';
+ d++;
+ domain = d;
+ } else {
+ domain = default_domain;
+ }
+
+ *user_info_mapped = talloc(mem_ctx, struct auth_usersupplied_info);
+ if (!*user_info_mapped) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ if (!talloc_reference(*user_info_mapped, user_info)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ **user_info_mapped = *user_info;
+ (*user_info_mapped)->mapped_state = true;
+ (*user_info_mapped)->mapped.domain_name = talloc_strdup(*user_info_mapped, domain);
+ (*user_info_mapped)->mapped.account_name = talloc_strdup(*user_info_mapped, account_name);
+ talloc_free(account_name);
+ if (!(*user_info_mapped)->mapped.domain_name
+ || !(*user_info_mapped)->mapped.account_name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Create an auth_usersupplied_data structure after appropriate mapping.
+****************************************************************************/
+
+NTSTATUS encrypt_user_info(TALLOC_CTX *mem_ctx, struct auth_context *auth_context,
+ enum auth_password_state to_state,
+ const struct auth_usersupplied_info *user_info_in,
+ const struct auth_usersupplied_info **user_info_encrypted)
+{
+ NTSTATUS nt_status;
+ struct auth_usersupplied_info *user_info_temp;
+ switch (to_state) {
+ case AUTH_PASSWORD_RESPONSE:
+ switch (user_info_in->password_state) {
+ case AUTH_PASSWORD_PLAIN:
+ {
+ const struct auth_usersupplied_info *user_info_temp2;
+ nt_status = encrypt_user_info(mem_ctx, auth_context,
+ AUTH_PASSWORD_HASH,
+ user_info_in, &user_info_temp2);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+ user_info_in = user_info_temp2;
+ /* fall through */
+ }
+ case AUTH_PASSWORD_HASH:
+ {
+ const uint8_t *challenge;
+ DATA_BLOB chall_blob;
+ user_info_temp = talloc(mem_ctx, struct auth_usersupplied_info);
+ if (!user_info_temp) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ if (!talloc_reference(user_info_temp, user_info_in)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ *user_info_temp = *user_info_in;
+ user_info_temp->mapped_state = to_state;
+
+ nt_status = auth_get_challenge(auth_context, &challenge);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ chall_blob = data_blob_talloc(mem_ctx, challenge, 8);
+ if (lp_client_ntlmv2_auth(auth_context->lp_ctx)) {
+ DATA_BLOB names_blob = NTLMv2_generate_names_blob(mem_ctx, lp_netbios_name(auth_context->lp_ctx), lp_workgroup(auth_context->lp_ctx));
+ DATA_BLOB lmv2_response, ntlmv2_response, lmv2_session_key, ntlmv2_session_key;
+
+ if (!SMBNTLMv2encrypt_hash(user_info_temp,
+ user_info_in->client.account_name,
+ user_info_in->client.domain_name,
+ user_info_in->password.hash.nt->hash, &chall_blob,
+ &names_blob,
+ &lmv2_response, &ntlmv2_response,
+ &lmv2_session_key, &ntlmv2_session_key)) {
+ data_blob_free(&names_blob);
+ return NT_STATUS_NO_MEMORY;
+ }
+ data_blob_free(&names_blob);
+ user_info_temp->password.response.lanman = lmv2_response;
+ user_info_temp->password.response.nt = ntlmv2_response;
+
+ data_blob_free(&lmv2_session_key);
+ data_blob_free(&ntlmv2_session_key);
+ } else {
+ DATA_BLOB blob = data_blob_talloc(mem_ctx, NULL, 24);
+ SMBOWFencrypt(user_info_in->password.hash.nt->hash, challenge, blob.data);
+
+ user_info_temp->password.response.nt = blob;
+ if (lp_client_lanman_auth(auth_context->lp_ctx) && user_info_in->password.hash.lanman) {
+ DATA_BLOB lm_blob = data_blob_talloc(mem_ctx, NULL, 24);
+ SMBOWFencrypt(user_info_in->password.hash.lanman->hash, challenge, blob.data);
+ user_info_temp->password.response.lanman = lm_blob;
+ } else {
+ /* if not sending the LM password, send the NT password twice */
+ user_info_temp->password.response.lanman = user_info_temp->password.response.nt;
+ }
+ }
+
+ user_info_in = user_info_temp;
+ /* fall through */
+ }
+ case AUTH_PASSWORD_RESPONSE:
+ *user_info_encrypted = user_info_in;
+ }
+ break;
+ case AUTH_PASSWORD_HASH:
+ {
+ switch (user_info_in->password_state) {
+ case AUTH_PASSWORD_PLAIN:
+ {
+ struct samr_Password lanman;
+ struct samr_Password nt;
+
+ user_info_temp = talloc(mem_ctx, struct auth_usersupplied_info);
+ if (!user_info_temp) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ if (!talloc_reference(user_info_temp, user_info_in)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ *user_info_temp = *user_info_in;
+ user_info_temp->mapped_state = to_state;
+
+ if (E_deshash(user_info_in->password.plaintext, lanman.hash)) {
+ user_info_temp->password.hash.lanman = talloc(user_info_temp,
+ struct samr_Password);
+ *user_info_temp->password.hash.lanman = lanman;
+ } else {
+ user_info_temp->password.hash.lanman = NULL;
+ }
+
+ E_md4hash(user_info_in->password.plaintext, nt.hash);
+ user_info_temp->password.hash.nt = talloc(user_info_temp,
+ struct samr_Password);
+ *user_info_temp->password.hash.nt = nt;
+
+ user_info_in = user_info_temp;
+ /* fall through */
+ }
+ case AUTH_PASSWORD_HASH:
+ *user_info_encrypted = user_info_in;
+ break;
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ break;
+ }
+ break;
+ }
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/**
+ * Squash an NT_STATUS in line with security requirements.
+ * In an attempt to avoid giving the whole game away when users
+ * are authenticating, NT replaces both NT_STATUS_NO_SUCH_USER and
+ * NT_STATUS_WRONG_PASSWORD with NT_STATUS_LOGON_FAILURE in certain situations
+ * (session setups in particular).
+ *
+ * @param nt_status NTSTATUS input for squashing.
+ * @return the 'squashed' nt_status
+ **/
+_PUBLIC_ NTSTATUS auth_nt_status_squash(NTSTATUS nt_status)
+{
+ if NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_SUCH_USER) {
+ /* Match WinXP and don't give the game away */
+ return NT_STATUS_LOGON_FAILURE;
+ } else if NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD) {
+ /* Match WinXP and don't give the game away */
+ return NT_STATUS_LOGON_FAILURE;
+ }
+
+ return nt_status;
+}
diff --git a/source4/auth/ntlm/auth_winbind.c b/source4/auth/ntlm/auth_winbind.c
new file mode 100644
index 0000000000..bf75ad9eca
--- /dev/null
+++ b/source4/auth/ntlm/auth_winbind.c
@@ -0,0 +1,282 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind authentication mechnism
+
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Andrew Bartlett 2001 - 2002
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "auth/auth.h"
+#include "auth/ntlm/auth_proto.h"
+#include "auth/auth_sam_reply.h"
+#include "nsswitch/winbind_client.h"
+#include "librpc/gen_ndr/ndr_netlogon.h"
+#include "librpc/gen_ndr/ndr_winbind.h"
+#include "lib/messaging/irpc.h"
+#include "param/param.h"
+
+static NTSTATUS get_info3_from_ndr(TALLOC_CTX *mem_ctx, struct smb_iconv_convenience *iconv_convenience, struct winbindd_response *response, struct netr_SamInfo3 *info3)
+{
+ size_t len = response->length - sizeof(struct winbindd_response);
+ if (len > 4) {
+ enum ndr_err_code ndr_err;
+ DATA_BLOB blob;
+ blob.length = len - 4;
+ blob.data = (uint8_t *)(((char *)response->extra_data.data) + 4);
+
+ ndr_err = ndr_pull_struct_blob(&blob, mem_ctx,
+ iconv_convenience, info3,
+ (ndr_pull_flags_fn_t)ndr_pull_netr_SamInfo3);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ return NT_STATUS_OK;
+ } else {
+ DEBUG(2, ("get_info3_from_ndr: No info3 struct found!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+}
+
+static NTSTATUS winbind_want_check(struct auth_method_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ const struct auth_usersupplied_info *user_info)
+{
+ if (!user_info->mapped.account_name || !*user_info->mapped.account_name) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ /* TODO: maybe limit the user scope to remote users only */
+ return NT_STATUS_OK;
+}
+
+/*
+ Authenticate a user with a challenge/response
+ using the samba3 winbind protocol
+*/
+static NTSTATUS winbind_check_password_samba3(struct auth_method_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ const struct auth_usersupplied_info *user_info,
+ struct auth_serversupplied_info **server_info)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ NSS_STATUS result;
+ NTSTATUS nt_status;
+ struct netr_SamInfo3 info3;
+
+ /* Send off request */
+ const struct auth_usersupplied_info *user_info_temp;
+ nt_status = encrypt_user_info(mem_ctx, ctx->auth_ctx,
+ AUTH_PASSWORD_RESPONSE,
+ user_info, &user_info_temp);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+ user_info = user_info_temp;
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+ request.flags = WBFLAG_PAM_INFO3_NDR;
+
+ request.data.auth_crap.logon_parameters = user_info->logon_parameters;
+
+ safe_strcpy(request.data.auth_crap.user,
+ user_info->client.account_name, sizeof(fstring));
+ safe_strcpy(request.data.auth_crap.domain,
+ user_info->client.domain_name, sizeof(fstring));
+ safe_strcpy(request.data.auth_crap.workstation,
+ user_info->workstation_name, sizeof(fstring));
+
+ memcpy(request.data.auth_crap.chal, ctx->auth_ctx->challenge.data.data, sizeof(request.data.auth_crap.chal));
+
+ request.data.auth_crap.lm_resp_len = MIN(user_info->password.response.lanman.length,
+ sizeof(request.data.auth_crap.lm_resp));
+ request.data.auth_crap.nt_resp_len = MIN(user_info->password.response.nt.length,
+ sizeof(request.data.auth_crap.nt_resp));
+
+ memcpy(request.data.auth_crap.lm_resp, user_info->password.response.lanman.data,
+ request.data.auth_crap.lm_resp_len);
+ memcpy(request.data.auth_crap.nt_resp, user_info->password.response.nt.data,
+ request.data.auth_crap.nt_resp_len);
+
+ result = winbindd_request_response(WINBINDD_PAM_AUTH_CRAP, &request, &response);
+
+ nt_status = NT_STATUS(response.data.auth.nt_status);
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+
+ if (result == NSS_STATUS_SUCCESS && response.extra_data.data) {
+ union netr_Validation validation;
+
+ nt_status = get_info3_from_ndr(mem_ctx, lp_iconv_convenience(ctx->auth_ctx->lp_ctx), &response, &info3);
+ SAFE_FREE(response.extra_data.data);
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+
+ validation.sam3 = &info3;
+ nt_status = make_server_info_netlogon_validation(mem_ctx,
+ user_info->client.account_name,
+ 3, &validation,
+ server_info);
+ return nt_status;
+ } else if (result == NSS_STATUS_SUCCESS && !response.extra_data.data) {
+ DEBUG(0, ("Winbindd authenticated the user [%s]\\[%s], "
+ "but did not include the required info3 reply!\n",
+ user_info->client.domain_name, user_info->client.account_name));
+ return NT_STATUS_INSUFFICIENT_LOGON_INFO;
+ } else if (NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(1, ("Winbindd authentication for [%s]\\[%s] failed, "
+ "but no error code is available!\n",
+ user_info->client.domain_name, user_info->client.account_name));
+ return NT_STATUS_NO_LOGON_SERVERS;
+ }
+
+ return nt_status;
+}
+
+struct winbind_check_password_state {
+ struct winbind_SamLogon req;
+};
+
+/*
+ Authenticate a user with a challenge/response
+ using IRPC to the winbind task
+*/
+static NTSTATUS winbind_check_password(struct auth_method_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ const struct auth_usersupplied_info *user_info,
+ struct auth_serversupplied_info **server_info)
+{
+ NTSTATUS status;
+ struct server_id *winbind_servers;
+ struct winbind_check_password_state *s;
+ const struct auth_usersupplied_info *user_info_new;
+ struct netr_IdentityInfo *identity_info;
+
+ s = talloc(mem_ctx, struct winbind_check_password_state);
+ NT_STATUS_HAVE_NO_MEMORY(s);
+
+ winbind_servers = irpc_servers_byname(ctx->auth_ctx->msg_ctx, s, "winbind_server");
+ if ((winbind_servers == NULL) || (winbind_servers[0].id == 0)) {
+ DEBUG(0, ("Winbind authentication for [%s]\\[%s] failed, "
+ "no winbind_server running!\n",
+ user_info->client.domain_name, user_info->client.account_name));
+ return NT_STATUS_NO_LOGON_SERVERS;
+ }
+
+ if (user_info->flags & USER_INFO_INTERACTIVE_LOGON) {
+ struct netr_PasswordInfo *password_info;
+
+ status = encrypt_user_info(s, ctx->auth_ctx, AUTH_PASSWORD_HASH,
+ user_info, &user_info_new);
+ NT_STATUS_NOT_OK_RETURN(status);
+ user_info = user_info_new;
+
+ password_info = talloc(s, struct netr_PasswordInfo);
+ NT_STATUS_HAVE_NO_MEMORY(password_info);
+
+ password_info->lmpassword = *user_info->password.hash.lanman;
+ password_info->ntpassword = *user_info->password.hash.nt;
+
+ identity_info = &password_info->identity_info;
+ s->req.in.logon_level = 1;
+ s->req.in.logon.password= password_info;
+ } else {
+ struct netr_NetworkInfo *network_info;
+ const uint8_t *challenge;
+
+ status = encrypt_user_info(s, ctx->auth_ctx, AUTH_PASSWORD_RESPONSE,
+ user_info, &user_info_new);
+ NT_STATUS_NOT_OK_RETURN(status);
+ user_info = user_info_new;
+
+ network_info = talloc(s, struct netr_NetworkInfo);
+ NT_STATUS_HAVE_NO_MEMORY(network_info);
+
+ status = auth_get_challenge(ctx->auth_ctx, &challenge);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ memcpy(network_info->challenge, challenge, sizeof(network_info->challenge));
+
+ network_info->nt.length = user_info->password.response.nt.length;
+ network_info->nt.data = user_info->password.response.nt.data;
+
+ network_info->lm.length = user_info->password.response.lanman.length;
+ network_info->lm.data = user_info->password.response.lanman.data;
+
+ identity_info = &network_info->identity_info;
+ s->req.in.logon_level = 2;
+ s->req.in.logon.network = network_info;
+ }
+
+ identity_info->domain_name.string = user_info->client.domain_name;
+ identity_info->parameter_control = user_info->logon_parameters; /* see MSV1_0_* */
+ identity_info->logon_id_low = 0;
+ identity_info->logon_id_high = 0;
+ identity_info->account_name.string = user_info->client.account_name;
+ identity_info->workstation.string = user_info->workstation_name;
+
+ s->req.in.validation_level = 3;
+
+ status = IRPC_CALL(ctx->auth_ctx->msg_ctx, winbind_servers[0],
+ winbind, WINBIND_SAMLOGON,
+ &s->req, s);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ status = make_server_info_netlogon_validation(mem_ctx,
+ user_info->client.account_name,
+ s->req.in.validation_level,
+ &s->req.out.validation,
+ server_info);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ return NT_STATUS_OK;
+}
+
+static const struct auth_operations winbind_samba3_ops = {
+ .name = "winbind_samba3",
+ .get_challenge = auth_get_challenge_not_implemented,
+ .want_check = winbind_want_check,
+ .check_password = winbind_check_password_samba3
+};
+
+static const struct auth_operations winbind_ops = {
+ .name = "winbind",
+ .get_challenge = auth_get_challenge_not_implemented,
+ .want_check = winbind_want_check,
+ .check_password = winbind_check_password
+};
+
+_PUBLIC_ NTSTATUS auth_winbind_init(void)
+{
+ NTSTATUS ret;
+
+ ret = auth_register(&winbind_samba3_ops);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("Failed to register 'winbind_samba3' auth backend!\n"));
+ return ret;
+ }
+
+ ret = auth_register(&winbind_ops);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("Failed to register 'winbind' auth backend!\n"));
+ return ret;
+ }
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/auth/ntlm/config.mk b/source4/auth/ntlm/config.mk
new file mode 100644
index 0000000000..668c528ea9
--- /dev/null
+++ b/source4/auth/ntlm/config.mk
@@ -0,0 +1,85 @@
+# NTLM auth server subsystem
+
+[SUBSYSTEM::ntlm_check]
+PRIVATE_DEPENDENCIES = LIBSAMBA-UTIL
+
+ntlm_check_OBJ_FILES = $(addprefix $(authsrcdir)/ntlm/, ntlm_check.o)
+
+#######################
+# Start MODULE auth_sam
+[MODULE::auth_sam_module]
+INIT_FUNCTION = auth_sam_init
+SUBSYSTEM = auth
+PRIVATE_DEPENDENCIES = \
+ SAMDB auth_sam ntlm_check
+# End MODULE auth_sam
+#######################
+
+auth_sam_module_OBJ_FILES = $(addprefix $(authsrcdir)/ntlm/, auth_sam.o)
+
+#######################
+# Start MODULE auth_anonymous
+[MODULE::auth_anonymous]
+INIT_FUNCTION = auth_anonymous_init
+SUBSYSTEM = auth
+# End MODULE auth_anonymous
+#######################
+
+auth_anonymous_OBJ_FILES = $(addprefix $(authsrcdir)/ntlm/, auth_anonymous.o)
+
+#######################
+# Start MODULE auth_anonymous
+[MODULE::auth_server]
+INIT_FUNCTION = auth_server_init
+SUBSYSTEM = auth
+PRIVATE_DEPENDENCIES = LIBSAMBA-UTIL LIBCLI_SMB
+# End MODULE auth_server
+#######################
+
+auth_server_OBJ_FILES = $(addprefix $(authsrcdir)/ntlm/, auth_server.o)
+
+#######################
+# Start MODULE auth_winbind
+[MODULE::auth_winbind]
+INIT_FUNCTION = auth_winbind_init
+SUBSYSTEM = auth
+PRIVATE_DEPENDENCIES = NDR_WINBIND MESSAGING LIBWINBIND-CLIENT
+# End MODULE auth_winbind
+#######################
+
+auth_winbind_OBJ_FILES = $(addprefix $(authsrcdir)/ntlm/, auth_winbind.o)
+
+#######################
+# Start MODULE auth_developer
+[MODULE::auth_developer]
+INIT_FUNCTION = auth_developer_init
+SUBSYSTEM = auth
+# End MODULE auth_developer
+#######################
+
+auth_developer_OBJ_FILES = $(addprefix $(authsrcdir)/ntlm/, auth_developer.o)
+
+[MODULE::auth_unix]
+INIT_FUNCTION = auth_unix_init
+SUBSYSTEM = auth
+PRIVATE_DEPENDENCIES = CRYPT PAM PAM_ERRORS NSS_WRAPPER
+
+auth_unix_OBJ_FILES = $(addprefix $(authsrcdir)/ntlm/, auth_unix.o)
+
+[SUBSYSTEM::PAM_ERRORS]
+
+#VERSION = 0.0.1
+#SO_VERSION = 0
+PAM_ERRORS_OBJ_FILES = $(addprefix $(authsrcdir)/ntlm/, pam_errors.o)
+
+[MODULE::auth]
+INIT_FUNCTION = server_service_auth_init
+SUBSYSTEM = service
+OUTPUT_TYPE = MERGED_OBJ
+PRIVATE_DEPENDENCIES = LIBSAMBA-UTIL LIBSECURITY SAMDB CREDENTIALS
+
+auth_OBJ_FILES = $(addprefix $(authsrcdir)/ntlm/, auth.o auth_util.o auth_simple.o)
+$(eval $(call proto_header_template,$(authsrcdir)/auth_proto.h,$(auth_OBJ_FILES:.o=.c)))
+
+# PUBLIC_HEADERS += auth/auth.h
+
diff --git a/source4/auth/ntlm/ntlm_check.c b/source4/auth/ntlm/ntlm_check.c
new file mode 100644
index 0000000000..0805b1b043
--- /dev/null
+++ b/source4/auth/ntlm/ntlm_check.c
@@ -0,0 +1,603 @@
+/*
+ Unix SMB/CIFS implementation.
+ Password and authentication handling
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-2004
+ Copyright (C) Gerald Carter 2003
+ Copyright (C) Luke Kenneth Casson Leighton 1996-2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "../lib/crypto/crypto.h"
+#include "librpc/gen_ndr/netlogon.h"
+#include "libcli/auth/libcli_auth.h"
+#include "auth/ntlm/ntlm_check.h"
+
+/****************************************************************************
+ Core of smb password checking routine.
+****************************************************************************/
+
+static bool smb_pwd_check_ntlmv1(TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *nt_response,
+ const uint8_t *part_passwd,
+ const DATA_BLOB *sec_blob,
+ DATA_BLOB *user_sess_key)
+{
+ /* Finish the encryption of part_passwd. */
+ uint8_t p24[24];
+
+ if (part_passwd == NULL) {
+ DEBUG(10,("No password set - DISALLOWING access\n"));
+ /* No password set - always false ! */
+ return false;
+ }
+
+ if (sec_blob->length != 8) {
+ DEBUG(0, ("smb_pwd_check_ntlmv1: incorrect challenge size (%lu)\n",
+ (unsigned long)sec_blob->length));
+ return false;
+ }
+
+ if (nt_response->length != 24) {
+ DEBUG(0, ("smb_pwd_check_ntlmv1: incorrect password length (%lu)\n",
+ (unsigned long)nt_response->length));
+ return false;
+ }
+
+ SMBOWFencrypt(part_passwd, sec_blob->data, p24);
+
+#if DEBUG_PASSWORD
+ DEBUG(100,("Part password (P16) was |\n"));
+ dump_data(100, part_passwd, 16);
+ DEBUGADD(100,("Password from client was |\n"));
+ dump_data(100, nt_response->data, nt_response->length);
+ DEBUGADD(100,("Given challenge was |\n"));
+ dump_data(100, sec_blob->data, sec_blob->length);
+ DEBUGADD(100,("Value from encryption was |\n"));
+ dump_data(100, p24, 24);
+#endif
+ if (memcmp(p24, nt_response->data, 24) == 0) {
+ if (user_sess_key != NULL) {
+ *user_sess_key = data_blob_talloc(mem_ctx, NULL, 16);
+ SMBsesskeygen_ntv1(part_passwd, user_sess_key->data);
+ }
+ return true;
+ }
+ return false;
+}
+
+/****************************************************************************
+ Core of smb password checking routine. (NTLMv2, LMv2)
+ Note: The same code works with both NTLMv2 and LMv2.
+****************************************************************************/
+
+static bool smb_pwd_check_ntlmv2(TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *ntv2_response,
+ const uint8_t *part_passwd,
+ const DATA_BLOB *sec_blob,
+ const char *user, const char *domain,
+ bool upper_case_domain, /* should the domain be transformed into upper case? */
+ DATA_BLOB *user_sess_key)
+{
+ /* Finish the encryption of part_passwd. */
+ uint8_t kr[16];
+ uint8_t value_from_encryption[16];
+ DATA_BLOB client_key_data;
+
+ if (part_passwd == NULL) {
+ DEBUG(10,("No password set - DISALLOWING access\n"));
+ /* No password set - always false */
+ return false;
+ }
+
+ if (sec_blob->length != 8) {
+ DEBUG(0, ("smb_pwd_check_ntlmv2: incorrect challenge size (%lu)\n",
+ (unsigned long)sec_blob->length));
+ return false;
+ }
+
+ if (ntv2_response->length < 24) {
+ /* We MUST have more than 16 bytes, or the stuff below will go
+ crazy. No known implementation sends less than the 24 bytes
+ for LMv2, let alone NTLMv2. */
+ DEBUG(0, ("smb_pwd_check_ntlmv2: incorrect password length (%lu)\n",
+ (unsigned long)ntv2_response->length));
+ return false;
+ }
+
+ client_key_data = data_blob_talloc(mem_ctx, ntv2_response->data+16, ntv2_response->length-16);
+ /*
+ todo: should we be checking this for anything? We can't for LMv2,
+ but for NTLMv2 it is meant to contain the current time etc.
+ */
+
+ if (!ntv2_owf_gen(part_passwd, user, domain, upper_case_domain, kr)) {
+ return false;
+ }
+
+ SMBOWFencrypt_ntv2(kr, sec_blob, &client_key_data, value_from_encryption);
+
+#if DEBUG_PASSWORD
+ DEBUG(100,("Part password (P16) was |\n"));
+ dump_data(100, part_passwd, 16);
+ DEBUGADD(100,("Password from client was |\n"));
+ dump_data(100, ntv2_response->data, ntv2_response->length);
+ DEBUGADD(100,("Variable data from client was |\n"));
+ dump_data(100, client_key_data.data, client_key_data.length);
+ DEBUGADD(100,("Given challenge was |\n"));
+ dump_data(100, sec_blob->data, sec_blob->length);
+ DEBUGADD(100,("Value from encryption was |\n"));
+ dump_data(100, value_from_encryption, 16);
+#endif
+ data_blob_clear_free(&client_key_data);
+ if (memcmp(value_from_encryption, ntv2_response->data, 16) == 0) {
+ if (user_sess_key != NULL) {
+ *user_sess_key = data_blob_talloc(mem_ctx, NULL, 16);
+ SMBsesskeygen_ntv2(kr, value_from_encryption, user_sess_key->data);
+ }
+ return true;
+ }
+ return false;
+}
+
+/****************************************************************************
+ Core of smb password checking routine. (NTLMv2, LMv2)
+ Note: The same code works with both NTLMv2 and LMv2.
+****************************************************************************/
+
+static bool smb_sess_key_ntlmv2(TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *ntv2_response,
+ const uint8_t *part_passwd,
+ const DATA_BLOB *sec_blob,
+ const char *user, const char *domain,
+ bool upper_case_domain, /* should the domain be transformed into upper case? */
+ DATA_BLOB *user_sess_key)
+{
+ /* Finish the encryption of part_passwd. */
+ uint8_t kr[16];
+ uint8_t value_from_encryption[16];
+ DATA_BLOB client_key_data;
+
+ if (part_passwd == NULL) {
+ DEBUG(10,("No password set - DISALLOWING access\n"));
+ /* No password set - always false */
+ return false;
+ }
+
+ if (sec_blob->length != 8) {
+ DEBUG(0, ("smb_sess_key_ntlmv2: incorrect challenge size (%lu)\n",
+ (unsigned long)sec_blob->length));
+ return false;
+ }
+
+ if (ntv2_response->length < 24) {
+ /* We MUST have more than 16 bytes, or the stuff below will go
+ crazy. No known implementation sends less than the 24 bytes
+ for LMv2, let alone NTLMv2. */
+ DEBUG(0, ("smb_sess_key_ntlmv2: incorrect password length (%lu)\n",
+ (unsigned long)ntv2_response->length));
+ return false;
+ }
+
+ client_key_data = data_blob_talloc(mem_ctx, ntv2_response->data+16, ntv2_response->length-16);
+
+ if (!ntv2_owf_gen(part_passwd, user, domain, upper_case_domain, kr)) {
+ return false;
+ }
+
+ SMBOWFencrypt_ntv2(kr, sec_blob, &client_key_data, value_from_encryption);
+ *user_sess_key = data_blob_talloc(mem_ctx, NULL, 16);
+ SMBsesskeygen_ntv2(kr, value_from_encryption, user_sess_key->data);
+ return true;
+}
+
+/**
+ * Compare password hashes against those from the SAM
+ *
+ * @param mem_ctx talloc context
+ * @param client_lanman LANMAN password hash, as supplied by the client
+ * @param client_nt NT (MD4) password hash, as supplied by the client
+ * @param username internal Samba username, for log messages
+ * @param client_username username the client used
+ * @param client_domain domain name the client used (may be mapped)
+ * @param stored_lanman LANMAN password hash, as stored on the SAM
+ * @param stored_nt NT (MD4) password hash, as stored on the SAM
+ * @param user_sess_key User session key
+ * @param lm_sess_key LM session key (first 8 bytes of the LM hash)
+ */
+
+NTSTATUS hash_password_check(TALLOC_CTX *mem_ctx,
+ bool lanman_auth,
+ const struct samr_Password *client_lanman,
+ const struct samr_Password *client_nt,
+ const char *username,
+ const struct samr_Password *stored_lanman,
+ const struct samr_Password *stored_nt)
+{
+ if (stored_nt == NULL) {
+ DEBUG(3,("ntlm_password_check: NO NT password stored for user %s.\n",
+ username));
+ }
+
+ if (client_nt && stored_nt) {
+ if (memcmp(client_nt->hash, stored_nt->hash, sizeof(stored_nt->hash)) == 0) {
+ return NT_STATUS_OK;
+ } else {
+ DEBUG(3,("ntlm_password_check: Interactive logon: NT password check failed for user %s\n",
+ username));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ } else if (client_lanman && stored_lanman) {
+ if (!lanman_auth) {
+ DEBUG(3,("ntlm_password_check: Interactive logon: only LANMAN password supplied for user %s, and LM passwords are disabled!\n",
+ username));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+ if (strchr_m(username, '@')) {
+ return NT_STATUS_NOT_FOUND;
+ }
+
+ if (memcmp(client_lanman->hash, stored_lanman->hash, sizeof(stored_lanman->hash)) == 0) {
+ return NT_STATUS_OK;
+ } else {
+ DEBUG(3,("ntlm_password_check: Interactive logon: LANMAN password check failed for user %s\n",
+ username));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+ }
+ if (strchr_m(username, '@')) {
+ return NT_STATUS_NOT_FOUND;
+ }
+ return NT_STATUS_WRONG_PASSWORD;
+}
+
+/**
+ * Check a challenge-response password against the value of the NT or
+ * LM password hash.
+ *
+ * @param mem_ctx talloc context
+ * @param challenge 8-byte challenge. If all zero, forces plaintext comparison
+ * @param nt_response 'unicode' NT response to the challenge, or unicode password
+ * @param lm_response ASCII or LANMAN response to the challenge, or password in DOS code page
+ * @param username internal Samba username, for log messages
+ * @param client_username username the client used
+ * @param client_domain domain name the client used (may be mapped)
+ * @param stored_lanman LANMAN ASCII password from our passdb or similar
+ * @param stored_nt MD4 unicode password from our passdb or similar
+ * @param user_sess_key User session key
+ * @param lm_sess_key LM session key (first 8 bytes of the LM hash)
+ */
+
+NTSTATUS ntlm_password_check(TALLOC_CTX *mem_ctx,
+ bool lanman_auth,
+ bool ntlm_auth,
+ uint32_t logon_parameters,
+ const DATA_BLOB *challenge,
+ const DATA_BLOB *lm_response,
+ const DATA_BLOB *nt_response,
+ const char *username,
+ const char *client_username,
+ const char *client_domain,
+ const struct samr_Password *stored_lanman,
+ const struct samr_Password *stored_nt,
+ DATA_BLOB *user_sess_key,
+ DATA_BLOB *lm_sess_key)
+{
+ const static uint8_t zeros[8];
+ DATA_BLOB tmp_sess_key;
+
+ if (stored_nt == NULL) {
+ DEBUG(3,("ntlm_password_check: NO NT password stored for user %s.\n",
+ username));
+ }
+
+ *lm_sess_key = data_blob(NULL, 0);
+ *user_sess_key = data_blob(NULL, 0);
+
+ /* Check for cleartext netlogon. Used by Exchange 5.5. */
+ if ((logon_parameters & MSV1_0_CLEARTEXT_PASSWORD_ALLOWED)
+ && challenge->length == sizeof(zeros)
+ && (memcmp(challenge->data, zeros, challenge->length) == 0 )) {
+ struct samr_Password client_nt;
+ struct samr_Password client_lm;
+ char *unix_pw = NULL;
+ bool lm_ok;
+
+ DEBUG(4,("ntlm_password_check: checking plaintext passwords for user %s\n",
+ username));
+ mdfour(client_nt.hash, nt_response->data, nt_response->length);
+
+ if (lm_response->length &&
+ (convert_string_talloc(mem_ctx, CH_DOS, CH_UNIX,
+ lm_response->data, lm_response->length,
+ (void **)&unix_pw, NULL, false))) {
+ if (E_deshash(unix_pw, client_lm.hash)) {
+ lm_ok = true;
+ } else {
+ lm_ok = false;
+ }
+ } else {
+ lm_ok = false;
+ }
+ return hash_password_check(mem_ctx,
+ lanman_auth,
+ lm_ok ? &client_lm : NULL,
+ nt_response->length ? &client_nt : NULL,
+ username,
+ stored_lanman, stored_nt);
+ }
+
+ if (nt_response->length != 0 && nt_response->length < 24) {
+ DEBUG(2,("ntlm_password_check: invalid NT password length (%lu) for user %s\n",
+ (unsigned long)nt_response->length, username));
+ }
+
+ if (nt_response->length > 24 && stored_nt) {
+ /* We have the NT MD4 hash challenge available - see if we can
+ use it
+ */
+ DEBUG(4,("ntlm_password_check: Checking NTLMv2 password with domain [%s]\n", client_domain));
+ if (smb_pwd_check_ntlmv2(mem_ctx,
+ nt_response,
+ stored_nt->hash, challenge,
+ client_username,
+ client_domain,
+ false,
+ user_sess_key)) {
+ *lm_sess_key = *user_sess_key;
+ if (user_sess_key->length) {
+ lm_sess_key->length = 8;
+ }
+ return NT_STATUS_OK;
+ }
+
+ DEBUG(4,("ntlm_password_check: Checking NTLMv2 password with uppercased version of domain [%s]\n", client_domain));
+ if (smb_pwd_check_ntlmv2(mem_ctx,
+ nt_response,
+ stored_nt->hash, challenge,
+ client_username,
+ client_domain,
+ true,
+ user_sess_key)) {
+ *lm_sess_key = *user_sess_key;
+ if (user_sess_key->length) {
+ lm_sess_key->length = 8;
+ }
+ return NT_STATUS_OK;
+ }
+
+ DEBUG(4,("ntlm_password_check: Checking NTLMv2 password without a domain\n"));
+ if (smb_pwd_check_ntlmv2(mem_ctx,
+ nt_response,
+ stored_nt->hash, challenge,
+ client_username,
+ "",
+ false,
+ user_sess_key)) {
+ *lm_sess_key = *user_sess_key;
+ if (user_sess_key->length) {
+ lm_sess_key->length = 8;
+ }
+ return NT_STATUS_OK;
+ } else {
+ DEBUG(3,("ntlm_password_check: NTLMv2 password check failed\n"));
+ }
+ } else if (nt_response->length == 24 && stored_nt) {
+ if (ntlm_auth) {
+ /* We have the NT MD4 hash challenge available - see if we can
+ use it (ie. does it exist in the smbpasswd file).
+ */
+ DEBUG(4,("ntlm_password_check: Checking NT MD4 password\n"));
+ if (smb_pwd_check_ntlmv1(mem_ctx,
+ nt_response,
+ stored_nt->hash, challenge,
+ user_sess_key)) {
+ /* The LM session key for this response is not very secure,
+ so use it only if we otherwise allow LM authentication */
+
+ if (lanman_auth && stored_lanman) {
+ *lm_sess_key = data_blob_talloc(mem_ctx, stored_lanman->hash, 8);
+ }
+ return NT_STATUS_OK;
+ } else {
+ DEBUG(3,("ntlm_password_check: NT MD4 password check failed for user %s\n",
+ username));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+ } else {
+ DEBUG(2,("ntlm_password_check: NTLMv1 passwords NOT PERMITTED for user %s\n",
+ username));
+ /* no return, becouse we might pick up LMv2 in the LM field */
+ }
+ }
+
+ if (lm_response->length == 0) {
+ DEBUG(3,("ntlm_password_check: NEITHER LanMan nor NT password supplied for user %s\n",
+ username));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ if (lm_response->length < 24) {
+ DEBUG(2,("ntlm_password_check: invalid LanMan password length (%lu) for user %s\n",
+ (unsigned long)nt_response->length, username));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ if (!lanman_auth) {
+ DEBUG(3,("ntlm_password_check: Lanman passwords NOT PERMITTED for user %s\n",
+ username));
+ } else if (!stored_lanman) {
+ DEBUG(3,("ntlm_password_check: NO LanMan password set for user %s (and no NT password supplied)\n",
+ username));
+ } else if (strchr_m(username, '@')) {
+ DEBUG(3,("ntlm_password_check: NO LanMan password allowed for username@realm logins (user: %s)\n",
+ username));
+ } else {
+ DEBUG(4,("ntlm_password_check: Checking LM password\n"));
+ if (smb_pwd_check_ntlmv1(mem_ctx,
+ lm_response,
+ stored_lanman->hash, challenge,
+ NULL)) {
+ /* The session key for this response is still very odd.
+ It not very secure, so use it only if we otherwise
+ allow LM authentication */
+
+ if (lanman_auth && stored_lanman) {
+ uint8_t first_8_lm_hash[16];
+ memcpy(first_8_lm_hash, stored_lanman->hash, 8);
+ memset(first_8_lm_hash + 8, '\0', 8);
+ *user_sess_key = data_blob_talloc(mem_ctx, first_8_lm_hash, 16);
+ *lm_sess_key = data_blob_talloc(mem_ctx, stored_lanman->hash, 8);
+ }
+ return NT_STATUS_OK;
+ }
+ }
+
+ if (!stored_nt) {
+ DEBUG(4,("ntlm_password_check: LM password check failed for user, no NT password %s\n",username));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /* This is for 'LMv2' authentication. almost NTLMv2 but limited to 24 bytes.
+ - related to Win9X, legacy NAS pass-though authentication
+ */
+ DEBUG(4,("ntlm_password_check: Checking LMv2 password with domain %s\n", client_domain));
+ if (smb_pwd_check_ntlmv2(mem_ctx,
+ lm_response,
+ stored_nt->hash, challenge,
+ client_username,
+ client_domain,
+ false,
+ &tmp_sess_key)) {
+ if (nt_response->length > 24) {
+ /* If NTLMv2 authentication has preceeded us
+ * (even if it failed), then use the session
+ * key from that. See the RPC-SAMLOGON
+ * torture test */
+ smb_sess_key_ntlmv2(mem_ctx,
+ nt_response,
+ stored_nt->hash, challenge,
+ client_username,
+ client_domain,
+ false,
+ user_sess_key);
+ } else {
+ /* Otherwise, use the LMv2 session key */
+ *user_sess_key = tmp_sess_key;
+ }
+ *lm_sess_key = *user_sess_key;
+ if (user_sess_key->length) {
+ lm_sess_key->length = 8;
+ }
+ return NT_STATUS_OK;
+ }
+
+ DEBUG(4,("ntlm_password_check: Checking LMv2 password with upper-cased version of domain %s\n", client_domain));
+ if (smb_pwd_check_ntlmv2(mem_ctx,
+ lm_response,
+ stored_nt->hash, challenge,
+ client_username,
+ client_domain,
+ true,
+ &tmp_sess_key)) {
+ if (nt_response->length > 24) {
+ /* If NTLMv2 authentication has preceeded us
+ * (even if it failed), then use the session
+ * key from that. See the RPC-SAMLOGON
+ * torture test */
+ smb_sess_key_ntlmv2(mem_ctx,
+ nt_response,
+ stored_nt->hash, challenge,
+ client_username,
+ client_domain,
+ true,
+ user_sess_key);
+ } else {
+ /* Otherwise, use the LMv2 session key */
+ *user_sess_key = tmp_sess_key;
+ }
+ *lm_sess_key = *user_sess_key;
+ if (user_sess_key->length) {
+ lm_sess_key->length = 8;
+ }
+ return NT_STATUS_OK;
+ }
+
+ DEBUG(4,("ntlm_password_check: Checking LMv2 password without a domain\n"));
+ if (smb_pwd_check_ntlmv2(mem_ctx,
+ lm_response,
+ stored_nt->hash, challenge,
+ client_username,
+ "",
+ false,
+ &tmp_sess_key)) {
+ if (nt_response->length > 24) {
+ /* If NTLMv2 authentication has preceeded us
+ * (even if it failed), then use the session
+ * key from that. See the RPC-SAMLOGON
+ * torture test */
+ smb_sess_key_ntlmv2(mem_ctx,
+ nt_response,
+ stored_nt->hash, challenge,
+ client_username,
+ "",
+ false,
+ user_sess_key);
+ } else {
+ /* Otherwise, use the LMv2 session key */
+ *user_sess_key = tmp_sess_key;
+ }
+ *lm_sess_key = *user_sess_key;
+ if (user_sess_key->length) {
+ lm_sess_key->length = 8;
+ }
+ return NT_STATUS_OK;
+ }
+
+ /* Apparently NT accepts NT responses in the LM field
+ - I think this is related to Win9X pass-though authentication
+ */
+ DEBUG(4,("ntlm_password_check: Checking NT MD4 password in LM field\n"));
+ if (ntlm_auth) {
+ if (smb_pwd_check_ntlmv1(mem_ctx,
+ lm_response,
+ stored_nt->hash, challenge,
+ NULL)) {
+ /* The session key for this response is still very odd.
+ It not very secure, so use it only if we otherwise
+ allow LM authentication */
+
+ if (lanman_auth && stored_lanman) {
+ uint8_t first_8_lm_hash[16];
+ memcpy(first_8_lm_hash, stored_lanman->hash, 8);
+ memset(first_8_lm_hash + 8, '\0', 8);
+ *user_sess_key = data_blob_talloc(mem_ctx, first_8_lm_hash, 16);
+ *lm_sess_key = data_blob_talloc(mem_ctx, stored_lanman->hash, 8);
+ }
+ return NT_STATUS_OK;
+ }
+ DEBUG(3,("ntlm_password_check: LM password, NT MD4 password in LM field and LMv2 failed for user %s\n",username));
+ } else {
+ DEBUG(3,("ntlm_password_check: LM password and LMv2 failed for user %s, and NT MD4 password in LM field not permitted\n",username));
+ }
+
+ /* Try and match error codes */
+ if (strchr_m(username, '@')) {
+ return NT_STATUS_NOT_FOUND;
+ }
+ return NT_STATUS_WRONG_PASSWORD;
+}
+
diff --git a/source4/auth/ntlm/ntlm_check.h b/source4/auth/ntlm/ntlm_check.h
new file mode 100644
index 0000000000..df11f7d7a2
--- /dev/null
+++ b/source4/auth/ntlm/ntlm_check.h
@@ -0,0 +1,76 @@
+/*
+ Unix SMB/CIFS implementation.
+ Password and authentication handling
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-2004
+ Copyright (C) Gerald Carter 2003
+ Copyright (C) Luke Kenneth Casson Leighton 1996-2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+/**
+ * Compare password hashes against those from the SAM
+ *
+ * @param mem_ctx talloc context
+ * @param client_lanman LANMAN password hash, as supplied by the client
+ * @param client_nt NT (MD4) password hash, as supplied by the client
+ * @param username internal Samba username, for log messages
+ * @param client_username username the client used
+ * @param client_domain domain name the client used (may be mapped)
+ * @param stored_lanman LANMAN password hash, as stored on the SAM
+ * @param stored_nt NT (MD4) password hash, as stored on the SAM
+ * @param user_sess_key User session key
+ * @param lm_sess_key LM session key (first 8 bytes of the LM hash)
+ */
+
+NTSTATUS hash_password_check(TALLOC_CTX *mem_ctx,
+ bool lanman_auth,
+ const struct samr_Password *client_lanman,
+ const struct samr_Password *client_nt,
+ const char *username,
+ const struct samr_Password *stored_lanman,
+ const struct samr_Password *stored_nt);
+
+/**
+ * Check a challenge-response password against the value of the NT or
+ * LM password hash.
+ *
+ * @param mem_ctx talloc context
+ * @param challenge 8-byte challenge. If all zero, forces plaintext comparison
+ * @param nt_response 'unicode' NT response to the challenge, or unicode password
+ * @param lm_response ASCII or LANMAN response to the challenge, or password in DOS code page
+ * @param username internal Samba username, for log messages
+ * @param client_username username the client used
+ * @param client_domain domain name the client used (may be mapped)
+ * @param stored_lanman LANMAN ASCII password from our passdb or similar
+ * @param stored_nt MD4 unicode password from our passdb or similar
+ * @param user_sess_key User session key
+ * @param lm_sess_key LM session key (first 8 bytes of the LM hash)
+ */
+
+NTSTATUS ntlm_password_check(TALLOC_CTX *mem_ctx,
+ bool lanman_auth,
+ bool ntlm_auth,
+ uint32_t logon_parameters,
+ const DATA_BLOB *challenge,
+ const DATA_BLOB *lm_response,
+ const DATA_BLOB *nt_response,
+ const char *username,
+ const char *client_username,
+ const char *client_domain,
+ const struct samr_Password *stored_lanman,
+ const struct samr_Password *stored_nt,
+ DATA_BLOB *user_sess_key,
+ DATA_BLOB *lm_sess_key);
diff --git a/source4/auth/ntlm/pam_errors.c b/source4/auth/ntlm/pam_errors.c
new file mode 100644
index 0000000000..29fa4a8133
--- /dev/null
+++ b/source4/auth/ntlm/pam_errors.c
@@ -0,0 +1,126 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * PAM error mapping functions
+ * Copyright (C) Andrew Bartlett 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "auth/ntlm/pam_errors.h"
+
+#ifdef WITH_HAVE_SECURITY_PAM_APPL_H
+#include <security/pam_appl.h>
+
+#if defined(PAM_AUTHTOK_RECOVERY_ERR) && !defined(PAM_AUTHTOK_RECOVER_ERR)
+#define PAM_AUTHTOK_RECOVER_ERR PAM_AUTHTOK_RECOVERY_ERR
+#endif
+
+/* PAM -> NT_STATUS map */
+static const struct {
+ int pam_code;
+ NTSTATUS ntstatus;
+} pam_to_nt_status_map[] = {
+ {PAM_OPEN_ERR, NT_STATUS_UNSUCCESSFUL},
+ {PAM_SYMBOL_ERR, NT_STATUS_UNSUCCESSFUL},
+ {PAM_SERVICE_ERR, NT_STATUS_UNSUCCESSFUL},
+ {PAM_SYSTEM_ERR, NT_STATUS_UNSUCCESSFUL},
+ {PAM_BUF_ERR, NT_STATUS_UNSUCCESSFUL},
+ {PAM_PERM_DENIED, NT_STATUS_ACCESS_DENIED},
+ {PAM_AUTH_ERR, NT_STATUS_WRONG_PASSWORD},
+ {PAM_CRED_INSUFFICIENT, NT_STATUS_INSUFFICIENT_LOGON_INFO}, /* FIXME: Is this correct? */
+ {PAM_AUTHINFO_UNAVAIL, NT_STATUS_LOGON_FAILURE},
+ {PAM_USER_UNKNOWN, NT_STATUS_NO_SUCH_USER},
+ {PAM_MAXTRIES, NT_STATUS_REMOTE_SESSION_LIMIT}, /* FIXME: Is this correct? */
+ {PAM_NEW_AUTHTOK_REQD, NT_STATUS_PASSWORD_MUST_CHANGE},
+ {PAM_ACCT_EXPIRED, NT_STATUS_ACCOUNT_EXPIRED},
+ {PAM_SESSION_ERR, NT_STATUS_INSUFFICIENT_RESOURCES},
+ {PAM_CRED_UNAVAIL, NT_STATUS_NO_TOKEN}, /* FIXME: Is this correct? */
+ {PAM_CRED_EXPIRED, NT_STATUS_PASSWORD_EXPIRED}, /* FIXME: Is this correct? */
+ {PAM_CRED_ERR, NT_STATUS_UNSUCCESSFUL},
+ {PAM_AUTHTOK_ERR, NT_STATUS_UNSUCCESSFUL},
+#ifdef PAM_AUTHTOK_RECOVER_ERR
+ {PAM_AUTHTOK_RECOVER_ERR, NT_STATUS_UNSUCCESSFUL},
+#endif
+ {PAM_AUTHTOK_EXPIRED, NT_STATUS_PASSWORD_EXPIRED},
+ {PAM_SUCCESS, NT_STATUS_OK}
+};
+
+/* NT_STATUS -> PAM map */
+static const struct {
+ NTSTATUS ntstatus;
+ int pam_code;
+} nt_status_to_pam_map[] = {
+ {NT_STATUS_UNSUCCESSFUL, PAM_SYSTEM_ERR},
+ {NT_STATUS_NO_SUCH_USER, PAM_USER_UNKNOWN},
+ {NT_STATUS_WRONG_PASSWORD, PAM_AUTH_ERR},
+ {NT_STATUS_LOGON_FAILURE, PAM_AUTH_ERR},
+ {NT_STATUS_ACCOUNT_EXPIRED, PAM_ACCT_EXPIRED},
+ {NT_STATUS_PASSWORD_EXPIRED, PAM_AUTHTOK_EXPIRED},
+ {NT_STATUS_PASSWORD_MUST_CHANGE, PAM_NEW_AUTHTOK_REQD},
+ {NT_STATUS_OK, PAM_SUCCESS}
+};
+
+/*****************************************************************************
+convert a PAM error to a NT status32 code
+ *****************************************************************************/
+NTSTATUS pam_to_nt_status(int pam_error)
+{
+ int i;
+ if (pam_error == 0) return NT_STATUS_OK;
+
+ for (i=0; NT_STATUS_V(pam_to_nt_status_map[i].ntstatus); i++) {
+ if (pam_error == pam_to_nt_status_map[i].pam_code)
+ return pam_to_nt_status_map[i].ntstatus;
+ }
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+/*****************************************************************************
+convert an NT status32 code to a PAM error
+ *****************************************************************************/
+int nt_status_to_pam(NTSTATUS nt_status)
+{
+ int i;
+ if NT_STATUS_IS_OK(nt_status) return PAM_SUCCESS;
+
+ for (i=0; NT_STATUS_V(nt_status_to_pam_map[i].ntstatus); i++) {
+ if (NT_STATUS_EQUAL(nt_status,nt_status_to_pam_map[i].ntstatus))
+ return nt_status_to_pam_map[i].pam_code;
+ }
+ return PAM_SYSTEM_ERR;
+}
+
+#else
+
+/*****************************************************************************
+convert a PAM error to a NT status32 code
+ *****************************************************************************/
+NTSTATUS pam_to_nt_status(int pam_error)
+{
+ if (pam_error == 0) return NT_STATUS_OK;
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+/*****************************************************************************
+convert an NT status32 code to a PAM error
+ *****************************************************************************/
+int nt_status_to_pam(NTSTATUS nt_status)
+{
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_OK)) return 0;
+ return 4; /* PAM_SYSTEM_ERR */
+}
+
+#endif
+
diff --git a/source4/auth/ntlm/pam_errors.h b/source4/auth/ntlm/pam_errors.h
new file mode 100644
index 0000000000..2dfe085b77
--- /dev/null
+++ b/source4/auth/ntlm/pam_errors.h
@@ -0,0 +1,34 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * PAM error mapping functions
+ * Copyright (C) Andrew Bartlett 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __AUTH_NTLM_PAM_ERRORS_H__
+#define __AUTH_NTLM_PAM_ERRORS_H__
+
+/*****************************************************************************
+convert a PAM error to a NT status32 code
+ *****************************************************************************/
+NTSTATUS pam_to_nt_status(int pam_error);
+
+/*****************************************************************************
+convert an NT status32 code to a PAM error
+ *****************************************************************************/
+int nt_status_to_pam(NTSTATUS nt_status);
+
+#endif /* __AUTH_NTLM_PAM_ERRORS_H__ */
+
diff --git a/source4/auth/ntlmssp/config.mk b/source4/auth/ntlmssp/config.mk
new file mode 100644
index 0000000000..c0446bcac1
--- /dev/null
+++ b/source4/auth/ntlmssp/config.mk
@@ -0,0 +1,19 @@
+[SUBSYSTEM::MSRPC_PARSE]
+
+MSRPC_PARSE_OBJ_FILES = $(addprefix $(authsrcdir)/ntlmssp/, ntlmssp_parse.o)
+
+$(eval $(call proto_header_template,$(authsrcdir)/ntlmssp/msrpc_parse.h,$(MSRPC_PARSE_OBJ_FILES:.o=.c)))
+
+################################################
+# Start MODULE gensec_ntlmssp
+[MODULE::gensec_ntlmssp]
+SUBSYSTEM = gensec
+INIT_FUNCTION = gensec_ntlmssp_init
+PRIVATE_DEPENDENCIES = MSRPC_PARSE CREDENTIALS
+OUTPUT_TYPE = MERGED_OBJ
+# End MODULE gensec_ntlmssp
+################################################
+
+gensec_ntlmssp_OBJ_FILES = $(addprefix $(authsrcdir)/ntlmssp/, ntlmssp.o ntlmssp_sign.o ntlmssp_client.o ntlmssp_server.o)
+
+$(eval $(call proto_header_template,$(authsrcdir)/ntlmssp/proto.h,$(gensec_ntlmssp_OBJ_FILES:.o=.c)))
diff --git a/source4/auth/ntlmssp/ntlmssp.c b/source4/auth/ntlmssp/ntlmssp.c
new file mode 100644
index 0000000000..c4b3a31365
--- /dev/null
+++ b/source4/auth/ntlmssp/ntlmssp.c
@@ -0,0 +1,445 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 3.0
+ handle NLTMSSP, client server side parsing
+
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "auth/ntlmssp/ntlmssp.h"
+#include "auth/ntlmssp/msrpc_parse.h"
+#include "librpc/gen_ndr/ndr_dcerpc.h"
+#include "auth/credentials/credentials.h"
+#include "auth/gensec/gensec.h"
+#include "auth/gensec/gensec_proto.h"
+#include "auth/auth.h"
+#include "auth/ntlm/auth_proto.h"
+#include "param/param.h"
+
+/**
+ * Callbacks for NTLMSSP - for both client and server operating modes
+ *
+ */
+
+static const struct ntlmssp_callbacks {
+ enum ntlmssp_role role;
+ enum ntlmssp_message_type command;
+ NTSTATUS (*sync_fn)(struct gensec_security *gensec_security,
+ TALLOC_CTX *out_mem_ctx,
+ DATA_BLOB in, DATA_BLOB *out);
+} ntlmssp_callbacks[] = {
+ {
+ .role = NTLMSSP_CLIENT,
+ .command = NTLMSSP_INITIAL,
+ .sync_fn = ntlmssp_client_initial,
+ },{
+ .role = NTLMSSP_SERVER,
+ .command = NTLMSSP_NEGOTIATE,
+ .sync_fn = ntlmssp_server_negotiate,
+ },{
+ .role = NTLMSSP_CLIENT,
+ .command = NTLMSSP_CHALLENGE,
+ .sync_fn = ntlmssp_client_challenge,
+ },{
+ .role = NTLMSSP_SERVER,
+ .command = NTLMSSP_AUTH,
+ .sync_fn = ntlmssp_server_auth,
+ }
+};
+
+
+/**
+ * Print out the NTLMSSP flags for debugging
+ * @param neg_flags The flags from the packet
+ */
+
+void debug_ntlmssp_flags(uint32_t neg_flags)
+{
+ DEBUG(3,("Got NTLMSSP neg_flags=0x%08x\n", neg_flags));
+
+ if (neg_flags & NTLMSSP_NEGOTIATE_UNICODE)
+ DEBUGADD(4, (" NTLMSSP_NEGOTIATE_UNICODE\n"));
+ if (neg_flags & NTLMSSP_NEGOTIATE_OEM)
+ DEBUGADD(4, (" NTLMSSP_NEGOTIATE_OEM\n"));
+ if (neg_flags & NTLMSSP_REQUEST_TARGET)
+ DEBUGADD(4, (" NTLMSSP_REQUEST_TARGET\n"));
+ if (neg_flags & NTLMSSP_NEGOTIATE_SIGN)
+ DEBUGADD(4, (" NTLMSSP_NEGOTIATE_SIGN\n"));
+ if (neg_flags & NTLMSSP_NEGOTIATE_SEAL)
+ DEBUGADD(4, (" NTLMSSP_NEGOTIATE_SEAL\n"));
+ if (neg_flags & NTLMSSP_NEGOTIATE_DATAGRAM_STYLE)
+ DEBUGADD(4, (" NTLMSSP_NEGOTIATE_DATAGRAM_STYLE\n"));
+ if (neg_flags & NTLMSSP_NEGOTIATE_LM_KEY)
+ DEBUGADD(4, (" NTLMSSP_NEGOTIATE_LM_KEY\n"));
+ if (neg_flags & NTLMSSP_NEGOTIATE_NETWARE)
+ DEBUGADD(4, (" NTLMSSP_NEGOTIATE_NETWARE\n"));
+ if (neg_flags & NTLMSSP_NEGOTIATE_NTLM)
+ DEBUGADD(4, (" NTLMSSP_NEGOTIATE_NTLM\n"));
+ if (neg_flags & NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED)
+ DEBUGADD(4, (" NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED\n"));
+ if (neg_flags & NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED)
+ DEBUGADD(4, (" NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED\n"));
+ if (neg_flags & NTLMSSP_NEGOTIATE_THIS_IS_LOCAL_CALL)
+ DEBUGADD(4, (" NTLMSSP_NEGOTIATE_THIS_IS_LOCAL_CALL\n"));
+ if (neg_flags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN)
+ DEBUGADD(4, (" NTLMSSP_NEGOTIATE_ALWAYS_SIGN\n"));
+ if (neg_flags & NTLMSSP_CHAL_ACCEPT_RESPONSE)
+ DEBUGADD(4, (" NTLMSSP_CHAL_ACCEPT_RESPONSE\n"));
+ if (neg_flags & NTLMSSP_CHAL_NON_NT_SESSION_KEY)
+ DEBUGADD(4, (" NTLMSSP_CHAL_NON_NT_SESSION_KEY\n"));
+ if (neg_flags & NTLMSSP_NEGOTIATE_NTLM2)
+ DEBUGADD(4, (" NTLMSSP_NEGOTIATE_NTLM2\n"));
+ if (neg_flags & NTLMSSP_CHAL_TARGET_INFO)
+ DEBUGADD(4, (" NTLMSSP_CHAL_TARGET_INFO\n"));
+ if (neg_flags & NTLMSSP_NEGOTIATE_128)
+ DEBUGADD(4, (" NTLMSSP_NEGOTIATE_128\n"));
+ if (neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH)
+ DEBUGADD(4, (" NTLMSSP_NEGOTIATE_KEY_EXCH\n"));
+ if (neg_flags & NTLMSSP_NEGOTIATE_56)
+ DEBUGADD(4, (" NTLMSSP_NEGOTIATE_56\n"));
+}
+
+static NTSTATUS gensec_ntlmssp_magic(struct gensec_security *gensec_security,
+ const DATA_BLOB *first_packet)
+{
+ if (first_packet->length > 8 && memcmp("NTLMSSP\0", first_packet->data, 8) == 0) {
+ return NT_STATUS_OK;
+ } else {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+}
+
+static NTSTATUS gensec_ntlmssp_update_find(struct gensec_ntlmssp_state *gensec_ntlmssp_state,
+ const DATA_BLOB input, uint32_t *idx)
+{
+ struct gensec_security *gensec_security = gensec_ntlmssp_state->gensec_security;
+ uint32_t ntlmssp_command;
+ uint32_t i;
+
+ if (gensec_ntlmssp_state->expected_state == NTLMSSP_DONE) {
+ /* We are strict here because other modules, which we
+ * don't fully control (such as GSSAPI) are also
+ * strict, but are tested less often */
+
+ DEBUG(1, ("Called NTLMSSP after state machine was 'done'\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!input.length) {
+ switch (gensec_ntlmssp_state->role) {
+ case NTLMSSP_CLIENT:
+ ntlmssp_command = NTLMSSP_INITIAL;
+ break;
+ case NTLMSSP_SERVER:
+ if (gensec_security->want_features & GENSEC_FEATURE_DATAGRAM_MODE) {
+ /* 'datagram' mode - no neg packet */
+ ntlmssp_command = NTLMSSP_NEGOTIATE;
+ } else {
+ /* This is normal in SPNEGO mech negotiation fallback */
+ DEBUG(2, ("Failed to parse NTLMSSP packet: zero length\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ break;
+ }
+ } else {
+ if (!msrpc_parse(gensec_ntlmssp_state,
+ &input, "Cd",
+ "NTLMSSP",
+ &ntlmssp_command)) {
+ DEBUG(1, ("Failed to parse NTLMSSP packet, could not extract NTLMSSP command\n"));
+ dump_data(2, input.data, input.length);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ }
+
+ if (ntlmssp_command != gensec_ntlmssp_state->expected_state) {
+ DEBUG(2, ("got NTLMSSP command %u, expected %u\n", ntlmssp_command, gensec_ntlmssp_state->expected_state));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ for (i=0; i < ARRAY_SIZE(ntlmssp_callbacks); i++) {
+ if (ntlmssp_callbacks[i].role == gensec_ntlmssp_state->role &&
+ ntlmssp_callbacks[i].command == ntlmssp_command) {
+ *idx = i;
+ return NT_STATUS_OK;
+ }
+ }
+
+ DEBUG(1, ("failed to find NTLMSSP callback for NTLMSSP mode %u, command %u\n",
+ gensec_ntlmssp_state->role, ntlmssp_command));
+
+ return NT_STATUS_INVALID_PARAMETER;
+}
+
+/**
+ * Next state function for the wrapped NTLMSSP state machine
+ *
+ * @param gensec_security GENSEC state, initialised to NTLMSSP
+ * @param out_mem_ctx The TALLOC_CTX for *out to be allocated on
+ * @param in The request, as a DATA_BLOB
+ * @param out The reply, as an talloc()ed DATA_BLOB, on *out_mem_ctx
+ * @return Error, MORE_PROCESSING_REQUIRED if a reply is sent,
+ * or NT_STATUS_OK if the user is authenticated.
+ */
+
+static NTSTATUS gensec_ntlmssp_update(struct gensec_security *gensec_security,
+ TALLOC_CTX *out_mem_ctx,
+ const DATA_BLOB input, DATA_BLOB *out)
+{
+ struct gensec_ntlmssp_state *gensec_ntlmssp_state = (struct gensec_ntlmssp_state *)gensec_security->private_data;
+ NTSTATUS status;
+ uint32_t i;
+
+ *out = data_blob(NULL, 0);
+
+ if (!out_mem_ctx) {
+ /* if the caller doesn't want to manage/own the memory,
+ we can put it on our context */
+ out_mem_ctx = gensec_ntlmssp_state;
+ }
+
+ status = gensec_ntlmssp_update_find(gensec_ntlmssp_state, input, &i);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ status = ntlmssp_callbacks[i].sync_fn(gensec_security, out_mem_ctx, input, out);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ return NT_STATUS_OK;
+}
+
+/**
+ * Return the NTLMSSP master session key
+ *
+ * @param gensec_ntlmssp_state NTLMSSP State
+ */
+
+NTSTATUS gensec_ntlmssp_session_key(struct gensec_security *gensec_security,
+ DATA_BLOB *session_key)
+{
+ struct gensec_ntlmssp_state *gensec_ntlmssp_state = (struct gensec_ntlmssp_state *)gensec_security->private_data;
+
+ if (gensec_ntlmssp_state->expected_state != NTLMSSP_DONE) {
+ return NT_STATUS_NO_USER_SESSION_KEY;
+ }
+
+ if (!gensec_ntlmssp_state->session_key.data) {
+ return NT_STATUS_NO_USER_SESSION_KEY;
+ }
+ *session_key = gensec_ntlmssp_state->session_key;
+
+ return NT_STATUS_OK;
+}
+
+void ntlmssp_handle_neg_flags(struct gensec_ntlmssp_state *gensec_ntlmssp_state,
+ uint32_t neg_flags, bool allow_lm)
+{
+ if (neg_flags & NTLMSSP_NEGOTIATE_UNICODE) {
+ gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_UNICODE;
+ gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_OEM;
+ gensec_ntlmssp_state->unicode = true;
+ } else {
+ gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_UNICODE;
+ gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_OEM;
+ gensec_ntlmssp_state->unicode = false;
+ }
+
+ if ((neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) && allow_lm && !gensec_ntlmssp_state->use_ntlmv2) {
+ /* other end forcing us to use LM */
+ gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_LM_KEY;
+ gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2;
+ } else {
+ gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY;
+ }
+
+ if (!(neg_flags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN)) {
+ gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_ALWAYS_SIGN;
+ }
+
+ if (!(neg_flags & NTLMSSP_NEGOTIATE_SIGN)) {
+ gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_SIGN;
+ }
+
+ if (!(neg_flags & NTLMSSP_NEGOTIATE_SEAL)) {
+ gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_SEAL;
+ }
+
+ if (!(neg_flags & NTLMSSP_NEGOTIATE_NTLM2)) {
+ gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2;
+ }
+
+ if (!(neg_flags & NTLMSSP_NEGOTIATE_128)) {
+ gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_128;
+ }
+
+ if (!(neg_flags & NTLMSSP_NEGOTIATE_56)) {
+ gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_56;
+ }
+
+ if (!(neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH)) {
+ gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_KEY_EXCH;
+ }
+
+ /* Woop Woop - unknown flag for Windows compatibility...
+ What does this really do ? JRA. */
+ if (!(neg_flags & NTLMSSP_UNKNOWN_02000000)) {
+ gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_UNKNOWN_02000000;
+ }
+
+ if ((neg_flags & NTLMSSP_REQUEST_TARGET)) {
+ gensec_ntlmssp_state->neg_flags |= NTLMSSP_REQUEST_TARGET;
+ }
+
+}
+
+/**
+ Weaken NTLMSSP keys to cope with down-level clients and servers.
+
+ We probably should have some parameters to control this, but as
+ it only occours for LM_KEY connections, and this is controlled
+ by the client lanman auth/lanman auth parameters, it isn't too bad.
+*/
+
+DATA_BLOB ntlmssp_weakend_key(struct gensec_ntlmssp_state *gensec_ntlmssp_state,
+ TALLOC_CTX *mem_ctx)
+{
+ DATA_BLOB weakened_key = data_blob_talloc(mem_ctx,
+ gensec_ntlmssp_state->session_key.data,
+ gensec_ntlmssp_state->session_key.length);
+ /* Nothing to weaken. We certainly don't want to 'extend' the length... */
+ if (weakened_key.length < 16) {
+ /* perhaps there was no key? */
+ return weakened_key;
+ }
+
+ /* Key weakening not performed on the master key for NTLM2
+ and does not occour for NTLM1. Therefore we only need
+ to do this for the LM_KEY.
+ */
+ if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) {
+ /* LM key doesn't support 128 bit crypto, so this is
+ * the best we can do. If you negotiate 128 bit, but
+ * not 56, you end up with 40 bit... */
+ if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_56) {
+ weakened_key.data[7] = 0xa0;
+ weakened_key.length = 8;
+ } else { /* forty bits */
+ weakened_key.data[5] = 0xe5;
+ weakened_key.data[6] = 0x38;
+ weakened_key.data[7] = 0xb0;
+ weakened_key.length = 8;
+ }
+ }
+ return weakened_key;
+}
+
+static bool gensec_ntlmssp_have_feature(struct gensec_security *gensec_security,
+ uint32_t feature)
+{
+ struct gensec_ntlmssp_state *gensec_ntlmssp_state = (struct gensec_ntlmssp_state *)gensec_security->private_data;
+ if (feature & GENSEC_FEATURE_SIGN) {
+ if (!gensec_ntlmssp_state->session_key.length) {
+ return false;
+ }
+ if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_SIGN) {
+ return true;
+ }
+ }
+ if (feature & GENSEC_FEATURE_SEAL) {
+ if (!gensec_ntlmssp_state->session_key.length) {
+ return false;
+ }
+ if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_SEAL) {
+ return true;
+ }
+ }
+ if (feature & GENSEC_FEATURE_SESSION_KEY) {
+ if (gensec_ntlmssp_state->session_key.length) {
+ return true;
+ }
+ }
+ if (feature & GENSEC_FEATURE_DCE_STYLE) {
+ return true;
+ }
+ if (feature & GENSEC_FEATURE_ASYNC_REPLIES) {
+ if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
+ return true;
+ }
+ }
+ return false;
+}
+
+NTSTATUS gensec_ntlmssp_start(struct gensec_security *gensec_security)
+{
+ struct gensec_ntlmssp_state *gensec_ntlmssp_state;
+
+ gensec_ntlmssp_state = talloc_zero(gensec_security, struct gensec_ntlmssp_state);
+ if (!gensec_ntlmssp_state) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ gensec_ntlmssp_state->gensec_security = gensec_security;
+ gensec_ntlmssp_state->auth_context = NULL;
+ gensec_ntlmssp_state->server_info = NULL;
+
+ gensec_security->private_data = gensec_ntlmssp_state;
+ return NT_STATUS_OK;
+}
+
+static const char *gensec_ntlmssp_oids[] = {
+ GENSEC_OID_NTLMSSP,
+ NULL
+};
+
+static const struct gensec_security_ops gensec_ntlmssp_security_ops = {
+ .name = "ntlmssp",
+ .sasl_name = "NTLM",
+ .auth_type = DCERPC_AUTH_TYPE_NTLMSSP,
+ .oid = gensec_ntlmssp_oids,
+ .client_start = gensec_ntlmssp_client_start,
+ .server_start = gensec_ntlmssp_server_start,
+ .magic = gensec_ntlmssp_magic,
+ .update = gensec_ntlmssp_update,
+ .sig_size = gensec_ntlmssp_sig_size,
+ .sign_packet = gensec_ntlmssp_sign_packet,
+ .check_packet = gensec_ntlmssp_check_packet,
+ .seal_packet = gensec_ntlmssp_seal_packet,
+ .unseal_packet = gensec_ntlmssp_unseal_packet,
+ .wrap = gensec_ntlmssp_wrap,
+ .unwrap = gensec_ntlmssp_unwrap,
+ .session_key = gensec_ntlmssp_session_key,
+ .session_info = gensec_ntlmssp_session_info,
+ .have_feature = gensec_ntlmssp_have_feature,
+ .enabled = true,
+ .priority = GENSEC_NTLMSSP
+};
+
+
+_PUBLIC_ NTSTATUS gensec_ntlmssp_init(void)
+{
+ NTSTATUS ret;
+
+ ret = gensec_register(&gensec_ntlmssp_security_ops);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("Failed to register '%s' gensec backend!\n",
+ gensec_ntlmssp_security_ops.name));
+ return ret;
+ }
+
+ return ret;
+}
diff --git a/source4/auth/ntlmssp/ntlmssp.h b/source4/auth/ntlmssp/ntlmssp.h
new file mode 100644
index 0000000000..df950e4756
--- /dev/null
+++ b/source4/auth/ntlmssp/ntlmssp.h
@@ -0,0 +1,190 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB parameters and setup
+ Copyright (C) Andrew Tridgell 1992-1997
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1997
+ Copyright (C) Paul Ashton 1997
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "librpc/gen_ndr/samr.h"
+
+/* NTLMSSP mode */
+enum ntlmssp_role
+{
+ NTLMSSP_SERVER,
+ NTLMSSP_CLIENT
+};
+
+/* NTLMSSP message types */
+enum ntlmssp_message_type
+{
+ NTLMSSP_INITIAL = 0 /* samba internal state */,
+ NTLMSSP_NEGOTIATE = 1,
+ NTLMSSP_CHALLENGE = 2,
+ NTLMSSP_AUTH = 3,
+ NTLMSSP_UNKNOWN = 4,
+ NTLMSSP_DONE = 5 /* samba final state */
+};
+
+/* NTLMSSP negotiation flags */
+#define NTLMSSP_NEGOTIATE_UNICODE 0x00000001
+#define NTLMSSP_NEGOTIATE_OEM 0x00000002
+#define NTLMSSP_REQUEST_TARGET 0x00000004
+#define NTLMSSP_NEGOTIATE_SIGN 0x00000010 /* Message integrity */
+#define NTLMSSP_NEGOTIATE_SEAL 0x00000020 /* Message confidentiality */
+#define NTLMSSP_NEGOTIATE_DATAGRAM_STYLE 0x00000040
+#define NTLMSSP_NEGOTIATE_LM_KEY 0x00000080
+#define NTLMSSP_NEGOTIATE_NETWARE 0x00000100
+#define NTLMSSP_NEGOTIATE_NTLM 0x00000200
+#define NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED 0x00001000
+#define NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED 0x00002000
+#define NTLMSSP_NEGOTIATE_THIS_IS_LOCAL_CALL 0x00004000
+#define NTLMSSP_NEGOTIATE_ALWAYS_SIGN 0x00008000
+#define NTLMSSP_TARGET_TYPE_DOMAIN 0x10000
+#define NTLMSSP_TARGET_TYPE_SERVER 0x20000
+#define NTLMSSP_CHAL_INIT_RESPONSE 0x00010000
+
+#define NTLMSSP_CHAL_ACCEPT_RESPONSE 0x00020000
+#define NTLMSSP_CHAL_NON_NT_SESSION_KEY 0x00040000
+#define NTLMSSP_NEGOTIATE_NTLM2 0x00080000
+#define NTLMSSP_CHAL_TARGET_INFO 0x00800000
+#define NTLMSSP_UNKNOWN_02000000 0x02000000
+#define NTLMSSP_NEGOTIATE_128 0x20000000 /* 128-bit encryption */
+#define NTLMSSP_NEGOTIATE_KEY_EXCH 0x40000000
+#define NTLMSSP_NEGOTIATE_56 0x80000000
+
+#define NTLMSSP_NAME_TYPE_SERVER 0x01
+#define NTLMSSP_NAME_TYPE_DOMAIN 0x02
+#define NTLMSSP_NAME_TYPE_SERVER_DNS 0x03
+#define NTLMSSP_NAME_TYPE_DOMAIN_DNS 0x04
+
+#define NTLMSSP_SIGN_VERSION 1
+
+#define NTLMSSP_SIG_SIZE 16
+
+struct gensec_ntlmssp_state
+{
+ struct gensec_security *gensec_security;
+
+ enum ntlmssp_role role;
+ enum samr_Role server_role;
+ uint32_t expected_state;
+
+ bool unicode;
+ bool use_ntlmv2;
+ bool use_nt_response; /* Set to 'False' to debug what happens when the NT response is omited */
+ bool allow_lm_key; /* The LM_KEY code is not functional at this point, and it's not
+ very secure anyway */
+
+ bool server_multiple_authentications; /* Set to 'True' to allow squid 2.5
+ style 'challenge caching' */
+
+ char *user;
+ const char *domain;
+ const char *workstation;
+ char *server_domain;
+
+ DATA_BLOB internal_chal; /* Random challenge as supplied to the client for NTLM authentication */
+
+ DATA_BLOB chal; /* Random challenge as input into the actual NTLM (or NTLM2) authentication */
+ DATA_BLOB lm_resp;
+ DATA_BLOB nt_resp;
+ DATA_BLOB session_key;
+
+ uint32_t neg_flags; /* the current state of negotiation with the NTLMSSP partner */
+
+ /* internal variables used by KEY_EXCH (client-supplied user session key */
+ DATA_BLOB encrypted_session_key;
+
+ /**
+ * Callback to get the 'challenge' used for NTLM authentication.
+ *
+ * @param ntlmssp_state This structure
+ * @return 8 bytes of challenge data, determined by the server to be the challenge for NTLM authentication
+ *
+ */
+ const uint8_t *(*get_challenge)(const struct gensec_ntlmssp_state *);
+
+ /**
+ * Callback to find if the challenge used by NTLM authentication may be modified
+ *
+ * The NTLM2 authentication scheme modifies the effective challenge, but this is not compatiable with the
+ * current 'security=server' implementation..
+ *
+ * @param ntlmssp_state This structure
+ * @return Can the challenge be set to arbitary values?
+ *
+ */
+ bool (*may_set_challenge)(const struct gensec_ntlmssp_state *);
+
+ /**
+ * Callback to set the 'challenge' used for NTLM authentication.
+ *
+ * The callback may use the void *auth_context to store state information, but the same value is always available
+ * from the DATA_BLOB chal on this structure.
+ *
+ * @param ntlmssp_state This structure
+ * @param challenge 8 bytes of data, agreed by the client and server to be the effective challenge for NTLM2 authentication
+ *
+ */
+ NTSTATUS (*set_challenge)(struct gensec_ntlmssp_state *, DATA_BLOB *challenge);
+
+ /**
+ * Callback to check the user's password.
+ *
+ * The callback must reads the feilds of this structure for the information it needs on the user
+ * @param ntlmssp_state This structure
+ * @param nt_session_key If an NT session key is returned by the authentication process, return it here
+ * @param lm_session_key If an LM session key is returned by the authentication process, return it here
+ *
+ */
+ NTSTATUS (*check_password)(struct gensec_ntlmssp_state *,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *nt_session_key, DATA_BLOB *lm_session_key);
+
+ const char *server_name;
+
+ bool doing_ntlm2;
+
+ union {
+ /* NTLM */
+ struct {
+ uint32_t seq_num;
+ struct arcfour_state *arcfour_state;
+ } ntlm;
+
+ /* NTLM2 */
+ struct {
+ uint32_t send_seq_num;
+ uint32_t recv_seq_num;
+ DATA_BLOB send_sign_key;
+ DATA_BLOB recv_sign_key;
+ struct arcfour_state *send_seal_arcfour_state;
+ struct arcfour_state *recv_seal_arcfour_state;
+
+ /* internal variables used by NTLM2 */
+ uint8_t session_nonce[16];
+ } ntlm2;
+ } crypt;
+
+ struct auth_context *auth_context;
+ struct auth_serversupplied_info *server_info;
+};
+
+struct loadparm_context;
+struct auth_session_info;
+
+#include "auth/ntlmssp/proto.h"
diff --git a/source4/auth/ntlmssp/ntlmssp_client.c b/source4/auth/ntlmssp/ntlmssp_client.c
new file mode 100644
index 0000000000..e28d8462d4
--- /dev/null
+++ b/source4/auth/ntlmssp/ntlmssp_client.c
@@ -0,0 +1,379 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 3.0
+ handle NLTMSSP, client server side parsing
+
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "auth/ntlmssp/ntlmssp.h"
+#include "auth/ntlmssp/msrpc_parse.h"
+#include "../lib/crypto/crypto.h"
+#include "libcli/auth/libcli_auth.h"
+#include "auth/credentials/credentials.h"
+#include "auth/gensec/gensec.h"
+#include "param/param.h"
+
+/*********************************************************************
+ Client side NTLMSSP
+*********************************************************************/
+
+/**
+ * Next state function for the Initial packet
+ *
+ * @param ntlmssp_state NTLMSSP State
+ * @param out_mem_ctx The DATA_BLOB *out will be allocated on this context
+ * @param in A NULL data blob (input ignored)
+ * @param out The initial negotiate request to the server, as an talloc()ed DATA_BLOB, on out_mem_ctx
+ * @return Errors or NT_STATUS_OK.
+ */
+
+NTSTATUS ntlmssp_client_initial(struct gensec_security *gensec_security,
+ TALLOC_CTX *out_mem_ctx,
+ DATA_BLOB in, DATA_BLOB *out)
+{
+ struct gensec_ntlmssp_state *gensec_ntlmssp_state = (struct gensec_ntlmssp_state *)gensec_security->private_data;
+ const char *domain = gensec_ntlmssp_state->domain;
+ const char *workstation = cli_credentials_get_workstation(gensec_security->credentials);
+
+ /* These don't really matter in the initial packet, so don't panic if they are not set */
+ if (!domain) {
+ domain = "";
+ }
+
+ if (!workstation) {
+ workstation = "";
+ }
+
+ if (gensec_ntlmssp_state->unicode) {
+ gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_UNICODE;
+ } else {
+ gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_OEM;
+ }
+
+ if (gensec_ntlmssp_state->use_ntlmv2) {
+ gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_NTLM2;
+ }
+
+ /* generate the ntlmssp negotiate packet */
+ msrpc_gen(out_mem_ctx,
+ out, "CddAA",
+ "NTLMSSP",
+ NTLMSSP_NEGOTIATE,
+ gensec_ntlmssp_state->neg_flags,
+ domain,
+ workstation);
+
+ gensec_ntlmssp_state->expected_state = NTLMSSP_CHALLENGE;
+
+ return NT_STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+/**
+ * Next state function for the Challenge Packet. Generate an auth packet.
+ *
+ * @param gensec_security GENSEC state
+ * @param out_mem_ctx Memory context for *out
+ * @param in The server challnege, as a DATA_BLOB. reply.data must be NULL
+ * @param out The next request (auth packet) to the server, as an allocated DATA_BLOB, on the out_mem_ctx context
+ * @return Errors or NT_STATUS_OK.
+ */
+
+NTSTATUS ntlmssp_client_challenge(struct gensec_security *gensec_security,
+ TALLOC_CTX *out_mem_ctx,
+ const DATA_BLOB in, DATA_BLOB *out)
+{
+ struct gensec_ntlmssp_state *gensec_ntlmssp_state = (struct gensec_ntlmssp_state *)gensec_security->private_data;
+ uint32_t chal_flags, ntlmssp_command, unkn1, unkn2;
+ DATA_BLOB server_domain_blob;
+ DATA_BLOB challenge_blob;
+ DATA_BLOB target_info = data_blob(NULL, 0);
+ char *server_domain;
+ const char *chal_parse_string;
+ const char *auth_gen_string;
+ DATA_BLOB lm_response = data_blob(NULL, 0);
+ DATA_BLOB nt_response = data_blob(NULL, 0);
+ DATA_BLOB session_key = data_blob(NULL, 0);
+ DATA_BLOB lm_session_key = data_blob(NULL, 0);
+ DATA_BLOB encrypted_session_key = data_blob(NULL, 0);
+ NTSTATUS nt_status;
+ int flags = 0;
+ const char *user, *domain;
+
+ TALLOC_CTX *mem_ctx = talloc_new(out_mem_ctx);
+ if (!mem_ctx) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!msrpc_parse(mem_ctx,
+ &in, "CdBd",
+ "NTLMSSP",
+ &ntlmssp_command,
+ &server_domain_blob,
+ &chal_flags)) {
+ DEBUG(1, ("Failed to parse the NTLMSSP Challenge: (#1)\n"));
+ dump_data(2, in.data, in.length);
+ talloc_free(mem_ctx);
+
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ data_blob_free(&server_domain_blob);
+
+ DEBUG(3, ("Got challenge flags:\n"));
+ debug_ntlmssp_flags(chal_flags);
+
+ ntlmssp_handle_neg_flags(gensec_ntlmssp_state, chal_flags, gensec_ntlmssp_state->allow_lm_key);
+
+ if (gensec_ntlmssp_state->unicode) {
+ if (chal_flags & NTLMSSP_CHAL_TARGET_INFO) {
+ chal_parse_string = "CdUdbddB";
+ } else {
+ chal_parse_string = "CdUdbdd";
+ }
+ auth_gen_string = "CdBBUUUBd";
+ } else {
+ if (chal_flags & NTLMSSP_CHAL_TARGET_INFO) {
+ chal_parse_string = "CdAdbddB";
+ } else {
+ chal_parse_string = "CdAdbdd";
+ }
+
+ auth_gen_string = "CdBBAAABd";
+ }
+
+ if (!msrpc_parse(mem_ctx,
+ &in, chal_parse_string,
+ "NTLMSSP",
+ &ntlmssp_command,
+ &server_domain,
+ &chal_flags,
+ &challenge_blob, 8,
+ &unkn1, &unkn2,
+ &target_info)) {
+ DEBUG(1, ("Failed to parse the NTLMSSP Challenge: (#2)\n"));
+ dump_data(2, in.data, in.length);
+ talloc_free(mem_ctx);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ gensec_ntlmssp_state->server_domain = server_domain;
+
+ if (challenge_blob.length != 8) {
+ talloc_free(mem_ctx);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ cli_credentials_get_ntlm_username_domain(gensec_security->credentials, mem_ctx,
+ &user, &domain);
+
+ if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
+ flags |= CLI_CRED_NTLM2;
+ }
+ if (gensec_ntlmssp_state->use_ntlmv2) {
+ flags |= CLI_CRED_NTLMv2_AUTH;
+ }
+ if (gensec_ntlmssp_state->use_nt_response) {
+ flags |= CLI_CRED_NTLM_AUTH;
+ }
+ if (lp_client_lanman_auth(gensec_security->settings->lp_ctx)) {
+ flags |= CLI_CRED_LANMAN_AUTH;
+ }
+
+ nt_status = cli_credentials_get_ntlm_response(gensec_security->credentials, mem_ctx,
+ &flags, challenge_blob, target_info,
+ &lm_response, &nt_response,
+ &lm_session_key, &session_key);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ if (!(flags & CLI_CRED_LANMAN_AUTH)) {
+ /* LM Key is still possible, just silly. Fortunetly
+ * we require command line options to end up here */
+ /* gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; */
+ }
+
+ if (!(flags & CLI_CRED_NTLM2)) {
+ /* NTLM2 is incompatible... */
+ gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2;
+ }
+
+ if ((gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_LM_KEY)
+ && lp_client_lanman_auth(gensec_security->settings->lp_ctx) && lm_session_key.length == 16) {
+ DATA_BLOB new_session_key = data_blob_talloc(mem_ctx, NULL, 16);
+ if (lm_response.length == 24) {
+ SMBsesskeygen_lm_sess_key(lm_session_key.data, lm_response.data,
+ new_session_key.data);
+ } else {
+ static const uint8_t zeros[24];
+ SMBsesskeygen_lm_sess_key(lm_session_key.data, zeros,
+ new_session_key.data);
+ }
+ session_key = new_session_key;
+ dump_data_pw("LM session key\n", session_key.data, session_key.length);
+ }
+
+
+ /* Key exchange encryptes a new client-generated session key with
+ the password-derived key */
+ if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) {
+ /* Make up a new session key */
+ uint8_t client_session_key[16];
+ generate_secret_buffer(client_session_key, sizeof(client_session_key));
+
+ /* Encrypt the new session key with the old one */
+ encrypted_session_key = data_blob_talloc(gensec_ntlmssp_state,
+ client_session_key, sizeof(client_session_key));
+ dump_data_pw("KEY_EXCH session key:\n", encrypted_session_key.data, encrypted_session_key.length);
+ arcfour_crypt(encrypted_session_key.data, session_key.data, encrypted_session_key.length);
+ dump_data_pw("KEY_EXCH session key (enc):\n", encrypted_session_key.data, encrypted_session_key.length);
+
+ /* Mark the new session key as the 'real' session key */
+ session_key = data_blob_talloc(mem_ctx, client_session_key, sizeof(client_session_key));
+ }
+
+ DEBUG(3, ("NTLMSSP: Set final flags:\n"));
+ debug_ntlmssp_flags(gensec_ntlmssp_state->neg_flags);
+
+ /* this generates the actual auth packet */
+ if (!msrpc_gen(mem_ctx,
+ out, auth_gen_string,
+ "NTLMSSP",
+ NTLMSSP_AUTH,
+ lm_response.data, lm_response.length,
+ nt_response.data, nt_response.length,
+ domain,
+ user,
+ cli_credentials_get_workstation(gensec_security->credentials),
+ encrypted_session_key.data, encrypted_session_key.length,
+ gensec_ntlmssp_state->neg_flags)) {
+ talloc_free(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ gensec_ntlmssp_state->session_key = session_key;
+ talloc_steal(gensec_ntlmssp_state, session_key.data);
+
+ talloc_steal(out_mem_ctx, out->data);
+
+ gensec_ntlmssp_state->chal = challenge_blob;
+ gensec_ntlmssp_state->lm_resp = lm_response;
+ talloc_steal(gensec_ntlmssp_state->lm_resp.data, lm_response.data);
+ gensec_ntlmssp_state->nt_resp = nt_response;
+ talloc_steal(gensec_ntlmssp_state->nt_resp.data, nt_response.data);
+
+ gensec_ntlmssp_state->expected_state = NTLMSSP_DONE;
+
+ if (gensec_security->want_features & (GENSEC_FEATURE_SIGN|GENSEC_FEATURE_SEAL)) {
+ nt_status = ntlmssp_sign_init(gensec_ntlmssp_state);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(1, ("Could not setup NTLMSSP signing/sealing system (error was: %s)\n",
+ nt_errstr(nt_status)));
+ talloc_free(mem_ctx);
+ return nt_status;
+ }
+ }
+
+ talloc_free(mem_ctx);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gensec_ntlmssp_client_start(struct gensec_security *gensec_security)
+{
+ struct gensec_ntlmssp_state *gensec_ntlmssp_state;
+ NTSTATUS nt_status;
+
+ nt_status = gensec_ntlmssp_start(gensec_security);
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+
+ gensec_ntlmssp_state = (struct gensec_ntlmssp_state *)gensec_security->private_data;
+
+ gensec_ntlmssp_state->role = NTLMSSP_CLIENT;
+
+ gensec_ntlmssp_state->domain = lp_workgroup(gensec_security->settings->lp_ctx);
+
+ gensec_ntlmssp_state->unicode = gensec_setting_bool(gensec_security->settings, "ntlmssp_client", "unicode", true);
+
+ gensec_ntlmssp_state->use_nt_response = gensec_setting_bool(gensec_security->settings, "ntlmssp_client", "send_nt_reponse", true);
+
+ gensec_ntlmssp_state->allow_lm_key = (lp_client_lanman_auth(gensec_security->settings->lp_ctx)
+ && (gensec_setting_bool(gensec_security->settings, "ntlmssp_client", "allow_lm_key", false)
+ || gensec_setting_bool(gensec_security->settings, "ntlmssp_client", "lm_key", false)));
+
+ gensec_ntlmssp_state->use_ntlmv2 = lp_client_ntlmv2_auth(gensec_security->settings->lp_ctx);
+
+ gensec_ntlmssp_state->expected_state = NTLMSSP_INITIAL;
+
+ gensec_ntlmssp_state->neg_flags =
+ NTLMSSP_NEGOTIATE_NTLM |
+ NTLMSSP_REQUEST_TARGET;
+
+ if (gensec_setting_bool(gensec_security->settings, "ntlmssp_client", "128bit", true)) {
+ gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_128;
+ }
+
+ if (gensec_setting_bool(gensec_security->settings, "ntlmssp_client", "56bit", false)) {
+ gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_56;
+ }
+
+ if (gensec_setting_bool(gensec_security->settings, "ntlmssp_client", "lm_key", false)) {
+ gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_LM_KEY;
+ }
+
+ if (gensec_setting_bool(gensec_security->settings, "ntlmssp_client", "keyexchange", true)) {
+ gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_KEY_EXCH;
+ }
+
+ if (gensec_setting_bool(gensec_security->settings, "ntlmssp_client", "alwayssign", true)) {
+ gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN;
+ }
+
+ if (gensec_setting_bool(gensec_security->settings, "ntlmssp_client", "ntlm2", true)) {
+ gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_NTLM2;
+ } else {
+ /* apparently we can't do ntlmv2 if we don't do ntlm2 */
+ gensec_ntlmssp_state->use_ntlmv2 = false;
+ }
+
+ if (gensec_security->want_features & GENSEC_FEATURE_SESSION_KEY) {
+ /*
+ * We need to set this to allow a later SetPassword
+ * via the SAMR pipe to succeed. Strange.... We could
+ * also add NTLMSSP_NEGOTIATE_SEAL here. JRA.
+ *
+ * Without this, Windows will not create the master key
+ * that it thinks is only used for NTLMSSP signing and
+ * sealing. (It is actually pulled out and used directly)
+ */
+ gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SIGN;
+ }
+ if (gensec_security->want_features & GENSEC_FEATURE_SIGN) {
+ gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SIGN;
+ }
+ if (gensec_security->want_features & GENSEC_FEATURE_SEAL) {
+ gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SEAL;
+ }
+
+ gensec_security->private_data = gensec_ntlmssp_state;
+
+ return NT_STATUS_OK;
+}
+
diff --git a/source4/auth/ntlmssp/ntlmssp_parse.c b/source4/auth/ntlmssp/ntlmssp_parse.c
new file mode 100644
index 0000000000..969845d6c5
--- /dev/null
+++ b/source4/auth/ntlmssp/ntlmssp_parse.c
@@ -0,0 +1,368 @@
+/*
+ Unix SMB/CIFS implementation.
+ simple kerberos5/SPNEGO routines
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
+ Copyright (C) Andrew Bartlett 2002-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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "auth/ntlmssp/msrpc_parse.h"
+
+/*
+ this is a tiny msrpc packet generator. I am only using this to
+ avoid tying this code to a particular varient of our rpc code. This
+ generator is not general enough for all our rpc needs, its just
+ enough for the spnego/ntlmssp code
+
+ format specifiers are:
+
+ U = unicode string (input is unix string)
+ a = address (input is char *unix_string)
+ (1 byte type, 1 byte length, unicode/ASCII string, all inline)
+ A = ASCII string (input is unix string)
+ B = data blob (pointer + length)
+ b = data blob in header (pointer + length)
+ D
+ d = word (4 bytes)
+ C = constant ascii string
+ */
+bool msrpc_gen(TALLOC_CTX *mem_ctx,
+ DATA_BLOB *blob,
+ const char *format, ...)
+{
+ int i, j;
+ bool ret;
+ va_list ap;
+ char *s;
+ uint8_t *b;
+ int head_size=0, data_size=0;
+ int head_ofs, data_ofs;
+ int *intargs;
+ size_t n;
+
+ DATA_BLOB *pointers;
+
+ pointers = talloc_array(mem_ctx, DATA_BLOB, strlen(format));
+ intargs = talloc_array(pointers, int, strlen(format));
+
+ /* first scan the format to work out the header and body size */
+ va_start(ap, format);
+ for (i=0; format[i]; i++) {
+ switch (format[i]) {
+ case 'U':
+ s = va_arg(ap, char *);
+ head_size += 8;
+ ret = push_ucs2_talloc(pointers, (smb_ucs2_t **)&pointers[i].data,
+ s, &n);
+ if (!ret) {
+ return false;
+ }
+ pointers[i].length = n;
+ pointers[i].length -= 2;
+ data_size += pointers[i].length;
+ break;
+ case 'A':
+ s = va_arg(ap, char *);
+ head_size += 8;
+ ret = push_ascii_talloc(pointers, (char **)&pointers[i].data, s,
+ &n);
+ if (!ret) {
+ return false;
+ }
+ pointers[i].length = n;
+ pointers[i].length -= 1;
+ data_size += pointers[i].length;
+ break;
+ case 'a':
+ j = va_arg(ap, int);
+ intargs[i] = j;
+ s = va_arg(ap, char *);
+ ret = push_ucs2_talloc(pointers, (smb_ucs2_t **)&pointers[i].data,
+ s, &n);
+ if (!ret) {
+ return false;
+ }
+ pointers[i].length = n;
+ pointers[i].length -= 2;
+ data_size += pointers[i].length + 4;
+ break;
+ case 'B':
+ b = va_arg(ap, uint8_t *);
+ head_size += 8;
+ pointers[i].data = b;
+ pointers[i].length = va_arg(ap, int);
+ data_size += pointers[i].length;
+ break;
+ case 'b':
+ b = va_arg(ap, uint8_t *);
+ pointers[i].data = b;
+ pointers[i].length = va_arg(ap, int);
+ head_size += pointers[i].length;
+ break;
+ case 'd':
+ j = va_arg(ap, int);
+ intargs[i] = j;
+ head_size += 4;
+ break;
+ case 'C':
+ s = va_arg(ap, char *);
+ pointers[i].data = (uint8_t *)s;
+ pointers[i].length = strlen(s)+1;
+ head_size += pointers[i].length;
+ break;
+ }
+ }
+ va_end(ap);
+
+ /* allocate the space, then scan the format again to fill in the values */
+ *blob = data_blob_talloc(mem_ctx, NULL, head_size + data_size);
+
+ head_ofs = 0;
+ data_ofs = head_size;
+
+ va_start(ap, format);
+ for (i=0; format[i]; i++) {
+ switch (format[i]) {
+ case 'U':
+ case 'A':
+ case 'B':
+ n = pointers[i].length;
+ SSVAL(blob->data, head_ofs, n); head_ofs += 2;
+ SSVAL(blob->data, head_ofs, n); head_ofs += 2;
+ SIVAL(blob->data, head_ofs, data_ofs); head_ofs += 4;
+ if (pointers[i].data && n) /* don't follow null pointers... */
+ memcpy(blob->data+data_ofs, pointers[i].data, n);
+ data_ofs += n;
+ break;
+ case 'a':
+ j = intargs[i];
+ SSVAL(blob->data, data_ofs, j); data_ofs += 2;
+
+ n = pointers[i].length;
+ SSVAL(blob->data, data_ofs, n); data_ofs += 2;
+ if (n >= 0) {
+ memcpy(blob->data+data_ofs, pointers[i].data, n);
+ }
+ data_ofs += n;
+ break;
+ case 'd':
+ j = intargs[i];
+ SIVAL(blob->data, head_ofs, j);
+ head_ofs += 4;
+ break;
+ case 'b':
+ n = pointers[i].length;
+ memcpy(blob->data + head_ofs, pointers[i].data, n);
+ head_ofs += n;
+ break;
+ case 'C':
+ n = pointers[i].length;
+ memcpy(blob->data + head_ofs, pointers[i].data, n);
+ head_ofs += n;
+ break;
+ }
+ }
+ va_end(ap);
+
+ talloc_free(pointers);
+
+ return true;
+}
+
+
+/* a helpful macro to avoid running over the end of our blob */
+#define NEED_DATA(amount) \
+if ((head_ofs + amount) > blob->length) { \
+ return false; \
+}
+
+/**
+ this is a tiny msrpc packet parser. This the the partner of msrpc_gen
+
+ format specifiers are:
+
+ U = unicode string (output is unix string)
+ A = ascii string
+ B = data blob
+ b = data blob in header
+ d = word (4 bytes)
+ C = constant ascii string
+ */
+
+bool msrpc_parse(TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *blob,
+ const char *format, ...)
+{
+ int i;
+ va_list ap;
+ const char **ps, *s;
+ DATA_BLOB *b;
+ size_t head_ofs = 0;
+ uint16_t len1, len2;
+ uint32_t ptr;
+ uint32_t *v;
+ size_t p_len = 1024;
+ char *p = talloc_array(mem_ctx, char, p_len);
+ bool ret = true;
+
+ va_start(ap, format);
+ for (i=0; format[i]; i++) {
+ switch (format[i]) {
+ case 'U':
+ NEED_DATA(8);
+ len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
+ len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
+ ptr = IVAL(blob->data, head_ofs); head_ofs += 4;
+
+ ps = (const char **)va_arg(ap, char **);
+ if (len1 == 0 && len2 == 0) {
+ *ps = "";
+ } else {
+ /* make sure its in the right format - be strict */
+ if ((len1 != len2) || (ptr + len1 < ptr) || (ptr + len1 < len1) || (ptr + len1 > blob->length)) {
+ ret = false;
+ goto cleanup;
+ }
+ if (len1 & 1) {
+ /* if odd length and unicode */
+ ret = false;
+ goto cleanup;
+ }
+ if (blob->data + ptr < (uint8_t *)(uintptr_t)ptr ||
+ blob->data + ptr < blob->data) {
+ ret = false;
+ goto cleanup;
+ }
+
+ if (0 < len1) {
+ pull_string(p, blob->data + ptr, p_len,
+ len1, STR_UNICODE|STR_NOALIGN);
+ (*ps) = talloc_strdup(mem_ctx, p);
+ if (!(*ps)) {
+ ret = false;
+ goto cleanup;
+ }
+ } else {
+ (*ps) = "";
+ }
+ }
+ break;
+ case 'A':
+ NEED_DATA(8);
+ len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
+ len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
+ ptr = IVAL(blob->data, head_ofs); head_ofs += 4;
+
+ ps = (const char **)va_arg(ap, char **);
+ /* make sure its in the right format - be strict */
+ if (len1 == 0 && len2 == 0) {
+ *ps = "";
+ } else {
+ if ((len1 != len2) || (ptr + len1 < ptr) || (ptr + len1 < len1) || (ptr + len1 > blob->length)) {
+ ret = false;
+ goto cleanup;
+ }
+
+ if (blob->data + ptr < (uint8_t *)(uintptr_t)ptr ||
+ blob->data + ptr < blob->data) {
+ ret = false;
+ goto cleanup;
+ }
+
+ if (0 < len1) {
+ pull_string(p, blob->data + ptr, p_len,
+ len1, STR_ASCII|STR_NOALIGN);
+ (*ps) = talloc_strdup(mem_ctx, p);
+ if (!(*ps)) {
+ ret = false;
+ goto cleanup;
+ }
+ } else {
+ (*ps) = "";
+ }
+ }
+ break;
+ case 'B':
+ NEED_DATA(8);
+ len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
+ len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
+ ptr = IVAL(blob->data, head_ofs); head_ofs += 4;
+
+ b = (DATA_BLOB *)va_arg(ap, void *);
+ if (len1 == 0 && len2 == 0) {
+ *b = data_blob_talloc(mem_ctx, NULL, 0);
+ } else {
+ /* make sure its in the right format - be strict */
+ if ((len1 != len2) || (ptr + len1 < ptr) || (ptr + len1 < len1) || (ptr + len1 > blob->length)) {
+ ret = false;
+ goto cleanup;
+ }
+
+ if (blob->data + ptr < (uint8_t *)(uintptr_t)ptr ||
+ blob->data + ptr < blob->data) {
+ ret = false;
+ goto cleanup;
+ }
+
+ *b = data_blob_talloc(mem_ctx, blob->data + ptr, len1);
+ }
+ break;
+ case 'b':
+ b = (DATA_BLOB *)va_arg(ap, void *);
+ len1 = va_arg(ap, uint_t);
+ /* make sure its in the right format - be strict */
+ NEED_DATA(len1);
+ if (blob->data + head_ofs < (uint8_t *)head_ofs ||
+ blob->data + head_ofs < blob->data) {
+ ret = false;
+ goto cleanup;
+ }
+
+ *b = data_blob_talloc(mem_ctx, blob->data + head_ofs, len1);
+ head_ofs += len1;
+ break;
+ case 'd':
+ v = va_arg(ap, uint32_t *);
+ NEED_DATA(4);
+ *v = IVAL(blob->data, head_ofs); head_ofs += 4;
+ break;
+ case 'C':
+ s = va_arg(ap, char *);
+
+ if (blob->data + head_ofs < (uint8_t *)head_ofs ||
+ blob->data + head_ofs < blob->data) {
+ ret = false;
+ goto cleanup;
+ }
+
+ head_ofs += pull_string(p,
+ blob->data+head_ofs, p_len,
+ blob->length - head_ofs,
+ STR_ASCII|STR_TERMINATE);
+ if (strcmp(s, p) != 0) {
+ ret = false;
+ goto cleanup;
+ }
+ break;
+ }
+ }
+
+cleanup:
+ va_end(ap);
+ talloc_free(p);
+ return ret;
+}
diff --git a/source4/auth/ntlmssp/ntlmssp_server.c b/source4/auth/ntlmssp/ntlmssp_server.c
new file mode 100644
index 0000000000..9215ccac8c
--- /dev/null
+++ b/source4/auth/ntlmssp/ntlmssp_server.c
@@ -0,0 +1,812 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 3.0
+ handle NLTMSSP, client server side parsing
+
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/network.h"
+#include "auth/ntlmssp/ntlmssp.h"
+#include "auth/ntlmssp/msrpc_parse.h"
+#include "../lib/crypto/crypto.h"
+#include "libcli/auth/libcli_auth.h"
+#include "auth/credentials/credentials.h"
+#include "auth/gensec/gensec.h"
+#include "auth/auth.h"
+#include "auth/ntlm/auth_proto.h"
+#include "param/param.h"
+#include "auth/session_proto.h"
+
+/**
+ * Set a username on an NTLMSSP context - ensures it is talloc()ed
+ *
+ */
+
+static NTSTATUS ntlmssp_set_username(struct gensec_ntlmssp_state *gensec_ntlmssp_state, const char *user)
+{
+ if (!user) {
+ /* it should be at least "" */
+ DEBUG(1, ("NTLMSSP failed to set username - cannot accept NULL username\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ gensec_ntlmssp_state->user = talloc_strdup(gensec_ntlmssp_state, user);
+ if (!gensec_ntlmssp_state->user) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ return NT_STATUS_OK;
+}
+
+/**
+ * Set a domain on an NTLMSSP context - ensures it is talloc()ed
+ *
+ */
+static NTSTATUS ntlmssp_set_domain(struct gensec_ntlmssp_state *gensec_ntlmssp_state, const char *domain)
+{
+ gensec_ntlmssp_state->domain = talloc_strdup(gensec_ntlmssp_state, domain);
+ if (!gensec_ntlmssp_state->domain) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ return NT_STATUS_OK;
+}
+
+/**
+ * Set a workstation on an NTLMSSP context - ensures it is talloc()ed
+ *
+ */
+static NTSTATUS ntlmssp_set_workstation(struct gensec_ntlmssp_state *gensec_ntlmssp_state, const char *workstation)
+{
+ gensec_ntlmssp_state->workstation = talloc_strdup(gensec_ntlmssp_state, workstation);
+ if (!gensec_ntlmssp_state->workstation) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ return NT_STATUS_OK;
+}
+
+/**
+ * Determine correct target name flags for reply, given server role
+ * and negotiated flags
+ *
+ * @param gensec_ntlmssp_state NTLMSSP State
+ * @param neg_flags The flags from the packet
+ * @param chal_flags The flags to be set in the reply packet
+ * @return The 'target name' string.
+ */
+
+static const char *ntlmssp_target_name(struct gensec_ntlmssp_state *gensec_ntlmssp_state,
+ uint32_t neg_flags, uint32_t *chal_flags)
+{
+ if (neg_flags & NTLMSSP_REQUEST_TARGET) {
+ *chal_flags |= NTLMSSP_CHAL_TARGET_INFO;
+ *chal_flags |= NTLMSSP_REQUEST_TARGET;
+ if (gensec_ntlmssp_state->server_role == ROLE_STANDALONE) {
+ *chal_flags |= NTLMSSP_TARGET_TYPE_SERVER;
+ return gensec_ntlmssp_state->server_name;
+ } else {
+ *chal_flags |= NTLMSSP_TARGET_TYPE_DOMAIN;
+ return gensec_ntlmssp_state->domain;
+ };
+ } else {
+ return "";
+ }
+}
+
+
+
+/**
+ * Next state function for the Negotiate packet
+ *
+ * @param gensec_security GENSEC state
+ * @param out_mem_ctx Memory context for *out
+ * @param in The request, as a DATA_BLOB. reply.data must be NULL
+ * @param out The reply, as an allocated DATA_BLOB, caller to free.
+ * @return Errors or MORE_PROCESSING_REQUIRED if (normal) a reply is required.
+ */
+
+NTSTATUS ntlmssp_server_negotiate(struct gensec_security *gensec_security,
+ TALLOC_CTX *out_mem_ctx,
+ const DATA_BLOB in, DATA_BLOB *out)
+{
+ struct gensec_ntlmssp_state *gensec_ntlmssp_state = (struct gensec_ntlmssp_state *)gensec_security->private_data;
+ DATA_BLOB struct_blob;
+ uint32_t neg_flags = 0;
+ uint32_t ntlmssp_command, chal_flags;
+ const uint8_t *cryptkey;
+ const char *target_name;
+
+ /* parse the NTLMSSP packet */
+#if 0
+ file_save("ntlmssp_negotiate.dat", request.data, request.length);
+#endif
+
+ if (in.length) {
+ if ((in.length < 16) || !msrpc_parse(out_mem_ctx,
+ &in, "Cdd",
+ "NTLMSSP",
+ &ntlmssp_command,
+ &neg_flags)) {
+ DEBUG(1, ("ntlmssp_server_negotiate: failed to parse "
+ "NTLMSSP Negotiate of length %u:\n",
+ (unsigned int)in.length ));
+ dump_data(2, in.data, in.length);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ debug_ntlmssp_flags(neg_flags);
+ }
+
+ ntlmssp_handle_neg_flags(gensec_ntlmssp_state, neg_flags, gensec_ntlmssp_state->allow_lm_key);
+
+ /* Ask our caller what challenge they would like in the packet */
+ cryptkey = gensec_ntlmssp_state->get_challenge(gensec_ntlmssp_state);
+ if (!cryptkey) {
+ DEBUG(1, ("ntlmssp_server_negotiate: backend doesn't give a challenge\n"));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ /* Check if we may set the challenge */
+ if (!gensec_ntlmssp_state->may_set_challenge(gensec_ntlmssp_state)) {
+ gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2;
+ }
+
+ /* The flags we send back are not just the negotiated flags,
+ * they are also 'what is in this packet'. Therfore, we
+ * operate on 'chal_flags' from here on
+ */
+
+ chal_flags = gensec_ntlmssp_state->neg_flags;
+
+ /* get the right name to fill in as 'target' */
+ target_name = ntlmssp_target_name(gensec_ntlmssp_state,
+ neg_flags, &chal_flags);
+ if (target_name == NULL)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ gensec_ntlmssp_state->chal = data_blob_talloc(gensec_ntlmssp_state, cryptkey, 8);
+ gensec_ntlmssp_state->internal_chal = data_blob_talloc(gensec_ntlmssp_state, cryptkey, 8);
+
+ /* This creates the 'blob' of names that appears at the end of the packet */
+ if (chal_flags & NTLMSSP_CHAL_TARGET_INFO) {
+ char dnsdomname[MAXHOSTNAMELEN], dnsname[MAXHOSTNAMELEN];
+ const char *target_name_dns = "";
+
+ /* Find out the DNS domain name */
+ dnsdomname[0] = '\0';
+ safe_strcpy(dnsdomname, lp_realm(gensec_security->settings->lp_ctx), sizeof(dnsdomname) - 1);
+ strlower_m(dnsdomname);
+
+ /* Find out the DNS host name */
+ safe_strcpy(dnsname, gensec_ntlmssp_state->server_name, sizeof(dnsname) - 1);
+ if (dnsdomname[0] != '\0') {
+ safe_strcat(dnsname, ".", sizeof(dnsname) - 1);
+ safe_strcat(dnsname, dnsdomname, sizeof(dnsname) - 1);
+ }
+ strlower_m(dnsname);
+
+ if (chal_flags |= NTLMSSP_TARGET_TYPE_DOMAIN) {
+ target_name_dns = dnsdomname;
+ } else if (chal_flags |= NTLMSSP_TARGET_TYPE_SERVER) {
+ target_name_dns = dnsname;
+ }
+
+ msrpc_gen(out_mem_ctx,
+ &struct_blob, "aaaaa",
+ NTLMSSP_NAME_TYPE_DOMAIN, target_name,
+ NTLMSSP_NAME_TYPE_SERVER, gensec_ntlmssp_state->server_name,
+ NTLMSSP_NAME_TYPE_DOMAIN_DNS, dnsdomname,
+ NTLMSSP_NAME_TYPE_SERVER_DNS, dnsname,
+ 0, "");
+ } else {
+ struct_blob = data_blob(NULL, 0);
+ }
+
+ {
+ /* Marshal the packet in the right format, be it unicode or ASCII */
+ const char *gen_string;
+ if (gensec_ntlmssp_state->unicode) {
+ gen_string = "CdUdbddB";
+ } else {
+ gen_string = "CdAdbddB";
+ }
+
+ msrpc_gen(out_mem_ctx,
+ out, gen_string,
+ "NTLMSSP",
+ NTLMSSP_CHALLENGE,
+ target_name,
+ chal_flags,
+ cryptkey, 8,
+ 0, 0,
+ struct_blob.data, struct_blob.length);
+ }
+
+ gensec_ntlmssp_state->expected_state = NTLMSSP_AUTH;
+
+ return NT_STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+/**
+ * Next state function for the Authenticate packet
+ *
+ * @param gensec_ntlmssp_state NTLMSSP State
+ * @param request The request, as a DATA_BLOB
+ * @return Errors or NT_STATUS_OK.
+ */
+
+static NTSTATUS ntlmssp_server_preauth(struct gensec_ntlmssp_state *gensec_ntlmssp_state,
+ const DATA_BLOB request)
+{
+ uint32_t ntlmssp_command, auth_flags;
+ NTSTATUS nt_status;
+
+ uint8_t session_nonce_hash[16];
+
+ const char *parse_string;
+ char *domain = NULL;
+ char *user = NULL;
+ char *workstation = NULL;
+
+#if 0
+ file_save("ntlmssp_auth.dat", request.data, request.length);
+#endif
+
+ if (gensec_ntlmssp_state->unicode) {
+ parse_string = "CdBBUUUBd";
+ } else {
+ parse_string = "CdBBAAABd";
+ }
+
+ /* zero these out */
+ data_blob_free(&gensec_ntlmssp_state->lm_resp);
+ data_blob_free(&gensec_ntlmssp_state->nt_resp);
+ data_blob_free(&gensec_ntlmssp_state->encrypted_session_key);
+
+ gensec_ntlmssp_state->user = NULL;
+ gensec_ntlmssp_state->domain = NULL;
+ gensec_ntlmssp_state->workstation = NULL;
+
+ /* now the NTLMSSP encoded auth hashes */
+ if (!msrpc_parse(gensec_ntlmssp_state,
+ &request, parse_string,
+ "NTLMSSP",
+ &ntlmssp_command,
+ &gensec_ntlmssp_state->lm_resp,
+ &gensec_ntlmssp_state->nt_resp,
+ &domain,
+ &user,
+ &workstation,
+ &gensec_ntlmssp_state->encrypted_session_key,
+ &auth_flags)) {
+ DEBUG(10, ("ntlmssp_server_auth: failed to parse NTLMSSP (nonfatal):\n"));
+ dump_data(10, request.data, request.length);
+
+ /* zero this out */
+ data_blob_free(&gensec_ntlmssp_state->encrypted_session_key);
+ auth_flags = 0;
+
+ /* Try again with a shorter string (Win9X truncates this packet) */
+ if (gensec_ntlmssp_state->unicode) {
+ parse_string = "CdBBUUU";
+ } else {
+ parse_string = "CdBBAAA";
+ }
+
+ /* now the NTLMSSP encoded auth hashes */
+ if (!msrpc_parse(gensec_ntlmssp_state,
+ &request, parse_string,
+ "NTLMSSP",
+ &ntlmssp_command,
+ &gensec_ntlmssp_state->lm_resp,
+ &gensec_ntlmssp_state->nt_resp,
+ &domain,
+ &user,
+ &workstation)) {
+ DEBUG(1, ("ntlmssp_server_auth: failed to parse NTLMSSP:\n"));
+ dump_data(2, request.data, request.length);
+
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ }
+
+ if (auth_flags)
+ ntlmssp_handle_neg_flags(gensec_ntlmssp_state, auth_flags, gensec_ntlmssp_state->allow_lm_key);
+
+ if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_domain(gensec_ntlmssp_state, domain))) {
+ /* zero this out */
+ data_blob_free(&gensec_ntlmssp_state->encrypted_session_key);
+ return nt_status;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_username(gensec_ntlmssp_state, user))) {
+ /* zero this out */
+ data_blob_free(&gensec_ntlmssp_state->encrypted_session_key);
+ return nt_status;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_workstation(gensec_ntlmssp_state, workstation))) {
+ /* zero this out */
+ data_blob_free(&gensec_ntlmssp_state->encrypted_session_key);
+ return nt_status;
+ }
+
+ DEBUG(3,("Got user=[%s] domain=[%s] workstation=[%s] len1=%lu len2=%lu\n",
+ gensec_ntlmssp_state->user, gensec_ntlmssp_state->domain, gensec_ntlmssp_state->workstation, (unsigned long)gensec_ntlmssp_state->lm_resp.length, (unsigned long)gensec_ntlmssp_state->nt_resp.length));
+
+#if 0
+ file_save("nthash1.dat", &gensec_ntlmssp_state->nt_resp.data, &gensec_ntlmssp_state->nt_resp.length);
+ file_save("lmhash1.dat", &gensec_ntlmssp_state->lm_resp.data, &gensec_ntlmssp_state->lm_resp.length);
+#endif
+
+ /* NTLM2 uses a 'challenge' that is made of up both the server challenge, and a
+ client challenge
+
+ However, the NTLM2 flag may still be set for the real NTLMv2 logins, be careful.
+ */
+ if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
+ if (gensec_ntlmssp_state->nt_resp.length == 24 && gensec_ntlmssp_state->lm_resp.length == 24) {
+ struct MD5Context md5_session_nonce_ctx;
+ SMB_ASSERT(gensec_ntlmssp_state->internal_chal.data
+ && gensec_ntlmssp_state->internal_chal.length == 8);
+
+ gensec_ntlmssp_state->doing_ntlm2 = true;
+
+ memcpy(gensec_ntlmssp_state->crypt.ntlm2.session_nonce, gensec_ntlmssp_state->internal_chal.data, 8);
+ memcpy(&gensec_ntlmssp_state->crypt.ntlm2.session_nonce[8], gensec_ntlmssp_state->lm_resp.data, 8);
+
+ MD5Init(&md5_session_nonce_ctx);
+ MD5Update(&md5_session_nonce_ctx, gensec_ntlmssp_state->crypt.ntlm2.session_nonce, 16);
+ MD5Final(session_nonce_hash, &md5_session_nonce_ctx);
+
+ gensec_ntlmssp_state->chal = data_blob_talloc(gensec_ntlmssp_state,
+ session_nonce_hash, 8);
+
+ /* LM response is no longer useful, zero it out */
+ data_blob_free(&gensec_ntlmssp_state->lm_resp);
+
+ /* We changed the effective challenge - set it */
+ if (!NT_STATUS_IS_OK(nt_status =
+ gensec_ntlmssp_state->set_challenge(gensec_ntlmssp_state,
+ &gensec_ntlmssp_state->chal))) {
+ /* zero this out */
+ data_blob_free(&gensec_ntlmssp_state->encrypted_session_key);
+ return nt_status;
+ }
+
+ /* LM Key is incompatible... */
+ gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY;
+ }
+ }
+ return NT_STATUS_OK;
+}
+
+/**
+ * Next state function for the Authenticate packet
+ * (after authentication - figures out the session keys etc)
+ *
+ * @param gensec_ntlmssp_state NTLMSSP State
+ * @return Errors or NT_STATUS_OK.
+ */
+
+static NTSTATUS ntlmssp_server_postauth(struct gensec_security *gensec_security,
+ DATA_BLOB *user_session_key,
+ DATA_BLOB *lm_session_key)
+{
+ struct gensec_ntlmssp_state *gensec_ntlmssp_state = (struct gensec_ntlmssp_state *)gensec_security->private_data;
+ NTSTATUS nt_status;
+ DATA_BLOB session_key = data_blob(NULL, 0);
+
+ if (user_session_key)
+ dump_data_pw("USER session key:\n", user_session_key->data, user_session_key->length);
+
+ if (lm_session_key)
+ dump_data_pw("LM first-8:\n", lm_session_key->data, lm_session_key->length);
+
+ /* Handle the different session key derivation for NTLM2 */
+ if (gensec_ntlmssp_state->doing_ntlm2) {
+ if (user_session_key && user_session_key->data && user_session_key->length == 16) {
+ session_key = data_blob_talloc(gensec_ntlmssp_state, NULL, 16);
+ hmac_md5(user_session_key->data, gensec_ntlmssp_state->crypt.ntlm2.session_nonce,
+ sizeof(gensec_ntlmssp_state->crypt.ntlm2.session_nonce), session_key.data);
+ DEBUG(10,("ntlmssp_server_auth: Created NTLM2 session key.\n"));
+ dump_data_pw("NTLM2 session key:\n", session_key.data, session_key.length);
+
+ } else {
+ DEBUG(10,("ntlmssp_server_auth: Failed to create NTLM2 session key.\n"));
+ session_key = data_blob(NULL, 0);
+ }
+ } else if ((gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_LM_KEY)
+ /* Ensure we can never get here on NTLMv2 */
+ && (gensec_ntlmssp_state->nt_resp.length == 0 || gensec_ntlmssp_state->nt_resp.length == 24)) {
+
+ if (lm_session_key && lm_session_key->data && lm_session_key->length >= 8) {
+ if (gensec_ntlmssp_state->lm_resp.data && gensec_ntlmssp_state->lm_resp.length == 24) {
+ session_key = data_blob_talloc(gensec_ntlmssp_state, NULL, 16);
+ SMBsesskeygen_lm_sess_key(lm_session_key->data, gensec_ntlmssp_state->lm_resp.data,
+ session_key.data);
+ DEBUG(10,("ntlmssp_server_auth: Created NTLM session key.\n"));
+ dump_data_pw("LM session key:\n", session_key.data, session_key.length);
+ } else {
+
+ /* When there is no LM response, just use zeros */
+ static const uint8_t zeros[24];
+ session_key = data_blob_talloc(gensec_ntlmssp_state, NULL, 16);
+ SMBsesskeygen_lm_sess_key(zeros, zeros,
+ session_key.data);
+ DEBUG(10,("ntlmssp_server_auth: Created NTLM session key.\n"));
+ dump_data_pw("LM session key:\n", session_key.data, session_key.length);
+ }
+ } else {
+ /* LM Key not selected */
+ gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY;
+
+ DEBUG(10,("ntlmssp_server_auth: Failed to create NTLM session key.\n"));
+ session_key = data_blob(NULL, 0);
+ }
+
+ } else if (user_session_key && user_session_key->data) {
+ session_key = *user_session_key;
+ DEBUG(10,("ntlmssp_server_auth: Using unmodified nt session key.\n"));
+ dump_data_pw("unmodified session key:\n", session_key.data, session_key.length);
+
+ /* LM Key not selected */
+ gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY;
+
+ } else if (lm_session_key && lm_session_key->data) {
+ /* Very weird to have LM key, but no user session key, but anyway.. */
+ session_key = *lm_session_key;
+ DEBUG(10,("ntlmssp_server_auth: Using unmodified lm session key.\n"));
+ dump_data_pw("unmodified session key:\n", session_key.data, session_key.length);
+
+ /* LM Key not selected */
+ gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY;
+
+ } else {
+ DEBUG(10,("ntlmssp_server_auth: Failed to create unmodified session key.\n"));
+ session_key = data_blob(NULL, 0);
+
+ /* LM Key not selected */
+ gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY;
+ }
+
+ /* With KEY_EXCH, the client supplies the proposed session key,
+ but encrypts it with the long-term key */
+ if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) {
+ if (!gensec_ntlmssp_state->encrypted_session_key.data
+ || gensec_ntlmssp_state->encrypted_session_key.length != 16) {
+ data_blob_free(&gensec_ntlmssp_state->encrypted_session_key);
+ DEBUG(1, ("Client-supplied KEY_EXCH session key was of invalid length (%u)!\n",
+ (unsigned)gensec_ntlmssp_state->encrypted_session_key.length));
+ return NT_STATUS_INVALID_PARAMETER;
+ } else if (!session_key.data || session_key.length != 16) {
+ DEBUG(5, ("server session key is invalid (len == %u), cannot do KEY_EXCH!\n",
+ (unsigned)session_key.length));
+ gensec_ntlmssp_state->session_key = session_key;
+ } else {
+ dump_data_pw("KEY_EXCH session key (enc):\n",
+ gensec_ntlmssp_state->encrypted_session_key.data,
+ gensec_ntlmssp_state->encrypted_session_key.length);
+ arcfour_crypt(gensec_ntlmssp_state->encrypted_session_key.data,
+ session_key.data,
+ gensec_ntlmssp_state->encrypted_session_key.length);
+ gensec_ntlmssp_state->session_key = data_blob_talloc(gensec_ntlmssp_state,
+ gensec_ntlmssp_state->encrypted_session_key.data,
+ gensec_ntlmssp_state->encrypted_session_key.length);
+ dump_data_pw("KEY_EXCH session key:\n", gensec_ntlmssp_state->encrypted_session_key.data,
+ gensec_ntlmssp_state->encrypted_session_key.length);
+ }
+ } else {
+ gensec_ntlmssp_state->session_key = session_key;
+ }
+
+ /* keep the session key around on the new context */
+ talloc_steal(gensec_ntlmssp_state, session_key.data);
+
+ if ((gensec_security->want_features & GENSEC_FEATURE_SIGN)
+ || (gensec_security->want_features & GENSEC_FEATURE_SEAL)) {
+ nt_status = ntlmssp_sign_init(gensec_ntlmssp_state);
+ } else {
+ nt_status = NT_STATUS_OK;
+ }
+
+ data_blob_free(&gensec_ntlmssp_state->encrypted_session_key);
+
+ /* allow arbitarily many authentications, but watch that this will cause a
+ memory leak, until the gensec_ntlmssp_state is shutdown
+ */
+
+ if (gensec_ntlmssp_state->server_multiple_authentications) {
+ gensec_ntlmssp_state->expected_state = NTLMSSP_AUTH;
+ } else {
+ gensec_ntlmssp_state->expected_state = NTLMSSP_DONE;
+ }
+
+ return nt_status;
+}
+
+
+/**
+ * Next state function for the Authenticate packet
+ *
+ * @param gensec_security GENSEC state
+ * @param out_mem_ctx Memory context for *out
+ * @param in The request, as a DATA_BLOB. reply.data must be NULL
+ * @param out The reply, as an allocated DATA_BLOB, caller to free.
+ * @return Errors or NT_STATUS_OK if authentication sucessful
+ */
+
+NTSTATUS ntlmssp_server_auth(struct gensec_security *gensec_security,
+ TALLOC_CTX *out_mem_ctx,
+ const DATA_BLOB in, DATA_BLOB *out)
+{
+ struct gensec_ntlmssp_state *gensec_ntlmssp_state = (struct gensec_ntlmssp_state *)gensec_security->private_data;
+ DATA_BLOB user_session_key = data_blob(NULL, 0);
+ DATA_BLOB lm_session_key = data_blob(NULL, 0);
+ NTSTATUS nt_status;
+
+ TALLOC_CTX *mem_ctx = talloc_new(out_mem_ctx);
+ if (!mem_ctx) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* zero the outbound NTLMSSP packet */
+ *out = data_blob_talloc(out_mem_ctx, NULL, 0);
+
+ if (!NT_STATUS_IS_OK(nt_status = ntlmssp_server_preauth(gensec_ntlmssp_state, in))) {
+ talloc_free(mem_ctx);
+ return nt_status;
+ }
+
+ /*
+ * Note we don't check here for NTLMv2 auth settings. If NTLMv2 auth
+ * is required (by "ntlm auth = no" and "lm auth = no" being set in the
+ * smb.conf file) and no NTLMv2 response was sent then the password check
+ * will fail here. JRA.
+ */
+
+ /* Finally, actually ask if the password is OK */
+
+ if (!NT_STATUS_IS_OK(nt_status = gensec_ntlmssp_state->check_password(gensec_ntlmssp_state, mem_ctx,
+ &user_session_key, &lm_session_key))) {
+ talloc_free(mem_ctx);
+ return nt_status;
+ }
+
+ if (gensec_security->want_features
+ & (GENSEC_FEATURE_SIGN|GENSEC_FEATURE_SEAL|GENSEC_FEATURE_SESSION_KEY)) {
+ nt_status = ntlmssp_server_postauth(gensec_security, &user_session_key, &lm_session_key);
+ talloc_free(mem_ctx);
+ return nt_status;
+ } else {
+ gensec_ntlmssp_state->session_key = data_blob(NULL, 0);
+ talloc_free(mem_ctx);
+ return NT_STATUS_OK;
+ }
+}
+
+/**
+ * Return the challenge as determined by the authentication subsystem
+ * @return an 8 byte random challenge
+ */
+
+static const uint8_t *auth_ntlmssp_get_challenge(const struct gensec_ntlmssp_state *gensec_ntlmssp_state)
+{
+ NTSTATUS status;
+ const uint8_t *chal;
+
+ status = gensec_ntlmssp_state->auth_context->get_challenge(gensec_ntlmssp_state->auth_context, &chal);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("auth_ntlmssp_get_challenge: failed to get challenge: %s\n",
+ nt_errstr(status)));
+ return NULL;
+ }
+
+ return chal;
+}
+
+/**
+ * Some authentication methods 'fix' the challenge, so we may not be able to set it
+ *
+ * @return If the effective challenge used by the auth subsystem may be modified
+ */
+static bool auth_ntlmssp_may_set_challenge(const struct gensec_ntlmssp_state *gensec_ntlmssp_state)
+{
+ return gensec_ntlmssp_state->auth_context->challenge_may_be_modified(gensec_ntlmssp_state->auth_context);
+}
+
+/**
+ * NTLM2 authentication modifies the effective challenge,
+ * @param challenge The new challenge value
+ */
+static NTSTATUS auth_ntlmssp_set_challenge(struct gensec_ntlmssp_state *gensec_ntlmssp_state, DATA_BLOB *challenge)
+{
+ NTSTATUS nt_status;
+ struct auth_context *auth_context = gensec_ntlmssp_state->auth_context;
+ const uint8_t *chal;
+
+ if (challenge->length != 8) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ chal = challenge->data;
+
+ nt_status = gensec_ntlmssp_state->auth_context->set_challenge(auth_context,
+ chal,
+ "NTLMSSP callback (NTLM2)");
+
+ return nt_status;
+}
+
+/**
+ * Check the password on an NTLMSSP login.
+ *
+ * Return the session keys used on the connection.
+ */
+
+static NTSTATUS auth_ntlmssp_check_password(struct gensec_ntlmssp_state *gensec_ntlmssp_state,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *user_session_key, DATA_BLOB *lm_session_key)
+{
+ NTSTATUS nt_status;
+ struct auth_usersupplied_info *user_info = talloc(mem_ctx, struct auth_usersupplied_info);
+ if (!user_info) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ user_info->logon_parameters = MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT | MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT;
+ user_info->flags = 0;
+ user_info->mapped_state = false;
+ user_info->client.account_name = gensec_ntlmssp_state->user;
+ user_info->client.domain_name = gensec_ntlmssp_state->domain;
+ user_info->workstation_name = gensec_ntlmssp_state->workstation;
+ user_info->remote_host = gensec_get_peer_addr(gensec_ntlmssp_state->gensec_security);
+
+ user_info->password_state = AUTH_PASSWORD_RESPONSE;
+ user_info->password.response.lanman = gensec_ntlmssp_state->lm_resp;
+ user_info->password.response.lanman.data = talloc_steal(user_info, gensec_ntlmssp_state->lm_resp.data);
+ user_info->password.response.nt = gensec_ntlmssp_state->nt_resp;
+ user_info->password.response.nt.data = talloc_steal(user_info, gensec_ntlmssp_state->nt_resp.data);
+
+ nt_status = gensec_ntlmssp_state->auth_context->check_password(gensec_ntlmssp_state->auth_context,
+ mem_ctx,
+ user_info,
+ &gensec_ntlmssp_state->server_info);
+ talloc_free(user_info);
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+
+ talloc_steal(gensec_ntlmssp_state, gensec_ntlmssp_state->server_info);
+
+ if (gensec_ntlmssp_state->server_info->user_session_key.length) {
+ DEBUG(10, ("Got NT session key of length %u\n",
+ (unsigned)gensec_ntlmssp_state->server_info->user_session_key.length));
+ if (!talloc_reference(mem_ctx, gensec_ntlmssp_state->server_info->user_session_key.data)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ *user_session_key = gensec_ntlmssp_state->server_info->user_session_key;
+ }
+ if (gensec_ntlmssp_state->server_info->lm_session_key.length) {
+ DEBUG(10, ("Got LM session key of length %u\n",
+ (unsigned)gensec_ntlmssp_state->server_info->lm_session_key.length));
+ if (!talloc_reference(mem_ctx, gensec_ntlmssp_state->server_info->lm_session_key.data)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ *lm_session_key = gensec_ntlmssp_state->server_info->lm_session_key;
+ }
+ return nt_status;
+}
+
+/**
+ * Return the credentials of a logged on user, including session keys
+ * etc.
+ *
+ * Only valid after a successful authentication
+ *
+ * May only be called once per authentication.
+ *
+ */
+
+NTSTATUS gensec_ntlmssp_session_info(struct gensec_security *gensec_security,
+ struct auth_session_info **session_info)
+{
+ NTSTATUS nt_status;
+ struct gensec_ntlmssp_state *gensec_ntlmssp_state = (struct gensec_ntlmssp_state *)gensec_security->private_data;
+
+ nt_status = auth_generate_session_info(gensec_ntlmssp_state, gensec_security->event_ctx, gensec_security->settings->lp_ctx, gensec_ntlmssp_state->server_info, session_info);
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+
+ (*session_info)->session_key = data_blob_talloc(*session_info,
+ gensec_ntlmssp_state->session_key.data,
+ gensec_ntlmssp_state->session_key.length);
+
+ return NT_STATUS_OK;
+}
+
+/**
+ * Start NTLMSSP on the server side
+ *
+ */
+NTSTATUS gensec_ntlmssp_server_start(struct gensec_security *gensec_security)
+{
+ NTSTATUS nt_status;
+ struct gensec_ntlmssp_state *gensec_ntlmssp_state;
+
+ nt_status = gensec_ntlmssp_start(gensec_security);
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+
+ gensec_ntlmssp_state = (struct gensec_ntlmssp_state *)gensec_security->private_data;
+
+ gensec_ntlmssp_state->role = NTLMSSP_SERVER;
+
+ gensec_ntlmssp_state->workstation = NULL;
+ gensec_ntlmssp_state->server_name = lp_netbios_name(gensec_security->settings->lp_ctx);
+
+ gensec_ntlmssp_state->domain = lp_workgroup(gensec_security->settings->lp_ctx);
+
+ gensec_ntlmssp_state->expected_state = NTLMSSP_NEGOTIATE;
+
+ gensec_ntlmssp_state->allow_lm_key = (lp_lanman_auth(gensec_security->settings->lp_ctx)
+ && gensec_setting_bool(gensec_security->settings, "ntlmssp_server", "allow_lm_key", false));
+
+ gensec_ntlmssp_state->server_multiple_authentications = false;
+
+ gensec_ntlmssp_state->neg_flags =
+ NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_UNKNOWN_02000000;
+
+ gensec_ntlmssp_state->lm_resp = data_blob(NULL, 0);
+ gensec_ntlmssp_state->nt_resp = data_blob(NULL, 0);
+ gensec_ntlmssp_state->encrypted_session_key = data_blob(NULL, 0);
+
+ if (gensec_setting_bool(gensec_security->settings, "ntlmssp_server", "128bit", true)) {
+ gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_128;
+ }
+
+ if (gensec_setting_bool(gensec_security->settings, "ntlmssp_server", "56bit", true)) {
+ gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_56;
+ }
+
+ if (gensec_setting_bool(gensec_security->settings, "ntlmssp_server", "keyexchange", true)) {
+ gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_KEY_EXCH;
+ }
+
+ if (gensec_setting_bool(gensec_security->settings, "ntlmssp_server", "alwayssign", true)) {
+ gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN;
+ }
+
+ if (gensec_setting_bool(gensec_security->settings, "ntlmssp_server", "ntlm2", true)) {
+ gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_NTLM2;
+ }
+
+ if (gensec_security->want_features & GENSEC_FEATURE_SIGN) {
+ gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SIGN;
+ }
+ if (gensec_security->want_features & GENSEC_FEATURE_SEAL) {
+ gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SEAL;
+ }
+
+ gensec_ntlmssp_state->auth_context = gensec_security->auth_context;
+
+ gensec_ntlmssp_state->get_challenge = auth_ntlmssp_get_challenge;
+ gensec_ntlmssp_state->may_set_challenge = auth_ntlmssp_may_set_challenge;
+ gensec_ntlmssp_state->set_challenge = auth_ntlmssp_set_challenge;
+ gensec_ntlmssp_state->check_password = auth_ntlmssp_check_password;
+ gensec_ntlmssp_state->server_role = lp_server_role(gensec_security->settings->lp_ctx);
+
+ return NT_STATUS_OK;
+}
+
diff --git a/source4/auth/ntlmssp/ntlmssp_sign.c b/source4/auth/ntlmssp/ntlmssp_sign.c
new file mode 100644
index 0000000000..3a07d7e7bd
--- /dev/null
+++ b/source4/auth/ntlmssp/ntlmssp_sign.c
@@ -0,0 +1,545 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Version 3.0
+ * NTLMSSP Signing routines
+ * Copyright (C) Luke Kenneth Casson Leighton 1996-2001
+ * Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003-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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "auth/auth.h"
+#include "auth/ntlmssp/ntlmssp.h"
+#include "auth/ntlmssp/msrpc_parse.h"
+#include "../lib/crypto/crypto.h"
+#include "auth/credentials/credentials.h"
+#include "auth/gensec/gensec.h"
+
+#define CLI_SIGN "session key to client-to-server signing key magic constant"
+#define CLI_SEAL "session key to client-to-server sealing key magic constant"
+#define SRV_SIGN "session key to server-to-client signing key magic constant"
+#define SRV_SEAL "session key to server-to-client sealing key magic constant"
+
+/**
+ * Some notes on the NTLM2 code:
+ *
+ * NTLM2 is a AEAD system. This means that the data encrypted is not
+ * all the data that is signed. In DCE-RPC case, the headers of the
+ * DCE-RPC packets are also signed. This prevents some of the
+ * fun-and-games one might have by changing them.
+ *
+ */
+
+static void calc_ntlmv2_key(TALLOC_CTX *mem_ctx,
+ DATA_BLOB *subkey,
+ DATA_BLOB session_key,
+ const char *constant)
+{
+ struct MD5Context ctx3;
+ *subkey = data_blob_talloc(mem_ctx, NULL, 16);
+ MD5Init(&ctx3);
+ MD5Update(&ctx3, session_key.data, session_key.length);
+ MD5Update(&ctx3, (const uint8_t *)constant, strlen(constant)+1);
+ MD5Final(subkey->data, &ctx3);
+}
+
+enum ntlmssp_direction {
+ NTLMSSP_SEND,
+ NTLMSSP_RECEIVE
+};
+
+static NTSTATUS ntlmssp_make_packet_signature(struct gensec_ntlmssp_state *gensec_ntlmssp_state,
+ TALLOC_CTX *sig_mem_ctx,
+ const uint8_t *data, size_t length,
+ const uint8_t *whole_pdu, size_t pdu_length,
+ enum ntlmssp_direction direction,
+ DATA_BLOB *sig, bool encrypt_sig)
+{
+ if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
+
+ HMACMD5Context ctx;
+ uint8_t digest[16];
+ uint8_t seq_num[4];
+
+ *sig = data_blob_talloc(sig_mem_ctx, NULL, NTLMSSP_SIG_SIZE);
+ if (!sig->data) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ switch (direction) {
+ case NTLMSSP_SEND:
+ SIVAL(seq_num, 0, gensec_ntlmssp_state->crypt.ntlm2.send_seq_num);
+ gensec_ntlmssp_state->crypt.ntlm2.send_seq_num++;
+ hmac_md5_init_limK_to_64(gensec_ntlmssp_state->crypt.ntlm2.send_sign_key.data,
+ gensec_ntlmssp_state->crypt.ntlm2.send_sign_key.length, &ctx);
+ break;
+ case NTLMSSP_RECEIVE:
+ SIVAL(seq_num, 0, gensec_ntlmssp_state->crypt.ntlm2.recv_seq_num);
+ gensec_ntlmssp_state->crypt.ntlm2.recv_seq_num++;
+ hmac_md5_init_limK_to_64(gensec_ntlmssp_state->crypt.ntlm2.recv_sign_key.data,
+ gensec_ntlmssp_state->crypt.ntlm2.recv_sign_key.length, &ctx);
+ break;
+ }
+ hmac_md5_update(seq_num, sizeof(seq_num), &ctx);
+ hmac_md5_update(whole_pdu, pdu_length, &ctx);
+ hmac_md5_final(digest, &ctx);
+
+ if (encrypt_sig && gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) {
+ switch (direction) {
+ case NTLMSSP_SEND:
+ arcfour_crypt_sbox(gensec_ntlmssp_state->crypt.ntlm2.send_seal_arcfour_state, digest, 8);
+ break;
+ case NTLMSSP_RECEIVE:
+ arcfour_crypt_sbox(gensec_ntlmssp_state->crypt.ntlm2.recv_seal_arcfour_state, digest, 8);
+ break;
+ }
+ }
+
+ SIVAL(sig->data, 0, NTLMSSP_SIGN_VERSION);
+ memcpy(sig->data + 4, digest, 8);
+ memcpy(sig->data + 12, seq_num, 4);
+
+ DEBUG(10, ("NTLM2: created signature over %llu bytes of input:\n", (unsigned long long)pdu_length));
+ dump_data(11, sig->data, sig->length);
+
+ } else {
+ uint32_t crc;
+ crc = crc32_calc_buffer(data, length);
+ if (!msrpc_gen(sig_mem_ctx,
+ sig, "dddd", NTLMSSP_SIGN_VERSION, 0, crc, gensec_ntlmssp_state->crypt.ntlm.seq_num)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ gensec_ntlmssp_state->crypt.ntlm.seq_num++;
+
+ arcfour_crypt_sbox(gensec_ntlmssp_state->crypt.ntlm.arcfour_state, sig->data+4, sig->length-4);
+
+ DEBUG(10, ("NTLM1: created signature over %llu bytes of input:\n", (unsigned long long)length));
+ dump_data(11, sig->data, sig->length);
+ }
+ return NT_STATUS_OK;
+}
+
+/* TODO: make this non-public */
+NTSTATUS gensec_ntlmssp_sign_packet(struct gensec_security *gensec_security,
+ TALLOC_CTX *sig_mem_ctx,
+ const uint8_t *data, size_t length,
+ const uint8_t *whole_pdu, size_t pdu_length,
+ DATA_BLOB *sig)
+{
+ struct gensec_ntlmssp_state *gensec_ntlmssp_state = (struct gensec_ntlmssp_state *)gensec_security->private_data;
+
+ return ntlmssp_make_packet_signature(gensec_ntlmssp_state, sig_mem_ctx,
+ data, length,
+ whole_pdu, pdu_length,
+ NTLMSSP_SEND, sig, true);
+}
+
+/**
+ * Check the signature of an incoming packet
+ *
+ */
+
+NTSTATUS gensec_ntlmssp_check_packet(struct gensec_security *gensec_security,
+ TALLOC_CTX *sig_mem_ctx,
+ const uint8_t *data, size_t length,
+ const uint8_t *whole_pdu, size_t pdu_length,
+ const DATA_BLOB *sig)
+{
+ struct gensec_ntlmssp_state *gensec_ntlmssp_state = (struct gensec_ntlmssp_state *)gensec_security->private_data;
+
+ DATA_BLOB local_sig;
+ NTSTATUS nt_status;
+
+ if (!gensec_ntlmssp_state->session_key.length) {
+ DEBUG(3, ("NO session key, cannot check packet signature\n"));
+ return NT_STATUS_NO_USER_SESSION_KEY;
+ }
+
+ nt_status = ntlmssp_make_packet_signature(gensec_ntlmssp_state, sig_mem_ctx,
+ data, length,
+ whole_pdu, pdu_length,
+ NTLMSSP_RECEIVE, &local_sig, true);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("NTLMSSP packet check failed with %s\n", nt_errstr(nt_status)));
+ return nt_status;
+ }
+
+ if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
+ if (local_sig.length != sig->length ||
+ memcmp(local_sig.data,
+ sig->data, sig->length) != 0) {
+ DEBUG(5, ("BAD SIG NTLM2: wanted signature over %llu bytes of input:\n", (unsigned long long)pdu_length));
+ dump_data(5, local_sig.data, local_sig.length);
+
+ DEBUG(5, ("BAD SIG: got signature over %llu bytes of input:\n", (unsigned long long)pdu_length));
+ dump_data(5, sig->data, sig->length);
+
+ DEBUG(1, ("NTLMSSP NTLM2 packet check failed due to invalid signature on %llu bytes of input!\n", (unsigned long long)pdu_length));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ } else {
+ if (local_sig.length != sig->length ||
+ memcmp(local_sig.data + 8,
+ sig->data + 8, sig->length - 8) != 0) {
+ DEBUG(5, ("BAD SIG NTLM1: wanted signature of %llu bytes of input:\n", (unsigned long long)length));
+ dump_data(5, local_sig.data, local_sig.length);
+
+ DEBUG(5, ("BAD SIG: got signature of %llu bytes of input:\n", (unsigned long long)length));
+ dump_data(5, sig->data, sig->length);
+
+ DEBUG(1, ("NTLMSSP NTLM1 packet check failed due to invalid signature on %llu bytes of input:\n", (unsigned long long)length));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ }
+ dump_data_pw("checked ntlmssp signature\n", sig->data, sig->length);
+
+ return NT_STATUS_OK;
+}
+
+
+/**
+ * Seal data with the NTLMSSP algorithm
+ *
+ */
+
+NTSTATUS gensec_ntlmssp_seal_packet(struct gensec_security *gensec_security,
+ TALLOC_CTX *sig_mem_ctx,
+ uint8_t *data, size_t length,
+ const uint8_t *whole_pdu, size_t pdu_length,
+ DATA_BLOB *sig)
+{
+ struct gensec_ntlmssp_state *gensec_ntlmssp_state = (struct gensec_ntlmssp_state *)gensec_security->private_data;
+ NTSTATUS nt_status;
+ if (!gensec_ntlmssp_state->session_key.length) {
+ DEBUG(3, ("NO session key, cannot seal packet\n"));
+ return NT_STATUS_NO_USER_SESSION_KEY;
+ }
+
+ DEBUG(10,("ntlmssp_seal_data: seal\n"));
+ dump_data_pw("ntlmssp clear data\n", data, length);
+ if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
+ /* The order of these two operations matters - we must first seal the packet,
+ then seal the sequence number - this is becouse the send_seal_hash is not
+ constant, but is is rather updated with each iteration */
+ nt_status = ntlmssp_make_packet_signature(gensec_ntlmssp_state, sig_mem_ctx,
+ data, length,
+ whole_pdu, pdu_length,
+ NTLMSSP_SEND, sig, false);
+ arcfour_crypt_sbox(gensec_ntlmssp_state->crypt.ntlm2.send_seal_arcfour_state, data, length);
+ if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) {
+ arcfour_crypt_sbox(gensec_ntlmssp_state->crypt.ntlm2.send_seal_arcfour_state, sig->data+4, 8);
+ }
+ } else {
+ uint32_t crc;
+ crc = crc32_calc_buffer(data, length);
+ if (!msrpc_gen(sig_mem_ctx,
+ sig, "dddd", NTLMSSP_SIGN_VERSION, 0, crc, gensec_ntlmssp_state->crypt.ntlm.seq_num)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* The order of these two operations matters - we must
+ first seal the packet, then seal the sequence
+ number - this is becouse the ntlmssp_hash is not
+ constant, but is is rather updated with each
+ iteration */
+
+ arcfour_crypt_sbox(gensec_ntlmssp_state->crypt.ntlm.arcfour_state, data, length);
+ arcfour_crypt_sbox(gensec_ntlmssp_state->crypt.ntlm.arcfour_state, sig->data+4, sig->length-4);
+ /* increment counter on send */
+ gensec_ntlmssp_state->crypt.ntlm.seq_num++;
+ nt_status = NT_STATUS_OK;
+ }
+ dump_data_pw("ntlmssp signature\n", sig->data, sig->length);
+ dump_data_pw("ntlmssp sealed data\n", data, length);
+
+
+ return nt_status;
+}
+
+/**
+ * Unseal data with the NTLMSSP algorithm
+ *
+ */
+
+/*
+ wrappers for the ntlmssp_*() functions
+*/
+NTSTATUS gensec_ntlmssp_unseal_packet(struct gensec_security *gensec_security,
+ TALLOC_CTX *sig_mem_ctx,
+ uint8_t *data, size_t length,
+ const uint8_t *whole_pdu, size_t pdu_length,
+ const DATA_BLOB *sig)
+{
+ struct gensec_ntlmssp_state *gensec_ntlmssp_state = (struct gensec_ntlmssp_state *)gensec_security->private_data;
+ if (!gensec_ntlmssp_state->session_key.length) {
+ DEBUG(3, ("NO session key, cannot unseal packet\n"));
+ return NT_STATUS_NO_USER_SESSION_KEY;
+ }
+
+ dump_data_pw("ntlmssp sealed data\n", data, length);
+ if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
+ arcfour_crypt_sbox(gensec_ntlmssp_state->crypt.ntlm2.recv_seal_arcfour_state, data, length);
+ } else {
+ arcfour_crypt_sbox(gensec_ntlmssp_state->crypt.ntlm.arcfour_state, data, length);
+ }
+ dump_data_pw("ntlmssp clear data\n", data, length);
+ return gensec_ntlmssp_check_packet(gensec_security, sig_mem_ctx, data, length, whole_pdu, pdu_length, sig);
+}
+
+/**
+ Initialise the state for NTLMSSP signing.
+*/
+/* TODO: make this non-public */
+NTSTATUS ntlmssp_sign_init(struct gensec_ntlmssp_state *gensec_ntlmssp_state)
+{
+ TALLOC_CTX *mem_ctx = talloc_new(gensec_ntlmssp_state);
+
+ if (!mem_ctx) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ DEBUG(3, ("NTLMSSP Sign/Seal - Initialising with flags:\n"));
+ debug_ntlmssp_flags(gensec_ntlmssp_state->neg_flags);
+
+ if (gensec_ntlmssp_state->session_key.length < 8) {
+ talloc_free(mem_ctx);
+ DEBUG(3, ("NO session key, cannot intialise signing\n"));
+ return NT_STATUS_NO_USER_SESSION_KEY;
+ }
+
+ if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2)
+ {
+ DATA_BLOB weak_session_key = gensec_ntlmssp_state->session_key;
+ const char *send_sign_const;
+ const char *send_seal_const;
+ const char *recv_sign_const;
+ const char *recv_seal_const;
+
+ DATA_BLOB send_seal_key;
+ DATA_BLOB recv_seal_key;
+
+ switch (gensec_ntlmssp_state->role) {
+ case NTLMSSP_CLIENT:
+ send_sign_const = CLI_SIGN;
+ send_seal_const = CLI_SEAL;
+ recv_sign_const = SRV_SIGN;
+ recv_seal_const = SRV_SEAL;
+ break;
+ case NTLMSSP_SERVER:
+ send_sign_const = SRV_SIGN;
+ send_seal_const = SRV_SEAL;
+ recv_sign_const = CLI_SIGN;
+ recv_seal_const = CLI_SEAL;
+ break;
+ default:
+ talloc_free(mem_ctx);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ gensec_ntlmssp_state->crypt.ntlm2.send_seal_arcfour_state = talloc(gensec_ntlmssp_state, struct arcfour_state);
+ NT_STATUS_HAVE_NO_MEMORY(gensec_ntlmssp_state->crypt.ntlm2.send_seal_arcfour_state);
+ gensec_ntlmssp_state->crypt.ntlm2.recv_seal_arcfour_state = talloc(gensec_ntlmssp_state, struct arcfour_state);
+ NT_STATUS_HAVE_NO_MEMORY(gensec_ntlmssp_state->crypt.ntlm2.send_seal_arcfour_state);
+
+ /**
+ Weaken NTLMSSP keys to cope with down-level
+ clients, servers and export restrictions.
+
+ We probably should have some parameters to control
+ this, once we get NTLM2 working.
+ */
+
+ /* Key weakening was not performed on the master key
+ * for NTLM2 (in ntlmssp_weaken_keys()), but must be
+ * done on the encryption subkeys only. That is why
+ * we don't have this code for the ntlmv1 case.
+ */
+
+ if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_128) {
+
+ } else if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_56) {
+ weak_session_key.length = 7;
+ } else { /* forty bits */
+ weak_session_key.length = 5;
+ }
+ dump_data_pw("NTLMSSP weakend master key:\n",
+ weak_session_key.data,
+ weak_session_key.length);
+
+ /* SEND: sign key */
+ calc_ntlmv2_key(gensec_ntlmssp_state,
+ &gensec_ntlmssp_state->crypt.ntlm2.send_sign_key,
+ gensec_ntlmssp_state->session_key, send_sign_const);
+ dump_data_pw("NTLMSSP send sign key:\n",
+ gensec_ntlmssp_state->crypt.ntlm2.send_sign_key.data,
+ gensec_ntlmssp_state->crypt.ntlm2.send_sign_key.length);
+
+ /* SEND: seal ARCFOUR pad */
+ calc_ntlmv2_key(mem_ctx,
+ &send_seal_key,
+ weak_session_key, send_seal_const);
+ dump_data_pw("NTLMSSP send seal key:\n",
+ send_seal_key.data,
+ send_seal_key.length);
+ arcfour_init(gensec_ntlmssp_state->crypt.ntlm2.send_seal_arcfour_state,
+ &send_seal_key);
+ dump_data_pw("NTLMSSP send sesl hash:\n",
+ gensec_ntlmssp_state->crypt.ntlm2.send_seal_arcfour_state->sbox,
+ sizeof(gensec_ntlmssp_state->crypt.ntlm2.send_seal_arcfour_state->sbox));
+
+ /* RECV: sign key */
+ calc_ntlmv2_key(gensec_ntlmssp_state,
+ &gensec_ntlmssp_state->crypt.ntlm2.recv_sign_key,
+ gensec_ntlmssp_state->session_key, recv_sign_const);
+ dump_data_pw("NTLMSSP recv sign key:\n",
+ gensec_ntlmssp_state->crypt.ntlm2.recv_sign_key.data,
+ gensec_ntlmssp_state->crypt.ntlm2.recv_sign_key.length);
+
+ /* RECV: seal ARCFOUR pad */
+ calc_ntlmv2_key(mem_ctx,
+ &recv_seal_key,
+ weak_session_key, recv_seal_const);
+ dump_data_pw("NTLMSSP recv seal key:\n",
+ recv_seal_key.data,
+ recv_seal_key.length);
+ arcfour_init(gensec_ntlmssp_state->crypt.ntlm2.recv_seal_arcfour_state,
+ &recv_seal_key);
+ dump_data_pw("NTLMSSP receive seal hash:\n",
+ gensec_ntlmssp_state->crypt.ntlm2.recv_seal_arcfour_state->sbox,
+ sizeof(gensec_ntlmssp_state->crypt.ntlm2.recv_seal_arcfour_state->sbox));
+
+ gensec_ntlmssp_state->crypt.ntlm2.send_seq_num = 0;
+ gensec_ntlmssp_state->crypt.ntlm2.recv_seq_num = 0;
+
+ } else {
+ DATA_BLOB weak_session_key = ntlmssp_weakend_key(gensec_ntlmssp_state, mem_ctx);
+ DEBUG(5, ("NTLMSSP Sign/Seal - using NTLM1\n"));
+
+ gensec_ntlmssp_state->crypt.ntlm.arcfour_state = talloc(gensec_ntlmssp_state, struct arcfour_state);
+ NT_STATUS_HAVE_NO_MEMORY(gensec_ntlmssp_state->crypt.ntlm.arcfour_state);
+
+ arcfour_init(gensec_ntlmssp_state->crypt.ntlm.arcfour_state,
+ &weak_session_key);
+ dump_data_pw("NTLMSSP hash:\n", gensec_ntlmssp_state->crypt.ntlm.arcfour_state->sbox,
+ sizeof(gensec_ntlmssp_state->crypt.ntlm.arcfour_state->sbox));
+
+ gensec_ntlmssp_state->crypt.ntlm.seq_num = 0;
+ }
+
+ talloc_free(mem_ctx);
+ return NT_STATUS_OK;
+}
+
+size_t gensec_ntlmssp_sig_size(struct gensec_security *gensec_security, size_t data_size)
+{
+ return NTLMSSP_SIG_SIZE;
+}
+
+NTSTATUS gensec_ntlmssp_wrap(struct gensec_security *gensec_security,
+ TALLOC_CTX *sig_mem_ctx,
+ const DATA_BLOB *in,
+ DATA_BLOB *out)
+{
+ DATA_BLOB sig;
+ NTSTATUS nt_status;
+
+ if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
+
+ *out = data_blob_talloc(sig_mem_ctx, NULL, in->length + NTLMSSP_SIG_SIZE);
+ if (!out->data) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ memcpy(out->data + NTLMSSP_SIG_SIZE, in->data, in->length);
+
+ nt_status = gensec_ntlmssp_seal_packet(gensec_security, sig_mem_ctx,
+ out->data + NTLMSSP_SIG_SIZE,
+ out->length - NTLMSSP_SIG_SIZE,
+ out->data + NTLMSSP_SIG_SIZE,
+ out->length - NTLMSSP_SIG_SIZE,
+ &sig);
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ memcpy(out->data, sig.data, NTLMSSP_SIG_SIZE);
+ }
+ return nt_status;
+
+ } else if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) {
+
+ *out = data_blob_talloc(sig_mem_ctx, NULL, in->length + NTLMSSP_SIG_SIZE);
+ if (!out->data) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ memcpy(out->data + NTLMSSP_SIG_SIZE, in->data, in->length);
+
+ nt_status = gensec_ntlmssp_sign_packet(gensec_security, sig_mem_ctx,
+ out->data + NTLMSSP_SIG_SIZE,
+ out->length - NTLMSSP_SIG_SIZE,
+ out->data + NTLMSSP_SIG_SIZE,
+ out->length - NTLMSSP_SIG_SIZE,
+ &sig);
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ memcpy(out->data, sig.data, NTLMSSP_SIG_SIZE);
+ }
+ return nt_status;
+
+ } else {
+ *out = *in;
+ return NT_STATUS_OK;
+ }
+}
+
+
+NTSTATUS gensec_ntlmssp_unwrap(struct gensec_security *gensec_security,
+ TALLOC_CTX *sig_mem_ctx,
+ const DATA_BLOB *in,
+ DATA_BLOB *out)
+{
+ DATA_BLOB sig;
+
+ if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
+ if (in->length < NTLMSSP_SIG_SIZE) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ sig.data = in->data;
+ sig.length = NTLMSSP_SIG_SIZE;
+
+ *out = data_blob_talloc(sig_mem_ctx, in->data + NTLMSSP_SIG_SIZE, in->length - NTLMSSP_SIG_SIZE);
+
+ return gensec_ntlmssp_unseal_packet(gensec_security, sig_mem_ctx,
+ out->data, out->length,
+ out->data, out->length,
+ &sig);
+
+ } else if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) {
+ if (in->length < NTLMSSP_SIG_SIZE) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ sig.data = in->data;
+ sig.length = NTLMSSP_SIG_SIZE;
+
+ *out = data_blob_talloc(sig_mem_ctx, in->data + NTLMSSP_SIG_SIZE, in->length - NTLMSSP_SIG_SIZE);
+
+ return gensec_ntlmssp_check_packet(gensec_security, sig_mem_ctx,
+ out->data, out->length,
+ out->data, out->length,
+ &sig);
+ } else {
+ *out = *in;
+ return NT_STATUS_OK;
+ }
+}
+
diff --git a/source4/auth/pyauth.c b/source4/auth/pyauth.c
new file mode 100644
index 0000000000..e97174fcc3
--- /dev/null
+++ b/source4/auth/pyauth.c
@@ -0,0 +1,92 @@
+/*
+ Unix SMB/CIFS implementation.
+ Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "param/param.h"
+#include "pyauth.h"
+#include "auth/system_session_proto.h"
+#include "param/pyparam.h"
+
+PyTypeObject PyAuthSession = {
+ .tp_name = "AuthSession",
+ .tp_basicsize = sizeof(py_talloc_Object),
+ .tp_dealloc = py_talloc_dealloc,
+ .tp_flags = Py_TPFLAGS_DEFAULT,
+ .tp_repr = py_talloc_default_repr,
+};
+
+PyObject *PyAuthSession_FromSession(struct auth_session_info *session)
+{
+ return py_talloc_import(&PyAuthSession, session);
+}
+
+static PyObject *py_system_session(PyObject *module, PyObject *args)
+{
+ PyObject *py_lp_ctx = Py_None;
+ struct loadparm_context *lp_ctx = NULL;
+ struct auth_session_info *session;
+ if (!PyArg_ParseTuple(args, "|O", &py_lp_ctx))
+ return NULL;
+
+ lp_ctx = lp_from_py_object(py_lp_ctx);
+ if (lp_ctx == NULL)
+ return NULL;
+
+ session = system_session(NULL, lp_ctx);
+
+ return PyAuthSession_FromSession(session);
+}
+
+
+static PyObject *py_system_session_anon(PyObject *module, PyObject *args)
+{
+ PyObject *py_lp_ctx = Py_None;
+ struct loadparm_context *lp_ctx;
+ struct auth_session_info *session;
+ if (!PyArg_ParseTuple(args, "|O", &py_lp_ctx))
+ return NULL;
+
+ lp_ctx = lp_from_py_object(py_lp_ctx);
+ if (lp_ctx == NULL)
+ return NULL;
+
+ session = system_session_anon(NULL, lp_ctx);
+
+ return PyAuthSession_FromSession(session);
+}
+
+static PyMethodDef py_auth_methods[] = {
+ { "system_session", (PyCFunction)py_system_session, METH_VARARGS, NULL },
+ { "system_session_anonymous", (PyCFunction)py_system_session_anon, METH_VARARGS, NULL },
+ { NULL },
+};
+
+void initauth(void)
+{
+ PyObject *m;
+
+ if (PyType_Ready(&PyAuthSession) < 0)
+ return;
+
+ m = Py_InitModule3("auth", py_auth_methods, "Authentication and authorization support.");
+ if (m == NULL)
+ return;
+
+ Py_INCREF(&PyAuthSession);
+ PyModule_AddObject(m, "AuthSession", (PyObject *)&PyAuthSession);
+}
diff --git a/source4/auth/pyauth.h b/source4/auth/pyauth.h
new file mode 100644
index 0000000000..f5c63392ac
--- /dev/null
+++ b/source4/auth/pyauth.h
@@ -0,0 +1,32 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Jelmer Vernooij <jelmer@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _PYAUTH_H_
+#define _PYAUTH_H_
+
+#include "pytalloc.h"
+#include "auth/session.h"
+
+PyAPI_DATA(PyTypeObject) PyAuthSession;
+#define PyAuthSession_AsSession(obj) py_talloc_get_type(obj, struct auth_session_info)
+#define PyAuthSession_Check(obj) PyObject_TypeCheck(obj, &PyAuthSession)
+struct auth_session_info *PyObject_AsSession(PyObject *obj);
+PyObject *PyAuthSession_FromSession(struct auth_session_info *session);
+
+#endif /* _PYAUTH_H */
diff --git a/source4/auth/sam.c b/source4/auth/sam.c
new file mode 100644
index 0000000000..819bca0db0
--- /dev/null
+++ b/source4/auth/sam.c
@@ -0,0 +1,430 @@
+/*
+ Unix SMB/CIFS implementation.
+ Password and authentication handling
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-2004
+ Copyright (C) Gerald Carter 2003
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/time.h"
+#include "auth/auth.h"
+#include <ldb.h>
+#include "../lib/util/util_ldb.h"
+#include "dsdb/samdb/samdb.h"
+#include "libcli/security/security.h"
+#include "libcli/ldap/ldap.h"
+#include "librpc/gen_ndr/ndr_netlogon.h"
+#include "param/param.h"
+#include "auth/auth_sam.h"
+
+const char *user_attrs[] = {
+ /* required for the krb5 kdc */
+ "objectClass",
+ "sAMAccountName",
+ "userPrincipalName",
+ "servicePrincipalName",
+ "msDS-KeyVersionNumber",
+ "supplementalCredentials",
+
+ /* passwords */
+ "dBCSPwd",
+ "unicodePwd",
+
+ "userAccountControl",
+
+ "pwdLastSet",
+ "accountExpires",
+ "logonHours",
+ "objectSid",
+
+ /* check 'allowed workstations' */
+ "userWorkstations",
+
+ /* required for server_info, not access control: */
+ "displayName",
+ "scriptPath",
+ "profilePath",
+ "homeDirectory",
+ "homeDrive",
+ "lastLogon",
+ "lastLogoff",
+ "accountExpires",
+ "badPwdCount",
+ "logonCount",
+ "primaryGroupID",
+ NULL,
+};
+
+const char *domain_ref_attrs[] = {"nETBIOSName", "nCName",
+ "dnsRoot", "objectClass", NULL};
+
+/****************************************************************************
+ Check if a user is allowed to logon at this time. Note this is the
+ servers local time, as logon hours are just specified as a weekly
+ bitmask.
+****************************************************************************/
+
+static bool logon_hours_ok(struct ldb_message *msg, const char *name_for_logs)
+{
+ /* In logon hours first bit is Sunday from 12AM to 1AM */
+ const struct ldb_val *hours;
+ struct tm *utctime;
+ time_t lasttime;
+ const char *asct;
+ uint8_t bitmask, bitpos;
+
+ hours = ldb_msg_find_ldb_val(msg, "logonHours");
+ if (!hours) {
+ DEBUG(5,("logon_hours_ok: No hours restrictions for user %s\n", name_for_logs));
+ return true;
+ }
+
+ if (hours->length != 168/8) {
+ DEBUG(5,("logon_hours_ok: malformed logon hours restrictions for user %s\n", name_for_logs));
+ return true;
+ }
+
+ lasttime = time(NULL);
+ utctime = gmtime(&lasttime);
+ if (!utctime) {
+ DEBUG(1, ("logon_hours_ok: failed to get gmtime. Failing logon for user %s\n",
+ name_for_logs));
+ return false;
+ }
+
+ /* find the corresponding byte and bit */
+ bitpos = (utctime->tm_wday * 24 + utctime->tm_hour) % 168;
+ bitmask = 1 << (bitpos % 8);
+
+ if (! (hours->data[bitpos/8] & bitmask)) {
+ struct tm *t = localtime(&lasttime);
+ if (!t) {
+ asct = "INVALID TIME";
+ } else {
+ asct = asctime(t);
+ if (!asct) {
+ asct = "INVALID TIME";
+ }
+ }
+
+ DEBUG(1, ("logon_hours_ok: Account for user %s not allowed to "
+ "logon at this time (%s).\n",
+ name_for_logs, asct ));
+ return false;
+ }
+
+ asct = asctime(utctime);
+ DEBUG(5,("logon_hours_ok: user %s allowed to logon at this time (%s)\n",
+ name_for_logs, asct ? asct : "UNKNOWN TIME" ));
+
+ return true;
+}
+
+/****************************************************************************
+ Do a specific test for a SAM_ACCOUNT being vaild for this connection
+ (ie not disabled, expired and the like).
+****************************************************************************/
+_PUBLIC_ NTSTATUS authsam_account_ok(TALLOC_CTX *mem_ctx,
+ struct ldb_context *sam_ctx,
+ uint32_t logon_parameters,
+ struct ldb_message *msg,
+ struct ldb_message *msg_domain_ref,
+ const char *logon_workstation,
+ const char *name_for_logs,
+ bool allow_domain_trust)
+{
+ uint16_t acct_flags;
+ const char *workstation_list;
+ NTTIME acct_expiry;
+ NTTIME must_change_time;
+
+ struct ldb_dn *domain_dn = samdb_result_dn(sam_ctx, mem_ctx, msg_domain_ref, "nCName", ldb_dn_new(mem_ctx, sam_ctx, NULL));
+
+ NTTIME now;
+ DEBUG(4,("authsam_account_ok: Checking SMB password for user %s\n", name_for_logs));
+
+ acct_flags = samdb_result_acct_flags(sam_ctx, mem_ctx, msg, domain_dn);
+
+ acct_expiry = samdb_result_account_expires(msg);
+
+ /* Check for when we must change this password, taking the
+ * userAccountControl flags into account */
+ must_change_time = samdb_result_force_password_change(sam_ctx, mem_ctx,
+ domain_dn, msg);
+
+ workstation_list = samdb_result_string(msg, "userWorkstations", NULL);
+
+ /* Quit if the account was disabled. */
+ if (acct_flags & ACB_DISABLED) {
+ DEBUG(1,("authsam_account_ok: Account for user '%s' was disabled.\n", name_for_logs));
+ return NT_STATUS_ACCOUNT_DISABLED;
+ }
+
+ /* Quit if the account was locked out. */
+ if (acct_flags & ACB_AUTOLOCK) {
+ DEBUG(1,("authsam_account_ok: Account for user %s was locked out.\n", name_for_logs));
+ return NT_STATUS_ACCOUNT_LOCKED_OUT;
+ }
+
+ /* Test account expire time */
+ unix_to_nt_time(&now, time(NULL));
+ if (now > acct_expiry) {
+ DEBUG(1,("authsam_account_ok: Account for user '%s' has expired.\n", name_for_logs));
+ DEBUG(3,("authsam_account_ok: Account expired at '%s'.\n",
+ nt_time_string(mem_ctx, acct_expiry)));
+ return NT_STATUS_ACCOUNT_EXPIRED;
+ }
+
+ /* check for immediate expiry "must change at next logon" */
+ if (must_change_time == 0) {
+ DEBUG(1,("sam_account_ok: Account for user '%s' password must change!.\n",
+ name_for_logs));
+ return NT_STATUS_PASSWORD_MUST_CHANGE;
+ }
+
+ /* check for expired password */
+ if (must_change_time < now) {
+ DEBUG(1,("sam_account_ok: Account for user '%s' password expired!.\n",
+ name_for_logs));
+ DEBUG(1,("sam_account_ok: Password expired at '%s' unix time.\n",
+ nt_time_string(mem_ctx, must_change_time)));
+ return NT_STATUS_PASSWORD_EXPIRED;
+ }
+
+ /* Test workstation. Workstation list is comma separated. */
+ if (logon_workstation && workstation_list && *workstation_list) {
+ bool invalid_ws = true;
+ int i;
+ const char **workstations = (const char **)str_list_make(mem_ctx, workstation_list, ",");
+
+ for (i = 0; workstations && workstations[i]; i++) {
+ DEBUG(10,("sam_account_ok: checking for workstation match '%s' and '%s'\n",
+ workstations[i], logon_workstation));
+
+ if (strequal(workstations[i], logon_workstation)) {
+ invalid_ws = false;
+ break;
+ }
+ }
+
+ talloc_free(workstations);
+
+ if (invalid_ws) {
+ return NT_STATUS_INVALID_WORKSTATION;
+ }
+ }
+
+ if (!logon_hours_ok(msg, name_for_logs)) {
+ return NT_STATUS_INVALID_LOGON_HOURS;
+ }
+
+ if (!allow_domain_trust) {
+ if (acct_flags & ACB_DOMTRUST) {
+ DEBUG(2,("sam_account_ok: Domain trust account %s denied by server\n", name_for_logs));
+ return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
+ }
+ }
+ if (!(logon_parameters & MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT)) {
+ if (acct_flags & ACB_SVRTRUST) {
+ DEBUG(2,("sam_account_ok: Server trust account %s denied by server\n", name_for_logs));
+ return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
+ }
+ }
+ if (!(logon_parameters & MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT)) {
+ if (acct_flags & ACB_WSTRUST) {
+ DEBUG(4,("sam_account_ok: Wksta trust account %s denied by server\n", name_for_logs));
+ return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+_PUBLIC_ NTSTATUS authsam_make_server_info(TALLOC_CTX *mem_ctx, struct ldb_context *sam_ctx,
+ const char *netbios_name,
+ struct ldb_message *msg,
+ struct ldb_message *msg_domain_ref,
+ DATA_BLOB user_sess_key, DATA_BLOB lm_sess_key,
+ struct auth_serversupplied_info **_server_info)
+{
+ struct auth_serversupplied_info *server_info;
+ struct ldb_message **group_msgs;
+ int group_ret;
+ const char *group_attrs[3] = { "sAMAccountType", "objectSid", NULL };
+ /* find list of sids */
+ struct dom_sid **groupSIDs = NULL;
+ struct dom_sid *account_sid;
+ struct dom_sid *primary_group_sid;
+ struct ldb_dn *domain_dn;
+ const char *str;
+ struct ldb_dn *ncname;
+ int i;
+ uint_t rid;
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+
+ group_ret = gendb_search(sam_ctx,
+ tmp_ctx, NULL, &group_msgs, group_attrs,
+ "(&(member=%s)(sAMAccountType=*))",
+ ldb_dn_get_linearized(msg->dn));
+ if (group_ret == -1) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ server_info = talloc(mem_ctx, struct auth_serversupplied_info);
+ NT_STATUS_HAVE_NO_MEMORY(server_info);
+
+ if (group_ret > 0) {
+ groupSIDs = talloc_array(server_info, struct dom_sid *, group_ret);
+ NT_STATUS_HAVE_NO_MEMORY(groupSIDs);
+ }
+
+ /* Need to unroll some nested groups, but not aliases */
+ for (i = 0; i < group_ret; i++) {
+ groupSIDs[i] = samdb_result_dom_sid(groupSIDs,
+ group_msgs[i], "objectSid");
+ NT_STATUS_HAVE_NO_MEMORY(groupSIDs[i]);
+ }
+
+ talloc_free(tmp_ctx);
+
+ account_sid = samdb_result_dom_sid(server_info, msg, "objectSid");
+ NT_STATUS_HAVE_NO_MEMORY(account_sid);
+
+ primary_group_sid = dom_sid_dup(server_info, account_sid);
+ NT_STATUS_HAVE_NO_MEMORY(primary_group_sid);
+
+ rid = samdb_result_uint(msg, "primaryGroupID", ~0);
+ if (rid == ~0) {
+ if (group_ret > 0) {
+ primary_group_sid = groupSIDs[0];
+ } else {
+ primary_group_sid = NULL;
+ }
+ } else {
+ primary_group_sid->sub_auths[primary_group_sid->num_auths-1] = rid;
+ }
+
+ server_info->account_sid = account_sid;
+ server_info->primary_group_sid = primary_group_sid;
+
+ server_info->n_domain_groups = group_ret;
+ server_info->domain_groups = groupSIDs;
+
+ server_info->account_name = talloc_steal(server_info, samdb_result_string(msg, "sAMAccountName", NULL));
+
+ server_info->domain_name = talloc_steal(server_info, samdb_result_string(msg_domain_ref, "nETBIOSName", NULL));
+
+ str = samdb_result_string(msg, "displayName", "");
+ server_info->full_name = talloc_strdup(server_info, str);
+ NT_STATUS_HAVE_NO_MEMORY(server_info->full_name);
+
+ str = samdb_result_string(msg, "scriptPath", "");
+ server_info->logon_script = talloc_strdup(server_info, str);
+ NT_STATUS_HAVE_NO_MEMORY(server_info->logon_script);
+
+ str = samdb_result_string(msg, "profilePath", "");
+ server_info->profile_path = talloc_strdup(server_info, str);
+ NT_STATUS_HAVE_NO_MEMORY(server_info->profile_path);
+
+ str = samdb_result_string(msg, "homeDirectory", "");
+ server_info->home_directory = talloc_strdup(server_info, str);
+ NT_STATUS_HAVE_NO_MEMORY(server_info->home_directory);
+
+ str = samdb_result_string(msg, "homeDrive", "");
+ server_info->home_drive = talloc_strdup(server_info, str);
+ NT_STATUS_HAVE_NO_MEMORY(server_info->home_drive);
+
+ server_info->logon_server = talloc_strdup(server_info, netbios_name);
+ NT_STATUS_HAVE_NO_MEMORY(server_info->logon_server);
+
+ server_info->last_logon = samdb_result_nttime(msg, "lastLogon", 0);
+ server_info->last_logoff = samdb_result_nttime(msg, "lastLogoff", 0);
+ server_info->acct_expiry = samdb_result_account_expires(msg);
+ server_info->last_password_change = samdb_result_nttime(msg, "pwdLastSet", 0);
+
+ ncname = samdb_result_dn(sam_ctx, mem_ctx, msg_domain_ref, "nCName", NULL);
+ if (!ncname) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ server_info->allow_password_change
+ = samdb_result_allow_password_change(sam_ctx, mem_ctx,
+ ncname, msg, "pwdLastSet");
+ server_info->force_password_change
+ = samdb_result_force_password_change(sam_ctx, mem_ctx,
+ ncname, msg);
+
+ server_info->logon_count = samdb_result_uint(msg, "logonCount", 0);
+ server_info->bad_password_count = samdb_result_uint(msg, "badPwdCount", 0);
+
+ domain_dn = samdb_result_dn(sam_ctx, mem_ctx, msg_domain_ref, "nCName", NULL);
+
+ server_info->acct_flags = samdb_result_acct_flags(sam_ctx, mem_ctx,
+ msg, domain_dn);
+
+ server_info->user_session_key = user_sess_key;
+ server_info->lm_session_key = lm_sess_key;
+
+ server_info->authenticated = true;
+
+ *_server_info = server_info;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sam_get_results_principal(struct ldb_context *sam_ctx,
+ TALLOC_CTX *mem_ctx, const char *principal,
+ struct ldb_message ***msgs,
+ struct ldb_message ***msgs_domain_ref)
+{
+ struct ldb_dn *user_dn, *domain_dn;
+ NTSTATUS nt_status;
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+ int ret;
+ struct ldb_dn *partitions_basedn = samdb_partitions_dn(sam_ctx, mem_ctx);
+
+ if (!tmp_ctx) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ nt_status = crack_user_principal_name(sam_ctx, tmp_ctx, principal, &user_dn, &domain_dn);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(tmp_ctx);
+ return nt_status;
+ }
+
+ /* grab domain info from the reference */
+ ret = gendb_search(sam_ctx, tmp_ctx, partitions_basedn, msgs_domain_ref, domain_ref_attrs,
+ "(ncName=%s)", ldb_dn_get_linearized(domain_dn));
+
+ if (ret != 1) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ /* pull the user attributes */
+ ret = gendb_search_dn(sam_ctx, tmp_ctx, user_dn, msgs, user_attrs);
+ if (ret != 1) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ talloc_steal(mem_ctx, *msgs);
+ talloc_steal(mem_ctx, *msgs_domain_ref);
+ talloc_free(tmp_ctx);
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/auth/samba_server_gensec.c b/source4/auth/samba_server_gensec.c
new file mode 100644
index 0000000000..0576b15eb3
--- /dev/null
+++ b/source4/auth/samba_server_gensec.c
@@ -0,0 +1,73 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Generic Authentication Interface for Samba Servers
+
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+/* This code sets up GENSEC in the way that all Samba servers want
+ * (becaue they have presumed access to the sam.ldb etc */
+
+#include "includes.h"
+#include "auth/auth.h"
+#include "auth/gensec/gensec.h"
+#include "param/param.h"
+
+NTSTATUS samba_server_gensec_start(TALLOC_CTX *mem_ctx,
+ struct tevent_context *event_ctx,
+ struct messaging_context *msg_ctx,
+ struct loadparm_context *lp_ctx,
+ struct cli_credentials *server_credentials,
+ const char *target_service,
+ struct gensec_security **gensec_context)
+{
+ NTSTATUS nt_status;
+ struct gensec_security *gensec_ctx;
+ struct auth_context *auth_context;
+
+ nt_status = auth_context_create(mem_ctx,
+ event_ctx,
+ msg_ctx,
+ lp_ctx,
+ &auth_context);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(1, ("Failed to start auth server code: %s\n", nt_errstr(nt_status)));
+ return nt_status;
+ }
+
+ nt_status = gensec_server_start(mem_ctx,
+ event_ctx,
+ lp_gensec_settings(mem_ctx, lp_ctx),
+ auth_context,
+ &gensec_ctx);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(auth_context);
+ DEBUG(1, ("Failed to start GENSEC server code: %s\n", nt_errstr(nt_status)));
+ return nt_status;
+ }
+
+ talloc_steal(gensec_ctx, auth_context);
+
+ gensec_set_credentials(gensec_ctx, server_credentials);
+
+ if (target_service) {
+ gensec_set_target_service(gensec_ctx, target_service);
+ }
+ *gensec_context = gensec_ctx;
+ return nt_status;
+}
diff --git a/source4/auth/session.c b/source4/auth/session.c
new file mode 100644
index 0000000000..ef5646fd37
--- /dev/null
+++ b/source4/auth/session.c
@@ -0,0 +1,203 @@
+/*
+ Unix SMB/CIFS implementation.
+ Authentication utility functions
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Andrew Bartlett 2001
+ Copyright (C) Jeremy Allison 2000-2001
+ Copyright (C) Rafal Szczesniak 2002
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "auth/auth.h"
+#include "libcli/security/security.h"
+#include "libcli/auth/libcli_auth.h"
+#include "dsdb/samdb/samdb.h"
+#include "auth/credentials/credentials.h"
+#include "param/param.h"
+#include "auth/session_proto.h"
+
+_PUBLIC_ struct auth_session_info *anonymous_session(TALLOC_CTX *mem_ctx,
+ struct tevent_context *event_ctx,
+ struct loadparm_context *lp_ctx)
+{
+ NTSTATUS nt_status;
+ struct auth_session_info *session_info = NULL;
+ nt_status = auth_anonymous_session_info(mem_ctx, event_ctx, lp_ctx, &session_info);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return NULL;
+ }
+ return session_info;
+}
+
+_PUBLIC_ NTSTATUS auth_anonymous_session_info(TALLOC_CTX *parent_ctx,
+ struct tevent_context *event_ctx,
+ struct loadparm_context *lp_ctx,
+ struct auth_session_info **_session_info)
+{
+ NTSTATUS nt_status;
+ struct auth_serversupplied_info *server_info = NULL;
+ struct auth_session_info *session_info = NULL;
+ TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
+
+ nt_status = auth_anonymous_server_info(mem_ctx,
+ lp_netbios_name(lp_ctx),
+ &server_info);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(mem_ctx);
+ return nt_status;
+ }
+
+ /* references the server_info into the session_info */
+ nt_status = auth_generate_session_info(parent_ctx, event_ctx, lp_ctx, server_info, &session_info);
+ talloc_free(mem_ctx);
+
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+
+ session_info->credentials = cli_credentials_init(session_info);
+ if (!session_info->credentials) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ cli_credentials_set_conf(session_info->credentials, lp_ctx);
+ cli_credentials_set_anonymous(session_info->credentials);
+
+ *_session_info = session_info;
+
+ return NT_STATUS_OK;
+}
+
+_PUBLIC_ NTSTATUS auth_anonymous_server_info(TALLOC_CTX *mem_ctx,
+ const char *netbios_name,
+ struct auth_serversupplied_info **_server_info)
+{
+ struct auth_serversupplied_info *server_info;
+ server_info = talloc(mem_ctx, struct auth_serversupplied_info);
+ NT_STATUS_HAVE_NO_MEMORY(server_info);
+
+ server_info->account_sid = dom_sid_parse_talloc(server_info, SID_NT_ANONYMOUS);
+ NT_STATUS_HAVE_NO_MEMORY(server_info->account_sid);
+
+ /* is this correct? */
+ server_info->primary_group_sid = dom_sid_parse_talloc(server_info, SID_BUILTIN_GUESTS);
+ NT_STATUS_HAVE_NO_MEMORY(server_info->primary_group_sid);
+
+ server_info->n_domain_groups = 0;
+ server_info->domain_groups = NULL;
+
+ /* annoying, but the Anonymous really does have a session key... */
+ server_info->user_session_key = data_blob_talloc(server_info, NULL, 16);
+ NT_STATUS_HAVE_NO_MEMORY(server_info->user_session_key.data);
+
+ server_info->lm_session_key = data_blob_talloc(server_info, NULL, 16);
+ NT_STATUS_HAVE_NO_MEMORY(server_info->lm_session_key.data);
+
+ /* and it is all zeros! */
+ data_blob_clear(&server_info->user_session_key);
+ data_blob_clear(&server_info->lm_session_key);
+
+ server_info->account_name = talloc_strdup(server_info, "ANONYMOUS LOGON");
+ NT_STATUS_HAVE_NO_MEMORY(server_info->account_name);
+
+ server_info->domain_name = talloc_strdup(server_info, "NT AUTHORITY");
+ NT_STATUS_HAVE_NO_MEMORY(server_info->domain_name);
+
+ server_info->full_name = talloc_strdup(server_info, "Anonymous Logon");
+ NT_STATUS_HAVE_NO_MEMORY(server_info->full_name);
+
+ server_info->logon_script = talloc_strdup(server_info, "");
+ NT_STATUS_HAVE_NO_MEMORY(server_info->logon_script);
+
+ server_info->profile_path = talloc_strdup(server_info, "");
+ NT_STATUS_HAVE_NO_MEMORY(server_info->profile_path);
+
+ server_info->home_directory = talloc_strdup(server_info, "");
+ NT_STATUS_HAVE_NO_MEMORY(server_info->home_directory);
+
+ server_info->home_drive = talloc_strdup(server_info, "");
+ NT_STATUS_HAVE_NO_MEMORY(server_info->home_drive);
+
+ server_info->logon_server = talloc_strdup(server_info, netbios_name);
+ NT_STATUS_HAVE_NO_MEMORY(server_info->logon_server);
+
+ server_info->last_logon = 0;
+ server_info->last_logoff = 0;
+ server_info->acct_expiry = 0;
+ server_info->last_password_change = 0;
+ server_info->allow_password_change = 0;
+ server_info->force_password_change = 0;
+
+ server_info->logon_count = 0;
+ server_info->bad_password_count = 0;
+
+ server_info->acct_flags = ACB_NORMAL;
+
+ server_info->authenticated = false;
+
+ *_server_info = server_info;
+
+ return NT_STATUS_OK;
+}
+
+_PUBLIC_ NTSTATUS auth_generate_session_info(TALLOC_CTX *mem_ctx,
+ struct tevent_context *event_ctx,
+ struct loadparm_context *lp_ctx,
+ struct auth_serversupplied_info *server_info,
+ struct auth_session_info **_session_info)
+{
+ struct auth_session_info *session_info;
+ NTSTATUS nt_status;
+
+ session_info = talloc(mem_ctx, struct auth_session_info);
+ NT_STATUS_HAVE_NO_MEMORY(session_info);
+
+ session_info->server_info = talloc_reference(session_info, server_info);
+
+ /* unless set otherwise, the session key is the user session
+ * key from the auth subsystem */
+ session_info->session_key = server_info->user_session_key;
+
+ nt_status = security_token_create(session_info,
+ event_ctx,
+ lp_ctx,
+ server_info->account_sid,
+ server_info->primary_group_sid,
+ server_info->n_domain_groups,
+ server_info->domain_groups,
+ server_info->authenticated,
+ &session_info->security_token);
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+
+ session_info->credentials = NULL;
+
+ *_session_info = session_info;
+ return NT_STATUS_OK;
+}
+
+/**
+ * prints a struct auth_session_info security token to debug output.
+ */
+void auth_session_info_debug(int dbg_lev,
+ const struct auth_session_info *session_info)
+{
+ if (!session_info) {
+ DEBUG(dbg_lev, ("Session Info: (NULL)\n"));
+ return;
+ }
+
+ security_token_debug(dbg_lev, session_info->security_token);
+}
+
diff --git a/source4/auth/session.h b/source4/auth/session.h
new file mode 100644
index 0000000000..15570c4414
--- /dev/null
+++ b/source4/auth/session.h
@@ -0,0 +1,66 @@
+/*
+ Unix SMB/CIFS implementation.
+ Process and provide the logged on user's authorization token
+ Copyright (C) Andrew Bartlett 2001
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _SAMBA_AUTH_SESSION_H
+#define _SAMBA_AUTH_SESSION_H
+
+struct auth_session_info {
+ struct security_token *security_token;
+ struct auth_serversupplied_info *server_info;
+ DATA_BLOB session_key;
+ struct cli_credentials *credentials;
+};
+
+#include "librpc/gen_ndr/netlogon.h"
+
+struct tevent_context;
+
+/* Create a security token for a session SYSTEM (the most
+ * trusted/prvilaged account), including the local machine account as
+ * the off-host credentials */
+struct auth_session_info *system_session(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx) ;
+
+/*
+ * Create a system session, but with anonymous credentials (so we do
+ * not need to open secrets.ldb)
+ */
+struct auth_session_info *system_session_anon(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx);
+
+
+NTSTATUS auth_anonymous_server_info(TALLOC_CTX *mem_ctx,
+ const char *netbios_name,
+ struct auth_serversupplied_info **_server_info) ;
+NTSTATUS auth_generate_session_info(TALLOC_CTX *mem_ctx,
+ struct tevent_context *event_ctx,
+ struct loadparm_context *lp_ctx,
+ struct auth_serversupplied_info *server_info,
+ struct auth_session_info **_session_info) ;
+
+NTSTATUS auth_anonymous_session_info(TALLOC_CTX *parent_ctx,
+ struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx,
+ struct auth_session_info **_session_info);
+
+struct auth_session_info *anonymous_session(TALLOC_CTX *mem_ctx,
+ struct tevent_context *event_ctx,
+ struct loadparm_context *lp_ctx);
+
+
+#endif /* _SAMBA_AUTH_SESSION_H */
diff --git a/source4/auth/system_session.c b/source4/auth/system_session.c
new file mode 100644
index 0000000000..07b0060643
--- /dev/null
+++ b/source4/auth/system_session.c
@@ -0,0 +1,305 @@
+/*
+ Unix SMB/CIFS implementation.
+ Authentication utility functions
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Andrew Bartlett 2001
+ Copyright (C) Jeremy Allison 2000-2001
+ Copyright (C) Rafal Szczesniak 2002
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/security/security.h"
+#include "libcli/auth/libcli_auth.h"
+#include "auth/credentials/credentials.h"
+#include "param/param.h"
+#include "auth/auth.h" /* for auth_serversupplied_info */
+#include "auth/session.h"
+#include "auth/system_session_proto.h"
+
+/**
+ * Create the SID list for this user.
+ *
+ * @note Specialised version for system sessions that doesn't use the SAM.
+ */
+static NTSTATUS create_token(TALLOC_CTX *mem_ctx,
+ struct dom_sid *user_sid,
+ struct dom_sid *group_sid,
+ int n_groupSIDs,
+ struct dom_sid **groupSIDs,
+ bool is_authenticated,
+ struct security_token **token)
+{
+ struct security_token *ptoken;
+ int i;
+
+ ptoken = security_token_initialise(mem_ctx);
+ NT_STATUS_HAVE_NO_MEMORY(ptoken);
+
+ ptoken->sids = talloc_array(ptoken, struct dom_sid *, n_groupSIDs + 5);
+ NT_STATUS_HAVE_NO_MEMORY(ptoken->sids);
+
+ ptoken->user_sid = talloc_reference(ptoken, user_sid);
+ ptoken->group_sid = talloc_reference(ptoken, group_sid);
+ ptoken->privilege_mask = 0;
+
+ ptoken->sids[0] = ptoken->user_sid;
+ ptoken->sids[1] = ptoken->group_sid;
+
+ /*
+ * Finally add the "standard" SIDs.
+ * The only difference between guest and "anonymous"
+ * is the addition of Authenticated_Users.
+ */
+ ptoken->sids[2] = dom_sid_parse_talloc(ptoken->sids, SID_WORLD);
+ NT_STATUS_HAVE_NO_MEMORY(ptoken->sids[2]);
+ ptoken->sids[3] = dom_sid_parse_talloc(ptoken->sids, SID_NT_NETWORK);
+ NT_STATUS_HAVE_NO_MEMORY(ptoken->sids[3]);
+ ptoken->num_sids = 4;
+
+ if (is_authenticated) {
+ ptoken->sids[4] = dom_sid_parse_talloc(ptoken->sids, SID_NT_AUTHENTICATED_USERS);
+ NT_STATUS_HAVE_NO_MEMORY(ptoken->sids[4]);
+ ptoken->num_sids++;
+ }
+
+ for (i = 0; i < n_groupSIDs; i++) {
+ size_t check_sid_idx;
+ for (check_sid_idx = 1;
+ check_sid_idx < ptoken->num_sids;
+ check_sid_idx++) {
+ if (dom_sid_equal(ptoken->sids[check_sid_idx], groupSIDs[i])) {
+ break;
+ }
+ }
+
+ if (check_sid_idx == ptoken->num_sids) {
+ ptoken->sids[ptoken->num_sids++] = talloc_reference(ptoken->sids, groupSIDs[i]);
+ }
+ }
+
+ *token = ptoken;
+
+ /* Shortcuts to prevent recursion and avoid lookups */
+ if (ptoken->user_sid == NULL) {
+ ptoken->privilege_mask = 0;
+ return NT_STATUS_OK;
+ }
+
+ if (security_token_is_system(ptoken)) {
+ ptoken->privilege_mask = ~0;
+ return NT_STATUS_OK;
+ }
+
+ if (security_token_is_anonymous(ptoken)) {
+ ptoken->privilege_mask = 0;
+ return NT_STATUS_OK;
+ }
+
+ DEBUG(0, ("Created token was not system or anonymous token!"));
+ *token = NULL;
+ return NT_STATUS_INTERNAL_ERROR;
+}
+
+static NTSTATUS generate_session_info(TALLOC_CTX *mem_ctx,
+ struct auth_serversupplied_info *server_info,
+ struct auth_session_info **_session_info)
+{
+ struct auth_session_info *session_info;
+ NTSTATUS nt_status;
+
+ session_info = talloc(mem_ctx, struct auth_session_info);
+ NT_STATUS_HAVE_NO_MEMORY(session_info);
+
+ session_info->server_info = talloc_reference(session_info, server_info);
+
+ /* unless set otherwise, the session key is the user session
+ * key from the auth subsystem */
+ session_info->session_key = server_info->user_session_key;
+
+ nt_status = create_token(session_info,
+ server_info->account_sid,
+ server_info->primary_group_sid,
+ server_info->n_domain_groups,
+ server_info->domain_groups,
+ server_info->authenticated,
+ &session_info->security_token);
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+
+ session_info->credentials = NULL;
+
+ *_session_info = session_info;
+ return NT_STATUS_OK;
+}
+
+
+
+/* Create a security token for a session SYSTEM (the most
+ * trusted/prvilaged account), including the local machine account as
+ * the off-host credentials
+ */
+_PUBLIC_ struct auth_session_info *system_session(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx)
+{
+ NTSTATUS nt_status;
+ struct auth_session_info *session_info = NULL;
+ nt_status = auth_system_session_info(mem_ctx,
+ lp_ctx,
+ &session_info);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return NULL;
+ }
+ return session_info;
+}
+
+static NTSTATUS _auth_system_session_info(TALLOC_CTX *parent_ctx,
+ struct loadparm_context *lp_ctx,
+ bool anonymous_credentials,
+ struct auth_session_info **_session_info)
+{
+ NTSTATUS nt_status;
+ struct auth_serversupplied_info *server_info = NULL;
+ struct auth_session_info *session_info = NULL;
+ TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
+
+ nt_status = auth_system_server_info(mem_ctx, lp_netbios_name(lp_ctx),
+ &server_info);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(mem_ctx);
+ return nt_status;
+ }
+
+ /* references the server_info into the session_info */
+ nt_status = generate_session_info(parent_ctx, server_info, &session_info);
+ talloc_free(mem_ctx);
+
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+
+ session_info->credentials = cli_credentials_init(session_info);
+ if (!session_info->credentials) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ cli_credentials_set_conf(session_info->credentials, lp_ctx);
+
+ if (anonymous_credentials) {
+ cli_credentials_set_anonymous(session_info->credentials);
+ } else {
+ cli_credentials_set_machine_account_pending(session_info->credentials, lp_ctx);
+ }
+ *_session_info = session_info;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ Create a system session, but with anonymous credentials (so we do not need to open secrets.ldb)
+*/
+_PUBLIC_ struct auth_session_info *system_session_anon(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx)
+{
+ NTSTATUS nt_status;
+ struct auth_session_info *session_info = NULL;
+ nt_status = _auth_system_session_info(mem_ctx, lp_ctx, false, &session_info);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return NULL;
+ }
+ return session_info;
+}
+
+
+
+_PUBLIC_ NTSTATUS auth_system_session_info(TALLOC_CTX *parent_ctx,
+ struct loadparm_context *lp_ctx,
+ struct auth_session_info **_session_info)
+{
+ return _auth_system_session_info(parent_ctx,
+ lp_ctx,
+ lp_parm_bool(lp_ctx, NULL, "system", "anonymous", false),
+ _session_info);
+}
+
+NTSTATUS auth_system_server_info(TALLOC_CTX *mem_ctx, const char *netbios_name,
+ struct auth_serversupplied_info **_server_info)
+{
+ struct auth_serversupplied_info *server_info;
+
+ server_info = talloc(mem_ctx, struct auth_serversupplied_info);
+ NT_STATUS_HAVE_NO_MEMORY(server_info);
+
+ server_info->account_sid = dom_sid_parse_talloc(server_info, SID_NT_SYSTEM);
+ NT_STATUS_HAVE_NO_MEMORY(server_info->account_sid);
+
+ /* is this correct? */
+ server_info->primary_group_sid = dom_sid_parse_talloc(server_info, SID_BUILTIN_ADMINISTRATORS);
+ NT_STATUS_HAVE_NO_MEMORY(server_info->primary_group_sid);
+
+ server_info->n_domain_groups = 0;
+ server_info->domain_groups = NULL;
+
+ /* annoying, but the Anonymous really does have a session key,
+ and it is all zeros! */
+ server_info->user_session_key = data_blob_talloc(server_info, NULL, 16);
+ NT_STATUS_HAVE_NO_MEMORY(server_info->user_session_key.data);
+
+ server_info->lm_session_key = data_blob_talloc(server_info, NULL, 16);
+ NT_STATUS_HAVE_NO_MEMORY(server_info->lm_session_key.data);
+
+ data_blob_clear(&server_info->user_session_key);
+ data_blob_clear(&server_info->lm_session_key);
+
+ server_info->account_name = talloc_strdup(server_info, "SYSTEM");
+ NT_STATUS_HAVE_NO_MEMORY(server_info->account_name);
+
+ server_info->domain_name = talloc_strdup(server_info, "NT AUTHORITY");
+ NT_STATUS_HAVE_NO_MEMORY(server_info->domain_name);
+
+ server_info->full_name = talloc_strdup(server_info, "System");
+ NT_STATUS_HAVE_NO_MEMORY(server_info->full_name);
+
+ server_info->logon_script = talloc_strdup(server_info, "");
+ NT_STATUS_HAVE_NO_MEMORY(server_info->logon_script);
+
+ server_info->profile_path = talloc_strdup(server_info, "");
+ NT_STATUS_HAVE_NO_MEMORY(server_info->profile_path);
+
+ server_info->home_directory = talloc_strdup(server_info, "");
+ NT_STATUS_HAVE_NO_MEMORY(server_info->home_directory);
+
+ server_info->home_drive = talloc_strdup(server_info, "");
+ NT_STATUS_HAVE_NO_MEMORY(server_info->home_drive);
+
+ server_info->logon_server = talloc_strdup(server_info, netbios_name);
+ NT_STATUS_HAVE_NO_MEMORY(server_info->logon_server);
+
+ server_info->last_logon = 0;
+ server_info->last_logoff = 0;
+ server_info->acct_expiry = 0;
+ server_info->last_password_change = 0;
+ server_info->allow_password_change = 0;
+ server_info->force_password_change = 0;
+
+ server_info->logon_count = 0;
+ server_info->bad_password_count = 0;
+
+ server_info->acct_flags = ACB_NORMAL;
+
+ server_info->authenticated = true;
+
+ *_server_info = server_info;
+
+ return NT_STATUS_OK;
+}
+
+
diff --git a/source4/auth/tests/bindings.py b/source4/auth/tests/bindings.py
new file mode 100644
index 0000000000..15368a4bf7
--- /dev/null
+++ b/source4/auth/tests/bindings.py
@@ -0,0 +1,34 @@
+#!/usr/bin/python
+
+# Unix SMB/CIFS implementation.
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 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 <http://www.gnu.org/licenses/>.
+#
+
+"""Tests for the Auth Python bindings.
+
+Note that this just tests the bindings work. It does not intend to test
+the functionality, that's already done in other tests.
+"""
+
+import unittest
+from samba import auth
+
+class AuthTests(unittest.TestCase):
+ def test_system_session(self):
+ auth.system_session()
+
+ def test_system_session_anon(self):
+ auth.system_session_anonymous()
diff --git a/source4/autogen.sh b/source4/autogen.sh
new file mode 100755
index 0000000000..8b97023073
--- /dev/null
+++ b/source4/autogen.sh
@@ -0,0 +1,79 @@
+#!/bin/sh
+
+# Run this script to build samba from git.
+
+while true; do
+ case $1 in
+ --version-file)
+ VERSION_FILE=$2
+ shift 2
+ ;;
+ *)
+ break
+ ;;
+ esac
+done
+
+## insert all possible names (only works with
+## autoconf 2.x)
+TESTAUTOHEADER="autoheader autoheader-2.53 autoheader2.50 autoheader259 autoheader253"
+TESTAUTOCONF="autoconf autoconf-2.53 autoconf2.50 autoconf259 autoconf253"
+
+AUTOHEADERFOUND="0"
+AUTOCONFFOUND="0"
+
+
+##
+## Look for autoheader
+##
+for i in $TESTAUTOHEADER; do
+ if which $i > /dev/null 2>&1; then
+ if test `$i --version | head -n 1 | cut -d. -f 2 | sed "s/[^0-9]//g"` -ge 53; then
+ AUTOHEADER=$i
+ AUTOHEADERFOUND="1"
+ break
+ fi
+ fi
+done
+
+##
+## Look for autoconf
+##
+
+for i in $TESTAUTOCONF; do
+ if which $i > /dev/null 2>&1; then
+ if test `$i --version | head -n 1 | cut -d. -f 2 | sed "s/[^0-9]//g"` -ge 53; then
+ AUTOCONF=$i
+ AUTOCONFFOUND="1"
+ break
+ fi
+ fi
+done
+
+
+##
+## do we have it?
+##
+if test "$AUTOCONFFOUND" = "0" -o "$AUTOHEADERFOUND" = "0"; then
+ echo "$0: need autoconf 2.53 or later to build samba from git" >&2
+ exit 1
+fi
+
+echo "$0: running script/mkversion.sh"
+./script/mkversion.sh $VERSION_FILE || exit 1
+
+rm -rf autom4te*.cache
+rm -f configure include/config_tmp.h*
+
+IPATHS="-I. -I../lib/replace"
+
+echo "$0: running $AUTOHEADER $IPATHS"
+$AUTOHEADER $IPATHS || exit 1
+
+echo "$0: running $AUTOCONF $IPATHS"
+$AUTOCONF $IPATHS || exit 1
+
+rm -rf autom4te*.cache
+
+echo "Now run ./configure and then make."
+exit 0
diff --git a/source4/build/m4/ax_cflags_gcc_option.m4 b/source4/build/m4/ax_cflags_gcc_option.m4
new file mode 100644
index 0000000000..ec01a000aa
--- /dev/null
+++ b/source4/build/m4/ax_cflags_gcc_option.m4
@@ -0,0 +1,109 @@
+dnl @synopsis AX_CFLAGS_GCC_OPTION (optionflag [,[shellvar][,[A][,[NA]]])
+dnl
+dnl AX_CFLAGS_GCC_OPTION(-fvomit-frame) would show a message as like
+dnl "checking CFLAGS for gcc -fvomit-frame ... yes" and adds
+dnl the optionflag to CFLAGS if it is understood. You can override
+dnl the shellvar-default of CFLAGS of course. The order of arguments
+dnl stems from the explicit macros like AX_CFLAGS_WARN_ALL.
+dnl
+dnl The macro is a lot simpler than any special AX_CFLAGS_* macro (or
+dnl ac_cxx_rtti.m4 macro) but allows to check for arbitrary options.
+dnl However, if you use this macro in a few places, it would be great
+dnl if you would make up a new function-macro and submit it to the
+dnl ac-archive.
+dnl
+dnl - $1 option-to-check-for : required ("-option" as non-value)
+dnl - $2 shell-variable-to-add-to : CFLAGS
+dnl - $3 action-if-found : add value to shellvariable
+dnl - $4 action-if-not-found : nothing
+dnl
+dnl note: in earlier versions, $1-$2 were swapped. We try to detect the
+dnl situation and accept a $2=~/-/ as being the old option-to-check-for.
+dnl
+dnl also: there are other variants that emerged from the original macro
+dnl variant which did just test an option to be possibly added. However,
+dnl some compilers accept an option silently, or possibly for just
+dnl another option that was not intended. Therefore, we have to do a
+dnl generic test for a compiler family. For gcc we check "-pedantic"
+dnl being accepted which is also understood by compilers who just want
+dnl to be compatible with gcc even when not being made from gcc sources.
+dnl
+dnl see also:
+dnl AX_CFLAGS_SUN_OPTION AX_CFLAGS_HPUX_OPTION
+dnl AX_CFLAGS_AIX_OPTION AX_CFLAGS_IRIX_OPTION
+dnl
+dnl @, tested, experimental
+dnl @version $Id: ax_cflags_gcc_option.m4,v 1.5 2003/11/29 08:13:25 guidod Exp $
+dnl @author Guido Draheim <guidod@gmx.de>
+dnl http://ac-archive.sourceforge.net/C_Support/ax_cflags_gcc_option.m4
+dnl
+AC_DEFUN([AX_CFLAGS_GCC_OPTION_OLD], [dnl
+AS_VAR_PUSHDEF([FLAGS],[CFLAGS])dnl
+AS_VAR_PUSHDEF([VAR],[ac_cv_cflags_gcc_option_$2])dnl
+AC_CACHE_CHECK([m4_ifval($1,$1,FLAGS) for gcc m4_ifval($2,$2,-option)],
+VAR,[VAR="no, unknown"
+ AC_LANG_SAVE
+ AC_LANG_C
+ ac_save_[]FLAGS="$[]FLAGS"
+for ac_arg dnl
+in "-pedantic % m4_ifval($2,$2,-option)" dnl GCC
+ #
+do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'`
+ AC_TRY_COMPILE([],[return 0;],
+ [VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break])
+done
+ FLAGS="$ac_save_[]FLAGS"
+ AC_LANG_RESTORE
+])
+case ".$VAR" in
+ .ok|.ok,*) m4_ifvaln($3,$3) ;;
+ .|.no|.no,*) m4_ifvaln($4,$4) ;;
+ *) m4_ifvaln($3,$3,[
+ if echo " $[]m4_ifval($1,$1,FLAGS) " | grep " $VAR " 2>&1 >/dev/null
+ then AC_RUN_LOG([: m4_ifval($1,$1,FLAGS) does contain $VAR])
+ else AC_RUN_LOG([: m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR"])
+ m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR"
+ fi ]) ;;
+esac
+AS_VAR_POPDEF([VAR])dnl
+AS_VAR_POPDEF([FLAGS])dnl
+])
+
+
+dnl -------------------------------------------------------------------------
+
+AC_DEFUN([AX_CFLAGS_GCC_OPTION_NEW], [dnl
+AS_VAR_PUSHDEF([FLAGS],[CFLAGS])dnl
+AS_VAR_PUSHDEF([VAR],[ac_cv_cflags_gcc_option_$1])dnl
+AC_CACHE_CHECK([m4_ifval($2,$2,FLAGS) for gcc m4_ifval($1,$1,-option)],
+VAR,[VAR="no, unknown"
+ AC_LANG_SAVE
+ AC_LANG_C
+ ac_save_[]FLAGS="$[]FLAGS"
+for ac_arg dnl
+in "-pedantic % m4_ifval($1,$1,-option)" dnl GCC
+ #
+do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'`
+ AC_TRY_COMPILE([],[return 0;],
+ [VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break])
+done
+ FLAGS="$ac_save_[]FLAGS"
+ AC_LANG_RESTORE
+])
+case ".$VAR" in
+ .ok|.ok,*) m4_ifvaln($3,$3) ;;
+ .|.no|.no,*) m4_ifvaln($4,$4) ;;
+ *) m4_ifvaln($3,$3,[
+ if echo " $[]m4_ifval($2,$2,FLAGS) " | grep " $VAR " 2>&1 >/dev/null
+ then AC_RUN_LOG([: m4_ifval($2,$2,FLAGS) does contain $VAR])
+ else AC_RUN_LOG([: m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $VAR"])
+ m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $VAR"
+ fi ]) ;;
+esac
+AS_VAR_POPDEF([VAR])dnl
+AS_VAR_POPDEF([FLAGS])dnl
+])
+
+
+AC_DEFUN([AX_CFLAGS_GCC_OPTION],[ifelse(m4_bregexp([$2],[-]),-1,
+[AX_CFLAGS_GCC_OPTION_NEW($@)],[AX_CFLAGS_GCC_OPTION_OLD($@)])])
diff --git a/source4/build/m4/ax_cflags_irix_option.m4 b/source4/build/m4/ax_cflags_irix_option.m4
new file mode 100644
index 0000000000..f7fe2a19b0
--- /dev/null
+++ b/source4/build/m4/ax_cflags_irix_option.m4
@@ -0,0 +1,174 @@
+dnl @synopsis AX_CFLAGS_IRIX_OPTION (optionflag [,[shellvar][,[A][,[NA]]])
+dnl
+dnl AX_CFLAGS_IRIX_OPTION(-go_for_it) would show a message as like
+dnl "checking CFLAGS for irix/cc -go_for_it ... yes" and adds the
+dnl optionflag to CFLAGS if it is understood. You can override the
+dnl shellvar-default of CFLAGS of course. The order of arguments stems
+dnl from the explicit macros like AX_CFLAGS_WARN_ALL.
+dnl
+dnl The cousin AX_CXXFLAGS_IRIX_OPTION would check for an option to add
+dnl to CXXFLAGS - and it uses the autoconf setup for C++ instead of C
+dnl (since it is possible to use different compilers for C and C++).
+dnl
+dnl The macro is a lot simpler than any special AX_CFLAGS_* macro (or
+dnl ac_cxx_rtti.m4 macro) but allows to check for arbitrary options.
+dnl However, if you use this macro in a few places, it would be great
+dnl if you would make up a new function-macro and submit it to the
+dnl ac-archive.
+dnl
+dnl - $1 option-to-check-for : required ("-option" as non-value)
+dnl - $2 shell-variable-to-add-to : CFLAGS (or CXXFLAGS in the other case)
+dnl - $3 action-if-found : add value to shellvariable
+dnl - $4 action-if-not-found : nothing
+dnl
+dnl note: in earlier versions, $1-$2 were swapped. We try to detect the
+dnl situation and accept a $2=~/-/ as being the old
+dnl option-to-check-for.
+dnl
+dnl see also: AX_CFLAGS_GCC_OPTION for the widely used original
+dnl variant.
+dnl
+dnl @category C
+dnl @author Guido Draheim <guidod@gmx.de>
+dnl @version 2005-01-21
+dnl @license GPLWithACException
+
+AC_DEFUN([AX_CFLAGS_IRIX_OPTION_OLD], [dnl
+AS_VAR_PUSHDEF([FLAGS],[CFLAGS])dnl
+AS_VAR_PUSHDEF([VAR],[ac_cv_cflags_irix_option_$2])dnl
+AC_CACHE_CHECK([m4_ifval($1,$1,FLAGS) for irix/cc m4_ifval($2,$2,-option)],
+VAR,[VAR="no, unknown"
+ AC_LANG_SAVE
+ AC_LANG_C
+ ac_save_[]FLAGS="$[]FLAGS"
+for ac_arg dnl
+in "-fullwarn -use_readonly_const % m4_ifval($2,$2,-option)" dnl IRIX C
+ #
+do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'`
+ AC_TRY_COMPILE([],[return 0;],
+ [VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break])
+done
+ FLAGS="$ac_save_[]FLAGS"
+ AC_LANG_RESTORE
+])
+case ".$VAR" in
+ .ok|.ok,*) m4_ifvaln($3,$3) ;;
+ .|.no|.no,*) m4_ifvaln($4,$4) ;;
+ *) m4_ifvaln($3,$3,[
+ if echo " $[]m4_ifval($1,$1,FLAGS) " | grep " $VAR " 2>&1 >/dev/null
+ then AC_RUN_LOG([: m4_ifval($1,$1,FLAGS) does contain $VAR])
+ else AC_RUN_LOG([: m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR"])
+ m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR"
+ fi ]) ;;
+esac
+AS_VAR_POPDEF([VAR])dnl
+AS_VAR_POPDEF([FLAGS])dnl
+])
+
+dnl the only difference - the LANG selection... and the default FLAGS
+
+AC_DEFUN([AX_CXXFLAGS_IRIX_OPTION_OLD], [dnl
+AS_VAR_PUSHDEF([FLAGS],[CXXFLAGS])dnl
+AS_VAR_PUSHDEF([VAR],[ac_cv_cxxflags_irix_option_$2])dnl
+AC_CACHE_CHECK([m4_ifval($1,$1,FLAGS) for irix/cc m4_ifval($2,$2,-option)],
+VAR,[VAR="no, unknown"
+ AC_LANG_SAVE
+ AC_LANG_CXX
+ ac_save_[]FLAGS="$[]FLAGS"
+for ac_arg dnl
+in "-fullwarn -use_readonly_const % m4_ifval($2,$2,-option)" dnl IRIX C
+ #
+do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'`
+ AC_TRY_COMPILE([],[return 0;],
+ [VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break])
+done
+ FLAGS="$ac_save_[]FLAGS"
+ AC_LANG_RESTORE
+])
+case ".$VAR" in
+ .ok|.ok,*) m4_ifvaln($3,$3) ;;
+ .|.no|.no,*) m4_ifvaln($4,$4) ;;
+ *) m4_ifvaln($3,$3,[
+ if echo " $[]m4_ifval($1,$1,FLAGS) " | grep " $VAR " 2>&1 >/dev/null
+ then AC_RUN_LOG([: m4_ifval($1,$1,FLAGS) does contain $VAR])
+ else AC_RUN_LOG([: m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR"])
+ m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR"
+ fi ]) ;;
+esac
+AS_VAR_POPDEF([VAR])dnl
+AS_VAR_POPDEF([FLAGS])dnl
+])
+
+dnl --------------------------------------------------------------------------
+
+AC_DEFUN([AX_CFLAGS_IRIX_OPTION_NEW], [dnl
+AS_VAR_PUSHDEF([FLAGS],[CFLAGS])dnl
+AS_VAR_PUSHDEF([VAR],[ac_cv_cflags_irix_option_$1])dnl
+AC_CACHE_CHECK([m4_ifval($2,$2,FLAGS) for irix/cc m4_ifval($1,$1,-option)],
+VAR,[VAR="no, unknown"
+ AC_LANG_SAVE
+ AC_LANG_C
+ ac_save_[]FLAGS="$[]FLAGS"
+for ac_arg dnl
+in "-fullwarn -use_readonly_const % m4_ifval($1,$1,-option)" dnl IRIX C
+ #
+do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'`
+ AC_TRY_COMPILE([],[return 0;],
+ [VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break])
+done
+ FLAGS="$ac_save_[]FLAGS"
+ AC_LANG_RESTORE
+])
+case ".$VAR" in
+ .ok|.ok,*) m4_ifvaln($3,$3) ;;
+ .|.no|.no,*) m4_ifvaln($4,$4) ;;
+ *) m4_ifvaln($3,$3,[
+ if echo " $[]m4_ifval($2,$2,FLAGS) " | grep " $VAR " 2>&1 >/dev/null
+ then AC_RUN_LOG([: m4_ifval($2,$2,FLAGS) does contain $VAR])
+ else AC_RUN_LOG([: m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $VAR"])
+ m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $VAR"
+ fi ]) ;;
+esac
+AS_VAR_POPDEF([VAR])dnl
+AS_VAR_POPDEF([FLAGS])dnl
+])
+
+dnl the only difference - the LANG selection... and the default FLAGS
+
+AC_DEFUN([AX_CXXFLAGS_IRIX_OPTION_NEW], [dnl
+AS_VAR_PUSHDEF([FLAGS],[CXXFLAGS])dnl
+AS_VAR_PUSHDEF([VAR],[ac_cv_cxxflags_irix_option_$1])dnl
+AC_CACHE_CHECK([m4_ifval($2,$2,FLAGS) for irix/cc m4_ifval($1,$1,-option)],
+VAR,[VAR="no, unknown"
+ AC_LANG_SAVE
+ AC_LANG_CXX
+ ac_save_[]FLAGS="$[]FLAGS"
+for ac_arg dnl
+in "-fullwarn -use_readonly_const % m4_ifval($1,$1,-option)" dnl IRIX C
+ #
+do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'`
+ AC_TRY_COMPILE([],[return 0;],
+ [VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break])
+done
+ FLAGS="$ac_save_[]FLAGS"
+ AC_LANG_RESTORE
+])
+case ".$VAR" in
+ .ok|.ok,*) m4_ifvaln($3,$3) ;;
+ .|.no|.no,*) m4_ifvaln($4,$4) ;;
+ *) m4_ifvaln($3,$3,[
+ if echo " $[]m4_ifval($2,$2,FLAGS) " | grep " $VAR " 2>&1 >/dev/null
+ then AC_RUN_LOG([: m4_ifval($2,$2,FLAGS) does contain $VAR])
+ else AC_RUN_LOG([: m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $VAR"])
+ m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $VAR"
+ fi ]) ;;
+esac
+AS_VAR_POPDEF([VAR])dnl
+AS_VAR_POPDEF([FLAGS])dnl
+])
+
+AC_DEFUN([AX_CFLAGS_IRIX_OPTION],[ifelse(m4_bregexp([$2],[-]),-1,
+[AX_CFLAGS_IRIX_OPTION_NEW($@)],[AX_CFLAGS_IRIX_OPTION_OLD($@)])])
+
+AC_DEFUN([AX_CXXFLAGS_IRIX_OPTION],[ifelse(m4_bregexp([$2],[-]),-1,
+[AX_CXXFLAGS_IRIX_OPTION_NEW($@)],[AX_CXXFLAGS_IRIX_OPTION_OLD($@)])])
diff --git a/source4/build/m4/check_cc.m4 b/source4/build/m4/check_cc.m4
new file mode 100644
index 0000000000..55802850b0
--- /dev/null
+++ b/source4/build/m4/check_cc.m4
@@ -0,0 +1,160 @@
+dnl SMB Build Environment CC Checks
+dnl -------------------------------------------------------
+dnl Copyright (C) Stefan (metze) Metzmacher 2004
+dnl Released under the GNU GPL
+dnl -------------------------------------------------------
+dnl
+
+AC_LIBREPLACE_CC_CHECKS
+
+#
+# Set the debug symbol option if we have
+# --enable-*developer or --enable-debug
+# and the compiler supports it
+#
+if test x$ac_cv_prog_cc_g = xyes -a x$debug = xyes; then
+ CFLAGS="${CFLAGS} -g"
+fi
+
+############################################
+# check if the compiler handles c99 struct initialization
+LIBREPLACE_C99_STRUCT_INIT(samba_cv_c99_struct_initialization=yes,
+ samba_cv_c99_struct_initialization=no)
+
+if test x"$samba_cv_c99_struct_initialization" != x"yes"; then
+ AC_MSG_WARN([C compiler does not support c99 struct initialization!])
+ AC_MSG_ERROR([Please Install gcc from http://gcc.gnu.org/])
+fi
+
+############################################
+# check if the compiler can handle negative enum values
+# and don't truncate the values to INT_MAX
+# a runtime test is needed here
+AC_CACHE_CHECK([that the C compiler understands negative enum values],samba_cv_CC_NEGATIVE_ENUM_VALUES, [
+ AC_TRY_RUN(
+[
+ #include <stdio.h>
+ enum negative_values { NEGATIVE_VALUE = 0xFFFFFFFF };
+ int main(void) {
+ enum negative_values v1 = NEGATIVE_VALUE;
+ unsigned v2 = 0xFFFFFFFF;
+ if (v1 != v2) {
+ printf("v1=0x%08x v2=0x%08x\n", v1, v2);
+ return 1;
+ }
+ return 0;
+ }
+],
+ samba_cv_CC_NEGATIVE_ENUM_VALUES=yes,samba_cv_CC_NEGATIVE_ENUM_VALUES=no)])
+if test x"$samba_cv_CC_NEGATIVE_ENUM_VALUES" != x"yes"; then
+ AC_DEFINE(USE_UINT_ENUMS, 1, [Whether the compiler has uint enum support])
+fi
+
+AC_MSG_CHECKING([for test routines])
+AC_TRY_RUN([#include "${srcdir-.}/../tests/trivial.c"],
+ AC_MSG_RESULT(yes),
+ AC_MSG_ERROR([cant find test code. Aborting config]),
+ AC_MSG_WARN([cannot run when cross-compiling]))
+
+#
+# Check if the compiler support ELF visibility for symbols
+#
+
+visibility_attribute=no
+VISIBILITY_CFLAGS=""
+if test x"$GCC" = x"yes" ; then
+ AX_CFLAGS_GCC_OPTION([-fvisibility=hidden], VISIBILITY_CFLAGS)
+fi
+
+if test -n "$VISIBILITY_CFLAGS"; then
+ AC_MSG_CHECKING([whether the C compiler supports the visibility attribute])
+ OLD_CFLAGS="$CFLAGS"
+
+ CFLAGS="$CFLAGS $VISIBILITY_CFLAGS"
+ AC_TRY_LINK([
+ void vis_foo1(void) {}
+ __attribute__((visibility("default"))) void vis_foo2(void) {}
+ ],[
+ ],[
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(HAVE_VISIBILITY_ATTR,1,[Whether the C compiler supports the visibility attribute])
+ visibility_attribute=yes
+ ],[
+ AC_MSG_RESULT(no)
+ ])
+ CFLAGS="$OLD_CFLAGS"
+fi
+AC_SUBST(visibility_attribute)
+
+#
+# Check if the compiler can handle the options we selected by
+# --enable-*developer
+#
+DEVELOPER_CFLAGS=""
+if test x$developer = xyes; then
+ OLD_CFLAGS="${CFLAGS}"
+
+ CFLAGS="${CFLAGS} -D_SAMBA_DEVELOPER_DONNOT_USE_O2_"
+ DEVELOPER_CFLAGS="-DDEBUG_PASSWORD -DDEVELOPER"
+ if test x"$GCC" = x"yes" ; then
+ #
+ # warnings we want...
+ #
+ AX_CFLAGS_GCC_OPTION(-Wall, DEVELOPER_CFLAGS)
+ AX_CFLAGS_GCC_OPTION(-Wshadow, DEVELOPER_CFLAGS)
+ AX_CFLAGS_GCC_OPTION(-Werror-implicit-function-declaration, DEVELOPER_CFLAGS)
+ AX_CFLAGS_GCC_OPTION(-Wstrict-prototypes, DEVELOPER_CFLAGS)
+ AX_CFLAGS_GCC_OPTION(-Wpointer-arith, DEVELOPER_CFLAGS)
+ AX_CFLAGS_GCC_OPTION(-Wcast-qual, DEVELOPER_CFLAGS)
+ AX_CFLAGS_GCC_OPTION(-Wcast-align, DEVELOPER_CFLAGS)
+ AX_CFLAGS_GCC_OPTION(-Wwrite-strings, DEVELOPER_CFLAGS)
+ AX_CFLAGS_GCC_OPTION(-Wmissing-format-attribute, DEVELOPER_CFLAGS)
+ AX_CFLAGS_GCC_OPTION(-Wformat=2, DEVELOPER_CFLAGS)
+ AX_CFLAGS_GCC_OPTION(-Wdeclaration-after-statement, DEVELOPER_CFLAGS)
+ AX_CFLAGS_GCC_OPTION(-Wunused-macros, DEVELOPER_CFLAGS)
+# AX_CFLAGS_GCC_OPTION(-Wextra, DEVELOPER_CFLAGS)
+# AX_CFLAGS_GCC_OPTION(-Wc++-compat, DEVELOPER_CFLAGS)
+# AX_CFLAGS_GCC_OPTION(-Wmissing-prototypes, DEVELOPER_CFLAGS)
+# AX_CFLAGS_GCC_OPTION(-Wmissing-declarations, DEVELOPER_CFLAGS)
+# AX_CFLAGS_GCC_OPTION(-Wmissing-field-initializers, DEVELOPER_CFLAGS)
+ #
+ # warnings we don't want...
+ #
+ AX_CFLAGS_GCC_OPTION(-Wno-format-y2k, DEVELOPER_CFLAGS)
+ AX_CFLAGS_GCC_OPTION(-Wno-unused-parameter, DEVELOPER_CFLAGS)
+ #
+ # warnings we don't want just for some files e.g. swig bindings
+ #
+ AX_CFLAGS_GCC_OPTION(-Wno-cast-qual, CFLAG_NO_CAST_QUAL)
+ AC_SUBST(CFLAG_NO_CAST_QUAL)
+ AX_CFLAGS_GCC_OPTION(-Wno-unused-macros, CFLAG_NO_UNUSED_MACROS)
+ AC_SUBST(CFLAG_NO_UNUSED_MACROS)
+ else
+ AX_CFLAGS_IRIX_OPTION(-fullwarn, DEVELOPER_CFLAGS)
+ fi
+
+ CFLAGS="${OLD_CFLAGS}"
+fi
+if test -n "$DEVELOPER_CFLAGS"; then
+ OLD_CFLAGS="${CFLAGS}"
+ CFLAGS="${CFLAGS} ${DEVELOPER_CFLAGS}"
+ AC_MSG_CHECKING([that the C compiler can use the DEVELOPER_CFLAGS])
+ AC_TRY_COMPILE([],[],
+ AC_MSG_RESULT(yes),
+ DEVELOPER_CFLAGS=""; AC_MSG_RESULT(no))
+ CFLAGS="${OLD_CFLAGS}"
+fi
+
+# allow for --with-hostcc=gcc
+AC_ARG_WITH(hostcc,[ --with-hostcc=compiler choose host compiler],
+[HOSTCC=$withval],
+[
+if test z"$cross_compiling" = "yes"; then
+ HOSTCC=cc
+else
+ HOSTCC=$CC
+fi
+])
+AC_SUBST(HOSTCC)
+
+AC_PATH_PROG(GCOV,gcov)
diff --git a/source4/build/m4/check_doc.m4 b/source4/build/m4/check_doc.m4
new file mode 100644
index 0000000000..2aaf9596de
--- /dev/null
+++ b/source4/build/m4/check_doc.m4
@@ -0,0 +1 @@
+AC_PATH_PROG(XSLTPROC, xsltproc)
diff --git a/source4/build/m4/check_ld.m4 b/source4/build/m4/check_ld.m4
new file mode 100644
index 0000000000..48d6b333ef
--- /dev/null
+++ b/source4/build/m4/check_ld.m4
@@ -0,0 +1,187 @@
+dnl SMB Build Environment LD Checks
+dnl -------------------------------------------------------
+dnl Copyright (C) Stefan (metze) Metzmacher 2004
+dnl Copyright (C) Jelmer Vernooij 2006
+dnl Released under the GNU GPL
+dnl -------------------------------------------------------
+dnl
+
+AC_PATH_PROG(PROG_LD,ld)
+LD=${PROG_LD}
+AC_PROG_LD_GNU
+LD=""
+
+AC_SUBST(BLDSHARED)
+AC_SUBST(LD)
+AC_SUBST(SYS_LDFLAGS)
+AC_SUBST(LDFLAGS)
+
+# Assume non-shared by default and override below
+# these are the defaults, good for lots of systems
+BLDSHARED="false"
+LD="${CC}"
+PICFLAG=""
+
+# allow for --with-hostld=gcc
+AC_ARG_WITH(hostld,[ --with-hostld=linker choose host linker],
+[HOSTLD=$withval],
+[HOSTLD=$HOSTCC])
+
+AC_MSG_CHECKING([whether to try to build shared libraries on $host_os])
+
+# and these are for particular systems
+case "$host_os" in
+ *linux*)
+ BLDSHARED="true"
+ SYS_LDFLAGS="-Wl,--export-dynamic"
+ ;;
+ *solaris*)
+ BLDSHARED="true"
+ if test "${GCC}" = "yes"; then
+ if test "${ac_cv_prog_gnu_ld}" = "yes"; then
+ SYS_LDFLAGS="-Wl,-E"
+ fi
+ fi
+ ;;
+ *sunos*)
+ BLDSHARED="true"
+ ;;
+ *netbsd* | *freebsd* | *dragonfly* )
+ BLDSHARED="true"
+ SYS_LDFLAGS="-Wl,--export-dynamic"
+ ;;
+ *openbsd*)
+ BLDSHARED="true"
+ SYS_LDFLAGS="-Wl,-Bdynamic"
+ ;;
+ *irix*)
+ BLDSHARED="true"
+ ;;
+ *aix*)
+ BLDSHARED="true"
+ SYS_LDFLAGS="-Wl,-brtl,-bexpall,-bbigtoc"
+ ;;
+ *hpux*)
+ # Use special PIC flags for the native HP-UX compiler.
+ BLDSHARED="true" # I hope this is correct
+ if test "$host_cpu" = "ia64"; then
+ SYS_LDFLAGS="-Wl,-E,+b/usr/local/lib/hpux32:/usr/lib/hpux32"
+ else
+ SYS_LDFLAGS="-Wl,-E,+b/usr/local/lib:/usr/lib"
+ fi
+ ;;
+ *osf*)
+ BLDSHARED="true"
+ ;;
+ *unixware*)
+ BLDSHARED="true"
+ ;;
+ *darwin*)
+ BLDSHARED="true"
+ ;;
+esac
+
+AC_MSG_RESULT($BLDSHARED)
+
+AC_MSG_CHECKING([LD])
+AC_MSG_RESULT([$LD])
+AC_MSG_CHECKING([LDFLAGS])
+AC_MSG_RESULT([$LDFLAGS])
+AC_MSG_CHECKING([SYS_LDFLAGS])
+AC_MSG_RESULT([$SYS_LDFLAGS])
+
+AC_SUBST(HOSTLD)
+
+AC_LIBREPLACE_STLD
+AC_LIBREPLACE_STLD_FLAGS
+
+AC_MSG_CHECKING([STLD])
+AC_MSG_RESULT([$STLD])
+AC_MSG_CHECKING([STLD_FLAGS])
+AC_MSG_RESULT([$STLD_FLAGS])
+
+AC_LD_PICFLAG
+AC_LD_EXPORT_DYNAMIC
+AC_LD_SHLIBEXT
+AC_LD_SONAMEFLAG
+AC_LIBREPLACE_SHLD
+AC_LIBREPLACE_SHLD_FLAGS
+AC_LIBREPLACE_MDLD
+AC_LIBREPLACE_MDLD_FLAGS
+AC_LIBREPLACE_RUNTIME_LIB_PATH_VAR
+
+#######################################################
+# test whether building a shared library actually works
+if test $BLDSHARED = true; then
+
+ AC_MSG_CHECKING([SHLD])
+ AC_MSG_RESULT([$SHLD])
+ AC_MSG_CHECKING([SHLD_FLAGS])
+ AC_MSG_RESULT([$SHLD_FLAGS])
+
+ AC_MSG_CHECKING([MDLD])
+ AC_MSG_RESULT([$MDLD])
+ AC_MSG_CHECKING([MDLD_FLAGS])
+ AC_MSG_RESULT([$MDLD_FLAGS])
+
+ AC_MSG_CHECKING([SHLIBEXT])
+ AC_MSG_RESULT([$SHLIBEXT])
+ AC_MSG_CHECKING([SONAMEFLAG])
+ AC_MSG_RESULT([$SONAMEFLAG])
+
+ AC_MSG_CHECKING([PICFLAG])
+ AC_MSG_RESULT([$PICFLAG])
+
+ AC_CACHE_CHECK([whether building shared libraries actually works],
+ [ac_cv_shlib_works],[
+ ac_cv_shlib_works=no
+ # try building a trivial shared library
+ # TODO: also test SONAMEFLAG
+ ${CC} ${CFLAGS} ${PICFLAG} -c ${srcdir-.}/../tests/shlib.c -o shlib.o &&
+ ${SHLD} ${SHLD_FLAGS} -o shlib.${SHLIBEXT} shlib.o &&
+ ac_cv_shlib_works=yes
+ rm -f shlib.${SHLIBEXT} shlib.o
+ ])
+ AC_CACHE_CHECK([whether building shared modules actually works],
+ [ac_cv_shmod_works],[
+ ac_cv_shmod_works=no
+ # try building a trivial shared library
+ ${CC} ${CFLAGS} ${PICFLAG} -c ${srcdir-.}/../tests/shlib.c -o shlib.o &&
+ ${MDLD} ${MDLD_FLAGS} -o shlib.${SHLIBEXT} shlib.o &&
+ ac_cv_shmod_works=yes
+ rm -f shlib.${SHLIBEXT} shlib.o
+ ])
+ if test $ac_cv_shlib_works = no; then
+ AC_MSG_ERROR(unable to build shared libraries)
+ fi
+ if test $ac_cv_shmod_works = no; then
+ AC_MSG_ERROR(unable to build shared modules)
+ fi
+fi
+
+AC_DEFINE_UNQUOTED(SHLIBEXT, "$SHLIBEXT", [Shared library extension])
+
+AC_MSG_CHECKING([if we can link using the selected flags])
+AC_TRY_RUN([#include "${srcdir-.}/../tests/trivial.c"],
+ AC_MSG_RESULT(yes),
+ AC_MSG_ERROR([we cannot link with the selected cc and ld flags. Aborting configure]),
+ AC_MSG_WARN([cannot run when cross-compiling]))
+
+
+USESHARED=false
+AC_SUBST(USESHARED)
+
+AC_ARG_ENABLE(dso,
+[ --enable-dso Enable using shared libraries internally (experimental)],
+[],[enable_dso=no])
+
+if test x"$enable_dso" = x"yes" -a x"$BLDSHARED" != x"true"; then
+ AC_MSG_ERROR([--enable-dso: no support for shared libraries])
+fi
+
+if test x"$enable_dso" != x"no"; then
+ USESHARED=$BLDSHARED
+fi
+
+AC_MSG_CHECKING([if binaries will use shared libraries])
+AC_MSG_RESULT([$USESHARED])
diff --git a/source4/build/m4/check_path.m4 b/source4/build/m4/check_path.m4
new file mode 100644
index 0000000000..1751a89e5f
--- /dev/null
+++ b/source4/build/m4/check_path.m4
@@ -0,0 +1,217 @@
+dnl SMB Build Environment Path Checks
+dnl -------------------------------------------------------
+dnl Copyright (C) Stefan (metze) Metzmacher 2004
+dnl Released under the GNU GPL
+dnl -------------------------------------------------------
+dnl
+
+AC_LIBREPLACE_LOCATION_CHECKS
+
+#################################################
+# Directory handling stuff to support both the
+# legacy SAMBA directories and FHS compliant
+# ones...
+AC_PREFIX_DEFAULT(/usr/local/samba)
+
+# Defaults and --without-fhs
+logfilebase="${localstatedir}"
+lockdir="${localstatedir}/locks"
+piddir="${localstatedir}/run"
+privatedir="\${prefix}/private"
+modulesdir="\${prefix}/modules"
+winbindd_socket_dir="${localstatedir}/run/winbindd"
+winbindd_privileged_socket_dir="${localstatedir}/lib/winbindd_privileged"
+ntp_signd_socket_dir="${localstatedir}/run/ntp_signd"
+
+AC_ARG_ENABLE(fhs,
+[AS_HELP_STRING([--enable-fhs],[Use FHS-compliant paths (default=no)])],
+[fhs=$enableval],
+[fhs=no]
+)
+
+if test x$fhs = xyes; then
+ lockdir="${localstatedir}/lib/samba"
+ piddir="${localstatedir}/run/samba"
+ logfilebase="${localstatedir}/log/samba"
+ privatedir="${localstatedir}/lib/samba/private"
+ sysconfdir="${sysconfdir}/samba"
+ modulesdir="${libdir}/samba"
+ datadir="${datadir}/samba"
+ includedir="${includedir}/samba-4.0"
+ ntp_signd_socket_dir="${localstatedir}/run/samba/ntp_signd"
+ winbindd_socket_dir="${localstatedir}/run/samba/winbindd"
+ winbindd_privileged_socket_dir="${localstatedir}/lib/samba/winbindd_privileged"
+else
+ # Check to prevent installing directly under /usr without the FHS
+ AS_IF([test $prefix = /usr || test $prefix = /usr/local],[
+ AC_MSG_ERROR([Don't install directly under "/usr" or "/usr/local" without using the FHS option (--enable-fhs). This could lead to file loss!])
+ ])
+fi
+
+#################################################
+# set private directory location
+AC_ARG_WITH(privatedir,
+[AS_HELP_STRING([--with-privatedir=DIR],[Where to put sam.ldb and other private files containing key material ($ac_default_prefix/private)])],
+[ case "$withval" in
+ yes|no)
+ #
+ # Just in case anybody calls it without argument
+ #
+ AC_MSG_WARN([--with-privatedir called without argument - will use default])
+ ;;
+ * )
+ privatedir="$withval"
+ ;;
+ esac])
+
+#################################################
+# set where the winbindd socket should be put
+AC_ARG_WITH(winbindd-socket-dir,
+[AS_HELP_STRING([--with-winbindd-socket-dir=DIR],[Where to put the winbindd socket ($winbindd_socket_dir)])],
+[ case "$withval" in
+ yes|no)
+ #
+ # Just in case anybody calls it without argument
+ #
+ AC_MSG_WARN([--with-winbind-socketdir called without argument - will use default])
+ ;;
+ * )
+ winbindd_socket_dir="$withval"
+ ;;
+ esac])
+
+#################################################
+# set where the winbindd privilaged socket should be put
+AC_ARG_WITH(winbindd-privileged-socket-dir,
+[AS_HELP_STRING([--with-winbindd-privileged-socket-dir=DIR],[Where to put the winbindd socket ($winbindd_privileged_socket_dir)])],
+[ case "$withval" in
+ yes|no)
+ #
+ # Just in case anybody calls it without argument
+ #
+ AC_MSG_WARN([--with-winbind-privileged-socketdir called without argument - will use default])
+ ;;
+ * )
+ winbindd_privileged_socket_dir="$withval"
+ ;;
+ esac])
+
+#################################################
+# set where the NTP signing deamon socket should be put
+AC_ARG_WITH(ntp-signd-socket-dir,
+[AS_HELP_STRING([--with-ntp-signd-socket-dir=DIR],[Where to put the NTP signing deamon socket ($ac_default_prefix/run/ntp_signd)])],
+[ case "$withval" in
+ yes|no)
+ #
+ # Just in case anybody calls it without argument
+ #
+ AC_MSG_WARN([--with-ntp-signd-socketdir called without argument - will use default])
+ ;;
+ * )
+ ntp_signd_socket_dir="$withval"
+ ;;
+ esac])
+
+#################################################
+# set lock directory location
+AC_ARG_WITH(lockdir,
+[AS_HELP_STRING([--with-lockdir=DIR],[Where to put lock files ($ac_default_prefix/var/locks)])],
+[ case "$withval" in
+ yes|no)
+ #
+ # Just in case anybody calls it without argument
+ #
+ AC_MSG_WARN([--with-lockdir called without argument - will use default])
+ ;;
+ * )
+ lockdir="$withval"
+ ;;
+ esac])
+
+#################################################
+# set pid directory location
+AC_ARG_WITH(piddir,
+[AS_HELP_STRING([--with-piddir=DIR],[Where to put pid files ($ac_default_prefix/var/locks)])],
+[ case "$withval" in
+ yes|no)
+ #
+ # Just in case anybody calls it without argument
+ #
+ AC_MSG_WARN([--with-piddir called without argument - will use default])
+ ;;
+ * )
+ piddir="$withval"
+ ;;
+ esac])
+
+#################################################
+# set log directory location
+AC_ARG_WITH(logfilebase,
+[AS_HELP_STRING([--with-logfilebase=DIR],[Where to put log files (\$(VARDIR))])],
+[ case "$withval" in
+ yes|no)
+ #
+ # Just in case anybody does it
+ #
+ AC_MSG_WARN([--with-logfilebase called without argument - will use default])
+ ;;
+ * )
+ logfilebase="$withval"
+ ;;
+ esac])
+
+
+AC_SUBST(lockdir)
+AC_SUBST(piddir)
+AC_SUBST(logfilebase)
+AC_SUBST(privatedir)
+AC_SUBST(bindir)
+AC_SUBST(sbindir)
+AC_SUBST(winbindd_socket_dir)
+AC_SUBST(winbindd_privileged_socket_dir)
+AC_SUBST(ntp_signd_socket_dir)
+AC_SUBST(modulesdir)
+
+#################################################
+# set prefix for 'make test'
+# this is needed to workarround the 108 char
+# unix socket path limitation!
+#
+selftest_prefix="./st"
+AC_SUBST(selftest_prefix)
+AC_ARG_WITH(selftest-prefix,
+[AS_HELP_STRING([--with-selftest-prefix=DIR],[The prefix where make test will be run ($selftest_prefix)])],
+[ case "$withval" in
+ yes|no)
+ AC_MSG_WARN([--with-selftest-prefix called without argument - will use default])
+ ;;
+ * )
+ selftest_prefix="$withval"
+ ;;
+ esac])
+
+debug=no
+AC_ARG_ENABLE(debug,
+[AS_HELP_STRING([--enable-debug],[Turn on compiler debugging information (default=no)])],
+ [if test x$enable_debug = xyes; then
+ debug=yes
+ fi])
+
+developer=no
+AC_SUBST(developer)
+AC_ARG_ENABLE(developer,
+[AS_HELP_STRING([--enable-developer],[Turn on developer warnings and debugging (default=no)])],
+ [if test x$enable_developer = xyes; then
+ debug=yes
+ developer=yes
+ fi])
+
+dnl disable these external libs
+AC_ARG_WITH(disable-ext-lib,
+[AS_HELP_STRING([--with-disable-ext-lib=LIB],[Comma-seperated list of external libraries])],
+[ if test $withval; then
+ for i in `echo $withval | sed -e's/,/ /g'`
+ do
+ eval SMB_$i=NO
+ done
+fi ])
diff --git a/source4/build/m4/env.m4 b/source4/build/m4/env.m4
new file mode 100644
index 0000000000..738ab8b1ae
--- /dev/null
+++ b/source4/build/m4/env.m4
@@ -0,0 +1,90 @@
+dnl SMB Build Environment Checks
+dnl -------------------------------------------------------
+dnl Copyright (C) Stefan (metze) Metzmacher 2004
+dnl Copyright (C) Jelmer Vernooij 2005,2008
+dnl Released under the GNU GPL
+dnl -------------------------------------------------------
+dnl
+
+AC_SUBST(srcdir)
+export srcdir;
+
+# we always set builddir to "." as that's nicer than
+# having the absolute path of the current work directory
+builddir=.
+AC_SUBST(builddir)
+export builddir;
+
+AC_SUBST(datarootdir)
+
+AC_SUBST(VPATH)
+VPATH="\$(builddir):\$(srcdir)"
+
+SMB_VERSION_STRING=`cat ${srcdir}/version.h | grep 'SAMBA_VERSION_OFFICIAL_STRING' | cut -d '"' -f2`
+echo "SAMBA VERSION: ${SMB_VERSION_STRING}"
+
+SAMBA_VERSION_GIT_COMMIT_FULLREV=`cat ${srcdir}/version.h | grep 'SAMBA_VERSION_GIT_COMMIT_FULLREV' | cut -d ' ' -f3- | cut -d '"' -f2`
+if test -n "${SAMBA_VERSION_GIT_COMMIT_FULLREV}";then
+ echo "BUILD COMMIT REVISION: ${SAMBA_VERSION_GIT_COMMIT_FULLREV}"
+fi
+SAMBA_VERSION_GIT_COMMIT_DATE=`cat ${srcdir}/version.h | grep 'SAMBA_VERSION_GIT_COMMIT_DATE' | cut -d ' ' -f3-`
+if test -n "${SAMBA_VERSION_GIT_COMMIT_DATE}";then
+ echo "BUILD COMMIT DATE: ${SAMBA_VERSION_GIT_COMMIT_DATE}"
+fi
+SAMBA_VERSION_GIT_COMMIT_TIME=`cat ${srcdir}/version.h | grep 'SAMBA_VERSION_GIT_COMMIT_TIME' | cut -d ' ' -f3-`
+if test -n "${SAMBA_VERSION_GIT_COMMIT_TIME}";then
+ echo "BUILD COMMIT TIME: ${SAMBA_VERSION_GIT_COMMIT_TIME}"
+
+ # just to keep the build-farm gui happy for now...
+ echo "BUILD REVISION: ${SAMBA_VERSION_GIT_COMMIT_TIME}"
+fi
+
+m4_include(build/m4/check_path.m4)
+m4_include(../m4/check_perl.m4)
+
+AC_SAMBA_PERL([], [AC_MSG_ERROR([Please install perl from http://www.perl.com/])])
+
+AC_PATH_PROG(YAPP, yapp, false)
+
+m4_include(build/m4/check_cc.m4)
+m4_include(build/m4/check_ld.m4)
+m4_include(../m4/check_make.m4)
+
+AC_SAMBA_GNU_MAKE([AC_MSG_RESULT(found)], [AC_MSG_ERROR([Unable to find GNU make])])
+AC_SAMBA_GNU_MAKE_VERSION()
+GNU_MAKE_VERSION=$samba_cv_gnu_make_version
+AC_SUBST(GNU_MAKE_VERSION)
+
+new_make=no
+AC_MSG_CHECKING([for GNU make >= 3.81])
+if $PERL -e " \$_ = '$GNU_MAKE_VERSION'; s/@<:@^\d\.@:>@.*//g; exit (\$_ < 3.81);"; then
+ new_make=yes
+fi
+AC_MSG_RESULT($new_make)
+automatic_dependencies=no
+AX_CFLAGS_GCC_OPTION([-M -MT conftest.d -MF conftest.o], [], [ automatic_dependencies=$new_make ], [])
+AC_MSG_CHECKING([Whether to use automatic dependencies])
+AC_ARG_ENABLE(automatic-dependencies,
+[ --enable-automatic-dependencies Enable automatic dependencies],
+[ automatic_dependencies=$enableval ],
+[ automatic_dependencies=no ])
+AC_MSG_RESULT($automatic_dependencies)
+AC_SUBST(automatic_dependencies)
+
+m4_include(build/m4/check_doc.m4)
+
+m4_include(../m4/check_python.m4)
+
+AC_SAMBA_PYTHON_DEVEL([
+SMB_EXT_LIB(EXT_LIB_PYTHON, [$PYTHON_LDFLAGS], [$PYTHON_CFLAGS])
+SMB_ENABLE(EXT_LIB_PYTHON,YES)
+SMB_ENABLE(LIBPYTHON,YES)
+],[
+AC_MSG_ERROR([Python not found. Please install Python 2.x and its development headers/libraries.])
+])
+
+AC_MSG_CHECKING(python library directory)
+pythondir=`$PYTHON -c "from distutils import sysconfig; print sysconfig.get_python_lib(1, 0, '\\${prefix}')"`
+AC_MSG_RESULT($pythondir)
+
+AC_SUBST(pythondir)
diff --git a/source4/build/m4/public.m4 b/source4/build/m4/public.m4
new file mode 100644
index 0000000000..b0b8af3db4
--- /dev/null
+++ b/source4/build/m4/public.m4
@@ -0,0 +1,277 @@
+dnl SMB Build System
+dnl ----------------
+dnl Copyright (C) 2004 Stefan Metzmacher
+dnl Copyright (C) 2004-2005 Jelmer Vernooij
+dnl Published under the GPL
+dnl
+dnl SMB_EXT_LIB_FROM_PKGCONFIG(name,pkg-config name,[ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND])
+dnl
+dnl SMB_INCLUDED_LIB_PKGCONFIG(name,pkg-config name,[ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND])
+dnl
+dnl SMB_EXT_LIB(name,libs,cflags,cppflags,ldflags)
+dnl
+dnl SMB_ENABLE(name,default_build)
+dnl
+dnl SMB_INCLUDE_MK(file)
+dnl
+dnl SMB_WRITE_MAKEVARS(file)
+dnl
+dnl SMB_WRITE_PERLVARS(file)
+dnl
+dnl #######################################################
+dnl ### And now the implementation ###
+dnl #######################################################
+
+dnl SMB_SUBSYSTEM(name,obj_files,required_subsystems,cflags)
+AC_DEFUN([SMB_SUBSYSTEM],
+[
+MAKE_SETTINGS="$MAKE_SETTINGS
+$1_CFLAGS = $4
+$1_ENABLE = YES
+$1_OBJ_FILES = $2
+"
+
+SMB_INFO_SUBSYSTEMS="$SMB_INFO_SUBSYSTEMS
+###################################
+# Start Subsystem $1
+@<:@SUBSYSTEM::$1@:>@
+PRIVATE_DEPENDENCIES = $3
+CFLAGS = \$($1_CFLAGS)
+ENABLE = YES
+# End Subsystem $1
+###################################
+"
+])
+
+dnl SMB_LIBRARY(name,obj_files,required_subsystems,cflags,ldflags)
+AC_DEFUN([SMB_LIBRARY],
+[
+MAKE_SETTINGS="$MAKE_SETTINGS
+$1_CFLAGS = $6
+$1_LDFLAGS = $7
+n1_ENABLE = YES
+$1_OBJ_FILES = $2
+"
+
+SMB_INFO_LIBRARIES="$SMB_INFO_LIBRARIES
+###################################
+# Start Library $1
+@<:@LIBRARY::$1@:>@
+PRIVATE_DEPENDENCIES = $3
+CFLAGS = \$($1_CFLAGS)
+LDFLAGS = \$($1_LDFLAGS)
+ENABLE = YES
+# End Library $1
+###################################
+"
+])
+
+dnl SMB_EXT_LIB_FROM_PKGCONFIG(name,pkg-config name,[ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND])
+AC_DEFUN([SMB_EXT_LIB_FROM_PKGCONFIG],
+[
+ dnl Figure out the correct variables and call SMB_EXT_LIB()
+
+ if test -z "$PKG_CONFIG"; then
+ AC_PATH_PROG(PKG_CONFIG, pkg-config, no)
+ fi
+
+ if test "$PKG_CONFIG" = "no" ; then
+ echo "*** The pkg-config script could not be found. Make sure it is"
+ echo "*** in your path, or set the PKG_CONFIG environment variable"
+ echo "*** to the full path to pkg-config."
+ echo "*** Or see http://pkg-config.freedesktop.org/ to get pkg-config."
+ ac_cv_$1_found=no
+ else
+ SAMBA_PKG_CONFIG_MIN_VERSION="0.9.0"
+ if $PKG_CONFIG --atleast-pkgconfig-version $SAMBA_PKG_CONFIG_MIN_VERSION; then
+ AC_MSG_CHECKING(for $2)
+
+ if $PKG_CONFIG --exists '$2' ; then
+ AC_MSG_RESULT(yes)
+
+ $1_CFLAGS="`$PKG_CONFIG --cflags '$2'`"
+ OLD_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $$1_CFLAGS"
+ AC_MSG_CHECKING([that the C compiler can use the $1_CFLAGS])
+ AC_TRY_RUN([#include "${srcdir-.}/../tests/trivial.c"],
+ SMB_ENABLE($1, YES)
+ AC_MSG_RESULT(yes),
+ AC_MSG_RESULT(no),
+ AC_MSG_WARN([cannot run when cross-compiling]))
+ CFLAGS="$OLD_CFLAGS"
+
+ SMB_EXT_LIB($1,
+ [`$PKG_CONFIG --libs-only-l '$2'`],
+ [`$PKG_CONFIG --cflags-only-other '$2'`],
+ [`$PKG_CONFIG --cflags-only-I '$2'`],
+ [`$PKG_CONFIG --libs-only-other '$2'` `$PKG_CONFIG --libs-only-L '$2'`])
+ ac_cv_$1_found=yes
+
+ else
+ AC_MSG_RESULT(no)
+ $PKG_CONFIG --errors-to-stdout --print-errors '$2'
+ ac_cv_$1_found=no
+ fi
+ else
+ echo "*** Your version of pkg-config is too old. You need version $SAMBA_PKG_CONFIG_MIN_VERSION or newer."
+ echo "*** See http://pkg-config.freedesktop.org/"
+ ac_cv_$1_found=no
+ fi
+ fi
+ if test x$ac_cv_$1_found = x"yes"; then
+ ifelse([$3], [], [echo -n ""], [$3])
+ else
+ ifelse([$4], [], [
+ SMB_EXT_LIB($1)
+ SMB_ENABLE($1, NO)
+ ], [$4])
+ fi
+])
+
+dnl SMB_INCLUDED_LIB_PKGCONFIG(name,pkg-config name,[ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND])
+AC_DEFUN([SMB_INCLUDED_LIB_PKGCONFIG],
+[
+ AC_ARG_ENABLE([external-]translit($1,`A-Z',`a-z'),
+ AS_HELP_STRING([--enable-external-]translit($1,`A-Z',`a-z'), [Use external $1 instead of built-in (default=ifelse([$5],[],auto,$5))]), [], [enableval=ifelse([$5],[],auto,$5)])
+
+ if test $enableval = yes -o $enableval = auto; then
+ SMB_EXT_LIB_FROM_PKGCONFIG([$1], [$2], [$3], [
+ if test $enableval = yes; then
+ AC_MSG_ERROR([Unable to find external $1])
+ fi
+ enableval=no
+ ])
+ fi
+ if test $enableval = no; then
+ ifelse([$4], [], [
+ SMB_EXT_LIB($1)
+ SMB_ENABLE($1, NO)
+ ], [$4])
+ fi
+])
+
+dnl SMB_INCLUDE_MK(file)
+AC_DEFUN([SMB_INCLUDE_MK],
+[
+SMB_INFO_EXT_LIBS="$SMB_INFO_EXT_LIBS
+mkinclude $1
+"
+])
+
+dnl
+dnl SMB_EXT_LIB() just specifies the details of the library.
+dnl Note: the library isn't enabled by default.
+dnl You need to enable it with SMB_ENABLE(name) if configure
+dnl find it should be used. E.g. it should not be enabled
+dnl if the library is present, but the header file is missing.
+dnl
+dnl SMB_EXT_LIB(name,libs,cflags,cppflags,ldflags)
+AC_DEFUN([SMB_EXT_LIB],
+[
+MAKE_SETTINGS="$MAKE_SETTINGS
+$1_LIBS = $2
+$1_CFLAGS = $3
+$1_CPPFLAGS = $4
+$1_LDFLAGS = $5
+"
+
+])
+
+dnl SMB_ENABLE(name,default_build)
+AC_DEFUN([SMB_ENABLE],
+[
+ MAKE_SETTINGS="$MAKE_SETTINGS
+$1_ENABLE = $2
+"
+SMB_INFO_ENABLES="$SMB_INFO_ENABLES
+\$enabled{$1} = \"$2\";"
+])
+
+dnl SMB_MAKE_SETTINGS(text)
+AC_DEFUN([SMB_MAKE_SETTINGS],
+[
+MAKE_SETTINGS="$MAKE_SETTINGS
+$1
+"
+])
+
+dnl SMB_WRITE_MAKEVARS(path, skip_vars)
+AC_DEFUN([SMB_WRITE_MAKEVARS],
+[
+echo "configure: creating $1"
+cat >$1<<CEOF
+# $1 - Autogenerated by configure, DO NOT EDIT!
+$MAKE_SETTINGS
+CEOF
+skip_vars=" $2 "
+for ac_var in $ac_subst_vars
+do
+ eval ac_val=\$$ac_var
+ if echo "$skip_vars" | grep -v " $ac_var " >/dev/null 2>/dev/null; then
+ echo "$ac_var = $ac_val" >> $1
+ fi
+done
+])
+
+dnl SMB_WRITE_PERLVARS(path)
+AC_DEFUN([SMB_WRITE_PERLVARS],
+[
+echo "configure: creating $1"
+cat >$1<<CEOF
+# config.pm - Autogenerate by configure. DO NOT EDIT!
+
+package config;
+require Exporter;
+@ISA = qw(Exporter);
+@EXPORT_OK = qw(%enabled %config);
+use strict;
+
+use vars qw(%enabled %config);
+
+%config = (
+CEOF
+
+for ac_var in $ac_subst_vars
+do
+ eval ac_val=\$$ac_var
+ # quote ' (\x27) inside '...' and make sure \ isn't eaten by shells, so use perl:
+ QAC_VAL=$ac_val QAC_VAR=$ac_var perl -e '$myval="$ENV{QAC_VAL}"; $myval =~ s/\x27/\\\x27/g ; print $ENV{QAC_VAR}." => \x27$myval\x27,\n"' >> $1
+done
+
+cat >>$1<<CEOF
+);
+$SMB_INFO_ENABLES
+1;
+CEOF
+])
+
+dnl SMB_BUILD_RUN(OUTPUT_FILE)
+AC_DEFUN([SMB_BUILD_RUN],
+[
+AC_OUTPUT_COMMANDS(
+[
+test "x$ac_abs_srcdir" != "x$ac_abs_builddir" && (
+ cd $builddir;
+ # NOTE: We *must* use -R so we don't follow symlinks (at least on BSD
+ # systems).
+ test -d heimdal || cp -R $srcdir/heimdal $builddir/
+ test -d heimdal_build || cp -R $srcdir/heimdal_build $builddir/
+ test -d build || builddir="$builddir" \
+ srcdir="$srcdir" \
+ $PERL ${srcdir}/script/buildtree.pl
+ )
+
+$PERL -I${builddir} -I${builddir}/build \
+ -I${srcdir} -I${srcdir}/build \
+ ${srcdir}/build/smb_build/main.pl --output=$1 main.mk || exit $?
+],
+[
+srcdir="$srcdir"
+builddir="$builddir"
+PERL="$PERL"
+
+export PERL
+export srcdir
+export builddir
+])
+])
diff --git a/source4/build/make/lex_compile.sh b/source4/build/make/lex_compile.sh
new file mode 100755
index 0000000000..d05056d100
--- /dev/null
+++ b/source4/build/make/lex_compile.sh
@@ -0,0 +1,60 @@
+#!/bin/sh
+
+LEX="$1"
+SRC="$2"
+DEST="$3"
+shift 3
+ARGS="$*"
+
+dir=`dirname $SRC`
+file=`basename $SRC`
+base=`basename $SRC .l`
+if [ -z "$LEX" ]; then
+ # if $DEST is more recent than $SRC, we can just touch
+ # otherwise we touch but print out warnings
+ if [ -r $DEST ]; then
+ if [ x`find $SRC -newer $DEST -print` = x$SRC ]; then
+ echo "warning: lex not found - cannot generate $SRC => $DEST" >&2
+ echo "warning: lex not found - only updating the timestamp of $DEST" >&2
+ fi
+ touch $DEST;
+ exit;
+ fi
+ echo "error: lex not found - cannot generate $SRC => $DEST" >&2
+ exit 1;
+fi
+# if $DEST is more recent than $SRC, we can just touch
+if [ -r $DEST ]; then
+ if [ x`find $SRC -newer $DEST -print` != x$SRC ]; then
+ touch $DEST;
+ exit;
+ fi
+fi
+TOP=`pwd`
+echo "info: running $LEX $ARGS $file"
+if cd $dir && $LEX $ARGS $file; then
+ if [ -r lex.yy.c ];then
+ # we must guarantee that config.h comes first
+ echo "info: move lex.yy.c to $base.c"
+ echo "#include \"config.h\"" > $base.c
+ sed -e "s|lex\.yy\.c|$DEST|" lex.yy.c >> $base.c
+ rm -f $base.yy.c
+ elif [ -r $base.yy.c ];then
+ # we must guarantee that config.h comes first
+ echo "info: move $base.yy.c to $base.c"
+ echo "#include \"config.h\"" > $base.c
+ sed -e "s|$base\.yy\.c|$DEST|" $base.yy.c >> $base.c
+ rm -f $base.yy.c
+ elif [ -r $base.c ];then
+ # we must guarantee that config.h comes first
+ echo "info: add #include \"config.h\" to $base.c"
+ mv $base.c $base.c.tmp
+ echo "#include \"config.h\"" > $base.c
+ sed -e "s|$base\.yy\.c|$DEST|" $base.c.tmp >> $base.c
+ rm -f $base.c.tmp
+ elif [ ! -r base.c ]; then
+ echo "$base.c nor $base.yy.c nor lex.yy.c generated."
+ exit 1
+ fi
+fi
+cd $TOP
diff --git a/source4/build/make/python.mk b/source4/build/make/python.mk
new file mode 100644
index 0000000000..e5a5b87ae5
--- /dev/null
+++ b/source4/build/make/python.mk
@@ -0,0 +1,54 @@
+pythonbuilddir = bin/python
+
+installpython::
+ mkdir -p $(DESTDIR)$(pythondir)
+
+# Install Python
+# Arguments: Module path
+define python_module_template
+
+installpython:: $$(pythonbuilddir)/$(1) ;
+ mkdir -p $$(DESTDIR)$$(pythondir)/$$(dir $(1))
+ cp $$< $$(DESTDIR)$$(pythondir)/$(1)
+
+uninstallpython::
+ rm -f $$(DESTDIR)$$(pythondir)/$(1) ;
+
+pythonmods:: $$(pythonbuilddir)/$(1) ;
+
+endef
+
+define python_py_module_template
+
+$$(pythonbuilddir)/$(1): $(2) ;
+ mkdir -p $$(@D)
+ cp $$< $$@
+
+$(call python_module_template,$(1))
+
+endef
+
+# Python C module
+# Arguments: File name, dependencies, link list
+define python_c_module_template
+
+$$(pythonbuilddir)/$(1): $(2) ;
+ @echo Linking $$@
+ @mkdir -p $$(@D)
+ @$$(MDLD) $$(LDFLAGS) $$(MDLD_FLAGS) $$(INTERN_LDFLAGS) -o $$@ $$(INSTALL_LINK_FLAGS) $(3)
+
+$(call python_module_template,$(1))
+endef
+
+pythonmods::
+
+clean::
+ @echo "Removing python modules"
+ @rm -rf $(pythonbuilddir)
+
+pydoctor:: pythonmods
+ LD_LIBRARY_PATH=bin/shared PYTHONPATH=$(pythonbuilddir) pydoctor --project-name=Samba --project-url=http://www.samba.org --make-html --docformat=restructuredtext --add-package $(pythonbuilddir)/samba
+
+bin/python/%.py:
+ mkdir -p $(@D)
+ cp $< $@
diff --git a/source4/build/make/rules.mk b/source4/build/make/rules.mk
new file mode 100644
index 0000000000..55ecf8968b
--- /dev/null
+++ b/source4/build/make/rules.mk
@@ -0,0 +1,191 @@
+# Rules file for Samba 4
+# This relies on GNU make.
+#
+# Dependencies command
+DEPENDS = $(CC) -M -MG -MP -MT $(<:.c=.o) -MT $@ -MT : \
+ $(CFLAGS) $(CPPFLAGS) $< -o $@
+# Dependencies for host objects
+HDEPENDS = $(CC) -M -MG -MP -MT $(<:.c=.ho) -MT $@ -MT : \
+ $(HOSTCC_FLAGS) $(CPPFLAGS) $< -o $@
+# Dependencies for precompiled headers
+PCHDEPENDS = $(CC) -M -MG -MT include/includes.h.gch -MT $@ \
+ $(CFLAGS) $(CPPFLAGS) $< -o $@
+
+# Run a static analysis checker
+CHECK = $(CC_CHECKER) $(CFLAGS) $(PICFLAG) $(CPPLAGS) -c $< -o $@
+
+# Run the configured compiler
+COMPILE = $(CC) $(CFLAGS) $(PICFLAG) \
+ $(CPPFLAGS) \
+ -c $< -o $@
+
+# Run the compiler for the build host
+HCOMPILE = $(HOSTCC) $(HOSTCC_FLAGS) $(CPPFLAGS) -c $< -o $@
+
+# Precompile headers
+PCHCOMPILE = @$(CC) -Ilib/replace \
+ $(CFLAGS) $(PICFLAG) $(CPPFLAGS) -c $< -o $@
+
+# Partial linking
+PARTLINK = @$(PROG_LD) -r
+
+make_utility_dir = $(srcdir)/build/make/
+
+include/config.h:
+ @echo "include/config.h not present"
+ @echo "You need to rerun ./autogen.sh and ./configure"
+ @/bin/false
+
+pch::
+
+clean:: clean_pch
+ @echo Removing objects
+ @-find . -name '*.o' -exec rm -f '{}' \;
+ @echo Removing hostcc objects
+ @-find . -name '*.ho' -exec rm -f '{}' \;
+ @echo Removing libraries
+ @-rm -f $(STATIC_LIBS) $(SHARED_LIBS)
+ @-rm -f bin/static/*.a $(shliboutputdir)/*.$(SHLIBEXT) bin/mergedobj/*.o
+ @echo Removing modules
+ @-rm -f bin/modules/*/*.$(SHLIBEXT)
+ @-rm -f bin/*_init_module.c
+ @echo Removing dummy targets
+ @-rm -f bin/.*_*
+ @echo Removing generated files
+ @-rm -f bin/*_init_module.c
+ @-rm -rf librpc/gen_*
+
+distclean:: clean
+ -rm -f include/config.h include/config_tmp.h include/build.h
+ -rm -f data.mk
+ -rm -f config.status
+ -rm -f config.log config.cache
+ -rm -f config.pm config.mk
+ -rm -f $(PC_FILES)
+
+removebackup::
+ -rm -f *.bak *~ */*.bak */*~ */*/*.bak */*/*~ */*/*/*.bak */*/*/*~
+
+realdistclean:: distclean removebackup
+ -rm -f include/config_tmp.h.in
+ -rm -f version.h
+ -rm -f configure
+ -rm -f $(MANPAGES)
+
+check:: test
+
+unused_macros:
+ $(srcdir)/script/find_unused_macros.pl `find . -name "*.[ch]"` | sort
+
+# Create a static library
+%.a:
+ @echo Linking $@
+ @rm -f $@
+ @mkdir -p $(@D)
+ @$(STLD) $(STLD_FLAGS) $@ $^
+
+include $(make_utility_dir)/templates.mk
+
+###############################################################################
+# File types
+###############################################################################
+
+.SUFFIXES: .x .c .et .y .l .d .o .h .h.gch .a .$(SHLIBEXT) .1 .1.xml .3 .3.xml .5 .5.xml .7 .7.xml .8 .8.xml .ho .idl .hd
+
+.c.d:
+ @echo "Generating dependencies for $<"
+ @$(DEPENDS)
+
+.c.hd:
+ @echo "Generating host-compiler dependencies for $<"
+ @$(HDEPENDS)
+
+include/includes.d: include/includes.h
+ @echo "Generating dependencies for $<"
+ @$(PCHDEPENDS)
+
+.c.o:
+ @if test -n "$(CC_CHECKER)"; then \
+ echo "Checking $< with '$(CC_CHECKER)'"; \
+ $(CHECK) ; \
+ fi
+ @echo "Compiling $<"
+ @-mkdir -p `dirname $@`
+ @$(COMPILE) && exit 0 ; \
+ echo "The following command failed:" 1>&2;\
+ echo "$(subst ",\",$(COMPILE))" 1>&2 && exit 1
+
+
+.c.ho:
+ @echo "Compiling $< with host compiler"
+ @-mkdir -p `dirname $@`
+ @$(HCOMPILE) && exit 0;\
+ echo "The following command failed:" 1>&2;\
+ echo "$(subst ",\",$(HCOMPILE))" 1>&2;\
+ $(HCOMPILE) >/dev/null 2>&1
+
+.h.h.gch:
+ @echo "Precompiling $<"
+ @$(PCHCOMPILE)
+
+.y.c:
+ @echo "Building $< with $(YACC)"
+ @-$(make_utility_dir)/yacc_compile.sh "$(YACC)" "$<" "$@"
+
+.l.c:
+ @echo "Building $< with $(LEX)"
+ @-$(make_utility_dir)/lex_compile.sh "$(LEX)" "$<" "$@"
+
+%.a:
+ @echo Linking $@
+ @rm -f $@
+ @mkdir -p $(@D)
+ @$(STLD) $(STLD_FLAGS) $@ $^
+
+
+DOCBOOK_MANPAGE_URL = http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl
+
+.1.xml.1:
+ $(XSLTPROC) -o $@ $(DOCBOOK_MANPAGE_URL) $<
+
+.3.xml.3:
+ $(XSLTPROC) -o $@ $(DOCBOOK_MANPAGE_URL) $<
+
+.5.xml.5:
+ $(XSLTPROC) -o $@ $(DOCBOOK_MANPAGE_URL) $<
+
+.7.xml.7:
+ $(XSLTPROC) -o $@ $(DOCBOOK_MANPAGE_URL) $<
+
+.8.xml.8:
+ $(XSLTPROC) -o $@ $(DOCBOOK_MANPAGE_URL) $<
+
+dist:: idl_full manpages configure distclean
+
+configure:
+ ./autogen.sh
+
+showflags::
+ @echo 'Samba will be compiled with flags:'
+ @echo ' CPP = $(CPP)'
+ @echo ' CPPFLAGS = $(CPPFLAGS)'
+ @echo ' CC = $(CC)'
+ @echo ' CFLAGS = $(CFLAGS)'
+ @echo ' PICFLAG = $(PICFLAG)'
+ @echo ' BNLD = $(BNLD)'
+ @echo ' BNLD_FLAGS = $(BNLD_FLAGS)'
+ @echo ' STLD = $(STLD)'
+ @echo ' STLD_FLAGS = $(STLD_FLAGS)'
+ @echo ' SHLD = $(SHLD)'
+ @echo ' SHLD_FLAGS = $(SHLD_FLAGS)'
+ @echo ' MDLD = $(MDLD)'
+ @echo ' MDLD_FLAGS = $(MDLD_FLAGS)'
+ @echo ' SHLIBEXT = $(SHLIBEXT)'
+
+base_srcdirs = $(srcdir) ../librpc/ ../lib/ ../libcli
+
+etags:
+ etags `find $(base_srcdirs) -name "*.[ch]"`
+
+ctags:
+ ctags `find $(base_srcdirs) -name "*.[ch]"`
diff --git a/source4/build/make/templates.mk b/source4/build/make/templates.mk
new file mode 100644
index 0000000000..dbfc738ae1
--- /dev/null
+++ b/source4/build/make/templates.mk
@@ -0,0 +1,141 @@
+# Templates file for Samba 4
+# This relies on GNU make.
+#
+# © 2008 Jelmer Vernooij <jelmer@samba.org>
+#
+###############################################################################
+# Templates
+###############################################################################
+
+# Partially link
+# Arguments: target object file, source object files
+define partial_link_template
+$(1): $(2) ;
+ @echo Partially linking $$@
+ @mkdir -p $$(@D)
+ $$(PARTLINK) -o $$@ $$^
+endef
+
+# Link a binary
+# Arguments: target file, depends, flags
+define binary_link_template
+$(1): $(2) ;
+ @echo Linking $$@
+ @$$(BNLD) $$(BNLD_FLAGS) $$(INTERN_LDFLAGS) -o $$@ $$(INSTALL_LINK_FLAGS) $(3)
+
+clean::
+ @rm -f $(1)
+
+binaries:: $(1)
+
+endef
+
+# Link a host-machine binary
+# Arguments: target file, depends, flags
+define host_binary_link_template
+$(1): $(2) ;
+ @echo Linking $$@
+ @$$(HOSTLD) $$(HOSTLD_FLAGS) -L$${builddir}/bin/static -o $$@ $$(INSTALL_LINK_FLAGS) $(3)
+
+clean::
+ rm -f $(1)
+
+binaries:: $(1)
+
+endef
+
+# Create a prototype header
+# Arguments: header file, c files
+define proto_header_template
+
+proto:: $(1) ;
+
+clean:: ;
+ rm -f $(1)
+
+$(1): $(2) ;
+ @echo "Creating $$@"
+ @$$(PERL) $$(srcdir)/script/mkproto.pl --srcdir=$$(srcdir) --builddir=$$(builddir) --public=/dev/null --private=$$@ $$^
+
+endef
+
+# Shared module
+# Arguments: Target, dependencies, objects
+define shared_module_template
+
+$(1): $(2) ;
+ @echo Linking $$@
+ @mkdir -p $$(@D)
+ @$$(MDLD) $$(LDFLAGS) $$(MDLD_FLAGS) $$(INTERN_LDFLAGS) -o $$@ $$(INSTALL_LINK_FLAGS) $(3)
+
+endef
+
+# Shared library
+# Arguments: Target, dependencies, link flags, soname
+define shared_library_template
+$(1): $(2)
+ @echo Linking $$@
+ @mkdir -p $$(@D)
+ @$$(SHLD) $$(LDFLAGS) $$(SHLD_FLAGS) $$(INTERN_LDFLAGS) -o $$@ $$(INSTALL_LINK_FLAGS) \
+ $(3) \
+ $$(if $$(SONAMEFLAG), $$(SONAMEFLAG)$(notdir $(4)))
+
+ifneq ($(notdir $(1)),$(notdir $(4)))
+$(4): $(1)
+ @echo "Creating symbolic link for $$@"
+ @ln -fs $$(<F) $$@
+endif
+
+ifneq ($(notdir $(1)),$(notdir $(5)))
+$(5): $(1) $(4)
+ @echo "Creating symbolic link for $$@"
+ @ln -fs $$(<F) $$@
+endif
+endef
+
+# Shared alias
+# Arguments: Target, subsystem name, alias name
+define shared_module_alias_template
+bin/modules/$(2)/$(3).$$(SHLIBEXT): $(1)
+ @ln -fs $$(<F) $$@
+
+PLUGINS += bin/modules/$(2)/$(3).$$(SHLIBEXT)
+
+uninstallplugins::
+ @-rm $$(DESTDIR)$$(modulesdir)/$(2)/$(3).$$(SHLIBEXT)
+installplugins::
+ @ln -fs $(notdir $(1)) $$(DESTDIR)$$(modulesdir)/$(2)/$(3).$$(SHLIBEXT)
+
+endef
+
+define shared_module_install_template
+installplugins:: bin/modules/$(1)/$(2)
+ @echo Installing $(2) as $$(DESTDIR)$$(modulesdir)/$(1)/$(2)
+ @mkdir -p $$(DESTDIR)$$(modulesdir)/$(1)/
+ @cp bin/modules/$(1)/$(2) $$(DESTDIR)$$(modulesdir)/$(1)/$(2)
+uninstallplugins::
+ @echo Uninstalling $$(DESTDIR)$$(modulesdir)/$(1)/$(2)
+ @-rm $$(DESTDIR)$$(modulesdir)/$(1)/$(2)
+
+endef
+
+# abspath for older makes
+abspath = $(shell cd $(dir $(1)); pwd)/$(notdir $(1))
+
+# Install a binary
+# Arguments: path to binary to install
+define binary_install_template
+installbin:: $$(DESTDIR)$$(bindir)/$(notdir $(1)) installdirs
+
+uninstallbin::
+ @echo "Removing $(notdir $(1))"
+ @rm -f $$(DESTDIR)$$(bindir)/$(notdir $(1))
+endef
+
+define sbinary_install_template
+installsbin:: $$(DESTDIR)$$(sbindir)/$(notdir $(1)) installdirs
+
+uninstallsbin::
+ @echo "Removing $(notdir $(1))"
+ @rm -f $$(DESTDIR)$$(sbindir)/$(notdir $(1))
+endef
diff --git a/source4/build/make/yacc_compile.sh b/source4/build/make/yacc_compile.sh
new file mode 100755
index 0000000000..ac4afea3f6
--- /dev/null
+++ b/source4/build/make/yacc_compile.sh
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+YACC="$1"
+SRC="$2"
+DEST="$3"
+
+dir=`dirname $SRC`
+file=`basename $SRC`
+base=`basename $SRC .y`
+if [ -z "$YACC" ]; then
+ # if $DEST is more recent than $SRC, we can just touch
+ # otherwise we touch but print out warnings
+ if [ -r $DEST ]; then
+ if [ x`find $SRC -newer $DEST -print` = x$SRC ]; then
+ echo "warning: yacc not found - cannot generate $SRC => $DEST" >&2
+ echo "warning: yacc not found - only updating the timestamp of $DEST" >&2
+ fi
+ touch $DEST;
+ exit;
+ fi
+ echo "error: yacc not found - cannot generate $SRC => $DEST" >&2
+ exit 1;
+fi
+# if $DEST is more recent than $SRC, we can just touch
+if [ -r $DEST ]; then
+ if [ x`find $SRC -newer $DEST -print` != x$SRC ]; then
+ touch $DEST;
+ exit;
+ fi
+fi
+TOP=`pwd`
+echo "info: running $YACC -d $file"
+if cd $dir && $YACC -d $file; then
+ if [ -r y.tab.h -a -r y.tab.c ];then
+ echo "info: move y.tab.h to $base.h"
+ sed -e "/^#/!b" -e "s|y\.tab\.h|$SRC|" -e "s|\"$base.y|\"$SRC|" y.tab.h > $base.h
+ echo "info: move y.tab.c to $base.c"
+ sed -e "s|y\.tab\.c|$SRC|" -e "s|\"$base.y|\"$SRC|" y.tab.c > $base.c
+ rm -f y.tab.c y.tab.h
+ elif [ ! -r $base.h -a ! -r $base.c]; then
+ echo "$base.h nor $base.c generated."
+ exit 1
+ fi
+fi
+cd $TOP
diff --git a/source4/build/pasn1/Makefile b/source4/build/pasn1/Makefile
new file mode 100644
index 0000000000..24da2b79e8
--- /dev/null
+++ b/source4/build/pasn1/Makefile
@@ -0,0 +1,5 @@
+asn1.pm: asn1.yp
+ yapp -s asn1.yp
+
+clean:
+ rm -f asn1.pm
diff --git a/source4/build/pasn1/asn1.yp b/source4/build/pasn1/asn1.yp
new file mode 100644
index 0000000000..7fc834ff2b
--- /dev/null
+++ b/source4/build/pasn1/asn1.yp
@@ -0,0 +1,306 @@
+########################
+# ASN.1 Parse::Yapp parser
+# Copyright (C) Stefan (metze) Metzmacher <metze@samba.org>
+# released under the GNU GPL version 3 or later
+
+
+
+# the precedence actually doesn't matter at all for this grammer, but
+# by providing a precedence we reduce the number of conflicts
+# enormously
+%left '-' '+' '&' '|' '*' '>' '.' '/' '(' ')' '[' ']' ':' ',' ';'
+
+
+################
+# grammer
+%%
+
+asn1:
+ identifier asn1_definitions asn1_delimitter asn1_begin asn1_decls asn1_end
+ {{
+ "OBJECT" => "ASN1_DEFINITION",
+ "IDENTIFIER" => $_[1],
+ "DATA" => $_[5]
+ }}
+;
+
+asn1_delimitter:
+ delimitter
+;
+
+asn1_definitions:
+ 'DEFINITIONS'
+;
+
+asn1_begin:
+ 'BEGIN'
+;
+
+asn1_end:
+ 'END'
+;
+
+asn1_decls:
+ asn1_def
+ { [ $_[1] ] }
+ | asn1_decls asn1_def
+ { push(@{$_[1]}, $_[2]); $_[1] }
+;
+
+
+
+asn1_def:
+ asn1_target asn1_delimitter asn1_application asn1_type
+ {{
+ "OBJECT" => "ASN1_DEF",
+ "IDENTIFIER" => $_[1],
+ "APPLICATION" => $_[3],
+ "STRUCTURE" => $_[4]
+ }}
+;
+
+asn1_target:
+ identifier
+;
+
+asn1_application:
+ #empty
+ | '[' 'APPLICATION' constant ']'
+ { $_[3] }
+;
+
+asn1_type:
+ asn1_boolean
+ | asn1_integer
+ | asn1_bit_string
+ | asn1_octet_string
+ | asn1_null
+ | asn1_object_identifier
+ | asn1_real
+ | asn1_enumerated
+ | asn1_sequence
+ | identifier
+;
+
+asn1_boolean:
+ 'BOOLEAN'
+ {{
+ "TYPE" => "BOOLEAN",
+ "TAG" => 1
+ }}
+;
+
+asn1_integer:
+ 'INTEGER'
+ {{
+ "TYPE" => "INTEGER",
+ "TAG" => 2
+ }}
+ | 'INTEGER' '(' constant '.' '.' constant ')'
+ {{
+ "TYPE" => "INTEGER",
+ "TAG" => 2,
+ "RANGE_LOW" => $_[3],
+ "RENAGE_HIGH" => $_[6]
+ }}
+;
+
+asn1_bit_string:
+ 'BIT' 'STRING'
+ {{
+ "TYPE" => "BIT STRING",
+ "TAG" => 3
+ }}
+;
+
+asn1_octet_string:
+ 'OCTET' 'STRING'
+ {{
+ "TYPE" => "OCTET STRING",
+ "TAG" => 4
+ }}
+;
+
+asn1_null:
+ 'NULL'
+ {{
+ "TYPE" => "NULL",
+ "TAG" => 5
+ }}
+;
+
+asn1_object_identifier:
+ 'OBJECT' 'IDENTIFIER'
+ {{
+ "TYPE" => "OBJECT IDENTIFIER",
+ "TAG" => 6
+ }}
+;
+
+asn1_real:
+ 'REAL'
+ {{
+ "TYPE" => "REAL",
+ "TAG" => 9
+ }}
+;
+
+asn1_enumerated:
+ 'ENUMERATED'
+ {{
+ "TYPE" => "ENUMERATED",
+ "TAG" => 10
+ }}
+;
+
+asn1_sequence:
+ 'SEQUENCE' '{' asn1_var_dec_list '}'
+ {{
+ "TYPE" => "SEQUENCE",
+ "TAG" => 16,
+ "STRUCTURE" => $_[3]
+ }}
+;
+
+asn1_var_dec_list:
+ asn1_var_dec
+ { [ $_[1] ] }
+ | asn1_var_dec_list ',' asn1_var_dec
+ { push(@{$_[1]}, $_[3]); $_[1] }
+;
+
+asn1_var_dec:
+ identifier asn1_type
+ {{
+ "NAME" => $_[1],
+ "TYPE" => $_[2]
+ }}
+;
+
+anytext: #empty { "" }
+ | identifier | constant | text
+ | anytext '-' anytext { "$_[1]$_[2]$_[3]" }
+ | anytext '.' anytext { "$_[1]$_[2]$_[3]" }
+ | anytext '*' anytext { "$_[1]$_[2]$_[3]" }
+ | anytext '>' anytext { "$_[1]$_[2]$_[3]" }
+ | anytext '|' anytext { "$_[1]$_[2]$_[3]" }
+ | anytext '&' anytext { "$_[1]$_[2]$_[3]" }
+ | anytext '/' anytext { "$_[1]$_[2]$_[3]" }
+ | anytext '+' anytext { "$_[1]$_[2]$_[3]" }
+ | anytext '(' anytext ')' anytext { "$_[1]$_[2]$_[3]$_[4]$_[5]" }
+;
+
+delimitter: DELIMITTER
+;
+
+identifier: IDENTIFIER
+;
+
+constant: CONSTANT
+;
+
+text: TEXT { "\"$_[1]\"" }
+;
+
+#####################################
+# start code
+%%
+
+use util;
+
+sub _ASN1_Error {
+ if (exists $_[0]->YYData->{ERRMSG}) {
+ print $_[0]->YYData->{ERRMSG};
+ delete $_[0]->YYData->{ERRMSG};
+ return;
+ };
+ my $line = $_[0]->YYData->{LINE};
+ my $last_token = $_[0]->YYData->{LAST_TOKEN};
+ my $file = $_[0]->YYData->{INPUT_FILENAME};
+
+ print "$file:$line: Syntax error near '$last_token'\n";
+}
+
+sub _ASN1_Lexer($)
+{
+ my($parser)=shift;
+
+ $parser->YYData->{INPUT}
+ or return('',undef);
+
+again:
+ $parser->YYData->{INPUT} =~ s/^[ \t]*//;
+
+ for ($parser->YYData->{INPUT}) {
+ if (/^\#/) {
+ if (s/^\# (\d+) \"(.*?)\"( \d+|)//) {
+ $parser->YYData->{LINE} = $1-1;
+ $parser->YYData->{INPUT_FILENAME} = $2;
+ goto again;
+ }
+ if (s/^\#line (\d+) \"(.*?)\"( \d+|)//) {
+ $parser->YYData->{LINE} = $1-1;
+ $parser->YYData->{INPUT_FILENAME} = $2;
+ goto again;
+ }
+ if (s/^(\#.*)$//m) {
+ goto again;
+ }
+ }
+ if (s/^(\n)//) {
+ $parser->YYData->{LINE}++;
+ goto again;
+ }
+ if (s/^(--.*\n)//) {
+ $parser->YYData->{LINE}++;
+ goto again;
+ }
+ if (s/^(::=)//) {
+ $parser->YYData->{LAST_TOKEN} = $1;
+ return('DELIMITTER',$1);
+ }
+ if (s/^\"(.*?)\"//) {
+ $parser->YYData->{LAST_TOKEN} = $1;
+ return('TEXT',$1);
+ }
+ if (s/^(\d+)(\W|$)/$2/) {
+ $parser->YYData->{LAST_TOKEN} = $1;
+ return('CONSTANT',$1);
+ }
+ if (s/^([\w_-]+)//) {
+ $parser->YYData->{LAST_TOKEN} = $1;
+ if ($1 =~
+ /^(SEQUENCE|INTEGER|OCTET|STRING|
+ APPLICATION|OPTIONAL|NULL|COMPONENTS|OF|
+ BOOLEAN|ENUMERATED|CHOISE|REAL|BIT|OBJECT|IDENTIFIER|
+ DEFAULT|FALSE|TRUE|SET|DEFINITIONS|BEGIN|END)$/x) {
+ return $1;
+ }
+ return('IDENTIFIER',$1);
+ }
+ if (s/^(.)//s) {
+ $parser->YYData->{LAST_TOKEN} = $1;
+ return($1,$1);
+ }
+ }
+}
+
+sub parse_asn1($$)
+{
+ my $self = shift;
+ my $filename = shift;
+
+ my $saved_delim = $/;
+ undef $/;
+ my $cpp = $ENV{CPP};
+ if (! defined $cpp) {
+ $cpp = "cpp"
+ }
+ my $data = `$cpp -xc $filename`;
+ $/ = $saved_delim;
+
+ $self->YYData->{INPUT} = $data;
+ $self->YYData->{LINE} = 0;
+ $self->YYData->{LAST_TOKEN} = "NONE";
+ return $self->YYParse( yylex => \&_ASN1_Lexer, yyerror => \&_ASN1_Error );
+}
diff --git a/source4/build/pasn1/pasn1.pl b/source4/build/pasn1/pasn1.pl
new file mode 100755
index 0000000000..c3689fd928
--- /dev/null
+++ b/source4/build/pasn1/pasn1.pl
@@ -0,0 +1,93 @@
+#!/usr/bin/perl -w
+
+###################################################
+# package to parse ASN.1 files and generate code for
+# LDAP functions in Samba
+# Copyright tridge@samba.org 2002-2003
+# Copyright metze@samba.org 2004
+
+# released under the GNU GPL
+
+use strict;
+
+use FindBin qw($RealBin);
+use lib "$RealBin";
+use lib "$RealBin/lib";
+use Getopt::Long;
+use File::Basename;
+use asn1;
+use util;
+
+my($opt_help) = 0;
+my($opt_output);
+
+my $asn1_parser = new asn1;
+
+#####################################################################
+# parse an ASN.1 file returning a structure containing all the data
+sub ASN1Parse($)
+{
+ my $filename = shift;
+ my $asn1 = $asn1_parser->parse_asn1($filename);
+ util::CleanData($asn1);
+ return $asn1;
+}
+
+
+#########################################
+# display help text
+sub ShowHelp()
+{
+ print "
+ perl ASN.1 parser and code generator
+ Copyright (C) tridge\@samba.org
+ Copyright (C) metze\@samba.org
+
+ Usage: pasn1.pl [options] <asn1file>
+
+ Options:
+ --help this help page
+ --output OUTNAME put output in OUTNAME
+ \n";
+ exit(0);
+}
+
+# main program
+GetOptions (
+ 'help|h|?' => \$opt_help,
+ 'output|o=s' => \$opt_output,
+ );
+
+if ($opt_help) {
+ ShowHelp();
+ exit(0);
+}
+
+sub process_file($)
+{
+ my $input_file = shift;
+ my $output_file;
+ my $pasn1;
+
+ my $basename = basename($input_file, ".asn1");
+
+ if (!defined($opt_output)) {
+ $output_file = util::ChangeExtension($input_file, ".pasn1");
+ } else {
+ $output_file = $opt_output;
+ }
+
+# if (file is .pasn1) {
+# $pasn1 = util::LoadStructure($pasn1_file);
+# defined $pasn1 || die "Failed to load $pasn1_file - maybe you need --parse\n";
+# } else {
+ $pasn1 = ASN1Parse($input_file);
+ defined $pasn1 || die "Failed to parse $input_file";
+ util::SaveStructure($output_file, $pasn1) ||
+ die "Failed to save $output_file\n";
+ #}
+}
+
+foreach my $filename (@ARGV) {
+ process_file($filename);
+}
diff --git a/source4/build/pasn1/util.pm b/source4/build/pasn1/util.pm
new file mode 100644
index 0000000000..f822222b45
--- /dev/null
+++ b/source4/build/pasn1/util.pm
@@ -0,0 +1,379 @@
+###################################################
+# utility functions to support pidl
+# Copyright tridge@samba.org 2000
+# released under the GNU GPL
+package util;
+
+#####################################################################
+# load a data structure from a file (as saved with SaveStructure)
+sub LoadStructure($)
+{
+ my $f = shift;
+ my $contents = FileLoad($f);
+ defined $contents || return undef;
+ return eval "$contents";
+}
+
+use strict;
+
+#####################################################################
+# flatten an array of arrays into a single array
+sub FlattenArray2($)
+{
+ my $a = shift;
+ my @b;
+ for my $d (@{$a}) {
+ for my $d1 (@{$d}) {
+ push(@b, $d1);
+ }
+ }
+ return \@b;
+}
+
+#####################################################################
+# flatten an array of arrays into a single array
+sub FlattenArray($)
+{
+ my $a = shift;
+ my @b;
+ for my $d (@{$a}) {
+ for my $d1 (@{$d}) {
+ push(@b, $d1);
+ }
+ }
+ return \@b;
+}
+
+#####################################################################
+# flatten an array of hashes into a single hash
+sub FlattenHash($)
+{
+ my $a = shift;
+ my %b;
+ for my $d (@{$a}) {
+ for my $k (keys %{$d}) {
+ $b{$k} = $d->{$k};
+ }
+ }
+ return \%b;
+}
+
+
+#####################################################################
+# traverse a perl data structure removing any empty arrays or
+# hashes and any hash elements that map to undef
+sub CleanData($)
+{
+ sub CleanData($);
+ my($v) = shift;
+ if (ref($v) eq "ARRAY") {
+ foreach my $i (0 .. $#{$v}) {
+ CleanData($v->[$i]);
+ if (ref($v->[$i]) eq "ARRAY" && $#{$v->[$i]}==-1) {
+ $v->[$i] = undef;
+ next;
+ }
+ }
+ # this removes any undefined elements from the array
+ @{$v} = grep { defined $_ } @{$v};
+ } elsif (ref($v) eq "HASH") {
+ foreach my $x (keys %{$v}) {
+ CleanData($v->{$x});
+ if (!defined $v->{$x}) { delete($v->{$x}); next; }
+ if (ref($v->{$x}) eq "ARRAY" && $#{$v->{$x}}==-1) { delete($v->{$x}); next; }
+ }
+ }
+}
+
+
+#####################################################################
+# return the modification time of a file
+sub FileModtime($)
+{
+ my($filename) = shift;
+ return (stat($filename))[9];
+}
+
+
+#####################################################################
+# read a file into a string
+sub FileLoad($)
+{
+ my($filename) = shift;
+ local(*INPUTFILE);
+ open(INPUTFILE, $filename) || return undef;
+ my($saved_delim) = $/;
+ undef $/;
+ my($data) = <INPUTFILE>;
+ close(INPUTFILE);
+ $/ = $saved_delim;
+ return $data;
+}
+
+#####################################################################
+# write a string into a file
+sub FileSave($$)
+{
+ my($filename) = shift;
+ my($v) = shift;
+ local(*FILE);
+ open(FILE, ">$filename") || die "can't open $filename";
+ print FILE $v;
+ close(FILE);
+}
+
+#####################################################################
+# return a filename with a changed extension
+sub ChangeExtension($$)
+{
+ my($fname) = shift;
+ my($ext) = shift;
+ if ($fname =~ /^(.*)\.(.*?)$/) {
+ return "$1$ext";
+ }
+ return "$fname$ext";
+}
+
+#####################################################################
+# a dumper wrapper to prevent dependence on the Data::Dumper module
+# unless we actually need it
+sub MyDumper($)
+{
+ require Data::Dumper;
+ my $s = shift;
+ return Data::Dumper::Dumper($s);
+}
+
+#####################################################################
+# save a data structure into a file
+sub SaveStructure($$)
+{
+ my($filename) = shift;
+ my($v) = shift;
+ FileSave($filename, MyDumper($v));
+}
+
+#####################################################################
+# see if a pidl property list contains a give property
+sub has_property($$)
+{
+ my($e) = shift;
+ my($p) = shift;
+
+ if (!defined $e->{PROPERTIES}) {
+ return undef;
+ }
+
+ return $e->{PROPERTIES}->{$p};
+}
+
+
+sub is_scalar_type($)
+{
+ my($type) = shift;
+
+ if ($type =~ /^u?int\d+/) {
+ return 1;
+ }
+ if ($type =~ /char|short|long|NTTIME|
+ time_t|error_status_t|boolean32|unsigned32|
+ HYPER_T|wchar_t|DATA_BLOB/x) {
+ return 1;
+ }
+
+ return 0;
+}
+
+# return the NDR alignment for a type
+sub type_align($)
+{
+ my($e) = shift;
+ my $type = $e->{TYPE};
+
+ if (need_wire_pointer($e)) {
+ return 4;
+ }
+
+ return 4, if ($type eq "uint32");
+ return 4, if ($type eq "long");
+ return 2, if ($type eq "short");
+ return 1, if ($type eq "char");
+ return 1, if ($type eq "uint8");
+ return 2, if ($type eq "uint16");
+ return 4, if ($type eq "NTTIME");
+ return 4, if ($type eq "time_t");
+ return 8, if ($type eq "HYPER_T");
+ return 2, if ($type eq "wchar_t");
+ return 4, if ($type eq "DATA_BLOB");
+
+ # it must be an external type - all we can do is guess
+ return 4;
+}
+
+# this is used to determine if the ndr push/pull functions will need
+# a ndr_flags field to split by buffers/scalars
+sub is_builtin_type($)
+{
+ my($type) = shift;
+
+ return 1, if (is_scalar_type($type));
+
+ return 0;
+}
+
+# determine if an element needs a reference pointer on the wire
+# in its NDR representation
+sub need_wire_pointer($)
+{
+ my $e = shift;
+ if ($e->{POINTERS} &&
+ !has_property($e, "ref")) {
+ return $e->{POINTERS};
+ }
+ return undef;
+}
+
+# determine if an element is a pass-by-reference structure
+sub is_ref_struct($)
+{
+ my $e = shift;
+ if (!is_scalar_type($e->{TYPE}) &&
+ has_property($e, "ref")) {
+ return 1;
+ }
+ return 0;
+}
+
+# determine if an element is a pure scalar. pure scalars do not
+# have a "buffers" section in NDR
+sub is_pure_scalar($)
+{
+ my $e = shift;
+ if (has_property($e, "ref")) {
+ return 1;
+ }
+ if (is_scalar_type($e->{TYPE}) &&
+ !$e->{POINTERS} &&
+ !array_size($e)) {
+ return 1;
+ }
+ return 0;
+}
+
+# determine the array size (size_is() or ARRAY_LEN)
+sub array_size($)
+{
+ my $e = shift;
+ my $size = has_property($e, "size_is");
+ if ($size) {
+ return $size;
+ }
+ $size = $e->{ARRAY_LEN};
+ if ($size) {
+ return $size;
+ }
+ return undef;
+}
+
+# see if a variable needs to be allocated by the NDR subsystem on pull
+sub need_alloc($)
+{
+ my $e = shift;
+
+ if (has_property($e, "ref")) {
+ return 0;
+ }
+
+ if ($e->{POINTERS} || array_size($e)) {
+ return 1;
+ }
+
+ return 0;
+}
+
+# determine the C prefix used to refer to a variable when passing to a push
+# function. This will be '*' for pointers to scalar types, '' for scalar
+# types and normal pointers and '&' for pass-by-reference structures
+sub c_push_prefix($)
+{
+ my $e = shift;
+
+ if ($e->{TYPE} =~ "string") {
+ return "";
+ }
+
+ if (is_scalar_type($e->{TYPE}) &&
+ $e->{POINTERS}) {
+ return "*";
+ }
+ if (!is_scalar_type($e->{TYPE}) &&
+ !$e->{POINTERS} &&
+ !array_size($e)) {
+ return "&";
+ }
+ return "";
+}
+
+
+# determine the C prefix used to refer to a variable when passing to a pull
+# return '&' or ''
+sub c_pull_prefix($)
+{
+ my $e = shift;
+
+ if (!$e->{POINTERS} && !array_size($e)) {
+ return "&";
+ }
+
+ if ($e->{TYPE} =~ "string") {
+ return "&";
+ }
+
+ return "";
+}
+
+# determine if an element has a direct buffers component
+sub has_direct_buffers($)
+{
+ my $e = shift;
+ if ($e->{POINTERS} || array_size($e)) {
+ return 1;
+ }
+ return 0;
+}
+
+# return 1 if the string is a C constant
+sub is_constant($)
+{
+ my $s = shift;
+ if ($s =~ /^\d/) {
+ return 1;
+ }
+ return 0;
+}
+
+# return 1 if this is a fixed array
+sub is_fixed_array($)
+{
+ my $e = shift;
+ my $len = $e->{"ARRAY_LEN"};
+ if (defined $len && is_constant($len)) {
+ return 1;
+ }
+ return 0;
+}
+
+# return 1 if this is a inline array
+sub is_inline_array($)
+{
+ my $e = shift;
+ my $len = $e->{"ARRAY_LEN"};
+ if (is_fixed_array($e) ||
+ defined $len && $len ne "*") {
+ return 1;
+ }
+ return 0;
+}
+
+1;
+
diff --git a/source4/build/smb_build/README.txt b/source4/build/smb_build/README.txt
new file mode 100644
index 0000000000..eac3905cce
--- /dev/null
+++ b/source4/build/smb_build/README.txt
@@ -0,0 +1,83 @@
+The Samba Build System
+======================
+
+The build system basically has two main parts: the autoconf-generated
+shell scripts which check for availability of functions and libraries
+which is stored in the .m4 files and the information about the various
+subsystems which is stored in the .mk files.
+
+Object Types
+------------
+the build system knows about the following object types
+
+SUBSYSTEM:
+ a SUBSYSTEM is basicly a collection of functions, which provide an
+ an generic API for a specific problem (e.g. libldb provides an api
+ for gneric ldb databases. libldb_plugin provides a generic api
+ for calling ldb plugins, so 'libldb' and 'libldb_plugin' are subsystems)
+
+MODULE:
+ a MODULE is a specify implementation of a API provided by a SUBSYSTEM.
+ (e.g. 'libldb_tdb' and 'libldb_ldap' are implementations of the subsystem 'libldb' API,
+ and 'libldb_plugin_timestamp' is a module of the 'libldb_plugin' subsystem)
+
+EXT_LIB:
+ an EXT_LIB is an external library which is needed by a SUBSYSTEM, MODULE, BINARY or LIBRARY.
+ (e.g. 'gtk' or 'KRB5')
+
+BINARY:
+ a BINARY means a executable binary file.
+ (e.g. 'smbtorture' or 'ldbedit')
+ a BINARY typically has only commandline handling and basic
+ functionality code in it and depends on the functions of
+ SUBSYSTEM's (REQUIRED_SUBSYSTEMS).
+
+LIBRARY:
+ a LIBRARY means a static and/or shared library,
+ which depends on the used OS.
+ (e.g. for libldb 'libldb.so', 'libldb.so.0' 'libldb.so.0.0.1'
+ and libldb.a are created on linux)
+ a LIBRARY typicly has only glue code in it and depends on
+ SUBSYSTEM's (REQUIRED_SUBSYSTEMS).
+
+File summary:
+-------------
+public.m4 - public M4 macros of the build system
+config_mk.pm - Support for reading .mk files
+dot.pm - Support for generating .dot files for analysis of dependencies
+input.pm - Input validation
+main.pm - Main
+makefile.pm - Makefile generation
+output.pm - Dependency calculation
+
+Layout
+-------
+
+Toplevel file: configure.in
+- included by autogen.sh: aclocal.m4
+ which includes the SMB_YXZ*() macros
+
+- default tests of the build system
+ are in build/smb_build/check_*.m4
+ (mostly compiler and basic C type and function
+ checks)
+
+- subsystem specific stuff should be included by 'SMB_INCLUDE_M4()'
+
+
+Generating the configure file
+-------------------------
+you need to rerun ./autogen.sh when 'configure.in' or any
+'.m4' file was modified, then you need to rerun configure.
+
+
+Generating config.status
+-----------------------------
+you need to run ./config.status (or 'configure') after a '.mk'
+file was changed.
+
+
+Examples
+--------
+for now please take a look at the .m4 and .mk files
+you find in the source tree, they should be a good reference to start.
diff --git a/source4/build/smb_build/TODO b/source4/build/smb_build/TODO
new file mode 100644
index 0000000000..adfe17d423
--- /dev/null
+++ b/source4/build/smb_build/TODO
@@ -0,0 +1,25 @@
+- use pkg-config files in the buildsystem?
+- let the build system implement some make functions($(patsubst),$(wildcard),...) and use our own implementations where `make' does not support them
+- include extra_flags.txt using Makefile construction if
+ supported by current make
+- fix shared module loading for selftest during builds without install
+- remove recursive dependency between LIBSOCKET, LIBCLI_NBT and LIBCLI_RESOLVE
+- clearer distinction between dcerpc and ndr. seperate interface tables? Maybe get rid of
+ NDR's table altogether and use dlopen/dlsym ?
+- saner names for:
+ libcli.so.0.0.1 (rename to libsmb?)
+ libcli_cldap.so.0.0.1 (rename to libcldap?)
+ libcli_nbt.so.0.0.1 (rename to libnbt?)
+ libcli_wrepl.so.0.0.1 (rename to libwrepl?)
+- generate headermap.txt
+
+set of test scripts that check the code:
+- configure_check_unused.pl
+- find_unused_macros.pl
+- find_unused_makefilevars.pl
+- find_unused_options.sh
+- findstatic.pl
+- minimal_includes.pl
+- check dependencies based on #include lines ?
+- check whether private headers are not used outside their own subsystem
+- undocumented (no manpage) installed binaries
diff --git a/source4/build/smb_build/config_mk.pm b/source4/build/smb_build/config_mk.pm
new file mode 100644
index 0000000000..8c7d75221c
--- /dev/null
+++ b/source4/build/smb_build/config_mk.pm
@@ -0,0 +1,284 @@
+# Samba Build System
+# - config.mk parsing functions
+#
+# Copyright (C) Stefan (metze) Metzmacher 2004
+# Copyright (C) Jelmer Vernooij 2005
+# Released under the GNU GPL
+#
+
+package smb_build::config_mk;
+use smb_build::input;
+use File::Basename;
+
+use strict;
+
+my $section_types = {
+ "EXT_LIB" => {
+ "LIBS" => "list",
+ "CFLAGS" => "list",
+ "CPPFLAGS" => "list",
+ "LDFLAGS" => "list",
+ },
+ "PYTHON" => {
+ "LIBRARY_REALNAME" => "string",
+ "PRIVATE_DEPENDENCIES" => "list",
+ "PUBLIC_DEPENDENCIES" => "list",
+ "ENABLE" => "bool",
+ "LDFLAGS" => "list",
+ },
+ "SUBSYSTEM" => {
+ "PRIVATE_DEPENDENCIES" => "list",
+ "PUBLIC_DEPENDENCIES" => "list",
+
+ "ENABLE" => "bool",
+
+ "CFLAGS" => "list",
+ "LDFLAGS" => "list",
+ "STANDARD_VISIBILITY" => "string",
+ "INIT_FUNCTION_SENTINEL" => "string"
+ },
+ "MODULE" => {
+ "SUBSYSTEM" => "string",
+
+ "INIT_FUNCTION" => "string",
+
+ "PRIVATE_DEPENDENCIES" => "list",
+
+ "ALIASES" => "list",
+
+ "ENABLE" => "bool",
+
+ "OUTPUT_TYPE" => "list",
+
+ "CFLAGS" => "list"
+ },
+ "BINARY" => {
+
+ "PRIVATE_DEPENDENCIES" => "list",
+
+ "ENABLE" => "bool",
+
+ "INSTALLDIR" => "string",
+ "LDFLAGS" => "list",
+ "STANDARD_VISIBILITY" => "string",
+
+ "USE_HOSTCC" => "bool"
+ },
+ "LIBRARY" => {
+ "LIBRARY_REALNAME" => "string",
+
+ "INIT_FUNCTION_TYPE" => "string",
+ "INIT_FUNCTION_SENTINEL" => "string",
+ "OUTPUT_TYPE" => "list",
+
+ "PRIVATE_DEPENDENCIES" => "list",
+ "PUBLIC_DEPENDENCIES" => "list",
+
+ "ENABLE" => "bool",
+
+ "CFLAGS" => "list",
+ "LDFLAGS" => "list",
+ "STANDARD_VISIBILITY" => "string"
+ }
+};
+
+use vars qw(@parsed_files);
+
+@parsed_files = ();
+
+sub _read_config_file($$$)
+{
+ use Cwd;
+
+ my ($srcdir, $builddir, $filename) = @_;
+ my @dirlist;
+
+ # We need to change our working directory because config.mk files can
+ # give shell commands as the argument to "include". These shell
+ # commands can take arguments that are relative paths and we don't have
+ # a way of sensibly rewriting these.
+ my $cwd = getcwd;
+ chomp $cwd;
+
+ if ($srcdir ne $builddir) {
+ # Push the builddir path on the front, so we prefer builddir
+ # to srcdir when the file exists in both.
+ @dirlist = ($builddir, $srcdir);
+ } else {
+ @dirlist = ($srcdir);
+ }
+
+ foreach my $d (@dirlist) {
+ my @lines;
+ my $basedir;
+
+ chdir $cwd;
+ chdir $d;
+
+ # We need to catch the exception from open in the case where
+ # the filename is actually a shell pipeline. Why is this
+ # different to opening a regular file? Because this is perl!
+ eval {
+ open(CONFIG_MK, "./$filename");
+ @lines = <CONFIG_MK>;
+ close(CONFIG_MK);
+ };
+
+ chdir $cwd;
+ next unless (@lines);
+
+ # I blame abartlett for this crazy hack -- jpeach
+ if ($filename =~ /\|$/) {
+ $basedir = $builddir;
+ } else {
+ $basedir = dirname($filename);
+ push(@parsed_files, $filename);
+ }
+ $basedir =~ s!^($builddir|$srcdir)[/]!!;
+ return ($filename, $basedir, @lines);
+ }
+
+ chdir $cwd;
+ return;
+}
+
+###########################################################
+# The parsing function which parses the file
+#
+# $result = _parse_config_mk($input, $srcdir, $builddir, $filename)
+#
+# $filename - the path of the config.mk file
+# which should be parsed
+sub run_config_mk($$$$)
+{
+ sub run_config_mk($$$$);
+ my ($input, $srcdir, $builddir, $filename) = @_;
+ my $result;
+ my $linenum = -1;
+ my $infragment = 0;
+ my $section = "GLOBAL";
+ my $makefile = "";
+
+ my $basedir;
+
+ my $parsing_file;
+ my @lines;
+
+ $ENV{builddir} = $builddir;
+ $ENV{srcdir} = $srcdir;
+
+ ($parsing_file, $basedir, @lines) =
+ _read_config_file($srcdir, $builddir, $filename);
+
+ die ("$0: can't open '$filename'")
+ unless ($parsing_file and $basedir and @lines);
+
+ my $line = "";
+ my $prev = "";
+
+ # Emit a line that lets us match up final makefile output with the
+ # corresponding input files. The curlies are so you can match the
+ # BEGIN/END pairs in a text editor.
+ $makefile .= "# BEGIN{ $parsing_file\n";
+
+ foreach (@lines) {
+ $linenum++;
+
+ # lines beginning with '#' are ignored
+ next if (/^\#.*$/);
+
+ if (/^(.*)\\$/) {
+ $prev .= $1;
+ next;
+ } else {
+ $line = "$prev$_";
+ $prev = "";
+ }
+
+ if ($line =~ /^\[([-a-zA-Z0-9_.:]+)\][\t ]*$/)
+ {
+ $section = $1;
+ $infragment = 0;
+
+ $result->{$section}{EXISTS}{KEY} = "EXISTS";
+ $result->{$section}{EXISTS}{VAL} = 1;
+ next;
+ }
+
+ # include
+ if ($line =~ /^mkinclude (.*)$/) {
+ my $subfile= $1;
+ my $subdir = dirname($filename);
+ $subdir =~ s/^\.$//g;
+ $subdir =~ s/^\.\///g;
+ $subdir .= "/" if ($subdir ne "");
+ $makefile .= "basedir := $subdir\n";
+ $makefile .= run_config_mk($input, $srcdir, $builddir, $subdir.$subfile);
+ next;
+ }
+
+ # empty line
+ if ($line =~ /^[ \t]*$/) {
+ $section = "GLOBAL";
+ if ($infragment) { $makefile.="\n"; }
+ next;
+ }
+
+ # global stuff is considered part of the makefile
+ if ($section eq "GLOBAL") {
+ if (!$infragment) { $makefile.="\n"; }
+ $makefile .= $line;
+ $infragment = 1;
+ next;
+ }
+
+ # Assignment
+ if ($line =~ /^([a-zA-Z0-9_]+)[\t ]*=(.*)$/) {
+ $result->{$section}{$1}{VAL} = $2;
+ $result->{$section}{$1}{KEY} = $1;
+
+ next;
+ }
+
+ die("$parsing_file:$linenum: Bad line");
+ }
+
+ $makefile .= "# }END $parsing_file\n";
+
+ foreach my $section (keys %{$result}) {
+ my ($type, $name) = split(/::/, $section, 2);
+
+ my $sectype = $section_types->{$type};
+ if (not defined($sectype)) {
+ die($parsing_file.":[".$section."] unknown section type \"".$type."\"!");
+ }
+
+ $input->{$name}{NAME} = $name;
+ $input->{$name}{TYPE} = $type;
+ $input->{$name}{MK_FILE} = $parsing_file;
+ $input->{$name}{BASEDIR} = $basedir;
+
+ foreach my $key (values %{$result->{$section}}) {
+ next if ($key->{KEY} eq "EXISTS");
+ $key->{VAL} = smb_build::input::strtrim($key->{VAL});
+ my $vartype = $sectype->{$key->{KEY}};
+ if (not defined($vartype)) {
+ die($parsing_file.":[".$section."]: unknown attribute type \"$key->{KEY}\"!");
+ }
+ if ($vartype eq "string") {
+ $input->{$name}{$key->{KEY}} = $key->{VAL};
+ } elsif ($vartype eq "list") {
+ $input->{$name}{$key->{KEY}} = [smb_build::input::str2array($key->{VAL})];
+ } elsif ($vartype eq "bool") {
+ if (($key->{VAL} ne "YES") and ($key->{VAL} ne "NO")) {
+ die("Invalid value for bool attribute $key->{KEY}: $key->{VAL} in section $section");
+ }
+ $input->{$name}{$key->{KEY}} = $key->{VAL};
+ }
+ }
+ }
+
+ return $makefile;
+}
+
+1;
diff --git a/source4/build/smb_build/dot.pl b/source4/build/smb_build/dot.pl
new file mode 100755
index 0000000000..b30c320c6e
--- /dev/null
+++ b/source4/build/smb_build/dot.pl
@@ -0,0 +1,63 @@
+#!/usr/bin/perl
+# Samba4 Dependency Graph Generator
+# (C) 2004-2005 Jelmer Vernooij <jelmer@samba.org>
+# Published under the GNU GPL
+
+use strict;
+use lib 'build';
+use smb_build::config_mk;
+
+my $subsys = shift @ARGV;
+
+sub contains($$)
+{
+ my ($haystack,$needle) = @_;
+ foreach (@$haystack) {
+ return 1 if ($_ eq $needle);
+ }
+ return 0;
+}
+
+sub generate($$$)
+{
+ my ($depend,$only,$name) = @_;
+ my $res = "digraph $name {\n";
+
+ foreach my $part (values %{$depend}) {
+ next if (defined($only) and not contains($only,$part->{NAME}));
+ foreach my $elem (@{$part->{PUBLIC_DEPENDENCIES}}) {
+ $res .= "\t\"$part->{NAME}\" -> \"$elem\" [style=filled]; /* public */\n";
+ }
+ foreach my $elem (@{$part->{PRIVATE_DEPENDENCIES}}) {
+ $res .= "\t\"$part->{NAME}\" -> \"$elem\" [style=dotted]; /* private */\n";
+ }
+ }
+
+ return $res . "}\n";
+}
+
+my $INPUT = {};
+smb_build::config_mk::run_config_mk($INPUT, '.', '.', "main.mk");
+
+my $name = "samba4";
+
+my $only;
+if (defined($subsys)) {
+ my $DEPEND = smb_build::input::check($INPUT, \%config::enabled,
+ "MERGED_OBJ", "SHARED_LIBRARY", "SHARED_LIBRARY");
+
+ die("No such subsystem $subsys") unless (defined($DEPEND->{$subsys}));
+
+ $only = $DEPEND->{$subsys}->{UNIQUE_DEPENDENCIES_ALL};
+ push (@$only, "$subsys");
+
+ $name = $subsys;
+}
+
+my $fname = "$name-deps.dot";
+print __FILE__.": creating $fname\n";
+open DOTTY, ">$fname";
+print DOTTY generate($INPUT, $only, $name);
+close DOTTY;
+
+1;
diff --git a/source4/build/smb_build/input.pm b/source4/build/smb_build/input.pm
new file mode 100644
index 0000000000..df9a525f4f
--- /dev/null
+++ b/source4/build/smb_build/input.pm
@@ -0,0 +1,277 @@
+# Samba Build System
+# - the input checking functions
+#
+# Copyright (C) Stefan (metze) Metzmacher 2004
+# Copyright (C) Jelmer Vernooij 2004
+# Released under the GNU GPL
+
+use strict;
+package smb_build::input;
+use File::Basename;
+
+sub strtrim($)
+{
+ $_ = shift;
+ s/^[\t\n ]*//g;
+ s/[\t\n ]*$//g;
+ return $_;
+}
+
+sub str2array($)
+{
+ $_ = shift;
+ s/^[\t\n ]*//g;
+ s/[\t\n ]*$//g;
+ s/([\t\n ]+)/ /g;
+
+ return () if (length($_)==0);
+ return split /[ \t\n]/;
+}
+
+sub add_libreplace($)
+{
+ my ($part) = @_;
+
+ return if ($part->{NAME} eq "LIBREPLACE");
+ return if ($part->{NAME} eq "LIBREPLACE_HOSTCC");
+ return if ($part->{NAME} eq "REPLACE_READDIR");
+
+ foreach my $n (@{$part->{PRIVATE_DEPENDENCIES}}) {
+ return if ($n eq "LIBREPLACE");
+ return if ($n eq "LIBREPLACE_HOSTCC");
+ }
+ foreach my $n (@{$part->{PUBLIC_DEPENDENCIES}}) {
+ return if ($n eq "LIBREPLACE");
+ return if ($n eq "LIBREPLACE_HOSTCC");
+ }
+
+ if (defined($part->{USE_HOSTCC}) && $part->{USE_HOSTCC} eq "YES") {
+ unshift (@{$part->{PRIVATE_DEPENDENCIES}}, "LIBREPLACE_HOSTCC");
+ } else {
+ unshift (@{$part->{PRIVATE_DEPENDENCIES}}, "LIBREPLACE");
+ }
+}
+
+sub check_subsystem($$$)
+{
+ my ($INPUT, $subsys, $default_ot) = @_;
+ return if ($subsys->{ENABLE} ne "YES");
+
+ unless (defined($subsys->{OUTPUT_TYPE})) { $subsys->{OUTPUT_TYPE} = $default_ot; }
+ unless (defined($subsys->{INIT_FUNCTION_TYPE})) { $subsys->{INIT_FUNCTION_TYPE} = "NTSTATUS (*) (void)"; }
+ unless (defined($subsys->{INIT_FUNCTION_SENTINEL})) { $subsys->{INIT_FUNCTION_SENTINEL} = "NULL"; }
+}
+
+sub check_module($$$)
+{
+ my ($INPUT, $mod, $default_ot) = @_;
+
+ die("Module $mod->{NAME} does not have a SUBSYSTEM set") if not defined($mod->{SUBSYSTEM});
+
+ if (not exists($INPUT->{$mod->{SUBSYSTEM}}{INIT_FUNCTIONS})) {
+ $INPUT->{$mod->{SUBSYSTEM}}{INIT_FUNCTIONS} = [];
+ }
+
+ if (!(defined($INPUT->{$mod->{SUBSYSTEM}}))) {
+ die("Unknown subsystem $mod->{SUBSYSTEM} for module $mod->{NAME}");
+ }
+
+ if ($INPUT->{$mod->{SUBSYSTEM}} eq "NO") {
+ warn("Disabling module $mod->{NAME} because subsystem $mod->{SUBSYSTEM} is disabled");
+ $mod->{ENABLE} = "NO";
+ return;
+ }
+
+ return if ($mod->{ENABLE} ne "YES");
+
+ if (exists($INPUT->{$mod->{SUBSYSTEM}}{INIT_FUNCTION_TYPE})) {
+ $mod->{INIT_FUNCTION_TYPE} = $INPUT->{$mod->{SUBSYSTEM}}{INIT_FUNCTION_TYPE};
+ } else {
+ $mod->{INIT_FUNCTION_TYPE} = "NTSTATUS (*) (void)";
+ }
+
+ unless (defined($mod->{INIT_FUNCTION_SENTINEL})) { $mod->{INIT_FUNCTION_SENTINEL} = "NULL"; }
+
+ if (not defined($mod->{OUTPUT_TYPE})) {
+ if ((not defined($INPUT->{$mod->{SUBSYSTEM}}->{TYPE})) or
+ $INPUT->{$mod->{SUBSYSTEM}}->{TYPE} eq "EXT_LIB") {
+ $mod->{OUTPUT_TYPE} = undef;
+ } else {
+ $mod->{OUTPUT_TYPE} = $default_ot;
+ }
+ }
+
+ if (grep(/SHARED_LIBRARY/, @{$mod->{OUTPUT_TYPE}})) {
+ my $sane_subsystem = lc($mod->{SUBSYSTEM});
+ $sane_subsystem =~ s/^lib//;
+ $mod->{INSTALLDIR} = "MODULESDIR/$sane_subsystem";
+ push (@{$mod->{PUBLIC_DEPENDENCIES}}, $mod->{SUBSYSTEM});
+ add_libreplace($mod);
+ }
+ if (grep(/MERGED_OBJ/, @{$mod->{OUTPUT_TYPE}}) and $mod->{TYPE} ne "PYTHON") {
+ push (@{$INPUT->{$mod->{SUBSYSTEM}}{INIT_FUNCTIONS}}, $mod->{INIT_FUNCTION}) if defined($mod->{INIT_FUNCTION});
+ push (@{$INPUT->{$mod->{SUBSYSTEM}}{PRIVATE_DEPENDENCIES}}, $mod->{NAME});
+ }
+}
+
+sub check_library($$$)
+{
+ my ($INPUT, $lib, $default_ot) = @_;
+
+ return if ($lib->{ENABLE} ne "YES");
+
+ unless (defined($lib->{OUTPUT_TYPE})) { $lib->{OUTPUT_TYPE} = $default_ot; }
+
+ unless (defined($lib->{INIT_FUNCTION_TYPE})) { $lib->{INIT_FUNCTION_TYPE} = "NTSTATUS (*) (void)"; }
+ unless (defined($lib->{INIT_FUNCTION_SENTINEL})) { $lib->{INIT_FUNCTION_SENTINEL} = "NULL"; }
+ unless (defined($lib->{INSTALLDIR})) { $lib->{INSTALLDIR} = "LIBDIR"; }
+
+ add_libreplace($lib);
+}
+
+sub check_python($$$)
+{
+ my ($INPUT, $python, $default_ot) = @_;
+
+ return if ($INPUT->{LIBPYTHON}{ENABLE} ne "YES");
+
+ $python->{INSTALLDIR} = "PYTHONDIR";
+ unless (defined($python->{CFLAGS})) { $python->{CFLAGS} = []; }
+ my $basename = $python->{NAME};
+ $basename =~ s/^python_//g;
+ unless (defined($python->{LIBRARY_REALNAME})) {
+ $python->{LIBRARY_REALNAME} = "$basename.\$(SHLIBEXT)";
+ }
+ $python->{INIT_FUNCTION} = "{ (char *)\"$basename\", init$basename }";
+ push (@{$python->{CFLAGS}}, "\$(EXT_LIB_PYTHON_CFLAGS)");
+
+ $python->{SUBSYSTEM} = "LIBPYTHON";
+
+ check_module($INPUT, $python, $default_ot);
+}
+
+sub check_binary($$)
+{
+ my ($INPUT, $bin) = @_;
+
+ return if ($bin->{ENABLE} ne "YES");
+
+ ($bin->{BINARY} = (lc $bin->{NAME})) if not defined($bin->{BINARY});
+ unless (defined($bin->{INIT_FUNCTION_SENTINEL})) { $bin->{INIT_FUNCTION_SENTINEL} = "NULL"; }
+ unless (defined($bin->{INIT_FUNCTION_TYPE})) { $bin->{INIT_FUNCTION_TYPE} = "NTSTATUS (*) (void)"; }
+
+ $bin->{OUTPUT_TYPE} = ["BINARY"];
+ add_libreplace($bin);
+}
+
+sub add_implicit($$)
+{
+ my ($INPUT, $n) = @_;
+
+ $INPUT->{$n}->{TYPE} = "MAKE_RULE";
+ $INPUT->{$n}->{NAME} = $n;
+ $INPUT->{$n}->{OUTPUT_TYPE} = undef;
+ $INPUT->{$n}->{LIBS} = ["\$(".uc($n)."_LIBS)"];
+ $INPUT->{$n}->{LDFLAGS} = ["\$(".uc($n)."_LDFLAGS)"];
+ $INPUT->{$n}->{CFLAGS} = ["\$(".uc($n)."_CFLAGS)"];
+ $INPUT->{$n}->{CPPFLAGS} = ["\$(".uc($n)."_CPPFLAGS)"];
+ $INPUT->{$n}->{ENABLE} = "YES";
+}
+
+sub calc_unique_deps($$$$$$$$)
+{
+ sub calc_unique_deps($$$$$$$$);
+ my ($name, $INPUT, $deps, $udeps, $withlibs, $forward, $pubonly, $busy) = @_;
+
+ foreach my $n (@$deps) {
+ add_implicit($INPUT, $n) unless (defined($INPUT->{$n}) and defined($INPUT->{$n}->{TYPE}));
+ my $dep = $INPUT->{$n};
+ if (grep (/^$n$/, @$busy)) {
+ next if (@{$dep->{OUTPUT_TYPE}}[0] eq "MERGED_OBJ");
+ die("Recursive dependency: $n, list: " . join(',', @$busy));
+ }
+ next if (grep /^$n$/, @$udeps);
+
+ push (@{$udeps}, $n) if $forward;
+
+ if (defined ($dep->{OUTPUT_TYPE}) &&
+ ($withlibs or
+ (@{$dep->{OUTPUT_TYPE}}[0] eq "MERGED_OBJ") or
+ (@{$dep->{OUTPUT_TYPE}}[0] eq "STATIC_LIBRARY"))) {
+ push (@$busy, $n);
+ calc_unique_deps($n, $INPUT, $dep->{PUBLIC_DEPENDENCIES}, $udeps, $withlibs, $forward, $pubonly, $busy);
+ calc_unique_deps($n, $INPUT, $dep->{PRIVATE_DEPENDENCIES}, $udeps, $withlibs, $forward, $pubonly, $busy) unless $pubonly;
+ pop (@$busy);
+ }
+
+ unshift (@{$udeps}, $n) unless $forward;
+ }
+}
+
+sub check($$$$$)
+{
+ my ($INPUT, $enabled, $subsys_ot, $lib_ot, $module_ot) = @_;
+
+ foreach my $part (values %$INPUT) {
+ if (defined($enabled->{$part->{NAME}})) {
+ $part->{ENABLE} = $enabled->{$part->{NAME}};
+ next;
+ }
+
+ unless(defined($part->{ENABLE})) {
+ if ($part->{TYPE} eq "EXT_LIB") {
+ $part->{ENABLE} = "NO";
+ } else {
+ $part->{ENABLE} = "YES";
+ }
+ }
+ }
+
+ foreach my $part (values %$INPUT) {
+ $part->{LINK_FLAGS} = [];
+ $part->{FULL_OBJ_LIST} = ["\$($part->{NAME}_OBJ_FILES)"];
+
+ if ($part->{TYPE} eq "SUBSYSTEM") {
+ check_subsystem($INPUT, $part, $subsys_ot);
+ } elsif ($part->{TYPE} eq "MODULE") {
+ check_module($INPUT, $part, $module_ot);
+ } elsif ($part->{TYPE} eq "LIBRARY") {
+ check_library($INPUT, $part, $lib_ot);
+ } elsif ($part->{TYPE} eq "BINARY") {
+ check_binary($INPUT, $part);
+ } elsif ($part->{TYPE} eq "PYTHON") {
+ check_python($INPUT, $part, $module_ot);
+ } elsif ($part->{TYPE} eq "EXT_LIB") {
+ } else {
+ die("Unknown type $part->{TYPE}");
+ }
+ }
+
+ foreach my $part (values %$INPUT) {
+ if (defined($part->{INIT_FUNCTIONS})) {
+ push (@{$part->{LINK_FLAGS}}, "\$(DYNEXP)");
+ }
+ }
+
+ foreach my $part (values %$INPUT) {
+ $part->{UNIQUE_DEPENDENCIES_LINK} = [];
+ calc_unique_deps($part->{NAME}, $INPUT, $part->{PUBLIC_DEPENDENCIES}, $part->{UNIQUE_DEPENDENCIES_LINK}, 0, 0, 0, []);
+ calc_unique_deps($part->{NAME}, $INPUT, $part->{PRIVATE_DEPENDENCIES}, $part->{UNIQUE_DEPENDENCIES_LINK}, 0, 0, 0, []);
+ }
+
+ foreach my $part (values %$INPUT) {
+ $part->{UNIQUE_DEPENDENCIES_COMPILE} = [];
+ calc_unique_deps($part->{NAME}, $INPUT, $part->{PUBLIC_DEPENDENCIES}, $part->{UNIQUE_DEPENDENCIES_COMPILE}, 1, 1, 1, []);
+ calc_unique_deps($part->{NAME}, $INPUT, $part->{PRIVATE_DEPENDENCIES}, $part->{UNIQUE_DEPENDENCIES_COMPILE}, 1, 1, 1, []);
+ }
+
+ foreach my $part (values %$INPUT) {
+ $part->{UNIQUE_DEPENDENCIES_ALL} = [];
+ calc_unique_deps($part->{NAME}, $INPUT, $part->{PUBLIC_DEPENDENCIES}, $part->{UNIQUE_DEPENDENCIES_ALL}, 1, 0, 0, []);
+ calc_unique_deps($part->{NAME}, $INPUT, $part->{PRIVATE_DEPENDENCIES}, $part->{UNIQUE_DEPENDENCIES_ALL}, 1, 0, 0, []);
+ }
+
+ return $INPUT;
+}
+
+1;
diff --git a/source4/build/smb_build/main.pl b/source4/build/smb_build/main.pl
new file mode 100644
index 0000000000..0d19e41827
--- /dev/null
+++ b/source4/build/smb_build/main.pl
@@ -0,0 +1,104 @@
+# Samba Build System
+# - the main program
+#
+# Copyright (C) Stefan (metze) Metzmacher 2004
+# Copyright (C) Jelmer Vernooij 2005
+# Released under the GNU GPL
+
+use smb_build::makefile;
+use smb_build::input;
+use smb_build::config_mk;
+use smb_build::output;
+use smb_build::summary;
+use smb_build::config;
+use Getopt::Long;
+use strict;
+
+my $output_file = "data.mk";
+
+my $result = GetOptions (
+ 'output=s' => \$output_file);
+
+if (not $result) {
+ exit(1);
+}
+
+my $input_file = shift @ARGV;
+
+my $INPUT = {};
+my $mkfile = smb_build::config_mk::run_config_mk($INPUT, $config::config{srcdir}, $config::config{builddir}, $input_file);
+
+my $subsys_output_type = ["MERGED_OBJ"];
+
+my $library_output_type;
+my $useshared = (defined($ENV{USESHARED})?$ENV{USESHARED}:$config::config{USESHARED});
+
+if ($useshared eq "true") {
+ $library_output_type = ["SHARED_LIBRARY", "MERGED_OBJ"];
+} else {
+ $library_output_type = ["MERGED_OBJ"];
+ push (@$library_output_type, "SHARED_LIBRARY") if
+ ($config::config{BLDSHARED} eq "true")
+}
+
+my $module_output_type;
+if ($useshared eq "true") {
+ $module_output_type = ["SHARED_LIBRARY"];
+} else {
+ $module_output_type = ["MERGED_OBJ"];
+}
+
+my $DEPEND = smb_build::input::check($INPUT, \%config::enabled,
+ $subsys_output_type,
+ $library_output_type,
+ $module_output_type);
+my $OUTPUT = output::create_output($DEPEND, \%config::config);
+my $mkenv = new smb_build::makefile(\%config::config, $mkfile);
+
+my $shared_libs_used = 0;
+foreach my $key (values %$OUTPUT) {
+ next if ($key->{ENABLE} ne "YES");
+ push(@{$mkenv->{all_objs}}, "\$($key->{NAME}_OBJ_FILES)");
+}
+
+foreach my $key (values %$OUTPUT) {
+ next unless defined $key->{OUTPUT_TYPE};
+
+ $mkenv->StaticLibraryPrimitives($key) if grep(/STATIC_LIBRARY/, @{$key->{OUTPUT_TYPE}});
+ $mkenv->MergedObj($key) if grep(/MERGED_OBJ/, @{$key->{OUTPUT_TYPE}});
+ $mkenv->SharedLibraryPrimitives($key) if ($key->{TYPE} eq "LIBRARY") and
+ grep(/SHARED_LIBRARY/, @{$key->{OUTPUT_TYPE}});
+ if ($key->{TYPE} eq "LIBRARY" and
+ ${$key->{OUTPUT_TYPE}}[0] eq "SHARED_LIBRARY") {
+ $shared_libs_used = 1;
+ }
+ if ($key->{TYPE} eq "MODULE" and @{$key->{OUTPUT_TYPE}}[0] eq "MERGED_OBJ" and defined($key->{INIT_FUNCTION})) {
+ $mkenv->output("$key->{SUBSYSTEM}_INIT_FUNCTIONS +=$key->{INIT_FUNCTION},\n");
+ }
+ $mkenv->CFlags($key);
+}
+
+foreach my $key (values %$OUTPUT) {
+ next unless defined $key->{OUTPUT_TYPE};
+
+ $mkenv->Integrated($key) if grep(/INTEGRATED/, @{$key->{OUTPUT_TYPE}});
+}
+
+foreach my $key (values %$OUTPUT) {
+ next unless defined $key->{OUTPUT_TYPE};
+ $mkenv->StaticLibrary($key) if grep(/STATIC_LIBRARY/, @{$key->{OUTPUT_TYPE}});
+
+ $mkenv->SharedLibrary($key) if ($key->{TYPE} eq "LIBRARY") and
+ grep(/SHARED_LIBRARY/, @{$key->{OUTPUT_TYPE}});
+ $mkenv->SharedModule($key) if ($key->{TYPE} eq "MODULE" and
+ grep(/SHARED_LIBRARY/, @{$key->{OUTPUT_TYPE}}));
+ $mkenv->PythonModule($key) if ($key->{TYPE} eq "PYTHON");
+ $mkenv->Binary($key) if grep(/BINARY/, @{$key->{OUTPUT_TYPE}});
+ $mkenv->InitFunctions($key) if defined($key->{INIT_FUNCTIONS});
+}
+
+$mkenv->write($output_file);
+
+summary::show($OUTPUT, \%config::config);
+
+1;
diff --git a/source4/build/smb_build/makefile.pm b/source4/build/smb_build/makefile.pm
new file mode 100644
index 0000000000..a80d10733b
--- /dev/null
+++ b/source4/build/smb_build/makefile.pm
@@ -0,0 +1,281 @@
+# Samba Build System
+# - create output for Makefile
+#
+# Copyright (C) Stefan (metze) Metzmacher 2004
+# Copyright (C) Jelmer Vernooij 2005
+# Released under the GNU GPL
+
+package smb_build::makefile;
+use smb_build::output;
+use File::Basename;
+use strict;
+
+use Cwd 'abs_path';
+
+sub new($$$)
+{
+ my ($myname, $config, $mkfile) = @_;
+ my $self = {};
+
+ bless($self, $myname);
+
+ $self->_set_config($config);
+
+ $self->{output} = "";
+
+ $self->output("################################################\n");
+ $self->output("# Autogenerated by build/smb_build/makefile.pm #\n");
+ $self->output("################################################\n");
+ $self->output("\n");
+ $self->output($mkfile);
+
+ return $self;
+}
+
+sub _set_config($$)
+{
+ my ($self, $config) = @_;
+
+ $self->{config} = $config;
+
+ if (not defined($self->{config}->{srcdir})) {
+ $self->{config}->{srcdir} = '.';
+ }
+
+ if (not defined($self->{config}->{builddir})) {
+ $self->{config}->{builddir} = '.';
+ }
+
+ if ($self->{config}->{prefix} eq "NONE") {
+ $self->{config}->{prefix} = $self->{config}->{ac_default_prefix};
+ }
+
+ if ($self->{config}->{exec_prefix} eq "NONE") {
+ $self->{config}->{exec_prefix} = $self->{config}->{prefix};
+ }
+}
+
+sub output($$)
+{
+ my ($self, $text) = @_;
+
+ $self->{output} .= $text;
+}
+
+sub _prepare_mk_files($)
+{
+ my $self = shift;
+ my @tmp = ();
+
+ foreach (@smb_build::config_mk::parsed_files) {
+ s/ .*$//g;
+ push (@tmp, $_);
+ }
+
+ $self->output("MK_FILES = " . array2oneperline(\@tmp) . "\n");
+}
+
+sub array2oneperline($)
+{
+ my $array = shift;
+ my $output = "";
+
+ foreach (@$array) {
+ next unless defined($_);
+
+ $output .= " \\\n\t\t$_";
+ }
+
+ return $output;
+}
+
+sub _prepare_list($$$)
+{
+ my ($self,$ctx,$var) = @_;
+ my @tmparr = ();
+
+ push(@tmparr, @{$ctx->{$var}}) if defined($ctx->{$var});
+
+ my $tmplist = array2oneperline(\@tmparr);
+ return if ($tmplist eq "");
+
+ $self->output("$ctx->{NAME}_$var =$tmplist\n");
+}
+
+sub PythonModule($$)
+{
+ my ($self,$ctx) = @_;
+
+ $self->_prepare_list($ctx, "FULL_OBJ_LIST");
+ $self->_prepare_list($ctx, "DEPEND_LIST");
+ $self->_prepare_list($ctx, "LINK_FLAGS");
+
+ $self->output("\$(eval \$(call python_c_module_template,$ctx->{LIBRARY_REALNAME},\$($ctx->{NAME}_DEPEND_LIST) \$($ctx->{NAME}_FULL_OBJ_LIST), \$($ctx->{NAME}\_FULL_OBJ_LIST) \$($ctx->{NAME}_LINK_FLAGS)))\n");
+}
+
+sub SharedModule($$)
+{
+ my ($self,$ctx) = @_;
+
+ my $sane_subsystem = lc($ctx->{SUBSYSTEM});
+ $sane_subsystem =~ s/^lib//;
+
+ $self->output("PLUGINS += $ctx->{SHAREDDIR}/$ctx->{LIBRARY_REALNAME}\n");
+ $self->output("\$(eval \$(call shared_module_install_template,$sane_subsystem,$ctx->{LIBRARY_REALNAME}))\n");
+
+ $self->_prepare_list($ctx, "FULL_OBJ_LIST");
+ $self->_prepare_list($ctx, "DEPEND_LIST");
+ $self->_prepare_list($ctx, "LINK_FLAGS");
+
+ if (defined($ctx->{INIT_FUNCTION}) and $ctx->{INIT_FUNCTION_TYPE} =~ /\(\*\)/ and not ($ctx->{INIT_FUNCTION} =~ /\(/)) {
+ $self->output("\$($ctx->{NAME}_OBJ_FILES): CFLAGS+=-D$ctx->{INIT_FUNCTION}=samba_init_module\n");
+ }
+
+ $self->output("\$(eval \$(call shared_module_template,$ctx->{SHAREDDIR}/$ctx->{LIBRARY_REALNAME}, \$($ctx->{NAME}_DEPEND_LIST) \$($ctx->{NAME}_FULL_OBJ_LIST), \$($ctx->{NAME}\_FULL_OBJ_LIST) \$($ctx->{NAME}_LINK_FLAGS)))\n");
+
+
+ if (defined($ctx->{ALIASES})) {
+ $self->output("\$(eval \$(foreach alias,". join(' ', @{$ctx->{ALIASES}}) . ",\$(call shared_module_alias_template,$ctx->{SHAREDDIR}/$ctx->{LIBRARY_REALNAME},$sane_subsystem,\$(alias))))\n");
+ }
+}
+
+sub StaticLibraryPrimitives($$)
+{
+ my ($self,$ctx) = @_;
+
+ $self->output("$ctx->{NAME}_OUTPUT = $ctx->{OUTPUT}\n");
+ $self->_prepare_list($ctx, "FULL_OBJ_LIST");
+}
+
+sub SharedLibraryPrimitives($$)
+{
+ my ($self,$ctx) = @_;
+
+ if (not grep(/STATIC_LIBRARY/, @{$ctx->{OUTPUT_TYPE}})) {
+ $self->output("$ctx->{NAME}_OUTPUT = $ctx->{OUTPUT}\n");
+ $self->_prepare_list($ctx, "FULL_OBJ_LIST");
+ }
+}
+
+sub SharedLibrary($$)
+{
+ my ($self,$ctx) = @_;
+
+ $self->output("SHARED_LIBS += $ctx->{RESULT_SHARED_LIBRARY}\n");
+
+ $self->_prepare_list($ctx, "DEPEND_LIST");
+ $self->_prepare_list($ctx, "LINK_FLAGS");
+
+ $self->output("\$(eval \$(call shared_library_template,$ctx->{RESULT_SHARED_LIBRARY}, \$($ctx->{NAME}_DEPEND_LIST) \$($ctx->{NAME}_FULL_OBJ_LIST), \$($ctx->{NAME}\_FULL_OBJ_LIST) \$($ctx->{NAME}_LINK_FLAGS),$ctx->{SHAREDDIR}/$ctx->{LIBRARY_SONAME},$ctx->{SHAREDDIR}/$ctx->{LIBRARY_DEBUGNAME}))\n");
+}
+
+sub MergedObj($$)
+{
+ my ($self, $ctx) = @_;
+
+ $self->output("\$(call partial_link_template, $ctx->{OUTPUT}, \$($ctx->{NAME}_OBJ_FILES))\n");
+}
+
+sub InitFunctions($$)
+{
+ my ($self, $ctx) = @_;
+ $self->output("\$($ctx->{NAME}_OBJ_FILES): CFLAGS+=-DSTATIC_$ctx->{NAME}_MODULES=\"\$($ctx->{NAME}_INIT_FUNCTIONS)$ctx->{INIT_FUNCTION_SENTINEL}\"\n");
+}
+
+sub StaticLibrary($$)
+{
+ my ($self,$ctx) = @_;
+
+ $self->output("STATIC_LIBS += $ctx->{RESULT_STATIC_LIBRARY}\n") if ($ctx->{TYPE} eq "LIBRARY");
+ $self->output("$ctx->{NAME}_OUTPUT = $ctx->{OUTPUT}\n");
+ $self->output("$ctx->{RESULT_STATIC_LIBRARY}: \$($ctx->{NAME}_FULL_OBJ_LIST)\n");
+}
+
+sub Binary($$)
+{
+ my ($self,$ctx) = @_;
+
+ unless (defined($ctx->{INSTALLDIR})) {
+ } elsif ($ctx->{INSTALLDIR} eq "SBINDIR") {
+ $self->output("\$(eval \$(call sbinary_install_template,$ctx->{RESULT_BINARY}))\n");
+ } elsif ($ctx->{INSTALLDIR} eq "BINDIR") {
+ $self->output("\$(eval \$(call binary_install_template,$ctx->{RESULT_BINARY}))\n");
+ }
+
+ $self->_prepare_list($ctx, "FULL_OBJ_LIST");
+ $self->_prepare_list($ctx, "DEPEND_LIST");
+ $self->_prepare_list($ctx, "LINK_FLAGS");
+
+ if (defined($ctx->{USE_HOSTCC}) && $ctx->{USE_HOSTCC} eq "YES") {
+$self->output("\$(eval \$(call host_binary_link_template, $ctx->{RESULT_BINARY}, \$($ctx->{NAME}_FULL_OBJ_LIST) \$($ctx->{NAME}_DEPEND_LIST), \$($ctx->{NAME}_LINK_FLAGS)))\n");
+ } else {
+$self->output("\$(eval \$(call binary_link_template, $ctx->{RESULT_BINARY}, \$($ctx->{NAME}_FULL_OBJ_LIST) \$($ctx->{NAME}_DEPEND_LIST), \$($ctx->{NAME}_LINK_FLAGS)))\n");
+ }
+}
+
+sub write($$)
+{
+ my ($self, $file) = @_;
+
+ $self->_prepare_mk_files();
+
+ $self->output("ALL_OBJS = " . array2oneperline($self->{all_objs}) . "\n");
+
+ open(MAKEFILE,">$file") || die ("Can't open $file\n");
+ print MAKEFILE $self->{output};
+ close(MAKEFILE);
+
+ print __FILE__.": creating $file\n";
+}
+
+my $sort_available = eval "use sort 'stable'; return 1;";
+$sort_available = 0 unless defined($sort_available);
+
+sub by_path {
+ return 1 if($a =~ m#^\-I/#);
+ return -1 if($b =~ m#^\-I/#);
+ return 0;
+}
+
+sub CFlags($$)
+{
+ my ($self, $key) = @_;
+
+ my $srcdir = $self->{config}->{srcdir};
+ my $builddir = $self->{config}->{builddir};
+
+ my $src_ne_build = ($srcdir ne $builddir) ? 1 : 0;
+
+ return unless defined ($key->{FINAL_CFLAGS});
+ return unless (@{$key->{FINAL_CFLAGS}} > 0);
+
+ my @sorted_cflags = @{$key->{FINAL_CFLAGS}};
+ if ($sort_available) {
+ @sorted_cflags = sort by_path @{$key->{FINAL_CFLAGS}};
+ }
+
+ # Rewrite CFLAGS so that both the source and the build
+ # directories are in the path.
+ my @cflags = ();
+ foreach my $flag (@sorted_cflags) {
+ if($src_ne_build) {
+ if($flag =~ m#^-I([^/].*$)#) {
+ my $dir = $1;
+ if ($dir =~ /^\$\(/) {
+ push (@cflags, $flag);
+ next;
+ }
+ $dir =~ s#^\$\((?:src|build)dir\)/?##;
+ push(@cflags, "-I$builddir/$dir", "-I$srcdir/$dir");
+ next;
+ }
+ }
+ push(@cflags, $flag);
+ }
+
+ my $cflags = join(' ', @cflags);
+
+ $self->output("\$(patsubst %.ho,%.d,\$($key->{NAME}_OBJ_FILES:.o=.d)) \$($key->{NAME}_OBJ_FILES): CFLAGS+= $cflags\n");
+}
+
+1;
diff --git a/source4/build/smb_build/output.pm b/source4/build/smb_build/output.pm
new file mode 100644
index 0000000000..aada681ef4
--- /dev/null
+++ b/source4/build/smb_build/output.pm
@@ -0,0 +1,172 @@
+# SMB Build System
+# - the output generating functions
+#
+# Copyright (C) Stefan (metze) Metzmacher 2004
+# Copyright (C) Jelmer Vernooij 2004
+# Released under the GNU GPL
+
+package output;
+use strict;
+
+sub generate_shared_library($)
+{
+ my $lib = shift;
+ my $link_name;
+ my $lib_name;
+
+ $lib->{DEPEND_LIST} = [];
+
+ $link_name = lc($lib->{NAME});
+ $lib_name = $link_name;
+
+ if ($lib->{TYPE} eq "LIBRARY") {
+ $link_name = $lib->{NAME};
+ $link_name =~ s/^LIB//;
+ $link_name = lc($link_name);
+ $lib_name = "lib$link_name";
+ }
+
+ if ($lib->{TYPE} eq "PYTHON") {
+ $lib->{SHAREDDIR} = "bin/python";
+ } elsif (defined($lib->{LIBRARY_REALNAME})) {
+ $lib->{BASEDIR} =~ s/^\.\///g;
+ $lib->{SHAREDDIR} = $lib->{BASEDIR};
+ } else {
+ if ($lib->{TYPE} eq "MODULE") {
+ my $sane_subsystem = lc($lib->{SUBSYSTEM});
+ $sane_subsystem =~ s/^lib//;
+ $lib->{SHAREDDIR} = "bin/modules/$sane_subsystem";
+ $lib->{LIBRARY_REALNAME} = $link_name;
+ $lib->{LIBRARY_REALNAME} =~ s/^$sane_subsystem\_//g;
+ $lib->{LIBRARY_REALNAME}.= ".\$(SHLIBEXT)";
+ } else {
+ $lib->{SHAREDDIR} = "\$(shliboutputdir)";
+ $lib->{LIBRARY_REALNAME} = "$lib_name.\$(SHLIBEXT)";
+ }
+ }
+
+ $lib->{LIBRARY_DEBUGNAME} = $lib->{LIBRARY_REALNAME};
+
+ $lib->{LIBRARY_SONAME} = "\$(if \$($lib->{NAME}_SOVERSION),$lib->{LIBRARY_REALNAME}.\$($lib->{NAME}_SOVERSION),$lib->{LIBRARY_REALNAME})";
+ $lib->{LIBRARY_REALNAME} = "\$(if \$($lib->{NAME}_VERSION),$lib->{LIBRARY_REALNAME}.\$($lib->{NAME}_VERSION),$lib->{LIBRARY_REALNAME})";
+
+ $lib->{RESULT_SHARED_LIBRARY} = "$lib->{SHAREDDIR}/$lib->{LIBRARY_REALNAME}";
+ $lib->{OUTPUT_SHARED_LIBRARY} = "-l$link_name";
+ $lib->{TARGET_SHARED_LIBRARY} = "$lib->{SHAREDDIR}/$lib->{LIBRARY_DEBUGNAME}";
+}
+
+sub generate_merged_obj($)
+{
+ my $lib = shift;
+
+ my $link_name = $lib->{NAME};
+ $link_name =~ s/^LIB//;
+
+ $lib->{MERGED_OBJNAME} = lc($link_name).".o";
+ $lib->{RESULT_MERGED_OBJ} = $lib->{OUTPUT_MERGED_OBJ} = "bin/mergedobj/$lib->{MERGED_OBJNAME}";
+ $lib->{TARGET_MERGED_OBJ} = $lib->{RESULT_MERGED_OBJ};
+}
+
+sub generate_static_library($)
+{
+ my $lib = shift;
+ my $link_name;
+
+ $lib->{DEPEND_LIST} = [];
+
+ $link_name = $lib->{NAME};
+ $link_name =~ s/^LIB//;
+
+ $lib->{LIBRARY_NAME} = "lib".lc($link_name).".a";
+
+ $lib->{RESULT_STATIC_LIBRARY} = "bin/static/$lib->{LIBRARY_NAME}";
+ $lib->{TARGET_STATIC_LIBRARY} = $lib->{RESULT_STATIC_LIBRARY};
+ $lib->{STATICDIR} = 'bin/static';
+ $lib->{OUTPUT_STATIC_LIBRARY} = "-l".lc($link_name);
+}
+
+sub generate_binary($)
+{
+ my $bin = shift;
+
+ $bin->{DEPEND_LIST} = [];
+ push(@{$bin->{LINK_FLAGS}}, "\$($bin->{NAME}\_FULL_OBJ_LIST)");
+
+ $bin->{DEBUGDIR} = "bin";
+ $bin->{RESULT_BINARY} = $bin->{OUTPUT_BINARY} = "$bin->{DEBUGDIR}/$bin->{NAME}";
+ $bin->{TARGET_BINARY} = $bin->{RESULT_BINARY};
+ $bin->{BINARY} = $bin->{NAME};
+}
+
+sub merge_array($$)
+{
+ # $dest is a reference to an array
+ # $src is an array
+ my ($dest, $src) = @_;
+
+ return unless defined($src);
+ return unless ($#{$src} >= 0);
+
+ foreach my $line (@{$src}) {
+ next if (grep /^$line$/, @{$$dest});
+ push(@{$$dest}, $line);
+ }
+}
+
+sub create_output($$)
+{
+ my ($depend, $config) = @_;
+ my $part;
+
+ foreach $part (values %{$depend}) {
+ next unless(defined($part->{OUTPUT_TYPE}));
+
+ generate_binary($part) if grep(/BINARY/, @{$part->{OUTPUT_TYPE}});
+ generate_shared_library($part) if grep(/SHARED_LIBRARY/, @{$part->{OUTPUT_TYPE}});
+ generate_static_library($part) if grep(/STATIC_LIBRARY/, @{$part->{OUTPUT_TYPE}});
+ generate_merged_obj($part) if grep(/MERGED_OBJ/, @{$part->{OUTPUT_TYPE}});
+ $part->{OUTPUT} = $part->{"OUTPUT_" . @{$part->{OUTPUT_TYPE}}[0]};
+ $part->{TARGET} = $part->{"TARGET_" . @{$part->{OUTPUT_TYPE}}[0]};
+ }
+
+ foreach $part (values %{$depend}) {
+ next if not defined($part->{OUTPUT_TYPE});
+
+ merge_array(\$part->{FINAL_CFLAGS}, $part->{CPPFLAGS});
+ merge_array(\$part->{FINAL_CFLAGS}, $part->{CFLAGS});
+
+ foreach (@{$part->{UNIQUE_DEPENDENCIES_ALL}}) {
+ my $elem = $depend->{$_};
+ next if $elem == $part;
+
+ merge_array(\$part->{FINAL_CFLAGS}, $elem->{CPPFLAGS});
+ merge_array(\$part->{FINAL_CFLAGS}, $elem->{CFLAGS});
+ }
+
+ # Always import the link options of the unique dependencies
+ foreach (@{$part->{UNIQUE_DEPENDENCIES_LINK}}) {
+ my $elem = $depend->{$_};
+ next if $elem == $part;
+
+ push(@{$part->{LINK_FLAGS}}, @{$elem->{LIBS}}) if defined($elem->{LIBS});
+ push(@{$part->{LINK_FLAGS}}, @{$elem->{LDFLAGS}}) if defined($elem->{LDFLAGS});
+ if (defined($elem->{OUTPUT_TYPE}) and @{$elem->{OUTPUT_TYPE}}[0] eq "MERGED_OBJ") {
+ push (@{$part->{FULL_OBJ_LIST}}, $elem->{TARGET});
+ } else {
+ push(@{$part->{LINK_FLAGS}}, "\$($elem->{NAME}_OUTPUT)") if defined($elem->{OUTPUT});
+ push(@{$part->{DEPEND_LIST}}, $elem->{TARGET}) if (defined($elem->{TARGET}));
+ }
+ }
+ }
+
+ foreach $part (values %{$depend}) {
+ if (defined($part->{STANDARD_VISIBILITY}) and ($part->{STANDARD_VISIBILITY} ne "default") and
+ ($config->{visibility_attribute} eq "yes")) {
+ push(@{$part->{FINAL_CFLAGS}}, "-fvisibility=$part->{STANDARD_VISIBILITY}");
+ }
+ }
+
+ return $depend;
+}
+
+1;
diff --git a/source4/build/smb_build/summary.pm b/source4/build/smb_build/summary.pm
new file mode 100644
index 0000000000..c7916216ab
--- /dev/null
+++ b/source4/build/smb_build/summary.pm
@@ -0,0 +1,80 @@
+# Samba Build System
+# - write out summary
+#
+# Copyright (C) Jelmer Vernooij 2006
+# Released under the GNU GPL
+
+package summary;
+use smb_build::config;
+use strict;
+
+sub enabled($)
+{
+ my ($val) = @_;
+
+ return (defined($val) && $val =~ m/yes|true/i);
+}
+
+sub showitem($$$)
+{
+ my ($output,$desc,$items) = @_;
+
+ my @need = ();
+
+ foreach (@$items) {
+ push (@need, $_) if (enabled($config::enable{$_}));
+ }
+
+ print "Support for $desc: ";
+ if ($#need >= 0) {
+ print "no (install " . join(',',@need) . ")\n";
+ } else {
+ print "yes\n";
+ }
+}
+
+sub showisexternal($$$)
+{
+ my ($output, $desc, $name) = @_;
+ print "Using external $desc: ".
+ (($output->{$name}->{TYPE} eq "EXT_LIB")?"yes":"no")."\n";
+}
+
+sub show($$)
+{
+ my ($output,$config) = @_;
+
+ print "Summary:\n\n";
+ showitem($output, "SSL in SWAT and LDAP", ["GNUTLS"]);
+ showitem($output, "threads in server (see --with-pthread)", ["PTHREAD"]);
+ showitem($output, "intelligent command line editing", ["READLINE"]);
+ showitem($output, "changing process titles (see --with-setproctitle)", ["SETPROCTITLE"]);
+ showitem($output, "using extended attributes", ["XATTR"]);
+ showitem($output, "using libblkid", ["BLKID"]);
+ showitem($output, "using iconv", ["ICONV"]);
+ showitem($output, "using pam", ["PAM"]);
+ showitem($output, "python bindings", ["LIBPYTHON"]);
+ showisexternal($output, "popt", "LIBPOPT");
+ showisexternal($output, "talloc", "LIBTALLOC");
+ showisexternal($output, "tdb", "LIBTDB");
+ showisexternal($output, "tevent", "LIBTEVENT");
+ showisexternal($output, "ldb", "LIBLDB");
+ print "Developer mode: ".(enabled($config->{developer})?"yes":"no")."\n";
+ print "Automatic dependencies: ".
+ (enabled($config->{automatic_dependencies})
+ ? "yes" : "no (install GNU make >= 3.81 and see --enable-automatic-dependencies)") .
+ "\n";
+
+ print "Building shared libraries: " .
+ (enabled($config->{BLDSHARED})
+ ? "yes" : "no (not supported on this system)") .
+ "\n";
+ print "Using shared libraries internally: " .
+ (enabled($config->{USESHARED})
+ ? "yes" : "no (specify --enable-dso)") .
+ "\n";
+
+ print "\n";
+}
+
+1;
diff --git a/source4/cldap_server/cldap_server.c b/source4/cldap_server/cldap_server.c
new file mode 100644
index 0000000000..240f2b1dc2
--- /dev/null
+++ b/source4/cldap_server/cldap_server.c
@@ -0,0 +1,214 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ CLDAP server task
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/ldap/ldap.h"
+#include "lib/socket/socket.h"
+#include "lib/messaging/irpc.h"
+#include "smbd/service_task.h"
+#include "smbd/service.h"
+#include "cldap_server/cldap_server.h"
+#include "system/network.h"
+#include "lib/socket/netif.h"
+#include "lib/ldb/include/ldb.h"
+#include "lib/ldb/include/ldb_errors.h"
+#include "dsdb/samdb/samdb.h"
+#include "ldb_wrap.h"
+#include "auth/auth.h"
+#include "param/param.h"
+
+/*
+ handle incoming cldap requests
+*/
+static void cldapd_request_handler(struct cldap_socket *cldap,
+ struct ldap_message *ldap_msg,
+ struct socket_address *src)
+{
+ struct ldap_SearchRequest *search;
+ if (ldap_msg->type != LDAP_TAG_SearchRequest) {
+ DEBUG(0,("Invalid CLDAP request type %d from %s:%d\n",
+ ldap_msg->type, src->addr, src->port));
+ cldap_error_reply(cldap, ldap_msg->messageid, src,
+ LDAP_OPERATIONS_ERROR, "Invalid CLDAP request");
+ return;
+ }
+
+ search = &ldap_msg->r.SearchRequest;
+
+ if (strcmp("", search->basedn) != 0) {
+ DEBUG(0,("Invalid CLDAP basedn '%s' from %s:%d\n",
+ search->basedn, src->addr, src->port));
+ cldap_error_reply(cldap, ldap_msg->messageid, src,
+ LDAP_OPERATIONS_ERROR, "Invalid CLDAP basedn");
+ return;
+ }
+
+ if (search->scope != LDAP_SEARCH_SCOPE_BASE) {
+ DEBUG(0,("Invalid CLDAP scope %d from %s:%d\n",
+ search->scope, src->addr, src->port));
+ cldap_error_reply(cldap, ldap_msg->messageid, src,
+ LDAP_OPERATIONS_ERROR, "Invalid CLDAP scope");
+ return;
+ }
+
+ if (search->num_attributes == 1 &&
+ strcasecmp(search->attributes[0], "netlogon") == 0) {
+ cldapd_netlogon_request(cldap, ldap_msg->messageid,
+ search->tree, src);
+ return;
+ }
+
+ cldapd_rootdse_request(cldap, ldap_msg->messageid,
+ search, src);
+}
+
+
+/*
+ start listening on the given address
+*/
+static NTSTATUS cldapd_add_socket(struct cldapd_server *cldapd, struct loadparm_context *lp_ctx,
+ const char *address)
+{
+ struct cldap_socket *cldapsock;
+ struct socket_address *socket_address;
+ NTSTATUS status;
+
+ /* listen for unicasts on the CLDAP port (389) */
+ cldapsock = cldap_socket_init(cldapd, cldapd->task->event_ctx, lp_iconv_convenience(cldapd->task->lp_ctx));
+ NT_STATUS_HAVE_NO_MEMORY(cldapsock);
+
+ socket_address = socket_address_from_strings(cldapsock, cldapsock->sock->backend_name,
+ address, lp_cldap_port(lp_ctx));
+ if (!socket_address) {
+ talloc_free(cldapsock);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = socket_listen(cldapsock->sock, socket_address, 0, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Failed to bind to %s:%d - %s\n",
+ address, lp_cldap_port(lp_ctx), nt_errstr(status)));
+ talloc_free(cldapsock);
+ return status;
+ }
+
+ talloc_free(socket_address);
+
+ cldap_set_incoming_handler(cldapsock, cldapd_request_handler, cldapd);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ setup our listening sockets on the configured network interfaces
+*/
+static NTSTATUS cldapd_startup_interfaces(struct cldapd_server *cldapd, struct loadparm_context *lp_ctx,
+ struct interface *ifaces)
+{
+ int num_interfaces;
+ TALLOC_CTX *tmp_ctx = talloc_new(cldapd);
+ NTSTATUS status;
+ int i;
+
+ num_interfaces = iface_count(ifaces);
+
+ /* if we are allowing incoming packets from any address, then
+ we need to bind to the wildcard address */
+ if (!lp_bind_interfaces_only(lp_ctx)) {
+ status = cldapd_add_socket(cldapd, lp_ctx, "0.0.0.0");
+ NT_STATUS_NOT_OK_RETURN(status);
+ }
+
+ /* now we have to also listen on the specific interfaces,
+ so that replies always come from the right IP */
+ for (i=0; i<num_interfaces; i++) {
+ const char *address = talloc_strdup(tmp_ctx, iface_n_ip(ifaces, i));
+ status = cldapd_add_socket(cldapd, lp_ctx, address);
+ NT_STATUS_NOT_OK_RETURN(status);
+ }
+
+ talloc_free(tmp_ctx);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ startup the cldapd task
+*/
+static void cldapd_task_init(struct task_server *task)
+{
+ struct cldapd_server *cldapd;
+ NTSTATUS status;
+ struct interface *ifaces;
+
+ load_interfaces(task, lp_interfaces(task->lp_ctx), &ifaces);
+
+ if (iface_count(ifaces) == 0) {
+ task_server_terminate(task, "cldapd: no network interfaces configured");
+ return;
+ }
+
+ switch (lp_server_role(task->lp_ctx)) {
+ case ROLE_STANDALONE:
+ task_server_terminate(task, "cldap_server: no CLDAP server required in standalone configuration");
+ return;
+ case ROLE_DOMAIN_MEMBER:
+ task_server_terminate(task, "cldap_server: no CLDAP server required in member server configuration");
+ return;
+ case ROLE_DOMAIN_CONTROLLER:
+ /* Yes, we want an CLDAP server */
+ break;
+ }
+
+ task_server_set_title(task, "task[cldapd]");
+
+ cldapd = talloc(task, struct cldapd_server);
+ if (cldapd == NULL) {
+ task_server_terminate(task, "cldapd: out of memory");
+ return;
+ }
+
+ cldapd->task = task;
+ cldapd->samctx = samdb_connect(cldapd, task->event_ctx, task->lp_ctx, system_session(cldapd, task->lp_ctx));
+ if (cldapd->samctx == NULL) {
+ task_server_terminate(task, "cldapd failed to open samdb");
+ return;
+ }
+
+ /* start listening on the configured network interfaces */
+ status = cldapd_startup_interfaces(cldapd, task->lp_ctx, ifaces);
+ if (!NT_STATUS_IS_OK(status)) {
+ task_server_terminate(task, "cldapd failed to setup interfaces");
+ return;
+ }
+
+ irpc_add_name(task->msg_ctx, "cldap_server");
+}
+
+
+/*
+ register ourselves as a available server
+*/
+NTSTATUS server_service_cldapd_init(void)
+{
+ return register_server_service("cldap", cldapd_task_init);
+}
diff --git a/source4/cldap_server/cldap_server.h b/source4/cldap_server/cldap_server.h
new file mode 100644
index 0000000000..da2bd20050
--- /dev/null
+++ b/source4/cldap_server/cldap_server.h
@@ -0,0 +1,34 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ CLDAP server structures
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "libcli/cldap/cldap.h"
+
+/*
+ top level context structure for the cldap server
+*/
+struct cldapd_server {
+ struct task_server *task;
+ struct ldb_context *samctx;
+};
+
+struct ldap_SearchRequest;
+
+#include "cldap_server/proto.h"
diff --git a/source4/cldap_server/config.mk b/source4/cldap_server/config.mk
new file mode 100644
index 0000000000..045fca3fd0
--- /dev/null
+++ b/source4/cldap_server/config.mk
@@ -0,0 +1,18 @@
+# CLDAP server subsystem
+
+#######################
+# Start SUBSYSTEM CLDAPD
+[MODULE::CLDAPD]
+INIT_FUNCTION = server_service_cldapd_init
+SUBSYSTEM = service
+PRIVATE_DEPENDENCIES = \
+ LIBCLI_CLDAP LIBNETIF process_model
+# End SUBSYSTEM CLDAPD
+#######################
+
+CLDAPD_OBJ_FILES = $(addprefix $(cldap_serversrcdir)/, \
+ cldap_server.o \
+ netlogon.o \
+ rootdse.o)
+
+$(eval $(call proto_header_template,$(cldap_serversrcdir)/proto.h,$(CLDAPD_OBJ_FILES:.o=.c)))
diff --git a/source4/cldap_server/netlogon.c b/source4/cldap_server/netlogon.c
new file mode 100644
index 0000000000..0df35be6fd
--- /dev/null
+++ b/source4/cldap_server/netlogon.c
@@ -0,0 +1,501 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ CLDAP server - netlogon handling
+
+ Copyright (C) Andrew Tridgell 2005
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/ldb/include/ldb.h"
+#include "lib/ldb/include/ldb_errors.h"
+#include "lib/events/events.h"
+#include "lib/socket/socket.h"
+#include "smbd/service_task.h"
+#include "cldap_server/cldap_server.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "libcli/ldap/ldap_ndr.h"
+#include "libcli/security/security.h"
+#include "dsdb/samdb/samdb.h"
+#include "auth/auth.h"
+#include "ldb_wrap.h"
+#include "system/network.h"
+#include "lib/socket/netif.h"
+#include "param/param.h"
+/*
+ fill in the cldap netlogon union for a given version
+*/
+NTSTATUS fill_netlogon_samlogon_response(struct ldb_context *sam_ctx,
+ TALLOC_CTX *mem_ctx,
+ const char *domain,
+ const char *netbios_domain,
+ struct dom_sid *domain_sid,
+ const char *domain_guid,
+ const char *user,
+ uint32_t acct_control,
+ const char *src_address,
+ uint32_t version,
+ struct loadparm_context *lp_ctx,
+ struct netlogon_samlogon_response *netlogon)
+{
+ const char *ref_attrs[] = {"nETBIOSName", "dnsRoot", "ncName", NULL};
+ const char *dom_attrs[] = {"objectGUID", NULL};
+ const char *none_attrs[] = {NULL};
+ struct ldb_result *ref_res = NULL, *dom_res = NULL, *user_res = NULL;
+ int ret;
+ const char **services = lp_server_services(lp_ctx);
+ uint32_t server_type;
+ const char *pdc_name;
+ struct GUID domain_uuid;
+ const char *realm;
+ const char *dns_domain;
+ const char *pdc_dns_name;
+ const char *flatname;
+ const char *server_site;
+ const char *client_site;
+ const char *pdc_ip;
+ struct ldb_dn *partitions_basedn;
+ struct interface *ifaces;
+ bool user_known;
+ NTSTATUS status;
+
+ partitions_basedn = samdb_partitions_dn(sam_ctx, mem_ctx);
+
+ /* the domain has an optional trailing . */
+ if (domain && domain[strlen(domain)-1] == '.') {
+ domain = talloc_strndup(mem_ctx, domain, strlen(domain)-1);
+ }
+
+ if (domain) {
+ struct ldb_dn *dom_dn;
+ /* try and find the domain */
+
+ ret = ldb_search(sam_ctx, mem_ctx, &ref_res,
+ partitions_basedn, LDB_SCOPE_ONELEVEL,
+ ref_attrs,
+ "(&(&(objectClass=crossRef)(dnsRoot=%s))(nETBIOSName=*))",
+ ldb_binary_encode_string(mem_ctx, domain));
+
+ if (ret != LDB_SUCCESS) {
+ DEBUG(2,("Unable to find referece to '%s' in sam: %s\n",
+ domain,
+ ldb_errstring(sam_ctx)));
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ } else if (ref_res->count == 1) {
+ dom_dn = ldb_msg_find_attr_as_dn(sam_ctx, mem_ctx, ref_res->msgs[0], "ncName");
+ if (!dom_dn) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+ ret = ldb_search(sam_ctx, mem_ctx, &dom_res,
+ dom_dn, LDB_SCOPE_BASE, dom_attrs,
+ "objectClass=domain");
+ if (ret != LDB_SUCCESS) {
+ DEBUG(2,("Error finding domain '%s'/'%s' in sam: %s\n", domain, ldb_dn_get_linearized(dom_dn), ldb_errstring(sam_ctx)));
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+ if (dom_res->count != 1) {
+ DEBUG(2,("Error finding domain '%s'/'%s' in sam\n", domain, ldb_dn_get_linearized(dom_dn)));
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+ } else if (ref_res->count > 1) {
+ talloc_free(ref_res);
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+ }
+
+ if (netbios_domain) {
+ struct ldb_dn *dom_dn;
+ /* try and find the domain */
+
+ ret = ldb_search(sam_ctx, mem_ctx, &ref_res,
+ partitions_basedn, LDB_SCOPE_ONELEVEL,
+ ref_attrs,
+ "(&(objectClass=crossRef)(ncName=*)(nETBIOSName=%s))",
+ ldb_binary_encode_string(mem_ctx, netbios_domain));
+
+ if (ret != LDB_SUCCESS) {
+ DEBUG(2,("Unable to find referece to '%s' in sam: %s\n",
+ netbios_domain,
+ ldb_errstring(sam_ctx)));
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ } else if (ref_res->count == 1) {
+ dom_dn = ldb_msg_find_attr_as_dn(sam_ctx, mem_ctx, ref_res->msgs[0], "ncName");
+ if (!dom_dn) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+ ret = ldb_search(sam_ctx, mem_ctx, &dom_res,
+ dom_dn, LDB_SCOPE_BASE, dom_attrs,
+ "objectClass=domain");
+ if (ret != LDB_SUCCESS) {
+ DEBUG(2,("Error finding domain '%s'/'%s' in sam: %s\n", domain, ldb_dn_get_linearized(dom_dn), ldb_errstring(sam_ctx)));
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+ if (dom_res->count != 1) {
+ DEBUG(2,("Error finding domain '%s'/'%s' in sam\n", domain, ldb_dn_get_linearized(dom_dn)));
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+ } else if (ref_res->count > 1) {
+ talloc_free(ref_res);
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+ }
+
+ if ((dom_res == NULL || dom_res->count == 0) && (domain_guid || domain_sid)) {
+ ref_res = NULL;
+
+ if (domain_guid) {
+ struct GUID binary_guid;
+ struct ldb_val guid_val;
+ enum ndr_err_code ndr_err;
+
+ /* By this means, we ensure we don't have funny stuff in the GUID */
+
+ status = GUID_from_string(domain_guid, &binary_guid);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* And this gets the result into the binary format we want anyway */
+ ndr_err = ndr_push_struct_blob(&guid_val, mem_ctx, NULL, &binary_guid,
+ (ndr_push_flags_fn_t)ndr_push_GUID);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ ret = ldb_search(sam_ctx, mem_ctx, &dom_res,
+ NULL, LDB_SCOPE_SUBTREE,
+ dom_attrs,
+ "(&(objectCategory=DomainDNS)(objectGUID=%s))",
+ ldb_binary_encode(mem_ctx, guid_val));
+ } else { /* domain_sid case */
+ struct dom_sid *sid;
+ struct ldb_val sid_val;
+ enum ndr_err_code ndr_err;
+
+ /* Rather than go via the string, just push into the NDR form */
+ ndr_err = ndr_push_struct_blob(&sid_val, mem_ctx, NULL, &sid,
+ (ndr_push_flags_fn_t)ndr_push_dom_sid);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ ret = ldb_search(sam_ctx, mem_ctx, &dom_res,
+ NULL, LDB_SCOPE_SUBTREE,
+ dom_attrs,
+ "(&(objectCategory=DomainDNS)(objectSID=%s))",
+ ldb_binary_encode(mem_ctx, sid_val));
+ }
+
+ if (ret != LDB_SUCCESS) {
+ DEBUG(2,("Unable to find referece to GUID '%s' or SID %s in sam: %s\n",
+ domain_guid, dom_sid_string(mem_ctx, domain_sid),
+ ldb_errstring(sam_ctx)));
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ } else if (dom_res->count == 1) {
+ /* try and find the domain */
+ ret = ldb_search(sam_ctx, mem_ctx, &ref_res,
+ partitions_basedn, LDB_SCOPE_ONELEVEL,
+ ref_attrs,
+ "(&(objectClass=crossRef)(ncName=%s))",
+ ldb_dn_get_linearized(dom_res->msgs[0]->dn));
+
+ if (ret != LDB_SUCCESS) {
+ DEBUG(2,("Unable to find referece to '%s' in sam: %s\n",
+ ldb_dn_get_linearized(dom_res->msgs[0]->dn),
+ ldb_errstring(sam_ctx)));
+ return NT_STATUS_NO_SUCH_DOMAIN;
+
+ } else if (ref_res->count != 1) {
+ DEBUG(2,("Unable to find referece to '%s' in sam\n",
+ ldb_dn_get_linearized(dom_res->msgs[0]->dn)));
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+ } else if (dom_res->count > 1) {
+ talloc_free(ref_res);
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+ }
+
+
+ if ((ref_res == NULL || ref_res->count == 0)) {
+ DEBUG(2,("Unable to find domain reference with name %s or GUID {%s}\n", domain, domain_guid));
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ if ((dom_res == NULL || dom_res->count == 0)) {
+ DEBUG(2,("Unable to find domain with name %s or GUID {%s}\n", domain, domain_guid));
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ /* work around different inputs for not-specified users */
+ if (!user) {
+ user = "";
+ }
+
+ /* Enquire about any valid username with just a CLDAP packet -
+ * if kerberos didn't also do this, the security folks would
+ * scream... */
+ if (user[0]) { \
+ /* Only allow some bits to be enquired: [MS-ATDS] 7.3.3.2 */
+ if (acct_control == (uint32_t)-1) {
+ acct_control = 0;
+ }
+ acct_control = acct_control & (ACB_TEMPDUP | ACB_NORMAL | ACB_DOMTRUST | ACB_WSTRUST | ACB_SVRTRUST);
+
+ /* We must exclude disabled accounts, but otherwise do the bitwise match the client asked for */
+ ret = ldb_search(sam_ctx, mem_ctx, &user_res,
+ dom_res->msgs[0]->dn, LDB_SCOPE_SUBTREE,
+ none_attrs,
+ "(&(objectClass=user)(samAccountName=%s)"
+ "(!(userAccountControl:" LDB_OID_COMPARATOR_AND ":=%u))"
+ "(userAccountControl:" LDB_OID_COMPARATOR_OR ":=%u))",
+ ldb_binary_encode_string(mem_ctx, user),
+ UF_ACCOUNTDISABLE, samdb_acb2uf(acct_control));
+ if (ret != LDB_SUCCESS) {
+ DEBUG(2,("Unable to find referece to user '%s' with ACB 0x%8x under %s: %s\n",
+ user, acct_control, ldb_dn_get_linearized(dom_res->msgs[0]->dn),
+ ldb_errstring(sam_ctx)));
+ return NT_STATUS_NO_SUCH_USER;
+ } else if (user_res->count == 1) {
+ user_known = true;
+ } else {
+ user_known = false;
+ }
+
+ } else {
+ user_known = true;
+ }
+
+ server_type =
+ NBT_SERVER_DS | NBT_SERVER_TIMESERV |
+ NBT_SERVER_CLOSEST | NBT_SERVER_WRITABLE |
+ NBT_SERVER_GOOD_TIMESERV | DS_DNS_CONTROLLER |
+ DS_DNS_DOMAIN;
+
+ if (samdb_is_pdc(sam_ctx)) {
+ server_type |= NBT_SERVER_PDC;
+ }
+
+ if (samdb_is_gc(sam_ctx)) {
+ server_type |= NBT_SERVER_GC;
+ }
+
+ if (str_list_check(services, "ldap")) {
+ server_type |= NBT_SERVER_LDAP;
+ }
+
+ if (str_list_check(services, "kdc")) {
+ server_type |= NBT_SERVER_KDC;
+ }
+
+ if (ldb_dn_compare(ldb_get_root_basedn(sam_ctx), ldb_get_default_basedn(sam_ctx)) == 0) {
+ server_type |= DS_DNS_FOREST;
+ }
+
+ pdc_name = talloc_asprintf(mem_ctx, "\\\\%s", lp_netbios_name(lp_ctx));
+ domain_uuid = samdb_result_guid(dom_res->msgs[0], "objectGUID");
+ realm = samdb_result_string(ref_res->msgs[0], "dnsRoot", lp_realm(lp_ctx));
+ dns_domain = samdb_result_string(ref_res->msgs[0], "dnsRoot", lp_realm(lp_ctx));
+ pdc_dns_name = talloc_asprintf(mem_ctx, "%s.%s",
+ strlower_talloc(mem_ctx,
+ lp_netbios_name(lp_ctx)),
+ dns_domain);
+
+ flatname = samdb_result_string(ref_res->msgs[0], "nETBIOSName",
+ lp_workgroup(lp_ctx));
+ /* FIXME: Hardcoded site names */
+ server_site = "Default-First-Site-Name";
+ client_site = "Default-First-Site-Name";
+ load_interfaces(mem_ctx, lp_interfaces(lp_ctx), &ifaces);
+ pdc_ip = iface_best_ip(ifaces, src_address);
+
+ ZERO_STRUCTP(netlogon);
+
+ /* check if either of these bits is present */
+ if (version & (NETLOGON_NT_VERSION_5EX|NETLOGON_NT_VERSION_5EX_WITH_IP)) {
+ uint32_t extra_flags = 0;
+ netlogon->ntver = NETLOGON_NT_VERSION_5EX;
+
+ /* could check if the user exists */
+ if (user_known) {
+ netlogon->data.nt5_ex.command = LOGON_SAM_LOGON_RESPONSE_EX;
+ } else {
+ netlogon->data.nt5_ex.command = LOGON_SAM_LOGON_USER_UNKNOWN_EX;
+ }
+ netlogon->data.nt5_ex.server_type = server_type;
+ netlogon->data.nt5_ex.domain_uuid = domain_uuid;
+ netlogon->data.nt5_ex.forest = realm;
+ netlogon->data.nt5_ex.dns_domain = dns_domain;
+ netlogon->data.nt5_ex.pdc_dns_name = pdc_dns_name;
+ netlogon->data.nt5_ex.domain = flatname;
+ netlogon->data.nt5_ex.pdc_name = lp_netbios_name(lp_ctx);
+ netlogon->data.nt5_ex.user_name = user;
+ netlogon->data.nt5_ex.server_site = server_site;
+ netlogon->data.nt5_ex.client_site = client_site;
+
+ if (version & NETLOGON_NT_VERSION_5EX_WITH_IP) {
+ /* Clearly this needs to be fixed up for IPv6 */
+ extra_flags = NETLOGON_NT_VERSION_5EX_WITH_IP;
+ netlogon->data.nt5_ex.sockaddr.sockaddr_family = 2;
+ netlogon->data.nt5_ex.sockaddr.pdc_ip = pdc_ip;
+ netlogon->data.nt5_ex.sockaddr.remaining = data_blob_talloc_zero(mem_ctx, 8);
+ }
+ netlogon->data.nt5_ex.nt_version = NETLOGON_NT_VERSION_1|NETLOGON_NT_VERSION_5EX|extra_flags;
+ netlogon->data.nt5_ex.lmnt_token = 0xFFFF;
+ netlogon->data.nt5_ex.lm20_token = 0xFFFF;
+
+ } else if (version & NETLOGON_NT_VERSION_5) {
+ netlogon->ntver = NETLOGON_NT_VERSION_5;
+
+ /* could check if the user exists */
+ if (user_known) {
+ netlogon->data.nt5.command = LOGON_SAM_LOGON_RESPONSE;
+ } else {
+ netlogon->data.nt5.command = LOGON_SAM_LOGON_USER_UNKNOWN;
+ }
+ netlogon->data.nt5.pdc_name = pdc_name;
+ netlogon->data.nt5.user_name = user;
+ netlogon->data.nt5.domain_name = flatname;
+ netlogon->data.nt5.domain_uuid = domain_uuid;
+ netlogon->data.nt5.forest = realm;
+ netlogon->data.nt5.dns_domain = dns_domain;
+ netlogon->data.nt5.pdc_dns_name = pdc_dns_name;
+ netlogon->data.nt5.pdc_ip = pdc_ip;
+ netlogon->data.nt5.server_type = server_type;
+ netlogon->data.nt5.nt_version = NETLOGON_NT_VERSION_1|NETLOGON_NT_VERSION_5;
+ netlogon->data.nt5.lmnt_token = 0xFFFF;
+ netlogon->data.nt5.lm20_token = 0xFFFF;
+
+ } else /* (version & NETLOGON_NT_VERSION_1) and all other cases */ {
+ netlogon->ntver = NETLOGON_NT_VERSION_1;
+ /* could check if the user exists */
+ if (user_known) {
+ netlogon->data.nt4.command = LOGON_SAM_LOGON_RESPONSE;
+ } else {
+ netlogon->data.nt4.command = LOGON_SAM_LOGON_USER_UNKNOWN;
+ }
+ netlogon->data.nt4.server = pdc_name;
+ netlogon->data.nt4.user_name = user;
+ netlogon->data.nt4.domain = flatname;
+ netlogon->data.nt4.nt_version = NETLOGON_NT_VERSION_1;
+ netlogon->data.nt4.lmnt_token = 0xFFFF;
+ netlogon->data.nt4.lm20_token = 0xFFFF;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ handle incoming cldap requests
+*/
+void cldapd_netlogon_request(struct cldap_socket *cldap,
+ uint32_t message_id,
+ struct ldb_parse_tree *tree,
+ struct socket_address *src)
+{
+ struct cldapd_server *cldapd = talloc_get_type(cldap->incoming.private_data, struct cldapd_server);
+ int i;
+ const char *domain = NULL;
+ const char *host = NULL;
+ const char *user = NULL;
+ const char *domain_guid = NULL;
+ const char *domain_sid = NULL;
+ int acct_control = -1;
+ int version = -1;
+ struct netlogon_samlogon_response netlogon;
+ NTSTATUS status = NT_STATUS_INVALID_PARAMETER;
+
+ TALLOC_CTX *tmp_ctx = talloc_new(cldap);
+
+ if (tree->operation != LDB_OP_AND) goto failed;
+
+ /* extract the query elements */
+ for (i=0;i<tree->u.list.num_elements;i++) {
+ struct ldb_parse_tree *t = tree->u.list.elements[i];
+ if (t->operation != LDB_OP_EQUALITY) goto failed;
+ if (strcasecmp(t->u.equality.attr, "DnsDomain") == 0) {
+ domain = talloc_strndup(tmp_ctx,
+ (const char *)t->u.equality.value.data,
+ t->u.equality.value.length);
+ }
+ if (strcasecmp(t->u.equality.attr, "Host") == 0) {
+ host = talloc_strndup(tmp_ctx,
+ (const char *)t->u.equality.value.data,
+ t->u.equality.value.length);
+ }
+ if (strcasecmp(t->u.equality.attr, "DomainGuid") == 0) {
+ NTSTATUS enc_status;
+ struct GUID guid;
+ enc_status = ldap_decode_ndr_GUID(tmp_ctx,
+ t->u.equality.value, &guid);
+ if (NT_STATUS_IS_OK(enc_status)) {
+ domain_guid = GUID_string(tmp_ctx, &guid);
+ }
+ }
+ if (strcasecmp(t->u.equality.attr, "DomainSid") == 0) {
+ domain_sid = talloc_strndup(tmp_ctx,
+ (const char *)t->u.equality.value.data,
+ t->u.equality.value.length);
+ }
+ if (strcasecmp(t->u.equality.attr, "User") == 0) {
+ user = talloc_strndup(tmp_ctx,
+ (const char *)t->u.equality.value.data,
+ t->u.equality.value.length);
+ }
+ if (strcasecmp(t->u.equality.attr, "NtVer") == 0 &&
+ t->u.equality.value.length == 4) {
+ version = IVAL(t->u.equality.value.data, 0);
+ }
+ if (strcasecmp(t->u.equality.attr, "AAC") == 0 &&
+ t->u.equality.value.length == 4) {
+ acct_control = IVAL(t->u.equality.value.data, 0);
+ }
+ }
+
+ if (domain_guid == NULL && domain == NULL) {
+ domain = lp_realm(cldapd->task->lp_ctx);
+ }
+
+ if (version == -1) {
+ goto failed;
+ }
+
+ DEBUG(5,("cldap netlogon query domain=%s host=%s user=%s version=%d guid=%s\n",
+ domain, host, user, version, domain_guid));
+
+ status = fill_netlogon_samlogon_response(cldapd->samctx, tmp_ctx, domain, NULL, NULL, domain_guid,
+ user, acct_control, src->addr,
+ version, cldapd->task->lp_ctx, &netlogon);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+
+ status = cldap_netlogon_reply(cldap, message_id, src, version,
+ &netlogon);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+
+ talloc_free(tmp_ctx);
+ return;
+
+failed:
+ DEBUG(2,("cldap netlogon query failed domain=%s host=%s version=%d - %s\n",
+ domain, host, version, nt_errstr(status)));
+ talloc_free(tmp_ctx);
+ cldap_empty_reply(cldap, message_id, src);
+}
diff --git a/source4/cldap_server/rootdse.c b/source4/cldap_server/rootdse.c
new file mode 100644
index 0000000000..daa5060d07
--- /dev/null
+++ b/source4/cldap_server/rootdse.c
@@ -0,0 +1,181 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ CLDAP server - rootdse handling
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/ldap/ldap.h"
+#include "lib/ldb/include/ldb.h"
+#include "lib/ldb/include/ldb_errors.h"
+#include "lib/events/events.h"
+#include "lib/socket/socket.h"
+#include "smbd/service_task.h"
+#include "cldap_server/cldap_server.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "dsdb/samdb/samdb.h"
+#include "auth/auth.h"
+#include "ldb_wrap.h"
+#include "system/network.h"
+#include "lib/socket/netif.h"
+
+static void cldapd_rootdse_fill(struct cldapd_server *cldapd,
+ TALLOC_CTX *mem_ctx,
+ struct ldap_SearchRequest *search,
+ struct ldap_SearchResEntry **response,
+ struct ldap_Result *result)
+{
+ struct ldap_SearchResEntry *ent = NULL;
+ struct ldb_dn *basedn;
+ struct ldb_result *res = NULL;
+ struct ldb_request *lreq;
+ enum ldb_scope scope = LDB_SCOPE_DEFAULT;
+ const char **attrs = NULL;
+ const char *errstr = NULL;
+ int ret = 0;
+ int ldb_ret = -1;
+
+ basedn = ldb_dn_new(mem_ctx, cldapd->samctx, NULL);
+ if (basedn == NULL) goto nomem;
+ scope = LDB_SCOPE_BASE;
+
+ if (search->num_attributes >= 1) {
+ int i;
+
+ attrs = talloc_array(mem_ctx, const char *, search->num_attributes+1);
+ if (attrs == NULL) goto nomem;
+
+ for (i=0; i < search->num_attributes; i++) {
+ attrs[i] = search->attributes[i];
+ }
+ attrs[i] = NULL;
+ }
+
+ res = talloc_zero(mem_ctx, struct ldb_result);
+ if (res == NULL) goto nomem;
+
+ ldb_ret = ldb_build_search_req_ex(&lreq, cldapd->samctx, mem_ctx,
+ basedn, scope,
+ search->tree, attrs,
+ NULL,
+ res, ldb_search_default_callback,
+ NULL);
+
+ if (ldb_ret != LDB_SUCCESS) {
+ goto reply;
+ }
+
+ /* Copy the timeout from the incoming call */
+ ldb_set_timeout(cldapd->samctx, lreq, search->timelimit);
+
+ ldb_ret = ldb_request(cldapd->samctx, lreq);
+ if (ldb_ret != LDB_SUCCESS) {
+ goto reply;
+ }
+
+ ldb_ret = ldb_wait(lreq->handle, LDB_WAIT_ALL);
+ if (ldb_ret != LDB_SUCCESS) {
+ goto reply;
+ }
+
+ if (res->count > 1) {
+ errstr = "Internal error: to much replies";
+ ldb_ret = LDB_ERR_OTHER;
+ goto reply;
+ } else if (res->count == 1) {
+ int j;
+
+ ent = talloc(mem_ctx, struct ldap_SearchResEntry);
+ if (ent == NULL) goto nomem;
+
+ ent->dn = ldb_dn_alloc_linearized(ent, res->msgs[0]->dn);
+ if (ent->dn == NULL) goto nomem;
+ ent->num_attributes = 0;
+ ent->attributes = NULL;
+ if (res->msgs[0]->num_elements == 0) {
+ goto reply;
+ }
+ ent->num_attributes = res->msgs[0]->num_elements;
+ ent->attributes = talloc_array(ent, struct ldb_message_element, ent->num_attributes);
+ if (ent->attributes == NULL) goto nomem;
+ for (j=0; j < ent->num_attributes; j++) {
+ ent->attributes[j].name = talloc_steal(ent->attributes, res->msgs[0]->elements[j].name);
+ ent->attributes[j].num_values = 0;
+ ent->attributes[j].values = NULL;
+ if (search->attributesonly && (res->msgs[0]->elements[j].num_values == 0)) {
+ continue;
+ }
+ ent->attributes[j].num_values = res->msgs[0]->elements[j].num_values;
+ ent->attributes[j].values = res->msgs[0]->elements[j].values;
+ talloc_steal(ent->attributes, res->msgs[0]->elements[j].values);
+ }
+ }
+
+reply:
+ if (ret) {
+ /* nothing ... */
+ } else if (ldb_ret == LDB_SUCCESS) {
+ ret = LDAP_SUCCESS;
+ errstr = NULL;
+ } else {
+ ret = ldb_ret;
+ errstr = ldb_errstring(cldapd->samctx);
+ }
+ goto done;
+nomem:
+ talloc_free(ent);
+ ret = LDAP_OPERATIONS_ERROR;
+ errstr = "No memory";
+done:
+ *response = ent;
+ result->resultcode = ret;
+ result->errormessage = (errstr?talloc_strdup(mem_ctx, errstr):NULL);
+}
+
+/*
+ handle incoming cldap requests
+*/
+void cldapd_rootdse_request(struct cldap_socket *cldap,
+ uint32_t message_id,
+ struct ldap_SearchRequest *search,
+ struct socket_address *src)
+{
+ struct cldapd_server *cldapd = talloc_get_type(cldap->incoming.private_data, struct cldapd_server);
+ NTSTATUS status;
+ struct cldap_reply reply;
+ struct ldap_Result result;
+ TALLOC_CTX *tmp_ctx = talloc_new(cldap);
+
+ ZERO_STRUCT(result);
+
+ reply.messageid = message_id;
+ reply.dest = src;
+ reply.response = NULL;
+ reply.result = &result;
+
+ cldapd_rootdse_fill(cldapd, tmp_ctx, search, &reply.response, reply.result);
+
+ status = cldap_reply_send(cldap, &reply);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(2,("cldap rootdse query failed '%s' - %s\n",
+ ldb_filter_from_tree(tmp_ctx, search->tree), nt_errstr(status)));
+ }
+
+ talloc_free(tmp_ctx);
+ return;
+}
diff --git a/source4/client/cifsdd.c b/source4/client/cifsdd.c
new file mode 100644
index 0000000000..302d470afd
--- /dev/null
+++ b/source4/client/cifsdd.c
@@ -0,0 +1,629 @@
+/*
+ CIFSDD - dd for SMB.
+ Main program, argument handling and block copying.
+
+ Copyright (C) James Peach 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "auth/gensec/gensec.h"
+#include "lib/cmdline/popt_common.h"
+#include "libcli/resolve/resolve.h"
+#include "libcli/raw/libcliraw.h"
+#include "lib/events/events.h"
+
+#include "cifsdd.h"
+#include "param/param.h"
+
+const char * const PROGNAME = "cifsdd";
+
+#define SYNTAX_EXIT_CODE 1 /* Invokation syntax or logic error. */
+#define EOM_EXIT_CODE 9 /* Out of memory error. */
+#define FILESYS_EXIT_CODE 10 /* File manipulation error. */
+#define IOERROR_EXIT_CODE 11 /* Error during IO phase. */
+
+struct dd_stats_record dd_stats;
+
+static int dd_sigint;
+static int dd_sigusr1;
+
+static void dd_handle_signal(int sig)
+{
+ switch (sig)
+ {
+ case SIGINT:
+ ++dd_sigint;
+ break;
+ case SIGUSR1:
+ ++dd_sigusr1;
+ break;
+ default:
+ break;
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+/* Argument handling. */
+/* ------------------------------------------------------------------------- */
+
+static const char * argtype_str(enum argtype arg_type)
+{
+ static const struct {
+ enum argtype arg_type;
+ const char * arg_name;
+ } names [] =
+ {
+ { ARG_NUMERIC, "COUNT" },
+ { ARG_SIZE, "SIZE" },
+ { ARG_PATHNAME, "FILE" },
+ { ARG_BOOL, "BOOLEAN" },
+ };
+
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(names); ++i) {
+ if (arg_type == names[i].arg_type) {
+ return(names[i].arg_name);
+ }
+ }
+
+ return("<unknown>");
+}
+
+static struct argdef args[] =
+{
+ { "bs", ARG_SIZE, "force ibs and obs to SIZE bytes" },
+ { "ibs", ARG_SIZE, "read SIZE bytes at a time" },
+ { "obs", ARG_SIZE, "write SIZE bytes at a time" },
+
+ { "count", ARG_NUMERIC, "copy COUNT input blocks" },
+ { "seek",ARG_NUMERIC, "skip COUNT blocks at start of output" },
+ { "skip",ARG_NUMERIC, "skip COUNT blocks at start of input" },
+
+ { "if", ARG_PATHNAME, "read input from FILE" },
+ { "of", ARG_PATHNAME, "write output to FILE" },
+
+ { "direct", ARG_BOOL, "use direct I/O if non-zero" },
+ { "sync", ARG_BOOL, "use synchronous writes if non-zero" },
+ { "oplock", ARG_BOOL, "take oplocks on the input and output files" },
+
+/* FIXME: We should support using iflags and oflags for setting oplock and I/O
+ * options. This would make us compatible with GNU dd.
+ */
+};
+
+static struct argdef * find_named_arg(const char * arg)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(args); ++i) {
+ if (strwicmp(arg, args[i].arg_name) == 0) {
+ return(&args[i]);
+ }
+ }
+
+ return(NULL);
+}
+
+int set_arg_argv(const char * argv)
+{
+ struct argdef * arg;
+
+ char * name;
+ char * val;
+
+ if ((name = strdup(argv)) == NULL) {
+ return(0);
+ }
+
+ if ((val = strchr(name, '=')) == NULL) {
+ fprintf(stderr, "%s: malformed argument \"%s\"\n",
+ PROGNAME, argv);
+ goto fail;
+ }
+
+ *val = '\0';
+ val++;
+
+ if ((arg = find_named_arg(name)) == NULL) {
+ fprintf(stderr, "%s: ignoring unknown argument \"%s\"\n",
+ PROGNAME, name);
+ goto fail;
+ }
+
+ /* Found a matching name; convert the variable argument. */
+ switch (arg->arg_type) {
+ case ARG_NUMERIC:
+ if (!conv_str_u64(val, &arg->arg_val.nval)) {
+ goto fail;
+ }
+ break;
+ case ARG_SIZE:
+ if (!conv_str_size(val, &arg->arg_val.nval)) {
+ goto fail;
+ }
+ break;
+ case ARG_BOOL:
+ if (!conv_str_bool(val, &arg->arg_val.bval)) {
+ goto fail;
+ }
+ break;
+ case ARG_PATHNAME:
+ if (!(arg->arg_val.pval = strdup(val))) {
+ goto fail;
+ }
+ break;
+ default:
+ fprintf(stderr, "%s: argument \"%s\" is of "
+ "unknown type\n", PROGNAME, name);
+ goto fail;
+ }
+
+ free(name);
+ return(1);
+
+fail:
+ free(name);
+ return(0);
+}
+
+void set_arg_val(const char * name, ...)
+{
+ va_list ap;
+ struct argdef * arg;
+
+ va_start(ap, name);
+ if ((arg = find_named_arg(name)) == NULL) {
+ goto fail;
+ }
+
+ /* Found a matching name; convert the variable argument. */
+ switch (arg->arg_type) {
+ case ARG_NUMERIC:
+ case ARG_SIZE:
+ arg->arg_val.nval = va_arg(ap, uint64_t);
+ break;
+ case ARG_BOOL:
+ arg->arg_val.bval = va_arg(ap, int);
+ break;
+ case ARG_PATHNAME:
+ arg->arg_val.pval = va_arg(ap, char *);
+ if (arg->arg_val.pval) {
+ arg->arg_val.pval = strdup(arg->arg_val.pval);
+ }
+ break;
+ default:
+ fprintf(stderr, "%s: argument \"%s\" is of "
+ "unknown type\n", PROGNAME, name);
+ goto fail;
+ }
+
+ va_end(ap);
+ return;
+
+fail:
+ fprintf(stderr, "%s: ignoring unknown argument \"%s\"\n",
+ PROGNAME, name);
+ va_end(ap);
+ return;
+}
+
+bool check_arg_bool(const char * name)
+{
+ struct argdef * arg;
+
+ if ((arg = find_named_arg(name)) &&
+ (arg->arg_type == ARG_BOOL)) {
+ return(arg->arg_val.bval);
+ }
+
+ DEBUG(0, ("invalid argument name: %s", name));
+ SMB_ASSERT(0);
+ return(false);
+}
+
+uint64_t check_arg_numeric(const char * name)
+{
+ struct argdef * arg;
+
+ if ((arg = find_named_arg(name)) &&
+ (arg->arg_type == ARG_NUMERIC || arg->arg_type == ARG_SIZE)) {
+ return(arg->arg_val.nval);
+ }
+
+ DEBUG(0, ("invalid argument name: %s", name));
+ SMB_ASSERT(0);
+ return(-1);
+}
+
+const char * check_arg_pathname(const char * name)
+{
+ struct argdef * arg;
+
+ if ((arg = find_named_arg(name)) &&
+ (arg->arg_type == ARG_PATHNAME)) {
+ return(arg->arg_val.pval);
+ }
+
+ DEBUG(0, ("invalid argument name: %s", name));
+ SMB_ASSERT(0);
+ return(NULL);
+}
+
+static void dump_args(void)
+{
+ int i;
+
+ DEBUG(10, ("dumping argument values:\n"));
+ for (i = 0; i < ARRAY_SIZE(args); ++i) {
+ switch (args[i].arg_type) {
+ case ARG_NUMERIC:
+ case ARG_SIZE:
+ DEBUG(10, ("\t%s=%llu\n", args[i].arg_name,
+ (unsigned long long)args[i].arg_val.nval));
+ break;
+ case ARG_BOOL:
+ DEBUG(10, ("\t%s=%s\n", args[i].arg_name,
+ args[i].arg_val.bval ? "yes" : "no"));
+ break;
+ case ARG_PATHNAME:
+ DEBUG(10, ("\t%s=%s\n", args[i].arg_name,
+ args[i].arg_val.pval ?
+ args[i].arg_val.pval :
+ "(NULL)"));
+ break;
+ default:
+ SMB_ASSERT(0);
+ }
+ }
+}
+
+static void cifsdd_help_message(poptContext pctx,
+ enum poptCallbackReason preason,
+ struct poptOption * poption,
+ const char * parg,
+ void * pdata)
+{
+ static const char notes[] =
+"FILE can be a local filename or a UNC path of the form //server/share/path.\n";
+
+ char prefix[24];
+ int i;
+
+ if (poption->shortName != '?') {
+ poptPrintUsage(pctx, stdout, 0);
+ fprintf(stdout, " [dd options]\n");
+ exit(0);
+ }
+
+ poptPrintHelp(pctx, stdout, 0);
+ fprintf(stdout, "\nCIFS dd options:\n");
+
+ for (i = 0; i < ARRAY_SIZE(args); ++i) {
+ if (args[i].arg_name == NULL) {
+ break;
+ }
+
+ snprintf(prefix, sizeof(prefix), "%s=%-*s",
+ args[i].arg_name,
+ (int)(sizeof(prefix) - strlen(args[i].arg_name) - 2),
+ argtype_str(args[i].arg_type));
+ prefix[sizeof(prefix) - 1] = '\0';
+ fprintf(stdout, " %s%s\n", prefix, args[i].arg_help);
+ }
+
+ fprintf(stdout, "\n%s\n", notes);
+ exit(0);
+}
+
+/* ------------------------------------------------------------------------- */
+/* Main block copying routine. */
+/* ------------------------------------------------------------------------- */
+
+static void print_transfer_stats(void)
+{
+ if (DEBUGLEVEL > 0) {
+ printf("%llu+%llu records in (%llu bytes)\n"
+ "%llu+%llu records out (%llu bytes)\n",
+ (unsigned long long)dd_stats.in.fblocks,
+ (unsigned long long)dd_stats.in.pblocks,
+ (unsigned long long)dd_stats.in.bytes,
+ (unsigned long long)dd_stats.out.fblocks,
+ (unsigned long long)dd_stats.out.pblocks,
+ (unsigned long long)dd_stats.out.bytes);
+ } else {
+ printf("%llu+%llu records in\n%llu+%llu records out\n",
+ (unsigned long long)dd_stats.in.fblocks,
+ (unsigned long long)dd_stats.in.pblocks,
+ (unsigned long long)dd_stats.out.fblocks,
+ (unsigned long long)dd_stats.out.pblocks);
+ }
+}
+
+static struct dd_iohandle * open_file(struct resolve_context *resolve_ctx,
+ struct tevent_context *ev,
+ const char * which, const char **ports,
+ struct smbcli_options *smb_options,
+ const char *socket_options,
+ struct smbcli_session_options *smb_session_options,
+ struct smb_iconv_convenience *iconv_convenience,
+ struct gensec_settings *gensec_settings)
+{
+ int options = 0;
+ const char * path = NULL;
+ struct dd_iohandle * handle = NULL;
+
+ if (check_arg_bool("direct")) {
+ options |= DD_DIRECT_IO;
+ }
+
+ if (check_arg_bool("sync")) {
+ options |= DD_SYNC_IO;
+ }
+
+ if (check_arg_bool("oplock")) {
+ options |= DD_OPLOCK;
+ }
+
+ if (strcmp(which, "if") == 0) {
+ path = check_arg_pathname("if");
+ handle = dd_open_path(resolve_ctx, ev, path, ports,
+ check_arg_numeric("ibs"), options,
+ socket_options,
+ smb_options, smb_session_options,
+ iconv_convenience,
+ gensec_settings);
+ } else if (strcmp(which, "of") == 0) {
+ options |= DD_WRITE;
+ path = check_arg_pathname("of");
+ handle = dd_open_path(resolve_ctx, ev, path, ports,
+ check_arg_numeric("obs"), options,
+ socket_options,
+ smb_options, smb_session_options,
+ iconv_convenience,
+ gensec_settings);
+ } else {
+ SMB_ASSERT(0);
+ return(NULL);
+ }
+
+ if (!handle) {
+ fprintf(stderr, "%s: failed to open %s\n", PROGNAME, path);
+ }
+
+ return(handle);
+}
+
+static int copy_files(struct tevent_context *ev, struct loadparm_context *lp_ctx)
+{
+ uint8_t * iobuf; /* IO buffer. */
+ uint64_t iomax; /* Size of the IO buffer. */
+ uint64_t data_size; /* Amount of data in the IO buffer. */
+
+ uint64_t ibs;
+ uint64_t obs;
+ uint64_t count;
+
+ struct dd_iohandle * ifile;
+ struct dd_iohandle * ofile;
+
+ struct smbcli_options options;
+ struct smbcli_session_options session_options;
+
+ ibs = check_arg_numeric("ibs");
+ obs = check_arg_numeric("obs");
+ count = check_arg_numeric("count");
+
+ lp_smbcli_options(lp_ctx, &options);
+ lp_smbcli_session_options(lp_ctx, &session_options);
+
+ /* Allocate IO buffer. We need more than the max IO size because we
+ * could accumulate a remainder if ibs and obs don't match.
+ */
+ iomax = 2 * MAX(ibs, obs);
+ if ((iobuf = malloc_array_p(uint8_t, iomax)) == NULL) {
+ fprintf(stderr,
+ "%s: failed to allocate IO buffer of %llu bytes\n",
+ PROGNAME, (unsigned long long)iomax);
+ return(EOM_EXIT_CODE);
+ }
+
+ options.max_xmit = MAX(ibs, obs);
+
+ DEBUG(4, ("IO buffer size is %llu, max xmit is %d\n",
+ (unsigned long long)iomax, options.max_xmit));
+
+ if (!(ifile = open_file(lp_resolve_context(lp_ctx), ev, "if",
+ lp_smb_ports(lp_ctx), &options,
+ lp_socket_options(lp_ctx),
+ &session_options, lp_iconv_convenience(lp_ctx),
+ lp_gensec_settings(lp_ctx, lp_ctx)))) {
+ return(FILESYS_EXIT_CODE);
+ }
+
+ if (!(ofile = open_file(lp_resolve_context(lp_ctx), ev, "of",
+ lp_smb_ports(lp_ctx), &options,
+ lp_socket_options(lp_ctx),
+ &session_options,
+ lp_iconv_convenience(lp_ctx),
+ lp_gensec_settings(lp_ctx, lp_ctx)))) {
+ return(FILESYS_EXIT_CODE);
+ }
+
+ /* Seek the files to their respective starting points. */
+ ifile->io_seek(ifile, check_arg_numeric("skip") * ibs);
+ ofile->io_seek(ofile, check_arg_numeric("seek") * obs);
+
+ DEBUG(4, ("max xmit was negotiated to be %d\n", options.max_xmit));
+
+ for (data_size = 0;;) {
+
+ /* Handle signals. We are somewhat compatible with GNU dd.
+ * SIGINT makes us stop, but still print transfer statistics.
+ * SIGUSR1 makes us print transfer statistics but we continue
+ * copying.
+ */
+ if (dd_sigint) {
+ break;
+ }
+
+ if (dd_sigusr1) {
+ print_transfer_stats();
+ dd_sigusr1 = 0;
+ }
+
+ if (ifile->io_flags & DD_END_OF_FILE) {
+ DEBUG(4, ("flushing %llu bytes at EOF\n",
+ (unsigned long long)data_size));
+ while (data_size > 0) {
+ if (!dd_flush_block(ofile, iobuf,
+ &data_size, obs)) {
+ return(IOERROR_EXIT_CODE);
+ }
+ }
+ goto done;
+ }
+
+ /* Try and read enough blocks of ibs bytes to be able write
+ * out one of obs bytes.
+ */
+ if (!dd_fill_block(ifile, iobuf, &data_size, obs, ibs)) {
+ return(IOERROR_EXIT_CODE);
+ }
+
+ if (data_size == 0) {
+ /* Done. */
+ SMB_ASSERT(ifile->io_flags & DD_END_OF_FILE);
+ }
+
+ /* Stop reading when we hit the block count. */
+ if (dd_stats.in.bytes >= (ibs * count)) {
+ ifile->io_flags |= DD_END_OF_FILE;
+ }
+
+ /* If we wanted to be a legitimate dd, we would do character
+ * conversions and other shenanigans here.
+ */
+
+ /* Flush what we read in units of obs bytes. We want to have
+ * at least obs bytes in the IO buffer but might not if the
+ * file is too small.
+ */
+ if (data_size &&
+ !dd_flush_block(ofile, iobuf, &data_size, obs)) {
+ return(IOERROR_EXIT_CODE);
+ }
+ }
+
+done:
+ print_transfer_stats();
+ return(0);
+}
+
+/* ------------------------------------------------------------------------- */
+/* Main. */
+/* ------------------------------------------------------------------------- */
+
+struct poptOption cifsddHelpOptions[] = {
+ { NULL, '\0', POPT_ARG_CALLBACK, (void *)&cifsdd_help_message, '\0', NULL, NULL },
+ { "help", '?', 0, NULL, '?', "Show this help message", NULL },
+ { "usage", '\0', 0, NULL, 'u', "Display brief usage message", NULL },
+ { NULL }
+} ;
+
+int main(int argc, const char ** argv)
+{
+ int i;
+ const char ** dd_args;
+ struct tevent_context *ev;
+
+ poptContext pctx;
+ struct poptOption poptions[] = {
+ /* POPT_AUTOHELP */
+ { NULL, '\0', POPT_ARG_INCLUDE_TABLE, cifsddHelpOptions,
+ 0, "Help options:", NULL },
+ POPT_COMMON_SAMBA
+ POPT_COMMON_CONNECTION
+ POPT_COMMON_CREDENTIALS
+ POPT_COMMON_VERSION
+ { NULL }
+ };
+
+ /* Block sizes. */
+ set_arg_val("bs", (uint64_t)4096);
+ set_arg_val("ibs", (uint64_t)4096);
+ set_arg_val("obs", (uint64_t)4096);
+ /* Block counts. */
+ set_arg_val("count", (uint64_t)-1);
+ set_arg_val("seek", (uint64_t)0);
+ set_arg_val("seek", (uint64_t)0);
+ /* Files. */
+ set_arg_val("if", NULL);
+ set_arg_val("of", NULL);
+ /* Options. */
+ set_arg_val("direct", false);
+ set_arg_val("sync", false);
+ set_arg_val("oplock", false);
+
+ pctx = poptGetContext(PROGNAME, argc, argv, poptions, 0);
+ while ((i = poptGetNextOpt(pctx)) != -1) {
+ ;
+ }
+
+ for (dd_args = poptGetArgs(pctx); dd_args && *dd_args; ++dd_args) {
+
+ if (!set_arg_argv(*dd_args)) {
+ fprintf(stderr, "%s: invalid option: %s\n",
+ PROGNAME, *dd_args);
+ exit(SYNTAX_EXIT_CODE);
+ }
+
+ /* "bs" has the side-effect of setting "ibs" and "obs". */
+ if (strncmp(*dd_args, "bs=", 3) == 0) {
+ uint64_t bs = check_arg_numeric("bs");
+ set_arg_val("ibs", bs);
+ set_arg_val("obs", bs);
+ }
+ }
+
+ ev = s4_event_context_init(talloc_autofree_context());
+
+ gensec_init(cmdline_lp_ctx);
+ dump_args();
+
+ if (check_arg_numeric("ibs") == 0 || check_arg_numeric("ibs") == 0) {
+ fprintf(stderr, "%s: block sizes must be greater that zero\n",
+ PROGNAME);
+ exit(SYNTAX_EXIT_CODE);
+ }
+
+ if (check_arg_pathname("if") == NULL) {
+ fprintf(stderr, "%s: missing input filename\n", PROGNAME);
+ exit(SYNTAX_EXIT_CODE);
+ }
+
+ if (check_arg_pathname("of") == NULL) {
+ fprintf(stderr, "%s: missing output filename\n", PROGNAME);
+ exit(SYNTAX_EXIT_CODE);
+ }
+
+ CatchSignal(SIGINT, dd_handle_signal);
+ CatchSignal(SIGUSR1, dd_handle_signal);
+ return(copy_files(ev, cmdline_lp_ctx));
+}
+
+/* vim: set sw=8 sts=8 ts=8 tw=79 : */
diff --git a/source4/client/cifsdd.h b/source4/client/cifsdd.h
new file mode 100644
index 0000000000..e39c046fe8
--- /dev/null
+++ b/source4/client/cifsdd.h
@@ -0,0 +1,110 @@
+/*
+ CIFSDD - dd for SMB.
+ Declarations and administrivia.
+
+ Copyright (C) James Peach 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 <http://www.gnu.org/licenses/>.
+*/
+
+extern const char * const PROGNAME;
+
+enum argtype
+{
+ ARG_NUMERIC,
+ ARG_SIZE,
+ ARG_PATHNAME,
+ ARG_BOOL,
+};
+
+struct argdef
+{
+ const char * arg_name;
+ enum argtype arg_type;
+ const char * arg_help;
+
+ union
+ {
+ bool bval;
+ uint64_t nval;
+ const char * pval;
+ } arg_val;
+};
+
+int set_arg_argv(const char * argv);
+void set_arg_val(const char * name, ...);
+
+bool check_arg_bool(const char * name);
+uint64_t check_arg_numeric(const char * name);
+const char * check_arg_pathname(const char * name);
+
+typedef bool (*dd_seek_func)(void * handle, uint64_t offset);
+typedef bool (*dd_read_func)(void * handle, uint8_t * buf,
+ uint64_t wanted, uint64_t * actual);
+typedef bool (*dd_write_func)(void * handle, uint8_t * buf,
+ uint64_t wanted, uint64_t * actual);
+
+struct dd_stats_record
+{
+ struct
+ {
+ uint64_t fblocks; /* Full blocks. */
+ uint64_t pblocks; /* Partial blocks. */
+ uint64_t bytes; /* Total bytes read. */
+ } in;
+ struct
+ {
+ uint64_t fblocks; /* Full blocks. */
+ uint64_t pblocks; /* Partial blocks. */
+ uint64_t bytes; /* Total bytes written. */
+ } out;
+};
+
+extern struct dd_stats_record dd_stats;
+
+struct dd_iohandle
+{
+ dd_seek_func io_seek;
+ dd_read_func io_read;
+ dd_write_func io_write;
+ int io_flags;
+};
+
+#define DD_END_OF_FILE 0x10000000
+
+#define DD_DIRECT_IO 0x00000001
+#define DD_SYNC_IO 0x00000002
+#define DD_WRITE 0x00000004
+#define DD_OPLOCK 0x00000008
+
+struct smbcli_options;
+struct smbcli_session_options;
+struct tevent_context;
+
+struct dd_iohandle * dd_open_path(struct resolve_context *resolve_ctx,
+ struct tevent_context *ev,
+ const char * path,
+ const char **ports,
+ uint64_t io_size, int options,
+ const char *socket_options,
+ struct smbcli_options *smb_options,
+ struct smbcli_session_options *smb_session_options,
+ struct smb_iconv_convenience *iconv_convenience,
+ struct gensec_settings *gensec_settings);
+bool dd_fill_block(struct dd_iohandle * h, uint8_t * buf,
+ uint64_t * buf_size, uint64_t need_size, uint64_t block_size);
+bool dd_flush_block(struct dd_iohandle * h, uint8_t * buf,
+ uint64_t * buf_size, uint64_t block_size);
+
+/* vim: set sw=8 sts=8 ts=8 tw=79 : */
diff --git a/source4/client/cifsddio.c b/source4/client/cifsddio.c
new file mode 100644
index 0000000000..ab18dd08d1
--- /dev/null
+++ b/source4/client/cifsddio.c
@@ -0,0 +1,530 @@
+/*
+ CIFSDD - dd for SMB.
+ IO routines, generic and specific.
+
+ Copyright (C) James Peach 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/libcli.h"
+#include "lib/cmdline/popt_common.h"
+
+#include "cifsdd.h"
+
+/* ------------------------------------------------------------------------- */
+/* UNIX file descriptor IO. */
+/* ------------------------------------------------------------------------- */
+
+struct fd_handle
+{
+ struct dd_iohandle h;
+ int fd;
+};
+
+#define IO_HANDLE_TO_FD(h) (((struct fd_handle *)(h))->fd)
+
+static bool fd_seek_func(void * handle, uint64_t offset)
+{
+ ssize_t ret;
+
+ ret = lseek(IO_HANDLE_TO_FD(handle), offset, SEEK_SET);
+ if (ret < 0) {
+ fprintf(stderr, "%s: seek failed: %s\n",
+ PROGNAME, strerror(errno));
+ return(false);
+ }
+
+ return(true);
+}
+
+static bool fd_read_func(void * handle,
+ uint8_t * buf,
+ uint64_t wanted,
+ uint64_t * actual)
+{
+ ssize_t ret;
+
+ ret = read(IO_HANDLE_TO_FD(handle), buf, wanted);
+ if (ret < 0) {
+ fprintf(stderr, "%s: %llu byte read failed: %s\n",
+ PROGNAME, (unsigned long long)wanted,
+ strerror(errno));
+ return(false);
+ }
+
+ *actual = (uint64_t)ret;
+ return(true);
+}
+
+static bool fd_write_func(void * handle,
+ uint8_t * buf,
+ uint64_t wanted,
+ uint64_t * actual)
+{
+ ssize_t ret;
+
+ ret = write(IO_HANDLE_TO_FD(handle), buf, wanted);
+ if (ret < 0) {
+ fprintf(stderr, "%s: %llu byte write failed: %s\n",
+ PROGNAME, (unsigned long long)wanted,
+ strerror(errno));
+ return(false);
+ }
+
+ *actual = (uint64_t)ret;
+ return(true);
+}
+
+static struct dd_iohandle * open_fd_handle(const char * path,
+ uint64_t io_size,
+ int options)
+{
+ struct fd_handle * fdh;
+ int oflags = 0;
+
+ DEBUG(4, ("opening fd stream for %s\n", path));
+ if ((fdh = talloc_zero(NULL, struct fd_handle)) == NULL) {
+ return(NULL);
+ }
+
+ fdh->h.io_read = fd_read_func;
+ fdh->h.io_write = fd_write_func;
+ fdh->h.io_seek = fd_seek_func;
+
+ if (options & DD_DIRECT_IO) {
+#ifdef HAVE_OPEN_O_DIRECT
+ oflags |= O_DIRECT;
+#else
+ DEBUG(1, ("no support for direct IO on this platform\n"));
+#endif
+ }
+
+ if (options & DD_SYNC_IO)
+ oflags |= O_SYNC;
+
+ oflags |= (options & DD_WRITE) ? (O_WRONLY | O_CREAT) : (O_RDONLY);
+
+ fdh->fd = open(path, oflags, 0644);
+ if (fdh->fd < 0) {
+ fprintf(stderr, "%s: %s: %s\n",
+ PROGNAME, path, strerror(errno));
+ talloc_free(fdh);
+ return(NULL);
+ }
+
+ if (options & DD_OPLOCK) {
+ DEBUG(2, ("FIXME: take local oplock on %s\n", path));
+ }
+
+ SMB_ASSERT((void *)fdh == (void *)&fdh->h);
+ return(&fdh->h);
+}
+
+/* ------------------------------------------------------------------------- */
+/* CIFS client IO. */
+/* ------------------------------------------------------------------------- */
+
+struct cifs_handle
+{
+ struct dd_iohandle h;
+ struct smbcli_state * cli;
+ int fnum;
+ uint64_t offset;
+};
+
+#define IO_HANDLE_TO_SMB(h) ((struct cifs_handle *)(h))
+
+static bool smb_seek_func(void * handle, uint64_t offset)
+{
+ IO_HANDLE_TO_SMB(handle)->offset = offset;
+ return(true);
+}
+
+static bool smb_read_func(void * handle, uint8_t * buf, uint64_t wanted,
+ uint64_t * actual)
+{
+ NTSTATUS ret;
+ union smb_read r;
+ struct cifs_handle * smbh;
+
+ ZERO_STRUCT(r);
+ smbh = IO_HANDLE_TO_SMB(handle);
+
+ r.generic.level = RAW_READ_READX;
+ r.readx.in.file.fnum = smbh->fnum;
+ r.readx.in.offset = smbh->offset;
+ r.readx.in.mincnt = wanted;
+ r.readx.in.maxcnt = wanted;
+ r.readx.out.data = buf;
+
+ /* FIXME: Should I really set readx.in.remaining? That just seems
+ * redundant.
+ */
+ ret = smb_raw_read(smbh->cli->tree, &r);
+ if (!NT_STATUS_IS_OK(ret)) {
+ fprintf(stderr, "%s: %llu byte read failed: %s\n",
+ PROGNAME, (unsigned long long)wanted,
+ nt_errstr(ret));
+ return(false);
+ }
+
+ /* Trap integer wrap. */
+ SMB_ASSERT((smbh->offset + r.readx.out.nread) >= smbh->offset);
+
+ *actual = r.readx.out.nread;
+ smbh->offset += r.readx.out.nread;
+ return(true);
+}
+
+static bool smb_write_func(void * handle, uint8_t * buf, uint64_t wanted,
+ uint64_t * actual)
+{
+ NTSTATUS ret;
+ union smb_write w;
+ struct cifs_handle * smbh;
+
+ ZERO_STRUCT(w);
+ smbh = IO_HANDLE_TO_SMB(handle);
+
+ w.generic.level = RAW_WRITE_WRITEX;
+ w.writex.in.file.fnum = smbh->fnum;
+ w.writex.in.offset = smbh->offset;
+ w.writex.in.count = wanted;
+ w.writex.in.data = buf;
+
+ ret = smb_raw_write(smbh->cli->tree, &w);
+ if (!NT_STATUS_IS_OK(ret)) {
+ fprintf(stderr, "%s: %llu byte write failed: %s\n",
+ PROGNAME, (unsigned long long)wanted,
+ nt_errstr(ret));
+ return(false);
+ }
+
+ *actual = w.writex.out.nwritten;
+ smbh->offset += w.writex.out.nwritten;
+ return(true);
+}
+
+static struct smbcli_state * init_smb_session(struct resolve_context *resolve_ctx,
+ struct tevent_context *ev,
+ const char * host,
+ const char **ports,
+ const char * share,
+ const char *socket_options,
+ struct smbcli_options *options,
+ struct smbcli_session_options *session_options,
+ struct smb_iconv_convenience *iconv_convenience,
+ struct gensec_settings *gensec_settings)
+{
+ NTSTATUS ret;
+ struct smbcli_state * cli = NULL;
+
+ /* When we support SMB URLs, we can get different user credentials for
+ * each connection, but for now, we just use the same one for both.
+ */
+ ret = smbcli_full_connection(NULL, &cli, host, ports, share,
+ NULL /* devtype */,
+ socket_options,
+ cmdline_credentials, resolve_ctx,
+ ev, options,
+ session_options,
+ iconv_convenience,
+ gensec_settings);
+
+ if (!NT_STATUS_IS_OK(ret)) {
+ fprintf(stderr, "%s: connecting to //%s/%s: %s\n",
+ PROGNAME, host, share, nt_errstr(ret));
+ return(NULL);
+ }
+
+ return(cli);
+}
+
+static int open_smb_file(struct smbcli_state * cli,
+ const char * path,
+ int options)
+{
+ NTSTATUS ret;
+ union smb_open o;
+
+ ZERO_STRUCT(o);
+
+ o.ntcreatex.level = RAW_OPEN_NTCREATEX;
+ o.ntcreatex.in.fname = path;
+
+ /* TODO: It's not clear whether to use these flags or to use the
+ * similarly named NTCREATEX flags in the create_options field.
+ */
+ if (options & DD_DIRECT_IO)
+ o.ntcreatex.in.flags |= FILE_FLAG_NO_BUFFERING;
+
+ if (options & DD_SYNC_IO)
+ o.ntcreatex.in.flags |= FILE_FLAG_WRITE_THROUGH;
+
+ o.ntcreatex.in.access_mask |=
+ (options & DD_WRITE) ? SEC_FILE_WRITE_DATA
+ : SEC_FILE_READ_DATA;
+
+ /* Try to create the file only if we will be writing to it. */
+ o.ntcreatex.in.open_disposition =
+ (options & DD_WRITE) ? NTCREATEX_DISP_OPEN_IF
+ : NTCREATEX_DISP_OPEN;
+
+ o.ntcreatex.in.share_access =
+ NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
+
+ if (options & DD_OPLOCK) {
+ o.ntcreatex.in.flags |= NTCREATEX_FLAGS_REQUEST_OPLOCK;
+ }
+
+ ret = smb_raw_open(cli->tree, NULL, &o);
+ if (!NT_STATUS_IS_OK(ret)) {
+ fprintf(stderr, "%s: opening %s: %s\n",
+ PROGNAME, path, nt_errstr(ret));
+ return(-1);
+ }
+
+ return(o.ntcreatex.out.file.fnum);
+}
+
+static struct dd_iohandle * open_cifs_handle(struct resolve_context *resolve_ctx,
+ struct tevent_context *ev,
+ const char * host,
+ const char **ports,
+ const char * share,
+ const char * path,
+ uint64_t io_size,
+ int options,
+ const char *socket_options,
+ struct smbcli_options *smb_options,
+ struct smbcli_session_options *smb_session_options,
+ struct smb_iconv_convenience *iconv_convenience,
+ struct gensec_settings *gensec_settings)
+{
+ struct cifs_handle * smbh;
+
+ if (path == NULL || *path == '\0') {
+ fprintf(stderr, "%s: missing path name within share //%s/%s\n",
+ PROGNAME, host, share);
+ }
+
+ DEBUG(4, ("opening SMB stream to //%s/%s for %s\n",
+ host, share, path));
+
+ if ((smbh = talloc_zero(NULL, struct cifs_handle)) == NULL) {
+ return(NULL);
+ }
+
+ smbh->h.io_read = smb_read_func;
+ smbh->h.io_write = smb_write_func;
+ smbh->h.io_seek = smb_seek_func;
+
+ if ((smbh->cli = init_smb_session(resolve_ctx, ev, host, ports, share,
+ socket_options,
+ smb_options, smb_session_options,
+ iconv_convenience,
+ gensec_settings)) == NULL) {
+ return(NULL);
+ }
+
+ DEBUG(4, ("connected to //%s/%s with xmit size of %u bytes\n",
+ host, share, smbh->cli->transport->negotiate.max_xmit));
+
+ smbh->fnum = open_smb_file(smbh->cli, path, options);
+ return(&smbh->h);
+}
+
+/* ------------------------------------------------------------------------- */
+/* Abstract IO interface. */
+/* ------------------------------------------------------------------------- */
+
+struct dd_iohandle * dd_open_path(struct resolve_context *resolve_ctx,
+ struct tevent_context *ev,
+ const char * path,
+ const char **ports,
+ uint64_t io_size,
+ int options,
+ const char *socket_options,
+ struct smbcli_options *smb_options,
+ struct smbcli_session_options *smb_session_options,
+ struct smb_iconv_convenience *iconv_convenience,
+ struct gensec_settings *gensec_settings)
+{
+ if (file_exist(path)) {
+ return(open_fd_handle(path, io_size, options));
+ } else {
+ char * host;
+ char * share;
+
+ if (smbcli_parse_unc(path, NULL, &host, &share)) {
+ const char * remain;
+ remain = strstr(path, share) + strlen(share);
+
+ /* Skip over leading directory separators. */
+ while (*remain == '/' || *remain == '\\') { remain++; }
+
+ return(open_cifs_handle(resolve_ctx, ev, host, ports,
+ share, remain,
+ io_size, options,
+ socket_options, smb_options,
+ smb_session_options,
+ iconv_convenience,
+ gensec_settings));
+ }
+
+ return(open_fd_handle(path, io_size, options));
+ }
+}
+
+/* Fill the buffer till it has at least need_size bytes. Use read operations of
+ * block_size bytes. Return the number of bytes read and fill buf_size with
+ * the new buffer size.
+ *
+ * NOTE: The IO buffer is guaranteed to be big enough to fit
+ * need_size + block_size bytes into it.
+ */
+bool dd_fill_block(struct dd_iohandle * h,
+ uint8_t * buf,
+ uint64_t * buf_size,
+ uint64_t need_size,
+ uint64_t block_size)
+{
+ uint64_t read_size;
+
+ SMB_ASSERT(block_size > 0);
+ SMB_ASSERT(need_size > 0);
+
+ while (*buf_size < need_size) {
+
+ if (!h->io_read(h, buf + (*buf_size), block_size, &read_size)) {
+ return(false);
+ }
+
+ if (read_size == 0) {
+ h->io_flags |= DD_END_OF_FILE;
+ break;
+ }
+
+ DEBUG(6, ("added %llu bytes to IO buffer (need %llu bytes)\n",
+ (unsigned long long)read_size,
+ (unsigned long long)need_size));
+
+ *buf_size += read_size;
+ dd_stats.in.bytes += read_size;
+
+ if (read_size == block_size) {
+ dd_stats.in.fblocks++;
+ } else {
+ DEBUG(3, ("partial read of %llu bytes (expected %llu)\n",
+ (unsigned long long)read_size,
+ (unsigned long long)block_size));
+ dd_stats.in.pblocks++;
+ }
+ }
+
+ return(true);
+}
+
+/* Flush a buffer that contains buf_size bytes. Use writes of block_size to do it,
+ * and shift any remaining bytes back to the head of the buffer when there are
+ * no more block_size sized IOs left.
+ */
+bool dd_flush_block(struct dd_iohandle * h,
+ uint8_t * buf,
+ uint64_t * buf_size,
+ uint64_t block_size)
+{
+ uint64_t write_size;
+ uint64_t total_size = 0;
+
+ SMB_ASSERT(block_size > 0);
+
+ /* We have explicitly been asked to write a partial block. */
+ if ((*buf_size) < block_size) {
+
+ if (!h->io_write(h, buf, *buf_size, &write_size)) {
+ return(false);
+ }
+
+ if (write_size == 0) {
+ fprintf(stderr, "%s: unexpectedly wrote 0 bytes\n",
+ PROGNAME);
+ return(false);
+ }
+
+ total_size += write_size;
+ dd_stats.out.bytes += write_size;
+ dd_stats.out.pblocks++;
+ }
+
+ /* Write as many full blocks as there are in the buffer. */
+ while (((*buf_size) - total_size) >= block_size) {
+
+ if (!h->io_write(h, buf + total_size, block_size, &write_size)) {
+ return(false);
+ }
+
+ if (write_size == 0) {
+ fprintf(stderr, "%s: unexpectedly wrote 0 bytes\n",
+ PROGNAME);
+ return(false);
+ }
+
+ if (write_size == block_size) {
+ dd_stats.out.fblocks++;
+ } else {
+ dd_stats.out.pblocks++;
+ }
+
+ total_size += write_size;
+ dd_stats.out.bytes += write_size;
+
+ DEBUG(6, ("flushed %llu bytes from IO buffer of %llu bytes (%llu remain)\n",
+ (unsigned long long)block_size,
+ (unsigned long long)block_size,
+ (unsigned long long)(block_size - total_size)));
+ }
+
+ SMB_ASSERT(total_size > 0);
+
+ /* We have flushed as much of the IO buffer as we can while
+ * still doing block_size'd operations. Shift any remaining data
+ * to the front of the IO buffer.
+ */
+ if ((*buf_size) > total_size) {
+ uint64_t remain = (*buf_size) - total_size;
+
+ DEBUG(3, ("shifting %llu remainder bytes to IO buffer head\n",
+ (unsigned long long)remain));
+
+ memmove(buf, buf + total_size, remain);
+ (*buf_size) = remain;
+ } else if ((*buf_size) == total_size) {
+ (*buf_size) = 0;
+ } else {
+ /* Else buffer contains buf_size bytes that we will append
+ * to next time round.
+ */
+ DEBUG(3, ("%llu unflushed bytes left in IO buffer\n",
+ (unsigned long long)(*buf_size)));
+ }
+
+ return(true);
+}
+
+/* vim: set sw=8 sts=8 ts=8 tw=79 : */
diff --git a/source4/client/client.c b/source4/client/client.c
new file mode 100644
index 0000000000..018be29761
--- /dev/null
+++ b/source4/client/client.c
@@ -0,0 +1,3305 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB client
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Simo Sorce 2001-2002
+ Copyright (C) Jelmer Vernooij 2003-2004
+ Copyright (C) James J Myers 2003 <myersjj@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * TODO: remove this ... and don't use talloc_append_string()
+ *
+ * NOTE: I'm not changing the code yet, because I assume there're
+ * some bugs in the existing code and I'm not sure how to fix
+ * them correctly.
+ */
+#define TALLOC_DEPRECATED 1
+
+#include "includes.h"
+#include "version.h"
+#include "libcli/libcli.h"
+#include "lib/events/events.h"
+#include "lib/cmdline/popt_common.h"
+#include "librpc/gen_ndr/ndr_srvsvc_c.h"
+#include "librpc/gen_ndr/ndr_lsa.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/util/clilsa.h"
+#include "system/dir.h"
+#include "system/filesys.h"
+#include "../lib/util/dlinklist.h"
+#include "system/readline.h"
+#include "auth/credentials/credentials.h"
+#include "auth/gensec/gensec.h"
+#include "system/time.h" /* needed by some systems for asctime() */
+#include "libcli/resolve/resolve.h"
+#include "libcli/security/security.h"
+#include "lib/smbreadline/smbreadline.h"
+#include "librpc/gen_ndr/ndr_nbt.h"
+#include "param/param.h"
+#include "librpc/rpc/dcerpc.h"
+#include "libcli/raw/raw_proto.h"
+
+/* the default pager to use for the client "more" command. Users can
+ * override this with the PAGER environment variable */
+#ifndef DEFAULT_PAGER
+#define DEFAULT_PAGER "more"
+#endif
+
+struct smbclient_context {
+ char *remote_cur_dir;
+ struct smbcli_state *cli;
+ char *fileselection;
+ time_t newer_than;
+ bool prompt;
+ bool recurse;
+ int archive_level;
+ bool lowercase;
+ int printmode;
+ bool translation;
+ int io_bufsize;
+};
+
+/* timing globals */
+static uint64_t get_total_size = 0;
+static uint_t get_total_time_ms = 0;
+static uint64_t put_total_size = 0;
+static uint_t put_total_time_ms = 0;
+
+/* Unfortunately, there is no way to pass the a context to the completion function as an argument */
+static struct smbclient_context *rl_ctx;
+
+/* totals globals */
+static double dir_total;
+
+/*******************************************************************
+ Reduce a file name, removing .. elements.
+********************************************************************/
+static void dos_clean_name(char *s)
+{
+ char *p=NULL,*r;
+
+ DEBUG(3,("dos_clean_name [%s]\n",s));
+
+ /* remove any double slashes */
+ all_string_sub(s, "\\\\", "\\", 0);
+
+ while ((p = strstr(s,"\\..\\")) != NULL) {
+ *p = '\0';
+ if ((r = strrchr(s,'\\')) != NULL)
+ memmove(r,p+3,strlen(p+3)+1);
+ }
+
+ trim_string(s,NULL,"\\..");
+
+ all_string_sub(s, "\\.\\", "\\", 0);
+}
+
+/****************************************************************************
+write to a local file with CR/LF->LF translation if appropriate. return the
+number taken from the buffer. This may not equal the number written.
+****************************************************************************/
+static int writefile(int f, const void *_b, int n, bool translation)
+{
+ const uint8_t *b = (const uint8_t *)_b;
+ int i;
+
+ if (!translation) {
+ return write(f,b,n);
+ }
+
+ i = 0;
+ while (i < n) {
+ if (*b == '\r' && (i<(n-1)) && *(b+1) == '\n') {
+ b++;i++;
+ }
+ if (write(f, b, 1) != 1) {
+ break;
+ }
+ b++;
+ i++;
+ }
+
+ return(i);
+}
+
+/****************************************************************************
+ read from a file with LF->CR/LF translation if appropriate. return the
+ number read. read approx n bytes.
+****************************************************************************/
+static int readfile(void *_b, int n, XFILE *f, bool translation)
+{
+ uint8_t *b = (uint8_t *)_b;
+ int i;
+ int c;
+
+ if (!translation)
+ return x_fread(b,1,n,f);
+
+ i = 0;
+ while (i < (n - 1)) {
+ if ((c = x_getc(f)) == EOF) {
+ break;
+ }
+
+ if (c == '\n') { /* change all LFs to CR/LF */
+ b[i++] = '\r';
+ }
+
+ b[i++] = c;
+ }
+
+ return(i);
+}
+
+
+/****************************************************************************
+send a message
+****************************************************************************/
+static void send_message(struct smbcli_state *cli, const char *desthost)
+{
+ char msg[1600];
+ int total_len = 0;
+ int grp_id;
+
+ if (!smbcli_message_start(cli->tree, desthost, cli_credentials_get_username(cmdline_credentials), &grp_id)) {
+ d_printf("message start: %s\n", smbcli_errstr(cli->tree));
+ return;
+ }
+
+
+ d_printf("Connected. Type your message, ending it with a Control-D\n");
+
+ while (!feof(stdin) && total_len < 1600) {
+ int maxlen = MIN(1600 - total_len,127);
+ int l=0;
+ int c;
+
+ for (l=0;l<maxlen && (c=fgetc(stdin))!=EOF;l++) {
+ if (c == '\n')
+ msg[l++] = '\r';
+ msg[l] = c;
+ }
+
+ if (!smbcli_message_text(cli->tree, msg, l, grp_id)) {
+ d_printf("SMBsendtxt failed (%s)\n",smbcli_errstr(cli->tree));
+ return;
+ }
+
+ total_len += l;
+ }
+
+ if (total_len >= 1600)
+ d_printf("the message was truncated to 1600 bytes\n");
+ else
+ d_printf("sent %d bytes\n",total_len);
+
+ if (!smbcli_message_end(cli->tree, grp_id)) {
+ d_printf("SMBsendend failed (%s)\n",smbcli_errstr(cli->tree));
+ return;
+ }
+}
+
+
+
+/****************************************************************************
+check the space on a device
+****************************************************************************/
+static int do_dskattr(struct smbclient_context *ctx)
+{
+ uint32_t bsize;
+ uint64_t total, avail;
+
+ if (NT_STATUS_IS_ERR(smbcli_dskattr(ctx->cli->tree, &bsize, &total, &avail))) {
+ d_printf("Error in dskattr: %s\n",smbcli_errstr(ctx->cli->tree));
+ return 1;
+ }
+
+ d_printf("\n\t\t%llu blocks of size %u. %llu blocks available\n",
+ (unsigned long long)total,
+ (unsigned)bsize,
+ (unsigned long long)avail);
+
+ return 0;
+}
+
+/****************************************************************************
+show cd/pwd
+****************************************************************************/
+static int cmd_pwd(struct smbclient_context *ctx, const char **args)
+{
+ d_printf("Current directory is %s\n", ctx->remote_cur_dir);
+ return 0;
+}
+
+/*
+ convert a string to dos format
+*/
+static void dos_format(char *s)
+{
+ string_replace(s, '/', '\\');
+}
+
+/****************************************************************************
+change directory - inner section
+****************************************************************************/
+static int do_cd(struct smbclient_context *ctx, const char *newdir)
+{
+ char *dname;
+
+ /* Save the current directory in case the
+ new directory is invalid */
+ if (newdir[0] == '\\')
+ dname = talloc_strdup(NULL, newdir);
+ else
+ dname = talloc_asprintf(NULL, "%s\\%s", ctx->remote_cur_dir, newdir);
+
+ dos_format(dname);
+
+ if (*(dname+strlen(dname)-1) != '\\') {
+ dname = talloc_append_string(NULL, dname, "\\");
+ }
+ dos_clean_name(dname);
+
+ if (NT_STATUS_IS_ERR(smbcli_chkpath(ctx->cli->tree, dname))) {
+ d_printf("cd %s: %s\n", dname, smbcli_errstr(ctx->cli->tree));
+ talloc_free(dname);
+ } else {
+ ctx->remote_cur_dir = dname;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+change directory
+****************************************************************************/
+static int cmd_cd(struct smbclient_context *ctx, const char **args)
+{
+ int rc = 0;
+
+ if (args[1])
+ rc = do_cd(ctx, args[1]);
+ else
+ d_printf("Current directory is %s\n",ctx->remote_cur_dir);
+
+ return rc;
+}
+
+
+static bool mask_match(struct smbcli_state *c, const char *string,
+ const char *pattern, bool is_case_sensitive)
+{
+ char *p2, *s2;
+ bool ret;
+
+ if (ISDOTDOT(string))
+ string = ".";
+ if (ISDOT(pattern))
+ return false;
+
+ if (is_case_sensitive)
+ return ms_fnmatch(pattern, string,
+ c->transport->negotiate.protocol) == 0;
+
+ p2 = strlower_talloc(NULL, pattern);
+ s2 = strlower_talloc(NULL, string);
+ ret = ms_fnmatch(p2, s2, c->transport->negotiate.protocol) == 0;
+ talloc_free(p2);
+ talloc_free(s2);
+
+ return ret;
+}
+
+
+
+/*******************************************************************
+ decide if a file should be operated on
+ ********************************************************************/
+static bool do_this_one(struct smbclient_context *ctx, struct clilist_file_info *finfo)
+{
+ if (finfo->attrib & FILE_ATTRIBUTE_DIRECTORY) return(true);
+
+ if (ctx->fileselection &&
+ !mask_match(ctx->cli, finfo->name,ctx->fileselection,false)) {
+ DEBUG(3,("mask_match %s failed\n", finfo->name));
+ return false;
+ }
+
+ if (ctx->newer_than && finfo->mtime < ctx->newer_than) {
+ DEBUG(3,("newer_than %s failed\n", finfo->name));
+ return(false);
+ }
+
+ if ((ctx->archive_level==1 || ctx->archive_level==2) && !(finfo->attrib & FILE_ATTRIBUTE_ARCHIVE)) {
+ DEBUG(3,("archive %s failed\n", finfo->name));
+ return(false);
+ }
+
+ return(true);
+}
+
+/****************************************************************************
+ display info about a file
+ ****************************************************************************/
+static void display_finfo(struct smbclient_context *ctx, struct clilist_file_info *finfo)
+{
+ if (do_this_one(ctx, finfo)) {
+ time_t t = finfo->mtime; /* the time is assumed to be passed as GMT */
+ char *astr = attrib_string(NULL, finfo->attrib);
+ d_printf(" %-30s%7.7s %8.0f %s",
+ finfo->name,
+ astr,
+ (double)finfo->size,
+ asctime(localtime(&t)));
+ dir_total += finfo->size;
+ talloc_free(astr);
+ }
+}
+
+
+/****************************************************************************
+ accumulate size of a file
+ ****************************************************************************/
+static void do_du(struct smbclient_context *ctx, struct clilist_file_info *finfo)
+{
+ if (do_this_one(ctx, finfo)) {
+ dir_total += finfo->size;
+ }
+}
+
+static bool do_list_recurse;
+static bool do_list_dirs;
+static char *do_list_queue = 0;
+static long do_list_queue_size = 0;
+static long do_list_queue_start = 0;
+static long do_list_queue_end = 0;
+static void (*do_list_fn)(struct smbclient_context *, struct clilist_file_info *);
+
+/****************************************************************************
+functions for do_list_queue
+ ****************************************************************************/
+
+/*
+ * The do_list_queue is a NUL-separated list of strings stored in a
+ * char*. Since this is a FIFO, we keep track of the beginning and
+ * ending locations of the data in the queue. When we overflow, we
+ * double the size of the char*. When the start of the data passes
+ * the midpoint, we move everything back. This is logically more
+ * complex than a linked list, but easier from a memory management
+ * angle. In any memory error condition, do_list_queue is reset.
+ * Functions check to ensure that do_list_queue is non-NULL before
+ * accessing it.
+ */
+static void reset_do_list_queue(void)
+{
+ SAFE_FREE(do_list_queue);
+ do_list_queue_size = 0;
+ do_list_queue_start = 0;
+ do_list_queue_end = 0;
+}
+
+static void init_do_list_queue(void)
+{
+ reset_do_list_queue();
+ do_list_queue_size = 1024;
+ do_list_queue = malloc_array_p(char, do_list_queue_size);
+ if (do_list_queue == 0) {
+ d_printf("malloc fail for size %d\n",
+ (int)do_list_queue_size);
+ reset_do_list_queue();
+ } else {
+ memset(do_list_queue, 0, do_list_queue_size);
+ }
+}
+
+static void adjust_do_list_queue(void)
+{
+ if (do_list_queue == NULL) return;
+
+ /*
+ * If the starting point of the queue is more than half way through,
+ * move everything toward the beginning.
+ */
+ if (do_list_queue_start == do_list_queue_end)
+ {
+ DEBUG(4,("do_list_queue is empty\n"));
+ do_list_queue_start = do_list_queue_end = 0;
+ *do_list_queue = '\0';
+ }
+ else if (do_list_queue_start > (do_list_queue_size / 2))
+ {
+ DEBUG(4,("sliding do_list_queue backward\n"));
+ memmove(do_list_queue,
+ do_list_queue + do_list_queue_start,
+ do_list_queue_end - do_list_queue_start);
+ do_list_queue_end -= do_list_queue_start;
+ do_list_queue_start = 0;
+ }
+
+}
+
+static void add_to_do_list_queue(const char* entry)
+{
+ char *dlq;
+ long new_end = do_list_queue_end + ((long)strlen(entry)) + 1;
+ while (new_end > do_list_queue_size)
+ {
+ do_list_queue_size *= 2;
+ DEBUG(4,("enlarging do_list_queue to %d\n",
+ (int)do_list_queue_size));
+ dlq = realloc_p(do_list_queue, char, do_list_queue_size);
+ if (! dlq) {
+ d_printf("failure enlarging do_list_queue to %d bytes\n",
+ (int)do_list_queue_size);
+ reset_do_list_queue();
+ }
+ else
+ {
+ do_list_queue = dlq;
+ memset(do_list_queue + do_list_queue_size / 2,
+ 0, do_list_queue_size / 2);
+ }
+ }
+ if (do_list_queue)
+ {
+ safe_strcpy(do_list_queue + do_list_queue_end, entry,
+ do_list_queue_size - do_list_queue_end - 1);
+ do_list_queue_end = new_end;
+ DEBUG(4,("added %s to do_list_queue (start=%d, end=%d)\n",
+ entry, (int)do_list_queue_start, (int)do_list_queue_end));
+ }
+}
+
+static char *do_list_queue_head(void)
+{
+ return do_list_queue + do_list_queue_start;
+}
+
+static void remove_do_list_queue_head(void)
+{
+ if (do_list_queue_end > do_list_queue_start)
+ {
+ do_list_queue_start += strlen(do_list_queue_head()) + 1;
+ adjust_do_list_queue();
+ DEBUG(4,("removed head of do_list_queue (start=%d, end=%d)\n",
+ (int)do_list_queue_start, (int)do_list_queue_end));
+ }
+}
+
+static int do_list_queue_empty(void)
+{
+ return (! (do_list_queue && *do_list_queue));
+}
+
+/****************************************************************************
+a helper for do_list
+ ****************************************************************************/
+static void do_list_helper(struct clilist_file_info *f, const char *mask, void *state)
+{
+ struct smbclient_context *ctx = (struct smbclient_context *)state;
+
+ if (f->attrib & FILE_ATTRIBUTE_DIRECTORY) {
+ if (do_list_dirs && do_this_one(ctx, f)) {
+ do_list_fn(ctx, f);
+ }
+ if (do_list_recurse &&
+ !ISDOT(f->name) &&
+ !ISDOTDOT(f->name)) {
+ char *mask2;
+ char *p;
+
+ mask2 = talloc_strdup(NULL, mask);
+ p = strrchr_m(mask2,'\\');
+ if (!p) return;
+ p[1] = 0;
+ mask2 = talloc_asprintf_append_buffer(mask2, "%s\\*", f->name);
+ add_to_do_list_queue(mask2);
+ }
+ return;
+ }
+
+ if (do_this_one(ctx, f)) {
+ do_list_fn(ctx, f);
+ }
+}
+
+
+/****************************************************************************
+a wrapper around smbcli_list that adds recursion
+ ****************************************************************************/
+static void do_list(struct smbclient_context *ctx, const char *mask,uint16_t attribute,
+ void (*fn)(struct smbclient_context *, struct clilist_file_info *),bool rec, bool dirs)
+{
+ static int in_do_list = 0;
+
+ if (in_do_list && rec)
+ {
+ fprintf(stderr, "INTERNAL ERROR: do_list called recursively when the recursive flag is true\n");
+ exit(1);
+ }
+
+ in_do_list = 1;
+
+ do_list_recurse = rec;
+ do_list_dirs = dirs;
+ do_list_fn = fn;
+
+ if (rec)
+ {
+ init_do_list_queue();
+ add_to_do_list_queue(mask);
+
+ while (! do_list_queue_empty())
+ {
+ /*
+ * Need to copy head so that it doesn't become
+ * invalid inside the call to smbcli_list. This
+ * would happen if the list were expanded
+ * during the call.
+ * Fix from E. Jay Berkenbilt (ejb@ql.org)
+ */
+ char *head;
+ head = do_list_queue_head();
+ smbcli_list(ctx->cli->tree, head, attribute, do_list_helper, ctx);
+ remove_do_list_queue_head();
+ if ((! do_list_queue_empty()) && (fn == display_finfo))
+ {
+ char* next_file = do_list_queue_head();
+ char* save_ch = 0;
+ if ((strlen(next_file) >= 2) &&
+ (next_file[strlen(next_file) - 1] == '*') &&
+ (next_file[strlen(next_file) - 2] == '\\'))
+ {
+ save_ch = next_file +
+ strlen(next_file) - 2;
+ *save_ch = '\0';
+ }
+ d_printf("\n%s\n",next_file);
+ if (save_ch)
+ {
+ *save_ch = '\\';
+ }
+ }
+ }
+ }
+ else
+ {
+ if (smbcli_list(ctx->cli->tree, mask, attribute, do_list_helper, ctx) == -1)
+ {
+ d_printf("%s listing %s\n", smbcli_errstr(ctx->cli->tree), mask);
+ }
+ }
+
+ in_do_list = 0;
+ reset_do_list_queue();
+}
+
+/****************************************************************************
+ get a directory listing
+ ****************************************************************************/
+static int cmd_dir(struct smbclient_context *ctx, const char **args)
+{
+ uint16_t attribute = FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN;
+ char *mask;
+ int rc;
+
+ dir_total = 0;
+
+ mask = talloc_strdup(ctx, ctx->remote_cur_dir);
+ if(mask[strlen(mask)-1]!='\\')
+ mask = talloc_append_string(ctx, mask,"\\");
+
+ if (args[1]) {
+ mask = talloc_strdup(ctx, args[1]);
+ if (mask[0] != '\\')
+ mask = talloc_append_string(ctx, mask, "\\");
+ dos_format(mask);
+ }
+ else {
+ if (ctx->cli->tree->session->transport->negotiate.protocol <=
+ PROTOCOL_LANMAN1) {
+ mask = talloc_append_string(ctx, mask, "*.*");
+ } else {
+ mask = talloc_append_string(ctx, mask, "*");
+ }
+ }
+
+ do_list(ctx, mask, attribute, display_finfo, ctx->recurse, true);
+
+ rc = do_dskattr(ctx);
+
+ DEBUG(3, ("Total bytes listed: %.0f\n", dir_total));
+
+ return rc;
+}
+
+
+/****************************************************************************
+ get a directory listing
+ ****************************************************************************/
+static int cmd_du(struct smbclient_context *ctx, const char **args)
+{
+ uint16_t attribute = FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN;
+ int rc;
+ char *mask;
+
+ dir_total = 0;
+
+ if (args[1]) {
+ if (args[1][0] == '\\')
+ mask = talloc_strdup(ctx, args[1]);
+ else
+ mask = talloc_asprintf(ctx, "%s\\%s", ctx->remote_cur_dir, args[1]);
+ dos_format(mask);
+ } else {
+ mask = talloc_asprintf(ctx, "%s\\*", ctx->remote_cur_dir);
+ }
+
+ do_list(ctx, mask, attribute, do_du, ctx->recurse, true);
+
+ talloc_free(mask);
+
+ rc = do_dskattr(ctx);
+
+ d_printf("Total number of bytes: %.0f\n", dir_total);
+
+ return rc;
+}
+
+
+/****************************************************************************
+ get a file from rname to lname
+ ****************************************************************************/
+static int do_get(struct smbclient_context *ctx, char *rname, const char *lname, bool reget)
+{
+ int handle = 0, fnum;
+ bool newhandle = false;
+ uint8_t *data;
+ struct timeval tp_start;
+ int read_size = ctx->io_bufsize;
+ uint16_t attr;
+ size_t size;
+ off_t start = 0;
+ off_t nread = 0;
+ int rc = 0;
+
+ GetTimeOfDay(&tp_start);
+
+ if (ctx->lowercase) {
+ strlower(discard_const_p(char, lname));
+ }
+
+ fnum = smbcli_open(ctx->cli->tree, rname, O_RDONLY, DENY_NONE);
+
+ if (fnum == -1) {
+ d_printf("%s opening remote file %s\n",smbcli_errstr(ctx->cli->tree),rname);
+ return 1;
+ }
+
+ if(!strcmp(lname,"-")) {
+ handle = fileno(stdout);
+ } else {
+ if (reget) {
+ handle = open(lname, O_WRONLY|O_CREAT, 0644);
+ if (handle >= 0) {
+ start = lseek(handle, 0, SEEK_END);
+ if (start == -1) {
+ d_printf("Error seeking local file\n");
+ return 1;
+ }
+ }
+ } else {
+ handle = open(lname, O_WRONLY|O_CREAT|O_TRUNC, 0644);
+ }
+ newhandle = true;
+ }
+ if (handle < 0) {
+ d_printf("Error opening local file %s\n",lname);
+ return 1;
+ }
+
+
+ if (NT_STATUS_IS_ERR(smbcli_qfileinfo(ctx->cli->tree, fnum,
+ &attr, &size, NULL, NULL, NULL, NULL, NULL)) &&
+ NT_STATUS_IS_ERR(smbcli_getattrE(ctx->cli->tree, fnum,
+ &attr, &size, NULL, NULL, NULL))) {
+ d_printf("getattrib: %s\n",smbcli_errstr(ctx->cli->tree));
+ return 1;
+ }
+
+ DEBUG(2,("getting file %s of size %.0f as %s ",
+ rname, (double)size, lname));
+
+ if(!(data = (uint8_t *)malloc(read_size))) {
+ d_printf("malloc fail for size %d\n", read_size);
+ smbcli_close(ctx->cli->tree, fnum);
+ return 1;
+ }
+
+ while (1) {
+ int n = smbcli_read(ctx->cli->tree, fnum, data, nread + start, read_size);
+
+ if (n <= 0) break;
+
+ if (writefile(handle,data, n, ctx->translation) != n) {
+ d_printf("Error writing local file\n");
+ rc = 1;
+ break;
+ }
+
+ nread += n;
+ }
+
+ if (nread + start < size) {
+ DEBUG (0, ("Short read when getting file %s. Only got %ld bytes.\n",
+ rname, (long)nread));
+
+ rc = 1;
+ }
+
+ SAFE_FREE(data);
+
+ if (NT_STATUS_IS_ERR(smbcli_close(ctx->cli->tree, fnum))) {
+ d_printf("Error %s closing remote file\n",smbcli_errstr(ctx->cli->tree));
+ rc = 1;
+ }
+
+ if (newhandle) {
+ close(handle);
+ }
+
+ if (ctx->archive_level >= 2 && (attr & FILE_ATTRIBUTE_ARCHIVE)) {
+ smbcli_setatr(ctx->cli->tree, rname, attr & ~(uint16_t)FILE_ATTRIBUTE_ARCHIVE, 0);
+ }
+
+ {
+ struct timeval tp_end;
+ int this_time;
+
+ GetTimeOfDay(&tp_end);
+ this_time =
+ (tp_end.tv_sec - tp_start.tv_sec)*1000 +
+ (tp_end.tv_usec - tp_start.tv_usec)/1000;
+ get_total_time_ms += this_time;
+ get_total_size += nread;
+
+ DEBUG(2,("(%3.1f kb/s) (average %3.1f kb/s)\n",
+ nread / (1.024*this_time + 1.0e-4),
+ get_total_size / (1.024*get_total_time_ms)));
+ }
+
+ return rc;
+}
+
+
+/****************************************************************************
+ get a file
+ ****************************************************************************/
+static int cmd_get(struct smbclient_context *ctx, const char **args)
+{
+ const char *lname;
+ char *rname;
+
+ if (!args[1]) {
+ d_printf("get <filename>\n");
+ return 1;
+ }
+
+ rname = talloc_asprintf(ctx, "%s\\%s", ctx->remote_cur_dir, args[1]);
+
+ if (args[2])
+ lname = args[2];
+ else
+ lname = args[1];
+
+ dos_clean_name(rname);
+
+ return do_get(ctx, rname, lname, false);
+}
+
+/****************************************************************************
+ Put up a yes/no prompt.
+****************************************************************************/
+static bool yesno(char *p)
+{
+ char ans[4];
+ printf("%s",p);
+
+ if (!fgets(ans,sizeof(ans)-1,stdin))
+ return(false);
+
+ if (*ans == 'y' || *ans == 'Y')
+ return(true);
+
+ return(false);
+}
+
+/****************************************************************************
+ do a mget operation on one file
+ ****************************************************************************/
+static void do_mget(struct smbclient_context *ctx, struct clilist_file_info *finfo)
+{
+ char *rname;
+ char *quest;
+ char *mget_mask;
+ char *saved_curdir;
+
+ if (ISDOT(finfo->name) || ISDOTDOT(finfo->name))
+ return;
+
+ if (finfo->attrib & FILE_ATTRIBUTE_DIRECTORY)
+ asprintf(&quest, "Get directory %s? ",finfo->name);
+ else
+ asprintf(&quest, "Get file %s? ",finfo->name);
+
+ if (ctx->prompt && !yesno(quest)) return;
+
+ SAFE_FREE(quest);
+
+ if (!(finfo->attrib & FILE_ATTRIBUTE_DIRECTORY)) {
+ asprintf(&rname, "%s%s",ctx->remote_cur_dir,finfo->name);
+ do_get(ctx, rname, finfo->name, false);
+ SAFE_FREE(rname);
+ return;
+ }
+
+ /* handle directories */
+ saved_curdir = talloc_strdup(NULL, ctx->remote_cur_dir);
+
+ ctx->remote_cur_dir = talloc_asprintf_append_buffer(NULL, "%s\\", finfo->name);
+
+ string_replace(discard_const_p(char, finfo->name), '\\', '/');
+ if (ctx->lowercase) {
+ strlower(discard_const_p(char, finfo->name));
+ }
+
+ if (!directory_exist(finfo->name) &&
+ mkdir(finfo->name,0777) != 0) {
+ d_printf("failed to create directory %s\n",finfo->name);
+ return;
+ }
+
+ if (chdir(finfo->name) != 0) {
+ d_printf("failed to chdir to directory %s\n",finfo->name);
+ return;
+ }
+
+ mget_mask = talloc_asprintf(NULL, "%s*", ctx->remote_cur_dir);
+
+ do_list(ctx, mget_mask, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_DIRECTORY,do_mget,false, true);
+ chdir("..");
+ talloc_free(ctx->remote_cur_dir);
+
+ ctx->remote_cur_dir = saved_curdir;
+}
+
+
+/****************************************************************************
+view the file using the pager
+****************************************************************************/
+static int cmd_more(struct smbclient_context *ctx, const char **args)
+{
+ char *rname;
+ char *pager_cmd;
+ char *lname;
+ char *pager;
+ int fd;
+ int rc = 0;
+
+ lname = talloc_asprintf(ctx, "%s/smbmore.XXXXXX",tmpdir());
+ fd = mkstemp(lname);
+ if (fd == -1) {
+ d_printf("failed to create temporary file for more\n");
+ return 1;
+ }
+ close(fd);
+
+ if (!args[1]) {
+ d_printf("more <filename>\n");
+ unlink(lname);
+ return 1;
+ }
+ rname = talloc_asprintf(ctx, "%s%s", ctx->remote_cur_dir, args[1]);
+ dos_clean_name(rname);
+
+ rc = do_get(ctx, rname, lname, false);
+
+ pager=getenv("PAGER");
+
+ pager_cmd = talloc_asprintf(ctx, "%s %s",(pager? pager:DEFAULT_PAGER), lname);
+ system(pager_cmd);
+ unlink(lname);
+
+ return rc;
+}
+
+
+
+/****************************************************************************
+do a mget command
+****************************************************************************/
+static int cmd_mget(struct smbclient_context *ctx, const char **args)
+{
+ uint16_t attribute = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN;
+ char *mget_mask = NULL;
+ int i;
+
+ if (ctx->recurse)
+ attribute |= FILE_ATTRIBUTE_DIRECTORY;
+
+ for (i = 1; args[i]; i++) {
+ mget_mask = talloc_strdup(ctx,ctx->remote_cur_dir);
+ if(mget_mask[strlen(mget_mask)-1]!='\\')
+ mget_mask = talloc_append_string(ctx, mget_mask, "\\");
+
+ mget_mask = talloc_strdup(ctx, args[i]);
+ if (mget_mask[0] != '\\')
+ mget_mask = talloc_append_string(ctx, mget_mask, "\\");
+ do_list(ctx, mget_mask, attribute,do_mget,false,true);
+
+ talloc_free(mget_mask);
+ }
+
+ if (mget_mask == NULL) {
+ mget_mask = talloc_asprintf(ctx, "%s\\*", ctx->remote_cur_dir);
+ do_list(ctx, mget_mask, attribute,do_mget,false,true);
+ talloc_free(mget_mask);
+ }
+
+ return 0;
+}
+
+
+/****************************************************************************
+make a directory of name "name"
+****************************************************************************/
+static NTSTATUS do_mkdir(struct smbclient_context *ctx, char *name)
+{
+ NTSTATUS status;
+
+ if (NT_STATUS_IS_ERR(status = smbcli_mkdir(ctx->cli->tree, name))) {
+ d_printf("%s making remote directory %s\n",
+ smbcli_errstr(ctx->cli->tree),name);
+ return status;
+ }
+
+ return status;
+}
+
+
+/****************************************************************************
+ Exit client.
+****************************************************************************/
+static int cmd_quit(struct smbclient_context *ctx, const char **args)
+{
+ talloc_free(ctx);
+ exit(0);
+ /* NOTREACHED */
+ return 0;
+}
+
+
+/****************************************************************************
+ make a directory
+ ****************************************************************************/
+static int cmd_mkdir(struct smbclient_context *ctx, const char **args)
+{
+ char *mask, *p;
+
+ if (!args[1]) {
+ if (!ctx->recurse)
+ d_printf("mkdir <dirname>\n");
+ return 1;
+ }
+
+ mask = talloc_asprintf(ctx, "%s%s", ctx->remote_cur_dir,args[1]);
+
+ if (ctx->recurse) {
+ dos_clean_name(mask);
+
+ trim_string(mask,".",NULL);
+ for (p = strtok(mask,"/\\"); p; p = strtok(p, "/\\")) {
+ char *parent = talloc_strndup(ctx, mask, PTR_DIFF(p, mask));
+
+ if (NT_STATUS_IS_ERR(smbcli_chkpath(ctx->cli->tree, parent))) {
+ do_mkdir(ctx, parent);
+ }
+
+ talloc_free(parent);
+ }
+ } else {
+ do_mkdir(ctx, mask);
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+show 8.3 name of a file
+****************************************************************************/
+static int cmd_altname(struct smbclient_context *ctx, const char **args)
+{
+ const char *altname;
+ char *name;
+
+ if (!args[1]) {
+ d_printf("altname <file>\n");
+ return 1;
+ }
+
+ name = talloc_asprintf(ctx, "%s%s", ctx->remote_cur_dir, args[1]);
+
+ if (!NT_STATUS_IS_OK(smbcli_qpathinfo_alt_name(ctx->cli->tree, name, &altname))) {
+ d_printf("%s getting alt name for %s\n",
+ smbcli_errstr(ctx->cli->tree),name);
+ return(false);
+ }
+ d_printf("%s\n", altname);
+
+ return 0;
+}
+
+
+/****************************************************************************
+ put a single file
+ ****************************************************************************/
+static int do_put(struct smbclient_context *ctx, char *rname, char *lname, bool reput)
+{
+ int fnum;
+ XFILE *f;
+ size_t start = 0;
+ off_t nread = 0;
+ uint8_t *buf = NULL;
+ int maxwrite = ctx->io_bufsize;
+ int rc = 0;
+
+ struct timeval tp_start;
+ GetTimeOfDay(&tp_start);
+
+ if (reput) {
+ fnum = smbcli_open(ctx->cli->tree, rname, O_RDWR|O_CREAT, DENY_NONE);
+ if (fnum >= 0) {
+ if (NT_STATUS_IS_ERR(smbcli_qfileinfo(ctx->cli->tree, fnum, NULL, &start, NULL, NULL, NULL, NULL, NULL)) &&
+ NT_STATUS_IS_ERR(smbcli_getattrE(ctx->cli->tree, fnum, NULL, &start, NULL, NULL, NULL))) {
+ d_printf("getattrib: %s\n",smbcli_errstr(ctx->cli->tree));
+ return 1;
+ }
+ }
+ } else {
+ fnum = smbcli_open(ctx->cli->tree, rname, O_RDWR|O_CREAT|O_TRUNC,
+ DENY_NONE);
+ }
+
+ if (fnum == -1) {
+ d_printf("%s opening remote file %s\n",smbcli_errstr(ctx->cli->tree),rname);
+ return 1;
+ }
+
+ /* allow files to be piped into smbclient
+ jdblair 24.jun.98
+
+ Note that in this case this function will exit(0) rather
+ than returning. */
+ if (!strcmp(lname, "-")) {
+ f = x_stdin;
+ /* size of file is not known */
+ } else {
+ f = x_fopen(lname,O_RDONLY, 0);
+ if (f && reput) {
+ if (x_tseek(f, start, SEEK_SET) == -1) {
+ d_printf("Error seeking local file\n");
+ return 1;
+ }
+ }
+ }
+
+ if (!f) {
+ d_printf("Error opening local file %s\n",lname);
+ return 1;
+ }
+
+
+ DEBUG(1,("putting file %s as %s ",lname,
+ rname));
+
+ buf = (uint8_t *)malloc(maxwrite);
+ if (!buf) {
+ d_printf("ERROR: Not enough memory!\n");
+ return 1;
+ }
+ while (!x_feof(f)) {
+ int n = maxwrite;
+ int ret;
+
+ if ((n = readfile(buf,n,f,ctx->translation)) < 1) {
+ if((n == 0) && x_feof(f))
+ break; /* Empty local file. */
+
+ d_printf("Error reading local file: %s\n", strerror(errno));
+ rc = 1;
+ break;
+ }
+
+ ret = smbcli_write(ctx->cli->tree, fnum, 0, buf, nread + start, n);
+
+ if (n != ret) {
+ d_printf("Error writing file: %s\n", smbcli_errstr(ctx->cli->tree));
+ rc = 1;
+ break;
+ }
+
+ nread += n;
+ }
+
+ if (NT_STATUS_IS_ERR(smbcli_close(ctx->cli->tree, fnum))) {
+ d_printf("%s closing remote file %s\n",smbcli_errstr(ctx->cli->tree),rname);
+ x_fclose(f);
+ SAFE_FREE(buf);
+ return 1;
+ }
+
+
+ if (f != x_stdin) {
+ x_fclose(f);
+ }
+
+ SAFE_FREE(buf);
+
+ {
+ struct timeval tp_end;
+ int this_time;
+
+ GetTimeOfDay(&tp_end);
+ this_time =
+ (tp_end.tv_sec - tp_start.tv_sec)*1000 +
+ (tp_end.tv_usec - tp_start.tv_usec)/1000;
+ put_total_time_ms += this_time;
+ put_total_size += nread;
+
+ DEBUG(1,("(%3.1f kb/s) (average %3.1f kb/s)\n",
+ nread / (1.024*this_time + 1.0e-4),
+ put_total_size / (1.024*put_total_time_ms)));
+ }
+
+ if (f == x_stdin) {
+ talloc_free(ctx);
+ exit(0);
+ }
+
+ return rc;
+}
+
+
+
+/****************************************************************************
+ put a file
+ ****************************************************************************/
+static int cmd_put(struct smbclient_context *ctx, const char **args)
+{
+ char *lname;
+ char *rname;
+
+ if (!args[1]) {
+ d_printf("put <filename> [<remotename>]\n");
+ return 1;
+ }
+
+ lname = talloc_strdup(ctx, args[1]);
+
+ if (args[2])
+ rname = talloc_strdup(ctx, args[2]);
+ else
+ rname = talloc_asprintf(ctx, "%s\\%s", ctx->remote_cur_dir, lname);
+
+ dos_clean_name(rname);
+
+ /* allow '-' to represent stdin
+ jdblair, 24.jun.98 */
+ if (!file_exist(lname) && (strcmp(lname,"-"))) {
+ d_printf("%s does not exist\n",lname);
+ return 1;
+ }
+
+ return do_put(ctx, rname, lname, false);
+}
+
+/*************************************
+ File list structure
+*************************************/
+
+static struct file_list {
+ struct file_list *prev, *next;
+ char *file_path;
+ bool isdir;
+} *file_list;
+
+/****************************************************************************
+ Free a file_list structure
+****************************************************************************/
+
+static void free_file_list (struct file_list * list)
+{
+ struct file_list *tmp;
+
+ while (list)
+ {
+ tmp = list;
+ DLIST_REMOVE(list, list);
+ SAFE_FREE(tmp->file_path);
+ SAFE_FREE(tmp);
+ }
+}
+
+/****************************************************************************
+ seek in a directory/file list until you get something that doesn't start with
+ the specified name
+ ****************************************************************************/
+static bool seek_list(struct file_list *list, char *name)
+{
+ while (list) {
+ trim_string(list->file_path,"./","\n");
+ if (strncmp(list->file_path, name, strlen(name)) != 0) {
+ return(true);
+ }
+ list = list->next;
+ }
+
+ return(false);
+}
+
+/****************************************************************************
+ set the file selection mask
+ ****************************************************************************/
+static int cmd_select(struct smbclient_context *ctx, const char **args)
+{
+ talloc_free(ctx->fileselection);
+ ctx->fileselection = talloc_strdup(NULL, args[1]);
+
+ return 0;
+}
+
+/*******************************************************************
+ A readdir wrapper which just returns the file name.
+ ********************************************************************/
+static const char *readdirname(DIR *p)
+{
+ struct dirent *ptr;
+ char *dname;
+
+ if (!p)
+ return(NULL);
+
+ ptr = (struct dirent *)readdir(p);
+ if (!ptr)
+ return(NULL);
+
+ dname = ptr->d_name;
+
+#ifdef NEXT2
+ if (telldir(p) < 0)
+ return(NULL);
+#endif
+
+#ifdef HAVE_BROKEN_READDIR
+ /* using /usr/ucb/cc is BAD */
+ dname = dname - 2;
+#endif
+
+ {
+ static char *buf;
+ int len = NAMLEN(ptr);
+ buf = talloc_strndup(NULL, dname, len);
+ dname = buf;
+ }
+
+ return(dname);
+}
+
+/****************************************************************************
+ Recursive file matching function act as find
+ match must be always set to true when calling this function
+****************************************************************************/
+static int file_find(struct smbclient_context *ctx, struct file_list **list, const char *directory,
+ const char *expression, bool match)
+{
+ DIR *dir;
+ struct file_list *entry;
+ struct stat statbuf;
+ int ret;
+ char *path;
+ bool isdir;
+ const char *dname;
+
+ dir = opendir(directory);
+ if (!dir) return -1;
+
+ while ((dname = readdirname(dir))) {
+ if (ISDOT(dname) || ISDOTDOT(dname)) {
+ continue;
+ }
+
+ if (asprintf(&path, "%s/%s", directory, dname) <= 0) {
+ continue;
+ }
+
+ isdir = false;
+ if (!match || !gen_fnmatch(expression, dname)) {
+ if (ctx->recurse) {
+ ret = stat(path, &statbuf);
+ if (ret == 0) {
+ if (S_ISDIR(statbuf.st_mode)) {
+ isdir = true;
+ ret = file_find(ctx, list, path, expression, false);
+ }
+ } else {
+ d_printf("file_find: cannot stat file %s\n", path);
+ }
+
+ if (ret == -1) {
+ SAFE_FREE(path);
+ closedir(dir);
+ return -1;
+ }
+ }
+ entry = malloc_p(struct file_list);
+ if (!entry) {
+ d_printf("Out of memory in file_find\n");
+ closedir(dir);
+ return -1;
+ }
+ entry->file_path = path;
+ entry->isdir = isdir;
+ DLIST_ADD(*list, entry);
+ } else {
+ SAFE_FREE(path);
+ }
+ }
+
+ closedir(dir);
+ return 0;
+}
+
+/****************************************************************************
+ mput some files
+ ****************************************************************************/
+static int cmd_mput(struct smbclient_context *ctx, const char **args)
+{
+ int i;
+
+ for (i = 1; args[i]; i++) {
+ int ret;
+ struct file_list *temp_list;
+ char *quest, *lname, *rname;
+
+ printf("%s\n", args[i]);
+
+ file_list = NULL;
+
+ ret = file_find(ctx, &file_list, ".", args[i], true);
+ if (ret) {
+ free_file_list(file_list);
+ continue;
+ }
+
+ quest = NULL;
+ lname = NULL;
+ rname = NULL;
+
+ for (temp_list = file_list; temp_list;
+ temp_list = temp_list->next) {
+
+ SAFE_FREE(lname);
+ if (asprintf(&lname, "%s/", temp_list->file_path) <= 0)
+ continue;
+ trim_string(lname, "./", "/");
+
+ /* check if it's a directory */
+ if (temp_list->isdir) {
+ /* if (!recurse) continue; */
+
+ SAFE_FREE(quest);
+ if (asprintf(&quest, "Put directory %s? ", lname) < 0) break;
+ if (ctx->prompt && !yesno(quest)) { /* No */
+ /* Skip the directory */
+ lname[strlen(lname)-1] = '/';
+ if (!seek_list(temp_list, lname))
+ break;
+ } else { /* Yes */
+ SAFE_FREE(rname);
+ if(asprintf(&rname, "%s%s", ctx->remote_cur_dir, lname) < 0) break;
+ dos_format(rname);
+ if (NT_STATUS_IS_ERR(smbcli_chkpath(ctx->cli->tree, rname)) &&
+ NT_STATUS_IS_ERR(do_mkdir(ctx, rname))) {
+ DEBUG (0, ("Unable to make dir, skipping..."));
+ /* Skip the directory */
+ lname[strlen(lname)-1] = '/';
+ if (!seek_list(temp_list, lname))
+ break;
+ }
+ }
+ continue;
+ } else {
+ SAFE_FREE(quest);
+ if (asprintf(&quest,"Put file %s? ", lname) < 0) break;
+ if (ctx->prompt && !yesno(quest)) /* No */
+ continue;
+
+ /* Yes */
+ SAFE_FREE(rname);
+ if (asprintf(&rname, "%s%s", ctx->remote_cur_dir, lname) < 0) break;
+ }
+
+ dos_format(rname);
+
+ do_put(ctx, rname, lname, false);
+ }
+ free_file_list(file_list);
+ SAFE_FREE(quest);
+ SAFE_FREE(lname);
+ SAFE_FREE(rname);
+ }
+
+ return 0;
+}
+
+
+/****************************************************************************
+ print a file
+ ****************************************************************************/
+static int cmd_print(struct smbclient_context *ctx, const char **args)
+{
+ char *lname, *rname;
+ char *p;
+
+ if (!args[1]) {
+ d_printf("print <filename>\n");
+ return 1;
+ }
+
+ lname = talloc_strdup(ctx, args[1]);
+
+ rname = talloc_strdup(ctx, lname);
+ p = strrchr_m(rname,'/');
+ if (p) {
+ slprintf(rname, sizeof(rname)-1, "%s-%d", p+1, (int)getpid());
+ }
+
+ if (strequal(lname,"-")) {
+ slprintf(rname, sizeof(rname)-1, "stdin-%d", (int)getpid());
+ }
+
+ return do_put(ctx, rname, lname, false);
+}
+
+
+static int cmd_rewrite(struct smbclient_context *ctx, const char **args)
+{
+ d_printf("REWRITE: command not implemented (FIXME!)\n");
+
+ return 0;
+}
+
+/****************************************************************************
+delete some files
+****************************************************************************/
+static int cmd_del(struct smbclient_context *ctx, const char **args)
+{
+ char *mask;
+ uint16_t attribute = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN;
+
+ if (ctx->recurse)
+ attribute |= FILE_ATTRIBUTE_DIRECTORY;
+
+ if (!args[1]) {
+ d_printf("del <filename>\n");
+ return 1;
+ }
+ mask = talloc_asprintf(ctx,"%s%s", ctx->remote_cur_dir, args[1]);
+
+ if (NT_STATUS_IS_ERR(smbcli_unlink(ctx->cli->tree, mask))) {
+ d_printf("%s deleting remote file %s\n",smbcli_errstr(ctx->cli->tree),mask);
+ }
+
+ return 0;
+}
+
+
+/****************************************************************************
+delete a whole directory tree
+****************************************************************************/
+static int cmd_deltree(struct smbclient_context *ctx, const char **args)
+{
+ char *dname;
+ int ret;
+
+ if (!args[1]) {
+ d_printf("deltree <dirname>\n");
+ return 1;
+ }
+
+ dname = talloc_asprintf(ctx, "%s%s", ctx->remote_cur_dir, args[1]);
+
+ ret = smbcli_deltree(ctx->cli->tree, dname);
+
+ if (ret == -1) {
+ printf("Failed to delete tree %s - %s\n", dname, smbcli_errstr(ctx->cli->tree));
+ return -1;
+ }
+
+ printf("Deleted %d files in %s\n", ret, dname);
+
+ return 0;
+}
+
+typedef struct {
+ const char *level_name;
+ enum smb_fsinfo_level level;
+} fsinfo_level_t;
+
+fsinfo_level_t fsinfo_levels[] = {
+ {"dskattr", RAW_QFS_DSKATTR},
+ {"allocation", RAW_QFS_ALLOCATION},
+ {"volume", RAW_QFS_VOLUME},
+ {"volumeinfo", RAW_QFS_VOLUME_INFO},
+ {"sizeinfo", RAW_QFS_SIZE_INFO},
+ {"deviceinfo", RAW_QFS_DEVICE_INFO},
+ {"attributeinfo", RAW_QFS_ATTRIBUTE_INFO},
+ {"unixinfo", RAW_QFS_UNIX_INFO},
+ {"volume-information", RAW_QFS_VOLUME_INFORMATION},
+ {"size-information", RAW_QFS_SIZE_INFORMATION},
+ {"device-information", RAW_QFS_DEVICE_INFORMATION},
+ {"attribute-information", RAW_QFS_ATTRIBUTE_INFORMATION},
+ {"quota-information", RAW_QFS_QUOTA_INFORMATION},
+ {"fullsize-information", RAW_QFS_FULL_SIZE_INFORMATION},
+ {"objectid", RAW_QFS_OBJECTID_INFORMATION},
+ {NULL, RAW_QFS_GENERIC}
+};
+
+
+static int cmd_fsinfo(struct smbclient_context *ctx, const char **args)
+{
+ union smb_fsinfo fsinfo;
+ NTSTATUS status;
+ fsinfo_level_t *fsinfo_level;
+
+ if (!args[1]) {
+ d_printf("fsinfo <level>, where level is one of following:\n");
+ fsinfo_level = fsinfo_levels;
+ while(fsinfo_level->level_name) {
+ d_printf("%s\n", fsinfo_level->level_name);
+ fsinfo_level++;
+ }
+ return 1;
+ }
+
+ fsinfo_level = fsinfo_levels;
+ while(fsinfo_level->level_name && !strequal(args[1],fsinfo_level->level_name)) {
+ fsinfo_level++;
+ }
+
+ if (!fsinfo_level->level_name) {
+ d_printf("wrong level name!\n");
+ return 1;
+ }
+
+ fsinfo.generic.level = fsinfo_level->level;
+ status = smb_raw_fsinfo(ctx->cli->tree, ctx, &fsinfo);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("fsinfo-level-%s - %s\n", fsinfo_level->level_name, nt_errstr(status));
+ return 1;
+ }
+
+ d_printf("fsinfo-level-%s:\n", fsinfo_level->level_name);
+ switch(fsinfo.generic.level) {
+ case RAW_QFS_DSKATTR:
+ d_printf("\tunits_total: %hu\n",
+ (unsigned short) fsinfo.dskattr.out.units_total);
+ d_printf("\tblocks_per_unit: %hu\n",
+ (unsigned short) fsinfo.dskattr.out.blocks_per_unit);
+ d_printf("\tblocks_size: %hu\n",
+ (unsigned short) fsinfo.dskattr.out.block_size);
+ d_printf("\tunits_free: %hu\n",
+ (unsigned short) fsinfo.dskattr.out.units_free);
+ break;
+ case RAW_QFS_ALLOCATION:
+ d_printf("\tfs_id: %lu\n",
+ (unsigned long) fsinfo.allocation.out.fs_id);
+ d_printf("\tsectors_per_unit: %lu\n",
+ (unsigned long) fsinfo.allocation.out.sectors_per_unit);
+ d_printf("\ttotal_alloc_units: %lu\n",
+ (unsigned long) fsinfo.allocation.out.total_alloc_units);
+ d_printf("\tavail_alloc_units: %lu\n",
+ (unsigned long) fsinfo.allocation.out.avail_alloc_units);
+ d_printf("\tbytes_per_sector: %hu\n",
+ (unsigned short) fsinfo.allocation.out.bytes_per_sector);
+ break;
+ case RAW_QFS_VOLUME:
+ d_printf("\tserial_number: %lu\n",
+ (unsigned long) fsinfo.volume.out.serial_number);
+ d_printf("\tvolume_name: %s\n", fsinfo.volume.out.volume_name.s);
+ break;
+ case RAW_QFS_VOLUME_INFO:
+ case RAW_QFS_VOLUME_INFORMATION:
+ d_printf("\tcreate_time: %s\n",
+ nt_time_string(ctx,fsinfo.volume_info.out.create_time));
+ d_printf("\tserial_number: %lu\n",
+ (unsigned long) fsinfo.volume_info.out.serial_number);
+ d_printf("\tvolume_name: %s\n", fsinfo.volume_info.out.volume_name.s);
+ break;
+ case RAW_QFS_SIZE_INFO:
+ case RAW_QFS_SIZE_INFORMATION:
+ d_printf("\ttotal_alloc_units: %llu\n",
+ (unsigned long long) fsinfo.size_info.out.total_alloc_units);
+ d_printf("\tavail_alloc_units: %llu\n",
+ (unsigned long long) fsinfo.size_info.out.avail_alloc_units);
+ d_printf("\tsectors_per_unit: %lu\n",
+ (unsigned long) fsinfo.size_info.out.sectors_per_unit);
+ d_printf("\tbytes_per_sector: %lu\n",
+ (unsigned long) fsinfo.size_info.out.bytes_per_sector);
+ break;
+ case RAW_QFS_DEVICE_INFO:
+ case RAW_QFS_DEVICE_INFORMATION:
+ d_printf("\tdevice_type: %lu\n",
+ (unsigned long) fsinfo.device_info.out.device_type);
+ d_printf("\tcharacteristics: 0x%lx\n",
+ (unsigned long) fsinfo.device_info.out.characteristics);
+ break;
+ case RAW_QFS_ATTRIBUTE_INFORMATION:
+ case RAW_QFS_ATTRIBUTE_INFO:
+ d_printf("\tfs_attr: 0x%lx\n",
+ (unsigned long) fsinfo.attribute_info.out.fs_attr);
+ d_printf("\tmax_file_component_length: %lu\n",
+ (unsigned long) fsinfo.attribute_info.out.max_file_component_length);
+ d_printf("\tfs_type: %s\n", fsinfo.attribute_info.out.fs_type.s);
+ break;
+ case RAW_QFS_UNIX_INFO:
+ d_printf("\tmajor_version: %hu\n",
+ (unsigned short) fsinfo.unix_info.out.major_version);
+ d_printf("\tminor_version: %hu\n",
+ (unsigned short) fsinfo.unix_info.out.minor_version);
+ d_printf("\tcapability: 0x%llx\n",
+ (unsigned long long) fsinfo.unix_info.out.capability);
+ break;
+ case RAW_QFS_QUOTA_INFORMATION:
+ d_printf("\tunknown[3]: [%llu,%llu,%llu]\n",
+ (unsigned long long) fsinfo.quota_information.out.unknown[0],
+ (unsigned long long) fsinfo.quota_information.out.unknown[1],
+ (unsigned long long) fsinfo.quota_information.out.unknown[2]);
+ d_printf("\tquota_soft: %llu\n",
+ (unsigned long long) fsinfo.quota_information.out.quota_soft);
+ d_printf("\tquota_hard: %llu\n",
+ (unsigned long long) fsinfo.quota_information.out.quota_hard);
+ d_printf("\tquota_flags: 0x%llx\n",
+ (unsigned long long) fsinfo.quota_information.out.quota_flags);
+ break;
+ case RAW_QFS_FULL_SIZE_INFORMATION:
+ d_printf("\ttotal_alloc_units: %llu\n",
+ (unsigned long long) fsinfo.full_size_information.out.total_alloc_units);
+ d_printf("\tcall_avail_alloc_units: %llu\n",
+ (unsigned long long) fsinfo.full_size_information.out.call_avail_alloc_units);
+ d_printf("\tactual_avail_alloc_units: %llu\n",
+ (unsigned long long) fsinfo.full_size_information.out.actual_avail_alloc_units);
+ d_printf("\tsectors_per_unit: %lu\n",
+ (unsigned long) fsinfo.full_size_information.out.sectors_per_unit);
+ d_printf("\tbytes_per_sector: %lu\n",
+ (unsigned long) fsinfo.full_size_information.out.bytes_per_sector);
+ break;
+ case RAW_QFS_OBJECTID_INFORMATION:
+ d_printf("\tGUID: %s\n",
+ GUID_string(ctx,&fsinfo.objectid_information.out.guid));
+ d_printf("\tunknown[6]: [%llu,%llu,%llu,%llu,%llu,%llu]\n",
+ (unsigned long long) fsinfo.objectid_information.out.unknown[0],
+ (unsigned long long) fsinfo.objectid_information.out.unknown[1],
+ (unsigned long long) fsinfo.objectid_information.out.unknown[2],
+ (unsigned long long) fsinfo.objectid_information.out.unknown[3],
+ (unsigned long long) fsinfo.objectid_information.out.unknown[4],
+ (unsigned long long) fsinfo.objectid_information.out.unknown[5] );
+ break;
+ case RAW_QFS_GENERIC:
+ d_printf("\twrong level returned\n");
+ break;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+show as much information as possible about a file
+****************************************************************************/
+static int cmd_allinfo(struct smbclient_context *ctx, const char **args)
+{
+ char *fname;
+ union smb_fileinfo finfo;
+ NTSTATUS status;
+ int fnum;
+
+ if (!args[1]) {
+ d_printf("allinfo <filename>\n");
+ return 1;
+ }
+ fname = talloc_asprintf(ctx, "%s%s", ctx->remote_cur_dir, args[1]);
+
+ /* first a ALL_INFO QPATHINFO */
+ finfo.generic.level = RAW_FILEINFO_ALL_INFO;
+ finfo.generic.in.file.path = fname;
+ status = smb_raw_pathinfo(ctx->cli->tree, ctx, &finfo);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("%s - %s\n", fname, nt_errstr(status));
+ return 1;
+ }
+
+ d_printf("\tcreate_time: %s\n", nt_time_string(ctx, finfo.all_info.out.create_time));
+ d_printf("\taccess_time: %s\n", nt_time_string(ctx, finfo.all_info.out.access_time));
+ d_printf("\twrite_time: %s\n", nt_time_string(ctx, finfo.all_info.out.write_time));
+ d_printf("\tchange_time: %s\n", nt_time_string(ctx, finfo.all_info.out.change_time));
+ d_printf("\tattrib: 0x%x\n", finfo.all_info.out.attrib);
+ d_printf("\talloc_size: %lu\n", (unsigned long)finfo.all_info.out.alloc_size);
+ d_printf("\tsize: %lu\n", (unsigned 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);
+
+ /* 8.3 name if any */
+ finfo.generic.level = RAW_FILEINFO_ALT_NAME_INFO;
+ status = smb_raw_pathinfo(ctx->cli->tree, ctx, &finfo);
+ if (NT_STATUS_IS_OK(status)) {
+ d_printf("\talt_name: %s\n", finfo.alt_name_info.out.fname.s);
+ }
+
+ /* file_id if available */
+ finfo.generic.level = RAW_FILEINFO_INTERNAL_INFORMATION;
+ status = smb_raw_pathinfo(ctx->cli->tree, ctx, &finfo);
+ if (NT_STATUS_IS_OK(status)) {
+ d_printf("\tfile_id %.0f\n",
+ (double)finfo.internal_information.out.file_id);
+ }
+
+ /* the EAs, if any */
+ finfo.generic.level = RAW_FILEINFO_ALL_EAS;
+ status = smb_raw_pathinfo(ctx->cli->tree, ctx, &finfo);
+ if (NT_STATUS_IS_OK(status)) {
+ int i;
+ for (i=0;i<finfo.all_eas.out.num_eas;i++) {
+ d_printf("\tEA[%d] flags=%d len=%d '%s'\n", i,
+ finfo.all_eas.out.eas[i].flags,
+ (int)finfo.all_eas.out.eas[i].value.length,
+ finfo.all_eas.out.eas[i].name.s);
+ }
+ }
+
+ /* streams, if available */
+ finfo.generic.level = RAW_FILEINFO_STREAM_INFO;
+ status = smb_raw_pathinfo(ctx->cli->tree, ctx, &finfo);
+ if (NT_STATUS_IS_OK(status)) {
+ int i;
+ for (i=0;i<finfo.stream_info.out.num_streams;i++) {
+ d_printf("\tstream %d:\n", i);
+ d_printf("\t\tsize %ld\n",
+ (long)finfo.stream_info.out.streams[i].size);
+ d_printf("\t\talloc size %ld\n",
+ (long)finfo.stream_info.out.streams[i].alloc_size);
+ d_printf("\t\tname %s\n", finfo.stream_info.out.streams[i].stream_name.s);
+ }
+ }
+
+ /* dev/inode if available */
+ finfo.generic.level = RAW_FILEINFO_COMPRESSION_INFORMATION;
+ status = smb_raw_pathinfo(ctx->cli->tree, ctx, &finfo);
+ if (NT_STATUS_IS_OK(status)) {
+ d_printf("\tcompressed size %ld\n", (long)finfo.compression_info.out.compressed_size);
+ d_printf("\tformat %ld\n", (long)finfo.compression_info.out.format);
+ d_printf("\tunit_shift %ld\n", (long)finfo.compression_info.out.unit_shift);
+ d_printf("\tchunk_shift %ld\n", (long)finfo.compression_info.out.chunk_shift);
+ d_printf("\tcluster_shift %ld\n", (long)finfo.compression_info.out.cluster_shift);
+ }
+
+ /* shadow copies if available */
+ fnum = smbcli_open(ctx->cli->tree, fname, O_RDONLY, DENY_NONE);
+ if (fnum != -1) {
+ struct smb_shadow_copy info;
+ int i;
+ info.in.file.fnum = fnum;
+ info.in.max_data = ~0;
+ status = smb_raw_shadow_data(ctx->cli->tree, ctx, &info);
+ if (NT_STATUS_IS_OK(status)) {
+ d_printf("\tshadow_copy: %u volumes %u names\n",
+ info.out.num_volumes, info.out.num_names);
+ for (i=0;i<info.out.num_names;i++) {
+ d_printf("\t%s\n", info.out.names[i]);
+ finfo.generic.level = RAW_FILEINFO_ALL_INFO;
+ finfo.generic.in.file.path = talloc_asprintf(ctx, "%s%s",
+ info.out.names[i], fname);
+ status = smb_raw_pathinfo(ctx->cli->tree, ctx, &finfo);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_NOT_FOUND) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ continue;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("%s - %s\n", finfo.generic.in.file.path,
+ nt_errstr(status));
+ return 1;
+ }
+
+ d_printf("\t\tcreate_time: %s\n", nt_time_string(ctx, finfo.all_info.out.create_time));
+ d_printf("\t\twrite_time: %s\n", nt_time_string(ctx, finfo.all_info.out.write_time));
+ d_printf("\t\tchange_time: %s\n", nt_time_string(ctx, finfo.all_info.out.change_time));
+ d_printf("\t\tsize: %lu\n", (unsigned long)finfo.all_info.out.size);
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+/****************************************************************************
+shows EA contents
+****************************************************************************/
+static int cmd_eainfo(struct smbclient_context *ctx, const char **args)
+{
+ char *fname;
+ union smb_fileinfo finfo;
+ NTSTATUS status;
+ int i;
+
+ if (!args[1]) {
+ d_printf("eainfo <filename>\n");
+ return 1;
+ }
+ fname = talloc_strdup(ctx, args[1]);
+
+ finfo.generic.level = RAW_FILEINFO_ALL_EAS;
+ finfo.generic.in.file.path = fname;
+ status = smb_raw_pathinfo(ctx->cli->tree, ctx, &finfo);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("RAW_FILEINFO_ALL_EAS - %s\n", nt_errstr(status));
+ return 1;
+ }
+
+ d_printf("%s has %d EAs\n", fname, finfo.all_eas.out.num_eas);
+
+ for (i=0;i<finfo.all_eas.out.num_eas;i++) {
+ d_printf("\tEA[%d] flags=%d len=%d '%s'\n", i,
+ finfo.all_eas.out.eas[i].flags,
+ (int)finfo.all_eas.out.eas[i].value.length,
+ finfo.all_eas.out.eas[i].name.s);
+ fflush(stdout);
+ dump_data(0,
+ finfo.all_eas.out.eas[i].value.data,
+ finfo.all_eas.out.eas[i].value.length);
+ }
+
+ return 0;
+}
+
+
+/****************************************************************************
+show any ACL on a file
+****************************************************************************/
+static int cmd_acl(struct smbclient_context *ctx, const char **args)
+{
+ char *fname;
+ union smb_fileinfo query;
+ NTSTATUS status;
+ int fnum;
+
+ if (!args[1]) {
+ d_printf("acl <filename>\n");
+ return 1;
+ }
+ fname = talloc_asprintf(ctx, "%s%s", ctx->remote_cur_dir, args[1]);
+
+ fnum = smbcli_nt_create_full(ctx->cli->tree, fname, 0,
+ SEC_STD_READ_CONTROL,
+ 0,
+ NTCREATEX_SHARE_ACCESS_DELETE|
+ NTCREATEX_SHARE_ACCESS_READ|
+ NTCREATEX_SHARE_ACCESS_WRITE,
+ NTCREATEX_DISP_OPEN,
+ 0, 0);
+ if (fnum == -1) {
+ d_printf("%s - %s\n", fname, smbcli_errstr(ctx->cli->tree));
+ return -1;
+ }
+
+ query.query_secdesc.level = RAW_FILEINFO_SEC_DESC;
+ query.query_secdesc.in.file.fnum = fnum;
+ query.query_secdesc.in.secinfo_flags = 0x7;
+
+ status = smb_raw_fileinfo(ctx->cli->tree, ctx, &query);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("%s - %s\n", fname, nt_errstr(status));
+ return 1;
+ }
+
+ NDR_PRINT_DEBUG(security_descriptor, query.query_secdesc.out.sd);
+
+ return 0;
+}
+
+/****************************************************************************
+lookup a name or sid
+****************************************************************************/
+static int cmd_lookup(struct smbclient_context *ctx, const char **args)
+{
+ NTSTATUS status;
+ struct dom_sid *sid;
+
+ if (!args[1]) {
+ d_printf("lookup <sid|name>\n");
+ return 1;
+ }
+
+ sid = dom_sid_parse_talloc(ctx, args[1]);
+ if (sid == NULL) {
+ const char *sidstr;
+ status = smblsa_lookup_name(ctx->cli, args[1], ctx, &sidstr);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("lsa_LookupNames - %s\n", nt_errstr(status));
+ return 1;
+ }
+
+ d_printf("%s\n", sidstr);
+ } else {
+ const char *name;
+ status = smblsa_lookup_sid(ctx->cli, args[1], ctx, &name);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("lsa_LookupSids - %s\n", nt_errstr(status));
+ return 1;
+ }
+
+ d_printf("%s\n", name);
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+show privileges for a user
+****************************************************************************/
+static int cmd_privileges(struct smbclient_context *ctx, const char **args)
+{
+ NTSTATUS status;
+ struct dom_sid *sid;
+ struct lsa_RightSet rights;
+ unsigned i;
+
+ if (!args[1]) {
+ d_printf("privileges <sid|name>\n");
+ return 1;
+ }
+
+ sid = dom_sid_parse_talloc(ctx, args[1]);
+ if (sid == NULL) {
+ const char *sid_str;
+ status = smblsa_lookup_name(ctx->cli, args[1], ctx, &sid_str);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("lsa_LookupNames - %s\n", nt_errstr(status));
+ return 1;
+ }
+ sid = dom_sid_parse_talloc(ctx, sid_str);
+ }
+
+ status = smblsa_sid_privileges(ctx->cli, sid, ctx, &rights);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("lsa_EnumAccountRights - %s\n", nt_errstr(status));
+ return 1;
+ }
+
+ for (i=0;i<rights.count;i++) {
+ d_printf("\t%s\n", rights.names[i].string);
+ }
+
+ return 0;
+}
+
+
+/****************************************************************************
+add privileges for a user
+****************************************************************************/
+static int cmd_addprivileges(struct smbclient_context *ctx, const char **args)
+{
+ NTSTATUS status;
+ struct dom_sid *sid;
+ struct lsa_RightSet rights;
+ int i;
+
+ if (!args[1]) {
+ d_printf("addprivileges <sid|name> <privilege...>\n");
+ return 1;
+ }
+
+ sid = dom_sid_parse_talloc(ctx, args[1]);
+ if (sid == NULL) {
+ const char *sid_str;
+ status = smblsa_lookup_name(ctx->cli, args[1], ctx, &sid_str);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("lsa_LookupNames - %s\n", nt_errstr(status));
+ return 1;
+ }
+ sid = dom_sid_parse_talloc(ctx, sid_str);
+ }
+
+ ZERO_STRUCT(rights);
+ for (i = 2; args[i]; i++) {
+ rights.names = talloc_realloc(ctx, rights.names,
+ struct lsa_StringLarge, rights.count+1);
+ rights.names[rights.count].string = talloc_strdup(ctx, args[i]);
+ rights.count++;
+ }
+
+
+ status = smblsa_sid_add_privileges(ctx->cli, sid, ctx, &rights);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("lsa_AddAccountRights - %s\n", nt_errstr(status));
+ return 1;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+delete privileges for a user
+****************************************************************************/
+static int cmd_delprivileges(struct smbclient_context *ctx, const char **args)
+{
+ NTSTATUS status;
+ struct dom_sid *sid;
+ struct lsa_RightSet rights;
+ int i;
+
+ if (!args[1]) {
+ d_printf("delprivileges <sid|name> <privilege...>\n");
+ return 1;
+ }
+
+ sid = dom_sid_parse_talloc(ctx, args[1]);
+ if (sid == NULL) {
+ const char *sid_str;
+ status = smblsa_lookup_name(ctx->cli, args[1], ctx, &sid_str);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("lsa_LookupNames - %s\n", nt_errstr(status));
+ return 1;
+ }
+ sid = dom_sid_parse_talloc(ctx, sid_str);
+ }
+
+ ZERO_STRUCT(rights);
+ for (i = 2; args[i]; i++) {
+ rights.names = talloc_realloc(ctx, rights.names,
+ struct lsa_StringLarge, rights.count+1);
+ rights.names[rights.count].string = talloc_strdup(ctx, args[i]);
+ rights.count++;
+ }
+
+
+ status = smblsa_sid_del_privileges(ctx->cli, sid, ctx, &rights);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("lsa_RemoveAccountRights - %s\n", nt_errstr(status));
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/****************************************************************************
+****************************************************************************/
+static int cmd_open(struct smbclient_context *ctx, const char **args)
+{
+ char *mask;
+
+ if (!args[1]) {
+ d_printf("open <filename>\n");
+ return 1;
+ }
+ mask = talloc_asprintf(ctx, "%s%s", ctx->remote_cur_dir, args[1]);
+
+ smbcli_open(ctx->cli->tree, mask, O_RDWR, DENY_ALL);
+
+ return 0;
+}
+
+
+/****************************************************************************
+remove a directory
+****************************************************************************/
+static int cmd_rmdir(struct smbclient_context *ctx, const char **args)
+{
+ char *mask;
+
+ if (!args[1]) {
+ d_printf("rmdir <dirname>\n");
+ return 1;
+ }
+ mask = talloc_asprintf(ctx, "%s%s", ctx->remote_cur_dir, args[1]);
+
+ if (NT_STATUS_IS_ERR(smbcli_rmdir(ctx->cli->tree, mask))) {
+ d_printf("%s removing remote directory file %s\n",
+ smbcli_errstr(ctx->cli->tree),mask);
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ UNIX hardlink.
+****************************************************************************/
+static int cmd_link(struct smbclient_context *ctx, const char **args)
+{
+ char *src,*dest;
+
+ if (!(ctx->cli->transport->negotiate.capabilities & CAP_UNIX)) {
+ d_printf("Server doesn't support UNIX CIFS calls.\n");
+ return 1;
+ }
+
+
+ if (!args[1] || !args[2]) {
+ d_printf("link <src> <dest>\n");
+ return 1;
+ }
+
+ src = talloc_asprintf(ctx, "%s%s", ctx->remote_cur_dir, args[1]);
+ dest = talloc_asprintf(ctx, "%s%s", ctx->remote_cur_dir, args[2]);
+
+ if (NT_STATUS_IS_ERR(smbcli_unix_hardlink(ctx->cli->tree, src, dest))) {
+ d_printf("%s linking files (%s -> %s)\n", smbcli_errstr(ctx->cli->tree), src, dest);
+ return 1;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ UNIX symlink.
+****************************************************************************/
+
+static int cmd_symlink(struct smbclient_context *ctx, const char **args)
+{
+ char *src,*dest;
+
+ if (!(ctx->cli->transport->negotiate.capabilities & CAP_UNIX)) {
+ d_printf("Server doesn't support UNIX CIFS calls.\n");
+ return 1;
+ }
+
+ if (!args[1] || !args[2]) {
+ d_printf("symlink <src> <dest>\n");
+ return 1;
+ }
+
+ src = talloc_asprintf(ctx, "%s%s", ctx->remote_cur_dir, args[1]);
+ dest = talloc_asprintf(ctx, "%s%s", ctx->remote_cur_dir, args[2]);
+
+ if (NT_STATUS_IS_ERR(smbcli_unix_symlink(ctx->cli->tree, src, dest))) {
+ d_printf("%s symlinking files (%s -> %s)\n",
+ smbcli_errstr(ctx->cli->tree), src, dest);
+ return 1;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ UNIX chmod.
+****************************************************************************/
+
+static int cmd_chmod(struct smbclient_context *ctx, const char **args)
+{
+ char *src;
+ mode_t mode;
+
+ if (!(ctx->cli->transport->negotiate.capabilities & CAP_UNIX)) {
+ d_printf("Server doesn't support UNIX CIFS calls.\n");
+ return 1;
+ }
+
+ if (!args[1] || !args[2]) {
+ d_printf("chmod mode file\n");
+ return 1;
+ }
+
+ src = talloc_asprintf(ctx, "%s%s", ctx->remote_cur_dir, args[2]);
+
+ mode = (mode_t)strtol(args[1], NULL, 8);
+
+ if (NT_STATUS_IS_ERR(smbcli_unix_chmod(ctx->cli->tree, src, mode))) {
+ d_printf("%s chmod file %s 0%o\n",
+ smbcli_errstr(ctx->cli->tree), src, (mode_t)mode);
+ return 1;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ UNIX chown.
+****************************************************************************/
+
+static int cmd_chown(struct smbclient_context *ctx, const char **args)
+{
+ char *src;
+ uid_t uid;
+ gid_t gid;
+
+ if (!(ctx->cli->transport->negotiate.capabilities & CAP_UNIX)) {
+ d_printf("Server doesn't support UNIX CIFS calls.\n");
+ return 1;
+ }
+
+ if (!args[1] || !args[2] || !args[3]) {
+ d_printf("chown uid gid file\n");
+ return 1;
+ }
+
+ uid = (uid_t)atoi(args[1]);
+ gid = (gid_t)atoi(args[2]);
+ src = talloc_asprintf(ctx, "%s%s", ctx->remote_cur_dir, args[3]);
+
+ if (NT_STATUS_IS_ERR(smbcli_unix_chown(ctx->cli->tree, src, uid, gid))) {
+ d_printf("%s chown file %s uid=%d, gid=%d\n",
+ smbcli_errstr(ctx->cli->tree), src, (int)uid, (int)gid);
+ return 1;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+rename some files
+****************************************************************************/
+static int cmd_rename(struct smbclient_context *ctx, const char **args)
+{
+ char *src,*dest;
+
+ if (!args[1] || !args[2]) {
+ d_printf("rename <src> <dest>\n");
+ return 1;
+ }
+
+ src = talloc_asprintf(ctx, "%s%s", ctx->remote_cur_dir, args[1]);
+ dest = talloc_asprintf(ctx, "%s%s", ctx->remote_cur_dir, args[2]);
+
+ if (NT_STATUS_IS_ERR(smbcli_rename(ctx->cli->tree, src, dest))) {
+ d_printf("%s renaming files\n",smbcli_errstr(ctx->cli->tree));
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/****************************************************************************
+toggle the prompt flag
+****************************************************************************/
+static int cmd_prompt(struct smbclient_context *ctx, const char **args)
+{
+ ctx->prompt = !ctx->prompt;
+ DEBUG(2,("prompting is now %s\n",ctx->prompt?"on":"off"));
+
+ return 1;
+}
+
+
+/****************************************************************************
+set the newer than time
+****************************************************************************/
+static int cmd_newer(struct smbclient_context *ctx, const char **args)
+{
+ struct stat sbuf;
+
+ if (args[1] && (stat(args[1],&sbuf) == 0)) {
+ ctx->newer_than = sbuf.st_mtime;
+ DEBUG(1,("Getting files newer than %s",
+ asctime(localtime(&ctx->newer_than))));
+ } else {
+ ctx->newer_than = 0;
+ }
+
+ if (args[1] && ctx->newer_than == 0) {
+ d_printf("Error setting newer-than time\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+set the archive level
+****************************************************************************/
+static int cmd_archive(struct smbclient_context *ctx, const char **args)
+{
+ if (args[1]) {
+ ctx->archive_level = atoi(args[1]);
+ } else
+ d_printf("Archive level is %d\n",ctx->archive_level);
+
+ return 0;
+}
+
+/****************************************************************************
+toggle the lowercaseflag
+****************************************************************************/
+static int cmd_lowercase(struct smbclient_context *ctx, const char **args)
+{
+ ctx->lowercase = !ctx->lowercase;
+ DEBUG(2,("filename lowercasing is now %s\n",ctx->lowercase?"on":"off"));
+
+ return 0;
+}
+
+
+
+
+/****************************************************************************
+toggle the recurse flag
+****************************************************************************/
+static int cmd_recurse(struct smbclient_context *ctx, const char **args)
+{
+ ctx->recurse = !ctx->recurse;
+ DEBUG(2,("directory recursion is now %s\n",ctx->recurse?"on":"off"));
+
+ return 0;
+}
+
+/****************************************************************************
+toggle the translate flag
+****************************************************************************/
+static int cmd_translate(struct smbclient_context *ctx, const char **args)
+{
+ ctx->translation = !ctx->translation;
+ DEBUG(2,("CR/LF<->LF and print text translation now %s\n",
+ ctx->translation?"on":"off"));
+
+ return 0;
+}
+
+
+/****************************************************************************
+do a printmode command
+****************************************************************************/
+static int cmd_printmode(struct smbclient_context *ctx, const char **args)
+{
+ if (args[1]) {
+ if (strequal(args[1],"text")) {
+ ctx->printmode = 0;
+ } else {
+ if (strequal(args[1],"graphics"))
+ ctx->printmode = 1;
+ else
+ ctx->printmode = atoi(args[1]);
+ }
+ }
+
+ switch(ctx->printmode)
+ {
+ case 0:
+ DEBUG(2,("the printmode is now text\n"));
+ break;
+ case 1:
+ DEBUG(2,("the printmode is now graphics\n"));
+ break;
+ default:
+ DEBUG(2,("the printmode is now %d\n", ctx->printmode));
+ break;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ do the lcd command
+ ****************************************************************************/
+static int cmd_lcd(struct smbclient_context *ctx, const char **args)
+{
+ char d[PATH_MAX];
+
+ if (args[1])
+ chdir(args[1]);
+ DEBUG(2,("the local directory is now %s\n",getcwd(d, PATH_MAX)));
+
+ return 0;
+}
+
+/****************************************************************************
+history
+****************************************************************************/
+static int cmd_history(struct smbclient_context *ctx, const char **args)
+{
+#if defined(HAVE_LIBREADLINE) && defined(HAVE_HISTORY_LIST)
+ HIST_ENTRY **hlist;
+ int i;
+
+ hlist = history_list();
+
+ for (i = 0; hlist && hlist[i]; i++) {
+ DEBUG(0, ("%d: %s\n", i, hlist[i]->line));
+ }
+#else
+ DEBUG(0,("no history without readline support\n"));
+#endif
+
+ return 0;
+}
+
+/****************************************************************************
+ get a file restarting at end of local file
+ ****************************************************************************/
+static int cmd_reget(struct smbclient_context *ctx, const char **args)
+{
+ char *local_name;
+ char *remote_name;
+
+ if (!args[1]) {
+ d_printf("reget <filename>\n");
+ return 1;
+ }
+ remote_name = talloc_asprintf(ctx, "%s\\%s", ctx->remote_cur_dir, args[1]);
+ dos_clean_name(remote_name);
+
+ if (args[2])
+ local_name = talloc_strdup(ctx, args[2]);
+ else
+ local_name = talloc_strdup(ctx, args[1]);
+
+ return do_get(ctx, remote_name, local_name, true);
+}
+
+/****************************************************************************
+ put a file restarting at end of local file
+ ****************************************************************************/
+static int cmd_reput(struct smbclient_context *ctx, const char **args)
+{
+ char *local_name;
+ char *remote_name;
+
+ if (!args[1]) {
+ d_printf("reput <filename>\n");
+ return 1;
+ }
+ local_name = talloc_asprintf(ctx, "%s\\%s", ctx->remote_cur_dir, args[1]);
+
+ if (!file_exist(local_name)) {
+ d_printf("%s does not exist\n", local_name);
+ return 1;
+ }
+
+ if (args[2])
+ remote_name = talloc_strdup(ctx, args[2]);
+ else
+ remote_name = talloc_strdup(ctx, args[1]);
+
+ dos_clean_name(remote_name);
+
+ return do_put(ctx, remote_name, local_name, true);
+}
+
+
+/*
+ return a string representing a share type
+*/
+static const char *share_type_str(uint32_t type)
+{
+ switch (type & 0xF) {
+ case STYPE_DISKTREE:
+ return "Disk";
+ case STYPE_PRINTQ:
+ return "Printer";
+ case STYPE_DEVICE:
+ return "Device";
+ case STYPE_IPC:
+ return "IPC";
+ default:
+ return "Unknown";
+ }
+}
+
+
+/*
+ display a list of shares from a level 1 share enum
+*/
+static void display_share_result(struct srvsvc_NetShareCtr1 *ctr1)
+{
+ int i;
+
+ for (i=0;i<ctr1->count;i++) {
+ struct srvsvc_NetShareInfo1 *info = ctr1->array+i;
+
+ printf("\t%-15s %-10.10s %s\n",
+ info->name,
+ share_type_str(info->type),
+ info->comment);
+ }
+}
+
+
+
+/****************************************************************************
+try and browse available shares on a host
+****************************************************************************/
+static bool browse_host(struct loadparm_context *lp_ctx,
+ struct tevent_context *ev_ctx,
+ const char *query_host)
+{
+ struct dcerpc_pipe *p;
+ char *binding;
+ NTSTATUS status;
+ struct srvsvc_NetShareEnumAll r;
+ struct srvsvc_NetShareInfoCtr info_ctr;
+ uint32_t resume_handle = 0;
+ TALLOC_CTX *mem_ctx = talloc_init("browse_host");
+ struct srvsvc_NetShareCtr1 ctr1;
+ uint32_t totalentries = 0;
+
+ binding = talloc_asprintf(mem_ctx, "ncacn_np:%s", query_host);
+
+ status = dcerpc_pipe_connect(mem_ctx, &p, binding,
+ &ndr_table_srvsvc,
+ cmdline_credentials, ev_ctx,
+ lp_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("Failed to connect to %s - %s\n",
+ binding, nt_errstr(status));
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ info_ctr.level = 1;
+ info_ctr.ctr.ctr1 = &ctr1;
+
+ r.in.server_unc = talloc_asprintf(mem_ctx,"\\\\%s",dcerpc_server_name(p));
+ r.in.info_ctr = &info_ctr;
+ r.in.max_buffer = ~0;
+ r.in.resume_handle = &resume_handle;
+ r.out.resume_handle = &resume_handle;
+ r.out.totalentries = &totalentries;
+ r.out.info_ctr = &info_ctr;
+
+ d_printf("\n\tSharename Type Comment\n");
+ d_printf("\t--------- ---- -------\n");
+
+ do {
+ ZERO_STRUCT(ctr1);
+ status = dcerpc_srvsvc_NetShareEnumAll(p, mem_ctx, &r);
+
+ if (NT_STATUS_IS_OK(status) &&
+ (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA) ||
+ W_ERROR_IS_OK(r.out.result)) &&
+ r.out.info_ctr->ctr.ctr1) {
+ display_share_result(r.out.info_ctr->ctr.ctr1);
+ resume_handle += r.out.info_ctr->ctr.ctr1->count;
+ }
+ } while (NT_STATUS_IS_OK(status) && W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA));
+
+ talloc_free(mem_ctx);
+
+ if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(r.out.result)) {
+ d_printf("Failed NetShareEnumAll %s - %s/%s\n",
+ binding, nt_errstr(status), win_errstr(r.out.result));
+ return false;
+ }
+
+ return false;
+}
+
+/****************************************************************************
+try and browse available connections on a host
+****************************************************************************/
+static bool list_servers(const char *wk_grp)
+{
+ d_printf("REWRITE: list servers not implemented\n");
+ return false;
+}
+
+/* Some constants for completing filename arguments */
+
+#define COMPL_NONE 0 /* No completions */
+#define COMPL_REMOTE 1 /* Complete remote filename */
+#define COMPL_LOCAL 2 /* Complete local filename */
+
+static int cmd_help(struct smbclient_context *ctx, const char **args);
+
+/* This defines the commands supported by this client.
+ * NOTE: The "!" must be the last one in the list because it's fn pointer
+ * field is NULL, and NULL in that field is used in process_tok()
+ * (below) to indicate the end of the list. crh
+ */
+static struct
+{
+ const char *name;
+ int (*fn)(struct smbclient_context *ctx, const char **args);
+ const char *description;
+ char compl_args[2]; /* Completion argument info */
+} commands[] =
+{
+ {"?",cmd_help,"[command] give help on a command",{COMPL_NONE,COMPL_NONE}},
+ {"addprivileges",cmd_addprivileges,"<sid|name> <privilege...> add privileges for a user",{COMPL_NONE,COMPL_NONE}},
+ {"altname",cmd_altname,"<file> show alt name",{COMPL_NONE,COMPL_NONE}},
+ {"acl",cmd_acl,"<file> show file ACL",{COMPL_NONE,COMPL_NONE}},
+ {"allinfo",cmd_allinfo,"<file> show all possible info about a file",{COMPL_NONE,COMPL_NONE}},
+ {"archive",cmd_archive,"<level>\n0=ignore archive bit\n1=only get archive files\n2=only get archive files and reset archive bit\n3=get all files and reset archive bit",{COMPL_NONE,COMPL_NONE}},
+ {"cancel",cmd_rewrite,"<jobid> cancel a print queue entry",{COMPL_NONE,COMPL_NONE}},
+ {"cd",cmd_cd,"[directory] change/report the remote directory",{COMPL_REMOTE,COMPL_NONE}},
+ {"chmod",cmd_chmod,"<src> <mode> chmod a file using UNIX permission",{COMPL_REMOTE,COMPL_REMOTE}},
+ {"chown",cmd_chown,"<src> <uid> <gid> chown a file using UNIX uids and gids",{COMPL_REMOTE,COMPL_REMOTE}},
+ {"del",cmd_del,"<mask> delete all matching files",{COMPL_REMOTE,COMPL_NONE}},
+ {"delprivileges",cmd_delprivileges,"<sid|name> <privilege...> remove privileges for a user",{COMPL_NONE,COMPL_NONE}},
+ {"deltree",cmd_deltree,"<dir> delete a whole directory tree",{COMPL_REMOTE,COMPL_NONE}},
+ {"dir",cmd_dir,"<mask> list the contents of the current directory",{COMPL_REMOTE,COMPL_NONE}},
+ {"du",cmd_du,"<mask> computes the total size of the current directory",{COMPL_REMOTE,COMPL_NONE}},
+ {"eainfo",cmd_eainfo,"<file> show EA contents for a file",{COMPL_NONE,COMPL_NONE}},
+ {"exit",cmd_quit,"logoff the server",{COMPL_NONE,COMPL_NONE}},
+ {"fsinfo",cmd_fsinfo,"query file system info",{COMPL_NONE,COMPL_NONE}},
+ {"get",cmd_get,"<remote name> [local name] get a file",{COMPL_REMOTE,COMPL_LOCAL}},
+ {"help",cmd_help,"[command] give help on a command",{COMPL_NONE,COMPL_NONE}},
+ {"history",cmd_history,"displays the command history",{COMPL_NONE,COMPL_NONE}},
+ {"lcd",cmd_lcd,"[directory] change/report the local current working directory",{COMPL_LOCAL,COMPL_NONE}},
+ {"link",cmd_link,"<src> <dest> create a UNIX hard link",{COMPL_REMOTE,COMPL_REMOTE}},
+ {"lookup",cmd_lookup,"<sid|name> show SID for name or name for SID",{COMPL_NONE,COMPL_NONE}},
+ {"lowercase",cmd_lowercase,"toggle lowercasing of filenames for get",{COMPL_NONE,COMPL_NONE}},
+ {"ls",cmd_dir,"<mask> list the contents of the current directory",{COMPL_REMOTE,COMPL_NONE}},
+ {"mask",cmd_select,"<mask> mask all filenames against this",{COMPL_REMOTE,COMPL_NONE}},
+ {"md",cmd_mkdir,"<directory> make a directory",{COMPL_NONE,COMPL_NONE}},
+ {"mget",cmd_mget,"<mask> get all the matching files",{COMPL_REMOTE,COMPL_NONE}},
+ {"mkdir",cmd_mkdir,"<directory> make a directory",{COMPL_NONE,COMPL_NONE}},
+ {"more",cmd_more,"<remote name> view a remote file with your pager",{COMPL_REMOTE,COMPL_NONE}},
+ {"mput",cmd_mput,"<mask> put all matching files",{COMPL_REMOTE,COMPL_NONE}},
+ {"newer",cmd_newer,"<file> only mget files newer than the specified local file",{COMPL_LOCAL,COMPL_NONE}},
+ {"open",cmd_open,"<mask> open a file",{COMPL_REMOTE,COMPL_NONE}},
+ {"privileges",cmd_privileges,"<user> show privileges for a user",{COMPL_NONE,COMPL_NONE}},
+ {"print",cmd_print,"<file name> print a file",{COMPL_NONE,COMPL_NONE}},
+ {"printmode",cmd_printmode,"<graphics or text> set the print mode",{COMPL_NONE,COMPL_NONE}},
+ {"prompt",cmd_prompt,"toggle prompting for filenames for mget and mput",{COMPL_NONE,COMPL_NONE}},
+ {"put",cmd_put,"<local name> [remote name] put a file",{COMPL_LOCAL,COMPL_REMOTE}},
+ {"pwd",cmd_pwd,"show current remote directory (same as 'cd' with no args)",{COMPL_NONE,COMPL_NONE}},
+ {"q",cmd_quit,"logoff the server",{COMPL_NONE,COMPL_NONE}},
+ {"queue",cmd_rewrite,"show the print queue",{COMPL_NONE,COMPL_NONE}},
+ {"quit",cmd_quit,"logoff the server",{COMPL_NONE,COMPL_NONE}},
+ {"rd",cmd_rmdir,"<directory> remove a directory",{COMPL_NONE,COMPL_NONE}},
+ {"recurse",cmd_recurse,"toggle directory recursion for mget and mput",{COMPL_NONE,COMPL_NONE}},
+ {"reget",cmd_reget,"<remote name> [local name] get a file restarting at end of local file",{COMPL_REMOTE,COMPL_LOCAL}},
+ {"rename",cmd_rename,"<src> <dest> rename some files",{COMPL_REMOTE,COMPL_REMOTE}},
+ {"reput",cmd_reput,"<local name> [remote name] put a file restarting at end of remote file",{COMPL_LOCAL,COMPL_REMOTE}},
+ {"rm",cmd_del,"<mask> delete all matching files",{COMPL_REMOTE,COMPL_NONE}},
+ {"rmdir",cmd_rmdir,"<directory> remove a directory",{COMPL_NONE,COMPL_NONE}},
+ {"symlink",cmd_symlink,"<src> <dest> create a UNIX symlink",{COMPL_REMOTE,COMPL_REMOTE}},
+ {"translate",cmd_translate,"toggle text translation for printing",{COMPL_NONE,COMPL_NONE}},
+
+ /* Yes, this must be here, see crh's comment above. */
+ {"!",NULL,"run a shell command on the local system",{COMPL_NONE,COMPL_NONE}},
+ {NULL,NULL,NULL,{COMPL_NONE,COMPL_NONE}}
+};
+
+
+/*******************************************************************
+ lookup a command string in the list of commands, including
+ abbreviations
+ ******************************************************************/
+static int process_tok(const char *tok)
+{
+ int i = 0, matches = 0;
+ int cmd=0;
+ int tok_len = strlen(tok);
+
+ while (commands[i].fn != NULL) {
+ if (strequal(commands[i].name,tok)) {
+ matches = 1;
+ cmd = i;
+ break;
+ } else if (strncasecmp(commands[i].name, tok, tok_len) == 0) {
+ matches++;
+ cmd = i;
+ }
+ i++;
+ }
+
+ if (matches == 0)
+ return(-1);
+ else if (matches == 1)
+ return(cmd);
+ else
+ return(-2);
+}
+
+/****************************************************************************
+help
+****************************************************************************/
+static int cmd_help(struct smbclient_context *ctx, const char **args)
+{
+ int i=0,j;
+
+ if (args[1]) {
+ if ((i = process_tok(args[1])) >= 0)
+ d_printf("HELP %s:\n\t%s\n\n",commands[i].name,commands[i].description);
+ } else {
+ while (commands[i].description) {
+ for (j=0; commands[i].description && (j<5); j++) {
+ d_printf("%-15s",commands[i].name);
+ i++;
+ }
+ d_printf("\n");
+ }
+ }
+ return 0;
+}
+
+static int process_line(struct smbclient_context *ctx, const char *cline);
+/****************************************************************************
+process a -c command string
+****************************************************************************/
+static int process_command_string(struct smbclient_context *ctx, const char *cmd)
+{
+ char **lines;
+ int i, rc = 0;
+
+ lines = str_list_make(NULL, cmd, ";");
+ for (i = 0; lines[i]; i++) {
+ rc |= process_line(ctx, lines[i]);
+ }
+ talloc_free(lines);
+
+ return rc;
+}
+
+#define MAX_COMPLETIONS 100
+
+typedef struct {
+ char *dirmask;
+ char **matches;
+ int count, samelen;
+ const char *text;
+ int len;
+} completion_remote_t;
+
+static void completion_remote_filter(struct clilist_file_info *f, const char *mask, void *state)
+{
+ completion_remote_t *info = (completion_remote_t *)state;
+
+ if ((info->count < MAX_COMPLETIONS - 1) && (strncmp(info->text, f->name, info->len) == 0) && (!ISDOT(f->name)) && (!ISDOTDOT(f->name))) {
+ if ((info->dirmask[0] == 0) && !(f->attrib & FILE_ATTRIBUTE_DIRECTORY))
+ info->matches[info->count] = strdup(f->name);
+ else {
+ char *tmp;
+
+ if (info->dirmask[0] != 0)
+ tmp = talloc_asprintf(NULL, "%s/%s", info->dirmask, f->name);
+ else
+ tmp = talloc_strdup(NULL, f->name);
+
+ if (f->attrib & FILE_ATTRIBUTE_DIRECTORY)
+ tmp = talloc_append_string(NULL, tmp, "/");
+ info->matches[info->count] = tmp;
+ }
+ if (info->matches[info->count] == NULL)
+ return;
+ if (f->attrib & FILE_ATTRIBUTE_DIRECTORY)
+ smb_readline_ca_char(0);
+
+ if (info->count == 1)
+ info->samelen = strlen(info->matches[info->count]);
+ else
+ while (strncmp(info->matches[info->count], info->matches[info->count-1], info->samelen) != 0)
+ info->samelen--;
+ info->count++;
+ }
+}
+
+static char **remote_completion(const char *text, int len)
+{
+ char *dirmask;
+ int i;
+ completion_remote_t info;
+
+ info.samelen = len;
+ info.text = text;
+ info.len = len;
+
+ if (len >= PATH_MAX)
+ return(NULL);
+
+ info.matches = malloc_array_p(char *, MAX_COMPLETIONS);
+ if (!info.matches) return NULL;
+ info.matches[0] = NULL;
+
+ for (i = len-1; i >= 0; i--)
+ if ((text[i] == '/') || (text[i] == '\\'))
+ break;
+ info.text = text+i+1;
+ info.samelen = info.len = len-i-1;
+
+ if (i > 0) {
+ info.dirmask = talloc_strndup(NULL, text, i+1);
+ info.dirmask[i+1] = 0;
+ asprintf(&dirmask, "%s%*s*", rl_ctx->remote_cur_dir, i-1, text);
+ } else
+ asprintf(&dirmask, "%s*", rl_ctx->remote_cur_dir);
+
+ if (smbcli_list(rl_ctx->cli->tree, dirmask,
+ FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN,
+ completion_remote_filter, &info) < 0)
+ goto cleanup;
+
+ if (info.count == 2)
+ info.matches[0] = strdup(info.matches[1]);
+ else {
+ info.matches[0] = malloc_array_p(char, info.samelen+1);
+ if (!info.matches[0])
+ goto cleanup;
+ strncpy(info.matches[0], info.matches[1], info.samelen);
+ info.matches[0][info.samelen] = 0;
+ }
+ info.matches[info.count] = NULL;
+ return info.matches;
+
+cleanup:
+ for (i = 0; i < info.count; i++)
+ free(info.matches[i]);
+ free(info.matches);
+ return NULL;
+}
+
+static char **completion_fn(const char *text, int start, int end)
+{
+ smb_readline_ca_char(' ');
+
+ if (start) {
+ const char *buf, *sp;
+ int i;
+ char compl_type;
+
+ buf = smb_readline_get_line_buffer();
+ if (buf == NULL)
+ return NULL;
+
+ sp = strchr(buf, ' ');
+ if (sp == NULL)
+ return NULL;
+
+ for (i = 0; commands[i].name; i++)
+ if ((strncmp(commands[i].name, text, sp - buf) == 0) && (commands[i].name[sp - buf] == 0))
+ break;
+ if (commands[i].name == NULL)
+ return NULL;
+
+ while (*sp == ' ')
+ sp++;
+
+ if (sp == (buf + start))
+ compl_type = commands[i].compl_args[0];
+ else
+ compl_type = commands[i].compl_args[1];
+
+ if (compl_type == COMPL_REMOTE)
+ return remote_completion(text, end - start);
+ else /* fall back to local filename completion */
+ return NULL;
+ } else {
+ char **matches;
+ int i, len, samelen = 0, count=1;
+
+ matches = malloc_array_p(char *, MAX_COMPLETIONS);
+ if (!matches) return NULL;
+ matches[0] = NULL;
+
+ len = strlen(text);
+ for (i=0;commands[i].fn && count < MAX_COMPLETIONS-1;i++) {
+ if (strncmp(text, commands[i].name, len) == 0) {
+ matches[count] = strdup(commands[i].name);
+ if (!matches[count])
+ goto cleanup;
+ if (count == 1)
+ samelen = strlen(matches[count]);
+ else
+ while (strncmp(matches[count], matches[count-1], samelen) != 0)
+ samelen--;
+ count++;
+ }
+ }
+
+ switch (count) {
+ case 0: /* should never happen */
+ case 1:
+ goto cleanup;
+ case 2:
+ matches[0] = strdup(matches[1]);
+ break;
+ default:
+ matches[0] = malloc_array_p(char, samelen+1);
+ if (!matches[0])
+ goto cleanup;
+ strncpy(matches[0], matches[1], samelen);
+ matches[0][samelen] = 0;
+ }
+ matches[count] = NULL;
+ return matches;
+
+cleanup:
+ count--;
+ while (count >= 0) {
+ free(matches[count]);
+ count--;
+ }
+ free(matches);
+ return NULL;
+ }
+}
+
+/****************************************************************************
+make sure we swallow keepalives during idle time
+****************************************************************************/
+static void readline_callback(void)
+{
+ static time_t last_t;
+ time_t t;
+
+ t = time(NULL);
+
+ if (t - last_t < 5) return;
+
+ last_t = t;
+
+ smbcli_transport_process(rl_ctx->cli->transport);
+
+ if (rl_ctx->cli->tree) {
+ smbcli_chkpath(rl_ctx->cli->tree, "\\");
+ }
+}
+
+static int process_line(struct smbclient_context *ctx, const char *cline)
+{
+ const char **args;
+ int i;
+
+ /* and get the first part of the command */
+ args = str_list_make_shell(ctx, cline, NULL);
+ if (!args || !args[0])
+ return 0;
+
+ if ((i = process_tok(args[0])) >= 0) {
+ i = commands[i].fn(ctx, args);
+ } else if (i == -2) {
+ d_printf("%s: command abbreviation ambiguous\n",args[0]);
+ } else {
+ d_printf("%s: command not found\n",args[0]);
+ }
+
+ talloc_free(args);
+
+ return i;
+}
+
+/****************************************************************************
+process commands on stdin
+****************************************************************************/
+static int process_stdin(struct smbclient_context *ctx)
+{
+ int rc = 0;
+ while (1) {
+ /* display a prompt */
+ char *the_prompt = talloc_asprintf(ctx, "smb: %s> ", ctx->remote_cur_dir);
+ char *cline = smb_readline(the_prompt, readline_callback, completion_fn);
+ talloc_free(the_prompt);
+
+ if (!cline) break;
+
+ /* special case - first char is ! */
+ if (*cline == '!') {
+ system(cline + 1);
+ free(cline);
+ continue;
+ }
+
+ rc |= process_command_string(ctx, cline);
+ free(cline);
+
+ }
+
+ return rc;
+}
+
+
+/*****************************************************
+return a connection to a server
+*******************************************************/
+static bool do_connect(struct smbclient_context *ctx,
+ struct tevent_context *ev_ctx,
+ struct resolve_context *resolve_ctx,
+ const char *specified_server, const char **ports,
+ const char *specified_share,
+ const char *socket_options,
+ struct cli_credentials *cred,
+ struct smbcli_options *options,
+ struct smbcli_session_options *session_options,
+ struct smb_iconv_convenience *iconv_convenience,
+ struct gensec_settings *gensec_settings)
+{
+ NTSTATUS status;
+ char *server, *share;
+
+ rl_ctx = ctx; /* Ugly hack */
+
+ if (strncmp(specified_share, "\\\\", 2) == 0 ||
+ strncmp(specified_share, "//", 2) == 0) {
+ smbcli_parse_unc(specified_share, ctx, &server, &share);
+ } else {
+ share = talloc_strdup(ctx, specified_share);
+ server = talloc_strdup(ctx, specified_server);
+ }
+
+ ctx->remote_cur_dir = talloc_strdup(ctx, "\\");
+
+ status = smbcli_full_connection(ctx, &ctx->cli, server, ports,
+ share, NULL,
+ socket_options,
+ cred, resolve_ctx,
+ ev_ctx, options, session_options,
+ iconv_convenience,
+ gensec_settings);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("Connection to \\\\%s\\%s failed - %s\n",
+ server, share, nt_errstr(status));
+ talloc_free(ctx);
+ return NULL;
+ }
+
+ return ctx;
+}
+
+/****************************************************************************
+handle a -L query
+****************************************************************************/
+static int do_host_query(struct loadparm_context *lp_ctx,
+ struct tevent_context *ev_ctx,
+ const char *query_host,
+ const char *workgroup)
+{
+ browse_host(lp_ctx, ev_ctx, query_host);
+ list_servers(workgroup);
+ return(0);
+}
+
+
+/****************************************************************************
+handle a message operation
+****************************************************************************/
+static int do_message_op(const char *netbios_name, const char *desthost,
+ const char **destports, const char *destip,
+ int name_type,
+ struct tevent_context *ev_ctx,
+ struct resolve_context *resolve_ctx,
+ struct smbcli_options *options,
+ struct smb_iconv_convenience *iconv_convenience,
+ const char *socket_options)
+{
+ struct nbt_name called, calling;
+ const char *server_name;
+ struct smbcli_state *cli;
+
+ make_nbt_name_client(&calling, netbios_name);
+
+ nbt_choose_called_name(NULL, &called, desthost, name_type);
+
+ server_name = destip ? destip : desthost;
+
+ if (!(cli = smbcli_state_init(NULL)) ||
+ !smbcli_socket_connect(cli, server_name, destports,
+ ev_ctx, resolve_ctx, options,
+ iconv_convenience,
+ socket_options)) {
+ d_printf("Connection to %s failed\n", server_name);
+ return 1;
+ }
+
+ if (!smbcli_transport_establish(cli, &calling, &called)) {
+ d_printf("session request failed\n");
+ talloc_free(cli);
+ return 1;
+ }
+
+ send_message(cli, desthost);
+ talloc_free(cli);
+
+ return 0;
+}
+
+
+/****************************************************************************
+ main program
+****************************************************************************/
+ int main(int argc,char *argv[])
+{
+ const char *base_directory = NULL;
+ const char *dest_ip = NULL;
+ int opt;
+ const char *query_host = NULL;
+ bool message = false;
+ const char *desthost = NULL;
+ poptContext pc;
+ const char *service = NULL;
+ int port = 0;
+ char *p;
+ int rc = 0;
+ int name_type = 0x20;
+ TALLOC_CTX *mem_ctx;
+ struct tevent_context *ev_ctx;
+ struct smbclient_context *ctx;
+ const char *cmdstr = NULL;
+ struct smbcli_options smb_options;
+ struct smbcli_session_options smb_session_options;
+
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+
+ { "message", 'M', POPT_ARG_STRING, NULL, 'M', "Send message", "HOST" },
+ { "ip-address", 'I', POPT_ARG_STRING, NULL, 'I', "Use this IP to connect to", "IP" },
+ { "stderr", 'E', POPT_ARG_NONE, NULL, 'E', "Write messages to stderr instead of stdout" },
+ { "list", 'L', POPT_ARG_STRING, NULL, 'L', "Get a list of shares available on a host", "HOST" },
+ { "directory", 'D', POPT_ARG_STRING, NULL, 'D', "Start from directory", "DIR" },
+ { "command", 'c', POPT_ARG_STRING, &cmdstr, 'c', "Execute semicolon separated commands" },
+ { "send-buffer", 'b', POPT_ARG_INT, NULL, 'b', "Changes the transmit/send buffer", "BYTES" },
+ { "port", 'p', POPT_ARG_INT, &port, 'p', "Port to connect to", "PORT" },
+ POPT_COMMON_SAMBA
+ POPT_COMMON_CONNECTION
+ POPT_COMMON_CREDENTIALS
+ POPT_COMMON_VERSION
+ { NULL }
+ };
+
+ mem_ctx = talloc_init("client.c/main");
+ if (!mem_ctx) {
+ d_printf("\nclient.c: Not enough memory\n");
+ exit(1);
+ }
+
+ ctx = talloc_zero(mem_ctx, struct smbclient_context);
+ ctx->io_bufsize = 64512;
+
+ pc = poptGetContext("smbclient", argc, (const char **) argv, long_options, 0);
+ poptSetOtherOptionHelp(pc, "[OPTIONS] service <password>");
+
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case 'M':
+ /* Messages are sent to NetBIOS name type 0x3
+ * (Messenger Service). Make sure we default
+ * to port 139 instead of port 445. srl,crh
+ */
+ name_type = 0x03;
+ desthost = strdup(poptGetOptArg(pc));
+ if( 0 == port ) port = 139;
+ message = true;
+ break;
+ case 'I':
+ dest_ip = poptGetOptArg(pc);
+ break;
+ case 'L':
+ query_host = strdup(poptGetOptArg(pc));
+ break;
+ case 'D':
+ base_directory = strdup(poptGetOptArg(pc));
+ break;
+ case 'b':
+ ctx->io_bufsize = MAX(1, atoi(poptGetOptArg(pc)));
+ break;
+ }
+ }
+
+ gensec_init(cmdline_lp_ctx);
+
+ if(poptPeekArg(pc)) {
+ char *s = strdup(poptGetArg(pc));
+
+ /* Convert any '/' characters in the service name to '\' characters */
+ string_replace(s, '/','\\');
+
+ service = s;
+
+ if (count_chars(s,'\\') < 3) {
+ d_printf("\n%s: Not enough '\\' characters in service\n",s);
+ poptPrintUsage(pc, stderr, 0);
+ exit(1);
+ }
+ }
+
+ if (poptPeekArg(pc)) {
+ cli_credentials_set_password(cmdline_credentials, poptGetArg(pc), CRED_SPECIFIED);
+ }
+
+ /*init_names(); */
+
+ if (!query_host && !service && !message) {
+ poptPrintUsage(pc, stderr, 0);
+ exit(1);
+ }
+
+ poptFreeContext(pc);
+
+ lp_smbcli_options(cmdline_lp_ctx, &smb_options);
+ lp_smbcli_session_options(cmdline_lp_ctx, &smb_session_options);
+
+ ev_ctx = s4_event_context_init(talloc_autofree_context());
+
+ DEBUG( 3, ( "Client started (version %s).\n", SAMBA_VERSION_STRING ) );
+
+ if (query_host && (p=strchr_m(query_host,'#'))) {
+ *p = 0;
+ p++;
+ sscanf(p, "%x", &name_type);
+ }
+
+ if (query_host) {
+ rc = do_host_query(cmdline_lp_ctx, ev_ctx, query_host,
+ lp_workgroup(cmdline_lp_ctx));
+ return rc;
+ }
+
+ if (message) {
+ rc = do_message_op(lp_netbios_name(cmdline_lp_ctx), desthost,
+ lp_smb_ports(cmdline_lp_ctx), dest_ip,
+ name_type, ev_ctx,
+ lp_resolve_context(cmdline_lp_ctx),
+ &smb_options, lp_iconv_convenience(cmdline_lp_ctx),
+ lp_socket_options(cmdline_lp_ctx));
+ return rc;
+ }
+
+ if (!do_connect(ctx, ev_ctx, lp_resolve_context(cmdline_lp_ctx),
+ desthost, lp_smb_ports(cmdline_lp_ctx), service,
+ lp_socket_options(cmdline_lp_ctx),
+ cmdline_credentials, &smb_options, &smb_session_options,
+ lp_iconv_convenience(cmdline_lp_ctx),
+ lp_gensec_settings(ctx, cmdline_lp_ctx)))
+ return 1;
+
+ if (base_directory)
+ do_cd(ctx, base_directory);
+
+ if (cmdstr) {
+ rc = process_command_string(ctx, cmdstr);
+ } else {
+ rc = process_stdin(ctx);
+ }
+
+ talloc_free(mem_ctx);
+
+ return rc;
+}
diff --git a/source4/client/config.mk b/source4/client/config.mk
new file mode 100644
index 0000000000..877544a09a
--- /dev/null
+++ b/source4/client/config.mk
@@ -0,0 +1,36 @@
+# client subsystem
+
+#################################
+# Start BINARY smbclient
+[BINARY::smbclient]
+INSTALLDIR = BINDIR
+PRIVATE_DEPENDENCIES = \
+ LIBSAMBA-HOSTCONFIG \
+ SMBREADLINE \
+ LIBSAMBA-UTIL \
+ LIBCLI_SMB \
+ RPC_NDR_SRVSVC \
+ LIBCLI_LSA \
+ LIBPOPT \
+ POPT_SAMBA \
+ POPT_CREDENTIALS \
+ LIBCLI_RAW
+# End BINARY smbclient
+#################################
+
+smbclient_OBJ_FILES = $(clientsrcdir)/client.o
+
+#################################
+# Start BINARY cifsdd
+[BINARY::cifsdd]
+INSTALLDIR = BINDIR
+PRIVATE_DEPENDENCIES = \
+ LIBSAMBA-HOSTCONFIG \
+ LIBCLI_SMB \
+ LIBPOPT \
+ POPT_SAMBA \
+ POPT_CREDENTIALS
+# End BINARY sdd
+#################################
+
+cifsdd_OBJ_FILES = $(addprefix $(clientsrcdir)/, cifsdd.o cifsddio.o)
diff --git a/source4/client/mount.cifs.c b/source4/client/mount.cifs.c
new file mode 100644
index 0000000000..899c90cefd
--- /dev/null
+++ b/source4/client/mount.cifs.c
@@ -0,0 +1,559 @@
+#define _GNU_SOURCE
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <getopt.h>
+#include <errno.h>
+#include <netdb.h>
+#include <string.h>
+#include <mntent.h>
+
+#define MOUNT_CIFS_VERSION "1"
+
+extern char *getusername(void);
+
+char * thisprogram;
+int verboseflag = 0;
+static int got_password = 0;
+static int got_user = 0;
+static int got_domain = 0;
+static int got_ip = 0;
+static int got_unc = 0;
+static int got_uid = 0;
+static int got_gid = 0;
+static char * user_name = NULL;
+char * mountpassword = NULL;
+
+
+void mount_cifs_usage()
+{
+ printf("\nUsage: %s remotetarget dir\n", thisprogram);
+ printf("\nMount the remotetarget, specified as either a UNC name or ");
+ printf(" CIFS URL, to the local directory, dir.\n");
+
+ exit(1);
+}
+
+/* caller frees username if necessary */
+char * getusername() {
+ char *username = NULL;
+ struct passwd *password = getpwuid(getuid());
+
+ if (password) {
+ username = password->pw_name;
+ }
+ return username;
+}
+
+char * parse_cifs_url(unc_name)
+{
+ printf("\ncifs url %s\n",unc_name);
+}
+
+int parse_options(char * options)
+{
+ char * data;
+ char * value = 0;
+
+ if (!options)
+ return 1;
+
+ while ((data = strsep(&options, ",")) != NULL) {
+ if (!*data)
+ continue;
+ if ((value = strchr(data, '=')) != NULL) {
+ *value++ = '\0';
+ }
+ if (strncmp(data, "user", 4) == 0) {
+ if (!value || !*value) {
+ printf("invalid or missing username\n");
+ return 1; /* needs_arg; */
+ }
+ if (strnlen(value, 260) < 260) {
+ got_user=1;
+ /* BB add check for format user%pass */
+ /* if(strchr(username%passw) got_password = 1) */
+ } else {
+ printf("username too long\n");
+ return 1;
+ }
+ } else if (strncmp(data, "pass", 4) == 0) {
+ if (!value || !*value) {
+ if(got_password) {
+ printf("password specified twice, ignoring second\n");
+ } else
+ got_password = 1;
+ } else if (strnlen(value, 17) < 17) {
+ got_password = 1;
+ } else {
+ printf("password too long\n");
+ return 1;
+ }
+ } else if (strncmp(data, "ip", 2) == 0) {
+ if (!value || !*value) {
+ printf("target ip address argument missing");
+ } else if (strnlen(value, 35) < 35) {
+ got_ip = 1;
+ } else {
+ printf("ip address too long\n");
+ return 1;
+ }
+ } else if ((strncmp(data, "unc", 3) == 0)
+ || (strncmp(data, "target", 6) == 0)
+ || (strncmp(data, "path", 4) == 0)) {
+ if (!value || !*value) {
+ printf("invalid path to network resource\n");
+ return 1; /* needs_arg; */
+ } else if(strnlen(value,5) < 5) {
+ printf("UNC name too short");
+ }
+
+ if (strnlen(value, 300) < 300) {
+ got_unc = 1;
+ if (strncmp(value, "//", 2) == 0) {
+ if(got_unc)
+ printf("unc name specified twice, ignoring second\n");
+ else
+ got_unc = 1;
+ } else if (strncmp(value, "\\\\", 2) != 0) {
+ printf("UNC Path does not begin with // or \\\\ \n");
+ return 1;
+ } else {
+ if(got_unc)
+ printf("unc name specified twice, ignoring second\n");
+ else
+ got_unc = 1;
+ }
+ } else {
+ printf("CIFS: UNC name too long\n");
+ return 1;
+ }
+ } else if ((strncmp(data, "domain", 3) == 0)
+ || (strncmp(data, "workgroup", 5) == 0)) {
+ if (!value || !*value) {
+ printf("CIFS: invalid domain name\n");
+ return 1; /* needs_arg; */
+ }
+ if (strnlen(value, 65) < 65) {
+ got_domain = 1;
+ } else {
+ printf("domain name too long\n");
+ return 1;
+ }
+ } else if (strncmp(data, "uid", 3) == 0) {
+ if (value && *value) {
+ got_uid = 1;
+ }
+ } else if (strncmp(data, "gid", 3) == 0) {
+ if (value && *value) {
+ got_gid = 1;
+ }
+ } /* else if (strnicmp(data, "file_mode", 4) == 0) {
+ if (value && *value) {
+ vol->file_mode =
+ simple_strtoul(value, &value, 0);
+ }
+ } else if (strnicmp(data, "dir_mode", 3) == 0) {
+ if (value && *value) {
+ vol->dir_mode =
+ simple_strtoul(value, &value, 0);
+ }
+ } else if (strnicmp(data, "port", 4) == 0) {
+ if (value && *value) {
+ vol->port =
+ simple_strtoul(value, &value, 0);
+ }
+ } else if (strnicmp(data, "rsize", 5) == 0) {
+ if (value && *value) {
+ vol->rsize =
+ simple_strtoul(value, &value, 0);
+ }
+ } else if (strnicmp(data, "wsize", 5) == 0) {
+ if (value && *value) {
+ vol->wsize =
+ simple_strtoul(value, &value, 0);
+ }
+ } else if (strnicmp(data, "version", 3) == 0) {
+
+ } else if (strnicmp(data, "rw", 2) == 0) {
+
+ } else
+ printf("CIFS: Unknown mount option %s\n",data); */
+ }
+ return 0;
+}
+
+/* Note that caller frees the returned buffer if necessary */
+char * parse_server(char * unc_name)
+{
+ int length = strnlen(unc_name,1024);
+ char * share;
+ char * ipaddress_string = NULL;
+ struct hostent * host_entry;
+ struct in_addr server_ipaddr;
+ int rc,j;
+ char temp[64];
+
+ if(length > 1023) {
+ printf("mount error: UNC name too long");
+ return 0;
+ }
+ if (strncasecmp("cifs://",unc_name,7) == 0)
+ return parse_cifs_url(unc_name+7);
+ if (strncasecmp("smb://",unc_name,6) == 0) {
+ return parse_cifs_url(unc_name+6);
+ }
+
+ if(length < 3) {
+ /* BB add code to find DFS root here */
+ printf("\nMounting the DFS root for domain not implemented yet");
+ return 0;
+ } else {
+ /* BB add support for \\\\ not just // */
+ if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) {
+ printf("mount error: improperly formatted UNC name.");
+ printf(" %s does not begin with \\\\ or //\n",unc_name);
+ return 0;
+ } else {
+ unc_name[0] = '\\';
+ unc_name[1] = '\\';
+ unc_name += 2;
+ if ((share = strchr(unc_name, '/')) ||
+ (share = strchr(unc_name,'\\'))) {
+ *share = 0; /* temporarily terminate the string */
+ share += 1;
+ host_entry = gethostbyname(unc_name);
+ *(share - 1) = '\\'; /* put the slash back */
+/* rc = getipnodebyname(unc_name, AF_INET, AT_ADDRCONFIG ,&rc);*/
+ if(host_entry == NULL) {
+ printf("mount error: could not find target server. TCP name %s not found ", unc_name);
+ printf(" rc = %d\n",rc);
+ return 0;
+ }
+ else {
+ /* BB should we pass an alternate version of the share name as Unicode */
+ /* BB what about ipv6? BB */
+ /* BB add retries with alternate servers in list */
+
+ memcpy(&server_ipaddr.s_addr, host_entry->h_addr, 4);
+
+ ipaddress_string = inet_ntoa(server_ipaddr);
+ if(ipaddress_string == NULL) {
+ printf("mount error: could not get valid ip address for target server\n");
+ return 0;
+ }
+ return ipaddress_string;
+ }
+ } else {
+ /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */
+ printf("Mounting the DFS root for a particular server not implemented yet\n");
+ return 0;
+ }
+ }
+ }
+}
+
+static struct option longopts[] = {
+ { "all", 0, 0, 'a' },
+ { "help", 0, 0, 'h' },
+ { "read-only", 0, 0, 'r' },
+ { "ro", 0, 0, 'r' },
+ { "verbose", 0, 0, 'v' },
+ { "version", 0, 0, 'V' },
+ { "read-write", 0, 0, 'w' },
+ { "rw", 0, 0, 'w' },
+ { "options", 1, 0, 'o' },
+ { "types", 1, 0, 't' },
+ { "replace", 0, 0, 129 },
+ { "after", 0, 0, 130 },
+ { "before", 0, 0, 131 },
+ { "over", 0, 0, 132 },
+ { "move", 0, 0, 133 },
+ { "rsize",1, 0, 136 },
+ { "wsize",1, 0, 137 },
+ { "uid", 1, 0, 138},
+ { "gid", 1, 0, 139},
+ { "uuid",1,0,'U' },
+ { "user",1,0,140},
+ { "username",1,0,140},
+ { "dom",1,0,141},
+ { "domain",1,0,141},
+ { "password",1,0,142},
+ { NULL, 0, 0, 0 }
+};
+
+int main(int argc, char ** argv)
+{
+ int c;
+ int flags = MS_MANDLOCK | MS_MGC_VAL;
+ char * orgoptions = NULL;
+ char * share_name = NULL;
+ char * domain_name = NULL;
+ char * ipaddr = NULL;
+ char * uuid = NULL;
+ char * mountpoint;
+ char * options;
+ int rc,i;
+ int rsize = 0;
+ int wsize = 0;
+ int nomtab = 0;
+ int uid = 0;
+ int gid = 0;
+ int optlen = 0;
+ struct stat statbuf;
+ struct utsname sysinfo;
+ struct mntent mountent;
+ FILE * pmntfile;
+
+ /* setlocale(LC_ALL, "");
+#if defined(LOCALEDIR)
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE); */
+#endif
+
+ if(argc && argv) {
+ thisprogram = argv[0];
+ }
+ if(thisprogram == NULL)
+ thisprogram = "mount.cifs";
+
+ uname(&sysinfo);
+ /* BB add workstation name and domain and pass down */
+/*#ifdef _GNU_SOURCE
+ printf(" node: %s machine: %s\n", sysinfo.nodename,sysinfo.machine);
+#endif*/
+ if(argc < 3)
+ mount_cifs_usage();
+ share_name = argv[1];
+ mountpoint = argv[2];
+ /* add sharename in opts string as unc= parm */
+
+ while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsU:vVwt:",
+ longopts, NULL)) != -1) {
+ switch (c) {
+/* case 'a':
+ ++mount_all;
+ break;
+ case 'f':
+ ++fake;
+ break;
+ case 'F':
+ ++optfork;
+ break; */
+ case 'h': /* help */
+ mount_cifs_usage ();
+ break;
+/* case 'i':
+ external_allowed = 0;
+ break;
+ case 'l':
+ list_with_volumelabel = 1;
+ break;
+ case 'L':
+ volumelabel = optarg;
+ break; */
+ case 'n':
+ ++nomtab;
+ break;
+ case 'o':
+ if (orgoptions) {
+ orgoptions = strcat(orgoptions, ",");
+ orgoptions = strcat(orgoptions,optarg);
+ } else
+ orgoptions = strdup(optarg);
+ break;
+
+/* case 'O':
+ if (test_opts)
+ test_opts = xstrconcat3(test_opts, ",", optarg);
+ else
+ test_opts = xstrdup(optarg);
+ break;*/
+ case 'r': /* mount readonly */
+ flags |= MS_RDONLY;;
+ break;
+ case 'U':
+ uuid = optarg;
+ break;
+ case 'v':
+ ++verboseflag;
+ break;
+/* case 'V':
+ printf ("mount: %s\n", version);
+ exit (0);*/
+ case 'w':
+ flags &= ~MS_RDONLY;;
+ break;
+/* case 0:
+ break;
+
+ case 128:
+ mounttype = MS_BIND;
+ break;
+ case 129:
+ mounttype = MS_REPLACE;
+ break;
+ case 130:
+ mounttype = MS_AFTER;
+ break;
+ case 131:
+ mounttype = MS_BEFORE;
+ break;
+ case 132:
+ mounttype = MS_OVER;
+ break;
+ case 133:
+ mounttype = MS_MOVE;
+ break;
+ case 135:
+ mounttype = (MS_BIND | MS_REC);
+ break; */
+ case 136:
+ rsize = atoi(optarg) ;
+ break;
+ case 137:
+ wsize = atoi(optarg);
+ break;
+ case 138:
+ uid = atoi(optarg);
+ break;
+ case 139:
+ gid = atoi(optarg);
+ break;
+ case 140:
+ got_user = 1;
+ user_name = optarg;
+ break;
+ case 141:
+ domain_name = optarg;
+ break;
+ case 142:
+ got_password = 1;
+ mountpassword = optarg;
+ break;
+ case '?':
+ default:
+ mount_cifs_usage ();
+ }
+ }
+
+ /* canonicalize the path in argv[1]? */
+
+ if(stat (mountpoint, &statbuf)) {
+ printf("mount error: mount point %s does not exist\n",mountpoint);
+ return -1;
+ }
+ if (S_ISDIR(statbuf.st_mode) == 0) {
+ printf("mount error: mount point %s is not a directory\n",mountpoint);
+ return -1;
+ }
+
+ if(geteuid()) {
+ printf("mount error: permission denied, not superuser and cifs.mount not installed SUID\n");
+ return -1;
+ }
+
+ ipaddr = parse_server(share_name);
+/* if(share_name == NULL)
+ return 1; */
+ if (parse_options(strdup(orgoptions)))
+ return 1;
+
+ if(got_user == 0)
+ user_name = getusername();
+
+/* check username for user%password format */
+
+ if(got_password == 0) {
+ if (getenv("PASSWD")) {
+ mountpassword = malloc(33);
+ if(mountpassword) {
+ strncpy(mountpassword,getenv("PASSWD"),32);
+ got_password = 1;
+ }
+/* } else if (getenv("PASSWD_FD") || getenv("PASSWD_FILE")) {
+ get_password_file();
+ got_password = 1;*/ /* BB add missing function */
+ } else {
+ mountpassword = getpass("Password: "); /* BB obsolete */
+ got_password = 1;
+ }
+ }
+ /* FIXME launch daemon (handles dfs name resolution and credential change)
+ remember to clear parms and overwrite password field before launching */
+ if(orgoptions) {
+ optlen = strlen(orgoptions);
+ } else
+ optlen = 0;
+ if(share_name)
+ optlen += strlen(share_name) + 4;
+ if(user_name)
+ optlen += strlen(user_name) + 6;
+ if(ipaddr)
+ optlen += strlen(ipaddr) + 4;
+ if(mountpassword)
+ optlen += strlen(mountpassword) + 6;
+ options = malloc(optlen + 10);
+
+ options[0] = 0;
+ strncat(options,"unc=",4);
+ strcat(options,share_name);
+ if(ipaddr) {
+ strncat(options,",ip=",4);
+ strcat(options,ipaddr);
+ }
+ if(user_name) {
+ strncat(options,",user=",6);
+ strcat(options,user_name);
+ }
+ if(mountpassword) {
+ strncat(options,",pass=",6);
+ strcat(options,mountpassword);
+ }
+ strncat(options,",ver=",5);
+ strcat(options,MOUNT_CIFS_VERSION);
+
+ if(orgoptions) {
+ strcat(options,",");
+ strcat(options,orgoptions);
+ }
+ /* printf("\noptions %s \n",options);*/
+ if(mount(share_name, mountpoint, "cifs", flags, options)) {
+ /* remember to kill daemon on error */
+ switch (errno) {
+ case 0:
+ printf("mount failed but no error number set\n");
+ return 0;
+ case ENODEV:
+ printf("mount error: cifs filesystem not supported by the system\n");
+ break;
+ default:
+ printf("mount error %d = %s",errno,strerror(errno));
+ }
+ printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n");
+ return -1;
+ } else {
+ pmntfile = setmntent(MOUNTED, "a+");
+ if(pmntfile) {
+ mountent.mnt_fsname = share_name;
+ mountent.mnt_dir = mountpoint;
+ mountent.mnt_type = "cifs";
+ mountent.mnt_opts = "";
+ mountent.mnt_freq = 0;
+ mountent.mnt_passno = 0;
+ rc = addmntent(pmntfile,&mountent);
+ endmntent(pmntfile);
+ } else {
+ printf("could not update mount table\n");
+ }
+ }
+ return 0;
+}
+
diff --git a/source4/client/smbmnt.c b/source4/client/smbmnt.c
new file mode 100644
index 0000000000..0d619a88fe
--- /dev/null
+++ b/source4/client/smbmnt.c
@@ -0,0 +1,306 @@
+/*
+ * smbmnt.c
+ *
+ * Copyright (C) 1995-1998 by Paal-Kr. Engstad and Volker Lendecke
+ * extensively modified by Tridge
+ *
+ */
+
+#include "includes.h"
+
+#include <mntent.h>
+#include <sys/utsname.h>
+
+#include <asm/types.h>
+#include <asm/posix_types.h>
+#include <linux/smb.h>
+#include <linux/smb_mount.h>
+#include <asm/unistd.h>
+
+#ifndef MS_MGC_VAL
+/* This may look strange but MS_MGC_VAL is what we are looking for and
+ is what we need from <linux/fs.h> under libc systems and is
+ provided in standard includes on glibc systems. So... We
+ switch on what we need... */
+#include <linux/fs.h>
+#endif
+
+static uid_t mount_uid;
+static gid_t mount_gid;
+static int mount_ro;
+static uint_t mount_fmask;
+static uint_t mount_dmask;
+static int user_mount;
+static char *options;
+
+static void
+help(void)
+{
+ printf("\n");
+ printf("Usage: smbmnt mount-point [options]\n");
+ printf("Version %s\n\n",VERSION);
+ printf("-s share share name on server\n"
+ "-r mount read-only\n"
+ "-u uid mount as uid\n"
+ "-g gid mount as gid\n"
+ "-f mask permission mask for files\n"
+ "-d mask permission mask for directories\n"
+ "-o options name=value, list of options\n"
+ "-h print this help text\n");
+}
+
+static int
+parse_args(int argc, char *argv[], struct smb_mount_data *data, char **share)
+{
+ int opt;
+
+ while ((opt = getopt (argc, argv, "s:u:g:rf:d:o:")) != EOF)
+ {
+ switch (opt)
+ {
+ case 's':
+ *share = optarg;
+ break;
+ case 'u':
+ if (!user_mount) {
+ mount_uid = strtol(optarg, NULL, 0);
+ }
+ break;
+ case 'g':
+ if (!user_mount) {
+ mount_gid = strtol(optarg, NULL, 0);
+ }
+ break;
+ case 'r':
+ mount_ro = 1;
+ break;
+ case 'f':
+ mount_fmask = strtol(optarg, NULL, 8);
+ break;
+ case 'd':
+ mount_dmask = strtol(optarg, NULL, 8);
+ break;
+ case 'o':
+ options = optarg;
+ break;
+ default:
+ return -1;
+ }
+ }
+ return 0;
+
+}
+
+static char *
+fullpath(const char *p)
+{
+ char path[MAXPATHLEN];
+
+ if (strlen(p) > MAXPATHLEN-1) {
+ return NULL;
+ }
+
+ if (realpath(p, path) == NULL) {
+ fprintf(stderr,"Failed to find real path for mount point\n");
+ exit(1);
+ }
+ return strdup(path);
+}
+
+/* Check whether user is allowed to mount on the specified mount point. If it's
+ OK then we change into that directory - this prevents race conditions */
+static int mount_ok(char *mount_point)
+{
+ struct stat st;
+
+ if (chdir(mount_point) != 0) {
+ return -1;
+ }
+
+ if (stat(".", &st) != 0) {
+ return -1;
+ }
+
+ if (!S_ISDIR(st.st_mode)) {
+ errno = ENOTDIR;
+ return -1;
+ }
+
+ if ((getuid() != 0) &&
+ ((getuid() != st.st_uid) ||
+ ((st.st_mode & S_IRWXU) != S_IRWXU))) {
+ errno = EPERM;
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Tries to mount using the appropriate format. For 2.2 the struct,
+ for 2.4 the ascii version. */
+static int
+do_mount(char *share_name, uint_t flags, struct smb_mount_data *data)
+{
+ pstring opts;
+ struct utsname uts;
+ char *release, *major, *minor;
+ char *data1, *data2;
+
+ uname(&uts);
+ release = uts.release;
+ major = strtok(release, ".");
+ minor = strtok(NULL, ".");
+ if (major && minor && atoi(major) == 2 && atoi(minor) < 4) {
+ /* < 2.4, assume struct */
+ data1 = (char *) data;
+ data2 = opts;
+ } else {
+ /* >= 2.4, assume ascii but fall back on struct */
+ data1 = opts;
+ data2 = (char *) data;
+ }
+
+ slprintf(opts, sizeof(opts)-1,
+ "version=7,uid=%d,gid=%d,file_mode=0%o,dir_mode=0%o,%s",
+ data->uid, data->gid, data->file_mode, data->dir_mode,options);
+ if (mount(share_name, ".", "smbfs", flags, data1) == 0)
+ return 0;
+ return mount(share_name, ".", "smbfs", flags, data2);
+}
+
+ int main(int argc, char *argv[])
+{
+ char *mount_point, *share_name = NULL;
+ FILE *mtab;
+ int fd;
+ uint_t flags;
+ struct smb_mount_data data;
+ struct mntent ment;
+
+ memset(&data, 0, sizeof(struct smb_mount_data));
+
+ if (argc < 2) {
+ help();
+ exit(1);
+ }
+
+ if (argv[1][0] == '-') {
+ help();
+ exit(1);
+ }
+
+ if (getuid() != 0) {
+ user_mount = 1;
+ }
+
+ if (geteuid() != 0) {
+ fprintf(stderr, "smbmnt must be installed suid root for direct user mounts (%d,%d)\n", getuid(), geteuid());
+ exit(1);
+ }
+
+ mount_uid = getuid();
+ mount_gid = getgid();
+ mount_fmask = umask(0);
+ umask(mount_fmask);
+ mount_fmask = ~mount_fmask;
+
+ mount_point = fullpath(argv[1]);
+
+ argv += 1;
+ argc -= 1;
+
+ if (mount_ok(mount_point) != 0) {
+ fprintf(stderr, "cannot mount on %s: %s\n",
+ mount_point, strerror(errno));
+ exit(1);
+ }
+
+ data.version = SMB_MOUNT_VERSION;
+
+ /* getuid() gives us the real uid, who may umount the fs */
+ data.mounted_uid = getuid();
+
+ if (parse_args(argc, argv, &data, &share_name) != 0) {
+ help();
+ return -1;
+ }
+
+ data.uid = mount_uid;
+ data.gid = mount_gid;
+ data.file_mode = (S_IRWXU|S_IRWXG|S_IRWXO) & mount_fmask;
+ data.dir_mode = (S_IRWXU|S_IRWXG|S_IRWXO) & mount_dmask;
+
+ if (mount_dmask == 0) {
+ data.dir_mode = data.file_mode;
+ if ((data.dir_mode & S_IRUSR) != 0)
+ data.dir_mode |= S_IXUSR;
+ if ((data.dir_mode & S_IRGRP) != 0)
+ data.dir_mode |= S_IXGRP;
+ if ((data.dir_mode & S_IROTH) != 0)
+ data.dir_mode |= S_IXOTH;
+ }
+
+ flags = MS_MGC_VAL;
+
+ if (mount_ro) flags |= MS_RDONLY;
+
+ if (do_mount(share_name, flags, &data) < 0) {
+ switch (errno) {
+ case ENODEV:
+ fprintf(stderr, "ERROR: smbfs filesystem not supported by the kernel\n");
+ break;
+ default:
+ perror("mount error");
+ }
+ fprintf(stderr, "Please refer to the smbmnt(8) manual page\n");
+ return -1;
+ }
+
+ ment.mnt_fsname = share_name ? share_name : "none";
+ ment.mnt_dir = mount_point;
+ ment.mnt_type = "smbfs";
+ ment.mnt_opts = "";
+ ment.mnt_freq = 0;
+ ment.mnt_passno= 0;
+
+ mount_point = ment.mnt_dir;
+
+ if (mount_point == NULL)
+ {
+ fprintf(stderr, "Mount point too long\n");
+ return -1;
+ }
+
+ if ((fd = open(MOUNTED"~", O_RDWR|O_CREAT|O_EXCL, 0600)) == -1)
+ {
+ fprintf(stderr, "Can't get "MOUNTED"~ lock file");
+ return 1;
+ }
+ close(fd);
+
+ if ((mtab = setmntent(MOUNTED, "a+")) == NULL)
+ {
+ fprintf(stderr, "Can't open " MOUNTED);
+ return 1;
+ }
+
+ if (addmntent(mtab, &ment) == 1)
+ {
+ fprintf(stderr, "Can't write mount entry");
+ return 1;
+ }
+ if (fchmod(fileno(mtab), 0644) == -1)
+ {
+ fprintf(stderr, "Can't set perms on "MOUNTED);
+ return 1;
+ }
+ endmntent(mtab);
+
+ if (unlink(MOUNTED"~") == -1)
+ {
+ fprintf(stderr, "Can't remove "MOUNTED"~");
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/source4/client/smbmount.c b/source4/client/smbmount.c
new file mode 100644
index 0000000000..c219a42f3a
--- /dev/null
+++ b/source4/client/smbmount.c
@@ -0,0 +1,942 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMBFS mount program
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/passwd.h"
+
+#include <mntent.h>
+#include <asm/types.h>
+#include <linux/smb_fs.h>
+
+#define pstrcpy(d,s) safe_strcpy((d),(s),sizeof(pstring)-1)
+#define pstrcat(d,s) safe_strcat((d),(s),sizeof(pstring)-1)
+
+static pstring credentials;
+static pstring my_netbios_name;
+static pstring password;
+static pstring username;
+static pstring workgroup;
+static pstring mpoint;
+static pstring service;
+static pstring options;
+
+static struct in_addr dest_ip;
+static bool have_ip;
+static int smb_port = 0;
+static bool got_user;
+static bool got_pass;
+static uid_t mount_uid;
+static gid_t mount_gid;
+static int mount_ro;
+static uint_t mount_fmask;
+static uint_t mount_dmask;
+static bool use_kerberos;
+/* TODO: Add code to detect smbfs version in kernel */
+static bool status32_smbfs = false;
+
+static void usage(void);
+
+static void exit_parent(int sig)
+{
+ /* parent simply exits when child says go... */
+ exit(0);
+}
+
+static void daemonize(void)
+{
+ int j, status;
+ pid_t child_pid;
+
+ signal( SIGTERM, exit_parent );
+
+ if ((child_pid = sys_fork()) < 0) {
+ DEBUG(0,("could not fork\n"));
+ }
+
+ if (child_pid > 0) {
+ while( 1 ) {
+ j = waitpid( child_pid, &status, 0 );
+ if( j < 0 ) {
+ if( EINTR == errno ) {
+ continue;
+ }
+ status = errno;
+ }
+ break;
+ }
+
+ /* If we get here - the child exited with some error status */
+ if (WIFSIGNALED(status))
+ exit(128 + WTERMSIG(status));
+ else
+ exit(WEXITSTATUS(status));
+ }
+
+ signal( SIGTERM, SIG_DFL );
+ chdir("/");
+}
+
+static void close_our_files(int client_fd)
+{
+ int i;
+ struct rlimit limits;
+
+ getrlimit(RLIMIT_NOFILE,&limits);
+ for (i = 0; i< limits.rlim_max; i++) {
+ if (i == client_fd)
+ continue;
+ close(i);
+ }
+}
+
+static void usr1_handler(int x)
+{
+ return;
+}
+
+
+/*****************************************************
+return a connection to a server
+*******************************************************/
+static struct smbcli_state *do_connection(const char *the_service, bool unicode, int maxprotocol,
+ struct smbcli_session_options session_options)
+{
+ struct smbcli_state *c;
+ struct nmb_name called, calling;
+ char *server_n;
+ struct in_addr ip;
+ pstring server;
+ char *share;
+
+ if (the_service[0] != '\\' || the_service[1] != '\\') {
+ usage();
+ exit(1);
+ }
+
+ pstrcpy(server, the_service+2);
+ share = strchr_m(server,'\\');
+ if (!share) {
+ usage();
+ exit(1);
+ }
+ *share = 0;
+ share++;
+
+ server_n = server;
+
+ make_nmb_name(&calling, my_netbios_name, 0x0);
+ choose_called_name(&called, server, 0x20);
+
+ again:
+ zero_ip(&ip);
+ if (have_ip) ip = dest_ip;
+
+ /* have to open a new connection */
+ if (!(c=smbcli_initialise(NULL)) || (smbcli_set_port(c, smb_port) != smb_port) ||
+ !smbcli_connect(c, server_n, &ip)) {
+ DEBUG(0,("%d: Connection to %s failed\n", sys_getpid(), server_n));
+ if (c) {
+ talloc_free(c);
+ }
+ return NULL;
+ }
+
+ /* SPNEGO doesn't work till we get NTSTATUS error support */
+ /* But it is REQUIRED for kerberos authentication */
+ if(!use_kerberos) c->use_spnego = false;
+
+ /* The kernel doesn't yet know how to sign it's packets */
+ c->sign_info.allow_smb_signing = false;
+
+ /* Use kerberos authentication if specified */
+ c->use_kerberos = use_kerberos;
+
+ if (!smbcli_session_request(c, &calling, &called)) {
+ char *p;
+ DEBUG(0,("%d: session request to %s failed (%s)\n",
+ sys_getpid(), called.name, smbcli_errstr(c)));
+ talloc_free(c);
+ if ((p=strchr_m(called.name, '.'))) {
+ *p = 0;
+ goto again;
+ }
+ if (strcmp(called.name, "*SMBSERVER")) {
+ make_nmb_name(&called , "*SMBSERVER", 0x20);
+ goto again;
+ }
+ return NULL;
+ }
+
+ DEBUG(4,("%d: session request ok\n", sys_getpid()));
+
+ if (!smbcli_negprot(c, unicode, maxprotocol)) {
+ DEBUG(0,("%d: protocol negotiation failed\n", sys_getpid()));
+ talloc_free(c);
+ return NULL;
+ }
+
+ if (!got_pass) {
+ char *pass = getpass("Password: ");
+ if (pass) {
+ pstrcpy(password, pass);
+ }
+ }
+
+ /* This should be right for current smbfs. Future versions will support
+ large files as well as unicode and oplocks. */
+ if (status32_smbfs) {
+ c->capabilities &= ~(CAP_UNICODE | CAP_LARGE_FILES | CAP_NT_SMBS |
+ CAP_NT_FIND | CAP_LEVEL_II_OPLOCKS);
+ }
+ else {
+ c->capabilities &= ~(CAP_UNICODE | CAP_LARGE_FILES | CAP_NT_SMBS |
+ CAP_NT_FIND | CAP_STATUS32 |
+ CAP_LEVEL_II_OPLOCKS);
+ c->force_dos_errors = true;
+ }
+
+ if (!smbcli_session_setup(c, username,
+ password, strlen(password),
+ password, strlen(password),
+ workgroup, session_options)) {
+ /* if a password was not supplied then try again with a
+ null username */
+ if (password[0] || !username[0] ||
+ !smbcli_session_setup(c, "", "", 0, "", 0, workgroup,
+ session_options)) {
+ DEBUG(0,("%d: session setup failed: %s\n",
+ sys_getpid(), smbcli_errstr(c)));
+ talloc_free(c);
+ return NULL;
+ }
+ DEBUG(0,("Anonymous login successful\n"));
+ }
+
+ DEBUG(4,("%d: session setup ok\n", sys_getpid()));
+
+ if (!smbcli_tconX(c, share, "?????", password, strlen(password)+1)) {
+ DEBUG(0,("%d: tree connect failed: %s\n",
+ sys_getpid(), smbcli_errstr(c)));
+ talloc_free(c);
+ return NULL;
+ }
+
+ DEBUG(4,("%d: tconx ok\n", sys_getpid()));
+
+ got_pass = true;
+
+ return c;
+}
+
+
+/****************************************************************************
+unmount smbfs (this is a bailout routine to clean up if a reconnect fails)
+ Code blatently stolen from smbumount.c
+ -mhw-
+****************************************************************************/
+static void smb_umount(const char *mount_point)
+{
+ int fd;
+ struct mntent *mnt;
+ FILE* mtab;
+ FILE* new_mtab;
+
+ /* Programmers Note:
+ This routine only gets called to the scene of a disaster
+ to shoot the survivors... A connection that was working
+ has now apparently failed. We have an active mount point
+ (presumably) that we need to dump. If we get errors along
+ the way - make some noise, but we are already turning out
+ the lights to exit anyways...
+ */
+ if (umount(mount_point) != 0) {
+ DEBUG(0,("%d: Could not umount %s: %s\n",
+ sys_getpid(), mount_point, strerror(errno)));
+ return;
+ }
+
+ if ((fd = open(MOUNTED"~", O_RDWR|O_CREAT|O_EXCL, 0600)) == -1) {
+ DEBUG(0,("%d: Can't get "MOUNTED"~ lock file", sys_getpid()));
+ return;
+ }
+
+ close(fd);
+
+ if ((mtab = setmntent(MOUNTED, "r")) == NULL) {
+ DEBUG(0,("%d: Can't open " MOUNTED ": %s\n",
+ sys_getpid(), strerror(errno)));
+ return;
+ }
+
+#define MOUNTED_TMP MOUNTED".tmp"
+
+ if ((new_mtab = setmntent(MOUNTED_TMP, "w")) == NULL) {
+ DEBUG(0,("%d: Can't open " MOUNTED_TMP ": %s\n",
+ sys_getpid(), strerror(errno)));
+ endmntent(mtab);
+ return;
+ }
+
+ while ((mnt = getmntent(mtab)) != NULL) {
+ if (strcmp(mnt->mnt_dir, mount_point) != 0) {
+ addmntent(new_mtab, mnt);
+ }
+ }
+
+ endmntent(mtab);
+
+ if (fchmod (fileno (new_mtab), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
+ DEBUG(0,("%d: Error changing mode of %s: %s\n",
+ sys_getpid(), MOUNTED_TMP, strerror(errno)));
+ return;
+ }
+
+ endmntent(new_mtab);
+
+ if (rename(MOUNTED_TMP, MOUNTED) < 0) {
+ DEBUG(0,("%d: Cannot rename %s to %s: %s\n",
+ sys_getpid(), MOUNTED, MOUNTED_TMP, strerror(errno)));
+ return;
+ }
+
+ if (unlink(MOUNTED"~") == -1) {
+ DEBUG(0,("%d: Can't remove "MOUNTED"~", sys_getpid()));
+ return;
+ }
+}
+
+
+/*
+ * Call the smbfs ioctl to install a connection socket,
+ * then wait for a signal to reconnect. Note that we do
+ * not exit after open_sockets() or send_login() errors,
+ * as the smbfs mount would then have no way to recover.
+ */
+static void send_fs_socket(struct loadparm_context *lp_ctx,
+ const char *the_service, const char *mount_point, struct smbcli_state *c)
+{
+ int fd, closed = 0, res = 1;
+ pid_t parentpid = getppid();
+ struct smb_conn_opt conn_options;
+ struct smbcli_session_options session_options;
+
+ lp_smbcli_session_options(lp_ctx, &session_options);
+
+ memset(&conn_options, 0, sizeof(conn_options));
+
+ while (1) {
+ if ((fd = open(mount_point, O_RDONLY)) < 0) {
+ DEBUG(0,("mount.smbfs[%d]: can't open %s\n",
+ sys_getpid(), mount_point));
+ break;
+ }
+
+ conn_options.fd = c->fd;
+ conn_options.protocol = c->protocol;
+ conn_options.case_handling = SMB_CASE_DEFAULT;
+ conn_options.max_xmit = c->max_xmit;
+ conn_options.server_uid = c->vuid;
+ conn_options.tid = c->cnum;
+ conn_options.secmode = c->sec_mode;
+ conn_options.rawmode = 0;
+ conn_options.sesskey = c->sesskey;
+ conn_options.maxraw = 0;
+ conn_options.capabilities = c->capabilities;
+ conn_options.serverzone = c->serverzone/60;
+
+ res = ioctl(fd, SMB_IOC_NEWCONN, &conn_options);
+ if (res != 0) {
+ DEBUG(0,("mount.smbfs[%d]: ioctl failed, res=%d\n",
+ sys_getpid(), res));
+ close(fd);
+ break;
+ }
+
+ if (parentpid) {
+ /* Ok... We are going to kill the parent. Now
+ is the time to break the process group... */
+ setsid();
+ /* Send a signal to the parent to terminate */
+ kill(parentpid, SIGTERM);
+ parentpid = 0;
+ }
+
+ close(fd);
+
+ /* This looks wierd but we are only closing the userspace
+ side, the connection has already been passed to smbfs and
+ it has increased the usage count on the socket.
+
+ If we don't do this we will "leak" sockets and memory on
+ each reconnection we have to make. */
+ talloc_free(c);
+ c = NULL;
+
+ if (!closed) {
+ /* redirect stdout & stderr since we can't know that
+ the library functions we use are using DEBUG. */
+ if ( (fd = open("/dev/null", O_WRONLY)) < 0)
+ DEBUG(2,("mount.smbfs: can't open /dev/null\n"));
+ close_our_files(fd);
+ if (fd >= 0) {
+ dup2(fd, STDOUT_FILENO);
+ dup2(fd, STDERR_FILENO);
+ close(fd);
+ }
+
+ /* here we are no longer interactive */
+ set_remote_machine_name("smbmount"); /* sneaky ... */
+ setup_logging("mount.smbfs", DEBUG_STDERR);
+ reopen_logs();
+ DEBUG(0, ("mount.smbfs: entering daemon mode for service %s, pid=%d\n", the_service, sys_getpid()));
+
+ closed = 1;
+ }
+
+ /* Wait for a signal from smbfs ... but don't continue
+ until we actually get a new connection. */
+ while (!c) {
+ CatchSignal(SIGUSR1, &usr1_handler);
+ pause();
+ DEBUG(2,("mount.smbfs[%d]: got signal, getting new socket\n", sys_getpid()));
+ c = do_connection(the_service,
+ lp_unicode(lp_ctx),
+ lp_cli_maxprotocol(lp_ctx),
+ session_options);
+ }
+ }
+
+ smb_umount(mount_point);
+ DEBUG(2,("mount.smbfs[%d]: exit\n", sys_getpid()));
+ exit(1);
+}
+
+
+/**
+ * Mount a smbfs
+ **/
+static void init_mount(struct loadparm_context *lp_ctx)
+{
+ char mount_point[MAXPATHLEN+1];
+ pstring tmp;
+ pstring svc2;
+ struct smbcli_state *c;
+ char *args[20];
+ int i, status;
+ struct smbcli_session_options session_options;
+
+ if (realpath(mpoint, mount_point) == NULL) {
+ fprintf(stderr, "Could not resolve mount point %s\n", mpoint);
+ return;
+ }
+
+ lp_smbcli_session_options(lp_ctx, &session_options);
+
+ c = do_connection(service, lp_unicode(lp_ctx), lp_cli_maxprotocol(lp_ctx),
+ session_options);
+ if (!c) {
+ fprintf(stderr,"SMB connection failed\n");
+ exit(1);
+ }
+
+ /*
+ Set up to return as a daemon child and wait in the parent
+ until the child say it's ready...
+ */
+ daemonize();
+
+ pstrcpy(svc2, service);
+ string_replace(svc2, '\\','/');
+ string_replace(svc2, ' ','_');
+
+ memset(args, 0, sizeof(args[0])*20);
+
+ i=0;
+ args[i++] = "smbmnt";
+
+ args[i++] = mount_point;
+ args[i++] = "-s";
+ args[i++] = svc2;
+
+ if (mount_ro) {
+ args[i++] = "-r";
+ }
+ if (mount_uid) {
+ slprintf(tmp, sizeof(tmp)-1, "%d", mount_uid);
+ args[i++] = "-u";
+ args[i++] = smb_xstrdup(tmp);
+ }
+ if (mount_gid) {
+ slprintf(tmp, sizeof(tmp)-1, "%d", mount_gid);
+ args[i++] = "-g";
+ args[i++] = smb_xstrdup(tmp);
+ }
+ if (mount_fmask) {
+ slprintf(tmp, sizeof(tmp)-1, "0%o", mount_fmask);
+ args[i++] = "-f";
+ args[i++] = smb_xstrdup(tmp);
+ }
+ if (mount_dmask) {
+ slprintf(tmp, sizeof(tmp)-1, "0%o", mount_dmask);
+ args[i++] = "-d";
+ args[i++] = smb_xstrdup(tmp);
+ }
+ if (options) {
+ args[i++] = "-o";
+ args[i++] = options;
+ }
+
+ if (sys_fork() == 0) {
+ char *smbmnt_path;
+
+ asprintf(&smbmnt_path, "%s/smbmnt", dyn_BINDIR);
+
+ if (file_exist(smbmnt_path)) {
+ execv(smbmnt_path, args);
+ fprintf(stderr,
+ "smbfs/init_mount: execv of %s failed. Error was %s.",
+ smbmnt_path, strerror(errno));
+ } else {
+ execvp("smbmnt", args);
+ fprintf(stderr,
+ "smbfs/init_mount: execv of %s failed. Error was %s.",
+ "smbmnt", strerror(errno));
+ }
+ free(smbmnt_path);
+ exit(1);
+ }
+
+ if (waitpid(-1, &status, 0) == -1) {
+ fprintf(stderr,"waitpid failed: Error was %s", strerror(errno) );
+ /* FIXME: do some proper error handling */
+ exit(1);
+ }
+
+ if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
+ fprintf(stderr,"smbmnt failed: %d\n", WEXITSTATUS(status));
+ /* FIXME: do some proper error handling */
+ exit(1);
+ } else if (WIFSIGNALED(status)) {
+ fprintf(stderr, "smbmnt killed by signal %d\n", WTERMSIG(status));
+ exit(1);
+ }
+
+ /* Ok... This is the rubicon for that mount point... At any point
+ after this, if the connections fail and can not be reconstructed
+ for any reason, we will have to unmount the mount point. There
+ is no exit from the next call...
+ */
+ send_fs_socket(lp_ctx, service, mount_point, c);
+}
+
+
+/****************************************************************************
+get a password from a a file or file descriptor
+exit on failure (from smbclient, move to libsmb or shared .c file?)
+****************************************************************************/
+static void get_password_file(void)
+{
+ int fd = -1;
+ char *p;
+ bool close_it = false;
+ pstring spec;
+ char pass[128];
+
+ if ((p = getenv("PASSWD_FD")) != NULL) {
+ pstrcpy(spec, "descriptor ");
+ pstrcat(spec, p);
+ sscanf(p, "%d", &fd);
+ close_it = false;
+ } else if ((p = getenv("PASSWD_FILE")) != NULL) {
+ fd = open(p, O_RDONLY, 0);
+ pstrcpy(spec, p);
+ if (fd < 0) {
+ fprintf(stderr, "Error opening PASSWD_FILE %s: %s\n",
+ spec, strerror(errno));
+ exit(1);
+ }
+ close_it = true;
+ }
+
+ for(p = pass, *p = '\0'; /* ensure that pass is null-terminated */
+ p && p - pass < sizeof(pass);) {
+ switch (read(fd, p, 1)) {
+ case 1:
+ if (*p != '\n' && *p != '\0') {
+ *++p = '\0'; /* advance p, and null-terminate pass */
+ break;
+ }
+ case 0:
+ if (p - pass) {
+ *p = '\0'; /* null-terminate it, just in case... */
+ p = NULL; /* then force the loop condition to become false */
+ break;
+ } else {
+ fprintf(stderr, "Error reading password from file %s: %s\n",
+ spec, "empty password\n");
+ exit(1);
+ }
+
+ default:
+ fprintf(stderr, "Error reading password from file %s: %s\n",
+ spec, strerror(errno));
+ exit(1);
+ }
+ }
+ pstrcpy(password, pass);
+ if (close_it)
+ close(fd);
+}
+
+/****************************************************************************
+get username and password from a credentials file
+exit on failure (from smbclient, move to libsmb or shared .c file?)
+****************************************************************************/
+static void read_credentials_file(char *filename)
+{
+ FILE *auth;
+ fstring buf;
+ uint16_t len = 0;
+ char *ptr, *val, *param;
+
+ if ((auth=sys_fopen(filename, "r")) == NULL)
+ {
+ /* fail if we can't open the credentials file */
+ DEBUG(0,("ERROR: Unable to open credentials file!\n"));
+ exit (-1);
+ }
+
+ while (!feof(auth))
+ {
+ /* get a line from the file */
+ if (!fgets (buf, sizeof(buf), auth))
+ continue;
+ len = strlen(buf);
+
+ if ((len) && (buf[len-1]=='\n'))
+ {
+ buf[len-1] = '\0';
+ len--;
+ }
+ if (len == 0)
+ continue;
+
+ /* break up the line into parameter & value.
+ will need to eat a little whitespace possibly */
+ param = buf;
+ if (!(ptr = strchr (buf, '=')))
+ continue;
+ val = ptr+1;
+ *ptr = '\0';
+
+ /* eat leading white space */
+ while ((*val!='\0') && ((*val==' ') || (*val=='\t')))
+ val++;
+
+ if (strwicmp("password", param) == 0)
+ {
+ pstrcpy(password, val);
+ got_pass = true;
+ }
+ else if (strwicmp("username", param) == 0) {
+ pstrcpy(username, val);
+ }
+
+ memset(buf, 0, sizeof(buf));
+ }
+ fclose(auth);
+}
+
+
+/****************************************************************************
+usage on the program
+****************************************************************************/
+static void usage(void)
+{
+ printf("Usage: mount.smbfs service mountpoint [-o options,...]\n");
+
+ printf("Version %s\n\n",VERSION);
+
+ printf(
+"Options:\n\
+ username=<arg> SMB username\n\
+ password=<arg> SMB password\n\
+ credentials=<filename> file with username/password\n\
+ krb use kerberos (active directory)\n\
+ netbiosname=<arg> source NetBIOS name\n\
+ uid=<arg> mount uid or username\n\
+ gid=<arg> mount gid or groupname\n\
+ port=<arg> remote SMB port number\n\
+ fmask=<arg> file umask\n\
+ dmask=<arg> directory umask\n\
+ debug=<arg> debug level\n\
+ ip=<arg> destination host or IP address\n\
+ workgroup=<arg> workgroup on destination\n\
+ sockopt=<arg> TCP socket options\n\
+ scope=<arg> NetBIOS scope\n\
+ iocharset=<arg> Linux charset (iso8859-1, utf8)\n\
+ codepage=<arg> server codepage (cp850)\n\
+ ttl=<arg> dircache time to live\n\
+ guest don't prompt for a password\n\
+ ro mount read-only\n\
+ rw mount read-write\n\
+\n\
+This command is designed to be run from within /bin/mount by giving\n\
+the option '-t smbfs'. For example:\n\
+ mount -t smbfs -o username=tridge,password=foobar //fjall/test /data/test\n\
+");
+}
+
+
+/****************************************************************************
+ Argument parsing for mount.smbfs interface
+ mount will call us like this:
+ mount.smbfs device mountpoint -o <options>
+
+ <options> is never empty, containing at least rw or ro
+ ****************************************************************************/
+static void parse_mount_smb(int argc, char **argv)
+{
+ int opt;
+ char *opts;
+ char *opteq;
+ extern char *optarg;
+ int val;
+ char *p;
+
+ /* FIXME: This function can silently fail if the arguments are
+ * not in the expected order.
+
+ > The arguments syntax of smbmount 2.2.3a (smbfs of Debian stable)
+ > requires that one gives "-o" before further options like username=...
+ > . Without -o, the username=.. setting is *silently* ignored. I've
+ > spent about an hour trying to find out why I couldn't log in now..
+
+ */
+
+
+ if (argc < 2 || argv[1][0] == '-') {
+ usage();
+ exit(1);
+ }
+
+ pstrcpy(service, argv[1]);
+ pstrcpy(mpoint, argv[2]);
+
+ /* Convert any '/' characters in the service name to
+ '\' characters */
+ string_replace(service, '/','\\');
+ argc -= 2;
+ argv += 2;
+
+ opt = getopt(argc, argv, "o:");
+ if(opt != 'o') {
+ return;
+ }
+
+ options[0] = 0;
+ p = options;
+
+ /*
+ * option parsing from nfsmount.c (util-linux-2.9u)
+ */
+ for (opts = strtok(optarg, ","); opts; opts = strtok(NULL, ",")) {
+ DEBUG(3, ("opts: %s\n", opts));
+ if ((opteq = strchr_m(opts, '='))) {
+ val = atoi(opteq + 1);
+ *opteq = '\0';
+
+ if (!strcmp(opts, "username") ||
+ !strcmp(opts, "logon")) {
+ char *lp;
+ got_user = true;
+ pstrcpy(username,opteq+1);
+ if ((lp=strchr_m(username,'%'))) {
+ *lp = 0;
+ pstrcpy(password,lp+1);
+ got_pass = true;
+ memset(strchr_m(opteq+1,'%')+1,'X',strlen(password));
+ }
+ if ((lp=strchr_m(username,'/'))) {
+ *lp = 0;
+ pstrcpy(workgroup,lp+1);
+ }
+ } else if(!strcmp(opts, "passwd") ||
+ !strcmp(opts, "password")) {
+ pstrcpy(password,opteq+1);
+ got_pass = true;
+ memset(opteq+1,'X',strlen(password));
+ } else if(!strcmp(opts, "credentials")) {
+ pstrcpy(credentials,opteq+1);
+ } else if(!strcmp(opts, "netbiosname")) {
+ pstrcpy(my_netbios_name,opteq+1);
+ } else if(!strcmp(opts, "uid")) {
+ mount_uid = nametouid(opteq+1);
+ } else if(!strcmp(opts, "gid")) {
+ mount_gid = nametogid(opteq+1);
+ } else if(!strcmp(opts, "port")) {
+ smb_port = val;
+ } else if(!strcmp(opts, "fmask")) {
+ mount_fmask = strtol(opteq+1, NULL, 8);
+ } else if(!strcmp(opts, "dmask")) {
+ mount_dmask = strtol(opteq+1, NULL, 8);
+ } else if(!strcmp(opts, "debug")) {
+ DEBUGLEVEL = val;
+ } else if(!strcmp(opts, "ip")) {
+ dest_ip = interpret_addr2(opteq+1);
+ if (is_zero_ip_v4(dest_ip)) {
+ fprintf(stderr,"Can't resolve address %s\n", opteq+1);
+ exit(1);
+ }
+ have_ip = true;
+ } else if(!strcmp(opts, "workgroup")) {
+ pstrcpy(workgroup,opteq+1);
+ } else if(!strcmp(opts, "sockopt")) {
+ lp_set_cmdline("socket options", opteq+1);
+ } else if(!strcmp(opts, "scope")) {
+ lp_set_cmdline("netbios scope", opteq+1);
+ } else {
+ slprintf(p, sizeof(pstring) - (p - options) - 1, "%s=%s,", opts, opteq+1);
+ p += strlen(p);
+ }
+ } else {
+ val = 1;
+ if(!strcmp(opts, "nocaps")) {
+ fprintf(stderr, "Unhandled option: %s\n", opteq+1);
+ exit(1);
+ } else if(!strcmp(opts, "guest")) {
+ *password = '\0';
+ got_pass = true;
+ } else if(!strcmp(opts, "krb")) {
+#ifdef HAVE_KRB5
+
+ use_kerberos = true;
+ if(!status32_smbfs)
+ fprintf(stderr, "Warning: kerberos support will only work for samba servers\n");
+#else
+ fprintf(stderr,"No kerberos support compiled in\n");
+ exit(1);
+#endif
+ } else if(!strcmp(opts, "rw")) {
+ mount_ro = 0;
+ } else if(!strcmp(opts, "ro")) {
+ mount_ro = 1;
+ } else {
+ strncpy(p, opts, sizeof(pstring) - (p - options) - 1);
+ p += strlen(opts);
+ *p++ = ',';
+ *p = 0;
+ }
+ }
+ }
+
+ if (!*service) {
+ usage();
+ exit(1);
+ }
+
+ if (p != options) {
+ *(p-1) = 0; /* remove trailing , */
+ DEBUG(3,("passthrough options '%s'\n", options));
+ }
+}
+
+/****************************************************************************
+ main program
+****************************************************************************/
+ int main(int argc,char *argv[])
+{
+ extern char *optarg;
+ extern int optind;
+ char *p;
+ struct loadparm_context *lp_ctx;
+
+ DEBUGLEVEL = 1;
+
+ /* here we are interactive, even if run from autofs */
+ setup_logging("mount.smbfs",DEBUG_STDERR);
+
+#if 0 /* JRA - Urban says not needed ? */
+ /* CLI_FORCE_ASCII=false makes smbmount negotiate unicode. The default
+ is to not announce any unicode capabilities as current smbfs does
+ not support it. */
+ p = getenv("CLI_FORCE_ASCII");
+ if (p && !strcmp(p, "false"))
+ unsetenv("CLI_FORCE_ASCII");
+ else
+ setenv("CLI_FORCE_ASCII", "true", 1);
+#endif
+
+ if (getenv("USER")) {
+ pstrcpy(username,getenv("USER"));
+
+ if ((p=strchr_m(username,'%'))) {
+ *p = 0;
+ pstrcpy(password,p+1);
+ got_pass = true;
+ memset(strchr_m(getenv("USER"),'%')+1,'X',strlen(password));
+ }
+ strupper(username);
+ }
+
+ if (getenv("PASSWD")) {
+ pstrcpy(password, getenv("PASSWD"));
+ got_pass = true;
+ }
+
+ if (getenv("PASSWD_FD") || getenv("PASSWD_FILE")) {
+ get_password_file();
+ got_pass = true;
+ }
+
+ if (*username == 0 && getenv("LOGNAME")) {
+ pstrcpy(username,getenv("LOGNAME"));
+ }
+
+ lp_ctx = loadparm_init(talloc_autofree_context());
+
+ if (!lp_load(lp_ctx, dyn_CONFIGFILE)) {
+ fprintf(stderr, "Can't load %s - run testparm to debug it\n",
+ lp_config_file());
+ }
+
+ parse_mount_smb(argc, argv);
+
+ if (use_kerberos && !got_user) {
+ got_pass = true;
+ }
+
+ if (*credentials != 0) {
+ read_credentials_file(credentials);
+ }
+
+ DEBUG(3,("mount.smbfs started (version %s)\n", VERSION));
+
+ if (*workgroup == 0) {
+ pstrcpy(workgroup, lp_workgroup());
+ }
+
+ if (!*my_netbios_name) {
+ pstrcpy(my_netbios_name, myhostname());
+ }
+ strupper(my_netbios_name);
+
+ init_mount(lp_ctx);
+ return 0;
+}
diff --git a/source4/client/smbumount.c b/source4/client/smbumount.c
new file mode 100644
index 0000000000..9ea3083a6f
--- /dev/null
+++ b/source4/client/smbumount.c
@@ -0,0 +1,186 @@
+/*
+ * smbumount.c
+ *
+ * Copyright (C) 1995-1998 by Volker Lendecke
+ *
+ */
+
+#include "includes.h"
+
+#include <mntent.h>
+
+#include <asm/types.h>
+#include <asm/posix_types.h>
+#include <linux/smb.h>
+#include <linux/smb_mount.h>
+#include <linux/smb_fs.h>
+
+/* This is a (hopefully) temporary hack due to the fact that
+ sizeof( uid_t ) != sizeof( __kernel_uid_t ) under glibc.
+ This may change in the future and smb.h may get fixed in the
+ future. In the mean time, it's ugly hack time - get over it.
+*/
+#undef SMB_IOC_GETMOUNTUID
+#define SMB_IOC_GETMOUNTUID _IOR('u', 1, __kernel_uid_t)
+
+#ifndef O_NOFOLLOW
+#define O_NOFOLLOW 0400000
+#endif
+
+static void
+usage(void)
+{
+ printf("usage: smbumount mountpoint\n");
+}
+
+static int
+umount_ok(const char *mount_point)
+{
+ /* we set O_NOFOLLOW to prevent users playing games with symlinks to
+ umount filesystems they don't own */
+ int fid = open(mount_point, O_RDONLY|O_NOFOLLOW, 0);
+ __kernel_uid_t mount_uid;
+
+ if (fid == -1) {
+ fprintf(stderr, "Could not open %s: %s\n",
+ mount_point, strerror(errno));
+ return -1;
+ }
+
+ if (ioctl(fid, SMB_IOC_GETMOUNTUID, &mount_uid) != 0) {
+ fprintf(stderr, "%s probably not smb-filesystem\n",
+ mount_point);
+ return -1;
+ }
+
+ if ((getuid() != 0)
+ && (mount_uid != getuid())) {
+ fprintf(stderr, "You are not allowed to umount %s\n",
+ mount_point);
+ return -1;
+ }
+
+ close(fid);
+ return 0;
+}
+
+/* Make a canonical pathname from PATH. Returns a freshly malloced string.
+ It is up the *caller* to ensure that the PATH is sensible. i.e.
+ canonicalize ("/dev/fd0/.") returns "/dev/fd0" even though ``/dev/fd0/.''
+ is not a legal pathname for ``/dev/fd0'' Anything we cannot parse
+ we return unmodified. */
+static char *
+canonicalize (char *path)
+{
+ char *canonical = malloc (PATH_MAX + 1);
+
+ if (!canonical) {
+ fprintf(stderr, "Error! Not enough memory!\n");
+ return NULL;
+ }
+
+ if (strlen(path) > PATH_MAX) {
+ fprintf(stderr, "Mount point string too long\n");
+ return NULL;
+ }
+
+ if (path == NULL)
+ return NULL;
+
+ if (realpath (path, canonical))
+ return canonical;
+
+ strncpy (canonical, path, PATH_MAX);
+ canonical[PATH_MAX] = '\0';
+ return canonical;
+}
+
+
+int
+main(int argc, char *argv[])
+{
+ int fd;
+ char* mount_point;
+ struct mntent *mnt;
+ FILE* mtab;
+ FILE* new_mtab;
+
+ if (argc != 2) {
+ usage();
+ exit(1);
+ }
+
+ if (geteuid() != 0) {
+ fprintf(stderr, "smbumount must be installed suid root\n");
+ exit(1);
+ }
+
+ mount_point = canonicalize(argv[1]);
+
+ if (mount_point == NULL)
+ {
+ exit(1);
+ }
+
+ if (umount_ok(mount_point) != 0) {
+ exit(1);
+ }
+
+ if (umount(mount_point) != 0) {
+ fprintf(stderr, "Could not umount %s: %s\n",
+ mount_point, strerror(errno));
+ exit(1);
+ }
+
+ if ((fd = open(MOUNTED"~", O_RDWR|O_CREAT|O_EXCL, 0600)) == -1)
+ {
+ fprintf(stderr, "Can't get "MOUNTED"~ lock file");
+ return 1;
+ }
+ close(fd);
+
+ if ((mtab = setmntent(MOUNTED, "r")) == NULL) {
+ fprintf(stderr, "Can't open " MOUNTED ": %s\n",
+ strerror(errno));
+ return 1;
+ }
+
+#define MOUNTED_TMP MOUNTED".tmp"
+
+ if ((new_mtab = setmntent(MOUNTED_TMP, "w")) == NULL) {
+ fprintf(stderr, "Can't open " MOUNTED_TMP ": %s\n",
+ strerror(errno));
+ endmntent(mtab);
+ return 1;
+ }
+
+ while ((mnt = getmntent(mtab)) != NULL) {
+ if (strcmp(mnt->mnt_dir, mount_point) != 0) {
+ addmntent(new_mtab, mnt);
+ }
+ }
+
+ endmntent(mtab);
+
+ if (fchmod (fileno (new_mtab), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
+ fprintf(stderr, "Error changing mode of %s: %s\n",
+ MOUNTED_TMP, strerror(errno));
+ exit(1);
+ }
+
+ endmntent(new_mtab);
+
+ if (rename(MOUNTED_TMP, MOUNTED) < 0) {
+ fprintf(stderr, "Cannot rename %s to %s: %s\n",
+ MOUNTED, MOUNTED_TMP, strerror(errno));
+ exit(1);
+ }
+
+ if (unlink(MOUNTED"~") == -1)
+ {
+ fprintf(stderr, "Can't remove "MOUNTED"~");
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/source4/client/tests/test_cifsdd.sh b/source4/client/tests/test_cifsdd.sh
new file mode 100755
index 0000000000..2268b6a091
--- /dev/null
+++ b/source4/client/tests/test_cifsdd.sh
@@ -0,0 +1,73 @@
+#!/bin/sh
+
+# Basic script to make sure that cifsdd can do both local and remote I/O.
+
+if [ $# -lt 4 ]; then
+cat <<EOF
+Usage: test_cifsdd.sh SERVER USERNAME PASSWORD DOMAIN
+EOF
+exit 1;
+fi
+
+SERVER=$1
+USERNAME=$2
+PASSWORD=$3
+DOMAIN=$4
+
+. `dirname $0`/../../../testprogs/blackbox/subunit.sh
+
+samba4bindir="$BUILDDIR/bin"
+DD="$samba4bindir/cifsdd$EXEEXT"
+
+SHARE=tmp
+DEBUGLEVEL=1
+
+runcopy() {
+ message="$1"
+ shift
+
+ testit "$message" $VALGRIND $DD $CONFIGURATION --debuglevel=$DEBUGLEVEL -W "$DOMAIN" -U "$USERNAME"%"$PASSWORD" \
+ "$@" || failed=`expr $failed + 1`
+}
+
+compare() {
+ testit "$1" cmp "$2" "$3" || failed=`expr $failed + 1`
+}
+
+sourcepath=tempfile.src.$$
+destpath=tempfile.dst.$$
+
+# Create a source file with arbitrary contents
+dd if=$DD of=$sourcepath bs=1024 count=50 > /dev/null
+
+ls -l $sourcepath
+
+for bs in 512 4k 48k ; do
+
+echo "Testing $bs block size ..."
+
+# Check whether we can do local IO
+runcopy "Testing local -> local copy" if=$sourcepath of=$destpath bs=$bs
+compare "Checking local differences" $sourcepath $destpath
+
+# Check whether we can do a round trip
+runcopy "Testing local -> remote copy" \
+ if=$sourcepath of=//$SERVER/$SHARE/$sourcepath bs=$bs
+runcopy "Testing remote -> local copy" \
+ if=//$SERVER/$SHARE/$sourcepath of=$destpath bs=$bs
+compare "Checking differences" $sourcepath $destpath
+
+# Check that copying within the remote server works
+runcopy "Testing local -> remote copy" \
+ if=//$SERVER/$SHARE/$sourcepath of=//$SERVER/$SHARE/$sourcepath bs=$bs
+runcopy "Testing remote -> remote copy" \
+ if=//$SERVER/$SHARE/$sourcepath of=//$SERVER/$SHARE/$destpath bs=$bs
+runcopy "Testing remote -> local copy" \
+ if=//$SERVER/$SHARE/$destpath of=$destpath bs=$bs
+compare "Checking differences" $sourcepath $destpath
+
+done
+
+rm -f $sourcepath $destpath
+
+exit $failed
diff --git a/source4/client/tests/test_smbclient.sh b/source4/client/tests/test_smbclient.sh
new file mode 100755
index 0000000000..7775422e33
--- /dev/null
+++ b/source4/client/tests/test_smbclient.sh
@@ -0,0 +1,160 @@
+#!/bin/sh
+# Blackbox tests for smbclient
+# Copyright (C) 2006-2007 Jelmer Vernooij <jelmer@samba.org>
+# Copyright (C) 2006-2007 Andrew Bartlett <abartlet@samba.org>
+
+if [ $# -lt 5 ]; then
+cat <<EOF
+Usage: test_smbclient.sh SERVER USERNAME PASSWORD DOMAIN PREFIX
+EOF
+exit 1;
+fi
+
+SERVER=$1
+USERNAME=$2
+PASSWORD=$3
+DOMAIN=$4
+PREFIX=$5
+shift 5
+failed=0
+
+samba4bindir="$BUILDDIR/bin"
+smbclient="$samba4bindir/smbclient$EXEEXT"
+
+. `dirname $0`/../../../testprogs/blackbox/subunit.sh
+
+runcmd() {
+ name="$1"
+ cmd="$2"
+ shift
+ shift
+ echo "test: $name"
+ $VALGRIND $smbclient $CONFIGURATION //$SERVER/tmp -c "$cmd" -W "$DOMAIN" -U"$USERNAME%$PASSWORD" $@
+ status=$?
+ if [ x$status = x0 ]; then
+ echo "success: $name"
+ else
+ echo "failure: $name"
+ fi
+ return $status
+}
+
+testit "share and server list" $VALGRIND $smbclient -L $SERVER $CONFIGURATION -W "$DOMAIN" -U"$USERNAME%$PASSWORD" $@ || failed=`expr $failed + 1`
+
+testit "share and server list anonymously" $VALGRIND $smbclient -N -L $SERVER $CONFIGURATION $@ || failed=`expr $failed + 1`
+
+# Generate random file
+cat >tmpfile<<EOF
+foo
+bar
+bloe
+blah
+EOF
+
+# put that file
+runcmd "MPutting file" 'mput tmpfile' || failed=`expr $failed + 1`
+# check file info
+runcmd "Getting alternative name" 'altname tmpfile'|| failed=`expr $failed + 1`
+# run allinfo on that file
+runcmd "Checking info on file" 'allinfo tmpfile'|| failed=`expr $failed + 1`
+# get that file
+mv tmpfile tmpfile-old
+runcmd "MGetting file" 'mget tmpfile' || failed=`expr $failed + 1`
+# remove that file
+runcmd "Removing file" 'rm tmpfile' || failed=`expr $failed + 1`
+# compare locally
+testit "Comparing files" diff tmpfile-old tmpfile || failed=`expr $failed + 1`
+# create directory
+# cd to directory
+# cd to top level directory
+# remove directory
+runcmd "Creating directory, Changing directory, Going back" 'mkdir bla; cd bla; cd ..; rmdir bla' || failed=`expr $failed + 1`
+# enable recurse, create nested directory
+runcmd "Creating nested directory" 'mkdir bla/bloe' || failed=`expr $failed + 1`
+# remove child directory
+runcmd "Removing directory" 'rmdir bla/bloe' || failed=`expr $failed + 1`
+# remove parent directory
+runcmd "Removing directory" 'rmdir bla'|| failed=`expr $failed + 1`
+# enable recurse, create nested directory
+runcmd "Creating nested directory" 'mkdir bla' || failed=`expr $failed + 1`
+# rename bla to bla2
+runcmd "rename of nested directory" 'rename bla bla2' || failed=`expr $failed + 1`
+# deltree
+runcmd "deltree of nested directory" 'deltree bla2' || failed=`expr $failed + 1`
+# run fsinfo
+runcmd "Getting file system info" 'fsinfo allocation'|| failed=`expr $failed + 1`
+runcmd "Getting file system info" 'fsinfo volume'|| failed=`expr $failed + 1`
+runcmd "Getting file system info" 'fsinfo volumeinfo'|| failed=`expr $failed + 1`
+runcmd "Getting file system info" 'fsinfo sizeinfo'|| failed=`expr $failed + 1`
+runcmd "Getting file system info" 'fsinfo deviceinfo'|| failed=`expr $failed + 1`
+runcmd "Getting file system info" 'fsinfo attributeinfo'|| failed=`expr $failed + 1`
+runcmd "Getting file system info" 'fsinfo volume-information'|| failed=`expr $failed + 1`
+runcmd "Getting file system info" 'fsinfo size-information'|| failed=`expr $failed + 1`
+runcmd "Getting file system info" 'fsinfo device-information'|| failed=`expr $failed + 1`
+runcmd "Getting file system info" 'fsinfo attribute-information'|| failed=`expr $failed + 1`
+runcmd "Getting file system info" 'fsinfo quota-information'|| failed=`expr $failed + 1`
+runcmd "Getting file system info" 'fsinfo fullsize-information'|| failed=`expr $failed + 1`
+runcmd "Getting file system info" 'fsinfo objectid'|| failed=`expr $failed + 1`
+
+# put that file
+runcmd "Putting file" 'put tmpfile'|| failed=`expr $failed + 1`
+# get that file
+mv tmpfile tmpfile-old
+runcmd "Getting file" 'get tmpfile'|| failed=`expr $failed + 1`
+runcmd "Getting file EA info" 'eainfo tmpfile'|| failed=`expr $failed + 1`
+# remove that file
+runcmd "Removing file" 'rm tmpfile' || failed=`expr $failed + 1`
+# compare locally
+testit "Comparing files" diff tmpfile-old tmpfile || failed=`expr $failed + 1`
+# put that file
+runcmd "Putting file with different name" 'put tmpfile tmpfilex' || failed=`expr $failed + 1`
+# get that file
+runcmd "Getting file again" 'get tmpfilex' || failed=`expr $failed + 1`
+# compare locally
+testit "Comparing files" diff tmpfilex tmpfile || failed=`expr $failed + 1`
+# remove that file
+runcmd "Removing file" 'rm tmpfilex'|| failed=`expr $failed + 1`
+
+runcmd "Lookup name" "lookup $DOMAIN\\$USERNAME" || failed=`expr $failed + 1`
+
+#Fails unless there are privilages
+#runcmd "Lookup privs of name" "privileges $DOMAIN\\$USERNAME" || failed=`expr $failed + 1`
+
+# do some simple operations using old protocol versions
+runcmd "List directory with LANMAN1" 'ls' -m LANMAN1 || failed=`expr $failed + 1`
+runcmd "List directory with LANMAN2" 'ls' -m LANMAN2 || failed=`expr $failed + 1`
+
+runcmd "Print current working directory" 'pwd'|| failed=`expr $failed + 1`
+
+(
+ echo "password=$PASSWORD"
+ echo "username=$USERNAME"
+ echo "domain=$DOMAIN"
+) > tmpauthfile
+
+testit "Test login with --authentication-file" $VALGRIND $smbclient -c 'ls' $CONFIGURATION //$SERVER/tmp --authentication-file=tmpauthfile || failed=`expr $failed + 1`
+
+PASSWD_FILE="tmppassfile"
+echo "$PASSWORD" > $PASSWD_FILE
+export PASSWD_FILE
+testit "Test login with PASSWD_FILE" $VALGRIND $smbclient -c 'ls' $CONFIGURATION //$SERVER/tmp -W "$DOMAIN" -U"$USERNAME" || failed=`expr $failed + 1`
+PASSWD_FILE=""
+export PASSWD_FILE
+unset PASSWD_FILE
+
+PASSWD="$PASSWORD"
+export PASSWD
+testit "Test login with PASSWD" $VALGRIND $smbclient -c 'ls' $CONFIGURATION //$SERVER/tmp -W "$DOMAIN" -U"$USERNAME" || failed=`expr $failed + 1`
+
+oldUSER=$USER
+USER="$USERNAME"
+export USER
+testit "Test login with USER and PASSWD" $VALGRIND $smbclient -k no -c 'ls' $CONFIGURATION //$SERVER/tmp -W "$DOMAIN" || failed=`expr $failed + 1`
+PASSWD=
+export PASSWD
+unset PASSWD
+USER=$oldUSER
+export USER
+
+rm -f tmpfile tmpfile-old tmpfilex tmpauthfile tmppassfile
+exit $failed
diff --git a/source4/client/tree.c b/source4/client/tree.c
new file mode 100644
index 0000000000..035192126c
--- /dev/null
+++ b/source4/client/tree.c
@@ -0,0 +1,809 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB client GTK+ tree-based application
+ Copyright (C) Andrew Tridgell 1998
+ Copyright (C) Richard Sharpe 2001
+ Copyright (C) John Terpstra 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 <http://www.gnu.org/licenses/>.
+*/
+
+/* example-gtk+ application, ripped off from the gtk+ tree.c sample */
+
+#include <stdio.h>
+#include <errno.h>
+#include <gtk/gtk.h>
+#include "libsmbclient.h"
+
+static GtkWidget *clist;
+
+struct tree_data {
+
+ guint32 type; /* Type of tree item, an SMBC_TYPE */
+ char name[256]; /* May need to change this later */
+
+};
+
+void error_message(gchar *message) {
+
+ GtkWidget *dialog, *label, *okay_button;
+
+ /* Create the widgets */
+
+ dialog = gtk_dialog_new();
+ gtk_window_set_modal(GTK_WINDOW(dialog), True);
+ label = gtk_label_new (message);
+ okay_button = gtk_button_new_with_label("Okay");
+
+ /* Ensure that the dialog box is destroyed when the user clicks ok. */
+
+ gtk_signal_connect_object (GTK_OBJECT (okay_button), "clicked",
+ GTK_SIGNAL_FUNC (gtk_widget_destroy), dialog);
+ gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->action_area),
+ okay_button);
+
+ /* Add the label, and show everything we've added to the dialog. */
+
+ gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->vbox),
+ label);
+ gtk_widget_show_all (dialog);
+}
+
+/*
+ * We are given a widget, and we want to retrieve its URL so we
+ * can do a directory listing.
+ *
+ * We walk back up the tree, picking up pieces until we hit a server or
+ * workgroup type and return a path from there
+ */
+
+static char path_string[1024];
+
+char *get_path(GtkWidget *item)
+{
+ GtkWidget *p = item;
+ struct tree_data *pd;
+ char *comps[1024]; /* We keep pointers to the components here */
+ int i = 0, j, level,type;
+
+ /* Walk back up the tree, getting the private data */
+
+ level = GTK_TREE(item->parent)->level;
+
+ /* Pick up this item's component info */
+
+ pd = (struct tree_data *)gtk_object_get_user_data(GTK_OBJECT(item));
+
+ comps[i++] = pd->name;
+ type = pd->type;
+
+ while (level > 0 && type != SMBC_SERVER && type != SMBC_WORKGROUP) {
+
+ /* Find the parent and extract the data etc ... */
+
+ p = GTK_WIDGET(p->parent);
+ p = GTK_WIDGET(GTK_TREE(p)->tree_owner);
+
+ pd = (struct tree_data *)gtk_object_get_user_data(GTK_OBJECT(p));
+
+ level = GTK_TREE(item->parent)->level;
+
+ comps[i++] = pd->name;
+ type = pd->type;
+
+ }
+
+ /*
+ * Got a list of comps now, should check that we did not hit a workgroup
+ * when we got other things as well ... Later
+ *
+ * Now, build the path
+ */
+
+ snprintf(path_string, sizeof(path_string), "smb:/");
+
+ for (j = i - 1; j >= 0; j--) {
+
+ strncat(path_string, "/", sizeof(path_string) - strlen(path_string));
+ strncat(path_string, comps[j], sizeof(path_string) - strlen(path_string));
+
+ }
+
+ fprintf(stdout, "Path string = %s\n", path_string);
+
+ return path_string;
+
+}
+
+struct tree_data *make_tree_data(guint32 type, const char *name)
+{
+ struct tree_data *p = malloc_p(struct tree_data);
+
+ if (p) {
+
+ p->type = type;
+ strncpy(p->name, name, sizeof(p->name));
+
+ }
+
+ return p;
+
+}
+
+/* Note that this is called every time the user clicks on an item,
+ whether it is already selected or not. */
+static void cb_select_child (GtkWidget *root_tree, GtkWidget *child,
+ GtkWidget *subtree)
+{
+ gint dh, err, dirlen;
+ char dirbuf[512];
+ struct smbc_dirent *dirp;
+ struct stat st1;
+ char path[1024], path1[1024];
+
+ g_print ("select_child called for root tree %p, subtree %p, child %p\n",
+ root_tree, subtree, child);
+
+ /* Now, figure out what it is, and display it in the clist ... */
+
+ gtk_clist_clear(GTK_CLIST(clist)); /* Clear the CLIST */
+
+ /* Now, get the private data for the subtree */
+
+ strncpy(path, get_path(child), 1024);
+
+ if ((dh = smbc_opendir(path)) < 0) { /* Handle error */
+
+ g_print("cb_select_child: Could not open dir %s, %s\n", path,
+ strerror(errno));
+
+ gtk_main_quit();
+
+ return;
+
+ }
+
+ while ((err = smbc_getdents(dh, (struct smbc_dirent *)dirbuf,
+ sizeof(dirbuf))) != 0) {
+
+ if (err < 0) {
+
+ g_print("cb_select_child: Could not read dir %s, %s\n", path,
+ strerror(errno));
+
+ gtk_main_quit();
+
+ return;
+
+ }
+
+ dirp = (struct smbc_dirent *)dirbuf;
+
+ while (err > 0) {
+ gchar col1[128], col2[128], col3[128], col4[128];
+ gchar *rowdata[4] = {col1, col2, col3, col4};
+
+ dirlen = dirp->dirlen;
+
+ /* Format each of the items ... */
+
+ strncpy(col1, dirp->name, 128);
+
+ col2[0] = col3[0] = col4[0] = (char)0;
+
+ switch (dirp->smbc_type) {
+
+ case SMBC_WORKGROUP:
+
+ break;
+
+ case SMBC_SERVER:
+
+ strncpy(col2, (dirp->comment?dirp->comment:""), 128);
+
+ break;
+
+ case SMBC_FILE_SHARE:
+
+ strncpy(col2, (dirp->comment?dirp->comment:""), 128);
+
+ break;
+
+ case SMBC_PRINTER_SHARE:
+
+ strncpy(col2, (dirp->comment?dirp->comment:""), 128);
+ break;
+
+ case SMBC_COMMS_SHARE:
+
+ break;
+
+ case SMBC_IPC_SHARE:
+
+ break;
+
+ case SMBC_DIR:
+ case SMBC_FILE:
+
+ /* Get stats on the file/dir and see what we have */
+
+ if (!ISDOT(dirp->name) && !ISDOTDOT(dirp->name)) {
+
+ strncpy(path1, path, sizeof(path1));
+ strncat(path1, "/", sizeof(path) - strlen(path));
+ strncat(path1, dirp->name, sizeof(path) - strlen(path));
+
+ if (smbc_stat(path1, &st1) < 0) {
+
+ if (errno != EBUSY) {
+
+ g_print("cb_select_child: Could not stat file %s, %s\n", path1,
+ strerror(errno));
+
+ gtk_main_quit();
+
+ return;
+
+ }
+ else {
+
+ strncpy(col2, "Device or resource busy", sizeof(col2));
+
+ }
+ }
+ else {
+ /* Now format each of the relevant things ... */
+
+ snprintf(col2, sizeof(col2), "%c%c%c%c%c%c%c%c%c(%0X)",
+ (st1.st_mode&S_IRUSR?'r':'-'),
+ (st1.st_mode&S_IWUSR?'w':'-'),
+ (st1.st_mode&S_IXUSR?'x':'-'),
+ (st1.st_mode&S_IRGRP?'r':'-'),
+ (st1.st_mode&S_IWGRP?'w':'-'),
+ (st1.st_mode&S_IXGRP?'x':'-'),
+ (st1.st_mode&S_IROTH?'r':'-'),
+ (st1.st_mode&S_IWOTH?'w':'-'),
+ (st1.st_mode&S_IXOTH?'x':'-'),
+ st1.st_mode);
+ snprintf(col3, sizeof(col3), "%u", st1.st_size);
+ snprintf(col4, sizeof(col4), "%s", ctime(&st1.st_mtime));
+ }
+ }
+
+ break;
+
+ default:
+
+ break;
+ }
+
+ gtk_clist_append(GTK_CLIST(clist), rowdata);
+
+ (char *)dirp += dirlen;
+ err -= dirlen;
+
+ }
+
+ }
+
+}
+
+/* Note that this is never called */
+static void cb_unselect_child( GtkWidget *root_tree,
+ GtkWidget *child,
+ GtkWidget *subtree )
+{
+ g_print ("unselect_child called for root tree %p, subtree %p, child %p\n",
+ root_tree, subtree, child);
+}
+
+/* for all the GtkItem:: and GtkTreeItem:: signals */
+static void cb_itemsignal( GtkWidget *item,
+ gchar *signame )
+{
+ GtkWidget *real_tree, *aitem, *subtree;
+ gchar *name;
+ GtkLabel *label;
+ gint dh, err, dirlen, level;
+ char dirbuf[512];
+ struct smbc_dirent *dirp;
+
+ label = GTK_LABEL (GTK_BIN (item)->child);
+ /* Get the text of the label */
+ gtk_label_get (label, &name);
+
+ level = GTK_TREE(item->parent)->level;
+
+ /* Get the level of the tree which the item is in */
+ g_print ("%s called for item %s->%p, level %d\n", signame, name,
+ item, GTK_TREE (item->parent)->level);
+
+ real_tree = GTK_TREE_ITEM_SUBTREE(item); /* Get the subtree */
+
+ if (strncmp(signame, "expand", 6) == 0) { /* Expand called */
+ char server[128];
+
+ if ((dh = smbc_opendir(get_path(item))) < 0) { /* Handle error */
+ gchar errmsg[256];
+
+ g_print("cb_itemsignal: Could not open dir %s, %s\n", get_path(item),
+ strerror(errno));
+
+ slprintf(errmsg, sizeof(errmsg), "cb_itemsignal: Could not open dir %s, %s\n", get_path(item), strerror(errno));
+
+ error_message(errmsg);
+
+ /* gtk_main_quit();*/
+
+ return;
+
+ }
+
+ while ((err = smbc_getdents(dh, (struct smbc_dirent *)dirbuf,
+ sizeof(dirbuf))) != 0) {
+
+ if (err < 0) { /* An error, report it */
+ gchar errmsg[256];
+
+ g_print("cb_itemsignal: Could not read dir smbc://, %s\n",
+ strerror(errno));
+
+ slprintf(errmsg, sizeof(errmsg), "cb_itemsignal: Could not read dir smbc://, %s\n", strerror(errno));
+
+ error_message(errmsg);
+
+ /* gtk_main_quit();*/
+
+ return;
+
+ }
+
+ dirp = (struct smbc_dirent *)dirbuf;
+
+ while (err > 0) {
+ struct tree_data *my_data;
+
+ dirlen = dirp->dirlen;
+
+ my_data = make_tree_data(dirp->smbc_type, dirp->name);
+
+ if (!my_data) {
+
+ g_print("Could not allocate space for tree_data: %s\n",
+ dirp->name);
+
+ gtk_main_quit();
+ return;
+
+ }
+
+ aitem = gtk_tree_item_new_with_label(dirp->name);
+
+ /* Connect all GtkItem:: and GtkTreeItem:: signals */
+ gtk_signal_connect (GTK_OBJECT(aitem), "select",
+ GTK_SIGNAL_FUNC(cb_itemsignal), "select");
+ gtk_signal_connect (GTK_OBJECT(aitem), "deselect",
+ GTK_SIGNAL_FUNC(cb_itemsignal), "deselect");
+ gtk_signal_connect (GTK_OBJECT(aitem), "toggle",
+ GTK_SIGNAL_FUNC(cb_itemsignal), "toggle");
+ gtk_signal_connect (GTK_OBJECT(aitem), "expand",
+ GTK_SIGNAL_FUNC(cb_itemsignal), "expand");
+ gtk_signal_connect (GTK_OBJECT(aitem), "collapse",
+ GTK_SIGNAL_FUNC(cb_itemsignal), "collapse");
+ /* Add it to the parent tree */
+ gtk_tree_append (GTK_TREE(real_tree), aitem);
+
+ gtk_widget_show (aitem);
+
+ gtk_object_set_user_data(GTK_OBJECT(aitem), (gpointer)my_data);
+
+ fprintf(stdout, "Added: %s, len: %u\n", dirp->name, dirlen);
+
+ if (dirp->smbc_type != SMBC_FILE &&
+ dirp->smbc_type != SMBC_IPC_SHARE &&
+ (!ISDOT(dirp->name)) &&
+ (!ISDOTDOT(dirp->name))){
+
+ subtree = gtk_tree_new();
+ gtk_tree_item_set_subtree(GTK_TREE_ITEM(aitem), subtree);
+
+ gtk_signal_connect(GTK_OBJECT(subtree), "select_child",
+ GTK_SIGNAL_FUNC(cb_select_child), real_tree);
+ gtk_signal_connect(GTK_OBJECT(subtree), "unselect_child",
+ GTK_SIGNAL_FUNC(cb_unselect_child), real_tree);
+
+ }
+
+ (char *)dirp += dirlen;
+ err -= dirlen;
+
+ }
+
+ }
+
+ smbc_closedir(dh);
+
+ }
+ else if (strncmp(signame, "collapse", 8) == 0) {
+ GtkWidget *subtree = gtk_tree_new();
+
+ gtk_tree_remove_items(GTK_TREE(real_tree), GTK_TREE(real_tree)->children);
+
+ gtk_tree_item_set_subtree(GTK_TREE_ITEM(item), subtree);
+
+ gtk_signal_connect (GTK_OBJECT(subtree), "select_child",
+ GTK_SIGNAL_FUNC(cb_select_child), real_tree);
+ gtk_signal_connect (GTK_OBJECT(subtree), "unselect_child",
+ GTK_SIGNAL_FUNC(cb_unselect_child), real_tree);
+
+ }
+
+}
+
+static void cb_selection_changed( GtkWidget *tree )
+{
+ GList *i;
+
+ g_print ("selection_change called for tree %p\n", tree);
+ g_print ("selected objects are:\n");
+
+ i = GTK_TREE_SELECTION(tree);
+ while (i){
+ gchar *name;
+ GtkLabel *label;
+ GtkWidget *item;
+
+ /* Get a GtkWidget pointer from the list node */
+ item = GTK_WIDGET (i->data);
+ label = GTK_LABEL (GTK_BIN (item)->child);
+ gtk_label_get (label, &name);
+ g_print ("\t%s on level %d\n", name, GTK_TREE
+ (item->parent)->level);
+ i = i->next;
+ }
+}
+
+/*
+ * Expand or collapse the whole network ...
+ */
+static void cb_wholenet(GtkWidget *item, gchar *signame)
+{
+ GtkWidget *real_tree, *aitem, *subtree;
+ gchar *name;
+ GtkLabel *label;
+ gint dh, err, dirlen;
+ char dirbuf[512];
+ struct smbc_dirent *dirp;
+
+ label = GTK_LABEL (GTK_BIN (item)->child);
+ gtk_label_get (label, &name);
+ g_print ("%s called for item %s->%p, level %d\n", signame, name,
+ item, GTK_TREE (item->parent)->level);
+
+ real_tree = GTK_TREE_ITEM_SUBTREE(item); /* Get the subtree */
+
+ if (strncmp(signame, "expand", 6) == 0) { /* Expand called */
+
+ if ((dh = smbc_opendir("smb://")) < 0) { /* Handle error */
+
+ g_print("cb_wholenet: Could not open dir smbc://, %s\n",
+ strerror(errno));
+
+ gtk_main_quit();
+
+ return;
+
+ }
+
+ while ((err = smbc_getdents(dh, (struct smbc_dirent *)dirbuf,
+ sizeof(dirbuf))) != 0) {
+
+ if (err < 0) { /* An error, report it */
+
+ g_print("cb_wholenet: Could not read dir smbc://, %s\n",
+ strerror(errno));
+
+ gtk_main_quit();
+
+ return;
+
+ }
+
+ dirp = (struct smbc_dirent *)dirbuf;
+
+ while (err > 0) {
+ struct tree_data *my_data;
+
+ dirlen = dirp->dirlen;
+
+ my_data = make_tree_data(dirp->smbc_type, dirp->name);
+
+ aitem = gtk_tree_item_new_with_label(dirp->name);
+
+ /* Connect all GtkItem:: and GtkTreeItem:: signals */
+ gtk_signal_connect (GTK_OBJECT(aitem), "select",
+ GTK_SIGNAL_FUNC(cb_itemsignal), "select");
+ gtk_signal_connect (GTK_OBJECT(aitem), "deselect",
+ GTK_SIGNAL_FUNC(cb_itemsignal), "deselect");
+ gtk_signal_connect (GTK_OBJECT(aitem), "toggle",
+ GTK_SIGNAL_FUNC(cb_itemsignal), "toggle");
+ gtk_signal_connect (GTK_OBJECT(aitem), "expand",
+ GTK_SIGNAL_FUNC(cb_itemsignal), "expand");
+ gtk_signal_connect (GTK_OBJECT(aitem), "collapse",
+ GTK_SIGNAL_FUNC(cb_itemsignal), "collapse");
+
+ gtk_tree_append (GTK_TREE(real_tree), aitem);
+ /* Show it - this can be done at any time */
+ gtk_widget_show (aitem);
+
+ gtk_object_set_user_data(GTK_OBJECT(aitem), (gpointer)my_data);
+
+ fprintf(stdout, "Added: %s, len: %u\n", dirp->name, dirlen);
+
+ subtree = gtk_tree_new();
+
+ gtk_tree_item_set_subtree(GTK_TREE_ITEM(aitem), subtree);
+
+ gtk_signal_connect(GTK_OBJECT(subtree), "select_child",
+ GTK_SIGNAL_FUNC(cb_select_child), real_tree);
+ gtk_signal_connect(GTK_OBJECT(subtree), "unselect_child",
+ GTK_SIGNAL_FUNC(cb_unselect_child), real_tree);
+
+ (char *)dirp += dirlen;
+ err -= dirlen;
+
+ }
+
+ }
+
+ smbc_closedir(dh);
+
+ }
+ else { /* Must be collapse ... FIXME ... */
+ GtkWidget *subtree = gtk_tree_new();
+
+ gtk_tree_remove_items(GTK_TREE(real_tree), GTK_TREE(real_tree)->children);
+
+ gtk_tree_item_set_subtree(GTK_TREE_ITEM(item), subtree);
+
+ gtk_signal_connect (GTK_OBJECT(subtree), "select_child",
+ GTK_SIGNAL_FUNC(cb_select_child), real_tree);
+ gtk_signal_connect (GTK_OBJECT(subtree), "unselect_child",
+ GTK_SIGNAL_FUNC(cb_unselect_child), real_tree);
+
+
+ }
+
+}
+
+/* Should put up a dialog box to ask the user for username and password */
+
+static void
+auth_fn(const char *server, const char *share,
+ char *workgroup, int wgmaxlen, char *username, int unmaxlen,
+ char *password, int pwmaxlen)
+{
+
+ strncpy(username, "test", unmaxlen);
+ strncpy(password, "test", pwmaxlen);
+
+}
+
+static char *col_titles[] = {
+ "Name", "Attributes", "Size", "Modification Date",
+};
+
+int main( int argc,
+ char *argv[] )
+{
+ GtkWidget *window, *scrolled_win, *scrolled_win2, *tree;
+ GtkWidget *subtree, *item, *main_hbox, *r_pane, *l_pane;
+ gint err, dh;
+ gint i;
+ char dirbuf[512];
+ struct smbc_dirent *dirp;
+
+ gtk_init (&argc, &argv);
+
+ /* Init the smbclient library */
+
+ err = smbc_init(auth_fn, 10);
+
+ /* Print an error response ... */
+
+ if (err < 0) {
+
+ fprintf(stderr, "smbc_init returned %s (%i)\nDo you have a ~/.smb/smb.conf file?\n", strerror(errno), errno);
+ exit(1);
+
+ }
+
+ /* a generic toplevel window */
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_widget_set_name(window, "main browser window");
+ gtk_signal_connect (GTK_OBJECT(window), "delete_event",
+ GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
+ gtk_window_set_title(GTK_WINDOW(window), "The Linux Windows Network Browser");
+ gtk_widget_set_usize(GTK_WIDGET(window), 750, -1);
+ gtk_container_set_border_width (GTK_CONTAINER(window), 5);
+
+ gtk_widget_show (window);
+
+ /* A container for the two panes ... */
+
+ main_hbox = gtk_hbox_new(FALSE, 1);
+ gtk_container_border_width(GTK_CONTAINER(main_hbox), 1);
+ gtk_container_add(GTK_CONTAINER(window), main_hbox);
+
+ gtk_widget_show(main_hbox);
+
+ l_pane = gtk_hpaned_new();
+ gtk_paned_gutter_size(GTK_PANED(l_pane), (GTK_PANED(l_pane))->handle_size);
+ r_pane = gtk_hpaned_new();
+ gtk_paned_gutter_size(GTK_PANED(r_pane), (GTK_PANED(r_pane))->handle_size);
+ gtk_container_add(GTK_CONTAINER(main_hbox), l_pane);
+ gtk_widget_show(l_pane);
+
+ /* A generic scrolled window */
+ scrolled_win = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+ gtk_widget_set_usize (scrolled_win, 150, 200);
+ gtk_container_add (GTK_CONTAINER(l_pane), scrolled_win);
+ gtk_widget_show (scrolled_win);
+
+ /* Another generic scrolled window */
+ scrolled_win2 = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win2),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+ gtk_widget_set_usize (scrolled_win2, 150, 200);
+ gtk_paned_add2 (GTK_PANED(l_pane), scrolled_win2);
+ gtk_widget_show (scrolled_win2);
+
+ /* Create the root tree */
+ tree = gtk_tree_new();
+ g_print ("root tree is %p\n", tree);
+ /* connect all GtkTree:: signals */
+ gtk_signal_connect (GTK_OBJECT(tree), "select_child",
+ GTK_SIGNAL_FUNC(cb_select_child), tree);
+ gtk_signal_connect (GTK_OBJECT(tree), "unselect_child",
+ GTK_SIGNAL_FUNC(cb_unselect_child), tree);
+ gtk_signal_connect (GTK_OBJECT(tree), "selection_changed",
+ GTK_SIGNAL_FUNC(cb_selection_changed), tree);
+ /* Add it to the scrolled window */
+ gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW(scrolled_win),
+ tree);
+ /* Set the selection mode */
+ gtk_tree_set_selection_mode (GTK_TREE(tree),
+ GTK_SELECTION_MULTIPLE);
+ /* Show it */
+ gtk_widget_show (tree);
+
+ /* Now, create a clist and attach it to the second pane */
+
+ clist = gtk_clist_new_with_titles(4, col_titles);
+
+ gtk_container_add (GTK_CONTAINER(scrolled_win2), clist);
+
+ gtk_widget_show(clist);
+
+ /* Now, build the top level display ... */
+
+ if ((dh = smbc_opendir("smb:///")) < 0) {
+
+ fprintf(stderr, "Could not list default workgroup: smb:///: %s\n",
+ strerror(errno));
+
+ exit(1);
+
+ }
+
+ /* Create a tree item for Whole Network */
+
+ item = gtk_tree_item_new_with_label ("Whole Network");
+ /* Connect all GtkItem:: and GtkTreeItem:: signals */
+ gtk_signal_connect (GTK_OBJECT(item), "select",
+ GTK_SIGNAL_FUNC(cb_itemsignal), "select");
+ gtk_signal_connect (GTK_OBJECT(item), "deselect",
+ GTK_SIGNAL_FUNC(cb_itemsignal), "deselect");
+ gtk_signal_connect (GTK_OBJECT(item), "toggle",
+ GTK_SIGNAL_FUNC(cb_itemsignal), "toggle");
+ gtk_signal_connect (GTK_OBJECT(item), "expand",
+ GTK_SIGNAL_FUNC(cb_wholenet), "expand");
+ gtk_signal_connect (GTK_OBJECT(item), "collapse",
+ GTK_SIGNAL_FUNC(cb_wholenet), "collapse");
+ /* Add it to the parent tree */
+ gtk_tree_append (GTK_TREE(tree), item);
+ /* Show it - this can be done at any time */
+ gtk_widget_show (item);
+
+ subtree = gtk_tree_new(); /* A subtree for Whole Network */
+
+ gtk_tree_item_set_subtree(GTK_TREE_ITEM(item), subtree);
+
+ gtk_signal_connect (GTK_OBJECT(subtree), "select_child",
+ GTK_SIGNAL_FUNC(cb_select_child), tree);
+ gtk_signal_connect (GTK_OBJECT(subtree), "unselect_child",
+ GTK_SIGNAL_FUNC(cb_unselect_child), tree);
+
+ /* Now, get the items in smb:/// and add them to the tree */
+
+ dirp = (struct smbc_dirent *)dirbuf;
+
+ while ((err = smbc_getdents(dh, (struct smbc_dirent *)dirbuf,
+ sizeof(dirbuf))) != 0) {
+
+ if (err < 0) { /* Handle the error */
+
+ fprintf(stderr, "Could not read directory for smbc:///: %s\n",
+ strerror(errno));
+
+ exit(1);
+
+ }
+
+ fprintf(stdout, "Dir len: %u\n", err);
+
+ while (err > 0) { /* Extract each entry and make a sub-tree */
+ struct tree_data *my_data;
+ int dirlen = dirp->dirlen;
+
+ my_data = make_tree_data(dirp->smbc_type, dirp->name);
+
+ item = gtk_tree_item_new_with_label(dirp->name);
+ /* Connect all GtkItem:: and GtkTreeItem:: signals */
+ gtk_signal_connect (GTK_OBJECT(item), "select",
+ GTK_SIGNAL_FUNC(cb_itemsignal), "select");
+ gtk_signal_connect (GTK_OBJECT(item), "deselect",
+ GTK_SIGNAL_FUNC(cb_itemsignal), "deselect");
+ gtk_signal_connect (GTK_OBJECT(item), "toggle",
+ GTK_SIGNAL_FUNC(cb_itemsignal), "toggle");
+ gtk_signal_connect (GTK_OBJECT(item), "expand",
+ GTK_SIGNAL_FUNC(cb_itemsignal), "expand");
+ gtk_signal_connect (GTK_OBJECT(item), "collapse",
+ GTK_SIGNAL_FUNC(cb_itemsignal), "collapse");
+ /* Add it to the parent tree */
+ gtk_tree_append (GTK_TREE(tree), item);
+ /* Show it - this can be done at any time */
+ gtk_widget_show (item);
+
+ gtk_object_set_user_data(GTK_OBJECT(item), (gpointer)my_data);
+
+ fprintf(stdout, "Added: %s, len: %u\n", dirp->name, dirlen);
+
+ subtree = gtk_tree_new();
+
+ gtk_tree_item_set_subtree(GTK_TREE_ITEM(item), subtree);
+
+ gtk_signal_connect (GTK_OBJECT(subtree), "select_child",
+ GTK_SIGNAL_FUNC(cb_select_child), tree);
+ gtk_signal_connect (GTK_OBJECT(subtree), "unselect_child",
+ GTK_SIGNAL_FUNC(cb_unselect_child), tree);
+
+ (char *)dirp += dirlen;
+ err -= dirlen;
+
+ }
+
+ }
+
+ smbc_closedir(dh); /* FIXME, check for error :-) */
+
+ /* Show the window and loop endlessly */
+ gtk_main();
+ return 0;
+}
+/* example-end */
diff --git a/source4/cluster/cluster.c b/source4/cluster/cluster.c
new file mode 100644
index 0000000000..b3fc9c2caf
--- /dev/null
+++ b/source4/cluster/cluster.c
@@ -0,0 +1,97 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ core clustering code
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "cluster/cluster.h"
+#include "cluster/cluster_private.h"
+#include "librpc/gen_ndr/misc.h"
+#include "librpc/gen_ndr/server_id.h"
+
+static struct cluster_ops *ops;
+
+/* set cluster operations */
+void cluster_set_ops(struct cluster_ops *new_ops)
+{
+ ops = new_ops;
+}
+
+/*
+ an ugly way of getting at the backend handle (eg. ctdb context) via the cluster API
+*/
+void *cluster_backend_handle(void)
+{
+ return ops->backend_handle(ops);
+}
+
+/* by default use the local ops */
+static void cluster_init(void)
+{
+ if (ops == NULL) cluster_local_init();
+}
+
+/*
+ create a server_id for the local node
+*/
+struct server_id cluster_id(uint64_t id, uint32_t id2)
+{
+ cluster_init();
+ return ops->cluster_id(ops, id, id2);
+}
+
+
+/*
+ return a server_id as a string
+*/
+const char *cluster_id_string(TALLOC_CTX *mem_ctx, struct server_id id)
+{
+ cluster_init();
+ return ops->cluster_id_string(ops, mem_ctx, id);
+}
+
+
+/*
+ open a temporary tdb in a cluster friendly manner
+*/
+struct tdb_wrap *cluster_tdb_tmp_open(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx, const char *dbname, int flags)
+{
+ cluster_init();
+ return ops->cluster_tdb_tmp_open(ops, mem_ctx, lp_ctx, dbname, flags);
+}
+
+
+/*
+ register a callback function for a messaging endpoint
+*/
+NTSTATUS cluster_message_init(struct messaging_context *msg, struct server_id server,
+ cluster_message_fn_t handler)
+{
+ cluster_init();
+ return ops->message_init(ops, msg, server, handler);
+}
+
+/*
+ send a message to another node in the cluster
+*/
+NTSTATUS cluster_message_send(struct server_id server, DATA_BLOB *data)
+{
+ cluster_init();
+ return ops->message_send(ops, server, data);
+}
diff --git a/source4/cluster/cluster.h b/source4/cluster/cluster.h
new file mode 100644
index 0000000000..7545757f2c
--- /dev/null
+++ b/source4/cluster/cluster.h
@@ -0,0 +1,50 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ structures for clustering
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __CLUSTER_H__
+#define __CLUSTER_H__
+
+/*
+ test for same cluster id
+*/
+#define cluster_id_equal(id_1, id_2) ((id_1)->id == (id_2)->id \
+ && (id_1)->id2 == (id_2)->id2 \
+ && (id_1)->node == (id_2)->node)
+
+/*
+ test for same cluster node
+*/
+#define cluster_node_equal(id1, id2) ((id1)->node == (id2)->node)
+
+struct messaging_context;
+typedef void (*cluster_message_fn_t)(struct messaging_context *, DATA_BLOB);
+
+/* prototypes */
+struct server_id cluster_id(uint64_t id, uint32_t id2);
+const char *cluster_id_string(TALLOC_CTX *mem_ctx, struct server_id id);
+struct tdb_wrap *cluster_tdb_tmp_open(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx, const char *dbname, int flags);
+void *cluster_backend_handle(void);
+
+NTSTATUS cluster_message_init(struct messaging_context *msg, struct server_id server,
+ cluster_message_fn_t handler);
+NTSTATUS cluster_message_send(struct server_id server, DATA_BLOB *data);
+
+#endif
diff --git a/source4/cluster/cluster_private.h b/source4/cluster/cluster_private.h
new file mode 100644
index 0000000000..e57c983ed0
--- /dev/null
+++ b/source4/cluster/cluster_private.h
@@ -0,0 +1,45 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ private structures for clustering
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _CLUSTER_PRIVATE_H_
+#define _CLUSTER_PRIVATE_H_
+
+struct cluster_ops {
+ struct server_id (*cluster_id)(struct cluster_ops *ops, uint64_t id, uint32_t id2);
+ const char *(*cluster_id_string)(struct cluster_ops *ops,
+ TALLOC_CTX *, struct server_id );
+ struct tdb_wrap *(*cluster_tdb_tmp_open)(struct cluster_ops *,
+ TALLOC_CTX *,
+ struct loadparm_context *,
+ const char *, int);
+ void *(*backend_handle)(struct cluster_ops *);
+ NTSTATUS (*message_init)(struct cluster_ops *ops,
+ struct messaging_context *msg, struct server_id server,
+ cluster_message_fn_t handler);
+ NTSTATUS (*message_send)(struct cluster_ops *ops,
+ struct server_id server, DATA_BLOB *data);
+ void *private_data; /* backend state */
+};
+
+void cluster_set_ops(struct cluster_ops *new_ops);
+void cluster_local_init(void);
+
+#endif
diff --git a/source4/cluster/config.mk b/source4/cluster/config.mk
new file mode 100644
index 0000000000..bbf45180eb
--- /dev/null
+++ b/source4/cluster/config.mk
@@ -0,0 +1,4 @@
+[SUBSYSTEM::CLUSTER]
+PRIVATE_DEPENDENCIES = TDB_WRAP
+
+CLUSTER_OBJ_FILES = $(addprefix $(clustersrcdir)/, cluster.o local.o)
diff --git a/source4/cluster/local.c b/source4/cluster/local.c
new file mode 100644
index 0000000000..96d1d53d4a
--- /dev/null
+++ b/source4/cluster/local.c
@@ -0,0 +1,113 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ local (dummy) clustering 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "cluster/cluster.h"
+#include "cluster/cluster_private.h"
+#include "../tdb/include/tdb.h"
+#include "tdb_wrap.h"
+#include "system/filesys.h"
+#include "param/param.h"
+#include "librpc/gen_ndr/server_id.h"
+
+/*
+ server a server_id for the local node
+*/
+static struct server_id local_id(struct cluster_ops *ops, uint64_t id, uint32_t id2)
+{
+ struct server_id server_id;
+ ZERO_STRUCT(server_id);
+ server_id.id = id;
+ server_id.id2 = id2;
+ return server_id;
+}
+
+
+/*
+ return a server_id as a string
+*/
+static const char *local_id_string(struct cluster_ops *ops,
+ TALLOC_CTX *mem_ctx, struct server_id id)
+{
+ return talloc_asprintf(mem_ctx, "%u.%llu.%u", id.node, (unsigned long long)id.id, id.id2);
+}
+
+
+/*
+ open a tmp tdb for the local node. By using smbd_tmp_path() we don't need
+ TDB_CLEAR_IF_FIRST as the tmp path is wiped at startup
+*/
+static struct tdb_wrap *local_tdb_tmp_open(struct cluster_ops *ops,
+ TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ const char *dbname, int flags)
+{
+ char *path = smbd_tmp_path(mem_ctx, lp_ctx, dbname);
+ struct tdb_wrap *w;
+ w = tdb_wrap_open(mem_ctx, path, 0, flags,
+ O_RDWR|O_CREAT, 0600);
+ talloc_free(path);
+ return w;
+}
+
+/*
+ dummy backend handle function
+*/
+static void *local_backend_handle(struct cluster_ops *ops)
+{
+ return NULL;
+}
+
+/*
+ dummy message init function - not needed as all messages are local
+*/
+static NTSTATUS local_message_init(struct cluster_ops *ops,
+ struct messaging_context *msg,
+ struct server_id server,
+ cluster_message_fn_t handler)
+{
+ return NT_STATUS_OK;
+}
+
+/*
+ dummy message send
+*/
+static NTSTATUS local_message_send(struct cluster_ops *ops,
+ struct server_id server, DATA_BLOB *data)
+{
+ return NT_STATUS_INVALID_DEVICE_REQUEST;
+}
+
+static struct cluster_ops cluster_local_ops = {
+ .cluster_id = local_id,
+ .cluster_id_string = local_id_string,
+ .cluster_tdb_tmp_open = local_tdb_tmp_open,
+ .backend_handle = local_backend_handle,
+ .message_init = local_message_init,
+ .message_send = local_message_send,
+ .private_data = NULL
+};
+
+void cluster_local_init(void)
+{
+ cluster_set_ops(&cluster_local_ops);
+}
+
diff --git a/source4/config.guess b/source4/config.guess
new file mode 100755
index 0000000000..354dbe175a
--- /dev/null
+++ b/source4/config.guess
@@ -0,0 +1,1464 @@
+#! /bin/sh
+# Attempt to guess a canonical system name.
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+# 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+
+timestamp='2005-08-03'
+
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <http://www.gnu.org/licenses/>.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+
+# Originally written by Per Bothner <per@bothner.com>.
+# Please send patches to <config-patches@gnu.org>. Submit a context
+# diff and a properly formatted ChangeLog entry.
+#
+# This script attempts to guess a canonical system name similar to
+# config.sub. If it succeeds, it prints the system name on stdout, and
+# exits with 0. Otherwise, it exits with 1.
+#
+# The plan is that this can be called by configure scripts if you
+# don't specify an explicit build system type.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION]
+
+Output the configuration name of the system \`$me' is run on.
+
+Operation modes:
+ -h, --help print this help, then exit
+ -t, --time-stamp print date of last modification, then exit
+ -v, --version print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.guess ($timestamp)
+
+Originally written by Per Bothner.
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+ case $1 in
+ --time-stamp | --time* | -t )
+ echo "$timestamp" ; exit ;;
+ --version | -v )
+ echo "$version" ; exit ;;
+ --help | --h* | -h )
+ echo "$usage"; exit ;;
+ -- ) # Stop option processing
+ shift; break ;;
+ - ) # Use stdin as input.
+ break ;;
+ -* )
+ echo "$me: invalid option $1$help" >&2
+ exit 1 ;;
+ * )
+ break ;;
+ esac
+done
+
+if test $# != 0; then
+ echo "$me: too many arguments$help" >&2
+ exit 1
+fi
+
+trap 'exit 1' 1 2 15
+
+# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
+# compiler to aid in system detection is discouraged as it requires
+# temporary files to be created and, as you can see below, it is a
+# headache to deal with in a portable fashion.
+
+# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
+# use `HOST_CC' if defined, but it is deprecated.
+
+# Portable tmp directory creation inspired by the Autoconf team.
+
+set_cc_for_build='
+trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ;
+trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ;
+: ${TMPDIR=/tmp} ;
+ { tmp=`(umask 077 && mktemp -d -q "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
+ { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } ||
+ { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } ||
+ { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ;
+dummy=$tmp/dummy ;
+tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ;
+case $CC_FOR_BUILD,$HOST_CC,$CC in
+ ,,) echo "int x;" > $dummy.c ;
+ for c in cc gcc c89 c99 ; do
+ if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then
+ CC_FOR_BUILD="$c"; break ;
+ fi ;
+ done ;
+ if test x"$CC_FOR_BUILD" = x ; then
+ CC_FOR_BUILD=no_compiler_found ;
+ fi
+ ;;
+ ,,*) CC_FOR_BUILD=$CC ;;
+ ,*,*) CC_FOR_BUILD=$HOST_CC ;;
+esac ; set_cc_for_build= ;'
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 1994-08-24)
+if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
+ PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+# Note: order is significant - the case branches are not exclusive.
+
+case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
+ *:NetBSD:*:*)
+ # NetBSD (nbsd) targets should (where applicable) match one or
+ # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*,
+ # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently
+ # switched to ELF, *-*-netbsd* would select the old
+ # object file format. This provides both forward
+ # compatibility and a consistent mechanism for selecting the
+ # object file format.
+ #
+ # Note: NetBSD doesn't particularly care about the vendor
+ # portion of the name. We always set it to "unknown".
+ sysctl="sysctl -n hw.machine_arch"
+ UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \
+ /usr/sbin/$sysctl 2>/dev/null || echo unknown)`
+ case "${UNAME_MACHINE_ARCH}" in
+ armeb) machine=armeb-unknown ;;
+ arm*) machine=arm-unknown ;;
+ sh3el) machine=shl-unknown ;;
+ sh3eb) machine=sh-unknown ;;
+ *) machine=${UNAME_MACHINE_ARCH}-unknown ;;
+ esac
+ # The Operating System including object format, if it has switched
+ # to ELF recently, or will in the future.
+ case "${UNAME_MACHINE_ARCH}" in
+ arm*|i386|m68k|ns32k|sh3*|sparc|vax)
+ eval $set_cc_for_build
+ if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep __ELF__ >/dev/null
+ then
+ # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
+ # Return netbsd for either. FIX?
+ os=netbsd
+ else
+ os=netbsdelf
+ fi
+ ;;
+ *)
+ os=netbsd
+ ;;
+ esac
+ # The OS release
+ # Debian GNU/NetBSD machines have a different userland, and
+ # thus, need a distinct triplet. However, they do not need
+ # kernel version information, so it can be replaced with a
+ # suitable tag, in the style of linux-gnu.
+ case "${UNAME_VERSION}" in
+ Debian*)
+ release='-gnu'
+ ;;
+ *)
+ release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+ ;;
+ esac
+ # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
+ # contains redundant information, the shorter form:
+ # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+ echo "${machine}-${os}${release}"
+ exit ;;
+ *:OpenBSD:*:*)
+ UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
+ echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE}
+ exit ;;
+ *:ekkoBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE}
+ exit ;;
+ macppc:MirBSD:*:*)
+ echo powerppc-unknown-mirbsd${UNAME_RELEASE}
+ exit ;;
+ *:MirBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE}
+ exit ;;
+ alpha:OSF1:*:*)
+ case $UNAME_RELEASE in
+ *4.0)
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+ ;;
+ *5.*)
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
+ ;;
+ esac
+ # According to Compaq, /usr/sbin/psrinfo has been available on
+ # OSF/1 and Tru64 systems produced since 1995. I hope that
+ # covers most systems running today. This code pipes the CPU
+ # types through head -n 1, so we only detect the type of CPU 0.
+ ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1`
+ case "$ALPHA_CPU_TYPE" in
+ "EV4 (21064)")
+ UNAME_MACHINE="alpha" ;;
+ "EV4.5 (21064)")
+ UNAME_MACHINE="alpha" ;;
+ "LCA4 (21066/21068)")
+ UNAME_MACHINE="alpha" ;;
+ "EV5 (21164)")
+ UNAME_MACHINE="alphaev5" ;;
+ "EV5.6 (21164A)")
+ UNAME_MACHINE="alphaev56" ;;
+ "EV5.6 (21164PC)")
+ UNAME_MACHINE="alphapca56" ;;
+ "EV5.7 (21164PC)")
+ UNAME_MACHINE="alphapca57" ;;
+ "EV6 (21264)")
+ UNAME_MACHINE="alphaev6" ;;
+ "EV6.7 (21264A)")
+ UNAME_MACHINE="alphaev67" ;;
+ "EV6.8CB (21264C)")
+ UNAME_MACHINE="alphaev68" ;;
+ "EV6.8AL (21264B)")
+ UNAME_MACHINE="alphaev68" ;;
+ "EV6.8CX (21264D)")
+ UNAME_MACHINE="alphaev68" ;;
+ "EV6.9A (21264/EV69A)")
+ UNAME_MACHINE="alphaev69" ;;
+ "EV7 (21364)")
+ UNAME_MACHINE="alphaev7" ;;
+ "EV7.9 (21364A)")
+ UNAME_MACHINE="alphaev79" ;;
+ esac
+ # A Pn.n version is a patched version.
+ # A Vn.n version is a released version.
+ # A Tn.n version is a released field test version.
+ # A Xn.n version is an unreleased experimental baselevel.
+ # 1.2 uses "1.2" for uname -r.
+ echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+ exit ;;
+ Alpha\ *:Windows_NT*:*)
+ # How do we know it's Interix rather than the generic POSIX subsystem?
+ # Should we change UNAME_MACHINE based on the output of uname instead
+ # of the specific Alpha model?
+ echo alpha-pc-interix
+ exit ;;
+ 21064:Windows_NT:50:3)
+ echo alpha-dec-winnt3.5
+ exit ;;
+ Amiga*:UNIX_System_V:4.0:*)
+ echo m68k-unknown-sysv4
+ exit ;;
+ *:[Aa]miga[Oo][Ss]:*:*)
+ echo ${UNAME_MACHINE}-unknown-amigaos
+ exit ;;
+ *:[Mm]orph[Oo][Ss]:*:*)
+ echo ${UNAME_MACHINE}-unknown-morphos
+ exit ;;
+ *:OS/390:*:*)
+ echo i370-ibm-openedition
+ exit ;;
+ *:z/VM:*:*)
+ echo s390-ibm-zvmoe
+ exit ;;
+ *:OS400:*:*)
+ echo powerpc-ibm-os400
+ exit ;;
+ arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+ echo arm-acorn-riscix${UNAME_RELEASE}
+ exit ;;
+ arm:riscos:*:*|arm:RISCOS:*:*)
+ echo arm-unknown-riscos
+ exit ;;
+ SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
+ echo hppa1.1-hitachi-hiuxmpp
+ exit ;;
+ Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
+ # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+ if test "`(/bin/universe) 2>/dev/null`" = att ; then
+ echo pyramid-pyramid-sysv3
+ else
+ echo pyramid-pyramid-bsd
+ fi
+ exit ;;
+ NILE*:*:*:dcosx)
+ echo pyramid-pyramid-svr4
+ exit ;;
+ DRS?6000:unix:4.0:6*)
+ echo sparc-icl-nx6
+ exit ;;
+ DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*)
+ case `/usr/bin/uname -p` in
+ sparc) echo sparc-icl-nx7; exit ;;
+ esac ;;
+ sun4H:SunOS:5.*:*)
+ echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+ echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ i86pc:SunOS:5.*:*)
+ echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ sun4*:SunOS:6*:*)
+ # According to config.sub, this is the proper way to canonicalize
+ # SunOS6. Hard to guess exactly what SunOS6 will be like, but
+ # it's likely to be more like Solaris than SunOS4.
+ echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ sun4*:SunOS:*:*)
+ case "`/usr/bin/arch -k`" in
+ Series*|S4*)
+ UNAME_RELEASE=`uname -v`
+ ;;
+ esac
+ # Japanese Language versions have a version number like `4.1.3-JL'.
+ echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
+ exit ;;
+ sun3*:SunOS:*:*)
+ echo m68k-sun-sunos${UNAME_RELEASE}
+ exit ;;
+ sun*:*:4.2BSD:*)
+ UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+ test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
+ case "`/bin/arch`" in
+ sun3)
+ echo m68k-sun-sunos${UNAME_RELEASE}
+ ;;
+ sun4)
+ echo sparc-sun-sunos${UNAME_RELEASE}
+ ;;
+ esac
+ exit ;;
+ aushp:SunOS:*:*)
+ echo sparc-auspex-sunos${UNAME_RELEASE}
+ exit ;;
+ # The situation for MiNT is a little confusing. The machine name
+ # can be virtually everything (everything which is not
+ # "atarist" or "atariste" at least should have a processor
+ # > m68000). The system name ranges from "MiNT" over "FreeMiNT"
+ # to the lowercase version "mint" (or "freemint"). Finally
+ # the system name "TOS" denotes a system which is actually not
+ # MiNT. But MiNT is downward compatible to TOS, so this should
+ # be no problem.
+ atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit ;;
+ atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit ;;
+ *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit ;;
+ milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+ echo m68k-milan-mint${UNAME_RELEASE}
+ exit ;;
+ hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+ echo m68k-hades-mint${UNAME_RELEASE}
+ exit ;;
+ *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+ echo m68k-unknown-mint${UNAME_RELEASE}
+ exit ;;
+ m68k:machten:*:*)
+ echo m68k-apple-machten${UNAME_RELEASE}
+ exit ;;
+ powerpc:machten:*:*)
+ echo powerpc-apple-machten${UNAME_RELEASE}
+ exit ;;
+ RISC*:Mach:*:*)
+ echo mips-dec-mach_bsd4.3
+ exit ;;
+ RISC*:ULTRIX:*:*)
+ echo mips-dec-ultrix${UNAME_RELEASE}
+ exit ;;
+ VAX*:ULTRIX*:*:*)
+ echo vax-dec-ultrix${UNAME_RELEASE}
+ exit ;;
+ 2020:CLIX:*:* | 2430:CLIX:*:*)
+ echo clipper-intergraph-clix${UNAME_RELEASE}
+ exit ;;
+ mips:*:*:UMIPS | mips:*:*:RISCos)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+#ifdef __cplusplus
+#include <stdio.h> /* for printf() prototype */
+ int main (int argc, char *argv[]) {
+#else
+ int main (argc, argv) int argc; char *argv[]; {
+#endif
+ #if defined (host_mips) && defined (MIPSEB)
+ #if defined (SYSTYPE_SYSV)
+ printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_SVR4)
+ printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+ printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
+ #endif
+ #endif
+ exit (-1);
+ }
+EOF
+ $CC_FOR_BUILD -o $dummy $dummy.c &&
+ dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` &&
+ SYSTEM_NAME=`$dummy $dummyarg` &&
+ { echo "$SYSTEM_NAME"; exit; }
+ echo mips-mips-riscos${UNAME_RELEASE}
+ exit ;;
+ Motorola:PowerMAX_OS:*:*)
+ echo powerpc-motorola-powermax
+ exit ;;
+ Motorola:*:4.3:PL8-*)
+ echo powerpc-harris-powermax
+ exit ;;
+ Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
+ echo powerpc-harris-powermax
+ exit ;;
+ Night_Hawk:Power_UNIX:*:*)
+ echo powerpc-harris-powerunix
+ exit ;;
+ m88k:CX/UX:7*:*)
+ echo m88k-harris-cxux7
+ exit ;;
+ m88k:*:4*:R4*)
+ echo m88k-motorola-sysv4
+ exit ;;
+ m88k:*:3*:R3*)
+ echo m88k-motorola-sysv3
+ exit ;;
+ AViiON:dgux:*:*)
+ # DG/UX returns AViiON for all architectures
+ UNAME_PROCESSOR=`/usr/bin/uname -p`
+ if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
+ then
+ if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
+ [ ${TARGET_BINARY_INTERFACE}x = x ]
+ then
+ echo m88k-dg-dgux${UNAME_RELEASE}
+ else
+ echo m88k-dg-dguxbcs${UNAME_RELEASE}
+ fi
+ else
+ echo i586-dg-dgux${UNAME_RELEASE}
+ fi
+ exit ;;
+ M88*:DolphinOS:*:*) # DolphinOS (SVR3)
+ echo m88k-dolphin-sysv3
+ exit ;;
+ M88*:*:R3*:*)
+ # Delta 88k system running SVR3
+ echo m88k-motorola-sysv3
+ exit ;;
+ XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+ echo m88k-tektronix-sysv3
+ exit ;;
+ Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+ echo m68k-tektronix-bsd
+ exit ;;
+ *:IRIX*:*:*)
+ echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
+ exit ;;
+ ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+ echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id
+ exit ;; # Note that: echo "'`uname -s`'" gives 'AIX '
+ i*86:AIX:*:*)
+ echo i386-ibm-aix
+ exit ;;
+ ia64:AIX:*:*)
+ if [ -x /usr/bin/oslevel ] ; then
+ IBM_REV=`/usr/bin/oslevel`
+ else
+ IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+ fi
+ echo ${UNAME_MACHINE}-ibm-aix${IBM_REV}
+ exit ;;
+ *:AIX:2:3)
+ if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <sys/systemcfg.h>
+
+ main()
+ {
+ if (!__power_pc())
+ exit(1);
+ puts("powerpc-ibm-aix3.2.5");
+ exit(0);
+ }
+EOF
+ if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy`
+ then
+ echo "$SYSTEM_NAME"
+ else
+ echo rs6000-ibm-aix3.2.5
+ fi
+ elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+ echo rs6000-ibm-aix3.2.4
+ else
+ echo rs6000-ibm-aix3.2
+ fi
+ exit ;;
+ *:AIX:*:[45])
+ IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
+ if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
+ IBM_ARCH=rs6000
+ else
+ IBM_ARCH=powerpc
+ fi
+ if [ -x /usr/bin/oslevel ] ; then
+ IBM_REV=`/usr/bin/oslevel`
+ else
+ IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+ fi
+ echo ${IBM_ARCH}-ibm-aix${IBM_REV}
+ exit ;;
+ *:AIX:*:*)
+ echo rs6000-ibm-aix
+ exit ;;
+ ibmrt:4.4BSD:*|romp-ibm:BSD:*)
+ echo romp-ibm-bsd4.4
+ exit ;;
+ ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and
+ echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to
+ exit ;; # report: romp-ibm BSD 4.3
+ *:BOSX:*:*)
+ echo rs6000-bull-bosx
+ exit ;;
+ DPX/2?00:B.O.S.:*:*)
+ echo m68k-bull-sysv3
+ exit ;;
+ 9000/[34]??:4.3bsd:1.*:*)
+ echo m68k-hp-bsd
+ exit ;;
+ hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+ echo m68k-hp-bsd4.4
+ exit ;;
+ 9000/[34678]??:HP-UX:*:*)
+ HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+ case "${UNAME_MACHINE}" in
+ 9000/31? ) HP_ARCH=m68000 ;;
+ 9000/[34]?? ) HP_ARCH=m68k ;;
+ 9000/[678][0-9][0-9])
+ if [ -x /usr/bin/getconf ]; then
+ sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
+ sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
+ case "${sc_cpu_version}" in
+ 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
+ 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
+ 532) # CPU_PA_RISC2_0
+ case "${sc_kernel_bits}" in
+ 32) HP_ARCH="hppa2.0n" ;;
+ 64) HP_ARCH="hppa2.0w" ;;
+ '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20
+ esac ;;
+ esac
+ fi
+ if [ "${HP_ARCH}" = "" ]; then
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+
+ #define _HPUX_SOURCE
+ #include <stdlib.h>
+ #include <unistd.h>
+
+ int main ()
+ {
+ #if defined(_SC_KERNEL_BITS)
+ long bits = sysconf(_SC_KERNEL_BITS);
+ #endif
+ long cpu = sysconf (_SC_CPU_VERSION);
+
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+ case CPU_PA_RISC2_0:
+ #if defined(_SC_KERNEL_BITS)
+ switch (bits)
+ {
+ case 64: puts ("hppa2.0w"); break;
+ case 32: puts ("hppa2.0n"); break;
+ default: puts ("hppa2.0"); break;
+ } break;
+ #else /* !defined(_SC_KERNEL_BITS) */
+ puts ("hppa2.0"); break;
+ #endif
+ default: puts ("hppa1.0"); break;
+ }
+ exit (0);
+ }
+EOF
+ (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
+ test -z "$HP_ARCH" && HP_ARCH=hppa
+ fi ;;
+ esac
+ if [ ${HP_ARCH} = "hppa2.0w" ]
+ then
+ eval $set_cc_for_build
+
+ # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating
+ # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler
+ # generating 64-bit code. GNU and HP use different nomenclature:
+ #
+ # $ CC_FOR_BUILD=cc ./config.guess
+ # => hppa2.0w-hp-hpux11.23
+ # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess
+ # => hppa64-hp-hpux11.23
+
+ if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) |
+ grep __LP64__ >/dev/null
+ then
+ HP_ARCH="hppa2.0w"
+ else
+ HP_ARCH="hppa64"
+ fi
+ fi
+ echo ${HP_ARCH}-hp-hpux${HPUX_REV}
+ exit ;;
+ ia64:HP-UX:*:*)
+ HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+ echo ia64-hp-hpux${HPUX_REV}
+ exit ;;
+ 3050*:HI-UX:*:*)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <unistd.h>
+ int
+ main ()
+ {
+ long cpu = sysconf (_SC_CPU_VERSION);
+ /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+ true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct
+ results, however. */
+ if (CPU_IS_PA_RISC (cpu))
+ {
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+ default: puts ("hppa-hitachi-hiuxwe2"); break;
+ }
+ }
+ else if (CPU_IS_HP_MC68K (cpu))
+ puts ("m68k-hitachi-hiuxwe2");
+ else puts ("unknown-hitachi-hiuxwe2");
+ exit (0);
+ }
+EOF
+ $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` &&
+ { echo "$SYSTEM_NAME"; exit; }
+ echo unknown-hitachi-hiuxwe2
+ exit ;;
+ 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
+ echo hppa1.1-hp-bsd
+ exit ;;
+ 9000/8??:4.3bsd:*:*)
+ echo hppa1.0-hp-bsd
+ exit ;;
+ *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
+ echo hppa1.0-hp-mpeix
+ exit ;;
+ hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
+ echo hppa1.1-hp-osf
+ exit ;;
+ hp8??:OSF1:*:*)
+ echo hppa1.0-hp-osf
+ exit ;;
+ i*86:OSF1:*:*)
+ if [ -x /usr/sbin/sysversion ] ; then
+ echo ${UNAME_MACHINE}-unknown-osf1mk
+ else
+ echo ${UNAME_MACHINE}-unknown-osf1
+ fi
+ exit ;;
+ parisc*:Lites*:*:*)
+ echo hppa1.1-hp-lites
+ exit ;;
+ C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+ echo c1-convex-bsd
+ exit ;;
+ C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit ;;
+ C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+ echo c34-convex-bsd
+ exit ;;
+ C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+ echo c38-convex-bsd
+ exit ;;
+ C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+ echo c4-convex-bsd
+ exit ;;
+ CRAY*Y-MP:*:*:*)
+ echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*[A-Z]90:*:*:*)
+ echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
+ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
+ -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*TS:*:*:*)
+ echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*T3E:*:*:*)
+ echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*SV1:*:*:*)
+ echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ *:UNICOS/mp:*:*)
+ echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
+ FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+ FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+ FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
+ echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit ;;
+ 5000:UNIX_System_V:4.*:*)
+ FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+ FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'`
+ echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit ;;
+ i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
+ echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
+ exit ;;
+ sparc*:BSD/OS:*:*)
+ echo sparc-unknown-bsdi${UNAME_RELEASE}
+ exit ;;
+ *:BSD/OS:*:*)
+ echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
+ exit ;;
+ *:FreeBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+ exit ;;
+ i*:CYGWIN*:*)
+ echo ${UNAME_MACHINE}-pc-cygwin
+ exit ;;
+ i*:MINGW*:*)
+ echo ${UNAME_MACHINE}-pc-mingw32
+ exit ;;
+ i*:windows32*:*)
+ # uname -m includes "-pc" on this system.
+ echo ${UNAME_MACHINE}-mingw32
+ exit ;;
+ i*:PW*:*)
+ echo ${UNAME_MACHINE}-pc-pw32
+ exit ;;
+ x86:Interix*:[34]*)
+ echo i586-pc-interix${UNAME_RELEASE}|sed -e 's/\..*//'
+ exit ;;
+ [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)
+ echo i${UNAME_MACHINE}-pc-mks
+ exit ;;
+ i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
+ # How do we know it's Interix rather than the generic POSIX subsystem?
+ # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
+ # UNAME_MACHINE based on the output of uname instead of i386?
+ echo i586-pc-interix
+ exit ;;
+ i*:UWIN*:*)
+ echo ${UNAME_MACHINE}-pc-uwin
+ exit ;;
+ amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*)
+ echo x86_64-unknown-cygwin
+ exit ;;
+ p*:CYGWIN*:*)
+ echo powerpcle-unknown-cygwin
+ exit ;;
+ prep*:SunOS:5.*:*)
+ echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ *:GNU:*:*)
+ # the GNU system
+ echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
+ exit ;;
+ *:GNU/*:*:*)
+ # other systems with GNU libc and userland
+ echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu
+ exit ;;
+ i*86:Minix:*:*)
+ echo ${UNAME_MACHINE}-pc-minix
+ exit ;;
+ arm*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ cris:Linux:*:*)
+ echo cris-axis-linux-gnu
+ exit ;;
+ crisv32:Linux:*:*)
+ echo crisv32-axis-linux-gnu
+ exit ;;
+ frv:Linux:*:*)
+ echo frv-unknown-linux-gnu
+ exit ;;
+ ia64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ m32r*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ m68*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ mips:Linux:*:*)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #undef CPU
+ #undef mips
+ #undef mipsel
+ #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+ CPU=mipsel
+ #else
+ #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+ CPU=mips
+ #else
+ CPU=
+ #endif
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=`
+ test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; }
+ ;;
+ mips64:Linux:*:*)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #undef CPU
+ #undef mips64
+ #undef mips64el
+ #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+ CPU=mips64el
+ #else
+ #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+ CPU=mips64
+ #else
+ CPU=
+ #endif
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=`
+ test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; }
+ ;;
+ or32:Linux:*:*)
+ echo or32-unknown-linux-gnu
+ exit ;;
+ ppc:Linux:*:*)
+ echo powerpc-unknown-linux-gnu
+ exit ;;
+ ppc64:Linux:*:*)
+ echo powerpc64-unknown-linux-gnu
+ exit ;;
+ alpha:Linux:*:*)
+ case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
+ EV5) UNAME_MACHINE=alphaev5 ;;
+ EV56) UNAME_MACHINE=alphaev56 ;;
+ PCA56) UNAME_MACHINE=alphapca56 ;;
+ PCA57) UNAME_MACHINE=alphapca56 ;;
+ EV6) UNAME_MACHINE=alphaev6 ;;
+ EV67) UNAME_MACHINE=alphaev67 ;;
+ EV68*) UNAME_MACHINE=alphaev68 ;;
+ esac
+ objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null
+ if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
+ echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
+ exit ;;
+ parisc:Linux:*:* | hppa:Linux:*:*)
+ # Look for CPU level
+ case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
+ PA7*) echo hppa1.1-unknown-linux-gnu ;;
+ PA8*) echo hppa2.0-unknown-linux-gnu ;;
+ *) echo hppa-unknown-linux-gnu ;;
+ esac
+ exit ;;
+ parisc64:Linux:*:* | hppa64:Linux:*:*)
+ echo hppa64-unknown-linux-gnu
+ exit ;;
+ s390:Linux:*:* | s390x:Linux:*:*)
+ echo ${UNAME_MACHINE}-ibm-linux
+ exit ;;
+ sh64*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ sh*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ sparc:Linux:*:* | sparc64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ x86_64:Linux:*:*)
+ echo x86_64-unknown-linux-gnu
+ exit ;;
+ i*86:Linux:*:*)
+ # The BFD linker knows what the default object file format is, so
+ # first see if it will tell us. cd to the root directory to prevent
+ # problems with other programs or directories called `ld' in the path.
+ # Set LC_ALL=C to ensure ld outputs messages in English.
+ ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \
+ | sed -ne '/supported targets:/!d
+ s/[ ][ ]*/ /g
+ s/.*supported targets: *//
+ s/ .*//
+ p'`
+ case "$ld_supported_targets" in
+ elf32-i386)
+ TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu"
+ ;;
+ a.out-i386-linux)
+ echo "${UNAME_MACHINE}-pc-linux-gnuaout"
+ exit ;;
+ coff-i386)
+ echo "${UNAME_MACHINE}-pc-linux-gnucoff"
+ exit ;;
+ "")
+ # Either a pre-BFD a.out linker (linux-gnuoldld) or
+ # one that does not give us useful --help.
+ echo "${UNAME_MACHINE}-pc-linux-gnuoldld"
+ exit ;;
+ esac
+ # Determine whether the default compiler is a.out or elf
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <features.h>
+ #ifdef __ELF__
+ # ifdef __GLIBC__
+ # if __GLIBC__ >= 2
+ LIBC=gnu
+ # else
+ LIBC=gnulibc1
+ # endif
+ # else
+ LIBC=gnulibc1
+ # endif
+ #else
+ #ifdef __INTEL_COMPILER
+ LIBC=gnu
+ #else
+ LIBC=gnuaout
+ #endif
+ #endif
+ #ifdef __dietlibc__
+ LIBC=dietlibc
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^LIBC=`
+ test x"${LIBC}" != x && {
+ echo "${UNAME_MACHINE}-pc-linux-${LIBC}"
+ exit
+ }
+ test x"${TENTATIVE}" != x && { echo "${TENTATIVE}"; exit; }
+ ;;
+ i*86:DYNIX/ptx:4*:*)
+ # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
+ # earlier versions are messed up and put the nodename in both
+ # sysname and nodename.
+ echo i386-sequent-sysv4
+ exit ;;
+ i*86:UNIX_SV:4.2MP:2.*)
+ # Unixware is an offshoot of SVR4, but it has its own version
+ # number series starting with 2...
+ # I am not positive that other SVR4 systems won't match this,
+ # I just have to hope. -- rms.
+ # Use sysv4.2uw... so that sysv4* matches it.
+ echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
+ exit ;;
+ i*86:OS/2:*:*)
+ # If we were able to find `uname', then EMX Unix compatibility
+ # is probably installed.
+ echo ${UNAME_MACHINE}-pc-os2-emx
+ exit ;;
+ i*86:XTS-300:*:STOP)
+ echo ${UNAME_MACHINE}-unknown-stop
+ exit ;;
+ i*86:atheos:*:*)
+ echo ${UNAME_MACHINE}-unknown-atheos
+ exit ;;
+ i*86:syllable:*:*)
+ echo ${UNAME_MACHINE}-pc-syllable
+ exit ;;
+ i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*)
+ echo i386-unknown-lynxos${UNAME_RELEASE}
+ exit ;;
+ i*86:*DOS:*:*)
+ echo ${UNAME_MACHINE}-pc-msdosdjgpp
+ exit ;;
+ i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)
+ UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
+ if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+ echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}
+ else
+ echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
+ fi
+ exit ;;
+ i*86:*:5:[678]*)
+ # UnixWare 7.x, OpenUNIX and OpenServer 6.
+ case `/bin/uname -X | grep "^Machine"` in
+ *486*) UNAME_MACHINE=i486 ;;
+ *Pentium) UNAME_MACHINE=i586 ;;
+ *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
+ esac
+ echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
+ exit ;;
+ i*86:*:3.2:*)
+ if test -f /usr/options/cb.name; then
+ UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+ echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
+ elif /bin/uname -X 2>/dev/null >/dev/null ; then
+ UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
+ (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
+ (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
+ && UNAME_MACHINE=i586
+ (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
+ && UNAME_MACHINE=i686
+ (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
+ && UNAME_MACHINE=i686
+ echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
+ else
+ echo ${UNAME_MACHINE}-pc-sysv32
+ fi
+ exit ;;
+ pc:*:*:*)
+ # Left here for compatibility:
+ # uname -m prints for DJGPP always 'pc', but it prints nothing about
+ # the processor, so we play safe by assuming i386.
+ echo i386-pc-msdosdjgpp
+ exit ;;
+ Intel:Mach:3*:*)
+ echo i386-pc-mach3
+ exit ;;
+ paragon:*:*:*)
+ echo i860-intel-osf1
+ exit ;;
+ i860:*:4.*:*) # i860-SVR4
+ if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+ echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
+ else # Add other i860-SVR4 vendors below as they are discovered.
+ echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4
+ fi
+ exit ;;
+ mini*:CTIX:SYS*5:*)
+ # "miniframe"
+ echo m68010-convergent-sysv
+ exit ;;
+ mc68k:UNIX:SYSTEM5:3.51m)
+ echo m68k-convergent-sysv
+ exit ;;
+ M680?0:D-NIX:5.3:*)
+ echo m68k-diab-dnix
+ exit ;;
+ M68*:*:R3V[5678]*:*)
+ test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;;
+ 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0)
+ OS_REL=''
+ test -r /etc/.relid \
+ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && { echo i486-ncr-sysv4.3${OS_REL}; exit; }
+ /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
+ 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && { echo i486-ncr-sysv4; exit; } ;;
+ m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
+ echo m68k-unknown-lynxos${UNAME_RELEASE}
+ exit ;;
+ mc68030:UNIX_System_V:4.*:*)
+ echo m68k-atari-sysv4
+ exit ;;
+ TSUNAMI:LynxOS:2.*:*)
+ echo sparc-unknown-lynxos${UNAME_RELEASE}
+ exit ;;
+ rs6000:LynxOS:2.*:*)
+ echo rs6000-unknown-lynxos${UNAME_RELEASE}
+ exit ;;
+ PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*)
+ echo powerpc-unknown-lynxos${UNAME_RELEASE}
+ exit ;;
+ SM[BE]S:UNIX_SV:*:*)
+ echo mips-dde-sysv${UNAME_RELEASE}
+ exit ;;
+ RM*:ReliantUNIX-*:*:*)
+ echo mips-sni-sysv4
+ exit ;;
+ RM*:SINIX-*:*:*)
+ echo mips-sni-sysv4
+ exit ;;
+ *:SINIX-*:*:*)
+ if uname -p 2>/dev/null >/dev/null ; then
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
+ echo ${UNAME_MACHINE}-sni-sysv4
+ else
+ echo ns32k-sni-sysv
+ fi
+ exit ;;
+ PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+ # says <Richard.M.Bartel@ccMail.Census.GOV>
+ echo i586-unisys-sysv4
+ exit ;;
+ *:UNIX_System_V:4*:FTX*)
+ # From Gerald Hewes <hewes@openmarket.com>.
+ # How about differentiating between stratus architectures? -djm
+ echo hppa1.1-stratus-sysv4
+ exit ;;
+ *:*:*:FTX*)
+ # From seanf@swdc.stratus.com.
+ echo i860-stratus-sysv4
+ exit ;;
+ i*86:VOS:*:*)
+ # From Paul.Green@stratus.com.
+ echo ${UNAME_MACHINE}-stratus-vos
+ exit ;;
+ *:VOS:*:*)
+ # From Paul.Green@stratus.com.
+ echo hppa1.1-stratus-vos
+ exit ;;
+ mc68*:A/UX:*:*)
+ echo m68k-apple-aux${UNAME_RELEASE}
+ exit ;;
+ news*:NEWS-OS:6*:*)
+ echo mips-sony-newsos6
+ exit ;;
+ R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
+ if [ -d /usr/nec ]; then
+ echo mips-nec-sysv${UNAME_RELEASE}
+ else
+ echo mips-unknown-sysv${UNAME_RELEASE}
+ fi
+ exit ;;
+ BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only.
+ echo powerpc-be-beos
+ exit ;;
+ BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only.
+ echo powerpc-apple-beos
+ exit ;;
+ BePC:BeOS:*:*) # BeOS running on Intel PC compatible.
+ echo i586-pc-beos
+ exit ;;
+ SX-4:SUPER-UX:*:*)
+ echo sx4-nec-superux${UNAME_RELEASE}
+ exit ;;
+ SX-5:SUPER-UX:*:*)
+ echo sx5-nec-superux${UNAME_RELEASE}
+ exit ;;
+ SX-6:SUPER-UX:*:*)
+ echo sx6-nec-superux${UNAME_RELEASE}
+ exit ;;
+ Power*:Rhapsody:*:*)
+ echo powerpc-apple-rhapsody${UNAME_RELEASE}
+ exit ;;
+ *:Rhapsody:*:*)
+ echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
+ exit ;;
+ *:Darwin:*:*)
+ UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
+ case $UNAME_PROCESSOR in
+ *86) UNAME_PROCESSOR=i686 ;;
+ unknown) UNAME_PROCESSOR=powerpc ;;
+ esac
+ echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
+ exit ;;
+ *:procnto*:*:* | *:QNX:[0123456789]*:*)
+ UNAME_PROCESSOR=`uname -p`
+ if test "$UNAME_PROCESSOR" = "x86"; then
+ UNAME_PROCESSOR=i386
+ UNAME_MACHINE=pc
+ fi
+ echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE}
+ exit ;;
+ *:QNX:*:4*)
+ echo i386-pc-qnx
+ exit ;;
+ NSE-?:NONSTOP_KERNEL:*:*)
+ echo nse-tandem-nsk${UNAME_RELEASE}
+ exit ;;
+ NSR-?:NONSTOP_KERNEL:*:*)
+ echo nsr-tandem-nsk${UNAME_RELEASE}
+ exit ;;
+ *:NonStop-UX:*:*)
+ echo mips-compaq-nonstopux
+ exit ;;
+ BS2000:POSIX*:*:*)
+ echo bs2000-siemens-sysv
+ exit ;;
+ DS/*:UNIX_System_V:*:*)
+ echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
+ exit ;;
+ *:Plan9:*:*)
+ # "uname -m" is not consistent, so use $cputype instead. 386
+ # is converted to i386 for consistency with other x86
+ # operating systems.
+ if test "$cputype" = "386"; then
+ UNAME_MACHINE=i386
+ else
+ UNAME_MACHINE="$cputype"
+ fi
+ echo ${UNAME_MACHINE}-unknown-plan9
+ exit ;;
+ *:TOPS-10:*:*)
+ echo pdp10-unknown-tops10
+ exit ;;
+ *:TENEX:*:*)
+ echo pdp10-unknown-tenex
+ exit ;;
+ KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
+ echo pdp10-dec-tops20
+ exit ;;
+ XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
+ echo pdp10-xkl-tops20
+ exit ;;
+ *:TOPS-20:*:*)
+ echo pdp10-unknown-tops20
+ exit ;;
+ *:ITS:*:*)
+ echo pdp10-unknown-its
+ exit ;;
+ SEI:*:*:SEIUX)
+ echo mips-sei-seiux${UNAME_RELEASE}
+ exit ;;
+ *:DragonFly:*:*)
+ echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+ exit ;;
+ *:*VMS:*:*)
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
+ case "${UNAME_MACHINE}" in
+ A*) echo alpha-dec-vms ; exit ;;
+ I*) echo ia64-dec-vms ; exit ;;
+ V*) echo vax-dec-vms ; exit ;;
+ esac ;;
+ *:XENIX:*:SysV)
+ echo i386-pc-xenix
+ exit ;;
+ i*86:skyos:*:*)
+ echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//'
+ exit ;;
+esac
+
+#echo '(No uname command or uname output not recognized.)' 1>&2
+#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
+
+eval $set_cc_for_build
+cat >$dummy.c <<EOF
+#ifdef _SEQUENT_
+# include <sys/types.h>
+# include <sys/utsname.h>
+#endif
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+ /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed,
+ I don't know.... */
+ printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+ printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+ "4"
+#else
+ ""
+#endif
+ ); exit (0);
+#endif
+#endif
+
+#if defined (__arm) && defined (__acorn) && defined (__unix)
+ printf ("arm-acorn-riscix\n"); exit (0);
+#endif
+
+#if defined (hp300) && !defined (hpux)
+ printf ("m68k-hp-bsd\n"); exit (0);
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+ int version;
+ version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+ if (version < 4)
+ printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+ else
+ printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+ exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+ printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+ printf ("ns32k-encore-mach\n"); exit (0);
+#else
+ printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+ printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+ printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+ printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+ struct utsname un;
+
+ uname(&un);
+
+ if (strncmp(un.version, "V2", 2) == 0) {
+ printf ("i386-sequent-ptx2\n"); exit (0);
+ }
+ if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+ printf ("i386-sequent-ptx1\n"); exit (0);
+ }
+ printf ("i386-sequent-ptx\n"); exit (0);
+
+#endif
+
+#if defined (vax)
+# if !defined (ultrix)
+# include <sys/param.h>
+# if defined (BSD)
+# if BSD == 43
+ printf ("vax-dec-bsd4.3\n"); exit (0);
+# else
+# if BSD == 199006
+ printf ("vax-dec-bsd4.3reno\n"); exit (0);
+# else
+ printf ("vax-dec-bsd\n"); exit (0);
+# endif
+# endif
+# else
+ printf ("vax-dec-bsd\n"); exit (0);
+# endif
+# else
+ printf ("vax-dec-ultrix\n"); exit (0);
+# endif
+#endif
+
+#if defined (alliant) && defined (i860)
+ printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+ exit (1);
+}
+EOF
+
+$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` &&
+ { echo "$SYSTEM_NAME"; exit; }
+
+# Apollos put the system type in the environment.
+
+test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; }
+
+# Convex versions that predate uname can use getsysinfo(1)
+
+if [ -x /usr/convex/getsysinfo ]
+then
+ case `getsysinfo -f cpu_type` in
+ c1*)
+ echo c1-convex-bsd
+ exit ;;
+ c2*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit ;;
+ c34*)
+ echo c34-convex-bsd
+ exit ;;
+ c38*)
+ echo c38-convex-bsd
+ exit ;;
+ c4*)
+ echo c4-convex-bsd
+ exit ;;
+ esac
+fi
+
+cat >&2 <<EOF
+$0: unable to guess system type
+
+This script, last modified $timestamp, has failed to recognize
+the operating system you are using. It is advised that you
+download the most up to date version of the config scripts from
+
+ http://savannah.gnu.org/cgi-bin/viewcvs/*checkout*/config/config/config.guess
+and
+ http://savannah.gnu.org/cgi-bin/viewcvs/*checkout*/config/config/config.sub
+
+If the version you run ($0) is already up to date, please
+send the following data and any information you think might be
+pertinent to <config-patches@gnu.org> in order to provide the needed
+information to handle your system.
+
+config.guess timestamp = $timestamp
+
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
+/bin/uname -X = `(/bin/uname -X) 2>/dev/null`
+
+hostinfo = `(hostinfo) 2>/dev/null`
+/bin/universe = `(/bin/universe) 2>/dev/null`
+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null`
+/bin/arch = `(/bin/arch) 2>/dev/null`
+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
+
+UNAME_MACHINE = ${UNAME_MACHINE}
+UNAME_RELEASE = ${UNAME_RELEASE}
+UNAME_SYSTEM = ${UNAME_SYSTEM}
+UNAME_VERSION = ${UNAME_VERSION}
+EOF
+
+exit 1
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/source4/config.sub b/source4/config.sub
new file mode 100755
index 0000000000..23cd6fd75c
--- /dev/null
+++ b/source4/config.sub
@@ -0,0 +1,1577 @@
+#! /bin/sh
+# Configuration validation subroutine script.
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+# 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+
+timestamp='2005-07-08'
+
+# This file is (in principle) common to ALL GNU software.
+# The presence of a machine in this file suggests that SOME GNU software
+# can handle that machine. It does not imply ALL GNU software can.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <http://www.gnu.org/licenses/>.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+
+# Please send patches to <config-patches@gnu.org>. Submit a context
+# diff and a properly formatted ChangeLog entry.
+#
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support. The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION] CPU-MFR-OPSYS
+ $0 [OPTION] ALIAS
+
+Canonicalize a configuration name.
+
+Operation modes:
+ -h, --help print this help, then exit
+ -t, --time-stamp print date of last modification, then exit
+ -v, --version print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.sub ($timestamp)
+
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+ case $1 in
+ --time-stamp | --time* | -t )
+ echo "$timestamp" ; exit ;;
+ --version | -v )
+ echo "$version" ; exit ;;
+ --help | --h* | -h )
+ echo "$usage"; exit ;;
+ -- ) # Stop option processing
+ shift; break ;;
+ - ) # Use stdin as input.
+ break ;;
+ -* )
+ echo "$me: invalid option $1$help"
+ exit 1 ;;
+
+ *local*)
+ # First pass through any local machine types.
+ echo $1
+ exit ;;
+
+ * )
+ break ;;
+ esac
+done
+
+case $# in
+ 0) echo "$me: missing argument$help" >&2
+ exit 1;;
+ 1) ;;
+ *) echo "$me: too many arguments$help" >&2
+ exit 1;;
+esac
+
+# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
+# Here we must recognize all the valid KERNEL-OS combinations.
+maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
+case $maybe_os in
+ nto-qnx* | linux-gnu* | linux-dietlibc | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | \
+ kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | storm-chaos* | os2-emx* | rtmk-nova*)
+ os=-$maybe_os
+ basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
+ ;;
+ *)
+ basic_machine=`echo $1 | sed 's/-[^-]*$//'`
+ if [ $basic_machine != $1 ]
+ then os=`echo $1 | sed 's/.*-/-/'`
+ else os=; fi
+ ;;
+esac
+
+### Let's recognize common machines as not being operating systems so
+### that things like config.sub decstation-3100 work. We also
+### recognize some manufacturers as not being operating systems, so we
+### can provide default operating systems below.
+case $os in
+ -sun*os*)
+ # Prevent following clause from handling this invalid input.
+ ;;
+ -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
+ -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
+ -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
+ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
+ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
+ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
+ -apple | -axis | -knuth | -cray)
+ os=
+ basic_machine=$1
+ ;;
+ -sim | -cisco | -oki | -wec | -winbond)
+ os=
+ basic_machine=$1
+ ;;
+ -scout)
+ ;;
+ -wrs)
+ os=-vxworks
+ basic_machine=$1
+ ;;
+ -chorusos*)
+ os=-chorusos
+ basic_machine=$1
+ ;;
+ -chorusrdb)
+ os=-chorusrdb
+ basic_machine=$1
+ ;;
+ -hiux*)
+ os=-hiuxwe2
+ ;;
+ -sco5)
+ os=-sco3.2v5
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco4)
+ os=-sco3.2v4
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco3.2.[4-9]*)
+ os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco3.2v[4-9]*)
+ # Don't forget version if it is 3.2v4 or newer.
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco*)
+ os=-sco3.2v2
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -udk*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -isc)
+ os=-isc2.2
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -clix*)
+ basic_machine=clipper-intergraph
+ ;;
+ -isc*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -lynx*)
+ os=-lynxos
+ ;;
+ -ptx*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
+ ;;
+ -windowsnt*)
+ os=`echo $os | sed -e 's/windowsnt/winnt/'`
+ ;;
+ -psos*)
+ os=-psos
+ ;;
+ -mint | -mint[0-9]*)
+ basic_machine=m68k-atari
+ os=-mint
+ ;;
+esac
+
+# Decode aliases for certain CPU-COMPANY combinations.
+case $basic_machine in
+ # Recognize the basic CPU types without company name.
+ # Some are omitted here because they have special meanings below.
+ 1750a | 580 \
+ | a29k \
+ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
+ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
+ | am33_2.0 \
+ | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \
+ | bfin \
+ | c4x | clipper \
+ | d10v | d30v | dlx | dsp16xx \
+ | fr30 | frv \
+ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+ | i370 | i860 | i960 | ia64 \
+ | ip2k | iq2000 \
+ | m32r | m32rle | m68000 | m68k | m88k | maxq | mcore \
+ | mips | mipsbe | mipseb | mipsel | mipsle \
+ | mips16 \
+ | mips64 | mips64el \
+ | mips64vr | mips64vrel \
+ | mips64orion | mips64orionel \
+ | mips64vr4100 | mips64vr4100el \
+ | mips64vr4300 | mips64vr4300el \
+ | mips64vr5000 | mips64vr5000el \
+ | mips64vr5900 | mips64vr5900el \
+ | mipsisa32 | mipsisa32el \
+ | mipsisa32r2 | mipsisa32r2el \
+ | mipsisa64 | mipsisa64el \
+ | mipsisa64r2 | mipsisa64r2el \
+ | mipsisa64sb1 | mipsisa64sb1el \
+ | mipsisa64sr71k | mipsisa64sr71kel \
+ | mipstx39 | mipstx39el \
+ | mn10200 | mn10300 \
+ | ms1 \
+ | msp430 \
+ | ns16k | ns32k \
+ | or32 \
+ | pdp10 | pdp11 | pj | pjl \
+ | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
+ | pyramid \
+ | sh | sh[1234] | sh[24]a | sh[23]e | sh[34]eb | shbe | shle | sh[1234]le | sh3ele \
+ | sh64 | sh64le \
+ | sparc | sparc64 | sparc64b | sparc86x | sparclet | sparclite \
+ | sparcv8 | sparcv9 | sparcv9b \
+ | strongarm \
+ | tahoe | thumb | tic4x | tic80 | tron \
+ | v850 | v850e \
+ | we32k \
+ | x86 | xscale | xscalee[bl] | xstormy16 | xtensa \
+ | z8k)
+ basic_machine=$basic_machine-unknown
+ ;;
+ m32c)
+ basic_machine=$basic_machine-unknown
+ ;;
+ m6811 | m68hc11 | m6812 | m68hc12)
+ # Motorola 68HC11/12.
+ basic_machine=$basic_machine-unknown
+ os=-none
+ ;;
+ m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)
+ ;;
+
+ # We use `pc' rather than `unknown'
+ # because (1) that's what they normally are, and
+ # (2) the word "unknown" tends to confuse beginning users.
+ i*86 | x86_64)
+ basic_machine=$basic_machine-pc
+ ;;
+ # Object if more than one company name word.
+ *-*-*)
+ echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+ exit 1
+ ;;
+ # Recognize the basic CPU types with company name.
+ 580-* \
+ | a29k-* \
+ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
+ | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
+ | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \
+ | arm-* | armbe-* | armle-* | armeb-* | armv*-* \
+ | avr-* \
+ | bfin-* | bs2000-* \
+ | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \
+ | clipper-* | craynv-* | cydra-* \
+ | d10v-* | d30v-* | dlx-* \
+ | elxsi-* \
+ | f30[01]-* | f700-* | fr30-* | frv-* | fx80-* \
+ | h8300-* | h8500-* \
+ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
+ | i*86-* | i860-* | i960-* | ia64-* \
+ | ip2k-* | iq2000-* \
+ | m32r-* | m32rle-* \
+ | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
+ | m88110-* | m88k-* | maxq-* | mcore-* \
+ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
+ | mips16-* \
+ | mips64-* | mips64el-* \
+ | mips64vr-* | mips64vrel-* \
+ | mips64orion-* | mips64orionel-* \
+ | mips64vr4100-* | mips64vr4100el-* \
+ | mips64vr4300-* | mips64vr4300el-* \
+ | mips64vr5000-* | mips64vr5000el-* \
+ | mips64vr5900-* | mips64vr5900el-* \
+ | mipsisa32-* | mipsisa32el-* \
+ | mipsisa32r2-* | mipsisa32r2el-* \
+ | mipsisa64-* | mipsisa64el-* \
+ | mipsisa64r2-* | mipsisa64r2el-* \
+ | mipsisa64sb1-* | mipsisa64sb1el-* \
+ | mipsisa64sr71k-* | mipsisa64sr71kel-* \
+ | mipstx39-* | mipstx39el-* \
+ | mmix-* \
+ | ms1-* \
+ | msp430-* \
+ | none-* | np1-* | ns16k-* | ns32k-* \
+ | orion-* \
+ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
+ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
+ | pyramid-* \
+ | romp-* | rs6000-* \
+ | sh-* | sh[1234]-* | sh[24]a-* | sh[23]e-* | sh[34]eb-* | shbe-* \
+ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
+ | sparc-* | sparc64-* | sparc64b-* | sparc86x-* | sparclet-* \
+ | sparclite-* \
+ | sparcv8-* | sparcv9-* | sparcv9b-* | strongarm-* | sv1-* | sx?-* \
+ | tahoe-* | thumb-* \
+ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
+ | tron-* \
+ | v850-* | v850e-* | vax-* \
+ | we32k-* \
+ | x86-* | x86_64-* | xps100-* | xscale-* | xscalee[bl]-* \
+ | xstormy16-* | xtensa-* \
+ | ymp-* \
+ | z8k-*)
+ ;;
+ m32c-*)
+ ;;
+ # Recognize the various machine names and aliases which stand
+ # for a CPU type and a company and sometimes even an OS.
+ 386bsd)
+ basic_machine=i386-unknown
+ os=-bsd
+ ;;
+ 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+ basic_machine=m68000-att
+ ;;
+ 3b*)
+ basic_machine=we32k-att
+ ;;
+ a29khif)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ abacus)
+ basic_machine=abacus-unknown
+ ;;
+ adobe68k)
+ basic_machine=m68010-adobe
+ os=-scout
+ ;;
+ alliant | fx80)
+ basic_machine=fx80-alliant
+ ;;
+ altos | altos3068)
+ basic_machine=m68k-altos
+ ;;
+ am29k)
+ basic_machine=a29k-none
+ os=-bsd
+ ;;
+ amd64)
+ basic_machine=x86_64-pc
+ ;;
+ amd64-*)
+ basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ amdahl)
+ basic_machine=580-amdahl
+ os=-sysv
+ ;;
+ amiga | amiga-*)
+ basic_machine=m68k-unknown
+ ;;
+ amigaos | amigados)
+ basic_machine=m68k-unknown
+ os=-amigaos
+ ;;
+ amigaunix | amix)
+ basic_machine=m68k-unknown
+ os=-sysv4
+ ;;
+ apollo68)
+ basic_machine=m68k-apollo
+ os=-sysv
+ ;;
+ apollo68bsd)
+ basic_machine=m68k-apollo
+ os=-bsd
+ ;;
+ aux)
+ basic_machine=m68k-apple
+ os=-aux
+ ;;
+ balance)
+ basic_machine=ns32k-sequent
+ os=-dynix
+ ;;
+ c90)
+ basic_machine=c90-cray
+ os=-unicos
+ ;;
+ convex-c1)
+ basic_machine=c1-convex
+ os=-bsd
+ ;;
+ convex-c2)
+ basic_machine=c2-convex
+ os=-bsd
+ ;;
+ convex-c32)
+ basic_machine=c32-convex
+ os=-bsd
+ ;;
+ convex-c34)
+ basic_machine=c34-convex
+ os=-bsd
+ ;;
+ convex-c38)
+ basic_machine=c38-convex
+ os=-bsd
+ ;;
+ cray | j90)
+ basic_machine=j90-cray
+ os=-unicos
+ ;;
+ craynv)
+ basic_machine=craynv-cray
+ os=-unicosmp
+ ;;
+ cr16c)
+ basic_machine=cr16c-unknown
+ os=-elf
+ ;;
+ crds | unos)
+ basic_machine=m68k-crds
+ ;;
+ crisv32 | crisv32-* | etraxfs*)
+ basic_machine=crisv32-axis
+ ;;
+ cris | cris-* | etrax*)
+ basic_machine=cris-axis
+ ;;
+ crx)
+ basic_machine=crx-unknown
+ os=-elf
+ ;;
+ da30 | da30-*)
+ basic_machine=m68k-da30
+ ;;
+ decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
+ basic_machine=mips-dec
+ ;;
+ decsystem10* | dec10*)
+ basic_machine=pdp10-dec
+ os=-tops10
+ ;;
+ decsystem20* | dec20*)
+ basic_machine=pdp10-dec
+ os=-tops20
+ ;;
+ delta | 3300 | motorola-3300 | motorola-delta \
+ | 3300-motorola | delta-motorola)
+ basic_machine=m68k-motorola
+ ;;
+ delta88)
+ basic_machine=m88k-motorola
+ os=-sysv3
+ ;;
+ djgpp)
+ basic_machine=i586-pc
+ os=-msdosdjgpp
+ ;;
+ dpx20 | dpx20-*)
+ basic_machine=rs6000-bull
+ os=-bosx
+ ;;
+ dpx2* | dpx2*-bull)
+ basic_machine=m68k-bull
+ os=-sysv3
+ ;;
+ ebmon29k)
+ basic_machine=a29k-amd
+ os=-ebmon
+ ;;
+ elxsi)
+ basic_machine=elxsi-elxsi
+ os=-bsd
+ ;;
+ encore | umax | mmax)
+ basic_machine=ns32k-encore
+ ;;
+ es1800 | OSE68k | ose68k | ose | OSE)
+ basic_machine=m68k-ericsson
+ os=-ose
+ ;;
+ fx2800)
+ basic_machine=i860-alliant
+ ;;
+ genix)
+ basic_machine=ns32k-ns
+ ;;
+ gmicro)
+ basic_machine=tron-gmicro
+ os=-sysv
+ ;;
+ go32)
+ basic_machine=i386-pc
+ os=-go32
+ ;;
+ h3050r* | hiux*)
+ basic_machine=hppa1.1-hitachi
+ os=-hiuxwe2
+ ;;
+ h8300hms)
+ basic_machine=h8300-hitachi
+ os=-hms
+ ;;
+ h8300xray)
+ basic_machine=h8300-hitachi
+ os=-xray
+ ;;
+ h8500hms)
+ basic_machine=h8500-hitachi
+ os=-hms
+ ;;
+ harris)
+ basic_machine=m88k-harris
+ os=-sysv3
+ ;;
+ hp300-*)
+ basic_machine=m68k-hp
+ ;;
+ hp300bsd)
+ basic_machine=m68k-hp
+ os=-bsd
+ ;;
+ hp300hpux)
+ basic_machine=m68k-hp
+ os=-hpux
+ ;;
+ hp3k9[0-9][0-9] | hp9[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hp9k2[0-9][0-9] | hp9k31[0-9])
+ basic_machine=m68000-hp
+ ;;
+ hp9k3[2-9][0-9])
+ basic_machine=m68k-hp
+ ;;
+ hp9k6[0-9][0-9] | hp6[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hp9k7[0-79][0-9] | hp7[0-79][0-9])
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k78[0-9] | hp78[0-9])
+ # FIXME: really hppa2.0-hp
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
+ # FIXME: really hppa2.0-hp
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[0-9][13679] | hp8[0-9][13679])
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[0-9][0-9] | hp8[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hppa-next)
+ os=-nextstep3
+ ;;
+ hppaosf)
+ basic_machine=hppa1.1-hp
+ os=-osf
+ ;;
+ hppro)
+ basic_machine=hppa1.1-hp
+ os=-proelf
+ ;;
+ i370-ibm* | ibm*)
+ basic_machine=i370-ibm
+ ;;
+# I'm not sure what "Sysv32" means. Should this be sysv3.2?
+ i*86v32)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv32
+ ;;
+ i*86v4*)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv4
+ ;;
+ i*86v)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv
+ ;;
+ i*86sol2)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-solaris2
+ ;;
+ i386mach)
+ basic_machine=i386-mach
+ os=-mach
+ ;;
+ i386-vsta | vsta)
+ basic_machine=i386-unknown
+ os=-vsta
+ ;;
+ iris | iris4d)
+ basic_machine=mips-sgi
+ case $os in
+ -irix*)
+ ;;
+ *)
+ os=-irix4
+ ;;
+ esac
+ ;;
+ isi68 | isi)
+ basic_machine=m68k-isi
+ os=-sysv
+ ;;
+ m88k-omron*)
+ basic_machine=m88k-omron
+ ;;
+ magnum | m3230)
+ basic_machine=mips-mips
+ os=-sysv
+ ;;
+ merlin)
+ basic_machine=ns32k-utek
+ os=-sysv
+ ;;
+ mingw32)
+ basic_machine=i386-pc
+ os=-mingw32
+ ;;
+ miniframe)
+ basic_machine=m68000-convergent
+ ;;
+ *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
+ basic_machine=m68k-atari
+ os=-mint
+ ;;
+ mips3*-*)
+ basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
+ ;;
+ mips3*)
+ basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
+ ;;
+ monitor)
+ basic_machine=m68k-rom68k
+ os=-coff
+ ;;
+ morphos)
+ basic_machine=powerpc-unknown
+ os=-morphos
+ ;;
+ msdos)
+ basic_machine=i386-pc
+ os=-msdos
+ ;;
+ mvs)
+ basic_machine=i370-ibm
+ os=-mvs
+ ;;
+ ncr3000)
+ basic_machine=i486-ncr
+ os=-sysv4
+ ;;
+ netbsd386)
+ basic_machine=i386-unknown
+ os=-netbsd
+ ;;
+ netwinder)
+ basic_machine=armv4l-rebel
+ os=-linux
+ ;;
+ news | news700 | news800 | news900)
+ basic_machine=m68k-sony
+ os=-newsos
+ ;;
+ news1000)
+ basic_machine=m68030-sony
+ os=-newsos
+ ;;
+ news-3600 | risc-news)
+ basic_machine=mips-sony
+ os=-newsos
+ ;;
+ necv70)
+ basic_machine=v70-nec
+ os=-sysv
+ ;;
+ next | m*-next )
+ basic_machine=m68k-next
+ case $os in
+ -nextstep* )
+ ;;
+ -ns2*)
+ os=-nextstep2
+ ;;
+ *)
+ os=-nextstep3
+ ;;
+ esac
+ ;;
+ nh3000)
+ basic_machine=m68k-harris
+ os=-cxux
+ ;;
+ nh[45]000)
+ basic_machine=m88k-harris
+ os=-cxux
+ ;;
+ nindy960)
+ basic_machine=i960-intel
+ os=-nindy
+ ;;
+ mon960)
+ basic_machine=i960-intel
+ os=-mon960
+ ;;
+ nonstopux)
+ basic_machine=mips-compaq
+ os=-nonstopux
+ ;;
+ np1)
+ basic_machine=np1-gould
+ ;;
+ nsr-tandem)
+ basic_machine=nsr-tandem
+ ;;
+ op50n-* | op60c-*)
+ basic_machine=hppa1.1-oki
+ os=-proelf
+ ;;
+ openrisc | openrisc-*)
+ basic_machine=or32-unknown
+ ;;
+ os400)
+ basic_machine=powerpc-ibm
+ os=-os400
+ ;;
+ OSE68000 | ose68000)
+ basic_machine=m68000-ericsson
+ os=-ose
+ ;;
+ os68k)
+ basic_machine=m68k-none
+ os=-os68k
+ ;;
+ pa-hitachi)
+ basic_machine=hppa1.1-hitachi
+ os=-hiuxwe2
+ ;;
+ paragon)
+ basic_machine=i860-intel
+ os=-osf
+ ;;
+ pbd)
+ basic_machine=sparc-tti
+ ;;
+ pbb)
+ basic_machine=m68k-tti
+ ;;
+ pc532 | pc532-*)
+ basic_machine=ns32k-pc532
+ ;;
+ pentium | p5 | k5 | k6 | nexgen | viac3)
+ basic_machine=i586-pc
+ ;;
+ pentiumpro | p6 | 6x86 | athlon | athlon_*)
+ basic_machine=i686-pc
+ ;;
+ pentiumii | pentium2 | pentiumiii | pentium3)
+ basic_machine=i686-pc
+ ;;
+ pentium4)
+ basic_machine=i786-pc
+ ;;
+ pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
+ basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentiumpro-* | p6-* | 6x86-* | athlon-*)
+ basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
+ basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentium4-*)
+ basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pn)
+ basic_machine=pn-gould
+ ;;
+ power) basic_machine=power-ibm
+ ;;
+ ppc) basic_machine=powerpc-unknown
+ ;;
+ ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppcle | powerpclittle | ppc-le | powerpc-little)
+ basic_machine=powerpcle-unknown
+ ;;
+ ppcle-* | powerpclittle-*)
+ basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppc64) basic_machine=powerpc64-unknown
+ ;;
+ ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppc64le | powerpc64little | ppc64-le | powerpc64-little)
+ basic_machine=powerpc64le-unknown
+ ;;
+ ppc64le-* | powerpc64little-*)
+ basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ps2)
+ basic_machine=i386-ibm
+ ;;
+ pw32)
+ basic_machine=i586-unknown
+ os=-pw32
+ ;;
+ rom68k)
+ basic_machine=m68k-rom68k
+ os=-coff
+ ;;
+ rm[46]00)
+ basic_machine=mips-siemens
+ ;;
+ rtpc | rtpc-*)
+ basic_machine=romp-ibm
+ ;;
+ s390 | s390-*)
+ basic_machine=s390-ibm
+ ;;
+ s390x | s390x-*)
+ basic_machine=s390x-ibm
+ ;;
+ sa29200)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ sb1)
+ basic_machine=mipsisa64sb1-unknown
+ ;;
+ sb1el)
+ basic_machine=mipsisa64sb1el-unknown
+ ;;
+ sei)
+ basic_machine=mips-sei
+ os=-seiux
+ ;;
+ sequent)
+ basic_machine=i386-sequent
+ ;;
+ sh)
+ basic_machine=sh-hitachi
+ os=-hms
+ ;;
+ sh64)
+ basic_machine=sh64-unknown
+ ;;
+ sparclite-wrs | simso-wrs)
+ basic_machine=sparclite-wrs
+ os=-vxworks
+ ;;
+ sps7)
+ basic_machine=m68k-bull
+ os=-sysv2
+ ;;
+ spur)
+ basic_machine=spur-unknown
+ ;;
+ st2000)
+ basic_machine=m68k-tandem
+ ;;
+ stratus)
+ basic_machine=i860-stratus
+ os=-sysv4
+ ;;
+ sun2)
+ basic_machine=m68000-sun
+ ;;
+ sun2os3)
+ basic_machine=m68000-sun
+ os=-sunos3
+ ;;
+ sun2os4)
+ basic_machine=m68000-sun
+ os=-sunos4
+ ;;
+ sun3os3)
+ basic_machine=m68k-sun
+ os=-sunos3
+ ;;
+ sun3os4)
+ basic_machine=m68k-sun
+ os=-sunos4
+ ;;
+ sun4os3)
+ basic_machine=sparc-sun
+ os=-sunos3
+ ;;
+ sun4os4)
+ basic_machine=sparc-sun
+ os=-sunos4
+ ;;
+ sun4sol2)
+ basic_machine=sparc-sun
+ os=-solaris2
+ ;;
+ sun3 | sun3-*)
+ basic_machine=m68k-sun
+ ;;
+ sun4)
+ basic_machine=sparc-sun
+ ;;
+ sun386 | sun386i | roadrunner)
+ basic_machine=i386-sun
+ ;;
+ sv1)
+ basic_machine=sv1-cray
+ os=-unicos
+ ;;
+ symmetry)
+ basic_machine=i386-sequent
+ os=-dynix
+ ;;
+ t3e)
+ basic_machine=alphaev5-cray
+ os=-unicos
+ ;;
+ t90)
+ basic_machine=t90-cray
+ os=-unicos
+ ;;
+ tic54x | c54x*)
+ basic_machine=tic54x-unknown
+ os=-coff
+ ;;
+ tic55x | c55x*)
+ basic_machine=tic55x-unknown
+ os=-coff
+ ;;
+ tic6x | c6x*)
+ basic_machine=tic6x-unknown
+ os=-coff
+ ;;
+ tx39)
+ basic_machine=mipstx39-unknown
+ ;;
+ tx39el)
+ basic_machine=mipstx39el-unknown
+ ;;
+ toad1)
+ basic_machine=pdp10-xkl
+ os=-tops20
+ ;;
+ tower | tower-32)
+ basic_machine=m68k-ncr
+ ;;
+ tpf)
+ basic_machine=s390x-ibm
+ os=-tpf
+ ;;
+ udi29k)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ ultra3)
+ basic_machine=a29k-nyu
+ os=-sym1
+ ;;
+ v810 | necv810)
+ basic_machine=v810-nec
+ os=-none
+ ;;
+ vaxv)
+ basic_machine=vax-dec
+ os=-sysv
+ ;;
+ vms)
+ basic_machine=vax-dec
+ os=-vms
+ ;;
+ vpp*|vx|vx-*)
+ basic_machine=f301-fujitsu
+ ;;
+ vxworks960)
+ basic_machine=i960-wrs
+ os=-vxworks
+ ;;
+ vxworks68)
+ basic_machine=m68k-wrs
+ os=-vxworks
+ ;;
+ vxworks29k)
+ basic_machine=a29k-wrs
+ os=-vxworks
+ ;;
+ w65*)
+ basic_machine=w65-wdc
+ os=-none
+ ;;
+ w89k-*)
+ basic_machine=hppa1.1-winbond
+ os=-proelf
+ ;;
+ xbox)
+ basic_machine=i686-pc
+ os=-mingw32
+ ;;
+ xps | xps100)
+ basic_machine=xps100-honeywell
+ ;;
+ ymp)
+ basic_machine=ymp-cray
+ os=-unicos
+ ;;
+ z8k-*-coff)
+ basic_machine=z8k-unknown
+ os=-sim
+ ;;
+ none)
+ basic_machine=none-none
+ os=-none
+ ;;
+
+# Here we handle the default manufacturer of certain CPU types. It is in
+# some cases the only manufacturer, in others, it is the most popular.
+ w89k)
+ basic_machine=hppa1.1-winbond
+ ;;
+ op50n)
+ basic_machine=hppa1.1-oki
+ ;;
+ op60c)
+ basic_machine=hppa1.1-oki
+ ;;
+ romp)
+ basic_machine=romp-ibm
+ ;;
+ mmix)
+ basic_machine=mmix-knuth
+ ;;
+ rs6000)
+ basic_machine=rs6000-ibm
+ ;;
+ vax)
+ basic_machine=vax-dec
+ ;;
+ pdp10)
+ # there are many clones, so DEC is not a safe bet
+ basic_machine=pdp10-unknown
+ ;;
+ pdp11)
+ basic_machine=pdp11-dec
+ ;;
+ we32k)
+ basic_machine=we32k-att
+ ;;
+ sh[1234] | sh[24]a | sh[34]eb | sh[1234]le | sh[23]ele)
+ basic_machine=sh-unknown
+ ;;
+ sparc | sparcv8 | sparcv9 | sparcv9b)
+ basic_machine=sparc-sun
+ ;;
+ cydra)
+ basic_machine=cydra-cydrome
+ ;;
+ orion)
+ basic_machine=orion-highlevel
+ ;;
+ orion105)
+ basic_machine=clipper-highlevel
+ ;;
+ mac | mpw | mac-mpw)
+ basic_machine=m68k-apple
+ ;;
+ pmac | pmac-mpw)
+ basic_machine=powerpc-apple
+ ;;
+ *-unknown)
+ # Make sure to match an already-canonicalized machine name.
+ ;;
+ *)
+ echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $basic_machine in
+ *-digital*)
+ basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
+ ;;
+ *-commodore*)
+ basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
+ ;;
+ *)
+ ;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if [ x"$os" != x"" ]
+then
+case $os in
+ # First match some system type aliases
+ # that might get confused with valid system types.
+ # -solaris* is a basic system type, with this one exception.
+ -solaris1 | -solaris1.*)
+ os=`echo $os | sed -e 's|solaris1|sunos4|'`
+ ;;
+ -solaris)
+ os=-solaris2
+ ;;
+ -svr4*)
+ os=-sysv4
+ ;;
+ -unixware*)
+ os=-sysv4.2uw
+ ;;
+ -gnu/linux*)
+ os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
+ ;;
+ # First accept the basic system types.
+ # The portable systems comes first.
+ # Each alternative MUST END IN A *, to match a version number.
+ # -sysv* is not here because it comes later, after sysvr4.
+ -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
+ | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\
+ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \
+ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
+ | -aos* \
+ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
+ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
+ | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* | -openbsd* \
+ | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \
+ | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
+ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
+ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
+ | -chorusos* | -chorusrdb* \
+ | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+ | -mingw32* | -linux-gnu* | -linux-uclibc* | -uxpv* | -beos* | -mpeix* | -udk* \
+ | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
+ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
+ | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
+ | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
+ | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
+ | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
+ | -skyos* | -haiku*)
+ # Remember, each alternative MUST END IN *, to match a version number.
+ ;;
+ -qnx*)
+ case $basic_machine in
+ x86-* | i*86-*)
+ ;;
+ *)
+ os=-nto$os
+ ;;
+ esac
+ ;;
+ -nto-qnx*)
+ ;;
+ -nto*)
+ os=`echo $os | sed -e 's|nto|nto-qnx|'`
+ ;;
+ -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
+ | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \
+ | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
+ ;;
+ -mac*)
+ os=`echo $os | sed -e 's|mac|macos|'`
+ ;;
+ -linux-dietlibc)
+ os=-linux-dietlibc
+ ;;
+ -linux*)
+ os=`echo $os | sed -e 's|linux|linux-gnu|'`
+ ;;
+ -sunos5*)
+ os=`echo $os | sed -e 's|sunos5|solaris2|'`
+ ;;
+ -sunos6*)
+ os=`echo $os | sed -e 's|sunos6|solaris3|'`
+ ;;
+ -opened*)
+ os=-openedition
+ ;;
+ -os400*)
+ os=-os400
+ ;;
+ -wince*)
+ os=-wince
+ ;;
+ -osfrose*)
+ os=-osfrose
+ ;;
+ -osf*)
+ os=-osf
+ ;;
+ -utek*)
+ os=-bsd
+ ;;
+ -dynix*)
+ os=-bsd
+ ;;
+ -acis*)
+ os=-aos
+ ;;
+ -atheos*)
+ os=-atheos
+ ;;
+ -syllable*)
+ os=-syllable
+ ;;
+ -386bsd)
+ os=-bsd
+ ;;
+ -ctix* | -uts*)
+ os=-sysv
+ ;;
+ -nova*)
+ os=-rtmk-nova
+ ;;
+ -ns2 )
+ os=-nextstep2
+ ;;
+ -nsk*)
+ os=-nsk
+ ;;
+ # Preserve the version number of sinix5.
+ -sinix5.*)
+ os=`echo $os | sed -e 's|sinix|sysv|'`
+ ;;
+ -sinix*)
+ os=-sysv4
+ ;;
+ -tpf*)
+ os=-tpf
+ ;;
+ -triton*)
+ os=-sysv3
+ ;;
+ -oss*)
+ os=-sysv3
+ ;;
+ -svr4)
+ os=-sysv4
+ ;;
+ -svr3)
+ os=-sysv3
+ ;;
+ -sysvr4)
+ os=-sysv4
+ ;;
+ # This must come after -sysvr4.
+ -sysv*)
+ ;;
+ -ose*)
+ os=-ose
+ ;;
+ -es1800*)
+ os=-ose
+ ;;
+ -xenix)
+ os=-xenix
+ ;;
+ -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+ os=-mint
+ ;;
+ -aros*)
+ os=-aros
+ ;;
+ -kaos*)
+ os=-kaos
+ ;;
+ -zvmoe)
+ os=-zvmoe
+ ;;
+ -none)
+ ;;
+ *)
+ # Get rid of the `-' at the beginning of $os.
+ os=`echo $os | sed 's/[^-]*-//'`
+ echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system. Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+case $basic_machine in
+ *-acorn)
+ os=-riscix1.2
+ ;;
+ arm*-rebel)
+ os=-linux
+ ;;
+ arm*-semi)
+ os=-aout
+ ;;
+ c4x-* | tic4x-*)
+ os=-coff
+ ;;
+ # This must come before the *-dec entry.
+ pdp10-*)
+ os=-tops20
+ ;;
+ pdp11-*)
+ os=-none
+ ;;
+ *-dec | vax-*)
+ os=-ultrix4.2
+ ;;
+ m68*-apollo)
+ os=-domain
+ ;;
+ i386-sun)
+ os=-sunos4.0.2
+ ;;
+ m68000-sun)
+ os=-sunos3
+ # This also exists in the configure program, but was not the
+ # default.
+ # os=-sunos4
+ ;;
+ m68*-cisco)
+ os=-aout
+ ;;
+ mips*-cisco)
+ os=-elf
+ ;;
+ mips*-*)
+ os=-elf
+ ;;
+ or32-*)
+ os=-coff
+ ;;
+ *-tti) # must be before sparc entry or we get the wrong os.
+ os=-sysv3
+ ;;
+ sparc-* | *-sun)
+ os=-sunos4.1.1
+ ;;
+ *-be)
+ os=-beos
+ ;;
+ *-haiku)
+ os=-haiku
+ ;;
+ *-ibm)
+ os=-aix
+ ;;
+ *-knuth)
+ os=-mmixware
+ ;;
+ *-wec)
+ os=-proelf
+ ;;
+ *-winbond)
+ os=-proelf
+ ;;
+ *-oki)
+ os=-proelf
+ ;;
+ *-hp)
+ os=-hpux
+ ;;
+ *-hitachi)
+ os=-hiux
+ ;;
+ i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+ os=-sysv
+ ;;
+ *-cbm)
+ os=-amigaos
+ ;;
+ *-dg)
+ os=-dgux
+ ;;
+ *-dolphin)
+ os=-sysv3
+ ;;
+ m68k-ccur)
+ os=-rtu
+ ;;
+ m88k-omron*)
+ os=-luna
+ ;;
+ *-next )
+ os=-nextstep
+ ;;
+ *-sequent)
+ os=-ptx
+ ;;
+ *-crds)
+ os=-unos
+ ;;
+ *-ns)
+ os=-genix
+ ;;
+ i370-*)
+ os=-mvs
+ ;;
+ *-next)
+ os=-nextstep3
+ ;;
+ *-gould)
+ os=-sysv
+ ;;
+ *-highlevel)
+ os=-bsd
+ ;;
+ *-encore)
+ os=-bsd
+ ;;
+ *-sgi)
+ os=-irix
+ ;;
+ *-siemens)
+ os=-sysv4
+ ;;
+ *-masscomp)
+ os=-rtu
+ ;;
+ f30[01]-fujitsu | f700-fujitsu)
+ os=-uxpv
+ ;;
+ *-rom68k)
+ os=-coff
+ ;;
+ *-*bug)
+ os=-coff
+ ;;
+ *-apple)
+ os=-macos
+ ;;
+ *-atari*)
+ os=-mint
+ ;;
+ *)
+ os=-none
+ ;;
+esac
+fi
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer. We pick the logical manufacturer.
+vendor=unknown
+case $basic_machine in
+ *-unknown)
+ case $os in
+ -riscix*)
+ vendor=acorn
+ ;;
+ -sunos*)
+ vendor=sun
+ ;;
+ -aix*)
+ vendor=ibm
+ ;;
+ -beos*)
+ vendor=be
+ ;;
+ -hpux*)
+ vendor=hp
+ ;;
+ -mpeix*)
+ vendor=hp
+ ;;
+ -hiux*)
+ vendor=hitachi
+ ;;
+ -unos*)
+ vendor=crds
+ ;;
+ -dgux*)
+ vendor=dg
+ ;;
+ -luna*)
+ vendor=omron
+ ;;
+ -genix*)
+ vendor=ns
+ ;;
+ -mvs* | -opened*)
+ vendor=ibm
+ ;;
+ -os400*)
+ vendor=ibm
+ ;;
+ -ptx*)
+ vendor=sequent
+ ;;
+ -tpf*)
+ vendor=ibm
+ ;;
+ -vxsim* | -vxworks* | -windiss*)
+ vendor=wrs
+ ;;
+ -aux*)
+ vendor=apple
+ ;;
+ -hms*)
+ vendor=hitachi
+ ;;
+ -mpw* | -macos*)
+ vendor=apple
+ ;;
+ -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+ vendor=atari
+ ;;
+ -vos*)
+ vendor=stratus
+ ;;
+ esac
+ basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
+ ;;
+esac
+
+echo $basic_machine$os
+exit
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/source4/configure.ac b/source4/configure.ac
new file mode 100644
index 0000000000..065a3300ca
--- /dev/null
+++ b/source4/configure.ac
@@ -0,0 +1,199 @@
+dnl -*- mode: m4-mode -*-
+dnl Process this file with autoconf to produce a configure script.
+
+AC_PREREQ(2.54)
+
+AC_INIT([samba],[4],[samba-technical@samba.org])
+
+AC_CONFIG_SRCDIR([include/includes.h])
+AC_CONFIG_HEADER(include/config_tmp.h)
+AC_DEFINE(CONFIG_H_IS_FROM_SAMBA,1,[Marker for samba's config.h])
+
+# Configuration rules.
+m4_include(build/m4/env.m4)
+m4_include(../lib/replace/samba.m4)
+m4_include(lib/smbreadline/readline.m4)
+m4_include(heimdal_build/config.m4)
+m4_include(../lib/util/fault.m4)
+m4_include(../lib/util/signal.m4)
+m4_include(../lib/util/util.m4)
+m4_include(../lib/util/fsusage.m4)
+m4_include(../lib/util/xattr.m4)
+m4_include(../lib/util/capability.m4)
+m4_include(../lib/util/time.m4)
+m4_include(../lib/popt/samba.m4)
+m4_include(../lib/util/charset/config.m4)
+m4_include(lib/socket/config.m4)
+m4_include(../lib/zlib/zlib.m4)
+AC_ZLIB([
+SMB_EXT_LIB(ZLIB, [${ZLIB_LIBS}])
+],[
+SMB_INCLUDE_MK(lib/zlib.mk)
+])
+m4_include(../nsswitch/nsstest.m4)
+m4_include(../pidl/config.m4)
+
+AC_CONFIG_FILES(lib/registry/registry.pc)
+AC_CONFIG_FILES(librpc/dcerpc.pc)
+AC_CONFIG_FILES(../librpc/ndr.pc)
+AC_CONFIG_FILES(../lib/torture/torture.pc)
+AC_CONFIG_FILES(auth/gensec/gensec.pc)
+AC_CONFIG_FILES(param/samba-hostconfig.pc)
+AC_CONFIG_FILES(librpc/dcerpc_samr.pc)
+AC_CONFIG_FILES(librpc/dcerpc_atsvc.pc)
+
+m4_include(min_versions.m4)
+
+SMB_INCLUDED_LIB_PKGCONFIG(LIBTALLOC, talloc >= $TALLOC_MIN_VERSION, [],
+ [
+ m4_include(../lib/talloc/libtalloc.m4)
+ SMB_INCLUDE_MK(../lib/talloc/config.mk)
+ ]
+)
+
+SMB_INCLUDED_LIB_PKGCONFIG(LIBTDB, tdb >= $TDB_MIN_VERSION,
+ [],
+ [
+ m4_include(../lib/tdb/libtdb.m4)
+ SMB_INCLUDE_MK(../lib/tdb/config.mk)
+ ]
+)
+
+SMB_INCLUDE_MK(../lib/tdb/python.mk)
+
+SMB_INCLUDED_LIB_PKGCONFIG(LIBTEVENT, tevent = TEVENT_REQUIRED_VERSION,
+ [],[m4_include(../lib/tevent/samba.m4)]
+)
+
+SMB_INCLUDE_MK(../lib/tevent/python.mk)
+
+SMB_INCLUDED_LIB_PKGCONFIG(LIBLDB, ldb = $LDB_REQUIRED_VERSION,
+ [
+ SMB_INCLUDE_MK(lib/ldb/ldb_ildap/config.mk)
+ SMB_INCLUDE_MK(lib/ldb/tools/config.mk)
+ define_ldb_modulesdir=no
+ ],
+ [
+ # Here we need to do some tricks
+ # with AC_CONFIG_COMMANDS_PRE
+ # as that's the deferrs the commands
+ # to location after $prefix and $exec_prefix
+ # have usefull values and directly before
+ # creating config.status.
+ #
+ # The 'eval eval echo' trick is used to
+ # actually get the raw absolute directory
+ # path as this is needed in config.h
+ define_ldb_modulesdir=yes
+ AC_CONFIG_COMMANDS_PRE([
+ if test x"$define_ldb_modulesdir" = x"yes";then
+ LDB_MODULESDIR=`eval eval echo ${modulesdir}/ldb`
+ AC_DEFINE_UNQUOTED(LDB_MODULESDIR, "${LDB_MODULESDIR}" , [ldb Modules directory])
+ fi
+ ])
+ ldbdir=lib/ldb
+ AC_SUBST(ldbdir)
+ m4_include(lib/ldb/sqlite3.m4)
+ m4_include(lib/ldb/libldb.m4)
+ SMB_INCLUDE_MK(lib/ldb/config.mk)
+ AC_CONFIG_FILES(lib/ldb/ldb.pc)
+ ], [no])
+SMB_INCLUDE_MK(lib/ldb/python.mk)
+
+m4_include(lib/tls/config.m4)
+
+dnl m4_include(auth/kerberos/config.m4)
+m4_include(auth/gensec/config.m4)
+m4_include(smbd/process_model.m4)
+m4_include(ntvfs/posix/config.m4)
+m4_include(ntvfs/unixuid/config.m4)
+m4_include(../lib/socket_wrapper/config.m4)
+m4_include(../lib/nss_wrapper/config.m4)
+m4_include(auth/config.m4)
+m4_include(kdc/config.m4)
+m4_include(ntvfs/sysdep/config.m4)
+m4_include(../nsswitch/config.m4)
+
+#################################################
+# add *_CFLAGS only for the real build
+CFLAGS="${CFLAGS} ${DEVELOPER_CFLAGS}"
+
+#################################################
+# final configure stuff
+
+AC_MSG_CHECKING([configure summary])
+AC_TRY_RUN([#include "${srcdir-.}/../tests/summary.c"],
+ AC_MSG_RESULT(yes),
+ AC_MSG_ERROR([summary failure. Aborting config]); exit 1;,
+ AC_MSG_WARN([cannot run when cross-compiling]))
+
+LIBS=`echo $LIBS | sed -e 's/ *//g'`
+if test x"$LIBS" != x""; then
+ echo "LIBS: $LIBS"
+ AC_MSG_WARN([the global \$LIBS variable contains some libraries!])
+ AC_MSG_WARN([this should not happen, please report to samba-technical@lists.samba.org!])
+ AC_MSG_ERROR([only _EXT macros from aclocal.m4 should be used!])
+fi
+
+dnl Remove -L/usr/lib/? from LDFLAGS and LIBS
+LIB_REMOVE_USR_LIB(LDFLAGS)
+LIB_REMOVE_USR_LIB(LIBS)
+
+dnl Remove -I/usr/include/? from CFLAGS and CPPFLAGS
+CFLAGS_REMOVE_USR_INCLUDE(CFLAGS)
+CFLAGS_REMOVE_USR_INCLUDE(CPPFLAGS)
+
+AC_SUBST(ac_default_prefix)
+
+for d in build/smb_build bin include ; do
+ test -d ${builddir}/$d || AS_MKDIR_P(${builddir}/$d)
+done
+
+AC_SUBST(INTERN_LDFLAGS)
+AC_SUBST(INSTALL_LINK_FLAGS)
+if test $USESHARED = "true";
+then
+ INTERN_LDFLAGS="-L\$(shliboutputdir) -L\${builddir}/bin/static"
+ INSTALL_LINK_FLAGS="-Wl,-rpath-link,\$(shliboutputdir)";
+else
+ INTERN_LDFLAGS="-L\${builddir}/bin/static -L\$(shliboutputdir)"
+fi
+
+builddir_headers=""
+
+if test "x$ac_abs_srcdir" != "x$ac_abs_builddir"
+then
+ builddir_headers="-I\$(builddir)/include -I\$(builddir) -I\$(builddir)/lib ";
+fi
+
+CPPFLAGS="$builddir_headers-I\$(srcdir)/include -I\$(srcdir) -I\$(srcdir)/lib -I\$(srcdir)/../lib/replace -I\$(srcdir)/../lib/talloc -I\$(srcdir)/.. -D_SAMBA_BUILD_=4 -DHAVE_CONFIG_H $CPPFLAGS"
+
+SMB_WRITE_PERLVARS(build/smb_build/config.pm)
+
+echo "configure: creating config.mk"
+cat >config.mk<<CEOF
+# config.mk - Autogenerated by configure, DO NOT EDIT!
+$SMB_INFO_EXT_LIBS
+$SMB_INFO_SUBSYSTEMS
+$SMB_INFO_LIBRARIES
+CEOF
+
+SMB_BUILD_RUN(data.mk)
+AC_OUTPUT
+
+cmp include/config_tmp.h include/config.h >/dev/null 2>&1
+CMP_RET=$?
+if test $CMP_RET != 0; then
+ cp include/config_tmp.h include/config.h
+fi
+
+SMB_WRITE_MAKEVARS(mkconfig.mk)
+
+if test $USESHARED = true
+then
+ echo "To run binaries without installing, set the following environment variable:"
+ echo "$LIB_PATH_VAR=$builddir/bin/shared"
+fi
+
+echo
+echo "To build Samba, run $MAKE"
diff --git a/source4/configure.developer b/source4/configure.developer
new file mode 100755
index 0000000000..5033670209
--- /dev/null
+++ b/source4/configure.developer
@@ -0,0 +1,6 @@
+#!/bin/sh
+`dirname $0`/configure -C \
+ --enable-developer \
+ --enable-socket-wrapper \
+ --enable-nss-wrapper \
+ "$@"
diff --git a/source4/configure.nodebug.developer b/source4/configure.nodebug.developer
new file mode 100755
index 0000000000..36741ffb3b
--- /dev/null
+++ b/source4/configure.nodebug.developer
@@ -0,0 +1,3 @@
+#!/bin/sh
+CFLAGS="-Wall -Wshadow -Wstrict-prototypes -Wpointer-arith -Wcast-qual -Wcast-align -DDEBUG_PASSWORD"; export CFLAGS
+./configure "$@"
diff --git a/source4/configure.tridge.opt b/source4/configure.tridge.opt
new file mode 100755
index 0000000000..4f6ee99d46
--- /dev/null
+++ b/source4/configure.tridge.opt
@@ -0,0 +1,2 @@
+#!/bin/sh
+./configure "$@" -C --prefix=/home/tridge/samba/samba4/prefix --enable-developer --enable-debug --enable-socket-wrapper
diff --git a/source4/dsdb/common/flag_mapping.c b/source4/dsdb/common/flag_mapping.c
new file mode 100644
index 0000000000..dceb41be67
--- /dev/null
+++ b/source4/dsdb/common/flag_mapping.c
@@ -0,0 +1,145 @@
+/*
+ Unix SMB/CIFS implementation.
+ helper mapping functions for the SAMDB server
+
+ Copyright (C) Stefan (metze) Metzmacher 2002
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "librpc/gen_ndr/samr.h"
+#include "dsdb/common/flags.h"
+#include "lib/ldb/include/ldb.h"
+#include "dsdb/common/proto.h"
+
+/*
+translated the ACB_CTRL Flags to UserFlags (userAccountControl)
+*/
+/* mapping between ADS userAccountControl and SAMR acct_flags */
+static const struct {
+ uint32_t uf;
+ uint32_t acb;
+} acct_flags_map[] = {
+ { UF_ACCOUNTDISABLE, ACB_DISABLED },
+ { UF_HOMEDIR_REQUIRED, ACB_HOMDIRREQ },
+ { UF_PASSWD_NOTREQD, ACB_PWNOTREQ },
+ { UF_TEMP_DUPLICATE_ACCOUNT, ACB_TEMPDUP },
+ { UF_NORMAL_ACCOUNT, ACB_NORMAL },
+ { UF_MNS_LOGON_ACCOUNT, ACB_MNS },
+ { UF_INTERDOMAIN_TRUST_ACCOUNT, ACB_DOMTRUST },
+ { UF_WORKSTATION_TRUST_ACCOUNT, ACB_WSTRUST },
+ { UF_SERVER_TRUST_ACCOUNT, ACB_SVRTRUST },
+ { UF_DONT_EXPIRE_PASSWD, ACB_PWNOEXP },
+ { UF_LOCKOUT, ACB_AUTOLOCK },
+ { UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED, ACB_ENC_TXT_PWD_ALLOWED },
+ { UF_SMARTCARD_REQUIRED, ACB_SMARTCARD_REQUIRED },
+ { UF_TRUSTED_FOR_DELEGATION, ACB_TRUSTED_FOR_DELEGATION },
+ { UF_NOT_DELEGATED, ACB_NOT_DELEGATED },
+ { UF_USE_DES_KEY_ONLY, ACB_USE_DES_KEY_ONLY},
+ { UF_DONT_REQUIRE_PREAUTH, ACB_DONT_REQUIRE_PREAUTH },
+ { UF_PASSWORD_EXPIRED, ACB_PW_EXPIRED },
+ { UF_NO_AUTH_DATA_REQUIRED, ACB_NO_AUTH_DATA_REQD }
+};
+
+uint32_t samdb_acb2uf(uint32_t acb)
+{
+ uint32_t i, ret = 0;
+ for (i=0;i<ARRAY_SIZE(acct_flags_map);i++) {
+ if (acct_flags_map[i].acb & acb) {
+ ret |= acct_flags_map[i].uf;
+ }
+ }
+ return ret;
+}
+
+/*
+translated the UserFlags (userAccountControl) to ACB_CTRL Flags
+*/
+uint32_t samdb_uf2acb(uint32_t uf)
+{
+ uint32_t i;
+ uint32_t ret = 0;
+ for (i=0;i<ARRAY_SIZE(acct_flags_map);i++) {
+ if (acct_flags_map[i].uf & uf) {
+ ret |= acct_flags_map[i].acb;
+ }
+ }
+ return ret;
+}
+
+/*
+get the accountType from the UserFlags
+*/
+uint32_t samdb_uf2atype(uint32_t uf)
+{
+ uint32_t atype = 0x00000000;
+
+ if (uf & UF_NORMAL_ACCOUNT) atype = ATYPE_NORMAL_ACCOUNT;
+ else if (uf & UF_TEMP_DUPLICATE_ACCOUNT) atype = ATYPE_NORMAL_ACCOUNT;
+ else if (uf & UF_SERVER_TRUST_ACCOUNT) atype = ATYPE_WORKSTATION_TRUST;
+ else if (uf & UF_WORKSTATION_TRUST_ACCOUNT) atype = ATYPE_WORKSTATION_TRUST;
+ else if (uf & UF_INTERDOMAIN_TRUST_ACCOUNT) atype = ATYPE_INTERDOMAIN_TRUST;
+
+ return atype;
+}
+
+/*
+get the accountType from the groupType
+*/
+uint32_t samdb_gtype2atype(uint32_t gtype)
+{
+ uint32_t atype = 0x00000000;
+
+ switch(gtype) {
+ case GTYPE_SECURITY_BUILTIN_LOCAL_GROUP:
+ atype = ATYPE_SECURITY_LOCAL_GROUP;
+ break;
+ case GTYPE_SECURITY_DOMAIN_LOCAL_GROUP:
+ atype = ATYPE_SECURITY_LOCAL_GROUP;
+ break;
+ case GTYPE_SECURITY_GLOBAL_GROUP:
+ atype = ATYPE_SECURITY_GLOBAL_GROUP;
+ break;
+
+ case GTYPE_DISTRIBUTION_GLOBAL_GROUP:
+ atype = ATYPE_DISTRIBUTION_GLOBAL_GROUP;
+ break;
+ case GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP:
+ atype = ATYPE_DISTRIBUTION_UNIVERSAL_GROUP;
+ break;
+ case GTYPE_DISTRIBUTION_UNIVERSAL_GROUP:
+ atype = ATYPE_DISTRIBUTION_LOCAL_GROUP;
+ break;
+ }
+
+ return atype;
+}
+
+/* turn a sAMAccountType into a SID_NAME_USE */
+enum lsa_SidType samdb_atype_map(uint32_t atype)
+{
+ switch (atype & 0xF0000000) {
+ case ATYPE_GLOBAL_GROUP:
+ return SID_NAME_DOM_GRP;
+ case ATYPE_SECURITY_LOCAL_GROUP:
+ return SID_NAME_ALIAS;
+ case ATYPE_ACCOUNT:
+ return SID_NAME_USER;
+ default:
+ DEBUG(1,("hmm, need to map account type 0x%x\n", atype));
+ }
+ return SID_NAME_UNKNOWN;
+}
diff --git a/source4/dsdb/common/flags.h b/source4/dsdb/common/flags.h
new file mode 100644
index 0000000000..dd8081732c
--- /dev/null
+++ b/source4/dsdb/common/flags.h
@@ -0,0 +1,137 @@
+/*
+ Unix SMB/CIFS implementation.
+ User/Group specific flags
+
+ Copyright (C) Andrew Tridgell 2001-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 <http://www.gnu.org/licenses/>.
+*/
+
+/* UserFlags for userAccountControl */
+#define UF_SCRIPT 0x00000001 /* NT or Lan Manager Login script must be executed */
+#define UF_ACCOUNTDISABLE 0x00000002
+#define UF_00000004 0x00000004
+#define UF_HOMEDIR_REQUIRED 0x00000008
+
+#define UF_LOCKOUT 0x00000010
+#define UF_PASSWD_NOTREQD 0x00000020
+#define UF_PASSWD_CANT_CHANGE 0x00000040
+#define UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED 0x00000080
+
+#define UF_TEMP_DUPLICATE_ACCOUNT 0x00000100 /* Local user account in usrmgr */
+#define UF_NORMAL_ACCOUNT 0x00000200
+#define UF_00000400 0x00000400
+#define UF_INTERDOMAIN_TRUST_ACCOUNT 0x00000800
+
+#define UF_WORKSTATION_TRUST_ACCOUNT 0x00001000
+#define UF_SERVER_TRUST_ACCOUNT 0x00002000
+#define UF_00004000 0x00004000
+#define UF_00008000 0x00008000
+
+#define UF_DONT_EXPIRE_PASSWD 0x00010000
+#define UF_MNS_LOGON_ACCOUNT 0x00020000
+#define UF_SMARTCARD_REQUIRED 0x00040000
+#define UF_TRUSTED_FOR_DELEGATION 0x00080000
+
+#define UF_NOT_DELEGATED 0x00100000
+#define UF_USE_DES_KEY_ONLY 0x00200000
+#define UF_DONT_REQUIRE_PREAUTH 0x00400000
+#define UF_PASSWORD_EXPIRED 0x00800000
+
+#define UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION 0x01000000
+#define UF_NO_AUTH_DATA_REQUIRED 0x02000000
+
+/* sAMAccountType */
+#define ATYPE_NORMAL_ACCOUNT 0x30000000 /* 805306368 */
+#define ATYPE_WORKSTATION_TRUST 0x30000001 /* 805306369 */
+#define ATYPE_INTERDOMAIN_TRUST 0x30000002 /* 805306370 */
+#define ATYPE_SECURITY_GLOBAL_GROUP 0x10000000 /* 268435456 */
+#define ATYPE_DISTRIBUTION_GLOBAL_GROUP 0x10000001 /* 268435457 */
+#define ATYPE_DISTRIBUTION_UNIVERSAL_GROUP ATYPE_DISTRIBUTION_GLOBAL_GROUP
+#define ATYPE_SECURITY_LOCAL_GROUP 0x20000000 /* 536870912 */
+#define ATYPE_DISTRIBUTION_LOCAL_GROUP 0x20000001 /* 536870913 */
+
+#define ATYPE_ACCOUNT ATYPE_NORMAL_ACCOUNT /* 0x30000000 805306368 */
+#define ATYPE_GLOBAL_GROUP ATYPE_SECURITY_GLOBAL_GROUP /* 0x10000000 268435456 */
+#define ATYPE_LOCAL_GROUP ATYPE_SECURITY_LOCAL_GROUP /* 0x20000000 536870912 */
+
+/* groupType */
+#define GROUP_TYPE_BUILTIN_LOCAL_GROUP 0x00000001
+#define GROUP_TYPE_ACCOUNT_GROUP 0x00000002
+#define GROUP_TYPE_RESOURCE_GROUP 0x00000004
+#define GROUP_TYPE_UNIVERSAL_GROUP 0x00000008
+#define GROUP_TYPE_APP_BASIC_GROUP 0x00000010
+#define GROUP_TYPE_APP_QUERY_GROUP 0x00000020
+#define GROUP_TYPE_SECURITY_ENABLED 0x80000000
+
+#define GTYPE_SECURITY_BUILTIN_LOCAL_GROUP ( \
+ /* 0x80000005 -2147483643 */ \
+ GROUP_TYPE_BUILTIN_LOCAL_GROUP| \
+ GROUP_TYPE_RESOURCE_GROUP| \
+ GROUP_TYPE_SECURITY_ENABLED \
+ )
+#define GTYPE_SECURITY_DOMAIN_LOCAL_GROUP ( \
+ /* 0x80000004 -2147483644 */ \
+ GROUP_TYPE_RESOURCE_GROUP| \
+ GROUP_TYPE_SECURITY_ENABLED \
+ )
+#define GTYPE_SECURITY_GLOBAL_GROUP ( \
+ /* 0x80000002 -2147483646 */ \
+ GROUP_TYPE_ACCOUNT_GROUP| \
+ GROUP_TYPE_SECURITY_ENABLED \
+ )
+#define GTYPE_SECURITY_UNIVERSAL_GROUP ( \
+ /* 0x80000008 -2147483656 */ \
+ GROUP_TYPE_UNIVERSAL_GROUP| \
+ GROUP_TYPE_SECURITY_ENABLED \
+ )
+#define GTYPE_DISTRIBUTION_GLOBAL_GROUP 0x00000002 /* 2 */
+#define GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP 0x00000004 /* 4 */
+#define GTYPE_DISTRIBUTION_UNIVERSAL_GROUP 0x00000008 /* 8 */
+
+#define INSTANCE_TYPE_IS_NC_HEAD 0x00000001
+#define INSTANCE_TYPE_UNINSTANT 0x00000002
+#define INSTANCE_TYPE_WRITE 0x00000004
+#define INSTANCE_TYPE_NC_ABOVE 0x00000008
+#define INSTANCE_TYPE_NC_COMING 0x00000010
+#define INSTANCE_TYPE_NC_GOING 0x00000020
+
+#define SYSTEM_FLAG_CR_NTDS_NC 0x00000001
+#define SYSTEM_FLAG_CR_NTDS_DOMAIN 0x00000002
+#define SYSTEM_FLAG_CR_NTDS_NOT_GC_REPLICATED 0x00000004
+#define SYSTEM_FLAG_SCHEMA_BASE_OBJECT 0x00000010
+#define SYSTEM_FLAG_ATTR_IS_RDN 0x00000020
+#define SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE 0x02000000
+#define SYSTEM_FLAG_DOMAIN_DISALLOW_MOVE 0x04000000
+#define SYSTEM_FLAG_DOMAIN_DISALLOW_RENAME 0x08000000
+#define SYSTEM_FLAG_CONFIG_ALLOW_LIMITED_MOVE 0x10000000
+#define SYSTEM_FLAG_CONFIG_ALLOW_MOVE 0x20000000
+#define SYSTEM_FLAG_CONFIG_ALLOW_RENAME 0x40000000
+#define SYSTEM_FLAG_DISALLOW_DELTE 0x80000000
+
+#define SEARCH_FLAG_ATTINDEX 0x0000001
+#define SEARCH_FLAG_PDNTATTINDEX 0x0000002
+#define SEARCH_FLAG_ANR 0x0000004
+#define SEARCH_FLAG_PRESERVEONDELETE 0x0000008
+#define SEARCH_FLAG_COPY 0x0000010
+#define SEARCH_FLAG_TUPLEINDEX 0x0000020
+#define SEARCH_FLAG_SUBTREEATTRINDEX 0x0000040
+#define SEARCH_FLAG_CONFIDENTIAL 0x0000080
+#define SEARCH_FLAG_NEVERVALUEAUDIT 0x0000100
+#define SEARCH_FLAG_RODC_ATTRIBUTE 0x0000200
+
+#define DS_BEHAVIOR_WIN2000 0
+#define DS_BEHAVIOR_WIN2003_INTERIM 1
+#define DS_BEHAVIOR_WIN2003 2
+#define DS_BEHAVIOR_WIN2008 3
diff --git a/source4/dsdb/common/sidmap.c b/source4/dsdb/common/sidmap.c
new file mode 100644
index 0000000000..5c20149384
--- /dev/null
+++ b/source4/dsdb/common/sidmap.c
@@ -0,0 +1,612 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ mapping routines for SID <-> unix uid/gid
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/passwd.h"
+#include "dsdb/common/flags.h"
+#include "dsdb/samdb/samdb.h"
+#include "auth/auth.h"
+#include "libcli/ldap/ldap_ndr.h"
+#include "lib/ldb/include/ldb.h"
+#include "../lib/util/util_ldb.h"
+#include "libcli/security/security.h"
+#include "param/param.h"
+
+/*
+ these are used for the fallback local uid/gid to sid mapping
+ code.
+*/
+#define SIDMAP_LOCAL_USER_BASE 0x80000000
+#define SIDMAP_LOCAL_GROUP_BASE 0xC0000000
+#define SIDMAP_MAX_LOCAL_UID 0x3fffffff
+#define SIDMAP_MAX_LOCAL_GID 0x3fffffff
+
+/*
+ private context for sid mapping routines
+*/
+struct sidmap_context {
+ struct ldb_context *samctx;
+};
+
+/*
+ open a sidmap context - use talloc_free to close
+*/
+struct sidmap_context *sidmap_open(TALLOC_CTX *mem_ctx, struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx)
+{
+ struct sidmap_context *sidmap;
+ sidmap = talloc(mem_ctx, struct sidmap_context);
+ if (sidmap == NULL) {
+ return NULL;
+ }
+ sidmap->samctx = samdb_connect(sidmap, ev_ctx, lp_ctx, system_session(sidmap, lp_ctx));
+ if (sidmap->samctx == NULL) {
+ talloc_free(sidmap);
+ return NULL;
+ }
+
+ return sidmap;
+}
+
+
+/*
+ check the sAMAccountType field of a search result to see if
+ the account is a user account
+*/
+static bool is_user_account(struct ldb_message *res)
+{
+ uint_t atype = samdb_result_uint(res, "sAMAccountType", 0);
+ if (atype && (!(atype & ATYPE_ACCOUNT))) {
+ return false;
+ }
+ return true;
+}
+
+/*
+ check the sAMAccountType field of a search result to see if
+ the account is a group account
+*/
+static bool is_group_account(struct ldb_message *res)
+{
+ uint_t atype = samdb_result_uint(res, "sAMAccountType", 0);
+ if (atype && atype == ATYPE_NORMAL_ACCOUNT) {
+ return false;
+ }
+ return true;
+}
+
+
+
+/*
+ return the dom_sid of our primary domain
+*/
+static NTSTATUS sidmap_primary_domain_sid(struct sidmap_context *sidmap,
+ TALLOC_CTX *mem_ctx, struct dom_sid **sid)
+{
+ const char *attrs[] = { "objectSid", NULL };
+ int ret;
+ struct ldb_message **res = NULL;
+
+ ret = gendb_search_dn(sidmap->samctx, mem_ctx, NULL, &res, attrs);
+ if (ret != 1) {
+ talloc_free(res);
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ *sid = samdb_result_dom_sid(mem_ctx, res[0], "objectSid");
+ talloc_free(res);
+ if (*sid == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ map a sid to a unix uid
+*/
+NTSTATUS sidmap_sid_to_unixuid(struct sidmap_context *sidmap,
+ const struct dom_sid *sid, uid_t *uid)
+{
+ const char *attrs[] = { "sAMAccountName", "uidNumber",
+ "sAMAccountType", "unixName", NULL };
+ int ret;
+ const char *s;
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_message **res;
+ struct dom_sid *domain_sid;
+ NTSTATUS status;
+
+ tmp_ctx = talloc_new(sidmap);
+
+ ret = gendb_search(sidmap->samctx, tmp_ctx, NULL, &res, attrs,
+ "objectSid=%s",
+ ldap_encode_ndr_dom_sid(tmp_ctx, sid));
+
+ if (ret != 1) {
+ goto allocated_sid;
+ }
+
+ /* make sure its a user, not a group */
+ if (!is_user_account(res[0])) {
+ DEBUG(0,("sid_to_unixuid: sid %s is not an account!\n",
+ dom_sid_string(tmp_ctx, sid)));
+ talloc_free(tmp_ctx);
+ return NT_STATUS_INVALID_SID;
+ }
+
+ /* first try to get the uid directly */
+ s = samdb_result_string(res[0], "uidNumber", NULL);
+ if (s != NULL) {
+ *uid = strtoul(s, NULL, 0);
+ talloc_free(tmp_ctx);
+ return NT_STATUS_OK;
+ }
+
+ /* next try via the UnixName attribute */
+ s = samdb_result_string(res[0], "unixName", NULL);
+ if (s != NULL) {
+ struct passwd *pwd = getpwnam(s);
+ if (!pwd) {
+ DEBUG(0,("unixName %s for sid %s does not exist as a local user\n", s,
+ dom_sid_string(tmp_ctx, sid)));
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+ *uid = pwd->pw_uid;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_OK;
+ }
+
+ /* finally try via the sAMAccountName attribute */
+ s = samdb_result_string(res[0], "sAMAccountName", NULL);
+ if (s != NULL) {
+ struct passwd *pwd = getpwnam(s);
+ if (!pwd) {
+ DEBUG(0,("sAMAccountName '%s' for sid %s does not exist as a local user\n",
+ s, dom_sid_string(tmp_ctx, sid)));
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_SUCH_USER;
+ }
+ *uid = pwd->pw_uid;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_OK;
+ }
+
+
+allocated_sid:
+ status = sidmap_primary_domain_sid(sidmap, tmp_ctx, &domain_sid);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ if (dom_sid_in_domain(domain_sid, sid)) {
+ uint32_t rid = sid->sub_auths[sid->num_auths-1];
+ if (rid >= SIDMAP_LOCAL_USER_BASE &&
+ rid < SIDMAP_LOCAL_GROUP_BASE) {
+ *uid = rid - SIDMAP_LOCAL_USER_BASE;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_OK;
+ }
+ }
+
+
+ DEBUG(0,("sid_to_unixuid: no uidNumber, unixName or sAMAccountName for sid %s\n",
+ dom_sid_string(tmp_ctx, sid)));
+
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NONE_MAPPED;
+}
+
+
+/*
+ see if a sid is a group - very inefficient!
+*/
+bool sidmap_sid_is_group(struct sidmap_context *sidmap, struct dom_sid *sid)
+{
+ const char *attrs[] = { "sAMAccountType", NULL };
+ int ret;
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_message **res;
+ NTSTATUS status;
+ struct dom_sid *domain_sid;
+ bool is_group;
+
+ tmp_ctx = talloc_new(sidmap);
+
+ ret = gendb_search(sidmap->samctx, tmp_ctx, NULL, &res, attrs,
+ "objectSid=%s", ldap_encode_ndr_dom_sid(tmp_ctx, sid));
+ if (ret == 1) {
+ is_group = is_group_account(res[0]);
+ talloc_free(tmp_ctx);
+ return is_group;
+ }
+
+ status = sidmap_primary_domain_sid(sidmap, tmp_ctx, &domain_sid);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ return false;
+ }
+
+ if (dom_sid_in_domain(domain_sid, sid)) {
+ uint32_t rid = sid->sub_auths[sid->num_auths-1];
+ if (rid >= SIDMAP_LOCAL_GROUP_BASE) {
+ talloc_free(tmp_ctx);
+ return true;
+ }
+ }
+
+ talloc_free(tmp_ctx);
+ return false;
+}
+
+/*
+ map a sid to a unix gid
+*/
+NTSTATUS sidmap_sid_to_unixgid(struct sidmap_context *sidmap,
+ const struct dom_sid *sid, gid_t *gid)
+{
+ const char *attrs[] = { "sAMAccountName", "gidNumber",
+ "unixName", "sAMAccountType", NULL };
+ int ret;
+ const char *s;
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_message **res;
+ NTSTATUS status;
+ struct dom_sid *domain_sid;
+
+ tmp_ctx = talloc_new(sidmap);
+
+ ret = gendb_search(sidmap->samctx, tmp_ctx, NULL, &res, attrs,
+ "objectSid=%s", ldap_encode_ndr_dom_sid(tmp_ctx, sid));
+ if (ret != 1) {
+ goto allocated_sid;
+ }
+
+ /* make sure its not a user */
+ if (!is_group_account(res[0])) {
+ DEBUG(0,("sid_to_unixgid: sid %s is a ATYPE_NORMAL_ACCOUNT\n",
+ dom_sid_string(tmp_ctx, sid)));
+ talloc_free(tmp_ctx);
+ return NT_STATUS_INVALID_SID;
+ }
+
+ /* first try to get the gid directly */
+ s = samdb_result_string(res[0], "gidNumber", NULL);
+ if (s != NULL) {
+ *gid = strtoul(s, NULL, 0);
+ talloc_free(tmp_ctx);
+ return NT_STATUS_OK;
+ }
+
+ /* next try via the UnixName attribute */
+ s = samdb_result_string(res[0], "unixName", NULL);
+ if (s != NULL) {
+ struct group *grp = getgrnam(s);
+ if (!grp) {
+ DEBUG(0,("unixName '%s' for sid %s does not exist as a local group\n",
+ s, dom_sid_string(tmp_ctx, sid)));
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+ *gid = grp->gr_gid;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_OK;
+ }
+
+ /* finally try via the sAMAccountName attribute */
+ s = samdb_result_string(res[0], "sAMAccountName", NULL);
+ if (s != NULL) {
+ struct group *grp = getgrnam(s);
+ if (!grp) {
+ DEBUG(0,("sAMAccountName '%s' for sid %s does not exist as a local group\n", s, dom_sid_string(tmp_ctx, sid)));
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+ *gid = grp->gr_gid;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_OK;
+ }
+
+allocated_sid:
+ status = sidmap_primary_domain_sid(sidmap, tmp_ctx, &domain_sid);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ if (dom_sid_in_domain(domain_sid, sid)) {
+ uint32_t rid = sid->sub_auths[sid->num_auths-1];
+ if (rid >= SIDMAP_LOCAL_GROUP_BASE) {
+ *gid = rid - SIDMAP_LOCAL_GROUP_BASE;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_OK;
+ }
+ }
+
+ DEBUG(0,("sid_to_unixgid: no gidNumber, unixName or sAMAccountName for sid %s\n",
+ dom_sid_string(tmp_ctx, sid)));
+
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NONE_MAPPED;
+}
+
+
+/*
+ map a unix uid to a dom_sid
+ the returned sid is allocated in the supplied mem_ctx
+*/
+NTSTATUS sidmap_uid_to_sid(struct sidmap_context *sidmap,
+ TALLOC_CTX *mem_ctx,
+ const uid_t uid, struct dom_sid **sid)
+{
+ const char *attrs[] = { "sAMAccountName", "objectSid", "sAMAccountType", NULL };
+ int ret, i;
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_message **res;
+ struct passwd *pwd;
+ struct dom_sid *domain_sid;
+ NTSTATUS status;
+
+ /*
+ we search for the mapping in the following order:
+
+ - check if the uid is in the dynamic uid range assigned for winbindd
+ use. If it is, then look in winbindd sid mapping
+ database (not implemented yet)
+ - look for a user account in samdb that has uidNumber set to the
+ given uid
+ - look for a user account in samdb that has unixName or
+ sAMAccountName set to the name given by getpwuid()
+ - assign a SID by adding the uid to SIDMAP_LOCAL_USER_BASE in the local
+ domain
+ */
+
+
+ tmp_ctx = talloc_new(mem_ctx);
+
+
+ /*
+ step 2: look for a user account in samdb that has uidNumber set to the
+ given uid
+ */
+
+ ret = gendb_search(sidmap->samctx, tmp_ctx, NULL, &res, attrs,
+ "uidNumber=%u", (unsigned int)uid);
+ for (i=0;i<ret;i++) {
+ if (!is_user_account(res[i])) continue;
+
+ *sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
+ talloc_free(tmp_ctx);
+ NT_STATUS_HAVE_NO_MEMORY(*sid);
+ return NT_STATUS_OK;
+ }
+
+ /*
+ step 3: look for a user account in samdb that has unixName
+ or sAMAccountName set to the name given by getpwuid()
+ */
+ pwd = getpwuid(uid);
+ if (pwd == NULL) {
+ goto allocate_sid;
+ }
+
+ ret = gendb_search(sidmap->samctx, tmp_ctx, NULL, &res, attrs,
+ "(|(unixName=%s)(sAMAccountName=%s))",
+ pwd->pw_name, pwd->pw_name);
+ for (i=0;i<ret;i++) {
+ if (!is_user_account(res[i])) continue;
+
+ *sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
+ talloc_free(tmp_ctx);
+ NT_STATUS_HAVE_NO_MEMORY(*sid);
+ return NT_STATUS_OK;
+ }
+
+
+ /*
+ step 4: assign a SID by adding the uid to
+ SIDMAP_LOCAL_USER_BASE in the local domain
+ */
+allocate_sid:
+ if (uid > SIDMAP_MAX_LOCAL_UID) {
+ return NT_STATUS_NONE_MAPPED;
+ }
+
+ status = sidmap_primary_domain_sid(sidmap, tmp_ctx, &domain_sid);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ *sid = dom_sid_add_rid(mem_ctx, domain_sid, SIDMAP_LOCAL_USER_BASE + uid);
+ talloc_free(tmp_ctx);
+
+ if (*sid == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ map a unix gid to a dom_sid
+ the returned sid is allocated in the supplied mem_ctx
+*/
+NTSTATUS sidmap_gid_to_sid(struct sidmap_context *sidmap,
+ TALLOC_CTX *mem_ctx,
+ const gid_t gid, struct dom_sid **sid)
+{
+ const char *attrs[] = { "sAMAccountName", "objectSid", "sAMAccountType", NULL };
+ int ret, i;
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_message **res;
+ struct group *grp;
+ struct dom_sid *domain_sid;
+ NTSTATUS status;
+
+ /*
+ we search for the mapping in the following order:
+
+ - check if the gid is in the dynamic gid range assigned for winbindd
+ use. If it is, then look in winbindd sid mapping
+ database (not implemented yet)
+ - look for a group account in samdb that has gidNumber set to the
+ given gid
+ - look for a group account in samdb that has unixName or
+ sAMAccountName set to the name given by getgrgid()
+ - assign a SID by adding the gid to SIDMAP_LOCAL_GROUP_BASE in the local
+ domain
+ */
+
+
+ tmp_ctx = talloc_new(sidmap);
+
+
+ /*
+ step 2: look for a group account in samdb that has gidNumber set to the
+ given gid
+ */
+
+ ret = gendb_search(sidmap->samctx, tmp_ctx, NULL, &res, attrs,
+ "gidNumber=%u", (unsigned int)gid);
+ for (i=0;i<ret;i++) {
+ if (!is_group_account(res[i])) continue;
+
+ *sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
+ talloc_free(tmp_ctx);
+ NT_STATUS_HAVE_NO_MEMORY(*sid);
+ return NT_STATUS_OK;
+ }
+
+ /*
+ step 3: look for a group account in samdb that has unixName
+ or sAMAccountName set to the name given by getgrgid()
+ */
+ grp = getgrgid(gid);
+ if (grp == NULL) {
+ goto allocate_sid;
+ }
+
+ ret = gendb_search(sidmap->samctx, tmp_ctx, NULL, &res, attrs,
+ "(|(unixName=%s)(sAMAccountName=%s))",
+ grp->gr_name, grp->gr_name);
+ for (i=0;i<ret;i++) {
+ if (!is_group_account(res[i])) continue;
+
+ *sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
+ talloc_free(tmp_ctx);
+ NT_STATUS_HAVE_NO_MEMORY(*sid);
+ return NT_STATUS_OK;
+ }
+
+
+ /*
+ step 4: assign a SID by adding the gid to
+ SIDMAP_LOCAL_GROUP_BASE in the local domain
+ */
+allocate_sid:
+ if (gid > SIDMAP_MAX_LOCAL_GID) {
+ return NT_STATUS_NONE_MAPPED;
+ }
+
+ status = sidmap_primary_domain_sid(sidmap, tmp_ctx, &domain_sid);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ *sid = dom_sid_add_rid(mem_ctx, domain_sid, SIDMAP_LOCAL_GROUP_BASE + gid);
+ talloc_free(tmp_ctx);
+
+ if (*sid == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ check if a sid is in the range of auto-allocated SIDs from our primary domain,
+ and if it is, then return the name and atype
+*/
+NTSTATUS sidmap_allocated_sid_lookup(struct sidmap_context *sidmap,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *sid,
+ const char **name,
+ enum lsa_SidType *rtype)
+{
+ NTSTATUS status;
+ struct dom_sid *domain_sid;
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+ uint32_t rid, atype;
+
+ status = sidmap_primary_domain_sid(sidmap, tmp_ctx, &domain_sid);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ if (!dom_sid_in_domain(domain_sid, sid)) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NONE_MAPPED;
+ }
+
+ talloc_free(tmp_ctx);
+
+ rid = sid->sub_auths[sid->num_auths-1];
+ if (rid < SIDMAP_LOCAL_USER_BASE) {
+ return NT_STATUS_NONE_MAPPED;
+ }
+
+ if (rid < SIDMAP_LOCAL_GROUP_BASE) {
+ struct passwd *pwd;
+ uid_t uid = rid - SIDMAP_LOCAL_USER_BASE;
+ atype = ATYPE_NORMAL_ACCOUNT;
+ *rtype = samdb_atype_map(atype);
+
+ pwd = getpwuid(uid);
+ if (pwd == NULL) {
+ *name = talloc_asprintf(mem_ctx, "uid%u", uid);
+ } else {
+ *name = talloc_strdup(mem_ctx, pwd->pw_name);
+ }
+ } else {
+ struct group *grp;
+ gid_t gid = rid - SIDMAP_LOCAL_GROUP_BASE;
+ atype = ATYPE_LOCAL_GROUP;
+ *rtype = samdb_atype_map(atype);
+ grp = getgrgid(gid);
+ if (grp == NULL) {
+ *name = talloc_asprintf(mem_ctx, "gid%u", gid);
+ } else {
+ *name = talloc_strdup(mem_ctx, grp->gr_name);
+ }
+ }
+
+ if (*name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/dsdb/common/util.c b/source4/dsdb/common/util.c
new file mode 100644
index 0000000000..19eb3433a9
--- /dev/null
+++ b/source4/dsdb/common/util.c
@@ -0,0 +1,2036 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+
+ Copyright (C) Andrew Tridgell 2004
+ Copyright (C) Volker Lendecke 2004
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2006
+ Copyright (C) Jelmer Vernooij <jelmer@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "events/events.h"
+#include "ldb.h"
+#include "ldb_errors.h"
+#include "../lib/util/util_ldb.h"
+#include "../lib/crypto/crypto.h"
+#include "dsdb/samdb/samdb.h"
+#include "libcli/security/security.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "dsdb/common/flags.h"
+#include "dsdb/common/proto.h"
+#include "libcli/ldap/ldap_ndr.h"
+#include "param/param.h"
+#include "libcli/auth/libcli_auth.h"
+
+/*
+ search the sam for the specified attributes in a specific domain, filter on
+ objectSid being in domain_sid.
+*/
+int samdb_search_domain(struct ldb_context *sam_ldb,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_dn *basedn,
+ struct ldb_message ***res,
+ const char * const *attrs,
+ const struct dom_sid *domain_sid,
+ const char *format, ...) _PRINTF_ATTRIBUTE(7,8)
+{
+ va_list ap;
+ int i, count;
+
+ va_start(ap, format);
+ count = gendb_search_v(sam_ldb, mem_ctx, basedn,
+ res, attrs, format, ap);
+ va_end(ap);
+
+ i=0;
+
+ while (i<count) {
+ struct dom_sid *entry_sid;
+
+ entry_sid = samdb_result_dom_sid(mem_ctx, (*res)[i], "objectSid");
+
+ if ((entry_sid == NULL) ||
+ (!dom_sid_in_domain(domain_sid, entry_sid))) {
+ /* Delete that entry from the result set */
+ (*res)[i] = (*res)[count-1];
+ count -= 1;
+ talloc_free(entry_sid);
+ continue;
+ }
+ talloc_free(entry_sid);
+ i += 1;
+ }
+
+ return count;
+}
+
+/*
+ search the sam for a single string attribute in exactly 1 record
+*/
+const char *samdb_search_string_v(struct ldb_context *sam_ldb,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_dn *basedn,
+ const char *attr_name,
+ const char *format, va_list ap) _PRINTF_ATTRIBUTE(5,0)
+{
+ int count;
+ const char *attrs[2] = { NULL, NULL };
+ struct ldb_message **res = NULL;
+
+ attrs[0] = attr_name;
+
+ count = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, attrs, format, ap);
+ if (count > 1) {
+ DEBUG(1,("samdb: search for %s %s not single valued (count=%d)\n",
+ attr_name, format, count));
+ }
+ if (count != 1) {
+ talloc_free(res);
+ return NULL;
+ }
+
+ return samdb_result_string(res[0], attr_name, NULL);
+}
+
+
+/*
+ search the sam for a single string attribute in exactly 1 record
+*/
+const char *samdb_search_string(struct ldb_context *sam_ldb,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_dn *basedn,
+ const char *attr_name,
+ const char *format, ...) _PRINTF_ATTRIBUTE(5,6)
+{
+ va_list ap;
+ const char *str;
+
+ va_start(ap, format);
+ str = samdb_search_string_v(sam_ldb, mem_ctx, basedn, attr_name, format, ap);
+ va_end(ap);
+
+ return str;
+}
+
+struct ldb_dn *samdb_search_dn(struct ldb_context *sam_ldb,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_dn *basedn,
+ const char *format, ...) _PRINTF_ATTRIBUTE(4,5)
+{
+ va_list ap;
+ struct ldb_dn *ret;
+ struct ldb_message **res = NULL;
+ int count;
+
+ va_start(ap, format);
+ count = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, NULL, format, ap);
+ va_end(ap);
+
+ if (count != 1) return NULL;
+
+ ret = talloc_steal(mem_ctx, res[0]->dn);
+ talloc_free(res);
+
+ return ret;
+}
+
+/*
+ search the sam for a dom_sid attribute in exactly 1 record
+*/
+struct dom_sid *samdb_search_dom_sid(struct ldb_context *sam_ldb,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_dn *basedn,
+ const char *attr_name,
+ const char *format, ...) _PRINTF_ATTRIBUTE(5,6)
+{
+ va_list ap;
+ int count;
+ struct ldb_message **res;
+ const char *attrs[2] = { NULL, NULL };
+ struct dom_sid *sid;
+
+ attrs[0] = attr_name;
+
+ va_start(ap, format);
+ count = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, attrs, format, ap);
+ va_end(ap);
+ if (count > 1) {
+ DEBUG(1,("samdb: search for %s %s not single valued (count=%d)\n",
+ attr_name, format, count));
+ }
+ if (count != 1) {
+ talloc_free(res);
+ return NULL;
+ }
+ sid = samdb_result_dom_sid(mem_ctx, res[0], attr_name);
+ talloc_free(res);
+ return sid;
+}
+
+/*
+ return the count of the number of records in the sam matching the query
+*/
+int samdb_search_count(struct ldb_context *sam_ldb,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_dn *basedn,
+ const char *format, ...) _PRINTF_ATTRIBUTE(4,5)
+{
+ va_list ap;
+ struct ldb_message **res;
+ const char * const attrs[] = { NULL };
+ int ret;
+
+ va_start(ap, format);
+ ret = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, attrs, format, ap);
+ va_end(ap);
+
+ return ret;
+}
+
+
+/*
+ search the sam for a single integer attribute in exactly 1 record
+*/
+uint_t samdb_search_uint(struct ldb_context *sam_ldb,
+ TALLOC_CTX *mem_ctx,
+ uint_t default_value,
+ struct ldb_dn *basedn,
+ const char *attr_name,
+ const char *format, ...) _PRINTF_ATTRIBUTE(6,7)
+{
+ va_list ap;
+ int count;
+ struct ldb_message **res;
+ const char *attrs[2] = { NULL, NULL };
+
+ attrs[0] = attr_name;
+
+ va_start(ap, format);
+ count = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, attrs, format, ap);
+ va_end(ap);
+
+ if (count != 1) {
+ return default_value;
+ }
+
+ return samdb_result_uint(res[0], attr_name, default_value);
+}
+
+/*
+ search the sam for a single signed 64 bit integer attribute in exactly 1 record
+*/
+int64_t samdb_search_int64(struct ldb_context *sam_ldb,
+ TALLOC_CTX *mem_ctx,
+ int64_t default_value,
+ struct ldb_dn *basedn,
+ const char *attr_name,
+ const char *format, ...) _PRINTF_ATTRIBUTE(6,7)
+{
+ va_list ap;
+ int count;
+ struct ldb_message **res;
+ const char *attrs[2] = { NULL, NULL };
+
+ attrs[0] = attr_name;
+
+ va_start(ap, format);
+ count = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, attrs, format, ap);
+ va_end(ap);
+
+ if (count != 1) {
+ return default_value;
+ }
+
+ return samdb_result_int64(res[0], attr_name, default_value);
+}
+
+/*
+ search the sam for multipe records each giving a single string attribute
+ return the number of matches, or -1 on error
+*/
+int samdb_search_string_multiple(struct ldb_context *sam_ldb,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_dn *basedn,
+ const char ***strs,
+ const char *attr_name,
+ const char *format, ...) _PRINTF_ATTRIBUTE(6,7)
+{
+ va_list ap;
+ int count, i;
+ const char *attrs[2] = { NULL, NULL };
+ struct ldb_message **res = NULL;
+
+ attrs[0] = attr_name;
+
+ va_start(ap, format);
+ count = gendb_search_v(sam_ldb, mem_ctx, basedn, &res, attrs, format, ap);
+ va_end(ap);
+
+ if (count <= 0) {
+ return count;
+ }
+
+ /* make sure its single valued */
+ for (i=0;i<count;i++) {
+ if (res[i]->num_elements != 1) {
+ DEBUG(1,("samdb: search for %s %s not single valued\n",
+ attr_name, format));
+ talloc_free(res);
+ return -1;
+ }
+ }
+
+ *strs = talloc_array(mem_ctx, const char *, count+1);
+ if (! *strs) {
+ talloc_free(res);
+ return -1;
+ }
+
+ for (i=0;i<count;i++) {
+ (*strs)[i] = samdb_result_string(res[i], attr_name, NULL);
+ }
+ (*strs)[count] = NULL;
+
+ return count;
+}
+
+/*
+ pull a uint from a result set.
+*/
+uint_t samdb_result_uint(const struct ldb_message *msg, const char *attr, uint_t default_value)
+{
+ return ldb_msg_find_attr_as_uint(msg, attr, default_value);
+}
+
+/*
+ pull a (signed) int64 from a result set.
+*/
+int64_t samdb_result_int64(const struct ldb_message *msg, const char *attr, int64_t default_value)
+{
+ return ldb_msg_find_attr_as_int64(msg, attr, default_value);
+}
+
+/*
+ pull a string from a result set.
+*/
+const char *samdb_result_string(const struct ldb_message *msg, const char *attr,
+ const char *default_value)
+{
+ return ldb_msg_find_attr_as_string(msg, attr, default_value);
+}
+
+struct ldb_dn *samdb_result_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, const struct ldb_message *msg,
+ const char *attr, struct ldb_dn *default_value)
+{
+ struct ldb_dn *ret_dn = ldb_msg_find_attr_as_dn(ldb, mem_ctx, msg, attr);
+ if (!ret_dn) {
+ return default_value;
+ }
+ return ret_dn;
+}
+
+/*
+ pull a rid from a objectSid in a result set.
+*/
+uint32_t samdb_result_rid_from_sid(TALLOC_CTX *mem_ctx, const struct ldb_message *msg,
+ const char *attr, uint32_t default_value)
+{
+ struct dom_sid *sid;
+ uint32_t rid;
+
+ sid = samdb_result_dom_sid(mem_ctx, msg, attr);
+ if (sid == NULL) {
+ return default_value;
+ }
+ rid = sid->sub_auths[sid->num_auths-1];
+ talloc_free(sid);
+ return rid;
+}
+
+/*
+ pull a dom_sid structure from a objectSid in a result set.
+*/
+struct dom_sid *samdb_result_dom_sid(TALLOC_CTX *mem_ctx, const struct ldb_message *msg,
+ const char *attr)
+{
+ const struct ldb_val *v;
+ struct dom_sid *sid;
+ enum ndr_err_code ndr_err;
+ v = ldb_msg_find_ldb_val(msg, attr);
+ if (v == NULL) {
+ return NULL;
+ }
+ sid = talloc(mem_ctx, struct dom_sid);
+ if (sid == NULL) {
+ return NULL;
+ }
+ ndr_err = ndr_pull_struct_blob(v, sid, NULL, sid,
+ (ndr_pull_flags_fn_t)ndr_pull_dom_sid);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ talloc_free(sid);
+ return NULL;
+ }
+ return sid;
+}
+
+/*
+ pull a guid structure from a objectGUID in a result set.
+*/
+struct GUID samdb_result_guid(const struct ldb_message *msg, const char *attr)
+{
+ const struct ldb_val *v;
+ enum ndr_err_code ndr_err;
+ struct GUID guid;
+ TALLOC_CTX *mem_ctx;
+
+ ZERO_STRUCT(guid);
+
+ v = ldb_msg_find_ldb_val(msg, attr);
+ if (!v) return guid;
+
+ mem_ctx = talloc_named_const(NULL, 0, "samdb_result_guid");
+ if (!mem_ctx) return guid;
+ ndr_err = ndr_pull_struct_blob(v, mem_ctx, NULL, &guid,
+ (ndr_pull_flags_fn_t)ndr_pull_GUID);
+ talloc_free(mem_ctx);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return guid;
+ }
+
+ return guid;
+}
+
+/*
+ pull a sid prefix from a objectSid in a result set.
+ this is used to find the domain sid for a user
+*/
+struct dom_sid *samdb_result_sid_prefix(TALLOC_CTX *mem_ctx, const struct ldb_message *msg,
+ const char *attr)
+{
+ struct dom_sid *sid = samdb_result_dom_sid(mem_ctx, msg, attr);
+ if (!sid || sid->num_auths < 1) return NULL;
+ sid->num_auths--;
+ return sid;
+}
+
+/*
+ pull a NTTIME in a result set.
+*/
+NTTIME samdb_result_nttime(struct ldb_message *msg, const char *attr, NTTIME default_value)
+{
+ return ldb_msg_find_attr_as_uint64(msg, attr, default_value);
+}
+
+/*
+ * Windows uses both 0 and 9223372036854775807 (0x7FFFFFFFFFFFFFFFULL) to
+ * indicate an account doesn't expire.
+ *
+ * When Windows initially creates an account, it sets
+ * accountExpires = 9223372036854775807 (0x7FFFFFFFFFFFFFFF). However,
+ * when changing from an account having a specific expiration date to
+ * that account never expiring, it sets accountExpires = 0.
+ *
+ * Consolidate that logic here to allow clearer logic for account expiry in
+ * the rest of the code.
+ */
+NTTIME samdb_result_account_expires(struct ldb_message *msg)
+{
+ NTTIME ret = ldb_msg_find_attr_as_uint64(msg, "accountExpires",
+ 0);
+
+ if (ret == 0)
+ ret = 0x7FFFFFFFFFFFFFFFULL;
+
+ return ret;
+}
+
+/*
+ pull a uint64_t from a result set.
+*/
+uint64_t samdb_result_uint64(struct ldb_message *msg, const char *attr, uint64_t default_value)
+{
+ return ldb_msg_find_attr_as_uint64(msg, attr, default_value);
+}
+
+
+/*
+ construct the allow_password_change field from the PwdLastSet attribute and the
+ domain password settings
+*/
+NTTIME samdb_result_allow_password_change(struct ldb_context *sam_ldb,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_dn *domain_dn,
+ struct ldb_message *msg,
+ const char *attr)
+{
+ uint64_t attr_time = samdb_result_uint64(msg, attr, 0);
+ int64_t minPwdAge;
+
+ if (attr_time == 0) {
+ return 0;
+ }
+
+ minPwdAge = samdb_search_int64(sam_ldb, mem_ctx, 0, domain_dn, "minPwdAge", NULL);
+
+ /* yes, this is a -= not a += as minPwdAge is stored as the negative
+ of the number of 100-nano-seconds */
+ attr_time -= minPwdAge;
+
+ return attr_time;
+}
+
+/*
+ construct the force_password_change field from the PwdLastSet
+ attribute, the userAccountControl and the domain password settings
+*/
+NTTIME samdb_result_force_password_change(struct ldb_context *sam_ldb,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_dn *domain_dn,
+ struct ldb_message *msg)
+{
+ uint64_t attr_time = samdb_result_uint64(msg, "pwdLastSet", 0);
+ uint32_t userAccountControl = samdb_result_uint64(msg, "userAccountControl", 0);
+ int64_t maxPwdAge;
+
+ /* Machine accounts don't expire, and there is a flag for 'no expiry' */
+ if (!(userAccountControl & UF_NORMAL_ACCOUNT)
+ || (userAccountControl & UF_DONT_EXPIRE_PASSWD)) {
+ return 0x7FFFFFFFFFFFFFFFULL;
+ }
+
+ if (attr_time == 0) {
+ return 0;
+ }
+
+ maxPwdAge = samdb_search_int64(sam_ldb, mem_ctx, 0, domain_dn, "maxPwdAge", NULL);
+ if (maxPwdAge == 0) {
+ return 0x7FFFFFFFFFFFFFFFULL;
+ } else {
+ attr_time -= maxPwdAge;
+ }
+
+ return attr_time;
+}
+
+/*
+ pull a samr_Password structutre from a result set.
+*/
+struct samr_Password *samdb_result_hash(TALLOC_CTX *mem_ctx, struct ldb_message *msg, const char *attr)
+{
+ struct samr_Password *hash = NULL;
+ const struct ldb_val *val = ldb_msg_find_ldb_val(msg, attr);
+ if (val && (val->length >= sizeof(hash->hash))) {
+ hash = talloc(mem_ctx, struct samr_Password);
+ memcpy(hash->hash, val->data, MIN(val->length, sizeof(hash->hash)));
+ }
+ return hash;
+}
+
+/*
+ pull an array of samr_Password structutres from a result set.
+*/
+uint_t samdb_result_hashes(TALLOC_CTX *mem_ctx, struct ldb_message *msg,
+ const char *attr, struct samr_Password **hashes)
+{
+ uint_t count = 0;
+ const struct ldb_val *val = ldb_msg_find_ldb_val(msg, attr);
+ int i;
+
+ *hashes = NULL;
+ if (!val) {
+ return 0;
+ }
+ count = val->length / 16;
+ if (count == 0) {
+ return 0;
+ }
+
+ *hashes = talloc_array(mem_ctx, struct samr_Password, count);
+ if (! *hashes) {
+ return 0;
+ }
+
+ for (i=0;i<count;i++) {
+ memcpy((*hashes)[i].hash, (i*16)+(char *)val->data, 16);
+ }
+
+ return count;
+}
+
+NTSTATUS samdb_result_passwords(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx, struct ldb_message *msg,
+ struct samr_Password **lm_pwd, struct samr_Password **nt_pwd)
+{
+ struct samr_Password *lmPwdHash, *ntPwdHash;
+ if (nt_pwd) {
+ int num_nt;
+ num_nt = samdb_result_hashes(mem_ctx, msg, "unicodePwd", &ntPwdHash);
+ if (num_nt == 0) {
+ *nt_pwd = NULL;
+ } else if (num_nt > 1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ } else {
+ *nt_pwd = &ntPwdHash[0];
+ }
+ }
+ if (lm_pwd) {
+ /* Ensure that if we have turned off LM
+ * authentication, that we never use the LM hash, even
+ * if we store it */
+ if (lp_lanman_auth(lp_ctx)) {
+ int num_lm;
+ num_lm = samdb_result_hashes(mem_ctx, msg, "dBCSPwd", &lmPwdHash);
+ if (num_lm == 0) {
+ *lm_pwd = NULL;
+ } else if (num_lm > 1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ } else {
+ *lm_pwd = &lmPwdHash[0];
+ }
+ } else {
+ *lm_pwd = NULL;
+ }
+ }
+ return NT_STATUS_OK;
+}
+
+/*
+ pull a samr_LogonHours structutre from a result set.
+*/
+struct samr_LogonHours samdb_result_logon_hours(TALLOC_CTX *mem_ctx, struct ldb_message *msg, const char *attr)
+{
+ struct samr_LogonHours hours;
+ const int units_per_week = 168;
+ const struct ldb_val *val = ldb_msg_find_ldb_val(msg, attr);
+ ZERO_STRUCT(hours);
+ hours.bits = talloc_array(mem_ctx, uint8_t, units_per_week);
+ if (!hours.bits) {
+ return hours;
+ }
+ hours.units_per_week = units_per_week;
+ memset(hours.bits, 0xFF, units_per_week);
+ if (val) {
+ memcpy(hours.bits, val->data, MIN(val->length, units_per_week));
+ }
+ return hours;
+}
+
+/*
+ pull a set of account_flags from a result set.
+
+ This requires that the attributes:
+ pwdLastSet
+ userAccountControl
+ be included in 'msg'
+*/
+uint32_t samdb_result_acct_flags(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
+ struct ldb_message *msg, struct ldb_dn *domain_dn)
+{
+ uint32_t userAccountControl = ldb_msg_find_attr_as_uint(msg, "userAccountControl", 0);
+ uint32_t acct_flags = samdb_uf2acb(userAccountControl);
+ NTTIME must_change_time;
+ NTTIME now;
+
+ must_change_time = samdb_result_force_password_change(sam_ctx, mem_ctx,
+ domain_dn, msg);
+
+ /* Test account expire time */
+ unix_to_nt_time(&now, time(NULL));
+ /* check for expired password */
+ if (must_change_time < now) {
+ acct_flags |= ACB_PW_EXPIRED;
+ }
+ return acct_flags;
+}
+
+struct lsa_BinaryString samdb_result_parameters(TALLOC_CTX *mem_ctx,
+ struct ldb_message *msg,
+ const char *attr)
+{
+ struct lsa_BinaryString s;
+ const struct ldb_val *val = ldb_msg_find_ldb_val(msg, attr);
+
+ ZERO_STRUCT(s);
+
+ if (!val) {
+ return s;
+ }
+
+ s.array = talloc_array(mem_ctx, uint16_t, val->length/2);
+ if (!s.array) {
+ return s;
+ }
+ s.length = s.size = val->length/2;
+ memcpy(s.array, val->data, val->length);
+
+ return s;
+}
+
+/* Find an attribute, with a particular value */
+
+/* The current callers of this function expect a very specific
+ * behaviour: In particular, objectClass subclass equivilance is not
+ * wanted. This means that we should not lookup the schema for the
+ * comparison function */
+struct ldb_message_element *samdb_find_attribute(struct ldb_context *ldb,
+ const struct ldb_message *msg,
+ const char *name, const char *value)
+{
+ int i;
+ struct ldb_message_element *el = ldb_msg_find_element(msg, name);
+
+ if (!el) {
+ return NULL;
+ }
+
+ for (i=0;i<el->num_values;i++) {
+ if (ldb_attr_cmp(value, (char *)el->values[i].data) == 0) {
+ return el;
+ }
+ }
+
+ return NULL;
+}
+
+int samdb_find_or_add_value(struct ldb_context *ldb, struct ldb_message *msg, const char *name, const char *set_value)
+{
+ if (samdb_find_attribute(ldb, msg, name, set_value) == NULL) {
+ return samdb_msg_add_string(ldb, msg, msg, name, set_value);
+ }
+ return LDB_SUCCESS;
+}
+
+int samdb_find_or_add_attribute(struct ldb_context *ldb, struct ldb_message *msg, const char *name, const char *set_value)
+{
+ struct ldb_message_element *el;
+
+ el = ldb_msg_find_element(msg, name);
+ if (el) {
+ return LDB_SUCCESS;
+ }
+
+ return samdb_msg_add_string(ldb, msg, msg, name, set_value);
+}
+
+
+
+/*
+ add a string element to a message
+*/
+int samdb_msg_add_string(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
+ const char *attr_name, const char *str)
+{
+ char *s = talloc_strdup(mem_ctx, str);
+ char *a = talloc_strdup(mem_ctx, attr_name);
+ if (s == NULL || a == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ return ldb_msg_add_string(msg, a, s);
+}
+
+/*
+ add a dom_sid element to a message
+*/
+int samdb_msg_add_dom_sid(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
+ const char *attr_name, struct dom_sid *sid)
+{
+ struct ldb_val v;
+ enum ndr_err_code ndr_err;
+
+ ndr_err = ndr_push_struct_blob(&v, mem_ctx,
+ lp_iconv_convenience(ldb_get_opaque(sam_ldb, "loadparm")),
+ sid,
+ (ndr_push_flags_fn_t)ndr_push_dom_sid);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return -1;
+ }
+ return ldb_msg_add_value(msg, attr_name, &v, NULL);
+}
+
+
+/*
+ add a delete element operation to a message
+*/
+int samdb_msg_add_delete(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
+ const char *attr_name)
+{
+ /* we use an empty replace rather than a delete, as it allows for
+ samdb_replace() to be used everywhere */
+ return ldb_msg_add_empty(msg, attr_name, LDB_FLAG_MOD_REPLACE, NULL);
+}
+
+/*
+ add a add attribute value to a message
+*/
+int samdb_msg_add_addval(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
+ const char *attr_name, const char *value)
+{
+ struct ldb_message_element *el;
+ char *a, *v;
+ int ret;
+ a = talloc_strdup(mem_ctx, attr_name);
+ if (a == NULL)
+ return -1;
+ v = talloc_strdup(mem_ctx, value);
+ if (v == NULL)
+ return -1;
+ ret = ldb_msg_add_string(msg, a, v);
+ if (ret != 0)
+ return ret;
+ el = ldb_msg_find_element(msg, a);
+ if (el == NULL)
+ return -1;
+ el->flags = LDB_FLAG_MOD_ADD;
+ return 0;
+}
+
+/*
+ add a delete attribute value to a message
+*/
+int samdb_msg_add_delval(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
+ const char *attr_name, const char *value)
+{
+ struct ldb_message_element *el;
+ char *a, *v;
+ int ret;
+ a = talloc_strdup(mem_ctx, attr_name);
+ if (a == NULL)
+ return -1;
+ v = talloc_strdup(mem_ctx, value);
+ if (v == NULL)
+ return -1;
+ ret = ldb_msg_add_string(msg, a, v);
+ if (ret != 0)
+ return ret;
+ el = ldb_msg_find_element(msg, a);
+ if (el == NULL)
+ return -1;
+ el->flags = LDB_FLAG_MOD_DELETE;
+ return 0;
+}
+
+/*
+ add a int element to a message
+*/
+int samdb_msg_add_int(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
+ const char *attr_name, int v)
+{
+ const char *s = talloc_asprintf(mem_ctx, "%d", v);
+ return samdb_msg_add_string(sam_ldb, mem_ctx, msg, attr_name, s);
+}
+
+/*
+ add a uint_t element to a message
+*/
+int samdb_msg_add_uint(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
+ const char *attr_name, uint_t v)
+{
+ const char *s = talloc_asprintf(mem_ctx, "%u", v);
+ return samdb_msg_add_string(sam_ldb, mem_ctx, msg, attr_name, s);
+}
+
+/*
+ add a (signed) int64_t element to a message
+*/
+int samdb_msg_add_int64(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
+ const char *attr_name, int64_t v)
+{
+ const char *s = talloc_asprintf(mem_ctx, "%lld", (long long)v);
+ return samdb_msg_add_string(sam_ldb, mem_ctx, msg, attr_name, s);
+}
+
+/*
+ add a uint64_t element to a message
+*/
+int samdb_msg_add_uint64(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
+ const char *attr_name, uint64_t v)
+{
+ const char *s = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)v);
+ return samdb_msg_add_string(sam_ldb, mem_ctx, msg, attr_name, s);
+}
+
+/*
+ add a samr_Password element to a message
+*/
+int samdb_msg_add_hash(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
+ const char *attr_name, struct samr_Password *hash)
+{
+ struct ldb_val val;
+ val.data = talloc_memdup(mem_ctx, hash->hash, 16);
+ if (!val.data) {
+ return -1;
+ }
+ val.length = 16;
+ return ldb_msg_add_value(msg, attr_name, &val, NULL);
+}
+
+/*
+ add a samr_Password array to a message
+*/
+int samdb_msg_add_hashes(TALLOC_CTX *mem_ctx, struct ldb_message *msg,
+ const char *attr_name, struct samr_Password *hashes, uint_t count)
+{
+ struct ldb_val val;
+ int i;
+ val.data = talloc_array_size(mem_ctx, 16, count);
+ val.length = count*16;
+ if (!val.data) {
+ return -1;
+ }
+ for (i=0;i<count;i++) {
+ memcpy(i*16 + (char *)val.data, hashes[i].hash, 16);
+ }
+ return ldb_msg_add_value(msg, attr_name, &val, NULL);
+}
+
+/*
+ add a acct_flags element to a message
+*/
+int samdb_msg_add_acct_flags(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
+ const char *attr_name, uint32_t v)
+{
+ return samdb_msg_add_uint(sam_ldb, mem_ctx, msg, attr_name, samdb_acb2uf(v));
+}
+
+/*
+ add a logon_hours element to a message
+*/
+int samdb_msg_add_logon_hours(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
+ const char *attr_name, struct samr_LogonHours *hours)
+{
+ struct ldb_val val;
+ val.length = hours->units_per_week / 8;
+ val.data = hours->bits;
+ return ldb_msg_add_value(msg, attr_name, &val, NULL);
+}
+
+/*
+ add a parameters element to a message
+*/
+int samdb_msg_add_parameters(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
+ const char *attr_name, struct lsa_BinaryString *parameters)
+{
+ struct ldb_val val;
+ val.length = parameters->length * 2;
+ val.data = (uint8_t *)parameters->array;
+ return ldb_msg_add_value(msg, attr_name, &val, NULL);
+}
+/*
+ add a general value element to a message
+*/
+int samdb_msg_add_value(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
+ const char *attr_name, const struct ldb_val *val)
+{
+ return ldb_msg_add_value(msg, attr_name, val, NULL);
+}
+
+/*
+ sets a general value element to a message
+*/
+int samdb_msg_set_value(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
+ const char *attr_name, const struct ldb_val *val)
+{
+ struct ldb_message_element *el;
+
+ el = ldb_msg_find_element(msg, attr_name);
+ if (el) {
+ el->num_values = 0;
+ }
+ return ldb_msg_add_value(msg, attr_name, val, NULL);
+}
+
+/*
+ set a string element in a message
+*/
+int samdb_msg_set_string(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
+ const char *attr_name, const char *str)
+{
+ struct ldb_message_element *el;
+
+ el = ldb_msg_find_element(msg, attr_name);
+ if (el) {
+ el->num_values = 0;
+ }
+ return samdb_msg_add_string(sam_ldb, mem_ctx, msg, attr_name, str);
+}
+
+/*
+ replace elements in a record
+*/
+int samdb_replace(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg)
+{
+ int i;
+
+ /* mark all the message elements as LDB_FLAG_MOD_REPLACE */
+ for (i=0;i<msg->num_elements;i++) {
+ msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
+ }
+
+ /* modify the samdb record */
+ return ldb_modify(sam_ldb, msg);
+}
+
+/*
+ return a default security descriptor
+*/
+struct security_descriptor *samdb_default_security_descriptor(TALLOC_CTX *mem_ctx)
+{
+ struct security_descriptor *sd;
+
+ sd = security_descriptor_initialise(mem_ctx);
+
+ return sd;
+}
+
+struct ldb_dn *samdb_base_dn(struct ldb_context *sam_ctx)
+{
+ return ldb_get_default_basedn(sam_ctx);
+}
+
+struct ldb_dn *samdb_config_dn(struct ldb_context *sam_ctx)
+{
+ return ldb_get_config_basedn(sam_ctx);
+}
+
+struct ldb_dn *samdb_schema_dn(struct ldb_context *sam_ctx)
+{
+ return ldb_get_schema_basedn(sam_ctx);
+}
+
+struct ldb_dn *samdb_root_dn(struct ldb_context *sam_ctx)
+{
+ return ldb_get_root_basedn(sam_ctx);
+}
+
+struct ldb_dn *samdb_partitions_dn(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx)
+{
+ struct ldb_dn *new_dn;
+
+ new_dn = ldb_dn_copy(mem_ctx, samdb_config_dn(sam_ctx));
+ if ( ! ldb_dn_add_child_fmt(new_dn, "CN=Partitions")) {
+ talloc_free(new_dn);
+ return NULL;
+ }
+ return new_dn;
+}
+
+struct ldb_dn *samdb_sites_dn(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx)
+{
+ struct ldb_dn *new_dn;
+
+ new_dn = ldb_dn_copy(mem_ctx, samdb_config_dn(sam_ctx));
+ if ( ! ldb_dn_add_child_fmt(new_dn, "CN=Sites")) {
+ talloc_free(new_dn);
+ return NULL;
+ }
+ return new_dn;
+}
+
+/*
+ work out the domain sid for the current open ldb
+*/
+const struct dom_sid *samdb_domain_sid(struct ldb_context *ldb)
+{
+ TALLOC_CTX *tmp_ctx;
+ const struct dom_sid *domain_sid;
+ const char *attrs[] = {
+ "objectSid",
+ NULL
+ };
+ struct ldb_result *res;
+ int ret;
+
+ /* see if we have a cached copy */
+ domain_sid = (struct dom_sid *)ldb_get_opaque(ldb, "cache.domain_sid");
+ if (domain_sid) {
+ return domain_sid;
+ }
+
+ tmp_ctx = talloc_new(ldb);
+ if (tmp_ctx == NULL) {
+ goto failed;
+ }
+
+ ret = ldb_search(ldb, tmp_ctx, &res, ldb_get_default_basedn(ldb), LDB_SCOPE_BASE, attrs, "objectSid=*");
+
+ if (ret != LDB_SUCCESS) {
+ goto failed;
+ }
+
+ if (res->count != 1) {
+ goto failed;
+ }
+
+ domain_sid = samdb_result_dom_sid(tmp_ctx, res->msgs[0], "objectSid");
+ if (domain_sid == NULL) {
+ goto failed;
+ }
+
+ /* cache the domain_sid in the ldb */
+ if (ldb_set_opaque(ldb, "cache.domain_sid", discard_const_p(struct dom_sid, domain_sid)) != LDB_SUCCESS) {
+ goto failed;
+ }
+
+ talloc_steal(ldb, domain_sid);
+ talloc_free(tmp_ctx);
+
+ return domain_sid;
+
+failed:
+ DEBUG(1,("Failed to find domain_sid for open ldb\n"));
+ talloc_free(tmp_ctx);
+ return NULL;
+}
+
+bool samdb_set_domain_sid(struct ldb_context *ldb, const struct dom_sid *dom_sid_in)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct dom_sid *dom_sid_new;
+ struct dom_sid *dom_sid_old;
+
+ /* see if we have a cached copy */
+ dom_sid_old = talloc_get_type(ldb_get_opaque(ldb,
+ "cache.domain_sid"), struct dom_sid);
+
+ tmp_ctx = talloc_new(ldb);
+ if (tmp_ctx == NULL) {
+ goto failed;
+ }
+
+ dom_sid_new = dom_sid_dup(tmp_ctx, dom_sid_in);
+ if (!dom_sid_new) {
+ goto failed;
+ }
+
+ /* cache the domain_sid in the ldb */
+ if (ldb_set_opaque(ldb, "cache.domain_sid", dom_sid_new) != LDB_SUCCESS) {
+ goto failed;
+ }
+
+ talloc_steal(ldb, dom_sid_new);
+ talloc_free(tmp_ctx);
+ talloc_free(dom_sid_old);
+
+ return true;
+
+failed:
+ DEBUG(1,("Failed to set our own cached domain SID in the ldb!\n"));
+ talloc_free(tmp_ctx);
+ return false;
+}
+
+/* Obtain the short name of the flexible single master operator
+ * (FSMO), such as the PDC Emulator */
+const char *samdb_result_fsmo_name(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, const struct ldb_message *msg,
+ const char *attr)
+{
+ /* Format is cn=NTDS Settings,cn=<NETBIOS name of FSMO>,.... */
+ struct ldb_dn *fsmo_dn = ldb_msg_find_attr_as_dn(ldb, mem_ctx, msg, attr);
+ const struct ldb_val *val = ldb_dn_get_component_val(fsmo_dn, 1);
+ const char *name = ldb_dn_get_component_name(fsmo_dn, 1);
+
+ if (!name || (ldb_attr_cmp(name, "cn") != 0)) {
+ /* Ensure this matches the format. This gives us a
+ * bit more confidence that a 'cn' value will be a
+ * ascii string */
+ return NULL;
+ }
+ if (val) {
+ return (char *)val->data;
+ }
+ return NULL;
+}
+
+/*
+ work out the ntds settings dn for the current open ldb
+*/
+struct ldb_dn *samdb_ntds_settings_dn(struct ldb_context *ldb)
+{
+ TALLOC_CTX *tmp_ctx;
+ const char *root_attrs[] = { "dsServiceName", NULL };
+ int ret;
+ struct ldb_result *root_res;
+ struct ldb_dn *settings_dn;
+
+ /* see if we have a cached copy */
+ settings_dn = (struct ldb_dn *)ldb_get_opaque(ldb, "cache.settings_dn");
+ if (settings_dn) {
+ return settings_dn;
+ }
+
+ tmp_ctx = talloc_new(ldb);
+ if (tmp_ctx == NULL) {
+ goto failed;
+ }
+
+
+ ret = ldb_search(ldb, tmp_ctx, &root_res, ldb_dn_new(tmp_ctx, ldb, ""), LDB_SCOPE_BASE, root_attrs, NULL);
+ if (ret) {
+ DEBUG(1,("Searching for dsServiceName in rootDSE failed: %s\n",
+ ldb_errstring(ldb)));
+ goto failed;
+ }
+
+ if (root_res->count != 1) {
+ goto failed;
+ }
+
+ settings_dn = ldb_msg_find_attr_as_dn(ldb, tmp_ctx, root_res->msgs[0], "dsServiceName");
+
+ /* cache the domain_sid in the ldb */
+ if (ldb_set_opaque(ldb, "cache.settings_dn", settings_dn) != LDB_SUCCESS) {
+ goto failed;
+ }
+
+ talloc_steal(ldb, settings_dn);
+ talloc_free(tmp_ctx);
+
+ return settings_dn;
+
+failed:
+ DEBUG(1,("Failed to find our own NTDS Settings DN in the ldb!\n"));
+ talloc_free(tmp_ctx);
+ return NULL;
+}
+
+/*
+ work out the ntds settings invocationId for the current open ldb
+*/
+const struct GUID *samdb_ntds_invocation_id(struct ldb_context *ldb)
+{
+ TALLOC_CTX *tmp_ctx;
+ const char *attrs[] = { "invocationId", NULL };
+ int ret;
+ struct ldb_result *res;
+ struct GUID *invocation_id;
+
+ /* see if we have a cached copy */
+ invocation_id = (struct GUID *)ldb_get_opaque(ldb, "cache.invocation_id");
+ if (invocation_id) {
+ return invocation_id;
+ }
+
+ tmp_ctx = talloc_new(ldb);
+ if (tmp_ctx == NULL) {
+ goto failed;
+ }
+
+ ret = ldb_search(ldb, tmp_ctx, &res, samdb_ntds_settings_dn(ldb), LDB_SCOPE_BASE, attrs, NULL);
+ if (ret) {
+ goto failed;
+ }
+
+ if (res->count != 1) {
+ goto failed;
+ }
+
+ invocation_id = talloc(tmp_ctx, struct GUID);
+ if (!invocation_id) {
+ goto failed;
+ }
+
+ *invocation_id = samdb_result_guid(res->msgs[0], "invocationId");
+
+ /* cache the domain_sid in the ldb */
+ if (ldb_set_opaque(ldb, "cache.invocation_id", invocation_id) != LDB_SUCCESS) {
+ goto failed;
+ }
+
+ talloc_steal(ldb, invocation_id);
+ talloc_free(tmp_ctx);
+
+ return invocation_id;
+
+failed:
+ DEBUG(1,("Failed to find our own NTDS Settings invocationId in the ldb!\n"));
+ talloc_free(tmp_ctx);
+ return NULL;
+}
+
+bool samdb_set_ntds_invocation_id(struct ldb_context *ldb, const struct GUID *invocation_id_in)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct GUID *invocation_id_new;
+ struct GUID *invocation_id_old;
+
+ /* see if we have a cached copy */
+ invocation_id_old = (struct GUID *)ldb_get_opaque(ldb,
+ "cache.invocation_id");
+
+ tmp_ctx = talloc_new(ldb);
+ if (tmp_ctx == NULL) {
+ goto failed;
+ }
+
+ invocation_id_new = talloc(tmp_ctx, struct GUID);
+ if (!invocation_id_new) {
+ goto failed;
+ }
+
+ *invocation_id_new = *invocation_id_in;
+
+ /* cache the domain_sid in the ldb */
+ if (ldb_set_opaque(ldb, "cache.invocation_id", invocation_id_new) != LDB_SUCCESS) {
+ goto failed;
+ }
+
+ talloc_steal(ldb, invocation_id_new);
+ talloc_free(tmp_ctx);
+ talloc_free(invocation_id_old);
+
+ return true;
+
+failed:
+ DEBUG(1,("Failed to set our own cached invocationId in the ldb!\n"));
+ talloc_free(tmp_ctx);
+ return false;
+}
+
+/*
+ work out the ntds settings objectGUID for the current open ldb
+*/
+const struct GUID *samdb_ntds_objectGUID(struct ldb_context *ldb)
+{
+ TALLOC_CTX *tmp_ctx;
+ const char *attrs[] = { "objectGUID", NULL };
+ int ret;
+ struct ldb_result *res;
+ struct GUID *ntds_guid;
+
+ /* see if we have a cached copy */
+ ntds_guid = (struct GUID *)ldb_get_opaque(ldb, "cache.ntds_guid");
+ if (ntds_guid) {
+ return ntds_guid;
+ }
+
+ tmp_ctx = talloc_new(ldb);
+ if (tmp_ctx == NULL) {
+ goto failed;
+ }
+
+ ret = ldb_search(ldb, tmp_ctx, &res, samdb_ntds_settings_dn(ldb), LDB_SCOPE_BASE, attrs, NULL);
+ if (ret) {
+ goto failed;
+ }
+
+ if (res->count != 1) {
+ goto failed;
+ }
+
+ ntds_guid = talloc(tmp_ctx, struct GUID);
+ if (!ntds_guid) {
+ goto failed;
+ }
+
+ *ntds_guid = samdb_result_guid(res->msgs[0], "objectGUID");
+
+ /* cache the domain_sid in the ldb */
+ if (ldb_set_opaque(ldb, "cache.ntds_guid", ntds_guid) != LDB_SUCCESS) {
+ goto failed;
+ }
+
+ talloc_steal(ldb, ntds_guid);
+ talloc_free(tmp_ctx);
+
+ return ntds_guid;
+
+failed:
+ DEBUG(1,("Failed to find our own NTDS Settings objectGUID in the ldb!\n"));
+ talloc_free(tmp_ctx);
+ return NULL;
+}
+
+bool samdb_set_ntds_objectGUID(struct ldb_context *ldb, const struct GUID *ntds_guid_in)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct GUID *ntds_guid_new;
+ struct GUID *ntds_guid_old;
+
+ /* see if we have a cached copy */
+ ntds_guid_old = (struct GUID *)ldb_get_opaque(ldb, "cache.ntds_guid");
+
+ tmp_ctx = talloc_new(ldb);
+ if (tmp_ctx == NULL) {
+ goto failed;
+ }
+
+ ntds_guid_new = talloc(tmp_ctx, struct GUID);
+ if (!ntds_guid_new) {
+ goto failed;
+ }
+
+ *ntds_guid_new = *ntds_guid_in;
+
+ /* cache the domain_sid in the ldb */
+ if (ldb_set_opaque(ldb, "cache.ntds_guid", ntds_guid_new) != LDB_SUCCESS) {
+ goto failed;
+ }
+
+ talloc_steal(ldb, ntds_guid_new);
+ talloc_free(tmp_ctx);
+ talloc_free(ntds_guid_old);
+
+ return true;
+
+failed:
+ DEBUG(1,("Failed to set our own cached invocationId in the ldb!\n"));
+ talloc_free(tmp_ctx);
+ return false;
+}
+
+/*
+ work out the server dn for the current open ldb
+*/
+struct ldb_dn *samdb_server_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx)
+{
+ return ldb_dn_get_parent(mem_ctx, samdb_ntds_settings_dn(ldb));
+}
+
+/*
+ work out the server dn for the current open ldb
+*/
+struct ldb_dn *samdb_server_site_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx)
+{
+ struct ldb_dn *server_dn;
+ struct ldb_dn *server_site_dn;
+
+ server_dn = samdb_server_dn(ldb, mem_ctx);
+ if (!server_dn) return NULL;
+
+ server_site_dn = ldb_dn_get_parent(mem_ctx, server_dn);
+
+ talloc_free(server_dn);
+ return server_site_dn;
+}
+
+/*
+ work out if we are the PDC for the domain of the current open ldb
+*/
+bool samdb_is_pdc(struct ldb_context *ldb)
+{
+ const char *dom_attrs[] = { "fSMORoleOwner", NULL };
+ int ret;
+ struct ldb_result *dom_res;
+ TALLOC_CTX *tmp_ctx;
+ bool is_pdc;
+ struct ldb_dn *pdc;
+
+ tmp_ctx = talloc_new(ldb);
+ if (tmp_ctx == NULL) {
+ DEBUG(1, ("talloc_new failed in samdb_is_pdc"));
+ return false;
+ }
+
+ ret = ldb_search(ldb, tmp_ctx, &dom_res, ldb_get_default_basedn(ldb), LDB_SCOPE_BASE, dom_attrs, NULL);
+ if (ret) {
+ DEBUG(1,("Searching for fSMORoleOwner in %s failed: %s\n",
+ ldb_dn_get_linearized(ldb_get_default_basedn(ldb)),
+ ldb_errstring(ldb)));
+ goto failed;
+ }
+ if (dom_res->count != 1) {
+ goto failed;
+ }
+
+ pdc = ldb_msg_find_attr_as_dn(ldb, tmp_ctx, dom_res->msgs[0], "fSMORoleOwner");
+
+ if (ldb_dn_compare(samdb_ntds_settings_dn(ldb), pdc) == 0) {
+ is_pdc = true;
+ } else {
+ is_pdc = false;
+ }
+
+ talloc_free(tmp_ctx);
+
+ return is_pdc;
+
+failed:
+ DEBUG(1,("Failed to find if we are the PDC for this ldb\n"));
+ talloc_free(tmp_ctx);
+ return false;
+}
+
+/*
+ work out if we are a Global Catalog server for the domain of the current open ldb
+*/
+bool samdb_is_gc(struct ldb_context *ldb)
+{
+ const char *attrs[] = { "options", NULL };
+ int ret, options;
+ struct ldb_result *res;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_new(ldb);
+ if (tmp_ctx == NULL) {
+ DEBUG(1, ("talloc_new failed in samdb_is_pdc"));
+ return false;
+ }
+
+ /* Query cn=ntds settings,.... */
+ ret = ldb_search(ldb, tmp_ctx, &res, samdb_ntds_settings_dn(ldb), LDB_SCOPE_BASE, attrs, NULL);
+ if (ret) {
+ talloc_free(tmp_ctx);
+ return false;
+ }
+ if (res->count != 1) {
+ talloc_free(tmp_ctx);
+ return false;
+ }
+
+ options = ldb_msg_find_attr_as_int(res->msgs[0], "options", 0);
+ talloc_free(tmp_ctx);
+
+ /* if options attribute has the 0x00000001 flag set, then enable the global catlog */
+ if (options & 0x000000001) {
+ return true;
+ }
+ return false;
+}
+
+/* Find a domain object in the parents of a particular DN. */
+int samdb_search_for_parent_domain(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, struct ldb_dn *dn,
+ struct ldb_dn **parent_dn, const char **errstring)
+{
+ TALLOC_CTX *local_ctx;
+ struct ldb_dn *sdn = dn;
+ struct ldb_result *res = NULL;
+ int ret = 0;
+ const char *attrs[] = { NULL };
+
+ local_ctx = talloc_new(mem_ctx);
+ if (local_ctx == NULL) return LDB_ERR_OPERATIONS_ERROR;
+
+ while ((sdn = ldb_dn_get_parent(local_ctx, sdn))) {
+ ret = ldb_search(ldb, local_ctx, &res, sdn, LDB_SCOPE_BASE, attrs,
+ "(|(|(objectClass=domain)(objectClass=builtinDomain))(objectClass=samba4LocalDomain))");
+ if (ret == LDB_SUCCESS) {
+ if (res->count == 1) {
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+
+ if (ret != LDB_SUCCESS) {
+ *errstring = talloc_asprintf(mem_ctx, "Error searching for parent domain of %s, failed searching for %s: %s",
+ ldb_dn_get_linearized(dn),
+ ldb_dn_get_linearized(sdn),
+ ldb_errstring(ldb));
+ talloc_free(local_ctx);
+ return ret;
+ }
+ if (res->count != 1) {
+ *errstring = talloc_asprintf(mem_ctx, "Invalid dn (%s), not child of a domain object",
+ ldb_dn_get_linearized(dn));
+ talloc_free(local_ctx);
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+
+ *parent_dn = talloc_steal(mem_ctx, res->msgs[0]->dn);
+ talloc_free(local_ctx);
+ return ret;
+}
+
+/*
+ check that a password is sufficiently complex
+*/
+static bool samdb_password_complexity_ok(const char *pass)
+{
+ return check_password_quality(pass);
+}
+
+
+
+/*
+ set the user password using plaintext, obeying any user or domain
+ password restrictions
+
+ note that this function doesn't actually store the result in the
+ database, it just fills in the "mod" structure with ldb modify
+ elements to setup the correct change when samdb_replace() is
+ called. This allows the caller to combine the change with other
+ changes (as is needed by some of the set user info levels)
+
+ The caller should probably have a transaction wrapping this
+*/
+NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
+ struct ldb_dn *user_dn,
+ struct ldb_dn *domain_dn,
+ struct ldb_message *mod,
+ const DATA_BLOB *new_password,
+ struct samr_Password *lmNewHash,
+ struct samr_Password *ntNewHash,
+ bool user_change,
+ enum samr_RejectReason *reject_reason,
+ struct samr_DomInfo1 **_dominfo)
+{
+ const char * const user_attrs[] = { "userAccountControl", "lmPwdHistory",
+ "ntPwdHistory",
+ "dBCSPwd", "unicodePwd",
+ "objectSid",
+ "pwdLastSet", NULL };
+ const char * const domain_attrs[] = { "pwdProperties", "pwdHistoryLength",
+ "maxPwdAge", "minPwdAge",
+ "minPwdLength", NULL };
+ NTTIME pwdLastSet;
+ int64_t minPwdAge;
+ uint_t minPwdLength, pwdProperties, pwdHistoryLength;
+ uint_t userAccountControl;
+ struct samr_Password *sambaLMPwdHistory, *sambaNTPwdHistory, *lmPwdHash, *ntPwdHash;
+ struct samr_Password local_lmNewHash, local_ntNewHash;
+ int sambaLMPwdHistory_len, sambaNTPwdHistory_len;
+ struct dom_sid *domain_sid;
+ struct ldb_message **res;
+ bool restrictions;
+ int count;
+ time_t now = time(NULL);
+ NTTIME now_nt;
+ int i;
+
+ /* we need to know the time to compute password age */
+ unix_to_nt_time(&now_nt, now);
+
+ /* pull all the user parameters */
+ count = gendb_search_dn(ctx, mem_ctx, user_dn, &res, user_attrs);
+ if (count != 1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ userAccountControl = samdb_result_uint(res[0], "userAccountControl", 0);
+ sambaLMPwdHistory_len = samdb_result_hashes(mem_ctx, res[0],
+ "lmPwdHistory", &sambaLMPwdHistory);
+ sambaNTPwdHistory_len = samdb_result_hashes(mem_ctx, res[0],
+ "ntPwdHistory", &sambaNTPwdHistory);
+ lmPwdHash = samdb_result_hash(mem_ctx, res[0], "dBCSPwd");
+ ntPwdHash = samdb_result_hash(mem_ctx, res[0], "unicodePwd");
+ pwdLastSet = samdb_result_uint64(res[0], "pwdLastSet", 0);
+
+ /* Only non-trust accounts have restrictions (possibly this
+ * test is the wrong way around, but I like to be restrictive
+ * if possible */
+ restrictions = !(userAccountControl & (UF_INTERDOMAIN_TRUST_ACCOUNT
+ |UF_WORKSTATION_TRUST_ACCOUNT
+ |UF_SERVER_TRUST_ACCOUNT));
+
+ if (domain_dn) {
+ /* pull the domain parameters */
+ count = gendb_search_dn(ctx, mem_ctx, domain_dn, &res, domain_attrs);
+ if (count != 1) {
+ DEBUG(2, ("samdb_set_password: Domain DN %s is invalid, for user %s\n",
+ ldb_dn_get_linearized(domain_dn),
+ ldb_dn_get_linearized(user_dn)));
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+ } else {
+ /* work out the domain sid, and pull the domain from there */
+ domain_sid = samdb_result_sid_prefix(mem_ctx, res[0], "objectSid");
+ if (domain_sid == NULL) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ count = gendb_search(ctx, mem_ctx, NULL, &res, domain_attrs,
+ "(objectSid=%s)",
+ ldap_encode_ndr_dom_sid(mem_ctx, domain_sid));
+ if (count != 1) {
+ DEBUG(2, ("samdb_set_password: Could not find domain to match SID: %s, for user %s\n",
+ dom_sid_string(mem_ctx, domain_sid),
+ ldb_dn_get_linearized(user_dn)));
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+ }
+
+ pwdProperties = samdb_result_uint(res[0], "pwdProperties", 0);
+ pwdHistoryLength = samdb_result_uint(res[0], "pwdHistoryLength", 0);
+ minPwdLength = samdb_result_uint(res[0], "minPwdLength", 0);
+ minPwdAge = samdb_result_int64(res[0], "minPwdAge", 0);
+
+ if (_dominfo) {
+ struct samr_DomInfo1 *dominfo;
+ /* on failure we need to fill in the reject reasons */
+ dominfo = talloc(mem_ctx, struct samr_DomInfo1);
+ if (dominfo == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ dominfo->min_password_length = minPwdLength;
+ dominfo->password_properties = pwdProperties;
+ dominfo->password_history_length = pwdHistoryLength;
+ dominfo->max_password_age = minPwdAge;
+ dominfo->min_password_age = minPwdAge;
+ *_dominfo = dominfo;
+ }
+
+ if (restrictions && new_password) {
+ char *new_pass;
+
+ /* check the various password restrictions */
+ if (restrictions && minPwdLength > utf16_len_n(new_password->data, new_password->length) / 2) {
+ if (reject_reason) {
+ *reject_reason = SAMR_REJECT_TOO_SHORT;
+ }
+ return NT_STATUS_PASSWORD_RESTRICTION;
+ }
+
+ /* Create the NT hash */
+ mdfour(local_ntNewHash.hash, new_password->data, new_password->length);
+
+ ntNewHash = &local_ntNewHash;
+
+ /* Only check complexity if we can convert it at all. Assuming unconvertable passwords are 'strong' */
+ if (convert_string_talloc_convenience(mem_ctx, lp_iconv_convenience(ldb_get_opaque(ctx, "loadparm")),
+ CH_UTF16, CH_UNIX,
+ new_password->data, new_password->length,
+ (void **)&new_pass, NULL, false)) {
+
+
+ /* possibly check password complexity */
+ if (restrictions && pwdProperties & DOMAIN_PASSWORD_COMPLEX &&
+ !samdb_password_complexity_ok(new_pass)) {
+ if (reject_reason) {
+ *reject_reason = SAMR_REJECT_COMPLEXITY;
+ }
+ return NT_STATUS_PASSWORD_RESTRICTION;
+ }
+
+ /* compute the new lm hashes (for checking history - case insenitivly!) */
+ if (E_deshash(new_pass, local_lmNewHash.hash)) {
+ lmNewHash = &local_lmNewHash;
+ }
+
+ }
+ }
+
+ if (restrictions && user_change) {
+ /* are all password changes disallowed? */
+ if (pwdProperties & DOMAIN_REFUSE_PASSWORD_CHANGE) {
+ if (reject_reason) {
+ *reject_reason = SAMR_REJECT_OTHER;
+ }
+ return NT_STATUS_PASSWORD_RESTRICTION;
+ }
+
+ /* can this user change password? */
+ if (userAccountControl & UF_PASSWD_CANT_CHANGE) {
+ if (reject_reason) {
+ *reject_reason = SAMR_REJECT_OTHER;
+ }
+ return NT_STATUS_PASSWORD_RESTRICTION;
+ }
+
+ /* yes, this is a minus. The ages are in negative 100nsec units! */
+ if (pwdLastSet - minPwdAge > now_nt) {
+ if (reject_reason) {
+ *reject_reason = SAMR_REJECT_OTHER;
+ }
+ return NT_STATUS_PASSWORD_RESTRICTION;
+ }
+
+ /* check the immediately past password */
+ if (pwdHistoryLength > 0) {
+ if (lmNewHash && lmPwdHash && memcmp(lmNewHash->hash, lmPwdHash->hash, 16) == 0) {
+ if (reject_reason) {
+ *reject_reason = SAMR_REJECT_IN_HISTORY;
+ }
+ return NT_STATUS_PASSWORD_RESTRICTION;
+ }
+ if (ntNewHash && ntPwdHash && memcmp(ntNewHash->hash, ntPwdHash->hash, 16) == 0) {
+ if (reject_reason) {
+ *reject_reason = SAMR_REJECT_IN_HISTORY;
+ }
+ return NT_STATUS_PASSWORD_RESTRICTION;
+ }
+ }
+
+ /* check the password history */
+ sambaLMPwdHistory_len = MIN(sambaLMPwdHistory_len, pwdHistoryLength);
+ sambaNTPwdHistory_len = MIN(sambaNTPwdHistory_len, pwdHistoryLength);
+
+ for (i=0; lmNewHash && i<sambaLMPwdHistory_len;i++) {
+ if (memcmp(lmNewHash->hash, sambaLMPwdHistory[i].hash, 16) == 0) {
+ if (reject_reason) {
+ *reject_reason = SAMR_REJECT_IN_HISTORY;
+ }
+ return NT_STATUS_PASSWORD_RESTRICTION;
+ }
+ }
+ for (i=0; ntNewHash && i<sambaNTPwdHistory_len;i++) {
+ if (memcmp(ntNewHash->hash, sambaNTPwdHistory[i].hash, 16) == 0) {
+ if (reject_reason) {
+ *reject_reason = SAMR_REJECT_IN_HISTORY;
+ }
+ return NT_STATUS_PASSWORD_RESTRICTION;
+ }
+ }
+ }
+
+#define CHECK_RET(x) do { if (x != 0) return NT_STATUS_NO_MEMORY; } while(0)
+
+ /* the password is acceptable. Start forming the new fields */
+ if (new_password) {
+ /* if we know the cleartext UTF16 password, then set it.
+ * Modules in ldb will set all the appropriate
+ * hashes */
+ CHECK_RET(ldb_msg_add_value(mod, "clearTextPassword", new_password, NULL));
+ } else {
+ /* We don't have the cleartext, so delete the old one
+ * and set what we have of the hashes */
+ CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "clearTextPassword"));
+
+ if (lmNewHash) {
+ CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "dBCSPwd", lmNewHash));
+ } else {
+ CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "dBCSPwd"));
+ }
+
+ if (ntNewHash) {
+ CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "unicodePwd", ntNewHash));
+ } else {
+ CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "unicodePwd"));
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ set the user password using plaintext, obeying any user or domain
+ password restrictions
+
+ This wrapper function takes a SID as input, rather than a user DN,
+ and actually performs the password change
+
+*/
+NTSTATUS samdb_set_password_sid(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
+ const struct dom_sid *user_sid,
+ const DATA_BLOB *new_pass,
+ struct samr_Password *lmNewHash,
+ struct samr_Password *ntNewHash,
+ bool user_change,
+ enum samr_RejectReason *reject_reason,
+ struct samr_DomInfo1 **_dominfo)
+{
+ NTSTATUS nt_status;
+ struct ldb_dn *user_dn;
+ struct ldb_message *msg;
+ int ret;
+
+ ret = ldb_transaction_start(ctx);
+ if (ret) {
+ DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(ctx)));
+ return NT_STATUS_TRANSACTION_ABORTED;
+ }
+
+ user_dn = samdb_search_dn(ctx, mem_ctx, NULL,
+ "(&(objectSid=%s)(objectClass=user))",
+ ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
+ if (!user_dn) {
+ ldb_transaction_cancel(ctx);
+ DEBUG(3, ("samdb_set_password_sid: SID %s not found in samdb, returning NO_SUCH_USER\n",
+ dom_sid_string(mem_ctx, user_sid)));
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ ldb_transaction_cancel(ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ msg->dn = ldb_dn_copy(msg, user_dn);
+ if (!msg->dn) {
+ ldb_transaction_cancel(ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ nt_status = samdb_set_password(ctx, mem_ctx,
+ user_dn, NULL,
+ msg, new_pass,
+ lmNewHash, ntNewHash,
+ user_change, /* This is a password set, not change */
+ reject_reason, _dominfo);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ ldb_transaction_cancel(ctx);
+ return nt_status;
+ }
+
+ /* modify the samdb record */
+ ret = samdb_replace(ctx, mem_ctx, msg);
+ if (ret != 0) {
+ ldb_transaction_cancel(ctx);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ ret = ldb_transaction_commit(ctx);
+ if (ret != 0) {
+ DEBUG(0,("Failed to commit transaction to change password on %s: %s\n",
+ ldb_dn_get_linearized(msg->dn),
+ ldb_errstring(ctx)));
+ return NT_STATUS_TRANSACTION_ABORTED;
+ }
+ return NT_STATUS_OK;
+}
+
+
+
+NTSTATUS samdb_create_foreign_security_principal(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
+ struct dom_sid *sid, struct ldb_dn **ret_dn)
+{
+ struct ldb_message *msg;
+ struct ldb_dn *basedn;
+ const char *sidstr;
+ int ret;
+
+ sidstr = dom_sid_string(mem_ctx, sid);
+ NT_STATUS_HAVE_NO_MEMORY(sidstr);
+
+ /* We might have to create a ForeignSecurityPrincipal, even if this user
+ * is in our own domain */
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* TODO: Hmmm. This feels wrong. How do I find the base dn to
+ * put the ForeignSecurityPrincipals? d_state->domain_dn does
+ * not work, this is wrong for the Builtin domain, there's no
+ * cn=For...,cn=Builtin,dc={BASEDN}. -- vl
+ */
+
+ basedn = samdb_search_dn(sam_ctx, mem_ctx, NULL,
+ "(&(objectClass=container)(cn=ForeignSecurityPrincipals))");
+
+ if (basedn == NULL) {
+ DEBUG(0, ("Failed to find DN for "
+ "ForeignSecurityPrincipal container\n"));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ /* add core elements to the ldb_message for the alias */
+ msg->dn = ldb_dn_copy(mem_ctx, basedn);
+ if ( ! ldb_dn_add_child_fmt(msg->dn, "CN=%s", sidstr))
+ return NT_STATUS_NO_MEMORY;
+
+ samdb_msg_add_string(sam_ctx, mem_ctx, msg,
+ "objectClass",
+ "foreignSecurityPrincipal");
+
+ /* create the alias */
+ ret = ldb_add(sam_ctx, msg);
+ if (ret != 0) {
+ DEBUG(0,("Failed to create foreignSecurityPrincipal "
+ "record %s: %s\n",
+ ldb_dn_get_linearized(msg->dn),
+ ldb_errstring(sam_ctx)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ *ret_dn = msg->dn;
+ return NT_STATUS_OK;
+}
+
+
+/*
+ Find the DN of a domain, assuming it to be a dotted.dns name
+*/
+
+struct ldb_dn *samdb_dns_domain_to_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, const char *dns_domain)
+{
+ int i;
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+ const char *binary_encoded;
+ const char **split_realm;
+ struct ldb_dn *dn;
+
+ if (!tmp_ctx) {
+ return NULL;
+ }
+
+ split_realm = (const char **)str_list_make(tmp_ctx, dns_domain, ".");
+ if (!split_realm) {
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+ dn = ldb_dn_new(mem_ctx, ldb, NULL);
+ for (i=0; split_realm[i]; i++) {
+ binary_encoded = ldb_binary_encode_string(tmp_ctx, split_realm[i]);
+ if (!ldb_dn_add_base_fmt(dn, "dc=%s", binary_encoded)) {
+ DEBUG(2, ("Failed to add dc=%s element to DN %s\n",
+ binary_encoded, ldb_dn_get_linearized(dn)));
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+ }
+ if (!ldb_dn_validate(dn)) {
+ DEBUG(2, ("Failed to validated DN %s\n",
+ ldb_dn_get_linearized(dn)));
+ return NULL;
+ }
+ return dn;
+}
+/*
+ Find the DN of a domain, be it the netbios or DNS name
+*/
+
+struct ldb_dn *samdb_domain_to_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
+ const char *domain_name)
+{
+ const char * const domain_ref_attrs[] = {
+ "ncName", NULL
+ };
+ const char * const domain_ref2_attrs[] = {
+ NULL
+ };
+ struct ldb_result *res_domain_ref;
+ char *escaped_domain = ldb_binary_encode_string(mem_ctx, domain_name);
+ /* find the domain's DN */
+ int ret_domain = ldb_search(ldb, mem_ctx,
+ &res_domain_ref,
+ samdb_partitions_dn(ldb, mem_ctx),
+ LDB_SCOPE_ONELEVEL,
+ domain_ref_attrs,
+ "(&(nETBIOSName=%s)(objectclass=crossRef))",
+ escaped_domain);
+ if (ret_domain != 0) {
+ return NULL;
+ }
+
+ if (res_domain_ref->count == 0) {
+ ret_domain = ldb_search(ldb, mem_ctx,
+ &res_domain_ref,
+ samdb_dns_domain_to_dn(ldb, mem_ctx, domain_name),
+ LDB_SCOPE_BASE,
+ domain_ref2_attrs,
+ "(objectclass=domain)");
+ if (ret_domain != 0) {
+ return NULL;
+ }
+
+ if (res_domain_ref->count == 1) {
+ return res_domain_ref->msgs[0]->dn;
+ }
+ return NULL;
+ }
+
+ if (res_domain_ref->count > 1) {
+ DEBUG(0,("Found %d records matching domain [%s]\n",
+ ret_domain, domain_name));
+ return NULL;
+ }
+
+ return samdb_result_dn(ldb, mem_ctx, res_domain_ref->msgs[0], "nCName", NULL);
+
+}
diff --git a/source4/dsdb/config.mk b/source4/dsdb/config.mk
new file mode 100644
index 0000000000..2ca4e4ca6d
--- /dev/null
+++ b/source4/dsdb/config.mk
@@ -0,0 +1,63 @@
+# Directory Service subsystem
+
+mkinclude samdb/ldb_modules/config.mk
+
+################################################
+# Start SUBSYSTEM SAMDB
+[SUBSYSTEM::SAMDB]
+PUBLIC_DEPENDENCIES = HEIMDAL_KRB5
+PRIVATE_DEPENDENCIES = LIBNDR NDR_DRSUAPI NDR_DRSBLOBS NSS_WRAPPER \
+ auth_system_session LDAP_ENCODE LIBCLI_AUTH LIBNDR \
+ SAMDB_SCHEMA LDB_WRAP SAMDB_COMMON
+
+
+SAMDB_OBJ_FILES = $(addprefix $(dsdbsrcdir)/, \
+ samdb/samdb.o \
+ samdb/samdb_privilege.o \
+ samdb/cracknames.o \
+ repl/replicated_objects.o)
+
+$(eval $(call proto_header_template,$(dsdbsrcdir)/samdb/samdb_proto.h,$(SAMDB_OBJ_FILES:.o=.c)))
+# PUBLIC_HEADERS += dsdb/samdb/samdb.h
+
+[SUBSYSTEM::SAMDB_COMMON]
+PRIVATE_DEPENDENCIES = LIBLDB
+
+SAMDB_COMMON_OBJ_FILES = $(addprefix $(dsdbsrcdir)/common/, \
+ sidmap.o \
+ flag_mapping.o \
+ util.o)
+$(eval $(call proto_header_template,$(dsdbsrcdir)/common/proto.h,$(SAMDB_COMMON_OBJ_FILES:.o=.c)))
+
+[SUBSYSTEM::SAMDB_SCHEMA]
+PRIVATE_DEPENDENCIES = SAMDB_COMMON NDR_DRSUAPI NDR_DRSBLOBS
+
+SAMDB_SCHEMA_OBJ_FILES = $(addprefix $(dsdbsrcdir)/schema/, \
+ schema_init.o \
+ schema_set.o \
+ schema_query.o \
+ schema_syntax.o \
+ schema_description.o)
+
+$(eval $(call proto_header_template,$(dsdbsrcdir)/schema/proto.h,$(SAMDB_SCHEMA_OBJ_FILES:.o=.c)))
+# PUBLIC_HEADERS += dsdb/schema/schema.h
+
+#######################
+# Start SUBSYSTEM DREPL_SRV
+[MODULE::DREPL_SRV]
+INIT_FUNCTION = server_service_drepl_init
+SUBSYSTEM = service
+PRIVATE_DEPENDENCIES = \
+ SAMDB \
+ process_model
+# End SUBSYSTEM DREPL_SRV
+#######################
+
+DREPL_SRV_OBJ_FILES = $(addprefix $(dsdbsrcdir)/repl/, \
+ drepl_service.o \
+ drepl_periodic.o \
+ drepl_partitions.o \
+ drepl_out_pull.o \
+ drepl_out_helpers.o)
+
+$(eval $(call proto_header_template,$(dsdbsrcdir)/repl/drepl_service_proto.h,$(DREPL_SRV_OBJ_FILES:.o=.c)))
diff --git a/source4/dsdb/repl/drepl_out_helpers.c b/source4/dsdb/repl/drepl_out_helpers.c
new file mode 100644
index 0000000000..c292c6db74
--- /dev/null
+++ b/source4/dsdb/repl/drepl_out_helpers.c
@@ -0,0 +1,444 @@
+/*
+ Unix SMB/CIFS mplementation.
+ DSDB replication service helper function for outgoing traffic
+
+ 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 <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "includes.h"
+#include "dsdb/samdb/samdb.h"
+#include "auth/auth.h"
+#include "smbd/service.h"
+#include "lib/events/events.h"
+#include "lib/messaging/irpc.h"
+#include "dsdb/repl/drepl_service.h"
+#include "lib/ldb/include/ldb_errors.h"
+#include "../lib/util/dlinklist.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "librpc/gen_ndr/ndr_drsuapi.h"
+#include "librpc/gen_ndr/ndr_drsblobs.h"
+#include "libcli/composite/composite.h"
+#include "auth/gensec/gensec.h"
+
+struct dreplsrv_out_drsuapi_state {
+ struct composite_context *creq;
+
+ struct dreplsrv_out_connection *conn;
+
+ struct dreplsrv_drsuapi_connection *drsuapi;
+
+ struct drsuapi_DsBindInfoCtr bind_info_ctr;
+ struct drsuapi_DsBind bind_r;
+};
+
+static void dreplsrv_out_drsuapi_connect_recv(struct composite_context *creq);
+
+static struct composite_context *dreplsrv_out_drsuapi_send(struct dreplsrv_out_connection *conn)
+{
+ struct composite_context *c;
+ struct composite_context *creq;
+ struct dreplsrv_out_drsuapi_state *st;
+
+ c = composite_create(conn, conn->service->task->event_ctx);
+ if (c == NULL) return NULL;
+
+ st = talloc_zero(c, struct dreplsrv_out_drsuapi_state);
+ if (composite_nomem(st, c)) return c;
+
+ c->private_data = st;
+
+ st->creq = c;
+ st->conn = conn;
+ st->drsuapi = conn->drsuapi;
+
+ if (st->drsuapi && !st->drsuapi->pipe->conn->dead) {
+ composite_done(c);
+ return c;
+ } else if (st->drsuapi && st->drsuapi->pipe->conn->dead) {
+ talloc_free(st->drsuapi);
+ conn->drsuapi = NULL;
+ }
+
+ st->drsuapi = talloc_zero(st, struct dreplsrv_drsuapi_connection);
+ if (composite_nomem(st->drsuapi, c)) return c;
+
+ creq = dcerpc_pipe_connect_b_send(st, conn->binding, &ndr_table_drsuapi,
+ conn->service->system_session_info->credentials,
+ c->event_ctx, conn->service->task->lp_ctx);
+ composite_continue(c, creq, dreplsrv_out_drsuapi_connect_recv, st);
+
+ return c;
+}
+
+static void dreplsrv_out_drsuapi_bind_send(struct dreplsrv_out_drsuapi_state *st);
+
+static void dreplsrv_out_drsuapi_connect_recv(struct composite_context *creq)
+{
+ struct dreplsrv_out_drsuapi_state *st = talloc_get_type(creq->async.private_data,
+ struct dreplsrv_out_drsuapi_state);
+ struct composite_context *c = st->creq;
+
+ c->status = dcerpc_pipe_connect_b_recv(creq, st->drsuapi, &st->drsuapi->pipe);
+ if (!composite_is_ok(c)) return;
+
+ c->status = gensec_session_key(st->drsuapi->pipe->conn->security_state.generic_state,
+ &st->drsuapi->gensec_skey);
+ if (!composite_is_ok(c)) return;
+
+ dreplsrv_out_drsuapi_bind_send(st);
+}
+
+static void dreplsrv_out_drsuapi_bind_recv(struct rpc_request *req);
+
+static void dreplsrv_out_drsuapi_bind_send(struct dreplsrv_out_drsuapi_state *st)
+{
+ struct composite_context *c = st->creq;
+ struct rpc_request *req;
+
+ st->bind_info_ctr.length = 28;
+ st->bind_info_ctr.info.info28 = st->conn->service->bind_info28;
+
+ st->bind_r.in.bind_guid = &st->conn->service->ntds_guid;
+ st->bind_r.in.bind_info = &st->bind_info_ctr;
+ st->bind_r.out.bind_handle = &st->drsuapi->bind_handle;
+
+ req = dcerpc_drsuapi_DsBind_send(st->drsuapi->pipe, st, &st->bind_r);
+ composite_continue_rpc(c, req, dreplsrv_out_drsuapi_bind_recv, st);
+}
+
+static void dreplsrv_out_drsuapi_bind_recv(struct rpc_request *req)
+{
+ struct dreplsrv_out_drsuapi_state *st = talloc_get_type(req->async.private_data,
+ struct dreplsrv_out_drsuapi_state);
+ struct composite_context *c = st->creq;
+
+ c->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(c)) return;
+
+ if (!W_ERROR_IS_OK(st->bind_r.out.result)) {
+ composite_error(c, werror_to_ntstatus(st->bind_r.out.result));
+ return;
+ }
+
+ ZERO_STRUCT(st->drsuapi->remote_info28);
+ if (st->bind_r.out.bind_info) {
+ switch (st->bind_r.out.bind_info->length) {
+ case 24: {
+ struct drsuapi_DsBindInfo24 *info24;
+ info24 = &st->bind_r.out.bind_info->info.info24;
+ st->drsuapi->remote_info28.supported_extensions = info24->supported_extensions;
+ st->drsuapi->remote_info28.site_guid = info24->site_guid;
+ st->drsuapi->remote_info28.pid = info24->pid;
+ st->drsuapi->remote_info28.repl_epoch = 0;
+ break;
+ }
+ case 48: {
+ struct drsuapi_DsBindInfo48 *info48;
+ info48 = &st->bind_r.out.bind_info->info.info48;
+ st->drsuapi->remote_info28.supported_extensions = info48->supported_extensions;
+ st->drsuapi->remote_info28.site_guid = info48->site_guid;
+ st->drsuapi->remote_info28.pid = info48->pid;
+ st->drsuapi->remote_info28.repl_epoch = info48->repl_epoch;
+ break;
+ }
+ case 28:
+ st->drsuapi->remote_info28 = st->bind_r.out.bind_info->info.info28;
+ break;
+ }
+ }
+
+ composite_done(c);
+}
+
+static NTSTATUS dreplsrv_out_drsuapi_recv(struct composite_context *c)
+{
+ NTSTATUS status;
+ struct dreplsrv_out_drsuapi_state *st = talloc_get_type(c->private_data,
+ struct dreplsrv_out_drsuapi_state);
+
+ status = composite_wait(c);
+
+ if (NT_STATUS_IS_OK(status)) {
+ st->conn->drsuapi = talloc_steal(st->conn, st->drsuapi);
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+struct dreplsrv_op_pull_source_state {
+ struct composite_context *creq;
+
+ struct dreplsrv_out_operation *op;
+
+ struct dreplsrv_drsuapi_connection *drsuapi;
+
+ bool have_all;
+
+ uint32_t ctr_level;
+ struct drsuapi_DsGetNCChangesCtr1 *ctr1;
+ struct drsuapi_DsGetNCChangesCtr6 *ctr6;
+};
+
+static void dreplsrv_op_pull_source_connect_recv(struct composite_context *creq);
+
+struct composite_context *dreplsrv_op_pull_source_send(struct dreplsrv_out_operation *op)
+{
+ struct composite_context *c;
+ struct composite_context *creq;
+ struct dreplsrv_op_pull_source_state *st;
+
+ c = composite_create(op, op->service->task->event_ctx);
+ if (c == NULL) return NULL;
+
+ st = talloc_zero(c, struct dreplsrv_op_pull_source_state);
+ if (composite_nomem(st, c)) return c;
+
+ st->creq = c;
+ st->op = op;
+
+ creq = dreplsrv_out_drsuapi_send(op->source_dsa->conn);
+ composite_continue(c, creq, dreplsrv_op_pull_source_connect_recv, st);
+
+ return c;
+}
+
+static void dreplsrv_op_pull_source_get_changes_send(struct dreplsrv_op_pull_source_state *st);
+
+static void dreplsrv_op_pull_source_connect_recv(struct composite_context *creq)
+{
+ struct dreplsrv_op_pull_source_state *st = talloc_get_type(creq->async.private_data,
+ struct dreplsrv_op_pull_source_state);
+ struct composite_context *c = st->creq;
+
+ c->status = dreplsrv_out_drsuapi_recv(creq);
+ if (!composite_is_ok(c)) return;
+
+ dreplsrv_op_pull_source_get_changes_send(st);
+}
+
+static void dreplsrv_op_pull_source_get_changes_recv(struct rpc_request *req);
+
+static void dreplsrv_op_pull_source_get_changes_send(struct dreplsrv_op_pull_source_state *st)
+{
+ struct composite_context *c = st->creq;
+ struct repsFromTo1 *rf1 = st->op->source_dsa->repsFrom1;
+ struct dreplsrv_service *service = st->op->service;
+ struct dreplsrv_partition *partition = st->op->source_dsa->partition;
+ struct dreplsrv_drsuapi_connection *drsuapi = st->op->source_dsa->conn->drsuapi;
+ struct rpc_request *req;
+ struct drsuapi_DsGetNCChanges *r;
+
+ r = talloc(st, struct drsuapi_DsGetNCChanges);
+ if (composite_nomem(r, c)) return;
+
+ r->out.level_out = talloc(r, int32_t);
+ if (composite_nomem(r->out.level_out, c)) return;
+ r->in.req = talloc(r, union drsuapi_DsGetNCChangesRequest);
+ if (composite_nomem(r->in.req, c)) return;
+ r->out.ctr = talloc(r, union drsuapi_DsGetNCChangesCtr);
+ if (composite_nomem(r->out.ctr, c)) return;
+
+ r->in.bind_handle = &drsuapi->bind_handle;
+ if (drsuapi->remote_info28.supported_extensions & DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8) {
+ r->in.level = 8;
+ r->in.req->req8.destination_dsa_guid = service->ntds_guid;
+ r->in.req->req8.source_dsa_invocation_id= rf1->source_dsa_invocation_id;
+ r->in.req->req8.naming_context = &partition->nc;
+ r->in.req->req8.highwatermark = rf1->highwatermark;
+ r->in.req->req8.uptodateness_vector = NULL;/*&partition->uptodatevector_ex;*/
+ r->in.req->req8.replica_flags = rf1->replica_flags;
+ r->in.req->req8.max_object_count = 133;
+ r->in.req->req8.max_ndr_size = 1336811;
+ 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;
+ } else {
+ r->in.level = 5;
+ r->in.req->req5.destination_dsa_guid = service->ntds_guid;
+ r->in.req->req5.source_dsa_invocation_id= rf1->source_dsa_invocation_id;
+ r->in.req->req5.naming_context = &partition->nc;
+ r->in.req->req5.highwatermark = rf1->highwatermark;
+ r->in.req->req5.uptodateness_vector = NULL;/*&partition->uptodatevector_ex;*/
+ r->in.req->req5.replica_flags = rf1->replica_flags;
+ 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;
+ }
+
+ req = dcerpc_drsuapi_DsGetNCChanges_send(drsuapi->pipe, r, r);
+ composite_continue_rpc(c, req, dreplsrv_op_pull_source_get_changes_recv, st);
+}
+
+static void dreplsrv_op_pull_source_apply_changes_send(struct dreplsrv_op_pull_source_state *st,
+ struct drsuapi_DsGetNCChanges *r,
+ uint32_t ctr_level,
+ struct drsuapi_DsGetNCChangesCtr1 *ctr1,
+ struct drsuapi_DsGetNCChangesCtr6 *ctr6);
+
+static void dreplsrv_op_pull_source_get_changes_recv(struct rpc_request *req)
+{
+ struct dreplsrv_op_pull_source_state *st = talloc_get_type(req->async.private_data,
+ struct dreplsrv_op_pull_source_state);
+ struct composite_context *c = st->creq;
+ struct drsuapi_DsGetNCChanges *r = talloc_get_type(req->ndr.struct_ptr,
+ struct drsuapi_DsGetNCChanges);
+ uint32_t ctr_level = 0;
+ struct drsuapi_DsGetNCChangesCtr1 *ctr1 = NULL;
+ struct drsuapi_DsGetNCChangesCtr6 *ctr6 = NULL;
+
+ c->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(c)) return;
+
+ if (!W_ERROR_IS_OK(r->out.result)) {
+ composite_error(c, werror_to_ntstatus(r->out.result));
+ return;
+ }
+
+ if (*r->out.level_out == 1) {
+ ctr_level = 1;
+ ctr1 = &r->out.ctr->ctr1;
+ } else if (*r->out.level_out == 2 &&
+ r->out.ctr->ctr2.mszip1.ts) {
+ ctr_level = 1;
+ ctr1 = &r->out.ctr->ctr2.mszip1.ts->ctr1;
+ } else if (*r->out.level_out == 6) {
+ ctr_level = 6;
+ ctr6 = &r->out.ctr->ctr6;
+ } else if (*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) {
+ ctr_level = 6;
+ ctr6 = &r->out.ctr->ctr7.ctr.mszip6.ts->ctr6;
+ } else if (*r->out.level_out == 7 &&
+ r->out.ctr->ctr7.level == 6 &&
+ r->out.ctr->ctr7.type == DRSUAPI_COMPRESSION_TYPE_XPRESS &&
+ r->out.ctr->ctr7.ctr.xpress6.ts) {
+ ctr_level = 6;
+ ctr6 = &r->out.ctr->ctr7.ctr.xpress6.ts->ctr6;
+ } else {
+ composite_error(c, werror_to_ntstatus(WERR_BAD_NET_RESP));
+ return;
+ }
+
+ if (!ctr1 && !ctr6) {
+ composite_error(c, werror_to_ntstatus(WERR_BAD_NET_RESP));
+ return;
+ }
+
+ if (ctr_level == 6) {
+ if (!W_ERROR_IS_OK(ctr6->drs_error)) {
+ composite_error(c, werror_to_ntstatus(ctr6->drs_error));
+ return;
+ }
+ }
+
+ dreplsrv_op_pull_source_apply_changes_send(st, r, ctr_level, ctr1, ctr6);
+}
+
+static void dreplsrv_op_pull_source_apply_changes_send(struct dreplsrv_op_pull_source_state *st,
+ struct drsuapi_DsGetNCChanges *r,
+ uint32_t ctr_level,
+ struct drsuapi_DsGetNCChangesCtr1 *ctr1,
+ struct drsuapi_DsGetNCChangesCtr6 *ctr6)
+{
+ struct composite_context *c = st->creq;
+ struct repsFromTo1 rf1 = *st->op->source_dsa->repsFrom1;
+ struct dreplsrv_service *service = st->op->service;
+ struct dreplsrv_partition *partition = st->op->source_dsa->partition;
+ struct dreplsrv_drsuapi_connection *drsuapi = st->op->source_dsa->conn->drsuapi;
+ const struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr;
+ uint32_t object_count;
+ struct drsuapi_DsReplicaObjectListItemEx *first_object;
+ uint32_t linked_attributes_count;
+ struct drsuapi_DsReplicaLinkedAttribute *linked_attributes;
+ const struct drsuapi_DsReplicaCursor2CtrEx *uptodateness_vector;
+ bool more_data = false;
+ WERROR status;
+
+ switch (ctr_level) {
+ case 1:
+ mapping_ctr = &ctr1->mapping_ctr;
+ object_count = ctr1->object_count;
+ first_object = ctr1->first_object;
+ linked_attributes_count = 0;
+ linked_attributes = NULL;
+ rf1.highwatermark = ctr1->new_highwatermark;
+ uptodateness_vector = NULL; /* TODO: map it */
+ more_data = ctr1->more_data;
+ break;
+ case 6:
+ mapping_ctr = &ctr6->mapping_ctr;
+ object_count = ctr6->object_count;
+ first_object = ctr6->first_object;
+ linked_attributes_count = ctr6->linked_attributes_count;
+ linked_attributes = ctr6->linked_attributes;
+ rf1.highwatermark = ctr6->new_highwatermark;
+ uptodateness_vector = ctr6->uptodateness_vector;
+ more_data = ctr6->more_data;
+ break;
+ default:
+ composite_error(c, werror_to_ntstatus(WERR_BAD_NET_RESP));
+ return;
+ }
+
+ status = dsdb_extended_replicated_objects_commit(service->samdb,
+ partition->nc.dn,
+ mapping_ctr,
+ object_count,
+ first_object,
+ linked_attributes_count,
+ linked_attributes,
+ &rf1,
+ uptodateness_vector,
+ &drsuapi->gensec_skey,
+ st, NULL);
+ if (!W_ERROR_IS_OK(status)) {
+ DEBUG(0,("Failed to commit objects: %s\n", win_errstr(status)));
+ composite_error(c, werror_to_ntstatus(status));
+ return;
+ }
+
+ /* if it applied fine, we need to update the highwatermark */
+ *st->op->source_dsa->repsFrom1 = rf1;
+
+ /*
+ * TODO: update our uptodatevector!
+ */
+
+ if (more_data) {
+ dreplsrv_op_pull_source_get_changes_send(st);
+ return;
+ }
+
+ composite_done(c);
+}
+
+WERROR dreplsrv_op_pull_source_recv(struct composite_context *c)
+{
+ NTSTATUS status;
+
+ status = composite_wait(c);
+
+ talloc_free(c);
+ return ntstatus_to_werror(status);
+}
diff --git a/source4/dsdb/repl/drepl_out_helpers.h b/source4/dsdb/repl/drepl_out_helpers.h
new file mode 100644
index 0000000000..626112b82f
--- /dev/null
+++ b/source4/dsdb/repl/drepl_out_helpers.h
@@ -0,0 +1,26 @@
+/*
+ Unix SMB/CIFS mplementation.
+ DSDB replication service helper function for outgoing traffic
+
+ 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 <http://www.gnu.org/licenses/>.
+
+*/
+
+#ifndef DREPL_OUT_HELPERS_H
+#define DREPL_OUT_HELPERS_H
+
+
+#endif /* DREPL_OUT_HELPERS_H */
diff --git a/source4/dsdb/repl/drepl_out_pull.c b/source4/dsdb/repl/drepl_out_pull.c
new file mode 100644
index 0000000000..c66c5bbd19
--- /dev/null
+++ b/source4/dsdb/repl/drepl_out_pull.c
@@ -0,0 +1,154 @@
+/*
+ Unix SMB/CIFS mplementation.
+ DSDB replication service outgoing Pull-Replication
+
+ 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 <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "includes.h"
+#include "dsdb/samdb/samdb.h"
+#include "auth/auth.h"
+#include "smbd/service.h"
+#include "lib/events/events.h"
+#include "lib/messaging/irpc.h"
+#include "dsdb/repl/drepl_service.h"
+#include "lib/ldb/include/ldb_errors.h"
+#include "../lib/util/dlinklist.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "librpc/gen_ndr/ndr_drsuapi.h"
+#include "librpc/gen_ndr/ndr_drsblobs.h"
+#include "libcli/composite/composite.h"
+
+static WERROR dreplsrv_schedule_partition_pull_source(struct dreplsrv_service *s,
+ struct dreplsrv_partition *p,
+ struct dreplsrv_partition_source_dsa *source,
+ TALLOC_CTX *mem_ctx)
+{
+ struct dreplsrv_out_operation *op;
+
+ op = talloc_zero(mem_ctx, struct dreplsrv_out_operation);
+ W_ERROR_HAVE_NO_MEMORY(op);
+
+ op->service = s;
+ op->source_dsa = source;
+
+ DLIST_ADD_END(s->ops.pending, op, struct dreplsrv_out_operation *);
+ talloc_steal(s, op);
+ return WERR_OK;
+}
+
+static WERROR dreplsrv_schedule_partition_pull(struct dreplsrv_service *s,
+ struct dreplsrv_partition *p,
+ TALLOC_CTX *mem_ctx)
+{
+ WERROR status;
+ struct dreplsrv_partition_source_dsa *cur;
+
+ for (cur = p->sources; cur; cur = cur->next) {
+ status = dreplsrv_schedule_partition_pull_source(s, p, cur, mem_ctx);
+ W_ERROR_NOT_OK_RETURN(status);
+ }
+
+ return WERR_OK;
+}
+
+WERROR dreplsrv_schedule_pull_replication(struct dreplsrv_service *s, TALLOC_CTX *mem_ctx)
+{
+ WERROR status;
+ struct dreplsrv_partition *p;
+
+ for (p = s->partitions; p; p = p->next) {
+ status = dreplsrv_schedule_partition_pull(s, p, mem_ctx);
+ W_ERROR_NOT_OK_RETURN(status);
+ }
+
+ return WERR_OK;
+}
+
+static void dreplsrv_pending_op_callback(struct dreplsrv_out_operation *op)
+{
+ struct repsFromTo1 *rf = op->source_dsa->repsFrom1;
+ struct dreplsrv_service *s = op->service;
+ time_t t;
+ NTTIME now;
+
+ t = time(NULL);
+ unix_to_nt_time(&now, t);
+
+ rf->result_last_attempt = dreplsrv_op_pull_source_recv(op->creq);
+ if (W_ERROR_IS_OK(rf->result_last_attempt)) {
+ rf->consecutive_sync_failures = 0;
+ rf->last_success = now;
+ DEBUG(2,("dreplsrv_op_pull_source(%s)\n",
+ win_errstr(rf->result_last_attempt)));
+ goto done;
+ }
+
+ rf->consecutive_sync_failures++;
+
+ DEBUG(1,("dreplsrv_op_pull_source(%s/%s) failures[%u]\n",
+ win_errstr(rf->result_last_attempt),
+ nt_errstr(werror_to_ntstatus(rf->result_last_attempt)),
+ rf->consecutive_sync_failures));
+
+done:
+ talloc_free(op);
+ s->ops.current = NULL;
+ dreplsrv_run_pending_ops(s);
+}
+
+static void dreplsrv_pending_op_callback_creq(struct composite_context *creq)
+{
+ struct dreplsrv_out_operation *op = talloc_get_type(creq->async.private_data,
+ struct dreplsrv_out_operation);
+ dreplsrv_pending_op_callback(op);
+}
+
+void dreplsrv_run_pending_ops(struct dreplsrv_service *s)
+{
+ struct dreplsrv_out_operation *op;
+ time_t t;
+ NTTIME now;
+
+ if (s->ops.current) {
+ /* if there's still one running, we're done */
+ return;
+ }
+
+ if (!s->ops.pending) {
+ /* if there're no pending operations, we're done */
+ return;
+ }
+
+ t = time(NULL);
+ unix_to_nt_time(&now, t);
+
+ op = s->ops.pending;
+ s->ops.current = op;
+ DLIST_REMOVE(s->ops.pending, op);
+
+ op->source_dsa->repsFrom1->last_attempt = now;
+
+ op->creq = dreplsrv_op_pull_source_send(op);
+ if (!op->creq) {
+ dreplsrv_pending_op_callback(op);
+ return;
+ }
+
+ op->creq->async.fn = dreplsrv_pending_op_callback_creq;
+ op->creq->async.private_data = op;
+}
diff --git a/source4/dsdb/repl/drepl_partitions.c b/source4/dsdb/repl/drepl_partitions.c
new file mode 100644
index 0000000000..f36b735d32
--- /dev/null
+++ b/source4/dsdb/repl/drepl_partitions.c
@@ -0,0 +1,270 @@
+/*
+ Unix SMB/CIFS mplementation.
+ DSDB replication service
+
+ 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 <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "includes.h"
+#include "dsdb/samdb/samdb.h"
+#include "auth/auth.h"
+#include "smbd/service.h"
+#include "lib/events/events.h"
+#include "lib/messaging/irpc.h"
+#include "dsdb/repl/drepl_service.h"
+#include "lib/ldb/include/ldb_errors.h"
+#include "../lib/util/dlinklist.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "librpc/gen_ndr/ndr_drsuapi.h"
+#include "librpc/gen_ndr/ndr_drsblobs.h"
+#include "param/param.h"
+
+static WERROR dreplsrv_refresh_partitions(struct dreplsrv_service *s);
+
+WERROR dreplsrv_load_partitions(struct dreplsrv_service *s)
+{
+ WERROR status;
+ struct ldb_dn *basedn;
+ struct ldb_result *r;
+ struct ldb_message_element *el;
+ static const char *attrs[] = { "namingContexts", NULL };
+ uint32_t i;
+ int ret;
+
+ basedn = ldb_dn_new(s, s->samdb, NULL);
+ W_ERROR_HAVE_NO_MEMORY(basedn);
+
+ ret = ldb_search(s->samdb, s, &r, basedn, LDB_SCOPE_BASE, attrs,
+ "(objectClass=*)");
+ talloc_free(basedn);
+ if (ret != LDB_SUCCESS) {
+ return WERR_FOOBAR;
+ } else if (r->count != 1) {
+ talloc_free(r);
+ return WERR_FOOBAR;
+ }
+
+ el = ldb_msg_find_element(r->msgs[0], "namingContexts");
+ if (!el) {
+ return WERR_FOOBAR;
+ }
+
+ for (i=0; el && i < el->num_values; i++) {
+ const char *v = (const char *)el->values[i].data;
+ struct ldb_dn *pdn;
+ struct dreplsrv_partition *p;
+
+ pdn = ldb_dn_new(s, s->samdb, v);
+ if (!ldb_dn_validate(pdn)) {
+ return WERR_FOOBAR;
+ }
+
+ p = talloc_zero(s, struct dreplsrv_partition);
+ W_ERROR_HAVE_NO_MEMORY(p);
+
+ p->dn = talloc_steal(p, pdn);
+
+ DLIST_ADD(s->partitions, p);
+
+ DEBUG(2, ("dreplsrv_partition[%s] loaded\n", v));
+ }
+
+ talloc_free(r);
+
+ status = dreplsrv_refresh_partitions(s);
+ W_ERROR_NOT_OK_RETURN(status);
+
+ return WERR_OK;
+}
+
+static WERROR dreplsrv_out_connection_attach(struct dreplsrv_service *s,
+ const struct repsFromTo1 *rft,
+ struct dreplsrv_out_connection **_conn)
+{
+ struct dreplsrv_out_connection *cur, *conn = NULL;
+ const char *hostname;
+
+ if (!rft->other_info) {
+ return WERR_FOOBAR;
+ }
+
+ if (!rft->other_info->dns_name) {
+ return WERR_FOOBAR;
+ }
+
+ hostname = rft->other_info->dns_name;
+
+ for (cur = s->connections; cur; cur = cur->next) {
+ if (strcmp(cur->binding->host, hostname) == 0) {
+ conn = cur;
+ break;
+ }
+ }
+
+ if (!conn) {
+ NTSTATUS nt_status;
+ char *binding_str;
+
+ conn = talloc_zero(s, struct dreplsrv_out_connection);
+ W_ERROR_HAVE_NO_MEMORY(conn);
+
+ conn->service = s;
+
+ binding_str = talloc_asprintf(conn, "ncacn_ip_tcp:%s[krb5,seal]",
+ hostname);
+ W_ERROR_HAVE_NO_MEMORY(binding_str);
+ nt_status = dcerpc_parse_binding(conn, binding_str, &conn->binding);
+ talloc_free(binding_str);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return ntstatus_to_werror(nt_status);
+ }
+
+ DLIST_ADD_END(s->connections, conn, struct dreplsrv_out_connection *);
+
+ DEBUG(2,("dreplsrv_out_connection_attach(%s): create\n", conn->binding->host));
+ } else {
+ DEBUG(2,("dreplsrv_out_connection_attach(%s): attach\n", conn->binding->host));
+ }
+
+ *_conn = conn;
+ return WERR_OK;
+}
+
+static WERROR dreplsrv_partition_add_source_dsa(struct dreplsrv_service *s,
+ struct dreplsrv_partition *p,
+ const struct ldb_val *val)
+{
+ WERROR status;
+ enum ndr_err_code ndr_err;
+ struct dreplsrv_partition_source_dsa *source;
+
+ source = talloc_zero(p, struct dreplsrv_partition_source_dsa);
+ W_ERROR_HAVE_NO_MEMORY(source);
+
+ ndr_err = ndr_pull_struct_blob(val, source,
+ lp_iconv_convenience(s->task->lp_ctx), &source->_repsFromBlob,
+ (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
+ return ntstatus_to_werror(nt_status);
+ }
+ /* NDR_PRINT_DEBUG(repsFromToBlob, &source->_repsFromBlob); */
+ if (source->_repsFromBlob.version != 1) {
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ source->partition = p;
+ source->repsFrom1 = &source->_repsFromBlob.ctr.ctr1;
+
+ status = dreplsrv_out_connection_attach(s, source->repsFrom1, &source->conn);
+ W_ERROR_NOT_OK_RETURN(status);
+
+ DLIST_ADD_END(p->sources, source, struct dreplsrv_partition_source_dsa *);
+ return WERR_OK;
+}
+
+static WERROR dreplsrv_refresh_partition(struct dreplsrv_service *s,
+ struct dreplsrv_partition *p,
+ TALLOC_CTX *mem_ctx)
+{
+ WERROR status;
+ const struct ldb_val *ouv_value;
+ struct replUpToDateVectorBlob ouv;
+ struct dom_sid *nc_sid;
+ struct ldb_message_element *orf_el = NULL;
+ struct ldb_result *r;
+ uint32_t i;
+ int ret;
+ static const char *attrs[] = {
+ "objectSid",
+ "objectGUID",
+ "replUpToDateVector",
+ "repsFrom",
+ NULL
+ };
+
+ DEBUG(2, ("dreplsrv_refresh_partition(%s)\n",
+ ldb_dn_get_linearized(p->dn)));
+
+ ret = ldb_search(s->samdb, mem_ctx, &r, p->dn, LDB_SCOPE_BASE, attrs,
+ "(objectClass=*)");
+ if (ret != LDB_SUCCESS) {
+ return WERR_FOOBAR;
+ } else if (r->count != 1) {
+ talloc_free(r);
+ return WERR_FOOBAR;
+ }
+
+ ZERO_STRUCT(p->nc);
+ p->nc.dn = ldb_dn_alloc_linearized(p, p->dn);
+ W_ERROR_HAVE_NO_MEMORY(p->nc.dn);
+ p->nc.guid = samdb_result_guid(r->msgs[0], "objectGUID");
+ nc_sid = samdb_result_dom_sid(p, r->msgs[0], "objectSid");
+ if (nc_sid) {
+ p->nc.sid = *nc_sid;
+ }
+
+ ouv_value = ldb_msg_find_ldb_val(r->msgs[0], "replUpToDateVector");
+ if (ouv_value) {
+ enum ndr_err_code ndr_err;
+ ndr_err = ndr_pull_struct_blob(ouv_value, mem_ctx,
+ lp_iconv_convenience(s->task->lp_ctx), &ouv,
+ (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
+ return ntstatus_to_werror(nt_status);
+ }
+ /* NDR_PRINT_DEBUG(replUpToDateVectorBlob, &ouv); */
+ if (ouv.version != 2) {
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+
+ p->uptodatevector.count = ouv.ctr.ctr2.count;
+ p->uptodatevector.reserved = ouv.ctr.ctr2.reserved;
+ p->uptodatevector.cursors = talloc_steal(p, ouv.ctr.ctr2.cursors);
+ }
+
+ /*
+ * TODO: add our own uptodatevector cursor
+ */
+
+
+ orf_el = ldb_msg_find_element(r->msgs[0], "repsFrom");
+ if (orf_el) {
+ for (i=0; i < orf_el->num_values; i++) {
+ status = dreplsrv_partition_add_source_dsa(s, p, &orf_el->values[i]);
+ W_ERROR_NOT_OK_RETURN(status);
+ }
+ }
+
+ talloc_free(r);
+
+ return WERR_OK;
+}
+
+static WERROR dreplsrv_refresh_partitions(struct dreplsrv_service *s)
+{
+ WERROR status;
+ struct dreplsrv_partition *p;
+
+ for (p = s->partitions; p; p = p->next) {
+ status = dreplsrv_refresh_partition(s, p, p);
+ W_ERROR_NOT_OK_RETURN(status);
+ }
+
+ return WERR_OK;
+}
diff --git a/source4/dsdb/repl/drepl_periodic.c b/source4/dsdb/repl/drepl_periodic.c
new file mode 100644
index 0000000000..b88d2cee1e
--- /dev/null
+++ b/source4/dsdb/repl/drepl_periodic.c
@@ -0,0 +1,109 @@
+/*
+ Unix SMB/CIFS mplementation.
+ DSDB replication service periodic handling
+
+ 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 <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "includes.h"
+#include "lib/events/events.h"
+#include "dsdb/samdb/samdb.h"
+#include "auth/auth.h"
+#include "smbd/service.h"
+#include "lib/messaging/irpc.h"
+#include "dsdb/repl/drepl_service.h"
+#include "lib/ldb/include/ldb_errors.h"
+#include "../lib/util/dlinklist.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "librpc/gen_ndr/ndr_drsuapi.h"
+#include "librpc/gen_ndr/ndr_drsblobs.h"
+
+static void dreplsrv_periodic_run(struct dreplsrv_service *service);
+
+static void dreplsrv_periodic_handler_te(struct tevent_context *ev, struct tevent_timer *te,
+ struct timeval t, void *ptr)
+{
+ struct dreplsrv_service *service = talloc_get_type(ptr, struct dreplsrv_service);
+ WERROR status;
+
+ service->periodic.te = NULL;
+
+ dreplsrv_periodic_run(service);
+
+ status = dreplsrv_periodic_schedule(service, service->periodic.interval);
+ if (!W_ERROR_IS_OK(status)) {
+ task_server_terminate(service->task, win_errstr(status));
+ return;
+ }
+}
+
+WERROR dreplsrv_periodic_schedule(struct dreplsrv_service *service, uint32_t next_interval)
+{
+ TALLOC_CTX *tmp_mem;
+ struct tevent_timer *new_te;
+ struct timeval next_time;
+
+ /* prevent looping */
+ if (next_interval == 0) next_interval = 1;
+
+ next_time = timeval_current_ofs(next_interval, 50);
+
+ if (service->periodic.te) {
+ /*
+ * if the timestamp of the new event is higher,
+ * as current next we don't need to reschedule
+ */
+ if (timeval_compare(&next_time, &service->periodic.next_event) > 0) {
+ return WERR_OK;
+ }
+ }
+
+ /* reset the next scheduled timestamp */
+ service->periodic.next_event = next_time;
+
+ new_te = event_add_timed(service->task->event_ctx, service,
+ service->periodic.next_event,
+ dreplsrv_periodic_handler_te, service);
+ W_ERROR_HAVE_NO_MEMORY(new_te);
+
+ tmp_mem = talloc_new(service);
+ DEBUG(2,("dreplsrv_periodic_schedule(%u) %sscheduled for: %s\n",
+ next_interval,
+ (service->periodic.te?"re":""),
+ nt_time_string(tmp_mem, timeval_to_nttime(&next_time))));
+ talloc_free(tmp_mem);
+
+ talloc_free(service->periodic.te);
+ service->periodic.te = new_te;
+
+ return WERR_OK;
+}
+
+static void dreplsrv_periodic_run(struct dreplsrv_service *service)
+{
+ TALLOC_CTX *mem_ctx;
+
+ DEBUG(2,("dreplsrv_periodic_run(): schedule pull replication\n"));
+
+ mem_ctx = talloc_new(service);
+ dreplsrv_schedule_pull_replication(service, mem_ctx);
+ talloc_free(mem_ctx);
+
+ DEBUG(2,("dreplsrv_periodic_run(): run pending_ops\n"));
+
+ dreplsrv_run_pending_ops(service);
+}
diff --git a/source4/dsdb/repl/drepl_service.c b/source4/dsdb/repl/drepl_service.c
new file mode 100644
index 0000000000..152bbec4d5
--- /dev/null
+++ b/source4/dsdb/repl/drepl_service.c
@@ -0,0 +1,189 @@
+/*
+ Unix SMB/CIFS mplementation.
+ DSDB replication service
+
+ 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 <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "includes.h"
+#include "dsdb/samdb/samdb.h"
+#include "auth/auth.h"
+#include "smbd/service.h"
+#include "lib/events/events.h"
+#include "lib/messaging/irpc.h"
+#include "dsdb/repl/drepl_service.h"
+#include "lib/ldb/include/ldb_errors.h"
+#include "../lib/util/dlinklist.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "librpc/gen_ndr/ndr_drsuapi.h"
+#include "librpc/gen_ndr/ndr_drsblobs.h"
+#include "param/param.h"
+
+static WERROR dreplsrv_init_creds(struct dreplsrv_service *service)
+{
+ NTSTATUS status;
+
+ status = auth_system_session_info(service, service->task->lp_ctx,
+ &service->system_session_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ntstatus_to_werror(status);
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dreplsrv_connect_samdb(struct dreplsrv_service *service, struct loadparm_context *lp_ctx)
+{
+ const struct GUID *ntds_guid;
+ struct drsuapi_DsBindInfo28 *bind_info28;
+
+ service->samdb = samdb_connect(service, service->task->event_ctx, lp_ctx, service->system_session_info);
+ if (!service->samdb) {
+ return WERR_DS_SERVICE_UNAVAILABLE;
+ }
+
+ ntds_guid = samdb_ntds_objectGUID(service->samdb);
+ if (!ntds_guid) {
+ return WERR_DS_SERVICE_UNAVAILABLE;
+ }
+
+ service->ntds_guid = *ntds_guid;
+
+ bind_info28 = &service->bind_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;
+#if 0
+ if (s->domain_behavior_version == 2) {
+ /* TODO: find out how this is really triggered! */
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_LINKED_VALUE_REPLICATION;
+ }
+#endif
+ 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_00100000;
+ 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;
+#if 0 /* we don't support XPRESS compression yet */
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_XPRESS_COMPRESS;
+#endif
+ /* TODO: fill in site_guid */
+ bind_info28->site_guid = GUID_zero();
+ /* TODO: find out how this is really triggered! */
+ bind_info28->pid = 0;
+ bind_info28->repl_epoch = 0;
+
+ return WERR_OK;
+}
+
+/*
+ startup the dsdb replicator service task
+*/
+static void dreplsrv_task_init(struct task_server *task)
+{
+ WERROR status;
+ struct dreplsrv_service *service;
+ uint32_t periodic_startup_interval;
+
+ switch (lp_server_role(task->lp_ctx)) {
+ case ROLE_STANDALONE:
+ task_server_terminate(task, "dreplsrv: no DSDB replication required in standalone configuration");
+ return;
+ case ROLE_DOMAIN_MEMBER:
+ task_server_terminate(task, "dreplsrv: no DSDB replication required in domain member configuration");
+ return;
+ case ROLE_DOMAIN_CONTROLLER:
+ /* Yes, we want DSDB replication */
+ break;
+ }
+
+ task_server_set_title(task, "task[dreplsrv]");
+
+ service = talloc_zero(task, struct dreplsrv_service);
+ if (!service) {
+ task_server_terminate(task, "dreplsrv_task_init: out of memory");
+ return;
+ }
+ service->task = task;
+ service->startup_time = timeval_current();
+ task->private_data = service;
+
+ status = dreplsrv_init_creds(service);
+ if (!W_ERROR_IS_OK(status)) {
+ task_server_terminate(task, talloc_asprintf(task,
+ "dreplsrv: Failed to obtain server credentials: %s\n",
+ win_errstr(status)));
+ return;
+ }
+
+ status = dreplsrv_connect_samdb(service, task->lp_ctx);
+ if (!W_ERROR_IS_OK(status)) {
+ task_server_terminate(task, talloc_asprintf(task,
+ "dreplsrv: Failed to connect to local samdb: %s\n",
+ win_errstr(status)));
+ return;
+ }
+
+ status = dreplsrv_load_partitions(service);
+ if (!W_ERROR_IS_OK(status)) {
+ task_server_terminate(task, talloc_asprintf(task,
+ "dreplsrv: Failed to load partitions: %s\n",
+ win_errstr(status)));
+ return;
+ }
+
+ periodic_startup_interval = lp_parm_int(task->lp_ctx, NULL, "dreplsrv", "periodic_startup_interval", 15); /* in seconds */
+ service->periodic.interval = lp_parm_int(task->lp_ctx, NULL, "dreplsrv", "periodic_interval", 300); /* in seconds */
+
+ status = dreplsrv_periodic_schedule(service, periodic_startup_interval);
+ if (!W_ERROR_IS_OK(status)) {
+ task_server_terminate(task, talloc_asprintf(task,
+ "dreplsrv: Failed to periodic schedule: %s\n",
+ win_errstr(status)));
+ return;
+ }
+
+ irpc_add_name(task->msg_ctx, "dreplsrv");
+}
+
+/*
+ register ourselves as a available server
+*/
+NTSTATUS server_service_drepl_init(void)
+{
+ return register_server_service("drepl", dreplsrv_task_init);
+}
diff --git a/source4/dsdb/repl/drepl_service.h b/source4/dsdb/repl/drepl_service.h
new file mode 100644
index 0000000000..a9eea30719
--- /dev/null
+++ b/source4/dsdb/repl/drepl_service.h
@@ -0,0 +1,175 @@
+/*
+ Unix SMB/CIFS mplementation.
+ DSDB replication service
+
+ 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 <http://www.gnu.org/licenses/>.
+
+*/
+
+#ifndef _DSDB_REPL_DREPL_SERVICE_H_
+#define _DSDB_REPL_DREPL_SERVICE_H_
+
+#include "librpc/gen_ndr/ndr_drsuapi_c.h"
+
+struct dreplsrv_service;
+struct dreplsrv_partition;
+
+struct dreplsrv_drsuapi_connection {
+ /*
+ * this pipe pointer is also the indicator
+ * for a valid connection
+ */
+ struct dcerpc_pipe *pipe;
+
+ DATA_BLOB gensec_skey;
+ struct drsuapi_DsBindInfo28 remote_info28;
+ struct policy_handle bind_handle;
+};
+
+struct dreplsrv_out_connection {
+ struct dreplsrv_out_connection *prev, *next;
+
+ struct dreplsrv_service *service;
+
+ /*
+ * the binding for the outgoing connection
+ */
+ struct dcerpc_binding *binding;
+
+ /* the out going connection to the source dsa */
+ struct dreplsrv_drsuapi_connection *drsuapi;
+};
+
+struct dreplsrv_partition_source_dsa {
+ struct dreplsrv_partition_source_dsa *prev, *next;
+
+ struct dreplsrv_partition *partition;
+
+ /*
+ * the cached repsFrom value for this source dsa
+ *
+ * it needs to be updated after each DsGetNCChanges() call
+ * to the source dsa
+ *
+ * repsFrom1 == &_repsFromBlob.ctr.ctr1
+ */
+ struct repsFromToBlob _repsFromBlob;
+ struct repsFromTo1 *repsFrom1;
+
+ /* the reference to the source_dsa and its outgoing connection */
+ struct dreplsrv_out_connection *conn;
+};
+
+struct dreplsrv_partition {
+ struct dreplsrv_partition *prev, *next;
+
+ struct dreplsrv_service *service;
+
+ /* the dn of the partition */
+ struct ldb_dn *dn;
+ struct drsuapi_DsReplicaObjectIdentifier nc;
+
+ /*
+ * uptodate vector needs to be updated before and after each DsGetNCChanges() call
+ *
+ * - before: we need to use our own invocationId together with our highestCommitedUsn
+ * - after: we need to merge in the remote uptodatevector, to avoid reading it again
+ */
+ struct replUpToDateVectorCtr2 uptodatevector;
+ struct drsuapi_DsReplicaCursorCtrEx uptodatevector_ex;
+
+ /*
+ * a linked list of all source dsa's we replicate from
+ */
+ struct dreplsrv_partition_source_dsa *sources;
+};
+
+struct dreplsrv_out_operation {
+ struct dreplsrv_out_operation *prev, *next;
+
+ struct dreplsrv_service *service;
+
+ struct dreplsrv_partition_source_dsa *source_dsa;
+
+ struct composite_context *creq;
+};
+
+struct dreplsrv_service {
+ /* the whole drepl service is in one task */
+ struct task_server *task;
+
+ /* the time the service was started */
+ struct timeval startup_time;
+
+ /*
+ * system session info
+ * with machine account credentials
+ */
+ struct auth_session_info *system_session_info;
+
+ /*
+ * a connection to the local samdb
+ */
+ struct ldb_context *samdb;
+
+ /* the guid of our NTDS Settings object, which never changes! */
+ struct GUID ntds_guid;
+ /*
+ * the struct holds the values used for outgoing DsBind() calls,
+ * so that we need to set them up only once
+ */
+ struct drsuapi_DsBindInfo28 bind_info28;
+
+ /* some stuff for periodic processing */
+ struct {
+ /*
+ * the interval between to periodic runs
+ */
+ uint32_t interval;
+
+ /*
+ * the timestamp for the next event,
+ * this is the timstamp passed to event_add_timed()
+ */
+ struct timeval next_event;
+
+ /* here we have a reference to the timed event the schedules the periodic stuff */
+ struct tevent_timer *te;
+ } periodic;
+
+ /*
+ * the list of partitions we need to replicate
+ */
+ struct dreplsrv_partition *partitions;
+
+ /*
+ * the list of cached connections
+ */
+ struct dreplsrv_out_connection *connections;
+
+ struct {
+ /* the pointer to the current active operation */
+ struct dreplsrv_out_operation *current;
+
+ /* the list of pending operations */
+ struct dreplsrv_out_operation *pending;
+ } ops;
+};
+
+#include "dsdb/repl/drepl_out_helpers.h"
+#include "dsdb/repl/drepl_service_proto.h"
+
+#endif /* _DSDB_REPL_DREPL_SERVICE_H_ */
diff --git a/source4/dsdb/repl/replicated_objects.c b/source4/dsdb/repl/replicated_objects.c
new file mode 100644
index 0000000000..560f75da7a
--- /dev/null
+++ b/source4/dsdb/repl/replicated_objects.c
@@ -0,0 +1,417 @@
+/*
+ Unix SMB/CIFS mplementation.
+ Helper functions for applying replicated objects
+
+ Copyright (C) Stefan Metzmacher <metze@samba.org> 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 <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "includes.h"
+#include "dsdb/samdb/samdb.h"
+#include "lib/ldb/include/ldb_errors.h"
+#include "../lib/util/dlinklist.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "librpc/gen_ndr/ndr_drsuapi.h"
+#include "librpc/gen_ndr/ndr_drsblobs.h"
+#include "../lib/crypto/crypto.h"
+#include "libcli/auth/libcli_auth.h"
+#include "param/param.h"
+
+static WERROR dsdb_decrypt_attribute_value(TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *gensec_skey,
+ bool rid_crypt,
+ uint32_t rid,
+ DATA_BLOB *in,
+ DATA_BLOB *out)
+{
+ DATA_BLOB confounder;
+ DATA_BLOB enc_buffer;
+
+ struct MD5Context md5;
+ uint8_t _enc_key[16];
+ DATA_BLOB enc_key;
+
+ DATA_BLOB dec_buffer;
+
+ uint32_t crc32_given;
+ uint32_t crc32_calc;
+ DATA_BLOB checked_buffer;
+
+ DATA_BLOB plain_buffer;
+
+ /*
+ * users with rid == 0 should not exist
+ */
+ if (rid_crypt && rid == 0) {
+ return WERR_DS_DRA_INVALID_PARAMETER;
+ }
+
+ /*
+ * the first 16 bytes at the beginning are the confounder
+ * followed by the 4 byte crc32 checksum
+ */
+ if (in->length < 20) {
+ return WERR_DS_DRA_INVALID_PARAMETER;
+ }
+ confounder = data_blob_const(in->data, 16);
+ enc_buffer = data_blob_const(in->data + 16, in->length - 16);
+
+ /*
+ * build the encryption key md5 over the session key followed
+ * by the confounder
+ *
+ * here the gensec session key is used and
+ * not the dcerpc ncacn_ip_tcp "SystemLibraryDTC" key!
+ */
+ enc_key = data_blob_const(_enc_key, sizeof(_enc_key));
+ MD5Init(&md5);
+ MD5Update(&md5, gensec_skey->data, gensec_skey->length);
+ MD5Update(&md5, confounder.data, confounder.length);
+ MD5Final(enc_key.data, &md5);
+
+ /*
+ * copy the encrypted buffer part and
+ * decrypt it using the created encryption key using arcfour
+ */
+ dec_buffer = data_blob_const(enc_buffer.data, enc_buffer.length);
+ arcfour_crypt_blob(dec_buffer.data, dec_buffer.length, &enc_key);
+
+ /*
+ * the first 4 byte are the crc32 checksum
+ * of the remaining bytes
+ */
+ crc32_given = IVAL(dec_buffer.data, 0);
+ crc32_calc = crc32_calc_buffer(dec_buffer.data + 4 , dec_buffer.length - 4);
+ if (crc32_given != crc32_calc) {
+ return WERR_SEC_E_DECRYPT_FAILURE;
+ }
+ checked_buffer = data_blob_const(dec_buffer.data + 4, dec_buffer.length - 4);
+
+ plain_buffer = data_blob_talloc(mem_ctx, checked_buffer.data, checked_buffer.length);
+ W_ERROR_HAVE_NO_MEMORY(plain_buffer.data);
+
+ /*
+ * The following rid_crypt obfuscation isn't session specific
+ * and not really needed here, because we allways know the rid of the
+ * user account.
+ *
+ * But for the rest of samba it's easier when we remove this static
+ * obfuscation here
+ */
+ if (rid_crypt) {
+ uint32_t i, num_hashes;
+
+ if ((checked_buffer.length % 16) != 0) {
+ return WERR_DS_DRA_INVALID_PARAMETER;
+ }
+
+ num_hashes = plain_buffer.length / 16;
+ for (i = 0; i < num_hashes; i++) {
+ uint32_t offset = i * 16;
+ sam_rid_crypt(rid, checked_buffer.data + offset, plain_buffer.data + offset, 0);
+ }
+ }
+
+ *out = plain_buffer;
+ return WERR_OK;
+}
+
+static WERROR dsdb_decrypt_attribute(const DATA_BLOB *gensec_skey,
+ uint32_t rid,
+ struct drsuapi_DsReplicaAttribute *attr)
+{
+ WERROR status;
+ TALLOC_CTX *mem_ctx;
+ DATA_BLOB *enc_data;
+ DATA_BLOB plain_data;
+ bool rid_crypt = false;
+
+ if (attr->value_ctr.num_values == 0) {
+ return WERR_OK;
+ }
+
+ switch (attr->attid) {
+ case DRSUAPI_ATTRIBUTE_dBCSPwd:
+ case DRSUAPI_ATTRIBUTE_unicodePwd:
+ case DRSUAPI_ATTRIBUTE_ntPwdHistory:
+ case DRSUAPI_ATTRIBUTE_lmPwdHistory:
+ rid_crypt = true;
+ break;
+ case DRSUAPI_ATTRIBUTE_supplementalCredentials:
+ case DRSUAPI_ATTRIBUTE_priorValue:
+ case DRSUAPI_ATTRIBUTE_currentValue:
+ case DRSUAPI_ATTRIBUTE_trustAuthOutgoing:
+ case DRSUAPI_ATTRIBUTE_trustAuthIncoming:
+ case DRSUAPI_ATTRIBUTE_initialAuthOutgoing:
+ case DRSUAPI_ATTRIBUTE_initialAuthIncoming:
+ break;
+ default:
+ return WERR_OK;
+ }
+
+ if (attr->value_ctr.num_values > 1) {
+ return WERR_DS_DRA_INVALID_PARAMETER;
+ }
+
+ if (!attr->value_ctr.values[0].blob) {
+ return WERR_DS_DRA_INVALID_PARAMETER;
+ }
+
+ mem_ctx = attr->value_ctr.values[0].blob;
+ enc_data = attr->value_ctr.values[0].blob;
+
+ status = dsdb_decrypt_attribute_value(mem_ctx,
+ gensec_skey,
+ rid_crypt,
+ rid,
+ enc_data,
+ &plain_data);
+ W_ERROR_NOT_OK_RETURN(status);
+
+ talloc_free(attr->value_ctr.values[0].blob->data);
+ *attr->value_ctr.values[0].blob = plain_data;
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_convert_object(struct ldb_context *ldb,
+ const struct dsdb_schema *schema,
+ struct dsdb_extended_replicated_objects *ctr,
+ const struct drsuapi_DsReplicaObjectListItemEx *in,
+ const DATA_BLOB *gensec_skey,
+ TALLOC_CTX *mem_ctx,
+ struct dsdb_extended_replicated_object *out)
+{
+ NTSTATUS nt_status;
+ enum ndr_err_code ndr_err;
+ WERROR status;
+ uint32_t i;
+ struct ldb_message *msg;
+ struct replPropertyMetaDataBlob *md;
+ struct ldb_val guid_value;
+ NTTIME whenChanged = 0;
+ time_t whenChanged_t;
+ const char *whenChanged_s;
+ const char *rdn_name = NULL;
+ const struct ldb_val *rdn_value = NULL;
+ const struct dsdb_attribute *rdn_attr = NULL;
+ uint32_t rdn_attid;
+ struct drsuapi_DsReplicaAttribute *name_a = NULL;
+ struct drsuapi_DsReplicaMetaData *name_d = NULL;
+ struct replPropertyMetaData1 *rdn_m = NULL;
+ struct dom_sid *sid = NULL;
+ uint32_t rid = 0;
+ int ret;
+
+ if (!in->object.identifier) {
+ return WERR_FOOBAR;
+ }
+
+ if (!in->object.identifier->dn || !in->object.identifier->dn[0]) {
+ return WERR_FOOBAR;
+ }
+
+ if (in->object.attribute_ctr.num_attributes != 0 && !in->meta_data_ctr) {
+ return WERR_FOOBAR;
+ }
+
+ if (in->object.attribute_ctr.num_attributes != in->meta_data_ctr->count) {
+ return WERR_FOOBAR;
+ }
+
+ sid = &in->object.identifier->sid;
+ if (sid->num_auths > 0) {
+ rid = sid->sub_auths[sid->num_auths - 1];
+ }
+
+ msg = ldb_msg_new(mem_ctx);
+ W_ERROR_HAVE_NO_MEMORY(msg);
+
+ msg->dn = ldb_dn_new(msg, ldb, in->object.identifier->dn);
+ W_ERROR_HAVE_NO_MEMORY(msg->dn);
+
+ rdn_name = ldb_dn_get_rdn_name(msg->dn);
+ rdn_attr = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
+ if (!rdn_attr) {
+ return WERR_FOOBAR;
+ }
+ rdn_attid = rdn_attr->attributeID_id;
+ rdn_value = ldb_dn_get_rdn_val(msg->dn);
+
+ msg->num_elements = in->object.attribute_ctr.num_attributes;
+ msg->elements = talloc_array(msg, struct ldb_message_element,
+ msg->num_elements);
+ W_ERROR_HAVE_NO_MEMORY(msg->elements);
+
+ md = talloc(mem_ctx, struct replPropertyMetaDataBlob);
+ W_ERROR_HAVE_NO_MEMORY(md);
+
+ md->version = 1;
+ md->reserved = 0;
+ md->ctr.ctr1.count = in->meta_data_ctr->count;
+ md->ctr.ctr1.reserved = 0;
+ md->ctr.ctr1.array = talloc_array(mem_ctx,
+ struct replPropertyMetaData1,
+ md->ctr.ctr1.count + 1); /* +1 because of the RDN attribute */
+ W_ERROR_HAVE_NO_MEMORY(md->ctr.ctr1.array);
+
+ for (i=0; i < in->meta_data_ctr->count; i++) {
+ struct drsuapi_DsReplicaAttribute *a;
+ struct drsuapi_DsReplicaMetaData *d;
+ struct replPropertyMetaData1 *m;
+ struct ldb_message_element *e;
+
+ a = &in->object.attribute_ctr.attributes[i];
+ d = &in->meta_data_ctr->meta_data[i];
+ m = &md->ctr.ctr1.array[i];
+ e = &msg->elements[i];
+
+ status = dsdb_decrypt_attribute(gensec_skey, rid, a);
+ W_ERROR_NOT_OK_RETURN(status);
+
+ status = dsdb_attribute_drsuapi_to_ldb(ldb, schema, a, msg->elements, e);
+ W_ERROR_NOT_OK_RETURN(status);
+
+ m->attid = a->attid;
+ m->version = d->version;
+ m->originating_change_time = d->originating_change_time;
+ m->originating_invocation_id = d->originating_invocation_id;
+ m->originating_usn = d->originating_usn;
+ m->local_usn = 0;
+
+ if (d->originating_change_time > whenChanged) {
+ whenChanged = d->originating_change_time;
+ }
+
+ if (a->attid == DRSUAPI_ATTRIBUTE_name) {
+ name_a = a;
+ name_d = d;
+ rdn_m = &md->ctr.ctr1.array[md->ctr.ctr1.count];
+ }
+ }
+
+ if (rdn_m) {
+ ret = ldb_msg_add_value(msg, rdn_attr->lDAPDisplayName, rdn_value, NULL);
+ if (ret != LDB_SUCCESS) {
+ return WERR_FOOBAR;
+ }
+
+ rdn_m->attid = rdn_attid;
+ rdn_m->version = name_d->version;
+ rdn_m->originating_change_time = name_d->originating_change_time;
+ rdn_m->originating_invocation_id = name_d->originating_invocation_id;
+ rdn_m->originating_usn = name_d->originating_usn;
+ rdn_m->local_usn = 0;
+ md->ctr.ctr1.count++;
+
+ }
+
+ whenChanged_t = nt_time_to_unix(whenChanged);
+ whenChanged_s = ldb_timestring(msg, whenChanged_t);
+ W_ERROR_HAVE_NO_MEMORY(whenChanged_s);
+
+ ndr_err = ndr_push_struct_blob(&guid_value, msg,
+ lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
+ &in->object.identifier->guid,
+ (ndr_push_flags_fn_t)ndr_push_GUID);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ nt_status = ndr_map_error2ntstatus(ndr_err);
+ return ntstatus_to_werror(nt_status);
+ }
+
+ out->msg = msg;
+ out->guid_value = guid_value;
+ out->when_changed = whenChanged_s;
+ out->meta_data = md;
+ return WERR_OK;
+}
+
+WERROR dsdb_extended_replicated_objects_commit(struct ldb_context *ldb,
+ const char *partition_dn,
+ const struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr,
+ uint32_t object_count,
+ const struct drsuapi_DsReplicaObjectListItemEx *first_object,
+ uint32_t linked_attributes_count,
+ const struct drsuapi_DsReplicaLinkedAttribute *linked_attributes,
+ const struct repsFromTo1 *source_dsa,
+ const struct drsuapi_DsReplicaCursor2CtrEx *uptodateness_vector,
+ const DATA_BLOB *gensec_skey,
+ TALLOC_CTX *mem_ctx,
+ struct dsdb_extended_replicated_objects **_out)
+{
+ WERROR status;
+ const struct dsdb_schema *schema;
+ struct dsdb_extended_replicated_objects *out;
+ struct ldb_result *ext_res;
+ const struct drsuapi_DsReplicaObjectListItemEx *cur;
+ uint32_t i;
+ int ret;
+
+ schema = dsdb_get_schema(ldb);
+ if (!schema) {
+ return WERR_DS_SCHEMA_NOT_LOADED;
+ }
+
+ status = dsdb_verify_oid_mappings_drsuapi(schema, mapping_ctr);
+ W_ERROR_NOT_OK_RETURN(status);
+
+ out = talloc_zero(mem_ctx, struct dsdb_extended_replicated_objects);
+ W_ERROR_HAVE_NO_MEMORY(out);
+ out->version = DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION;
+
+ out->partition_dn = ldb_dn_new(out, ldb, partition_dn);
+ W_ERROR_HAVE_NO_MEMORY(out->partition_dn);
+
+ out->source_dsa = source_dsa;
+ out->uptodateness_vector= uptodateness_vector;
+
+ out->num_objects = object_count;
+ out->objects = talloc_array(out,
+ struct dsdb_extended_replicated_object,
+ out->num_objects);
+ W_ERROR_HAVE_NO_MEMORY(out->objects);
+
+ for (i=0, cur = first_object; cur; cur = cur->next_object, i++) {
+ if (i == out->num_objects) {
+ return WERR_FOOBAR;
+ }
+
+ status = dsdb_convert_object(ldb, schema, out, cur, gensec_skey, out->objects, &out->objects[i]);
+ W_ERROR_NOT_OK_RETURN(status);
+ }
+ if (i != out->num_objects) {
+ return WERR_FOOBAR;
+ }
+
+ /* TODO: handle linked attributes */
+
+ ret = ldb_extended(ldb, DSDB_EXTENDED_REPLICATED_OBJECTS_OID, out, &ext_res);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0,("Failed to apply records: %s: %s\n",
+ ldb_errstring(ldb), ldb_strerror(ret)));
+ talloc_free(out);
+ return WERR_FOOBAR;
+ }
+ talloc_free(ext_res);
+
+ if (_out) {
+ *_out = out;
+ } else {
+ talloc_free(out);
+ }
+
+ return WERR_OK;
+}
diff --git a/source4/dsdb/samdb/cracknames.c b/source4/dsdb/samdb/cracknames.c
new file mode 100644
index 0000000000..9bcb007358
--- /dev/null
+++ b/source4/dsdb/samdb/cracknames.c
@@ -0,0 +1,1326 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the drsuapi pipe
+ DsCrackNames()
+
+ Copyright (C) Stefan Metzmacher 2004
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "librpc/gen_ndr/drsuapi.h"
+#include "rpc_server/common/common.h"
+#include "lib/events/events.h"
+#include "lib/ldb/include/ldb.h"
+#include "lib/ldb/include/ldb_errors.h"
+#include "system/kerberos.h"
+#include "auth/kerberos/kerberos.h"
+#include "libcli/ldap/ldap_ndr.h"
+#include "libcli/security/security.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "auth/auth.h"
+#include "../lib/util/util_ldb.h"
+#include "dsdb/samdb/samdb.h"
+#include "param/param.h"
+
+static WERROR DsCrackNameOneFilter(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
+ struct smb_krb5_context *smb_krb5_context,
+ uint32_t format_flags, uint32_t format_offered, uint32_t format_desired,
+ struct ldb_dn *name_dn, const char *name,
+ const char *domain_filter, const char *result_filter,
+ struct drsuapi_DsNameInfo1 *info1);
+static WERROR DsCrackNameOneSyntactical(TALLOC_CTX *mem_ctx,
+ uint32_t format_offered, uint32_t format_desired,
+ struct ldb_dn *name_dn, const char *name,
+ struct drsuapi_DsNameInfo1 *info1);
+
+static WERROR dns_domain_from_principal(TALLOC_CTX *mem_ctx, struct smb_krb5_context *smb_krb5_context,
+ const char *name,
+ struct drsuapi_DsNameInfo1 *info1)
+{
+ krb5_error_code ret;
+ krb5_principal principal;
+ /* perhaps it's a principal with a realm, so return the right 'domain only' response */
+ char **realm;
+ ret = krb5_parse_name_flags(smb_krb5_context->krb5_context, name,
+ KRB5_PRINCIPAL_PARSE_MUST_REALM, &principal);
+ if (ret) {
+ info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
+ return WERR_OK;
+ }
+
+ /* This isn't an allocation assignemnt, so it is free'ed with the krb5_free_principal */
+ realm = krb5_princ_realm(smb_krb5_context->krb5_context, principal);
+
+ info1->dns_domain_name = talloc_strdup(mem_ctx, *realm);
+ krb5_free_principal(smb_krb5_context->krb5_context, principal);
+
+ W_ERROR_HAVE_NO_MEMORY(info1->dns_domain_name);
+
+ info1->status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY;
+ return WERR_OK;
+}
+
+static enum drsuapi_DsNameStatus LDB_lookup_spn_alias(krb5_context context, struct ldb_context *ldb_ctx,
+ TALLOC_CTX *mem_ctx,
+ const char *alias_from,
+ char **alias_to)
+{
+ int i;
+ int ret;
+ struct ldb_result *res;
+ struct ldb_message_element *spnmappings;
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_dn *service_dn;
+ char *service_dn_str;
+
+ const char *directory_attrs[] = {
+ "sPNMappings",
+ NULL
+ };
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (!tmp_ctx) {
+ return DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
+ }
+
+ service_dn = ldb_dn_new(tmp_ctx, ldb_ctx, "CN=Directory Service,CN=Windows NT,CN=Services");
+ if ( ! ldb_dn_add_base(service_dn, samdb_config_dn(ldb_ctx))) {
+ return DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
+ }
+ service_dn_str = ldb_dn_alloc_linearized(tmp_ctx, service_dn);
+ if ( ! service_dn_str) {
+ return DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
+ }
+
+ ret = ldb_search(ldb_ctx, tmp_ctx, &res, service_dn, LDB_SCOPE_BASE,
+ directory_attrs, "(objectClass=nTDSService)");
+
+ if (ret != LDB_SUCCESS && ret != LDB_ERR_NO_SUCH_OBJECT) {
+ DEBUG(1, ("ldb_search: dn: %s not found: %s", service_dn_str, ldb_errstring(ldb_ctx)));
+ return DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
+ } else if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+ DEBUG(1, ("ldb_search: dn: %s not found", service_dn_str));
+ return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
+ } else if (res->count != 1) {
+ talloc_free(res);
+ DEBUG(1, ("ldb_search: dn: %s not found", service_dn_str));
+ return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
+ }
+
+ spnmappings = ldb_msg_find_element(res->msgs[0], "sPNMappings");
+ if (!spnmappings || spnmappings->num_values == 0) {
+ DEBUG(1, ("ldb_search: dn: %s no sPNMappings attribute", service_dn_str));
+ talloc_free(tmp_ctx);
+ return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
+ }
+
+ for (i = 0; i < spnmappings->num_values; i++) {
+ char *mapping, *p, *str;
+ mapping = talloc_strdup(tmp_ctx,
+ (const char *)spnmappings->values[i].data);
+ if (!mapping) {
+ DEBUG(1, ("LDB_lookup_spn_alias: ldb_search: dn: %s did not have an sPNMapping\n", service_dn_str));
+ talloc_free(tmp_ctx);
+ return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
+ }
+
+ /* C string manipulation sucks */
+
+ p = strchr(mapping, '=');
+ if (!p) {
+ DEBUG(1, ("ldb_search: dn: %s sPNMapping malformed: %s\n",
+ service_dn_str, mapping));
+ talloc_free(tmp_ctx);
+ return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
+ }
+ p[0] = '\0';
+ p++;
+ do {
+ str = p;
+ p = strchr(p, ',');
+ if (p) {
+ p[0] = '\0';
+ p++;
+ }
+ if (strcasecmp(str, alias_from) == 0) {
+ *alias_to = mapping;
+ talloc_steal(mem_ctx, mapping);
+ talloc_free(tmp_ctx);
+ return DRSUAPI_DS_NAME_STATUS_OK;
+ }
+ } while (p);
+ }
+ DEBUG(4, ("LDB_lookup_spn_alias: no alias for service %s applicable\n", alias_from));
+ talloc_free(tmp_ctx);
+ return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
+}
+
+/* When cracking a ServicePrincipalName, many services may be served
+ * by the host/ servicePrincipalName. The incoming query is for cifs/
+ * but we translate it here, and search on host/. This is done after
+ * the cifs/ entry has been searched for, making this a fallback */
+
+static WERROR DsCrackNameSPNAlias(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
+ struct smb_krb5_context *smb_krb5_context,
+ uint32_t format_flags, uint32_t format_offered, uint32_t format_desired,
+ const char *name, struct drsuapi_DsNameInfo1 *info1)
+{
+ WERROR wret;
+ krb5_error_code ret;
+ krb5_principal principal;
+ const char *service, *dns_name;
+ char *new_service;
+ char *new_princ;
+ enum drsuapi_DsNameStatus namestatus;
+
+ /* parse principal */
+ ret = krb5_parse_name_flags(smb_krb5_context->krb5_context,
+ name, KRB5_PRINCIPAL_PARSE_NO_REALM, &principal);
+ if (ret) {
+ DEBUG(2, ("Could not parse principal: %s: %s",
+ name, smb_get_krb5_error_message(smb_krb5_context->krb5_context,
+ ret, mem_ctx)));
+ return WERR_NOMEM;
+ }
+
+ /* grab cifs/, http/ etc */
+
+ /* This is checked for in callers, but be safe */
+ if (principal->name.name_string.len < 2) {
+ info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
+ return WERR_OK;
+ }
+ service = principal->name.name_string.val[0];
+ dns_name = principal->name.name_string.val[1];
+
+ /* MAP it */
+ namestatus = LDB_lookup_spn_alias(smb_krb5_context->krb5_context,
+ sam_ctx, mem_ctx,
+ service, &new_service);
+
+ if (namestatus == DRSUAPI_DS_NAME_STATUS_NOT_FOUND) {
+ info1->status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY;
+ info1->dns_domain_name = talloc_strdup(mem_ctx, dns_name);
+ if (!info1->dns_domain_name) {
+ krb5_free_principal(smb_krb5_context->krb5_context, principal);
+ return WERR_NOMEM;
+ }
+ return WERR_OK;
+ } else if (namestatus != DRSUAPI_DS_NAME_STATUS_OK) {
+ info1->status = namestatus;
+ krb5_free_principal(smb_krb5_context->krb5_context, principal);
+ return WERR_OK;
+ }
+
+ /* ooh, very nasty playing around in the Principal... */
+ free(principal->name.name_string.val[0]);
+ principal->name.name_string.val[0] = strdup(new_service);
+ if (!principal->name.name_string.val[0]) {
+ krb5_free_principal(smb_krb5_context->krb5_context, principal);
+ return WERR_NOMEM;
+ }
+
+ /* reform principal */
+ ret = krb5_unparse_name_flags(smb_krb5_context->krb5_context, principal,
+ KRB5_PRINCIPAL_UNPARSE_NO_REALM, &new_princ);
+
+ if (ret) {
+ krb5_free_principal(smb_krb5_context->krb5_context, principal);
+ return WERR_NOMEM;
+ }
+
+ wret = DsCrackNameOneName(sam_ctx, mem_ctx, format_flags, format_offered, format_desired,
+ new_princ, info1);
+ free(new_princ);
+ if (W_ERROR_IS_OK(wret) && (info1->status == DRSUAPI_DS_NAME_STATUS_NOT_FOUND)) {
+ info1->status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY;
+ info1->dns_domain_name = talloc_strdup(mem_ctx, dns_name);
+ if (!info1->dns_domain_name) {
+ wret = WERR_NOMEM;
+ }
+ }
+ krb5_free_principal(smb_krb5_context->krb5_context, principal);
+ return wret;
+}
+
+/* Subcase of CrackNames, for the userPrincipalName */
+
+static WERROR DsCrackNameUPN(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
+ struct smb_krb5_context *smb_krb5_context,
+ uint32_t format_flags, uint32_t format_offered, uint32_t format_desired,
+ const char *name, struct drsuapi_DsNameInfo1 *info1)
+{
+ int ldb_ret;
+ WERROR status;
+ const char *domain_filter = NULL;
+ const char *result_filter = NULL;
+ krb5_error_code ret;
+ krb5_principal principal;
+ char **realm;
+ char *unparsed_name_short;
+ const char *domain_attrs[] = { NULL };
+ struct ldb_result *domain_res = NULL;
+
+ /* Prevent recursion */
+ if (!name) {
+ info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
+ return WERR_OK;
+ }
+
+ ret = krb5_parse_name_flags(smb_krb5_context->krb5_context, name,
+ KRB5_PRINCIPAL_PARSE_MUST_REALM, &principal);
+ if (ret) {
+ info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
+ return WERR_OK;
+ }
+
+ realm = krb5_princ_realm(smb_krb5_context->krb5_context, principal);
+
+ ldb_ret = ldb_search(sam_ctx, mem_ctx, &domain_res,
+ samdb_partitions_dn(sam_ctx, mem_ctx),
+ LDB_SCOPE_ONELEVEL,
+ domain_attrs,
+ "(&(&(|(&(dnsRoot=%s)(nETBIOSName=*))(nETBIOSName=%s))(objectclass=crossRef))(ncName=*))",
+ ldb_binary_encode_string(mem_ctx, *realm),
+ ldb_binary_encode_string(mem_ctx, *realm));
+
+ if (ldb_ret != LDB_SUCCESS) {
+ DEBUG(2, ("DsCrackNameUPN domain ref search failed: %s", ldb_errstring(sam_ctx)));
+ info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
+ return WERR_OK;
+ }
+
+ switch (domain_res->count) {
+ case 1:
+ break;
+ case 0:
+ return dns_domain_from_principal(mem_ctx, smb_krb5_context,
+ name, info1);
+ default:
+ info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
+ return WERR_OK;
+ }
+
+ ret = krb5_unparse_name_flags(smb_krb5_context->krb5_context, principal,
+ KRB5_PRINCIPAL_UNPARSE_NO_REALM, &unparsed_name_short);
+ krb5_free_principal(smb_krb5_context->krb5_context, principal);
+
+ if (ret) {
+ free(unparsed_name_short);
+ return WERR_NOMEM;
+ }
+
+ /* This may need to be extended for more userPrincipalName variations */
+ result_filter = talloc_asprintf(mem_ctx, "(&(objectClass=user)(samAccountName=%s))",
+ ldb_binary_encode_string(mem_ctx, unparsed_name_short));
+
+ domain_filter = talloc_asprintf(mem_ctx, "(distinguishedName=%s)", ldb_dn_get_linearized(domain_res->msgs[0]->dn));
+
+ if (!result_filter || !domain_filter) {
+ free(unparsed_name_short);
+ return WERR_NOMEM;
+ }
+ status = DsCrackNameOneFilter(sam_ctx, mem_ctx,
+ smb_krb5_context,
+ format_flags, format_offered, format_desired,
+ NULL, unparsed_name_short, domain_filter, result_filter,
+ info1);
+ free(unparsed_name_short);
+
+ return status;
+}
+
+/* Crack a single 'name', from format_offered into format_desired, returning the result in info1 */
+
+WERROR DsCrackNameOneName(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
+ uint32_t format_flags, uint32_t format_offered, uint32_t format_desired,
+ const char *name, struct drsuapi_DsNameInfo1 *info1)
+{
+ krb5_error_code ret;
+ const char *domain_filter = NULL;
+ const char *result_filter = NULL;
+ struct ldb_dn *name_dn = NULL;
+
+ struct smb_krb5_context *smb_krb5_context = NULL;
+
+ info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
+ info1->dns_domain_name = NULL;
+ info1->result_name = NULL;
+
+ if (!name) {
+ return WERR_INVALID_PARAM;
+ }
+
+ /* TODO: - fill the correct names in all cases!
+ * - handle format_flags
+ */
+
+ /* here we need to set the domain_filter and/or the result_filter */
+ switch (format_offered) {
+ case DRSUAPI_DS_NAME_FORMAT_UNKNOWN:
+ {
+ int i;
+ enum drsuapi_DsNameFormat formats[] = {
+ DRSUAPI_DS_NAME_FORMAT_FQDN_1779, DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL,
+ DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT, DRSUAPI_DS_NAME_FORMAT_CANONICAL,
+ DRSUAPI_DS_NAME_FORMAT_GUID, DRSUAPI_DS_NAME_FORMAT_DISPLAY,
+ DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL,
+ DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY,
+ DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX
+ };
+ WERROR werr;
+ for (i=0; i < ARRAY_SIZE(formats); i++) {
+ werr = DsCrackNameOneName(sam_ctx, mem_ctx, format_flags, formats[i], format_desired, name, info1);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+ if (info1->status != DRSUAPI_DS_NAME_STATUS_NOT_FOUND) {
+ return werr;
+ }
+ }
+ return werr;
+ }
+
+ case DRSUAPI_DS_NAME_FORMAT_CANONICAL:
+ case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX:
+ {
+ char *str, *s, *account;
+
+ if (strlen(name) == 0) {
+ info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
+ return WERR_OK;
+ }
+
+ str = talloc_strdup(mem_ctx, name);
+ W_ERROR_HAVE_NO_MEMORY(str);
+
+ if (format_offered == DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX) {
+ /* Look backwards for the \n, and replace it with / */
+ s = strrchr(str, '\n');
+ if (!s) {
+ info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
+ return WERR_OK;
+ }
+ s[0] = '/';
+ }
+
+ s = strchr(str, '/');
+ if (!s) {
+ /* there must be at least one / */
+ info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
+ return WERR_OK;
+ }
+
+ s[0] = '\0';
+ s++;
+
+ domain_filter = talloc_asprintf(mem_ctx, "(&(objectClass=crossRef)(ncName=%s))",
+ ldb_dn_get_linearized(samdb_dns_domain_to_dn(sam_ctx, mem_ctx, str)));
+ W_ERROR_HAVE_NO_MEMORY(domain_filter);
+
+ /* There may not be anything after the domain component (search for the domain itself) */
+ if (s[0]) {
+
+ account = strrchr(s, '/');
+ if (!account) {
+ account = s;
+ } else {
+ account++;
+ }
+ account = ldb_binary_encode_string(mem_ctx, account);
+ W_ERROR_HAVE_NO_MEMORY(account);
+ result_filter = talloc_asprintf(mem_ctx, "(name=%s)",
+ account);
+ W_ERROR_HAVE_NO_MEMORY(result_filter);
+ }
+ break;
+ }
+ case DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT: {
+ char *p;
+ char *domain;
+ const char *account = NULL;
+
+ domain = talloc_strdup(mem_ctx, name);
+ W_ERROR_HAVE_NO_MEMORY(domain);
+
+ p = strchr(domain, '\\');
+ if (!p) {
+ /* invalid input format */
+ info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
+ return WERR_OK;
+ }
+ p[0] = '\0';
+
+ if (p[1]) {
+ account = &p[1];
+ }
+
+ domain_filter = talloc_asprintf(mem_ctx,
+ "(&(&(nETBIOSName=%s)(objectclass=crossRef))(ncName=*))",
+ ldb_binary_encode_string(mem_ctx, domain));
+ W_ERROR_HAVE_NO_MEMORY(domain_filter);
+ if (account) {
+ result_filter = talloc_asprintf(mem_ctx, "(sAMAccountName=%s)",
+ ldb_binary_encode_string(mem_ctx, account));
+ W_ERROR_HAVE_NO_MEMORY(result_filter);
+ }
+
+ talloc_free(domain);
+ break;
+ }
+
+ /* A LDAP DN as a string */
+ case DRSUAPI_DS_NAME_FORMAT_FQDN_1779: {
+ domain_filter = NULL;
+ name_dn = ldb_dn_new(mem_ctx, sam_ctx, name);
+ if (! ldb_dn_validate(name_dn)) {
+ info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
+ return WERR_OK;
+ }
+ break;
+ }
+
+ /* A GUID as a string */
+ case DRSUAPI_DS_NAME_FORMAT_GUID: {
+ struct GUID guid;
+ char *ldap_guid;
+ NTSTATUS nt_status;
+ domain_filter = NULL;
+
+ nt_status = GUID_from_string(name, &guid);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
+ return WERR_OK;
+ }
+
+ ldap_guid = ldap_encode_ndr_GUID(mem_ctx, &guid);
+ if (!ldap_guid) {
+ return WERR_NOMEM;
+ }
+ result_filter = talloc_asprintf(mem_ctx, "(objectGUID=%s)",
+ ldap_guid);
+ W_ERROR_HAVE_NO_MEMORY(result_filter);
+ break;
+ }
+ case DRSUAPI_DS_NAME_FORMAT_DISPLAY: {
+ domain_filter = NULL;
+
+ result_filter = talloc_asprintf(mem_ctx, "(|(displayName=%s)(samAccountName=%s))",
+ ldb_binary_encode_string(mem_ctx, name),
+ ldb_binary_encode_string(mem_ctx, name));
+ W_ERROR_HAVE_NO_MEMORY(result_filter);
+ break;
+ }
+
+ /* A S-1234-5678 style string */
+ case DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY: {
+ struct dom_sid *sid = dom_sid_parse_talloc(mem_ctx, name);
+ char *ldap_sid;
+
+ domain_filter = NULL;
+ if (!sid) {
+ info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
+ return WERR_OK;
+ }
+ ldap_sid = ldap_encode_ndr_dom_sid(mem_ctx,
+ sid);
+ if (!ldap_sid) {
+ return WERR_NOMEM;
+ }
+ result_filter = talloc_asprintf(mem_ctx, "(objectSid=%s)",
+ ldap_sid);
+ W_ERROR_HAVE_NO_MEMORY(result_filter);
+ break;
+ }
+ case DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL: {
+ krb5_principal principal;
+ char *unparsed_name;
+
+ ret = smb_krb5_init_context(mem_ctx,
+ ldb_get_event_context(sam_ctx),
+ (struct loadparm_context *)ldb_get_opaque(sam_ctx, "loadparm"),
+ &smb_krb5_context);
+
+ if (ret) {
+ return WERR_NOMEM;
+ }
+
+ ret = krb5_parse_name(smb_krb5_context->krb5_context, name, &principal);
+ if (ret) {
+ info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
+ return WERR_OK;
+ }
+
+ domain_filter = NULL;
+
+ ret = krb5_unparse_name(smb_krb5_context->krb5_context, principal, &unparsed_name);
+ if (ret) {
+ krb5_free_principal(smb_krb5_context->krb5_context, principal);
+ return WERR_NOMEM;
+ }
+
+ krb5_free_principal(smb_krb5_context->krb5_context, principal);
+ result_filter = talloc_asprintf(mem_ctx, "(&(objectClass=user)(userPrincipalName=%s))",
+ ldb_binary_encode_string(mem_ctx, unparsed_name));
+
+ free(unparsed_name);
+ W_ERROR_HAVE_NO_MEMORY(result_filter);
+ break;
+ }
+ case DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL: {
+ krb5_principal principal;
+ char *unparsed_name_short;
+ char *service;
+
+ ret = smb_krb5_init_context(mem_ctx,
+ ldb_get_event_context(sam_ctx),
+ (struct loadparm_context *)ldb_get_opaque(sam_ctx, "loadparm"),
+ &smb_krb5_context);
+
+ if (ret) {
+ return WERR_NOMEM;
+ }
+
+ ret = krb5_parse_name(smb_krb5_context->krb5_context, name, &principal);
+ if (ret == 0 && principal->name.name_string.len < 2) {
+ info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
+ krb5_free_principal(smb_krb5_context->krb5_context, principal);
+ return WERR_OK;
+ }
+ ret = krb5_parse_name_flags(smb_krb5_context->krb5_context, name,
+ KRB5_PRINCIPAL_PARSE_NO_REALM, &principal);
+ if (ret) {
+ krb5_free_principal(smb_krb5_context->krb5_context, principal);
+
+ return dns_domain_from_principal(mem_ctx, smb_krb5_context,
+ name, info1);
+ }
+
+ domain_filter = NULL;
+
+ ret = krb5_unparse_name_flags(smb_krb5_context->krb5_context, principal,
+ KRB5_PRINCIPAL_UNPARSE_NO_REALM, &unparsed_name_short);
+ if (ret) {
+ krb5_free_principal(smb_krb5_context->krb5_context, principal);
+ return WERR_NOMEM;
+ }
+
+ service = principal->name.name_string.val[0];
+ if ((principal->name.name_string.len == 2) && (strcasecmp(service, "host") == 0)) {
+ /* the 'cn' attribute is just the leading part of the name */
+ char *computer_name;
+ computer_name = talloc_strndup(mem_ctx, principal->name.name_string.val[1],
+ strcspn(principal->name.name_string.val[1], "."));
+ if (computer_name == NULL) {
+ return WERR_NOMEM;
+ }
+
+ result_filter = talloc_asprintf(mem_ctx, "(|(&(servicePrincipalName=%s)(objectClass=user))(&(cn=%s)(objectClass=computer)))",
+ ldb_binary_encode_string(mem_ctx, unparsed_name_short),
+ ldb_binary_encode_string(mem_ctx, computer_name));
+ } else {
+ result_filter = talloc_asprintf(mem_ctx, "(&(servicePrincipalName=%s)(objectClass=user))",
+ ldb_binary_encode_string(mem_ctx, unparsed_name_short));
+ }
+ krb5_free_principal(smb_krb5_context->krb5_context, principal);
+ free(unparsed_name_short);
+ W_ERROR_HAVE_NO_MEMORY(result_filter);
+
+ break;
+ }
+ default: {
+ info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
+ return WERR_OK;
+ }
+
+ }
+
+ if (format_flags & DRSUAPI_DS_NAME_FLAG_SYNTACTICAL_ONLY) {
+ return DsCrackNameOneSyntactical(mem_ctx, format_offered, format_desired,
+ name_dn, name, info1);
+ }
+
+ return DsCrackNameOneFilter(sam_ctx, mem_ctx,
+ smb_krb5_context,
+ format_flags, format_offered, format_desired,
+ name_dn, name,
+ domain_filter, result_filter,
+ info1);
+}
+
+/* Subcase of CrackNames. It is possible to translate a LDAP-style DN
+ * (FQDN_1779) into a canoical name without actually searching the
+ * database */
+
+static WERROR DsCrackNameOneSyntactical(TALLOC_CTX *mem_ctx,
+ uint32_t format_offered, uint32_t format_desired,
+ struct ldb_dn *name_dn, const char *name,
+ struct drsuapi_DsNameInfo1 *info1)
+{
+ char *cracked;
+ if (format_offered != DRSUAPI_DS_NAME_FORMAT_FQDN_1779) {
+ info1->status = DRSUAPI_DS_NAME_STATUS_NO_SYNTACTICAL_MAPPING;
+ return WERR_OK;
+ }
+
+ switch (format_desired) {
+ case DRSUAPI_DS_NAME_FORMAT_CANONICAL:
+ cracked = ldb_dn_canonical_string(mem_ctx, name_dn);
+ break;
+ case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX:
+ cracked = ldb_dn_canonical_ex_string(mem_ctx, name_dn);
+ break;
+ default:
+ info1->status = DRSUAPI_DS_NAME_STATUS_NO_SYNTACTICAL_MAPPING;
+ return WERR_OK;
+ }
+ info1->status = DRSUAPI_DS_NAME_STATUS_OK;
+ info1->result_name = cracked;
+ if (!cracked) {
+ return WERR_NOMEM;
+ }
+
+ return WERR_OK;
+}
+
+/* Given a filter for the domain, and one for the result, perform the
+ * ldb search. The format offered and desired flags change the
+ * behaviours, including what attributes to return.
+ *
+ * The smb_krb5_context is required because we use the krb5 libs for principal parsing
+ */
+
+static WERROR DsCrackNameOneFilter(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
+ struct smb_krb5_context *smb_krb5_context,
+ uint32_t format_flags, uint32_t format_offered, uint32_t format_desired,
+ struct ldb_dn *name_dn, const char *name,
+ const char *domain_filter, const char *result_filter,
+ struct drsuapi_DsNameInfo1 *info1)
+{
+ int ldb_ret;
+ struct ldb_result *domain_res = NULL;
+ const char * const *domain_attrs;
+ const char * const *result_attrs;
+ struct ldb_message **result_res = NULL;
+ struct ldb_message *result = NULL;
+ struct ldb_dn *result_basedn = NULL;
+ int i;
+ char *p;
+ struct ldb_dn *partitions_basedn = samdb_partitions_dn(sam_ctx, mem_ctx);
+
+ const char * const _domain_attrs_1779[] = { "ncName", "dnsRoot", NULL};
+ const char * const _result_attrs_null[] = { NULL };
+
+ const char * const _domain_attrs_canonical[] = { "ncName", "dnsRoot", NULL};
+ const char * const _result_attrs_canonical[] = { "canonicalName", NULL };
+
+ const char * const _domain_attrs_nt4[] = { "ncName", "dnsRoot", "nETBIOSName", NULL};
+ const char * const _result_attrs_nt4[] = { "sAMAccountName", "objectSid", "objectClass", NULL};
+
+ const char * const _domain_attrs_guid[] = { "ncName", "dnsRoot", NULL};
+ const char * const _result_attrs_guid[] = { "objectGUID", NULL};
+
+ const char * const _domain_attrs_display[] = { "ncName", "dnsRoot", NULL};
+ const char * const _result_attrs_display[] = { "displayName", "samAccountName", NULL};
+
+ const char * const _domain_attrs_none[] = { "ncName", "dnsRoot" , NULL};
+ const char * const _result_attrs_none[] = { NULL};
+
+ /* here we need to set the attrs lists for domain and result lookups */
+ switch (format_desired) {
+ case DRSUAPI_DS_NAME_FORMAT_FQDN_1779:
+ case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX:
+ domain_attrs = _domain_attrs_1779;
+ result_attrs = _result_attrs_null;
+ break;
+ case DRSUAPI_DS_NAME_FORMAT_CANONICAL:
+ domain_attrs = _domain_attrs_canonical;
+ result_attrs = _result_attrs_canonical;
+ break;
+ case DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT:
+ domain_attrs = _domain_attrs_nt4;
+ result_attrs = _result_attrs_nt4;
+ break;
+ case DRSUAPI_DS_NAME_FORMAT_GUID:
+ domain_attrs = _domain_attrs_guid;
+ result_attrs = _result_attrs_guid;
+ break;
+ case DRSUAPI_DS_NAME_FORMAT_DISPLAY:
+ domain_attrs = _domain_attrs_display;
+ result_attrs = _result_attrs_display;
+ break;
+ default:
+ domain_attrs = _domain_attrs_none;
+ result_attrs = _result_attrs_none;
+ break;
+ }
+
+ if (domain_filter) {
+ /* if we have a domain_filter look it up and set the result_basedn and the dns_domain_name */
+ ldb_ret = ldb_search(sam_ctx, mem_ctx, &domain_res,
+ partitions_basedn,
+ LDB_SCOPE_ONELEVEL,
+ domain_attrs,
+ "%s", domain_filter);
+
+ if (ldb_ret != LDB_SUCCESS) {
+ DEBUG(2, ("DsCrackNameOneFilter domain ref search failed: %s", ldb_errstring(sam_ctx)));
+ info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
+ return WERR_OK;
+ }
+
+ switch (domain_res->count) {
+ case 1:
+ break;
+ case 0:
+ info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
+ return WERR_OK;
+ default:
+ info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
+ return WERR_OK;
+ }
+
+ info1->dns_domain_name = samdb_result_string(domain_res->msgs[0], "dnsRoot", NULL);
+ W_ERROR_HAVE_NO_MEMORY(info1->dns_domain_name);
+ info1->status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY;
+ } else {
+ info1->dns_domain_name = NULL;
+ info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
+ }
+
+ if (result_filter) {
+ int ret;
+ struct ldb_result *res;
+ if (domain_res) {
+ result_basedn = samdb_result_dn(sam_ctx, mem_ctx, domain_res->msgs[0], "ncName", NULL);
+
+ ret = ldb_search(sam_ctx, mem_ctx, &res,
+ result_basedn, LDB_SCOPE_SUBTREE,
+ result_attrs, "%s", result_filter);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(result_res);
+ info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
+ return WERR_OK;
+ }
+ ldb_ret = res->count;
+ result_res = res->msgs;
+ } else {
+ /* search with the 'phantom root' flag */
+ struct ldb_request *req;
+
+ res = talloc_zero(mem_ctx, struct ldb_result);
+ W_ERROR_HAVE_NO_MEMORY(res);
+
+ ret = ldb_build_search_req(&req, sam_ctx, mem_ctx,
+ ldb_get_root_basedn(sam_ctx),
+ LDB_SCOPE_SUBTREE,
+ result_filter,
+ result_attrs,
+ NULL,
+ res,
+ ldb_search_default_callback,
+ NULL);
+ if (ret == LDB_SUCCESS) {
+ struct ldb_search_options_control *search_options;
+ search_options = talloc(req, struct ldb_search_options_control);
+ W_ERROR_HAVE_NO_MEMORY(search_options);
+ search_options->search_options = LDB_SEARCH_OPTION_PHANTOM_ROOT;
+
+ ret = ldb_request_add_control(req, LDB_CONTROL_SEARCH_OPTIONS_OID, false, search_options);
+ }
+ if (ret != LDB_SUCCESS) {
+ talloc_free(res);
+ info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
+ return WERR_OK;
+ }
+
+ ret = ldb_request(sam_ctx, req);
+
+ if (ret == LDB_SUCCESS) {
+ ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+ }
+
+ talloc_free(req);
+
+ if (ret != LDB_SUCCESS) {
+ DEBUG(2, ("DsCrackNameOneFilter phantom root search failed: %s",
+ ldb_errstring(sam_ctx)));
+ info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
+ return WERR_OK;
+ }
+ ldb_ret = res->count;
+ result_res = res->msgs;
+ }
+ } else if (format_offered == DRSUAPI_DS_NAME_FORMAT_FQDN_1779) {
+ ldb_ret = gendb_search_dn(sam_ctx, mem_ctx, name_dn, &result_res,
+ result_attrs);
+ } else if (domain_res) {
+ name_dn = samdb_result_dn(sam_ctx, mem_ctx, domain_res->msgs[0], "ncName", NULL);
+ ldb_ret = gendb_search_dn(sam_ctx, mem_ctx, name_dn, &result_res,
+ result_attrs);
+ } else {
+ /* Can't happen */
+ DEBUG(0, ("LOGIC ERROR: DsCrackNameOneFilter domain ref search not availible: This can't happen..."));
+ info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
+ return WERR_OK;
+ }
+
+ switch (ldb_ret) {
+ case 1:
+ result = result_res[0];
+ break;
+ case 0:
+ switch (format_offered) {
+ case DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL:
+ return DsCrackNameSPNAlias(sam_ctx, mem_ctx,
+ smb_krb5_context,
+ format_flags, format_offered, format_desired,
+ name, info1);
+
+ case DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL:
+ return DsCrackNameUPN(sam_ctx, mem_ctx, smb_krb5_context,
+ format_flags, format_offered, format_desired,
+ name, info1);
+ }
+ info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
+ return WERR_OK;
+ case -1:
+ DEBUG(2, ("DsCrackNameOneFilter result search failed: %s", ldb_errstring(sam_ctx)));
+ info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
+ return WERR_OK;
+ default:
+ switch (format_offered) {
+ case DRSUAPI_DS_NAME_FORMAT_CANONICAL:
+ case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX:
+ {
+ const char *canonical_name = NULL; /* Not required, but we get warnings... */
+ /* We may need to manually filter further */
+ for (i = 0; i < ldb_ret; i++) {
+ switch (format_offered) {
+ case DRSUAPI_DS_NAME_FORMAT_CANONICAL:
+ canonical_name = ldb_dn_canonical_string(mem_ctx, result_res[i]->dn);
+ break;
+ case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX:
+ canonical_name = ldb_dn_canonical_ex_string(mem_ctx, result_res[i]->dn);
+ break;
+ }
+ if (strcasecmp_m(canonical_name, name) == 0) {
+ result = result_res[i];
+ break;
+ }
+ }
+ if (!result) {
+ info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
+ return WERR_OK;
+ }
+ }
+ default:
+ info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
+ return WERR_OK;
+ }
+ }
+
+ info1->dns_domain_name = ldb_dn_canonical_string(mem_ctx, result->dn);
+ W_ERROR_HAVE_NO_MEMORY(info1->dns_domain_name);
+ p = strchr(info1->dns_domain_name, '/');
+ if (p) {
+ p[0] = '\0';
+ }
+
+ /* here we can use result and domain_res[0] */
+ switch (format_desired) {
+ case DRSUAPI_DS_NAME_FORMAT_FQDN_1779: {
+ info1->result_name = ldb_dn_alloc_linearized(mem_ctx, result->dn);
+ W_ERROR_HAVE_NO_MEMORY(info1->result_name);
+
+ info1->status = DRSUAPI_DS_NAME_STATUS_OK;
+ return WERR_OK;
+ }
+ case DRSUAPI_DS_NAME_FORMAT_CANONICAL: {
+ info1->result_name = samdb_result_string(result, "canonicalName", NULL);
+ info1->status = DRSUAPI_DS_NAME_STATUS_OK;
+ return WERR_OK;
+ }
+ case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX: {
+ /* Not in the virtual ldb attribute */
+ return DsCrackNameOneSyntactical(mem_ctx,
+ DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX,
+ result->dn, name, info1);
+ }
+ case DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT: {
+
+ const struct dom_sid *sid = samdb_result_dom_sid(mem_ctx, result, "objectSid");
+ const char *_acc = "", *_dom = "";
+
+ if (samdb_find_attribute(sam_ctx, result, "objectClass", "domain")) {
+
+ ldb_ret = ldb_search(sam_ctx, mem_ctx, &domain_res,
+ partitions_basedn,
+ LDB_SCOPE_ONELEVEL,
+ domain_attrs,
+ "(ncName=%s)", ldb_dn_get_linearized(result->dn));
+
+ if (ldb_ret != LDB_SUCCESS) {
+ DEBUG(2, ("DsCrackNameOneFilter domain ref search failed: %s", ldb_errstring(sam_ctx)));
+ info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
+ return WERR_OK;
+ }
+
+ switch (domain_res->count) {
+ case 1:
+ break;
+ case 0:
+ info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
+ return WERR_OK;
+ default:
+ info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
+ return WERR_OK;
+ }
+ _dom = samdb_result_string(domain_res->msgs[0], "nETBIOSName", NULL);
+ W_ERROR_HAVE_NO_MEMORY(_dom);
+ } else {
+ _acc = samdb_result_string(result, "sAMAccountName", NULL);
+ if (!_acc) {
+ info1->status = DRSUAPI_DS_NAME_STATUS_NO_MAPPING;
+ return WERR_OK;
+ }
+ if (dom_sid_in_domain(dom_sid_parse_talloc(mem_ctx, SID_BUILTIN), sid)) {
+ _dom = "BUILTIN";
+ } else {
+ const char *attrs[] = { NULL };
+ struct ldb_result *domain_res2;
+ struct dom_sid *dom_sid = dom_sid_dup(mem_ctx, sid);
+ if (!dom_sid) {
+ return WERR_OK;
+ }
+ dom_sid->num_auths--;
+ ldb_ret = ldb_search(sam_ctx, mem_ctx, &domain_res,
+ NULL,
+ LDB_SCOPE_BASE,
+ attrs,
+ "(&(objectSid=%s)(objectClass=domain))",
+ ldap_encode_ndr_dom_sid(mem_ctx, dom_sid));
+
+ if (ldb_ret != LDB_SUCCESS) {
+ DEBUG(2, ("DsCrackNameOneFilter domain search failed: %s", ldb_errstring(sam_ctx)));
+ info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
+ return WERR_OK;
+ }
+
+ switch (domain_res->count) {
+ case 1:
+ break;
+ case 0:
+ info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
+ return WERR_OK;
+ default:
+ info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
+ return WERR_OK;
+ }
+
+ ldb_ret = ldb_search(sam_ctx, mem_ctx, &domain_res2,
+ partitions_basedn,
+ LDB_SCOPE_ONELEVEL,
+ domain_attrs,
+ "(ncName=%s)", ldb_dn_get_linearized(domain_res->msgs[0]->dn));
+
+ if (ldb_ret != LDB_SUCCESS) {
+ DEBUG(2, ("DsCrackNameOneFilter domain ref search failed: %s", ldb_errstring(sam_ctx)));
+ info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
+ return WERR_OK;
+ }
+
+ switch (domain_res2->count) {
+ case 1:
+ break;
+ case 0:
+ info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
+ return WERR_OK;
+ default:
+ info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
+ return WERR_OK;
+ }
+ _dom = samdb_result_string(domain_res2->msgs[0], "nETBIOSName", NULL);
+ W_ERROR_HAVE_NO_MEMORY(_dom);
+ }
+ }
+
+ info1->result_name = talloc_asprintf(mem_ctx, "%s\\%s", _dom, _acc);
+ W_ERROR_HAVE_NO_MEMORY(info1->result_name);
+
+ info1->status = DRSUAPI_DS_NAME_STATUS_OK;
+ return WERR_OK;
+ }
+ case DRSUAPI_DS_NAME_FORMAT_GUID: {
+ struct GUID guid;
+
+ guid = samdb_result_guid(result, "objectGUID");
+
+ info1->result_name = GUID_string2(mem_ctx, &guid);
+ W_ERROR_HAVE_NO_MEMORY(info1->result_name);
+
+ info1->status = DRSUAPI_DS_NAME_STATUS_OK;
+ return WERR_OK;
+ }
+ case DRSUAPI_DS_NAME_FORMAT_DISPLAY: {
+ info1->result_name = samdb_result_string(result, "displayName", NULL);
+ if (!info1->result_name) {
+ info1->result_name = samdb_result_string(result, "sAMAccountName", NULL);
+ }
+ if (!info1->result_name) {
+ info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
+ } else {
+ info1->status = DRSUAPI_DS_NAME_STATUS_OK;
+ }
+ return WERR_OK;
+ }
+ case DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL: {
+ info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
+ return WERR_OK;
+ }
+ case DRSUAPI_DS_NAME_FORMAT_DNS_DOMAIN:
+ case DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY: {
+ info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
+ return WERR_OK;
+ }
+ default:
+ info1->status = DRSUAPI_DS_NAME_STATUS_NO_MAPPING;
+ return WERR_OK;
+ }
+}
+
+/* Given a user Principal Name (such as foo@bar.com),
+ * return the user and domain DNs. This is used in the KDC to then
+ * return the Keys and evaluate policy */
+
+NTSTATUS crack_user_principal_name(struct ldb_context *sam_ctx,
+ TALLOC_CTX *mem_ctx,
+ const char *user_principal_name,
+ struct ldb_dn **user_dn,
+ struct ldb_dn **domain_dn)
+{
+ WERROR werr;
+ struct drsuapi_DsNameInfo1 info1;
+ werr = DsCrackNameOneName(sam_ctx, mem_ctx, 0,
+ DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL,
+ DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ user_principal_name,
+ &info1);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werror_to_ntstatus(werr);
+ }
+ switch (info1.status) {
+ case DRSUAPI_DS_NAME_STATUS_OK:
+ break;
+ case DRSUAPI_DS_NAME_STATUS_NOT_FOUND:
+ case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY:
+ case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE:
+ return NT_STATUS_NO_SUCH_USER;
+ case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR:
+ default:
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ *user_dn = ldb_dn_new(mem_ctx, sam_ctx, info1.result_name);
+
+ if (domain_dn) {
+ werr = DsCrackNameOneName(sam_ctx, mem_ctx, 0,
+ DRSUAPI_DS_NAME_FORMAT_CANONICAL,
+ DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ talloc_asprintf(mem_ctx, "%s/",
+ info1.dns_domain_name),
+ &info1);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werror_to_ntstatus(werr);
+ }
+ switch (info1.status) {
+ case DRSUAPI_DS_NAME_STATUS_OK:
+ break;
+ case DRSUAPI_DS_NAME_STATUS_NOT_FOUND:
+ case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY:
+ case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE:
+ return NT_STATUS_NO_SUCH_USER;
+ case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR:
+ default:
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ *domain_dn = ldb_dn_new(mem_ctx, sam_ctx, info1.result_name);
+ }
+
+ return NT_STATUS_OK;
+
+}
+
+/* Given a Service Principal Name (such as host/foo.bar.com@BAR.COM),
+ * return the user and domain DNs. This is used in the KDC to then
+ * return the Keys and evaluate policy */
+
+NTSTATUS crack_service_principal_name(struct ldb_context *sam_ctx,
+ TALLOC_CTX *mem_ctx,
+ const char *service_principal_name,
+ struct ldb_dn **user_dn,
+ struct ldb_dn **domain_dn)
+{
+ WERROR werr;
+ struct drsuapi_DsNameInfo1 info1;
+ werr = DsCrackNameOneName(sam_ctx, mem_ctx, 0,
+ DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL,
+ DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ service_principal_name,
+ &info1);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werror_to_ntstatus(werr);
+ }
+ switch (info1.status) {
+ case DRSUAPI_DS_NAME_STATUS_OK:
+ break;
+ case DRSUAPI_DS_NAME_STATUS_NOT_FOUND:
+ case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY:
+ case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE:
+ return NT_STATUS_NO_SUCH_USER;
+ case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR:
+ default:
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ *user_dn = ldb_dn_new(mem_ctx, sam_ctx, info1.result_name);
+
+ if (domain_dn) {
+ werr = DsCrackNameOneName(sam_ctx, mem_ctx, 0,
+ DRSUAPI_DS_NAME_FORMAT_CANONICAL,
+ DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ talloc_asprintf(mem_ctx, "%s/",
+ info1.dns_domain_name),
+ &info1);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werror_to_ntstatus(werr);
+ }
+ switch (info1.status) {
+ case DRSUAPI_DS_NAME_STATUS_OK:
+ break;
+ case DRSUAPI_DS_NAME_STATUS_NOT_FOUND:
+ case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY:
+ case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE:
+ return NT_STATUS_NO_SUCH_USER;
+ case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR:
+ default:
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ *domain_dn = ldb_dn_new(mem_ctx, sam_ctx, info1.result_name);
+ }
+
+ return NT_STATUS_OK;
+
+}
+
+NTSTATUS crack_name_to_nt4_name(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx,
+ uint32_t format_offered,
+ const char *name,
+ const char **nt4_domain, const char **nt4_account)
+{
+ WERROR werr;
+ struct drsuapi_DsNameInfo1 info1;
+ struct ldb_context *ldb;
+ char *p;
+
+ /* Handle anonymous bind */
+ if (!name || !*name) {
+ *nt4_domain = "";
+ *nt4_account = "";
+ return NT_STATUS_OK;
+ }
+
+ ldb = samdb_connect(mem_ctx, ev_ctx, lp_ctx, system_session(mem_ctx, lp_ctx));
+ if (ldb == NULL) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ werr = DsCrackNameOneName(ldb, mem_ctx, 0,
+ format_offered,
+ DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT,
+ name,
+ &info1);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werror_to_ntstatus(werr);
+ }
+ switch (info1.status) {
+ case DRSUAPI_DS_NAME_STATUS_OK:
+ break;
+ case DRSUAPI_DS_NAME_STATUS_NOT_FOUND:
+ case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY:
+ case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE:
+ return NT_STATUS_NO_SUCH_USER;
+ case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR:
+ default:
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ *nt4_domain = talloc_strdup(mem_ctx, info1.result_name);
+
+ p = strchr(*nt4_domain, '\\');
+ if (!p) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ p[0] = '\0';
+
+ if (p[1]) {
+ *nt4_account = talloc_strdup(mem_ctx, &p[1]);
+ }
+
+ if (!*nt4_account || !*nt4_domain) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS crack_auto_name_to_nt4_name(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx,
+ const char *name,
+ const char **nt4_domain,
+ const char **nt4_account)
+{
+ uint32_t format_offered = DRSUAPI_DS_NAME_FORMAT_UNKNOWN;
+
+ /* Handle anonymous bind */
+ if (!name || !*name) {
+ *nt4_domain = "";
+ *nt4_account = "";
+ return NT_STATUS_OK;
+ }
+
+ if (strchr_m(name, '=')) {
+ format_offered = DRSUAPI_DS_NAME_FORMAT_FQDN_1779;
+ } else if (strchr_m(name, '@')) {
+ format_offered = DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL;
+ } else if (strchr_m(name, '\\')) {
+ format_offered = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT;
+ } else if (strchr_m(name, '/')) {
+ format_offered = DRSUAPI_DS_NAME_FORMAT_CANONICAL;
+ } else {
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ return crack_name_to_nt4_name(mem_ctx, ev_ctx, lp_ctx, format_offered, name, nt4_domain, nt4_account);
+}
diff --git a/source4/dsdb/samdb/ldb_modules/anr.c b/source4/dsdb/samdb/ldb_modules/anr.c
new file mode 100644
index 0000000000..a04f5ebfa6
--- /dev/null
+++ b/source4/dsdb/samdb/ldb_modules/anr.c
@@ -0,0 +1,370 @@
+/*
+ ldb database library
+
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2007
+ Copyright (C) Simo Sorce <idra@samba.org> 2008
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb anr module
+ *
+ * Description: module to implement 'ambiguous name resolution'
+ *
+ * Author: Andrew Bartlett
+ */
+
+#include "includes.h"
+#include "ldb_module.h"
+#include "dsdb/samdb/samdb.h"
+
+/**
+ * Make a and 'and' or 'or' tree from the two supplied elements
+ */
+static struct ldb_parse_tree *make_parse_list(struct ldb_module *module,
+ TALLOC_CTX *mem_ctx, enum ldb_parse_op op,
+ struct ldb_parse_tree *first_arm, struct ldb_parse_tree *second_arm)
+{
+ struct ldb_context *ldb;
+ struct ldb_parse_tree *list;
+
+ ldb = ldb_module_get_ctx(module);
+
+ list = talloc(mem_ctx, struct ldb_parse_tree);
+ if (list == NULL){
+ ldb_oom(ldb);
+ return NULL;
+ }
+ list->operation = op;
+
+ list->u.list.num_elements = 2;
+ list->u.list.elements = talloc_array(list, struct ldb_parse_tree *, 2);
+ if (!list->u.list.elements) {
+ ldb_oom(ldb);
+ return NULL;
+ }
+ list->u.list.elements[0] = talloc_steal(list, first_arm);
+ list->u.list.elements[1] = talloc_steal(list, second_arm);
+ return list;
+}
+
+/**
+ * Make an equality or prefix match tree, from the attribute, operation and matching value supplied
+ */
+static struct ldb_parse_tree *make_match_tree(struct ldb_module *module,
+ TALLOC_CTX *mem_ctx, enum ldb_parse_op op,
+ const char *attr, const DATA_BLOB *match)
+{
+ struct ldb_context *ldb;
+ struct ldb_parse_tree *match_tree;
+
+ ldb = ldb_module_get_ctx(module);
+
+ match_tree = talloc(mem_ctx, struct ldb_parse_tree);
+
+ /* Depending on what type of match was selected, fill in the right part of the union */
+
+ match_tree->operation = op;
+ switch (op) {
+ case LDB_OP_SUBSTRING:
+ match_tree->u.substring.attr = attr;
+
+ match_tree->u.substring.start_with_wildcard = 0;
+ match_tree->u.substring.end_with_wildcard = 1;
+ match_tree->u.substring.chunks = talloc_array(match_tree, struct ldb_val *, 2);
+
+ if (match_tree->u.substring.chunks == NULL){
+ ldb_oom(ldb);
+ return NULL;
+ }
+ match_tree->u.substring.chunks[0] = match;
+ match_tree->u.substring.chunks[1] = NULL;
+ break;
+ case LDB_OP_EQUALITY:
+ match_tree->u.equality.attr = attr;
+ match_tree->u.equality.value = *match;
+ break;
+ }
+ return match_tree;
+}
+
+struct anr_context {
+ bool found_anr;
+ struct ldb_module *module;
+ struct ldb_request *req;
+};
+
+/**
+ * Given the match for an 'ambigious name resolution' query, create a
+ * parse tree with an 'or' of all the anr attributes in the schema.
+ */
+
+/**
+ * Callback function to do the heavy lifting for the parse tree walker
+ */
+static int anr_replace_value(struct anr_context *ac,
+ TALLOC_CTX *mem_ctx,
+ const struct ldb_val *match,
+ struct ldb_parse_tree **ntree)
+{
+ struct ldb_parse_tree *tree = NULL;
+ struct ldb_module *module = ac->module;
+ struct ldb_parse_tree *match_tree;
+ struct dsdb_attribute *cur;
+ const struct dsdb_schema *schema;
+ struct ldb_context *ldb;
+ uint8_t *p;
+ enum ldb_parse_op op;
+
+ ldb = ldb_module_get_ctx(module);
+
+ schema = dsdb_get_schema(ldb);
+ if (!schema) {
+ ldb_asprintf_errstring(ldb, "no schema with which to construct anr filter");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->found_anr = true;
+
+ if (match->length > 1 && match->data[0] == '=') {
+ DATA_BLOB *match2 = talloc(mem_ctx, DATA_BLOB);
+ *match2 = data_blob_const(match->data+1, match->length - 1);
+ if (match2 == NULL){
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ match = match2;
+ op = LDB_OP_EQUALITY;
+ } else {
+ op = LDB_OP_SUBSTRING;
+ }
+ for (cur = schema->attributes; cur; cur = cur->next) {
+ if (!(cur->searchFlags & SEARCH_FLAG_ANR)) continue;
+ match_tree = make_match_tree(module, mem_ctx, op, cur->lDAPDisplayName, match);
+
+ if (tree) {
+ /* Inject an 'or' with the current tree */
+ tree = make_parse_list(module, mem_ctx, LDB_OP_OR, tree, match_tree);
+ if (tree == NULL) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ } else {
+ tree = match_tree;
+ }
+ }
+
+
+ /* If the search term has a space in it,
+ split it up at the first space. */
+
+ p = memchr(match->data, ' ', match->length);
+
+ if (p) {
+ struct ldb_parse_tree *first_split_filter, *second_split_filter, *split_filters, *match_tree_1, *match_tree_2;
+ DATA_BLOB *first_match = talloc(tree, DATA_BLOB);
+ DATA_BLOB *second_match = talloc(tree, DATA_BLOB);
+ if (!first_match || !second_match) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ *first_match = data_blob_const(match->data, p-match->data);
+ *second_match = data_blob_const(p+1, match->length - (p-match->data) - 1);
+
+ /* Add (|(&(givenname=first)(sn=second))(&(givenname=second)(sn=first))) */
+
+ match_tree_1 = make_match_tree(module, mem_ctx, op, "givenName", first_match);
+ match_tree_2 = make_match_tree(module, mem_ctx, op, "sn", second_match);
+
+ first_split_filter = make_parse_list(module, ac, LDB_OP_AND, match_tree_1, match_tree_2);
+ if (first_split_filter == NULL){
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ match_tree_1 = make_match_tree(module, mem_ctx, op, "sn", first_match);
+ match_tree_2 = make_match_tree(module, mem_ctx, op, "givenName", second_match);
+
+ second_split_filter = make_parse_list(module, ac, LDB_OP_AND, match_tree_1, match_tree_2);
+ if (second_split_filter == NULL){
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ split_filters = make_parse_list(module, mem_ctx, LDB_OP_OR,
+ first_split_filter, second_split_filter);
+ if (split_filters == NULL) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if (tree) {
+ /* Inject an 'or' with the current tree */
+ tree = make_parse_list(module, mem_ctx, LDB_OP_OR, tree, split_filters);
+ } else {
+ tree = split_filters;
+ }
+ }
+ *ntree = tree;
+ return LDB_SUCCESS;
+}
+
+/*
+ replace any occurances of an attribute with a new, generated attribute tree
+*/
+static int anr_replace_subtrees(struct anr_context *ac,
+ struct ldb_parse_tree *tree,
+ const char *attr,
+ struct ldb_parse_tree **ntree)
+{
+ int ret;
+ int i;
+
+ switch (tree->operation) {
+ case LDB_OP_AND:
+ case LDB_OP_OR:
+ for (i=0;i<tree->u.list.num_elements;i++) {
+ ret = anr_replace_subtrees(ac, tree->u.list.elements[i],
+ attr, &tree->u.list.elements[i]);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ *ntree = tree;
+ }
+ break;
+ case LDB_OP_NOT:
+ ret = anr_replace_subtrees(ac, tree->u.isnot.child, attr, &tree->u.isnot.child);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ *ntree = tree;
+ break;
+ case LDB_OP_EQUALITY:
+ if (ldb_attr_cmp(tree->u.equality.attr, attr) == 0) {
+ ret = anr_replace_value(ac, tree, &tree->u.equality.value, ntree);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+ break;
+ case LDB_OP_SUBSTRING:
+ if (ldb_attr_cmp(tree->u.substring.attr, attr) == 0) {
+ if (tree->u.substring.start_with_wildcard == 0 &&
+ tree->u.substring.end_with_wildcard == 1 &&
+ tree->u.substring.chunks[0] != NULL &&
+ tree->u.substring.chunks[1] == NULL) {
+ ret = anr_replace_value(ac, tree, tree->u.substring.chunks[0], ntree);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ return LDB_SUCCESS;
+}
+
+static int anr_search_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+ struct anr_context *ac;
+
+ ac = talloc_get_type(req->context, struct anr_context);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+ return ldb_module_send_entry(ac->req, ares->message, ares->controls);
+
+ case LDB_REPLY_REFERRAL:
+ return ldb_module_send_referral(ac->req, ares->referral);
+
+ case LDB_REPLY_DONE:
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, LDB_SUCCESS);
+
+ }
+ return LDB_SUCCESS;
+}
+
+/* search */
+static int anr_search(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct ldb_parse_tree *anr_tree;
+ struct ldb_request *down_req;
+ struct anr_context *ac;
+ int ret;
+
+ ldb = ldb_module_get_ctx(module);
+
+ ac = talloc(req, struct anr_context);
+ if (!ac) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->module = module;
+ ac->req = req;
+ ac->found_anr = false;
+
+#if 0
+ printf("oldanr : %s\n", ldb_filter_from_tree (0, req->op.search.tree));
+#endif
+
+ ret = anr_replace_subtrees(ac, req->op.search.tree, "anr", &anr_tree);
+ if (ret != LDB_SUCCESS) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if (!ac->found_anr) {
+ talloc_free(ac);
+ return ldb_next_request(module, req);
+ }
+
+ ret = ldb_build_search_req_ex(&down_req,
+ ldb, ac,
+ req->op.search.base,
+ req->op.search.scope,
+ anr_tree,
+ req->op.search.attrs,
+ req->controls,
+ ac, anr_search_callback,
+ req);
+ if (ret != LDB_SUCCESS) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ talloc_steal(down_req, anr_tree);
+
+ return ldb_next_request(module, down_req);
+}
+
+_PUBLIC_ const struct ldb_module_ops ldb_anr_module_ops = {
+ .name = "anr",
+ .search = anr_search
+};
diff --git a/source4/dsdb/samdb/ldb_modules/config.mk b/source4/dsdb/samdb/ldb_modules/config.mk
new file mode 100644
index 0000000000..583d1dcf04
--- /dev/null
+++ b/source4/dsdb/samdb/ldb_modules/config.mk
@@ -0,0 +1,325 @@
+################################################
+# Start MODULE ldb_objectguid
+[MODULE::ldb_objectguid]
+SUBSYSTEM = LIBLDB
+PRIVATE_DEPENDENCIES = SAMDB LIBTALLOC LIBEVENTS LIBNDR
+INIT_FUNCTION = LDB_MODULE(objectguid)
+# End MODULE ldb_objectguid
+################################################
+
+ldb_objectguid_OBJ_FILES = $(dsdbsrcdir)/samdb/ldb_modules/objectguid.o
+
+################################################
+# Start MODULE ldb_repl_meta_data
+[MODULE::ldb_repl_meta_data]
+SUBSYSTEM = LIBLDB
+PRIVATE_DEPENDENCIES = SAMDB LIBTALLOC LIBEVENTS \
+ LIBNDR NDR_DRSUAPI \
+ NDR_DRSBLOBS LIBNDR
+INIT_FUNCTION = LDB_MODULE(repl_meta_data)
+# End MODULE ldb_repl_meta_data
+################################################
+
+ldb_repl_meta_data_OBJ_FILES = \
+ $(dsdbsrcdir)/samdb/ldb_modules/repl_meta_data.o
+
+################################################
+# Start MODULE ldb_dsdb_cache
+[MODULE::ldb_dsdb_cache]
+SUBSYSTEM = LIBLDB
+PRIVATE_DEPENDENCIES = SAMDB LIBTALLOC LIBEVENTS
+INIT_FUNCTION = LDB_MODULE(dsdb_cache)
+# End MODULE ldb_dsdb_cache
+################################################
+
+ldb_dsdb_cache_OBJ_FILES = \
+ $(dsdbsrcdir)/samdb/ldb_modules/dsdb_cache.o
+
+################################################
+# Start MODULE ldb_schema_fsmo
+[MODULE::ldb_schema_fsmo]
+SUBSYSTEM = LIBLDB
+PRIVATE_DEPENDENCIES = SAMDB LIBTALLOC LIBEVENTS
+INIT_FUNCTION = LDB_MODULE(schema_fsmo)
+# End MODULE ldb_schema_fsmo
+################################################
+
+ldb_schema_fsmo_OBJ_FILES = \
+ $(dsdbsrcdir)/samdb/ldb_modules/schema_fsmo.o
+
+################################################
+# Start MODULE ldb_naming_fsmo
+[MODULE::ldb_naming_fsmo]
+SUBSYSTEM = LIBLDB
+PRIVATE_DEPENDENCIES = SAMDB LIBTALLOC LIBEVENTS
+INIT_FUNCTION = LDB_MODULE(naming_fsmo)
+# End MODULE ldb_naming_fsmo
+################################################
+
+ldb_naming_fsmo_OBJ_FILES = \
+ $(dsdbsrcdir)/samdb/ldb_modules/naming_fsmo.o
+
+################################################
+# Start MODULE ldb_pdc_fsmo
+[MODULE::ldb_pdc_fsmo]
+SUBSYSTEM = LIBLDB
+PRIVATE_DEPENDENCIES = SAMDB LIBTALLOC LIBEVENTS
+INIT_FUNCTION = LDB_MODULE(pdc_fsmo)
+# End MODULE ldb_pdc_fsmo
+################################################
+
+ldb_pdc_fsmo_OBJ_FILES = \
+ $(dsdbsrcdir)/samdb/ldb_modules/pdc_fsmo.o
+
+################################################
+# Start MODULE ldb_samldb
+[MODULE::ldb_samldb]
+SUBSYSTEM = LIBLDB
+PRIVATE_DEPENDENCIES = LIBTALLOC LIBEVENTS LDAP_ENCODE SAMDB
+INIT_FUNCTION = LDB_MODULE(samldb)
+#
+# End MODULE ldb_samldb
+################################################
+
+ldb_samldb_OBJ_FILES = \
+ $(dsdbsrcdir)/samdb/ldb_modules/samldb.o
+
+################################################
+# Start MODULE ldb_samba3sam
+[MODULE::ldb_samba3sam]
+SUBSYSTEM = LIBLDB
+INIT_FUNCTION = LDB_MODULE(samba3sam)
+PRIVATE_DEPENDENCIES = LIBTALLOC LIBEVENTS LIBLDB SMBPASSWD \
+ NSS_WRAPPER LIBSECURITY NDR_SECURITY
+# End MODULE ldb_samldb
+################################################
+
+ldb_samba3sam_OBJ_FILES = \
+ $(dsdbsrcdir)/samdb/ldb_modules/samba3sam.o
+
+################################################
+# Start MODULE ldb_simple_ldap_map
+[MODULE::ldb_simple_ldap_map]
+SUBSYSTEM = LIBLDB
+INIT_FUNCTION = LDB_MODULE(entryuuid),LDB_MODULE(nsuniqueid)
+PRIVATE_DEPENDENCIES = LIBTALLOC LIBEVENTS LIBLDB LIBNDR
+ENABLE = YES
+ALIASES = entryuuid nsuniqueid
+# End MODULE ldb_entryuuid
+################################################
+
+ldb_simple_ldap_map_OBJ_FILES = \
+ $(dsdbsrcdir)/samdb/ldb_modules/simple_ldap_map.o
+
+# ################################################
+# # Start MODULE ldb_proxy
+# [MODULE::ldb_proxy]
+# SUBSYSTEM = LIBLDB
+# INIT_FUNCTION = LDB_MODULE(proxy)
+# OBJ_FILES = \
+# proxy.o
+#
+# # End MODULE ldb_proxy
+# ################################################
+
+
+################################################
+# Start MODULE ldb_rootdse
+[MODULE::ldb_rootdse]
+SUBSYSTEM = LIBLDB
+PRIVATE_DEPENDENCIES = LIBTALLOC LIBEVENTS SAMDB
+INIT_FUNCTION = LDB_MODULE(rootdse)
+# End MODULE ldb_rootdse
+################################################
+
+ldb_rootdse_OBJ_FILES = $(dsdbsrcdir)/samdb/ldb_modules/rootdse.o
+
+################################################
+# Start MODULE ldb_password_hash
+[MODULE::ldb_password_hash]
+SUBSYSTEM = LIBLDB
+INIT_FUNCTION = LDB_MODULE(password_hash)
+PRIVATE_DEPENDENCIES = LIBTALLOC LIBEVENTS SAMDB LDAP_ENCODE \
+ LIBCLI_AUTH NDR_DRSBLOBS KERBEROS \
+ HEIMDAL_HDB_KEYS HEIMDAL_KRB5
+# End MODULE ldb_password_hash
+################################################
+
+ldb_password_hash_OBJ_FILES = $(dsdbsrcdir)/samdb/ldb_modules/password_hash.o
+
+################################################
+# Start MODULE ldb_local_password
+[MODULE::ldb_local_password]
+PRIVATE_DEPENDENCIES = LIBTALLOC LIBEVENTS LIBNDR SAMDB
+SUBSYSTEM = LIBLDB
+INIT_FUNCTION = LDB_MODULE(local_password)
+# End MODULE ldb_local_password
+################################################
+
+ldb_local_password_OBJ_FILES = $(dsdbsrcdir)/samdb/ldb_modules/local_password.o
+
+################################################
+# Start MODULE ldb_kludge_acl
+[MODULE::ldb_kludge_acl]
+PRIVATE_DEPENDENCIES = LIBTALLOC LIBEVENTS LIBSECURITY SAMDB
+SUBSYSTEM = LIBLDB
+INIT_FUNCTION = LDB_MODULE(kludge_acl)
+
+# End MODULE ldb_kludge_acl
+################################################
+
+ldb_kludge_acl_OBJ_FILES = $(dsdbsrcdir)/samdb/ldb_modules/kludge_acl.o
+
+################################################
+# Start MODULE ldb_extended_dn_in
+[MODULE::ldb_extended_dn_in]
+SUBSYSTEM = LIBLDB
+PRIVATE_DEPENDENCIES = LIBTALLOC LIBEVENTS
+INIT_FUNCTION = LDB_MODULE(extended_dn_in)
+# End MODULE ldb_extended_dn_in
+################################################
+
+ldb_extended_dn_in_OBJ_FILES = $(dsdbsrcdir)/samdb/ldb_modules/extended_dn_in.o
+
+################################################
+# Start MODULE ldb_extended_dn_out
+[MODULE::ldb_extended_dn_out]
+SUBSYSTEM = LIBLDB
+PRIVATE_DEPENDENCIES = LIBTALLOC LIBEVENTS
+INIT_FUNCTION = LDB_MODULE(extended_dn_out_ldb),LDB_MODULE(extended_dn_out_dereference)
+ENABLE = YES
+ALIASES = extended_dn_out_ldb extended_dn_out_dereference
+# End MODULE ldb_extended_dn_out
+################################################
+
+ldb_extended_dn_out_OBJ_FILES = $(dsdbsrcdir)/samdb/ldb_modules/extended_dn_out.o
+
+################################################
+# Start MODULE ldb_extended_dn_store
+[MODULE::ldb_extended_dn_store]
+SUBSYSTEM = LIBLDB
+PRIVATE_DEPENDENCIES = LIBTALLOC LIBEVENTS
+INIT_FUNCTION = LDB_MODULE(extended_dn_store)
+# End MODULE ldb_extended_dn_store
+################################################
+
+ldb_extended_dn_store_OBJ_FILES = $(dsdbsrcdir)/samdb/ldb_modules/extended_dn_store.o
+
+################################################
+# Start MODULE ldb_show_deleted
+[MODULE::ldb_show_deleted]
+SUBSYSTEM = LIBLDB
+PRIVATE_DEPENDENCIES = LIBTALLOC LIBEVENTS
+INIT_FUNCTION = LDB_MODULE(show_deleted)
+# End MODULE ldb_show_deleted
+################################################
+
+ldb_show_deleted_OBJ_FILES = $(dsdbsrcdir)/samdb/ldb_modules/show_deleted.o
+
+################################################
+# Start MODULE ldb_partition
+[MODULE::ldb_partition]
+SUBSYSTEM = LIBLDB
+PRIVATE_DEPENDENCIES = LIBTALLOC LIBEVENTS SAMDB
+INIT_FUNCTION = LDB_MODULE(partition)
+# End MODULE ldb_partition
+################################################
+
+ldb_partition_OBJ_FILES = $(dsdbsrcdir)/samdb/ldb_modules/partition.o
+
+################################################
+# Start MODULE ldb_update_kt
+[MODULE::ldb_update_keytab]
+SUBSYSTEM = LIBLDB
+PRIVATE_DEPENDENCIES = LIBTALLOC LIBEVENTS CREDENTIALS
+#Also depends on credentials, but that would loop
+INIT_FUNCTION = LDB_MODULE(update_keytab)
+# End MODULE ldb_update_kt
+################################################
+
+ldb_update_keytab_OBJ_FILES = $(dsdbsrcdir)/samdb/ldb_modules/update_keytab.o
+
+################################################
+# Start MODULE ldb_objectclass
+[MODULE::ldb_objectclass]
+INIT_FUNCTION = LDB_MODULE(objectclass)
+CFLAGS = -Ilib/ldb/include
+PRIVATE_DEPENDENCIES = LIBTALLOC LIBEVENTS LIBSECURITY NDR_SECURITY SAMDB
+SUBSYSTEM = LIBLDB
+# End MODULE ldb_objectclass
+################################################
+
+ldb_objectclass_OBJ_FILES = $(dsdbsrcdir)/samdb/ldb_modules/objectclass.o
+
+################################################
+# Start MODULE ldb_subtree_rename
+[MODULE::ldb_subtree_rename]
+INIT_FUNCTION = LDB_MODULE(subtree_rename)
+CFLAGS = -Ilib/ldb/include
+PRIVATE_DEPENDENCIES = LIBTALLOC LIBEVENTS
+SUBSYSTEM = LIBLDB
+# End MODULE ldb_subtree_rename
+################################################
+
+ldb_subtree_rename_OBJ_FILES = $(dsdbsrcdir)/samdb/ldb_modules/subtree_rename.o
+
+################################################
+# Start MODULE ldb_subtree_rename
+[MODULE::ldb_subtree_delete]
+INIT_FUNCTION = LDB_MODULE(subtree_delete)
+CFLAGS = -Ilib/ldb/include
+PRIVATE_DEPENDENCIES = LIBTALLOC LIBEVENTS
+SUBSYSTEM = LIBLDB
+# End MODULE ldb_subtree_rename
+################################################
+
+ldb_subtree_delete_OBJ_FILES = $(dsdbsrcdir)/samdb/ldb_modules/subtree_delete.o
+
+################################################
+# Start MODULE ldb_linked_attributes
+[MODULE::ldb_linked_attributes]
+INIT_FUNCTION = LDB_MODULE(linked_attributes)
+CFLAGS = -Ilib/ldb/include
+PRIVATE_DEPENDENCIES = LIBTALLOC LIBEVENTS SAMDB
+SUBSYSTEM = LIBLDB
+# End MODULE ldb_linked_attributes
+################################################
+
+ldb_linked_attributes_OBJ_FILES = $(dsdbsrcdir)/samdb/ldb_modules/linked_attributes.o
+
+################################################
+# Start MODULE ldb_ranged_results
+[MODULE::ldb_ranged_results]
+INIT_FUNCTION = LDB_MODULE(ranged_results)
+CFLAGS = -Ilib/ldb/include
+PRIVATE_DEPENDENCIES = LIBTALLOC LIBEVENTS
+SUBSYSTEM = LIBLDB
+# End MODULE ldb_ranged_results
+################################################
+
+ldb_ranged_results_OBJ_FILES = $(dsdbsrcdir)/samdb/ldb_modules/ranged_results.o
+
+################################################
+# Start MODULE ldb_anr
+[MODULE::ldb_anr]
+INIT_FUNCTION = LDB_MODULE(anr)
+CFLAGS = -Ilib/ldb/include
+PRIVATE_DEPENDENCIES = LIBTALLOC LIBEVENTS LIBSAMBA-UTIL SAMDB
+SUBSYSTEM = LIBLDB
+# End MODULE ldb_anr
+################################################
+
+ldb_anr_OBJ_FILES = $(dsdbsrcdir)/samdb/ldb_modules/anr.o
+
+################################################
+# Start MODULE ldb_instancetype
+[MODULE::ldb_instancetype]
+INIT_FUNCTION = LDB_MODULE(instancetype)
+CFLAGS = -Ilib/ldb/include
+PRIVATE_DEPENDENCIES = LIBTALLOC LIBEVENTS LIBSAMBA-UTIL SAMDB
+SUBSYSTEM = LIBLDB
+# End MODULE ldb_instancetype
+################################################
+
+ldb_instancetype_OBJ_FILES = $(dsdbsrcdir)/samdb/ldb_modules/instancetype.o
+
diff --git a/source4/dsdb/samdb/ldb_modules/dsdb_cache.c b/source4/dsdb/samdb/ldb_modules/dsdb_cache.c
new file mode 100644
index 0000000000..e60605dce1
--- /dev/null
+++ b/source4/dsdb/samdb/ldb_modules/dsdb_cache.c
@@ -0,0 +1,42 @@
+/*
+ Unix SMB/CIFS mplementation.
+
+ The Module that loads some DSDB related things
+ into memory. E.g. it loads the dsdb_schema struture
+
+ 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 <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "includes.h"
+#include "lib/ldb/include/ldb.h"
+#include "lib/ldb/include/ldb_errors.h"
+#include "lib/ldb/include/ldb_private.h"
+#include "dsdb/samdb/samdb.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "librpc/gen_ndr/ndr_drsuapi.h"
+#include "librpc/gen_ndr/ndr_drsblobs.h"
+
+static int dsdb_cache_init(struct ldb_module *module)
+{
+ /* TODO: load the schema */
+ return ldb_next_init(module);
+}
+
+_PUBLIC_ const struct ldb_module_ops ldb_dsdb_cache_module_ops = {
+ .name = "dsdb_cache",
+ .init_context = dsdb_cache_init
+};
diff --git a/source4/dsdb/samdb/ldb_modules/extended_dn_in.c b/source4/dsdb/samdb/ldb_modules/extended_dn_in.c
new file mode 100644
index 0000000000..c3db4fa588
--- /dev/null
+++ b/source4/dsdb/samdb/ldb_modules/extended_dn_in.c
@@ -0,0 +1,394 @@
+/*
+ ldb database library
+
+ Copyright (C) Simo Sorce 2005-2008
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2007-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 <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb extended dn control module
+ *
+ * Description: this module interprets DNs of the form <SID=S-1-2-4456> into normal DNs.
+ *
+ * Authors: Simo Sorce
+ * Andrew Bartlett
+ */
+
+#include "includes.h"
+#include "ldb/include/ldb.h"
+#include "ldb/include/ldb_errors.h"
+#include "ldb/include/ldb_private.h"
+
+/* search */
+struct extended_search_context {
+ struct ldb_module *module;
+ struct ldb_request *req;
+ struct ldb_dn *basedn;
+ char *wellknown_object;
+ int extended_type;
+};
+
+/* An extra layer of indirection because LDB does not allow the original request to be altered */
+
+static int extended_final_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+ int ret = LDB_ERR_OPERATIONS_ERROR;
+ struct extended_search_context *ac;
+ ac = talloc_get_type(req->context, struct extended_search_context);
+
+ if (ares->error != LDB_SUCCESS) {
+ ret = ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ } else {
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+
+ ret = ldb_module_send_entry(ac->req, ares->message, ares->controls);
+ break;
+ case LDB_REPLY_REFERRAL:
+
+ ret = ldb_module_send_referral(ac->req, ares->referral);
+ break;
+ case LDB_REPLY_DONE:
+
+ ret = ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ break;
+ }
+ }
+ return ret;
+}
+
+static int extended_base_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+ struct extended_search_context *ac;
+ struct ldb_request *down_req;
+ struct ldb_message_element *el;
+ int ret;
+ size_t i;
+ size_t wkn_len = 0;
+ char *valstr = NULL;
+ const char *found = NULL;
+
+ ac = talloc_get_type(req->context, struct extended_search_context);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+ if (!ac->wellknown_object) {
+ ac->basedn = talloc_steal(ac, ares->message->dn);
+ break;
+ }
+
+ wkn_len = strlen(ac->wellknown_object);
+
+ el = ldb_msg_find_element(ares->message, "wellKnownObjects");
+ if (!el) {
+ ac->basedn = NULL;
+ break;
+ }
+
+ for (i=0; i < el->num_values; i++) {
+ valstr = talloc_strndup(ac,
+ (const char *)el->values[i].data,
+ el->values[i].length);
+ if (!valstr) {
+ ldb_oom(ac->module->ldb);
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ if (strncasecmp(valstr, ac->wellknown_object, wkn_len) != 0) {
+ talloc_free(valstr);
+ continue;
+ }
+
+ found = &valstr[wkn_len];
+ break;
+ }
+
+ if (!found) {
+ break;
+ }
+
+ ac->basedn = ldb_dn_new(ac, ac->module->ldb, found);
+ talloc_free(valstr);
+ if (!ac->basedn) {
+ ldb_oom(ac->module->ldb);
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ break;
+
+ case LDB_REPLY_REFERRAL:
+ break;
+
+ case LDB_REPLY_DONE:
+
+ if (!ac->basedn) {
+ const char *str = talloc_asprintf(req, "Base-DN '%s' not found",
+ ldb_dn_get_linearized(ac->req->op.search.base));
+ ldb_set_errstring(ac->module->ldb, str);
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_NO_SUCH_OBJECT);
+ }
+
+ switch (ac->req->operation) {
+ case LDB_SEARCH:
+ ret = ldb_build_search_req_ex(&down_req,
+ ac->module->ldb, ac->req,
+ ac->basedn,
+ ac->req->op.search.scope,
+ ac->req->op.search.tree,
+ ac->req->op.search.attrs,
+ ac->req->controls,
+ ac, extended_final_callback,
+ ac->req);
+ break;
+ case LDB_ADD:
+ {
+ struct ldb_message *add_msg = ldb_msg_copy_shallow(ac, ac->req->op.add.message);
+ if (!add_msg) {
+ ldb_oom(ac->module->ldb);
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ add_msg->dn = ac->basedn;
+
+ ret = ldb_build_add_req(&down_req,
+ ac->module->ldb, ac->req,
+ add_msg,
+ ac->req->controls,
+ ac, extended_final_callback,
+ ac->req);
+ break;
+ }
+ case LDB_MODIFY:
+ {
+ struct ldb_message *mod_msg = ldb_msg_copy_shallow(ac, ac->req->op.mod.message);
+ if (!mod_msg) {
+ ldb_oom(ac->module->ldb);
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ mod_msg->dn = ac->basedn;
+
+ ret = ldb_build_mod_req(&down_req,
+ ac->module->ldb, ac->req,
+ mod_msg,
+ ac->req->controls,
+ ac, extended_final_callback,
+ ac->req);
+ break;
+ }
+ case LDB_DELETE:
+ ret = ldb_build_del_req(&down_req,
+ ac->module->ldb, ac->req,
+ ac->basedn,
+ ac->req->controls,
+ ac, extended_final_callback,
+ ac->req);
+ break;
+ case LDB_RENAME:
+ ret = ldb_build_rename_req(&down_req,
+ ac->module->ldb, ac->req,
+ ac->basedn,
+ ac->req->op.rename.newdn,
+ ac->req->controls,
+ ac, extended_final_callback,
+ ac->req);
+ break;
+ default:
+ return ldb_module_done(ac->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+
+ return ldb_next_request(ac->module, down_req);
+ }
+ talloc_free(ares);
+ return LDB_SUCCESS;
+}
+
+static int extended_dn_in_fix(struct ldb_module *module, struct ldb_request *req, struct ldb_dn *dn)
+{
+ struct extended_search_context *ac;
+ struct ldb_request *down_req;
+ int ret;
+ struct ldb_dn *base_dn = NULL;
+ enum ldb_scope base_dn_scope = LDB_SCOPE_BASE;
+ const char *base_dn_filter = NULL;
+ const char * const *base_dn_attrs = NULL;
+ char *wellknown_object = NULL;
+ static const char *no_attr[] = {
+ NULL
+ };
+ static const char *wkattr[] = {
+ "wellKnownObjects",
+ NULL
+ };
+
+ if (!ldb_dn_has_extended(dn)) {
+ /* Move along there isn't anything to see here */
+ return ldb_next_request(module, req);
+ } else {
+ /* It looks like we need to map the DN */
+ const struct ldb_val *sid_val, *guid_val, *wkguid_val;
+
+ sid_val = ldb_dn_get_extended_component(dn, "SID");
+ guid_val = ldb_dn_get_extended_component(dn, "GUID");
+ wkguid_val = ldb_dn_get_extended_component(dn, "WKGUID");
+
+ if (sid_val) {
+ /* TODO: do a search over all partitions */
+ base_dn = ldb_get_default_basedn(module->ldb);
+ base_dn_filter = talloc_asprintf(req, "(objectSid=%s)",
+ ldb_binary_encode(req, *sid_val));
+ if (!base_dn_filter) {
+ ldb_oom(module->ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ base_dn_scope = LDB_SCOPE_SUBTREE;
+ base_dn_attrs = no_attr;
+
+ } else if (guid_val) {
+
+ /* TODO: do a search over all partitions */
+ base_dn = ldb_get_default_basedn(module->ldb);
+ base_dn_filter = talloc_asprintf(req, "(objectGUID=%s)",
+ ldb_binary_encode(req, *guid_val));
+ if (!base_dn_filter) {
+ ldb_oom(module->ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ base_dn_scope = LDB_SCOPE_SUBTREE;
+ base_dn_attrs = no_attr;
+
+
+ } else if (wkguid_val) {
+ char *wkguid_dup;
+ char *tail_str;
+ char *p;
+
+ wkguid_dup = talloc_strndup(req, (char *)wkguid_val->data, wkguid_val->length);
+
+ p = strchr(wkguid_dup, ',');
+ if (!p) {
+ return LDB_ERR_INVALID_DN_SYNTAX;
+ }
+
+ p[0] = '\0';
+ p++;
+
+ wellknown_object = talloc_asprintf(req, "B:32:%s:", wkguid_dup);
+ if (!wellknown_object) {
+ ldb_oom(module->ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ tail_str = p;
+
+ base_dn = ldb_dn_new(req, module->ldb, tail_str);
+ talloc_free(wkguid_dup);
+ if (!base_dn) {
+ ldb_oom(module->ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ base_dn_filter = talloc_strdup(req, "(objectClass=*)");
+ if (!base_dn_filter) {
+ ldb_oom(module->ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ base_dn_scope = LDB_SCOPE_BASE;
+ base_dn_attrs = wkattr;
+ } else {
+ return LDB_ERR_INVALID_DN_SYNTAX;
+ }
+
+ ac = talloc_zero(req, struct extended_search_context);
+ if (ac == NULL) {
+ ldb_oom(module->ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->module = module;
+ ac->req = req;
+ ac->basedn = NULL; /* Filled in if the search finds the DN by SID/GUID etc */
+ ac->wellknown_object = wellknown_object;
+
+ /* If the base DN was an extended DN (perhaps a well known
+ * GUID) then search for that, so we can proceed with the original operation */
+
+ ret = ldb_build_search_req(&down_req,
+ module->ldb, ac,
+ base_dn,
+ base_dn_scope,
+ base_dn_filter,
+ base_dn_attrs,
+ NULL,
+ ac, extended_base_callback,
+ req);
+ if (ret != LDB_SUCCESS) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* perform the search */
+ return ldb_next_request(module, down_req);
+ }
+}
+
+static int extended_dn_in_search(struct ldb_module *module, struct ldb_request *req)
+{
+ return extended_dn_in_fix(module, req, req->op.search.base);
+}
+
+static int extended_dn_in_modify(struct ldb_module *module, struct ldb_request *req)
+{
+ return extended_dn_in_fix(module, req, req->op.mod.message->dn);
+}
+
+static int extended_dn_in_del(struct ldb_module *module, struct ldb_request *req)
+{
+ return extended_dn_in_fix(module, req, req->op.del.dn);
+}
+
+static int extended_dn_in_rename(struct ldb_module *module, struct ldb_request *req)
+{
+ return extended_dn_in_fix(module, req, req->op.rename.olddn);
+}
+
+_PUBLIC_ const struct ldb_module_ops ldb_extended_dn_in_module_ops = {
+ .name = "extended_dn_in",
+ .search = extended_dn_in_search,
+ .modify = extended_dn_in_modify,
+ .del = extended_dn_in_del,
+ .rename = extended_dn_in_rename,
+};
diff --git a/source4/dsdb/samdb/ldb_modules/extended_dn_out.c b/source4/dsdb/samdb/ldb_modules/extended_dn_out.c
new file mode 100644
index 0000000000..f8526faf3b
--- /dev/null
+++ b/source4/dsdb/samdb/ldb_modules/extended_dn_out.c
@@ -0,0 +1,655 @@
+/*
+ ldb database library
+
+ Copyright (C) Simo Sorce 2005-2008
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2007-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 <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb extended dn control module
+ *
+ * Description: this module builds a special dn for returned search
+ * results, and fixes some other aspects of the result (returned case issues)
+ * values.
+ *
+ * Authors: Simo Sorce
+ * Andrew Bartlett
+ */
+
+#include "includes.h"
+#include "ldb/include/ldb.h"
+#include "ldb/include/ldb_errors.h"
+#include "ldb/include/ldb_private.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "librpc/ndr/libndr.h"
+#include "dsdb/samdb/samdb.h"
+
+struct extended_dn_out_private {
+ bool dereference;
+ bool normalise;
+ struct dsdb_openldap_dereference_control *dereference_control;
+};
+
+static bool is_attr_in_list(const char * const * attrs, const char *attr)
+{
+ int i;
+
+ for (i = 0; attrs[i]; i++) {
+ if (ldb_attr_cmp(attrs[i], attr) == 0)
+ return true;
+ }
+
+ return false;
+}
+
+static char **copy_attrs(void *mem_ctx, const char * const * attrs)
+{
+ char **nattrs;
+ int i, num;
+
+ for (num = 0; attrs[num]; num++);
+
+ nattrs = talloc_array(mem_ctx, char *, num + 1);
+ if (!nattrs) return NULL;
+
+ for(i = 0; i < num; i++) {
+ nattrs[i] = talloc_strdup(nattrs, attrs[i]);
+ if (!nattrs[i]) {
+ talloc_free(nattrs);
+ return NULL;
+ }
+ }
+ nattrs[i] = NULL;
+
+ return nattrs;
+}
+
+static bool add_attrs(void *mem_ctx, char ***attrs, const char *attr)
+{
+ char **nattrs;
+ int num;
+
+ for (num = 0; (*attrs)[num]; num++);
+
+ nattrs = talloc_realloc(mem_ctx, *attrs, char *, num + 2);
+ if (!nattrs) return false;
+
+ *attrs = nattrs;
+
+ nattrs[num] = talloc_strdup(nattrs, attr);
+ if (!nattrs[num]) return false;
+
+ nattrs[num + 1] = NULL;
+
+ return true;
+}
+
+/* Fix the DN so that the relative attribute names are in upper case so that the DN:
+ cn=Adminstrator,cn=users,dc=samba,dc=example,dc=com becomes
+ CN=Adminstrator,CN=users,DC=samba,DC=example,DC=com
+*/
+
+
+static int fix_dn(struct ldb_dn *dn)
+{
+ int i, ret;
+ char *upper_rdn_attr;
+
+ for (i=0; i < ldb_dn_get_comp_num(dn); i++) {
+ /* We need the attribute name in upper case */
+ upper_rdn_attr = strupper_talloc(dn,
+ ldb_dn_get_component_name(dn, i));
+ if (!upper_rdn_attr) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* And replace it with CN=foo (we need the attribute in upper case */
+ ret = ldb_dn_set_component(dn, i, upper_rdn_attr,
+ *ldb_dn_get_component_val(dn, i));
+ talloc_free(upper_rdn_attr);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+ return LDB_SUCCESS;
+}
+
+/* Inject the extended DN components, so the DN cn=Adminstrator,cn=users,dc=samba,dc=example,dc=com becomes
+ <GUID=541203ae-f7d6-47ef-8390-bfcf019f9583>;<SID=S-1-5-21-4177067393-1453636373-93818737-500>;cn=Adminstrator,cn=users,dc=samba,dc=example,dc=com */
+
+static int inject_extended_dn_out(struct ldb_reply *ares,
+ struct ldb_context *ldb,
+ int type,
+ bool remove_guid,
+ bool remove_sid)
+{
+ int ret;
+ const DATA_BLOB *guid_blob;
+ const DATA_BLOB *sid_blob;
+
+ guid_blob = ldb_msg_find_ldb_val(ares->message, "objectGUID");
+ sid_blob = ldb_msg_find_ldb_val(ares->message, "objectSID");
+
+ if (!guid_blob) {
+ ldb_set_errstring(ldb, "Did not find objectGUID to inject into extended DN");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ldb_dn_set_extended_component(ares->message->dn, "GUID", guid_blob);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ if (sid_blob) {
+ ret = ldb_dn_set_extended_component(ares->message->dn, "SID", sid_blob);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+
+ if (remove_guid) {
+ ldb_msg_remove_attr(ares->message, "objectGUID");
+ }
+
+ if (sid_blob && remove_sid) {
+ ldb_msg_remove_attr(ares->message, "objectSID");
+ }
+
+ return LDB_SUCCESS;
+}
+
+static int handle_dereference(struct ldb_dn *dn,
+ struct dsdb_openldap_dereference_result **dereference_attrs,
+ const char *attr, const DATA_BLOB *val)
+{
+ const struct ldb_val *entryUUIDblob, *sid_blob;
+ struct ldb_message fake_msg; /* easier to use routines that expect an ldb_message */
+ int j;
+
+ fake_msg.num_elements = 0;
+
+ /* Look for this attribute in the returned control */
+ for (j = 0; dereference_attrs && dereference_attrs[j]; j++) {
+ struct ldb_val source_dn = data_blob_string_const(dereference_attrs[j]->dereferenced_dn);
+ if (ldb_attr_cmp(dereference_attrs[j]->source_attribute, attr) == 0
+ && data_blob_cmp(&source_dn, val) == 0) {
+ fake_msg.num_elements = dereference_attrs[j]->num_attributes;
+ fake_msg.elements = dereference_attrs[j]->attributes;
+ break;
+ }
+ }
+ if (!fake_msg.num_elements) {
+ return LDB_SUCCESS;
+ }
+ /* Look for an OpenLDAP entryUUID */
+
+ entryUUIDblob = ldb_msg_find_ldb_val(&fake_msg, "entryUUID");
+ if (entryUUIDblob) {
+ NTSTATUS status;
+ enum ndr_err_code ndr_err;
+
+ struct ldb_val guid_blob;
+ struct GUID guid;
+
+ status = GUID_from_data_blob(entryUUIDblob, &guid);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return LDB_ERR_INVALID_DN_SYNTAX;
+ }
+ ndr_err = ndr_push_struct_blob(&guid_blob, NULL, NULL, &guid,
+ (ndr_push_flags_fn_t)ndr_push_GUID);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return LDB_ERR_INVALID_DN_SYNTAX;
+ }
+
+ ldb_dn_set_extended_component(dn, "GUID", &guid_blob);
+ }
+
+ sid_blob = ldb_msg_find_ldb_val(&fake_msg, "objectSID");
+
+ /* Look for the objectSID */
+ if (sid_blob) {
+ ldb_dn_set_extended_component(dn, "SID", sid_blob);
+ }
+ return LDB_SUCCESS;
+}
+
+/* search */
+struct extended_search_context {
+ struct ldb_module *module;
+ const struct dsdb_schema *schema;
+ struct ldb_request *req;
+ bool inject;
+ bool remove_guid;
+ bool remove_sid;
+ int extended_type;
+};
+
+static int extended_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+ struct extended_search_context *ac;
+ struct ldb_control *control;
+ struct dsdb_openldap_dereference_result_control *dereference_control = NULL;
+ int ret, i, j;
+ struct ldb_message *msg = ares->message;
+ struct extended_dn_out_private *p;
+
+ ac = talloc_get_type(req->context, struct extended_search_context);
+ p = talloc_get_type(ac->module->private_data, struct extended_dn_out_private);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ switch (ares->type) {
+ case LDB_REPLY_REFERRAL:
+ return ldb_module_send_referral(ac->req, ares->referral);
+
+ case LDB_REPLY_DONE:
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, LDB_SUCCESS);
+ case LDB_REPLY_ENTRY:
+ break;
+ }
+
+ if (p && p->normalise) {
+ ret = fix_dn(ares->message->dn);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+ }
+
+ if (ac->inject) {
+ /* for each record returned post-process to add any derived
+ attributes that have been asked for */
+ ret = inject_extended_dn_out(ares, ac->module->ldb,
+ ac->extended_type, ac->remove_guid,
+ ac->remove_sid);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+ }
+
+ if ((p && p->normalise) || ac->inject) {
+ const struct ldb_val *val = ldb_msg_find_ldb_val(ares->message, "distinguishedName");
+ if (val) {
+ ldb_msg_remove_attr(ares->message, "distinguishedName");
+ if (ac->inject) {
+ ret = ldb_msg_add_steal_string(ares->message, "distinguishedName",
+ ldb_dn_get_extended_linearized(ares->message, ares->message->dn, ac->extended_type));
+ } else {
+ ret = ldb_msg_add_string(ares->message, "distinguishedName",
+ ldb_dn_get_linearized(ares->message->dn));
+ }
+ if (ret != LDB_SUCCESS) {
+ ldb_oom(ac->module->ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ }
+ }
+
+ if (p && p->dereference) {
+ control = ldb_reply_get_control(ares, DSDB_OPENLDAP_DEREFERENCE_CONTROL);
+
+ if (control && control->data) {
+ dereference_control = talloc_get_type(control->data, struct dsdb_openldap_dereference_result_control);
+ }
+ }
+
+ /* Walk the retruned elements (but only if we have a schema to interpret the list with) */
+ for (i = 0; ac->schema && i < msg->num_elements; i++) {
+ const struct dsdb_attribute *attribute;
+ attribute = dsdb_attribute_by_lDAPDisplayName(ac->schema, msg->elements[i].name);
+ if (!attribute) {
+ continue;
+ }
+
+ if (p->normalise) {
+ /* If we are also in 'normalise' mode, then
+ * fix the attribute names to be in the
+ * correct case */
+ msg->elements[i].name = talloc_strdup(msg->elements, attribute->lDAPDisplayName);
+ if (!msg->elements[i].name) {
+ ldb_oom(ac->module->ldb);
+ return ldb_module_done(ac->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
+ }
+ }
+
+ /* distinguishedName has been dealt with above */
+ if (ldb_attr_cmp(msg->elements[i].name, "distinguishedName") == 0) {
+ continue;
+ }
+
+ /* Look to see if this attributeSyntax is a DN */
+ if (strcmp(attribute->attributeSyntax_oid, "2.5.5.1") != 0) {
+ continue;
+ }
+
+ for (j = 0; j < msg->elements[i].num_values; j++) {
+ const char *dn_str;
+ struct ldb_dn *dn = ldb_dn_from_ldb_val(ac, ac->module->ldb, &msg->elements[i].values[j]);
+ if (!dn || !ldb_dn_validate(dn)) {
+ return ldb_module_done(ac->req, NULL, NULL, LDB_ERR_INVALID_DN_SYNTAX);
+ }
+
+ if (p->normalise) {
+ ret = fix_dn(dn);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+ }
+
+ /* If we are running in dereference mode (such
+ * as against OpenLDAP) then the DN in the msg
+ * above does not contain the extended values,
+ * and we need to look in the dereference
+ * result */
+
+ /* Look for this value in the attribute */
+
+ if (dereference_control) {
+ ret = handle_dereference(dn,
+ dereference_control->attributes,
+ msg->elements[i].name,
+ &msg->elements[i].values[j]);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+ }
+
+ if (!ac->inject) {
+ dn_str = talloc_steal(msg->elements[i].values,
+ ldb_dn_get_linearized(dn));
+ } else {
+ dn_str = talloc_steal(msg->elements[i].values,
+ ldb_dn_get_extended_linearized(msg->elements[i].values,
+ dn, ac->extended_type));
+ }
+ if (!dn_str) {
+ ldb_oom(ac->module->ldb);
+ return ldb_module_done(ac->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
+ }
+ msg->elements[i].values[j] = data_blob_string_const(dn_str);
+ talloc_free(dn);
+ }
+ }
+ return ldb_module_send_entry(ac->req, msg, ares->controls);
+}
+
+
+static int extended_dn_out_search(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_control *control;
+ struct ldb_control *storage_format_control;
+ struct ldb_extended_dn_control *extended_ctrl = NULL;
+ struct ldb_control **saved_controls;
+ struct extended_search_context *ac;
+ struct ldb_request *down_req;
+ char **new_attrs;
+ const char * const *const_attrs;
+ int ret;
+
+ struct extended_dn_out_private *p = talloc_get_type(module->private_data, struct extended_dn_out_private);
+
+ /* check if there's an extended dn control */
+ control = ldb_request_get_control(req, LDB_CONTROL_EXTENDED_DN_OID);
+ if (control && control->data) {
+ extended_ctrl = talloc_get_type(control->data, struct ldb_extended_dn_control);
+ if (!extended_ctrl) {
+ return LDB_ERR_PROTOCOL_ERROR;
+ }
+ }
+
+ /* Look to see if, as we are in 'store DN+GUID+SID' mode, the
+ * client is after the storage format (to fill in linked
+ * attributes) */
+ storage_format_control = ldb_request_get_control(req, DSDB_CONTROL_DN_STORAGE_FORMAT_OID);
+ if (!control && storage_format_control && storage_format_control->data) {
+ extended_ctrl = talloc_get_type(storage_format_control->data, struct ldb_extended_dn_control);
+ if (!extended_ctrl) {
+ ldb_set_errstring(module->ldb, "extended_dn_out: extended_ctrl was of the wrong data type");
+ return LDB_ERR_PROTOCOL_ERROR;
+ }
+ }
+
+ ac = talloc_zero(req, struct extended_search_context);
+ if (ac == NULL) {
+ ldb_oom(module->ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->module = module;
+ ac->schema = dsdb_get_schema(module->ldb);
+ ac->req = req;
+ ac->inject = false;
+ ac->remove_guid = false;
+ ac->remove_sid = false;
+
+ const_attrs = req->op.search.attrs;
+
+ /* We only need to do special processing if we were asked for
+ * the extended DN, or we are 'store DN+GUID+SID'
+ * (!dereference) mode. (This is the normal mode for LDB on
+ * tdb). */
+ if (control || (storage_format_control && p && !p->dereference)) {
+ ac->inject = true;
+ if (extended_ctrl) {
+ ac->extended_type = extended_ctrl->type;
+ } else {
+ ac->extended_type = 0;
+ }
+
+ /* check if attrs only is specified, in that case check wether we need to modify them */
+ if (req->op.search.attrs && !is_attr_in_list(req->op.search.attrs, "*")) {
+ if (! is_attr_in_list(req->op.search.attrs, "objectGUID")) {
+ ac->remove_guid = true;
+ }
+ if (! is_attr_in_list(req->op.search.attrs, "objectSID")) {
+ ac->remove_sid = true;
+ }
+ if (ac->remove_guid || ac->remove_sid) {
+ new_attrs = copy_attrs(ac, req->op.search.attrs);
+ if (new_attrs == NULL) {
+ ldb_oom(module->ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if (ac->remove_guid) {
+ if (!add_attrs(ac, &new_attrs, "objectGUID"))
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ if (ac->remove_sid) {
+ if (!add_attrs(ac, &new_attrs, "objectSID"))
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ const_attrs = (const char * const *)new_attrs;
+ }
+ }
+ }
+
+ ret = ldb_build_search_req_ex(&down_req,
+ module->ldb, ac,
+ req->op.search.base,
+ req->op.search.scope,
+ req->op.search.tree,
+ const_attrs,
+ req->controls,
+ ac, extended_callback,
+ req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ /* Remove extended DN and storage format controls */
+
+ if (control) {
+ /* save it locally and remove it from the list */
+ /* we do not need to replace them later as we
+ * are keeping the original req intact */
+ if (!save_controls(control, down_req, &saved_controls)) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ }
+
+ if (storage_format_control) {
+ /* save it locally and remove it from the list */
+ /* we do not need to replace them later as we
+ * are keeping the original req intact */
+ if (!save_controls(storage_format_control, down_req, &saved_controls)) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ }
+
+ /* Add in dereference control, if we were asked to, we are
+ * using the 'dereference' mode (such as with an OpenLDAP
+ * backend) and have the control prepared */
+ if (control && p && p->dereference && p->dereference_control) {
+ ret = ldb_request_add_control(down_req,
+ DSDB_OPENLDAP_DEREFERENCE_CONTROL,
+ false, p->dereference_control);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+
+ /* perform the search */
+ return ldb_next_request(module, down_req);
+}
+
+static int extended_dn_out_ldb_init(struct ldb_module *module)
+{
+ int ret;
+
+ struct extended_dn_out_private *p = talloc(module, struct extended_dn_out_private);
+
+ module->private_data = p;
+
+ if (!p) {
+ ldb_oom(module->ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ p->dereference = false;
+ p->normalise = false;
+
+ ret = ldb_mod_register_control(module, LDB_CONTROL_EXTENDED_DN_OID);
+ if (ret != LDB_SUCCESS) {
+ ldb_debug(module->ldb, LDB_DEBUG_ERROR,
+ "extended_dn_out: Unable to register control with rootdse!\n");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ return ldb_next_init(module);
+}
+
+static int extended_dn_out_dereference_init(struct ldb_module *module)
+{
+ int ret, i = 0;
+ struct extended_dn_out_private *p;
+ struct dsdb_openldap_dereference_control *dereference_control;
+ struct dsdb_attribute *cur;
+
+ struct dsdb_schema *schema;
+
+ module->private_data = p = talloc_zero(module, struct extended_dn_out_private);
+
+ if (!p) {
+ ldb_oom(module->ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ p->dereference = true;
+
+ /* At the moment, servers that need dereference also need the
+ * DN and attribute names to be normalised */
+ p->normalise = true;
+
+ ret = ldb_mod_register_control(module, LDB_CONTROL_EXTENDED_DN_OID);
+ if (ret != LDB_SUCCESS) {
+ ldb_debug(module->ldb, LDB_DEBUG_ERROR,
+ "extended_dn_out: Unable to register control with rootdse!\n");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ldb_next_init(module);
+
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ schema = dsdb_get_schema(module->ldb);
+ if (!schema) {
+ /* No schema on this DB (yet) */
+ return LDB_SUCCESS;
+ }
+
+ p->dereference_control = dereference_control
+ = talloc_zero(p, struct dsdb_openldap_dereference_control);
+
+ if (!p->dereference_control) {
+ ldb_oom(module->ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ for (cur = schema->attributes; cur; cur = cur->next) {
+ static const char *attrs[] = {
+ "entryUUID",
+ "objectSID",
+ NULL
+ };
+
+ if (strcmp(cur->syntax->attributeSyntax_oid, "2.5.5.1") != 0) {
+ continue;
+ }
+ dereference_control->dereference
+ = talloc_realloc(p, dereference_control->dereference,
+ struct dsdb_openldap_dereference *, i + 2);
+ if (!dereference_control) {
+ ldb_oom(module->ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ dereference_control->dereference[i] = talloc(dereference_control->dereference,
+ struct dsdb_openldap_dereference);
+ if (!dereference_control->dereference[i]) {
+ ldb_oom(module->ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ dereference_control->dereference[i]->source_attribute = cur->lDAPDisplayName;
+ dereference_control->dereference[i]->dereference_attribute = attrs;
+ i++;
+ dereference_control->dereference[i] = NULL;
+ }
+ return LDB_SUCCESS;
+}
+
+_PUBLIC_ const struct ldb_module_ops ldb_extended_dn_out_ldb_module_ops = {
+ .name = "extended_dn_out_ldb",
+ .search = extended_dn_out_search,
+ .init_context = extended_dn_out_ldb_init,
+};
+
+
+_PUBLIC_ const struct ldb_module_ops ldb_extended_dn_out_dereference_module_ops = {
+ .name = "extended_dn_out_dereference",
+ .search = extended_dn_out_search,
+ .init_context = extended_dn_out_dereference_init,
+};
diff --git a/source4/dsdb/samdb/ldb_modules/extended_dn_store.c b/source4/dsdb/samdb/ldb_modules/extended_dn_store.c
new file mode 100644
index 0000000000..4f4e9d0fd7
--- /dev/null
+++ b/source4/dsdb/samdb/ldb_modules/extended_dn_store.c
@@ -0,0 +1,431 @@
+/*
+ ldb database library
+
+ Copyright (C) Simo Sorce 2005-2008
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2007-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 <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb extended dn control module
+ *
+ * Description: this module builds a special dn for returned search
+ * results nad creates the special DN in the backend store for new
+ * values.
+ *
+ * This also has the curious result that we convert <SID=S-1-2-345>
+ * in an attribute value into a normal DN for the rest of the stack
+ * to process
+ *
+ * Authors: Simo Sorce
+ * Andrew Bartlett
+ */
+
+#include "includes.h"
+#include "ldb/include/ldb.h"
+#include "ldb/include/ldb_errors.h"
+#include "ldb/include/ldb_private.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "dsdb/samdb/samdb.h"
+#include "libcli/security/security.h"
+
+#include <time.h>
+
+struct extended_dn_replace_list {
+ struct extended_dn_replace_list *next;
+ struct ldb_dn *dn;
+ TALLOC_CTX *mem_ctx;
+ struct ldb_val *replace_dn;
+ struct extended_dn_context *ac;
+ struct ldb_request *search_req;
+};
+
+
+struct extended_dn_context {
+ const struct dsdb_schema *schema;
+ struct ldb_module *module;
+ struct ldb_request *req;
+ struct ldb_request *new_req;
+
+ struct extended_dn_replace_list *ops;
+ struct extended_dn_replace_list *cur;
+};
+
+
+static struct extended_dn_context *extended_dn_context_init(struct ldb_module *module,
+ struct ldb_request *req)
+{
+ struct extended_dn_context *ac;
+
+ ac = talloc_zero(req, struct extended_dn_context);
+ if (ac == NULL) {
+ ldb_oom(module->ldb);
+ return NULL;
+ }
+
+ ac->schema = dsdb_get_schema(module->ldb);
+ ac->module = module;
+ ac->req = req;
+
+ return ac;
+}
+
+/* An extra layer of indirection because LDB does not allow the original request to be altered */
+
+static int extended_final_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+ int ret = LDB_ERR_OPERATIONS_ERROR;
+ struct extended_dn_context *ac;
+ ac = talloc_get_type(req->context, struct extended_dn_context);
+
+ if (ares->error != LDB_SUCCESS) {
+ ret = ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ } else {
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+
+ ret = ldb_module_send_entry(ac->req, ares->message, ares->controls);
+ break;
+ case LDB_REPLY_REFERRAL:
+
+ ret = ldb_module_send_referral(ac->req, ares->referral);
+ break;
+ case LDB_REPLY_DONE:
+
+ ret = ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ break;
+ }
+ }
+ return ret;
+}
+
+static int extended_replace_dn(struct ldb_request *req, struct ldb_reply *ares)
+{
+ struct extended_dn_replace_list *os = talloc_get_type(req->context,
+ struct extended_dn_replace_list);
+
+ if (!ares) {
+ return ldb_module_done(os->ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error == LDB_ERR_NO_SUCH_OBJECT) {
+ /* Don't worry too much about dangling references */
+
+ ldb_reset_err_string(os->ac->module->ldb);
+ if (os->next) {
+ struct extended_dn_replace_list *next;
+
+ next = os->next;
+
+ talloc_free(os);
+
+ os = next;
+ return ldb_next_request(os->ac->module, next->search_req);
+ } else {
+ /* Otherwise, we are done - let's run the
+ * request now we have swapped the DNs for the
+ * full versions */
+ return ldb_next_request(os->ac->module, os->ac->req);
+ }
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(os->ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ /* Only entries are interesting, and we only want the olddn */
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+ {
+ /* This *must* be the right DN, as this is a base
+ * search. We can't check, as it could be an extended
+ * DN, so a module below will resolve it */
+ struct ldb_dn *dn = ares->message->dn;
+
+ /* Replace the DN with the extended version of the DN
+ * (ie, add SID and GUID) */
+ *os->replace_dn = data_blob_string_const(
+ ldb_dn_get_extended_linearized(os->mem_ctx,
+ dn, 1));
+ if (os->replace_dn->data == NULL) {
+ return ldb_module_done(os->ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ break;
+ }
+ case LDB_REPLY_REFERRAL:
+ /* ignore */
+ break;
+
+ case LDB_REPLY_DONE:
+
+ talloc_free(ares);
+
+ /* Run the next search */
+
+ if (os->next) {
+ struct extended_dn_replace_list *next;
+
+ next = os->next;
+
+ talloc_free(os);
+
+ os = next;
+ return ldb_next_request(os->ac->module, next->search_req);
+ } else {
+ /* Otherwise, we are done - let's run the
+ * request now we have swapped the DNs for the
+ * full versions */
+ return ldb_next_request(os->ac->module, os->ac->new_req);
+ }
+ }
+
+ talloc_free(ares);
+ return LDB_SUCCESS;
+}
+
+/* We have a 'normal' DN in the inbound request. We need to find out
+ * what the GUID and SID are on the DN it points to, so we can
+ * construct an extended DN for storage.
+ *
+ * This creates a list of DNs to look up, and the plain DN to replace
+ */
+
+static int extended_store_replace(struct extended_dn_context *ac,
+ TALLOC_CTX *callback_mem_ctx,
+ struct ldb_val *plain_dn)
+{
+ int ret;
+ struct extended_dn_replace_list *os;
+ static const char *attrs[] = {
+ "objectSid",
+ "objectGUID",
+ NULL
+ };
+
+ os = talloc_zero(ac, struct extended_dn_replace_list);
+ if (!os) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ os->ac = ac;
+
+ os->mem_ctx = callback_mem_ctx;
+
+ os->dn = ldb_dn_from_ldb_val(os, ac->module->ldb, plain_dn);
+ if (!os->dn || !ldb_dn_validate(os->dn)) {
+ talloc_free(os);
+ ldb_asprintf_errstring(ac->module->ldb,
+ "could not parse %.*s as a DN", (int)plain_dn->length, plain_dn->data);
+ return LDB_ERR_INVALID_DN_SYNTAX;
+ }
+
+ os->replace_dn = plain_dn;
+
+ /* The search request here might happen to be for an
+ * 'extended' style DN, such as <GUID=abced...>. The next
+ * module in the stack will convert this into a normal DN for
+ * processing */
+ ret = ldb_build_search_req(&os->search_req,
+ ac->module->ldb, os, os->dn, LDB_SCOPE_BASE, NULL,
+ attrs, NULL, os, extended_replace_dn,
+ ac->req);
+
+ if (ret != LDB_SUCCESS) {
+ talloc_free(os);
+ return ret;
+ }
+
+ ret = ldb_request_add_control(os->search_req,
+ DSDB_CONTROL_DN_STORAGE_FORMAT_OID,
+ true, NULL);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(os);
+ return ret;
+ }
+
+ if (ac->ops) {
+ ac->cur->next = os;
+ } else {
+ ac->ops = os;
+ }
+ ac->cur = os;
+
+ return LDB_SUCCESS;
+}
+
+
+/* add */
+static int extended_dn_add(struct ldb_module *module, struct ldb_request *req)
+{
+ struct extended_dn_context *ac;
+ int ret;
+ int i, j;
+
+ if (ldb_dn_is_special(req->op.add.message->dn)) {
+ /* do not manipulate our control entries */
+ return ldb_next_request(module, req);
+ }
+
+ ac = extended_dn_context_init(module, req);
+ if (!ac) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if (!ac->schema) {
+ /* without schema, this doesn't make any sense */
+ talloc_free(ac);
+ return ldb_next_request(module, req);
+ }
+
+ for (i=0; i < req->op.add.message->num_elements; i++) {
+ const struct ldb_message_element *el = &req->op.add.message->elements[i];
+ const struct dsdb_attribute *schema_attr
+ = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
+ if (!schema_attr) {
+ continue;
+ }
+
+ /* We only setup an extended DN GUID on these particular DN objects */
+ if (strcmp(schema_attr->attributeSyntax_oid, "2.5.5.1") != 0) {
+ continue;
+ }
+
+ /* Before we setup a procedure to modify the incoming message, we must copy it */
+ if (!ac->new_req) {
+ struct ldb_message *msg = ldb_msg_copy(ac, req->op.add.message);
+ if (!msg) {
+ ldb_oom(module->ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ldb_build_add_req(&ac->new_req, module->ldb, ac, msg, req->controls, ac, extended_final_callback, req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+ /* Re-calculate el */
+ el = &ac->new_req->op.add.message->elements[i];
+ for (j = 0; j < el->num_values; j++) {
+ ret = extended_store_replace(ac, ac->new_req->op.add.message->elements, &el->values[j]);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+ }
+
+ /* if DNs were set continue */
+ if (ac->ops == NULL) {
+ talloc_free(ac);
+ return ldb_next_request(module, req);
+ }
+
+ /* start with the searches */
+ return ldb_next_request(module, ac->ops->search_req);
+}
+
+/* modify */
+static int extended_dn_modify(struct ldb_module *module, struct ldb_request *req)
+{
+ /* Look over list of modifications */
+ /* Find if any are for linked attributes */
+ /* Determine the effect of the modification */
+ /* Apply the modify to the linked entry */
+
+ int i, j;
+ struct extended_dn_context *ac;
+ int ret;
+
+ if (ldb_dn_is_special(req->op.mod.message->dn)) {
+ /* do not manipulate our control entries */
+ return ldb_next_request(module, req);
+ }
+
+ ac = extended_dn_context_init(module, req);
+ if (!ac) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if (!ac->schema) {
+ /* without schema, this doesn't make any sense */
+ return ldb_next_request(module, req);
+ }
+
+ for (i=0; i < req->op.mod.message->num_elements; i++) {
+ const struct ldb_message_element *el = &req->op.mod.message->elements[i];
+ const struct dsdb_attribute *schema_attr
+ = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
+ if (!schema_attr) {
+ continue;
+ }
+
+ /* We only setup an extended DN GUID on these particular DN objects */
+ if (strcmp(schema_attr->attributeSyntax_oid, "2.5.5.1") != 0) {
+ continue;
+ }
+
+ /* Before we setup a procedure to modify the incoming message, we must copy it */
+ if (!ac->new_req) {
+ struct ldb_message *msg = ldb_msg_copy(ac, req->op.mod.message);
+ if (!msg) {
+ ldb_oom(module->ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ldb_build_mod_req(&ac->new_req, module->ldb, ac, msg, req->controls, ac, extended_final_callback, req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+ /* Re-calculate el */
+ el = &ac->new_req->op.mod.message->elements[i];
+ /* For each value being added, we need to setup the lookups to fill in the extended DN */
+ for (j = 0; j < el->num_values; j++) {
+ struct ldb_dn *dn = ldb_dn_from_ldb_val(ac, module->ldb, &el->values[j]);
+ if (!dn || !ldb_dn_validate(dn)) {
+ ldb_asprintf_errstring(module->ldb,
+ "could not parse attribute %s as a DN", el->name);
+ return LDB_ERR_INVALID_DN_SYNTAX;
+ }
+ if (((el->flags & LDB_FLAG_MOD_MASK) == LDB_FLAG_MOD_DELETE) && !ldb_dn_has_extended(dn)) {
+ /* NO need to figure this DN out, it's going to be deleted anyway */
+ continue;
+ }
+ ret = extended_store_replace(ac, req->op.mod.message->elements, &el->values[j]);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+ }
+
+ /* if DNs were set continue */
+ if (ac->ops == NULL) {
+ talloc_free(ac);
+ return ldb_next_request(module, req);
+ }
+
+ /* start with the searches */
+ return ldb_next_request(module, ac->ops->search_req);
+}
+
+_PUBLIC_ const struct ldb_module_ops ldb_extended_dn_store_module_ops = {
+ .name = "extended_dn_store",
+ .add = extended_dn_add,
+ .modify = extended_dn_modify,
+};
diff --git a/source4/dsdb/samdb/ldb_modules/instancetype.c b/source4/dsdb/samdb/ldb_modules/instancetype.c
new file mode 100644
index 0000000000..8d648d6d82
--- /dev/null
+++ b/source4/dsdb/samdb/ldb_modules/instancetype.c
@@ -0,0 +1,154 @@
+/*
+ ldb database library
+
+ Copyright (C) Simo Sorce 2004-2008
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
+ Copyright (C) Andrew Tridgell 2005
+ Copyright (C) Stefan Metzmacher <metze@samba.org> 2007
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb instancetype module
+ *
+ * Description: add an instanceType onto every new record
+ *
+ * Author: Andrew Bartlett
+ */
+
+#include "includes.h"
+#include "ldb_module.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "dsdb/samdb/samdb.h"
+#include "dsdb/common/flags.h"
+
+struct it_context {
+ struct ldb_module *module;
+ struct ldb_request *req;
+};
+
+static int it_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+ struct ldb_context *ldb;
+ struct it_context *ac;
+
+ ac = talloc_get_type(req->context, struct it_context);
+ ldb = ldb_module_get_ctx(ac->module);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ if (ares->type != LDB_REPLY_DONE) {
+ ldb_set_errstring(ldb, "Invalid reply type!");
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+}
+
+/* add_record: add instancetype attribute */
+static int instancetype_add(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct ldb_request *down_req;
+ struct ldb_message *msg;
+ struct it_context *ac;
+ uint32_t instance_type;
+ int ret;
+ const struct ldb_control *partition_ctrl;
+ const struct dsdb_control_current_partition *partition;
+
+ ldb = ldb_module_get_ctx(module);
+
+ ldb_debug(ldb, LDB_DEBUG_TRACE, "instancetype_add_record\n");
+
+ /* do not manipulate our control entries */
+ if (ldb_dn_is_special(req->op.add.message->dn)) {
+ return ldb_next_request(module, req);
+ }
+
+ partition_ctrl = ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID);
+ if (!partition_ctrl) {
+ ldb_debug_set(ldb, LDB_DEBUG_FATAL,
+ "instancetype_add: no current partition control found");
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+
+ partition = talloc_get_type(partition_ctrl->data,
+ struct dsdb_control_current_partition);
+ SMB_ASSERT(partition && partition->version == DSDB_CONTROL_CURRENT_PARTITION_VERSION);
+
+ ac = talloc(req, struct it_context);
+ if (ac == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ac->module = module;
+ ac->req = req;
+
+ /* we have to copy the message as the caller might have it as a const */
+ msg = ldb_msg_copy_shallow(ac, req->op.add.message);
+ if (msg == NULL) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /*
+ * TODO: calculate correct instance type
+ */
+ instance_type = INSTANCE_TYPE_WRITE;
+ if (ldb_dn_compare(partition->dn, msg->dn) == 0) {
+ instance_type |= INSTANCE_TYPE_IS_NC_HEAD;
+ if (ldb_dn_compare(msg->dn, samdb_base_dn(ldb)) != 0) {
+ instance_type |= INSTANCE_TYPE_NC_ABOVE;
+ }
+ }
+
+ ret = ldb_msg_add_fmt(msg, "instanceType", "%u", instance_type);
+ if (ret != LDB_SUCCESS) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ldb_build_add_req(&down_req, ldb, ac,
+ msg,
+ req->controls,
+ ac, it_callback,
+ req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ /* go on with the call chain */
+ return ldb_next_request(module, down_req);
+}
+
+_PUBLIC_ const struct ldb_module_ops ldb_instancetype_module_ops = {
+ .name = "instancetype",
+ .add = instancetype_add,
+};
diff --git a/source4/dsdb/samdb/ldb_modules/kludge_acl.c b/source4/dsdb/samdb/ldb_modules/kludge_acl.c
new file mode 100644
index 0000000000..0b5994bb88
--- /dev/null
+++ b/source4/dsdb/samdb/ldb_modules/kludge_acl.c
@@ -0,0 +1,535 @@
+/*
+ ldb database library
+
+ Copyright (C) Andrew Bartlett 2005
+ Copyright (C) Simo Sorce 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 <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb kludge ACL module
+ *
+ * Description: Simple module to enforce a simple form of access
+ * control, sufficient for securing a default Samba4
+ * installation.
+ *
+ * Author: Andrew Bartlett
+ */
+
+#include "includes.h"
+#include "ldb_module.h"
+#include "auth/auth.h"
+#include "libcli/security/security.h"
+#include "dsdb/samdb/samdb.h"
+
+/* Kludge ACL rules:
+ *
+ * - System can read passwords
+ * - Administrators can write anything
+ * - Users can read anything that is not a password
+ *
+ */
+
+struct kludge_private_data {
+ const char **password_attrs;
+};
+
+static enum security_user_level what_is_user(struct ldb_module *module)
+{
+ struct ldb_context *ldb = ldb_module_get_ctx(module);
+ struct auth_session_info *session_info
+ = (struct auth_session_info *)ldb_get_opaque(ldb, "sessionInfo");
+ return security_session_user_level(session_info);
+}
+
+static const char *user_name(TALLOC_CTX *mem_ctx, struct ldb_module *module)
+{
+ struct ldb_context *ldb = ldb_module_get_ctx(module);
+ struct auth_session_info *session_info
+ = (struct auth_session_info *)ldb_get_opaque(ldb, "sessionInfo");
+ if (!session_info) {
+ return "UNKNOWN (NULL)";
+ }
+
+ return talloc_asprintf(mem_ctx, "%s\\%s",
+ session_info->server_info->domain_name,
+ session_info->server_info->account_name);
+}
+
+/* search */
+struct kludge_acl_context {
+
+ struct ldb_module *module;
+ struct ldb_request *req;
+
+ enum security_user_level user_type;
+ bool allowedAttributes;
+ bool allowedAttributesEffective;
+ bool allowedChildClasses;
+ bool allowedChildClassesEffective;
+ const char * const *attrs;
+};
+
+/* read all objectClasses */
+
+static int kludge_acl_allowedAttributes(struct ldb_context *ldb, struct ldb_message *msg,
+ const char *attrName)
+{
+ struct ldb_message_element *oc_el;
+ struct ldb_message_element *allowedAttributes;
+ const struct dsdb_schema *schema = dsdb_get_schema(ldb);
+ TALLOC_CTX *mem_ctx;
+ const char **objectclass_list, **attr_list;
+ int i, ret;
+
+ /* If we don't have a schema yet, we can't do anything... */
+ if (schema == NULL) {
+ return LDB_SUCCESS;
+ }
+
+ /* Must remove any existing attribute, or else confusion reins */
+ ldb_msg_remove_attr(msg, attrName);
+ ret = ldb_msg_add_empty(msg, attrName, 0, &allowedAttributes);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ mem_ctx = talloc_new(msg);
+ if (!mem_ctx) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* To ensure that oc_el is valid, we must look for it after
+ we alter the element array in ldb_msg_add_empty() */
+ oc_el = ldb_msg_find_element(msg, "objectClass");
+
+ objectclass_list = talloc_array(mem_ctx, const char *, oc_el->num_values + 1);
+ if (!objectclass_list) {
+ ldb_oom(ldb);
+ talloc_free(mem_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ for (i=0; oc_el && i < oc_el->num_values; i++) {
+ objectclass_list[i] = (const char *)oc_el->values[i].data;
+ }
+ objectclass_list[i] = NULL;
+
+ attr_list = dsdb_full_attribute_list(mem_ctx, schema, objectclass_list, DSDB_SCHEMA_ALL);
+ if (!attr_list) {
+ ldb_asprintf_errstring(ldb, "kludge_acl: Failed to get list of attributes create %s attribute", attrName);
+ talloc_free(mem_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ for (i=0; attr_list && attr_list[i]; i++) {
+ ldb_msg_add_string(msg, attrName, attr_list[i]);
+ }
+ talloc_free(mem_ctx);
+ return LDB_SUCCESS;
+
+}
+/* read all objectClasses */
+
+static int kludge_acl_childClasses(struct ldb_context *ldb, struct ldb_message *msg,
+ const char *attrName)
+{
+ struct ldb_message_element *oc_el;
+ struct ldb_message_element *allowedClasses;
+ const struct dsdb_schema *schema = dsdb_get_schema(ldb);
+ const struct dsdb_class *sclass;
+ int i, j, ret;
+
+ /* If we don't have a schema yet, we can't do anything... */
+ if (schema == NULL) {
+ return LDB_SUCCESS;
+ }
+
+ /* Must remove any existing attribute, or else confusion reins */
+ ldb_msg_remove_attr(msg, attrName);
+ ret = ldb_msg_add_empty(msg, attrName, 0, &allowedClasses);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ /* To ensure that oc_el is valid, we must look for it after
+ we alter the element array in ldb_msg_add_empty() */
+ oc_el = ldb_msg_find_element(msg, "objectClass");
+
+ for (i=0; oc_el && i < oc_el->num_values; i++) {
+ sclass = dsdb_class_by_lDAPDisplayName(schema, (const char *)oc_el->values[i].data);
+ if (!sclass) {
+ /* We don't know this class? what is going on? */
+ continue;
+ }
+
+ for (j=0; sclass->possibleInferiors && sclass->possibleInferiors[j]; j++) {
+ ldb_msg_add_string(msg, attrName, sclass->possibleInferiors[j]);
+ }
+ }
+
+ if (allowedClasses->num_values > 1) {
+ qsort(allowedClasses->values,
+ allowedClasses->num_values,
+ sizeof(*allowedClasses->values),
+ (comparison_fn_t)data_blob_cmp);
+
+ for (i=1 ; i < allowedClasses->num_values; i++) {
+
+ struct ldb_val *val1 = &allowedClasses->values[i-1];
+ struct ldb_val *val2 = &allowedClasses->values[i];
+ if (data_blob_cmp(val1, val2) == 0) {
+ memmove(val1, val2, (allowedClasses->num_values - i) * sizeof( struct ldb_val));
+ allowedClasses->num_values--;
+ i--;
+ }
+ }
+ }
+
+ return LDB_SUCCESS;
+
+}
+
+/* find all attributes allowed by all these objectClasses */
+
+static int kludge_acl_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+ struct ldb_context *ldb;
+ struct kludge_acl_context *ac;
+ struct kludge_private_data *data;
+ int i, ret;
+
+ ac = talloc_get_type(req->context, struct kludge_acl_context);
+ data = talloc_get_type(ldb_module_get_private(ac->module), struct kludge_private_data);
+ ldb = ldb_module_get_ctx(ac->module);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+ if (ac->allowedAttributes) {
+ ret = kludge_acl_allowedAttributes(ldb,
+ ares->message,
+ "allowedAttributes");
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+ }
+ if (ac->allowedChildClasses) {
+ ret = kludge_acl_childClasses(ldb,
+ ares->message,
+ "allowedChildClasses");
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+ }
+
+ if (data && data->password_attrs) /* if we are not initialized just get through */
+ {
+ switch (ac->user_type) {
+ case SECURITY_SYSTEM:
+ if (ac->allowedAttributesEffective) {
+ ret = kludge_acl_allowedAttributes(ldb, ares->message,
+ "allowedAttributesEffective");
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+ }
+ if (ac->allowedChildClassesEffective) {
+ ret = kludge_acl_childClasses(ldb, ares->message,
+ "allowedChildClassesEffective");
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+ }
+ break;
+
+ case SECURITY_ADMINISTRATOR:
+ if (ac->allowedAttributesEffective) {
+ ret = kludge_acl_allowedAttributes(ldb, ares->message,
+ "allowedAttributesEffective");
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+ }
+ if (ac->allowedChildClassesEffective) {
+ ret = kludge_acl_childClasses(ldb, ares->message,
+ "allowedChildClassesEffective");
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+ }
+ /* fall through */
+ default:
+ /* remove password attributes */
+ for (i = 0; data->password_attrs[i]; i++) {
+ ldb_msg_remove_attr(ares->message, data->password_attrs[i]);
+ }
+ }
+ }
+
+ if (ac->allowedAttributes ||
+ ac->allowedAttributesEffective ||
+ ac->allowedChildClasses ||
+ ac->allowedChildClassesEffective) {
+
+ if (!ldb_attr_in_list(ac->attrs, "objectClass") &&
+ !ldb_attr_in_list(ac->attrs, "*")) {
+
+ ldb_msg_remove_attr(ares->message,
+ "objectClass");
+ }
+ }
+
+ return ldb_module_send_entry(ac->req, ares->message, ares->controls);
+
+ case LDB_REPLY_REFERRAL:
+ return ldb_module_send_referral(ac->req, ares->referral);
+
+ case LDB_REPLY_DONE:
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, LDB_SUCCESS);
+
+ }
+ return LDB_SUCCESS;
+}
+
+static int kludge_acl_search(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct kludge_acl_context *ac;
+ struct ldb_request *down_req;
+ struct kludge_private_data *data;
+ const char * const *attrs;
+ int ret, i;
+ struct ldb_control *sd_control;
+ struct ldb_control **sd_saved_controls;
+
+ ldb = ldb_module_get_ctx(module);
+
+ ac = talloc(req, struct kludge_acl_context);
+ if (ac == NULL) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ data = talloc_get_type(ldb_module_get_private(module), struct kludge_private_data);
+
+ ac->module = module;
+ ac->req = req;
+ ac->user_type = what_is_user(module);
+ ac->attrs = req->op.search.attrs;
+
+ ac->allowedAttributes = ldb_attr_in_list(req->op.search.attrs, "allowedAttributes");
+
+ ac->allowedAttributesEffective = ldb_attr_in_list(req->op.search.attrs, "allowedAttributesEffective");
+
+ ac->allowedChildClasses = ldb_attr_in_list(req->op.search.attrs, "allowedChildClasses");
+
+ ac->allowedChildClassesEffective = ldb_attr_in_list(req->op.search.attrs, "allowedChildClassesEffective");
+
+ if (ac->allowedAttributes || ac->allowedAttributesEffective || ac->allowedChildClasses || ac->allowedChildClassesEffective) {
+ attrs = ldb_attr_list_copy_add(ac, req->op.search.attrs, "objectClass");
+ } else {
+ attrs = req->op.search.attrs;
+ }
+
+ /* replace any attributes in the parse tree that are private,
+ so we don't allow a search for 'userPassword=penguin',
+ just as we would not allow that attribute to be returned */
+ switch (ac->user_type) {
+ case SECURITY_SYSTEM:
+ break;
+ default:
+ /* FIXME: We should copy the tree and keep the original unmodified. */
+ /* remove password attributes */
+
+ if (!data || !data->password_attrs) {
+ break;
+ }
+ for (i = 0; data->password_attrs[i]; i++) {
+ ldb_parse_tree_attr_replace(req->op.search.tree,
+ data->password_attrs[i],
+ "kludgeACLredactedattribute");
+ }
+ }
+
+ ret = ldb_build_search_req_ex(&down_req,
+ ldb, ac,
+ req->op.search.base,
+ req->op.search.scope,
+ req->op.search.tree,
+ attrs,
+ req->controls,
+ ac, kludge_acl_callback,
+ req);
+ if (ret != LDB_SUCCESS) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* check if there's an SD_FLAGS control */
+ sd_control = ldb_request_get_control(down_req, LDB_CONTROL_SD_FLAGS_OID);
+ if (sd_control) {
+ /* save it locally and remove it from the list */
+ /* we do not need to replace them later as we
+ * are keeping the original req intact */
+ if (!save_controls(sd_control, down_req, &sd_saved_controls)) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ }
+
+ /* perform the search */
+ return ldb_next_request(module, down_req);
+}
+
+/* ANY change type */
+static int kludge_acl_change(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb = ldb_module_get_ctx(module);
+ enum security_user_level user_type = what_is_user(module);
+ switch (user_type) {
+ case SECURITY_SYSTEM:
+ case SECURITY_ADMINISTRATOR:
+ return ldb_next_request(module, req);
+ default:
+ ldb_asprintf_errstring(ldb,
+ "kludge_acl_change: "
+ "attempted database modify not permitted. "
+ "User %s is not SYSTEM or an administrator",
+ user_name(req, module));
+ return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
+ }
+}
+
+static int kludge_acl_extended(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb = ldb_module_get_ctx(module);
+ enum security_user_level user_type;
+
+ /* allow everybody to read the sequence number */
+ if (strcmp(req->op.extended.oid,
+ LDB_EXTENDED_SEQUENCE_NUMBER) == 0) {
+ return ldb_next_request(module, req);
+ }
+
+ user_type = what_is_user(module);
+
+ switch (user_type) {
+ case SECURITY_SYSTEM:
+ case SECURITY_ADMINISTRATOR:
+ return ldb_next_request(module, req);
+ default:
+ ldb_asprintf_errstring(ldb,
+ "kludge_acl_change: "
+ "attempted database modify not permitted. "
+ "User %s is not SYSTEM or an administrator",
+ user_name(req, module));
+ return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
+ }
+}
+
+static int kludge_acl_init(struct ldb_module *module)
+{
+ struct ldb_context *ldb;
+ int ret, i;
+ TALLOC_CTX *mem_ctx = talloc_new(module);
+ static const char *attrs[] = { "passwordAttribute", NULL };
+ struct ldb_result *res;
+ struct ldb_message *msg;
+ struct ldb_message_element *password_attributes;
+
+ struct kludge_private_data *data;
+
+ ldb = ldb_module_get_ctx(module);
+
+ data = talloc(module, struct kludge_private_data);
+ if (data == NULL) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ data->password_attrs = NULL;
+ ldb_module_set_private(module, data);
+
+ if (!mem_ctx) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ldb_search(ldb, mem_ctx, &res,
+ ldb_dn_new(mem_ctx, ldb, "@KLUDGEACL"),
+ LDB_SCOPE_BASE, attrs, NULL);
+ if (ret != LDB_SUCCESS) {
+ goto done;
+ }
+ if (res->count == 0) {
+ goto done;
+ }
+
+ if (res->count > 1) {
+ talloc_free(mem_ctx);
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+
+ msg = res->msgs[0];
+
+ password_attributes = ldb_msg_find_element(msg, "passwordAttribute");
+ if (!password_attributes) {
+ goto done;
+ }
+ data->password_attrs = talloc_array(data, const char *, password_attributes->num_values + 1);
+ if (!data->password_attrs) {
+ talloc_free(mem_ctx);
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ for (i=0; i < password_attributes->num_values; i++) {
+ data->password_attrs[i] = (const char *)password_attributes->values[i].data;
+ talloc_steal(data->password_attrs, password_attributes->values[i].data);
+ }
+ data->password_attrs[i] = NULL;
+
+ ret = ldb_mod_register_control(module, LDB_CONTROL_SD_FLAGS_OID);
+ if (ret != LDB_SUCCESS) {
+ ldb_debug(ldb, LDB_DEBUG_ERROR,
+ "partition: Unable to register control with rootdse!\n");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+done:
+ talloc_free(mem_ctx);
+ return ldb_next_init(module);
+}
+
+_PUBLIC_ const struct ldb_module_ops ldb_kludge_acl_module_ops = {
+ .name = "kludge_acl",
+ .search = kludge_acl_search,
+ .add = kludge_acl_change,
+ .modify = kludge_acl_change,
+ .del = kludge_acl_change,
+ .rename = kludge_acl_change,
+ .extended = kludge_acl_extended,
+ .init_context = kludge_acl_init
+};
diff --git a/source4/dsdb/samdb/ldb_modules/linked_attributes.c b/source4/dsdb/samdb/ldb_modules/linked_attributes.c
new file mode 100644
index 0000000000..4e28c8a149
--- /dev/null
+++ b/source4/dsdb/samdb/ldb_modules/linked_attributes.c
@@ -0,0 +1,1105 @@
+/*
+ ldb database library
+
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2007
+ Copyright (C) Simo Sorce <idra@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb linked_attributes module
+ *
+ * Description: Module to ensure linked attribute pairs remain in sync
+ *
+ * Author: Andrew Bartlett
+ */
+
+#include "includes.h"
+#include "ldb_module.h"
+#include "dlinklist.h"
+#include "dsdb/samdb/samdb.h"
+
+struct la_op_store {
+ struct la_op_store *next;
+ struct la_op_store *prev;
+ enum la_op {LA_OP_ADD, LA_OP_DEL} op;
+ struct ldb_dn *dn;
+ char *name;
+ char *value;
+};
+
+struct replace_context {
+ struct la_context *ac;
+ unsigned int num_elements;
+ struct ldb_message_element *el;
+};
+
+struct la_context {
+ const struct dsdb_schema *schema;
+ struct ldb_module *module;
+ struct ldb_request *req;
+ struct ldb_dn *add_dn;
+ struct ldb_dn *del_dn;
+ struct replace_context *rc;
+ struct la_op_store *ops;
+ struct ldb_extended *op_response;
+ struct ldb_control **op_controls;
+};
+
+static struct la_context *linked_attributes_init(struct ldb_module *module,
+ struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct la_context *ac;
+
+ ldb = ldb_module_get_ctx(module);
+
+ ac = talloc_zero(req, struct la_context);
+ if (ac == NULL) {
+ ldb_oom(ldb);
+ return NULL;
+ }
+
+ ac->schema = dsdb_get_schema(ldb);
+ ac->module = module;
+ ac->req = req;
+
+ return ac;
+}
+
+/* Common routine to handle reading the attributes and creating a
+ * series of modify requests */
+static int la_store_op(struct la_context *ac,
+ enum la_op op, struct ldb_val *dn,
+ const char *name)
+{
+ struct ldb_context *ldb;
+ struct la_op_store *os;
+ struct ldb_dn *op_dn;
+
+ ldb = ldb_module_get_ctx(ac->module);
+
+ op_dn = ldb_dn_from_ldb_val(ac, ldb, dn);
+ if (!op_dn) {
+ ldb_asprintf_errstring(ldb,
+ "could not parse attribute as a DN");
+ return LDB_ERR_INVALID_DN_SYNTAX;
+ }
+
+ os = talloc_zero(ac, struct la_op_store);
+ if (!os) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ os->op = op;
+
+ os->dn = talloc_steal(os, op_dn);
+
+ os->name = talloc_strdup(os, name);
+ if (!os->name) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* Do deletes before adds */
+ if (op == LA_OP_ADD) {
+ DLIST_ADD_END(ac->ops, os, struct la_op_store *);
+ } else {
+ /* By adding to the head of the list, we do deletes before
+ * adds when processing a replace */
+ DLIST_ADD(ac->ops, os);
+ }
+
+ return LDB_SUCCESS;
+}
+
+static int la_op_search_callback(struct ldb_request *req,
+ struct ldb_reply *ares);
+static int la_do_mod_request(struct la_context *ac);
+static int la_mod_callback(struct ldb_request *req,
+ struct ldb_reply *ares);
+static int la_down_req(struct la_context *ac);
+
+
+
+/* add */
+static int linked_attributes_add(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ const struct dsdb_attribute *target_attr;
+ struct la_context *ac;
+ const char *attr_name;
+ int ret;
+ int i, j;
+
+ ldb = ldb_module_get_ctx(module);
+
+ if (ldb_dn_is_special(req->op.add.message->dn)) {
+ /* do not manipulate our control entries */
+ return ldb_next_request(module, req);
+ }
+
+ ac = linked_attributes_init(module, req);
+ if (!ac) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if (!ac->schema) {
+ /* without schema, this doesn't make any sense */
+ talloc_free(ac);
+ return ldb_next_request(module, req);
+ }
+
+ /* Need to ensure we only have forward links being specified */
+ for (i=0; i < req->op.add.message->num_elements; i++) {
+ const struct ldb_message_element *el = &req->op.add.message->elements[i];
+ const struct dsdb_attribute *schema_attr
+ = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
+ if (!schema_attr) {
+ ldb_asprintf_errstring(ldb,
+ "attribute %s is not a valid attribute in schema", el->name);
+ return LDB_ERR_OBJECT_CLASS_VIOLATION;
+ }
+ /* We have a valid attribute, now find out if it is linked */
+ if (schema_attr->linkID == 0) {
+ continue;
+ }
+
+ if ((schema_attr->linkID & 1) == 1) {
+ /* Odd is for the target. Illigal to modify */
+ ldb_asprintf_errstring(ldb,
+ "attribute %s must not be modified directly, it is a linked attribute", el->name);
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+
+ /* Even link IDs are for the originating attribute */
+ target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
+ if (!target_attr) {
+ /*
+ * windows 2003 has a broken schema where
+ * the definition of msDS-IsDomainFor
+ * is missing (which is supposed to be
+ * the backlink of the msDS-HasDomainNCs
+ * attribute
+ */
+ continue;
+ }
+
+ attr_name = target_attr->lDAPDisplayName;
+
+ for (j = 0; j < el->num_values; j++) {
+ ret = la_store_op(ac, LA_OP_ADD,
+ &el->values[j],
+ attr_name);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+ }
+
+ /* if no linked attributes are present continue */
+ if (ac->ops == NULL) {
+ /* nothing to do for this module, proceed */
+ talloc_free(ac);
+ return ldb_next_request(module, req);
+ }
+
+ /* start with the original request */
+ return la_down_req(ac);
+}
+
+/* For a delete or rename, we need to find out what linked attributes
+ * are currently on this DN, and then deal with them. This is the
+ * callback to the base search */
+
+static int la_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+ struct ldb_context *ldb;
+ const struct dsdb_attribute *schema_attr;
+ const struct dsdb_attribute *target_attr;
+ struct ldb_message_element *search_el;
+ struct replace_context *rc;
+ struct la_context *ac;
+ const char *attr_name;
+ int i, j;
+ int ret = LDB_SUCCESS;
+
+ ac = talloc_get_type(req->context, struct la_context);
+ ldb = ldb_module_get_ctx(ac->module);
+ rc = ac->rc;
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ /* Only entries are interesting, and we only want the olddn */
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+
+ if (ldb_dn_compare(ares->message->dn, ac->req->op.mod.message->dn) != 0) {
+ ldb_asprintf_errstring(ldb,
+ "linked_attributes: %s is not the DN we were looking for", ldb_dn_get_linearized(ares->message->dn));
+ /* Guh? We only asked for this DN */
+ talloc_free(ares);
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ ac->add_dn = ac->del_dn = talloc_steal(ac, ares->message->dn);
+
+ /* We don't populate 'rc' for ADD - it can't be deleting elements anyway */
+ for (i = 0; rc && i < rc->num_elements; i++) {
+
+ schema_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rc->el[i].name);
+ if (!schema_attr) {
+ ldb_asprintf_errstring(ldb,
+ "attribute %s is not a valid attribute in schema",
+ rc->el[i].name);
+ talloc_free(ares);
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OBJECT_CLASS_VIOLATION);
+ }
+
+ search_el = ldb_msg_find_element(ares->message,
+ rc->el[i].name);
+
+ /* See if this element already exists */
+ /* otherwise just ignore as
+ * the add has already been scheduled */
+ if ( ! search_el) {
+ continue;
+ }
+
+ target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
+ if (!target_attr) {
+ /*
+ * windows 2003 has a broken schema where
+ * the definition of msDS-IsDomainFor
+ * is missing (which is supposed to be
+ * the backlink of the msDS-HasDomainNCs
+ * attribute
+ */
+ continue;
+ }
+ attr_name = target_attr->lDAPDisplayName;
+
+ /* Now we know what was there, we can remove it for the re-add */
+ for (j = 0; j < search_el->num_values; j++) {
+ ret = la_store_op(ac, LA_OP_DEL,
+ &search_el->values[j],
+ attr_name);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(ares);
+ return ldb_module_done(ac->req,
+ NULL, NULL, ret);
+ }
+ }
+ }
+
+ break;
+
+ case LDB_REPLY_REFERRAL:
+ /* ignore */
+ break;
+
+ case LDB_REPLY_DONE:
+
+ talloc_free(ares);
+
+ if (ac->req->operation == LDB_ADD) {
+ /* Start the modifies to the backlinks */
+ ret = la_do_mod_request(ac);
+
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ ret);
+ }
+ } else {
+ /* Start with the original request */
+ ret = la_down_req(ac);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+ }
+ return LDB_SUCCESS;
+ }
+
+ talloc_free(ares);
+ return ret;
+}
+
+
+/* modify */
+static int linked_attributes_modify(struct ldb_module *module, struct ldb_request *req)
+{
+ /* Look over list of modifications */
+ /* Find if any are for linked attributes */
+ /* Determine the effect of the modification */
+ /* Apply the modify to the linked entry */
+
+ struct ldb_context *ldb;
+ int i, j;
+ struct la_context *ac;
+ struct ldb_request *search_req;
+ const char **attrs;
+
+ int ret;
+
+ ldb = ldb_module_get_ctx(module);
+
+ if (ldb_dn_is_special(req->op.mod.message->dn)) {
+ /* do not manipulate our control entries */
+ return ldb_next_request(module, req);
+ }
+
+ ac = linked_attributes_init(module, req);
+ if (!ac) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if (!ac->schema) {
+ /* without schema, this doesn't make any sense */
+ return ldb_next_request(module, req);
+ }
+
+ ac->rc = talloc_zero(ac, struct replace_context);
+ if (!ac->rc) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ for (i=0; i < req->op.mod.message->num_elements; i++) {
+ bool store_el = false;
+ const char *attr_name;
+ const struct dsdb_attribute *target_attr;
+ const struct ldb_message_element *el = &req->op.mod.message->elements[i];
+ const struct dsdb_attribute *schema_attr
+ = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
+ if (!schema_attr) {
+ ldb_asprintf_errstring(ldb,
+ "attribute %s is not a valid attribute in schema", el->name);
+ return LDB_ERR_OBJECT_CLASS_VIOLATION;
+ }
+ /* We have a valid attribute, now find out if it is linked */
+ if (schema_attr->linkID == 0) {
+ continue;
+ }
+
+ if ((schema_attr->linkID & 1) == 1) {
+ /* Odd is for the target. Illegal to modify */
+ ldb_asprintf_errstring(ldb,
+ "attribute %s must not be modified directly, it is a linked attribute", el->name);
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+
+ /* Even link IDs are for the originating attribute */
+
+ /* Now find the target attribute */
+ target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
+ if (!target_attr) {
+ /*
+ * windows 2003 has a broken schema where
+ * the definition of msDS-IsDomainFor
+ * is missing (which is supposed to be
+ * the backlink of the msDS-HasDomainNCs
+ * attribute
+ */
+ continue;
+ }
+
+ attr_name = target_attr->lDAPDisplayName;
+
+ switch (el->flags & LDB_FLAG_MOD_MASK) {
+ case LDB_FLAG_MOD_REPLACE:
+ /* treat as just a normal add the delete part is handled by the callback */
+ store_el = true;
+
+ /* break intentionally missing */
+
+ case LDB_FLAG_MOD_ADD:
+
+ /* For each value being added, we need to setup the adds */
+ for (j = 0; j < el->num_values; j++) {
+ ret = la_store_op(ac, LA_OP_ADD,
+ &el->values[j],
+ attr_name);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+ break;
+
+ case LDB_FLAG_MOD_DELETE:
+
+ if (el->num_values) {
+ /* For each value being deleted, we need to setup the delete */
+ for (j = 0; j < el->num_values; j++) {
+ ret = la_store_op(ac, LA_OP_DEL,
+ &el->values[j],
+ attr_name);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+ } else {
+ /* Flag that there was a DELETE
+ * without a value specified, so we
+ * need to look for the old value */
+ store_el = true;
+ }
+
+ break;
+ }
+
+ if (store_el) {
+ struct ldb_message_element *search_el;
+
+ search_el = talloc_realloc(ac->rc, ac->rc->el,
+ struct ldb_message_element,
+ ac->rc->num_elements +1);
+ if (!search_el) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ac->rc->el = search_el;
+
+ ac->rc->el[ac->rc->num_elements] = *el;
+ ac->rc->num_elements++;
+ }
+ }
+
+ if (ac->ops || ac->rc->el) {
+ /* both replace and delete without values are handled in the callback
+ * after the search on the entry to be modified is performed */
+
+ attrs = talloc_array(ac->rc, const char *, ac->rc->num_elements + 1);
+ if (!attrs) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ for (i = 0; ac->rc && i < ac->rc->num_elements; i++) {
+ attrs[i] = ac->rc->el[i].name;
+ }
+ attrs[i] = NULL;
+
+ /* The callback does all the hard work here */
+ ret = ldb_build_search_req(&search_req, ldb, ac,
+ req->op.mod.message->dn,
+ LDB_SCOPE_BASE,
+ "(objectClass=*)", attrs,
+ NULL,
+ ac, la_mod_search_callback,
+ req);
+
+ /* We need to figure out our own extended DN, to fill in as the backlink target */
+ if (ret == LDB_SUCCESS) {
+ ret = ldb_request_add_control(search_req,
+ LDB_CONTROL_EXTENDED_DN_OID,
+ false, NULL);
+ }
+ if (ret == LDB_SUCCESS) {
+ talloc_steal(search_req, attrs);
+
+ ret = ldb_next_request(module, search_req);
+ }
+
+ } else {
+ /* nothing to do for this module, proceed */
+ talloc_free(ac);
+ ret = ldb_next_request(module, req);
+ }
+
+ return ret;
+}
+
+/* delete, rename */
+static int linked_attributes_del(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct ldb_request *search_req;
+ struct la_context *ac;
+ const char **attrs;
+ WERROR werr;
+ int ret;
+
+ /* This gets complex: We need to:
+ - Do a search for the entry
+ - Wait for these result to appear
+ - In the callback for the result, issue a modify
+ request based on the linked attributes found
+ - Wait for each modify result
+ - Regain our sainity
+ */
+
+ ldb = ldb_module_get_ctx(module);
+
+ ac = linked_attributes_init(module, req);
+ if (!ac) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if (!ac->schema) {
+ /* without schema, this doesn't make any sense */
+ return ldb_next_request(module, req);
+ }
+
+ werr = dsdb_linked_attribute_lDAPDisplayName_list(ac->schema, ac, &attrs);
+ if (!W_ERROR_IS_OK(werr)) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ldb_build_search_req(&search_req, ldb, req,
+ req->op.del.dn, LDB_SCOPE_BASE,
+ "(objectClass=*)", attrs,
+ NULL,
+ ac, la_op_search_callback,
+ req);
+
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ talloc_steal(search_req, attrs);
+
+ return ldb_next_request(module, search_req);
+}
+
+/* delete, rename */
+static int linked_attributes_rename(struct ldb_module *module, struct ldb_request *req)
+{
+ struct la_context *ac;
+
+ /* This gets complex: We need to:
+ - Do a search for the entry
+ - Wait for these result to appear
+ - In the callback for the result, issue a modify
+ request based on the linked attributes found
+ - Wait for each modify result
+ - Regain our sainity
+ */
+
+ ac = linked_attributes_init(module, req);
+ if (!ac) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if (!ac->schema) {
+ /* without schema, this doesn't make any sense */
+ return ldb_next_request(module, req);
+ }
+
+ /* start with the original request */
+ return la_down_req(ac);
+}
+
+
+static int la_op_search_callback(struct ldb_request *req,
+ struct ldb_reply *ares)
+{
+ struct ldb_context *ldb;
+ struct la_context *ac;
+ const struct dsdb_attribute *schema_attr;
+ const struct dsdb_attribute *target_attr;
+ const struct ldb_message_element *el;
+ const char *attr_name;
+ int i, j;
+ int ret;
+
+ ac = talloc_get_type(req->context, struct la_context);
+ ldb = ldb_module_get_ctx(ac->module);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ /* Only entries are interesting, and we only want the olddn */
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+ ret = ldb_dn_compare(ares->message->dn, req->op.search.base);
+ if (ret != 0) {
+ /* Guh? We only asked for this DN */
+ talloc_free(ares);
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->message->num_elements == 0) {
+ /* only bother at all if there were some
+ * linked attributes found */
+ talloc_free(ares);
+ return LDB_SUCCESS;
+ }
+
+ switch (ac->req->operation) {
+ case LDB_DELETE:
+ ac->del_dn = talloc_steal(ac, ares->message->dn);
+ break;
+ case LDB_RENAME:
+ ac->add_dn = talloc_steal(ac, ares->message->dn);
+ ac->del_dn = talloc_steal(ac, ac->req->op.rename.olddn);
+ break;
+ default:
+ talloc_free(ares);
+ ldb_set_errstring(ldb,
+ "operations must be delete or rename");
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ for (i = 0; i < ares->message->num_elements; i++) {
+ el = &ares->message->elements[i];
+
+ schema_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
+ if (!schema_attr) {
+ ldb_asprintf_errstring(ldb,
+ "attribute %s is not a valid attribute"
+ " in schema", el->name);
+ talloc_free(ares);
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OBJECT_CLASS_VIOLATION);
+ }
+
+ /* Valid attribute, now find out if it is linked */
+ if (schema_attr->linkID == 0) {
+ /* Not a linked attribute, skip */
+ continue;
+ }
+
+ if ((schema_attr->linkID & 1) == 0) {
+ /* Odd is for the target. */
+ target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
+ if (!target_attr) {
+ continue;
+ }
+ attr_name = target_attr->lDAPDisplayName;
+ } else {
+ target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID - 1);
+ if (!target_attr) {
+ continue;
+ }
+ attr_name = target_attr->lDAPDisplayName;
+ }
+ for (j = 0; j < el->num_values; j++) {
+ ret = la_store_op(ac, LA_OP_DEL,
+ &el->values[j],
+ attr_name);
+
+ /* for renames, ensure we add it back */
+ if (ret == LDB_SUCCESS
+ && ac->req->operation == LDB_RENAME) {
+ ret = la_store_op(ac, LA_OP_ADD,
+ &el->values[j],
+ attr_name);
+ }
+ if (ret != LDB_SUCCESS) {
+ talloc_free(ares);
+ return ldb_module_done(ac->req,
+ NULL, NULL, ret);
+ }
+ }
+ }
+
+ break;
+
+ case LDB_REPLY_REFERRAL:
+ /* ignore */
+ break;
+
+ case LDB_REPLY_DONE:
+
+ talloc_free(ares);
+
+
+ switch (ac->req->operation) {
+ case LDB_DELETE:
+ /* start the mod requests chain */
+ ret = la_down_req(ac);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+ break;
+ case LDB_RENAME:
+
+ ret = la_do_mod_request(ac);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ ret);
+ }
+
+ return ret;
+
+ default:
+ talloc_free(ares);
+ ldb_set_errstring(ldb,
+ "operations must be delete or rename");
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ return LDB_SUCCESS;
+ }
+
+ talloc_free(ares);
+ return LDB_SUCCESS;
+}
+
+/* do a linked attributes modify request */
+static int la_do_mod_request(struct la_context *ac)
+{
+ struct ldb_message_element *ret_el;
+ struct ldb_request *mod_req;
+ struct ldb_message *new_msg;
+ struct ldb_context *ldb;
+ int ret;
+
+ /* If we have no modifies in the queue, we are done! */
+ if (!ac->ops) {
+ return ldb_module_done(ac->req, ac->op_controls,
+ ac->op_response, LDB_SUCCESS);
+ }
+
+ ldb = ldb_module_get_ctx(ac->module);
+
+ /* Create the modify request */
+ new_msg = ldb_msg_new(ac);
+ if (!new_msg) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ new_msg->dn = ac->ops->dn;
+
+ if (ac->ops->op == LA_OP_ADD) {
+ ret = ldb_msg_add_empty(new_msg, ac->ops->name,
+ LDB_FLAG_MOD_ADD, &ret_el);
+ } else {
+ ret = ldb_msg_add_empty(new_msg, ac->ops->name,
+ LDB_FLAG_MOD_DELETE, &ret_el);
+ }
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ ret_el->values = talloc_array(new_msg, struct ldb_val, 1);
+ if (!ret_el->values) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ret_el->num_values = 1;
+ if (ac->ops->op == LA_OP_ADD) {
+ ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->add_dn, 1));
+ } else {
+ ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->del_dn, 1));
+ }
+
+#if 0
+ ldb_debug(ldb, LDB_DEBUG_WARNING,
+ "link on %s %s: %s %s\n",
+ ldb_dn_get_linearized(new_msg->dn), ret_el->name,
+ ret_el->values[0].data, ac->ops->op == LA_OP_ADD ? "added" : "deleted");
+#endif
+
+ /* use ac->ops as the mem_ctx so that the request will be freed
+ * in the callback as soon as completed */
+ ret = ldb_build_mod_req(&mod_req, ldb, ac->ops,
+ new_msg,
+ NULL,
+ ac, la_mod_callback,
+ ac->req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ talloc_steal(mod_req, new_msg);
+
+ /* Run the new request */
+ return ldb_next_request(ac->module, mod_req);
+}
+
+static int la_mod_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+ struct la_context *ac;
+ struct ldb_context *ldb;
+ struct la_op_store *os;
+
+ ac = talloc_get_type(req->context, struct la_context);
+ ldb = ldb_module_get_ctx(ac->module);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ if (ares->type != LDB_REPLY_DONE) {
+ ldb_set_errstring(ldb,
+ "invalid ldb_reply_type in callback");
+ talloc_free(ares);
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ talloc_free(ares);
+
+ os = ac->ops;
+ DLIST_REMOVE(ac->ops, os);
+
+ /* this frees the request too
+ * DO NOT access 'req' after this point */
+ talloc_free(os);
+
+ return la_do_mod_request(ac);
+}
+
+/* Having done the original operation, then try to fix up all the linked attributes for modify and delete */
+static int la_mod_del_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+ int ret;
+ struct la_context *ac;
+ struct ldb_context *ldb;
+
+ ac = talloc_get_type(req->context, struct la_context);
+ ldb = ldb_module_get_ctx(ac->module);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ if (ares->type != LDB_REPLY_DONE) {
+ ldb_set_errstring(ldb,
+ "invalid ldb_reply_type in callback");
+ talloc_free(ares);
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ ac->op_controls = talloc_steal(ac, ares->controls);
+ ac->op_response = talloc_steal(ac, ares->response);
+
+ /* If we have modfies to make, this is the time to do them for modify and delete */
+ ret = la_do_mod_request(ac);
+
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+ talloc_free(ares);
+
+ /* la_do_mod_request has already sent the callbacks */
+ return LDB_SUCCESS;
+
+}
+
+/* Having done the original rename try to fix up all the linked attributes */
+static int la_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+ int ret;
+ struct la_context *ac;
+ struct ldb_request *search_req;
+ const char **attrs;
+ WERROR werr;
+ struct ldb_context *ldb;
+
+ ac = talloc_get_type(req->context, struct la_context);
+ ldb = ldb_module_get_ctx(ac->module);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ if (ares->type != LDB_REPLY_DONE) {
+ ldb_set_errstring(ldb,
+ "invalid ldb_reply_type in callback");
+ talloc_free(ares);
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ werr = dsdb_linked_attribute_lDAPDisplayName_list(ac->schema, ac, &attrs);
+ if (!W_ERROR_IS_OK(werr)) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ldb_build_search_req(&search_req, ldb, req,
+ ac->req->op.rename.newdn, LDB_SCOPE_BASE,
+ "(objectClass=*)", attrs,
+ NULL,
+ ac, la_op_search_callback,
+ req);
+
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ talloc_steal(search_req, attrs);
+
+ if (ret == LDB_SUCCESS) {
+ ret = ldb_request_add_control(search_req,
+ LDB_CONTROL_EXTENDED_DN_OID,
+ false, NULL);
+ }
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ ret);
+ }
+
+ ac->op_controls = talloc_steal(ac, ares->controls);
+ ac->op_response = talloc_steal(ac, ares->response);
+
+ return ldb_next_request(ac->module, search_req);
+}
+
+/* Having done the original add, then try to fix up all the linked attributes
+
+ This is done after the add so the links can get the extended DNs correctly.
+ */
+static int la_add_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+ int ret;
+ struct la_context *ac;
+ struct ldb_context *ldb;
+
+ ac = talloc_get_type(req->context, struct la_context);
+ ldb = ldb_module_get_ctx(ac->module);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ if (ares->type != LDB_REPLY_DONE) {
+ ldb_set_errstring(ldb,
+ "invalid ldb_reply_type in callback");
+ talloc_free(ares);
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ if (ac->ops) {
+ struct ldb_request *search_req;
+ static const char *attrs[] = { NULL };
+
+ /* The callback does all the hard work here - we need
+ * the objectGUID and SID of the added record */
+ ret = ldb_build_search_req(&search_req, ldb, ac,
+ ac->req->op.add.message->dn,
+ LDB_SCOPE_BASE,
+ "(objectClass=*)", attrs,
+ NULL,
+ ac, la_mod_search_callback,
+ ac->req);
+
+ if (ret == LDB_SUCCESS) {
+ ret = ldb_request_add_control(search_req,
+ LDB_CONTROL_EXTENDED_DN_OID,
+ false, NULL);
+ }
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ ret);
+ }
+
+ ac->op_controls = talloc_steal(ac, ares->controls);
+ ac->op_response = talloc_steal(ac, ares->response);
+
+ return ldb_next_request(ac->module, search_req);
+
+ } else {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+}
+
+/* Reconstruct the original request, but pointing at our local callback to finish things off */
+static int la_down_req(struct la_context *ac)
+{
+ struct ldb_request *down_req;
+ int ret;
+ struct ldb_context *ldb;
+
+ ldb = ldb_module_get_ctx(ac->module);
+
+ switch (ac->req->operation) {
+ case LDB_ADD:
+ ret = ldb_build_add_req(&down_req, ldb, ac,
+ ac->req->op.add.message,
+ ac->req->controls,
+ ac, la_add_callback,
+ ac->req);
+ break;
+ case LDB_MODIFY:
+ ret = ldb_build_mod_req(&down_req, ldb, ac,
+ ac->req->op.mod.message,
+ ac->req->controls,
+ ac, la_mod_del_callback,
+ ac->req);
+ break;
+ case LDB_DELETE:
+ ret = ldb_build_del_req(&down_req, ldb, ac,
+ ac->req->op.del.dn,
+ ac->req->controls,
+ ac, la_mod_del_callback,
+ ac->req);
+ break;
+ case LDB_RENAME:
+ ret = ldb_build_rename_req(&down_req, ldb, ac,
+ ac->req->op.rename.olddn,
+ ac->req->op.rename.newdn,
+ ac->req->controls,
+ ac, la_rename_callback,
+ ac->req);
+ break;
+ default:
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ }
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ return ldb_next_request(ac->module, down_req);
+}
+
+
+_PUBLIC_ const struct ldb_module_ops ldb_linked_attributes_module_ops = {
+ .name = "linked_attributes",
+ .add = linked_attributes_add,
+ .modify = linked_attributes_modify,
+ .del = linked_attributes_del,
+ .rename = linked_attributes_rename,
+};
diff --git a/source4/dsdb/samdb/ldb_modules/local_password.c b/source4/dsdb/samdb/ldb_modules/local_password.c
new file mode 100644
index 0000000000..58c0f1f0d5
--- /dev/null
+++ b/source4/dsdb/samdb/ldb_modules/local_password.c
@@ -0,0 +1,1098 @@
+/*
+ ldb database module
+
+ Copyright (C) Simo Sorce 2004-2008
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2006
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb local_password module
+ *
+ * Description: correctly update hash values based on changes to userPassword and friends
+ *
+ * Author: Andrew Bartlett
+ */
+
+#include "includes.h"
+#include "libcli/ldap/ldap.h"
+#include "ldb_module.h"
+#include "dsdb/samdb/samdb.h"
+#include "librpc/ndr/libndr.h"
+#include "dsdb/samdb/ldb_modules/password_modules.h"
+
+#define PASSWORD_GUID_ATTR "masterGUID"
+
+/* This module maintains a local password database, seperate from the main LDAP server.
+
+ This allows the password database to be syncronised in a multi-master
+ fashion, seperate to the more difficult concerns of the main
+ database. (With passwords, the last writer always wins)
+
+ Each incoming add/modify is split into a remote, and a local request, done in that order.
+
+ We maintain a list of attributes that are kept locally - perhaps
+ this should use the @KLUDGE_ACL list of passwordAttribute
+ */
+
+static const char * const password_attrs[] = {
+ "supplementalCredentials",
+ "unicodePwd",
+ "dBCSPwd",
+ "lmPwdHistory",
+ "ntPwdHistory",
+ "msDS-KeyVersionNumber",
+ "pwdLastSet"
+};
+
+/* And we merge them back into search requests when asked to do so */
+
+struct lpdb_reply {
+ struct lpdb_reply *next;
+ struct ldb_reply *remote;
+ struct ldb_dn *local_dn;
+};
+
+struct lpdb_context {
+
+ struct ldb_module *module;
+ struct ldb_request *req;
+
+ struct ldb_message *local_message;
+
+ struct lpdb_reply *list;
+ struct lpdb_reply *current;
+ struct ldb_reply *remote_done;
+ struct ldb_reply *remote;
+
+ bool added_objectGUID;
+ bool added_objectClass;
+
+};
+
+static struct lpdb_context *lpdb_init_context(struct ldb_module *module,
+ struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct lpdb_context *ac;
+
+ ldb = ldb_module_get_ctx(module);
+
+ ac = talloc_zero(req, struct lpdb_context);
+ if (ac == NULL) {
+ ldb_set_errstring(ldb, "Out of Memory");
+ return NULL;
+ }
+
+ ac->module = module;
+ ac->req = req;
+
+ return ac;
+}
+
+static int lpdb_local_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+ struct ldb_context *ldb;
+ struct lpdb_context *ac;
+
+ ac = talloc_get_type(req->context, struct lpdb_context);
+ ldb = ldb_module_get_ctx(ac->module);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ if (ares->type != LDB_REPLY_DONE) {
+ ldb_set_errstring(ldb, "Unexpected reply type");
+ talloc_free(ares);
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ talloc_free(ares);
+ return ldb_module_done(ac->req,
+ ac->remote_done->controls,
+ ac->remote_done->response,
+ ac->remote_done->error);
+}
+
+/*****************************************************************************
+ * ADD
+ ****************************************************************************/
+
+static int lpdb_add_callback(struct ldb_request *req,
+ struct ldb_reply *ares);
+
+static int local_password_add(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct ldb_message *remote_message;
+ struct ldb_request *remote_req;
+ struct lpdb_context *ac;
+ struct GUID objectGUID;
+ int ret;
+ int i;
+
+ ldb = ldb_module_get_ctx(module);
+ ldb_debug(ldb, LDB_DEBUG_TRACE, "local_password_add\n");
+
+ if (ldb_dn_is_special(req->op.add.message->dn)) { /* do not manipulate our control entries */
+ return ldb_next_request(module, req);
+ }
+
+ /* If the caller is manipulating the local passwords directly, let them pass */
+ if (ldb_dn_compare_base(ldb_dn_new(req, ldb, LOCAL_BASE),
+ req->op.add.message->dn) == 0) {
+ return ldb_next_request(module, req);
+ }
+
+ for (i=0; i < ARRAY_SIZE(password_attrs); i++) {
+ if (ldb_msg_find_element(req->op.add.message, password_attrs[i])) {
+ break;
+ }
+ }
+
+ /* It didn't match any of our password attributes, go on */
+ if (i == ARRAY_SIZE(password_attrs)) {
+ return ldb_next_request(module, req);
+ }
+
+ /* TODO: remove this when userPassword will be in schema */
+ if (!ldb_msg_check_string_attribute(req->op.add.message, "objectClass", "person")) {
+ ldb_asprintf_errstring(ldb,
+ "Cannot relocate a password on entry: %s, does not have objectClass 'person'",
+ ldb_dn_get_linearized(req->op.add.message->dn));
+ return LDB_ERR_OBJECT_CLASS_VIOLATION;
+ }
+
+ /* From here, we assume we have password attributes to split off */
+ ac = lpdb_init_context(module, req);
+ if (!ac) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ remote_message = ldb_msg_copy_shallow(remote_req, req->op.add.message);
+ if (remote_message == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* Remove any password attributes from the remote message */
+ for (i=0; i < ARRAY_SIZE(password_attrs); i++) {
+ ldb_msg_remove_attr(remote_message, password_attrs[i]);
+ }
+
+ /* Find the objectGUID to use as the key */
+ objectGUID = samdb_result_guid(ac->req->op.add.message, "objectGUID");
+
+ ac->local_message = ldb_msg_copy_shallow(ac, req->op.add.message);
+ if (ac->local_message == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* Remove anything seen in the remote message from the local
+ * message (leaving only password attributes) */
+ for (i=0; i < remote_message->num_elements; i++) {
+ ldb_msg_remove_attr(ac->local_message, remote_message->elements[i].name);
+ }
+
+ /* We must have an objectGUID already, or we don't know where
+ * to add the password. This may be changed to an 'add and
+ * search', to allow the directory to create the objectGUID */
+ if (ldb_msg_find_ldb_val(req->op.add.message, "objectGUID") == NULL) {
+ ldb_set_errstring(ldb,
+ "no objectGUID found in search: "
+ "local_password module must be "
+ "onfigured below objectGUID module!\n");
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+
+ ac->local_message->dn = ldb_dn_new(ac->local_message,
+ ldb, LOCAL_BASE);
+ if ((ac->local_message->dn == NULL) ||
+ ( ! ldb_dn_add_child_fmt(ac->local_message->dn,
+ PASSWORD_GUID_ATTR "=%s",
+ GUID_string(ac->local_message,
+ &objectGUID)))) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ldb_build_add_req(&remote_req, ldb, ac,
+ remote_message,
+ req->controls,
+ ac, lpdb_add_callback,
+ req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ return ldb_next_request(module, remote_req);
+}
+
+/* Add a record, splitting password attributes from the user's main
+ * record */
+static int lpdb_add_callback(struct ldb_request *req,
+ struct ldb_reply *ares)
+{
+ struct ldb_context *ldb;
+ struct ldb_request *local_req;
+ struct lpdb_context *ac;
+ int ret;
+
+ ac = talloc_get_type(req->context, struct lpdb_context);
+ ldb = ldb_module_get_ctx(ac->module);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ if (ares->type != LDB_REPLY_DONE) {
+ ldb_set_errstring(ldb, "Unexpected reply type");
+ talloc_free(ares);
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ ac->remote_done = talloc_steal(ac, ares);
+
+ ret = ldb_build_add_req(&local_req, ldb, ac,
+ ac->local_message,
+ NULL,
+ ac, lpdb_local_callback,
+ ac->req);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+
+ ret = ldb_next_request(ac->module, local_req);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+ return LDB_SUCCESS;
+}
+
+/*****************************************************************************
+ * MODIFY
+ ****************************************************************************/
+
+static int lpdb_modify_callabck(struct ldb_request *req,
+ struct ldb_reply *ares);
+static int lpdb_mod_search_callback(struct ldb_request *req,
+ struct ldb_reply *ares);
+
+static int local_password_modify(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct lpdb_context *ac;
+ struct ldb_message *remote_message;
+ struct ldb_request *remote_req;
+ int ret;
+ int i;
+
+ ldb = ldb_module_get_ctx(module);
+ ldb_debug(ldb, LDB_DEBUG_TRACE, "local_password_modify\n");
+
+ if (ldb_dn_is_special(req->op.mod.message->dn)) { /* do not manipulate our control entries */
+ return ldb_next_request(module, req);
+ }
+
+ /* If the caller is manipulating the local passwords directly, let them pass */
+ if (ldb_dn_compare_base(ldb_dn_new(req, ldb, LOCAL_BASE),
+ req->op.mod.message->dn) == 0) {
+ return ldb_next_request(module, req);
+ }
+
+ for (i=0; i < ARRAY_SIZE(password_attrs); i++) {
+ if (ldb_msg_find_element(req->op.add.message, password_attrs[i])) {
+ break;
+ }
+ }
+
+ /* It didn't match any of our password attributes, then we have nothing to do here */
+ if (i == ARRAY_SIZE(password_attrs)) {
+ return ldb_next_request(module, req);
+ }
+
+ /* From here, we assume we have password attributes to split off */
+ ac = lpdb_init_context(module, req);
+ if (!ac) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ remote_message = ldb_msg_copy_shallow(ac, ac->req->op.mod.message);
+ if (remote_message == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* Remove any password attributes from the remote message */
+ for (i=0; i < ARRAY_SIZE(password_attrs); i++) {
+ ldb_msg_remove_attr(remote_message, password_attrs[i]);
+ }
+
+ ac->local_message = ldb_msg_copy_shallow(ac, ac->req->op.mod.message);
+ if (ac->local_message == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* Remove anything seen in the remote message from the local
+ * message (leaving only password attributes) */
+ for (i=0; i < remote_message->num_elements;i++) {
+ ldb_msg_remove_attr(ac->local_message, remote_message->elements[i].name);
+ }
+
+ ret = ldb_build_mod_req(&remote_req, ldb, ac,
+ remote_message,
+ req->controls,
+ ac, lpdb_modify_callabck,
+ req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ return ldb_next_request(module, remote_req);
+}
+
+/* On a modify, we don't have the objectGUID handy, so we need to
+ * search our DN for it */
+static int lpdb_modify_callabck(struct ldb_request *req,
+ struct ldb_reply *ares)
+{
+ struct ldb_context *ldb;
+ static const char * const attrs[] = { "objectGUID", "objectClass", NULL };
+ struct ldb_request *search_req;
+ struct lpdb_context *ac;
+ int ret;
+
+ ac = talloc_get_type(req->context, struct lpdb_context);
+ ldb = ldb_module_get_ctx(ac->module);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ if (ares->type != LDB_REPLY_DONE) {
+ ldb_set_errstring(ldb, "Unexpected reply type");
+ talloc_free(ares);
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ ac->remote_done = talloc_steal(ac, ares);
+
+ /* prepare the search operation */
+ ret = ldb_build_search_req(&search_req, ldb, ac,
+ ac->req->op.mod.message->dn, LDB_SCOPE_BASE,
+ "(objectclass=*)", attrs,
+ NULL,
+ ac, lpdb_mod_search_callback,
+ ac->req);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ ret = ldb_next_request(ac->module, search_req);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ return LDB_SUCCESS;
+}
+
+/* Called when we search for our own entry. Stores the one entry we
+ * expect (as it is a base search) on the context pointer */
+static int lpdb_mod_search_callback(struct ldb_request *req,
+ struct ldb_reply *ares)
+{
+ struct ldb_context *ldb;
+ struct ldb_request *local_req;
+ struct lpdb_context *ac;
+ struct ldb_dn *local_dn;
+ struct GUID objectGUID;
+ int ret = LDB_SUCCESS;
+
+ ac = talloc_get_type(req->context, struct lpdb_context);
+ ldb = ldb_module_get_ctx(ac->module);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+ if (ac->remote != NULL) {
+ ldb_set_errstring(ldb, "Too many results");
+ talloc_free(ares);
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ ac->remote = talloc_steal(ac, ares);
+ break;
+
+ case LDB_REPLY_REFERRAL:
+
+ /* ignore */
+ talloc_free(ares);
+ break;
+
+ case LDB_REPLY_DONE:
+ /* After we find out the objectGUID for the entry, modify the local
+ * password database as required */
+
+ talloc_free(ares);
+
+ /* if it is not an entry of type person this is an error */
+ /* TODO: remove this when sambaPassword will be in schema */
+ if (ac->remote == NULL) {
+ ldb_asprintf_errstring(ldb,
+ "entry just modified (%s) not found!",
+ ldb_dn_get_linearized(req->op.search.base));
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (!ldb_msg_check_string_attribute(ac->remote->message,
+ "objectClass", "person")) {
+ /* Not relevent to us */
+ return ldb_module_done(ac->req,
+ ac->remote_done->controls,
+ ac->remote_done->response,
+ ac->remote_done->error);
+ }
+
+ if (ldb_msg_find_ldb_val(ac->remote->message,
+ "objectGUID") == NULL) {
+ ldb_set_errstring(ldb,
+ "no objectGUID found in search: "
+ "local_password module must be "
+ "configured below objectGUID "
+ "module!\n");
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OBJECT_CLASS_VIOLATION);
+ }
+
+ objectGUID = samdb_result_guid(ac->remote->message,
+ "objectGUID");
+
+ local_dn = ldb_dn_new(ac, ldb, LOCAL_BASE);
+ if ((local_dn == NULL) ||
+ ( ! ldb_dn_add_child_fmt(local_dn,
+ PASSWORD_GUID_ATTR "=%s",
+ GUID_string(ac, &objectGUID)))) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ ac->local_message->dn = local_dn;
+
+ ret = ldb_build_mod_req(&local_req, ldb, ac,
+ ac->local_message,
+ NULL,
+ ac, lpdb_local_callback,
+ ac->req);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+
+ /* perform the local update */
+ ret = ldb_next_request(ac->module, local_req);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+ }
+
+ return LDB_SUCCESS;
+}
+
+/*****************************************************************************
+ * DELETE
+ ****************************************************************************/
+
+static int lpdb_delete_callabck(struct ldb_request *req,
+ struct ldb_reply *ares);
+static int lpdb_del_search_callback(struct ldb_request *req,
+ struct ldb_reply *ares);
+
+static int local_password_delete(struct ldb_module *module,
+ struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct ldb_request *remote_req;
+ struct lpdb_context *ac;
+ int ret;
+
+ ldb = ldb_module_get_ctx(module);
+ ldb_debug(ldb, LDB_DEBUG_TRACE, "local_password_delete\n");
+
+ /* do not manipulate our control entries */
+ if (ldb_dn_is_special(req->op.mod.message->dn)) {
+ return ldb_next_request(module, req);
+ }
+
+ /* If the caller is manipulating the local passwords directly,
+ * let them pass */
+ if (ldb_dn_compare_base(ldb_dn_new(req, ldb, LOCAL_BASE),
+ req->op.del.dn) == 0) {
+ return ldb_next_request(module, req);
+ }
+
+ /* From here, we assume we have password attributes to split off */
+ ac = lpdb_init_context(module, req);
+ if (!ac) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ldb_build_del_req(&remote_req, ldb, ac,
+ req->op.del.dn,
+ req->controls,
+ ac, lpdb_delete_callabck,
+ req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ return ldb_next_request(module, remote_req);
+}
+
+/* On a modify, we don't have the objectGUID handy, so we need to
+ * search our DN for it */
+static int lpdb_delete_callabck(struct ldb_request *req,
+ struct ldb_reply *ares)
+{
+ struct ldb_context *ldb;
+ static const char * const attrs[] = { "objectGUID", "objectClass", NULL };
+ struct ldb_request *search_req;
+ struct lpdb_context *ac;
+ int ret;
+
+ ac = talloc_get_type(req->context, struct lpdb_context);
+ ldb = ldb_module_get_ctx(ac->module);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ if (ares->type != LDB_REPLY_DONE) {
+ ldb_set_errstring(ldb, "Unexpected reply type");
+ talloc_free(ares);
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ ac->remote_done = talloc_steal(ac, ares);
+
+ /* prepare the search operation */
+ ret = ldb_build_search_req(&search_req, ldb, ac,
+ ac->req->op.del.dn, LDB_SCOPE_BASE,
+ "(objectclass=*)", attrs,
+ NULL,
+ ac, lpdb_del_search_callback,
+ ac->req);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ ret = ldb_next_request(ac->module, search_req);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ return LDB_SUCCESS;
+}
+
+/* Called when we search for our own entry. Stores the one entry we
+ * expect (as it is a base search) on the context pointer */
+static int lpdb_del_search_callback(struct ldb_request *req,
+ struct ldb_reply *ares)
+{
+ struct ldb_context *ldb;
+ struct ldb_request *local_req;
+ struct lpdb_context *ac;
+ struct ldb_dn *local_dn;
+ struct GUID objectGUID;
+ int ret = LDB_SUCCESS;
+
+ ac = talloc_get_type(req->context, struct lpdb_context);
+ ldb = ldb_module_get_ctx(ac->module);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+ if (ac->remote != NULL) {
+ ldb_set_errstring(ldb, "Too many results");
+ talloc_free(ares);
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ ac->remote = talloc_steal(ac, ares);
+ break;
+
+ case LDB_REPLY_REFERRAL:
+
+ /* ignore */
+ talloc_free(ares);
+ break;
+
+ case LDB_REPLY_DONE:
+ /* After we find out the objectGUID for the entry, modify the local
+ * password database as required */
+
+ talloc_free(ares);
+
+ /* if it is not an entry of type person this is NOT an error */
+ /* TODO: remove this when sambaPassword will be in schema */
+ if (ac->remote == NULL) {
+ return ldb_module_done(ac->req,
+ ac->remote_done->controls,
+ ac->remote_done->response,
+ ac->remote_done->error);
+ }
+ if (!ldb_msg_check_string_attribute(ac->remote->message,
+ "objectClass", "person")) {
+ /* Not relevent to us */
+ return ldb_module_done(ac->req,
+ ac->remote_done->controls,
+ ac->remote_done->response,
+ ac->remote_done->error);
+ }
+
+ if (ldb_msg_find_ldb_val(ac->remote->message,
+ "objectGUID") == NULL) {
+ ldb_set_errstring(ldb,
+ "no objectGUID found in search: "
+ "local_password module must be "
+ "configured below objectGUID "
+ "module!\n");
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OBJECT_CLASS_VIOLATION);
+ }
+
+ objectGUID = samdb_result_guid(ac->remote->message,
+ "objectGUID");
+
+ local_dn = ldb_dn_new(ac, ldb, LOCAL_BASE);
+ if ((local_dn == NULL) ||
+ ( ! ldb_dn_add_child_fmt(local_dn,
+ PASSWORD_GUID_ATTR "=%s",
+ GUID_string(ac, &objectGUID)))) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ ret = ldb_build_del_req(&local_req, ldb, ac,
+ local_dn,
+ NULL,
+ ac, lpdb_local_callback,
+ ac->req);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+
+ /* perform the local update */
+ ret = ldb_next_request(ac->module, local_req);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+ }
+
+ return LDB_SUCCESS;
+}
+
+
+/*****************************************************************************
+ * SEARCH
+ ****************************************************************************/
+
+static int lpdb_local_search_callback(struct ldb_request *req,
+ struct ldb_reply *ares);
+
+static int lpdb_local_search(struct lpdb_context *ac)
+{
+ struct ldb_context *ldb;
+ struct ldb_request *local_req;
+ int ret;
+
+ ldb = ldb_module_get_ctx(ac->module);
+
+ ret = ldb_build_search_req(&local_req, ldb, ac,
+ ac->current->local_dn,
+ LDB_SCOPE_BASE,
+ "(objectclass=*)",
+ ac->req->op.search.attrs,
+ NULL,
+ ac, lpdb_local_search_callback,
+ ac->req);
+ if (ret != LDB_SUCCESS) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ return ldb_next_request(ac->module, local_req);
+}
+
+static int lpdb_local_search_callback(struct ldb_request *req,
+ struct ldb_reply *ares)
+{
+ struct ldb_context *ldb;
+ struct lpdb_context *ac;
+ struct ldb_reply *merge;
+ struct lpdb_reply *lr;
+ int ret;
+ int i;
+
+ ac = talloc_get_type(req->context, struct lpdb_context);
+ ldb = ldb_module_get_ctx(ac->module);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ lr = ac->current;
+
+ /* we are interested only in a single reply (base search) */
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+
+ if (lr->remote == NULL) {
+ ldb_set_errstring(ldb,
+ "Too many results for password entry search!");
+ talloc_free(ares);
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ merge = lr->remote;
+ lr->remote = NULL;
+
+ /* steal the local results on the remote results to be
+ * returned all together */
+ talloc_steal(merge, ares->message->elements);
+
+ /* Make sure never to return the internal key attribute */
+ ldb_msg_remove_attr(ares->message, PASSWORD_GUID_ATTR);
+
+ for (i=0; i < ares->message->num_elements; i++) {
+ struct ldb_message_element *el;
+
+ el = ldb_msg_find_element(merge->message,
+ ares->message->elements[i].name);
+ if (!el) {
+ ret = ldb_msg_add_empty(merge->message,
+ ares->message->elements[i].name,
+ 0, &el);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(ares);
+ return ldb_module_done(ac->req,
+ NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ *el = ares->message->elements[i];
+ }
+ }
+
+ /* free the rest */
+ talloc_free(ares);
+
+ return ldb_module_send_entry(ac->req, merge->message, merge->controls);
+
+ case LDB_REPLY_REFERRAL:
+ /* ignore */
+ talloc_free(ares);
+ break;
+
+ case LDB_REPLY_DONE:
+
+ talloc_free(ares);
+
+ /* if this entry was not returned yet, return it now */
+ if (lr->remote) {
+ ret = ldb_module_send_entry(ac->req, ac->remote->message, ac->remote->controls);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req,
+ NULL, NULL, ret);
+ }
+ lr->remote = NULL;
+ }
+
+ if (lr->next->remote->type == LDB_REPLY_DONE) {
+ /* this was the last one */
+ return ldb_module_done(ac->req,
+ lr->next->remote->controls,
+ lr->next->remote->response,
+ lr->next->remote->error);
+ } else {
+ /* next one */
+ ac->current = lr->next;
+ talloc_free(lr);
+
+ ret = lpdb_local_search(ac);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req,
+ NULL, NULL, ret);
+ }
+ }
+ }
+
+ return LDB_SUCCESS;
+}
+
+/* For each entry returned in a remote search, do a local base search,
+ * based on the objectGUID we asked for as an additional attribute */
+static int lpdb_remote_search_callback(struct ldb_request *req,
+ struct ldb_reply *ares)
+{
+ struct ldb_context *ldb;
+ struct lpdb_context *ac;
+ struct ldb_dn *local_dn;
+ struct GUID objectGUID;
+ struct lpdb_reply *lr;
+ int ret;
+
+ ac = talloc_get_type(req->context, struct lpdb_context);
+ ldb = ldb_module_get_ctx(ac->module);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+ /* No point searching further if it's not a 'person' entry */
+ if (!ldb_msg_check_string_attribute(ares->message, "objectClass", "person")) {
+
+ /* Make sure to remove anything we added */
+ if (ac->added_objectGUID) {
+ ldb_msg_remove_attr(ares->message, "objectGUID");
+ }
+
+ if (ac->added_objectClass) {
+ ldb_msg_remove_attr(ares->message, "objectClass");
+ }
+
+ return ldb_module_send_entry(ac->req, ares->message, ares->controls);
+ }
+
+ if (ldb_msg_find_ldb_val(ares->message, "objectGUID") == NULL) {
+ ldb_set_errstring(ldb,
+ "no objectGUID found in search: local_password module must be configured below objectGUID module!\n");
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ objectGUID = samdb_result_guid(ares->message, "objectGUID");
+
+ if (ac->added_objectGUID) {
+ ldb_msg_remove_attr(ares->message, "objectGUID");
+ }
+
+ if (ac->added_objectClass) {
+ ldb_msg_remove_attr(ares->message, "objectClass");
+ }
+
+ local_dn = ldb_dn_new(ac, ldb, LOCAL_BASE);
+ if ((local_dn == NULL) ||
+ (! ldb_dn_add_child_fmt(local_dn,
+ PASSWORD_GUID_ATTR "=%s",
+ GUID_string(ac, &objectGUID)))) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ lr = talloc_zero(ac, struct lpdb_reply);
+ if (lr == NULL) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ lr->local_dn = talloc_steal(lr, local_dn);
+ lr->remote = talloc_steal(lr, ares);
+
+ if (ac->list) {
+ ac->current->next = lr;
+ } else {
+ ac->list = lr;
+ }
+ ac->current= lr;
+
+ break;
+
+ case LDB_REPLY_REFERRAL:
+
+ return ldb_module_send_referral(ac->req, ares->referral);
+
+ case LDB_REPLY_DONE:
+
+ if (ac->list == NULL) {
+ /* found nothing */
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ lr = talloc_zero(ac, struct lpdb_reply);
+ if (lr == NULL) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ lr->remote = talloc_steal(lr, ares);
+
+ ac->current->next = lr;
+
+ /* rewind current and start local searches */
+ ac->current= ac->list;
+
+ ret = lpdb_local_search(ac);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+ }
+
+ return LDB_SUCCESS;
+}
+
+/* Search for passwords and other attributes. The passwords are
+ * local, but the other attributes are remote, and we need to glue the
+ * two search spaces back togeather */
+
+static int local_password_search(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct ldb_request *remote_req;
+ struct lpdb_context *ac;
+ int i;
+ int ret;
+ const char * const *search_attrs = NULL;
+
+ ldb = ldb_module_get_ctx(module);
+ ldb_debug(ldb, LDB_DEBUG_TRACE, "local_password_search\n");
+
+ if (ldb_dn_is_special(req->op.search.base)) { /* do not manipulate our control entries */
+ return ldb_next_request(module, req);
+ }
+
+ search_attrs = NULL;
+
+ /* If the caller is searching for the local passwords directly, let them pass */
+ if (ldb_dn_compare_base(ldb_dn_new(req, ldb, LOCAL_BASE),
+ req->op.search.base) == 0) {
+ return ldb_next_request(module, req);
+ }
+
+ if (req->op.search.attrs && (!ldb_attr_in_list(req->op.search.attrs, "*"))) {
+ for (i=0; i < ARRAY_SIZE(password_attrs); i++) {
+ if (ldb_attr_in_list(req->op.search.attrs, password_attrs[i])) {
+ break;
+ }
+ }
+
+ /* It didn't match any of our password attributes, go on */
+ if (i == ARRAY_SIZE(password_attrs)) {
+ return ldb_next_request(module, req);
+ }
+ }
+
+ ac = lpdb_init_context(module, req);
+ if (!ac) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* Remote search is for all attributes: if the remote LDAP server has these attributes, then it overrides the local database */
+ if (req->op.search.attrs && !ldb_attr_in_list(req->op.search.attrs, "*")) {
+ if (!ldb_attr_in_list(req->op.search.attrs, "objectGUID")) {
+ search_attrs = ldb_attr_list_copy_add(ac, req->op.search.attrs, "objectGUID");
+ ac->added_objectGUID = true;
+ if (!search_attrs) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ } else {
+ search_attrs = req->op.search.attrs;
+ }
+ if (!ldb_attr_in_list(search_attrs, "objectClass")) {
+ search_attrs = ldb_attr_list_copy_add(ac, search_attrs, "objectClass");
+ ac->added_objectClass = true;
+ if (!search_attrs) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ }
+ } else {
+ search_attrs = req->op.search.attrs;
+ }
+
+ ret = ldb_build_search_req_ex(&remote_req, ldb, ac,
+ req->op.search.base,
+ req->op.search.scope,
+ req->op.search.tree,
+ search_attrs,
+ req->controls,
+ ac, lpdb_remote_search_callback,
+ req);
+ if (ret != LDB_SUCCESS) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* perform the search */
+ return ldb_next_request(module, remote_req);
+}
+
+_PUBLIC_ const struct ldb_module_ops ldb_local_password_module_ops = {
+ .name = "local_password",
+ .add = local_password_add,
+ .modify = local_password_modify,
+ .del = local_password_delete,
+ .search = local_password_search
+};
diff --git a/source4/dsdb/samdb/ldb_modules/naming_fsmo.c b/source4/dsdb/samdb/ldb_modules/naming_fsmo.c
new file mode 100644
index 0000000000..607bf054d2
--- /dev/null
+++ b/source4/dsdb/samdb/ldb_modules/naming_fsmo.c
@@ -0,0 +1,122 @@
+/*
+ Unix SMB/CIFS mplementation.
+
+ The module that handles the Domain Naming FSMO Role Owner
+ checkings
+
+ 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 <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "includes.h"
+#include "ldb_module.h"
+#include "dsdb/samdb/samdb.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "librpc/gen_ndr/ndr_drsuapi.h"
+#include "librpc/gen_ndr/ndr_drsblobs.h"
+#include "../lib/util/dlinklist.h"
+
+static int naming_fsmo_init(struct ldb_module *module)
+{
+ struct ldb_context *ldb;
+ TALLOC_CTX *mem_ctx;
+ struct ldb_dn *naming_dn;
+ struct dsdb_naming_fsmo *naming_fsmo;
+ struct ldb_result *naming_res;
+ int ret;
+ static const char *naming_attrs[] = {
+ "fSMORoleOwner",
+ NULL
+ };
+
+ ldb = ldb_module_get_ctx(module);
+
+ mem_ctx = talloc_new(module);
+ if (!mem_ctx) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ naming_dn = samdb_partitions_dn(ldb, mem_ctx);
+ if (!naming_dn) {
+ ldb_debug(ldb, LDB_DEBUG_WARNING,
+ "naming_fsmo_init: no partitions dn present: (skip loading of naming contexts details)\n");
+ talloc_free(mem_ctx);
+ return ldb_next_init(module);
+ }
+
+ naming_fsmo = talloc_zero(mem_ctx, struct dsdb_naming_fsmo);
+ if (!naming_fsmo) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ldb_module_set_private(module, naming_fsmo);
+
+ ret = ldb_search(ldb, mem_ctx, &naming_res,
+ naming_dn, LDB_SCOPE_BASE,
+ naming_attrs, NULL);
+ if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+ ldb_debug(ldb, LDB_DEBUG_WARNING,
+ "naming_fsmo_init: no partitions dn present: (skip loading of naming contexts details)\n");
+ talloc_free(mem_ctx);
+ return ldb_next_init(module);
+ }
+ if (ret != LDB_SUCCESS) {
+ ldb_debug_set(ldb, LDB_DEBUG_FATAL,
+ "naming_fsmo_init: failed to search the cross-ref container: %s: %s",
+ ldb_strerror(ret), ldb_errstring(ldb));
+ talloc_free(mem_ctx);
+ return ret;
+ }
+ if (naming_res->count == 0) {
+ ldb_debug(ldb, LDB_DEBUG_WARNING,
+ "naming_fsmo_init: no cross-ref container present: (skip loading of naming contexts details)\n");
+ talloc_free(mem_ctx);
+ return ldb_next_init(module);
+ } else if (naming_res->count > 1) {
+ ldb_debug_set(ldb, LDB_DEBUG_FATAL,
+ "naming_fsmo_init: [%u] cross-ref containers found on a base search",
+ naming_res->count);
+ talloc_free(mem_ctx);
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+
+ naming_fsmo->master_dn = ldb_msg_find_attr_as_dn(ldb, naming_fsmo, naming_res->msgs[0], "fSMORoleOwner");
+ if (ldb_dn_compare(samdb_ntds_settings_dn(ldb), naming_fsmo->master_dn) == 0) {
+ naming_fsmo->we_are_master = true;
+ } else {
+ naming_fsmo->we_are_master = false;
+ }
+
+ if (ldb_set_opaque(ldb, "dsdb_naming_fsmo", naming_fsmo) != LDB_SUCCESS) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ talloc_steal(module, naming_fsmo);
+
+ ldb_debug(ldb, LDB_DEBUG_TRACE,
+ "naming_fsmo_init: we are master: %s\n",
+ (naming_fsmo->we_are_master?"yes":"no"));
+
+ talloc_free(mem_ctx);
+ return ldb_next_init(module);
+}
+
+_PUBLIC_ const struct ldb_module_ops ldb_naming_fsmo_module_ops = {
+ .name = "naming_fsmo",
+ .init_context = naming_fsmo_init
+};
diff --git a/source4/dsdb/samdb/ldb_modules/objectclass.c b/source4/dsdb/samdb/ldb_modules/objectclass.c
new file mode 100644
index 0000000000..898d913965
--- /dev/null
+++ b/source4/dsdb/samdb/ldb_modules/objectclass.c
@@ -0,0 +1,1073 @@
+/*
+ ldb database library
+
+ Copyright (C) Simo Sorce 2006-2008
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-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 <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: objectClass sorting module
+ *
+ * Description:
+ * - sort the objectClass attribute into the class
+ * hierarchy,
+ * - fix DNs and attributes into 'standard' case
+ * - Add objectCategory and ntSecurityDescriptor defaults
+ *
+ * Author: Andrew Bartlett
+ */
+
+
+#include "includes.h"
+#include "ldb_module.h"
+#include "dlinklist.h"
+#include "dsdb/samdb/samdb.h"
+#include "librpc/ndr/libndr.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "libcli/security/security.h"
+#include "auth/auth.h"
+#include "param/param.h"
+
+struct oc_context {
+
+ struct ldb_module *module;
+ struct ldb_request *req;
+
+ struct ldb_reply *search_res;
+
+ int (*step_fn)(struct oc_context *);
+};
+
+struct class_list {
+ struct class_list *prev, *next;
+ const struct dsdb_class *objectclass;
+};
+
+static struct oc_context *oc_init_context(struct ldb_module *module,
+ struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct oc_context *ac;
+
+ ldb = ldb_module_get_ctx(module);
+
+ ac = talloc_zero(req, struct oc_context);
+ if (ac == NULL) {
+ ldb_set_errstring(ldb, "Out of Memory");
+ return NULL;
+ }
+
+ ac->module = module;
+ ac->req = req;
+
+ return ac;
+}
+
+static int objectclass_do_add(struct oc_context *ac);
+
+/* Sort objectClasses into correct order, and validate that all
+ * objectClasses specified actually exist in the schema
+ */
+
+static int objectclass_sort(struct ldb_module *module,
+ const struct dsdb_schema *schema,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message_element *objectclass_element,
+ struct class_list **sorted_out)
+{
+ struct ldb_context *ldb;
+ int i;
+ int layer;
+ struct class_list *sorted = NULL, *parent_class = NULL,
+ *subclass = NULL, *unsorted = NULL, *current, *poss_subclass, *poss_parent, *new_parent;
+
+ ldb = ldb_module_get_ctx(module);
+
+ /* DESIGN:
+ *
+ * We work on 4 different 'bins' (implemented here as linked lists):
+ *
+ * * sorted: the eventual list, in the order we wish to push
+ * into the database. This is the only ordered list.
+ *
+ * * parent_class: The current parent class 'bin' we are
+ * trying to find subclasses for
+ *
+ * * subclass: The subclasses we have found so far
+ *
+ * * unsorted: The remaining objectClasses
+ *
+ * The process is a matter of filtering objectClasses up from
+ * unsorted into sorted. Order is irrelevent in the later 3 'bins'.
+ *
+ * We start with 'top' (found and promoted to parent_class
+ * initially). Then we find (in unsorted) all the direct
+ * subclasses of 'top'. parent_classes is concatenated onto
+ * the end of 'sorted', and subclass becomes the list in
+ * parent_class.
+ *
+ * We then repeat, until we find no more subclasses. Any left
+ * over classes are added to the end.
+ *
+ */
+
+ /* Firstly, dump all the objectClass elements into the
+ * unsorted bin, except for 'top', which is special */
+ for (i=0; i < objectclass_element->num_values; i++) {
+ current = talloc(mem_ctx, struct class_list);
+ if (!current) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ current->objectclass = dsdb_class_by_lDAPDisplayName(schema, (const char *)objectclass_element->values[i].data);
+ if (!current->objectclass) {
+ ldb_asprintf_errstring(ldb, "objectclass %s is not a valid objectClass in schema", (const char *)objectclass_element->values[i].data);
+ return LDB_ERR_OBJECT_CLASS_VIOLATION;
+ }
+
+ /* this is the root of the tree. We will start
+ * looking for subclasses from here */
+ if (ldb_attr_cmp("top", current->objectclass->lDAPDisplayName) == 0) {
+ DLIST_ADD_END(parent_class, current, struct class_list *);
+ } else {
+ DLIST_ADD_END(unsorted, current, struct class_list *);
+ }
+ }
+
+ if (parent_class == NULL) {
+ current = talloc(mem_ctx, struct class_list);
+ current->objectclass = dsdb_class_by_lDAPDisplayName(schema, "top");
+ DLIST_ADD_END(parent_class, current, struct class_list *);
+ }
+
+ /* For each object: find parent chain */
+ for (current = unsorted; schema && current; current = current->next) {
+ for (poss_parent = unsorted; poss_parent; poss_parent = poss_parent->next) {
+ if (ldb_attr_cmp(poss_parent->objectclass->lDAPDisplayName, current->objectclass->subClassOf) == 0) {
+ break;
+ }
+ }
+ /* If we didn't get to the end of the list, we need to add this parent */
+ if (poss_parent || (ldb_attr_cmp("top", current->objectclass->subClassOf) == 0)) {
+ continue;
+ }
+
+ new_parent = talloc(mem_ctx, struct class_list);
+ new_parent->objectclass = dsdb_class_by_lDAPDisplayName(schema, current->objectclass->subClassOf);
+ DLIST_ADD_END(unsorted, new_parent, struct class_list *);
+ }
+
+ /* DEBUGGING aid: how many layers are we down now? */
+ layer = 0;
+ do {
+ layer++;
+ /* Find all the subclasses of classes in the
+ * parent_classes. Push them onto the subclass list */
+
+ /* Ensure we don't bother if there are no unsorted entries left */
+ for (current = parent_class; schema && unsorted && current; current = current->next) {
+ /* Walk the list of possible subclasses in unsorted */
+ for (poss_subclass = unsorted; poss_subclass; ) {
+ struct class_list *next;
+
+ /* Save the next pointer, as the DLIST_ macros will change poss_subclass->next */
+ next = poss_subclass->next;
+
+ if (ldb_attr_cmp(poss_subclass->objectclass->subClassOf, current->objectclass->lDAPDisplayName) == 0) {
+ DLIST_REMOVE(unsorted, poss_subclass);
+ DLIST_ADD(subclass, poss_subclass);
+
+ break;
+ }
+ poss_subclass = next;
+ }
+ }
+
+ /* Now push the parent_classes as sorted, we are done with
+ these. Add to the END of the list by concatenation */
+ DLIST_CONCATENATE(sorted, parent_class, struct class_list *);
+
+ /* and now find subclasses of these */
+ parent_class = subclass;
+ subclass = NULL;
+
+ /* If we didn't find any subclasses we will fall out
+ * the bottom here */
+ } while (parent_class);
+
+ if (!unsorted) {
+ *sorted_out = sorted;
+ return LDB_SUCCESS;
+ }
+
+ if (!schema) {
+ /* If we don't have schema yet, then just merge the lists again */
+ DLIST_CONCATENATE(sorted, unsorted, struct class_list *);
+ *sorted_out = sorted;
+ return LDB_SUCCESS;
+ }
+
+ /* This shouldn't happen, and would break MMC, perhaps there
+ * was no 'top', a conflict in the objectClasses or some other
+ * schema error?
+ */
+ ldb_asprintf_errstring(ldb, "objectclass %s is not a valid objectClass in objectClass chain", unsorted->objectclass->lDAPDisplayName);
+ return LDB_ERR_OBJECT_CLASS_VIOLATION;
+}
+
+static DATA_BLOB *get_sd(struct ldb_module *module, TALLOC_CTX *mem_ctx,
+ const struct dsdb_class *objectclass)
+{
+ struct ldb_context *ldb = ldb_module_get_ctx(module);
+ enum ndr_err_code ndr_err;
+ DATA_BLOB *linear_sd;
+ struct auth_session_info *session_info
+ = ldb_get_opaque(ldb, "sessionInfo");
+ struct security_descriptor *sd;
+ const struct dom_sid *domain_sid = samdb_domain_sid(ldb);
+
+ if (!objectclass->defaultSecurityDescriptor || !domain_sid) {
+ return NULL;
+ }
+
+ sd = sddl_decode(mem_ctx,
+ objectclass->defaultSecurityDescriptor,
+ domain_sid);
+
+ if (!sd || !session_info || !session_info->security_token) {
+ return NULL;
+ }
+
+ sd->owner_sid = session_info->security_token->user_sid;
+ sd->group_sid = session_info->security_token->group_sid;
+
+ linear_sd = talloc(mem_ctx, DATA_BLOB);
+ if (!linear_sd) {
+ return NULL;
+ }
+
+ ndr_err = ndr_push_struct_blob(linear_sd, mem_ctx,
+ lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
+ sd,
+ (ndr_push_flags_fn_t)ndr_push_security_descriptor);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return NULL;
+ }
+
+ return linear_sd;
+
+}
+
+static int get_search_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+ struct ldb_context *ldb;
+ struct oc_context *ac;
+ int ret;
+
+ ac = talloc_get_type(req->context, struct oc_context);
+ ldb = ldb_module_get_ctx(ac->module);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS &&
+ ares->error != LDB_ERR_NO_SUCH_OBJECT) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+ if (ac->search_res != NULL) {
+ ldb_set_errstring(ldb, "Too many results");
+ talloc_free(ares);
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ ac->search_res = talloc_steal(ac, ares);
+ break;
+
+ case LDB_REPLY_REFERRAL:
+ /* ignore */
+ talloc_free(ares);
+ break;
+
+ case LDB_REPLY_DONE:
+ talloc_free(ares);
+ ret = ac->step_fn(ac);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+ break;
+ }
+
+ return LDB_SUCCESS;
+}
+
+static int oc_op_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+ struct oc_context *ac;
+
+ ac = talloc_get_type(req->context, struct oc_context);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ if (ares->type != LDB_REPLY_DONE) {
+ talloc_free(ares);
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+}
+
+/* Fix up the DN to be in the standard form, taking particular care to match the parent DN
+
+ This should mean that if the parent is:
+ CN=Users,DC=samba,DC=example,DC=com
+ and a proposed child is
+ cn=Admins ,cn=USERS,dc=Samba,dc=example,dc=COM
+
+ The resulting DN should be:
+
+ CN=Admins,CN=Users,DC=samba,DC=example,DC=com
+
+ */
+static int fix_dn(TALLOC_CTX *mem_ctx,
+ struct ldb_dn *newdn, struct ldb_dn *parent_dn,
+ struct ldb_dn **fixed_dn)
+{
+ char *upper_rdn_attr;
+ /* Fix up the DN to be in the standard form, taking particular care to match the parent DN */
+ *fixed_dn = ldb_dn_copy(mem_ctx, parent_dn);
+
+ /* We need the attribute name in upper case */
+ upper_rdn_attr = strupper_talloc(*fixed_dn,
+ ldb_dn_get_rdn_name(newdn));
+ if (!upper_rdn_attr) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* Create a new child */
+ if (ldb_dn_add_child_fmt(*fixed_dn, "X=X") == false) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* And replace it with CN=foo (we need the attribute in upper case */
+ return ldb_dn_set_component(*fixed_dn, 0, upper_rdn_attr,
+ *ldb_dn_get_rdn_val(newdn));
+}
+
+/* Fix all attribute names to be in the correct case, and check they are all valid per the schema */
+static int fix_attributes(struct ldb_context *ldb, const struct dsdb_schema *schema, struct ldb_message *msg)
+{
+ int i;
+ for (i=0; i < msg->num_elements; i++) {
+ const struct dsdb_attribute *attribute = dsdb_attribute_by_lDAPDisplayName(schema, msg->elements[i].name);
+ /* Add in a very special case for 'clearTextPassword',
+ * which is used for internal processing only, and is
+ * not presented in the schema */
+ if (!attribute) {
+ if (strcasecmp(msg->elements[i].name, "clearTextPassword") != 0) {
+ ldb_asprintf_errstring(ldb, "attribute %s is not a valid attribute in schema", msg->elements[i].name);
+ return LDB_ERR_UNDEFINED_ATTRIBUTE_TYPE;
+ }
+ } else {
+ msg->elements[i].name = attribute->lDAPDisplayName;
+ }
+ }
+
+ return LDB_SUCCESS;
+}
+
+static int objectclass_do_add(struct oc_context *ac);
+
+static int objectclass_add(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct ldb_request *search_req;
+ struct oc_context *ac;
+ struct ldb_dn *parent_dn;
+ int ret;
+
+ ldb = ldb_module_get_ctx(module);
+
+ ldb_debug(ldb, LDB_DEBUG_TRACE, "objectclass_add\n");
+
+ /* do not manipulate our control entries */
+ if (ldb_dn_is_special(req->op.add.message->dn)) {
+ return ldb_next_request(module, req);
+ }
+
+ /* the objectClass must be specified on add */
+ if (ldb_msg_find_element(req->op.add.message,
+ "objectClass") == NULL) {
+ return LDB_ERR_OBJECT_CLASS_VIOLATION;
+ }
+
+ ac = oc_init_context(module, req);
+ if (ac == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* If there isn't a parent, just go on to the add processing */
+ if (ldb_dn_get_comp_num(ac->req->op.add.message->dn) == 1) {
+ return objectclass_do_add(ac);
+ }
+
+ /* get copy of parent DN */
+ parent_dn = ldb_dn_get_parent(ac, ac->req->op.add.message->dn);
+ if (parent_dn == NULL) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ldb_build_search_req(&search_req, ldb,
+ ac, parent_dn, LDB_SCOPE_BASE,
+ "(objectClass=*)", NULL,
+ NULL,
+ ac, get_search_callback,
+ req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ talloc_steal(search_req, parent_dn);
+
+ ac->step_fn = objectclass_do_add;
+
+ return ldb_next_request(ac->module, search_req);
+}
+
+static int objectclass_do_add(struct oc_context *ac)
+{
+ struct ldb_context *ldb;
+ const struct dsdb_schema *schema;
+ struct ldb_request *add_req;
+ char *value;
+ struct ldb_message_element *objectclass_element;
+ struct ldb_message *msg;
+ TALLOC_CTX *mem_ctx;
+ struct class_list *sorted, *current;
+ int ret;
+
+ ldb = ldb_module_get_ctx(ac->module);
+ schema = dsdb_get_schema(ldb);
+
+ mem_ctx = talloc_new(ac);
+ if (mem_ctx == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ msg = ldb_msg_copy_shallow(ac, ac->req->op.add.message);
+
+ /* Check we have a valid parent */
+ if (ac->search_res == NULL) {
+ if (ldb_dn_compare(ldb_get_root_basedn(ldb),
+ msg->dn) == 0) {
+ /* Allow the tree to be started */
+
+ /* but don't keep any error string, it's meaningless */
+ ldb_set_errstring(ldb, NULL);
+ } else {
+ ldb_asprintf_errstring(ldb, "objectclass: Cannot add %s, parent does not exist!",
+ ldb_dn_get_linearized(msg->dn));
+ talloc_free(mem_ctx);
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+ } else {
+
+ /* Fix up the DN to be in the standard form, taking particular care to match the parent DN */
+ ret = fix_dn(msg,
+ ac->req->op.add.message->dn,
+ ac->search_res->message->dn,
+ &msg->dn);
+
+ if (ret != LDB_SUCCESS) {
+ ldb_asprintf_errstring(ldb, "Could not munge DN %s into normal form",
+ ldb_dn_get_linearized(ac->req->op.add.message->dn));
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ /* TODO: Check this is a valid child to this parent,
+ * by reading the allowedChildClasses and
+ * allowedChildClasssesEffective attributes */
+
+ }
+
+ if (schema) {
+ ret = fix_attributes(ldb, schema, msg);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ /* This is now the objectClass list from the database */
+ objectclass_element = ldb_msg_find_element(msg, "objectClass");
+
+ if (!objectclass_element) {
+ /* Where did it go? bail now... */
+ talloc_free(mem_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ret = objectclass_sort(ac->module, schema, mem_ctx, objectclass_element, &sorted);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ ldb_msg_remove_attr(msg, "objectClass");
+ ret = ldb_msg_add_empty(msg, "objectClass", 0, NULL);
+
+ if (ret != LDB_SUCCESS) {
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ /* We must completely replace the existing objectClass entry,
+ * because we need it sorted */
+
+ /* Move from the linked list back into an ldb msg */
+ for (current = sorted; current; current = current->next) {
+ value = talloc_strdup(msg, current->objectclass->lDAPDisplayName);
+ if (value == NULL) {
+ ldb_oom(ldb);
+ talloc_free(mem_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ret = ldb_msg_add_string(msg, "objectClass", value);
+ if (ret != LDB_SUCCESS) {
+ ldb_set_errstring(ldb,
+ "objectclass: could not re-add sorted "
+ "objectclass to modify msg");
+ talloc_free(mem_ctx);
+ return ret;
+ }
+ /* Last one is the critical one */
+ if (!current->next) {
+ struct ldb_message_element *el;
+ int32_t systemFlags = 0;
+ if (!ldb_msg_find_element(msg, "objectCategory")) {
+ value = talloc_strdup(msg, current->objectclass->defaultObjectCategory);
+ if (value == NULL) {
+ ldb_oom(ldb);
+ talloc_free(mem_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ldb_msg_add_string(msg, "objectCategory", value);
+ }
+ if (!ldb_msg_find_element(msg, "showInAdvancedViewOnly") && (current->objectclass->defaultHidingValue == true)) {
+ ldb_msg_add_string(msg, "showInAdvancedViewOnly",
+ "TRUE");
+ }
+ if (!ldb_msg_find_element(msg, "nTSecurityDescriptor")) {
+ DATA_BLOB *sd = get_sd(ac->module, mem_ctx, current->objectclass);
+ if (sd) {
+ ldb_msg_add_steal_value(msg, "nTSecurityDescriptor", sd);
+ }
+ }
+
+ /* There are very special rules for systemFlags, see MS-ADTS 3.1.1.5.2.4 */
+ el = ldb_msg_find_element(msg, "systemFlags");
+
+ systemFlags = ldb_msg_find_attr_as_int(msg, "systemFlags", 0);
+
+ if (el) {
+ /* Only these flags may be set by a client, but we can't tell between a client and our provision at this point */
+ /* systemFlags &= ( SYSTEM_FLAG_CONFIG_ALLOW_RENAME | SYSTEM_FLAG_CONFIG_ALLOW_MOVE | SYSTEM_FLAG_CONFIG_LIMITED_MOVE); */
+ ldb_msg_remove_element(msg, el);
+ }
+
+ /* This flag is only allowed on attributeSchema objects */
+ if (ldb_attr_cmp(current->objectclass->lDAPDisplayName, "attributeSchema") == 0) {
+ systemFlags &= ~SYSTEM_FLAG_ATTR_IS_RDN;
+ }
+
+ if (ldb_attr_cmp(current->objectclass->lDAPDisplayName, "server") == 0) {
+ systemFlags |= (int32_t)(SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE | SYSTEM_FLAG_CONFIG_ALLOW_RENAME | SYSTEM_FLAG_CONFIG_ALLOW_LIMITED_MOVE);
+ } else if (ldb_attr_cmp(current->objectclass->lDAPDisplayName, "site") == 0
+ || ldb_attr_cmp(current->objectclass->lDAPDisplayName, "serverContainer") == 0
+ || ldb_attr_cmp(current->objectclass->lDAPDisplayName, "ntDSDSA") == 0) {
+ systemFlags |= (int32_t)(SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE);
+
+ } else if (ldb_attr_cmp(current->objectclass->lDAPDisplayName, "siteLink") == 0
+ || ldb_attr_cmp(current->objectclass->lDAPDisplayName, "siteLinkBridge") == 0
+ || ldb_attr_cmp(current->objectclass->lDAPDisplayName, "nTDSConnection") == 0) {
+ systemFlags |= (int32_t)(SYSTEM_FLAG_CONFIG_ALLOW_RENAME);
+ }
+
+ /* TODO: If parent object is site or subnet, also add (SYSTEM_FLAG_CONFIG_ALLOW_RENAME) */
+
+ if (el || systemFlags != 0) {
+ samdb_msg_add_int(ldb, msg, msg, "systemFlags", systemFlags);
+ }
+ }
+ }
+ }
+
+ talloc_free(mem_ctx);
+ ret = ldb_msg_sanity_check(ldb, msg);
+
+
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ ret = ldb_build_add_req(&add_req, ldb, ac,
+ msg,
+ ac->req->controls,
+ ac, oc_op_callback,
+ ac->req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ /* perform the add */
+ return ldb_next_request(ac->module, add_req);
+}
+
+static int oc_modify_callback(struct ldb_request *req,
+ struct ldb_reply *ares);
+static int objectclass_do_mod(struct oc_context *ac);
+
+static int objectclass_modify(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb = ldb_module_get_ctx(module);
+ struct ldb_message_element *objectclass_element;
+ struct ldb_message *msg;
+ const struct dsdb_schema *schema = dsdb_get_schema(ldb);
+ struct class_list *sorted, *current;
+ struct ldb_request *down_req;
+ struct oc_context *ac;
+ TALLOC_CTX *mem_ctx;
+ char *value;
+ int ret;
+
+ ldb_debug(ldb, LDB_DEBUG_TRACE, "objectclass_modify\n");
+
+ /* do not manipulate our control entries */
+ if (ldb_dn_is_special(req->op.mod.message->dn)) {
+ return ldb_next_request(module, req);
+ }
+
+ /* Without schema, there isn't much to do here */
+ if (!schema) {
+ return ldb_next_request(module, req);
+ }
+ objectclass_element = ldb_msg_find_element(req->op.mod.message, "objectClass");
+
+ ac = oc_init_context(module, req);
+ if (ac == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* If no part of this touches the objectClass, then we don't
+ * need to make any changes. */
+
+ /* If the only operation is the deletion of the objectClass
+ * then go on with just fixing the attribute case */
+ if (!objectclass_element) {
+ msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
+ if (msg == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = fix_attributes(ldb, schema, msg);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ ret = ldb_build_mod_req(&down_req, ldb, ac,
+ msg,
+ req->controls,
+ ac, oc_op_callback,
+ req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ /* go on with the call chain */
+ return ldb_next_request(module, down_req);
+ }
+
+ switch (objectclass_element->flags & LDB_FLAG_MOD_MASK) {
+ case LDB_FLAG_MOD_DELETE:
+ if (objectclass_element->num_values == 0) {
+ return LDB_ERR_OBJECT_CLASS_MODS_PROHIBITED;
+ }
+ break;
+
+ case LDB_FLAG_MOD_REPLACE:
+ mem_ctx = talloc_new(ac);
+ if (mem_ctx == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
+ if (msg == NULL) {
+ talloc_free(mem_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = fix_attributes(ldb, schema, msg);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ ret = objectclass_sort(module, schema, mem_ctx, objectclass_element, &sorted);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ /* We must completely replace the existing objectClass entry,
+ * because we need it sorted */
+
+ ldb_msg_remove_attr(msg, "objectClass");
+ ret = ldb_msg_add_empty(msg, "objectClass", LDB_FLAG_MOD_REPLACE, NULL);
+
+ if (ret != LDB_SUCCESS) {
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ /* Move from the linked list back into an ldb msg */
+ for (current = sorted; current; current = current->next) {
+ /* copy the value as this string is on the schema
+ * context and we can't rely on it not changing
+ * before the operation is over */
+ value = talloc_strdup(msg,
+ current->objectclass->lDAPDisplayName);
+ if (value == NULL) {
+ ldb_oom(ldb);
+ talloc_free(mem_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ret = ldb_msg_add_string(msg, "objectClass", value);
+ if (ret != LDB_SUCCESS) {
+ ldb_set_errstring(ldb,
+ "objectclass: could not re-add sorted "
+ "objectclass to modify msg");
+ talloc_free(mem_ctx);
+ return ret;
+ }
+ }
+
+ talloc_free(mem_ctx);
+
+ ret = ldb_msg_sanity_check(ldb, msg);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ ret = ldb_build_mod_req(&down_req, ldb, ac,
+ msg,
+ req->controls,
+ ac, oc_op_callback,
+ req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ /* go on with the call chain */
+ return ldb_next_request(module, down_req);
+ }
+
+ /* This isn't the default branch of the switch, but a 'in any
+ * other case'. When a delete isn't for all objectClasses for
+ * example
+ */
+
+ msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
+ if (msg == NULL) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = fix_attributes(ldb, schema, msg);
+ if (ret != LDB_SUCCESS) {
+ ldb_oom(ldb);
+ return ret;
+ }
+
+ ret = ldb_build_mod_req(&down_req, ldb, ac,
+ msg,
+ req->controls,
+ ac, oc_modify_callback,
+ req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ return ldb_next_request(module, down_req);
+}
+
+static int oc_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+ struct ldb_context *ldb;
+ static const char * const attrs[] = { "objectClass", NULL };
+ struct ldb_request *search_req;
+ struct oc_context *ac;
+ int ret;
+
+ ac = talloc_get_type(req->context, struct oc_context);
+ ldb = ldb_module_get_ctx(ac->module);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ if (ares->type != LDB_REPLY_DONE) {
+ talloc_free(ares);
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ ret = ldb_build_search_req(&search_req, ldb, ac,
+ ac->req->op.mod.message->dn, LDB_SCOPE_BASE,
+ "(objectClass=*)",
+ attrs, NULL,
+ ac, get_search_callback,
+ ac->req);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+
+ ac->step_fn = objectclass_do_mod;
+
+ ret = ldb_next_request(ac->module, search_req);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+ return LDB_SUCCESS;
+}
+
+static int objectclass_do_mod(struct oc_context *ac)
+{
+ struct ldb_context *ldb;
+ const struct dsdb_schema *schema;
+ struct ldb_request *mod_req;
+ char *value;
+ struct ldb_message_element *objectclass_element;
+ struct ldb_message *msg;
+ TALLOC_CTX *mem_ctx;
+ struct class_list *sorted, *current;
+ int ret;
+
+ ldb = ldb_module_get_ctx(ac->module);
+
+ if (ac->search_res == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ schema = dsdb_get_schema(ldb);
+
+ mem_ctx = talloc_new(ac);
+ if (mem_ctx == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* use a new message structure */
+ msg = ldb_msg_new(ac);
+ if (msg == NULL) {
+ ldb_set_errstring(ldb,
+ "objectclass: could not create new modify msg");
+ talloc_free(mem_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* This is now the objectClass list from the database */
+ objectclass_element = ldb_msg_find_element(ac->search_res->message,
+ "objectClass");
+ if (!objectclass_element) {
+ /* Where did it go? bail now... */
+ talloc_free(mem_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* modify dn */
+ msg->dn = ac->req->op.mod.message->dn;
+
+ ret = objectclass_sort(ac->module, schema, mem_ctx, objectclass_element, &sorted);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ /* We must completely replace the existing objectClass entry.
+ * We could do a constrained add/del, but we are meant to be
+ * in a transaction... */
+
+ ret = ldb_msg_add_empty(msg, "objectClass", LDB_FLAG_MOD_REPLACE, NULL);
+ if (ret != LDB_SUCCESS) {
+ ldb_set_errstring(ldb, "objectclass: could not clear objectclass in modify msg");
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ /* Move from the linked list back into an ldb msg */
+ for (current = sorted; current; current = current->next) {
+ value = talloc_strdup(msg, current->objectclass->lDAPDisplayName);
+ if (value == NULL) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ret = ldb_msg_add_string(msg, "objectClass", value);
+ if (ret != LDB_SUCCESS) {
+ ldb_set_errstring(ldb, "objectclass: could not re-add sorted objectclass to modify msg");
+ talloc_free(mem_ctx);
+ return ret;
+ }
+ }
+
+ ret = ldb_msg_sanity_check(ldb, msg);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ ret = ldb_build_mod_req(&mod_req, ldb, ac,
+ msg,
+ ac->req->controls,
+ ac, oc_op_callback,
+ ac->req);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ talloc_free(mem_ctx);
+ /* perform the modify */
+ return ldb_next_request(ac->module, mod_req);
+}
+
+static int objectclass_do_rename(struct oc_context *ac);
+
+static int objectclass_rename(struct ldb_module *module, struct ldb_request *req)
+{
+ static const char * const attrs[] = { NULL };
+ struct ldb_context *ldb;
+ struct ldb_request *search_req;
+ struct oc_context *ac;
+ struct ldb_dn *parent_dn;
+ int ret;
+
+ ldb = ldb_module_get_ctx(module);
+
+ ldb_debug(ldb, LDB_DEBUG_TRACE, "objectclass_rename\n");
+
+ if (ldb_dn_is_special(req->op.rename.newdn)) { /* do not manipulate our control entries */
+ return ldb_next_request(module, req);
+ }
+
+ /* Firstly ensure we are not trying to rename it to be a child of itself */
+ if ((ldb_dn_compare_base(req->op.rename.olddn, req->op.rename.newdn) == 0)
+ && (ldb_dn_compare(req->op.rename.olddn, req->op.rename.newdn) != 0)) {
+ ldb_asprintf_errstring(ldb, "Cannot rename %s to be a child of itself",
+ ldb_dn_get_linearized(req->op.rename.olddn));
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+
+ ac = oc_init_context(module, req);
+ if (ac == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ parent_dn = ldb_dn_get_parent(ac, req->op.rename.newdn);
+ if (parent_dn == NULL) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ret = ldb_build_search_req(&search_req, ldb,
+ ac, parent_dn, LDB_SCOPE_BASE,
+ "(objectClass=*)",
+ attrs, NULL,
+ ac, get_search_callback,
+ req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ ac->step_fn = objectclass_do_rename;
+
+ return ldb_next_request(ac->module, search_req);
+}
+
+static int objectclass_do_rename(struct oc_context *ac)
+{
+ struct ldb_context *ldb;
+ struct ldb_request *rename_req;
+ struct ldb_dn *fixed_dn;
+ int ret;
+
+ ldb = ldb_module_get_ctx(ac->module);
+
+ /* Check we have a valid parent */
+ if (ac->search_res == NULL) {
+ ldb_asprintf_errstring(ldb, "objectclass: Cannot rename %s, parent does not exist!",
+ ldb_dn_get_linearized(ac->req->op.rename.newdn));
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+
+ /* Fix up the DN to be in the standard form,
+ * taking particular care to match the parent DN */
+ ret = fix_dn(ac,
+ ac->req->op.rename.newdn,
+ ac->search_res->message->dn,
+ &fixed_dn);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ /* TODO: Check this is a valid child to this parent,
+ * by reading the allowedChildClasses and
+ * allowedChildClasssesEffective attributes */
+
+ ret = ldb_build_rename_req(&rename_req, ldb, ac,
+ ac->req->op.rename.olddn, fixed_dn,
+ ac->req->controls,
+ ac, oc_op_callback,
+ ac->req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ /* perform the rename */
+ return ldb_next_request(ac->module, rename_req);
+}
+
+_PUBLIC_ const struct ldb_module_ops ldb_objectclass_module_ops = {
+ .name = "objectclass",
+ .add = objectclass_add,
+ .modify = objectclass_modify,
+ .rename = objectclass_rename,
+};
diff --git a/source4/dsdb/samdb/ldb_modules/objectguid.c b/source4/dsdb/samdb/ldb_modules/objectguid.c
new file mode 100644
index 0000000000..46ba8ebdb3
--- /dev/null
+++ b/source4/dsdb/samdb/ldb_modules/objectguid.c
@@ -0,0 +1,285 @@
+/*
+ ldb database library
+
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
+ Copyright (C) Andrew Tridgell 2005
+ Copyright (C) Simo Sorce 2004-2008
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb objectguid module
+ *
+ * Description: add a unique objectGUID onto every new record
+ *
+ * Author: Simo Sorce
+ */
+
+#include "includes.h"
+#include "ldb_module.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "param/param.h"
+
+static struct ldb_message_element *objectguid_find_attribute(const struct ldb_message *msg, const char *name)
+{
+ int i;
+
+ for (i = 0; i < msg->num_elements; i++) {
+ if (ldb_attr_cmp(name, msg->elements[i].name) == 0) {
+ return &msg->elements[i];
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ add a time element to a record
+*/
+static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
+{
+ struct ldb_message_element *el;
+ char *s;
+
+ if (ldb_msg_find_element(msg, attr) != NULL) {
+ return 0;
+ }
+
+ s = ldb_timestring(msg, t);
+ if (s == NULL) {
+ return -1;
+ }
+
+ if (ldb_msg_add_string(msg, attr, s) != 0) {
+ return -1;
+ }
+
+ el = ldb_msg_find_element(msg, attr);
+ /* always set as replace. This works because on add ops, the flag
+ is ignored */
+ el->flags = LDB_FLAG_MOD_REPLACE;
+
+ return 0;
+}
+
+/*
+ add a uint64_t element to a record
+*/
+static int add_uint64_element(struct ldb_message *msg, const char *attr, uint64_t v)
+{
+ struct ldb_message_element *el;
+
+ if (ldb_msg_find_element(msg, attr) != NULL) {
+ return 0;
+ }
+
+ if (ldb_msg_add_fmt(msg, attr, "%llu", (unsigned long long)v) != 0) {
+ return -1;
+ }
+
+ el = ldb_msg_find_element(msg, attr);
+ /* always set as replace. This works because on add ops, the flag
+ is ignored */
+ el->flags = LDB_FLAG_MOD_REPLACE;
+
+ return 0;
+}
+
+struct og_context {
+ struct ldb_module *module;
+ struct ldb_request *req;
+};
+
+static int og_op_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+ struct og_context *ac;
+
+ ac = talloc_get_type(req->context, struct og_context);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ if (ares->type != LDB_REPLY_DONE) {
+ talloc_free(ares);
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+}
+
+/* add_record: add objectGUID attribute */
+static int objectguid_add(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct ldb_request *down_req;
+ struct ldb_message_element *attribute;
+ struct ldb_message *msg;
+ struct ldb_val v;
+ struct GUID guid;
+ uint64_t seq_num;
+ enum ndr_err_code ndr_err;
+ int ret;
+ time_t t = time(NULL);
+ struct og_context *ac;
+
+ ldb = ldb_module_get_ctx(module);
+
+ ldb_debug(ldb, LDB_DEBUG_TRACE, "objectguid_add_record\n");
+
+ /* do not manipulate our control entries */
+ if (ldb_dn_is_special(req->op.add.message->dn)) {
+ return ldb_next_request(module, req);
+ }
+
+ if ((attribute = objectguid_find_attribute(req->op.add.message, "objectGUID")) != NULL ) {
+ return ldb_next_request(module, req);
+ }
+
+ ac = talloc(req, struct og_context);
+ if (ac == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ac->module = module;
+ ac->req = req;
+
+ /* we have to copy the message as the caller might have it as a const */
+ msg = ldb_msg_copy_shallow(ac, req->op.add.message);
+ if (msg == NULL) {
+ talloc_free(down_req);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* a new GUID */
+ guid = GUID_random();
+
+ ndr_err = ndr_push_struct_blob(&v, msg,
+ lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
+ &guid,
+ (ndr_push_flags_fn_t)ndr_push_GUID);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ldb_msg_add_value(msg, "objectGUID", &v, NULL);
+ if (ret) {
+ return ret;
+ }
+
+ if (add_time_element(msg, "whenCreated", t) != 0 ||
+ add_time_element(msg, "whenChanged", t) != 0) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* Get a sequence number from the backend */
+ /* FIXME: ldb_sequence_number is a semi-async call,
+ * make sure this function is split and a callback is used */
+ ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
+ if (ret == LDB_SUCCESS) {
+ if (add_uint64_element(msg, "uSNCreated", seq_num) != 0 ||
+ add_uint64_element(msg, "uSNChanged", seq_num) != 0) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ }
+
+ ret = ldb_build_add_req(&down_req, ldb, ac,
+ msg,
+ req->controls,
+ ac, og_op_callback,
+ req);
+ if (ret != LDB_SUCCESS) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* go on with the call chain */
+ return ldb_next_request(module, down_req);
+}
+
+/* modify_record: update timestamps */
+static int objectguid_modify(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct ldb_request *down_req;
+ struct ldb_message *msg;
+ int ret;
+ time_t t = time(NULL);
+ uint64_t seq_num;
+ struct og_context *ac;
+
+ ldb = ldb_module_get_ctx(module);
+
+ ldb_debug(ldb, LDB_DEBUG_TRACE, "objectguid_add_record\n");
+
+ /* do not manipulate our control entries */
+ if (ldb_dn_is_special(req->op.add.message->dn)) {
+ return ldb_next_request(module, req);
+ }
+
+ ac = talloc(req, struct og_context);
+ if (ac == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ac->module = module;
+ ac->req = req;
+
+ /* we have to copy the message as the caller might have it as a const */
+ msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
+ if (msg == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if (add_time_element(msg, "whenChanged", t) != 0) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* Get a sequence number from the backend */
+ ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
+ if (ret == LDB_SUCCESS) {
+ if (add_uint64_element(msg, "uSNChanged", seq_num) != 0) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ }
+
+ ret = ldb_build_mod_req(&down_req, ldb, ac,
+ msg,
+ req->controls,
+ ac, og_op_callback,
+ req);
+ if (ret != LDB_SUCCESS) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* go on with the call chain */
+ return ldb_next_request(module, down_req);
+}
+
+_PUBLIC_ const struct ldb_module_ops ldb_objectguid_module_ops = {
+ .name = "objectguid",
+ .add = objectguid_add,
+ .modify = objectguid_modify,
+};
diff --git a/source4/dsdb/samdb/ldb_modules/partition.c b/source4/dsdb/samdb/ldb_modules/partition.c
new file mode 100644
index 0000000000..71a1b8e942
--- /dev/null
+++ b/source4/dsdb/samdb/ldb_modules/partition.c
@@ -0,0 +1,1345 @@
+/*
+ Partitions ldb module
+
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2006
+ Copyright (C) Stefan Metzmacher <metze@samba.org> 2007
+
+ * NOTICE: this module is NOT released under the GNU LGPL license as
+ * other ldb code. This module is release under the GNU GPL v3 or
+ * later license.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb partitions module
+ *
+ * Description: Implement LDAP partitions
+ *
+ * Author: Andrew Bartlett
+ * Author: Stefan Metzmacher
+ */
+
+#include "includes.h"
+#include "ldb_private.h"
+#include "dsdb/samdb/samdb.h"
+
+struct partition_private_data {
+ struct dsdb_control_current_partition **partitions;
+ struct ldb_dn **replicate;
+};
+
+struct part_request {
+ struct ldb_module *module;
+ struct ldb_request *req;
+};
+
+struct partition_context {
+ struct ldb_module *module;
+ struct ldb_request *req;
+ bool got_success;
+
+ struct part_request *part_req;
+ int num_requests;
+ int finished_requests;
+};
+
+static struct partition_context *partition_init_ctx(struct ldb_module *module, struct ldb_request *req)
+{
+ struct partition_context *ac;
+
+ ac = talloc_zero(req, struct partition_context);
+ if (ac == NULL) {
+ ldb_set_errstring(module->ldb, "Out of Memory");
+ return NULL;
+ }
+
+ ac->module = module;
+ ac->req = req;
+
+ return ac;
+}
+
+#define PARTITION_FIND_OP(module, op) do { \
+ struct ldb_context *ldbctx = module->ldb; \
+ while (module && module->ops->op == NULL) module = module->next; \
+ if (module == NULL) { \
+ ldb_asprintf_errstring(ldbctx, \
+ "Unable to find backend operation for " #op ); \
+ return LDB_ERR_OPERATIONS_ERROR; \
+ } \
+} while (0)
+
+/*
+ * helper functions to call the next module in chain
+ * */
+
+static int partition_request(struct ldb_module *module, struct ldb_request *request)
+{
+ int ret;
+ switch (request->operation) {
+ case LDB_SEARCH:
+ PARTITION_FIND_OP(module, search);
+ ret = module->ops->search(module, request);
+ break;
+ case LDB_ADD:
+ PARTITION_FIND_OP(module, add);
+ ret = module->ops->add(module, request);
+ break;
+ case LDB_MODIFY:
+ PARTITION_FIND_OP(module, modify);
+ ret = module->ops->modify(module, request);
+ break;
+ case LDB_DELETE:
+ PARTITION_FIND_OP(module, del);
+ ret = module->ops->del(module, request);
+ break;
+ case LDB_RENAME:
+ PARTITION_FIND_OP(module, rename);
+ ret = module->ops->rename(module, request);
+ break;
+ case LDB_EXTENDED:
+ PARTITION_FIND_OP(module, extended);
+ ret = module->ops->extended(module, request);
+ break;
+ default:
+ PARTITION_FIND_OP(module, request);
+ ret = module->ops->request(module, request);
+ break;
+ }
+ if (ret == LDB_SUCCESS) {
+ return ret;
+ }
+ if (!ldb_errstring(module->ldb)) {
+ /* Set a default error string, to place the blame somewhere */
+ ldb_asprintf_errstring(module->ldb,
+ "error in module %s: %s (%d)",
+ module->ops->name,
+ ldb_strerror(ret), ret);
+ }
+ return ret;
+}
+
+static struct dsdb_control_current_partition *find_partition(struct partition_private_data *data,
+ struct ldb_dn *dn)
+{
+ int i;
+
+ /* Look at base DN */
+ /* Figure out which partition it is under */
+ /* Skip the lot if 'data' isn't here yet (initialistion) */
+ for (i=0; data && data->partitions && data->partitions[i]; i++) {
+ if (ldb_dn_compare_base(data->partitions[i]->dn, dn) == 0) {
+ return data->partitions[i];
+ }
+ }
+
+ return NULL;
+};
+
+/**
+ * fire the caller's callback for every entry, but only send 'done' once.
+ */
+static int partition_req_callback(struct ldb_request *req,
+ struct ldb_reply *ares)
+{
+ struct partition_context *ac;
+ struct ldb_module *module;
+ struct ldb_request *nreq;
+ int ret;
+
+ ac = talloc_get_type(req->context, struct partition_context);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ if (ares->error != LDB_SUCCESS && !ac->got_success) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ switch (ares->type) {
+ case LDB_REPLY_REFERRAL:
+ /* ignore referrals for now */
+ break;
+
+ case LDB_REPLY_ENTRY:
+ if (ac->req->operation != LDB_SEARCH) {
+ ldb_set_errstring(ac->module->ldb,
+ "partition_req_callback:"
+ " Unsupported reply type for this request");
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ return ldb_module_send_entry(ac->req, ares->message, ares->controls);
+
+ case LDB_REPLY_DONE:
+ if (ares->error == LDB_SUCCESS) {
+ ac->got_success = true;
+ }
+ if (ac->req->operation == LDB_EXTENDED) {
+ /* FIXME: check for ares->response, replmd does not fill it ! */
+ if (ares->response) {
+ if (strcmp(ares->response->oid, LDB_EXTENDED_START_TLS_OID) != 0) {
+ ldb_set_errstring(ac->module->ldb,
+ "partition_req_callback:"
+ " Unknown extended reply, "
+ "only supports START_TLS");
+ talloc_free(ares);
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ }
+ }
+
+ ac->finished_requests++;
+ if (ac->finished_requests == ac->num_requests) {
+ /* this was the last one, call callback */
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response,
+ ac->got_success?LDB_SUCCESS:ares->error);
+ }
+
+ /* not the last, now call the next one */
+ module = ac->part_req[ac->finished_requests].module;
+ nreq = ac->part_req[ac->finished_requests].req;
+
+ ret = partition_request(module, nreq);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(ares);
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+
+ break;
+ }
+
+ talloc_free(ares);
+ return LDB_SUCCESS;
+}
+
+static int partition_prep_request(struct partition_context *ac,
+ struct dsdb_control_current_partition *partition)
+{
+ int ret;
+ struct ldb_request *req;
+
+ ac->part_req = talloc_realloc(ac, ac->part_req,
+ struct part_request,
+ ac->num_requests + 1);
+ if (ac->part_req == NULL) {
+ ldb_oom(ac->module->ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ switch (ac->req->operation) {
+ case LDB_SEARCH:
+ ret = ldb_build_search_req_ex(&req, ac->module->ldb,
+ ac->part_req,
+ ac->req->op.search.base,
+ ac->req->op.search.scope,
+ ac->req->op.search.tree,
+ ac->req->op.search.attrs,
+ ac->req->controls,
+ ac, partition_req_callback,
+ ac->req);
+ break;
+ case LDB_ADD:
+ ret = ldb_build_add_req(&req, ac->module->ldb, ac->part_req,
+ ac->req->op.add.message,
+ ac->req->controls,
+ ac, partition_req_callback,
+ ac->req);
+ break;
+ case LDB_MODIFY:
+ ret = ldb_build_mod_req(&req, ac->module->ldb, ac->part_req,
+ ac->req->op.mod.message,
+ ac->req->controls,
+ ac, partition_req_callback,
+ ac->req);
+ break;
+ case LDB_DELETE:
+ ret = ldb_build_del_req(&req, ac->module->ldb, ac->part_req,
+ ac->req->op.del.dn,
+ ac->req->controls,
+ ac, partition_req_callback,
+ ac->req);
+ break;
+ case LDB_RENAME:
+ ret = ldb_build_rename_req(&req, ac->module->ldb, ac->part_req,
+ ac->req->op.rename.olddn,
+ ac->req->op.rename.newdn,
+ ac->req->controls,
+ ac, partition_req_callback,
+ ac->req);
+ break;
+ case LDB_EXTENDED:
+ ret = ldb_build_extended_req(&req, ac->module->ldb,
+ ac->part_req,
+ ac->req->op.extended.oid,
+ ac->req->op.extended.data,
+ ac->req->controls,
+ ac, partition_req_callback,
+ ac->req);
+ break;
+ default:
+ ldb_set_errstring(ac->module->ldb,
+ "Unsupported request type!");
+ ret = LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ ac->part_req[ac->num_requests].req = req;
+
+ if (ac->req->controls) {
+ req->controls = talloc_memdup(req, ac->req->controls,
+ talloc_get_size(ac->req->controls));
+ if (req->controls == NULL) {
+ ldb_oom(ac->module->ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ }
+
+ if (partition) {
+ ac->part_req[ac->num_requests].module = partition->module;
+
+ ret = ldb_request_add_control(req,
+ DSDB_CONTROL_CURRENT_PARTITION_OID,
+ false, partition);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ if (req->operation == LDB_SEARCH) {
+ /* If the search is for 'more' than this partition,
+ * then change the basedn, so a remote LDAP server
+ * doesn't object */
+ if (ldb_dn_compare_base(partition->dn,
+ req->op.search.base) != 0) {
+ req->op.search.base = partition->dn;
+ }
+ }
+
+ } else {
+ /* make sure you put the NEXT module here, or
+ * partition_request() will simply loop forever on itself */
+ ac->part_req[ac->num_requests].module = ac->module->next;
+ }
+
+ ac->num_requests++;
+
+ return LDB_SUCCESS;
+}
+
+static int partition_call_first(struct partition_context *ac)
+{
+ return partition_request(ac->part_req[0].module, ac->part_req[0].req);
+}
+
+/**
+ * Send a request down to all the partitions
+ */
+static int partition_send_all(struct ldb_module *module,
+ struct partition_context *ac,
+ struct ldb_request *req)
+{
+ int i;
+ struct partition_private_data *data = talloc_get_type(module->private_data,
+ struct partition_private_data);
+ int ret = partition_prep_request(ac, NULL);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ for (i=0; data && data->partitions && data->partitions[i]; i++) {
+ ret = partition_prep_request(ac, data->partitions[i]);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+
+ /* fire the first one */
+ return partition_call_first(ac);
+}
+
+/**
+ * Figure out which backend a request needs to be aimed at. Some
+ * requests must be replicated to all backends
+ */
+static int partition_replicate(struct ldb_module *module, struct ldb_request *req, struct ldb_dn *dn)
+{
+ struct partition_context *ac;
+ unsigned i;
+ int ret;
+ struct dsdb_control_current_partition *partition;
+ struct partition_private_data *data = talloc_get_type(module->private_data,
+ struct partition_private_data);
+ if (!data || !data->partitions) {
+ return ldb_next_request(module, req);
+ }
+
+ if (req->operation != LDB_SEARCH) {
+ /* Is this a special DN, we need to replicate to every backend? */
+ for (i=0; data->replicate && data->replicate[i]; i++) {
+ if (ldb_dn_compare(data->replicate[i],
+ dn) == 0) {
+
+ ac = partition_init_ctx(module, req);
+ if (!ac) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ return partition_send_all(module, ac, req);
+ }
+ }
+ }
+
+ /* Otherwise, we need to find the partition to fire it to */
+
+ /* Find partition */
+ partition = find_partition(data, dn);
+ if (!partition) {
+ /*
+ * if we haven't found a matching partition
+ * pass the request to the main ldb
+ *
+ * TODO: we should maybe return an error here
+ * if it's not a special dn
+ */
+
+ return ldb_next_request(module, req);
+ }
+
+ ac = partition_init_ctx(module, req);
+ if (!ac) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* we need to add a control but we never touch the original request */
+ ret = partition_prep_request(ac, partition);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ /* fire the first one */
+ return partition_call_first(ac);
+}
+
+/* search */
+static int partition_search(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_control **saved_controls;
+
+ /* Find backend */
+ struct partition_private_data *data = talloc_get_type(module->private_data,
+ struct partition_private_data);
+ /* issue request */
+
+ /* (later) consider if we should be searching multiple
+ * partitions (for 'invisible' partition behaviour */
+
+ struct ldb_control *search_control = ldb_request_get_control(req, LDB_CONTROL_SEARCH_OPTIONS_OID);
+ struct ldb_control *domain_scope_control = ldb_request_get_control(req, LDB_CONTROL_DOMAIN_SCOPE_OID);
+
+ struct ldb_search_options_control *search_options = NULL;
+ if (search_control) {
+ search_options = talloc_get_type(search_control->data, struct ldb_search_options_control);
+ }
+
+ /* Remove the domain_scope control, so we don't confuse a backend server */
+ if (domain_scope_control && !save_controls(domain_scope_control, req, &saved_controls)) {
+ ldb_oom(module->ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /*
+ * for now pass down the LDB_CONTROL_SEARCH_OPTIONS_OID control
+ * down as uncritical to make windows 2008 dcpromo happy.
+ */
+ if (search_control) {
+ search_control->critical = 0;
+ }
+
+ /* TODO:
+ Generate referrals (look for a partition under this DN) if we don't have the above control specified
+ */
+
+ if (search_options && (search_options->search_options & LDB_SEARCH_OPTION_PHANTOM_ROOT)) {
+ int ret, i;
+ struct partition_context *ac;
+ if ((search_options->search_options & ~LDB_SEARCH_OPTION_PHANTOM_ROOT) == 0) {
+ /* We have processed this flag, so we are done with this control now */
+
+ /* Remove search control, so we don't confuse a backend server */
+ if (search_control && !save_controls(search_control, req, &saved_controls)) {
+ ldb_oom(module->ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ }
+ ac = partition_init_ctx(module, req);
+ if (!ac) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* Search from the base DN */
+ if (!req->op.search.base || ldb_dn_is_null(req->op.search.base)) {
+ return partition_send_all(module, ac, req);
+ }
+ for (i=0; data && data->partitions && data->partitions[i]; i++) {
+ bool match = false, stop = false;
+ /* Find all partitions under the search base
+
+ we match if:
+
+ 1) the DN we are looking for exactly matches the partition
+ or
+ 2) the DN we are looking for is a parent of the partition and it isn't
+ a scope base search
+ or
+ 3) the DN we are looking for is a child of the partition
+ */
+ if (ldb_dn_compare(data->partitions[i]->dn, req->op.search.base) == 0) {
+ match = true;
+ if (req->op.search.scope == LDB_SCOPE_BASE) {
+ stop = true;
+ }
+ }
+ if (!match &&
+ (ldb_dn_compare_base(req->op.search.base, data->partitions[i]->dn) == 0 &&
+ req->op.search.scope != LDB_SCOPE_BASE)) {
+ match = true;
+ }
+ if (!match &&
+ ldb_dn_compare_base(data->partitions[i]->dn, req->op.search.base) == 0) {
+ match = true;
+ stop = true; /* note that this relies on partition ordering */
+ }
+ if (match) {
+ ret = partition_prep_request(ac, data->partitions[i]);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+ if (stop) break;
+ }
+
+ /* Perhaps we didn't match any partitions. Try the main partition, only */
+ if (ac->num_requests == 0) {
+ talloc_free(ac);
+ return ldb_next_request(module, req);
+ }
+
+ /* fire the first one */
+ return partition_call_first(ac);
+
+ } else {
+ /* Handle this like all other requests */
+ if (search_control && (search_options->search_options & ~LDB_SEARCH_OPTION_PHANTOM_ROOT) == 0) {
+ /* We have processed this flag, so we are done with this control now */
+
+ /* Remove search control, so we don't confuse a backend server */
+ if (search_control && !save_controls(search_control, req, &saved_controls)) {
+ ldb_oom(module->ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ }
+
+ return partition_replicate(module, req, req->op.search.base);
+ }
+}
+
+/* add */
+static int partition_add(struct ldb_module *module, struct ldb_request *req)
+{
+ return partition_replicate(module, req, req->op.add.message->dn);
+}
+
+/* modify */
+static int partition_modify(struct ldb_module *module, struct ldb_request *req)
+{
+ return partition_replicate(module, req, req->op.mod.message->dn);
+}
+
+/* delete */
+static int partition_delete(struct ldb_module *module, struct ldb_request *req)
+{
+ return partition_replicate(module, req, req->op.del.dn);
+}
+
+/* rename */
+static int partition_rename(struct ldb_module *module, struct ldb_request *req)
+{
+ /* Find backend */
+ struct dsdb_control_current_partition *backend, *backend2;
+
+ struct partition_private_data *data = talloc_get_type(module->private_data,
+ struct partition_private_data);
+
+ /* Skip the lot if 'data' isn't here yet (initialization) */
+ if (!data) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ backend = find_partition(data, req->op.rename.olddn);
+ backend2 = find_partition(data, req->op.rename.newdn);
+
+ if ((backend && !backend2) || (!backend && backend2)) {
+ return LDB_ERR_AFFECTS_MULTIPLE_DSAS;
+ }
+
+ if (backend != backend2) {
+ ldb_asprintf_errstring(module->ldb,
+ "Cannot rename from %s in %s to %s in %s: %s",
+ ldb_dn_get_linearized(req->op.rename.olddn),
+ ldb_dn_get_linearized(backend->dn),
+ ldb_dn_get_linearized(req->op.rename.newdn),
+ ldb_dn_get_linearized(backend2->dn),
+ ldb_strerror(LDB_ERR_AFFECTS_MULTIPLE_DSAS));
+ return LDB_ERR_AFFECTS_MULTIPLE_DSAS;
+ }
+
+ return partition_replicate(module, req, req->op.rename.olddn);
+}
+
+/* start a transaction */
+static int partition_start_trans(struct ldb_module *module)
+{
+ int i, ret;
+ struct partition_private_data *data = talloc_get_type(module->private_data,
+ struct partition_private_data);
+ /* Look at base DN */
+ /* Figure out which partition it is under */
+ /* Skip the lot if 'data' isn't here yet (initialization) */
+ ret = ldb_next_start_trans(module);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ for (i=0; data && data->partitions && data->partitions[i]; i++) {
+ struct ldb_module *next = data->partitions[i]->module;
+ PARTITION_FIND_OP(next, start_transaction);
+
+ ret = next->ops->start_transaction(next);
+ if (ret != LDB_SUCCESS) {
+ /* Back it out, if it fails on one */
+ for (i--; i >= 0; i--) {
+ next = data->partitions[i]->module;
+ PARTITION_FIND_OP(next, del_transaction);
+
+ next->ops->del_transaction(next);
+ }
+ ldb_next_del_trans(module);
+ return ret;
+ }
+ }
+ return LDB_SUCCESS;
+}
+
+/* end a transaction */
+static int partition_end_trans(struct ldb_module *module)
+{
+ int i, ret;
+ struct partition_private_data *data = talloc_get_type(module->private_data,
+ struct partition_private_data);
+ ret = ldb_next_end_trans(module);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ /* Look at base DN */
+ /* Figure out which partition it is under */
+ /* Skip the lot if 'data' isn't here yet (initialistion) */
+ for (i=0; data && data->partitions && data->partitions[i]; i++) {
+ struct ldb_module *next = data->partitions[i]->module;
+ PARTITION_FIND_OP(next, end_transaction);
+
+ ret = next->ops->end_transaction(next);
+ if (ret != LDB_SUCCESS) {
+ /* Back it out, if it fails on one */
+ for (i--; i >= 0; i--) {
+ next = data->partitions[i]->module;
+ PARTITION_FIND_OP(next, del_transaction);
+
+ next->ops->del_transaction(next);
+ }
+ ldb_next_del_trans(module);
+ return ret;
+ }
+ }
+
+ return LDB_SUCCESS;
+}
+
+/* delete a transaction */
+static int partition_del_trans(struct ldb_module *module)
+{
+ int i, ret, ret2 = LDB_SUCCESS;
+ struct partition_private_data *data = talloc_get_type(module->private_data,
+ struct partition_private_data);
+ ret = ldb_next_del_trans(module);
+ if (ret != LDB_SUCCESS) {
+ ret2 = ret;
+ }
+
+ /* Look at base DN */
+ /* Figure out which partition it is under */
+ /* Skip the lot if 'data' isn't here yet (initialistion) */
+ for (i=0; data && data->partitions && data->partitions[i]; i++) {
+ struct ldb_module *next = data->partitions[i]->module;
+ PARTITION_FIND_OP(next, del_transaction);
+
+ ret = next->ops->del_transaction(next);
+ if (ret != LDB_SUCCESS) {
+ ret2 = ret;
+ }
+ }
+ return ret2;
+}
+
+
+/* FIXME: This function is still semi-async */
+static int partition_sequence_number(struct ldb_module *module, struct ldb_request *req)
+{
+ int i, ret;
+ uint64_t seq_number = 0;
+ uint64_t timestamp_sequence = 0;
+ uint64_t timestamp = 0;
+ struct partition_private_data *data = talloc_get_type(module->private_data,
+ struct partition_private_data);
+ struct ldb_seqnum_request *seq;
+ struct ldb_seqnum_result *seqr;
+ struct ldb_request *treq;
+ struct ldb_seqnum_request *tseq;
+ struct ldb_seqnum_result *tseqr;
+ struct ldb_extended *ext;
+ struct ldb_result *res;
+
+ seq = talloc_get_type(req->op.extended.data, struct ldb_seqnum_request);
+
+ switch (seq->type) {
+ case LDB_SEQ_NEXT:
+ case LDB_SEQ_HIGHEST_SEQ:
+ res = talloc_zero(req, struct ldb_result);
+ if (res == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ tseq = talloc_zero(res, struct ldb_seqnum_request);
+ if (tseq == NULL) {
+ talloc_free(res);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ tseq->type = seq->type;
+
+ ret = ldb_build_extended_req(&treq, module->ldb, res,
+ LDB_EXTENDED_SEQUENCE_NUMBER,
+ tseq,
+ NULL,
+ res,
+ ldb_extended_default_callback,
+ NULL);
+ ret = ldb_next_request(module, treq);
+ if (ret == LDB_SUCCESS) {
+ ret = ldb_wait(treq->handle, LDB_WAIT_ALL);
+ }
+ if (ret != LDB_SUCCESS) {
+ talloc_free(res);
+ return ret;
+ }
+ seqr = talloc_get_type(res->extended->data,
+ struct ldb_seqnum_result);
+ if (seqr->flags & LDB_SEQ_TIMESTAMP_SEQUENCE) {
+ timestamp_sequence = seqr->seq_num;
+ } else {
+ seq_number += seqr->seq_num;
+ }
+ talloc_free(res);
+
+ /* Skip the lot if 'data' isn't here yet (initialistion) */
+ for (i=0; data && data->partitions && data->partitions[i]; i++) {
+
+ res = talloc_zero(req, struct ldb_result);
+ if (res == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ tseq = talloc_zero(res, struct ldb_seqnum_request);
+ if (tseq == NULL) {
+ talloc_free(res);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ tseq->type = seq->type;
+
+ ret = ldb_build_extended_req(&treq, module->ldb, res,
+ LDB_EXTENDED_SEQUENCE_NUMBER,
+ tseq,
+ NULL,
+ res,
+ ldb_extended_default_callback,
+ NULL);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(res);
+ return ret;
+ }
+
+ ret = ldb_request_add_control(treq,
+ DSDB_CONTROL_CURRENT_PARTITION_OID,
+ false, data->partitions[i]);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(res);
+ return ret;
+ }
+
+ ret = partition_request(data->partitions[i]->module, treq);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(res);
+ return ret;
+ }
+ ret = ldb_wait(treq->handle, LDB_WAIT_ALL);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(res);
+ return ret;
+ }
+ tseqr = talloc_get_type(res->extended->data,
+ struct ldb_seqnum_result);
+ if (tseqr->flags & LDB_SEQ_TIMESTAMP_SEQUENCE) {
+ timestamp_sequence = MAX(timestamp_sequence,
+ tseqr->seq_num);
+ } else {
+ seq_number += tseqr->seq_num;
+ }
+ talloc_free(res);
+ }
+ /* fall through */
+ case LDB_SEQ_HIGHEST_TIMESTAMP:
+
+ res = talloc_zero(req, struct ldb_result);
+ if (res == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ tseq = talloc_zero(res, struct ldb_seqnum_request);
+ if (tseq == NULL) {
+ talloc_free(res);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ tseq->type = LDB_SEQ_HIGHEST_TIMESTAMP;
+
+ ret = ldb_build_extended_req(&treq, module->ldb, res,
+ LDB_EXTENDED_SEQUENCE_NUMBER,
+ tseq,
+ NULL,
+ res,
+ ldb_extended_default_callback,
+ NULL);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(res);
+ return ret;
+ }
+
+ ret = ldb_next_request(module, treq);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(res);
+ return ret;
+ }
+ ret = ldb_wait(treq->handle, LDB_WAIT_ALL);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(res);
+ return ret;
+ }
+
+ tseqr = talloc_get_type(res->extended->data,
+ struct ldb_seqnum_result);
+ timestamp = tseqr->seq_num;
+
+ talloc_free(res);
+
+ /* Skip the lot if 'data' isn't here yet (initialistion) */
+ for (i=0; data && data->partitions && data->partitions[i]; i++) {
+
+ res = talloc_zero(req, struct ldb_result);
+ if (res == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ tseq = talloc_zero(res, struct ldb_seqnum_request);
+ if (tseq == NULL) {
+ talloc_free(res);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ tseq->type = LDB_SEQ_HIGHEST_TIMESTAMP;
+
+ ret = ldb_build_extended_req(&treq, module->ldb, res,
+ LDB_EXTENDED_SEQUENCE_NUMBER,
+ tseq,
+ NULL,
+ res,
+ ldb_extended_default_callback,
+ NULL);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(res);
+ return ret;
+ }
+
+ ret = ldb_request_add_control(treq,
+ DSDB_CONTROL_CURRENT_PARTITION_OID,
+ false, data->partitions[i]);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(res);
+ return ret;
+ }
+
+ ret = partition_request(data->partitions[i]->module, treq);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(res);
+ return ret;
+ }
+ ret = ldb_wait(treq->handle, LDB_WAIT_ALL);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(res);
+ return ret;
+ }
+
+ tseqr = talloc_get_type(res->extended->data,
+ struct ldb_seqnum_result);
+ timestamp = MAX(timestamp, tseqr->seq_num);
+
+ talloc_free(res);
+ }
+
+ break;
+ }
+
+ ext = talloc_zero(req, struct ldb_extended);
+ if (!ext) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ seqr = talloc_zero(ext, struct ldb_seqnum_result);
+ if (seqr == NULL) {
+ talloc_free(ext);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ext->oid = LDB_EXTENDED_SEQUENCE_NUMBER;
+ ext->data = seqr;
+
+ switch (seq->type) {
+ case LDB_SEQ_NEXT:
+ case LDB_SEQ_HIGHEST_SEQ:
+
+ /* Has someone above set a timebase sequence? */
+ if (timestamp_sequence) {
+ seqr->seq_num = (((unsigned long long)timestamp << 24) | (seq_number & 0xFFFFFF));
+ } else {
+ seqr->seq_num = seq_number;
+ }
+
+ if (timestamp_sequence > seqr->seq_num) {
+ seqr->seq_num = timestamp_sequence;
+ seqr->flags |= LDB_SEQ_TIMESTAMP_SEQUENCE;
+ }
+
+ seqr->flags |= LDB_SEQ_GLOBAL_SEQUENCE;
+ break;
+ case LDB_SEQ_HIGHEST_TIMESTAMP:
+ seqr->seq_num = timestamp;
+ break;
+ }
+
+ if (seq->type == LDB_SEQ_NEXT) {
+ seqr->seq_num++;
+ }
+
+ /* send request done */
+ return ldb_module_done(req, NULL, ext, LDB_SUCCESS);
+}
+
+static int partition_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
+{
+ struct dsdb_extended_replicated_objects *ext;
+
+ ext = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
+ if (!ext) {
+ ldb_debug(module->ldb, LDB_DEBUG_FATAL, "partition_extended_replicated_objects: invalid extended data\n");
+ return LDB_ERR_PROTOCOL_ERROR;
+ }
+
+ if (ext->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
+ ldb_debug(module->ldb, LDB_DEBUG_FATAL, "partition_extended_replicated_objects: extended data invalid version [%u != %u]\n",
+ ext->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
+ return LDB_ERR_PROTOCOL_ERROR;
+ }
+
+ return partition_replicate(module, req, ext->partition_dn);
+}
+
+static int partition_extended_schema_update_now(struct ldb_module *module, struct ldb_request *req)
+{
+ struct dsdb_control_current_partition *partition;
+ struct partition_private_data *data;
+ struct ldb_dn *schema_dn;
+ struct partition_context *ac;
+ int ret;
+
+ schema_dn = talloc_get_type(req->op.extended.data, struct ldb_dn);
+ if (!schema_dn) {
+ ldb_debug(module->ldb, LDB_DEBUG_FATAL, "partition_extended: invalid extended data\n");
+ return LDB_ERR_PROTOCOL_ERROR;
+ }
+
+ data = talloc_get_type(module->private_data, struct partition_private_data);
+ if (!data) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ partition = find_partition( data, schema_dn );
+ if (!partition) {
+ return ldb_next_request(module, req);
+ }
+
+ ac = partition_init_ctx(module, req);
+ if (!ac) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* we need to add a control but we never touch the original request */
+ ret = partition_prep_request(ac, partition);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ /* fire the first one */
+ return partition_call_first(ac);
+}
+
+
+/* extended */
+static int partition_extended(struct ldb_module *module, struct ldb_request *req)
+{
+ struct partition_private_data *data;
+ struct partition_context *ac;
+
+ data = talloc_get_type(module->private_data, struct partition_private_data);
+ if (!data || !data->partitions) {
+ return ldb_next_request(module, req);
+ }
+
+ if (strcmp(req->op.extended.oid, LDB_EXTENDED_SEQUENCE_NUMBER) == 0) {
+ return partition_sequence_number(module, req);
+ }
+
+ if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
+ return partition_extended_replicated_objects(module, req);
+ }
+
+ /* forward schemaUpdateNow operation to schema_fsmo module*/
+ if (strcmp(req->op.extended.oid, DSDB_EXTENDED_SCHEMA_UPDATE_NOW_OID) == 0) {
+ return partition_extended_schema_update_now( module, req );
+ }
+
+ /*
+ * as the extended operation has no dn
+ * we need to send it to all partitions
+ */
+
+ ac = partition_init_ctx(module, req);
+ if (!ac) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ return partition_send_all(module, ac, req);
+}
+
+static int partition_sort_compare(const void *v1, const void *v2)
+{
+ const struct dsdb_control_current_partition *p1;
+ const struct dsdb_control_current_partition *p2;
+
+ p1 = *((struct dsdb_control_current_partition * const*)v1);
+ p2 = *((struct dsdb_control_current_partition * const*)v2);
+
+ return ldb_dn_compare(p1->dn, p2->dn);
+}
+
+static int partition_init(struct ldb_module *module)
+{
+ int ret, i;
+ TALLOC_CTX *mem_ctx = talloc_new(module);
+ const char *attrs[] = { "partition", "replicateEntries", "modules", NULL };
+ struct ldb_result *res;
+ struct ldb_message *msg;
+ struct ldb_message_element *partition_attributes;
+ struct ldb_message_element *replicate_attributes;
+ struct ldb_message_element *modules_attributes;
+
+ struct partition_private_data *data;
+
+ if (!mem_ctx) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ data = talloc(mem_ctx, struct partition_private_data);
+ if (data == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ldb_search(module->ldb, mem_ctx, &res,
+ ldb_dn_new(mem_ctx, module->ldb, "@PARTITION"),
+ LDB_SCOPE_BASE, attrs, NULL);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(mem_ctx);
+ return ret;
+ }
+ if (res->count == 0) {
+ talloc_free(mem_ctx);
+ return ldb_next_init(module);
+ }
+
+ if (res->count > 1) {
+ talloc_free(mem_ctx);
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+
+ msg = res->msgs[0];
+
+ partition_attributes = ldb_msg_find_element(msg, "partition");
+ if (!partition_attributes) {
+ ldb_set_errstring(module->ldb, "partition_init: no partitions specified");
+ talloc_free(mem_ctx);
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+ data->partitions = talloc_array(data, struct dsdb_control_current_partition *, partition_attributes->num_values + 1);
+ if (!data->partitions) {
+ talloc_free(mem_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ for (i=0; i < partition_attributes->num_values; i++) {
+ char *base = talloc_strdup(data->partitions, (char *)partition_attributes->values[i].data);
+ char *p = strchr(base, ':');
+ if (!p) {
+ ldb_asprintf_errstring(module->ldb,
+ "partition_init: "
+ "invalid form for partition record (missing ':'): %s", base);
+ talloc_free(mem_ctx);
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+ p[0] = '\0';
+ p++;
+ if (!p[0]) {
+ ldb_asprintf_errstring(module->ldb,
+ "partition_init: "
+ "invalid form for partition record (missing backend database): %s", base);
+ talloc_free(mem_ctx);
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+ data->partitions[i] = talloc(data->partitions, struct dsdb_control_current_partition);
+ if (!data->partitions[i]) {
+ talloc_free(mem_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ data->partitions[i]->version = DSDB_CONTROL_CURRENT_PARTITION_VERSION;
+
+ data->partitions[i]->dn = ldb_dn_new(data->partitions[i], module->ldb, base);
+ if (!data->partitions[i]->dn) {
+ ldb_asprintf_errstring(module->ldb,
+ "partition_init: invalid DN in partition record: %s", base);
+ talloc_free(mem_ctx);
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+
+ data->partitions[i]->backend = samdb_relative_path(module->ldb,
+ data->partitions[i],
+ p);
+ if (!data->partitions[i]->backend) {
+ ldb_asprintf_errstring(module->ldb,
+ "partition_init: unable to determine an relative path for partition: %s", base);
+ talloc_free(mem_ctx);
+ }
+ ret = ldb_connect_backend(module->ldb, data->partitions[i]->backend, NULL, &data->partitions[i]->module);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(mem_ctx);
+ return ret;
+ }
+ }
+ data->partitions[i] = NULL;
+
+ /* sort these into order, most to least specific */
+ qsort(data->partitions, partition_attributes->num_values,
+ sizeof(*data->partitions), partition_sort_compare);
+
+ for (i=0; data->partitions[i]; i++) {
+ struct ldb_request *req;
+ req = talloc_zero(mem_ctx, struct ldb_request);
+ if (req == NULL) {
+ ldb_debug(module->ldb, LDB_DEBUG_ERROR, "partition: Out of memory!\n");
+ talloc_free(mem_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ req->operation = LDB_REQ_REGISTER_PARTITION;
+ req->op.reg_partition.dn = data->partitions[i]->dn;
+ req->callback = ldb_op_default_callback;
+
+ ldb_set_timeout(module->ldb, req, 0);
+
+ req->handle = ldb_handle_new(req, module->ldb);
+ if (req->handle == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ldb_request(module->ldb, req);
+ if (ret == LDB_SUCCESS) {
+ ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+ }
+ if (ret != LDB_SUCCESS) {
+ ldb_debug(module->ldb, LDB_DEBUG_ERROR, "partition: Unable to register partition with rootdse!\n");
+ talloc_free(mem_ctx);
+ return LDB_ERR_OTHER;
+ }
+ talloc_free(req);
+ }
+
+ replicate_attributes = ldb_msg_find_element(msg, "replicateEntries");
+ if (!replicate_attributes) {
+ data->replicate = NULL;
+ } else {
+ data->replicate = talloc_array(data, struct ldb_dn *, replicate_attributes->num_values + 1);
+ if (!data->replicate) {
+ talloc_free(mem_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ for (i=0; i < replicate_attributes->num_values; i++) {
+ data->replicate[i] = ldb_dn_from_ldb_val(data->replicate, module->ldb, &replicate_attributes->values[i]);
+ if (!ldb_dn_validate(data->replicate[i])) {
+ ldb_asprintf_errstring(module->ldb,
+ "partition_init: "
+ "invalid DN in partition replicate record: %s",
+ replicate_attributes->values[i].data);
+ talloc_free(mem_ctx);
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+ }
+ data->replicate[i] = NULL;
+ }
+
+ /* Make the private data available to any searches the modules may trigger in initialisation */
+ module->private_data = data;
+ talloc_steal(module, data);
+
+ modules_attributes = ldb_msg_find_element(msg, "modules");
+ if (modules_attributes) {
+ for (i=0; i < modules_attributes->num_values; i++) {
+ struct ldb_dn *base_dn;
+ int partition_idx;
+ struct dsdb_control_current_partition *partition = NULL;
+ const char **modules = NULL;
+
+ char *base = talloc_strdup(data->partitions, (char *)modules_attributes->values[i].data);
+ char *p = strchr(base, ':');
+ if (!p) {
+ ldb_asprintf_errstring(module->ldb,
+ "partition_init: "
+ "invalid form for partition module record (missing ':'): %s", base);
+ talloc_free(mem_ctx);
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+ p[0] = '\0';
+ p++;
+ if (!p[0]) {
+ ldb_asprintf_errstring(module->ldb,
+ "partition_init: "
+ "invalid form for partition module record (missing backend database): %s", base);
+ talloc_free(mem_ctx);
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+
+ modules = ldb_modules_list_from_string(module->ldb, mem_ctx,
+ p);
+
+ base_dn = ldb_dn_new(mem_ctx, module->ldb, base);
+ if (!ldb_dn_validate(base_dn)) {
+ talloc_free(mem_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ for (partition_idx = 0; data->partitions[partition_idx]; partition_idx++) {
+ if (ldb_dn_compare(data->partitions[partition_idx]->dn, base_dn) == 0) {
+ partition = data->partitions[partition_idx];
+ break;
+ }
+ }
+
+ if (!partition) {
+ ldb_asprintf_errstring(module->ldb,
+ "partition_init: "
+ "invalid form for partition module record (no such partition): %s", base);
+ talloc_free(mem_ctx);
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+
+ ret = ldb_load_modules_list(module->ldb, modules, partition->module, &partition->module);
+ if (ret != LDB_SUCCESS) {
+ ldb_asprintf_errstring(module->ldb,
+ "partition_init: "
+ "loading backend for %s failed: %s",
+ base, ldb_errstring(module->ldb));
+ talloc_free(mem_ctx);
+ return ret;
+ }
+ ret = ldb_init_module_chain(module->ldb, partition->module);
+ if (ret != LDB_SUCCESS) {
+ ldb_asprintf_errstring(module->ldb,
+ "partition_init: "
+ "initialising backend for %s failed: %s",
+ base, ldb_errstring(module->ldb));
+ talloc_free(mem_ctx);
+ return ret;
+ }
+ }
+ }
+
+ ret = ldb_mod_register_control(module, LDB_CONTROL_DOMAIN_SCOPE_OID);
+ if (ret != LDB_SUCCESS) {
+ ldb_debug(module->ldb, LDB_DEBUG_ERROR,
+ "partition: Unable to register control with rootdse!\n");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ldb_mod_register_control(module, LDB_CONTROL_SEARCH_OPTIONS_OID);
+ if (ret != LDB_SUCCESS) {
+ ldb_debug(module->ldb, LDB_DEBUG_ERROR,
+ "partition: Unable to register control with rootdse!\n");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ talloc_free(mem_ctx);
+ return ldb_next_init(module);
+}
+
+_PUBLIC_ const struct ldb_module_ops ldb_partition_module_ops = {
+ .name = "partition",
+ .init_context = partition_init,
+ .search = partition_search,
+ .add = partition_add,
+ .modify = partition_modify,
+ .del = partition_delete,
+ .rename = partition_rename,
+ .extended = partition_extended,
+ .start_transaction = partition_start_trans,
+ .end_transaction = partition_end_trans,
+ .del_transaction = partition_del_trans,
+};
diff --git a/source4/dsdb/samdb/ldb_modules/password_hash.c b/source4/dsdb/samdb/ldb_modules/password_hash.c
new file mode 100644
index 0000000000..56d4c4fe36
--- /dev/null
+++ b/source4/dsdb/samdb/ldb_modules/password_hash.c
@@ -0,0 +1,2257 @@
+/*
+ ldb database module
+
+ Copyright (C) Simo Sorce 2004-2008
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2006
+ Copyright (C) Andrew Tridgell 2004
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb password_hash module
+ *
+ * Description: correctly update hash values based on changes to userPassword and friends
+ *
+ * Author: Andrew Bartlett
+ * Author: Stefan Metzmacher
+ */
+
+#include "includes.h"
+#include "libcli/ldap/ldap_ndr.h"
+#include "ldb_module.h"
+#include "librpc/gen_ndr/misc.h"
+#include "librpc/gen_ndr/samr.h"
+#include "libcli/auth/libcli_auth.h"
+#include "libcli/security/security.h"
+#include "system/kerberos.h"
+#include "auth/kerberos/kerberos.h"
+#include "system/time.h"
+#include "dsdb/samdb/samdb.h"
+#include "dsdb/common/flags.h"
+#include "dsdb/samdb/ldb_modules/password_modules.h"
+#include "librpc/ndr/libndr.h"
+#include "librpc/gen_ndr/ndr_drsblobs.h"
+#include "../lib/crypto/crypto.h"
+#include "param/param.h"
+
+/* If we have decided there is reason to work on this request, then
+ * setup all the password hash types correctly.
+ *
+ * If the administrator doesn't want the userPassword stored (set in the
+ * domain and per-account policies) then we must strip that out before
+ * we do the first operation.
+ *
+ * Once this is done (which could update anything at all), we
+ * calculate the password hashes.
+ *
+ * This function must not only update the unicodePwd, dBCSPwd and
+ * supplementalCredentials fields, it must also atomicly increment the
+ * msDS-KeyVersionNumber. We should be in a transaction, so all this
+ * should be quite safe...
+ *
+ * Finally, if the administrator has requested that a password history
+ * be maintained, then this should also be written out.
+ *
+ */
+
+struct ph_context {
+
+ struct ldb_module *module;
+ struct ldb_request *req;
+
+ struct ldb_request *dom_req;
+ struct ldb_reply *dom_res;
+
+ struct ldb_reply *search_res;
+
+ struct dom_sid *domain_sid;
+ struct domain_data *domain;
+};
+
+struct domain_data {
+ bool store_cleartext;
+ uint_t pwdProperties;
+ uint_t pwdHistoryLength;
+ char *netbios_domain;
+ char *dns_domain;
+ char *realm;
+};
+
+struct setup_password_fields_io {
+ struct ph_context *ac;
+ struct domain_data *domain;
+ struct smb_krb5_context *smb_krb5_context;
+
+ /* infos about the user account */
+ struct {
+ uint32_t user_account_control;
+ const char *sAMAccountName;
+ const char *user_principal_name;
+ bool is_computer;
+ } u;
+
+ /* new credentials */
+ struct {
+ const struct ldb_val *cleartext_utf8;
+ const struct ldb_val *cleartext_utf16;
+ struct ldb_val quoted_utf16;
+ struct samr_Password *nt_hash;
+ struct samr_Password *lm_hash;
+ } n;
+
+ /* old credentials */
+ struct {
+ uint32_t nt_history_len;
+ struct samr_Password *nt_history;
+ uint32_t lm_history_len;
+ struct samr_Password *lm_history;
+ const struct ldb_val *supplemental;
+ struct supplementalCredentialsBlob scb;
+ uint32_t kvno;
+ } o;
+
+ /* generated credentials */
+ struct {
+ struct samr_Password *nt_hash;
+ struct samr_Password *lm_hash;
+ uint32_t nt_history_len;
+ struct samr_Password *nt_history;
+ uint32_t lm_history_len;
+ struct samr_Password *lm_history;
+ const char *salt;
+ DATA_BLOB aes_256;
+ DATA_BLOB aes_128;
+ DATA_BLOB des_md5;
+ DATA_BLOB des_crc;
+ struct ldb_val supplemental;
+ NTTIME last_set;
+ uint32_t kvno;
+ } g;
+};
+
+/* Get the NT hash, and fill it in as an entry in the password history,
+ and specify it into io->g.nt_hash */
+
+static int setup_nt_fields(struct setup_password_fields_io *io)
+{
+ struct ldb_context *ldb;
+ uint32_t i;
+
+ io->g.nt_hash = io->n.nt_hash;
+ ldb = ldb_module_get_ctx(io->ac->module);
+
+ if (io->domain->pwdHistoryLength == 0) {
+ return LDB_SUCCESS;
+ }
+
+ /* We might not have an old NT password */
+ io->g.nt_history = talloc_array(io->ac,
+ struct samr_Password,
+ io->domain->pwdHistoryLength);
+ if (!io->g.nt_history) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ for (i = 0; i < MIN(io->domain->pwdHistoryLength-1, io->o.nt_history_len); i++) {
+ io->g.nt_history[i+1] = io->o.nt_history[i];
+ }
+ io->g.nt_history_len = i + 1;
+
+ if (io->g.nt_hash) {
+ io->g.nt_history[0] = *io->g.nt_hash;
+ } else {
+ /*
+ * TODO: is this correct?
+ * the simular behavior is correct for the lm history case
+ */
+ E_md4hash("", io->g.nt_history[0].hash);
+ }
+
+ return LDB_SUCCESS;
+}
+
+/* Get the LANMAN hash, and fill it in as an entry in the password history,
+ and specify it into io->g.lm_hash */
+
+static int setup_lm_fields(struct setup_password_fields_io *io)
+{
+ struct ldb_context *ldb;
+ uint32_t i;
+
+ io->g.lm_hash = io->n.lm_hash;
+ ldb = ldb_module_get_ctx(io->ac->module);
+
+ if (io->domain->pwdHistoryLength == 0) {
+ return LDB_SUCCESS;
+ }
+
+ /* We might not have an old NT password */
+ io->g.lm_history = talloc_array(io->ac,
+ struct samr_Password,
+ io->domain->pwdHistoryLength);
+ if (!io->g.lm_history) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ for (i = 0; i < MIN(io->domain->pwdHistoryLength-1, io->o.lm_history_len); i++) {
+ io->g.lm_history[i+1] = io->o.lm_history[i];
+ }
+ io->g.lm_history_len = i + 1;
+
+ if (io->g.lm_hash) {
+ io->g.lm_history[0] = *io->g.lm_hash;
+ } else {
+ E_deshash("", io->g.lm_history[0].hash);
+ }
+
+ return LDB_SUCCESS;
+}
+
+static int setup_kerberos_keys(struct setup_password_fields_io *io)
+{
+ struct ldb_context *ldb;
+ krb5_error_code krb5_ret;
+ Principal *salt_principal;
+ krb5_salt salt;
+ krb5_keyblock key;
+ krb5_data cleartext_data;
+
+ ldb = ldb_module_get_ctx(io->ac->module);
+ cleartext_data.data = io->n.cleartext_utf8->data;
+ cleartext_data.length = io->n.cleartext_utf8->length;
+
+ /* Many, many thanks to lukeh@padl.com for this
+ * algorithm, described in his Nov 10 2004 mail to
+ * samba-technical@samba.org */
+
+ /*
+ * Determine a salting principal
+ */
+ if (io->u.is_computer) {
+ char *name;
+ char *saltbody;
+
+ name = talloc_strdup(io->ac, io->u.sAMAccountName);
+ if (!name) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if (name[strlen(name)-1] == '$') {
+ name[strlen(name)-1] = '\0';
+ }
+
+ saltbody = talloc_asprintf(io->ac, "%s.%s", name, io->domain->dns_domain);
+ if (!saltbody) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ krb5_ret = krb5_make_principal(io->smb_krb5_context->krb5_context,
+ &salt_principal,
+ io->domain->realm, "host",
+ saltbody, NULL);
+ } else if (io->u.user_principal_name) {
+ char *user_principal_name;
+ char *p;
+
+ user_principal_name = talloc_strdup(io->ac, io->u.user_principal_name);
+ if (!user_principal_name) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ p = strchr(user_principal_name, '@');
+ if (p) {
+ p[0] = '\0';
+ }
+
+ krb5_ret = krb5_make_principal(io->smb_krb5_context->krb5_context,
+ &salt_principal,
+ io->domain->realm, user_principal_name,
+ NULL);
+ } else {
+ krb5_ret = krb5_make_principal(io->smb_krb5_context->krb5_context,
+ &salt_principal,
+ io->domain->realm, io->u.sAMAccountName,
+ NULL);
+ }
+ if (krb5_ret) {
+ ldb_asprintf_errstring(ldb,
+ "setup_kerberos_keys: "
+ "generation of a salting principal failed: %s",
+ smb_get_krb5_error_message(io->smb_krb5_context->krb5_context, krb5_ret, io->ac));
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /*
+ * create salt from salt_principal
+ */
+ krb5_ret = krb5_get_pw_salt(io->smb_krb5_context->krb5_context,
+ salt_principal, &salt);
+ krb5_free_principal(io->smb_krb5_context->krb5_context, salt_principal);
+ if (krb5_ret) {
+ ldb_asprintf_errstring(ldb,
+ "setup_kerberos_keys: "
+ "generation of krb5_salt failed: %s",
+ smb_get_krb5_error_message(io->smb_krb5_context->krb5_context, krb5_ret, io->ac));
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ /* create a talloc copy */
+ io->g.salt = talloc_strndup(io->ac,
+ salt.saltvalue.data,
+ salt.saltvalue.length);
+ krb5_free_salt(io->smb_krb5_context->krb5_context, salt);
+ if (!io->g.salt) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ salt.saltvalue.data = discard_const(io->g.salt);
+ salt.saltvalue.length = strlen(io->g.salt);
+
+ /*
+ * create ENCTYPE_AES256_CTS_HMAC_SHA1_96 key out of
+ * the salt and the cleartext password
+ */
+ krb5_ret = krb5_string_to_key_data_salt(io->smb_krb5_context->krb5_context,
+ ENCTYPE_AES256_CTS_HMAC_SHA1_96,
+ cleartext_data,
+ salt,
+ &key);
+ if (krb5_ret) {
+ ldb_asprintf_errstring(ldb,
+ "setup_kerberos_keys: "
+ "generation of a aes256-cts-hmac-sha1-96 key failed: %s",
+ smb_get_krb5_error_message(io->smb_krb5_context->krb5_context, krb5_ret, io->ac));
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ io->g.aes_256 = data_blob_talloc(io->ac,
+ key.keyvalue.data,
+ key.keyvalue.length);
+ krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
+ if (!io->g.aes_256.data) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /*
+ * create ENCTYPE_AES128_CTS_HMAC_SHA1_96 key out of
+ * the salt and the cleartext password
+ */
+ krb5_ret = krb5_string_to_key_data_salt(io->smb_krb5_context->krb5_context,
+ ENCTYPE_AES128_CTS_HMAC_SHA1_96,
+ cleartext_data,
+ salt,
+ &key);
+ if (krb5_ret) {
+ ldb_asprintf_errstring(ldb,
+ "setup_kerberos_keys: "
+ "generation of a aes128-cts-hmac-sha1-96 key failed: %s",
+ smb_get_krb5_error_message(io->smb_krb5_context->krb5_context, krb5_ret, io->ac));
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ io->g.aes_128 = data_blob_talloc(io->ac,
+ key.keyvalue.data,
+ key.keyvalue.length);
+ krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
+ if (!io->g.aes_128.data) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /*
+ * create ENCTYPE_DES_CBC_MD5 key out of
+ * the salt and the cleartext password
+ */
+ krb5_ret = krb5_string_to_key_data_salt(io->smb_krb5_context->krb5_context,
+ ENCTYPE_DES_CBC_MD5,
+ cleartext_data,
+ salt,
+ &key);
+ if (krb5_ret) {
+ ldb_asprintf_errstring(ldb,
+ "setup_kerberos_keys: "
+ "generation of a des-cbc-md5 key failed: %s",
+ smb_get_krb5_error_message(io->smb_krb5_context->krb5_context, krb5_ret, io->ac));
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ io->g.des_md5 = data_blob_talloc(io->ac,
+ key.keyvalue.data,
+ key.keyvalue.length);
+ krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
+ if (!io->g.des_md5.data) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /*
+ * create ENCTYPE_DES_CBC_CRC key out of
+ * the salt and the cleartext password
+ */
+ krb5_ret = krb5_string_to_key_data_salt(io->smb_krb5_context->krb5_context,
+ ENCTYPE_DES_CBC_CRC,
+ cleartext_data,
+ salt,
+ &key);
+ if (krb5_ret) {
+ ldb_asprintf_errstring(ldb,
+ "setup_kerberos_keys: "
+ "generation of a des-cbc-crc key failed: %s",
+ smb_get_krb5_error_message(io->smb_krb5_context->krb5_context, krb5_ret, io->ac));
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ io->g.des_crc = data_blob_talloc(io->ac,
+ key.keyvalue.data,
+ key.keyvalue.length);
+ krb5_free_keyblock_contents(io->smb_krb5_context->krb5_context, &key);
+ if (!io->g.des_crc.data) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ return LDB_SUCCESS;
+}
+
+static int setup_primary_kerberos(struct setup_password_fields_io *io,
+ const struct supplementalCredentialsBlob *old_scb,
+ struct package_PrimaryKerberosBlob *pkb)
+{
+ struct ldb_context *ldb;
+ struct package_PrimaryKerberosCtr3 *pkb3 = &pkb->ctr.ctr3;
+ struct supplementalCredentialsPackage *old_scp = NULL;
+ struct package_PrimaryKerberosBlob _old_pkb;
+ struct package_PrimaryKerberosCtr3 *old_pkb3 = NULL;
+ uint32_t i;
+ enum ndr_err_code ndr_err;
+
+ ldb = ldb_module_get_ctx(io->ac->module);
+
+ /*
+ * prepare generation of keys
+ *
+ * ENCTYPE_DES_CBC_MD5
+ * ENCTYPE_DES_CBC_CRC
+ */
+ pkb->version = 3;
+ pkb3->salt.string = io->g.salt;
+ pkb3->num_keys = 2;
+ pkb3->keys = talloc_array(io->ac,
+ struct package_PrimaryKerberosKey3,
+ pkb3->num_keys);
+ if (!pkb3->keys) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ pkb3->keys[0].keytype = ENCTYPE_DES_CBC_MD5;
+ pkb3->keys[0].value = &io->g.des_md5;
+ pkb3->keys[1].keytype = ENCTYPE_DES_CBC_CRC;
+ pkb3->keys[1].value = &io->g.des_crc;
+
+ /* initialize the old keys to zero */
+ pkb3->num_old_keys = 0;
+ pkb3->old_keys = NULL;
+
+ /* if there're no old keys, then we're done */
+ if (!old_scb) {
+ return LDB_SUCCESS;
+ }
+
+ for (i=0; i < old_scb->sub.num_packages; i++) {
+ if (strcmp("Primary:Kerberos", old_scb->sub.packages[i].name) != 0) {
+ continue;
+ }
+
+ if (!old_scb->sub.packages[i].data || !old_scb->sub.packages[i].data[0]) {
+ continue;
+ }
+
+ old_scp = &old_scb->sub.packages[i];
+ break;
+ }
+ /* Primary:Kerberos element of supplementalCredentials */
+ if (old_scp) {
+ DATA_BLOB blob;
+
+ blob = strhex_to_data_blob(io->ac, old_scp->data);
+ if (!blob.data) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* TODO: use ndr_pull_struct_blob_all(), when the ndr layer handles it correct with relative pointers */
+ ndr_err = ndr_pull_struct_blob(&blob, io->ac, lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), &_old_pkb,
+ (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryKerberosBlob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
+ ldb_asprintf_errstring(ldb,
+ "setup_primary_kerberos: "
+ "failed to pull old package_PrimaryKerberosBlob: %s",
+ nt_errstr(status));
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if (_old_pkb.version != 3) {
+ ldb_asprintf_errstring(ldb,
+ "setup_primary_kerberos: "
+ "package_PrimaryKerberosBlob version[%u] expected[3]",
+ _old_pkb.version);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ old_pkb3 = &_old_pkb.ctr.ctr3;
+ }
+
+ /* if we didn't found the old keys we're done */
+ if (!old_pkb3) {
+ return LDB_SUCCESS;
+ }
+
+ /* fill in the old keys */
+ pkb3->num_old_keys = old_pkb3->num_keys;
+ pkb3->old_keys = old_pkb3->keys;
+
+ return LDB_SUCCESS;
+}
+
+static int setup_primary_kerberos_newer(struct setup_password_fields_io *io,
+ const struct supplementalCredentialsBlob *old_scb,
+ struct package_PrimaryKerberosBlob *pkb)
+{
+ struct ldb_context *ldb;
+ struct package_PrimaryKerberosCtr4 *pkb4 = &pkb->ctr.ctr4;
+ struct supplementalCredentialsPackage *old_scp = NULL;
+ struct package_PrimaryKerberosBlob _old_pkb;
+ struct package_PrimaryKerberosCtr4 *old_pkb4 = NULL;
+ uint32_t i;
+ enum ndr_err_code ndr_err;
+
+ ldb = ldb_module_get_ctx(io->ac->module);
+
+ /*
+ * prepare generation of keys
+ *
+ * ENCTYPE_AES256_CTS_HMAC_SHA1_96
+ * ENCTYPE_AES128_CTS_HMAC_SHA1_96
+ * ENCTYPE_DES_CBC_MD5
+ * ENCTYPE_DES_CBC_CRC
+ */
+ pkb->version = 4;
+ pkb4->salt.string = io->g.salt;
+ pkb4->default_iteration_count = 4096;
+ pkb4->num_keys = 4;
+
+ pkb4->keys = talloc_array(io->ac,
+ struct package_PrimaryKerberosKey4,
+ pkb4->num_keys);
+ if (!pkb4->keys) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ pkb4->keys[0].iteration_count = 4096;
+ pkb4->keys[0].keytype = ENCTYPE_AES256_CTS_HMAC_SHA1_96;
+ pkb4->keys[0].value = &io->g.aes_256;
+ pkb4->keys[1].iteration_count = 4096;
+ pkb4->keys[1].keytype = ENCTYPE_AES128_CTS_HMAC_SHA1_96;
+ pkb4->keys[1].value = &io->g.aes_128;
+ pkb4->keys[2].iteration_count = 4096;
+ pkb4->keys[2].keytype = ENCTYPE_DES_CBC_MD5;
+ pkb4->keys[2].value = &io->g.des_md5;
+ pkb4->keys[3].iteration_count = 4096;
+ pkb4->keys[3].keytype = ENCTYPE_DES_CBC_CRC;
+ pkb4->keys[3].value = &io->g.des_crc;
+
+ /* initialize the old keys to zero */
+ pkb4->num_old_keys = 0;
+ pkb4->old_keys = NULL;
+ pkb4->num_older_keys = 0;
+ pkb4->older_keys = NULL;
+
+ /* if there're no old keys, then we're done */
+ if (!old_scb) {
+ return LDB_SUCCESS;
+ }
+
+ for (i=0; i < old_scb->sub.num_packages; i++) {
+ if (strcmp("Primary:Kerberos-Newer-Keys", old_scb->sub.packages[i].name) != 0) {
+ continue;
+ }
+
+ if (!old_scb->sub.packages[i].data || !old_scb->sub.packages[i].data[0]) {
+ continue;
+ }
+
+ old_scp = &old_scb->sub.packages[i];
+ break;
+ }
+ /* Primary:Kerberos-Newer-Keys element of supplementalCredentials */
+ if (old_scp) {
+ DATA_BLOB blob;
+
+ blob = strhex_to_data_blob(io->ac, old_scp->data);
+ if (!blob.data) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* TODO: use ndr_pull_struct_blob_all(), when the ndr layer handles it correct with relative pointers */
+ ndr_err = ndr_pull_struct_blob(&blob, io->ac,
+ lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
+ &_old_pkb,
+ (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryKerberosBlob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
+ ldb_asprintf_errstring(ldb,
+ "setup_primary_kerberos_newer: "
+ "failed to pull old package_PrimaryKerberosBlob: %s",
+ nt_errstr(status));
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if (_old_pkb.version != 4) {
+ ldb_asprintf_errstring(ldb,
+ "setup_primary_kerberos_newer: "
+ "package_PrimaryKerberosBlob version[%u] expected[4]",
+ _old_pkb.version);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ old_pkb4 = &_old_pkb.ctr.ctr4;
+ }
+
+ /* if we didn't found the old keys we're done */
+ if (!old_pkb4) {
+ return LDB_SUCCESS;
+ }
+
+ /* fill in the old keys */
+ pkb4->num_old_keys = old_pkb4->num_keys;
+ pkb4->old_keys = old_pkb4->keys;
+ pkb4->num_older_keys = old_pkb4->num_old_keys;
+ pkb4->older_keys = old_pkb4->old_keys;
+
+ return LDB_SUCCESS;
+}
+
+static int setup_primary_wdigest(struct setup_password_fields_io *io,
+ const struct supplementalCredentialsBlob *old_scb,
+ struct package_PrimaryWDigestBlob *pdb)
+{
+ struct ldb_context *ldb = ldb_module_get_ctx(io->ac->module);
+ DATA_BLOB sAMAccountName;
+ DATA_BLOB sAMAccountName_l;
+ DATA_BLOB sAMAccountName_u;
+ const char *user_principal_name = io->u.user_principal_name;
+ DATA_BLOB userPrincipalName;
+ DATA_BLOB userPrincipalName_l;
+ DATA_BLOB userPrincipalName_u;
+ DATA_BLOB netbios_domain;
+ DATA_BLOB netbios_domain_l;
+ DATA_BLOB netbios_domain_u;
+ DATA_BLOB dns_domain;
+ DATA_BLOB dns_domain_l;
+ DATA_BLOB dns_domain_u;
+ DATA_BLOB digest;
+ DATA_BLOB delim;
+ DATA_BLOB backslash;
+ uint8_t i;
+ struct {
+ DATA_BLOB *user;
+ DATA_BLOB *realm;
+ DATA_BLOB *nt4dom;
+ } wdigest[] = {
+ /*
+ * See
+ * http://technet2.microsoft.com/WindowsServer/en/library/717b450c-f4a0-4cc9-86f4-cc0633aae5f91033.mspx?mfr=true
+ * for what precalculated hashes are supposed to be stored...
+ *
+ * I can't reproduce all values which should contain "Digest" as realm,
+ * am I doing something wrong or is w2k3 just broken...?
+ *
+ * W2K3 fills in following for a user:
+ *
+ * dn: CN=NewUser,OU=newtop,DC=sub1,DC=w2k3,DC=vmnet1,DC=vm,DC=base
+ * sAMAccountName: NewUser2Sam
+ * userPrincipalName: NewUser2Princ@sub1.w2k3.vmnet1.vm.base
+ *
+ * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
+ * b7ec9da91062199aee7d121e6710fe23 => newuser2sam:sub1:TestPwd2007
+ * 17d290bc5c9f463fac54c37a8cea134d => NEWUSER2SAM:SUB1:TestPwd2007
+ * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
+ * 5d57e7823938348127322e08cd81bcb5 => NewUser2Sam:sub1:TestPwd2007
+ * 07dd701bf8a011ece585de3d47237140 => NEWUSER2SAM:sub1:TestPwd2007
+ * e14fb0eb401498d2cb33c9aae1cc7f37 => newuser2sam:SUB1:TestPwd2007
+ * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
+ * f52da1266a6bdd290ffd48b2c823dda7 => newuser2sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
+ * d2b42f171248cec37a3c5c6b55404062 => NEWUSER2SAM:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
+ * fff8d790ff6c152aaeb6ebe17b4021de => NewUser2Sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
+ * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
+ * 2a7563c3715bc418d626dabef378c008 => NEWUSER2SAM:sub1.w2k3.vmnet1.vm.base:TestPwd2007
+ * c8e9557a87cd4200fda0c11d2fa03f96 => newuser2sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
+ * 221c55284451ae9b3aacaa2a3c86f10f => NewUser2Princ@sub1.w2k3.vmnet1.vm.base::TestPwd2007
+ * 74e1be668853d4324d38c07e2acfb8ea => (w2k3 has a bug here!) newuser2princ@sub1.w2k3.vmnet1.vm.base::TestPwd2007
+ * e1e244ab7f098e3ae1761be7f9229bbb => NEWUSER2PRINC@SUB1.W2K3.VMNET1.VM.BASE::TestPwd2007
+ * 86db637df42513039920e605499c3af6 => SUB1\NewUser2Sam::TestPwd2007
+ * f5e43474dfaf067fee8197a253debaa2 => sub1\newuser2sam::TestPwd2007
+ * 2ecaa8382e2518e4b77a52422b279467 => SUB1\NEWUSER2SAM::TestPwd2007
+ * 31dc704d3640335b2123d4ee28aa1f11 => ??? changes with NewUser2Sam => NewUser1Sam
+ * 36349f5cecd07320fb3bb0e119230c43 => ??? changes with NewUser2Sam => NewUser1Sam
+ * 12adf019d037fb535c01fd0608e78d9d => ??? changes with NewUser2Sam => NewUser1Sam
+ * 6feecf8e724906f3ee1105819c5105a1 => ??? changes with NewUser2Princ => NewUser1Princ
+ * 6c6911f3de6333422640221b9c51ff1f => ??? changes with NewUser2Princ => NewUser1Princ
+ * 4b279877e742895f9348ac67a8de2f69 => ??? changes with NewUser2Princ => NewUser1Princ
+ * db0c6bff069513e3ebb9870d29b57490 => ??? changes with NewUser2Sam => NewUser1Sam
+ * 45072621e56b1c113a4e04a8ff68cd0e => ??? changes with NewUser2Sam => NewUser1Sam
+ * 11d1220abc44a9c10cf91ef4a9c1de02 => ??? changes with NewUser2Sam => NewUser1Sam
+ *
+ * dn: CN=NewUser,OU=newtop,DC=sub1,DC=w2k3,DC=vmnet1,DC=vm,DC=base
+ * sAMAccountName: NewUser2Sam
+ *
+ * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
+ * b7ec9da91062199aee7d121e6710fe23 => newuser2sam:sub1:TestPwd2007
+ * 17d290bc5c9f463fac54c37a8cea134d => NEWUSER2SAM:SUB1:TestPwd2007
+ * 4279815024bda54fc074a5f8bd0a6e6f => NewUser2Sam:SUB1:TestPwd2007
+ * 5d57e7823938348127322e08cd81bcb5 => NewUser2Sam:sub1:TestPwd2007
+ * 07dd701bf8a011ece585de3d47237140 => NEWUSER2SAM:sub1:TestPwd2007
+ * e14fb0eb401498d2cb33c9aae1cc7f37 => newuser2sam:SUB1:TestPwd2007
+ * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
+ * f52da1266a6bdd290ffd48b2c823dda7 => newuser2sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
+ * d2b42f171248cec37a3c5c6b55404062 => NEWUSER2SAM:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
+ * fff8d790ff6c152aaeb6ebe17b4021de => NewUser2Sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
+ * 8dadc90250f873d8b883f79d890bef82 => NewUser2Sam:sub1.w2k3.vmnet1.vm.base:TestPwd2007
+ * 2a7563c3715bc418d626dabef378c008 => NEWUSER2SAM:sub1.w2k3.vmnet1.vm.base:TestPwd2007
+ * c8e9557a87cd4200fda0c11d2fa03f96 => newuser2sam:SUB1.W2K3.VMNET1.VM.BASE:TestPwd2007
+ * 8a140d30b6f0a5912735dc1e3bc993b4 => NewUser2Sam@sub1.w2k3.vmnet1.vm.base::TestPwd2007
+ * 86d95b2faae6cae4ec261e7fbaccf093 => (here w2k3 is correct) newuser2sam@sub1.w2k3.vmnet1.vm.base::TestPwd2007
+ * dfeff1493110220efcdfc6362e5f5450 => NEWUSER2SAM@SUB1.W2K3.VMNET1.VM.BASE::TestPwd2007
+ * 86db637df42513039920e605499c3af6 => SUB1\NewUser2Sam::TestPwd2007
+ * f5e43474dfaf067fee8197a253debaa2 => sub1\newuser2sam::TestPwd2007
+ * 2ecaa8382e2518e4b77a52422b279467 => SUB1\NEWUSER2SAM::TestPwd2007
+ * 31dc704d3640335b2123d4ee28aa1f11 => ???M1 changes with NewUser2Sam => NewUser1Sam
+ * 36349f5cecd07320fb3bb0e119230c43 => ???M1.L changes with newuser2sam => newuser1sam
+ * 12adf019d037fb535c01fd0608e78d9d => ???M1.U changes with NEWUSER2SAM => NEWUSER1SAM
+ * 569b4533f2d9e580211dd040e5e360a8 => ???M2 changes with NewUser2Princ => NewUser1Princ
+ * 52528bddf310a587c5d7e6a9ae2cbb20 => ???M2.L changes with newuser2princ => newuser1princ
+ * 4f629a4f0361289ca4255ab0f658fcd5 => ???M3 changes with NewUser2Princ => NewUser1Princ (doesn't depend on case of userPrincipal )
+ * db0c6bff069513e3ebb9870d29b57490 => ???M4 changes with NewUser2Sam => NewUser1Sam
+ * 45072621e56b1c113a4e04a8ff68cd0e => ???M5 changes with NewUser2Sam => NewUser1Sam (doesn't depend on case of sAMAccountName)
+ * 11d1220abc44a9c10cf91ef4a9c1de02 => ???M4.U changes with NEWUSER2SAM => NEWUSER1SAM
+ */
+
+ /*
+ * sAMAccountName, netbios_domain
+ */
+ {
+ .user = &sAMAccountName,
+ .realm = &netbios_domain,
+ },
+ {
+ .user = &sAMAccountName_l,
+ .realm = &netbios_domain_l,
+ },
+ {
+ .user = &sAMAccountName_u,
+ .realm = &netbios_domain_u,
+ },
+ {
+ .user = &sAMAccountName,
+ .realm = &netbios_domain_u,
+ },
+ {
+ .user = &sAMAccountName,
+ .realm = &netbios_domain_l,
+ },
+ {
+ .user = &sAMAccountName_u,
+ .realm = &netbios_domain_l,
+ },
+ {
+ .user = &sAMAccountName_l,
+ .realm = &netbios_domain_u,
+ },
+ /*
+ * sAMAccountName, dns_domain
+ */
+ {
+ .user = &sAMAccountName,
+ .realm = &dns_domain,
+ },
+ {
+ .user = &sAMAccountName_l,
+ .realm = &dns_domain_l,
+ },
+ {
+ .user = &sAMAccountName_u,
+ .realm = &dns_domain_u,
+ },
+ {
+ .user = &sAMAccountName,
+ .realm = &dns_domain_u,
+ },
+ {
+ .user = &sAMAccountName,
+ .realm = &dns_domain_l,
+ },
+ {
+ .user = &sAMAccountName_u,
+ .realm = &dns_domain_l,
+ },
+ {
+ .user = &sAMAccountName_l,
+ .realm = &dns_domain_u,
+ },
+ /*
+ * userPrincipalName, no realm
+ */
+ {
+ .user = &userPrincipalName,
+ },
+ {
+ /*
+ * NOTE: w2k3 messes this up, if the user has a real userPrincipalName,
+ * the fallback to the sAMAccountName based userPrincipalName is correct
+ */
+ .user = &userPrincipalName_l,
+ },
+ {
+ .user = &userPrincipalName_u,
+ },
+ /*
+ * nt4dom\sAMAccountName, no realm
+ */
+ {
+ .user = &sAMAccountName,
+ .nt4dom = &netbios_domain
+ },
+ {
+ .user = &sAMAccountName_l,
+ .nt4dom = &netbios_domain_l
+ },
+ {
+ .user = &sAMAccountName_u,
+ .nt4dom = &netbios_domain_u
+ },
+
+ /*
+ * the following ones are guessed depending on the technet2 article
+ * but not reproducable on a w2k3 server
+ */
+ /* sAMAccountName with "Digest" realm */
+ {
+ .user = &sAMAccountName,
+ .realm = &digest
+ },
+ {
+ .user = &sAMAccountName_l,
+ .realm = &digest
+ },
+ {
+ .user = &sAMAccountName_u,
+ .realm = &digest
+ },
+ /* userPrincipalName with "Digest" realm */
+ {
+ .user = &userPrincipalName,
+ .realm = &digest
+ },
+ {
+ .user = &userPrincipalName_l,
+ .realm = &digest
+ },
+ {
+ .user = &userPrincipalName_u,
+ .realm = &digest
+ },
+ /* nt4dom\\sAMAccountName with "Digest" realm */
+ {
+ .user = &sAMAccountName,
+ .nt4dom = &netbios_domain,
+ .realm = &digest
+ },
+ {
+ .user = &sAMAccountName_l,
+ .nt4dom = &netbios_domain_l,
+ .realm = &digest
+ },
+ {
+ .user = &sAMAccountName_u,
+ .nt4dom = &netbios_domain_u,
+ .realm = &digest
+ },
+ };
+
+ /* prepare DATA_BLOB's used in the combinations array */
+ sAMAccountName = data_blob_string_const(io->u.sAMAccountName);
+ sAMAccountName_l = data_blob_string_const(strlower_talloc(io->ac, io->u.sAMAccountName));
+ if (!sAMAccountName_l.data) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ sAMAccountName_u = data_blob_string_const(strupper_talloc(io->ac, io->u.sAMAccountName));
+ if (!sAMAccountName_u.data) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* if the user doesn't have a userPrincipalName, create one (with lower case realm) */
+ if (!user_principal_name) {
+ user_principal_name = talloc_asprintf(io->ac, "%s@%s",
+ io->u.sAMAccountName,
+ io->domain->dns_domain);
+ if (!user_principal_name) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ }
+ userPrincipalName = data_blob_string_const(user_principal_name);
+ userPrincipalName_l = data_blob_string_const(strlower_talloc(io->ac, user_principal_name));
+ if (!userPrincipalName_l.data) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ userPrincipalName_u = data_blob_string_const(strupper_talloc(io->ac, user_principal_name));
+ if (!userPrincipalName_u.data) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ netbios_domain = data_blob_string_const(io->domain->netbios_domain);
+ netbios_domain_l = data_blob_string_const(strlower_talloc(io->ac, io->domain->netbios_domain));
+ if (!netbios_domain_l.data) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ netbios_domain_u = data_blob_string_const(strupper_talloc(io->ac, io->domain->netbios_domain));
+ if (!netbios_domain_u.data) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ dns_domain = data_blob_string_const(io->domain->dns_domain);
+ dns_domain_l = data_blob_string_const(io->domain->dns_domain);
+ dns_domain_u = data_blob_string_const(io->domain->realm);
+
+ digest = data_blob_string_const("Digest");
+
+ delim = data_blob_string_const(":");
+ backslash = data_blob_string_const("\\");
+
+ pdb->num_hashes = ARRAY_SIZE(wdigest);
+ pdb->hashes = talloc_array(io->ac, struct package_PrimaryWDigestHash, pdb->num_hashes);
+ if (!pdb->hashes) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ for (i=0; i < ARRAY_SIZE(wdigest); i++) {
+ struct MD5Context md5;
+ MD5Init(&md5);
+ if (wdigest[i].nt4dom) {
+ MD5Update(&md5, wdigest[i].nt4dom->data, wdigest[i].nt4dom->length);
+ MD5Update(&md5, backslash.data, backslash.length);
+ }
+ MD5Update(&md5, wdigest[i].user->data, wdigest[i].user->length);
+ MD5Update(&md5, delim.data, delim.length);
+ if (wdigest[i].realm) {
+ MD5Update(&md5, wdigest[i].realm->data, wdigest[i].realm->length);
+ }
+ MD5Update(&md5, delim.data, delim.length);
+ MD5Update(&md5, io->n.cleartext_utf8->data, io->n.cleartext_utf8->length);
+ MD5Final(pdb->hashes[i].hash, &md5);
+ }
+
+ return LDB_SUCCESS;
+}
+
+static int setup_supplemental_field(struct setup_password_fields_io *io)
+{
+ struct ldb_context *ldb;
+ struct supplementalCredentialsBlob scb;
+ struct supplementalCredentialsBlob _old_scb;
+ struct supplementalCredentialsBlob *old_scb = NULL;
+ /* Packages + (Kerberos-Newer-Keys, Kerberos, WDigest and CLEARTEXT) */
+ uint32_t num_names = 0;
+ const char *names[1+4];
+ uint32_t num_packages = 0;
+ struct supplementalCredentialsPackage packages[1+4];
+ /* Packages */
+ struct supplementalCredentialsPackage *pp = NULL;
+ struct package_PackagesBlob pb;
+ DATA_BLOB pb_blob;
+ char *pb_hexstr;
+ /* Primary:Kerberos-Newer-Keys */
+ const char **nkn = NULL;
+ struct supplementalCredentialsPackage *pkn = NULL;
+ struct package_PrimaryKerberosBlob pknb;
+ DATA_BLOB pknb_blob;
+ char *pknb_hexstr;
+ /* Primary:Kerberos */
+ const char **nk = NULL;
+ struct supplementalCredentialsPackage *pk = NULL;
+ struct package_PrimaryKerberosBlob pkb;
+ DATA_BLOB pkb_blob;
+ char *pkb_hexstr;
+ /* Primary:WDigest */
+ const char **nd = NULL;
+ struct supplementalCredentialsPackage *pd = NULL;
+ struct package_PrimaryWDigestBlob pdb;
+ DATA_BLOB pdb_blob;
+ char *pdb_hexstr;
+ /* Primary:CLEARTEXT */
+ const char **nc = NULL;
+ struct supplementalCredentialsPackage *pc = NULL;
+ struct package_PrimaryCLEARTEXTBlob pcb;
+ DATA_BLOB pcb_blob;
+ char *pcb_hexstr;
+ int ret;
+ enum ndr_err_code ndr_err;
+ uint8_t zero16[16];
+ bool do_newer_keys = false;
+ bool do_cleartext = false;
+
+ ZERO_STRUCT(zero16);
+ ZERO_STRUCT(names);
+
+ ldb = ldb_module_get_ctx(io->ac->module);
+
+ if (!io->n.cleartext_utf8) {
+ /*
+ * when we don't have a cleartext password
+ * we can't setup a supplementalCredential value
+ */
+ return LDB_SUCCESS;
+ }
+
+ /* if there's an old supplementaCredentials blob then parse it */
+ if (io->o.supplemental) {
+ ndr_err = ndr_pull_struct_blob_all(io->o.supplemental, io->ac,
+ lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
+ &_old_scb,
+ (ndr_pull_flags_fn_t)ndr_pull_supplementalCredentialsBlob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
+ ldb_asprintf_errstring(ldb,
+ "setup_supplemental_field: "
+ "failed to pull old supplementalCredentialsBlob: %s",
+ nt_errstr(status));
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if (_old_scb.sub.signature == SUPPLEMENTAL_CREDENTIALS_SIGNATURE) {
+ old_scb = &_old_scb;
+ } else {
+ ldb_debug(ldb, LDB_DEBUG_ERROR,
+ "setup_supplemental_field: "
+ "supplementalCredentialsBlob signature[0x%04X] expected[0x%04X]",
+ _old_scb.sub.signature, SUPPLEMENTAL_CREDENTIALS_SIGNATURE);
+ }
+ }
+
+ /* TODO: do the correct check for this, it maybe depends on the functional level? */
+ do_newer_keys = lp_parm_bool(ldb_get_opaque(ldb, "loadparm"),
+ NULL, "password_hash", "create_aes_key", false);
+
+ if (io->domain->store_cleartext &&
+ (io->u.user_account_control & UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED)) {
+ do_cleartext = true;
+ }
+
+ /*
+ * The ordering is this
+ *
+ * Primary:Kerberos-Newer-Keys (optional)
+ * Primary:Kerberos
+ * Primary:WDigest
+ * Primary:CLEARTEXT (optional)
+ *
+ * And the 'Packages' package is insert before the last
+ * other package.
+ */
+ if (do_newer_keys) {
+ /* Primary:Kerberos-Newer-Keys */
+ nkn = &names[num_names++];
+ pkn = &packages[num_packages++];
+ }
+
+ /* Primary:Kerberos */
+ nk = &names[num_names++];
+ pk = &packages[num_packages++];
+
+ if (!do_cleartext) {
+ /* Packages */
+ pp = &packages[num_packages++];
+ }
+
+ /* Primary:WDigest */
+ nd = &names[num_names++];
+ pd = &packages[num_packages++];
+
+ if (do_cleartext) {
+ /* Packages */
+ pp = &packages[num_packages++];
+
+ /* Primary:CLEARTEXT */
+ nc = &names[num_names++];
+ pc = &packages[num_packages++];
+ }
+
+ if (pkn) {
+ /*
+ * setup 'Primary:Kerberos-Newer-Keys' element
+ */
+ *nkn = "Kerberos-Newer-Keys";
+
+ ret = setup_primary_kerberos_newer(io, old_scb, &pknb);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ ndr_err = ndr_push_struct_blob(&pknb_blob, io->ac,
+ lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
+ &pknb,
+ (ndr_push_flags_fn_t)ndr_push_package_PrimaryKerberosBlob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
+ ldb_asprintf_errstring(ldb,
+ "setup_supplemental_field: "
+ "failed to push package_PrimaryKerberosNeverBlob: %s",
+ nt_errstr(status));
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ pknb_hexstr = data_blob_hex_string(io->ac, &pknb_blob);
+ if (!pknb_hexstr) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ pkn->name = "Primary:Kerberos-Newer-Keys";
+ pkn->reserved = 1;
+ pkn->data = pknb_hexstr;
+ }
+
+ /*
+ * setup 'Primary:Kerberos' element
+ */
+ *nk = "Kerberos";
+
+ ret = setup_primary_kerberos(io, old_scb, &pkb);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ ndr_err = ndr_push_struct_blob(&pkb_blob, io->ac,
+ lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
+ &pkb,
+ (ndr_push_flags_fn_t)ndr_push_package_PrimaryKerberosBlob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
+ ldb_asprintf_errstring(ldb,
+ "setup_supplemental_field: "
+ "failed to push package_PrimaryKerberosBlob: %s",
+ nt_errstr(status));
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ pkb_hexstr = data_blob_hex_string(io->ac, &pkb_blob);
+ if (!pkb_hexstr) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ pk->name = "Primary:Kerberos";
+ pk->reserved = 1;
+ pk->data = pkb_hexstr;
+
+ /*
+ * setup 'Primary:WDigest' element
+ */
+ *nd = "WDigest";
+
+ ret = setup_primary_wdigest(io, old_scb, &pdb);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ ndr_err = ndr_push_struct_blob(&pdb_blob, io->ac,
+ lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
+ &pdb,
+ (ndr_push_flags_fn_t)ndr_push_package_PrimaryWDigestBlob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
+ ldb_asprintf_errstring(ldb,
+ "setup_supplemental_field: "
+ "failed to push package_PrimaryWDigestBlob: %s",
+ nt_errstr(status));
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ pdb_hexstr = data_blob_hex_string(io->ac, &pdb_blob);
+ if (!pdb_hexstr) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ pd->name = "Primary:WDigest";
+ pd->reserved = 1;
+ pd->data = pdb_hexstr;
+
+ /*
+ * setup 'Primary:CLEARTEXT' element
+ */
+ if (pc) {
+ *nc = "CLEARTEXT";
+
+ pcb.cleartext = *io->n.cleartext_utf16;
+
+ ndr_err = ndr_push_struct_blob(&pcb_blob, io->ac,
+ lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
+ &pcb,
+ (ndr_push_flags_fn_t)ndr_push_package_PrimaryCLEARTEXTBlob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
+ ldb_asprintf_errstring(ldb,
+ "setup_supplemental_field: "
+ "failed to push package_PrimaryCLEARTEXTBlob: %s",
+ nt_errstr(status));
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ pcb_hexstr = data_blob_hex_string(io->ac, &pcb_blob);
+ if (!pcb_hexstr) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ pc->name = "Primary:CLEARTEXT";
+ pc->reserved = 1;
+ pc->data = pcb_hexstr;
+ }
+
+ /*
+ * setup 'Packages' element
+ */
+ pb.names = names;
+ ndr_err = ndr_push_struct_blob(&pb_blob, io->ac,
+ lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
+ &pb,
+ (ndr_push_flags_fn_t)ndr_push_package_PackagesBlob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
+ ldb_asprintf_errstring(ldb,
+ "setup_supplemental_field: "
+ "failed to push package_PackagesBlob: %s",
+ nt_errstr(status));
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ pb_hexstr = data_blob_hex_string(io->ac, &pb_blob);
+ if (!pb_hexstr) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ pp->name = "Packages";
+ pp->reserved = 2;
+ pp->data = pb_hexstr;
+
+ /*
+ * setup 'supplementalCredentials' value
+ */
+ ZERO_STRUCT(scb);
+ scb.sub.num_packages = num_packages;
+ scb.sub.packages = packages;
+
+ ndr_err = ndr_push_struct_blob(&io->g.supplemental, io->ac,
+ lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
+ &scb,
+ (ndr_push_flags_fn_t)ndr_push_supplementalCredentialsBlob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
+ ldb_asprintf_errstring(ldb,
+ "setup_supplemental_field: "
+ "failed to push supplementalCredentialsBlob: %s",
+ nt_errstr(status));
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ return LDB_SUCCESS;
+}
+
+static int setup_last_set_field(struct setup_password_fields_io *io)
+{
+ /* set it as now */
+ unix_to_nt_time(&io->g.last_set, time(NULL));
+
+ return LDB_SUCCESS;
+}
+
+static int setup_kvno_field(struct setup_password_fields_io *io)
+{
+ /* increment by one */
+ io->g.kvno = io->o.kvno + 1;
+
+ return LDB_SUCCESS;
+}
+
+static int setup_password_fields(struct setup_password_fields_io *io)
+{
+ struct ldb_context *ldb;
+ bool ok;
+ int ret;
+ size_t converted_pw_len;
+
+ ldb = ldb_module_get_ctx(io->ac->module);
+
+ /*
+ * refuse the change if someone want to change the cleartext
+ * and supply his own hashes at the same time...
+ */
+ if ((io->n.cleartext_utf8 || io->n.cleartext_utf16) && (io->n.nt_hash || io->n.lm_hash)) {
+ ldb_asprintf_errstring(ldb,
+ "setup_password_fields: "
+ "it's only allowed to set the cleartext password or the password hashes");
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+
+ if (io->n.cleartext_utf8 && io->n.cleartext_utf16) {
+ ldb_asprintf_errstring(ldb,
+ "setup_password_fields: "
+ "it's only allowed to set the cleartext password as userPassword or clearTextPasssword, not both at once");
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+
+ if (io->n.cleartext_utf8) {
+ char **cleartext_utf16_str;
+ struct ldb_val *cleartext_utf16_blob;
+ io->n.cleartext_utf16 = cleartext_utf16_blob = talloc(io->ac, struct ldb_val);
+ if (!io->n.cleartext_utf16) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ if (!convert_string_talloc_convenience(io->ac, lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
+ CH_UTF8, CH_UTF16, io->n.cleartext_utf8->data, io->n.cleartext_utf8->length,
+ (void **)&cleartext_utf16_str, &converted_pw_len, false)) {
+ ldb_asprintf_errstring(ldb,
+ "setup_password_fields: "
+ "failed to generate UTF16 password from cleartext UTF8 password");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ *cleartext_utf16_blob = data_blob_const(cleartext_utf16_str, converted_pw_len);
+ } else if (io->n.cleartext_utf16) {
+ char *cleartext_utf8_str;
+ struct ldb_val *cleartext_utf8_blob;
+ io->n.cleartext_utf8 = cleartext_utf8_blob = talloc(io->ac, struct ldb_val);
+ if (!io->n.cleartext_utf8) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ if (!convert_string_talloc_convenience(io->ac, lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
+ CH_UTF16MUNGED, CH_UTF8, io->n.cleartext_utf16->data, io->n.cleartext_utf16->length,
+ (void **)&cleartext_utf8_str, &converted_pw_len, false)) {
+ /* We can't bail out entirely, as these unconvertable passwords are frustratingly valid */
+ io->n.cleartext_utf8 = NULL;
+ talloc_free(cleartext_utf8_blob);
+ }
+ *cleartext_utf8_blob = data_blob_const(cleartext_utf8_str, converted_pw_len);
+ }
+ if (io->n.cleartext_utf16) {
+ struct samr_Password *nt_hash;
+ nt_hash = talloc(io->ac, struct samr_Password);
+ if (!nt_hash) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ io->n.nt_hash = nt_hash;
+
+ /* compute the new nt hash */
+ mdfour(nt_hash->hash, io->n.cleartext_utf16->data, io->n.cleartext_utf16->length);
+ }
+
+ if (io->n.cleartext_utf8) {
+ struct samr_Password *lm_hash;
+ char *cleartext_unix;
+ if (convert_string_talloc_convenience(io->ac, lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
+ CH_UTF8, CH_UNIX, io->n.cleartext_utf8->data, io->n.cleartext_utf8->length,
+ (void **)&cleartext_unix, &converted_pw_len, false)) {
+ lm_hash = talloc(io->ac, struct samr_Password);
+ if (!lm_hash) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* compute the new lm hash. */
+ ok = E_deshash((char *)cleartext_unix, lm_hash->hash);
+ if (ok) {
+ io->n.lm_hash = lm_hash;
+ } else {
+ talloc_free(lm_hash->hash);
+ }
+ }
+
+ ret = setup_kerberos_keys(io);
+ if (ret != 0) {
+ return ret;
+ }
+ }
+
+ ret = setup_nt_fields(io);
+ if (ret != 0) {
+ return ret;
+ }
+
+ ret = setup_lm_fields(io);
+ if (ret != 0) {
+ return ret;
+ }
+
+ ret = setup_supplemental_field(io);
+ if (ret != 0) {
+ return ret;
+ }
+
+ ret = setup_last_set_field(io);
+ if (ret != 0) {
+ return ret;
+ }
+
+ ret = setup_kvno_field(io);
+ if (ret != 0) {
+ return ret;
+ }
+
+ return LDB_SUCCESS;
+}
+
+static struct ph_context *ph_init_context(struct ldb_module *module,
+ struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct ph_context *ac;
+
+ ldb = ldb_module_get_ctx(module);
+
+ ac = talloc_zero(req, struct ph_context);
+ if (ac == NULL) {
+ ldb_set_errstring(ldb, "Out of Memory");
+ return NULL;
+ }
+
+ ac->module = module;
+ ac->req = req;
+
+ return ac;
+}
+
+static int ph_op_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+ struct ph_context *ac;
+
+ ac = talloc_get_type(req->context, struct ph_context);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ if (ares->type != LDB_REPLY_DONE) {
+ talloc_free(ares);
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+}
+
+static int password_hash_add_do_add(struct ph_context *ac);
+static int ph_modify_callback(struct ldb_request *req, struct ldb_reply *ares);
+static int password_hash_mod_search_self(struct ph_context *ac);
+static int ph_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares);
+static int password_hash_mod_do_mod(struct ph_context *ac);
+
+static int get_domain_data_callback(struct ldb_request *req,
+ struct ldb_reply *ares)
+{
+ struct ldb_context *ldb;
+ struct domain_data *data;
+ struct ph_context *ac;
+ int ret;
+ char *tmp;
+ char *p;
+
+ ac = talloc_get_type(req->context, struct ph_context);
+ ldb = ldb_module_get_ctx(ac->module);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+ if (ac->domain != NULL) {
+ ldb_set_errstring(ldb, "Too many results");
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ data = talloc_zero(ac, struct domain_data);
+ if (data == NULL) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ data->pwdProperties = samdb_result_uint(ares->message, "pwdProperties", 0);
+ data->store_cleartext = data->pwdProperties & DOMAIN_PASSWORD_STORE_CLEARTEXT;
+ data->pwdHistoryLength = samdb_result_uint(ares->message, "pwdHistoryLength", 0);
+
+ /* For a domain DN, this puts things in dotted notation */
+ /* For builtin domains, this will give details for the host,
+ * but that doesn't really matter, as it's just used for salt
+ * and kerberos principals, which don't exist here */
+
+ tmp = ldb_dn_canonical_string(data, ares->message->dn);
+ if (!tmp) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ /* But it puts a trailing (or just before 'builtin') / on things, so kill that */
+ p = strchr(tmp, '/');
+ if (p) {
+ p[0] = '\0';
+ }
+
+ data->dns_domain = strlower_talloc(data, tmp);
+ if (data->dns_domain == NULL) {
+ ldb_oom(ldb);
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ data->realm = strupper_talloc(data, tmp);
+ if (data->realm == NULL) {
+ ldb_oom(ldb);
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ /* FIXME: NetbIOS name is *always* the first domain component ?? -SSS */
+ p = strchr(tmp, '.');
+ if (p) {
+ p[0] = '\0';
+ }
+ data->netbios_domain = strupper_talloc(data, tmp);
+ if (data->netbios_domain == NULL) {
+ ldb_oom(ldb);
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ talloc_free(tmp);
+ ac->domain = data;
+ break;
+
+ case LDB_REPLY_DONE:
+
+ /* call the next step */
+ switch (ac->req->operation) {
+ case LDB_ADD:
+ ret = password_hash_add_do_add(ac);
+ break;
+
+ case LDB_MODIFY:
+ ret = password_hash_mod_do_mod(ac);
+ break;
+
+ default:
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ break;
+ }
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+
+ case LDB_REPLY_REFERRAL:
+ /* ignore */
+ break;
+ }
+
+ talloc_free(ares);
+ return LDB_SUCCESS;
+}
+
+static int build_domain_data_request(struct ph_context *ac)
+{
+ /* attrs[] is returned from this function in
+ ac->dom_req->op.search.attrs, so it must be static, as
+ otherwise the compiler can put it on the stack */
+ struct ldb_context *ldb;
+ static const char * const attrs[] = { "pwdProperties", "pwdHistoryLength", NULL };
+ char *filter;
+
+ ldb = ldb_module_get_ctx(ac->module);
+
+ filter = talloc_asprintf(ac,
+ "(&(objectSid=%s)(|(|(objectClass=domain)(objectClass=builtinDomain))(objectClass=samba4LocalDomain)))",
+ ldap_encode_ndr_dom_sid(ac, ac->domain_sid));
+ if (filter == NULL) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ return ldb_build_search_req(&ac->dom_req, ldb, ac,
+ ldb_get_default_basedn(ldb),
+ LDB_SCOPE_SUBTREE,
+ filter, attrs,
+ NULL,
+ ac, get_domain_data_callback,
+ ac->req);
+}
+
+static int password_hash_add(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct ph_context *ac;
+ struct ldb_message_element *sambaAttr;
+ struct ldb_message_element *clearTextPasswordAttr;
+ struct ldb_message_element *ntAttr;
+ struct ldb_message_element *lmAttr;
+ int ret;
+
+ ldb = ldb_module_get_ctx(module);
+
+ ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_add\n");
+
+ if (ldb_dn_is_special(req->op.add.message->dn)) { /* do not manipulate our control entries */
+ return ldb_next_request(module, req);
+ }
+
+ /* If the caller is manipulating the local passwords directly, let them pass */
+ if (ldb_dn_compare_base(ldb_dn_new(req, ldb, LOCAL_BASE),
+ req->op.add.message->dn) == 0) {
+ return ldb_next_request(module, req);
+ }
+
+ /* nobody must touch these fields */
+ if (ldb_msg_find_element(req->op.add.message, "ntPwdHistory")) {
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+ if (ldb_msg_find_element(req->op.add.message, "lmPwdHistory")) {
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+ if (ldb_msg_find_element(req->op.add.message, "supplementalCredentials")) {
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+
+ /* If no part of this ADD touches the userPassword, or the NT
+ * or LM hashes, then we don't need to make any changes. */
+
+ sambaAttr = ldb_msg_find_element(req->op.mod.message, "userPassword");
+ clearTextPasswordAttr = ldb_msg_find_element(req->op.mod.message, "clearTextPassword");
+ ntAttr = ldb_msg_find_element(req->op.mod.message, "unicodePwd");
+ lmAttr = ldb_msg_find_element(req->op.mod.message, "dBCSPwd");
+
+ if ((!sambaAttr) && (!clearTextPasswordAttr) && (!ntAttr) && (!lmAttr)) {
+ return ldb_next_request(module, req);
+ }
+
+ /* if it is not an entry of type person its an error */
+ /* TODO: remove this when userPassword will be in schema */
+ if (!ldb_msg_check_string_attribute(req->op.add.message, "objectClass", "person")) {
+ ldb_set_errstring(ldb, "Cannot set a password on entry that does not have objectClass 'person'");
+ return LDB_ERR_OBJECT_CLASS_VIOLATION;
+ }
+
+ /* check userPassword is single valued here */
+ /* TODO: remove this when userPassword will be single valued in schema */
+ if (sambaAttr && sambaAttr->num_values > 1) {
+ ldb_set_errstring(ldb, "mupltiple values for userPassword not allowed!\n");
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+ if (clearTextPasswordAttr && clearTextPasswordAttr->num_values > 1) {
+ ldb_set_errstring(ldb, "mupltiple values for clearTextPassword not allowed!\n");
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+
+ if (ntAttr && (ntAttr->num_values > 1)) {
+ ldb_set_errstring(ldb, "mupltiple values for unicodePwd not allowed!\n");
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+ if (lmAttr && (lmAttr->num_values > 1)) {
+ ldb_set_errstring(ldb, "mupltiple values for dBCSPwd not allowed!\n");
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+
+ if (sambaAttr && sambaAttr->num_values == 0) {
+ ldb_set_errstring(ldb, "userPassword must have a value!\n");
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+
+ if (clearTextPasswordAttr && clearTextPasswordAttr->num_values == 0) {
+ ldb_set_errstring(ldb, "clearTextPassword must have a value!\n");
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+
+ if (ntAttr && (ntAttr->num_values == 0)) {
+ ldb_set_errstring(ldb, "unicodePwd must have a value!\n");
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+ if (lmAttr && (lmAttr->num_values == 0)) {
+ ldb_set_errstring(ldb, "dBCSPwd must have a value!\n");
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+
+ ac = ph_init_context(module, req);
+ if (ac == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* get user domain data */
+ ac->domain_sid = samdb_result_sid_prefix(ac, req->op.add.message, "objectSid");
+ if (ac->domain_sid == NULL) {
+ ldb_debug(ldb, LDB_DEBUG_ERROR,
+ "can't handle entry with missing objectSid!\n");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = build_domain_data_request(ac);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ return ldb_next_request(module, ac->dom_req);
+}
+
+static int password_hash_add_do_add(struct ph_context *ac)
+{
+ struct ldb_context *ldb;
+ struct ldb_request *down_req;
+ struct smb_krb5_context *smb_krb5_context;
+ struct ldb_message *msg;
+ struct setup_password_fields_io io;
+ int ret;
+
+ ldb = ldb_module_get_ctx(ac->module);
+
+ msg = ldb_msg_copy_shallow(ac, ac->req->op.add.message);
+ if (msg == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* Some operations below require kerberos contexts */
+ if (smb_krb5_init_context(ac,
+ ldb_get_event_context(ldb),
+ (struct loadparm_context *)ldb_get_opaque(ldb, "loadparm"),
+ &smb_krb5_context) != 0) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ZERO_STRUCT(io);
+ io.ac = ac;
+ io.domain = ac->domain;
+ io.smb_krb5_context = smb_krb5_context;
+
+ io.u.user_account_control = samdb_result_uint(msg, "userAccountControl", 0);
+ io.u.sAMAccountName = samdb_result_string(msg, "samAccountName", NULL);
+ io.u.user_principal_name = samdb_result_string(msg, "userPrincipalName", NULL);
+ io.u.is_computer = ldb_msg_check_string_attribute(msg, "objectClass", "computer");
+
+ io.n.cleartext_utf8 = ldb_msg_find_ldb_val(msg, "userPassword");
+ io.n.cleartext_utf16 = ldb_msg_find_ldb_val(msg, "clearTextPassword");
+ io.n.nt_hash = samdb_result_hash(io.ac, msg, "unicodePwd");
+ io.n.lm_hash = samdb_result_hash(io.ac, msg, "dBCSPwd");
+
+ /* remove attributes */
+ if (io.n.cleartext_utf8) ldb_msg_remove_attr(msg, "userPassword");
+ if (io.n.cleartext_utf16) ldb_msg_remove_attr(msg, "clearTextPassword");
+ if (io.n.nt_hash) ldb_msg_remove_attr(msg, "unicodePwd");
+ if (io.n.lm_hash) ldb_msg_remove_attr(msg, "dBCSPwd");
+ ldb_msg_remove_attr(msg, "pwdLastSet");
+ io.o.kvno = samdb_result_uint(msg, "msDs-KeyVersionNumber", 1) - 1;
+ ldb_msg_remove_attr(msg, "msDs-KeyVersionNumber");
+
+ ret = setup_password_fields(&io);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ if (io.g.nt_hash) {
+ ret = samdb_msg_add_hash(ldb, ac, msg,
+ "unicodePwd", io.g.nt_hash);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+ if (io.g.lm_hash) {
+ ret = samdb_msg_add_hash(ldb, ac, msg,
+ "dBCSPwd", io.g.lm_hash);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+ if (io.g.nt_history_len > 0) {
+ ret = samdb_msg_add_hashes(ac, msg,
+ "ntPwdHistory",
+ io.g.nt_history,
+ io.g.nt_history_len);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+ if (io.g.lm_history_len > 0) {
+ ret = samdb_msg_add_hashes(ac, msg,
+ "lmPwdHistory",
+ io.g.lm_history,
+ io.g.lm_history_len);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+ if (io.g.supplemental.length > 0) {
+ ret = ldb_msg_add_value(msg, "supplementalCredentials",
+ &io.g.supplemental, NULL);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+ ret = samdb_msg_add_uint64(ldb, ac, msg,
+ "pwdLastSet",
+ io.g.last_set);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ ret = samdb_msg_add_uint(ldb, ac, msg,
+ "msDs-KeyVersionNumber",
+ io.g.kvno);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ ret = ldb_build_add_req(&down_req, ldb, ac,
+ msg,
+ ac->req->controls,
+ ac, ph_op_callback,
+ ac->req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ return ldb_next_request(ac->module, down_req);
+}
+
+static int password_hash_modify(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct ph_context *ac;
+ struct ldb_message_element *sambaAttr;
+ struct ldb_message_element *clearTextAttr;
+ struct ldb_message_element *ntAttr;
+ struct ldb_message_element *lmAttr;
+ struct ldb_message *msg;
+ struct ldb_request *down_req;
+ int ret;
+
+ ldb = ldb_module_get_ctx(module);
+
+ ldb_debug(ldb, LDB_DEBUG_TRACE, "password_hash_modify\n");
+
+ if (ldb_dn_is_special(req->op.mod.message->dn)) { /* do not manipulate our control entries */
+ return ldb_next_request(module, req);
+ }
+
+ /* If the caller is manipulating the local passwords directly, let them pass */
+ if (ldb_dn_compare_base(ldb_dn_new(req, ldb, LOCAL_BASE),
+ req->op.mod.message->dn) == 0) {
+ return ldb_next_request(module, req);
+ }
+
+ /* nobody must touch password Histories */
+ if (ldb_msg_find_element(req->op.add.message, "ntPwdHistory")) {
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+ if (ldb_msg_find_element(req->op.add.message, "lmPwdHistory")) {
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+ if (ldb_msg_find_element(req->op.add.message, "supplementalCredentials")) {
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+
+ sambaAttr = ldb_msg_find_element(req->op.mod.message, "userPassword");
+ clearTextAttr = ldb_msg_find_element(req->op.mod.message, "clearTextPassword");
+ ntAttr = ldb_msg_find_element(req->op.mod.message, "unicodePwd");
+ lmAttr = ldb_msg_find_element(req->op.mod.message, "dBCSPwd");
+
+ /* If no part of this touches the userPassword OR
+ * clearTextPassword OR unicodePwd and/or dBCSPwd, then we
+ * don't need to make any changes. For password changes/set
+ * there should be a 'delete' or a 'modify' on this
+ * attribute. */
+ if ((!sambaAttr) && (!clearTextAttr) && (!ntAttr) && (!lmAttr)) {
+ return ldb_next_request(module, req);
+ }
+
+ /* check passwords are single valued here */
+ /* TODO: remove this when passwords will be single valued in schema */
+ if (sambaAttr && (sambaAttr->num_values > 1)) {
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+ if (clearTextAttr && (clearTextAttr->num_values > 1)) {
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+ if (ntAttr && (ntAttr->num_values > 1)) {
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+ if (lmAttr && (lmAttr->num_values > 1)) {
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+
+ ac = ph_init_context(module, req);
+ if (!ac) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* use a new message structure so that we can modify it */
+ msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
+ if (msg == NULL) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* - remove any modification to the password from the first commit
+ * we will make the real modification later */
+ if (sambaAttr) ldb_msg_remove_attr(msg, "userPassword");
+ if (clearTextAttr) ldb_msg_remove_attr(msg, "clearTextPassword");
+ if (ntAttr) ldb_msg_remove_attr(msg, "unicodePwd");
+ if (lmAttr) ldb_msg_remove_attr(msg, "dBCSPwd");
+
+ /* if there was nothing else to be modified skip to next step */
+ if (msg->num_elements == 0) {
+ return password_hash_mod_search_self(ac);
+ }
+
+ ret = ldb_build_mod_req(&down_req, ldb, ac,
+ msg,
+ req->controls,
+ ac, ph_modify_callback,
+ req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ return ldb_next_request(module, down_req);
+}
+
+static int ph_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+ struct ph_context *ac;
+ int ret;
+
+ ac = talloc_get_type(req->context, struct ph_context);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ if (ares->type != LDB_REPLY_DONE) {
+ talloc_free(ares);
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ ret = password_hash_mod_search_self(ac);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+
+ talloc_free(ares);
+ return LDB_SUCCESS;
+}
+
+static int ph_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+ struct ldb_context *ldb;
+ struct ph_context *ac;
+ int ret;
+
+ ac = talloc_get_type(req->context, struct ph_context);
+ ldb = ldb_module_get_ctx(ac->module);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ /* we are interested only in the single reply (base search) */
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+
+ if (ac->search_res != NULL) {
+ ldb_set_errstring(ldb, "Too many results");
+ talloc_free(ares);
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ /* if it is not an entry of type person this is an error */
+ /* TODO: remove this when sambaPassword will be in schema */
+ if (!ldb_msg_check_string_attribute(ares->message, "objectClass", "person")) {
+ ldb_set_errstring(ldb, "Object class violation");
+ talloc_free(ares);
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OBJECT_CLASS_VIOLATION);
+ }
+
+ ac->search_res = talloc_steal(ac, ares);
+ return LDB_SUCCESS;
+
+ case LDB_REPLY_DONE:
+
+ /* get object domain sid */
+ ac->domain_sid = samdb_result_sid_prefix(ac,
+ ac->search_res->message,
+ "objectSid");
+ if (ac->domain_sid == NULL) {
+ ldb_debug(ldb, LDB_DEBUG_ERROR,
+ "can't handle entry without objectSid!\n");
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ /* get user domain data */
+ ret = build_domain_data_request(ac);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL,ret);
+ }
+
+ return ldb_next_request(ac->module, ac->dom_req);
+
+ case LDB_REPLY_REFERRAL:
+ /*ignore anything else for now */
+ break;
+ }
+
+ talloc_free(ares);
+ return LDB_SUCCESS;
+}
+
+static int password_hash_mod_search_self(struct ph_context *ac)
+{
+ struct ldb_context *ldb;
+ static const char * const attrs[] = { "userAccountControl", "lmPwdHistory",
+ "ntPwdHistory",
+ "objectSid", "msDS-KeyVersionNumber",
+ "objectClass", "userPrincipalName",
+ "sAMAccountName",
+ "dBCSPwd", "unicodePwd",
+ "supplementalCredentials",
+ NULL };
+ struct ldb_request *search_req;
+ int ret;
+
+ ldb = ldb_module_get_ctx(ac->module);
+
+ ret = ldb_build_search_req(&search_req, ldb, ac,
+ ac->req->op.mod.message->dn,
+ LDB_SCOPE_BASE,
+ "(objectclass=*)",
+ attrs,
+ NULL,
+ ac, ph_mod_search_callback,
+ ac->req);
+
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ return ldb_next_request(ac->module, search_req);
+}
+
+static int password_hash_mod_do_mod(struct ph_context *ac)
+{
+ struct ldb_context *ldb;
+ struct ldb_request *mod_req;
+ struct smb_krb5_context *smb_krb5_context;
+ struct ldb_message *msg;
+ struct ldb_message *orig_msg;
+ struct ldb_message *searched_msg;
+ struct setup_password_fields_io io;
+ const struct ldb_val *quoted_utf16;
+ int ret;
+
+ ldb = ldb_module_get_ctx(ac->module);
+
+ /* use a new message structure so that we can modify it */
+ msg = ldb_msg_new(ac);
+ if (msg == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* modify dn */
+ msg->dn = ac->req->op.mod.message->dn;
+
+ /* Some operations below require kerberos contexts */
+ if (smb_krb5_init_context(ac,
+ ldb_get_event_context(ldb),
+ (struct loadparm_context *)ldb_get_opaque(ldb, "loadparm"),
+ &smb_krb5_context) != 0) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ orig_msg = discard_const(ac->req->op.mod.message);
+ searched_msg = ac->search_res->message;
+
+ ZERO_STRUCT(io);
+ io.ac = ac;
+ io.domain = ac->domain;
+ io.smb_krb5_context = smb_krb5_context;
+
+ io.u.user_account_control = samdb_result_uint(searched_msg, "userAccountControl", 0);
+ io.u.sAMAccountName = samdb_result_string(searched_msg, "samAccountName", NULL);
+ io.u.user_principal_name = samdb_result_string(searched_msg, "userPrincipalName", NULL);
+ io.u.is_computer = ldb_msg_check_string_attribute(searched_msg, "objectClass", "computer");
+
+ io.n.cleartext_utf8 = ldb_msg_find_ldb_val(orig_msg, "userPassword");
+ io.n.cleartext_utf16 = ldb_msg_find_ldb_val(orig_msg, "clearTextPassword");
+
+ /* this rather strange looking piece of code is there to
+ handle a ldap client setting a password remotely using the
+ unicodePwd ldap field. The syntax is that the password is
+ in UTF-16LE, with a " at either end. Unfortunately the
+ unicodePwd field is also used to store the nt hashes
+ internally in Samba, and is used in the nt hash format on
+ the wire in DRS replication, so we have a single name for
+ two distinct values. The code below leaves us with a small
+ chance (less than 1 in 2^32) of a mixup, if someone manages
+ to create a MD4 hash which starts and ends in 0x22 0x00, as
+ that would then be treated as a UTF16 password rather than
+ a nthash */
+ quoted_utf16 = ldb_msg_find_ldb_val(orig_msg, "unicodePwd");
+ if (quoted_utf16 &&
+ quoted_utf16->length >= 4 &&
+ quoted_utf16->data[0] == '"' &&
+ quoted_utf16->data[1] == 0 &&
+ quoted_utf16->data[quoted_utf16->length-2] == '"' &&
+ quoted_utf16->data[quoted_utf16->length-1] == 0) {
+ io.n.quoted_utf16.data = talloc_memdup(orig_msg, quoted_utf16->data+2, quoted_utf16->length-4);
+ io.n.quoted_utf16.length = quoted_utf16->length-4;
+ io.n.cleartext_utf16 = &io.n.quoted_utf16;
+ io.n.nt_hash = NULL;
+ } else {
+ io.n.nt_hash = samdb_result_hash(io.ac, orig_msg, "unicodePwd");
+ }
+
+ io.n.lm_hash = samdb_result_hash(io.ac, orig_msg, "dBCSPwd");
+
+ io.o.kvno = samdb_result_uint(searched_msg, "msDs-KeyVersionNumber", 0);
+ io.o.nt_history_len = samdb_result_hashes(io.ac, searched_msg, "ntPwdHistory", &io.o.nt_history);
+ io.o.lm_history_len = samdb_result_hashes(io.ac, searched_msg, "lmPwdHistory", &io.o.lm_history);
+ io.o.supplemental = ldb_msg_find_ldb_val(searched_msg, "supplementalCredentials");
+
+ ret = setup_password_fields(&io);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ /* make sure we replace all the old attributes */
+ ret = ldb_msg_add_empty(msg, "unicodePwd", LDB_FLAG_MOD_REPLACE, NULL);
+ ret = ldb_msg_add_empty(msg, "dBCSPwd", LDB_FLAG_MOD_REPLACE, NULL);
+ ret = ldb_msg_add_empty(msg, "ntPwdHistory", LDB_FLAG_MOD_REPLACE, NULL);
+ ret = ldb_msg_add_empty(msg, "lmPwdHistory", LDB_FLAG_MOD_REPLACE, NULL);
+ ret = ldb_msg_add_empty(msg, "supplementalCredentials", LDB_FLAG_MOD_REPLACE, NULL);
+ ret = ldb_msg_add_empty(msg, "pwdLastSet", LDB_FLAG_MOD_REPLACE, NULL);
+ ret = ldb_msg_add_empty(msg, "msDs-KeyVersionNumber", LDB_FLAG_MOD_REPLACE, NULL);
+
+ if (io.g.nt_hash) {
+ ret = samdb_msg_add_hash(ldb, ac, msg,
+ "unicodePwd", io.g.nt_hash);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+ if (io.g.lm_hash) {
+ ret = samdb_msg_add_hash(ldb, ac, msg,
+ "dBCSPwd", io.g.lm_hash);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+ if (io.g.nt_history_len > 0) {
+ ret = samdb_msg_add_hashes(ac, msg,
+ "ntPwdHistory",
+ io.g.nt_history,
+ io.g.nt_history_len);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+ if (io.g.lm_history_len > 0) {
+ ret = samdb_msg_add_hashes(ac, msg,
+ "lmPwdHistory",
+ io.g.lm_history,
+ io.g.lm_history_len);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+ if (io.g.supplemental.length > 0) {
+ ret = ldb_msg_add_value(msg, "supplementalCredentials",
+ &io.g.supplemental, NULL);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+ ret = samdb_msg_add_uint64(ldb, ac, msg,
+ "pwdLastSet",
+ io.g.last_set);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ ret = samdb_msg_add_uint(ldb, ac, msg,
+ "msDs-KeyVersionNumber",
+ io.g.kvno);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ ret = ldb_build_mod_req(&mod_req, ldb, ac,
+ msg,
+ ac->req->controls,
+ ac, ph_op_callback,
+ ac->req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ return ldb_next_request(ac->module, mod_req);
+}
+
+_PUBLIC_ const struct ldb_module_ops ldb_password_hash_module_ops = {
+ .name = "password_hash",
+ .add = password_hash_add,
+ .modify = password_hash_modify,
+};
diff --git a/source4/dsdb/samdb/ldb_modules/password_modules.h b/source4/dsdb/samdb/ldb_modules/password_modules.h
new file mode 100644
index 0000000000..40d0144416
--- /dev/null
+++ b/source4/dsdb/samdb/ldb_modules/password_modules.h
@@ -0,0 +1,3 @@
+/* We store these passwords under this base DN: */
+
+#define LOCAL_BASE "cn=Passwords"
diff --git a/source4/dsdb/samdb/ldb_modules/pdc_fsmo.c b/source4/dsdb/samdb/ldb_modules/pdc_fsmo.c
new file mode 100644
index 0000000000..fefaef4755
--- /dev/null
+++ b/source4/dsdb/samdb/ldb_modules/pdc_fsmo.c
@@ -0,0 +1,120 @@
+/*
+ Unix SMB/CIFS mplementation.
+
+ The module that handles the PDC FSMO Role Owner checkings
+
+ 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 <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "includes.h"
+#include "ldb_module.h"
+#include "dsdb/samdb/samdb.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "librpc/gen_ndr/ndr_drsuapi.h"
+#include "librpc/gen_ndr/ndr_drsblobs.h"
+#include "../lib/util/dlinklist.h"
+
+static int pdc_fsmo_init(struct ldb_module *module)
+{
+ struct ldb_context *ldb;
+ TALLOC_CTX *mem_ctx;
+ struct ldb_dn *pdc_dn;
+ struct dsdb_pdc_fsmo *pdc_fsmo;
+ struct ldb_result *pdc_res;
+ int ret;
+ static const char *pdc_attrs[] = {
+ "fSMORoleOwner",
+ NULL
+ };
+
+ ldb = ldb_module_get_ctx(module);
+
+ mem_ctx = talloc_new(module);
+ if (!mem_ctx) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ pdc_dn = samdb_base_dn(ldb);
+ if (!pdc_dn) {
+ ldb_debug(ldb, LDB_DEBUG_WARNING,
+ "pdc_fsmo_init: no domain dn present: (skip loading of domain details)\n");
+ talloc_free(mem_ctx);
+ return ldb_next_init(module);
+ }
+
+ pdc_fsmo = talloc_zero(mem_ctx, struct dsdb_pdc_fsmo);
+ if (!pdc_fsmo) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ldb_module_set_private(module, pdc_fsmo);
+
+ ret = ldb_search(ldb, mem_ctx, &pdc_res,
+ pdc_dn, LDB_SCOPE_BASE,
+ pdc_attrs, NULL);
+ if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+ ldb_debug(ldb, LDB_DEBUG_WARNING,
+ "pdc_fsmo_init: no domain object present: (skip loading of domain details)\n");
+ talloc_free(mem_ctx);
+ return ldb_next_init(module);
+ } else if (ret != LDB_SUCCESS) {
+ ldb_debug_set(ldb, LDB_DEBUG_FATAL,
+ "pdc_fsmo_init: failed to search the domain object: %d:%s",
+ ret, ldb_strerror(ret));
+ talloc_free(mem_ctx);
+ return ret;
+ }
+ if (pdc_res->count == 0) {
+ ldb_debug(ldb, LDB_DEBUG_WARNING,
+ "pdc_fsmo_init: no domain object present: (skip loading of domain details)\n");
+ talloc_free(mem_ctx);
+ return ldb_next_init(module);
+ } else if (pdc_res->count > 1) {
+ ldb_debug_set(ldb, LDB_DEBUG_FATAL,
+ "pdc_fsmo_init: [%u] domain objects found on a base search",
+ pdc_res->count);
+ talloc_free(mem_ctx);
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+
+ pdc_fsmo->master_dn = ldb_msg_find_attr_as_dn(ldb, mem_ctx, pdc_res->msgs[0], "fSMORoleOwner");
+ if (ldb_dn_compare(samdb_ntds_settings_dn(ldb), pdc_fsmo->master_dn) == 0) {
+ pdc_fsmo->we_are_master = true;
+ } else {
+ pdc_fsmo->we_are_master = false;
+ }
+
+ if (ldb_set_opaque(ldb, "dsdb_pdc_fsmo", pdc_fsmo) != LDB_SUCCESS) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ talloc_steal(module, pdc_fsmo);
+
+ ldb_debug(ldb, LDB_DEBUG_TRACE,
+ "pdc_fsmo_init: we are master: %s\n",
+ (pdc_fsmo->we_are_master?"yes":"no"));
+
+ talloc_free(mem_ctx);
+ return ldb_next_init(module);
+}
+
+_PUBLIC_ const struct ldb_module_ops ldb_pdc_fsmo_module_ops = {
+ .name = "pdc_fsmo",
+ .init_context = pdc_fsmo_init
+};
diff --git a/source4/dsdb/samdb/ldb_modules/proxy.c b/source4/dsdb/samdb/ldb_modules/proxy.c
new file mode 100644
index 0000000000..72b47c308d
--- /dev/null
+++ b/source4/dsdb/samdb/ldb_modules/proxy.c
@@ -0,0 +1,403 @@
+/*
+ samdb proxy module
+
+ Copyright (C) Andrew Tridgell 2005
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ ldb proxy module. At startup this looks for a record like this:
+
+ dn=@PROXYINFO
+ url=destination url
+ olddn = basedn to proxy in upstream server
+ newdn = basedn in local server
+ username = username to connect to upstream
+ password = password for upstream
+
+ NOTE: this module is a complete hack at this stage. I am committing it just
+ so others can know how I am investigating mmc support
+
+ */
+
+#include "includes.h"
+#include "ldb_module.h"
+#include "auth/credentials/credentials.h"
+
+struct proxy_data {
+ struct ldb_context *upstream;
+ struct ldb_dn *olddn;
+ struct ldb_dn *newdn;
+ const char **oldstr;
+ const char **newstr;
+};
+
+struct proxy_ctx {
+ struct ldb_module *module;
+ struct ldb_request *req;
+
+#ifdef DEBUG_PROXY
+ int count;
+#endif
+};
+
+/*
+ load the @PROXYINFO record
+*/
+static int load_proxy_info(struct ldb_module *module)
+{
+ struct ldb_context *ldb = ldb_module_get_ctx(module);
+ struct proxy_data *proxy = talloc_get_type(ldb_module_get_private(module), struct proxy_data);
+ struct ldb_dn *dn;
+ struct ldb_result *res = NULL;
+ int ret;
+ const char *olddn, *newdn, *url, *username, *password, *oldstr, *newstr;
+ struct cli_credentials *creds;
+
+ /* see if we have already loaded it */
+ if (proxy->upstream != NULL) {
+ return LDB_SUCCESS;
+ }
+
+ dn = ldb_dn_new(proxy, ldb, "@PROXYINFO");
+ if (dn == NULL) {
+ goto failed;
+ }
+ ret = ldb_search(ldb, proxy, &res, dn, LDB_SCOPE_BASE, NULL, NULL);
+ talloc_free(dn);
+ if (ret != LDB_SUCCESS || res->count != 1) {
+ ldb_debug(ldb, LDB_DEBUG_FATAL, "Can't find @PROXYINFO\n");
+ goto failed;
+ }
+
+ url = ldb_msg_find_attr_as_string(res->msgs[0], "url", NULL);
+ olddn = ldb_msg_find_attr_as_string(res->msgs[0], "olddn", NULL);
+ newdn = ldb_msg_find_attr_as_string(res->msgs[0], "newdn", NULL);
+ username = ldb_msg_find_attr_as_string(res->msgs[0], "username", NULL);
+ password = ldb_msg_find_attr_as_string(res->msgs[0], "password", NULL);
+ oldstr = ldb_msg_find_attr_as_string(res->msgs[0], "oldstr", NULL);
+ newstr = ldb_msg_find_attr_as_string(res->msgs[0], "newstr", NULL);
+
+ if (url == NULL || olddn == NULL || newdn == NULL || username == NULL || password == NULL) {
+ ldb_debug(ldb, LDB_DEBUG_FATAL, "Need url, olddn, newdn, oldstr, newstr, username and password in @PROXYINFO\n");
+ goto failed;
+ }
+
+ proxy->olddn = ldb_dn_new(proxy, ldb, olddn);
+ if (proxy->olddn == NULL) {
+ ldb_debug(ldb, LDB_DEBUG_FATAL, "Failed to explode olddn '%s'\n", olddn);
+ goto failed;
+ }
+
+ proxy->newdn = ldb_dn_new(proxy, ldb, newdn);
+ if (proxy->newdn == NULL) {
+ ldb_debug(ldb, LDB_DEBUG_FATAL, "Failed to explode newdn '%s'\n", newdn);
+ goto failed;
+ }
+
+ proxy->upstream = ldb_init(proxy, ldb_get_event_context(ldb));
+ if (proxy->upstream == NULL) {
+ ldb_oom(ldb);
+ goto failed;
+ }
+
+ proxy->oldstr = str_list_make(proxy, oldstr, ", ");
+ if (proxy->oldstr == NULL) {
+ ldb_oom(ldb);
+ goto failed;
+ }
+
+ proxy->newstr = str_list_make(proxy, newstr, ", ");
+ if (proxy->newstr == NULL) {
+ ldb_oom(ldb);
+ goto failed;
+ }
+
+ /* setup credentials for connection */
+ creds = cli_credentials_init(proxy->upstream);
+ if (creds == NULL) {
+ ldb_oom(ldb);
+ goto failed;
+ }
+ cli_credentials_guess(creds, ldb_get_opaque(ldb, "loadparm"));
+ cli_credentials_set_username(creds, username, CRED_SPECIFIED);
+ cli_credentials_set_password(creds, password, CRED_SPECIFIED);
+
+ ldb_set_opaque(proxy->upstream, "credentials", creds);
+
+ ret = ldb_connect(proxy->upstream, url, 0, NULL);
+ if (ret != 0) {
+ ldb_debug(ldb, LDB_DEBUG_FATAL, "proxy failed to connect to %s\n", url);
+ goto failed;
+ }
+
+ ldb_debug(ldb, LDB_DEBUG_TRACE, "proxy connected to %s\n", url);
+
+ talloc_free(res);
+
+ return LDB_SUCCESS;
+
+failed:
+ talloc_free(res);
+ talloc_free(proxy->olddn);
+ talloc_free(proxy->newdn);
+ talloc_free(proxy->upstream);
+ proxy->upstream = NULL;
+ return LDB_ERR_OPERATIONS_ERROR;
+}
+
+
+/*
+ convert a binary blob
+*/
+static void proxy_convert_blob(TALLOC_CTX *mem_ctx, struct ldb_val *v,
+ const char *oldstr, const char *newstr)
+{
+ int len1, len2, len3;
+ uint8_t *olddata = v->data;
+ char *p = strcasestr((char *)v->data, oldstr);
+
+ len1 = (p - (char *)v->data);
+ len2 = strlen(newstr);
+ len3 = v->length - (p+strlen(oldstr) - (char *)v->data);
+ v->length = len1+len2+len3;
+ v->data = talloc_size(mem_ctx, v->length);
+ memcpy(v->data, olddata, len1);
+ memcpy(v->data+len1, newstr, len2);
+ memcpy(v->data+len1+len2, olddata + len1 + strlen(oldstr), len3);
+}
+
+/*
+ convert a returned value
+*/
+static void proxy_convert_value(struct proxy_data *proxy, struct ldb_message *msg, struct ldb_val *v)
+{
+ int i;
+
+ for (i=0;proxy->oldstr[i];i++) {
+ char *p = strcasestr((char *)v->data, proxy->oldstr[i]);
+ if (p == NULL) continue;
+ proxy_convert_blob(msg, v, proxy->oldstr[i], proxy->newstr[i]);
+ }
+}
+
+
+/*
+ convert a returned value
+*/
+static struct ldb_parse_tree *proxy_convert_tree(TALLOC_CTX *mem_ctx,
+ struct proxy_data *proxy,
+ struct ldb_parse_tree *tree)
+{
+ int i;
+ char *expression = ldb_filter_from_tree(mem_ctx, tree);
+
+ for (i=0;proxy->newstr[i];i++) {
+ struct ldb_val v;
+ char *p = strcasestr(expression, proxy->newstr[i]);
+ if (p == NULL) continue;
+ v.data = (uint8_t *)expression;
+ v.length = strlen(expression)+1;
+ proxy_convert_blob(mem_ctx, &v, proxy->newstr[i], proxy->oldstr[i]);
+ return ldb_parse_tree(mem_ctx, (const char *)v.data);
+ }
+ return tree;
+}
+
+
+
+/*
+ convert a returned record
+*/
+static void proxy_convert_record(struct ldb_context *ldb,
+ struct proxy_data *proxy,
+ struct ldb_message *msg)
+{
+ int attr, v;
+
+ /* fix the message DN */
+ if (ldb_dn_compare_base(proxy->olddn, msg->dn) == 0) {
+ ldb_dn_remove_base_components(msg->dn, ldb_dn_get_comp_num(proxy->olddn));
+ ldb_dn_add_base(msg->dn, proxy->newdn);
+ }
+
+ /* fix any attributes */
+ for (attr=0;attr<msg->num_elements;attr++) {
+ for (v=0;v<msg->elements[attr].num_values;v++) {
+ proxy_convert_value(proxy, msg, &msg->elements[attr].values[v]);
+ }
+ }
+
+ /* fix any DN components */
+ for (attr=0;attr<msg->num_elements;attr++) {
+ for (v=0;v<msg->elements[attr].num_values;v++) {
+ proxy_convert_value(proxy, msg, &msg->elements[attr].values[v]);
+ }
+ }
+}
+
+static int proxy_search_callback(struct ldb_request *req,
+ struct ldb_reply *ares)
+{
+ struct ldb_context *ldb;
+ struct proxy_data *proxy;
+ struct proxy_ctx *ac;
+ int ret;
+
+ ac = talloc_get_type(req->context, struct proxy_ctx);
+ ldb = ldb_module_get_ctx(ac->module);
+ proxy = talloc_get_type(ldb_module_get_private(module), struct proxy_data);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ /* Only entries are interesting, and we only want the olddn */
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+
+#ifdef DEBUG_PROXY
+ ac->count++;
+#endif
+ proxy_convert_record(ldb, proxy, ares->message);
+ ret = ldb_module_send_entry(ac->req, ares->message, ares->controls);
+ break;
+
+ case LDB_REPLY_REFERRAL:
+
+ /* ignore remote referrals */
+ break;
+
+ case LDB_REPLY_DONE:
+
+#ifdef DEBUG_PROXY
+ printf("# record %d\n", ac->count+1);
+#endif
+
+ return ldb_module_done(ac->req, NULL, NULL, LDB_SUCCESS);
+ }
+
+ talloc_free(ares);
+ return ret;
+}
+
+/* search */
+static int proxy_search_bytree(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct proxy_ctx *ac;
+ struct ldb_parse_tree *newtree;
+ struct proxy_data *proxy = talloc_get_type(ldb_module_get_private(module), struct proxy_data);
+ struct ldb_request *newreq;
+ struct ldb_dn *base;
+ int ret, i;
+
+ ldb = ldb_module_get_ctx(module);
+
+ if (req->op.search.base == NULL ||
+ (req->op.search.base->comp_num == 1 &&
+ req->op.search.base->components[0].name[0] == '@')) {
+ goto passthru;
+ }
+
+ if (load_proxy_info(module) != LDB_SUCCESS) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* see if the dn is within olddn */
+ if (ldb_dn_compare_base(proxy->newdn, req->op.search.base) != 0) {
+ goto passthru;
+ }
+
+ ac = talloc(req, struct proxy_ctx);
+ if (ac == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->module = module;
+ ac->req = req;
+#ifdef DEBUG_PROXY
+ ac->count = 0;
+#endif
+
+ newtree = proxy_convert_tree(ac, proxy, req->op.search.tree);
+
+ /* convert the basedn of this search */
+ base = ldb_dn_copy(ac, req->op.search.base);
+ if (base == NULL) {
+ goto failed;
+ }
+ ldb_dn_remove_base_components(base, ldb_dn_get_comp_num(proxy->newdn));
+ ldb_dn_add_base(base, proxy->olddn);
+
+ ldb_debug(ldb, LDB_DEBUG_FATAL, "proxying: '%s' with dn '%s' \n",
+ ldb_filter_from_tree(ac, newreq->op.search.tree), ldb_dn_get_linearized(newreq->op.search.base));
+ for (i = 0; req->op.search.attrs && req->op.search.attrs[i]; i++) {
+ ldb_debug(ldb, LDB_DEBUG_FATAL, "attr: '%s'\n", req->op.search.attrs[i]);
+ }
+
+ ret = ldb_build_search_req_ex(&newreq, ldb, ac,
+ base, req->op.search.scope,
+ newtree, req->op.search.attrs,
+ req->controls,
+ ac, proxy_search_callback,
+ req);
+
+ /* FIXME: warning, need a real event system hooked up for this to work properly,
+ * for now this makes the module *not* ASYNC */
+ ret = ldb_request(proxy->upstream, newreq);
+ if (ret != LDB_SUCCESS) {
+ ldb_set_errstring(ldb, ldb_errstring(proxy->upstream));
+ }
+ ret = ldb_wait(newreq->handle, LDB_WAIT_ALL);
+ if (ret != LDB_SUCCESS) {
+ ldb_set_errstring(ldb, ldb_errstring(proxy->upstream));
+ }
+ return ret;
+
+failed:
+ ldb_debug(ldb, LDB_DEBUG_TRACE, "proxy failed for %s\n",
+ ldb_dn_get_linearized(req->op.search.base));
+
+passthru:
+ return ldb_next_request(module, req);
+}
+
+static int proxy_request(struct ldb_module *module, struct ldb_request *req)
+{
+ switch (req->operation) {
+
+ case LDB_REQ_SEARCH:
+ return proxy_search_bytree(module, req);
+
+ default:
+ return ldb_next_request(module, req);
+
+ }
+}
+
+_PUBLIC_ const struct ldb_module_ops ldb_proxy_module_ops = {
+ .name = "proxy",
+ .request = proxy_request
+};
diff --git a/source4/dsdb/samdb/ldb_modules/ranged_results.c b/source4/dsdb/samdb/ldb_modules/ranged_results.c
new file mode 100644
index 0000000000..5ce69a26a2
--- /dev/null
+++ b/source4/dsdb/samdb/ldb_modules/ranged_results.c
@@ -0,0 +1,252 @@
+/*
+ ldb database library
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb ranged results module
+ *
+ * Description: munge AD-style 'ranged results' requests into
+ * requests for all values in an attribute, then return the range to
+ * the client.
+ *
+ * Author: Andrew Bartlett
+ */
+
+#include "includes.h"
+#include "ldb_module.h"
+
+struct rr_context {
+ struct ldb_module *module;
+ struct ldb_request *req;
+};
+
+static struct rr_context *rr_init_context(struct ldb_module *module,
+ struct ldb_request *req)
+{
+ struct rr_context *ac;
+
+ ac = talloc_zero(req, struct rr_context);
+ if (ac == NULL) {
+ ldb_set_errstring(ldb_module_get_ctx(module), "Out of Memory");
+ return NULL;
+ }
+
+ ac->module = module;
+ ac->req = req;
+
+ return ac;
+}
+
+static int rr_search_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+ struct ldb_context *ldb;
+ struct rr_context *ac;
+ int i, j;
+
+ ac = talloc_get_type(req->context, struct rr_context);
+ ldb = ldb_module_get_ctx(ac->module);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ if (ares->type == LDB_REPLY_REFERRAL) {
+ return ldb_module_send_referral(ac->req, ares->referral);
+ }
+
+ if (ares->type == LDB_REPLY_DONE) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ /* LDB_REPLY_ENTRY */
+
+ /* Find those that are range requests from the attribute list */
+ for (i = 0; ac->req->op.search.attrs[i]; i++) {
+ char *p, *new_attr;
+ const char *end_str;
+ unsigned int start, end, orig_num_values;
+ struct ldb_message_element *el;
+ struct ldb_val *orig_values;
+ p = strchr(ac->req->op.search.attrs[i], ';');
+ if (!p) {
+ continue;
+ }
+ if (strncasecmp(p, ";range=", strlen(";range=")) != 0) {
+ continue;
+ }
+ if (sscanf(p, ";range=%u-%u", &start, &end) != 2) {
+ if (sscanf(p, ";range=%u-*", &start) == 1) {
+ end = (unsigned int)-1;
+ } else {
+ continue;
+ }
+ }
+ new_attr = talloc_strndup(ac->req,
+ ac->req->op.search.attrs[i],
+ (size_t)(p - ac->req->op.search.attrs[i]));
+
+ if (!new_attr) {
+ ldb_oom(ldb);
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ el = ldb_msg_find_element(ares->message, new_attr);
+ talloc_free(new_attr);
+ if (!el) {
+ continue;
+ }
+ if (end >= (el->num_values - 1)) {
+ /* Need to leave the requested attribute in
+ * there (so add an empty one to match) */
+ end_str = "*";
+ end = el->num_values - 1;
+ } else {
+ end_str = talloc_asprintf(el, "%u", end);
+ if (!end_str) {
+ ldb_oom(ldb);
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ }
+ /* If start is greater then where we are find the end to be */
+ if (start > end) {
+ el->num_values = 0;
+ el->values = NULL;
+ } else {
+ orig_values = el->values;
+ orig_num_values = el->num_values;
+
+ if ((start + end < start) || (start + end < end)) {
+ ldb_asprintf_errstring(ldb,
+ "range request error: start or end would overflow!");
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_UNWILLING_TO_PERFORM);
+ }
+
+ el->num_values = 0;
+
+ el->values = talloc_array(el, struct ldb_val, (end - start) + 1);
+ if (!el->values) {
+ ldb_oom(ldb);
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ for (j=start; j <= end; j++) {
+ el->values[el->num_values] = orig_values[j];
+ el->num_values++;
+ }
+ }
+ el->name = talloc_asprintf(el, "%s;range=%u-%s", el->name, start, end_str);
+ if (!el->name) {
+ ldb_oom(ldb);
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ }
+
+ return ldb_module_send_entry(ac->req, ares->message, ares->controls);
+}
+
+/* search */
+static int rr_search(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ int i;
+ unsigned int start, end;
+ const char **new_attrs = NULL;
+ bool found_rr = false;
+ struct ldb_request *down_req;
+ struct rr_context *ac;
+ int ret;
+
+ ldb = ldb_module_get_ctx(module);
+
+ /* Strip the range request from the attribute */
+ for (i = 0; req->op.search.attrs && req->op.search.attrs[i]; i++) {
+ char *p;
+ new_attrs = talloc_realloc(req, new_attrs, const char *, i+2);
+ new_attrs[i] = req->op.search.attrs[i];
+ new_attrs[i+1] = NULL;
+ p = strchr(new_attrs[i], ';');
+ if (!p) {
+ continue;
+ }
+ if (strncasecmp(p, ";range=", strlen(";range=")) != 0) {
+ continue;
+ }
+ end = (unsigned int)-1;
+ if (sscanf(p, ";range=%u-*", &start) != 1) {
+ if (sscanf(p, ";range=%u-%u", &start, &end) != 2) {
+ ldb_asprintf_errstring(ldb,
+ "range request error: "
+ "range request malformed");
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+ }
+ if (start > end) {
+ ldb_asprintf_errstring(ldb, "range request error: start must not be greater than end");
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+
+ found_rr = true;
+ new_attrs[i] = talloc_strndup(new_attrs, new_attrs[i],
+ (size_t)(p - new_attrs[i]));
+
+ if (!new_attrs[i]) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ }
+
+ if (found_rr) {
+ ac = rr_init_context(module, req);
+ if (!ac) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ldb_build_search_req_ex(&down_req, ldb, ac,
+ req->op.search.base,
+ req->op.search.scope,
+ req->op.search.tree,
+ new_attrs,
+ req->controls,
+ ac, rr_search_callback,
+ req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ return ldb_next_request(module, down_req);
+ }
+
+ /* No change, just run the original request as if we were never here */
+ talloc_free(new_attrs);
+ return ldb_next_request(module, req);
+}
+
+const struct ldb_module_ops ldb_ranged_results_module_ops = {
+ .name = "ranged_results",
+ .search = rr_search,
+};
diff --git a/source4/dsdb/samdb/ldb_modules/repl_meta_data.c b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c
new file mode 100644
index 0000000000..41f4e8e7d5
--- /dev/null
+++ b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c
@@ -0,0 +1,1466 @@
+/*
+ ldb database library
+
+ Copyright (C) Simo Sorce 2004-2008
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
+ Copyright (C) Andrew Tridgell 2005
+ Copyright (C) Stefan Metzmacher <metze@samba.org> 2007
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb repl_meta_data module
+ *
+ * Description: - add a unique objectGUID onto every new record,
+ * - handle whenCreated, whenChanged timestamps
+ * - handle uSNCreated, uSNChanged numbers
+ * - handle replPropertyMetaData attribute
+ *
+ * Author: Simo Sorce
+ * Author: Stefan Metzmacher
+ */
+
+#include "includes.h"
+#include "ldb_module.h"
+#include "dsdb/samdb/samdb.h"
+#include "dsdb/common/flags.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "librpc/gen_ndr/ndr_drsuapi.h"
+#include "librpc/gen_ndr/ndr_drsblobs.h"
+#include "param/param.h"
+
+struct replmd_replicated_request {
+ struct ldb_module *module;
+ struct ldb_request *req;
+
+ const struct dsdb_schema *schema;
+
+ struct dsdb_extended_replicated_objects *objs;
+
+ /* the controls we pass down */
+ struct ldb_control **controls;
+
+ uint32_t index_current;
+
+ struct ldb_message *search_msg;
+};
+
+static struct replmd_replicated_request *replmd_ctx_init(struct ldb_module *module,
+ struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct replmd_replicated_request *ac;
+
+ ldb = ldb_module_get_ctx(module);
+
+ ac = talloc_zero(req, struct replmd_replicated_request);
+ if (ac == NULL) {
+ ldb_oom(ldb);
+ return NULL;
+ }
+
+ ac->module = module;
+ ac->req = req;
+ return ac;
+}
+
+/*
+ add a time element to a record
+*/
+static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
+{
+ struct ldb_message_element *el;
+ char *s;
+
+ if (ldb_msg_find_element(msg, attr) != NULL) {
+ return LDB_SUCCESS;
+ }
+
+ s = ldb_timestring(msg, t);
+ if (s == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if (ldb_msg_add_string(msg, attr, s) != LDB_SUCCESS) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ el = ldb_msg_find_element(msg, attr);
+ /* always set as replace. This works because on add ops, the flag
+ is ignored */
+ el->flags = LDB_FLAG_MOD_REPLACE;
+
+ return LDB_SUCCESS;
+}
+
+/*
+ add a uint64_t element to a record
+*/
+static int add_uint64_element(struct ldb_message *msg, const char *attr, uint64_t v)
+{
+ struct ldb_message_element *el;
+
+ if (ldb_msg_find_element(msg, attr) != NULL) {
+ return LDB_SUCCESS;
+ }
+
+ if (ldb_msg_add_fmt(msg, attr, "%llu", (unsigned long long)v) != LDB_SUCCESS) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ el = ldb_msg_find_element(msg, attr);
+ /* always set as replace. This works because on add ops, the flag
+ is ignored */
+ el->flags = LDB_FLAG_MOD_REPLACE;
+
+ return LDB_SUCCESS;
+}
+
+static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMetaData1 *m1,
+ const struct replPropertyMetaData1 *m2,
+ const uint32_t *rdn_attid)
+{
+ if (m1->attid == m2->attid) {
+ return 0;
+ }
+
+ /*
+ * the rdn attribute should be at the end!
+ * so we need to return a value greater than zero
+ * which means m1 is greater than m2
+ */
+ if (m1->attid == *rdn_attid) {
+ return 1;
+ }
+
+ /*
+ * the rdn attribute should be at the end!
+ * so we need to return a value less than zero
+ * which means m2 is greater than m1
+ */
+ if (m2->attid == *rdn_attid) {
+ return -1;
+ }
+
+ return m1->attid - m2->attid;
+}
+
+static void replmd_replPropertyMetaDataCtr1_sort(struct replPropertyMetaDataCtr1 *ctr1,
+ const uint32_t *rdn_attid)
+{
+ ldb_qsort(ctr1->array, ctr1->count, sizeof(struct replPropertyMetaData1),
+ discard_const_p(void, rdn_attid), (ldb_qsort_cmp_fn_t)replmd_replPropertyMetaData1_attid_sort);
+}
+
+static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element *e1,
+ const struct ldb_message_element *e2,
+ const struct dsdb_schema *schema)
+{
+ const struct dsdb_attribute *a1;
+ const struct dsdb_attribute *a2;
+
+ /*
+ * TODO: make this faster by caching the dsdb_attribute pointer
+ * on the ldb_messag_element
+ */
+
+ a1 = dsdb_attribute_by_lDAPDisplayName(schema, e1->name);
+ a2 = dsdb_attribute_by_lDAPDisplayName(schema, e2->name);
+
+ /*
+ * TODO: remove this check, we should rely on e1 and e2 having valid attribute names
+ * in the schema
+ */
+ if (!a1 || !a2) {
+ return strcasecmp(e1->name, e2->name);
+ }
+
+ return a1->attributeID_id - a2->attributeID_id;
+}
+
+static void replmd_ldb_message_sort(struct ldb_message *msg,
+ const struct dsdb_schema *schema)
+{
+ ldb_qsort(msg->elements, msg->num_elements, sizeof(struct ldb_message_element),
+ discard_const_p(void, schema), (ldb_qsort_cmp_fn_t)replmd_ldb_message_element_attid_sort);
+}
+
+static int replmd_op_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+ struct ldb_context *ldb;
+ struct replmd_replicated_request *ac;
+
+ ac = talloc_get_type(req->context, struct replmd_replicated_request);
+ ldb = ldb_module_get_ctx(ac->module);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ if (ares->type != LDB_REPLY_DONE) {
+ ldb_set_errstring(ldb,
+ "invalid ldb_reply_type in callback");
+ talloc_free(ares);
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, LDB_SUCCESS);
+}
+
+static int replmd_add(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct replmd_replicated_request *ac;
+ const struct dsdb_schema *schema;
+ enum ndr_err_code ndr_err;
+ struct ldb_request *down_req;
+ struct ldb_message *msg;
+ const struct dsdb_attribute *rdn_attr = NULL;
+ struct GUID guid;
+ struct ldb_val guid_value;
+ struct replPropertyMetaDataBlob nmd;
+ struct ldb_val nmd_value;
+ uint64_t seq_num;
+ const struct GUID *our_invocation_id;
+ time_t t = time(NULL);
+ NTTIME now;
+ char *time_str;
+ int ret;
+ uint32_t i, ni=0;
+
+ /* do not manipulate our control entries */
+ if (ldb_dn_is_special(req->op.add.message->dn)) {
+ return ldb_next_request(module, req);
+ }
+
+ ldb = ldb_module_get_ctx(module);
+
+ ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_add\n");
+
+ schema = dsdb_get_schema(ldb);
+ if (!schema) {
+ ldb_debug_set(ldb, LDB_DEBUG_FATAL,
+ "replmd_modify: no dsdb_schema loaded");
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+
+ ac = replmd_ctx_init(module, req);
+ if (!ac) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->schema = schema;
+
+ if (ldb_msg_find_element(req->op.add.message, "objectGUID") != NULL) {
+ ldb_debug_set(ldb, LDB_DEBUG_ERROR,
+ "replmd_add: it's not allowed to add an object with objectGUID\n");
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+
+ /* Get a sequence number from the backend */
+ ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ /* a new GUID */
+ guid = GUID_random();
+
+ /* get our invicationId */
+ our_invocation_id = samdb_ntds_invocation_id(ldb);
+ if (!our_invocation_id) {
+ ldb_debug_set(ldb, LDB_DEBUG_ERROR,
+ "replmd_add: unable to find invocationId\n");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* we have to copy the message as the caller might have it as a const */
+ msg = ldb_msg_copy_shallow(ac, req->op.add.message);
+ if (msg == NULL) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* generated times */
+ unix_to_nt_time(&now, t);
+ time_str = ldb_timestring(msg, t);
+ if (!time_str) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /*
+ * remove autogenerated attributes
+ */
+ ldb_msg_remove_attr(msg, "whenCreated");
+ ldb_msg_remove_attr(msg, "whenChanged");
+ ldb_msg_remove_attr(msg, "uSNCreated");
+ ldb_msg_remove_attr(msg, "uSNChanged");
+ ldb_msg_remove_attr(msg, "replPropertyMetaData");
+
+ /*
+ * readd replicated attributes
+ */
+ ret = ldb_msg_add_string(msg, "whenCreated", time_str);
+ if (ret != LDB_SUCCESS) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* build the replication meta_data */
+ ZERO_STRUCT(nmd);
+ nmd.version = 1;
+ nmd.ctr.ctr1.count = msg->num_elements;
+ nmd.ctr.ctr1.array = talloc_array(msg,
+ struct replPropertyMetaData1,
+ nmd.ctr.ctr1.count);
+ if (!nmd.ctr.ctr1.array) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ for (i=0; i < msg->num_elements; i++) {
+ struct ldb_message_element *e = &msg->elements[i];
+ struct replPropertyMetaData1 *m = &nmd.ctr.ctr1.array[ni];
+ const struct dsdb_attribute *sa;
+
+ if (e->name[0] == '@') continue;
+
+ sa = dsdb_attribute_by_lDAPDisplayName(schema, e->name);
+ if (!sa) {
+ ldb_debug_set(ldb, LDB_DEBUG_ERROR,
+ "replmd_add: attribute '%s' not defined in schema\n",
+ e->name);
+ return LDB_ERR_NO_SUCH_ATTRIBUTE;
+ }
+
+ if ((sa->systemFlags & 0x00000001) || (sa->systemFlags & 0x00000004)) {
+ /* if the attribute is not replicated (0x00000001)
+ * or constructed (0x00000004) it has no metadata
+ */
+ continue;
+ }
+
+ m->attid = sa->attributeID_id;
+ m->version = 1;
+ m->originating_change_time = now;
+ m->originating_invocation_id = *our_invocation_id;
+ m->originating_usn = seq_num;
+ m->local_usn = seq_num;
+ ni++;
+
+ if (ldb_attr_cmp(e->name, ldb_dn_get_rdn_name(msg->dn))) {
+ rdn_attr = sa;
+ }
+ }
+
+ /* fix meta data count */
+ nmd.ctr.ctr1.count = ni;
+
+ /*
+ * sort meta data array, and move the rdn attribute entry to the end
+ */
+ replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, &rdn_attr->attributeID_id);
+
+ /* generated NDR encoded values */
+ ndr_err = ndr_push_struct_blob(&guid_value, msg,
+ NULL,
+ &guid,
+ (ndr_push_flags_fn_t)ndr_push_GUID);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ndr_err = ndr_push_struct_blob(&nmd_value, msg,
+ lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
+ &nmd,
+ (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /*
+ * add the autogenerated values
+ */
+ ret = ldb_msg_add_value(msg, "objectGUID", &guid_value, NULL);
+ if (ret != LDB_SUCCESS) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ret = ldb_msg_add_string(msg, "whenChanged", time_str);
+ if (ret != LDB_SUCCESS) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", seq_num);
+ if (ret != LDB_SUCCESS) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", seq_num);
+ if (ret != LDB_SUCCESS) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
+ if (ret != LDB_SUCCESS) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /*
+ * sort the attributes by attid before storing the object
+ */
+ replmd_ldb_message_sort(msg, schema);
+
+ ret = ldb_build_add_req(&down_req, ldb, ac,
+ msg,
+ req->controls,
+ ac, replmd_op_callback,
+ req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ /* go on with the call chain */
+ return ldb_next_request(module, down_req);
+}
+
+static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct replmd_replicated_request *ac;
+ const struct dsdb_schema *schema;
+ struct ldb_request *down_req;
+ struct ldb_message *msg;
+ int ret;
+ time_t t = time(NULL);
+ uint64_t seq_num;
+
+ /* do not manipulate our control entries */
+ if (ldb_dn_is_special(req->op.mod.message->dn)) {
+ return ldb_next_request(module, req);
+ }
+
+ ldb = ldb_module_get_ctx(module);
+
+ ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
+
+ schema = dsdb_get_schema(ldb);
+ if (!schema) {
+ ldb_debug_set(ldb, LDB_DEBUG_FATAL,
+ "replmd_modify: no dsdb_schema loaded");
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+
+ ac = replmd_ctx_init(module, req);
+ if (!ac) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->schema = schema;
+
+ /* we have to copy the message as the caller might have it as a const */
+ msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
+ if (msg == NULL) {
+ talloc_free(ac);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* TODO:
+ * - get the whole old object
+ * - if the old object doesn't exist report an error
+ * - give an error when a readonly attribute should
+ * be modified
+ * - merge the changed into the old object
+ * if the caller set values to the same value
+ * ignore the attribute, return success when no
+ * attribute was changed
+ * - calculate the new replPropertyMetaData attribute
+ */
+
+ if (add_time_element(msg, "whenChanged", t) != LDB_SUCCESS) {
+ talloc_free(ac);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* Get a sequence number from the backend */
+ ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
+ if (ret == LDB_SUCCESS) {
+ if (add_uint64_element(msg, "uSNChanged", seq_num) != LDB_SUCCESS) {
+ talloc_free(ac);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ }
+
+ /* TODO:
+ * - sort the attributes by attid with replmd_ldb_message_sort()
+ * - replace the old object with the newly constructed one
+ */
+
+ ret = ldb_build_mod_req(&down_req, ldb, ac,
+ msg,
+ req->controls,
+ ac, replmd_op_callback,
+ req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ talloc_steal(down_req, msg);
+
+ /* go on with the call chain */
+ return ldb_next_request(module, down_req);
+}
+
+static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
+{
+ return ret;
+}
+
+static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
+{
+ int ret = LDB_ERR_OTHER;
+ /* TODO: do some error mapping */
+ return ret;
+}
+
+static int replmd_replicated_apply_next(struct replmd_replicated_request *ar);
+
+static int replmd_replicated_apply_add_callback(struct ldb_request *req,
+ struct ldb_reply *ares)
+{
+ struct ldb_context *ldb;
+ struct replmd_replicated_request *ar = talloc_get_type(req->context,
+ struct replmd_replicated_request);
+ int ret;
+
+ ldb = ldb_module_get_ctx(ar->module);
+
+ if (!ares) {
+ return ldb_module_done(ar->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ar->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ if (ares->type != LDB_REPLY_DONE) {
+ ldb_set_errstring(ldb, "Invalid reply type\n!");
+ return ldb_module_done(ar->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ talloc_free(ares);
+ ar->index_current++;
+
+ ret = replmd_replicated_apply_next(ar);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ar->req, NULL, NULL, ret);
+ }
+
+ return LDB_SUCCESS;
+}
+
+static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
+{
+ struct ldb_context *ldb;
+ struct ldb_request *change_req;
+ enum ndr_err_code ndr_err;
+ struct ldb_message *msg;
+ struct replPropertyMetaDataBlob *md;
+ struct ldb_val md_value;
+ uint32_t i;
+ uint64_t seq_num;
+ int ret;
+
+ /*
+ * TODO: check if the parent object exist
+ */
+
+ /*
+ * TODO: handle the conflict case where an object with the
+ * same name exist
+ */
+
+ ldb = ldb_module_get_ctx(ar->module);
+ msg = ar->objs->objects[ar->index_current].msg;
+ md = ar->objs->objects[ar->index_current].meta_data;
+
+ ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
+ if (ret != LDB_SUCCESS) {
+ return replmd_replicated_request_error(ar, ret);
+ }
+
+ ret = ldb_msg_add_value(msg, "objectGUID", &ar->objs->objects[ar->index_current].guid_value, NULL);
+ if (ret != LDB_SUCCESS) {
+ return replmd_replicated_request_error(ar, ret);
+ }
+
+ ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
+ if (ret != LDB_SUCCESS) {
+ return replmd_replicated_request_error(ar, ret);
+ }
+
+ ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", seq_num);
+ if (ret != LDB_SUCCESS) {
+ return replmd_replicated_request_error(ar, ret);
+ }
+
+ ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", seq_num);
+ if (ret != LDB_SUCCESS) {
+ return replmd_replicated_request_error(ar, ret);
+ }
+
+ /*
+ * the meta data array is already sorted by the caller
+ */
+ for (i=0; i < md->ctr.ctr1.count; i++) {
+ md->ctr.ctr1.array[i].local_usn = seq_num;
+ }
+ ndr_err = ndr_push_struct_blob(&md_value, msg,
+ lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
+ md,
+ (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
+ return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
+ }
+ ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
+ if (ret != LDB_SUCCESS) {
+ return replmd_replicated_request_error(ar, ret);
+ }
+
+ replmd_ldb_message_sort(msg, ar->schema);
+
+ ret = ldb_build_add_req(&change_req,
+ ldb,
+ ar,
+ msg,
+ ar->controls,
+ ar,
+ replmd_replicated_apply_add_callback,
+ ar->req);
+ if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
+
+ return ldb_next_request(ar->module, change_req);
+}
+
+static int replmd_replPropertyMetaData1_conflict_compare(struct replPropertyMetaData1 *m1,
+ struct replPropertyMetaData1 *m2)
+{
+ int ret;
+
+ if (m1->version != m2->version) {
+ return m1->version - m2->version;
+ }
+
+ if (m1->originating_change_time != m2->originating_change_time) {
+ return m1->originating_change_time - m2->originating_change_time;
+ }
+
+ ret = GUID_compare(&m1->originating_invocation_id, &m2->originating_invocation_id);
+ if (ret != 0) {
+ return ret;
+ }
+
+ return m1->originating_usn - m2->originating_usn;
+}
+
+static int replmd_replicated_apply_merge_callback(struct ldb_request *req,
+ struct ldb_reply *ares)
+{
+ struct ldb_context *ldb;
+ struct replmd_replicated_request *ar = talloc_get_type(req->context,
+ struct replmd_replicated_request);
+ int ret;
+
+ ldb = ldb_module_get_ctx(ar->module);
+
+ if (!ares) {
+ return ldb_module_done(ar->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ar->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ if (ares->type != LDB_REPLY_DONE) {
+ ldb_set_errstring(ldb, "Invalid reply type\n!");
+ return ldb_module_done(ar->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ talloc_free(ares);
+ ar->index_current++;
+
+ ret = replmd_replicated_apply_next(ar);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ar->req, NULL, NULL, ret);
+ }
+
+ return LDB_SUCCESS;
+}
+
+static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
+{
+ struct ldb_context *ldb;
+ struct ldb_request *change_req;
+ enum ndr_err_code ndr_err;
+ struct ldb_message *msg;
+ struct replPropertyMetaDataBlob *rmd;
+ struct replPropertyMetaDataBlob omd;
+ const struct ldb_val *omd_value;
+ struct replPropertyMetaDataBlob nmd;
+ struct ldb_val nmd_value;
+ uint32_t i,j,ni=0;
+ uint32_t removed_attrs = 0;
+ uint64_t seq_num;
+ int ret;
+
+ ldb = ldb_module_get_ctx(ar->module);
+ msg = ar->objs->objects[ar->index_current].msg;
+ rmd = ar->objs->objects[ar->index_current].meta_data;
+ ZERO_STRUCT(omd);
+ omd.version = 1;
+
+ /*
+ * TODO: add rename conflict handling
+ */
+ if (ldb_dn_compare(msg->dn, ar->search_msg->dn) != 0) {
+ ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_replicated_apply_merge[%u]: rename not supported",
+ ar->index_current);
+ ldb_debug(ldb, LDB_DEBUG_FATAL, "%s => %s\n",
+ ldb_dn_get_linearized(ar->search_msg->dn),
+ ldb_dn_get_linearized(msg->dn));
+ return replmd_replicated_request_werror(ar, WERR_NOT_SUPPORTED);
+ }
+
+ ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
+ if (ret != LDB_SUCCESS) {
+ return replmd_replicated_request_error(ar, ret);
+ }
+
+ /* find existing meta data */
+ omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
+ if (omd_value) {
+ ndr_err = ndr_pull_struct_blob(omd_value, ar,
+ lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), &omd,
+ (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
+ return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
+ }
+
+ if (omd.version != 1) {
+ return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
+ }
+ }
+
+ ZERO_STRUCT(nmd);
+ nmd.version = 1;
+ nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
+ nmd.ctr.ctr1.array = talloc_array(ar,
+ struct replPropertyMetaData1,
+ nmd.ctr.ctr1.count);
+ if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOMEM);
+
+ /* first copy the old meta data */
+ for (i=0; i < omd.ctr.ctr1.count; i++) {
+ nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
+ ni++;
+ }
+
+ /* now merge in the new meta data */
+ for (i=0; i < rmd->ctr.ctr1.count; i++) {
+ bool found = false;
+
+ rmd->ctr.ctr1.array[i].local_usn = seq_num;
+
+ for (j=0; j < ni; j++) {
+ int cmp;
+
+ if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
+ continue;
+ }
+
+ cmp = replmd_replPropertyMetaData1_conflict_compare(&rmd->ctr.ctr1.array[i],
+ &nmd.ctr.ctr1.array[j]);
+ if (cmp > 0) {
+ /* replace the entry */
+ nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
+ found = true;
+ break;
+ }
+
+ /* we don't want to apply this change so remove the attribute */
+ ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
+ removed_attrs++;
+
+ found = true;
+ break;
+ }
+
+ if (found) continue;
+
+ nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
+ ni++;
+ }
+
+ /*
+ * finally correct the size of the meta_data array
+ */
+ nmd.ctr.ctr1.count = ni;
+
+ /*
+ * the rdn attribute (the alias for the name attribute),
+ * 'cn' for most objects is the last entry in the meta data array
+ * we have stored
+ *
+ * sort the new meta data array
+ */
+ {
+ struct replPropertyMetaData1 *rdn_p;
+ uint32_t rdn_idx = omd.ctr.ctr1.count - 1;
+
+ rdn_p = &nmd.ctr.ctr1.array[rdn_idx];
+ replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, &rdn_p->attid);
+ }
+
+ /* create the meta data value */
+ ndr_err = ndr_push_struct_blob(&nmd_value, msg,
+ lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
+ &nmd,
+ (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
+ return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
+ }
+
+ /*
+ * check if some replicated attributes left, otherwise skip the ldb_modify() call
+ */
+ if (msg->num_elements == 0) {
+ ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
+ ar->index_current);
+
+ ar->index_current++;
+ return replmd_replicated_apply_next(ar);
+ }
+
+ ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
+ ar->index_current, msg->num_elements);
+
+ /*
+ * when we know that we'll modify the record, add the whenChanged, uSNChanged
+ * and replPopertyMetaData attributes
+ */
+ ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
+ if (ret != LDB_SUCCESS) {
+ return replmd_replicated_request_error(ar, ret);
+ }
+ ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", seq_num);
+ if (ret != LDB_SUCCESS) {
+ return replmd_replicated_request_error(ar, ret);
+ }
+ ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
+ if (ret != LDB_SUCCESS) {
+ return replmd_replicated_request_error(ar, ret);
+ }
+
+ replmd_ldb_message_sort(msg, ar->schema);
+
+ /* we want to replace the old values */
+ for (i=0; i < msg->num_elements; i++) {
+ msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
+ }
+
+ ret = ldb_build_mod_req(&change_req,
+ ldb,
+ ar,
+ msg,
+ ar->controls,
+ ar,
+ replmd_replicated_apply_merge_callback,
+ ar->req);
+ if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
+
+ return ldb_next_request(ar->module, change_req);
+}
+
+static int replmd_replicated_apply_search_callback(struct ldb_request *req,
+ struct ldb_reply *ares)
+{
+ struct replmd_replicated_request *ar = talloc_get_type(req->context,
+ struct replmd_replicated_request);
+ int ret;
+
+ if (!ares) {
+ return ldb_module_done(ar->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS &&
+ ares->error != LDB_ERR_NO_SUCH_OBJECT) {
+ return ldb_module_done(ar->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+ ar->search_msg = talloc_steal(ar, ares->message);
+ break;
+
+ case LDB_REPLY_REFERRAL:
+ /* we ignore referrals */
+ break;
+
+ case LDB_REPLY_DONE:
+ if (ar->search_msg != NULL) {
+ ret = replmd_replicated_apply_merge(ar);
+ } else {
+ ret = replmd_replicated_apply_add(ar);
+ }
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ar->req, NULL, NULL, ret);
+ }
+ }
+
+ talloc_free(ares);
+ return LDB_SUCCESS;
+}
+
+static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
+
+static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
+{
+ struct ldb_context *ldb;
+ int ret;
+ char *tmp_str;
+ char *filter;
+ struct ldb_request *search_req;
+
+ if (ar->index_current >= ar->objs->num_objects) {
+ /* done with it, go to the last op */
+ return replmd_replicated_uptodate_vector(ar);
+ }
+
+ ldb = ldb_module_get_ctx(ar->module);
+ ar->search_msg = NULL;
+
+ tmp_str = ldb_binary_encode(ar, ar->objs->objects[ar->index_current].guid_value);
+ if (!tmp_str) return replmd_replicated_request_werror(ar, WERR_NOMEM);
+
+ filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
+ if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
+ talloc_free(tmp_str);
+
+ ret = ldb_build_search_req(&search_req,
+ ldb,
+ ar,
+ ar->objs->partition_dn,
+ LDB_SCOPE_SUBTREE,
+ filter,
+ NULL,
+ NULL,
+ ar,
+ replmd_replicated_apply_search_callback,
+ ar->req);
+ if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
+
+ return ldb_next_request(ar->module, search_req);
+}
+
+static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
+ struct ldb_reply *ares)
+{
+ struct ldb_context *ldb;
+ struct replmd_replicated_request *ar = talloc_get_type(req->context,
+ struct replmd_replicated_request);
+ ldb = ldb_module_get_ctx(ar->module);
+
+ if (!ares) {
+ return ldb_module_done(ar->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ar->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ if (ares->type != LDB_REPLY_DONE) {
+ ldb_set_errstring(ldb, "Invalid reply type\n!");
+ return ldb_module_done(ar->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ talloc_free(ares);
+
+ return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
+}
+
+static int replmd_drsuapi_DsReplicaCursor2_compare(const struct drsuapi_DsReplicaCursor2 *c1,
+ const struct drsuapi_DsReplicaCursor2 *c2)
+{
+ return GUID_compare(&c1->source_dsa_invocation_id, &c2->source_dsa_invocation_id);
+}
+
+static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
+{
+ struct ldb_context *ldb;
+ struct ldb_request *change_req;
+ enum ndr_err_code ndr_err;
+ struct ldb_message *msg;
+ struct replUpToDateVectorBlob ouv;
+ const struct ldb_val *ouv_value;
+ const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
+ struct replUpToDateVectorBlob nuv;
+ struct ldb_val nuv_value;
+ struct ldb_message_element *nuv_el = NULL;
+ const struct GUID *our_invocation_id;
+ struct ldb_message_element *orf_el = NULL;
+ struct repsFromToBlob nrf;
+ struct ldb_val *nrf_value = NULL;
+ struct ldb_message_element *nrf_el = NULL;
+ uint32_t i,j,ni=0;
+ uint64_t seq_num;
+ bool found = false;
+ time_t t = time(NULL);
+ NTTIME now;
+ int ret;
+
+ ldb = ldb_module_get_ctx(ar->module);
+ ruv = ar->objs->uptodateness_vector;
+ ZERO_STRUCT(ouv);
+ ouv.version = 2;
+ ZERO_STRUCT(nuv);
+ nuv.version = 2;
+
+ unix_to_nt_time(&now, t);
+
+ /*
+ * we use the next sequence number for our own highest_usn
+ * because we will do a modify request and this will increment
+ * our highest_usn
+ */
+ ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
+ if (ret != LDB_SUCCESS) {
+ return replmd_replicated_request_error(ar, ret);
+ }
+
+ /*
+ * first create the new replUpToDateVector
+ */
+ ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
+ if (ouv_value) {
+ ndr_err = ndr_pull_struct_blob(ouv_value, ar,
+ lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), &ouv,
+ (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
+ return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
+ }
+
+ if (ouv.version != 2) {
+ return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
+ }
+ }
+
+ /*
+ * the new uptodateness vector will at least
+ * contain 1 entry, one for the source_dsa
+ *
+ * plus optional values from our old vector and the one from the source_dsa
+ */
+ nuv.ctr.ctr2.count = 1 + ouv.ctr.ctr2.count;
+ if (ruv) nuv.ctr.ctr2.count += ruv->count;
+ nuv.ctr.ctr2.cursors = talloc_array(ar,
+ struct drsuapi_DsReplicaCursor2,
+ nuv.ctr.ctr2.count);
+ if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOMEM);
+
+ /* first copy the old vector */
+ for (i=0; i < ouv.ctr.ctr2.count; i++) {
+ nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
+ ni++;
+ }
+
+ /* get our invocation_id if we have one already attached to the ldb */
+ our_invocation_id = samdb_ntds_invocation_id(ldb);
+
+ /* merge in the source_dsa vector is available */
+ for (i=0; (ruv && i < ruv->count); i++) {
+ found = false;
+
+ if (our_invocation_id &&
+ GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
+ our_invocation_id)) {
+ continue;
+ }
+
+ for (j=0; j < ni; j++) {
+ if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
+ &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
+ continue;
+ }
+
+ found = true;
+
+ /*
+ * we update only the highest_usn and not the latest_sync_success time,
+ * because the last success stands for direct replication
+ */
+ if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
+ nuv.ctr.ctr2.cursors[j].highest_usn = ruv->cursors[i].highest_usn;
+ }
+ break;
+ }
+
+ if (found) continue;
+
+ /* if it's not there yet, add it */
+ nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
+ ni++;
+ }
+
+ /*
+ * merge in the current highwatermark for the source_dsa
+ */
+ found = false;
+ for (j=0; j < ni; j++) {
+ if (!GUID_equal(&ar->objs->source_dsa->source_dsa_invocation_id,
+ &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
+ continue;
+ }
+
+ found = true;
+
+ /*
+ * here we update the highest_usn and last_sync_success time
+ * because we're directly replicating from the source_dsa
+ *
+ * and use the tmp_highest_usn because this is what we have just applied
+ * to our ldb
+ */
+ nuv.ctr.ctr2.cursors[j].highest_usn = ar->objs->source_dsa->highwatermark.tmp_highest_usn;
+ nuv.ctr.ctr2.cursors[j].last_sync_success = now;
+ break;
+ }
+ if (!found) {
+ /*
+ * here we update the highest_usn and last_sync_success time
+ * because we're directly replicating from the source_dsa
+ *
+ * and use the tmp_highest_usn because this is what we have just applied
+ * to our ldb
+ */
+ nuv.ctr.ctr2.cursors[ni].source_dsa_invocation_id= ar->objs->source_dsa->source_dsa_invocation_id;
+ nuv.ctr.ctr2.cursors[ni].highest_usn = ar->objs->source_dsa->highwatermark.tmp_highest_usn;
+ nuv.ctr.ctr2.cursors[ni].last_sync_success = now;
+ ni++;
+ }
+
+ /*
+ * finally correct the size of the cursors array
+ */
+ nuv.ctr.ctr2.count = ni;
+
+ /*
+ * sort the cursors
+ */
+ qsort(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count,
+ sizeof(struct drsuapi_DsReplicaCursor2),
+ (comparison_fn_t)replmd_drsuapi_DsReplicaCursor2_compare);
+
+ /*
+ * create the change ldb_message
+ */
+ msg = ldb_msg_new(ar);
+ if (!msg) return replmd_replicated_request_werror(ar, WERR_NOMEM);
+ msg->dn = ar->search_msg->dn;
+
+ ndr_err = ndr_push_struct_blob(&nuv_value, msg,
+ lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
+ &nuv,
+ (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
+ return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
+ }
+ ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
+ if (ret != LDB_SUCCESS) {
+ return replmd_replicated_request_error(ar, ret);
+ }
+ nuv_el->flags = LDB_FLAG_MOD_REPLACE;
+
+ /*
+ * now create the new repsFrom value from the given repsFromTo1 structure
+ */
+ ZERO_STRUCT(nrf);
+ nrf.version = 1;
+ nrf.ctr.ctr1 = *ar->objs->source_dsa;
+ /* and fix some values... */
+ nrf.ctr.ctr1.consecutive_sync_failures = 0;
+ nrf.ctr.ctr1.last_success = now;
+ nrf.ctr.ctr1.last_attempt = now;
+ nrf.ctr.ctr1.result_last_attempt = WERR_OK;
+ nrf.ctr.ctr1.highwatermark.highest_usn = nrf.ctr.ctr1.highwatermark.tmp_highest_usn;
+
+ /*
+ * first see if we already have a repsFrom value for the current source dsa
+ * if so we'll later replace this value
+ */
+ orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
+ if (orf_el) {
+ for (i=0; i < orf_el->num_values; i++) {
+ struct repsFromToBlob *trf;
+
+ trf = talloc(ar, struct repsFromToBlob);
+ if (!trf) return replmd_replicated_request_werror(ar, WERR_NOMEM);
+
+ ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), trf,
+ (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
+ return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
+ }
+
+ if (trf->version != 1) {
+ return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
+ }
+
+ /*
+ * we compare the source dsa objectGUID not the invocation_id
+ * because we want only one repsFrom value per source dsa
+ * and when the invocation_id of the source dsa has changed we don't need
+ * the old repsFrom with the old invocation_id
+ */
+ if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
+ &ar->objs->source_dsa->source_dsa_obj_guid)) {
+ talloc_free(trf);
+ continue;
+ }
+
+ talloc_free(trf);
+ nrf_value = &orf_el->values[i];
+ break;
+ }
+
+ /*
+ * copy over all old values to the new ldb_message
+ */
+ ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
+ if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
+ *nrf_el = *orf_el;
+ }
+
+ /*
+ * if we haven't found an old repsFrom value for the current source dsa
+ * we'll add a new value
+ */
+ if (!nrf_value) {
+ struct ldb_val zero_value;
+ ZERO_STRUCT(zero_value);
+ ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
+ if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
+
+ nrf_value = &nrf_el->values[nrf_el->num_values - 1];
+ }
+
+ /* we now fill the value which is already attached to ldb_message */
+ ndr_err = ndr_push_struct_blob(nrf_value, msg,
+ lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
+ &nrf,
+ (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
+ return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
+ }
+
+ /*
+ * the ldb_message_element for the attribute, has all the old values and the new one
+ * so we'll replace the whole attribute with all values
+ */
+ nrf_el->flags = LDB_FLAG_MOD_REPLACE;
+
+ /* prepare the ldb_modify() request */
+ ret = ldb_build_mod_req(&change_req,
+ ldb,
+ ar,
+ msg,
+ ar->controls,
+ ar,
+ replmd_replicated_uptodate_modify_callback,
+ ar->req);
+ if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
+
+ return ldb_next_request(ar->module, change_req);
+}
+
+static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
+ struct ldb_reply *ares)
+{
+ struct replmd_replicated_request *ar = talloc_get_type(req->context,
+ struct replmd_replicated_request);
+ int ret;
+
+ if (!ares) {
+ return ldb_module_done(ar->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS &&
+ ares->error != LDB_ERR_NO_SUCH_OBJECT) {
+ return ldb_module_done(ar->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+ ar->search_msg = talloc_steal(ar, ares->message);
+ break;
+
+ case LDB_REPLY_REFERRAL:
+ /* we ignore referrals */
+ break;
+
+ case LDB_REPLY_DONE:
+ if (ar->search_msg == NULL) {
+ ret = replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
+ } else {
+ ret = replmd_replicated_uptodate_modify(ar);
+ }
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ar->req, NULL, NULL, ret);
+ }
+ }
+
+ talloc_free(ares);
+ return LDB_SUCCESS;
+}
+
+
+static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
+{
+ struct ldb_context *ldb;
+ int ret;
+ static const char *attrs[] = {
+ "replUpToDateVector",
+ "repsFrom",
+ NULL
+ };
+ struct ldb_request *search_req;
+
+ ldb = ldb_module_get_ctx(ar->module);
+ ar->search_msg = NULL;
+
+ ret = ldb_build_search_req(&search_req,
+ ldb,
+ ar,
+ ar->objs->partition_dn,
+ LDB_SCOPE_BASE,
+ "(objectClass=*)",
+ attrs,
+ NULL,
+ ar,
+ replmd_replicated_uptodate_search_callback,
+ ar->req);
+ if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
+
+ return ldb_next_request(ar->module, search_req);
+}
+
+static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct dsdb_extended_replicated_objects *objs;
+ struct replmd_replicated_request *ar;
+ struct ldb_control **ctrls;
+ int ret;
+
+ ldb = ldb_module_get_ctx(module);
+
+ ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
+
+ objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
+ if (!objs) {
+ ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
+ return LDB_ERR_PROTOCOL_ERROR;
+ }
+
+ if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
+ ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
+ objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
+ return LDB_ERR_PROTOCOL_ERROR;
+ }
+
+ ar = replmd_ctx_init(module, req);
+ if (!ar)
+ return LDB_ERR_OPERATIONS_ERROR;
+
+ ar->objs = objs;
+ ar->schema = dsdb_get_schema(ldb);
+ if (!ar->schema) {
+ ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
+ talloc_free(ar);
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+
+ ctrls = req->controls;
+
+ if (req->controls) {
+ req->controls = talloc_memdup(ar, req->controls,
+ talloc_get_size(req->controls));
+ if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOMEM);
+ }
+
+ ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, NULL);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ ar->controls = req->controls;
+ req->controls = ctrls;
+
+ return replmd_replicated_apply_next(ar);
+}
+
+static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
+{
+ if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
+ return replmd_extended_replicated_objects(module, req);
+ }
+
+ return ldb_next_request(module, req);
+}
+
+_PUBLIC_ const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
+ .name = "repl_meta_data",
+ .add = replmd_add,
+ .modify = replmd_modify,
+ .extended = replmd_extended,
+};
diff --git a/source4/dsdb/samdb/ldb_modules/rootdse.c b/source4/dsdb/samdb/ldb_modules/rootdse.c
new file mode 100644
index 0000000000..9ae894d55f
--- /dev/null
+++ b/source4/dsdb/samdb/ldb_modules/rootdse.c
@@ -0,0 +1,466 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ rootDSE ldb module
+
+ Copyright (C) Andrew Tridgell 2005
+ Copyright (C) Simo Sorce 2005-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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "ldb_private.h"
+#include "system/time.h"
+#include "dsdb/samdb/samdb.h"
+#include "version.h"
+
+struct private_data {
+ int num_controls;
+ char **controls;
+ int num_partitions;
+ struct ldb_dn **partitions;
+};
+
+/*
+ return 1 if a specific attribute has been requested
+*/
+static int do_attribute(const char * const *attrs, const char *name)
+{
+ return attrs == NULL ||
+ ldb_attr_in_list(attrs, name) ||
+ ldb_attr_in_list(attrs, "*");
+}
+
+static int do_attribute_explicit(const char * const *attrs, const char *name)
+{
+ return attrs != NULL && ldb_attr_in_list(attrs, name);
+}
+
+
+/*
+ add dynamically generated attributes to rootDSE result
+*/
+static int rootdse_add_dynamic(struct ldb_module *module, struct ldb_message *msg, const char * const *attrs)
+{
+ struct ldb_context *ldb;
+ struct private_data *priv = talloc_get_type(ldb_module_get_private(module), struct private_data);
+ char **server_sasl;
+ const struct dsdb_schema *schema;
+
+ ldb = ldb_module_get_ctx(module);
+ schema = dsdb_get_schema(ldb);
+
+ msg->dn = ldb_dn_new(msg, ldb, NULL);
+
+ /* don't return the distinduishedName, cn and name attributes */
+ ldb_msg_remove_attr(msg, "distinguishedName");
+ ldb_msg_remove_attr(msg, "cn");
+ ldb_msg_remove_attr(msg, "name");
+
+ if (do_attribute(attrs, "currentTime")) {
+ if (ldb_msg_add_steal_string(msg, "currentTime",
+ ldb_timestring(msg, time(NULL))) != 0) {
+ goto failed;
+ }
+ }
+
+ if (do_attribute(attrs, "supportedControl")) {
+ int i;
+ for (i = 0; i < priv->num_controls; i++) {
+ char *control = talloc_strdup(msg, priv->controls[i]);
+ if (!control) {
+ goto failed;
+ }
+ if (ldb_msg_add_steal_string(msg, "supportedControl",
+ control) != 0) {
+ goto failed;
+ }
+ }
+ }
+
+ if (do_attribute(attrs, "namingContexts")) {
+ int i;
+ for (i = 0; i < priv->num_partitions; i++) {
+ struct ldb_dn *dn = priv->partitions[i];
+ if (ldb_msg_add_steal_string(msg, "namingContexts",
+ ldb_dn_alloc_linearized(msg, dn)) != 0) {
+ goto failed;
+ }
+ }
+ }
+
+ server_sasl = talloc_get_type(ldb_get_opaque(ldb, "supportedSASLMechanims"),
+ char *);
+ if (server_sasl && do_attribute(attrs, "supportedSASLMechanisms")) {
+ int i;
+ for (i = 0; server_sasl && server_sasl[i]; i++) {
+ char *sasl_name = talloc_strdup(msg, server_sasl[i]);
+ if (!sasl_name) {
+ goto failed;
+ }
+ if (ldb_msg_add_steal_string(msg, "supportedSASLMechanisms",
+ sasl_name) != 0) {
+ goto failed;
+ }
+ }
+ }
+
+ if (do_attribute(attrs, "highestCommittedUSN")) {
+ uint64_t seq_num;
+ int ret = ldb_sequence_number(ldb, LDB_SEQ_HIGHEST_SEQ, &seq_num);
+ if (ret == LDB_SUCCESS) {
+ if (ldb_msg_add_fmt(msg, "highestCommittedUSN",
+ "%llu", (unsigned long long)seq_num) != 0) {
+ goto failed;
+ }
+ }
+ }
+
+ if (schema && do_attribute_explicit(attrs, "dsSchemaAttrCount")) {
+ struct dsdb_attribute *cur;
+ uint32_t n = 0;
+
+ for (cur = schema->attributes; cur; cur = cur->next) {
+ n++;
+ }
+
+ if (ldb_msg_add_fmt(msg, "dsSchemaAttrCount",
+ "%u", n) != 0) {
+ goto failed;
+ }
+ }
+
+ if (schema && do_attribute_explicit(attrs, "dsSchemaClassCount")) {
+ struct dsdb_class *cur;
+ uint32_t n = 0;
+
+ for (cur = schema->classes; cur; cur = cur->next) {
+ n++;
+ }
+
+ if (ldb_msg_add_fmt(msg, "dsSchemaClassCount",
+ "%u", n) != 0) {
+ goto failed;
+ }
+ }
+
+ if (schema && do_attribute_explicit(attrs, "dsSchemaPrefixCount")) {
+ if (ldb_msg_add_fmt(msg, "dsSchemaPrefixCount",
+ "%u", schema->num_prefixes) != 0) {
+ goto failed;
+ }
+ }
+
+ if (do_attribute_explicit(attrs, "validFSMOs")) {
+ const struct dsdb_naming_fsmo *naming_fsmo;
+ const struct dsdb_pdc_fsmo *pdc_fsmo;
+ const char *dn_str;
+
+ if (schema && schema->fsmo.we_are_master) {
+ dn_str = ldb_dn_get_linearized(samdb_schema_dn(ldb));
+ if (dn_str && dn_str[0]) {
+ if (ldb_msg_add_fmt(msg, "validFSMOs", "%s", dn_str) != 0) {
+ goto failed;
+ }
+ }
+ }
+
+ naming_fsmo = talloc_get_type(ldb_get_opaque(ldb, "dsdb_naming_fsmo"),
+ struct dsdb_naming_fsmo);
+ if (naming_fsmo && naming_fsmo->we_are_master) {
+ dn_str = ldb_dn_get_linearized(samdb_partitions_dn(ldb, msg));
+ if (dn_str && dn_str[0]) {
+ if (ldb_msg_add_fmt(msg, "validFSMOs", "%s", dn_str) != 0) {
+ goto failed;
+ }
+ }
+ }
+
+ pdc_fsmo = talloc_get_type(ldb_get_opaque(ldb, "dsdb_pdc_fsmo"),
+ struct dsdb_pdc_fsmo);
+ if (pdc_fsmo && pdc_fsmo->we_are_master) {
+ dn_str = ldb_dn_get_linearized(samdb_base_dn(ldb));
+ if (dn_str && dn_str[0]) {
+ if (ldb_msg_add_fmt(msg, "validFSMOs", "%s", dn_str) != 0) {
+ goto failed;
+ }
+ }
+ }
+ }
+
+ if (schema && do_attribute_explicit(attrs, "vendorVersion")) {
+ if (ldb_msg_add_fmt(msg, "vendorVersion",
+ "%s", SAMBA_VERSION_STRING) != 0) {
+ goto failed;
+ }
+ }
+
+ /* TODO: lots more dynamic attributes should be added here */
+
+ return LDB_SUCCESS;
+
+failed:
+ return LDB_ERR_OPERATIONS_ERROR;
+}
+
+/*
+ handle search requests
+*/
+
+struct rootdse_context {
+ struct ldb_module *module;
+ struct ldb_request *req;
+};
+
+static struct rootdse_context *rootdse_init_context(struct ldb_module *module,
+ struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct rootdse_context *ac;
+
+ ldb = ldb_module_get_ctx(module);
+
+ ac = talloc_zero(req, struct rootdse_context);
+ if (ac == NULL) {
+ ldb_set_errstring(ldb, "Out of Memory");
+ return NULL;
+ }
+
+ ac->module = module;
+ ac->req = req;
+
+ return ac;
+}
+
+static int rootdse_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+ struct rootdse_context *ac;
+ int ret;
+
+ ac = talloc_get_type(req->context, struct rootdse_context);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+ /*
+ * if the client explicit asks for the 'netlogon' attribute
+ * the reply_entry needs to be skipped
+ */
+ if (ac->req->op.search.attrs &&
+ ldb_attr_in_list(ac->req->op.search.attrs, "netlogon")) {
+ talloc_free(ares);
+ return LDB_SUCCESS;
+ }
+
+ /* for each record returned post-process to add any dynamic
+ attributes that have been asked for */
+ ret = rootdse_add_dynamic(ac->module, ares->message,
+ ac->req->op.search.attrs);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(ares);
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+
+ return ldb_module_send_entry(ac->req, ares->message, ares->controls);
+
+ case LDB_REPLY_REFERRAL:
+ /* should we allow the backend to return referrals in this case
+ * ?? */
+ break;
+
+ case LDB_REPLY_DONE:
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ talloc_free(ares);
+ return LDB_SUCCESS;
+}
+
+static int rootdse_search(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct rootdse_context *ac;
+ struct ldb_request *down_req;
+ int ret;
+
+ ldb = ldb_module_get_ctx(module);
+
+ /* see if its for the rootDSE - only a base search on the "" DN qualifies */
+ if (!(req->op.search.scope == LDB_SCOPE_BASE && ldb_dn_is_null(req->op.search.base))) {
+ /* Otherwise, pass down to the rest of the stack */
+ return ldb_next_request(module, req);
+ }
+
+ ac = rootdse_init_context(module, req);
+ if (ac == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* in our db we store the rootDSE with a DN of @ROOTDSE */
+ ret = ldb_build_search_req(&down_req, ldb, ac,
+ ldb_dn_new(ac, ldb, "@ROOTDSE"),
+ LDB_SCOPE_BASE,
+ NULL,
+ req->op.search.attrs,
+ NULL,/* for now skip the controls from the client */
+ ac, rootdse_callback,
+ req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ return ldb_next_request(module, down_req);
+}
+
+static int rootdse_register_control(struct ldb_module *module, struct ldb_request *req)
+{
+ struct private_data *priv = talloc_get_type(ldb_module_get_private(module), struct private_data);
+ char **list;
+
+ list = talloc_realloc(priv, priv->controls, char *, priv->num_controls + 1);
+ if (!list) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ list[priv->num_controls] = talloc_strdup(list, req->op.reg_control.oid);
+ if (!list[priv->num_controls]) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ priv->num_controls += 1;
+ priv->controls = list;
+
+ return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
+}
+
+static int rootdse_register_partition(struct ldb_module *module, struct ldb_request *req)
+{
+ struct private_data *priv = talloc_get_type(ldb_module_get_private(module), struct private_data);
+ struct ldb_dn **list;
+
+ list = talloc_realloc(priv, priv->partitions, struct ldb_dn *, priv->num_partitions + 1);
+ if (!list) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ list[priv->num_partitions] = ldb_dn_copy(list, req->op.reg_partition.dn);
+ if (!list[priv->num_partitions]) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ priv->num_partitions += 1;
+ priv->partitions = list;
+
+ return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
+}
+
+
+static int rootdse_request(struct ldb_module *module, struct ldb_request *req)
+{
+ switch (req->operation) {
+
+ case LDB_REQ_REGISTER_CONTROL:
+ return rootdse_register_control(module, req);
+ case LDB_REQ_REGISTER_PARTITION:
+ return rootdse_register_partition(module, req);
+
+ default:
+ break;
+ }
+ return ldb_next_request(module, req);
+}
+
+static int rootdse_init(struct ldb_module *module)
+{
+ struct ldb_context *ldb;
+ struct private_data *data;
+
+ ldb = ldb_module_get_ctx(module);
+
+ data = talloc(module, struct private_data);
+ if (data == NULL) {
+ return -1;
+ }
+
+ data->num_controls = 0;
+ data->controls = NULL;
+ data->num_partitions = 0;
+ data->partitions = NULL;
+ ldb_module_set_private(module, data);
+
+ ldb_set_default_dns(ldb);
+
+ return ldb_next_init(module);
+}
+
+static int rootdse_modify(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct ldb_result *ext_res;
+ int ret;
+ struct ldb_dn *schema_dn;
+ struct ldb_message_element *schemaUpdateNowAttr;
+
+ /*
+ If dn is not "" we should let it pass through
+ */
+ if (!ldb_dn_is_null(req->op.mod.message->dn)) {
+ return ldb_next_request(module, req);
+ }
+
+ ldb = ldb_module_get_ctx(module);
+
+ /*
+ dn is empty so check for schemaUpdateNow attribute
+ "The type of modification and values specified in the LDAP modify operation do not matter." MSDN
+ */
+ schemaUpdateNowAttr = ldb_msg_find_element(req->op.mod.message, "schemaUpdateNow");
+ if (!schemaUpdateNowAttr) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ schema_dn = samdb_schema_dn(ldb);
+ if (!schema_dn) {
+ ldb_reset_err_string(ldb);
+ ldb_debug(ldb, LDB_DEBUG_WARNING,
+ "rootdse_modify: no schema dn present: (skip ldb_extended call)\n");
+ return ldb_next_request(module, req);
+ }
+
+ ret = ldb_extended(ldb, DSDB_EXTENDED_SCHEMA_UPDATE_NOW_OID, schema_dn, &ext_res);
+ if (ret != LDB_SUCCESS) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ talloc_free(ext_res);
+ return ret;
+}
+
+_PUBLIC_ const struct ldb_module_ops ldb_rootdse_module_ops = {
+ .name = "rootdse",
+ .init_context = rootdse_init,
+ .search = rootdse_search,
+ .request = rootdse_request,
+ .modify = rootdse_modify
+};
diff --git a/source4/dsdb/samdb/ldb_modules/samba3sam.c b/source4/dsdb/samdb/ldb_modules/samba3sam.c
new file mode 100644
index 0000000000..59cb9de717
--- /dev/null
+++ b/source4/dsdb/samdb/ldb_modules/samba3sam.c
@@ -0,0 +1,933 @@
+/*
+ ldb database library - Samba3 SAM compatibility backend
+
+ Copyright (C) Jelmer Vernooij 2005
+ Copyright (C) Martin Kuehl <mkhl@samba.org> 2006
+*/
+
+#include "includes.h"
+#include "ldb_module.h"
+#include "ldb/ldb_map/ldb_map.h"
+#include "system/passwd.h"
+
+#include "librpc/gen_ndr/ndr_security.h"
+#include "librpc/gen_ndr/ndr_samr.h"
+#include "librpc/ndr/libndr.h"
+#include "libcli/security/security.h"
+#include "libcli/security/proto.h"
+#include "lib/samba3/samba3.h"
+
+/*
+ * sambaSID -> member (dn!)
+ * sambaSIDList -> member (dn!)
+ * sambaDomainName -> name
+ * sambaTrustPassword
+ * sambaUnixIdPool
+ * sambaIdmapEntry
+ * sambaAccountPolicy
+ * sambaSidEntry
+ * sambaAcctFlags -> systemFlags ?
+ * sambaPasswordHistory -> ntPwdHistory*/
+
+/* Not necessary:
+ * sambaConfig
+ * sambaShare
+ * sambaConfigOption
+ * sambaNextGroupRid
+ * sambaNextUserRid
+ * sambaAlgorithmicRidBase
+ */
+
+/* Not in Samba4:
+ * sambaKickoffTime
+ * sambaPwdCanChange
+ * sambaPwdMustChange
+ * sambaHomePath
+ * sambaHomeDrive
+ * sambaLogonScript
+ * sambaProfilePath
+ * sambaUserWorkstations
+ * sambaMungedDial
+ * sambaLogonHours */
+
+/* In Samba4 but not in Samba3:
+*/
+
+/* From a sambaPrimaryGroupSID, generate a primaryGroupID (integer) attribute */
+static struct ldb_message_element *generate_primaryGroupID(struct ldb_module *module, TALLOC_CTX *ctx, const char *local_attr, const struct ldb_message *remote)
+{
+ struct ldb_message_element *el;
+ const char *sid = ldb_msg_find_attr_as_string(remote, "sambaPrimaryGroupSID", NULL);
+ const char *p;
+
+ if (!sid)
+ return NULL;
+
+ p = strrchr(sid, '-');
+ if (!p)
+ return NULL;
+
+ el = talloc_zero(ctx, struct ldb_message_element);
+ el->name = talloc_strdup(ctx, "primaryGroupID");
+ el->num_values = 1;
+ el->values = talloc_array(ctx, struct ldb_val, 1);
+ el->values[0].data = (uint8_t *)talloc_strdup(el->values, p+1);
+ el->values[0].length = strlen((char *)el->values[0].data);
+
+ return el;
+}
+
+static void generate_sambaPrimaryGroupSID(struct ldb_module *module, const char *local_attr, const struct ldb_message *local, struct ldb_message *remote_mp, struct ldb_message *remote_fb)
+{
+ const struct ldb_val *sidval;
+ char *sidstring;
+ struct dom_sid *sid;
+ enum ndr_err_code ndr_err;
+
+ /* We need the domain, so we get it from the objectSid that we hope is here... */
+ sidval = ldb_msg_find_ldb_val(local, "objectSid");
+
+ if (!sidval)
+ return; /* Sorry, no SID today.. */
+
+ sid = talloc(remote_mp, struct dom_sid);
+ if (sid == NULL) {
+ return;
+ }
+
+ ndr_err = ndr_pull_struct_blob(sidval, sid, NULL, sid, (ndr_pull_flags_fn_t)ndr_pull_dom_sid);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ talloc_free(sid);
+ return;
+ }
+
+ if (!ldb_msg_find_ldb_val(local, "primaryGroupID"))
+ return; /* Sorry, no SID today.. */
+
+ sid->num_auths--;
+
+ sidstring = dom_sid_string(remote_mp, sid);
+ talloc_free(sid);
+ ldb_msg_add_fmt(remote_mp, "sambaPrimaryGroupSID", "%s-%d", sidstring, ldb_msg_find_attr_as_uint(local, "primaryGroupID", 0));
+ talloc_free(sidstring);
+}
+
+/* Just copy the old value. */
+static struct ldb_val convert_uid_samaccount(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
+{
+ struct ldb_val out = data_blob(NULL, 0);
+ out = ldb_val_dup(ctx, val);
+
+ return out;
+}
+
+static struct ldb_val lookup_homedir(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
+{
+ struct ldb_context *ldb;
+ struct passwd *pwd;
+ struct ldb_val retval;
+
+ ldb = ldb_module_get_ctx(module);
+
+ pwd = getpwnam((char *)val->data);
+
+ if (!pwd) {
+ ldb_debug(ldb, LDB_DEBUG_WARNING, "Unable to lookup '%s' in passwd", (char *)val->data);
+ return *talloc_zero(ctx, struct ldb_val);
+ }
+
+ retval.data = (uint8_t *)talloc_strdup(ctx, pwd->pw_dir);
+ retval.length = strlen((char *)retval.data);
+
+ return retval;
+}
+
+static struct ldb_val lookup_gid(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
+{
+ struct passwd *pwd;
+ struct ldb_val retval;
+
+ pwd = getpwnam((char *)val->data);
+
+ if (!pwd) {
+ return *talloc_zero(ctx, struct ldb_val);
+ }
+
+ retval.data = (uint8_t *)talloc_asprintf(ctx, "%ld", (unsigned long)pwd->pw_gid);
+ retval.length = strlen((char *)retval.data);
+
+ return retval;
+}
+
+static struct ldb_val lookup_uid(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
+{
+ struct passwd *pwd;
+ struct ldb_val retval;
+
+ pwd = getpwnam((char *)val->data);
+
+ if (!pwd) {
+ return *talloc_zero(ctx, struct ldb_val);
+ }
+
+ retval.data = (uint8_t *)talloc_asprintf(ctx, "%ld", (unsigned long)pwd->pw_uid);
+ retval.length = strlen((char *)retval.data);
+
+ return retval;
+}
+
+/* Encode a sambaSID to an objectSid. */
+static struct ldb_val encode_sid(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
+{
+ struct ldb_val out = data_blob(NULL, 0);
+ struct dom_sid *sid;
+ enum ndr_err_code ndr_err;
+
+ sid = dom_sid_parse_talloc(ctx, (char *)val->data);
+ if (sid == NULL) {
+ return out;
+ }
+
+ ndr_err = ndr_push_struct_blob(&out, ctx,
+ NULL,
+ sid, (ndr_push_flags_fn_t)ndr_push_dom_sid);
+ talloc_free(sid);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return out;
+ }
+
+ return out;
+}
+
+/* Decode an objectSid to a sambaSID. */
+static struct ldb_val decode_sid(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
+{
+ struct ldb_val out = data_blob(NULL, 0);
+ struct dom_sid *sid;
+ enum ndr_err_code ndr_err;
+
+ sid = talloc(ctx, struct dom_sid);
+ if (sid == NULL) {
+ return out;
+ }
+
+ ndr_err = ndr_pull_struct_blob(val, sid, NULL, sid,
+ (ndr_pull_flags_fn_t)ndr_pull_dom_sid);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ goto done;
+ }
+
+ out.data = (uint8_t *)dom_sid_string(ctx, sid);
+ if (out.data == NULL) {
+ goto done;
+ }
+ out.length = strlen((const char *)out.data);
+
+done:
+ talloc_free(sid);
+ return out;
+}
+
+/* Convert 16 bytes to 32 hex digits. */
+static struct ldb_val bin2hex(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
+{
+ struct ldb_val out;
+ struct samr_Password pwd;
+ if (val->length != sizeof(pwd.hash)) {
+ return data_blob(NULL, 0);
+ }
+ memcpy(pwd.hash, val->data, sizeof(pwd.hash));
+ out = data_blob_string_const(smbpasswd_sethexpwd(ctx, &pwd, 0));
+ if (!out.data) {
+ return data_blob(NULL, 0);
+ }
+ return out;
+}
+
+/* Convert 32 hex digits to 16 bytes. */
+static struct ldb_val hex2bin(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
+{
+ struct ldb_val out;
+ struct samr_Password *pwd;
+ pwd = smbpasswd_gethexpwd(ctx, (const char *)val->data);
+ if (!pwd) {
+ return data_blob(NULL, 0);
+ }
+ out = data_blob_talloc(ctx, pwd->hash, sizeof(pwd->hash));
+ return out;
+}
+
+const struct ldb_map_objectclass samba3_objectclasses[] = {
+ {
+ .local_name = "user",
+ .remote_name = "posixAccount",
+ .base_classes = { "top", NULL },
+ .musts = { "cn", "uid", "uidNumber", "gidNumber", "homeDirectory", NULL },
+ .mays = { "userPassword", "loginShell", "gecos", "description", NULL },
+ },
+ {
+ .local_name = "group",
+ .remote_name = "posixGroup",
+ .base_classes = { "top", NULL },
+ .musts = { "cn", "gidNumber", NULL },
+ .mays = { "userPassword", "memberUid", "description", NULL },
+ },
+ {
+ .local_name = "group",
+ .remote_name = "sambaGroupMapping",
+ .base_classes = { "top", "posixGroup", NULL },
+ .musts = { "gidNumber", "sambaSID", "sambaGroupType", NULL },
+ .mays = { "displayName", "description", "sambaSIDList", NULL },
+ },
+ {
+ .local_name = "user",
+ .remote_name = "sambaSAMAccount",
+ .base_classes = { "top", "posixAccount", NULL },
+ .musts = { "uid", "sambaSID", NULL },
+ .mays = { "cn", "sambaLMPassword", "sambaNTPassword",
+ "sambaPwdLastSet", "sambaLogonTime", "sambaLogoffTime",
+ "sambaKickoffTime", "sambaPwdCanChange", "sambaPwdMustChange",
+ "sambaAcctFlags", "displayName", "sambaHomePath", "sambaHomeDrive",
+ "sambaLogonScript", "sambaProfilePath", "description", "sambaUserWorkstations",
+ "sambaPrimaryGroupSID", "sambaDomainName", "sambaMungedDial",
+ "sambaBadPasswordCount", "sambaBadPasswordTime",
+ "sambaPasswordHistory", "sambaLogonHours", NULL }
+
+ },
+ {
+ .local_name = "domain",
+ .remote_name = "sambaDomain",
+ .base_classes = { "top", NULL },
+ .musts = { "sambaDomainName", "sambaSID", NULL },
+ .mays = { "sambaNextRid", "sambaNextGroupRid", "sambaNextUserRid", "sambaAlgorithmicRidBase", NULL },
+ },
+ { NULL, NULL }
+};
+
+const struct ldb_map_attribute samba3_attributes[] =
+{
+ /* sambaNextRid -> nextRid */
+ {
+ .local_name = "nextRid",
+ .type = MAP_RENAME,
+ .u = {
+ .rename = {
+ .remote_name = "sambaNextRid",
+ },
+ },
+ },
+
+ /* sambaBadPasswordTime -> badPasswordtime*/
+ {
+ .local_name = "badPasswordTime",
+ .type = MAP_RENAME,
+ .u = {
+ .rename = {
+ .remote_name = "sambaBadPasswordTime",
+ },
+ },
+ },
+
+ /* sambaLMPassword -> lmPwdHash*/
+ {
+ .local_name = "dBCSPwd",
+ .type = MAP_CONVERT,
+ .u = {
+ .convert = {
+ .remote_name = "sambaLMPassword",
+ .convert_local = bin2hex,
+ .convert_remote = hex2bin,
+ },
+ },
+ },
+
+ /* sambaGroupType -> groupType */
+ {
+ .local_name = "groupType",
+ .type = MAP_RENAME,
+ .u = {
+ .rename = {
+ .remote_name = "sambaGroupType",
+ },
+ },
+ },
+
+ /* sambaNTPassword -> ntPwdHash*/
+ {
+ .local_name = "ntpwdhash",
+ .type = MAP_CONVERT,
+ .u = {
+ .convert = {
+ .remote_name = "sambaNTPassword",
+ .convert_local = bin2hex,
+ .convert_remote = hex2bin,
+ },
+ },
+ },
+
+ /* sambaPrimaryGroupSID -> primaryGroupID */
+ {
+ .local_name = "primaryGroupID",
+ .type = MAP_GENERATE,
+ .u = {
+ .generate = {
+ .remote_names = { "sambaPrimaryGroupSID", NULL },
+ .generate_local = generate_primaryGroupID,
+ .generate_remote = generate_sambaPrimaryGroupSID,
+ },
+ },
+ },
+
+ /* sambaBadPasswordCount -> badPwdCount */
+ {
+ .local_name = "badPwdCount",
+ .type = MAP_RENAME,
+ .u = {
+ .rename = {
+ .remote_name = "sambaBadPasswordCount",
+ },
+ },
+ },
+
+ /* sambaLogonTime -> lastLogon*/
+ {
+ .local_name = "lastLogon",
+ .type = MAP_RENAME,
+ .u = {
+ .rename = {
+ .remote_name = "sambaLogonTime",
+ },
+ },
+ },
+
+ /* sambaLogoffTime -> lastLogoff*/
+ {
+ .local_name = "lastLogoff",
+ .type = MAP_RENAME,
+ .u = {
+ .rename = {
+ .remote_name = "sambaLogoffTime",
+ },
+ },
+ },
+
+ /* uid -> unixName */
+ {
+ .local_name = "unixName",
+ .type = MAP_RENAME,
+ .u = {
+ .rename = {
+ .remote_name = "uid",
+ },
+ },
+ },
+
+ /* displayName -> name */
+ {
+ .local_name = "name",
+ .type = MAP_RENAME,
+ .u = {
+ .rename = {
+ .remote_name = "displayName",
+ },
+ },
+ },
+
+ /* cn */
+ {
+ .local_name = "cn",
+ .type = MAP_KEEP,
+ },
+
+ /* sAMAccountName -> cn */
+ {
+ .local_name = "sAMAccountName",
+ .type = MAP_CONVERT,
+ .u = {
+ .convert = {
+ .remote_name = "uid",
+ .convert_remote = convert_uid_samaccount,
+ },
+ },
+ },
+
+ /* objectCategory */
+ {
+ .local_name = "objectCategory",
+ .type = MAP_IGNORE,
+ },
+
+ /* objectGUID */
+ {
+ .local_name = "objectGUID",
+ .type = MAP_IGNORE,
+ },
+
+ /* objectVersion */
+ {
+ .local_name = "objectVersion",
+ .type = MAP_IGNORE,
+ },
+
+ /* codePage */
+ {
+ .local_name = "codePage",
+ .type = MAP_IGNORE,
+ },
+
+ /* dNSHostName */
+ {
+ .local_name = "dNSHostName",
+ .type = MAP_IGNORE,
+ },
+
+
+ /* dnsDomain */
+ {
+ .local_name = "dnsDomain",
+ .type = MAP_IGNORE,
+ },
+
+ /* dnsRoot */
+ {
+ .local_name = "dnsRoot",
+ .type = MAP_IGNORE,
+ },
+
+ /* countryCode */
+ {
+ .local_name = "countryCode",
+ .type = MAP_IGNORE,
+ },
+
+ /* nTMixedDomain */
+ {
+ .local_name = "nTMixedDomain",
+ .type = MAP_IGNORE,
+ },
+
+ /* operatingSystem */
+ {
+ .local_name = "operatingSystem",
+ .type = MAP_IGNORE,
+ },
+
+ /* operatingSystemVersion */
+ {
+ .local_name = "operatingSystemVersion",
+ .type = MAP_IGNORE,
+ },
+
+
+ /* servicePrincipalName */
+ {
+ .local_name = "servicePrincipalName",
+ .type = MAP_IGNORE,
+ },
+
+ /* msDS-Behavior-Version */
+ {
+ .local_name = "msDS-Behavior-Version",
+ .type = MAP_IGNORE,
+ },
+
+ /* msDS-KeyVersionNumber */
+ {
+ .local_name = "msDS-KeyVersionNumber",
+ .type = MAP_IGNORE,
+ },
+
+ /* msDs-masteredBy */
+ {
+ .local_name = "msDs-masteredBy",
+ .type = MAP_IGNORE,
+ },
+
+ /* ou */
+ {
+ .local_name = "ou",
+ .type = MAP_KEEP,
+ },
+
+ /* dc */
+ {
+ .local_name = "dc",
+ .type = MAP_KEEP,
+ },
+
+ /* description */
+ {
+ .local_name = "description",
+ .type = MAP_KEEP,
+ },
+
+ /* sambaSID -> objectSid*/
+ {
+ .local_name = "objectSid",
+ .type = MAP_CONVERT,
+ .u = {
+ .convert = {
+ .remote_name = "sambaSID",
+ .convert_local = decode_sid,
+ .convert_remote = encode_sid,
+ },
+ },
+ },
+
+ /* sambaPwdLastSet -> pwdLastSet */
+ {
+ .local_name = "pwdLastSet",
+ .type = MAP_RENAME,
+ .u = {
+ .rename = {
+ .remote_name = "sambaPwdLastSet",
+ },
+ },
+ },
+
+ /* accountExpires */
+ {
+ .local_name = "accountExpires",
+ .type = MAP_IGNORE,
+ },
+
+ /* adminCount */
+ {
+ .local_name = "adminCount",
+ .type = MAP_IGNORE,
+ },
+
+ /* canonicalName */
+ {
+ .local_name = "canonicalName",
+ .type = MAP_IGNORE,
+ },
+
+ /* createTimestamp */
+ {
+ .local_name = "createTimestamp",
+ .type = MAP_IGNORE,
+ },
+
+ /* creationTime */
+ {
+ .local_name = "creationTime",
+ .type = MAP_IGNORE,
+ },
+
+ /* dMDLocation */
+ {
+ .local_name = "dMDLocation",
+ .type = MAP_IGNORE,
+ },
+
+ /* fSMORoleOwner */
+ {
+ .local_name = "fSMORoleOwner",
+ .type = MAP_IGNORE,
+ },
+
+ /* forceLogoff */
+ {
+ .local_name = "forceLogoff",
+ .type = MAP_IGNORE,
+ },
+
+ /* instanceType */
+ {
+ .local_name = "instanceType",
+ .type = MAP_IGNORE,
+ },
+
+ /* invocationId */
+ {
+ .local_name = "invocationId",
+ .type = MAP_IGNORE,
+ },
+
+ /* isCriticalSystemObject */
+ {
+ .local_name = "isCriticalSystemObject",
+ .type = MAP_IGNORE,
+ },
+
+ /* localPolicyFlags */
+ {
+ .local_name = "localPolicyFlags",
+ .type = MAP_IGNORE,
+ },
+
+ /* lockOutObservationWindow */
+ {
+ .local_name = "lockOutObservationWindow",
+ .type = MAP_IGNORE,
+ },
+
+ /* lockoutDuration */
+ {
+ .local_name = "lockoutDuration",
+ .type = MAP_IGNORE,
+ },
+
+ /* lockoutThreshold */
+ {
+ .local_name = "lockoutThreshold",
+ .type = MAP_IGNORE,
+ },
+
+ /* logonCount */
+ {
+ .local_name = "logonCount",
+ .type = MAP_IGNORE,
+ },
+
+ /* masteredBy */
+ {
+ .local_name = "masteredBy",
+ .type = MAP_IGNORE,
+ },
+
+ /* maxPwdAge */
+ {
+ .local_name = "maxPwdAge",
+ .type = MAP_IGNORE,
+ },
+
+ /* member */
+ {
+ .local_name = "member",
+ .type = MAP_IGNORE,
+ },
+
+ /* memberOf */
+ {
+ .local_name = "memberOf",
+ .type = MAP_IGNORE,
+ },
+
+ /* minPwdAge */
+ {
+ .local_name = "minPwdAge",
+ .type = MAP_IGNORE,
+ },
+
+ /* minPwdLength */
+ {
+ .local_name = "minPwdLength",
+ .type = MAP_IGNORE,
+ },
+
+ /* modifiedCount */
+ {
+ .local_name = "modifiedCount",
+ .type = MAP_IGNORE,
+ },
+
+ /* modifiedCountAtLastProm */
+ {
+ .local_name = "modifiedCountAtLastProm",
+ .type = MAP_IGNORE,
+ },
+
+ /* modifyTimestamp */
+ {
+ .local_name = "modifyTimestamp",
+ .type = MAP_IGNORE,
+ },
+
+ /* nCName */
+ {
+ .local_name = "nCName",
+ .type = MAP_IGNORE,
+ },
+
+ /* nETBIOSName */
+ {
+ .local_name = "nETBIOSName",
+ .type = MAP_IGNORE,
+ },
+
+ /* oEMInformation */
+ {
+ .local_name = "oEMInformation",
+ .type = MAP_IGNORE,
+ },
+
+ /* privilege */
+ {
+ .local_name = "privilege",
+ .type = MAP_IGNORE,
+ },
+
+ /* pwdHistoryLength */
+ {
+ .local_name = "pwdHistoryLength",
+ .type = MAP_IGNORE,
+ },
+
+ /* pwdProperties */
+ {
+ .local_name = "pwdProperties",
+ .type = MAP_IGNORE,
+ },
+
+ /* rIDAvailablePool */
+ {
+ .local_name = "rIDAvailablePool",
+ .type = MAP_IGNORE,
+ },
+
+ /* revision */
+ {
+ .local_name = "revision",
+ .type = MAP_IGNORE,
+ },
+
+ /* ridManagerReference */
+ {
+ .local_name = "ridManagerReference",
+ .type = MAP_IGNORE,
+ },
+
+ /* sAMAccountType */
+ {
+ .local_name = "sAMAccountType",
+ .type = MAP_IGNORE,
+ },
+
+ /* sPNMappings */
+ {
+ .local_name = "sPNMappings",
+ .type = MAP_IGNORE,
+ },
+
+ /* serverReference */
+ {
+ .local_name = "serverReference",
+ .type = MAP_IGNORE,
+ },
+
+ /* serverState */
+ {
+ .local_name = "serverState",
+ .type = MAP_IGNORE,
+ },
+
+ /* showInAdvancedViewOnly */
+ {
+ .local_name = "showInAdvancedViewOnly",
+ .type = MAP_IGNORE,
+ },
+
+ /* subRefs */
+ {
+ .local_name = "subRefs",
+ .type = MAP_IGNORE,
+ },
+
+ /* systemFlags */
+ {
+ .local_name = "systemFlags",
+ .type = MAP_IGNORE,
+ },
+
+ /* uASCompat */
+ {
+ .local_name = "uASCompat",
+ .type = MAP_IGNORE,
+ },
+
+ /* uSNChanged */
+ {
+ .local_name = "uSNChanged",
+ .type = MAP_IGNORE,
+ },
+
+ /* uSNCreated */
+ {
+ .local_name = "uSNCreated",
+ .type = MAP_IGNORE,
+ },
+
+ /* userPassword */
+ {
+ .local_name = "userPassword",
+ .type = MAP_IGNORE,
+ },
+
+ /* userAccountControl */
+ {
+ .local_name = "userAccountControl",
+ .type = MAP_IGNORE,
+ },
+
+ /* whenChanged */
+ {
+ .local_name = "whenChanged",
+ .type = MAP_IGNORE,
+ },
+
+ /* whenCreated */
+ {
+ .local_name = "whenCreated",
+ .type = MAP_IGNORE,
+ },
+
+ /* uidNumber */
+ {
+ .local_name = "unixName",
+ .type = MAP_CONVERT,
+ .u = {
+ .convert = {
+ .remote_name = "uidNumber",
+ .convert_local = lookup_uid,
+ },
+ },
+ },
+
+ /* gidNumber. Perhaps make into generate so we can distinguish between
+ * groups and accounts? */
+ {
+ .local_name = "unixName",
+ .type = MAP_CONVERT,
+ .u = {
+ .convert = {
+ .remote_name = "gidNumber",
+ .convert_local = lookup_gid,
+ },
+ },
+ },
+
+ /* homeDirectory */
+ {
+ .local_name = "unixName",
+ .type = MAP_CONVERT,
+ .u = {
+ .convert = {
+ .remote_name = "homeDirectory",
+ .convert_local = lookup_homedir,
+ },
+ },
+ },
+ {
+ .local_name = NULL,
+ }
+};
+
+/* the context init function */
+static int samba3sam_init(struct ldb_module *module)
+{
+ int ret;
+
+ ret = ldb_map_init(module, samba3_attributes, samba3_objectclasses, NULL, NULL, "samba3sam");
+ if (ret != LDB_SUCCESS)
+ return ret;
+
+ return ldb_next_init(module);
+}
+
+_PUBLIC_ const struct ldb_module_ops ldb_samba3sam_module_ops = {
+ LDB_MAP_OPS
+ .name = "samba3sam",
+ .init_context = samba3sam_init,
+};
diff --git a/source4/dsdb/samdb/ldb_modules/samldb.c b/source4/dsdb/samdb/ldb_modules/samldb.c
new file mode 100644
index 0000000000..65e36416f1
--- /dev/null
+++ b/source4/dsdb/samdb/ldb_modules/samldb.c
@@ -0,0 +1,1416 @@
+/*
+ SAM ldb module
+
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
+ Copyright (C) Simo Sorce 2004-2008
+
+ * NOTICE: this module is NOT released under the GNU LGPL license as
+ * other ldb code. This module is release under the GNU GPL v3 or
+ * later license.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb samldb module
+ *
+ * Description: add embedded user/group creation functionality
+ *
+ * Author: Simo Sorce
+ */
+
+#include "includes.h"
+#include "libcli/ldap/ldap_ndr.h"
+#include "ldb_module.h"
+#include "dsdb/samdb/samdb.h"
+#include "libcli/security/security.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "../lib/util/util_ldb.h"
+#include "ldb_wrap.h"
+
+struct samldb_ctx;
+
+typedef int (*samldb_step_fn_t)(struct samldb_ctx *);
+
+struct samldb_step {
+ struct samldb_step *next;
+ samldb_step_fn_t fn;
+};
+
+struct samldb_ctx {
+ struct ldb_module *module;
+ struct ldb_request *req;
+
+ /* the resulting message */
+ struct ldb_message *msg;
+
+ /* used to apply templates */
+ const char *type;
+
+ /* used to find parent domain */
+ struct ldb_dn *check_dn;
+ struct ldb_dn *domain_dn;
+ struct dom_sid *domain_sid;
+ uint32_t next_rid;
+
+ /* generic storage, remember to zero it before use */
+ struct ldb_reply *ares;
+
+ /* holds the entry SID */
+ struct dom_sid *sid;
+
+ /* all the async steps necessary to complete the operation */
+ struct samldb_step *steps;
+ struct samldb_step *curstep;
+};
+
+static struct samldb_ctx *samldb_ctx_init(struct ldb_module *module,
+ struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct samldb_ctx *ac;
+
+ ldb = ldb_module_get_ctx(module);
+
+ ac = talloc_zero(req, struct samldb_ctx);
+ if (ac == NULL) {
+ ldb_oom(ldb);
+ return NULL;
+ }
+
+ ac->module = module;
+ ac->req = req;
+
+ return ac;
+}
+
+static int samldb_add_step(struct samldb_ctx *ac, samldb_step_fn_t fn)
+{
+ struct samldb_step *step;
+
+ step = talloc_zero(ac, struct samldb_step);
+ if (step == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if (ac->steps == NULL) {
+ ac->steps = step;
+ ac->curstep = step;
+ } else {
+ ac->curstep->next = step;
+ ac->curstep = step;
+ }
+
+ step->fn = fn;
+
+ return LDB_SUCCESS;
+}
+
+static int samldb_first_step(struct samldb_ctx *ac)
+{
+ if (ac->steps == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->curstep = ac->steps;
+ return ac->curstep->fn(ac);
+}
+
+static int samldb_next_step(struct samldb_ctx *ac)
+{
+ if (ac->curstep->next) {
+ ac->curstep = ac->curstep->next;
+ return ac->curstep->fn(ac);
+ }
+
+ /* it is an error if the last step does not properly
+ * return to the upper module by itself */
+ return LDB_ERR_OPERATIONS_ERROR;
+}
+
+static int samldb_search_template_callback(struct ldb_request *req,
+ struct ldb_reply *ares)
+{
+ struct ldb_context *ldb;
+ struct samldb_ctx *ac;
+ int ret;
+
+ ac = talloc_get_type(req->context, struct samldb_ctx);
+ ldb = ldb_module_get_ctx(ac->module);
+
+ if (!ares) {
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ goto done;
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+ /* save entry */
+ if (ac->ares != NULL) {
+ /* one too many! */
+ ldb_set_errstring(ldb,
+ "Invalid number of results while searching "
+ "for template objects");
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ goto done;
+ }
+
+ ac->ares = talloc_steal(ac, ares);
+ ret = LDB_SUCCESS;
+ break;
+
+ case LDB_REPLY_REFERRAL:
+ /* ignore */
+ talloc_free(ares);
+ ret = LDB_SUCCESS;
+ break;
+
+ case LDB_REPLY_DONE:
+
+ talloc_free(ares);
+ ret = samldb_next_step(ac);
+ break;
+ }
+
+done:
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+
+ return LDB_SUCCESS;
+}
+
+static int samldb_search_template(struct samldb_ctx *ac)
+{
+ struct ldb_context *ldb;
+ struct tevent_context *ev;
+ struct loadparm_context *lparm_ctx;
+ struct ldb_context *templates_ldb;
+ char *templates_ldb_path;
+ struct ldb_request *req;
+ struct ldb_dn *basedn;
+ void *opaque;
+ int ret;
+
+ ldb = ldb_module_get_ctx(ac->module);
+
+ opaque = ldb_get_opaque(ldb, "loadparm");
+ lparm_ctx = talloc_get_type(opaque, struct loadparm_context);
+ if (lparm_ctx == NULL) {
+ ldb_set_errstring(ldb,
+ "Unable to find loadparm context\n");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ opaque = ldb_get_opaque(ldb, "templates_ldb");
+ templates_ldb = talloc_get_type(opaque, struct ldb_context);
+
+ /* make sure we have the templates ldb */
+ if (!templates_ldb) {
+ templates_ldb_path = samdb_relative_path(ldb, ac,
+ "templates.ldb");
+ if (!templates_ldb_path) {
+ ldb_set_errstring(ldb,
+ "samldb_init_template: ERROR: Failed "
+ "to contruct path for template db");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ev = ldb_get_event_context(ldb);
+
+ templates_ldb = ldb_wrap_connect(ldb, ev,
+ lparm_ctx, templates_ldb_path,
+ NULL, NULL, 0, NULL);
+ talloc_free(templates_ldb_path);
+
+ if (!templates_ldb) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if (!talloc_reference(templates_ldb, ev)) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ldb_set_opaque(ldb,
+ "templates_ldb", templates_ldb);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+
+ /* search template */
+ basedn = ldb_dn_new_fmt(ac, templates_ldb,
+ "cn=Template%s,cn=Templates", ac->type);
+ if (basedn == NULL) {
+ ldb_set_errstring(ldb,
+ "samldb_init_template: ERROR: Failed "
+ "to contruct DN for template");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* pull the template record */
+ ret = ldb_build_search_req(&req, templates_ldb, ac,
+ basedn, LDB_SCOPE_BASE,
+ "(distinguishedName=*)", NULL,
+ NULL,
+ ac, samldb_search_template_callback,
+ ac->req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ talloc_steal(req, basedn);
+ ac->ares = NULL;
+
+ return ldb_request(templates_ldb, req);
+}
+
+static int samldb_apply_template(struct samldb_ctx *ac)
+{
+ struct ldb_context *ldb;
+ struct ldb_message_element *el;
+ struct ldb_message *msg;
+ int i, j;
+ int ret;
+
+ ldb = ldb_module_get_ctx(ac->module);
+ msg = ac->ares->message;
+
+ for (i = 0; i < msg->num_elements; i++) {
+ el = &msg->elements[i];
+ /* some elements should not be copied */
+ if (ldb_attr_cmp(el->name, "cn") == 0 ||
+ ldb_attr_cmp(el->name, "name") == 0 ||
+ ldb_attr_cmp(el->name, "objectClass") == 0 ||
+ ldb_attr_cmp(el->name, "sAMAccountName") == 0 ||
+ ldb_attr_cmp(el->name, "sAMAccountName") == 0 ||
+ ldb_attr_cmp(el->name, "distinguishedName") == 0 ||
+ ldb_attr_cmp(el->name, "objectGUID") == 0) {
+ continue;
+ }
+ for (j = 0; j < el->num_values; j++) {
+ ret = samdb_find_or_add_attribute(
+ ldb, ac->msg, el->name,
+ (char *)el->values[j].data);
+ if (ret != LDB_SUCCESS) {
+ ldb_set_errstring(ldb,
+ "Failed adding template attribute\n");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ }
+ }
+
+ return samldb_next_step(ac);
+}
+
+static int samldb_get_parent_domain(struct samldb_ctx *ac);
+
+static int samldb_get_parent_domain_callback(struct ldb_request *req,
+ struct ldb_reply *ares)
+{
+ struct ldb_context *ldb;
+ struct samldb_ctx *ac;
+ const char *nextRid;
+ int ret;
+
+ ac = talloc_get_type(req->context, struct samldb_ctx);
+ ldb = ldb_module_get_ctx(ac->module);
+
+ if (!ares) {
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ goto done;
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+ /* save entry */
+ if (ac->domain_dn != NULL) {
+ /* one too many! */
+ ldb_set_errstring(ldb,
+ "Invalid number of results while searching "
+ "for domain object");
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ break;
+ }
+
+ nextRid = ldb_msg_find_attr_as_string(ares->message,
+ "nextRid", NULL);
+ if (nextRid == NULL) {
+ ldb_asprintf_errstring(ldb,
+ "while looking for domain above %s attribute nextRid not found in %s\n",
+ ldb_dn_get_linearized(ac->req->op.add.message->dn),
+ ldb_dn_get_linearized(ares->message->dn));
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ break;
+ }
+
+ ac->next_rid = strtol(nextRid, NULL, 0);
+
+ ac->domain_sid = samdb_result_dom_sid(ac, ares->message,
+ "objectSid");
+ if (ac->domain_sid == NULL) {
+ ldb_set_errstring(ldb,
+ "error retrieving parent domain domain sid!\n");
+ ret = LDB_ERR_CONSTRAINT_VIOLATION;
+ break;
+ }
+ ac->domain_dn = talloc_steal(ac, ares->message->dn);
+
+ talloc_free(ares);
+ ret = LDB_SUCCESS;
+ ldb_reset_err_string(ldb);
+ break;
+
+ case LDB_REPLY_REFERRAL:
+ /* ignore */
+ talloc_free(ares);
+ ret = LDB_SUCCESS;
+ break;
+
+ case LDB_REPLY_DONE:
+
+ talloc_free(ares);
+ if (ac->domain_dn == NULL) {
+ /* search again */
+ ret = samldb_get_parent_domain(ac);
+ } else {
+ /* found, go on */
+ ret = samldb_next_step(ac);
+ }
+ break;
+ }
+
+done:
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+
+ return LDB_SUCCESS;
+}
+
+/* Find a domain object in the parents of a particular DN. */
+static int samldb_get_parent_domain(struct samldb_ctx *ac)
+{
+ struct ldb_context *ldb;
+ static const char * const attrs[3] = { "objectSid", "nextRid", NULL };
+ struct ldb_request *req;
+ struct ldb_dn *dn;
+ int ret;
+
+ ldb = ldb_module_get_ctx(ac->module);
+
+ if (ac->check_dn == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ dn = ldb_dn_get_parent(ac, ac->check_dn);
+ if (dn == NULL) {
+ ldb_set_errstring(ldb,
+ "Unable to find parent domain object");
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+
+ ac->check_dn = dn;
+
+ ret = ldb_build_search_req(&req, ldb, ac,
+ dn, LDB_SCOPE_BASE,
+ "(|(objectClass=domain)"
+ "(objectClass=builtinDomain)"
+ "(objectClass=samba4LocalDomain))",
+ attrs,
+ NULL,
+ ac, samldb_get_parent_domain_callback,
+ ac->req);
+
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ return ldb_next_request(ac->module, req);
+}
+
+static int samldb_generate_samAccountName(struct ldb_message *msg)
+{
+ char *name;
+
+ /* Format: $000000-000000000000 */
+
+ name = talloc_asprintf(msg, "$%.6X-%.6X%.6X",
+ (unsigned int)generate_random(),
+ (unsigned int)generate_random(),
+ (unsigned int)generate_random());
+ if (name == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ return ldb_msg_add_steal_string(msg, "samAccountName", name);
+}
+
+static int samldb_check_samAccountName_callback(struct ldb_request *req,
+ struct ldb_reply *ares)
+{
+ struct samldb_ctx *ac;
+ int ret;
+
+ ac = talloc_get_type(req->context, struct samldb_ctx);
+
+ if (!ares) {
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ goto done;
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+
+ /* if we get an entry it means this samAccountName
+ * already exists */
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_ENTRY_ALREADY_EXISTS);
+
+ case LDB_REPLY_REFERRAL:
+ /* ignore */
+ talloc_free(ares);
+ ret = LDB_SUCCESS;
+ break;
+
+ case LDB_REPLY_DONE:
+
+ /* not found, go on */
+ talloc_free(ares);
+ ret = samldb_next_step(ac);
+ break;
+ }
+
+done:
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+
+ return LDB_SUCCESS;
+}
+
+static int samldb_check_samAccountName(struct samldb_ctx *ac)
+{
+ struct ldb_context *ldb;
+ struct ldb_request *req;
+ const char *name;
+ char *filter;
+ int ret;
+
+ ldb = ldb_module_get_ctx(ac->module);
+
+ if (ldb_msg_find_element(ac->msg, "samAccountName") == NULL) {
+ ret = samldb_generate_samAccountName(ac->msg);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+
+ name = ldb_msg_find_attr_as_string(ac->msg, "samAccountName", NULL);
+ if (name == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ filter = talloc_asprintf(ac, "samAccountName=%s", name);
+ if (filter == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ldb_build_search_req(&req, ldb, ac,
+ ac->domain_dn, LDB_SCOPE_SUBTREE,
+ filter, NULL,
+ NULL,
+ ac, samldb_check_samAccountName_callback,
+ ac->req);
+ talloc_free(filter);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ ac->ares = NULL;
+ return ldb_next_request(ac->module, req);
+}
+
+static int samldb_check_samAccountType(struct samldb_ctx *ac)
+{
+ struct ldb_context *ldb;
+ unsigned int account_type;
+ unsigned int group_type;
+ unsigned int uac;
+ int ret;
+
+ ldb = ldb_module_get_ctx(ac->module);
+
+ /* make sure sAMAccountType is not specified */
+ if (ldb_msg_find_element(ac->msg, "sAMAccountType") != NULL) {
+ ldb_asprintf_errstring(ldb,
+ "sAMAccountType must not be specified");
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+
+ if (strcmp("user", ac->type) == 0) {
+ uac = samdb_result_uint(ac->msg, "userAccountControl", 0);
+ if (uac == 0) {
+ ldb_asprintf_errstring(ldb,
+ "userAccountControl invalid");
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ } else {
+ account_type = samdb_uf2atype(uac);
+ ret = samdb_msg_add_uint(ldb,
+ ac->msg, ac->msg,
+ "sAMAccountType",
+ account_type);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+ } else
+ if (strcmp("group", ac->type) == 0) {
+
+ group_type = samdb_result_uint(ac->msg, "groupType", 0);
+ if (group_type == 0) {
+ ldb_asprintf_errstring(ldb,
+ "groupType invalid");
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ } else {
+ account_type = samdb_gtype2atype(group_type);
+ ret = samdb_msg_add_uint(ldb,
+ ac->msg, ac->msg,
+ "sAMAccountType",
+ account_type);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+ }
+
+ return samldb_next_step(ac);
+}
+
+static int samldb_get_sid_domain_callback(struct ldb_request *req,
+ struct ldb_reply *ares)
+{
+ struct ldb_context *ldb;
+ struct samldb_ctx *ac;
+ const char *nextRid;
+ int ret;
+
+ ac = talloc_get_type(req->context, struct samldb_ctx);
+ ldb = ldb_module_get_ctx(ac->module);
+
+ if (!ares) {
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ goto done;
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+ /* save entry */
+ if (ac->next_rid != 0) {
+ /* one too many! */
+ ldb_set_errstring(ldb,
+ "Invalid number of results while searching "
+ "for domain object");
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ break;
+ }
+
+ nextRid = ldb_msg_find_attr_as_string(ares->message,
+ "nextRid", NULL);
+ if (nextRid == NULL) {
+ ldb_asprintf_errstring(ldb,
+ "attribute nextRid not found in %s\n",
+ ldb_dn_get_linearized(ares->message->dn));
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ break;
+ }
+
+ ac->next_rid = strtol(nextRid, NULL, 0);
+
+ ac->domain_dn = talloc_steal(ac, ares->message->dn);
+
+ talloc_free(ares);
+ ret = LDB_SUCCESS;
+ break;
+
+ case LDB_REPLY_REFERRAL:
+ /* ignore */
+ talloc_free(ares);
+ ret = LDB_SUCCESS;
+ break;
+
+ case LDB_REPLY_DONE:
+
+ if (ac->next_rid == 0) {
+ ldb_asprintf_errstring(ldb,
+ "Unable to get nextRid from domain entry\n");
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ break;
+ }
+
+ /* found, go on */
+ ret = samldb_next_step(ac);
+ break;
+ }
+
+done:
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+
+ return LDB_SUCCESS;
+}
+
+/* Find a domain object in the parents of a particular DN. */
+static int samldb_get_sid_domain(struct samldb_ctx *ac)
+{
+ struct ldb_context *ldb;
+ static const char * const attrs[2] = { "nextRid", NULL };
+ struct ldb_request *req;
+ char *filter;
+ int ret;
+
+ ldb = ldb_module_get_ctx(ac->module);
+
+ if (ac->sid == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->domain_sid = dom_sid_dup(ac, ac->sid);
+ if (!ac->domain_sid) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ /* get the domain component part of the provided SID */
+ ac->domain_sid->num_auths--;
+
+ filter = talloc_asprintf(ac, "(&(objectSid=%s)"
+ "(|(objectClass=domain)"
+ "(objectClass=builtinDomain)"
+ "(objectClass=samba4LocalDomain)))",
+ ldap_encode_ndr_dom_sid(ac, ac->domain_sid));
+ if (filter == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ldb_build_search_req(&req, ldb, ac,
+ ldb_get_default_basedn(ldb),
+ LDB_SCOPE_SUBTREE,
+ filter, attrs,
+ NULL,
+ ac, samldb_get_sid_domain_callback,
+ ac->req);
+
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ ac->next_rid = 0;
+ return ldb_next_request(ac->module, req);
+}
+
+static bool samldb_msg_add_sid(struct ldb_message *msg,
+ const char *name,
+ const struct dom_sid *sid)
+{
+ struct ldb_val v;
+ enum ndr_err_code ndr_err;
+
+ ndr_err = ndr_push_struct_blob(&v, msg, NULL, sid,
+ (ndr_push_flags_fn_t)ndr_push_dom_sid);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return false;
+ }
+ return (ldb_msg_add_value(msg, name, &v, NULL) == 0);
+}
+
+static int samldb_new_sid(struct samldb_ctx *ac)
+{
+
+ if (ac->domain_sid == NULL || ac->next_rid == 0) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->sid = dom_sid_add_rid(ac, ac->domain_sid, ac->next_rid + 1);
+ if (ac->sid == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if ( ! samldb_msg_add_sid(ac->msg, "objectSid", ac->sid)) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ return samldb_next_step(ac);
+}
+
+static int samldb_check_sid_callback(struct ldb_request *req,
+ struct ldb_reply *ares)
+{
+ struct samldb_ctx *ac;
+ int ret;
+
+ ac = talloc_get_type(req->context, struct samldb_ctx);
+
+ if (!ares) {
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ goto done;
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+
+ /* if we get an entry it means an object with the
+ * requested sid exists */
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_CONSTRAINT_VIOLATION);
+
+ case LDB_REPLY_REFERRAL:
+ /* ignore */
+ talloc_free(ares);
+ break;
+
+ case LDB_REPLY_DONE:
+
+ /* not found, go on */
+ talloc_free(ares);
+ ret = samldb_next_step(ac);
+ break;
+ }
+
+done:
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+
+ return LDB_SUCCESS;
+}
+
+static int samldb_check_sid(struct samldb_ctx *ac)
+{
+ struct ldb_context *ldb;
+ const char *const attrs[2] = { "objectSid", NULL };
+ struct ldb_request *req;
+ char *filter;
+ int ret;
+
+ if (ac->sid == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ldb = ldb_module_get_ctx(ac->module);
+
+ filter = talloc_asprintf(ac, "(objectSid=%s)",
+ ldap_encode_ndr_dom_sid(ac, ac->sid));
+ if (filter == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ldb_build_search_req(&req, ldb, ac,
+ ldb_get_default_basedn(ldb),
+ LDB_SCOPE_SUBTREE,
+ filter, attrs,
+ NULL,
+ ac, samldb_check_sid_callback,
+ ac->req);
+
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ return ldb_next_request(ac->module, req);
+}
+
+static int samldb_notice_sid_callback(struct ldb_request *req,
+ struct ldb_reply *ares)
+{
+ struct ldb_context *ldb;
+ struct samldb_ctx *ac;
+ int ret;
+
+ ac = talloc_get_type(req->context, struct samldb_ctx);
+ ldb = ldb_module_get_ctx(ac->module);
+
+ if (!ares) {
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ goto done;
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+ if (ares->type != LDB_REPLY_DONE) {
+ ldb_set_errstring(ldb,
+ "Invalid reply type!\n");
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ goto done;
+ }
+
+ ret = samldb_next_step(ac);
+
+done:
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+
+ return LDB_SUCCESS;
+}
+
+/* If we are adding new users/groups, we need to update the nextRid
+ * attribute to be 'above' the new/incoming RID. Attempt to do it
+ *atomically. */
+static int samldb_notice_sid(struct samldb_ctx *ac)
+{
+ struct ldb_context *ldb;
+ uint32_t old_id, new_id;
+ struct ldb_request *req;
+ struct ldb_message *msg;
+ struct ldb_message_element *els;
+ struct ldb_val *vals;
+ int ret;
+
+ ldb = ldb_module_get_ctx(ac->module);
+ old_id = ac->next_rid;
+ new_id = ac->sid->sub_auths[ac->sid->num_auths - 1];
+
+ if (old_id >= new_id) {
+ /* no need to update the domain nextRid attribute */
+ return samldb_next_step(ac);
+ }
+
+ /* we do a delete and add as a single operation. That prevents
+ a race, in case we are not actually on a transaction db */
+ msg = talloc_zero(ac, struct ldb_message);
+ if (msg == NULL) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ els = talloc_array(msg, struct ldb_message_element, 2);
+ if (els == NULL) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ vals = talloc_array(msg, struct ldb_val, 2);
+ if (vals == NULL) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ msg->dn = ac->domain_dn;
+ msg->num_elements = 2;
+ msg->elements = els;
+
+ els[0].num_values = 1;
+ els[0].values = &vals[0];
+ els[0].flags = LDB_FLAG_MOD_DELETE;
+ els[0].name = talloc_strdup(msg, "nextRid");
+ if (!els[0].name) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ els[1].num_values = 1;
+ els[1].values = &vals[1];
+ els[1].flags = LDB_FLAG_MOD_ADD;
+ els[1].name = els[0].name;
+
+ vals[0].data = (uint8_t *)talloc_asprintf(vals, "%u", old_id);
+ if (!vals[0].data) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ vals[0].length = strlen((char *)vals[0].data);
+
+ vals[1].data = (uint8_t *)talloc_asprintf(vals, "%u", new_id);
+ if (!vals[1].data) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ vals[1].length = strlen((char *)vals[1].data);
+
+ ret = ldb_build_mod_req(&req, ldb, ac,
+ msg, NULL,
+ ac, samldb_notice_sid_callback,
+ ac->req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ return ldb_next_request(ac->module, req);
+}
+
+static int samldb_add_entry_callback(struct ldb_request *req,
+ struct ldb_reply *ares)
+{
+ struct ldb_context *ldb;
+ struct samldb_ctx *ac;
+
+ ac = talloc_get_type(req->context, struct samldb_ctx);
+ ldb = ldb_module_get_ctx(ac->module);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+ if (ares->type != LDB_REPLY_DONE) {
+ ldb_set_errstring(ldb,
+ "Invalid reply type!\n");
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ /* we exit the samldb module here */
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, LDB_SUCCESS);
+}
+
+static int samldb_add_entry(struct samldb_ctx *ac)
+{
+ struct ldb_context *ldb;
+ struct ldb_request *req;
+ int ret;
+
+ ldb = ldb_module_get_ctx(ac->module);
+
+ ret = ldb_build_add_req(&req, ldb, ac,
+ ac->msg,
+ ac->req->controls,
+ ac, samldb_add_entry_callback,
+ ac->req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ return ldb_next_request(ac->module, req);
+}
+
+static int samldb_fill_object(struct samldb_ctx *ac, const char *type)
+{
+ int ret;
+
+ /* first look for the template */
+ ac->type = type;
+ ret = samldb_add_step(ac, samldb_search_template);
+ if (ret != LDB_SUCCESS) return ret;
+
+ /* then apply it */
+ ret = samldb_add_step(ac, samldb_apply_template);
+ if (ret != LDB_SUCCESS) return ret;
+
+ /* search for a parent domain objet */
+ ac->check_dn = ac->req->op.add.message->dn;
+ ret = samldb_add_step(ac, samldb_get_parent_domain);
+ if (ret != LDB_SUCCESS) return ret;
+
+ /* check if we have a valid samAccountName */
+ ret = samldb_add_step(ac, samldb_check_samAccountName);
+ if (ret != LDB_SUCCESS) return ret;
+
+ /* check account_type/group_type */
+ ret = samldb_add_step(ac, samldb_check_samAccountType);
+ if (ret != LDB_SUCCESS) return ret;
+
+ /* check if we have a valid SID */
+ ac->sid = samdb_result_dom_sid(ac, ac->msg, "objectSid");
+ if ( ! ac->sid) {
+ ret = samldb_add_step(ac, samldb_new_sid);
+ if (ret != LDB_SUCCESS) return ret;
+ } else {
+ ret = samldb_add_step(ac, samldb_get_sid_domain);
+ if (ret != LDB_SUCCESS) return ret;
+ }
+
+ ret = samldb_add_step(ac, samldb_check_sid);
+ if (ret != LDB_SUCCESS) return ret;
+
+ ret = samldb_add_step(ac, samldb_notice_sid);
+ if (ret != LDB_SUCCESS) return ret;
+
+ /* finally proceed with adding the entry */
+ ret = samldb_add_step(ac, samldb_add_entry);
+ if (ret != LDB_SUCCESS) return ret;
+
+ return samldb_first_step(ac);
+
+ /* TODO: userAccountControl, badPwdCount, codePage,
+ * countryCode, badPasswordTime, lastLogoff, lastLogon,
+ * pwdLastSet, primaryGroupID, accountExpires, logonCount */
+
+}
+
+static int samldb_foreign_notice_sid_callback(struct ldb_request *req,
+ struct ldb_reply *ares)
+{
+ struct ldb_context *ldb;
+ struct samldb_ctx *ac;
+ const char *nextRid;
+ const char *name;
+ int ret;
+
+ ac = talloc_get_type(req->context, struct samldb_ctx);
+ ldb = ldb_module_get_ctx(ac->module);
+
+ if (!ares) {
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ goto done;
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+ /* save entry */
+ if (ac->next_rid != 0) {
+ /* one too many! */
+ ldb_set_errstring(ldb,
+ "Invalid number of results while searching "
+ "for domain object");
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ break;
+ }
+
+ nextRid = ldb_msg_find_attr_as_string(ares->message,
+ "nextRid", NULL);
+ if (nextRid == NULL) {
+ ldb_asprintf_errstring(ldb,
+ "while looking for forign sid %s attribute nextRid not found in %s\n",
+ dom_sid_string(ares, ac->sid), ldb_dn_get_linearized(ares->message->dn));
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ break;
+ }
+
+ ac->next_rid = strtol(nextRid, NULL, 0);
+
+ ac->domain_dn = talloc_steal(ac, ares->message->dn);
+
+ name = samdb_result_string(ares->message, "name", NULL);
+ ldb_debug(ldb, LDB_DEBUG_TRACE,
+ "NOTE (strange but valid): Adding foreign SID "
+ "record with SID %s, but this domain (%s) is "
+ "not foreign in the database",
+ dom_sid_string(ares, ac->sid), name);
+
+ talloc_free(ares);
+ break;
+
+ case LDB_REPLY_REFERRAL:
+ /* ignore */
+ talloc_free(ares);
+ break;
+
+ case LDB_REPLY_DONE:
+
+ /* if this is a fake foreign SID, notice the SID */
+ if (ac->domain_dn) {
+ ret = samldb_notice_sid(ac);
+ break;
+ }
+
+ /* found, go on */
+ ret = samldb_next_step(ac);
+ break;
+ }
+
+done:
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+
+ return LDB_SUCCESS;
+}
+
+/* Find a domain object in the parents of a particular DN. */
+static int samldb_foreign_notice_sid(struct samldb_ctx *ac)
+{
+ struct ldb_context *ldb;
+ static const char * const attrs[3] = { "nextRid", "name", NULL };
+ struct ldb_request *req;
+ NTSTATUS status;
+ char *filter;
+ int ret;
+
+ ldb = ldb_module_get_ctx(ac->module);
+
+ if (ac->sid == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ status = dom_sid_split_rid(ac, ac->sid, &ac->domain_sid, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ filter = talloc_asprintf(ac, "(&(objectSid=%s)(objectclass=domain))",
+ ldap_encode_ndr_dom_sid(ac, ac->domain_sid));
+ if (filter == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ldb_build_search_req(&req, ldb, ac,
+ ldb_get_default_basedn(ldb),
+ LDB_SCOPE_SUBTREE,
+ filter, attrs,
+ NULL,
+ ac, samldb_foreign_notice_sid_callback,
+ ac->req);
+
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ ac->next_rid = 0;
+ return ldb_next_request(ac->module, req);
+}
+
+static int samldb_fill_foreignSecurityPrincipal_object(struct samldb_ctx *ac)
+{
+ struct ldb_context *ldb;
+ int ret;
+
+ ldb = ldb_module_get_ctx(ac->module);
+
+ ac->sid = samdb_result_dom_sid(ac->msg, ac->msg, "objectSid");
+ if (ac->sid == NULL) {
+ ac->sid = dom_sid_parse_talloc(ac->msg,
+ (const char *)ldb_dn_get_rdn_val(ac->msg->dn)->data);
+ if (!ac->sid) {
+ ldb_set_errstring(ldb,
+ "No valid found SID in "
+ "ForeignSecurityPrincipal CN!");
+ talloc_free(ac);
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+ if ( ! samldb_msg_add_sid(ac->msg, "objectSid", ac->sid)) {
+ talloc_free(ac);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ }
+
+ /* first look for the template */
+ ac->type = "foreignSecurityPrincipal";
+ ret = samldb_add_step(ac, samldb_search_template);
+ if (ret != LDB_SUCCESS) return ret;
+
+ /* then apply it */
+ ret = samldb_add_step(ac, samldb_apply_template);
+ if (ret != LDB_SUCCESS) return ret;
+
+ /* check we do not already have this SID */
+ ret = samldb_add_step(ac, samldb_check_sid);
+ if (ret != LDB_SUCCESS) return ret;
+
+ /* check if we need to notice this SID */
+ ret = samldb_add_step(ac, samldb_foreign_notice_sid);
+ if (ret != LDB_SUCCESS) return ret;
+
+ /* finally proceed with adding the entry */
+ ret = samldb_add_step(ac, samldb_add_entry);
+ if (ret != LDB_SUCCESS) return ret;
+
+ return samldb_first_step(ac);
+}
+
+static int samldb_check_rdn(struct ldb_module *module, struct ldb_dn *dn)
+{
+ struct ldb_context *ldb;
+ const char *rdn_name;
+
+ ldb = ldb_module_get_ctx(module);
+ rdn_name = ldb_dn_get_rdn_name(dn);
+
+ if (strcasecmp(rdn_name, "cn") != 0) {
+ ldb_asprintf_errstring(ldb,
+ "Bad RDN (%s=) for samldb object, "
+ "should be CN=!\n", rdn_name);
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+
+ return LDB_SUCCESS;
+}
+
+/* add_record */
+static int samldb_add(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct samldb_ctx *ac;
+ int ret;
+
+ ldb = ldb_module_get_ctx(module);
+ ldb_debug(ldb, LDB_DEBUG_TRACE, "samldb_add_record\n");
+
+ /* do not manipulate our control entries */
+ if (ldb_dn_is_special(req->op.add.message->dn)) {
+ return ldb_next_request(module, req);
+ }
+
+ ac = samldb_ctx_init(module, req);
+ if (ac == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* build the new msg */
+ ac->msg = ldb_msg_copy(ac, ac->req->op.add.message);
+ if (!ac->msg) {
+ talloc_free(ac);
+ ldb_debug(ldb, LDB_DEBUG_FATAL,
+ "samldb_add: ldb_msg_copy failed!\n");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if (samdb_find_attribute(ldb, ac->msg,
+ "objectclass", "computer") != NULL) {
+
+ /* make sure the computer object also has the 'user'
+ * objectclass so it will be handled by the next call */
+ ret = samdb_find_or_add_value(ldb, ac->msg,
+ "objectclass", "user");
+ if (ret != LDB_SUCCESS) {
+ talloc_free(ac);
+ return ret;
+ }
+ }
+
+ if (samdb_find_attribute(ldb, ac->msg,
+ "objectclass", "user") != NULL) {
+
+ ret = samldb_check_rdn(module, ac->req->op.add.message->dn);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(ac);
+ return ret;
+ }
+
+ return samldb_fill_object(ac, "user");
+ }
+
+ if (samdb_find_attribute(ldb, ac->msg,
+ "objectclass", "group") != NULL) {
+
+ ret = samldb_check_rdn(module, ac->req->op.add.message->dn);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(ac);
+ return ret;
+ }
+
+ return samldb_fill_object(ac, "group");
+ }
+
+ /* perhaps a foreignSecurityPrincipal? */
+ if (samdb_find_attribute(ldb, ac->msg,
+ "objectclass",
+ "foreignSecurityPrincipal") != NULL) {
+
+ ret = samldb_check_rdn(module, ac->req->op.add.message->dn);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(ac);
+ return ret;
+ }
+
+ return samldb_fill_foreignSecurityPrincipal_object(ac);
+ }
+
+ talloc_free(ac);
+
+ /* nothing matched, go on */
+ return ldb_next_request(module, req);
+}
+
+/* modify */
+static int samldb_modify(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct ldb_message *msg;
+ struct ldb_message_element *el, *el2;
+ int ret;
+ unsigned int group_type, user_account_control, account_type;
+ if (ldb_dn_is_special(req->op.mod.message->dn)) { /* do not manipulate our control entries */
+ return ldb_next_request(module, req);
+ }
+
+ ldb = ldb_module_get_ctx(module);
+
+ if (ldb_msg_find_element(req->op.mod.message, "sAMAccountType") != NULL) {
+ ldb_asprintf_errstring(ldb, "sAMAccountType must not be specified");
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+
+ /* TODO: do not modify original request, create a new one */
+
+ el = ldb_msg_find_element(req->op.mod.message, "groupType");
+ if (el && el->flags & (LDB_FLAG_MOD_ADD|LDB_FLAG_MOD_REPLACE) && el->num_values == 1) {
+ req->op.mod.message = msg = ldb_msg_copy_shallow(req, req->op.mod.message);
+
+ group_type = strtoul((const char *)el->values[0].data, NULL, 0);
+ account_type = samdb_gtype2atype(group_type);
+ ret = samdb_msg_add_uint(ldb, msg, msg,
+ "sAMAccountType",
+ account_type);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ el2 = ldb_msg_find_element(msg, "sAMAccountType");
+ el2->flags = LDB_FLAG_MOD_REPLACE;
+ }
+
+ el = ldb_msg_find_element(req->op.mod.message, "userAccountControl");
+ if (el && el->flags & (LDB_FLAG_MOD_ADD|LDB_FLAG_MOD_REPLACE) && el->num_values == 1) {
+ req->op.mod.message = msg = ldb_msg_copy_shallow(req, req->op.mod.message);
+
+ user_account_control = strtoul((const char *)el->values[0].data, NULL, 0);
+ account_type = samdb_uf2atype(user_account_control);
+ ret = samdb_msg_add_uint(ldb, msg, msg,
+ "sAMAccountType",
+ account_type);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ el2 = ldb_msg_find_element(msg, "sAMAccountType");
+ el2->flags = LDB_FLAG_MOD_REPLACE;
+ }
+ return ldb_next_request(module, req);
+}
+
+
+static int samldb_init(struct ldb_module *module)
+{
+ return ldb_next_init(module);
+}
+
+_PUBLIC_ const struct ldb_module_ops ldb_samldb_module_ops = {
+ .name = "samldb",
+ .init_context = samldb_init,
+ .add = samldb_add,
+ .modify = samldb_modify
+};
diff --git a/source4/dsdb/samdb/ldb_modules/schema_fsmo.c b/source4/dsdb/samdb/ldb_modules/schema_fsmo.c
new file mode 100644
index 0000000000..edd451255e
--- /dev/null
+++ b/source4/dsdb/samdb/ldb_modules/schema_fsmo.c
@@ -0,0 +1,499 @@
+/*
+ Unix SMB/CIFS mplementation.
+
+ The module that handles the Schema FSMO Role Owner
+ checkings, it also loads the dsdb_schema.
+
+ Copyright (C) Stefan Metzmacher <metze@samba.org> 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 <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "includes.h"
+#include "ldb_module.h"
+#include "dsdb/samdb/samdb.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "librpc/gen_ndr/ndr_drsuapi.h"
+#include "librpc/gen_ndr/ndr_drsblobs.h"
+#include "param/param.h"
+
+static int generate_objectClasses(struct ldb_context *ldb, struct ldb_message *msg,
+ const struct dsdb_schema *schema);
+static int generate_attributeTypes(struct ldb_context *ldb, struct ldb_message *msg,
+ const struct dsdb_schema *schema);
+static int generate_dITContentRules(struct ldb_context *ldb, struct ldb_message *msg,
+ const struct dsdb_schema *schema);
+static int generate_extendedAttributeInfo(struct ldb_context *ldb, struct ldb_message *msg,
+ const struct dsdb_schema *schema);
+static int generate_extendedClassInfo(struct ldb_context *ldb, struct ldb_message *msg,
+ const struct dsdb_schema *schema);
+
+static const struct {
+ const char *attr;
+ int (*fn)(struct ldb_context *, struct ldb_message *, const struct dsdb_schema *);
+} generated_attrs[] = {
+ {
+ .attr = "objectClasses",
+ .fn = generate_objectClasses
+ },
+ {
+ .attr = "attributeTypes",
+ .fn = generate_attributeTypes
+ },
+ {
+ .attr = "dITContentRules",
+ .fn = generate_dITContentRules
+ },
+ {
+ .attr = "extendedAttributeInfo",
+ .fn = generate_extendedAttributeInfo
+ },
+ {
+ .attr = "extendedClassInfo",
+ .fn = generate_extendedClassInfo
+ }
+};
+
+struct schema_fsmo_private_data {
+ struct ldb_dn *aggregate_dn;
+};
+
+struct schema_fsmo_search_data {
+ struct ldb_module *module;
+ struct ldb_request *req;
+
+ const struct dsdb_schema *schema;
+};
+
+static int schema_fsmo_init(struct ldb_module *module)
+{
+ struct ldb_context *ldb;
+ TALLOC_CTX *mem_ctx;
+ struct ldb_dn *schema_dn;
+ struct dsdb_schema *schema;
+ char *error_string = NULL;
+ int ret;
+ struct schema_fsmo_private_data *data;
+
+ ldb = ldb_module_get_ctx(module);
+ schema_dn = samdb_schema_dn(ldb);
+ if (!schema_dn) {
+ ldb_reset_err_string(ldb);
+ ldb_debug(ldb, LDB_DEBUG_WARNING,
+ "schema_fsmo_init: no schema dn present: (skip schema loading)\n");
+ return ldb_next_init(module);
+ }
+
+ data = talloc(module, struct schema_fsmo_private_data);
+ if (data == NULL) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* Check to see if this is a result on the CN=Aggregate schema */
+ data->aggregate_dn = ldb_dn_copy(data, schema_dn);
+ if (!ldb_dn_add_child_fmt(data->aggregate_dn, "CN=Aggregate")) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ldb_module_set_private(module, data);
+
+ if (dsdb_get_schema(ldb)) {
+ return ldb_next_init(module);
+ }
+
+ mem_ctx = talloc_new(module);
+ if (!mem_ctx) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = dsdb_schema_from_schema_dn(mem_ctx, ldb,
+ lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
+ schema_dn, &schema, &error_string);
+
+ if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+ ldb_reset_err_string(ldb);
+ ldb_debug(ldb, LDB_DEBUG_WARNING,
+ "schema_fsmo_init: no schema head present: (skip schema loading)\n");
+ talloc_free(mem_ctx);
+ return ldb_next_init(module);
+ }
+
+ if (ret != LDB_SUCCESS) {
+ ldb_asprintf_errstring(ldb,
+ "schema_fsmo_init: dsdb_schema load failed: %s",
+ error_string);
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ /* dsdb_set_schema() steal schema into the ldb_context */
+ ret = dsdb_set_schema(ldb, schema);
+ if (ret != LDB_SUCCESS) {
+ ldb_debug_set(ldb, LDB_DEBUG_FATAL,
+ "schema_fsmo_init: dsdb_set_schema() failed: %d:%s",
+ ret, ldb_strerror(ret));
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ talloc_free(mem_ctx);
+ return ldb_next_init(module);
+}
+
+static int schema_fsmo_add(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct dsdb_schema *schema;
+ const char *attributeID = NULL;
+ const char *governsID = NULL;
+ const char *oid_attr = NULL;
+ const char *oid = NULL;
+ uint32_t id32;
+ WERROR status;
+
+ ldb = ldb_module_get_ctx(module);
+
+ /* special objects should always go through */
+ if (ldb_dn_is_special(req->op.add.message->dn)) {
+ return ldb_next_request(module, req);
+ }
+
+ /* replicated update should always go through */
+ if (ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)) {
+ return ldb_next_request(module, req);
+ }
+
+ schema = dsdb_get_schema(ldb);
+ if (!schema) {
+ return ldb_next_request(module, req);
+ }
+
+ if (!schema->fsmo.we_are_master) {
+ ldb_debug_set(ldb, LDB_DEBUG_ERROR,
+ "schema_fsmo_add: we are not master: reject request\n");
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+
+ attributeID = samdb_result_string(req->op.add.message, "attributeID", NULL);
+ governsID = samdb_result_string(req->op.add.message, "governsID", NULL);
+
+ if (attributeID) {
+ oid_attr = "attributeID";
+ oid = attributeID;
+ } else if (governsID) {
+ oid_attr = "governsID";
+ oid = governsID;
+ }
+
+ if (!oid) {
+ return ldb_next_request(module, req);
+ }
+
+ status = dsdb_map_oid2int(schema, oid, &id32);
+ if (W_ERROR_IS_OK(status)) {
+ return ldb_next_request(module, req);
+ } else if (!W_ERROR_EQUAL(WERR_DS_NO_MSDS_INTID, status)) {
+ ldb_debug_set(ldb, LDB_DEBUG_ERROR,
+ "schema_fsmo_add: failed to map %s[%s]: %s\n",
+ oid_attr, oid, win_errstr(status));
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+
+ status = dsdb_create_prefix_mapping(ldb, schema, oid);
+ if (!W_ERROR_IS_OK(status)) {
+ ldb_debug_set(ldb, LDB_DEBUG_ERROR,
+ "schema_fsmo_add: failed to create prefix mapping for %s[%s]: %s\n",
+ oid_attr, oid, win_errstr(status));
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+
+ return ldb_next_request(module, req);
+}
+
+static int schema_fsmo_extended(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct ldb_dn *schema_dn;
+ struct dsdb_schema *schema;
+ char *error_string = NULL;
+ int ret;
+ TALLOC_CTX *mem_ctx;
+
+ ldb = ldb_module_get_ctx(module);
+
+ if (strcmp(req->op.extended.oid, DSDB_EXTENDED_SCHEMA_UPDATE_NOW_OID) != 0) {
+ return ldb_next_request(module, req);
+ }
+
+ schema_dn = samdb_schema_dn(ldb);
+ if (!schema_dn) {
+ ldb_reset_err_string(ldb);
+ ldb_debug(ldb, LDB_DEBUG_WARNING,
+ "schema_fsmo_extended: no schema dn present: (skip schema loading)\n");
+ return ldb_next_request(module, req);
+ }
+
+ mem_ctx = talloc_new(module);
+ if (!mem_ctx) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = dsdb_schema_from_schema_dn(mem_ctx, ldb,
+ lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
+ schema_dn, &schema, &error_string);
+
+ if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+ ldb_reset_err_string(ldb);
+ ldb_debug(ldb, LDB_DEBUG_WARNING,
+ "schema_fsmo_extended: no schema head present: (skip schema loading)\n");
+ talloc_free(mem_ctx);
+ return ldb_next_request(module, req);
+ }
+
+ if (ret != LDB_SUCCESS) {
+ ldb_asprintf_errstring(ldb,
+ "schema_fsmo_extended: dsdb_schema load failed: %s",
+ error_string);
+ talloc_free(mem_ctx);
+ return ldb_next_request(module, req);
+ }
+
+ /* Replace the old schema*/
+ ret = dsdb_set_schema(ldb, schema);
+ if (ret != LDB_SUCCESS) {
+ ldb_debug_set(ldb, LDB_DEBUG_FATAL,
+ "schema_fsmo_extended: dsdb_set_schema() failed: %d:%s",
+ ret, ldb_strerror(ret));
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ talloc_free(mem_ctx);
+ return LDB_SUCCESS;
+}
+
+static int generate_objectClasses(struct ldb_context *ldb, struct ldb_message *msg,
+ const struct dsdb_schema *schema)
+{
+ const struct dsdb_class *sclass;
+ int ret;
+
+ for (sclass = schema->classes; sclass; sclass = sclass->next) {
+ ret = ldb_msg_add_string(msg, "objectClasses", schema_class_to_description(msg, sclass));
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+ return LDB_SUCCESS;
+}
+static int generate_attributeTypes(struct ldb_context *ldb, struct ldb_message *msg,
+ const struct dsdb_schema *schema)
+{
+ const struct dsdb_attribute *attribute;
+ int ret;
+
+ for (attribute = schema->attributes; attribute; attribute = attribute->next) {
+ ret = ldb_msg_add_string(msg, "attributeTypes", schema_attribute_to_description(msg, attribute));
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+ return LDB_SUCCESS;
+}
+
+static int generate_dITContentRules(struct ldb_context *ldb, struct ldb_message *msg,
+ const struct dsdb_schema *schema)
+{
+ const struct dsdb_class *sclass;
+ int ret;
+
+ for (sclass = schema->classes; sclass; sclass = sclass->next) {
+ if (sclass->auxiliaryClass || sclass->systemAuxiliaryClass) {
+ char *ditcontentrule = schema_class_to_dITContentRule(msg, sclass, schema);
+ if (!ditcontentrule) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ret = ldb_msg_add_steal_string(msg, "dITContentRules", ditcontentrule);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+ }
+ return LDB_SUCCESS;
+}
+
+static int generate_extendedAttributeInfo(struct ldb_context *ldb,
+ struct ldb_message *msg,
+ const struct dsdb_schema *schema)
+{
+ const struct dsdb_attribute *attribute;
+ int ret;
+
+ for (attribute = schema->attributes; attribute; attribute = attribute->next) {
+ char *val = schema_attribute_to_extendedInfo(msg, attribute);
+ if (!val) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ldb_msg_add_string(msg, "extendedAttributeInfo", val);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+
+ return LDB_SUCCESS;
+}
+
+static int generate_extendedClassInfo(struct ldb_context *ldb,
+ struct ldb_message *msg,
+ const struct dsdb_schema *schema)
+{
+ const struct dsdb_class *sclass;
+ int ret;
+
+ for (sclass = schema->classes; sclass; sclass = sclass->next) {
+ char *val = schema_class_to_extendedInfo(msg, sclass);
+ if (!val) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ldb_msg_add_string(msg, "extendedClassInfo", val);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+
+ return LDB_SUCCESS;
+}
+
+/* Add objectClasses, attributeTypes and dITContentRules from the
+ schema object (they are not stored in the database)
+ */
+static int schema_fsmo_search_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+ struct ldb_context *ldb;
+ struct schema_fsmo_search_data *ac;
+ struct schema_fsmo_private_data *mc;
+ int i, ret;
+
+ ac = talloc_get_type(req->context, struct schema_fsmo_search_data);
+ mc = talloc_get_type(ldb_module_get_private(ac->module), struct schema_fsmo_private_data);
+ ldb = ldb_module_get_ctx(ac->module);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+ /* Only entries are interesting, and we handle the case of the parent seperatly */
+
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+
+ if (ldb_dn_compare(ares->message->dn, mc->aggregate_dn) != 0) {
+ return ldb_module_send_entry(ac->req, ares->message, ares->controls);
+ }
+
+ for (i=0; i < ARRAY_SIZE(generated_attrs); i++) {
+ if (ldb_attr_in_list(ac->req->op.search.attrs, generated_attrs[i].attr)) {
+ ret = generated_attrs[i].fn(ldb, ares->message, ac->schema);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+ }
+
+ return ldb_module_send_entry(ac->req, ares->message, ares->controls);
+
+ case LDB_REPLY_REFERRAL:
+
+ return ldb_module_send_referral(ac->req, ares->referral);
+
+ case LDB_REPLY_DONE:
+
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ return LDB_SUCCESS;
+}
+
+/* search */
+static int schema_fsmo_search(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb = ldb_module_get_ctx(module);
+ int i, ret;
+ struct schema_fsmo_search_data *search_context;
+ struct ldb_request *down_req;
+ struct dsdb_schema *schema = dsdb_get_schema(ldb);
+
+ if (!schema || !ldb_module_get_private(module)) {
+ /* If there is no schema, there is little we can do */
+ return ldb_next_request(module, req);
+ }
+ for (i=0; i < ARRAY_SIZE(generated_attrs); i++) {
+ if (ldb_attr_in_list(req->op.search.attrs, generated_attrs[i].attr)) {
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(generated_attrs)) {
+ /* No request for a generated attr found, nothing to
+ * see here, move along... */
+ return ldb_next_request(module, req);
+ }
+
+ search_context = talloc(req, struct schema_fsmo_search_data);
+ if (!search_context) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ search_context->module = module;
+ search_context->req = req;
+ search_context->schema = schema;
+
+ ret = ldb_build_search_req_ex(&down_req, ldb, search_context,
+ req->op.search.base,
+ req->op.search.scope,
+ req->op.search.tree,
+ req->op.search.attrs,
+ req->controls,
+ search_context, schema_fsmo_search_callback,
+ req);
+ if (ret != LDB_SUCCESS) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ return ldb_next_request(module, down_req);
+}
+
+
+_PUBLIC_ const struct ldb_module_ops ldb_schema_fsmo_module_ops = {
+ .name = "schema_fsmo",
+ .init_context = schema_fsmo_init,
+ .add = schema_fsmo_add,
+ .extended = schema_fsmo_extended,
+ .search = schema_fsmo_search
+};
diff --git a/source4/dsdb/samdb/ldb_modules/show_deleted.c b/source4/dsdb/samdb/ldb_modules/show_deleted.c
new file mode 100644
index 0000000000..d619558c21
--- /dev/null
+++ b/source4/dsdb/samdb/ldb_modules/show_deleted.c
@@ -0,0 +1,159 @@
+/*
+ ldb database library
+
+ Copyright (C) Simo Sorce 2005
+ Copyright (C) Stefa Metzmacher <metze@samba.org> 2007
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb deleted objects control module
+ *
+ * Description: this module hides deleted objects, and returns them if the right control is there
+ *
+ * Author: Stefan Metzmacher
+ */
+
+#include "includes.h"
+#include "ldb/include/ldb_module.h"
+#include "dsdb/samdb/samdb.h"
+
+/* search */
+struct show_deleted_search_request {
+
+ struct ldb_module *module;
+ struct ldb_request *req;
+};
+
+static int show_deleted_search_callback(struct ldb_request *req,
+ struct ldb_reply *ares)
+{
+ struct show_deleted_search_request *ar;
+
+ ar = talloc_get_type(req->context, struct show_deleted_search_request);
+
+ if (!ares) {
+ return ldb_module_done(ar->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ar->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+
+ return ldb_module_send_entry(ar->req, ares->message, ares->controls);
+
+ case LDB_REPLY_REFERRAL:
+ return ldb_module_send_referral(ar->req, ares->referral);
+
+ case LDB_REPLY_DONE:
+ return ldb_module_done(ar->req, ares->controls,
+ ares->response, LDB_SUCCESS);
+
+ }
+ return LDB_SUCCESS;
+}
+
+static int show_deleted_search(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct ldb_control *control;
+ struct ldb_control **saved_controls;
+ struct show_deleted_search_request *ar;
+ struct ldb_request *down_req;
+ char *old_filter;
+ char *new_filter;
+ int ret;
+
+ ldb = ldb_module_get_ctx(module);
+
+ ar = talloc_zero(req, struct show_deleted_search_request);
+ if (ar == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ar->module = module;
+ ar->req = req;
+
+ /* check if there's a show deleted control */
+ control = ldb_request_get_control(req, LDB_CONTROL_SHOW_DELETED_OID);
+
+ if ( ! control) {
+ old_filter = ldb_filter_from_tree(ar, req->op.search.tree);
+ new_filter = talloc_asprintf(ar, "(&(!(isDeleted=TRUE))%s)",
+ old_filter);
+
+ ret = ldb_build_search_req(&down_req, ldb, ar,
+ req->op.search.base,
+ req->op.search.scope,
+ new_filter,
+ req->op.search.attrs,
+ req->controls,
+ ar, show_deleted_search_callback,
+ req);
+
+ } else {
+ ret = ldb_build_search_req_ex(&down_req, ldb, ar,
+ req->op.search.base,
+ req->op.search.scope,
+ req->op.search.tree,
+ req->op.search.attrs,
+ req->controls,
+ ar, show_deleted_search_callback,
+ req);
+ }
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ /* if a control is there remove if from the modified request */
+ if (control && !save_controls(control, down_req, &saved_controls)) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* perform the search */
+ return ldb_next_request(module, down_req);
+}
+
+static int show_deleted_init(struct ldb_module *module)
+{
+ struct ldb_context *ldb;
+ int ret;
+
+ ldb = ldb_module_get_ctx(module);
+
+ ret = ldb_mod_register_control(module, LDB_CONTROL_SHOW_DELETED_OID);
+ if (ret != LDB_SUCCESS) {
+ ldb_debug(ldb, LDB_DEBUG_ERROR,
+ "extended_dn: Unable to register control with rootdse!\n");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ return ldb_next_init(module);
+}
+
+_PUBLIC_ const struct ldb_module_ops ldb_show_deleted_module_ops = {
+ .name = "show_deleted",
+ .search = show_deleted_search,
+ .init_context = show_deleted_init
+};
diff --git a/source4/dsdb/samdb/ldb_modules/simple_ldap_map.c b/source4/dsdb/samdb/ldb_modules/simple_ldap_map.c
new file mode 100644
index 0000000000..948241b094
--- /dev/null
+++ b/source4/dsdb/samdb/ldb_modules/simple_ldap_map.c
@@ -0,0 +1,719 @@
+/*
+ ldb database module
+
+ LDAP semantics mapping module
+
+ Copyright (C) Jelmer Vernooij 2005
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ This module relies on ldb_map to do all the real work, but performs
+ some of the trivial mappings between AD semantics and that provided
+ by OpenLDAP and similar servers.
+*/
+
+#include "includes.h"
+#include "ldb/include/ldb_module.h"
+#include "ldb/ldb_map/ldb_map.h"
+
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "librpc/ndr/libndr.h"
+#include "dsdb/samdb/samdb.h"
+
+struct entryuuid_private {
+ struct ldb_context *ldb;
+ struct ldb_dn **base_dns;
+};
+
+static struct ldb_val encode_guid(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
+{
+ struct GUID guid;
+ NTSTATUS status = GUID_from_data_blob(val, &guid);
+ enum ndr_err_code ndr_err;
+ struct ldb_val out = data_blob(NULL, 0);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return out;
+ }
+ ndr_err = ndr_push_struct_blob(&out, ctx, NULL, &guid,
+ (ndr_push_flags_fn_t)ndr_push_GUID);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return out;
+ }
+
+ return out;
+}
+
+static struct ldb_val guid_always_string(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
+{
+ struct ldb_val out = data_blob(NULL, 0);
+ struct GUID guid;
+ NTSTATUS status = GUID_from_data_blob(val, &guid);
+ if (!NT_STATUS_IS_OK(status)) {
+ return out;
+ }
+ return data_blob_string_const(GUID_string(ctx, &guid));
+}
+
+static struct ldb_val encode_ns_guid(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
+{
+ struct GUID guid;
+ NTSTATUS status = NS_GUID_from_string((char *)val->data, &guid);
+ enum ndr_err_code ndr_err;
+ struct ldb_val out = data_blob(NULL, 0);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return out;
+ }
+ ndr_err = ndr_push_struct_blob(&out, ctx, NULL, &guid,
+ (ndr_push_flags_fn_t)ndr_push_GUID);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return out;
+ }
+
+ return out;
+}
+
+static struct ldb_val guid_ns_string(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
+{
+ struct ldb_val out = data_blob(NULL, 0);
+ struct GUID guid;
+ NTSTATUS status = GUID_from_data_blob(val, &guid);
+ if (!NT_STATUS_IS_OK(status)) {
+ return out;
+ }
+ return data_blob_string_const(NS_GUID_string(ctx, &guid));
+}
+
+/* The backend holds binary sids, so just copy them back */
+static struct ldb_val val_copy(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
+{
+ struct ldb_val out = data_blob(NULL, 0);
+ out = ldb_val_dup(ctx, val);
+
+ return out;
+}
+
+/* Ensure we always convert sids into binary, so the backend doesn't have to know about both forms */
+static struct ldb_val sid_always_binary(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
+{
+ struct ldb_context *ldb = ldb_module_get_ctx(module);
+ struct ldb_val out = data_blob(NULL, 0);
+ const struct ldb_schema_attribute *a = ldb_schema_attribute_by_name(ldb, "objectSid");
+
+ if (a->syntax->canonicalise_fn(ldb, ctx, val, &out) != LDB_SUCCESS) {
+ return data_blob(NULL, 0);
+ }
+
+ return out;
+}
+
+/* Ensure we always convert objectCategory into a DN */
+static struct ldb_val objectCategory_always_dn(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
+{
+ struct ldb_context *ldb = ldb_module_get_ctx(module);
+ struct ldb_dn *dn;
+ struct ldb_val out = data_blob(NULL, 0);
+ const struct ldb_schema_attribute *a = ldb_schema_attribute_by_name(ldb, "objectCategory");
+
+ dn = ldb_dn_from_ldb_val(ctx, ldb, val);
+ if (dn && ldb_dn_validate(dn)) {
+ talloc_free(dn);
+ return val_copy(module, ctx, val);
+ }
+ talloc_free(dn);
+
+ if (a->syntax->canonicalise_fn(ldb, ctx, val, &out) != LDB_SUCCESS) {
+ return data_blob(NULL, 0);
+ }
+
+ return out;
+}
+
+static struct ldb_val normalise_to_signed32(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
+{
+ long long int signed_ll = strtoll((const char *)val->data, NULL, 10);
+ if (signed_ll >= 0x80000000LL) {
+ union {
+ int32_t signed_int;
+ uint32_t unsigned_int;
+ } u = {
+ .unsigned_int = strtoul((const char *)val->data, NULL, 10)
+ };
+
+ struct ldb_val out = data_blob_string_const(talloc_asprintf(ctx, "%d", u.signed_int));
+ return out;
+ }
+ return val_copy(module, ctx, val);
+}
+
+static struct ldb_val usn_to_entryCSN(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
+{
+ struct ldb_val out;
+ unsigned long long usn = strtoull((const char *)val->data, NULL, 10);
+ time_t t = (usn >> 24);
+ out = data_blob_string_const(talloc_asprintf(ctx, "%s#%06x#00#000000", ldb_timestring(ctx, t), (unsigned int)(usn & 0xFFFFFF)));
+ return out;
+}
+
+static unsigned long long entryCSN_to_usn_int(TALLOC_CTX *ctx, const struct ldb_val *val)
+{
+ char *entryCSN = talloc_strdup(ctx, (const char *)val->data);
+ char *mod_per_sec;
+ time_t t;
+ unsigned long long usn;
+ char *p;
+ if (!entryCSN) {
+ return 0;
+ }
+ p = strchr(entryCSN, '#');
+ if (!p) {
+ return 0;
+ }
+ p[0] = '\0';
+ p++;
+ mod_per_sec = p;
+
+ p = strchr(p, '#');
+ if (!p) {
+ return 0;
+ }
+ p[0] = '\0';
+ p++;
+
+ usn = strtol(mod_per_sec, NULL, 16);
+
+ t = ldb_string_to_time(entryCSN);
+
+ usn = usn | ((unsigned long long)t <<24);
+ return usn;
+}
+
+static struct ldb_val entryCSN_to_usn(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
+{
+ struct ldb_val out;
+ unsigned long long usn = entryCSN_to_usn_int(ctx, val);
+ out = data_blob_string_const(talloc_asprintf(ctx, "%lld", usn));
+ return out;
+}
+
+static struct ldb_val usn_to_timestamp(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
+{
+ struct ldb_val out;
+ unsigned long long usn = strtoull((const char *)val->data, NULL, 10);
+ time_t t = (usn >> 24);
+ out = data_blob_string_const(ldb_timestring(ctx, t));
+ return out;
+}
+
+static struct ldb_val timestamp_to_usn(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
+{
+ struct ldb_val out;
+ time_t t;
+ unsigned long long usn;
+
+ t = ldb_string_to_time((const char *)val->data);
+
+ usn = ((unsigned long long)t <<24);
+
+ out = data_blob_string_const(talloc_asprintf(ctx, "%lld", usn));
+ return out;
+}
+
+
+static const struct ldb_map_attribute entryuuid_attributes[] =
+{
+ /* objectGUID */
+ {
+ .local_name = "objectGUID",
+ .type = MAP_CONVERT,
+ .u = {
+ .convert = {
+ .remote_name = "entryUUID",
+ .convert_local = guid_always_string,
+ .convert_remote = encode_guid,
+ },
+ },
+ },
+ /* invocationId */
+ {
+ .local_name = "invocationId",
+ .type = MAP_CONVERT,
+ .u = {
+ .convert = {
+ .remote_name = "invocationId",
+ .convert_local = guid_always_string,
+ .convert_remote = encode_guid,
+ },
+ },
+ },
+ /* objectSid */
+ {
+ .local_name = "objectSid",
+ .type = MAP_CONVERT,
+ .u = {
+ .convert = {
+ .remote_name = "objectSid",
+ .convert_local = sid_always_binary,
+ .convert_remote = val_copy,
+ },
+ },
+ },
+ {
+ .local_name = "name",
+ .type = MAP_RENAME,
+ .u = {
+ .rename = {
+ .remote_name = "samba4RDN"
+ }
+ }
+ },
+ {
+ .local_name = "whenCreated",
+ .type = MAP_RENAME,
+ .u = {
+ .rename = {
+ .remote_name = "createTimestamp"
+ }
+ }
+ },
+ {
+ .local_name = "whenChanged",
+ .type = MAP_RENAME,
+ .u = {
+ .rename = {
+ .remote_name = "modifyTimestamp"
+ }
+ }
+ },
+ {
+ .local_name = "objectClasses",
+ .type = MAP_RENAME,
+ .u = {
+ .rename = {
+ .remote_name = "samba4ObjectClasses"
+ }
+ }
+ },
+ {
+ .local_name = "dITContentRules",
+ .type = MAP_RENAME,
+ .u = {
+ .rename = {
+ .remote_name = "samba4DITContentRules"
+ }
+ }
+ },
+ {
+ .local_name = "attributeTypes",
+ .type = MAP_RENAME,
+ .u = {
+ .rename = {
+ .remote_name = "samba4AttributeTypes"
+ }
+ }
+ },
+ {
+ .local_name = "objectCategory",
+ .type = MAP_CONVERT,
+ .u = {
+ .convert = {
+ .remote_name = "objectCategory",
+ .convert_local = objectCategory_always_dn,
+ .convert_remote = val_copy,
+ },
+ },
+ },
+ {
+ .local_name = "distinguishedName",
+ .type = MAP_RENAME,
+ .u = {
+ .rename = {
+ .remote_name = "entryDN"
+ }
+ }
+ },
+ {
+ .local_name = "groupType",
+ .type = MAP_CONVERT,
+ .u = {
+ .convert = {
+ .remote_name = "groupType",
+ .convert_local = normalise_to_signed32,
+ .convert_remote = val_copy,
+ },
+ }
+ },
+ {
+ .local_name = "sAMAccountType",
+ .type = MAP_CONVERT,
+ .u = {
+ .convert = {
+ .remote_name = "sAMAccountType",
+ .convert_local = normalise_to_signed32,
+ .convert_remote = val_copy,
+ },
+ }
+ },
+ {
+ .local_name = "usnChanged",
+ .type = MAP_CONVERT,
+ .u = {
+ .convert = {
+ .remote_name = "entryCSN",
+ .convert_local = usn_to_entryCSN,
+ .convert_remote = entryCSN_to_usn
+ },
+ },
+ },
+ {
+ .local_name = "usnCreated",
+ .type = MAP_CONVERT,
+ .u = {
+ .convert = {
+ .remote_name = "createTimestamp",
+ .convert_local = usn_to_timestamp,
+ .convert_remote = timestamp_to_usn,
+ },
+ },
+ },
+ {
+ .local_name = "*",
+ .type = MAP_KEEP,
+ },
+ {
+ .local_name = NULL,
+ }
+};
+
+/* This objectClass conflicts with builtin classes on OpenLDAP */
+const struct ldb_map_objectclass entryuuid_objectclasses[] =
+{
+ {
+ .local_name = "subSchema",
+ .remote_name = "samba4SubSchema"
+ },
+ {
+ .local_name = NULL
+ }
+};
+
+/* These things do not show up in wildcard searches in OpenLDAP, but
+ * we need them to show up in the AD-like view */
+static const char * const entryuuid_wildcard_attributes[] = {
+ "objectGUID",
+ "whenCreated",
+ "whenChanged",
+ "usnCreated",
+ "usnChanged",
+ "memberOf",
+ NULL
+};
+
+static const struct ldb_map_attribute nsuniqueid_attributes[] =
+{
+ /* objectGUID */
+ {
+ .local_name = "objectGUID",
+ .type = MAP_CONVERT,
+ .u = {
+ .convert = {
+ .remote_name = "nsuniqueid",
+ .convert_local = guid_ns_string,
+ .convert_remote = encode_ns_guid,
+ },
+ },
+ },
+ /* objectSid */
+ {
+ .local_name = "objectSid",
+ .type = MAP_CONVERT,
+ .u = {
+ .convert = {
+ .remote_name = "objectSid",
+ .convert_local = sid_always_binary,
+ .convert_remote = val_copy,
+ },
+ },
+ },
+ {
+ .local_name = "whenCreated",
+ .type = MAP_RENAME,
+ .u = {
+ .rename = {
+ .remote_name = "createTimestamp"
+ }
+ }
+ },
+ {
+ .local_name = "whenChanged",
+ .type = MAP_RENAME,
+ .u = {
+ .rename = {
+ .remote_name = "modifyTimestamp"
+ }
+ }
+ },
+ {
+ .local_name = "objectCategory",
+ .type = MAP_CONVERT,
+ .u = {
+ .convert = {
+ .remote_name = "objectCategory",
+ .convert_local = objectCategory_always_dn,
+ .convert_remote = val_copy,
+ },
+ },
+ },
+ {
+ .local_name = "distinguishedName",
+ .type = MAP_RENAME,
+ .u = {
+ .rename = {
+ .remote_name = "entryDN"
+ }
+ }
+ },
+ {
+ .local_name = "groupType",
+ .type = MAP_CONVERT,
+ .u = {
+ .convert = {
+ .remote_name = "groupType",
+ .convert_local = normalise_to_signed32,
+ .convert_remote = val_copy,
+ },
+ }
+ },
+ {
+ .local_name = "sAMAccountType",
+ .type = MAP_CONVERT,
+ .u = {
+ .convert = {
+ .remote_name = "sAMAccountType",
+ .convert_local = normalise_to_signed32,
+ .convert_remote = val_copy,
+ },
+ }
+ },
+ {
+ .local_name = "usnChanged",
+ .type = MAP_CONVERT,
+ .u = {
+ .convert = {
+ .remote_name = "modifyTimestamp",
+ .convert_local = usn_to_timestamp,
+ .convert_remote = timestamp_to_usn,
+ },
+ },
+ },
+ {
+ .local_name = "usnCreated",
+ .type = MAP_CONVERT,
+ .u = {
+ .convert = {
+ .remote_name = "createTimestamp",
+ .convert_local = usn_to_timestamp,
+ .convert_remote = timestamp_to_usn,
+ },
+ },
+ },
+ {
+ .local_name = "*",
+ .type = MAP_KEEP,
+ },
+ {
+ .local_name = NULL,
+ }
+};
+
+/* These things do not show up in wildcard searches in OpenLDAP, but
+ * we need them to show up in the AD-like view */
+static const char * const nsuniqueid_wildcard_attributes[] = {
+ "objectGUID",
+ "whenCreated",
+ "whenChanged",
+ "usnCreated",
+ "usnChanged",
+ NULL
+};
+
+/* the context init function */
+static int entryuuid_init(struct ldb_module *module)
+{
+ int ret;
+ ret = ldb_map_init(module, entryuuid_attributes, entryuuid_objectclasses, entryuuid_wildcard_attributes, "samba4Top", NULL);
+ if (ret != LDB_SUCCESS)
+ return ret;
+
+ return ldb_next_init(module);
+}
+
+/* the context init function */
+static int nsuniqueid_init(struct ldb_module *module)
+{
+ int ret;
+ ret = ldb_map_init(module, nsuniqueid_attributes, NULL, nsuniqueid_wildcard_attributes, "extensibleObject", NULL);
+ if (ret != LDB_SUCCESS)
+ return ret;
+
+ return ldb_next_init(module);
+}
+
+static int get_seq_callback(struct ldb_request *req,
+ struct ldb_reply *ares)
+{
+ unsigned long long *seq = (unsigned long long *)req->context;
+
+ if (!ares) {
+ return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_request_done(req, ares->error);
+ }
+
+ if (ares->type == LDB_REPLY_ENTRY) {
+ struct ldb_message_element *el = ldb_msg_find_element(ares->message, "contextCSN");
+ if (el) {
+ *seq = entryCSN_to_usn_int(ares, &el->values[0]);
+ }
+ }
+
+ if (ares->type == LDB_REPLY_DONE) {
+ return ldb_request_done(req, LDB_SUCCESS);
+ }
+
+ talloc_free(ares);
+ return LDB_SUCCESS;
+}
+
+static int entryuuid_sequence_number(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ int ret;
+ struct map_private *map_private;
+ struct entryuuid_private *entryuuid_private;
+ unsigned long long seq_num = 0;
+ struct ldb_request *search_req;
+
+ const struct ldb_control *partition_ctrl;
+ const struct dsdb_control_current_partition *partition;
+
+ static const char *contextCSN_attr[] = {
+ "contextCSN", NULL
+ };
+
+ struct ldb_seqnum_request *seq;
+ struct ldb_seqnum_result *seqr;
+ struct ldb_extended *ext;
+
+ ldb = ldb_module_get_ctx(module);
+
+ seq = talloc_get_type(req->op.extended.data, struct ldb_seqnum_request);
+
+ map_private = talloc_get_type(ldb_module_get_private(module), struct map_private);
+
+ entryuuid_private = talloc_get_type(map_private->caller_private, struct entryuuid_private);
+
+ /* All this to get the DN of the parition, so we can search the right thing */
+ partition_ctrl = ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID);
+ if (!partition_ctrl) {
+ ldb_debug_set(ldb, LDB_DEBUG_FATAL,
+ "entryuuid_sequence_number: no current partition control found");
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+
+ partition = talloc_get_type(partition_ctrl->data,
+ struct dsdb_control_current_partition);
+ SMB_ASSERT(partition && partition->version == DSDB_CONTROL_CURRENT_PARTITION_VERSION);
+
+ ret = ldb_build_search_req(&search_req, ldb, req,
+ partition->dn, LDB_SCOPE_BASE,
+ NULL, contextCSN_attr, NULL,
+ &seq_num, get_seq_callback,
+ NULL);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ ret = ldb_next_request(module, search_req);
+
+ if (ret == LDB_SUCCESS) {
+ ret = ldb_wait(search_req->handle, LDB_WAIT_ALL);
+ }
+
+ talloc_free(search_req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ ext = talloc_zero(req, struct ldb_extended);
+ if (!ext) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ seqr = talloc_zero(req, struct ldb_seqnum_result);
+ if (seqr == NULL) {
+ talloc_free(ext);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ext->oid = LDB_EXTENDED_SEQUENCE_NUMBER;
+ ext->data = seqr;
+
+ switch (seq->type) {
+ case LDB_SEQ_HIGHEST_SEQ:
+ seqr->seq_num = seq_num;
+ break;
+ case LDB_SEQ_NEXT:
+ seqr->seq_num = seq_num;
+ seqr->seq_num++;
+ break;
+ case LDB_SEQ_HIGHEST_TIMESTAMP:
+ {
+ seqr->seq_num = (seq_num >> 24);
+ break;
+ }
+ }
+ seqr->flags = 0;
+ seqr->flags |= LDB_SEQ_TIMESTAMP_SEQUENCE;
+ seqr->flags |= LDB_SEQ_GLOBAL_SEQUENCE;
+
+ /* send request done */
+ return ldb_module_done(req, NULL, ext, LDB_SUCCESS);
+}
+
+static int entryuuid_extended(struct ldb_module *module, struct ldb_request *req)
+{
+ if (strcmp(req->op.extended.oid, LDB_EXTENDED_SEQUENCE_NUMBER) == 0) {
+ return entryuuid_sequence_number(module, req);
+ }
+
+ return ldb_next_request(module, req);
+}
+
+_PUBLIC_ const struct ldb_module_ops ldb_entryuuid_module_ops = {
+ .name = "entryuuid",
+ .init_context = entryuuid_init,
+ .extended = entryuuid_extended,
+ LDB_MAP_OPS
+};
+
+_PUBLIC_ const struct ldb_module_ops ldb_nsuniqueid_module_ops = {
+ .name = "nsuniqueid",
+ .init_context = nsuniqueid_init,
+ .extended = entryuuid_extended,
+ LDB_MAP_OPS
+};
diff --git a/source4/dsdb/samdb/ldb_modules/subtree_delete.c b/source4/dsdb/samdb/ldb_modules/subtree_delete.c
new file mode 100644
index 0000000000..55a24549dd
--- /dev/null
+++ b/source4/dsdb/samdb/ldb_modules/subtree_delete.c
@@ -0,0 +1,162 @@
+/*
+ ldb database library
+
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2006-2007
+ Copyright (C) Stefan Metzmacher <metze@samba.org> 2007
+ Copyright (C) Simo Sorce <idra@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb subtree delete (prevention) module
+ *
+ * Description: Prevent deletion of a subtree in LDB
+ *
+ * Author: Andrew Bartlett
+ */
+
+#include "ldb_module.h"
+
+struct subtree_delete_context {
+ struct ldb_module *module;
+ struct ldb_request *req;
+
+ int num_children;
+};
+
+static struct subtree_delete_context *subdel_ctx_init(struct ldb_module *module,
+ struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct subtree_delete_context *ac;
+
+ ldb = ldb_module_get_ctx(module);
+
+ ac = talloc_zero(req, struct subtree_delete_context);
+ if (ac == NULL) {
+ ldb_oom(ldb);
+ return NULL;
+ }
+
+ ac->module = module;
+ ac->req = req;
+
+ return ac;
+}
+
+static int subtree_delete_search_callback(struct ldb_request *req,
+ struct ldb_reply *ares)
+{
+ struct ldb_context *ldb;
+ struct subtree_delete_context *ac;
+ int ret;
+
+ ac = talloc_get_type(req->context, struct subtree_delete_context);
+ ldb = ldb_module_get_ctx(ac->module);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+
+ talloc_free(ares);
+ ac->num_children++;
+ break;
+
+ case LDB_REPLY_REFERRAL:
+
+ /* ignore */
+ talloc_free(ares);
+ break;
+
+ case LDB_REPLY_DONE:
+
+ if (ac->num_children > 0) {
+ talloc_free(ares);
+ ldb_asprintf_errstring(ldb,
+ "Cannot delete %s, not a leaf node "
+ "(has %d children)\n",
+ ldb_dn_get_linearized(ac->req->op.del.dn),
+ ac->num_children);
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_NOT_ALLOWED_ON_NON_LEAF);
+ }
+
+ /* ok no children, let the original request through */
+ ret = ldb_next_request(ac->module, ac->req);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+
+ /* free our own context we are not going to be called back */
+ talloc_free(ac);
+ }
+ return LDB_SUCCESS;
+}
+
+static int subtree_delete(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ static const char * const attrs[2] = { "distinguishedName", NULL };
+ struct ldb_request *search_req;
+ struct subtree_delete_context *ac;
+ int ret;
+ if (ldb_dn_is_special(req->op.rename.olddn)) { /* do not manipulate our control entries */
+ return ldb_next_request(module, req);
+ }
+
+ ldb = ldb_module_get_ctx(module);
+
+ /* This gets complex: We need to:
+ - Do a search for all entires under this entry
+ - Wait for these results to appear
+ - In the callback for each result, count the children (if any)
+ - return an error if there are any
+ */
+
+ ac = subdel_ctx_init(module, req);
+ if (!ac) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* we do not really need to find all descendents,
+ * if there is even one single direct child, that's
+ * enough to bail out */
+ ret = ldb_build_search_req(&search_req, ldb, ac,
+ req->op.del.dn, LDB_SCOPE_ONELEVEL,
+ "(objectClass=*)", attrs,
+ req->controls,
+ ac, subtree_delete_search_callback,
+ req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ return ldb_next_request(module, search_req);
+}
+
+const struct ldb_module_ops ldb_subtree_delete_module_ops = {
+ .name = "subtree_delete",
+ .del = subtree_delete,
+};
diff --git a/source4/dsdb/samdb/ldb_modules/subtree_rename.c b/source4/dsdb/samdb/ldb_modules/subtree_rename.c
new file mode 100644
index 0000000000..e2f6b1d059
--- /dev/null
+++ b/source4/dsdb/samdb/ldb_modules/subtree_rename.c
@@ -0,0 +1,268 @@
+/*
+ ldb database library
+
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2006-2007
+ Copyright (C) Stefan Metzmacher <metze@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb subtree rename module
+ *
+ * Description: Rename a subtree in LDB
+ *
+ * Author: Andrew Bartlett
+ */
+
+#include "ldb_module.h"
+
+struct subren_msg_store {
+ struct subren_msg_store *next;
+ struct ldb_dn *olddn;
+ struct ldb_dn *newdn;
+};
+
+struct subtree_rename_context {
+ struct ldb_module *module;
+ struct ldb_request *req;
+
+ struct subren_msg_store *list;
+ struct subren_msg_store *current;
+};
+
+static struct subtree_rename_context *subren_ctx_init(struct ldb_module *module,
+ struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct subtree_rename_context *ac;
+
+ ldb = ldb_module_get_ctx(module);
+
+ ac = talloc_zero(req, struct subtree_rename_context);
+ if (ac == NULL) {
+ ldb_oom(ldb);
+ return NULL;
+ }
+
+ ac->module = module;
+ ac->req = req;
+
+ return ac;
+}
+
+static int subtree_rename_next_request(struct subtree_rename_context *ac);
+
+static int subtree_rename_callback(struct ldb_request *req,
+ struct ldb_reply *ares)
+{
+ struct ldb_context *ldb;
+ struct subtree_rename_context *ac;
+ int ret;
+
+ ac = talloc_get_type(req->context, struct subtree_rename_context);
+ ldb = ldb_module_get_ctx(ac->module);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ if (ares->type != LDB_REPLY_DONE) {
+ ldb_set_errstring(ldb, "Invalid reply type!\n");
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ if (ac->current == NULL) {
+ /* this was the last one */
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, LDB_SUCCESS);
+ }
+
+ ret = subtree_rename_next_request(ac);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+
+ talloc_free(ares);
+ return LDB_SUCCESS;
+}
+
+static int subtree_rename_next_request(struct subtree_rename_context *ac)
+{
+ struct ldb_context *ldb;
+ struct ldb_request *req;
+ int ret;
+
+ ldb = ldb_module_get_ctx(ac->module);
+
+ if (ac->current == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ldb_build_rename_req(&req, ldb, ac->current,
+ ac->current->olddn,
+ ac->current->newdn,
+ ac->req->controls,
+ ac, subtree_rename_callback,
+ ac->req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ ac->current = ac->current->next;
+
+ return ldb_next_request(ac->module, req);
+}
+
+static int subtree_rename_search_callback(struct ldb_request *req,
+ struct ldb_reply *ares)
+{
+ struct subren_msg_store *store;
+ struct subtree_rename_context *ac;
+ int ret;
+
+ ac = talloc_get_type(req->context, struct subtree_rename_context);
+
+ if (!ares || !ac->current) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+
+ if (ldb_dn_compare(ares->message->dn, ac->list->olddn) == 0) {
+ /* this was already stored by the
+ * subtree_rename_search() */
+ talloc_free(ares);
+ return LDB_SUCCESS;
+ }
+
+ store = talloc_zero(ac, struct subren_msg_store);
+ if (store == NULL) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ ac->current->next = store;
+ ac->current = store;
+
+ /* the first list element contains the base for the rename */
+ store->olddn = talloc_steal(store, ares->message->dn);
+ store->newdn = ldb_dn_copy(store, store->olddn);
+
+ if ( ! ldb_dn_remove_base_components(store->newdn,
+ ldb_dn_get_comp_num(ac->list->olddn))) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ if ( ! ldb_dn_add_base(store->newdn, ac->list->newdn)) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ break;
+
+ case LDB_REPLY_REFERRAL:
+ /* ignore */
+ break;
+
+ case LDB_REPLY_DONE:
+
+ /* rewind ac->current */
+ ac->current = ac->list;
+
+ /* All dns set up, start with the first one */
+ ret = subtree_rename_next_request(ac);
+
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+ break;
+ }
+
+ talloc_free(ares);
+ return LDB_SUCCESS;
+}
+
+/* rename */
+static int subtree_rename(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ static const char *attrs[2] = { "distinguishedName", NULL };
+ struct ldb_request *search_req;
+ struct subtree_rename_context *ac;
+ int ret;
+ if (ldb_dn_is_special(req->op.rename.olddn)) { /* do not manipulate our control entries */
+ return ldb_next_request(module, req);
+ }
+
+ ldb = ldb_module_get_ctx(module);
+
+ /* This gets complex: We need to:
+ - Do a search for all entires under this entry
+ - Wait for these results to appear
+ - In the callback for each result, issue a modify request
+ - That will include this rename, we hope
+ - Wait for each modify result
+ - Regain our sainity
+ */
+
+ ac = subren_ctx_init(module, req);
+ if (!ac) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* add this entry as the first to do */
+ ac->current = talloc_zero(ac, struct subren_msg_store);
+ if (ac->current == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ac->current->olddn = req->op.rename.olddn;
+ ac->current->newdn = req->op.rename.newdn;
+ ac->list = ac->current;
+
+ ret = ldb_build_search_req(&search_req, ldb, ac,
+ req->op.rename.olddn,
+ LDB_SCOPE_SUBTREE,
+ "(objectClass=*)",
+ attrs,
+ NULL,
+ ac,
+ subtree_rename_search_callback,
+ req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ return ldb_next_request(module, search_req);
+}
+
+const struct ldb_module_ops ldb_subtree_rename_module_ops = {
+ .name = "subtree_rename",
+ .rename = subtree_rename,
+};
diff --git a/source4/dsdb/samdb/ldb_modules/tests/samba3sam.py b/source4/dsdb/samdb/ldb_modules/tests/samba3sam.py
new file mode 100644
index 0000000000..75aaeb7366
--- /dev/null
+++ b/source4/dsdb/samdb/ldb_modules/tests/samba3sam.py
@@ -0,0 +1,1086 @@
+#!/usr/bin/python
+
+# Unix SMB/CIFS implementation.
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2005-2008
+# Copyright (C) Martin Kuehl <mkhl@samba.org> 2006
+#
+# This is a Python port of the original in testprogs/ejs/samba3sam.js
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+"""Tests for the samba3sam LDB module, which maps Samba3 LDAP to AD LDAP."""
+
+import os
+import ldb
+from ldb import SCOPE_DEFAULT, SCOPE_BASE, SCOPE_SUBTREE
+from samba import Ldb, substitute_var
+from samba.tests import LdbTestCase, TestCaseInTempDir, cmdline_loadparm
+import samba.dcerpc.security
+import samba.ndr
+
+datadir = os.path.join(os.path.dirname(__file__),
+ "../../../../../testdata/samba3")
+
+def read_datafile(filename):
+ return open(os.path.join(datadir, filename), 'r').read()
+
+def ldb_debug(l, text):
+ print text
+
+
+class MapBaseTestCase(TestCaseInTempDir):
+ """Base test case for mapping tests."""
+
+ def setup_modules(self, ldb, s3, s4):
+ ldb.add({"dn": "@MAP=samba3sam",
+ "@FROM": s4.basedn,
+ "@TO": "sambaDomainName=TESTS," + s3.basedn})
+
+ ldb.add({"dn": "@MODULES",
+ "@LIST": "rootdse,paged_results,server_sort,asq,samldb,password_hash,operational,objectguid,rdn_name,samba3sam,partition"})
+
+ ldb.add({"dn": "@PARTITION",
+ "partition": ["%s:%s" % (s4.basedn, s4.url),
+ "%s:%s" % (s3.basedn, s3.url)],
+ "replicateEntries": ["@ATTRIBUTES", "@INDEXLIST"]})
+
+ def setUp(self):
+ super(MapBaseTestCase, self).setUp()
+
+ def make_dn(basedn, rdn):
+ return "%s,sambaDomainName=TESTS,%s" % (rdn, basedn)
+
+ def make_s4dn(basedn, rdn):
+ return "%s,%s" % (rdn, basedn)
+
+ self.ldbfile = os.path.join(self.tempdir, "test.ldb")
+ self.ldburl = "tdb://" + self.ldbfile
+
+ tempdir = self.tempdir
+
+ class Target:
+ """Simple helper class that contains data for a specific SAM
+ connection."""
+ def __init__(self, file, basedn, dn):
+ self.file = os.path.join(tempdir, file)
+ self.url = "tdb://" + self.file
+ self.basedn = basedn
+ self.substvars = {"BASEDN": self.basedn}
+ self.db = Ldb(lp=cmdline_loadparm)
+ self._dn = dn
+
+ def dn(self, rdn):
+ return self._dn(self.basedn, rdn)
+
+ def connect(self):
+ return self.db.connect(self.url)
+
+ def setup_data(self, path):
+ self.add_ldif(read_datafile(path))
+
+ def subst(self, text):
+ return substitute_var(text, self.substvars)
+
+ def add_ldif(self, ldif):
+ self.db.add_ldif(self.subst(ldif))
+
+ def modify_ldif(self, ldif):
+ self.db.modify_ldif(self.subst(ldif))
+
+ self.samba4 = Target("samba4.ldb", "dc=vernstok,dc=nl", make_s4dn)
+ self.samba3 = Target("samba3.ldb", "cn=Samba3Sam", make_dn)
+ self.templates = Target("templates.ldb", "cn=templates", None)
+
+ self.samba3.connect()
+ self.templates.connect()
+ self.samba4.connect()
+
+ def tearDown(self):
+ os.unlink(self.ldbfile)
+ os.unlink(self.samba3.file)
+ os.unlink(self.templates.file)
+ os.unlink(self.samba4.file)
+ super(MapBaseTestCase, self).tearDown()
+
+ def assertSidEquals(self, text, ndr_sid):
+ sid_obj1 = samba.ndr.ndr_unpack(samba.dcerpc.security.dom_sid,
+ str(ndr_sid[0]))
+ sid_obj2 = samba.dcerpc.security.dom_sid(text)
+ self.assertEquals(sid_obj1, sid_obj2)
+
+
+class Samba3SamTestCase(MapBaseTestCase):
+
+ def setUp(self):
+ super(Samba3SamTestCase, self).setUp()
+ ldb = Ldb(self.ldburl, lp=cmdline_loadparm)
+ self.samba3.setup_data("samba3.ldif")
+ self.templates.setup_data("provision_samba3sam_templates.ldif")
+ ldif = read_datafile("provision_samba3sam.ldif")
+ ldb.add_ldif(self.samba4.subst(ldif))
+ self.setup_modules(ldb, self.samba3, self.samba4)
+ del ldb
+ self.ldb = Ldb(self.ldburl, lp=cmdline_loadparm)
+
+ def test_search_non_mapped(self):
+ """Looking up by non-mapped attribute"""
+ msg = self.ldb.search(expression="(cn=Administrator)")
+ self.assertEquals(len(msg), 1)
+ self.assertEquals(msg[0]["cn"], "Administrator")
+
+ def test_search_non_mapped(self):
+ """Looking up by mapped attribute"""
+ msg = self.ldb.search(expression="(name=Backup Operators)")
+ self.assertEquals(len(msg), 1)
+ self.assertEquals(str(msg[0]["name"]), "Backup Operators")
+
+ def test_old_name_of_renamed(self):
+ """Looking up by old name of renamed attribute"""
+ msg = self.ldb.search(expression="(displayName=Backup Operators)")
+ self.assertEquals(len(msg), 0)
+
+ def test_mapped_containing_sid(self):
+ """Looking up mapped entry containing SID"""
+ msg = self.ldb.search(expression="(cn=Replicator)")
+ self.assertEquals(len(msg), 1)
+ self.assertEquals(str(msg[0].dn),
+ "cn=Replicator,ou=Groups,dc=vernstok,dc=nl")
+ self.assertTrue("objectSid" in msg[0])
+ self.assertSidEquals("S-1-5-21-4231626423-2410014848-2360679739-552",
+ msg[0]["objectSid"])
+ oc = set(msg[0]["objectClass"])
+ self.assertEquals(oc, set(["group"]))
+
+ def test_search_by_objclass(self):
+ """Looking up by objectClass"""
+ msg = self.ldb.search(expression="(|(objectClass=user)(cn=Administrator))")
+ self.assertEquals(set([str(m.dn) for m in msg]),
+ set(["unixName=Administrator,ou=Users,dc=vernstok,dc=nl",
+ "unixName=nobody,ou=Users,dc=vernstok,dc=nl"]))
+
+ def test_s3sam_modify(self):
+ # Adding a record that will be fallbacked
+ self.ldb.add({"dn": "cn=Foo",
+ "foo": "bar",
+ "blah": "Blie",
+ "cn": "Foo",
+ "showInAdvancedViewOnly": "TRUE"}
+ )
+
+ # Checking for existence of record (local)
+ # TODO: This record must be searched in the local database, which is
+ # currently only supported for base searches
+ # msg = ldb.search(expression="(cn=Foo)", ['foo','blah','cn','showInAdvancedViewOnly')]
+ # TODO: Actually, this version should work as well but doesn't...
+ #
+ #
+ msg = self.ldb.search(expression="(cn=Foo)", base="cn=Foo",
+ scope=SCOPE_BASE,
+ attrs=['foo','blah','cn','showInAdvancedViewOnly'])
+ self.assertEquals(len(msg), 1)
+ self.assertEquals(str(msg[0]["showInAdvancedViewOnly"]), "TRUE")
+ self.assertEquals(str(msg[0]["foo"]), "bar")
+ self.assertEquals(str(msg[0]["blah"]), "Blie")
+
+ # Adding record that will be mapped
+ self.ldb.add({"dn": "cn=Niemand,cn=Users,dc=vernstok,dc=nl",
+ "objectClass": "user",
+ "unixName": "bin",
+ "sambaUnicodePwd": "geheim",
+ "cn": "Niemand"})
+
+ # Checking for existence of record (remote)
+ msg = self.ldb.search(expression="(unixName=bin)",
+ attrs=['unixName','cn','dn', 'sambaUnicodePwd'])
+ self.assertEquals(len(msg), 1)
+ self.assertEquals(str(msg[0]["cn"]), "Niemand")
+ self.assertEquals(str(msg[0]["sambaUnicodePwd"]), "geheim")
+
+ # Checking for existence of record (local && remote)
+ msg = self.ldb.search(expression="(&(unixName=bin)(sambaUnicodePwd=geheim))",
+ attrs=['unixName','cn','dn', 'sambaUnicodePwd'])
+ self.assertEquals(len(msg), 1) # TODO: should check with more records
+ self.assertEquals(str(msg[0]["cn"]), "Niemand")
+ self.assertEquals(str(msg[0]["unixName"]), "bin")
+ self.assertEquals(str(msg[0]["sambaUnicodePwd"]), "geheim")
+
+ # Checking for existence of record (local || remote)
+ msg = self.ldb.search(expression="(|(unixName=bin)(sambaUnicodePwd=geheim))",
+ attrs=['unixName','cn','dn', 'sambaUnicodePwd'])
+ #print "got %d replies" % len(msg)
+ self.assertEquals(len(msg), 1) # TODO: should check with more records
+ self.assertEquals(str(msg[0]["cn"]), "Niemand")
+ self.assertEquals(str(msg[0]["unixName"]), "bin")
+ self.assertEquals(str(msg[0]["sambaUnicodePwd"]), "geheim")
+
+ # Checking for data in destination database
+ msg = self.samba3.db.search(expression="(cn=Niemand)")
+ self.assertTrue(len(msg) >= 1)
+ self.assertEquals(str(msg[0]["sambaSID"]),
+ "S-1-5-21-4231626423-2410014848-2360679739-2001")
+ self.assertEquals(str(msg[0]["displayName"]), "Niemand")
+
+ # Adding attribute...
+ self.ldb.modify_ldif("""
+dn: cn=Niemand,cn=Users,dc=vernstok,dc=nl
+changetype: modify
+add: description
+description: Blah
+""")
+
+ # Checking whether changes are still there...
+ msg = self.ldb.search(expression="(cn=Niemand)")
+ self.assertTrue(len(msg) >= 1)
+ self.assertEquals(str(msg[0]["cn"]), "Niemand")
+ self.assertEquals(str(msg[0]["description"]), "Blah")
+
+ # Modifying attribute...
+ self.ldb.modify_ldif("""
+dn: cn=Niemand,cn=Users,dc=vernstok,dc=nl
+changetype: modify
+replace: description
+description: Blie
+""")
+
+ # Checking whether changes are still there...
+ msg = self.ldb.search(expression="(cn=Niemand)")
+ self.assertTrue(len(msg) >= 1)
+ self.assertEquals(str(msg[0]["description"]), "Blie")
+
+ # Deleting attribute...
+ self.ldb.modify_ldif("""
+dn: cn=Niemand,cn=Users,dc=vernstok,dc=nl
+changetype: modify
+delete: description
+""")
+
+ # Checking whether changes are no longer there...
+ msg = self.ldb.search(expression="(cn=Niemand)")
+ self.assertTrue(len(msg) >= 1)
+ self.assertTrue(not "description" in msg[0])
+
+ # Renaming record...
+ self.ldb.rename("cn=Niemand,cn=Users,dc=vernstok,dc=nl",
+ "cn=Niemand2,cn=Users,dc=vernstok,dc=nl")
+
+ # Checking whether DN has changed...
+ msg = self.ldb.search(expression="(cn=Niemand2)")
+ self.assertEquals(len(msg), 1)
+ self.assertEquals(str(msg[0].dn),
+ "cn=Niemand2,cn=Users,dc=vernstok,dc=nl")
+
+ # Deleting record...
+ self.ldb.delete("cn=Niemand2,cn=Users,dc=vernstok,dc=nl")
+
+ # Checking whether record is gone...
+ msg = self.ldb.search(expression="(cn=Niemand2)")
+ self.assertEquals(len(msg), 0)
+
+
+class MapTestCase(MapBaseTestCase):
+
+ def setUp(self):
+ super(MapTestCase, self).setUp()
+ ldb = Ldb(self.ldburl, lp=cmdline_loadparm)
+ self.templates.setup_data("provision_samba3sam_templates.ldif")
+ ldif = read_datafile("provision_samba3sam.ldif")
+ ldb.add_ldif(self.samba4.subst(ldif))
+ self.setup_modules(ldb, self.samba3, self.samba4)
+ del ldb
+ self.ldb = Ldb(self.ldburl, lp=cmdline_loadparm)
+
+ def test_map_search(self):
+ """Running search tests on mapped data."""
+ self.samba3.db.add({
+ "dn": "sambaDomainName=TESTS," + self.samba3.basedn,
+ "objectclass": ["sambaDomain", "top"],
+ "sambaSID": "S-1-5-21-4231626423-2410014848-2360679739",
+ "sambaNextRid": "2000",
+ "sambaDomainName": "TESTS"
+ })
+
+ # Add a set of split records
+ self.ldb.add_ldif("""
+dn: """+ self.samba4.dn("cn=X") + """
+objectClass: user
+cn: X
+codePage: x
+revision: x
+dnsHostName: x
+nextRid: y
+lastLogon: x
+description: x
+objectSid: S-1-5-21-4231626423-2410014848-2360679739-552
+primaryGroupID: 1-5-21-4231626423-2410014848-2360679739-512
+
+""")
+
+ self.ldb.add({
+ "dn": self.samba4.dn("cn=Y"),
+ "objectClass": "top",
+ "cn": "Y",
+ "codePage": "x",
+ "revision": "x",
+ "dnsHostName": "y",
+ "nextRid": "y",
+ "lastLogon": "y",
+ "description": "x"})
+
+ self.ldb.add({
+ "dn": self.samba4.dn("cn=Z"),
+ "objectClass": "top",
+ "cn": "Z",
+ "codePage": "x",
+ "revision": "y",
+ "dnsHostName": "z",
+ "nextRid": "y",
+ "lastLogon": "z",
+ "description": "y"})
+
+ # Add a set of remote records
+
+ self.samba3.db.add({
+ "dn": self.samba3.dn("cn=A"),
+ "objectClass": "posixAccount",
+ "cn": "A",
+ "sambaNextRid": "x",
+ "sambaBadPasswordCount": "x",
+ "sambaLogonTime": "x",
+ "description": "x",
+ "sambaSID": "S-1-5-21-4231626423-2410014848-2360679739-552",
+ "sambaPrimaryGroupSID": "S-1-5-21-4231626423-2410014848-2360679739-512"})
+
+ self.samba3.db.add({
+ "dn": self.samba3.dn("cn=B"),
+ "objectClass": "top",
+ "cn": "B",
+ "sambaNextRid": "x",
+ "sambaBadPasswordCount": "x",
+ "sambaLogonTime": "y",
+ "description": "x"})
+
+ self.samba3.db.add({
+ "dn": self.samba3.dn("cn=C"),
+ "objectClass": "top",
+ "cn": "C",
+ "sambaNextRid": "x",
+ "sambaBadPasswordCount": "y",
+ "sambaLogonTime": "z",
+ "description": "y"})
+
+ # Testing search by DN
+
+ # Search remote record by local DN
+ dn = self.samba4.dn("cn=A")
+ res = self.ldb.search(dn, scope=SCOPE_BASE,
+ attrs=["dnsHostName", "lastLogon"])
+ self.assertEquals(len(res), 1)
+ self.assertEquals(str(res[0].dn), dn)
+ self.assertTrue(not "dnsHostName" in res[0])
+ self.assertEquals(str(res[0]["lastLogon"]), "x")
+
+ # Search remote record by remote DN
+ dn = self.samba3.dn("cn=A")
+ res = self.samba3.db.search(dn, scope=SCOPE_BASE,
+ attrs=["dnsHostName", "lastLogon", "sambaLogonTime"])
+ self.assertEquals(len(res), 1)
+ self.assertEquals(str(res[0].dn), dn)
+ self.assertTrue(not "dnsHostName" in res[0])
+ self.assertTrue(not "lastLogon" in res[0])
+ self.assertEquals(str(res[0]["sambaLogonTime"]), "x")
+
+ # Search split record by local DN
+ dn = self.samba4.dn("cn=X")
+ res = self.ldb.search(dn, scope=SCOPE_BASE,
+ attrs=["dnsHostName", "lastLogon"])
+ self.assertEquals(len(res), 1)
+ self.assertEquals(str(res[0].dn), dn)
+ self.assertEquals(str(res[0]["dnsHostName"]), "x")
+ self.assertEquals(str(res[0]["lastLogon"]), "x")
+
+ # Search split record by remote DN
+ dn = self.samba3.dn("cn=X")
+ res = self.samba3.db.search(dn, scope=SCOPE_BASE,
+ attrs=["dnsHostName", "lastLogon", "sambaLogonTime"])
+ self.assertEquals(len(res), 1)
+ self.assertEquals(str(res[0].dn), dn)
+ self.assertTrue(not "dnsHostName" in res[0])
+ self.assertTrue(not "lastLogon" in res[0])
+ self.assertEquals(str(res[0]["sambaLogonTime"]), "x")
+
+ # Testing search by attribute
+
+ # Search by ignored attribute
+ res = self.ldb.search(expression="(revision=x)", scope=SCOPE_DEFAULT,
+ attrs=["dnsHostName", "lastLogon"])
+ self.assertEquals(len(res), 2)
+ self.assertEquals(str(res[0].dn), self.samba4.dn("cn=Y"))
+ self.assertEquals(str(res[0]["dnsHostName"]), "y")
+ self.assertEquals(str(res[0]["lastLogon"]), "y")
+ self.assertEquals(str(res[1].dn), self.samba4.dn("cn=X"))
+ self.assertEquals(str(res[1]["dnsHostName"]), "x")
+ self.assertEquals(str(res[1]["lastLogon"]), "x")
+
+ # Search by kept attribute
+ res = self.ldb.search(expression="(description=y)",
+ scope=SCOPE_DEFAULT, attrs=["dnsHostName", "lastLogon"])
+ self.assertEquals(len(res), 2)
+ self.assertEquals(str(res[0].dn), self.samba4.dn("cn=Z"))
+ self.assertEquals(str(res[0]["dnsHostName"]), "z")
+ self.assertEquals(str(res[0]["lastLogon"]), "z")
+ self.assertEquals(str(res[1].dn), self.samba4.dn("cn=C"))
+ self.assertTrue(not "dnsHostName" in res[1])
+ self.assertEquals(str(res[1]["lastLogon"]), "z")
+
+ # Search by renamed attribute
+ res = self.ldb.search(expression="(badPwdCount=x)", scope=SCOPE_DEFAULT,
+ attrs=["dnsHostName", "lastLogon"])
+ self.assertEquals(len(res), 2)
+ self.assertEquals(str(res[0].dn), self.samba4.dn("cn=B"))
+ self.assertTrue(not "dnsHostName" in res[0])
+ self.assertEquals(str(res[0]["lastLogon"]), "y")
+ self.assertEquals(str(res[1].dn), self.samba4.dn("cn=A"))
+ self.assertTrue(not "dnsHostName" in res[1])
+ self.assertEquals(str(res[1]["lastLogon"]), "x")
+
+ # Search by converted attribute
+ # TODO:
+ # Using the SID directly in the parse tree leads to conversion
+ # errors, letting the search fail with no results.
+ #res = self.ldb.search("(objectSid=S-1-5-21-4231626423-2410014848-2360679739-552)", scope=SCOPE_DEFAULT, attrs)
+ res = self.ldb.search(expression="(objectSid=*)", base=None, scope=SCOPE_DEFAULT, attrs=["dnsHostName", "lastLogon", "objectSid"])
+ self.assertEquals(len(res), 3)
+ self.assertEquals(str(res[0].dn), self.samba4.dn("cn=X"))
+ self.assertEquals(str(res[0]["dnsHostName"]), "x")
+ self.assertEquals(str(res[0]["lastLogon"]), "x")
+ self.assertSidEquals("S-1-5-21-4231626423-2410014848-2360679739-552",
+ res[0]["objectSid"])
+ self.assertTrue("objectSid" in res[0])
+ self.assertEquals(str(res[1].dn), self.samba4.dn("cn=A"))
+ self.assertTrue(not "dnsHostName" in res[1])
+ self.assertEquals(str(res[1]["lastLogon"]), "x")
+ self.assertSidEquals("S-1-5-21-4231626423-2410014848-2360679739-552",
+ res[1]["objectSid"])
+ self.assertTrue("objectSid" in res[1])
+
+ # Search by generated attribute
+ # In most cases, this even works when the mapping is missing
+ # a `convert_operator' by enumerating the remote db.
+ res = self.ldb.search(expression="(primaryGroupID=512)",
+ attrs=["dnsHostName", "lastLogon", "primaryGroupID"])
+ self.assertEquals(len(res), 1)
+ self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A"))
+ self.assertTrue(not "dnsHostName" in res[0])
+ self.assertEquals(str(res[0]["lastLogon"]), "x")
+ self.assertEquals(str(res[0]["primaryGroupID"]), "512")
+
+ # TODO: There should actually be two results, A and X. The
+ # primaryGroupID of X seems to get corrupted somewhere, and the
+ # objectSid isn't available during the generation of remote (!) data,
+ # which can be observed with the following search. Also note that Xs
+ # objectSid seems to be fine in the previous search for objectSid... */
+ #res = ldb.search(expression="(primaryGroupID=*)", NULL, ldb. SCOPE_DEFAULT, attrs)
+ #print len(res) + " results found"
+ #for i in range(len(res)):
+ # for (obj in res[i]) {
+ # print obj + ": " + res[i][obj]
+ # }
+ # print "---"
+ #
+
+ # Search by remote name of renamed attribute */
+ res = self.ldb.search(expression="(sambaBadPasswordCount=*)",
+ attrs=["dnsHostName", "lastLogon"])
+ self.assertEquals(len(res), 0)
+
+ # Search by objectClass
+ attrs = ["dnsHostName", "lastLogon", "objectClass"]
+ res = self.ldb.search(expression="(objectClass=user)", attrs=attrs)
+ self.assertEquals(len(res), 2)
+ self.assertEquals(str(res[0].dn), self.samba4.dn("cn=X"))
+ self.assertEquals(str(res[0]["dnsHostName"]), "x")
+ self.assertEquals(str(res[0]["lastLogon"]), "x")
+ self.assertEquals(str(res[0]["objectClass"][0]), "user")
+ self.assertEquals(str(res[1].dn), self.samba4.dn("cn=A"))
+ self.assertTrue(not "dnsHostName" in res[1])
+ self.assertEquals(str(res[1]["lastLogon"]), "x")
+ self.assertEquals(str(res[1]["objectClass"][0]), "user")
+
+ # Prove that the objectClass is actually used for the search
+ res = self.ldb.search(expression="(|(objectClass=user)(badPwdCount=x))",
+ attrs=attrs)
+ self.assertEquals(len(res), 3)
+ self.assertEquals(str(res[0].dn), self.samba4.dn("cn=B"))
+ self.assertTrue(not "dnsHostName" in res[0])
+ self.assertEquals(str(res[0]["lastLogon"]), "y")
+ self.assertEquals(set(res[0]["objectClass"]), set(["top"]))
+ self.assertEquals(str(res[1].dn), self.samba4.dn("cn=X"))
+ self.assertEquals(str(res[1]["dnsHostName"]), "x")
+ self.assertEquals(str(res[1]["lastLogon"]), "x")
+ self.assertEquals(str(res[1]["objectClass"][0]), "user")
+ self.assertEquals(str(res[2].dn), self.samba4.dn("cn=A"))
+ self.assertTrue(not "dnsHostName" in res[2])
+ self.assertEquals(str(res[2]["lastLogon"]), "x")
+ self.assertEquals(res[2]["objectClass"][0], "user")
+
+ # Testing search by parse tree
+
+ # Search by conjunction of local attributes
+ res = self.ldb.search(expression="(&(codePage=x)(revision=x))",
+ attrs=["dnsHostName", "lastLogon"])
+ self.assertEquals(len(res), 2)
+ self.assertEquals(str(res[0].dn), self.samba4.dn("cn=Y"))
+ self.assertEquals(str(res[0]["dnsHostName"]), "y")
+ self.assertEquals(str(res[0]["lastLogon"]), "y")
+ self.assertEquals(str(res[1].dn), self.samba4.dn("cn=X"))
+ self.assertEquals(str(res[1]["dnsHostName"]), "x")
+ self.assertEquals(str(res[1]["lastLogon"]), "x")
+
+ # Search by conjunction of remote attributes
+ res = self.ldb.search(expression="(&(lastLogon=x)(description=x))",
+ attrs=["dnsHostName", "lastLogon"])
+ self.assertEquals(len(res), 2)
+ self.assertEquals(str(res[0].dn), self.samba4.dn("cn=X"))
+ self.assertEquals(str(res[0]["dnsHostName"]), "x")
+ self.assertEquals(str(res[0]["lastLogon"]), "x")
+ self.assertEquals(str(res[1].dn), self.samba4.dn("cn=A"))
+ self.assertTrue(not "dnsHostName" in res[1])
+ self.assertEquals(str(res[1]["lastLogon"]), "x")
+
+ # Search by conjunction of local and remote attribute
+ res = self.ldb.search(expression="(&(codePage=x)(description=x))",
+ attrs=["dnsHostName", "lastLogon"])
+ self.assertEquals(len(res), 2)
+ self.assertEquals(str(res[0].dn), self.samba4.dn("cn=Y"))
+ self.assertEquals(str(res[0]["dnsHostName"]), "y")
+ self.assertEquals(str(res[0]["lastLogon"]), "y")
+ self.assertEquals(str(res[1].dn), self.samba4.dn("cn=X"))
+ self.assertEquals(str(res[1]["dnsHostName"]), "x")
+ self.assertEquals(str(res[1]["lastLogon"]), "x")
+
+ # Search by conjunction of local and remote attribute w/o match
+ attrs = ["dnsHostName", "lastLogon"]
+ res = self.ldb.search(expression="(&(codePage=x)(nextRid=x))",
+ attrs=attrs)
+ self.assertEquals(len(res), 0)
+ res = self.ldb.search(expression="(&(revision=x)(lastLogon=z))",
+ attrs=attrs)
+ self.assertEquals(len(res), 0)
+
+ # Search by disjunction of local attributes
+ res = self.ldb.search(expression="(|(revision=x)(dnsHostName=x))",
+ attrs=["dnsHostName", "lastLogon"])
+ self.assertEquals(len(res), 2)
+ self.assertEquals(str(res[0].dn), self.samba4.dn("cn=Y"))
+ self.assertEquals(str(res[0]["dnsHostName"]), "y")
+ self.assertEquals(str(res[0]["lastLogon"]), "y")
+ self.assertEquals(str(res[1].dn), self.samba4.dn("cn=X"))
+ self.assertEquals(str(res[1]["dnsHostName"]), "x")
+ self.assertEquals(str(res[1]["lastLogon"]), "x")
+
+ # Search by disjunction of remote attributes
+ res = self.ldb.search(expression="(|(badPwdCount=x)(lastLogon=x))",
+ attrs=["dnsHostName", "lastLogon"])
+ self.assertEquals(len(res), 3)
+ self.assertEquals(str(res[0].dn), self.samba4.dn("cn=B"))
+ self.assertFalse("dnsHostName" in res[0])
+ self.assertEquals(str(res[0]["lastLogon"]), "y")
+ self.assertEquals(str(res[1].dn), self.samba4.dn("cn=X"))
+ self.assertEquals(str(res[1]["dnsHostName"]), "x")
+ self.assertEquals(str(res[1]["lastLogon"]), "x")
+ self.assertEquals(str(res[2].dn), self.samba4.dn("cn=A"))
+ self.assertFalse("dnsHostName" in res[2])
+ self.assertEquals(str(res[2]["lastLogon"]), "x")
+
+ # Search by disjunction of local and remote attribute
+ res = self.ldb.search(expression="(|(revision=x)(lastLogon=y))",
+ attrs=["dnsHostName", "lastLogon"])
+ self.assertEquals(len(res), 3)
+ self.assertEquals(str(res[0].dn), self.samba4.dn("cn=Y"))
+ self.assertEquals(str(res[0]["dnsHostName"]), "y")
+ self.assertEquals(str(res[0]["lastLogon"]), "y")
+ self.assertEquals(str(res[1].dn), self.samba4.dn("cn=B"))
+ self.assertFalse("dnsHostName" in res[1])
+ self.assertEquals(str(res[1]["lastLogon"]), "y")
+ self.assertEquals(str(res[2].dn), self.samba4.dn("cn=X"))
+ self.assertEquals(str(res[2]["dnsHostName"]), "x")
+ self.assertEquals(str(res[2]["lastLogon"]), "x")
+
+ # Search by disjunction of local and remote attribute w/o match
+ res = self.ldb.search(expression="(|(codePage=y)(nextRid=z))",
+ attrs=["dnsHostName", "lastLogon"])
+ self.assertEquals(len(res), 0)
+
+ # Search by negated local attribute
+ res = self.ldb.search(expression="(!(revision=x))",
+ attrs=["dnsHostName", "lastLogon"])
+ self.assertEquals(len(res), 5)
+ self.assertEquals(str(res[0].dn), self.samba4.dn("cn=B"))
+ self.assertTrue(not "dnsHostName" in res[0])
+ self.assertEquals(str(res[0]["lastLogon"]), "y")
+ self.assertEquals(str(res[1].dn), self.samba4.dn("cn=A"))
+ self.assertTrue(not "dnsHostName" in res[1])
+ self.assertEquals(str(res[1]["lastLogon"]), "x")
+ self.assertEquals(str(res[2].dn), self.samba4.dn("cn=Z"))
+ self.assertEquals(str(res[2]["dnsHostName"]), "z")
+ self.assertEquals(str(res[2]["lastLogon"]), "z")
+ self.assertEquals(str(res[3].dn), self.samba4.dn("cn=C"))
+ self.assertTrue(not "dnsHostName" in res[3])
+ self.assertEquals(str(res[3]["lastLogon"]), "z")
+
+ # Search by negated remote attribute
+ res = self.ldb.search(expression="(!(description=x))",
+ attrs=["dnsHostName", "lastLogon"])
+ self.assertEquals(len(res), 3)
+ self.assertEquals(str(res[0].dn), self.samba4.dn("cn=Z"))
+ self.assertEquals(str(res[0]["dnsHostName"]), "z")
+ self.assertEquals(str(res[0]["lastLogon"]), "z")
+ self.assertEquals(str(res[1].dn), self.samba4.dn("cn=C"))
+ self.assertTrue(not "dnsHostName" in res[1])
+ self.assertEquals(str(res[1]["lastLogon"]), "z")
+
+ # Search by negated conjunction of local attributes
+ res = self.ldb.search(expression="(!(&(codePage=x)(revision=x)))",
+ attrs=["dnsHostName", "lastLogon"])
+ self.assertEquals(len(res), 5)
+ self.assertEquals(str(res[0].dn), self.samba4.dn("cn=B"))
+ self.assertTrue(not "dnsHostName" in res[0])
+ self.assertEquals(str(res[0]["lastLogon"]), "y")
+ self.assertEquals(str(res[1].dn), self.samba4.dn("cn=A"))
+ self.assertTrue(not "dnsHostName" in res[1])
+ self.assertEquals(str(res[1]["lastLogon"]), "x")
+ self.assertEquals(str(res[2].dn), self.samba4.dn("cn=Z"))
+ self.assertEquals(str(res[2]["dnsHostName"]), "z")
+ self.assertEquals(str(res[2]["lastLogon"]), "z")
+ self.assertEquals(str(res[3].dn), self.samba4.dn("cn=C"))
+ self.assertTrue(not "dnsHostName" in res[3])
+ self.assertEquals(str(res[3]["lastLogon"]), "z")
+
+ # Search by negated conjunction of remote attributes
+ res = self.ldb.search(expression="(!(&(lastLogon=x)(description=x)))",
+ attrs=["dnsHostName", "lastLogon"])
+ self.assertEquals(len(res), 5)
+ self.assertEquals(str(res[0].dn), self.samba4.dn("cn=Y"))
+ self.assertEquals(str(res[0]["dnsHostName"]), "y")
+ self.assertEquals(str(res[0]["lastLogon"]), "y")
+ self.assertEquals(str(res[1].dn), self.samba4.dn("cn=B"))
+ self.assertTrue(not "dnsHostName" in res[1])
+ self.assertEquals(str(res[1]["lastLogon"]), "y")
+ self.assertEquals(str(res[2].dn), self.samba4.dn("cn=Z"))
+ self.assertEquals(str(res[2]["dnsHostName"]), "z")
+ self.assertEquals(str(res[2]["lastLogon"]), "z")
+ self.assertEquals(str(res[3].dn), self.samba4.dn("cn=C"))
+ self.assertTrue(not "dnsHostName" in res[3])
+ self.assertEquals(str(res[3]["lastLogon"]), "z")
+
+ # Search by negated conjunction of local and remote attribute
+ res = self.ldb.search(expression="(!(&(codePage=x)(description=x)))",
+ attrs=["dnsHostName", "lastLogon"])
+ self.assertEquals(len(res), 5)
+ self.assertEquals(str(res[0].dn), self.samba4.dn("cn=B"))
+ self.assertTrue(not "dnsHostName" in res[0])
+ self.assertEquals(str(res[0]["lastLogon"]), "y")
+ self.assertEquals(str(res[1].dn), self.samba4.dn("cn=A"))
+ self.assertTrue(not "dnsHostName" in res[1])
+ self.assertEquals(str(res[1]["lastLogon"]), "x")
+ self.assertEquals(str(res[2].dn), self.samba4.dn("cn=Z"))
+ self.assertEquals(str(res[2]["dnsHostName"]), "z")
+ self.assertEquals(str(res[2]["lastLogon"]), "z")
+ self.assertEquals(str(res[3].dn), self.samba4.dn("cn=C"))
+ self.assertTrue(not "dnsHostName" in res[3])
+ self.assertEquals(str(res[3]["lastLogon"]), "z")
+
+ # Search by negated disjunction of local attributes
+ res = self.ldb.search(expression="(!(|(revision=x)(dnsHostName=x)))",
+ attrs=["dnsHostName", "lastLogon"])
+ self.assertEquals(str(res[0].dn), self.samba4.dn("cn=B"))
+ self.assertTrue(not "dnsHostName" in res[0])
+ self.assertEquals(str(res[0]["lastLogon"]), "y")
+ self.assertEquals(str(res[1].dn), self.samba4.dn("cn=A"))
+ self.assertTrue(not "dnsHostName" in res[1])
+ self.assertEquals(str(res[1]["lastLogon"]), "x")
+ self.assertEquals(str(res[2].dn), self.samba4.dn("cn=Z"))
+ self.assertEquals(str(res[2]["dnsHostName"]), "z")
+ self.assertEquals(str(res[2]["lastLogon"]), "z")
+ self.assertEquals(str(res[3].dn), self.samba4.dn("cn=C"))
+ self.assertTrue(not "dnsHostName" in res[3])
+ self.assertEquals(str(res[3]["lastLogon"]), "z")
+
+ # Search by negated disjunction of remote attributes
+ res = self.ldb.search(expression="(!(|(badPwdCount=x)(lastLogon=x)))",
+ attrs=["dnsHostName", "lastLogon"])
+ self.assertEquals(len(res), 4)
+ self.assertEquals(str(res[0].dn), self.samba4.dn("cn=Y"))
+ self.assertEquals(str(res[0]["dnsHostName"]), "y")
+ self.assertEquals(str(res[0]["lastLogon"]), "y")
+ self.assertEquals(str(res[1].dn), self.samba4.dn("cn=Z"))
+ self.assertEquals(str(res[1]["dnsHostName"]), "z")
+ self.assertEquals(str(res[1]["lastLogon"]), "z")
+ self.assertEquals(str(res[2].dn), self.samba4.dn("cn=C"))
+ self.assertTrue(not "dnsHostName" in res[2])
+ self.assertEquals(str(res[2]["lastLogon"]), "z")
+
+ # Search by negated disjunction of local and remote attribute
+ res = self.ldb.search(expression="(!(|(revision=x)(lastLogon=y)))",
+ attrs=["dnsHostName", "lastLogon"])
+ self.assertEquals(len(res), 4)
+ self.assertEquals(str(res[0].dn), self.samba4.dn("cn=A"))
+ self.assertTrue(not "dnsHostName" in res[0])
+ self.assertEquals(str(res[0]["lastLogon"]), "x")
+ self.assertEquals(str(res[1].dn), self.samba4.dn("cn=Z"))
+ self.assertEquals(str(res[1]["dnsHostName"]), "z")
+ self.assertEquals(str(res[1]["lastLogon"]), "z")
+ self.assertEquals(str(res[2].dn), self.samba4.dn("cn=C"))
+ self.assertTrue(not "dnsHostName" in res[2])
+ self.assertEquals(str(res[2]["lastLogon"]), "z")
+
+ # Search by complex parse tree
+ res = self.ldb.search(expression="(|(&(revision=x)(dnsHostName=x))(!(&(description=x)(nextRid=y)))(badPwdCount=y))", attrs=["dnsHostName", "lastLogon"])
+ self.assertEquals(len(res), 6)
+ self.assertEquals(str(res[0].dn), self.samba4.dn("cn=B"))
+ self.assertTrue(not "dnsHostName" in res[0])
+ self.assertEquals(str(res[0]["lastLogon"]), "y")
+ self.assertEquals(str(res[1].dn), self.samba4.dn("cn=X"))
+ self.assertEquals(str(res[1]["dnsHostName"]), "x")
+ self.assertEquals(str(res[1]["lastLogon"]), "x")
+ self.assertEquals(str(res[2].dn), self.samba4.dn("cn=A"))
+ self.assertTrue(not "dnsHostName" in res[2])
+ self.assertEquals(str(res[2]["lastLogon"]), "x")
+ self.assertEquals(str(res[3].dn), self.samba4.dn("cn=Z"))
+ self.assertEquals(str(res[3]["dnsHostName"]), "z")
+ self.assertEquals(str(res[3]["lastLogon"]), "z")
+ self.assertEquals(str(res[4].dn), self.samba4.dn("cn=C"))
+ self.assertTrue(not "dnsHostName" in res[4])
+ self.assertEquals(str(res[4]["lastLogon"]), "z")
+
+ # Clean up
+ dns = [self.samba4.dn("cn=%s" % n) for n in ["A","B","C","X","Y","Z"]]
+ for dn in dns:
+ self.ldb.delete(dn)
+
+ def test_map_modify_local(self):
+ """Modification of local records."""
+ # Add local record
+ dn = "cn=test,dc=idealx,dc=org"
+ self.ldb.add({"dn": dn,
+ "cn": "test",
+ "foo": "bar",
+ "revision": "1",
+ "description": "test"})
+ # Check it's there
+ attrs = ["foo", "revision", "description"]
+ res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs)
+ self.assertEquals(len(res), 1)
+ self.assertEquals(str(res[0].dn), dn)
+ self.assertEquals(str(res[0]["foo"]), "bar")
+ self.assertEquals(str(res[0]["revision"]), "1")
+ self.assertEquals(str(res[0]["description"]), "test")
+ # Check it's not in the local db
+ res = self.samba4.db.search(expression="(cn=test)",
+ scope=SCOPE_DEFAULT, attrs=attrs)
+ self.assertEquals(len(res), 0)
+ # Check it's not in the remote db
+ res = self.samba3.db.search(expression="(cn=test)",
+ scope=SCOPE_DEFAULT, attrs=attrs)
+ self.assertEquals(len(res), 0)
+
+ # Modify local record
+ ldif = """
+dn: """ + dn + """
+replace: foo
+foo: baz
+replace: description
+description: foo
+"""
+ self.ldb.modify_ldif(ldif)
+ # Check in local db
+ res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs)
+ self.assertEquals(len(res), 1)
+ self.assertEquals(str(res[0].dn), dn)
+ self.assertEquals(str(res[0]["foo"]), "baz")
+ self.assertEquals(str(res[0]["revision"]), "1")
+ self.assertEquals(str(res[0]["description"]), "foo")
+
+ # Rename local record
+ dn2 = "cn=toast,dc=idealx,dc=org"
+ self.ldb.rename(dn, dn2)
+ # Check in local db
+ res = self.ldb.search(dn2, scope=SCOPE_BASE, attrs=attrs)
+ self.assertEquals(len(res), 1)
+ self.assertEquals(str(res[0].dn), dn2)
+ self.assertEquals(str(res[0]["foo"]), "baz")
+ self.assertEquals(str(res[0]["revision"]), "1")
+ self.assertEquals(str(res[0]["description"]), "foo")
+
+ # Delete local record
+ self.ldb.delete(dn2)
+ # Check it's gone
+ res = self.ldb.search(dn2, scope=SCOPE_BASE)
+ self.assertEquals(len(res), 0)
+
+ def test_map_modify_remote_remote(self):
+ """Modification of remote data of remote records"""
+ # Add remote record
+ dn = self.samba4.dn("cn=test")
+ dn2 = self.samba3.dn("cn=test")
+ self.samba3.db.add({"dn": dn2,
+ "cn": "test",
+ "description": "foo",
+ "sambaBadPasswordCount": "3",
+ "sambaNextRid": "1001"})
+ # Check it's there
+ res = self.samba3.db.search(dn2, scope=SCOPE_BASE,
+ attrs=["description", "sambaBadPasswordCount", "sambaNextRid"])
+ self.assertEquals(len(res), 1)
+ self.assertEquals(str(res[0].dn), dn2)
+ self.assertEquals(str(res[0]["description"]), "foo")
+ self.assertEquals(str(res[0]["sambaBadPasswordCount"]), "3")
+ self.assertEquals(str(res[0]["sambaNextRid"]), "1001")
+ # Check in mapped db
+ attrs = ["description", "badPwdCount", "nextRid"]
+ res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs, expression="")
+ self.assertEquals(len(res), 1)
+ self.assertEquals(str(res[0].dn), dn)
+ self.assertEquals(str(res[0]["description"]), "foo")
+ self.assertEquals(str(res[0]["badPwdCount"]), "3")
+ self.assertEquals(str(res[0]["nextRid"]), "1001")
+ # Check in local db
+ res = self.samba4.db.search(dn, scope=SCOPE_BASE, attrs=attrs)
+ self.assertEquals(len(res), 0)
+
+ # Modify remote data of remote record
+ ldif = """
+dn: """ + dn + """
+replace: description
+description: test
+replace: badPwdCount
+badPwdCount: 4
+"""
+ self.ldb.modify_ldif(ldif)
+ # Check in mapped db
+ res = self.ldb.search(dn, scope=SCOPE_BASE,
+ attrs=["description", "badPwdCount", "nextRid"])
+ self.assertEquals(len(res), 1)
+ self.assertEquals(str(res[0].dn), dn)
+ self.assertEquals(str(res[0]["description"]), "test")
+ self.assertEquals(str(res[0]["badPwdCount"]), "4")
+ self.assertEquals(str(res[0]["nextRid"]), "1001")
+ # Check in remote db
+ res = self.samba3.db.search(dn2, scope=SCOPE_BASE,
+ attrs=["description", "sambaBadPasswordCount", "sambaNextRid"])
+ self.assertEquals(len(res), 1)
+ self.assertEquals(str(res[0].dn), dn2)
+ self.assertEquals(str(res[0]["description"]), "test")
+ self.assertEquals(str(res[0]["sambaBadPasswordCount"]), "4")
+ self.assertEquals(str(res[0]["sambaNextRid"]), "1001")
+
+ # Rename remote record
+ dn2 = self.samba4.dn("cn=toast")
+ self.ldb.rename(dn, dn2)
+ # Check in mapped db
+ dn = dn2
+ res = self.ldb.search(dn, scope=SCOPE_BASE,
+ attrs=["description", "badPwdCount", "nextRid"])
+ self.assertEquals(len(res), 1)
+ self.assertEquals(str(res[0].dn), dn)
+ self.assertEquals(str(res[0]["description"]), "test")
+ self.assertEquals(str(res[0]["badPwdCount"]), "4")
+ self.assertEquals(str(res[0]["nextRid"]), "1001")
+ # Check in remote db
+ dn2 = self.samba3.dn("cn=toast")
+ res = self.samba3.db.search(dn2, scope=SCOPE_BASE,
+ attrs=["description", "sambaBadPasswordCount", "sambaNextRid"])
+ self.assertEquals(len(res), 1)
+ self.assertEquals(str(res[0].dn), dn2)
+ self.assertEquals(str(res[0]["description"]), "test")
+ self.assertEquals(str(res[0]["sambaBadPasswordCount"]), "4")
+ self.assertEquals(str(res[0]["sambaNextRid"]), "1001")
+
+ # Delete remote record
+ self.ldb.delete(dn)
+ # Check in mapped db that it's removed
+ res = self.ldb.search(dn, scope=SCOPE_BASE)
+ self.assertEquals(len(res), 0)
+ # Check in remote db
+ res = self.samba3.db.search(dn2, scope=SCOPE_BASE)
+ self.assertEquals(len(res), 0)
+
+ def test_map_modify_remote_local(self):
+ """Modification of local data of remote records"""
+ # Add remote record (same as before)
+ dn = self.samba4.dn("cn=test")
+ dn2 = self.samba3.dn("cn=test")
+ self.samba3.db.add({"dn": dn2,
+ "cn": "test",
+ "description": "foo",
+ "sambaBadPasswordCount": "3",
+ "sambaNextRid": "1001"})
+
+ # Modify local data of remote record
+ ldif = """
+dn: """ + dn + """
+add: revision
+revision: 1
+replace: description
+description: test
+
+"""
+ self.ldb.modify_ldif(ldif)
+ # Check in mapped db
+ attrs = ["revision", "description"]
+ res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs)
+ self.assertEquals(len(res), 1)
+ self.assertEquals(str(res[0].dn), dn)
+ self.assertEquals(str(res[0]["description"]), "test")
+ self.assertEquals(str(res[0]["revision"]), "1")
+ # Check in remote db
+ res = self.samba3.db.search(dn2, scope=SCOPE_BASE, attrs=attrs)
+ self.assertEquals(len(res), 1)
+ self.assertEquals(str(res[0].dn), dn2)
+ self.assertEquals(str(res[0]["description"]), "test")
+ self.assertTrue(not "revision" in res[0])
+ # Check in local db
+ res = self.samba4.db.search(dn, scope=SCOPE_BASE, attrs=attrs)
+ self.assertEquals(len(res), 1)
+ self.assertEquals(str(res[0].dn), dn)
+ self.assertTrue(not "description" in res[0])
+ self.assertEquals(str(res[0]["revision"]), "1")
+
+ # Delete (newly) split record
+ self.ldb.delete(dn)
+
+ def test_map_modify_split(self):
+ """Testing modification of split records"""
+ # Add split record
+ dn = self.samba4.dn("cn=test")
+ dn2 = self.samba3.dn("cn=test")
+ self.ldb.add({
+ "dn": dn,
+ "cn": "test",
+ "description": "foo",
+ "badPwdCount": "3",
+ "nextRid": "1001",
+ "revision": "1"})
+ # Check it's there
+ attrs = ["description", "badPwdCount", "nextRid", "revision"]
+ res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs)
+ self.assertEquals(len(res), 1)
+ self.assertEquals(str(res[0].dn), dn)
+ self.assertEquals(str(res[0]["description"]), "foo")
+ self.assertEquals(str(res[0]["badPwdCount"]), "3")
+ self.assertEquals(str(res[0]["nextRid"]), "1001")
+ self.assertEquals(str(res[0]["revision"]), "1")
+ # Check in local db
+ res = self.samba4.db.search(dn, scope=SCOPE_BASE, attrs=attrs)
+ self.assertEquals(len(res), 1)
+ self.assertEquals(str(res[0].dn), dn)
+ self.assertTrue(not "description" in res[0])
+ self.assertTrue(not "badPwdCount" in res[0])
+ self.assertTrue(not "nextRid" in res[0])
+ self.assertEquals(str(res[0]["revision"]), "1")
+ # Check in remote db
+ attrs = ["description", "sambaBadPasswordCount", "sambaNextRid",
+ "revision"]
+ res = self.samba3.db.search(dn2, scope=SCOPE_BASE, attrs=attrs)
+ self.assertEquals(len(res), 1)
+ self.assertEquals(str(res[0].dn), dn2)
+ self.assertEquals(str(res[0]["description"]), "foo")
+ self.assertEquals(str(res[0]["sambaBadPasswordCount"]), "3")
+ self.assertEquals(str(res[0]["sambaNextRid"]), "1001")
+ self.assertTrue(not "revision" in res[0])
+
+ # Modify of split record
+ ldif = """
+dn: """ + dn + """
+replace: description
+description: test
+replace: badPwdCount
+badPwdCount: 4
+replace: revision
+revision: 2
+"""
+ self.ldb.modify_ldif(ldif)
+ # Check in mapped db
+ attrs = ["description", "badPwdCount", "nextRid", "revision"]
+ res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs)
+ self.assertEquals(len(res), 1)
+ self.assertEquals(str(res[0].dn), dn)
+ self.assertEquals(str(res[0]["description"]), "test")
+ self.assertEquals(str(res[0]["badPwdCount"]), "4")
+ self.assertEquals(str(res[0]["nextRid"]), "1001")
+ self.assertEquals(str(res[0]["revision"]), "2")
+ # Check in local db
+ res = self.samba4.db.search(dn, scope=SCOPE_BASE, attrs=attrs)
+ self.assertEquals(len(res), 1)
+ self.assertEquals(str(res[0].dn), dn)
+ self.assertTrue(not "description" in res[0])
+ self.assertTrue(not "badPwdCount" in res[0])
+ self.assertTrue(not "nextRid" in res[0])
+ self.assertEquals(str(res[0]["revision"]), "2")
+ # Check in remote db
+ attrs = ["description", "sambaBadPasswordCount", "sambaNextRid",
+ "revision"]
+ res = self.samba3.db.search(dn2, scope=SCOPE_BASE, attrs=attrs)
+ self.assertEquals(len(res), 1)
+ self.assertEquals(str(res[0].dn), dn2)
+ self.assertEquals(str(res[0]["description"]), "test")
+ self.assertEquals(str(res[0]["sambaBadPasswordCount"]), "4")
+ self.assertEquals(str(res[0]["sambaNextRid"]), "1001")
+ self.assertTrue(not "revision" in res[0])
+
+ # Rename split record
+ dn2 = self.samba4.dn("cn=toast")
+ self.ldb.rename(dn, dn2)
+ # Check in mapped db
+ dn = dn2
+ attrs = ["description", "badPwdCount", "nextRid", "revision"]
+ res = self.ldb.search(dn, scope=SCOPE_BASE, attrs=attrs)
+ self.assertEquals(len(res), 1)
+ self.assertEquals(str(res[0].dn), dn)
+ self.assertEquals(str(res[0]["description"]), "test")
+ self.assertEquals(str(res[0]["badPwdCount"]), "4")
+ self.assertEquals(str(res[0]["nextRid"]), "1001")
+ self.assertEquals(str(res[0]["revision"]), "2")
+ # Check in local db
+ res = self.samba4.db.search(dn, scope=SCOPE_BASE, attrs=attrs)
+ self.assertEquals(len(res), 1)
+ self.assertEquals(str(res[0].dn), dn)
+ self.assertTrue(not "description" in res[0])
+ self.assertTrue(not "badPwdCount" in res[0])
+ self.assertTrue(not "nextRid" in res[0])
+ self.assertEquals(str(res[0]["revision"]), "2")
+ # Check in remote db
+ dn2 = self.samba3.dn("cn=toast")
+ res = self.samba3.db.search(dn2, scope=SCOPE_BASE,
+ attrs=["description", "sambaBadPasswordCount", "sambaNextRid",
+ "revision"])
+ self.assertEquals(len(res), 1)
+ self.assertEquals(str(res[0].dn), dn2)
+ self.assertEquals(str(res[0]["description"]), "test")
+ self.assertEquals(str(res[0]["sambaBadPasswordCount"]), "4")
+ self.assertEquals(str(res[0]["sambaNextRid"]), "1001")
+ self.assertTrue(not "revision" in res[0])
+
+ # Delete split record
+ self.ldb.delete(dn)
+ # Check in mapped db
+ res = self.ldb.search(dn, scope=SCOPE_BASE)
+ self.assertEquals(len(res), 0)
+ # Check in local db
+ res = self.samba4.db.search(dn, scope=SCOPE_BASE)
+ self.assertEquals(len(res), 0)
+ # Check in remote db
+ res = self.samba3.db.search(dn2, scope=SCOPE_BASE)
+ self.assertEquals(len(res), 0)
diff --git a/source4/dsdb/samdb/ldb_modules/update_keytab.c b/source4/dsdb/samdb/ldb_modules/update_keytab.c
new file mode 100644
index 0000000000..f1b6863cdb
--- /dev/null
+++ b/source4/dsdb/samdb/ldb_modules/update_keytab.c
@@ -0,0 +1,447 @@
+/*
+ ldb database library
+
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb update_keytabs module
+ *
+ * Description: Update keytabs whenever their matching secret record changes
+ *
+ * Author: Andrew Bartlett
+ */
+
+#include "includes.h"
+#include "ldb_module.h"
+#include "dlinklist.h"
+#include "auth/credentials/credentials.h"
+#include "auth/credentials/credentials_krb5.h"
+#include "system/kerberos.h"
+
+struct dn_list {
+ struct cli_credentials *creds;
+ struct dn_list *prev, *next;
+};
+
+struct update_kt_private {
+ struct dn_list *changed_dns;
+};
+
+struct update_kt_ctx {
+ struct ldb_module *module;
+ struct ldb_request *req;
+
+ struct ldb_dn *dn;
+ bool do_delete;
+
+ struct ldb_reply *op_reply;
+ bool found;
+};
+
+static struct update_kt_ctx *update_kt_ctx_init(struct ldb_module *module,
+ struct ldb_request *req)
+{
+ struct update_kt_ctx *ac;
+
+ ac = talloc_zero(req, struct update_kt_ctx);
+ if (ac == NULL) {
+ ldb_oom(ldb_module_get_ctx(module));
+ return NULL;
+ }
+
+ ac->module = module;
+ ac->req = req;
+
+ return ac;
+}
+
+/* FIXME: too many semi-async searches here for my taste, direct and indirect as
+ * cli_credentials_set_secrets() performs a sync ldb search.
+ * Just hope we are lucky and nothing breaks (using the tdb backend masks a lot
+ * of async issues). -SSS
+ */
+static int add_modified(struct ldb_module *module, struct ldb_dn *dn, bool do_delete) {
+ struct ldb_context *ldb = ldb_module_get_ctx(module);
+ struct update_kt_private *data = talloc_get_type(ldb_module_get_private(module), struct update_kt_private);
+ struct dn_list *item;
+ char *filter;
+ struct ldb_result *res;
+ const char *attrs[] = { NULL };
+ int ret;
+ NTSTATUS status;
+
+ filter = talloc_asprintf(data, "(&(dn=%s)(&(objectClass=kerberosSecret)(privateKeytab=*)))",
+ ldb_dn_get_linearized(dn));
+ if (!filter) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ldb_search(ldb, data, &res,
+ dn, LDB_SCOPE_BASE, attrs, "%s", filter);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(filter);
+ return ret;
+ }
+
+ if (res->count != 1) {
+ /* if it's not a kerberosSecret then we don't have anything to update */
+ talloc_free(res);
+ talloc_free(filter);
+ return LDB_SUCCESS;
+ }
+ talloc_free(res);
+
+ item = talloc(data->changed_dns? (void *)data->changed_dns: (void *)data, struct dn_list);
+ if (!item) {
+ talloc_free(filter);
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ item->creds = cli_credentials_init(item);
+ if (!item->creds) {
+ DEBUG(1, ("cli_credentials_init failed!"));
+ talloc_free(filter);
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ cli_credentials_set_conf(item->creds, ldb_get_opaque(ldb, "loadparm"));
+ status = cli_credentials_set_secrets(item->creds, ldb_get_event_context(ldb), ldb_get_opaque(ldb, "loadparm"), ldb, NULL, filter);
+ talloc_free(filter);
+ if (NT_STATUS_IS_OK(status)) {
+ if (do_delete) {
+ /* Ensure we don't helpfully keep an old keytab entry */
+ cli_credentials_set_kvno(item->creds, cli_credentials_get_kvno(item->creds)+2);
+ /* Wipe passwords */
+ cli_credentials_set_nt_hash(item->creds, NULL,
+ CRED_SPECIFIED);
+ }
+ DLIST_ADD_END(data->changed_dns, item, struct dn_list *);
+ }
+ return LDB_SUCCESS;
+}
+
+static int ukt_search_modified(struct update_kt_ctx *ac);
+
+static int update_kt_op_callback(struct ldb_request *req,
+ struct ldb_reply *ares)
+{
+ struct ldb_context *ldb;
+ struct update_kt_ctx *ac;
+ int ret;
+
+ ac = talloc_get_type(req->context, struct update_kt_ctx);
+ ldb = ldb_module_get_ctx(ac->module);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ if (ares->type != LDB_REPLY_DONE) {
+ ldb_set_errstring(ldb, "Invalid request type!\n");
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ if (ac->do_delete) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, LDB_SUCCESS);
+ }
+
+ ac->op_reply = talloc_steal(ac, ares);
+
+ ret = ukt_search_modified(ac);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+
+ return LDB_SUCCESS;
+}
+
+static int ukt_del_op(struct update_kt_ctx *ac)
+{
+ struct ldb_context *ldb;
+ struct ldb_request *down_req;
+ int ret;
+
+ ldb = ldb_module_get_ctx(ac->module);
+
+ ret = ldb_build_del_req(&down_req, ldb, ac,
+ ac->dn,
+ ac->req->controls,
+ ac, update_kt_op_callback,
+ ac->req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ return ldb_next_request(ac->module, down_req);
+}
+
+static int ukt_search_modified_callback(struct ldb_request *req,
+ struct ldb_reply *ares)
+{
+ struct update_kt_ctx *ac;
+ int ret;
+
+ ac = talloc_get_type(req->context, struct update_kt_ctx);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+
+ ac->found = true;
+ break;
+
+ case LDB_REPLY_REFERRAL:
+ /* ignore */
+ break;
+
+ case LDB_REPLY_DONE:
+
+ if (ac->found) {
+ /* do the dirty sync job here :/ */
+ ret = add_modified(ac->module, ac->dn, ac->do_delete);
+ }
+
+ if (ac->do_delete) {
+ ret = ukt_del_op(ac);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req,
+ NULL, NULL, ret);
+ }
+ break;
+ }
+
+ return ldb_module_done(ac->req, ac->op_reply->controls,
+ ac->op_reply->response, LDB_SUCCESS);
+ }
+
+ talloc_free(ares);
+ return LDB_SUCCESS;
+}
+
+static int ukt_search_modified(struct update_kt_ctx *ac)
+{
+ struct ldb_context *ldb;
+ static const char * const attrs[] = { "distinguishedName", NULL };
+ struct ldb_request *search_req;
+ int ret;
+
+ ldb = ldb_module_get_ctx(ac->module);
+
+ ret = ldb_build_search_req(&search_req, ldb, ac,
+ ac->dn, LDB_SCOPE_BASE,
+ "(&(objectClass=kerberosSecret)"
+ "(privateKeytab=*))", attrs,
+ NULL,
+ ac, ukt_search_modified_callback,
+ ac->req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ return ldb_next_request(ac->module, search_req);
+}
+
+
+/* add */
+static int update_kt_add(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct update_kt_ctx *ac;
+ struct ldb_request *down_req;
+ int ret;
+
+ ldb = ldb_module_get_ctx(module);
+
+ ac = update_kt_ctx_init(module, req);
+ if (ac == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->dn = req->op.add.message->dn;
+
+ ret = ldb_build_add_req(&down_req, ldb, ac,
+ req->op.add.message,
+ req->controls,
+ ac, update_kt_op_callback,
+ req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ return ldb_next_request(module, down_req);
+}
+
+/* modify */
+static int update_kt_modify(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct update_kt_ctx *ac;
+ struct ldb_request *down_req;
+ int ret;
+
+ ldb = ldb_module_get_ctx(module);
+
+ ac = update_kt_ctx_init(module, req);
+ if (ac == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->dn = req->op.mod.message->dn;
+
+ ret = ldb_build_mod_req(&down_req, ldb, ac,
+ req->op.mod.message,
+ req->controls,
+ ac, update_kt_op_callback,
+ req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ return ldb_next_request(module, down_req);
+}
+
+/* delete */
+static int update_kt_delete(struct ldb_module *module, struct ldb_request *req)
+{
+ struct update_kt_ctx *ac;
+
+ ac = update_kt_ctx_init(module, req);
+ if (ac == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->dn = req->op.del.dn;
+ ac->do_delete = true;
+
+ return ukt_search_modified(ac);
+}
+
+/* rename */
+static int update_kt_rename(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct update_kt_ctx *ac;
+ struct ldb_request *down_req;
+ int ret;
+
+ ldb = ldb_module_get_ctx(module);
+
+ ac = update_kt_ctx_init(module, req);
+ if (ac == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->dn = req->op.rename.newdn;
+
+ ret = ldb_build_rename_req(&down_req, ldb, ac,
+ req->op.rename.olddn,
+ req->op.rename.newdn,
+ req->controls,
+ ac, update_kt_op_callback,
+ req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ return ldb_next_request(module, down_req);
+}
+
+/* end a transaction */
+static int update_kt_end_trans(struct ldb_module *module)
+{
+ struct ldb_context *ldb;
+ struct update_kt_private *data = talloc_get_type(ldb_module_get_private(module), struct update_kt_private);
+ struct dn_list *p;
+
+ ldb = ldb_module_get_ctx(module);
+
+ for (p=data->changed_dns; p; p = p->next) {
+ int kret;
+ kret = cli_credentials_update_keytab(p->creds, ldb_get_event_context(ldb), ldb_get_opaque(ldb, "loadparm"));
+ if (kret != 0) {
+ talloc_free(data->changed_dns);
+ data->changed_dns = NULL;
+ ldb_asprintf_errstring(ldb, "Failed to update keytab: %s", error_message(kret));
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ }
+
+ talloc_free(data->changed_dns);
+ data->changed_dns = NULL;
+
+ return ldb_next_end_trans(module);
+}
+
+/* end a transaction */
+static int update_kt_del_trans(struct ldb_module *module)
+{
+ struct update_kt_private *data = talloc_get_type(ldb_module_get_private(module), struct update_kt_private);
+
+ talloc_free(data->changed_dns);
+ data->changed_dns = NULL;
+
+ return ldb_next_del_trans(module);
+}
+
+static int update_kt_init(struct ldb_module *module)
+{
+ struct ldb_context *ldb;
+ struct update_kt_private *data;
+
+ ldb = ldb_module_get_ctx(module);
+
+ data = talloc(module, struct update_kt_private);
+ if (data == NULL) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ data->changed_dns = NULL;
+
+ ldb_module_set_private(module, data);
+
+ return ldb_next_init(module);
+}
+
+_PUBLIC_ const struct ldb_module_ops ldb_update_keytab_module_ops = {
+ .name = "update_keytab",
+ .init_context = update_kt_init,
+ .add = update_kt_add,
+ .modify = update_kt_modify,
+ .rename = update_kt_rename,
+ .del = update_kt_delete,
+ .end_transaction = update_kt_end_trans,
+ .del_transaction = update_kt_del_trans,
+};
diff --git a/source4/dsdb/samdb/samdb.c b/source4/dsdb/samdb/samdb.c
new file mode 100644
index 0000000000..98ca6d03a1
--- /dev/null
+++ b/source4/dsdb/samdb/samdb.c
@@ -0,0 +1,296 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ interface functions for the sam database
+
+ Copyright (C) Andrew Tridgell 2004
+ Copyright (C) Volker Lendecke 2004
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "librpc/gen_ndr/ndr_netlogon.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "lib/events/events.h"
+#include "lib/ldb/include/ldb.h"
+#include "lib/ldb/include/ldb_errors.h"
+#include "libcli/security/security.h"
+#include "libcli/auth/libcli_auth.h"
+#include "libcli/ldap/ldap_ndr.h"
+#include "system/time.h"
+#include "system/filesys.h"
+#include "ldb_wrap.h"
+#include "../lib/util/util_ldb.h"
+#include "dsdb/samdb/samdb.h"
+#include "dsdb/common/flags.h"
+#include "param/param.h"
+#include "lib/events/events.h"
+#include "auth/credentials/credentials.h"
+#include "param/secrets.h"
+
+char *samdb_relative_path(struct ldb_context *ldb,
+ TALLOC_CTX *mem_ctx,
+ const char *name)
+{
+ const char *base_url =
+ (const char *)ldb_get_opaque(ldb, "ldb_url");
+ char *path, *p, *full_name;
+ if (name == NULL) {
+ return NULL;
+ }
+ if (name[0] == 0 || name[0] == '/' || strstr(name, ":/")) {
+ return talloc_strdup(mem_ctx, name);
+ }
+ path = talloc_strdup(mem_ctx, base_url);
+ if (path == NULL) {
+ return NULL;
+ }
+ if ( (p = strrchr(path, '/')) != NULL) {
+ p[0] = '\0';
+ full_name = talloc_asprintf(mem_ctx, "%s/%s", path, name);
+ } else {
+ full_name = talloc_asprintf(mem_ctx, "./%s", name);
+ }
+ talloc_free(path);
+ return full_name;
+}
+
+struct cli_credentials *samdb_credentials(TALLOC_CTX *mem_ctx,
+ struct tevent_context *event_ctx,
+ struct loadparm_context *lp_ctx)
+{
+ struct cli_credentials *cred = cli_credentials_init(mem_ctx);
+ if (!cred) {
+ return NULL;
+ }
+ cli_credentials_set_conf(cred, lp_ctx);
+
+ /* We don't want to use krb5 to talk to our samdb - recursion
+ * here would be bad, and this account isn't in the KDC
+ * anyway */
+ cli_credentials_set_kerberos_state(cred, CRED_DONT_USE_KERBEROS);
+
+ if (!NT_STATUS_IS_OK(cli_credentials_set_secrets(cred, event_ctx, lp_ctx, NULL, NULL,
+ SECRETS_LDAP_FILTER))) {
+ /* Perfectly OK - if not against an LDAP backend */
+ return NULL;
+ }
+ return cred;
+}
+
+/*
+ connect to the SAM database
+ return an opaque context pointer on success, or NULL on failure
+ */
+struct ldb_context *samdb_connect(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx,
+ struct auth_session_info *session_info)
+{
+ struct ldb_context *ldb;
+ ldb = ldb_wrap_connect(mem_ctx, ev_ctx, lp_ctx,
+ lp_sam_url(lp_ctx), session_info,
+ samdb_credentials(mem_ctx, ev_ctx, lp_ctx),
+ 0, NULL);
+ if (!ldb) {
+ return NULL;
+ }
+ dsdb_make_schema_global(ldb);
+ return ldb;
+}
+
+/*
+ copy from a template record to a message
+*/
+int samdb_copy_template(struct ldb_context *ldb,
+ struct ldb_message *msg, const char *name,
+ const char **errstring)
+{
+ struct ldb_result *res;
+ struct ldb_message *t;
+ int ret, i, j;
+ struct ldb_context *templates_ldb;
+ char *templates_ldb_path;
+ struct ldb_dn *basedn;
+ struct tevent_context *event_ctx;
+ struct loadparm_context *lp_ctx;
+
+ templates_ldb = talloc_get_type(ldb_get_opaque(ldb, "templates_ldb"), struct ldb_context);
+
+ if (!templates_ldb) {
+ templates_ldb_path = samdb_relative_path(ldb,
+ msg,
+ "templates.ldb");
+ if (!templates_ldb_path) {
+ *errstring = talloc_asprintf(msg, "samdb_copy_template: ERROR: Failed to contruct path for template db");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ event_ctx = ldb_get_event_context(ldb);
+ lp_ctx = (struct loadparm_context *)ldb_get_opaque(ldb, "loadparm");
+
+ /* FIXME: need to remove this wehn we finally pass the event
+ * context around in ldb */
+ if (event_ctx == NULL) {
+ event_ctx = s4_event_context_init(templates_ldb);
+ }
+
+ templates_ldb = ldb_wrap_connect(ldb, event_ctx, lp_ctx,
+ templates_ldb_path, NULL,
+ NULL, 0, NULL);
+ talloc_free(templates_ldb_path);
+ if (!templates_ldb) {
+ *errstring = talloc_asprintf(msg, "samdb_copy_template: ERROR: Failed to connect to templates db at: %s",
+ templates_ldb_path);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ldb_set_opaque(ldb, "templates_ldb", templates_ldb);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+ *errstring = NULL;
+
+ basedn = ldb_dn_new(templates_ldb, ldb, "cn=Templates");
+ if (!ldb_dn_add_child_fmt(basedn, "CN=Template%s", name)) {
+ talloc_free(basedn);
+ *errstring = talloc_asprintf(msg, "samdb_copy_template: ERROR: Failed to contruct DN for template '%s'",
+ name);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* pull the template record */
+ ret = ldb_search(templates_ldb, msg, &res, basedn, LDB_SCOPE_BASE, NULL, "distinguishedName=*");
+ talloc_free(basedn);
+ if (ret != LDB_SUCCESS) {
+ *errstring = talloc_steal(msg, ldb_errstring(templates_ldb));
+ return ret;
+ }
+ if (res->count != 1) {
+ *errstring = talloc_asprintf(msg, "samdb_copy_template: ERROR: template '%s' matched %d records, expected 1",
+ name,
+ res->count);
+ talloc_free(res);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ t = res->msgs[0];
+
+ for (i = 0; i < t->num_elements; i++) {
+ struct ldb_message_element *el = &t->elements[i];
+ /* some elements should not be copied from the template */
+ if (ldb_attr_cmp(el->name, "cn") == 0 ||
+ ldb_attr_cmp(el->name, "name") == 0 ||
+ ldb_attr_cmp(el->name, "objectClass") == 0 ||
+ ldb_attr_cmp(el->name, "sAMAccountName") == 0 ||
+ ldb_attr_cmp(el->name, "sAMAccountName") == 0 ||
+ ldb_attr_cmp(el->name, "distinguishedName") == 0 ||
+ ldb_attr_cmp(el->name, "objectGUID") == 0) {
+ continue;
+ }
+ for (j = 0; j < el->num_values; j++) {
+ ret = samdb_find_or_add_attribute(ldb, msg, el->name,
+ (char *)el->values[j].data);
+ if (ret) {
+ *errstring = talloc_asprintf(msg, "Adding attribute %s failed.", el->name);
+ talloc_free(res);
+ return ret;
+ }
+ }
+ }
+
+ talloc_free(res);
+
+ return LDB_SUCCESS;
+}
+
+
+/****************************************************************************
+ Create the SID list for this user.
+****************************************************************************/
+NTSTATUS security_token_create(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx,
+ struct dom_sid *user_sid,
+ struct dom_sid *group_sid,
+ int n_groupSIDs,
+ struct dom_sid **groupSIDs,
+ bool is_authenticated,
+ struct security_token **token)
+{
+ struct security_token *ptoken;
+ int i;
+ NTSTATUS status;
+
+ ptoken = security_token_initialise(mem_ctx);
+ NT_STATUS_HAVE_NO_MEMORY(ptoken);
+
+ ptoken->sids = talloc_array(ptoken, struct dom_sid *, n_groupSIDs + 5);
+ NT_STATUS_HAVE_NO_MEMORY(ptoken->sids);
+
+ ptoken->user_sid = talloc_reference(ptoken, user_sid);
+ ptoken->group_sid = talloc_reference(ptoken, group_sid);
+ ptoken->privilege_mask = 0;
+
+ ptoken->sids[0] = ptoken->user_sid;
+ ptoken->sids[1] = ptoken->group_sid;
+
+ /*
+ * Finally add the "standard" SIDs.
+ * The only difference between guest and "anonymous"
+ * is the addition of Authenticated_Users.
+ */
+ ptoken->sids[2] = dom_sid_parse_talloc(ptoken->sids, SID_WORLD);
+ NT_STATUS_HAVE_NO_MEMORY(ptoken->sids[2]);
+ ptoken->sids[3] = dom_sid_parse_talloc(ptoken->sids, SID_NT_NETWORK);
+ NT_STATUS_HAVE_NO_MEMORY(ptoken->sids[3]);
+ ptoken->num_sids = 4;
+
+ if (is_authenticated) {
+ ptoken->sids[4] = dom_sid_parse_talloc(ptoken->sids, SID_NT_AUTHENTICATED_USERS);
+ NT_STATUS_HAVE_NO_MEMORY(ptoken->sids[4]);
+ ptoken->num_sids++;
+ }
+
+ for (i = 0; i < n_groupSIDs; i++) {
+ size_t check_sid_idx;
+ for (check_sid_idx = 1;
+ check_sid_idx < ptoken->num_sids;
+ check_sid_idx++) {
+ if (dom_sid_equal(ptoken->sids[check_sid_idx], groupSIDs[i])) {
+ break;
+ }
+ }
+
+ if (check_sid_idx == ptoken->num_sids) {
+ ptoken->sids[ptoken->num_sids++] = talloc_reference(ptoken->sids, groupSIDs[i]);
+ }
+ }
+
+ /* setup the privilege mask for this token */
+ status = samdb_privilege_setup(ev_ctx, lp_ctx, ptoken);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(ptoken);
+ return status;
+ }
+
+ security_token_debug(10, ptoken);
+
+ *token = ptoken;
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/dsdb/samdb/samdb.h b/source4/dsdb/samdb/samdb.h
new file mode 100644
index 0000000000..49dc14d74c
--- /dev/null
+++ b/source4/dsdb/samdb/samdb.h
@@ -0,0 +1,130 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ interface functions for the sam database
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __SAMDB_H__
+#define __SAMDB_H__
+
+struct auth_session_info;
+struct dsdb_control_current_partition;
+struct dsdb_extended_replicated_object;
+struct dsdb_extended_replicated_objects;
+struct loadparm_context;
+struct tevent_context;
+
+#include "librpc/gen_ndr/security.h"
+#include "lib/ldb/include/ldb.h"
+#include "lib/ldb-samba/ldif_handlers.h"
+#include "librpc/gen_ndr/samr.h"
+#include "librpc/gen_ndr/drsuapi.h"
+#include "librpc/gen_ndr/drsblobs.h"
+#include "dsdb/schema/schema.h"
+#include "dsdb/samdb/samdb_proto.h"
+#include "dsdb/common/proto.h"
+#include "dsdb/common/flags.h"
+
+#define DSDB_CONTROL_CURRENT_PARTITION_OID "1.3.6.1.4.1.7165.4.3.2"
+struct dsdb_control_current_partition {
+ /*
+ * this is the version of the dsdb_control_current_partition
+ * version 0: initial implementation
+ */
+#define DSDB_CONTROL_CURRENT_PARTITION_VERSION 0
+ uint32_t version;
+
+ struct ldb_dn *dn;
+
+ const char *backend;
+
+ struct ldb_module *module;
+};
+
+#define DSDB_CONTROL_REPLICATED_UPDATE_OID "1.3.6.1.4.1.7165.4.3.3"
+/* DSDB_CONTROL_REPLICATED_UPDATE_OID has NULL data */
+
+#define DSDB_CONTROL_DN_STORAGE_FORMAT_OID "1.3.6.1.4.1.7165.4.3.4"
+/* DSDB_CONTROL_DN_STORAGE_FORMAT_OID has NULL data and behaves very
+ * much like LDB_CONTROL_EXTENDED_DN_OID when the DB stores an
+ * extended DN, and otherwise returns normal DNs */
+
+#define DSDB_EXTENDED_REPLICATED_OBJECTS_OID "1.3.6.1.4.1.7165.4.4.1"
+struct dsdb_extended_replicated_object {
+ struct ldb_message *msg;
+ struct ldb_val guid_value;
+ const char *when_changed;
+ struct replPropertyMetaDataBlob *meta_data;
+};
+
+struct dsdb_extended_replicated_objects {
+ /*
+ * this is the version of the dsdb_extended_replicated_objects
+ * version 0: initial implementation
+ */
+#define DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION 0
+ uint32_t version;
+
+ struct ldb_dn *partition_dn;
+
+ const struct repsFromTo1 *source_dsa;
+ const struct drsuapi_DsReplicaCursor2CtrEx *uptodateness_vector;
+
+ uint32_t num_objects;
+ struct dsdb_extended_replicated_object *objects;
+};
+
+struct dsdb_naming_fsmo {
+ bool we_are_master;
+ struct ldb_dn *master_dn;
+};
+
+struct dsdb_pdc_fsmo {
+ bool we_are_master;
+ struct ldb_dn *master_dn;
+};
+
+/*
+ * the schema_dn is passed as struct ldb_dn in
+ * req->op.extended.data
+ */
+#define DSDB_EXTENDED_SCHEMA_UPDATE_NOW_OID "1.3.6.1.4.1.7165.4.4.2"
+
+#define DSDB_OPENLDAP_DEREFERENCE_CONTROL "1.3.6.1.4.1.4203.666.5.16"
+
+struct dsdb_openldap_dereference {
+ const char *source_attribute;
+ const char **dereference_attribute;
+};
+
+struct dsdb_openldap_dereference_control {
+ struct dsdb_openldap_dereference **dereference;
+};
+
+struct dsdb_openldap_dereference_result {
+ const char *source_attribute;
+ const char *dereferenced_dn;
+ int num_attributes;
+ struct ldb_message_element *attributes;
+};
+
+struct dsdb_openldap_dereference_result_control {
+ struct dsdb_openldap_dereference_result **attributes;
+};
+
+#endif /* __SAMDB_H__ */
diff --git a/source4/dsdb/samdb/samdb_privilege.c b/source4/dsdb/samdb/samdb_privilege.c
new file mode 100644
index 0000000000..e9c6f4c527
--- /dev/null
+++ b/source4/dsdb/samdb/samdb_privilege.c
@@ -0,0 +1,121 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ manipulate privilege records in samdb
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/ldap/ldap_ndr.h"
+#include "dsdb/samdb/samdb.h"
+#include "auth/auth.h"
+#include "libcli/security/security.h"
+#include "../lib/util/util_ldb.h"
+#include "param/param.h"
+
+/*
+ add privilege bits for one sid to a security_token
+*/
+static NTSTATUS samdb_privilege_setup_sid(void *samctx, TALLOC_CTX *mem_ctx,
+ struct security_token *token,
+ const struct dom_sid *sid)
+{
+ const char * const attrs[] = { "privilege", NULL };
+ struct ldb_message **res = NULL;
+ struct ldb_message_element *el;
+ int ret, i;
+ char *sidstr;
+
+ sidstr = ldap_encode_ndr_dom_sid(mem_ctx, sid);
+ NT_STATUS_HAVE_NO_MEMORY(sidstr);
+
+ ret = gendb_search(samctx, mem_ctx, NULL, &res, attrs, "objectSid=%s", sidstr);
+ talloc_free(sidstr);
+ if (ret != 1) {
+ /* not an error to not match */
+ return NT_STATUS_OK;
+ }
+
+ el = ldb_msg_find_element(res[0], "privilege");
+ if (el == NULL) {
+ return NT_STATUS_OK;
+ }
+
+ for (i=0;i<el->num_values;i++) {
+ const char *priv_str = (const char *)el->values[i].data;
+ enum sec_privilege privilege = sec_privilege_id(priv_str);
+ if (privilege == -1) {
+ DEBUG(1,("Unknown privilege '%s' in samdb\n",
+ priv_str));
+ continue;
+ }
+ security_token_set_privilege(token, privilege);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ setup the privilege mask for this security token based on our
+ local SAM
+*/
+NTSTATUS samdb_privilege_setup(struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx, struct security_token *token)
+{
+ void *samctx;
+ TALLOC_CTX *mem_ctx;
+ int i;
+ NTSTATUS status;
+
+ /* Shortcuts to prevent recursion and avoid lookups */
+ if (token->user_sid == NULL) {
+ token->privilege_mask = 0;
+ return NT_STATUS_OK;
+ }
+
+ if (security_token_is_system(token)) {
+ token->privilege_mask = ~0;
+ return NT_STATUS_OK;
+ }
+
+ if (security_token_is_anonymous(token)) {
+ token->privilege_mask = 0;
+ return NT_STATUS_OK;
+ }
+
+ mem_ctx = talloc_new(token);
+ samctx = samdb_connect(mem_ctx, ev_ctx, lp_ctx, system_session(mem_ctx, lp_ctx));
+ if (samctx == NULL) {
+ talloc_free(mem_ctx);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ token->privilege_mask = 0;
+
+ for (i=0;i<token->num_sids;i++) {
+ status = samdb_privilege_setup_sid(samctx, mem_ctx,
+ token, token->sids[i]);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(mem_ctx);
+ return status;
+ }
+ }
+
+ talloc_free(mem_ctx);
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/dsdb/schema/schema.h b/source4/dsdb/schema/schema.h
new file mode 100644
index 0000000000..f7d59a7c39
--- /dev/null
+++ b/source4/dsdb/schema/schema.h
@@ -0,0 +1,185 @@
+/*
+ Unix SMB/CIFS mplementation.
+ DSDB schema header
+
+ Copyright (C) Stefan Metzmacher <metze@samba.org> 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 <http://www.gnu.org/licenses/>.
+
+*/
+
+#ifndef _DSDB_SCHEMA_H
+#define _DSDB_SCHEMA_H
+
+struct dsdb_attribute;
+struct dsdb_class;
+struct dsdb_schema;
+
+struct dsdb_syntax {
+ const char *name;
+ const char *ldap_oid;
+ uint32_t oMSyntax;
+ struct ldb_val oMObjectClass;
+ const char *attributeSyntax_oid;
+ const char *equality;
+ const char *substring;
+ const char *comment;
+ const char *ldb_syntax;
+
+ WERROR (*drsuapi_to_ldb)(struct ldb_context *ldb,
+ const struct dsdb_schema *schema,
+ const struct dsdb_attribute *attr,
+ const struct drsuapi_DsReplicaAttribute *in,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message_element *out);
+ WERROR (*ldb_to_drsuapi)(struct ldb_context *ldb,
+ const struct dsdb_schema *schema,
+ const struct dsdb_attribute *attr,
+ const struct ldb_message_element *in,
+ TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsReplicaAttribute *out);
+};
+
+struct dsdb_attribute {
+ struct dsdb_attribute *prev, *next;
+
+ const char *cn;
+ const char *lDAPDisplayName;
+ const char *attributeID_oid;
+ uint32_t attributeID_id;
+ struct GUID schemaIDGUID;
+ uint32_t mAPIID;
+
+ struct GUID attributeSecurityGUID;
+
+ uint32_t searchFlags;
+ uint32_t systemFlags;
+ bool isMemberOfPartialAttributeSet;
+ uint32_t linkID;
+
+ const char *attributeSyntax_oid;
+ uint32_t attributeSyntax_id;
+ uint32_t oMSyntax;
+ struct ldb_val oMObjectClass;
+
+ bool isSingleValued;
+ uint32_t *rangeLower;
+ uint32_t *rangeUpper;
+ bool extendedCharsAllowed;
+
+ uint32_t schemaFlagsEx;
+ struct ldb_val msDs_Schema_Extensions;
+
+ bool showInAdvancedViewOnly;
+ const char *adminDisplayName;
+ const char *adminDescription;
+ const char *classDisplayName;
+ bool isEphemeral;
+ bool isDefunct;
+ bool systemOnly;
+
+ /* internal stuff */
+ const struct dsdb_syntax *syntax;
+};
+
+struct dsdb_class {
+ struct dsdb_class *prev, *next;
+
+ const char *cn;
+ const char *lDAPDisplayName;
+ const char *governsID_oid;
+ uint32_t governsID_id;
+ struct GUID schemaIDGUID;
+
+ uint32_t objectClassCategory;
+ const char *rDNAttID;
+ const char *defaultObjectCategory;
+
+ const char *subClassOf;
+
+ const char **systemAuxiliaryClass;
+ const char **systemPossSuperiors;
+ const char **systemMustContain;
+ const char **systemMayContain;
+
+ const char **auxiliaryClass;
+ const char **possSuperiors;
+ const char **mustContain;
+ const char **mayContain;
+ const char **possibleInferiors;
+
+ const char *defaultSecurityDescriptor;
+
+ uint32_t schemaFlagsEx;
+ struct ldb_val msDs_Schema_Extensions;
+
+ bool showInAdvancedViewOnly;
+ const char *adminDisplayName;
+ const char *adminDescription;
+ const char *classDisplayName;
+ bool defaultHidingValue;
+ bool isDefunct;
+ bool systemOnly;
+};
+
+struct dsdb_schema_oid_prefix {
+ uint32_t id;
+ const char *oid;
+ size_t oid_len;
+};
+
+struct dsdb_schema {
+ uint32_t num_prefixes;
+ struct dsdb_schema_oid_prefix *prefixes;
+
+ /*
+ * the last element of the prefix mapping table isn't a oid,
+ * it starts with 0xFF and has 21 bytes and is maybe a schema
+ * version number
+ *
+ * this is the content of the schemaInfo attribute of the
+ * Schema-Partition head object.
+ */
+ const char *schema_info;
+
+ struct dsdb_attribute *attributes;
+ struct dsdb_class *classes;
+
+ struct {
+ bool we_are_master;
+ struct ldb_dn *master_dn;
+ } fsmo;
+
+ struct smb_iconv_convenience *iconv_convenience;
+};
+
+enum dsdb_attr_list_query {
+ DSDB_SCHEMA_ALL_MAY,
+ DSDB_SCHEMA_ALL_MUST,
+ DSDB_SCHEMA_SYS_MAY,
+ DSDB_SCHEMA_SYS_MUST,
+ DSDB_SCHEMA_MAY,
+ DSDB_SCHEMA_MUST,
+ DSDB_SCHEMA_ALL
+};
+
+enum dsdb_schema_convert_target {
+ TARGET_OPENLDAP,
+ TARGET_FEDORA_DS,
+ TARGET_AD_SCHEMA_SUBENTRY
+};
+
+#include "dsdb/schema/proto.h"
+
+#endif /* _DSDB_SCHEMA_H */
diff --git a/source4/dsdb/schema/schema_description.c b/source4/dsdb/schema/schema_description.c
new file mode 100644
index 0000000000..0ff8a72bcd
--- /dev/null
+++ b/source4/dsdb/schema/schema_description.c
@@ -0,0 +1,414 @@
+/*
+ Unix SMB/CIFS mplementation.
+ Print schema info into string format
+
+ Copyright (C) Andrew Bartlett 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 <http://www.gnu.org/licenses/>.
+
+*/
+#include "includes.h"
+#include "dsdb/samdb/samdb.h"
+#include "librpc/ndr/libndr.h"
+
+#define IF_NULL_FAIL_RET(x) do { \
+ if (!x) { \
+ return NULL; \
+ } \
+ } while (0)
+
+
+char *schema_attribute_description(TALLOC_CTX *mem_ctx,
+ enum dsdb_schema_convert_target target,
+ const char *seperator,
+ const char *oid,
+ const char *name,
+ const char *equality,
+ const char *substring,
+ const char *syntax,
+ bool single_value, bool operational,
+ uint32_t *range_lower,
+ uint32_t *range_upper,
+ const char *property_guid,
+ const char *property_set_guid,
+ bool indexed, bool system_only)
+{
+ char *schema_entry = talloc_asprintf(mem_ctx,
+ "(%s%s%s", seperator, oid, seperator);
+
+ schema_entry = talloc_asprintf_append(schema_entry,
+ "NAME '%s'%s", name, seperator);
+ IF_NULL_FAIL_RET(schema_entry);
+
+ if (equality) {
+ schema_entry = talloc_asprintf_append(schema_entry,
+ "EQUALITY %s%s", equality, seperator);
+ IF_NULL_FAIL_RET(schema_entry);
+ }
+ if (substring) {
+ schema_entry = talloc_asprintf_append(schema_entry,
+ "SUBSTR %s%s", substring, seperator);
+ IF_NULL_FAIL_RET(schema_entry);
+ }
+
+ if (syntax) {
+ schema_entry = talloc_asprintf_append(schema_entry,
+ "SYNTAX %s%s", syntax, seperator);
+ IF_NULL_FAIL_RET(schema_entry);
+ }
+
+ if (single_value) {
+ schema_entry = talloc_asprintf_append(schema_entry,
+ "SINGLE-VALUE%s", seperator);
+ IF_NULL_FAIL_RET(schema_entry);
+ }
+
+ if (operational) {
+ schema_entry = talloc_asprintf_append(schema_entry,
+ "NO-USER-MODIFICATION%s", seperator);
+ IF_NULL_FAIL_RET(schema_entry);
+ }
+
+ if (range_lower) {
+ schema_entry = talloc_asprintf_append(schema_entry,
+ "RANGE-LOWER '%u'%s",
+ *range_lower, seperator);
+ IF_NULL_FAIL_RET(schema_entry);
+ }
+
+ if (range_upper) {
+ schema_entry = talloc_asprintf_append(schema_entry,
+ "RANGE-UPPER '%u'%s",
+ *range_upper, seperator);
+ IF_NULL_FAIL_RET(schema_entry);
+ }
+
+ if (property_guid) {
+ schema_entry = talloc_asprintf_append(schema_entry,
+ "PROPERTY-GUID '%s'%s",
+ property_guid, seperator);
+ IF_NULL_FAIL_RET(schema_entry);
+ }
+
+ if (property_set_guid) {
+ schema_entry = talloc_asprintf_append(schema_entry,
+ "PROPERTY-SET-GUID '%s'%s",
+ property_set_guid, seperator);
+ IF_NULL_FAIL_RET(schema_entry);
+ }
+
+ if (indexed) {
+ schema_entry = talloc_asprintf_append(schema_entry,
+ "INDEXED%s", seperator);
+ IF_NULL_FAIL_RET(schema_entry);
+ }
+
+ if (system_only) {
+ schema_entry = talloc_asprintf_append(schema_entry,
+ "SYSTEM-ONLY%s", seperator);
+ IF_NULL_FAIL_RET(schema_entry);
+ }
+
+ schema_entry = talloc_asprintf_append(schema_entry,
+ ")");
+ return schema_entry;
+}
+
+char *schema_attribute_to_description(TALLOC_CTX *mem_ctx, const struct dsdb_attribute *attribute)
+{
+ char *schema_description;
+ const char *syntax = attribute->syntax->ldap_oid;
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+ if (!tmp_ctx) {
+ return NULL;
+ }
+
+ schema_description
+ = schema_attribute_description(mem_ctx,
+ TARGET_AD_SCHEMA_SUBENTRY,
+ " ",
+ attribute->attributeID_oid,
+ attribute->lDAPDisplayName,
+ NULL, NULL, talloc_asprintf(tmp_ctx, "'%s'", syntax),
+ attribute->isSingleValued,
+ attribute->systemOnly,/* TODO: is this correct? */
+ NULL, NULL, NULL, NULL,
+ false, false);
+ talloc_free(tmp_ctx);
+ return schema_description;
+}
+
+char *schema_attribute_to_extendedInfo(TALLOC_CTX *mem_ctx, const struct dsdb_attribute *attribute)
+{
+ char *schema_description;
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+ if (!tmp_ctx) {
+ return NULL;
+ }
+
+ schema_description
+ = schema_attribute_description(mem_ctx,
+ TARGET_AD_SCHEMA_SUBENTRY,
+ " ",
+ attribute->attributeID_oid,
+ attribute->lDAPDisplayName,
+ NULL, NULL, NULL,
+ false, false,
+ attribute->rangeLower,
+ attribute->rangeUpper,
+ GUID_hexstring(tmp_ctx, &attribute->schemaIDGUID),
+ GUID_hexstring(tmp_ctx, &attribute->attributeSecurityGUID),
+ (attribute->searchFlags & SEARCH_FLAG_ATTINDEX),
+ attribute->systemOnly);
+ talloc_free(tmp_ctx);
+ return schema_description;
+}
+
+#define APPEND_ATTRS(attributes) \
+ do { \
+ int k; \
+ for (k=0; attributes && attributes[k]; k++) { \
+ const char *attr_name = attributes[k]; \
+ \
+ schema_entry = talloc_asprintf_append(schema_entry, \
+ "%s ", \
+ attr_name); \
+ IF_NULL_FAIL_RET(schema_entry); \
+ if (attributes[k+1]) { \
+ IF_NULL_FAIL_RET(schema_entry); \
+ if (target == TARGET_OPENLDAP && ((k+1)%5 == 0)) { \
+ schema_entry = talloc_asprintf_append(schema_entry, \
+ "$%s ", seperator); \
+ IF_NULL_FAIL_RET(schema_entry); \
+ } else { \
+ schema_entry = talloc_asprintf_append(schema_entry, \
+ "$ "); \
+ } \
+ } \
+ } \
+ } while (0)
+
+
+/* Print a schema class or dITContentRule as a string.
+ *
+ * To print a scheam class, specify objectClassCategory but not auxillary_classes
+ * To print a dITContentRule, specify auxillary_classes but set objectClassCategory == -1
+ *
+ */
+
+char *schema_class_description(TALLOC_CTX *mem_ctx,
+ enum dsdb_schema_convert_target target,
+ const char *seperator,
+ const char *oid,
+ const char *name,
+ const char **auxillary_classes,
+ const char *subClassOf,
+ int objectClassCategory,
+ const char **must,
+ const char **may,
+ const char *schemaHexGUID)
+{
+ char *schema_entry = talloc_asprintf(mem_ctx,
+ "(%s%s%s", seperator, oid, seperator);
+
+ IF_NULL_FAIL_RET(schema_entry);
+
+ schema_entry = talloc_asprintf_append(schema_entry,
+ "NAME '%s'%s", name, seperator);
+ IF_NULL_FAIL_RET(schema_entry);
+
+ if (auxillary_classes) {
+ schema_entry = talloc_asprintf_append(schema_entry,
+ "AUX ( ");
+ IF_NULL_FAIL_RET(schema_entry);
+
+ APPEND_ATTRS(auxillary_classes);
+
+ schema_entry = talloc_asprintf_append(schema_entry,
+ ")%s", seperator);
+ IF_NULL_FAIL_RET(schema_entry);
+ }
+
+ if (subClassOf && strcasecmp(subClassOf, name) != 0) {
+ schema_entry = talloc_asprintf_append(schema_entry,
+ "SUP %s%s", subClassOf, seperator);
+ IF_NULL_FAIL_RET(schema_entry);
+ }
+
+ switch (objectClassCategory) {
+ case -1:
+ break;
+ /* Dummy case for when used for printing ditContentRules */
+ case 0:
+ /*
+ * NOTE: this is an type 88 class
+ * e.g. 2.5.6.6 NAME 'person'
+ * but w2k3 gives STRUCTURAL here!
+ */
+ schema_entry = talloc_asprintf_append(schema_entry,
+ "STRUCTURAL%s", seperator);
+ IF_NULL_FAIL_RET(schema_entry);
+ break;
+ case 1:
+ schema_entry = talloc_asprintf_append(schema_entry,
+ "STRUCTURAL%s", seperator);
+ IF_NULL_FAIL_RET(schema_entry);
+ break;
+ case 2:
+ schema_entry = talloc_asprintf_append(schema_entry,
+ "ABSTRACT%s", seperator);
+ IF_NULL_FAIL_RET(schema_entry);
+ break;
+ case 3:
+ schema_entry = talloc_asprintf_append(schema_entry,
+ "AUXILIARY%s", seperator);
+ IF_NULL_FAIL_RET(schema_entry);
+ break;
+ }
+
+ if (must) {
+ schema_entry = talloc_asprintf_append(schema_entry,
+ "MUST (%s", target == TARGET_AD_SCHEMA_SUBENTRY ? "" : " ");
+ IF_NULL_FAIL_RET(schema_entry);
+
+ APPEND_ATTRS(must);
+
+ schema_entry = talloc_asprintf_append(schema_entry,
+ ")%s", seperator);
+ IF_NULL_FAIL_RET(schema_entry);
+ }
+
+ if (may) {
+ schema_entry = talloc_asprintf_append(schema_entry,
+ "MAY (%s", target == TARGET_AD_SCHEMA_SUBENTRY ? "" : " ");
+ IF_NULL_FAIL_RET(schema_entry);
+
+ APPEND_ATTRS(may);
+
+ schema_entry = talloc_asprintf_append(schema_entry,
+ ")%s", seperator);
+ IF_NULL_FAIL_RET(schema_entry);
+ }
+
+ if (schemaHexGUID) {
+ schema_entry = talloc_asprintf_append(schema_entry,
+ "CLASS-GUID '%s'%s",
+ schemaHexGUID, seperator);
+ IF_NULL_FAIL_RET(schema_entry);
+ }
+
+ schema_entry = talloc_asprintf_append(schema_entry,
+ ")");
+ return schema_entry;
+}
+
+char *schema_class_to_description(TALLOC_CTX *mem_ctx, const struct dsdb_class *sclass)
+{
+ char *schema_description;
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+ if (!tmp_ctx) {
+ return NULL;
+ }
+
+ schema_description
+ = schema_class_description(mem_ctx,
+ TARGET_AD_SCHEMA_SUBENTRY,
+ " ",
+ sclass->governsID_oid,
+ sclass->lDAPDisplayName,
+ NULL,
+ sclass->subClassOf,
+ sclass->objectClassCategory,
+ dsdb_attribute_list(tmp_ctx,
+ sclass, DSDB_SCHEMA_ALL_MUST),
+ dsdb_attribute_list(tmp_ctx,
+ sclass, DSDB_SCHEMA_ALL_MAY),
+ NULL);
+ talloc_free(tmp_ctx);
+ return schema_description;
+}
+
+char *schema_class_to_dITContentRule(TALLOC_CTX *mem_ctx, const struct dsdb_class *sclass,
+ const struct dsdb_schema *schema)
+{
+ int i;
+ char *schema_description;
+ const char **aux_class_list = NULL;
+ const char **attrs;
+ const char **must_attr_list = NULL;
+ const char **may_attr_list = NULL;
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+ const struct dsdb_class *aux_class;
+ if (!tmp_ctx) {
+ return NULL;
+ }
+
+ aux_class_list = merge_attr_list(tmp_ctx, aux_class_list, sclass->systemAuxiliaryClass);
+ aux_class_list = merge_attr_list(tmp_ctx, aux_class_list, sclass->auxiliaryClass);
+
+ for (i=0; aux_class_list && aux_class_list[i]; i++) {
+ aux_class = dsdb_class_by_lDAPDisplayName(schema, aux_class_list[i]);
+
+ attrs = dsdb_attribute_list(mem_ctx, aux_class, DSDB_SCHEMA_ALL_MUST);
+ must_attr_list = merge_attr_list(mem_ctx, must_attr_list, attrs);
+
+ attrs = dsdb_attribute_list(mem_ctx, aux_class, DSDB_SCHEMA_ALL_MAY);
+ may_attr_list = merge_attr_list(mem_ctx, may_attr_list, attrs);
+ }
+
+ schema_description
+ = schema_class_description(mem_ctx,
+ TARGET_AD_SCHEMA_SUBENTRY,
+ " ",
+ sclass->governsID_oid,
+ sclass->lDAPDisplayName,
+ (const char **)aux_class_list,
+ NULL, /* Must not specify a
+ * SUP (subclass) in
+ * ditContentRules
+ * per MS-ADTS
+ * 3.1.1.3.1.1.1 */
+ -1, must_attr_list, may_attr_list,
+ NULL);
+ talloc_free(tmp_ctx);
+ return schema_description;
+}
+
+char *schema_class_to_extendedInfo(TALLOC_CTX *mem_ctx, const struct dsdb_class *sclass)
+{
+ char *schema_description = NULL;
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+ if (!tmp_ctx) {
+ return NULL;
+ }
+
+ schema_description
+ = schema_class_description(mem_ctx,
+ TARGET_AD_SCHEMA_SUBENTRY,
+ " ",
+ sclass->governsID_oid,
+ sclass->lDAPDisplayName,
+ NULL,
+ NULL, /* Must not specify a
+ * SUP (subclass) in
+ * ditContentRules
+ * per MS-ADTS
+ * 3.1.1.3.1.1.1 */
+ -1, NULL, NULL,
+ GUID_hexstring(tmp_ctx, &sclass->schemaIDGUID));
+ talloc_free(tmp_ctx);
+ return schema_description;
+}
+
+
diff --git a/source4/dsdb/schema/schema_init.c b/source4/dsdb/schema/schema_init.c
new file mode 100644
index 0000000000..a67aecd1e8
--- /dev/null
+++ b/source4/dsdb/schema/schema_init.c
@@ -0,0 +1,1471 @@
+/*
+ Unix SMB/CIFS mplementation.
+ DSDB schema header
+
+ Copyright (C) Stefan Metzmacher <metze@samba.org> 2006-2007
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "includes.h"
+#include "dsdb/samdb/samdb.h"
+#include "lib/ldb/include/ldb_errors.h"
+#include "../lib/util/dlinklist.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "librpc/gen_ndr/ndr_drsuapi.h"
+#include "librpc/gen_ndr/ndr_drsblobs.h"
+#include "param/param.h"
+
+struct dsdb_schema *dsdb_new_schema(TALLOC_CTX *mem_ctx, struct smb_iconv_convenience *iconv_convenience)
+{
+ struct dsdb_schema *schema = talloc_zero(mem_ctx, struct dsdb_schema);
+ if (!schema) {
+ return NULL;
+ }
+
+ schema->iconv_convenience = iconv_convenience;
+ return schema;
+}
+
+
+WERROR dsdb_load_oid_mappings_drsuapi(struct dsdb_schema *schema, const struct drsuapi_DsReplicaOIDMapping_Ctr *ctr)
+{
+ uint32_t i,j;
+
+ schema->prefixes = talloc_array(schema, struct dsdb_schema_oid_prefix, ctr->num_mappings);
+ W_ERROR_HAVE_NO_MEMORY(schema->prefixes);
+
+ for (i=0, j=0; i < ctr->num_mappings; i++) {
+ if (ctr->mappings[i].oid.oid == NULL) {
+ return WERR_INVALID_PARAM;
+ }
+
+ if (strncasecmp(ctr->mappings[i].oid.oid, "ff", 2) == 0) {
+ if (ctr->mappings[i].id_prefix != 0) {
+ return WERR_INVALID_PARAM;
+ }
+
+ /* the magic value should be in the last array member */
+ if (i != (ctr->num_mappings - 1)) {
+ return WERR_INVALID_PARAM;
+ }
+
+ if (ctr->mappings[i].oid.__ndr_size != 21) {
+ return WERR_INVALID_PARAM;
+ }
+
+ schema->schema_info = talloc_strdup(schema, ctr->mappings[i].oid.oid);
+ W_ERROR_HAVE_NO_MEMORY(schema->schema_info);
+ } else {
+ /* the last array member should contain the magic value not a oid */
+ if (i == (ctr->num_mappings - 1)) {
+ return WERR_INVALID_PARAM;
+ }
+
+ schema->prefixes[j].id = ctr->mappings[i].id_prefix<<16;
+ schema->prefixes[j].oid = talloc_asprintf(schema->prefixes, "%s.",
+ ctr->mappings[i].oid.oid);
+ W_ERROR_HAVE_NO_MEMORY(schema->prefixes[j].oid);
+ schema->prefixes[j].oid_len = strlen(schema->prefixes[j].oid);
+ j++;
+ }
+ }
+
+ schema->num_prefixes = j;
+ return WERR_OK;
+}
+
+WERROR dsdb_load_oid_mappings_ldb(struct dsdb_schema *schema,
+ const struct ldb_val *prefixMap,
+ const struct ldb_val *schemaInfo)
+{
+ WERROR status;
+ enum ndr_err_code ndr_err;
+ struct prefixMapBlob pfm;
+ char *schema_info;
+
+ TALLOC_CTX *mem_ctx = talloc_new(schema);
+ W_ERROR_HAVE_NO_MEMORY(mem_ctx);
+
+ ndr_err = ndr_pull_struct_blob(prefixMap, mem_ctx, schema->iconv_convenience, &pfm, (ndr_pull_flags_fn_t)ndr_pull_prefixMapBlob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
+ talloc_free(mem_ctx);
+ return ntstatus_to_werror(nt_status);
+ }
+
+ if (pfm.version != PREFIX_MAP_VERSION_DSDB) {
+ talloc_free(mem_ctx);
+ return WERR_FOOBAR;
+ }
+
+ if (schemaInfo->length != 21 && schemaInfo->data[0] == 0xFF) {
+ talloc_free(mem_ctx);
+ return WERR_FOOBAR;
+ }
+
+ /* append the schema info as last element */
+ pfm.ctr.dsdb.num_mappings++;
+ pfm.ctr.dsdb.mappings = talloc_realloc(mem_ctx, pfm.ctr.dsdb.mappings,
+ struct drsuapi_DsReplicaOIDMapping,
+ pfm.ctr.dsdb.num_mappings);
+ W_ERROR_HAVE_NO_MEMORY(pfm.ctr.dsdb.mappings);
+
+ schema_info = data_blob_hex_string(pfm.ctr.dsdb.mappings, schemaInfo);
+ W_ERROR_HAVE_NO_MEMORY(schema_info);
+
+ pfm.ctr.dsdb.mappings[pfm.ctr.dsdb.num_mappings - 1].id_prefix = 0;
+ pfm.ctr.dsdb.mappings[pfm.ctr.dsdb.num_mappings - 1].oid.__ndr_size = schemaInfo->length;
+ pfm.ctr.dsdb.mappings[pfm.ctr.dsdb.num_mappings - 1].oid.oid = schema_info;
+
+ /* call the drsuapi version */
+ status = dsdb_load_oid_mappings_drsuapi(schema, &pfm.ctr.dsdb);
+ talloc_free(mem_ctx);
+
+ W_ERROR_NOT_OK_RETURN(status);
+
+ return WERR_OK;
+}
+
+WERROR dsdb_get_oid_mappings_drsuapi(const struct dsdb_schema *schema,
+ bool include_schema_info,
+ TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsReplicaOIDMapping_Ctr **_ctr)
+{
+ struct drsuapi_DsReplicaOIDMapping_Ctr *ctr;
+ uint32_t i;
+
+ ctr = talloc(mem_ctx, struct drsuapi_DsReplicaOIDMapping_Ctr);
+ W_ERROR_HAVE_NO_MEMORY(ctr);
+
+ ctr->num_mappings = schema->num_prefixes;
+ if (include_schema_info) ctr->num_mappings++;
+ ctr->mappings = talloc_array(schema, struct drsuapi_DsReplicaOIDMapping, ctr->num_mappings);
+ W_ERROR_HAVE_NO_MEMORY(ctr->mappings);
+
+ for (i=0; i < schema->num_prefixes; i++) {
+ ctr->mappings[i].id_prefix = schema->prefixes[i].id>>16;
+ ctr->mappings[i].oid.oid = talloc_strndup(ctr->mappings,
+ schema->prefixes[i].oid,
+ schema->prefixes[i].oid_len - 1);
+ W_ERROR_HAVE_NO_MEMORY(ctr->mappings[i].oid.oid);
+ }
+
+ if (include_schema_info) {
+ ctr->mappings[i].id_prefix = 0;
+ ctr->mappings[i].oid.oid = talloc_strdup(ctr->mappings,
+ schema->schema_info);
+ W_ERROR_HAVE_NO_MEMORY(ctr->mappings[i].oid.oid);
+ }
+
+ *_ctr = ctr;
+ return WERR_OK;
+}
+
+WERROR dsdb_get_oid_mappings_ldb(const struct dsdb_schema *schema,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_val *prefixMap,
+ struct ldb_val *schemaInfo)
+{
+ WERROR status;
+ enum ndr_err_code ndr_err;
+ struct drsuapi_DsReplicaOIDMapping_Ctr *ctr;
+ struct prefixMapBlob pfm;
+
+ status = dsdb_get_oid_mappings_drsuapi(schema, false, mem_ctx, &ctr);
+ W_ERROR_NOT_OK_RETURN(status);
+
+ pfm.version = PREFIX_MAP_VERSION_DSDB;
+ pfm.reserved = 0;
+ pfm.ctr.dsdb = *ctr;
+
+ ndr_err = ndr_push_struct_blob(prefixMap, mem_ctx, schema->iconv_convenience, &pfm, (ndr_push_flags_fn_t)ndr_push_prefixMapBlob);
+ talloc_free(ctr);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
+ return ntstatus_to_werror(nt_status);
+ }
+
+ *schemaInfo = strhex_to_data_blob(mem_ctx, schema->schema_info);
+ W_ERROR_HAVE_NO_MEMORY(schemaInfo->data);
+
+ return WERR_OK;
+}
+
+WERROR dsdb_verify_oid_mappings_drsuapi(const struct dsdb_schema *schema, const struct drsuapi_DsReplicaOIDMapping_Ctr *ctr)
+{
+ uint32_t i,j;
+
+ for (i=0; i < ctr->num_mappings; i++) {
+ if (ctr->mappings[i].oid.oid == NULL) {
+ return WERR_INVALID_PARAM;
+ }
+
+ if (strncasecmp(ctr->mappings[i].oid.oid, "ff", 2) == 0) {
+ if (ctr->mappings[i].id_prefix != 0) {
+ return WERR_INVALID_PARAM;
+ }
+
+ /* the magic value should be in the last array member */
+ if (i != (ctr->num_mappings - 1)) {
+ return WERR_INVALID_PARAM;
+ }
+
+ if (ctr->mappings[i].oid.__ndr_size != 21) {
+ return WERR_INVALID_PARAM;
+ }
+
+ if (strcasecmp(schema->schema_info, ctr->mappings[i].oid.oid) != 0) {
+ return WERR_DS_DRA_SCHEMA_MISMATCH;
+ }
+ } else {
+ /* the last array member should contain the magic value not a oid */
+ if (i == (ctr->num_mappings - 1)) {
+ return WERR_INVALID_PARAM;
+ }
+
+ for (j=0; j < schema->num_prefixes; j++) {
+ size_t oid_len;
+ if (schema->prefixes[j].id != (ctr->mappings[i].id_prefix<<16)) {
+ continue;
+ }
+
+ oid_len = strlen(ctr->mappings[i].oid.oid);
+
+ if (oid_len != (schema->prefixes[j].oid_len - 1)) {
+ return WERR_DS_DRA_SCHEMA_MISMATCH;
+ }
+
+ if (strncmp(ctr->mappings[i].oid.oid, schema->prefixes[j].oid, oid_len) != 0) {
+ return WERR_DS_DRA_SCHEMA_MISMATCH;
+ }
+
+ break;
+ }
+
+ if (j == schema->num_prefixes) {
+ return WERR_DS_DRA_SCHEMA_MISMATCH;
+ }
+ }
+ }
+
+ return WERR_OK;
+}
+
+WERROR dsdb_map_oid2int(const struct dsdb_schema *schema, const char *in, uint32_t *out)
+{
+ return dsdb_find_prefix_for_oid(schema->num_prefixes, schema->prefixes, in, out);
+}
+
+
+WERROR dsdb_map_int2oid(const struct dsdb_schema *schema, uint32_t in, TALLOC_CTX *mem_ctx, const char **out)
+{
+ uint32_t i;
+
+ for (i=0; i < schema->num_prefixes; i++) {
+ const char *val;
+ if (schema->prefixes[i].id != (in & 0xFFFF0000)) {
+ continue;
+ }
+
+ val = talloc_asprintf(mem_ctx, "%s%u",
+ schema->prefixes[i].oid,
+ in & 0xFFFF);
+ W_ERROR_HAVE_NO_MEMORY(val);
+
+ *out = val;
+ return WERR_OK;
+ }
+
+ return WERR_DS_NO_MSDS_INTID;
+}
+
+/*
+ * this function is called from within a ldb transaction from the schema_fsmo module
+ */
+WERROR dsdb_create_prefix_mapping(struct ldb_context *ldb, struct dsdb_schema *schema, const char *full_oid)
+{
+ WERROR status;
+ uint32_t num_prefixes;
+ struct dsdb_schema_oid_prefix *prefixes;
+ TALLOC_CTX *mem_ctx;
+ uint32_t out;
+
+ mem_ctx = talloc_new(ldb);
+ W_ERROR_HAVE_NO_MEMORY(mem_ctx);
+
+ /* Read prefixes from disk*/
+ status = dsdb_read_prefixes_from_ldb( mem_ctx, ldb, &num_prefixes, &prefixes );
+ if (!W_ERROR_IS_OK(status)) {
+ DEBUG(0,("dsdb_create_prefix_mapping: dsdb_read_prefixes_from_ldb: %s\n",
+ win_errstr(status)));
+ talloc_free(mem_ctx);
+ return status;
+ }
+
+ /* Check if there is a prefix for the oid in the prefixes array*/
+ status = dsdb_find_prefix_for_oid( num_prefixes, prefixes, full_oid, &out );
+ if (W_ERROR_IS_OK(status)) {
+ /* prefix found*/
+ talloc_free(mem_ctx);
+ return status;
+ } else if (!W_ERROR_EQUAL(WERR_DS_NO_MSDS_INTID, status)) {
+ /* error */
+ DEBUG(0,("dsdb_create_prefix_mapping: dsdb_find_prefix_for_oid: %s\n",
+ win_errstr(status)));
+ talloc_free(mem_ctx);
+ return status;
+ }
+
+ /* Create the new mapping for the prefix of full_oid */
+ status = dsdb_prefix_map_update(mem_ctx, &num_prefixes, &prefixes, full_oid);
+ if (!W_ERROR_IS_OK(status)) {
+ DEBUG(0,("dsdb_create_prefix_mapping: dsdb_prefix_map_update: %s\n",
+ win_errstr(status)));
+ talloc_free(mem_ctx);
+ return status;
+ }
+
+ /* Update prefixMap in ldb*/
+ status = dsdb_write_prefixes_to_ldb(mem_ctx, ldb, num_prefixes, prefixes);
+ if (!W_ERROR_IS_OK(status)) {
+ DEBUG(0,("dsdb_create_prefix_mapping: dsdb_write_prefixes_to_ldb: %s\n",
+ win_errstr(status)));
+ talloc_free(mem_ctx);
+ return status;
+ }
+
+ talloc_free(mem_ctx);
+ return status;
+}
+
+WERROR dsdb_prefix_map_update(TALLOC_CTX *mem_ctx, uint32_t *num_prefixes, struct dsdb_schema_oid_prefix **prefixes, const char *oid)
+{
+ uint32_t new_num_prefixes, index_new_prefix, new_entry_id;
+ const char* lastDotOffset;
+ size_t size;
+
+ new_num_prefixes = *num_prefixes + 1;
+ index_new_prefix = *num_prefixes;
+
+ /*
+ * this is the algorithm we use to create new mappings for now
+ *
+ * TODO: find what algorithm windows use
+ */
+ new_entry_id = (*num_prefixes)<<16;
+
+ /* Extract the prefix from the oid*/
+ lastDotOffset = strrchr(oid, '.');
+ if (lastDotOffset == NULL) {
+ DEBUG(0,("dsdb_prefix_map_update: failed to find the last dot\n"));
+ return WERR_NOT_FOUND;
+ }
+
+ /* Calculate the size of the remainig string that should be the prefix of it */
+ size = strlen(oid) - strlen(lastDotOffset);
+ if (size <= 0) {
+ DEBUG(0,("dsdb_prefix_map_update: size of the remaining string invalid\n"));
+ return WERR_FOOBAR;
+ }
+ /* Add one because we need to copy the dot */
+ size += 1;
+
+ /* Create a spot in the prefixMap for one more prefix*/
+ (*prefixes) = talloc_realloc(mem_ctx, *prefixes, struct dsdb_schema_oid_prefix, new_num_prefixes);
+ W_ERROR_HAVE_NO_MEMORY(*prefixes);
+
+ /* Add the new prefix entry*/
+ (*prefixes)[index_new_prefix].id = new_entry_id;
+ (*prefixes)[index_new_prefix].oid = talloc_strndup(mem_ctx, oid, size);
+ (*prefixes)[index_new_prefix].oid_len = strlen((*prefixes)[index_new_prefix].oid);
+
+ /* Increase num_prefixes because new prefix has been added */
+ ++(*num_prefixes);
+
+ return WERR_OK;
+}
+
+WERROR dsdb_find_prefix_for_oid(uint32_t num_prefixes, const struct dsdb_schema_oid_prefix *prefixes, const char *in, uint32_t *out)
+{
+ uint32_t i;
+
+ for (i=0; i < num_prefixes; i++) {
+ const char *val_str;
+ char *end_str;
+ unsigned val;
+
+ if (strncmp(prefixes[i].oid, in, prefixes[i].oid_len) != 0) {
+ continue;
+ }
+
+ val_str = in + prefixes[i].oid_len;
+ end_str = NULL;
+ errno = 0;
+
+ if (val_str[0] == '\0') {
+ return WERR_INVALID_PARAM;
+ }
+
+ /* two '.' chars are invalid */
+ if (val_str[0] == '.') {
+ return WERR_INVALID_PARAM;
+ }
+
+ val = strtoul(val_str, &end_str, 10);
+ if (end_str[0] == '.' && end_str[1] != '\0') {
+ /*
+ * if it's a '.' and not the last char
+ * then maybe an other mapping apply
+ */
+ continue;
+ } else if (end_str[0] != '\0') {
+ return WERR_INVALID_PARAM;
+ } else if (val > 0xFFFF) {
+ return WERR_INVALID_PARAM;
+ }
+
+ *out = prefixes[i].id | val;
+ return WERR_OK;
+ }
+
+ return WERR_DS_NO_MSDS_INTID;
+}
+
+WERROR dsdb_write_prefixes_to_ldb(TALLOC_CTX *mem_ctx, struct ldb_context *ldb,
+ uint32_t num_prefixes,
+ const struct dsdb_schema_oid_prefix *prefixes)
+{
+ struct ldb_message msg;
+ struct ldb_dn *schema_dn;
+ struct ldb_message_element el;
+ struct prefixMapBlob pm;
+ struct ldb_val ndr_blob;
+ enum ndr_err_code ndr_err;
+ uint32_t i;
+ int ret;
+
+ schema_dn = samdb_schema_dn(ldb);
+ if (!schema_dn) {
+ DEBUG(0,("dsdb_write_prefixes_to_ldb: no schema dn present\n"));
+ return WERR_FOOBAR;
+ }
+
+ pm.version = PREFIX_MAP_VERSION_DSDB;
+ pm.ctr.dsdb.num_mappings = num_prefixes;
+ pm.ctr.dsdb.mappings = talloc_array(mem_ctx,
+ struct drsuapi_DsReplicaOIDMapping,
+ pm.ctr.dsdb.num_mappings);
+ if (!pm.ctr.dsdb.mappings) {
+ return WERR_NOMEM;
+ }
+
+ for (i=0; i < num_prefixes; i++) {
+ pm.ctr.dsdb.mappings[i].id_prefix = prefixes[i].id>>16;
+ pm.ctr.dsdb.mappings[i].oid.oid = talloc_strdup(pm.ctr.dsdb.mappings, prefixes[i].oid);
+ }
+
+ ndr_err = ndr_push_struct_blob(&ndr_blob, ldb,
+ lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
+ &pm,
+ (ndr_push_flags_fn_t)ndr_push_prefixMapBlob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return WERR_FOOBAR;
+ }
+
+ el.num_values = 1;
+ el.values = &ndr_blob;
+ el.flags = LDB_FLAG_MOD_REPLACE;
+ el.name = talloc_strdup(mem_ctx, "prefixMap");
+
+ msg.dn = ldb_dn_copy(mem_ctx, schema_dn);
+ msg.num_elements = 1;
+ msg.elements = &el;
+
+ ret = ldb_modify( ldb, &msg );
+ if (ret != 0) {
+ DEBUG(0,("dsdb_write_prefixes_to_ldb: ldb_modify failed\n"));
+ return WERR_FOOBAR;
+ }
+
+ return WERR_OK;
+}
+
+WERROR dsdb_read_prefixes_from_ldb(TALLOC_CTX *mem_ctx, struct ldb_context *ldb, uint32_t* num_prefixes, struct dsdb_schema_oid_prefix **prefixes)
+{
+ struct prefixMapBlob *blob;
+ enum ndr_err_code ndr_err;
+ uint32_t i;
+ const struct ldb_val *prefix_val;
+ struct ldb_dn *schema_dn;
+ struct ldb_result *schema_res;
+ int ret;
+ static const char *schema_attrs[] = {
+ "prefixMap",
+ NULL
+ };
+
+ schema_dn = samdb_schema_dn(ldb);
+ if (!schema_dn) {
+ DEBUG(0,("dsdb_read_prefixes_from_ldb: no schema dn present\n"));
+ return WERR_FOOBAR;
+ }
+
+ ret = ldb_search(ldb, mem_ctx, &schema_res, schema_dn, LDB_SCOPE_BASE, schema_attrs, NULL);
+ if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+ DEBUG(0,("dsdb_read_prefixes_from_ldb: no prefix map present\n"));
+ talloc_free(schema_res);
+ return WERR_FOOBAR;
+ } else if (ret != LDB_SUCCESS) {
+ DEBUG(0,("dsdb_read_prefixes_from_ldb: failed to search the schema head\n"));
+ talloc_free(schema_res);
+ return WERR_FOOBAR;
+ }
+
+ prefix_val = ldb_msg_find_ldb_val(schema_res->msgs[0], "prefixMap");
+ if (!prefix_val) {
+ DEBUG(0,("dsdb_read_prefixes_from_ldb: no prefixMap attribute found\n"));
+ talloc_free(schema_res);
+ return WERR_FOOBAR;
+ }
+
+ blob = talloc(mem_ctx, struct prefixMapBlob);
+ W_ERROR_HAVE_NO_MEMORY(blob);
+
+ ndr_err = ndr_pull_struct_blob(prefix_val, blob,
+ lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
+ blob,
+ (ndr_pull_flags_fn_t)ndr_pull_prefixMapBlob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DEBUG(0,("dsdb_read_prefixes_from_ldb: ndr_pull_struct_blob failed\n"));
+ talloc_free(blob);
+ talloc_free(schema_res);
+ return WERR_FOOBAR;
+ }
+
+ talloc_free(schema_res);
+
+ if (blob->version != PREFIX_MAP_VERSION_DSDB) {
+ DEBUG(0,("dsdb_read_prefixes_from_ldb: blob->version incorect\n"));
+ talloc_free(blob);
+ return WERR_FOOBAR;
+ }
+
+ *num_prefixes = blob->ctr.dsdb.num_mappings;
+ *prefixes = talloc_array(mem_ctx, struct dsdb_schema_oid_prefix, *num_prefixes);
+ if(!(*prefixes)) {
+ talloc_free(blob);
+ return WERR_NOMEM;
+ }
+ for (i=0; i < blob->ctr.dsdb.num_mappings; i++) {
+ char *oid;
+ (*prefixes)[i].id = blob->ctr.dsdb.mappings[i].id_prefix<<16;
+ oid = talloc_strdup(mem_ctx, blob->ctr.dsdb.mappings[i].oid.oid);
+ (*prefixes)[i].oid = talloc_asprintf_append(oid, ".");
+ (*prefixes)[i].oid_len = strlen(blob->ctr.dsdb.mappings[i].oid.oid);
+ }
+
+ talloc_free(blob);
+ return WERR_OK;
+}
+
+#define GET_STRING_LDB(msg, attr, mem_ctx, p, elem, strict) do { \
+ (p)->elem = samdb_result_string(msg, attr, NULL);\
+ if (strict && (p)->elem == NULL) { \
+ d_printf("%s: %s == NULL\n", __location__, attr); \
+ return WERR_INVALID_PARAM; \
+ } \
+ talloc_steal(mem_ctx, (p)->elem); \
+} while (0)
+
+#define GET_STRING_LIST_LDB(msg, attr, mem_ctx, p, elem, strict) do { \
+ int get_string_list_counter; \
+ struct ldb_message_element *get_string_list_el = ldb_msg_find_element(msg, attr); \
+ if (get_string_list_el == NULL) { \
+ if (strict) { \
+ d_printf("%s: %s == NULL\n", __location__, attr); \
+ return WERR_INVALID_PARAM; \
+ } else { \
+ (p)->elem = NULL; \
+ break; \
+ } \
+ } \
+ (p)->elem = talloc_array(mem_ctx, const char *, get_string_list_el->num_values + 1); \
+ for (get_string_list_counter=0; \
+ get_string_list_counter < get_string_list_el->num_values; \
+ get_string_list_counter++) { \
+ (p)->elem[get_string_list_counter] = talloc_strndup((p)->elem, \
+ (const char *)get_string_list_el->values[get_string_list_counter].data, \
+ get_string_list_el->values[get_string_list_counter].length); \
+ if (!(p)->elem[get_string_list_counter]) { \
+ d_printf("%s: talloc_strndup failed for %s\n", __location__, attr); \
+ return WERR_NOMEM; \
+ } \
+ (p)->elem[get_string_list_counter+1] = NULL; \
+ } \
+ talloc_steal(mem_ctx, (p)->elem); \
+} while (0)
+
+#define GET_BOOL_LDB(msg, attr, p, elem, strict) do { \
+ const char *str; \
+ str = samdb_result_string(msg, attr, NULL);\
+ if (str == NULL) { \
+ if (strict) { \
+ d_printf("%s: %s == NULL\n", __location__, attr); \
+ return WERR_INVALID_PARAM; \
+ } else { \
+ (p)->elem = false; \
+ } \
+ } else if (strcasecmp("TRUE", str) == 0) { \
+ (p)->elem = true; \
+ } else if (strcasecmp("FALSE", str) == 0) { \
+ (p)->elem = false; \
+ } else { \
+ d_printf("%s: %s == %s\n", __location__, attr, str); \
+ return WERR_INVALID_PARAM; \
+ } \
+} while (0)
+
+#define GET_UINT32_LDB(msg, attr, p, elem) do { \
+ (p)->elem = samdb_result_uint(msg, attr, 0);\
+} while (0)
+
+#define GET_UINT32_PTR_LDB(msg, attr, p, elem) do { \
+ uint64_t _v = samdb_result_uint64(msg, attr, UINT64_MAX);\
+ if (_v == UINT64_MAX) { \
+ (p)->elem = NULL; \
+ } else if (_v > UINT32_MAX) { \
+ d_printf("%s: %s == 0x%llX\n", __location__, \
+ attr, (unsigned long long)_v); \
+ return WERR_INVALID_PARAM; \
+ } else { \
+ (p)->elem = talloc(mem_ctx, uint32_t); \
+ if (!(p)->elem) { \
+ d_printf("%s: talloc failed for %s\n", __location__, attr); \
+ return WERR_NOMEM; \
+ } \
+ *(p)->elem = (uint32_t)_v; \
+ } \
+} while (0)
+
+#define GET_GUID_LDB(msg, attr, p, elem) do { \
+ (p)->elem = samdb_result_guid(msg, attr);\
+} while (0)
+
+#define GET_BLOB_LDB(msg, attr, mem_ctx, p, elem) do { \
+ const struct ldb_val *_val;\
+ _val = ldb_msg_find_ldb_val(msg, attr);\
+ if (_val) {\
+ (p)->elem = *_val;\
+ talloc_steal(mem_ctx, (p)->elem.data);\
+ } else {\
+ ZERO_STRUCT((p)->elem);\
+ }\
+} while (0)
+
+WERROR dsdb_attribute_from_ldb(const struct dsdb_schema *schema,
+ struct ldb_message *msg,
+ TALLOC_CTX *mem_ctx,
+ struct dsdb_attribute *attr)
+{
+ WERROR status;
+
+ GET_STRING_LDB(msg, "cn", mem_ctx, attr, cn, false);
+ GET_STRING_LDB(msg, "lDAPDisplayName", mem_ctx, attr, lDAPDisplayName, true);
+ GET_STRING_LDB(msg, "attributeID", mem_ctx, attr, attributeID_oid, true);
+ if (schema->num_prefixes == 0) {
+ /* set an invalid value */
+ attr->attributeID_id = 0xFFFFFFFF;
+ } else {
+ status = dsdb_map_oid2int(schema, attr->attributeID_oid, &attr->attributeID_id);
+ if (!W_ERROR_IS_OK(status)) {
+ DEBUG(0,("%s: '%s': unable to map attributeID %s: %s\n",
+ __location__, attr->lDAPDisplayName, attr->attributeID_oid,
+ win_errstr(status)));
+ return status;
+ }
+ }
+ GET_GUID_LDB(msg, "schemaIDGUID", attr, schemaIDGUID);
+ GET_UINT32_LDB(msg, "mAPIID", attr, mAPIID);
+
+ GET_GUID_LDB(msg, "attributeSecurityGUID", attr, attributeSecurityGUID);
+
+ GET_UINT32_LDB(msg, "searchFlags", attr, searchFlags);
+ GET_UINT32_LDB(msg, "systemFlags", attr, systemFlags);
+ GET_BOOL_LDB(msg, "isMemberOfPartialAttributeSet", attr, isMemberOfPartialAttributeSet, false);
+ GET_UINT32_LDB(msg, "linkID", attr, linkID);
+
+ GET_STRING_LDB(msg, "attributeSyntax", mem_ctx, attr, attributeSyntax_oid, true);
+ if (schema->num_prefixes == 0) {
+ /* set an invalid value */
+ attr->attributeSyntax_id = 0xFFFFFFFF;
+ } else {
+ status = dsdb_map_oid2int(schema, attr->attributeSyntax_oid, &attr->attributeSyntax_id);
+ if (!W_ERROR_IS_OK(status)) {
+ DEBUG(0,("%s: '%s': unable to map attributeSyntax_ %s: %s\n",
+ __location__, attr->lDAPDisplayName, attr->attributeSyntax_oid,
+ win_errstr(status)));
+ return status;
+ }
+ }
+ GET_UINT32_LDB(msg, "oMSyntax", attr, oMSyntax);
+ GET_BLOB_LDB(msg, "oMObjectClass", mem_ctx, attr, oMObjectClass);
+
+ GET_BOOL_LDB(msg, "isSingleValued", attr, isSingleValued, true);
+ GET_UINT32_PTR_LDB(msg, "rangeLower", attr, rangeLower);
+ GET_UINT32_PTR_LDB(msg, "rangeUpper", attr, rangeUpper);
+ GET_BOOL_LDB(msg, "extendedCharsAllowed", attr, extendedCharsAllowed, false);
+
+ GET_UINT32_LDB(msg, "schemaFlagsEx", attr, schemaFlagsEx);
+ GET_BLOB_LDB(msg, "msDs-Schema-Extensions", mem_ctx, attr, msDs_Schema_Extensions);
+
+ GET_BOOL_LDB(msg, "showInAdvancedViewOnly", attr, showInAdvancedViewOnly, false);
+ GET_STRING_LDB(msg, "adminDisplayName", mem_ctx, attr, adminDisplayName, false);
+ GET_STRING_LDB(msg, "adminDescription", mem_ctx, attr, adminDescription, false);
+ GET_STRING_LDB(msg, "classDisplayName", mem_ctx, attr, classDisplayName, false);
+ GET_BOOL_LDB(msg, "isEphemeral", attr, isEphemeral, false);
+ GET_BOOL_LDB(msg, "isDefunct", attr, isDefunct, false);
+ GET_BOOL_LDB(msg, "systemOnly", attr, systemOnly, false);
+
+ attr->syntax = dsdb_syntax_for_attribute(attr);
+ if (!attr->syntax) {
+ return WERR_DS_ATT_SCHEMA_REQ_SYNTAX;
+ }
+
+ return WERR_OK;
+}
+
+WERROR dsdb_class_from_ldb(const struct dsdb_schema *schema,
+ struct ldb_message *msg,
+ TALLOC_CTX *mem_ctx,
+ struct dsdb_class *obj)
+{
+ WERROR status;
+
+ GET_STRING_LDB(msg, "cn", mem_ctx, obj, cn, false);
+ GET_STRING_LDB(msg, "lDAPDisplayName", mem_ctx, obj, lDAPDisplayName, true);
+ GET_STRING_LDB(msg, "governsID", mem_ctx, obj, governsID_oid, true);
+ if (schema->num_prefixes == 0) {
+ /* set an invalid value */
+ obj->governsID_id = 0xFFFFFFFF;
+ } else {
+ status = dsdb_map_oid2int(schema, obj->governsID_oid, &obj->governsID_id);
+ if (!W_ERROR_IS_OK(status)) {
+ DEBUG(0,("%s: '%s': unable to map governsID %s: %s\n",
+ __location__, obj->lDAPDisplayName, obj->governsID_oid,
+ win_errstr(status)));
+ return status;
+ }
+ }
+ GET_GUID_LDB(msg, "schemaIDGUID", obj, schemaIDGUID);
+
+ GET_UINT32_LDB(msg, "objectClassCategory", obj, objectClassCategory);
+ GET_STRING_LDB(msg, "rDNAttID", mem_ctx, obj, rDNAttID, false);
+ GET_STRING_LDB(msg, "defaultObjectCategory", mem_ctx, obj, defaultObjectCategory, true);
+
+ GET_STRING_LDB(msg, "subClassOf", mem_ctx, obj, subClassOf, true);
+
+ GET_STRING_LIST_LDB(msg, "systemAuxiliaryClass", mem_ctx, obj, systemAuxiliaryClass, false);
+ GET_STRING_LIST_LDB(msg, "auxiliaryClass", mem_ctx, obj, auxiliaryClass, false);
+
+ GET_STRING_LIST_LDB(msg, "systemMustContain", mem_ctx, obj, systemMustContain, false);
+ GET_STRING_LIST_LDB(msg, "systemMayContain", mem_ctx, obj, systemMayContain, false);
+ GET_STRING_LIST_LDB(msg, "mustContain", mem_ctx, obj, mustContain, false);
+ GET_STRING_LIST_LDB(msg, "mayContain", mem_ctx, obj, mayContain, false);
+
+ GET_STRING_LIST_LDB(msg, "systemPossSuperiors", mem_ctx, obj, systemPossSuperiors, false);
+ GET_STRING_LIST_LDB(msg, "possSuperiors", mem_ctx, obj, possSuperiors, false);
+ GET_STRING_LIST_LDB(msg, "possibleInferiors", mem_ctx, obj, possibleInferiors, false);
+
+ GET_STRING_LDB(msg, "defaultSecurityDescriptor", mem_ctx, obj, defaultSecurityDescriptor, false);
+
+ GET_UINT32_LDB(msg, "schemaFlagsEx", obj, schemaFlagsEx);
+ GET_BLOB_LDB(msg, "msDs-Schema-Extensions", mem_ctx, obj, msDs_Schema_Extensions);
+
+ GET_BOOL_LDB(msg, "showInAdvancedViewOnly", obj, showInAdvancedViewOnly, false);
+ GET_STRING_LDB(msg, "adminDisplayName", mem_ctx, obj, adminDisplayName, false);
+ GET_STRING_LDB(msg, "adminDescription", mem_ctx, obj, adminDescription, false);
+ GET_STRING_LDB(msg, "classDisplayName", mem_ctx, obj, classDisplayName, false);
+ GET_BOOL_LDB(msg, "defaultHidingValue", obj, defaultHidingValue, false);
+ GET_BOOL_LDB(msg, "isDefunct", obj, isDefunct, false);
+ GET_BOOL_LDB(msg, "systemOnly", obj, systemOnly, false);
+
+ return WERR_OK;
+}
+
+#define dsdb_oom(error_string, mem_ctx) *error_string = talloc_asprintf(mem_ctx, "dsdb out of memory at %s:%d\n", __FILE__, __LINE__)
+
+int dsdb_schema_from_ldb_results(TALLOC_CTX *mem_ctx, struct ldb_context *ldb,
+ struct smb_iconv_convenience *iconv_convenience,
+ struct ldb_result *schema_res,
+ struct ldb_result *attrs_res, struct ldb_result *objectclass_res,
+ struct dsdb_schema **schema_out,
+ char **error_string)
+{
+ WERROR status;
+ uint32_t i;
+ const struct ldb_val *prefix_val;
+ const struct ldb_val *info_val;
+ struct ldb_val info_val_default;
+ struct dsdb_schema *schema;
+
+ schema = dsdb_new_schema(mem_ctx, iconv_convenience);
+ if (!schema) {
+ dsdb_oom(error_string, mem_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ prefix_val = ldb_msg_find_ldb_val(schema_res->msgs[0], "prefixMap");
+ if (!prefix_val) {
+ *error_string = talloc_asprintf(mem_ctx,
+ "schema_fsmo_init: no prefixMap attribute found");
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+ info_val = ldb_msg_find_ldb_val(schema_res->msgs[0], "schemaInfo");
+ if (!info_val) {
+ info_val_default = strhex_to_data_blob(mem_ctx, "FF0000000000000000000000000000000000000000");
+ if (!info_val_default.data) {
+ dsdb_oom(error_string, mem_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ info_val = &info_val_default;
+ }
+
+ status = dsdb_load_oid_mappings_ldb(schema, prefix_val, info_val);
+ if (!W_ERROR_IS_OK(status)) {
+ *error_string = talloc_asprintf(mem_ctx,
+ "schema_fsmo_init: failed to load oid mappings: %s",
+ win_errstr(status));
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+
+ for (i=0; i < attrs_res->count; i++) {
+ struct dsdb_attribute *sa;
+
+ sa = talloc_zero(schema, struct dsdb_attribute);
+ if (!sa) {
+ dsdb_oom(error_string, mem_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ status = dsdb_attribute_from_ldb(schema, attrs_res->msgs[i], sa, sa);
+ if (!W_ERROR_IS_OK(status)) {
+ *error_string = talloc_asprintf(mem_ctx,
+ "schema_fsmo_init: failed to load attribute definition: %s:%s",
+ ldb_dn_get_linearized(attrs_res->msgs[i]->dn),
+ win_errstr(status));
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+
+ DLIST_ADD_END(schema->attributes, sa, struct dsdb_attribute *);
+ }
+
+ for (i=0; i < objectclass_res->count; i++) {
+ struct dsdb_class *sc;
+
+ sc = talloc_zero(schema, struct dsdb_class);
+ if (!sc) {
+ dsdb_oom(error_string, mem_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ status = dsdb_class_from_ldb(schema, objectclass_res->msgs[i], sc, sc);
+ if (!W_ERROR_IS_OK(status)) {
+ *error_string = talloc_asprintf(mem_ctx,
+ "schema_fsmo_init: failed to load class definition: %s:%s",
+ ldb_dn_get_linearized(objectclass_res->msgs[i]->dn),
+ win_errstr(status));
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+
+ DLIST_ADD_END(schema->classes, sc, struct dsdb_class *);
+ }
+
+ schema->fsmo.master_dn = ldb_msg_find_attr_as_dn(ldb, schema, schema_res->msgs[0], "fSMORoleOwner");
+ if (ldb_dn_compare(samdb_ntds_settings_dn(ldb), schema->fsmo.master_dn) == 0) {
+ schema->fsmo.we_are_master = true;
+ } else {
+ schema->fsmo.we_are_master = false;
+ }
+
+ DEBUG(5, ("schema_fsmo_init: we are master: %s\n",
+ (schema->fsmo.we_are_master?"yes":"no")));
+
+ *schema_out = schema;
+ return LDB_SUCCESS;
+}
+
+/* This recursive load of the objectClasses presumes that they
+ * everything is in a strict subClassOf hirarchy.
+ *
+ * We load this in order so we produce certain outputs (such as the
+ * exported schema for openldap, and sorted objectClass attribute) 'in
+ * order' */
+
+static int fetch_oc_recursive(struct ldb_context *ldb, struct ldb_dn *schemadn,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_result *search_from,
+ struct ldb_result *res_list)
+{
+ int i;
+ int ret = 0;
+ for (i=0; i < search_from->count; i++) {
+ struct ldb_result *res;
+ const char *name = ldb_msg_find_attr_as_string(search_from->msgs[i],
+ "lDAPDisplayname", NULL);
+
+ ret = ldb_search(ldb, mem_ctx, &res,
+ schemadn, LDB_SCOPE_SUBTREE, NULL,
+ "(&(&(objectClass=classSchema)(subClassOf=%s))(!(lDAPDisplayName=%s)))",
+ name, name);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ res_list->msgs = talloc_realloc(res_list, res_list->msgs,
+ struct ldb_message *, res_list->count + 2);
+ if (!res_list->msgs) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ res_list->msgs[res_list->count] = talloc_move(res_list,
+ &search_from->msgs[i]);
+ res_list->count++;
+ res_list->msgs[res_list->count] = NULL;
+
+ if (res->count > 0) {
+ ret = fetch_oc_recursive(ldb, schemadn, mem_ctx, res, res_list);
+ }
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+ return ret;
+}
+
+static int fetch_objectclass_schema(struct ldb_context *ldb, struct ldb_dn *schemadn,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_result **objectclasses_res,
+ char **error_string)
+{
+ TALLOC_CTX *local_ctx = talloc_new(mem_ctx);
+ struct ldb_result *top_res, *ret_res;
+ int ret;
+ if (!local_ctx) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* Download 'top' */
+ ret = ldb_search(ldb, local_ctx, &top_res,
+ schemadn, LDB_SCOPE_SUBTREE, NULL,
+ "(&(objectClass=classSchema)(lDAPDisplayName=top))");
+ if (ret != LDB_SUCCESS) {
+ *error_string = talloc_asprintf(mem_ctx,
+ "dsdb_schema: failed to search for top classSchema object: %s",
+ ldb_errstring(ldb));
+ return ret;
+ }
+
+ if (top_res->count != 1) {
+ *error_string = talloc_asprintf(mem_ctx,
+ "dsdb_schema: failed to find top classSchema object");
+ return LDB_ERR_NO_SUCH_OBJECT;
+ }
+
+ ret_res = talloc_zero(local_ctx, struct ldb_result);
+ if (!ret_res) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = fetch_oc_recursive(ldb, schemadn, local_ctx, top_res, ret_res);
+
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ *objectclasses_res = talloc_move(mem_ctx, &ret_res);
+ return ret;
+}
+
+int dsdb_schema_from_schema_dn(TALLOC_CTX *mem_ctx, struct ldb_context *ldb,
+ struct smb_iconv_convenience *iconv_convenience,
+ struct ldb_dn *schema_dn,
+ struct dsdb_schema **schema,
+ char **error_string_out)
+{
+ TALLOC_CTX *tmp_ctx;
+ char *error_string;
+ int ret;
+
+ struct ldb_result *schema_res;
+ struct ldb_result *a_res;
+ struct ldb_result *c_res;
+ static const char *schema_attrs[] = {
+ "prefixMap",
+ "schemaInfo",
+ "fSMORoleOwner",
+ NULL
+ };
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (!tmp_ctx) {
+ dsdb_oom(error_string_out, mem_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /*
+ * setup the prefix mappings and schema info
+ */
+ ret = ldb_search(ldb, tmp_ctx, &schema_res,
+ schema_dn, LDB_SCOPE_BASE, schema_attrs, NULL);
+ if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+ talloc_free(tmp_ctx);
+ return ret;
+ } else if (ret != LDB_SUCCESS) {
+ *error_string_out = talloc_asprintf(mem_ctx,
+ "dsdb_schema: failed to search the schema head: %s",
+ ldb_errstring(ldb));
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+ if (schema_res->count != 1) {
+ *error_string_out = talloc_asprintf(mem_ctx,
+ "dsdb_schema: [%u] schema heads found on a base search",
+ schema_res->count);
+ talloc_free(tmp_ctx);
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+
+ /*
+ * load the attribute definitions
+ */
+ ret = ldb_search(ldb, tmp_ctx, &a_res,
+ schema_dn, LDB_SCOPE_ONELEVEL, NULL,
+ "(objectClass=attributeSchema)");
+ if (ret != LDB_SUCCESS) {
+ *error_string_out = talloc_asprintf(mem_ctx,
+ "dsdb_schema: failed to search attributeSchema objects: %s",
+ ldb_errstring(ldb));
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ /*
+ * load the objectClass definitions
+ */
+ ret = fetch_objectclass_schema(ldb, schema_dn, tmp_ctx, &c_res, &error_string);
+ if (ret != LDB_SUCCESS) {
+ *error_string_out = talloc_asprintf(mem_ctx,
+ "Failed to fetch objectClass schema elements: %s", error_string);
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ ret = dsdb_schema_from_ldb_results(tmp_ctx, ldb,
+ lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
+ schema_res, a_res, c_res, schema, &error_string);
+ if (ret != LDB_SUCCESS) {
+ *error_string_out = talloc_asprintf(mem_ctx,
+ "dsdb_schema load failed: %s",
+ error_string);
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+ talloc_steal(mem_ctx, *schema);
+ talloc_free(tmp_ctx);
+
+ return LDB_SUCCESS;
+}
+
+
+static const struct {
+ const char *name;
+ const char *oid;
+} name_mappings[] = {
+ { "cn", "2.5.4.3" },
+ { "name", "1.2.840.113556.1.4.1" },
+ { "lDAPDisplayName", "1.2.840.113556.1.2.460" },
+ { "attributeID", "1.2.840.113556.1.2.30" },
+ { "schemaIDGUID", "1.2.840.113556.1.4.148" },
+ { "mAPIID", "1.2.840.113556.1.2.49" },
+ { "attributeSecurityGUID", "1.2.840.113556.1.4.149" },
+ { "searchFlags", "1.2.840.113556.1.2.334" },
+ { "systemFlags", "1.2.840.113556.1.4.375" },
+ { "isMemberOfPartialAttributeSet", "1.2.840.113556.1.4.639" },
+ { "linkID", "1.2.840.113556.1.2.50" },
+ { "attributeSyntax", "1.2.840.113556.1.2.32" },
+ { "oMSyntax", "1.2.840.113556.1.2.231" },
+ { "oMObjectClass", "1.2.840.113556.1.2.218" },
+ { "isSingleValued", "1.2.840.113556.1.2.33" },
+ { "rangeLower", "1.2.840.113556.1.2.34" },
+ { "rangeUpper", "1.2.840.113556.1.2.35" },
+ { "extendedCharsAllowed", "1.2.840.113556.1.2.380" },
+ { "schemaFlagsEx", "1.2.840.113556.1.4.120" },
+ { "msDs-Schema-Extensions", "1.2.840.113556.1.4.1440" },
+ { "showInAdvancedViewOnly", "1.2.840.113556.1.2.169" },
+ { "adminDisplayName", "1.2.840.113556.1.2.194" },
+ { "adminDescription", "1.2.840.113556.1.2.226" },
+ { "classDisplayName", "1.2.840.113556.1.4.610" },
+ { "isEphemeral", "1.2.840.113556.1.4.1212" },
+ { "isDefunct", "1.2.840.113556.1.4.661" },
+ { "systemOnly", "1.2.840.113556.1.4.170" },
+ { "governsID", "1.2.840.113556.1.2.22" },
+ { "objectClassCategory", "1.2.840.113556.1.2.370" },
+ { "rDNAttID", "1.2.840.113556.1.2.26" },
+ { "defaultObjectCategory", "1.2.840.113556.1.4.783" },
+ { "subClassOf", "1.2.840.113556.1.2.21" },
+ { "systemAuxiliaryClass", "1.2.840.113556.1.4.198" },
+ { "systemPossSuperiors", "1.2.840.113556.1.4.195" },
+ { "systemMustContain", "1.2.840.113556.1.4.197" },
+ { "systemMayContain", "1.2.840.113556.1.4.196" },
+ { "auxiliaryClass", "1.2.840.113556.1.2.351" },
+ { "possSuperiors", "1.2.840.113556.1.2.8" },
+ { "mustContain", "1.2.840.113556.1.2.24" },
+ { "mayContain", "1.2.840.113556.1.2.25" },
+ { "defaultSecurityDescriptor", "1.2.840.113556.1.4.224" },
+ { "defaultHidingValue", "1.2.840.113556.1.4.518" },
+};
+
+static struct drsuapi_DsReplicaAttribute *dsdb_find_object_attr_name(struct dsdb_schema *schema,
+ struct drsuapi_DsReplicaObject *obj,
+ const char *name,
+ uint32_t *idx)
+{
+ WERROR status;
+ uint32_t i, id;
+ const char *oid = NULL;
+
+ for(i=0; i < ARRAY_SIZE(name_mappings); i++) {
+ if (strcmp(name_mappings[i].name, name) != 0) continue;
+
+ oid = name_mappings[i].oid;
+ break;
+ }
+
+ if (!oid) {
+ return NULL;
+ }
+
+ status = dsdb_map_oid2int(schema, oid, &id);
+ if (!W_ERROR_IS_OK(status)) {
+ return NULL;
+ }
+
+ for (i=0; i < obj->attribute_ctr.num_attributes; i++) {
+ if (obj->attribute_ctr.attributes[i].attid != id) continue;
+
+ if (idx) *idx = i;
+ return &obj->attribute_ctr.attributes[i];
+ }
+
+ return NULL;
+}
+
+#define GET_STRING_DS(s, r, attr, mem_ctx, p, elem, strict) do { \
+ struct drsuapi_DsReplicaAttribute *_a; \
+ _a = dsdb_find_object_attr_name(s, r, attr, NULL); \
+ if (strict && !_a) { \
+ d_printf("%s: %s == NULL\n", __location__, attr); \
+ return WERR_INVALID_PARAM; \
+ } \
+ if (strict && _a->value_ctr.num_values != 1) { \
+ d_printf("%s: %s num_values == %u\n", __location__, attr, \
+ _a->value_ctr.num_values); \
+ return WERR_INVALID_PARAM; \
+ } \
+ if (_a && _a->value_ctr.num_values >= 1) { \
+ size_t _ret; \
+ if (!convert_string_talloc_convenience(mem_ctx, s->iconv_convenience, CH_UTF16, CH_UNIX, \
+ _a->value_ctr.values[0].blob->data, \
+ _a->value_ctr.values[0].blob->length, \
+ (void **)discard_const(&(p)->elem), &_ret, false)) { \
+ DEBUG(0,("%s: invalid data!\n", attr)); \
+ dump_data(0, \
+ _a->value_ctr.values[0].blob->data, \
+ _a->value_ctr.values[0].blob->length); \
+ return WERR_FOOBAR; \
+ } \
+ } else { \
+ (p)->elem = NULL; \
+ } \
+} while (0)
+
+#define GET_STRING_LIST_DS(s, r, attr, mem_ctx, p, elem, strict) do { \
+ int get_string_list_counter; \
+ struct drsuapi_DsReplicaAttribute *_a; \
+ _a = dsdb_find_object_attr_name(s, r, attr, NULL); \
+ if (strict && !_a) { \
+ d_printf("%s: %s == NULL\n", __location__, attr); \
+ return WERR_INVALID_PARAM; \
+ } \
+ (p)->elem = _a ? talloc_array(mem_ctx, const char *, _a->value_ctr.num_values + 1) : NULL; \
+ for (get_string_list_counter=0; \
+ _a && get_string_list_counter < _a->value_ctr.num_values; \
+ get_string_list_counter++) { \
+ size_t _ret; \
+ if (!convert_string_talloc_convenience(mem_ctx, s->iconv_convenience, CH_UTF16, CH_UNIX, \
+ _a->value_ctr.values[get_string_list_counter].blob->data, \
+ _a->value_ctr.values[get_string_list_counter].blob->length, \
+ (void **)discard_const(&(p)->elem[get_string_list_counter]), &_ret, false)) { \
+ DEBUG(0,("%s: invalid data!\n", attr)); \
+ dump_data(0, \
+ _a->value_ctr.values[get_string_list_counter].blob->data, \
+ _a->value_ctr.values[get_string_list_counter].blob->length); \
+ return WERR_FOOBAR; \
+ } \
+ (p)->elem[get_string_list_counter+1] = NULL; \
+ } \
+ talloc_steal(mem_ctx, (p)->elem); \
+} while (0)
+
+#define GET_DN_DS(s, r, attr, mem_ctx, p, elem, strict) do { \
+ struct drsuapi_DsReplicaAttribute *_a; \
+ _a = dsdb_find_object_attr_name(s, r, attr, NULL); \
+ if (strict && !_a) { \
+ d_printf("%s: %s == NULL\n", __location__, attr); \
+ return WERR_INVALID_PARAM; \
+ } \
+ if (strict && _a->value_ctr.num_values != 1) { \
+ d_printf("%s: %s num_values == %u\n", __location__, attr, \
+ _a->value_ctr.num_values); \
+ return WERR_INVALID_PARAM; \
+ } \
+ if (strict && !_a->value_ctr.values[0].blob) { \
+ d_printf("%s: %s data == NULL\n", __location__, attr); \
+ return WERR_INVALID_PARAM; \
+ } \
+ if (_a && _a->value_ctr.num_values >= 1 \
+ && _a->value_ctr.values[0].blob) { \
+ struct drsuapi_DsReplicaObjectIdentifier3 _id3; \
+ enum ndr_err_code _ndr_err; \
+ _ndr_err = ndr_pull_struct_blob_all(_a->value_ctr.values[0].blob, \
+ mem_ctx, s->iconv_convenience, &_id3,\
+ (ndr_pull_flags_fn_t)ndr_pull_drsuapi_DsReplicaObjectIdentifier3);\
+ if (!NDR_ERR_CODE_IS_SUCCESS(_ndr_err)) { \
+ NTSTATUS _nt_status = ndr_map_error2ntstatus(_ndr_err); \
+ return ntstatus_to_werror(_nt_status); \
+ } \
+ (p)->elem = _id3.dn; \
+ } else { \
+ (p)->elem = NULL; \
+ } \
+} while (0)
+
+#define GET_BOOL_DS(s, r, attr, p, elem, strict) do { \
+ struct drsuapi_DsReplicaAttribute *_a; \
+ _a = dsdb_find_object_attr_name(s, r, attr, NULL); \
+ if (strict && !_a) { \
+ d_printf("%s: %s == NULL\n", __location__, attr); \
+ return WERR_INVALID_PARAM; \
+ } \
+ if (strict && _a->value_ctr.num_values != 1) { \
+ d_printf("%s: %s num_values == %u\n", __location__, attr, \
+ (unsigned int)_a->value_ctr.num_values); \
+ return WERR_INVALID_PARAM; \
+ } \
+ if (strict && !_a->value_ctr.values[0].blob) { \
+ d_printf("%s: %s data == NULL\n", __location__, attr); \
+ return WERR_INVALID_PARAM; \
+ } \
+ if (strict && _a->value_ctr.values[0].blob->length != 4) { \
+ d_printf("%s: %s length == %u\n", __location__, attr, \
+ (unsigned int)_a->value_ctr.values[0].blob->length); \
+ return WERR_INVALID_PARAM; \
+ } \
+ if (_a && _a->value_ctr.num_values >= 1 \
+ && _a->value_ctr.values[0].blob \
+ && _a->value_ctr.values[0].blob->length == 4) { \
+ (p)->elem = (IVAL(_a->value_ctr.values[0].blob->data,0)?true:false);\
+ } else { \
+ (p)->elem = false; \
+ } \
+} while (0)
+
+#define GET_UINT32_DS(s, r, attr, p, elem) do { \
+ struct drsuapi_DsReplicaAttribute *_a; \
+ _a = dsdb_find_object_attr_name(s, r, attr, NULL); \
+ if (_a && _a->value_ctr.num_values >= 1 \
+ && _a->value_ctr.values[0].blob \
+ && _a->value_ctr.values[0].blob->length == 4) { \
+ (p)->elem = IVAL(_a->value_ctr.values[0].blob->data,0);\
+ } else { \
+ (p)->elem = 0; \
+ } \
+} while (0)
+
+#define GET_UINT32_PTR_DS(s, r, attr, p, elem) do { \
+ struct drsuapi_DsReplicaAttribute *_a; \
+ _a = dsdb_find_object_attr_name(s, r, attr, NULL); \
+ if (_a && _a->value_ctr.num_values >= 1 \
+ && _a->value_ctr.values[0].blob \
+ && _a->value_ctr.values[0].blob->length == 4) { \
+ (p)->elem = talloc(mem_ctx, uint32_t); \
+ if (!(p)->elem) { \
+ d_printf("%s: talloc failed for %s\n", __location__, attr); \
+ return WERR_NOMEM; \
+ } \
+ *(p)->elem = IVAL(_a->value_ctr.values[0].blob->data,0);\
+ } else { \
+ (p)->elem = NULL; \
+ } \
+} while (0)
+
+#define GET_GUID_DS(s, r, attr, mem_ctx, p, elem) do { \
+ struct drsuapi_DsReplicaAttribute *_a; \
+ _a = dsdb_find_object_attr_name(s, r, attr, NULL); \
+ if (_a && _a->value_ctr.num_values >= 1 \
+ && _a->value_ctr.values[0].blob \
+ && _a->value_ctr.values[0].blob->length == 16) { \
+ enum ndr_err_code _ndr_err; \
+ _ndr_err = ndr_pull_struct_blob_all(_a->value_ctr.values[0].blob, \
+ mem_ctx, s->iconv_convenience, &(p)->elem, \
+ (ndr_pull_flags_fn_t)ndr_pull_GUID); \
+ if (!NDR_ERR_CODE_IS_SUCCESS(_ndr_err)) { \
+ NTSTATUS _nt_status = ndr_map_error2ntstatus(_ndr_err); \
+ return ntstatus_to_werror(_nt_status); \
+ } \
+ } else { \
+ ZERO_STRUCT((p)->elem);\
+ } \
+} while (0)
+
+#define GET_BLOB_DS(s, r, attr, mem_ctx, p, elem) do { \
+ struct drsuapi_DsReplicaAttribute *_a; \
+ _a = dsdb_find_object_attr_name(s, r, attr, NULL); \
+ if (_a && _a->value_ctr.num_values >= 1 \
+ && _a->value_ctr.values[0].blob) { \
+ (p)->elem = *_a->value_ctr.values[0].blob;\
+ talloc_steal(mem_ctx, (p)->elem.data); \
+ } else { \
+ ZERO_STRUCT((p)->elem);\
+ }\
+} while (0)
+
+WERROR dsdb_attribute_from_drsuapi(struct dsdb_schema *schema,
+ struct drsuapi_DsReplicaObject *r,
+ TALLOC_CTX *mem_ctx,
+ struct dsdb_attribute *attr)
+{
+ WERROR status;
+
+ GET_STRING_DS(schema, r, "name", mem_ctx, attr, cn, true);
+ GET_STRING_DS(schema, r, "lDAPDisplayName", mem_ctx, attr, lDAPDisplayName, true);
+ GET_UINT32_DS(schema, r, "attributeID", attr, attributeID_id);
+ status = dsdb_map_int2oid(schema, attr->attributeID_id, mem_ctx, &attr->attributeID_oid);
+ if (!W_ERROR_IS_OK(status)) {
+ DEBUG(0,("%s: '%s': unable to map attributeID 0x%08X: %s\n",
+ __location__, attr->lDAPDisplayName, attr->attributeID_id,
+ win_errstr(status)));
+ return status;
+ }
+ GET_GUID_DS(schema, r, "schemaIDGUID", mem_ctx, attr, schemaIDGUID);
+ GET_UINT32_DS(schema, r, "mAPIID", attr, mAPIID);
+
+ GET_GUID_DS(schema, r, "attributeSecurityGUID", mem_ctx, attr, attributeSecurityGUID);
+
+ GET_UINT32_DS(schema, r, "searchFlags", attr, searchFlags);
+ GET_UINT32_DS(schema, r, "systemFlags", attr, systemFlags);
+ GET_BOOL_DS(schema, r, "isMemberOfPartialAttributeSet", attr, isMemberOfPartialAttributeSet, false);
+ GET_UINT32_DS(schema, r, "linkID", attr, linkID);
+
+ GET_UINT32_DS(schema, r, "attributeSyntax", attr, attributeSyntax_id);
+ status = dsdb_map_int2oid(schema, attr->attributeSyntax_id, mem_ctx, &attr->attributeSyntax_oid);
+ if (!W_ERROR_IS_OK(status)) {
+ DEBUG(0,("%s: '%s': unable to map attributeSyntax 0x%08X: %s\n",
+ __location__, attr->lDAPDisplayName, attr->attributeSyntax_id,
+ win_errstr(status)));
+ return status;
+ }
+ GET_UINT32_DS(schema, r, "oMSyntax", attr, oMSyntax);
+ GET_BLOB_DS(schema, r, "oMObjectClass", mem_ctx, attr, oMObjectClass);
+
+ GET_BOOL_DS(schema, r, "isSingleValued", attr, isSingleValued, true);
+ GET_UINT32_PTR_DS(schema, r, "rangeLower", attr, rangeLower);
+ GET_UINT32_PTR_DS(schema, r, "rangeUpper", attr, rangeUpper);
+ GET_BOOL_DS(schema, r, "extendedCharsAllowed", attr, extendedCharsAllowed, false);
+
+ GET_UINT32_DS(schema, r, "schemaFlagsEx", attr, schemaFlagsEx);
+ GET_BLOB_DS(schema, r, "msDs-Schema-Extensions", mem_ctx, attr, msDs_Schema_Extensions);
+
+ GET_BOOL_DS(schema, r, "showInAdvancedViewOnly", attr, showInAdvancedViewOnly, false);
+ GET_STRING_DS(schema, r, "adminDisplayName", mem_ctx, attr, adminDisplayName, false);
+ GET_STRING_DS(schema, r, "adminDescription", mem_ctx, attr, adminDescription, false);
+ GET_STRING_DS(schema, r, "classDisplayName", mem_ctx, attr, classDisplayName, false);
+ GET_BOOL_DS(schema, r, "isEphemeral", attr, isEphemeral, false);
+ GET_BOOL_DS(schema, r, "isDefunct", attr, isDefunct, false);
+ GET_BOOL_DS(schema, r, "systemOnly", attr, systemOnly, false);
+
+ attr->syntax = dsdb_syntax_for_attribute(attr);
+ if (!attr->syntax) {
+ return WERR_DS_ATT_SCHEMA_REQ_SYNTAX;
+ }
+
+ return WERR_OK;
+}
+
+WERROR dsdb_class_from_drsuapi(struct dsdb_schema *schema,
+ struct drsuapi_DsReplicaObject *r,
+ TALLOC_CTX *mem_ctx,
+ struct dsdb_class *obj)
+{
+ WERROR status;
+
+ GET_STRING_DS(schema, r, "name", mem_ctx, obj, cn, true);
+ GET_STRING_DS(schema, r, "lDAPDisplayName", mem_ctx, obj, lDAPDisplayName, true);
+ GET_UINT32_DS(schema, r, "governsID", obj, governsID_id);
+ status = dsdb_map_int2oid(schema, obj->governsID_id, mem_ctx, &obj->governsID_oid);
+ if (!W_ERROR_IS_OK(status)) {
+ DEBUG(0,("%s: '%s': unable to map governsID 0x%08X: %s\n",
+ __location__, obj->lDAPDisplayName, obj->governsID_id,
+ win_errstr(status)));
+ return status;
+ }
+ GET_GUID_DS(schema, r, "schemaIDGUID", mem_ctx, obj, schemaIDGUID);
+
+ GET_UINT32_DS(schema, r, "objectClassCategory", obj, objectClassCategory);
+ GET_STRING_DS(schema, r, "rDNAttID", mem_ctx, obj, rDNAttID, false);
+ GET_DN_DS(schema, r, "defaultObjectCategory", mem_ctx, obj, defaultObjectCategory, true);
+
+ GET_STRING_DS(schema, r, "subClassOf", mem_ctx, obj, subClassOf, true);
+
+
+ GET_STRING_LIST_DS(schema, r, "systemAuxiliaryClass", mem_ctx, obj, systemAuxiliaryClass, false);
+ GET_STRING_LIST_DS(schema, r, "auxiliaryClass", mem_ctx, obj, auxiliaryClass, false);
+
+ GET_STRING_LIST_DS(schema, r, "systemMustContain", mem_ctx, obj, systemMustContain, false);
+ GET_STRING_LIST_DS(schema, r, "systemMayContain", mem_ctx, obj, systemMayContain, false);
+ GET_STRING_LIST_DS(schema, r, "mustContain", mem_ctx, obj, mustContain, false);
+ GET_STRING_LIST_DS(schema, r, "mayContain", mem_ctx, obj, mayContain, false);
+
+ GET_STRING_LIST_DS(schema, r, "systemPossSuperiors", mem_ctx, obj, systemPossSuperiors, false);
+ GET_STRING_LIST_DS(schema, r, "possSuperiors", mem_ctx, obj, possSuperiors, false);
+ GET_STRING_LIST_DS(schema, r, "possibleInferiors", mem_ctx, obj, possibleInferiors, false);
+
+ GET_STRING_DS(schema, r, "defaultSecurityDescriptor", mem_ctx, obj, defaultSecurityDescriptor, false);
+
+ GET_UINT32_DS(schema, r, "schemaFlagsEx", obj, schemaFlagsEx);
+ GET_BLOB_DS(schema, r, "msDs-Schema-Extensions", mem_ctx, obj, msDs_Schema_Extensions);
+
+ GET_BOOL_DS(schema, r, "showInAdvancedViewOnly", obj, showInAdvancedViewOnly, false);
+ GET_STRING_DS(schema, r, "adminDisplayName", mem_ctx, obj, adminDisplayName, false);
+ GET_STRING_DS(schema, r, "adminDescription", mem_ctx, obj, adminDescription, false);
+ GET_STRING_DS(schema, r, "classDisplayName", mem_ctx, obj, classDisplayName, false);
+ GET_BOOL_DS(schema, r, "defaultHidingValue", obj, defaultHidingValue, false);
+ GET_BOOL_DS(schema, r, "isDefunct", obj, isDefunct, false);
+ GET_BOOL_DS(schema, r, "systemOnly", obj, systemOnly, false);
+
+ return WERR_OK;
+}
+
diff --git a/source4/dsdb/schema/schema_query.c b/source4/dsdb/schema/schema_query.c
new file mode 100644
index 0000000000..00de0f8983
--- /dev/null
+++ b/source4/dsdb/schema/schema_query.c
@@ -0,0 +1,344 @@
+/*
+ Unix SMB/CIFS mplementation.
+ DSDB schema header
+
+ Copyright (C) Stefan Metzmacher <metze@samba.org> 2006-2007
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "includes.h"
+#include "dsdb/samdb/samdb.h"
+
+const struct dsdb_attribute *dsdb_attribute_by_attributeID_id(const struct dsdb_schema *schema,
+ uint32_t id)
+{
+ struct dsdb_attribute *cur;
+
+ /*
+ * 0xFFFFFFFF is used as value when no mapping table is available,
+ * so don't try to match with it
+ */
+ if (id == 0xFFFFFFFF) return NULL;
+
+ /* TODO: add binary search */
+ for (cur = schema->attributes; cur; cur = cur->next) {
+ if (cur->attributeID_id != id) continue;
+
+ return cur;
+ }
+
+ return NULL;
+}
+
+const struct dsdb_attribute *dsdb_attribute_by_attributeID_oid(const struct dsdb_schema *schema,
+ const char *oid)
+{
+ struct dsdb_attribute *cur;
+
+ if (!oid) return NULL;
+
+ /* TODO: add binary search */
+ for (cur = schema->attributes; cur; cur = cur->next) {
+ if (strcmp(cur->attributeID_oid, oid) != 0) continue;
+
+ return cur;
+ }
+
+ return NULL;
+}
+
+const struct dsdb_attribute *dsdb_attribute_by_lDAPDisplayName(const struct dsdb_schema *schema,
+ const char *name)
+{
+ struct dsdb_attribute *cur;
+
+ if (!name) return NULL;
+
+ /* TODO: add binary search */
+ for (cur = schema->attributes; cur; cur = cur->next) {
+ if (strcasecmp(cur->lDAPDisplayName, name) != 0) continue;
+
+ return cur;
+ }
+
+ return NULL;
+}
+
+const struct dsdb_attribute *dsdb_attribute_by_linkID(const struct dsdb_schema *schema,
+ int linkID)
+{
+ struct dsdb_attribute *cur;
+
+ /* TODO: add binary search */
+ for (cur = schema->attributes; cur; cur = cur->next) {
+ if (cur->linkID != linkID) continue;
+
+ return cur;
+ }
+
+ return NULL;
+}
+
+const struct dsdb_class *dsdb_class_by_governsID_id(const struct dsdb_schema *schema,
+ uint32_t id)
+{
+ struct dsdb_class *cur;
+
+ /*
+ * 0xFFFFFFFF is used as value when no mapping table is available,
+ * so don't try to match with it
+ */
+ if (id == 0xFFFFFFFF) return NULL;
+
+ /* TODO: add binary search */
+ for (cur = schema->classes; cur; cur = cur->next) {
+ if (cur->governsID_id != id) continue;
+
+ return cur;
+ }
+
+ return NULL;
+}
+
+const struct dsdb_class *dsdb_class_by_governsID_oid(const struct dsdb_schema *schema,
+ const char *oid)
+{
+ struct dsdb_class *cur;
+
+ if (!oid) return NULL;
+
+ /* TODO: add binary search */
+ for (cur = schema->classes; cur; cur = cur->next) {
+ if (strcmp(cur->governsID_oid, oid) != 0) continue;
+
+ return cur;
+ }
+
+ return NULL;
+}
+
+const struct dsdb_class *dsdb_class_by_lDAPDisplayName(const struct dsdb_schema *schema,
+ const char *name)
+{
+ struct dsdb_class *cur;
+
+ if (!name) return NULL;
+
+ /* TODO: add binary search */
+ for (cur = schema->classes; cur; cur = cur->next) {
+ if (strcasecmp(cur->lDAPDisplayName, name) != 0) continue;
+
+ return cur;
+ }
+
+ return NULL;
+}
+
+const struct dsdb_class *dsdb_class_by_cn(const struct dsdb_schema *schema,
+ const char *cn)
+{
+ struct dsdb_class *cur;
+
+ if (!cn) return NULL;
+
+ /* TODO: add binary search */
+ for (cur = schema->classes; cur; cur = cur->next) {
+ if (strcasecmp(cur->cn, cn) != 0) continue;
+
+ return cur;
+ }
+
+ return NULL;
+}
+
+const char *dsdb_lDAPDisplayName_by_id(const struct dsdb_schema *schema,
+ uint32_t id)
+{
+ const struct dsdb_attribute *a;
+ const struct dsdb_class *c;
+
+ /* TODO: add binary search */
+ a = dsdb_attribute_by_attributeID_id(schema, id);
+ if (a) {
+ return a->lDAPDisplayName;
+ }
+
+ c = dsdb_class_by_governsID_id(schema, id);
+ if (c) {
+ return c->lDAPDisplayName;
+ }
+
+ return NULL;
+}
+
+/**
+ Return a list of linked attributes, in lDAPDisplayName format.
+
+ This may be used to determine if a modification would require
+ backlinks to be updated, for example
+*/
+
+WERROR dsdb_linked_attribute_lDAPDisplayName_list(const struct dsdb_schema *schema, TALLOC_CTX *mem_ctx, const char ***attr_list_ret)
+{
+ const char **attr_list = NULL;
+ struct dsdb_attribute *cur;
+ int i = 0;
+ for (cur = schema->attributes; cur; cur = cur->next) {
+ if (cur->linkID == 0) continue;
+
+ attr_list = talloc_realloc(mem_ctx, attr_list, const char *, i+2);
+ if (!attr_list) {
+ return WERR_NOMEM;
+ }
+ attr_list[i] = cur->lDAPDisplayName;
+ i++;
+ }
+ attr_list[i] = NULL;
+ *attr_list_ret = attr_list;
+ return WERR_OK;
+}
+
+const char **merge_attr_list(TALLOC_CTX *mem_ctx,
+ const char **attrs, const char * const*new_attrs)
+{
+ const char **ret_attrs;
+ int i;
+ size_t new_len, orig_len = str_list_length(attrs);
+ if (!new_attrs) {
+ return attrs;
+ }
+
+ ret_attrs = talloc_realloc(mem_ctx,
+ attrs, const char *, orig_len + str_list_length(new_attrs) + 1);
+ if (ret_attrs) {
+ for (i=0; i < str_list_length(new_attrs); i++) {
+ ret_attrs[orig_len + i] = new_attrs[i];
+ }
+ new_len = orig_len + str_list_length(new_attrs);
+
+ ret_attrs[new_len] = NULL;
+ }
+
+ return ret_attrs;
+}
+
+/*
+ Return a merged list of the attributes of exactly one class (not
+ considering subclasses, auxillary classes etc)
+*/
+
+const char **dsdb_attribute_list(TALLOC_CTX *mem_ctx, const struct dsdb_class *sclass, enum dsdb_attr_list_query query)
+{
+ const char **attr_list = NULL;
+ switch (query) {
+ case DSDB_SCHEMA_ALL_MAY:
+ attr_list = merge_attr_list(mem_ctx, attr_list, sclass->mayContain);
+ attr_list = merge_attr_list(mem_ctx, attr_list, sclass->systemMayContain);
+ break;
+
+ case DSDB_SCHEMA_ALL_MUST:
+ attr_list = merge_attr_list(mem_ctx, attr_list, sclass->mustContain);
+ attr_list = merge_attr_list(mem_ctx, attr_list, sclass->systemMustContain);
+ break;
+
+ case DSDB_SCHEMA_SYS_MAY:
+ attr_list = merge_attr_list(mem_ctx, attr_list, sclass->systemMayContain);
+ break;
+
+ case DSDB_SCHEMA_SYS_MUST:
+ attr_list = merge_attr_list(mem_ctx, attr_list, sclass->systemMustContain);
+ break;
+
+ case DSDB_SCHEMA_MAY:
+ attr_list = merge_attr_list(mem_ctx, attr_list, sclass->mayContain);
+ break;
+
+ case DSDB_SCHEMA_MUST:
+ attr_list = merge_attr_list(mem_ctx, attr_list, sclass->mustContain);
+ break;
+
+ case DSDB_SCHEMA_ALL:
+ attr_list = merge_attr_list(mem_ctx, attr_list, sclass->mayContain);
+ attr_list = merge_attr_list(mem_ctx, attr_list, sclass->systemMayContain);
+ attr_list = merge_attr_list(mem_ctx, attr_list, sclass->mustContain);
+ attr_list = merge_attr_list(mem_ctx, attr_list, sclass->systemMustContain);
+ break;
+ }
+ return attr_list;
+}
+
+static const char **dsdb_full_attribute_list_internal(TALLOC_CTX *mem_ctx,
+ const struct dsdb_schema *schema,
+ const char **class_list,
+ enum dsdb_attr_list_query query)
+{
+ int i;
+ const struct dsdb_class *sclass;
+
+ const char **attr_list = NULL;
+ const char **this_class_list;
+ const char **recursive_list;
+
+ for (i=0; class_list && class_list[i]; i++) {
+ sclass = dsdb_class_by_lDAPDisplayName(schema, class_list[i]);
+
+ this_class_list = dsdb_attribute_list(mem_ctx, sclass, query);
+ attr_list = merge_attr_list(mem_ctx, attr_list, this_class_list);
+
+ recursive_list = dsdb_full_attribute_list_internal(mem_ctx, schema,
+ sclass->systemAuxiliaryClass,
+ query);
+
+ attr_list = merge_attr_list(mem_ctx, attr_list, recursive_list);
+
+ recursive_list = dsdb_full_attribute_list_internal(mem_ctx, schema,
+ sclass->auxiliaryClass,
+ query);
+
+ attr_list = merge_attr_list(mem_ctx, attr_list, recursive_list);
+
+ }
+ return attr_list;
+}
+
+const char **dsdb_full_attribute_list(TALLOC_CTX *mem_ctx,
+ const struct dsdb_schema *schema,
+ const char **class_list,
+ enum dsdb_attr_list_query query)
+{
+ const char **attr_list = dsdb_full_attribute_list_internal(mem_ctx, schema, class_list, query);
+ size_t new_len = str_list_length(attr_list);
+
+ /* Remove duplicates */
+ if (new_len > 1) {
+ int i;
+ qsort(attr_list, new_len,
+ sizeof(*attr_list),
+ (comparison_fn_t)strcasecmp);
+
+ for (i=1 ; i < new_len; i++) {
+ const char **val1 = &attr_list[i-1];
+ const char **val2 = &attr_list[i];
+ if (ldb_attr_cmp(*val1, *val2) == 0) {
+ memmove(val1, val2, (new_len - i) * sizeof( *attr_list));
+ new_len--;
+ i--;
+ }
+ }
+ }
+ return attr_list;
+}
diff --git a/source4/dsdb/schema/schema_set.c b/source4/dsdb/schema/schema_set.c
new file mode 100644
index 0000000000..6abd8a8f88
--- /dev/null
+++ b/source4/dsdb/schema/schema_set.c
@@ -0,0 +1,407 @@
+/*
+ Unix SMB/CIFS mplementation.
+ DSDB schema header
+
+ Copyright (C) Stefan Metzmacher <metze@samba.org> 2006-2007
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "includes.h"
+#include "dlinklist.h"
+#include "dsdb/samdb/samdb.h"
+#include "lib/ldb/include/ldb_module.h"
+#include "param/param.h"
+
+
+static int dsdb_schema_set_attributes(struct ldb_context *ldb, struct dsdb_schema *schema, bool write_attributes)
+{
+ int ret = LDB_SUCCESS;
+ struct ldb_result *res;
+ struct ldb_result *res_idx;
+ struct dsdb_attribute *attr;
+ struct ldb_message *mod_msg;
+ TALLOC_CTX *mem_ctx = talloc_new(ldb);
+
+ struct ldb_message *msg;
+ struct ldb_message *msg_idx;
+
+ if (!mem_ctx) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ msg = ldb_msg_new(mem_ctx);
+ if (!msg) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ msg_idx = ldb_msg_new(mem_ctx);
+ if (!msg_idx) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ msg->dn = ldb_dn_new(msg, ldb, "@ATTRIBUTES");
+ if (!msg->dn) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ msg_idx->dn = ldb_dn_new(msg, ldb, "@INDEXLIST");
+ if (!msg_idx->dn) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ for (attr = schema->attributes; attr; attr = attr->next) {
+ const struct ldb_schema_syntax *s;
+ const char *syntax = attr->syntax->ldb_syntax;
+ if (!syntax) {
+ syntax = attr->syntax->ldap_oid;
+ }
+
+ /* Write out a rough approximation of the schema as an @ATTRIBUTES value, for bootstrapping */
+ if (strcmp(syntax, LDB_SYNTAX_INTEGER) == 0) {
+ ret = ldb_msg_add_string(msg, attr->lDAPDisplayName, "INTEGER");
+ } else if (strcmp(syntax, LDB_SYNTAX_DIRECTORY_STRING) == 0) {
+ ret = ldb_msg_add_string(msg, attr->lDAPDisplayName, "CASE_INSENSITIVE");
+ }
+ if (ret != LDB_SUCCESS) {
+ break;
+ }
+
+ if (attr->searchFlags & SEARCH_FLAG_ATTINDEX) {
+ ret = ldb_msg_add_string(msg_idx, "@IDXATTR", attr->lDAPDisplayName);
+ if (ret != LDB_SUCCESS) {
+ break;
+ }
+ }
+
+ if (!attr->syntax) {
+ continue;
+ }
+
+ ret = ldb_schema_attribute_add(ldb, attr->lDAPDisplayName, LDB_ATTR_FLAG_FIXED,
+ syntax);
+ if (ret != LDB_SUCCESS) {
+ s = ldb_samba_syntax_by_name(ldb, attr->syntax->ldap_oid);
+ if (s) {
+ ret = ldb_schema_attribute_add_with_syntax(ldb, attr->lDAPDisplayName, LDB_ATTR_FLAG_FIXED, s);
+ } else {
+ ret = LDB_SUCCESS; /* Nothing to do here */
+ }
+ }
+
+ if (ret != LDB_SUCCESS) {
+ break;
+ }
+ }
+
+ if (!write_attributes || ret != LDB_SUCCESS) {
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+
+ /* Try to avoid churning the attributes too much - we only want to do this if they have changed */
+ ret = ldb_search(ldb, mem_ctx, &res, msg->dn, LDB_SCOPE_BASE, NULL, "dn=%s", ldb_dn_get_linearized(msg->dn));
+ if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+ ret = ldb_add(ldb, msg);
+ } else if (ret != LDB_SUCCESS) {
+ } else if (res->count != 1) {
+ ret = ldb_add(ldb, msg);
+ } else {
+ ret = LDB_SUCCESS;
+ /* Annoyingly added to our search results */
+ ldb_msg_remove_attr(res->msgs[0], "distinguishedName");
+
+ mod_msg = ldb_msg_diff(ldb, res->msgs[0], msg);
+ if (mod_msg->num_elements > 0) {
+ ret = ldb_modify(ldb, mod_msg);
+ }
+ }
+
+ if (ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) {
+ /* We might be on a read-only DB */
+ ret = LDB_SUCCESS;
+ }
+ if (ret != LDB_SUCCESS) {
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ /* Now write out the indexs, as found in the schema (if they have changed) */
+
+ ret = ldb_search(ldb, mem_ctx, &res_idx, msg_idx->dn, LDB_SCOPE_BASE, NULL, "dn=%s", ldb_dn_get_linearized(msg_idx->dn));
+ if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+ ret = ldb_add(ldb, msg_idx);
+ } else if (ret != LDB_SUCCESS) {
+ } else if (res->count != 1) {
+ ret = ldb_add(ldb, msg_idx);
+ } else {
+ ret = LDB_SUCCESS;
+ /* Annoyingly added to our search results */
+ ldb_msg_remove_attr(res_idx->msgs[0], "distinguishedName");
+
+ mod_msg = ldb_msg_diff(ldb, res_idx->msgs[0], msg_idx);
+ if (mod_msg->num_elements > 0) {
+ ret = ldb_modify(ldb, mod_msg);
+ }
+ }
+ if (ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) {
+ /* We might be on a read-only DB */
+ ret = LDB_SUCCESS;
+ }
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+
+/**
+ * Attach the schema to an opaque pointer on the ldb, so ldb modules
+ * can find it
+ */
+
+int dsdb_set_schema(struct ldb_context *ldb, struct dsdb_schema *schema)
+{
+ int ret;
+
+ ret = ldb_set_opaque(ldb, "dsdb_schema", schema);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ /* Set the new attributes based on the new schema */
+ ret = dsdb_schema_set_attributes(ldb, schema, true);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ talloc_steal(ldb, schema);
+
+ return LDB_SUCCESS;
+}
+
+/**
+ * Global variable to hold one copy of the schema, used to avoid memory bloat
+ */
+static struct dsdb_schema *global_schema;
+
+/**
+ * Make this ldb use the 'global' schema, setup to avoid having multiple copies in this process
+ */
+int dsdb_set_global_schema(struct ldb_context *ldb)
+{
+ int ret;
+ if (!global_schema) {
+ return LDB_SUCCESS;
+ }
+ ret = ldb_set_opaque(ldb, "dsdb_schema", global_schema);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ /* Set the new attributes based on the new schema */
+ ret = dsdb_schema_set_attributes(ldb, global_schema, false);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ /* Keep a reference to this schema, just incase the global copy is replaced */
+ if (talloc_reference(ldb, global_schema) == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ return LDB_SUCCESS;
+}
+
+/**
+ * Find the schema object for this ldb
+ */
+
+struct dsdb_schema *dsdb_get_schema(struct ldb_context *ldb)
+{
+ const void *p;
+ struct dsdb_schema *schema;
+
+ /* see if we have a cached copy */
+ p = ldb_get_opaque(ldb, "dsdb_schema");
+ if (!p) {
+ return NULL;
+ }
+
+ schema = talloc_get_type(p, struct dsdb_schema);
+ if (!schema) {
+ return NULL;
+ }
+
+ return schema;
+}
+
+/**
+ * Make the schema found on this ldb the 'global' schema
+ */
+
+void dsdb_make_schema_global(struct ldb_context *ldb)
+{
+ struct dsdb_schema *schema = dsdb_get_schema(ldb);
+ if (!schema) {
+ return;
+ }
+
+ if (global_schema) {
+ talloc_unlink(talloc_autofree_context(), schema);
+ }
+
+ talloc_steal(talloc_autofree_context(), schema);
+ global_schema = schema;
+
+ dsdb_set_global_schema(ldb);
+}
+
+
+/**
+ * Rather than read a schema from the LDB itself, read it from an ldif
+ * file. This allows schema to be loaded and used while adding the
+ * schema itself to the directory.
+ */
+
+WERROR dsdb_attach_schema_from_ldif_file(struct ldb_context *ldb, const char *pf, const char *df)
+{
+ struct ldb_ldif *ldif;
+ struct ldb_message *msg;
+ TALLOC_CTX *mem_ctx;
+ WERROR status;
+ int ret;
+ struct dsdb_schema *schema;
+ const struct ldb_val *prefix_val;
+ const struct ldb_val *info_val;
+ struct ldb_val info_val_default;
+
+ mem_ctx = talloc_new(ldb);
+ if (!mem_ctx) {
+ goto nomem;
+ }
+
+ schema = dsdb_new_schema(mem_ctx, lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")));
+
+ schema->fsmo.we_are_master = true;
+ schema->fsmo.master_dn = ldb_dn_new_fmt(schema, ldb, "@PROVISION_SCHEMA_MASTER");
+ if (!schema->fsmo.master_dn) {
+ goto nomem;
+ }
+
+ /*
+ * load the prefixMap attribute from pf
+ */
+ ldif = ldb_ldif_read_string(ldb, &pf);
+ if (!ldif) {
+ status = WERR_INVALID_PARAM;
+ goto failed;
+ }
+ talloc_steal(mem_ctx, ldif);
+
+ msg = ldb_msg_canonicalize(ldb, ldif->msg);
+ if (!msg) {
+ goto nomem;
+ }
+ talloc_steal(mem_ctx, msg);
+ talloc_free(ldif);
+
+ prefix_val = ldb_msg_find_ldb_val(msg, "prefixMap");
+ if (!prefix_val) {
+ status = WERR_INVALID_PARAM;
+ goto failed;
+ }
+
+ info_val = ldb_msg_find_ldb_val(msg, "schemaInfo");
+ if (!info_val) {
+ info_val_default = strhex_to_data_blob(mem_ctx, "FF0000000000000000000000000000000000000000");
+ if (!info_val_default.data) {
+ goto nomem;
+ }
+ info_val = &info_val_default;
+ }
+
+ status = dsdb_load_oid_mappings_ldb(schema, prefix_val, info_val);
+ if (!W_ERROR_IS_OK(status)) {
+ goto failed;
+ }
+
+ /*
+ * load the attribute and class definitions outof df
+ */
+ while ((ldif = ldb_ldif_read_string(ldb, &df))) {
+ bool is_sa;
+ bool is_sc;
+
+ talloc_steal(mem_ctx, ldif);
+
+ msg = ldb_msg_canonicalize(ldb, ldif->msg);
+ if (!msg) {
+ goto nomem;
+ }
+
+ talloc_steal(mem_ctx, msg);
+ talloc_free(ldif);
+
+ is_sa = ldb_msg_check_string_attribute(msg, "objectClass", "attributeSchema");
+ is_sc = ldb_msg_check_string_attribute(msg, "objectClass", "classSchema");
+
+ if (is_sa) {
+ struct dsdb_attribute *sa;
+
+ sa = talloc_zero(schema, struct dsdb_attribute);
+ if (!sa) {
+ goto nomem;
+ }
+
+ status = dsdb_attribute_from_ldb(schema, msg, sa, sa);
+ if (!W_ERROR_IS_OK(status)) {
+ goto failed;
+ }
+
+ DLIST_ADD_END(schema->attributes, sa, struct dsdb_attribute *);
+ } else if (is_sc) {
+ struct dsdb_class *sc;
+
+ sc = talloc_zero(schema, struct dsdb_class);
+ if (!sc) {
+ goto nomem;
+ }
+
+ status = dsdb_class_from_ldb(schema, msg, sc, sc);
+ if (!W_ERROR_IS_OK(status)) {
+ goto failed;
+ }
+
+ DLIST_ADD_END(schema->classes, sc, struct dsdb_class *);
+ }
+ }
+
+ ret = dsdb_set_schema(ldb, schema);
+ if (ret != LDB_SUCCESS) {
+ status = WERR_FOOBAR;
+ goto failed;
+ }
+
+ goto done;
+
+nomem:
+ status = WERR_NOMEM;
+failed:
+done:
+ talloc_free(mem_ctx);
+ return status;
+}
diff --git a/source4/dsdb/schema/schema_syntax.c b/source4/dsdb/schema/schema_syntax.c
new file mode 100644
index 0000000000..27c9a6c4a4
--- /dev/null
+++ b/source4/dsdb/schema/schema_syntax.c
@@ -0,0 +1,1538 @@
+/*
+ Unix SMB/CIFS mplementation.
+ DSDB schema syntaxes
+
+ Copyright (C) Stefan Metzmacher <metze@samba.org> 2006
+ Copyright (C) Simo Sorce 2005
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+
+*/
+#include "includes.h"
+#include "dsdb/samdb/samdb.h"
+#include "librpc/gen_ndr/ndr_drsuapi.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "lib/ldb/include/ldb.h"
+#include "lib/ldb/include/ldb_errors.h"
+#include "system/time.h"
+#include "../lib/util/charset/charset.h"
+#include "librpc/ndr/libndr.h"
+
+static WERROR dsdb_syntax_FOOBAR_drsuapi_to_ldb(struct ldb_context *ldb,
+ const struct dsdb_schema *schema,
+ const struct dsdb_attribute *attr,
+ const struct drsuapi_DsReplicaAttribute *in,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message_element *out)
+{
+ uint32_t i;
+
+ out->flags = 0;
+ out->name = talloc_strdup(mem_ctx, attr->lDAPDisplayName);
+ W_ERROR_HAVE_NO_MEMORY(out->name);
+
+ out->num_values = in->value_ctr.num_values;
+ out->values = talloc_array(mem_ctx, struct ldb_val, out->num_values);
+ W_ERROR_HAVE_NO_MEMORY(out->values);
+
+ for (i=0; i < out->num_values; i++) {
+ char *str;
+
+ if (in->value_ctr.values[i].blob == NULL) {
+ return WERR_FOOBAR;
+ }
+
+ str = talloc_asprintf(out->values, "%s: not implemented",
+ attr->syntax->name);
+ W_ERROR_HAVE_NO_MEMORY(str);
+
+ out->values[i] = data_blob_string_const(str);
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_FOOBAR_ldb_to_drsuapi(struct ldb_context *ldb,
+ const struct dsdb_schema *schema,
+ const struct dsdb_attribute *attr,
+ const struct ldb_message_element *in,
+ TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsReplicaAttribute *out)
+{
+ return WERR_FOOBAR;
+}
+
+static WERROR dsdb_syntax_BOOL_drsuapi_to_ldb(struct ldb_context *ldb,
+ const struct dsdb_schema *schema,
+ const struct dsdb_attribute *attr,
+ const struct drsuapi_DsReplicaAttribute *in,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message_element *out)
+{
+ uint32_t i;
+
+ out->flags = 0;
+ out->name = talloc_strdup(mem_ctx, attr->lDAPDisplayName);
+ W_ERROR_HAVE_NO_MEMORY(out->name);
+
+ out->num_values = in->value_ctr.num_values;
+ out->values = talloc_array(mem_ctx, struct ldb_val, out->num_values);
+ W_ERROR_HAVE_NO_MEMORY(out->values);
+
+ for (i=0; i < out->num_values; i++) {
+ uint32_t v;
+ char *str;
+
+ if (in->value_ctr.values[i].blob == NULL) {
+ return WERR_FOOBAR;
+ }
+
+ if (in->value_ctr.values[i].blob->length != 4) {
+ return WERR_FOOBAR;
+ }
+
+ v = IVAL(in->value_ctr.values[i].blob->data, 0);
+
+ if (v != 0) {
+ str = talloc_strdup(out->values, "TRUE");
+ W_ERROR_HAVE_NO_MEMORY(str);
+ } else {
+ str = talloc_strdup(out->values, "FALSE");
+ W_ERROR_HAVE_NO_MEMORY(str);
+ }
+
+ out->values[i] = data_blob_string_const(str);
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_BOOL_ldb_to_drsuapi(struct ldb_context *ldb,
+ const struct dsdb_schema *schema,
+ const struct dsdb_attribute *attr,
+ const struct ldb_message_element *in,
+ TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsReplicaAttribute *out)
+{
+ uint32_t i;
+ DATA_BLOB *blobs;
+
+ if (attr->attributeID_id == 0xFFFFFFFF) {
+ return WERR_FOOBAR;
+ }
+
+ out->attid = attr->attributeID_id;
+ out->value_ctr.num_values = in->num_values;
+ out->value_ctr.values = talloc_array(mem_ctx,
+ struct drsuapi_DsAttributeValue,
+ in->num_values);
+ W_ERROR_HAVE_NO_MEMORY(out->value_ctr.values);
+
+ blobs = talloc_array(mem_ctx, DATA_BLOB, in->num_values);
+ W_ERROR_HAVE_NO_MEMORY(blobs);
+
+ for (i=0; i < in->num_values; i++) {
+ out->value_ctr.values[i].blob = &blobs[i];
+
+ blobs[i] = data_blob_talloc(blobs, NULL, 4);
+ W_ERROR_HAVE_NO_MEMORY(blobs[i].data);
+
+ if (strcmp("TRUE", (const char *)in->values[i].data) == 0) {
+ SIVAL(blobs[i].data, 0, 0x00000001);
+ } else if (strcmp("FALSE", (const char *)in->values[i].data) == 0) {
+ SIVAL(blobs[i].data, 0, 0x00000000);
+ } else {
+ return WERR_FOOBAR;
+ }
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_INT32_drsuapi_to_ldb(struct ldb_context *ldb,
+ const struct dsdb_schema *schema,
+ const struct dsdb_attribute *attr,
+ const struct drsuapi_DsReplicaAttribute *in,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message_element *out)
+{
+ uint32_t i;
+
+ out->flags = 0;
+ out->name = talloc_strdup(mem_ctx, attr->lDAPDisplayName);
+ W_ERROR_HAVE_NO_MEMORY(out->name);
+
+ out->num_values = in->value_ctr.num_values;
+ out->values = talloc_array(mem_ctx, struct ldb_val, out->num_values);
+ W_ERROR_HAVE_NO_MEMORY(out->values);
+
+ for (i=0; i < out->num_values; i++) {
+ int32_t v;
+ char *str;
+
+ if (in->value_ctr.values[i].blob == NULL) {
+ return WERR_FOOBAR;
+ }
+
+ if (in->value_ctr.values[i].blob->length != 4) {
+ return WERR_FOOBAR;
+ }
+
+ v = IVALS(in->value_ctr.values[i].blob->data, 0);
+
+ str = talloc_asprintf(out->values, "%d", v);
+ W_ERROR_HAVE_NO_MEMORY(str);
+
+ out->values[i] = data_blob_string_const(str);
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_INT32_ldb_to_drsuapi(struct ldb_context *ldb,
+ const struct dsdb_schema *schema,
+ const struct dsdb_attribute *attr,
+ const struct ldb_message_element *in,
+ TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsReplicaAttribute *out)
+{
+ uint32_t i;
+ DATA_BLOB *blobs;
+
+ if (attr->attributeID_id == 0xFFFFFFFF) {
+ return WERR_FOOBAR;
+ }
+
+ out->attid = attr->attributeID_id;
+ out->value_ctr.num_values = in->num_values;
+ out->value_ctr.values = talloc_array(mem_ctx,
+ struct drsuapi_DsAttributeValue,
+ in->num_values);
+ W_ERROR_HAVE_NO_MEMORY(out->value_ctr.values);
+
+ blobs = talloc_array(mem_ctx, DATA_BLOB, in->num_values);
+ W_ERROR_HAVE_NO_MEMORY(blobs);
+
+ for (i=0; i < in->num_values; i++) {
+ int32_t v;
+
+ out->value_ctr.values[i].blob = &blobs[i];
+
+ blobs[i] = data_blob_talloc(blobs, NULL, 4);
+ W_ERROR_HAVE_NO_MEMORY(blobs[i].data);
+
+ v = strtol((const char *)in->values[i].data, NULL, 10);
+
+ SIVALS(blobs[i].data, 0, v);
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_INT64_drsuapi_to_ldb(struct ldb_context *ldb,
+ const struct dsdb_schema *schema,
+ const struct dsdb_attribute *attr,
+ const struct drsuapi_DsReplicaAttribute *in,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message_element *out)
+{
+ uint32_t i;
+
+ out->flags = 0;
+ out->name = talloc_strdup(mem_ctx, attr->lDAPDisplayName);
+ W_ERROR_HAVE_NO_MEMORY(out->name);
+
+ out->num_values = in->value_ctr.num_values;
+ out->values = talloc_array(mem_ctx, struct ldb_val, out->num_values);
+ W_ERROR_HAVE_NO_MEMORY(out->values);
+
+ for (i=0; i < out->num_values; i++) {
+ int64_t v;
+ char *str;
+
+ if (in->value_ctr.values[i].blob == NULL) {
+ return WERR_FOOBAR;
+ }
+
+ if (in->value_ctr.values[i].blob->length != 8) {
+ return WERR_FOOBAR;
+ }
+
+ v = BVALS(in->value_ctr.values[i].blob->data, 0);
+
+ str = talloc_asprintf(out->values, "%lld", (long long int)v);
+ W_ERROR_HAVE_NO_MEMORY(str);
+
+ out->values[i] = data_blob_string_const(str);
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_INT64_ldb_to_drsuapi(struct ldb_context *ldb,
+ const struct dsdb_schema *schema,
+ const struct dsdb_attribute *attr,
+ const struct ldb_message_element *in,
+ TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsReplicaAttribute *out)
+{
+ uint32_t i;
+ DATA_BLOB *blobs;
+
+ if (attr->attributeID_id == 0xFFFFFFFF) {
+ return WERR_FOOBAR;
+ }
+
+ out->attid = attr->attributeID_id;
+ out->value_ctr.num_values = in->num_values;
+ out->value_ctr.values = talloc_array(mem_ctx,
+ struct drsuapi_DsAttributeValue,
+ in->num_values);
+ W_ERROR_HAVE_NO_MEMORY(out->value_ctr.values);
+
+ blobs = talloc_array(mem_ctx, DATA_BLOB, in->num_values);
+ W_ERROR_HAVE_NO_MEMORY(blobs);
+
+ for (i=0; i < in->num_values; i++) {
+ int64_t v;
+
+ out->value_ctr.values[i].blob = &blobs[i];
+
+ blobs[i] = data_blob_talloc(blobs, NULL, 8);
+ W_ERROR_HAVE_NO_MEMORY(blobs[i].data);
+
+ v = strtoll((const char *)in->values[i].data, NULL, 10);
+
+ SBVALS(blobs[i].data, 0, v);
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_NTTIME_UTC_drsuapi_to_ldb(struct ldb_context *ldb,
+ const struct dsdb_schema *schema,
+ const struct dsdb_attribute *attr,
+ const struct drsuapi_DsReplicaAttribute *in,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message_element *out)
+{
+ uint32_t i;
+
+ out->flags = 0;
+ out->name = talloc_strdup(mem_ctx, attr->lDAPDisplayName);
+ W_ERROR_HAVE_NO_MEMORY(out->name);
+
+ out->num_values = in->value_ctr.num_values;
+ out->values = talloc_array(mem_ctx, struct ldb_val, out->num_values);
+ W_ERROR_HAVE_NO_MEMORY(out->values);
+
+ for (i=0; i < out->num_values; i++) {
+ NTTIME v;
+ time_t t;
+ char *str;
+
+ if (in->value_ctr.values[i].blob == NULL) {
+ return WERR_FOOBAR;
+ }
+
+ if (in->value_ctr.values[i].blob->length != 8) {
+ return WERR_FOOBAR;
+ }
+
+ v = BVAL(in->value_ctr.values[i].blob->data, 0);
+ v *= 10000000;
+ t = nt_time_to_unix(v);
+
+ /*
+ * NOTE: On a w2k3 server you can set a GeneralizedTime string
+ * via LDAP, but you get back an UTCTime string,
+ * but via DRSUAPI you get back the NTTIME_1sec value
+ * that represents the GeneralizedTime value!
+ *
+ * So if we store the UTCTime string in our ldb
+ * we'll loose information!
+ */
+ str = ldb_timestring_utc(out->values, t);
+ W_ERROR_HAVE_NO_MEMORY(str);
+ out->values[i] = data_blob_string_const(str);
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_NTTIME_UTC_ldb_to_drsuapi(struct ldb_context *ldb,
+ const struct dsdb_schema *schema,
+ const struct dsdb_attribute *attr,
+ const struct ldb_message_element *in,
+ TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsReplicaAttribute *out)
+{
+ uint32_t i;
+ DATA_BLOB *blobs;
+
+ if (attr->attributeID_id == 0xFFFFFFFF) {
+ return WERR_FOOBAR;
+ }
+
+ out->attid = attr->attributeID_id;
+ out->value_ctr.num_values = in->num_values;
+ out->value_ctr.values = talloc_array(mem_ctx,
+ struct drsuapi_DsAttributeValue,
+ in->num_values);
+ W_ERROR_HAVE_NO_MEMORY(out->value_ctr.values);
+
+ blobs = talloc_array(mem_ctx, DATA_BLOB, in->num_values);
+ W_ERROR_HAVE_NO_MEMORY(blobs);
+
+ for (i=0; i < in->num_values; i++) {
+ NTTIME v;
+ time_t t;
+
+ out->value_ctr.values[i].blob = &blobs[i];
+
+ blobs[i] = data_blob_talloc(blobs, NULL, 8);
+ W_ERROR_HAVE_NO_MEMORY(blobs[i].data);
+
+ t = ldb_string_utc_to_time((const char *)in->values[i].data);
+ unix_to_nt_time(&v, t);
+ v /= 10000000;
+
+ SBVAL(blobs[i].data, 0, v);
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_NTTIME_drsuapi_to_ldb(struct ldb_context *ldb,
+ const struct dsdb_schema *schema,
+ const struct dsdb_attribute *attr,
+ const struct drsuapi_DsReplicaAttribute *in,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message_element *out)
+{
+ uint32_t i;
+
+ out->flags = 0;
+ out->name = talloc_strdup(mem_ctx, attr->lDAPDisplayName);
+ W_ERROR_HAVE_NO_MEMORY(out->name);
+
+ out->num_values = in->value_ctr.num_values;
+ out->values = talloc_array(mem_ctx, struct ldb_val, out->num_values);
+ W_ERROR_HAVE_NO_MEMORY(out->values);
+
+ for (i=0; i < out->num_values; i++) {
+ NTTIME v;
+ time_t t;
+ char *str;
+
+ if (in->value_ctr.values[i].blob == NULL) {
+ return WERR_FOOBAR;
+ }
+
+ if (in->value_ctr.values[i].blob->length != 8) {
+ return WERR_FOOBAR;
+ }
+
+ v = BVAL(in->value_ctr.values[i].blob->data, 0);
+ v *= 10000000;
+ t = nt_time_to_unix(v);
+
+ str = ldb_timestring(out->values, t);
+ W_ERROR_HAVE_NO_MEMORY(str);
+
+ out->values[i] = data_blob_string_const(str);
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_NTTIME_ldb_to_drsuapi(struct ldb_context *ldb,
+ const struct dsdb_schema *schema,
+ const struct dsdb_attribute *attr,
+ const struct ldb_message_element *in,
+ TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsReplicaAttribute *out)
+{
+ uint32_t i;
+ DATA_BLOB *blobs;
+
+ if (attr->attributeID_id == 0xFFFFFFFF) {
+ return WERR_FOOBAR;
+ }
+
+ out->attid = attr->attributeID_id;
+ out->value_ctr.num_values = in->num_values;
+ out->value_ctr.values = talloc_array(mem_ctx,
+ struct drsuapi_DsAttributeValue,
+ in->num_values);
+ W_ERROR_HAVE_NO_MEMORY(out->value_ctr.values);
+
+ blobs = talloc_array(mem_ctx, DATA_BLOB, in->num_values);
+ W_ERROR_HAVE_NO_MEMORY(blobs);
+
+ for (i=0; i < in->num_values; i++) {
+ NTTIME v;
+ time_t t;
+
+ out->value_ctr.values[i].blob = &blobs[i];
+
+ blobs[i] = data_blob_talloc(blobs, NULL, 8);
+ W_ERROR_HAVE_NO_MEMORY(blobs[i].data);
+
+ t = ldb_string_to_time((const char *)in->values[i].data);
+ unix_to_nt_time(&v, t);
+ v /= 10000000;
+
+ SBVAL(blobs[i].data, 0, v);
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_DATA_BLOB_drsuapi_to_ldb(struct ldb_context *ldb,
+ const struct dsdb_schema *schema,
+ const struct dsdb_attribute *attr,
+ const struct drsuapi_DsReplicaAttribute *in,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message_element *out)
+{
+ uint32_t i;
+
+ out->flags = 0;
+ out->name = talloc_strdup(mem_ctx, attr->lDAPDisplayName);
+ W_ERROR_HAVE_NO_MEMORY(out->name);
+
+ out->num_values = in->value_ctr.num_values;
+ out->values = talloc_array(mem_ctx, struct ldb_val, out->num_values);
+ W_ERROR_HAVE_NO_MEMORY(out->values);
+
+ for (i=0; i < out->num_values; i++) {
+ if (in->value_ctr.values[i].blob == NULL) {
+ return WERR_FOOBAR;
+ }
+
+ if (in->value_ctr.values[i].blob->length == 0) {
+ return WERR_FOOBAR;
+ }
+
+ out->values[i] = data_blob_dup_talloc(out->values,
+ in->value_ctr.values[i].blob);
+ W_ERROR_HAVE_NO_MEMORY(out->values[i].data);
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_DATA_BLOB_ldb_to_drsuapi(struct ldb_context *ldb,
+ const struct dsdb_schema *schema,
+ const struct dsdb_attribute *attr,
+ const struct ldb_message_element *in,
+ TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsReplicaAttribute *out)
+{
+ uint32_t i;
+ DATA_BLOB *blobs;
+
+ if (attr->attributeID_id == 0xFFFFFFFF) {
+ return WERR_FOOBAR;
+ }
+
+ out->attid = attr->attributeID_id;
+ out->value_ctr.num_values = in->num_values;
+ out->value_ctr.values = talloc_array(mem_ctx,
+ struct drsuapi_DsAttributeValue,
+ in->num_values);
+ W_ERROR_HAVE_NO_MEMORY(out->value_ctr.values);
+
+ blobs = talloc_array(mem_ctx, DATA_BLOB, in->num_values);
+ W_ERROR_HAVE_NO_MEMORY(blobs);
+
+ for (i=0; i < in->num_values; i++) {
+ out->value_ctr.values[i].blob = &blobs[i];
+
+ blobs[i] = data_blob_dup_talloc(blobs, &in->values[i]);
+ W_ERROR_HAVE_NO_MEMORY(blobs[i].data);
+ }
+
+ return WERR_OK;
+}
+
+static WERROR _dsdb_syntax_OID_obj_drsuapi_to_ldb(struct ldb_context *ldb,
+ const struct dsdb_schema *schema,
+ const struct dsdb_attribute *attr,
+ const struct drsuapi_DsReplicaAttribute *in,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message_element *out)
+{
+ uint32_t i;
+
+ out->flags = 0;
+ out->name = talloc_strdup(mem_ctx, attr->lDAPDisplayName);
+ W_ERROR_HAVE_NO_MEMORY(out->name);
+
+ out->num_values = in->value_ctr.num_values;
+ out->values = talloc_array(mem_ctx, struct ldb_val, out->num_values);
+ W_ERROR_HAVE_NO_MEMORY(out->values);
+
+ for (i=0; i < out->num_values; i++) {
+ uint32_t v;
+ const struct dsdb_class *c;
+ const char *str;
+
+ if (in->value_ctr.values[i].blob == NULL) {
+ return WERR_FOOBAR;
+ }
+
+ if (in->value_ctr.values[i].blob->length != 4) {
+ return WERR_FOOBAR;
+ }
+
+ v = IVAL(in->value_ctr.values[i].blob->data, 0);
+
+ c = dsdb_class_by_governsID_id(schema, v);
+ if (!c) {
+ return WERR_FOOBAR;
+ }
+
+ str = talloc_strdup(out->values, c->lDAPDisplayName);
+ W_ERROR_HAVE_NO_MEMORY(str);
+
+ /* the values need to be reversed */
+ out->values[out->num_values - (i + 1)] = data_blob_string_const(str);
+ }
+
+ return WERR_OK;
+}
+
+static WERROR _dsdb_syntax_OID_oid_drsuapi_to_ldb(struct ldb_context *ldb,
+ const struct dsdb_schema *schema,
+ const struct dsdb_attribute *attr,
+ const struct drsuapi_DsReplicaAttribute *in,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message_element *out)
+{
+ uint32_t i;
+
+ out->flags = 0;
+ out->name = talloc_strdup(mem_ctx, attr->lDAPDisplayName);
+ W_ERROR_HAVE_NO_MEMORY(out->name);
+
+ out->num_values = in->value_ctr.num_values;
+ out->values = talloc_array(mem_ctx, struct ldb_val, out->num_values);
+ W_ERROR_HAVE_NO_MEMORY(out->values);
+
+ for (i=0; i < out->num_values; i++) {
+ uint32_t v;
+ WERROR status;
+ const char *str;
+
+ if (in->value_ctr.values[i].blob == NULL) {
+ return WERR_FOOBAR;
+ }
+
+ if (in->value_ctr.values[i].blob->length != 4) {
+ return WERR_FOOBAR;
+ }
+
+ v = IVAL(in->value_ctr.values[i].blob->data, 0);
+
+ status = dsdb_map_int2oid(schema, v, out->values, &str);
+ W_ERROR_NOT_OK_RETURN(status);
+
+ out->values[i] = data_blob_string_const(str);
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_OID_drsuapi_to_ldb(struct ldb_context *ldb,
+ const struct dsdb_schema *schema,
+ const struct dsdb_attribute *attr,
+ const struct drsuapi_DsReplicaAttribute *in,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message_element *out)
+{
+ uint32_t i;
+
+ switch (attr->attributeID_id) {
+ case DRSUAPI_ATTRIBUTE_objectClass:
+ return _dsdb_syntax_OID_obj_drsuapi_to_ldb(ldb, schema, attr, in, mem_ctx, out);
+ case DRSUAPI_ATTRIBUTE_governsID:
+ case DRSUAPI_ATTRIBUTE_attributeID:
+ case DRSUAPI_ATTRIBUTE_attributeSyntax:
+ return _dsdb_syntax_OID_oid_drsuapi_to_ldb(ldb, schema, attr, in, mem_ctx, out);
+ }
+
+ out->flags = 0;
+ out->name = talloc_strdup(mem_ctx, attr->lDAPDisplayName);
+ W_ERROR_HAVE_NO_MEMORY(out->name);
+
+ out->num_values = in->value_ctr.num_values;
+ out->values = talloc_array(mem_ctx, struct ldb_val, out->num_values);
+ W_ERROR_HAVE_NO_MEMORY(out->values);
+
+ for (i=0; i < out->num_values; i++) {
+ uint32_t v;
+ const char *name;
+ char *str;
+
+ if (in->value_ctr.values[i].blob == NULL) {
+ return WERR_FOOBAR;
+ }
+
+ if (in->value_ctr.values[i].blob->length != 4) {
+ return WERR_FOOBAR;
+ }
+
+ v = IVAL(in->value_ctr.values[i].blob->data, 0);
+
+ name = dsdb_lDAPDisplayName_by_id(schema, v);
+ if (!name) {
+ return WERR_FOOBAR;
+ }
+
+ str = talloc_strdup(out->values, name);
+ W_ERROR_HAVE_NO_MEMORY(str);
+
+ out->values[i] = data_blob_string_const(str);
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_OID_ldb_to_drsuapi(struct ldb_context *ldb,
+ const struct dsdb_schema *schema,
+ const struct dsdb_attribute *attr,
+ const struct ldb_message_element *in,
+ TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsReplicaAttribute *out)
+{
+ uint32_t i;
+ DATA_BLOB *blobs;
+
+ if (attr->attributeID_id == 0xFFFFFFFF) {
+ return WERR_FOOBAR;
+ }
+
+ switch (attr->attributeID_id) {
+ case DRSUAPI_ATTRIBUTE_objectClass:
+ case DRSUAPI_ATTRIBUTE_governsID:
+ case DRSUAPI_ATTRIBUTE_attributeID:
+ case DRSUAPI_ATTRIBUTE_attributeSyntax:
+ return dsdb_syntax_FOOBAR_ldb_to_drsuapi(ldb, schema, attr, in, mem_ctx, out);
+ }
+
+ out->attid = attr->attributeID_id;
+ out->value_ctr.num_values = in->num_values;
+ out->value_ctr.values = talloc_array(mem_ctx,
+ struct drsuapi_DsAttributeValue,
+ in->num_values);
+ W_ERROR_HAVE_NO_MEMORY(out->value_ctr.values);
+
+ blobs = talloc_array(mem_ctx, DATA_BLOB, in->num_values);
+ W_ERROR_HAVE_NO_MEMORY(blobs);
+
+ for (i=0; i < in->num_values; i++) {
+ uint32_t v;
+
+ out->value_ctr.values[i].blob = &blobs[i];
+
+ blobs[i] = data_blob_talloc(blobs, NULL, 4);
+ W_ERROR_HAVE_NO_MEMORY(blobs[i].data);
+
+ v = strtol((const char *)in->values[i].data, NULL, 10);
+
+ SIVAL(blobs[i].data, 0, v);
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_UNICODE_drsuapi_to_ldb(struct ldb_context *ldb,
+ const struct dsdb_schema *schema,
+ const struct dsdb_attribute *attr,
+ const struct drsuapi_DsReplicaAttribute *in,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message_element *out)
+{
+ uint32_t i;
+
+ out->flags = 0;
+ out->name = talloc_strdup(mem_ctx, attr->lDAPDisplayName);
+ W_ERROR_HAVE_NO_MEMORY(out->name);
+
+ out->num_values = in->value_ctr.num_values;
+ out->values = talloc_array(mem_ctx, struct ldb_val, out->num_values);
+ W_ERROR_HAVE_NO_MEMORY(out->values);
+
+ for (i=0; i < out->num_values; i++) {
+ char *str;
+
+ if (in->value_ctr.values[i].blob == NULL) {
+ return WERR_FOOBAR;
+ }
+
+ if (in->value_ctr.values[i].blob->length == 0) {
+ return WERR_FOOBAR;
+ }
+
+ if (!convert_string_talloc_convenience(out->values,
+ schema->iconv_convenience,
+ CH_UTF16, CH_UNIX,
+ in->value_ctr.values[i].blob->data,
+ in->value_ctr.values[i].blob->length,
+ (void **)&str, NULL, false)) {
+ return WERR_FOOBAR;
+ }
+
+ out->values[i] = data_blob_string_const(str);
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_UNICODE_ldb_to_drsuapi(struct ldb_context *ldb,
+ const struct dsdb_schema *schema,
+ const struct dsdb_attribute *attr,
+ const struct ldb_message_element *in,
+ TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsReplicaAttribute *out)
+{
+ uint32_t i;
+ DATA_BLOB *blobs;
+
+ if (attr->attributeID_id == 0xFFFFFFFF) {
+ return WERR_FOOBAR;
+ }
+
+ out->attid = attr->attributeID_id;
+ out->value_ctr.num_values = in->num_values;
+ out->value_ctr.values = talloc_array(mem_ctx,
+ struct drsuapi_DsAttributeValue,
+ in->num_values);
+ W_ERROR_HAVE_NO_MEMORY(out->value_ctr.values);
+
+ blobs = talloc_array(mem_ctx, DATA_BLOB, in->num_values);
+ W_ERROR_HAVE_NO_MEMORY(blobs);
+
+ for (i=0; i < in->num_values; i++) {
+ ssize_t ret;
+
+ out->value_ctr.values[i].blob = &blobs[i];
+
+ if (!convert_string_talloc_convenience(blobs, schema->iconv_convenience, CH_UNIX, CH_UTF16,
+ in->values[i].data,
+ in->values[i].length,
+ (void **)&blobs[i].data, NULL, false)) {
+ return WERR_FOOBAR;
+ }
+ blobs[i].length = ret;
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_DN_drsuapi_to_ldb(struct ldb_context *ldb,
+ const struct dsdb_schema *schema,
+ const struct dsdb_attribute *attr,
+ const struct drsuapi_DsReplicaAttribute *in,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message_element *out)
+{
+ uint32_t i;
+ int ret;
+
+ out->flags = 0;
+ out->name = talloc_strdup(mem_ctx, attr->lDAPDisplayName);
+ W_ERROR_HAVE_NO_MEMORY(out->name);
+
+ out->num_values = in->value_ctr.num_values;
+ out->values = talloc_array(mem_ctx, struct ldb_val, out->num_values);
+ W_ERROR_HAVE_NO_MEMORY(out->values);
+
+ for (i=0; i < out->num_values; i++) {
+ struct drsuapi_DsReplicaObjectIdentifier3 id3;
+ enum ndr_err_code ndr_err;
+ DATA_BLOB guid_blob;
+ struct ldb_dn *dn;
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+ if (!tmp_ctx) {
+ W_ERROR_HAVE_NO_MEMORY(tmp_ctx);
+ }
+
+ if (in->value_ctr.values[i].blob == NULL) {
+ talloc_free(tmp_ctx);
+ return WERR_FOOBAR;
+ }
+
+ if (in->value_ctr.values[i].blob->length == 0) {
+ talloc_free(tmp_ctx);
+ return WERR_FOOBAR;
+ }
+
+
+
+ ndr_err = ndr_pull_struct_blob_all(in->value_ctr.values[i].blob,
+ tmp_ctx, schema->iconv_convenience, &id3,
+ (ndr_pull_flags_fn_t)ndr_pull_drsuapi_DsReplicaObjectIdentifier3);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
+ talloc_free(tmp_ctx);
+ return ntstatus_to_werror(status);
+ }
+
+ dn = ldb_dn_new(tmp_ctx, ldb, id3.dn);
+ if (!dn) {
+ talloc_free(tmp_ctx);
+ /* If this fails, it must be out of memory, as it does not do much parsing */
+ W_ERROR_HAVE_NO_MEMORY(dn);
+ }
+
+ ndr_err = ndr_push_struct_blob(&guid_blob, tmp_ctx, schema->iconv_convenience, &id3.guid,
+ (ndr_push_flags_fn_t)ndr_push_GUID);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
+ talloc_free(tmp_ctx);
+ return ntstatus_to_werror(status);
+ }
+
+ ret = ldb_dn_set_extended_component(dn, "GUID", &guid_blob);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return WERR_FOOBAR;
+ }
+
+ talloc_free(guid_blob.data);
+
+ if (id3.__ndr_size_sid) {
+ DATA_BLOB sid_blob;
+ ndr_err = ndr_push_struct_blob(&sid_blob, tmp_ctx, schema->iconv_convenience, &id3.sid,
+ (ndr_push_flags_fn_t)ndr_push_dom_sid);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
+ talloc_free(tmp_ctx);
+ return ntstatus_to_werror(status);
+ }
+
+ ret = ldb_dn_set_extended_component(dn, "SID", &sid_blob);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return WERR_FOOBAR;
+ }
+ }
+
+ out->values[i] = data_blob_string_const(ldb_dn_get_extended_linearized(out->values, dn, 1));
+ talloc_free(tmp_ctx);
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_DN_ldb_to_drsuapi(struct ldb_context *ldb,
+ const struct dsdb_schema *schema,
+ const struct dsdb_attribute *attr,
+ const struct ldb_message_element *in,
+ TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsReplicaAttribute *out)
+{
+ uint32_t i;
+ DATA_BLOB *blobs;
+
+ if (attr->attributeID_id == 0xFFFFFFFF) {
+ return WERR_FOOBAR;
+ }
+
+ out->attid = attr->attributeID_id;
+ out->value_ctr.num_values = in->num_values;
+ out->value_ctr.values = talloc_array(mem_ctx,
+ struct drsuapi_DsAttributeValue,
+ in->num_values);
+ W_ERROR_HAVE_NO_MEMORY(out->value_ctr.values);
+
+ blobs = talloc_array(mem_ctx, DATA_BLOB, in->num_values);
+ W_ERROR_HAVE_NO_MEMORY(blobs);
+
+ for (i=0; i < in->num_values; i++) {
+ struct drsuapi_DsReplicaObjectIdentifier3 id3;
+ enum ndr_err_code ndr_err;
+ const DATA_BLOB *guid_blob, *sid_blob;
+ struct ldb_dn *dn;
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+ W_ERROR_HAVE_NO_MEMORY(tmp_ctx);
+
+ out->value_ctr.values[i].blob = &blobs[i];
+
+ dn = ldb_dn_from_ldb_val(tmp_ctx, ldb, &in->values[i]);
+
+ W_ERROR_HAVE_NO_MEMORY(dn);
+
+ guid_blob = ldb_dn_get_extended_component(dn, "GUID");
+
+ ZERO_STRUCT(id3);
+
+ if (guid_blob) {
+ ndr_err = ndr_pull_struct_blob_all(guid_blob,
+ tmp_ctx, schema->iconv_convenience, &id3.guid,
+ (ndr_pull_flags_fn_t)ndr_pull_GUID);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
+ talloc_free(tmp_ctx);
+ return ntstatus_to_werror(status);
+ }
+ }
+
+ sid_blob = ldb_dn_get_extended_component(dn, "SID");
+ if (sid_blob) {
+
+ ndr_err = ndr_pull_struct_blob_all(sid_blob,
+ tmp_ctx, schema->iconv_convenience, &id3.sid,
+ (ndr_pull_flags_fn_t)ndr_pull_dom_sid);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
+ talloc_free(tmp_ctx);
+ return ntstatus_to_werror(status);
+ }
+ }
+
+ id3.dn = ldb_dn_get_linearized(dn);
+
+ ndr_err = ndr_push_struct_blob(&blobs[i], blobs, schema->iconv_convenience, &id3, (ndr_push_flags_fn_t)ndr_push_drsuapi_DsReplicaObjectIdentifier3);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
+ talloc_free(tmp_ctx);
+ return ntstatus_to_werror(status);
+ }
+ talloc_free(tmp_ctx);
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_DN_BINARY_drsuapi_to_ldb(struct ldb_context *ldb,
+ const struct dsdb_schema *schema,
+ const struct dsdb_attribute *attr,
+ const struct drsuapi_DsReplicaAttribute *in,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message_element *out)
+{
+ uint32_t i;
+
+ out->flags = 0;
+ out->name = talloc_strdup(mem_ctx, attr->lDAPDisplayName);
+ W_ERROR_HAVE_NO_MEMORY(out->name);
+
+ out->num_values = in->value_ctr.num_values;
+ out->values = talloc_array(mem_ctx, struct ldb_val, out->num_values);
+ W_ERROR_HAVE_NO_MEMORY(out->values);
+
+ for (i=0; i < out->num_values; i++) {
+ struct drsuapi_DsReplicaObjectIdentifier3Binary id3b;
+ char *binary;
+ char *str;
+ enum ndr_err_code ndr_err;
+
+ if (in->value_ctr.values[i].blob == NULL) {
+ return WERR_FOOBAR;
+ }
+
+ if (in->value_ctr.values[i].blob->length == 0) {
+ return WERR_FOOBAR;
+ }
+
+ ndr_err = ndr_pull_struct_blob_all(in->value_ctr.values[i].blob,
+ out->values, schema->iconv_convenience, &id3b,
+ (ndr_pull_flags_fn_t)ndr_pull_drsuapi_DsReplicaObjectIdentifier3Binary);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
+ return ntstatus_to_werror(status);
+ }
+
+ /* TODO: handle id3.guid and id3.sid */
+ binary = data_blob_hex_string(out->values, &id3b.binary);
+ W_ERROR_HAVE_NO_MEMORY(binary);
+
+ str = talloc_asprintf(out->values, "B:%u:%s:%s",
+ (unsigned int)(id3b.binary.length * 2), /* because of 2 hex chars per byte */
+ binary,
+ id3b.dn);
+ W_ERROR_HAVE_NO_MEMORY(str);
+
+ /* TODO: handle id3.guid and id3.sid */
+ out->values[i] = data_blob_string_const(str);
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_DN_BINARY_ldb_to_drsuapi(struct ldb_context *ldb,
+ const struct dsdb_schema *schema,
+ const struct dsdb_attribute *attr,
+ const struct ldb_message_element *in,
+ TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsReplicaAttribute *out)
+{
+ uint32_t i;
+ DATA_BLOB *blobs;
+
+ if (attr->attributeID_id == 0xFFFFFFFF) {
+ return WERR_FOOBAR;
+ }
+
+ out->attid = attr->attributeID_id;
+ out->value_ctr.num_values = in->num_values;
+ out->value_ctr.values = talloc_array(mem_ctx,
+ struct drsuapi_DsAttributeValue,
+ in->num_values);
+ W_ERROR_HAVE_NO_MEMORY(out->value_ctr.values);
+
+ blobs = talloc_array(mem_ctx, DATA_BLOB, in->num_values);
+ W_ERROR_HAVE_NO_MEMORY(blobs);
+
+ for (i=0; i < in->num_values; i++) {
+ struct drsuapi_DsReplicaObjectIdentifier3Binary id3b;
+ enum ndr_err_code ndr_err;
+
+ out->value_ctr.values[i].blob = &blobs[i];
+
+ /* TODO: handle id3b.guid and id3b.sid, id3.binary */
+ ZERO_STRUCT(id3b);
+ id3b.dn = (const char *)in->values[i].data;
+ id3b.binary = data_blob(NULL, 0);
+
+ ndr_err = ndr_push_struct_blob(&blobs[i], blobs, schema->iconv_convenience, &id3b,
+ (ndr_push_flags_fn_t)ndr_push_drsuapi_DsReplicaObjectIdentifier3Binary);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
+ return ntstatus_to_werror(status);
+ }
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_PRESENTATION_ADDRESS_drsuapi_to_ldb(struct ldb_context *ldb,
+ const struct dsdb_schema *schema,
+ const struct dsdb_attribute *attr,
+ const struct drsuapi_DsReplicaAttribute *in,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message_element *out)
+{
+ uint32_t i;
+
+ out->flags = 0;
+ out->name = talloc_strdup(mem_ctx, attr->lDAPDisplayName);
+ W_ERROR_HAVE_NO_MEMORY(out->name);
+
+ out->num_values = in->value_ctr.num_values;
+ out->values = talloc_array(mem_ctx, struct ldb_val, out->num_values);
+ W_ERROR_HAVE_NO_MEMORY(out->values);
+
+ for (i=0; i < out->num_values; i++) {
+ uint32_t len;
+ char *str;
+
+ if (in->value_ctr.values[i].blob == NULL) {
+ return WERR_FOOBAR;
+ }
+
+ if (in->value_ctr.values[i].blob->length < 4) {
+ return WERR_FOOBAR;
+ }
+
+ len = IVAL(in->value_ctr.values[i].blob->data, 0);
+
+ if (len != in->value_ctr.values[i].blob->length) {
+ return WERR_FOOBAR;
+ }
+
+ if (!convert_string_talloc_convenience(out->values, schema->iconv_convenience, CH_UTF16, CH_UNIX,
+ in->value_ctr.values[i].blob->data+4,
+ in->value_ctr.values[i].blob->length-4,
+ (void **)&str, NULL, false)) {
+ return WERR_FOOBAR;
+ }
+
+ out->values[i] = data_blob_string_const(str);
+ }
+
+ return WERR_OK;
+}
+
+static WERROR dsdb_syntax_PRESENTATION_ADDRESS_ldb_to_drsuapi(struct ldb_context *ldb,
+ const struct dsdb_schema *schema,
+ const struct dsdb_attribute *attr,
+ const struct ldb_message_element *in,
+ TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsReplicaAttribute *out)
+{
+ uint32_t i;
+ DATA_BLOB *blobs;
+
+ if (attr->attributeID_id == 0xFFFFFFFF) {
+ return WERR_FOOBAR;
+ }
+
+ out->attid = attr->attributeID_id;
+ out->value_ctr.num_values = in->num_values;
+ out->value_ctr.values = talloc_array(mem_ctx,
+ struct drsuapi_DsAttributeValue,
+ in->num_values);
+ W_ERROR_HAVE_NO_MEMORY(out->value_ctr.values);
+
+ blobs = talloc_array(mem_ctx, DATA_BLOB, in->num_values);
+ W_ERROR_HAVE_NO_MEMORY(blobs);
+
+ for (i=0; i < in->num_values; i++) {
+ uint8_t *data;
+ size_t ret;
+
+ out->value_ctr.values[i].blob = &blobs[i];
+
+ if (!convert_string_talloc_convenience(blobs, schema->iconv_convenience, CH_UNIX, CH_UTF16,
+ in->values[i].data,
+ in->values[i].length,
+ (void **)&data, &ret, false)) {
+ return WERR_FOOBAR;
+ }
+
+ blobs[i] = data_blob_talloc(blobs, NULL, 4 + ret);
+ W_ERROR_HAVE_NO_MEMORY(blobs[i].data);
+
+ SIVAL(blobs[i].data, 0, 4 + ret);
+
+ if (ret > 0) {
+ memcpy(blobs[i].data + 4, data, ret);
+ talloc_free(data);
+ }
+ }
+
+ return WERR_OK;
+}
+
+#define OMOBJECTCLASS(val) { .length = sizeof(val) - 1, .data = discard_const_p(uint8_t, val) }
+
+static const struct dsdb_syntax dsdb_syntaxes[] = {
+ {
+ .name = "Boolean",
+ .ldap_oid = "1.3.6.1.4.1.1466.115.121.1.7",
+ .oMSyntax = 1,
+ .attributeSyntax_oid = "2.5.5.8",
+ .drsuapi_to_ldb = dsdb_syntax_BOOL_drsuapi_to_ldb,
+ .ldb_to_drsuapi = dsdb_syntax_BOOL_ldb_to_drsuapi,
+ .equality = "booleanMatch",
+ .comment = "Boolean"
+ },{
+ .name = "Integer",
+ .ldap_oid = LDB_SYNTAX_INTEGER,
+ .oMSyntax = 2,
+ .attributeSyntax_oid = "2.5.5.9",
+ .drsuapi_to_ldb = dsdb_syntax_INT32_drsuapi_to_ldb,
+ .ldb_to_drsuapi = dsdb_syntax_INT32_ldb_to_drsuapi,
+ .equality = "integerMatch",
+ .comment = "Integer",
+ },{
+ .name = "String(Octet)",
+ .ldap_oid = LDB_SYNTAX_OCTET_STRING,
+ .oMSyntax = 4,
+ .attributeSyntax_oid = "2.5.5.10",
+ .drsuapi_to_ldb = dsdb_syntax_DATA_BLOB_drsuapi_to_ldb,
+ .ldb_to_drsuapi = dsdb_syntax_DATA_BLOB_ldb_to_drsuapi,
+ .equality = "octetStringMatch",
+ .comment = "Octet String",
+ },{
+ .name = "String(Sid)",
+ .ldap_oid = LDB_SYNTAX_OCTET_STRING,
+ .oMSyntax = 4,
+ .attributeSyntax_oid = "2.5.5.17",
+ .drsuapi_to_ldb = dsdb_syntax_DATA_BLOB_drsuapi_to_ldb,
+ .ldb_to_drsuapi = dsdb_syntax_DATA_BLOB_ldb_to_drsuapi,
+ .equality = "octetStringMatch",
+ .comment = "Octet String - Security Identifier (SID)",
+ .ldb_syntax = LDB_SYNTAX_SAMBA_SID
+ },{
+ .name = "String(Object-Identifier)",
+ .ldap_oid = "1.3.6.1.4.1.1466.115.121.1.38",
+ .oMSyntax = 6,
+ .attributeSyntax_oid = "2.5.5.2",
+ .drsuapi_to_ldb = dsdb_syntax_OID_drsuapi_to_ldb,
+ .ldb_to_drsuapi = dsdb_syntax_OID_ldb_to_drsuapi,
+ .equality = "caseIgnoreMatch", /* Would use "objectIdentifierMatch" but most are ldap attribute/class names */
+ .comment = "OID String",
+ .ldb_syntax = LDB_SYNTAX_DIRECTORY_STRING
+ },{
+ .name = "Enumeration",
+ .ldap_oid = LDB_SYNTAX_INTEGER,
+ .oMSyntax = 10,
+ .attributeSyntax_oid = "2.5.5.9",
+ .drsuapi_to_ldb = dsdb_syntax_INT32_drsuapi_to_ldb,
+ .ldb_to_drsuapi = dsdb_syntax_INT32_ldb_to_drsuapi,
+ },{
+ /* not used in w2k3 forest */
+ .name = "String(Numeric)",
+ .ldap_oid = "1.3.6.1.4.1.1466.115.121.1.36",
+ .oMSyntax = 18,
+ .attributeSyntax_oid = "2.5.5.6",
+ .drsuapi_to_ldb = dsdb_syntax_DATA_BLOB_drsuapi_to_ldb,
+ .ldb_to_drsuapi = dsdb_syntax_DATA_BLOB_ldb_to_drsuapi,
+ .equality = "numericStringMatch",
+ .substring = "numericStringSubstringsMatch",
+ .comment = "Numeric String"
+ },{
+ .name = "String(Printable)",
+ .ldap_oid = "1.3.6.1.4.1.1466.115.121.1.44",
+ .oMSyntax = 19,
+ .attributeSyntax_oid = "2.5.5.5",
+ .drsuapi_to_ldb = dsdb_syntax_DATA_BLOB_drsuapi_to_ldb,
+ .ldb_to_drsuapi = dsdb_syntax_DATA_BLOB_ldb_to_drsuapi,
+ },{
+ .name = "String(Teletex)",
+ .ldap_oid = "1.2.840.113556.1.4.905",
+ .oMSyntax = 20,
+ .attributeSyntax_oid = "2.5.5.4",
+ .drsuapi_to_ldb = dsdb_syntax_DATA_BLOB_drsuapi_to_ldb,
+ .ldb_to_drsuapi = dsdb_syntax_DATA_BLOB_ldb_to_drsuapi,
+ .equality = "caseIgnoreMatch",
+ .substring = "caseIgnoreSubstringsMatch",
+ .comment = "Case Insensitive String",
+ .ldb_syntax = LDB_SYNTAX_DIRECTORY_STRING,
+ },{
+ .name = "String(IA5)",
+ .ldap_oid = "1.3.6.1.4.1.1466.115.121.1.26",
+ .oMSyntax = 22,
+ .attributeSyntax_oid = "2.5.5.5",
+ .drsuapi_to_ldb = dsdb_syntax_DATA_BLOB_drsuapi_to_ldb,
+ .ldb_to_drsuapi = dsdb_syntax_DATA_BLOB_ldb_to_drsuapi,
+ .equality = "caseExactIA5Match",
+ .comment = "Printable String"
+ },{
+ .name = "String(UTC-Time)",
+ .ldap_oid = "1.3.6.1.4.1.1466.115.121.1.53",
+ .oMSyntax = 23,
+ .attributeSyntax_oid = "2.5.5.11",
+ .drsuapi_to_ldb = dsdb_syntax_NTTIME_UTC_drsuapi_to_ldb,
+ .ldb_to_drsuapi = dsdb_syntax_NTTIME_UTC_ldb_to_drsuapi,
+ .equality = "generalizedTimeMatch",
+ .comment = "UTC Time",
+ },{
+ .name = "String(Generalized-Time)",
+ .ldap_oid = "1.3.6.1.4.1.1466.115.121.1.24",
+ .oMSyntax = 24,
+ .attributeSyntax_oid = "2.5.5.11",
+ .drsuapi_to_ldb = dsdb_syntax_NTTIME_drsuapi_to_ldb,
+ .ldb_to_drsuapi = dsdb_syntax_NTTIME_ldb_to_drsuapi,
+ .equality = "generalizedTimeMatch",
+ .comment = "Generalized Time",
+ .ldb_syntax = LDB_SYNTAX_UTC_TIME,
+ },{
+ /* not used in w2k3 schema */
+ .name = "String(Case Sensitive)",
+ .ldap_oid = "1.2.840.113556.1.4.1362",
+ .oMSyntax = 27,
+ .attributeSyntax_oid = "2.5.5.3",
+ .drsuapi_to_ldb = dsdb_syntax_FOOBAR_drsuapi_to_ldb,
+ .ldb_to_drsuapi = dsdb_syntax_FOOBAR_ldb_to_drsuapi,
+ },{
+ .name = "String(Unicode)",
+ .ldap_oid = LDB_SYNTAX_DIRECTORY_STRING,
+ .oMSyntax = 64,
+ .attributeSyntax_oid = "2.5.5.12",
+ .drsuapi_to_ldb = dsdb_syntax_UNICODE_drsuapi_to_ldb,
+ .ldb_to_drsuapi = dsdb_syntax_UNICODE_ldb_to_drsuapi,
+ .equality = "caseIgnoreMatch",
+ .substring = "caseIgnoreSubstringsMatch",
+ .comment = "Directory String",
+ },{
+ .name = "Interval/LargeInteger",
+ .ldap_oid = "1.2.840.113556.1.4.906",
+ .oMSyntax = 65,
+ .attributeSyntax_oid = "2.5.5.16",
+ .drsuapi_to_ldb = dsdb_syntax_INT64_drsuapi_to_ldb,
+ .ldb_to_drsuapi = dsdb_syntax_INT64_ldb_to_drsuapi,
+ .equality = "integerMatch",
+ .comment = "Large Integer",
+ .ldb_syntax = LDB_SYNTAX_INTEGER,
+ },{
+ .name = "String(NT-Sec-Desc)",
+ .ldap_oid = LDB_SYNTAX_SAMBA_SECURITY_DESCRIPTOR,
+ .oMSyntax = 66,
+ .attributeSyntax_oid = "2.5.5.15",
+ .drsuapi_to_ldb = dsdb_syntax_DATA_BLOB_drsuapi_to_ldb,
+ .ldb_to_drsuapi = dsdb_syntax_DATA_BLOB_ldb_to_drsuapi,
+ },{
+ .name = "Object(DS-DN)",
+ .ldap_oid = LDB_SYNTAX_DN,
+ .oMSyntax = 127,
+ .oMObjectClass = OMOBJECTCLASS("\x2b\x0c\x02\x87\x73\x1c\x00\x85\x4a"),
+ .attributeSyntax_oid = "2.5.5.1",
+ .drsuapi_to_ldb = dsdb_syntax_DN_drsuapi_to_ldb,
+ .ldb_to_drsuapi = dsdb_syntax_DN_ldb_to_drsuapi,
+ .equality = "distinguishedNameMatch",
+ .comment = "Object(DS-DN) == a DN",
+ },{
+ .name = "Object(DN-Binary)",
+ .ldap_oid = "1.2.840.113556.1.4.903",
+ .oMSyntax = 127,
+ .oMObjectClass = OMOBJECTCLASS("\x2a\x86\x48\x86\xf7\x14\x01\x01\x01\x0b"),
+ .attributeSyntax_oid = "2.5.5.7",
+ .drsuapi_to_ldb = dsdb_syntax_DN_BINARY_drsuapi_to_ldb,
+ .ldb_to_drsuapi = dsdb_syntax_DN_BINARY_ldb_to_drsuapi,
+ .equality = "octetStringMatch",
+ .comment = "OctetString: Binary+DN",
+ .ldb_syntax = LDB_SYNTAX_OCTET_STRING,
+ },{
+ /* not used in w2k3 schema */
+ .name = "Object(OR-Name)",
+ .ldap_oid = "1.2.840.113556.1.4.1221",
+ .oMSyntax = 127,
+ .oMObjectClass = OMOBJECTCLASS("\x56\x06\x01\x02\x05\x0b\x1D"),
+ .attributeSyntax_oid = "2.5.5.7",
+ .drsuapi_to_ldb = dsdb_syntax_FOOBAR_drsuapi_to_ldb,
+ .ldb_to_drsuapi = dsdb_syntax_FOOBAR_ldb_to_drsuapi,
+ },{
+ /*
+ * TODO: verify if DATA_BLOB is correct here...!
+ *
+ * repsFrom and repsTo are the only attributes using
+ * this attribute syntax, but they're not replicated...
+ */
+ .name = "Object(Replica-Link)",
+ .ldap_oid = "1.3.6.1.4.1.1466.115.121.1.40",
+ .oMSyntax = 127,
+ .oMObjectClass = OMOBJECTCLASS("\x2a\x86\x48\x86\xf7\x14\x01\x01\x01\x06"),
+ .attributeSyntax_oid = "2.5.5.10",
+ .drsuapi_to_ldb = dsdb_syntax_DATA_BLOB_drsuapi_to_ldb,
+ .ldb_to_drsuapi = dsdb_syntax_DATA_BLOB_ldb_to_drsuapi,
+ },{
+ .name = "Object(Presentation-Address)",
+ .ldap_oid = "1.3.6.1.4.1.1466.115.121.1.43",
+ .oMSyntax = 127,
+ .oMObjectClass = OMOBJECTCLASS("\x2b\x0c\x02\x87\x73\x1c\x00\x85\x5c"),
+ .attributeSyntax_oid = "2.5.5.13",
+ .drsuapi_to_ldb = dsdb_syntax_PRESENTATION_ADDRESS_drsuapi_to_ldb,
+ .ldb_to_drsuapi = dsdb_syntax_PRESENTATION_ADDRESS_ldb_to_drsuapi,
+ .comment = "Presentation Address"
+ },{
+ /* not used in w2k3 schema */
+ .name = "Object(Access-Point)",
+ .ldap_oid = "1.3.6.1.4.1.1466.115.121.1.2",
+ .oMSyntax = 127,
+ .oMObjectClass = OMOBJECTCLASS("\x2b\x0c\x02\x87\x73\x1c\x00\x85\x3e"),
+ .attributeSyntax_oid = "2.5.5.14",
+ .drsuapi_to_ldb = dsdb_syntax_FOOBAR_drsuapi_to_ldb,
+ .ldb_to_drsuapi = dsdb_syntax_FOOBAR_ldb_to_drsuapi,
+ },{
+ /* not used in w2k3 schema */
+ .name = "Object(DN-String)",
+ .ldap_oid = "1.2.840.113556.1.4.904",
+ .oMSyntax = 127,
+ .oMObjectClass = OMOBJECTCLASS("\x2a\x86\x48\x86\xf7\x14\x01\x01\x01\x0c"),
+ .attributeSyntax_oid = "2.5.5.14",
+ .drsuapi_to_ldb = dsdb_syntax_DN_BINARY_drsuapi_to_ldb,
+ .ldb_to_drsuapi = dsdb_syntax_DN_BINARY_ldb_to_drsuapi,
+ .equality = "octetStringMatch",
+ .comment = "OctetString: String+DN",
+ .ldb_syntax = LDB_SYNTAX_OCTET_STRING,
+ }
+};
+
+const struct dsdb_syntax *find_syntax_map_by_ad_oid(const char *ad_oid)
+{
+ int i;
+ for (i=0; dsdb_syntaxes[i].ldap_oid; i++) {
+ if (strcasecmp(ad_oid, dsdb_syntaxes[i].attributeSyntax_oid) == 0) {
+ return &dsdb_syntaxes[i];
+ }
+ }
+ return NULL;
+}
+
+const struct dsdb_syntax *find_syntax_map_by_ad_syntax(int oMSyntax)
+{
+ int i;
+ for (i=0; dsdb_syntaxes[i].ldap_oid; i++) {
+ if (oMSyntax == dsdb_syntaxes[i].oMSyntax) {
+ return &dsdb_syntaxes[i];
+ }
+ }
+ return NULL;
+}
+
+const struct dsdb_syntax *find_syntax_map_by_standard_oid(const char *standard_oid)
+{
+ int i;
+ for (i=0; dsdb_syntaxes[i].ldap_oid; i++) {
+ if (strcasecmp(standard_oid, dsdb_syntaxes[i].ldap_oid) == 0) {
+ return &dsdb_syntaxes[i];
+ }
+ }
+ return NULL;
+}
+const struct dsdb_syntax *dsdb_syntax_for_attribute(const struct dsdb_attribute *attr)
+{
+ uint32_t i;
+
+ for (i=0; i < ARRAY_SIZE(dsdb_syntaxes); i++) {
+ if (attr->oMSyntax != dsdb_syntaxes[i].oMSyntax) continue;
+
+ if (attr->oMObjectClass.length != dsdb_syntaxes[i].oMObjectClass.length) continue;
+
+ if (attr->oMObjectClass.length) {
+ int ret;
+ ret = memcmp(attr->oMObjectClass.data,
+ dsdb_syntaxes[i].oMObjectClass.data,
+ attr->oMObjectClass.length);
+ if (ret != 0) continue;
+ }
+
+ if (strcmp(attr->attributeSyntax_oid, dsdb_syntaxes[i].attributeSyntax_oid) != 0) continue;
+
+ return &dsdb_syntaxes[i];
+ }
+
+ return NULL;
+}
+
+WERROR dsdb_attribute_drsuapi_to_ldb(struct ldb_context *ldb,
+ const struct dsdb_schema *schema,
+ const struct drsuapi_DsReplicaAttribute *in,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message_element *out)
+{
+ const struct dsdb_attribute *sa;
+
+ sa = dsdb_attribute_by_attributeID_id(schema, in->attid);
+ if (!sa) {
+ return WERR_FOOBAR;
+ }
+
+ return sa->syntax->drsuapi_to_ldb(ldb, schema, sa, in, mem_ctx, out);
+}
+
+WERROR dsdb_attribute_ldb_to_drsuapi(struct ldb_context *ldb,
+ const struct dsdb_schema *schema,
+ const struct ldb_message_element *in,
+ TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsReplicaAttribute *out)
+{
+ const struct dsdb_attribute *sa;
+
+ sa = dsdb_attribute_by_lDAPDisplayName(schema, in->name);
+ if (!sa) {
+ return WERR_FOOBAR;
+ }
+
+ return sa->syntax->ldb_to_drsuapi(ldb, schema, sa, in, mem_ctx, out);
+}
diff --git a/source4/dynconfig/config.mk b/source4/dynconfig/config.mk
new file mode 100644
index 0000000000..f79cdb8e75
--- /dev/null
+++ b/source4/dynconfig/config.mk
@@ -0,0 +1,25 @@
+[SUBSYSTEM::DYNCONFIG]
+
+DYNCONFIG_OBJ_FILES = $(dynconfigsrcdir)/dynconfig.o
+
+# set these to where to find various files
+# These can be overridden by command line switches (see samba(8))
+# or in smb.conf (see smb.conf(5))
+CONFIG4FILE = $(sysconfdir)/smb.conf
+pkgconfigdir = $(libdir)/pkgconfig
+LMHOSTSFILE4 = $(sysconfdir)/lmhosts
+
+$(dynconfigsrcdir)/dynconfig.o: CFLAGS+=-DCONFIGFILE=\"$(CONFIG4FILE)\" -DBINDIR=\"$(bindir)\" \
+ -DLMHOSTSFILE=\"$(LMHOSTSFILE4)\" \
+ -DLOCKDIR=\"$(lockdir)\" -DPIDDIR=\"$(piddir)\" -DDATADIR=\"$(datadir)\" \
+ -DLOGFILEBASE=\"$(logfilebase)\" \
+ -DCONFIGDIR=\"$(sysconfdir)\" -DNCALRPCDIR=\"$(ncalrpcdir)\" \
+ -DSWATDIR=\"$(swatdir)\" \
+ -DPRIVATE_DIR=\"$(privatedir)\" \
+ -DMODULESDIR=\"$(modulesdir)\" \
+ -DTORTUREDIR=\"$(torturedir)\" \
+ -DSETUPDIR=\"$(setupdir)\" \
+ -DWINBINDD_PRIVILEGED_SOCKET_DIR=\"$(winbindd_privileged_socket_dir)\" \
+ -DWINBINDD_SOCKET_DIR=\"$(winbindd_socket_dir)\" \
+ -DNTP_SIGND_SOCKET_DIR=\"$(ntp_signd_socket_dir)\"
+
diff --git a/source4/dynconfig/dynconfig.c b/source4/dynconfig/dynconfig.c
new file mode 100644
index 0000000000..e0cabef317
--- /dev/null
+++ b/source4/dynconfig/dynconfig.c
@@ -0,0 +1,89 @@
+/*
+ Unix SMB/CIFS implementation.
+ Copyright (C) 2001 by Martin Pool <mbp@samba.org>
+ Copyright (C) Jim McDonough (jmcd@us.ibm.com) 2003.
+ Copyright (C) Stefan 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+/**
+ * @file dynconfig.c
+ *
+ * @brief Global configurations, initialized to configured defaults.
+ *
+ * This file should be the only file that depends on path
+ * configuration (--prefix, etc), so that if ./configure is re-run,
+ * all programs will be appropriately updated. Everything else in
+ * Samba should import extern variables from here, rather than relying
+ * on preprocessor macros.
+ *
+ * Eventually some of these may become even more variable, so that
+ * they can for example consistently be set across the whole of Samba
+ * by command-line parameters, config file entries, or environment
+ * variables.
+ *
+ * @todo Perhaps eventually these should be merged into the parameter
+ * table? There's kind of a chicken-and-egg situation there...
+ **/
+
+/** Directory with generic binaries */
+_PUBLIC_ const char *dyn_BINDIR = BINDIR;
+
+/**< Location of smb.conf file. **/
+_PUBLIC_ const char *dyn_CONFIGFILE = CONFIGFILE;
+
+/** Log file directory. **/
+_PUBLIC_ const char *dyn_LOGFILEBASE = LOGFILEBASE;
+
+/** Directory for local RPC (ncalrpc: transport) */
+_PUBLIC_ const char *dyn_NCALRPCDIR = NCALRPCDIR;
+
+/** Statically configured LanMan hosts. **/
+_PUBLIC_ const char *dyn_LMHOSTSFILE = LMHOSTSFILE;
+
+/** Samba data directory. */
+_PUBLIC_ const char *dyn_DATADIR = DATADIR;
+
+_PUBLIC_ const char *dyn_MODULESDIR = MODULESDIR;
+
+/**
+ * @brief Directory holding lock files.
+ *
+ * Not writable, but used to set a default in the parameter table.
+ **/
+_PUBLIC_ const char *dyn_LOCKDIR = LOCKDIR;
+
+/** pid file directory */
+_PUBLIC_ const char *dyn_PIDDIR = PIDDIR;
+
+/** Private data directory; holds ldb files and the like */
+_PUBLIC_ const char *dyn_PRIVATE_DIR = PRIVATE_DIR;
+
+/** SWAT directory */
+_PUBLIC_ const char *dyn_SWATDIR = SWATDIR;
+
+/** SETUP files (source files used by the provision) */
+_PUBLIC_ const char *dyn_SETUPDIR = SETUPDIR;
+
+/** Where to find the winbindd socket */
+_PUBLIC_ const char *dyn_WINBINDD_SOCKET_DIR = WINBINDD_SOCKET_DIR;
+
+/** Where to find the winbindd privileged socket */
+_PUBLIC_ const char *dyn_WINBINDD_PRIVILEGED_SOCKET_DIR = WINBINDD_PRIVILEGED_SOCKET_DIR;
+
+/** Where to find the NTP signing deamon socket */
+_PUBLIC_ const char *dyn_NTP_SIGND_SOCKET_DIR = NTP_SIGND_SOCKET_DIR;
diff --git a/source4/dynconfig/dynconfig.h b/source4/dynconfig/dynconfig.h
new file mode 100644
index 0000000000..1bba1f07db
--- /dev/null
+++ b/source4/dynconfig/dynconfig.h
@@ -0,0 +1,42 @@
+/*
+ Unix SMB/CIFS implementation.
+ Copyright (C) 2001 by Martin Pool <mbp@samba.org>
+ Copyright (C) Jim McDonough (jmcd@us.ibm.com) 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 <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file dynconfig.h
+ *
+ * @brief Exported global configurations.
+ **/
+
+extern const char *dyn_BINDIR;
+extern const char *dyn_CONFIGFILE;
+extern const char *dyn_NCALRPCDIR;
+extern const char *dyn_LOGFILEBASE;
+extern const char *dyn_LMHOSTSFILE;
+extern const char *dyn_DATADIR;
+extern const char *dyn_MODULESDIR;
+extern const char *dyn_LOCKDIR;
+extern const char *dyn_PIDDIR;
+extern const char *dyn_PRIVATE_DIR;
+extern const char *dyn_SWATDIR;
+extern const char *dyn_JSDIR;
+extern const char *dyn_SETUPDIR;
+extern const char *dyn_WINBINDD_SOCKET_DIR;
+extern const char *dyn_WINBINDD_PRIVILEGED_SOCKET_DIR;
+extern const char *dyn_NTP_SIGND_SOCKET_DIR;
diff --git a/source4/headermap.txt b/source4/headermap.txt
new file mode 100644
index 0000000000..8287044622
--- /dev/null
+++ b/source4/headermap.txt
@@ -0,0 +1,108 @@
+# FIXME: This file should be autogenerated by the build system at some
+# point
+../lib/util/util.h: util.h
+../lib/util/debug.h: util/debug.h
+../lib/util/mutex.h: util/mutex.h
+../lib/util/attr.h: util/attr.h
+../lib/util/byteorder.h: util/byteorder.h
+../lib/util/safe_string.h: util/safe_string.h
+../lib/util/memory.h: util/memory.h
+../lib/util/talloc_stack.h: util/talloc_stack.h
+../lib/util/xfile.h: util/xfile.h
+lib/tdr/tdr.h: tdr.h
+librpc/rpc/dcerpc.h: dcerpc.h
+lib/ldb/include/ldb.h: ldb.h
+lib/ldb/include/ldb_errors.h: ldb_errors.h
+auth/gensec/gensec.h: gensec.h
+../librpc/ndr/libndr.h: ndr.h
+librpc/ndr/libndr.h: ndr.h
+lib/registry/registry.h: registry.h
+../libcli/util/werror.h: core/werror.h
+../libcli/util/doserr.h: core/doserr.h
+../libcli/util/ntstatus.h: core/ntstatus.h
+libcli/util/werror.h: core/werror.h
+libcli/util/doserr.h: core/doserr.h
+libcli/util/ntstatus.h: core/ntstatus.h
+libcli/cldap/cldap.h: cldap.h
+auth/credentials/credentials.h: credentials.h
+auth/credentials/credentials_krb5.h: credentials/krb5.h
+rpc_server/dcerpc_server.h: dcerpc_server.h
+rpc_server/common/common.h: dcerpc_server/common.h
+libcli/auth/credentials.h: domain_credentials.h
+../lib/util/charset/charset.h: charset.h
+libcli/ldap/ldap.h: ldap-util.h
+../lib/torture/torture.h: torture.h
+libcli/libcli.h: client.h
+librpc/gen_ndr/ntp_signd.h: gen_ndr/ntp_signd.h
+lib/cmdline/popt_common.h: samba/popt.h
+../lib/util/dlinklist.h: dlinklist.h
+../lib/util/data_blob.h: util/data_blob.h
+../lib/util/time.h: util/time.h
+version.h: samba/version.h
+param/param.h: param.h
+../lib/util/asn1.h: samba/asn1.h
+../libcli/util/error.h: core/error.h
+lib/tdb_wrap.h: tdb_wrap.h
+lib/ldb_wrap.h: ldb_wrap.h
+torture/smbtorture.h: smbtorture.h
+param/share.h: share.h
+../lib/util/util_tdb.h: util_tdb.h
+../lib/util/util_ldb.h: util_ldb.h
+../lib/util/wrap_xattr.h: wrap_xattr.h
+../libcli/ldap/ldap_message.h: ldap_message.h
+../libcli/ldap/ldap_errors.h: ldap_errors.h
+../libcli/ldap/ldap_ndr.h: ldap_ndr.h
+../lib/tevent/tevent.h: tevent.h
+../lib/tevent/tevent_internal.h: tevent_internal.h
+auth/session.h: samba/session.h
+../talloc/talloc.h: talloc.h
+
+# pidl generated files
+# we need some of them with and without '../'
+librpc/gen_ndr/dcerpc.h: gen_ndr/dcerpc.h
+librpc/gen_ndr/ndr_dcerpc.h: gen_ndr/ndr_dcerpc.h
+librpc/gen_ndr/winbind.h: gen_ndr/winbind.h
+librpc/gen_ndr/netlogon.h: gen_ndr/netlogon.h
+librpc/gen_ndr/ndr_misc.h: gen_ndr/ndr_misc.h
+librpc/gen_ndr/mgmt.h: gen_ndr/mgmt.h
+librpc/gen_ndr/ndr_mgmt.h: gen_ndr/ndr_mgmt.h
+librpc/gen_ndr/ndr_mgmt_c.h: gen_ndr/ndr_mgmt_c.h
+librpc/gen_ndr/epmapper.h: gen_ndr/epmapper.h
+librpc/gen_ndr/ndr_epmapper.h: gen_ndr/ndr_epmapper.h
+librpc/gen_ndr/ndr_epmapper_c.h: gen_ndr/ndr_epmapper_c.h
+librpc/gen_ndr/ndr_atsvc.h: gen_ndr/ndr_atsvc.h
+librpc/gen_ndr/atsvc.h: gen_ndr/atsvc.h
+librpc/gen_ndr/ndr_atsvc_c.h: gen_ndr/ndr_atsvc_c.h
+librpc/gen_ndr/misc.h: gen_ndr/misc.h
+librpc/gen_ndr/lsa.h: gen_ndr/lsa.h
+librpc/gen_ndr/samr.h: gen_ndr/samr.h
+librpc/gen_ndr/ndr_samr.h: gen_ndr/ndr_samr.h
+librpc/gen_ndr/ndr_samr_c.h: gen_ndr/ndr_samr_c.h
+librpc/gen_ndr/security.h: gen_ndr/security.h
+librpc/gen_ndr/server_id.h: gen_ndr/server_id.h
+librpc/gen_ndr/nbt.h: gen_ndr/nbt.h
+librpc/gen_ndr/svcctl.h: gen_ndr/svcctl.h
+librpc/gen_ndr/ndr_svcctl.h: gen_ndr/ndr_svcctl.h
+librpc/gen_ndr/ndr_svcctl_c.h: gen_ndr/ndr_svcctl_c.h
+../librpc/gen_ndr/netlogon.h: gen_ndr/netlogon.h
+../librpc/gen_ndr/ndr_misc.h: gen_ndr/ndr_misc.h
+../librpc/gen_ndr/mgmt.h: gen_ndr/mgmt.h
+../librpc/gen_ndr/ndr_mgmt.h: gen_ndr/ndr_mgmt.h
+../librpc/gen_ndr/ndr_mgmt_c.h: gen_ndr/ndr_mgmt_c.h
+../librpc/gen_ndr/epmapper.h: gen_ndr/epmapper.h
+../librpc/gen_ndr/ndr_epmapper.h: gen_ndr/ndr_epmapper.h
+../librpc/gen_ndr/ndr_epmapper_c.h: gen_ndr/ndr_epmapper_c.h
+../librpc/gen_ndr/ndr_atsvc.h: gen_ndr/ndr_atsvc.h
+../librpc/gen_ndr/atsvc.h: gen_ndr/atsvc.h
+../librpc/gen_ndr/ndr_atsvc_c.h: gen_ndr/ndr_atsvc_c.h
+../librpc/gen_ndr/misc.h: gen_ndr/misc.h
+../librpc/gen_ndr/lsa.h: gen_ndr/lsa.h
+../librpc/gen_ndr/samr.h: gen_ndr/samr.h
+../librpc/gen_ndr/ndr_samr.h: gen_ndr/ndr_samr.h
+../librpc/gen_ndr/ndr_samr_c.h: gen_ndr/ndr_samr_c.h
+../librpc/gen_ndr/security.h: gen_ndr/security.h
+../librpc/gen_ndr/server_id.h: gen_ndr/server_id.h
+../librpc/gen_ndr/nbt.h: gen_ndr/nbt.h
+../librpc/gen_ndr/svcctl.h: gen_ndr/svcctl.h
+../librpc/gen_ndr/ndr_svcctl.h: gen_ndr/ndr_svcctl.h
+../librpc/gen_ndr/ndr_svcctl_c.h: gen_ndr/ndr_svcctl_c.h
diff --git a/source4/heimdal/HEIMDAL-LICENCE.txt b/source4/heimdal/HEIMDAL-LICENCE.txt
new file mode 100644
index 0000000000..90ff52309f
--- /dev/null
+++ b/source4/heimdal/HEIMDAL-LICENCE.txt
@@ -0,0 +1,45 @@
+This is a copy of the standard copyright notice on most files in the
+heimdal tree. This license is confidered to be GPL compatible by the
+Free Software Foundation (see http://www.fsf.org/licensing/licenses/index_html#GPLCompatibleLicenses)
+
+Note that the list of copyright holders varies between the individial
+files. Also note that this Samba4 MODIFIED VERSION may depend on GPL'ed
+libraries.
+
+Many thanks to the Heimdal developers for their support and
+cooperation in the use of the heimdal code in Samba.
+
+
+/*
+ * Copyright (c) 1997 - 2005 Kungliga Tekniska Högskolan and others.
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
diff --git a/source4/heimdal/README b/source4/heimdal/README
new file mode 100644
index 0000000000..3b938248fc
--- /dev/null
+++ b/source4/heimdal/README
@@ -0,0 +1,19 @@
+$Id$
+
+Heimdal is a Kerberos 5 implementation.
+
+Please see the manual in doc, by default installed in
+/usr/heimdal/info/heimdal.info for information on how to install.
+There are also briefer man pages for most of the commands.
+
+Bug reports and bugs are appreciated, see more under Bug reports in
+the manual on how we prefer them.
+
+For more information see the web-page at
+<http://www.h5l.org/> or the mailing lists:
+
+heimdal-announce@sics.se low-volume announcement
+heimdal-discuss@sics.se high-volume discussion
+
+send a mail to heimdal-announce-request@sics.se and
+heimdal-discuss-request@sics.se respectively to subscribe.
diff --git a/source4/heimdal/cf/check-var.m4 b/source4/heimdal/cf/check-var.m4
new file mode 100644
index 0000000000..f81f3524c1
--- /dev/null
+++ b/source4/heimdal/cf/check-var.m4
@@ -0,0 +1,27 @@
+dnl $Id$
+dnl
+dnl rk_CHECK_VAR(variable, includes)
+AC_DEFUN([rk_CHECK_VAR], [
+AC_MSG_CHECKING(for $1)
+AC_CACHE_VAL(ac_cv_var_$1, [
+m4_ifval([$2],[
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[$2
+ void * foo(void) { return &$1; }]],[[foo()]])],
+ [ac_cv_var_$1=yes],[ac_cv_var_$1=no])])
+if test "$ac_cv_var_$1" != yes ; then
+AC_LINK_IFELSE([AC_LANG_PROGRAM([[extern int $1;
+int foo(void) { return $1; }]],[[foo()]])],
+ [ac_cv_var_$1=yes],[ac_cv_var_$1=no])
+fi
+])
+ac_foo=`eval echo \\$ac_cv_var_$1`
+AC_MSG_RESULT($ac_foo)
+if test "$ac_foo" = yes; then
+ AC_DEFINE_UNQUOTED(AS_TR_CPP(HAVE_[]$1), 1,
+ [Define if you have the `]$1[' variable.])
+ m4_ifval([$2], AC_CHECK_DECLS([$1],[],[],[$2]))
+fi
+])
+
+AC_WARNING_ENABLE([obsolete])
+AU_DEFUN([AC_CHECK_VAR], [rk_CHECK_VAR([$2], [$1])], [foo])
diff --git a/source4/heimdal/cf/find-func-no-libs.m4 b/source4/heimdal/cf/find-func-no-libs.m4
new file mode 100644
index 0000000000..f3413409f6
--- /dev/null
+++ b/source4/heimdal/cf/find-func-no-libs.m4
@@ -0,0 +1,9 @@
+dnl $Id$
+dnl
+dnl
+dnl Look for function in any of the specified libraries
+dnl
+
+dnl AC_FIND_FUNC_NO_LIBS(func, libraries, includes, arguments, extra libs, extra args)
+AC_DEFUN([AC_FIND_FUNC_NO_LIBS], [
+AC_FIND_FUNC_NO_LIBS2([$1], ["" $2], [$3], [$4], [$5], [$6])])
diff --git a/source4/heimdal/cf/find-func-no-libs2.m4 b/source4/heimdal/cf/find-func-no-libs2.m4
new file mode 100644
index 0000000000..692001c103
--- /dev/null
+++ b/source4/heimdal/cf/find-func-no-libs2.m4
@@ -0,0 +1,63 @@
+dnl $Id$
+dnl
+dnl
+dnl Look for function in any of the specified libraries
+dnl
+
+dnl AC_FIND_FUNC_NO_LIBS2(func, libraries, includes, arguments, extra libs, extra args)
+AC_DEFUN([AC_FIND_FUNC_NO_LIBS2], [
+
+AC_MSG_CHECKING([for $1])
+AC_CACHE_VAL(ac_cv_funclib_$1,
+[
+if eval "test \"\$ac_cv_func_$1\" != yes" ; then
+ ac_save_LIBS="$LIBS"
+ for ac_lib in $2; do
+ case "$ac_lib" in
+ "") ;;
+ yes) ac_lib="" ;;
+ no) continue ;;
+ -l*) ;;
+ *) ac_lib="-l$ac_lib" ;;
+ esac
+ LIBS="$6 $ac_lib $5 $ac_save_LIBS"
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[$3]],[[$1($4)]])],[eval "if test -n \"$ac_lib\";then ac_cv_funclib_$1=$ac_lib; else ac_cv_funclib_$1=yes; fi";break])
+ done
+ eval "ac_cv_funclib_$1=\${ac_cv_funclib_$1-no}"
+ LIBS="$ac_save_LIBS"
+fi
+])
+
+eval "ac_res=\$ac_cv_funclib_$1"
+
+if false; then
+ AC_CHECK_FUNCS($1)
+dnl AC_CHECK_LIBS($2, foo)
+fi
+# $1
+eval "ac_tr_func=HAVE_[]upcase($1)"
+eval "ac_tr_lib=HAVE_LIB[]upcase($ac_res | sed -e 's/-l//')"
+eval "LIB_$1=$ac_res"
+
+case "$ac_res" in
+ yes)
+ eval "ac_cv_func_$1=yes"
+ eval "LIB_$1="
+ AC_DEFINE_UNQUOTED($ac_tr_func)
+ AC_MSG_RESULT([yes])
+ ;;
+ no)
+ eval "ac_cv_func_$1=no"
+ eval "LIB_$1="
+ AC_MSG_RESULT([no])
+ ;;
+ *)
+ eval "ac_cv_func_$1=yes"
+ eval "ac_cv_lib_`echo "$ac_res" | sed 's/-l//'`=yes"
+ AC_DEFINE_UNQUOTED($ac_tr_func)
+ AC_DEFINE_UNQUOTED($ac_tr_lib)
+ AC_MSG_RESULT([yes, in $ac_res])
+ ;;
+esac
+AC_SUBST(LIB_$1)
+])
diff --git a/source4/heimdal/cf/find-func.m4 b/source4/heimdal/cf/find-func.m4
new file mode 100644
index 0000000000..865772a700
--- /dev/null
+++ b/source4/heimdal/cf/find-func.m4
@@ -0,0 +1,9 @@
+dnl $Id$
+dnl
+dnl AC_FIND_FUNC(func, libraries, includes, arguments)
+AC_DEFUN([AC_FIND_FUNC], [
+AC_FIND_FUNC_NO_LIBS([$1], [$2], [$3], [$4])
+if test -n "$LIB_$1"; then
+ LIBS="$LIB_$1 $LIBS"
+fi
+])
diff --git a/source4/heimdal/cf/make-proto.pl b/source4/heimdal/cf/make-proto.pl
new file mode 100644
index 0000000000..b89ef79067
--- /dev/null
+++ b/source4/heimdal/cf/make-proto.pl
@@ -0,0 +1,351 @@
+# Make prototypes from .c files
+# $Id$
+
+##use Getopt::Std;
+require 'getopts.pl';
+
+my $comment = 0;
+my $if_0 = 0;
+my $brace = 0;
+my $line = "";
+my $debug = 0;
+my $oproto = 1;
+my $private_func_re = "^_";
+
+do Getopts('x:m:o:p:dqE:R:P:') || die "foo";
+
+if($opt_d) {
+ $debug = 1;
+}
+
+if($opt_q) {
+ $oproto = 0;
+}
+
+if($opt_R) {
+ $private_func_re = $opt_R;
+}
+%flags = (
+ 'multiline-proto' => 1,
+ 'header' => 1,
+ 'function-blocking' => 0,
+ 'gnuc-attribute' => 1,
+ 'cxx' => 1
+ );
+if($opt_m) {
+ foreach $i (split(/,/, $opt_m)) {
+ if($i eq "roken") {
+ $flags{"multiline-proto"} = 0;
+ $flags{"header"} = 0;
+ $flags{"function-blocking"} = 0;
+ $flags{"gnuc-attribute"} = 0;
+ $flags{"cxx"} = 0;
+ } else {
+ if(substr($i, 0, 3) eq "no-") {
+ $flags{substr($i, 3)} = 0;
+ } else {
+ $flags{$i} = 1;
+ }
+ }
+ }
+}
+
+if($opt_x) {
+ open(EXP, $opt_x);
+ while(<EXP>) {
+ chomp;
+ s/\#.*//g;
+ s/\s+/ /g;
+ if(/^([a-zA-Z0-9_]+)\s?(.*)$/) {
+ $exported{$1} = $2;
+ } else {
+ print $_, "\n";
+ }
+ }
+ close EXP;
+}
+
+while(<>) {
+ print $brace, " ", $_ if($debug);
+
+ # Handle C comments
+ s@/\*.*\*/@@;
+ s@//.*/@@;
+ if ( s@/\*.*@@) { $comment = 1;
+ } elsif ($comment && s@.*\*/@@) { $comment = 0;
+ } elsif ($comment) { next; }
+
+ if(/^\#if 0/) {
+ $if_0 = 1;
+ }
+ if($if_0 && /^\#endif/) {
+ $if_0 = 0;
+ }
+ if($if_0) { next }
+ if(/^\s*\#/) {
+ next;
+ }
+ if(/^\s*$/) {
+ $line = "";
+ next;
+ }
+ if(/\{/){
+ if (!/\}/) {
+ $brace++;
+ }
+ $_ = $line;
+ while(s/\*\//\ca/){
+ s/\/\*(.|\n)*\ca//;
+ }
+ s/^\s*//;
+ s/\s*$//;
+ s/\s+/ /g;
+ if($_ =~ /\)$/){
+ if(!/^static/ && !/^PRIVATE/){
+ if(/(.*)(__attribute__\s?\(.*\))/) {
+ $attr = $2;
+ $_ = $1;
+ } else {
+ $attr = "";
+ }
+ # remove outer ()
+ s/\s*\(/</;
+ s/\)\s?$/>/;
+ # remove , within ()
+ while(s/\(([^()]*),(.*)\)/($1\$$2)/g){}
+ s/\<\s*void\s*\>/<>/;
+ # remove parameter names
+ if($opt_P eq "remove") {
+ s/(\s*)([a-zA-Z0-9_]+)([,>])/$3/g;
+ s/\s+\*/*/g;
+ s/\(\*(\s*)([a-zA-Z0-9_]+)\)/(*)/g;
+ } elsif($opt_P eq "comment") {
+ s/([a-zA-Z0-9_]+)([,>])/\/\*$1\*\/$2/g;
+ s/\(\*([a-zA-Z0-9_]+)\)/(*\/\*$1\*\/)/g;
+ }
+ s/\<\>/<void>/;
+ # add newlines before parameters
+ if($flags{"multiline-proto"}) {
+ s/,\s*/,\n\t/g;
+ } else {
+ s/,\s*/, /g;
+ }
+ # fix removed ,
+ s/\$/,/g;
+ # match function name
+ /([a-zA-Z0-9_]+)\s*\</;
+ $f = $1;
+ if($oproto) {
+ $LP = "__P((";
+ $RP = "))";
+ } else {
+ $LP = "(";
+ $RP = ")";
+ }
+ # only add newline if more than one parameter
+ if($flags{"multiline-proto"} && /,/){
+ s/\</ $LP\n\t/;
+ }else{
+ s/\</ $LP/;
+ }
+ s/\>/$RP/;
+ # insert newline before function name
+ if($flags{"multiline-proto"}) {
+ s/(.*)\s([a-zA-Z0-9_]+ \Q$LP\E)/$1\n$2/;
+ }
+ if($attr ne "") {
+ $_ .= "\n $attr";
+ }
+ $_ = $_ . ";";
+ $funcs{$f} = $_;
+ }
+ }
+ $line = "";
+ }
+ if(/\}/){
+ $brace--;
+ }
+ if(/^\}/){
+ $brace = 0;
+ }
+ if($brace == 0) {
+ $line = $line . " " . $_;
+ }
+}
+
+sub foo {
+ local ($arg) = @_;
+ $_ = $arg;
+ s/.*\/([^\/]*)/$1/;
+ s/[^a-zA-Z0-9]/_/g;
+ "__" . $_ . "__";
+}
+
+if($opt_o) {
+ open(OUT, ">$opt_o");
+ $block = &foo($opt_o);
+} else {
+ $block = "__public_h__";
+}
+
+if($opt_p) {
+ open(PRIV, ">$opt_p");
+ $private = &foo($opt_p);
+} else {
+ $private = "__private_h__";
+}
+
+$public_h = "";
+$private_h = "";
+
+$public_h_header .= "/* This is a generated file */
+#ifndef $block
+#define $block
+
+";
+if ($oproto) {
+ $public_h_header .= "#ifdef __STDC__
+#include <stdarg.h>
+#ifndef __P
+#define __P(x) x
+#endif
+#else
+#ifndef __P
+#define __P(x) ()
+#endif
+#endif
+
+";
+} else {
+ $public_h_header .= "#include <stdarg.h>
+
+";
+}
+$public_h_trailer = "";
+
+$private_h_header = "/* This is a generated file */
+#ifndef $private
+#define $private
+
+";
+if($oproto) {
+ $private_h_header .= "#ifdef __STDC__
+#include <stdarg.h>
+#ifndef __P
+#define __P(x) x
+#endif
+#else
+#ifndef __P
+#define __P(x) ()
+#endif
+#endif
+
+";
+} else {
+ $private_h_header .= "#include <stdarg.h>
+
+";
+}
+$private_h_trailer = "";
+
+foreach(sort keys %funcs){
+ if(/^(main)$/) { next }
+ if(!defined($exported{$_}) && /$private_func_re/) {
+ $private_h .= $funcs{$_} . "\n\n";
+ if($funcs{$_} =~ /__attribute__/) {
+ $private_attribute_seen = 1;
+ }
+ } else {
+ if($flags{"function-blocking"}) {
+ $fupper = uc $_;
+ if($exported{$_} =~ /proto/) {
+ $public_h .= "#if !defined(HAVE_$fupper) || defined(NEED_${fupper}_PROTO)\n";
+ } else {
+ $public_h .= "#ifndef HAVE_$fupper\n";
+ }
+ }
+ $public_h .= $funcs{$_} . "\n";
+ if($funcs{$_} =~ /__attribute__/) {
+ $public_attribute_seen = 1;
+ }
+ if($flags{"function-blocking"}) {
+ $public_h .= "#endif\n";
+ }
+ $public_h .= "\n";
+ }
+}
+
+if($flags{"gnuc-attribute"}) {
+ if ($public_attribute_seen) {
+ $public_h_header .= "#if !defined(__GNUC__) && !defined(__attribute__)
+#define __attribute__(x)
+#endif
+
+";
+ }
+
+ if ($private_attribute_seen) {
+ $private_h_header .= "#if !defined(__GNUC__) && !defined(__attribute__)
+#define __attribute__(x)
+#endif
+
+";
+ }
+}
+if($flags{"cxx"}) {
+ $public_h_header .= "#ifdef __cplusplus
+extern \"C\" {
+#endif
+
+";
+ $public_h_trailer .= "#ifdef __cplusplus
+}
+#endif
+
+";
+
+}
+if ($opt_E) {
+ $public_h_header .= "#ifndef $opt_E
+#if defined(_WIN32)
+#define ${opt_E}_FUNCTION _stdcall __declspec(dllimport)
+#define ${opt_E}_VARIABLE __declspec(dllimport)
+#else
+#define ${opt_E}_FUNCTION
+#define ${opt_E}_VARIABLE
+#endif
+#endif
+
+";
+
+ $private_h_header .= "#ifndef $opt_E
+#if defined(_WIN32)
+#define ${opt_E}_FUNCTION _stdcall __declspec(dllimport)
+#define ${opt_E}_VARIABLE __declspec(dllimport)
+#else
+#define ${opt_E}_FUNCTION
+#define ${opt_E}_VARIABLE
+#endif
+#endif
+
+";
+}
+
+if ($public_h ne "" && $flags{"header"}) {
+ $public_h = $public_h_header . $public_h .
+ $public_h_trailer . "#endif /* $block */\n";
+}
+if ($private_h ne "" && $flags{"header"}) {
+ $private_h = $private_h_header . $private_h .
+ $private_h_trailer . "#endif /* $private */\n";
+}
+
+if($opt_o) {
+ print OUT $public_h;
+}
+if($opt_p) {
+ print PRIV $private_h;
+}
+
+close OUT;
+close PRIV;
diff --git a/source4/heimdal/cf/resolv.m4 b/source4/heimdal/cf/resolv.m4
new file mode 100644
index 0000000000..b4045094d8
--- /dev/null
+++ b/source4/heimdal/cf/resolv.m4
@@ -0,0 +1,109 @@
+dnl stuff used by DNS resolv code in roken
+dnl
+dnl $Id$
+dnl
+
+AC_DEFUN([rk_RESOLV],[
+
+AC_CHECK_HEADERS([arpa/nameser.h])
+
+AC_CHECK_HEADERS(resolv.h, , , [AC_INCLUDES_DEFAULT
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_ARPA_NAMESER_H
+#include <arpa/nameser.h>
+#endif
+])
+
+AC_FIND_FUNC(res_search, resolv,
+[
+#include <stdio.h>
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_ARPA_NAMESER_H
+#include <arpa/nameser.h>
+#endif
+#ifdef HAVE_RESOLV_H
+#include <resolv.h>
+#endif
+],
+[0,0,0,0,0])
+
+AC_FIND_FUNC(res_nsearch, resolv,
+[
+#include <stdio.h>
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_ARPA_NAMESER_H
+#include <arpa/nameser.h>
+#endif
+#ifdef HAVE_RESOLV_H
+#include <resolv.h>
+#endif
+],
+[0,0,0,0,0,0])
+
+AC_FIND_FUNC(res_ndestroy, resolv,
+[
+#include <stdio.h>
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_ARPA_NAMESER_H
+#include <arpa/nameser.h>
+#endif
+#ifdef HAVE_RESOLV_H
+#include <resolv.h>
+#endif
+],
+[0])
+
+AC_FIND_FUNC(dn_expand, resolv,
+[
+#include <stdio.h>
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_ARPA_NAMESER_H
+#include <arpa/nameser.h>
+#endif
+#ifdef HAVE_RESOLV_H
+#include <resolv.h>
+#endif
+],
+[0,0,0,0,0])
+
+rk_CHECK_VAR(_res,
+[#include <stdio.h>
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_ARPA_NAMESER_H
+#include <arpa/nameser.h>
+#endif
+#ifdef HAVE_RESOLV_H
+#include <resolv.h>
+#endif])
+
+])
diff --git a/source4/heimdal/kdc/524.c b/source4/heimdal/kdc/524.c
new file mode 100644
index 0000000000..d15310384a
--- /dev/null
+++ b/source4/heimdal/kdc/524.c
@@ -0,0 +1,400 @@
+/*
+ * Copyright (c) 1997-2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kdc_locl.h"
+
+RCSID("$Id$");
+
+#include <krb5-v4compat.h>
+
+/*
+ * fetch the server from `t', returning the name in malloced memory in
+ * `spn' and the entry itself in `server'
+ */
+
+static krb5_error_code
+fetch_server (krb5_context context,
+ krb5_kdc_configuration *config,
+ const Ticket *t,
+ char **spn,
+ hdb_entry_ex **server,
+ const char *from)
+{
+ krb5_error_code ret;
+ krb5_principal sprinc;
+
+ ret = _krb5_principalname2krb5_principal(context, &sprinc,
+ t->sname, t->realm);
+ if (ret) {
+ kdc_log(context, config, 0, "_krb5_principalname2krb5_principal: %s",
+ krb5_get_err_text(context, ret));
+ return ret;
+ }
+ ret = krb5_unparse_name(context, sprinc, spn);
+ if (ret) {
+ krb5_free_principal(context, sprinc);
+ kdc_log(context, config, 0, "krb5_unparse_name: %s",
+ krb5_get_err_text(context, ret));
+ return ret;
+ }
+ ret = _kdc_db_fetch(context, config, sprinc, HDB_F_GET_SERVER,
+ NULL, server);
+ krb5_free_principal(context, sprinc);
+ if (ret) {
+ kdc_log(context, config, 0,
+ "Request to convert ticket from %s for unknown principal %s: %s",
+ from, *spn, krb5_get_err_text(context, ret));
+ if (ret == HDB_ERR_NOENTRY)
+ ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
+ return ret;
+ }
+ return 0;
+}
+
+static krb5_error_code
+log_524 (krb5_context context,
+ krb5_kdc_configuration *config,
+ const EncTicketPart *et,
+ const char *from,
+ const char *spn)
+{
+ krb5_principal client;
+ char *cpn;
+ krb5_error_code ret;
+
+ ret = _krb5_principalname2krb5_principal(context, &client,
+ et->cname, et->crealm);
+ if (ret) {
+ kdc_log(context, config, 0, "_krb5_principalname2krb5_principal: %s",
+ krb5_get_err_text (context, ret));
+ return ret;
+ }
+ ret = krb5_unparse_name(context, client, &cpn);
+ if (ret) {
+ krb5_free_principal(context, client);
+ kdc_log(context, config, 0, "krb5_unparse_name: %s",
+ krb5_get_err_text (context, ret));
+ return ret;
+ }
+ kdc_log(context, config, 1, "524-REQ %s from %s for %s", cpn, from, spn);
+ free(cpn);
+ krb5_free_principal(context, client);
+ return 0;
+}
+
+static krb5_error_code
+verify_flags (krb5_context context,
+ krb5_kdc_configuration *config,
+ const EncTicketPart *et,
+ const char *spn)
+{
+ if(et->endtime < kdc_time){
+ kdc_log(context, config, 0, "Ticket expired (%s)", spn);
+ return KRB5KRB_AP_ERR_TKT_EXPIRED;
+ }
+ if(et->flags.invalid){
+ kdc_log(context, config, 0, "Ticket not valid (%s)", spn);
+ return KRB5KRB_AP_ERR_TKT_NYV;
+ }
+ return 0;
+}
+
+/*
+ * set the `et->caddr' to the most appropriate address to use, where
+ * `addr' is the address the request was received from.
+ */
+
+static krb5_error_code
+set_address (krb5_context context,
+ krb5_kdc_configuration *config,
+ EncTicketPart *et,
+ struct sockaddr *addr,
+ const char *from)
+{
+ krb5_error_code ret;
+ krb5_address *v4_addr;
+
+ v4_addr = malloc (sizeof(*v4_addr));
+ if (v4_addr == NULL)
+ return ENOMEM;
+
+ ret = krb5_sockaddr2address(context, addr, v4_addr);
+ if(ret) {
+ free (v4_addr);
+ kdc_log(context, config, 0, "Failed to convert address (%s)", from);
+ return ret;
+ }
+
+ if (et->caddr && !krb5_address_search (context, v4_addr, et->caddr)) {
+ kdc_log(context, config, 0, "Incorrect network address (%s)", from);
+ krb5_free_address(context, v4_addr);
+ free (v4_addr);
+ return KRB5KRB_AP_ERR_BADADDR;
+ }
+ if(v4_addr->addr_type == KRB5_ADDRESS_INET) {
+ /* we need to collapse the addresses in the ticket to a
+ single address; best guess is to use the address the
+ connection came from */
+
+ if (et->caddr != NULL) {
+ free_HostAddresses(et->caddr);
+ } else {
+ et->caddr = malloc (sizeof (*et->caddr));
+ if (et->caddr == NULL) {
+ krb5_free_address(context, v4_addr);
+ free(v4_addr);
+ return ENOMEM;
+ }
+ }
+ et->caddr->val = v4_addr;
+ et->caddr->len = 1;
+ } else {
+ krb5_free_address(context, v4_addr);
+ free(v4_addr);
+ }
+ return 0;
+}
+
+
+static krb5_error_code
+encrypt_v4_ticket(krb5_context context,
+ krb5_kdc_configuration *config,
+ void *buf,
+ size_t len,
+ krb5_keyblock *skey,
+ EncryptedData *reply)
+{
+ krb5_crypto crypto;
+ krb5_error_code ret;
+ ret = krb5_crypto_init(context, skey, ETYPE_DES_PCBC_NONE, &crypto);
+ if (ret) {
+ free(buf);
+ kdc_log(context, config, 0, "krb5_crypto_init failed: %s",
+ krb5_get_err_text(context, ret));
+ return ret;
+ }
+
+ ret = krb5_encrypt_EncryptedData(context,
+ crypto,
+ KRB5_KU_TICKET,
+ buf,
+ len,
+ 0,
+ reply);
+ krb5_crypto_destroy(context, crypto);
+ if(ret) {
+ kdc_log(context, config, 0, "Failed to encrypt data: %s",
+ krb5_get_err_text(context, ret));
+ return ret;
+ }
+ return 0;
+}
+
+static krb5_error_code
+encode_524_response(krb5_context context,
+ krb5_kdc_configuration *config,
+ const char *spn, const EncTicketPart et,
+ const Ticket *t, hdb_entry_ex *server,
+ EncryptedData *ticket, int *kvno)
+{
+ krb5_error_code ret;
+ int use_2b;
+ size_t len;
+
+ use_2b = krb5_config_get_bool(context, NULL, "kdc", "use_2b", spn, NULL);
+ if(use_2b) {
+ ASN1_MALLOC_ENCODE(EncryptedData,
+ ticket->cipher.data, ticket->cipher.length,
+ &t->enc_part, &len, ret);
+
+ if (ret) {
+ kdc_log(context, config, 0,
+ "Failed to encode v4 (2b) ticket (%s)", spn);
+ return ret;
+ }
+
+ ticket->etype = 0;
+ ticket->kvno = NULL;
+ *kvno = 213; /* 2b's use this magic kvno */
+ } else {
+ unsigned char buf[MAX_KTXT_LEN + 4 * 4];
+ Key *skey;
+
+ if (!config->enable_v4_cross_realm && strcmp (et.crealm, t->realm) != 0) {
+ kdc_log(context, config, 0, "524 cross-realm %s -> %s disabled", et.crealm,
+ t->realm);
+ return KRB5KDC_ERR_POLICY;
+ }
+
+ ret = _kdc_encode_v4_ticket(context, config,
+ buf + sizeof(buf) - 1, sizeof(buf),
+ &et, &t->sname, &len);
+ if(ret){
+ kdc_log(context, config, 0,
+ "Failed to encode v4 ticket (%s)", spn);
+ return ret;
+ }
+ ret = _kdc_get_des_key(context, server, TRUE, FALSE, &skey);
+ if(ret){
+ kdc_log(context, config, 0,
+ "no suitable DES key for server (%s)", spn);
+ return ret;
+ }
+ ret = encrypt_v4_ticket(context, config, buf + sizeof(buf) - len, len,
+ &skey->key, ticket);
+ if(ret){
+ kdc_log(context, config, 0,
+ "Failed to encrypt v4 ticket (%s)", spn);
+ return ret;
+ }
+ *kvno = server->entry.kvno;
+ }
+
+ return 0;
+}
+
+/*
+ * process a 5->4 request, based on `t', and received `from, addr',
+ * returning the reply in `reply'
+ */
+
+krb5_error_code
+_kdc_do_524(krb5_context context,
+ krb5_kdc_configuration *config,
+ const Ticket *t, krb5_data *reply,
+ const char *from, struct sockaddr *addr)
+{
+ krb5_error_code ret = 0;
+ krb5_crypto crypto;
+ hdb_entry_ex *server = NULL;
+ Key *skey;
+ krb5_data et_data;
+ EncTicketPart et;
+ EncryptedData ticket;
+ krb5_storage *sp;
+ char *spn = NULL;
+ unsigned char buf[MAX_KTXT_LEN + 4 * 4];
+ size_t len;
+ int kvno = 0;
+
+ if(!config->enable_524) {
+ ret = KRB5KDC_ERR_POLICY;
+ kdc_log(context, config, 0,
+ "Rejected ticket conversion request from %s", from);
+ goto out;
+ }
+
+ ret = fetch_server (context, config, t, &spn, &server, from);
+ if (ret) {
+ goto out;
+ }
+
+ ret = hdb_enctype2key(context, &server->entry, t->enc_part.etype, &skey);
+ if(ret){
+ kdc_log(context, config, 0,
+ "No suitable key found for server (%s) from %s", spn, from);
+ goto out;
+ }
+ ret = krb5_crypto_init(context, &skey->key, 0, &crypto);
+ if (ret) {
+ kdc_log(context, config, 0, "krb5_crypto_init failed: %s",
+ krb5_get_err_text(context, ret));
+ goto out;
+ }
+ ret = krb5_decrypt_EncryptedData (context,
+ crypto,
+ KRB5_KU_TICKET,
+ &t->enc_part,
+ &et_data);
+ krb5_crypto_destroy(context, crypto);
+ if(ret){
+ kdc_log(context, config, 0,
+ "Failed to decrypt ticket from %s for %s", from, spn);
+ goto out;
+ }
+ ret = krb5_decode_EncTicketPart(context, et_data.data, et_data.length,
+ &et, &len);
+ krb5_data_free(&et_data);
+ if(ret){
+ kdc_log(context, config, 0,
+ "Failed to decode ticket from %s for %s", from, spn);
+ goto out;
+ }
+
+ ret = log_524 (context, config, &et, from, spn);
+ if (ret) {
+ free_EncTicketPart(&et);
+ goto out;
+ }
+
+ ret = verify_flags (context, config, &et, spn);
+ if (ret) {
+ free_EncTicketPart(&et);
+ goto out;
+ }
+
+ ret = set_address (context, config, &et, addr, from);
+ if (ret) {
+ free_EncTicketPart(&et);
+ goto out;
+ }
+
+ ret = encode_524_response(context, config, spn, et, t,
+ server, &ticket, &kvno);
+ free_EncTicketPart(&et);
+
+ out:
+ /* make reply */
+ memset(buf, 0, sizeof(buf));
+ sp = krb5_storage_from_mem(buf, sizeof(buf));
+ if (sp) {
+ krb5_store_int32(sp, ret);
+ if(ret == 0){
+ krb5_store_int32(sp, kvno);
+ krb5_store_data(sp, ticket.cipher);
+ /* Aargh! This is coded as a KTEXT_ST. */
+ krb5_storage_seek(sp, MAX_KTXT_LEN - ticket.cipher.length, SEEK_CUR);
+ krb5_store_int32(sp, 0); /* mbz */
+ free_EncryptedData(&ticket);
+ }
+ ret = krb5_storage_to_data(sp, reply);
+ reply->length = krb5_storage_seek(sp, 0, SEEK_CUR);
+ krb5_storage_free(sp);
+ } else
+ krb5_data_zero(reply);
+ if(spn)
+ free(spn);
+ if(server)
+ _kdc_free_ent (context, server);
+ return ret;
+}
diff --git a/source4/heimdal/kdc/default_config.c b/source4/heimdal/kdc/default_config.c
new file mode 100644
index 0000000000..60fbc92903
--- /dev/null
+++ b/source4/heimdal/kdc/default_config.c
@@ -0,0 +1,285 @@
+/*
+ * Copyright (c) 1997-2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kdc_locl.h"
+#include <getarg.h>
+#include <parse_bytes.h>
+
+RCSID("$Id$");
+
+krb5_error_code
+krb5_kdc_get_config(krb5_context context, krb5_kdc_configuration **config)
+{
+ krb5_kdc_configuration *c;
+
+ c = calloc(1, sizeof(*c));
+ if (c == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+
+ c->require_preauth = TRUE;
+ c->kdc_warn_pwexpire = 0;
+ c->encode_as_rep_as_tgs_rep = FALSE;
+ c->check_ticket_addresses = TRUE;
+ c->allow_null_ticket_addresses = TRUE;
+ c->allow_anonymous = FALSE;
+ c->trpolicy = TRPOLICY_ALWAYS_CHECK;
+ c->enable_v4 = FALSE;
+ c->enable_kaserver = FALSE;
+ c->enable_524 = FALSE;
+ c->enable_v4_cross_realm = FALSE;
+ c->enable_pkinit = FALSE;
+ c->pkinit_princ_in_cert = TRUE;
+ c->pkinit_require_binding = TRUE;
+ c->db = NULL;
+ c->num_db = 0;
+ c->logf = NULL;
+
+ c->require_preauth =
+ krb5_config_get_bool_default(context, NULL,
+ c->require_preauth,
+ "kdc", "require-preauth", NULL);
+ c->enable_v4 =
+ krb5_config_get_bool_default(context, NULL,
+ c->enable_v4,
+ "kdc", "enable-kerberos4", NULL);
+ c->enable_v4_cross_realm =
+ krb5_config_get_bool_default(context, NULL,
+ c->enable_v4_cross_realm,
+ "kdc",
+ "enable-kerberos4-cross-realm", NULL);
+ c->enable_524 =
+ krb5_config_get_bool_default(context, NULL,
+ c->enable_v4,
+ "kdc", "enable-524", NULL);
+ c->enable_digest =
+ krb5_config_get_bool_default(context, NULL,
+ FALSE,
+ "kdc", "enable-digest", NULL);
+
+ {
+ const char *digests;
+
+ digests = krb5_config_get_string(context, NULL,
+ "kdc",
+ "digests_allowed", NULL);
+ if (digests == NULL)
+ digests = "ntlm-v2";
+ c->digests_allowed = parse_flags(digests,_kdc_digestunits, 0);
+ if (c->digests_allowed == -1) {
+ kdc_log(context, c, 0,
+ "unparsable digest units (%s), turning off digest",
+ digests);
+ c->enable_digest = 0;
+ } else if (c->digests_allowed == 0) {
+ kdc_log(context, c, 0,
+ "no digest enable, turning digest off",
+ digests);
+ c->enable_digest = 0;
+ }
+ }
+
+ c->enable_kx509 =
+ krb5_config_get_bool_default(context, NULL,
+ FALSE,
+ "kdc", "enable-kx509", NULL);
+
+ if (c->enable_kx509) {
+ c->kx509_template =
+ krb5_config_get_string(context, NULL,
+ "kdc", "kx509_template", NULL);
+ c->kx509_ca =
+ krb5_config_get_string(context, NULL,
+ "kdc", "kx509_ca", NULL);
+ if (c->kx509_ca == NULL || c->kx509_template == NULL) {
+ kdc_log(context, c, 0,
+ "missing kx509 configuration, turning off");
+ c->enable_kx509 = FALSE;
+ }
+ }
+
+ c->check_ticket_addresses =
+ krb5_config_get_bool_default(context, NULL,
+ c->check_ticket_addresses,
+ "kdc",
+ "check-ticket-addresses", NULL);
+ c->allow_null_ticket_addresses =
+ krb5_config_get_bool_default(context, NULL,
+ c->allow_null_ticket_addresses,
+ "kdc",
+ "allow-null-ticket-addresses", NULL);
+
+ c->allow_anonymous =
+ krb5_config_get_bool_default(context, NULL,
+ c->allow_anonymous,
+ "kdc",
+ "allow-anonymous", NULL);
+
+ c->max_datagram_reply_length =
+ krb5_config_get_int_default(context,
+ NULL,
+ 1400,
+ "kdc",
+ "max-kdc-datagram-reply-length",
+ NULL);
+
+ {
+ const char *trpolicy_str;
+
+ trpolicy_str =
+ krb5_config_get_string_default(context, NULL, "DEFAULT", "kdc",
+ "transited-policy", NULL);
+ if(strcasecmp(trpolicy_str, "always-check") == 0) {
+ c->trpolicy = TRPOLICY_ALWAYS_CHECK;
+ } else if(strcasecmp(trpolicy_str, "allow-per-principal") == 0) {
+ c->trpolicy = TRPOLICY_ALLOW_PER_PRINCIPAL;
+ } else if(strcasecmp(trpolicy_str, "always-honour-request") == 0) {
+ c->trpolicy = TRPOLICY_ALWAYS_HONOUR_REQUEST;
+ } else if(strcasecmp(trpolicy_str, "DEFAULT") == 0) {
+ /* default */
+ } else {
+ kdc_log(context, c, 0,
+ "unknown transited-policy: %s, "
+ "reverting to default (always-check)",
+ trpolicy_str);
+ }
+ }
+
+ {
+ const char *p;
+ p = krb5_config_get_string (context, NULL,
+ "kdc",
+ "v4-realm",
+ NULL);
+ if(p != NULL) {
+ c->v4_realm = strdup(p);
+ if (c->v4_realm == NULL)
+ krb5_errx(context, 1, "out of memory");
+ } else {
+ c->v4_realm = NULL;
+ }
+ }
+
+ c->enable_kaserver =
+ krb5_config_get_bool_default(context,
+ NULL,
+ c->enable_kaserver,
+ "kdc", "enable-kaserver", NULL);
+
+
+ c->encode_as_rep_as_tgs_rep =
+ krb5_config_get_bool_default(context, NULL,
+ c->encode_as_rep_as_tgs_rep,
+ "kdc",
+ "encode_as_rep_as_tgs_rep", NULL);
+
+ c->kdc_warn_pwexpire =
+ krb5_config_get_time_default (context, NULL,
+ c->kdc_warn_pwexpire,
+ "kdc", "kdc_warn_pwexpire", NULL);
+
+
+#ifdef PKINIT
+ c->enable_pkinit =
+ krb5_config_get_bool_default(context,
+ NULL,
+ c->enable_pkinit,
+ "kdc",
+ "enable-pkinit",
+ NULL);
+ if (c->enable_pkinit) {
+ const char *user_id, *anchors, *ocsp_file;
+ char **pool_list, **revoke_list;
+
+ user_id =
+ krb5_config_get_string(context, NULL,
+ "kdc", "pkinit_identity", NULL);
+ if (user_id == NULL)
+ krb5_errx(context, 1, "pkinit enabled but no identity");
+
+ anchors = krb5_config_get_string(context, NULL,
+ "kdc", "pkinit_anchors", NULL);
+ if (anchors == NULL)
+ krb5_errx(context, 1, "pkinit enabled but no X509 anchors");
+
+ pool_list =
+ krb5_config_get_strings(context, NULL,
+ "kdc", "pkinit_pool", NULL);
+
+ revoke_list =
+ krb5_config_get_strings(context, NULL,
+ "kdc", "pkinit_revoke", NULL);
+
+ ocsp_file =
+ krb5_config_get_string(context, NULL,
+ "kdc", "pkinit_kdc_ocsp", NULL);
+ if (ocsp_file) {
+ c->pkinit_kdc_ocsp_file = strdup(ocsp_file);
+ if (c->pkinit_kdc_ocsp_file == NULL)
+ krb5_errx(context, 1, "out of memory");
+ }
+
+ _kdc_pk_initialize(context, c, user_id, anchors,
+ pool_list, revoke_list);
+
+ krb5_config_free_strings(pool_list);
+ krb5_config_free_strings(revoke_list);
+
+ c->pkinit_princ_in_cert =
+ krb5_config_get_bool_default(context, NULL,
+ c->pkinit_princ_in_cert,
+ "kdc",
+ "pkinit_principal_in_certificate",
+ NULL);
+
+ c->pkinit_require_binding =
+ krb5_config_get_bool_default(context, NULL,
+ c->pkinit_require_binding,
+ "kdc",
+ "pkinit_win2k_require_binding",
+ NULL);
+ }
+
+ c->pkinit_dh_min_bits =
+ krb5_config_get_int_default(context, NULL,
+ 0,
+ "kdc", "pkinit_dh_min_bits", NULL);
+
+#endif
+
+ *config = c;
+
+ return 0;
+}
diff --git a/source4/heimdal/kdc/digest.c b/source4/heimdal/kdc/digest.c
new file mode 100644
index 0000000000..96986c1a87
--- /dev/null
+++ b/source4/heimdal/kdc/digest.c
@@ -0,0 +1,1458 @@
+/*
+ * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kdc_locl.h"
+#include <hex.h>
+
+RCSID("$Id$");
+
+#define MS_CHAP_V2 0x20
+#define CHAP_MD5 0x10
+#define DIGEST_MD5 0x08
+#define NTLM_V2 0x04
+#define NTLM_V1_SESSION 0x02
+#define NTLM_V1 0x01
+
+const struct units _kdc_digestunits[] = {
+ {"ms-chap-v2", 1U << 5},
+ {"chap-md5", 1U << 4},
+ {"digest-md5", 1U << 3},
+ {"ntlm-v2", 1U << 2},
+ {"ntlm-v1-session", 1U << 1},
+ {"ntlm-v1", 1U << 0},
+ {NULL, 0}
+};
+
+
+static krb5_error_code
+get_digest_key(krb5_context context,
+ krb5_kdc_configuration *config,
+ hdb_entry_ex *server,
+ krb5_crypto *crypto)
+{
+ krb5_error_code ret;
+ krb5_enctype enctype;
+ Key *key;
+
+ ret = _kdc_get_preferred_key(context,
+ config,
+ server,
+ "digest-service",
+ &enctype,
+ &key);
+ if (ret)
+ return ret;
+ return krb5_crypto_init(context, &key->key, 0, crypto);
+}
+
+/*
+ *
+ */
+
+static char *
+get_ntlm_targetname(krb5_context context,
+ hdb_entry_ex *client)
+{
+ char *targetname, *p;
+
+ targetname = strdup(krb5_principal_get_realm(context,
+ client->entry.principal));
+ if (targetname == NULL)
+ return NULL;
+
+ p = strchr(targetname, '.');
+ if (p)
+ *p = '\0';
+
+ strupr(targetname);
+ return targetname;
+}
+
+static krb5_error_code
+fill_targetinfo(krb5_context context,
+ char *targetname,
+ hdb_entry_ex *client,
+ krb5_data *data)
+{
+ struct ntlm_targetinfo ti;
+ krb5_error_code ret;
+ struct ntlm_buf d;
+ krb5_principal p;
+ const char *str;
+
+ memset(&ti, 0, sizeof(ti));
+
+ ti.domainname = targetname;
+ p = client->entry.principal;
+ str = krb5_principal_get_comp_string(context, p, 0);
+ if (str != NULL &&
+ (strcmp("host", str) == 0 ||
+ strcmp("ftp", str) == 0 ||
+ strcmp("imap", str) == 0 ||
+ strcmp("pop", str) == 0 ||
+ strcmp("smtp", str)))
+ {
+ str = krb5_principal_get_comp_string(context, p, 1);
+ ti.dnsservername = rk_UNCONST(str);
+ }
+
+ ret = heim_ntlm_encode_targetinfo(&ti, 1, &d);
+ if (ret)
+ return ret;
+
+ data->data = d.data;
+ data->length = d.length;
+
+ return 0;
+}
+
+
+static const unsigned char ms_chap_v2_magic1[39] = {
+ 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
+ 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
+ 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
+ 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74
+};
+static const unsigned char ms_chap_v2_magic2[41] = {
+ 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
+ 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
+ 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
+ 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
+ 0x6E
+};
+static const unsigned char ms_rfc3079_magic1[27] = {
+ 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
+ 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79
+};
+
+/*
+ *
+ */
+
+static krb5_error_code
+get_password_entry(krb5_context context,
+ krb5_kdc_configuration *config,
+ const char *username,
+ char **password)
+{
+ krb5_principal clientprincipal;
+ krb5_error_code ret;
+ hdb_entry_ex *user;
+ HDB *db;
+
+ /* get username */
+ ret = krb5_parse_name(context, username, &clientprincipal);
+ if (ret)
+ return ret;
+
+ ret = _kdc_db_fetch(context, config, clientprincipal,
+ HDB_F_GET_CLIENT, &db, &user);
+ krb5_free_principal(context, clientprincipal);
+ if (ret)
+ return ret;
+
+ ret = hdb_entry_get_password(context, db, &user->entry, password);
+ if (ret || password == NULL) {
+ if (ret == 0) {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret, "password missing");
+ }
+ memset(user, 0, sizeof(*user));
+ }
+ _kdc_free_ent (context, user);
+ return ret;
+}
+
+/*
+ *
+ */
+
+krb5_error_code
+_kdc_do_digest(krb5_context context,
+ krb5_kdc_configuration *config,
+ const DigestREQ *req, krb5_data *reply,
+ const char *from, struct sockaddr *addr)
+{
+ krb5_error_code ret = 0;
+ krb5_ticket *ticket = NULL;
+ krb5_auth_context ac = NULL;
+ krb5_keytab id = NULL;
+ krb5_crypto crypto = NULL;
+ DigestReqInner ireq;
+ DigestRepInner r;
+ DigestREP rep;
+ krb5_flags ap_req_options;
+ krb5_data buf;
+ size_t size;
+ krb5_storage *sp = NULL;
+ Checksum res;
+ hdb_entry_ex *server = NULL, *user = NULL;
+ hdb_entry_ex *client = NULL;
+ char *client_name = NULL, *password = NULL;
+ krb5_data serverNonce;
+
+ if(!config->enable_digest) {
+ kdc_log(context, config, 0,
+ "Rejected digest request (disabled) from %s", from);
+ return KRB5KDC_ERR_POLICY;
+ }
+
+ krb5_data_zero(&buf);
+ krb5_data_zero(reply);
+ krb5_data_zero(&serverNonce);
+ memset(&ireq, 0, sizeof(ireq));
+ memset(&r, 0, sizeof(r));
+ memset(&rep, 0, sizeof(rep));
+
+ kdc_log(context, config, 0, "Digest request from %s", from);
+
+ ret = krb5_kt_resolve(context, "HDB:", &id);
+ if (ret) {
+ kdc_log(context, config, 0, "Can't open database for digest");
+ goto out;
+ }
+
+ ret = krb5_rd_req(context,
+ &ac,
+ &req->apReq,
+ NULL,
+ id,
+ &ap_req_options,
+ &ticket);
+ if (ret)
+ goto out;
+
+ /* check the server principal in the ticket matches digest/R@R */
+ {
+ krb5_principal principal = NULL;
+ const char *p, *r;
+
+ ret = krb5_ticket_get_server(context, ticket, &principal);
+ if (ret)
+ goto out;
+
+ ret = EINVAL;
+ krb5_set_error_message(context, ret, "Wrong digest server principal used");
+ p = krb5_principal_get_comp_string(context, principal, 0);
+ if (p == NULL) {
+ krb5_free_principal(context, principal);
+ goto out;
+ }
+ if (strcmp(p, KRB5_DIGEST_NAME) != 0) {
+ krb5_free_principal(context, principal);
+ goto out;
+ }
+
+ p = krb5_principal_get_comp_string(context, principal, 1);
+ if (p == NULL) {
+ krb5_free_principal(context, principal);
+ goto out;
+ }
+ r = krb5_principal_get_realm(context, principal);
+ if (r == NULL) {
+ krb5_free_principal(context, principal);
+ goto out;
+ }
+ if (strcmp(p, r) != 0) {
+ krb5_free_principal(context, principal);
+ goto out;
+ }
+ krb5_clear_error_message(context);
+
+ ret = _kdc_db_fetch(context, config, principal,
+ HDB_F_GET_SERVER, NULL, &server);
+ if (ret)
+ goto out;
+
+ krb5_free_principal(context, principal);
+ }
+
+ /* check the client is allowed to do digest auth */
+ {
+ krb5_principal principal = NULL;
+
+ ret = krb5_ticket_get_client(context, ticket, &principal);
+ if (ret)
+ goto out;
+
+ ret = krb5_unparse_name(context, principal, &client_name);
+ if (ret) {
+ krb5_free_principal(context, principal);
+ goto out;
+ }
+
+ ret = _kdc_db_fetch(context, config, principal,
+ HDB_F_GET_CLIENT, NULL, &client);
+ krb5_free_principal(context, principal);
+ if (ret)
+ goto out;
+
+ if (client->entry.flags.allow_digest == 0) {
+ kdc_log(context, config, 0,
+ "Client %s tried to use digest "
+ "but is not allowed to",
+ client_name);
+ ret = KRB5KDC_ERR_POLICY;
+ krb5_set_error_message(context, ret,
+ "Client is not permitted to use digest");
+ goto out;
+ }
+ }
+
+ /* unpack request */
+ {
+ krb5_keyblock *key;
+
+ ret = krb5_auth_con_getremotesubkey(context, ac, &key);
+ if (ret)
+ goto out;
+ if (key == NULL) {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret, "digest: remote subkey not found");
+ goto out;
+ }
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ krb5_free_keyblock (context, key);
+ if (ret)
+ goto out;
+ }
+
+ ret = krb5_decrypt_EncryptedData(context, crypto, KRB5_KU_DIGEST_ENCRYPT,
+ &req->innerReq, &buf);
+ krb5_crypto_destroy(context, crypto);
+ crypto = NULL;
+ if (ret)
+ goto out;
+
+ ret = decode_DigestReqInner(buf.data, buf.length, &ireq, NULL);
+ krb5_data_free(&buf);
+ if (ret) {
+ krb5_set_error_message(context, ret, "Failed to decode digest inner request");
+ goto out;
+ }
+
+ kdc_log(context, config, 0, "Valid digest request from %s (%s)",
+ client_name, from);
+
+ /*
+ * Process the inner request
+ */
+
+ switch (ireq.element) {
+ case choice_DigestReqInner_init: {
+ unsigned char server_nonce[16], identifier;
+
+ RAND_pseudo_bytes(&identifier, sizeof(identifier));
+ RAND_pseudo_bytes(server_nonce, sizeof(server_nonce));
+
+ server_nonce[0] = kdc_time & 0xff;
+ server_nonce[1] = (kdc_time >> 8) & 0xff;
+ server_nonce[2] = (kdc_time >> 16) & 0xff;
+ server_nonce[3] = (kdc_time >> 24) & 0xff;
+
+ r.element = choice_DigestRepInner_initReply;
+
+ hex_encode(server_nonce, sizeof(server_nonce), &r.u.initReply.nonce);
+ if (r.u.initReply.nonce == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "Failed to decode server nonce");
+ goto out;
+ }
+
+ sp = krb5_storage_emem();
+ if (sp == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto out;
+ }
+ ret = krb5_store_stringz(sp, ireq.u.init.type);
+ if (ret) {
+ krb5_clear_error_message(context);
+ goto out;
+ }
+
+ if (ireq.u.init.channel) {
+ char *s;
+
+ asprintf(&s, "%s-%s:%s", r.u.initReply.nonce,
+ ireq.u.init.channel->cb_type,
+ ireq.u.init.channel->cb_binding);
+ if (s == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret,
+ "Failed to allocate channel binding");
+ goto out;
+ }
+ free(r.u.initReply.nonce);
+ r.u.initReply.nonce = s;
+ }
+
+ ret = krb5_store_stringz(sp, r.u.initReply.nonce);
+ if (ret) {
+ krb5_clear_error_message(context);
+ goto out;
+ }
+
+ if (strcasecmp(ireq.u.init.type, "CHAP") == 0) {
+ r.u.initReply.identifier =
+ malloc(sizeof(*r.u.initReply.identifier));
+ if (r.u.initReply.identifier == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto out;
+ }
+
+ asprintf(r.u.initReply.identifier, "%02X", identifier & 0xff);
+ if (*r.u.initReply.identifier == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto out;
+ }
+
+ } else
+ r.u.initReply.identifier = NULL;
+
+ if (ireq.u.init.hostname) {
+ ret = krb5_store_stringz(sp, *ireq.u.init.hostname);
+ if (ret) {
+ krb5_clear_error_message(context);
+ goto out;
+ }
+ }
+
+ ret = krb5_storage_to_data(sp, &buf);
+ if (ret) {
+ krb5_clear_error_message(context);
+ goto out;
+ }
+
+ ret = get_digest_key(context, config, server, &crypto);
+ if (ret)
+ goto out;
+
+ ret = krb5_create_checksum(context,
+ crypto,
+ KRB5_KU_DIGEST_OPAQUE,
+ 0,
+ buf.data,
+ buf.length,
+ &res);
+ krb5_crypto_destroy(context, crypto);
+ crypto = NULL;
+ krb5_data_free(&buf);
+ if (ret)
+ goto out;
+
+ ASN1_MALLOC_ENCODE(Checksum, buf.data, buf.length, &res, &size, ret);
+ free_Checksum(&res);
+ if (ret) {
+ krb5_set_error_message(context, ret, "Failed to encode "
+ "checksum in digest request");
+ goto out;
+ }
+ if (size != buf.length)
+ krb5_abortx(context, "ASN1 internal error");
+
+ hex_encode(buf.data, buf.length, &r.u.initReply.opaque);
+ free(buf.data);
+ if (r.u.initReply.opaque == NULL) {
+ krb5_clear_error_message(context);
+ ret = ENOMEM;
+ goto out;
+ }
+
+ kdc_log(context, config, 0, "Digest %s init request successful from %s",
+ ireq.u.init.type, from);
+
+ break;
+ }
+ case choice_DigestReqInner_digestRequest: {
+ sp = krb5_storage_emem();
+ if (sp == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto out;
+ }
+ ret = krb5_store_stringz(sp, ireq.u.digestRequest.type);
+ if (ret) {
+ krb5_clear_error_message(context);
+ goto out;
+ }
+
+ krb5_store_stringz(sp, ireq.u.digestRequest.serverNonce);
+
+ if (ireq.u.digestRequest.hostname) {
+ ret = krb5_store_stringz(sp, *ireq.u.digestRequest.hostname);
+ if (ret) {
+ krb5_clear_error_message(context);
+ goto out;
+ }
+ }
+
+ buf.length = strlen(ireq.u.digestRequest.opaque);
+ buf.data = malloc(buf.length);
+ if (buf.data == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto out;
+ }
+
+ ret = hex_decode(ireq.u.digestRequest.opaque, buf.data, buf.length);
+ if (ret <= 0) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "Failed to decode opaque");
+ goto out;
+ }
+ buf.length = ret;
+
+ ret = decode_Checksum(buf.data, buf.length, &res, NULL);
+ free(buf.data);
+ if (ret) {
+ krb5_set_error_message(context, ret, "Failed to decode digest Checksum");
+ goto out;
+ }
+
+ ret = krb5_storage_to_data(sp, &buf);
+ if (ret) {
+ krb5_clear_error_message(context);
+ goto out;
+ }
+
+ serverNonce.length = strlen(ireq.u.digestRequest.serverNonce);
+ serverNonce.data = malloc(serverNonce.length);
+ if (serverNonce.data == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto out;
+ }
+
+ /*
+ * CHAP does the checksum of the raw nonce, but do it for all
+ * types, since we need to check the timestamp.
+ */
+ {
+ ssize_t ssize;
+
+ ssize = hex_decode(ireq.u.digestRequest.serverNonce,
+ serverNonce.data, serverNonce.length);
+ if (ssize <= 0) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "Failed to decode serverNonce");
+ goto out;
+ }
+ serverNonce.length = ssize;
+ }
+
+ ret = get_digest_key(context, config, server, &crypto);
+ if (ret)
+ goto out;
+
+ ret = krb5_verify_checksum(context, crypto,
+ KRB5_KU_DIGEST_OPAQUE,
+ buf.data, buf.length, &res);
+ krb5_crypto_destroy(context, crypto);
+ crypto = NULL;
+ if (ret)
+ goto out;
+
+ /* verify time */
+ {
+ unsigned char *p = serverNonce.data;
+ uint32_t t;
+
+ if (serverNonce.length < 4) {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret, "server nonce too short");
+ goto out;
+ }
+ t = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
+
+ if (abs((kdc_time & 0xffffffff) - t) > context->max_skew) {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret, "time screw in server nonce ");
+ goto out;
+ }
+ }
+
+ if (strcasecmp(ireq.u.digestRequest.type, "CHAP") == 0) {
+ MD5_CTX ctx;
+ unsigned char md[MD5_DIGEST_LENGTH];
+ char *mdx;
+ char id;
+
+ if ((config->digests_allowed & CHAP_MD5) == 0) {
+ kdc_log(context, config, 0, "Digest CHAP MD5 not allowed");
+ goto out;
+ }
+
+ if (ireq.u.digestRequest.identifier == NULL) {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret, "Identifier missing "
+ "from CHAP request");
+ goto out;
+ }
+
+ if (hex_decode(*ireq.u.digestRequest.identifier, &id, 1) != 1) {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret, "failed to decode identifier");
+ goto out;
+ }
+
+ ret = get_password_entry(context, config,
+ ireq.u.digestRequest.username,
+ &password);
+ if (ret)
+ goto out;
+
+ MD5_Init(&ctx);
+ MD5_Update(&ctx, &id, 1);
+ MD5_Update(&ctx, password, strlen(password));
+ MD5_Update(&ctx, serverNonce.data, serverNonce.length);
+ MD5_Final(md, &ctx);
+
+ hex_encode(md, sizeof(md), &mdx);
+ if (mdx == NULL) {
+ krb5_clear_error_message(context);
+ ret = ENOMEM;
+ goto out;
+ }
+
+ r.element = choice_DigestRepInner_response;
+
+ ret = strcasecmp(mdx, ireq.u.digestRequest.responseData);
+ free(mdx);
+ if (ret == 0) {
+ r.u.response.success = TRUE;
+ } else {
+ kdc_log(context, config, 0,
+ "CHAP reply mismatch for %s",
+ ireq.u.digestRequest.username);
+ r.u.response.success = FALSE;
+ }
+
+ } else if (strcasecmp(ireq.u.digestRequest.type, "SASL-DIGEST-MD5") == 0) {
+ MD5_CTX ctx;
+ unsigned char md[MD5_DIGEST_LENGTH];
+ char *mdx;
+ char *A1, *A2;
+
+ if ((config->digests_allowed & DIGEST_MD5) == 0) {
+ kdc_log(context, config, 0, "Digest SASL MD5 not allowed");
+ goto out;
+ }
+
+ if (ireq.u.digestRequest.nonceCount == NULL)
+ goto out;
+ if (ireq.u.digestRequest.clientNonce == NULL)
+ goto out;
+ if (ireq.u.digestRequest.qop == NULL)
+ goto out;
+ if (ireq.u.digestRequest.realm == NULL)
+ goto out;
+
+ ret = get_password_entry(context, config,
+ ireq.u.digestRequest.username,
+ &password);
+ if (ret)
+ goto failed;
+
+ MD5_Init(&ctx);
+ MD5_Update(&ctx, ireq.u.digestRequest.username,
+ strlen(ireq.u.digestRequest.username));
+ MD5_Update(&ctx, ":", 1);
+ MD5_Update(&ctx, *ireq.u.digestRequest.realm,
+ strlen(*ireq.u.digestRequest.realm));
+ MD5_Update(&ctx, ":", 1);
+ MD5_Update(&ctx, password, strlen(password));
+ MD5_Final(md, &ctx);
+
+ MD5_Init(&ctx);
+ MD5_Update(&ctx, md, sizeof(md));
+ MD5_Update(&ctx, ":", 1);
+ MD5_Update(&ctx, ireq.u.digestRequest.serverNonce,
+ strlen(ireq.u.digestRequest.serverNonce));
+ MD5_Update(&ctx, ":", 1);
+ MD5_Update(&ctx, *ireq.u.digestRequest.nonceCount,
+ strlen(*ireq.u.digestRequest.nonceCount));
+ if (ireq.u.digestRequest.authid) {
+ MD5_Update(&ctx, ":", 1);
+ MD5_Update(&ctx, *ireq.u.digestRequest.authid,
+ strlen(*ireq.u.digestRequest.authid));
+ }
+ MD5_Final(md, &ctx);
+ hex_encode(md, sizeof(md), &A1);
+ if (A1 == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto failed;
+ }
+
+ MD5_Init(&ctx);
+ MD5_Update(&ctx, "AUTHENTICATE:", sizeof("AUTHENTICATE:") - 1);
+ MD5_Update(&ctx, *ireq.u.digestRequest.uri,
+ strlen(*ireq.u.digestRequest.uri));
+
+ /* conf|int */
+ if (strcmp(ireq.u.digestRequest.digest, "clear") != 0) {
+ static char conf_zeros[] = ":00000000000000000000000000000000";
+ MD5_Update(&ctx, conf_zeros, sizeof(conf_zeros) - 1);
+ }
+
+ MD5_Final(md, &ctx);
+ hex_encode(md, sizeof(md), &A2);
+ if (A2 == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ free(A1);
+ goto failed;
+ }
+
+ MD5_Init(&ctx);
+ MD5_Update(&ctx, A1, strlen(A2));
+ MD5_Update(&ctx, ":", 1);
+ MD5_Update(&ctx, ireq.u.digestRequest.serverNonce,
+ strlen(ireq.u.digestRequest.serverNonce));
+ MD5_Update(&ctx, ":", 1);
+ MD5_Update(&ctx, *ireq.u.digestRequest.nonceCount,
+ strlen(*ireq.u.digestRequest.nonceCount));
+ MD5_Update(&ctx, ":", 1);
+ MD5_Update(&ctx, *ireq.u.digestRequest.clientNonce,
+ strlen(*ireq.u.digestRequest.clientNonce));
+ MD5_Update(&ctx, ":", 1);
+ MD5_Update(&ctx, *ireq.u.digestRequest.qop,
+ strlen(*ireq.u.digestRequest.qop));
+ MD5_Update(&ctx, ":", 1);
+ MD5_Update(&ctx, A2, strlen(A2));
+
+ MD5_Final(md, &ctx);
+
+ free(A1);
+ free(A2);
+
+ hex_encode(md, sizeof(md), &mdx);
+ if (mdx == NULL) {
+ krb5_clear_error_message(context);
+ ret = ENOMEM;
+ goto out;
+ }
+
+ r.element = choice_DigestRepInner_response;
+ ret = strcasecmp(mdx, ireq.u.digestRequest.responseData);
+ free(mdx);
+ if (ret == 0) {
+ r.u.response.success = TRUE;
+ } else {
+ kdc_log(context, config, 0,
+ "DIGEST-MD5 reply mismatch for %s",
+ ireq.u.digestRequest.username);
+ r.u.response.success = FALSE;
+ }
+
+ } else if (strcasecmp(ireq.u.digestRequest.type, "MS-CHAP-V2") == 0) {
+ unsigned char md[SHA_DIGEST_LENGTH], challange[SHA_DIGEST_LENGTH];
+ krb5_principal clientprincipal = NULL;
+ char *mdx;
+ const char *username;
+ struct ntlm_buf answer;
+ Key *key = NULL;
+ SHA_CTX ctx;
+
+ if ((config->digests_allowed & MS_CHAP_V2) == 0) {
+ kdc_log(context, config, 0, "MS-CHAP-V2 not allowed");
+ goto failed;
+ }
+
+ if (ireq.u.digestRequest.clientNonce == NULL) {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret,
+ "MS-CHAP-V2 clientNonce missing");
+ goto failed;
+ }
+ if (serverNonce.length != 16) {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret,
+ "MS-CHAP-V2 serverNonce wrong length");
+ goto failed;
+ }
+
+ /* strip of the domain component */
+ username = strchr(ireq.u.digestRequest.username, '\\');
+ if (username == NULL)
+ username = ireq.u.digestRequest.username;
+ else
+ username++;
+
+ /* ChallangeHash */
+ SHA1_Init(&ctx);
+ {
+ ssize_t ssize;
+ krb5_data clientNonce;
+
+ clientNonce.length = strlen(*ireq.u.digestRequest.clientNonce);
+ clientNonce.data = malloc(clientNonce.length);
+ if (clientNonce.data == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto out;
+ }
+
+ ssize = hex_decode(*ireq.u.digestRequest.clientNonce,
+ clientNonce.data, clientNonce.length);
+ if (ssize != 16) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret,
+ "Failed to decode clientNonce");
+ goto out;
+ }
+ SHA1_Update(&ctx, clientNonce.data, ssize);
+ free(clientNonce.data);
+ }
+ SHA1_Update(&ctx, serverNonce.data, serverNonce.length);
+ SHA1_Update(&ctx, username, strlen(username));
+ SHA1_Final(challange, &ctx);
+
+ /* NtPasswordHash */
+ ret = krb5_parse_name(context, username, &clientprincipal);
+ if (ret)
+ goto failed;
+
+ ret = _kdc_db_fetch(context, config, clientprincipal,
+ HDB_F_GET_CLIENT, NULL, &user);
+ krb5_free_principal(context, clientprincipal);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ "MS-CHAP-V2 user %s not in database",
+ username);
+ goto failed;
+ }
+
+ ret = hdb_enctype2key(context, &user->entry,
+ ETYPE_ARCFOUR_HMAC_MD5, &key);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ "MS-CHAP-V2 missing arcfour key %s",
+ username);
+ goto failed;
+ }
+
+ /* ChallengeResponse */
+ ret = heim_ntlm_calculate_ntlm1(key->key.keyvalue.data,
+ key->key.keyvalue.length,
+ challange, &answer);
+ if (ret) {
+ krb5_set_error_message(context, ret, "NTLM missing arcfour key");
+ goto failed;
+ }
+
+ hex_encode(answer.data, answer.length, &mdx);
+ if (mdx == NULL) {
+ free(answer.data);
+ krb5_clear_error_message(context);
+ ret = ENOMEM;
+ goto out;
+ }
+
+ r.element = choice_DigestRepInner_response;
+ ret = strcasecmp(mdx, ireq.u.digestRequest.responseData);
+ if (ret == 0) {
+ r.u.response.success = TRUE;
+ } else {
+ kdc_log(context, config, 0,
+ "MS-CHAP-V2 hash mismatch for %s",
+ ireq.u.digestRequest.username);
+ r.u.response.success = FALSE;
+ }
+ free(mdx);
+
+ if (r.u.response.success) {
+ unsigned char hashhash[MD4_DIGEST_LENGTH];
+
+ /* hashhash */
+ {
+ MD4_CTX hctx;
+
+ MD4_Init(&hctx);
+ MD4_Update(&hctx, key->key.keyvalue.data,
+ key->key.keyvalue.length);
+ MD4_Final(hashhash, &hctx);
+ }
+
+ /* GenerateAuthenticatorResponse */
+ SHA1_Init(&ctx);
+ SHA1_Update(&ctx, hashhash, sizeof(hashhash));
+ SHA1_Update(&ctx, answer.data, answer.length);
+ SHA1_Update(&ctx, ms_chap_v2_magic1,sizeof(ms_chap_v2_magic1));
+ SHA1_Final(md, &ctx);
+
+ SHA1_Init(&ctx);
+ SHA1_Update(&ctx, md, sizeof(md));
+ SHA1_Update(&ctx, challange, 8);
+ SHA1_Update(&ctx, ms_chap_v2_magic2, sizeof(ms_chap_v2_magic2));
+ SHA1_Final(md, &ctx);
+
+ r.u.response.rsp = calloc(1, sizeof(*r.u.response.rsp));
+ if (r.u.response.rsp == NULL) {
+ free(answer.data);
+ krb5_clear_error_message(context);
+ ret = ENOMEM;
+ goto out;
+ }
+
+ hex_encode(md, sizeof(md), r.u.response.rsp);
+ if (r.u.response.rsp == NULL) {
+ free(answer.data);
+ krb5_clear_error_message(context);
+ ret = ENOMEM;
+ goto out;
+ }
+
+ /* get_master, rfc 3079 3.4 */
+ SHA1_Init(&ctx);
+ SHA1_Update(&ctx, hashhash, 16); /* md4(hash) */
+ SHA1_Update(&ctx, answer.data, answer.length);
+ SHA1_Update(&ctx, ms_rfc3079_magic1, sizeof(ms_rfc3079_magic1));
+ SHA1_Final(md, &ctx);
+
+ free(answer.data);
+
+ r.u.response.session_key =
+ calloc(1, sizeof(*r.u.response.session_key));
+ if (r.u.response.session_key == NULL) {
+ krb5_clear_error_message(context);
+ ret = ENOMEM;
+ goto out;
+ }
+
+ ret = krb5_data_copy(r.u.response.session_key, md, 16);
+ if (ret) {
+ krb5_clear_error_message(context);
+ goto out;
+ }
+ }
+
+ } else {
+ r.element = choice_DigestRepInner_error;
+ asprintf(&r.u.error.reason, "Unsupported digest type %s",
+ ireq.u.digestRequest.type);
+ if (r.u.error.reason == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto out;
+ }
+ r.u.error.code = EINVAL;
+ }
+
+ kdc_log(context, config, 0, "Digest %s request successful %s",
+ ireq.u.digestRequest.type, ireq.u.digestRequest.username);
+
+ break;
+ }
+ case choice_DigestReqInner_ntlmInit:
+
+ if ((config->digests_allowed & (NTLM_V1|NTLM_V1_SESSION|NTLM_V2)) == 0) {
+ kdc_log(context, config, 0, "NTLM not allowed");
+ goto failed;
+ }
+
+ r.element = choice_DigestRepInner_ntlmInitReply;
+
+ r.u.ntlmInitReply.flags = NTLM_NEG_UNICODE;
+
+ if ((ireq.u.ntlmInit.flags & NTLM_NEG_UNICODE) == 0) {
+ kdc_log(context, config, 0, "NTLM client have no unicode");
+ goto failed;
+ }
+
+ if (ireq.u.ntlmInit.flags & NTLM_NEG_NTLM)
+ r.u.ntlmInitReply.flags |= NTLM_NEG_NTLM;
+ else {
+ kdc_log(context, config, 0, "NTLM client doesn't support NTLM");
+ goto failed;
+ }
+
+ r.u.ntlmInitReply.flags |=
+ NTLM_NEG_TARGET |
+ NTLM_TARGET_DOMAIN |
+ NTLM_ENC_128;
+
+#define ALL \
+ NTLM_NEG_SIGN| \
+ NTLM_NEG_SEAL| \
+ NTLM_NEG_ALWAYS_SIGN| \
+ NTLM_NEG_NTLM2_SESSION| \
+ NTLM_NEG_KEYEX
+
+ r.u.ntlmInitReply.flags |= (ireq.u.ntlmInit.flags & (ALL));
+
+#undef ALL
+
+ r.u.ntlmInitReply.targetname =
+ get_ntlm_targetname(context, client);
+ if (r.u.ntlmInitReply.targetname == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto out;
+ }
+ r.u.ntlmInitReply.challange.data = malloc(8);
+ if (r.u.ntlmInitReply.challange.data == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto out;
+ }
+ r.u.ntlmInitReply.challange.length = 8;
+ if (RAND_bytes(r.u.ntlmInitReply.challange.data,
+ r.u.ntlmInitReply.challange.length) != 1)
+ {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "out of random error");
+ goto out;
+ }
+ /* XXX fix targetinfo */
+ ALLOC(r.u.ntlmInitReply.targetinfo);
+ if (r.u.ntlmInitReply.targetinfo == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto out;
+ }
+
+ ret = fill_targetinfo(context,
+ r.u.ntlmInitReply.targetname,
+ client,
+ r.u.ntlmInitReply.targetinfo);
+ if (ret) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto out;
+ }
+
+ /*
+ * Save data encryted in opaque for the second part of the
+ * ntlm authentication
+ */
+ sp = krb5_storage_emem();
+ if (sp == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto out;
+ }
+
+ ret = krb5_storage_write(sp, r.u.ntlmInitReply.challange.data, 8);
+ if (ret != 8) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "storage write challange");
+ goto out;
+ }
+ ret = krb5_store_uint32(sp, r.u.ntlmInitReply.flags);
+ if (ret) {
+ krb5_clear_error_message(context);
+ goto out;
+ }
+
+ ret = krb5_storage_to_data(sp, &buf);
+ if (ret) {
+ krb5_clear_error_message(context);
+ goto out;
+ }
+
+ ret = get_digest_key(context, config, server, &crypto);
+ if (ret)
+ goto out;
+
+ ret = krb5_encrypt(context, crypto, KRB5_KU_DIGEST_OPAQUE,
+ buf.data, buf.length, &r.u.ntlmInitReply.opaque);
+ krb5_data_free(&buf);
+ krb5_crypto_destroy(context, crypto);
+ crypto = NULL;
+ if (ret)
+ goto out;
+
+ kdc_log(context, config, 0, "NTLM init from %s", from);
+
+ break;
+
+ case choice_DigestReqInner_ntlmRequest: {
+ krb5_principal clientprincipal;
+ unsigned char sessionkey[16];
+ unsigned char challange[8];
+ uint32_t flags;
+ Key *key = NULL;
+ int version;
+
+ r.element = choice_DigestRepInner_ntlmResponse;
+ r.u.ntlmResponse.success = 0;
+ r.u.ntlmResponse.flags = 0;
+ r.u.ntlmResponse.sessionkey = NULL;
+ r.u.ntlmResponse.tickets = NULL;
+
+ /* get username */
+ ret = krb5_parse_name(context,
+ ireq.u.ntlmRequest.username,
+ &clientprincipal);
+ if (ret)
+ goto failed;
+
+ ret = _kdc_db_fetch(context, config, clientprincipal,
+ HDB_F_GET_CLIENT, NULL, &user);
+ krb5_free_principal(context, clientprincipal);
+ if (ret) {
+ krb5_set_error_message(context, ret, "NTLM user %s not in database",
+ ireq.u.ntlmRequest.username);
+ goto failed;
+ }
+
+ ret = get_digest_key(context, config, server, &crypto);
+ if (ret)
+ goto failed;
+
+ ret = krb5_decrypt(context, crypto, KRB5_KU_DIGEST_OPAQUE,
+ ireq.u.ntlmRequest.opaque.data,
+ ireq.u.ntlmRequest.opaque.length, &buf);
+ krb5_crypto_destroy(context, crypto);
+ crypto = NULL;
+ if (ret) {
+ kdc_log(context, config, 0,
+ "Failed to decrypt nonce from %s", from);
+ goto failed;
+ }
+
+ sp = krb5_storage_from_data(&buf);
+ if (sp == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto out;
+ }
+
+ ret = krb5_storage_read(sp, challange, sizeof(challange));
+ if (ret != sizeof(challange)) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "NTLM storage read challange");
+ goto out;
+ }
+ ret = krb5_ret_uint32(sp, &flags);
+ if (ret) {
+ krb5_set_error_message(context, ret, "NTLM storage read flags");
+ goto out;
+ }
+ krb5_data_free(&buf);
+
+ if ((flags & NTLM_NEG_NTLM) == 0) {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret, "NTLM not negotiated");
+ goto out;
+ }
+
+ ret = hdb_enctype2key(context, &user->entry,
+ ETYPE_ARCFOUR_HMAC_MD5, &key);
+ if (ret) {
+ krb5_set_error_message(context, ret, "NTLM missing arcfour key");
+ goto out;
+ }
+
+ /* check if this is NTLMv2 */
+ if (ireq.u.ntlmRequest.ntlm.length != 24) {
+ struct ntlm_buf infotarget, answer;
+ char *targetname;
+
+ if ((config->digests_allowed & NTLM_V2) == 0) {
+ kdc_log(context, config, 0, "NTLM v2 not allowed");
+ goto out;
+ }
+
+ version = 2;
+
+ targetname = get_ntlm_targetname(context, client);
+ if (targetname == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto out;
+ }
+
+ answer.length = ireq.u.ntlmRequest.ntlm.length;
+ answer.data = ireq.u.ntlmRequest.ntlm.data;
+
+ ret = heim_ntlm_verify_ntlm2(key->key.keyvalue.data,
+ key->key.keyvalue.length,
+ ireq.u.ntlmRequest.username,
+ targetname,
+ 0,
+ challange,
+ &answer,
+ &infotarget,
+ sessionkey);
+ free(targetname);
+ if (ret) {
+ krb5_set_error_message(context, ret, "NTLM v2 verify failed");
+ goto failed;
+ }
+
+ /* XXX verify infotarget matches client (checksum ?) */
+
+ free(infotarget.data);
+ /* */
+
+ } else {
+ struct ntlm_buf answer;
+
+ version = 1;
+
+ if (flags & NTLM_NEG_NTLM2_SESSION) {
+ unsigned char sessionhash[MD5_DIGEST_LENGTH];
+ MD5_CTX md5ctx;
+
+ if ((config->digests_allowed & NTLM_V1_SESSION) == 0) {
+ kdc_log(context, config, 0, "NTLM v1-session not allowed");
+ ret = EINVAL;
+ goto failed;
+ }
+
+ if (ireq.u.ntlmRequest.lm.length != 24) {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret, "LM hash have wrong length "
+ "for NTLM session key");
+ goto failed;
+ }
+
+ MD5_Init(&md5ctx);
+ MD5_Update(&md5ctx, challange, sizeof(challange));
+ MD5_Update(&md5ctx, ireq.u.ntlmRequest.lm.data, 8);
+ MD5_Final(sessionhash, &md5ctx);
+ memcpy(challange, sessionhash, sizeof(challange));
+ } else {
+ if ((config->digests_allowed & NTLM_V1) == 0) {
+ kdc_log(context, config, 0, "NTLM v1 not allowed");
+ goto failed;
+ }
+ }
+
+ ret = heim_ntlm_calculate_ntlm1(key->key.keyvalue.data,
+ key->key.keyvalue.length,
+ challange, &answer);
+ if (ret) {
+ krb5_set_error_message(context, ret, "NTLM missing arcfour key");
+ goto failed;
+ }
+
+ if (ireq.u.ntlmRequest.ntlm.length != answer.length ||
+ memcmp(ireq.u.ntlmRequest.ntlm.data, answer.data, answer.length) != 0)
+ {
+ free(answer.data);
+ ret = EINVAL;
+ krb5_set_error_message(context, ret, "NTLM hash mismatch");
+ goto failed;
+ }
+ free(answer.data);
+
+ {
+ MD4_CTX ctx;
+
+ MD4_Init(&ctx);
+ MD4_Update(&ctx,
+ key->key.keyvalue.data, key->key.keyvalue.length);
+ MD4_Final(sessionkey, &ctx);
+ }
+ }
+
+ if (ireq.u.ntlmRequest.sessionkey) {
+ unsigned char masterkey[MD4_DIGEST_LENGTH];
+ RC4_KEY rc4;
+ size_t len;
+
+ if ((flags & NTLM_NEG_KEYEX) == 0) {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret,
+ "NTLM client failed to neg key "
+ "exchange but still sent key");
+ goto failed;
+ }
+
+ len = ireq.u.ntlmRequest.sessionkey->length;
+ if (len != sizeof(masterkey)){
+ ret = EINVAL;
+ krb5_set_error_message(context, ret,
+ "NTLM master key wrong length: %lu",
+ (unsigned long)len);
+ goto failed;
+ }
+
+ RC4_set_key(&rc4, sizeof(sessionkey), sessionkey);
+
+ RC4(&rc4, sizeof(masterkey),
+ ireq.u.ntlmRequest.sessionkey->data,
+ masterkey);
+ memset(&rc4, 0, sizeof(rc4));
+
+ r.u.ntlmResponse.sessionkey =
+ malloc(sizeof(*r.u.ntlmResponse.sessionkey));
+ if (r.u.ntlmResponse.sessionkey == NULL) {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto out;
+ }
+
+ ret = krb5_data_copy(r.u.ntlmResponse.sessionkey,
+ masterkey, sizeof(masterkey));
+ if (ret) {
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto out;
+ }
+ }
+
+ r.u.ntlmResponse.success = 1;
+ kdc_log(context, config, 0, "NTLM version %d successful for %s",
+ version, ireq.u.ntlmRequest.username);
+ break;
+ }
+ case choice_DigestReqInner_supportedMechs:
+
+ kdc_log(context, config, 0, "digest supportedMechs from %s", from);
+
+ r.element = choice_DigestRepInner_supportedMechs;
+ memset(&r.u.supportedMechs, 0, sizeof(r.u.supportedMechs));
+
+ if (config->digests_allowed & NTLM_V1)
+ r.u.supportedMechs.ntlm_v1 = 1;
+ if (config->digests_allowed & NTLM_V1_SESSION)
+ r.u.supportedMechs.ntlm_v1_session = 1;
+ if (config->digests_allowed & NTLM_V2)
+ r.u.supportedMechs.ntlm_v2 = 1;
+ if (config->digests_allowed & DIGEST_MD5)
+ r.u.supportedMechs.digest_md5 = 1;
+ if (config->digests_allowed & CHAP_MD5)
+ r.u.supportedMechs.chap_md5 = 1;
+ if (config->digests_allowed & MS_CHAP_V2)
+ r.u.supportedMechs.ms_chap_v2 = 1;
+ break;
+
+ default: {
+ const char *s;
+ ret = EINVAL;
+ krb5_set_error_message(context, ret, "unknown operation to digest");
+
+ failed:
+
+ s = krb5_get_error_message(context, ret);
+ if (s == NULL) {
+ krb5_clear_error_message(context);
+ goto out;
+ }
+
+ kdc_log(context, config, 0, "Digest failed with: %s", s);
+
+ r.element = choice_DigestRepInner_error;
+ r.u.error.reason = strdup("unknown error");
+ krb5_free_error_message(context, s);
+ if (r.u.error.reason == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto out;
+ }
+ r.u.error.code = EINVAL;
+ break;
+ }
+ }
+
+ ASN1_MALLOC_ENCODE(DigestRepInner, buf.data, buf.length, &r, &size, ret);
+ if (ret) {
+ krb5_set_error_message(context, ret, "Failed to encode inner digest reply");
+ goto out;
+ }
+ if (size != buf.length)
+ krb5_abortx(context, "ASN1 internal error");
+
+ krb5_auth_con_addflags(context, ac, KRB5_AUTH_CONTEXT_USE_SUBKEY, NULL);
+
+ ret = krb5_mk_rep (context, ac, &rep.apRep);
+ if (ret)
+ goto out;
+
+ {
+ krb5_keyblock *key;
+
+ ret = krb5_auth_con_getlocalsubkey(context, ac, &key);
+ if (ret)
+ goto out;
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ krb5_free_keyblock (context, key);
+ if (ret)
+ goto out;
+ }
+
+ ret = krb5_encrypt_EncryptedData(context, crypto, KRB5_KU_DIGEST_ENCRYPT,
+ buf.data, buf.length, 0,
+ &rep.innerRep);
+
+ ASN1_MALLOC_ENCODE(DigestREP, reply->data, reply->length, &rep, &size, ret);
+ if (ret) {
+ krb5_set_error_message(context, ret, "Failed to encode digest reply");
+ goto out;
+ }
+ if (size != reply->length)
+ krb5_abortx(context, "ASN1 internal error");
+
+
+ out:
+ if (ac)
+ krb5_auth_con_free(context, ac);
+ if (ret)
+ krb5_warn(context, ret, "Digest request from %s failed", from);
+ if (ticket)
+ krb5_free_ticket(context, ticket);
+ if (id)
+ krb5_kt_close(context, id);
+ if (crypto)
+ krb5_crypto_destroy(context, crypto);
+ if (sp)
+ krb5_storage_free(sp);
+ if (user)
+ _kdc_free_ent (context, user);
+ if (server)
+ _kdc_free_ent (context, server);
+ if (client)
+ _kdc_free_ent (context, client);
+ if (password) {
+ memset(password, 0, strlen(password));
+ free (password);
+ }
+ if (client_name)
+ free (client_name);
+ krb5_data_free(&buf);
+ krb5_data_free(&serverNonce);
+ free_DigestREP(&rep);
+ free_DigestRepInner(&r);
+ free_DigestReqInner(&ireq);
+
+ return ret;
+}
diff --git a/source4/heimdal/kdc/headers.h b/source4/heimdal/kdc/headers.h
new file mode 100644
index 0000000000..3635d3c56a
--- /dev/null
+++ b/source4/heimdal/kdc/headers.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 1997 - 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id$
+ */
+
+#ifndef __HEADERS_H__
+#define __HEADERS_H__
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdarg.h>
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETINET_IN6_H
+#include <netinet/in6.h>
+#endif
+#ifdef HAVE_NETINET6_IN6_H
+#include <netinet6/in6.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef HAVE_UTIL_H
+#include <util.h>
+#endif
+#ifdef HAVE_LIBUTIL_H
+#include <libutil.h>
+#endif
+#include <err.h>
+#include <roken.h>
+#include <getarg.h>
+#include <base64.h>
+#include <parse_units.h>
+#include <krb5.h>
+#include <krb5_locl.h>
+#include <digest_asn1.h>
+#include <kx509_asn1.h>
+#include <hdb.h>
+#include <hdb_err.h>
+#include <der.h>
+
+#include <heimntlm.h>
+#include <windc_plugin.h>
+
+#undef ALLOC
+#define ALLOC(X) ((X) = malloc(sizeof(*(X))))
+#undef ALLOC_SEQ
+#define ALLOC_SEQ(X, N) do { (X)->len = (N); \
+(X)->val = calloc((X)->len, sizeof(*(X)->val)); } while(0)
+
+#endif /* __HEADERS_H__ */
diff --git a/source4/heimdal/kdc/kaserver.c b/source4/heimdal/kdc/kaserver.c
new file mode 100644
index 0000000000..9226ae115d
--- /dev/null
+++ b/source4/heimdal/kdc/kaserver.c
@@ -0,0 +1,951 @@
+/*
+ * Copyright (c) 1997 - 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kdc_locl.h"
+
+RCSID("$Id$");
+
+#include <krb5-v4compat.h>
+#include <rx.h>
+
+#define KA_AUTHENTICATION_SERVICE 731
+#define KA_TICKET_GRANTING_SERVICE 732
+#define KA_MAINTENANCE_SERVICE 733
+
+#define AUTHENTICATE_OLD 1
+#define CHANGEPASSWORD 2
+#define GETTICKET_OLD 3
+#define SETPASSWORD 4
+#define SETFIELDS 5
+#define CREATEUSER 6
+#define DELETEUSER 7
+#define GETENTRY 8
+#define LISTENTRY 9
+#define GETSTATS 10
+#define DEBUG 11
+#define GETPASSWORD 12
+#define GETRANDOMKEY 13
+#define AUTHENTICATE 21
+#define AUTHENTICATE_V2 22
+#define GETTICKET 23
+
+/* XXX - Where do we get these? */
+
+#define RXGEN_OPCODE (-455)
+
+#define KADATABASEINCONSISTENT (180480L)
+#define KAEXIST (180481L)
+#define KAIO (180482L)
+#define KACREATEFAIL (180483L)
+#define KANOENT (180484L)
+#define KAEMPTY (180485L)
+#define KABADNAME (180486L)
+#define KABADINDEX (180487L)
+#define KANOAUTH (180488L)
+#define KAANSWERTOOLONG (180489L)
+#define KABADREQUEST (180490L)
+#define KAOLDINTERFACE (180491L)
+#define KABADARGUMENT (180492L)
+#define KABADCMD (180493L)
+#define KANOKEYS (180494L)
+#define KAREADPW (180495L)
+#define KABADKEY (180496L)
+#define KAUBIKINIT (180497L)
+#define KAUBIKCALL (180498L)
+#define KABADPROTOCOL (180499L)
+#define KANOCELLS (180500L)
+#define KANOCELL (180501L)
+#define KATOOMANYUBIKS (180502L)
+#define KATOOMANYKEYS (180503L)
+#define KABADTICKET (180504L)
+#define KAUNKNOWNKEY (180505L)
+#define KAKEYCACHEINVALID (180506L)
+#define KABADSERVER (180507L)
+#define KABADUSER (180508L)
+#define KABADCPW (180509L)
+#define KABADCREATE (180510L)
+#define KANOTICKET (180511L)
+#define KAASSOCUSER (180512L)
+#define KANOTSPECIAL (180513L)
+#define KACLOCKSKEW (180514L)
+#define KANORECURSE (180515L)
+#define KARXFAIL (180516L)
+#define KANULLPASSWORD (180517L)
+#define KAINTERNALERROR (180518L)
+#define KAPWEXPIRED (180519L)
+#define KAREUSED (180520L)
+#define KATOOSOON (180521L)
+#define KALOCKED (180522L)
+
+
+static krb5_error_code
+decode_rx_header (krb5_storage *sp,
+ struct rx_header *h)
+{
+ krb5_error_code ret;
+
+ ret = krb5_ret_uint32(sp, &h->epoch);
+ if (ret) return ret;
+ ret = krb5_ret_uint32(sp, &h->connid);
+ if (ret) return ret;
+ ret = krb5_ret_uint32(sp, &h->callid);
+ if (ret) return ret;
+ ret = krb5_ret_uint32(sp, &h->seqno);
+ if (ret) return ret;
+ ret = krb5_ret_uint32(sp, &h->serialno);
+ if (ret) return ret;
+ ret = krb5_ret_uint8(sp, &h->type);
+ if (ret) return ret;
+ ret = krb5_ret_uint8(sp, &h->flags);
+ if (ret) return ret;
+ ret = krb5_ret_uint8(sp, &h->status);
+ if (ret) return ret;
+ ret = krb5_ret_uint8(sp, &h->secindex);
+ if (ret) return ret;
+ ret = krb5_ret_uint16(sp, &h->reserved);
+ if (ret) return ret;
+ ret = krb5_ret_uint16(sp, &h->serviceid);
+ if (ret) return ret;
+
+ return 0;
+}
+
+static krb5_error_code
+encode_rx_header (struct rx_header *h,
+ krb5_storage *sp)
+{
+ krb5_error_code ret;
+
+ ret = krb5_store_uint32(sp, h->epoch);
+ if (ret) return ret;
+ ret = krb5_store_uint32(sp, h->connid);
+ if (ret) return ret;
+ ret = krb5_store_uint32(sp, h->callid);
+ if (ret) return ret;
+ ret = krb5_store_uint32(sp, h->seqno);
+ if (ret) return ret;
+ ret = krb5_store_uint32(sp, h->serialno);
+ if (ret) return ret;
+ ret = krb5_store_uint8(sp, h->type);
+ if (ret) return ret;
+ ret = krb5_store_uint8(sp, h->flags);
+ if (ret) return ret;
+ ret = krb5_store_uint8(sp, h->status);
+ if (ret) return ret;
+ ret = krb5_store_uint8(sp, h->secindex);
+ if (ret) return ret;
+ ret = krb5_store_uint16(sp, h->reserved);
+ if (ret) return ret;
+ ret = krb5_store_uint16(sp, h->serviceid);
+ if (ret) return ret;
+
+ return 0;
+}
+
+static void
+init_reply_header (struct rx_header *hdr,
+ struct rx_header *reply_hdr,
+ u_char type,
+ u_char flags)
+{
+ reply_hdr->epoch = hdr->epoch;
+ reply_hdr->connid = hdr->connid;
+ reply_hdr->callid = hdr->callid;
+ reply_hdr->seqno = 1;
+ reply_hdr->serialno = 1;
+ reply_hdr->type = type;
+ reply_hdr->flags = flags;
+ reply_hdr->status = 0;
+ reply_hdr->secindex = 0;
+ reply_hdr->reserved = 0;
+ reply_hdr->serviceid = hdr->serviceid;
+}
+
+/*
+ * Create an error `reply´ using for the packet `hdr' with the error
+ * `error´ code.
+ */
+static void
+make_error_reply (struct rx_header *hdr,
+ uint32_t error,
+ krb5_data *reply)
+
+{
+ struct rx_header reply_hdr;
+ krb5_error_code ret;
+ krb5_storage *sp;
+
+ init_reply_header (hdr, &reply_hdr, HT_ABORT, HF_LAST);
+ sp = krb5_storage_emem();
+ if (sp == NULL)
+ return;
+ ret = encode_rx_header (&reply_hdr, sp);
+ if (ret)
+ return;
+ krb5_store_int32(sp, error);
+ krb5_storage_to_data (sp, reply);
+ krb5_storage_free (sp);
+}
+
+static krb5_error_code
+krb5_ret_xdr_data(krb5_storage *sp,
+ krb5_data *data)
+{
+ int ret;
+ int size;
+ ret = krb5_ret_int32(sp, &size);
+ if(ret)
+ return ret;
+ if(size < 0)
+ return ERANGE;
+ data->length = size;
+ if (size) {
+ u_char foo[4];
+ size_t pad = (4 - size % 4) % 4;
+
+ data->data = malloc(size);
+ if (data->data == NULL)
+ return ENOMEM;
+ ret = krb5_storage_read(sp, data->data, size);
+ if(ret != size)
+ return (ret < 0)? errno : KRB5_CC_END;
+ if (pad) {
+ ret = krb5_storage_read(sp, foo, pad);
+ if (ret != pad)
+ return (ret < 0)? errno : KRB5_CC_END;
+ }
+ } else
+ data->data = NULL;
+ return 0;
+}
+
+static krb5_error_code
+krb5_store_xdr_data(krb5_storage *sp,
+ krb5_data data)
+{
+ u_char zero[4] = {0, 0, 0, 0};
+ int ret;
+ size_t pad;
+
+ ret = krb5_store_int32(sp, data.length);
+ if(ret < 0)
+ return ret;
+ ret = krb5_storage_write(sp, data.data, data.length);
+ if(ret != data.length){
+ if(ret < 0)
+ return errno;
+ return KRB5_CC_END;
+ }
+ pad = (4 - data.length % 4) % 4;
+ if (pad) {
+ ret = krb5_storage_write(sp, zero, pad);
+ if (ret != pad) {
+ if (ret < 0)
+ return errno;
+ return KRB5_CC_END;
+ }
+ }
+ return 0;
+}
+
+
+static krb5_error_code
+create_reply_ticket (krb5_context context,
+ struct rx_header *hdr,
+ Key *skey,
+ char *name, char *instance, char *realm,
+ struct sockaddr_in *addr,
+ int life,
+ int kvno,
+ int32_t max_seq_len,
+ const char *sname, const char *sinstance,
+ uint32_t challenge,
+ const char *label,
+ krb5_keyblock *key,
+ krb5_data *reply)
+{
+ krb5_error_code ret;
+ krb5_data ticket;
+ krb5_keyblock session;
+ krb5_storage *sp;
+ krb5_data enc_data;
+ struct rx_header reply_hdr;
+ char zero[8];
+ size_t pad;
+ unsigned fyrtiosjuelva;
+
+ /* create the ticket */
+
+ krb5_generate_random_keyblock(context, ETYPE_DES_PCBC_NONE, &session);
+
+ _krb5_krb_create_ticket(context,
+ 0,
+ name,
+ instance,
+ realm,
+ addr->sin_addr.s_addr,
+ &session,
+ life,
+ kdc_time,
+ sname,
+ sinstance,
+ &skey->key,
+ &ticket);
+
+ /* create the encrypted part of the reply */
+ sp = krb5_storage_emem ();
+ krb5_generate_random_block(&fyrtiosjuelva, sizeof(fyrtiosjuelva));
+ fyrtiosjuelva &= 0xffffffff;
+ krb5_store_int32 (sp, fyrtiosjuelva);
+ krb5_store_int32 (sp, challenge);
+ krb5_storage_write (sp, session.keyvalue.data, 8);
+ krb5_free_keyblock_contents(context, &session);
+ krb5_store_int32 (sp, kdc_time);
+ krb5_store_int32 (sp, kdc_time + _krb5_krb_life_to_time (0, life));
+ krb5_store_int32 (sp, kvno);
+ krb5_store_int32 (sp, ticket.length);
+ krb5_store_stringz (sp, name);
+ krb5_store_stringz (sp, instance);
+#if 1 /* XXX - Why shouldn't the realm go here? */
+ krb5_store_stringz (sp, "");
+#else
+ krb5_store_stringz (sp, realm);
+#endif
+ krb5_store_stringz (sp, sname);
+ krb5_store_stringz (sp, sinstance);
+ krb5_storage_write (sp, ticket.data, ticket.length);
+ krb5_storage_write (sp, label, strlen(label));
+
+ /* pad to DES block */
+ memset (zero, 0, sizeof(zero));
+ pad = (8 - krb5_storage_seek (sp, 0, SEEK_CUR) % 8) % 8;
+ krb5_storage_write (sp, zero, pad);
+
+ krb5_storage_to_data (sp, &enc_data);
+ krb5_storage_free (sp);
+
+ if (enc_data.length > max_seq_len) {
+ krb5_data_free (&enc_data);
+ make_error_reply (hdr, KAANSWERTOOLONG, reply);
+ return 0;
+ }
+
+ /* encrypt it */
+ {
+ DES_key_schedule schedule;
+ DES_cblock deskey;
+
+ memcpy (&deskey, key->keyvalue.data, sizeof(deskey));
+ DES_set_key_unchecked (&deskey, &schedule);
+ DES_pcbc_encrypt (enc_data.data,
+ enc_data.data,
+ enc_data.length,
+ &schedule,
+ &deskey,
+ DES_ENCRYPT);
+ memset (&schedule, 0, sizeof(schedule));
+ memset (&deskey, 0, sizeof(deskey));
+ }
+
+ /* create the reply packet */
+ init_reply_header (hdr, &reply_hdr, HT_DATA, HF_LAST);
+ sp = krb5_storage_emem ();
+ ret = encode_rx_header (&reply_hdr, sp);
+ krb5_store_int32 (sp, max_seq_len);
+ krb5_store_xdr_data (sp, enc_data);
+ krb5_data_free (&enc_data);
+ krb5_storage_to_data (sp, reply);
+ krb5_storage_free (sp);
+ return 0;
+}
+
+static krb5_error_code
+unparse_auth_args (krb5_storage *sp,
+ char **name,
+ char **instance,
+ time_t *start_time,
+ time_t *end_time,
+ krb5_data *request,
+ int32_t *max_seq_len)
+{
+ krb5_data data;
+ int32_t tmp;
+
+ krb5_ret_xdr_data (sp, &data);
+ *name = malloc(data.length + 1);
+ if (*name == NULL)
+ return ENOMEM;
+ memcpy (*name, data.data, data.length);
+ (*name)[data.length] = '\0';
+ krb5_data_free (&data);
+
+ krb5_ret_xdr_data (sp, &data);
+ *instance = malloc(data.length + 1);
+ if (*instance == NULL) {
+ free (*name);
+ return ENOMEM;
+ }
+ memcpy (*instance, data.data, data.length);
+ (*instance)[data.length] = '\0';
+ krb5_data_free (&data);
+
+ krb5_ret_int32 (sp, &tmp);
+ *start_time = tmp;
+ krb5_ret_int32 (sp, &tmp);
+ *end_time = tmp;
+ krb5_ret_xdr_data (sp, request);
+ krb5_ret_int32 (sp, max_seq_len);
+ /* ignore the rest */
+ return 0;
+}
+
+static void
+do_authenticate (krb5_context context,
+ krb5_kdc_configuration *config,
+ struct rx_header *hdr,
+ krb5_storage *sp,
+ struct sockaddr_in *addr,
+ const char *from,
+ krb5_data *reply)
+{
+ krb5_error_code ret;
+ char *name = NULL;
+ char *instance = NULL;
+ time_t start_time;
+ time_t end_time;
+ krb5_data request;
+ int32_t max_seq_len;
+ hdb_entry_ex *client_entry = NULL;
+ hdb_entry_ex *server_entry = NULL;
+ Key *ckey = NULL;
+ Key *skey = NULL;
+ krb5_storage *reply_sp;
+ time_t max_life;
+ uint8_t life;
+ int32_t chal;
+ char client_name[256];
+ char server_name[256];
+
+ krb5_data_zero (&request);
+
+ ret = unparse_auth_args (sp, &name, &instance, &start_time, &end_time,
+ &request, &max_seq_len);
+ if (ret != 0 || request.length < 8) {
+ make_error_reply (hdr, KABADREQUEST, reply);
+ goto out;
+ }
+
+ snprintf (client_name, sizeof(client_name), "%s.%s@%s",
+ name, instance, config->v4_realm);
+ snprintf (server_name, sizeof(server_name), "%s.%s@%s",
+ "krbtgt", config->v4_realm, config->v4_realm);
+
+ kdc_log(context, config, 0, "AS-REQ (kaserver) %s from %s for %s",
+ client_name, from, server_name);
+
+ ret = _kdc_db_fetch4 (context, config, name, instance,
+ config->v4_realm, HDB_F_GET_CLIENT,
+ &client_entry);
+ if (ret) {
+ kdc_log(context, config, 0, "Client not found in database: %s: %s",
+ client_name, krb5_get_err_text(context, ret));
+ make_error_reply (hdr, KANOENT, reply);
+ goto out;
+ }
+
+ ret = _kdc_db_fetch4 (context, config, "krbtgt",
+ config->v4_realm, config->v4_realm,
+ HDB_F_GET_KRBTGT, &server_entry);
+ if (ret) {
+ kdc_log(context, config, 0, "Server not found in database: %s: %s",
+ server_name, krb5_get_err_text(context, ret));
+ make_error_reply (hdr, KANOENT, reply);
+ goto out;
+ }
+
+ ret = _kdc_check_flags (context, config,
+ client_entry, client_name,
+ server_entry, server_name,
+ TRUE);
+ if (ret) {
+ make_error_reply (hdr, KAPWEXPIRED, reply);
+ goto out;
+ }
+
+ /* find a DES key */
+ ret = _kdc_get_des_key(context, client_entry, FALSE, TRUE, &ckey);
+ if(ret){
+ kdc_log(context, config, 0, "no suitable DES key for client");
+ make_error_reply (hdr, KANOKEYS, reply);
+ goto out;
+ }
+
+ /* find a DES key */
+ ret = _kdc_get_des_key(context, server_entry, TRUE, TRUE, &skey);
+ if(ret){
+ kdc_log(context, config, 0, "no suitable DES key for server");
+ make_error_reply (hdr, KANOKEYS, reply);
+ goto out;
+ }
+
+ {
+ DES_cblock key;
+ DES_key_schedule schedule;
+
+ /* try to decode the `request' */
+ memcpy (&key, ckey->key.keyvalue.data, sizeof(key));
+ DES_set_key_unchecked (&key, &schedule);
+ DES_pcbc_encrypt (request.data,
+ request.data,
+ request.length,
+ &schedule,
+ &key,
+ DES_DECRYPT);
+ memset (&schedule, 0, sizeof(schedule));
+ memset (&key, 0, sizeof(key));
+ }
+
+ /* check for the magic label */
+ if (memcmp ((char *)request.data + 4, "gTGS", 4) != 0) {
+ kdc_log(context, config, 0, "preauth failed for %s", client_name);
+ make_error_reply (hdr, KABADREQUEST, reply);
+ goto out;
+ }
+
+ reply_sp = krb5_storage_from_mem (request.data, 4);
+ krb5_ret_int32 (reply_sp, &chal);
+ krb5_storage_free (reply_sp);
+
+ if (abs(chal - kdc_time) > context->max_skew) {
+ make_error_reply (hdr, KACLOCKSKEW, reply);
+ goto out;
+ }
+
+ /* life */
+ max_life = end_time - kdc_time;
+ /* end_time - kdc_time can sometimes be non-positive due to slight
+ time skew between client and server. Let's make sure it is postive */
+ if(max_life < 1)
+ max_life = 1;
+ if (client_entry->entry.max_life)
+ max_life = min(max_life, *client_entry->entry.max_life);
+ if (server_entry->entry.max_life)
+ max_life = min(max_life, *server_entry->entry.max_life);
+
+ life = krb_time_to_life(kdc_time, kdc_time + max_life);
+
+ create_reply_ticket (context,
+ hdr, skey,
+ name, instance, config->v4_realm,
+ addr, life, server_entry->entry.kvno,
+ max_seq_len,
+ "krbtgt", config->v4_realm,
+ chal + 1, "tgsT",
+ &ckey->key, reply);
+
+ out:
+ if (request.length) {
+ memset (request.data, 0, request.length);
+ krb5_data_free (&request);
+ }
+ if (name)
+ free (name);
+ if (instance)
+ free (instance);
+ if (client_entry)
+ _kdc_free_ent (context, client_entry);
+ if (server_entry)
+ _kdc_free_ent (context, server_entry);
+}
+
+static krb5_error_code
+unparse_getticket_args (krb5_storage *sp,
+ int *kvno,
+ char **auth_domain,
+ krb5_data *ticket,
+ char **name,
+ char **instance,
+ krb5_data *times,
+ int32_t *max_seq_len)
+{
+ krb5_data data;
+ int32_t tmp;
+
+ krb5_ret_int32 (sp, &tmp);
+ *kvno = tmp;
+
+ krb5_ret_xdr_data (sp, &data);
+ *auth_domain = malloc(data.length + 1);
+ if (*auth_domain == NULL)
+ return ENOMEM;
+ memcpy (*auth_domain, data.data, data.length);
+ (*auth_domain)[data.length] = '\0';
+ krb5_data_free (&data);
+
+ krb5_ret_xdr_data (sp, ticket);
+
+ krb5_ret_xdr_data (sp, &data);
+ *name = malloc(data.length + 1);
+ if (*name == NULL) {
+ free (*auth_domain);
+ return ENOMEM;
+ }
+ memcpy (*name, data.data, data.length);
+ (*name)[data.length] = '\0';
+ krb5_data_free (&data);
+
+ krb5_ret_xdr_data (sp, &data);
+ *instance = malloc(data.length + 1);
+ if (*instance == NULL) {
+ free (*auth_domain);
+ free (*name);
+ return ENOMEM;
+ }
+ memcpy (*instance, data.data, data.length);
+ (*instance)[data.length] = '\0';
+ krb5_data_free (&data);
+
+ krb5_ret_xdr_data (sp, times);
+
+ krb5_ret_int32 (sp, max_seq_len);
+ /* ignore the rest */
+ return 0;
+}
+
+static void
+do_getticket (krb5_context context,
+ krb5_kdc_configuration *config,
+ struct rx_header *hdr,
+ krb5_storage *sp,
+ struct sockaddr_in *addr,
+ const char *from,
+ krb5_data *reply)
+{
+ krb5_error_code ret;
+ int kvno;
+ char *auth_domain = NULL;
+ krb5_data aticket;
+ char *name = NULL;
+ char *instance = NULL;
+ krb5_data times;
+ int32_t max_seq_len;
+ hdb_entry_ex *server_entry = NULL;
+ hdb_entry_ex *client_entry = NULL;
+ hdb_entry_ex *krbtgt_entry = NULL;
+ Key *kkey = NULL;
+ Key *skey = NULL;
+ DES_cblock key;
+ DES_key_schedule schedule;
+ DES_cblock session;
+ time_t max_life;
+ int8_t life;
+ time_t start_time, end_time;
+ char server_name[256];
+ char client_name[256];
+ struct _krb5_krb_auth_data ad;
+
+ krb5_data_zero (&aticket);
+ krb5_data_zero (&times);
+
+ memset(&ad, 0, sizeof(ad));
+
+ unparse_getticket_args (sp, &kvno, &auth_domain, &aticket,
+ &name, &instance, &times, &max_seq_len);
+ if (times.length < 8) {
+ make_error_reply (hdr, KABADREQUEST, reply);
+ goto out;
+
+ }
+
+ snprintf (server_name, sizeof(server_name),
+ "%s.%s@%s", name, instance, config->v4_realm);
+
+ ret = _kdc_db_fetch4 (context, config, name, instance,
+ config->v4_realm, HDB_F_GET_SERVER, &server_entry);
+ if (ret) {
+ kdc_log(context, config, 0, "Server not found in database: %s: %s",
+ server_name, krb5_get_err_text(context, ret));
+ make_error_reply (hdr, KANOENT, reply);
+ goto out;
+ }
+
+ ret = _kdc_db_fetch4 (context, config, "krbtgt",
+ config->v4_realm, config->v4_realm, HDB_F_GET_KRBTGT, &krbtgt_entry);
+ if (ret) {
+ kdc_log(context, config, 0,
+ "Server not found in database: %s.%s@%s: %s",
+ "krbtgt", config->v4_realm, config->v4_realm,
+ krb5_get_err_text(context, ret));
+ make_error_reply (hdr, KANOENT, reply);
+ goto out;
+ }
+
+ /* find a DES key */
+ ret = _kdc_get_des_key(context, krbtgt_entry, TRUE, TRUE, &kkey);
+ if(ret){
+ kdc_log(context, config, 0, "no suitable DES key for krbtgt");
+ make_error_reply (hdr, KANOKEYS, reply);
+ goto out;
+ }
+
+ /* find a DES key */
+ ret = _kdc_get_des_key(context, server_entry, TRUE, TRUE, &skey);
+ if(ret){
+ kdc_log(context, config, 0, "no suitable DES key for server");
+ make_error_reply (hdr, KANOKEYS, reply);
+ goto out;
+ }
+
+ /* decrypt the incoming ticket */
+ memcpy (&key, kkey->key.keyvalue.data, sizeof(key));
+
+ /* unpack the ticket */
+ {
+ char *sname = NULL;
+ char *sinstance = NULL;
+
+ ret = _krb5_krb_decomp_ticket(context, &aticket, &kkey->key,
+ config->v4_realm, &sname,
+ &sinstance, &ad);
+ if (ret) {
+ kdc_log(context, config, 0,
+ "kaserver: decomp failed for %s.%s with %d",
+ sname, sinstance, ret);
+ make_error_reply (hdr, KABADTICKET, reply);
+ goto out;
+ }
+
+ if (strcmp (sname, "krbtgt") != 0
+ || strcmp (sinstance, config->v4_realm) != 0) {
+ kdc_log(context, config, 0, "no TGT: %s.%s for %s.%s@%s",
+ sname, sinstance,
+ ad.pname, ad.pinst, ad.prealm);
+ make_error_reply (hdr, KABADTICKET, reply);
+ free(sname);
+ free(sinstance);
+ goto out;
+ }
+ free(sname);
+ free(sinstance);
+
+ if (kdc_time > _krb5_krb_life_to_time(ad.time_sec, ad.life)) {
+ kdc_log(context, config, 0, "TGT expired: %s.%s@%s",
+ ad.pname, ad.pinst, ad.prealm);
+ make_error_reply (hdr, KABADTICKET, reply);
+ goto out;
+ }
+ }
+
+ snprintf (client_name, sizeof(client_name),
+ "%s.%s@%s", ad.pname, ad.pinst, ad.prealm);
+
+ kdc_log(context, config, 0, "TGS-REQ (kaserver) %s from %s for %s",
+ client_name, from, server_name);
+
+ ret = _kdc_db_fetch4 (context, config,
+ ad.pname, ad.pinst, ad.prealm, HDB_F_GET_CLIENT,
+ &client_entry);
+ if(ret && ret != HDB_ERR_NOENTRY) {
+ kdc_log(context, config, 0,
+ "Client not found in database: (krb4) %s: %s",
+ client_name, krb5_get_err_text(context, ret));
+ make_error_reply (hdr, KANOENT, reply);
+ goto out;
+ }
+ if (client_entry == NULL && strcmp(ad.prealm, config->v4_realm) == 0) {
+ kdc_log(context, config, 0,
+ "Local client not found in database: (krb4) "
+ "%s", client_name);
+ make_error_reply (hdr, KANOENT, reply);
+ goto out;
+ }
+
+ ret = _kdc_check_flags (context, config,
+ client_entry, client_name,
+ server_entry, server_name,
+ FALSE);
+ if (ret) {
+ make_error_reply (hdr, KAPWEXPIRED, reply);
+ goto out;
+ }
+
+ /* decrypt the times */
+ memcpy(&session, ad.session.keyvalue.data, sizeof(session));
+ DES_set_key_unchecked (&session, &schedule);
+ DES_ecb_encrypt (times.data,
+ times.data,
+ &schedule,
+ DES_DECRYPT);
+ memset (&schedule, 0, sizeof(schedule));
+ memset (&session, 0, sizeof(session));
+
+ /* and extract them */
+ {
+ krb5_storage *tsp;
+ int32_t tmp;
+
+ tsp = krb5_storage_from_mem (times.data, times.length);
+ krb5_ret_int32 (tsp, &tmp);
+ start_time = tmp;
+ krb5_ret_int32 (tsp, &tmp);
+ end_time = tmp;
+ krb5_storage_free (tsp);
+ }
+
+ /* life */
+ max_life = end_time - kdc_time;
+ /* end_time - kdc_time can sometimes be non-positive due to slight
+ time skew between client and server. Let's make sure it is postive */
+ if(max_life < 1)
+ max_life = 1;
+ if (krbtgt_entry->entry.max_life)
+ max_life = min(max_life, *krbtgt_entry->entry.max_life);
+ if (server_entry->entry.max_life)
+ max_life = min(max_life, *server_entry->entry.max_life);
+ /* if this is a cross realm request, the client_entry will likely
+ be NULL */
+ if (client_entry && client_entry->entry.max_life)
+ max_life = min(max_life, *client_entry->entry.max_life);
+
+ life = _krb5_krb_time_to_life(kdc_time, kdc_time + max_life);
+
+ create_reply_ticket (context,
+ hdr, skey,
+ ad.pname, ad.pinst, ad.prealm,
+ addr, life, server_entry->entry.kvno,
+ max_seq_len,
+ name, instance,
+ 0, "gtkt",
+ &ad.session, reply);
+
+ out:
+ _krb5_krb_free_auth_data(context, &ad);
+ if (aticket.length) {
+ memset (aticket.data, 0, aticket.length);
+ krb5_data_free (&aticket);
+ }
+ if (times.length) {
+ memset (times.data, 0, times.length);
+ krb5_data_free (&times);
+ }
+ if (auth_domain)
+ free (auth_domain);
+ if (name)
+ free (name);
+ if (instance)
+ free (instance);
+ if (krbtgt_entry)
+ _kdc_free_ent (context, krbtgt_entry);
+ if (server_entry)
+ _kdc_free_ent (context, server_entry);
+}
+
+krb5_error_code
+_kdc_do_kaserver(krb5_context context,
+ krb5_kdc_configuration *config,
+ unsigned char *buf,
+ size_t len,
+ krb5_data *reply,
+ const char *from,
+ struct sockaddr_in *addr)
+{
+ krb5_error_code ret = 0;
+ struct rx_header hdr;
+ uint32_t op;
+ krb5_storage *sp;
+
+ if (len < RX_HEADER_SIZE)
+ return -1;
+ sp = krb5_storage_from_mem (buf, len);
+
+ ret = decode_rx_header (sp, &hdr);
+ if (ret)
+ goto out;
+ buf += RX_HEADER_SIZE;
+ len -= RX_HEADER_SIZE;
+
+ switch (hdr.type) {
+ case HT_DATA :
+ break;
+ case HT_ACK :
+ case HT_BUSY :
+ case HT_ABORT :
+ case HT_ACKALL :
+ case HT_CHAL :
+ case HT_RESP :
+ case HT_DEBUG :
+ default:
+ /* drop */
+ goto out;
+ }
+
+
+ if (hdr.serviceid != KA_AUTHENTICATION_SERVICE
+ && hdr.serviceid != KA_TICKET_GRANTING_SERVICE) {
+ ret = -1;
+ goto out;
+ }
+
+ ret = krb5_ret_uint32(sp, &op);
+ if (ret)
+ goto out;
+ switch (op) {
+ case AUTHENTICATE :
+ case AUTHENTICATE_V2 :
+ do_authenticate (context, config, &hdr, sp, addr, from, reply);
+ break;
+ case GETTICKET :
+ do_getticket (context, config, &hdr, sp, addr, from, reply);
+ break;
+ case AUTHENTICATE_OLD :
+ case CHANGEPASSWORD :
+ case GETTICKET_OLD :
+ case SETPASSWORD :
+ case SETFIELDS :
+ case CREATEUSER :
+ case DELETEUSER :
+ case GETENTRY :
+ case LISTENTRY :
+ case GETSTATS :
+ case DEBUG :
+ case GETPASSWORD :
+ case GETRANDOMKEY :
+ default :
+ make_error_reply (&hdr, RXGEN_OPCODE, reply);
+ break;
+ }
+
+out:
+ krb5_storage_free (sp);
+ return ret;
+}
diff --git a/source4/heimdal/kdc/kdc.h b/source4/heimdal/kdc/kdc.h
new file mode 100644
index 0000000000..843bd5fa56
--- /dev/null
+++ b/source4/heimdal/kdc/kdc.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 1997-2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ *
+ * Copyright (c) 2005 Andrew Bartlett <abartlet@samba.org>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id$
+ */
+
+#ifndef __KDC_H__
+#define __KDC_H__
+
+#include <krb5.h>
+
+enum krb5_kdc_trpolicy {
+ TRPOLICY_ALWAYS_CHECK,
+ TRPOLICY_ALLOW_PER_PRINCIPAL,
+ TRPOLICY_ALWAYS_HONOUR_REQUEST
+};
+
+typedef struct krb5_kdc_configuration {
+ krb5_boolean require_preauth; /* require preauth for all principals */
+ time_t kdc_warn_pwexpire; /* time before expiration to print a warning */
+
+ struct HDB **db;
+ int num_db;
+
+ krb5_boolean encode_as_rep_as_tgs_rep; /* bug compatibility */
+
+ krb5_boolean check_ticket_addresses;
+ krb5_boolean allow_null_ticket_addresses;
+ krb5_boolean allow_anonymous;
+ enum krb5_kdc_trpolicy trpolicy;
+
+ char *v4_realm;
+ krb5_boolean enable_v4;
+ krb5_boolean enable_v4_cross_realm;
+ krb5_boolean enable_v4_per_principal;
+
+ krb5_boolean enable_kaserver;
+
+ krb5_boolean enable_524;
+
+ krb5_boolean enable_pkinit;
+ krb5_boolean pkinit_princ_in_cert;
+ char *pkinit_kdc_ocsp_file;
+ int pkinit_dh_min_bits;
+ int pkinit_require_binding;
+
+ krb5_log_facility *logf;
+
+ int enable_digest;
+ int digests_allowed;
+
+ size_t max_datagram_reply_length;
+
+ int enable_kx509;
+ const char *kx509_template;
+ const char *kx509_ca;
+
+} krb5_kdc_configuration;
+
+#include <kdc-protos.h>
+
+#endif
diff --git a/source4/heimdal/kdc/kdc_locl.h b/source4/heimdal/kdc/kdc_locl.h
new file mode 100644
index 0000000000..8e34c50049
--- /dev/null
+++ b/source4/heimdal/kdc/kdc_locl.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 1997-2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id$
+ */
+
+#ifndef __KDC_LOCL_H__
+#define __KDC_LOCL_H__
+
+#include "headers.h"
+#include "kdc.h"
+
+typedef struct pk_client_params pk_client_params;
+#include <kdc-private.h>
+
+extern sig_atomic_t exit_flag;
+extern size_t max_request;
+extern const char *request_log;
+extern const char *port_str;
+extern krb5_addresses explicit_addresses;
+
+extern int enable_http;
+
+#define DETACH_IS_DEFAULT FALSE
+
+extern int detach_from_console;
+
+extern const struct units _kdc_digestunits[];
+
+#define KDC_LOG_FILE "kdc.log"
+
+extern struct timeval _kdc_now;
+#define kdc_time (_kdc_now.tv_sec)
+
+void
+loop(krb5_context context, krb5_kdc_configuration *config);
+
+krb5_kdc_configuration *
+configure(krb5_context context, int argc, char **argv);
+
+#endif /* __KDC_LOCL_H__ */
diff --git a/source4/heimdal/kdc/kerberos4.c b/source4/heimdal/kdc/kerberos4.c
new file mode 100644
index 0000000000..2bd2383940
--- /dev/null
+++ b/source4/heimdal/kdc/kerberos4.c
@@ -0,0 +1,794 @@
+/*
+ * Copyright (c) 1997 - 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kdc_locl.h"
+
+#include <krb5-v4compat.h>
+
+RCSID("$Id$");
+
+#ifndef swap32
+static uint32_t
+swap32(uint32_t x)
+{
+ return ((x << 24) & 0xff000000) |
+ ((x << 8) & 0xff0000) |
+ ((x >> 8) & 0xff00) |
+ ((x >> 24) & 0xff);
+}
+#endif /* swap32 */
+
+int
+_kdc_maybe_version4(unsigned char *buf, int len)
+{
+ return len > 0 && *buf == 4;
+}
+
+static void
+make_err_reply(krb5_context context, krb5_data *reply,
+ int code, const char *msg)
+{
+ _krb5_krb_cr_err_reply(context, "", "", "",
+ kdc_time, code, msg, reply);
+}
+
+struct valid_princ_ctx {
+ krb5_kdc_configuration *config;
+ unsigned flags;
+};
+
+static krb5_boolean
+valid_princ(krb5_context context,
+ void *funcctx,
+ krb5_principal princ)
+{
+ struct valid_princ_ctx *ctx = funcctx;
+ krb5_error_code ret;
+ char *s;
+ hdb_entry_ex *ent;
+
+ ret = krb5_unparse_name(context, princ, &s);
+ if (ret)
+ return FALSE;
+ ret = _kdc_db_fetch(context, ctx->config, princ, ctx->flags, NULL, &ent);
+ if (ret) {
+ kdc_log(context, ctx->config, 7, "Lookup %s failed: %s", s,
+ krb5_get_err_text (context, ret));
+ free(s);
+ return FALSE;
+ }
+ kdc_log(context, ctx->config, 7, "Lookup %s succeeded", s);
+ free(s);
+ _kdc_free_ent(context, ent);
+ return TRUE;
+}
+
+krb5_error_code
+_kdc_db_fetch4(krb5_context context,
+ krb5_kdc_configuration *config,
+ const char *name, const char *instance, const char *realm,
+ unsigned flags,
+ hdb_entry_ex **ent)
+{
+ krb5_principal p;
+ krb5_error_code ret;
+ struct valid_princ_ctx ctx;
+
+ ctx.config = config;
+ ctx.flags = flags;
+
+ ret = krb5_425_conv_principal_ext2(context, name, instance, realm,
+ valid_princ, &ctx, 0, &p);
+ if(ret)
+ return ret;
+ ret = _kdc_db_fetch(context, config, p, flags, NULL, ent);
+ krb5_free_principal(context, p);
+ return ret;
+}
+
+#define RCHECK(X, L) if(X){make_err_reply(context, reply, KFAILURE, "Packet too short"); goto L;}
+
+/*
+ * Process the v4 request in `buf, len' (received from `addr'
+ * (with string `from').
+ * Return an error code and a reply in `reply'.
+ */
+
+krb5_error_code
+_kdc_do_version4(krb5_context context,
+ krb5_kdc_configuration *config,
+ unsigned char *buf,
+ size_t len,
+ krb5_data *reply,
+ const char *from,
+ struct sockaddr_in *addr)
+{
+ krb5_storage *sp;
+ krb5_error_code ret = EINVAL;
+ hdb_entry_ex *client = NULL, *server = NULL;
+ Key *ckey, *skey;
+ int8_t pvno;
+ int8_t msg_type;
+ int lsb;
+ char *name = NULL, *inst = NULL, *realm = NULL;
+ char *sname = NULL, *sinst = NULL;
+ int32_t req_time;
+ time_t max_life;
+ uint8_t life;
+ char client_name[256];
+ char server_name[256];
+
+ if(!config->enable_v4) {
+ kdc_log(context, config, 0,
+ "Rejected version 4 request from %s", from);
+ make_err_reply(context, reply, KRB4ET_KDC_GEN_ERR,
+ "Function not enabled");
+ return 0;
+ }
+
+ sp = krb5_storage_from_mem(buf, len);
+ RCHECK(krb5_ret_int8(sp, &pvno), out);
+ if(pvno != 4){
+ kdc_log(context, config, 0,
+ "Protocol version mismatch (krb4) (%d)", pvno);
+ make_err_reply(context, reply, KRB4ET_KDC_PKT_VER, "protocol mismatch");
+ ret = KRB4ET_KDC_PKT_VER;
+ goto out;
+ }
+ RCHECK(krb5_ret_int8(sp, &msg_type), out);
+ lsb = msg_type & 1;
+ msg_type &= ~1;
+ switch(msg_type){
+ case AUTH_MSG_KDC_REQUEST: {
+ krb5_data ticket, cipher;
+ krb5_keyblock session;
+
+ krb5_data_zero(&ticket);
+ krb5_data_zero(&cipher);
+
+ RCHECK(krb5_ret_stringz(sp, &name), out1);
+ RCHECK(krb5_ret_stringz(sp, &inst), out1);
+ RCHECK(krb5_ret_stringz(sp, &realm), out1);
+ RCHECK(krb5_ret_int32(sp, &req_time), out1);
+ if(lsb)
+ req_time = swap32(req_time);
+ RCHECK(krb5_ret_uint8(sp, &life), out1);
+ RCHECK(krb5_ret_stringz(sp, &sname), out1);
+ RCHECK(krb5_ret_stringz(sp, &sinst), out1);
+ snprintf (client_name, sizeof(client_name),
+ "%s.%s@%s", name, inst, realm);
+ snprintf (server_name, sizeof(server_name),
+ "%s.%s@%s", sname, sinst, config->v4_realm);
+
+ kdc_log(context, config, 0, "AS-REQ (krb4) %s from %s for %s",
+ client_name, from, server_name);
+
+ ret = _kdc_db_fetch4(context, config, name, inst, realm,
+ HDB_F_GET_CLIENT, &client);
+ if(ret) {
+ kdc_log(context, config, 0, "Client not found in database: %s: %s",
+ client_name, krb5_get_err_text(context, ret));
+ make_err_reply(context, reply, KRB4ET_KDC_PR_UNKNOWN,
+ "principal unknown");
+ goto out1;
+ }
+ ret = _kdc_db_fetch4(context, config, sname, sinst, config->v4_realm,
+ HDB_F_GET_SERVER, &server);
+ if(ret){
+ kdc_log(context, config, 0, "Server not found in database: %s: %s",
+ server_name, krb5_get_err_text(context, ret));
+ make_err_reply(context, reply, KRB4ET_KDC_PR_UNKNOWN,
+ "principal unknown");
+ goto out1;
+ }
+
+ ret = _kdc_check_flags (context, config,
+ client, client_name,
+ server, server_name,
+ TRUE);
+ if (ret) {
+ /* good error code? */
+ make_err_reply(context, reply, KRB4ET_KDC_NAME_EXP,
+ "operation not allowed");
+ goto out1;
+ }
+
+ if (config->enable_v4_per_principal &&
+ client->entry.flags.allow_kerberos4 == 0)
+ {
+ kdc_log(context, config, 0,
+ "Per principal Kerberos 4 flag not turned on for %s",
+ client_name);
+ make_err_reply(context, reply, KRB4ET_KDC_NULL_KEY,
+ "allow kerberos4 flag required");
+ goto out1;
+ }
+
+ /*
+ * There's no way to do pre-authentication in v4 and thus no
+ * good error code to return if preauthentication is required.
+ */
+
+ if (config->require_preauth
+ || client->entry.flags.require_preauth
+ || server->entry.flags.require_preauth) {
+ kdc_log(context, config, 0,
+ "Pre-authentication required for v4-request: "
+ "%s for %s",
+ client_name, server_name);
+ make_err_reply(context, reply, KRB4ET_KDC_NULL_KEY,
+ "preauth required");
+ goto out1;
+ }
+
+ ret = _kdc_get_des_key(context, client, FALSE, FALSE, &ckey);
+ if(ret){
+ kdc_log(context, config, 0, "no suitable DES key for client");
+ make_err_reply(context, reply, KRB4ET_KDC_NULL_KEY,
+ "no suitable DES key for client");
+ goto out1;
+ }
+
+ ret = _kdc_get_des_key(context, server, TRUE, FALSE, &skey);
+ if(ret){
+ kdc_log(context, config, 0, "no suitable DES key for server");
+ make_err_reply(context, reply, KRB4ET_KDC_NULL_KEY,
+ "no suitable DES key for server");
+ goto out1;
+ }
+
+ max_life = _krb5_krb_life_to_time(0, life);
+ if(client->entry.max_life)
+ max_life = min(max_life, *client->entry.max_life);
+ if(server->entry.max_life)
+ max_life = min(max_life, *server->entry.max_life);
+
+ life = krb_time_to_life(kdc_time, kdc_time + max_life);
+
+ ret = krb5_generate_random_keyblock(context,
+ ETYPE_DES_PCBC_NONE,
+ &session);
+ if (ret) {
+ make_err_reply(context, reply, KFAILURE,
+ "Not enough random i KDC");
+ goto out1;
+ }
+
+ ret = _krb5_krb_create_ticket(context,
+ 0,
+ name,
+ inst,
+ config->v4_realm,
+ addr->sin_addr.s_addr,
+ &session,
+ life,
+ kdc_time,
+ sname,
+ sinst,
+ &skey->key,
+ &ticket);
+ if (ret) {
+ krb5_free_keyblock_contents(context, &session);
+ make_err_reply(context, reply, KFAILURE,
+ "failed to create v4 ticket");
+ goto out1;
+ }
+
+ ret = _krb5_krb_create_ciph(context,
+ &session,
+ sname,
+ sinst,
+ config->v4_realm,
+ life,
+ server->entry.kvno % 255,
+ &ticket,
+ kdc_time,
+ &ckey->key,
+ &cipher);
+ krb5_free_keyblock_contents(context, &session);
+ krb5_data_free(&ticket);
+ if (ret) {
+ make_err_reply(context, reply, KFAILURE,
+ "Failed to create v4 cipher");
+ goto out1;
+ }
+
+ ret = _krb5_krb_create_auth_reply(context,
+ name,
+ inst,
+ realm,
+ req_time,
+ 0,
+ client->entry.pw_end ? *client->entry.pw_end : 0,
+ client->entry.kvno % 256,
+ &cipher,
+ reply);
+ krb5_data_free(&cipher);
+
+ out1:
+ break;
+ }
+ case AUTH_MSG_APPL_REQUEST: {
+ struct _krb5_krb_auth_data ad;
+ int8_t kvno;
+ int8_t ticket_len;
+ int8_t req_len;
+ krb5_data auth;
+ int32_t address;
+ size_t pos;
+ krb5_principal tgt_princ = NULL;
+ hdb_entry_ex *tgt = NULL;
+ Key *tkey;
+ time_t max_end, actual_end, issue_time;
+
+ memset(&ad, 0, sizeof(ad));
+ krb5_data_zero(&auth);
+
+ RCHECK(krb5_ret_int8(sp, &kvno), out2);
+ RCHECK(krb5_ret_stringz(sp, &realm), out2);
+
+ ret = krb5_425_conv_principal(context, "krbtgt", realm,
+ config->v4_realm,
+ &tgt_princ);
+ if(ret){
+ kdc_log(context, config, 0,
+ "Converting krbtgt principal (krb4): %s",
+ krb5_get_err_text(context, ret));
+ make_err_reply(context, reply, KFAILURE,
+ "Failed to convert v4 principal (krbtgt)");
+ goto out2;
+ }
+
+ ret = _kdc_db_fetch(context, config, tgt_princ,
+ HDB_F_GET_KRBTGT, NULL, &tgt);
+ if(ret){
+ char *s;
+ s = kdc_log_msg(context, config, 0, "Ticket-granting ticket not "
+ "found in database (krb4): krbtgt.%s@%s: %s",
+ realm, config->v4_realm,
+ krb5_get_err_text(context, ret));
+ make_err_reply(context, reply, KFAILURE, s);
+ free(s);
+ goto out2;
+ }
+
+ if(tgt->entry.kvno % 256 != kvno){
+ kdc_log(context, config, 0,
+ "tgs-req (krb4) with old kvno %d (current %d) for "
+ "krbtgt.%s@%s", kvno, tgt->entry.kvno % 256,
+ realm, config->v4_realm);
+ make_err_reply(context, reply, KRB4ET_KDC_AUTH_EXP,
+ "old krbtgt kvno used");
+ goto out2;
+ }
+
+ ret = _kdc_get_des_key(context, tgt, TRUE, FALSE, &tkey);
+ if(ret){
+ kdc_log(context, config, 0,
+ "no suitable DES key for krbtgt (krb4)");
+ make_err_reply(context, reply, KRB4ET_KDC_NULL_KEY,
+ "no suitable DES key for krbtgt");
+ goto out2;
+ }
+
+ RCHECK(krb5_ret_int8(sp, &ticket_len), out2);
+ RCHECK(krb5_ret_int8(sp, &req_len), out2);
+
+ pos = krb5_storage_seek(sp, ticket_len + req_len, SEEK_CUR);
+
+ auth.data = buf;
+ auth.length = pos;
+
+ if (config->check_ticket_addresses)
+ address = addr->sin_addr.s_addr;
+ else
+ address = 0;
+
+ ret = _krb5_krb_rd_req(context, &auth, "krbtgt", realm,
+ config->v4_realm,
+ address, &tkey->key, &ad);
+ if(ret){
+ kdc_log(context, config, 0, "krb_rd_req: %d", ret);
+ make_err_reply(context, reply, ret, "failed to parse request");
+ goto out2;
+ }
+
+ RCHECK(krb5_ret_int32(sp, &req_time), out2);
+ if(lsb)
+ req_time = swap32(req_time);
+ RCHECK(krb5_ret_uint8(sp, &life), out2);
+ RCHECK(krb5_ret_stringz(sp, &sname), out2);
+ RCHECK(krb5_ret_stringz(sp, &sinst), out2);
+ snprintf (server_name, sizeof(server_name),
+ "%s.%s@%s",
+ sname, sinst, config->v4_realm);
+ snprintf (client_name, sizeof(client_name),
+ "%s.%s@%s",
+ ad.pname, ad.pinst, ad.prealm);
+
+ kdc_log(context, config, 0, "TGS-REQ (krb4) %s from %s for %s",
+ client_name, from, server_name);
+
+ if(strcmp(ad.prealm, realm)){
+ kdc_log(context, config, 0,
+ "Can't hop realms (krb4) %s -> %s", realm, ad.prealm);
+ make_err_reply(context, reply, KRB4ET_KDC_PR_UNKNOWN,
+ "Can't hop realms");
+ goto out2;
+ }
+
+ if (!config->enable_v4_cross_realm && strcmp(realm, config->v4_realm) != 0) {
+ kdc_log(context, config, 0,
+ "krb4 Cross-realm %s -> %s disabled",
+ realm, config->v4_realm);
+ make_err_reply(context, reply, KRB4ET_KDC_PR_UNKNOWN,
+ "Can't hop realms");
+ goto out2;
+ }
+
+ if(strcmp(sname, "changepw") == 0){
+ kdc_log(context, config, 0,
+ "Bad request for changepw ticket (krb4)");
+ make_err_reply(context, reply, KRB4ET_KDC_PR_UNKNOWN,
+ "Can't authorize password change based on TGT");
+ goto out2;
+ }
+
+ ret = _kdc_db_fetch4(context, config, ad.pname, ad.pinst, ad.prealm,
+ HDB_F_GET_CLIENT, &client);
+ if(ret && ret != HDB_ERR_NOENTRY) {
+ char *s;
+ s = kdc_log_msg(context, config, 0,
+ "Client not found in database: (krb4) %s: %s",
+ client_name, krb5_get_err_text(context, ret));
+ make_err_reply(context, reply, KRB4ET_KDC_PR_UNKNOWN, s);
+ free(s);
+ goto out2;
+ }
+ if (client == NULL && strcmp(ad.prealm, config->v4_realm) == 0) {
+ char *s;
+ s = kdc_log_msg(context, config, 0,
+ "Local client not found in database: (krb4) "
+ "%s", client_name);
+ make_err_reply(context, reply, KRB4ET_KDC_PR_UNKNOWN, s);
+ free(s);
+ goto out2;
+ }
+
+ ret = _kdc_db_fetch4(context, config, sname, sinst, config->v4_realm,
+ HDB_F_GET_SERVER, &server);
+ if(ret){
+ char *s;
+ s = kdc_log_msg(context, config, 0,
+ "Server not found in database (krb4): %s: %s",
+ server_name, krb5_get_err_text(context, ret));
+ make_err_reply(context, reply, KRB4ET_KDC_PR_UNKNOWN, s);
+ free(s);
+ goto out2;
+ }
+
+ ret = _kdc_check_flags (context, config,
+ client, client_name,
+ server, server_name,
+ FALSE);
+ if (ret) {
+ make_err_reply(context, reply, KRB4ET_KDC_NAME_EXP,
+ "operation not allowed");
+ goto out2;
+ }
+
+ ret = _kdc_get_des_key(context, server, TRUE, FALSE, &skey);
+ if(ret){
+ kdc_log(context, config, 0,
+ "no suitable DES key for server (krb4)");
+ make_err_reply(context, reply, KRB4ET_KDC_NULL_KEY,
+ "no suitable DES key for server");
+ goto out2;
+ }
+
+ max_end = _krb5_krb_life_to_time(ad.time_sec, ad.life);
+ max_end = min(max_end, _krb5_krb_life_to_time(kdc_time, life));
+ if(server->entry.max_life)
+ max_end = min(max_end, kdc_time + *server->entry.max_life);
+ if(client && client->entry.max_life)
+ max_end = min(max_end, kdc_time + *client->entry.max_life);
+ life = min(life, krb_time_to_life(kdc_time, max_end));
+
+ issue_time = kdc_time;
+ actual_end = _krb5_krb_life_to_time(issue_time, life);
+ while (actual_end > max_end && life > 1) {
+ /* move them into the next earlier lifetime bracket */
+ life--;
+ actual_end = _krb5_krb_life_to_time(issue_time, life);
+ }
+ if (actual_end > max_end) {
+ /* if life <= 1 and it's still too long, backdate the ticket */
+ issue_time -= actual_end - max_end;
+ }
+
+ {
+ krb5_data ticket, cipher;
+ krb5_keyblock session;
+
+ krb5_data_zero(&ticket);
+ krb5_data_zero(&cipher);
+
+ ret = krb5_generate_random_keyblock(context,
+ ETYPE_DES_PCBC_NONE,
+ &session);
+ if (ret) {
+ make_err_reply(context, reply, KFAILURE,
+ "Not enough random i KDC");
+ goto out2;
+ }
+
+ ret = _krb5_krb_create_ticket(context,
+ 0,
+ ad.pname,
+ ad.pinst,
+ ad.prealm,
+ addr->sin_addr.s_addr,
+ &session,
+ life,
+ issue_time,
+ sname,
+ sinst,
+ &skey->key,
+ &ticket);
+ if (ret) {
+ krb5_free_keyblock_contents(context, &session);
+ make_err_reply(context, reply, KFAILURE,
+ "failed to create v4 ticket");
+ goto out2;
+ }
+
+ ret = _krb5_krb_create_ciph(context,
+ &session,
+ sname,
+ sinst,
+ config->v4_realm,
+ life,
+ server->entry.kvno % 255,
+ &ticket,
+ issue_time,
+ &ad.session,
+ &cipher);
+ krb5_free_keyblock_contents(context, &session);
+ if (ret) {
+ make_err_reply(context, reply, KFAILURE,
+ "failed to create v4 cipher");
+ goto out2;
+ }
+
+ ret = _krb5_krb_create_auth_reply(context,
+ ad.pname,
+ ad.pinst,
+ ad.prealm,
+ req_time,
+ 0,
+ 0,
+ 0,
+ &cipher,
+ reply);
+ krb5_data_free(&cipher);
+ }
+ out2:
+ _krb5_krb_free_auth_data(context, &ad);
+ if(tgt_princ)
+ krb5_free_principal(context, tgt_princ);
+ if(tgt)
+ _kdc_free_ent(context, tgt);
+ break;
+ }
+ case AUTH_MSG_ERR_REPLY:
+ ret = EINVAL;
+ break;
+ default:
+ kdc_log(context, config, 0, "Unknown message type (krb4): %d from %s",
+ msg_type, from);
+
+ make_err_reply(context, reply, KFAILURE, "Unknown message type");
+ ret = EINVAL;
+ }
+ out:
+ if(name)
+ free(name);
+ if(inst)
+ free(inst);
+ if(realm)
+ free(realm);
+ if(sname)
+ free(sname);
+ if(sinst)
+ free(sinst);
+ if(client)
+ _kdc_free_ent(context, client);
+ if(server)
+ _kdc_free_ent(context, server);
+ krb5_storage_free(sp);
+ return ret;
+}
+
+krb5_error_code
+_kdc_encode_v4_ticket(krb5_context context,
+ krb5_kdc_configuration *config,
+ void *buf, size_t len, const EncTicketPart *et,
+ const PrincipalName *service, size_t *size)
+{
+ krb5_storage *sp;
+ krb5_error_code ret;
+ char name[40], inst[40], realm[40];
+ char sname[40], sinst[40];
+
+ {
+ krb5_principal princ;
+ _krb5_principalname2krb5_principal(context,
+ &princ,
+ *service,
+ et->crealm);
+ ret = krb5_524_conv_principal(context,
+ princ,
+ sname,
+ sinst,
+ realm);
+ krb5_free_principal(context, princ);
+ if(ret)
+ return ret;
+
+ _krb5_principalname2krb5_principal(context,
+ &princ,
+ et->cname,
+ et->crealm);
+
+ ret = krb5_524_conv_principal(context,
+ princ,
+ name,
+ inst,
+ realm);
+ krb5_free_principal(context, princ);
+ }
+ if(ret)
+ return ret;
+
+ sp = krb5_storage_emem();
+
+ krb5_store_int8(sp, 0); /* flags */
+ krb5_store_stringz(sp, name);
+ krb5_store_stringz(sp, inst);
+ krb5_store_stringz(sp, realm);
+ {
+ unsigned char tmp[4] = { 0, 0, 0, 0 };
+ int i;
+ if(et->caddr){
+ for(i = 0; i < et->caddr->len; i++)
+ if(et->caddr->val[i].addr_type == AF_INET &&
+ et->caddr->val[i].address.length == 4){
+ memcpy(tmp, et->caddr->val[i].address.data, 4);
+ break;
+ }
+ }
+ krb5_storage_write(sp, tmp, sizeof(tmp));
+ }
+
+ if((et->key.keytype != ETYPE_DES_CBC_MD5 &&
+ et->key.keytype != ETYPE_DES_CBC_MD4 &&
+ et->key.keytype != ETYPE_DES_CBC_CRC) ||
+ et->key.keyvalue.length != 8)
+ return -1;
+ krb5_storage_write(sp, et->key.keyvalue.data, 8);
+
+ {
+ time_t start = et->starttime ? *et->starttime : et->authtime;
+ krb5_store_int8(sp, krb_time_to_life(start, et->endtime));
+ krb5_store_int32(sp, start);
+ }
+
+ krb5_store_stringz(sp, sname);
+ krb5_store_stringz(sp, sinst);
+
+ {
+ krb5_data data;
+ krb5_storage_to_data(sp, &data);
+ krb5_storage_free(sp);
+ *size = (data.length + 7) & ~7; /* pad to 8 bytes */
+ if(*size > len)
+ return -1;
+ memset((unsigned char*)buf - *size + 1, 0, *size);
+ memcpy((unsigned char*)buf - *size + 1, data.data, data.length);
+ krb5_data_free(&data);
+ }
+ return 0;
+}
+
+krb5_error_code
+_kdc_get_des_key(krb5_context context,
+ hdb_entry_ex *principal, krb5_boolean is_server,
+ krb5_boolean prefer_afs_key, Key **ret_key)
+{
+ Key *v5_key = NULL, *v4_key = NULL, *afs_key = NULL, *server_key = NULL;
+ int i;
+ krb5_enctype etypes[] = { ETYPE_DES_CBC_MD5,
+ ETYPE_DES_CBC_MD4,
+ ETYPE_DES_CBC_CRC };
+
+ for(i = 0;
+ i < sizeof(etypes)/sizeof(etypes[0])
+ && (v5_key == NULL || v4_key == NULL ||
+ afs_key == NULL || server_key == NULL);
+ ++i) {
+ Key *key = NULL;
+ while(hdb_next_enctype2key(context, &principal->entry, etypes[i], &key) == 0) {
+ if(key->salt == NULL) {
+ if(v5_key == NULL)
+ v5_key = key;
+ } else if(key->salt->type == hdb_pw_salt &&
+ key->salt->salt.length == 0) {
+ if(v4_key == NULL)
+ v4_key = key;
+ } else if(key->salt->type == hdb_afs3_salt) {
+ if(afs_key == NULL)
+ afs_key = key;
+ } else if(server_key == NULL)
+ server_key = key;
+ }
+ }
+
+ if(prefer_afs_key) {
+ if(afs_key)
+ *ret_key = afs_key;
+ else if(v4_key)
+ *ret_key = v4_key;
+ else if(v5_key)
+ *ret_key = v5_key;
+ else if(is_server && server_key)
+ *ret_key = server_key;
+ else
+ return KRB4ET_KDC_NULL_KEY;
+ } else {
+ if(v4_key)
+ *ret_key = v4_key;
+ else if(afs_key)
+ *ret_key = afs_key;
+ else if(v5_key)
+ *ret_key = v5_key;
+ else if(is_server && server_key)
+ *ret_key = server_key;
+ else
+ return KRB4ET_KDC_NULL_KEY;
+ }
+
+ if((*ret_key)->key.keyvalue.length == 0)
+ return KRB4ET_KDC_NULL_KEY;
+ return 0;
+}
+
diff --git a/source4/heimdal/kdc/kerberos5.c b/source4/heimdal/kdc/kerberos5.c
new file mode 100644
index 0000000000..c715e0812f
--- /dev/null
+++ b/source4/heimdal/kdc/kerberos5.c
@@ -0,0 +1,1871 @@
+/*
+ * Copyright (c) 1997-2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kdc_locl.h"
+
+RCSID("$Id$");
+
+#define MAX_TIME ((time_t)((1U << 31) - 1))
+
+void
+_kdc_fix_time(time_t **t)
+{
+ if(*t == NULL){
+ ALLOC(*t);
+ **t = MAX_TIME;
+ }
+ if(**t == 0) **t = MAX_TIME; /* fix for old clients */
+}
+
+static int
+realloc_method_data(METHOD_DATA *md)
+{
+ PA_DATA *pa;
+ pa = realloc(md->val, (md->len + 1) * sizeof(*md->val));
+ if(pa == NULL)
+ return ENOMEM;
+ md->val = pa;
+ md->len++;
+ return 0;
+}
+
+static void
+set_salt_padata (METHOD_DATA *md, Salt *salt)
+{
+ if (salt) {
+ realloc_method_data(md);
+ md->val[md->len - 1].padata_type = salt->type;
+ der_copy_octet_string(&salt->salt,
+ &md->val[md->len - 1].padata_value);
+ }
+}
+
+const PA_DATA*
+_kdc_find_padata(const KDC_REQ *req, int *start, int type)
+{
+ if (req->padata == NULL)
+ return NULL;
+
+ while(*start < req->padata->len){
+ (*start)++;
+ if(req->padata->val[*start - 1].padata_type == type)
+ return &req->padata->val[*start - 1];
+ }
+ return NULL;
+}
+
+/*
+ * This is a hack to allow predefined weak services, like afs to
+ * still use weak types
+ */
+
+krb5_boolean
+_kdc_is_weak_expection(krb5_principal principal, krb5_enctype etype)
+{
+ if (principal->name.name_string.len > 0 &&
+ strcmp(principal->name.name_string.val[0], "afs") == 0 &&
+ (etype == ETYPE_DES_CBC_CRC
+ || etype == ETYPE_DES_CBC_MD4
+ || etype == ETYPE_DES_CBC_MD5))
+ return TRUE;
+ return FALSE;
+}
+
+
+/*
+ * Detect if `key' is the using the the precomputed `default_salt'.
+ */
+
+static krb5_boolean
+is_default_salt_p(const krb5_salt *default_salt, const Key *key)
+{
+ if (key->salt == NULL)
+ return TRUE;
+ if (default_salt->salttype != key->salt->type)
+ return FALSE;
+ if (krb5_data_cmp(&default_salt->saltvalue, &key->salt->salt))
+ return FALSE;
+ return TRUE;
+}
+
+/*
+ * return the first appropriate key of `princ' in `ret_key'. Look for
+ * all the etypes in (`etypes', `len'), stopping as soon as we find
+ * one, but preferring one that has default salt
+ */
+
+krb5_error_code
+_kdc_find_etype(krb5_context context, const hdb_entry_ex *princ,
+ krb5_enctype *etypes, unsigned len,
+ Key **ret_key, krb5_enctype *ret_etype)
+{
+ int i;
+ krb5_error_code ret = KRB5KDC_ERR_ETYPE_NOSUPP;
+ krb5_salt def_salt;
+
+ krb5_get_pw_salt (context, princ->entry.principal, &def_salt);
+
+ for(i = 0; ret != 0 && i < len ; i++) {
+ Key *key = NULL;
+
+ if (krb5_enctype_valid(context, etypes[i]) != 0 &&
+ !_kdc_is_weak_expection(princ->entry.principal, etypes[i]))
+ continue;
+
+ while (hdb_next_enctype2key(context, &princ->entry, etypes[i], &key) == 0) {
+ if (key->key.keyvalue.length == 0) {
+ ret = KRB5KDC_ERR_NULL_KEY;
+ continue;
+ }
+ *ret_key = key;
+ *ret_etype = etypes[i];
+ ret = 0;
+ if (is_default_salt_p(&def_salt, key)) {
+ krb5_free_salt (context, def_salt);
+ return ret;
+ }
+ }
+ }
+ krb5_free_salt (context, def_salt);
+ return ret;
+}
+
+krb5_error_code
+_kdc_make_anonymous_principalname (PrincipalName *pn)
+{
+ pn->name_type = KRB5_NT_PRINCIPAL;
+ pn->name_string.len = 1;
+ pn->name_string.val = malloc(sizeof(*pn->name_string.val));
+ if (pn->name_string.val == NULL)
+ return ENOMEM;
+ pn->name_string.val[0] = strdup("anonymous");
+ if (pn->name_string.val[0] == NULL) {
+ free(pn->name_string.val);
+ pn->name_string.val = NULL;
+ return ENOMEM;
+ }
+ return 0;
+}
+
+void
+_kdc_log_timestamp(krb5_context context,
+ krb5_kdc_configuration *config,
+ const char *type,
+ KerberosTime authtime, KerberosTime *starttime,
+ KerberosTime endtime, KerberosTime *renew_till)
+{
+ char authtime_str[100], starttime_str[100],
+ endtime_str[100], renewtime_str[100];
+
+ krb5_format_time(context, authtime,
+ authtime_str, sizeof(authtime_str), TRUE);
+ if (starttime)
+ krb5_format_time(context, *starttime,
+ starttime_str, sizeof(starttime_str), TRUE);
+ else
+ strlcpy(starttime_str, "unset", sizeof(starttime_str));
+ krb5_format_time(context, endtime,
+ endtime_str, sizeof(endtime_str), TRUE);
+ if (renew_till)
+ krb5_format_time(context, *renew_till,
+ renewtime_str, sizeof(renewtime_str), TRUE);
+ else
+ strlcpy(renewtime_str, "unset", sizeof(renewtime_str));
+
+ kdc_log(context, config, 5,
+ "%s authtime: %s starttime: %s endtime: %s renew till: %s",
+ type, authtime_str, starttime_str, endtime_str, renewtime_str);
+}
+
+static void
+log_patypes(krb5_context context,
+ krb5_kdc_configuration *config,
+ METHOD_DATA *padata)
+{
+ struct rk_strpool *p = NULL;
+ char *str;
+ int i;
+
+ for (i = 0; i < padata->len; i++) {
+ switch(padata->val[i].padata_type) {
+ case KRB5_PADATA_PK_AS_REQ:
+ p = rk_strpoolprintf(p, "PK-INIT(ietf)");
+ break;
+ case KRB5_PADATA_PK_AS_REQ_WIN:
+ p = rk_strpoolprintf(p, "PK-INIT(win2k)");
+ break;
+ case KRB5_PADATA_PA_PK_OCSP_RESPONSE:
+ p = rk_strpoolprintf(p, "OCSP");
+ break;
+ case KRB5_PADATA_ENC_TIMESTAMP:
+ p = rk_strpoolprintf(p, "encrypted-timestamp");
+ break;
+ default:
+ p = rk_strpoolprintf(p, "%d", padata->val[i].padata_type);
+ break;
+ }
+ if (p && i + 1 < padata->len)
+ p = rk_strpoolprintf(p, ", ");
+ if (p == NULL) {
+ kdc_log(context, config, 0, "out of memory");
+ return;
+ }
+ }
+ if (p == NULL)
+ p = rk_strpoolprintf(p, "none");
+
+ str = rk_strpoolcollect(p);
+ kdc_log(context, config, 0, "Client sent patypes: %s", str);
+ free(str);
+}
+
+/*
+ *
+ */
+
+
+krb5_error_code
+_kdc_encode_reply(krb5_context context,
+ krb5_kdc_configuration *config,
+ KDC_REP *rep, const EncTicketPart *et, EncKDCRepPart *ek,
+ krb5_enctype etype,
+ int skvno, const EncryptionKey *skey,
+ int ckvno, const EncryptionKey *ckey,
+ const char **e_text,
+ krb5_data *reply)
+{
+ unsigned char *buf;
+ size_t buf_size;
+ size_t len;
+ krb5_error_code ret;
+ krb5_crypto crypto;
+
+ ASN1_MALLOC_ENCODE(EncTicketPart, buf, buf_size, et, &len, ret);
+ if(ret) {
+ kdc_log(context, config, 0, "Failed to encode ticket: %s",
+ krb5_get_err_text(context, ret));
+ return ret;
+ }
+ if(buf_size != len) {
+ free(buf);
+ kdc_log(context, config, 0, "Internal error in ASN.1 encoder");
+ *e_text = "KDC internal error";
+ return KRB5KRB_ERR_GENERIC;
+ }
+
+ ret = krb5_crypto_init(context, skey, etype, &crypto);
+ if (ret) {
+ free(buf);
+ kdc_log(context, config, 0, "krb5_crypto_init failed: %s",
+ krb5_get_err_text(context, ret));
+ return ret;
+ }
+
+ ret = krb5_encrypt_EncryptedData(context,
+ crypto,
+ KRB5_KU_TICKET,
+ buf,
+ len,
+ skvno,
+ &rep->ticket.enc_part);
+ free(buf);
+ krb5_crypto_destroy(context, crypto);
+ if(ret) {
+ kdc_log(context, config, 0, "Failed to encrypt data: %s",
+ krb5_get_err_text(context, ret));
+ return ret;
+ }
+
+ if(rep->msg_type == krb_as_rep && !config->encode_as_rep_as_tgs_rep)
+ ASN1_MALLOC_ENCODE(EncASRepPart, buf, buf_size, ek, &len, ret);
+ else
+ ASN1_MALLOC_ENCODE(EncTGSRepPart, buf, buf_size, ek, &len, ret);
+ if(ret) {
+ kdc_log(context, config, 0, "Failed to encode KDC-REP: %s",
+ krb5_get_err_text(context, ret));
+ return ret;
+ }
+ if(buf_size != len) {
+ free(buf);
+ kdc_log(context, config, 0, "Internal error in ASN.1 encoder");
+ *e_text = "KDC internal error";
+ return KRB5KRB_ERR_GENERIC;
+ }
+ ret = krb5_crypto_init(context, ckey, 0, &crypto);
+ if (ret) {
+ free(buf);
+ kdc_log(context, config, 0, "krb5_crypto_init failed: %s",
+ krb5_get_err_text(context, ret));
+ return ret;
+ }
+ if(rep->msg_type == krb_as_rep) {
+ krb5_encrypt_EncryptedData(context,
+ crypto,
+ KRB5_KU_AS_REP_ENC_PART,
+ buf,
+ len,
+ ckvno,
+ &rep->enc_part);
+ free(buf);
+ ASN1_MALLOC_ENCODE(AS_REP, buf, buf_size, rep, &len, ret);
+ } else {
+ krb5_encrypt_EncryptedData(context,
+ crypto,
+ KRB5_KU_TGS_REP_ENC_PART_SESSION,
+ buf,
+ len,
+ ckvno,
+ &rep->enc_part);
+ free(buf);
+ ASN1_MALLOC_ENCODE(TGS_REP, buf, buf_size, rep, &len, ret);
+ }
+ krb5_crypto_destroy(context, crypto);
+ if(ret) {
+ kdc_log(context, config, 0, "Failed to encode KDC-REP: %s",
+ krb5_get_err_text(context, ret));
+ return ret;
+ }
+ if(buf_size != len) {
+ free(buf);
+ kdc_log(context, config, 0, "Internal error in ASN.1 encoder");
+ *e_text = "KDC internal error";
+ return KRB5KRB_ERR_GENERIC;
+ }
+ reply->data = buf;
+ reply->length = buf_size;
+ return 0;
+}
+
+/*
+ * Return 1 if the client have only older enctypes, this is for
+ * determining if the server should send ETYPE_INFO2 or not.
+ */
+
+static int
+older_enctype(krb5_enctype enctype)
+{
+ switch (enctype) {
+ case ETYPE_DES_CBC_CRC:
+ case ETYPE_DES_CBC_MD4:
+ case ETYPE_DES_CBC_MD5:
+ case ETYPE_DES3_CBC_SHA1:
+ case ETYPE_ARCFOUR_HMAC_MD5:
+ case ETYPE_ARCFOUR_HMAC_MD5_56:
+ /*
+ * The following three is "old" windows enctypes and is needed for
+ * windows 2000 hosts.
+ */
+ case ETYPE_ARCFOUR_MD4:
+ case ETYPE_ARCFOUR_HMAC_OLD:
+ case ETYPE_ARCFOUR_HMAC_OLD_EXP:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int
+only_older_enctype_p(const KDC_REQ *req)
+{
+ int i;
+
+ for(i = 0; i < req->req_body.etype.len; i++) {
+ if (!older_enctype(req->req_body.etype.val[i]))
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ *
+ */
+
+static krb5_error_code
+make_etype_info_entry(krb5_context context, ETYPE_INFO_ENTRY *ent, Key *key)
+{
+ ent->etype = key->key.keytype;
+ if(key->salt){
+#if 0
+ ALLOC(ent->salttype);
+
+ if(key->salt->type == hdb_pw_salt)
+ *ent->salttype = 0; /* or 1? or NULL? */
+ else if(key->salt->type == hdb_afs3_salt)
+ *ent->salttype = 2;
+ else {
+ kdc_log(context, config, 0, "unknown salt-type: %d",
+ key->salt->type);
+ return KRB5KRB_ERR_GENERIC;
+ }
+ /* according to `the specs', we can't send a salt if
+ we have AFS3 salted key, but that requires that you
+ *know* what cell you are using (e.g by assuming
+ that the cell is the same as the realm in lower
+ case) */
+#elif 0
+ ALLOC(ent->salttype);
+ *ent->salttype = key->salt->type;
+#else
+ /*
+ * We shouldn't sent salttype since it is incompatible with the
+ * specification and it breaks windows clients. The afs
+ * salting problem is solved by using KRB5-PADATA-AFS3-SALT
+ * implemented in Heimdal 0.7 and later.
+ */
+ ent->salttype = NULL;
+#endif
+ krb5_copy_data(context, &key->salt->salt,
+ &ent->salt);
+ } else {
+ /* we return no salt type at all, as that should indicate
+ * the default salt type and make everybody happy. some
+ * systems (like w2k) dislike being told the salt type
+ * here. */
+
+ ent->salttype = NULL;
+ ent->salt = NULL;
+ }
+ return 0;
+}
+
+static krb5_error_code
+get_pa_etype_info(krb5_context context,
+ krb5_kdc_configuration *config,
+ METHOD_DATA *md, hdb_entry *client,
+ ENCTYPE *etypes, unsigned int etypes_len)
+{
+ krb5_error_code ret = 0;
+ int i, j;
+ unsigned int n = 0;
+ ETYPE_INFO pa;
+ unsigned char *buf;
+ size_t len;
+
+
+ pa.len = client->keys.len;
+ if(pa.len > UINT_MAX/sizeof(*pa.val))
+ return ERANGE;
+ pa.val = malloc(pa.len * sizeof(*pa.val));
+ if(pa.val == NULL)
+ return ENOMEM;
+ memset(pa.val, 0, pa.len * sizeof(*pa.val));
+
+ for(i = 0; i < client->keys.len; i++) {
+ for (j = 0; j < n; j++)
+ if (pa.val[j].etype == client->keys.val[i].key.keytype)
+ goto skip1;
+ for(j = 0; j < etypes_len; j++) {
+ if(client->keys.val[i].key.keytype == etypes[j]) {
+ if (krb5_enctype_valid(context, etypes[j]) != 0)
+ continue;
+ if (!older_enctype(etypes[j]))
+ continue;
+ if (n >= pa.len)
+ krb5_abortx(context, "internal error: n >= p.len");
+ if((ret = make_etype_info_entry(context,
+ &pa.val[n++],
+ &client->keys.val[i])) != 0) {
+ free_ETYPE_INFO(&pa);
+ return ret;
+ }
+ break;
+ }
+ }
+ skip1:;
+ }
+ for(i = 0; i < client->keys.len; i++) {
+ /* already added? */
+ for(j = 0; j < etypes_len; j++) {
+ if(client->keys.val[i].key.keytype == etypes[j])
+ goto skip2;
+ }
+ if (krb5_enctype_valid(context, client->keys.val[i].key.keytype) != 0)
+ continue;
+ if (!older_enctype(etypes[j]))
+ continue;
+ if (n >= pa.len)
+ krb5_abortx(context, "internal error: n >= p.len");
+ if((ret = make_etype_info_entry(context,
+ &pa.val[n++],
+ &client->keys.val[i])) != 0) {
+ free_ETYPE_INFO(&pa);
+ return ret;
+ }
+ skip2:;
+ }
+
+ if(n < pa.len) {
+ /* stripped out dups, newer enctypes, and not valid enctypes */
+ pa.len = n;
+ }
+
+ ASN1_MALLOC_ENCODE(ETYPE_INFO, buf, len, &pa, &len, ret);
+ free_ETYPE_INFO(&pa);
+ if(ret)
+ return ret;
+ ret = realloc_method_data(md);
+ if(ret) {
+ free(buf);
+ return ret;
+ }
+ md->val[md->len - 1].padata_type = KRB5_PADATA_ETYPE_INFO;
+ md->val[md->len - 1].padata_value.length = len;
+ md->val[md->len - 1].padata_value.data = buf;
+ return 0;
+}
+
+/*
+ *
+ */
+
+extern int _krb5_AES_string_to_default_iterator;
+
+static krb5_error_code
+make_etype_info2_entry(ETYPE_INFO2_ENTRY *ent, Key *key)
+{
+ ent->etype = key->key.keytype;
+ if(key->salt) {
+ ALLOC(ent->salt);
+ if (ent->salt == NULL)
+ return ENOMEM;
+ *ent->salt = malloc(key->salt->salt.length + 1);
+ if (*ent->salt == NULL) {
+ free(ent->salt);
+ ent->salt = NULL;
+ return ENOMEM;
+ }
+ memcpy(*ent->salt, key->salt->salt.data, key->salt->salt.length);
+ (*ent->salt)[key->salt->salt.length] = '\0';
+ } else
+ ent->salt = NULL;
+
+ ent->s2kparams = NULL;
+
+ switch (key->key.keytype) {
+ case ETYPE_AES128_CTS_HMAC_SHA1_96:
+ case ETYPE_AES256_CTS_HMAC_SHA1_96:
+ ALLOC(ent->s2kparams);
+ if (ent->s2kparams == NULL)
+ return ENOMEM;
+ ent->s2kparams->length = 4;
+ ent->s2kparams->data = malloc(ent->s2kparams->length);
+ if (ent->s2kparams->data == NULL) {
+ free(ent->s2kparams);
+ ent->s2kparams = NULL;
+ return ENOMEM;
+ }
+ _krb5_put_int(ent->s2kparams->data,
+ _krb5_AES_string_to_default_iterator,
+ ent->s2kparams->length);
+ break;
+ case ETYPE_DES_CBC_CRC:
+ case ETYPE_DES_CBC_MD4:
+ case ETYPE_DES_CBC_MD5:
+ /* Check if this was a AFS3 salted key */
+ if(key->salt && key->salt->type == hdb_afs3_salt){
+ ALLOC(ent->s2kparams);
+ if (ent->s2kparams == NULL)
+ return ENOMEM;
+ ent->s2kparams->length = 1;
+ ent->s2kparams->data = malloc(ent->s2kparams->length);
+ if (ent->s2kparams->data == NULL) {
+ free(ent->s2kparams);
+ ent->s2kparams = NULL;
+ return ENOMEM;
+ }
+ _krb5_put_int(ent->s2kparams->data,
+ 1,
+ ent->s2kparams->length);
+ }
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+/*
+ * Return an ETYPE-INFO2. Enctypes are storted the same way as in the
+ * database (client supported enctypes first, then the unsupported
+ * enctypes).
+ */
+
+static krb5_error_code
+get_pa_etype_info2(krb5_context context,
+ krb5_kdc_configuration *config,
+ METHOD_DATA *md, hdb_entry *client,
+ ENCTYPE *etypes, unsigned int etypes_len)
+{
+ krb5_error_code ret = 0;
+ int i, j;
+ unsigned int n = 0;
+ ETYPE_INFO2 pa;
+ unsigned char *buf;
+ size_t len;
+
+ pa.len = client->keys.len;
+ if(pa.len > UINT_MAX/sizeof(*pa.val))
+ return ERANGE;
+ pa.val = malloc(pa.len * sizeof(*pa.val));
+ if(pa.val == NULL)
+ return ENOMEM;
+ memset(pa.val, 0, pa.len * sizeof(*pa.val));
+
+ for(i = 0; i < client->keys.len; i++) {
+ for (j = 0; j < n; j++)
+ if (pa.val[j].etype == client->keys.val[i].key.keytype)
+ goto skip1;
+ for(j = 0; j < etypes_len; j++) {
+ if(client->keys.val[i].key.keytype == etypes[j]) {
+ if (krb5_enctype_valid(context, etypes[j]) != 0)
+ continue;
+ if (n >= pa.len)
+ krb5_abortx(context, "internal error: n >= p.len");
+ if((ret = make_etype_info2_entry(&pa.val[n++],
+ &client->keys.val[i])) != 0) {
+ free_ETYPE_INFO2(&pa);
+ return ret;
+ }
+ break;
+ }
+ }
+ skip1:;
+ }
+ /* send enctypes that the client doesn't know about too */
+ for(i = 0; i < client->keys.len; i++) {
+ /* already added? */
+ for(j = 0; j < etypes_len; j++) {
+ if(client->keys.val[i].key.keytype == etypes[j])
+ goto skip2;
+ }
+ if (krb5_enctype_valid(context, client->keys.val[i].key.keytype) != 0)
+ continue;
+ if (n >= pa.len)
+ krb5_abortx(context, "internal error: n >= p.len");
+ if((ret = make_etype_info2_entry(&pa.val[n++],
+ &client->keys.val[i])) != 0) {
+ free_ETYPE_INFO2(&pa);
+ return ret;
+ }
+ skip2:;
+ }
+
+ if(n < pa.len) {
+ /* stripped out dups, and not valid enctypes */
+ pa.len = n;
+ }
+
+ ASN1_MALLOC_ENCODE(ETYPE_INFO2, buf, len, &pa, &len, ret);
+ free_ETYPE_INFO2(&pa);
+ if(ret)
+ return ret;
+ ret = realloc_method_data(md);
+ if(ret) {
+ free(buf);
+ return ret;
+ }
+ md->val[md->len - 1].padata_type = KRB5_PADATA_ETYPE_INFO2;
+ md->val[md->len - 1].padata_value.length = len;
+ md->val[md->len - 1].padata_value.data = buf;
+ return 0;
+}
+
+/*
+ *
+ */
+
+static void
+log_as_req(krb5_context context,
+ krb5_kdc_configuration *config,
+ krb5_enctype cetype,
+ krb5_enctype setype,
+ const KDC_REQ_BODY *b)
+{
+ krb5_error_code ret;
+ struct rk_strpool *p = NULL;
+ char *str;
+ int i;
+
+ for (i = 0; i < b->etype.len; i++) {
+ ret = krb5_enctype_to_string(context, b->etype.val[i], &str);
+ if (ret == 0) {
+ p = rk_strpoolprintf(p, "%s", str);
+ free(str);
+ } else
+ p = rk_strpoolprintf(p, "%d", b->etype.val[i]);
+ if (p && i + 1 < b->etype.len)
+ p = rk_strpoolprintf(p, ", ");
+ if (p == NULL) {
+ kdc_log(context, config, 0, "out of memory");
+ return;
+ }
+ }
+ if (p == NULL)
+ p = rk_strpoolprintf(p, "no encryption types");
+
+ str = rk_strpoolcollect(p);
+ kdc_log(context, config, 0, "Client supported enctypes: %s", str);
+ free(str);
+
+ {
+ char *cet;
+ char *set;
+
+ ret = krb5_enctype_to_string(context, cetype, &cet);
+ if(ret == 0) {
+ ret = krb5_enctype_to_string(context, setype, &set);
+ if (ret == 0) {
+ kdc_log(context, config, 5, "Using %s/%s", cet, set);
+ free(set);
+ }
+ free(cet);
+ }
+ if (ret != 0)
+ kdc_log(context, config, 5, "Using e-types %d/%d", cetype, setype);
+ }
+
+ {
+ char fixedstr[128];
+ unparse_flags(KDCOptions2int(b->kdc_options), asn1_KDCOptions_units(),
+ fixedstr, sizeof(fixedstr));
+ if(*fixedstr)
+ kdc_log(context, config, 2, "Requested flags: %s", fixedstr);
+ }
+}
+
+/*
+ * verify the flags on `client' and `server', returning 0
+ * if they are OK and generating an error messages and returning
+ * and error code otherwise.
+ */
+
+krb5_error_code
+_kdc_check_flags(krb5_context context,
+ krb5_kdc_configuration *config,
+ hdb_entry_ex *client_ex, const char *client_name,
+ hdb_entry_ex *server_ex, const char *server_name,
+ krb5_boolean is_as_req)
+{
+ if(client_ex != NULL) {
+ hdb_entry *client = &client_ex->entry;
+
+ /* check client */
+ if (client->flags.invalid) {
+ kdc_log(context, config, 0,
+ "Client (%s) has invalid bit set", client_name);
+ return KRB5KDC_ERR_POLICY;
+ }
+
+ if(!client->flags.client){
+ kdc_log(context, config, 0,
+ "Principal may not act as client -- %s", client_name);
+ return KRB5KDC_ERR_POLICY;
+ }
+
+ if (client->valid_start && *client->valid_start > kdc_time) {
+ char starttime_str[100];
+ krb5_format_time(context, *client->valid_start,
+ starttime_str, sizeof(starttime_str), TRUE);
+ kdc_log(context, config, 0,
+ "Client not yet valid until %s -- %s",
+ starttime_str, client_name);
+ return KRB5KDC_ERR_CLIENT_NOTYET;
+ }
+
+ if (client->valid_end && *client->valid_end < kdc_time) {
+ char endtime_str[100];
+ krb5_format_time(context, *client->valid_end,
+ endtime_str, sizeof(endtime_str), TRUE);
+ kdc_log(context, config, 0,
+ "Client expired at %s -- %s",
+ endtime_str, client_name);
+ return KRB5KDC_ERR_NAME_EXP;
+ }
+
+ if (client->pw_end && *client->pw_end < kdc_time
+ && (server_ex == NULL || !server_ex->entry.flags.change_pw)) {
+ char pwend_str[100];
+ krb5_format_time(context, *client->pw_end,
+ pwend_str, sizeof(pwend_str), TRUE);
+ kdc_log(context, config, 0,
+ "Client's key has expired at %s -- %s",
+ pwend_str, client_name);
+ return KRB5KDC_ERR_KEY_EXPIRED;
+ }
+ }
+
+ /* check server */
+
+ if (server_ex != NULL) {
+ hdb_entry *server = &server_ex->entry;
+
+ if (server->flags.invalid) {
+ kdc_log(context, config, 0,
+ "Server has invalid flag set -- %s", server_name);
+ return KRB5KDC_ERR_POLICY;
+ }
+
+ if(!server->flags.server){
+ kdc_log(context, config, 0,
+ "Principal may not act as server -- %s", server_name);
+ return KRB5KDC_ERR_POLICY;
+ }
+
+ if(!is_as_req && server->flags.initial) {
+ kdc_log(context, config, 0,
+ "AS-REQ is required for server -- %s", server_name);
+ return KRB5KDC_ERR_POLICY;
+ }
+
+ if (server->valid_start && *server->valid_start > kdc_time) {
+ char starttime_str[100];
+ krb5_format_time(context, *server->valid_start,
+ starttime_str, sizeof(starttime_str), TRUE);
+ kdc_log(context, config, 0,
+ "Server not yet valid until %s -- %s",
+ starttime_str, server_name);
+ return KRB5KDC_ERR_SERVICE_NOTYET;
+ }
+
+ if (server->valid_end && *server->valid_end < kdc_time) {
+ char endtime_str[100];
+ krb5_format_time(context, *server->valid_end,
+ endtime_str, sizeof(endtime_str), TRUE);
+ kdc_log(context, config, 0,
+ "Server expired at %s -- %s",
+ endtime_str, server_name);
+ return KRB5KDC_ERR_SERVICE_EXP;
+ }
+
+ if (server->pw_end && *server->pw_end < kdc_time) {
+ char pwend_str[100];
+ krb5_format_time(context, *server->pw_end,
+ pwend_str, sizeof(pwend_str), TRUE);
+ kdc_log(context, config, 0,
+ "Server's key has expired at -- %s",
+ pwend_str, server_name);
+ return KRB5KDC_ERR_KEY_EXPIRED;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Return TRUE if `from' is part of `addresses' taking into consideration
+ * the configuration variables that tells us how strict we should be about
+ * these checks
+ */
+
+krb5_boolean
+_kdc_check_addresses(krb5_context context,
+ krb5_kdc_configuration *config,
+ HostAddresses *addresses, const struct sockaddr *from)
+{
+ krb5_error_code ret;
+ krb5_address addr;
+ krb5_boolean result;
+ krb5_boolean only_netbios = TRUE;
+ int i;
+
+ if(config->check_ticket_addresses == 0)
+ return TRUE;
+
+ if(addresses == NULL)
+ return config->allow_null_ticket_addresses;
+
+ for (i = 0; i < addresses->len; ++i) {
+ if (addresses->val[i].addr_type != KRB5_ADDRESS_NETBIOS) {
+ only_netbios = FALSE;
+ }
+ }
+
+ /* Windows sends it's netbios name, which I can only assume is
+ * used for the 'allowed workstations' check. This is painful,
+ * but we still want to check IP addresses if they happen to be
+ * present.
+ */
+
+ if(only_netbios)
+ return config->allow_null_ticket_addresses;
+
+ ret = krb5_sockaddr2address (context, from, &addr);
+ if(ret)
+ return FALSE;
+
+ result = krb5_address_search(context, &addr, addresses);
+ krb5_free_address (context, &addr);
+ return result;
+}
+
+/*
+ *
+ */
+
+static krb5_boolean
+send_pac_p(krb5_context context, KDC_REQ *req)
+{
+ krb5_error_code ret;
+ PA_PAC_REQUEST pacreq;
+ const PA_DATA *pa;
+ int i = 0;
+
+ pa = _kdc_find_padata(req, &i, KRB5_PADATA_PA_PAC_REQUEST);
+ if (pa == NULL)
+ return TRUE;
+
+ ret = decode_PA_PAC_REQUEST(pa->padata_value.data,
+ pa->padata_value.length,
+ &pacreq,
+ NULL);
+ if (ret)
+ return TRUE;
+ i = pacreq.include_pac;
+ free_PA_PAC_REQUEST(&pacreq);
+ if (i == 0)
+ return FALSE;
+ return TRUE;
+}
+
+/*
+ *
+ */
+
+krb5_error_code
+_kdc_as_rep(krb5_context context,
+ krb5_kdc_configuration *config,
+ KDC_REQ *req,
+ const krb5_data *req_buffer,
+ krb5_data *reply,
+ const char *from,
+ struct sockaddr *from_addr,
+ int datagram_reply)
+{
+ KDC_REQ_BODY *b = &req->req_body;
+ AS_REP rep;
+ KDCOptions f = b->kdc_options;
+ hdb_entry_ex *client = NULL, *server = NULL;
+ krb5_enctype cetype, setype, sessionetype;
+ krb5_data e_data;
+ EncTicketPart et;
+ EncKDCRepPart ek;
+ krb5_principal client_princ = NULL, server_princ = NULL;
+ char *client_name = NULL, *server_name = NULL;
+ krb5_error_code ret = 0;
+ const char *e_text = NULL;
+ krb5_crypto crypto;
+ Key *ckey, *skey;
+ EncryptionKey *reply_key;
+ int flags = 0;
+#ifdef PKINIT
+ pk_client_params *pkp = NULL;
+#endif
+
+ memset(&rep, 0, sizeof(rep));
+ krb5_data_zero(&e_data);
+
+ if (f.canonicalize)
+ flags |= HDB_F_CANON;
+
+ if(b->sname == NULL){
+ ret = KRB5KRB_ERR_GENERIC;
+ e_text = "No server in request";
+ } else{
+ ret = _krb5_principalname2krb5_principal (context,
+ &server_princ,
+ *(b->sname),
+ b->realm);
+ if (ret == 0)
+ ret = krb5_unparse_name(context, server_princ, &server_name);
+ }
+ if (ret) {
+ kdc_log(context, config, 0,
+ "AS-REQ malformed server name from %s", from);
+ goto out;
+ }
+
+ if(b->cname == NULL){
+ ret = KRB5KRB_ERR_GENERIC;
+ e_text = "No client in request";
+ } else {
+
+ if (b->cname->name_type == KRB5_NT_ENTERPRISE_PRINCIPAL) {
+ if (b->cname->name_string.len != 1) {
+ kdc_log(context, config, 0,
+ "AS-REQ malformed canon request from %s, "
+ "enterprise name with %d name components",
+ from, b->cname->name_string.len);
+ ret = KRB5_PARSE_MALFORMED;
+ goto out;
+ }
+ ret = krb5_parse_name(context, b->cname->name_string.val[0],
+ &client_princ);
+ if (ret)
+ goto out;
+ } else {
+ ret = _krb5_principalname2krb5_principal (context,
+ &client_princ,
+ *(b->cname),
+ b->realm);
+ if (ret)
+ goto out;
+ }
+ ret = krb5_unparse_name(context, client_princ, &client_name);
+ }
+ if (ret) {
+ kdc_log(context, config, 0,
+ "AS-REQ malformed client name from %s", from);
+ goto out;
+ }
+
+ kdc_log(context, config, 0, "AS-REQ %s from %s for %s",
+ client_name, from, server_name);
+
+ ret = _kdc_db_fetch(context, config, client_princ,
+ HDB_F_GET_CLIENT | flags, NULL, &client);
+ if(ret){
+ kdc_log(context, config, 0, "UNKNOWN -- %s: %s", client_name,
+ krb5_get_err_text(context, ret));
+ ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
+ goto out;
+ }
+
+ ret = _kdc_db_fetch(context, config, server_princ,
+ HDB_F_GET_SERVER|HDB_F_GET_KRBTGT,
+ NULL, &server);
+ if(ret){
+ kdc_log(context, config, 0, "UNKNOWN -- %s: %s", server_name,
+ krb5_get_err_text(context, ret));
+ ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
+ goto out;
+ }
+
+ ret = _kdc_windc_client_access(context, client, req, &e_data);
+ if(ret)
+ goto out;
+
+ ret = _kdc_check_flags(context, config,
+ client, client_name,
+ server, server_name,
+ TRUE);
+ if(ret)
+ goto out;
+
+ memset(&et, 0, sizeof(et));
+ memset(&ek, 0, sizeof(ek));
+
+ if(req->padata){
+ int i;
+ const PA_DATA *pa;
+ int found_pa = 0;
+
+ log_patypes(context, config, req->padata);
+
+#ifdef PKINIT
+ kdc_log(context, config, 5,
+ "Looking for PKINIT pa-data -- %s", client_name);
+
+ e_text = "No PKINIT PA found";
+
+ i = 0;
+ if ((pa = _kdc_find_padata(req, &i, KRB5_PADATA_PK_AS_REQ)))
+ ;
+ if (pa == NULL) {
+ i = 0;
+ if((pa = _kdc_find_padata(req, &i, KRB5_PADATA_PK_AS_REQ_WIN)))
+ ;
+ }
+ if (pa) {
+ char *client_cert = NULL;
+
+ ret = _kdc_pk_rd_padata(context, config, req, pa, &pkp);
+ if (ret) {
+ ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
+ kdc_log(context, config, 5,
+ "Failed to decode PKINIT PA-DATA -- %s",
+ client_name);
+ goto ts_enc;
+ }
+ if (ret == 0 && pkp == NULL)
+ goto ts_enc;
+
+ ret = _kdc_pk_check_client(context,
+ config,
+ client,
+ pkp,
+ &client_cert);
+ if (ret) {
+ e_text = "PKINIT certificate not allowed to "
+ "impersonate principal";
+ _kdc_pk_free_client_param(context, pkp);
+
+ kdc_log(context, config, 0, "%s", e_text);
+ pkp = NULL;
+ goto out;
+ }
+ found_pa = 1;
+ et.flags.pre_authent = 1;
+ kdc_log(context, config, 0,
+ "PKINIT pre-authentication succeeded -- %s using %s",
+ client_name, client_cert);
+ free(client_cert);
+ if (pkp)
+ goto preauth_done;
+ }
+ ts_enc:
+#endif
+ kdc_log(context, config, 5, "Looking for ENC-TS pa-data -- %s",
+ client_name);
+
+ i = 0;
+ e_text = "No ENC-TS found";
+ while((pa = _kdc_find_padata(req, &i, KRB5_PADATA_ENC_TIMESTAMP))){
+ krb5_data ts_data;
+ PA_ENC_TS_ENC p;
+ size_t len;
+ EncryptedData enc_data;
+ Key *pa_key;
+ char *str;
+
+ found_pa = 1;
+
+ ret = decode_EncryptedData(pa->padata_value.data,
+ pa->padata_value.length,
+ &enc_data,
+ &len);
+ if (ret) {
+ ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
+ kdc_log(context, config, 5, "Failed to decode PA-DATA -- %s",
+ client_name);
+ goto out;
+ }
+
+ ret = hdb_enctype2key(context, &client->entry,
+ enc_data.etype, &pa_key);
+ if(ret){
+ char *estr;
+ e_text = "No key matches pa-data";
+ ret = KRB5KDC_ERR_ETYPE_NOSUPP;
+ if(krb5_enctype_to_string(context, enc_data.etype, &estr))
+ estr = NULL;
+ if(estr == NULL)
+ kdc_log(context, config, 5,
+ "No client key matching pa-data (%d) -- %s",
+ enc_data.etype, client_name);
+ else
+ kdc_log(context, config, 5,
+ "No client key matching pa-data (%s) -- %s",
+ estr, client_name);
+ free(estr);
+
+ free_EncryptedData(&enc_data);
+ continue;
+ }
+
+ try_next_key:
+ ret = krb5_crypto_init(context, &pa_key->key, 0, &crypto);
+ if (ret) {
+ kdc_log(context, config, 0, "krb5_crypto_init failed: %s",
+ krb5_get_err_text(context, ret));
+ free_EncryptedData(&enc_data);
+ continue;
+ }
+
+ ret = krb5_decrypt_EncryptedData (context,
+ crypto,
+ KRB5_KU_PA_ENC_TIMESTAMP,
+ &enc_data,
+ &ts_data);
+ krb5_crypto_destroy(context, crypto);
+ if(ret){
+ krb5_error_code ret2;
+ ret2 = krb5_enctype_to_string(context,
+ pa_key->key.keytype, &str);
+ if (ret2)
+ str = NULL;
+ kdc_log(context, config, 5,
+ "Failed to decrypt PA-DATA -- %s "
+ "(enctype %s) error %s",
+ client_name,
+ str ? str : "unknown enctype",
+ krb5_get_err_text(context, ret));
+ free(str);
+
+ if(hdb_next_enctype2key(context, &client->entry,
+ enc_data.etype, &pa_key) == 0)
+ goto try_next_key;
+ e_text = "Failed to decrypt PA-DATA";
+
+ free_EncryptedData(&enc_data);
+ ret = KRB5KDC_ERR_PREAUTH_FAILED;
+ continue;
+ }
+ free_EncryptedData(&enc_data);
+ ret = decode_PA_ENC_TS_ENC(ts_data.data,
+ ts_data.length,
+ &p,
+ &len);
+ krb5_data_free(&ts_data);
+ if(ret){
+ e_text = "Failed to decode PA-ENC-TS-ENC";
+ ret = KRB5KDC_ERR_PREAUTH_FAILED;
+ kdc_log(context, config,
+ 5, "Failed to decode PA-ENC-TS_ENC -- %s",
+ client_name);
+ continue;
+ }
+ free_PA_ENC_TS_ENC(&p);
+ if (abs(kdc_time - p.patimestamp) > context->max_skew) {
+ char client_time[100];
+
+ krb5_format_time(context, p.patimestamp,
+ client_time, sizeof(client_time), TRUE);
+
+ ret = KRB5KRB_AP_ERR_SKEW;
+ kdc_log(context, config, 0,
+ "Too large time skew, "
+ "client time %s is out by %u > %u seconds -- %s",
+ client_time,
+ (unsigned)abs(kdc_time - p.patimestamp),
+ context->max_skew,
+ client_name);
+#if 1
+ /* This code is from samba, needs testing */
+ /*
+ * the following is needed to make windows clients
+ * to retry using the timestamp in the error message
+ *
+ * this is maybe a bug in windows to not trying when e_text
+ * is present...
+ */
+ e_text = NULL;
+#else
+ e_text = "Too large time skew";
+#endif
+ goto out;
+ }
+ et.flags.pre_authent = 1;
+
+ ret = krb5_enctype_to_string(context,pa_key->key.keytype, &str);
+ if (ret)
+ str = NULL;
+
+ kdc_log(context, config, 2,
+ "ENC-TS Pre-authentication succeeded -- %s using %s",
+ client_name, str ? str : "unknown enctype");
+ free(str);
+ break;
+ }
+#ifdef PKINIT
+ preauth_done:
+#endif
+ if(found_pa == 0 && config->require_preauth)
+ goto use_pa;
+ /* We come here if we found a pa-enc-timestamp, but if there
+ was some problem with it, other than too large skew */
+ if(found_pa && et.flags.pre_authent == 0){
+ kdc_log(context, config, 0, "%s -- %s", e_text, client_name);
+ e_text = NULL;
+ goto out;
+ }
+ }else if (config->require_preauth
+ || client->entry.flags.require_preauth
+ || server->entry.flags.require_preauth) {
+ METHOD_DATA method_data;
+ PA_DATA *pa;
+ unsigned char *buf;
+ size_t len;
+
+ use_pa:
+ method_data.len = 0;
+ method_data.val = NULL;
+
+ ret = realloc_method_data(&method_data);
+ pa = &method_data.val[method_data.len-1];
+ pa->padata_type = KRB5_PADATA_ENC_TIMESTAMP;
+ pa->padata_value.length = 0;
+ pa->padata_value.data = NULL;
+
+#ifdef PKINIT
+ ret = realloc_method_data(&method_data);
+ pa = &method_data.val[method_data.len-1];
+ pa->padata_type = KRB5_PADATA_PK_AS_REQ;
+ pa->padata_value.length = 0;
+ pa->padata_value.data = NULL;
+
+ ret = realloc_method_data(&method_data);
+ pa = &method_data.val[method_data.len-1];
+ pa->padata_type = KRB5_PADATA_PK_AS_REQ_WIN;
+ pa->padata_value.length = 0;
+ pa->padata_value.data = NULL;
+#endif
+
+ /*
+ * RFC4120 requires:
+ * - If the client only knows about old enctypes, then send
+ * both info replies (we send 'info' first in the list).
+ * - If the client is 'modern', because it knows about 'new'
+ * enctype types, then only send the 'info2' reply.
+ */
+
+ /* XXX check ret */
+ if (only_older_enctype_p(req))
+ ret = get_pa_etype_info(context, config,
+ &method_data, &client->entry,
+ b->etype.val, b->etype.len);
+ /* XXX check ret */
+ ret = get_pa_etype_info2(context, config, &method_data,
+ &client->entry, b->etype.val, b->etype.len);
+
+
+ ASN1_MALLOC_ENCODE(METHOD_DATA, buf, len, &method_data, &len, ret);
+ free_METHOD_DATA(&method_data);
+
+ e_data.data = buf;
+ e_data.length = len;
+ e_text ="Need to use PA-ENC-TIMESTAMP/PA-PK-AS-REQ",
+
+ ret = KRB5KDC_ERR_PREAUTH_REQUIRED;
+
+ kdc_log(context, config, 0,
+ "No preauth found, returning PREAUTH-REQUIRED -- %s",
+ client_name);
+ goto out;
+ }
+
+ /*
+ * Find the client key (for preauth ENC-TS verification and reply
+ * encryption). Then the best encryption type for the KDC and
+ * last the best session key that shared between the client and
+ * KDC runtime enctypes.
+ */
+
+ ret = _kdc_find_etype(context, client, b->etype.val, b->etype.len,
+ &ckey, &cetype);
+ if (ret) {
+ kdc_log(context, config, 0,
+ "Client (%s) has no support for etypes", client_name);
+ goto out;
+ }
+
+ ret = _kdc_get_preferred_key(context, config,
+ server, server_name,
+ &setype, &skey);
+ if(ret)
+ goto out;
+
+ /*
+ * Select a session enctype from the list of the crypto systems
+ * supported enctype, is supported by the client and is one of the
+ * enctype of the enctype of the krbtgt.
+ *
+ * The later is used as a hint what enctype all KDC are supporting
+ * to make sure a newer version of KDC wont generate a session
+ * enctype that and older version of a KDC in the same realm can't
+ * decrypt.
+ *
+ * But if the KDC admin is paranoid and doesn't want to have "no
+ * the best" enctypes on the krbtgt, lets save the best pick from
+ * the client list and hope that that will work for any other
+ * KDCs.
+ */
+ {
+ const krb5_enctype *p;
+ krb5_enctype clientbest = ETYPE_NULL;
+ int i, j;
+
+ p = krb5_kerberos_enctypes(context);
+
+ sessionetype = ETYPE_NULL;
+
+ for (i = 0; p[i] != ETYPE_NULL && sessionetype == ETYPE_NULL; i++) {
+ if (krb5_enctype_valid(context, p[i]) != 0)
+ continue;
+
+ for (j = 0; j < b->etype.len && sessionetype == ETYPE_NULL; j++) {
+ Key *dummy;
+ /* check with client */
+ if (p[i] != b->etype.val[j])
+ continue;
+ /* save best of union of { client, crypto system } */
+ if (clientbest == ETYPE_NULL)
+ clientbest = p[i];
+ /* check with krbtgt */
+ ret = hdb_enctype2key(context, &server->entry, p[i], &dummy);
+ if (ret)
+ continue;
+ sessionetype = p[i];
+ }
+ }
+ /* if krbtgt had no shared keys with client, pick clients best */
+ if (clientbest != ETYPE_NULL && sessionetype == ETYPE_NULL) {
+ sessionetype = clientbest;
+ } else if (sessionetype == ETYPE_NULL) {
+ kdc_log(context, config, 0,
+ "Client (%s) from %s has no common enctypes with KDC"
+ "to use for the session key",
+ client_name, from);
+ goto out;
+ }
+ }
+
+ log_as_req(context, config, cetype, setype, b);
+
+ if(f.renew || f.validate || f.proxy || f.forwarded || f.enc_tkt_in_skey
+ || (f.request_anonymous && !config->allow_anonymous)) {
+ ret = KRB5KDC_ERR_BADOPTION;
+ kdc_log(context, config, 0, "Bad KDC options -- %s", client_name);
+ goto out;
+ }
+
+ rep.pvno = 5;
+ rep.msg_type = krb_as_rep;
+ copy_Realm(&client->entry.principal->realm, &rep.crealm);
+ if (f.request_anonymous)
+ _kdc_make_anonymous_principalname (&rep.cname);
+ else
+ _krb5_principal2principalname(&rep.cname,
+ client->entry.principal);
+ rep.ticket.tkt_vno = 5;
+ copy_Realm(&server->entry.principal->realm, &rep.ticket.realm);
+ _krb5_principal2principalname(&rep.ticket.sname,
+ server->entry.principal);
+ /* java 1.6 expects the name to be the same type, lets allow that
+ * uncomplicated name-types. */
+#define CNT(sp,t) (((sp)->sname->name_type) == KRB5_NT_##t)
+ if (CNT(b, UNKNOWN) || CNT(b, PRINCIPAL) || CNT(b, SRV_INST) || CNT(b, SRV_HST) || CNT(b, SRV_XHST))
+ rep.ticket.sname.name_type = b->sname->name_type;
+#undef CNT
+
+ et.flags.initial = 1;
+ if(client->entry.flags.forwardable && server->entry.flags.forwardable)
+ et.flags.forwardable = f.forwardable;
+ else if (f.forwardable) {
+ ret = KRB5KDC_ERR_POLICY;
+ kdc_log(context, config, 0,
+ "Ticket may not be forwardable -- %s", client_name);
+ goto out;
+ }
+ if(client->entry.flags.proxiable && server->entry.flags.proxiable)
+ et.flags.proxiable = f.proxiable;
+ else if (f.proxiable) {
+ ret = KRB5KDC_ERR_POLICY;
+ kdc_log(context, config, 0,
+ "Ticket may not be proxiable -- %s", client_name);
+ goto out;
+ }
+ if(client->entry.flags.postdate && server->entry.flags.postdate)
+ et.flags.may_postdate = f.allow_postdate;
+ else if (f.allow_postdate){
+ ret = KRB5KDC_ERR_POLICY;
+ kdc_log(context, config, 0,
+ "Ticket may not be postdatable -- %s", client_name);
+ goto out;
+ }
+
+ /* check for valid set of addresses */
+ if(!_kdc_check_addresses(context, config, b->addresses, from_addr)) {
+ ret = KRB5KRB_AP_ERR_BADADDR;
+ kdc_log(context, config, 0,
+ "Bad address list requested -- %s", client_name);
+ goto out;
+ }
+
+ ret = krb5_generate_random_keyblock(context, sessionetype, &et.key);
+ if (ret)
+ goto out;
+ copy_PrincipalName(&rep.cname, &et.cname);
+ copy_Realm(&rep.crealm, &et.crealm);
+
+ {
+ time_t start;
+ time_t t;
+
+ start = et.authtime = kdc_time;
+
+ if(f.postdated && req->req_body.from){
+ ALLOC(et.starttime);
+ start = *et.starttime = *req->req_body.from;
+ et.flags.invalid = 1;
+ et.flags.postdated = 1; /* XXX ??? */
+ }
+ _kdc_fix_time(&b->till);
+ t = *b->till;
+
+ /* be careful not overflowing */
+
+ if(client->entry.max_life)
+ t = start + min(t - start, *client->entry.max_life);
+ if(server->entry.max_life)
+ t = start + min(t - start, *server->entry.max_life);
+#if 0
+ t = min(t, start + realm->max_life);
+#endif
+ et.endtime = t;
+ if(f.renewable_ok && et.endtime < *b->till){
+ f.renewable = 1;
+ if(b->rtime == NULL){
+ ALLOC(b->rtime);
+ *b->rtime = 0;
+ }
+ if(*b->rtime < *b->till)
+ *b->rtime = *b->till;
+ }
+ if(f.renewable && b->rtime){
+ t = *b->rtime;
+ if(t == 0)
+ t = MAX_TIME;
+ if(client->entry.max_renew)
+ t = start + min(t - start, *client->entry.max_renew);
+ if(server->entry.max_renew)
+ t = start + min(t - start, *server->entry.max_renew);
+#if 0
+ t = min(t, start + realm->max_renew);
+#endif
+ ALLOC(et.renew_till);
+ *et.renew_till = t;
+ et.flags.renewable = 1;
+ }
+ }
+
+ if (f.request_anonymous)
+ et.flags.anonymous = 1;
+
+ if(b->addresses){
+ ALLOC(et.caddr);
+ copy_HostAddresses(b->addresses, et.caddr);
+ }
+
+ et.transited.tr_type = DOMAIN_X500_COMPRESS;
+ krb5_data_zero(&et.transited.contents);
+
+ copy_EncryptionKey(&et.key, &ek.key);
+
+ /* The MIT ASN.1 library (obviously) doesn't tell lengths encoded
+ * as 0 and as 0x80 (meaning indefinite length) apart, and is thus
+ * incapable of correctly decoding SEQUENCE OF's of zero length.
+ *
+ * To fix this, always send at least one no-op last_req
+ *
+ * If there's a pw_end or valid_end we will use that,
+ * otherwise just a dummy lr.
+ */
+ ek.last_req.val = malloc(2 * sizeof(*ek.last_req.val));
+ if (ek.last_req.val == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+ ek.last_req.len = 0;
+ if (client->entry.pw_end
+ && (config->kdc_warn_pwexpire == 0
+ || kdc_time + config->kdc_warn_pwexpire >= *client->entry.pw_end)) {
+ ek.last_req.val[ek.last_req.len].lr_type = LR_PW_EXPTIME;
+ ek.last_req.val[ek.last_req.len].lr_value = *client->entry.pw_end;
+ ++ek.last_req.len;
+ }
+ if (client->entry.valid_end) {
+ ek.last_req.val[ek.last_req.len].lr_type = LR_ACCT_EXPTIME;
+ ek.last_req.val[ek.last_req.len].lr_value = *client->entry.valid_end;
+ ++ek.last_req.len;
+ }
+ if (ek.last_req.len == 0) {
+ ek.last_req.val[ek.last_req.len].lr_type = LR_NONE;
+ ek.last_req.val[ek.last_req.len].lr_value = 0;
+ ++ek.last_req.len;
+ }
+ ek.nonce = b->nonce;
+ if (client->entry.valid_end || client->entry.pw_end) {
+ ALLOC(ek.key_expiration);
+ if (client->entry.valid_end) {
+ if (client->entry.pw_end)
+ *ek.key_expiration = min(*client->entry.valid_end,
+ *client->entry.pw_end);
+ else
+ *ek.key_expiration = *client->entry.valid_end;
+ } else
+ *ek.key_expiration = *client->entry.pw_end;
+ } else
+ ek.key_expiration = NULL;
+ ek.flags = et.flags;
+ ek.authtime = et.authtime;
+ if (et.starttime) {
+ ALLOC(ek.starttime);
+ *ek.starttime = *et.starttime;
+ }
+ ek.endtime = et.endtime;
+ if (et.renew_till) {
+ ALLOC(ek.renew_till);
+ *ek.renew_till = *et.renew_till;
+ }
+ copy_Realm(&rep.ticket.realm, &ek.srealm);
+ copy_PrincipalName(&rep.ticket.sname, &ek.sname);
+ if(et.caddr){
+ ALLOC(ek.caddr);
+ copy_HostAddresses(et.caddr, ek.caddr);
+ }
+
+ ALLOC(rep.padata);
+ rep.padata->len = 0;
+ rep.padata->val = NULL;
+
+ reply_key = &ckey->key;
+#if PKINIT
+ if (pkp) {
+ ret = _kdc_pk_mk_pa_reply(context, config, pkp, client,
+ req, req_buffer,
+ &reply_key, rep.padata);
+ if (ret)
+ goto out;
+ ret = _kdc_add_inital_verified_cas(context,
+ config,
+ pkp,
+ &et);
+ if (ret)
+ goto out;
+ }
+#endif
+
+ set_salt_padata (rep.padata, ckey->salt);
+
+ /* Add signing of alias referral */
+ if (f.canonicalize) {
+ PA_ClientCanonicalized canon;
+ krb5_data data;
+ PA_DATA pa;
+ krb5_crypto crypto;
+ size_t len;
+
+ memset(&canon, 0, sizeof(canon));
+
+ canon.names.requested_name = *b->cname;
+ canon.names.mapped_name = client->entry.principal->name;
+
+ ASN1_MALLOC_ENCODE(PA_ClientCanonicalizedNames, data.data, data.length,
+ &canon.names, &len, ret);
+ if (ret)
+ goto out;
+ if (data.length != len)
+ krb5_abortx(context, "internal asn.1 error");
+
+ /* sign using "returned session key" */
+ ret = krb5_crypto_init(context, &et.key, 0, &crypto);
+ if (ret) {
+ free(data.data);
+ goto out;
+ }
+
+ ret = krb5_create_checksum(context, crypto,
+ KRB5_KU_CANONICALIZED_NAMES, 0,
+ data.data, data.length,
+ &canon.canon_checksum);
+ free(data.data);
+ krb5_crypto_destroy(context, crypto);
+ if (ret)
+ goto out;
+
+ ASN1_MALLOC_ENCODE(PA_ClientCanonicalized, data.data, data.length,
+ &canon, &len, ret);
+ free_Checksum(&canon.canon_checksum);
+ if (ret)
+ goto out;
+ if (data.length != len)
+ krb5_abortx(context, "internal asn.1 error");
+
+ pa.padata_type = KRB5_PADATA_CLIENT_CANONICALIZED;
+ pa.padata_value = data;
+ ret = add_METHOD_DATA(rep.padata, &pa);
+ free(data.data);
+ if (ret)
+ goto out;
+ }
+
+ if (rep.padata->len == 0) {
+ free(rep.padata);
+ rep.padata = NULL;
+ }
+
+ /* Add the PAC */
+ if (send_pac_p(context, req)) {
+ krb5_pac p = NULL;
+ krb5_data data;
+
+ ret = _kdc_pac_generate(context, client, &p);
+ if (ret) {
+ kdc_log(context, config, 0, "PAC generation failed for -- %s",
+ client_name);
+ goto out;
+ }
+ if (p != NULL) {
+ ret = _krb5_pac_sign(context, p, et.authtime,
+ client->entry.principal,
+ &skey->key, /* Server key */
+ &skey->key, /* FIXME: should be krbtgt key */
+ &data);
+ krb5_pac_free(context, p);
+ if (ret) {
+ kdc_log(context, config, 0, "PAC signing failed for -- %s",
+ client_name);
+ goto out;
+ }
+
+ ret = _kdc_tkt_add_if_relevant_ad(context, &et,
+ KRB5_AUTHDATA_WIN2K_PAC,
+ &data);
+ krb5_data_free(&data);
+ if (ret)
+ goto out;
+ }
+ }
+
+ _kdc_log_timestamp(context, config, "AS-REQ", et.authtime, et.starttime,
+ et.endtime, et.renew_till);
+
+ /* do this as the last thing since this signs the EncTicketPart */
+ ret = _kdc_add_KRB5SignedPath(context,
+ config,
+ server,
+ setype,
+ NULL,
+ NULL,
+ &et);
+ if (ret)
+ goto out;
+
+ ret = _kdc_encode_reply(context, config,
+ &rep, &et, &ek, setype, server->entry.kvno,
+ &skey->key, client->entry.kvno,
+ reply_key, &e_text, reply);
+ free_EncTicketPart(&et);
+ free_EncKDCRepPart(&ek);
+ if (ret)
+ goto out;
+
+ /* */
+ if (datagram_reply && reply->length > config->max_datagram_reply_length) {
+ krb5_data_free(reply);
+ ret = KRB5KRB_ERR_RESPONSE_TOO_BIG;
+ e_text = "Reply packet too large";
+ }
+
+out:
+ free_AS_REP(&rep);
+ if(ret){
+ krb5_mk_error(context,
+ ret,
+ e_text,
+ (e_data.data ? &e_data : NULL),
+ client_princ,
+ server_princ,
+ NULL,
+ NULL,
+ reply);
+ ret = 0;
+ }
+#ifdef PKINIT
+ if (pkp)
+ _kdc_pk_free_client_param(context, pkp);
+#endif
+ if (e_data.data)
+ free(e_data.data);
+ if (client_princ)
+ krb5_free_principal(context, client_princ);
+ free(client_name);
+ if (server_princ)
+ krb5_free_principal(context, server_princ);
+ free(server_name);
+ if(client)
+ _kdc_free_ent(context, client);
+ if(server)
+ _kdc_free_ent(context, server);
+ return ret;
+}
+
+/*
+ * Add the AuthorizationData `data´ of `type´ to the last element in
+ * the sequence of authorization_data in `tkt´ wrapped in an IF_RELEVANT
+ */
+
+krb5_error_code
+_kdc_tkt_add_if_relevant_ad(krb5_context context,
+ EncTicketPart *tkt,
+ int type,
+ const krb5_data *data)
+{
+ krb5_error_code ret;
+ size_t size;
+
+ if (tkt->authorization_data == NULL) {
+ tkt->authorization_data = calloc(1, sizeof(*tkt->authorization_data));
+ if (tkt->authorization_data == NULL) {
+ krb5_set_error_message(context, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+ }
+
+ /* add the entry to the last element */
+ {
+ AuthorizationData ad = { 0, NULL };
+ AuthorizationDataElement ade;
+
+ ade.ad_type = type;
+ ade.ad_data = *data;
+
+ ret = add_AuthorizationData(&ad, &ade);
+ if (ret) {
+ krb5_set_error_message(context, ret, "add AuthorizationData failed");
+ return ret;
+ }
+
+ ade.ad_type = KRB5_AUTHDATA_IF_RELEVANT;
+
+ ASN1_MALLOC_ENCODE(AuthorizationData,
+ ade.ad_data.data, ade.ad_data.length,
+ &ad, &size, ret);
+ free_AuthorizationData(&ad);
+ if (ret) {
+ krb5_set_error_message(context, ret, "ASN.1 encode of "
+ "AuthorizationData failed");
+ return ret;
+ }
+ if (ade.ad_data.length != size)
+ krb5_abortx(context, "internal asn.1 encoder error");
+
+ ret = add_AuthorizationData(tkt->authorization_data, &ade);
+ der_free_octet_string(&ade.ad_data);
+ if (ret) {
+ krb5_set_error_message(context, ret, "add AuthorizationData failed");
+ return ret;
+ }
+ }
+
+ return 0;
+}
diff --git a/source4/heimdal/kdc/krb5tgs.c b/source4/heimdal/kdc/krb5tgs.c
new file mode 100644
index 0000000000..4cf93e5a54
--- /dev/null
+++ b/source4/heimdal/kdc/krb5tgs.c
@@ -0,0 +1,2087 @@
+/*
+ * Copyright (c) 1997-2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kdc_locl.h"
+
+RCSID("$Id$");
+
+/*
+ * return the realm of a krbtgt-ticket or NULL
+ */
+
+static Realm
+get_krbtgt_realm(const PrincipalName *p)
+{
+ if(p->name_string.len == 2
+ && strcmp(p->name_string.val[0], KRB5_TGS_NAME) == 0)
+ return p->name_string.val[1];
+ else
+ return NULL;
+}
+
+/*
+ * The KDC might add a signed path to the ticket authorization data
+ * field. This is to avoid server impersonating clients and the
+ * request constrained delegation.
+ *
+ * This is done by storing a KRB5_AUTHDATA_IF_RELEVANT with a single
+ * entry of type KRB5SignedPath.
+ */
+
+static krb5_error_code
+find_KRB5SignedPath(krb5_context context,
+ const AuthorizationData *ad,
+ krb5_data *data)
+{
+ AuthorizationData child;
+ krb5_error_code ret;
+ int pos;
+
+ if (ad == NULL || ad->len == 0)
+ return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
+
+ pos = ad->len - 1;
+
+ if (ad->val[pos].ad_type != KRB5_AUTHDATA_IF_RELEVANT)
+ return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
+
+ ret = decode_AuthorizationData(ad->val[pos].ad_data.data,
+ ad->val[pos].ad_data.length,
+ &child,
+ NULL);
+ if (ret) {
+ krb5_set_error_message(context, ret, "Failed to decode "
+ "IF_RELEVANT with %d", ret);
+ return ret;
+ }
+
+ if (child.len != 1) {
+ free_AuthorizationData(&child);
+ return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
+ }
+
+ if (child.val[0].ad_type != KRB5_AUTHDATA_SIGNTICKET) {
+ free_AuthorizationData(&child);
+ return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
+ }
+
+ if (data)
+ ret = der_copy_octet_string(&child.val[0].ad_data, data);
+ free_AuthorizationData(&child);
+ return ret;
+}
+
+krb5_error_code
+_kdc_add_KRB5SignedPath(krb5_context context,
+ krb5_kdc_configuration *config,
+ hdb_entry_ex *krbtgt,
+ krb5_enctype enctype,
+ krb5_const_principal server,
+ KRB5SignedPathPrincipals *principals,
+ EncTicketPart *tkt)
+{
+ krb5_error_code ret;
+ KRB5SignedPath sp;
+ krb5_data data;
+ krb5_crypto crypto = NULL;
+ size_t size;
+
+ if (server && principals) {
+ ret = add_KRB5SignedPathPrincipals(principals, server);
+ if (ret)
+ return ret;
+ }
+
+ {
+ KRB5SignedPathData spd;
+
+ spd.encticket = *tkt;
+ spd.delegated = principals;
+
+ ASN1_MALLOC_ENCODE(KRB5SignedPathData, data.data, data.length,
+ &spd, &size, ret);
+ if (ret)
+ return ret;
+ if (data.length != size)
+ krb5_abortx(context, "internal asn.1 encoder error");
+ }
+
+ {
+ Key *key;
+ ret = hdb_enctype2key(context, &krbtgt->entry, enctype, &key);
+ if (ret == 0)
+ ret = krb5_crypto_init(context, &key->key, 0, &crypto);
+ if (ret) {
+ free(data.data);
+ return ret;
+ }
+ }
+
+ /*
+ * Fill in KRB5SignedPath
+ */
+
+ sp.etype = enctype;
+ sp.delegated = principals;
+
+ ret = krb5_create_checksum(context, crypto, KRB5_KU_KRB5SIGNEDPATH, 0,
+ data.data, data.length, &sp.cksum);
+ krb5_crypto_destroy(context, crypto);
+ free(data.data);
+ if (ret)
+ return ret;
+
+ ASN1_MALLOC_ENCODE(KRB5SignedPath, data.data, data.length, &sp, &size, ret);
+ free_Checksum(&sp.cksum);
+ if (ret)
+ return ret;
+ if (data.length != size)
+ krb5_abortx(context, "internal asn.1 encoder error");
+
+
+ /*
+ * Add IF-RELEVANT(KRB5SignedPath) to the last slot in
+ * authorization data field.
+ */
+
+ ret = _kdc_tkt_add_if_relevant_ad(context, tkt,
+ KRB5_AUTHDATA_SIGNTICKET, &data);
+ krb5_data_free(&data);
+
+ return ret;
+}
+
+static krb5_error_code
+check_KRB5SignedPath(krb5_context context,
+ krb5_kdc_configuration *config,
+ hdb_entry_ex *krbtgt,
+ EncTicketPart *tkt,
+ KRB5SignedPathPrincipals **delegated,
+ int *signedpath)
+{
+ krb5_error_code ret;
+ krb5_data data;
+ krb5_crypto crypto = NULL;
+
+ if (delegated)
+ *delegated = NULL;
+
+ ret = find_KRB5SignedPath(context, tkt->authorization_data, &data);
+ if (ret == 0) {
+ KRB5SignedPathData spd;
+ KRB5SignedPath sp;
+ AuthorizationData *ad;
+ size_t size;
+
+ ret = decode_KRB5SignedPath(data.data, data.length, &sp, NULL);
+ krb5_data_free(&data);
+ if (ret)
+ return ret;
+
+ spd.encticket = *tkt;
+ /* the KRB5SignedPath is the last entry */
+ ad = spd.encticket.authorization_data;
+ if (--ad->len == 0)
+ spd.encticket.authorization_data = NULL;
+ spd.delegated = sp.delegated;
+
+ ASN1_MALLOC_ENCODE(KRB5SignedPathData, data.data, data.length,
+ &spd, &size, ret);
+ ad->len++;
+ spd.encticket.authorization_data = ad;
+ if (ret) {
+ free_KRB5SignedPath(&sp);
+ return ret;
+ }
+ if (data.length != size)
+ krb5_abortx(context, "internal asn.1 encoder error");
+
+ {
+ Key *key;
+ ret = hdb_enctype2key(context, &krbtgt->entry, sp.etype, &key);
+ if (ret == 0)
+ ret = krb5_crypto_init(context, &key->key, 0, &crypto);
+ if (ret) {
+ free(data.data);
+ free_KRB5SignedPath(&sp);
+ return ret;
+ }
+ }
+ ret = krb5_verify_checksum(context, crypto, KRB5_KU_KRB5SIGNEDPATH,
+ data.data, data.length,
+ &sp.cksum);
+ krb5_crypto_destroy(context, crypto);
+ free(data.data);
+ if (ret) {
+ free_KRB5SignedPath(&sp);
+ return ret;
+ }
+
+ if (delegated && sp.delegated) {
+
+ *delegated = malloc(sizeof(*sp.delegated));
+ if (*delegated == NULL) {
+ free_KRB5SignedPath(&sp);
+ return ENOMEM;
+ }
+
+ ret = copy_KRB5SignedPathPrincipals(*delegated, sp.delegated);
+ if (ret) {
+ free_KRB5SignedPath(&sp);
+ free(*delegated);
+ *delegated = NULL;
+ return ret;
+ }
+ }
+ free_KRB5SignedPath(&sp);
+
+ *signedpath = 1;
+ }
+
+ return 0;
+}
+
+/*
+ *
+ */
+
+static krb5_error_code
+check_PAC(krb5_context context,
+ krb5_kdc_configuration *config,
+ const krb5_principal client_principal,
+ hdb_entry_ex *client,
+ hdb_entry_ex *server,
+ const EncryptionKey *server_key,
+ const EncryptionKey *krbtgt_key,
+ EncTicketPart *tkt,
+ krb5_data *rspac,
+ int *signedpath)
+{
+ AuthorizationData *ad = tkt->authorization_data;
+ unsigned i, j;
+ krb5_error_code ret;
+
+ if (ad == NULL || ad->len == 0)
+ return 0;
+
+ for (i = 0; i < ad->len; i++) {
+ AuthorizationData child;
+
+ if (ad->val[i].ad_type != KRB5_AUTHDATA_IF_RELEVANT)
+ continue;
+
+ ret = decode_AuthorizationData(ad->val[i].ad_data.data,
+ ad->val[i].ad_data.length,
+ &child,
+ NULL);
+ if (ret) {
+ krb5_set_error_message(context, ret, "Failed to decode "
+ "IF_RELEVANT with %d", ret);
+ return ret;
+ }
+ for (j = 0; j < child.len; j++) {
+
+ if (child.val[j].ad_type == KRB5_AUTHDATA_WIN2K_PAC) {
+ krb5_pac pac;
+
+ /* Found PAC */
+ ret = krb5_pac_parse(context,
+ child.val[j].ad_data.data,
+ child.val[j].ad_data.length,
+ &pac);
+ free_AuthorizationData(&child);
+ if (ret)
+ return ret;
+
+ ret = krb5_pac_verify(context, pac, tkt->authtime,
+ client_principal,
+ krbtgt_key, NULL);
+ if (ret) {
+ krb5_pac_free(context, pac);
+ return ret;
+ }
+
+ ret = _kdc_pac_verify(context, client_principal,
+ client, server, &pac);
+ if (ret) {
+ krb5_pac_free(context, pac);
+ return ret;
+ }
+ *signedpath = 1;
+
+ ret = _krb5_pac_sign(context, pac, tkt->authtime,
+ client_principal,
+ server_key, krbtgt_key, rspac);
+
+ krb5_pac_free(context, pac);
+
+ return ret;
+ }
+ }
+ free_AuthorizationData(&child);
+ }
+ return 0;
+}
+
+/*
+ *
+ */
+
+static krb5_error_code
+check_tgs_flags(krb5_context context,
+ krb5_kdc_configuration *config,
+ KDC_REQ_BODY *b, const EncTicketPart *tgt, EncTicketPart *et)
+{
+ KDCOptions f = b->kdc_options;
+
+ if(f.validate){
+ if(!tgt->flags.invalid || tgt->starttime == NULL){
+ kdc_log(context, config, 0,
+ "Bad request to validate ticket");
+ return KRB5KDC_ERR_BADOPTION;
+ }
+ if(*tgt->starttime > kdc_time){
+ kdc_log(context, config, 0,
+ "Early request to validate ticket");
+ return KRB5KRB_AP_ERR_TKT_NYV;
+ }
+ /* XXX tkt = tgt */
+ et->flags.invalid = 0;
+ }else if(tgt->flags.invalid){
+ kdc_log(context, config, 0,
+ "Ticket-granting ticket has INVALID flag set");
+ return KRB5KRB_AP_ERR_TKT_INVALID;
+ }
+
+ if(f.forwardable){
+ if(!tgt->flags.forwardable){
+ kdc_log(context, config, 0,
+ "Bad request for forwardable ticket");
+ return KRB5KDC_ERR_BADOPTION;
+ }
+ et->flags.forwardable = 1;
+ }
+ if(f.forwarded){
+ if(!tgt->flags.forwardable){
+ kdc_log(context, config, 0,
+ "Request to forward non-forwardable ticket");
+ return KRB5KDC_ERR_BADOPTION;
+ }
+ et->flags.forwarded = 1;
+ et->caddr = b->addresses;
+ }
+ if(tgt->flags.forwarded)
+ et->flags.forwarded = 1;
+
+ if(f.proxiable){
+ if(!tgt->flags.proxiable){
+ kdc_log(context, config, 0,
+ "Bad request for proxiable ticket");
+ return KRB5KDC_ERR_BADOPTION;
+ }
+ et->flags.proxiable = 1;
+ }
+ if(f.proxy){
+ if(!tgt->flags.proxiable){
+ kdc_log(context, config, 0,
+ "Request to proxy non-proxiable ticket");
+ return KRB5KDC_ERR_BADOPTION;
+ }
+ et->flags.proxy = 1;
+ et->caddr = b->addresses;
+ }
+ if(tgt->flags.proxy)
+ et->flags.proxy = 1;
+
+ if(f.allow_postdate){
+ if(!tgt->flags.may_postdate){
+ kdc_log(context, config, 0,
+ "Bad request for post-datable ticket");
+ return KRB5KDC_ERR_BADOPTION;
+ }
+ et->flags.may_postdate = 1;
+ }
+ if(f.postdated){
+ if(!tgt->flags.may_postdate){
+ kdc_log(context, config, 0,
+ "Bad request for postdated ticket");
+ return KRB5KDC_ERR_BADOPTION;
+ }
+ if(b->from)
+ *et->starttime = *b->from;
+ et->flags.postdated = 1;
+ et->flags.invalid = 1;
+ }else if(b->from && *b->from > kdc_time + context->max_skew){
+ kdc_log(context, config, 0, "Ticket cannot be postdated");
+ return KRB5KDC_ERR_CANNOT_POSTDATE;
+ }
+
+ if(f.renewable){
+ if(!tgt->flags.renewable){
+ kdc_log(context, config, 0,
+ "Bad request for renewable ticket");
+ return KRB5KDC_ERR_BADOPTION;
+ }
+ et->flags.renewable = 1;
+ ALLOC(et->renew_till);
+ _kdc_fix_time(&b->rtime);
+ *et->renew_till = *b->rtime;
+ }
+ if(f.renew){
+ time_t old_life;
+ if(!tgt->flags.renewable || tgt->renew_till == NULL){
+ kdc_log(context, config, 0,
+ "Request to renew non-renewable ticket");
+ return KRB5KDC_ERR_BADOPTION;
+ }
+ old_life = tgt->endtime;
+ if(tgt->starttime)
+ old_life -= *tgt->starttime;
+ else
+ old_life -= tgt->authtime;
+ et->endtime = *et->starttime + old_life;
+ if (et->renew_till != NULL)
+ et->endtime = min(*et->renew_till, et->endtime);
+ }
+
+#if 0
+ /* checks for excess flags */
+ if(f.request_anonymous && !config->allow_anonymous){
+ kdc_log(context, config, 0,
+ "Request for anonymous ticket");
+ return KRB5KDC_ERR_BADOPTION;
+ }
+#endif
+ return 0;
+}
+
+/*
+ *
+ */
+
+static krb5_error_code
+check_constrained_delegation(krb5_context context,
+ krb5_kdc_configuration *config,
+ hdb_entry_ex *client,
+ krb5_const_principal server)
+{
+ const HDB_Ext_Constrained_delegation_acl *acl;
+ krb5_error_code ret;
+ int i;
+
+ ret = hdb_entry_get_ConstrainedDelegACL(&client->entry, &acl);
+ if (ret) {
+ krb5_clear_error_message(context);
+ return ret;
+ }
+
+ if (acl) {
+ for (i = 0; i < acl->len; i++) {
+ if (krb5_principal_compare(context, server, &acl->val[i]) == TRUE)
+ return 0;
+ }
+ }
+ kdc_log(context, config, 0,
+ "Bad request for constrained delegation");
+ return KRB5KDC_ERR_BADOPTION;
+}
+
+/*
+ *
+ */
+
+static krb5_error_code
+verify_flags (krb5_context context,
+ krb5_kdc_configuration *config,
+ const EncTicketPart *et,
+ const char *pstr)
+{
+ if(et->endtime < kdc_time){
+ kdc_log(context, config, 0, "Ticket expired (%s)", pstr);
+ return KRB5KRB_AP_ERR_TKT_EXPIRED;
+ }
+ if(et->flags.invalid){
+ kdc_log(context, config, 0, "Ticket not valid (%s)", pstr);
+ return KRB5KRB_AP_ERR_TKT_NYV;
+ }
+ return 0;
+}
+
+/*
+ *
+ */
+
+static krb5_error_code
+fix_transited_encoding(krb5_context context,
+ krb5_kdc_configuration *config,
+ krb5_boolean check_policy,
+ const TransitedEncoding *tr,
+ EncTicketPart *et,
+ const char *client_realm,
+ const char *server_realm,
+ const char *tgt_realm)
+{
+ krb5_error_code ret = 0;
+ char **realms, **tmp;
+ unsigned int num_realms;
+ int i;
+
+ switch (tr->tr_type) {
+ case DOMAIN_X500_COMPRESS:
+ break;
+ case 0:
+ /*
+ * Allow empty content of type 0 because that is was Microsoft
+ * generates in their TGT.
+ */
+ if (tr->contents.length == 0)
+ break;
+ kdc_log(context, config, 0,
+ "Transited type 0 with non empty content");
+ return KRB5KDC_ERR_TRTYPE_NOSUPP;
+ default:
+ kdc_log(context, config, 0,
+ "Unknown transited type: %u", tr->tr_type);
+ return KRB5KDC_ERR_TRTYPE_NOSUPP;
+ }
+
+ ret = krb5_domain_x500_decode(context,
+ tr->contents,
+ &realms,
+ &num_realms,
+ client_realm,
+ server_realm);
+ if(ret){
+ krb5_warn(context, ret,
+ "Decoding transited encoding");
+ return ret;
+ }
+ if(strcmp(client_realm, tgt_realm) && strcmp(server_realm, tgt_realm)) {
+ /* not us, so add the previous realm to transited set */
+ if (num_realms + 1 > UINT_MAX/sizeof(*realms)) {
+ ret = ERANGE;
+ goto free_realms;
+ }
+ tmp = realloc(realms, (num_realms + 1) * sizeof(*realms));
+ if(tmp == NULL){
+ ret = ENOMEM;
+ goto free_realms;
+ }
+ realms = tmp;
+ realms[num_realms] = strdup(tgt_realm);
+ if(realms[num_realms] == NULL){
+ ret = ENOMEM;
+ goto free_realms;
+ }
+ num_realms++;
+ }
+ if(num_realms == 0) {
+ if(strcmp(client_realm, server_realm))
+ kdc_log(context, config, 0,
+ "cross-realm %s -> %s", client_realm, server_realm);
+ } else {
+ size_t l = 0;
+ char *rs;
+ for(i = 0; i < num_realms; i++)
+ l += strlen(realms[i]) + 2;
+ rs = malloc(l);
+ if(rs != NULL) {
+ *rs = '\0';
+ for(i = 0; i < num_realms; i++) {
+ if(i > 0)
+ strlcat(rs, ", ", l);
+ strlcat(rs, realms[i], l);
+ }
+ kdc_log(context, config, 0,
+ "cross-realm %s -> %s via [%s]",
+ client_realm, server_realm, rs);
+ free(rs);
+ }
+ }
+ if(check_policy) {
+ ret = krb5_check_transited(context, client_realm,
+ server_realm,
+ realms, num_realms, NULL);
+ if(ret) {
+ krb5_warn(context, ret, "cross-realm %s -> %s",
+ client_realm, server_realm);
+ goto free_realms;
+ }
+ et->flags.transited_policy_checked = 1;
+ }
+ et->transited.tr_type = DOMAIN_X500_COMPRESS;
+ ret = krb5_domain_x500_encode(realms, num_realms, &et->transited.contents);
+ if(ret)
+ krb5_warn(context, ret, "Encoding transited encoding");
+ free_realms:
+ for(i = 0; i < num_realms; i++)
+ free(realms[i]);
+ free(realms);
+ return ret;
+}
+
+
+static krb5_error_code
+tgs_make_reply(krb5_context context,
+ krb5_kdc_configuration *config,
+ KDC_REQ_BODY *b,
+ krb5_const_principal tgt_name,
+ const EncTicketPart *tgt,
+ const EncryptionKey *serverkey,
+ const krb5_keyblock *sessionkey,
+ krb5_kvno kvno,
+ AuthorizationData *auth_data,
+ hdb_entry_ex *server,
+ krb5_principal server_principal,
+ const char *server_name,
+ hdb_entry_ex *client,
+ krb5_principal client_principal,
+ hdb_entry_ex *krbtgt,
+ krb5_enctype krbtgt_etype,
+ KRB5SignedPathPrincipals *spp,
+ const krb5_data *rspac,
+ const METHOD_DATA *enc_pa_data,
+ const char **e_text,
+ krb5_data *reply)
+{
+ KDC_REP rep;
+ EncKDCRepPart ek;
+ EncTicketPart et;
+ KDCOptions f = b->kdc_options;
+ krb5_error_code ret;
+ int is_weak = 0;
+
+ memset(&rep, 0, sizeof(rep));
+ memset(&et, 0, sizeof(et));
+ memset(&ek, 0, sizeof(ek));
+
+ rep.pvno = 5;
+ rep.msg_type = krb_tgs_rep;
+
+ et.authtime = tgt->authtime;
+ _kdc_fix_time(&b->till);
+ et.endtime = min(tgt->endtime, *b->till);
+ ALLOC(et.starttime);
+ *et.starttime = kdc_time;
+
+ ret = check_tgs_flags(context, config, b, tgt, &et);
+ if(ret)
+ goto out;
+
+ /* We should check the transited encoding if:
+ 1) the request doesn't ask not to be checked
+ 2) globally enforcing a check
+ 3) principal requires checking
+ 4) we allow non-check per-principal, but principal isn't marked as allowing this
+ 5) we don't globally allow this
+ */
+
+#define GLOBAL_FORCE_TRANSITED_CHECK \
+ (config->trpolicy == TRPOLICY_ALWAYS_CHECK)
+#define GLOBAL_ALLOW_PER_PRINCIPAL \
+ (config->trpolicy == TRPOLICY_ALLOW_PER_PRINCIPAL)
+#define GLOBAL_ALLOW_DISABLE_TRANSITED_CHECK \
+ (config->trpolicy == TRPOLICY_ALWAYS_HONOUR_REQUEST)
+
+/* these will consult the database in future release */
+#define PRINCIPAL_FORCE_TRANSITED_CHECK(P) 0
+#define PRINCIPAL_ALLOW_DISABLE_TRANSITED_CHECK(P) 0
+
+ ret = fix_transited_encoding(context, config,
+ !f.disable_transited_check ||
+ GLOBAL_FORCE_TRANSITED_CHECK ||
+ PRINCIPAL_FORCE_TRANSITED_CHECK(server) ||
+ !((GLOBAL_ALLOW_PER_PRINCIPAL &&
+ PRINCIPAL_ALLOW_DISABLE_TRANSITED_CHECK(server)) ||
+ GLOBAL_ALLOW_DISABLE_TRANSITED_CHECK),
+ &tgt->transited, &et,
+ *krb5_princ_realm(context, client_principal),
+ *krb5_princ_realm(context, server->entry.principal),
+ *krb5_princ_realm(context, krbtgt->entry.principal));
+ if(ret)
+ goto out;
+
+ copy_Realm(krb5_princ_realm(context, server_principal),
+ &rep.ticket.realm);
+ _krb5_principal2principalname(&rep.ticket.sname, server_principal);
+ copy_Realm(&tgt_name->realm, &rep.crealm);
+/*
+ if (f.request_anonymous)
+ _kdc_make_anonymous_principalname (&rep.cname);
+ else */
+
+ copy_PrincipalName(&tgt_name->name, &rep.cname);
+ rep.ticket.tkt_vno = 5;
+
+ ek.caddr = et.caddr;
+ if(et.caddr == NULL)
+ et.caddr = tgt->caddr;
+
+ {
+ time_t life;
+ life = et.endtime - *et.starttime;
+ if(client && client->entry.max_life)
+ life = min(life, *client->entry.max_life);
+ if(server->entry.max_life)
+ life = min(life, *server->entry.max_life);
+ et.endtime = *et.starttime + life;
+ }
+ if(f.renewable_ok && tgt->flags.renewable &&
+ et.renew_till == NULL && et.endtime < *b->till){
+ et.flags.renewable = 1;
+ ALLOC(et.renew_till);
+ *et.renew_till = *b->till;
+ }
+ if(et.renew_till){
+ time_t renew;
+ renew = *et.renew_till - et.authtime;
+ if(client && client->entry.max_renew)
+ renew = min(renew, *client->entry.max_renew);
+ if(server->entry.max_renew)
+ renew = min(renew, *server->entry.max_renew);
+ *et.renew_till = et.authtime + renew;
+ }
+
+ if(et.renew_till){
+ *et.renew_till = min(*et.renew_till, *tgt->renew_till);
+ *et.starttime = min(*et.starttime, *et.renew_till);
+ et.endtime = min(et.endtime, *et.renew_till);
+ }
+
+ *et.starttime = min(*et.starttime, et.endtime);
+
+ if(*et.starttime == et.endtime){
+ ret = KRB5KDC_ERR_NEVER_VALID;
+ goto out;
+ }
+ if(et.renew_till && et.endtime == *et.renew_till){
+ free(et.renew_till);
+ et.renew_till = NULL;
+ et.flags.renewable = 0;
+ }
+
+ et.flags.pre_authent = tgt->flags.pre_authent;
+ et.flags.hw_authent = tgt->flags.hw_authent;
+ et.flags.anonymous = tgt->flags.anonymous;
+ et.flags.ok_as_delegate = server->entry.flags.ok_as_delegate;
+
+ if (auth_data) {
+ /* XXX Check enc-authorization-data */
+ et.authorization_data = calloc(1, sizeof(*et.authorization_data));
+ if (et.authorization_data == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+ ret = copy_AuthorizationData(auth_data, et.authorization_data);
+ if (ret)
+ goto out;
+
+ /* Filter out type KRB5SignedPath */
+ ret = find_KRB5SignedPath(context, et.authorization_data, NULL);
+ if (ret == 0) {
+ if (et.authorization_data->len == 1) {
+ free_AuthorizationData(et.authorization_data);
+ free(et.authorization_data);
+ et.authorization_data = NULL;
+ } else {
+ AuthorizationData *ad = et.authorization_data;
+ free_AuthorizationDataElement(&ad->val[ad->len - 1]);
+ ad->len--;
+ }
+ }
+ }
+
+ if(rspac->length) {
+ /*
+ * No not need to filter out the any PAC from the
+ * auth_data since it's signed by the KDC.
+ */
+ ret = _kdc_tkt_add_if_relevant_ad(context, &et,
+ KRB5_AUTHDATA_WIN2K_PAC,
+ rspac);
+ if (ret)
+ goto out;
+ }
+
+ ret = krb5_copy_keyblock_contents(context, sessionkey, &et.key);
+ if (ret)
+ goto out;
+ et.crealm = tgt->crealm;
+ et.cname = tgt_name->name;
+
+ ek.key = et.key;
+ /* MIT must have at least one last_req */
+ ek.last_req.len = 1;
+ ek.last_req.val = calloc(1, sizeof(*ek.last_req.val));
+ if (ek.last_req.val == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+ ek.nonce = b->nonce;
+ ek.flags = et.flags;
+ ek.authtime = et.authtime;
+ ek.starttime = et.starttime;
+ ek.endtime = et.endtime;
+ ek.renew_till = et.renew_till;
+ ek.srealm = rep.ticket.realm;
+ ek.sname = rep.ticket.sname;
+
+ _kdc_log_timestamp(context, config, "TGS-REQ", et.authtime, et.starttime,
+ et.endtime, et.renew_till);
+
+ /* Don't sign cross realm tickets, they can't be checked anyway */
+ {
+ char *r = get_krbtgt_realm(&ek.sname);
+
+ if (r == NULL || strcmp(r, ek.srealm) == 0) {
+ ret = _kdc_add_KRB5SignedPath(context,
+ config,
+ krbtgt,
+ krbtgt_etype,
+ NULL,
+ spp,
+ &et);
+ if (ret)
+ goto out;
+ }
+ }
+
+ if (enc_pa_data->len) {
+ rep.padata = calloc(1, sizeof(*rep.padata));
+ if (rep.padata == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+ ret = copy_METHOD_DATA(enc_pa_data, rep.padata);
+ if (ret)
+ goto out;
+ }
+
+ if (krb5_enctype_valid(context, et.key.keytype) != 0
+ && _kdc_is_weak_expection(server->entry.principal, et.key.keytype))
+ {
+ krb5_enctype_enable(context, et.key.keytype);
+ is_weak = 1;
+ }
+
+
+ /* It is somewhat unclear where the etype in the following
+ encryption should come from. What we have is a session
+ key in the passed tgt, and a list of preferred etypes
+ *for the new ticket*. Should we pick the best possible
+ etype, given the keytype in the tgt, or should we look
+ at the etype list here as well? What if the tgt
+ session key is DES3 and we want a ticket with a (say)
+ CAST session key. Should the DES3 etype be added to the
+ etype list, even if we don't want a session key with
+ DES3? */
+ ret = _kdc_encode_reply(context, config,
+ &rep, &et, &ek, et.key.keytype,
+ kvno,
+ serverkey, 0, &tgt->key, e_text, reply);
+ if (is_weak)
+ krb5_enctype_disable(context, et.key.keytype);
+
+out:
+ free_TGS_REP(&rep);
+ free_TransitedEncoding(&et.transited);
+ if(et.starttime)
+ free(et.starttime);
+ if(et.renew_till)
+ free(et.renew_till);
+ if(et.authorization_data) {
+ free_AuthorizationData(et.authorization_data);
+ free(et.authorization_data);
+ }
+ free_LastReq(&ek.last_req);
+ memset(et.key.keyvalue.data, 0, et.key.keyvalue.length);
+ free_EncryptionKey(&et.key);
+ return ret;
+}
+
+static krb5_error_code
+tgs_check_authenticator(krb5_context context,
+ krb5_kdc_configuration *config,
+ krb5_auth_context ac,
+ KDC_REQ_BODY *b,
+ const char **e_text,
+ krb5_keyblock *key)
+{
+ krb5_authenticator auth;
+ size_t len;
+ unsigned char *buf;
+ size_t buf_size;
+ krb5_error_code ret;
+ krb5_crypto crypto;
+
+ krb5_auth_con_getauthenticator(context, ac, &auth);
+ if(auth->cksum == NULL){
+ kdc_log(context, config, 0, "No authenticator in request");
+ ret = KRB5KRB_AP_ERR_INAPP_CKSUM;
+ goto out;
+ }
+ /*
+ * according to RFC1510 it doesn't need to be keyed,
+ * but according to the latest draft it needs to.
+ */
+ if (
+#if 0
+!krb5_checksum_is_keyed(context, auth->cksum->cksumtype)
+ ||
+#endif
+ !krb5_checksum_is_collision_proof(context, auth->cksum->cksumtype)) {
+ kdc_log(context, config, 0, "Bad checksum type in authenticator: %d",
+ auth->cksum->cksumtype);
+ ret = KRB5KRB_AP_ERR_INAPP_CKSUM;
+ goto out;
+ }
+
+ /* XXX should not re-encode this */
+ ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, b, &len, ret);
+ if(ret){
+ kdc_log(context, config, 0, "Failed to encode KDC-REQ-BODY: %s",
+ krb5_get_err_text(context, ret));
+ goto out;
+ }
+ if(buf_size != len) {
+ free(buf);
+ kdc_log(context, config, 0, "Internal error in ASN.1 encoder");
+ *e_text = "KDC internal error";
+ ret = KRB5KRB_ERR_GENERIC;
+ goto out;
+ }
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret) {
+ free(buf);
+ kdc_log(context, config, 0, "krb5_crypto_init failed: %s",
+ krb5_get_err_text(context, ret));
+ goto out;
+ }
+ ret = krb5_verify_checksum(context,
+ crypto,
+ KRB5_KU_TGS_REQ_AUTH_CKSUM,
+ buf,
+ len,
+ auth->cksum);
+ free(buf);
+ krb5_crypto_destroy(context, crypto);
+ if(ret){
+ kdc_log(context, config, 0,
+ "Failed to verify authenticator checksum: %s",
+ krb5_get_err_text(context, ret));
+ }
+out:
+ free_Authenticator(auth);
+ free(auth);
+ return ret;
+}
+
+/*
+ *
+ */
+
+static const char *
+find_rpath(krb5_context context, Realm crealm, Realm srealm)
+{
+ const char *new_realm = krb5_config_get_string(context,
+ NULL,
+ "capaths",
+ crealm,
+ srealm,
+ NULL);
+ return new_realm;
+}
+
+
+static krb5_boolean
+need_referral(krb5_context context, krb5_kdc_configuration *config,
+ const KDCOptions * const options, krb5_principal server,
+ krb5_realm **realms)
+{
+ const char *name;
+
+ if(!options->canonicalize && server->name.name_type != KRB5_NT_SRV_INST)
+ return FALSE;
+
+ if (server->name.name_string.len == 1)
+ name = server->name.name_string.val[0];
+ if (server->name.name_string.len > 1)
+ name = server->name.name_string.val[1];
+ else
+ return FALSE;
+
+ kdc_log(context, config, 0, "Searching referral for %s", name);
+
+ return _krb5_get_host_realm_int(context, name, FALSE, realms) == 0;
+}
+
+static krb5_error_code
+tgs_parse_request(krb5_context context,
+ krb5_kdc_configuration *config,
+ KDC_REQ_BODY *b,
+ const PA_DATA *tgs_req,
+ hdb_entry_ex **krbtgt,
+ krb5_enctype *krbtgt_etype,
+ krb5_ticket **ticket,
+ const char **e_text,
+ const char *from,
+ const struct sockaddr *from_addr,
+ time_t **csec,
+ int **cusec,
+ AuthorizationData **auth_data)
+{
+ krb5_ap_req ap_req;
+ krb5_error_code ret;
+ krb5_principal princ;
+ krb5_auth_context ac = NULL;
+ krb5_flags ap_req_options;
+ krb5_flags verify_ap_req_flags;
+ krb5_crypto crypto;
+ Key *tkey;
+
+ *auth_data = NULL;
+ *csec = NULL;
+ *cusec = NULL;
+
+ memset(&ap_req, 0, sizeof(ap_req));
+ ret = krb5_decode_ap_req(context, &tgs_req->padata_value, &ap_req);
+ if(ret){
+ kdc_log(context, config, 0, "Failed to decode AP-REQ: %s",
+ krb5_get_err_text(context, ret));
+ goto out;
+ }
+
+ if(!get_krbtgt_realm(&ap_req.ticket.sname)){
+ /* XXX check for ticket.sname == req.sname */
+ kdc_log(context, config, 0, "PA-DATA is not a ticket-granting ticket");
+ ret = KRB5KDC_ERR_POLICY; /* ? */
+ goto out;
+ }
+
+ _krb5_principalname2krb5_principal(context,
+ &princ,
+ ap_req.ticket.sname,
+ ap_req.ticket.realm);
+
+ ret = _kdc_db_fetch(context, config, princ, HDB_F_GET_KRBTGT, NULL, krbtgt);
+
+ if(ret) {
+ char *p;
+ ret = krb5_unparse_name(context, princ, &p);
+ if (ret != 0)
+ p = "<unparse_name failed>";
+ krb5_free_principal(context, princ);
+ kdc_log(context, config, 0,
+ "Ticket-granting ticket not found in database: %s: %s",
+ p, krb5_get_err_text(context, ret));
+ if (ret == 0)
+ free(p);
+ ret = KRB5KRB_AP_ERR_NOT_US;
+ goto out;
+ }
+
+ if(ap_req.ticket.enc_part.kvno &&
+ *ap_req.ticket.enc_part.kvno != (*krbtgt)->entry.kvno){
+ char *p;
+
+ ret = krb5_unparse_name (context, princ, &p);
+ krb5_free_principal(context, princ);
+ if (ret != 0)
+ p = "<unparse_name failed>";
+ kdc_log(context, config, 0,
+ "Ticket kvno = %d, DB kvno = %d (%s)",
+ *ap_req.ticket.enc_part.kvno,
+ (*krbtgt)->entry.kvno,
+ p);
+ if (ret == 0)
+ free (p);
+ ret = KRB5KRB_AP_ERR_BADKEYVER;
+ goto out;
+ }
+
+ *krbtgt_etype = ap_req.ticket.enc_part.etype;
+
+ ret = hdb_enctype2key(context, &(*krbtgt)->entry,
+ ap_req.ticket.enc_part.etype, &tkey);
+ if(ret){
+ char *str = NULL, *p = NULL;
+
+ krb5_enctype_to_string(context, ap_req.ticket.enc_part.etype, &str);
+ krb5_unparse_name(context, princ, &p);
+ kdc_log(context, config, 0,
+ "No server key with enctype %s found for %s",
+ str ? str : "<unknown enctype>",
+ p ? p : "<unparse_name failed>");
+ free(str);
+ free(p);
+ ret = KRB5KRB_AP_ERR_BADKEYVER;
+ goto out;
+ }
+
+ if (b->kdc_options.validate)
+ verify_ap_req_flags = KRB5_VERIFY_AP_REQ_IGNORE_INVALID;
+ else
+ verify_ap_req_flags = 0;
+
+ ret = krb5_verify_ap_req2(context,
+ &ac,
+ &ap_req,
+ princ,
+ &tkey->key,
+ verify_ap_req_flags,
+ &ap_req_options,
+ ticket,
+ KRB5_KU_TGS_REQ_AUTH);
+
+ krb5_free_principal(context, princ);
+ if(ret) {
+ kdc_log(context, config, 0, "Failed to verify AP-REQ: %s",
+ krb5_get_err_text(context, ret));
+ goto out;
+ }
+
+ {
+ krb5_authenticator auth;
+
+ ret = krb5_auth_con_getauthenticator(context, ac, &auth);
+ if (ret == 0) {
+ *csec = malloc(sizeof(**csec));
+ if (*csec == NULL) {
+ krb5_free_authenticator(context, &auth);
+ kdc_log(context, config, 0, "malloc failed");
+ goto out;
+ }
+ **csec = auth->ctime;
+ *cusec = malloc(sizeof(**cusec));
+ if (*cusec == NULL) {
+ krb5_free_authenticator(context, &auth);
+ kdc_log(context, config, 0, "malloc failed");
+ goto out;
+ }
+ **cusec = auth->cusec;
+ krb5_free_authenticator(context, &auth);
+ }
+ }
+
+ ret = tgs_check_authenticator(context, config,
+ ac, b, e_text, &(*ticket)->ticket.key);
+ if (ret) {
+ krb5_auth_con_free(context, ac);
+ goto out;
+ }
+
+ if (b->enc_authorization_data) {
+ unsigned usage = KRB5_KU_TGS_REQ_AUTH_DAT_SUBKEY;
+ krb5_keyblock *subkey;
+ krb5_data ad;
+
+ ret = krb5_auth_con_getremotesubkey(context,
+ ac,
+ &subkey);
+ if(ret){
+ krb5_auth_con_free(context, ac);
+ kdc_log(context, config, 0, "Failed to get remote subkey: %s",
+ krb5_get_err_text(context, ret));
+ goto out;
+ }
+ if(subkey == NULL){
+ usage = KRB5_KU_TGS_REQ_AUTH_DAT_SESSION;
+ ret = krb5_auth_con_getkey(context, ac, &subkey);
+ if(ret) {
+ krb5_auth_con_free(context, ac);
+ kdc_log(context, config, 0, "Failed to get session key: %s",
+ krb5_get_err_text(context, ret));
+ goto out;
+ }
+ }
+ if(subkey == NULL){
+ krb5_auth_con_free(context, ac);
+ kdc_log(context, config, 0,
+ "Failed to get key for enc-authorization-data");
+ ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
+ goto out;
+ }
+ ret = krb5_crypto_init(context, subkey, 0, &crypto);
+ if (ret) {
+ krb5_auth_con_free(context, ac);
+ kdc_log(context, config, 0, "krb5_crypto_init failed: %s",
+ krb5_get_err_text(context, ret));
+ goto out;
+ }
+ ret = krb5_decrypt_EncryptedData (context,
+ crypto,
+ usage,
+ b->enc_authorization_data,
+ &ad);
+ krb5_crypto_destroy(context, crypto);
+ if(ret){
+ krb5_auth_con_free(context, ac);
+ kdc_log(context, config, 0,
+ "Failed to decrypt enc-authorization-data");
+ ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
+ goto out;
+ }
+ krb5_free_keyblock(context, subkey);
+ ALLOC(*auth_data);
+ if (*auth_data == NULL) {
+ krb5_auth_con_free(context, ac);
+ ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
+ goto out;
+ }
+ ret = decode_AuthorizationData(ad.data, ad.length, *auth_data, NULL);
+ if(ret){
+ krb5_auth_con_free(context, ac);
+ free(*auth_data);
+ *auth_data = NULL;
+ kdc_log(context, config, 0, "Failed to decode authorization data");
+ ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
+ goto out;
+ }
+ }
+
+ krb5_auth_con_free(context, ac);
+
+out:
+ free_AP_REQ(&ap_req);
+
+ return ret;
+}
+
+static krb5_error_code
+build_server_referral(krb5_context context,
+ krb5_kdc_configuration *config,
+ krb5_crypto session,
+ krb5_const_realm referred_realm,
+ const PrincipalName *true_principal_name,
+ const PrincipalName *requested_principal,
+ krb5_data *outdata)
+{
+ PA_ServerReferralData ref;
+ krb5_error_code ret;
+ EncryptedData ed;
+ krb5_data data;
+ size_t size;
+
+ memset(&ref, 0, sizeof(ref));
+
+ if (referred_realm) {
+ ALLOC(ref.referred_realm);
+ if (ref.referred_realm == NULL)
+ goto eout;
+ *ref.referred_realm = strdup(referred_realm);
+ if (*ref.referred_realm == NULL)
+ goto eout;
+ }
+ if (true_principal_name) {
+ ALLOC(ref.true_principal_name);
+ if (ref.true_principal_name == NULL)
+ goto eout;
+ ret = copy_PrincipalName(true_principal_name, ref.true_principal_name);
+ if (ret)
+ goto eout;
+ }
+ if (requested_principal) {
+ ALLOC(ref.requested_principal_name);
+ if (ref.requested_principal_name == NULL)
+ goto eout;
+ ret = copy_PrincipalName(requested_principal,
+ ref.requested_principal_name);
+ if (ret)
+ goto eout;
+ }
+
+ ASN1_MALLOC_ENCODE(PA_ServerReferralData,
+ data.data, data.length,
+ &ref, &size, ret);
+ free_PA_ServerReferralData(&ref);
+ if (ret)
+ return ret;
+ if (data.length != size)
+ krb5_abortx(context, "internal asn.1 encoder error");
+
+ ret = krb5_encrypt_EncryptedData(context, session,
+ KRB5_KU_PA_SERVER_REFERRAL,
+ data.data, data.length,
+ 0 /* kvno */, &ed);
+ free(data.data);
+ if (ret)
+ return ret;
+
+ ASN1_MALLOC_ENCODE(EncryptedData,
+ outdata->data, outdata->length,
+ &ed, &size, ret);
+ free_EncryptedData(&ed);
+ if (ret)
+ return ret;
+ if (outdata->length != size)
+ krb5_abortx(context, "internal asn.1 encoder error");
+
+ return 0;
+eout:
+ free_PA_ServerReferralData(&ref);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+}
+
+static krb5_error_code
+tgs_build_reply(krb5_context context,
+ krb5_kdc_configuration *config,
+ KDC_REQ *req,
+ KDC_REQ_BODY *b,
+ hdb_entry_ex *krbtgt,
+ krb5_enctype krbtgt_etype,
+ krb5_ticket *ticket,
+ krb5_data *reply,
+ const char *from,
+ const char **e_text,
+ AuthorizationData **auth_data,
+ const struct sockaddr *from_addr,
+ int datagram_reply)
+{
+ krb5_error_code ret;
+ krb5_principal cp = NULL, sp = NULL;
+ krb5_principal client_principal = NULL;
+ char *spn = NULL, *cpn = NULL;
+ hdb_entry_ex *server = NULL, *client = NULL;
+ krb5_realm ref_realm = NULL;
+ EncTicketPart *tgt = &ticket->ticket;
+ KRB5SignedPathPrincipals *spp = NULL;
+ Key *tkey;
+ const EncryptionKey *ekey;
+ krb5_keyblock sessionkey;
+ krb5_kvno kvno;
+ krb5_data rspac;
+ int cross_realm = 0;
+
+ METHOD_DATA enc_pa_data;
+
+ PrincipalName *s;
+ Realm r;
+ int nloop = 0;
+ EncTicketPart adtkt;
+ char opt_str[128];
+ int signedpath = 0;
+
+ memset(&sessionkey, 0, sizeof(sessionkey));
+ memset(&adtkt, 0, sizeof(adtkt));
+ krb5_data_zero(&rspac);
+ memset(&enc_pa_data, 0, sizeof(enc_pa_data));
+
+ s = b->sname;
+ r = b->realm;
+
+ if(b->kdc_options.enc_tkt_in_skey){
+ Ticket *t;
+ hdb_entry_ex *uu;
+ krb5_principal p;
+ Key *uukey;
+
+ if(b->additional_tickets == NULL ||
+ b->additional_tickets->len == 0){
+ ret = KRB5KDC_ERR_BADOPTION; /* ? */
+ kdc_log(context, config, 0,
+ "No second ticket present in request");
+ goto out;
+ }
+ t = &b->additional_tickets->val[0];
+ if(!get_krbtgt_realm(&t->sname)){
+ kdc_log(context, config, 0,
+ "Additional ticket is not a ticket-granting ticket");
+ ret = KRB5KDC_ERR_POLICY;
+ goto out;
+ }
+ _krb5_principalname2krb5_principal(context, &p, t->sname, t->realm);
+ ret = _kdc_db_fetch(context, config, p,
+ HDB_F_GET_CLIENT|HDB_F_GET_SERVER,
+ NULL, &uu);
+ krb5_free_principal(context, p);
+ if(ret){
+ if (ret == HDB_ERR_NOENTRY)
+ ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
+ goto out;
+ }
+ ret = hdb_enctype2key(context, &uu->entry,
+ t->enc_part.etype, &uukey);
+ if(ret){
+ _kdc_free_ent(context, uu);
+ ret = KRB5KDC_ERR_ETYPE_NOSUPP; /* XXX */
+ goto out;
+ }
+ ret = krb5_decrypt_ticket(context, t, &uukey->key, &adtkt, 0);
+ _kdc_free_ent(context, uu);
+ if(ret)
+ goto out;
+
+ ret = verify_flags(context, config, &adtkt, spn);
+ if (ret)
+ goto out;
+
+ s = &adtkt.cname;
+ r = adtkt.crealm;
+ }
+
+ _krb5_principalname2krb5_principal(context, &sp, *s, r);
+ ret = krb5_unparse_name(context, sp, &spn);
+ if (ret)
+ goto out;
+ _krb5_principalname2krb5_principal(context, &cp, tgt->cname, tgt->crealm);
+ ret = krb5_unparse_name(context, cp, &cpn);
+ if (ret)
+ goto out;
+ unparse_flags (KDCOptions2int(b->kdc_options),
+ asn1_KDCOptions_units(),
+ opt_str, sizeof(opt_str));
+ if(*opt_str)
+ kdc_log(context, config, 0,
+ "TGS-REQ %s from %s for %s [%s]",
+ cpn, from, spn, opt_str);
+ else
+ kdc_log(context, config, 0,
+ "TGS-REQ %s from %s for %s", cpn, from, spn);
+
+ /*
+ * Fetch server
+ */
+
+server_lookup:
+ ret = _kdc_db_fetch(context, config, sp, HDB_F_GET_SERVER | HDB_F_CANON,
+ NULL, &server);
+
+ if(ret){
+ const char *new_rlm;
+ Realm req_rlm;
+ krb5_realm *realms;
+
+ if ((req_rlm = get_krbtgt_realm(&sp->name)) != NULL) {
+ if(nloop++ < 2) {
+ new_rlm = find_rpath(context, tgt->crealm, req_rlm);
+ if(new_rlm) {
+ kdc_log(context, config, 5, "krbtgt for realm %s "
+ "not found, trying %s",
+ req_rlm, new_rlm);
+ krb5_free_principal(context, sp);
+ free(spn);
+ krb5_make_principal(context, &sp, r,
+ KRB5_TGS_NAME, new_rlm, NULL);
+ ret = krb5_unparse_name(context, sp, &spn);
+ if (ret)
+ goto out;
+
+ if (ref_realm)
+ free(ref_realm);
+ ref_realm = strdup(new_rlm);
+ goto server_lookup;
+ }
+ }
+ } else if(need_referral(context, config, &b->kdc_options, sp, &realms)) {
+ if (strcmp(realms[0], sp->realm) != 0) {
+ kdc_log(context, config, 5,
+ "Returning a referral to realm %s for "
+ "server %s that was not found",
+ realms[0], spn);
+ krb5_free_principal(context, sp);
+ free(spn);
+ krb5_make_principal(context, &sp, r, KRB5_TGS_NAME,
+ realms[0], NULL);
+ ret = krb5_unparse_name(context, sp, &spn);
+ if (ret)
+ goto out;
+
+ if (ref_realm)
+ free(ref_realm);
+ ref_realm = strdup(realms[0]);
+
+ krb5_free_host_realm(context, realms);
+ goto server_lookup;
+ }
+ krb5_free_host_realm(context, realms);
+ }
+ kdc_log(context, config, 0,
+ "Server not found in database: %s: %s", spn,
+ krb5_get_err_text(context, ret));
+ if (ret == HDB_ERR_NOENTRY)
+ ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
+ goto out;
+ }
+
+ ret = _kdc_db_fetch(context, config, cp, HDB_F_GET_CLIENT | HDB_F_CANON,
+ NULL, &client);
+ if(ret) {
+ const char *krbtgt_realm;
+
+ /*
+ * If the client belongs to the same realm as our krbtgt, it
+ * should exist in the local database.
+ *
+ */
+
+ krbtgt_realm =
+ krb5_principal_get_comp_string(context,
+ krbtgt->entry.principal, 1);
+
+ if(strcmp(krb5_principal_get_realm(context, cp), krbtgt_realm) == 0) {
+ if (ret == HDB_ERR_NOENTRY)
+ ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
+ kdc_log(context, config, 1, "Client no longer in database: %s",
+ cpn);
+ goto out;
+ }
+
+ kdc_log(context, config, 1, "Client not found in database: %s: %s",
+ cpn, krb5_get_err_text(context, ret));
+
+ cross_realm = 1;
+ }
+
+ /*
+ * Select enctype, return key and kvno.
+ */
+
+ {
+ krb5_enctype etype;
+
+ if(b->kdc_options.enc_tkt_in_skey) {
+ int i;
+ ekey = &adtkt.key;
+ for(i = 0; i < b->etype.len; i++)
+ if (b->etype.val[i] == adtkt.key.keytype)
+ break;
+ if(i == b->etype.len) {
+ kdc_log(context, config, 0,
+ "Addition ticket have not matching etypes", spp);
+ krb5_clear_error_message(context);
+ return KRB5KDC_ERR_ETYPE_NOSUPP;
+ }
+ etype = b->etype.val[i];
+ kvno = 0;
+ } else {
+ Key *skey;
+
+ ret = _kdc_find_etype(context, server, b->etype.val, b->etype.len,
+ &skey, &etype);
+ if(ret) {
+ kdc_log(context, config, 0,
+ "Server (%s) has no support for etypes", spn);
+ return ret;
+ }
+ ekey = &skey->key;
+ kvno = server->entry.kvno;
+ }
+
+ ret = krb5_generate_random_keyblock(context, etype, &sessionkey);
+ if (ret)
+ goto out;
+ }
+
+ /*
+ * Validate authoriation data
+ */
+
+ /*
+ * Check that service is in the same realm as the krbtgt. If it's
+ * not the same, it's someone that is using a uni-directional trust
+ * backward.
+ */
+
+ if (strcmp(krb5_principal_get_realm(context, sp),
+ krb5_principal_get_comp_string(context,
+ krbtgt->entry.principal,
+ 1)) != 0) {
+ char *tpn;
+ ret = krb5_unparse_name(context, krbtgt->entry.principal, &tpn);
+ kdc_log(context, config, 0,
+ "Request with wrong krbtgt: %s",
+ (ret == 0) ? tpn : "<unknown>");
+ if(ret == 0)
+ free(tpn);
+ ret = KRB5KRB_AP_ERR_NOT_US;
+ goto out;
+ }
+
+ /* check PAC if there is one */
+
+ ret = hdb_enctype2key(context, &krbtgt->entry,
+ krbtgt_etype, &tkey);
+ if(ret) {
+ kdc_log(context, config, 0,
+ "Failed to find key for krbtgt PAC check");
+ goto out;
+ }
+
+ ret = check_PAC(context, config, cp,
+ client, server, ekey, &tkey->key,
+ tgt, &rspac, &signedpath);
+ if (ret) {
+ kdc_log(context, config, 0,
+ "Verify PAC failed for %s (%s) from %s with %s",
+ spn, cpn, from, krb5_get_err_text(context, ret));
+ goto out;
+ }
+
+ /* also check the krbtgt for signature */
+ ret = check_KRB5SignedPath(context,
+ config,
+ krbtgt,
+ tgt,
+ &spp,
+ &signedpath);
+ if (ret) {
+ kdc_log(context, config, 0,
+ "KRB5SignedPath check failed for %s (%s) from %s with %s",
+ spn, cpn, from, krb5_get_err_text(context, ret));
+ goto out;
+ }
+
+ /*
+ * Process request
+ */
+
+ client_principal = cp;
+
+ if (client) {
+ const PA_DATA *sdata;
+ int i = 0;
+
+ sdata = _kdc_find_padata(req, &i, KRB5_PADATA_S4U2SELF);
+ if (sdata) {
+ krb5_crypto crypto;
+ krb5_data datack;
+ PA_S4U2Self self;
+ char *selfcpn = NULL;
+ const char *str;
+
+ ret = decode_PA_S4U2Self(sdata->padata_value.data,
+ sdata->padata_value.length,
+ &self, NULL);
+ if (ret) {
+ kdc_log(context, config, 0, "Failed to decode PA-S4U2Self");
+ goto out;
+ }
+
+ ret = _krb5_s4u2self_to_checksumdata(context, &self, &datack);
+ if (ret)
+ goto out;
+
+ ret = krb5_crypto_init(context, &tgt->key, 0, &crypto);
+ if (ret) {
+ free_PA_S4U2Self(&self);
+ krb5_data_free(&datack);
+ kdc_log(context, config, 0, "krb5_crypto_init failed: %s",
+ krb5_get_err_text(context, ret));
+ goto out;
+ }
+
+ ret = krb5_verify_checksum(context,
+ crypto,
+ KRB5_KU_OTHER_CKSUM,
+ datack.data,
+ datack.length,
+ &self.cksum);
+ krb5_data_free(&datack);
+ krb5_crypto_destroy(context, crypto);
+ if (ret) {
+ free_PA_S4U2Self(&self);
+ kdc_log(context, config, 0,
+ "krb5_verify_checksum failed for S4U2Self: %s",
+ krb5_get_err_text(context, ret));
+ goto out;
+ }
+
+ ret = _krb5_principalname2krb5_principal(context,
+ &client_principal,
+ self.name,
+ self.realm);
+ free_PA_S4U2Self(&self);
+ if (ret)
+ goto out;
+
+ ret = krb5_unparse_name(context, client_principal, &selfcpn);
+ if (ret)
+ goto out;
+
+ /*
+ * Check that service doing the impersonating is
+ * requesting a ticket to it-self.
+ */
+ if (krb5_principal_compare(context, cp, sp) != TRUE) {
+ kdc_log(context, config, 0, "S4U2Self: %s is not allowed "
+ "to impersonate some other user "
+ "(tried for user %s to service %s)",
+ cpn, selfcpn, spn);
+ free(selfcpn);
+ ret = KRB5KDC_ERR_BADOPTION; /* ? */
+ goto out;
+ }
+
+ /*
+ * If the service isn't trusted for authentication to
+ * delegation, remove the forward flag.
+ */
+
+ if (client->entry.flags.trusted_for_delegation) {
+ str = "[forwardable]";
+ } else {
+ b->kdc_options.forwardable = 0;
+ str = "";
+ }
+ kdc_log(context, config, 0, "s4u2self %s impersonating %s to "
+ "service %s %s", cpn, selfcpn, spn, str);
+ free(selfcpn);
+ }
+ }
+
+ /*
+ * Constrained delegation
+ */
+
+ if (client != NULL
+ && b->additional_tickets != NULL
+ && b->additional_tickets->len != 0
+ && b->kdc_options.enc_tkt_in_skey == 0)
+ {
+ int ad_signedpath = 0;
+ Key *clientkey;
+ Ticket *t;
+ char *str;
+
+ /*
+ * Require that the KDC have issued the service's krbtgt (not
+ * self-issued ticket with kimpersonate(1).
+ */
+ if (!signedpath) {
+ ret = KRB5KDC_ERR_BADOPTION;
+ kdc_log(context, config, 0,
+ "Constrained delegation done on service ticket %s/%s",
+ cpn, spn);
+ goto out;
+ }
+
+ t = &b->additional_tickets->val[0];
+
+ ret = hdb_enctype2key(context, &client->entry,
+ t->enc_part.etype, &clientkey);
+ if(ret){
+ ret = KRB5KDC_ERR_ETYPE_NOSUPP; /* XXX */
+ goto out;
+ }
+
+ ret = krb5_decrypt_ticket(context, t, &clientkey->key, &adtkt, 0);
+ if (ret) {
+ kdc_log(context, config, 0,
+ "failed to decrypt ticket for "
+ "constrained delegation from %s to %s ", spn, cpn);
+ goto out;
+ }
+
+ /* check that ticket is valid */
+ if (adtkt.flags.forwardable == 0) {
+ kdc_log(context, config, 0,
+ "Missing forwardable flag on ticket for "
+ "constrained delegation from %s to %s ", spn, cpn);
+ ret = KRB5KDC_ERR_BADOPTION;
+ goto out;
+ }
+
+ ret = check_constrained_delegation(context, config, client, sp);
+ if (ret) {
+ kdc_log(context, config, 0,
+ "constrained delegation from %s to %s not allowed",
+ spn, cpn);
+ goto out;
+ }
+
+ ret = _krb5_principalname2krb5_principal(context,
+ &client_principal,
+ adtkt.cname,
+ adtkt.crealm);
+ if (ret)
+ goto out;
+
+ ret = krb5_unparse_name(context, client_principal, &str);
+ if (ret)
+ goto out;
+
+ ret = verify_flags(context, config, &adtkt, str);
+ if (ret) {
+ free(str);
+ goto out;
+ }
+
+ /*
+ * Check that the KDC issued the user's ticket.
+ */
+ ret = check_KRB5SignedPath(context,
+ config,
+ krbtgt,
+ &adtkt,
+ NULL,
+ &ad_signedpath);
+ if (ret == 0 && !ad_signedpath)
+ ret = KRB5KDC_ERR_BADOPTION;
+ if (ret) {
+ kdc_log(context, config, 0,
+ "KRB5SignedPath check from service %s failed "
+ "for delegation to %s for client %s "
+ "from %s failed with %s",
+ spn, str, cpn, from, krb5_get_err_text(context, ret));
+ free(str);
+ goto out;
+ }
+
+ kdc_log(context, config, 0, "constrained delegation for %s "
+ "from %s to %s", str, cpn, spn);
+ free(str);
+ }
+
+ /*
+ * Check flags
+ */
+
+ ret = _kdc_check_flags(context, config,
+ client, cpn,
+ server, spn,
+ FALSE);
+ if(ret)
+ goto out;
+
+ if((b->kdc_options.validate || b->kdc_options.renew) &&
+ !krb5_principal_compare(context,
+ krbtgt->entry.principal,
+ server->entry.principal)){
+ kdc_log(context, config, 0, "Inconsistent request.");
+ ret = KRB5KDC_ERR_SERVER_NOMATCH;
+ goto out;
+ }
+
+ /* check for valid set of addresses */
+ if(!_kdc_check_addresses(context, config, tgt->caddr, from_addr)) {
+ ret = KRB5KRB_AP_ERR_BADADDR;
+ kdc_log(context, config, 0, "Request from wrong address");
+ goto out;
+ }
+
+ /*
+ * If this is an referral, add server referral data to the
+ * auth_data reply .
+ */
+ if (ref_realm) {
+ PA_DATA pa;
+ krb5_crypto crypto;
+
+ kdc_log(context, config, 0,
+ "Adding server referral to %s", ref_realm);
+
+ ret = krb5_crypto_init(context, &sessionkey, 0, &crypto);
+ if (ret)
+ goto out;
+
+ ret = build_server_referral(context, config, crypto, ref_realm,
+ NULL, s, &pa.padata_value);
+ krb5_crypto_destroy(context, crypto);
+ if (ret) {
+ kdc_log(context, config, 0,
+ "Failed building server referral");
+ goto out;
+ }
+ pa.padata_type = KRB5_PADATA_SERVER_REFERRAL;
+
+ ret = add_METHOD_DATA(&enc_pa_data, &pa);
+ krb5_data_free(&pa.padata_value);
+ if (ret) {
+ kdc_log(context, config, 0,
+ "Add server referral METHOD-DATA failed");
+ goto out;
+ }
+ }
+
+ /*
+ *
+ */
+
+ ret = tgs_make_reply(context,
+ config,
+ b,
+ client_principal,
+ tgt,
+ ekey,
+ &sessionkey,
+ kvno,
+ *auth_data,
+ server,
+ sp,
+ spn,
+ client,
+ cp,
+ krbtgt,
+ krbtgt_etype,
+ spp,
+ &rspac,
+ &enc_pa_data,
+ e_text,
+ reply);
+
+out:
+ free(spn);
+ free(cpn);
+
+ krb5_data_free(&rspac);
+ krb5_free_keyblock_contents(context, &sessionkey);
+ if(server)
+ _kdc_free_ent(context, server);
+ if(client)
+ _kdc_free_ent(context, client);
+
+ if (client_principal && client_principal != cp)
+ krb5_free_principal(context, client_principal);
+ if (cp)
+ krb5_free_principal(context, cp);
+ if (sp)
+ krb5_free_principal(context, sp);
+ if (ref_realm)
+ free(ref_realm);
+ free_METHOD_DATA(&enc_pa_data);
+
+ free_EncTicketPart(&adtkt);
+
+ return ret;
+}
+
+/*
+ *
+ */
+
+krb5_error_code
+_kdc_tgs_rep(krb5_context context,
+ krb5_kdc_configuration *config,
+ KDC_REQ *req,
+ krb5_data *data,
+ const char *from,
+ struct sockaddr *from_addr,
+ int datagram_reply)
+{
+ AuthorizationData *auth_data = NULL;
+ krb5_error_code ret;
+ int i = 0;
+ const PA_DATA *tgs_req;
+
+ hdb_entry_ex *krbtgt = NULL;
+ krb5_ticket *ticket = NULL;
+ const char *e_text = NULL;
+ krb5_enctype krbtgt_etype = ETYPE_NULL;
+
+ time_t *csec = NULL;
+ int *cusec = NULL;
+
+ if(req->padata == NULL){
+ ret = KRB5KDC_ERR_PREAUTH_REQUIRED; /* XXX ??? */
+ kdc_log(context, config, 0,
+ "TGS-REQ from %s without PA-DATA", from);
+ goto out;
+ }
+
+ tgs_req = _kdc_find_padata(req, &i, KRB5_PADATA_TGS_REQ);
+
+ if(tgs_req == NULL){
+ ret = KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
+
+ kdc_log(context, config, 0,
+ "TGS-REQ from %s without PA-TGS-REQ", from);
+ goto out;
+ }
+ ret = tgs_parse_request(context, config,
+ &req->req_body, tgs_req,
+ &krbtgt,
+ &krbtgt_etype,
+ &ticket,
+ &e_text,
+ from, from_addr,
+ &csec, &cusec,
+ &auth_data);
+ if (ret) {
+ kdc_log(context, config, 0,
+ "Failed parsing TGS-REQ from %s", from);
+ goto out;
+ }
+
+ ret = tgs_build_reply(context,
+ config,
+ req,
+ &req->req_body,
+ krbtgt,
+ krbtgt_etype,
+ ticket,
+ data,
+ from,
+ &e_text,
+ &auth_data,
+ from_addr,
+ datagram_reply);
+ if (ret) {
+ kdc_log(context, config, 0,
+ "Failed building TGS-REP to %s", from);
+ goto out;
+ }
+
+ /* */
+ if (datagram_reply && data->length > config->max_datagram_reply_length) {
+ krb5_data_free(data);
+ ret = KRB5KRB_ERR_RESPONSE_TOO_BIG;
+ e_text = "Reply packet too large";
+ }
+
+out:
+ if(ret && data->data == NULL){
+ krb5_mk_error(context,
+ ret,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ csec,
+ cusec,
+ data);
+ }
+ free(csec);
+ free(cusec);
+ if (ticket)
+ krb5_free_ticket(context, ticket);
+ if(krbtgt)
+ _kdc_free_ent(context, krbtgt);
+
+ if (auth_data) {
+ free_AuthorizationData(auth_data);
+ free(auth_data);
+ }
+
+ return 0;
+}
diff --git a/source4/heimdal/kdc/kx509.c b/source4/heimdal/kdc/kx509.c
new file mode 100644
index 0000000000..83e05b81c5
--- /dev/null
+++ b/source4/heimdal/kdc/kx509.c
@@ -0,0 +1,460 @@
+/*
+ * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kdc_locl.h"
+#include <hex.h>
+#include <rfc2459_asn1.h>
+#include <hx509.h>
+
+RCSID("$Id$");
+
+/*
+ *
+ */
+
+krb5_error_code
+_kdc_try_kx509_request(void *ptr, size_t len, Kx509Request *req, size_t *size)
+{
+ if (len < 4)
+ return -1;
+ if (memcmp("\x00\x00\x02\x00", ptr, 4) != 0)
+ return -1;
+ return decode_Kx509Request(((unsigned char *)ptr) + 4, len - 4, req, size);
+}
+
+/*
+ *
+ */
+
+static const unsigned char version_2_0[4] = {0 , 0, 2, 0};
+
+static krb5_error_code
+verify_req_hash(krb5_context context,
+ const Kx509Request *req,
+ krb5_keyblock *key)
+{
+ unsigned char digest[SHA_DIGEST_LENGTH];
+ HMAC_CTX ctx;
+
+ if (req->pk_hash.length != sizeof(digest)) {
+ krb5_set_error_message(context, KRB5KDC_ERR_PREAUTH_FAILED,
+ "pk-hash have wrong length: %lu",
+ (unsigned long)req->pk_hash.length);
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+
+ HMAC_CTX_init(&ctx);
+ HMAC_Init_ex(&ctx,
+ key->keyvalue.data, key->keyvalue.length,
+ EVP_sha1(), NULL);
+ if (sizeof(digest) != HMAC_size(&ctx))
+ krb5_abortx(context, "runtime error, hmac buffer wrong size in kx509");
+ HMAC_Update(&ctx, version_2_0, sizeof(version_2_0));
+ HMAC_Update(&ctx, req->pk_key.data, req->pk_key.length);
+ HMAC_Final(&ctx, digest, 0);
+ HMAC_CTX_cleanup(&ctx);
+
+ if (memcmp(req->pk_hash.data, digest, sizeof(digest)) != 0) {
+ krb5_set_error_message(context, KRB5KDC_ERR_PREAUTH_FAILED,
+ "pk-hash is not correct");
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+ return 0;
+}
+
+static krb5_error_code
+calculate_reply_hash(krb5_context context,
+ krb5_keyblock *key,
+ Kx509Response *rep)
+{
+ HMAC_CTX ctx;
+
+ HMAC_CTX_init(&ctx);
+
+ HMAC_Init_ex(&ctx,
+ key->keyvalue.data, key->keyvalue.length,
+ EVP_sha1(), NULL);
+ rep->hash->length = HMAC_size(&ctx);
+ rep->hash->data = malloc(rep->hash->length);
+ if (rep->hash->data == NULL) {
+ HMAC_CTX_cleanup(&ctx);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+
+ HMAC_Update(&ctx, version_2_0, sizeof(version_2_0));
+ if (rep->error_code) {
+ int32_t t = *rep->error_code;
+ do {
+ unsigned char p = (t & 0xff);
+ HMAC_Update(&ctx, &p, 1);
+ t >>= 8;
+ } while (t);
+ }
+ if (rep->certificate)
+ HMAC_Update(&ctx, rep->certificate->data, rep->certificate->length);
+ if (rep->e_text)
+ HMAC_Update(&ctx, (unsigned char *)*rep->e_text, strlen(*rep->e_text));
+
+ HMAC_Final(&ctx, rep->hash->data, 0);
+ HMAC_CTX_cleanup(&ctx);
+
+ return 0;
+}
+
+/*
+ * Build a certifate for `principal´ that will expire at `endtime´.
+ */
+
+static krb5_error_code
+build_certificate(krb5_context context,
+ krb5_kdc_configuration *config,
+ const krb5_data *key,
+ time_t endtime,
+ krb5_principal principal,
+ krb5_data *certificate)
+{
+ hx509_context hxctx = NULL;
+ hx509_ca_tbs tbs = NULL;
+ hx509_env env = NULL;
+ hx509_cert cert = NULL;
+ hx509_cert signer = NULL;
+ int ret;
+
+ if (krb5_principal_get_comp_string(context, principal, 1) != NULL) {
+ kdc_log(context, config, 0, "Principal is not a user");
+ return EINVAL;
+ }
+
+ ret = hx509_context_init(&hxctx);
+ if (ret)
+ goto out;
+
+ ret = hx509_env_add(hxctx, &env, "principal-name",
+ krb5_principal_get_comp_string(context, principal, 0));
+ if (ret)
+ goto out;
+
+ {
+ hx509_certs certs;
+ hx509_query *q;
+
+ ret = hx509_certs_init(hxctx, config->kx509_ca, 0,
+ NULL, &certs);
+ if (ret) {
+ kdc_log(context, config, 0, "Failed to load CA %s",
+ config->kx509_ca);
+ goto out;
+ }
+ ret = hx509_query_alloc(hxctx, &q);
+ if (ret) {
+ hx509_certs_free(&certs);
+ goto out;
+ }
+
+ hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
+ hx509_query_match_option(q, HX509_QUERY_OPTION_KU_KEYCERTSIGN);
+
+ ret = hx509_certs_find(hxctx, certs, q, &signer);
+ hx509_query_free(hxctx, q);
+ hx509_certs_free(&certs);
+ if (ret) {
+ kdc_log(context, config, 0, "Failed to find a CA in %s",
+ config->kx509_ca);
+ goto out;
+ }
+ }
+
+ ret = hx509_ca_tbs_init(hxctx, &tbs);
+ if (ret)
+ goto out;
+
+ {
+ SubjectPublicKeyInfo spki;
+ heim_any any;
+
+ memset(&spki, 0, sizeof(spki));
+
+ spki.subjectPublicKey.data = key->data;
+ spki.subjectPublicKey.length = key->length * 8;
+
+ ret = der_copy_oid(oid_id_pkcs1_rsaEncryption(),
+ &spki.algorithm.algorithm);
+
+ any.data = "\x05\x00";
+ any.length = 2;
+ spki.algorithm.parameters = &any;
+
+ ret = hx509_ca_tbs_set_spki(hxctx, tbs, &spki);
+ der_free_oid(&spki.algorithm.algorithm);
+ if (ret)
+ goto out;
+ }
+
+ {
+ hx509_certs certs;
+ hx509_cert template;
+
+ ret = hx509_certs_init(hxctx, config->kx509_template, 0,
+ NULL, &certs);
+ if (ret) {
+ kdc_log(context, config, 0, "Failed to load template %s",
+ config->kx509_template);
+ goto out;
+ }
+ ret = hx509_get_one_cert(hxctx, certs, &template);
+ hx509_certs_free(&certs);
+ if (ret) {
+ kdc_log(context, config, 0, "Failed to find template in %s",
+ config->kx509_template);
+ goto out;
+ }
+ ret = hx509_ca_tbs_set_template(hxctx, tbs,
+ HX509_CA_TEMPLATE_SUBJECT|
+ HX509_CA_TEMPLATE_KU|
+ HX509_CA_TEMPLATE_EKU,
+ template);
+ hx509_cert_free(template);
+ if (ret)
+ goto out;
+ }
+
+ hx509_ca_tbs_set_notAfter(hxctx, tbs, endtime);
+
+ hx509_ca_tbs_subject_expand(hxctx, tbs, env);
+ hx509_env_free(&env);
+
+ ret = hx509_ca_sign(hxctx, tbs, signer, &cert);
+ hx509_cert_free(signer);
+ if (ret)
+ goto out;
+
+ hx509_ca_tbs_free(&tbs);
+
+ ret = hx509_cert_binary(hxctx, cert, certificate);
+ hx509_cert_free(cert);
+ if (ret)
+ goto out;
+
+ hx509_context_free(&hxctx);
+
+ return 0;
+out:
+ if (env)
+ hx509_env_free(&env);
+ if (tbs)
+ hx509_ca_tbs_free(&tbs);
+ if (signer)
+ hx509_cert_free(signer);
+ if (hxctx)
+ hx509_context_free(&hxctx);
+ krb5_set_error_message(context, ret, "cert creation failed");
+ return ret;
+}
+
+/*
+ *
+ */
+
+krb5_error_code
+_kdc_do_kx509(krb5_context context,
+ krb5_kdc_configuration *config,
+ const Kx509Request *req, krb5_data *reply,
+ const char *from, struct sockaddr *addr)
+{
+ krb5_error_code ret;
+ krb5_ticket *ticket = NULL;
+ krb5_flags ap_req_options;
+ krb5_auth_context ac = NULL;
+ krb5_keytab id = NULL;
+ krb5_principal sprincipal = NULL, cprincipal = NULL;
+ char *cname = NULL;
+ Kx509Response rep;
+ size_t size;
+ krb5_keyblock *key = NULL;
+
+ krb5_data_zero(reply);
+ memset(&rep, 0, sizeof(rep));
+
+ if(!config->enable_kx509) {
+ kdc_log(context, config, 0,
+ "Rejected kx509 request (disabled) from %s", from);
+ return KRB5KDC_ERR_POLICY;
+ }
+
+ kdc_log(context, config, 0, "Kx509 request from %s", from);
+
+ ret = krb5_kt_resolve(context, "HDB:", &id);
+ if (ret) {
+ kdc_log(context, config, 0, "Can't open database for digest");
+ goto out;
+ }
+
+ ret = krb5_rd_req(context,
+ &ac,
+ &req->authenticator,
+ NULL,
+ id,
+ &ap_req_options,
+ &ticket);
+ if (ret)
+ goto out;
+
+ ret = krb5_ticket_get_client(context, ticket, &cprincipal);
+ if (ret)
+ goto out;
+
+ ret = krb5_unparse_name(context, cprincipal, &cname);
+ if (ret)
+ goto out;
+
+ /* verify server principal */
+
+ ret = krb5_sname_to_principal(context, NULL, "kca_service",
+ KRB5_NT_UNKNOWN, &sprincipal);
+ if (ret)
+ goto out;
+
+ {
+ krb5_principal principal = NULL;
+
+ ret = krb5_ticket_get_server(context, ticket, &principal);
+ if (ret)
+ goto out;
+
+ ret = krb5_principal_compare(context, sprincipal, principal);
+ krb5_free_principal(context, principal);
+ if (ret != TRUE) {
+ ret = KRB5KDC_ERR_SERVER_NOMATCH;
+ krb5_set_error_message(context, ret,
+ "User %s used wrong Kx509 service principal",
+ cname);
+ goto out;
+ }
+ }
+
+ ret = krb5_auth_con_getkey(context, ac, &key);
+ if (ret == 0 && key == NULL)
+ ret = KRB5KDC_ERR_NULL_KEY;
+ if (ret) {
+ krb5_set_error_message(context, ret, "Kx509 can't get session key");
+ goto out;
+ }
+
+ ret = verify_req_hash(context, req, key);
+ if (ret)
+ goto out;
+
+ /* Verify that the key is encoded RSA key */
+ {
+ RSAPublicKey key;
+ size_t size;
+
+ ret = decode_RSAPublicKey(req->pk_key.data, req->pk_key.length,
+ &key, &size);
+ if (ret)
+ goto out;
+ free_RSAPublicKey(&key);
+ if (size != req->pk_key.length)
+ ;
+ }
+
+ ALLOC(rep.certificate);
+ if (rep.certificate == NULL)
+ goto out;
+ krb5_data_zero(rep.certificate);
+ ALLOC(rep.hash);
+ if (rep.hash == NULL)
+ goto out;
+ krb5_data_zero(rep.hash);
+
+ ret = build_certificate(context, config, &req->pk_key,
+ krb5_ticket_get_endtime(context, ticket),
+ cprincipal, rep.certificate);
+ if (ret)
+ goto out;
+
+ ret = calculate_reply_hash(context, key, &rep);
+ if (ret)
+ goto out;
+
+ /*
+ * Encode reply, [ version | Kx509Response ]
+ */
+
+ {
+ krb5_data data;
+
+ ASN1_MALLOC_ENCODE(Kx509Response, data.data, data.length, &rep,
+ &size, ret);
+ if (ret) {
+ krb5_set_error_message(context, ret, "Failed to encode kx509 reply");
+ goto out;
+ }
+ if (size != data.length)
+ krb5_abortx(context, "ASN1 internal error");
+
+ ret = krb5_data_alloc(reply, data.length + sizeof(version_2_0));
+ if (ret) {
+ free(data.data);
+ goto out;
+ }
+ memcpy(reply->data, version_2_0, sizeof(version_2_0));
+ memcpy(((unsigned char *)reply->data) + sizeof(version_2_0),
+ data.data, data.length);
+ free(data.data);
+ }
+
+ kdc_log(context, config, 0, "Successful Kx509 request for %s", cname);
+
+out:
+ if (ac)
+ krb5_auth_con_free(context, ac);
+ if (ret)
+ krb5_warn(context, ret, "Kx509 request from %s failed", from);
+ if (ticket)
+ krb5_free_ticket(context, ticket);
+ if (id)
+ krb5_kt_close(context, id);
+ if (sprincipal)
+ krb5_free_principal(context, sprincipal);
+ if (cprincipal)
+ krb5_free_principal(context, cprincipal);
+ if (key)
+ krb5_free_keyblock (context, key);
+ if (cname)
+ free(cname);
+ free_Kx509Response(&rep);
+
+ return 0;
+}
diff --git a/source4/heimdal/kdc/log.c b/source4/heimdal/kdc/log.c
new file mode 100644
index 0000000000..b4161da45d
--- /dev/null
+++ b/source4/heimdal/kdc/log.c
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 1997, 1998, 2002 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kdc_locl.h"
+RCSID("$Id$");
+
+void
+kdc_openlog(krb5_context context,
+ krb5_kdc_configuration *config)
+{
+ char **s = NULL, **p;
+ krb5_initlog(context, "kdc", &config->logf);
+ s = krb5_config_get_strings(context, NULL, "kdc", "logging", NULL);
+ if(s == NULL)
+ s = krb5_config_get_strings(context, NULL, "logging", "kdc", NULL);
+ if(s){
+ for(p = s; *p; p++)
+ krb5_addlog_dest(context, config->logf, *p);
+ krb5_config_free_strings(s);
+ }else {
+ char *s;
+ asprintf(&s, "0-1/FILE:%s/%s", hdb_db_dir(context), KDC_LOG_FILE);
+ krb5_addlog_dest(context, config->logf, s);
+ free(s);
+ }
+ krb5_set_warn_dest(context, config->logf);
+}
+
+char*
+kdc_log_msg_va(krb5_context context,
+ krb5_kdc_configuration *config,
+ int level, const char *fmt, va_list ap)
+{
+ char *msg;
+ krb5_vlog_msg(context, config->logf, &msg, level, fmt, ap);
+ return msg;
+}
+
+char*
+kdc_log_msg(krb5_context context,
+ krb5_kdc_configuration *config,
+ int level, const char *fmt, ...)
+{
+ va_list ap;
+ char *s;
+ va_start(ap, fmt);
+ s = kdc_log_msg_va(context, config, level, fmt, ap);
+ va_end(ap);
+ return s;
+}
+
+void
+kdc_log(krb5_context context,
+ krb5_kdc_configuration *config,
+ int level, const char *fmt, ...)
+{
+ va_list ap;
+ char *s;
+ va_start(ap, fmt);
+ s = kdc_log_msg_va(context, config, level, fmt, ap);
+ if(s) free(s);
+ va_end(ap);
+}
diff --git a/source4/heimdal/kdc/misc.c b/source4/heimdal/kdc/misc.c
new file mode 100644
index 0000000000..8a53fc8827
--- /dev/null
+++ b/source4/heimdal/kdc/misc.c
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kdc_locl.h"
+
+RCSID("$Id$");
+
+struct timeval _kdc_now;
+
+krb5_error_code
+_kdc_db_fetch(krb5_context context,
+ krb5_kdc_configuration *config,
+ krb5_const_principal principal,
+ unsigned flags,
+ HDB **db,
+ hdb_entry_ex **h)
+{
+ hdb_entry_ex *ent;
+ krb5_error_code ret;
+ int i;
+
+ ent = calloc (1, sizeof (*ent));
+ if (ent == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+
+ for(i = 0; i < config->num_db; i++) {
+ ret = config->db[i]->hdb_open(context, config->db[i], O_RDONLY, 0);
+ if (ret) {
+ kdc_log(context, config, 0, "Failed to open database: %s",
+ krb5_get_err_text(context, ret));
+ continue;
+ }
+ ret = config->db[i]->hdb_fetch(context,
+ config->db[i],
+ principal,
+ flags | HDB_F_DECRYPT,
+ ent);
+ config->db[i]->hdb_close(context, config->db[i]);
+ if(ret == 0) {
+ if (db)
+ *db = config->db[i];
+ *h = ent;
+ return 0;
+ }
+ }
+ free(ent);
+ krb5_set_error_message(context, HDB_ERR_NOENTRY, "no such entry found in hdb");
+ return HDB_ERR_NOENTRY;
+}
+
+void
+_kdc_free_ent(krb5_context context, hdb_entry_ex *ent)
+{
+ hdb_free_entry (context, ent);
+ free (ent);
+}
+
+/*
+ * Use the order list of preferred encryption types and sort the
+ * available keys and return the most preferred key.
+ */
+
+krb5_error_code
+_kdc_get_preferred_key(krb5_context context,
+ krb5_kdc_configuration *config,
+ hdb_entry_ex *h,
+ const char *name,
+ krb5_enctype *enctype,
+ Key **key)
+{
+ const krb5_enctype *p;
+ krb5_error_code ret;
+ int i;
+
+ p = krb5_kerberos_enctypes(context);
+
+ for (i = 0; p[i] != ETYPE_NULL; i++) {
+ if (krb5_enctype_valid(context, p[i]) != 0)
+ continue;
+ ret = hdb_enctype2key(context, &h->entry, p[i], key);
+ if (ret == 0) {
+ *enctype = p[i];
+ return 0;
+ }
+ }
+
+ krb5_set_error_message(context, EINVAL,
+ "No valid kerberos key found for %s", name);
+ return EINVAL;
+}
+
diff --git a/source4/heimdal/kdc/pkinit.c b/source4/heimdal/kdc/pkinit.c
new file mode 100644
index 0000000000..82358682d8
--- /dev/null
+++ b/source4/heimdal/kdc/pkinit.c
@@ -0,0 +1,1665 @@
+/*
+ * Copyright (c) 2003 - 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kdc_locl.h"
+
+RCSID("$Id$");
+
+#ifdef PKINIT
+
+#include <heim_asn1.h>
+#include <rfc2459_asn1.h>
+#include <cms_asn1.h>
+#include <pkinit_asn1.h>
+
+#include <hx509.h>
+#include "crypto-headers.h"
+
+struct pk_client_params {
+ enum krb5_pk_type type;
+ BIGNUM *dh_public_key;
+ hx509_cert cert;
+ unsigned nonce;
+ DH *dh;
+ EncryptionKey reply_key;
+ char *dh_group_name;
+ hx509_peer_info peer;
+ hx509_certs client_anchors;
+};
+
+struct pk_principal_mapping {
+ unsigned int len;
+ struct pk_allowed_princ {
+ krb5_principal principal;
+ char *subject;
+ } *val;
+};
+
+static struct krb5_pk_identity *kdc_identity;
+static struct pk_principal_mapping principal_mappings;
+static struct krb5_dh_moduli **moduli;
+
+static struct {
+ krb5_data data;
+ time_t expire;
+ time_t next_update;
+} ocsp;
+
+/*
+ *
+ */
+
+static krb5_error_code
+pk_check_pkauthenticator_win2k(krb5_context context,
+ PKAuthenticator_Win2k *a,
+ const KDC_REQ *req)
+{
+ krb5_timestamp now;
+
+ krb5_timeofday (context, &now);
+
+ /* XXX cusec */
+ if (a->ctime == 0 || abs(a->ctime - now) > context->max_skew) {
+ krb5_clear_error_message(context);
+ return KRB5KRB_AP_ERR_SKEW;
+ }
+ return 0;
+}
+
+static krb5_error_code
+pk_check_pkauthenticator(krb5_context context,
+ PKAuthenticator *a,
+ const KDC_REQ *req)
+{
+ u_char *buf = NULL;
+ size_t buf_size;
+ krb5_error_code ret;
+ size_t len;
+ krb5_timestamp now;
+ Checksum checksum;
+
+ krb5_timeofday (context, &now);
+
+ /* XXX cusec */
+ if (a->ctime == 0 || abs(a->ctime - now) > context->max_skew) {
+ krb5_clear_error_message(context);
+ return KRB5KRB_AP_ERR_SKEW;
+ }
+
+ ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, &req->req_body, &len, ret);
+ if (ret) {
+ krb5_clear_error_message(context);
+ return ret;
+ }
+ if (buf_size != len)
+ krb5_abortx(context, "Internal error in ASN.1 encoder");
+
+ ret = krb5_create_checksum(context,
+ NULL,
+ 0,
+ CKSUMTYPE_SHA1,
+ buf,
+ len,
+ &checksum);
+ free(buf);
+ if (ret) {
+ krb5_clear_error_message(context);
+ return ret;
+ }
+
+ if (a->paChecksum == NULL) {
+ krb5_clear_error_message(context);
+ ret = KRB5_KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED;
+ goto out;
+ }
+
+ if (der_heim_octet_string_cmp(a->paChecksum, &checksum.checksum) != 0) {
+ krb5_clear_error_message(context);
+ ret = KRB5KRB_ERR_GENERIC;
+ }
+
+out:
+ free_Checksum(&checksum);
+
+ return ret;
+}
+
+void
+_kdc_pk_free_client_param(krb5_context context,
+ pk_client_params *client_params)
+{
+ if (client_params->cert)
+ hx509_cert_free(client_params->cert);
+ if (client_params->dh)
+ DH_free(client_params->dh);
+ if (client_params->dh_public_key)
+ BN_free(client_params->dh_public_key);
+ krb5_free_keyblock_contents(context, &client_params->reply_key);
+ if (client_params->dh_group_name)
+ free(client_params->dh_group_name);
+ if (client_params->peer)
+ hx509_peer_info_free(client_params->peer);
+ if (client_params->client_anchors)
+ hx509_certs_free(&client_params->client_anchors);
+ memset(client_params, 0, sizeof(*client_params));
+ free(client_params);
+}
+
+static krb5_error_code
+generate_dh_keyblock(krb5_context context, pk_client_params *client_params,
+ krb5_enctype enctype, krb5_keyblock *reply_key)
+{
+ unsigned char *dh_gen_key = NULL;
+ krb5_keyblock key;
+ krb5_error_code ret;
+ size_t dh_gen_keylen, size;
+
+ memset(&key, 0, sizeof(key));
+
+ if (!DH_generate_key(client_params->dh)) {
+ ret = KRB5KRB_ERR_GENERIC;
+ krb5_set_error_message(context, ret, "Can't generate Diffie-Hellman keys");
+ goto out;
+ }
+ if (client_params->dh_public_key == NULL) {
+ ret = KRB5KRB_ERR_GENERIC;
+ krb5_set_error_message(context, ret, "dh_public_key");
+ goto out;
+ }
+
+ dh_gen_keylen = DH_size(client_params->dh);
+ size = BN_num_bytes(client_params->dh->p);
+ if (size < dh_gen_keylen)
+ size = dh_gen_keylen;
+
+ dh_gen_key = malloc(size);
+ if (dh_gen_key == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto out;
+ }
+ memset(dh_gen_key, 0, size - dh_gen_keylen);
+
+ dh_gen_keylen = DH_compute_key(dh_gen_key + (size - dh_gen_keylen),
+ client_params->dh_public_key,
+ client_params->dh);
+ if (dh_gen_keylen == -1) {
+ ret = KRB5KRB_ERR_GENERIC;
+ krb5_set_error_message(context, ret, "Can't compute Diffie-Hellman key");
+ goto out;
+ }
+
+ ret = _krb5_pk_octetstring2key(context,
+ enctype,
+ dh_gen_key, dh_gen_keylen,
+ NULL, NULL,
+ reply_key);
+
+ out:
+ if (dh_gen_key)
+ free(dh_gen_key);
+ if (key.keyvalue.data)
+ krb5_free_keyblock_contents(context, &key);
+
+ return ret;
+}
+
+static BIGNUM *
+integer_to_BN(krb5_context context, const char *field, heim_integer *f)
+{
+ BIGNUM *bn;
+
+ bn = BN_bin2bn((const unsigned char *)f->data, f->length, NULL);
+ if (bn == NULL) {
+ krb5_set_error_message(context, KRB5_BADMSGTYPE,
+ "PKINIT: parsing BN failed %s", field);
+ return NULL;
+ }
+ BN_set_negative(bn, f->negative);
+ return bn;
+}
+
+static krb5_error_code
+get_dh_param(krb5_context context,
+ krb5_kdc_configuration *config,
+ SubjectPublicKeyInfo *dh_key_info,
+ pk_client_params *client_params)
+{
+ DomainParameters dhparam;
+ DH *dh = NULL;
+ krb5_error_code ret;
+
+ memset(&dhparam, 0, sizeof(dhparam));
+
+ if (der_heim_oid_cmp(&dh_key_info->algorithm.algorithm, oid_id_dhpublicnumber())) {
+ krb5_set_error_message(context, KRB5_BADMSGTYPE,
+ "PKINIT invalid oid in clientPublicValue");
+ return KRB5_BADMSGTYPE;
+ }
+
+ if (dh_key_info->algorithm.parameters == NULL) {
+ krb5_set_error_message(context, KRB5_BADMSGTYPE,
+ "PKINIT missing algorithm parameter "
+ "in clientPublicValue");
+ return KRB5_BADMSGTYPE;
+ }
+
+ ret = decode_DomainParameters(dh_key_info->algorithm.parameters->data,
+ dh_key_info->algorithm.parameters->length,
+ &dhparam,
+ NULL);
+ if (ret) {
+ krb5_set_error_message(context, ret, "Can't decode algorithm "
+ "parameters in clientPublicValue");
+ goto out;
+ }
+
+ if ((dh_key_info->subjectPublicKey.length % 8) != 0) {
+ ret = KRB5_BADMSGTYPE;
+ krb5_set_error_message(context, ret,
+ "PKINIT: subjectPublicKey not aligned "
+ "to 8 bit boundary");
+ goto out;
+ }
+
+
+ ret = _krb5_dh_group_ok(context, config->pkinit_dh_min_bits,
+ &dhparam.p, &dhparam.g, &dhparam.q, moduli,
+ &client_params->dh_group_name);
+ if (ret) {
+ /* XXX send back proposal of better group */
+ goto out;
+ }
+
+ dh = DH_new();
+ if (dh == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "Cannot create DH structure");
+ goto out;
+ }
+ ret = KRB5_BADMSGTYPE;
+ dh->p = integer_to_BN(context, "DH prime", &dhparam.p);
+ if (dh->p == NULL)
+ goto out;
+ dh->g = integer_to_BN(context, "DH base", &dhparam.g);
+ if (dh->g == NULL)
+ goto out;
+ dh->q = integer_to_BN(context, "DH p-1 factor", &dhparam.q);
+ if (dh->g == NULL)
+ goto out;
+
+ {
+ heim_integer glue;
+ size_t size;
+
+ ret = decode_DHPublicKey(dh_key_info->subjectPublicKey.data,
+ dh_key_info->subjectPublicKey.length / 8,
+ &glue,
+ &size);
+ if (ret) {
+ krb5_clear_error_message(context);
+ return ret;
+ }
+
+ client_params->dh_public_key = integer_to_BN(context,
+ "subjectPublicKey",
+ &glue);
+ der_free_heim_integer(&glue);
+ if (client_params->dh_public_key == NULL) {
+ ret = KRB5_BADMSGTYPE;
+ goto out;
+ }
+ }
+
+ client_params->dh = dh;
+ dh = NULL;
+ ret = 0;
+
+ out:
+ if (dh)
+ DH_free(dh);
+ free_DomainParameters(&dhparam);
+ return ret;
+}
+
+krb5_error_code
+_kdc_pk_rd_padata(krb5_context context,
+ krb5_kdc_configuration *config,
+ const KDC_REQ *req,
+ const PA_DATA *pa,
+ pk_client_params **ret_params)
+{
+ pk_client_params *client_params;
+ krb5_error_code ret;
+ heim_oid eContentType = { 0, NULL }, contentInfoOid = { 0, NULL };
+ krb5_data eContent = { 0, NULL };
+ krb5_data signed_content = { 0, NULL };
+ const char *type = "unknown type";
+ int have_data = 0;
+
+ *ret_params = NULL;
+
+ if (!config->enable_pkinit) {
+ kdc_log(context, config, 0, "PK-INIT request but PK-INIT not enabled");
+ krb5_clear_error_message(context);
+ return 0;
+ }
+
+ hx509_verify_set_time(kdc_identity->verify_ctx, kdc_time);
+
+ client_params = calloc(1, sizeof(*client_params));
+ if (client_params == NULL) {
+ krb5_clear_error_message(context);
+ ret = ENOMEM;
+ goto out;
+ }
+
+ if (pa->padata_type == KRB5_PADATA_PK_AS_REQ_WIN) {
+ PA_PK_AS_REQ_Win2k r;
+
+ type = "PK-INIT-Win2k";
+
+ ret = decode_PA_PK_AS_REQ_Win2k(pa->padata_value.data,
+ pa->padata_value.length,
+ &r,
+ NULL);
+ if (ret) {
+ krb5_set_error_message(context, ret, "Can't decode "
+ "PK-AS-REQ-Win2k: %d", ret);
+ goto out;
+ }
+
+ ret = hx509_cms_unwrap_ContentInfo(&r.signed_auth_pack,
+ &contentInfoOid,
+ &signed_content,
+ &have_data);
+ free_PA_PK_AS_REQ_Win2k(&r);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ "Can't decode PK-AS-REQ: %d", ret);
+ goto out;
+ }
+
+ } else if (pa->padata_type == KRB5_PADATA_PK_AS_REQ) {
+ PA_PK_AS_REQ r;
+
+ type = "PK-INIT-IETF";
+
+ ret = decode_PA_PK_AS_REQ(pa->padata_value.data,
+ pa->padata_value.length,
+ &r,
+ NULL);
+ if (ret) {
+ krb5_set_error_message(context, ret, "Can't decode PK-AS-REQ: %d", ret);
+ goto out;
+ }
+
+ /* XXX look at r.kdcPkId */
+ if (r.trustedCertifiers) {
+ ExternalPrincipalIdentifiers *edi = r.trustedCertifiers;
+ unsigned int i;
+
+ ret = hx509_certs_init(kdc_identity->hx509ctx,
+ "MEMORY:client-anchors",
+ 0, NULL,
+ &client_params->client_anchors);
+ if (ret) {
+ krb5_set_error_message(context, ret, "Can't allocate client anchors: %d", ret);
+ goto out;
+
+ }
+ for (i = 0; i < edi->len; i++) {
+ IssuerAndSerialNumber iasn;
+ hx509_query *q;
+ hx509_cert cert;
+ size_t size;
+
+ if (edi->val[i].issuerAndSerialNumber == NULL)
+ continue;
+
+ ret = hx509_query_alloc(kdc_identity->hx509ctx, &q);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ "Failed to allocate hx509_query");
+ goto out;
+ }
+
+ ret = decode_IssuerAndSerialNumber(edi->val[i].issuerAndSerialNumber->data,
+ edi->val[i].issuerAndSerialNumber->length,
+ &iasn,
+ &size);
+ if (ret) {
+ hx509_query_free(kdc_identity->hx509ctx, q);
+ continue;
+ }
+ ret = hx509_query_match_issuer_serial(q, &iasn.issuer, &iasn.serialNumber);
+ free_IssuerAndSerialNumber(&iasn);
+ if (ret)
+ continue;
+
+ ret = hx509_certs_find(kdc_identity->hx509ctx,
+ kdc_identity->certs,
+ q,
+ &cert);
+ hx509_query_free(kdc_identity->hx509ctx, q);
+ if (ret)
+ continue;
+ hx509_certs_add(kdc_identity->hx509ctx,
+ client_params->client_anchors, cert);
+ hx509_cert_free(cert);
+ }
+ }
+
+ ret = hx509_cms_unwrap_ContentInfo(&r.signedAuthPack,
+ &contentInfoOid,
+ &signed_content,
+ &have_data);
+ free_PA_PK_AS_REQ(&r);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ "Can't unwrap ContentInfo: %d", ret);
+ goto out;
+ }
+
+ } else {
+ krb5_clear_error_message(context);
+ ret = KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
+ goto out;
+ }
+
+ ret = der_heim_oid_cmp(&contentInfoOid, oid_id_pkcs7_signedData());
+ if (ret != 0) {
+ ret = KRB5KRB_ERR_GENERIC;
+ krb5_set_error_message(context, ret,
+ "PK-AS-REQ-Win2k invalid content type oid");
+ goto out;
+ }
+
+ if (!have_data) {
+ ret = KRB5KRB_ERR_GENERIC;
+ krb5_set_error_message(context, ret,
+ "PK-AS-REQ-Win2k no signed auth pack");
+ goto out;
+ }
+
+ {
+ hx509_certs signer_certs;
+
+ ret = hx509_cms_verify_signed(kdc_identity->hx509ctx,
+ kdc_identity->verify_ctx,
+ signed_content.data,
+ signed_content.length,
+ NULL,
+ kdc_identity->certpool,
+ &eContentType,
+ &eContent,
+ &signer_certs);
+ if (ret) {
+ char *s = hx509_get_error_string(kdc_identity->hx509ctx, ret);
+ krb5_warnx(context, "PKINIT: failed to verify signature: %s: %d",
+ s, ret);
+ free(s);
+ goto out;
+ }
+
+ ret = hx509_get_one_cert(kdc_identity->hx509ctx, signer_certs,
+ &client_params->cert);
+ hx509_certs_free(&signer_certs);
+ if (ret)
+ goto out;
+ }
+
+ /* Signature is correct, now verify the signed message */
+ if (der_heim_oid_cmp(&eContentType, oid_id_pkcs7_data()) != 0 &&
+ der_heim_oid_cmp(&eContentType, oid_id_pkauthdata()) != 0)
+ {
+ ret = KRB5_BADMSGTYPE;
+ krb5_set_error_message(context, ret, "got wrong oid for pkauthdata");
+ goto out;
+ }
+
+ if (pa->padata_type == KRB5_PADATA_PK_AS_REQ_WIN) {
+ AuthPack_Win2k ap;
+
+ ret = decode_AuthPack_Win2k(eContent.data,
+ eContent.length,
+ &ap,
+ NULL);
+ if (ret) {
+ krb5_set_error_message(context, ret, "can't decode AuthPack: %d", ret);
+ goto out;
+ }
+
+ ret = pk_check_pkauthenticator_win2k(context,
+ &ap.pkAuthenticator,
+ req);
+ if (ret) {
+ free_AuthPack_Win2k(&ap);
+ goto out;
+ }
+
+ client_params->type = PKINIT_WIN2K;
+ client_params->nonce = ap.pkAuthenticator.nonce;
+
+ if (ap.clientPublicValue) {
+ ret = KRB5KRB_ERR_GENERIC;
+ krb5_set_error_message(context, ret, "DH not supported for windows");
+ goto out;
+ }
+ free_AuthPack_Win2k(&ap);
+
+ } else if (pa->padata_type == KRB5_PADATA_PK_AS_REQ) {
+ AuthPack ap;
+
+ ret = decode_AuthPack(eContent.data,
+ eContent.length,
+ &ap,
+ NULL);
+ if (ret) {
+ krb5_set_error_message(context, ret, "can't decode AuthPack: %d", ret);
+ free_AuthPack(&ap);
+ goto out;
+ }
+
+ ret = pk_check_pkauthenticator(context,
+ &ap.pkAuthenticator,
+ req);
+ if (ret) {
+ free_AuthPack(&ap);
+ goto out;
+ }
+
+ client_params->type = PKINIT_27;
+ client_params->nonce = ap.pkAuthenticator.nonce;
+
+ if (ap.clientPublicValue) {
+ ret = get_dh_param(context, config,
+ ap.clientPublicValue, client_params);
+ if (ret) {
+ free_AuthPack(&ap);
+ goto out;
+ }
+ }
+
+ if (ap.supportedCMSTypes) {
+ ret = hx509_peer_info_alloc(kdc_identity->hx509ctx,
+ &client_params->peer);
+ if (ret) {
+ free_AuthPack(&ap);
+ goto out;
+ }
+ ret = hx509_peer_info_set_cms_algs(kdc_identity->hx509ctx,
+ client_params->peer,
+ ap.supportedCMSTypes->val,
+ ap.supportedCMSTypes->len);
+ if (ret) {
+ free_AuthPack(&ap);
+ goto out;
+ }
+ }
+ free_AuthPack(&ap);
+ } else
+ krb5_abortx(context, "internal pkinit error");
+
+ kdc_log(context, config, 0, "PK-INIT request of type %s", type);
+
+out:
+ if (ret)
+ krb5_warn(context, ret, "PKINIT");
+
+ if (signed_content.data)
+ free(signed_content.data);
+ krb5_data_free(&eContent);
+ der_free_oid(&eContentType);
+ der_free_oid(&contentInfoOid);
+ if (ret)
+ _kdc_pk_free_client_param(context, client_params);
+ else
+ *ret_params = client_params;
+ return ret;
+}
+
+/*
+ *
+ */
+
+static krb5_error_code
+BN_to_integer(krb5_context context, BIGNUM *bn, heim_integer *integer)
+{
+ integer->length = BN_num_bytes(bn);
+ integer->data = malloc(integer->length);
+ if (integer->data == NULL) {
+ krb5_clear_error_message(context);
+ return ENOMEM;
+ }
+ BN_bn2bin(bn, integer->data);
+ integer->negative = BN_is_negative(bn);
+ return 0;
+}
+
+static krb5_error_code
+pk_mk_pa_reply_enckey(krb5_context context,
+ krb5_kdc_configuration *config,
+ pk_client_params *client_params,
+ const KDC_REQ *req,
+ const krb5_data *req_buffer,
+ krb5_keyblock *reply_key,
+ ContentInfo *content_info)
+{
+ const heim_oid *envelopedAlg = NULL, *sdAlg = NULL, *evAlg = NULL;
+ krb5_error_code ret;
+ krb5_data buf, signed_data;
+ size_t size;
+ int do_win2k = 0;
+
+ krb5_data_zero(&buf);
+ krb5_data_zero(&signed_data);
+
+ /*
+ * If the message client is a win2k-type but it send pa data
+ * 09-binding it expects a IETF (checksum) reply so there can be
+ * no replay attacks.
+ */
+
+ switch (client_params->type) {
+ case PKINIT_WIN2K: {
+ int i = 0;
+ if (_kdc_find_padata(req, &i, KRB5_PADATA_PK_AS_09_BINDING) == NULL
+ && config->pkinit_require_binding == 0)
+ {
+ do_win2k = 1;
+ }
+ sdAlg = oid_id_pkcs7_data();
+ evAlg = oid_id_pkcs7_data();
+ envelopedAlg = oid_id_rsadsi_des_ede3_cbc();
+ break;
+ }
+ case PKINIT_27:
+ sdAlg = oid_id_pkrkeydata();
+ evAlg = oid_id_pkcs7_signedData();
+ break;
+ default:
+ krb5_abortx(context, "internal pkinit error");
+ }
+
+ if (do_win2k) {
+ ReplyKeyPack_Win2k kp;
+ memset(&kp, 0, sizeof(kp));
+
+ ret = copy_EncryptionKey(reply_key, &kp.replyKey);
+ if (ret) {
+ krb5_clear_error_message(context);
+ goto out;
+ }
+ kp.nonce = client_params->nonce;
+
+ ASN1_MALLOC_ENCODE(ReplyKeyPack_Win2k,
+ buf.data, buf.length,
+ &kp, &size,ret);
+ free_ReplyKeyPack_Win2k(&kp);
+ } else {
+ krb5_crypto ascrypto;
+ ReplyKeyPack kp;
+ memset(&kp, 0, sizeof(kp));
+
+ ret = copy_EncryptionKey(reply_key, &kp.replyKey);
+ if (ret) {
+ krb5_clear_error_message(context);
+ goto out;
+ }
+
+ ret = krb5_crypto_init(context, reply_key, 0, &ascrypto);
+ if (ret) {
+ krb5_clear_error_message(context);
+ goto out;
+ }
+
+ ret = krb5_create_checksum(context, ascrypto, 6, 0,
+ req_buffer->data, req_buffer->length,
+ &kp.asChecksum);
+ if (ret) {
+ krb5_clear_error_message(context);
+ goto out;
+ }
+
+ ret = krb5_crypto_destroy(context, ascrypto);
+ if (ret) {
+ krb5_clear_error_message(context);
+ goto out;
+ }
+ ASN1_MALLOC_ENCODE(ReplyKeyPack, buf.data, buf.length, &kp, &size,ret);
+ free_ReplyKeyPack(&kp);
+ }
+ if (ret) {
+ krb5_set_error_message(context, ret, "ASN.1 encoding of ReplyKeyPack "
+ "failed (%d)", ret);
+ goto out;
+ }
+ if (buf.length != size)
+ krb5_abortx(context, "Internal ASN.1 encoder error");
+
+ {
+ hx509_query *q;
+ hx509_cert cert;
+
+ ret = hx509_query_alloc(kdc_identity->hx509ctx, &q);
+ if (ret)
+ goto out;
+
+ hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
+ hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
+
+ ret = hx509_certs_find(kdc_identity->hx509ctx,
+ kdc_identity->certs,
+ q,
+ &cert);
+ hx509_query_free(kdc_identity->hx509ctx, q);
+ if (ret)
+ goto out;
+
+ ret = hx509_cms_create_signed_1(kdc_identity->hx509ctx,
+ 0,
+ sdAlg,
+ buf.data,
+ buf.length,
+ NULL,
+ cert,
+ client_params->peer,
+ client_params->client_anchors,
+ kdc_identity->certpool,
+ &signed_data);
+ hx509_cert_free(cert);
+ }
+
+ krb5_data_free(&buf);
+ if (ret)
+ goto out;
+
+ if (client_params->type == PKINIT_WIN2K) {
+ ret = hx509_cms_wrap_ContentInfo(oid_id_pkcs7_signedData(),
+ &signed_data,
+ &buf);
+ if (ret)
+ goto out;
+ krb5_data_free(&signed_data);
+ signed_data = buf;
+ }
+
+ ret = hx509_cms_envelope_1(kdc_identity->hx509ctx,
+ 0,
+ client_params->cert,
+ signed_data.data, signed_data.length,
+ envelopedAlg,
+ evAlg, &buf);
+ if (ret)
+ goto out;
+
+ ret = _krb5_pk_mk_ContentInfo(context,
+ &buf,
+ oid_id_pkcs7_envelopedData(),
+ content_info);
+out:
+ krb5_data_free(&buf);
+ krb5_data_free(&signed_data);
+ return ret;
+}
+
+/*
+ *
+ */
+
+static krb5_error_code
+pk_mk_pa_reply_dh(krb5_context context,
+ DH *kdc_dh,
+ pk_client_params *client_params,
+ krb5_keyblock *reply_key,
+ ContentInfo *content_info,
+ hx509_cert *kdc_cert)
+{
+ KDCDHKeyInfo dh_info;
+ krb5_data signed_data, buf;
+ ContentInfo contentinfo;
+ krb5_error_code ret;
+ size_t size;
+ heim_integer i;
+
+ memset(&contentinfo, 0, sizeof(contentinfo));
+ memset(&dh_info, 0, sizeof(dh_info));
+ krb5_data_zero(&buf);
+ krb5_data_zero(&signed_data);
+
+ *kdc_cert = NULL;
+
+ ret = BN_to_integer(context, kdc_dh->pub_key, &i);
+ if (ret)
+ return ret;
+
+ ASN1_MALLOC_ENCODE(DHPublicKey, buf.data, buf.length, &i, &size, ret);
+ if (ret) {
+ krb5_set_error_message(context, ret, "ASN.1 encoding of "
+ "DHPublicKey failed (%d)", ret);
+ return ret;
+ }
+ if (buf.length != size)
+ krb5_abortx(context, "Internal ASN.1 encoder error");
+
+ dh_info.subjectPublicKey.length = buf.length * 8;
+ dh_info.subjectPublicKey.data = buf.data;
+
+ dh_info.nonce = client_params->nonce;
+
+ ASN1_MALLOC_ENCODE(KDCDHKeyInfo, buf.data, buf.length, &dh_info, &size,
+ ret);
+ if (ret) {
+ krb5_set_error_message(context, ret, "ASN.1 encoding of "
+ "KdcDHKeyInfo failed (%d)", ret);
+ goto out;
+ }
+ if (buf.length != size)
+ krb5_abortx(context, "Internal ASN.1 encoder error");
+
+ /*
+ * Create the SignedData structure and sign the KdcDHKeyInfo
+ * filled in above
+ */
+
+ {
+ hx509_query *q;
+ hx509_cert cert;
+
+ ret = hx509_query_alloc(kdc_identity->hx509ctx, &q);
+ if (ret)
+ goto out;
+
+ hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
+ hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
+
+ ret = hx509_certs_find(kdc_identity->hx509ctx,
+ kdc_identity->certs,
+ q,
+ &cert);
+ hx509_query_free(kdc_identity->hx509ctx, q);
+ if (ret)
+ goto out;
+
+ ret = hx509_cms_create_signed_1(kdc_identity->hx509ctx,
+ 0,
+ oid_id_pkdhkeydata(),
+ buf.data,
+ buf.length,
+ NULL,
+ cert,
+ client_params->peer,
+ client_params->client_anchors,
+ kdc_identity->certpool,
+ &signed_data);
+ *kdc_cert = cert;
+ }
+ if (ret)
+ goto out;
+
+ ret = _krb5_pk_mk_ContentInfo(context,
+ &signed_data,
+ oid_id_pkcs7_signedData(),
+ content_info);
+ if (ret)
+ goto out;
+
+ out:
+ if (ret && *kdc_cert) {
+ hx509_cert_free(*kdc_cert);
+ *kdc_cert = NULL;
+ }
+
+ krb5_data_free(&buf);
+ krb5_data_free(&signed_data);
+ free_KDCDHKeyInfo(&dh_info);
+
+ return ret;
+}
+
+/*
+ *
+ */
+
+krb5_error_code
+_kdc_pk_mk_pa_reply(krb5_context context,
+ krb5_kdc_configuration *config,
+ pk_client_params *client_params,
+ const hdb_entry_ex *client,
+ const KDC_REQ *req,
+ const krb5_data *req_buffer,
+ krb5_keyblock **reply_key,
+ METHOD_DATA *md)
+{
+ krb5_error_code ret;
+ void *buf;
+ size_t len, size;
+ krb5_enctype enctype;
+ int pa_type;
+ hx509_cert kdc_cert = NULL;
+ int i;
+
+ if (!config->enable_pkinit) {
+ krb5_clear_error_message(context);
+ return 0;
+ }
+
+ if (req->req_body.etype.len > 0) {
+ for (i = 0; i < req->req_body.etype.len; i++)
+ if (krb5_enctype_valid(context, req->req_body.etype.val[i]) == 0)
+ break;
+ if (req->req_body.etype.len <= i) {
+ ret = KRB5KRB_ERR_GENERIC;
+ krb5_set_error_message(context, ret,
+ "No valid enctype available from client");
+ goto out;
+ }
+ enctype = req->req_body.etype.val[i];
+ } else
+ enctype = ETYPE_DES3_CBC_SHA1;
+
+ if (client_params->type == PKINIT_27) {
+ PA_PK_AS_REP rep;
+ const char *type, *other = "";
+
+ memset(&rep, 0, sizeof(rep));
+
+ pa_type = KRB5_PADATA_PK_AS_REP;
+
+ if (client_params->dh == NULL) {
+ ContentInfo info;
+
+ type = "enckey";
+
+ rep.element = choice_PA_PK_AS_REP_encKeyPack;
+
+ ret = krb5_generate_random_keyblock(context, enctype,
+ &client_params->reply_key);
+ if (ret) {
+ free_PA_PK_AS_REP(&rep);
+ goto out;
+ }
+ ret = pk_mk_pa_reply_enckey(context,
+ config,
+ client_params,
+ req,
+ req_buffer,
+ &client_params->reply_key,
+ &info);
+ if (ret) {
+ free_PA_PK_AS_REP(&rep);
+ goto out;
+ }
+ ASN1_MALLOC_ENCODE(ContentInfo, rep.u.encKeyPack.data,
+ rep.u.encKeyPack.length, &info, &size,
+ ret);
+ free_ContentInfo(&info);
+ if (ret) {
+ krb5_set_error_message(context, ret, "encoding of Key ContentInfo "
+ "failed %d", ret);
+ free_PA_PK_AS_REP(&rep);
+ goto out;
+ }
+ if (rep.u.encKeyPack.length != size)
+ krb5_abortx(context, "Internal ASN.1 encoder error");
+
+ } else {
+ ContentInfo info;
+
+ type = "dh";
+ if (client_params->dh_group_name)
+ other = client_params->dh_group_name;
+
+ rep.element = choice_PA_PK_AS_REP_dhInfo;
+
+ ret = generate_dh_keyblock(context, client_params, enctype,
+ &client_params->reply_key);
+ if (ret)
+ return ret;
+
+ ret = pk_mk_pa_reply_dh(context, client_params->dh,
+ client_params,
+ &client_params->reply_key,
+ &info,
+ &kdc_cert);
+
+ ASN1_MALLOC_ENCODE(ContentInfo, rep.u.dhInfo.dhSignedData.data,
+ rep.u.dhInfo.dhSignedData.length, &info, &size,
+ ret);
+ free_ContentInfo(&info);
+ if (ret) {
+ krb5_set_error_message(context, ret, "encoding of Key ContentInfo "
+ "failed %d", ret);
+ free_PA_PK_AS_REP(&rep);
+ goto out;
+ }
+ if (rep.u.encKeyPack.length != size)
+ krb5_abortx(context, "Internal ASN.1 encoder error");
+
+ }
+ if (ret) {
+ free_PA_PK_AS_REP(&rep);
+ goto out;
+ }
+
+ ASN1_MALLOC_ENCODE(PA_PK_AS_REP, buf, len, &rep, &size, ret);
+ free_PA_PK_AS_REP(&rep);
+ if (ret) {
+ krb5_set_error_message(context, ret, "encode PA-PK-AS-REP failed %d",
+ ret);
+ goto out;
+ }
+ if (len != size)
+ krb5_abortx(context, "Internal ASN.1 encoder error");
+
+ kdc_log(context, config, 0, "PK-INIT using %s %s", type, other);
+
+ } else if (client_params->type == PKINIT_WIN2K) {
+ PA_PK_AS_REP_Win2k rep;
+ ContentInfo info;
+
+ if (client_params->dh) {
+ ret = KRB5KRB_ERR_GENERIC;
+ krb5_set_error_message(context, ret, "Windows PK-INIT doesn't support DH");
+ goto out;
+ }
+
+ memset(&rep, 0, sizeof(rep));
+
+ pa_type = KRB5_PADATA_PK_AS_REP_19;
+ rep.element = choice_PA_PK_AS_REP_encKeyPack;
+
+ ret = krb5_generate_random_keyblock(context, enctype,
+ &client_params->reply_key);
+ if (ret) {
+ free_PA_PK_AS_REP_Win2k(&rep);
+ goto out;
+ }
+ ret = pk_mk_pa_reply_enckey(context,
+ config,
+ client_params,
+ req,
+ req_buffer,
+ &client_params->reply_key,
+ &info);
+ if (ret) {
+ free_PA_PK_AS_REP_Win2k(&rep);
+ goto out;
+ }
+ ASN1_MALLOC_ENCODE(ContentInfo, rep.u.encKeyPack.data,
+ rep.u.encKeyPack.length, &info, &size,
+ ret);
+ free_ContentInfo(&info);
+ if (ret) {
+ krb5_set_error_message(context, ret, "encoding of Key ContentInfo "
+ "failed %d", ret);
+ free_PA_PK_AS_REP_Win2k(&rep);
+ goto out;
+ }
+ if (rep.u.encKeyPack.length != size)
+ krb5_abortx(context, "Internal ASN.1 encoder error");
+
+ ASN1_MALLOC_ENCODE(PA_PK_AS_REP_Win2k, buf, len, &rep, &size, ret);
+ free_PA_PK_AS_REP_Win2k(&rep);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ "encode PA-PK-AS-REP-Win2k failed %d", ret);
+ goto out;
+ }
+ if (len != size)
+ krb5_abortx(context, "Internal ASN.1 encoder error");
+
+ } else
+ krb5_abortx(context, "PK-INIT internal error");
+
+
+ ret = krb5_padata_add(context, md, pa_type, buf, len);
+ if (ret) {
+ krb5_set_error_message(context, ret, "failed adding PA-PK-AS-REP %d", ret);
+ free(buf);
+ goto out;
+ }
+
+ if (config->pkinit_kdc_ocsp_file) {
+
+ if (ocsp.expire == 0 && ocsp.next_update > kdc_time) {
+ struct stat sb;
+ int fd;
+
+ krb5_data_free(&ocsp.data);
+
+ ocsp.expire = 0;
+ ocsp.next_update = kdc_time + 60 * 5;
+
+ fd = open(config->pkinit_kdc_ocsp_file, O_RDONLY);
+ if (fd < 0) {
+ kdc_log(context, config, 0,
+ "PK-INIT failed to open ocsp data file %d", errno);
+ goto out_ocsp;
+ }
+ ret = fstat(fd, &sb);
+ if (ret) {
+ ret = errno;
+ close(fd);
+ kdc_log(context, config, 0,
+ "PK-INIT failed to stat ocsp data %d", ret);
+ goto out_ocsp;
+ }
+
+ ret = krb5_data_alloc(&ocsp.data, sb.st_size);
+ if (ret) {
+ close(fd);
+ kdc_log(context, config, 0,
+ "PK-INIT failed to stat ocsp data %d", ret);
+ goto out_ocsp;
+ }
+ ocsp.data.length = sb.st_size;
+ ret = read(fd, ocsp.data.data, sb.st_size);
+ close(fd);
+ if (ret != sb.st_size) {
+ kdc_log(context, config, 0,
+ "PK-INIT failed to read ocsp data %d", errno);
+ goto out_ocsp;
+ }
+
+ ret = hx509_ocsp_verify(kdc_identity->hx509ctx,
+ kdc_time,
+ kdc_cert,
+ 0,
+ ocsp.data.data, ocsp.data.length,
+ &ocsp.expire);
+ if (ret) {
+ kdc_log(context, config, 0,
+ "PK-INIT failed to verify ocsp data %d", ret);
+ krb5_data_free(&ocsp.data);
+ ocsp.expire = 0;
+ } else if (ocsp.expire > 180) {
+ ocsp.expire -= 180; /* refetch the ocsp before it expire */
+ ocsp.next_update = ocsp.expire;
+ } else {
+ ocsp.next_update = kdc_time;
+ }
+ out_ocsp:
+ ret = 0;
+ }
+
+ if (ocsp.expire != 0 && ocsp.expire > kdc_time) {
+
+ ret = krb5_padata_add(context, md,
+ KRB5_PADATA_PA_PK_OCSP_RESPONSE,
+ ocsp.data.data, ocsp.data.length);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ "Failed adding OCSP response %d", ret);
+ goto out;
+ }
+ }
+ }
+
+out:
+ if (kdc_cert)
+ hx509_cert_free(kdc_cert);
+
+ if (ret == 0)
+ *reply_key = &client_params->reply_key;
+ return ret;
+}
+
+static int
+match_rfc_san(krb5_context context,
+ krb5_kdc_configuration *config,
+ hx509_context hx509ctx,
+ hx509_cert client_cert,
+ krb5_const_principal match)
+{
+ hx509_octet_string_list list;
+ int ret, i, found = 0;
+
+ memset(&list, 0 , sizeof(list));
+
+ ret = hx509_cert_find_subjectAltName_otherName(hx509ctx,
+ client_cert,
+ oid_id_pkinit_san(),
+ &list);
+ if (ret)
+ goto out;
+
+ for (i = 0; !found && i < list.len; i++) {
+ krb5_principal_data principal;
+ KRB5PrincipalName kn;
+ size_t size;
+
+ ret = decode_KRB5PrincipalName(list.val[i].data,
+ list.val[i].length,
+ &kn, &size);
+ if (ret) {
+ kdc_log(context, config, 0,
+ "Decoding kerberos name in certificate failed: %s",
+ krb5_get_err_text(context, ret));
+ break;
+ }
+ if (size != list.val[i].length) {
+ kdc_log(context, config, 0,
+ "Decoding kerberos name have extra bits on the end");
+ return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
+ }
+
+ principal.name = kn.principalName;
+ principal.realm = kn.realm;
+
+ if (krb5_principal_compare(context, &principal, match) == TRUE)
+ found = 1;
+ free_KRB5PrincipalName(&kn);
+ }
+
+out:
+ hx509_free_octet_string_list(&list);
+ if (ret)
+ return ret;
+
+ if (!found)
+ return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
+
+ return 0;
+}
+
+static int
+match_ms_upn_san(krb5_context context,
+ krb5_kdc_configuration *config,
+ hx509_context hx509ctx,
+ hx509_cert client_cert,
+ krb5_const_principal match)
+{
+ hx509_octet_string_list list;
+ krb5_principal principal = NULL;
+ int ret, found = 0;
+ MS_UPN_SAN upn;
+ size_t size;
+
+ memset(&list, 0 , sizeof(list));
+
+ ret = hx509_cert_find_subjectAltName_otherName(hx509ctx,
+ client_cert,
+ oid_id_pkinit_ms_san(),
+ &list);
+ if (ret)
+ goto out;
+
+ if (list.len != 1) {
+ kdc_log(context, config, 0,
+ "More then one PK-INIT MS UPN SAN");
+ goto out;
+ }
+
+ ret = decode_MS_UPN_SAN(list.val[0].data, list.val[0].length, &upn, &size);
+ if (ret) {
+ kdc_log(context, config, 0, "Decode of MS-UPN-SAN failed");
+ goto out;
+ }
+
+ kdc_log(context, config, 0, "found MS UPN SAN: %s", upn);
+
+ ret = krb5_parse_name(context, upn, &principal);
+ free_MS_UPN_SAN(&upn);
+ if (ret) {
+ kdc_log(context, config, 0, "Failed to parse principal in MS UPN SAN");
+ goto out;
+ }
+
+ /*
+ * This is very wrong, but will do for now, should really and a
+ * plugin to the windc layer to very this ACL.
+ */
+ strupr(principal->realm);
+
+ if (krb5_principal_compare(context, principal, match) == TRUE)
+ found = 1;
+
+out:
+ if (principal)
+ krb5_free_principal(context, principal);
+ hx509_free_octet_string_list(&list);
+ if (ret)
+ return ret;
+
+ if (!found)
+ return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
+
+ return 0;
+}
+
+krb5_error_code
+_kdc_pk_check_client(krb5_context context,
+ krb5_kdc_configuration *config,
+ const hdb_entry_ex *client,
+ pk_client_params *client_params,
+ char **subject_name)
+{
+ const HDB_Ext_PKINIT_acl *acl;
+ krb5_error_code ret;
+ hx509_name name;
+ int i;
+
+ ret = hx509_cert_get_base_subject(kdc_identity->hx509ctx,
+ client_params->cert,
+ &name);
+ if (ret)
+ return ret;
+
+ ret = hx509_name_to_string(name, subject_name);
+ hx509_name_free(&name);
+ if (ret)
+ return ret;
+
+ kdc_log(context, config, 0,
+ "Trying to authorize PK-INIT subject DN %s",
+ *subject_name);
+
+ if (config->pkinit_princ_in_cert) {
+ ret = match_rfc_san(context, config,
+ kdc_identity->hx509ctx,
+ client_params->cert,
+ client->entry.principal);
+ if (ret == 0) {
+ kdc_log(context, config, 5,
+ "Found matching PK-INIT SAN in certificate");
+ return 0;
+ }
+ ret = match_ms_upn_san(context, config,
+ kdc_identity->hx509ctx,
+ client_params->cert,
+ client->entry.principal);
+ if (ret == 0) {
+ kdc_log(context, config, 5,
+ "Found matching MS UPN SAN in certificate");
+ return 0;
+ }
+ }
+
+ ret = hdb_entry_get_pkinit_acl(&client->entry, &acl);
+ if (ret == 0 && acl != NULL) {
+ /*
+ * Cheat here and compare the generated name with the string
+ * and not the reverse.
+ */
+ for (i = 0; i < acl->len; i++) {
+ if (strcmp(*subject_name, acl->val[0].subject) != 0)
+ continue;
+
+ /* Don't support isser and anchor checking right now */
+ if (acl->val[0].issuer)
+ continue;
+ if (acl->val[0].anchor)
+ continue;
+
+ kdc_log(context, config, 5,
+ "Found matching PK-INIT database ACL");
+ return 0;
+ }
+ }
+
+ for (i = 0; i < principal_mappings.len; i++) {
+ krb5_boolean b;
+
+ b = krb5_principal_compare(context,
+ client->entry.principal,
+ principal_mappings.val[i].principal);
+ if (b == FALSE)
+ continue;
+ if (strcmp(principal_mappings.val[i].subject, *subject_name) != 0)
+ continue;
+ kdc_log(context, config, 5,
+ "Found matching PK-INIT FILE ACL");
+ return 0;
+ }
+
+ ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
+ krb5_set_error_message(context, ret,
+ "PKINIT no matching principals for %s",
+ *subject_name);
+
+ kdc_log(context, config, 5,
+ "PKINIT no matching principals for %s",
+ *subject_name);
+
+ free(*subject_name);
+ *subject_name = NULL;
+
+ return ret;
+}
+
+static krb5_error_code
+add_principal_mapping(krb5_context context,
+ const char *principal_name,
+ const char * subject)
+{
+ struct pk_allowed_princ *tmp;
+ krb5_principal principal;
+ krb5_error_code ret;
+
+ tmp = realloc(principal_mappings.val,
+ (principal_mappings.len + 1) * sizeof(*tmp));
+ if (tmp == NULL)
+ return ENOMEM;
+ principal_mappings.val = tmp;
+
+ ret = krb5_parse_name(context, principal_name, &principal);
+ if (ret)
+ return ret;
+
+ principal_mappings.val[principal_mappings.len].principal = principal;
+
+ principal_mappings.val[principal_mappings.len].subject = strdup(subject);
+ if (principal_mappings.val[principal_mappings.len].subject == NULL) {
+ krb5_free_principal(context, principal);
+ return ENOMEM;
+ }
+ principal_mappings.len++;
+
+ return 0;
+}
+
+krb5_error_code
+_kdc_add_inital_verified_cas(krb5_context context,
+ krb5_kdc_configuration *config,
+ pk_client_params *params,
+ EncTicketPart *tkt)
+{
+ AD_INITIAL_VERIFIED_CAS cas;
+ krb5_error_code ret;
+ krb5_data data;
+ size_t size;
+
+ memset(&cas, 0, sizeof(cas));
+
+ /* XXX add CAs to cas here */
+
+ ASN1_MALLOC_ENCODE(AD_INITIAL_VERIFIED_CAS, data.data, data.length,
+ &cas, &size, ret);
+ if (ret)
+ return ret;
+ if (data.length != size)
+ krb5_abortx(context, "internal asn.1 encoder error");
+
+ ret = _kdc_tkt_add_if_relevant_ad(context, tkt,
+ KRB5_AUTHDATA_INITIAL_VERIFIED_CAS,
+ &data);
+ krb5_data_free(&data);
+ return ret;
+}
+
+/*
+ *
+ */
+
+static void
+load_mappings(krb5_context context, const char *fn)
+{
+ krb5_error_code ret;
+ char buf[1024];
+ unsigned long lineno = 0;
+ FILE *f;
+
+ f = fopen(fn, "r");
+ if (f == NULL)
+ return;
+
+ while (fgets(buf, sizeof(buf), f) != NULL) {
+ char *subject_name, *p;
+
+ buf[strcspn(buf, "\n")] = '\0';
+ lineno++;
+
+ p = buf + strspn(buf, " \t");
+
+ if (*p == '#' || *p == '\0')
+ continue;
+
+ subject_name = strchr(p, ':');
+ if (subject_name == NULL) {
+ krb5_warnx(context, "pkinit mapping file line %lu "
+ "missing \":\" :%s",
+ lineno, buf);
+ continue;
+ }
+ *subject_name++ = '\0';
+
+ ret = add_principal_mapping(context, p, subject_name);
+ if (ret) {
+ krb5_warn(context, ret, "failed to add line %lu \":\" :%s\n",
+ lineno, buf);
+ continue;
+ }
+ }
+
+ fclose(f);
+}
+
+/*
+ *
+ */
+
+krb5_error_code
+_kdc_pk_initialize(krb5_context context,
+ krb5_kdc_configuration *config,
+ const char *user_id,
+ const char *anchors,
+ char **pool,
+ char **revoke_list)
+{
+ const char *file;
+ char *fn = NULL;
+ krb5_error_code ret;
+
+ file = krb5_config_get_string(context, NULL,
+ "libdefaults", "moduli", NULL);
+
+ ret = _krb5_parse_moduli(context, file, &moduli);
+ if (ret)
+ krb5_err(context, 1, ret, "PKINIT: failed to load modidi file");
+
+ principal_mappings.len = 0;
+ principal_mappings.val = NULL;
+
+ ret = _krb5_pk_load_id(context,
+ &kdc_identity,
+ user_id,
+ anchors,
+ pool,
+ revoke_list,
+ NULL,
+ NULL,
+ NULL);
+ if (ret) {
+ krb5_warn(context, ret, "PKINIT: ");
+ config->enable_pkinit = 0;
+ return ret;
+ }
+
+ {
+ hx509_query *q;
+ hx509_cert cert;
+
+ ret = hx509_query_alloc(kdc_identity->hx509ctx, &q);
+ if (ret) {
+ krb5_warnx(context, "PKINIT: out of memory");
+ return ENOMEM;
+ }
+
+ hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
+ hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
+
+ ret = hx509_certs_find(kdc_identity->hx509ctx,
+ kdc_identity->certs,
+ q,
+ &cert);
+ hx509_query_free(kdc_identity->hx509ctx, q);
+ if (ret == 0) {
+ if (hx509_cert_check_eku(kdc_identity->hx509ctx, cert,
+ oid_id_pkkdcekuoid(), 0))
+ krb5_warnx(context, "WARNING Found KDC certificate "
+ "is missing the PK-INIT KDC EKU, this is bad for "
+ "interoperability.");
+ hx509_cert_free(cert);
+ } else
+ krb5_warnx(context, "PKINIT: failed to find a signing "
+ "certifiate with a public key");
+ }
+
+ ret = krb5_config_get_bool_default(context,
+ NULL,
+ FALSE,
+ "kdc",
+ "pkinit_allow_proxy_certificate",
+ NULL);
+ _krb5_pk_allow_proxy_certificate(kdc_identity, ret);
+
+ file = krb5_config_get_string(context,
+ NULL,
+ "kdc",
+ "pkinit_mappings_file",
+ NULL);
+ if (file == NULL) {
+ asprintf(&fn, "%s/pki-mapping", hdb_db_dir(context));
+ file = fn;
+ }
+
+ load_mappings(context, file);
+ if (fn)
+ free(fn);
+
+ return 0;
+}
+
+#endif /* PKINIT */
diff --git a/source4/heimdal/kdc/process.c b/source4/heimdal/kdc/process.c
new file mode 100644
index 0000000000..a27911914b
--- /dev/null
+++ b/source4/heimdal/kdc/process.c
@@ -0,0 +1,220 @@
+/*
+ * Copyright (c) 1997-2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kdc_locl.h"
+
+RCSID("$Id$");
+
+/*
+ *
+ */
+
+void
+krb5_kdc_update_time(struct timeval *tv)
+{
+ if (tv == NULL)
+ gettimeofday(&_kdc_now, NULL);
+ else
+ _kdc_now = *tv;
+}
+
+/*
+ * handle the request in `buf, len', from `addr' (or `from' as a string),
+ * sending a reply in `reply'.
+ */
+
+int
+krb5_kdc_process_request(krb5_context context,
+ krb5_kdc_configuration *config,
+ unsigned char *buf,
+ size_t len,
+ krb5_data *reply,
+ krb5_boolean *prependlength,
+ const char *from,
+ struct sockaddr *addr,
+ int datagram_reply)
+{
+ KDC_REQ req;
+ Ticket ticket;
+ DigestREQ digestreq;
+ Kx509Request kx509req;
+ krb5_error_code ret;
+ size_t i;
+
+ if(decode_AS_REQ(buf, len, &req, &i) == 0){
+ krb5_data req_buffer;
+
+ req_buffer.data = buf;
+ req_buffer.length = len;
+
+ ret = _kdc_as_rep(context, config, &req, &req_buffer,
+ reply, from, addr, datagram_reply);
+ free_AS_REQ(&req);
+ return ret;
+ }else if(decode_TGS_REQ(buf, len, &req, &i) == 0){
+ ret = _kdc_tgs_rep(context, config, &req, reply, from, addr, datagram_reply);
+ free_TGS_REQ(&req);
+ return ret;
+ }else if(decode_Ticket(buf, len, &ticket, &i) == 0){
+ ret = _kdc_do_524(context, config, &ticket, reply, from, addr);
+ free_Ticket(&ticket);
+ return ret;
+ }else if(decode_DigestREQ(buf, len, &digestreq, &i) == 0){
+ ret = _kdc_do_digest(context, config, &digestreq, reply, from, addr);
+ free_DigestREQ(&digestreq);
+ return ret;
+ } else if (_kdc_try_kx509_request(buf, len, &kx509req, &i) == 0) {
+ ret = _kdc_do_kx509(context, config, &kx509req, reply, from, addr);
+ free_Kx509Request(&kx509req);
+ return ret;
+ } else if(_kdc_maybe_version4(buf, len)){
+ *prependlength = FALSE; /* elbitapmoc sdrawkcab XXX */
+ ret = _kdc_do_version4(context, config, buf, len, reply, from,
+ (struct sockaddr_in*)addr);
+ return ret;
+ } else if (config->enable_kaserver) {
+ ret = _kdc_do_kaserver(context, config, buf, len, reply, from,
+ (struct sockaddr_in*)addr);
+ return ret;
+ }
+
+ return -1;
+}
+
+/*
+ * handle the request in `buf, len', from `addr' (or `from' as a string),
+ * sending a reply in `reply'.
+ *
+ * This only processes krb5 requests
+ */
+
+int
+krb5_kdc_process_krb5_request(krb5_context context,
+ krb5_kdc_configuration *config,
+ unsigned char *buf,
+ size_t len,
+ krb5_data *reply,
+ const char *from,
+ struct sockaddr *addr,
+ int datagram_reply)
+{
+ KDC_REQ req;
+ krb5_error_code ret;
+ size_t i;
+
+ if(decode_AS_REQ(buf, len, &req, &i) == 0){
+ krb5_data req_buffer;
+
+ req_buffer.data = buf;
+ req_buffer.length = len;
+
+ ret = _kdc_as_rep(context, config, &req, &req_buffer,
+ reply, from, addr, datagram_reply);
+ free_AS_REQ(&req);
+ return ret;
+ }else if(decode_TGS_REQ(buf, len, &req, &i) == 0){
+ ret = _kdc_tgs_rep(context, config, &req, reply, from, addr, datagram_reply);
+ free_TGS_REQ(&req);
+ return ret;
+ }
+ return -1;
+}
+
+/*
+ *
+ */
+
+int
+krb5_kdc_save_request(krb5_context context,
+ const char *fn,
+ const unsigned char *buf,
+ size_t len,
+ const krb5_data *reply,
+ const struct sockaddr *sa)
+{
+ krb5_storage *sp;
+ krb5_address a;
+ int fd, ret;
+ uint32_t t;
+ krb5_data d;
+
+ memset(&a, 0, sizeof(a));
+
+ d.data = rk_UNCONST(buf);
+ d.length = len;
+ t = _kdc_now.tv_sec;
+
+ fd = open(fn, O_WRONLY|O_CREAT|O_APPEND, 0600);
+ if (fd < 0) {
+ int saved_errno = errno;
+ krb5_set_error_message(context, saved_errno, "Failed to open: %s", fn);
+ return saved_errno;
+ }
+
+ sp = krb5_storage_from_fd(fd);
+ close(fd);
+ if (sp == NULL) {
+ krb5_set_error_message(context, ENOMEM, "Storage failed to open fd");
+ return ENOMEM;
+ }
+
+ ret = krb5_sockaddr2address(context, sa, &a);
+ if (ret)
+ goto out;
+
+ krb5_store_uint32(sp, 1);
+ krb5_store_uint32(sp, t);
+ krb5_store_address(sp, a);
+ krb5_store_data(sp, d);
+ {
+ Der_class cl;
+ Der_type ty;
+ unsigned int tag;
+ ret = der_get_tag (reply->data, reply->length,
+ &cl, &ty, &tag, NULL);
+ if (ret) {
+ krb5_store_uint32(sp, 0xffffffff);
+ krb5_store_uint32(sp, 0xffffffff);
+ } else {
+ krb5_store_uint32(sp, MAKE_TAG(cl, ty, 0));
+ krb5_store_uint32(sp, tag);
+ }
+ }
+
+ krb5_free_address(context, &a);
+out:
+ krb5_storage_free(sp);
+
+ return 0;
+}
diff --git a/source4/heimdal/kdc/rx.h b/source4/heimdal/kdc/rx.h
new file mode 100644
index 0000000000..f914e93e6e
--- /dev/null
+++ b/source4/heimdal/kdc/rx.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 1997 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef __RX_H__
+#define __RX_H__
+
+/* header of a RPC packet */
+
+enum rx_header_type {
+ HT_DATA = 1,
+ HT_ACK = 2,
+ HT_BUSY = 3,
+ HT_ABORT = 4,
+ HT_ACKALL = 5,
+ HT_CHAL = 6,
+ HT_RESP = 7,
+ HT_DEBUG = 8
+};
+
+/* For flags in header */
+
+enum rx_header_flag {
+ HF_CLIENT_INITIATED = 1,
+ HF_REQ_ACK = 2,
+ HF_LAST = 4,
+ HF_MORE = 8
+};
+
+struct rx_header {
+ uint32_t epoch;
+ uint32_t connid; /* And channel ID */
+ uint32_t callid;
+ uint32_t seqno;
+ uint32_t serialno;
+ u_char type;
+ u_char flags;
+ u_char status;
+ u_char secindex;
+ uint16_t reserved; /* ??? verifier? */
+ uint16_t serviceid;
+/* This should be the other way around according to everything but */
+/* tcpdump */
+};
+
+#define RX_HEADER_SIZE 28
+
+#endif /* __RX_H__ */
diff --git a/source4/heimdal/kdc/windc.c b/source4/heimdal/kdc/windc.c
new file mode 100644
index 0000000000..fe3cd997e7
--- /dev/null
+++ b/source4/heimdal/kdc/windc.c
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kdc_locl.h"
+
+RCSID("$Id$");
+
+static krb5plugin_windc_ftable *windcft;
+static void *windcctx;
+
+/*
+ * Pick the first WINDC module that we find.
+ */
+
+krb5_error_code
+krb5_kdc_windc_init(krb5_context context)
+{
+ struct krb5_plugin *list = NULL, *e;
+ krb5_error_code ret;
+
+ ret = _krb5_plugin_find(context, PLUGIN_TYPE_DATA, "windc", &list);
+ if(ret != 0 || list == NULL)
+ return 0;
+
+ for (e = list; e != NULL; e = _krb5_plugin_get_next(e)) {
+
+ windcft = _krb5_plugin_get_symbol(e);
+ if (windcft->minor_version < KRB5_WINDC_PLUGING_MINOR)
+ continue;
+
+ (*windcft->init)(context, &windcctx);
+ break;
+ }
+ if (e == NULL) {
+ _krb5_plugin_free(list);
+ krb5_set_error_message(context, ENOENT, "Did not find any WINDC plugin");
+ windcft = NULL;
+ return ENOENT;
+ }
+
+ return 0;
+}
+
+
+krb5_error_code
+_kdc_pac_generate(krb5_context context,
+ hdb_entry_ex *client,
+ krb5_pac *pac)
+{
+ *pac = NULL;
+ if (windcft == NULL)
+ return 0;
+ return (windcft->pac_generate)(windcctx, context, client, pac);
+}
+
+krb5_error_code
+_kdc_pac_verify(krb5_context context,
+ const krb5_principal client_principal,
+ hdb_entry_ex *client,
+ hdb_entry_ex *server,
+ krb5_pac *pac)
+{
+ if (windcft == NULL) {
+ krb5_set_error_message(context, EINVAL, "Can't verify PAC, no function");
+ return EINVAL;
+ }
+ return (windcft->pac_verify)(windcctx, context,
+ client_principal, client, server, pac);
+}
+
+krb5_error_code
+_kdc_windc_client_access(krb5_context context,
+ struct hdb_entry_ex *client,
+ KDC_REQ *req,
+ krb5_data *e_data)
+{
+ if (windcft == NULL)
+ return 0;
+ return (windcft->client_access)(windcctx, context, client, req, e_data);
+}
diff --git a/source4/heimdal/kdc/windc_plugin.h b/source4/heimdal/kdc/windc_plugin.h
new file mode 100644
index 0000000000..34016694b2
--- /dev/null
+++ b/source4/heimdal/kdc/windc_plugin.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef HEIMDAL_KRB5_PAC_PLUGIN_H
+#define HEIMDAL_KRB5_PAC_PLUGIN_H 1
+
+#include <krb5.h>
+
+/*
+ * The PAC generate function should allocate a krb5_pac using
+ * krb5_pac_init and fill in the PAC structure for the principal using
+ * krb5_pac_add_buffer.
+ *
+ * The PAC verify function should verify all components in the PAC
+ * using krb5_pac_get_types and krb5_pac_get_buffer for all types.
+ *
+ * Check client access function check if the client is authorized.
+ */
+
+struct hdb_entry_ex;
+
+typedef krb5_error_code
+(*krb5plugin_windc_pac_generate)(void *, krb5_context,
+ struct hdb_entry_ex *, krb5_pac *);
+
+typedef krb5_error_code
+(*krb5plugin_windc_pac_verify)(void *, krb5_context,
+ const krb5_principal,
+ struct hdb_entry_ex *,
+ struct hdb_entry_ex *,
+ krb5_pac *);
+
+typedef krb5_error_code
+(*krb5plugin_windc_client_access)(
+ void *, krb5_context, struct hdb_entry_ex *, KDC_REQ *, krb5_data *);
+
+
+#define KRB5_WINDC_PLUGING_MINOR 3
+
+typedef struct krb5plugin_windc_ftable {
+ int minor_version;
+ krb5_error_code (*init)(krb5_context, void **);
+ void (*fini)(void *);
+ krb5plugin_windc_pac_generate pac_generate;
+ krb5plugin_windc_pac_verify pac_verify;
+ krb5plugin_windc_client_access client_access;
+} krb5plugin_windc_ftable;
+
+#endif /* HEIMDAL_KRB5_PAC_PLUGIN_H */
+
diff --git a/source4/heimdal/kpasswd/kpasswd.c b/source4/heimdal/kpasswd/kpasswd.c
new file mode 100644
index 0000000000..f67130a052
--- /dev/null
+++ b/source4/heimdal/kpasswd/kpasswd.c
@@ -0,0 +1,247 @@
+/*
+ * Copyright (c) 1997-2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kpasswd_locl.h"
+RCSID("$Id$");
+
+static int version_flag;
+static int help_flag;
+static char *admin_principal_str;
+static char *cred_cache_str;
+
+static struct getargs args[] = {
+ { "admin-principal", 0, arg_string, &admin_principal_str },
+ { "cache", 'c', arg_string, &cred_cache_str },
+ { "version", 0, arg_flag, &version_flag },
+ { "help", 0, arg_flag, &help_flag }
+};
+
+static void
+usage (int ret, struct getargs *a, int num_args)
+{
+ arg_printusage (a, num_args, NULL, "[principal ...]");
+ exit (ret);
+}
+
+static int
+change_password(krb5_context context,
+ krb5_principal principal,
+ krb5_ccache id)
+{
+ krb5_data result_code_string, result_string;
+ int result_code;
+ krb5_error_code ret;
+ char pwbuf[BUFSIZ];
+ char *msg, *name;
+
+ krb5_data_zero (&result_code_string);
+ krb5_data_zero (&result_string);
+
+ name = msg = NULL;
+ if (principal == NULL)
+ asprintf(&msg, "New password: ");
+ else {
+ ret = krb5_unparse_name(context, principal, &name);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_unparse_name");
+
+ asprintf(&msg, "New password for %s: ", name);
+ }
+
+ if (msg == NULL)
+ krb5_errx (context, 1, "out of memory");
+
+ ret = UI_UTIL_read_pw_string (pwbuf, sizeof(pwbuf), msg, 1);
+ free(msg);
+ if (name)
+ free(name);
+ if (ret != 0) {
+ return 1;
+ }
+
+ ret = krb5_set_password_using_ccache (context, id, pwbuf,
+ principal,
+ &result_code,
+ &result_code_string,
+ &result_string);
+ if (ret) {
+ krb5_warn (context, ret, "krb5_set_password_using_ccache");
+ return 1;
+ }
+
+ printf ("%s%s%.*s\n", krb5_passwd_result_to_string(context, result_code),
+ result_string.length > 0 ? " : " : "",
+ (int)result_string.length,
+ result_string.length > 0 ? (char *)result_string.data : "");
+
+ krb5_data_free (&result_code_string);
+ krb5_data_free (&result_string);
+
+ return ret != 0;
+}
+
+
+int
+main (int argc, char **argv)
+{
+ krb5_error_code ret;
+ krb5_context context;
+ krb5_principal principal;
+ int optind = 0;
+ krb5_get_init_creds_opt *opt;
+ krb5_ccache id = NULL;
+ int exit_value;
+
+ optind = krb5_program_setup(&context, argc, argv,
+ args, sizeof(args) / sizeof(args[0]), usage);
+
+ if (help_flag)
+ usage (0, args, sizeof(args) / sizeof(args[0]));
+
+ if(version_flag){
+ print_version (NULL);
+ exit(0);
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ ret = krb5_init_context (&context);
+ if (ret)
+ errx (1, "krb5_init_context failed: %d", ret);
+
+ ret = krb5_get_init_creds_opt_alloc (context, &opt);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_get_init_creds_opt_alloc");
+
+ krb5_get_init_creds_opt_set_tkt_life (opt, 300);
+ krb5_get_init_creds_opt_set_forwardable (opt, FALSE);
+ krb5_get_init_creds_opt_set_proxiable (opt, FALSE);
+
+ if (cred_cache_str) {
+ ret = krb5_cc_resolve(context, cred_cache_str, &id);
+ if (ret)
+ krb5_err (context, 1, ret, "krb5_cc_resolve");
+ } else {
+ ret = krb5_cc_gen_new(context, &krb5_mcc_ops, &id);
+ if (ret)
+ krb5_err (context, 1, ret, "krb5_cc_gen_new");
+ }
+
+ if (cred_cache_str == NULL) {
+ krb5_principal admin_principal = NULL;
+ krb5_creds cred;
+
+ if (admin_principal_str) {
+ ret = krb5_parse_name (context, admin_principal_str,
+ &admin_principal);
+ if (ret)
+ krb5_err (context, 1, ret, "krb5_parse_name");
+ } else if (argc == 1) {
+ ret = krb5_parse_name (context, argv[0], &admin_principal);
+ if (ret)
+ krb5_err (context, 1, ret, "krb5_parse_name");
+ } else {
+ ret = krb5_get_default_principal (context, &admin_principal);
+ if (ret)
+ krb5_err (context, 1, ret, "krb5_get_default_principal");
+ }
+
+ ret = krb5_get_init_creds_password (context,
+ &cred,
+ admin_principal,
+ NULL,
+ krb5_prompter_posix,
+ NULL,
+ 0,
+ "kadmin/changepw",
+ opt);
+ switch (ret) {
+ case 0:
+ break;
+ case KRB5_LIBOS_PWDINTR :
+ return 1;
+ case KRB5KRB_AP_ERR_BAD_INTEGRITY :
+ case KRB5KRB_AP_ERR_MODIFIED :
+ krb5_errx(context, 1, "Password incorrect");
+ break;
+ default:
+ krb5_err(context, 1, ret, "krb5_get_init_creds");
+ }
+
+ krb5_get_init_creds_opt_free(context, opt);
+
+ ret = krb5_cc_initialize(context, id, admin_principal);
+ krb5_free_principal(context, admin_principal);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_initialize");
+
+ ret = krb5_cc_store_cred(context, id, &cred);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_store_cred");
+
+ krb5_free_cred_contents (context, &cred);
+ }
+
+ if (argc == 0) {
+ exit_value = change_password(context, NULL, id);
+ } else {
+ exit_value = 0;
+
+ while (argc-- > 0) {
+
+ ret = krb5_parse_name (context, argv[0], &principal);
+ if (ret)
+ krb5_err (context, 1, ret, "krb5_parse_name");
+
+ ret = change_password(context, principal, id);
+ if (ret)
+ exit_value = 1;
+ krb5_free_principal(context, principal);
+ argv++;
+ }
+ }
+
+ if (cred_cache_str == NULL) {
+ ret = krb5_cc_destroy(context, id);
+ if (ret)
+ krb5_err (context, 1, ret, "krb5_cc_destroy");
+ } else {
+ ret = krb5_cc_close(context, id);
+ if (ret)
+ krb5_err (context, 1, ret, "krb5_cc_close");
+ }
+
+ krb5_free_context (context);
+ return exit_value;
+}
diff --git a/source4/heimdal/kpasswd/kpasswd_locl.h b/source4/heimdal/kpasswd/kpasswd_locl.h
new file mode 100644
index 0000000000..a1ed2e3762
--- /dev/null
+++ b/source4/heimdal/kpasswd/kpasswd_locl.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef __KPASSWD_LOCL_H__
+#define __KPASSWD_LOCL_H__
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETINET_IN6_H
+#include <netinet/in6.h>
+#endif
+#ifdef HAVE_NETINET6_IN6_H
+#include <netinet6/in6.h>
+#endif
+
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+#ifdef HAVE_UTIL_H
+#include <util.h>
+#endif
+#ifdef HAVE_LIBUTIL_H
+#include <libutil.h>
+#endif
+#include <err.h>
+#include <roken.h>
+#include <getarg.h>
+#include <krb5.h>
+#include "crypto-headers.h" /* for des_read_pw_string */
+
+#endif /* __KPASSWD_LOCL_H__ */
diff --git a/source4/heimdal/kuser/kinit.c b/source4/heimdal/kuser/kinit.c
new file mode 100644
index 0000000000..fbb2d2287b
--- /dev/null
+++ b/source4/heimdal/kuser/kinit.c
@@ -0,0 +1,901 @@
+/*
+ * Copyright (c) 1997-2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kuser_locl.h"
+RCSID("$Id$");
+
+#include "krb5-v4compat.h"
+
+#include "heimntlm.h"
+
+int forwardable_flag = -1;
+int proxiable_flag = -1;
+int renewable_flag = -1;
+int renew_flag = 0;
+int pac_flag = -1;
+int validate_flag = 0;
+int version_flag = 0;
+int help_flag = 0;
+int addrs_flag = -1;
+struct getarg_strings extra_addresses;
+int anonymous_flag = 0;
+char *lifetime = NULL;
+char *renew_life = NULL;
+char *server_str = NULL;
+char *cred_cache = NULL;
+char *start_str = NULL;
+struct getarg_strings etype_str;
+int use_keytab = 0;
+char *keytab_str = NULL;
+int do_afslog = -1;
+#ifndef HEIMDAL_SMALLER
+int get_v4_tgt = -1;
+int convert_524 = 0;
+static char *krb4_cc_name;
+#endif
+int fcache_version;
+char *password_file = NULL;
+char *pk_user_id = NULL;
+char *pk_x509_anchors = NULL;
+int pk_use_enckey = 0;
+static int canonicalize_flag = 0;
+static int ok_as_delegate_flag = 0;
+static int use_referrals_flag = 0;
+static int windows_flag = 0;
+static char *ntlm_domain;
+
+
+static struct getargs args[] = {
+ /*
+ * used by MIT
+ * a: ~A
+ * V: verbose
+ * F: ~f
+ * P: ~p
+ * C: v4 cache name?
+ * 5:
+ */
+#ifndef HEIMDAL_SMALLER
+ { "524init", '4', arg_flag, &get_v4_tgt,
+ NP_("obtain version 4 TGT", "") },
+
+ { "524convert", '9', arg_flag, &convert_524,
+ NP_("only convert ticket to version 4", "") },
+#endif
+ { "afslog", 0 , arg_flag, &do_afslog,
+ NP_("obtain afs tokens", "") },
+
+ { "cache", 'c', arg_string, &cred_cache,
+ NP_("credentials cache", ""), "cachename" },
+
+ { "forwardable", 'f', arg_flag, &forwardable_flag,
+ NP_("get forwardable tickets", "")},
+
+ { "keytab", 't', arg_string, &keytab_str,
+ NP_("keytab to use", ""), "keytabname" },
+
+ { "lifetime", 'l', arg_string, &lifetime,
+ NP_("lifetime of tickets", ""), "time"},
+
+ { "proxiable", 'p', arg_flag, &proxiable_flag,
+ NP_("get proxiable tickets", "") },
+
+ { "renew", 'R', arg_flag, &renew_flag,
+ NP_("renew TGT", "") },
+
+ { "renewable", 0, arg_flag, &renewable_flag,
+ NP_("get renewable tickets", "") },
+
+ { "renewable-life", 'r', arg_string, &renew_life,
+ NP_("renewable lifetime of tickets", ""), "time" },
+
+ { "server", 'S', arg_string, &server_str,
+ NP_("server to get ticket for", ""), "principal" },
+
+ { "start-time", 's', arg_string, &start_str,
+ NP_("when ticket gets valid", ""), "time" },
+
+ { "use-keytab", 'k', arg_flag, &use_keytab,
+ NP_("get key from keytab", "") },
+
+ { "validate", 'v', arg_flag, &validate_flag,
+ NP_("validate TGT", "") },
+
+ { "enctypes", 'e', arg_strings, &etype_str,
+ NP_("encryption types to use", ""), "enctypes" },
+
+ { "fcache-version", 0, arg_integer, &fcache_version,
+ NP_("file cache version to create", "") },
+
+ { "addresses", 'A', arg_negative_flag, &addrs_flag,
+ NP_("request a ticket with no addresses", "") },
+
+ { "extra-addresses",'a', arg_strings, &extra_addresses,
+ NP_("include these extra addresses", ""), "addresses" },
+
+ { "anonymous", 0, arg_flag, &anonymous_flag,
+ NP_("request an anonymous ticket", "") },
+
+ { "request-pac", 0, arg_flag, &pac_flag,
+ NP_("request a Windows PAC", "") },
+
+ { "password-file", 0, arg_string, &password_file,
+ NP_("read the password from a file", "") },
+
+ { "canonicalize",0, arg_flag, &canonicalize_flag,
+ NP_("canonicalize client principal", "") },
+#ifdef PKINIT
+ { "pk-user", 'C', arg_string, &pk_user_id,
+ NP_("principal's public/private/certificate identifier", ""), "id" },
+
+ { "x509-anchors", 'D', arg_string, &pk_x509_anchors,
+ NP_("directory with CA certificates", ""), "directory" },
+
+ { "pk-use-enckey", 0, arg_flag, &pk_use_enckey,
+ NP_("Use RSA encrypted reply (instead of DH)", "") },
+#endif
+ { "ntlm-domain", 0, arg_string, &ntlm_domain,
+ NP_("NTLM domain", ""), "domain" },
+
+ { "ok-as-delegate", 0, arg_flag, &ok_as_delegate_flag,
+ NP_("honor ok-as-delegate on tickets", "") },
+
+ { "use-referrals", 0, arg_flag, &use_referrals_flag,
+ NP_("only use referrals, no dns canalisation", "") },
+
+ { "windows", 0, arg_flag, &windows_flag,
+ NP_("get windows behavior", "") },
+
+ { "version", 0, arg_flag, &version_flag },
+ { "help", 0, arg_flag, &help_flag }
+};
+
+static void
+usage (int ret)
+{
+ arg_printusage_i18n (args,
+ sizeof(args)/sizeof(*args),
+ N_("Usage: ", ""),
+ NULL,
+ "[principal [command]]",
+ getarg_i18n);
+ exit (ret);
+}
+
+static krb5_error_code
+get_server(krb5_context context,
+ krb5_principal client,
+ const char *server,
+ krb5_principal *princ)
+{
+ krb5_realm *client_realm;
+ if(server)
+ return krb5_parse_name(context, server, princ);
+
+ client_realm = krb5_princ_realm (context, client);
+ return krb5_make_principal(context, princ, *client_realm,
+ KRB5_TGS_NAME, *client_realm, NULL);
+}
+
+#ifndef HEIMDAL_SMALLER
+
+static krb5_error_code
+do_524init(krb5_context context, krb5_ccache ccache,
+ krb5_creds *creds, const char *server)
+{
+ krb5_error_code ret;
+
+ struct credentials c;
+ krb5_creds in_creds, *real_creds;
+
+ if(creds != NULL)
+ real_creds = creds;
+ else {
+ krb5_principal client;
+ krb5_cc_get_principal(context, ccache, &client);
+ memset(&in_creds, 0, sizeof(in_creds));
+ ret = get_server(context, client, server, &in_creds.server);
+ if(ret) {
+ krb5_free_principal(context, client);
+ return ret;
+ }
+ in_creds.client = client;
+ ret = krb5_get_credentials(context, 0, ccache, &in_creds, &real_creds);
+ krb5_free_principal(context, client);
+ krb5_free_principal(context, in_creds.server);
+ if(ret)
+ return ret;
+ }
+ ret = krb524_convert_creds_kdc_ccache(context, ccache, real_creds, &c);
+ if(ret)
+ krb5_warn(context, ret, "converting creds");
+ else {
+ krb5_error_code tret = _krb5_krb_tf_setup(context, &c, NULL, 0);
+ if(tret)
+ krb5_warn(context, tret, "saving v4 creds");
+ }
+
+ if(creds == NULL)
+ krb5_free_creds(context, real_creds);
+ memset(&c, 0, sizeof(c));
+
+ return ret;
+}
+
+#endif
+
+static int
+renew_validate(krb5_context context,
+ int renew,
+ int validate,
+ krb5_ccache cache,
+ const char *server,
+ krb5_deltat life)
+{
+ krb5_error_code ret;
+ krb5_creds in, *out = NULL;
+ krb5_kdc_flags flags;
+
+ memset(&in, 0, sizeof(in));
+
+ ret = krb5_cc_get_principal(context, cache, &in.client);
+ if(ret) {
+ krb5_warn(context, ret, "krb5_cc_get_principal");
+ return ret;
+ }
+ ret = get_server(context, in.client, server, &in.server);
+ if(ret) {
+ krb5_warn(context, ret, "get_server");
+ goto out;
+ }
+
+ if (renew) {
+ /*
+ * no need to check the error here, it's only to be
+ * friendly to the user
+ */
+ krb5_get_credentials(context, KRB5_GC_CACHED, cache, &in, &out);
+ }
+
+ flags.i = 0;
+ flags.b.renewable = flags.b.renew = renew;
+ flags.b.validate = validate;
+
+ if (forwardable_flag != -1)
+ flags.b.forwardable = forwardable_flag;
+ else if (out)
+ flags.b.forwardable = out->flags.b.forwardable;
+
+ if (proxiable_flag != -1)
+ flags.b.proxiable = proxiable_flag;
+ else if (out)
+ flags.b.proxiable = out->flags.b.proxiable;
+
+ if (anonymous_flag != -1)
+ flags.b.request_anonymous = anonymous_flag;
+ if(life)
+ in.times.endtime = time(NULL) + life;
+
+ if (out) {
+ krb5_free_creds (context, out);
+ out = NULL;
+ }
+
+
+ ret = krb5_get_kdc_cred(context,
+ cache,
+ flags,
+ NULL,
+ NULL,
+ &in,
+ &out);
+ if(ret) {
+ krb5_warn(context, ret, "krb5_get_kdc_cred");
+ goto out;
+ }
+ ret = krb5_cc_initialize(context, cache, in.client);
+ if(ret) {
+ krb5_free_creds (context, out);
+ krb5_warn(context, ret, "krb5_cc_initialize");
+ goto out;
+ }
+ ret = krb5_cc_store_cred(context, cache, out);
+
+ if(ret == 0 && server == NULL) {
+ /* only do this if it's a general renew-my-tgt request */
+#ifndef HEIMDAL_SMALLER
+ if(get_v4_tgt)
+ do_524init(context, cache, out, NULL);
+#endif
+ if(do_afslog && k_hasafs())
+ krb5_afslog(context, cache, NULL, NULL);
+ }
+
+ krb5_free_creds (context, out);
+ if(ret) {
+ krb5_warn(context, ret, "krb5_cc_store_cred");
+ goto out;
+ }
+out:
+ krb5_free_cred_contents(context, &in);
+ return ret;
+}
+
+static krb5_error_code
+store_ntlmkey(krb5_context context, krb5_ccache id,
+ const char *domain, struct ntlm_buf *buf)
+{
+ krb5_error_code ret;
+ krb5_data data;
+ char *name;
+
+ asprintf(&name, "ntlm-key-%s", domain);
+ if (name == NULL) {
+ krb5_clear_error_message(context);
+ return ENOMEM;
+ }
+
+ data.length = buf->length;
+ data.data = buf->data;
+
+ ret = krb5_cc_set_config(context, id, NULL, name, &data);
+ free(name);
+ return ret;
+}
+
+static krb5_error_code
+get_new_tickets(krb5_context context,
+ krb5_principal principal,
+ krb5_ccache ccache,
+ krb5_deltat ticket_life,
+ int interactive)
+{
+ krb5_error_code ret;
+ krb5_get_init_creds_opt *opt;
+ krb5_creds cred;
+ char passwd[256];
+ krb5_deltat start_time = 0;
+ krb5_deltat renew = 0;
+ char *renewstr = NULL;
+ krb5_enctype *enctype = NULL;
+ struct ntlm_buf ntlmkey;
+ krb5_ccache tempccache;
+
+ memset(&ntlmkey, 0, sizeof(ntlmkey));
+ passwd[0] = '\0';
+
+ if (password_file) {
+ FILE *f;
+
+ if (strcasecmp("STDIN", password_file) == 0)
+ f = stdin;
+ else
+ f = fopen(password_file, "r");
+ if (f == NULL)
+ krb5_errx(context, 1, "Failed to open the password file %s",
+ password_file);
+
+ if (fgets(passwd, sizeof(passwd), f) == NULL)
+ krb5_errx(context, 1,
+ N_("Failed to read password from file %s", ""),
+ password_file);
+ if (f != stdin)
+ fclose(f);
+ passwd[strcspn(passwd, "\n")] = '\0';
+ }
+
+
+ memset(&cred, 0, sizeof(cred));
+
+ ret = krb5_get_init_creds_opt_alloc (context, &opt);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_get_init_creds_opt_alloc");
+
+ krb5_get_init_creds_opt_set_default_flags(context, "kinit",
+ krb5_principal_get_realm(context, principal), opt);
+
+ if(forwardable_flag != -1)
+ krb5_get_init_creds_opt_set_forwardable (opt, forwardable_flag);
+ if(proxiable_flag != -1)
+ krb5_get_init_creds_opt_set_proxiable (opt, proxiable_flag);
+ if(anonymous_flag != -1)
+ krb5_get_init_creds_opt_set_anonymous (opt, anonymous_flag);
+ if (pac_flag != -1)
+ krb5_get_init_creds_opt_set_pac_request(context, opt,
+ pac_flag ? TRUE : FALSE);
+ if (canonicalize_flag)
+ krb5_get_init_creds_opt_set_canonicalize(context, opt, TRUE);
+ if (pk_user_id) {
+ ret = krb5_get_init_creds_opt_set_pkinit(context, opt,
+ principal,
+ pk_user_id,
+ pk_x509_anchors,
+ NULL,
+ NULL,
+ pk_use_enckey ? 2 : 0,
+ krb5_prompter_posix,
+ NULL,
+ passwd);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_get_init_creds_opt_set_pkinit");
+ }
+
+ if (addrs_flag != -1)
+ krb5_get_init_creds_opt_set_addressless(context, opt,
+ addrs_flag ? FALSE : TRUE);
+
+ if (renew_life == NULL && renewable_flag)
+ renewstr = "1 month";
+ if (renew_life)
+ renewstr = renew_life;
+ if (renewstr) {
+ renew = parse_time (renewstr, "s");
+ if (renew < 0)
+ errx (1, "unparsable time: %s", renewstr);
+
+ krb5_get_init_creds_opt_set_renew_life (opt, renew);
+ }
+
+ if(ticket_life != 0)
+ krb5_get_init_creds_opt_set_tkt_life (opt, ticket_life);
+
+ if(start_str) {
+ int tmp = parse_time (start_str, "s");
+ if (tmp < 0)
+ errx (1, N_("unparsable time: %s", ""), start_str);
+
+ start_time = tmp;
+ }
+
+ if(etype_str.num_strings) {
+ int i;
+
+ enctype = malloc(etype_str.num_strings * sizeof(*enctype));
+ if(enctype == NULL)
+ errx(1, "out of memory");
+ for(i = 0; i < etype_str.num_strings; i++) {
+ ret = krb5_string_to_enctype(context,
+ etype_str.strings[i],
+ &enctype[i]);
+ if(ret)
+ errx(1, "unrecognized enctype: %s", etype_str.strings[i]);
+ }
+ krb5_get_init_creds_opt_set_etype_list(opt, enctype,
+ etype_str.num_strings);
+ }
+
+ if(use_keytab || keytab_str) {
+ krb5_keytab kt;
+ if(keytab_str)
+ ret = krb5_kt_resolve(context, keytab_str, &kt);
+ else
+ ret = krb5_kt_default(context, &kt);
+ if (ret)
+ krb5_err (context, 1, ret, "resolving keytab");
+ ret = krb5_get_init_creds_keytab (context,
+ &cred,
+ principal,
+ kt,
+ start_time,
+ server_str,
+ opt);
+ krb5_kt_close(context, kt);
+ } else if (pk_user_id) {
+ ret = krb5_get_init_creds_password (context,
+ &cred,
+ principal,
+ passwd,
+ krb5_prompter_posix,
+ NULL,
+ start_time,
+ server_str,
+ opt);
+ } else if (!interactive) {
+ krb5_warnx(context, "Not interactive, failed to get initial ticket");
+ krb5_get_init_creds_opt_free(context, opt);
+ return 0;
+ } else {
+
+ if (passwd[0] == '\0') {
+ char *p, *prompt;
+
+ krb5_unparse_name (context, principal, &p);
+ asprintf (&prompt, N_("%s's Password: ", ""), p);
+ free (p);
+
+ if (UI_UTIL_read_pw_string(passwd, sizeof(passwd)-1, prompt, 0)){
+ memset(passwd, 0, sizeof(passwd));
+ exit(1);
+ }
+ free (prompt);
+ }
+
+
+ ret = krb5_get_init_creds_password (context,
+ &cred,
+ principal,
+ passwd,
+ krb5_prompter_posix,
+ NULL,
+ start_time,
+ server_str,
+ opt);
+ }
+ krb5_get_init_creds_opt_free(context, opt);
+ if (ntlm_domain && passwd[0])
+ heim_ntlm_nt_key(passwd, &ntlmkey);
+ memset(passwd, 0, sizeof(passwd));
+
+ switch(ret){
+ case 0:
+ break;
+ case KRB5_LIBOS_PWDINTR: /* don't print anything if it was just C-c:ed */
+ exit(1);
+ case KRB5KRB_AP_ERR_BAD_INTEGRITY:
+ case KRB5KRB_AP_ERR_MODIFIED:
+ case KRB5KDC_ERR_PREAUTH_FAILED:
+ krb5_errx(context, 1, N_("Password incorrect", ""));
+ break;
+ case KRB5KRB_AP_ERR_V4_REPLY:
+ krb5_errx(context, 1, N_("Looks like a Kerberos 4 reply", ""));
+ break;
+ default:
+ krb5_err(context, 1, ret, "krb5_get_init_creds");
+ }
+
+ if(ticket_life != 0) {
+ if(abs(cred.times.endtime - cred.times.starttime - ticket_life) > 30) {
+ char life[64];
+ unparse_time_approx(cred.times.endtime - cred.times.starttime,
+ life, sizeof(life));
+ krb5_warnx(context, N_("NOTICE: ticket lifetime is %s", ""), life);
+ }
+ }
+ if(renew_life) {
+ if(abs(cred.times.renew_till - cred.times.starttime - renew) > 30) {
+ char life[64];
+ unparse_time_approx(cred.times.renew_till - cred.times.starttime,
+ life, sizeof(life));
+ krb5_warnx(context,
+ N_("NOTICE: ticket renewable lifetime is %s", ""),
+ life);
+ }
+ }
+
+ ret = krb5_cc_new_unique(context, krb5_cc_get_type(context, ccache),
+ NULL, &tempccache);
+ if (ret)
+ krb5_err (context, 1, ret, "krb5_cc_new_unique");
+
+ ret = krb5_cc_initialize (context, tempccache, cred.client);
+ if (ret)
+ krb5_err (context, 1, ret, "krb5_cc_initialize");
+
+ ret = krb5_cc_store_cred (context, tempccache, &cred);
+ if (ret)
+ krb5_err (context, 1, ret, "krb5_cc_store_cred");
+
+ krb5_free_cred_contents (context, &cred);
+
+ ret = krb5_cc_move(context, tempccache, ccache);
+ if (ret)
+ krb5_err (context, 1, ret, "krb5_cc_move");
+
+ if (ntlm_domain && ntlmkey.data)
+ store_ntlmkey(context, ccache, ntlm_domain, &ntlmkey);
+
+ if (ok_as_delegate_flag || windows_flag || use_referrals_flag) {
+ unsigned char d = 0;
+ krb5_data data;
+
+ if (ok_as_delegate_flag || windows_flag)
+ d |= 1;
+ if (use_referrals_flag || windows_flag)
+ d |= 2;
+
+ data.length = 1;
+ data.data = &d;
+
+ krb5_cc_set_config(context, ccache, NULL, "realm-config", &data);
+ }
+
+
+ if (enctype)
+ free(enctype);
+
+ return 0;
+}
+
+static time_t
+ticket_lifetime(krb5_context context, krb5_ccache cache,
+ krb5_principal client, const char *server)
+{
+ krb5_creds in_cred, *cred;
+ krb5_error_code ret;
+ time_t timeout;
+
+ memset(&in_cred, 0, sizeof(in_cred));
+
+ ret = krb5_cc_get_principal(context, cache, &in_cred.client);
+ if(ret) {
+ krb5_warn(context, ret, "krb5_cc_get_principal");
+ return 0;
+ }
+ ret = get_server(context, in_cred.client, server, &in_cred.server);
+ if(ret) {
+ krb5_free_principal(context, in_cred.client);
+ krb5_warn(context, ret, "get_server");
+ return 0;
+ }
+
+ ret = krb5_get_credentials(context, KRB5_GC_CACHED,
+ cache, &in_cred, &cred);
+ krb5_free_principal(context, in_cred.client);
+ krb5_free_principal(context, in_cred.server);
+ if(ret) {
+ krb5_warn(context, ret, "krb5_get_credentials");
+ return 0;
+ }
+ timeout = cred->times.endtime - cred->times.starttime;
+ if (timeout < 0)
+ timeout = 0;
+ krb5_free_creds(context, cred);
+ return timeout;
+}
+
+struct renew_ctx {
+ krb5_context context;
+ krb5_ccache ccache;
+ krb5_principal principal;
+ krb5_deltat ticket_life;
+};
+
+static time_t
+renew_func(void *ptr)
+{
+ struct renew_ctx *ctx = ptr;
+ krb5_error_code ret;
+ time_t expire;
+ int new_tickets = 0;
+
+ if (renewable_flag) {
+ ret = renew_validate(ctx->context, renewable_flag, validate_flag,
+ ctx->ccache, server_str, ctx->ticket_life);
+ if (ret)
+ new_tickets = 1;
+ } else
+ new_tickets = 1;
+
+ if (new_tickets)
+ get_new_tickets(ctx->context, ctx->principal,
+ ctx->ccache, ctx->ticket_life, 0);
+
+#ifndef HEIMDAL_SMALLER
+ if(get_v4_tgt || convert_524)
+ do_524init(ctx->context, ctx->ccache, NULL, server_str);
+#endif
+ if(do_afslog && k_hasafs())
+ krb5_afslog(ctx->context, ctx->ccache, NULL, NULL);
+
+ expire = ticket_lifetime(ctx->context, ctx->ccache, ctx->principal,
+ server_str) / 2;
+ return expire + 1;
+}
+
+int
+main (int argc, char **argv)
+{
+ krb5_error_code ret;
+ krb5_context context;
+ krb5_ccache ccache;
+ krb5_principal principal;
+ int optidx = 0;
+ krb5_deltat ticket_life = 0;
+ int parseflags = 0;
+
+ setprogname (argv[0]);
+
+ setlocale (LC_ALL, "");
+#if defined(HEIMDAL_LOCALEDIR)
+ bindtextdomain ("heimdal_kuser", HEIMDAL_LOCALEDIR);
+ textdomain("heimdal_kuser");
+#endif
+
+ ret = krb5_init_context (&context);
+ if (ret == KRB5_CONFIG_BADFORMAT)
+ errx (1, "krb5_init_context failed to parse configuration file");
+ else if (ret)
+ errx(1, "krb5_init_context failed: %d", ret);
+
+ if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
+ usage(1);
+
+ if (help_flag)
+ usage (0);
+
+ if(version_flag) {
+ print_version(NULL);
+ exit(0);
+ }
+
+ argc -= optidx;
+ argv += optidx;
+
+ if (canonicalize_flag)
+ parseflags |= KRB5_PRINCIPAL_PARSE_ENTERPRISE;
+
+ if (argv[0]) {
+ ret = krb5_parse_name_flags (context, argv[0], parseflags, &principal);
+ if (ret)
+ krb5_err (context, 1, ret, "krb5_parse_name");
+ } else {
+ ret = krb5_get_default_principal (context, &principal);
+ if (ret)
+ krb5_err (context, 1, ret, "krb5_get_default_principal");
+ }
+
+ if(fcache_version)
+ krb5_set_fcache_version(context, fcache_version);
+
+ if(renewable_flag == -1)
+ /* this seems somewhat pointless, but whatever */
+ krb5_appdefault_boolean(context, "kinit",
+ krb5_principal_get_realm(context, principal),
+ "renewable", FALSE, &renewable_flag);
+#ifndef HEIMDAL_SMALLER
+ if(get_v4_tgt == -1)
+ krb5_appdefault_boolean(context, "kinit",
+ krb5_principal_get_realm(context, principal),
+ "krb4_get_tickets", FALSE, &get_v4_tgt);
+#endif
+ if(do_afslog == -1)
+ krb5_appdefault_boolean(context, "kinit",
+ krb5_principal_get_realm(context, principal),
+ "afslog", TRUE, &do_afslog);
+
+ if(cred_cache)
+ ret = krb5_cc_resolve(context, cred_cache, &ccache);
+ else {
+ if(argc > 1) {
+ char s[1024];
+ ret = krb5_cc_gen_new(context, &krb5_fcc_ops, &ccache);
+ if(ret)
+ krb5_err(context, 1, ret, "creating cred cache");
+ snprintf(s, sizeof(s), "%s:%s",
+ krb5_cc_get_type(context, ccache),
+ krb5_cc_get_name(context, ccache));
+ setenv("KRB5CCNAME", s, 1);
+#ifndef HEIMDAL_SMALLER
+ if (get_v4_tgt) {
+ int fd;
+ if (asprintf(&krb4_cc_name, "%s_XXXXXX", TKT_ROOT) < 0)
+ krb5_errx(context, 1, "out of memory");
+ if((fd = mkstemp(krb4_cc_name)) >= 0) {
+ close(fd);
+ setenv("KRBTKFILE", krb4_cc_name, 1);
+ } else {
+ free(krb4_cc_name);
+ krb4_cc_name = NULL;
+ }
+ }
+#endif
+ } else {
+ ret = krb5_cc_cache_match(context, principal, &ccache);
+ if (ret)
+ ret = krb5_cc_default (context, &ccache);
+ }
+ }
+ if (ret)
+ krb5_err (context, 1, ret, N_("resolving credentials cache", ""));
+
+ if(argc > 1 && k_hasafs ())
+ k_setpag();
+
+ if (lifetime) {
+ int tmp = parse_time (lifetime, "s");
+ if (tmp < 0)
+ errx (1, N_("unparsable time: %s", ""), lifetime);
+
+ ticket_life = tmp;
+ }
+
+ if(addrs_flag == 0 && extra_addresses.num_strings > 0)
+ krb5_errx(context, 1,
+ N_("specifying both extra addresses and "
+ "no addresses makes no sense", ""));
+ {
+ int i;
+ krb5_addresses addresses;
+ memset(&addresses, 0, sizeof(addresses));
+ for(i = 0; i < extra_addresses.num_strings; i++) {
+ ret = krb5_parse_address(context, extra_addresses.strings[i],
+ &addresses);
+ if (ret == 0) {
+ krb5_add_extra_addresses(context, &addresses);
+ krb5_free_addresses(context, &addresses);
+ }
+ }
+ free_getarg_strings(&extra_addresses);
+ }
+
+ if(renew_flag || validate_flag) {
+ ret = renew_validate(context, renew_flag, validate_flag,
+ ccache, server_str, ticket_life);
+ exit(ret != 0);
+ }
+
+#ifndef HEIMDAL_SMALLER
+ if(!convert_524)
+#endif
+ get_new_tickets(context, principal, ccache, ticket_life, 1);
+
+#ifndef HEIMDAL_SMALLER
+ if(get_v4_tgt || convert_524)
+ do_524init(context, ccache, NULL, server_str);
+#endif
+ if(do_afslog && k_hasafs())
+ krb5_afslog(context, ccache, NULL, NULL);
+ if(argc > 1) {
+ struct renew_ctx ctx;
+ time_t timeout;
+
+ timeout = ticket_lifetime(context, ccache, principal, server_str) / 2;
+
+ ctx.context = context;
+ ctx.ccache = ccache;
+ ctx.principal = principal;
+ ctx.ticket_life = ticket_life;
+
+ ret = simple_execvp_timed(argv[1], argv+1,
+ renew_func, &ctx, timeout);
+#define EX_NOEXEC 126
+#define EX_NOTFOUND 127
+ if(ret == EX_NOEXEC)
+ krb5_warnx(context, N_("permission denied: %s", ""), argv[1]);
+ else if(ret == EX_NOTFOUND)
+ krb5_warnx(context, N_("command not found: %s", ""), argv[1]);
+
+ krb5_cc_destroy(context, ccache);
+#ifndef HEIMDAL_SMALLER
+ _krb5_krb_dest_tkt(context, krb4_cc_name);
+#endif
+ if(k_hasafs())
+ k_unlog();
+ } else {
+ krb5_cc_close (context, ccache);
+ ret = 0;
+ }
+ krb5_free_principal(context, principal);
+ krb5_free_context (context);
+ return ret;
+}
diff --git a/source4/heimdal/kuser/kuser_locl.h b/source4/heimdal/kuser/kuser_locl.h
new file mode 100644
index 0000000000..eed9e00af6
--- /dev/null
+++ b/source4/heimdal/kuser/kuser_locl.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 1997 - 1999 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef __KUSER_LOCL_H__
+#define __KUSER_LOCL_H__
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETINET_IN6_H
+#include <netinet/in6.h>
+#endif
+#ifdef HAVE_NETINET6_IN6_H
+#include <netinet6/in6.h>
+#endif
+
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#include <roken.h>
+#include <getarg.h>
+#include <parse_time.h>
+#include <err.h>
+#include <krb5.h>
+
+#if defined(HAVE_SYS_IOCTL_H) && SunOS != 40
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_SYS_IOCCOM_H
+#include <sys/ioccom.h>
+#endif
+#include <kafs.h>
+#include "crypto-headers.h" /* for UI_UTIL_read_pw_string */
+
+#ifdef HAVE_LOCALE_H
+#include <locale.h>
+#endif
+
+#ifdef HAVE_LIBINTL_H
+#include <libintl.h>
+#define N_(x,y) gettext(x)
+#define NP_(x,y) (x)
+#define getarg_i18n gettext
+#else
+#define N_(x,y) (x)
+#define NP_(x,y) (x)
+#define getarg_i18n NULL
+#define bindtextdomain(package, localedir)
+#define textdomain(package)
+#endif
+
+
+#endif /* __KUSER_LOCL_H__ */
diff --git a/source4/heimdal/lib/asn1/CMS.asn1 b/source4/heimdal/lib/asn1/CMS.asn1
new file mode 100644
index 0000000000..65a467521d
--- /dev/null
+++ b/source4/heimdal/lib/asn1/CMS.asn1
@@ -0,0 +1,157 @@
+-- From RFC 3369 --
+-- $Id$ --
+
+CMS DEFINITIONS ::= BEGIN
+
+IMPORTS CertificateSerialNumber, AlgorithmIdentifier, Name,
+ Attribute, Certificate, Name, SubjectKeyIdentifier FROM rfc2459
+ heim_any, heim_any_set FROM heim;
+
+id-pkcs7 OBJECT IDENTIFIER ::= { iso(1) member-body(2)
+ us(840) rsadsi(113549) pkcs(1) pkcs7(7) }
+
+id-pkcs7-data OBJECT IDENTIFIER ::= { id-pkcs7 1 }
+id-pkcs7-signedData OBJECT IDENTIFIER ::= { id-pkcs7 2 }
+id-pkcs7-envelopedData OBJECT IDENTIFIER ::= { id-pkcs7 3 }
+id-pkcs7-signedAndEnvelopedData OBJECT IDENTIFIER ::= { id-pkcs7 4 }
+id-pkcs7-digestedData OBJECT IDENTIFIER ::= { id-pkcs7 5 }
+id-pkcs7-encryptedData OBJECT IDENTIFIER ::= { id-pkcs7 6 }
+
+CMSVersion ::= INTEGER {
+ CMSVersion_v0(0),
+ CMSVersion_v1(1),
+ CMSVersion_v2(2),
+ CMSVersion_v3(3),
+ CMSVersion_v4(4)
+}
+
+DigestAlgorithmIdentifier ::= AlgorithmIdentifier
+DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier
+SignatureAlgorithmIdentifier ::= AlgorithmIdentifier
+
+ContentType ::= OBJECT IDENTIFIER
+MessageDigest ::= OCTET STRING
+
+ContentInfo ::= SEQUENCE {
+ contentType ContentType,
+ content [0] EXPLICIT heim_any OPTIONAL -- DEFINED BY contentType
+}
+
+EncapsulatedContentInfo ::= SEQUENCE {
+ eContentType ContentType,
+ eContent [0] EXPLICIT OCTET STRING OPTIONAL
+}
+
+CertificateSet ::= SET OF heim_any
+
+CertificateList ::= Certificate
+
+CertificateRevocationLists ::= SET OF CertificateList
+
+IssuerAndSerialNumber ::= SEQUENCE {
+ issuer Name,
+ serialNumber CertificateSerialNumber
+}
+
+-- RecipientIdentifier is same as SignerIdentifier,
+-- lets glue them togheter and save some bytes and share code for them
+
+CMSIdentifier ::= CHOICE {
+ issuerAndSerialNumber IssuerAndSerialNumber,
+ subjectKeyIdentifier [0] SubjectKeyIdentifier
+}
+
+SignerIdentifier ::= CMSIdentifier
+RecipientIdentifier ::= CMSIdentifier
+
+--- CMSAttributes are the combined UnsignedAttributes and SignedAttributes
+--- to store space and share code
+
+CMSAttributes ::= SET OF Attribute -- SIZE (1..MAX)
+
+SignatureValue ::= OCTET STRING
+
+SignerInfo ::= SEQUENCE {
+ version CMSVersion,
+ sid SignerIdentifier,
+ digestAlgorithm DigestAlgorithmIdentifier,
+ signedAttrs [0] IMPLICIT -- CMSAttributes --
+ SET OF Attribute OPTIONAL,
+ signatureAlgorithm SignatureAlgorithmIdentifier,
+ signature SignatureValue,
+ unsignedAttrs [1] IMPLICIT -- CMSAttributes --
+ SET OF Attribute OPTIONAL
+}
+
+SignerInfos ::= SET OF SignerInfo
+
+SignedData ::= SEQUENCE {
+ version CMSVersion,
+ digestAlgorithms DigestAlgorithmIdentifiers,
+ encapContentInfo EncapsulatedContentInfo,
+ certificates [0] IMPLICIT -- CertificateSet --
+ SET OF heim_any OPTIONAL,
+ crls [1] IMPLICIT -- CertificateRevocationLists --
+ heim_any OPTIONAL,
+ signerInfos SignerInfos
+}
+
+OriginatorInfo ::= SEQUENCE {
+ certs [0] IMPLICIT -- CertificateSet --
+ SET OF heim_any OPTIONAL,
+ crls [1] IMPLICIT --CertificateRevocationLists --
+ heim_any OPTIONAL
+}
+
+KeyEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
+ContentEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
+
+EncryptedKey ::= OCTET STRING
+
+KeyTransRecipientInfo ::= SEQUENCE {
+ version CMSVersion, -- always set to 0 or 2
+ rid RecipientIdentifier,
+ keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
+ encryptedKey EncryptedKey
+}
+
+RecipientInfo ::= KeyTransRecipientInfo
+
+RecipientInfos ::= SET OF RecipientInfo
+
+EncryptedContent ::= OCTET STRING
+
+EncryptedContentInfo ::= SEQUENCE {
+ contentType ContentType,
+ contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier,
+ encryptedContent [0] IMPLICIT OCTET STRING OPTIONAL
+}
+
+UnprotectedAttributes ::= SET OF Attribute -- SIZE (1..MAX)
+
+CMSEncryptedData ::= SEQUENCE {
+ version CMSVersion,
+ encryptedContentInfo EncryptedContentInfo,
+ unprotectedAttrs [1] IMPLICIT -- UnprotectedAttributes --
+ heim_any OPTIONAL
+}
+
+EnvelopedData ::= SEQUENCE {
+ version CMSVersion,
+ originatorInfo [0] IMPLICIT -- OriginatorInfo -- heim_any OPTIONAL,
+ recipientInfos RecipientInfos,
+ encryptedContentInfo EncryptedContentInfo,
+ unprotectedAttrs [1] IMPLICIT -- UnprotectedAttributes --
+ heim_any OPTIONAL
+}
+
+-- Data ::= OCTET STRING
+
+CMSRC2CBCParameter ::= SEQUENCE {
+ rc2ParameterVersion INTEGER (0..4294967295),
+ iv OCTET STRING -- exactly 8 octets
+}
+
+CMSCBCParameter ::= OCTET STRING
+
+END
diff --git a/source4/heimdal/lib/asn1/asn1-common.h b/source4/heimdal/lib/asn1/asn1-common.h
new file mode 100644
index 0000000000..4c6af8b45e
--- /dev/null
+++ b/source4/heimdal/lib/asn1/asn1-common.h
@@ -0,0 +1,66 @@
+/* $Id$ */
+
+#include <stddef.h>
+#include <time.h>
+
+#ifndef __asn1_common_definitions__
+#define __asn1_common_definitions__
+
+typedef struct heim_integer {
+ size_t length;
+ void *data;
+ int negative;
+} heim_integer;
+
+typedef struct heim_octet_string {
+ size_t length;
+ void *data;
+} heim_octet_string;
+
+typedef char *heim_general_string;
+typedef char *heim_utf8_string;
+typedef char *heim_printable_string;
+typedef char *heim_ia5_string;
+
+typedef struct heim_bmp_string {
+ size_t length;
+ uint16_t *data;
+} heim_bmp_string;
+
+typedef struct heim_universal_string {
+ size_t length;
+ uint32_t *data;
+} heim_universal_string;
+
+typedef char *heim_visible_string;
+
+typedef struct heim_oid {
+ size_t length;
+ unsigned *components;
+} heim_oid;
+
+typedef struct heim_bit_string {
+ size_t length;
+ void *data;
+} heim_bit_string;
+
+typedef struct heim_octet_string heim_any;
+typedef struct heim_octet_string heim_any_set;
+
+#define ASN1_MALLOC_ENCODE(T, B, BL, S, L, R) \
+ do { \
+ (BL) = length_##T((S)); \
+ (B) = malloc((BL)); \
+ if((B) == NULL) { \
+ (R) = ENOMEM; \
+ } else { \
+ (R) = encode_##T(((unsigned char*)(B)) + (BL) - 1, (BL), \
+ (S), (L)); \
+ if((R) != 0) { \
+ free((B)); \
+ (B) = NULL; \
+ } \
+ } \
+ } while (0)
+
+#endif
diff --git a/source4/heimdal/lib/asn1/asn1_err.et b/source4/heimdal/lib/asn1/asn1_err.et
new file mode 100644
index 0000000000..26bda55c19
--- /dev/null
+++ b/source4/heimdal/lib/asn1/asn1_err.et
@@ -0,0 +1,25 @@
+#
+# Error messages for the asn.1 library
+#
+# This might look like a com_err file, but is not
+#
+id "$Id$"
+
+error_table asn1
+prefix ASN1
+error_code BAD_TIMEFORMAT, "ASN.1 failed call to system time library"
+error_code MISSING_FIELD, "ASN.1 structure is missing a required field"
+error_code MISPLACED_FIELD, "ASN.1 unexpected field number"
+error_code TYPE_MISMATCH, "ASN.1 type numbers are inconsistent"
+error_code OVERFLOW, "ASN.1 value too large"
+error_code OVERRUN, "ASN.1 encoding ended unexpectedly"
+error_code BAD_ID, "ASN.1 identifier doesn't match expected value"
+error_code BAD_LENGTH, "ASN.1 length doesn't match expected value"
+error_code BAD_FORMAT, "ASN.1 badly-formatted encoding"
+error_code PARSE_ERROR, "ASN.1 parse error"
+error_code EXTRA_DATA, "ASN.1 extra data past end of end structure"
+error_code BAD_CHARACTER, "ASN.1 invalid character in string"
+error_code MIN_CONSTRAINT, "ASN.1 too few elements"
+error_code MAX_CONSTRAINT, "ASN.1 too many elements"
+error_code EXACT_CONSTRAINT, "ASN.1 wrong number of elements"
+end
diff --git a/source4/heimdal/lib/asn1/asn1_gen.c b/source4/heimdal/lib/asn1/asn1_gen.c
new file mode 100644
index 0000000000..e25f6d919e
--- /dev/null
+++ b/source4/heimdal/lib/asn1/asn1_gen.c
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "der_locl.h"
+#include <com_err.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <getarg.h>
+#include <hex.h>
+#include <err.h>
+
+RCSID("$Id$");
+
+static int
+doit(const char *fn)
+{
+ char buf[2048];
+ char *fnout;
+ const char *bname;
+ unsigned long line = 0;
+ FILE *f, *fout;
+ size_t offset = 0;
+
+ f = fopen(fn, "r");
+ if (f == NULL)
+ err(1, "fopen");
+
+ bname = strrchr(fn, '/');
+ if (bname)
+ bname++;
+ else
+ bname = fn;
+
+ asprintf(&fnout, "%s.out", bname);
+ if (fnout == NULL)
+ errx(1, "malloc");
+
+ fout = fopen(fnout, "w");
+ if (fout == NULL)
+ err(1, "fopen: output file");
+
+ while (fgets(buf, sizeof(buf), f) != NULL) {
+ char *ptr, *class, *type, *tag, *length, *data, *foo;
+ int ret, l, c, ty, ta;
+ unsigned char p[6], *pdata;
+ size_t sz;
+
+ line++;
+
+ buf[strcspn(buf, "\r\n")] = '\0';
+ if (buf[0] == '#' || buf[0] == '\0')
+ continue;
+
+ ptr = buf;
+ while (isspace((unsigned char)*ptr))
+ ptr++;
+
+ class = strtok_r(ptr, " \t\n", &foo);
+ if (class == NULL) errx(1, "class missing on line %lu", line);
+ type = strtok_r(NULL, " \t\n", &foo);
+ if (type == NULL) errx(1, "type missing on line %lu", line);
+ tag = strtok_r(NULL, " \t\n", &foo);
+ if (tag == NULL) errx(1, "tag missing on line %lu", line);
+ length = strtok_r(NULL, " \t\n", &foo);
+ if (length == NULL) errx(1, "length missing on line %lu", line);
+ data = strtok_r(NULL, " \t\n", &foo);
+
+ c = der_get_class_num(class);
+ if (c == -1) errx(1, "no valid class on line %lu", line);
+ ty = der_get_type_num(type);
+ if (ty == -1) errx(1, "no valid type on line %lu", line);
+ ta = der_get_tag_num(tag);
+ if (ta == -1)
+ ta = atoi(tag);
+
+ l = atoi(length);
+
+ printf("line: %3lu offset: %3lu class: %d type: %d "
+ "tag: %3d length: %3d %s\n",
+ line, (unsigned long)offset, c, ty, ta, l,
+ data ? "<have data>" : "<no data>");
+
+ ret = der_put_length_and_tag(p + sizeof(p) - 1, sizeof(p),
+ l,
+ c,
+ ty,
+ ta,
+ &sz);
+ if (ret)
+ errx(1, "der_put_length_and_tag: %d", ret);
+
+ if (fwrite(p + sizeof(p) - sz , sz, 1, fout) != 1)
+ err(1, "fwrite length/tag failed");
+ offset += sz;
+
+ if (data) {
+ size_t datalen;
+
+ datalen = strlen(data) / 2;
+ pdata = emalloc(sz);
+
+ if (hex_decode(data, pdata, datalen) != datalen)
+ errx(1, "failed to decode data");
+
+ if (fwrite(pdata, datalen, 1, fout) != 1)
+ err(1, "fwrite data failed");
+ offset += datalen;
+
+ free(pdata);
+ }
+ }
+ printf("line: eof offset: %lu\n", (unsigned long)offset);
+
+ fclose(fout);
+ fclose(f);
+ return 0;
+}
+
+
+static int version_flag;
+static int help_flag;
+struct getargs args[] = {
+ { "version", 0, arg_flag, &version_flag },
+ { "help", 0, arg_flag, &help_flag }
+};
+int num_args = sizeof(args) / sizeof(args[0]);
+
+static void
+usage(int code)
+{
+ arg_printusage(args, num_args, NULL, "parse-file");
+ exit(code);
+}
+
+int
+main(int argc, char **argv)
+{
+ int optidx = 0;
+
+ setprogname (argv[0]);
+
+ if(getarg(args, num_args, argc, argv, &optidx))
+ usage(1);
+ if(help_flag)
+ usage(0);
+ if(version_flag) {
+ print_version(NULL);
+ exit(0);
+ }
+ argv += optidx;
+ argc -= optidx;
+ if (argc != 1)
+ usage (1);
+
+ return doit (argv[0]);
+}
diff --git a/source4/heimdal/lib/asn1/asn1_queue.h b/source4/heimdal/lib/asn1/asn1_queue.h
new file mode 100644
index 0000000000..73eb50f8b8
--- /dev/null
+++ b/source4/heimdal/lib/asn1/asn1_queue.h
@@ -0,0 +1,167 @@
+/* $NetBSD: queue.h,v 1.38 2004/04/18 14:12:05 lukem Exp $ */
+/* $Id$ */
+
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)queue.h 8.5 (Berkeley) 8/20/94
+ */
+
+#ifndef _ASN1_QUEUE_H_
+#define _ASN1_QUEUE_H_
+
+/*
+ * Tail queue definitions.
+ */
+#define ASN1_TAILQ_HEAD(name, type) \
+struct name { \
+ struct type *tqh_first; /* first element */ \
+ struct type **tqh_last; /* addr of last next element */ \
+}
+
+#define ASN1_TAILQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).tqh_first }
+#define ASN1_TAILQ_ENTRY(type) \
+struct { \
+ struct type *tqe_next; /* next element */ \
+ struct type **tqe_prev; /* address of previous next element */ \
+}
+
+/*
+ * Tail queue functions.
+ */
+#if defined(_KERNEL) && defined(QUEUEDEBUG)
+#define QUEUEDEBUG_ASN1_TAILQ_INSERT_HEAD(head, elm, field) \
+ if ((head)->tqh_first && \
+ (head)->tqh_first->field.tqe_prev != &(head)->tqh_first) \
+ panic("ASN1_TAILQ_INSERT_HEAD %p %s:%d", (head), __FILE__, __LINE__);
+#define QUEUEDEBUG_ASN1_TAILQ_INSERT_TAIL(head, elm, field) \
+ if (*(head)->tqh_last != NULL) \
+ panic("ASN1_TAILQ_INSERT_TAIL %p %s:%d", (head), __FILE__, __LINE__);
+#define QUEUEDEBUG_ASN1_TAILQ_OP(elm, field) \
+ if ((elm)->field.tqe_next && \
+ (elm)->field.tqe_next->field.tqe_prev != \
+ &(elm)->field.tqe_next) \
+ panic("ASN1_TAILQ_* forw %p %s:%d", (elm), __FILE__, __LINE__);\
+ if (*(elm)->field.tqe_prev != (elm)) \
+ panic("ASN1_TAILQ_* back %p %s:%d", (elm), __FILE__, __LINE__);
+#define QUEUEDEBUG_ASN1_TAILQ_PREREMOVE(head, elm, field) \
+ if ((elm)->field.tqe_next == NULL && \
+ (head)->tqh_last != &(elm)->field.tqe_next) \
+ panic("ASN1_TAILQ_PREREMOVE head %p elm %p %s:%d", \
+ (head), (elm), __FILE__, __LINE__);
+#define QUEUEDEBUG_ASN1_TAILQ_POSTREMOVE(elm, field) \
+ (elm)->field.tqe_next = (void *)1L; \
+ (elm)->field.tqe_prev = (void *)1L;
+#else
+#define QUEUEDEBUG_ASN1_TAILQ_INSERT_HEAD(head, elm, field)
+#define QUEUEDEBUG_ASN1_TAILQ_INSERT_TAIL(head, elm, field)
+#define QUEUEDEBUG_ASN1_TAILQ_OP(elm, field)
+#define QUEUEDEBUG_ASN1_TAILQ_PREREMOVE(head, elm, field)
+#define QUEUEDEBUG_ASN1_TAILQ_POSTREMOVE(elm, field)
+#endif
+
+#define ASN1_TAILQ_INIT(head) do { \
+ (head)->tqh_first = NULL; \
+ (head)->tqh_last = &(head)->tqh_first; \
+} while (/*CONSTCOND*/0)
+
+#define ASN1_TAILQ_INSERT_HEAD(head, elm, field) do { \
+ QUEUEDEBUG_ASN1_TAILQ_INSERT_HEAD((head), (elm), field) \
+ if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
+ (head)->tqh_first->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (head)->tqh_first = (elm); \
+ (elm)->field.tqe_prev = &(head)->tqh_first; \
+} while (/*CONSTCOND*/0)
+
+#define ASN1_TAILQ_INSERT_TAIL(head, elm, field) do { \
+ QUEUEDEBUG_ASN1_TAILQ_INSERT_TAIL((head), (elm), field) \
+ (elm)->field.tqe_next = NULL; \
+ (elm)->field.tqe_prev = (head)->tqh_last; \
+ *(head)->tqh_last = (elm); \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define ASN1_TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ QUEUEDEBUG_ASN1_TAILQ_OP((listelm), field) \
+ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
+ (elm)->field.tqe_next->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (listelm)->field.tqe_next = (elm); \
+ (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define ASN1_TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
+ QUEUEDEBUG_ASN1_TAILQ_OP((listelm), field) \
+ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
+ (elm)->field.tqe_next = (listelm); \
+ *(listelm)->field.tqe_prev = (elm); \
+ (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define ASN1_TAILQ_REMOVE(head, elm, field) do { \
+ QUEUEDEBUG_ASN1_TAILQ_PREREMOVE((head), (elm), field) \
+ QUEUEDEBUG_ASN1_TAILQ_OP((elm), field) \
+ if (((elm)->field.tqe_next) != NULL) \
+ (elm)->field.tqe_next->field.tqe_prev = \
+ (elm)->field.tqe_prev; \
+ else \
+ (head)->tqh_last = (elm)->field.tqe_prev; \
+ *(elm)->field.tqe_prev = (elm)->field.tqe_next; \
+ QUEUEDEBUG_ASN1_TAILQ_POSTREMOVE((elm), field); \
+} while (/*CONSTCOND*/0)
+
+#define ASN1_TAILQ_FOREACH(var, head, field) \
+ for ((var) = ((head)->tqh_first); \
+ (var); \
+ (var) = ((var)->field.tqe_next))
+
+#define ASN1_TAILQ_FOREACH_REVERSE(var, head, headname, field) \
+ for ((var) = (*(((struct headname *)((head)->tqh_last))->tqh_last)); \
+ (var); \
+ (var) = (*(((struct headname *)((var)->field.tqe_prev))->tqh_last)))
+
+/*
+ * Tail queue access methods.
+ */
+#define ASN1_TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
+#define ASN1_TAILQ_FIRST(head) ((head)->tqh_first)
+#define ASN1_TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+
+#define ASN1_TAILQ_LAST(head, headname) \
+ (*(((struct headname *)((head)->tqh_last))->tqh_last))
+#define ASN1_TAILQ_PREV(elm, headname, field) \
+ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+
+
+#endif /* !_ASN1_QUEUE_H_ */
diff --git a/source4/heimdal/lib/asn1/canthandle.asn1 b/source4/heimdal/lib/asn1/canthandle.asn1
new file mode 100644
index 0000000000..5c2690f9b6
--- /dev/null
+++ b/source4/heimdal/lib/asn1/canthandle.asn1
@@ -0,0 +1,34 @@
+-- $Id$ --
+
+CANTHANDLE DEFINITIONS ::= BEGIN
+
+-- Code the tag [1] but not the [ CONTEXT CONS UT_Sequence ] for Kaka2
+-- Workaround: use inline the structure directly
+-- Code the tag [2] but it should be primitive since KAKA3 is
+-- Workaround: use the INTEGER type directly
+
+Kaka2 ::= SEQUENCE {
+ kaka2-1 [0] INTEGER
+}
+
+Kaka3 ::= INTEGER
+
+Foo ::= SEQUENCE {
+ kaka1 [0] IMPLICIT INTEGER OPTIONAL,
+ kaka2 [1] IMPLICIT Kaka2 OPTIONAL,
+ kaka3 [2] IMPLICIT Kaka3 OPTIONAL
+}
+
+-- Don't code kaka if it's 1
+-- Workaround is to use OPTIONAL and check for in the encoder stubs
+
+Bar ::= SEQUENCE {
+ kaka [0] INTEGER DEFAULT 1
+}
+
+-- Can't handle primitives in SET OF
+-- Workaround is to define a type that is only an integer and use that
+
+Baz ::= SET OF INTEGER
+
+END
diff --git a/source4/heimdal/lib/asn1/der.c b/source4/heimdal/lib/asn1/der.c
new file mode 100644
index 0000000000..0c59e6f640
--- /dev/null
+++ b/source4/heimdal/lib/asn1/der.c
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 1997 - 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "der_locl.h"
+#include <com_err.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <getarg.h>
+#include <err.h>
+
+RCSID("$Id$");
+
+
+static const char *class_names[] = {
+ "UNIV", /* 0 */
+ "APPL", /* 1 */
+ "CONTEXT", /* 2 */
+ "PRIVATE" /* 3 */
+};
+
+static const char *type_names[] = {
+ "PRIM", /* 0 */
+ "CONS" /* 1 */
+};
+
+static const char *tag_names[] = {
+ "EndOfContent", /* 0 */
+ "Boolean", /* 1 */
+ "Integer", /* 2 */
+ "BitString", /* 3 */
+ "OctetString", /* 4 */
+ "Null", /* 5 */
+ "ObjectID", /* 6 */
+ NULL, /* 7 */
+ NULL, /* 8 */
+ NULL, /* 9 */
+ "Enumerated", /* 10 */
+ NULL, /* 11 */
+ NULL, /* 12 */
+ NULL, /* 13 */
+ NULL, /* 14 */
+ NULL, /* 15 */
+ "Sequence", /* 16 */
+ "Set", /* 17 */
+ NULL, /* 18 */
+ "PrintableString", /* 19 */
+ NULL, /* 20 */
+ NULL, /* 21 */
+ "IA5String", /* 22 */
+ "UTCTime", /* 23 */
+ "GeneralizedTime", /* 24 */
+ NULL, /* 25 */
+ "VisibleString", /* 26 */
+ "GeneralString", /* 27 */
+ NULL, /* 28 */
+ NULL, /* 29 */
+ "BMPString" /* 30 */
+};
+
+static int
+get_type(const char *name, const char *list[], unsigned len)
+{
+ unsigned i;
+ for (i = 0; i < len; i++)
+ if (list[i] && strcasecmp(list[i], name) == 0)
+ return i;
+ return -1;
+}
+
+#define SIZEOF_ARRAY(a) (sizeof((a))/sizeof((a)[0]))
+
+const char *
+der_get_class_name(unsigned num)
+{
+ if (num >= SIZEOF_ARRAY(class_names))
+ return NULL;
+ return class_names[num];
+}
+
+int
+der_get_class_num(const char *name)
+{
+ return get_type(name, class_names, SIZEOF_ARRAY(class_names));
+}
+
+const char *
+der_get_type_name(unsigned num)
+{
+ if (num >= SIZEOF_ARRAY(type_names))
+ return NULL;
+ return type_names[num];
+}
+
+int
+der_get_type_num(const char *name)
+{
+ return get_type(name, type_names, SIZEOF_ARRAY(type_names));
+}
+
+const char *
+der_get_tag_name(unsigned num)
+{
+ if (num >= SIZEOF_ARRAY(tag_names))
+ return NULL;
+ return tag_names[num];
+}
+
+int
+der_get_tag_num(const char *name)
+{
+ return get_type(name, tag_names, SIZEOF_ARRAY(tag_names));
+}
diff --git a/source4/heimdal/lib/asn1/der.h b/source4/heimdal/lib/asn1/der.h
new file mode 100644
index 0000000000..3b6f30887c
--- /dev/null
+++ b/source4/heimdal/lib/asn1/der.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 1997 - 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef __DER_H__
+#define __DER_H__
+
+typedef enum {
+ ASN1_C_UNIV = 0,
+ ASN1_C_APPL = 1,
+ ASN1_C_CONTEXT = 2,
+ ASN1_C_PRIVATE = 3
+} Der_class;
+
+typedef enum {PRIM = 0, CONS = 1} Der_type;
+
+#define MAKE_TAG(CLASS, TYPE, TAG) (((CLASS) << 6) | ((TYPE) << 5) | (TAG))
+
+/* Universal tags */
+
+enum {
+ UT_EndOfContent = 0,
+ UT_Boolean = 1,
+ UT_Integer = 2,
+ UT_BitString = 3,
+ UT_OctetString = 4,
+ UT_Null = 5,
+ UT_OID = 6,
+ UT_Enumerated = 10,
+ UT_UTF8String = 12,
+ UT_Sequence = 16,
+ UT_Set = 17,
+ UT_PrintableString = 19,
+ UT_IA5String = 22,
+ UT_UTCTime = 23,
+ UT_GeneralizedTime = 24,
+ UT_UniversalString = 25,
+ UT_VisibleString = 26,
+ UT_GeneralString = 27,
+ UT_BMPString = 30,
+ /* unsupported types */
+ UT_ObjectDescriptor = 7,
+ UT_External = 8,
+ UT_Real = 9,
+ UT_EmbeddedPDV = 11,
+ UT_RelativeOID = 13,
+ UT_NumericString = 18,
+ UT_TeletexString = 20,
+ UT_VideotexString = 21,
+ UT_GraphicString = 25
+};
+
+#define ASN1_INDEFINITE 0xdce0deed
+
+typedef struct heim_der_time_t {
+ time_t dt_sec;
+ unsigned long dt_nsec;
+} heim_der_time_t;
+
+typedef struct heim_ber_time_t {
+ time_t bt_sec;
+ unsigned bt_nsec;
+ int bt_zone;
+} heim_ber_time_t;
+
+#include <der-protos.h>
+
+int _heim_fix_dce(size_t reallen, size_t *len);
+int _heim_der_set_sort(const void *, const void *);
+int _heim_time2generalizedtime (time_t, heim_octet_string *, int);
+
+#endif /* __DER_H__ */
diff --git a/source4/heimdal/lib/asn1/der_cmp.c b/source4/heimdal/lib/asn1/der_cmp.c
new file mode 100644
index 0000000000..7329c5867f
--- /dev/null
+++ b/source4/heimdal/lib/asn1/der_cmp.c
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2003-2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "der_locl.h"
+
+int
+der_heim_oid_cmp(const heim_oid *p, const heim_oid *q)
+{
+ if (p->length != q->length)
+ return p->length - q->length;
+ return memcmp(p->components,
+ q->components,
+ p->length * sizeof(*p->components));
+}
+
+int
+der_heim_octet_string_cmp(const heim_octet_string *p,
+ const heim_octet_string *q)
+{
+ if (p->length != q->length)
+ return p->length - q->length;
+ return memcmp(p->data, q->data, p->length);
+}
+
+int
+der_heim_bit_string_cmp(const heim_bit_string *p,
+ const heim_bit_string *q)
+{
+ int i, r1, r2;
+ if (p->length != q->length)
+ return p->length - q->length;
+ i = memcmp(p->data, q->data, p->length / 8);
+ if (i)
+ return i;
+ if ((p->length % 8) == 0)
+ return 0;
+ i = (p->length / 8);
+ r1 = ((unsigned char *)p->data)[i];
+ r2 = ((unsigned char *)q->data)[i];
+ i = 8 - (p->length % 8);
+ r1 = r1 >> i;
+ r2 = r2 >> i;
+ return r1 - r2;
+}
+
+int
+der_heim_integer_cmp(const heim_integer *p,
+ const heim_integer *q)
+{
+ if (p->negative != q->negative)
+ return q->negative - p->negative;
+ if (p->length != q->length)
+ return p->length - q->length;
+ return memcmp(p->data, q->data, p->length);
+}
+
+int
+der_heim_bmp_string_cmp(const heim_bmp_string *p, const heim_bmp_string *q)
+{
+ if (p->length != q->length)
+ return p->length - q->length;
+ return memcmp(p->data, q->data, q->length * sizeof(q->data[0]));
+}
+
+int
+der_heim_universal_string_cmp(const heim_universal_string *p,
+ const heim_universal_string *q)
+{
+ if (p->length != q->length)
+ return p->length - q->length;
+ return memcmp(p->data, q->data, q->length * sizeof(q->data[0]));
+}
diff --git a/source4/heimdal/lib/asn1/der_copy.c b/source4/heimdal/lib/asn1/der_copy.c
new file mode 100644
index 0000000000..51a2a03300
--- /dev/null
+++ b/source4/heimdal/lib/asn1/der_copy.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 1997 - 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "der_locl.h"
+
+RCSID("$Id$");
+
+int
+der_copy_general_string (const heim_general_string *from,
+ heim_general_string *to)
+{
+ *to = strdup(*from);
+ if(*to == NULL)
+ return ENOMEM;
+ return 0;
+}
+
+int
+der_copy_utf8string (const heim_utf8_string *from, heim_utf8_string *to)
+{
+ return der_copy_general_string(from, to);
+}
+
+int
+der_copy_printable_string (const heim_printable_string *from,
+ heim_printable_string *to)
+{
+ return der_copy_general_string(from, to);
+}
+
+int
+der_copy_ia5_string (const heim_printable_string *from,
+ heim_printable_string *to)
+{
+ return der_copy_general_string(from, to);
+}
+
+int
+der_copy_bmp_string (const heim_bmp_string *from, heim_bmp_string *to)
+{
+ to->length = from->length;
+ to->data = malloc(to->length * sizeof(to->data[0]));
+ if(to->length != 0 && to->data == NULL)
+ return ENOMEM;
+ memcpy(to->data, from->data, to->length * sizeof(to->data[0]));
+ return 0;
+}
+
+int
+der_copy_universal_string (const heim_universal_string *from,
+ heim_universal_string *to)
+{
+ to->length = from->length;
+ to->data = malloc(to->length * sizeof(to->data[0]));
+ if(to->length != 0 && to->data == NULL)
+ return ENOMEM;
+ memcpy(to->data, from->data, to->length * sizeof(to->data[0]));
+ return 0;
+}
+
+int
+der_copy_visible_string (const heim_visible_string *from,
+ heim_visible_string *to)
+{
+ return der_copy_general_string(from, to);
+}
+
+int
+der_copy_octet_string (const heim_octet_string *from, heim_octet_string *to)
+{
+ to->length = from->length;
+ to->data = malloc(to->length);
+ if(to->length != 0 && to->data == NULL)
+ return ENOMEM;
+ memcpy(to->data, from->data, to->length);
+ return 0;
+}
+
+int
+der_copy_heim_integer (const heim_integer *from, heim_integer *to)
+{
+ to->length = from->length;
+ to->data = malloc(to->length);
+ if(to->length != 0 && to->data == NULL)
+ return ENOMEM;
+ memcpy(to->data, from->data, to->length);
+ to->negative = from->negative;
+ return 0;
+}
+
+int
+der_copy_oid (const heim_oid *from, heim_oid *to)
+{
+ to->length = from->length;
+ to->components = malloc(to->length * sizeof(*to->components));
+ if (to->length != 0 && to->components == NULL)
+ return ENOMEM;
+ memcpy(to->components, from->components,
+ to->length * sizeof(*to->components));
+ return 0;
+}
+
+int
+der_copy_bit_string (const heim_bit_string *from, heim_bit_string *to)
+{
+ size_t len;
+
+ len = (from->length + 7) / 8;
+ to->length = from->length;
+ to->data = malloc(len);
+ if(len != 0 && to->data == NULL)
+ return ENOMEM;
+ memcpy(to->data, from->data, len);
+ return 0;
+}
diff --git a/source4/heimdal/lib/asn1/der_format.c b/source4/heimdal/lib/asn1/der_format.c
new file mode 100644
index 0000000000..fc79a30b56
--- /dev/null
+++ b/source4/heimdal/lib/asn1/der_format.c
@@ -0,0 +1,170 @@
+/*
+ * Copyright (c) 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "der_locl.h"
+#include <hex.h>
+
+RCSID("$Id$");
+
+int
+der_parse_hex_heim_integer (const char *p, heim_integer *data)
+{
+ ssize_t len;
+
+ data->length = 0;
+ data->negative = 0;
+ data->data = NULL;
+
+ if (*p == '-') {
+ p++;
+ data->negative = 1;
+ }
+
+ len = strlen(p);
+ if (len <= 0) {
+ data->data = NULL;
+ data->length = 0;
+ return EINVAL;
+ }
+
+ data->length = (len / 2) + 1;
+ data->data = malloc(data->length);
+ if (data->data == NULL) {
+ data->length = 0;
+ return ENOMEM;
+ }
+
+ len = hex_decode(p, data->data, data->length);
+ if (len < 0) {
+ free(data->data);
+ data->data = NULL;
+ data->length = 0;
+ return EINVAL;
+ }
+
+ {
+ unsigned char *q = data->data;
+ while(len > 0 && *q == 0) {
+ q++;
+ len--;
+ }
+ data->length = len;
+ memmove(data->data, q, len);
+ }
+ return 0;
+}
+
+int
+der_print_hex_heim_integer (const heim_integer *data, char **p)
+{
+ ssize_t len;
+ char *q;
+
+ len = hex_encode(data->data, data->length, p);
+ if (len < 0)
+ return ENOMEM;
+
+ if (data->negative) {
+ len = asprintf(&q, "-%s", *p);
+ free(*p);
+ if (len < 0)
+ return ENOMEM;
+ *p = q;
+ }
+ return 0;
+}
+
+int
+der_print_heim_oid (const heim_oid *oid, char delim, char **str)
+{
+ struct rk_strpool *p = NULL;
+ int i;
+
+ if (oid->length == 0)
+ return EINVAL;
+
+ for (i = 0; i < oid->length ; i++) {
+ p = rk_strpoolprintf(p, "%d", oid->components[i]);
+ if (p && i < oid->length - 1)
+ p = rk_strpoolprintf(p, "%c", delim);
+ if (p == NULL) {
+ *str = NULL;
+ return ENOMEM;
+ }
+ }
+
+ *str = rk_strpoolcollect(p);
+ if (*str == NULL)
+ return ENOMEM;
+ return 0;
+}
+
+int
+der_parse_heim_oid (const char *str, const char *sep, heim_oid *data)
+{
+ char *s, *w, *brkt, *endptr;
+ unsigned int *c;
+ long l;
+
+ data->length = 0;
+ data->components = NULL;
+
+ if (sep == NULL)
+ sep = ".";
+
+ s = strdup(str);
+
+ for (w = strtok_r(s, sep, &brkt);
+ w != NULL;
+ w = strtok_r(NULL, sep, &brkt)) {
+
+ c = realloc(data->components,
+ (data->length + 1) * sizeof(data->components[0]));
+ if (c == NULL) {
+ der_free_oid(data);
+ free(s);
+ return ENOMEM;
+ }
+ data->components = c;
+
+ l = strtol(w, &endptr, 10);
+ if (*endptr != '\0' || l < 0 || l > INT_MAX) {
+ der_free_oid(data);
+ free(s);
+ return EINVAL;
+ }
+ data->components[data->length++] = l;
+ }
+ free(s);
+ return 0;
+}
diff --git a/source4/heimdal/lib/asn1/der_free.c b/source4/heimdal/lib/asn1/der_free.c
new file mode 100644
index 0000000000..743bb6c466
--- /dev/null
+++ b/source4/heimdal/lib/asn1/der_free.c
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 1997 - 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "der_locl.h"
+
+RCSID("$Id$");
+
+void
+der_free_general_string (heim_general_string *str)
+{
+ free(*str);
+ *str = NULL;
+}
+
+void
+der_free_utf8string (heim_utf8_string *str)
+{
+ free(*str);
+ *str = NULL;
+}
+
+void
+der_free_printable_string (heim_printable_string *str)
+{
+ free(*str);
+ *str = NULL;
+}
+
+void
+der_free_ia5_string (heim_ia5_string *str)
+{
+ free(*str);
+ *str = NULL;
+}
+
+void
+der_free_bmp_string (heim_bmp_string *k)
+{
+ free(k->data);
+ k->data = NULL;
+ k->length = 0;
+}
+
+void
+der_free_universal_string (heim_universal_string *k)
+{
+ free(k->data);
+ k->data = NULL;
+ k->length = 0;
+}
+
+void
+der_free_visible_string (heim_visible_string *str)
+{
+ free(*str);
+ *str = NULL;
+}
+
+void
+der_free_octet_string (heim_octet_string *k)
+{
+ free(k->data);
+ k->data = NULL;
+ k->length = 0;
+}
+
+void
+der_free_heim_integer (heim_integer *k)
+{
+ free(k->data);
+ k->data = NULL;
+ k->length = 0;
+}
+
+void
+der_free_oid (heim_oid *k)
+{
+ free(k->components);
+ k->components = NULL;
+ k->length = 0;
+}
+
+void
+der_free_bit_string (heim_bit_string *k)
+{
+ free(k->data);
+ k->data = NULL;
+ k->length = 0;
+}
diff --git a/source4/heimdal/lib/asn1/der_get.c b/source4/heimdal/lib/asn1/der_get.c
new file mode 100644
index 0000000000..8a70966413
--- /dev/null
+++ b/source4/heimdal/lib/asn1/der_get.c
@@ -0,0 +1,546 @@
+/*
+ * Copyright (c) 1997 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "der_locl.h"
+
+RCSID("$Id$");
+
+#include <version.h>
+
+/*
+ * All decoding functions take a pointer `p' to first position in
+ * which to read, from the left, `len' which means the maximum number
+ * of characters we are able to read, `ret' were the value will be
+ * returned and `size' where the number of used bytes is stored.
+ * Either 0 or an error code is returned.
+ */
+
+int
+der_get_unsigned (const unsigned char *p, size_t len,
+ unsigned *ret, size_t *size)
+{
+ unsigned val = 0;
+ size_t oldlen = len;
+
+ if (len == sizeof(unsigned) + 1 && p[0] == 0)
+ ;
+ else if (len > sizeof(unsigned))
+ return ASN1_OVERRUN;
+
+ while (len--)
+ val = val * 256 + *p++;
+ *ret = val;
+ if(size) *size = oldlen;
+ return 0;
+}
+
+int
+der_get_integer (const unsigned char *p, size_t len,
+ int *ret, size_t *size)
+{
+ int val = 0;
+ size_t oldlen = len;
+
+ if (len > sizeof(int))
+ return ASN1_OVERRUN;
+
+ if (len > 0) {
+ val = (signed char)*p++;
+ while (--len)
+ val = val * 256 + *p++;
+ }
+ *ret = val;
+ if(size) *size = oldlen;
+ return 0;
+}
+
+int
+der_get_length (const unsigned char *p, size_t len,
+ size_t *val, size_t *size)
+{
+ size_t v;
+
+ if (len <= 0)
+ return ASN1_OVERRUN;
+ --len;
+ v = *p++;
+ if (v < 128) {
+ *val = v;
+ if(size) *size = 1;
+ } else {
+ int e;
+ size_t l;
+ unsigned tmp;
+
+ if(v == 0x80){
+ *val = ASN1_INDEFINITE;
+ if(size) *size = 1;
+ return 0;
+ }
+ v &= 0x7F;
+ if (len < v)
+ return ASN1_OVERRUN;
+ e = der_get_unsigned (p, v, &tmp, &l);
+ if(e) return e;
+ *val = tmp;
+ if(size) *size = l + 1;
+ }
+ return 0;
+}
+
+int
+der_get_boolean(const unsigned char *p, size_t len, int *data, size_t *size)
+{
+ if(len < 1)
+ return ASN1_OVERRUN;
+ if(*p != 0)
+ *data = 1;
+ else
+ *data = 0;
+ *size = 1;
+ return 0;
+}
+
+int
+der_get_general_string (const unsigned char *p, size_t len,
+ heim_general_string *str, size_t *size)
+{
+ const unsigned char *p1;
+ char *s;
+
+ p1 = memchr(p, 0, len);
+ if (p1 != NULL) {
+ /*
+ * Allow trailing NULs. We allow this since MIT Kerberos sends
+ * an strings in the NEED_PREAUTH case that includes a
+ * trailing NUL.
+ */
+ while (p1 - p < len && *p1 == '\0')
+ p1++;
+ if (p1 - p != len)
+ return ASN1_BAD_CHARACTER;
+ }
+ if (len > len + 1)
+ return ASN1_BAD_LENGTH;
+
+ s = malloc (len + 1);
+ if (s == NULL)
+ return ENOMEM;
+ memcpy (s, p, len);
+ s[len] = '\0';
+ *str = s;
+ if(size) *size = len;
+ return 0;
+}
+
+int
+der_get_utf8string (const unsigned char *p, size_t len,
+ heim_utf8_string *str, size_t *size)
+{
+ return der_get_general_string(p, len, str, size);
+}
+
+int
+der_get_printable_string (const unsigned char *p, size_t len,
+ heim_printable_string *str, size_t *size)
+{
+ return der_get_general_string(p, len, str, size);
+}
+
+int
+der_get_ia5_string (const unsigned char *p, size_t len,
+ heim_ia5_string *str, size_t *size)
+{
+ return der_get_general_string(p, len, str, size);
+}
+
+int
+der_get_bmp_string (const unsigned char *p, size_t len,
+ heim_bmp_string *data, size_t *size)
+{
+ size_t i;
+
+ if (len & 1)
+ return ASN1_BAD_FORMAT;
+ data->length = len / 2;
+ if (data->length > UINT_MAX/sizeof(data->data[0]))
+ return ERANGE;
+ data->data = malloc(data->length * sizeof(data->data[0]));
+ if (data->data == NULL && data->length != 0)
+ return ENOMEM;
+
+ for (i = 0; i < data->length; i++) {
+ data->data[i] = (p[0] << 8) | p[1];
+ p += 2;
+ }
+ if (size) *size = len;
+
+ return 0;
+}
+
+int
+der_get_universal_string (const unsigned char *p, size_t len,
+ heim_universal_string *data, size_t *size)
+{
+ size_t i;
+
+ if (len & 3)
+ return ASN1_BAD_FORMAT;
+ data->length = len / 4;
+ if (data->length > UINT_MAX/sizeof(data->data[0]))
+ return ERANGE;
+ data->data = malloc(data->length * sizeof(data->data[0]));
+ if (data->data == NULL && data->length != 0)
+ return ENOMEM;
+
+ for (i = 0; i < data->length; i++) {
+ data->data[i] = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
+ p += 4;
+ }
+ if (size) *size = len;
+ return 0;
+}
+
+int
+der_get_visible_string (const unsigned char *p, size_t len,
+ heim_visible_string *str, size_t *size)
+{
+ return der_get_general_string(p, len, str, size);
+}
+
+int
+der_get_octet_string (const unsigned char *p, size_t len,
+ heim_octet_string *data, size_t *size)
+{
+ data->length = len;
+ data->data = malloc(len);
+ if (data->data == NULL && data->length != 0)
+ return ENOMEM;
+ memcpy (data->data, p, len);
+ if(size) *size = len;
+ return 0;
+}
+
+int
+der_get_heim_integer (const unsigned char *p, size_t len,
+ heim_integer *data, size_t *size)
+{
+ data->length = 0;
+ data->negative = 0;
+ data->data = NULL;
+
+ if (len == 0) {
+ if (size)
+ *size = 0;
+ return 0;
+ }
+ if (p[0] & 0x80) {
+ unsigned char *q;
+ int carry = 1;
+ data->negative = 1;
+
+ data->length = len;
+
+ if (p[0] == 0xff) {
+ p++;
+ data->length--;
+ }
+ data->data = malloc(data->length);
+ if (data->data == NULL) {
+ data->length = 0;
+ if (size)
+ *size = 0;
+ return ENOMEM;
+ }
+ q = &((unsigned char*)data->data)[data->length - 1];
+ p += data->length - 1;
+ while (q >= (unsigned char*)data->data) {
+ *q = *p ^ 0xff;
+ if (carry)
+ carry = !++*q;
+ p--;
+ q--;
+ }
+ } else {
+ data->negative = 0;
+ data->length = len;
+
+ if (p[0] == 0) {
+ p++;
+ data->length--;
+ }
+ data->data = malloc(data->length);
+ if (data->data == NULL && data->length != 0) {
+ data->length = 0;
+ if (size)
+ *size = 0;
+ return ENOMEM;
+ }
+ memcpy(data->data, p, data->length);
+ }
+ if (size)
+ *size = len;
+ return 0;
+}
+
+static int
+generalizedtime2time (const char *s, time_t *t)
+{
+ struct tm tm;
+
+ memset(&tm, 0, sizeof(tm));
+ if (sscanf (s, "%04d%02d%02d%02d%02d%02dZ",
+ &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour,
+ &tm.tm_min, &tm.tm_sec) != 6) {
+ if (sscanf (s, "%02d%02d%02d%02d%02d%02dZ",
+ &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour,
+ &tm.tm_min, &tm.tm_sec) != 6)
+ return ASN1_BAD_TIMEFORMAT;
+ if (tm.tm_year < 50)
+ tm.tm_year += 2000;
+ else
+ tm.tm_year += 1900;
+ }
+ tm.tm_year -= 1900;
+ tm.tm_mon -= 1;
+ *t = _der_timegm (&tm);
+ return 0;
+}
+
+static int
+der_get_time (const unsigned char *p, size_t len,
+ time_t *data, size_t *size)
+{
+ char *times;
+ int e;
+
+ if (len > len + 1 || len == 0)
+ return ASN1_BAD_LENGTH;
+
+ times = malloc(len + 1);
+ if (times == NULL)
+ return ENOMEM;
+ memcpy(times, p, len);
+ times[len] = '\0';
+ e = generalizedtime2time(times, data);
+ free (times);
+ if(size) *size = len;
+ return e;
+}
+
+int
+der_get_generalized_time (const unsigned char *p, size_t len,
+ time_t *data, size_t *size)
+{
+ return der_get_time(p, len, data, size);
+}
+
+int
+der_get_utctime (const unsigned char *p, size_t len,
+ time_t *data, size_t *size)
+{
+ return der_get_time(p, len, data, size);
+}
+
+int
+der_get_oid (const unsigned char *p, size_t len,
+ heim_oid *data, size_t *size)
+{
+ size_t n;
+ size_t oldlen = len;
+
+ if (len < 1)
+ return ASN1_OVERRUN;
+
+ if (len > len + 1)
+ return ASN1_BAD_LENGTH;
+
+ if (len + 1 > UINT_MAX/sizeof(data->components[0]))
+ return ERANGE;
+
+ data->components = malloc((len + 1) * sizeof(data->components[0]));
+ if (data->components == NULL)
+ return ENOMEM;
+ data->components[0] = (*p) / 40;
+ data->components[1] = (*p) % 40;
+ --len;
+ ++p;
+ for (n = 2; len > 0; ++n) {
+ unsigned u = 0, u1;
+
+ do {
+ --len;
+ u1 = u * 128 + (*p++ % 128);
+ /* check that we don't overflow the element */
+ if (u1 < u) {
+ der_free_oid(data);
+ return ASN1_OVERRUN;
+ }
+ u = u1;
+ } while (len > 0 && p[-1] & 0x80);
+ data->components[n] = u;
+ }
+ if (n > 2 && p[-1] & 0x80) {
+ der_free_oid (data);
+ return ASN1_OVERRUN;
+ }
+ data->length = n;
+ if (size)
+ *size = oldlen;
+ return 0;
+}
+
+int
+der_get_tag (const unsigned char *p, size_t len,
+ Der_class *class, Der_type *type,
+ unsigned int *tag, size_t *size)
+{
+ size_t ret = 0;
+ if (len < 1)
+ return ASN1_OVERRUN;
+ *class = (Der_class)(((*p) >> 6) & 0x03);
+ *type = (Der_type)(((*p) >> 5) & 0x01);
+ *tag = (*p) & 0x1f;
+ p++; len--; ret++;
+ if(*tag == 0x1f) {
+ unsigned int continuation;
+ unsigned int tag1;
+ *tag = 0;
+ do {
+ if(len < 1)
+ return ASN1_OVERRUN;
+ continuation = *p & 128;
+ tag1 = *tag * 128 + (*p % 128);
+ /* check that we don't overflow the tag */
+ if (tag1 < *tag)
+ return ASN1_OVERFLOW;
+ *tag = tag1;
+ p++; len--; ret++;
+ } while(continuation);
+ }
+ if(size) *size = ret;
+ return 0;
+}
+
+int
+der_match_tag (const unsigned char *p, size_t len,
+ Der_class class, Der_type type,
+ unsigned int tag, size_t *size)
+{
+ size_t l;
+ Der_class thisclass;
+ Der_type thistype;
+ unsigned int thistag;
+ int e;
+
+ e = der_get_tag (p, len, &thisclass, &thistype, &thistag, &l);
+ if (e) return e;
+ if (class != thisclass || type != thistype)
+ return ASN1_BAD_ID;
+ if(tag > thistag)
+ return ASN1_MISPLACED_FIELD;
+ if(tag < thistag)
+ return ASN1_MISSING_FIELD;
+ if(size) *size = l;
+ return 0;
+}
+
+int
+der_match_tag_and_length (const unsigned char *p, size_t len,
+ Der_class class, Der_type type, unsigned int tag,
+ size_t *length_ret, size_t *size)
+{
+ size_t l, ret = 0;
+ int e;
+
+ e = der_match_tag (p, len, class, type, tag, &l);
+ if (e) return e;
+ p += l;
+ len -= l;
+ ret += l;
+ e = der_get_length (p, len, length_ret, &l);
+ if (e) return e;
+ p += l;
+ len -= l;
+ ret += l;
+ if(size) *size = ret;
+ return 0;
+}
+
+/*
+ * Old versions of DCE was based on a very early beta of the MIT code,
+ * which used MAVROS for ASN.1 encoding. MAVROS had the interesting
+ * feature that it encoded data in the forward direction, which has
+ * it's problems, since you have no idea how long the data will be
+ * until after you're done. MAVROS solved this by reserving one byte
+ * for length, and later, if the actual length was longer, it reverted
+ * to indefinite, BER style, lengths. The version of MAVROS used by
+ * the DCE people could apparently generate correct X.509 DER encodings, and
+ * did this by making space for the length after encoding, but
+ * unfortunately this feature wasn't used with Kerberos.
+ */
+
+int
+_heim_fix_dce(size_t reallen, size_t *len)
+{
+ if(reallen == ASN1_INDEFINITE)
+ return 1;
+ if(*len < reallen)
+ return -1;
+ *len = reallen;
+ return 0;
+}
+
+int
+der_get_bit_string (const unsigned char *p, size_t len,
+ heim_bit_string *data, size_t *size)
+{
+ if (len < 1)
+ return ASN1_OVERRUN;
+ if (p[0] > 7)
+ return ASN1_BAD_FORMAT;
+ if (len - 1 == 0 && p[0] != 0)
+ return ASN1_BAD_FORMAT;
+ /* check if any of the three upper bits are set
+ * any of them will cause a interger overrun */
+ if ((len - 1) >> (sizeof(len) * 8 - 3))
+ return ASN1_OVERRUN;
+ data->length = (len - 1) * 8;
+ data->data = malloc(len - 1);
+ if (data->data == NULL && (len - 1) != 0)
+ return ENOMEM;
+ memcpy (data->data, p + 1, len - 1);
+ data->length -= p[0];
+ if(size) *size = len;
+ return 0;
+}
diff --git a/source4/heimdal/lib/asn1/der_length.c b/source4/heimdal/lib/asn1/der_length.c
new file mode 100644
index 0000000000..5ea3a84845
--- /dev/null
+++ b/source4/heimdal/lib/asn1/der_length.c
@@ -0,0 +1,232 @@
+/*
+ * Copyright (c) 1997-2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "der_locl.h"
+
+RCSID("$Id$");
+
+size_t
+_heim_len_unsigned (unsigned val)
+{
+ size_t ret = 0;
+ int last_val_gt_128;
+
+ do {
+ ++ret;
+ last_val_gt_128 = (val >= 128);
+ val /= 256;
+ } while (val);
+
+ if(last_val_gt_128)
+ ret++;
+
+ return ret;
+}
+
+size_t
+_heim_len_int (int val)
+{
+ unsigned char q;
+ size_t ret = 0;
+
+ if (val >= 0) {
+ do {
+ q = val % 256;
+ ret++;
+ val /= 256;
+ } while(val);
+ if(q >= 128)
+ ret++;
+ } else {
+ val = ~val;
+ do {
+ q = ~(val % 256);
+ ret++;
+ val /= 256;
+ } while(val);
+ if(q < 128)
+ ret++;
+ }
+ return ret;
+}
+
+static size_t
+len_oid (const heim_oid *oid)
+{
+ size_t ret = 1;
+ int n;
+
+ for (n = 2; n < oid->length; ++n) {
+ unsigned u = oid->components[n];
+
+ do {
+ ++ret;
+ u /= 128;
+ } while(u > 0);
+ }
+ return ret;
+}
+
+size_t
+der_length_len (size_t len)
+{
+ if (len < 128)
+ return 1;
+ else {
+ int ret = 0;
+ do {
+ ++ret;
+ len /= 256;
+ } while (len);
+ return ret + 1;
+ }
+}
+
+size_t
+der_length_integer (const int *data)
+{
+ return _heim_len_int (*data);
+}
+
+size_t
+der_length_unsigned (const unsigned *data)
+{
+ return _heim_len_unsigned(*data);
+}
+
+size_t
+der_length_enumerated (const unsigned *data)
+{
+ return _heim_len_int (*data);
+}
+
+size_t
+der_length_general_string (const heim_general_string *data)
+{
+ return strlen(*data);
+}
+
+size_t
+der_length_utf8string (const heim_utf8_string *data)
+{
+ return strlen(*data);
+}
+
+size_t
+der_length_printable_string (const heim_printable_string *data)
+{
+ return strlen(*data);
+}
+
+size_t
+der_length_ia5_string (const heim_ia5_string *data)
+{
+ return strlen(*data);
+}
+
+size_t
+der_length_bmp_string (const heim_bmp_string *data)
+{
+ return data->length * 2;
+}
+
+size_t
+der_length_universal_string (const heim_universal_string *data)
+{
+ return data->length * 4;
+}
+
+size_t
+der_length_visible_string (const heim_visible_string *data)
+{
+ return strlen(*data);
+}
+
+size_t
+der_length_octet_string (const heim_octet_string *k)
+{
+ return k->length;
+}
+
+size_t
+der_length_heim_integer (const heim_integer *k)
+{
+ if (k->length == 0)
+ return 1;
+ if (k->negative)
+ return k->length + (((~(((unsigned char *)k->data)[0])) & 0x80) ? 0 : 1);
+ else
+ return k->length + ((((unsigned char *)k->data)[0] & 0x80) ? 1 : 0);
+}
+
+size_t
+der_length_oid (const heim_oid *k)
+{
+ return len_oid (k);
+}
+
+size_t
+der_length_generalized_time (const time_t *t)
+{
+ heim_octet_string k;
+ size_t ret;
+
+ _heim_time2generalizedtime (*t, &k, 1);
+ ret = k.length;
+ free(k.data);
+ return ret;
+}
+
+size_t
+der_length_utctime (const time_t *t)
+{
+ heim_octet_string k;
+ size_t ret;
+
+ _heim_time2generalizedtime (*t, &k, 0);
+ ret = k.length;
+ free(k.data);
+ return ret;
+}
+
+size_t
+der_length_boolean (const int *k)
+{
+ return 1;
+}
+
+size_t
+der_length_bit_string (const heim_bit_string *k)
+{
+ return (k->length + 7) / 8 + 1;
+}
diff --git a/source4/heimdal/lib/asn1/der_locl.h b/source4/heimdal/lib/asn1/der_locl.h
new file mode 100644
index 0000000000..f8a21de71c
--- /dev/null
+++ b/source4/heimdal/lib/asn1/der_locl.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 1997 - 2002, 2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef __DER_LOCL_H__
+#define __DER_LOCL_H__
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <ctype.h>
+#include <time.h>
+#include <errno.h>
+#include <roken.h>
+
+#include <asn1-common.h>
+#include <asn1_err.h>
+#include <der.h>
+
+time_t _der_timegm (struct tm *);
+size_t _heim_len_unsigned (unsigned);
+size_t _heim_len_int (int);
+
+#endif /* __DER_LOCL_H__ */
diff --git a/source4/heimdal/lib/asn1/der_put.c b/source4/heimdal/lib/asn1/der_put.c
new file mode 100644
index 0000000000..5afddb1d05
--- /dev/null
+++ b/source4/heimdal/lib/asn1/der_put.c
@@ -0,0 +1,483 @@
+/*
+ * Copyright (c) 1997-2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "der_locl.h"
+
+RCSID("$Id$");
+
+/*
+ * All encoding functions take a pointer `p' to first position in
+ * which to write, from the right, `len' which means the maximum
+ * number of characters we are able to write. The function returns
+ * the number of characters written in `size' (if non-NULL).
+ * The return value is 0 or an error.
+ */
+
+int
+der_put_unsigned (unsigned char *p, size_t len, const unsigned *v, size_t *size)
+{
+ unsigned char *base = p;
+ unsigned val = *v;
+
+ if (val) {
+ while (len > 0 && val) {
+ *p-- = val % 256;
+ val /= 256;
+ --len;
+ }
+ if (val != 0)
+ return ASN1_OVERFLOW;
+ else {
+ if(p[1] >= 128) {
+ if(len < 1)
+ return ASN1_OVERFLOW;
+ *p-- = 0;
+ }
+ *size = base - p;
+ return 0;
+ }
+ } else if (len < 1)
+ return ASN1_OVERFLOW;
+ else {
+ *p = 0;
+ *size = 1;
+ return 0;
+ }
+}
+
+int
+der_put_integer (unsigned char *p, size_t len, const int *v, size_t *size)
+{
+ unsigned char *base = p;
+ int val = *v;
+
+ if(val >= 0) {
+ do {
+ if(len < 1)
+ return ASN1_OVERFLOW;
+ *p-- = val % 256;
+ len--;
+ val /= 256;
+ } while(val);
+ if(p[1] >= 128) {
+ if(len < 1)
+ return ASN1_OVERFLOW;
+ *p-- = 0;
+ len--;
+ }
+ } else {
+ val = ~val;
+ do {
+ if(len < 1)
+ return ASN1_OVERFLOW;
+ *p-- = ~(val % 256);
+ len--;
+ val /= 256;
+ } while(val);
+ if(p[1] < 128) {
+ if(len < 1)
+ return ASN1_OVERFLOW;
+ *p-- = 0xff;
+ len--;
+ }
+ }
+ *size = base - p;
+ return 0;
+}
+
+
+int
+der_put_length (unsigned char *p, size_t len, size_t val, size_t *size)
+{
+ if (len < 1)
+ return ASN1_OVERFLOW;
+
+ if (val < 128) {
+ *p = val;
+ *size = 1;
+ } else {
+ size_t l = 0;
+
+ while(val > 0) {
+ if(len < 2)
+ return ASN1_OVERFLOW;
+ *p-- = val % 256;
+ val /= 256;
+ len--;
+ l++;
+ }
+ *p = 0x80 | l;
+ if(size)
+ *size = l + 1;
+ }
+ return 0;
+}
+
+int
+der_put_boolean(unsigned char *p, size_t len, const int *data, size_t *size)
+{
+ if(len < 1)
+ return ASN1_OVERFLOW;
+ if(*data != 0)
+ *p = 0xff;
+ else
+ *p = 0;
+ *size = 1;
+ return 0;
+}
+
+int
+der_put_general_string (unsigned char *p, size_t len,
+ const heim_general_string *str, size_t *size)
+{
+ size_t slen = strlen(*str);
+
+ if (len < slen)
+ return ASN1_OVERFLOW;
+ p -= slen;
+ len -= slen;
+ memcpy (p+1, *str, slen);
+ *size = slen;
+ return 0;
+}
+
+int
+der_put_utf8string (unsigned char *p, size_t len,
+ const heim_utf8_string *str, size_t *size)
+{
+ return der_put_general_string(p, len, str, size);
+}
+
+int
+der_put_printable_string (unsigned char *p, size_t len,
+ const heim_printable_string *str, size_t *size)
+{
+ return der_put_general_string(p, len, str, size);
+}
+
+int
+der_put_ia5_string (unsigned char *p, size_t len,
+ const heim_ia5_string *str, size_t *size)
+{
+ return der_put_general_string(p, len, str, size);
+}
+
+int
+der_put_bmp_string (unsigned char *p, size_t len,
+ const heim_bmp_string *data, size_t *size)
+{
+ size_t i;
+ if (len / 2 < data->length)
+ return ASN1_OVERFLOW;
+ p -= data->length * 2;
+ len -= data->length * 2;
+ for (i = 0; i < data->length; i++) {
+ p[1] = (data->data[i] >> 8) & 0xff;
+ p[2] = data->data[i] & 0xff;
+ p += 2;
+ }
+ if (size) *size = data->length * 2;
+ return 0;
+}
+
+int
+der_put_universal_string (unsigned char *p, size_t len,
+ const heim_universal_string *data, size_t *size)
+{
+ size_t i;
+ if (len / 4 < data->length)
+ return ASN1_OVERFLOW;
+ p -= data->length * 4;
+ len -= data->length * 4;
+ for (i = 0; i < data->length; i++) {
+ p[1] = (data->data[i] >> 24) & 0xff;
+ p[2] = (data->data[i] >> 16) & 0xff;
+ p[3] = (data->data[i] >> 8) & 0xff;
+ p[4] = data->data[i] & 0xff;
+ p += 4;
+ }
+ if (size) *size = data->length * 4;
+ return 0;
+}
+
+int
+der_put_visible_string (unsigned char *p, size_t len,
+ const heim_visible_string *str, size_t *size)
+{
+ return der_put_general_string(p, len, str, size);
+}
+
+int
+der_put_octet_string (unsigned char *p, size_t len,
+ const heim_octet_string *data, size_t *size)
+{
+ if (len < data->length)
+ return ASN1_OVERFLOW;
+ p -= data->length;
+ len -= data->length;
+ memcpy (p+1, data->data, data->length);
+ *size = data->length;
+ return 0;
+}
+
+int
+der_put_heim_integer (unsigned char *p, size_t len,
+ const heim_integer *data, size_t *size)
+{
+ unsigned char *buf = data->data;
+ int hibitset = 0;
+
+ if (data->length == 0) {
+ if (len < 1)
+ return ASN1_OVERFLOW;
+ *p-- = 0;
+ if (size)
+ *size = 1;
+ return 0;
+ }
+ if (len < data->length)
+ return ASN1_OVERFLOW;
+
+ len -= data->length;
+
+ if (data->negative) {
+ int i, carry;
+ for (i = data->length - 1, carry = 1; i >= 0; i--) {
+ *p = buf[i] ^ 0xff;
+ if (carry)
+ carry = !++*p;
+ p--;
+ }
+ if (p[1] < 128) {
+ if (len < 1)
+ return ASN1_OVERFLOW;
+ *p-- = 0xff;
+ len--;
+ hibitset = 1;
+ }
+ } else {
+ p -= data->length;
+ memcpy(p + 1, buf, data->length);
+
+ if (p[1] >= 128) {
+ if (len < 1)
+ return ASN1_OVERFLOW;
+ p[0] = 0;
+ len--;
+ hibitset = 1;
+ }
+ }
+ if (size)
+ *size = data->length + hibitset;
+ return 0;
+}
+
+int
+der_put_generalized_time (unsigned char *p, size_t len,
+ const time_t *data, size_t *size)
+{
+ heim_octet_string k;
+ size_t l;
+ int e;
+
+ e = _heim_time2generalizedtime (*data, &k, 1);
+ if (e)
+ return e;
+ e = der_put_octet_string(p, len, &k, &l);
+ free(k.data);
+ if(e)
+ return e;
+ if(size)
+ *size = l;
+ return 0;
+}
+
+int
+der_put_utctime (unsigned char *p, size_t len,
+ const time_t *data, size_t *size)
+{
+ heim_octet_string k;
+ size_t l;
+ int e;
+
+ e = _heim_time2generalizedtime (*data, &k, 0);
+ if (e)
+ return e;
+ e = der_put_octet_string(p, len, &k, &l);
+ free(k.data);
+ if(e)
+ return e;
+ if(size)
+ *size = l;
+ return 0;
+}
+
+int
+der_put_oid (unsigned char *p, size_t len,
+ const heim_oid *data, size_t *size)
+{
+ unsigned char *base = p;
+ int n;
+
+ for (n = data->length - 1; n >= 2; --n) {
+ unsigned u = data->components[n];
+
+ if (len < 1)
+ return ASN1_OVERFLOW;
+ *p-- = u % 128;
+ u /= 128;
+ --len;
+ while (u > 0) {
+ if (len < 1)
+ return ASN1_OVERFLOW;
+ *p-- = 128 + u % 128;
+ u /= 128;
+ --len;
+ }
+ }
+ if (len < 1)
+ return ASN1_OVERFLOW;
+ *p-- = 40 * data->components[0] + data->components[1];
+ *size = base - p;
+ return 0;
+}
+
+int
+der_put_tag (unsigned char *p, size_t len, Der_class class, Der_type type,
+ unsigned int tag, size_t *size)
+{
+ if (tag <= 30) {
+ if (len < 1)
+ return ASN1_OVERFLOW;
+ *p = MAKE_TAG(class, type, tag);
+ *size = 1;
+ } else {
+ size_t ret = 0;
+ unsigned int continuation = 0;
+
+ do {
+ if (len < 1)
+ return ASN1_OVERFLOW;
+ *p-- = tag % 128 | continuation;
+ len--;
+ ret++;
+ tag /= 128;
+ continuation = 0x80;
+ } while(tag > 0);
+ if (len < 1)
+ return ASN1_OVERFLOW;
+ *p-- = MAKE_TAG(class, type, 0x1f);
+ ret++;
+ *size = ret;
+ }
+ return 0;
+}
+
+int
+der_put_length_and_tag (unsigned char *p, size_t len, size_t len_val,
+ Der_class class, Der_type type,
+ unsigned int tag, size_t *size)
+{
+ size_t ret = 0;
+ size_t l;
+ int e;
+
+ e = der_put_length (p, len, len_val, &l);
+ if(e)
+ return e;
+ p -= l;
+ len -= l;
+ ret += l;
+ e = der_put_tag (p, len, class, type, tag, &l);
+ if(e)
+ return e;
+ p -= l;
+ len -= l;
+ ret += l;
+ *size = ret;
+ return 0;
+}
+
+int
+_heim_time2generalizedtime (time_t t, heim_octet_string *s, int gtimep)
+{
+ struct tm *tm;
+ const size_t len = gtimep ? 15 : 13;
+
+ s->data = malloc(len + 1);
+ if (s->data == NULL)
+ return ENOMEM;
+ s->length = len;
+ tm = gmtime (&t);
+ if (gtimep)
+ snprintf (s->data, len + 1, "%04d%02d%02d%02d%02d%02dZ",
+ tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+ else
+ snprintf (s->data, len + 1, "%02d%02d%02d%02d%02d%02dZ",
+ tm->tm_year % 100, tm->tm_mon + 1, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+ return 0;
+}
+
+int
+der_put_bit_string (unsigned char *p, size_t len,
+ const heim_bit_string *data, size_t *size)
+{
+ size_t data_size = (data->length + 7) / 8;
+ if (len < data_size + 1)
+ return ASN1_OVERFLOW;
+ p -= data_size + 1;
+ len -= data_size + 1;
+ memcpy (p+2, data->data, data_size);
+ if (data->length && (data->length % 8) != 0)
+ p[1] = 8 - (data->length % 8);
+ else
+ p[1] = 0;
+ *size = data_size + 1;
+ return 0;
+}
+
+int
+_heim_der_set_sort(const void *a1, const void *a2)
+{
+ const struct heim_octet_string *s1 = a1, *s2 = a2;
+ int ret;
+
+ ret = memcmp(s1->data, s2->data,
+ s1->length < s2->length ? s1->length : s2->length);
+ if(ret)
+ return ret;
+ return s1->length - s2->length;
+}
diff --git a/source4/heimdal/lib/asn1/digest.asn1 b/source4/heimdal/lib/asn1/digest.asn1
new file mode 100644
index 0000000000..1cf58b4638
--- /dev/null
+++ b/source4/heimdal/lib/asn1/digest.asn1
@@ -0,0 +1,164 @@
+-- $Id$
+
+DIGEST DEFINITIONS ::=
+BEGIN
+
+IMPORTS EncryptedData, Principal FROM krb5;
+
+DigestTypes ::= BIT STRING {
+ ntlm-v1(0),
+ ntlm-v1-session(1),
+ ntlm-v2(2),
+ digest-md5(3),
+ chap-md5(4),
+ ms-chap-v2(5)
+}
+
+DigestInit ::= SEQUENCE {
+ type UTF8String, -- http, sasl, chap, cram-md5 --
+ channel [0] SEQUENCE {
+ cb-type UTF8String,
+ cb-binding UTF8String
+ } OPTIONAL,
+ hostname [1] UTF8String OPTIONAL -- for chap/cram-md5
+}
+
+DigestInitReply ::= SEQUENCE {
+ nonce UTF8String, -- service nonce/challange
+ opaque UTF8String, -- server state
+ identifier [0] UTF8String OPTIONAL
+}
+
+
+DigestRequest ::= SEQUENCE {
+ type UTF8String, -- http, sasl-md5, chap, cram-md5 --
+ digest UTF8String, -- http:md5/md5-sess sasl:clear/int/conf --
+ username UTF8String, -- username user used
+ responseData UTF8String, -- client response
+ authid [0] UTF8String OPTIONAL,
+ authentication-user [1] Principal OPTIONAL, -- principal to get key from
+ realm [2] UTF8String OPTIONAL,
+ method [3] UTF8String OPTIONAL,
+ uri [4] UTF8String OPTIONAL,
+ serverNonce UTF8String, -- same as "DigestInitReply.nonce"
+ clientNonce [5] UTF8String OPTIONAL,
+ nonceCount [6] UTF8String OPTIONAL,
+ qop [7] UTF8String OPTIONAL,
+ identifier [8] UTF8String OPTIONAL,
+ hostname [9] UTF8String OPTIONAL,
+ opaque UTF8String -- same as "DigestInitReply.opaque"
+}
+-- opaque = hex(cksum(type|serverNonce|identifier|hostname,digest-key))
+-- serverNonce = hex(time[4bytes]random[12bytes])(-cbType:cbBinding)
+
+
+DigestError ::= SEQUENCE {
+ reason UTF8String,
+ code INTEGER (-2147483648..2147483647)
+}
+
+DigestResponse ::= SEQUENCE {
+ success BOOLEAN,
+ rsp [0] UTF8String OPTIONAL,
+ tickets [1] SEQUENCE OF OCTET STRING OPTIONAL,
+ channel [2] SEQUENCE {
+ cb-type UTF8String,
+ cb-binding UTF8String
+ } OPTIONAL,
+ session-key [3] OCTET STRING OPTIONAL
+}
+
+NTLMInit ::= SEQUENCE {
+ flags [0] INTEGER (0..4294967295),
+ hostname [1] UTF8String OPTIONAL,
+ domain [1] UTF8String OPTIONAL
+}
+
+NTLMInitReply ::= SEQUENCE {
+ flags [0] INTEGER (0..4294967295),
+ opaque [1] OCTET STRING,
+ targetname [2] UTF8String,
+ challange [3] OCTET STRING,
+ targetinfo [4] OCTET STRING OPTIONAL
+}
+
+NTLMRequest ::= SEQUENCE {
+ flags [0] INTEGER (0..4294967295),
+ opaque [1] OCTET STRING,
+ username [2] UTF8String,
+ targetname [3] UTF8String,
+ targetinfo [4] OCTET STRING OPTIONAL,
+ lm [5] OCTET STRING,
+ ntlm [6] OCTET STRING,
+ sessionkey [7] OCTET STRING OPTIONAL
+}
+
+NTLMResponse ::= SEQUENCE {
+ success [0] BOOLEAN,
+ flags [1] INTEGER (0..4294967295),
+ sessionkey [2] OCTET STRING OPTIONAL,
+ tickets [3] SEQUENCE OF OCTET STRING OPTIONAL
+}
+
+DigestReqInner ::= CHOICE {
+ init [0] DigestInit,
+ digestRequest [1] DigestRequest,
+ ntlmInit [2] NTLMInit,
+ ntlmRequest [3] NTLMRequest,
+ supportedMechs [4] NULL
+}
+
+DigestREQ ::= [APPLICATION 128] SEQUENCE {
+ apReq [0] OCTET STRING,
+ innerReq [1] EncryptedData
+}
+
+DigestRepInner ::= CHOICE {
+ error [0] DigestError,
+ initReply [1] DigestInitReply,
+ response [2] DigestResponse,
+ ntlmInitReply [3] NTLMInitReply,
+ ntlmResponse [4] NTLMResponse,
+ supportedMechs [5] DigestTypes,
+ ...
+}
+
+DigestREP ::= [APPLICATION 129] SEQUENCE {
+ apRep [0] OCTET STRING,
+ innerRep [1] EncryptedData
+}
+
+
+-- HTTP
+
+-- md5
+-- A1 = unq(username-value) ":" unq(realm-value) ":" passwd
+-- md5-sess
+-- A1 = HEX(H(unq(username-value) ":" unq(realm-value) ":" passwd ) ":" unq(nonce-value) ":" unq(cnonce-value))
+
+-- qop == auth
+-- A2 = Method ":" digest-uri-value
+-- qop == auth-int
+-- A2 = Method ":" digest-uri-value ":" H(entity-body)
+
+-- request-digest = HEX(KD(HEX(H(A1)),
+-- unq(nonce-value) ":" nc-value ":" unq(cnonce-value) ":" unq(qop-value) ":" HEX(H(A2))))
+-- no "qop"
+-- request-digest = HEX(KD(HEX(H(A1)), unq(nonce-value) ":" HEX(H(A2))))
+
+
+-- SASL:
+-- SS = H( { unq(username-value), ":", unq(realm-value), ":", password } )
+-- A1 = { SS, ":", unq(nonce-value), ":", unq(cnonce-value) }
+-- A1 = { SS, ":", unq(nonce-value), ":", unq(cnonce-value), ":", unq(authzid-value) }
+
+-- A2 = "AUTHENTICATE:", ":", digest-uri-value
+-- qop == auth-int,auth-conf
+-- A2 = "AUTHENTICATE:", ":", digest-uri-value, ":00000000000000000000000000000000"
+
+-- response-value = HEX( KD ( HEX(H(A1)),
+-- { unq(nonce-value), ":" nc-value, ":",
+-- unq(cnonce-value), ":", qop-value, ":",
+-- HEX(H(A2)) }))
+
+END
diff --git a/source4/heimdal/lib/asn1/extra.c b/source4/heimdal/lib/asn1/extra.c
new file mode 100644
index 0000000000..49240605c8
--- /dev/null
+++ b/source4/heimdal/lib/asn1/extra.c
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2003 - 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "der_locl.h"
+#include "heim_asn1.h"
+
+RCSID("$Id$");
+
+int
+encode_heim_any(unsigned char *p, size_t len,
+ const heim_any *data, size_t *size)
+{
+ if (data->length > len)
+ return ASN1_OVERFLOW;
+ p -= data->length;
+ len -= data->length;
+ memcpy (p+1, data->data, data->length);
+ *size = data->length;
+ return 0;
+}
+
+int
+decode_heim_any(const unsigned char *p, size_t len,
+ heim_any *data, size_t *size)
+{
+ size_t len_len, length, l;
+ Der_class thisclass;
+ Der_type thistype;
+ unsigned int thistag;
+ int e;
+
+ memset(data, 0, sizeof(*data));
+
+ e = der_get_tag (p, len, &thisclass, &thistype, &thistag, &l);
+ if (e) return e;
+ if (l > len)
+ return ASN1_OVERFLOW;
+ e = der_get_length(p + l, len - l, &length, &len_len);
+ if (e) return e;
+ if (length + len_len + l > len)
+ return ASN1_OVERFLOW;
+
+ data->data = malloc(length + len_len + l);
+ if (data->data == NULL)
+ return ENOMEM;
+ data->length = length + len_len + l;
+ memcpy(data->data, p, length + len_len + l);
+
+ if (size)
+ *size = length + len_len + l;
+
+ return 0;
+}
+
+void
+free_heim_any(heim_any *data)
+{
+ free(data->data);
+ data->data = NULL;
+}
+
+size_t
+length_heim_any(const heim_any *data)
+{
+ return data->length;
+}
+
+int
+copy_heim_any(const heim_any *from, heim_any *to)
+{
+ to->data = malloc(from->length);
+ if (to->data == NULL && from->length != 0)
+ return ENOMEM;
+ memcpy(to->data, from->data, from->length);
+ to->length = from->length;
+ return 0;
+}
+
+int
+encode_heim_any_set(unsigned char *p, size_t len,
+ const heim_any_set *data, size_t *size)
+{
+ return encode_heim_any(p, len, data, size);
+}
+
+
+int
+decode_heim_any_set(const unsigned char *p, size_t len,
+ heim_any_set *data, size_t *size)
+{
+ memset(data, 0, sizeof(*data));
+ data->data = malloc(len);
+ if (data->data == NULL && len != 0)
+ return ENOMEM;
+ data->length = len;
+ memcpy(data->data, p, len);
+ if (size) *size = len;
+ return 0;
+}
+
+void
+free_heim_any_set(heim_any_set *data)
+{
+ free_heim_any(data);
+}
+
+size_t
+length_heim_any_set(const heim_any *data)
+{
+ return length_heim_any(data);
+}
+
+int
+copy_heim_any_set(const heim_any_set *from, heim_any_set *to)
+{
+ return copy_heim_any(from, to);
+}
+
+int
+heim_any_cmp(const heim_any_set *p, const heim_any_set *q)
+{
+ if (p->length != q->length)
+ return p->length - q->length;
+ return memcmp(p->data, q->data, p->length);
+}
diff --git a/source4/heimdal/lib/asn1/gen.c b/source4/heimdal/lib/asn1/gen.c
new file mode 100644
index 0000000000..52fd0d393b
--- /dev/null
+++ b/source4/heimdal/lib/asn1/gen.c
@@ -0,0 +1,802 @@
+/*
+ * Copyright (c) 1997 - 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "gen_locl.h"
+
+RCSID("$Id$");
+
+FILE *headerfile, *codefile, *logfile;
+
+#define STEM "asn1"
+
+static const char *orig_filename;
+static char *header;
+static const char *headerbase = STEM;
+
+/*
+ * list of all IMPORTs
+ */
+
+struct import {
+ const char *module;
+ struct import *next;
+};
+
+static struct import *imports = NULL;
+
+void
+add_import (const char *module)
+{
+ struct import *tmp = emalloc (sizeof(*tmp));
+
+ tmp->module = module;
+ tmp->next = imports;
+ imports = tmp;
+
+ fprintf (headerfile, "#include <%s_asn1.h>\n", module);
+}
+
+const char *
+get_filename (void)
+{
+ return orig_filename;
+}
+
+void
+init_generate (const char *filename, const char *base)
+{
+ char *fn;
+
+ orig_filename = filename;
+ if (base != NULL) {
+ headerbase = strdup(base);
+ if (headerbase == NULL)
+ errx(1, "strdup");
+ }
+ asprintf(&header, "%s.h", headerbase);
+ if (header == NULL)
+ errx(1, "malloc");
+ headerfile = fopen (header, "w");
+ if (headerfile == NULL)
+ err (1, "open %s", header);
+ fprintf (headerfile,
+ "/* Generated from %s */\n"
+ "/* Do not edit */\n\n",
+ filename);
+ fprintf (headerfile,
+ "#ifndef __%s_h__\n"
+ "#define __%s_h__\n\n", headerbase, headerbase);
+ fprintf (headerfile,
+ "#include <stddef.h>\n"
+ "#include <time.h>\n\n");
+ fprintf (headerfile,
+ "#ifndef __asn1_common_definitions__\n"
+ "#define __asn1_common_definitions__\n\n");
+ fprintf (headerfile,
+ "typedef struct heim_integer {\n"
+ " size_t length;\n"
+ " void *data;\n"
+ " int negative;\n"
+ "} heim_integer;\n\n");
+ fprintf (headerfile,
+ "typedef struct heim_octet_string {\n"
+ " size_t length;\n"
+ " void *data;\n"
+ "} heim_octet_string;\n\n");
+ fprintf (headerfile,
+ "typedef char *heim_general_string;\n\n"
+ );
+ fprintf (headerfile,
+ "typedef char *heim_utf8_string;\n\n"
+ );
+ fprintf (headerfile,
+ "typedef char *heim_printable_string;\n\n"
+ );
+ fprintf (headerfile,
+ "typedef char *heim_ia5_string;\n\n"
+ );
+ fprintf (headerfile,
+ "typedef struct heim_bmp_string {\n"
+ " size_t length;\n"
+ " uint16_t *data;\n"
+ "} heim_bmp_string;\n\n");
+ fprintf (headerfile,
+ "typedef struct heim_universal_string {\n"
+ " size_t length;\n"
+ " uint32_t *data;\n"
+ "} heim_universal_string;\n\n");
+ fprintf (headerfile,
+ "typedef char *heim_visible_string;\n\n"
+ );
+ fprintf (headerfile,
+ "typedef struct heim_oid {\n"
+ " size_t length;\n"
+ " unsigned *components;\n"
+ "} heim_oid;\n\n");
+ fprintf (headerfile,
+ "typedef struct heim_bit_string {\n"
+ " size_t length;\n"
+ " void *data;\n"
+ "} heim_bit_string;\n\n");
+ fprintf (headerfile,
+ "typedef struct heim_octet_string heim_any;\n"
+ "typedef struct heim_octet_string heim_any_set;\n\n");
+ fputs("#define ASN1_MALLOC_ENCODE(T, B, BL, S, L, R) \\\n"
+ " do { \\\n"
+ " (BL) = length_##T((S)); \\\n"
+ " (B) = malloc((BL)); \\\n"
+ " if((B) == NULL) { \\\n"
+ " (R) = ENOMEM; \\\n"
+ " } else { \\\n"
+ " (R) = encode_##T(((unsigned char*)(B)) + (BL) - 1, (BL), \\\n"
+ " (S), (L)); \\\n"
+ " if((R) != 0) { \\\n"
+ " free((B)); \\\n"
+ " (B) = NULL; \\\n"
+ " } \\\n"
+ " } \\\n"
+ " } while (0)\n\n",
+ headerfile);
+ fprintf (headerfile, "struct units;\n\n");
+ fprintf (headerfile, "#endif\n\n");
+ asprintf(&fn, "%s_files", base);
+ if (fn == NULL)
+ errx(1, "malloc");
+ logfile = fopen(fn, "w");
+ if (logfile == NULL)
+ err (1, "open %s", fn);
+}
+
+void
+close_generate (void)
+{
+ fprintf (headerfile, "#endif /* __%s_h__ */\n", headerbase);
+
+ fclose (headerfile);
+ fprintf (logfile, "\n");
+ fclose (logfile);
+}
+
+void
+gen_assign_defval(const char *var, struct value *val)
+{
+ switch(val->type) {
+ case stringvalue:
+ fprintf(codefile, "if((%s = strdup(\"%s\")) == NULL)\nreturn ENOMEM;\n", var, val->u.stringvalue);
+ break;
+ case integervalue:
+ fprintf(codefile, "%s = %d;\n", var, val->u.integervalue);
+ break;
+ case booleanvalue:
+ if(val->u.booleanvalue)
+ fprintf(codefile, "%s = TRUE;\n", var);
+ else
+ fprintf(codefile, "%s = FALSE;\n", var);
+ break;
+ default:
+ abort();
+ }
+}
+
+void
+gen_compare_defval(const char *var, struct value *val)
+{
+ switch(val->type) {
+ case stringvalue:
+ fprintf(codefile, "if(strcmp(%s, \"%s\") != 0)\n", var, val->u.stringvalue);
+ break;
+ case integervalue:
+ fprintf(codefile, "if(%s != %d)\n", var, val->u.integervalue);
+ break;
+ case booleanvalue:
+ if(val->u.booleanvalue)
+ fprintf(codefile, "if(!%s)\n", var);
+ else
+ fprintf(codefile, "if(%s)\n", var);
+ break;
+ default:
+ abort();
+ }
+}
+
+static void
+generate_header_of_codefile(const char *name)
+{
+ char *filename;
+
+ if (codefile != NULL)
+ abort();
+
+ asprintf (&filename, "%s_%s.x", STEM, name);
+ if (filename == NULL)
+ errx(1, "malloc");
+ codefile = fopen (filename, "w");
+ if (codefile == NULL)
+ err (1, "fopen %s", filename);
+ fprintf(logfile, "%s ", filename);
+ free(filename);
+ fprintf (codefile,
+ "/* Generated from %s */\n"
+ "/* Do not edit */\n\n"
+ "#include <stdio.h>\n"
+ "#include <stdlib.h>\n"
+ "#include <time.h>\n"
+ "#include <string.h>\n"
+ "#include <errno.h>\n"
+ "#include <limits.h>\n"
+ "#include <krb5-types.h>\n",
+ orig_filename);
+
+ fprintf (codefile,
+ "#include <%s.h>\n",
+ headerbase);
+ fprintf (codefile,
+ "#include <asn1_err.h>\n"
+ "#include <der.h>\n"
+ "#include <parse_units.h>\n\n");
+
+}
+
+static void
+close_codefile(void)
+{
+ if (codefile == NULL)
+ abort();
+
+ fclose(codefile);
+ codefile = NULL;
+}
+
+
+void
+generate_constant (const Symbol *s)
+{
+ switch(s->value->type) {
+ case booleanvalue:
+ break;
+ case integervalue:
+ fprintf (headerfile, "enum { %s = %d };\n\n",
+ s->gen_name, s->value->u.integervalue);
+ break;
+ case nullvalue:
+ break;
+ case stringvalue:
+ break;
+ case objectidentifiervalue: {
+ struct objid *o, **list;
+ unsigned int i, len;
+
+ generate_header_of_codefile(s->gen_name);
+
+ len = 0;
+ for (o = s->value->u.objectidentifiervalue; o != NULL; o = o->next)
+ len++;
+ if (len == 0) {
+ printf("s->gen_name: %s",s->gen_name);
+ fflush(stdout);
+ break;
+ }
+ list = emalloc(sizeof(*list) * len);
+
+ i = 0;
+ for (o = s->value->u.objectidentifiervalue; o != NULL; o = o->next)
+ list[i++] = o;
+
+ fprintf (headerfile, "/* OBJECT IDENTIFIER %s ::= { ", s->name);
+ for (i = len ; i > 0; i--) {
+ o = list[i - 1];
+ fprintf(headerfile, "%s(%d) ",
+ o->label ? o->label : "label-less", o->value);
+ }
+
+ fprintf (headerfile, "} */\n");
+ fprintf (headerfile, "const heim_oid *oid_%s(void);\n\n",
+ s->gen_name);
+
+ fprintf (codefile, "static unsigned oid_%s_variable_num[%d] = {",
+ s->gen_name, len);
+ for (i = len ; i > 0; i--) {
+ fprintf(codefile, "%d%s ", list[i - 1]->value, i > 1 ? "," : "");
+ }
+ fprintf(codefile, "};\n");
+
+ fprintf (codefile, "static const heim_oid oid_%s_variable = "
+ "{ %d, oid_%s_variable_num };\n\n",
+ s->gen_name, len, s->gen_name);
+
+ fprintf (codefile, "const heim_oid *oid_%s(void)\n"
+ "{\n"
+ "return &oid_%s_variable;\n"
+ "}\n\n",
+ s->gen_name, s->gen_name);
+
+ close_codefile();
+
+ break;
+ }
+ default:
+ abort();
+ }
+}
+
+static void
+space(int level)
+{
+ while(level-- > 0)
+ fprintf(headerfile, " ");
+}
+
+static const char *
+last_member_p(struct member *m)
+{
+ struct member *n = ASN1_TAILQ_NEXT(m, members);
+ if (n == NULL)
+ return "";
+ if (n->ellipsis && ASN1_TAILQ_NEXT(n, members) == NULL)
+ return "";
+ return ",";
+}
+
+static struct member *
+have_ellipsis(Type *t)
+{
+ struct member *m;
+ ASN1_TAILQ_FOREACH(m, t->members, members) {
+ if (m->ellipsis)
+ return m;
+ }
+ return NULL;
+}
+
+static void
+define_asn1 (int level, Type *t)
+{
+ switch (t->type) {
+ case TType:
+ fprintf (headerfile, "%s", t->symbol->name);
+ break;
+ case TInteger:
+ if(t->members == NULL) {
+ fprintf (headerfile, "INTEGER");
+ if (t->range)
+ fprintf (headerfile, " (%d..%d)",
+ t->range->min, t->range->max);
+ } else {
+ Member *m;
+ fprintf (headerfile, "INTEGER {\n");
+ ASN1_TAILQ_FOREACH(m, t->members, members) {
+ space (level + 1);
+ fprintf(headerfile, "%s(%d)%s\n", m->gen_name, m->val,
+ last_member_p(m));
+ }
+ space(level);
+ fprintf (headerfile, "}");
+ }
+ break;
+ case TBoolean:
+ fprintf (headerfile, "BOOLEAN");
+ break;
+ case TOctetString:
+ fprintf (headerfile, "OCTET STRING");
+ break;
+ case TEnumerated :
+ case TBitString: {
+ Member *m;
+
+ space(level);
+ if(t->type == TBitString)
+ fprintf (headerfile, "BIT STRING {\n");
+ else
+ fprintf (headerfile, "ENUMERATED {\n");
+ ASN1_TAILQ_FOREACH(m, t->members, members) {
+ space(level + 1);
+ fprintf (headerfile, "%s(%d)%s\n", m->name, m->val,
+ last_member_p(m));
+ }
+ space(level);
+ fprintf (headerfile, "}");
+ break;
+ }
+ case TChoice:
+ case TSet:
+ case TSequence: {
+ Member *m;
+ int max_width = 0;
+
+ if(t->type == TChoice)
+ fprintf(headerfile, "CHOICE {\n");
+ else if(t->type == TSet)
+ fprintf(headerfile, "SET {\n");
+ else
+ fprintf(headerfile, "SEQUENCE {\n");
+ ASN1_TAILQ_FOREACH(m, t->members, members) {
+ if(strlen(m->name) > max_width)
+ max_width = strlen(m->name);
+ }
+ max_width += 3;
+ if(max_width < 16) max_width = 16;
+ ASN1_TAILQ_FOREACH(m, t->members, members) {
+ int width = max_width;
+ space(level + 1);
+ if (m->ellipsis) {
+ fprintf (headerfile, "...");
+ } else {
+ width -= fprintf(headerfile, "%s", m->name);
+ fprintf(headerfile, "%*s", width, "");
+ define_asn1(level + 1, m->type);
+ if(m->optional)
+ fprintf(headerfile, " OPTIONAL");
+ }
+ if(last_member_p(m))
+ fprintf (headerfile, ",");
+ fprintf (headerfile, "\n");
+ }
+ space(level);
+ fprintf (headerfile, "}");
+ break;
+ }
+ case TSequenceOf:
+ fprintf (headerfile, "SEQUENCE OF ");
+ define_asn1 (0, t->subtype);
+ break;
+ case TSetOf:
+ fprintf (headerfile, "SET OF ");
+ define_asn1 (0, t->subtype);
+ break;
+ case TGeneralizedTime:
+ fprintf (headerfile, "GeneralizedTime");
+ break;
+ case TGeneralString:
+ fprintf (headerfile, "GeneralString");
+ break;
+ case TTag: {
+ const char *classnames[] = { "UNIVERSAL ", "APPLICATION ",
+ "" /* CONTEXT */, "PRIVATE " };
+ if(t->tag.tagclass != ASN1_C_UNIV)
+ fprintf (headerfile, "[%s%d] ",
+ classnames[t->tag.tagclass],
+ t->tag.tagvalue);
+ if(t->tag.tagenv == TE_IMPLICIT)
+ fprintf (headerfile, "IMPLICIT ");
+ define_asn1 (level, t->subtype);
+ break;
+ }
+ case TUTCTime:
+ fprintf (headerfile, "UTCTime");
+ break;
+ case TUTF8String:
+ space(level);
+ fprintf (headerfile, "UTF8String");
+ break;
+ case TPrintableString:
+ space(level);
+ fprintf (headerfile, "PrintableString");
+ break;
+ case TIA5String:
+ space(level);
+ fprintf (headerfile, "IA5String");
+ break;
+ case TBMPString:
+ space(level);
+ fprintf (headerfile, "BMPString");
+ break;
+ case TUniversalString:
+ space(level);
+ fprintf (headerfile, "UniversalString");
+ break;
+ case TVisibleString:
+ space(level);
+ fprintf (headerfile, "VisibleString");
+ break;
+ case TOID :
+ space(level);
+ fprintf(headerfile, "OBJECT IDENTIFIER");
+ break;
+ case TNull:
+ space(level);
+ fprintf (headerfile, "NULL");
+ break;
+ default:
+ abort ();
+ }
+}
+
+static void
+define_type (int level, const char *name, Type *t, int typedefp, int preservep)
+{
+ switch (t->type) {
+ case TType:
+ space(level);
+ fprintf (headerfile, "%s %s;\n", t->symbol->gen_name, name);
+ break;
+ case TInteger:
+ space(level);
+ if(t->members) {
+ Member *m;
+ fprintf (headerfile, "enum %s {\n", typedefp ? name : "");
+ ASN1_TAILQ_FOREACH(m, t->members, members) {
+ space (level + 1);
+ fprintf(headerfile, "%s = %d%s\n", m->gen_name, m->val,
+ last_member_p(m));
+ }
+ fprintf (headerfile, "} %s;\n", name);
+ } else if (t->range == NULL) {
+ fprintf (headerfile, "heim_integer %s;\n", name);
+ } else if (t->range->min == INT_MIN && t->range->max == INT_MAX) {
+ fprintf (headerfile, "int %s;\n", name);
+ } else if (t->range->min == 0 && t->range->max == UINT_MAX) {
+ fprintf (headerfile, "unsigned int %s;\n", name);
+ } else if (t->range->min == 0 && t->range->max == INT_MAX) {
+ fprintf (headerfile, "unsigned int %s;\n", name);
+ } else
+ errx(1, "%s: unsupported range %d -> %d",
+ name, t->range->min, t->range->max);
+ break;
+ case TBoolean:
+ space(level);
+ fprintf (headerfile, "int %s;\n", name);
+ break;
+ case TOctetString:
+ space(level);
+ fprintf (headerfile, "heim_octet_string %s;\n", name);
+ break;
+ case TBitString: {
+ Member *m;
+ Type i;
+ struct range range = { 0, INT_MAX };
+
+ i.type = TInteger;
+ i.range = &range;
+ i.members = NULL;
+ i.constraint = NULL;
+
+ space(level);
+ if(ASN1_TAILQ_EMPTY(t->members))
+ fprintf (headerfile, "heim_bit_string %s;\n", name);
+ else {
+ fprintf (headerfile, "struct %s {\n", typedefp ? name : "");
+ ASN1_TAILQ_FOREACH(m, t->members, members) {
+ char *n;
+
+ asprintf (&n, "%s:1", m->gen_name);
+ if (n == NULL)
+ errx(1, "malloc");
+ define_type (level + 1, n, &i, FALSE, FALSE);
+ free (n);
+ }
+ space(level);
+ fprintf (headerfile, "} %s;\n\n", name);
+ }
+ break;
+ }
+ case TEnumerated: {
+ Member *m;
+
+ space(level);
+ fprintf (headerfile, "enum %s {\n", typedefp ? name : "");
+ ASN1_TAILQ_FOREACH(m, t->members, members) {
+ space(level + 1);
+ if (m->ellipsis)
+ fprintf (headerfile, "/* ... */\n");
+ else
+ fprintf (headerfile, "%s = %d%s\n", m->gen_name, m->val,
+ last_member_p(m));
+ }
+ space(level);
+ fprintf (headerfile, "} %s;\n\n", name);
+ break;
+ }
+ case TSet:
+ case TSequence: {
+ Member *m;
+
+ space(level);
+ fprintf (headerfile, "struct %s {\n", typedefp ? name : "");
+ if (t->type == TSequence && preservep) {
+ space(level + 1);
+ fprintf(headerfile, "heim_octet_string _save;\n");
+ }
+ ASN1_TAILQ_FOREACH(m, t->members, members) {
+ if (m->ellipsis) {
+ ;
+ } else if (m->optional) {
+ char *n;
+
+ asprintf (&n, "*%s", m->gen_name);
+ if (n == NULL)
+ errx(1, "malloc");
+ define_type (level + 1, n, m->type, FALSE, FALSE);
+ free (n);
+ } else
+ define_type (level + 1, m->gen_name, m->type, FALSE, FALSE);
+ }
+ space(level);
+ fprintf (headerfile, "} %s;\n", name);
+ break;
+ }
+ case TSetOf:
+ case TSequenceOf: {
+ Type i;
+ struct range range = { 0, INT_MAX };
+
+ i.type = TInteger;
+ i.range = &range;
+ i.members = NULL;
+ i.constraint = NULL;
+
+ space(level);
+ fprintf (headerfile, "struct %s {\n", typedefp ? name : "");
+ define_type (level + 1, "len", &i, FALSE, FALSE);
+ define_type (level + 1, "*val", t->subtype, FALSE, FALSE);
+ space(level);
+ fprintf (headerfile, "} %s;\n", name);
+ break;
+ }
+ case TGeneralizedTime:
+ space(level);
+ fprintf (headerfile, "time_t %s;\n", name);
+ break;
+ case TGeneralString:
+ space(level);
+ fprintf (headerfile, "heim_general_string %s;\n", name);
+ break;
+ case TTag:
+ define_type (level, name, t->subtype, typedefp, preservep);
+ break;
+ case TChoice: {
+ int first = 1;
+ Member *m;
+
+ space(level);
+ fprintf (headerfile, "struct %s {\n", typedefp ? name : "");
+ if (preservep) {
+ space(level + 1);
+ fprintf(headerfile, "heim_octet_string _save;\n");
+ }
+ space(level + 1);
+ fprintf (headerfile, "enum {\n");
+ m = have_ellipsis(t);
+ if (m) {
+ space(level + 2);
+ fprintf (headerfile, "%s = 0,\n", m->label);
+ first = 0;
+ }
+ ASN1_TAILQ_FOREACH(m, t->members, members) {
+ space(level + 2);
+ if (m->ellipsis)
+ fprintf (headerfile, "/* ... */\n");
+ else
+ fprintf (headerfile, "%s%s%s\n", m->label,
+ first ? " = 1" : "",
+ last_member_p(m));
+ first = 0;
+ }
+ space(level + 1);
+ fprintf (headerfile, "} element;\n");
+ space(level + 1);
+ fprintf (headerfile, "union {\n");
+ ASN1_TAILQ_FOREACH(m, t->members, members) {
+ if (m->ellipsis) {
+ space(level + 2);
+ fprintf(headerfile, "heim_octet_string asn1_ellipsis;\n");
+ } else if (m->optional) {
+ char *n;
+
+ asprintf (&n, "*%s", m->gen_name);
+ if (n == NULL)
+ errx(1, "malloc");
+ define_type (level + 2, n, m->type, FALSE, FALSE);
+ free (n);
+ } else
+ define_type (level + 2, m->gen_name, m->type, FALSE, FALSE);
+ }
+ space(level + 1);
+ fprintf (headerfile, "} u;\n");
+ space(level);
+ fprintf (headerfile, "} %s;\n", name);
+ break;
+ }
+ case TUTCTime:
+ space(level);
+ fprintf (headerfile, "time_t %s;\n", name);
+ break;
+ case TUTF8String:
+ space(level);
+ fprintf (headerfile, "heim_utf8_string %s;\n", name);
+ break;
+ case TPrintableString:
+ space(level);
+ fprintf (headerfile, "heim_printable_string %s;\n", name);
+ break;
+ case TIA5String:
+ space(level);
+ fprintf (headerfile, "heim_ia5_string %s;\n", name);
+ break;
+ case TBMPString:
+ space(level);
+ fprintf (headerfile, "heim_bmp_string %s;\n", name);
+ break;
+ case TUniversalString:
+ space(level);
+ fprintf (headerfile, "heim_universal_string %s;\n", name);
+ break;
+ case TVisibleString:
+ space(level);
+ fprintf (headerfile, "heim_visible_string %s;\n", name);
+ break;
+ case TOID :
+ space(level);
+ fprintf (headerfile, "heim_oid %s;\n", name);
+ break;
+ case TNull:
+ space(level);
+ fprintf (headerfile, "int %s;\n", name);
+ break;
+ default:
+ abort ();
+ }
+}
+
+static void
+generate_type_header (const Symbol *s)
+{
+ int preservep = preserve_type(s->name) ? TRUE : FALSE;
+
+ fprintf (headerfile, "/*\n");
+ fprintf (headerfile, "%s ::= ", s->name);
+ define_asn1 (0, s->type);
+ fprintf (headerfile, "\n*/\n\n");
+
+ fprintf (headerfile, "typedef ");
+ define_type (0, s->gen_name, s->type, TRUE, preservep);
+
+ fprintf (headerfile, "\n");
+}
+
+
+void
+generate_type (const Symbol *s)
+{
+ generate_header_of_codefile(s->gen_name);
+
+ generate_type_header (s);
+ generate_type_encode (s);
+ generate_type_decode (s);
+ generate_type_free (s);
+ generate_type_length (s);
+ generate_type_copy (s);
+ generate_type_seq (s);
+ generate_glue (s->type, s->gen_name);
+ fprintf(headerfile, "\n\n");
+ close_codefile();
+}
diff --git a/source4/heimdal/lib/asn1/gen_copy.c b/source4/heimdal/lib/asn1/gen_copy.c
new file mode 100644
index 0000000000..37c9304779
--- /dev/null
+++ b/source4/heimdal/lib/asn1/gen_copy.c
@@ -0,0 +1,249 @@
+/*
+ * Copyright (c) 1997 - 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "gen_locl.h"
+
+RCSID("$Id$");
+
+static int used_fail;
+
+static void
+copy_primitive (const char *typename, const char *from, const char *to)
+{
+ fprintf (codefile, "if(der_copy_%s(%s, %s)) goto fail;\n",
+ typename, from, to);
+ used_fail++;
+}
+
+static void
+copy_type (const char *from, const char *to, const Type *t, int preserve)
+{
+ switch (t->type) {
+ case TType:
+#if 0
+ copy_type (from, to, t->symbol->type, preserve);
+#endif
+ fprintf (codefile, "if(copy_%s(%s, %s)) goto fail;\n",
+ t->symbol->gen_name, from, to);
+ used_fail++;
+ break;
+ case TInteger:
+ if (t->range == NULL && t->members == NULL) {
+ copy_primitive ("heim_integer", from, to);
+ break;
+ }
+ case TBoolean:
+ case TEnumerated :
+ fprintf(codefile, "*(%s) = *(%s);\n", to, from);
+ break;
+ case TOctetString:
+ copy_primitive ("octet_string", from, to);
+ break;
+ case TBitString:
+ if (ASN1_TAILQ_EMPTY(t->members))
+ copy_primitive ("bit_string", from, to);
+ else
+ fprintf(codefile, "*(%s) = *(%s);\n", to, from);
+ break;
+ case TSet:
+ case TSequence:
+ case TChoice: {
+ Member *m, *have_ellipsis = NULL;
+
+ if(t->members == NULL)
+ break;
+
+ if ((t->type == TSequence || t->type == TChoice) && preserve) {
+ fprintf(codefile,
+ "{ int ret;\n"
+ "ret = der_copy_octet_string(&(%s)->_save, &(%s)->_save);\n"
+ "if (ret) goto fail;\n"
+ "}\n",
+ from, to);
+ used_fail++;
+ }
+
+ if(t->type == TChoice) {
+ fprintf(codefile, "(%s)->element = (%s)->element;\n", to, from);
+ fprintf(codefile, "switch((%s)->element) {\n", from);
+ }
+
+ ASN1_TAILQ_FOREACH(m, t->members, members) {
+ char *fs;
+ char *ts;
+
+ if (m->ellipsis) {
+ have_ellipsis = m;
+ continue;
+ }
+
+ if(t->type == TChoice)
+ fprintf(codefile, "case %s:\n", m->label);
+
+ asprintf (&fs, "%s(%s)->%s%s",
+ m->optional ? "" : "&", from,
+ t->type == TChoice ? "u." : "", m->gen_name);
+ if (fs == NULL)
+ errx(1, "malloc");
+ asprintf (&ts, "%s(%s)->%s%s",
+ m->optional ? "" : "&", to,
+ t->type == TChoice ? "u." : "", m->gen_name);
+ if (ts == NULL)
+ errx(1, "malloc");
+ if(m->optional){
+ fprintf(codefile, "if(%s) {\n", fs);
+ fprintf(codefile, "%s = malloc(sizeof(*%s));\n", ts, ts);
+ fprintf(codefile, "if(%s == NULL) goto fail;\n", ts);
+ used_fail++;
+ }
+ copy_type (fs, ts, m->type, FALSE);
+ if(m->optional){
+ fprintf(codefile, "}else\n");
+ fprintf(codefile, "%s = NULL;\n", ts);
+ }
+ free (fs);
+ free (ts);
+ if(t->type == TChoice)
+ fprintf(codefile, "break;\n");
+ }
+ if(t->type == TChoice) {
+ if (have_ellipsis) {
+ fprintf(codefile, "case %s: {\n"
+ "int ret;\n"
+ "ret=der_copy_octet_string(&(%s)->u.%s, &(%s)->u.%s);\n"
+ "if (ret) goto fail;\n"
+ "break;\n"
+ "}\n",
+ have_ellipsis->label,
+ from, have_ellipsis->gen_name,
+ to, have_ellipsis->gen_name);
+ used_fail++;
+ }
+ fprintf(codefile, "}\n");
+ }
+ break;
+ }
+ case TSetOf:
+ case TSequenceOf: {
+ char *f;
+ char *T;
+
+ fprintf (codefile, "if(((%s)->val = "
+ "malloc((%s)->len * sizeof(*(%s)->val))) == NULL && (%s)->len != 0)\n",
+ to, from, to, from);
+ fprintf (codefile, "goto fail;\n");
+ used_fail++;
+ fprintf(codefile,
+ "for((%s)->len = 0; (%s)->len < (%s)->len; (%s)->len++){\n",
+ to, to, from, to);
+ asprintf(&f, "&(%s)->val[(%s)->len]", from, to);
+ if (f == NULL)
+ errx(1, "malloc");
+ asprintf(&T, "&(%s)->val[(%s)->len]", to, to);
+ if (T == NULL)
+ errx(1, "malloc");
+ copy_type(f, T, t->subtype, FALSE);
+ fprintf(codefile, "}\n");
+ free(f);
+ free(T);
+ break;
+ }
+ case TGeneralizedTime:
+ fprintf(codefile, "*(%s) = *(%s);\n", to, from);
+ break;
+ case TGeneralString:
+ copy_primitive ("general_string", from, to);
+ break;
+ case TUTCTime:
+ fprintf(codefile, "*(%s) = *(%s);\n", to, from);
+ break;
+ case TUTF8String:
+ copy_primitive ("utf8string", from, to);
+ break;
+ case TPrintableString:
+ copy_primitive ("printable_string", from, to);
+ break;
+ case TIA5String:
+ copy_primitive ("ia5_string", from, to);
+ break;
+ case TBMPString:
+ copy_primitive ("bmp_string", from, to);
+ break;
+ case TUniversalString:
+ copy_primitive ("universal_string", from, to);
+ break;
+ case TVisibleString:
+ copy_primitive ("visible_string", from, to);
+ break;
+ case TTag:
+ copy_type (from, to, t->subtype, preserve);
+ break;
+ case TOID:
+ copy_primitive ("oid", from, to);
+ break;
+ case TNull:
+ break;
+ default :
+ abort ();
+ }
+}
+
+void
+generate_type_copy (const Symbol *s)
+{
+ int preserve = preserve_type(s->name) ? TRUE : FALSE;
+
+ used_fail = 0;
+
+ fprintf (headerfile,
+ "int copy_%s (const %s *, %s *);\n",
+ s->gen_name, s->gen_name, s->gen_name);
+
+ fprintf (codefile, "int\n"
+ "copy_%s(const %s *from, %s *to)\n"
+ "{\n"
+ "memset(to, 0, sizeof(*to));\n",
+ s->gen_name, s->gen_name, s->gen_name);
+ copy_type ("from", "to", s->type, preserve);
+ fprintf (codefile, "return 0;\n");
+
+ if (used_fail)
+ fprintf (codefile, "fail:\n"
+ "free_%s(to);\n"
+ "return ENOMEM;\n",
+ s->gen_name);
+
+ fprintf(codefile,
+ "}\n\n");
+}
+
diff --git a/source4/heimdal/lib/asn1/gen_decode.c b/source4/heimdal/lib/asn1/gen_decode.c
new file mode 100644
index 0000000000..2bd5acb47e
--- /dev/null
+++ b/source4/heimdal/lib/asn1/gen_decode.c
@@ -0,0 +1,720 @@
+/*
+ * Copyright (c) 1997 - 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "gen_locl.h"
+#include "lex.h"
+
+RCSID("$Id$");
+
+static void
+decode_primitive (const char *typename, const char *name, const char *forwstr)
+{
+#if 0
+ fprintf (codefile,
+ "e = decode_%s(p, len, %s, &l);\n"
+ "%s;\n",
+ typename,
+ name,
+ forwstr);
+#else
+ fprintf (codefile,
+ "e = der_get_%s(p, len, %s, &l);\n"
+ "if(e) %s;\np += l; len -= l; ret += l;\n",
+ typename,
+ name,
+ forwstr);
+#endif
+}
+
+static int
+is_primitive_type(int type)
+{
+ switch(type) {
+ case TInteger:
+ case TBoolean:
+ case TOctetString:
+ case TBitString:
+ case TEnumerated:
+ case TGeneralizedTime:
+ case TGeneralString:
+ case TOID:
+ case TUTCTime:
+ case TUTF8String:
+ case TPrintableString:
+ case TIA5String:
+ case TBMPString:
+ case TUniversalString:
+ case TVisibleString:
+ case TNull:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static void
+find_tag (const Type *t,
+ Der_class *cl, Der_type *ty, unsigned *tag)
+{
+ switch (t->type) {
+ case TBitString:
+ *cl = ASN1_C_UNIV;
+ *ty = PRIM;
+ *tag = UT_BitString;
+ break;
+ case TBoolean:
+ *cl = ASN1_C_UNIV;
+ *ty = PRIM;
+ *tag = UT_Boolean;
+ break;
+ case TChoice:
+ errx(1, "Cannot have recursive CHOICE");
+ case TEnumerated:
+ *cl = ASN1_C_UNIV;
+ *ty = PRIM;
+ *tag = UT_Enumerated;
+ break;
+ case TGeneralString:
+ *cl = ASN1_C_UNIV;
+ *ty = PRIM;
+ *tag = UT_GeneralString;
+ break;
+ case TGeneralizedTime:
+ *cl = ASN1_C_UNIV;
+ *ty = PRIM;
+ *tag = UT_GeneralizedTime;
+ break;
+ case TIA5String:
+ *cl = ASN1_C_UNIV;
+ *ty = PRIM;
+ *tag = UT_IA5String;
+ break;
+ case TInteger:
+ *cl = ASN1_C_UNIV;
+ *ty = PRIM;
+ *tag = UT_Integer;
+ break;
+ case TNull:
+ *cl = ASN1_C_UNIV;
+ *ty = PRIM;
+ *tag = UT_Null;
+ break;
+ case TOID:
+ *cl = ASN1_C_UNIV;
+ *ty = PRIM;
+ *tag = UT_OID;
+ break;
+ case TOctetString:
+ *cl = ASN1_C_UNIV;
+ *ty = PRIM;
+ *tag = UT_OctetString;
+ break;
+ case TPrintableString:
+ *cl = ASN1_C_UNIV;
+ *ty = PRIM;
+ *tag = UT_PrintableString;
+ break;
+ case TSequence:
+ case TSequenceOf:
+ *cl = ASN1_C_UNIV;
+ *ty = CONS;
+ *tag = UT_Sequence;
+ break;
+ case TSet:
+ case TSetOf:
+ *cl = ASN1_C_UNIV;
+ *ty = CONS;
+ *tag = UT_Set;
+ break;
+ case TTag:
+ *cl = t->tag.tagclass;
+ *ty = is_primitive_type(t->subtype->type) ? PRIM : CONS;
+ *tag = t->tag.tagvalue;
+ break;
+ case TType:
+ if ((t->symbol->stype == Stype && t->symbol->type == NULL)
+ || t->symbol->stype == SUndefined) {
+ error_message("%s is imported or still undefined, "
+ " can't generate tag checking data in CHOICE "
+ "without this information",
+ t->symbol->name);
+ exit(1);
+ }
+ find_tag(t->symbol->type, cl, ty, tag);
+ return;
+ case TUTCTime:
+ *cl = ASN1_C_UNIV;
+ *ty = PRIM;
+ *tag = UT_UTCTime;
+ break;
+ case TUTF8String:
+ *cl = ASN1_C_UNIV;
+ *ty = PRIM;
+ *tag = UT_UTF8String;
+ break;
+ case TBMPString:
+ *cl = ASN1_C_UNIV;
+ *ty = PRIM;
+ *tag = UT_BMPString;
+ break;
+ case TUniversalString:
+ *cl = ASN1_C_UNIV;
+ *ty = PRIM;
+ *tag = UT_UniversalString;
+ break;
+ case TVisibleString:
+ *cl = ASN1_C_UNIV;
+ *ty = PRIM;
+ *tag = UT_VisibleString;
+ break;
+ default:
+ abort();
+ }
+}
+
+static void
+range_check(const char *name,
+ const char *length,
+ const char *forwstr,
+ struct range *r)
+{
+ if (r->min == r->max + 2 || r->min < r->max)
+ fprintf (codefile,
+ "if ((%s)->%s > %d) {\n"
+ "e = ASN1_MAX_CONSTRAINT; %s;\n"
+ "}\n",
+ name, length, r->max, forwstr);
+ if (r->min - 1 == r->max || r->min < r->max)
+ fprintf (codefile,
+ "if ((%s)->%s < %d) {\n"
+ "e = ASN1_MIN_CONSTRAINT; %s;\n"
+ "}\n",
+ name, length, r->min, forwstr);
+ if (r->max == r->min)
+ fprintf (codefile,
+ "if ((%s)->%s != %d) {\n"
+ "e = ASN1_EXACT_CONSTRAINT; %s;\n"
+ "}\n",
+ name, length, r->min, forwstr);
+}
+
+static int
+decode_type (const char *name, const Type *t, int optional,
+ const char *forwstr, const char *tmpstr)
+{
+ switch (t->type) {
+ case TType: {
+ if (optional)
+ fprintf(codefile,
+ "%s = calloc(1, sizeof(*%s));\n"
+ "if (%s == NULL) %s;\n",
+ name, name, name, forwstr);
+ fprintf (codefile,
+ "e = decode_%s(p, len, %s, &l);\n",
+ t->symbol->gen_name, name);
+ if (optional) {
+ fprintf (codefile,
+ "if(e) {\n"
+ "free(%s);\n"
+ "%s = NULL;\n"
+ "} else {\n"
+ "p += l; len -= l; ret += l;\n"
+ "}\n",
+ name, name);
+ } else {
+ fprintf (codefile,
+ "if(e) %s;\n",
+ forwstr);
+ fprintf (codefile,
+ "p += l; len -= l; ret += l;\n");
+ }
+ break;
+ }
+ case TInteger:
+ if(t->members) {
+ fprintf(codefile,
+ "{\n"
+ "int enumint;\n");
+ decode_primitive ("integer", "&enumint", forwstr);
+ fprintf(codefile,
+ "*%s = enumint;\n"
+ "}\n",
+ name);
+ } else if (t->range == NULL) {
+ decode_primitive ("heim_integer", name, forwstr);
+ } else if (t->range->min == INT_MIN && t->range->max == INT_MAX) {
+ decode_primitive ("integer", name, forwstr);
+ } else if (t->range->min == 0 && t->range->max == UINT_MAX) {
+ decode_primitive ("unsigned", name, forwstr);
+ } else if (t->range->min == 0 && t->range->max == INT_MAX) {
+ decode_primitive ("unsigned", name, forwstr);
+ } else
+ errx(1, "%s: unsupported range %d -> %d",
+ name, t->range->min, t->range->max);
+ break;
+ case TBoolean:
+ decode_primitive ("boolean", name, forwstr);
+ break;
+ case TEnumerated:
+ decode_primitive ("enumerated", name, forwstr);
+ break;
+ case TOctetString:
+ decode_primitive ("octet_string", name, forwstr);
+ if (t->range)
+ range_check(name, "length", forwstr, t->range);
+ break;
+ case TBitString: {
+ Member *m;
+ int pos = 0;
+
+ if (ASN1_TAILQ_EMPTY(t->members)) {
+ decode_primitive ("bit_string", name, forwstr);
+ break;
+ }
+ fprintf(codefile,
+ "if (len < 1) return ASN1_OVERRUN;\n"
+ "p++; len--; ret++;\n");
+ fprintf(codefile,
+ "do {\n"
+ "if (len < 1) break;\n");
+ ASN1_TAILQ_FOREACH(m, t->members, members) {
+ while (m->val / 8 > pos / 8) {
+ fprintf (codefile,
+ "p++; len--; ret++;\n"
+ "if (len < 1) break;\n");
+ pos += 8;
+ }
+ fprintf (codefile,
+ "(%s)->%s = (*p >> %d) & 1;\n",
+ name, m->gen_name, 7 - m->val % 8);
+ }
+ fprintf(codefile,
+ "} while(0);\n");
+ fprintf (codefile,
+ "p += len; ret += len;\n");
+ break;
+ }
+ case TSequence: {
+ Member *m;
+
+ if (t->members == NULL)
+ break;
+
+ ASN1_TAILQ_FOREACH(m, t->members, members) {
+ char *s;
+
+ if (m->ellipsis)
+ continue;
+
+ asprintf (&s, "%s(%s)->%s", m->optional ? "" : "&",
+ name, m->gen_name);
+ if (s == NULL)
+ errx(1, "malloc");
+ decode_type (s, m->type, m->optional, forwstr, m->gen_name);
+ free (s);
+ }
+
+ break;
+ }
+ case TSet: {
+ Member *m;
+ unsigned int memno;
+
+ if(t->members == NULL)
+ break;
+
+ fprintf(codefile, "{\n");
+ fprintf(codefile, "unsigned int members = 0;\n");
+ fprintf(codefile, "while(len > 0) {\n");
+ fprintf(codefile,
+ "Der_class class;\n"
+ "Der_type type;\n"
+ "int tag;\n"
+ "e = der_get_tag (p, len, &class, &type, &tag, NULL);\n"
+ "if(e) %s;\n", forwstr);
+ fprintf(codefile, "switch (MAKE_TAG(class, type, tag)) {\n");
+ memno = 0;
+ ASN1_TAILQ_FOREACH(m, t->members, members) {
+ char *s;
+
+ assert(m->type->type == TTag);
+
+ fprintf(codefile, "case MAKE_TAG(%s, %s, %s):\n",
+ classname(m->type->tag.tagclass),
+ is_primitive_type(m->type->subtype->type) ? "PRIM" : "CONS",
+ valuename(m->type->tag.tagclass, m->type->tag.tagvalue));
+
+ asprintf (&s, "%s(%s)->%s", m->optional ? "" : "&", name, m->gen_name);
+ if (s == NULL)
+ errx(1, "malloc");
+ if(m->optional)
+ fprintf(codefile,
+ "%s = calloc(1, sizeof(*%s));\n"
+ "if (%s == NULL) { e = ENOMEM; %s; }\n",
+ s, s, s, forwstr);
+ decode_type (s, m->type, 0, forwstr, m->gen_name);
+ free (s);
+
+ fprintf(codefile, "members |= (1 << %d);\n", memno);
+ memno++;
+ fprintf(codefile, "break;\n");
+ }
+ fprintf(codefile,
+ "default:\n"
+ "return ASN1_MISPLACED_FIELD;\n"
+ "break;\n");
+ fprintf(codefile, "}\n");
+ fprintf(codefile, "}\n");
+ memno = 0;
+ ASN1_TAILQ_FOREACH(m, t->members, members) {
+ char *s;
+
+ asprintf (&s, "%s->%s", name, m->gen_name);
+ if (s == NULL)
+ errx(1, "malloc");
+ fprintf(codefile, "if((members & (1 << %d)) == 0)\n", memno);
+ if(m->optional)
+ fprintf(codefile, "%s = NULL;\n", s);
+ else if(m->defval)
+ gen_assign_defval(s, m->defval);
+ else
+ fprintf(codefile, "return ASN1_MISSING_FIELD;\n");
+ free(s);
+ memno++;
+ }
+ fprintf(codefile, "}\n");
+ break;
+ }
+ case TSetOf:
+ case TSequenceOf: {
+ char *n;
+ char *sname;
+
+ fprintf (codefile,
+ "{\n"
+ "size_t %s_origlen = len;\n"
+ "size_t %s_oldret = ret;\n"
+ "size_t %s_olen = 0;\n"
+ "void *%s_tmp;\n"
+ "ret = 0;\n"
+ "(%s)->len = 0;\n"
+ "(%s)->val = NULL;\n",
+ tmpstr,
+ tmpstr,
+ tmpstr,
+ tmpstr,
+ name,
+ name);
+
+ fprintf (codefile,
+ "while(ret < %s_origlen) {\n"
+ "size_t %s_nlen = %s_olen + sizeof(*((%s)->val));\n"
+ "if (%s_olen > %s_nlen) { e = ASN1_OVERFLOW; %s; }\n"
+ "%s_olen = %s_nlen;\n"
+ "%s_tmp = realloc((%s)->val, %s_olen);\n"
+ "if (%s_tmp == NULL) { e = ENOMEM; %s; }\n"
+ "(%s)->val = %s_tmp;\n",
+ tmpstr,
+ tmpstr, tmpstr, name,
+ tmpstr, tmpstr, forwstr,
+ tmpstr, tmpstr,
+ tmpstr, name, tmpstr,
+ tmpstr, forwstr,
+ name, tmpstr);
+
+ asprintf (&n, "&(%s)->val[(%s)->len]", name, name);
+ if (n == NULL)
+ errx(1, "malloc");
+ asprintf (&sname, "%s_s_of", tmpstr);
+ if (sname == NULL)
+ errx(1, "malloc");
+ decode_type (n, t->subtype, 0, forwstr, sname);
+ fprintf (codefile,
+ "(%s)->len++;\n"
+ "len = %s_origlen - ret;\n"
+ "}\n"
+ "ret += %s_oldret;\n"
+ "}\n",
+ name,
+ tmpstr, tmpstr);
+ if (t->range)
+ range_check(name, "len", forwstr, t->range);
+ free (n);
+ free (sname);
+ break;
+ }
+ case TGeneralizedTime:
+ decode_primitive ("generalized_time", name, forwstr);
+ break;
+ case TGeneralString:
+ decode_primitive ("general_string", name, forwstr);
+ break;
+ case TTag:{
+ char *tname;
+
+ fprintf(codefile,
+ "{\n"
+ "size_t %s_datalen, %s_oldlen;\n",
+ tmpstr, tmpstr);
+ if(dce_fix)
+ fprintf(codefile,
+ "int dce_fix;\n");
+ fprintf(codefile, "e = der_match_tag_and_length(p, len, %s, %s, %s, "
+ "&%s_datalen, &l);\n",
+ classname(t->tag.tagclass),
+ is_primitive_type(t->subtype->type) ? "PRIM" : "CONS",
+ valuename(t->tag.tagclass, t->tag.tagvalue),
+ tmpstr);
+ if(optional) {
+ fprintf(codefile,
+ "if(e) {\n"
+ "%s = NULL;\n"
+ "} else {\n"
+ "%s = calloc(1, sizeof(*%s));\n"
+ "if (%s == NULL) { e = ENOMEM; %s; }\n",
+ name, name, name, name, forwstr);
+ } else {
+ fprintf(codefile, "if(e) %s;\n", forwstr);
+ }
+ fprintf (codefile,
+ "p += l; len -= l; ret += l;\n"
+ "%s_oldlen = len;\n",
+ tmpstr);
+ if(dce_fix)
+ fprintf (codefile,
+ "if((dce_fix = _heim_fix_dce(%s_datalen, &len)) < 0)\n"
+ "{ e = ASN1_BAD_FORMAT; %s; }\n",
+ tmpstr, forwstr);
+ else
+ fprintf(codefile,
+ "if (%s_datalen > len) { e = ASN1_OVERRUN; %s; }\n"
+ "len = %s_datalen;\n", tmpstr, forwstr, tmpstr);
+ asprintf (&tname, "%s_Tag", tmpstr);
+ if (tname == NULL)
+ errx(1, "malloc");
+ decode_type (name, t->subtype, 0, forwstr, tname);
+ if(dce_fix)
+ fprintf(codefile,
+ "if(dce_fix){\n"
+ "e = der_match_tag_and_length (p, len, "
+ "(Der_class)0,(Der_type)0, UT_EndOfContent, "
+ "&%s_datalen, &l);\n"
+ "if(e) %s;\np += l; len -= l; ret += l;\n"
+ "} else \n", tmpstr, forwstr);
+ fprintf(codefile,
+ "len = %s_oldlen - %s_datalen;\n",
+ tmpstr, tmpstr);
+ if(optional)
+ fprintf(codefile,
+ "}\n");
+ fprintf(codefile,
+ "}\n");
+ free(tname);
+ break;
+ }
+ case TChoice: {
+ Member *m, *have_ellipsis = NULL;
+ const char *els = "";
+
+ if (t->members == NULL)
+ break;
+
+ ASN1_TAILQ_FOREACH(m, t->members, members) {
+ const Type *tt = m->type;
+ char *s;
+ Der_class cl;
+ Der_type ty;
+ unsigned tag;
+
+ if (m->ellipsis) {
+ have_ellipsis = m;
+ continue;
+ }
+
+ find_tag(tt, &cl, &ty, &tag);
+
+ fprintf(codefile,
+ "%sif (der_match_tag(p, len, %s, %s, %s, NULL) == 0) {\n",
+ els,
+ classname(cl),
+ ty ? "CONS" : "PRIM",
+ valuename(cl, tag));
+ asprintf (&s, "%s(%s)->u.%s", m->optional ? "" : "&",
+ name, m->gen_name);
+ if (s == NULL)
+ errx(1, "malloc");
+ decode_type (s, m->type, m->optional, forwstr, m->gen_name);
+ fprintf(codefile,
+ "(%s)->element = %s;\n",
+ name, m->label);
+ free(s);
+ fprintf(codefile,
+ "}\n");
+ els = "else ";
+ }
+ if (have_ellipsis) {
+ fprintf(codefile,
+ "else {\n"
+ "(%s)->u.%s.data = calloc(1, len);\n"
+ "if ((%s)->u.%s.data == NULL) {\n"
+ "e = ENOMEM; %s;\n"
+ "}\n"
+ "(%s)->u.%s.length = len;\n"
+ "memcpy((%s)->u.%s.data, p, len);\n"
+ "(%s)->element = %s;\n"
+ "p += len;\n"
+ "ret += len;\n"
+ "len -= len;\n"
+ "}\n",
+ name, have_ellipsis->gen_name,
+ name, have_ellipsis->gen_name,
+ forwstr,
+ name, have_ellipsis->gen_name,
+ name, have_ellipsis->gen_name,
+ name, have_ellipsis->label);
+ } else {
+ fprintf(codefile,
+ "else {\n"
+ "e = ASN1_PARSE_ERROR;\n"
+ "%s;\n"
+ "}\n",
+ forwstr);
+ }
+ break;
+ }
+ case TUTCTime:
+ decode_primitive ("utctime", name, forwstr);
+ break;
+ case TUTF8String:
+ decode_primitive ("utf8string", name, forwstr);
+ break;
+ case TPrintableString:
+ decode_primitive ("printable_string", name, forwstr);
+ break;
+ case TIA5String:
+ decode_primitive ("ia5_string", name, forwstr);
+ break;
+ case TBMPString:
+ decode_primitive ("bmp_string", name, forwstr);
+ break;
+ case TUniversalString:
+ decode_primitive ("universal_string", name, forwstr);
+ break;
+ case TVisibleString:
+ decode_primitive ("visible_string", name, forwstr);
+ break;
+ case TNull:
+ fprintf (codefile, "/* NULL */\n");
+ break;
+ case TOID:
+ decode_primitive ("oid", name, forwstr);
+ break;
+ default :
+ abort ();
+ }
+ return 0;
+}
+
+void
+generate_type_decode (const Symbol *s)
+{
+ int preserve = preserve_type(s->name) ? TRUE : FALSE;
+
+ fprintf (headerfile,
+ "int "
+ "decode_%s(const unsigned char *, size_t, %s *, size_t *);\n",
+ s->gen_name, s->gen_name);
+
+ fprintf (codefile, "int\n"
+ "decode_%s(const unsigned char *p,"
+ " size_t len, %s *data, size_t *size)\n"
+ "{\n",
+ s->gen_name, s->gen_name);
+
+ switch (s->type->type) {
+ case TInteger:
+ case TBoolean:
+ case TOctetString:
+ case TOID:
+ case TGeneralizedTime:
+ case TGeneralString:
+ case TUTF8String:
+ case TPrintableString:
+ case TIA5String:
+ case TBMPString:
+ case TUniversalString:
+ case TVisibleString:
+ case TUTCTime:
+ case TNull:
+ case TEnumerated:
+ case TBitString:
+ case TSequence:
+ case TSequenceOf:
+ case TSet:
+ case TSetOf:
+ case TTag:
+ case TType:
+ case TChoice:
+ fprintf (codefile,
+ "size_t ret = 0;\n"
+ "size_t l;\n"
+ "int e;\n");
+ if (preserve)
+ fprintf (codefile, "const unsigned char *begin = p;\n");
+
+ fprintf (codefile, "\n");
+ fprintf (codefile, "memset(data, 0, sizeof(*data));\n"); /* hack to avoid `unused variable' */
+
+ decode_type ("data", s->type, 0, "goto fail", "Top");
+ if (preserve)
+ fprintf (codefile,
+ "data->_save.data = calloc(1, ret);\n"
+ "if (data->_save.data == NULL) { \n"
+ "e = ENOMEM; goto fail; \n"
+ "}\n"
+ "data->_save.length = ret;\n"
+ "memcpy(data->_save.data, begin, ret);\n");
+ fprintf (codefile,
+ "if(size) *size = ret;\n"
+ "return 0;\n");
+ fprintf (codefile,
+ "fail:\n"
+ "free_%s(data);\n"
+ "return e;\n",
+ s->gen_name);
+ break;
+ default:
+ abort ();
+ }
+ fprintf (codefile, "}\n\n");
+}
diff --git a/source4/heimdal/lib/asn1/gen_encode.c b/source4/heimdal/lib/asn1/gen_encode.c
new file mode 100644
index 0000000000..d80a2f8d1f
--- /dev/null
+++ b/source4/heimdal/lib/asn1/gen_encode.c
@@ -0,0 +1,557 @@
+/*
+ * Copyright (c) 1997 - 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "gen_locl.h"
+
+RCSID("$Id$");
+
+static void
+encode_primitive (const char *typename, const char *name)
+{
+ fprintf (codefile,
+ "e = der_put_%s(p, len, %s, &l);\n"
+ "if (e) return e;\np -= l; len -= l; ret += l;\n\n",
+ typename,
+ name);
+}
+
+const char *
+classname(Der_class class)
+{
+ const char *cn[] = { "ASN1_C_UNIV", "ASN1_C_APPL",
+ "ASN1_C_CONTEXT", "ASN1_C_PRIV" };
+ if(class < ASN1_C_UNIV || class > ASN1_C_PRIVATE)
+ return "???";
+ return cn[class];
+}
+
+
+const char *
+valuename(Der_class class, int value)
+{
+ static char s[32];
+ struct {
+ int value;
+ const char *s;
+ } *p, values[] = {
+#define X(Y) { Y, #Y }
+ X(UT_BMPString),
+ X(UT_BitString),
+ X(UT_Boolean),
+ X(UT_EmbeddedPDV),
+ X(UT_Enumerated),
+ X(UT_External),
+ X(UT_GeneralString),
+ X(UT_GeneralizedTime),
+ X(UT_GraphicString),
+ X(UT_IA5String),
+ X(UT_Integer),
+ X(UT_Null),
+ X(UT_NumericString),
+ X(UT_OID),
+ X(UT_ObjectDescriptor),
+ X(UT_OctetString),
+ X(UT_PrintableString),
+ X(UT_Real),
+ X(UT_RelativeOID),
+ X(UT_Sequence),
+ X(UT_Set),
+ X(UT_TeletexString),
+ X(UT_UTCTime),
+ X(UT_UTF8String),
+ X(UT_UniversalString),
+ X(UT_VideotexString),
+ X(UT_VisibleString),
+#undef X
+ { -1, NULL }
+ };
+ if(class == ASN1_C_UNIV) {
+ for(p = values; p->value != -1; p++)
+ if(p->value == value)
+ return p->s;
+ }
+ snprintf(s, sizeof(s), "%d", value);
+ return s;
+}
+
+static int
+encode_type (const char *name, const Type *t, const char *tmpstr)
+{
+ int constructed = 1;
+
+ switch (t->type) {
+ case TType:
+#if 0
+ encode_type (name, t->symbol->type);
+#endif
+ fprintf (codefile,
+ "e = encode_%s(p, len, %s, &l);\n"
+ "if (e) return e;\np -= l; len -= l; ret += l;\n\n",
+ t->symbol->gen_name, name);
+ break;
+ case TInteger:
+ if(t->members) {
+ fprintf(codefile,
+ "{\n"
+ "int enumint = (int)*%s;\n",
+ name);
+ encode_primitive ("integer", "&enumint");
+ fprintf(codefile, "}\n;");
+ } else if (t->range == NULL) {
+ encode_primitive ("heim_integer", name);
+ } else if (t->range->min == INT_MIN && t->range->max == INT_MAX) {
+ encode_primitive ("integer", name);
+ } else if (t->range->min == 0 && t->range->max == UINT_MAX) {
+ encode_primitive ("unsigned", name);
+ } else if (t->range->min == 0 && t->range->max == INT_MAX) {
+ encode_primitive ("unsigned", name);
+ } else
+ errx(1, "%s: unsupported range %d -> %d",
+ name, t->range->min, t->range->max);
+ constructed = 0;
+ break;
+ case TBoolean:
+ encode_primitive ("boolean", name);
+ constructed = 0;
+ break;
+ case TOctetString:
+ encode_primitive ("octet_string", name);
+ constructed = 0;
+ break;
+ case TBitString: {
+ Member *m;
+ int pos;
+
+ if (ASN1_TAILQ_EMPTY(t->members)) {
+ encode_primitive("bit_string", name);
+ constructed = 0;
+ break;
+ }
+
+ fprintf (codefile, "{\n"
+ "unsigned char c = 0;\n");
+ if (!rfc1510_bitstring)
+ fprintf (codefile,
+ "int rest = 0;\n"
+ "int bit_set = 0;\n");
+#if 0
+ pos = t->members->prev->val;
+ /* fix for buggy MIT (and OSF?) code */
+ if (pos > 31)
+ abort ();
+#endif
+ /*
+ * It seems that if we do not always set pos to 31 here, the MIT
+ * code will do the wrong thing.
+ *
+ * I hate ASN.1 (and DER), but I hate it even more when everybody
+ * has to screw it up differently.
+ */
+ pos = ASN1_TAILQ_LAST(t->members, memhead)->val;
+ if (rfc1510_bitstring) {
+ if (pos < 31)
+ pos = 31;
+ }
+
+ ASN1_TAILQ_FOREACH_REVERSE(m, t->members, memhead, members) {
+ while (m->val / 8 < pos / 8) {
+ if (!rfc1510_bitstring)
+ fprintf (codefile,
+ "if (c != 0 || bit_set) {\n");
+ fprintf (codefile,
+ "if (len < 1) return ASN1_OVERFLOW;\n"
+ "*p-- = c; len--; ret++;\n");
+ if (!rfc1510_bitstring)
+ fprintf (codefile,
+ "if (!bit_set) {\n"
+ "rest = 0;\n"
+ "while(c) { \n"
+ "if (c & 1) break;\n"
+ "c = c >> 1;\n"
+ "rest++;\n"
+ "}\n"
+ "bit_set = 1;\n"
+ "}\n"
+ "}\n");
+ fprintf (codefile,
+ "c = 0;\n");
+ pos -= 8;
+ }
+ fprintf (codefile,
+ "if((%s)->%s) {\n"
+ "c |= 1<<%d;\n",
+ name, m->gen_name, 7 - m->val % 8);
+ fprintf (codefile,
+ "}\n");
+ }
+
+ if (!rfc1510_bitstring)
+ fprintf (codefile,
+ "if (c != 0 || bit_set) {\n");
+ fprintf (codefile,
+ "if (len < 1) return ASN1_OVERFLOW;\n"
+ "*p-- = c; len--; ret++;\n");
+ if (!rfc1510_bitstring)
+ fprintf (codefile,
+ "if (!bit_set) {\n"
+ "rest = 0;\n"
+ "if(c) { \n"
+ "while(c) { \n"
+ "if (c & 1) break;\n"
+ "c = c >> 1;\n"
+ "rest++;\n"
+ "}\n"
+ "}\n"
+ "}\n"
+ "}\n");
+
+ fprintf (codefile,
+ "if (len < 1) return ASN1_OVERFLOW;\n"
+ "*p-- = %s;\n"
+ "len -= 1;\n"
+ "ret += 1;\n"
+ "}\n\n",
+ rfc1510_bitstring ? "0" : "rest");
+ constructed = 0;
+ break;
+ }
+ case TEnumerated : {
+ encode_primitive ("enumerated", name);
+ constructed = 0;
+ break;
+ }
+
+ case TSet:
+ case TSequence: {
+ Member *m;
+
+ if (t->members == NULL)
+ break;
+
+ ASN1_TAILQ_FOREACH_REVERSE(m, t->members, memhead, members) {
+ char *s;
+
+ if (m->ellipsis)
+ continue;
+
+ asprintf (&s, "%s(%s)->%s", m->optional ? "" : "&", name, m->gen_name);
+ if (s == NULL)
+ errx(1, "malloc");
+ fprintf(codefile, "/* %s */\n", m->name);
+ if (m->optional)
+ fprintf (codefile,
+ "if(%s) ",
+ s);
+ else if(m->defval)
+ gen_compare_defval(s + 1, m->defval);
+ fprintf (codefile, "{\n");
+ fprintf (codefile, "size_t %s_oldret = ret;\n", tmpstr);
+ fprintf (codefile, "ret = 0;\n");
+ encode_type (s, m->type, m->gen_name);
+ fprintf (codefile, "ret += %s_oldret;\n", tmpstr);
+ fprintf (codefile, "}\n");
+ free (s);
+ }
+ break;
+ }
+ case TSetOf: {
+
+ fprintf(codefile,
+ "{\n"
+ "struct heim_octet_string *val;\n"
+ "size_t elen, totallen = 0;\n"
+ "int eret;\n");
+
+ fprintf(codefile,
+ "if ((%s)->len > UINT_MAX/sizeof(val[0]))\n"
+ "return ERANGE;\n",
+ name);
+
+ fprintf(codefile,
+ "val = malloc(sizeof(val[0]) * (%s)->len);\n"
+ "if (val == NULL && (%s)->len != 0) return ENOMEM;\n",
+ name, name);
+
+ fprintf(codefile,
+ "for(i = 0; i < (%s)->len; i++) {\n",
+ name);
+
+ fprintf(codefile,
+ "ASN1_MALLOC_ENCODE(%s, val[i].data, "
+ "val[i].length, &(%s)->val[i], &elen, eret);\n",
+ t->subtype->symbol->gen_name,
+ name);
+
+ fprintf(codefile,
+ "if(eret) {\n"
+ "i--;\n"
+ "while (i >= 0) {\n"
+ "free(val[i].data);\n"
+ "i--;\n"
+ "}\n"
+ "free(val);\n"
+ "return eret;\n"
+ "}\n"
+ "totallen += elen;\n"
+ "}\n");
+
+ fprintf(codefile,
+ "if (totallen > len) {\n"
+ "for (i = 0; i < (%s)->len; i++) {\n"
+ "free(val[i].data);\n"
+ "}\n"
+ "free(val);\n"
+ "return ASN1_OVERFLOW;\n"
+ "}\n",
+ name);
+
+ fprintf(codefile,
+ "qsort(val, (%s)->len, sizeof(val[0]), _heim_der_set_sort);\n",
+ name);
+
+ fprintf (codefile,
+ "for(i = (%s)->len - 1; i >= 0; --i) {\n"
+ "p -= val[i].length;\n"
+ "ret += val[i].length;\n"
+ "memcpy(p + 1, val[i].data, val[i].length);\n"
+ "free(val[i].data);\n"
+ "}\n"
+ "free(val);\n"
+ "}\n",
+ name);
+ break;
+ }
+ case TSequenceOf: {
+ char *n;
+ char *sname;
+
+ fprintf (codefile,
+ "for(i = (%s)->len - 1; i >= 0; --i) {\n"
+ "size_t %s_for_oldret = ret;\n"
+ "ret = 0;\n",
+ name, tmpstr);
+ asprintf (&n, "&(%s)->val[i]", name);
+ if (n == NULL)
+ errx(1, "malloc");
+ asprintf (&sname, "%s_S_Of", tmpstr);
+ if (sname == NULL)
+ errx(1, "malloc");
+ encode_type (n, t->subtype, sname);
+ fprintf (codefile,
+ "ret += %s_for_oldret;\n"
+ "}\n",
+ tmpstr);
+ free (n);
+ free (sname);
+ break;
+ }
+ case TGeneralizedTime:
+ encode_primitive ("generalized_time", name);
+ constructed = 0;
+ break;
+ case TGeneralString:
+ encode_primitive ("general_string", name);
+ constructed = 0;
+ break;
+ case TTag: {
+ char *tname;
+ int c;
+ asprintf (&tname, "%s_tag", tmpstr);
+ if (tname == NULL)
+ errx(1, "malloc");
+ c = encode_type (name, t->subtype, tname);
+ fprintf (codefile,
+ "e = der_put_length_and_tag (p, len, ret, %s, %s, %s, &l);\n"
+ "if (e) return e;\np -= l; len -= l; ret += l;\n\n",
+ classname(t->tag.tagclass),
+ c ? "CONS" : "PRIM",
+ valuename(t->tag.tagclass, t->tag.tagvalue));
+ free (tname);
+ break;
+ }
+ case TChoice:{
+ Member *m, *have_ellipsis = NULL;
+ char *s;
+
+ if (t->members == NULL)
+ break;
+
+ fprintf(codefile, "\n");
+
+ asprintf (&s, "(%s)", name);
+ if (s == NULL)
+ errx(1, "malloc");
+ fprintf(codefile, "switch(%s->element) {\n", s);
+
+ ASN1_TAILQ_FOREACH_REVERSE(m, t->members, memhead, members) {
+ char *s2;
+
+ if (m->ellipsis) {
+ have_ellipsis = m;
+ continue;
+ }
+
+ fprintf (codefile, "case %s: {", m->label);
+ asprintf(&s2, "%s(%s)->u.%s", m->optional ? "" : "&",
+ s, m->gen_name);
+ if (s2 == NULL)
+ errx(1, "malloc");
+ if (m->optional)
+ fprintf (codefile, "if(%s) {\n", s2);
+ fprintf (codefile, "size_t %s_oldret = ret;\n", tmpstr);
+ fprintf (codefile, "ret = 0;\n");
+ constructed = encode_type (s2, m->type, m->gen_name);
+ fprintf (codefile, "ret += %s_oldret;\n", tmpstr);
+ if(m->optional)
+ fprintf (codefile, "}\n");
+ fprintf(codefile, "break;\n");
+ fprintf(codefile, "}\n");
+ free (s2);
+ }
+ free (s);
+ if (have_ellipsis) {
+ fprintf(codefile,
+ "case %s: {\n"
+ "if (len < (%s)->u.%s.length)\n"
+ "return ASN1_OVERFLOW;\n"
+ "p -= (%s)->u.%s.length;\n"
+ "ret += (%s)->u.%s.length;\n"
+ "memcpy(p + 1, (%s)->u.%s.data, (%s)->u.%s.length);\n"
+ "break;\n"
+ "}\n",
+ have_ellipsis->label,
+ name, have_ellipsis->gen_name,
+ name, have_ellipsis->gen_name,
+ name, have_ellipsis->gen_name,
+ name, have_ellipsis->gen_name,
+ name, have_ellipsis->gen_name);
+ }
+ fprintf(codefile, "};\n");
+ break;
+ }
+ case TOID:
+ encode_primitive ("oid", name);
+ constructed = 0;
+ break;
+ case TUTCTime:
+ encode_primitive ("utctime", name);
+ constructed = 0;
+ break;
+ case TUTF8String:
+ encode_primitive ("utf8string", name);
+ constructed = 0;
+ break;
+ case TPrintableString:
+ encode_primitive ("printable_string", name);
+ constructed = 0;
+ break;
+ case TIA5String:
+ encode_primitive ("ia5_string", name);
+ constructed = 0;
+ break;
+ case TBMPString:
+ encode_primitive ("bmp_string", name);
+ constructed = 0;
+ break;
+ case TUniversalString:
+ encode_primitive ("universal_string", name);
+ constructed = 0;
+ break;
+ case TVisibleString:
+ encode_primitive ("visible_string", name);
+ constructed = 0;
+ break;
+ case TNull:
+ fprintf (codefile, "/* NULL */\n");
+ constructed = 0;
+ break;
+ default:
+ abort ();
+ }
+ return constructed;
+}
+
+void
+generate_type_encode (const Symbol *s)
+{
+ fprintf (headerfile,
+ "int "
+ "encode_%s(unsigned char *, size_t, const %s *, size_t *);\n",
+ s->gen_name, s->gen_name);
+
+ fprintf (codefile, "int\n"
+ "encode_%s(unsigned char *p, size_t len,"
+ " const %s *data, size_t *size)\n"
+ "{\n",
+ s->gen_name, s->gen_name);
+
+ switch (s->type->type) {
+ case TInteger:
+ case TBoolean:
+ case TOctetString:
+ case TGeneralizedTime:
+ case TGeneralString:
+ case TUTCTime:
+ case TUTF8String:
+ case TPrintableString:
+ case TIA5String:
+ case TBMPString:
+ case TUniversalString:
+ case TVisibleString:
+ case TNull:
+ case TBitString:
+ case TEnumerated:
+ case TOID:
+ case TSequence:
+ case TSequenceOf:
+ case TSet:
+ case TSetOf:
+ case TTag:
+ case TType:
+ case TChoice:
+ fprintf (codefile,
+ "size_t ret = 0;\n"
+ "size_t l;\n"
+ "int i, e;\n\n");
+ fprintf(codefile, "i = 0;\n"); /* hack to avoid `unused variable' */
+
+ encode_type("data", s->type, "Top");
+
+ fprintf (codefile, "*size = ret;\n"
+ "return 0;\n");
+ break;
+ default:
+ abort ();
+ }
+ fprintf (codefile, "}\n\n");
+}
diff --git a/source4/heimdal/lib/asn1/gen_free.c b/source4/heimdal/lib/asn1/gen_free.c
new file mode 100644
index 0000000000..305d7de247
--- /dev/null
+++ b/source4/heimdal/lib/asn1/gen_free.c
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 1997 - 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "gen_locl.h"
+
+RCSID("$Id$");
+
+static void
+free_primitive (const char *typename, const char *name)
+{
+ fprintf (codefile, "der_free_%s(%s);\n", typename, name);
+}
+
+static void
+free_type (const char *name, const Type *t, int preserve)
+{
+ switch (t->type) {
+ case TType:
+#if 0
+ free_type (name, t->symbol->type, preserve);
+#endif
+ fprintf (codefile, "free_%s(%s);\n", t->symbol->gen_name, name);
+ break;
+ case TInteger:
+ if (t->range == NULL && t->members == NULL) {
+ free_primitive ("heim_integer", name);
+ break;
+ }
+ case TBoolean:
+ case TEnumerated :
+ case TNull:
+ case TGeneralizedTime:
+ case TUTCTime:
+ break;
+ case TBitString:
+ if (ASN1_TAILQ_EMPTY(t->members))
+ free_primitive("bit_string", name);
+ break;
+ case TOctetString:
+ free_primitive ("octet_string", name);
+ break;
+ case TChoice:
+ case TSet:
+ case TSequence: {
+ Member *m, *have_ellipsis = NULL;
+
+ if (t->members == NULL)
+ break;
+
+ if ((t->type == TSequence || t->type == TChoice) && preserve)
+ fprintf(codefile, "der_free_octet_string(&data->_save);\n");
+
+ if(t->type == TChoice)
+ fprintf(codefile, "switch((%s)->element) {\n", name);
+
+ ASN1_TAILQ_FOREACH(m, t->members, members) {
+ char *s;
+
+ if (m->ellipsis){
+ have_ellipsis = m;
+ continue;
+ }
+
+ if(t->type == TChoice)
+ fprintf(codefile, "case %s:\n", m->label);
+ asprintf (&s, "%s(%s)->%s%s",
+ m->optional ? "" : "&", name,
+ t->type == TChoice ? "u." : "", m->gen_name);
+ if (s == NULL)
+ errx(1, "malloc");
+ if(m->optional)
+ fprintf(codefile, "if(%s) {\n", s);
+ free_type (s, m->type, FALSE);
+ if(m->optional)
+ fprintf(codefile,
+ "free(%s);\n"
+ "%s = NULL;\n"
+ "}\n",s, s);
+ free (s);
+ if(t->type == TChoice)
+ fprintf(codefile, "break;\n");
+ }
+
+ if(t->type == TChoice) {
+ if (have_ellipsis)
+ fprintf(codefile,
+ "case %s:\n"
+ "der_free_octet_string(&(%s)->u.%s);\n"
+ "break;",
+ have_ellipsis->label,
+ name, have_ellipsis->gen_name);
+ fprintf(codefile, "}\n");
+ }
+ break;
+ }
+ case TSetOf:
+ case TSequenceOf: {
+ char *n;
+
+ fprintf (codefile, "while((%s)->len){\n", name);
+ asprintf (&n, "&(%s)->val[(%s)->len-1]", name, name);
+ if (n == NULL)
+ errx(1, "malloc");
+ free_type(n, t->subtype, FALSE);
+ fprintf(codefile,
+ "(%s)->len--;\n"
+ "}\n",
+ name);
+ fprintf(codefile,
+ "free((%s)->val);\n"
+ "(%s)->val = NULL;\n", name, name);
+ free(n);
+ break;
+ }
+ case TGeneralString:
+ free_primitive ("general_string", name);
+ break;
+ case TUTF8String:
+ free_primitive ("utf8string", name);
+ break;
+ case TPrintableString:
+ free_primitive ("printable_string", name);
+ break;
+ case TIA5String:
+ free_primitive ("ia5_string", name);
+ break;
+ case TBMPString:
+ free_primitive ("bmp_string", name);
+ break;
+ case TUniversalString:
+ free_primitive ("universal_string", name);
+ break;
+ case TVisibleString:
+ free_primitive ("visible_string", name);
+ break;
+ case TTag:
+ free_type (name, t->subtype, preserve);
+ break;
+ case TOID :
+ free_primitive ("oid", name);
+ break;
+ default :
+ abort ();
+ }
+}
+
+void
+generate_type_free (const Symbol *s)
+{
+ int preserve = preserve_type(s->name) ? TRUE : FALSE;
+
+ fprintf (headerfile,
+ "void free_%s (%s *);\n",
+ s->gen_name, s->gen_name);
+
+ fprintf (codefile, "void\n"
+ "free_%s(%s *data)\n"
+ "{\n",
+ s->gen_name, s->gen_name);
+
+ free_type ("data", s->type, preserve);
+ fprintf (codefile, "}\n\n");
+}
+
diff --git a/source4/heimdal/lib/asn1/gen_glue.c b/source4/heimdal/lib/asn1/gen_glue.c
new file mode 100644
index 0000000000..9f7eca45eb
--- /dev/null
+++ b/source4/heimdal/lib/asn1/gen_glue.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 1997, 1999, 2000, 2003 - 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "gen_locl.h"
+
+RCSID("$Id$");
+
+static void
+generate_2int (const Type *t, const char *gen_name)
+{
+ Member *m;
+
+ fprintf (headerfile,
+ "unsigned %s2int(%s);\n",
+ gen_name, gen_name);
+
+ fprintf (codefile,
+ "unsigned %s2int(%s f)\n"
+ "{\n"
+ "unsigned r = 0;\n",
+ gen_name, gen_name);
+
+ ASN1_TAILQ_FOREACH(m, t->members, members) {
+ fprintf (codefile, "if(f.%s) r |= (1U << %d);\n",
+ m->gen_name, m->val);
+ }
+ fprintf (codefile, "return r;\n"
+ "}\n\n");
+}
+
+static void
+generate_int2 (const Type *t, const char *gen_name)
+{
+ Member *m;
+
+ fprintf (headerfile,
+ "%s int2%s(unsigned);\n",
+ gen_name, gen_name);
+
+ fprintf (codefile,
+ "%s int2%s(unsigned n)\n"
+ "{\n"
+ "\t%s flags;\n\n",
+ gen_name, gen_name, gen_name);
+
+ if(t->members) {
+ ASN1_TAILQ_FOREACH(m, t->members, members) {
+ fprintf (codefile, "\tflags.%s = (n >> %d) & 1;\n",
+ m->gen_name, m->val);
+ }
+ }
+ fprintf (codefile, "\treturn flags;\n"
+ "}\n\n");
+}
+
+/*
+ * This depends on the bit string being declared in increasing order
+ */
+
+static void
+generate_units (const Type *t, const char *gen_name)
+{
+ Member *m;
+
+ fprintf (headerfile,
+ "const struct units * asn1_%s_units(void);",
+ gen_name);
+
+ fprintf (codefile,
+ "static struct units %s_units[] = {\n",
+ gen_name);
+
+ if(t->members) {
+ ASN1_TAILQ_FOREACH_REVERSE(m, t->members, memhead, members) {
+ fprintf (codefile,
+ "\t{\"%s\",\t1U << %d},\n", m->name, m->val);
+ }
+ }
+
+ fprintf (codefile,
+ "\t{NULL,\t0}\n"
+ "};\n\n");
+
+ fprintf (codefile,
+ "const struct units * asn1_%s_units(void){\n"
+ "return %s_units;\n"
+ "}\n\n",
+ gen_name, gen_name);
+
+
+}
+
+void
+generate_glue (const Type *t, const char *gen_name)
+{
+ switch(t->type) {
+ case TTag:
+ generate_glue(t->subtype, gen_name);
+ break;
+ case TBitString :
+ if (!ASN1_TAILQ_EMPTY(t->members)) {
+ generate_2int (t, gen_name);
+ generate_int2 (t, gen_name);
+ generate_units (t, gen_name);
+ }
+ break;
+ default :
+ break;
+ }
+}
diff --git a/source4/heimdal/lib/asn1/gen_length.c b/source4/heimdal/lib/asn1/gen_length.c
new file mode 100644
index 0000000000..a1df4eef6b
--- /dev/null
+++ b/source4/heimdal/lib/asn1/gen_length.c
@@ -0,0 +1,283 @@
+/*
+ * Copyright (c) 1997 - 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "gen_locl.h"
+
+RCSID("$Id$");
+
+static void
+length_primitive (const char *typename,
+ const char *name,
+ const char *variable)
+{
+ fprintf (codefile, "%s += der_length_%s(%s);\n", variable, typename, name);
+}
+
+static size_t
+length_tag(unsigned int tag)
+{
+ size_t len = 0;
+
+ if(tag <= 30)
+ return 1;
+ while(tag) {
+ tag /= 128;
+ len++;
+ }
+ return len + 1;
+}
+
+
+static int
+length_type (const char *name, const Type *t,
+ const char *variable, const char *tmpstr)
+{
+ switch (t->type) {
+ case TType:
+#if 0
+ length_type (name, t->symbol->type);
+#endif
+ fprintf (codefile, "%s += length_%s(%s);\n",
+ variable, t->symbol->gen_name, name);
+ break;
+ case TInteger:
+ if(t->members) {
+ fprintf(codefile,
+ "{\n"
+ "int enumint = *%s;\n", name);
+ length_primitive ("integer", "&enumint", variable);
+ fprintf(codefile, "}\n");
+ } else if (t->range == NULL) {
+ length_primitive ("heim_integer", name, variable);
+ } else if (t->range->min == INT_MIN && t->range->max == INT_MAX) {
+ length_primitive ("integer", name, variable);
+ } else if (t->range->min == 0 && t->range->max == UINT_MAX) {
+ length_primitive ("unsigned", name, variable);
+ } else if (t->range->min == 0 && t->range->max == INT_MAX) {
+ length_primitive ("unsigned", name, variable);
+ } else
+ errx(1, "%s: unsupported range %d -> %d",
+ name, t->range->min, t->range->max);
+
+ break;
+ case TBoolean:
+ fprintf (codefile, "%s += 1;\n", variable);
+ break;
+ case TEnumerated :
+ length_primitive ("enumerated", name, variable);
+ break;
+ case TOctetString:
+ length_primitive ("octet_string", name, variable);
+ break;
+ case TBitString: {
+ if (ASN1_TAILQ_EMPTY(t->members))
+ length_primitive("bit_string", name, variable);
+ else {
+ if (!rfc1510_bitstring) {
+ Member *m;
+ int pos = ASN1_TAILQ_LAST(t->members, memhead)->val;
+
+ fprintf(codefile,
+ "do {\n");
+ ASN1_TAILQ_FOREACH_REVERSE(m, t->members, memhead, members) {
+ while (m->val / 8 < pos / 8) {
+ pos -= 8;
+ }
+ fprintf (codefile,
+ "if((%s)->%s) { %s += %d; break; }\n",
+ name, m->gen_name, variable, (pos + 8) / 8);
+ }
+ fprintf(codefile,
+ "} while(0);\n");
+ fprintf (codefile, "%s += 1;\n", variable);
+ } else {
+ fprintf (codefile, "%s += 5;\n", variable);
+ }
+ }
+ break;
+ }
+ case TSet:
+ case TSequence:
+ case TChoice: {
+ Member *m, *have_ellipsis = NULL;
+
+ if (t->members == NULL)
+ break;
+
+ if(t->type == TChoice)
+ fprintf (codefile, "switch((%s)->element) {\n", name);
+
+ ASN1_TAILQ_FOREACH(m, t->members, members) {
+ char *s;
+
+ if (m->ellipsis) {
+ have_ellipsis = m;
+ continue;
+ }
+
+ if(t->type == TChoice)
+ fprintf(codefile, "case %s:\n", m->label);
+
+ asprintf (&s, "%s(%s)->%s%s",
+ m->optional ? "" : "&", name,
+ t->type == TChoice ? "u." : "", m->gen_name);
+ if (s == NULL)
+ errx(1, "malloc");
+ if (m->optional)
+ fprintf (codefile, "if(%s)", s);
+ else if(m->defval)
+ gen_compare_defval(s + 1, m->defval);
+ fprintf (codefile, "{\n"
+ "size_t %s_oldret = %s;\n"
+ "%s = 0;\n", tmpstr, variable, variable);
+ length_type (s, m->type, "ret", m->gen_name);
+ fprintf (codefile, "ret += %s_oldret;\n", tmpstr);
+ fprintf (codefile, "}\n");
+ free (s);
+ if(t->type == TChoice)
+ fprintf(codefile, "break;\n");
+ }
+ if(t->type == TChoice) {
+ if (have_ellipsis)
+ fprintf(codefile,
+ "case %s:\n"
+ "ret += (%s)->u.%s.length;\n"
+ "break;\n",
+ have_ellipsis->label,
+ name,
+ have_ellipsis->gen_name);
+ fprintf (codefile, "}\n"); /* switch */
+ }
+ break;
+ }
+ case TSetOf:
+ case TSequenceOf: {
+ char *n;
+ char *sname;
+
+ fprintf (codefile,
+ "{\n"
+ "int %s_oldret = %s;\n"
+ "int i;\n"
+ "%s = 0;\n",
+ tmpstr, variable, variable);
+
+ fprintf (codefile, "for(i = (%s)->len - 1; i >= 0; --i){\n", name);
+ fprintf (codefile, "int %s_for_oldret = %s;\n"
+ "%s = 0;\n", tmpstr, variable, variable);
+ asprintf (&n, "&(%s)->val[i]", name);
+ if (n == NULL)
+ errx(1, "malloc");
+ asprintf (&sname, "%s_S_Of", tmpstr);
+ if (sname == NULL)
+ errx(1, "malloc");
+ length_type(n, t->subtype, variable, sname);
+ fprintf (codefile, "%s += %s_for_oldret;\n",
+ variable, tmpstr);
+ fprintf (codefile, "}\n");
+
+ fprintf (codefile,
+ "%s += %s_oldret;\n"
+ "}\n", variable, tmpstr);
+ free(n);
+ free(sname);
+ break;
+ }
+ case TGeneralizedTime:
+ length_primitive ("generalized_time", name, variable);
+ break;
+ case TGeneralString:
+ length_primitive ("general_string", name, variable);
+ break;
+ case TUTCTime:
+ length_primitive ("utctime", name, variable);
+ break;
+ case TUTF8String:
+ length_primitive ("utf8string", name, variable);
+ break;
+ case TPrintableString:
+ length_primitive ("printable_string", name, variable);
+ break;
+ case TIA5String:
+ length_primitive ("ia5_string", name, variable);
+ break;
+ case TBMPString:
+ length_primitive ("bmp_string", name, variable);
+ break;
+ case TUniversalString:
+ length_primitive ("universal_string", name, variable);
+ break;
+ case TVisibleString:
+ length_primitive ("visible_string", name, variable);
+ break;
+ case TNull:
+ fprintf (codefile, "/* NULL */\n");
+ break;
+ case TTag:{
+ char *tname;
+ asprintf(&tname, "%s_tag", tmpstr);
+ if (tname == NULL)
+ errx(1, "malloc");
+ length_type (name, t->subtype, variable, tname);
+ fprintf (codefile, "ret += %lu + der_length_len (ret);\n",
+ (unsigned long)length_tag(t->tag.tagvalue));
+ free(tname);
+ break;
+ }
+ case TOID:
+ length_primitive ("oid", name, variable);
+ break;
+ default :
+ abort ();
+ }
+ return 0;
+}
+
+void
+generate_type_length (const Symbol *s)
+{
+ fprintf (headerfile,
+ "size_t length_%s(const %s *);\n",
+ s->gen_name, s->gen_name);
+
+ fprintf (codefile,
+ "size_t\n"
+ "length_%s(const %s *data)\n"
+ "{\n"
+ "size_t ret = 0;\n",
+ s->gen_name, s->gen_name);
+
+ length_type ("data", s->type, "ret", "Top");
+ fprintf (codefile, "return ret;\n}\n\n");
+}
+
diff --git a/source4/heimdal/lib/asn1/gen_locl.h b/source4/heimdal/lib/asn1/gen_locl.h
new file mode 100644
index 0000000000..c8b3896314
--- /dev/null
+++ b/source4/heimdal/lib/asn1/gen_locl.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 1997-2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef __GEN_LOCL_H__
+#define __GEN_LOCL_H__
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <ctype.h>
+#include <time.h>
+#include <errno.h>
+#include <err.h>
+#include <roken.h>
+#include "hash.h"
+#include "symbol.h"
+#include "asn1-common.h"
+#include "der.h"
+
+void generate_type (const Symbol *);
+void generate_constant (const Symbol *);
+void generate_type_encode (const Symbol *);
+void generate_type_decode (const Symbol *);
+void generate_type_free (const Symbol *);
+void generate_type_length (const Symbol *);
+void generate_type_copy (const Symbol *);
+void generate_type_seq (const Symbol *);
+void generate_glue (const Type *, const char*);
+
+const char *classname(Der_class);
+const char *valuename(Der_class, int);
+
+void gen_compare_defval(const char *, struct value *);
+void gen_assign_defval(const char *, struct value *);
+
+
+void init_generate (const char *, const char *);
+const char *get_filename (void);
+void close_generate(void);
+void add_import(const char *);
+int yyparse(void);
+
+int preserve_type(const char *);
+int seq_type(const char *);
+
+extern FILE *headerfile, *codefile, *logfile;
+extern int dce_fix;
+extern int rfc1510_bitstring;
+
+extern int error_flag;
+
+#endif /* __GEN_LOCL_H__ */
diff --git a/source4/heimdal/lib/asn1/gen_seq.c b/source4/heimdal/lib/asn1/gen_seq.c
new file mode 100644
index 0000000000..ec3ccc265e
--- /dev/null
+++ b/source4/heimdal/lib/asn1/gen_seq.c
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 1997 - 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "gen_locl.h"
+
+RCSID("$Id$");
+
+void
+generate_type_seq (const Symbol *s)
+{
+ char *subname;
+ Type *type;
+
+ if (!seq_type(s->name))
+ return;
+ type = s->type;
+ while(type->type == TTag)
+ type = type->subtype;
+
+ if (type->type != TSequenceOf) {
+ printf("%s not seq of %d\n", s->name, (int)type->type);
+ return;
+ }
+
+ /*
+ * Require the subtype to be a type so we can name it and use
+ * copy_/free_
+ */
+
+ if (type->subtype->type != TType) {
+ fprintf(stderr, "%s subtype is not a type, can't generate "
+ "sequence code for this case: %d\n",
+ s->name, (int)type->subtype->type);
+ exit(1);
+ }
+
+ subname = type->subtype->symbol->gen_name;
+
+ fprintf (headerfile,
+ "int add_%s (%s *, const %s *);\n"
+ "int remove_%s (%s *, unsigned int);\n",
+ s->gen_name, s->gen_name, subname,
+ s->gen_name, s->gen_name);
+
+ fprintf (codefile, "int\n"
+ "add_%s(%s *data, const %s *element)\n"
+ "{\n",
+ s->gen_name, s->gen_name, subname);
+
+ fprintf (codefile,
+ "int ret;\n"
+ "void *ptr;\n"
+ "\n"
+ "ptr = realloc(data->val, \n"
+ "\t(data->len + 1) * sizeof(data->val[0]));\n"
+ "if (ptr == NULL) return ENOMEM;\n"
+ "data->val = ptr;\n\n"
+ "ret = copy_%s(element, &data->val[data->len]);\n"
+ "if (ret) return ret;\n"
+ "data->len++;\n"
+ "return 0;\n",
+ subname);
+
+ fprintf (codefile, "}\n\n");
+
+ fprintf (codefile, "int\n"
+ "remove_%s(%s *data, unsigned int element)\n"
+ "{\n",
+ s->gen_name, s->gen_name);
+
+ fprintf (codefile,
+ "void *ptr;\n"
+ "\n"
+ "if (data->len == 0 || element >= data->len)\n"
+ "\treturn ASN1_OVERRUN;\n"
+ "free_%s(&data->val[element]);\n"
+ "data->len--;\n"
+ /* don't move if its the last element */
+ "if (element < data->len)\n"
+ "\tmemmove(&data->val[element], &data->val[element + 1], \n"
+ "\t\tsizeof(data->val[0]) * data->len);\n"
+ /* resize but don't care about failures since it doesn't matter */
+ "ptr = realloc(data->val, data->len * sizeof(data->val[0]));\n"
+ "if (ptr != NULL || data->len == 0) data->val = ptr;\n"
+ "return 0;\n",
+ subname);
+
+ fprintf (codefile, "}\n\n");
+}
diff --git a/source4/heimdal/lib/asn1/hash.c b/source4/heimdal/lib/asn1/hash.c
new file mode 100644
index 0000000000..73b6cf97c4
--- /dev/null
+++ b/source4/heimdal/lib/asn1/hash.c
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 1997 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Hash table functions
+ */
+
+#include "gen_locl.h"
+
+RCSID("$Id$");
+
+static Hashentry *_search(Hashtab * htab, /* The hash table */
+ void *ptr); /* And key */
+
+Hashtab *
+hashtabnew(int sz,
+ int (*cmp) (void *, void *),
+ unsigned (*hash) (void *))
+{
+ Hashtab *htab;
+ int i;
+
+ assert(sz > 0);
+
+ htab = (Hashtab *) malloc(sizeof(Hashtab) + (sz - 1) * sizeof(Hashentry *));
+ if (htab == NULL)
+ return NULL;
+
+ for (i = 0; i < sz; ++i)
+ htab->tab[i] = NULL;
+
+ htab->cmp = cmp;
+ htab->hash = hash;
+ htab->sz = sz;
+ return htab;
+}
+
+/* Intern search function */
+
+static Hashentry *
+_search(Hashtab * htab, void *ptr)
+{
+ Hashentry *hptr;
+
+ assert(htab && ptr);
+
+ for (hptr = htab->tab[(*htab->hash) (ptr) % htab->sz];
+ hptr;
+ hptr = hptr->next)
+ if ((*htab->cmp) (ptr, hptr->ptr) == 0)
+ break;
+ return hptr;
+}
+
+/* Search for element in hash table */
+
+void *
+hashtabsearch(Hashtab * htab, void *ptr)
+{
+ Hashentry *tmp;
+
+ tmp = _search(htab, ptr);
+ return tmp ? tmp->ptr : tmp;
+}
+
+/* add element to hash table */
+/* if already there, set new value */
+/* !NULL if succesful */
+
+void *
+hashtabadd(Hashtab * htab, void *ptr)
+{
+ Hashentry *h = _search(htab, ptr);
+ Hashentry **tabptr;
+
+ assert(htab && ptr);
+
+ if (h)
+ free((void *) h->ptr);
+ else {
+ h = (Hashentry *) malloc(sizeof(Hashentry));
+ if (h == NULL) {
+ return NULL;
+ }
+ tabptr = &htab->tab[(*htab->hash) (ptr) % htab->sz];
+ h->next = *tabptr;
+ *tabptr = h;
+ h->prev = tabptr;
+ if (h->next)
+ h->next->prev = &h->next;
+ }
+ h->ptr = ptr;
+ return h;
+}
+
+/* delete element with key key. Iff freep, free Hashentry->ptr */
+
+int
+_hashtabdel(Hashtab * htab, void *ptr, int freep)
+{
+ Hashentry *h;
+
+ assert(htab && ptr);
+
+ h = _search(htab, ptr);
+ if (h) {
+ if (freep)
+ free(h->ptr);
+ if ((*(h->prev) = h->next))
+ h->next->prev = h->prev;
+ free(h);
+ return 0;
+ } else
+ return -1;
+}
+
+/* Do something for each element */
+
+void
+hashtabforeach(Hashtab * htab, int (*func) (void *ptr, void *arg),
+ void *arg)
+{
+ Hashentry **h, *g;
+
+ assert(htab);
+
+ for (h = htab->tab; h < &htab->tab[htab->sz]; ++h)
+ for (g = *h; g; g = g->next)
+ if ((*func) (g->ptr, arg))
+ return;
+}
+
+/* standard hash-functions for strings */
+
+unsigned
+hashadd(const char *s)
+{ /* Standard hash function */
+ unsigned i;
+
+ assert(s);
+
+ for (i = 0; *s; ++s)
+ i += *s;
+ return i;
+}
+
+unsigned
+hashcaseadd(const char *s)
+{ /* Standard hash function */
+ unsigned i;
+
+ assert(s);
+
+ for (i = 0; *s; ++s)
+ i += toupper((unsigned char)*s);
+ return i;
+}
+
+#define TWELVE (sizeof(unsigned))
+#define SEVENTYFIVE (6*sizeof(unsigned))
+#define HIGH_BITS (~((unsigned)(~0) >> TWELVE))
+
+unsigned
+hashjpw(const char *ss)
+{ /* another hash function */
+ unsigned h = 0;
+ unsigned g;
+ const unsigned char *s = (const unsigned char *)ss;
+
+ for (; *s; ++s) {
+ h = (h << TWELVE) + *s;
+ if ((g = h & HIGH_BITS))
+ h = (h ^ (g >> SEVENTYFIVE)) & ~HIGH_BITS;
+ }
+ return h;
+}
diff --git a/source4/heimdal/lib/asn1/hash.h b/source4/heimdal/lib/asn1/hash.h
new file mode 100644
index 0000000000..f37bdbb849
--- /dev/null
+++ b/source4/heimdal/lib/asn1/hash.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 1997 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * hash.h. Header file for hash table functions
+ */
+
+/* $Id$ */
+
+struct hashentry { /* Entry in bucket */
+ struct hashentry **prev;
+ struct hashentry *next;
+ void *ptr;
+};
+
+typedef struct hashentry Hashentry;
+
+struct hashtab { /* Hash table */
+ int (*cmp)(void *, void *); /* Compare function */
+ unsigned (*hash)(void *); /* hash function */
+ int sz; /* Size */
+ Hashentry *tab[1]; /* The table */
+};
+
+typedef struct hashtab Hashtab;
+
+/* prototypes */
+
+Hashtab *hashtabnew(int sz,
+ int (*cmp)(void *, void *),
+ unsigned (*hash)(void *)); /* Make new hash table */
+
+void *hashtabsearch(Hashtab *htab, /* The hash table */
+ void *ptr); /* The key */
+
+
+void *hashtabadd(Hashtab *htab, /* The hash table */
+ void *ptr); /* The element */
+
+int _hashtabdel(Hashtab *htab, /* The table */
+ void *ptr, /* Key */
+ int freep); /* Free data part? */
+
+void hashtabforeach(Hashtab *htab,
+ int (*func)(void *ptr, void *arg),
+ void *arg);
+
+unsigned hashadd(const char *s); /* Standard hash function */
+unsigned hashcaseadd(const char *s); /* Standard hash function */
+unsigned hashjpw(const char *s); /* another hash function */
+
+/* macros */
+
+ /* Don't free space */
+#define hashtabdel(htab,key) _hashtabdel(htab,key,FALSE)
+
+#define hashtabfree(htab,key) _hashtabdel(htab,key,TRUE) /* Do! */
diff --git a/source4/heimdal/lib/asn1/heim_asn1.h b/source4/heimdal/lib/asn1/heim_asn1.h
new file mode 100644
index 0000000000..4eeafc20f9
--- /dev/null
+++ b/source4/heimdal/lib/asn1/heim_asn1.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2003-2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef __HEIM_ANY_H__
+#define __HEIM_ANY_H__ 1
+
+int encode_heim_any(unsigned char *, size_t, const heim_any *, size_t *);
+int decode_heim_any(const unsigned char *, size_t, heim_any *, size_t *);
+void free_heim_any(heim_any *);
+size_t length_heim_any(const heim_any *);
+int copy_heim_any(const heim_any *, heim_any *);
+
+int encode_heim_any_set(unsigned char *, size_t,
+ const heim_any_set *, size_t *);
+int decode_heim_any_set(const unsigned char *, size_t,
+ heim_any_set *,size_t *);
+void free_heim_any_set(heim_any_set *);
+size_t length_heim_any_set(const heim_any_set *);
+int copy_heim_any_set(const heim_any_set *, heim_any_set *);
+int heim_any_cmp(const heim_any_set *, const heim_any_set *);
+
+#endif /* __HEIM_ANY_H__ */
diff --git a/source4/heimdal/lib/asn1/k5.asn1 b/source4/heimdal/lib/asn1/k5.asn1
new file mode 100644
index 0000000000..9b36498161
--- /dev/null
+++ b/source4/heimdal/lib/asn1/k5.asn1
@@ -0,0 +1,671 @@
+-- $Id$
+
+KERBEROS5 DEFINITIONS ::=
+BEGIN
+
+NAME-TYPE ::= INTEGER {
+ KRB5_NT_UNKNOWN(0), -- Name type not known
+ KRB5_NT_PRINCIPAL(1), -- Just the name of the principal as in
+ KRB5_NT_SRV_INST(2), -- Service and other unique instance (krbtgt)
+ KRB5_NT_SRV_HST(3), -- Service with host name as instance
+ KRB5_NT_SRV_XHST(4), -- Service with host as remaining components
+ KRB5_NT_UID(5), -- Unique ID
+ KRB5_NT_X500_PRINCIPAL(6), -- PKINIT
+ KRB5_NT_SMTP_NAME(7), -- Name in form of SMTP email name
+ KRB5_NT_ENTERPRISE_PRINCIPAL(10), -- Windows 2000 UPN
+ KRB5_NT_ENT_PRINCIPAL_AND_ID(-130), -- Windows 2000 UPN and SID
+ KRB5_NT_MS_PRINCIPAL(-128), -- NT 4 style name
+ KRB5_NT_MS_PRINCIPAL_AND_ID(-129) -- NT style name and SID
+}
+
+-- message types
+
+MESSAGE-TYPE ::= INTEGER {
+ krb-as-req(10), -- Request for initial authentication
+ krb-as-rep(11), -- Response to KRB_AS_REQ request
+ krb-tgs-req(12), -- Request for authentication based on TGT
+ krb-tgs-rep(13), -- Response to KRB_TGS_REQ request
+ krb-ap-req(14), -- application request to server
+ krb-ap-rep(15), -- Response to KRB_AP_REQ_MUTUAL
+ krb-safe(20), -- Safe (checksummed) application message
+ krb-priv(21), -- Private (encrypted) application message
+ krb-cred(22), -- Private (encrypted) message to forward credentials
+ krb-error(30) -- Error response
+}
+
+
+-- pa-data types
+
+PADATA-TYPE ::= INTEGER {
+ KRB5-PADATA-NONE(0),
+ KRB5-PADATA-TGS-REQ(1),
+ KRB5-PADATA-AP-REQ(1),
+ KRB5-PADATA-ENC-TIMESTAMP(2),
+ KRB5-PADATA-PW-SALT(3),
+ KRB5-PADATA-ENC-UNIX-TIME(5),
+ KRB5-PADATA-SANDIA-SECUREID(6),
+ KRB5-PADATA-SESAME(7),
+ KRB5-PADATA-OSF-DCE(8),
+ KRB5-PADATA-CYBERSAFE-SECUREID(9),
+ KRB5-PADATA-AFS3-SALT(10),
+ KRB5-PADATA-ETYPE-INFO(11),
+ KRB5-PADATA-SAM-CHALLENGE(12), -- (sam/otp)
+ KRB5-PADATA-SAM-RESPONSE(13), -- (sam/otp)
+ KRB5-PADATA-PK-AS-REQ-19(14), -- (PKINIT-19)
+ KRB5-PADATA-PK-AS-REP-19(15), -- (PKINIT-19)
+ KRB5-PADATA-PK-AS-REQ-WIN(15), -- (PKINIT - old number)
+ KRB5-PADATA-PK-AS-REQ(16), -- (PKINIT-25)
+ KRB5-PADATA-PK-AS-REP(17), -- (PKINIT-25)
+ KRB5-PADATA-PA-PK-OCSP-RESPONSE(18),
+ KRB5-PADATA-ETYPE-INFO2(19),
+ KRB5-PADATA-USE-SPECIFIED-KVNO(20),
+ KRB5-PADATA-SVR-REFERRAL-INFO(20), --- old ms referral number
+ KRB5-PADATA-SAM-REDIRECT(21), -- (sam/otp)
+ KRB5-PADATA-GET-FROM-TYPED-DATA(22),
+ KRB5-PADATA-SAM-ETYPE-INFO(23),
+ KRB5-PADATA-SERVER-REFERRAL(25),
+ KRB5-PADATA-TD-KRB-PRINCIPAL(102), -- PrincipalName
+ KRB5-PADATA-PK-TD-TRUSTED-CERTIFIERS(104), -- PKINIT
+ KRB5-PADATA-PK-TD-CERTIFICATE-INDEX(105), -- PKINIT
+ KRB5-PADATA-TD-APP-DEFINED-ERROR(106), -- application specific
+ KRB5-PADATA-TD-REQ-NONCE(107), -- INTEGER
+ KRB5-PADATA-TD-REQ-SEQ(108), -- INTEGER
+ KRB5-PADATA-PA-PAC-REQUEST(128), -- jbrezak@exchange.microsoft.com
+ KRB5-PADATA-S4U2SELF(129),
+ KRB5-PADATA-EPAC(130), -- EPAK
+ KRB5-PADATA-PK-AS-09-BINDING(132), -- client send this to
+ -- tell KDC that is supports
+ -- the asCheckSum in the
+ -- PK-AS-REP
+ KRB5-PADATA-CLIENT-CANONICALIZED(133) --
+}
+
+AUTHDATA-TYPE ::= INTEGER {
+ KRB5-AUTHDATA-IF-RELEVANT(1),
+ KRB5-AUTHDATA-INTENDED-FOR_SERVER(2),
+ KRB5-AUTHDATA-INTENDED-FOR-APPLICATION-CLASS(3),
+ KRB5-AUTHDATA-KDC-ISSUED(4),
+ KRB5-AUTHDATA-AND-OR(5),
+ KRB5-AUTHDATA-MANDATORY-TICKET-EXTENSIONS(6),
+ KRB5-AUTHDATA-IN-TICKET-EXTENSIONS(7),
+ KRB5-AUTHDATA-MANDATORY-FOR-KDC(8),
+ KRB5-AUTHDATA-INITIAL-VERIFIED-CAS(9),
+ KRB5-AUTHDATA-OSF-DCE(64),
+ KRB5-AUTHDATA-SESAME(65),
+ KRB5-AUTHDATA-OSF-DCE-PKI-CERTID(66),
+ KRB5-AUTHDATA-WIN2K-PAC(128),
+ KRB5-AUTHDATA-GSS-API-ETYPE-NEGOTIATION(129), -- Authenticator only
+ KRB5-AUTHDATA-SIGNTICKET-OLD(-17),
+ KRB5-AUTHDATA-SIGNTICKET(142)
+}
+
+-- checksumtypes
+
+CKSUMTYPE ::= INTEGER {
+ CKSUMTYPE_NONE(0),
+ CKSUMTYPE_CRC32(1),
+ CKSUMTYPE_RSA_MD4(2),
+ CKSUMTYPE_RSA_MD4_DES(3),
+ CKSUMTYPE_DES_MAC(4),
+ CKSUMTYPE_DES_MAC_K(5),
+ CKSUMTYPE_RSA_MD4_DES_K(6),
+ CKSUMTYPE_RSA_MD5(7),
+ CKSUMTYPE_RSA_MD5_DES(8),
+ CKSUMTYPE_RSA_MD5_DES3(9),
+ CKSUMTYPE_SHA1_OTHER(10),
+ CKSUMTYPE_HMAC_SHA1_DES3(12),
+ CKSUMTYPE_SHA1(14),
+ CKSUMTYPE_HMAC_SHA1_96_AES_128(15),
+ CKSUMTYPE_HMAC_SHA1_96_AES_256(16),
+ CKSUMTYPE_GSSAPI(0x8003),
+ CKSUMTYPE_HMAC_MD5(-138), -- unofficial microsoft number
+ CKSUMTYPE_HMAC_MD5_ENC(-1138) -- even more unofficial
+}
+
+--enctypes
+ENCTYPE ::= INTEGER {
+ ETYPE_NULL(0),
+ ETYPE_DES_CBC_CRC(1),
+ ETYPE_DES_CBC_MD4(2),
+ ETYPE_DES_CBC_MD5(3),
+ ETYPE_DES3_CBC_MD5(5),
+ ETYPE_OLD_DES3_CBC_SHA1(7),
+ ETYPE_SIGN_DSA_GENERATE(8),
+ ETYPE_ENCRYPT_RSA_PRIV(9),
+ ETYPE_ENCRYPT_RSA_PUB(10),
+ ETYPE_DES3_CBC_SHA1(16), -- with key derivation
+ ETYPE_AES128_CTS_HMAC_SHA1_96(17),
+ ETYPE_AES256_CTS_HMAC_SHA1_96(18),
+ ETYPE_ARCFOUR_HMAC_MD5(23),
+ ETYPE_ARCFOUR_HMAC_MD5_56(24),
+ ETYPE_ENCTYPE_PK_CROSS(48),
+-- some "old" windows types
+ ETYPE_ARCFOUR_MD4(-128),
+ ETYPE_ARCFOUR_HMAC_OLD(-133),
+ ETYPE_ARCFOUR_HMAC_OLD_EXP(-135),
+-- these are for Heimdal internal use
+ ETYPE_DES_CBC_NONE(-0x1000),
+ ETYPE_DES3_CBC_NONE(-0x1001),
+ ETYPE_DES_CFB64_NONE(-0x1002),
+ ETYPE_DES_PCBC_NONE(-0x1003),
+ ETYPE_DIGEST_MD5_NONE(-0x1004), -- private use, lukeh@padl.com
+ ETYPE_CRAM_MD5_NONE(-0x1005) -- private use, lukeh@padl.com
+}
+
+
+
+
+-- this is sugar to make something ASN1 does not have: unsigned
+
+krb5uint32 ::= INTEGER (0..4294967295)
+krb5int32 ::= INTEGER (-2147483648..2147483647)
+
+KerberosString ::= GeneralString
+
+Realm ::= GeneralString
+PrincipalName ::= SEQUENCE {
+ name-type[0] NAME-TYPE,
+ name-string[1] SEQUENCE OF GeneralString
+}
+
+-- this is not part of RFC1510
+Principal ::= SEQUENCE {
+ name[0] PrincipalName,
+ realm[1] Realm
+}
+
+HostAddress ::= SEQUENCE {
+ addr-type[0] krb5int32,
+ address[1] OCTET STRING
+}
+
+-- This is from RFC1510.
+--
+-- HostAddresses ::= SEQUENCE OF SEQUENCE {
+-- addr-type[0] krb5int32,
+-- address[1] OCTET STRING
+-- }
+
+-- This seems much better.
+HostAddresses ::= SEQUENCE OF HostAddress
+
+
+KerberosTime ::= GeneralizedTime -- Specifying UTC time zone (Z)
+
+AuthorizationDataElement ::= SEQUENCE {
+ ad-type[0] krb5int32,
+ ad-data[1] OCTET STRING
+}
+
+AuthorizationData ::= SEQUENCE OF AuthorizationDataElement
+
+APOptions ::= BIT STRING {
+ reserved(0),
+ use-session-key(1),
+ mutual-required(2)
+}
+
+TicketFlags ::= BIT STRING {
+ reserved(0),
+ forwardable(1),
+ forwarded(2),
+ proxiable(3),
+ proxy(4),
+ may-postdate(5),
+ postdated(6),
+ invalid(7),
+ renewable(8),
+ initial(9),
+ pre-authent(10),
+ hw-authent(11),
+ transited-policy-checked(12),
+ ok-as-delegate(13),
+ anonymous(14)
+}
+
+KDCOptions ::= BIT STRING {
+ reserved(0),
+ forwardable(1),
+ forwarded(2),
+ proxiable(3),
+ proxy(4),
+ allow-postdate(5),
+ postdated(6),
+ unused7(7),
+ renewable(8),
+ unused9(9),
+ unused10(10),
+ unused11(11),
+ request-anonymous(14),
+ canonicalize(15),
+ constrained-delegation(16), -- ms extension
+ disable-transited-check(26),
+ renewable-ok(27),
+ enc-tkt-in-skey(28),
+ renew(30),
+ validate(31)
+}
+
+LR-TYPE ::= INTEGER {
+ LR_NONE(0), -- no information
+ LR_INITIAL_TGT(1), -- last initial TGT request
+ LR_INITIAL(2), -- last initial request
+ LR_ISSUE_USE_TGT(3), -- time of newest TGT used
+ LR_RENEWAL(4), -- time of last renewal
+ LR_REQUEST(5), -- time of last request (of any type)
+ LR_PW_EXPTIME(6), -- expiration time of password
+ LR_ACCT_EXPTIME(7) -- expiration time of account
+}
+
+LastReq ::= SEQUENCE OF SEQUENCE {
+ lr-type[0] LR-TYPE,
+ lr-value[1] KerberosTime
+}
+
+
+EncryptedData ::= SEQUENCE {
+ etype[0] ENCTYPE, -- EncryptionType
+ kvno[1] krb5int32 OPTIONAL,
+ cipher[2] OCTET STRING -- ciphertext
+}
+
+EncryptionKey ::= SEQUENCE {
+ keytype[0] krb5int32,
+ keyvalue[1] OCTET STRING
+}
+
+-- encoded Transited field
+TransitedEncoding ::= SEQUENCE {
+ tr-type[0] krb5int32, -- must be registered
+ contents[1] OCTET STRING
+}
+
+Ticket ::= [APPLICATION 1] SEQUENCE {
+ tkt-vno[0] krb5int32,
+ realm[1] Realm,
+ sname[2] PrincipalName,
+ enc-part[3] EncryptedData
+}
+-- Encrypted part of ticket
+EncTicketPart ::= [APPLICATION 3] SEQUENCE {
+ flags[0] TicketFlags,
+ key[1] EncryptionKey,
+ crealm[2] Realm,
+ cname[3] PrincipalName,
+ transited[4] TransitedEncoding,
+ authtime[5] KerberosTime,
+ starttime[6] KerberosTime OPTIONAL,
+ endtime[7] KerberosTime,
+ renew-till[8] KerberosTime OPTIONAL,
+ caddr[9] HostAddresses OPTIONAL,
+ authorization-data[10] AuthorizationData OPTIONAL
+}
+
+Checksum ::= SEQUENCE {
+ cksumtype[0] CKSUMTYPE,
+ checksum[1] OCTET STRING
+}
+
+Authenticator ::= [APPLICATION 2] SEQUENCE {
+ authenticator-vno[0] krb5int32,
+ crealm[1] Realm,
+ cname[2] PrincipalName,
+ cksum[3] Checksum OPTIONAL,
+ cusec[4] krb5int32,
+ ctime[5] KerberosTime,
+ subkey[6] EncryptionKey OPTIONAL,
+ seq-number[7] krb5uint32 OPTIONAL,
+ authorization-data[8] AuthorizationData OPTIONAL
+}
+
+PA-DATA ::= SEQUENCE {
+ -- might be encoded AP-REQ
+ padata-type[1] PADATA-TYPE,
+ padata-value[2] OCTET STRING
+}
+
+ETYPE-INFO-ENTRY ::= SEQUENCE {
+ etype[0] ENCTYPE,
+ salt[1] OCTET STRING OPTIONAL,
+ salttype[2] krb5int32 OPTIONAL
+}
+
+ETYPE-INFO ::= SEQUENCE OF ETYPE-INFO-ENTRY
+
+ETYPE-INFO2-ENTRY ::= SEQUENCE {
+ etype[0] ENCTYPE,
+ salt[1] KerberosString OPTIONAL,
+ s2kparams[2] OCTET STRING OPTIONAL
+}
+
+ETYPE-INFO2 ::= SEQUENCE SIZE (1..MAX) OF ETYPE-INFO2-ENTRY
+
+METHOD-DATA ::= SEQUENCE OF PA-DATA
+
+TypedData ::= SEQUENCE {
+ data-type[0] krb5int32,
+ data-value[1] OCTET STRING OPTIONAL
+}
+
+TYPED-DATA ::= SEQUENCE SIZE (1..MAX) OF TypedData
+
+KDC-REQ-BODY ::= SEQUENCE {
+ kdc-options[0] KDCOptions,
+ cname[1] PrincipalName OPTIONAL, -- Used only in AS-REQ
+ realm[2] Realm, -- Server's realm
+ -- Also client's in AS-REQ
+ sname[3] PrincipalName OPTIONAL,
+ from[4] KerberosTime OPTIONAL,
+ till[5] KerberosTime OPTIONAL,
+ rtime[6] KerberosTime OPTIONAL,
+ nonce[7] krb5int32,
+ etype[8] SEQUENCE OF ENCTYPE, -- EncryptionType,
+ -- in preference order
+ addresses[9] HostAddresses OPTIONAL,
+ enc-authorization-data[10] EncryptedData OPTIONAL,
+ -- Encrypted AuthorizationData encoding
+ additional-tickets[11] SEQUENCE OF Ticket OPTIONAL
+}
+
+KDC-REQ ::= SEQUENCE {
+ pvno[1] krb5int32,
+ msg-type[2] MESSAGE-TYPE,
+ padata[3] METHOD-DATA OPTIONAL,
+ req-body[4] KDC-REQ-BODY
+}
+
+AS-REQ ::= [APPLICATION 10] KDC-REQ
+TGS-REQ ::= [APPLICATION 12] KDC-REQ
+
+-- padata-type ::= PA-ENC-TIMESTAMP
+-- padata-value ::= EncryptedData - PA-ENC-TS-ENC
+
+PA-ENC-TS-ENC ::= SEQUENCE {
+ patimestamp[0] KerberosTime, -- client's time
+ pausec[1] krb5int32 OPTIONAL
+}
+
+-- draft-brezak-win2k-krb-authz-01
+PA-PAC-REQUEST ::= SEQUENCE {
+ include-pac[0] BOOLEAN -- Indicates whether a PAC
+ -- should be included or not
+}
+
+-- PacketCable provisioning server location, PKT-SP-SEC-I09-030728.pdf
+PROV-SRV-LOCATION ::= GeneralString
+
+KDC-REP ::= SEQUENCE {
+ pvno[0] krb5int32,
+ msg-type[1] MESSAGE-TYPE,
+ padata[2] METHOD-DATA OPTIONAL,
+ crealm[3] Realm,
+ cname[4] PrincipalName,
+ ticket[5] Ticket,
+ enc-part[6] EncryptedData
+}
+
+AS-REP ::= [APPLICATION 11] KDC-REP
+TGS-REP ::= [APPLICATION 13] KDC-REP
+
+EncKDCRepPart ::= SEQUENCE {
+ key[0] EncryptionKey,
+ last-req[1] LastReq,
+ nonce[2] krb5int32,
+ key-expiration[3] KerberosTime OPTIONAL,
+ flags[4] TicketFlags,
+ authtime[5] KerberosTime,
+ starttime[6] KerberosTime OPTIONAL,
+ endtime[7] KerberosTime,
+ renew-till[8] KerberosTime OPTIONAL,
+ srealm[9] Realm,
+ sname[10] PrincipalName,
+ caddr[11] HostAddresses OPTIONAL,
+ encrypted-pa-data[12] METHOD-DATA OPTIONAL
+}
+
+EncASRepPart ::= [APPLICATION 25] EncKDCRepPart
+EncTGSRepPart ::= [APPLICATION 26] EncKDCRepPart
+
+AP-REQ ::= [APPLICATION 14] SEQUENCE {
+ pvno[0] krb5int32,
+ msg-type[1] MESSAGE-TYPE,
+ ap-options[2] APOptions,
+ ticket[3] Ticket,
+ authenticator[4] EncryptedData
+}
+
+AP-REP ::= [APPLICATION 15] SEQUENCE {
+ pvno[0] krb5int32,
+ msg-type[1] MESSAGE-TYPE,
+ enc-part[2] EncryptedData
+}
+
+EncAPRepPart ::= [APPLICATION 27] SEQUENCE {
+ ctime[0] KerberosTime,
+ cusec[1] krb5int32,
+ subkey[2] EncryptionKey OPTIONAL,
+ seq-number[3] krb5uint32 OPTIONAL
+}
+
+KRB-SAFE-BODY ::= SEQUENCE {
+ user-data[0] OCTET STRING,
+ timestamp[1] KerberosTime OPTIONAL,
+ usec[2] krb5int32 OPTIONAL,
+ seq-number[3] krb5uint32 OPTIONAL,
+ s-address[4] HostAddress OPTIONAL,
+ r-address[5] HostAddress OPTIONAL
+}
+
+KRB-SAFE ::= [APPLICATION 20] SEQUENCE {
+ pvno[0] krb5int32,
+ msg-type[1] MESSAGE-TYPE,
+ safe-body[2] KRB-SAFE-BODY,
+ cksum[3] Checksum
+}
+
+KRB-PRIV ::= [APPLICATION 21] SEQUENCE {
+ pvno[0] krb5int32,
+ msg-type[1] MESSAGE-TYPE,
+ enc-part[3] EncryptedData
+}
+EncKrbPrivPart ::= [APPLICATION 28] SEQUENCE {
+ user-data[0] OCTET STRING,
+ timestamp[1] KerberosTime OPTIONAL,
+ usec[2] krb5int32 OPTIONAL,
+ seq-number[3] krb5uint32 OPTIONAL,
+ s-address[4] HostAddress OPTIONAL, -- sender's addr
+ r-address[5] HostAddress OPTIONAL -- recip's addr
+}
+
+KRB-CRED ::= [APPLICATION 22] SEQUENCE {
+ pvno[0] krb5int32,
+ msg-type[1] MESSAGE-TYPE, -- KRB_CRED
+ tickets[2] SEQUENCE OF Ticket,
+ enc-part[3] EncryptedData
+}
+
+KrbCredInfo ::= SEQUENCE {
+ key[0] EncryptionKey,
+ prealm[1] Realm OPTIONAL,
+ pname[2] PrincipalName OPTIONAL,
+ flags[3] TicketFlags OPTIONAL,
+ authtime[4] KerberosTime OPTIONAL,
+ starttime[5] KerberosTime OPTIONAL,
+ endtime[6] KerberosTime OPTIONAL,
+ renew-till[7] KerberosTime OPTIONAL,
+ srealm[8] Realm OPTIONAL,
+ sname[9] PrincipalName OPTIONAL,
+ caddr[10] HostAddresses OPTIONAL
+}
+
+EncKrbCredPart ::= [APPLICATION 29] SEQUENCE {
+ ticket-info[0] SEQUENCE OF KrbCredInfo,
+ nonce[1] krb5int32 OPTIONAL,
+ timestamp[2] KerberosTime OPTIONAL,
+ usec[3] krb5int32 OPTIONAL,
+ s-address[4] HostAddress OPTIONAL,
+ r-address[5] HostAddress OPTIONAL
+}
+
+KRB-ERROR ::= [APPLICATION 30] SEQUENCE {
+ pvno[0] krb5int32,
+ msg-type[1] MESSAGE-TYPE,
+ ctime[2] KerberosTime OPTIONAL,
+ cusec[3] krb5int32 OPTIONAL,
+ stime[4] KerberosTime,
+ susec[5] krb5int32,
+ error-code[6] krb5int32,
+ crealm[7] Realm OPTIONAL,
+ cname[8] PrincipalName OPTIONAL,
+ realm[9] Realm, -- Correct realm
+ sname[10] PrincipalName, -- Correct name
+ e-text[11] GeneralString OPTIONAL,
+ e-data[12] OCTET STRING OPTIONAL
+}
+
+ChangePasswdDataMS ::= SEQUENCE {
+ newpasswd[0] OCTET STRING,
+ targname[1] PrincipalName OPTIONAL,
+ targrealm[2] Realm OPTIONAL
+}
+
+EtypeList ::= SEQUENCE OF krb5int32
+ -- the client's proposed enctype list in
+ -- decreasing preference order, favorite choice first
+
+krb5-pvno krb5int32 ::= 5 -- current Kerberos protocol version number
+
+-- transited encodings
+
+DOMAIN-X500-COMPRESS krb5int32 ::= 1
+
+-- authorization data primitives
+
+AD-IF-RELEVANT ::= AuthorizationData
+
+AD-KDCIssued ::= SEQUENCE {
+ ad-checksum[0] Checksum,
+ i-realm[1] Realm OPTIONAL,
+ i-sname[2] PrincipalName OPTIONAL,
+ elements[3] AuthorizationData
+}
+
+AD-AND-OR ::= SEQUENCE {
+ condition-count[0] INTEGER,
+ elements[1] AuthorizationData
+}
+
+AD-MANDATORY-FOR-KDC ::= AuthorizationData
+
+-- PA-SAM-RESPONSE-2/PA-SAM-RESPONSE-2
+
+PA-SAM-TYPE ::= INTEGER {
+ PA_SAM_TYPE_ENIGMA(1), -- Enigma Logic
+ PA_SAM_TYPE_DIGI_PATH(2), -- Digital Pathways
+ PA_SAM_TYPE_SKEY_K0(3), -- S/key where KDC has key 0
+ PA_SAM_TYPE_SKEY(4), -- Traditional S/Key
+ PA_SAM_TYPE_SECURID(5), -- Security Dynamics
+ PA_SAM_TYPE_CRYPTOCARD(6) -- CRYPTOCard
+}
+
+PA-SAM-REDIRECT ::= HostAddresses
+
+SAMFlags ::= BIT STRING {
+ use-sad-as-key(0),
+ send-encrypted-sad(1),
+ must-pk-encrypt-sad(2)
+}
+
+PA-SAM-CHALLENGE-2-BODY ::= SEQUENCE {
+ sam-type[0] krb5int32,
+ sam-flags[1] SAMFlags,
+ sam-type-name[2] GeneralString OPTIONAL,
+ sam-track-id[3] GeneralString OPTIONAL,
+ sam-challenge-label[4] GeneralString OPTIONAL,
+ sam-challenge[5] GeneralString OPTIONAL,
+ sam-response-prompt[6] GeneralString OPTIONAL,
+ sam-pk-for-sad[7] EncryptionKey OPTIONAL,
+ sam-nonce[8] krb5int32,
+ sam-etype[9] krb5int32,
+ ...
+}
+
+PA-SAM-CHALLENGE-2 ::= SEQUENCE {
+ sam-body[0] PA-SAM-CHALLENGE-2-BODY,
+ sam-cksum[1] SEQUENCE OF Checksum, -- (1..MAX)
+ ...
+}
+
+PA-SAM-RESPONSE-2 ::= SEQUENCE {
+ sam-type[0] krb5int32,
+ sam-flags[1] SAMFlags,
+ sam-track-id[2] GeneralString OPTIONAL,
+ sam-enc-nonce-or-sad[3] EncryptedData, -- PA-ENC-SAM-RESPONSE-ENC
+ sam-nonce[4] krb5int32,
+ ...
+}
+
+PA-ENC-SAM-RESPONSE-ENC ::= SEQUENCE {
+ sam-nonce[0] krb5int32,
+ sam-sad[1] GeneralString OPTIONAL,
+ ...
+}
+
+PA-S4U2Self ::= SEQUENCE {
+ name[0] PrincipalName,
+ realm[1] Realm,
+ cksum[2] Checksum,
+ auth[3] GeneralString
+}
+
+KRB5SignedPathPrincipals ::= SEQUENCE OF Principal
+
+-- never encoded on the wire, just used to checksum over
+KRB5SignedPathData ::= SEQUENCE {
+ encticket[0] EncTicketPart,
+ delegated[1] KRB5SignedPathPrincipals OPTIONAL
+}
+
+KRB5SignedPath ::= SEQUENCE {
+ -- DERcoded KRB5SignedPathData
+ -- krbtgt key (etype), KeyUsage = XXX
+ etype[0] ENCTYPE,
+ cksum[1] Checksum,
+ -- srvs delegated though
+ delegated[2] KRB5SignedPathPrincipals OPTIONAL
+}
+
+PA-ClientCanonicalizedNames ::= SEQUENCE{
+ requested-name [0] PrincipalName,
+ mapped-name [1] PrincipalName
+}
+
+PA-ClientCanonicalized ::= SEQUENCE {
+ names [0] PA-ClientCanonicalizedNames,
+ canon-checksum [1] Checksum
+}
+
+AD-LoginAlias ::= SEQUENCE { -- ad-type number TBD --
+ login-alias [0] PrincipalName,
+ checksum [1] Checksum
+}
+
+-- old ms referral
+PA-SvrReferralData ::= SEQUENCE {
+ referred-name [1] PrincipalName OPTIONAL,
+ referred-realm [0] Realm
+}
+
+PA-SERVER-REFERRAL-DATA ::= EncryptedData
+
+PA-ServerReferralData ::= SEQUENCE {
+ referred-realm [0] Realm OPTIONAL,
+ true-principal-name [1] PrincipalName OPTIONAL,
+ requested-principal-name [2] PrincipalName OPTIONAL,
+ referral-valid-until [3] KerberosTime OPTIONAL,
+ ...
+}
+
+END
+
+-- etags -r '/\([A-Za-z][-A-Za-z0-9]*\).*::=/\1/' k5.asn1
diff --git a/source4/heimdal/lib/asn1/kx509.asn1 b/source4/heimdal/lib/asn1/kx509.asn1
new file mode 100644
index 0000000000..820abc8106
--- /dev/null
+++ b/source4/heimdal/lib/asn1/kx509.asn1
@@ -0,0 +1,20 @@
+-- $Id$
+
+KX509 DEFINITIONS ::=
+BEGIN
+
+Kx509Request ::= SEQUENCE {
+ authenticator OCTET STRING,
+ pk-hash OCTET STRING,
+ pk-key OCTET STRING
+}
+
+Kx509Response ::= SEQUENCE {
+ error-code[0] INTEGER (-2147483648..2147483647)
+ OPTIONAL -- DEFAULT 0 --,
+ hash[1] OCTET STRING OPTIONAL,
+ certificate[2] OCTET STRING OPTIONAL,
+ e-text[3] VisibleString OPTIONAL
+}
+
+END
diff --git a/source4/heimdal/lib/asn1/lex.c b/source4/heimdal/lib/asn1/lex.c
new file mode 100644
index 0000000000..5eaa156f7b
--- /dev/null
+++ b/source4/heimdal/lib/asn1/lex.c
@@ -0,0 +1,2709 @@
+#include "config.h"
+
+#line 3 "heimdal/lib/asn1/lex.c"
+
+#define YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+#define YY_FLEX_SUBMINOR_VERSION 34
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* First, we deal with platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types.
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t;
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+#endif /* ! C99 */
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX (4294967295U)
+#endif
+
+#endif /* ! FLEXINT_H */
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else /* ! __cplusplus */
+
+/* C99 requires __STDC__ to be defined as 1. */
+#if defined (__STDC__)
+
+#define YY_USE_CONST
+
+#endif /* defined (__STDC__) */
+#endif /* ! __cplusplus */
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index. If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* Enter a start condition. This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN (yy_start) = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state. The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START (((yy_start) - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE yyrestart(yyin )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#define YY_BUF_SIZE 16384
+#endif
+
+/* The state buf must be large enough to hold one state per character in the main buffer.
+ */
+#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+extern int yyleng;
+
+extern FILE *yyin, *yyout;
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+ #define YY_LESS_LINENO(n)
+
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ *yy_cp = (yy_hold_char); \
+ YY_RESTORE_YY_MORE_OFFSET \
+ (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+ YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+ } \
+ while ( 0 )
+
+#define unput(c) yyunput( c, (yytext_ptr) )
+
+/* The following is because we cannot portably get our hands on size_t
+ * (without autoconf's help, which isn't available because we want
+ * flex-generated scanners to compile on their own).
+ * Given that the standard has decreed that size_t exists since 1989,
+ * I guess we can afford to depend on it. Manoj.
+ */
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef size_t yy_size_t;
+#endif
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+ {
+ FILE *yy_input_file;
+
+ char *yy_ch_buf; /* input buffer */
+ char *yy_buf_pos; /* current position in input buffer */
+
+ /* Size of input buffer in bytes, not including room for EOB
+ * characters.
+ */
+ yy_size_t yy_buf_size;
+
+ /* Number of characters read into yy_ch_buf, not including EOB
+ * characters.
+ */
+ int yy_n_chars;
+
+ /* Whether we "own" the buffer - i.e., we know we created it,
+ * and can realloc() it to grow it, and should free() it to
+ * delete it.
+ */
+ int yy_is_our_buffer;
+
+ /* Whether this is an "interactive" input source; if so, and
+ * if we're using stdio for input, then we want to use getc()
+ * instead of fread(), to make sure we stop fetching input after
+ * each newline.
+ */
+ int yy_is_interactive;
+
+ /* Whether we're considered to be at the beginning of a line.
+ * If so, '^' rules will be active on the next match, otherwise
+ * not.
+ */
+ int yy_at_bol;
+
+ int yy_bs_lineno; /**< The line count. */
+ int yy_bs_column; /**< The column count. */
+
+ /* Whether to try to fill the input buffer when we reach the
+ * end of it.
+ */
+ int yy_fill_buffer;
+
+ int yy_buffer_status;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+ /* When an EOF's been seen but there's still some text to process
+ * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+ * shouldn't try reading from the input source any more. We might
+ * still have a bunch of tokens to match, though, because of
+ * possible backing-up.
+ *
+ * When we actually see the EOF, we change the status to "new"
+ * (via yyrestart()), so that the user can continue scanning by
+ * just pointing yyin at a new input file.
+ */
+#define YY_BUFFER_EOF_PENDING 2
+
+ };
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+/* Stack of input buffers. */
+static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */
+static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */
+static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \
+ ? (yy_buffer_stack)[(yy_buffer_stack_top)] \
+ : NULL)
+
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)]
+
+/* yy_hold_char holds the character lost when yytext is formed. */
+static char yy_hold_char;
+static int yy_n_chars; /* number of characters read into yy_ch_buf */
+int yyleng;
+
+/* Points to current character in buffer. */
+static char *yy_c_buf_p = (char *) 0;
+static int yy_init = 0; /* whether we need to initialize */
+static int yy_start = 0; /* start state number */
+
+/* Flag which is used to allow yywrap()'s to do buffer switches
+ * instead of setting up a fresh yyin. A bit of a hack ...
+ */
+static int yy_did_buffer_switch_on_eof;
+
+void yyrestart (FILE *input_file );
+void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer );
+YY_BUFFER_STATE yy_create_buffer (FILE *file,int size );
+void yy_delete_buffer (YY_BUFFER_STATE b );
+void yy_flush_buffer (YY_BUFFER_STATE b );
+void yypush_buffer_state (YY_BUFFER_STATE new_buffer );
+void yypop_buffer_state (void );
+
+static void yyensure_buffer_stack (void );
+static void yy_load_buffer_state (void );
+static void yy_init_buffer (YY_BUFFER_STATE b,FILE *file );
+
+#define YY_FLUSH_BUFFER yy_flush_buffer(YY_CURRENT_BUFFER )
+
+YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size );
+YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str );
+YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,int len );
+
+void *yyalloc (yy_size_t );
+void *yyrealloc (void *,yy_size_t );
+void yyfree (void * );
+
+#define yy_new_buffer yy_create_buffer
+
+#define yy_set_interactive(is_interactive) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){ \
+ yyensure_buffer_stack (); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ yy_create_buffer(yyin,YY_BUF_SIZE ); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+ }
+
+#define yy_set_bol(at_bol) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){\
+ yyensure_buffer_stack (); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ yy_create_buffer(yyin,YY_BUF_SIZE ); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+ }
+
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+/* Begin user sect3 */
+
+typedef unsigned char YY_CHAR;
+
+FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0;
+
+typedef int yy_state_type;
+
+extern int yylineno;
+
+int yylineno = 1;
+
+extern char *yytext;
+#define yytext_ptr yytext
+
+static yy_state_type yy_get_previous_state (void );
+static yy_state_type yy_try_NUL_trans (yy_state_type current_state );
+static int yy_get_next_buffer (void );
+static void yy_fatal_error (yyconst char msg[] );
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+ (yytext_ptr) = yy_bp; \
+ yyleng = (size_t) (yy_cp - yy_bp); \
+ (yy_hold_char) = *yy_cp; \
+ *yy_cp = '\0'; \
+ (yy_c_buf_p) = yy_cp;
+
+#define YY_NUM_RULES 95
+#define YY_END_OF_BUFFER 96
+/* This struct is not used in this scanner,
+ but its presence is necessary. */
+struct yy_trans_info
+ {
+ flex_int32_t yy_verify;
+ flex_int32_t yy_nxt;
+ };
+static yyconst flex_int16_t yy_accept[568] =
+ { 0,
+ 0, 0, 96, 94, 90, 91, 87, 81, 81, 94,
+ 94, 88, 88, 94, 89, 89, 89, 89, 89, 89,
+ 89, 89, 89, 89, 89, 89, 89, 89, 89, 89,
+ 89, 89, 89, 82, 83, 85, 88, 88, 93, 86,
+ 0, 0, 89, 89, 89, 89, 89, 89, 89, 89,
+ 89, 10, 89, 89, 89, 89, 89, 89, 89, 89,
+ 89, 89, 89, 89, 89, 89, 89, 89, 89, 89,
+ 89, 89, 89, 89, 51, 89, 89, 89, 89, 89,
+ 89, 89, 89, 89, 89, 89, 89, 89, 89, 89,
+ 89, 89, 89, 89, 89, 89, 89, 92, 88, 84,
+
+ 89, 3, 89, 89, 89, 7, 89, 89, 89, 89,
+ 89, 89, 89, 89, 89, 89, 22, 89, 89, 89,
+ 89, 89, 89, 89, 89, 89, 89, 89, 89, 89,
+ 89, 89, 44, 45, 89, 89, 89, 89, 89, 89,
+ 89, 55, 89, 89, 89, 89, 89, 89, 89, 63,
+ 89, 89, 89, 89, 89, 89, 89, 89, 89, 89,
+ 89, 89, 89, 89, 89, 89, 89, 89, 89, 89,
+ 89, 89, 89, 89, 89, 89, 89, 89, 89, 89,
+ 89, 89, 89, 89, 89, 89, 89, 89, 30, 89,
+ 89, 89, 89, 89, 89, 89, 89, 89, 89, 89,
+
+ 47, 89, 89, 89, 89, 89, 89, 89, 89, 89,
+ 89, 60, 89, 89, 64, 89, 89, 89, 68, 69,
+ 89, 89, 89, 89, 89, 89, 89, 89, 89, 89,
+ 80, 89, 89, 89, 89, 6, 89, 89, 89, 89,
+ 13, 89, 89, 89, 89, 89, 89, 89, 89, 89,
+ 89, 89, 89, 89, 29, 89, 89, 89, 89, 89,
+ 89, 89, 89, 89, 89, 89, 89, 89, 89, 50,
+ 89, 89, 89, 89, 89, 89, 89, 89, 89, 89,
+ 89, 89, 89, 89, 72, 89, 89, 89, 89, 89,
+ 89, 89, 1, 89, 89, 89, 89, 89, 89, 12,
+
+ 89, 89, 89, 89, 89, 89, 89, 89, 24, 89,
+ 89, 89, 89, 89, 89, 89, 89, 89, 89, 89,
+ 89, 89, 89, 89, 89, 89, 89, 49, 89, 89,
+ 89, 89, 89, 89, 89, 89, 89, 65, 66, 89,
+ 89, 89, 73, 89, 89, 89, 89, 89, 89, 89,
+ 89, 89, 89, 9, 89, 89, 89, 89, 18, 89,
+ 89, 21, 89, 89, 26, 89, 89, 89, 89, 89,
+ 89, 89, 37, 38, 89, 89, 41, 89, 89, 89,
+ 89, 89, 89, 54, 89, 57, 58, 89, 89, 89,
+ 89, 89, 89, 89, 75, 89, 89, 89, 89, 89,
+
+ 89, 89, 89, 89, 89, 89, 89, 89, 20, 89,
+ 25, 89, 28, 89, 89, 89, 89, 89, 36, 39,
+ 40, 89, 89, 89, 89, 52, 89, 89, 89, 89,
+ 62, 89, 89, 89, 89, 89, 89, 89, 89, 89,
+ 89, 5, 8, 11, 14, 89, 89, 89, 89, 89,
+ 89, 89, 89, 34, 89, 89, 89, 89, 89, 89,
+ 89, 89, 89, 67, 89, 89, 74, 89, 89, 89,
+ 89, 89, 89, 15, 89, 17, 89, 23, 89, 89,
+ 89, 89, 35, 89, 89, 89, 89, 89, 89, 89,
+ 89, 89, 89, 76, 89, 89, 89, 89, 4, 16,
+
+ 19, 89, 89, 89, 89, 89, 89, 89, 89, 89,
+ 89, 89, 89, 89, 89, 89, 89, 89, 89, 89,
+ 89, 89, 89, 42, 43, 89, 89, 89, 89, 89,
+ 61, 89, 89, 89, 89, 89, 89, 27, 31, 89,
+ 33, 89, 48, 89, 56, 89, 89, 71, 89, 89,
+ 79, 89, 89, 46, 89, 89, 89, 89, 78, 2,
+ 32, 89, 59, 70, 77, 53, 0
+ } ;
+
+static yyconst flex_int32_t yy_ec[256] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 2, 3,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 2, 1, 4, 1, 1, 1, 1, 1, 5,
+ 5, 6, 1, 5, 7, 8, 9, 10, 11, 12,
+ 12, 13, 14, 15, 12, 16, 12, 17, 5, 1,
+ 18, 1, 1, 1, 19, 20, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
+ 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
+ 45, 1, 46, 1, 47, 1, 48, 49, 50, 51,
+
+ 52, 53, 54, 55, 56, 57, 29, 58, 59, 60,
+ 61, 62, 29, 63, 64, 65, 66, 67, 29, 68,
+ 29, 69, 5, 5, 5, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1
+ } ;
+
+static yyconst flex_int32_t yy_meta[70] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 2, 1, 1, 3,
+ 3, 3, 3, 3, 3, 3, 1, 1, 3, 3,
+ 3, 3, 3, 3, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 1, 1, 2, 3, 3, 3,
+ 3, 3, 3, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2
+ } ;
+
+static yyconst flex_int16_t yy_base[570] =
+ { 0,
+ 0, 0, 636, 637, 637, 637, 637, 637, 63, 627,
+ 628, 70, 77, 616, 74, 72, 76, 609, 65, 81,
+ 49, 0, 92, 91, 32, 101, 97, 608, 103, 113,
+ 99, 574, 602, 637, 637, 637, 156, 163, 620, 637,
+ 0, 609, 0, 589, 595, 590, 585, 597, 583, 586,
+ 586, 0, 101, 599, 108, 593, 596, 122, 124, 585,
+ 581, 553, 564, 597, 587, 575, 115, 575, 565, 574,
+ 575, 545, 575, 564, 0, 563, 543, 561, 558, 558,
+ 124, 540, 161, 119, 551, 558, 561, 581, 566, 551,
+ 555, 530, 560, 160, 530, 91, 547, 637, 0, 637,
+
+ 125, 0, 554, 550, 555, 0, 544, 550, 543, 551,
+ 540, 542, 145, 166, 552, 541, 0, 542, 549, 156,
+ 548, 533, 538, 516, 505, 529, 533, 157, 534, 525,
+ 539, 546, 0, 521, 529, 506, 534, 533, 528, 502,
+ 515, 0, 515, 514, 510, 489, 518, 528, 507, 0,
+ 522, 517, 505, 505, 504, 517, 516, 486, 159, 499,
+ 520, 468, 482, 477, 506, 499, 494, 502, 497, 495,
+ 461, 502, 505, 502, 485, 488, 482, 500, 479, 485,
+ 494, 493, 491, 479, 485, 475, 164, 487, 0, 446,
+ 453, 442, 468, 478, 468, 464, 483, 170, 488, 463,
+
+ 0, 436, 477, 459, 463, 445, 471, 486, 469, 472,
+ 425, 0, 451, 465, 0, 455, 467, 420, 0, 0,
+ 477, 418, 450, 442, 457, 423, 441, 425, 415, 426,
+ 0, 436, 454, 451, 452, 0, 407, 450, 447, 444,
+ 0, 434, 429, 437, 433, 435, 439, 437, 423, 420,
+ 436, 418, 418, 422, 0, 405, 396, 388, 423, 180,
+ 411, 426, 415, 423, 408, 429, 436, 386, 403, 0,
+ 408, 374, 402, 410, 404, 397, 386, 406, 400, 406,
+ 388, 366, 401, 375, 0, 403, 389, 365, 358, 359,
+ 356, 362, 0, 398, 399, 379, 360, 383, 376, 0,
+
+ 390, 393, 379, 372, 371, 385, 385, 387, 0, 378,
+ 367, 376, 383, 343, 350, 343, 374, 370, 374, 358,
+ 371, 372, 356, 368, 353, 362, 338, 0, 368, 364,
+ 353, 352, 345, 359, 332, 340, 358, 0, 0, 322,
+ 355, 308, 0, 338, 322, 310, 308, 319, 318, 331,
+ 330, 340, 306, 0, 342, 332, 336, 335, 0, 334,
+ 338, 0, 321, 320, 0, 337, 326, 151, 318, 294,
+ 326, 314, 0, 0, 314, 327, 0, 328, 283, 315,
+ 309, 315, 292, 0, 319, 0, 0, 284, 318, 317,
+ 279, 315, 300, 317, 0, 279, 286, 265, 295, 324,
+
+ 303, 308, 274, 291, 288, 293, 292, 290, 0, 299,
+ 0, 294, 0, 255, 250, 253, 263, 293, 0, 0,
+ 0, 277, 251, 289, 247, 0, 247, 283, 257, 261,
+ 0, 253, 274, 240, 274, 243, 244, 264, 235, 262,
+ 265, 0, 0, 0, 260, 273, 270, 262, 271, 262,
+ 228, 238, 226, 0, 252, 260, 230, 258, 221, 233,
+ 250, 244, 247, 0, 241, 215, 0, 223, 239, 210,
+ 211, 230, 240, 0, 249, 0, 233, 0, 242, 212,
+ 216, 210, 0, 232, 204, 231, 206, 198, 233, 194,
+ 231, 230, 200, 0, 190, 191, 197, 220, 0, 0,
+
+ 0, 213, 190, 211, 188, 215, 192, 218, 184, 187,
+ 204, 178, 218, 215, 178, 174, 180, 175, 196, 190,
+ 178, 175, 176, 0, 0, 191, 174, 165, 180, 166,
+ 0, 194, 166, 163, 158, 163, 197, 0, 0, 156,
+ 0, 171, 0, 148, 0, 152, 188, 0, 150, 155,
+ 0, 166, 153, 0, 143, 148, 162, 143, 0, 0,
+ 0, 101, 0, 0, 0, 0, 637, 223, 69
+ } ;
+
+static yyconst flex_int16_t yy_def[570] =
+ { 0,
+ 567, 1, 567, 567, 567, 567, 567, 567, 567, 567,
+ 567, 567, 567, 567, 568, 568, 568, 568, 568, 568,
+ 568, 568, 568, 568, 568, 568, 568, 568, 568, 568,
+ 568, 568, 568, 567, 567, 567, 567, 567, 567, 567,
+ 569, 567, 568, 568, 568, 568, 568, 568, 568, 568,
+ 568, 568, 568, 568, 568, 568, 568, 568, 568, 568,
+ 568, 568, 568, 568, 568, 568, 568, 568, 568, 568,
+ 568, 568, 568, 568, 568, 568, 568, 568, 568, 568,
+ 568, 568, 568, 568, 568, 568, 568, 568, 568, 568,
+ 568, 568, 568, 568, 568, 568, 568, 567, 569, 567,
+
+ 568, 568, 568, 568, 568, 568, 568, 568, 568, 568,
+ 568, 568, 568, 568, 568, 568, 568, 568, 568, 568,
+ 568, 568, 568, 568, 568, 568, 568, 568, 568, 568,
+ 568, 568, 568, 568, 568, 568, 568, 568, 568, 568,
+ 568, 568, 568, 568, 568, 568, 568, 568, 568, 568,
+ 568, 568, 568, 568, 568, 568, 568, 568, 568, 568,
+ 568, 568, 568, 568, 568, 568, 568, 568, 568, 568,
+ 568, 568, 568, 568, 568, 568, 568, 568, 568, 568,
+ 568, 568, 568, 568, 568, 568, 568, 568, 568, 568,
+ 568, 568, 568, 568, 568, 568, 568, 568, 568, 568,
+
+ 568, 568, 568, 568, 568, 568, 568, 568, 568, 568,
+ 568, 568, 568, 568, 568, 568, 568, 568, 568, 568,
+ 568, 568, 568, 568, 568, 568, 568, 568, 568, 568,
+ 568, 568, 568, 568, 568, 568, 568, 568, 568, 568,
+ 568, 568, 568, 568, 568, 568, 568, 568, 568, 568,
+ 568, 568, 568, 568, 568, 568, 568, 568, 568, 568,
+ 568, 568, 568, 568, 568, 568, 568, 568, 568, 568,
+ 568, 568, 568, 568, 568, 568, 568, 568, 568, 568,
+ 568, 568, 568, 568, 568, 568, 568, 568, 568, 568,
+ 568, 568, 568, 568, 568, 568, 568, 568, 568, 568,
+
+ 568, 568, 568, 568, 568, 568, 568, 568, 568, 568,
+ 568, 568, 568, 568, 568, 568, 568, 568, 568, 568,
+ 568, 568, 568, 568, 568, 568, 568, 568, 568, 568,
+ 568, 568, 568, 568, 568, 568, 568, 568, 568, 568,
+ 568, 568, 568, 568, 568, 568, 568, 568, 568, 568,
+ 568, 568, 568, 568, 568, 568, 568, 568, 568, 568,
+ 568, 568, 568, 568, 568, 568, 568, 568, 568, 568,
+ 568, 568, 568, 568, 568, 568, 568, 568, 568, 568,
+ 568, 568, 568, 568, 568, 568, 568, 568, 568, 568,
+ 568, 568, 568, 568, 568, 568, 568, 568, 568, 568,
+
+ 568, 568, 568, 568, 568, 568, 568, 568, 568, 568,
+ 568, 568, 568, 568, 568, 568, 568, 568, 568, 568,
+ 568, 568, 568, 568, 568, 568, 568, 568, 568, 568,
+ 568, 568, 568, 568, 568, 568, 568, 568, 568, 568,
+ 568, 568, 568, 568, 568, 568, 568, 568, 568, 568,
+ 568, 568, 568, 568, 568, 568, 568, 568, 568, 568,
+ 568, 568, 568, 568, 568, 568, 568, 568, 568, 568,
+ 568, 568, 568, 568, 568, 568, 568, 568, 568, 568,
+ 568, 568, 568, 568, 568, 568, 568, 568, 568, 568,
+ 568, 568, 568, 568, 568, 568, 568, 568, 568, 568,
+
+ 568, 568, 568, 568, 568, 568, 568, 568, 568, 568,
+ 568, 568, 568, 568, 568, 568, 568, 568, 568, 568,
+ 568, 568, 568, 568, 568, 568, 568, 568, 568, 568,
+ 568, 568, 568, 568, 568, 568, 568, 568, 568, 568,
+ 568, 568, 568, 568, 568, 568, 568, 568, 568, 568,
+ 568, 568, 568, 568, 568, 568, 568, 568, 568, 568,
+ 568, 568, 568, 568, 568, 568, 0, 567, 567
+ } ;
+
+static yyconst flex_int16_t yy_nxt[707] =
+ { 0,
+ 4, 5, 6, 7, 8, 4, 9, 10, 11, 12,
+ 13, 13, 13, 13, 13, 13, 14, 4, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 22, 22, 22,
+ 24, 25, 26, 27, 22, 28, 29, 30, 31, 32,
+ 33, 22, 22, 22, 34, 35, 4, 22, 22, 22,
+ 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
+ 22, 22, 22, 22, 22, 22, 22, 22, 22, 36,
+ 71, 99, 37, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 44, 48, 57, 58, 72, 49, 60,
+
+ 62, 53, 50, 45, 51, 54, 59, 46, 55, 69,
+ 64, 63, 47, 65, 52, 78, 61, 70, 79, 109,
+ 73, 74, 66, 67, 75, 84, 80, 88, 68, 85,
+ 93, 89, 81, 110, 76, 129, 94, 41, 112, 113,
+ 86, 163, 116, 117, 119, 87, 144, 166, 90, 77,
+ 145, 130, 131, 149, 164, 91, 150, 120, 95, 82,
+ 118, 121, 167, 566, 92, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38, 38, 147,
+ 160, 177, 178, 161, 179, 185, 194, 414, 186, 195,
+ 148, 223, 180, 224, 264, 253, 565, 564, 225, 254,
+
+ 318, 563, 319, 562, 561, 265, 415, 560, 559, 558,
+ 557, 556, 555, 554, 553, 552, 551, 550, 549, 548,
+ 547, 546, 545, 41, 43, 43, 544, 543, 542, 541,
+ 540, 539, 538, 537, 536, 535, 534, 533, 532, 531,
+ 530, 529, 528, 527, 526, 525, 524, 523, 522, 521,
+ 520, 519, 518, 517, 516, 515, 514, 513, 512, 511,
+ 510, 509, 508, 507, 506, 505, 504, 503, 502, 501,
+ 500, 499, 498, 497, 496, 495, 494, 493, 492, 491,
+ 490, 489, 488, 487, 486, 485, 484, 483, 482, 481,
+ 480, 479, 478, 477, 476, 475, 474, 473, 472, 471,
+
+ 470, 469, 468, 467, 466, 465, 464, 463, 462, 461,
+ 460, 459, 458, 457, 456, 455, 454, 453, 452, 451,
+ 450, 449, 448, 447, 446, 445, 444, 443, 442, 441,
+ 440, 439, 438, 437, 436, 435, 434, 433, 432, 431,
+ 430, 429, 428, 427, 426, 425, 424, 423, 422, 421,
+ 420, 419, 418, 417, 416, 413, 412, 411, 410, 409,
+ 408, 407, 406, 405, 404, 403, 402, 401, 400, 399,
+ 398, 397, 396, 395, 394, 393, 392, 391, 390, 389,
+ 388, 387, 386, 385, 384, 383, 382, 381, 380, 379,
+ 378, 377, 376, 375, 374, 373, 372, 371, 370, 369,
+
+ 368, 367, 366, 365, 364, 363, 362, 361, 360, 359,
+ 358, 357, 356, 355, 354, 353, 352, 351, 350, 349,
+ 348, 347, 346, 345, 344, 343, 342, 341, 340, 339,
+ 338, 337, 336, 335, 334, 333, 332, 331, 330, 329,
+ 328, 327, 326, 325, 324, 323, 322, 321, 320, 317,
+ 316, 315, 314, 313, 312, 311, 310, 309, 308, 307,
+ 306, 305, 304, 303, 302, 301, 300, 299, 298, 297,
+ 296, 295, 294, 293, 292, 291, 290, 289, 288, 287,
+ 286, 285, 284, 283, 282, 281, 280, 279, 278, 277,
+ 276, 275, 274, 273, 272, 271, 270, 269, 268, 267,
+
+ 266, 263, 262, 261, 260, 259, 258, 257, 256, 255,
+ 252, 251, 250, 249, 248, 247, 246, 245, 244, 243,
+ 242, 241, 240, 239, 238, 237, 236, 235, 234, 233,
+ 232, 231, 230, 229, 228, 227, 226, 222, 221, 220,
+ 219, 218, 217, 216, 215, 214, 213, 212, 211, 210,
+ 209, 208, 207, 206, 205, 204, 203, 202, 201, 200,
+ 199, 198, 197, 196, 193, 192, 191, 190, 189, 188,
+ 187, 184, 183, 182, 181, 176, 175, 174, 173, 172,
+ 171, 170, 169, 168, 165, 162, 159, 158, 157, 156,
+ 155, 154, 153, 152, 151, 146, 143, 142, 141, 140,
+
+ 139, 138, 137, 136, 135, 134, 133, 132, 128, 127,
+ 126, 125, 124, 123, 122, 115, 114, 111, 108, 107,
+ 106, 105, 104, 103, 102, 101, 100, 98, 97, 96,
+ 83, 56, 42, 40, 39, 567, 3, 567, 567, 567,
+ 567, 567, 567, 567, 567, 567, 567, 567, 567, 567,
+ 567, 567, 567, 567, 567, 567, 567, 567, 567, 567,
+ 567, 567, 567, 567, 567, 567, 567, 567, 567, 567,
+ 567, 567, 567, 567, 567, 567, 567, 567, 567, 567,
+ 567, 567, 567, 567, 567, 567, 567, 567, 567, 567,
+ 567, 567, 567, 567, 567, 567, 567, 567, 567, 567,
+
+ 567, 567, 567, 567, 567, 567
+ } ;
+
+static yyconst flex_int16_t yy_chk[707] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 9,
+ 25, 569, 9, 9, 9, 9, 9, 9, 9, 12,
+ 12, 12, 12, 12, 12, 12, 13, 13, 13, 13,
+ 13, 13, 13, 15, 16, 19, 19, 25, 16, 20,
+
+ 21, 17, 16, 15, 16, 17, 19, 15, 17, 24,
+ 23, 21, 15, 23, 16, 27, 20, 24, 27, 53,
+ 26, 26, 23, 23, 26, 29, 27, 30, 23, 29,
+ 31, 30, 27, 53, 26, 67, 31, 12, 55, 55,
+ 29, 96, 58, 58, 59, 29, 81, 101, 30, 26,
+ 81, 67, 67, 84, 96, 30, 84, 59, 31, 27,
+ 58, 59, 101, 562, 30, 37, 37, 37, 37, 37,
+ 37, 37, 38, 38, 38, 38, 38, 38, 38, 83,
+ 94, 113, 113, 94, 114, 120, 128, 368, 120, 128,
+ 83, 159, 114, 159, 198, 187, 558, 557, 159, 187,
+
+ 260, 556, 260, 555, 553, 198, 368, 552, 550, 549,
+ 547, 546, 544, 542, 540, 537, 536, 535, 534, 533,
+ 532, 530, 529, 37, 568, 568, 528, 527, 526, 523,
+ 522, 521, 520, 519, 518, 517, 516, 515, 514, 513,
+ 512, 511, 510, 509, 508, 507, 506, 505, 504, 503,
+ 502, 498, 497, 496, 495, 493, 492, 491, 490, 489,
+ 488, 487, 486, 485, 484, 482, 481, 480, 479, 477,
+ 475, 473, 472, 471, 470, 469, 468, 466, 465, 463,
+ 462, 461, 460, 459, 458, 457, 456, 455, 453, 452,
+ 451, 450, 449, 448, 447, 446, 445, 441, 440, 439,
+
+ 438, 437, 436, 435, 434, 433, 432, 430, 429, 428,
+ 427, 425, 424, 423, 422, 418, 417, 416, 415, 414,
+ 412, 410, 408, 407, 406, 405, 404, 403, 402, 401,
+ 400, 399, 398, 397, 396, 394, 393, 392, 391, 390,
+ 389, 388, 385, 383, 382, 381, 380, 379, 378, 376,
+ 375, 372, 371, 370, 369, 367, 366, 364, 363, 361,
+ 360, 358, 357, 356, 355, 353, 352, 351, 350, 349,
+ 348, 347, 346, 345, 344, 342, 341, 340, 337, 336,
+ 335, 334, 333, 332, 331, 330, 329, 327, 326, 325,
+ 324, 323, 322, 321, 320, 319, 318, 317, 316, 315,
+
+ 314, 313, 312, 311, 310, 308, 307, 306, 305, 304,
+ 303, 302, 301, 299, 298, 297, 296, 295, 294, 292,
+ 291, 290, 289, 288, 287, 286, 284, 283, 282, 281,
+ 280, 279, 278, 277, 276, 275, 274, 273, 272, 271,
+ 269, 268, 267, 266, 265, 264, 263, 262, 261, 259,
+ 258, 257, 256, 254, 253, 252, 251, 250, 249, 248,
+ 247, 246, 245, 244, 243, 242, 240, 239, 238, 237,
+ 235, 234, 233, 232, 230, 229, 228, 227, 226, 225,
+ 224, 223, 222, 221, 218, 217, 216, 214, 213, 211,
+ 210, 209, 208, 207, 206, 205, 204, 203, 202, 200,
+
+ 199, 197, 196, 195, 194, 193, 192, 191, 190, 188,
+ 186, 185, 184, 183, 182, 181, 180, 179, 178, 177,
+ 176, 175, 174, 173, 172, 171, 170, 169, 168, 167,
+ 166, 165, 164, 163, 162, 161, 160, 158, 157, 156,
+ 155, 154, 153, 152, 151, 149, 148, 147, 146, 145,
+ 144, 143, 141, 140, 139, 138, 137, 136, 135, 134,
+ 132, 131, 130, 129, 127, 126, 125, 124, 123, 122,
+ 121, 119, 118, 116, 115, 112, 111, 110, 109, 108,
+ 107, 105, 104, 103, 97, 95, 93, 92, 91, 90,
+ 89, 88, 87, 86, 85, 82, 80, 79, 78, 77,
+
+ 76, 74, 73, 72, 71, 70, 69, 68, 66, 65,
+ 64, 63, 62, 61, 60, 57, 56, 54, 51, 50,
+ 49, 48, 47, 46, 45, 44, 42, 39, 33, 32,
+ 28, 18, 14, 11, 10, 3, 567, 567, 567, 567,
+ 567, 567, 567, 567, 567, 567, 567, 567, 567, 567,
+ 567, 567, 567, 567, 567, 567, 567, 567, 567, 567,
+ 567, 567, 567, 567, 567, 567, 567, 567, 567, 567,
+ 567, 567, 567, 567, 567, 567, 567, 567, 567, 567,
+ 567, 567, 567, 567, 567, 567, 567, 567, 567, 567,
+ 567, 567, 567, 567, 567, 567, 567, 567, 567, 567,
+
+ 567, 567, 567, 567, 567, 567
+ } ;
+
+static yy_state_type yy_last_accepting_state;
+static char *yy_last_accepting_cpos;
+
+extern int yy_flex_debug;
+int yy_flex_debug = 0;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+char *yytext;
+#line 1 "lex.l"
+#line 2 "lex.l"
+/*
+ * Copyright (c) 1997 - 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#undef ECHO
+#include "symbol.h"
+#include "parse.h"
+#include "lex.h"
+#include "gen_locl.h"
+
+static unsigned lineno = 1;
+
+#undef ECHO
+
+static void unterminated(const char *, unsigned);
+
+/* This is for broken old lexes (solaris 10 and hpux) */
+#line 858 "heimdal/lib/asn1/lex.c"
+
+#define INITIAL 0
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+#include <unistd.h>
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+static int yy_init_globals (void );
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int yywrap (void );
+#else
+extern int yywrap (void );
+#endif
+#endif
+
+ static void yyunput (int c,char *buf_ptr );
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char *,yyconst char *,int );
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * );
+#endif
+
+#ifndef YY_NO_INPUT
+
+#ifdef __cplusplus
+static int yyinput (void );
+#else
+static int input (void );
+#endif
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO fwrite( yytext, yyleng, 1, yyout )
+#endif
+
+/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
+ { \
+ int c = '*'; \
+ int n; \
+ for ( n = 0; n < max_size && \
+ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
+ buf[n] = (char) c; \
+ if ( c == '\n' ) \
+ buf[n++] = (char) c; \
+ if ( c == EOF && ferror( yyin ) ) \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ result = n; \
+ } \
+ else \
+ { \
+ errno=0; \
+ while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \
+ { \
+ if( errno != EINTR) \
+ { \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ break; \
+ } \
+ errno=0; \
+ clearerr(yyin); \
+ } \
+ }\
+\
+
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg )
+#endif
+
+/* end tables serialization structures and prototypes */
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int yylex (void);
+
+#define YY_DECL int yylex (void)
+#endif /* !YY_DECL */
+
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+ YY_USER_ACTION
+
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp, *yy_bp;
+ register int yy_act;
+
+#line 68 "lex.l"
+
+#line 1013 "heimdal/lib/asn1/lex.c"
+
+ if ( !(yy_init) )
+ {
+ (yy_init) = 1;
+
+#ifdef YY_USER_INIT
+ YY_USER_INIT;
+#endif
+
+ if ( ! (yy_start) )
+ (yy_start) = 1; /* first start state */
+
+ if ( ! yyin )
+ yyin = stdin;
+
+ if ( ! yyout )
+ yyout = stdout;
+
+ if ( ! YY_CURRENT_BUFFER ) {
+ yyensure_buffer_stack ();
+ YY_CURRENT_BUFFER_LVALUE =
+ yy_create_buffer(yyin,YY_BUF_SIZE );
+ }
+
+ yy_load_buffer_state( );
+ }
+
+ while ( 1 ) /* loops until end-of-file is reached */
+ {
+ yy_cp = (yy_c_buf_p);
+
+ /* Support of yytext. */
+ *yy_cp = (yy_hold_char);
+
+ /* yy_bp points to the position in yy_ch_buf of the start of
+ * the current run.
+ */
+ yy_bp = yy_cp;
+
+ yy_current_state = (yy_start);
+yy_match:
+ do
+ {
+ register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
+ if ( yy_accept[yy_current_state] )
+ {
+ (yy_last_accepting_state) = yy_current_state;
+ (yy_last_accepting_cpos) = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 568 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ ++yy_cp;
+ }
+ while ( yy_base[yy_current_state] != 637 );
+
+yy_find_action:
+ yy_act = yy_accept[yy_current_state];
+ if ( yy_act == 0 )
+ { /* have to back up */
+ yy_cp = (yy_last_accepting_cpos);
+ yy_current_state = (yy_last_accepting_state);
+ yy_act = yy_accept[yy_current_state];
+ }
+
+ YY_DO_BEFORE_ACTION;
+
+do_action: /* This label is used only to access EOF actions. */
+
+ switch ( yy_act )
+ { /* beginning of action switch */
+ case 0: /* must back up */
+ /* undo the effects of YY_DO_BEFORE_ACTION */
+ *yy_cp = (yy_hold_char);
+ yy_cp = (yy_last_accepting_cpos);
+ yy_current_state = (yy_last_accepting_state);
+ goto yy_find_action;
+
+case 1:
+YY_RULE_SETUP
+#line 69 "lex.l"
+{ return kw_ABSENT; }
+ YY_BREAK
+case 2:
+YY_RULE_SETUP
+#line 70 "lex.l"
+{ return kw_ABSTRACT_SYNTAX; }
+ YY_BREAK
+case 3:
+YY_RULE_SETUP
+#line 71 "lex.l"
+{ return kw_ALL; }
+ YY_BREAK
+case 4:
+YY_RULE_SETUP
+#line 72 "lex.l"
+{ return kw_APPLICATION; }
+ YY_BREAK
+case 5:
+YY_RULE_SETUP
+#line 73 "lex.l"
+{ return kw_AUTOMATIC; }
+ YY_BREAK
+case 6:
+YY_RULE_SETUP
+#line 74 "lex.l"
+{ return kw_BEGIN; }
+ YY_BREAK
+case 7:
+YY_RULE_SETUP
+#line 75 "lex.l"
+{ return kw_BIT; }
+ YY_BREAK
+case 8:
+YY_RULE_SETUP
+#line 76 "lex.l"
+{ return kw_BMPString; }
+ YY_BREAK
+case 9:
+YY_RULE_SETUP
+#line 77 "lex.l"
+{ return kw_BOOLEAN; }
+ YY_BREAK
+case 10:
+YY_RULE_SETUP
+#line 78 "lex.l"
+{ return kw_BY; }
+ YY_BREAK
+case 11:
+YY_RULE_SETUP
+#line 79 "lex.l"
+{ return kw_CHARACTER; }
+ YY_BREAK
+case 12:
+YY_RULE_SETUP
+#line 80 "lex.l"
+{ return kw_CHOICE; }
+ YY_BREAK
+case 13:
+YY_RULE_SETUP
+#line 81 "lex.l"
+{ return kw_CLASS; }
+ YY_BREAK
+case 14:
+YY_RULE_SETUP
+#line 82 "lex.l"
+{ return kw_COMPONENT; }
+ YY_BREAK
+case 15:
+YY_RULE_SETUP
+#line 83 "lex.l"
+{ return kw_COMPONENTS; }
+ YY_BREAK
+case 16:
+YY_RULE_SETUP
+#line 84 "lex.l"
+{ return kw_CONSTRAINED; }
+ YY_BREAK
+case 17:
+YY_RULE_SETUP
+#line 85 "lex.l"
+{ return kw_CONTAINING; }
+ YY_BREAK
+case 18:
+YY_RULE_SETUP
+#line 86 "lex.l"
+{ return kw_DEFAULT; }
+ YY_BREAK
+case 19:
+YY_RULE_SETUP
+#line 87 "lex.l"
+{ return kw_DEFINITIONS; }
+ YY_BREAK
+case 20:
+YY_RULE_SETUP
+#line 88 "lex.l"
+{ return kw_EMBEDDED; }
+ YY_BREAK
+case 21:
+YY_RULE_SETUP
+#line 89 "lex.l"
+{ return kw_ENCODED; }
+ YY_BREAK
+case 22:
+YY_RULE_SETUP
+#line 90 "lex.l"
+{ return kw_END; }
+ YY_BREAK
+case 23:
+YY_RULE_SETUP
+#line 91 "lex.l"
+{ return kw_ENUMERATED; }
+ YY_BREAK
+case 24:
+YY_RULE_SETUP
+#line 92 "lex.l"
+{ return kw_EXCEPT; }
+ YY_BREAK
+case 25:
+YY_RULE_SETUP
+#line 93 "lex.l"
+{ return kw_EXPLICIT; }
+ YY_BREAK
+case 26:
+YY_RULE_SETUP
+#line 94 "lex.l"
+{ return kw_EXPORTS; }
+ YY_BREAK
+case 27:
+YY_RULE_SETUP
+#line 95 "lex.l"
+{ return kw_EXTENSIBILITY; }
+ YY_BREAK
+case 28:
+YY_RULE_SETUP
+#line 96 "lex.l"
+{ return kw_EXTERNAL; }
+ YY_BREAK
+case 29:
+YY_RULE_SETUP
+#line 97 "lex.l"
+{ return kw_FALSE; }
+ YY_BREAK
+case 30:
+YY_RULE_SETUP
+#line 98 "lex.l"
+{ return kw_FROM; }
+ YY_BREAK
+case 31:
+YY_RULE_SETUP
+#line 99 "lex.l"
+{ return kw_GeneralString; }
+ YY_BREAK
+case 32:
+YY_RULE_SETUP
+#line 100 "lex.l"
+{ return kw_GeneralizedTime; }
+ YY_BREAK
+case 33:
+YY_RULE_SETUP
+#line 101 "lex.l"
+{ return kw_GraphicString; }
+ YY_BREAK
+case 34:
+YY_RULE_SETUP
+#line 102 "lex.l"
+{ return kw_IA5String; }
+ YY_BREAK
+case 35:
+YY_RULE_SETUP
+#line 103 "lex.l"
+{ return kw_IDENTIFIER; }
+ YY_BREAK
+case 36:
+YY_RULE_SETUP
+#line 104 "lex.l"
+{ return kw_IMPLICIT; }
+ YY_BREAK
+case 37:
+YY_RULE_SETUP
+#line 105 "lex.l"
+{ return kw_IMPLIED; }
+ YY_BREAK
+case 38:
+YY_RULE_SETUP
+#line 106 "lex.l"
+{ return kw_IMPORTS; }
+ YY_BREAK
+case 39:
+YY_RULE_SETUP
+#line 107 "lex.l"
+{ return kw_INCLUDES; }
+ YY_BREAK
+case 40:
+YY_RULE_SETUP
+#line 108 "lex.l"
+{ return kw_INSTANCE; }
+ YY_BREAK
+case 41:
+YY_RULE_SETUP
+#line 109 "lex.l"
+{ return kw_INTEGER; }
+ YY_BREAK
+case 42:
+YY_RULE_SETUP
+#line 110 "lex.l"
+{ return kw_INTERSECTION; }
+ YY_BREAK
+case 43:
+YY_RULE_SETUP
+#line 111 "lex.l"
+{ return kw_ISO646String; }
+ YY_BREAK
+case 44:
+YY_RULE_SETUP
+#line 112 "lex.l"
+{ return kw_MAX; }
+ YY_BREAK
+case 45:
+YY_RULE_SETUP
+#line 113 "lex.l"
+{ return kw_MIN; }
+ YY_BREAK
+case 46:
+YY_RULE_SETUP
+#line 114 "lex.l"
+{ return kw_MINUS_INFINITY; }
+ YY_BREAK
+case 47:
+YY_RULE_SETUP
+#line 115 "lex.l"
+{ return kw_NULL; }
+ YY_BREAK
+case 48:
+YY_RULE_SETUP
+#line 116 "lex.l"
+{ return kw_NumericString; }
+ YY_BREAK
+case 49:
+YY_RULE_SETUP
+#line 117 "lex.l"
+{ return kw_OBJECT; }
+ YY_BREAK
+case 50:
+YY_RULE_SETUP
+#line 118 "lex.l"
+{ return kw_OCTET; }
+ YY_BREAK
+case 51:
+YY_RULE_SETUP
+#line 119 "lex.l"
+{ return kw_OF; }
+ YY_BREAK
+case 52:
+YY_RULE_SETUP
+#line 120 "lex.l"
+{ return kw_OPTIONAL; }
+ YY_BREAK
+case 53:
+YY_RULE_SETUP
+#line 121 "lex.l"
+{ return kw_ObjectDescriptor; }
+ YY_BREAK
+case 54:
+YY_RULE_SETUP
+#line 122 "lex.l"
+{ return kw_PATTERN; }
+ YY_BREAK
+case 55:
+YY_RULE_SETUP
+#line 123 "lex.l"
+{ return kw_PDV; }
+ YY_BREAK
+case 56:
+YY_RULE_SETUP
+#line 124 "lex.l"
+{ return kw_PLUS_INFINITY; }
+ YY_BREAK
+case 57:
+YY_RULE_SETUP
+#line 125 "lex.l"
+{ return kw_PRESENT; }
+ YY_BREAK
+case 58:
+YY_RULE_SETUP
+#line 126 "lex.l"
+{ return kw_PRIVATE; }
+ YY_BREAK
+case 59:
+YY_RULE_SETUP
+#line 127 "lex.l"
+{ return kw_PrintableString; }
+ YY_BREAK
+case 60:
+YY_RULE_SETUP
+#line 128 "lex.l"
+{ return kw_REAL; }
+ YY_BREAK
+case 61:
+YY_RULE_SETUP
+#line 129 "lex.l"
+{ return kw_RELATIVE_OID; }
+ YY_BREAK
+case 62:
+YY_RULE_SETUP
+#line 130 "lex.l"
+{ return kw_SEQUENCE; }
+ YY_BREAK
+case 63:
+YY_RULE_SETUP
+#line 131 "lex.l"
+{ return kw_SET; }
+ YY_BREAK
+case 64:
+YY_RULE_SETUP
+#line 132 "lex.l"
+{ return kw_SIZE; }
+ YY_BREAK
+case 65:
+YY_RULE_SETUP
+#line 133 "lex.l"
+{ return kw_STRING; }
+ YY_BREAK
+case 66:
+YY_RULE_SETUP
+#line 134 "lex.l"
+{ return kw_SYNTAX; }
+ YY_BREAK
+case 67:
+YY_RULE_SETUP
+#line 135 "lex.l"
+{ return kw_T61String; }
+ YY_BREAK
+case 68:
+YY_RULE_SETUP
+#line 136 "lex.l"
+{ return kw_TAGS; }
+ YY_BREAK
+case 69:
+YY_RULE_SETUP
+#line 137 "lex.l"
+{ return kw_TRUE; }
+ YY_BREAK
+case 70:
+YY_RULE_SETUP
+#line 138 "lex.l"
+{ return kw_TYPE_IDENTIFIER; }
+ YY_BREAK
+case 71:
+YY_RULE_SETUP
+#line 139 "lex.l"
+{ return kw_TeletexString; }
+ YY_BREAK
+case 72:
+YY_RULE_SETUP
+#line 140 "lex.l"
+{ return kw_UNION; }
+ YY_BREAK
+case 73:
+YY_RULE_SETUP
+#line 141 "lex.l"
+{ return kw_UNIQUE; }
+ YY_BREAK
+case 74:
+YY_RULE_SETUP
+#line 142 "lex.l"
+{ return kw_UNIVERSAL; }
+ YY_BREAK
+case 75:
+YY_RULE_SETUP
+#line 143 "lex.l"
+{ return kw_UTCTime; }
+ YY_BREAK
+case 76:
+YY_RULE_SETUP
+#line 144 "lex.l"
+{ return kw_UTF8String; }
+ YY_BREAK
+case 77:
+YY_RULE_SETUP
+#line 145 "lex.l"
+{ return kw_UniversalString; }
+ YY_BREAK
+case 78:
+YY_RULE_SETUP
+#line 146 "lex.l"
+{ return kw_VideotexString; }
+ YY_BREAK
+case 79:
+YY_RULE_SETUP
+#line 147 "lex.l"
+{ return kw_VisibleString; }
+ YY_BREAK
+case 80:
+YY_RULE_SETUP
+#line 148 "lex.l"
+{ return kw_WITH; }
+ YY_BREAK
+case 81:
+YY_RULE_SETUP
+#line 149 "lex.l"
+{ return *yytext; }
+ YY_BREAK
+case 82:
+YY_RULE_SETUP
+#line 150 "lex.l"
+{ return *yytext; }
+ YY_BREAK
+case 83:
+YY_RULE_SETUP
+#line 151 "lex.l"
+{ return *yytext; }
+ YY_BREAK
+case 84:
+YY_RULE_SETUP
+#line 152 "lex.l"
+{ return EEQUAL; }
+ YY_BREAK
+case 85:
+YY_RULE_SETUP
+#line 153 "lex.l"
+{
+ int c, start_lineno = lineno;
+ int f = 0;
+ while((c = input()) != EOF) {
+ if(f && c == '-')
+ break;
+ if(c == '-') {
+ f = 1;
+ continue;
+ }
+ if(c == '\n') {
+ lineno++;
+ break;
+ }
+ f = 0;
+ }
+ if(c == EOF)
+ unterminated("comment", start_lineno);
+ }
+ YY_BREAK
+case 86:
+YY_RULE_SETUP
+#line 172 "lex.l"
+{
+ int c, start_lineno = lineno;
+ int level = 1;
+ int seen_star = 0;
+ int seen_slash = 0;
+ while((c = input()) != EOF) {
+ if(c == '/') {
+ if(seen_star) {
+ if(--level == 0)
+ break;
+ seen_star = 0;
+ continue;
+ }
+ seen_slash = 1;
+ continue;
+ }
+ if(seen_star && c == '/') {
+ if(--level == 0)
+ break;
+ seen_star = 0;
+ continue;
+ }
+ if(c == '*') {
+ if(seen_slash) {
+ level++;
+ seen_star = seen_slash = 0;
+ continue;
+ }
+ seen_star = 1;
+ continue;
+ }
+ seen_star = seen_slash = 0;
+ if(c == '\n') {
+ lineno++;
+ continue;
+ }
+ }
+ if(c == EOF)
+ unterminated("comment", start_lineno);
+ }
+ YY_BREAK
+case 87:
+YY_RULE_SETUP
+#line 212 "lex.l"
+{
+ int start_lineno = lineno;
+ int c;
+ char buf[1024];
+ char *p = buf;
+ int f = 0;
+ int skip_ws = 0;
+
+ while((c = input()) != EOF) {
+ if(isspace(c) && skip_ws) {
+ if(c == '\n')
+ lineno++;
+ continue;
+ }
+ skip_ws = 0;
+
+ if(c == '"') {
+ if(f) {
+ *p++ = '"';
+ f = 0;
+ } else
+ f = 1;
+ continue;
+ }
+ if(f == 1) {
+ unput(c);
+ break;
+ }
+ if(c == '\n') {
+ lineno++;
+ while(p > buf && isspace((unsigned char)p[-1]))
+ p--;
+ skip_ws = 1;
+ continue;
+ }
+ *p++ = c;
+ }
+ if(c == EOF)
+ unterminated("string", start_lineno);
+ *p++ = '\0';
+ fprintf(stderr, "string -- %s\n", buf);
+ yylval.name = estrdup(buf);
+ return STRING;
+ }
+ YY_BREAK
+case 88:
+YY_RULE_SETUP
+#line 257 "lex.l"
+{ char *e, *y = yytext;
+ yylval.constant = strtol((const char *)yytext,
+ &e, 0);
+ if(e == y)
+ error_message("malformed constant (%s)", yytext);
+ else
+ return NUMBER;
+ }
+ YY_BREAK
+case 89:
+YY_RULE_SETUP
+#line 265 "lex.l"
+{
+ yylval.name = estrdup ((const char *)yytext);
+ return IDENTIFIER;
+ }
+ YY_BREAK
+case 90:
+YY_RULE_SETUP
+#line 269 "lex.l"
+;
+ YY_BREAK
+case 91:
+/* rule 91 can match eol */
+YY_RULE_SETUP
+#line 270 "lex.l"
+{ ++lineno; }
+ YY_BREAK
+case 92:
+YY_RULE_SETUP
+#line 271 "lex.l"
+{ return ELLIPSIS; }
+ YY_BREAK
+case 93:
+YY_RULE_SETUP
+#line 272 "lex.l"
+{ return RANGE; }
+ YY_BREAK
+case 94:
+YY_RULE_SETUP
+#line 273 "lex.l"
+{ error_message("Ignoring char(%c)\n", *yytext); }
+ YY_BREAK
+case 95:
+YY_RULE_SETUP
+#line 274 "lex.l"
+ECHO;
+ YY_BREAK
+#line 1682 "heimdal/lib/asn1/lex.c"
+case YY_STATE_EOF(INITIAL):
+ yyterminate();
+
+ case YY_END_OF_BUFFER:
+ {
+ /* Amount of text matched not including the EOB char. */
+ int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1;
+
+ /* Undo the effects of YY_DO_BEFORE_ACTION. */
+ *yy_cp = (yy_hold_char);
+ YY_RESTORE_YY_MORE_OFFSET
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+ {
+ /* We're scanning a new file or input source. It's
+ * possible that this happened because the user
+ * just pointed yyin at a new source and called
+ * yylex(). If so, then we have to assure
+ * consistency between YY_CURRENT_BUFFER and our
+ * globals. Here is the right place to do so, because
+ * this is the first action (other than possibly a
+ * back-up) that will match for the new input source.
+ */
+ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+ }
+
+ /* Note that here we test for yy_c_buf_p "<=" to the position
+ * of the first EOB in the buffer, since yy_c_buf_p will
+ * already have been incremented past the NUL character
+ * (since all states make transitions on EOB to the
+ * end-of-buffer state). Contrast this with the test
+ * in input().
+ */
+ if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+ { /* This was really a NUL. */
+ yy_state_type yy_next_state;
+
+ (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( );
+
+ /* Okay, we're now positioned to make the NUL
+ * transition. We couldn't have
+ * yy_get_previous_state() go ahead and do it
+ * for us because it doesn't know how to deal
+ * with the possibility of jamming (and we don't
+ * want to build jamming into it because then it
+ * will run more slowly).
+ */
+
+ yy_next_state = yy_try_NUL_trans( yy_current_state );
+
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+
+ if ( yy_next_state )
+ {
+ /* Consume the NUL. */
+ yy_cp = ++(yy_c_buf_p);
+ yy_current_state = yy_next_state;
+ goto yy_match;
+ }
+
+ else
+ {
+ yy_cp = (yy_c_buf_p);
+ goto yy_find_action;
+ }
+ }
+
+ else switch ( yy_get_next_buffer( ) )
+ {
+ case EOB_ACT_END_OF_FILE:
+ {
+ (yy_did_buffer_switch_on_eof) = 0;
+
+ if ( yywrap( ) )
+ {
+ /* Note: because we've taken care in
+ * yy_get_next_buffer() to have set up
+ * yytext, we can now set up
+ * yy_c_buf_p so that if some total
+ * hoser (like flex itself) wants to
+ * call the scanner after we return the
+ * YY_NULL, it'll still work - another
+ * YY_NULL will get returned.
+ */
+ (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ;
+
+ yy_act = YY_STATE_EOF(YY_START);
+ goto do_action;
+ }
+
+ else
+ {
+ if ( ! (yy_did_buffer_switch_on_eof) )
+ YY_NEW_FILE;
+ }
+ break;
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ (yy_c_buf_p) =
+ (yytext_ptr) + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( );
+
+ yy_cp = (yy_c_buf_p);
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+ goto yy_match;
+
+ case EOB_ACT_LAST_MATCH:
+ (yy_c_buf_p) =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)];
+
+ yy_current_state = yy_get_previous_state( );
+
+ yy_cp = (yy_c_buf_p);
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+ goto yy_find_action;
+ }
+ break;
+ }
+
+ default:
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--no action found" );
+ } /* end of action switch */
+ } /* end of scanning one token */
+} /* end of yylex */
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ * EOB_ACT_LAST_MATCH -
+ * EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ * EOB_ACT_END_OF_FILE - end of file
+ */
+static int yy_get_next_buffer (void)
+{
+ register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+ register char *source = (yytext_ptr);
+ register int number_to_move, i;
+ int ret_val;
+
+ if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] )
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--end of buffer missed" );
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+ { /* Don't try to fill the buffer, so this is an EOF. */
+ if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 )
+ {
+ /* We matched a single character, the EOB, so
+ * treat this as a final EOF.
+ */
+ return EOB_ACT_END_OF_FILE;
+ }
+
+ else
+ {
+ /* We matched some text prior to the EOB, first
+ * process it.
+ */
+ return EOB_ACT_LAST_MATCH;
+ }
+ }
+
+ /* Try to read more data. */
+
+ /* First move last chars to start of buffer. */
+ number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1;
+
+ for ( i = 0; i < number_to_move; ++i )
+ *(dest++) = *(source++);
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+ /* don't do the read, it's not guaranteed to return an EOF,
+ * just force an EOF
+ */
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0;
+
+ else
+ {
+ int num_to_read =
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+ while ( num_to_read <= 0 )
+ { /* Not enough room in the buffer - grow it. */
+
+ /* just a shorter name for the current buffer */
+ YY_BUFFER_STATE b = YY_CURRENT_BUFFER;
+
+ int yy_c_buf_p_offset =
+ (int) ((yy_c_buf_p) - b->yy_ch_buf);
+
+ if ( b->yy_is_our_buffer )
+ {
+ int new_size = b->yy_buf_size * 2;
+
+ if ( new_size <= 0 )
+ b->yy_buf_size += b->yy_buf_size / 8;
+ else
+ b->yy_buf_size *= 2;
+
+ b->yy_ch_buf = (char *)
+ /* Include room in for 2 EOB chars. */
+ yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 );
+ }
+ else
+ /* Can't grow it, we don't own it. */
+ b->yy_ch_buf = 0;
+
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR(
+ "fatal error - scanner input buffer overflow" );
+
+ (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+ num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
+ number_to_move - 1;
+
+ }
+
+ if ( num_to_read > YY_READ_BUF_SIZE )
+ num_to_read = YY_READ_BUF_SIZE;
+
+ /* Read in more data. */
+ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+ (yy_n_chars), (size_t) num_to_read );
+
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ if ( (yy_n_chars) == 0 )
+ {
+ if ( number_to_move == YY_MORE_ADJ )
+ {
+ ret_val = EOB_ACT_END_OF_FILE;
+ yyrestart(yyin );
+ }
+
+ else
+ {
+ ret_val = EOB_ACT_LAST_MATCH;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+ YY_BUFFER_EOF_PENDING;
+ }
+ }
+
+ else
+ ret_val = EOB_ACT_CONTINUE_SCAN;
+
+ if ((yy_size_t) ((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
+ /* Extend the array by 50%, plus the number we really need. */
+ yy_size_t new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1);
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size );
+ if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
+ }
+
+ (yy_n_chars) += number_to_move;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR;
+
+ (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+ return ret_val;
+}
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+ static yy_state_type yy_get_previous_state (void)
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp;
+
+ yy_current_state = (yy_start);
+
+ for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp )
+ {
+ register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+ if ( yy_accept[yy_current_state] )
+ {
+ (yy_last_accepting_state) = yy_current_state;
+ (yy_last_accepting_cpos) = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 568 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ }
+
+ return yy_current_state;
+}
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ * next_state = yy_try_NUL_trans( current_state );
+ */
+ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state )
+{
+ register int yy_is_jam;
+ register char *yy_cp = (yy_c_buf_p);
+
+ register YY_CHAR yy_c = 1;
+ if ( yy_accept[yy_current_state] )
+ {
+ (yy_last_accepting_state) = yy_current_state;
+ (yy_last_accepting_cpos) = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 568 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ yy_is_jam = (yy_current_state == 567);
+
+ return yy_is_jam ? 0 : yy_current_state;
+}
+
+ static void yyunput (int c, register char * yy_bp )
+{
+ register char *yy_cp;
+
+ yy_cp = (yy_c_buf_p);
+
+ /* undo effects of setting up yytext */
+ *yy_cp = (yy_hold_char);
+
+ if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+ { /* need to shift things up to make room */
+ /* +2 for EOB chars. */
+ register int number_to_move = (yy_n_chars) + 2;
+ register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2];
+ register char *source =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move];
+
+ while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+ *--dest = *--source;
+
+ yy_cp += (int) (dest - source);
+ yy_bp += (int) (dest - source);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars =
+ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_buf_size;
+
+ if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+ YY_FATAL_ERROR( "flex scanner push-back overflow" );
+ }
+
+ *--yy_cp = (char) c;
+
+ (yytext_ptr) = yy_bp;
+ (yy_hold_char) = *yy_cp;
+ (yy_c_buf_p) = yy_cp;
+}
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+ static int yyinput (void)
+#else
+ static int input (void)
+#endif
+
+{
+ int c;
+
+ *(yy_c_buf_p) = (yy_hold_char);
+
+ if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR )
+ {
+ /* yy_c_buf_p now points to the character we want to return.
+ * If this occurs *before* the EOB characters, then it's a
+ * valid NUL; if not, then we've hit the end of the buffer.
+ */
+ if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+ /* This was really a NUL. */
+ *(yy_c_buf_p) = '\0';
+
+ else
+ { /* need more input */
+ int offset = (yy_c_buf_p) - (yytext_ptr);
+ ++(yy_c_buf_p);
+
+ switch ( yy_get_next_buffer( ) )
+ {
+ case EOB_ACT_LAST_MATCH:
+ /* This happens because yy_g_n_b()
+ * sees that we've accumulated a
+ * token and flags that we need to
+ * try matching the token before
+ * proceeding. But for input(),
+ * there's no matching to consider.
+ * So convert the EOB_ACT_LAST_MATCH
+ * to EOB_ACT_END_OF_FILE.
+ */
+
+ /* Reset buffer status. */
+ yyrestart(yyin );
+
+ /*FALLTHROUGH*/
+
+ case EOB_ACT_END_OF_FILE:
+ {
+ if ( yywrap( ) )
+ return EOF;
+
+ if ( ! (yy_did_buffer_switch_on_eof) )
+ YY_NEW_FILE;
+#ifdef __cplusplus
+ return yyinput();
+#else
+ return input();
+#endif
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ (yy_c_buf_p) = (yytext_ptr) + offset;
+ break;
+ }
+ }
+ }
+
+ c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */
+ *(yy_c_buf_p) = '\0'; /* preserve yytext */
+ (yy_hold_char) = *++(yy_c_buf_p);
+
+ return c;
+}
+#endif /* ifndef YY_NO_INPUT */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ *
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+ void yyrestart (FILE * input_file )
+{
+
+ if ( ! YY_CURRENT_BUFFER ){
+ yyensure_buffer_stack ();
+ YY_CURRENT_BUFFER_LVALUE =
+ yy_create_buffer(yyin,YY_BUF_SIZE );
+ }
+
+ yy_init_buffer(YY_CURRENT_BUFFER,input_file );
+ yy_load_buffer_state( );
+}
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ *
+ */
+ void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer )
+{
+
+ /* TODO. We should be able to replace this entire function body
+ * with
+ * yypop_buffer_state();
+ * yypush_buffer_state(new_buffer);
+ */
+ yyensure_buffer_stack ();
+ if ( YY_CURRENT_BUFFER == new_buffer )
+ return;
+
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *(yy_c_buf_p) = (yy_hold_char);
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+ yy_load_buffer_state( );
+
+ /* We don't actually know whether we did this switch during
+ * EOF (yywrap()) processing, but the only time this flag
+ * is looked at is after yywrap() is called, so it's safe
+ * to go ahead and always set it.
+ */
+ (yy_did_buffer_switch_on_eof) = 1;
+}
+
+static void yy_load_buffer_state (void)
+{
+ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+ yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+ (yy_hold_char) = *(yy_c_buf_p);
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ *
+ * @return the allocated buffer state.
+ */
+ YY_BUFFER_STATE yy_create_buffer (FILE * file, int size )
+{
+ YY_BUFFER_STATE b;
+
+ b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+ b->yy_buf_size = size;
+
+ /* yy_ch_buf has to be 2 characters longer than the size given because
+ * we need to put in 2 end-of-buffer characters.
+ */
+ b->yy_ch_buf = (char *) yyalloc(b->yy_buf_size + 2 );
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+ b->yy_is_our_buffer = 1;
+
+ yy_init_buffer(b,file );
+
+ return b;
+}
+
+/** Destroy the buffer.
+ * @param b a buffer created with yy_create_buffer()
+ *
+ */
+ void yy_delete_buffer (YY_BUFFER_STATE b )
+{
+
+ if ( ! b )
+ return;
+
+ if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+ if ( b->yy_is_our_buffer )
+ yyfree((void *) b->yy_ch_buf );
+
+ yyfree((void *) b );
+}
+
+#ifndef __cplusplus
+extern int isatty (int );
+#endif /* __cplusplus */
+
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a yyrestart() or at EOF.
+ */
+ static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file )
+
+{
+ int oerrno = errno;
+
+ yy_flush_buffer(b );
+
+ b->yy_input_file = file;
+ b->yy_fill_buffer = 1;
+
+ /* If b is the current buffer, then yy_init_buffer was _probably_
+ * called from yyrestart() or through yy_get_next_buffer.
+ * In that case, we don't want to reset the lineno or column.
+ */
+ if (b != YY_CURRENT_BUFFER){
+ b->yy_bs_lineno = 1;
+ b->yy_bs_column = 0;
+ }
+
+ b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+
+ errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ *
+ */
+ void yy_flush_buffer (YY_BUFFER_STATE b )
+{
+ if ( ! b )
+ return;
+
+ b->yy_n_chars = 0;
+
+ /* We always need two end-of-buffer characters. The first causes
+ * a transition to the end-of-buffer state. The second causes
+ * a jam in that state.
+ */
+ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+ b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+ b->yy_buf_pos = &b->yy_ch_buf[0];
+
+ b->yy_at_bol = 1;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ if ( b == YY_CURRENT_BUFFER )
+ yy_load_buffer_state( );
+}
+
+/** Pushes the new state onto the stack. The new state becomes
+ * the current state. This function will allocate the stack
+ * if necessary.
+ * @param new_buffer The new state.
+ *
+ */
+void yypush_buffer_state (YY_BUFFER_STATE new_buffer )
+{
+ if (new_buffer == NULL)
+ return;
+
+ yyensure_buffer_stack();
+
+ /* This block is copied from yy_switch_to_buffer. */
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *(yy_c_buf_p) = (yy_hold_char);
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ /* Only push if top exists. Otherwise, replace top. */
+ if (YY_CURRENT_BUFFER)
+ (yy_buffer_stack_top)++;
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+ /* copied from yy_switch_to_buffer. */
+ yy_load_buffer_state( );
+ (yy_did_buffer_switch_on_eof) = 1;
+}
+
+/** Removes and deletes the top of the stack, if present.
+ * The next element becomes the new top.
+ *
+ */
+void yypop_buffer_state (void)
+{
+ if (!YY_CURRENT_BUFFER)
+ return;
+
+ yy_delete_buffer(YY_CURRENT_BUFFER );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ if ((yy_buffer_stack_top) > 0)
+ --(yy_buffer_stack_top);
+
+ if (YY_CURRENT_BUFFER) {
+ yy_load_buffer_state( );
+ (yy_did_buffer_switch_on_eof) = 1;
+ }
+}
+
+/* Allocates the stack if it does not exist.
+ * Guarantees space for at least one push.
+ */
+static void yyensure_buffer_stack (void)
+{
+ int num_to_alloc;
+
+ if (!(yy_buffer_stack)) {
+
+ /* First allocation is just for 2 elements, since we don't know if this
+ * scanner will even need a stack. We use 2 instead of 1 to avoid an
+ * immediate realloc on the next call.
+ */
+ num_to_alloc = 1;
+ (yy_buffer_stack) = (struct yy_buffer_state**)yyalloc
+ (num_to_alloc * sizeof(struct yy_buffer_state*)
+ );
+ if ( ! (yy_buffer_stack) )
+ YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" );
+
+ memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+
+ (yy_buffer_stack_max) = num_to_alloc;
+ (yy_buffer_stack_top) = 0;
+ return;
+ }
+
+ if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){
+
+ /* Increase the buffer to prepare for a possible push. */
+ int grow_size = 8 /* arbitrary grow size */;
+
+ num_to_alloc = (yy_buffer_stack_max) + grow_size;
+ (yy_buffer_stack) = (struct yy_buffer_state**)yyrealloc
+ ((yy_buffer_stack),
+ num_to_alloc * sizeof(struct yy_buffer_state*)
+ );
+ if ( ! (yy_buffer_stack) )
+ YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" );
+
+ /* zero only the new slots.*/
+ memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*));
+ (yy_buffer_stack_max) = num_to_alloc;
+ }
+}
+
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ *
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size )
+{
+ YY_BUFFER_STATE b;
+
+ if ( size < 2 ||
+ base[size-2] != YY_END_OF_BUFFER_CHAR ||
+ base[size-1] != YY_END_OF_BUFFER_CHAR )
+ /* They forgot to leave room for the EOB's. */
+ return 0;
+
+ b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" );
+
+ b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */
+ b->yy_buf_pos = b->yy_ch_buf = base;
+ b->yy_is_our_buffer = 0;
+ b->yy_input_file = 0;
+ b->yy_n_chars = b->yy_buf_size;
+ b->yy_is_interactive = 0;
+ b->yy_at_bol = 1;
+ b->yy_fill_buffer = 0;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ yy_switch_to_buffer(b );
+
+ return b;
+}
+
+/** Setup the input buffer state to scan a string. The next call to yylex() will
+ * scan from a @e copy of @a str.
+ * @param yystr a NUL-terminated string to scan
+ *
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ * yy_scan_bytes() instead.
+ */
+YY_BUFFER_STATE yy_scan_string (yyconst char * yystr )
+{
+
+ return yy_scan_bytes(yystr,strlen(yystr) );
+}
+
+/** Setup the input buffer state to scan the given bytes. The next call to yylex() will
+ * scan from a @e copy of @a bytes.
+ * @param bytes the byte buffer to scan
+ * @param len the number of bytes in the buffer pointed to by @a bytes.
+ *
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE yy_scan_bytes (yyconst char * yybytes, int _yybytes_len )
+{
+ YY_BUFFER_STATE b;
+ char *buf;
+ yy_size_t n;
+ int i;
+
+ /* Get memory for full buffer, including space for trailing EOB's. */
+ n = _yybytes_len + 2;
+ buf = (char *) yyalloc(n );
+ if ( ! buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" );
+
+ for ( i = 0; i < _yybytes_len; ++i )
+ buf[i] = yybytes[i];
+
+ buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
+
+ b = yy_scan_buffer(buf,n );
+ if ( ! b )
+ YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" );
+
+ /* It's okay to grow etc. this buffer, and we should throw it
+ * away when we're done.
+ */
+ b->yy_is_our_buffer = 1;
+
+ return b;
+}
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+static void yy_fatal_error (yyconst char* msg )
+{
+ (void) fprintf( stderr, "%s\n", msg );
+ exit( YY_EXIT_FAILURE );
+}
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ yytext[yyleng] = (yy_hold_char); \
+ (yy_c_buf_p) = yytext + yyless_macro_arg; \
+ (yy_hold_char) = *(yy_c_buf_p); \
+ *(yy_c_buf_p) = '\0'; \
+ yyleng = yyless_macro_arg; \
+ } \
+ while ( 0 )
+
+/* Accessor methods (get/set functions) to struct members. */
+
+/** Get the current line number.
+ *
+ */
+int yyget_lineno (void)
+{
+
+ return yylineno;
+}
+
+/** Get the input stream.
+ *
+ */
+FILE *yyget_in (void)
+{
+ return yyin;
+}
+
+/** Get the output stream.
+ *
+ */
+FILE *yyget_out (void)
+{
+ return yyout;
+}
+
+/** Get the length of the current token.
+ *
+ */
+int yyget_leng (void)
+{
+ return yyleng;
+}
+
+/** Get the current token.
+ *
+ */
+
+char *yyget_text (void)
+{
+ return yytext;
+}
+
+/** Set the current line number.
+ * @param line_number
+ *
+ */
+void yyset_lineno (int line_number )
+{
+
+ yylineno = line_number;
+}
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param in_str A readable stream.
+ *
+ * @see yy_switch_to_buffer
+ */
+void yyset_in (FILE * in_str )
+{
+ yyin = in_str ;
+}
+
+void yyset_out (FILE * out_str )
+{
+ yyout = out_str ;
+}
+
+int yyget_debug (void)
+{
+ return yy_flex_debug;
+}
+
+void yyset_debug (int bdebug )
+{
+ yy_flex_debug = bdebug ;
+}
+
+static int yy_init_globals (void)
+{
+ /* Initialization is the same as for the non-reentrant scanner.
+ * This function is called from yylex_destroy(), so don't allocate here.
+ */
+
+ (yy_buffer_stack) = 0;
+ (yy_buffer_stack_top) = 0;
+ (yy_buffer_stack_max) = 0;
+ (yy_c_buf_p) = (char *) 0;
+ (yy_init) = 0;
+ (yy_start) = 0;
+
+/* Defined in main.c */
+#ifdef YY_STDINIT
+ yyin = stdin;
+ yyout = stdout;
+#else
+ yyin = (FILE *) 0;
+ yyout = (FILE *) 0;
+#endif
+
+ /* For future reference: Set errno on error, since we are called by
+ * yylex_init()
+ */
+ return 0;
+}
+
+/* yylex_destroy is for both reentrant and non-reentrant scanners. */
+int yylex_destroy (void)
+{
+
+ /* Pop the buffer stack, destroying each element. */
+ while(YY_CURRENT_BUFFER){
+ yy_delete_buffer(YY_CURRENT_BUFFER );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ yypop_buffer_state();
+ }
+
+ /* Destroy the stack itself. */
+ yyfree((yy_buffer_stack) );
+ (yy_buffer_stack) = NULL;
+
+ /* Reset the globals. This is important in a non-reentrant scanner so the next time
+ * yylex() is called, initialization will occur. */
+ yy_init_globals( );
+
+ return 0;
+}
+
+/*
+ * Internal utility routines.
+ */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, yyconst char * s2, int n )
+{
+ register int i;
+ for ( i = 0; i < n; ++i )
+ s1[i] = s2[i];
+}
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * s )
+{
+ register int n;
+ for ( n = 0; s[n]; ++n )
+ ;
+
+ return n;
+}
+#endif
+
+void *yyalloc (yy_size_t size )
+{
+ return (void *) malloc( size );
+}
+
+void *yyrealloc (void * ptr, yy_size_t size )
+{
+ /* The cast to (char *) in the following accommodates both
+ * implementations that use char* generic pointers, and those
+ * that use void* generic pointers. It works with the latter
+ * because both ANSI C and C++ allow castless assignment from
+ * any pointer type to void*, and deal with argument conversions
+ * as though doing an assignment.
+ */
+ return (void *) realloc( (char *) ptr, size );
+}
+
+void yyfree (void * ptr )
+{
+ free( (char *) ptr ); /* see yyrealloc() for (char *) cast */
+}
+
+#define YYTABLES_NAME "yytables"
+
+#line 274 "lex.l"
+
+
+
+#ifndef yywrap /* XXX */
+int
+yywrap ()
+{
+ return 1;
+}
+#endif
+
+void
+error_message (const char *format, ...)
+{
+ va_list args;
+
+ va_start (args, format);
+ fprintf (stderr, "%s:%d: ", get_filename(), lineno);
+ vfprintf (stderr, format, args);
+ va_end (args);
+ error_flag++;
+}
+
+static void
+unterminated(const char *type, unsigned start_lineno)
+{
+ error_message("unterminated %s, possibly started on line %d\n", type, start_lineno);
+}
+
diff --git a/source4/heimdal/lib/asn1/lex.h b/source4/heimdal/lib/asn1/lex.h
new file mode 100644
index 0000000000..abca8f723d
--- /dev/null
+++ b/source4/heimdal/lib/asn1/lex.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 1997 - 2000 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#include <roken.h>
+
+void error_message (const char *, ...)
+__attribute__ ((format (printf, 1, 2)));
+extern int error_flag;
+
+int yylex(void);
diff --git a/source4/heimdal/lib/asn1/lex.l b/source4/heimdal/lib/asn1/lex.l
new file mode 100644
index 0000000000..1198ef16a6
--- /dev/null
+++ b/source4/heimdal/lib/asn1/lex.l
@@ -0,0 +1,300 @@
+%{
+/*
+ * Copyright (c) 1997 - 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#undef ECHO
+#include "symbol.h"
+#include "parse.h"
+#include "lex.h"
+#include "gen_locl.h"
+
+static unsigned lineno = 1;
+
+#undef ECHO
+
+static void unterminated(const char *, unsigned);
+
+%}
+
+/* This is for broken old lexes (solaris 10 and hpux) */
+%e 2000
+%p 5000
+%a 5000
+%n 1000
+%o 10000
+
+%%
+ABSENT { return kw_ABSENT; }
+ABSTRACT-SYNTAX { return kw_ABSTRACT_SYNTAX; }
+ALL { return kw_ALL; }
+APPLICATION { return kw_APPLICATION; }
+AUTOMATIC { return kw_AUTOMATIC; }
+BEGIN { return kw_BEGIN; }
+BIT { return kw_BIT; }
+BMPString { return kw_BMPString; }
+BOOLEAN { return kw_BOOLEAN; }
+BY { return kw_BY; }
+CHARACTER { return kw_CHARACTER; }
+CHOICE { return kw_CHOICE; }
+CLASS { return kw_CLASS; }
+COMPONENT { return kw_COMPONENT; }
+COMPONENTS { return kw_COMPONENTS; }
+CONSTRAINED { return kw_CONSTRAINED; }
+CONTAINING { return kw_CONTAINING; }
+DEFAULT { return kw_DEFAULT; }
+DEFINITIONS { return kw_DEFINITIONS; }
+EMBEDDED { return kw_EMBEDDED; }
+ENCODED { return kw_ENCODED; }
+END { return kw_END; }
+ENUMERATED { return kw_ENUMERATED; }
+EXCEPT { return kw_EXCEPT; }
+EXPLICIT { return kw_EXPLICIT; }
+EXPORTS { return kw_EXPORTS; }
+EXTENSIBILITY { return kw_EXTENSIBILITY; }
+EXTERNAL { return kw_EXTERNAL; }
+FALSE { return kw_FALSE; }
+FROM { return kw_FROM; }
+GeneralString { return kw_GeneralString; }
+GeneralizedTime { return kw_GeneralizedTime; }
+GraphicString { return kw_GraphicString; }
+IA5String { return kw_IA5String; }
+IDENTIFIER { return kw_IDENTIFIER; }
+IMPLICIT { return kw_IMPLICIT; }
+IMPLIED { return kw_IMPLIED; }
+IMPORTS { return kw_IMPORTS; }
+INCLUDES { return kw_INCLUDES; }
+INSTANCE { return kw_INSTANCE; }
+INTEGER { return kw_INTEGER; }
+INTERSECTION { return kw_INTERSECTION; }
+ISO646String { return kw_ISO646String; }
+MAX { return kw_MAX; }
+MIN { return kw_MIN; }
+MINUS-INFINITY { return kw_MINUS_INFINITY; }
+NULL { return kw_NULL; }
+NumericString { return kw_NumericString; }
+OBJECT { return kw_OBJECT; }
+OCTET { return kw_OCTET; }
+OF { return kw_OF; }
+OPTIONAL { return kw_OPTIONAL; }
+ObjectDescriptor { return kw_ObjectDescriptor; }
+PATTERN { return kw_PATTERN; }
+PDV { return kw_PDV; }
+PLUS-INFINITY { return kw_PLUS_INFINITY; }
+PRESENT { return kw_PRESENT; }
+PRIVATE { return kw_PRIVATE; }
+PrintableString { return kw_PrintableString; }
+REAL { return kw_REAL; }
+RELATIVE_OID { return kw_RELATIVE_OID; }
+SEQUENCE { return kw_SEQUENCE; }
+SET { return kw_SET; }
+SIZE { return kw_SIZE; }
+STRING { return kw_STRING; }
+SYNTAX { return kw_SYNTAX; }
+T61String { return kw_T61String; }
+TAGS { return kw_TAGS; }
+TRUE { return kw_TRUE; }
+TYPE-IDENTIFIER { return kw_TYPE_IDENTIFIER; }
+TeletexString { return kw_TeletexString; }
+UNION { return kw_UNION; }
+UNIQUE { return kw_UNIQUE; }
+UNIVERSAL { return kw_UNIVERSAL; }
+UTCTime { return kw_UTCTime; }
+UTF8String { return kw_UTF8String; }
+UniversalString { return kw_UniversalString; }
+VideotexString { return kw_VideotexString; }
+VisibleString { return kw_VisibleString; }
+WITH { return kw_WITH; }
+[-,;{}()|] { return *yytext; }
+"[" { return *yytext; }
+"]" { return *yytext; }
+::= { return EEQUAL; }
+-- {
+ int c, start_lineno = lineno;
+ int f = 0;
+ while((c = input()) != EOF) {
+ if(f && c == '-')
+ break;
+ if(c == '-') {
+ f = 1;
+ continue;
+ }
+ if(c == '\n') {
+ lineno++;
+ break;
+ }
+ f = 0;
+ }
+ if(c == EOF)
+ unterminated("comment", start_lineno);
+ }
+\/\* {
+ int c, start_lineno = lineno;
+ int level = 1;
+ int seen_star = 0;
+ int seen_slash = 0;
+ while((c = input()) != EOF) {
+ if(c == '/') {
+ if(seen_star) {
+ if(--level == 0)
+ break;
+ seen_star = 0;
+ continue;
+ }
+ seen_slash = 1;
+ continue;
+ }
+ if(seen_star && c == '/') {
+ if(--level == 0)
+ break;
+ seen_star = 0;
+ continue;
+ }
+ if(c == '*') {
+ if(seen_slash) {
+ level++;
+ seen_star = seen_slash = 0;
+ continue;
+ }
+ seen_star = 1;
+ continue;
+ }
+ seen_star = seen_slash = 0;
+ if(c == '\n') {
+ lineno++;
+ continue;
+ }
+ }
+ if(c == EOF)
+ unterminated("comment", start_lineno);
+ }
+"\"" {
+ int start_lineno = lineno;
+ int c;
+ char buf[1024];
+ char *p = buf;
+ int f = 0;
+ int skip_ws = 0;
+
+ while((c = input()) != EOF) {
+ if(isspace(c) && skip_ws) {
+ if(c == '\n')
+ lineno++;
+ continue;
+ }
+ skip_ws = 0;
+
+ if(c == '"') {
+ if(f) {
+ *p++ = '"';
+ f = 0;
+ } else
+ f = 1;
+ continue;
+ }
+ if(f == 1) {
+ unput(c);
+ break;
+ }
+ if(c == '\n') {
+ lineno++;
+ while(p > buf && isspace((unsigned char)p[-1]))
+ p--;
+ skip_ws = 1;
+ continue;
+ }
+ *p++ = c;
+ }
+ if(c == EOF)
+ unterminated("string", start_lineno);
+ *p++ = '\0';
+ fprintf(stderr, "string -- %s\n", buf);
+ yylval.name = estrdup(buf);
+ return STRING;
+ }
+
+-?0x[0-9A-Fa-f]+|-?[0-9]+ { char *e, *y = yytext;
+ yylval.constant = strtol((const char *)yytext,
+ &e, 0);
+ if(e == y)
+ error_message("malformed constant (%s)", yytext);
+ else
+ return NUMBER;
+ }
+[A-Za-z][-A-Za-z0-9_]* {
+ yylval.name = estrdup ((const char *)yytext);
+ return IDENTIFIER;
+ }
+[ \t] ;
+\n { ++lineno; }
+\.\.\. { return ELLIPSIS; }
+\.\. { return RANGE; }
+. { error_message("Ignoring char(%c)\n", *yytext); }
+%%
+
+#ifndef yywrap /* XXX */
+int
+yywrap ()
+{
+ return 1;
+}
+#endif
+
+void
+error_message (const char *format, ...)
+{
+ va_list args;
+
+ va_start (args, format);
+ fprintf (stderr, "%s:%d: ", get_filename(), lineno);
+ vfprintf (stderr, format, args);
+ va_end (args);
+ error_flag++;
+}
+
+static void
+unterminated(const char *type, unsigned start_lineno)
+{
+ error_message("unterminated %s, possibly started on line %d\n", type, start_lineno);
+}
diff --git a/source4/heimdal/lib/asn1/main.c b/source4/heimdal/lib/asn1/main.c
new file mode 100644
index 0000000000..6a97634310
--- /dev/null
+++ b/source4/heimdal/lib/asn1/main.c
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 1997-2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "gen_locl.h"
+#include <getarg.h>
+#include "lex.h"
+
+RCSID("$Id$");
+
+extern FILE *yyin;
+
+static getarg_strings preserve;
+static getarg_strings seq;
+
+int
+preserve_type(const char *p)
+{
+ int i;
+ for (i = 0; i < preserve.num_strings; i++)
+ if (strcmp(preserve.strings[i], p) == 0)
+ return 1;
+ return 0;
+}
+
+int
+seq_type(const char *p)
+{
+ int i;
+ for (i = 0; i < seq.num_strings; i++)
+ if (strcmp(seq.strings[i], p) == 0)
+ return 1;
+ return 0;
+}
+
+int dce_fix;
+int rfc1510_bitstring;
+int version_flag;
+int help_flag;
+struct getargs args[] = {
+ { "encode-rfc1510-bit-string", 0, arg_flag, &rfc1510_bitstring },
+ { "decode-dce-ber", 0, arg_flag, &dce_fix },
+ { "preserve-binary", 0, arg_strings, &preserve },
+ { "sequence", 0, arg_strings, &seq },
+ { "version", 0, arg_flag, &version_flag },
+ { "help", 0, arg_flag, &help_flag }
+};
+int num_args = sizeof(args) / sizeof(args[0]);
+
+static void
+usage(int code)
+{
+ arg_printusage(args, num_args, NULL, "[asn1-file [name]]");
+ exit(code);
+}
+
+int error_flag;
+
+int
+main(int argc, char **argv)
+{
+ int ret;
+ const char *file;
+ const char *name = NULL;
+ int optidx = 0;
+
+ setprogname(argv[0]);
+ if(getarg(args, num_args, argc, argv, &optidx))
+ usage(1);
+ if(help_flag)
+ usage(0);
+ if(version_flag) {
+ print_version(NULL);
+ exit(0);
+ }
+ if (argc == optidx) {
+ file = "stdin";
+ name = "stdin";
+ yyin = stdin;
+ } else {
+ file = argv[optidx];
+ yyin = fopen (file, "r");
+ if (yyin == NULL)
+ err (1, "open %s", file);
+ if (argc == optidx + 1) {
+ char *p;
+ name = estrdup(file);
+ p = strrchr(name, '.');
+ if (p)
+ *p = '\0';
+ } else
+ name = argv[optidx + 1];
+ }
+
+ init_generate (file, name);
+ initsym ();
+ ret = yyparse ();
+ if(ret != 0 || error_flag != 0)
+ exit(1);
+ close_generate ();
+ if (argc != optidx)
+ fclose(yyin);
+ return 0;
+}
diff --git a/source4/heimdal/lib/asn1/parse.c b/source4/heimdal/lib/asn1/parse.c
new file mode 100644
index 0000000000..5e73953b24
--- /dev/null
+++ b/source4/heimdal/lib/asn1/parse.c
@@ -0,0 +1,2831 @@
+/* A Bison parser, made by GNU Bison 2.3. */
+
+/* Skeleton implementation for Bison's Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+ Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU 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., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+ simplifying the original so-called "semantic" parser. */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+ infringing on user name space. This should be done even for local
+ variables, as they might otherwise be expanded by user macros.
+ There are some unavoidable exceptions within include files to
+ define necessary library symbols; they are noted "INFRINGES ON
+ USER NAME SPACE" below. */
+
+/* Identify Bison output. */
+#define YYBISON 1
+
+/* Bison version. */
+#define YYBISON_VERSION "2.3"
+
+/* Skeleton name. */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers. */
+#define YYPURE 0
+
+/* Using locations. */
+#define YYLSP_NEEDED 0
+
+
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ kw_ABSENT = 258,
+ kw_ABSTRACT_SYNTAX = 259,
+ kw_ALL = 260,
+ kw_APPLICATION = 261,
+ kw_AUTOMATIC = 262,
+ kw_BEGIN = 263,
+ kw_BIT = 264,
+ kw_BMPString = 265,
+ kw_BOOLEAN = 266,
+ kw_BY = 267,
+ kw_CHARACTER = 268,
+ kw_CHOICE = 269,
+ kw_CLASS = 270,
+ kw_COMPONENT = 271,
+ kw_COMPONENTS = 272,
+ kw_CONSTRAINED = 273,
+ kw_CONTAINING = 274,
+ kw_DEFAULT = 275,
+ kw_DEFINITIONS = 276,
+ kw_EMBEDDED = 277,
+ kw_ENCODED = 278,
+ kw_END = 279,
+ kw_ENUMERATED = 280,
+ kw_EXCEPT = 281,
+ kw_EXPLICIT = 282,
+ kw_EXPORTS = 283,
+ kw_EXTENSIBILITY = 284,
+ kw_EXTERNAL = 285,
+ kw_FALSE = 286,
+ kw_FROM = 287,
+ kw_GeneralString = 288,
+ kw_GeneralizedTime = 289,
+ kw_GraphicString = 290,
+ kw_IA5String = 291,
+ kw_IDENTIFIER = 292,
+ kw_IMPLICIT = 293,
+ kw_IMPLIED = 294,
+ kw_IMPORTS = 295,
+ kw_INCLUDES = 296,
+ kw_INSTANCE = 297,
+ kw_INTEGER = 298,
+ kw_INTERSECTION = 299,
+ kw_ISO646String = 300,
+ kw_MAX = 301,
+ kw_MIN = 302,
+ kw_MINUS_INFINITY = 303,
+ kw_NULL = 304,
+ kw_NumericString = 305,
+ kw_OBJECT = 306,
+ kw_OCTET = 307,
+ kw_OF = 308,
+ kw_OPTIONAL = 309,
+ kw_ObjectDescriptor = 310,
+ kw_PATTERN = 311,
+ kw_PDV = 312,
+ kw_PLUS_INFINITY = 313,
+ kw_PRESENT = 314,
+ kw_PRIVATE = 315,
+ kw_PrintableString = 316,
+ kw_REAL = 317,
+ kw_RELATIVE_OID = 318,
+ kw_SEQUENCE = 319,
+ kw_SET = 320,
+ kw_SIZE = 321,
+ kw_STRING = 322,
+ kw_SYNTAX = 323,
+ kw_T61String = 324,
+ kw_TAGS = 325,
+ kw_TRUE = 326,
+ kw_TYPE_IDENTIFIER = 327,
+ kw_TeletexString = 328,
+ kw_UNION = 329,
+ kw_UNIQUE = 330,
+ kw_UNIVERSAL = 331,
+ kw_UTCTime = 332,
+ kw_UTF8String = 333,
+ kw_UniversalString = 334,
+ kw_VideotexString = 335,
+ kw_VisibleString = 336,
+ kw_WITH = 337,
+ RANGE = 338,
+ EEQUAL = 339,
+ ELLIPSIS = 340,
+ IDENTIFIER = 341,
+ referencename = 342,
+ STRING = 343,
+ NUMBER = 344
+ };
+#endif
+/* Tokens. */
+#define kw_ABSENT 258
+#define kw_ABSTRACT_SYNTAX 259
+#define kw_ALL 260
+#define kw_APPLICATION 261
+#define kw_AUTOMATIC 262
+#define kw_BEGIN 263
+#define kw_BIT 264
+#define kw_BMPString 265
+#define kw_BOOLEAN 266
+#define kw_BY 267
+#define kw_CHARACTER 268
+#define kw_CHOICE 269
+#define kw_CLASS 270
+#define kw_COMPONENT 271
+#define kw_COMPONENTS 272
+#define kw_CONSTRAINED 273
+#define kw_CONTAINING 274
+#define kw_DEFAULT 275
+#define kw_DEFINITIONS 276
+#define kw_EMBEDDED 277
+#define kw_ENCODED 278
+#define kw_END 279
+#define kw_ENUMERATED 280
+#define kw_EXCEPT 281
+#define kw_EXPLICIT 282
+#define kw_EXPORTS 283
+#define kw_EXTENSIBILITY 284
+#define kw_EXTERNAL 285
+#define kw_FALSE 286
+#define kw_FROM 287
+#define kw_GeneralString 288
+#define kw_GeneralizedTime 289
+#define kw_GraphicString 290
+#define kw_IA5String 291
+#define kw_IDENTIFIER 292
+#define kw_IMPLICIT 293
+#define kw_IMPLIED 294
+#define kw_IMPORTS 295
+#define kw_INCLUDES 296
+#define kw_INSTANCE 297
+#define kw_INTEGER 298
+#define kw_INTERSECTION 299
+#define kw_ISO646String 300
+#define kw_MAX 301
+#define kw_MIN 302
+#define kw_MINUS_INFINITY 303
+#define kw_NULL 304
+#define kw_NumericString 305
+#define kw_OBJECT 306
+#define kw_OCTET 307
+#define kw_OF 308
+#define kw_OPTIONAL 309
+#define kw_ObjectDescriptor 310
+#define kw_PATTERN 311
+#define kw_PDV 312
+#define kw_PLUS_INFINITY 313
+#define kw_PRESENT 314
+#define kw_PRIVATE 315
+#define kw_PrintableString 316
+#define kw_REAL 317
+#define kw_RELATIVE_OID 318
+#define kw_SEQUENCE 319
+#define kw_SET 320
+#define kw_SIZE 321
+#define kw_STRING 322
+#define kw_SYNTAX 323
+#define kw_T61String 324
+#define kw_TAGS 325
+#define kw_TRUE 326
+#define kw_TYPE_IDENTIFIER 327
+#define kw_TeletexString 328
+#define kw_UNION 329
+#define kw_UNIQUE 330
+#define kw_UNIVERSAL 331
+#define kw_UTCTime 332
+#define kw_UTF8String 333
+#define kw_UniversalString 334
+#define kw_VideotexString 335
+#define kw_VisibleString 336
+#define kw_WITH 337
+#define RANGE 338
+#define EEQUAL 339
+#define ELLIPSIS 340
+#define IDENTIFIER 341
+#define referencename 342
+#define STRING 343
+#define NUMBER 344
+
+
+
+
+/* Copy the first part of user declarations. */
+#line 36 "heimdal/lib/asn1/parse.y"
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "symbol.h"
+#include "lex.h"
+#include "gen_locl.h"
+#include "der.h"
+
+RCSID("$Id$");
+
+static Type *new_type (Typetype t);
+static struct constraint_spec *new_constraint_spec(enum ctype);
+static Type *new_tag(int tagclass, int tagvalue, int tagenv, Type *oldtype);
+void yyerror (const char *);
+static struct objid *new_objid(const char *label, int value);
+static void add_oid_to_tail(struct objid *, struct objid *);
+static void fix_labels(Symbol *s);
+
+struct string_list {
+ char *string;
+ struct string_list *next;
+};
+
+
+
+/* Enabling traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+
+/* Enabling verbose error messages. */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 0
+#endif
+
+/* Enabling the token table. */
+#ifndef YYTOKEN_TABLE
+# define YYTOKEN_TABLE 0
+#endif
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+#line 65 "heimdal/lib/asn1/parse.y"
+{
+ int constant;
+ struct value *value;
+ struct range *range;
+ char *name;
+ Type *type;
+ Member *member;
+ struct objid *objid;
+ char *defval;
+ struct string_list *sl;
+ struct tagtype tag;
+ struct memhead *members;
+ struct constraint_spec *constraint_spec;
+}
+/* Line 187 of yacc.c. */
+#line 318 "heimdal/lib/asn1/parse.y"
+ YYSTYPE;
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+
+
+/* Copy the second part of user declarations. */
+
+
+/* Line 216 of yacc.c. */
+#line 331 "heimdal/lib/asn1/parse.y"
+
+#ifdef short
+# undef short
+#endif
+
+#ifdef YYTYPE_UINT8
+typedef YYTYPE_UINT8 yytype_uint8;
+#else
+typedef unsigned char yytype_uint8;
+#endif
+
+#ifdef YYTYPE_INT8
+typedef YYTYPE_INT8 yytype_int8;
+#elif (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+typedef signed char yytype_int8;
+#else
+typedef short int yytype_int8;
+#endif
+
+#ifdef YYTYPE_UINT16
+typedef YYTYPE_UINT16 yytype_uint16;
+#else
+typedef unsigned short int yytype_uint16;
+#endif
+
+#ifdef YYTYPE_INT16
+typedef YYTYPE_INT16 yytype_int16;
+#else
+typedef short int yytype_int16;
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+# define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+# define YYSIZE_T size_t
+# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+# else
+# define YYSIZE_T unsigned int
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+
+#ifndef YY_
+# if YYENABLE_NLS
+# if ENABLE_NLS
+# include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+# define YY_(msgid) dgettext ("bison-runtime", msgid)
+# endif
+# endif
+# ifndef YY_
+# define YY_(msgid) msgid
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E. */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(e) ((void) (e))
+#else
+# define YYUSE(e) /* empty */
+#endif
+
+/* Identity function, used to suppress warnings about constant conditions. */
+#ifndef lint
+# define YYID(n) (n)
+#else
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static int
+YYID (int i)
+#else
+static int
+YYID (i)
+ int i;
+#endif
+{
+ return i;
+}
+#endif
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols. */
+
+# ifdef YYSTACK_USE_ALLOCA
+# if YYSTACK_USE_ALLOCA
+# ifdef __GNUC__
+# define YYSTACK_ALLOC __builtin_alloca
+# elif defined __BUILTIN_VA_ARG_INCR
+# include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+# elif defined _AIX
+# define YYSTACK_ALLOC __alloca
+# elif defined _MSC_VER
+# include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+# define alloca _alloca
+# else
+# define YYSTACK_ALLOC alloca
+# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef _STDLIB_H
+# define _STDLIB_H 1
+# endif
+# endif
+# endif
+# endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+ /* Pacify GCC's `empty if-body' warning. */
+# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0))
+# ifndef YYSTACK_ALLOC_MAXIMUM
+ /* The OS might guarantee only one guard page at the bottom of the stack,
+ and a page size can be as small as 4096 bytes. So we cannot safely
+ invoke alloca (N) if N exceeds 4096. Use a slightly smaller number
+ to allow for a few compiler-allocated temporary stack slots. */
+# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+# endif
+# else
+# define YYSTACK_ALLOC YYMALLOC
+# define YYSTACK_FREE YYFREE
+# ifndef YYSTACK_ALLOC_MAXIMUM
+# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+# endif
+# if (defined __cplusplus && ! defined _STDLIB_H \
+ && ! ((defined YYMALLOC || defined malloc) \
+ && (defined YYFREE || defined free)))
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef _STDLIB_H
+# define _STDLIB_H 1
+# endif
+# endif
+# ifndef YYMALLOC
+# define YYMALLOC malloc
+# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# ifndef YYFREE
+# define YYFREE free
+# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+ && (! defined __cplusplus \
+ || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member. */
+union yyalloc
+{
+ yytype_int16 yyss;
+ YYSTYPE yyvs;
+ };
+
+/* The size of the maximum gap between one aligned stack and the next. */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+ N elements. */
+# define YYSTACK_BYTES(N) \
+ ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \
+ + YYSTACK_GAP_MAXIMUM)
+
+/* Copy COUNT objects from FROM to TO. The source and destination do
+ not overlap. */
+# ifndef YYCOPY
+# if defined __GNUC__ && 1 < __GNUC__
+# define YYCOPY(To, From, Count) \
+ __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+# else
+# define YYCOPY(To, From, Count) \
+ do \
+ { \
+ YYSIZE_T yyi; \
+ for (yyi = 0; yyi < (Count); yyi++) \
+ (To)[yyi] = (From)[yyi]; \
+ } \
+ while (YYID (0))
+# endif
+# endif
+
+/* Relocate STACK from its old location to the new one. The
+ local variables YYSIZE and YYSTACKSIZE give the old and new number of
+ elements in the stack, and YYPTR gives the new location of the
+ stack. Advance YYPTR to a properly aligned location for the next
+ stack. */
+# define YYSTACK_RELOCATE(Stack) \
+ do \
+ { \
+ YYSIZE_T yynewbytes; \
+ YYCOPY (&yyptr->Stack, Stack, yysize); \
+ Stack = &yyptr->Stack; \
+ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+ yyptr += yynewbytes / sizeof (*yyptr); \
+ } \
+ while (YYID (0))
+
+#endif
+
+/* YYFINAL -- State number of the termination state. */
+#define YYFINAL 6
+/* YYLAST -- Last index in YYTABLE. */
+#define YYLAST 195
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS 98
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS 68
+/* YYNRULES -- Number of rules. */
+#define YYNRULES 136
+/* YYNRULES -- Number of states. */
+#define YYNSTATES 214
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
+#define YYUNDEFTOK 2
+#define YYMAXUTOK 344
+
+#define YYTRANSLATE(YYX) \
+ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */
+static const yytype_uint8 yytranslate[] =
+{
+ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 92, 93, 2, 2, 91, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 90,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 96, 2, 97, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 94, 2, 95, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
+ 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
+ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
+ 65, 66, 67, 68, 69, 70, 71, 72, 73, 74,
+ 75, 76, 77, 78, 79, 80, 81, 82, 83, 84,
+ 85, 86, 87, 88, 89
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+ YYRHS. */
+static const yytype_uint16 yyprhs[] =
+{
+ 0, 0, 3, 13, 16, 19, 22, 23, 26, 27,
+ 30, 31, 35, 36, 38, 39, 41, 44, 49, 51,
+ 54, 56, 58, 62, 64, 68, 70, 72, 74, 76,
+ 78, 80, 82, 84, 86, 88, 90, 92, 94, 96,
+ 98, 100, 102, 104, 110, 116, 122, 126, 128, 131,
+ 136, 138, 142, 146, 151, 156, 158, 161, 167, 170,
+ 174, 176, 177, 180, 185, 189, 194, 199, 203, 207,
+ 212, 214, 216, 218, 220, 222, 225, 229, 231, 233,
+ 235, 238, 242, 248, 253, 257, 262, 263, 265, 267,
+ 269, 270, 272, 274, 279, 281, 283, 285, 287, 289,
+ 291, 293, 295, 297, 301, 305, 308, 310, 313, 317,
+ 319, 323, 328, 330, 331, 335, 336, 339, 344, 346,
+ 348, 350, 352, 354, 356, 358, 360, 362, 364, 366,
+ 368, 370, 372, 374, 376, 378, 380
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS. */
+static const yytype_int16 yyrhs[] =
+{
+ 99, 0, -1, 86, 151, 21, 100, 101, 84, 8,
+ 102, 24, -1, 27, 70, -1, 38, 70, -1, 7,
+ 70, -1, -1, 29, 39, -1, -1, 103, 107, -1,
+ -1, 40, 104, 90, -1, -1, 105, -1, -1, 106,
+ -1, 105, 106, -1, 109, 32, 86, 151, -1, 108,
+ -1, 108, 107, -1, 110, -1, 143, -1, 86, 91,
+ 109, -1, 86, -1, 86, 84, 111, -1, 112, -1,
+ 130, -1, 133, -1, 120, -1, 113, -1, 144, -1,
+ 129, -1, 118, -1, 115, -1, 123, -1, 121, -1,
+ 122, -1, 125, -1, 126, -1, 127, -1, 128, -1,
+ 139, -1, 11, -1, 92, 155, 83, 155, 93, -1,
+ 92, 155, 83, 46, 93, -1, 92, 47, 83, 155,
+ 93, -1, 92, 155, 93, -1, 43, -1, 43, 114,
+ -1, 43, 94, 116, 95, -1, 117, -1, 116, 91,
+ 117, -1, 116, 91, 85, -1, 86, 92, 163, 93,
+ -1, 25, 94, 119, 95, -1, 116, -1, 9, 67,
+ -1, 9, 67, 94, 149, 95, -1, 51, 37, -1,
+ 52, 67, 124, -1, 49, -1, -1, 66, 114, -1,
+ 64, 94, 146, 95, -1, 64, 94, 95, -1, 64,
+ 124, 53, 111, -1, 65, 94, 146, 95, -1, 65,
+ 94, 95, -1, 65, 53, 111, -1, 14, 94, 146,
+ 95, -1, 131, -1, 132, -1, 86, -1, 34, -1,
+ 77, -1, 111, 134, -1, 92, 135, 93, -1, 136,
+ -1, 137, -1, 138, -1, 19, 111, -1, 23, 12,
+ 155, -1, 19, 111, 23, 12, 155, -1, 18, 12,
+ 94, 95, -1, 140, 142, 111, -1, 96, 141, 89,
+ 97, -1, -1, 76, -1, 6, -1, 60, -1, -1,
+ 27, -1, 38, -1, 86, 111, 84, 155, -1, 145,
+ -1, 33, -1, 78, -1, 61, -1, 81, -1, 36,
+ -1, 10, -1, 79, -1, 148, -1, 146, 91, 148,
+ -1, 146, 91, 85, -1, 86, 111, -1, 147, -1,
+ 147, 54, -1, 147, 20, 155, -1, 150, -1, 149,
+ 91, 150, -1, 86, 92, 89, 93, -1, 152, -1,
+ -1, 94, 153, 95, -1, -1, 154, 153, -1, 86,
+ 92, 89, 93, -1, 86, -1, 89, -1, 156, -1,
+ 157, -1, 161, -1, 160, -1, 162, -1, 165, -1,
+ 164, -1, 158, -1, 159, -1, 86, -1, 88, -1,
+ 71, -1, 31, -1, 163, -1, 89, -1, 49, -1,
+ 152, -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
+static const yytype_uint16 yyrline[] =
+{
+ 0, 233, 233, 240, 241, 243, 245, 248, 250, 253,
+ 254, 257, 258, 261, 262, 265, 266, 269, 280, 281,
+ 284, 285, 288, 294, 302, 312, 313, 314, 317, 318,
+ 319, 320, 321, 322, 323, 324, 325, 326, 327, 328,
+ 329, 330, 333, 340, 350, 358, 366, 377, 382, 388,
+ 396, 402, 407, 411, 424, 432, 435, 442, 450, 456,
+ 465, 473, 474, 479, 485, 493, 502, 508, 516, 524,
+ 531, 532, 535, 546, 551, 558, 574, 580, 583, 584,
+ 587, 593, 601, 611, 617, 630, 639, 642, 646, 650,
+ 657, 660, 664, 671, 682, 685, 690, 695, 700, 705,
+ 710, 715, 723, 729, 734, 745, 756, 762, 768, 776,
+ 782, 789, 802, 803, 806, 813, 816, 827, 831, 842,
+ 848, 849, 852, 853, 854, 855, 856, 859, 862, 865,
+ 876, 884, 890, 898, 906, 909, 914
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+ First, the terminals, then, starting at YYNTOKENS, nonterminals. */
+static const char *const yytname[] =
+{
+ "$end", "error", "$undefined", "kw_ABSENT", "kw_ABSTRACT_SYNTAX",
+ "kw_ALL", "kw_APPLICATION", "kw_AUTOMATIC", "kw_BEGIN", "kw_BIT",
+ "kw_BMPString", "kw_BOOLEAN", "kw_BY", "kw_CHARACTER", "kw_CHOICE",
+ "kw_CLASS", "kw_COMPONENT", "kw_COMPONENTS", "kw_CONSTRAINED",
+ "kw_CONTAINING", "kw_DEFAULT", "kw_DEFINITIONS", "kw_EMBEDDED",
+ "kw_ENCODED", "kw_END", "kw_ENUMERATED", "kw_EXCEPT", "kw_EXPLICIT",
+ "kw_EXPORTS", "kw_EXTENSIBILITY", "kw_EXTERNAL", "kw_FALSE", "kw_FROM",
+ "kw_GeneralString", "kw_GeneralizedTime", "kw_GraphicString",
+ "kw_IA5String", "kw_IDENTIFIER", "kw_IMPLICIT", "kw_IMPLIED",
+ "kw_IMPORTS", "kw_INCLUDES", "kw_INSTANCE", "kw_INTEGER",
+ "kw_INTERSECTION", "kw_ISO646String", "kw_MAX", "kw_MIN",
+ "kw_MINUS_INFINITY", "kw_NULL", "kw_NumericString", "kw_OBJECT",
+ "kw_OCTET", "kw_OF", "kw_OPTIONAL", "kw_ObjectDescriptor", "kw_PATTERN",
+ "kw_PDV", "kw_PLUS_INFINITY", "kw_PRESENT", "kw_PRIVATE",
+ "kw_PrintableString", "kw_REAL", "kw_RELATIVE_OID", "kw_SEQUENCE",
+ "kw_SET", "kw_SIZE", "kw_STRING", "kw_SYNTAX", "kw_T61String", "kw_TAGS",
+ "kw_TRUE", "kw_TYPE_IDENTIFIER", "kw_TeletexString", "kw_UNION",
+ "kw_UNIQUE", "kw_UNIVERSAL", "kw_UTCTime", "kw_UTF8String",
+ "kw_UniversalString", "kw_VideotexString", "kw_VisibleString", "kw_WITH",
+ "RANGE", "EEQUAL", "ELLIPSIS", "IDENTIFIER", "referencename", "STRING",
+ "NUMBER", "';'", "','", "'('", "')'", "'{'", "'}'", "'['", "']'",
+ "$accept", "ModuleDefinition", "TagDefault", "ExtensionDefault",
+ "ModuleBody", "Imports", "SymbolsImported", "SymbolsFromModuleList",
+ "SymbolsFromModule", "AssignmentList", "Assignment", "referencenames",
+ "TypeAssignment", "Type", "BuiltinType", "BooleanType", "range",
+ "IntegerType", "NamedNumberList", "NamedNumber", "EnumeratedType",
+ "Enumerations", "BitStringType", "ObjectIdentifierType",
+ "OctetStringType", "NullType", "size", "SequenceType", "SequenceOfType",
+ "SetType", "SetOfType", "ChoiceType", "ReferencedType", "DefinedType",
+ "UsefulType", "ConstrainedType", "Constraint", "ConstraintSpec",
+ "GeneralConstraint", "ContentsConstraint", "UserDefinedConstraint",
+ "TaggedType", "Tag", "Class", "tagenv", "ValueAssignment",
+ "CharacterStringType", "RestrictedCharactedStringType",
+ "ComponentTypeList", "NamedType", "ComponentType", "NamedBitList",
+ "NamedBit", "objid_opt", "objid", "objid_list", "objid_element", "Value",
+ "BuiltinValue", "ReferencedValue", "DefinedValue", "Valuereference",
+ "CharacterStringValue", "BooleanValue", "IntegerValue", "SignedNumber",
+ "NullValue", "ObjectIdentifierValue", 0
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+ token YYLEX-NUM. */
+static const yytype_uint16 yytoknum[] =
+{
+ 0, 256, 257, 258, 259, 260, 261, 262, 263, 264,
+ 265, 266, 267, 268, 269, 270, 271, 272, 273, 274,
+ 275, 276, 277, 278, 279, 280, 281, 282, 283, 284,
+ 285, 286, 287, 288, 289, 290, 291, 292, 293, 294,
+ 295, 296, 297, 298, 299, 300, 301, 302, 303, 304,
+ 305, 306, 307, 308, 309, 310, 311, 312, 313, 314,
+ 315, 316, 317, 318, 319, 320, 321, 322, 323, 324,
+ 325, 326, 327, 328, 329, 330, 331, 332, 333, 334,
+ 335, 336, 337, 338, 339, 340, 341, 342, 343, 344,
+ 59, 44, 40, 41, 123, 125, 91, 93
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
+static const yytype_uint8 yyr1[] =
+{
+ 0, 98, 99, 100, 100, 100, 100, 101, 101, 102,
+ 102, 103, 103, 104, 104, 105, 105, 106, 107, 107,
+ 108, 108, 109, 109, 110, 111, 111, 111, 112, 112,
+ 112, 112, 112, 112, 112, 112, 112, 112, 112, 112,
+ 112, 112, 113, 114, 114, 114, 114, 115, 115, 115,
+ 116, 116, 116, 117, 118, 119, 120, 120, 121, 122,
+ 123, 124, 124, 125, 125, 126, 127, 127, 128, 129,
+ 130, 130, 131, 132, 132, 133, 134, 135, 136, 136,
+ 137, 137, 137, 138, 139, 140, 141, 141, 141, 141,
+ 142, 142, 142, 143, 144, 145, 145, 145, 145, 145,
+ 145, 145, 146, 146, 146, 147, 148, 148, 148, 149,
+ 149, 150, 151, 151, 152, 153, 153, 154, 154, 154,
+ 155, 155, 156, 156, 156, 156, 156, 157, 158, 159,
+ 160, 161, 161, 162, 163, 164, 165
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
+static const yytype_uint8 yyr2[] =
+{
+ 0, 2, 9, 2, 2, 2, 0, 2, 0, 2,
+ 0, 3, 0, 1, 0, 1, 2, 4, 1, 2,
+ 1, 1, 3, 1, 3, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 5, 5, 5, 3, 1, 2, 4,
+ 1, 3, 3, 4, 4, 1, 2, 5, 2, 3,
+ 1, 0, 2, 4, 3, 4, 4, 3, 3, 4,
+ 1, 1, 1, 1, 1, 2, 3, 1, 1, 1,
+ 2, 3, 5, 4, 3, 4, 0, 1, 1, 1,
+ 0, 1, 1, 4, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 3, 3, 2, 1, 2, 3, 1,
+ 3, 4, 1, 0, 3, 0, 2, 4, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1
+};
+
+/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
+ STATE-NUM when YYTABLE doesn't specify something else to do. Zero
+ means the default is an error. */
+static const yytype_uint8 yydefact[] =
+{
+ 0, 113, 0, 115, 0, 112, 1, 118, 119, 0,
+ 115, 6, 0, 114, 116, 0, 0, 0, 8, 0,
+ 5, 3, 4, 0, 0, 117, 7, 0, 10, 14,
+ 0, 0, 23, 0, 13, 15, 0, 2, 0, 9,
+ 18, 20, 21, 0, 11, 16, 0, 0, 100, 42,
+ 0, 0, 95, 73, 99, 47, 60, 0, 0, 97,
+ 61, 0, 74, 96, 101, 98, 0, 72, 86, 0,
+ 25, 29, 33, 32, 28, 35, 36, 34, 37, 38,
+ 39, 40, 31, 26, 70, 71, 27, 41, 90, 30,
+ 94, 19, 22, 113, 56, 0, 0, 0, 0, 48,
+ 58, 61, 0, 0, 0, 0, 0, 24, 88, 89,
+ 87, 0, 0, 0, 75, 91, 92, 0, 17, 0,
+ 0, 0, 106, 102, 0, 55, 50, 0, 132, 0,
+ 135, 131, 129, 130, 134, 136, 0, 120, 121, 127,
+ 128, 123, 122, 124, 133, 126, 125, 0, 59, 62,
+ 64, 0, 0, 68, 67, 0, 0, 93, 0, 0,
+ 0, 0, 77, 78, 79, 84, 0, 0, 109, 105,
+ 0, 69, 0, 107, 0, 0, 54, 0, 0, 46,
+ 49, 63, 65, 66, 85, 0, 80, 0, 76, 0,
+ 0, 57, 104, 103, 108, 0, 52, 51, 0, 0,
+ 0, 0, 0, 81, 0, 110, 53, 45, 44, 43,
+ 83, 0, 111, 82
+};
+
+/* YYDEFGOTO[NTERM-NUM]. */
+static const yytype_int16 yydefgoto[] =
+{
+ -1, 2, 18, 24, 30, 31, 33, 34, 35, 39,
+ 40, 36, 41, 69, 70, 71, 99, 72, 125, 126,
+ 73, 127, 74, 75, 76, 77, 104, 78, 79, 80,
+ 81, 82, 83, 84, 85, 86, 114, 161, 162, 163,
+ 164, 87, 88, 111, 117, 42, 89, 90, 121, 122,
+ 123, 167, 168, 4, 135, 9, 10, 136, 137, 138,
+ 139, 140, 141, 142, 143, 144, 145, 146
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+ STATE-NUM. */
+#define YYPACT_NINF -113
+static const yytype_int16 yypact[] =
+{
+ -74, -67, 38, -69, 23, -113, -113, -44, -113, -41,
+ -69, 4, -26, -113, -113, -3, 1, 10, 52, -10,
+ -113, -113, -113, 45, 13, -113, -113, 77, -35, 15,
+ 64, 19, 17, 20, 15, -113, 85, -113, 25, -113,
+ 19, -113, -113, 15, -113, -113, 27, 47, -113, -113,
+ 26, 29, -113, -113, -113, -30, -113, 89, 61, -113,
+ -57, -47, -113, -113, -113, -113, 82, -113, -4, -68,
+ -113, -113, -113, -113, -113, -113, -113, -113, -113, -113,
+ -113, -113, -113, -113, -113, -113, -113, -113, -17, -113,
+ -113, -113, -113, -67, 35, 33, 46, 51, 46, -113,
+ -113, 69, 44, -73, 88, 82, -72, 56, -113, -113,
+ -113, 49, 93, 7, -113, -113, -113, 82, -113, 58,
+ 82, -76, -13, -113, 57, 59, -113, 60, -113, 68,
+ -113, -113, -113, -113, -113, -113, -75, -113, -113, -113,
+ -113, -113, -113, -113, -113, -113, -113, -63, -113, -113,
+ -113, -62, 82, 56, -113, -46, 65, -113, 141, 82,
+ 142, 63, -113, -113, -113, 56, 66, -38, -113, 56,
+ -16, -113, 93, -113, 76, -7, -113, 93, 81, -113,
+ -113, -113, 56, -113, -113, 72, -19, 93, -113, 83,
+ 58, -113, -113, -113, -113, 78, -113, -113, 80, 84,
+ 87, 62, 162, -113, 90, -113, -113, -113, -113, -113,
+ -113, 93, -113, -113
+};
+
+/* YYPGOTO[NTERM-NUM]. */
+static const yytype_int16 yypgoto[] =
+{
+ -113, -113, -113, -113, -113, -113, -113, -113, 150, 136,
+ -113, 143, -113, -65, -113, -113, 86, -113, 91, 16,
+ -113, -113, -113, -113, -113, -113, 92, -113, -113, -113,
+ -113, -113, -113, -113, -113, -113, -113, -113, -113, -113,
+ -113, -113, -113, -113, -113, -113, -113, -113, -60, -113,
+ 22, -113, -5, 97, 2, 184, -113, -112, -113, -113,
+ -113, -113, -113, -113, -113, 21, -113, -113
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If
+ positive, shift that token. If negative, reduce the rule which
+ number is the opposite. If zero, do what YYDEFACT says.
+ If YYTABLE_NINF, syntax error. */
+#define YYTABLE_NINF -13
+static const yytype_int16 yytable[] =
+{
+ 157, 107, 108, 5, 202, 29, 105, 172, 178, 102,
+ 115, 15, 1, 120, 120, 170, 112, 7, 179, 171,
+ 8, 116, 150, 154, 113, 158, 159, 3, 175, 170,
+ 160, 16, 180, 181, 47, 48, 49, 103, 6, 50,
+ 153, 173, 17, 151, 11, 170, 155, 106, 12, 183,
+ 51, -12, 165, 190, 13, 169, 109, 191, 52, 53,
+ 194, 54, 97, 19, 98, 198, 200, 20, 55, 192,
+ 120, 21, 110, 113, 56, 203, 57, 58, 196, 124,
+ 22, 23, 128, 25, 26, 28, 59, 182, 37, 60,
+ 61, 47, 48, 49, 186, 5, 50, 27, 129, 213,
+ 130, 32, 62, 63, 64, 38, 65, 51, 43, 66,
+ 44, 67, 128, 93, 94, 52, 53, 46, 54, 120,
+ 95, 68, 131, 96, 128, 55, 100, 199, 101, 119,
+ 130, 56, 124, 57, 58, 102, 97, 132, 156, 133,
+ 134, 152, 130, 59, 166, 3, 60, 61, 113, 174,
+ 175, 177, 131, 185, 187, 176, 188, 210, 189, 62,
+ 63, 64, 184, 65, 131, 134, 201, 132, 67, 133,
+ 134, 206, 204, 207, 211, 3, 91, 208, 68, 132,
+ 209, 133, 134, 212, 45, 205, 92, 3, 149, 147,
+ 118, 197, 193, 148, 14, 195
+};
+
+static const yytype_uint8 yycheck[] =
+{
+ 112, 66, 6, 1, 23, 40, 53, 20, 83, 66,
+ 27, 7, 86, 86, 86, 91, 84, 86, 93, 95,
+ 89, 38, 95, 95, 92, 18, 19, 94, 91, 91,
+ 23, 27, 95, 95, 9, 10, 11, 94, 0, 14,
+ 105, 54, 38, 103, 21, 91, 106, 94, 92, 95,
+ 25, 86, 117, 91, 95, 120, 60, 95, 33, 34,
+ 172, 36, 92, 89, 94, 177, 178, 70, 43, 85,
+ 86, 70, 76, 92, 49, 187, 51, 52, 85, 86,
+ 70, 29, 31, 93, 39, 8, 61, 152, 24, 64,
+ 65, 9, 10, 11, 159, 93, 14, 84, 47, 211,
+ 49, 86, 77, 78, 79, 86, 81, 25, 91, 84,
+ 90, 86, 31, 86, 67, 33, 34, 32, 36, 86,
+ 94, 96, 71, 94, 31, 43, 37, 46, 67, 94,
+ 49, 49, 86, 51, 52, 66, 92, 86, 89, 88,
+ 89, 53, 49, 61, 86, 94, 64, 65, 92, 92,
+ 91, 83, 71, 12, 12, 95, 93, 95, 92, 77,
+ 78, 79, 97, 81, 71, 89, 94, 86, 86, 88,
+ 89, 93, 89, 93, 12, 94, 40, 93, 96, 86,
+ 93, 88, 89, 93, 34, 190, 43, 94, 102, 98,
+ 93, 175, 170, 101, 10, 174
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+ symbol of state STATE-NUM. */
+static const yytype_uint8 yystos[] =
+{
+ 0, 86, 99, 94, 151, 152, 0, 86, 89, 153,
+ 154, 21, 92, 95, 153, 7, 27, 38, 100, 89,
+ 70, 70, 70, 29, 101, 93, 39, 84, 8, 40,
+ 102, 103, 86, 104, 105, 106, 109, 24, 86, 107,
+ 108, 110, 143, 91, 90, 106, 32, 9, 10, 11,
+ 14, 25, 33, 34, 36, 43, 49, 51, 52, 61,
+ 64, 65, 77, 78, 79, 81, 84, 86, 96, 111,
+ 112, 113, 115, 118, 120, 121, 122, 123, 125, 126,
+ 127, 128, 129, 130, 131, 132, 133, 139, 140, 144,
+ 145, 107, 109, 86, 67, 94, 94, 92, 94, 114,
+ 37, 67, 66, 94, 124, 53, 94, 111, 6, 60,
+ 76, 141, 84, 92, 134, 27, 38, 142, 151, 94,
+ 86, 146, 147, 148, 86, 116, 117, 119, 31, 47,
+ 49, 71, 86, 88, 89, 152, 155, 156, 157, 158,
+ 159, 160, 161, 162, 163, 164, 165, 116, 124, 114,
+ 95, 146, 53, 111, 95, 146, 89, 155, 18, 19,
+ 23, 135, 136, 137, 138, 111, 86, 149, 150, 111,
+ 91, 95, 20, 54, 92, 91, 95, 83, 83, 93,
+ 95, 95, 111, 95, 97, 12, 111, 12, 93, 92,
+ 91, 95, 85, 148, 155, 163, 85, 117, 155, 46,
+ 155, 94, 23, 155, 89, 150, 93, 93, 93, 93,
+ 95, 12, 93, 155
+};
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY (-2)
+#define YYEOF 0
+
+#define YYACCEPT goto yyacceptlab
+#define YYABORT goto yyabortlab
+#define YYERROR goto yyerrorlab
+
+
+/* Like YYERROR except do call yyerror. This remains here temporarily
+ to ease the transition to the new meaning of YYERROR, for GCC.
+ Once GCC version 2 has supplanted version 1, this can go. */
+
+#define YYFAIL goto yyerrlab
+
+#define YYRECOVERING() (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value) \
+do \
+ if (yychar == YYEMPTY && yylen == 1) \
+ { \
+ yychar = (Token); \
+ yylval = (Value); \
+ yytoken = YYTRANSLATE (yychar); \
+ YYPOPSTACK (1); \
+ goto yybackup; \
+ } \
+ else \
+ { \
+ yyerror (YY_("syntax error: cannot back up")); \
+ YYERROR; \
+ } \
+while (YYID (0))
+
+
+#define YYTERROR 1
+#define YYERRCODE 256
+
+
+/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
+ If N is 0, then set CURRENT to the empty location which ends
+ the previous symbol: RHS[0] (always defined). */
+
+#define YYRHSLOC(Rhs, K) ((Rhs)[K])
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N) \
+ do \
+ if (YYID (N)) \
+ { \
+ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \
+ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \
+ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \
+ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \
+ } \
+ else \
+ { \
+ (Current).first_line = (Current).last_line = \
+ YYRHSLOC (Rhs, 0).last_line; \
+ (Current).first_column = (Current).last_column = \
+ YYRHSLOC (Rhs, 0).last_column; \
+ } \
+ while (YYID (0))
+#endif
+
+
+/* YY_LOCATION_PRINT -- Print the location on the stream.
+ This macro was not mandated originally: define only if we know
+ we won't break user code: when these are the locations we know. */
+
+#ifndef YY_LOCATION_PRINT
+# if YYLTYPE_IS_TRIVIAL
+# define YY_LOCATION_PRINT(File, Loc) \
+ fprintf (File, "%d.%d-%d.%d", \
+ (Loc).first_line, (Loc).first_column, \
+ (Loc).last_line, (Loc).last_column)
+# else
+# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+# endif
+#endif
+
+
+/* YYLEX -- calling `yylex' with the right arguments. */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (YYLEX_PARAM)
+#else
+# define YYLEX yylex ()
+#endif
+
+/* Enable debugging if requested. */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+# include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+# define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args) \
+do { \
+ if (yydebug) \
+ YYFPRINTF Args; \
+} while (YYID (0))
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \
+do { \
+ if (yydebug) \
+ { \
+ YYFPRINTF (stderr, "%s ", Title); \
+ yy_symbol_print (stderr, \
+ Type, Value); \
+ YYFPRINTF (stderr, "\n"); \
+ } \
+} while (YYID (0))
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+#else
+static void
+yy_symbol_value_print (yyoutput, yytype, yyvaluep)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+#endif
+{
+ if (!yyvaluep)
+ return;
+# ifdef YYPRINT
+ if (yytype < YYNTOKENS)
+ YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# else
+ YYUSE (yyoutput);
+# endif
+ switch (yytype)
+ {
+ default:
+ break;
+ }
+}
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+#else
+static void
+yy_symbol_print (yyoutput, yytype, yyvaluep)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+#endif
+{
+ if (yytype < YYNTOKENS)
+ YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+ else
+ YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+ yy_symbol_value_print (yyoutput, yytype, yyvaluep);
+ YYFPRINTF (yyoutput, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included). |
+`------------------------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_stack_print (yytype_int16 *bottom, yytype_int16 *top)
+#else
+static void
+yy_stack_print (bottom, top)
+ yytype_int16 *bottom;
+ yytype_int16 *top;
+#endif
+{
+ YYFPRINTF (stderr, "Stack now");
+ for (; bottom <= top; ++bottom)
+ YYFPRINTF (stderr, " %d", *bottom);
+ YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top) \
+do { \
+ if (yydebug) \
+ yy_stack_print ((Bottom), (Top)); \
+} while (YYID (0))
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced. |
+`------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_reduce_print (YYSTYPE *yyvsp, int yyrule)
+#else
+static void
+yy_reduce_print (yyvsp, yyrule)
+ YYSTYPE *yyvsp;
+ int yyrule;
+#endif
+{
+ int yynrhs = yyr2[yyrule];
+ int yyi;
+ unsigned long int yylno = yyrline[yyrule];
+ YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+ yyrule - 1, yylno);
+ /* The symbols being reduced. */
+ for (yyi = 0; yyi < yynrhs; yyi++)
+ {
+ fprintf (stderr, " $%d = ", yyi + 1);
+ yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
+ &(yyvsp[(yyi + 1) - (yynrhs)])
+ );
+ fprintf (stderr, "\n");
+ }
+}
+
+# define YY_REDUCE_PRINT(Rule) \
+do { \
+ if (yydebug) \
+ yy_reduce_print (yyvsp, Rule); \
+} while (YYID (0))
+
+/* Nonzero means print parse trace. It is left uninitialized so that
+ multiple parsers can coexist. */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks. */
+#ifndef YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+ if the built-in stack extension method is used).
+
+ Do not make this value too large; the results are undefined if
+ YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+ evaluated with infinite-precision integer arithmetic. */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+# if defined __GLIBC__ && defined _STRING_H
+# define yystrlen strlen
+# else
+/* Return the length of YYSTR. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static YYSIZE_T
+yystrlen (const char *yystr)
+#else
+static YYSIZE_T
+yystrlen (yystr)
+ const char *yystr;
+#endif
+{
+ YYSIZE_T yylen;
+ for (yylen = 0; yystr[yylen]; yylen++)
+ continue;
+ return yylen;
+}
+# endif
+# endif
+
+# ifndef yystpcpy
+# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+# define yystpcpy stpcpy
+# else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+ YYDEST. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static char *
+yystpcpy (char *yydest, const char *yysrc)
+#else
+static char *
+yystpcpy (yydest, yysrc)
+ char *yydest;
+ const char *yysrc;
+#endif
+{
+ char *yyd = yydest;
+ const char *yys = yysrc;
+
+ while ((*yyd++ = *yys++) != '\0')
+ continue;
+
+ return yyd - 1;
+}
+# endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+ quotes and backslashes, so that it's suitable for yyerror. The
+ heuristic is that double-quoting is unnecessary unless the string
+ contains an apostrophe, a comma, or backslash (other than
+ backslash-backslash). YYSTR is taken from yytname. If YYRES is
+ null, do not copy; instead, return the length of what the result
+ would have been. */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+ if (*yystr == '"')
+ {
+ YYSIZE_T yyn = 0;
+ char const *yyp = yystr;
+
+ for (;;)
+ switch (*++yyp)
+ {
+ case '\'':
+ case ',':
+ goto do_not_strip_quotes;
+
+ case '\\':
+ if (*++yyp != '\\')
+ goto do_not_strip_quotes;
+ /* Fall through. */
+ default:
+ if (yyres)
+ yyres[yyn] = *yyp;
+ yyn++;
+ break;
+
+ case '"':
+ if (yyres)
+ yyres[yyn] = '\0';
+ return yyn;
+ }
+ do_not_strip_quotes: ;
+ }
+
+ if (! yyres)
+ return yystrlen (yystr);
+
+ return yystpcpy (yyres, yystr) - yyres;
+}
+# endif
+
+/* Copy into YYRESULT an error message about the unexpected token
+ YYCHAR while in state YYSTATE. Return the number of bytes copied,
+ including the terminating null byte. If YYRESULT is null, do not
+ copy anything; just return the number of bytes that would be
+ copied. As a special case, return 0 if an ordinary "syntax error"
+ message will do. Return YYSIZE_MAXIMUM if overflow occurs during
+ size calculation. */
+static YYSIZE_T
+yysyntax_error (char *yyresult, int yystate, int yychar)
+{
+ int yyn = yypact[yystate];
+
+ if (! (YYPACT_NINF < yyn && yyn <= YYLAST))
+ return 0;
+ else
+ {
+ int yytype = YYTRANSLATE (yychar);
+ YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]);
+ YYSIZE_T yysize = yysize0;
+ YYSIZE_T yysize1;
+ int yysize_overflow = 0;
+ enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+ int yyx;
+
+# if 0
+ /* This is so xgettext sees the translatable formats that are
+ constructed on the fly. */
+ YY_("syntax error, unexpected %s");
+ YY_("syntax error, unexpected %s, expecting %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s or %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s");
+# endif
+ char *yyfmt;
+ char const *yyf;
+ static char const yyunexpected[] = "syntax error, unexpected %s";
+ static char const yyexpecting[] = ", expecting %s";
+ static char const yyor[] = " or %s";
+ char yyformat[sizeof yyunexpected
+ + sizeof yyexpecting - 1
+ + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2)
+ * (sizeof yyor - 1))];
+ char const *yyprefix = yyexpecting;
+
+ /* Start YYX at -YYN if negative to avoid negative indexes in
+ YYCHECK. */
+ int yyxbegin = yyn < 0 ? -yyn : 0;
+
+ /* Stay within bounds of both yycheck and yytname. */
+ int yychecklim = YYLAST - yyn + 1;
+ int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+ int yycount = 1;
+
+ yyarg[0] = yytname[yytype];
+ yyfmt = yystpcpy (yyformat, yyunexpected);
+
+ for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+ {
+ if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+ {
+ yycount = 1;
+ yysize = yysize0;
+ yyformat[sizeof yyunexpected - 1] = '\0';
+ break;
+ }
+ yyarg[yycount++] = yytname[yyx];
+ yysize1 = yysize + yytnamerr (0, yytname[yyx]);
+ yysize_overflow |= (yysize1 < yysize);
+ yysize = yysize1;
+ yyfmt = yystpcpy (yyfmt, yyprefix);
+ yyprefix = yyor;
+ }
+
+ yyf = YY_(yyformat);
+ yysize1 = yysize + yystrlen (yyf);
+ yysize_overflow |= (yysize1 < yysize);
+ yysize = yysize1;
+
+ if (yysize_overflow)
+ return YYSIZE_MAXIMUM;
+
+ if (yyresult)
+ {
+ /* Avoid sprintf, as that infringes on the user's name space.
+ Don't have undefined behavior even if the translation
+ produced a string with the wrong number of "%s"s. */
+ char *yyp = yyresult;
+ int yyi = 0;
+ while ((*yyp = *yyf) != '\0')
+ {
+ if (*yyp == '%' && yyf[1] == 's' && yyi < yycount)
+ {
+ yyp += yytnamerr (yyp, yyarg[yyi++]);
+ yyf += 2;
+ }
+ else
+ {
+ yyp++;
+ yyf++;
+ }
+ }
+ }
+ return yysize;
+ }
+}
+#endif /* YYERROR_VERBOSE */
+
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol. |
+`-----------------------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yydestruct (yymsg, yytype, yyvaluep)
+ const char *yymsg;
+ int yytype;
+ YYSTYPE *yyvaluep;
+#endif
+{
+ YYUSE (yyvaluep);
+
+ if (!yymsg)
+ yymsg = "Deleting";
+ YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+ switch (yytype)
+ {
+
+ default:
+ break;
+ }
+}
+
+
+/* Prevent warnings from -Wmissing-prototypes. */
+
+#ifdef YYPARSE_PARAM
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void *YYPARSE_PARAM);
+#else
+int yyparse ();
+#endif
+#else /* ! YYPARSE_PARAM */
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+
+
+/* The look-ahead symbol. */
+int yychar;
+
+/* The semantic value of the look-ahead symbol. */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far. */
+int yynerrs;
+
+
+
+/*----------.
+| yyparse. |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void *YYPARSE_PARAM)
+#else
+int
+yyparse (YYPARSE_PARAM)
+ void *YYPARSE_PARAM;
+#endif
+#else /* ! YYPARSE_PARAM */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void)
+#else
+int
+yyparse ()
+
+#endif
+#endif
+{
+
+ int yystate;
+ int yyn;
+ int yyresult;
+ /* Number of tokens to shift before error messages enabled. */
+ int yyerrstatus;
+ /* Look-ahead token as an internal (translated) token number. */
+ int yytoken = 0;
+#if YYERROR_VERBOSE
+ /* Buffer for error messages, and its allocated size. */
+ char yymsgbuf[128];
+ char *yymsg = yymsgbuf;
+ YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
+#endif
+
+ /* Three stacks and their tools:
+ `yyss': related to states,
+ `yyvs': related to semantic values,
+ `yyls': related to locations.
+
+ Refer to the stacks thru separate pointers, to allow yyoverflow
+ to reallocate them elsewhere. */
+
+ /* The state stack. */
+ yytype_int16 yyssa[YYINITDEPTH];
+ yytype_int16 *yyss = yyssa;
+ yytype_int16 *yyssp;
+
+ /* The semantic value stack. */
+ YYSTYPE yyvsa[YYINITDEPTH];
+ YYSTYPE *yyvs = yyvsa;
+ YYSTYPE *yyvsp;
+
+
+
+#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N))
+
+ YYSIZE_T yystacksize = YYINITDEPTH;
+
+ /* The variables used to return semantic value and location from the
+ action routines. */
+ YYSTYPE yyval;
+
+
+ /* The number of symbols on the RHS of the reduced rule.
+ Keep to zero when no symbol should be popped. */
+ int yylen = 0;
+
+ YYDPRINTF ((stderr, "Starting parse\n"));
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+
+ /* Initialize stack pointers.
+ Waste one element of value and location stack
+ so that they stay on the same level as the state stack.
+ The wasted elements are never initialized. */
+
+ yyssp = yyss;
+ yyvsp = yyvs;
+
+ goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate. |
+`------------------------------------------------------------*/
+ yynewstate:
+ /* In all cases, when you get here, the value and location stacks
+ have just been pushed. So pushing a state here evens the stacks. */
+ yyssp++;
+
+ yysetstate:
+ *yyssp = yystate;
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ {
+ /* Get the current used size of the three stacks, in elements. */
+ YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ {
+ /* Give user a chance to reallocate the stack. Use copies of
+ these so that the &'s don't force the real ones into
+ memory. */
+ YYSTYPE *yyvs1 = yyvs;
+ yytype_int16 *yyss1 = yyss;
+
+
+ /* Each stack pointer address is followed by the size of the
+ data in use in that stack, in bytes. This used to be a
+ conditional around just the two extra args, but that might
+ be undefined if yyoverflow is a macro. */
+ yyoverflow (YY_("memory exhausted"),
+ &yyss1, yysize * sizeof (*yyssp),
+ &yyvs1, yysize * sizeof (*yyvsp),
+
+ &yystacksize);
+
+ yyss = yyss1;
+ yyvs = yyvs1;
+ }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+ goto yyexhaustedlab;
+# else
+ /* Extend the stack our own way. */
+ if (YYMAXDEPTH <= yystacksize)
+ goto yyexhaustedlab;
+ yystacksize *= 2;
+ if (YYMAXDEPTH < yystacksize)
+ yystacksize = YYMAXDEPTH;
+
+ {
+ yytype_int16 *yyss1 = yyss;
+ union yyalloc *yyptr =
+ (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+ if (! yyptr)
+ goto yyexhaustedlab;
+ YYSTACK_RELOCATE (yyss);
+ YYSTACK_RELOCATE (yyvs);
+
+# undef YYSTACK_RELOCATE
+ if (yyss1 != yyssa)
+ YYSTACK_FREE (yyss1);
+ }
+# endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + yysize - 1;
+ yyvsp = yyvs + yysize - 1;
+
+
+ YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+ (unsigned long int) yystacksize));
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ YYABORT;
+ }
+
+ YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+ goto yybackup;
+
+/*-----------.
+| yybackup. |
+`-----------*/
+yybackup:
+
+ /* Do appropriate processing given the current state. Read a
+ look-ahead token if we need one and don't already have one. */
+
+ /* First try to decide what to do without reference to look-ahead token. */
+ yyn = yypact[yystate];
+ if (yyn == YYPACT_NINF)
+ goto yydefault;
+
+ /* Not known => get a look-ahead token if don't already have one. */
+
+ /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */
+ if (yychar == YYEMPTY)
+ {
+ YYDPRINTF ((stderr, "Reading a token: "));
+ yychar = YYLEX;
+ }
+
+ if (yychar <= YYEOF)
+ {
+ yychar = yytoken = YYEOF;
+ YYDPRINTF ((stderr, "Now at end of input.\n"));
+ }
+ else
+ {
+ yytoken = YYTRANSLATE (yychar);
+ YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+ }
+
+ /* If the proper action on seeing token YYTOKEN is to reduce or to
+ detect an error, take that action. */
+ yyn += yytoken;
+ if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+ goto yydefault;
+ yyn = yytable[yyn];
+ if (yyn <= 0)
+ {
+ if (yyn == 0 || yyn == YYTABLE_NINF)
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ /* Count tokens shifted since error; after three, turn off error
+ status. */
+ if (yyerrstatus)
+ yyerrstatus--;
+
+ /* Shift the look-ahead token. */
+ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+ /* Discard the shifted token unless it is eof. */
+ if (yychar != YYEOF)
+ yychar = YYEMPTY;
+
+ yystate = yyn;
+ *++yyvsp = yylval;
+
+ goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state. |
+`-----------------------------------------------------------*/
+yydefault:
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+ goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction. |
+`-----------------------------*/
+yyreduce:
+ /* yyn is the number of a rule to reduce with. */
+ yylen = yyr2[yyn];
+
+ /* If YYLEN is nonzero, implement the default value of the action:
+ `$$ = $1'.
+
+ Otherwise, the following line sets YYVAL to garbage.
+ This behavior is undocumented and Bison
+ users should not rely upon it. Assigning to YYVAL
+ unconditionally makes the parser a bit smaller, and it avoids a
+ GCC warning that YYVAL may be used uninitialized. */
+ yyval = yyvsp[1-yylen];
+
+
+ YY_REDUCE_PRINT (yyn);
+ switch (yyn)
+ {
+ case 2:
+#line 235 "heimdal/lib/asn1/parse.y"
+ {
+ checkundefined();
+ }
+ break;
+
+ case 4:
+#line 242 "heimdal/lib/asn1/parse.y"
+ { error_message("implicit tagging is not supported"); }
+ break;
+
+ case 5:
+#line 244 "heimdal/lib/asn1/parse.y"
+ { error_message("automatic tagging is not supported"); }
+ break;
+
+ case 7:
+#line 249 "heimdal/lib/asn1/parse.y"
+ { error_message("no extensibility options supported"); }
+ break;
+
+ case 17:
+#line 270 "heimdal/lib/asn1/parse.y"
+ {
+ struct string_list *sl;
+ for(sl = (yyvsp[(1) - (4)].sl); sl != NULL; sl = sl->next) {
+ Symbol *s = addsym(sl->string);
+ s->stype = Stype;
+ }
+ add_import((yyvsp[(3) - (4)].name));
+ }
+ break;
+
+ case 22:
+#line 289 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.sl) = emalloc(sizeof(*(yyval.sl)));
+ (yyval.sl)->string = (yyvsp[(1) - (3)].name);
+ (yyval.sl)->next = (yyvsp[(3) - (3)].sl);
+ }
+ break;
+
+ case 23:
+#line 295 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.sl) = emalloc(sizeof(*(yyval.sl)));
+ (yyval.sl)->string = (yyvsp[(1) - (1)].name);
+ (yyval.sl)->next = NULL;
+ }
+ break;
+
+ case 24:
+#line 303 "heimdal/lib/asn1/parse.y"
+ {
+ Symbol *s = addsym ((yyvsp[(1) - (3)].name));
+ s->stype = Stype;
+ s->type = (yyvsp[(3) - (3)].type);
+ fix_labels(s);
+ generate_type (s);
+ }
+ break;
+
+ case 42:
+#line 334 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.type) = new_tag(ASN1_C_UNIV, UT_Boolean,
+ TE_EXPLICIT, new_type(TBoolean));
+ }
+ break;
+
+ case 43:
+#line 341 "heimdal/lib/asn1/parse.y"
+ {
+ if((yyvsp[(2) - (5)].value)->type != integervalue)
+ error_message("Non-integer used in first part of range");
+ if((yyvsp[(2) - (5)].value)->type != integervalue)
+ error_message("Non-integer in second part of range");
+ (yyval.range) = ecalloc(1, sizeof(*(yyval.range)));
+ (yyval.range)->min = (yyvsp[(2) - (5)].value)->u.integervalue;
+ (yyval.range)->max = (yyvsp[(4) - (5)].value)->u.integervalue;
+ }
+ break;
+
+ case 44:
+#line 351 "heimdal/lib/asn1/parse.y"
+ {
+ if((yyvsp[(2) - (5)].value)->type != integervalue)
+ error_message("Non-integer in first part of range");
+ (yyval.range) = ecalloc(1, sizeof(*(yyval.range)));
+ (yyval.range)->min = (yyvsp[(2) - (5)].value)->u.integervalue;
+ (yyval.range)->max = (yyvsp[(2) - (5)].value)->u.integervalue - 1;
+ }
+ break;
+
+ case 45:
+#line 359 "heimdal/lib/asn1/parse.y"
+ {
+ if((yyvsp[(4) - (5)].value)->type != integervalue)
+ error_message("Non-integer in second part of range");
+ (yyval.range) = ecalloc(1, sizeof(*(yyval.range)));
+ (yyval.range)->min = (yyvsp[(4) - (5)].value)->u.integervalue + 2;
+ (yyval.range)->max = (yyvsp[(4) - (5)].value)->u.integervalue;
+ }
+ break;
+
+ case 46:
+#line 367 "heimdal/lib/asn1/parse.y"
+ {
+ if((yyvsp[(2) - (3)].value)->type != integervalue)
+ error_message("Non-integer used in limit");
+ (yyval.range) = ecalloc(1, sizeof(*(yyval.range)));
+ (yyval.range)->min = (yyvsp[(2) - (3)].value)->u.integervalue;
+ (yyval.range)->max = (yyvsp[(2) - (3)].value)->u.integervalue;
+ }
+ break;
+
+ case 47:
+#line 378 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.type) = new_tag(ASN1_C_UNIV, UT_Integer,
+ TE_EXPLICIT, new_type(TInteger));
+ }
+ break;
+
+ case 48:
+#line 383 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.type) = new_type(TInteger);
+ (yyval.type)->range = (yyvsp[(2) - (2)].range);
+ (yyval.type) = new_tag(ASN1_C_UNIV, UT_Integer, TE_EXPLICIT, (yyval.type));
+ }
+ break;
+
+ case 49:
+#line 389 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.type) = new_type(TInteger);
+ (yyval.type)->members = (yyvsp[(3) - (4)].members);
+ (yyval.type) = new_tag(ASN1_C_UNIV, UT_Integer, TE_EXPLICIT, (yyval.type));
+ }
+ break;
+
+ case 50:
+#line 397 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.members) = emalloc(sizeof(*(yyval.members)));
+ ASN1_TAILQ_INIT((yyval.members));
+ ASN1_TAILQ_INSERT_HEAD((yyval.members), (yyvsp[(1) - (1)].member), members);
+ }
+ break;
+
+ case 51:
+#line 403 "heimdal/lib/asn1/parse.y"
+ {
+ ASN1_TAILQ_INSERT_TAIL((yyvsp[(1) - (3)].members), (yyvsp[(3) - (3)].member), members);
+ (yyval.members) = (yyvsp[(1) - (3)].members);
+ }
+ break;
+
+ case 52:
+#line 408 "heimdal/lib/asn1/parse.y"
+ { (yyval.members) = (yyvsp[(1) - (3)].members); }
+ break;
+
+ case 53:
+#line 412 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.member) = emalloc(sizeof(*(yyval.member)));
+ (yyval.member)->name = (yyvsp[(1) - (4)].name);
+ (yyval.member)->gen_name = estrdup((yyvsp[(1) - (4)].name));
+ output_name ((yyval.member)->gen_name);
+ (yyval.member)->val = (yyvsp[(3) - (4)].constant);
+ (yyval.member)->optional = 0;
+ (yyval.member)->ellipsis = 0;
+ (yyval.member)->type = NULL;
+ }
+ break;
+
+ case 54:
+#line 425 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.type) = new_type(TInteger);
+ (yyval.type)->members = (yyvsp[(3) - (4)].members);
+ (yyval.type) = new_tag(ASN1_C_UNIV, UT_Enumerated, TE_EXPLICIT, (yyval.type));
+ }
+ break;
+
+ case 56:
+#line 436 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.type) = new_type(TBitString);
+ (yyval.type)->members = emalloc(sizeof(*(yyval.type)->members));
+ ASN1_TAILQ_INIT((yyval.type)->members);
+ (yyval.type) = new_tag(ASN1_C_UNIV, UT_BitString, TE_EXPLICIT, (yyval.type));
+ }
+ break;
+
+ case 57:
+#line 443 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.type) = new_type(TBitString);
+ (yyval.type)->members = (yyvsp[(4) - (5)].members);
+ (yyval.type) = new_tag(ASN1_C_UNIV, UT_BitString, TE_EXPLICIT, (yyval.type));
+ }
+ break;
+
+ case 58:
+#line 451 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.type) = new_tag(ASN1_C_UNIV, UT_OID,
+ TE_EXPLICIT, new_type(TOID));
+ }
+ break;
+
+ case 59:
+#line 457 "heimdal/lib/asn1/parse.y"
+ {
+ Type *t = new_type(TOctetString);
+ t->range = (yyvsp[(3) - (3)].range);
+ (yyval.type) = new_tag(ASN1_C_UNIV, UT_OctetString,
+ TE_EXPLICIT, t);
+ }
+ break;
+
+ case 60:
+#line 466 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.type) = new_tag(ASN1_C_UNIV, UT_Null,
+ TE_EXPLICIT, new_type(TNull));
+ }
+ break;
+
+ case 61:
+#line 473 "heimdal/lib/asn1/parse.y"
+ { (yyval.range) = NULL; }
+ break;
+
+ case 62:
+#line 475 "heimdal/lib/asn1/parse.y"
+ { (yyval.range) = (yyvsp[(2) - (2)].range); }
+ break;
+
+ case 63:
+#line 480 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.type) = new_type(TSequence);
+ (yyval.type)->members = (yyvsp[(3) - (4)].members);
+ (yyval.type) = new_tag(ASN1_C_UNIV, UT_Sequence, TE_EXPLICIT, (yyval.type));
+ }
+ break;
+
+ case 64:
+#line 486 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.type) = new_type(TSequence);
+ (yyval.type)->members = NULL;
+ (yyval.type) = new_tag(ASN1_C_UNIV, UT_Sequence, TE_EXPLICIT, (yyval.type));
+ }
+ break;
+
+ case 65:
+#line 494 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.type) = new_type(TSequenceOf);
+ (yyval.type)->range = (yyvsp[(2) - (4)].range);
+ (yyval.type)->subtype = (yyvsp[(4) - (4)].type);
+ (yyval.type) = new_tag(ASN1_C_UNIV, UT_Sequence, TE_EXPLICIT, (yyval.type));
+ }
+ break;
+
+ case 66:
+#line 503 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.type) = new_type(TSet);
+ (yyval.type)->members = (yyvsp[(3) - (4)].members);
+ (yyval.type) = new_tag(ASN1_C_UNIV, UT_Set, TE_EXPLICIT, (yyval.type));
+ }
+ break;
+
+ case 67:
+#line 509 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.type) = new_type(TSet);
+ (yyval.type)->members = NULL;
+ (yyval.type) = new_tag(ASN1_C_UNIV, UT_Set, TE_EXPLICIT, (yyval.type));
+ }
+ break;
+
+ case 68:
+#line 517 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.type) = new_type(TSetOf);
+ (yyval.type)->subtype = (yyvsp[(3) - (3)].type);
+ (yyval.type) = new_tag(ASN1_C_UNIV, UT_Set, TE_EXPLICIT, (yyval.type));
+ }
+ break;
+
+ case 69:
+#line 525 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.type) = new_type(TChoice);
+ (yyval.type)->members = (yyvsp[(3) - (4)].members);
+ }
+ break;
+
+ case 72:
+#line 536 "heimdal/lib/asn1/parse.y"
+ {
+ Symbol *s = addsym((yyvsp[(1) - (1)].name));
+ (yyval.type) = new_type(TType);
+ if(s->stype != Stype && s->stype != SUndefined)
+ error_message ("%s is not a type\n", (yyvsp[(1) - (1)].name));
+ else
+ (yyval.type)->symbol = s;
+ }
+ break;
+
+ case 73:
+#line 547 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.type) = new_tag(ASN1_C_UNIV, UT_GeneralizedTime,
+ TE_EXPLICIT, new_type(TGeneralizedTime));
+ }
+ break;
+
+ case 74:
+#line 552 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.type) = new_tag(ASN1_C_UNIV, UT_UTCTime,
+ TE_EXPLICIT, new_type(TUTCTime));
+ }
+ break;
+
+ case 75:
+#line 559 "heimdal/lib/asn1/parse.y"
+ {
+ /* if (Constraint.type == contentConstrant) {
+ assert(Constraint.u.constraint.type == octetstring|bitstring-w/o-NamedBitList); // remember to check type reference too
+ if (Constraint.u.constraint.type) {
+ assert((Constraint.u.constraint.type.length % 8) == 0);
+ }
+ }
+ if (Constraint.u.constraint.encoding) {
+ type == der-oid|ber-oid
+ }
+ */
+ }
+ break;
+
+ case 76:
+#line 575 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.constraint_spec) = (yyvsp[(2) - (3)].constraint_spec);
+ }
+ break;
+
+ case 80:
+#line 588 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.constraint_spec) = new_constraint_spec(CT_CONTENTS);
+ (yyval.constraint_spec)->u.content.type = (yyvsp[(2) - (2)].type);
+ (yyval.constraint_spec)->u.content.encoding = NULL;
+ }
+ break;
+
+ case 81:
+#line 594 "heimdal/lib/asn1/parse.y"
+ {
+ if ((yyvsp[(3) - (3)].value)->type != objectidentifiervalue)
+ error_message("Non-OID used in ENCODED BY constraint");
+ (yyval.constraint_spec) = new_constraint_spec(CT_CONTENTS);
+ (yyval.constraint_spec)->u.content.type = NULL;
+ (yyval.constraint_spec)->u.content.encoding = (yyvsp[(3) - (3)].value);
+ }
+ break;
+
+ case 82:
+#line 602 "heimdal/lib/asn1/parse.y"
+ {
+ if ((yyvsp[(5) - (5)].value)->type != objectidentifiervalue)
+ error_message("Non-OID used in ENCODED BY constraint");
+ (yyval.constraint_spec) = new_constraint_spec(CT_CONTENTS);
+ (yyval.constraint_spec)->u.content.type = (yyvsp[(2) - (5)].type);
+ (yyval.constraint_spec)->u.content.encoding = (yyvsp[(5) - (5)].value);
+ }
+ break;
+
+ case 83:
+#line 612 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.constraint_spec) = new_constraint_spec(CT_USER);
+ }
+ break;
+
+ case 84:
+#line 618 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.type) = new_type(TTag);
+ (yyval.type)->tag = (yyvsp[(1) - (3)].tag);
+ (yyval.type)->tag.tagenv = (yyvsp[(2) - (3)].constant);
+ if((yyvsp[(3) - (3)].type)->type == TTag && (yyvsp[(2) - (3)].constant) == TE_IMPLICIT) {
+ (yyval.type)->subtype = (yyvsp[(3) - (3)].type)->subtype;
+ free((yyvsp[(3) - (3)].type));
+ } else
+ (yyval.type)->subtype = (yyvsp[(3) - (3)].type);
+ }
+ break;
+
+ case 85:
+#line 631 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.tag).tagclass = (yyvsp[(2) - (4)].constant);
+ (yyval.tag).tagvalue = (yyvsp[(3) - (4)].constant);
+ (yyval.tag).tagenv = TE_EXPLICIT;
+ }
+ break;
+
+ case 86:
+#line 639 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.constant) = ASN1_C_CONTEXT;
+ }
+ break;
+
+ case 87:
+#line 643 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.constant) = ASN1_C_UNIV;
+ }
+ break;
+
+ case 88:
+#line 647 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.constant) = ASN1_C_APPL;
+ }
+ break;
+
+ case 89:
+#line 651 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.constant) = ASN1_C_PRIVATE;
+ }
+ break;
+
+ case 90:
+#line 657 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.constant) = TE_EXPLICIT;
+ }
+ break;
+
+ case 91:
+#line 661 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.constant) = TE_EXPLICIT;
+ }
+ break;
+
+ case 92:
+#line 665 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.constant) = TE_IMPLICIT;
+ }
+ break;
+
+ case 93:
+#line 672 "heimdal/lib/asn1/parse.y"
+ {
+ Symbol *s;
+ s = addsym ((yyvsp[(1) - (4)].name));
+
+ s->stype = SValue;
+ s->value = (yyvsp[(4) - (4)].value);
+ generate_constant (s);
+ }
+ break;
+
+ case 95:
+#line 686 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.type) = new_tag(ASN1_C_UNIV, UT_GeneralString,
+ TE_EXPLICIT, new_type(TGeneralString));
+ }
+ break;
+
+ case 96:
+#line 691 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.type) = new_tag(ASN1_C_UNIV, UT_UTF8String,
+ TE_EXPLICIT, new_type(TUTF8String));
+ }
+ break;
+
+ case 97:
+#line 696 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.type) = new_tag(ASN1_C_UNIV, UT_PrintableString,
+ TE_EXPLICIT, new_type(TPrintableString));
+ }
+ break;
+
+ case 98:
+#line 701 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.type) = new_tag(ASN1_C_UNIV, UT_VisibleString,
+ TE_EXPLICIT, new_type(TVisibleString));
+ }
+ break;
+
+ case 99:
+#line 706 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.type) = new_tag(ASN1_C_UNIV, UT_IA5String,
+ TE_EXPLICIT, new_type(TIA5String));
+ }
+ break;
+
+ case 100:
+#line 711 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.type) = new_tag(ASN1_C_UNIV, UT_BMPString,
+ TE_EXPLICIT, new_type(TBMPString));
+ }
+ break;
+
+ case 101:
+#line 716 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.type) = new_tag(ASN1_C_UNIV, UT_UniversalString,
+ TE_EXPLICIT, new_type(TUniversalString));
+ }
+ break;
+
+ case 102:
+#line 724 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.members) = emalloc(sizeof(*(yyval.members)));
+ ASN1_TAILQ_INIT((yyval.members));
+ ASN1_TAILQ_INSERT_HEAD((yyval.members), (yyvsp[(1) - (1)].member), members);
+ }
+ break;
+
+ case 103:
+#line 730 "heimdal/lib/asn1/parse.y"
+ {
+ ASN1_TAILQ_INSERT_TAIL((yyvsp[(1) - (3)].members), (yyvsp[(3) - (3)].member), members);
+ (yyval.members) = (yyvsp[(1) - (3)].members);
+ }
+ break;
+
+ case 104:
+#line 735 "heimdal/lib/asn1/parse.y"
+ {
+ struct member *m = ecalloc(1, sizeof(*m));
+ m->name = estrdup("...");
+ m->gen_name = estrdup("asn1_ellipsis");
+ m->ellipsis = 1;
+ ASN1_TAILQ_INSERT_TAIL((yyvsp[(1) - (3)].members), m, members);
+ (yyval.members) = (yyvsp[(1) - (3)].members);
+ }
+ break;
+
+ case 105:
+#line 746 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.member) = emalloc(sizeof(*(yyval.member)));
+ (yyval.member)->name = (yyvsp[(1) - (2)].name);
+ (yyval.member)->gen_name = estrdup((yyvsp[(1) - (2)].name));
+ output_name ((yyval.member)->gen_name);
+ (yyval.member)->type = (yyvsp[(2) - (2)].type);
+ (yyval.member)->ellipsis = 0;
+ }
+ break;
+
+ case 106:
+#line 757 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.member) = (yyvsp[(1) - (1)].member);
+ (yyval.member)->optional = 0;
+ (yyval.member)->defval = NULL;
+ }
+ break;
+
+ case 107:
+#line 763 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.member) = (yyvsp[(1) - (2)].member);
+ (yyval.member)->optional = 1;
+ (yyval.member)->defval = NULL;
+ }
+ break;
+
+ case 108:
+#line 769 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.member) = (yyvsp[(1) - (3)].member);
+ (yyval.member)->optional = 0;
+ (yyval.member)->defval = (yyvsp[(3) - (3)].value);
+ }
+ break;
+
+ case 109:
+#line 777 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.members) = emalloc(sizeof(*(yyval.members)));
+ ASN1_TAILQ_INIT((yyval.members));
+ ASN1_TAILQ_INSERT_HEAD((yyval.members), (yyvsp[(1) - (1)].member), members);
+ }
+ break;
+
+ case 110:
+#line 783 "heimdal/lib/asn1/parse.y"
+ {
+ ASN1_TAILQ_INSERT_TAIL((yyvsp[(1) - (3)].members), (yyvsp[(3) - (3)].member), members);
+ (yyval.members) = (yyvsp[(1) - (3)].members);
+ }
+ break;
+
+ case 111:
+#line 790 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.member) = emalloc(sizeof(*(yyval.member)));
+ (yyval.member)->name = (yyvsp[(1) - (4)].name);
+ (yyval.member)->gen_name = estrdup((yyvsp[(1) - (4)].name));
+ output_name ((yyval.member)->gen_name);
+ (yyval.member)->val = (yyvsp[(3) - (4)].constant);
+ (yyval.member)->optional = 0;
+ (yyval.member)->ellipsis = 0;
+ (yyval.member)->type = NULL;
+ }
+ break;
+
+ case 113:
+#line 803 "heimdal/lib/asn1/parse.y"
+ { (yyval.objid) = NULL; }
+ break;
+
+ case 114:
+#line 807 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.objid) = (yyvsp[(2) - (3)].objid);
+ }
+ break;
+
+ case 115:
+#line 813 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.objid) = NULL;
+ }
+ break;
+
+ case 116:
+#line 817 "heimdal/lib/asn1/parse.y"
+ {
+ if ((yyvsp[(2) - (2)].objid)) {
+ (yyval.objid) = (yyvsp[(2) - (2)].objid);
+ add_oid_to_tail((yyvsp[(2) - (2)].objid), (yyvsp[(1) - (2)].objid));
+ } else {
+ (yyval.objid) = (yyvsp[(1) - (2)].objid);
+ }
+ }
+ break;
+
+ case 117:
+#line 828 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.objid) = new_objid((yyvsp[(1) - (4)].name), (yyvsp[(3) - (4)].constant));
+ }
+ break;
+
+ case 118:
+#line 832 "heimdal/lib/asn1/parse.y"
+ {
+ Symbol *s = addsym((yyvsp[(1) - (1)].name));
+ if(s->stype != SValue ||
+ s->value->type != objectidentifiervalue) {
+ error_message("%s is not an object identifier\n",
+ s->name);
+ exit(1);
+ }
+ (yyval.objid) = s->value->u.objectidentifiervalue;
+ }
+ break;
+
+ case 119:
+#line 843 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.objid) = new_objid(NULL, (yyvsp[(1) - (1)].constant));
+ }
+ break;
+
+ case 129:
+#line 866 "heimdal/lib/asn1/parse.y"
+ {
+ Symbol *s = addsym((yyvsp[(1) - (1)].name));
+ if(s->stype != SValue)
+ error_message ("%s is not a value\n",
+ s->name);
+ else
+ (yyval.value) = s->value;
+ }
+ break;
+
+ case 130:
+#line 877 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.value) = emalloc(sizeof(*(yyval.value)));
+ (yyval.value)->type = stringvalue;
+ (yyval.value)->u.stringvalue = (yyvsp[(1) - (1)].name);
+ }
+ break;
+
+ case 131:
+#line 885 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.value) = emalloc(sizeof(*(yyval.value)));
+ (yyval.value)->type = booleanvalue;
+ (yyval.value)->u.booleanvalue = 0;
+ }
+ break;
+
+ case 132:
+#line 891 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.value) = emalloc(sizeof(*(yyval.value)));
+ (yyval.value)->type = booleanvalue;
+ (yyval.value)->u.booleanvalue = 0;
+ }
+ break;
+
+ case 133:
+#line 899 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.value) = emalloc(sizeof(*(yyval.value)));
+ (yyval.value)->type = integervalue;
+ (yyval.value)->u.integervalue = (yyvsp[(1) - (1)].constant);
+ }
+ break;
+
+ case 135:
+#line 910 "heimdal/lib/asn1/parse.y"
+ {
+ }
+ break;
+
+ case 136:
+#line 915 "heimdal/lib/asn1/parse.y"
+ {
+ (yyval.value) = emalloc(sizeof(*(yyval.value)));
+ (yyval.value)->type = objectidentifiervalue;
+ (yyval.value)->u.objectidentifiervalue = (yyvsp[(1) - (1)].objid);
+ }
+ break;
+
+
+/* Line 1267 of yacc.c. */
+#line 2523 "heimdal/lib/asn1/parse.y"
+ default: break;
+ }
+ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+
+ *++yyvsp = yyval;
+
+
+ /* Now `shift' the result of the reduction. Determine what state
+ that goes to, based on the state we popped back to and the rule
+ number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+ if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTOKENS];
+
+ goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+ /* If not already recovering from an error, report this error. */
+ if (!yyerrstatus)
+ {
+ ++yynerrs;
+#if ! YYERROR_VERBOSE
+ yyerror (YY_("syntax error"));
+#else
+ {
+ YYSIZE_T yysize = yysyntax_error (0, yystate, yychar);
+ if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM)
+ {
+ YYSIZE_T yyalloc = 2 * yysize;
+ if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM))
+ yyalloc = YYSTACK_ALLOC_MAXIMUM;
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+ yymsg = (char *) YYSTACK_ALLOC (yyalloc);
+ if (yymsg)
+ yymsg_alloc = yyalloc;
+ else
+ {
+ yymsg = yymsgbuf;
+ yymsg_alloc = sizeof yymsgbuf;
+ }
+ }
+
+ if (0 < yysize && yysize <= yymsg_alloc)
+ {
+ (void) yysyntax_error (yymsg, yystate, yychar);
+ yyerror (yymsg);
+ }
+ else
+ {
+ yyerror (YY_("syntax error"));
+ if (yysize != 0)
+ goto yyexhaustedlab;
+ }
+ }
+#endif
+ }
+
+
+
+ if (yyerrstatus == 3)
+ {
+ /* If just tried and failed to reuse look-ahead token after an
+ error, discard it. */
+
+ if (yychar <= YYEOF)
+ {
+ /* Return failure if at end of input. */
+ if (yychar == YYEOF)
+ YYABORT;
+ }
+ else
+ {
+ yydestruct ("Error: discarding",
+ yytoken, &yylval);
+ yychar = YYEMPTY;
+ }
+ }
+
+ /* Else will try to reuse look-ahead token after shifting the error
+ token. */
+ goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR. |
+`---------------------------------------------------*/
+yyerrorlab:
+
+ /* Pacify compilers like GCC when the user code never invokes
+ YYERROR and the label yyerrorlab therefore never appears in user
+ code. */
+ if (/*CONSTCOND*/ 0)
+ goto yyerrorlab;
+
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYERROR. */
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+ yystate = *yyssp;
+ goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR. |
+`-------------------------------------------------------------*/
+yyerrlab1:
+ yyerrstatus = 3; /* Each real token shifted decrements this. */
+
+ for (;;)
+ {
+ yyn = yypact[yystate];
+ if (yyn != YYPACT_NINF)
+ {
+ yyn += YYTERROR;
+ if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+ {
+ yyn = yytable[yyn];
+ if (0 < yyn)
+ break;
+ }
+ }
+
+ /* Pop the current state because it cannot handle the error token. */
+ if (yyssp == yyss)
+ YYABORT;
+
+
+ yydestruct ("Error: popping",
+ yystos[yystate], yyvsp);
+ YYPOPSTACK (1);
+ yystate = *yyssp;
+ YY_STACK_PRINT (yyss, yyssp);
+ }
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ *++yyvsp = yylval;
+
+
+ /* Shift the error token. */
+ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here. |
+`-------------------------------------*/
+yyacceptlab:
+ yyresult = 0;
+ goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here. |
+`-----------------------------------*/
+yyabortlab:
+ yyresult = 1;
+ goto yyreturn;
+
+#ifndef yyoverflow
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here. |
+`-------------------------------------------------*/
+yyexhaustedlab:
+ yyerror (YY_("memory exhausted"));
+ yyresult = 2;
+ /* Fall through. */
+#endif
+
+yyreturn:
+ if (yychar != YYEOF && yychar != YYEMPTY)
+ yydestruct ("Cleanup: discarding lookahead",
+ yytoken, &yylval);
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYABORT or YYACCEPT. */
+ YYPOPSTACK (yylen);
+ YY_STACK_PRINT (yyss, yyssp);
+ while (yyssp != yyss)
+ {
+ yydestruct ("Cleanup: popping",
+ yystos[*yyssp], yyvsp);
+ YYPOPSTACK (1);
+ }
+#ifndef yyoverflow
+ if (yyss != yyssa)
+ YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+#endif
+ /* Make sure YYID is used. */
+ return YYID (yyresult);
+}
+
+
+#line 922 "heimdal/lib/asn1/parse.y"
+
+
+void
+yyerror (const char *s)
+{
+ error_message ("%s\n", s);
+}
+
+static Type *
+new_tag(int tagclass, int tagvalue, int tagenv, Type *oldtype)
+{
+ Type *t;
+ if(oldtype->type == TTag && oldtype->tag.tagenv == TE_IMPLICIT) {
+ t = oldtype;
+ oldtype = oldtype->subtype; /* XXX */
+ } else
+ t = new_type (TTag);
+
+ t->tag.tagclass = tagclass;
+ t->tag.tagvalue = tagvalue;
+ t->tag.tagenv = tagenv;
+ t->subtype = oldtype;
+ return t;
+}
+
+static struct objid *
+new_objid(const char *label, int value)
+{
+ struct objid *s;
+ s = emalloc(sizeof(*s));
+ s->label = label;
+ s->value = value;
+ s->next = NULL;
+ return s;
+}
+
+static void
+add_oid_to_tail(struct objid *head, struct objid *tail)
+{
+ struct objid *o;
+ o = head;
+ while (o->next)
+ o = o->next;
+ o->next = tail;
+}
+
+static Type *
+new_type (Typetype tt)
+{
+ Type *t = ecalloc(1, sizeof(*t));
+ t->type = tt;
+ return t;
+}
+
+static struct constraint_spec *
+new_constraint_spec(enum ctype ct)
+{
+ struct constraint_spec *c = ecalloc(1, sizeof(*c));
+ c->ctype = ct;
+ return c;
+}
+
+static void fix_labels2(Type *t, const char *prefix);
+static void fix_labels1(struct memhead *members, const char *prefix)
+{
+ Member *m;
+
+ if(members == NULL)
+ return;
+ ASN1_TAILQ_FOREACH(m, members, members) {
+ asprintf(&m->label, "%s_%s", prefix, m->gen_name);
+ if (m->label == NULL)
+ errx(1, "malloc");
+ if(m->type != NULL)
+ fix_labels2(m->type, m->label);
+ }
+}
+
+static void fix_labels2(Type *t, const char *prefix)
+{
+ for(; t; t = t->subtype)
+ fix_labels1(t->members, prefix);
+}
+
+static void
+fix_labels(Symbol *s)
+{
+ char *p;
+ asprintf(&p, "choice_%s", s->gen_name);
+ if (p == NULL)
+ errx(1, "malloc");
+ fix_labels2(s->type, p);
+ free(p);
+}
+
diff --git a/source4/heimdal/lib/asn1/parse.h b/source4/heimdal/lib/asn1/parse.h
new file mode 100644
index 0000000000..bea506ca7b
--- /dev/null
+++ b/source4/heimdal/lib/asn1/parse.h
@@ -0,0 +1,249 @@
+/* A Bison parser, made by GNU Bison 2.3. */
+
+/* Skeleton interface for Bison's Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+ Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU 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., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ kw_ABSENT = 258,
+ kw_ABSTRACT_SYNTAX = 259,
+ kw_ALL = 260,
+ kw_APPLICATION = 261,
+ kw_AUTOMATIC = 262,
+ kw_BEGIN = 263,
+ kw_BIT = 264,
+ kw_BMPString = 265,
+ kw_BOOLEAN = 266,
+ kw_BY = 267,
+ kw_CHARACTER = 268,
+ kw_CHOICE = 269,
+ kw_CLASS = 270,
+ kw_COMPONENT = 271,
+ kw_COMPONENTS = 272,
+ kw_CONSTRAINED = 273,
+ kw_CONTAINING = 274,
+ kw_DEFAULT = 275,
+ kw_DEFINITIONS = 276,
+ kw_EMBEDDED = 277,
+ kw_ENCODED = 278,
+ kw_END = 279,
+ kw_ENUMERATED = 280,
+ kw_EXCEPT = 281,
+ kw_EXPLICIT = 282,
+ kw_EXPORTS = 283,
+ kw_EXTENSIBILITY = 284,
+ kw_EXTERNAL = 285,
+ kw_FALSE = 286,
+ kw_FROM = 287,
+ kw_GeneralString = 288,
+ kw_GeneralizedTime = 289,
+ kw_GraphicString = 290,
+ kw_IA5String = 291,
+ kw_IDENTIFIER = 292,
+ kw_IMPLICIT = 293,
+ kw_IMPLIED = 294,
+ kw_IMPORTS = 295,
+ kw_INCLUDES = 296,
+ kw_INSTANCE = 297,
+ kw_INTEGER = 298,
+ kw_INTERSECTION = 299,
+ kw_ISO646String = 300,
+ kw_MAX = 301,
+ kw_MIN = 302,
+ kw_MINUS_INFINITY = 303,
+ kw_NULL = 304,
+ kw_NumericString = 305,
+ kw_OBJECT = 306,
+ kw_OCTET = 307,
+ kw_OF = 308,
+ kw_OPTIONAL = 309,
+ kw_ObjectDescriptor = 310,
+ kw_PATTERN = 311,
+ kw_PDV = 312,
+ kw_PLUS_INFINITY = 313,
+ kw_PRESENT = 314,
+ kw_PRIVATE = 315,
+ kw_PrintableString = 316,
+ kw_REAL = 317,
+ kw_RELATIVE_OID = 318,
+ kw_SEQUENCE = 319,
+ kw_SET = 320,
+ kw_SIZE = 321,
+ kw_STRING = 322,
+ kw_SYNTAX = 323,
+ kw_T61String = 324,
+ kw_TAGS = 325,
+ kw_TRUE = 326,
+ kw_TYPE_IDENTIFIER = 327,
+ kw_TeletexString = 328,
+ kw_UNION = 329,
+ kw_UNIQUE = 330,
+ kw_UNIVERSAL = 331,
+ kw_UTCTime = 332,
+ kw_UTF8String = 333,
+ kw_UniversalString = 334,
+ kw_VideotexString = 335,
+ kw_VisibleString = 336,
+ kw_WITH = 337,
+ RANGE = 338,
+ EEQUAL = 339,
+ ELLIPSIS = 340,
+ IDENTIFIER = 341,
+ referencename = 342,
+ STRING = 343,
+ NUMBER = 344
+ };
+#endif
+/* Tokens. */
+#define kw_ABSENT 258
+#define kw_ABSTRACT_SYNTAX 259
+#define kw_ALL 260
+#define kw_APPLICATION 261
+#define kw_AUTOMATIC 262
+#define kw_BEGIN 263
+#define kw_BIT 264
+#define kw_BMPString 265
+#define kw_BOOLEAN 266
+#define kw_BY 267
+#define kw_CHARACTER 268
+#define kw_CHOICE 269
+#define kw_CLASS 270
+#define kw_COMPONENT 271
+#define kw_COMPONENTS 272
+#define kw_CONSTRAINED 273
+#define kw_CONTAINING 274
+#define kw_DEFAULT 275
+#define kw_DEFINITIONS 276
+#define kw_EMBEDDED 277
+#define kw_ENCODED 278
+#define kw_END 279
+#define kw_ENUMERATED 280
+#define kw_EXCEPT 281
+#define kw_EXPLICIT 282
+#define kw_EXPORTS 283
+#define kw_EXTENSIBILITY 284
+#define kw_EXTERNAL 285
+#define kw_FALSE 286
+#define kw_FROM 287
+#define kw_GeneralString 288
+#define kw_GeneralizedTime 289
+#define kw_GraphicString 290
+#define kw_IA5String 291
+#define kw_IDENTIFIER 292
+#define kw_IMPLICIT 293
+#define kw_IMPLIED 294
+#define kw_IMPORTS 295
+#define kw_INCLUDES 296
+#define kw_INSTANCE 297
+#define kw_INTEGER 298
+#define kw_INTERSECTION 299
+#define kw_ISO646String 300
+#define kw_MAX 301
+#define kw_MIN 302
+#define kw_MINUS_INFINITY 303
+#define kw_NULL 304
+#define kw_NumericString 305
+#define kw_OBJECT 306
+#define kw_OCTET 307
+#define kw_OF 308
+#define kw_OPTIONAL 309
+#define kw_ObjectDescriptor 310
+#define kw_PATTERN 311
+#define kw_PDV 312
+#define kw_PLUS_INFINITY 313
+#define kw_PRESENT 314
+#define kw_PRIVATE 315
+#define kw_PrintableString 316
+#define kw_REAL 317
+#define kw_RELATIVE_OID 318
+#define kw_SEQUENCE 319
+#define kw_SET 320
+#define kw_SIZE 321
+#define kw_STRING 322
+#define kw_SYNTAX 323
+#define kw_T61String 324
+#define kw_TAGS 325
+#define kw_TRUE 326
+#define kw_TYPE_IDENTIFIER 327
+#define kw_TeletexString 328
+#define kw_UNION 329
+#define kw_UNIQUE 330
+#define kw_UNIVERSAL 331
+#define kw_UTCTime 332
+#define kw_UTF8String 333
+#define kw_UniversalString 334
+#define kw_VideotexString 335
+#define kw_VisibleString 336
+#define kw_WITH 337
+#define RANGE 338
+#define EEQUAL 339
+#define ELLIPSIS 340
+#define IDENTIFIER 341
+#define referencename 342
+#define STRING 343
+#define NUMBER 344
+
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+#line 65 "heimdal/lib/asn1/parse.y"
+{
+ int constant;
+ struct value *value;
+ struct range *range;
+ char *name;
+ Type *type;
+ Member *member;
+ struct objid *objid;
+ char *defval;
+ struct string_list *sl;
+ struct tagtype tag;
+ struct memhead *members;
+ struct constraint_spec *constraint_spec;
+}
+/* Line 1489 of yacc.c. */
+#line 242 "heimdal/lib/asn1/parse.y"
+ YYSTYPE;
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+extern YYSTYPE yylval;
+
diff --git a/source4/heimdal/lib/asn1/parse.y b/source4/heimdal/lib/asn1/parse.y
new file mode 100644
index 0000000000..aca4a062b8
--- /dev/null
+++ b/source4/heimdal/lib/asn1/parse.y
@@ -0,0 +1,1015 @@
+/*
+ * Copyright (c) 1997 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+%{
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "symbol.h"
+#include "lex.h"
+#include "gen_locl.h"
+#include "der.h"
+
+RCSID("$Id$");
+
+static Type *new_type (Typetype t);
+static struct constraint_spec *new_constraint_spec(enum ctype);
+static Type *new_tag(int tagclass, int tagvalue, int tagenv, Type *oldtype);
+void yyerror (const char *);
+static struct objid *new_objid(const char *label, int value);
+static void add_oid_to_tail(struct objid *, struct objid *);
+static void fix_labels(Symbol *s);
+
+struct string_list {
+ char *string;
+ struct string_list *next;
+};
+
+%}
+
+%union {
+ int constant;
+ struct value *value;
+ struct range *range;
+ char *name;
+ Type *type;
+ Member *member;
+ struct objid *objid;
+ char *defval;
+ struct string_list *sl;
+ struct tagtype tag;
+ struct memhead *members;
+ struct constraint_spec *constraint_spec;
+}
+
+%token kw_ABSENT
+%token kw_ABSTRACT_SYNTAX
+%token kw_ALL
+%token kw_APPLICATION
+%token kw_AUTOMATIC
+%token kw_BEGIN
+%token kw_BIT
+%token kw_BMPString
+%token kw_BOOLEAN
+%token kw_BY
+%token kw_CHARACTER
+%token kw_CHOICE
+%token kw_CLASS
+%token kw_COMPONENT
+%token kw_COMPONENTS
+%token kw_CONSTRAINED
+%token kw_CONTAINING
+%token kw_DEFAULT
+%token kw_DEFINITIONS
+%token kw_EMBEDDED
+%token kw_ENCODED
+%token kw_END
+%token kw_ENUMERATED
+%token kw_EXCEPT
+%token kw_EXPLICIT
+%token kw_EXPORTS
+%token kw_EXTENSIBILITY
+%token kw_EXTERNAL
+%token kw_FALSE
+%token kw_FROM
+%token kw_GeneralString
+%token kw_GeneralizedTime
+%token kw_GraphicString
+%token kw_IA5String
+%token kw_IDENTIFIER
+%token kw_IMPLICIT
+%token kw_IMPLIED
+%token kw_IMPORTS
+%token kw_INCLUDES
+%token kw_INSTANCE
+%token kw_INTEGER
+%token kw_INTERSECTION
+%token kw_ISO646String
+%token kw_MAX
+%token kw_MIN
+%token kw_MINUS_INFINITY
+%token kw_NULL
+%token kw_NumericString
+%token kw_OBJECT
+%token kw_OCTET
+%token kw_OF
+%token kw_OPTIONAL
+%token kw_ObjectDescriptor
+%token kw_PATTERN
+%token kw_PDV
+%token kw_PLUS_INFINITY
+%token kw_PRESENT
+%token kw_PRIVATE
+%token kw_PrintableString
+%token kw_REAL
+%token kw_RELATIVE_OID
+%token kw_SEQUENCE
+%token kw_SET
+%token kw_SIZE
+%token kw_STRING
+%token kw_SYNTAX
+%token kw_T61String
+%token kw_TAGS
+%token kw_TRUE
+%token kw_TYPE_IDENTIFIER
+%token kw_TeletexString
+%token kw_UNION
+%token kw_UNIQUE
+%token kw_UNIVERSAL
+%token kw_UTCTime
+%token kw_UTF8String
+%token kw_UniversalString
+%token kw_VideotexString
+%token kw_VisibleString
+%token kw_WITH
+
+%token RANGE
+%token EEQUAL
+%token ELLIPSIS
+
+%token <name> IDENTIFIER referencename
+%token <name> STRING
+
+%token <constant> NUMBER
+%type <constant> SignedNumber
+%type <constant> Class tagenv
+
+%type <value> Value
+%type <value> BuiltinValue
+%type <value> IntegerValue
+%type <value> BooleanValue
+%type <value> ObjectIdentifierValue
+%type <value> CharacterStringValue
+%type <value> NullValue
+%type <value> DefinedValue
+%type <value> ReferencedValue
+%type <value> Valuereference
+
+%type <type> Type
+%type <type> BuiltinType
+%type <type> BitStringType
+%type <type> BooleanType
+%type <type> ChoiceType
+%type <type> ConstrainedType
+%type <type> EnumeratedType
+%type <type> IntegerType
+%type <type> NullType
+%type <type> OctetStringType
+%type <type> SequenceType
+%type <type> SequenceOfType
+%type <type> SetType
+%type <type> SetOfType
+%type <type> TaggedType
+%type <type> ReferencedType
+%type <type> DefinedType
+%type <type> UsefulType
+%type <type> ObjectIdentifierType
+%type <type> CharacterStringType
+%type <type> RestrictedCharactedStringType
+
+%type <tag> Tag
+
+%type <member> ComponentType
+%type <member> NamedBit
+%type <member> NamedNumber
+%type <member> NamedType
+%type <members> ComponentTypeList
+%type <members> Enumerations
+%type <members> NamedBitList
+%type <members> NamedNumberList
+
+%type <objid> objid objid_list objid_element objid_opt
+%type <range> range size
+
+%type <sl> referencenames
+
+%type <constraint_spec> Constraint
+%type <constraint_spec> ConstraintSpec
+%type <constraint_spec> GeneralConstraint
+%type <constraint_spec> ContentsConstraint
+%type <constraint_spec> UserDefinedConstraint
+
+
+
+%start ModuleDefinition
+
+%%
+
+ModuleDefinition: IDENTIFIER objid_opt kw_DEFINITIONS TagDefault ExtensionDefault
+ EEQUAL kw_BEGIN ModuleBody kw_END
+ {
+ checkundefined();
+ }
+ ;
+
+TagDefault : kw_EXPLICIT kw_TAGS
+ | kw_IMPLICIT kw_TAGS
+ { error_message("implicit tagging is not supported"); }
+ | kw_AUTOMATIC kw_TAGS
+ { error_message("automatic tagging is not supported"); }
+ | /* empty */
+ ;
+
+ExtensionDefault: kw_EXTENSIBILITY kw_IMPLIED
+ { error_message("no extensibility options supported"); }
+ | /* empty */
+ ;
+
+ModuleBody : /* Exports */ Imports AssignmentList
+ | /* empty */
+ ;
+
+Imports : kw_IMPORTS SymbolsImported ';'
+ | /* empty */
+ ;
+
+SymbolsImported : SymbolsFromModuleList
+ | /* empty */
+ ;
+
+SymbolsFromModuleList: SymbolsFromModule
+ | SymbolsFromModuleList SymbolsFromModule
+ ;
+
+SymbolsFromModule: referencenames kw_FROM IDENTIFIER objid_opt
+ {
+ struct string_list *sl;
+ for(sl = $1; sl != NULL; sl = sl->next) {
+ Symbol *s = addsym(sl->string);
+ s->stype = Stype;
+ }
+ add_import($3);
+ }
+ ;
+
+AssignmentList : Assignment
+ | Assignment AssignmentList
+ ;
+
+Assignment : TypeAssignment
+ | ValueAssignment
+ ;
+
+referencenames : IDENTIFIER ',' referencenames
+ {
+ $$ = emalloc(sizeof(*$$));
+ $$->string = $1;
+ $$->next = $3;
+ }
+ | IDENTIFIER
+ {
+ $$ = emalloc(sizeof(*$$));
+ $$->string = $1;
+ $$->next = NULL;
+ }
+ ;
+
+TypeAssignment : IDENTIFIER EEQUAL Type
+ {
+ Symbol *s = addsym ($1);
+ s->stype = Stype;
+ s->type = $3;
+ fix_labels(s);
+ generate_type (s);
+ }
+ ;
+
+Type : BuiltinType
+ | ReferencedType
+ | ConstrainedType
+ ;
+
+BuiltinType : BitStringType
+ | BooleanType
+ | CharacterStringType
+ | ChoiceType
+ | EnumeratedType
+ | IntegerType
+ | NullType
+ | ObjectIdentifierType
+ | OctetStringType
+ | SequenceType
+ | SequenceOfType
+ | SetType
+ | SetOfType
+ | TaggedType
+ ;
+
+BooleanType : kw_BOOLEAN
+ {
+ $$ = new_tag(ASN1_C_UNIV, UT_Boolean,
+ TE_EXPLICIT, new_type(TBoolean));
+ }
+ ;
+
+range : '(' Value RANGE Value ')'
+ {
+ if($2->type != integervalue)
+ error_message("Non-integer used in first part of range");
+ if($2->type != integervalue)
+ error_message("Non-integer in second part of range");
+ $$ = ecalloc(1, sizeof(*$$));
+ $$->min = $2->u.integervalue;
+ $$->max = $4->u.integervalue;
+ }
+ | '(' Value RANGE kw_MAX ')'
+ {
+ if($2->type != integervalue)
+ error_message("Non-integer in first part of range");
+ $$ = ecalloc(1, sizeof(*$$));
+ $$->min = $2->u.integervalue;
+ $$->max = $2->u.integervalue - 1;
+ }
+ | '(' kw_MIN RANGE Value ')'
+ {
+ if($4->type != integervalue)
+ error_message("Non-integer in second part of range");
+ $$ = ecalloc(1, sizeof(*$$));
+ $$->min = $4->u.integervalue + 2;
+ $$->max = $4->u.integervalue;
+ }
+ | '(' Value ')'
+ {
+ if($2->type != integervalue)
+ error_message("Non-integer used in limit");
+ $$ = ecalloc(1, sizeof(*$$));
+ $$->min = $2->u.integervalue;
+ $$->max = $2->u.integervalue;
+ }
+ ;
+
+
+IntegerType : kw_INTEGER
+ {
+ $$ = new_tag(ASN1_C_UNIV, UT_Integer,
+ TE_EXPLICIT, new_type(TInteger));
+ }
+ | kw_INTEGER range
+ {
+ $$ = new_type(TInteger);
+ $$->range = $2;
+ $$ = new_tag(ASN1_C_UNIV, UT_Integer, TE_EXPLICIT, $$);
+ }
+ | kw_INTEGER '{' NamedNumberList '}'
+ {
+ $$ = new_type(TInteger);
+ $$->members = $3;
+ $$ = new_tag(ASN1_C_UNIV, UT_Integer, TE_EXPLICIT, $$);
+ }
+ ;
+
+NamedNumberList : NamedNumber
+ {
+ $$ = emalloc(sizeof(*$$));
+ ASN1_TAILQ_INIT($$);
+ ASN1_TAILQ_INSERT_HEAD($$, $1, members);
+ }
+ | NamedNumberList ',' NamedNumber
+ {
+ ASN1_TAILQ_INSERT_TAIL($1, $3, members);
+ $$ = $1;
+ }
+ | NamedNumberList ',' ELLIPSIS
+ { $$ = $1; } /* XXX used for Enumerations */
+ ;
+
+NamedNumber : IDENTIFIER '(' SignedNumber ')'
+ {
+ $$ = emalloc(sizeof(*$$));
+ $$->name = $1;
+ $$->gen_name = estrdup($1);
+ output_name ($$->gen_name);
+ $$->val = $3;
+ $$->optional = 0;
+ $$->ellipsis = 0;
+ $$->type = NULL;
+ }
+ ;
+
+EnumeratedType : kw_ENUMERATED '{' Enumerations '}'
+ {
+ $$ = new_type(TInteger);
+ $$->members = $3;
+ $$ = new_tag(ASN1_C_UNIV, UT_Enumerated, TE_EXPLICIT, $$);
+ }
+ ;
+
+Enumerations : NamedNumberList /* XXX */
+ ;
+
+BitStringType : kw_BIT kw_STRING
+ {
+ $$ = new_type(TBitString);
+ $$->members = emalloc(sizeof(*$$->members));
+ ASN1_TAILQ_INIT($$->members);
+ $$ = new_tag(ASN1_C_UNIV, UT_BitString, TE_EXPLICIT, $$);
+ }
+ | kw_BIT kw_STRING '{' NamedBitList '}'
+ {
+ $$ = new_type(TBitString);
+ $$->members = $4;
+ $$ = new_tag(ASN1_C_UNIV, UT_BitString, TE_EXPLICIT, $$);
+ }
+ ;
+
+ObjectIdentifierType: kw_OBJECT kw_IDENTIFIER
+ {
+ $$ = new_tag(ASN1_C_UNIV, UT_OID,
+ TE_EXPLICIT, new_type(TOID));
+ }
+ ;
+OctetStringType : kw_OCTET kw_STRING size
+ {
+ Type *t = new_type(TOctetString);
+ t->range = $3;
+ $$ = new_tag(ASN1_C_UNIV, UT_OctetString,
+ TE_EXPLICIT, t);
+ }
+ ;
+
+NullType : kw_NULL
+ {
+ $$ = new_tag(ASN1_C_UNIV, UT_Null,
+ TE_EXPLICIT, new_type(TNull));
+ }
+ ;
+
+size :
+ { $$ = NULL; }
+ | kw_SIZE range
+ { $$ = $2; }
+ ;
+
+
+SequenceType : kw_SEQUENCE '{' /* ComponentTypeLists */ ComponentTypeList '}'
+ {
+ $$ = new_type(TSequence);
+ $$->members = $3;
+ $$ = new_tag(ASN1_C_UNIV, UT_Sequence, TE_EXPLICIT, $$);
+ }
+ | kw_SEQUENCE '{' '}'
+ {
+ $$ = new_type(TSequence);
+ $$->members = NULL;
+ $$ = new_tag(ASN1_C_UNIV, UT_Sequence, TE_EXPLICIT, $$);
+ }
+ ;
+
+SequenceOfType : kw_SEQUENCE size kw_OF Type
+ {
+ $$ = new_type(TSequenceOf);
+ $$->range = $2;
+ $$->subtype = $4;
+ $$ = new_tag(ASN1_C_UNIV, UT_Sequence, TE_EXPLICIT, $$);
+ }
+ ;
+
+SetType : kw_SET '{' /* ComponentTypeLists */ ComponentTypeList '}'
+ {
+ $$ = new_type(TSet);
+ $$->members = $3;
+ $$ = new_tag(ASN1_C_UNIV, UT_Set, TE_EXPLICIT, $$);
+ }
+ | kw_SET '{' '}'
+ {
+ $$ = new_type(TSet);
+ $$->members = NULL;
+ $$ = new_tag(ASN1_C_UNIV, UT_Set, TE_EXPLICIT, $$);
+ }
+ ;
+
+SetOfType : kw_SET kw_OF Type
+ {
+ $$ = new_type(TSetOf);
+ $$->subtype = $3;
+ $$ = new_tag(ASN1_C_UNIV, UT_Set, TE_EXPLICIT, $$);
+ }
+ ;
+
+ChoiceType : kw_CHOICE '{' /* AlternativeTypeLists */ ComponentTypeList '}'
+ {
+ $$ = new_type(TChoice);
+ $$->members = $3;
+ }
+ ;
+
+ReferencedType : DefinedType
+ | UsefulType
+ ;
+
+DefinedType : IDENTIFIER
+ {
+ Symbol *s = addsym($1);
+ $$ = new_type(TType);
+ if(s->stype != Stype && s->stype != SUndefined)
+ error_message ("%s is not a type\n", $1);
+ else
+ $$->symbol = s;
+ }
+ ;
+
+UsefulType : kw_GeneralizedTime
+ {
+ $$ = new_tag(ASN1_C_UNIV, UT_GeneralizedTime,
+ TE_EXPLICIT, new_type(TGeneralizedTime));
+ }
+ | kw_UTCTime
+ {
+ $$ = new_tag(ASN1_C_UNIV, UT_UTCTime,
+ TE_EXPLICIT, new_type(TUTCTime));
+ }
+ ;
+
+ConstrainedType : Type Constraint
+ {
+ /* if (Constraint.type == contentConstrant) {
+ assert(Constraint.u.constraint.type == octetstring|bitstring-w/o-NamedBitList); // remember to check type reference too
+ if (Constraint.u.constraint.type) {
+ assert((Constraint.u.constraint.type.length % 8) == 0);
+ }
+ }
+ if (Constraint.u.constraint.encoding) {
+ type == der-oid|ber-oid
+ }
+ */
+ }
+ ;
+
+
+Constraint : '(' ConstraintSpec ')'
+ {
+ $$ = $2;
+ }
+ ;
+
+ConstraintSpec : GeneralConstraint
+ ;
+
+GeneralConstraint: ContentsConstraint
+ | UserDefinedConstraint
+ ;
+
+ContentsConstraint: kw_CONTAINING Type
+ {
+ $$ = new_constraint_spec(CT_CONTENTS);
+ $$->u.content.type = $2;
+ $$->u.content.encoding = NULL;
+ }
+ | kw_ENCODED kw_BY Value
+ {
+ if ($3->type != objectidentifiervalue)
+ error_message("Non-OID used in ENCODED BY constraint");
+ $$ = new_constraint_spec(CT_CONTENTS);
+ $$->u.content.type = NULL;
+ $$->u.content.encoding = $3;
+ }
+ | kw_CONTAINING Type kw_ENCODED kw_BY Value
+ {
+ if ($5->type != objectidentifiervalue)
+ error_message("Non-OID used in ENCODED BY constraint");
+ $$ = new_constraint_spec(CT_CONTENTS);
+ $$->u.content.type = $2;
+ $$->u.content.encoding = $5;
+ }
+ ;
+
+UserDefinedConstraint: kw_CONSTRAINED kw_BY '{' '}'
+ {
+ $$ = new_constraint_spec(CT_USER);
+ }
+ ;
+
+TaggedType : Tag tagenv Type
+ {
+ $$ = new_type(TTag);
+ $$->tag = $1;
+ $$->tag.tagenv = $2;
+ if($3->type == TTag && $2 == TE_IMPLICIT) {
+ $$->subtype = $3->subtype;
+ free($3);
+ } else
+ $$->subtype = $3;
+ }
+ ;
+
+Tag : '[' Class NUMBER ']'
+ {
+ $$.tagclass = $2;
+ $$.tagvalue = $3;
+ $$.tagenv = TE_EXPLICIT;
+ }
+ ;
+
+Class : /* */
+ {
+ $$ = ASN1_C_CONTEXT;
+ }
+ | kw_UNIVERSAL
+ {
+ $$ = ASN1_C_UNIV;
+ }
+ | kw_APPLICATION
+ {
+ $$ = ASN1_C_APPL;
+ }
+ | kw_PRIVATE
+ {
+ $$ = ASN1_C_PRIVATE;
+ }
+ ;
+
+tagenv : /* */
+ {
+ $$ = TE_EXPLICIT;
+ }
+ | kw_EXPLICIT
+ {
+ $$ = TE_EXPLICIT;
+ }
+ | kw_IMPLICIT
+ {
+ $$ = TE_IMPLICIT;
+ }
+ ;
+
+
+ValueAssignment : IDENTIFIER Type EEQUAL Value
+ {
+ Symbol *s;
+ s = addsym ($1);
+
+ s->stype = SValue;
+ s->value = $4;
+ generate_constant (s);
+ }
+ ;
+
+CharacterStringType: RestrictedCharactedStringType
+ ;
+
+RestrictedCharactedStringType: kw_GeneralString
+ {
+ $$ = new_tag(ASN1_C_UNIV, UT_GeneralString,
+ TE_EXPLICIT, new_type(TGeneralString));
+ }
+ | kw_UTF8String
+ {
+ $$ = new_tag(ASN1_C_UNIV, UT_UTF8String,
+ TE_EXPLICIT, new_type(TUTF8String));
+ }
+ | kw_PrintableString
+ {
+ $$ = new_tag(ASN1_C_UNIV, UT_PrintableString,
+ TE_EXPLICIT, new_type(TPrintableString));
+ }
+ | kw_VisibleString
+ {
+ $$ = new_tag(ASN1_C_UNIV, UT_VisibleString,
+ TE_EXPLICIT, new_type(TVisibleString));
+ }
+ | kw_IA5String
+ {
+ $$ = new_tag(ASN1_C_UNIV, UT_IA5String,
+ TE_EXPLICIT, new_type(TIA5String));
+ }
+ | kw_BMPString
+ {
+ $$ = new_tag(ASN1_C_UNIV, UT_BMPString,
+ TE_EXPLICIT, new_type(TBMPString));
+ }
+ | kw_UniversalString
+ {
+ $$ = new_tag(ASN1_C_UNIV, UT_UniversalString,
+ TE_EXPLICIT, new_type(TUniversalString));
+ }
+
+ ;
+
+ComponentTypeList: ComponentType
+ {
+ $$ = emalloc(sizeof(*$$));
+ ASN1_TAILQ_INIT($$);
+ ASN1_TAILQ_INSERT_HEAD($$, $1, members);
+ }
+ | ComponentTypeList ',' ComponentType
+ {
+ ASN1_TAILQ_INSERT_TAIL($1, $3, members);
+ $$ = $1;
+ }
+ | ComponentTypeList ',' ELLIPSIS
+ {
+ struct member *m = ecalloc(1, sizeof(*m));
+ m->name = estrdup("...");
+ m->gen_name = estrdup("asn1_ellipsis");
+ m->ellipsis = 1;
+ ASN1_TAILQ_INSERT_TAIL($1, m, members);
+ $$ = $1;
+ }
+ ;
+
+NamedType : IDENTIFIER Type
+ {
+ $$ = emalloc(sizeof(*$$));
+ $$->name = $1;
+ $$->gen_name = estrdup($1);
+ output_name ($$->gen_name);
+ $$->type = $2;
+ $$->ellipsis = 0;
+ }
+ ;
+
+ComponentType : NamedType
+ {
+ $$ = $1;
+ $$->optional = 0;
+ $$->defval = NULL;
+ }
+ | NamedType kw_OPTIONAL
+ {
+ $$ = $1;
+ $$->optional = 1;
+ $$->defval = NULL;
+ }
+ | NamedType kw_DEFAULT Value
+ {
+ $$ = $1;
+ $$->optional = 0;
+ $$->defval = $3;
+ }
+ ;
+
+NamedBitList : NamedBit
+ {
+ $$ = emalloc(sizeof(*$$));
+ ASN1_TAILQ_INIT($$);
+ ASN1_TAILQ_INSERT_HEAD($$, $1, members);
+ }
+ | NamedBitList ',' NamedBit
+ {
+ ASN1_TAILQ_INSERT_TAIL($1, $3, members);
+ $$ = $1;
+ }
+ ;
+
+NamedBit : IDENTIFIER '(' NUMBER ')'
+ {
+ $$ = emalloc(sizeof(*$$));
+ $$->name = $1;
+ $$->gen_name = estrdup($1);
+ output_name ($$->gen_name);
+ $$->val = $3;
+ $$->optional = 0;
+ $$->ellipsis = 0;
+ $$->type = NULL;
+ }
+ ;
+
+objid_opt : objid
+ | /* empty */ { $$ = NULL; }
+ ;
+
+objid : '{' objid_list '}'
+ {
+ $$ = $2;
+ }
+ ;
+
+objid_list : /* empty */
+ {
+ $$ = NULL;
+ }
+ | objid_element objid_list
+ {
+ if ($2) {
+ $$ = $2;
+ add_oid_to_tail($2, $1);
+ } else {
+ $$ = $1;
+ }
+ }
+ ;
+
+objid_element : IDENTIFIER '(' NUMBER ')'
+ {
+ $$ = new_objid($1, $3);
+ }
+ | IDENTIFIER
+ {
+ Symbol *s = addsym($1);
+ if(s->stype != SValue ||
+ s->value->type != objectidentifiervalue) {
+ error_message("%s is not an object identifier\n",
+ s->name);
+ exit(1);
+ }
+ $$ = s->value->u.objectidentifiervalue;
+ }
+ | NUMBER
+ {
+ $$ = new_objid(NULL, $1);
+ }
+ ;
+
+Value : BuiltinValue
+ | ReferencedValue
+ ;
+
+BuiltinValue : BooleanValue
+ | CharacterStringValue
+ | IntegerValue
+ | ObjectIdentifierValue
+ | NullValue
+ ;
+
+ReferencedValue : DefinedValue
+ ;
+
+DefinedValue : Valuereference
+ ;
+
+Valuereference : IDENTIFIER
+ {
+ Symbol *s = addsym($1);
+ if(s->stype != SValue)
+ error_message ("%s is not a value\n",
+ s->name);
+ else
+ $$ = s->value;
+ }
+ ;
+
+CharacterStringValue: STRING
+ {
+ $$ = emalloc(sizeof(*$$));
+ $$->type = stringvalue;
+ $$->u.stringvalue = $1;
+ }
+ ;
+
+BooleanValue : kw_TRUE
+ {
+ $$ = emalloc(sizeof(*$$));
+ $$->type = booleanvalue;
+ $$->u.booleanvalue = 0;
+ }
+ | kw_FALSE
+ {
+ $$ = emalloc(sizeof(*$$));
+ $$->type = booleanvalue;
+ $$->u.booleanvalue = 0;
+ }
+ ;
+
+IntegerValue : SignedNumber
+ {
+ $$ = emalloc(sizeof(*$$));
+ $$->type = integervalue;
+ $$->u.integervalue = $1;
+ }
+ ;
+
+SignedNumber : NUMBER
+ ;
+
+NullValue : kw_NULL
+ {
+ }
+ ;
+
+ObjectIdentifierValue: objid
+ {
+ $$ = emalloc(sizeof(*$$));
+ $$->type = objectidentifiervalue;
+ $$->u.objectidentifiervalue = $1;
+ }
+ ;
+
+%%
+
+void
+yyerror (const char *s)
+{
+ error_message ("%s\n", s);
+}
+
+static Type *
+new_tag(int tagclass, int tagvalue, int tagenv, Type *oldtype)
+{
+ Type *t;
+ if(oldtype->type == TTag && oldtype->tag.tagenv == TE_IMPLICIT) {
+ t = oldtype;
+ oldtype = oldtype->subtype; /* XXX */
+ } else
+ t = new_type (TTag);
+
+ t->tag.tagclass = tagclass;
+ t->tag.tagvalue = tagvalue;
+ t->tag.tagenv = tagenv;
+ t->subtype = oldtype;
+ return t;
+}
+
+static struct objid *
+new_objid(const char *label, int value)
+{
+ struct objid *s;
+ s = emalloc(sizeof(*s));
+ s->label = label;
+ s->value = value;
+ s->next = NULL;
+ return s;
+}
+
+static void
+add_oid_to_tail(struct objid *head, struct objid *tail)
+{
+ struct objid *o;
+ o = head;
+ while (o->next)
+ o = o->next;
+ o->next = tail;
+}
+
+static Type *
+new_type (Typetype tt)
+{
+ Type *t = ecalloc(1, sizeof(*t));
+ t->type = tt;
+ return t;
+}
+
+static struct constraint_spec *
+new_constraint_spec(enum ctype ct)
+{
+ struct constraint_spec *c = ecalloc(1, sizeof(*c));
+ c->ctype = ct;
+ return c;
+}
+
+static void fix_labels2(Type *t, const char *prefix);
+static void fix_labels1(struct memhead *members, const char *prefix)
+{
+ Member *m;
+
+ if(members == NULL)
+ return;
+ ASN1_TAILQ_FOREACH(m, members, members) {
+ asprintf(&m->label, "%s_%s", prefix, m->gen_name);
+ if (m->label == NULL)
+ errx(1, "malloc");
+ if(m->type != NULL)
+ fix_labels2(m->type, m->label);
+ }
+}
+
+static void fix_labels2(Type *t, const char *prefix)
+{
+ for(; t; t = t->subtype)
+ fix_labels1(t->members, prefix);
+}
+
+static void
+fix_labels(Symbol *s)
+{
+ char *p;
+ asprintf(&p, "choice_%s", s->gen_name);
+ if (p == NULL)
+ errx(1, "malloc");
+ fix_labels2(s->type, p);
+ free(p);
+}
diff --git a/source4/heimdal/lib/asn1/pkcs12.asn1 b/source4/heimdal/lib/asn1/pkcs12.asn1
new file mode 100644
index 0000000000..4d6454a08f
--- /dev/null
+++ b/source4/heimdal/lib/asn1/pkcs12.asn1
@@ -0,0 +1,81 @@
+-- $Id$ --
+
+PKCS12 DEFINITIONS ::=
+
+BEGIN
+
+IMPORTS ContentInfo FROM cms
+ DigestInfo FROM rfc2459
+ heim_any, heim_any_set FROM heim;
+
+-- The PFX PDU
+
+id-pkcs-12 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840)
+ rsadsi(113549) pkcs(1) pkcs-12(12) }
+
+id-pkcs-12PbeIds OBJECT IDENTIFIER ::= { id-pkcs-12 1}
+id-pbeWithSHAAnd128BitRC4 OBJECT IDENTIFIER ::= { id-pkcs-12PbeIds 1}
+id-pbeWithSHAAnd40BitRC4 OBJECT IDENTIFIER ::= { id-pkcs-12PbeIds 2}
+id-pbeWithSHAAnd3-KeyTripleDES-CBC OBJECT IDENTIFIER ::= { id-pkcs-12PbeIds 3}
+id-pbeWithSHAAnd2-KeyTripleDES-CBC OBJECT IDENTIFIER ::= { id-pkcs-12PbeIds 4}
+id-pbeWithSHAAnd128BitRC2-CBC OBJECT IDENTIFIER ::= { id-pkcs-12PbeIds 5}
+id-pbewithSHAAnd40BitRC2-CBC OBJECT IDENTIFIER ::= { id-pkcs-12PbeIds 6}
+
+id-pkcs12-bagtypes OBJECT IDENTIFIER ::= { id-pkcs-12 10 1}
+
+id-pkcs12-keyBag OBJECT IDENTIFIER ::= { id-pkcs12-bagtypes 1 }
+id-pkcs12-pkcs8ShroudedKeyBag OBJECT IDENTIFIER ::= { id-pkcs12-bagtypes 2 }
+id-pkcs12-certBag OBJECT IDENTIFIER ::= { id-pkcs12-bagtypes 3 }
+id-pkcs12-crlBag OBJECT IDENTIFIER ::= { id-pkcs12-bagtypes 4 }
+id-pkcs12-secretBag OBJECT IDENTIFIER ::= { id-pkcs12-bagtypes 5 }
+id-pkcs12-safeContentsBag OBJECT IDENTIFIER ::= { id-pkcs12-bagtypes 6 }
+
+
+PKCS12-MacData ::= SEQUENCE {
+ mac DigestInfo,
+ macSalt OCTET STRING,
+ iterations INTEGER OPTIONAL
+}
+
+PKCS12-PFX ::= SEQUENCE {
+ version INTEGER,
+ authSafe ContentInfo,
+ macData PKCS12-MacData OPTIONAL
+}
+
+PKCS12-AuthenticatedSafe ::= SEQUENCE OF ContentInfo
+ -- Data if unencrypted
+ -- EncryptedData if password-encrypted
+ -- EnvelopedData if public key-encrypted
+
+PKCS12-Attribute ::= SEQUENCE {
+ attrId OBJECT IDENTIFIER,
+ attrValues -- SET OF -- heim_any_set
+}
+
+PKCS12-Attributes ::= SET OF PKCS12-Attribute
+
+PKCS12-SafeBag ::= SEQUENCE {
+ bagId OBJECT IDENTIFIER,
+ bagValue [0] heim_any,
+ bagAttributes PKCS12-Attributes OPTIONAL
+}
+
+PKCS12-SafeContents ::= SEQUENCE OF PKCS12-SafeBag
+
+PKCS12-CertBag ::= SEQUENCE {
+ certType OBJECT IDENTIFIER,
+ certValue [0] heim_any
+}
+
+PKCS12-PBEParams ::= SEQUENCE {
+ salt OCTET STRING,
+ iterations INTEGER (0..4294967295) OPTIONAL
+}
+
+PKCS12-OctetString ::= OCTET STRING
+
+-- KeyBag ::= PrivateKeyInfo
+-- PKCS8ShroudedKeyBag ::= EncryptedPrivateKeyInfo
+
+END
diff --git a/source4/heimdal/lib/asn1/pkcs8.asn1 b/source4/heimdal/lib/asn1/pkcs8.asn1
new file mode 100644
index 0000000000..203d91eef8
--- /dev/null
+++ b/source4/heimdal/lib/asn1/pkcs8.asn1
@@ -0,0 +1,30 @@
+-- $Id$ --
+
+PKCS8 DEFINITIONS ::=
+
+BEGIN
+
+IMPORTS Attribute, AlgorithmIdentifier FROM rfc2459
+ heim_any, heim_any_set FROM heim;
+
+PKCS8PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
+
+PKCS8PrivateKey ::= OCTET STRING
+
+PKCS8Attributes ::= SET OF Attribute
+
+PKCS8PrivateKeyInfo ::= SEQUENCE {
+ version INTEGER,
+ privateKeyAlgorithm PKCS8PrivateKeyAlgorithmIdentifier,
+ privateKey PKCS8PrivateKey,
+ attributes [0] IMPLICIT SET OF Attribute OPTIONAL
+}
+
+PKCS8EncryptedData ::= OCTET STRING
+
+PKCS8EncryptedPrivateKeyInfo ::= SEQUENCE {
+ encryptionAlgorithm AlgorithmIdentifier,
+ encryptedData PKCS8EncryptedData
+}
+
+END
diff --git a/source4/heimdal/lib/asn1/pkcs9.asn1 b/source4/heimdal/lib/asn1/pkcs9.asn1
new file mode 100644
index 0000000000..50bf9dd1cd
--- /dev/null
+++ b/source4/heimdal/lib/asn1/pkcs9.asn1
@@ -0,0 +1,28 @@
+-- $Id$ --
+
+PKCS9 DEFINITIONS ::=
+
+BEGIN
+
+-- The PFX PDU
+
+id-pkcs-9 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840)
+ rsadsi(113549) pkcs(1) pkcs-9(9) }
+
+id-pkcs9-emailAddress OBJECT IDENTIFIER ::= {id-pkcs-9 1 }
+id-pkcs9-contentType OBJECT IDENTIFIER ::= {id-pkcs-9 3 }
+id-pkcs9-messageDigest OBJECT IDENTIFIER ::= {id-pkcs-9 4 }
+id-pkcs9-signingTime OBJECT IDENTIFIER ::= {id-pkcs-9 5 }
+id-pkcs9-countersignature OBJECT IDENTIFIER ::= {id-pkcs-9 6 }
+
+id-pkcs-9-at-friendlyName OBJECT IDENTIFIER ::= {id-pkcs-9 20}
+id-pkcs-9-at-localKeyId OBJECT IDENTIFIER ::= {id-pkcs-9 21}
+id-pkcs-9-at-certTypes OBJECT IDENTIFIER ::= {id-pkcs-9 22}
+id-pkcs-9-at-certTypes-x509 OBJECT IDENTIFIER ::= {id-pkcs-9-at-certTypes 1}
+
+PKCS9-BMPString ::= BMPString
+
+PKCS9-friendlyName ::= SET OF PKCS9-BMPString
+
+END
+
diff --git a/source4/heimdal/lib/asn1/pkinit.asn1 b/source4/heimdal/lib/asn1/pkinit.asn1
new file mode 100644
index 0000000000..758af6f86e
--- /dev/null
+++ b/source4/heimdal/lib/asn1/pkinit.asn1
@@ -0,0 +1,195 @@
+-- $Id$ --
+
+PKINIT DEFINITIONS ::= BEGIN
+
+IMPORTS EncryptionKey, PrincipalName, Realm, KerberosTime, Checksum, Ticket FROM krb5
+ IssuerAndSerialNumber, ContentInfo FROM cms
+ SubjectPublicKeyInfo, AlgorithmIdentifier FROM rfc2459
+ heim_any FROM heim;
+
+id-pkinit OBJECT IDENTIFIER ::=
+ { iso (1) org (3) dod (6) internet (1) security (5)
+ kerberosv5 (2) pkinit (3) }
+
+id-pkauthdata OBJECT IDENTIFIER ::= { id-pkinit 1 }
+id-pkdhkeydata OBJECT IDENTIFIER ::= { id-pkinit 2 }
+id-pkrkeydata OBJECT IDENTIFIER ::= { id-pkinit 3 }
+id-pkekuoid OBJECT IDENTIFIER ::= { id-pkinit 4 }
+id-pkkdcekuoid OBJECT IDENTIFIER ::= { id-pkinit 5 }
+
+id-pkinit-kdf OBJECT IDENTIFIER ::= { id-pkinit 6 }
+id-pkinit-kdf-ah-sha1 OBJECT IDENTIFIER ::= { id-pkinit-kdf 1 }
+id-pkinit-kdf-ah-sha256 OBJECT IDENTIFIER ::= { id-pkinit-kdf 2 }
+id-pkinit-kdf-ah-sha512 OBJECT IDENTIFIER ::= { id-pkinit-kdf 3 }
+
+id-pkinit-san OBJECT IDENTIFIER ::=
+ { iso(1) org(3) dod(6) internet(1) security(5) kerberosv5(2)
+ x509-sanan(2) }
+
+id-pkinit-ms-eku OBJECT IDENTIFIER ::=
+ { iso(1) org(3) dod(6) internet(1) private(4)
+ enterprise(1) microsoft(311) 20 2 2 }
+
+id-pkinit-ms-san OBJECT IDENTIFIER ::=
+ { iso(1) org(3) dod(6) internet(1) private(4)
+ enterprise(1) microsoft(311) 20 2 3 }
+
+MS-UPN-SAN ::= UTF8String
+
+pa-pk-as-req INTEGER ::= 16
+pa-pk-as-rep INTEGER ::= 17
+
+td-trusted-certifiers INTEGER ::= 104
+td-invalid-certificates INTEGER ::= 105
+td-dh-parameters INTEGER ::= 109
+
+DHNonce ::= OCTET STRING
+
+KDFAlgorithmId ::= SEQUENCE {
+ kdf-id [0] OBJECT IDENTIFIER,
+ ...
+}
+
+TrustedCA ::= SEQUENCE {
+ caName [0] IMPLICIT OCTET STRING,
+ certificateSerialNumber [1] INTEGER OPTIONAL,
+ subjectKeyIdentifier [2] OCTET STRING OPTIONAL,
+ ...
+}
+
+ExternalPrincipalIdentifier ::= SEQUENCE {
+ subjectName [0] IMPLICIT OCTET STRING OPTIONAL,
+ issuerAndSerialNumber [1] IMPLICIT OCTET STRING OPTIONAL,
+ subjectKeyIdentifier [2] IMPLICIT OCTET STRING OPTIONAL,
+ ...
+}
+
+ExternalPrincipalIdentifiers ::= SEQUENCE OF ExternalPrincipalIdentifier
+
+PA-PK-AS-REQ ::= SEQUENCE {
+ signedAuthPack [0] IMPLICIT OCTET STRING,
+ trustedCertifiers [1] ExternalPrincipalIdentifiers OPTIONAL,
+ kdcPkId [2] IMPLICIT OCTET STRING OPTIONAL,
+ ...
+}
+
+PKAuthenticator ::= SEQUENCE {
+ cusec [0] INTEGER -- (0..999999) --,
+ ctime [1] KerberosTime,
+ nonce [2] INTEGER (0..4294967295),
+ paChecksum [3] OCTET STRING OPTIONAL,
+ ...
+}
+
+AuthPack ::= SEQUENCE {
+ pkAuthenticator [0] PKAuthenticator,
+ clientPublicValue [1] SubjectPublicKeyInfo OPTIONAL,
+ supportedCMSTypes [2] SEQUENCE OF AlgorithmIdentifier OPTIONAL,
+ clientDHNonce [3] DHNonce OPTIONAL,
+ ...,
+ supportedKDFs [4] SEQUENCE OF KDFAlgorithmId OPTIONAL,
+ ...
+}
+
+TD-TRUSTED-CERTIFIERS ::= ExternalPrincipalIdentifiers
+TD-INVALID-CERTIFICATES ::= ExternalPrincipalIdentifiers
+
+KRB5PrincipalName ::= SEQUENCE {
+ realm [0] Realm,
+ principalName [1] PrincipalName
+}
+
+AD-INITIAL-VERIFIED-CAS ::= SEQUENCE OF ExternalPrincipalIdentifier
+
+DHRepInfo ::= SEQUENCE {
+ dhSignedData [0] IMPLICIT OCTET STRING,
+ serverDHNonce [1] DHNonce OPTIONAL,
+ ...,
+ kdf [2] KDFAlgorithmId OPTIONAL,
+ ...
+}
+
+PA-PK-AS-REP ::= CHOICE {
+ dhInfo [0] DHRepInfo,
+ encKeyPack [1] IMPLICIT OCTET STRING,
+ ...
+}
+
+KDCDHKeyInfo ::= SEQUENCE {
+ subjectPublicKey [0] BIT STRING,
+ nonce [1] INTEGER (0..4294967295),
+ dhKeyExpiration [2] KerberosTime OPTIONAL,
+ ...
+}
+
+ReplyKeyPack ::= SEQUENCE {
+ replyKey [0] EncryptionKey,
+ asChecksum [1] Checksum,
+ ...
+}
+
+TD-DH-PARAMETERS ::= SEQUENCE OF AlgorithmIdentifier
+
+
+-- Windows compat glue --
+
+PKAuthenticator-Win2k ::= SEQUENCE {
+ kdcName [0] PrincipalName,
+ kdcRealm [1] Realm,
+ cusec [2] INTEGER (0..4294967295),
+ ctime [3] KerberosTime,
+ nonce [4] INTEGER (-2147483648..2147483647)
+}
+
+AuthPack-Win2k ::= SEQUENCE {
+ pkAuthenticator [0] PKAuthenticator-Win2k,
+ clientPublicValue [1] SubjectPublicKeyInfo OPTIONAL
+}
+
+
+TrustedCA-Win2k ::= CHOICE {
+ caName [1] heim_any,
+ issuerAndSerial [2] IssuerAndSerialNumber
+}
+
+PA-PK-AS-REQ-Win2k ::= SEQUENCE {
+ signed-auth-pack [0] IMPLICIT OCTET STRING,
+ trusted-certifiers [2] SEQUENCE OF TrustedCA-Win2k OPTIONAL,
+ kdc-cert [3] IMPLICIT OCTET STRING OPTIONAL,
+ encryption-cert [4] IMPLICIT OCTET STRING OPTIONAL
+}
+
+PA-PK-AS-REP-Win2k ::= CHOICE {
+ dhSignedData [0] IMPLICIT OCTET STRING,
+ encKeyPack [1] IMPLICIT OCTET STRING
+}
+
+
+KDCDHKeyInfo-Win2k ::= SEQUENCE {
+ nonce [0] INTEGER (-2147483648..2147483647),
+ subjectPublicKey [2] BIT STRING
+}
+
+ReplyKeyPack-Win2k ::= SEQUENCE {
+ replyKey [0] EncryptionKey,
+ nonce [1] INTEGER (-2147483648..2147483647),
+ ...
+}
+
+PkinitSP80056AOtherInfo ::= SEQUENCE {
+ algorithmID AlgorithmIdentifier,
+ partyUInfo [0] OCTET STRING,
+ partyVInfo [1] OCTET STRING,
+ suppPubInfo [2] OCTET STRING OPTIONAL,
+ suppPrivInfo [3] OCTET STRING OPTIONAL
+}
+
+PkinitSuppPubInfo ::= SEQUENCE {
+ enctype [0] INTEGER (-2147483648..2147483647),
+ as-REQ [1] OCTET STRING,
+ pk-as-rep [2] OCTET STRING,
+ ticket [3] Ticket,
+ ...
+}
+
+END
diff --git a/source4/heimdal/lib/asn1/rfc2459.asn1 b/source4/heimdal/lib/asn1/rfc2459.asn1
new file mode 100644
index 0000000000..8e24f0740b
--- /dev/null
+++ b/source4/heimdal/lib/asn1/rfc2459.asn1
@@ -0,0 +1,506 @@
+-- $Id$ --
+-- Definitions from rfc2459/rfc3280
+
+RFC2459 DEFINITIONS ::= BEGIN
+
+IMPORTS heim_any FROM heim;
+
+Version ::= INTEGER {
+ rfc3280_version_1(0),
+ rfc3280_version_2(1),
+ rfc3280_version_3(2)
+}
+
+id-pkcs-1 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840)
+ rsadsi(113549) pkcs(1) 1 }
+id-pkcs1-rsaEncryption OBJECT IDENTIFIER ::= { id-pkcs-1 1 }
+id-pkcs1-md2WithRSAEncryption OBJECT IDENTIFIER ::= { id-pkcs-1 2 }
+id-pkcs1-md5WithRSAEncryption OBJECT IDENTIFIER ::= { id-pkcs-1 4 }
+id-pkcs1-sha1WithRSAEncryption OBJECT IDENTIFIER ::= { id-pkcs-1 5 }
+id-pkcs1-sha256WithRSAEncryption OBJECT IDENTIFIER ::= { id-pkcs-1 11 }
+id-pkcs1-sha384WithRSAEncryption OBJECT IDENTIFIER ::= { id-pkcs-1 12 }
+id-pkcs1-sha512WithRSAEncryption OBJECT IDENTIFIER ::= { id-pkcs-1 13 }
+
+id-heim-rsa-pkcs1-x509 OBJECT IDENTIFIER ::= { 1 2 752 43 16 1 }
+
+id-pkcs-2 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840)
+ rsadsi(113549) pkcs(1) 2 }
+id-pkcs2-md2 OBJECT IDENTIFIER ::= { id-pkcs-2 2 }
+id-pkcs2-md4 OBJECT IDENTIFIER ::= { id-pkcs-2 4 }
+id-pkcs2-md5 OBJECT IDENTIFIER ::= { id-pkcs-2 5 }
+
+id-rsa-digestAlgorithm OBJECT IDENTIFIER ::=
+{ iso(1) member-body(2) us(840) rsadsi(113549) 2 }
+
+id-rsa-digest-md2 OBJECT IDENTIFIER ::= { id-rsa-digestAlgorithm 2 }
+id-rsa-digest-md4 OBJECT IDENTIFIER ::= { id-rsa-digestAlgorithm 4 }
+id-rsa-digest-md5 OBJECT IDENTIFIER ::= { id-rsa-digestAlgorithm 5 }
+
+id-pkcs-3 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840)
+ rsadsi(113549) pkcs(1) 3 }
+
+id-pkcs3-rc2-cbc OBJECT IDENTIFIER ::= { id-pkcs-3 2 }
+id-pkcs3-rc4 OBJECT IDENTIFIER ::= { id-pkcs-3 4 }
+id-pkcs3-des-ede3-cbc OBJECT IDENTIFIER ::= { id-pkcs-3 7 }
+
+id-rsadsi-encalg OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840)
+ rsadsi(113549) 3 }
+
+id-rsadsi-rc2-cbc OBJECT IDENTIFIER ::= { id-rsadsi-encalg 2 }
+id-rsadsi-des-ede3-cbc OBJECT IDENTIFIER ::= { id-rsadsi-encalg 7 }
+
+id-secsig-sha-1 OBJECT IDENTIFIER ::= { iso(1) identified-organization(3)
+ oiw(14) secsig(3) algorithm(2) 26 }
+
+id-nistAlgorithm OBJECT IDENTIFIER ::= {
+ joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101) csor(3) 4 }
+
+id-nist-aes-algs OBJECT IDENTIFIER ::= { id-nistAlgorithm 1 }
+
+id-aes-128-cbc OBJECT IDENTIFIER ::= { id-nist-aes-algs 2 }
+id-aes-192-cbc OBJECT IDENTIFIER ::= { id-nist-aes-algs 22 }
+id-aes-256-cbc OBJECT IDENTIFIER ::= { id-nist-aes-algs 42 }
+
+id-nist-sha-algs OBJECT IDENTIFIER ::= { id-nistAlgorithm 2 }
+
+id-sha256 OBJECT IDENTIFIER ::= { id-nist-sha-algs 1 }
+id-sha224 OBJECT IDENTIFIER ::= { id-nist-sha-algs 4 }
+id-sha384 OBJECT IDENTIFIER ::= { id-nist-sha-algs 2 }
+id-sha512 OBJECT IDENTIFIER ::= { id-nist-sha-algs 3 }
+
+id-dhpublicnumber OBJECT IDENTIFIER ::= {
+ iso(1) member-body(2) us(840) ansi-x942(10046)
+ number-type(2) 1 }
+
+id-x9-57 OBJECT IDENTIFIER ::= {
+ iso(1) member-body(2) us(840) ansi-x942(10046)
+ 4 }
+
+id-dsa OBJECT IDENTIFIER ::= { id-x9-57 1 }
+id-dsa-with-sha1 OBJECT IDENTIFIER ::= { id-x9-57 3 }
+
+-- x.520 names types
+
+id-x520-at OBJECT IDENTIFIER ::= { joint-iso-ccitt(2) ds(5) 4 }
+
+id-at-commonName OBJECT IDENTIFIER ::= { id-x520-at 3 }
+id-at-surname OBJECT IDENTIFIER ::= { id-x520-at 4 }
+id-at-serialNumber OBJECT IDENTIFIER ::= { id-x520-at 5 }
+id-at-countryName OBJECT IDENTIFIER ::= { id-x520-at 6 }
+id-at-localityName OBJECT IDENTIFIER ::= { id-x520-at 7 }
+id-at-stateOrProvinceName OBJECT IDENTIFIER ::= { id-x520-at 8 }
+id-at-streetAddress OBJECT IDENTIFIER ::= { id-x520-at 9 }
+id-at-organizationName OBJECT IDENTIFIER ::= { id-x520-at 10 }
+id-at-organizationalUnitName OBJECT IDENTIFIER ::= { id-x520-at 11 }
+id-at-name OBJECT IDENTIFIER ::= { id-x520-at 41 }
+id-at-givenName OBJECT IDENTIFIER ::= { id-x520-at 42 }
+id-at-initials OBJECT IDENTIFIER ::= { id-x520-at 43 }
+id-at-generationQualifier OBJECT IDENTIFIER ::= { id-x520-at 44 }
+id-at-pseudonym OBJECT IDENTIFIER ::= { id-x520-at 65 }
+-- RFC 2247
+id-Userid OBJECT IDENTIFIER ::=
+ { 0 9 2342 19200300 100 1 1 }
+id-domainComponent OBJECT IDENTIFIER ::=
+ { 0 9 2342 19200300 100 1 25 }
+
+
+-- rfc3280
+
+id-x509-ce OBJECT IDENTIFIER ::= {joint-iso-ccitt(2) ds(5) 29}
+
+AlgorithmIdentifier ::= SEQUENCE {
+ algorithm OBJECT IDENTIFIER,
+ parameters heim_any OPTIONAL
+}
+
+AttributeType ::= OBJECT IDENTIFIER
+
+AttributeValue ::= heim_any
+
+TeletexStringx ::= [UNIVERSAL 20] IMPLICIT OCTET STRING
+
+DirectoryString ::= CHOICE {
+ ia5String IA5String,
+ teletexString TeletexStringx,
+ printableString PrintableString,
+ universalString UniversalString,
+ utf8String UTF8String,
+ bmpString BMPString
+}
+
+Attribute ::= SEQUENCE {
+ type AttributeType,
+ value SET OF -- AttributeValue -- heim_any
+}
+
+AttributeTypeAndValue ::= SEQUENCE {
+ type AttributeType,
+ value DirectoryString
+}
+
+RelativeDistinguishedName ::= SET OF AttributeTypeAndValue
+
+RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
+
+Name ::= CHOICE {
+ rdnSequence RDNSequence
+}
+
+CertificateSerialNumber ::= INTEGER
+
+Time ::= CHOICE {
+ utcTime UTCTime,
+ generalTime GeneralizedTime
+}
+
+Validity ::= SEQUENCE {
+ notBefore Time,
+ notAfter Time
+}
+
+UniqueIdentifier ::= BIT STRING
+
+SubjectPublicKeyInfo ::= SEQUENCE {
+ algorithm AlgorithmIdentifier,
+ subjectPublicKey BIT STRING
+}
+
+Extension ::= SEQUENCE {
+ extnID OBJECT IDENTIFIER,
+ critical BOOLEAN OPTIONAL, -- DEFAULT FALSE XXX
+ extnValue OCTET STRING
+}
+
+Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
+
+TBSCertificate ::= SEQUENCE {
+ version [0] Version OPTIONAL, -- EXPLICIT nnn DEFAULT 1,
+ serialNumber CertificateSerialNumber,
+ signature AlgorithmIdentifier,
+ issuer Name,
+ validity Validity,
+ subject Name,
+ subjectPublicKeyInfo SubjectPublicKeyInfo,
+ issuerUniqueID [1] IMPLICIT BIT STRING -- UniqueIdentifier -- OPTIONAL,
+ -- If present, version shall be v2 or v3
+ subjectUniqueID [2] IMPLICIT BIT STRING -- UniqueIdentifier -- OPTIONAL,
+ -- If present, version shall be v2 or v3
+ extensions [3] EXPLICIT Extensions OPTIONAL
+ -- If present, version shall be v3
+}
+
+Certificate ::= SEQUENCE {
+ tbsCertificate TBSCertificate,
+ signatureAlgorithm AlgorithmIdentifier,
+ signatureValue BIT STRING
+}
+
+Certificates ::= SEQUENCE OF Certificate
+
+ValidationParms ::= SEQUENCE {
+ seed BIT STRING,
+ pgenCounter INTEGER
+}
+
+DomainParameters ::= SEQUENCE {
+ p INTEGER, -- odd prime, p=jq +1
+ g INTEGER, -- generator, g
+ q INTEGER, -- factor of p-1
+ j INTEGER OPTIONAL, -- subgroup factor
+ validationParms ValidationParms OPTIONAL -- ValidationParms
+}
+
+DHPublicKey ::= INTEGER
+
+OtherName ::= SEQUENCE {
+ type-id OBJECT IDENTIFIER,
+ value [0] EXPLICIT heim_any
+}
+
+GeneralName ::= CHOICE {
+ otherName [0] IMPLICIT -- OtherName -- SEQUENCE {
+ type-id OBJECT IDENTIFIER,
+ value [0] EXPLICIT heim_any
+ },
+ rfc822Name [1] IMPLICIT IA5String,
+ dNSName [2] IMPLICIT IA5String,
+-- x400Address [3] IMPLICIT ORAddress,--
+ directoryName [4] IMPLICIT -- Name -- CHOICE {
+ rdnSequence RDNSequence
+ },
+-- ediPartyName [5] IMPLICIT EDIPartyName, --
+ uniformResourceIdentifier [6] IMPLICIT IA5String,
+ iPAddress [7] IMPLICIT OCTET STRING,
+ registeredID [8] IMPLICIT OBJECT IDENTIFIER
+}
+
+GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
+
+id-x509-ce-keyUsage OBJECT IDENTIFIER ::= { id-x509-ce 15 }
+
+KeyUsage ::= BIT STRING {
+ digitalSignature (0),
+ nonRepudiation (1),
+ keyEncipherment (2),
+ dataEncipherment (3),
+ keyAgreement (4),
+ keyCertSign (5),
+ cRLSign (6),
+ encipherOnly (7),
+ decipherOnly (8)
+}
+
+id-x509-ce-authorityKeyIdentifier OBJECT IDENTIFIER ::= { id-x509-ce 35 }
+
+KeyIdentifier ::= OCTET STRING
+
+AuthorityKeyIdentifier ::= SEQUENCE {
+ keyIdentifier [0] IMPLICIT OCTET STRING OPTIONAL,
+ authorityCertIssuer [1] IMPLICIT -- GeneralName --
+ SEQUENCE -- SIZE (1..MAX) -- OF GeneralName OPTIONAL,
+ authorityCertSerialNumber [2] IMPLICIT INTEGER OPTIONAL
+}
+
+id-x509-ce-subjectKeyIdentifier OBJECT IDENTIFIER ::= { id-x509-ce 14 }
+
+SubjectKeyIdentifier ::= KeyIdentifier
+
+id-x509-ce-basicConstraints OBJECT IDENTIFIER ::= { id-x509-ce 19 }
+
+BasicConstraints ::= SEQUENCE {
+ cA BOOLEAN OPTIONAL -- DEFAULT FALSE --,
+ pathLenConstraint INTEGER (0..4294967295) OPTIONAL
+}
+
+id-x509-ce-nameConstraints OBJECT IDENTIFIER ::= { id-x509-ce 30 }
+
+BaseDistance ::= INTEGER -- (0..MAX) --
+
+GeneralSubtree ::= SEQUENCE {
+ base GeneralName,
+ minimum [0] IMPLICIT -- BaseDistance -- INTEGER OPTIONAL -- DEFAULT 0 --,
+ maximum [1] IMPLICIT -- BaseDistance -- INTEGER OPTIONAL
+}
+
+GeneralSubtrees ::= SEQUENCE -- SIZE (1..MAX) -- OF GeneralSubtree
+
+NameConstraints ::= SEQUENCE {
+ permittedSubtrees [0] IMPLICIT -- GeneralSubtrees -- SEQUENCE OF GeneralSubtree OPTIONAL,
+ excludedSubtrees [1] IMPLICIT -- GeneralSubtrees -- SEQUENCE OF GeneralSubtree OPTIONAL
+}
+
+id-x509-ce-privateKeyUsagePeriod OBJECT IDENTIFIER ::= { id-x509-ce 16 }
+id-x509-ce-certificatePolicies OBJECT IDENTIFIER ::= { id-x509-ce 32 }
+id-x509-ce-policyMappings OBJECT IDENTIFIER ::= { id-x509-ce 33 }
+id-x509-ce-subjectAltName OBJECT IDENTIFIER ::= { id-x509-ce 17 }
+id-x509-ce-issuerAltName OBJECT IDENTIFIER ::= { id-x509-ce 18 }
+id-x509-ce-subjectDirectoryAttributes OBJECT IDENTIFIER ::= { id-x509-ce 9 }
+id-x509-ce-policyConstraints OBJECT IDENTIFIER ::= { id-x509-ce 36 }
+
+id-x509-ce-extKeyUsage OBJECT IDENTIFIER ::= { id-x509-ce 37}
+
+ExtKeyUsage ::= SEQUENCE OF OBJECT IDENTIFIER
+
+id-x509-ce-cRLDistributionPoints OBJECT IDENTIFIER ::= { id-x509-ce 31 }
+id-x509-ce-deltaCRLIndicator OBJECT IDENTIFIER ::= { id-x509-ce 27 }
+id-x509-ce-issuingDistributionPoint OBJECT IDENTIFIER ::= { id-x509-ce 28 }
+id-x509-ce-holdInstructionCode OBJECT IDENTIFIER ::= { id-x509-ce 23 }
+id-x509-ce-invalidityDate OBJECT IDENTIFIER ::= { id-x509-ce 24 }
+id-x509-ce-certificateIssuer OBJECT IDENTIFIER ::= { id-x509-ce 29 }
+id-x509-ce-inhibitAnyPolicy OBJECT IDENTIFIER ::= { id-x509-ce 54 }
+
+DistributionPointReasonFlags ::= BIT STRING {
+ unused (0),
+ keyCompromise (1),
+ cACompromise (2),
+ affiliationChanged (3),
+ superseded (4),
+ cessationOfOperation (5),
+ certificateHold (6),
+ privilegeWithdrawn (7),
+ aACompromise (8)
+}
+
+DistributionPointName ::= CHOICE {
+ fullName [0] IMPLICIT -- GeneralNames -- SEQUENCE SIZE (1..MAX) OF GeneralName,
+ nameRelativeToCRLIssuer [1] RelativeDistinguishedName
+}
+
+DistributionPoint ::= SEQUENCE {
+ distributionPoint [0] IMPLICIT heim_any -- DistributionPointName -- OPTIONAL,
+ reasons [1] IMPLICIT heim_any -- DistributionPointReasonFlags -- OPTIONAL,
+ cRLIssuer [2] IMPLICIT heim_any -- GeneralNames -- OPTIONAL
+}
+
+CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint
+
+
+-- rfc3279
+
+DSASigValue ::= SEQUENCE {
+ r INTEGER,
+ s INTEGER
+}
+
+DSAPublicKey ::= INTEGER
+
+DSAParams ::= SEQUENCE {
+ p INTEGER,
+ q INTEGER,
+ g INTEGER
+}
+
+-- really pkcs1
+
+RSAPublicKey ::= SEQUENCE {
+ modulus INTEGER, -- n
+ publicExponent INTEGER -- e
+}
+
+RSAPrivateKey ::= SEQUENCE {
+ version INTEGER (0..4294967295),
+ modulus INTEGER, -- n
+ publicExponent INTEGER, -- e
+ privateExponent INTEGER, -- d
+ prime1 INTEGER, -- p
+ prime2 INTEGER, -- q
+ exponent1 INTEGER, -- d mod (p-1)
+ exponent2 INTEGER, -- d mod (q-1)
+ coefficient INTEGER -- (inverse of q) mod p
+}
+
+DigestInfo ::= SEQUENCE {
+ digestAlgorithm AlgorithmIdentifier,
+ digest OCTET STRING
+}
+
+-- some ms ext
+
+-- szOID_ENROLL_CERTTYPE_EXTENSION "1.3.6.1.4.1.311.20.2" is Encoded as a
+
+-- UNICODESTRING (0x1E tag)
+
+-- szOID_CERTIFICATE_TEMPLATE "1.3.6.1.4.1.311.21.7" is Encoded as:
+
+-- TemplateVersion ::= INTEGER (0..4294967295)
+
+-- CertificateTemplate ::= SEQUENCE {
+-- templateID OBJECT IDENTIFIER,
+-- templateMajorVersion TemplateVersion,
+-- templateMinorVersion TemplateVersion OPTIONAL
+-- }
+
+
+--
+-- CRL
+--
+
+TBSCRLCertList ::= SEQUENCE {
+ version Version OPTIONAL, -- if present, MUST be v2
+ signature AlgorithmIdentifier,
+ issuer Name,
+ thisUpdate Time,
+ nextUpdate Time OPTIONAL,
+ revokedCertificates SEQUENCE OF SEQUENCE {
+ userCertificate CertificateSerialNumber,
+ revocationDate Time,
+ crlEntryExtensions Extensions OPTIONAL
+ -- if present, MUST be v2
+ } OPTIONAL,
+ crlExtensions [0] EXPLICIT Extensions OPTIONAL
+ -- if present, MUST be v2
+}
+
+
+CRLCertificateList ::= SEQUENCE {
+ tbsCertList TBSCRLCertList,
+ signatureAlgorithm AlgorithmIdentifier,
+ signatureValue BIT STRING
+}
+
+id-x509-ce-cRLNumber OBJECT IDENTIFIER ::= { id-x509-ce 20 }
+id-x509-ce-freshestCRL OBJECT IDENTIFIER ::= { id-x509-ce 46 }
+id-x509-ce-cRLReason OBJECT IDENTIFIER ::= { id-x509-ce 21 }
+
+CRLReason ::= ENUMERATED {
+ unspecified (0),
+ keyCompromise (1),
+ cACompromise (2),
+ affiliationChanged (3),
+ superseded (4),
+ cessationOfOperation (5),
+ certificateHold (6),
+ removeFromCRL (8),
+ privilegeWithdrawn (9),
+ aACompromise (10)
+}
+
+PKIXXmppAddr ::= UTF8String
+
+id-pkix OBJECT IDENTIFIER ::= { iso(1) identified-organization(3)
+ dod(6) internet(1) security(5) mechanisms(5) pkix(7) }
+
+id-pkix-on OBJECT IDENTIFIER ::= { id-pkix 8 }
+id-pkix-on-xmppAddr OBJECT IDENTIFIER ::= { id-pkix-on 5 }
+id-pkix-on-dnsSRV OBJECT IDENTIFIER ::= { id-pkix-on 7 }
+
+id-pkix-kp OBJECT IDENTIFIER ::= { id-pkix 3 }
+id-pkix-kp-serverAuth OBJECT IDENTIFIER ::= { id-pkix-kp 1 }
+id-pkix-kp-clientAuth OBJECT IDENTIFIER ::= { id-pkix-kp 2 }
+id-pkix-kp-emailProtection OBJECT IDENTIFIER ::= { id-pkix-kp 4 }
+id-pkix-kp-timeStamping OBJECT IDENTIFIER ::= { id-pkix-kp 8 }
+id-pkix-kp-OCSPSigning OBJECT IDENTIFIER ::= { id-pkix-kp 9 }
+
+id-pkix-pe OBJECT IDENTIFIER ::= { id-pkix 1 }
+
+id-pkix-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pkix-pe 1 }
+
+AccessDescription ::= SEQUENCE {
+ accessMethod OBJECT IDENTIFIER,
+ accessLocation GeneralName
+}
+
+AuthorityInfoAccessSyntax ::= SEQUENCE SIZE (1..MAX) OF AccessDescription
+
+-- RFC 3820 Proxy Certificate Profile
+
+id-pkix-pe-proxyCertInfo OBJECT IDENTIFIER ::= { id-pkix-pe 14 }
+
+id-pkix-ppl OBJECT IDENTIFIER ::= { id-pkix 21 }
+
+id-pkix-ppl-anyLanguage OBJECT IDENTIFIER ::= { id-pkix-ppl 0 }
+id-pkix-ppl-inheritAll OBJECT IDENTIFIER ::= { id-pkix-ppl 1 }
+id-pkix-ppl-independent OBJECT IDENTIFIER ::= { id-pkix-ppl 2 }
+
+ProxyPolicy ::= SEQUENCE {
+ policyLanguage OBJECT IDENTIFIER,
+ policy OCTET STRING OPTIONAL
+}
+
+ProxyCertInfo ::= SEQUENCE {
+ pCPathLenConstraint INTEGER (0..4294967295) OPTIONAL, -- really MAX
+ proxyPolicy ProxyPolicy
+}
+
+--- U.S. Federal PKI Common Policy Framework
+-- Card Authentication key
+id-uspkicommon-card-id OBJECT IDENTIFIER ::= { 2 16 840 1 101 3 6 6 }
+id-uspkicommon-piv-interim OBJECT IDENTIFIER ::= { 2 16 840 1 101 3 6 9 1 }
+
+--- Netscape extentions
+
+id-netscape OBJECT IDENTIFIER ::=
+ { joint-iso-itu-t(2) country(16) us(840) organization(1) netscape(113730) }
+id-netscape-cert-comment OBJECT IDENTIFIER ::= { id-netscape 1 13 }
+
+--- MS extentions
+
+id-ms-cert-enroll-domaincontroller OBJECT IDENTIFIER ::=
+ { 1 3 6 1 4 1 311 20 2 }
+
+id-ms-client-authentication OBJECT IDENTIFIER ::=
+ { 1 3 6 1 5 5 7 3 2 }
+
+-- DER:1e:20:00:44:00:6f:00:6d:00:61:00:69:00:6e:00:43:00:6f:00:6e:00:74:00:72:00:6f:00:6c:00:6c:00:65:00:72
+
+END
diff --git a/source4/heimdal/lib/asn1/symbol.c b/source4/heimdal/lib/asn1/symbol.c
new file mode 100644
index 0000000000..ac00bdc6cb
--- /dev/null
+++ b/source4/heimdal/lib/asn1/symbol.c
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 1997 - 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "gen_locl.h"
+#include "lex.h"
+
+RCSID("$Id$");
+
+static Hashtab *htab;
+
+static int
+cmp(void *a, void *b)
+{
+ Symbol *s1 = (Symbol *) a;
+ Symbol *s2 = (Symbol *) b;
+
+ return strcmp(s1->name, s2->name);
+}
+
+static unsigned
+hash(void *a)
+{
+ Symbol *s = (Symbol *) a;
+
+ return hashjpw(s->name);
+}
+
+void
+initsym(void)
+{
+ htab = hashtabnew(101, cmp, hash);
+}
+
+
+void
+output_name(char *s)
+{
+ char *p;
+
+ for (p = s; *p; ++p)
+ if (*p == '-')
+ *p = '_';
+}
+
+Symbol *
+addsym(char *name)
+{
+ Symbol key, *s;
+
+ key.name = name;
+ s = (Symbol *) hashtabsearch(htab, (void *) &key);
+ if (s == NULL) {
+ s = (Symbol *) emalloc(sizeof(*s));
+ s->name = name;
+ s->gen_name = estrdup(name);
+ output_name(s->gen_name);
+ s->stype = SUndefined;
+ hashtabadd(htab, s);
+ }
+ return s;
+}
+
+static int
+checkfunc(void *ptr, void *arg)
+{
+ Symbol *s = ptr;
+ if (s->stype == SUndefined) {
+ error_message("%s is still undefined\n", s->name);
+ *(int *) arg = 1;
+ }
+ return 0;
+}
+
+int
+checkundefined(void)
+{
+ int f = 0;
+ hashtabforeach(htab, checkfunc, &f);
+ return f;
+}
diff --git a/source4/heimdal/lib/asn1/symbol.h b/source4/heimdal/lib/asn1/symbol.h
new file mode 100644
index 0000000000..3a0f807980
--- /dev/null
+++ b/source4/heimdal/lib/asn1/symbol.h
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 1997 - 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef _SYMBOL_H
+#define _SYMBOL_H
+
+#include "asn1_queue.h"
+
+enum typetype {
+ TBitString,
+ TBoolean,
+ TChoice,
+ TEnumerated,
+ TGeneralString,
+ TGeneralizedTime,
+ TIA5String,
+ TInteger,
+ TNull,
+ TOID,
+ TOctetString,
+ TPrintableString,
+ TSequence,
+ TSequenceOf,
+ TSet,
+ TSetOf,
+ TTag,
+ TType,
+ TUTCTime,
+ TUTF8String,
+ TBMPString,
+ TUniversalString,
+ TVisibleString
+};
+
+typedef enum typetype Typetype;
+
+struct type;
+
+struct value {
+ enum { booleanvalue,
+ nullvalue,
+ integervalue,
+ stringvalue,
+ objectidentifiervalue
+ } type;
+ union {
+ int booleanvalue;
+ int integervalue;
+ char *stringvalue;
+ struct objid *objectidentifiervalue;
+ } u;
+};
+
+struct member {
+ char *name;
+ char *gen_name;
+ char *label;
+ int val;
+ int optional;
+ int ellipsis;
+ struct type *type;
+ ASN1_TAILQ_ENTRY(member) members;
+ struct value *defval;
+};
+
+typedef struct member Member;
+
+ASN1_TAILQ_HEAD(memhead, member);
+
+struct symbol;
+
+struct tagtype {
+ int tagclass;
+ int tagvalue;
+ enum { TE_IMPLICIT, TE_EXPLICIT } tagenv;
+};
+
+struct range {
+ int min;
+ int max;
+};
+
+enum ctype { CT_CONTENTS, CT_USER } ;
+
+struct constraint_spec;
+
+struct type {
+ Typetype type;
+ struct memhead *members;
+ struct symbol *symbol;
+ struct type *subtype;
+ struct tagtype tag;
+ struct range *range;
+ struct constraint_spec *constraint;
+};
+
+typedef struct type Type;
+
+struct constraint_spec {
+ enum ctype ctype;
+ union {
+ struct {
+ Type *type;
+ struct value *encoding;
+ } content;
+ } u;
+};
+
+struct objid {
+ const char *label;
+ int value;
+ struct objid *next;
+};
+
+struct symbol {
+ char *name;
+ char *gen_name;
+ enum { SUndefined, SValue, Stype } stype;
+ struct value *value;
+ Type *type;
+};
+
+typedef struct symbol Symbol;
+
+void initsym (void);
+Symbol *addsym (char *);
+void output_name (char *);
+int checkundefined(void);
+#endif
diff --git a/source4/heimdal/lib/asn1/test.asn1 b/source4/heimdal/lib/asn1/test.asn1
new file mode 100644
index 0000000000..d07bba6185
--- /dev/null
+++ b/source4/heimdal/lib/asn1/test.asn1
@@ -0,0 +1,95 @@
+-- $Id$ --
+
+TEST DEFINITIONS ::=
+
+BEGIN
+
+IMPORTS heim_any FROM heim;
+
+TESTLargeTag ::= SEQUENCE {
+ foo[127] INTEGER (-2147483648..2147483647)
+}
+
+TESTSeq ::= SEQUENCE {
+ tag0[0] INTEGER (-2147483648..2147483647),
+ tag1[1] TESTLargeTag,
+ tagless INTEGER (-2147483648..2147483647),
+ tag3[2] INTEGER (-2147483648..2147483647)
+}
+
+TESTChoice1 ::= CHOICE {
+ i1[1] INTEGER (-2147483648..2147483647),
+ i2[2] INTEGER (-2147483648..2147483647),
+ ...
+}
+
+TESTChoice2 ::= CHOICE {
+ i1[1] INTEGER (-2147483648..2147483647),
+ ...
+}
+
+TESTInteger ::= INTEGER (-2147483648..2147483647)
+
+TESTInteger2 ::= [4] IMPLICIT TESTInteger
+TESTInteger3 ::= [5] IMPLICIT TESTInteger2
+
+TESTImplicit ::= SEQUENCE {
+ ti1[0] IMPLICIT INTEGER (-2147483648..2147483647),
+ ti2[1] IMPLICIT SEQUENCE {
+ foo[127] INTEGER (-2147483648..2147483647)
+ },
+ ti3[2] IMPLICIT [5] IMPLICIT [4] IMPLICIT INTEGER (-2147483648..2147483647)
+}
+
+TESTImplicit2 ::= SEQUENCE {
+ ti1[0] IMPLICIT TESTInteger,
+ ti2[1] IMPLICIT TESTLargeTag,
+ ti3[2] IMPLICIT TESTInteger3
+}
+
+TESTAllocInner ::= SEQUENCE {
+ ai[0] TESTInteger
+}
+
+TESTAlloc ::= SEQUENCE {
+ tagless TESTAllocInner OPTIONAL,
+ three [1] INTEGER (-2147483648..2147483647),
+ tagless2 heim_any OPTIONAL
+}
+
+
+TESTCONTAINING ::= OCTET STRING ( CONTAINING INTEGER )
+TESTENCODEDBY ::= OCTET STRING ( ENCODED BY
+ { joint-iso-itu-t(2) asn(1) ber-derived(2) distinguished-encoding(1) }
+)
+
+TESTDer OBJECT IDENTIFIER ::= {
+ joint-iso-itu-t(2) asn(1) ber-derived(2) distinguished-encoding(1)
+}
+
+TESTCONTAININGENCODEDBY ::= OCTET STRING ( CONTAINING INTEGER ENCODED BY
+ { joint-iso-itu-t(2) asn(1) ber-derived(2) distinguished-encoding(1) }
+)
+
+TESTCONTAININGENCODEDBY2 ::= OCTET STRING (
+ CONTAINING INTEGER ENCODED BY TESTDer
+)
+
+
+TESTValue1 INTEGER ::= 1
+
+TESTUSERCONSTRAINED ::= OCTET STRING (CONSTRAINED BY { -- meh -- })
+-- TESTUSERCONSTRAINED2 ::= OCTET STRING (CONSTRAINED BY { TESTInteger })
+-- TESTUSERCONSTRAINED3 ::= OCTET STRING (CONSTRAINED BY { INTEGER })
+-- TESTUSERCONSTRAINED4 ::= OCTET STRING (CONSTRAINED BY { INTEGER : 1 })
+
+TESTSeqOf ::= SEQUENCE OF TESTInteger
+
+TESTSeqSizeOf1 ::= SEQUENCE SIZE (2) OF TESTInteger
+TESTSeqSizeOf2 ::= SEQUENCE SIZE (1..2) OF TESTInteger
+TESTSeqSizeOf3 ::= SEQUENCE SIZE (1..MAX) OF TESTInteger
+TESTSeqSizeOf4 ::= SEQUENCE SIZE (MIN..2) OF TESTInteger
+
+TESTOSSize1 ::= OCTET STRING SIZE (1..2)
+
+END
diff --git a/source4/heimdal/lib/asn1/test.gen b/source4/heimdal/lib/asn1/test.gen
new file mode 100644
index 0000000000..bfb0486481
--- /dev/null
+++ b/source4/heimdal/lib/asn1/test.gen
@@ -0,0 +1,14 @@
+# $Id$
+# Sample for TESTSeq in test.asn1
+#
+
+UNIV CONS Sequence 23
+ CONTEXT CONS 0 3
+ UNIV PRIM Integer 1 01
+ CONTEXT CONS 1 8
+ UNIV CONS Sequence 6
+ CONTEXT CONS 127 3
+ UNIV PRIM Integer 1 01
+ UNIV PRIM Integer 1 01
+ CONTEXT CONS 2 3
+ UNIV PRIM Integer 1 01
diff --git a/source4/heimdal/lib/asn1/timegm.c b/source4/heimdal/lib/asn1/timegm.c
new file mode 100644
index 0000000000..c72968dc04
--- /dev/null
+++ b/source4/heimdal/lib/asn1/timegm.c
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 1997 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "der_locl.h"
+
+RCSID("$Id$");
+
+static int
+is_leap(unsigned y)
+{
+ y += 1900;
+ return (y % 4) == 0 && ((y % 100) != 0 || (y % 400) == 0);
+}
+
+/*
+ * This is a simplifed version of timegm(3) that doesn't accept out of
+ * bound values that timegm(3) normally accepts but those are not
+ * valid in asn1 encodings.
+ */
+
+time_t
+_der_timegm (struct tm *tm)
+{
+ static const unsigned ndays[2][12] ={
+ {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
+ {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}};
+ time_t res = 0;
+ unsigned i;
+
+ if (tm->tm_year < 0)
+ return -1;
+ if (tm->tm_mon < 0 || tm->tm_mon > 11)
+ return -1;
+ if (tm->tm_mday < 1 || tm->tm_mday > ndays[is_leap(tm->tm_year)][tm->tm_mon])
+ return -1;
+ if (tm->tm_hour < 0 || tm->tm_hour > 23)
+ return -1;
+ if (tm->tm_min < 0 || tm->tm_min > 59)
+ return -1;
+ if (tm->tm_sec < 0 || tm->tm_sec > 59)
+ return -1;
+
+ for (i = 70; i < tm->tm_year; ++i)
+ res += is_leap(i) ? 366 : 365;
+
+ for (i = 0; i < tm->tm_mon; ++i)
+ res += ndays[is_leap(tm->tm_year)][i];
+ res += tm->tm_mday - 1;
+ res *= 24;
+ res += tm->tm_hour;
+ res *= 60;
+ res += tm->tm_min;
+ res *= 60;
+ res += tm->tm_sec;
+ return res;
+}
diff --git a/source4/heimdal/lib/com_err/com_err.c b/source4/heimdal/lib/com_err/com_err.c
new file mode 100644
index 0000000000..89c8ab2c89
--- /dev/null
+++ b/source4/heimdal/lib/com_err/com_err.c
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 1997 - 2002 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+RCSID("$Id$");
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <roken.h>
+#include "com_err.h"
+
+struct et_list *_et_list = NULL;
+
+
+const char *
+error_message (long code)
+{
+ static char msg[128];
+ const char *p = com_right(_et_list, code);
+ if (p == NULL) {
+ if (code < 0)
+ snprintf(msg, sizeof(msg), "Unknown error %ld", code);
+ else
+ p = strerror(code);
+ }
+ if (p != NULL && *p != '\0') {
+ strlcpy(msg, p, sizeof(msg));
+ } else
+ snprintf(msg, sizeof(msg), "Unknown error %ld", code);
+ return msg;
+}
+
+int
+init_error_table(const char **msgs, long base, int count)
+{
+ initialize_error_table_r(&_et_list, msgs, count, base);
+ return 0;
+}
+
+static void
+default_proc (const char *whoami, long code, const char *fmt, va_list args)
+ __attribute__((__format__(__printf__, 3, 0)));
+
+static void
+default_proc (const char *whoami, long code, const char *fmt, va_list args)
+{
+ if (whoami)
+ fprintf(stderr, "%s: ", whoami);
+ if (code)
+ fprintf(stderr, "%s ", error_message(code));
+ if (fmt)
+ vfprintf(stderr, fmt, args);
+ fprintf(stderr, "\r\n"); /* ??? */
+}
+
+static errf com_err_hook = default_proc;
+
+void
+com_err_va (const char *whoami,
+ long code,
+ const char *fmt,
+ va_list args)
+{
+ (*com_err_hook) (whoami, code, fmt, args);
+}
+
+void
+com_err (const char *whoami,
+ long code,
+ const char *fmt,
+ ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ com_err_va (whoami, code, fmt, ap);
+ va_end(ap);
+}
+
+errf
+set_com_err_hook (errf new)
+{
+ errf old = com_err_hook;
+
+ if (new)
+ com_err_hook = new;
+ else
+ com_err_hook = default_proc;
+
+ return old;
+}
+
+errf
+reset_com_err_hook (void)
+{
+ return set_com_err_hook(NULL);
+}
+
+#define ERRCODE_RANGE 8 /* # of bits to shift table number */
+#define BITS_PER_CHAR 6 /* # bits to shift per character in name */
+
+static const char char_set[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_";
+
+static char buf[6];
+
+const char *
+error_table_name(int num)
+{
+ int ch;
+ int i;
+ char *p;
+
+ /* num = aa aaa abb bbb bcc ccc cdd ddd d?? ??? ??? */
+ p = buf;
+ num >>= ERRCODE_RANGE;
+ /* num = ?? ??? ??? aaa aaa bbb bbb ccc ccc ddd ddd */
+ num &= 077777777;
+ /* num = 00 000 000 aaa aaa bbb bbb ccc ccc ddd ddd */
+ for (i = 4; i >= 0; i--) {
+ ch = (num >> BITS_PER_CHAR * i) & ((1 << BITS_PER_CHAR) - 1);
+ if (ch != 0)
+ *p++ = char_set[ch-1];
+ }
+ *p = '\0';
+ return(buf);
+}
+
+void
+add_to_error_table(struct et_list *new_table)
+{
+ struct et_list *et;
+
+ for (et = _et_list; et; et = et->next) {
+ if (et->table->base == new_table->table->base)
+ return;
+ }
+
+ new_table->next = _et_list;
+ _et_list = new_table;
+}
diff --git a/source4/heimdal/lib/com_err/com_err.h b/source4/heimdal/lib/com_err/com_err.h
new file mode 100644
index 0000000000..a8ceb969cb
--- /dev/null
+++ b/source4/heimdal/lib/com_err/com_err.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+/* MIT compatible com_err library */
+
+#ifndef __COM_ERR_H__
+#define __COM_ERR_H__
+
+#include <com_right.h>
+#include <stdarg.h>
+
+#if !defined(__GNUC__) && !defined(__attribute__)
+#define __attribute__(X)
+#endif
+
+typedef void (*errf) (const char *, long, const char *, va_list);
+
+const char * error_message (long);
+int init_error_table (const char**, long, int);
+
+void com_err_va (const char *, long, const char *, va_list)
+ __attribute__((format(printf, 3, 0)));
+
+void com_err (const char *, long, const char *, ...)
+ __attribute__((format(printf, 3, 4)));
+
+errf set_com_err_hook (errf);
+errf reset_com_err_hook (void);
+
+const char *error_table_name (int num);
+
+void add_to_error_table (struct et_list *new_table);
+
+#endif /* __COM_ERR_H__ */
diff --git a/source4/heimdal/lib/com_err/com_right.h b/source4/heimdal/lib/com_err/com_right.h
new file mode 100644
index 0000000000..46aec001ad
--- /dev/null
+++ b/source4/heimdal/lib/com_err/com_right.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 1997 - 2000 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef __COM_RIGHT_H__
+#define __COM_RIGHT_H__
+
+#ifdef __STDC__
+#include <stdarg.h>
+#endif
+
+struct error_table {
+ char const * const * msgs;
+ long base;
+ int n_msgs;
+};
+struct et_list {
+ struct et_list *next;
+ struct error_table *table;
+};
+extern struct et_list *_et_list;
+
+const char *com_right (struct et_list *list, long code);
+void initialize_error_table_r (struct et_list **, const char **, int, long);
+void free_error_table (struct et_list *);
+
+#endif /* __COM_RIGHT_H__ */
diff --git a/source4/heimdal/lib/com_err/compile_et.c b/source4/heimdal/lib/com_err/compile_et.c
new file mode 100644
index 0000000000..239617d1de
--- /dev/null
+++ b/source4/heimdal/lib/com_err/compile_et.c
@@ -0,0 +1,241 @@
+/*
+ * Copyright (c) 1998-2002 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#undef ROKEN_RENAME
+#include "compile_et.h"
+#include <getarg.h>
+
+RCSID("$Id$");
+
+#include <roken.h>
+#include <err.h>
+#include "parse.h"
+
+int numerror;
+extern FILE *yyin;
+
+extern void yyparse(void);
+
+long base_id;
+int number;
+char *prefix;
+char *id_str;
+
+char name[128];
+char Basename[128];
+
+#ifdef YYDEBUG
+extern int yydebug = 1;
+#endif
+
+char *filename;
+char hfn[128];
+char cfn[128];
+
+struct error_code *codes = NULL;
+
+static int
+generate_c(void)
+{
+ int n;
+ struct error_code *ec;
+
+ FILE *c_file = fopen(cfn, "w");
+ if(c_file == NULL)
+ return 1;
+
+ fprintf(c_file, "/* Generated from %s */\n", filename);
+ if(id_str)
+ fprintf(c_file, "/* %s */\n", id_str);
+ fprintf(c_file, "\n");
+ fprintf(c_file, "#include <stddef.h>\n");
+ fprintf(c_file, "#include <com_err.h>\n");
+ fprintf(c_file, "#include \"%s\"\n", hfn);
+ fprintf(c_file, "\n");
+ fprintf(c_file, "#define N_(x) (x)\n");
+ fprintf(c_file, "\n");
+
+ fprintf(c_file, "static const char *%s_error_strings[] = {\n", name);
+
+ for(ec = codes, n = 0; ec; ec = ec->next, n++) {
+ while(n < ec->number) {
+ fprintf(c_file, "\t/* %03d */ \"Reserved %s error (%d)\",\n",
+ n, name, n);
+ n++;
+
+ }
+ fprintf(c_file, "\t/* %03d */ N_(\"%s\"),\n",
+ ec->number, ec->string);
+ }
+
+ fprintf(c_file, "\tNULL\n");
+ fprintf(c_file, "};\n");
+ fprintf(c_file, "\n");
+ fprintf(c_file, "#define num_errors %d\n", number);
+ fprintf(c_file, "\n");
+ fprintf(c_file,
+ "void initialize_%s_error_table_r(struct et_list **list)\n",
+ name);
+ fprintf(c_file, "{\n");
+ fprintf(c_file,
+ " initialize_error_table_r(list, %s_error_strings, "
+ "num_errors, ERROR_TABLE_BASE_%s);\n", name, name);
+ fprintf(c_file, "}\n");
+ fprintf(c_file, "\n");
+ fprintf(c_file, "void initialize_%s_error_table(void)\n", name);
+ fprintf(c_file, "{\n");
+ fprintf(c_file,
+ " init_error_table(%s_error_strings, ERROR_TABLE_BASE_%s, "
+ "num_errors);\n", name, name);
+ fprintf(c_file, "}\n");
+
+ fclose(c_file);
+ return 0;
+}
+
+static int
+generate_h(void)
+{
+ struct error_code *ec;
+ char fn[128];
+ FILE *h_file = fopen(hfn, "w");
+ char *p;
+
+ if(h_file == NULL)
+ return 1;
+
+ snprintf(fn, sizeof(fn), "__%s__", hfn);
+ for(p = fn; *p; p++)
+ if(!isalnum((unsigned char)*p))
+ *p = '_';
+
+ fprintf(h_file, "/* Generated from %s */\n", filename);
+ if(id_str)
+ fprintf(h_file, "/* %s */\n", id_str);
+ fprintf(h_file, "\n");
+ fprintf(h_file, "#ifndef %s\n", fn);
+ fprintf(h_file, "#define %s\n", fn);
+ fprintf(h_file, "\n");
+ fprintf(h_file, "struct et_list;\n");
+ fprintf(h_file, "\n");
+ fprintf(h_file,
+ "void initialize_%s_error_table_r(struct et_list **);\n",
+ name);
+ fprintf(h_file, "\n");
+ fprintf(h_file, "void initialize_%s_error_table(void);\n", name);
+ fprintf(h_file, "#define init_%s_err_tbl initialize_%s_error_table\n",
+ name, name);
+ fprintf(h_file, "\n");
+ fprintf(h_file, "typedef enum %s_error_number{\n", name);
+
+ for(ec = codes; ec; ec = ec->next) {
+ fprintf(h_file, "\t%s = %ld%s\n", ec->name, base_id + ec->number,
+ (ec->next != NULL) ? "," : "");
+ }
+
+ fprintf(h_file, "} %s_error_number;\n", name);
+ fprintf(h_file, "\n");
+ fprintf(h_file, "#define ERROR_TABLE_BASE_%s %ld\n", name, base_id);
+ fprintf(h_file, "\n");
+ fprintf(h_file, "#define COM_ERR_BINDDOMAIN_%s \"heim_com_err%ld\"\n", name, base_id);
+ fprintf(h_file, "\n");
+ fprintf(h_file, "#endif /* %s */\n", fn);
+
+
+ fclose(h_file);
+ return 0;
+}
+
+static int
+generate(void)
+{
+ return generate_c() || generate_h();
+}
+
+int version_flag;
+int help_flag;
+struct getargs args[] = {
+ { "version", 0, arg_flag, &version_flag },
+ { "help", 0, arg_flag, &help_flag }
+};
+int num_args = sizeof(args) / sizeof(args[0]);
+
+static void
+usage(int code)
+{
+ arg_printusage(args, num_args, NULL, "error-table");
+ exit(code);
+}
+
+int
+main(int argc, char **argv)
+{
+ char *p;
+ int optidx = 0;
+
+ setprogname(argv[0]);
+ if(getarg(args, num_args, argc, argv, &optidx))
+ usage(1);
+ if(help_flag)
+ usage(0);
+ if(version_flag) {
+ print_version(NULL);
+ exit(0);
+ }
+
+ if(optidx == argc)
+ usage(1);
+ filename = argv[optidx];
+ yyin = fopen(filename, "r");
+ if(yyin == NULL)
+ err(1, "%s", filename);
+
+
+ p = strrchr(filename, '/');
+ if(p)
+ p++;
+ else
+ p = filename;
+ strlcpy(Basename, p, sizeof(Basename));
+
+ Basename[strcspn(Basename, ".")] = '\0';
+
+ snprintf(hfn, sizeof(hfn), "%s.h", Basename);
+ snprintf(cfn, sizeof(cfn), "%s.c", Basename);
+
+ yyparse();
+ if(numerror)
+ return 1;
+
+ return generate();
+}
diff --git a/source4/heimdal/lib/com_err/compile_et.h b/source4/heimdal/lib/com_err/compile_et.h
new file mode 100644
index 0000000000..430405d9d5
--- /dev/null
+++ b/source4/heimdal/lib/com_err/compile_et.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 1998 - 2000 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef __COMPILE_ET_H__
+#define __COMPILE_ET_H__
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <err.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <roken.h>
+
+extern long base_id;
+extern int number;
+extern char *prefix;
+extern char name[128];
+extern char *id_str;
+extern char *filename;
+extern int numerror;
+
+struct error_code {
+ unsigned number;
+ char *name;
+ char *string;
+ struct error_code *next, **tail;
+};
+
+extern struct error_code *codes;
+
+#define APPEND(L, V) \
+do { \
+ if((L) == NULL) { \
+ (L) = (V); \
+ (L)->tail = &(V)->next; \
+ (L)->next = NULL; \
+ }else{ \
+ *(L)->tail = (V); \
+ (L)->tail = &(V)->next; \
+ } \
+}while(0)
+
+#endif /* __COMPILE_ET_H__ */
diff --git a/source4/heimdal/lib/com_err/error.c b/source4/heimdal/lib/com_err/error.c
new file mode 100644
index 0000000000..d64225a758
--- /dev/null
+++ b/source4/heimdal/lib/com_err/error.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 1997, 1998, 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+RCSID("$Id$");
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <com_right.h>
+
+#ifdef LIBINTL
+#include <libintl.h>
+#else
+#define dgettext(d,s) (s)
+#endif
+
+const char *
+com_right(struct et_list *list, long code)
+{
+ struct et_list *p;
+ for (p = list; p; p = p->next) {
+ if (code >= p->table->base && code < p->table->base + p->table->n_msgs) {
+ const char *str = p->table->msgs[code - p->table->base];
+#ifdef LIBINTL
+ char domain[12 + 20];
+ snprintf(domain, sizeof(domain), "heim_com_err%d", p->table->base);
+#endif
+ return dgettext(domain, str);
+ }
+
+ }
+ return NULL;
+}
+
+struct foobar {
+ struct et_list etl;
+ struct error_table et;
+};
+
+void
+initialize_error_table_r(struct et_list **list,
+ const char **messages,
+ int num_errors,
+ long base)
+{
+ struct et_list *et, **end;
+ struct foobar *f;
+ for (end = list, et = *list; et; end = &et->next, et = et->next)
+ if (et->table->msgs == messages)
+ return;
+ f = malloc(sizeof(*f));
+ if (f == NULL)
+ return;
+ et = &f->etl;
+ et->table = &f->et;
+ et->table->msgs = messages;
+ et->table->n_msgs = num_errors;
+ et->table->base = base;
+ et->next = NULL;
+ *end = et;
+}
+
+
+void
+free_error_table(struct et_list *et)
+{
+ while(et){
+ struct et_list *p = et;
+ et = et->next;
+ free(p);
+ }
+}
diff --git a/source4/heimdal/lib/com_err/lex.c b/source4/heimdal/lib/com_err/lex.c
new file mode 100644
index 0000000000..fbc14c9984
--- /dev/null
+++ b/source4/heimdal/lib/com_err/lex.c
@@ -0,0 +1,1912 @@
+#include "config.h"
+
+#line 3 "heimdal/lib/com_err/lex.c"
+
+#define YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+#define YY_FLEX_SUBMINOR_VERSION 34
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* First, we deal with platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types.
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t;
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+#endif /* ! C99 */
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX (4294967295U)
+#endif
+
+#endif /* ! FLEXINT_H */
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else /* ! __cplusplus */
+
+/* C99 requires __STDC__ to be defined as 1. */
+#if defined (__STDC__)
+
+#define YY_USE_CONST
+
+#endif /* defined (__STDC__) */
+#endif /* ! __cplusplus */
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index. If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* Enter a start condition. This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN (yy_start) = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state. The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START (((yy_start) - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE yyrestart(yyin )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#define YY_BUF_SIZE 16384
+#endif
+
+/* The state buf must be large enough to hold one state per character in the main buffer.
+ */
+#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+extern int yyleng;
+
+extern FILE *yyin, *yyout;
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+ #define YY_LESS_LINENO(n)
+
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ *yy_cp = (yy_hold_char); \
+ YY_RESTORE_YY_MORE_OFFSET \
+ (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+ YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+ } \
+ while ( 0 )
+
+#define unput(c) yyunput( c, (yytext_ptr) )
+
+/* The following is because we cannot portably get our hands on size_t
+ * (without autoconf's help, which isn't available because we want
+ * flex-generated scanners to compile on their own).
+ * Given that the standard has decreed that size_t exists since 1989,
+ * I guess we can afford to depend on it. Manoj.
+ */
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef size_t yy_size_t;
+#endif
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+ {
+ FILE *yy_input_file;
+
+ char *yy_ch_buf; /* input buffer */
+ char *yy_buf_pos; /* current position in input buffer */
+
+ /* Size of input buffer in bytes, not including room for EOB
+ * characters.
+ */
+ yy_size_t yy_buf_size;
+
+ /* Number of characters read into yy_ch_buf, not including EOB
+ * characters.
+ */
+ int yy_n_chars;
+
+ /* Whether we "own" the buffer - i.e., we know we created it,
+ * and can realloc() it to grow it, and should free() it to
+ * delete it.
+ */
+ int yy_is_our_buffer;
+
+ /* Whether this is an "interactive" input source; if so, and
+ * if we're using stdio for input, then we want to use getc()
+ * instead of fread(), to make sure we stop fetching input after
+ * each newline.
+ */
+ int yy_is_interactive;
+
+ /* Whether we're considered to be at the beginning of a line.
+ * If so, '^' rules will be active on the next match, otherwise
+ * not.
+ */
+ int yy_at_bol;
+
+ int yy_bs_lineno; /**< The line count. */
+ int yy_bs_column; /**< The column count. */
+
+ /* Whether to try to fill the input buffer when we reach the
+ * end of it.
+ */
+ int yy_fill_buffer;
+
+ int yy_buffer_status;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+ /* When an EOF's been seen but there's still some text to process
+ * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+ * shouldn't try reading from the input source any more. We might
+ * still have a bunch of tokens to match, though, because of
+ * possible backing-up.
+ *
+ * When we actually see the EOF, we change the status to "new"
+ * (via yyrestart()), so that the user can continue scanning by
+ * just pointing yyin at a new input file.
+ */
+#define YY_BUFFER_EOF_PENDING 2
+
+ };
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+/* Stack of input buffers. */
+static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */
+static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */
+static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \
+ ? (yy_buffer_stack)[(yy_buffer_stack_top)] \
+ : NULL)
+
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)]
+
+/* yy_hold_char holds the character lost when yytext is formed. */
+static char yy_hold_char;
+static int yy_n_chars; /* number of characters read into yy_ch_buf */
+int yyleng;
+
+/* Points to current character in buffer. */
+static char *yy_c_buf_p = (char *) 0;
+static int yy_init = 0; /* whether we need to initialize */
+static int yy_start = 0; /* start state number */
+
+/* Flag which is used to allow yywrap()'s to do buffer switches
+ * instead of setting up a fresh yyin. A bit of a hack ...
+ */
+static int yy_did_buffer_switch_on_eof;
+
+void yyrestart (FILE *input_file );
+void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer );
+YY_BUFFER_STATE yy_create_buffer (FILE *file,int size );
+void yy_delete_buffer (YY_BUFFER_STATE b );
+void yy_flush_buffer (YY_BUFFER_STATE b );
+void yypush_buffer_state (YY_BUFFER_STATE new_buffer );
+void yypop_buffer_state (void );
+
+static void yyensure_buffer_stack (void );
+static void yy_load_buffer_state (void );
+static void yy_init_buffer (YY_BUFFER_STATE b,FILE *file );
+
+#define YY_FLUSH_BUFFER yy_flush_buffer(YY_CURRENT_BUFFER )
+
+YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size );
+YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str );
+YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,int len );
+
+void *yyalloc (yy_size_t );
+void *yyrealloc (void *,yy_size_t );
+void yyfree (void * );
+
+#define yy_new_buffer yy_create_buffer
+
+#define yy_set_interactive(is_interactive) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){ \
+ yyensure_buffer_stack (); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ yy_create_buffer(yyin,YY_BUF_SIZE ); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+ }
+
+#define yy_set_bol(at_bol) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){\
+ yyensure_buffer_stack (); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ yy_create_buffer(yyin,YY_BUF_SIZE ); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+ }
+
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+/* Begin user sect3 */
+
+typedef unsigned char YY_CHAR;
+
+FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0;
+
+typedef int yy_state_type;
+
+extern int yylineno;
+
+int yylineno = 1;
+
+extern char *yytext;
+#define yytext_ptr yytext
+
+static yy_state_type yy_get_previous_state (void );
+static yy_state_type yy_try_NUL_trans (yy_state_type current_state );
+static int yy_get_next_buffer (void );
+static void yy_fatal_error (yyconst char msg[] );
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+ (yytext_ptr) = yy_bp; \
+ yyleng = (size_t) (yy_cp - yy_bp); \
+ (yy_hold_char) = *yy_cp; \
+ *yy_cp = '\0'; \
+ (yy_c_buf_p) = yy_cp;
+
+#define YY_NUM_RULES 16
+#define YY_END_OF_BUFFER 17
+/* This struct is not used in this scanner,
+ but its presence is necessary. */
+struct yy_trans_info
+ {
+ flex_int32_t yy_verify;
+ flex_int32_t yy_nxt;
+ };
+static yyconst flex_int16_t yy_accept[46] =
+ { 0,
+ 0, 0, 17, 15, 11, 12, 13, 10, 9, 14,
+ 14, 14, 14, 10, 9, 14, 3, 14, 14, 1,
+ 7, 14, 14, 8, 14, 14, 14, 14, 14, 14,
+ 14, 6, 14, 14, 5, 14, 14, 14, 14, 14,
+ 14, 4, 14, 2, 0
+ } ;
+
+static yyconst flex_int32_t yy_ec[256] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 2, 3,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 2, 1, 4, 5, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 1, 1, 1,
+ 1, 1, 1, 1, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 1, 1, 1, 1, 8, 1, 9, 10, 11, 12,
+
+ 13, 14, 7, 7, 15, 7, 7, 16, 7, 17,
+ 18, 19, 7, 20, 7, 21, 7, 7, 7, 22,
+ 7, 7, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1
+ } ;
+
+static yyconst flex_int32_t yy_meta[23] =
+ { 0,
+ 1, 1, 2, 1, 1, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3
+ } ;
+
+static yyconst flex_int16_t yy_base[48] =
+ { 0,
+ 0, 0, 56, 57, 57, 57, 57, 0, 49, 0,
+ 12, 13, 34, 0, 47, 0, 0, 40, 31, 0,
+ 0, 38, 36, 0, 30, 34, 32, 25, 22, 28,
+ 34, 0, 19, 13, 0, 22, 30, 26, 26, 18,
+ 12, 0, 14, 0, 57, 34, 23
+ } ;
+
+static yyconst flex_int16_t yy_def[48] =
+ { 0,
+ 45, 1, 45, 45, 45, 45, 45, 46, 47, 47,
+ 47, 47, 47, 46, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 0, 45, 45
+ } ;
+
+static yyconst flex_int16_t yy_nxt[80] =
+ { 0,
+ 4, 5, 6, 7, 8, 9, 10, 10, 10, 10,
+ 10, 10, 11, 10, 12, 10, 10, 10, 13, 10,
+ 10, 10, 17, 36, 21, 16, 44, 43, 18, 22,
+ 42, 19, 20, 37, 14, 41, 14, 40, 39, 38,
+ 35, 34, 33, 32, 31, 30, 29, 28, 27, 26,
+ 25, 24, 15, 23, 15, 45, 3, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45
+ } ;
+
+static yyconst flex_int16_t yy_chk[80] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 11, 34, 12, 47, 43, 41, 11, 12,
+ 40, 11, 11, 34, 46, 39, 46, 38, 37, 36,
+ 33, 31, 30, 29, 28, 27, 26, 25, 23, 22,
+ 19, 18, 15, 13, 9, 3, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45
+ } ;
+
+static yy_state_type yy_last_accepting_state;
+static char *yy_last_accepting_cpos;
+
+extern int yy_flex_debug;
+int yy_flex_debug = 0;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+char *yytext;
+#line 1 "lex.l"
+#line 2 "lex.l"
+/*
+ * Copyright (c) 1998 - 2000 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * This is to handle the definition of this symbol in some AIX
+ * headers, which will conflict with the definition that lex will
+ * generate for it. It's only a problem for AIX lex.
+ */
+
+#undef ECHO
+
+#include "compile_et.h"
+#include "parse.h"
+#include "lex.h"
+
+RCSID("$Id$");
+
+static unsigned lineno = 1;
+static int getstring(void);
+
+#define YY_NO_UNPUT
+
+#undef ECHO
+
+#line 539 "heimdal/lib/com_err/lex.c"
+
+#define INITIAL 0
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+#include <unistd.h>
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+static int yy_init_globals (void );
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int yywrap (void );
+#else
+extern int yywrap (void );
+#endif
+#endif
+
+ static void yyunput (int c,char *buf_ptr );
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char *,yyconst char *,int );
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * );
+#endif
+
+#ifndef YY_NO_INPUT
+
+#ifdef __cplusplus
+static int yyinput (void );
+#else
+static int input (void );
+#endif
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO fwrite( yytext, yyleng, 1, yyout )
+#endif
+
+/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
+ { \
+ int c = '*'; \
+ int n; \
+ for ( n = 0; n < max_size && \
+ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
+ buf[n] = (char) c; \
+ if ( c == '\n' ) \
+ buf[n++] = (char) c; \
+ if ( c == EOF && ferror( yyin ) ) \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ result = n; \
+ } \
+ else \
+ { \
+ errno=0; \
+ while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \
+ { \
+ if( errno != EINTR) \
+ { \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ break; \
+ } \
+ errno=0; \
+ clearerr(yyin); \
+ } \
+ }\
+\
+
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg )
+#endif
+
+/* end tables serialization structures and prototypes */
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int yylex (void);
+
+#define YY_DECL int yylex (void)
+#endif /* !YY_DECL */
+
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+ YY_USER_ACTION
+
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp, *yy_bp;
+ register int yy_act;
+
+#line 59 "lex.l"
+
+#line 694 "heimdal/lib/com_err/lex.c"
+
+ if ( !(yy_init) )
+ {
+ (yy_init) = 1;
+
+#ifdef YY_USER_INIT
+ YY_USER_INIT;
+#endif
+
+ if ( ! (yy_start) )
+ (yy_start) = 1; /* first start state */
+
+ if ( ! yyin )
+ yyin = stdin;
+
+ if ( ! yyout )
+ yyout = stdout;
+
+ if ( ! YY_CURRENT_BUFFER ) {
+ yyensure_buffer_stack ();
+ YY_CURRENT_BUFFER_LVALUE =
+ yy_create_buffer(yyin,YY_BUF_SIZE );
+ }
+
+ yy_load_buffer_state( );
+ }
+
+ while ( 1 ) /* loops until end-of-file is reached */
+ {
+ yy_cp = (yy_c_buf_p);
+
+ /* Support of yytext. */
+ *yy_cp = (yy_hold_char);
+
+ /* yy_bp points to the position in yy_ch_buf of the start of
+ * the current run.
+ */
+ yy_bp = yy_cp;
+
+ yy_current_state = (yy_start);
+yy_match:
+ do
+ {
+ register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
+ if ( yy_accept[yy_current_state] )
+ {
+ (yy_last_accepting_state) = yy_current_state;
+ (yy_last_accepting_cpos) = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 46 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ ++yy_cp;
+ }
+ while ( yy_base[yy_current_state] != 57 );
+
+yy_find_action:
+ yy_act = yy_accept[yy_current_state];
+ if ( yy_act == 0 )
+ { /* have to back up */
+ yy_cp = (yy_last_accepting_cpos);
+ yy_current_state = (yy_last_accepting_state);
+ yy_act = yy_accept[yy_current_state];
+ }
+
+ YY_DO_BEFORE_ACTION;
+
+do_action: /* This label is used only to access EOF actions. */
+
+ switch ( yy_act )
+ { /* beginning of action switch */
+ case 0: /* must back up */
+ /* undo the effects of YY_DO_BEFORE_ACTION */
+ *yy_cp = (yy_hold_char);
+ yy_cp = (yy_last_accepting_cpos);
+ yy_current_state = (yy_last_accepting_state);
+ goto yy_find_action;
+
+case 1:
+YY_RULE_SETUP
+#line 60 "lex.l"
+{ return ET; }
+ YY_BREAK
+case 2:
+YY_RULE_SETUP
+#line 61 "lex.l"
+{ return ET; }
+ YY_BREAK
+case 3:
+YY_RULE_SETUP
+#line 62 "lex.l"
+{ return EC; }
+ YY_BREAK
+case 4:
+YY_RULE_SETUP
+#line 63 "lex.l"
+{ return EC; }
+ YY_BREAK
+case 5:
+YY_RULE_SETUP
+#line 64 "lex.l"
+{ return PREFIX; }
+ YY_BREAK
+case 6:
+YY_RULE_SETUP
+#line 65 "lex.l"
+{ return INDEX; }
+ YY_BREAK
+case 7:
+YY_RULE_SETUP
+#line 66 "lex.l"
+{ return ID; }
+ YY_BREAK
+case 8:
+YY_RULE_SETUP
+#line 67 "lex.l"
+{ return END; }
+ YY_BREAK
+case 9:
+YY_RULE_SETUP
+#line 68 "lex.l"
+{ yylval.number = atoi(yytext); return NUMBER; }
+ YY_BREAK
+case 10:
+YY_RULE_SETUP
+#line 69 "lex.l"
+;
+ YY_BREAK
+case 11:
+YY_RULE_SETUP
+#line 70 "lex.l"
+;
+ YY_BREAK
+case 12:
+/* rule 12 can match eol */
+YY_RULE_SETUP
+#line 71 "lex.l"
+{ lineno++; }
+ YY_BREAK
+case 13:
+YY_RULE_SETUP
+#line 72 "lex.l"
+{ return getstring(); }
+ YY_BREAK
+case 14:
+YY_RULE_SETUP
+#line 73 "lex.l"
+{ yylval.string = strdup(yytext); return STRING; }
+ YY_BREAK
+case 15:
+YY_RULE_SETUP
+#line 74 "lex.l"
+{ return *yytext; }
+ YY_BREAK
+case 16:
+YY_RULE_SETUP
+#line 75 "lex.l"
+ECHO;
+ YY_BREAK
+#line 858 "heimdal/lib/com_err/lex.c"
+case YY_STATE_EOF(INITIAL):
+ yyterminate();
+
+ case YY_END_OF_BUFFER:
+ {
+ /* Amount of text matched not including the EOB char. */
+ int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1;
+
+ /* Undo the effects of YY_DO_BEFORE_ACTION. */
+ *yy_cp = (yy_hold_char);
+ YY_RESTORE_YY_MORE_OFFSET
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+ {
+ /* We're scanning a new file or input source. It's
+ * possible that this happened because the user
+ * just pointed yyin at a new source and called
+ * yylex(). If so, then we have to assure
+ * consistency between YY_CURRENT_BUFFER and our
+ * globals. Here is the right place to do so, because
+ * this is the first action (other than possibly a
+ * back-up) that will match for the new input source.
+ */
+ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+ }
+
+ /* Note that here we test for yy_c_buf_p "<=" to the position
+ * of the first EOB in the buffer, since yy_c_buf_p will
+ * already have been incremented past the NUL character
+ * (since all states make transitions on EOB to the
+ * end-of-buffer state). Contrast this with the test
+ * in input().
+ */
+ if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+ { /* This was really a NUL. */
+ yy_state_type yy_next_state;
+
+ (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( );
+
+ /* Okay, we're now positioned to make the NUL
+ * transition. We couldn't have
+ * yy_get_previous_state() go ahead and do it
+ * for us because it doesn't know how to deal
+ * with the possibility of jamming (and we don't
+ * want to build jamming into it because then it
+ * will run more slowly).
+ */
+
+ yy_next_state = yy_try_NUL_trans( yy_current_state );
+
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+
+ if ( yy_next_state )
+ {
+ /* Consume the NUL. */
+ yy_cp = ++(yy_c_buf_p);
+ yy_current_state = yy_next_state;
+ goto yy_match;
+ }
+
+ else
+ {
+ yy_cp = (yy_c_buf_p);
+ goto yy_find_action;
+ }
+ }
+
+ else switch ( yy_get_next_buffer( ) )
+ {
+ case EOB_ACT_END_OF_FILE:
+ {
+ (yy_did_buffer_switch_on_eof) = 0;
+
+ if ( yywrap( ) )
+ {
+ /* Note: because we've taken care in
+ * yy_get_next_buffer() to have set up
+ * yytext, we can now set up
+ * yy_c_buf_p so that if some total
+ * hoser (like flex itself) wants to
+ * call the scanner after we return the
+ * YY_NULL, it'll still work - another
+ * YY_NULL will get returned.
+ */
+ (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ;
+
+ yy_act = YY_STATE_EOF(YY_START);
+ goto do_action;
+ }
+
+ else
+ {
+ if ( ! (yy_did_buffer_switch_on_eof) )
+ YY_NEW_FILE;
+ }
+ break;
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ (yy_c_buf_p) =
+ (yytext_ptr) + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( );
+
+ yy_cp = (yy_c_buf_p);
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+ goto yy_match;
+
+ case EOB_ACT_LAST_MATCH:
+ (yy_c_buf_p) =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)];
+
+ yy_current_state = yy_get_previous_state( );
+
+ yy_cp = (yy_c_buf_p);
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+ goto yy_find_action;
+ }
+ break;
+ }
+
+ default:
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--no action found" );
+ } /* end of action switch */
+ } /* end of scanning one token */
+} /* end of yylex */
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ * EOB_ACT_LAST_MATCH -
+ * EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ * EOB_ACT_END_OF_FILE - end of file
+ */
+static int yy_get_next_buffer (void)
+{
+ register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+ register char *source = (yytext_ptr);
+ register int number_to_move, i;
+ int ret_val;
+
+ if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] )
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--end of buffer missed" );
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+ { /* Don't try to fill the buffer, so this is an EOF. */
+ if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 )
+ {
+ /* We matched a single character, the EOB, so
+ * treat this as a final EOF.
+ */
+ return EOB_ACT_END_OF_FILE;
+ }
+
+ else
+ {
+ /* We matched some text prior to the EOB, first
+ * process it.
+ */
+ return EOB_ACT_LAST_MATCH;
+ }
+ }
+
+ /* Try to read more data. */
+
+ /* First move last chars to start of buffer. */
+ number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1;
+
+ for ( i = 0; i < number_to_move; ++i )
+ *(dest++) = *(source++);
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+ /* don't do the read, it's not guaranteed to return an EOF,
+ * just force an EOF
+ */
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0;
+
+ else
+ {
+ int num_to_read =
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+ while ( num_to_read <= 0 )
+ { /* Not enough room in the buffer - grow it. */
+
+ /* just a shorter name for the current buffer */
+ YY_BUFFER_STATE b = YY_CURRENT_BUFFER;
+
+ int yy_c_buf_p_offset =
+ (int) ((yy_c_buf_p) - b->yy_ch_buf);
+
+ if ( b->yy_is_our_buffer )
+ {
+ int new_size = b->yy_buf_size * 2;
+
+ if ( new_size <= 0 )
+ b->yy_buf_size += b->yy_buf_size / 8;
+ else
+ b->yy_buf_size *= 2;
+
+ b->yy_ch_buf = (char *)
+ /* Include room in for 2 EOB chars. */
+ yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 );
+ }
+ else
+ /* Can't grow it, we don't own it. */
+ b->yy_ch_buf = 0;
+
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR(
+ "fatal error - scanner input buffer overflow" );
+
+ (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+ num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
+ number_to_move - 1;
+
+ }
+
+ if ( num_to_read > YY_READ_BUF_SIZE )
+ num_to_read = YY_READ_BUF_SIZE;
+
+ /* Read in more data. */
+ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+ (yy_n_chars), (size_t) num_to_read );
+
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ if ( (yy_n_chars) == 0 )
+ {
+ if ( number_to_move == YY_MORE_ADJ )
+ {
+ ret_val = EOB_ACT_END_OF_FILE;
+ yyrestart(yyin );
+ }
+
+ else
+ {
+ ret_val = EOB_ACT_LAST_MATCH;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+ YY_BUFFER_EOF_PENDING;
+ }
+ }
+
+ else
+ ret_val = EOB_ACT_CONTINUE_SCAN;
+
+ if ((yy_size_t) ((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
+ /* Extend the array by 50%, plus the number we really need. */
+ yy_size_t new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1);
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size );
+ if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
+ }
+
+ (yy_n_chars) += number_to_move;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR;
+
+ (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+ return ret_val;
+}
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+ static yy_state_type yy_get_previous_state (void)
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp;
+
+ yy_current_state = (yy_start);
+
+ for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp )
+ {
+ register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+ if ( yy_accept[yy_current_state] )
+ {
+ (yy_last_accepting_state) = yy_current_state;
+ (yy_last_accepting_cpos) = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 46 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ }
+
+ return yy_current_state;
+}
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ * next_state = yy_try_NUL_trans( current_state );
+ */
+ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state )
+{
+ register int yy_is_jam;
+ register char *yy_cp = (yy_c_buf_p);
+
+ register YY_CHAR yy_c = 1;
+ if ( yy_accept[yy_current_state] )
+ {
+ (yy_last_accepting_state) = yy_current_state;
+ (yy_last_accepting_cpos) = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 46 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ yy_is_jam = (yy_current_state == 45);
+
+ return yy_is_jam ? 0 : yy_current_state;
+}
+
+ static void yyunput (int c, register char * yy_bp )
+{
+ register char *yy_cp;
+
+ yy_cp = (yy_c_buf_p);
+
+ /* undo effects of setting up yytext */
+ *yy_cp = (yy_hold_char);
+
+ if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+ { /* need to shift things up to make room */
+ /* +2 for EOB chars. */
+ register int number_to_move = (yy_n_chars) + 2;
+ register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2];
+ register char *source =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move];
+
+ while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+ *--dest = *--source;
+
+ yy_cp += (int) (dest - source);
+ yy_bp += (int) (dest - source);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars =
+ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_buf_size;
+
+ if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+ YY_FATAL_ERROR( "flex scanner push-back overflow" );
+ }
+
+ *--yy_cp = (char) c;
+
+ (yytext_ptr) = yy_bp;
+ (yy_hold_char) = *yy_cp;
+ (yy_c_buf_p) = yy_cp;
+}
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+ static int yyinput (void)
+#else
+ static int input (void)
+#endif
+
+{
+ int c;
+
+ *(yy_c_buf_p) = (yy_hold_char);
+
+ if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR )
+ {
+ /* yy_c_buf_p now points to the character we want to return.
+ * If this occurs *before* the EOB characters, then it's a
+ * valid NUL; if not, then we've hit the end of the buffer.
+ */
+ if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+ /* This was really a NUL. */
+ *(yy_c_buf_p) = '\0';
+
+ else
+ { /* need more input */
+ int offset = (yy_c_buf_p) - (yytext_ptr);
+ ++(yy_c_buf_p);
+
+ switch ( yy_get_next_buffer( ) )
+ {
+ case EOB_ACT_LAST_MATCH:
+ /* This happens because yy_g_n_b()
+ * sees that we've accumulated a
+ * token and flags that we need to
+ * try matching the token before
+ * proceeding. But for input(),
+ * there's no matching to consider.
+ * So convert the EOB_ACT_LAST_MATCH
+ * to EOB_ACT_END_OF_FILE.
+ */
+
+ /* Reset buffer status. */
+ yyrestart(yyin );
+
+ /*FALLTHROUGH*/
+
+ case EOB_ACT_END_OF_FILE:
+ {
+ if ( yywrap( ) )
+ return EOF;
+
+ if ( ! (yy_did_buffer_switch_on_eof) )
+ YY_NEW_FILE;
+#ifdef __cplusplus
+ return yyinput();
+#else
+ return input();
+#endif
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ (yy_c_buf_p) = (yytext_ptr) + offset;
+ break;
+ }
+ }
+ }
+
+ c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */
+ *(yy_c_buf_p) = '\0'; /* preserve yytext */
+ (yy_hold_char) = *++(yy_c_buf_p);
+
+ return c;
+}
+#endif /* ifndef YY_NO_INPUT */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ *
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+ void yyrestart (FILE * input_file )
+{
+
+ if ( ! YY_CURRENT_BUFFER ){
+ yyensure_buffer_stack ();
+ YY_CURRENT_BUFFER_LVALUE =
+ yy_create_buffer(yyin,YY_BUF_SIZE );
+ }
+
+ yy_init_buffer(YY_CURRENT_BUFFER,input_file );
+ yy_load_buffer_state( );
+}
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ *
+ */
+ void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer )
+{
+
+ /* TODO. We should be able to replace this entire function body
+ * with
+ * yypop_buffer_state();
+ * yypush_buffer_state(new_buffer);
+ */
+ yyensure_buffer_stack ();
+ if ( YY_CURRENT_BUFFER == new_buffer )
+ return;
+
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *(yy_c_buf_p) = (yy_hold_char);
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+ yy_load_buffer_state( );
+
+ /* We don't actually know whether we did this switch during
+ * EOF (yywrap()) processing, but the only time this flag
+ * is looked at is after yywrap() is called, so it's safe
+ * to go ahead and always set it.
+ */
+ (yy_did_buffer_switch_on_eof) = 1;
+}
+
+static void yy_load_buffer_state (void)
+{
+ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+ yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+ (yy_hold_char) = *(yy_c_buf_p);
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ *
+ * @return the allocated buffer state.
+ */
+ YY_BUFFER_STATE yy_create_buffer (FILE * file, int size )
+{
+ YY_BUFFER_STATE b;
+
+ b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+ b->yy_buf_size = size;
+
+ /* yy_ch_buf has to be 2 characters longer than the size given because
+ * we need to put in 2 end-of-buffer characters.
+ */
+ b->yy_ch_buf = (char *) yyalloc(b->yy_buf_size + 2 );
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+ b->yy_is_our_buffer = 1;
+
+ yy_init_buffer(b,file );
+
+ return b;
+}
+
+/** Destroy the buffer.
+ * @param b a buffer created with yy_create_buffer()
+ *
+ */
+ void yy_delete_buffer (YY_BUFFER_STATE b )
+{
+
+ if ( ! b )
+ return;
+
+ if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+ if ( b->yy_is_our_buffer )
+ yyfree((void *) b->yy_ch_buf );
+
+ yyfree((void *) b );
+}
+
+#ifndef __cplusplus
+extern int isatty (int );
+#endif /* __cplusplus */
+
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a yyrestart() or at EOF.
+ */
+ static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file )
+
+{
+ int oerrno = errno;
+
+ yy_flush_buffer(b );
+
+ b->yy_input_file = file;
+ b->yy_fill_buffer = 1;
+
+ /* If b is the current buffer, then yy_init_buffer was _probably_
+ * called from yyrestart() or through yy_get_next_buffer.
+ * In that case, we don't want to reset the lineno or column.
+ */
+ if (b != YY_CURRENT_BUFFER){
+ b->yy_bs_lineno = 1;
+ b->yy_bs_column = 0;
+ }
+
+ b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+
+ errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ *
+ */
+ void yy_flush_buffer (YY_BUFFER_STATE b )
+{
+ if ( ! b )
+ return;
+
+ b->yy_n_chars = 0;
+
+ /* We always need two end-of-buffer characters. The first causes
+ * a transition to the end-of-buffer state. The second causes
+ * a jam in that state.
+ */
+ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+ b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+ b->yy_buf_pos = &b->yy_ch_buf[0];
+
+ b->yy_at_bol = 1;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ if ( b == YY_CURRENT_BUFFER )
+ yy_load_buffer_state( );
+}
+
+/** Pushes the new state onto the stack. The new state becomes
+ * the current state. This function will allocate the stack
+ * if necessary.
+ * @param new_buffer The new state.
+ *
+ */
+void yypush_buffer_state (YY_BUFFER_STATE new_buffer )
+{
+ if (new_buffer == NULL)
+ return;
+
+ yyensure_buffer_stack();
+
+ /* This block is copied from yy_switch_to_buffer. */
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *(yy_c_buf_p) = (yy_hold_char);
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ /* Only push if top exists. Otherwise, replace top. */
+ if (YY_CURRENT_BUFFER)
+ (yy_buffer_stack_top)++;
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+ /* copied from yy_switch_to_buffer. */
+ yy_load_buffer_state( );
+ (yy_did_buffer_switch_on_eof) = 1;
+}
+
+/** Removes and deletes the top of the stack, if present.
+ * The next element becomes the new top.
+ *
+ */
+void yypop_buffer_state (void)
+{
+ if (!YY_CURRENT_BUFFER)
+ return;
+
+ yy_delete_buffer(YY_CURRENT_BUFFER );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ if ((yy_buffer_stack_top) > 0)
+ --(yy_buffer_stack_top);
+
+ if (YY_CURRENT_BUFFER) {
+ yy_load_buffer_state( );
+ (yy_did_buffer_switch_on_eof) = 1;
+ }
+}
+
+/* Allocates the stack if it does not exist.
+ * Guarantees space for at least one push.
+ */
+static void yyensure_buffer_stack (void)
+{
+ int num_to_alloc;
+
+ if (!(yy_buffer_stack)) {
+
+ /* First allocation is just for 2 elements, since we don't know if this
+ * scanner will even need a stack. We use 2 instead of 1 to avoid an
+ * immediate realloc on the next call.
+ */
+ num_to_alloc = 1;
+ (yy_buffer_stack) = (struct yy_buffer_state**)yyalloc
+ (num_to_alloc * sizeof(struct yy_buffer_state*)
+ );
+ if ( ! (yy_buffer_stack) )
+ YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" );
+
+ memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+
+ (yy_buffer_stack_max) = num_to_alloc;
+ (yy_buffer_stack_top) = 0;
+ return;
+ }
+
+ if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){
+
+ /* Increase the buffer to prepare for a possible push. */
+ int grow_size = 8 /* arbitrary grow size */;
+
+ num_to_alloc = (yy_buffer_stack_max) + grow_size;
+ (yy_buffer_stack) = (struct yy_buffer_state**)yyrealloc
+ ((yy_buffer_stack),
+ num_to_alloc * sizeof(struct yy_buffer_state*)
+ );
+ if ( ! (yy_buffer_stack) )
+ YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" );
+
+ /* zero only the new slots.*/
+ memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*));
+ (yy_buffer_stack_max) = num_to_alloc;
+ }
+}
+
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ *
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size )
+{
+ YY_BUFFER_STATE b;
+
+ if ( size < 2 ||
+ base[size-2] != YY_END_OF_BUFFER_CHAR ||
+ base[size-1] != YY_END_OF_BUFFER_CHAR )
+ /* They forgot to leave room for the EOB's. */
+ return 0;
+
+ b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" );
+
+ b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */
+ b->yy_buf_pos = b->yy_ch_buf = base;
+ b->yy_is_our_buffer = 0;
+ b->yy_input_file = 0;
+ b->yy_n_chars = b->yy_buf_size;
+ b->yy_is_interactive = 0;
+ b->yy_at_bol = 1;
+ b->yy_fill_buffer = 0;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ yy_switch_to_buffer(b );
+
+ return b;
+}
+
+/** Setup the input buffer state to scan a string. The next call to yylex() will
+ * scan from a @e copy of @a str.
+ * @param yystr a NUL-terminated string to scan
+ *
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ * yy_scan_bytes() instead.
+ */
+YY_BUFFER_STATE yy_scan_string (yyconst char * yystr )
+{
+
+ return yy_scan_bytes(yystr,strlen(yystr) );
+}
+
+/** Setup the input buffer state to scan the given bytes. The next call to yylex() will
+ * scan from a @e copy of @a bytes.
+ * @param bytes the byte buffer to scan
+ * @param len the number of bytes in the buffer pointed to by @a bytes.
+ *
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE yy_scan_bytes (yyconst char * yybytes, int _yybytes_len )
+{
+ YY_BUFFER_STATE b;
+ char *buf;
+ yy_size_t n;
+ int i;
+
+ /* Get memory for full buffer, including space for trailing EOB's. */
+ n = _yybytes_len + 2;
+ buf = (char *) yyalloc(n );
+ if ( ! buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" );
+
+ for ( i = 0; i < _yybytes_len; ++i )
+ buf[i] = yybytes[i];
+
+ buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
+
+ b = yy_scan_buffer(buf,n );
+ if ( ! b )
+ YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" );
+
+ /* It's okay to grow etc. this buffer, and we should throw it
+ * away when we're done.
+ */
+ b->yy_is_our_buffer = 1;
+
+ return b;
+}
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+static void yy_fatal_error (yyconst char* msg )
+{
+ (void) fprintf( stderr, "%s\n", msg );
+ exit( YY_EXIT_FAILURE );
+}
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ yytext[yyleng] = (yy_hold_char); \
+ (yy_c_buf_p) = yytext + yyless_macro_arg; \
+ (yy_hold_char) = *(yy_c_buf_p); \
+ *(yy_c_buf_p) = '\0'; \
+ yyleng = yyless_macro_arg; \
+ } \
+ while ( 0 )
+
+/* Accessor methods (get/set functions) to struct members. */
+
+/** Get the current line number.
+ *
+ */
+int yyget_lineno (void)
+{
+
+ return yylineno;
+}
+
+/** Get the input stream.
+ *
+ */
+FILE *yyget_in (void)
+{
+ return yyin;
+}
+
+/** Get the output stream.
+ *
+ */
+FILE *yyget_out (void)
+{
+ return yyout;
+}
+
+/** Get the length of the current token.
+ *
+ */
+int yyget_leng (void)
+{
+ return yyleng;
+}
+
+/** Get the current token.
+ *
+ */
+
+char *yyget_text (void)
+{
+ return yytext;
+}
+
+/** Set the current line number.
+ * @param line_number
+ *
+ */
+void yyset_lineno (int line_number )
+{
+
+ yylineno = line_number;
+}
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param in_str A readable stream.
+ *
+ * @see yy_switch_to_buffer
+ */
+void yyset_in (FILE * in_str )
+{
+ yyin = in_str ;
+}
+
+void yyset_out (FILE * out_str )
+{
+ yyout = out_str ;
+}
+
+int yyget_debug (void)
+{
+ return yy_flex_debug;
+}
+
+void yyset_debug (int bdebug )
+{
+ yy_flex_debug = bdebug ;
+}
+
+static int yy_init_globals (void)
+{
+ /* Initialization is the same as for the non-reentrant scanner.
+ * This function is called from yylex_destroy(), so don't allocate here.
+ */
+
+ (yy_buffer_stack) = 0;
+ (yy_buffer_stack_top) = 0;
+ (yy_buffer_stack_max) = 0;
+ (yy_c_buf_p) = (char *) 0;
+ (yy_init) = 0;
+ (yy_start) = 0;
+
+/* Defined in main.c */
+#ifdef YY_STDINIT
+ yyin = stdin;
+ yyout = stdout;
+#else
+ yyin = (FILE *) 0;
+ yyout = (FILE *) 0;
+#endif
+
+ /* For future reference: Set errno on error, since we are called by
+ * yylex_init()
+ */
+ return 0;
+}
+
+/* yylex_destroy is for both reentrant and non-reentrant scanners. */
+int yylex_destroy (void)
+{
+
+ /* Pop the buffer stack, destroying each element. */
+ while(YY_CURRENT_BUFFER){
+ yy_delete_buffer(YY_CURRENT_BUFFER );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ yypop_buffer_state();
+ }
+
+ /* Destroy the stack itself. */
+ yyfree((yy_buffer_stack) );
+ (yy_buffer_stack) = NULL;
+
+ /* Reset the globals. This is important in a non-reentrant scanner so the next time
+ * yylex() is called, initialization will occur. */
+ yy_init_globals( );
+
+ return 0;
+}
+
+/*
+ * Internal utility routines.
+ */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, yyconst char * s2, int n )
+{
+ register int i;
+ for ( i = 0; i < n; ++i )
+ s1[i] = s2[i];
+}
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * s )
+{
+ register int n;
+ for ( n = 0; s[n]; ++n )
+ ;
+
+ return n;
+}
+#endif
+
+void *yyalloc (yy_size_t size )
+{
+ return (void *) malloc( size );
+}
+
+void *yyrealloc (void * ptr, yy_size_t size )
+{
+ /* The cast to (char *) in the following accommodates both
+ * implementations that use char* generic pointers, and those
+ * that use void* generic pointers. It works with the latter
+ * because both ANSI C and C++ allow castless assignment from
+ * any pointer type to void*, and deal with argument conversions
+ * as though doing an assignment.
+ */
+ return (void *) realloc( (char *) ptr, size );
+}
+
+void yyfree (void * ptr )
+{
+ free( (char *) ptr ); /* see yyrealloc() for (char *) cast */
+}
+
+#define YYTABLES_NAME "yytables"
+
+#line 75 "lex.l"
+
+
+
+#ifndef yywrap /* XXX */
+int
+yywrap ()
+{
+ return 1;
+}
+#endif
+
+static int
+getstring(void)
+{
+ char x[128];
+ int i = 0;
+ int c;
+ int quote = 0;
+ while(i < sizeof(x) - 1 && (c = input()) != EOF){
+ if(quote) {
+ x[i++] = c;
+ quote = 0;
+ continue;
+ }
+ if(c == '\n'){
+ error_message("unterminated string");
+ lineno++;
+ break;
+ }
+ if(c == '\\'){
+ quote++;
+ continue;
+ }
+ if(c == '\"')
+ break;
+ x[i++] = c;
+ }
+ x[i] = '\0';
+ yylval.string = strdup(x);
+ if (yylval.string == NULL)
+ err(1, "malloc");
+ return STRING;
+}
+
+void
+error_message (const char *format, ...)
+{
+ va_list args;
+
+ va_start (args, format);
+ fprintf (stderr, "%s:%d:", filename, lineno);
+ vfprintf (stderr, format, args);
+ va_end (args);
+ numerror++;
+}
+
diff --git a/source4/heimdal/lib/com_err/lex.h b/source4/heimdal/lib/com_err/lex.h
new file mode 100644
index 0000000000..76f3e2b2a5
--- /dev/null
+++ b/source4/heimdal/lib/com_err/lex.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 1997 - 2000 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+void error_message (const char *, ...)
+__attribute__ ((format (printf, 1, 2)));
+
+int yylex(void);
diff --git a/source4/heimdal/lib/com_err/lex.l b/source4/heimdal/lib/com_err/lex.l
new file mode 100644
index 0000000000..84356405dd
--- /dev/null
+++ b/source4/heimdal/lib/com_err/lex.l
@@ -0,0 +1,128 @@
+%{
+/*
+ * Copyright (c) 1998 - 2000 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * This is to handle the definition of this symbol in some AIX
+ * headers, which will conflict with the definition that lex will
+ * generate for it. It's only a problem for AIX lex.
+ */
+
+#undef ECHO
+
+#include "compile_et.h"
+#include "parse.h"
+#include "lex.h"
+
+RCSID("$Id$");
+
+static unsigned lineno = 1;
+static int getstring(void);
+
+#define YY_NO_UNPUT
+
+#undef ECHO
+
+%}
+
+
+%%
+et { return ET; }
+error_table { return ET; }
+ec { return EC; }
+error_code { return EC; }
+prefix { return PREFIX; }
+index { return INDEX; }
+id { return ID; }
+end { return END; }
+[0-9]+ { yylval.number = atoi(yytext); return NUMBER; }
+#[^\n]* ;
+[ \t] ;
+\n { lineno++; }
+\" { return getstring(); }
+[a-zA-Z0-9_]+ { yylval.string = strdup(yytext); return STRING; }
+. { return *yytext; }
+%%
+
+#ifndef yywrap /* XXX */
+int
+yywrap ()
+{
+ return 1;
+}
+#endif
+
+static int
+getstring(void)
+{
+ char x[128];
+ int i = 0;
+ int c;
+ int quote = 0;
+ while(i < sizeof(x) - 1 && (c = input()) != EOF){
+ if(quote) {
+ x[i++] = c;
+ quote = 0;
+ continue;
+ }
+ if(c == '\n'){
+ error_message("unterminated string");
+ lineno++;
+ break;
+ }
+ if(c == '\\'){
+ quote++;
+ continue;
+ }
+ if(c == '\"')
+ break;
+ x[i++] = c;
+ }
+ x[i] = '\0';
+ yylval.string = strdup(x);
+ if (yylval.string == NULL)
+ err(1, "malloc");
+ return STRING;
+}
+
+void
+error_message (const char *format, ...)
+{
+ va_list args;
+
+ va_start (args, format);
+ fprintf (stderr, "%s:%d:", filename, lineno);
+ vfprintf (stderr, format, args);
+ va_end (args);
+ numerror++;
+}
diff --git a/source4/heimdal/lib/com_err/parse.c b/source4/heimdal/lib/com_err/parse.c
new file mode 100644
index 0000000000..70b6a1528f
--- /dev/null
+++ b/source4/heimdal/lib/com_err/parse.c
@@ -0,0 +1,1716 @@
+/* A Bison parser, made by GNU Bison 2.3. */
+
+/* Skeleton implementation for Bison's Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+ Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU 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., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+ simplifying the original so-called "semantic" parser. */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+ infringing on user name space. This should be done even for local
+ variables, as they might otherwise be expanded by user macros.
+ There are some unavoidable exceptions within include files to
+ define necessary library symbols; they are noted "INFRINGES ON
+ USER NAME SPACE" below. */
+
+/* Identify Bison output. */
+#define YYBISON 1
+
+/* Bison version. */
+#define YYBISON_VERSION "2.3"
+
+/* Skeleton name. */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers. */
+#define YYPURE 0
+
+/* Using locations. */
+#define YYLSP_NEEDED 0
+
+
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ ET = 258,
+ INDEX = 259,
+ PREFIX = 260,
+ EC = 261,
+ ID = 262,
+ END = 263,
+ STRING = 264,
+ NUMBER = 265
+ };
+#endif
+/* Tokens. */
+#define ET 258
+#define INDEX 259
+#define PREFIX 260
+#define EC 261
+#define ID 262
+#define END 263
+#define STRING 264
+#define NUMBER 265
+
+
+
+
+/* Copy the first part of user declarations. */
+#line 1 "heimdal/lib/com_err/parse.y"
+
+/*
+ * Copyright (c) 1998 - 2000 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "compile_et.h"
+#include "lex.h"
+
+RCSID("$Id$");
+
+void yyerror (char *s);
+static long name2number(const char *str);
+
+extern char *yytext;
+
+/* This is for bison */
+
+#if !defined(alloca) && !defined(HAVE_ALLOCA)
+#define alloca(x) malloc(x)
+#endif
+
+
+
+/* Enabling traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+
+/* Enabling verbose error messages. */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 0
+#endif
+
+/* Enabling the token table. */
+#ifndef YYTOKEN_TABLE
+# define YYTOKEN_TABLE 0
+#endif
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+#line 53 "heimdal/lib/com_err/parse.y"
+{
+ char *string;
+ int number;
+}
+/* Line 187 of yacc.c. */
+#line 173 "heimdal/lib/com_err/parse.y"
+ YYSTYPE;
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+
+
+/* Copy the second part of user declarations. */
+
+
+/* Line 216 of yacc.c. */
+#line 186 "heimdal/lib/com_err/parse.y"
+
+#ifdef short
+# undef short
+#endif
+
+#ifdef YYTYPE_UINT8
+typedef YYTYPE_UINT8 yytype_uint8;
+#else
+typedef unsigned char yytype_uint8;
+#endif
+
+#ifdef YYTYPE_INT8
+typedef YYTYPE_INT8 yytype_int8;
+#elif (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+typedef signed char yytype_int8;
+#else
+typedef short int yytype_int8;
+#endif
+
+#ifdef YYTYPE_UINT16
+typedef YYTYPE_UINT16 yytype_uint16;
+#else
+typedef unsigned short int yytype_uint16;
+#endif
+
+#ifdef YYTYPE_INT16
+typedef YYTYPE_INT16 yytype_int16;
+#else
+typedef short int yytype_int16;
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+# define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+# define YYSIZE_T size_t
+# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+# else
+# define YYSIZE_T unsigned int
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+
+#ifndef YY_
+# if YYENABLE_NLS
+# if ENABLE_NLS
+# include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+# define YY_(msgid) dgettext ("bison-runtime", msgid)
+# endif
+# endif
+# ifndef YY_
+# define YY_(msgid) msgid
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E. */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(e) ((void) (e))
+#else
+# define YYUSE(e) /* empty */
+#endif
+
+/* Identity function, used to suppress warnings about constant conditions. */
+#ifndef lint
+# define YYID(n) (n)
+#else
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static int
+YYID (int i)
+#else
+static int
+YYID (i)
+ int i;
+#endif
+{
+ return i;
+}
+#endif
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols. */
+
+# ifdef YYSTACK_USE_ALLOCA
+# if YYSTACK_USE_ALLOCA
+# ifdef __GNUC__
+# define YYSTACK_ALLOC __builtin_alloca
+# elif defined __BUILTIN_VA_ARG_INCR
+# include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+# elif defined _AIX
+# define YYSTACK_ALLOC __alloca
+# elif defined _MSC_VER
+# include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+# define alloca _alloca
+# else
+# define YYSTACK_ALLOC alloca
+# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef _STDLIB_H
+# define _STDLIB_H 1
+# endif
+# endif
+# endif
+# endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+ /* Pacify GCC's `empty if-body' warning. */
+# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0))
+# ifndef YYSTACK_ALLOC_MAXIMUM
+ /* The OS might guarantee only one guard page at the bottom of the stack,
+ and a page size can be as small as 4096 bytes. So we cannot safely
+ invoke alloca (N) if N exceeds 4096. Use a slightly smaller number
+ to allow for a few compiler-allocated temporary stack slots. */
+# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+# endif
+# else
+# define YYSTACK_ALLOC YYMALLOC
+# define YYSTACK_FREE YYFREE
+# ifndef YYSTACK_ALLOC_MAXIMUM
+# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+# endif
+# if (defined __cplusplus && ! defined _STDLIB_H \
+ && ! ((defined YYMALLOC || defined malloc) \
+ && (defined YYFREE || defined free)))
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef _STDLIB_H
+# define _STDLIB_H 1
+# endif
+# endif
+# ifndef YYMALLOC
+# define YYMALLOC malloc
+# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# ifndef YYFREE
+# define YYFREE free
+# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+ && (! defined __cplusplus \
+ || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member. */
+union yyalloc
+{
+ yytype_int16 yyss;
+ YYSTYPE yyvs;
+ };
+
+/* The size of the maximum gap between one aligned stack and the next. */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+ N elements. */
+# define YYSTACK_BYTES(N) \
+ ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \
+ + YYSTACK_GAP_MAXIMUM)
+
+/* Copy COUNT objects from FROM to TO. The source and destination do
+ not overlap. */
+# ifndef YYCOPY
+# if defined __GNUC__ && 1 < __GNUC__
+# define YYCOPY(To, From, Count) \
+ __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+# else
+# define YYCOPY(To, From, Count) \
+ do \
+ { \
+ YYSIZE_T yyi; \
+ for (yyi = 0; yyi < (Count); yyi++) \
+ (To)[yyi] = (From)[yyi]; \
+ } \
+ while (YYID (0))
+# endif
+# endif
+
+/* Relocate STACK from its old location to the new one. The
+ local variables YYSIZE and YYSTACKSIZE give the old and new number of
+ elements in the stack, and YYPTR gives the new location of the
+ stack. Advance YYPTR to a properly aligned location for the next
+ stack. */
+# define YYSTACK_RELOCATE(Stack) \
+ do \
+ { \
+ YYSIZE_T yynewbytes; \
+ YYCOPY (&yyptr->Stack, Stack, yysize); \
+ Stack = &yyptr->Stack; \
+ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+ yyptr += yynewbytes / sizeof (*yyptr); \
+ } \
+ while (YYID (0))
+
+#endif
+
+/* YYFINAL -- State number of the termination state. */
+#define YYFINAL 9
+/* YYLAST -- Last index in YYTABLE. */
+#define YYLAST 23
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS 12
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS 7
+/* YYNRULES -- Number of rules. */
+#define YYNRULES 15
+/* YYNRULES -- Number of states. */
+#define YYNSTATES 24
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
+#define YYUNDEFTOK 2
+#define YYMAXUTOK 265
+
+#define YYTRANSLATE(YYX) \
+ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */
+static const yytype_uint8 yytranslate[] =
+{
+ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 11, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+ YYRHS. */
+static const yytype_uint8 yyprhs[] =
+{
+ 0, 0, 3, 4, 7, 10, 12, 15, 18, 22,
+ 24, 27, 30, 33, 35, 40
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS. */
+static const yytype_int8 yyrhs[] =
+{
+ 13, 0, -1, -1, 14, 17, -1, 15, 16, -1,
+ 16, -1, 7, 9, -1, 3, 9, -1, 3, 9,
+ 9, -1, 18, -1, 17, 18, -1, 4, 10, -1,
+ 5, 9, -1, 5, -1, 6, 9, 11, 9, -1,
+ 8, -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
+static const yytype_uint8 yyrline[] =
+{
+ 0, 64, 64, 65, 68, 69, 72, 78, 84, 93,
+ 94, 97, 101, 109, 116, 136
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+ First, the terminals, then, starting at YYNTOKENS, nonterminals. */
+static const char *const yytname[] =
+{
+ "$end", "error", "$undefined", "ET", "INDEX", "PREFIX", "EC", "ID",
+ "END", "STRING", "NUMBER", "','", "$accept", "file", "header", "id",
+ "et", "statements", "statement", 0
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+ token YYLEX-NUM. */
+static const yytype_uint16 yytoknum[] =
+{
+ 0, 256, 257, 258, 259, 260, 261, 262, 263, 264,
+ 265, 44
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
+static const yytype_uint8 yyr1[] =
+{
+ 0, 12, 13, 13, 14, 14, 15, 16, 16, 17,
+ 17, 18, 18, 18, 18, 18
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
+static const yytype_uint8 yyr2[] =
+{
+ 0, 2, 0, 2, 2, 1, 2, 2, 3, 1,
+ 2, 2, 2, 1, 4, 1
+};
+
+/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
+ STATE-NUM when YYTABLE doesn't specify something else to do. Zero
+ means the default is an error. */
+static const yytype_uint8 yydefact[] =
+{
+ 2, 0, 0, 0, 0, 0, 5, 7, 6, 1,
+ 0, 13, 0, 15, 3, 9, 4, 8, 11, 12,
+ 0, 10, 0, 14
+};
+
+/* YYDEFGOTO[NTERM-NUM]. */
+static const yytype_int8 yydefgoto[] =
+{
+ -1, 3, 4, 5, 6, 14, 15
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+ STATE-NUM. */
+#define YYPACT_NINF -5
+static const yytype_int8 yypact[] =
+{
+ 0, -3, -1, 5, -4, 6, -5, 1, -5, -5,
+ 2, 4, 7, -5, -4, -5, -5, -5, -5, -5,
+ 3, -5, 8, -5
+};
+
+/* YYPGOTO[NTERM-NUM]. */
+static const yytype_int8 yypgoto[] =
+{
+ -5, -5, -5, -5, 10, -5, 9
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If
+ positive, shift that token. If negative, reduce the rule which
+ number is the opposite. If zero, do what YYDEFACT says.
+ If YYTABLE_NINF, syntax error. */
+#define YYTABLE_NINF -1
+static const yytype_uint8 yytable[] =
+{
+ 10, 11, 12, 1, 13, 9, 7, 2, 8, 1,
+ 17, 0, 18, 19, 22, 16, 20, 23, 0, 0,
+ 0, 0, 0, 21
+};
+
+static const yytype_int8 yycheck[] =
+{
+ 4, 5, 6, 3, 8, 0, 9, 7, 9, 3,
+ 9, -1, 10, 9, 11, 5, 9, 9, -1, -1,
+ -1, -1, -1, 14
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+ symbol of state STATE-NUM. */
+static const yytype_uint8 yystos[] =
+{
+ 0, 3, 7, 13, 14, 15, 16, 9, 9, 0,
+ 4, 5, 6, 8, 17, 18, 16, 9, 10, 9,
+ 9, 18, 11, 9
+};
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY (-2)
+#define YYEOF 0
+
+#define YYACCEPT goto yyacceptlab
+#define YYABORT goto yyabortlab
+#define YYERROR goto yyerrorlab
+
+
+/* Like YYERROR except do call yyerror. This remains here temporarily
+ to ease the transition to the new meaning of YYERROR, for GCC.
+ Once GCC version 2 has supplanted version 1, this can go. */
+
+#define YYFAIL goto yyerrlab
+
+#define YYRECOVERING() (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value) \
+do \
+ if (yychar == YYEMPTY && yylen == 1) \
+ { \
+ yychar = (Token); \
+ yylval = (Value); \
+ yytoken = YYTRANSLATE (yychar); \
+ YYPOPSTACK (1); \
+ goto yybackup; \
+ } \
+ else \
+ { \
+ yyerror (YY_("syntax error: cannot back up")); \
+ YYERROR; \
+ } \
+while (YYID (0))
+
+
+#define YYTERROR 1
+#define YYERRCODE 256
+
+
+/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
+ If N is 0, then set CURRENT to the empty location which ends
+ the previous symbol: RHS[0] (always defined). */
+
+#define YYRHSLOC(Rhs, K) ((Rhs)[K])
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N) \
+ do \
+ if (YYID (N)) \
+ { \
+ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \
+ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \
+ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \
+ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \
+ } \
+ else \
+ { \
+ (Current).first_line = (Current).last_line = \
+ YYRHSLOC (Rhs, 0).last_line; \
+ (Current).first_column = (Current).last_column = \
+ YYRHSLOC (Rhs, 0).last_column; \
+ } \
+ while (YYID (0))
+#endif
+
+
+/* YY_LOCATION_PRINT -- Print the location on the stream.
+ This macro was not mandated originally: define only if we know
+ we won't break user code: when these are the locations we know. */
+
+#ifndef YY_LOCATION_PRINT
+# if YYLTYPE_IS_TRIVIAL
+# define YY_LOCATION_PRINT(File, Loc) \
+ fprintf (File, "%d.%d-%d.%d", \
+ (Loc).first_line, (Loc).first_column, \
+ (Loc).last_line, (Loc).last_column)
+# else
+# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+# endif
+#endif
+
+
+/* YYLEX -- calling `yylex' with the right arguments. */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (YYLEX_PARAM)
+#else
+# define YYLEX yylex ()
+#endif
+
+/* Enable debugging if requested. */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+# include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+# define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args) \
+do { \
+ if (yydebug) \
+ YYFPRINTF Args; \
+} while (YYID (0))
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \
+do { \
+ if (yydebug) \
+ { \
+ YYFPRINTF (stderr, "%s ", Title); \
+ yy_symbol_print (stderr, \
+ Type, Value); \
+ YYFPRINTF (stderr, "\n"); \
+ } \
+} while (YYID (0))
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+#else
+static void
+yy_symbol_value_print (yyoutput, yytype, yyvaluep)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+#endif
+{
+ if (!yyvaluep)
+ return;
+# ifdef YYPRINT
+ if (yytype < YYNTOKENS)
+ YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# else
+ YYUSE (yyoutput);
+# endif
+ switch (yytype)
+ {
+ default:
+ break;
+ }
+}
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+#else
+static void
+yy_symbol_print (yyoutput, yytype, yyvaluep)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+#endif
+{
+ if (yytype < YYNTOKENS)
+ YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+ else
+ YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+ yy_symbol_value_print (yyoutput, yytype, yyvaluep);
+ YYFPRINTF (yyoutput, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included). |
+`------------------------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_stack_print (yytype_int16 *bottom, yytype_int16 *top)
+#else
+static void
+yy_stack_print (bottom, top)
+ yytype_int16 *bottom;
+ yytype_int16 *top;
+#endif
+{
+ YYFPRINTF (stderr, "Stack now");
+ for (; bottom <= top; ++bottom)
+ YYFPRINTF (stderr, " %d", *bottom);
+ YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top) \
+do { \
+ if (yydebug) \
+ yy_stack_print ((Bottom), (Top)); \
+} while (YYID (0))
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced. |
+`------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_reduce_print (YYSTYPE *yyvsp, int yyrule)
+#else
+static void
+yy_reduce_print (yyvsp, yyrule)
+ YYSTYPE *yyvsp;
+ int yyrule;
+#endif
+{
+ int yynrhs = yyr2[yyrule];
+ int yyi;
+ unsigned long int yylno = yyrline[yyrule];
+ YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+ yyrule - 1, yylno);
+ /* The symbols being reduced. */
+ for (yyi = 0; yyi < yynrhs; yyi++)
+ {
+ fprintf (stderr, " $%d = ", yyi + 1);
+ yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
+ &(yyvsp[(yyi + 1) - (yynrhs)])
+ );
+ fprintf (stderr, "\n");
+ }
+}
+
+# define YY_REDUCE_PRINT(Rule) \
+do { \
+ if (yydebug) \
+ yy_reduce_print (yyvsp, Rule); \
+} while (YYID (0))
+
+/* Nonzero means print parse trace. It is left uninitialized so that
+ multiple parsers can coexist. */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks. */
+#ifndef YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+ if the built-in stack extension method is used).
+
+ Do not make this value too large; the results are undefined if
+ YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+ evaluated with infinite-precision integer arithmetic. */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+# if defined __GLIBC__ && defined _STRING_H
+# define yystrlen strlen
+# else
+/* Return the length of YYSTR. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static YYSIZE_T
+yystrlen (const char *yystr)
+#else
+static YYSIZE_T
+yystrlen (yystr)
+ const char *yystr;
+#endif
+{
+ YYSIZE_T yylen;
+ for (yylen = 0; yystr[yylen]; yylen++)
+ continue;
+ return yylen;
+}
+# endif
+# endif
+
+# ifndef yystpcpy
+# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+# define yystpcpy stpcpy
+# else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+ YYDEST. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static char *
+yystpcpy (char *yydest, const char *yysrc)
+#else
+static char *
+yystpcpy (yydest, yysrc)
+ char *yydest;
+ const char *yysrc;
+#endif
+{
+ char *yyd = yydest;
+ const char *yys = yysrc;
+
+ while ((*yyd++ = *yys++) != '\0')
+ continue;
+
+ return yyd - 1;
+}
+# endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+ quotes and backslashes, so that it's suitable for yyerror. The
+ heuristic is that double-quoting is unnecessary unless the string
+ contains an apostrophe, a comma, or backslash (other than
+ backslash-backslash). YYSTR is taken from yytname. If YYRES is
+ null, do not copy; instead, return the length of what the result
+ would have been. */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+ if (*yystr == '"')
+ {
+ YYSIZE_T yyn = 0;
+ char const *yyp = yystr;
+
+ for (;;)
+ switch (*++yyp)
+ {
+ case '\'':
+ case ',':
+ goto do_not_strip_quotes;
+
+ case '\\':
+ if (*++yyp != '\\')
+ goto do_not_strip_quotes;
+ /* Fall through. */
+ default:
+ if (yyres)
+ yyres[yyn] = *yyp;
+ yyn++;
+ break;
+
+ case '"':
+ if (yyres)
+ yyres[yyn] = '\0';
+ return yyn;
+ }
+ do_not_strip_quotes: ;
+ }
+
+ if (! yyres)
+ return yystrlen (yystr);
+
+ return yystpcpy (yyres, yystr) - yyres;
+}
+# endif
+
+/* Copy into YYRESULT an error message about the unexpected token
+ YYCHAR while in state YYSTATE. Return the number of bytes copied,
+ including the terminating null byte. If YYRESULT is null, do not
+ copy anything; just return the number of bytes that would be
+ copied. As a special case, return 0 if an ordinary "syntax error"
+ message will do. Return YYSIZE_MAXIMUM if overflow occurs during
+ size calculation. */
+static YYSIZE_T
+yysyntax_error (char *yyresult, int yystate, int yychar)
+{
+ int yyn = yypact[yystate];
+
+ if (! (YYPACT_NINF < yyn && yyn <= YYLAST))
+ return 0;
+ else
+ {
+ int yytype = YYTRANSLATE (yychar);
+ YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]);
+ YYSIZE_T yysize = yysize0;
+ YYSIZE_T yysize1;
+ int yysize_overflow = 0;
+ enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+ int yyx;
+
+# if 0
+ /* This is so xgettext sees the translatable formats that are
+ constructed on the fly. */
+ YY_("syntax error, unexpected %s");
+ YY_("syntax error, unexpected %s, expecting %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s or %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s");
+# endif
+ char *yyfmt;
+ char const *yyf;
+ static char const yyunexpected[] = "syntax error, unexpected %s";
+ static char const yyexpecting[] = ", expecting %s";
+ static char const yyor[] = " or %s";
+ char yyformat[sizeof yyunexpected
+ + sizeof yyexpecting - 1
+ + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2)
+ * (sizeof yyor - 1))];
+ char const *yyprefix = yyexpecting;
+
+ /* Start YYX at -YYN if negative to avoid negative indexes in
+ YYCHECK. */
+ int yyxbegin = yyn < 0 ? -yyn : 0;
+
+ /* Stay within bounds of both yycheck and yytname. */
+ int yychecklim = YYLAST - yyn + 1;
+ int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+ int yycount = 1;
+
+ yyarg[0] = yytname[yytype];
+ yyfmt = yystpcpy (yyformat, yyunexpected);
+
+ for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+ {
+ if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+ {
+ yycount = 1;
+ yysize = yysize0;
+ yyformat[sizeof yyunexpected - 1] = '\0';
+ break;
+ }
+ yyarg[yycount++] = yytname[yyx];
+ yysize1 = yysize + yytnamerr (0, yytname[yyx]);
+ yysize_overflow |= (yysize1 < yysize);
+ yysize = yysize1;
+ yyfmt = yystpcpy (yyfmt, yyprefix);
+ yyprefix = yyor;
+ }
+
+ yyf = YY_(yyformat);
+ yysize1 = yysize + yystrlen (yyf);
+ yysize_overflow |= (yysize1 < yysize);
+ yysize = yysize1;
+
+ if (yysize_overflow)
+ return YYSIZE_MAXIMUM;
+
+ if (yyresult)
+ {
+ /* Avoid sprintf, as that infringes on the user's name space.
+ Don't have undefined behavior even if the translation
+ produced a string with the wrong number of "%s"s. */
+ char *yyp = yyresult;
+ int yyi = 0;
+ while ((*yyp = *yyf) != '\0')
+ {
+ if (*yyp == '%' && yyf[1] == 's' && yyi < yycount)
+ {
+ yyp += yytnamerr (yyp, yyarg[yyi++]);
+ yyf += 2;
+ }
+ else
+ {
+ yyp++;
+ yyf++;
+ }
+ }
+ }
+ return yysize;
+ }
+}
+#endif /* YYERROR_VERBOSE */
+
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol. |
+`-----------------------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yydestruct (yymsg, yytype, yyvaluep)
+ const char *yymsg;
+ int yytype;
+ YYSTYPE *yyvaluep;
+#endif
+{
+ YYUSE (yyvaluep);
+
+ if (!yymsg)
+ yymsg = "Deleting";
+ YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+ switch (yytype)
+ {
+
+ default:
+ break;
+ }
+}
+
+
+/* Prevent warnings from -Wmissing-prototypes. */
+
+#ifdef YYPARSE_PARAM
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void *YYPARSE_PARAM);
+#else
+int yyparse ();
+#endif
+#else /* ! YYPARSE_PARAM */
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+
+
+/* The look-ahead symbol. */
+int yychar;
+
+/* The semantic value of the look-ahead symbol. */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far. */
+int yynerrs;
+
+
+
+/*----------.
+| yyparse. |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void *YYPARSE_PARAM)
+#else
+int
+yyparse (YYPARSE_PARAM)
+ void *YYPARSE_PARAM;
+#endif
+#else /* ! YYPARSE_PARAM */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void)
+#else
+int
+yyparse ()
+
+#endif
+#endif
+{
+
+ int yystate;
+ int yyn;
+ int yyresult;
+ /* Number of tokens to shift before error messages enabled. */
+ int yyerrstatus;
+ /* Look-ahead token as an internal (translated) token number. */
+ int yytoken = 0;
+#if YYERROR_VERBOSE
+ /* Buffer for error messages, and its allocated size. */
+ char yymsgbuf[128];
+ char *yymsg = yymsgbuf;
+ YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
+#endif
+
+ /* Three stacks and their tools:
+ `yyss': related to states,
+ `yyvs': related to semantic values,
+ `yyls': related to locations.
+
+ Refer to the stacks thru separate pointers, to allow yyoverflow
+ to reallocate them elsewhere. */
+
+ /* The state stack. */
+ yytype_int16 yyssa[YYINITDEPTH];
+ yytype_int16 *yyss = yyssa;
+ yytype_int16 *yyssp;
+
+ /* The semantic value stack. */
+ YYSTYPE yyvsa[YYINITDEPTH];
+ YYSTYPE *yyvs = yyvsa;
+ YYSTYPE *yyvsp;
+
+
+
+#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N))
+
+ YYSIZE_T yystacksize = YYINITDEPTH;
+
+ /* The variables used to return semantic value and location from the
+ action routines. */
+ YYSTYPE yyval;
+
+
+ /* The number of symbols on the RHS of the reduced rule.
+ Keep to zero when no symbol should be popped. */
+ int yylen = 0;
+
+ YYDPRINTF ((stderr, "Starting parse\n"));
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+
+ /* Initialize stack pointers.
+ Waste one element of value and location stack
+ so that they stay on the same level as the state stack.
+ The wasted elements are never initialized. */
+
+ yyssp = yyss;
+ yyvsp = yyvs;
+
+ goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate. |
+`------------------------------------------------------------*/
+ yynewstate:
+ /* In all cases, when you get here, the value and location stacks
+ have just been pushed. So pushing a state here evens the stacks. */
+ yyssp++;
+
+ yysetstate:
+ *yyssp = yystate;
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ {
+ /* Get the current used size of the three stacks, in elements. */
+ YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ {
+ /* Give user a chance to reallocate the stack. Use copies of
+ these so that the &'s don't force the real ones into
+ memory. */
+ YYSTYPE *yyvs1 = yyvs;
+ yytype_int16 *yyss1 = yyss;
+
+
+ /* Each stack pointer address is followed by the size of the
+ data in use in that stack, in bytes. This used to be a
+ conditional around just the two extra args, but that might
+ be undefined if yyoverflow is a macro. */
+ yyoverflow (YY_("memory exhausted"),
+ &yyss1, yysize * sizeof (*yyssp),
+ &yyvs1, yysize * sizeof (*yyvsp),
+
+ &yystacksize);
+
+ yyss = yyss1;
+ yyvs = yyvs1;
+ }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+ goto yyexhaustedlab;
+# else
+ /* Extend the stack our own way. */
+ if (YYMAXDEPTH <= yystacksize)
+ goto yyexhaustedlab;
+ yystacksize *= 2;
+ if (YYMAXDEPTH < yystacksize)
+ yystacksize = YYMAXDEPTH;
+
+ {
+ yytype_int16 *yyss1 = yyss;
+ union yyalloc *yyptr =
+ (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+ if (! yyptr)
+ goto yyexhaustedlab;
+ YYSTACK_RELOCATE (yyss);
+ YYSTACK_RELOCATE (yyvs);
+
+# undef YYSTACK_RELOCATE
+ if (yyss1 != yyssa)
+ YYSTACK_FREE (yyss1);
+ }
+# endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + yysize - 1;
+ yyvsp = yyvs + yysize - 1;
+
+
+ YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+ (unsigned long int) yystacksize));
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ YYABORT;
+ }
+
+ YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+ goto yybackup;
+
+/*-----------.
+| yybackup. |
+`-----------*/
+yybackup:
+
+ /* Do appropriate processing given the current state. Read a
+ look-ahead token if we need one and don't already have one. */
+
+ /* First try to decide what to do without reference to look-ahead token. */
+ yyn = yypact[yystate];
+ if (yyn == YYPACT_NINF)
+ goto yydefault;
+
+ /* Not known => get a look-ahead token if don't already have one. */
+
+ /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */
+ if (yychar == YYEMPTY)
+ {
+ YYDPRINTF ((stderr, "Reading a token: "));
+ yychar = YYLEX;
+ }
+
+ if (yychar <= YYEOF)
+ {
+ yychar = yytoken = YYEOF;
+ YYDPRINTF ((stderr, "Now at end of input.\n"));
+ }
+ else
+ {
+ yytoken = YYTRANSLATE (yychar);
+ YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+ }
+
+ /* If the proper action on seeing token YYTOKEN is to reduce or to
+ detect an error, take that action. */
+ yyn += yytoken;
+ if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+ goto yydefault;
+ yyn = yytable[yyn];
+ if (yyn <= 0)
+ {
+ if (yyn == 0 || yyn == YYTABLE_NINF)
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ /* Count tokens shifted since error; after three, turn off error
+ status. */
+ if (yyerrstatus)
+ yyerrstatus--;
+
+ /* Shift the look-ahead token. */
+ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+ /* Discard the shifted token unless it is eof. */
+ if (yychar != YYEOF)
+ yychar = YYEMPTY;
+
+ yystate = yyn;
+ *++yyvsp = yylval;
+
+ goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state. |
+`-----------------------------------------------------------*/
+yydefault:
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+ goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction. |
+`-----------------------------*/
+yyreduce:
+ /* yyn is the number of a rule to reduce with. */
+ yylen = yyr2[yyn];
+
+ /* If YYLEN is nonzero, implement the default value of the action:
+ `$$ = $1'.
+
+ Otherwise, the following line sets YYVAL to garbage.
+ This behavior is undocumented and Bison
+ users should not rely upon it. Assigning to YYVAL
+ unconditionally makes the parser a bit smaller, and it avoids a
+ GCC warning that YYVAL may be used uninitialized. */
+ yyval = yyvsp[1-yylen];
+
+
+ YY_REDUCE_PRINT (yyn);
+ switch (yyn)
+ {
+ case 6:
+#line 73 "heimdal/lib/com_err/parse.y"
+ {
+ id_str = (yyvsp[(2) - (2)].string);
+ }
+ break;
+
+ case 7:
+#line 79 "heimdal/lib/com_err/parse.y"
+ {
+ base_id = name2number((yyvsp[(2) - (2)].string));
+ strlcpy(name, (yyvsp[(2) - (2)].string), sizeof(name));
+ free((yyvsp[(2) - (2)].string));
+ }
+ break;
+
+ case 8:
+#line 85 "heimdal/lib/com_err/parse.y"
+ {
+ base_id = name2number((yyvsp[(2) - (3)].string));
+ strlcpy(name, (yyvsp[(3) - (3)].string), sizeof(name));
+ free((yyvsp[(2) - (3)].string));
+ free((yyvsp[(3) - (3)].string));
+ }
+ break;
+
+ case 11:
+#line 98 "heimdal/lib/com_err/parse.y"
+ {
+ number = (yyvsp[(2) - (2)].number);
+ }
+ break;
+
+ case 12:
+#line 102 "heimdal/lib/com_err/parse.y"
+ {
+ free(prefix);
+ asprintf (&prefix, "%s_", (yyvsp[(2) - (2)].string));
+ if (prefix == NULL)
+ errx(1, "malloc");
+ free((yyvsp[(2) - (2)].string));
+ }
+ break;
+
+ case 13:
+#line 110 "heimdal/lib/com_err/parse.y"
+ {
+ prefix = realloc(prefix, 1);
+ if (prefix == NULL)
+ errx(1, "malloc");
+ *prefix = '\0';
+ }
+ break;
+
+ case 14:
+#line 117 "heimdal/lib/com_err/parse.y"
+ {
+ struct error_code *ec = malloc(sizeof(*ec));
+
+ if (ec == NULL)
+ errx(1, "malloc");
+
+ ec->next = NULL;
+ ec->number = number;
+ if(prefix && *prefix != '\0') {
+ asprintf (&ec->name, "%s%s", prefix, (yyvsp[(2) - (4)].string));
+ if (ec->name == NULL)
+ errx(1, "malloc");
+ free((yyvsp[(2) - (4)].string));
+ } else
+ ec->name = (yyvsp[(2) - (4)].string);
+ ec->string = (yyvsp[(4) - (4)].string);
+ APPEND(codes, ec);
+ number++;
+ }
+ break;
+
+ case 15:
+#line 137 "heimdal/lib/com_err/parse.y"
+ {
+ YYACCEPT;
+ }
+ break;
+
+
+/* Line 1267 of yacc.c. */
+#line 1470 "heimdal/lib/com_err/parse.y"
+ default: break;
+ }
+ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+
+ *++yyvsp = yyval;
+
+
+ /* Now `shift' the result of the reduction. Determine what state
+ that goes to, based on the state we popped back to and the rule
+ number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+ if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTOKENS];
+
+ goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+ /* If not already recovering from an error, report this error. */
+ if (!yyerrstatus)
+ {
+ ++yynerrs;
+#if ! YYERROR_VERBOSE
+ yyerror (YY_("syntax error"));
+#else
+ {
+ YYSIZE_T yysize = yysyntax_error (0, yystate, yychar);
+ if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM)
+ {
+ YYSIZE_T yyalloc = 2 * yysize;
+ if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM))
+ yyalloc = YYSTACK_ALLOC_MAXIMUM;
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+ yymsg = (char *) YYSTACK_ALLOC (yyalloc);
+ if (yymsg)
+ yymsg_alloc = yyalloc;
+ else
+ {
+ yymsg = yymsgbuf;
+ yymsg_alloc = sizeof yymsgbuf;
+ }
+ }
+
+ if (0 < yysize && yysize <= yymsg_alloc)
+ {
+ (void) yysyntax_error (yymsg, yystate, yychar);
+ yyerror (yymsg);
+ }
+ else
+ {
+ yyerror (YY_("syntax error"));
+ if (yysize != 0)
+ goto yyexhaustedlab;
+ }
+ }
+#endif
+ }
+
+
+
+ if (yyerrstatus == 3)
+ {
+ /* If just tried and failed to reuse look-ahead token after an
+ error, discard it. */
+
+ if (yychar <= YYEOF)
+ {
+ /* Return failure if at end of input. */
+ if (yychar == YYEOF)
+ YYABORT;
+ }
+ else
+ {
+ yydestruct ("Error: discarding",
+ yytoken, &yylval);
+ yychar = YYEMPTY;
+ }
+ }
+
+ /* Else will try to reuse look-ahead token after shifting the error
+ token. */
+ goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR. |
+`---------------------------------------------------*/
+yyerrorlab:
+
+ /* Pacify compilers like GCC when the user code never invokes
+ YYERROR and the label yyerrorlab therefore never appears in user
+ code. */
+ if (/*CONSTCOND*/ 0)
+ goto yyerrorlab;
+
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYERROR. */
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+ yystate = *yyssp;
+ goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR. |
+`-------------------------------------------------------------*/
+yyerrlab1:
+ yyerrstatus = 3; /* Each real token shifted decrements this. */
+
+ for (;;)
+ {
+ yyn = yypact[yystate];
+ if (yyn != YYPACT_NINF)
+ {
+ yyn += YYTERROR;
+ if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+ {
+ yyn = yytable[yyn];
+ if (0 < yyn)
+ break;
+ }
+ }
+
+ /* Pop the current state because it cannot handle the error token. */
+ if (yyssp == yyss)
+ YYABORT;
+
+
+ yydestruct ("Error: popping",
+ yystos[yystate], yyvsp);
+ YYPOPSTACK (1);
+ yystate = *yyssp;
+ YY_STACK_PRINT (yyss, yyssp);
+ }
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ *++yyvsp = yylval;
+
+
+ /* Shift the error token. */
+ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here. |
+`-------------------------------------*/
+yyacceptlab:
+ yyresult = 0;
+ goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here. |
+`-----------------------------------*/
+yyabortlab:
+ yyresult = 1;
+ goto yyreturn;
+
+#ifndef yyoverflow
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here. |
+`-------------------------------------------------*/
+yyexhaustedlab:
+ yyerror (YY_("memory exhausted"));
+ yyresult = 2;
+ /* Fall through. */
+#endif
+
+yyreturn:
+ if (yychar != YYEOF && yychar != YYEMPTY)
+ yydestruct ("Cleanup: discarding lookahead",
+ yytoken, &yylval);
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYABORT or YYACCEPT. */
+ YYPOPSTACK (yylen);
+ YY_STACK_PRINT (yyss, yyssp);
+ while (yyssp != yyss)
+ {
+ yydestruct ("Cleanup: popping",
+ yystos[*yyssp], yyvsp);
+ YYPOPSTACK (1);
+ }
+#ifndef yyoverflow
+ if (yyss != yyssa)
+ YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+#endif
+ /* Make sure YYID is used. */
+ return YYID (yyresult);
+}
+
+
+#line 142 "heimdal/lib/com_err/parse.y"
+
+
+static long
+name2number(const char *str)
+{
+ const char *p;
+ long num = 0;
+ const char *x = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz0123456789_";
+ if(strlen(str) > 4) {
+ yyerror("table name too long");
+ return 0;
+ }
+ for(p = str; *p; p++){
+ char *q = strchr(x, *p);
+ if(q == NULL) {
+ yyerror("invalid character in table name");
+ return 0;
+ }
+ num = (num << 6) + (q - x) + 1;
+ }
+ num <<= 8;
+ if(num > 0x7fffffff)
+ num = -(0xffffffff - num + 1);
+ return num;
+}
+
+void
+yyerror (char *s)
+{
+ error_message ("%s\n", s);
+}
+
diff --git a/source4/heimdal/lib/com_err/parse.h b/source4/heimdal/lib/com_err/parse.h
new file mode 100644
index 0000000000..9aabca9023
--- /dev/null
+++ b/source4/heimdal/lib/com_err/parse.h
@@ -0,0 +1,81 @@
+/* A Bison parser, made by GNU Bison 2.3. */
+
+/* Skeleton interface for Bison's Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+ Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU 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., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ ET = 258,
+ INDEX = 259,
+ PREFIX = 260,
+ EC = 261,
+ ID = 262,
+ END = 263,
+ STRING = 264,
+ NUMBER = 265
+ };
+#endif
+/* Tokens. */
+#define ET 258
+#define INDEX 259
+#define PREFIX 260
+#define EC 261
+#define ID 262
+#define END 263
+#define STRING 264
+#define NUMBER 265
+
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+#line 53 "heimdal/lib/com_err/parse.y"
+{
+ char *string;
+ int number;
+}
+/* Line 1489 of yacc.c. */
+#line 74 "heimdal/lib/com_err/parse.y"
+ YYSTYPE;
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+extern YYSTYPE yylval;
+
diff --git a/source4/heimdal/lib/com_err/parse.y b/source4/heimdal/lib/com_err/parse.y
new file mode 100644
index 0000000000..99dddbe1e1
--- /dev/null
+++ b/source4/heimdal/lib/com_err/parse.y
@@ -0,0 +1,173 @@
+%{
+/*
+ * Copyright (c) 1998 - 2000 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "compile_et.h"
+#include "lex.h"
+
+RCSID("$Id$");
+
+void yyerror (char *s);
+static long name2number(const char *str);
+
+extern char *yytext;
+
+/* This is for bison */
+
+#if !defined(alloca) && !defined(HAVE_ALLOCA)
+#define alloca(x) malloc(x)
+#endif
+
+%}
+
+%union {
+ char *string;
+ int number;
+}
+
+%token ET INDEX PREFIX EC ID END
+%token <string> STRING
+%token <number> NUMBER
+
+%%
+
+file : /* */
+ | header statements
+ ;
+
+header : id et
+ | et
+ ;
+
+id : ID STRING
+ {
+ id_str = $2;
+ }
+ ;
+
+et : ET STRING
+ {
+ base_id = name2number($2);
+ strlcpy(name, $2, sizeof(name));
+ free($2);
+ }
+ | ET STRING STRING
+ {
+ base_id = name2number($2);
+ strlcpy(name, $3, sizeof(name));
+ free($2);
+ free($3);
+ }
+ ;
+
+statements : statement
+ | statements statement
+ ;
+
+statement : INDEX NUMBER
+ {
+ number = $2;
+ }
+ | PREFIX STRING
+ {
+ free(prefix);
+ asprintf (&prefix, "%s_", $2);
+ if (prefix == NULL)
+ errx(1, "malloc");
+ free($2);
+ }
+ | PREFIX
+ {
+ prefix = realloc(prefix, 1);
+ if (prefix == NULL)
+ errx(1, "malloc");
+ *prefix = '\0';
+ }
+ | EC STRING ',' STRING
+ {
+ struct error_code *ec = malloc(sizeof(*ec));
+
+ if (ec == NULL)
+ errx(1, "malloc");
+
+ ec->next = NULL;
+ ec->number = number;
+ if(prefix && *prefix != '\0') {
+ asprintf (&ec->name, "%s%s", prefix, $2);
+ if (ec->name == NULL)
+ errx(1, "malloc");
+ free($2);
+ } else
+ ec->name = $2;
+ ec->string = $4;
+ APPEND(codes, ec);
+ number++;
+ }
+ | END
+ {
+ YYACCEPT;
+ }
+ ;
+
+%%
+
+static long
+name2number(const char *str)
+{
+ const char *p;
+ long num = 0;
+ const char *x = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz0123456789_";
+ if(strlen(str) > 4) {
+ yyerror("table name too long");
+ return 0;
+ }
+ for(p = str; *p; p++){
+ char *q = strchr(x, *p);
+ if(q == NULL) {
+ yyerror("invalid character in table name");
+ return 0;
+ }
+ num = (num << 6) + (q - x) + 1;
+ }
+ num <<= 8;
+ if(num > 0x7fffffff)
+ num = -(0xffffffff - num + 1);
+ return num;
+}
+
+void
+yyerror (char *s)
+{
+ error_message ("%s\n", s);
+}
diff --git a/source4/heimdal/lib/gssapi/gssapi/gssapi.h b/source4/heimdal/lib/gssapi/gssapi/gssapi.h
new file mode 100644
index 0000000000..f8b599a664
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/gssapi/gssapi.h
@@ -0,0 +1,815 @@
+/*
+ * Copyright (c) 1997 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef GSSAPI_GSSAPI_H_
+#define GSSAPI_GSSAPI_H_
+
+/*
+ * First, include stddef.h to get size_t defined.
+ */
+#include <stddef.h>
+
+#include <krb5-types.h>
+
+#ifndef BUILD_GSSAPI_LIB
+#if defined(_WIN32)
+#define GSSAPI_LIB_FUNCTION _stdcall __declspec(dllimport)
+#define GSSAPI_LIB_VARIABLE __declspec(dllimport)
+#else
+#define GSSAPI_LIB_FUNCTION
+#define GSSAPI_LIB_VARIABLE
+#endif
+#endif
+
+/*
+ * Now define the three implementation-dependent types.
+ */
+
+typedef uint32_t OM_uint32;
+typedef uint64_t OM_uint64;
+
+typedef uint32_t gss_uint32;
+
+struct gss_name_t_desc_struct;
+typedef struct gss_name_t_desc_struct *gss_name_t;
+
+struct gss_ctx_id_t_desc_struct;
+typedef struct gss_ctx_id_t_desc_struct *gss_ctx_id_t;
+
+typedef struct gss_OID_desc_struct {
+ OM_uint32 length;
+ void *elements;
+} gss_OID_desc, *gss_OID;
+
+typedef struct gss_OID_set_desc_struct {
+ size_t count;
+ gss_OID elements;
+} gss_OID_set_desc, *gss_OID_set;
+
+typedef int gss_cred_usage_t;
+
+struct gss_cred_id_t_desc_struct;
+typedef struct gss_cred_id_t_desc_struct *gss_cred_id_t;
+
+typedef struct gss_buffer_desc_struct {
+ size_t length;
+ void *value;
+} gss_buffer_desc, *gss_buffer_t;
+
+typedef struct gss_channel_bindings_struct {
+ OM_uint32 initiator_addrtype;
+ gss_buffer_desc initiator_address;
+ OM_uint32 acceptor_addrtype;
+ gss_buffer_desc acceptor_address;
+ gss_buffer_desc application_data;
+} *gss_channel_bindings_t;
+
+/* GGF extension data types */
+typedef struct gss_buffer_set_desc_struct {
+ size_t count;
+ gss_buffer_desc *elements;
+} gss_buffer_set_desc, *gss_buffer_set_t;
+
+/*
+ * For now, define a QOP-type as an OM_uint32
+ */
+typedef OM_uint32 gss_qop_t;
+
+/*
+ * Flag bits for context-level services.
+ */
+#define GSS_C_DELEG_FLAG 1
+#define GSS_C_MUTUAL_FLAG 2
+#define GSS_C_REPLAY_FLAG 4
+#define GSS_C_SEQUENCE_FLAG 8
+#define GSS_C_CONF_FLAG 16
+#define GSS_C_INTEG_FLAG 32
+#define GSS_C_ANON_FLAG 64
+#define GSS_C_PROT_READY_FLAG 128
+#define GSS_C_TRANS_FLAG 256
+
+#define GSS_C_DCE_STYLE 4096
+#define GSS_C_IDENTIFY_FLAG 8192
+#define GSS_C_EXTENDED_ERROR_FLAG 16384
+#define GSS_C_DELEG_POLICY_FLAG 32768
+
+/*
+ * Credential usage options
+ */
+#define GSS_C_BOTH 0
+#define GSS_C_INITIATE 1
+#define GSS_C_ACCEPT 2
+
+/*
+ * Status code types for gss_display_status
+ */
+#define GSS_C_GSS_CODE 1
+#define GSS_C_MECH_CODE 2
+
+/*
+ * The constant definitions for channel-bindings address families
+ */
+#define GSS_C_AF_UNSPEC 0
+#define GSS_C_AF_LOCAL 1
+#define GSS_C_AF_INET 2
+#define GSS_C_AF_IMPLINK 3
+#define GSS_C_AF_PUP 4
+#define GSS_C_AF_CHAOS 5
+#define GSS_C_AF_NS 6
+#define GSS_C_AF_NBS 7
+#define GSS_C_AF_ECMA 8
+#define GSS_C_AF_DATAKIT 9
+#define GSS_C_AF_CCITT 10
+#define GSS_C_AF_SNA 11
+#define GSS_C_AF_DECnet 12
+#define GSS_C_AF_DLI 13
+#define GSS_C_AF_LAT 14
+#define GSS_C_AF_HYLINK 15
+#define GSS_C_AF_APPLETALK 16
+#define GSS_C_AF_BSC 17
+#define GSS_C_AF_DSS 18
+#define GSS_C_AF_OSI 19
+#define GSS_C_AF_X25 21
+#define GSS_C_AF_INET6 24
+
+#define GSS_C_AF_NULLADDR 255
+
+/*
+ * Various Null values
+ */
+#define GSS_C_NO_NAME ((gss_name_t) 0)
+#define GSS_C_NO_BUFFER ((gss_buffer_t) 0)
+#define GSS_C_NO_BUFFER_SET ((gss_buffer_set_t) 0)
+#define GSS_C_NO_OID ((gss_OID) 0)
+#define GSS_C_NO_OID_SET ((gss_OID_set) 0)
+#define GSS_C_NO_CONTEXT ((gss_ctx_id_t) 0)
+#define GSS_C_NO_CREDENTIAL ((gss_cred_id_t) 0)
+#define GSS_C_NO_CHANNEL_BINDINGS ((gss_channel_bindings_t) 0)
+#define GSS_C_EMPTY_BUFFER {0, NULL}
+
+/*
+ * Some alternate names for a couple of the above
+ * values. These are defined for V1 compatibility.
+ */
+#define GSS_C_NULL_OID GSS_C_NO_OID
+#define GSS_C_NULL_OID_SET GSS_C_NO_OID_SET
+
+/*
+ * Define the default Quality of Protection for per-message
+ * services. Note that an implementation that offers multiple
+ * levels of QOP may define GSS_C_QOP_DEFAULT to be either zero
+ * (as done here) to mean "default protection", or to a specific
+ * explicit QOP value. However, a value of 0 should always be
+ * interpreted by a GSSAPI implementation as a request for the
+ * default protection level.
+ */
+#define GSS_C_QOP_DEFAULT 0
+
+#define GSS_KRB5_CONF_C_QOP_DES 0x0100
+#define GSS_KRB5_CONF_C_QOP_DES3_KD 0x0200
+
+/*
+ * Expiration time of 2^32-1 seconds means infinite lifetime for a
+ * credential or security context
+ */
+#define GSS_C_INDEFINITE 0xfffffffful
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * The implementation must reserve static storage for a
+ * gss_OID_desc object containing the value
+ * {10, (void *)"\x2a\x86\x48\x86\xf7\x12"
+ * "\x01\x02\x01\x01"},
+ * corresponding to an object-identifier value of
+ * {iso(1) member-body(2) United States(840) mit(113554)
+ * infosys(1) gssapi(2) generic(1) user_name(1)}. The constant
+ * GSS_C_NT_USER_NAME should be initialized to point
+ * to that gss_OID_desc.
+ */
+extern GSSAPI_LIB_VARIABLE gss_OID GSS_C_NT_USER_NAME;
+
+/*
+ * The implementation must reserve static storage for a
+ * gss_OID_desc object containing the value
+ * {10, (void *)"\x2a\x86\x48\x86\xf7\x12"
+ * "\x01\x02\x01\x02"},
+ * corresponding to an object-identifier value of
+ * {iso(1) member-body(2) United States(840) mit(113554)
+ * infosys(1) gssapi(2) generic(1) machine_uid_name(2)}.
+ * The constant GSS_C_NT_MACHINE_UID_NAME should be
+ * initialized to point to that gss_OID_desc.
+ */
+extern GSSAPI_LIB_VARIABLE gss_OID GSS_C_NT_MACHINE_UID_NAME;
+
+/*
+ * The implementation must reserve static storage for a
+ * gss_OID_desc object containing the value
+ * {10, (void *)"\x2a\x86\x48\x86\xf7\x12"
+ * "\x01\x02\x01\x03"},
+ * corresponding to an object-identifier value of
+ * {iso(1) member-body(2) United States(840) mit(113554)
+ * infosys(1) gssapi(2) generic(1) string_uid_name(3)}.
+ * The constant GSS_C_NT_STRING_UID_NAME should be
+ * initialized to point to that gss_OID_desc.
+ */
+extern GSSAPI_LIB_VARIABLE gss_OID GSS_C_NT_STRING_UID_NAME;
+
+/*
+ * The implementation must reserve static storage for a
+ * gss_OID_desc object containing the value
+ * {6, (void *)"\x2b\x06\x01\x05\x06\x02"},
+ * corresponding to an object-identifier value of
+ * {iso(1) org(3) dod(6) internet(1) security(5)
+ * nametypes(6) gss-host-based-services(2)). The constant
+ * GSS_C_NT_HOSTBASED_SERVICE_X should be initialized to point
+ * to that gss_OID_desc. This is a deprecated OID value, and
+ * implementations wishing to support hostbased-service names
+ * should instead use the GSS_C_NT_HOSTBASED_SERVICE OID,
+ * defined below, to identify such names;
+ * GSS_C_NT_HOSTBASED_SERVICE_X should be accepted a synonym
+ * for GSS_C_NT_HOSTBASED_SERVICE when presented as an input
+ * parameter, but should not be emitted by GSS-API
+ * implementations
+ */
+extern GSSAPI_LIB_VARIABLE gss_OID GSS_C_NT_HOSTBASED_SERVICE_X;
+
+/*
+ * The implementation must reserve static storage for a
+ * gss_OID_desc object containing the value
+ * {10, (void *)"\x2a\x86\x48\x86\xf7\x12"
+ * "\x01\x02\x01\x04"}, corresponding to an
+ * object-identifier value of {iso(1) member-body(2)
+ * Unites States(840) mit(113554) infosys(1) gssapi(2)
+ * generic(1) service_name(4)}. The constant
+ * GSS_C_NT_HOSTBASED_SERVICE should be initialized
+ * to point to that gss_OID_desc.
+ */
+extern GSSAPI_LIB_VARIABLE gss_OID GSS_C_NT_HOSTBASED_SERVICE;
+
+/*
+ * The implementation must reserve static storage for a
+ * gss_OID_desc object containing the value
+ * {6, (void *)"\x2b\x06\01\x05\x06\x03"},
+ * corresponding to an object identifier value of
+ * {1(iso), 3(org), 6(dod), 1(internet), 5(security),
+ * 6(nametypes), 3(gss-anonymous-name)}. The constant
+ * and GSS_C_NT_ANONYMOUS should be initialized to point
+ * to that gss_OID_desc.
+ */
+extern GSSAPI_LIB_VARIABLE gss_OID GSS_C_NT_ANONYMOUS;
+
+/*
+ * The implementation must reserve static storage for a
+ * gss_OID_desc object containing the value
+ * {6, (void *)"\x2b\x06\x01\x05\x06\x04"},
+ * corresponding to an object-identifier value of
+ * {1(iso), 3(org), 6(dod), 1(internet), 5(security),
+ * 6(nametypes), 4(gss-api-exported-name)}. The constant
+ * GSS_C_NT_EXPORT_NAME should be initialized to point
+ * to that gss_OID_desc.
+ */
+extern GSSAPI_LIB_VARIABLE gss_OID GSS_C_NT_EXPORT_NAME;
+
+/*
+ * Digest mechanism
+ */
+
+extern GSSAPI_LIB_VARIABLE gss_OID GSS_SASL_DIGEST_MD5_MECHANISM;
+
+/*
+ * NTLM mechanism
+ */
+
+extern GSSAPI_LIB_VARIABLE gss_OID GSS_NTLM_MECHANISM;
+
+/* Major status codes */
+
+#define GSS_S_COMPLETE 0
+
+/*
+ * Some "helper" definitions to make the status code macros obvious.
+ */
+#define GSS_C_CALLING_ERROR_OFFSET 24
+#define GSS_C_ROUTINE_ERROR_OFFSET 16
+#define GSS_C_SUPPLEMENTARY_OFFSET 0
+#define GSS_C_CALLING_ERROR_MASK 0377ul
+#define GSS_C_ROUTINE_ERROR_MASK 0377ul
+#define GSS_C_SUPPLEMENTARY_MASK 0177777ul
+
+/*
+ * The macros that test status codes for error conditions.
+ * Note that the GSS_ERROR() macro has changed slightly from
+ * the V1 GSSAPI so that it now evaluates its argument
+ * only once.
+ */
+#define GSS_CALLING_ERROR(x) \
+ (x & (GSS_C_CALLING_ERROR_MASK << GSS_C_CALLING_ERROR_OFFSET))
+#define GSS_ROUTINE_ERROR(x) \
+ (x & (GSS_C_ROUTINE_ERROR_MASK << GSS_C_ROUTINE_ERROR_OFFSET))
+#define GSS_SUPPLEMENTARY_INFO(x) \
+ (x & (GSS_C_SUPPLEMENTARY_MASK << GSS_C_SUPPLEMENTARY_OFFSET))
+#define GSS_ERROR(x) \
+ (x & ((GSS_C_CALLING_ERROR_MASK << GSS_C_CALLING_ERROR_OFFSET) | \
+ (GSS_C_ROUTINE_ERROR_MASK << GSS_C_ROUTINE_ERROR_OFFSET)))
+
+/*
+ * Now the actual status code definitions
+ */
+
+/*
+ * Calling errors:
+ */
+#define GSS_S_CALL_INACCESSIBLE_READ \
+ (1ul << GSS_C_CALLING_ERROR_OFFSET)
+#define GSS_S_CALL_INACCESSIBLE_WRITE \
+ (2ul << GSS_C_CALLING_ERROR_OFFSET)
+#define GSS_S_CALL_BAD_STRUCTURE \
+ (3ul << GSS_C_CALLING_ERROR_OFFSET)
+
+/*
+ * Routine errors:
+ */
+#define GSS_S_BAD_MECH (1ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_BAD_NAME (2ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_BAD_NAMETYPE (3ul << GSS_C_ROUTINE_ERROR_OFFSET)
+
+#define GSS_S_BAD_BINDINGS (4ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_BAD_STATUS (5ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_BAD_SIG (6ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_BAD_MIC GSS_S_BAD_SIG
+#define GSS_S_NO_CRED (7ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_NO_CONTEXT (8ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_DEFECTIVE_TOKEN (9ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_DEFECTIVE_CREDENTIAL (10ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_CREDENTIALS_EXPIRED (11ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_CONTEXT_EXPIRED (12ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_FAILURE (13ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_BAD_QOP (14ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_UNAUTHORIZED (15ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_UNAVAILABLE (16ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_DUPLICATE_ELEMENT (17ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_NAME_NOT_MN (18ul << GSS_C_ROUTINE_ERROR_OFFSET)
+
+/*
+ * Supplementary info bits:
+ */
+#define GSS_S_CONTINUE_NEEDED (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 0))
+#define GSS_S_DUPLICATE_TOKEN (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 1))
+#define GSS_S_OLD_TOKEN (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 2))
+#define GSS_S_UNSEQ_TOKEN (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 3))
+#define GSS_S_GAP_TOKEN (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 4))
+
+/*
+ * Finally, function prototypes for the GSS-API routines.
+ */
+
+OM_uint32 GSSAPI_LIB_FUNCTION gss_acquire_cred
+ (OM_uint32 * /*minor_status*/,
+ const gss_name_t /*desired_name*/,
+ OM_uint32 /*time_req*/,
+ const gss_OID_set /*desired_mechs*/,
+ gss_cred_usage_t /*cred_usage*/,
+ gss_cred_id_t * /*output_cred_handle*/,
+ gss_OID_set * /*actual_mechs*/,
+ OM_uint32 * /*time_rec*/
+ );
+
+OM_uint32 GSSAPI_LIB_FUNCTION gss_release_cred
+ (OM_uint32 * /*minor_status*/,
+ gss_cred_id_t * /*cred_handle*/
+ );
+
+OM_uint32 GSSAPI_LIB_FUNCTION gss_init_sec_context
+ (OM_uint32 * /*minor_status*/,
+ const gss_cred_id_t /*initiator_cred_handle*/,
+ gss_ctx_id_t * /*context_handle*/,
+ const gss_name_t /*target_name*/,
+ const gss_OID /*mech_type*/,
+ OM_uint32 /*req_flags*/,
+ OM_uint32 /*time_req*/,
+ const gss_channel_bindings_t /*input_chan_bindings*/,
+ const gss_buffer_t /*input_token*/,
+ gss_OID * /*actual_mech_type*/,
+ gss_buffer_t /*output_token*/,
+ OM_uint32 * /*ret_flags*/,
+ OM_uint32 * /*time_rec*/
+ );
+
+OM_uint32 GSSAPI_LIB_FUNCTION gss_accept_sec_context
+ (OM_uint32 * /*minor_status*/,
+ gss_ctx_id_t * /*context_handle*/,
+ const gss_cred_id_t /*acceptor_cred_handle*/,
+ const gss_buffer_t /*input_token_buffer*/,
+ const gss_channel_bindings_t /*input_chan_bindings*/,
+ gss_name_t * /*src_name*/,
+ gss_OID * /*mech_type*/,
+ gss_buffer_t /*output_token*/,
+ OM_uint32 * /*ret_flags*/,
+ OM_uint32 * /*time_rec*/,
+ gss_cred_id_t * /*delegated_cred_handle*/
+ );
+
+OM_uint32 GSSAPI_LIB_FUNCTION gss_process_context_token
+ (OM_uint32 * /*minor_status*/,
+ const gss_ctx_id_t /*context_handle*/,
+ const gss_buffer_t /*token_buffer*/
+ );
+
+OM_uint32 GSSAPI_LIB_FUNCTION gss_delete_sec_context
+ (OM_uint32 * /*minor_status*/,
+ gss_ctx_id_t * /*context_handle*/,
+ gss_buffer_t /*output_token*/
+ );
+
+OM_uint32 GSSAPI_LIB_FUNCTION gss_context_time
+ (OM_uint32 * /*minor_status*/,
+ const gss_ctx_id_t /*context_handle*/,
+ OM_uint32 * /*time_rec*/
+ );
+
+OM_uint32 GSSAPI_LIB_FUNCTION gss_get_mic
+ (OM_uint32 * /*minor_status*/,
+ const gss_ctx_id_t /*context_handle*/,
+ gss_qop_t /*qop_req*/,
+ const gss_buffer_t /*message_buffer*/,
+ gss_buffer_t /*message_token*/
+ );
+
+OM_uint32 GSSAPI_LIB_FUNCTION gss_verify_mic
+ (OM_uint32 * /*minor_status*/,
+ const gss_ctx_id_t /*context_handle*/,
+ const gss_buffer_t /*message_buffer*/,
+ const gss_buffer_t /*token_buffer*/,
+ gss_qop_t * /*qop_state*/
+ );
+
+OM_uint32 GSSAPI_LIB_FUNCTION gss_wrap
+ (OM_uint32 * /*minor_status*/,
+ const gss_ctx_id_t /*context_handle*/,
+ int /*conf_req_flag*/,
+ gss_qop_t /*qop_req*/,
+ const gss_buffer_t /*input_message_buffer*/,
+ int * /*conf_state*/,
+ gss_buffer_t /*output_message_buffer*/
+ );
+
+OM_uint32 GSSAPI_LIB_FUNCTION gss_unwrap
+ (OM_uint32 * /*minor_status*/,
+ const gss_ctx_id_t /*context_handle*/,
+ const gss_buffer_t /*input_message_buffer*/,
+ gss_buffer_t /*output_message_buffer*/,
+ int * /*conf_state*/,
+ gss_qop_t * /*qop_state*/
+ );
+
+OM_uint32 GSSAPI_LIB_FUNCTION gss_display_status
+ (OM_uint32 * /*minor_status*/,
+ OM_uint32 /*status_value*/,
+ int /*status_type*/,
+ const gss_OID /*mech_type*/,
+ OM_uint32 * /*message_context*/,
+ gss_buffer_t /*status_string*/
+ );
+
+OM_uint32 GSSAPI_LIB_FUNCTION gss_indicate_mechs
+ (OM_uint32 * /*minor_status*/,
+ gss_OID_set * /*mech_set*/
+ );
+
+OM_uint32 GSSAPI_LIB_FUNCTION gss_compare_name
+ (OM_uint32 * /*minor_status*/,
+ const gss_name_t /*name1*/,
+ const gss_name_t /*name2*/,
+ int * /*name_equal*/
+ );
+
+OM_uint32 GSSAPI_LIB_FUNCTION gss_display_name
+ (OM_uint32 * /*minor_status*/,
+ const gss_name_t /*input_name*/,
+ gss_buffer_t /*output_name_buffer*/,
+ gss_OID * /*output_name_type*/
+ );
+
+OM_uint32 GSSAPI_LIB_FUNCTION gss_import_name
+ (OM_uint32 * /*minor_status*/,
+ const gss_buffer_t /*input_name_buffer*/,
+ const gss_OID /*input_name_type*/,
+ gss_name_t * /*output_name*/
+ );
+
+OM_uint32 GSSAPI_LIB_FUNCTION gss_export_name
+ (OM_uint32 * /*minor_status*/,
+ const gss_name_t /*input_name*/,
+ gss_buffer_t /*exported_name*/
+ );
+
+OM_uint32 GSSAPI_LIB_FUNCTION gss_release_name
+ (OM_uint32 * /*minor_status*/,
+ gss_name_t * /*input_name*/
+ );
+
+OM_uint32 GSSAPI_LIB_FUNCTION gss_release_buffer
+ (OM_uint32 * /*minor_status*/,
+ gss_buffer_t /*buffer*/
+ );
+
+OM_uint32 GSSAPI_LIB_FUNCTION gss_release_oid_set
+ (OM_uint32 * /*minor_status*/,
+ gss_OID_set * /*set*/
+ );
+
+OM_uint32 GSSAPI_LIB_FUNCTION gss_inquire_cred
+ (OM_uint32 * /*minor_status*/,
+ const gss_cred_id_t /*cred_handle*/,
+ gss_name_t * /*name*/,
+ OM_uint32 * /*lifetime*/,
+ gss_cred_usage_t * /*cred_usage*/,
+ gss_OID_set * /*mechanisms*/
+ );
+
+OM_uint32 GSSAPI_LIB_FUNCTION gss_inquire_context (
+ OM_uint32 * /*minor_status*/,
+ const gss_ctx_id_t /*context_handle*/,
+ gss_name_t * /*src_name*/,
+ gss_name_t * /*targ_name*/,
+ OM_uint32 * /*lifetime_rec*/,
+ gss_OID * /*mech_type*/,
+ OM_uint32 * /*ctx_flags*/,
+ int * /*locally_initiated*/,
+ int * /*open_context*/
+ );
+
+OM_uint32 GSSAPI_LIB_FUNCTION gss_wrap_size_limit (
+ OM_uint32 * /*minor_status*/,
+ const gss_ctx_id_t /*context_handle*/,
+ int /*conf_req_flag*/,
+ gss_qop_t /*qop_req*/,
+ OM_uint32 /*req_output_size*/,
+ OM_uint32 * /*max_input_size*/
+ );
+
+OM_uint32 GSSAPI_LIB_FUNCTION gss_add_cred (
+ OM_uint32 * /*minor_status*/,
+ const gss_cred_id_t /*input_cred_handle*/,
+ const gss_name_t /*desired_name*/,
+ const gss_OID /*desired_mech*/,
+ gss_cred_usage_t /*cred_usage*/,
+ OM_uint32 /*initiator_time_req*/,
+ OM_uint32 /*acceptor_time_req*/,
+ gss_cred_id_t * /*output_cred_handle*/,
+ gss_OID_set * /*actual_mechs*/,
+ OM_uint32 * /*initiator_time_rec*/,
+ OM_uint32 * /*acceptor_time_rec*/
+ );
+
+OM_uint32 GSSAPI_LIB_FUNCTION gss_inquire_cred_by_mech (
+ OM_uint32 * /*minor_status*/,
+ const gss_cred_id_t /*cred_handle*/,
+ const gss_OID /*mech_type*/,
+ gss_name_t * /*name*/,
+ OM_uint32 * /*initiator_lifetime*/,
+ OM_uint32 * /*acceptor_lifetime*/,
+ gss_cred_usage_t * /*cred_usage*/
+ );
+
+OM_uint32 GSSAPI_LIB_FUNCTION gss_export_sec_context (
+ OM_uint32 * /*minor_status*/,
+ gss_ctx_id_t * /*context_handle*/,
+ gss_buffer_t /*interprocess_token*/
+ );
+
+OM_uint32 GSSAPI_LIB_FUNCTION gss_import_sec_context (
+ OM_uint32 * /*minor_status*/,
+ const gss_buffer_t /*interprocess_token*/,
+ gss_ctx_id_t * /*context_handle*/
+ );
+
+OM_uint32 GSSAPI_LIB_FUNCTION gss_create_empty_oid_set (
+ OM_uint32 * /*minor_status*/,
+ gss_OID_set * /*oid_set*/
+ );
+
+OM_uint32 GSSAPI_LIB_FUNCTION gss_add_oid_set_member (
+ OM_uint32 * /*minor_status*/,
+ const gss_OID /*member_oid*/,
+ gss_OID_set * /*oid_set*/
+ );
+
+OM_uint32 GSSAPI_LIB_FUNCTION gss_test_oid_set_member (
+ OM_uint32 * /*minor_status*/,
+ const gss_OID /*member*/,
+ const gss_OID_set /*set*/,
+ int * /*present*/
+ );
+
+OM_uint32 GSSAPI_LIB_FUNCTION gss_inquire_names_for_mech (
+ OM_uint32 * /*minor_status*/,
+ const gss_OID /*mechanism*/,
+ gss_OID_set * /*name_types*/
+ );
+
+OM_uint32 GSSAPI_LIB_FUNCTION gss_inquire_mechs_for_name (
+ OM_uint32 * /*minor_status*/,
+ const gss_name_t /*input_name*/,
+ gss_OID_set * /*mech_types*/
+ );
+
+OM_uint32 GSSAPI_LIB_FUNCTION gss_canonicalize_name (
+ OM_uint32 * /*minor_status*/,
+ const gss_name_t /*input_name*/,
+ const gss_OID /*mech_type*/,
+ gss_name_t * /*output_name*/
+ );
+
+OM_uint32 GSSAPI_LIB_FUNCTION gss_duplicate_name (
+ OM_uint32 * /*minor_status*/,
+ const gss_name_t /*src_name*/,
+ gss_name_t * /*dest_name*/
+ );
+
+OM_uint32 GSSAPI_LIB_FUNCTION gss_duplicate_oid (
+ OM_uint32 * /* minor_status */,
+ gss_OID /* src_oid */,
+ gss_OID * /* dest_oid */
+ );
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_release_oid
+ (OM_uint32 * /*minor_status*/,
+ gss_OID * /* oid */
+ );
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_oid_to_str(
+ OM_uint32 * /*minor_status*/,
+ gss_OID /* oid */,
+ gss_buffer_t /* str */
+ );
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_inquire_sec_context_by_oid(
+ OM_uint32 * minor_status,
+ const gss_ctx_id_t context_handle,
+ const gss_OID desired_object,
+ gss_buffer_set_t *data_set
+ );
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_set_sec_context_option (OM_uint32 *minor_status,
+ gss_ctx_id_t *context_handle,
+ const gss_OID desired_object,
+ const gss_buffer_t value);
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_set_cred_option (OM_uint32 *minor_status,
+ gss_cred_id_t *cred_handle,
+ const gss_OID object,
+ const gss_buffer_t value);
+
+int GSSAPI_LIB_FUNCTION
+gss_oid_equal(const gss_OID a, const gss_OID b);
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_create_empty_buffer_set
+ (OM_uint32 * minor_status,
+ gss_buffer_set_t *buffer_set);
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_add_buffer_set_member
+ (OM_uint32 * minor_status,
+ const gss_buffer_t member_buffer,
+ gss_buffer_set_t *buffer_set);
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_release_buffer_set
+ (OM_uint32 * minor_status,
+ gss_buffer_set_t *buffer_set);
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_inquire_cred_by_oid(OM_uint32 *minor_status,
+ const gss_cred_id_t cred_handle,
+ const gss_OID desired_object,
+ gss_buffer_set_t *data_set);
+
+/*
+ * RFC 4401
+ */
+
+#define GSS_C_PRF_KEY_FULL 0
+#define GSS_C_PRF_KEY_PARTIAL 1
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_pseudo_random
+ (OM_uint32 *minor_status,
+ gss_ctx_id_t context,
+ int prf_key,
+ const gss_buffer_t prf_in,
+ ssize_t desired_output_len,
+ gss_buffer_t prf_out
+ );
+
+/*
+ * The following routines are obsolete variants of gss_get_mic,
+ * gss_verify_mic, gss_wrap and gss_unwrap. They should be
+ * provided by GSSAPI V2 implementations for backwards
+ * compatibility with V1 applications. Distinct entrypoints
+ * (as opposed to #defines) should be provided, both to allow
+ * GSSAPI V1 applications to link against GSSAPI V2 implementations,
+ * and to retain the slight parameter type differences between the
+ * obsolete versions of these routines and their current forms.
+ */
+
+OM_uint32 GSSAPI_LIB_FUNCTION gss_sign
+ (OM_uint32 * /*minor_status*/,
+ gss_ctx_id_t /*context_handle*/,
+ int /*qop_req*/,
+ gss_buffer_t /*message_buffer*/,
+ gss_buffer_t /*message_token*/
+ );
+
+OM_uint32 GSSAPI_LIB_FUNCTION gss_verify
+ (OM_uint32 * /*minor_status*/,
+ gss_ctx_id_t /*context_handle*/,
+ gss_buffer_t /*message_buffer*/,
+ gss_buffer_t /*token_buffer*/,
+ int * /*qop_state*/
+ );
+
+OM_uint32 GSSAPI_LIB_FUNCTION gss_seal
+ (OM_uint32 * /*minor_status*/,
+ gss_ctx_id_t /*context_handle*/,
+ int /*conf_req_flag*/,
+ int /*qop_req*/,
+ gss_buffer_t /*input_message_buffer*/,
+ int * /*conf_state*/,
+ gss_buffer_t /*output_message_buffer*/
+ );
+
+OM_uint32 GSSAPI_LIB_FUNCTION gss_unseal
+ (OM_uint32 * /*minor_status*/,
+ gss_ctx_id_t /*context_handle*/,
+ gss_buffer_t /*input_message_buffer*/,
+ gss_buffer_t /*output_message_buffer*/,
+ int * /*conf_state*/,
+ int * /*qop_state*/
+ );
+
+/*
+ *
+ */
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_encapsulate_token(gss_buffer_t /* input_token */,
+ gss_OID /* oid */,
+ gss_buffer_t /* output_token */);
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_decapsulate_token(gss_buffer_t /* input_token */,
+ gss_OID /* oid */,
+ gss_buffer_t /* output_token */);
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#include <gssapi/gssapi_krb5.h>
+#include <gssapi/gssapi_spnego.h>
+
+#endif /* GSSAPI_GSSAPI_H_ */
diff --git a/source4/heimdal/lib/gssapi/gssapi/gssapi_krb5.h b/source4/heimdal/lib/gssapi/gssapi/gssapi_krb5.h
new file mode 100644
index 0000000000..a821f73d2a
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/gssapi/gssapi_krb5.h
@@ -0,0 +1,253 @@
+/*
+ * Copyright (c) 1997 - 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef GSSAPI_KRB5_H_
+#define GSSAPI_KRB5_H_
+
+#include <gssapi/gssapi.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if !defined(__GNUC__) && !defined(__attribute__)
+#define __attribute__(x)
+#endif
+
+#ifndef GSSKRB5_FUNCTION_DEPRECATED
+#define GSSKRB5_FUNCTION_DEPRECATED __attribute__((deprecated))
+#endif
+
+
+/*
+ * This is for kerberos5 names.
+ */
+
+extern GSSAPI_LIB_VARIABLE gss_OID GSS_KRB5_NT_PRINCIPAL_NAME;
+extern GSSAPI_LIB_VARIABLE gss_OID GSS_KRB5_NT_USER_NAME;
+extern GSSAPI_LIB_VARIABLE gss_OID GSS_KRB5_NT_MACHINE_UID_NAME;
+extern GSSAPI_LIB_VARIABLE gss_OID GSS_KRB5_NT_STRING_UID_NAME;
+
+extern GSSAPI_LIB_VARIABLE gss_OID GSS_KRB5_MECHANISM;
+
+/* for compatibility with MIT api */
+
+#define gss_mech_krb5 GSS_KRB5_MECHANISM
+#define gss_krb5_nt_general_name GSS_KRB5_NT_PRINCIPAL_NAME
+
+/* Extensions set contexts options */
+extern GSSAPI_LIB_VARIABLE gss_OID GSS_KRB5_COPY_CCACHE_X;
+extern GSSAPI_LIB_VARIABLE gss_OID GSS_KRB5_COMPAT_DES3_MIC_X;
+extern GSSAPI_LIB_VARIABLE gss_OID GSS_KRB5_REGISTER_ACCEPTOR_IDENTITY_X;
+extern GSSAPI_LIB_VARIABLE gss_OID GSS_KRB5_SET_DNS_CANONICALIZE_X;
+extern GSSAPI_LIB_VARIABLE gss_OID GSS_KRB5_SEND_TO_KDC_X;
+extern GSSAPI_LIB_VARIABLE gss_OID GSS_KRB5_SET_DEFAULT_REALM_X;
+extern GSSAPI_LIB_VARIABLE gss_OID GSS_KRB5_CCACHE_NAME_X;
+extern GSSAPI_LIB_VARIABLE gss_OID GSS_KRB5_SET_TIME_OFFSET_X;
+extern GSSAPI_LIB_VARIABLE gss_OID GSS_KRB5_GET_TIME_OFFSET_X;
+extern GSSAPI_LIB_VARIABLE gss_OID GSS_KRB5_PLUGIN_REGISTER_X;
+/* Extensions inquire context */
+extern GSSAPI_LIB_VARIABLE gss_OID GSS_KRB5_GET_TKT_FLAGS_X;
+extern GSSAPI_LIB_VARIABLE gss_OID GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_X;
+extern GSSAPI_LIB_VARIABLE gss_OID GSS_C_PEER_HAS_UPDATED_SPNEGO;
+extern GSSAPI_LIB_VARIABLE gss_OID GSS_KRB5_EXPORT_LUCID_CONTEXT_X;
+extern GSSAPI_LIB_VARIABLE gss_OID GSS_KRB5_EXPORT_LUCID_CONTEXT_V1_X;
+extern GSSAPI_LIB_VARIABLE gss_OID GSS_KRB5_GET_SUBKEY_X;
+extern GSSAPI_LIB_VARIABLE gss_OID GSS_KRB5_GET_INITIATOR_SUBKEY_X;
+extern GSSAPI_LIB_VARIABLE gss_OID GSS_KRB5_GET_ACCEPTOR_SUBKEY_X;
+extern GSSAPI_LIB_VARIABLE gss_OID GSS_KRB5_GET_AUTHTIME_X;
+extern GSSAPI_LIB_VARIABLE gss_OID GSS_KRB5_GET_SERVICE_KEYBLOCK_X;
+/* Extensions creds */
+extern GSSAPI_LIB_VARIABLE gss_OID GSS_KRB5_IMPORT_CRED_X;
+extern GSSAPI_LIB_VARIABLE gss_OID GSS_KRB5_SET_ALLOWABLE_ENCTYPES_X;
+extern GSSAPI_LIB_VARIABLE gss_OID GSS_KRB5_CRED_NO_CI_FLAGS_X;
+
+/*
+ * kerberos mechanism specific functions
+ */
+
+struct krb5_keytab_data;
+struct krb5_ccache_data;
+struct Principal;
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_krb5_ccache_name(OM_uint32 * /*minor_status*/,
+ const char * /*name */,
+ const char ** /*out_name */);
+
+OM_uint32 GSSAPI_LIB_FUNCTION gsskrb5_register_acceptor_identity
+ (const char */*identity*/);
+
+OM_uint32 GSSAPI_LIB_FUNCTION krb5_gss_register_acceptor_identity
+ (const char */*identity*/);
+
+OM_uint32 GSSAPI_LIB_FUNCTION gss_krb5_copy_ccache
+ (OM_uint32 */*minor*/,
+ gss_cred_id_t /*cred*/,
+ struct krb5_ccache_data */*out*/);
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_krb5_import_cred(OM_uint32 */*minor*/,
+ struct krb5_ccache_data * /*in*/,
+ struct Principal * /*keytab_principal*/,
+ struct krb5_keytab_data * /*keytab*/,
+ gss_cred_id_t */*out*/);
+
+OM_uint32 GSSAPI_LIB_FUNCTION gss_krb5_get_tkt_flags
+ (OM_uint32 */*minor*/,
+ gss_ctx_id_t /*context_handle*/,
+ OM_uint32 */*tkt_flags*/);
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gsskrb5_extract_authz_data_from_sec_context
+ (OM_uint32 * /*minor_status*/,
+ gss_ctx_id_t /*context_handle*/,
+ int /*ad_type*/,
+ gss_buffer_t /*ad_data*/);
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gsskrb5_set_dns_canonicalize(int);
+
+struct gsskrb5_send_to_kdc {
+ void *func;
+ void *ptr;
+};
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gsskrb5_set_send_to_kdc(struct gsskrb5_send_to_kdc *)
+ GSSKRB5_FUNCTION_DEPRECATED;
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gsskrb5_set_default_realm(const char *);
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gsskrb5_extract_authtime_from_sec_context(OM_uint32 *, gss_ctx_id_t, time_t *);
+
+struct EncryptionKey;
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gsskrb5_extract_service_keyblock(OM_uint32 *minor_status,
+ gss_ctx_id_t context_handle,
+ struct EncryptionKey **out);
+OM_uint32 GSSAPI_LIB_FUNCTION
+gsskrb5_get_initiator_subkey(OM_uint32 *minor_status,
+ gss_ctx_id_t context_handle,
+ struct EncryptionKey **out);
+OM_uint32 GSSAPI_LIB_FUNCTION
+gsskrb5_get_subkey(OM_uint32 *minor_status,
+ gss_ctx_id_t context_handle,
+ struct EncryptionKey **out);
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gsskrb5_set_time_offset(int);
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gsskrb5_get_time_offset(int *);
+
+struct gsskrb5_krb5_plugin {
+ int type;
+ char *name;
+ void *symbol;
+};
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gsskrb5_plugin_register(struct gsskrb5_krb5_plugin *);
+
+
+/*
+ * Lucid - NFSv4 interface to GSS-API KRB5 to expose key material to
+ * do GSS content token handling in-kernel.
+ */
+
+typedef struct gss_krb5_lucid_key {
+ OM_uint32 type;
+ OM_uint32 length;
+ void * data;
+} gss_krb5_lucid_key_t;
+
+typedef struct gss_krb5_rfc1964_keydata {
+ OM_uint32 sign_alg;
+ OM_uint32 seal_alg;
+ gss_krb5_lucid_key_t ctx_key;
+} gss_krb5_rfc1964_keydata_t;
+
+typedef struct gss_krb5_cfx_keydata {
+ OM_uint32 have_acceptor_subkey;
+ gss_krb5_lucid_key_t ctx_key;
+ gss_krb5_lucid_key_t acceptor_subkey;
+} gss_krb5_cfx_keydata_t;
+
+typedef struct gss_krb5_lucid_context_v1 {
+ OM_uint32 version;
+ OM_uint32 initiate;
+ OM_uint32 endtime;
+ OM_uint64 send_seq;
+ OM_uint64 recv_seq;
+ OM_uint32 protocol;
+ gss_krb5_rfc1964_keydata_t rfc1964_kd;
+ gss_krb5_cfx_keydata_t cfx_kd;
+} gss_krb5_lucid_context_v1_t;
+
+typedef struct gss_krb5_lucid_context_version {
+ OM_uint32 version; /* Structure version number */
+} gss_krb5_lucid_context_version_t;
+
+/*
+ * Function declarations
+ */
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_krb5_export_lucid_sec_context(OM_uint32 *minor_status,
+ gss_ctx_id_t *context_handle,
+ OM_uint32 version,
+ void **kctx);
+
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_krb5_free_lucid_sec_context(OM_uint32 *minor_status,
+ void *kctx);
+
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_krb5_set_allowable_enctypes(OM_uint32 *minor_status,
+ gss_cred_id_t cred,
+ OM_uint32 num_enctypes,
+ int32_t *enctypes);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GSSAPI_SPNEGO_H_ */
diff --git a/source4/heimdal/lib/gssapi/gssapi/gssapi_spnego.h b/source4/heimdal/lib/gssapi/gssapi/gssapi_spnego.h
new file mode 100644
index 0000000000..3c4869f08d
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/gssapi/gssapi_spnego.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 1997 - 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef GSSAPI_SPNEGO_H_
+#define GSSAPI_SPNEGO_H_
+
+#include <gssapi.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * RFC2478, SPNEGO:
+ * The security mechanism of the initial
+ * negotiation token is identified by the Object Identifier
+ * iso.org.dod.internet.security.mechanism.snego (1.3.6.1.5.5.2).
+ */
+extern GSSAPI_LIB_VARIABLE gss_OID GSS_SPNEGO_MECHANISM;
+#define gss_mech_spnego GSS_SPNEGO_MECHANISM
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GSSAPI_SPNEGO_H_ */
diff --git a/source4/heimdal/lib/gssapi/gssapi_mech.h b/source4/heimdal/lib/gssapi/gssapi_mech.h
new file mode 100644
index 0000000000..b360de13fc
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/gssapi_mech.h
@@ -0,0 +1,361 @@
+/*-
+ * Copyright (c) 2005 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libgssapi/mech_switch.h,v 1.1 2005/12/29 14:40:20 dfr Exp $
+ */
+
+#ifndef GSSAPI_MECH_H
+#define GSSAPI_MECH_H 1
+
+#include <gssapi.h>
+
+typedef OM_uint32 _gss_acquire_cred_t
+ (OM_uint32 *, /* minor_status */
+ const gss_name_t, /* desired_name */
+ OM_uint32, /* time_req */
+ const gss_OID_set, /* desired_mechs */
+ gss_cred_usage_t, /* cred_usage */
+ gss_cred_id_t *, /* output_cred_handle */
+ gss_OID_set *, /* actual_mechs */
+ OM_uint32 * /* time_rec */
+ );
+
+typedef OM_uint32 _gss_release_cred_t
+ (OM_uint32 *, /* minor_status */
+ gss_cred_id_t * /* cred_handle */
+ );
+
+typedef OM_uint32 _gss_init_sec_context_t
+ (OM_uint32 *, /* minor_status */
+ const gss_cred_id_t, /* initiator_cred_handle */
+ gss_ctx_id_t *, /* context_handle */
+ const gss_name_t, /* target_name */
+ const gss_OID, /* mech_type */
+ OM_uint32, /* req_flags */
+ OM_uint32, /* time_req */
+ const gss_channel_bindings_t,
+ /* input_chan_bindings */
+ const gss_buffer_t, /* input_token */
+ gss_OID *, /* actual_mech_type */
+ gss_buffer_t, /* output_token */
+ OM_uint32 *, /* ret_flags */
+ OM_uint32 * /* time_rec */
+ );
+
+typedef OM_uint32 _gss_accept_sec_context_t
+ (OM_uint32 *, /* minor_status */
+ gss_ctx_id_t *, /* context_handle */
+ const gss_cred_id_t, /* acceptor_cred_handle */
+ const gss_buffer_t, /* input_token_buffer */
+ const gss_channel_bindings_t,
+ /* input_chan_bindings */
+ gss_name_t *, /* src_name */
+ gss_OID *, /* mech_type */
+ gss_buffer_t, /* output_token */
+ OM_uint32 *, /* ret_flags */
+ OM_uint32 *, /* time_rec */
+ gss_cred_id_t * /* delegated_cred_handle */
+ );
+
+typedef OM_uint32 _gss_process_context_token_t
+ (OM_uint32 *, /* minor_status */
+ const gss_ctx_id_t, /* context_handle */
+ const gss_buffer_t /* token_buffer */
+ );
+
+typedef OM_uint32 _gss_delete_sec_context_t
+ (OM_uint32 *, /* minor_status */
+ gss_ctx_id_t *, /* context_handle */
+ gss_buffer_t /* output_token */
+ );
+
+typedef OM_uint32 _gss_context_time_t
+ (OM_uint32 *, /* minor_status */
+ const gss_ctx_id_t, /* context_handle */
+ OM_uint32 * /* time_rec */
+ );
+
+typedef OM_uint32 _gss_get_mic_t
+ (OM_uint32 *, /* minor_status */
+ const gss_ctx_id_t, /* context_handle */
+ gss_qop_t, /* qop_req */
+ const gss_buffer_t, /* message_buffer */
+ gss_buffer_t /* message_token */
+ );
+
+typedef OM_uint32 _gss_verify_mic_t
+ (OM_uint32 *, /* minor_status */
+ const gss_ctx_id_t, /* context_handle */
+ const gss_buffer_t, /* message_buffer */
+ const gss_buffer_t, /* token_buffer */
+ gss_qop_t * /* qop_state */
+ );
+
+typedef OM_uint32 _gss_wrap_t
+ (OM_uint32 *, /* minor_status */
+ const gss_ctx_id_t, /* context_handle */
+ int, /* conf_req_flag */
+ gss_qop_t, /* qop_req */
+ const gss_buffer_t, /* input_message_buffer */
+ int *, /* conf_state */
+ gss_buffer_t /* output_message_buffer */
+ );
+
+typedef OM_uint32 _gss_unwrap_t
+ (OM_uint32 *, /* minor_status */
+ const gss_ctx_id_t, /* context_handle */
+ const gss_buffer_t, /* input_message_buffer */
+ gss_buffer_t, /* output_message_buffer */
+ int *, /* conf_state */
+ gss_qop_t * /* qop_state */
+ );
+
+typedef OM_uint32 _gss_display_status_t
+ (OM_uint32 *, /* minor_status */
+ OM_uint32, /* status_value */
+ int, /* status_type */
+ const gss_OID, /* mech_type */
+ OM_uint32 *, /* message_context */
+ gss_buffer_t /* status_string */
+ );
+
+typedef OM_uint32 _gss_indicate_mechs_t
+ (OM_uint32 *, /* minor_status */
+ gss_OID_set * /* mech_set */
+ );
+
+typedef OM_uint32 _gss_compare_name_t
+ (OM_uint32 *, /* minor_status */
+ const gss_name_t, /* name1 */
+ const gss_name_t, /* name2 */
+ int * /* name_equal */
+ );
+
+typedef OM_uint32 _gss_display_name_t
+ (OM_uint32 *, /* minor_status */
+ const gss_name_t, /* input_name */
+ gss_buffer_t, /* output_name_buffer */
+ gss_OID * /* output_name_type */
+ );
+
+typedef OM_uint32 _gss_import_name_t
+ (OM_uint32 *, /* minor_status */
+ const gss_buffer_t, /* input_name_buffer */
+ const gss_OID, /* input_name_type */
+ gss_name_t * /* output_name */
+ );
+
+typedef OM_uint32 _gss_export_name_t
+ (OM_uint32 *, /* minor_status */
+ const gss_name_t, /* input_name */
+ gss_buffer_t /* exported_name */
+ );
+
+typedef OM_uint32 _gss_release_name_t
+ (OM_uint32 *, /* minor_status */
+ gss_name_t * /* input_name */
+ );
+
+typedef OM_uint32 _gss_inquire_cred_t
+ (OM_uint32 *, /* minor_status */
+ const gss_cred_id_t, /* cred_handle */
+ gss_name_t *, /* name */
+ OM_uint32 *, /* lifetime */
+ gss_cred_usage_t *, /* cred_usage */
+ gss_OID_set * /* mechanisms */
+ );
+
+typedef OM_uint32 _gss_inquire_context_t
+ (OM_uint32 *, /* minor_status */
+ const gss_ctx_id_t, /* context_handle */
+ gss_name_t *, /* src_name */
+ gss_name_t *, /* targ_name */
+ OM_uint32 *, /* lifetime_rec */
+ gss_OID *, /* mech_type */
+ OM_uint32 *, /* ctx_flags */
+ int *, /* locally_initiated */
+ int * /* open */
+ );
+
+typedef OM_uint32 _gss_wrap_size_limit_t
+ (OM_uint32 *, /* minor_status */
+ const gss_ctx_id_t, /* context_handle */
+ int, /* conf_req_flag */
+ gss_qop_t, /* qop_req */
+ OM_uint32, /* req_output_size */
+ OM_uint32 * /* max_input_size */
+ );
+
+typedef OM_uint32 _gss_add_cred_t (
+ OM_uint32 *, /* minor_status */
+ const gss_cred_id_t, /* input_cred_handle */
+ const gss_name_t, /* desired_name */
+ const gss_OID, /* desired_mech */
+ gss_cred_usage_t, /* cred_usage */
+ OM_uint32, /* initiator_time_req */
+ OM_uint32, /* acceptor_time_req */
+ gss_cred_id_t *, /* output_cred_handle */
+ gss_OID_set *, /* actual_mechs */
+ OM_uint32 *, /* initiator_time_rec */
+ OM_uint32 * /* acceptor_time_rec */
+ );
+
+typedef OM_uint32 _gss_inquire_cred_by_mech_t (
+ OM_uint32 *, /* minor_status */
+ const gss_cred_id_t, /* cred_handle */
+ const gss_OID, /* mech_type */
+ gss_name_t *, /* name */
+ OM_uint32 *, /* initiator_lifetime */
+ OM_uint32 *, /* acceptor_lifetime */
+ gss_cred_usage_t * /* cred_usage */
+ );
+
+typedef OM_uint32 _gss_export_sec_context_t (
+ OM_uint32 *, /* minor_status */
+ gss_ctx_id_t *, /* context_handle */
+ gss_buffer_t /* interprocess_token */
+ );
+
+typedef OM_uint32 _gss_import_sec_context_t (
+ OM_uint32 *, /* minor_status */
+ const gss_buffer_t, /* interprocess_token */
+ gss_ctx_id_t * /* context_handle */
+ );
+
+typedef OM_uint32 _gss_inquire_names_for_mech_t (
+ OM_uint32 *, /* minor_status */
+ const gss_OID, /* mechanism */
+ gss_OID_set * /* name_types */
+ );
+
+typedef OM_uint32 _gss_inquire_mechs_for_name_t (
+ OM_uint32 *, /* minor_status */
+ const gss_name_t, /* input_name */
+ gss_OID_set * /* mech_types */
+ );
+
+typedef OM_uint32 _gss_canonicalize_name_t (
+ OM_uint32 *, /* minor_status */
+ const gss_name_t, /* input_name */
+ const gss_OID, /* mech_type */
+ gss_name_t * /* output_name */
+ );
+
+typedef OM_uint32 _gss_duplicate_name_t (
+ OM_uint32 *, /* minor_status */
+ const gss_name_t, /* src_name */
+ gss_name_t * /* dest_name */
+ );
+
+typedef OM_uint32 _gss_inquire_sec_context_by_oid (
+ OM_uint32 *minor_status,
+ const gss_ctx_id_t context_handle,
+ const gss_OID desired_object,
+ gss_buffer_set_t *data_set
+ );
+
+typedef OM_uint32 _gss_inquire_cred_by_oid (
+ OM_uint32 *minor_status,
+ const gss_cred_id_t cred,
+ const gss_OID desired_object,
+ gss_buffer_set_t *data_set
+ );
+
+typedef OM_uint32 _gss_set_sec_context_option (
+ OM_uint32 *minor_status,
+ gss_ctx_id_t *cred_handle,
+ const gss_OID desired_object,
+ const gss_buffer_t value
+ );
+
+typedef OM_uint32 _gss_set_cred_option (
+ OM_uint32 *minor_status,
+ gss_cred_id_t *cred_handle,
+ const gss_OID desired_object,
+ const gss_buffer_t value
+ );
+
+
+typedef OM_uint32 _gss_pseudo_random(
+ OM_uint32 *minor_status,
+ gss_ctx_id_t context,
+ int prf_key,
+ const gss_buffer_t prf_in,
+ ssize_t desired_output_len,
+ gss_buffer_t prf_out
+ );
+
+#define GMI_VERSION 1
+
+typedef struct gssapi_mech_interface_desc {
+ unsigned gm_version;
+ const char *gm_name;
+ gss_OID_desc gm_mech_oid;
+ _gss_acquire_cred_t *gm_acquire_cred;
+ _gss_release_cred_t *gm_release_cred;
+ _gss_init_sec_context_t *gm_init_sec_context;
+ _gss_accept_sec_context_t *gm_accept_sec_context;
+ _gss_process_context_token_t *gm_process_context_token;
+ _gss_delete_sec_context_t *gm_delete_sec_context;
+ _gss_context_time_t *gm_context_time;
+ _gss_get_mic_t *gm_get_mic;
+ _gss_verify_mic_t *gm_verify_mic;
+ _gss_wrap_t *gm_wrap;
+ _gss_unwrap_t *gm_unwrap;
+ _gss_display_status_t *gm_display_status;
+ _gss_indicate_mechs_t *gm_indicate_mechs;
+ _gss_compare_name_t *gm_compare_name;
+ _gss_display_name_t *gm_display_name;
+ _gss_import_name_t *gm_import_name;
+ _gss_export_name_t *gm_export_name;
+ _gss_release_name_t *gm_release_name;
+ _gss_inquire_cred_t *gm_inquire_cred;
+ _gss_inquire_context_t *gm_inquire_context;
+ _gss_wrap_size_limit_t *gm_wrap_size_limit;
+ _gss_add_cred_t *gm_add_cred;
+ _gss_inquire_cred_by_mech_t *gm_inquire_cred_by_mech;
+ _gss_export_sec_context_t *gm_export_sec_context;
+ _gss_import_sec_context_t *gm_import_sec_context;
+ _gss_inquire_names_for_mech_t *gm_inquire_names_for_mech;
+ _gss_inquire_mechs_for_name_t *gm_inquire_mechs_for_name;
+ _gss_canonicalize_name_t *gm_canonicalize_name;
+ _gss_duplicate_name_t *gm_duplicate_name;
+ _gss_inquire_sec_context_by_oid *gm_inquire_sec_context_by_oid;
+ _gss_inquire_cred_by_oid *gm_inquire_cred_by_oid;
+ _gss_set_sec_context_option *gm_set_sec_context_option;
+ _gss_set_cred_option *gm_set_cred_option;
+ _gss_pseudo_random *gm_pseudo_random;
+} gssapi_mech_interface_desc, *gssapi_mech_interface;
+
+gssapi_mech_interface
+__gss_get_mechanism(gss_OID /* oid */);
+
+gssapi_mech_interface __gss_spnego_initialize(void);
+gssapi_mech_interface __gss_krb5_initialize(void);
+gssapi_mech_interface __gss_ntlm_initialize(void);
+
+void gss_mg_collect_error(gss_OID, OM_uint32, OM_uint32);
+
+#endif /* GSSAPI_MECH_H */
diff --git a/source4/heimdal/lib/gssapi/krb5/8003.c b/source4/heimdal/lib/gssapi/krb5/8003.c
new file mode 100644
index 0000000000..119d49a0c5
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/krb5/8003.c
@@ -0,0 +1,248 @@
+/*
+ * Copyright (c) 1997 - 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5/gsskrb5_locl.h"
+
+RCSID("$Id$");
+
+krb5_error_code
+_gsskrb5_encode_om_uint32(OM_uint32 n, u_char *p)
+{
+ p[0] = (n >> 0) & 0xFF;
+ p[1] = (n >> 8) & 0xFF;
+ p[2] = (n >> 16) & 0xFF;
+ p[3] = (n >> 24) & 0xFF;
+ return 0;
+}
+
+krb5_error_code
+_gsskrb5_encode_be_om_uint32(OM_uint32 n, u_char *p)
+{
+ p[0] = (n >> 24) & 0xFF;
+ p[1] = (n >> 16) & 0xFF;
+ p[2] = (n >> 8) & 0xFF;
+ p[3] = (n >> 0) & 0xFF;
+ return 0;
+}
+
+krb5_error_code
+_gsskrb5_decode_om_uint32(const void *ptr, OM_uint32 *n)
+{
+ const u_char *p = ptr;
+ *n = (p[0] << 0) | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
+ return 0;
+}
+
+krb5_error_code
+_gsskrb5_decode_be_om_uint32(const void *ptr, OM_uint32 *n)
+{
+ const u_char *p = ptr;
+ *n = (p[0] <<24) | (p[1] << 16) | (p[2] << 8) | (p[3] << 0);
+ return 0;
+}
+
+static krb5_error_code
+hash_input_chan_bindings (const gss_channel_bindings_t b,
+ u_char *p)
+{
+ u_char num[4];
+ MD5_CTX md5;
+
+ MD5_Init(&md5);
+ _gsskrb5_encode_om_uint32 (b->initiator_addrtype, num);
+ MD5_Update (&md5, num, sizeof(num));
+ _gsskrb5_encode_om_uint32 (b->initiator_address.length, num);
+ MD5_Update (&md5, num, sizeof(num));
+ if (b->initiator_address.length)
+ MD5_Update (&md5,
+ b->initiator_address.value,
+ b->initiator_address.length);
+ _gsskrb5_encode_om_uint32 (b->acceptor_addrtype, num);
+ MD5_Update (&md5, num, sizeof(num));
+ _gsskrb5_encode_om_uint32 (b->acceptor_address.length, num);
+ MD5_Update (&md5, num, sizeof(num));
+ if (b->acceptor_address.length)
+ MD5_Update (&md5,
+ b->acceptor_address.value,
+ b->acceptor_address.length);
+ _gsskrb5_encode_om_uint32 (b->application_data.length, num);
+ MD5_Update (&md5, num, sizeof(num));
+ if (b->application_data.length)
+ MD5_Update (&md5,
+ b->application_data.value,
+ b->application_data.length);
+ MD5_Final (p, &md5);
+ return 0;
+}
+
+/*
+ * create a checksum over the chanel bindings in
+ * `input_chan_bindings', `flags' and `fwd_data' and return it in
+ * `result'
+ */
+
+OM_uint32
+_gsskrb5_create_8003_checksum (
+ OM_uint32 *minor_status,
+ const gss_channel_bindings_t input_chan_bindings,
+ OM_uint32 flags,
+ const krb5_data *fwd_data,
+ Checksum *result)
+{
+ u_char *p;
+
+ /*
+ * see rfc1964 (section 1.1.1 (Initial Token), and the checksum value
+ * field's format) */
+ result->cksumtype = CKSUMTYPE_GSSAPI;
+ if (fwd_data->length > 0 && (flags & GSS_C_DELEG_FLAG))
+ result->checksum.length = 24 + 4 + fwd_data->length;
+ else
+ result->checksum.length = 24;
+ result->checksum.data = malloc (result->checksum.length);
+ if (result->checksum.data == NULL) {
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+
+ p = result->checksum.data;
+ _gsskrb5_encode_om_uint32 (16, p);
+ p += 4;
+ if (input_chan_bindings == GSS_C_NO_CHANNEL_BINDINGS) {
+ memset (p, 0, 16);
+ } else {
+ hash_input_chan_bindings (input_chan_bindings, p);
+ }
+ p += 16;
+ _gsskrb5_encode_om_uint32 (flags, p);
+ p += 4;
+
+ if (fwd_data->length > 0 && (flags & GSS_C_DELEG_FLAG)) {
+
+ *p++ = (1 >> 0) & 0xFF; /* DlgOpt */ /* == 1 */
+ *p++ = (1 >> 8) & 0xFF; /* DlgOpt */ /* == 0 */
+ *p++ = (fwd_data->length >> 0) & 0xFF; /* Dlgth */
+ *p++ = (fwd_data->length >> 8) & 0xFF; /* Dlgth */
+ memcpy(p, (unsigned char *) fwd_data->data, fwd_data->length);
+
+ p += fwd_data->length;
+ }
+
+ return GSS_S_COMPLETE;
+}
+
+/*
+ * verify the checksum in `cksum' over `input_chan_bindings'
+ * returning `flags' and `fwd_data'
+ */
+
+OM_uint32
+_gsskrb5_verify_8003_checksum(
+ OM_uint32 *minor_status,
+ const gss_channel_bindings_t input_chan_bindings,
+ const Checksum *cksum,
+ OM_uint32 *flags,
+ krb5_data *fwd_data)
+{
+ unsigned char hash[16];
+ unsigned char *p;
+ OM_uint32 length;
+ int DlgOpt;
+ static unsigned char zeros[16];
+
+ if (cksum == NULL) {
+ *minor_status = 0;
+ return GSS_S_BAD_BINDINGS;
+ }
+
+ /* XXX should handle checksums > 24 bytes */
+ if(cksum->cksumtype != CKSUMTYPE_GSSAPI || cksum->checksum.length < 24) {
+ *minor_status = 0;
+ return GSS_S_BAD_BINDINGS;
+ }
+
+ p = cksum->checksum.data;
+ _gsskrb5_decode_om_uint32(p, &length);
+ if(length != sizeof(hash)) {
+ *minor_status = 0;
+ return GSS_S_BAD_BINDINGS;
+ }
+
+ p += 4;
+
+ if (input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS
+ && memcmp(p, zeros, sizeof(zeros)) != 0) {
+ if(hash_input_chan_bindings(input_chan_bindings, hash) != 0) {
+ *minor_status = 0;
+ return GSS_S_BAD_BINDINGS;
+ }
+ if(memcmp(hash, p, sizeof(hash)) != 0) {
+ *minor_status = 0;
+ return GSS_S_BAD_BINDINGS;
+ }
+ }
+
+ p += sizeof(hash);
+
+ _gsskrb5_decode_om_uint32(p, flags);
+ p += 4;
+
+ if (cksum->checksum.length > 24 && (*flags & GSS_C_DELEG_FLAG)) {
+ if(cksum->checksum.length < 28) {
+ *minor_status = 0;
+ return GSS_S_BAD_BINDINGS;
+ }
+
+ DlgOpt = (p[0] << 0) | (p[1] << 8);
+ p += 2;
+ if (DlgOpt != 1) {
+ *minor_status = 0;
+ return GSS_S_BAD_BINDINGS;
+ }
+
+ fwd_data->length = (p[0] << 0) | (p[1] << 8);
+ p += 2;
+ if(cksum->checksum.length < 28 + fwd_data->length) {
+ *minor_status = 0;
+ return GSS_S_BAD_BINDINGS;
+ }
+ fwd_data->data = malloc(fwd_data->length);
+ if (fwd_data->data == NULL) {
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+ memcpy(fwd_data->data, p, fwd_data->length);
+ }
+
+ return GSS_S_COMPLETE;
+}
diff --git a/source4/heimdal/lib/gssapi/krb5/accept_sec_context.c b/source4/heimdal/lib/gssapi/krb5/accept_sec_context.c
new file mode 100644
index 0000000000..626afa9384
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/krb5/accept_sec_context.c
@@ -0,0 +1,888 @@
+/*
+ * Copyright (c) 1997 - 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5/gsskrb5_locl.h"
+
+RCSID("$Id$");
+
+HEIMDAL_MUTEX gssapi_keytab_mutex = HEIMDAL_MUTEX_INITIALIZER;
+krb5_keytab _gsskrb5_keytab;
+
+OM_uint32
+_gsskrb5_register_acceptor_identity (const char *identity)
+{
+ krb5_context context;
+ krb5_error_code ret;
+
+ ret = _gsskrb5_init(&context);
+ if(ret)
+ return GSS_S_FAILURE;
+
+ HEIMDAL_MUTEX_lock(&gssapi_keytab_mutex);
+
+ if(_gsskrb5_keytab != NULL) {
+ krb5_kt_close(context, _gsskrb5_keytab);
+ _gsskrb5_keytab = NULL;
+ }
+ if (identity == NULL) {
+ ret = krb5_kt_default(context, &_gsskrb5_keytab);
+ } else {
+ char *p;
+
+ asprintf(&p, "FILE:%s", identity);
+ if(p == NULL) {
+ HEIMDAL_MUTEX_unlock(&gssapi_keytab_mutex);
+ return GSS_S_FAILURE;
+ }
+ ret = krb5_kt_resolve(context, p, &_gsskrb5_keytab);
+ free(p);
+ }
+ HEIMDAL_MUTEX_unlock(&gssapi_keytab_mutex);
+ if(ret)
+ return GSS_S_FAILURE;
+ return GSS_S_COMPLETE;
+}
+
+void
+_gsskrb5i_is_cfx(gsskrb5_ctx ctx, int *is_cfx)
+{
+ krb5_keyblock *key;
+ int acceptor = (ctx->more_flags & LOCAL) == 0;
+
+ *is_cfx = 0;
+
+ if (acceptor) {
+ if (ctx->auth_context->local_subkey)
+ key = ctx->auth_context->local_subkey;
+ else
+ key = ctx->auth_context->remote_subkey;
+ } else {
+ if (ctx->auth_context->remote_subkey)
+ key = ctx->auth_context->remote_subkey;
+ else
+ key = ctx->auth_context->local_subkey;
+ }
+ if (key == NULL)
+ key = ctx->auth_context->keyblock;
+
+ if (key == NULL)
+ return;
+
+ switch (key->keytype) {
+ case ETYPE_DES_CBC_CRC:
+ case ETYPE_DES_CBC_MD4:
+ case ETYPE_DES_CBC_MD5:
+ case ETYPE_DES3_CBC_MD5:
+ case ETYPE_DES3_CBC_SHA1:
+ case ETYPE_ARCFOUR_HMAC_MD5:
+ case ETYPE_ARCFOUR_HMAC_MD5_56:
+ break;
+ default :
+ *is_cfx = 1;
+ if ((acceptor && ctx->auth_context->local_subkey) ||
+ (!acceptor && ctx->auth_context->remote_subkey))
+ ctx->more_flags |= ACCEPTOR_SUBKEY;
+ break;
+ }
+}
+
+
+static OM_uint32
+gsskrb5_accept_delegated_token
+(OM_uint32 * minor_status,
+ gsskrb5_ctx ctx,
+ krb5_context context,
+ gss_cred_id_t * delegated_cred_handle
+ )
+{
+ krb5_ccache ccache = NULL;
+ krb5_error_code kret;
+ int32_t ac_flags, ret = GSS_S_COMPLETE;
+
+ *minor_status = 0;
+
+ /* XXX Create a new delegated_cred_handle? */
+ if (delegated_cred_handle == NULL) {
+ kret = krb5_cc_default (context, &ccache);
+ } else {
+ *delegated_cred_handle = NULL;
+ kret = krb5_cc_gen_new (context, &krb5_mcc_ops, &ccache);
+ }
+ if (kret) {
+ ctx->flags &= ~GSS_C_DELEG_FLAG;
+ goto out;
+ }
+
+ kret = krb5_cc_initialize(context, ccache, ctx->source);
+ if (kret) {
+ ctx->flags &= ~GSS_C_DELEG_FLAG;
+ goto out;
+ }
+
+ krb5_auth_con_removeflags(context,
+ ctx->auth_context,
+ KRB5_AUTH_CONTEXT_DO_TIME,
+ &ac_flags);
+ kret = krb5_rd_cred2(context,
+ ctx->auth_context,
+ ccache,
+ &ctx->fwd_data);
+ krb5_auth_con_setflags(context,
+ ctx->auth_context,
+ ac_flags);
+ if (kret) {
+ ctx->flags &= ~GSS_C_DELEG_FLAG;
+ ret = GSS_S_FAILURE;
+ *minor_status = kret;
+ goto out;
+ }
+
+ if (delegated_cred_handle) {
+ gsskrb5_cred handle;
+
+ ret = _gsskrb5_import_cred(minor_status,
+ ccache,
+ NULL,
+ NULL,
+ delegated_cred_handle);
+ if (ret != GSS_S_COMPLETE)
+ goto out;
+
+ handle = (gsskrb5_cred) *delegated_cred_handle;
+
+ handle->cred_flags |= GSS_CF_DESTROY_CRED_ON_RELEASE;
+ krb5_cc_close(context, ccache);
+ ccache = NULL;
+ }
+
+out:
+ if (ccache) {
+ /* Don't destroy the default cred cache */
+ if (delegated_cred_handle == NULL)
+ krb5_cc_close(context, ccache);
+ else
+ krb5_cc_destroy(context, ccache);
+ }
+ return ret;
+}
+
+static OM_uint32
+gsskrb5_acceptor_ready(OM_uint32 * minor_status,
+ gsskrb5_ctx ctx,
+ krb5_context context,
+ gss_cred_id_t *delegated_cred_handle)
+{
+ OM_uint32 ret;
+ int32_t seq_number;
+ int is_cfx = 0;
+
+ krb5_auth_getremoteseqnumber (context,
+ ctx->auth_context,
+ &seq_number);
+
+ _gsskrb5i_is_cfx(ctx, &is_cfx);
+
+ ret = _gssapi_msg_order_create(minor_status,
+ &ctx->order,
+ _gssapi_msg_order_f(ctx->flags),
+ seq_number, 0, is_cfx);
+ if (ret)
+ return ret;
+
+ /*
+ * If requested, set local sequence num to remote sequence if this
+ * isn't a mutual authentication context
+ */
+ if (!(ctx->flags & GSS_C_MUTUAL_FLAG) && _gssapi_msg_order_f(ctx->flags)) {
+ krb5_auth_con_setlocalseqnumber(context,
+ ctx->auth_context,
+ seq_number);
+ }
+
+ /*
+ * We should handle the delegation ticket, in case it's there
+ */
+ if (ctx->fwd_data.length > 0 && (ctx->flags & GSS_C_DELEG_FLAG)) {
+ ret = gsskrb5_accept_delegated_token(minor_status,
+ ctx,
+ context,
+ delegated_cred_handle);
+ if (ret)
+ return ret;
+ } else {
+ /* Well, looks like it wasn't there after all */
+ ctx->flags &= ~GSS_C_DELEG_FLAG;
+ }
+
+ ctx->state = ACCEPTOR_READY;
+ ctx->more_flags |= OPEN;
+
+ return GSS_S_COMPLETE;
+}
+
+static OM_uint32
+send_error_token(OM_uint32 *minor_status,
+ krb5_context context,
+ krb5_error_code kret,
+ krb5_principal server,
+ krb5_data *indata,
+ gss_buffer_t output_token)
+{
+ krb5_principal ap_req_server = NULL;
+ krb5_error_code ret;
+ krb5_data outbuf;
+
+ /* build server from request if the acceptor had not selected one */
+ if (server == NULL) {
+ AP_REQ ap_req;
+
+ ret = krb5_decode_ap_req(context, indata, &ap_req);
+ if (ret) {
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+ ret = _krb5_principalname2krb5_principal(context,
+ &ap_req_server,
+ ap_req.ticket.sname,
+ ap_req.ticket.realm);
+ free_AP_REQ(&ap_req);
+ if (ret) {
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+ server = ap_req_server;
+ }
+
+ ret = krb5_mk_error(context, kret, NULL, NULL, NULL,
+ server, NULL, NULL, &outbuf);
+ if (ap_req_server)
+ krb5_free_principal(context, ap_req_server);
+ if (ret) {
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+
+ ret = _gsskrb5_encapsulate(minor_status,
+ &outbuf,
+ output_token,
+ "\x03\x00",
+ GSS_KRB5_MECHANISM);
+ krb5_data_free (&outbuf);
+ if (ret)
+ return ret;
+
+ *minor_status = 0;
+ return GSS_S_CONTINUE_NEEDED;
+}
+
+
+static OM_uint32
+gsskrb5_acceptor_start(OM_uint32 * minor_status,
+ gsskrb5_ctx ctx,
+ krb5_context context,
+ const gss_cred_id_t acceptor_cred_handle,
+ const gss_buffer_t input_token_buffer,
+ const gss_channel_bindings_t input_chan_bindings,
+ gss_name_t * src_name,
+ gss_OID * mech_type,
+ gss_buffer_t output_token,
+ OM_uint32 * ret_flags,
+ OM_uint32 * time_rec,
+ gss_cred_id_t * delegated_cred_handle)
+{
+ krb5_error_code kret;
+ OM_uint32 ret = GSS_S_COMPLETE;
+ krb5_data indata;
+ krb5_flags ap_options;
+ krb5_keytab keytab = NULL;
+ int is_cfx = 0;
+ const gsskrb5_cred acceptor_cred = (gsskrb5_cred)acceptor_cred_handle;
+
+ /*
+ * We may, or may not, have an escapsulation.
+ */
+ ret = _gsskrb5_decapsulate (minor_status,
+ input_token_buffer,
+ &indata,
+ "\x01\x00",
+ GSS_KRB5_MECHANISM);
+
+ if (ret) {
+ /* Assume that there is no OID wrapping. */
+ indata.length = input_token_buffer->length;
+ indata.data = input_token_buffer->value;
+ }
+
+ /*
+ * We need to get our keytab
+ */
+ if (acceptor_cred == NULL) {
+ if (_gsskrb5_keytab != NULL)
+ keytab = _gsskrb5_keytab;
+ } else if (acceptor_cred->keytab != NULL) {
+ keytab = acceptor_cred->keytab;
+ }
+
+ /*
+ * We need to check the ticket and create the AP-REP packet
+ */
+
+ {
+ krb5_rd_req_in_ctx in = NULL;
+ krb5_rd_req_out_ctx out = NULL;
+ krb5_principal server = NULL;
+
+ if (acceptor_cred)
+ server = acceptor_cred->principal;
+
+ kret = krb5_rd_req_in_ctx_alloc(context, &in);
+ if (kret == 0)
+ kret = krb5_rd_req_in_set_keytab(context, in, keytab);
+ if (kret) {
+ if (in)
+ krb5_rd_req_in_ctx_free(context, in);
+ *minor_status = kret;
+ return GSS_S_FAILURE;
+ }
+
+ kret = krb5_rd_req_ctx(context,
+ &ctx->auth_context,
+ &indata,
+ server,
+ in, &out);
+ krb5_rd_req_in_ctx_free(context, in);
+ if (kret == KRB5KRB_AP_ERR_SKEW) {
+ /*
+ * No reply in non-MUTUAL mode, but we don't know that its
+ * non-MUTUAL mode yet, thats inside the 8003 checksum, so
+ * lets only send the error token on clock skew, that
+ * limit when send error token for non-MUTUAL.
+ */
+ return send_error_token(minor_status, context, kret,
+ server, &indata, output_token);
+ } else if (kret) {
+ *minor_status = kret;
+ return GSS_S_FAILURE;
+ }
+
+ /*
+ * we need to remember some data on the context_handle.
+ */
+ kret = krb5_rd_req_out_get_ap_req_options(context, out,
+ &ap_options);
+ if (kret == 0)
+ kret = krb5_rd_req_out_get_ticket(context, out,
+ &ctx->ticket);
+ if (kret == 0)
+ kret = krb5_rd_req_out_get_keyblock(context, out,
+ &ctx->service_keyblock);
+ ctx->lifetime = ctx->ticket->ticket.endtime;
+
+ krb5_rd_req_out_ctx_free(context, out);
+ if (kret) {
+ ret = GSS_S_FAILURE;
+ *minor_status = kret;
+ return ret;
+ }
+ }
+
+
+ /*
+ * We need to copy the principal names to the context and the
+ * calling layer.
+ */
+ kret = krb5_copy_principal(context,
+ ctx->ticket->client,
+ &ctx->source);
+ if (kret) {
+ ret = GSS_S_FAILURE;
+ *minor_status = kret;
+ }
+
+ kret = krb5_copy_principal(context,
+ ctx->ticket->server,
+ &ctx->target);
+ if (kret) {
+ ret = GSS_S_FAILURE;
+ *minor_status = kret;
+ return ret;
+ }
+
+ /*
+ * We need to setup some compat stuff, this assumes that
+ * context_handle->target is already set.
+ */
+ ret = _gss_DES3_get_mic_compat(minor_status, ctx, context);
+ if (ret)
+ return ret;
+
+ if (src_name != NULL) {
+ kret = krb5_copy_principal (context,
+ ctx->ticket->client,
+ (gsskrb5_name*)src_name);
+ if (kret) {
+ ret = GSS_S_FAILURE;
+ *minor_status = kret;
+ return ret;
+ }
+ }
+
+ /*
+ * We need to get the flags out of the 8003 checksum.
+ */
+ {
+ krb5_authenticator authenticator;
+
+ kret = krb5_auth_con_getauthenticator(context,
+ ctx->auth_context,
+ &authenticator);
+ if(kret) {
+ ret = GSS_S_FAILURE;
+ *minor_status = kret;
+ return ret;
+ }
+
+ if (authenticator->cksum->cksumtype == CKSUMTYPE_GSSAPI) {
+ ret = _gsskrb5_verify_8003_checksum(minor_status,
+ input_chan_bindings,
+ authenticator->cksum,
+ &ctx->flags,
+ &ctx->fwd_data);
+
+ krb5_free_authenticator(context, &authenticator);
+ if (ret) {
+ return ret;
+ }
+ } else {
+ krb5_crypto crypto;
+
+ kret = krb5_crypto_init(context,
+ ctx->auth_context->keyblock,
+ 0, &crypto);
+ if(kret) {
+ krb5_free_authenticator(context, &authenticator);
+
+ ret = GSS_S_FAILURE;
+ *minor_status = kret;
+ return ret;
+ }
+
+ /*
+ * Windows accepts Samba3's use of a kerberos, rather than
+ * GSSAPI checksum here
+ */
+
+ kret = krb5_verify_checksum(context,
+ crypto, KRB5_KU_AP_REQ_AUTH_CKSUM, NULL, 0,
+ authenticator->cksum);
+ krb5_free_authenticator(context, &authenticator);
+ krb5_crypto_destroy(context, crypto);
+
+ if(kret) {
+ ret = GSS_S_BAD_SIG;
+ *minor_status = kret;
+ return ret;
+ }
+
+ /*
+ * Samba style get some flags (but not DCE-STYLE)
+ */
+ ctx->flags =
+ GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG;
+ }
+ }
+
+ if(ctx->flags & GSS_C_MUTUAL_FLAG) {
+ krb5_data outbuf;
+ int use_subkey = 0;
+
+ _gsskrb5i_is_cfx(ctx, &is_cfx);
+
+ if (is_cfx || (ap_options & AP_OPTS_USE_SUBKEY)) {
+ use_subkey = 1;
+ } else {
+ krb5_keyblock *rkey;
+
+ /*
+ * If there is a initiator subkey, copy that to acceptor
+ * subkey to match Windows behavior
+ */
+ kret = krb5_auth_con_getremotesubkey(context,
+ ctx->auth_context,
+ &rkey);
+ if (kret == 0) {
+ kret = krb5_auth_con_setlocalsubkey(context,
+ ctx->auth_context,
+ rkey);
+ if (kret == 0)
+ use_subkey = 1;
+ krb5_free_keyblock(context, rkey);
+ }
+ }
+ if (use_subkey) {
+ ctx->more_flags |= ACCEPTOR_SUBKEY;
+ krb5_auth_con_addflags(context, ctx->auth_context,
+ KRB5_AUTH_CONTEXT_USE_SUBKEY,
+ NULL);
+ }
+
+ kret = krb5_mk_rep(context,
+ ctx->auth_context,
+ &outbuf);
+ if (kret) {
+ *minor_status = kret;
+ return GSS_S_FAILURE;
+ }
+
+ if (IS_DCE_STYLE(ctx)) {
+ output_token->length = outbuf.length;
+ output_token->value = outbuf.data;
+ } else {
+ ret = _gsskrb5_encapsulate(minor_status,
+ &outbuf,
+ output_token,
+ "\x02\x00",
+ GSS_KRB5_MECHANISM);
+ krb5_data_free (&outbuf);
+ if (ret)
+ return ret;
+ }
+ }
+
+ ctx->flags |= GSS_C_TRANS_FLAG;
+
+ /* Remember the flags */
+
+ ctx->lifetime = ctx->ticket->ticket.endtime;
+ ctx->more_flags |= OPEN;
+
+ if (mech_type)
+ *mech_type = GSS_KRB5_MECHANISM;
+
+ if (time_rec) {
+ ret = _gsskrb5_lifetime_left(minor_status,
+ context,
+ ctx->lifetime,
+ time_rec);
+ if (ret) {
+ return ret;
+ }
+ }
+
+ /*
+ * When GSS_C_DCE_STYLE is in use, we need ask for a AP-REP from
+ * the client.
+ */
+ if (IS_DCE_STYLE(ctx)) {
+ /*
+ * Return flags to caller, but we haven't processed
+ * delgations yet
+ */
+ if (ret_flags)
+ *ret_flags = (ctx->flags & ~GSS_C_DELEG_FLAG);
+
+ ctx->state = ACCEPTOR_WAIT_FOR_DCESTYLE;
+ return GSS_S_CONTINUE_NEEDED;
+ }
+
+ ret = gsskrb5_acceptor_ready(minor_status, ctx, context,
+ delegated_cred_handle);
+
+ if (ret_flags)
+ *ret_flags = ctx->flags;
+
+ return ret;
+}
+
+static OM_uint32
+acceptor_wait_for_dcestyle(OM_uint32 * minor_status,
+ gsskrb5_ctx ctx,
+ krb5_context context,
+ const gss_cred_id_t acceptor_cred_handle,
+ const gss_buffer_t input_token_buffer,
+ const gss_channel_bindings_t input_chan_bindings,
+ gss_name_t * src_name,
+ gss_OID * mech_type,
+ gss_buffer_t output_token,
+ OM_uint32 * ret_flags,
+ OM_uint32 * time_rec,
+ gss_cred_id_t * delegated_cred_handle)
+{
+ OM_uint32 ret;
+ krb5_error_code kret;
+ krb5_data inbuf;
+ int32_t r_seq_number, l_seq_number;
+
+ /*
+ * We know it's GSS_C_DCE_STYLE so we don't need to decapsulate the AP_REP
+ */
+
+ inbuf.length = input_token_buffer->length;
+ inbuf.data = input_token_buffer->value;
+
+ /*
+ * We need to remeber the old remote seq_number, then check if the
+ * client has replied with our local seq_number, and then reset
+ * the remote seq_number to the old value
+ */
+ {
+ kret = krb5_auth_con_getlocalseqnumber(context,
+ ctx->auth_context,
+ &l_seq_number);
+ if (kret) {
+ *minor_status = kret;
+ return GSS_S_FAILURE;
+ }
+
+ kret = krb5_auth_getremoteseqnumber(context,
+ ctx->auth_context,
+ &r_seq_number);
+ if (kret) {
+ *minor_status = kret;
+ return GSS_S_FAILURE;
+ }
+
+ kret = krb5_auth_con_setremoteseqnumber(context,
+ ctx->auth_context,
+ l_seq_number);
+ if (kret) {
+ *minor_status = kret;
+ return GSS_S_FAILURE;
+ }
+ }
+
+ /*
+ * We need to verify the AP_REP, but we need to flag that this is
+ * DCE_STYLE, so don't check the timestamps this time, but put the
+ * flag DO_TIME back afterward.
+ */
+ {
+ krb5_ap_rep_enc_part *repl;
+ int32_t auth_flags;
+
+ krb5_auth_con_removeflags(context,
+ ctx->auth_context,
+ KRB5_AUTH_CONTEXT_DO_TIME,
+ &auth_flags);
+
+ kret = krb5_rd_rep(context, ctx->auth_context, &inbuf, &repl);
+ if (kret) {
+ *minor_status = kret;
+ return GSS_S_FAILURE;
+ }
+ krb5_free_ap_rep_enc_part(context, repl);
+ krb5_auth_con_setflags(context, ctx->auth_context, auth_flags);
+ }
+
+ /* We need to check the liftime */
+ {
+ OM_uint32 lifetime_rec;
+
+ ret = _gsskrb5_lifetime_left(minor_status,
+ context,
+ ctx->lifetime,
+ &lifetime_rec);
+ if (ret) {
+ return ret;
+ }
+ if (lifetime_rec == 0) {
+ return GSS_S_CONTEXT_EXPIRED;
+ }
+
+ if (time_rec) *time_rec = lifetime_rec;
+ }
+
+ /* We need to give the caller the flags which are in use */
+ if (ret_flags) *ret_flags = ctx->flags;
+
+ if (src_name) {
+ kret = krb5_copy_principal(context,
+ ctx->source,
+ (gsskrb5_name*)src_name);
+ if (kret) {
+ *minor_status = kret;
+ return GSS_S_FAILURE;
+ }
+ }
+
+ /*
+ * After the krb5_rd_rep() the remote and local seq_number should
+ * be the same, because the client just replies the seq_number
+ * from our AP-REP in its AP-REP, but then the client uses the
+ * seq_number from its AP-REQ for GSS_wrap()
+ */
+ {
+ int32_t tmp_r_seq_number, tmp_l_seq_number;
+
+ kret = krb5_auth_getremoteseqnumber(context,
+ ctx->auth_context,
+ &tmp_r_seq_number);
+ if (kret) {
+ *minor_status = kret;
+ return GSS_S_FAILURE;
+ }
+
+ kret = krb5_auth_con_getlocalseqnumber(context,
+ ctx->auth_context,
+ &tmp_l_seq_number);
+ if (kret) {
+
+ *minor_status = kret;
+ return GSS_S_FAILURE;
+ }
+
+ /*
+ * Here we check if the client has responsed with our local seq_number,
+ */
+ if (tmp_r_seq_number != tmp_l_seq_number) {
+ return GSS_S_UNSEQ_TOKEN;
+ }
+ }
+
+ /*
+ * We need to reset the remote seq_number, because the client will use,
+ * the old one for the GSS_wrap() calls
+ */
+ {
+ kret = krb5_auth_con_setremoteseqnumber(context,
+ ctx->auth_context,
+ r_seq_number);
+ if (kret) {
+ *minor_status = kret;
+ return GSS_S_FAILURE;
+ }
+ }
+
+ return gsskrb5_acceptor_ready(minor_status, ctx, context,
+ delegated_cred_handle);
+}
+
+
+OM_uint32
+_gsskrb5_accept_sec_context(OM_uint32 * minor_status,
+ gss_ctx_id_t * context_handle,
+ const gss_cred_id_t acceptor_cred_handle,
+ const gss_buffer_t input_token_buffer,
+ const gss_channel_bindings_t input_chan_bindings,
+ gss_name_t * src_name,
+ gss_OID * mech_type,
+ gss_buffer_t output_token,
+ OM_uint32 * ret_flags,
+ OM_uint32 * time_rec,
+ gss_cred_id_t * delegated_cred_handle)
+{
+ krb5_context context;
+ OM_uint32 ret;
+ gsskrb5_ctx ctx;
+
+ GSSAPI_KRB5_INIT(&context);
+
+ output_token->length = 0;
+ output_token->value = NULL;
+
+ if (src_name != NULL)
+ *src_name = NULL;
+ if (mech_type)
+ *mech_type = GSS_KRB5_MECHANISM;
+
+ if (*context_handle == GSS_C_NO_CONTEXT) {
+ ret = _gsskrb5_create_ctx(minor_status,
+ context_handle,
+ context,
+ input_chan_bindings,
+ ACCEPTOR_START);
+ if (ret)
+ return ret;
+ }
+
+ ctx = (gsskrb5_ctx)*context_handle;
+
+
+ /*
+ * TODO: check the channel_bindings
+ * (above just sets them to krb5 layer)
+ */
+
+ HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
+
+ switch (ctx->state) {
+ case ACCEPTOR_START:
+ ret = gsskrb5_acceptor_start(minor_status,
+ ctx,
+ context,
+ acceptor_cred_handle,
+ input_token_buffer,
+ input_chan_bindings,
+ src_name,
+ mech_type,
+ output_token,
+ ret_flags,
+ time_rec,
+ delegated_cred_handle);
+ break;
+ case ACCEPTOR_WAIT_FOR_DCESTYLE:
+ ret = acceptor_wait_for_dcestyle(minor_status,
+ ctx,
+ context,
+ acceptor_cred_handle,
+ input_token_buffer,
+ input_chan_bindings,
+ src_name,
+ mech_type,
+ output_token,
+ ret_flags,
+ time_rec,
+ delegated_cred_handle);
+ break;
+ case ACCEPTOR_READY:
+ /*
+ * If we get there, the caller have called
+ * gss_accept_sec_context() one time too many.
+ */
+ ret = GSS_S_BAD_STATUS;
+ break;
+ default:
+ /* TODO: is this correct here? --metze */
+ ret = GSS_S_BAD_STATUS;
+ break;
+ }
+
+ HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
+
+ if (GSS_ERROR(ret)) {
+ OM_uint32 min2;
+ _gsskrb5_delete_sec_context(&min2, context_handle, GSS_C_NO_BUFFER);
+ }
+
+ return ret;
+}
diff --git a/source4/heimdal/lib/gssapi/krb5/acquire_cred.c b/source4/heimdal/lib/gssapi/krb5/acquire_cred.c
new file mode 100644
index 0000000000..be680840f5
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/krb5/acquire_cred.c
@@ -0,0 +1,402 @@
+/*
+ * Copyright (c) 1997 - 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5/gsskrb5_locl.h"
+
+RCSID("$Id$");
+
+OM_uint32
+__gsskrb5_ccache_lifetime(OM_uint32 *minor_status,
+ krb5_context context,
+ krb5_ccache id,
+ krb5_principal principal,
+ OM_uint32 *lifetime)
+{
+ krb5_creds in_cred, *out_cred;
+ krb5_const_realm realm;
+ krb5_error_code kret;
+
+ memset(&in_cred, 0, sizeof(in_cred));
+ in_cred.client = principal;
+
+ realm = krb5_principal_get_realm(context, principal);
+ if (realm == NULL) {
+ _gsskrb5_clear_status ();
+ *minor_status = KRB5_PRINC_NOMATCH; /* XXX */
+ return GSS_S_FAILURE;
+ }
+
+ kret = krb5_make_principal(context, &in_cred.server,
+ realm, KRB5_TGS_NAME, realm, NULL);
+ if (kret) {
+ *minor_status = kret;
+ return GSS_S_FAILURE;
+ }
+
+ kret = krb5_get_credentials(context, 0,
+ id, &in_cred, &out_cred);
+ krb5_free_principal(context, in_cred.server);
+ if (kret) {
+ *minor_status = kret;
+ return GSS_S_FAILURE;
+ }
+
+ *lifetime = out_cred->times.endtime;
+ krb5_free_creds(context, out_cred);
+
+ return GSS_S_COMPLETE;
+}
+
+
+
+
+static krb5_error_code
+get_keytab(krb5_context context, krb5_keytab *keytab)
+{
+ char kt_name[256];
+ krb5_error_code kret;
+
+ HEIMDAL_MUTEX_lock(&gssapi_keytab_mutex);
+
+ if (_gsskrb5_keytab != NULL) {
+ kret = krb5_kt_get_name(context,
+ _gsskrb5_keytab,
+ kt_name, sizeof(kt_name));
+ if (kret == 0)
+ kret = krb5_kt_resolve(context, kt_name, keytab);
+ } else
+ kret = krb5_kt_default(context, keytab);
+
+ HEIMDAL_MUTEX_unlock(&gssapi_keytab_mutex);
+
+ return (kret);
+}
+
+static OM_uint32 acquire_initiator_cred
+ (OM_uint32 * minor_status,
+ krb5_context context,
+ const gss_name_t desired_name,
+ OM_uint32 time_req,
+ const gss_OID_set desired_mechs,
+ gss_cred_usage_t cred_usage,
+ gsskrb5_cred handle,
+ gss_OID_set * actual_mechs,
+ OM_uint32 * time_rec
+ )
+{
+ OM_uint32 ret;
+ krb5_creds cred;
+ krb5_principal def_princ;
+ krb5_get_init_creds_opt *opt;
+ krb5_ccache ccache;
+ krb5_keytab keytab;
+ krb5_error_code kret;
+
+ keytab = NULL;
+ ccache = NULL;
+ def_princ = NULL;
+ ret = GSS_S_FAILURE;
+ memset(&cred, 0, sizeof(cred));
+
+ /*
+ * If we have a preferred principal, lets try to find it in all
+ * caches, otherwise, fall back to default cache, ignore all
+ * errors while searching.
+ */
+
+ if (handle->principal) {
+ kret = krb5_cc_cache_match (context,
+ handle->principal,
+ &ccache);
+ if (kret == 0) {
+ ret = GSS_S_COMPLETE;
+ goto found;
+ }
+ }
+
+ if (ccache == NULL) {
+ kret = krb5_cc_default(context, &ccache);
+ if (kret)
+ goto end;
+ }
+ kret = krb5_cc_get_principal(context, ccache, &def_princ);
+ if (kret != 0) {
+ /* we'll try to use a keytab below */
+ krb5_cc_close(context, ccache);
+ def_princ = NULL;
+ kret = 0;
+ } else if (handle->principal == NULL) {
+ kret = krb5_copy_principal(context, def_princ, &handle->principal);
+ if (kret)
+ goto end;
+ } else if (handle->principal != NULL &&
+ krb5_principal_compare(context, handle->principal,
+ def_princ) == FALSE) {
+ krb5_free_principal(context, def_princ);
+ def_princ = NULL;
+ krb5_cc_close(context, ccache);
+ ccache = NULL;
+ }
+ if (def_princ == NULL) {
+ /* We have no existing credentials cache,
+ * so attempt to get a TGT using a keytab.
+ */
+ if (handle->principal == NULL) {
+ kret = krb5_get_default_principal(context, &handle->principal);
+ if (kret)
+ goto end;
+ }
+ kret = get_keytab(context, &keytab);
+ if (kret)
+ goto end;
+ kret = krb5_get_init_creds_opt_alloc(context, &opt);
+ if (kret)
+ goto end;
+ kret = krb5_get_init_creds_keytab(context, &cred,
+ handle->principal, keytab, 0, NULL, opt);
+ krb5_get_init_creds_opt_free(context, opt);
+ if (kret)
+ goto end;
+ kret = krb5_cc_gen_new(context, &krb5_mcc_ops, &ccache);
+ if (kret)
+ goto end;
+ kret = krb5_cc_initialize(context, ccache, cred.client);
+ if (kret) {
+ krb5_cc_destroy(context, ccache);
+ goto end;
+ }
+ kret = krb5_cc_store_cred(context, ccache, &cred);
+ if (kret) {
+ krb5_cc_destroy(context, ccache);
+ goto end;
+ }
+ handle->lifetime = cred.times.endtime;
+ handle->cred_flags |= GSS_CF_DESTROY_CRED_ON_RELEASE;
+ } else {
+
+ ret = __gsskrb5_ccache_lifetime(minor_status,
+ context,
+ ccache,
+ handle->principal,
+ &handle->lifetime);
+ if (ret != GSS_S_COMPLETE) {
+ krb5_cc_close(context, ccache);
+ goto end;
+ }
+ kret = 0;
+ }
+ found:
+ handle->ccache = ccache;
+ ret = GSS_S_COMPLETE;
+
+end:
+ if (cred.client != NULL)
+ krb5_free_cred_contents(context, &cred);
+ if (def_princ != NULL)
+ krb5_free_principal(context, def_princ);
+ if (keytab != NULL)
+ krb5_kt_close(context, keytab);
+ if (ret != GSS_S_COMPLETE && kret != 0)
+ *minor_status = kret;
+ return (ret);
+}
+
+static OM_uint32 acquire_acceptor_cred
+ (OM_uint32 * minor_status,
+ krb5_context context,
+ const gss_name_t desired_name,
+ OM_uint32 time_req,
+ const gss_OID_set desired_mechs,
+ gss_cred_usage_t cred_usage,
+ gsskrb5_cred handle,
+ gss_OID_set * actual_mechs,
+ OM_uint32 * time_rec
+ )
+{
+ OM_uint32 ret;
+ krb5_error_code kret;
+
+ ret = GSS_S_FAILURE;
+ kret = get_keytab(context, &handle->keytab);
+ if (kret)
+ goto end;
+
+ /* check that the requested principal exists in the keytab */
+ if (handle->principal) {
+ krb5_keytab_entry entry;
+
+ kret = krb5_kt_get_entry(context, handle->keytab,
+ handle->principal, 0, 0, &entry);
+ if (kret)
+ goto end;
+ krb5_kt_free_entry(context, &entry);
+ ret = GSS_S_COMPLETE;
+ } else {
+ /*
+ * Check if there is at least one entry in the keytab before
+ * declaring it as an useful keytab.
+ */
+ krb5_keytab_entry tmp;
+ krb5_kt_cursor c;
+
+ kret = krb5_kt_start_seq_get (context, handle->keytab, &c);
+ if (kret)
+ goto end;
+ if (krb5_kt_next_entry(context, handle->keytab, &tmp, &c) == 0) {
+ krb5_kt_free_entry(context, &tmp);
+ ret = GSS_S_COMPLETE; /* ok found one entry */
+ }
+ krb5_kt_end_seq_get (context, handle->keytab, &c);
+ }
+end:
+ if (ret != GSS_S_COMPLETE) {
+ if (handle->keytab != NULL)
+ krb5_kt_close(context, handle->keytab);
+ if (kret != 0) {
+ *minor_status = kret;
+ }
+ }
+ return (ret);
+}
+
+OM_uint32 _gsskrb5_acquire_cred
+(OM_uint32 * minor_status,
+ const gss_name_t desired_name,
+ OM_uint32 time_req,
+ const gss_OID_set desired_mechs,
+ gss_cred_usage_t cred_usage,
+ gss_cred_id_t * output_cred_handle,
+ gss_OID_set * actual_mechs,
+ OM_uint32 * time_rec
+ )
+{
+ krb5_context context;
+ gsskrb5_cred handle;
+ OM_uint32 ret;
+
+ if (cred_usage != GSS_C_ACCEPT && cred_usage != GSS_C_INITIATE && cred_usage != GSS_C_BOTH) {
+ *minor_status = GSS_KRB5_S_G_BAD_USAGE;
+ return GSS_S_FAILURE;
+ }
+
+ GSSAPI_KRB5_INIT(&context);
+
+ *output_cred_handle = NULL;
+ if (time_rec)
+ *time_rec = 0;
+ if (actual_mechs)
+ *actual_mechs = GSS_C_NO_OID_SET;
+
+ if (desired_mechs) {
+ int present = 0;
+
+ ret = gss_test_oid_set_member(minor_status, GSS_KRB5_MECHANISM,
+ desired_mechs, &present);
+ if (ret)
+ return ret;
+ if (!present) {
+ *minor_status = 0;
+ return GSS_S_BAD_MECH;
+ }
+ }
+
+ handle = calloc(1, sizeof(*handle));
+ if (handle == NULL) {
+ *minor_status = ENOMEM;
+ return (GSS_S_FAILURE);
+ }
+
+ HEIMDAL_MUTEX_init(&handle->cred_id_mutex);
+
+ if (desired_name != GSS_C_NO_NAME) {
+
+ ret = _gsskrb5_canon_name(minor_status, context, 0, desired_name,
+ &handle->principal);
+ if (ret) {
+ HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex);
+ free(handle);
+ return ret;
+ }
+ }
+ if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH) {
+ ret = acquire_initiator_cred(minor_status, context,
+ desired_name, time_req,
+ desired_mechs, cred_usage, handle,
+ actual_mechs, time_rec);
+ if (ret != GSS_S_COMPLETE) {
+ HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex);
+ krb5_free_principal(context, handle->principal);
+ free(handle);
+ return (ret);
+ }
+ }
+ if (cred_usage == GSS_C_ACCEPT || cred_usage == GSS_C_BOTH) {
+ ret = acquire_acceptor_cred(minor_status, context,
+ desired_name, time_req,
+ desired_mechs, cred_usage, handle, actual_mechs, time_rec);
+ if (ret != GSS_S_COMPLETE) {
+ HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex);
+ krb5_free_principal(context, handle->principal);
+ free(handle);
+ return (ret);
+ }
+ }
+ ret = gss_create_empty_oid_set(minor_status, &handle->mechanisms);
+ if (ret == GSS_S_COMPLETE)
+ ret = gss_add_oid_set_member(minor_status, GSS_KRB5_MECHANISM,
+ &handle->mechanisms);
+ if (ret == GSS_S_COMPLETE)
+ ret = _gsskrb5_inquire_cred(minor_status, (gss_cred_id_t)handle,
+ NULL, time_rec, NULL, actual_mechs);
+ if (ret != GSS_S_COMPLETE) {
+ if (handle->mechanisms != NULL)
+ gss_release_oid_set(NULL, &handle->mechanisms);
+ HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex);
+ krb5_free_principal(context, handle->principal);
+ free(handle);
+ return (ret);
+ }
+ *minor_status = 0;
+ if (time_rec) {
+ ret = _gsskrb5_lifetime_left(minor_status,
+ context,
+ handle->lifetime,
+ time_rec);
+
+ if (ret)
+ return ret;
+ }
+ handle->usage = cred_usage;
+ *output_cred_handle = (gss_cred_id_t)handle;
+ return (GSS_S_COMPLETE);
+}
diff --git a/source4/heimdal/lib/gssapi/krb5/add_cred.c b/source4/heimdal/lib/gssapi/krb5/add_cred.c
new file mode 100644
index 0000000000..d6fd8f6f15
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/krb5/add_cred.c
@@ -0,0 +1,252 @@
+/*
+ * Copyright (c) 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5/gsskrb5_locl.h"
+
+RCSID("$Id$");
+
+OM_uint32 _gsskrb5_add_cred (
+ OM_uint32 *minor_status,
+ const gss_cred_id_t input_cred_handle,
+ const gss_name_t desired_name,
+ const gss_OID desired_mech,
+ gss_cred_usage_t cred_usage,
+ OM_uint32 initiator_time_req,
+ OM_uint32 acceptor_time_req,
+ gss_cred_id_t *output_cred_handle,
+ gss_OID_set *actual_mechs,
+ OM_uint32 *initiator_time_rec,
+ OM_uint32 *acceptor_time_rec)
+{
+ krb5_context context;
+ OM_uint32 ret, lifetime;
+ gsskrb5_cred cred, handle;
+ krb5_const_principal dname;
+
+ handle = NULL;
+ cred = (gsskrb5_cred)input_cred_handle;
+ dname = (krb5_const_principal)desired_name;
+
+ GSSAPI_KRB5_INIT (&context);
+
+ if (gss_oid_equal(desired_mech, GSS_KRB5_MECHANISM) == 0) {
+ *minor_status = 0;
+ return GSS_S_BAD_MECH;
+ }
+
+ if (cred == NULL && output_cred_handle == NULL) {
+ *minor_status = 0;
+ return GSS_S_NO_CRED;
+ }
+
+ if (cred == NULL) { /* XXX standard conformance failure */
+ *minor_status = 0;
+ return GSS_S_NO_CRED;
+ }
+
+ /* check if requested output usage is compatible with output usage */
+ if (output_cred_handle != NULL) {
+ HEIMDAL_MUTEX_lock(&cred->cred_id_mutex);
+ if (cred->usage != cred_usage && cred->usage != GSS_C_BOTH) {
+ HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex);
+ *minor_status = GSS_KRB5_S_G_BAD_USAGE;
+ return(GSS_S_FAILURE);
+ }
+ }
+
+ /* check that we have the same name */
+ if (dname != NULL &&
+ krb5_principal_compare(context, dname,
+ cred->principal) != FALSE) {
+ if (output_cred_handle)
+ HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex);
+ *minor_status = 0;
+ return GSS_S_BAD_NAME;
+ }
+
+ /* make a copy */
+ if (output_cred_handle) {
+ krb5_error_code kret;
+
+ handle = calloc(1, sizeof(*handle));
+ if (handle == NULL) {
+ HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex);
+ *minor_status = ENOMEM;
+ return (GSS_S_FAILURE);
+ }
+
+ handle->usage = cred_usage;
+ handle->lifetime = cred->lifetime;
+ handle->principal = NULL;
+ handle->keytab = NULL;
+ handle->ccache = NULL;
+ handle->mechanisms = NULL;
+ HEIMDAL_MUTEX_init(&handle->cred_id_mutex);
+
+ ret = GSS_S_FAILURE;
+
+ kret = krb5_copy_principal(context, cred->principal,
+ &handle->principal);
+ if (kret) {
+ HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex);
+ free(handle);
+ *minor_status = kret;
+ return GSS_S_FAILURE;
+ }
+
+ if (cred->keytab) {
+ char name[KRB5_KT_PREFIX_MAX_LEN + MAXPATHLEN];
+ int len;
+
+ ret = GSS_S_FAILURE;
+
+ kret = krb5_kt_get_type(context, cred->keytab,
+ name, KRB5_KT_PREFIX_MAX_LEN);
+ if (kret) {
+ *minor_status = kret;
+ goto failure;
+ }
+ len = strlen(name);
+ name[len++] = ':';
+
+ kret = krb5_kt_get_name(context, cred->keytab,
+ name + len,
+ sizeof(name) - len);
+ if (kret) {
+ *minor_status = kret;
+ goto failure;
+ }
+
+ kret = krb5_kt_resolve(context, name,
+ &handle->keytab);
+ if (kret){
+ *minor_status = kret;
+ goto failure;
+ }
+ }
+
+ if (cred->ccache) {
+ const char *type, *name;
+ char *type_name;
+
+ ret = GSS_S_FAILURE;
+
+ type = krb5_cc_get_type(context, cred->ccache);
+ if (type == NULL){
+ *minor_status = ENOMEM;
+ goto failure;
+ }
+
+ if (strcmp(type, "MEMORY") == 0) {
+ ret = krb5_cc_gen_new(context, &krb5_mcc_ops,
+ &handle->ccache);
+ if (ret) {
+ *minor_status = ret;
+ goto failure;
+ }
+
+ ret = krb5_cc_copy_cache(context, cred->ccache,
+ handle->ccache);
+ if (ret) {
+ *minor_status = ret;
+ goto failure;
+ }
+
+ } else {
+ name = krb5_cc_get_name(context, cred->ccache);
+ if (name == NULL) {
+ *minor_status = ENOMEM;
+ goto failure;
+ }
+
+ asprintf(&type_name, "%s:%s", type, name);
+ if (type_name == NULL) {
+ *minor_status = ENOMEM;
+ goto failure;
+ }
+
+ kret = krb5_cc_resolve(context, type_name,
+ &handle->ccache);
+ free(type_name);
+ if (kret) {
+ *minor_status = kret;
+ goto failure;
+ }
+ }
+ }
+ ret = gss_create_empty_oid_set(minor_status, &handle->mechanisms);
+ if (ret)
+ goto failure;
+
+ ret = gss_add_oid_set_member(minor_status, GSS_KRB5_MECHANISM,
+ &handle->mechanisms);
+ if (ret)
+ goto failure;
+ }
+
+ HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex);
+
+ ret = _gsskrb5_inquire_cred(minor_status, (gss_cred_id_t)cred,
+ NULL, &lifetime, NULL, actual_mechs);
+ if (ret)
+ goto failure;
+
+ if (initiator_time_rec)
+ *initiator_time_rec = lifetime;
+ if (acceptor_time_rec)
+ *acceptor_time_rec = lifetime;
+
+ if (output_cred_handle) {
+ *output_cred_handle = (gss_cred_id_t)handle;
+ }
+
+ *minor_status = 0;
+ return ret;
+
+ failure:
+
+ if (handle) {
+ if (handle->principal)
+ krb5_free_principal(context, handle->principal);
+ if (handle->keytab)
+ krb5_kt_close(context, handle->keytab);
+ if (handle->ccache)
+ krb5_cc_destroy(context, handle->ccache);
+ if (handle->mechanisms)
+ gss_release_oid_set(NULL, &handle->mechanisms);
+ free(handle);
+ }
+ if (output_cred_handle)
+ HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex);
+ return ret;
+}
diff --git a/source4/heimdal/lib/gssapi/krb5/address_to_krb5addr.c b/source4/heimdal/lib/gssapi/krb5/address_to_krb5addr.c
new file mode 100644
index 0000000000..ff0afdc059
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/krb5/address_to_krb5addr.c
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2000 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5/gsskrb5_locl.h"
+
+#include <roken.h>
+
+krb5_error_code
+_gsskrb5i_address_to_krb5addr(krb5_context context,
+ OM_uint32 gss_addr_type,
+ gss_buffer_desc *gss_addr,
+ int16_t port,
+ krb5_address *address)
+{
+ int addr_type;
+ struct sockaddr sa;
+ krb5_socklen_t sa_size = sizeof(sa);
+ krb5_error_code problem;
+
+ if (gss_addr == NULL)
+ return GSS_S_FAILURE;
+
+ switch (gss_addr_type) {
+#ifdef HAVE_IPV6
+ case GSS_C_AF_INET6: addr_type = AF_INET6;
+ break;
+#endif /* HAVE_IPV6 */
+
+ case GSS_C_AF_INET: addr_type = AF_INET;
+ break;
+ default:
+ return GSS_S_FAILURE;
+ }
+
+ problem = krb5_h_addr2sockaddr (context,
+ addr_type,
+ gss_addr->value,
+ &sa,
+ &sa_size,
+ port);
+ if (problem)
+ return GSS_S_FAILURE;
+
+ problem = krb5_sockaddr2address (context, &sa, address);
+
+ return problem;
+}
diff --git a/source4/heimdal/lib/gssapi/krb5/arcfour.c b/source4/heimdal/lib/gssapi/krb5/arcfour.c
new file mode 100644
index 0000000000..7288b58493
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/krb5/arcfour.c
@@ -0,0 +1,760 @@
+/*
+ * Copyright (c) 2003 - 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5/gsskrb5_locl.h"
+
+RCSID("$Id$");
+
+/*
+ * Implements draft-brezak-win2k-krb-rc4-hmac-04.txt
+ *
+ * The arcfour message have the following formats:
+ *
+ * MIC token
+ * TOK_ID[2] = 01 01
+ * SGN_ALG[2] = 11 00
+ * Filler[4]
+ * SND_SEQ[8]
+ * SGN_CKSUM[8]
+ *
+ * WRAP token
+ * TOK_ID[2] = 02 01
+ * SGN_ALG[2];
+ * SEAL_ALG[2]
+ * Filler[2]
+ * SND_SEQ[2]
+ * SGN_CKSUM[8]
+ * Confounder[8]
+ */
+
+/*
+ * WRAP in DCE-style have a fixed size header, the oid and length over
+ * the WRAP header is a total of
+ * GSS_ARCFOUR_WRAP_TOKEN_DCE_DER_HEADER_SIZE +
+ * GSS_ARCFOUR_WRAP_TOKEN_SIZE byte (ie total of 45 bytes overhead,
+ * remember the 2 bytes from APPL [0] SEQ).
+ */
+
+#define GSS_ARCFOUR_WRAP_TOKEN_SIZE 32
+#define GSS_ARCFOUR_WRAP_TOKEN_DCE_DER_HEADER_SIZE 13
+
+
+static krb5_error_code
+arcfour_mic_key(krb5_context context, krb5_keyblock *key,
+ void *cksum_data, size_t cksum_size,
+ void *key6_data, size_t key6_size)
+{
+ krb5_error_code ret;
+
+ Checksum cksum_k5;
+ krb5_keyblock key5;
+ char k5_data[16];
+
+ Checksum cksum_k6;
+
+ char T[4];
+
+ memset(T, 0, 4);
+ cksum_k5.checksum.data = k5_data;
+ cksum_k5.checksum.length = sizeof(k5_data);
+
+ if (key->keytype == KEYTYPE_ARCFOUR_56) {
+ char L40[14] = "fortybits";
+
+ memcpy(L40 + 10, T, sizeof(T));
+ ret = krb5_hmac(context, CKSUMTYPE_RSA_MD5,
+ L40, 14, 0, key, &cksum_k5);
+ memset(&k5_data[7], 0xAB, 9);
+ } else {
+ ret = krb5_hmac(context, CKSUMTYPE_RSA_MD5,
+ T, 4, 0, key, &cksum_k5);
+ }
+ if (ret)
+ return ret;
+
+ key5.keytype = KEYTYPE_ARCFOUR;
+ key5.keyvalue = cksum_k5.checksum;
+
+ cksum_k6.checksum.data = key6_data;
+ cksum_k6.checksum.length = key6_size;
+
+ return krb5_hmac(context, CKSUMTYPE_RSA_MD5,
+ cksum_data, cksum_size, 0, &key5, &cksum_k6);
+}
+
+
+static krb5_error_code
+arcfour_mic_cksum(krb5_context context,
+ krb5_keyblock *key, unsigned usage,
+ u_char *sgn_cksum, size_t sgn_cksum_sz,
+ const u_char *v1, size_t l1,
+ const void *v2, size_t l2,
+ const void *v3, size_t l3)
+{
+ Checksum CKSUM;
+ u_char *ptr;
+ size_t len;
+ krb5_crypto crypto;
+ krb5_error_code ret;
+
+ assert(sgn_cksum_sz == 8);
+
+ len = l1 + l2 + l3;
+
+ ptr = malloc(len);
+ if (ptr == NULL)
+ return ENOMEM;
+
+ memcpy(ptr, v1, l1);
+ memcpy(ptr + l1, v2, l2);
+ memcpy(ptr + l1 + l2, v3, l3);
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret) {
+ free(ptr);
+ return ret;
+ }
+
+ ret = krb5_create_checksum(context,
+ crypto,
+ usage,
+ 0,
+ ptr, len,
+ &CKSUM);
+ free(ptr);
+ if (ret == 0) {
+ memcpy(sgn_cksum, CKSUM.checksum.data, sgn_cksum_sz);
+ free_Checksum(&CKSUM);
+ }
+ krb5_crypto_destroy(context, crypto);
+
+ return ret;
+}
+
+
+OM_uint32
+_gssapi_get_mic_arcfour(OM_uint32 * minor_status,
+ const gsskrb5_ctx context_handle,
+ krb5_context context,
+ gss_qop_t qop_req,
+ const gss_buffer_t message_buffer,
+ gss_buffer_t message_token,
+ krb5_keyblock *key)
+{
+ krb5_error_code ret;
+ int32_t seq_number;
+ size_t len, total_len;
+ u_char k6_data[16], *p0, *p;
+ RC4_KEY rc4_key;
+
+ _gsskrb5_encap_length (22, &len, &total_len, GSS_KRB5_MECHANISM);
+
+ message_token->length = total_len;
+ message_token->value = malloc (total_len);
+ if (message_token->value == NULL) {
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+
+ p0 = _gssapi_make_mech_header(message_token->value,
+ len,
+ GSS_KRB5_MECHANISM);
+ p = p0;
+
+ *p++ = 0x01; /* TOK_ID */
+ *p++ = 0x01;
+ *p++ = 0x11; /* SGN_ALG */
+ *p++ = 0x00;
+ *p++ = 0xff; /* Filler */
+ *p++ = 0xff;
+ *p++ = 0xff;
+ *p++ = 0xff;
+
+ p = NULL;
+
+ ret = arcfour_mic_cksum(context,
+ key, KRB5_KU_USAGE_SIGN,
+ p0 + 16, 8, /* SGN_CKSUM */
+ p0, 8, /* TOK_ID, SGN_ALG, Filer */
+ message_buffer->value, message_buffer->length,
+ NULL, 0);
+ if (ret) {
+ _gsskrb5_release_buffer(minor_status, message_token);
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+
+ ret = arcfour_mic_key(context, key,
+ p0 + 16, 8, /* SGN_CKSUM */
+ k6_data, sizeof(k6_data));
+ if (ret) {
+ _gsskrb5_release_buffer(minor_status, message_token);
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+
+ HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex);
+ krb5_auth_con_getlocalseqnumber (context,
+ context_handle->auth_context,
+ &seq_number);
+ p = p0 + 8; /* SND_SEQ */
+ _gsskrb5_encode_be_om_uint32(seq_number, p);
+
+ krb5_auth_con_setlocalseqnumber (context,
+ context_handle->auth_context,
+ ++seq_number);
+ HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
+
+ memset (p + 4, (context_handle->more_flags & LOCAL) ? 0 : 0xff, 4);
+
+ RC4_set_key (&rc4_key, sizeof(k6_data), k6_data);
+ RC4 (&rc4_key, 8, p, p);
+
+ memset(&rc4_key, 0, sizeof(rc4_key));
+ memset(k6_data, 0, sizeof(k6_data));
+
+ *minor_status = 0;
+ return GSS_S_COMPLETE;
+}
+
+
+OM_uint32
+_gssapi_verify_mic_arcfour(OM_uint32 * minor_status,
+ const gsskrb5_ctx context_handle,
+ krb5_context context,
+ const gss_buffer_t message_buffer,
+ const gss_buffer_t token_buffer,
+ gss_qop_t * qop_state,
+ krb5_keyblock *key,
+ char *type)
+{
+ krb5_error_code ret;
+ uint32_t seq_number;
+ OM_uint32 omret;
+ u_char SND_SEQ[8], cksum_data[8], *p;
+ char k6_data[16];
+ int cmp;
+
+ if (qop_state)
+ *qop_state = 0;
+
+ p = token_buffer->value;
+ omret = _gsskrb5_verify_header (&p,
+ token_buffer->length,
+ (u_char *)type,
+ GSS_KRB5_MECHANISM);
+ if (omret)
+ return omret;
+
+ if (memcmp(p, "\x11\x00", 2) != 0) /* SGN_ALG = HMAC MD5 ARCFOUR */
+ return GSS_S_BAD_SIG;
+ p += 2;
+ if (memcmp (p, "\xff\xff\xff\xff", 4) != 0)
+ return GSS_S_BAD_MIC;
+ p += 4;
+
+ ret = arcfour_mic_cksum(context,
+ key, KRB5_KU_USAGE_SIGN,
+ cksum_data, sizeof(cksum_data),
+ p - 8, 8,
+ message_buffer->value, message_buffer->length,
+ NULL, 0);
+ if (ret) {
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+
+ ret = arcfour_mic_key(context, key,
+ cksum_data, sizeof(cksum_data),
+ k6_data, sizeof(k6_data));
+ if (ret) {
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+
+ cmp = memcmp(cksum_data, p + 8, 8);
+ if (cmp) {
+ *minor_status = 0;
+ return GSS_S_BAD_MIC;
+ }
+
+ {
+ RC4_KEY rc4_key;
+
+ RC4_set_key (&rc4_key, sizeof(k6_data), (void*)k6_data);
+ RC4 (&rc4_key, 8, p, SND_SEQ);
+
+ memset(&rc4_key, 0, sizeof(rc4_key));
+ memset(k6_data, 0, sizeof(k6_data));
+ }
+
+ _gsskrb5_decode_be_om_uint32(SND_SEQ, &seq_number);
+
+ if (context_handle->more_flags & LOCAL)
+ cmp = memcmp(&SND_SEQ[4], "\xff\xff\xff\xff", 4);
+ else
+ cmp = memcmp(&SND_SEQ[4], "\x00\x00\x00\x00", 4);
+
+ memset(SND_SEQ, 0, sizeof(SND_SEQ));
+ if (cmp != 0) {
+ *minor_status = 0;
+ return GSS_S_BAD_MIC;
+ }
+
+ HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex);
+ omret = _gssapi_msg_order_check(context_handle->order, seq_number);
+ HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
+ if (omret)
+ return omret;
+
+ *minor_status = 0;
+ return GSS_S_COMPLETE;
+}
+
+OM_uint32
+_gssapi_wrap_arcfour(OM_uint32 * minor_status,
+ const gsskrb5_ctx context_handle,
+ krb5_context context,
+ int conf_req_flag,
+ gss_qop_t qop_req,
+ const gss_buffer_t input_message_buffer,
+ int * conf_state,
+ gss_buffer_t output_message_buffer,
+ krb5_keyblock *key)
+{
+ u_char Klocaldata[16], k6_data[16], *p, *p0;
+ size_t len, total_len, datalen;
+ krb5_keyblock Klocal;
+ krb5_error_code ret;
+ int32_t seq_number;
+
+ if (conf_state)
+ *conf_state = 0;
+
+ datalen = input_message_buffer->length;
+
+ if (IS_DCE_STYLE(context_handle)) {
+ len = GSS_ARCFOUR_WRAP_TOKEN_SIZE;
+ _gssapi_encap_length(len, &len, &total_len, GSS_KRB5_MECHANISM);
+ total_len += datalen;
+ } else {
+ datalen += 1; /* padding */
+ len = datalen + GSS_ARCFOUR_WRAP_TOKEN_SIZE;
+ _gssapi_encap_length(len, &len, &total_len, GSS_KRB5_MECHANISM);
+ }
+
+ output_message_buffer->length = total_len;
+ output_message_buffer->value = malloc (total_len);
+ if (output_message_buffer->value == NULL) {
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+
+ p0 = _gssapi_make_mech_header(output_message_buffer->value,
+ len,
+ GSS_KRB5_MECHANISM);
+ p = p0;
+
+ *p++ = 0x02; /* TOK_ID */
+ *p++ = 0x01;
+ *p++ = 0x11; /* SGN_ALG */
+ *p++ = 0x00;
+ if (conf_req_flag) {
+ *p++ = 0x10; /* SEAL_ALG */
+ *p++ = 0x00;
+ } else {
+ *p++ = 0xff; /* SEAL_ALG */
+ *p++ = 0xff;
+ }
+ *p++ = 0xff; /* Filler */
+ *p++ = 0xff;
+
+ p = NULL;
+
+ HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex);
+ krb5_auth_con_getlocalseqnumber (context,
+ context_handle->auth_context,
+ &seq_number);
+
+ _gsskrb5_encode_be_om_uint32(seq_number, p0 + 8);
+
+ krb5_auth_con_setlocalseqnumber (context,
+ context_handle->auth_context,
+ ++seq_number);
+ HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
+
+ memset (p0 + 8 + 4,
+ (context_handle->more_flags & LOCAL) ? 0 : 0xff,
+ 4);
+
+ krb5_generate_random_block(p0 + 24, 8); /* fill in Confounder */
+
+ /* p points to data */
+ p = p0 + GSS_ARCFOUR_WRAP_TOKEN_SIZE;
+ memcpy(p, input_message_buffer->value, input_message_buffer->length);
+
+ if (!IS_DCE_STYLE(context_handle))
+ p[input_message_buffer->length] = 1; /* padding */
+
+ ret = arcfour_mic_cksum(context,
+ key, KRB5_KU_USAGE_SEAL,
+ p0 + 16, 8, /* SGN_CKSUM */
+ p0, 8, /* TOK_ID, SGN_ALG, SEAL_ALG, Filler */
+ p0 + 24, 8, /* Confounder */
+ p0 + GSS_ARCFOUR_WRAP_TOKEN_SIZE,
+ datalen);
+ if (ret) {
+ *minor_status = ret;
+ _gsskrb5_release_buffer(minor_status, output_message_buffer);
+ return GSS_S_FAILURE;
+ }
+
+ {
+ int i;
+
+ Klocal.keytype = key->keytype;
+ Klocal.keyvalue.data = Klocaldata;
+ Klocal.keyvalue.length = sizeof(Klocaldata);
+
+ for (i = 0; i < 16; i++)
+ Klocaldata[i] = ((u_char *)key->keyvalue.data)[i] ^ 0xF0;
+ }
+ ret = arcfour_mic_key(context, &Klocal,
+ p0 + 8, 4, /* SND_SEQ */
+ k6_data, sizeof(k6_data));
+ memset(Klocaldata, 0, sizeof(Klocaldata));
+ if (ret) {
+ _gsskrb5_release_buffer(minor_status, output_message_buffer);
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+
+
+ if(conf_req_flag) {
+ RC4_KEY rc4_key;
+
+ RC4_set_key (&rc4_key, sizeof(k6_data), (void *)k6_data);
+ /* XXX ? */
+ RC4 (&rc4_key, 8 + datalen, p0 + 24, p0 + 24); /* Confounder + data */
+ memset(&rc4_key, 0, sizeof(rc4_key));
+ }
+ memset(k6_data, 0, sizeof(k6_data));
+
+ ret = arcfour_mic_key(context, key,
+ p0 + 16, 8, /* SGN_CKSUM */
+ k6_data, sizeof(k6_data));
+ if (ret) {
+ _gsskrb5_release_buffer(minor_status, output_message_buffer);
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+
+ {
+ RC4_KEY rc4_key;
+
+ RC4_set_key (&rc4_key, sizeof(k6_data), k6_data);
+ RC4 (&rc4_key, 8, p0 + 8, p0 + 8); /* SND_SEQ */
+ memset(&rc4_key, 0, sizeof(rc4_key));
+ memset(k6_data, 0, sizeof(k6_data));
+ }
+
+ if (conf_state)
+ *conf_state = conf_req_flag;
+
+ *minor_status = 0;
+ return GSS_S_COMPLETE;
+}
+
+OM_uint32 _gssapi_unwrap_arcfour(OM_uint32 *minor_status,
+ const gsskrb5_ctx context_handle,
+ krb5_context context,
+ const gss_buffer_t input_message_buffer,
+ gss_buffer_t output_message_buffer,
+ int *conf_state,
+ gss_qop_t *qop_state,
+ krb5_keyblock *key)
+{
+ u_char Klocaldata[16];
+ krb5_keyblock Klocal;
+ krb5_error_code ret;
+ uint32_t seq_number;
+ size_t datalen;
+ OM_uint32 omret;
+ u_char k6_data[16], SND_SEQ[8], Confounder[8];
+ u_char cksum_data[8];
+ u_char *p, *p0;
+ int cmp;
+ int conf_flag;
+ size_t padlen = 0, len;
+
+ if (conf_state)
+ *conf_state = 0;
+ if (qop_state)
+ *qop_state = 0;
+
+ p0 = input_message_buffer->value;
+
+ if (IS_DCE_STYLE(context_handle)) {
+ len = GSS_ARCFOUR_WRAP_TOKEN_SIZE +
+ GSS_ARCFOUR_WRAP_TOKEN_DCE_DER_HEADER_SIZE;
+ if (input_message_buffer->length < len)
+ return GSS_S_BAD_MECH;
+ } else {
+ len = input_message_buffer->length;
+ }
+
+ omret = _gssapi_verify_mech_header(&p0,
+ len,
+ GSS_KRB5_MECHANISM);
+ if (omret)
+ return omret;
+
+ /* length of mech header */
+ len = (p0 - (u_char *)input_message_buffer->value) +
+ GSS_ARCFOUR_WRAP_TOKEN_SIZE;
+
+ if (len > input_message_buffer->length)
+ return GSS_S_BAD_MECH;
+
+ /* length of data */
+ datalen = input_message_buffer->length - len;
+
+ p = p0;
+
+ if (memcmp(p, "\x02\x01", 2) != 0)
+ return GSS_S_BAD_SIG;
+ p += 2;
+ if (memcmp(p, "\x11\x00", 2) != 0) /* SGN_ALG = HMAC MD5 ARCFOUR */
+ return GSS_S_BAD_SIG;
+ p += 2;
+
+ if (memcmp (p, "\x10\x00", 2) == 0)
+ conf_flag = 1;
+ else if (memcmp (p, "\xff\xff", 2) == 0)
+ conf_flag = 0;
+ else
+ return GSS_S_BAD_SIG;
+
+ p += 2;
+ if (memcmp (p, "\xff\xff", 2) != 0)
+ return GSS_S_BAD_MIC;
+ p = NULL;
+
+ ret = arcfour_mic_key(context, key,
+ p0 + 16, 8, /* SGN_CKSUM */
+ k6_data, sizeof(k6_data));
+ if (ret) {
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+
+ {
+ RC4_KEY rc4_key;
+
+ RC4_set_key (&rc4_key, sizeof(k6_data), k6_data);
+ RC4 (&rc4_key, 8, p0 + 8, SND_SEQ); /* SND_SEQ */
+ memset(&rc4_key, 0, sizeof(rc4_key));
+ memset(k6_data, 0, sizeof(k6_data));
+ }
+
+ _gsskrb5_decode_be_om_uint32(SND_SEQ, &seq_number);
+
+ if (context_handle->more_flags & LOCAL)
+ cmp = memcmp(&SND_SEQ[4], "\xff\xff\xff\xff", 4);
+ else
+ cmp = memcmp(&SND_SEQ[4], "\x00\x00\x00\x00", 4);
+
+ if (cmp != 0) {
+ *minor_status = 0;
+ return GSS_S_BAD_MIC;
+ }
+
+ {
+ int i;
+
+ Klocal.keytype = key->keytype;
+ Klocal.keyvalue.data = Klocaldata;
+ Klocal.keyvalue.length = sizeof(Klocaldata);
+
+ for (i = 0; i < 16; i++)
+ Klocaldata[i] = ((u_char *)key->keyvalue.data)[i] ^ 0xF0;
+ }
+ ret = arcfour_mic_key(context, &Klocal,
+ SND_SEQ, 4,
+ k6_data, sizeof(k6_data));
+ memset(Klocaldata, 0, sizeof(Klocaldata));
+ if (ret) {
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+
+ output_message_buffer->value = malloc(datalen);
+ if (output_message_buffer->value == NULL) {
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+ output_message_buffer->length = datalen;
+
+ if(conf_flag) {
+ RC4_KEY rc4_key;
+
+ RC4_set_key (&rc4_key, sizeof(k6_data), k6_data);
+ RC4 (&rc4_key, 8, p0 + 24, Confounder); /* Confounder */
+ RC4 (&rc4_key, datalen, p0 + GSS_ARCFOUR_WRAP_TOKEN_SIZE,
+ output_message_buffer->value);
+ memset(&rc4_key, 0, sizeof(rc4_key));
+ } else {
+ memcpy(Confounder, p0 + 24, 8); /* Confounder */
+ memcpy(output_message_buffer->value,
+ p0 + GSS_ARCFOUR_WRAP_TOKEN_SIZE,
+ datalen);
+ }
+ memset(k6_data, 0, sizeof(k6_data));
+
+ if (!IS_DCE_STYLE(context_handle)) {
+ ret = _gssapi_verify_pad(output_message_buffer, datalen, &padlen);
+ if (ret) {
+ _gsskrb5_release_buffer(minor_status, output_message_buffer);
+ *minor_status = 0;
+ return ret;
+ }
+ output_message_buffer->length -= padlen;
+ }
+
+ ret = arcfour_mic_cksum(context,
+ key, KRB5_KU_USAGE_SEAL,
+ cksum_data, sizeof(cksum_data),
+ p0, 8,
+ Confounder, sizeof(Confounder),
+ output_message_buffer->value,
+ output_message_buffer->length + padlen);
+ if (ret) {
+ _gsskrb5_release_buffer(minor_status, output_message_buffer);
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+
+ cmp = memcmp(cksum_data, p0 + 16, 8); /* SGN_CKSUM */
+ if (cmp) {
+ _gsskrb5_release_buffer(minor_status, output_message_buffer);
+ *minor_status = 0;
+ return GSS_S_BAD_MIC;
+ }
+
+ HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex);
+ omret = _gssapi_msg_order_check(context_handle->order, seq_number);
+ HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
+ if (omret)
+ return omret;
+
+ if (conf_state)
+ *conf_state = conf_flag;
+
+ *minor_status = 0;
+ return GSS_S_COMPLETE;
+}
+
+static OM_uint32
+max_wrap_length_arcfour(const gsskrb5_ctx ctx,
+ krb5_crypto crypto,
+ size_t input_length,
+ OM_uint32 *max_input_size)
+{
+ /*
+ * if GSS_C_DCE_STYLE is in use:
+ * - we only need to encapsulate the WRAP token
+ * However, since this is a fixed since, we just
+ */
+ if (IS_DCE_STYLE(ctx)) {
+ size_t len, total_len;
+
+ len = GSS_ARCFOUR_WRAP_TOKEN_SIZE;
+ _gssapi_encap_length(len, &len, &total_len, GSS_KRB5_MECHANISM);
+
+ if (input_length < len)
+ *max_input_size = 0;
+ else
+ *max_input_size = input_length - len;
+
+ } else {
+ size_t extrasize = GSS_ARCFOUR_WRAP_TOKEN_SIZE;
+ size_t blocksize = 8;
+ size_t len, total_len;
+
+ len = 8 + input_length + blocksize + extrasize;
+
+ _gsskrb5_encap_length(len, &len, &total_len, GSS_KRB5_MECHANISM);
+
+ total_len -= input_length; /* token length */
+ if (total_len < input_length) {
+ *max_input_size = (input_length - total_len);
+ (*max_input_size) &= (~(OM_uint32)(blocksize - 1));
+ } else {
+ *max_input_size = 0;
+ }
+ }
+
+ return GSS_S_COMPLETE;
+}
+
+OM_uint32
+_gssapi_wrap_size_arcfour(OM_uint32 *minor_status,
+ const gsskrb5_ctx ctx,
+ krb5_context context,
+ int conf_req_flag,
+ gss_qop_t qop_req,
+ OM_uint32 req_output_size,
+ OM_uint32 *max_input_size,
+ krb5_keyblock *key)
+{
+ krb5_error_code ret;
+ krb5_crypto crypto;
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret != 0) {
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+
+ ret = max_wrap_length_arcfour(ctx, crypto,
+ req_output_size, max_input_size);
+ if (ret != 0) {
+ *minor_status = ret;
+ krb5_crypto_destroy(context, crypto);
+ return GSS_S_FAILURE;
+ }
+
+ krb5_crypto_destroy(context, crypto);
+
+ return GSS_S_COMPLETE;
+}
diff --git a/source4/heimdal/lib/gssapi/krb5/canonicalize_name.c b/source4/heimdal/lib/gssapi/krb5/canonicalize_name.c
new file mode 100644
index 0000000000..fa2258ce82
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/krb5/canonicalize_name.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 1997 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5/gsskrb5_locl.h"
+
+RCSID("$Id$");
+
+OM_uint32 _gsskrb5_canonicalize_name (
+ OM_uint32 * minor_status,
+ const gss_name_t input_name,
+ const gss_OID mech_type,
+ gss_name_t * output_name
+ )
+{
+ krb5_context context;
+ krb5_principal name;
+ OM_uint32 ret;
+
+ *output_name = NULL;
+
+ GSSAPI_KRB5_INIT (&context);
+
+ ret = _gsskrb5_canon_name(minor_status, context, 1, input_name, &name);
+ if (ret)
+ return ret;
+
+ *output_name = (gss_name_t)name;
+
+ return GSS_S_COMPLETE;
+}
diff --git a/source4/heimdal/lib/gssapi/krb5/cfx.c b/source4/heimdal/lib/gssapi/krb5/cfx.c
new file mode 100755
index 0000000000..d029f55ce4
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/krb5/cfx.c
@@ -0,0 +1,905 @@
+/*
+ * Copyright (c) 2003, PADL Software Pty Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of PADL Software nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5/gsskrb5_locl.h"
+
+RCSID("$Id$");
+
+/*
+ * Implementation of draft-ietf-krb-wg-gssapi-cfx-06.txt
+ */
+
+#define CFXSentByAcceptor (1 << 0)
+#define CFXSealed (1 << 1)
+#define CFXAcceptorSubkey (1 << 2)
+
+krb5_error_code
+_gsskrb5cfx_wrap_length_cfx(const gsskrb5_ctx context_handle,
+ krb5_context context,
+ krb5_crypto crypto,
+ int conf_req_flag,
+ size_t input_length,
+ size_t *output_length,
+ size_t *cksumsize,
+ uint16_t *padlength)
+{
+ krb5_error_code ret;
+ krb5_cksumtype type;
+
+ /* 16-byte header is always first */
+ *output_length = sizeof(gss_cfx_wrap_token_desc);
+ *padlength = 0;
+
+ ret = krb5_crypto_get_checksum_type(context, crypto, &type);
+ if (ret)
+ return ret;
+
+ ret = krb5_checksumsize(context, type, cksumsize);
+ if (ret)
+ return ret;
+
+ if (conf_req_flag) {
+ size_t padsize;
+
+ /* Header is concatenated with data before encryption */
+ input_length += sizeof(gss_cfx_wrap_token_desc);
+
+ if (IS_DCE_STYLE(context_handle)) {
+ ret = krb5_crypto_getblocksize(context, crypto, &padsize);
+ } else {
+ ret = krb5_crypto_getpadsize(context, crypto, &padsize);
+ }
+ if (ret) {
+ return ret;
+ }
+ if (padsize > 1) {
+ /* XXX check this */
+ *padlength = padsize - (input_length % padsize);
+
+ /* We add the pad ourselves (noted here for completeness only) */
+ input_length += *padlength;
+ }
+
+ *output_length += krb5_get_wrapped_length(context,
+ crypto, input_length);
+ } else {
+ /* Checksum is concatenated with data */
+ *output_length += input_length + *cksumsize;
+ }
+
+ assert(*output_length > input_length);
+
+ return 0;
+}
+
+krb5_error_code
+_gsskrb5cfx_max_wrap_length_cfx(krb5_context context,
+ krb5_crypto crypto,
+ int conf_req_flag,
+ size_t input_length,
+ OM_uint32 *output_length)
+{
+ krb5_error_code ret;
+
+ *output_length = 0;
+
+ /* 16-byte header is always first */
+ if (input_length < 16)
+ return 0;
+ input_length -= 16;
+
+ if (conf_req_flag) {
+ size_t wrapped_size, sz;
+
+ wrapped_size = input_length + 1;
+ do {
+ wrapped_size--;
+ sz = krb5_get_wrapped_length(context,
+ crypto, wrapped_size);
+ } while (wrapped_size && sz > input_length);
+ if (wrapped_size == 0) {
+ *output_length = 0;
+ return 0;
+ }
+
+ /* inner header */
+ if (wrapped_size < 16) {
+ *output_length = 0;
+ return 0;
+ }
+ wrapped_size -= 16;
+
+ *output_length = wrapped_size;
+ } else {
+ krb5_cksumtype type;
+ size_t cksumsize;
+
+ ret = krb5_crypto_get_checksum_type(context, crypto, &type);
+ if (ret)
+ return ret;
+
+ ret = krb5_checksumsize(context, type, &cksumsize);
+ if (ret)
+ return ret;
+
+ if (input_length < cksumsize)
+ return 0;
+
+ /* Checksum is concatenated with data */
+ *output_length = input_length - cksumsize;
+ }
+
+ return 0;
+}
+
+
+OM_uint32 _gssapi_wrap_size_cfx(OM_uint32 *minor_status,
+ const gsskrb5_ctx context_handle,
+ krb5_context context,
+ int conf_req_flag,
+ gss_qop_t qop_req,
+ OM_uint32 req_output_size,
+ OM_uint32 *max_input_size,
+ krb5_keyblock *key)
+{
+ krb5_error_code ret;
+ krb5_crypto crypto;
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret != 0) {
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+
+ ret = _gsskrb5cfx_max_wrap_length_cfx(context, crypto, conf_req_flag,
+ req_output_size, max_input_size);
+ if (ret != 0) {
+ *minor_status = ret;
+ krb5_crypto_destroy(context, crypto);
+ return GSS_S_FAILURE;
+ }
+
+ krb5_crypto_destroy(context, crypto);
+
+ return GSS_S_COMPLETE;
+}
+
+/*
+ * Rotate "rrc" bytes to the front or back
+ */
+
+static krb5_error_code
+rrc_rotate(void *data, size_t len, uint16_t rrc, krb5_boolean unrotate)
+{
+ u_char *tmp, buf[256];
+ size_t left;
+
+ if (len == 0)
+ return 0;
+
+ rrc %= len;
+
+ if (rrc == 0)
+ return 0;
+
+ left = len - rrc;
+
+ if (rrc <= sizeof(buf)) {
+ tmp = buf;
+ } else {
+ tmp = malloc(rrc);
+ if (tmp == NULL)
+ return ENOMEM;
+ }
+
+ if (unrotate) {
+ memcpy(tmp, data, rrc);
+ memmove(data, (u_char *)data + rrc, left);
+ memcpy((u_char *)data + left, tmp, rrc);
+ } else {
+ memcpy(tmp, (u_char *)data + left, rrc);
+ memmove((u_char *)data + rrc, data, left);
+ memcpy(data, tmp, rrc);
+ }
+
+ if (rrc > sizeof(buf))
+ free(tmp);
+
+ return 0;
+}
+
+OM_uint32 _gssapi_wrap_cfx(OM_uint32 *minor_status,
+ const gsskrb5_ctx context_handle,
+ krb5_context context,
+ int conf_req_flag,
+ gss_qop_t qop_req,
+ const gss_buffer_t input_message_buffer,
+ int *conf_state,
+ gss_buffer_t output_message_buffer,
+ krb5_keyblock *key)
+{
+ krb5_crypto crypto;
+ gss_cfx_wrap_token token;
+ krb5_error_code ret;
+ unsigned usage;
+ krb5_data cipher;
+ size_t wrapped_len, cksumsize;
+ uint16_t padlength, rrc = 0;
+ int32_t seq_number;
+ u_char *p;
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret != 0) {
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+
+ ret = _gsskrb5cfx_wrap_length_cfx(context_handle, context,
+ crypto, conf_req_flag,
+ input_message_buffer->length,
+ &wrapped_len, &cksumsize, &padlength);
+ if (ret != 0) {
+ *minor_status = ret;
+ krb5_crypto_destroy(context, crypto);
+ return GSS_S_FAILURE;
+ }
+
+ /* Always rotate encrypted token (if any) and checksum to header */
+ rrc = (conf_req_flag ? sizeof(*token) : 0) + (uint16_t)cksumsize;
+
+ output_message_buffer->length = wrapped_len;
+ output_message_buffer->value = malloc(output_message_buffer->length);
+ if (output_message_buffer->value == NULL) {
+ *minor_status = ENOMEM;
+ krb5_crypto_destroy(context, crypto);
+ return GSS_S_FAILURE;
+ }
+
+ p = output_message_buffer->value;
+ token = (gss_cfx_wrap_token)p;
+ token->TOK_ID[0] = 0x05;
+ token->TOK_ID[1] = 0x04;
+ token->Flags = 0;
+ token->Filler = 0xFF;
+ if ((context_handle->more_flags & LOCAL) == 0)
+ token->Flags |= CFXSentByAcceptor;
+ if (context_handle->more_flags & ACCEPTOR_SUBKEY)
+ token->Flags |= CFXAcceptorSubkey;
+ if (conf_req_flag) {
+ /*
+ * In Wrap tokens with confidentiality, the EC field is
+ * used to encode the size (in bytes) of the random filler.
+ */
+ token->Flags |= CFXSealed;
+ token->EC[0] = (padlength >> 8) & 0xFF;
+ token->EC[1] = (padlength >> 0) & 0xFF;
+ } else {
+ /*
+ * In Wrap tokens without confidentiality, the EC field is
+ * used to encode the size (in bytes) of the trailing
+ * checksum.
+ *
+ * This is not used in the checksum calcuation itself,
+ * because the checksum length could potentially vary
+ * depending on the data length.
+ */
+ token->EC[0] = 0;
+ token->EC[1] = 0;
+ }
+
+ /*
+ * In Wrap tokens that provide for confidentiality, the RRC
+ * field in the header contains the hex value 00 00 before
+ * encryption.
+ *
+ * In Wrap tokens that do not provide for confidentiality,
+ * both the EC and RRC fields in the appended checksum
+ * contain the hex value 00 00 for the purpose of calculating
+ * the checksum.
+ */
+ token->RRC[0] = 0;
+ token->RRC[1] = 0;
+
+ HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex);
+ krb5_auth_con_getlocalseqnumber(context,
+ context_handle->auth_context,
+ &seq_number);
+ _gsskrb5_encode_be_om_uint32(0, &token->SND_SEQ[0]);
+ _gsskrb5_encode_be_om_uint32(seq_number, &token->SND_SEQ[4]);
+ krb5_auth_con_setlocalseqnumber(context,
+ context_handle->auth_context,
+ ++seq_number);
+ HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
+
+ /*
+ * If confidentiality is requested, the token header is
+ * appended to the plaintext before encryption; the resulting
+ * token is {"header" | encrypt(plaintext | pad | "header")}.
+ *
+ * If no confidentiality is requested, the checksum is
+ * calculated over the plaintext concatenated with the
+ * token header.
+ */
+ if (context_handle->more_flags & LOCAL) {
+ usage = KRB5_KU_USAGE_INITIATOR_SEAL;
+ } else {
+ usage = KRB5_KU_USAGE_ACCEPTOR_SEAL;
+ }
+
+ if (conf_req_flag) {
+ /*
+ * Any necessary padding is added here to ensure that the
+ * encrypted token header is always at the end of the
+ * ciphertext.
+ *
+ * The specification does not require that the padding
+ * bytes are initialized.
+ */
+ p += sizeof(*token);
+ memcpy(p, input_message_buffer->value, input_message_buffer->length);
+ memset(p + input_message_buffer->length, 0xFF, padlength);
+ memcpy(p + input_message_buffer->length + padlength,
+ token, sizeof(*token));
+
+ ret = krb5_encrypt(context, crypto,
+ usage, p,
+ input_message_buffer->length + padlength +
+ sizeof(*token),
+ &cipher);
+ if (ret != 0) {
+ *minor_status = ret;
+ krb5_crypto_destroy(context, crypto);
+ _gsskrb5_release_buffer(minor_status, output_message_buffer);
+ return GSS_S_FAILURE;
+ }
+ assert(sizeof(*token) + cipher.length == wrapped_len);
+ token->RRC[0] = (rrc >> 8) & 0xFF;
+ token->RRC[1] = (rrc >> 0) & 0xFF;
+
+ /*
+ * this is really ugly, but needed against windows
+ * for DCERPC, as windows rotates by EC+RRC.
+ */
+ if (IS_DCE_STYLE(context_handle)) {
+ ret = rrc_rotate(cipher.data, cipher.length, rrc+padlength, FALSE);
+ } else {
+ ret = rrc_rotate(cipher.data, cipher.length, rrc, FALSE);
+ }
+ if (ret != 0) {
+ *minor_status = ret;
+ krb5_crypto_destroy(context, crypto);
+ _gsskrb5_release_buffer(minor_status, output_message_buffer);
+ return GSS_S_FAILURE;
+ }
+ memcpy(p, cipher.data, cipher.length);
+ krb5_data_free(&cipher);
+ } else {
+ char *buf;
+ Checksum cksum;
+
+ buf = malloc(input_message_buffer->length + sizeof(*token));
+ if (buf == NULL) {
+ *minor_status = ENOMEM;
+ krb5_crypto_destroy(context, crypto);
+ _gsskrb5_release_buffer(minor_status, output_message_buffer);
+ return GSS_S_FAILURE;
+ }
+ memcpy(buf, input_message_buffer->value, input_message_buffer->length);
+ memcpy(buf + input_message_buffer->length, token, sizeof(*token));
+
+ ret = krb5_create_checksum(context, crypto,
+ usage, 0, buf,
+ input_message_buffer->length +
+ sizeof(*token),
+ &cksum);
+ if (ret != 0) {
+ *minor_status = ret;
+ krb5_crypto_destroy(context, crypto);
+ _gsskrb5_release_buffer(minor_status, output_message_buffer);
+ free(buf);
+ return GSS_S_FAILURE;
+ }
+
+ free(buf);
+
+ assert(cksum.checksum.length == cksumsize);
+ token->EC[0] = (cksum.checksum.length >> 8) & 0xFF;
+ token->EC[1] = (cksum.checksum.length >> 0) & 0xFF;
+ token->RRC[0] = (rrc >> 8) & 0xFF;
+ token->RRC[1] = (rrc >> 0) & 0xFF;
+
+ p += sizeof(*token);
+ memcpy(p, input_message_buffer->value, input_message_buffer->length);
+ memcpy(p + input_message_buffer->length,
+ cksum.checksum.data, cksum.checksum.length);
+
+ ret = rrc_rotate(p,
+ input_message_buffer->length + cksum.checksum.length, rrc, FALSE);
+ if (ret != 0) {
+ *minor_status = ret;
+ krb5_crypto_destroy(context, crypto);
+ _gsskrb5_release_buffer(minor_status, output_message_buffer);
+ free_Checksum(&cksum);
+ return GSS_S_FAILURE;
+ }
+ free_Checksum(&cksum);
+ }
+
+ krb5_crypto_destroy(context, crypto);
+
+ if (conf_state != NULL) {
+ *conf_state = conf_req_flag;
+ }
+
+ *minor_status = 0;
+ return GSS_S_COMPLETE;
+}
+
+OM_uint32 _gssapi_unwrap_cfx(OM_uint32 *minor_status,
+ const gsskrb5_ctx context_handle,
+ krb5_context context,
+ const gss_buffer_t input_message_buffer,
+ gss_buffer_t output_message_buffer,
+ int *conf_state,
+ gss_qop_t *qop_state,
+ krb5_keyblock *key)
+{
+ krb5_crypto crypto;
+ gss_cfx_wrap_token token;
+ u_char token_flags;
+ krb5_error_code ret;
+ unsigned usage;
+ krb5_data data;
+ uint16_t ec, rrc;
+ OM_uint32 seq_number_lo, seq_number_hi;
+ size_t len;
+ u_char *p;
+
+ *minor_status = 0;
+
+ if (input_message_buffer->length < sizeof(*token)) {
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+
+ p = input_message_buffer->value;
+
+ token = (gss_cfx_wrap_token)p;
+
+ if (token->TOK_ID[0] != 0x05 || token->TOK_ID[1] != 0x04) {
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+
+ /* Ignore unknown flags */
+ token_flags = token->Flags &
+ (CFXSentByAcceptor | CFXSealed | CFXAcceptorSubkey);
+
+ if (token_flags & CFXSentByAcceptor) {
+ if ((context_handle->more_flags & LOCAL) == 0)
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+
+ if (context_handle->more_flags & ACCEPTOR_SUBKEY) {
+ if ((token_flags & CFXAcceptorSubkey) == 0)
+ return GSS_S_DEFECTIVE_TOKEN;
+ } else {
+ if (token_flags & CFXAcceptorSubkey)
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+
+ if (token->Filler != 0xFF) {
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+
+ if (conf_state != NULL) {
+ *conf_state = (token_flags & CFXSealed) ? 1 : 0;
+ }
+
+ ec = (token->EC[0] << 8) | token->EC[1];
+ rrc = (token->RRC[0] << 8) | token->RRC[1];
+
+ /*
+ * Check sequence number
+ */
+ _gsskrb5_decode_be_om_uint32(&token->SND_SEQ[0], &seq_number_hi);
+ _gsskrb5_decode_be_om_uint32(&token->SND_SEQ[4], &seq_number_lo);
+ if (seq_number_hi) {
+ /* no support for 64-bit sequence numbers */
+ *minor_status = ERANGE;
+ return GSS_S_UNSEQ_TOKEN;
+ }
+
+ HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex);
+ ret = _gssapi_msg_order_check(context_handle->order, seq_number_lo);
+ if (ret != 0) {
+ *minor_status = 0;
+ HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
+ _gsskrb5_release_buffer(minor_status, output_message_buffer);
+ return ret;
+ }
+ HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
+
+ /*
+ * Decrypt and/or verify checksum
+ */
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret != 0) {
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+
+ if (context_handle->more_flags & LOCAL) {
+ usage = KRB5_KU_USAGE_ACCEPTOR_SEAL;
+ } else {
+ usage = KRB5_KU_USAGE_INITIATOR_SEAL;
+ }
+
+ p += sizeof(*token);
+ len = input_message_buffer->length;
+ len -= (p - (u_char *)input_message_buffer->value);
+
+ if (token_flags & CFXSealed) {
+ /*
+ * this is really ugly, but needed against windows
+ * for DCERPC, as windows rotates by EC+RRC.
+ */
+ if (IS_DCE_STYLE(context_handle)) {
+ *minor_status = rrc_rotate(p, len, rrc+ec, TRUE);
+ } else {
+ *minor_status = rrc_rotate(p, len, rrc, TRUE);
+ }
+ if (*minor_status != 0) {
+ krb5_crypto_destroy(context, crypto);
+ return GSS_S_FAILURE;
+ }
+
+ ret = krb5_decrypt(context, crypto, usage,
+ p, len, &data);
+ if (ret != 0) {
+ *minor_status = ret;
+ krb5_crypto_destroy(context, crypto);
+ return GSS_S_BAD_MIC;
+ }
+
+ /* Check that there is room for the pad and token header */
+ if (data.length < ec + sizeof(*token)) {
+ krb5_crypto_destroy(context, crypto);
+ krb5_data_free(&data);
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+ p = data.data;
+ p += data.length - sizeof(*token);
+
+ /* RRC is unprotected; don't modify input buffer */
+ ((gss_cfx_wrap_token)p)->RRC[0] = token->RRC[0];
+ ((gss_cfx_wrap_token)p)->RRC[1] = token->RRC[1];
+
+ /* Check the integrity of the header */
+ if (memcmp(p, token, sizeof(*token)) != 0) {
+ krb5_crypto_destroy(context, crypto);
+ krb5_data_free(&data);
+ return GSS_S_BAD_MIC;
+ }
+
+ output_message_buffer->value = data.data;
+ output_message_buffer->length = data.length - ec - sizeof(*token);
+ } else {
+ Checksum cksum;
+
+ /* Rotate by RRC; bogus to do this in-place XXX */
+ *minor_status = rrc_rotate(p, len, rrc, TRUE);
+ if (*minor_status != 0) {
+ krb5_crypto_destroy(context, crypto);
+ return GSS_S_FAILURE;
+ }
+
+ /* Determine checksum type */
+ ret = krb5_crypto_get_checksum_type(context,
+ crypto, &cksum.cksumtype);
+ if (ret != 0) {
+ *minor_status = ret;
+ krb5_crypto_destroy(context, crypto);
+ return GSS_S_FAILURE;
+ }
+
+ cksum.checksum.length = ec;
+
+ /* Check we have at least as much data as the checksum */
+ if (len < cksum.checksum.length) {
+ *minor_status = ERANGE;
+ krb5_crypto_destroy(context, crypto);
+ return GSS_S_BAD_MIC;
+ }
+
+ /* Length now is of the plaintext only, no checksum */
+ len -= cksum.checksum.length;
+ cksum.checksum.data = p + len;
+
+ output_message_buffer->length = len; /* for later */
+ output_message_buffer->value = malloc(len + sizeof(*token));
+ if (output_message_buffer->value == NULL) {
+ *minor_status = ENOMEM;
+ krb5_crypto_destroy(context, crypto);
+ return GSS_S_FAILURE;
+ }
+
+ /* Checksum is over (plaintext-data | "header") */
+ memcpy(output_message_buffer->value, p, len);
+ memcpy((u_char *)output_message_buffer->value + len,
+ token, sizeof(*token));
+
+ /* EC is not included in checksum calculation */
+ token = (gss_cfx_wrap_token)((u_char *)output_message_buffer->value +
+ len);
+ token->EC[0] = 0;
+ token->EC[1] = 0;
+ token->RRC[0] = 0;
+ token->RRC[1] = 0;
+
+ ret = krb5_verify_checksum(context, crypto,
+ usage,
+ output_message_buffer->value,
+ len + sizeof(*token),
+ &cksum);
+ if (ret != 0) {
+ *minor_status = ret;
+ krb5_crypto_destroy(context, crypto);
+ _gsskrb5_release_buffer(minor_status, output_message_buffer);
+ return GSS_S_BAD_MIC;
+ }
+ }
+
+ krb5_crypto_destroy(context, crypto);
+
+ if (qop_state != NULL) {
+ *qop_state = GSS_C_QOP_DEFAULT;
+ }
+
+ *minor_status = 0;
+ return GSS_S_COMPLETE;
+}
+
+OM_uint32 _gssapi_mic_cfx(OM_uint32 *minor_status,
+ const gsskrb5_ctx context_handle,
+ krb5_context context,
+ gss_qop_t qop_req,
+ const gss_buffer_t message_buffer,
+ gss_buffer_t message_token,
+ krb5_keyblock *key)
+{
+ krb5_crypto crypto;
+ gss_cfx_mic_token token;
+ krb5_error_code ret;
+ unsigned usage;
+ Checksum cksum;
+ u_char *buf;
+ size_t len;
+ int32_t seq_number;
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret != 0) {
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+
+ len = message_buffer->length + sizeof(*token);
+ buf = malloc(len);
+ if (buf == NULL) {
+ *minor_status = ENOMEM;
+ krb5_crypto_destroy(context, crypto);
+ return GSS_S_FAILURE;
+ }
+
+ memcpy(buf, message_buffer->value, message_buffer->length);
+
+ token = (gss_cfx_mic_token)(buf + message_buffer->length);
+ token->TOK_ID[0] = 0x04;
+ token->TOK_ID[1] = 0x04;
+ token->Flags = 0;
+ if ((context_handle->more_flags & LOCAL) == 0)
+ token->Flags |= CFXSentByAcceptor;
+ if (context_handle->more_flags & ACCEPTOR_SUBKEY)
+ token->Flags |= CFXAcceptorSubkey;
+ memset(token->Filler, 0xFF, 5);
+
+ HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex);
+ krb5_auth_con_getlocalseqnumber(context,
+ context_handle->auth_context,
+ &seq_number);
+ _gsskrb5_encode_be_om_uint32(0, &token->SND_SEQ[0]);
+ _gsskrb5_encode_be_om_uint32(seq_number, &token->SND_SEQ[4]);
+ krb5_auth_con_setlocalseqnumber(context,
+ context_handle->auth_context,
+ ++seq_number);
+ HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
+
+ if (context_handle->more_flags & LOCAL) {
+ usage = KRB5_KU_USAGE_INITIATOR_SIGN;
+ } else {
+ usage = KRB5_KU_USAGE_ACCEPTOR_SIGN;
+ }
+
+ ret = krb5_create_checksum(context, crypto,
+ usage, 0, buf, len, &cksum);
+ if (ret != 0) {
+ *minor_status = ret;
+ krb5_crypto_destroy(context, crypto);
+ free(buf);
+ return GSS_S_FAILURE;
+ }
+ krb5_crypto_destroy(context, crypto);
+
+ /* Determine MIC length */
+ message_token->length = sizeof(*token) + cksum.checksum.length;
+ message_token->value = malloc(message_token->length);
+ if (message_token->value == NULL) {
+ *minor_status = ENOMEM;
+ free_Checksum(&cksum);
+ free(buf);
+ return GSS_S_FAILURE;
+ }
+
+ /* Token is { "header" | get_mic("header" | plaintext-data) } */
+ memcpy(message_token->value, token, sizeof(*token));
+ memcpy((u_char *)message_token->value + sizeof(*token),
+ cksum.checksum.data, cksum.checksum.length);
+
+ free_Checksum(&cksum);
+ free(buf);
+
+ *minor_status = 0;
+ return GSS_S_COMPLETE;
+}
+
+OM_uint32 _gssapi_verify_mic_cfx(OM_uint32 *minor_status,
+ const gsskrb5_ctx context_handle,
+ krb5_context context,
+ const gss_buffer_t message_buffer,
+ const gss_buffer_t token_buffer,
+ gss_qop_t *qop_state,
+ krb5_keyblock *key)
+{
+ krb5_crypto crypto;
+ gss_cfx_mic_token token;
+ u_char token_flags;
+ krb5_error_code ret;
+ unsigned usage;
+ OM_uint32 seq_number_lo, seq_number_hi;
+ u_char *buf, *p;
+ Checksum cksum;
+
+ *minor_status = 0;
+
+ if (token_buffer->length < sizeof(*token)) {
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+
+ p = token_buffer->value;
+
+ token = (gss_cfx_mic_token)p;
+
+ if (token->TOK_ID[0] != 0x04 || token->TOK_ID[1] != 0x04) {
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+
+ /* Ignore unknown flags */
+ token_flags = token->Flags & (CFXSentByAcceptor | CFXAcceptorSubkey);
+
+ if (token_flags & CFXSentByAcceptor) {
+ if ((context_handle->more_flags & LOCAL) == 0)
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+ if (context_handle->more_flags & ACCEPTOR_SUBKEY) {
+ if ((token_flags & CFXAcceptorSubkey) == 0)
+ return GSS_S_DEFECTIVE_TOKEN;
+ } else {
+ if (token_flags & CFXAcceptorSubkey)
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+
+ if (memcmp(token->Filler, "\xff\xff\xff\xff\xff", 5) != 0) {
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+
+ /*
+ * Check sequence number
+ */
+ _gsskrb5_decode_be_om_uint32(&token->SND_SEQ[0], &seq_number_hi);
+ _gsskrb5_decode_be_om_uint32(&token->SND_SEQ[4], &seq_number_lo);
+ if (seq_number_hi) {
+ *minor_status = ERANGE;
+ return GSS_S_UNSEQ_TOKEN;
+ }
+
+ HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex);
+ ret = _gssapi_msg_order_check(context_handle->order, seq_number_lo);
+ if (ret != 0) {
+ *minor_status = 0;
+ HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
+ return ret;
+ }
+ HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
+
+ /*
+ * Verify checksum
+ */
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret != 0) {
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+
+ ret = krb5_crypto_get_checksum_type(context, crypto,
+ &cksum.cksumtype);
+ if (ret != 0) {
+ *minor_status = ret;
+ krb5_crypto_destroy(context, crypto);
+ return GSS_S_FAILURE;
+ }
+
+ cksum.checksum.data = p + sizeof(*token);
+ cksum.checksum.length = token_buffer->length - sizeof(*token);
+
+ if (context_handle->more_flags & LOCAL) {
+ usage = KRB5_KU_USAGE_ACCEPTOR_SIGN;
+ } else {
+ usage = KRB5_KU_USAGE_INITIATOR_SIGN;
+ }
+
+ buf = malloc(message_buffer->length + sizeof(*token));
+ if (buf == NULL) {
+ *minor_status = ENOMEM;
+ krb5_crypto_destroy(context, crypto);
+ return GSS_S_FAILURE;
+ }
+ memcpy(buf, message_buffer->value, message_buffer->length);
+ memcpy(buf + message_buffer->length, token, sizeof(*token));
+
+ ret = krb5_verify_checksum(context, crypto,
+ usage,
+ buf,
+ sizeof(*token) + message_buffer->length,
+ &cksum);
+ krb5_crypto_destroy(context, crypto);
+ if (ret != 0) {
+ *minor_status = ret;
+ free(buf);
+ return GSS_S_BAD_MIC;
+ }
+
+ free(buf);
+
+ if (qop_state != NULL) {
+ *qop_state = GSS_C_QOP_DEFAULT;
+ }
+
+ return GSS_S_COMPLETE;
+}
diff --git a/source4/heimdal/lib/gssapi/krb5/cfx.h b/source4/heimdal/lib/gssapi/krb5/cfx.h
new file mode 100644
index 0000000000..c30ed07840
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/krb5/cfx.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2003, PADL Software Pty Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of PADL Software nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef GSSAPI_CFX_H_
+#define GSSAPI_CFX_H_ 1
+
+/*
+ * Implementation of draft-ietf-krb-wg-gssapi-cfx-01.txt
+ */
+
+typedef struct gss_cfx_mic_token_desc_struct {
+ u_char TOK_ID[2]; /* 04 04 */
+ u_char Flags;
+ u_char Filler[5];
+ u_char SND_SEQ[8];
+} gss_cfx_mic_token_desc, *gss_cfx_mic_token;
+
+typedef struct gss_cfx_wrap_token_desc_struct {
+ u_char TOK_ID[2]; /* 04 05 */
+ u_char Flags;
+ u_char Filler;
+ u_char EC[2];
+ u_char RRC[2];
+ u_char SND_SEQ[8];
+} gss_cfx_wrap_token_desc, *gss_cfx_wrap_token;
+
+typedef struct gss_cfx_delete_token_desc_struct {
+ u_char TOK_ID[2]; /* 05 04 */
+ u_char Flags;
+ u_char Filler[5];
+ u_char SND_SEQ[8];
+} gss_cfx_delete_token_desc, *gss_cfx_delete_token;
+
+#endif /* GSSAPI_CFX_H_ */
diff --git a/source4/heimdal/lib/gssapi/krb5/compare_name.c b/source4/heimdal/lib/gssapi/krb5/compare_name.c
new file mode 100644
index 0000000000..d92f3ef405
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/krb5/compare_name.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 1997-2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5/gsskrb5_locl.h"
+
+RCSID("$Id$");
+
+OM_uint32 _gsskrb5_compare_name
+ (OM_uint32 * minor_status,
+ const gss_name_t name1,
+ const gss_name_t name2,
+ int * name_equal
+ )
+{
+ krb5_const_principal princ1 = (krb5_const_principal)name1;
+ krb5_const_principal princ2 = (krb5_const_principal)name2;
+ krb5_context context;
+
+ GSSAPI_KRB5_INIT(&context);
+
+ *name_equal = krb5_principal_compare (context,
+ princ1, princ2);
+ *minor_status = 0;
+ return GSS_S_COMPLETE;
+}
diff --git a/source4/heimdal/lib/gssapi/krb5/compat.c b/source4/heimdal/lib/gssapi/krb5/compat.c
new file mode 100644
index 0000000000..ee0d07d983
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/krb5/compat.c
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2003 - 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5/gsskrb5_locl.h"
+
+RCSID("$Id$");
+
+
+static krb5_error_code
+check_compat(OM_uint32 *minor_status,
+ krb5_context context, krb5_const_principal name,
+ const char *option, krb5_boolean *compat,
+ krb5_boolean match_val)
+{
+ krb5_error_code ret = 0;
+ char **p, **q;
+ krb5_principal match;
+
+
+ p = krb5_config_get_strings(context, NULL, "gssapi",
+ option, NULL);
+ if(p == NULL)
+ return 0;
+
+ match = NULL;
+ for(q = p; *q; q++) {
+ ret = krb5_parse_name(context, *q, &match);
+ if (ret)
+ break;
+
+ if (krb5_principal_match(context, name, match)) {
+ *compat = match_val;
+ break;
+ }
+
+ krb5_free_principal(context, match);
+ match = NULL;
+ }
+ if (match)
+ krb5_free_principal(context, match);
+ krb5_config_free_strings(p);
+
+ if (ret) {
+ if (minor_status)
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+
+ return 0;
+}
+
+/*
+ * ctx->ctx_id_mutex is assumed to be locked
+ */
+
+OM_uint32
+_gss_DES3_get_mic_compat(OM_uint32 *minor_status,
+ gsskrb5_ctx ctx,
+ krb5_context context)
+{
+ krb5_boolean use_compat = FALSE;
+ OM_uint32 ret;
+
+ if ((ctx->more_flags & COMPAT_OLD_DES3_SELECTED) == 0) {
+ ret = check_compat(minor_status, context, ctx->target,
+ "broken_des3_mic", &use_compat, TRUE);
+ if (ret)
+ return ret;
+ ret = check_compat(minor_status, context, ctx->target,
+ "correct_des3_mic", &use_compat, FALSE);
+ if (ret)
+ return ret;
+
+ if (use_compat)
+ ctx->more_flags |= COMPAT_OLD_DES3;
+ ctx->more_flags |= COMPAT_OLD_DES3_SELECTED;
+ }
+ return 0;
+}
+
+#if 0
+OM_uint32
+gss_krb5_compat_des3_mic(OM_uint32 *minor_status, gss_ctx_id_t ctx, int on)
+{
+ *minor_status = 0;
+
+ HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
+ if (on) {
+ ctx->more_flags |= COMPAT_OLD_DES3;
+ } else {
+ ctx->more_flags &= ~COMPAT_OLD_DES3;
+ }
+ ctx->more_flags |= COMPAT_OLD_DES3_SELECTED;
+ HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
+
+ return 0;
+}
+#endif
diff --git a/source4/heimdal/lib/gssapi/krb5/context_time.c b/source4/heimdal/lib/gssapi/krb5/context_time.c
new file mode 100644
index 0000000000..3854b68782
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/krb5/context_time.c
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 1997 - 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5/gsskrb5_locl.h"
+
+RCSID("$Id$");
+
+OM_uint32
+_gsskrb5_lifetime_left(OM_uint32 *minor_status,
+ krb5_context context,
+ OM_uint32 lifetime,
+ OM_uint32 *lifetime_rec)
+{
+ krb5_timestamp timeret;
+ krb5_error_code kret;
+
+ if (lifetime == 0) {
+ *lifetime_rec = GSS_C_INDEFINITE;
+ return GSS_S_COMPLETE;
+ }
+
+ kret = krb5_timeofday(context, &timeret);
+ if (kret) {
+ *minor_status = kret;
+ return GSS_S_FAILURE;
+ }
+
+ if (lifetime < timeret)
+ *lifetime_rec = 0;
+ else
+ *lifetime_rec = lifetime - timeret;
+
+ return GSS_S_COMPLETE;
+}
+
+
+OM_uint32 _gsskrb5_context_time
+ (OM_uint32 * minor_status,
+ const gss_ctx_id_t context_handle,
+ OM_uint32 * time_rec
+ )
+{
+ krb5_context context;
+ OM_uint32 lifetime;
+ OM_uint32 major_status;
+ const gsskrb5_ctx ctx = (const gsskrb5_ctx) context_handle;
+
+ GSSAPI_KRB5_INIT (&context);
+
+ HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
+ lifetime = ctx->lifetime;
+ HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
+
+ major_status = _gsskrb5_lifetime_left(minor_status, context,
+ lifetime, time_rec);
+ if (major_status != GSS_S_COMPLETE)
+ return major_status;
+
+ *minor_status = 0;
+
+ if (*time_rec == 0)
+ return GSS_S_CONTEXT_EXPIRED;
+
+ return GSS_S_COMPLETE;
+}
diff --git a/source4/heimdal/lib/gssapi/krb5/copy_ccache.c b/source4/heimdal/lib/gssapi/krb5/copy_ccache.c
new file mode 100644
index 0000000000..8961642671
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/krb5/copy_ccache.c
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2000 - 2001, 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5/gsskrb5_locl.h"
+
+RCSID("$Id$");
+
+#if 0
+OM_uint32
+gss_krb5_copy_ccache(OM_uint32 *minor_status,
+ krb5_context context,
+ gss_cred_id_t cred,
+ krb5_ccache out)
+{
+ krb5_error_code kret;
+
+ HEIMDAL_MUTEX_lock(&cred->cred_id_mutex);
+
+ if (cred->ccache == NULL) {
+ HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex);
+ *minor_status = EINVAL;
+ return GSS_S_FAILURE;
+ }
+
+ kret = krb5_cc_copy_cache(context, cred->ccache, out);
+ HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex);
+ if (kret) {
+ *minor_status = kret;
+ return GSS_S_FAILURE;
+ }
+ *minor_status = 0;
+ return GSS_S_COMPLETE;
+}
+#endif
+
+
+OM_uint32
+_gsskrb5_import_cred(OM_uint32 *minor_status,
+ krb5_ccache id,
+ krb5_principal keytab_principal,
+ krb5_keytab keytab,
+ gss_cred_id_t *cred)
+{
+ krb5_context context;
+ krb5_error_code kret;
+ gsskrb5_cred handle;
+ OM_uint32 ret;
+
+ *cred = NULL;
+
+ GSSAPI_KRB5_INIT (&context);
+
+ handle = calloc(1, sizeof(*handle));
+ if (handle == NULL) {
+ _gsskrb5_clear_status ();
+ *minor_status = ENOMEM;
+ return (GSS_S_FAILURE);
+ }
+ HEIMDAL_MUTEX_init(&handle->cred_id_mutex);
+
+ handle->usage = 0;
+
+ if (id) {
+ char *str;
+
+ handle->usage |= GSS_C_INITIATE;
+
+ kret = krb5_cc_get_principal(context, id,
+ &handle->principal);
+ if (kret) {
+ free(handle);
+ *minor_status = kret;
+ return GSS_S_FAILURE;
+ }
+
+ if (keytab_principal) {
+ krb5_boolean match;
+
+ match = krb5_principal_compare(context,
+ handle->principal,
+ keytab_principal);
+ if (match == FALSE) {
+ krb5_free_principal(context, handle->principal);
+ free(handle);
+ _gsskrb5_clear_status ();
+ *minor_status = EINVAL;
+ return GSS_S_FAILURE;
+ }
+ }
+
+ ret = __gsskrb5_ccache_lifetime(minor_status,
+ context,
+ id,
+ handle->principal,
+ &handle->lifetime);
+ if (ret != GSS_S_COMPLETE) {
+ krb5_free_principal(context, handle->principal);
+ free(handle);
+ return ret;
+ }
+
+
+ kret = krb5_cc_get_full_name(context, id, &str);
+ if (kret)
+ goto out;
+
+ kret = krb5_cc_resolve(context, str, &handle->ccache);
+ free(str);
+ if (kret)
+ goto out;
+ }
+
+
+ if (keytab) {
+ char *str;
+
+ handle->usage |= GSS_C_ACCEPT;
+
+ if (keytab_principal && handle->principal == NULL) {
+ kret = krb5_copy_principal(context,
+ keytab_principal,
+ &handle->principal);
+ if (kret)
+ goto out;
+ }
+
+ kret = krb5_kt_get_full_name(context, keytab, &str);
+ if (kret)
+ goto out;
+
+ kret = krb5_kt_resolve(context, str, &handle->keytab);
+ free(str);
+ if (kret)
+ goto out;
+ }
+
+
+ if (id || keytab) {
+ ret = gss_create_empty_oid_set(minor_status, &handle->mechanisms);
+ if (ret == GSS_S_COMPLETE)
+ ret = gss_add_oid_set_member(minor_status, GSS_KRB5_MECHANISM,
+ &handle->mechanisms);
+ if (ret != GSS_S_COMPLETE) {
+ kret = *minor_status;
+ goto out;
+ }
+ }
+
+ *minor_status = 0;
+ *cred = (gss_cred_id_t)handle;
+ return GSS_S_COMPLETE;
+
+out:
+ gss_release_oid_set(minor_status, &handle->mechanisms);
+ if (handle->ccache)
+ krb5_cc_close(context, handle->ccache);
+ if (handle->keytab)
+ krb5_kt_close(context, handle->keytab);
+ if (handle->principal)
+ krb5_free_principal(context, handle->principal);
+ HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex);
+ free(handle);
+ *minor_status = kret;
+ return GSS_S_FAILURE;
+}
diff --git a/source4/heimdal/lib/gssapi/krb5/decapsulate.c b/source4/heimdal/lib/gssapi/krb5/decapsulate.c
new file mode 100644
index 0000000000..22386fa737
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/krb5/decapsulate.c
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5/gsskrb5_locl.h"
+
+RCSID("$Id$");
+
+/*
+ * return the length of the mechanism in token or -1
+ * (which implies that the token was bad - GSS_S_DEFECTIVE_TOKEN
+ */
+
+ssize_t
+_gsskrb5_get_mech (const u_char *ptr,
+ size_t total_len,
+ const u_char **mech_ret)
+{
+ size_t len, len_len, mech_len, foo;
+ const u_char *p = ptr;
+ int e;
+
+ if (total_len < 1)
+ return -1;
+ if (*p++ != 0x60)
+ return -1;
+ e = der_get_length (p, total_len - 1, &len, &len_len);
+ if (e || 1 + len_len + len != total_len)
+ return -1;
+ p += len_len;
+ if (*p++ != 0x06)
+ return -1;
+ e = der_get_length (p, total_len - 1 - len_len - 1,
+ &mech_len, &foo);
+ if (e)
+ return -1;
+ p += foo;
+ *mech_ret = p;
+ return mech_len;
+}
+
+OM_uint32
+_gssapi_verify_mech_header(u_char **str,
+ size_t total_len,
+ gss_OID mech)
+{
+ const u_char *p;
+ ssize_t mech_len;
+
+ mech_len = _gsskrb5_get_mech (*str, total_len, &p);
+ if (mech_len < 0)
+ return GSS_S_DEFECTIVE_TOKEN;
+
+ if (mech_len != mech->length)
+ return GSS_S_BAD_MECH;
+ if (memcmp(p,
+ mech->elements,
+ mech->length) != 0)
+ return GSS_S_BAD_MECH;
+ p += mech_len;
+ *str = rk_UNCONST(p);
+ return GSS_S_COMPLETE;
+}
+
+OM_uint32
+_gsskrb5_verify_header(u_char **str,
+ size_t total_len,
+ const void *type,
+ gss_OID oid)
+{
+ OM_uint32 ret;
+ size_t len;
+ u_char *p = *str;
+
+ ret = _gssapi_verify_mech_header(str, total_len, oid);
+ if (ret)
+ return ret;
+
+ len = total_len - (*str - p);
+
+ if (len < 2)
+ return GSS_S_DEFECTIVE_TOKEN;
+
+ if (memcmp (*str, type, 2) != 0)
+ return GSS_S_DEFECTIVE_TOKEN;
+ *str += 2;
+
+ return 0;
+}
+
+/*
+ * Remove the GSS-API wrapping from `in_token' giving `out_data.
+ * Does not copy data, so just free `in_token'.
+ */
+
+OM_uint32
+_gssapi_decapsulate(
+ OM_uint32 *minor_status,
+ gss_buffer_t input_token_buffer,
+ krb5_data *out_data,
+ const gss_OID mech
+)
+{
+ u_char *p;
+ OM_uint32 ret;
+
+ p = input_token_buffer->value;
+ ret = _gssapi_verify_mech_header(&p,
+ input_token_buffer->length,
+ mech);
+ if (ret) {
+ *minor_status = 0;
+ return ret;
+ }
+
+ out_data->length = input_token_buffer->length -
+ (p - (u_char *)input_token_buffer->value);
+ out_data->data = p;
+ return GSS_S_COMPLETE;
+}
+
+/*
+ * Remove the GSS-API wrapping from `in_token' giving `out_data.
+ * Does not copy data, so just free `in_token'.
+ */
+
+OM_uint32
+_gsskrb5_decapsulate(OM_uint32 *minor_status,
+ gss_buffer_t input_token_buffer,
+ krb5_data *out_data,
+ const void *type,
+ gss_OID oid)
+{
+ u_char *p;
+ OM_uint32 ret;
+
+ p = input_token_buffer->value;
+ ret = _gsskrb5_verify_header(&p,
+ input_token_buffer->length,
+ type,
+ oid);
+ if (ret) {
+ *minor_status = 0;
+ return ret;
+ }
+
+ out_data->length = input_token_buffer->length -
+ (p - (u_char *)input_token_buffer->value);
+ out_data->data = p;
+ return GSS_S_COMPLETE;
+}
+
+/*
+ * Verify padding of a gss wrapped message and return its length.
+ */
+
+OM_uint32
+_gssapi_verify_pad(gss_buffer_t wrapped_token,
+ size_t datalen,
+ size_t *padlen)
+{
+ u_char *pad;
+ size_t padlength;
+ int i;
+
+ pad = (u_char *)wrapped_token->value + wrapped_token->length - 1;
+ padlength = *pad;
+
+ if (padlength > datalen)
+ return GSS_S_BAD_MECH;
+
+ for (i = padlength; i > 0 && *pad == padlength; i--, pad--)
+ ;
+ if (i != 0)
+ return GSS_S_BAD_MIC;
+
+ *padlen = padlength;
+
+ return 0;
+}
diff --git a/source4/heimdal/lib/gssapi/krb5/delete_sec_context.c b/source4/heimdal/lib/gssapi/krb5/delete_sec_context.c
new file mode 100644
index 0000000000..5ccfe9d015
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/krb5/delete_sec_context.c
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 1997 - 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5/gsskrb5_locl.h"
+
+RCSID("$Id$");
+
+OM_uint32
+_gsskrb5_delete_sec_context(OM_uint32 * minor_status,
+ gss_ctx_id_t * context_handle,
+ gss_buffer_t output_token)
+{
+ krb5_context context;
+ gsskrb5_ctx ctx;
+
+ GSSAPI_KRB5_INIT (&context);
+
+ *minor_status = 0;
+
+ if (output_token) {
+ output_token->length = 0;
+ output_token->value = NULL;
+ }
+
+ if (*context_handle == GSS_C_NO_CONTEXT)
+ return GSS_S_COMPLETE;
+
+ ctx = (gsskrb5_ctx) *context_handle;
+ *context_handle = GSS_C_NO_CONTEXT;
+
+ HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
+
+ krb5_auth_con_free (context, ctx->auth_context);
+ if (ctx->kcred)
+ krb5_free_creds(context, ctx->kcred);
+ if(ctx->source)
+ krb5_free_principal (context, ctx->source);
+ if(ctx->target)
+ krb5_free_principal (context, ctx->target);
+ if (ctx->ticket)
+ krb5_free_ticket (context, ctx->ticket);
+ if(ctx->order)
+ _gssapi_msg_order_destroy(&ctx->order);
+ if (ctx->service_keyblock)
+ krb5_free_keyblock (context, ctx->service_keyblock);
+ krb5_data_free(&ctx->fwd_data);
+
+ HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
+ HEIMDAL_MUTEX_destroy(&ctx->ctx_id_mutex);
+ memset(ctx, 0, sizeof(*ctx));
+ free (ctx);
+ return GSS_S_COMPLETE;
+}
diff --git a/source4/heimdal/lib/gssapi/krb5/display_name.c b/source4/heimdal/lib/gssapi/krb5/display_name.c
new file mode 100644
index 0000000000..d1834ebaf8
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/krb5/display_name.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 1997 - 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5/gsskrb5_locl.h"
+
+RCSID("$Id$");
+
+OM_uint32 _gsskrb5_display_name
+ (OM_uint32 * minor_status,
+ const gss_name_t input_name,
+ gss_buffer_t output_name_buffer,
+ gss_OID * output_name_type
+ )
+{
+ krb5_context context;
+ krb5_const_principal name = (krb5_const_principal)input_name;
+ krb5_error_code kret;
+ char *buf;
+ size_t len;
+
+ GSSAPI_KRB5_INIT (&context);
+
+ kret = krb5_unparse_name_flags (context, name,
+ KRB5_PRINCIPAL_UNPARSE_DISPLAY, &buf);
+ if (kret) {
+ *minor_status = kret;
+ return GSS_S_FAILURE;
+ }
+ len = strlen (buf);
+ output_name_buffer->length = len;
+ output_name_buffer->value = malloc(len + 1);
+ if (output_name_buffer->value == NULL) {
+ free (buf);
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+ memcpy (output_name_buffer->value, buf, len);
+ ((char *)output_name_buffer->value)[len] = '\0';
+ free (buf);
+ if (output_name_type)
+ *output_name_type = GSS_KRB5_NT_PRINCIPAL_NAME;
+ *minor_status = 0;
+ return GSS_S_COMPLETE;
+}
diff --git a/source4/heimdal/lib/gssapi/krb5/display_status.c b/source4/heimdal/lib/gssapi/krb5/display_status.c
new file mode 100644
index 0000000000..18622610d5
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/krb5/display_status.c
@@ -0,0 +1,199 @@
+/*
+ * Copyright (c) 1998 - 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5/gsskrb5_locl.h"
+
+RCSID("$Id$");
+
+static const char *
+calling_error(OM_uint32 v)
+{
+ static const char *msgs[] = {
+ NULL, /* 0 */
+ "A required input parameter could not be read.", /* */
+ "A required output parameter could not be written.", /* */
+ "A parameter was malformed"
+ };
+
+ v >>= GSS_C_CALLING_ERROR_OFFSET;
+
+ if (v == 0)
+ return "";
+ else if (v >= sizeof(msgs)/sizeof(*msgs))
+ return "unknown calling error";
+ else
+ return msgs[v];
+}
+
+static const char *
+routine_error(OM_uint32 v)
+{
+ static const char *msgs[] = {
+ NULL, /* 0 */
+ "An unsupported mechanism was requested",
+ "An invalid name was supplied",
+ "A supplied name was of an unsupported type",
+ "Incorrect channel bindings were supplied",
+ "An invalid status code was supplied",
+ "A token had an invalid MIC",
+ "No credentials were supplied, "
+ "or the credentials were unavailable or inaccessible.",
+ "No context has been established",
+ "A token was invalid",
+ "A credential was invalid",
+ "The referenced credentials have expired",
+ "The context has expired",
+ "Miscellaneous failure (see text)",
+ "The quality-of-protection requested could not be provide",
+ "The operation is forbidden by local security policy",
+ "The operation or option is not available",
+ "The requested credential element already exists",
+ "The provided name was not a mechanism name.",
+ };
+
+ v >>= GSS_C_ROUTINE_ERROR_OFFSET;
+
+ if (v == 0)
+ return "";
+ else if (v >= sizeof(msgs)/sizeof(*msgs))
+ return "unknown routine error";
+ else
+ return msgs[v];
+}
+
+static const char *
+supplementary_error(OM_uint32 v)
+{
+ static const char *msgs[] = {
+ "normal completion",
+ "continuation call to routine required",
+ "duplicate per-message token detected",
+ "timed-out per-message token detected",
+ "reordered (early) per-message token detected",
+ "skipped predecessor token(s) detected"
+ };
+
+ v >>= GSS_C_SUPPLEMENTARY_OFFSET;
+
+ if (v >= sizeof(msgs)/sizeof(*msgs))
+ return "unknown routine error";
+ else
+ return msgs[v];
+}
+
+void
+_gsskrb5_clear_status (void)
+{
+ krb5_context context;
+
+ if (_gsskrb5_init (&context) != 0)
+ return;
+ krb5_clear_error_message(context);
+}
+
+void
+_gsskrb5_set_status (int ret, const char *fmt, ...)
+{
+ krb5_context context;
+ va_list args;
+ char *str;
+
+ if (_gsskrb5_init (&context) != 0)
+ return;
+
+ va_start(args, fmt);
+ vasprintf(&str, fmt, args);
+ va_end(args);
+ if (str) {
+ krb5_set_error_message(context, ret, "%s", str);
+ free(str);
+ }
+}
+
+OM_uint32 _gsskrb5_display_status
+(OM_uint32 *minor_status,
+ OM_uint32 status_value,
+ int status_type,
+ const gss_OID mech_type,
+ OM_uint32 *message_context,
+ gss_buffer_t status_string)
+{
+ krb5_context context;
+ char *buf;
+
+ GSSAPI_KRB5_INIT (&context);
+
+ status_string->length = 0;
+ status_string->value = NULL;
+
+ if (gss_oid_equal(mech_type, GSS_C_NO_OID) == 0 &&
+ gss_oid_equal(mech_type, GSS_KRB5_MECHANISM) == 0) {
+ *minor_status = 0;
+ return GSS_C_GSS_CODE;
+ }
+
+ if (status_type == GSS_C_GSS_CODE) {
+ if (GSS_SUPPLEMENTARY_INFO(status_value))
+ asprintf(&buf, "%s",
+ supplementary_error(GSS_SUPPLEMENTARY_INFO(status_value)));
+ else
+ asprintf (&buf, "%s %s",
+ calling_error(GSS_CALLING_ERROR(status_value)),
+ routine_error(GSS_ROUTINE_ERROR(status_value)));
+ } else if (status_type == GSS_C_MECH_CODE) {
+ const char *buf2 = krb5_get_error_message(context, status_value);
+ if (buf2) {
+ buf = strdup(buf2);
+ krb5_free_error_message(context, buf2);
+ } else {
+ asprintf(&buf, "unknown mech error-code %u",
+ (unsigned)status_value);
+ }
+ } else {
+ *minor_status = EINVAL;
+ return GSS_S_BAD_STATUS;
+ }
+
+ if (buf == NULL) {
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+
+ *message_context = 0;
+ *minor_status = 0;
+
+ status_string->length = strlen(buf);
+ status_string->value = buf;
+
+ return GSS_S_COMPLETE;
+}
diff --git a/source4/heimdal/lib/gssapi/krb5/duplicate_name.c b/source4/heimdal/lib/gssapi/krb5/duplicate_name.c
new file mode 100644
index 0000000000..6c281f43de
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/krb5/duplicate_name.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 1997 - 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5/gsskrb5_locl.h"
+
+RCSID("$Id$");
+
+OM_uint32 _gsskrb5_duplicate_name (
+ OM_uint32 * minor_status,
+ const gss_name_t src_name,
+ gss_name_t * dest_name
+ )
+{
+ krb5_const_principal src = (krb5_const_principal)src_name;
+ krb5_context context;
+ krb5_principal dest;
+ krb5_error_code kret;
+
+ GSSAPI_KRB5_INIT (&context);
+
+ kret = krb5_copy_principal (context, src, &dest);
+ if (kret) {
+ *minor_status = kret;
+ return GSS_S_FAILURE;
+ } else {
+ *dest_name = (gss_name_t)dest;
+ *minor_status = 0;
+ return GSS_S_COMPLETE;
+ }
+}
diff --git a/source4/heimdal/lib/gssapi/krb5/encapsulate.c b/source4/heimdal/lib/gssapi/krb5/encapsulate.c
new file mode 100644
index 0000000000..19c6ec8ca7
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/krb5/encapsulate.c
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 1997 - 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5/gsskrb5_locl.h"
+
+RCSID("$Id$");
+
+void
+_gssapi_encap_length (size_t data_len,
+ size_t *len,
+ size_t *total_len,
+ const gss_OID mech)
+{
+ size_t len_len;
+
+ *len = 1 + 1 + mech->length + data_len;
+
+ len_len = der_length_len(*len);
+
+ *total_len = 1 + len_len + *len;
+}
+
+void
+_gsskrb5_encap_length (size_t data_len,
+ size_t *len,
+ size_t *total_len,
+ const gss_OID mech)
+{
+ _gssapi_encap_length(data_len + 2, len, total_len, mech);
+}
+
+void *
+_gsskrb5_make_header (void *ptr,
+ size_t len,
+ const void *type,
+ const gss_OID mech)
+{
+ u_char *p = ptr;
+ p = _gssapi_make_mech_header(p, len, mech);
+ memcpy (p, type, 2);
+ p += 2;
+ return p;
+}
+
+void *
+_gssapi_make_mech_header(void *ptr,
+ size_t len,
+ const gss_OID mech)
+{
+ u_char *p = ptr;
+ int e;
+ size_t len_len, foo;
+
+ *p++ = 0x60;
+ len_len = der_length_len(len);
+ e = der_put_length (p + len_len - 1, len_len, len, &foo);
+ if(e || foo != len_len)
+ abort ();
+ p += len_len;
+ *p++ = 0x06;
+ *p++ = mech->length;
+ memcpy (p, mech->elements, mech->length);
+ p += mech->length;
+ return p;
+}
+
+/*
+ * Give it a krb5_data and it will encapsulate with extra GSS-API wrappings.
+ */
+
+OM_uint32
+_gssapi_encapsulate(
+ OM_uint32 *minor_status,
+ const krb5_data *in_data,
+ gss_buffer_t output_token,
+ const gss_OID mech
+)
+{
+ size_t len, outer_len;
+ void *p;
+
+ _gssapi_encap_length (in_data->length, &len, &outer_len, mech);
+
+ output_token->length = outer_len;
+ output_token->value = malloc (outer_len);
+ if (output_token->value == NULL) {
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+
+ p = _gssapi_make_mech_header (output_token->value, len, mech);
+ memcpy (p, in_data->data, in_data->length);
+ return GSS_S_COMPLETE;
+}
+
+/*
+ * Give it a krb5_data and it will encapsulate with extra GSS-API krb5
+ * wrappings.
+ */
+
+OM_uint32
+_gsskrb5_encapsulate(
+ OM_uint32 *minor_status,
+ const krb5_data *in_data,
+ gss_buffer_t output_token,
+ const void *type,
+ const gss_OID mech
+)
+{
+ size_t len, outer_len;
+ u_char *p;
+
+ _gsskrb5_encap_length (in_data->length, &len, &outer_len, mech);
+
+ output_token->length = outer_len;
+ output_token->value = malloc (outer_len);
+ if (output_token->value == NULL) {
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+
+ p = _gsskrb5_make_header (output_token->value, len, type, mech);
+ memcpy (p, in_data->data, in_data->length);
+ return GSS_S_COMPLETE;
+}
diff --git a/source4/heimdal/lib/gssapi/krb5/export_name.c b/source4/heimdal/lib/gssapi/krb5/export_name.c
new file mode 100644
index 0000000000..b28777ea2e
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/krb5/export_name.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 1997, 1999, 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5/gsskrb5_locl.h"
+
+RCSID("$Id$");
+
+OM_uint32 _gsskrb5_export_name
+ (OM_uint32 * minor_status,
+ const gss_name_t input_name,
+ gss_buffer_t exported_name
+ )
+{
+ krb5_context context;
+ krb5_const_principal princ = (krb5_const_principal)input_name;
+ krb5_error_code kret;
+ char *buf, *name;
+ size_t len;
+
+ GSSAPI_KRB5_INIT (&context);
+
+ kret = krb5_unparse_name (context, princ, &name);
+ if (kret) {
+ *minor_status = kret;
+ return GSS_S_FAILURE;
+ }
+ len = strlen (name);
+
+ exported_name->length = 10 + len + GSS_KRB5_MECHANISM->length;
+ exported_name->value = malloc(exported_name->length);
+ if (exported_name->value == NULL) {
+ free (name);
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+
+ /* TOK, MECH_OID_LEN, DER(MECH_OID), NAME_LEN, NAME */
+
+ buf = exported_name->value;
+ memcpy(buf, "\x04\x01", 2);
+ buf += 2;
+ buf[0] = ((GSS_KRB5_MECHANISM->length + 2) >> 8) & 0xff;
+ buf[1] = (GSS_KRB5_MECHANISM->length + 2) & 0xff;
+ buf+= 2;
+ buf[0] = 0x06;
+ buf[1] = (GSS_KRB5_MECHANISM->length) & 0xFF;
+ buf+= 2;
+
+ memcpy(buf, GSS_KRB5_MECHANISM->elements, GSS_KRB5_MECHANISM->length);
+ buf += GSS_KRB5_MECHANISM->length;
+
+ buf[0] = (len >> 24) & 0xff;
+ buf[1] = (len >> 16) & 0xff;
+ buf[2] = (len >> 8) & 0xff;
+ buf[3] = (len) & 0xff;
+ buf += 4;
+
+ memcpy (buf, name, len);
+
+ free (name);
+
+ *minor_status = 0;
+ return GSS_S_COMPLETE;
+}
diff --git a/source4/heimdal/lib/gssapi/krb5/export_sec_context.c b/source4/heimdal/lib/gssapi/krb5/export_sec_context.c
new file mode 100644
index 0000000000..842921fe50
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/krb5/export_sec_context.c
@@ -0,0 +1,240 @@
+/*
+ * Copyright (c) 1999 - 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5/gsskrb5_locl.h"
+
+RCSID("$Id$");
+
+OM_uint32
+_gsskrb5_export_sec_context (
+ OM_uint32 * minor_status,
+ gss_ctx_id_t * context_handle,
+ gss_buffer_t interprocess_token
+ )
+{
+ krb5_context context;
+ const gsskrb5_ctx ctx = (const gsskrb5_ctx) *context_handle;
+ krb5_storage *sp;
+ krb5_auth_context ac;
+ OM_uint32 ret = GSS_S_COMPLETE;
+ krb5_data data;
+ gss_buffer_desc buffer;
+ int flags;
+ OM_uint32 minor;
+ krb5_error_code kret;
+
+ GSSAPI_KRB5_INIT (&context);
+
+ HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
+
+ if (!(ctx->flags & GSS_C_TRANS_FLAG)) {
+ HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
+ *minor_status = 0;
+ return GSS_S_UNAVAILABLE;
+ }
+
+ sp = krb5_storage_emem ();
+ if (sp == NULL) {
+ HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+ ac = ctx->auth_context;
+
+ /* flagging included fields */
+
+ flags = 0;
+ if (ac->local_address)
+ flags |= SC_LOCAL_ADDRESS;
+ if (ac->remote_address)
+ flags |= SC_REMOTE_ADDRESS;
+ if (ac->keyblock)
+ flags |= SC_KEYBLOCK;
+ if (ac->local_subkey)
+ flags |= SC_LOCAL_SUBKEY;
+ if (ac->remote_subkey)
+ flags |= SC_REMOTE_SUBKEY;
+
+ kret = krb5_store_int32 (sp, flags);
+ if (kret) {
+ *minor_status = kret;
+ goto failure;
+ }
+
+ /* marshall auth context */
+
+ kret = krb5_store_int32 (sp, ac->flags);
+ if (kret) {
+ *minor_status = kret;
+ goto failure;
+ }
+ if (ac->local_address) {
+ kret = krb5_store_address (sp, *ac->local_address);
+ if (kret) {
+ *minor_status = kret;
+ goto failure;
+ }
+ }
+ if (ac->remote_address) {
+ kret = krb5_store_address (sp, *ac->remote_address);
+ if (kret) {
+ *minor_status = kret;
+ goto failure;
+ }
+ }
+ kret = krb5_store_int16 (sp, ac->local_port);
+ if (kret) {
+ *minor_status = kret;
+ goto failure;
+ }
+ kret = krb5_store_int16 (sp, ac->remote_port);
+ if (kret) {
+ *minor_status = kret;
+ goto failure;
+ }
+ if (ac->keyblock) {
+ kret = krb5_store_keyblock (sp, *ac->keyblock);
+ if (kret) {
+ *minor_status = kret;
+ goto failure;
+ }
+ }
+ if (ac->local_subkey) {
+ kret = krb5_store_keyblock (sp, *ac->local_subkey);
+ if (kret) {
+ *minor_status = kret;
+ goto failure;
+ }
+ }
+ if (ac->remote_subkey) {
+ kret = krb5_store_keyblock (sp, *ac->remote_subkey);
+ if (kret) {
+ *minor_status = kret;
+ goto failure;
+ }
+ }
+ kret = krb5_store_int32 (sp, ac->local_seqnumber);
+ if (kret) {
+ *minor_status = kret;
+ goto failure;
+ }
+ kret = krb5_store_int32 (sp, ac->remote_seqnumber);
+ if (kret) {
+ *minor_status = kret;
+ goto failure;
+ }
+
+ kret = krb5_store_int32 (sp, ac->keytype);
+ if (kret) {
+ *minor_status = kret;
+ goto failure;
+ }
+ kret = krb5_store_int32 (sp, ac->cksumtype);
+ if (kret) {
+ *minor_status = kret;
+ goto failure;
+ }
+
+ /* names */
+
+ ret = _gsskrb5_export_name (minor_status,
+ (gss_name_t)ctx->source, &buffer);
+ if (ret)
+ goto failure;
+ data.data = buffer.value;
+ data.length = buffer.length;
+ kret = krb5_store_data (sp, data);
+ _gsskrb5_release_buffer (&minor, &buffer);
+ if (kret) {
+ *minor_status = kret;
+ goto failure;
+ }
+
+ ret = _gsskrb5_export_name (minor_status,
+ (gss_name_t)ctx->target, &buffer);
+ if (ret)
+ goto failure;
+ data.data = buffer.value;
+ data.length = buffer.length;
+
+ ret = GSS_S_FAILURE;
+
+ kret = krb5_store_data (sp, data);
+ _gsskrb5_release_buffer (&minor, &buffer);
+ if (kret) {
+ *minor_status = kret;
+ goto failure;
+ }
+
+ kret = krb5_store_int32 (sp, ctx->flags);
+ if (kret) {
+ *minor_status = kret;
+ goto failure;
+ }
+ kret = krb5_store_int32 (sp, ctx->more_flags);
+ if (kret) {
+ *minor_status = kret;
+ goto failure;
+ }
+ kret = krb5_store_int32 (sp, ctx->lifetime);
+ if (kret) {
+ *minor_status = kret;
+ goto failure;
+ }
+ kret = _gssapi_msg_order_export(sp, ctx->order);
+ if (kret ) {
+ *minor_status = kret;
+ goto failure;
+ }
+
+ kret = krb5_storage_to_data (sp, &data);
+ krb5_storage_free (sp);
+ if (kret) {
+ HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
+ *minor_status = kret;
+ return GSS_S_FAILURE;
+ }
+ interprocess_token->length = data.length;
+ interprocess_token->value = data.data;
+ HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
+ ret = _gsskrb5_delete_sec_context (minor_status, context_handle,
+ GSS_C_NO_BUFFER);
+ if (ret != GSS_S_COMPLETE)
+ _gsskrb5_release_buffer (NULL, interprocess_token);
+ *minor_status = 0;
+ return ret;
+ failure:
+ HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
+ krb5_storage_free (sp);
+ return ret;
+}
diff --git a/source4/heimdal/lib/gssapi/krb5/external.c b/source4/heimdal/lib/gssapi/krb5/external.c
new file mode 100644
index 0000000000..4efa61fd70
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/krb5/external.c
@@ -0,0 +1,479 @@
+/*
+ * Copyright (c) 1997 - 2000 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5/gsskrb5_locl.h"
+#include <gssapi_mech.h>
+
+RCSID("$Id$");
+
+/*
+ * The implementation must reserve static storage for a
+ * gss_OID_desc object containing the value
+ * {10, (void *)"\x2a\x86\x48\x86\xf7\x12"
+ * "\x01\x02\x01\x01"},
+ * corresponding to an object-identifier value of
+ * {iso(1) member-body(2) United States(840) mit(113554)
+ * infosys(1) gssapi(2) generic(1) user_name(1)}. The constant
+ * GSS_C_NT_USER_NAME should be initialized to point
+ * to that gss_OID_desc.
+ */
+
+static gss_OID_desc gss_c_nt_user_name_oid_desc =
+ {10, rk_UNCONST("\x2a\x86\x48\x86\xf7\x12" "\x01\x02\x01\x01")};
+
+gss_OID GSSAPI_LIB_VARIABLE GSS_C_NT_USER_NAME =
+ &gss_c_nt_user_name_oid_desc;
+
+/*
+ * The implementation must reserve static storage for a
+ * gss_OID_desc object containing the value
+ * {10, (void *)"\x2a\x86\x48\x86\xf7\x12"
+ * "\x01\x02\x01\x02"},
+ * corresponding to an object-identifier value of
+ * {iso(1) member-body(2) United States(840) mit(113554)
+ * infosys(1) gssapi(2) generic(1) machine_uid_name(2)}.
+ * The constant GSS_C_NT_MACHINE_UID_NAME should be
+ * initialized to point to that gss_OID_desc.
+ */
+
+static gss_OID_desc gss_c_nt_machine_uid_name_oid_desc =
+ {10, rk_UNCONST("\x2a\x86\x48\x86\xf7\x12" "\x01\x02\x01\x02")};
+
+gss_OID GSSAPI_LIB_VARIABLE GSS_C_NT_MACHINE_UID_NAME =
+ &gss_c_nt_machine_uid_name_oid_desc;
+
+/*
+ * The implementation must reserve static storage for a
+ * gss_OID_desc object containing the value
+ * {10, (void *)"\x2a\x86\x48\x86\xf7\x12"
+ * "\x01\x02\x01\x03"},
+ * corresponding to an object-identifier value of
+ * {iso(1) member-body(2) United States(840) mit(113554)
+ * infosys(1) gssapi(2) generic(1) string_uid_name(3)}.
+ * The constant GSS_C_NT_STRING_UID_NAME should be
+ * initialized to point to that gss_OID_desc.
+ */
+
+static gss_OID_desc gss_c_nt_string_uid_name_oid_desc =
+ {10, rk_UNCONST("\x2a\x86\x48\x86\xf7\x12" "\x01\x02\x01\x03")};
+
+gss_OID GSSAPI_LIB_VARIABLE GSS_C_NT_STRING_UID_NAME =
+ &gss_c_nt_string_uid_name_oid_desc;
+
+/*
+ * The implementation must reserve static storage for a
+ * gss_OID_desc object containing the value
+ * {6, (void *)"\x2b\x06\x01\x05\x06\x02"},
+ * corresponding to an object-identifier value of
+ * {iso(1) org(3) dod(6) internet(1) security(5)
+ * nametypes(6) gss-host-based-services(2)). The constant
+ * GSS_C_NT_HOSTBASED_SERVICE_X should be initialized to point
+ * to that gss_OID_desc. This is a deprecated OID value, and
+ * implementations wishing to support hostbased-service names
+ * should instead use the GSS_C_NT_HOSTBASED_SERVICE OID,
+ * defined below, to identify such names;
+ * GSS_C_NT_HOSTBASED_SERVICE_X should be accepted a synonym
+ * for GSS_C_NT_HOSTBASED_SERVICE when presented as an input
+ * parameter, but should not be emitted by GSS-API
+ * implementations
+ */
+
+static gss_OID_desc gss_c_nt_hostbased_service_x_oid_desc =
+ {6, rk_UNCONST("\x2b\x06\x01\x05\x06\x02")};
+
+gss_OID GSSAPI_LIB_VARIABLE GSS_C_NT_HOSTBASED_SERVICE_X =
+ &gss_c_nt_hostbased_service_x_oid_desc;
+
+/*
+ * The implementation must reserve static storage for a
+ * gss_OID_desc object containing the value
+ * {10, (void *)"\x2a\x86\x48\x86\xf7\x12"
+ * "\x01\x02\x01\x04"}, corresponding to an
+ * object-identifier value of {iso(1) member-body(2)
+ * Unites States(840) mit(113554) infosys(1) gssapi(2)
+ * generic(1) service_name(4)}. The constant
+ * GSS_C_NT_HOSTBASED_SERVICE should be initialized
+ * to point to that gss_OID_desc.
+ */
+static gss_OID_desc gss_c_nt_hostbased_service_oid_desc =
+ {10, rk_UNCONST("\x2a\x86\x48\x86\xf7\x12" "\x01\x02\x01\x04")};
+
+gss_OID GSSAPI_LIB_VARIABLE GSS_C_NT_HOSTBASED_SERVICE =
+ &gss_c_nt_hostbased_service_oid_desc;
+
+/*
+ * The implementation must reserve static storage for a
+ * gss_OID_desc object containing the value
+ * {6, (void *)"\x2b\x06\01\x05\x06\x03"},
+ * corresponding to an object identifier value of
+ * {1(iso), 3(org), 6(dod), 1(internet), 5(security),
+ * 6(nametypes), 3(gss-anonymous-name)}. The constant
+ * and GSS_C_NT_ANONYMOUS should be initialized to point
+ * to that gss_OID_desc.
+ */
+
+static gss_OID_desc gss_c_nt_anonymous_oid_desc =
+ {6, rk_UNCONST("\x2b\x06\01\x05\x06\x03")};
+
+gss_OID GSSAPI_LIB_VARIABLE GSS_C_NT_ANONYMOUS =
+ &gss_c_nt_anonymous_oid_desc;
+
+/*
+ * The implementation must reserve static storage for a
+ * gss_OID_desc object containing the value
+ * {6, (void *)"\x2b\x06\x01\x05\x06\x04"},
+ * corresponding to an object-identifier value of
+ * {1(iso), 3(org), 6(dod), 1(internet), 5(security),
+ * 6(nametypes), 4(gss-api-exported-name)}. The constant
+ * GSS_C_NT_EXPORT_NAME should be initialized to point
+ * to that gss_OID_desc.
+ */
+
+static gss_OID_desc gss_c_nt_export_name_oid_desc =
+ {6, rk_UNCONST("\x2b\x06\x01\x05\x06\x04") };
+
+gss_OID GSSAPI_LIB_VARIABLE GSS_C_NT_EXPORT_NAME =
+ &gss_c_nt_export_name_oid_desc;
+
+/*
+ * This name form shall be represented by the Object Identifier {iso(1)
+ * member-body(2) United States(840) mit(113554) infosys(1) gssapi(2)
+ * krb5(2) krb5_name(1)}. The recommended symbolic name for this type
+ * is "GSS_KRB5_NT_PRINCIPAL_NAME".
+ */
+
+static gss_OID_desc gss_krb5_nt_principal_name_oid_desc =
+ {10, rk_UNCONST("\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x01") };
+
+gss_OID GSSAPI_LIB_VARIABLE GSS_KRB5_NT_PRINCIPAL_NAME =
+ &gss_krb5_nt_principal_name_oid_desc;
+
+/*
+ * This name form shall be represented by the Object Identifier {iso(1)
+ * member-body(2) United States(840) mit(113554) infosys(1) gssapi(2)
+ * generic(1) user_name(1)}. The recommended symbolic name for this
+ * type is "GSS_KRB5_NT_USER_NAME".
+ */
+
+gss_OID GSSAPI_LIB_VARIABLE GSS_KRB5_NT_USER_NAME =
+ &gss_c_nt_user_name_oid_desc;
+
+/*
+ * This name form shall be represented by the Object Identifier {iso(1)
+ * member-body(2) United States(840) mit(113554) infosys(1) gssapi(2)
+ * generic(1) machine_uid_name(2)}. The recommended symbolic name for
+ * this type is "GSS_KRB5_NT_MACHINE_UID_NAME".
+ */
+
+gss_OID GSSAPI_LIB_VARIABLE GSS_KRB5_NT_MACHINE_UID_NAME =
+ &gss_c_nt_machine_uid_name_oid_desc;
+
+/*
+ * This name form shall be represented by the Object Identifier {iso(1)
+ * member-body(2) United States(840) mit(113554) infosys(1) gssapi(2)
+ * generic(1) string_uid_name(3)}. The recommended symbolic name for
+ * this type is "GSS_KRB5_NT_STRING_UID_NAME".
+ */
+
+gss_OID GSSAPI_LIB_VARIABLE GSS_KRB5_NT_STRING_UID_NAME =
+ &gss_c_nt_string_uid_name_oid_desc;
+
+/*
+ * To support ongoing experimentation, testing, and evolution of the
+ * specification, the Kerberos V5 GSS-API mechanism as defined in this
+ * and any successor memos will be identified with the following Object
+ * Identifier, as defined in RFC-1510, until the specification is
+ * advanced to the level of Proposed Standard RFC:
+ *
+ * {iso(1), org(3), dod(5), internet(1), security(5), kerberosv5(2)}
+ *
+ * Upon advancement to the level of Proposed Standard RFC, the Kerberos
+ * V5 GSS-API mechanism will be identified by an Object Identifier
+ * having the value:
+ *
+ * {iso(1) member-body(2) United States(840) mit(113554) infosys(1)
+ * gssapi(2) krb5(2)}
+ */
+
+#if 0 /* This is the old OID */
+
+static gss_OID_desc gss_krb5_mechanism_oid_desc =
+ {5, rk_UNCONST("\x2b\x05\x01\x05\x02")};
+
+#endif
+
+static gss_OID_desc gss_krb5_mechanism_oid_desc =
+ {9, rk_UNCONST("\x2a\x86\x48\x86\xf7\x12\x01\x02\x02") };
+
+gss_OID GSSAPI_LIB_VARIABLE GSS_KRB5_MECHANISM =
+ &gss_krb5_mechanism_oid_desc;
+
+/*
+ * draft-ietf-cat-iakerb-09, IAKERB:
+ * The mechanism ID for IAKERB proxy GSS-API Kerberos, in accordance
+ * with the mechanism proposed by SPNEGO [7] for negotiating protocol
+ * variations, is: {iso(1) org(3) dod(6) internet(1) security(5)
+ * mechanisms(5) iakerb(10) iakerbProxyProtocol(1)}. The proposed
+ * mechanism ID for IAKERB minimum messages GSS-API Kerberos, in
+ * accordance with the mechanism proposed by SPNEGO for negotiating
+ * protocol variations, is: {iso(1) org(3) dod(6) internet(1)
+ * security(5) mechanisms(5) iakerb(10)
+ * iakerbMinimumMessagesProtocol(2)}.
+ */
+
+static gss_OID_desc gss_iakerb_proxy_mechanism_oid_desc =
+ {7, rk_UNCONST("\x2b\x06\x01\x05\x05\x0a\x01")};
+
+gss_OID GSSAPI_LIB_VARIABLE GSS_IAKERB_PROXY_MECHANISM =
+ &gss_iakerb_proxy_mechanism_oid_desc;
+
+static gss_OID_desc gss_iakerb_min_msg_mechanism_oid_desc =
+ {7, rk_UNCONST("\x2b\x06\x01\x05\x05\x0a\x02") };
+
+gss_OID GSSAPI_LIB_VARIABLE GSS_IAKERB_MIN_MSG_MECHANISM =
+ &gss_iakerb_min_msg_mechanism_oid_desc;
+
+/*
+ *
+ */
+
+static gss_OID_desc gss_c_peer_has_updated_spnego_oid_desc =
+ {9, (void *)"\x2b\x06\x01\x04\x01\xa9\x4a\x13\x05"};
+
+gss_OID GSSAPI_LIB_VARIABLE GSS_C_PEER_HAS_UPDATED_SPNEGO =
+ &gss_c_peer_has_updated_spnego_oid_desc;
+
+/*
+ * 1.2.752.43.13 Heimdal GSS-API Extentions
+ */
+
+/* 1.2.752.43.13.1 */
+static gss_OID_desc gss_krb5_copy_ccache_x_oid_desc =
+ {6, rk_UNCONST("\x2a\x85\x70\x2b\x0d\x01")};
+
+gss_OID GSSAPI_LIB_VARIABLE GSS_KRB5_COPY_CCACHE_X =
+ &gss_krb5_copy_ccache_x_oid_desc;
+
+/* 1.2.752.43.13.2 */
+static gss_OID_desc gss_krb5_get_tkt_flags_x_oid_desc =
+ {6, rk_UNCONST("\x2a\x85\x70\x2b\x0d\x02")};
+
+gss_OID GSSAPI_LIB_VARIABLE GSS_KRB5_GET_TKT_FLAGS_X =
+ &gss_krb5_get_tkt_flags_x_oid_desc;
+
+/* 1.2.752.43.13.3 */
+static gss_OID_desc gss_krb5_extract_authz_data_from_sec_context_x_oid_desc =
+ {6, rk_UNCONST("\x2a\x85\x70\x2b\x0d\x03")};
+
+gss_OID GSSAPI_LIB_VARIABLE GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_X =
+ &gss_krb5_extract_authz_data_from_sec_context_x_oid_desc;
+
+/* 1.2.752.43.13.4 */
+static gss_OID_desc gss_krb5_compat_des3_mic_x_oid_desc =
+ {6, rk_UNCONST("\x2a\x85\x70\x2b\x0d\x04")};
+
+gss_OID GSSAPI_LIB_VARIABLE GSS_KRB5_COMPAT_DES3_MIC_X =
+ &gss_krb5_compat_des3_mic_x_oid_desc;
+
+/* 1.2.752.43.13.5 */
+static gss_OID_desc gss_krb5_register_acceptor_identity_x_desc =
+ {6, rk_UNCONST("\x2a\x85\x70\x2b\x0d\x05")};
+
+gss_OID GSSAPI_LIB_VARIABLE GSS_KRB5_REGISTER_ACCEPTOR_IDENTITY_X =
+ &gss_krb5_register_acceptor_identity_x_desc;
+
+/* 1.2.752.43.13.6 */
+static gss_OID_desc gss_krb5_export_lucid_context_x_desc =
+ {6, rk_UNCONST("\x2a\x85\x70\x2b\x0d\x06")};
+
+gss_OID GSSAPI_LIB_VARIABLE GSS_KRB5_EXPORT_LUCID_CONTEXT_X =
+ &gss_krb5_export_lucid_context_x_desc;
+
+/* 1.2.752.43.13.6.1 */
+static gss_OID_desc gss_krb5_export_lucid_context_v1_x_desc =
+ {7, rk_UNCONST("\x2a\x85\x70\x2b\x0d\x06\x01")};
+
+gss_OID GSSAPI_LIB_VARIABLE GSS_KRB5_EXPORT_LUCID_CONTEXT_V1_X =
+ &gss_krb5_export_lucid_context_v1_x_desc;
+
+/* 1.2.752.43.13.7 */
+static gss_OID_desc gss_krb5_set_dns_canonicalize_x_desc =
+ {6, rk_UNCONST("\x2a\x85\x70\x2b\x0d\x07")};
+
+gss_OID GSSAPI_LIB_VARIABLE GSS_KRB5_SET_DNS_CANONICALIZE_X =
+ &gss_krb5_set_dns_canonicalize_x_desc;
+
+/* 1.2.752.43.13.8 */
+static gss_OID_desc gss_krb5_get_subkey_x_desc =
+ {6, rk_UNCONST("\x2a\x85\x70\x2b\x0d\x08")};
+
+gss_OID GSSAPI_LIB_VARIABLE GSS_KRB5_GET_SUBKEY_X =
+ &gss_krb5_get_subkey_x_desc;
+
+/* 1.2.752.43.13.9 */
+static gss_OID_desc gss_krb5_get_initiator_subkey_x_desc =
+ {6, rk_UNCONST("\x2a\x85\x70\x2b\x0d\x09")};
+
+gss_OID GSSAPI_LIB_VARIABLE GSS_KRB5_GET_INITIATOR_SUBKEY_X =
+ &gss_krb5_get_initiator_subkey_x_desc;
+
+/* 1.2.752.43.13.10 */
+static gss_OID_desc gss_krb5_get_acceptor_subkey_x_desc =
+ {6, rk_UNCONST("\x2a\x85\x70\x2b\x0d\x0a")};
+
+gss_OID GSSAPI_LIB_VARIABLE GSS_KRB5_GET_ACCEPTOR_SUBKEY_X =
+ &gss_krb5_get_acceptor_subkey_x_desc;
+
+/* 1.2.752.43.13.11 */
+static gss_OID_desc gss_krb5_send_to_kdc_x_desc =
+ {6, rk_UNCONST("\x2a\x85\x70\x2b\x0d\x0b")};
+
+gss_OID GSSAPI_LIB_VARIABLE GSS_KRB5_SEND_TO_KDC_X =
+ &gss_krb5_send_to_kdc_x_desc;
+
+/* 1.2.752.43.13.12 */
+static gss_OID_desc gss_krb5_get_authtime_x_desc =
+ {6, rk_UNCONST("\x2a\x85\x70\x2b\x0d\x0c")};
+
+gss_OID GSSAPI_LIB_VARIABLE GSS_KRB5_GET_AUTHTIME_X =
+ &gss_krb5_get_authtime_x_desc;
+
+/* 1.2.752.43.13.13 */
+static gss_OID_desc gss_krb5_get_service_keyblock_x_desc =
+ {6, rk_UNCONST("\x2a\x85\x70\x2b\x0d\x0d")};
+
+gss_OID GSSAPI_LIB_VARIABLE GSS_KRB5_GET_SERVICE_KEYBLOCK_X =
+ &gss_krb5_get_service_keyblock_x_desc;
+
+/* 1.2.752.43.13.14 */
+static gss_OID_desc gss_krb5_set_allowable_enctypes_x_desc =
+ {6, rk_UNCONST("\x2a\x85\x70\x2b\x0d\x0e")};
+
+gss_OID GSSAPI_LIB_VARIABLE GSS_KRB5_SET_ALLOWABLE_ENCTYPES_X =
+ &gss_krb5_set_allowable_enctypes_x_desc;
+
+/* 1.2.752.43.13.15 */
+static gss_OID_desc gss_krb5_set_default_realm_x_desc =
+ {6, rk_UNCONST("\x2a\x85\x70\x2b\x0d\x0f")};
+
+gss_OID GSSAPI_LIB_VARIABLE GSS_KRB5_SET_DEFAULT_REALM_X =
+ &gss_krb5_set_default_realm_x_desc;
+
+/* 1.2.752.43.13.16 */
+static gss_OID_desc gss_krb5_ccache_name_x_desc =
+ {6, rk_UNCONST("\x2a\x85\x70\x2b\x0d\x10")};
+
+gss_OID GSSAPI_LIB_VARIABLE GSS_KRB5_CCACHE_NAME_X =
+ &gss_krb5_ccache_name_x_desc;
+
+/* 1.2.752.43.13.17 */
+static gss_OID_desc gss_krb5_set_time_offset_x_desc =
+ {6, rk_UNCONST("\x2a\x85\x70\x2b\x0d\x11")};
+
+gss_OID GSSAPI_LIB_VARIABLE GSS_KRB5_SET_TIME_OFFSET_X =
+ &gss_krb5_set_time_offset_x_desc;
+
+/* 1.2.752.43.13.18 */
+static gss_OID_desc gss_krb5_get_time_offset_x_desc =
+ {6, rk_UNCONST("\x2a\x85\x70\x2b\x0d\x12")};
+
+gss_OID GSSAPI_LIB_VARIABLE GSS_KRB5_GET_TIME_OFFSET_X =
+ &gss_krb5_get_time_offset_x_desc;
+
+/* 1.2.752.43.13.19 */
+static gss_OID_desc gss_krb5_plugin_register_x_desc =
+ {6, rk_UNCONST("\x2a\x85\x70\x2b\x0d\x13")};
+
+gss_OID GSSAPI_LIB_VARIABLE GSS_KRB5_PLUGIN_REGISTER_X =
+ &gss_krb5_plugin_register_x_desc;
+
+/* 1.2.752.43.14.1 */
+static gss_OID_desc gss_sasl_digest_md5_mechanism_desc =
+ {6, rk_UNCONST("\x2a\x85\x70\x2b\x0e\x01") };
+
+gss_OID GSSAPI_LIB_VARIABLE GSS_SASL_DIGEST_MD5_MECHANISM =
+ &gss_sasl_digest_md5_mechanism_desc;
+
+/*
+ * Context for krb5 calls.
+ */
+
+/*
+ *
+ */
+
+static gssapi_mech_interface_desc krb5_mech = {
+ GMI_VERSION,
+ "kerberos 5",
+ {9, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" },
+ _gsskrb5_acquire_cred,
+ _gsskrb5_release_cred,
+ _gsskrb5_init_sec_context,
+ _gsskrb5_accept_sec_context,
+ _gsskrb5_process_context_token,
+ _gsskrb5_delete_sec_context,
+ _gsskrb5_context_time,
+ _gsskrb5_get_mic,
+ _gsskrb5_verify_mic,
+ _gsskrb5_wrap,
+ _gsskrb5_unwrap,
+ _gsskrb5_display_status,
+ _gsskrb5_indicate_mechs,
+ _gsskrb5_compare_name,
+ _gsskrb5_display_name,
+ _gsskrb5_import_name,
+ _gsskrb5_export_name,
+ _gsskrb5_release_name,
+ _gsskrb5_inquire_cred,
+ _gsskrb5_inquire_context,
+ _gsskrb5_wrap_size_limit,
+ _gsskrb5_add_cred,
+ _gsskrb5_inquire_cred_by_mech,
+ _gsskrb5_export_sec_context,
+ _gsskrb5_import_sec_context,
+ _gsskrb5_inquire_names_for_mech,
+ _gsskrb5_inquire_mechs_for_name,
+ _gsskrb5_canonicalize_name,
+ _gsskrb5_duplicate_name,
+ _gsskrb5_inquire_sec_context_by_oid,
+ _gsskrb5_inquire_cred_by_oid,
+ _gsskrb5_set_sec_context_option,
+ _gsskrb5_set_cred_option,
+ _gsskrb5_pseudo_random
+};
+
+gssapi_mech_interface
+__gss_krb5_initialize(void)
+{
+ return &krb5_mech;
+}
diff --git a/source4/heimdal/lib/gssapi/krb5/get_mic.c b/source4/heimdal/lib/gssapi/krb5/get_mic.c
new file mode 100644
index 0000000000..199c414ef4
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/krb5/get_mic.c
@@ -0,0 +1,317 @@
+/*
+ * Copyright (c) 1997 - 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5/gsskrb5_locl.h"
+
+RCSID("$Id$");
+
+static OM_uint32
+mic_des
+ (OM_uint32 * minor_status,
+ const gsskrb5_ctx ctx,
+ krb5_context context,
+ gss_qop_t qop_req,
+ const gss_buffer_t message_buffer,
+ gss_buffer_t message_token,
+ krb5_keyblock *key
+ )
+{
+ u_char *p;
+ MD5_CTX md5;
+ u_char hash[16];
+ DES_key_schedule schedule;
+ DES_cblock deskey;
+ DES_cblock zero;
+ int32_t seq_number;
+ size_t len, total_len;
+
+ _gsskrb5_encap_length (22, &len, &total_len, GSS_KRB5_MECHANISM);
+
+ message_token->length = total_len;
+ message_token->value = malloc (total_len);
+ if (message_token->value == NULL) {
+ message_token->length = 0;
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+
+ p = _gsskrb5_make_header(message_token->value,
+ len,
+ "\x01\x01", /* TOK_ID */
+ GSS_KRB5_MECHANISM);
+
+ memcpy (p, "\x00\x00", 2); /* SGN_ALG = DES MAC MD5 */
+ p += 2;
+
+ memcpy (p, "\xff\xff\xff\xff", 4); /* Filler */
+ p += 4;
+
+ /* Fill in later (SND-SEQ) */
+ memset (p, 0, 16);
+ p += 16;
+
+ /* checksum */
+ MD5_Init (&md5);
+ MD5_Update (&md5, p - 24, 8);
+ MD5_Update (&md5, message_buffer->value, message_buffer->length);
+ MD5_Final (hash, &md5);
+
+ memset (&zero, 0, sizeof(zero));
+ memcpy (&deskey, key->keyvalue.data, sizeof(deskey));
+ DES_set_key_unchecked (&deskey, &schedule);
+ DES_cbc_cksum ((void *)hash, (void *)hash, sizeof(hash),
+ &schedule, &zero);
+ memcpy (p - 8, hash, 8); /* SGN_CKSUM */
+
+ HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
+ /* sequence number */
+ krb5_auth_con_getlocalseqnumber (context,
+ ctx->auth_context,
+ &seq_number);
+
+ p -= 16; /* SND_SEQ */
+ p[0] = (seq_number >> 0) & 0xFF;
+ p[1] = (seq_number >> 8) & 0xFF;
+ p[2] = (seq_number >> 16) & 0xFF;
+ p[3] = (seq_number >> 24) & 0xFF;
+ memset (p + 4,
+ (ctx->more_flags & LOCAL) ? 0 : 0xFF,
+ 4);
+
+ DES_set_key_unchecked (&deskey, &schedule);
+ DES_cbc_encrypt ((void *)p, (void *)p, 8,
+ &schedule, (DES_cblock *)(p + 8), DES_ENCRYPT);
+
+ krb5_auth_con_setlocalseqnumber (context,
+ ctx->auth_context,
+ ++seq_number);
+ HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
+
+ memset (deskey, 0, sizeof(deskey));
+ memset (&schedule, 0, sizeof(schedule));
+
+ *minor_status = 0;
+ return GSS_S_COMPLETE;
+}
+
+static OM_uint32
+mic_des3
+ (OM_uint32 * minor_status,
+ const gsskrb5_ctx ctx,
+ krb5_context context,
+ gss_qop_t qop_req,
+ const gss_buffer_t message_buffer,
+ gss_buffer_t message_token,
+ krb5_keyblock *key
+ )
+{
+ u_char *p;
+ Checksum cksum;
+ u_char seq[8];
+
+ int32_t seq_number;
+ size_t len, total_len;
+
+ krb5_crypto crypto;
+ krb5_error_code kret;
+ krb5_data encdata;
+ char *tmp;
+ char ivec[8];
+
+ _gsskrb5_encap_length (36, &len, &total_len, GSS_KRB5_MECHANISM);
+
+ message_token->length = total_len;
+ message_token->value = malloc (total_len);
+ if (message_token->value == NULL) {
+ message_token->length = 0;
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+
+ p = _gsskrb5_make_header(message_token->value,
+ len,
+ "\x01\x01", /* TOK-ID */
+ GSS_KRB5_MECHANISM);
+
+ memcpy (p, "\x04\x00", 2); /* SGN_ALG = HMAC SHA1 DES3-KD */
+ p += 2;
+
+ memcpy (p, "\xff\xff\xff\xff", 4); /* filler */
+ p += 4;
+
+ /* this should be done in parts */
+
+ tmp = malloc (message_buffer->length + 8);
+ if (tmp == NULL) {
+ free (message_token->value);
+ message_token->value = NULL;
+ message_token->length = 0;
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+ memcpy (tmp, p - 8, 8);
+ memcpy (tmp + 8, message_buffer->value, message_buffer->length);
+
+ kret = krb5_crypto_init(context, key, 0, &crypto);
+ if (kret) {
+ free (message_token->value);
+ message_token->value = NULL;
+ message_token->length = 0;
+ free (tmp);
+ *minor_status = kret;
+ return GSS_S_FAILURE;
+ }
+
+ kret = krb5_create_checksum (context,
+ crypto,
+ KRB5_KU_USAGE_SIGN,
+ 0,
+ tmp,
+ message_buffer->length + 8,
+ &cksum);
+ free (tmp);
+ krb5_crypto_destroy (context, crypto);
+ if (kret) {
+ free (message_token->value);
+ message_token->value = NULL;
+ message_token->length = 0;
+ *minor_status = kret;
+ return GSS_S_FAILURE;
+ }
+
+ memcpy (p + 8, cksum.checksum.data, cksum.checksum.length);
+
+ HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
+ /* sequence number */
+ krb5_auth_con_getlocalseqnumber (context,
+ ctx->auth_context,
+ &seq_number);
+
+ seq[0] = (seq_number >> 0) & 0xFF;
+ seq[1] = (seq_number >> 8) & 0xFF;
+ seq[2] = (seq_number >> 16) & 0xFF;
+ seq[3] = (seq_number >> 24) & 0xFF;
+ memset (seq + 4,
+ (ctx->more_flags & LOCAL) ? 0 : 0xFF,
+ 4);
+
+ kret = krb5_crypto_init(context, key,
+ ETYPE_DES3_CBC_NONE, &crypto);
+ if (kret) {
+ free (message_token->value);
+ message_token->value = NULL;
+ message_token->length = 0;
+ *minor_status = kret;
+ return GSS_S_FAILURE;
+ }
+
+ if (ctx->more_flags & COMPAT_OLD_DES3)
+ memset(ivec, 0, 8);
+ else
+ memcpy(ivec, p + 8, 8);
+
+ kret = krb5_encrypt_ivec (context,
+ crypto,
+ KRB5_KU_USAGE_SEQ,
+ seq, 8, &encdata, ivec);
+ krb5_crypto_destroy (context, crypto);
+ if (kret) {
+ free (message_token->value);
+ message_token->value = NULL;
+ message_token->length = 0;
+ *minor_status = kret;
+ return GSS_S_FAILURE;
+ }
+
+ assert (encdata.length == 8);
+
+ memcpy (p, encdata.data, encdata.length);
+ krb5_data_free (&encdata);
+
+ krb5_auth_con_setlocalseqnumber (context,
+ ctx->auth_context,
+ ++seq_number);
+ HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
+
+ free_Checksum (&cksum);
+ *minor_status = 0;
+ return GSS_S_COMPLETE;
+}
+
+OM_uint32 _gsskrb5_get_mic
+ (OM_uint32 * minor_status,
+ const gss_ctx_id_t context_handle,
+ gss_qop_t qop_req,
+ const gss_buffer_t message_buffer,
+ gss_buffer_t message_token
+ )
+{
+ krb5_context context;
+ const gsskrb5_ctx ctx = (const gsskrb5_ctx) context_handle;
+ krb5_keyblock *key;
+ OM_uint32 ret;
+ krb5_keytype keytype;
+
+ GSSAPI_KRB5_INIT (&context);
+
+ HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
+ ret = _gsskrb5i_get_token_key(ctx, context, &key);
+ HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
+ if (ret) {
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+ krb5_enctype_to_keytype (context, key->keytype, &keytype);
+
+ switch (keytype) {
+ case KEYTYPE_DES :
+ ret = mic_des (minor_status, ctx, context, qop_req,
+ message_buffer, message_token, key);
+ break;
+ case KEYTYPE_DES3 :
+ ret = mic_des3 (minor_status, ctx, context, qop_req,
+ message_buffer, message_token, key);
+ break;
+ case KEYTYPE_ARCFOUR:
+ case KEYTYPE_ARCFOUR_56:
+ ret = _gssapi_get_mic_arcfour (minor_status, ctx, context, qop_req,
+ message_buffer, message_token, key);
+ break;
+ default :
+ ret = _gssapi_mic_cfx (minor_status, ctx, context, qop_req,
+ message_buffer, message_token, key);
+ break;
+ }
+ krb5_free_keyblock (context, key);
+ return ret;
+}
diff --git a/source4/heimdal/lib/gssapi/krb5/gkrb5_err.et b/source4/heimdal/lib/gssapi/krb5/gkrb5_err.et
new file mode 100644
index 0000000000..3c23412a6a
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/krb5/gkrb5_err.et
@@ -0,0 +1,31 @@
+#
+# extended gss krb5 error messages
+#
+
+id "$Id$"
+
+error_table gk5
+
+prefix GSS_KRB5_S
+
+error_code G_BAD_SERVICE_NAME, "No @ in SERVICE-NAME name string"
+error_code G_BAD_STRING_UID, "STRING-UID-NAME contains nondigits"
+error_code G_NOUSER, "UID does not resolve to username"
+error_code G_VALIDATE_FAILED, "Validation error"
+error_code G_BUFFER_ALLOC, "Couldn't allocate gss_buffer_t data"
+error_code G_BAD_MSG_CTX, "Message context invalid"
+error_code G_WRONG_SIZE, "Buffer is the wrong size"
+error_code G_BAD_USAGE, "Credential usage type is unknown"
+error_code G_UNKNOWN_QOP, "Unknown quality of protection specified"
+
+index 128
+
+error_code KG_CCACHE_NOMATCH, "Principal in credential cache does not match desired name"
+error_code KG_KEYTAB_NOMATCH, "No principal in keytab matches desired name"
+error_code KG_TGT_MISSING, "Credential cache has no TGT"
+error_code KG_NO_SUBKEY, "Authenticator has no subkey"
+error_code KG_CONTEXT_ESTABLISHED, "Context is already fully established"
+error_code KG_BAD_SIGN_TYPE, "Unknown signature type in token"
+error_code KG_BAD_LENGTH, "Invalid field length in token"
+error_code KG_CTX_INCOMPLETE, "Attempt to use incomplete security context"
+error_code KG_INPUT_TOO_LONG, "Input too long"
diff --git a/source4/heimdal/lib/gssapi/krb5/gsskrb5_locl.h b/source4/heimdal/lib/gssapi/krb5/gsskrb5_locl.h
new file mode 100644
index 0000000000..6db842395f
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/krb5/gsskrb5_locl.h
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef GSSKRB5_LOCL_H
+#define GSSKRB5_LOCL_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <krb5_locl.h>
+#include <gkrb5_err.h>
+#include <gssapi.h>
+#include <gssapi_mech.h>
+#include <assert.h>
+
+#include "cfx.h"
+
+/*
+ *
+ */
+
+struct gss_msg_order;
+
+typedef struct {
+ struct krb5_auth_context_data *auth_context;
+ krb5_principal source, target;
+#define IS_DCE_STYLE(ctx) (((ctx)->flags & GSS_C_DCE_STYLE) != 0)
+ OM_uint32 flags;
+ enum { LOCAL = 1, OPEN = 2,
+ COMPAT_OLD_DES3 = 4,
+ COMPAT_OLD_DES3_SELECTED = 8,
+ ACCEPTOR_SUBKEY = 16,
+ RETRIED = 32,
+ CLOSE_CCACHE = 64
+ } more_flags;
+ enum gss_ctx_id_t_state {
+ /* initiator states */
+ INITIATOR_START,
+ INITIATOR_RESTART,
+ INITIATOR_WAIT_FOR_MUTAL,
+ INITIATOR_READY,
+ /* acceptor states */
+ ACCEPTOR_START,
+ ACCEPTOR_WAIT_FOR_DCESTYLE,
+ ACCEPTOR_READY
+ } state;
+ krb5_creds *kcred;
+ krb5_ccache ccache;
+ struct krb5_ticket *ticket;
+ OM_uint32 lifetime;
+ HEIMDAL_MUTEX ctx_id_mutex;
+ struct gss_msg_order *order;
+ krb5_keyblock *service_keyblock;
+ krb5_data fwd_data;
+} *gsskrb5_ctx;
+
+typedef struct {
+ krb5_principal principal;
+ int cred_flags;
+#define GSS_CF_DESTROY_CRED_ON_RELEASE 1
+#define GSS_CF_NO_CI_FLAGS 2
+ struct krb5_keytab_data *keytab;
+ OM_uint32 lifetime;
+ gss_cred_usage_t usage;
+ gss_OID_set mechanisms;
+ struct krb5_ccache_data *ccache;
+ HEIMDAL_MUTEX cred_id_mutex;
+ krb5_enctype *enctypes;
+} *gsskrb5_cred;
+
+typedef struct Principal *gsskrb5_name;
+
+/*
+ *
+ */
+
+extern krb5_keytab _gsskrb5_keytab;
+extern HEIMDAL_MUTEX gssapi_keytab_mutex;
+
+struct gssapi_thr_context {
+ HEIMDAL_MUTEX mutex;
+ char *error_string;
+};
+
+/*
+ * Prototypes
+ */
+
+#include <krb5/gsskrb5-private.h>
+
+#define GSSAPI_KRB5_INIT(ctx) do { \
+ krb5_error_code kret_gss_init; \
+ if((kret_gss_init = _gsskrb5_init (ctx)) != 0) { \
+ *minor_status = kret_gss_init; \
+ return GSS_S_FAILURE; \
+ } \
+} while (0)
+
+/* sec_context flags */
+
+#define SC_LOCAL_ADDRESS 0x01
+#define SC_REMOTE_ADDRESS 0x02
+#define SC_KEYBLOCK 0x04
+#define SC_LOCAL_SUBKEY 0x08
+#define SC_REMOTE_SUBKEY 0x10
+
+/* type to signal that that dns canon maybe should be done */
+#define MAGIC_HOSTBASED_NAME_TYPE 4711
+
+#endif
diff --git a/source4/heimdal/lib/gssapi/krb5/import_name.c b/source4/heimdal/lib/gssapi/krb5/import_name.c
new file mode 100644
index 0000000000..2f6b002f30
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/krb5/import_name.c
@@ -0,0 +1,252 @@
+/*
+ * Copyright (c) 1997 - 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5/gsskrb5_locl.h"
+
+RCSID("$Id$");
+
+static OM_uint32
+parse_krb5_name (OM_uint32 *minor_status,
+ krb5_context context,
+ const char *name,
+ gss_name_t *output_name)
+{
+ krb5_principal princ;
+ krb5_error_code kerr;
+
+ kerr = krb5_parse_name (context, name, &princ);
+
+ if (kerr == 0) {
+ *output_name = (gss_name_t)princ;
+ return GSS_S_COMPLETE;
+ }
+ *minor_status = kerr;
+
+ if (kerr == KRB5_PARSE_ILLCHAR || kerr == KRB5_PARSE_MALFORMED)
+ return GSS_S_BAD_NAME;
+
+ return GSS_S_FAILURE;
+}
+
+static OM_uint32
+import_krb5_name (OM_uint32 *minor_status,
+ krb5_context context,
+ const gss_buffer_t input_name_buffer,
+ gss_name_t *output_name)
+{
+ OM_uint32 ret;
+ char *tmp;
+
+ tmp = malloc (input_name_buffer->length + 1);
+ if (tmp == NULL) {
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+ memcpy (tmp,
+ input_name_buffer->value,
+ input_name_buffer->length);
+ tmp[input_name_buffer->length] = '\0';
+
+ ret = parse_krb5_name(minor_status, context, tmp, output_name);
+ free(tmp);
+
+ return ret;
+}
+
+OM_uint32
+_gsskrb5_canon_name(OM_uint32 *minor_status, krb5_context context,
+ int use_dns, gss_name_t name, krb5_principal *out)
+{
+ krb5_principal p = (krb5_principal)name;
+ krb5_error_code ret;
+ char *hostname = NULL, *service;
+
+ *minor_status = 0;
+
+ /* If its not a hostname */
+ if (krb5_principal_get_type(context, p) != MAGIC_HOSTBASED_NAME_TYPE) {
+ ret = krb5_copy_principal(context, p, out);
+ } else if (!use_dns) {
+ ret = krb5_copy_principal(context, p, out);
+ if (ret == 0)
+ krb5_principal_set_type(context, *out, KRB5_NT_SRV_HST);
+ } else {
+ if (p->name.name_string.len == 0)
+ return GSS_S_BAD_NAME;
+ else if (p->name.name_string.len > 1)
+ hostname = p->name.name_string.val[1];
+
+ service = p->name.name_string.val[0];
+
+ ret = krb5_sname_to_principal(context,
+ hostname,
+ service,
+ KRB5_NT_SRV_HST,
+ out);
+ }
+
+ if (ret) {
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+
+ return 0;
+}
+
+
+static OM_uint32
+import_hostbased_name (OM_uint32 *minor_status,
+ krb5_context context,
+ const gss_buffer_t input_name_buffer,
+ gss_name_t *output_name)
+{
+ krb5_principal princ = NULL;
+ krb5_error_code kerr;
+ char *tmp, *p, *host = NULL;
+
+ tmp = malloc (input_name_buffer->length + 1);
+ if (tmp == NULL) {
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+ memcpy (tmp,
+ input_name_buffer->value,
+ input_name_buffer->length);
+ tmp[input_name_buffer->length] = '\0';
+
+ p = strchr (tmp, '@');
+ if (p != NULL) {
+ *p = '\0';
+ host = p + 1;
+ }
+
+ kerr = krb5_make_principal(context, &princ, NULL, tmp, host, NULL);
+ free (tmp);
+ *minor_status = kerr;
+ if (kerr == KRB5_PARSE_ILLCHAR || kerr == KRB5_PARSE_MALFORMED)
+ return GSS_S_BAD_NAME;
+ else if (kerr)
+ return GSS_S_FAILURE;
+
+ krb5_principal_set_type(context, princ, MAGIC_HOSTBASED_NAME_TYPE);
+ *output_name = (gss_name_t)princ;
+
+ return 0;
+}
+
+static OM_uint32
+import_export_name (OM_uint32 *minor_status,
+ krb5_context context,
+ const gss_buffer_t input_name_buffer,
+ gss_name_t *output_name)
+{
+ unsigned char *p;
+ uint32_t length;
+ OM_uint32 ret;
+ char *name;
+
+ if (input_name_buffer->length < 10 + GSS_KRB5_MECHANISM->length)
+ return GSS_S_BAD_NAME;
+
+ /* TOK, MECH_OID_LEN, DER(MECH_OID), NAME_LEN, NAME */
+
+ p = input_name_buffer->value;
+
+ if (memcmp(&p[0], "\x04\x01\x00", 3) != 0 ||
+ p[3] != GSS_KRB5_MECHANISM->length + 2 ||
+ p[4] != 0x06 ||
+ p[5] != GSS_KRB5_MECHANISM->length ||
+ memcmp(&p[6], GSS_KRB5_MECHANISM->elements,
+ GSS_KRB5_MECHANISM->length) != 0)
+ return GSS_S_BAD_NAME;
+
+ p += 6 + GSS_KRB5_MECHANISM->length;
+
+ length = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3];
+ p += 4;
+
+ if (length > input_name_buffer->length - 10 - GSS_KRB5_MECHANISM->length)
+ return GSS_S_BAD_NAME;
+
+ name = malloc(length + 1);
+ if (name == NULL) {
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+ memcpy(name, p, length);
+ name[length] = '\0';
+
+ ret = parse_krb5_name(minor_status, context, name, output_name);
+ free(name);
+
+ return ret;
+}
+
+OM_uint32 _gsskrb5_import_name
+ (OM_uint32 * minor_status,
+ const gss_buffer_t input_name_buffer,
+ const gss_OID input_name_type,
+ gss_name_t * output_name
+ )
+{
+ krb5_context context;
+
+ *minor_status = 0;
+ *output_name = GSS_C_NO_NAME;
+
+ GSSAPI_KRB5_INIT (&context);
+
+ if (gss_oid_equal(input_name_type, GSS_C_NT_HOSTBASED_SERVICE) ||
+ gss_oid_equal(input_name_type, GSS_C_NT_HOSTBASED_SERVICE_X))
+ return import_hostbased_name (minor_status,
+ context,
+ input_name_buffer,
+ output_name);
+ else if (gss_oid_equal(input_name_type, GSS_C_NO_OID)
+ || gss_oid_equal(input_name_type, GSS_C_NT_USER_NAME)
+ || gss_oid_equal(input_name_type, GSS_KRB5_NT_PRINCIPAL_NAME))
+ /* default printable syntax */
+ return import_krb5_name (minor_status,
+ context,
+ input_name_buffer,
+ output_name);
+ else if (gss_oid_equal(input_name_type, GSS_C_NT_EXPORT_NAME)) {
+ return import_export_name(minor_status,
+ context,
+ input_name_buffer,
+ output_name);
+ } else {
+ *minor_status = 0;
+ return GSS_S_BAD_NAMETYPE;
+ }
+}
diff --git a/source4/heimdal/lib/gssapi/krb5/import_sec_context.c b/source4/heimdal/lib/gssapi/krb5/import_sec_context.c
new file mode 100644
index 0000000000..e1e8e551b4
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/krb5/import_sec_context.c
@@ -0,0 +1,229 @@
+/*
+ * Copyright (c) 1999 - 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5/gsskrb5_locl.h"
+
+RCSID("$Id$");
+
+OM_uint32
+_gsskrb5_import_sec_context (
+ OM_uint32 * minor_status,
+ const gss_buffer_t interprocess_token,
+ gss_ctx_id_t * context_handle
+ )
+{
+ OM_uint32 ret = GSS_S_FAILURE;
+ krb5_context context;
+ krb5_error_code kret;
+ krb5_storage *sp;
+ krb5_auth_context ac;
+ krb5_address local, remote;
+ krb5_address *localp, *remotep;
+ krb5_data data;
+ gss_buffer_desc buffer;
+ krb5_keyblock keyblock;
+ int32_t flags, tmp;
+ gsskrb5_ctx ctx;
+ gss_name_t name;
+
+ GSSAPI_KRB5_INIT (&context);
+
+ *context_handle = GSS_C_NO_CONTEXT;
+
+ localp = remotep = NULL;
+
+ sp = krb5_storage_from_mem (interprocess_token->value,
+ interprocess_token->length);
+ if (sp == NULL) {
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+
+ ctx = calloc(1, sizeof(*ctx));
+ if (ctx == NULL) {
+ *minor_status = ENOMEM;
+ krb5_storage_free (sp);
+ return GSS_S_FAILURE;
+ }
+ HEIMDAL_MUTEX_init(&ctx->ctx_id_mutex);
+
+ kret = krb5_auth_con_init (context,
+ &ctx->auth_context);
+ if (kret) {
+ *minor_status = kret;
+ ret = GSS_S_FAILURE;
+ goto failure;
+ }
+
+ /* flags */
+
+ *minor_status = 0;
+
+ if (krb5_ret_int32 (sp, &flags) != 0)
+ goto failure;
+
+ /* retrieve the auth context */
+
+ ac = ctx->auth_context;
+ if (krb5_ret_int32 (sp, &tmp) != 0)
+ goto failure;
+ ac->flags = tmp;
+ if (flags & SC_LOCAL_ADDRESS) {
+ if (krb5_ret_address (sp, localp = &local) != 0)
+ goto failure;
+ }
+
+ if (flags & SC_REMOTE_ADDRESS) {
+ if (krb5_ret_address (sp, remotep = &remote) != 0)
+ goto failure;
+ }
+
+ krb5_auth_con_setaddrs (context, ac, localp, remotep);
+ if (localp)
+ krb5_free_address (context, localp);
+ if (remotep)
+ krb5_free_address (context, remotep);
+ localp = remotep = NULL;
+
+ if (krb5_ret_int16 (sp, &ac->local_port) != 0)
+ goto failure;
+
+ if (krb5_ret_int16 (sp, &ac->remote_port) != 0)
+ goto failure;
+ if (flags & SC_KEYBLOCK) {
+ if (krb5_ret_keyblock (sp, &keyblock) != 0)
+ goto failure;
+ krb5_auth_con_setkey (context, ac, &keyblock);
+ krb5_free_keyblock_contents (context, &keyblock);
+ }
+ if (flags & SC_LOCAL_SUBKEY) {
+ if (krb5_ret_keyblock (sp, &keyblock) != 0)
+ goto failure;
+ krb5_auth_con_setlocalsubkey (context, ac, &keyblock);
+ krb5_free_keyblock_contents (context, &keyblock);
+ }
+ if (flags & SC_REMOTE_SUBKEY) {
+ if (krb5_ret_keyblock (sp, &keyblock) != 0)
+ goto failure;
+ krb5_auth_con_setremotesubkey (context, ac, &keyblock);
+ krb5_free_keyblock_contents (context, &keyblock);
+ }
+ if (krb5_ret_uint32 (sp, &ac->local_seqnumber))
+ goto failure;
+ if (krb5_ret_uint32 (sp, &ac->remote_seqnumber))
+ goto failure;
+
+ if (krb5_ret_int32 (sp, &tmp) != 0)
+ goto failure;
+ ac->keytype = tmp;
+ if (krb5_ret_int32 (sp, &tmp) != 0)
+ goto failure;
+ ac->cksumtype = tmp;
+
+ /* names */
+
+ if (krb5_ret_data (sp, &data))
+ goto failure;
+ buffer.value = data.data;
+ buffer.length = data.length;
+
+ ret = _gsskrb5_import_name (minor_status, &buffer, GSS_C_NT_EXPORT_NAME,
+ &name);
+ if (ret) {
+ ret = _gsskrb5_import_name (minor_status, &buffer, GSS_C_NO_OID,
+ &name);
+ if (ret) {
+ krb5_data_free (&data);
+ goto failure;
+ }
+ }
+ ctx->source = (krb5_principal)name;
+ krb5_data_free (&data);
+
+ if (krb5_ret_data (sp, &data) != 0)
+ goto failure;
+ buffer.value = data.data;
+ buffer.length = data.length;
+
+ ret = _gsskrb5_import_name (minor_status, &buffer, GSS_C_NT_EXPORT_NAME,
+ &name);
+ if (ret) {
+ ret = _gsskrb5_import_name (minor_status, &buffer, GSS_C_NO_OID,
+ &name);
+ if (ret) {
+ krb5_data_free (&data);
+ goto failure;
+ }
+ }
+ ctx->target = (krb5_principal)name;
+ krb5_data_free (&data);
+
+ if (krb5_ret_int32 (sp, &tmp))
+ goto failure;
+ ctx->flags = tmp;
+ if (krb5_ret_int32 (sp, &tmp))
+ goto failure;
+ ctx->more_flags = tmp;
+ if (krb5_ret_int32 (sp, &tmp))
+ goto failure;
+ ctx->lifetime = tmp;
+
+ ret = _gssapi_msg_order_import(minor_status, sp, &ctx->order);
+ if (ret)
+ goto failure;
+
+ krb5_storage_free (sp);
+
+ *context_handle = (gss_ctx_id_t)ctx;
+
+ return GSS_S_COMPLETE;
+
+failure:
+ krb5_auth_con_free (context,
+ ctx->auth_context);
+ if (ctx->source != NULL)
+ krb5_free_principal(context, ctx->source);
+ if (ctx->target != NULL)
+ krb5_free_principal(context, ctx->target);
+ if (localp)
+ krb5_free_address (context, localp);
+ if (remotep)
+ krb5_free_address (context, remotep);
+ if(ctx->order)
+ _gssapi_msg_order_destroy(&ctx->order);
+ HEIMDAL_MUTEX_destroy(&ctx->ctx_id_mutex);
+ krb5_storage_free (sp);
+ free (ctx);
+ *context_handle = GSS_C_NO_CONTEXT;
+ return ret;
+}
diff --git a/source4/heimdal/lib/gssapi/krb5/indicate_mechs.c b/source4/heimdal/lib/gssapi/krb5/indicate_mechs.c
new file mode 100644
index 0000000000..05b9447746
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/krb5/indicate_mechs.c
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 1997 - 2001, 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5/gsskrb5_locl.h"
+
+RCSID("$Id$");
+
+OM_uint32 _gsskrb5_indicate_mechs
+ (OM_uint32 * minor_status,
+ gss_OID_set * mech_set
+ )
+{
+ OM_uint32 ret, junk;
+
+ ret = gss_create_empty_oid_set(minor_status, mech_set);
+ if (ret)
+ return ret;
+
+ ret = gss_add_oid_set_member(minor_status, GSS_KRB5_MECHANISM, mech_set);
+ if (ret) {
+ gss_release_oid_set(&junk, mech_set);
+ return ret;
+ }
+
+ *minor_status = 0;
+ return GSS_S_COMPLETE;
+}
diff --git a/source4/heimdal/lib/gssapi/krb5/init.c b/source4/heimdal/lib/gssapi/krb5/init.c
new file mode 100644
index 0000000000..6c1c5949e0
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/krb5/init.c
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 1997 - 2001, 2003, 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5/gsskrb5_locl.h"
+
+RCSID("$Id$");
+
+static HEIMDAL_MUTEX context_mutex = HEIMDAL_MUTEX_INITIALIZER;
+static int created_key;
+static HEIMDAL_thread_key context_key;
+
+static void
+destroy_context(void *ptr)
+{
+ krb5_context context = ptr;
+
+ if (context == NULL)
+ return;
+ krb5_free_context(context);
+}
+
+krb5_error_code
+_gsskrb5_init (krb5_context *context)
+{
+ krb5_error_code ret = 0;
+
+ HEIMDAL_MUTEX_lock(&context_mutex);
+
+ if (!created_key) {
+ HEIMDAL_key_create(&context_key, destroy_context, ret);
+ if (ret) {
+ HEIMDAL_MUTEX_unlock(&context_mutex);
+ return ret;
+ }
+ created_key = 1;
+ }
+ HEIMDAL_MUTEX_unlock(&context_mutex);
+
+ *context = HEIMDAL_getspecific(context_key);
+ if (*context == NULL) {
+
+ ret = krb5_init_context(context);
+ if (ret == 0) {
+ HEIMDAL_setspecific(context_key, *context, ret);
+ if (ret) {
+ krb5_free_context(*context);
+ *context = NULL;
+ }
+ }
+ }
+
+ return ret;
+}
diff --git a/source4/heimdal/lib/gssapi/krb5/init_sec_context.c b/source4/heimdal/lib/gssapi/krb5/init_sec_context.c
new file mode 100644
index 0000000000..dfa0e935e6
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/krb5/init_sec_context.c
@@ -0,0 +1,972 @@
+/*
+ * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5/gsskrb5_locl.h"
+
+RCSID("$Id$");
+
+/*
+ * copy the addresses from `input_chan_bindings' (if any) to
+ * the auth context `ac'
+ */
+
+static OM_uint32
+set_addresses (krb5_context context,
+ krb5_auth_context ac,
+ const gss_channel_bindings_t input_chan_bindings)
+{
+ /* Port numbers are expected to be in application_data.value,
+ * initator's port first */
+
+ krb5_address initiator_addr, acceptor_addr;
+ krb5_error_code kret;
+
+ if (input_chan_bindings == GSS_C_NO_CHANNEL_BINDINGS
+ || input_chan_bindings->application_data.length !=
+ 2 * sizeof(ac->local_port))
+ return 0;
+
+ memset(&initiator_addr, 0, sizeof(initiator_addr));
+ memset(&acceptor_addr, 0, sizeof(acceptor_addr));
+
+ ac->local_port =
+ *(int16_t *) input_chan_bindings->application_data.value;
+
+ ac->remote_port =
+ *((int16_t *) input_chan_bindings->application_data.value + 1);
+
+ kret = _gsskrb5i_address_to_krb5addr(context,
+ input_chan_bindings->acceptor_addrtype,
+ &input_chan_bindings->acceptor_address,
+ ac->remote_port,
+ &acceptor_addr);
+ if (kret)
+ return kret;
+
+ kret = _gsskrb5i_address_to_krb5addr(context,
+ input_chan_bindings->initiator_addrtype,
+ &input_chan_bindings->initiator_address,
+ ac->local_port,
+ &initiator_addr);
+ if (kret) {
+ krb5_free_address (context, &acceptor_addr);
+ return kret;
+ }
+
+ kret = krb5_auth_con_setaddrs(context,
+ ac,
+ &initiator_addr, /* local address */
+ &acceptor_addr); /* remote address */
+
+ krb5_free_address (context, &initiator_addr);
+ krb5_free_address (context, &acceptor_addr);
+
+#if 0
+ free(input_chan_bindings->application_data.value);
+ input_chan_bindings->application_data.value = NULL;
+ input_chan_bindings->application_data.length = 0;
+#endif
+
+ return kret;
+}
+
+OM_uint32
+_gsskrb5_create_ctx(
+ OM_uint32 * minor_status,
+ gss_ctx_id_t * context_handle,
+ krb5_context context,
+ const gss_channel_bindings_t input_chan_bindings,
+ enum gss_ctx_id_t_state state)
+{
+ krb5_error_code kret;
+ gsskrb5_ctx ctx;
+
+ *context_handle = NULL;
+
+ ctx = malloc(sizeof(*ctx));
+ if (ctx == NULL) {
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+ ctx->auth_context = NULL;
+ ctx->source = NULL;
+ ctx->target = NULL;
+ ctx->kcred = NULL;
+ ctx->ccache = NULL;
+ ctx->state = state;
+ ctx->flags = 0;
+ ctx->more_flags = 0;
+ ctx->service_keyblock = NULL;
+ ctx->ticket = NULL;
+ krb5_data_zero(&ctx->fwd_data);
+ ctx->lifetime = GSS_C_INDEFINITE;
+ ctx->order = NULL;
+ HEIMDAL_MUTEX_init(&ctx->ctx_id_mutex);
+
+ kret = krb5_auth_con_init (context, &ctx->auth_context);
+ if (kret) {
+ *minor_status = kret;
+ HEIMDAL_MUTEX_destroy(&ctx->ctx_id_mutex);
+ return GSS_S_FAILURE;
+ }
+
+ kret = set_addresses(context, ctx->auth_context, input_chan_bindings);
+ if (kret) {
+ *minor_status = kret;
+
+ HEIMDAL_MUTEX_destroy(&ctx->ctx_id_mutex);
+
+ krb5_auth_con_free(context, ctx->auth_context);
+
+ return GSS_S_BAD_BINDINGS;
+ }
+
+ /*
+ * We need a sequence number
+ */
+
+ krb5_auth_con_addflags(context,
+ ctx->auth_context,
+ KRB5_AUTH_CONTEXT_DO_SEQUENCE |
+ KRB5_AUTH_CONTEXT_CLEAR_FORWARDED_CRED,
+ NULL);
+
+ *context_handle = (gss_ctx_id_t)ctx;
+
+ return GSS_S_COMPLETE;
+}
+
+
+static OM_uint32
+gsskrb5_get_creds(
+ OM_uint32 * minor_status,
+ krb5_context context,
+ krb5_ccache ccache,
+ gsskrb5_ctx ctx,
+ const gss_name_t target_name,
+ int use_dns,
+ OM_uint32 time_req,
+ OM_uint32 * time_rec,
+ krb5_creds ** cred)
+{
+ OM_uint32 ret;
+ krb5_error_code kret;
+ krb5_creds this_cred;
+ OM_uint32 lifetime_rec;
+
+ *cred = NULL;
+
+ if (ctx->target) {
+ krb5_free_principal(context, ctx->target);
+ ctx->target = NULL;
+ }
+
+ ret = _gsskrb5_canon_name(minor_status, context, use_dns,
+ target_name, &ctx->target);
+ if (ret)
+ return ret;
+
+ memset(&this_cred, 0, sizeof(this_cred));
+ this_cred.client = ctx->source;
+ this_cred.server = ctx->target;
+
+ if (time_req && time_req != GSS_C_INDEFINITE) {
+ krb5_timestamp ts;
+
+ krb5_timeofday (context, &ts);
+ this_cred.times.endtime = ts + time_req;
+ } else {
+ this_cred.times.endtime = 0;
+ }
+
+ this_cred.session.keytype = KEYTYPE_NULL;
+
+ kret = krb5_get_credentials(context,
+ 0,
+ ccache,
+ &this_cred,
+ cred);
+ if (kret) {
+ *minor_status = kret;
+ return GSS_S_FAILURE;
+ }
+
+ ctx->lifetime = (*cred)->times.endtime;
+
+ ret = _gsskrb5_lifetime_left(minor_status, context,
+ ctx->lifetime, &lifetime_rec);
+ if (ret) return ret;
+
+ if (lifetime_rec == 0) {
+ *minor_status = 0;
+ return GSS_S_CONTEXT_EXPIRED;
+ }
+
+ if (time_rec) *time_rec = lifetime_rec;
+
+ return GSS_S_COMPLETE;
+}
+
+static OM_uint32
+gsskrb5_initiator_ready(
+ OM_uint32 * minor_status,
+ gsskrb5_ctx ctx,
+ krb5_context context)
+{
+ OM_uint32 ret;
+ int32_t seq_number;
+ int is_cfx = 0;
+ OM_uint32 flags = ctx->flags;
+
+ krb5_free_creds(context, ctx->kcred);
+ ctx->kcred = NULL;
+
+ if (ctx->more_flags & CLOSE_CCACHE)
+ krb5_cc_close(context, ctx->ccache);
+ ctx->ccache = NULL;
+
+ krb5_auth_getremoteseqnumber (context, ctx->auth_context, &seq_number);
+
+ _gsskrb5i_is_cfx(ctx, &is_cfx);
+
+ ret = _gssapi_msg_order_create(minor_status,
+ &ctx->order,
+ _gssapi_msg_order_f(flags),
+ seq_number, 0, is_cfx);
+ if (ret) return ret;
+
+ ctx->state = INITIATOR_READY;
+ ctx->more_flags |= OPEN;
+
+ return GSS_S_COMPLETE;
+}
+
+/*
+ * handle delegated creds in init-sec-context
+ */
+
+static void
+do_delegation (krb5_context context,
+ krb5_auth_context ac,
+ krb5_ccache ccache,
+ krb5_creds *cred,
+ krb5_const_principal name,
+ krb5_data *fwd_data,
+ uint32_t flagmask,
+ uint32_t *flags)
+{
+ krb5_creds creds;
+ KDCOptions fwd_flags;
+ krb5_error_code kret;
+
+ memset (&creds, 0, sizeof(creds));
+ krb5_data_zero (fwd_data);
+
+ kret = krb5_cc_get_principal(context, ccache, &creds.client);
+ if (kret)
+ goto out;
+
+ kret = krb5_build_principal(context,
+ &creds.server,
+ strlen(creds.client->realm),
+ creds.client->realm,
+ KRB5_TGS_NAME,
+ creds.client->realm,
+ NULL);
+ if (kret)
+ goto out;
+
+ creds.times.endtime = 0;
+
+ memset(&fwd_flags, 0, sizeof(fwd_flags));
+ fwd_flags.forwarded = 1;
+ fwd_flags.forwardable = 1;
+
+ if ( /*target_name->name.name_type != KRB5_NT_SRV_HST ||*/
+ name->name.name_string.len < 2)
+ goto out;
+
+ kret = krb5_get_forwarded_creds(context,
+ ac,
+ ccache,
+ KDCOptions2int(fwd_flags),
+ name->name.name_string.val[1],
+ &creds,
+ fwd_data);
+
+ out:
+ if (kret)
+ *flags &= ~flagmask;
+ else
+ *flags |= flagmask;
+
+ if (creds.client)
+ krb5_free_principal(context, creds.client);
+ if (creds.server)
+ krb5_free_principal(context, creds.server);
+}
+
+/*
+ * first stage of init-sec-context
+ */
+
+static OM_uint32
+init_auth
+(OM_uint32 * minor_status,
+ gsskrb5_cred cred,
+ gsskrb5_ctx ctx,
+ krb5_context context,
+ gss_name_t name,
+ const gss_OID mech_type,
+ OM_uint32 req_flags,
+ OM_uint32 time_req,
+ const gss_buffer_t input_token,
+ gss_OID * actual_mech_type,
+ gss_buffer_t output_token,
+ OM_uint32 * ret_flags,
+ OM_uint32 * time_rec
+ )
+{
+ OM_uint32 ret = GSS_S_FAILURE;
+ krb5_error_code kret;
+ krb5_data outbuf;
+ krb5_data fwd_data;
+ OM_uint32 lifetime_rec;
+ int allow_dns = 1;
+
+ krb5_data_zero(&outbuf);
+ krb5_data_zero(&fwd_data);
+
+ *minor_status = 0;
+
+ if (actual_mech_type)
+ *actual_mech_type = GSS_KRB5_MECHANISM;
+
+ if (cred == NULL) {
+ kret = krb5_cc_default (context, &ctx->ccache);
+ if (kret) {
+ *minor_status = kret;
+ ret = GSS_S_FAILURE;
+ goto failure;
+ }
+ ctx->more_flags |= CLOSE_CCACHE;
+ } else
+ ctx->ccache = cred->ccache;
+
+ kret = krb5_cc_get_principal (context, ctx->ccache, &ctx->source);
+ if (kret) {
+ *minor_status = kret;
+ ret = GSS_S_FAILURE;
+ goto failure;
+ }
+
+ ret = _gss_DES3_get_mic_compat(minor_status, ctx, context);
+ if (ret)
+ goto failure;
+
+
+ /*
+ * This is hideous glue for (NFS) clients that wants to limit the
+ * available enctypes to what it can support (encryption in
+ * kernel). If there is no enctypes selected for this credential,
+ * reset it to the default set of enctypes.
+ */
+ {
+ krb5_enctype *enctypes = NULL;
+
+ if (cred && cred->enctypes)
+ enctypes = cred->enctypes;
+ krb5_set_default_in_tkt_etypes(context, enctypes);
+ }
+
+ /* canon name if needed for client + target realm */
+ kret = krb5_cc_get_config(context, ctx->ccache, NULL,
+ "realm-config", &outbuf);
+ if (kret == 0) {
+ /* XXX 2 is no server canon */
+ if (outbuf.length < 1 || ((((unsigned char *)outbuf.data)[0]) & 2))
+ allow_dns = 0;
+ krb5_data_free(&outbuf);
+ }
+
+ /*
+ * First we try w/o dns, hope that the KDC have register alias
+ * (and referrals if cross realm) for this principal. If that
+ * fails and if we are allowed to using this realm try again with
+ * DNS canonicalizion.
+ */
+ ret = gsskrb5_get_creds(minor_status, context, ctx->ccache,
+ ctx, name, 0, time_req,
+ time_rec, &ctx->kcred);
+ if (ret && allow_dns)
+ ret = gsskrb5_get_creds(minor_status, context, ctx->ccache,
+ ctx, name, 1, time_req,
+ time_rec, &ctx->kcred);
+ if (ret)
+ goto failure;
+
+ ctx->lifetime = ctx->kcred->times.endtime;
+
+ ret = _gsskrb5_lifetime_left(minor_status,
+ context,
+ ctx->lifetime,
+ &lifetime_rec);
+ if (ret)
+ goto failure;
+
+ if (lifetime_rec == 0) {
+ *minor_status = 0;
+ ret = GSS_S_CONTEXT_EXPIRED;
+ goto failure;
+ }
+
+ krb5_auth_con_setkey(context,
+ ctx->auth_context,
+ &ctx->kcred->session);
+
+ kret = krb5_auth_con_generatelocalsubkey(context,
+ ctx->auth_context,
+ &ctx->kcred->session);
+ if(kret) {
+ *minor_status = kret;
+ ret = GSS_S_FAILURE;
+ goto failure;
+ }
+
+ return GSS_S_COMPLETE;
+
+failure:
+ if (ctx->ccache && (ctx->more_flags & CLOSE_CCACHE))
+ krb5_cc_close(context, ctx->ccache);
+ ctx->ccache = NULL;
+
+ return ret;
+
+}
+
+static OM_uint32
+init_auth_restart
+(OM_uint32 * minor_status,
+ gsskrb5_cred cred,
+ gsskrb5_ctx ctx,
+ krb5_context context,
+ OM_uint32 req_flags,
+ const gss_channel_bindings_t input_chan_bindings,
+ const gss_buffer_t input_token,
+ gss_OID * actual_mech_type,
+ gss_buffer_t output_token,
+ OM_uint32 * ret_flags,
+ OM_uint32 * time_rec
+ )
+{
+ OM_uint32 ret = GSS_S_FAILURE;
+ krb5_error_code kret;
+ krb5_flags ap_options;
+ krb5_data outbuf;
+ uint32_t flags;
+ krb5_data authenticator;
+ Checksum cksum;
+ krb5_enctype enctype;
+ krb5_data fwd_data, timedata;
+ int32_t offset = 0, oldoffset;
+ uint32_t flagmask;
+
+ krb5_data_zero(&outbuf);
+ krb5_data_zero(&fwd_data);
+
+ *minor_status = 0;
+
+ /*
+ * If the credential doesn't have ok-as-delegate, check if there
+ * is a realm setting and use that.
+ */
+ if (!ctx->kcred->flags.b.ok_as_delegate) {
+ krb5_data data;
+
+ ret = krb5_cc_get_config(context, ctx->ccache, NULL,
+ "realm-config", &data);
+ if (ret == 0) {
+ /* XXX 1 is use ok-as-delegate */
+ if (data.length < 1 || ((((unsigned char *)data.data)[0]) & 1) == 0)
+ req_flags &= ~(GSS_C_DELEG_FLAG|GSS_C_DELEG_POLICY_FLAG);
+ krb5_data_free(&data);
+ }
+ }
+
+ flagmask = 0;
+
+ /* if we used GSS_C_DELEG_POLICY_FLAG, trust KDC */
+ if ((req_flags & GSS_C_DELEG_POLICY_FLAG)
+ && ctx->kcred->flags.b.ok_as_delegate)
+ flagmask |= GSS_C_DELEG_FLAG | GSS_C_DELEG_POLICY_FLAG;
+ /* if there still is a GSS_C_DELEG_FLAG, use that */
+ if (req_flags & GSS_C_DELEG_FLAG)
+ flagmask |= GSS_C_DELEG_FLAG;
+
+
+ flags = 0;
+ ap_options = 0;
+ if (flagmask & GSS_C_DELEG_FLAG) {
+ do_delegation (context,
+ ctx->auth_context,
+ ctx->ccache, ctx->kcred, ctx->target,
+ &fwd_data, flagmask, &flags);
+ }
+
+ if (req_flags & GSS_C_MUTUAL_FLAG) {
+ flags |= GSS_C_MUTUAL_FLAG;
+ ap_options |= AP_OPTS_MUTUAL_REQUIRED;
+ }
+
+ if (req_flags & GSS_C_REPLAY_FLAG)
+ flags |= GSS_C_REPLAY_FLAG;
+ if (req_flags & GSS_C_SEQUENCE_FLAG)
+ flags |= GSS_C_SEQUENCE_FLAG;
+ if (req_flags & GSS_C_ANON_FLAG)
+ ; /* XXX */
+ if (req_flags & GSS_C_DCE_STYLE) {
+ /* GSS_C_DCE_STYLE implies GSS_C_MUTUAL_FLAG */
+ flags |= GSS_C_DCE_STYLE | GSS_C_MUTUAL_FLAG;
+ ap_options |= AP_OPTS_MUTUAL_REQUIRED;
+ }
+ if (req_flags & GSS_C_IDENTIFY_FLAG)
+ flags |= GSS_C_IDENTIFY_FLAG;
+ if (req_flags & GSS_C_EXTENDED_ERROR_FLAG)
+ flags |= GSS_C_EXTENDED_ERROR_FLAG;
+
+ if (req_flags & GSS_C_CONF_FLAG) {
+ flags |= GSS_C_CONF_FLAG;
+ }
+ if (req_flags & GSS_C_INTEG_FLAG) {
+ flags |= GSS_C_INTEG_FLAG;
+ }
+ if (cred == NULL || !(cred->cred_flags & GSS_CF_NO_CI_FLAGS)) {
+ flags |= GSS_C_CONF_FLAG;
+ flags |= GSS_C_INTEG_FLAG;
+ }
+ flags |= GSS_C_TRANS_FLAG;
+
+ if (ret_flags)
+ *ret_flags = flags;
+ ctx->flags = flags;
+ ctx->more_flags |= LOCAL;
+
+ ret = _gsskrb5_create_8003_checksum (minor_status,
+ input_chan_bindings,
+ flags,
+ &fwd_data,
+ &cksum);
+ krb5_data_free (&fwd_data);
+ if (ret)
+ goto failure;
+
+ enctype = ctx->auth_context->keyblock->keytype;
+
+ ret = krb5_cc_get_config(context, ctx->ccache, ctx->target,
+ "time-offset", &timedata);
+ if (ret == 0) {
+ if (timedata.length == 4) {
+ const u_char *p = timedata.data;
+ offset = (p[0] <<24) | (p[1] << 16) | (p[2] << 8) | (p[3] << 0);
+ }
+ krb5_data_free(&timedata);
+ }
+
+ if (offset) {
+ krb5_get_kdc_sec_offset (context, &oldoffset, NULL);
+ krb5_set_kdc_sec_offset (context, offset, -1);
+ }
+
+ kret = krb5_build_authenticator (context,
+ ctx->auth_context,
+ enctype,
+ ctx->kcred,
+ &cksum,
+ NULL,
+ &authenticator,
+ KRB5_KU_AP_REQ_AUTH);
+
+ if (kret) {
+ if (offset)
+ krb5_set_kdc_sec_offset (context, oldoffset, -1);
+ *minor_status = kret;
+ ret = GSS_S_FAILURE;
+ goto failure;
+ }
+
+ kret = krb5_build_ap_req (context,
+ enctype,
+ ctx->kcred,
+ ap_options,
+ authenticator,
+ &outbuf);
+ if (offset)
+ krb5_set_kdc_sec_offset (context, oldoffset, -1);
+ if (kret) {
+ *minor_status = kret;
+ ret = GSS_S_FAILURE;
+ goto failure;
+ }
+
+ if (flags & GSS_C_DCE_STYLE) {
+ output_token->value = outbuf.data;
+ output_token->length = outbuf.length;
+ } else {
+ ret = _gsskrb5_encapsulate (minor_status, &outbuf, output_token,
+ (u_char *)"\x01\x00", GSS_KRB5_MECHANISM);
+ krb5_data_free (&outbuf);
+ if (ret)
+ goto failure;
+ }
+
+ free_Checksum(&cksum);
+
+ if (flags & GSS_C_MUTUAL_FLAG) {
+ ctx->state = INITIATOR_WAIT_FOR_MUTAL;
+ return GSS_S_CONTINUE_NEEDED;
+ }
+
+ return gsskrb5_initiator_ready(minor_status, ctx, context);
+failure:
+ if (ctx->ccache && (ctx->more_flags & CLOSE_CCACHE))
+ krb5_cc_close(context, ctx->ccache);
+ ctx->ccache = NULL;
+
+ return ret;
+}
+
+
+static OM_uint32
+repl_mutual
+(OM_uint32 * minor_status,
+ gsskrb5_ctx ctx,
+ krb5_context context,
+ const gss_OID mech_type,
+ OM_uint32 req_flags,
+ OM_uint32 time_req,
+ const gss_channel_bindings_t input_chan_bindings,
+ const gss_buffer_t input_token,
+ gss_OID * actual_mech_type,
+ gss_buffer_t output_token,
+ OM_uint32 * ret_flags,
+ OM_uint32 * time_rec
+ )
+{
+ OM_uint32 ret;
+ krb5_error_code kret;
+ krb5_data indata;
+ krb5_ap_rep_enc_part *repl;
+ int is_cfx = 0;
+
+ output_token->length = 0;
+ output_token->value = NULL;
+
+ if (actual_mech_type)
+ *actual_mech_type = GSS_KRB5_MECHANISM;
+
+ if (ctx->flags & GSS_C_DCE_STYLE) {
+ /* There is no OID wrapping. */
+ indata.length = input_token->length;
+ indata.data = input_token->value;
+ } else {
+ ret = _gsskrb5_decapsulate (minor_status,
+ input_token,
+ &indata,
+ "\x02\x00",
+ GSS_KRB5_MECHANISM);
+ if (ret == GSS_S_DEFECTIVE_TOKEN) {
+ /* check if there is an error token sent instead */
+ ret = _gsskrb5_decapsulate (minor_status,
+ input_token,
+ &indata,
+ "\x03\x00",
+ GSS_KRB5_MECHANISM);
+ if (ret == GSS_S_COMPLETE) {
+ KRB_ERROR error;
+
+ kret = krb5_rd_error(context, &indata, &error);
+ if (kret == 0) {
+ kret = krb5_error_from_rd_error(context, &error, NULL);
+
+ /* save the time skrew for this host */
+ if (kret == KRB5KRB_AP_ERR_SKEW) {
+ krb5_data timedata;
+ unsigned char p[4];
+ int32_t t = error.stime - time(NULL);
+
+ p[0] = (t >> 24) & 0xFF;
+ p[1] = (t >> 16) & 0xFF;
+ p[2] = (t >> 8) & 0xFF;
+ p[3] = (t >> 0) & 0xFF;
+
+ timedata.data = p;
+ timedata.length = sizeof(p);
+
+ krb5_cc_set_config(context, ctx->ccache, ctx->target,
+ "time-offset", &timedata);
+
+ if ((ctx->more_flags & RETRIED) == 0)
+ ctx->state = INITIATOR_RESTART;
+ ctx->more_flags |= RETRIED;
+ }
+ free_KRB_ERROR (&error);
+ }
+ *minor_status = kret;
+ return GSS_S_FAILURE;
+ }
+ return ret;
+ }
+ }
+
+ kret = krb5_rd_rep (context,
+ ctx->auth_context,
+ &indata,
+ &repl);
+ if (kret) {
+ *minor_status = kret;
+ return GSS_S_FAILURE;
+ }
+ krb5_free_ap_rep_enc_part (context,
+ repl);
+
+ _gsskrb5i_is_cfx(ctx, &is_cfx);
+ if (is_cfx) {
+ krb5_keyblock *key = NULL;
+
+ kret = krb5_auth_con_getremotesubkey(context,
+ ctx->auth_context,
+ &key);
+ if (kret == 0 && key != NULL) {
+ ctx->more_flags |= ACCEPTOR_SUBKEY;
+ krb5_free_keyblock (context, key);
+ }
+ }
+
+
+ *minor_status = 0;
+ if (time_rec) {
+ ret = _gsskrb5_lifetime_left(minor_status,
+ context,
+ ctx->lifetime,
+ time_rec);
+ } else {
+ ret = GSS_S_COMPLETE;
+ }
+ if (ret_flags)
+ *ret_flags = ctx->flags;
+
+ if (req_flags & GSS_C_DCE_STYLE) {
+ int32_t local_seq, remote_seq;
+ krb5_data outbuf;
+
+ /*
+ * So DCE_STYLE is strange. The client echos the seq number
+ * that the server used in the server's mk_rep in its own
+ * mk_rep(). After when done, it resets to it's own seq number
+ * for the gss_wrap calls.
+ */
+
+ krb5_auth_getremoteseqnumber(context, ctx->auth_context, &remote_seq);
+ krb5_auth_con_getlocalseqnumber(context, ctx->auth_context, &local_seq);
+ krb5_auth_con_setlocalseqnumber(context, ctx->auth_context, remote_seq);
+
+ kret = krb5_mk_rep(context, ctx->auth_context, &outbuf);
+ if (kret) {
+ *minor_status = kret;
+ return GSS_S_FAILURE;
+ }
+
+ /* reset local seq number */
+ krb5_auth_con_setlocalseqnumber(context, ctx->auth_context, local_seq);
+
+ output_token->length = outbuf.length;
+ output_token->value = outbuf.data;
+ }
+
+ return gsskrb5_initiator_ready(minor_status, ctx, context);
+}
+
+/*
+ * gss_init_sec_context
+ */
+
+OM_uint32 _gsskrb5_init_sec_context
+(OM_uint32 * minor_status,
+ const gss_cred_id_t cred_handle,
+ gss_ctx_id_t * context_handle,
+ const gss_name_t target_name,
+ const gss_OID mech_type,
+ OM_uint32 req_flags,
+ OM_uint32 time_req,
+ const gss_channel_bindings_t input_chan_bindings,
+ const gss_buffer_t input_token,
+ gss_OID * actual_mech_type,
+ gss_buffer_t output_token,
+ OM_uint32 * ret_flags,
+ OM_uint32 * time_rec
+ )
+{
+ krb5_context context;
+ gsskrb5_cred cred = (gsskrb5_cred)cred_handle;
+ gsskrb5_ctx ctx;
+ OM_uint32 ret;
+
+ GSSAPI_KRB5_INIT (&context);
+
+ output_token->length = 0;
+ output_token->value = NULL;
+
+ if (context_handle == NULL) {
+ *minor_status = 0;
+ return GSS_S_FAILURE | GSS_S_CALL_BAD_STRUCTURE;
+ }
+
+ if (ret_flags)
+ *ret_flags = 0;
+ if (time_rec)
+ *time_rec = 0;
+
+ if (target_name == GSS_C_NO_NAME) {
+ if (actual_mech_type)
+ *actual_mech_type = GSS_C_NO_OID;
+ *minor_status = 0;
+ return GSS_S_BAD_NAME;
+ }
+
+ if (mech_type != GSS_C_NO_OID &&
+ !gss_oid_equal(mech_type, GSS_KRB5_MECHANISM))
+ return GSS_S_BAD_MECH;
+
+ if (input_token == GSS_C_NO_BUFFER || input_token->length == 0) {
+ OM_uint32 ret;
+
+ if (*context_handle != GSS_C_NO_CONTEXT) {
+ *minor_status = 0;
+ return GSS_S_FAILURE | GSS_S_CALL_BAD_STRUCTURE;
+ }
+
+ ret = _gsskrb5_create_ctx(minor_status,
+ context_handle,
+ context,
+ input_chan_bindings,
+ INITIATOR_START);
+ if (ret)
+ return ret;
+ }
+
+ if (*context_handle == GSS_C_NO_CONTEXT) {
+ *minor_status = 0;
+ return GSS_S_FAILURE | GSS_S_CALL_BAD_STRUCTURE;
+ }
+
+ ctx = (gsskrb5_ctx) *context_handle;
+
+ HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
+
+ again:
+ switch (ctx->state) {
+ case INITIATOR_START:
+ ret = init_auth(minor_status,
+ cred,
+ ctx,
+ context,
+ target_name,
+ mech_type,
+ req_flags,
+ time_req,
+ input_token,
+ actual_mech_type,
+ output_token,
+ ret_flags,
+ time_rec);
+ if (ret != GSS_S_COMPLETE)
+ break;
+ /* FALL THOUGH */
+ case INITIATOR_RESTART:
+ ret = init_auth_restart(minor_status,
+ cred,
+ ctx,
+ context,
+ req_flags,
+ input_chan_bindings,
+ input_token,
+ actual_mech_type,
+ output_token,
+ ret_flags,
+ time_rec);
+ break;
+ case INITIATOR_WAIT_FOR_MUTAL:
+ ret = repl_mutual(minor_status,
+ ctx,
+ context,
+ mech_type,
+ req_flags,
+ time_req,
+ input_chan_bindings,
+ input_token,
+ actual_mech_type,
+ output_token,
+ ret_flags,
+ time_rec);
+ if (ctx->state == INITIATOR_RESTART)
+ goto again;
+ break;
+ case INITIATOR_READY:
+ /*
+ * If we get there, the caller have called
+ * gss_init_sec_context() one time too many.
+ */
+ _gsskrb5_set_status(EINVAL, "init_sec_context "
+ "called one time too many");
+ *minor_status = EINVAL;
+ ret = GSS_S_BAD_STATUS;
+ break;
+ default:
+ _gsskrb5_set_status(EINVAL, "init_sec_context "
+ "invalid state %d for client",
+ (int)ctx->state);
+ *minor_status = EINVAL;
+ ret = GSS_S_BAD_STATUS;
+ break;
+ }
+ HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
+
+ /* destroy context in case of error */
+ if (GSS_ERROR(ret)) {
+ OM_uint32 min2;
+ _gsskrb5_delete_sec_context(&min2, context_handle, GSS_C_NO_BUFFER);
+ }
+
+ return ret;
+
+}
diff --git a/source4/heimdal/lib/gssapi/krb5/inquire_context.c b/source4/heimdal/lib/gssapi/krb5/inquire_context.c
new file mode 100644
index 0000000000..e0aeb032ce
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/krb5/inquire_context.c
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 1997, 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5/gsskrb5_locl.h"
+
+RCSID("$Id$");
+
+OM_uint32 _gsskrb5_inquire_context (
+ OM_uint32 * minor_status,
+ const gss_ctx_id_t context_handle,
+ gss_name_t * src_name,
+ gss_name_t * targ_name,
+ OM_uint32 * lifetime_rec,
+ gss_OID * mech_type,
+ OM_uint32 * ctx_flags,
+ int * locally_initiated,
+ int * open_context
+ )
+{
+ krb5_context context;
+ OM_uint32 ret;
+ gsskrb5_ctx ctx = (gsskrb5_ctx)context_handle;
+ gss_name_t name;
+
+ if (src_name)
+ *src_name = GSS_C_NO_NAME;
+ if (targ_name)
+ *targ_name = GSS_C_NO_NAME;
+
+ GSSAPI_KRB5_INIT (&context);
+
+ HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
+
+ if (src_name) {
+ name = (gss_name_t)ctx->source;
+ ret = _gsskrb5_duplicate_name (minor_status, name, src_name);
+ if (ret)
+ goto failed;
+ }
+
+ if (targ_name) {
+ name = (gss_name_t)ctx->target;
+ ret = _gsskrb5_duplicate_name (minor_status, name, targ_name);
+ if (ret)
+ goto failed;
+ }
+
+ if (lifetime_rec) {
+ ret = _gsskrb5_lifetime_left(minor_status,
+ context,
+ ctx->lifetime,
+ lifetime_rec);
+ if (ret)
+ goto failed;
+ }
+
+ if (mech_type)
+ *mech_type = GSS_KRB5_MECHANISM;
+
+ if (ctx_flags)
+ *ctx_flags = ctx->flags;
+
+ if (locally_initiated)
+ *locally_initiated = ctx->more_flags & LOCAL;
+
+ if (open_context)
+ *open_context = ctx->more_flags & OPEN;
+
+ *minor_status = 0;
+
+ HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
+ return GSS_S_COMPLETE;
+
+failed:
+ if (src_name)
+ _gsskrb5_release_name(NULL, src_name);
+ if (targ_name)
+ _gsskrb5_release_name(NULL, targ_name);
+
+ HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
+ return ret;
+}
diff --git a/source4/heimdal/lib/gssapi/krb5/inquire_cred.c b/source4/heimdal/lib/gssapi/krb5/inquire_cred.c
new file mode 100644
index 0000000000..bb75978adb
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/krb5/inquire_cred.c
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 1997, 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5/gsskrb5_locl.h"
+
+RCSID("$Id$");
+
+OM_uint32 _gsskrb5_inquire_cred
+(OM_uint32 * minor_status,
+ const gss_cred_id_t cred_handle,
+ gss_name_t * output_name,
+ OM_uint32 * lifetime,
+ gss_cred_usage_t * cred_usage,
+ gss_OID_set * mechanisms
+ )
+{
+ krb5_context context;
+ gss_cred_id_t aqcred_init = GSS_C_NO_CREDENTIAL;
+ gss_cred_id_t aqcred_accept = GSS_C_NO_CREDENTIAL;
+ gsskrb5_cred acred = NULL, icred = NULL;
+ OM_uint32 ret;
+
+ *minor_status = 0;
+
+ if (output_name)
+ *output_name = NULL;
+ if (mechanisms)
+ *mechanisms = GSS_C_NO_OID_SET;
+
+ GSSAPI_KRB5_INIT (&context);
+
+ if (cred_handle == GSS_C_NO_CREDENTIAL) {
+ ret = _gsskrb5_acquire_cred(minor_status,
+ GSS_C_NO_NAME,
+ GSS_C_INDEFINITE,
+ GSS_C_NO_OID_SET,
+ GSS_C_ACCEPT,
+ &aqcred_accept,
+ NULL,
+ NULL);
+ if (ret == GSS_S_COMPLETE)
+ acred = (gsskrb5_cred)aqcred_accept;
+
+ ret = _gsskrb5_acquire_cred(minor_status,
+ GSS_C_NO_NAME,
+ GSS_C_INDEFINITE,
+ GSS_C_NO_OID_SET,
+ GSS_C_INITIATE,
+ &aqcred_init,
+ NULL,
+ NULL);
+ if (ret == GSS_S_COMPLETE)
+ icred = (gsskrb5_cred)aqcred_init;
+
+ if (icred == NULL && acred == NULL) {
+ *minor_status = 0;
+ return GSS_S_NO_CRED;
+ }
+ } else
+ acred = (gsskrb5_cred)cred_handle;
+
+ if (acred)
+ HEIMDAL_MUTEX_lock(&acred->cred_id_mutex);
+ if (icred)
+ HEIMDAL_MUTEX_lock(&icred->cred_id_mutex);
+
+ if (output_name != NULL) {
+ if (icred && icred->principal != NULL) {
+ gss_name_t name;
+
+ if (acred && acred->principal)
+ name = (gss_name_t)acred->principal;
+ else
+ name = (gss_name_t)icred->principal;
+
+ ret = _gsskrb5_duplicate_name(minor_status, name, output_name);
+ if (ret)
+ goto out;
+ } else if (acred && acred->usage == GSS_C_ACCEPT) {
+ krb5_principal princ;
+ *minor_status = krb5_sname_to_principal(context, NULL,
+ NULL, KRB5_NT_SRV_HST,
+ &princ);
+ if (*minor_status) {
+ ret = GSS_S_FAILURE;
+ goto out;
+ }
+ *output_name = (gss_name_t)princ;
+ } else {
+ krb5_principal princ;
+ *minor_status = krb5_get_default_principal(context,
+ &princ);
+ if (*minor_status) {
+ ret = GSS_S_FAILURE;
+ goto out;
+ }
+ *output_name = (gss_name_t)princ;
+ }
+ }
+ if (lifetime != NULL) {
+ OM_uint32 alife = GSS_C_INDEFINITE, ilife = GSS_C_INDEFINITE;
+
+ if (acred) alife = acred->lifetime;
+ if (icred) ilife = icred->lifetime;
+
+ ret = _gsskrb5_lifetime_left(minor_status,
+ context,
+ min(alife,ilife),
+ lifetime);
+ if (ret)
+ goto out;
+ }
+ if (cred_usage != NULL) {
+ if (acred && icred)
+ *cred_usage = GSS_C_BOTH;
+ else if (acred)
+ *cred_usage = GSS_C_ACCEPT;
+ else if (icred)
+ *cred_usage = GSS_C_INITIATE;
+ else
+ abort();
+ }
+
+ if (mechanisms != NULL) {
+ ret = gss_create_empty_oid_set(minor_status, mechanisms);
+ if (ret)
+ goto out;
+ if (acred)
+ ret = gss_add_oid_set_member(minor_status,
+ &acred->mechanisms->elements[0],
+ mechanisms);
+ if (ret == GSS_S_COMPLETE && icred)
+ ret = gss_add_oid_set_member(minor_status,
+ &icred->mechanisms->elements[0],
+ mechanisms);
+ if (ret)
+ goto out;
+ }
+ ret = GSS_S_COMPLETE;
+out:
+ if (acred)
+ HEIMDAL_MUTEX_unlock(&acred->cred_id_mutex);
+ if (icred)
+ HEIMDAL_MUTEX_unlock(&icred->cred_id_mutex);
+
+ if (aqcred_init != GSS_C_NO_CREDENTIAL)
+ ret = _gsskrb5_release_cred(minor_status, &aqcred_init);
+ if (aqcred_accept != GSS_C_NO_CREDENTIAL)
+ ret = _gsskrb5_release_cred(minor_status, &aqcred_accept);
+
+ return ret;
+}
diff --git a/source4/heimdal/lib/gssapi/krb5/inquire_cred_by_mech.c b/source4/heimdal/lib/gssapi/krb5/inquire_cred_by_mech.c
new file mode 100644
index 0000000000..cdf05d7934
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/krb5/inquire_cred_by_mech.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2003, 2006, 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5/gsskrb5_locl.h"
+
+RCSID("$Id$");
+
+OM_uint32 _gsskrb5_inquire_cred_by_mech (
+ OM_uint32 * minor_status,
+ const gss_cred_id_t cred_handle,
+ const gss_OID mech_type,
+ gss_name_t * name,
+ OM_uint32 * initiator_lifetime,
+ OM_uint32 * acceptor_lifetime,
+ gss_cred_usage_t * cred_usage
+ )
+{
+ gss_cred_usage_t usage;
+ OM_uint32 maj_stat;
+ OM_uint32 lifetime;
+
+ maj_stat =
+ _gsskrb5_inquire_cred (minor_status, cred_handle,
+ name, &lifetime, &usage, NULL);
+ if (maj_stat)
+ return maj_stat;
+
+ if (initiator_lifetime) {
+ if (usage == GSS_C_INITIATE || usage == GSS_C_BOTH)
+ *initiator_lifetime = lifetime;
+ else
+ *initiator_lifetime = 0;
+ }
+
+ if (acceptor_lifetime) {
+ if (usage == GSS_C_ACCEPT || usage == GSS_C_BOTH)
+ *acceptor_lifetime = lifetime;
+ else
+ *acceptor_lifetime = 0;
+ }
+
+ if (cred_usage)
+ *cred_usage = usage;
+
+ return GSS_S_COMPLETE;
+}
diff --git a/source4/heimdal/lib/gssapi/krb5/inquire_cred_by_oid.c b/source4/heimdal/lib/gssapi/krb5/inquire_cred_by_oid.c
new file mode 100644
index 0000000000..2bcc17683b
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/krb5/inquire_cred_by_oid.c
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2004, PADL Software Pty Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of PADL Software nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5/gsskrb5_locl.h"
+
+RCSID("$Id$");
+
+OM_uint32 _gsskrb5_inquire_cred_by_oid
+ (OM_uint32 * minor_status,
+ const gss_cred_id_t cred_handle,
+ const gss_OID desired_object,
+ gss_buffer_set_t *data_set)
+{
+ krb5_context context;
+ gsskrb5_cred cred = (gsskrb5_cred)cred_handle;
+ krb5_error_code ret;
+ gss_buffer_desc buffer;
+ char *str;
+
+ GSSAPI_KRB5_INIT (&context);
+
+ if (gss_oid_equal(desired_object, GSS_KRB5_COPY_CCACHE_X) == 0) {
+ *minor_status = EINVAL;
+ return GSS_S_FAILURE;
+ }
+
+ HEIMDAL_MUTEX_lock(&cred->cred_id_mutex);
+
+ if (cred->ccache == NULL) {
+ HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex);
+ *minor_status = EINVAL;
+ return GSS_S_FAILURE;
+ }
+
+ ret = krb5_cc_get_full_name(context, cred->ccache, &str);
+ HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex);
+ if (ret) {
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+
+ buffer.value = str;
+ buffer.length = strlen(str);
+
+ ret = gss_add_buffer_set_member(minor_status, &buffer, data_set);
+ if (ret != GSS_S_COMPLETE)
+ _gsskrb5_clear_status ();
+
+ free(str);
+
+ *minor_status = 0;
+ return GSS_S_COMPLETE;
+}
+
diff --git a/source4/heimdal/lib/gssapi/krb5/inquire_mechs_for_name.c b/source4/heimdal/lib/gssapi/krb5/inquire_mechs_for_name.c
new file mode 100644
index 0000000000..4fd730deab
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/krb5/inquire_mechs_for_name.c
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5/gsskrb5_locl.h"
+
+RCSID("$Id$");
+
+OM_uint32 _gsskrb5_inquire_mechs_for_name (
+ OM_uint32 * minor_status,
+ const gss_name_t input_name,
+ gss_OID_set * mech_types
+ )
+{
+ OM_uint32 ret;
+
+ ret = gss_create_empty_oid_set(minor_status, mech_types);
+ if (ret)
+ return ret;
+
+ ret = gss_add_oid_set_member(minor_status,
+ GSS_KRB5_MECHANISM,
+ mech_types);
+ if (ret)
+ gss_release_oid_set(NULL, mech_types);
+
+ return ret;
+}
diff --git a/source4/heimdal/lib/gssapi/krb5/inquire_names_for_mech.c b/source4/heimdal/lib/gssapi/krb5/inquire_names_for_mech.c
new file mode 100644
index 0000000000..a9d6495c7b
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/krb5/inquire_names_for_mech.c
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5/gsskrb5_locl.h"
+
+RCSID("$Id$");
+
+
+static gss_OID *name_list[] = {
+ &GSS_C_NT_HOSTBASED_SERVICE,
+ &GSS_C_NT_USER_NAME,
+ &GSS_KRB5_NT_PRINCIPAL_NAME,
+ &GSS_C_NT_EXPORT_NAME,
+ NULL
+};
+
+OM_uint32 _gsskrb5_inquire_names_for_mech (
+ OM_uint32 * minor_status,
+ const gss_OID mechanism,
+ gss_OID_set * name_types
+ )
+{
+ OM_uint32 ret;
+ int i;
+
+ *minor_status = 0;
+
+ if (gss_oid_equal(mechanism, GSS_KRB5_MECHANISM) == 0 &&
+ gss_oid_equal(mechanism, GSS_C_NULL_OID) == 0) {
+ *name_types = GSS_C_NO_OID_SET;
+ return GSS_S_BAD_MECH;
+ }
+
+ ret = gss_create_empty_oid_set(minor_status, name_types);
+ if (ret != GSS_S_COMPLETE)
+ return ret;
+
+ for (i = 0; name_list[i] != NULL; i++) {
+ ret = gss_add_oid_set_member(minor_status,
+ *(name_list[i]),
+ name_types);
+ if (ret != GSS_S_COMPLETE)
+ break;
+ }
+
+ if (ret != GSS_S_COMPLETE)
+ gss_release_oid_set(NULL, name_types);
+
+ return GSS_S_COMPLETE;
+}
diff --git a/source4/heimdal/lib/gssapi/krb5/inquire_sec_context_by_oid.c b/source4/heimdal/lib/gssapi/krb5/inquire_sec_context_by_oid.c
new file mode 100644
index 0000000000..8d40706294
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/krb5/inquire_sec_context_by_oid.c
@@ -0,0 +1,561 @@
+/*
+ * Copyright (c) 2004, PADL Software Pty Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of PADL Software nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5/gsskrb5_locl.h"
+
+RCSID("$Id$");
+
+static int
+oid_prefix_equal(gss_OID oid_enc, gss_OID prefix_enc, unsigned *suffix)
+{
+ int ret;
+ heim_oid oid;
+ heim_oid prefix;
+
+ *suffix = 0;
+
+ ret = der_get_oid(oid_enc->elements, oid_enc->length,
+ &oid, NULL);
+ if (ret) {
+ return 0;
+ }
+
+ ret = der_get_oid(prefix_enc->elements, prefix_enc->length,
+ &prefix, NULL);
+ if (ret) {
+ der_free_oid(&oid);
+ return 0;
+ }
+
+ ret = 0;
+
+ if (oid.length - 1 == prefix.length) {
+ *suffix = oid.components[oid.length - 1];
+ oid.length--;
+ ret = (der_heim_oid_cmp(&oid, &prefix) == 0);
+ oid.length++;
+ }
+
+ der_free_oid(&oid);
+ der_free_oid(&prefix);
+
+ return ret;
+}
+
+static OM_uint32 inquire_sec_context_tkt_flags
+ (OM_uint32 *minor_status,
+ const gsskrb5_ctx context_handle,
+ gss_buffer_set_t *data_set)
+{
+ OM_uint32 tkt_flags;
+ unsigned char buf[4];
+ gss_buffer_desc value;
+
+ HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex);
+
+ if (context_handle->ticket == NULL) {
+ HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
+ _gsskrb5_set_status(EINVAL, "No ticket from which to obtain flags");
+ *minor_status = EINVAL;
+ return GSS_S_BAD_MECH;
+ }
+
+ tkt_flags = TicketFlags2int(context_handle->ticket->ticket.flags);
+ HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
+
+ _gsskrb5_encode_om_uint32(tkt_flags, buf);
+ value.length = sizeof(buf);
+ value.value = buf;
+
+ return gss_add_buffer_set_member(minor_status,
+ &value,
+ data_set);
+}
+
+enum keytype { ACCEPTOR_KEY, INITIATOR_KEY, TOKEN_KEY };
+
+static OM_uint32 inquire_sec_context_get_subkey
+ (OM_uint32 *minor_status,
+ const gsskrb5_ctx context_handle,
+ krb5_context context,
+ enum keytype keytype,
+ gss_buffer_set_t *data_set)
+{
+ krb5_keyblock *key = NULL;
+ krb5_storage *sp = NULL;
+ krb5_data data;
+ OM_uint32 maj_stat = GSS_S_COMPLETE;
+ krb5_error_code ret;
+
+ krb5_data_zero(&data);
+
+ sp = krb5_storage_emem();
+ if (sp == NULL) {
+ _gsskrb5_clear_status();
+ ret = ENOMEM;
+ goto out;
+ }
+
+ HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex);
+ switch(keytype) {
+ case ACCEPTOR_KEY:
+ ret = _gsskrb5i_get_acceptor_subkey(context_handle, context, &key);
+ break;
+ case INITIATOR_KEY:
+ ret = _gsskrb5i_get_initiator_subkey(context_handle, context, &key);
+ break;
+ case TOKEN_KEY:
+ ret = _gsskrb5i_get_token_key(context_handle, context, &key);
+ break;
+ default:
+ _gsskrb5_set_status(EINVAL, "%d is not a valid subkey type", keytype);
+ ret = EINVAL;
+ break;
+ }
+ HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
+ if (ret)
+ goto out;
+ if (key == NULL) {
+ _gsskrb5_set_status(EINVAL, "have no subkey of type %d", keytype);
+ ret = EINVAL;
+ goto out;
+ }
+
+ ret = krb5_store_keyblock(sp, *key);
+ krb5_free_keyblock (context, key);
+ if (ret)
+ goto out;
+
+ ret = krb5_storage_to_data(sp, &data);
+ if (ret)
+ goto out;
+
+ {
+ gss_buffer_desc value;
+
+ value.length = data.length;
+ value.value = data.data;
+
+ maj_stat = gss_add_buffer_set_member(minor_status,
+ &value,
+ data_set);
+ }
+
+out:
+ krb5_data_free(&data);
+ if (sp)
+ krb5_storage_free(sp);
+ if (ret) {
+ *minor_status = ret;
+ maj_stat = GSS_S_FAILURE;
+ }
+ return maj_stat;
+}
+
+static OM_uint32 inquire_sec_context_authz_data
+ (OM_uint32 *minor_status,
+ const gsskrb5_ctx context_handle,
+ krb5_context context,
+ unsigned ad_type,
+ gss_buffer_set_t *data_set)
+{
+ krb5_data data;
+ gss_buffer_desc ad_data;
+ OM_uint32 ret;
+
+ *minor_status = 0;
+ *data_set = GSS_C_NO_BUFFER_SET;
+
+ HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex);
+ if (context_handle->ticket == NULL) {
+ HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
+ *minor_status = EINVAL;
+ _gsskrb5_set_status(EINVAL, "No ticket to obtain authz data from");
+ return GSS_S_NO_CONTEXT;
+ }
+
+ ret = krb5_ticket_get_authorization_data_type(context,
+ context_handle->ticket,
+ ad_type,
+ &data);
+ HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
+ if (ret) {
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+
+ ad_data.value = data.data;
+ ad_data.length = data.length;
+
+ ret = gss_add_buffer_set_member(minor_status,
+ &ad_data,
+ data_set);
+
+ krb5_data_free(&data);
+
+ return ret;
+}
+
+static OM_uint32 inquire_sec_context_has_updated_spnego
+ (OM_uint32 *minor_status,
+ const gsskrb5_ctx context_handle,
+ gss_buffer_set_t *data_set)
+{
+ int is_updated = 0;
+
+ *minor_status = 0;
+ *data_set = GSS_C_NO_BUFFER_SET;
+
+ /*
+ * For Windows SPNEGO implementations, both the initiator and the
+ * acceptor are assumed to have been updated if a "newer" [CLAR] or
+ * different enctype is negotiated for use by the Kerberos GSS-API
+ * mechanism.
+ */
+ HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex);
+ _gsskrb5i_is_cfx(context_handle, &is_updated);
+ if (is_updated == 0) {
+ krb5_keyblock *acceptor_subkey;
+
+ if (context_handle->more_flags & LOCAL)
+ acceptor_subkey = context_handle->auth_context->remote_subkey;
+ else
+ acceptor_subkey = context_handle->auth_context->local_subkey;
+
+ if (acceptor_subkey != NULL)
+ is_updated = (acceptor_subkey->keytype !=
+ context_handle->auth_context->keyblock->keytype);
+ }
+ HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
+
+ return is_updated ? GSS_S_COMPLETE : GSS_S_FAILURE;
+}
+
+/*
+ *
+ */
+
+static OM_uint32
+export_lucid_sec_context_v1(OM_uint32 *minor_status,
+ gsskrb5_ctx context_handle,
+ krb5_context context,
+ gss_buffer_set_t *data_set)
+{
+ krb5_storage *sp = NULL;
+ OM_uint32 major_status = GSS_S_COMPLETE;
+ krb5_error_code ret;
+ krb5_keyblock *key = NULL;
+ int32_t number;
+ int is_cfx;
+ krb5_data data;
+
+ *minor_status = 0;
+
+ HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex);
+
+ _gsskrb5i_is_cfx(context_handle, &is_cfx);
+
+ sp = krb5_storage_emem();
+ if (sp == NULL) {
+ _gsskrb5_clear_status();
+ ret = ENOMEM;
+ goto out;
+ }
+
+ ret = krb5_store_int32(sp, 1);
+ if (ret) goto out;
+ ret = krb5_store_int32(sp, (context_handle->more_flags & LOCAL) ? 1 : 0);
+ if (ret) goto out;
+ ret = krb5_store_int32(sp, context_handle->lifetime);
+ if (ret) goto out;
+ krb5_auth_con_getlocalseqnumber (context,
+ context_handle->auth_context,
+ &number);
+ ret = krb5_store_uint32(sp, (uint32_t)0); /* store top half as zero */
+ if (ret) goto out;
+ ret = krb5_store_uint32(sp, (uint32_t)number);
+ if (ret) goto out;
+ krb5_auth_getremoteseqnumber (context,
+ context_handle->auth_context,
+ &number);
+ ret = krb5_store_uint32(sp, (uint32_t)0); /* store top half as zero */
+ if (ret) goto out;
+ ret = krb5_store_uint32(sp, (uint32_t)number);
+ if (ret) goto out;
+ ret = krb5_store_int32(sp, (is_cfx) ? 1 : 0);
+ if (ret) goto out;
+
+ ret = _gsskrb5i_get_token_key(context_handle, context, &key);
+ if (ret) goto out;
+
+ if (is_cfx == 0) {
+ int sign_alg, seal_alg;
+
+ switch (key->keytype) {
+ case ETYPE_DES_CBC_CRC:
+ case ETYPE_DES_CBC_MD4:
+ case ETYPE_DES_CBC_MD5:
+ sign_alg = 0;
+ seal_alg = 0;
+ break;
+ case ETYPE_DES3_CBC_MD5:
+ case ETYPE_DES3_CBC_SHA1:
+ sign_alg = 4;
+ seal_alg = 2;
+ break;
+ case ETYPE_ARCFOUR_HMAC_MD5:
+ case ETYPE_ARCFOUR_HMAC_MD5_56:
+ sign_alg = 17;
+ seal_alg = 16;
+ break;
+ default:
+ sign_alg = -1;
+ seal_alg = -1;
+ break;
+ }
+ ret = krb5_store_int32(sp, sign_alg);
+ if (ret) goto out;
+ ret = krb5_store_int32(sp, seal_alg);
+ if (ret) goto out;
+ /* ctx_key */
+ ret = krb5_store_keyblock(sp, *key);
+ if (ret) goto out;
+ } else {
+ int subkey_p = (context_handle->more_flags & ACCEPTOR_SUBKEY) ? 1 : 0;
+
+ /* have_acceptor_subkey */
+ ret = krb5_store_int32(sp, subkey_p);
+ if (ret) goto out;
+ /* ctx_key */
+ ret = krb5_store_keyblock(sp, *key);
+ if (ret) goto out;
+ /* acceptor_subkey */
+ if (subkey_p) {
+ ret = krb5_store_keyblock(sp, *key);
+ if (ret) goto out;
+ }
+ }
+ ret = krb5_storage_to_data(sp, &data);
+ if (ret) goto out;
+
+ {
+ gss_buffer_desc ad_data;
+
+ ad_data.value = data.data;
+ ad_data.length = data.length;
+
+ ret = gss_add_buffer_set_member(minor_status, &ad_data, data_set);
+ krb5_data_free(&data);
+ if (ret)
+ goto out;
+ }
+
+out:
+ if (key)
+ krb5_free_keyblock (context, key);
+ if (sp)
+ krb5_storage_free(sp);
+ if (ret) {
+ *minor_status = ret;
+ major_status = GSS_S_FAILURE;
+ }
+ HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
+ return major_status;
+}
+
+static OM_uint32
+get_authtime(OM_uint32 *minor_status,
+ gsskrb5_ctx ctx,
+ gss_buffer_set_t *data_set)
+
+{
+ gss_buffer_desc value;
+ unsigned char buf[4];
+ OM_uint32 authtime;
+
+ HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
+ if (ctx->ticket == NULL) {
+ HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
+ _gsskrb5_set_status(EINVAL, "No ticket to obtain auth time from");
+ *minor_status = EINVAL;
+ return GSS_S_FAILURE;
+ }
+
+ authtime = ctx->ticket->ticket.authtime;
+
+ HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
+
+ _gsskrb5_encode_om_uint32(authtime, buf);
+ value.length = sizeof(buf);
+ value.value = buf;
+
+ return gss_add_buffer_set_member(minor_status,
+ &value,
+ data_set);
+}
+
+
+static OM_uint32
+get_service_keyblock
+ (OM_uint32 *minor_status,
+ gsskrb5_ctx ctx,
+ gss_buffer_set_t *data_set)
+{
+ krb5_storage *sp = NULL;
+ krb5_data data;
+ OM_uint32 maj_stat = GSS_S_COMPLETE;
+ krb5_error_code ret = EINVAL;
+
+ sp = krb5_storage_emem();
+ if (sp == NULL) {
+ _gsskrb5_clear_status();
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+
+ HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
+ if (ctx->service_keyblock == NULL) {
+ HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
+ _gsskrb5_set_status(EINVAL, "No service keyblock on gssapi context");
+ *minor_status = EINVAL;
+ return GSS_S_FAILURE;
+ }
+
+ krb5_data_zero(&data);
+
+ ret = krb5_store_keyblock(sp, *ctx->service_keyblock);
+
+ HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
+
+ if (ret)
+ goto out;
+
+ ret = krb5_storage_to_data(sp, &data);
+ if (ret)
+ goto out;
+
+ {
+ gss_buffer_desc value;
+
+ value.length = data.length;
+ value.value = data.data;
+
+ maj_stat = gss_add_buffer_set_member(minor_status,
+ &value,
+ data_set);
+ }
+
+out:
+ krb5_data_free(&data);
+ if (sp)
+ krb5_storage_free(sp);
+ if (ret) {
+ *minor_status = ret;
+ maj_stat = GSS_S_FAILURE;
+ }
+ return maj_stat;
+}
+/*
+ *
+ */
+
+OM_uint32 _gsskrb5_inquire_sec_context_by_oid
+ (OM_uint32 *minor_status,
+ const gss_ctx_id_t context_handle,
+ const gss_OID desired_object,
+ gss_buffer_set_t *data_set)
+{
+ krb5_context context;
+ const gsskrb5_ctx ctx = (const gsskrb5_ctx) context_handle;
+ unsigned suffix;
+
+ if (ctx == NULL) {
+ *minor_status = EINVAL;
+ return GSS_S_NO_CONTEXT;
+ }
+
+ GSSAPI_KRB5_INIT (&context);
+
+ if (gss_oid_equal(desired_object, GSS_KRB5_GET_TKT_FLAGS_X)) {
+ return inquire_sec_context_tkt_flags(minor_status,
+ ctx,
+ data_set);
+ } else if (gss_oid_equal(desired_object, GSS_C_PEER_HAS_UPDATED_SPNEGO)) {
+ return inquire_sec_context_has_updated_spnego(minor_status,
+ ctx,
+ data_set);
+ } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_SUBKEY_X)) {
+ return inquire_sec_context_get_subkey(minor_status,
+ ctx,
+ context,
+ TOKEN_KEY,
+ data_set);
+ } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_INITIATOR_SUBKEY_X)) {
+ return inquire_sec_context_get_subkey(minor_status,
+ ctx,
+ context,
+ INITIATOR_KEY,
+ data_set);
+ } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_ACCEPTOR_SUBKEY_X)) {
+ return inquire_sec_context_get_subkey(minor_status,
+ ctx,
+ context,
+ ACCEPTOR_KEY,
+ data_set);
+ } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_AUTHTIME_X)) {
+ return get_authtime(minor_status, ctx, data_set);
+ } else if (oid_prefix_equal(desired_object,
+ GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_X,
+ &suffix)) {
+ return inquire_sec_context_authz_data(minor_status,
+ ctx,
+ context,
+ suffix,
+ data_set);
+ } else if (oid_prefix_equal(desired_object,
+ GSS_KRB5_EXPORT_LUCID_CONTEXT_X,
+ &suffix)) {
+ if (suffix == 1)
+ return export_lucid_sec_context_v1(minor_status,
+ ctx,
+ context,
+ data_set);
+ *minor_status = 0;
+ return GSS_S_FAILURE;
+ } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_SERVICE_KEYBLOCK_X)) {
+ return get_service_keyblock(minor_status, ctx, data_set);
+ } else {
+ *minor_status = 0;
+ return GSS_S_FAILURE;
+ }
+}
+
diff --git a/source4/heimdal/lib/gssapi/krb5/prf.c b/source4/heimdal/lib/gssapi/krb5/prf.c
new file mode 100644
index 0000000000..9cbe603435
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/krb5/prf.c
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5/gsskrb5_locl.h"
+
+RCSID("$Id$");
+
+OM_uint32
+_gsskrb5_pseudo_random(OM_uint32 *minor_status,
+ gss_ctx_id_t context_handle,
+ int prf_key,
+ const gss_buffer_t prf_in,
+ ssize_t desired_output_len,
+ gss_buffer_t prf_out)
+{
+ gsskrb5_ctx ctx = (gsskrb5_ctx)context_handle;
+ krb5_context context;
+ krb5_error_code ret;
+ krb5_crypto crypto;
+ krb5_data input, output;
+ uint32_t num;
+ unsigned char *p;
+ krb5_keyblock *key = NULL;
+
+ if (ctx == NULL) {
+ *minor_status = 0;
+ return GSS_S_NO_CONTEXT;
+ }
+
+ if (desired_output_len <= 0) {
+ *minor_status = 0;
+ return GSS_S_FAILURE;
+ }
+
+ GSSAPI_KRB5_INIT (&context);
+
+ switch(prf_key) {
+ case GSS_C_PRF_KEY_FULL:
+ _gsskrb5i_get_acceptor_subkey(ctx, context, &key);
+ break;
+ case GSS_C_PRF_KEY_PARTIAL:
+ _gsskrb5i_get_initiator_subkey(ctx, context, &key);
+ break;
+ default:
+ _gsskrb5_set_status(EINVAL, "unknown kerberos prf_key");
+ *minor_status = EINVAL;
+ return GSS_S_FAILURE;
+ }
+
+ if (key == NULL) {
+ _gsskrb5_set_status(EINVAL, "no prf_key found");
+ *minor_status = EINVAL;
+ return GSS_S_FAILURE;
+ }
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ krb5_free_keyblock (context, key);
+ if (ret) {
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+
+ prf_out->value = malloc(desired_output_len);
+ if (prf_out->value == NULL) {
+ _gsskrb5_set_status(GSS_KRB5_S_KG_INPUT_TOO_LONG, "Out of memory");
+ *minor_status = GSS_KRB5_S_KG_INPUT_TOO_LONG;
+ krb5_crypto_destroy(context, crypto);
+ return GSS_S_FAILURE;
+ }
+ prf_out->length = desired_output_len;
+
+ HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
+
+ input.length = prf_in->length + 4;
+ input.data = malloc(prf_in->length + 4);
+ if (input.data == NULL) {
+ OM_uint32 junk;
+ _gsskrb5_set_status(GSS_KRB5_S_KG_INPUT_TOO_LONG, "Out of memory");
+ *minor_status = GSS_KRB5_S_KG_INPUT_TOO_LONG;
+ gss_release_buffer(&junk, prf_out);
+ krb5_crypto_destroy(context, crypto);
+ HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
+ return GSS_S_FAILURE;
+ }
+ memcpy(((unsigned char *)input.data) + 4, prf_in->value, prf_in->length);
+
+ num = 0;
+ p = prf_out->value;
+ while(desired_output_len > 0) {
+ _gsskrb5_encode_om_uint32(num, input.data);
+ ret = krb5_crypto_prf(context, crypto, &input, &output);
+ if (ret) {
+ OM_uint32 junk;
+ *minor_status = ret;
+ free(input.data);
+ gss_release_buffer(&junk, prf_out);
+ krb5_crypto_destroy(context, crypto);
+ HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
+ return GSS_S_FAILURE;
+ }
+ memcpy(p, output.data, min(desired_output_len, output.length));
+ p += output.length;
+ desired_output_len -= output.length;
+ krb5_data_free(&output);
+ num++;
+ }
+
+ krb5_crypto_destroy(context, crypto);
+
+ HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
+
+ return GSS_S_COMPLETE;
+}
diff --git a/source4/heimdal/lib/gssapi/krb5/process_context_token.c b/source4/heimdal/lib/gssapi/krb5/process_context_token.c
new file mode 100644
index 0000000000..6892d3ca60
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/krb5/process_context_token.c
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5/gsskrb5_locl.h"
+
+RCSID("$Id$");
+
+OM_uint32 _gsskrb5_process_context_token (
+ OM_uint32 *minor_status,
+ const gss_ctx_id_t context_handle,
+ const gss_buffer_t token_buffer
+ )
+{
+ krb5_context context;
+ OM_uint32 ret = GSS_S_FAILURE;
+ gss_buffer_desc empty_buffer;
+ gss_qop_t qop_state;
+
+ empty_buffer.length = 0;
+ empty_buffer.value = NULL;
+
+ GSSAPI_KRB5_INIT (&context);
+
+ qop_state = GSS_C_QOP_DEFAULT;
+
+ ret = _gsskrb5_verify_mic_internal(minor_status,
+ (gsskrb5_ctx)context_handle,
+ context,
+ token_buffer, &empty_buffer,
+ GSS_C_QOP_DEFAULT, "\x01\x02");
+
+ if (ret == GSS_S_COMPLETE)
+ ret = _gsskrb5_delete_sec_context(minor_status,
+ rk_UNCONST(&context_handle),
+ GSS_C_NO_BUFFER);
+ if (ret == GSS_S_COMPLETE)
+ *minor_status = 0;
+
+ return ret;
+}
diff --git a/source4/heimdal/lib/gssapi/krb5/release_buffer.c b/source4/heimdal/lib/gssapi/krb5/release_buffer.c
new file mode 100644
index 0000000000..a0f37c06f4
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/krb5/release_buffer.c
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 1997 - 2000, 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5/gsskrb5_locl.h"
+
+RCSID("$Id$");
+
+OM_uint32 _gsskrb5_release_buffer
+ (OM_uint32 * minor_status,
+ gss_buffer_t buffer
+ )
+{
+ *minor_status = 0;
+ free (buffer->value);
+ buffer->value = NULL;
+ buffer->length = 0;
+ return GSS_S_COMPLETE;
+}
diff --git a/source4/heimdal/lib/gssapi/krb5/release_cred.c b/source4/heimdal/lib/gssapi/krb5/release_cred.c
new file mode 100644
index 0000000000..5a0ec829d2
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/krb5/release_cred.c
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 1997-2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5/gsskrb5_locl.h"
+
+RCSID("$Id$");
+
+OM_uint32 _gsskrb5_release_cred
+ (OM_uint32 * minor_status,
+ gss_cred_id_t * cred_handle
+ )
+{
+ krb5_context context;
+ gsskrb5_cred cred;
+ OM_uint32 junk;
+
+ *minor_status = 0;
+
+ if (*cred_handle == NULL)
+ return GSS_S_COMPLETE;
+
+ cred = (gsskrb5_cred)*cred_handle;
+ *cred_handle = GSS_C_NO_CREDENTIAL;
+
+ GSSAPI_KRB5_INIT (&context);
+
+ HEIMDAL_MUTEX_lock(&cred->cred_id_mutex);
+
+ if (cred->principal != NULL)
+ krb5_free_principal(context, cred->principal);
+ if (cred->keytab != NULL)
+ krb5_kt_close(context, cred->keytab);
+ if (cred->ccache != NULL) {
+ const krb5_cc_ops *ops;
+ ops = krb5_cc_get_ops(context, cred->ccache);
+ if (cred->cred_flags & GSS_CF_DESTROY_CRED_ON_RELEASE)
+ krb5_cc_destroy(context, cred->ccache);
+ else
+ krb5_cc_close(context, cred->ccache);
+ }
+ gss_release_oid_set(&junk, &cred->mechanisms);
+ if (cred->enctypes)
+ free(cred->enctypes);
+ HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex);
+ HEIMDAL_MUTEX_destroy(&cred->cred_id_mutex);
+ memset(cred, 0, sizeof(*cred));
+ free(cred);
+ return GSS_S_COMPLETE;
+}
+
diff --git a/source4/heimdal/lib/gssapi/krb5/release_name.c b/source4/heimdal/lib/gssapi/krb5/release_name.c
new file mode 100644
index 0000000000..d39c705433
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/krb5/release_name.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 1997 - 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5/gsskrb5_locl.h"
+
+RCSID("$Id$");
+
+OM_uint32 _gsskrb5_release_name
+ (OM_uint32 * minor_status,
+ gss_name_t * input_name
+ )
+{
+ krb5_context context;
+ krb5_principal name = (krb5_principal)*input_name;
+
+ *minor_status = 0;
+
+ GSSAPI_KRB5_INIT (&context);
+
+ *input_name = GSS_C_NO_NAME;
+
+ krb5_free_principal(context, name);
+
+ return GSS_S_COMPLETE;
+}
diff --git a/source4/heimdal/lib/gssapi/krb5/sequence.c b/source4/heimdal/lib/gssapi/krb5/sequence.c
new file mode 100644
index 0000000000..61164ffec1
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/krb5/sequence.c
@@ -0,0 +1,294 @@
+/*
+ * Copyright (c) 2003 - 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5/gsskrb5_locl.h"
+
+RCSID("$Id$");
+
+#define DEFAULT_JITTER_WINDOW 20
+
+struct gss_msg_order {
+ OM_uint32 flags;
+ OM_uint32 start;
+ OM_uint32 length;
+ OM_uint32 jitter_window;
+ OM_uint32 first_seq;
+ OM_uint32 elem[1];
+};
+
+
+/*
+ *
+ */
+
+static OM_uint32
+msg_order_alloc(OM_uint32 *minor_status,
+ struct gss_msg_order **o,
+ OM_uint32 jitter_window)
+{
+ size_t len;
+
+ len = jitter_window * sizeof((*o)->elem[0]);
+ len += sizeof(**o);
+ len -= sizeof((*o)->elem[0]);
+
+ *o = calloc(1, len);
+ if (*o == NULL) {
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+
+ *minor_status = 0;
+ return GSS_S_COMPLETE;
+}
+
+/*
+ *
+ */
+
+OM_uint32
+_gssapi_msg_order_create(OM_uint32 *minor_status,
+ struct gss_msg_order **o,
+ OM_uint32 flags,
+ OM_uint32 seq_num,
+ OM_uint32 jitter_window,
+ int use_64)
+{
+ OM_uint32 ret;
+
+ if (jitter_window == 0)
+ jitter_window = DEFAULT_JITTER_WINDOW;
+
+ ret = msg_order_alloc(minor_status, o, jitter_window);
+ if(ret != GSS_S_COMPLETE)
+ return ret;
+
+ (*o)->flags = flags;
+ (*o)->length = 0;
+ (*o)->first_seq = seq_num;
+ (*o)->jitter_window = jitter_window;
+ (*o)->elem[0] = seq_num - 1;
+
+ *minor_status = 0;
+ return GSS_S_COMPLETE;
+}
+
+OM_uint32
+_gssapi_msg_order_destroy(struct gss_msg_order **m)
+{
+ free(*m);
+ *m = NULL;
+ return GSS_S_COMPLETE;
+}
+
+static void
+elem_set(struct gss_msg_order *o, unsigned int slot, OM_uint32 val)
+{
+ o->elem[slot % o->jitter_window] = val;
+}
+
+static void
+elem_insert(struct gss_msg_order *o,
+ unsigned int after_slot,
+ OM_uint32 seq_num)
+{
+ assert(o->jitter_window > after_slot);
+
+ if (o->length > after_slot)
+ memmove(&o->elem[after_slot + 1], &o->elem[after_slot],
+ (o->length - after_slot - 1) * sizeof(o->elem[0]));
+
+ elem_set(o, after_slot, seq_num);
+
+ if (o->length < o->jitter_window)
+ o->length++;
+}
+
+/* rule 1: expected sequence number */
+/* rule 2: > expected sequence number */
+/* rule 3: seqnum < seqnum(first) */
+/* rule 4+5: seqnum in [seqnum(first),seqnum(last)] */
+
+OM_uint32
+_gssapi_msg_order_check(struct gss_msg_order *o, OM_uint32 seq_num)
+{
+ OM_uint32 r;
+ int i;
+
+ if (o == NULL)
+ return GSS_S_COMPLETE;
+
+ if ((o->flags & (GSS_C_REPLAY_FLAG|GSS_C_SEQUENCE_FLAG)) == 0)
+ return GSS_S_COMPLETE;
+
+ /* check if the packet is the next in order */
+ if (o->elem[0] == seq_num - 1) {
+ elem_insert(o, 0, seq_num);
+ return GSS_S_COMPLETE;
+ }
+
+ r = (o->flags & (GSS_C_REPLAY_FLAG|GSS_C_SEQUENCE_FLAG))==GSS_C_REPLAY_FLAG;
+
+ /* sequence number larger then largest sequence number
+ * or smaller then the first sequence number */
+ if (seq_num > o->elem[0]
+ || seq_num < o->first_seq
+ || o->length == 0)
+ {
+ elem_insert(o, 0, seq_num);
+ if (r) {
+ return GSS_S_COMPLETE;
+ } else {
+ return GSS_S_GAP_TOKEN;
+ }
+ }
+
+ assert(o->length > 0);
+
+ /* sequence number smaller the first sequence number */
+ if (seq_num < o->elem[o->length - 1]) {
+ if (r)
+ return(GSS_S_OLD_TOKEN);
+ else
+ return(GSS_S_UNSEQ_TOKEN);
+ }
+
+ if (seq_num == o->elem[o->length - 1]) {
+ return GSS_S_DUPLICATE_TOKEN;
+ }
+
+ for (i = 0; i < o->length - 1; i++) {
+ if (o->elem[i] == seq_num)
+ return GSS_S_DUPLICATE_TOKEN;
+ if (o->elem[i + 1] < seq_num && o->elem[i] < seq_num) {
+ elem_insert(o, i, seq_num);
+ if (r)
+ return GSS_S_COMPLETE;
+ else
+ return GSS_S_UNSEQ_TOKEN;
+ }
+ }
+
+ return GSS_S_FAILURE;
+}
+
+OM_uint32
+_gssapi_msg_order_f(OM_uint32 flags)
+{
+ return flags & (GSS_C_SEQUENCE_FLAG|GSS_C_REPLAY_FLAG);
+}
+
+/*
+ * Translate `o` into inter-process format and export in to `sp'.
+ */
+
+krb5_error_code
+_gssapi_msg_order_export(krb5_storage *sp, struct gss_msg_order *o)
+{
+ krb5_error_code kret;
+ OM_uint32 i;
+
+ kret = krb5_store_int32(sp, o->flags);
+ if (kret)
+ return kret;
+ kret = krb5_store_int32(sp, o->start);
+ if (kret)
+ return kret;
+ kret = krb5_store_int32(sp, o->length);
+ if (kret)
+ return kret;
+ kret = krb5_store_int32(sp, o->jitter_window);
+ if (kret)
+ return kret;
+ kret = krb5_store_int32(sp, o->first_seq);
+ if (kret)
+ return kret;
+
+ for (i = 0; i < o->jitter_window; i++) {
+ kret = krb5_store_int32(sp, o->elem[i]);
+ if (kret)
+ return kret;
+ }
+
+ return 0;
+}
+
+OM_uint32
+_gssapi_msg_order_import(OM_uint32 *minor_status,
+ krb5_storage *sp,
+ struct gss_msg_order **o)
+{
+ OM_uint32 ret;
+ krb5_error_code kret;
+ int32_t i, flags, start, length, jitter_window, first_seq;
+
+ kret = krb5_ret_int32(sp, &flags);
+ if (kret)
+ goto failed;
+ kret = krb5_ret_int32(sp, &start);
+ if (kret)
+ goto failed;
+ kret = krb5_ret_int32(sp, &length);
+ if (kret)
+ goto failed;
+ kret = krb5_ret_int32(sp, &jitter_window);
+ if (kret)
+ goto failed;
+ kret = krb5_ret_int32(sp, &first_seq);
+ if (kret)
+ goto failed;
+
+ ret = msg_order_alloc(minor_status, o, jitter_window);
+ if (ret != GSS_S_COMPLETE)
+ return ret;
+
+ (*o)->flags = flags;
+ (*o)->start = start;
+ (*o)->length = length;
+ (*o)->jitter_window = jitter_window;
+ (*o)->first_seq = first_seq;
+
+ for( i = 0; i < jitter_window; i++ ) {
+ kret = krb5_ret_int32(sp, (int32_t*)&((*o)->elem[i]));
+ if (kret)
+ goto failed;
+ }
+
+ *minor_status = 0;
+ return GSS_S_COMPLETE;
+
+failed:
+ _gssapi_msg_order_destroy(o);
+ *minor_status = kret;
+ return GSS_S_FAILURE;
+}
diff --git a/source4/heimdal/lib/gssapi/krb5/set_cred_option.c b/source4/heimdal/lib/gssapi/krb5/set_cred_option.c
new file mode 100644
index 0000000000..e47e6fdb6c
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/krb5/set_cred_option.c
@@ -0,0 +1,264 @@
+/*
+ * Copyright (c) 2004, PADL Software Pty Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of PADL Software nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5/gsskrb5_locl.h"
+
+RCSID("$Id$");
+
+/* 1.2.752.43.13.17 */
+static gss_OID_desc gss_krb5_cred_no_ci_flags_x_oid_desc =
+{6, rk_UNCONST("\x2a\x85\x70\x2b\x0d\x11")};
+
+gss_OID GSS_KRB5_CRED_NO_CI_FLAGS_X = &gss_krb5_cred_no_ci_flags_x_oid_desc;
+
+/* 1.2.752.43.13.18 */
+static gss_OID_desc gss_krb5_import_cred_x_oid_desc =
+{6, rk_UNCONST("\x2a\x85\x70\x2b\x0d\x12")};
+
+gss_OID GSS_KRB5_IMPORT_CRED_X = &gss_krb5_import_cred_x_oid_desc;
+
+
+
+static OM_uint32
+import_cred(OM_uint32 *minor_status,
+ krb5_context context,
+ gss_cred_id_t *cred_handle,
+ const gss_buffer_t value)
+{
+ OM_uint32 major_stat;
+ krb5_error_code ret;
+ krb5_principal keytab_principal = NULL;
+ krb5_keytab keytab = NULL;
+ krb5_storage *sp = NULL;
+ krb5_ccache id = NULL;
+ char *str;
+
+ if (cred_handle == NULL || *cred_handle != GSS_C_NO_CREDENTIAL) {
+ *minor_status = 0;
+ return GSS_S_FAILURE;
+ }
+
+ sp = krb5_storage_from_mem(value->value, value->length);
+ if (sp == NULL) {
+ *minor_status = 0;
+ return GSS_S_FAILURE;
+ }
+
+ /* credential cache name */
+ ret = krb5_ret_string(sp, &str);
+ if (ret) {
+ *minor_status = ret;
+ major_stat = GSS_S_FAILURE;
+ goto out;
+ }
+ if (str[0]) {
+ ret = krb5_cc_resolve(context, str, &id);
+ if (ret) {
+ *minor_status = ret;
+ major_stat = GSS_S_FAILURE;
+ goto out;
+ }
+ }
+ free(str);
+ str = NULL;
+
+ /* keytab principal name */
+ ret = krb5_ret_string(sp, &str);
+ if (ret == 0 && str[0])
+ ret = krb5_parse_name(context, str, &keytab_principal);
+ if (ret) {
+ *minor_status = ret;
+ major_stat = GSS_S_FAILURE;
+ goto out;
+ }
+ free(str);
+ str = NULL;
+
+ /* keytab principal */
+ ret = krb5_ret_string(sp, &str);
+ if (ret) {
+ *minor_status = ret;
+ major_stat = GSS_S_FAILURE;
+ goto out;
+ }
+ if (str[0]) {
+ ret = krb5_kt_resolve(context, str, &keytab);
+ if (ret) {
+ *minor_status = ret;
+ major_stat = GSS_S_FAILURE;
+ goto out;
+ }
+ }
+ free(str);
+ str = NULL;
+
+ major_stat = _gsskrb5_import_cred(minor_status, id, keytab_principal,
+ keytab, cred_handle);
+out:
+ if (id)
+ krb5_cc_close(context, id);
+ if (keytab_principal)
+ krb5_free_principal(context, keytab_principal);
+ if (keytab)
+ krb5_kt_close(context, keytab);
+ if (str)
+ free(str);
+ if (sp)
+ krb5_storage_free(sp);
+
+ return major_stat;
+}
+
+
+static OM_uint32
+allowed_enctypes(OM_uint32 *minor_status,
+ krb5_context context,
+ gss_cred_id_t *cred_handle,
+ const gss_buffer_t value)
+{
+ OM_uint32 major_stat;
+ krb5_error_code ret;
+ size_t len, i;
+ krb5_enctype *enctypes = NULL;
+ krb5_storage *sp = NULL;
+ gsskrb5_cred cred;
+
+ if (cred_handle == NULL || *cred_handle == GSS_C_NO_CREDENTIAL) {
+ *minor_status = 0;
+ return GSS_S_FAILURE;
+ }
+
+ cred = (gsskrb5_cred)*cred_handle;
+
+ if ((value->length % 4) != 0) {
+ *minor_status = 0;
+ major_stat = GSS_S_FAILURE;
+ goto out;
+ }
+
+ len = value->length / 4;
+ enctypes = malloc((len + 1) * 4);
+ if (enctypes == NULL) {
+ *minor_status = ENOMEM;
+ major_stat = GSS_S_FAILURE;
+ goto out;
+ }
+
+ sp = krb5_storage_from_mem(value->value, value->length);
+ if (sp == NULL) {
+ *minor_status = ENOMEM;
+ major_stat = GSS_S_FAILURE;
+ goto out;
+ }
+
+ for (i = 0; i < len; i++) {
+ uint32_t e;
+
+ ret = krb5_ret_uint32(sp, &e);
+ if (ret) {
+ *minor_status = ret;
+ major_stat = GSS_S_FAILURE;
+ goto out;
+ }
+ enctypes[i] = e;
+ }
+ enctypes[i] = 0;
+
+ if (cred->enctypes)
+ free(cred->enctypes);
+ cred->enctypes = enctypes;
+
+ krb5_storage_free(sp);
+
+ return GSS_S_COMPLETE;
+
+out:
+ if (sp)
+ krb5_storage_free(sp);
+ if (enctypes)
+ free(enctypes);
+
+ return major_stat;
+}
+
+static OM_uint32
+no_ci_flags(OM_uint32 *minor_status,
+ krb5_context context,
+ gss_cred_id_t *cred_handle,
+ const gss_buffer_t value)
+{
+ gsskrb5_cred cred;
+
+ if (cred_handle == NULL || *cred_handle == GSS_C_NO_CREDENTIAL) {
+ *minor_status = 0;
+ return GSS_S_FAILURE;
+ }
+
+ cred = (gsskrb5_cred)*cred_handle;
+ cred->cred_flags |= GSS_CF_NO_CI_FLAGS;
+
+ *minor_status = 0;
+ return GSS_S_COMPLETE;
+
+}
+
+
+OM_uint32
+_gsskrb5_set_cred_option
+ (OM_uint32 *minor_status,
+ gss_cred_id_t *cred_handle,
+ const gss_OID desired_object,
+ const gss_buffer_t value)
+{
+ krb5_context context;
+
+ GSSAPI_KRB5_INIT (&context);
+
+ if (value == GSS_C_NO_BUFFER) {
+ *minor_status = EINVAL;
+ return GSS_S_FAILURE;
+ }
+
+ if (gss_oid_equal(desired_object, GSS_KRB5_IMPORT_CRED_X))
+ return import_cred(minor_status, context, cred_handle, value);
+
+ if (gss_oid_equal(desired_object, GSS_KRB5_SET_ALLOWABLE_ENCTYPES_X))
+ return allowed_enctypes(minor_status, context, cred_handle, value);
+
+ if (gss_oid_equal(desired_object, GSS_KRB5_CRED_NO_CI_FLAGS_X)) {
+ return no_ci_flags(minor_status, context, cred_handle, value);
+ }
+
+
+ *minor_status = EINVAL;
+ return GSS_S_FAILURE;
+}
diff --git a/source4/heimdal/lib/gssapi/krb5/set_sec_context_option.c b/source4/heimdal/lib/gssapi/krb5/set_sec_context_option.c
new file mode 100644
index 0000000000..6591ab04dd
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/krb5/set_sec_context_option.c
@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 2004, PADL Software Pty Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of PADL Software nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * glue routine for _gsskrb5_inquire_sec_context_by_oid
+ */
+
+#include "krb5/gsskrb5_locl.h"
+
+RCSID("$Id$");
+
+static OM_uint32
+get_bool(OM_uint32 *minor_status,
+ const gss_buffer_t value,
+ int *flag)
+{
+ if (value->value == NULL || value->length != 1) {
+ *minor_status = EINVAL;
+ return GSS_S_FAILURE;
+ }
+ *flag = *((const char *)value->value) != 0;
+ return GSS_S_COMPLETE;
+}
+
+static OM_uint32
+get_string(OM_uint32 *minor_status,
+ const gss_buffer_t value,
+ char **str)
+{
+ if (value == NULL || value->length == 0) {
+ *str = NULL;
+ } else {
+ *str = malloc(value->length + 1);
+ if (*str == NULL) {
+ *minor_status = 0;
+ return GSS_S_UNAVAILABLE;
+ }
+ memcpy(*str, value->value, value->length);
+ (*str)[value->length] = '\0';
+ }
+ return GSS_S_COMPLETE;
+}
+
+static OM_uint32
+get_int32(OM_uint32 *minor_status,
+ const gss_buffer_t value,
+ OM_uint32 *ret)
+{
+ *minor_status = 0;
+ if (value == NULL || value->length == 0)
+ *ret = 0;
+ else if (value->length == sizeof(*ret))
+ memcpy(ret, value->value, sizeof(*ret));
+ else
+ return GSS_S_UNAVAILABLE;
+
+ return GSS_S_COMPLETE;
+}
+
+static OM_uint32
+set_int32(OM_uint32 *minor_status,
+ const gss_buffer_t value,
+ OM_uint32 set)
+{
+ *minor_status = 0;
+ if (value->length == sizeof(set))
+ memcpy(value->value, &set, sizeof(set));
+ else
+ return GSS_S_UNAVAILABLE;
+
+ return GSS_S_COMPLETE;
+}
+
+OM_uint32
+_gsskrb5_set_sec_context_option
+ (OM_uint32 *minor_status,
+ gss_ctx_id_t *context_handle,
+ const gss_OID desired_object,
+ const gss_buffer_t value)
+{
+ krb5_context context;
+ OM_uint32 maj_stat;
+
+ GSSAPI_KRB5_INIT (&context);
+
+ if (value == GSS_C_NO_BUFFER) {
+ *minor_status = EINVAL;
+ return GSS_S_FAILURE;
+ }
+
+ if (gss_oid_equal(desired_object, GSS_KRB5_COMPAT_DES3_MIC_X)) {
+ gsskrb5_ctx ctx;
+ int flag;
+
+ if (*context_handle == GSS_C_NO_CONTEXT) {
+ *minor_status = EINVAL;
+ return GSS_S_NO_CONTEXT;
+ }
+
+ maj_stat = get_bool(minor_status, value, &flag);
+ if (maj_stat != GSS_S_COMPLETE)
+ return maj_stat;
+
+ ctx = (gsskrb5_ctx)*context_handle;
+ HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
+ if (flag)
+ ctx->more_flags |= COMPAT_OLD_DES3;
+ else
+ ctx->more_flags &= ~COMPAT_OLD_DES3;
+ ctx->more_flags |= COMPAT_OLD_DES3_SELECTED;
+ HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
+ return GSS_S_COMPLETE;
+ } else if (gss_oid_equal(desired_object, GSS_KRB5_SET_DNS_CANONICALIZE_X)) {
+ int flag;
+
+ maj_stat = get_bool(minor_status, value, &flag);
+ if (maj_stat != GSS_S_COMPLETE)
+ return maj_stat;
+
+ krb5_set_dns_canonicalize_hostname(context, flag);
+ return GSS_S_COMPLETE;
+
+ } else if (gss_oid_equal(desired_object, GSS_KRB5_REGISTER_ACCEPTOR_IDENTITY_X)) {
+ char *str;
+
+ maj_stat = get_string(minor_status, value, &str);
+ if (maj_stat != GSS_S_COMPLETE)
+ return maj_stat;
+
+ _gsskrb5_register_acceptor_identity(str);
+ free(str);
+
+ *minor_status = 0;
+ return GSS_S_COMPLETE;
+
+ } else if (gss_oid_equal(desired_object, GSS_KRB5_SET_DEFAULT_REALM_X)) {
+ char *str;
+
+ maj_stat = get_string(minor_status, value, &str);
+ if (maj_stat != GSS_S_COMPLETE)
+ return maj_stat;
+ if (str == NULL) {
+ *minor_status = 0;
+ return GSS_S_CALL_INACCESSIBLE_READ;
+ }
+
+ krb5_set_default_realm(context, str);
+ free(str);
+
+ *minor_status = 0;
+ return GSS_S_COMPLETE;
+
+ } else if (gss_oid_equal(desired_object, GSS_KRB5_SEND_TO_KDC_X)) {
+
+ if (value == NULL || value->length == 0) {
+ krb5_set_send_to_kdc_func(context, NULL, NULL);
+ } else {
+ struct gsskrb5_send_to_kdc c;
+
+ if (value->length != sizeof(c)) {
+ *minor_status = EINVAL;
+ return GSS_S_FAILURE;
+ }
+ memcpy(&c, value->value, sizeof(c));
+ krb5_set_send_to_kdc_func(context,
+ (krb5_send_to_kdc_func)c.func,
+ c.ptr);
+ }
+
+ *minor_status = 0;
+ return GSS_S_COMPLETE;
+ } else if (gss_oid_equal(desired_object, GSS_KRB5_CCACHE_NAME_X)) {
+ char *str;
+
+ maj_stat = get_string(minor_status, value, &str);
+ if (maj_stat != GSS_S_COMPLETE)
+ return maj_stat;
+ if (str == NULL) {
+ *minor_status = 0;
+ return GSS_S_CALL_INACCESSIBLE_READ;
+ }
+
+ *minor_status = krb5_cc_set_default_name(context, str);
+ free(str);
+ if (*minor_status)
+ return GSS_S_FAILURE;
+
+ return GSS_S_COMPLETE;
+ } else if (gss_oid_equal(desired_object, GSS_KRB5_SET_TIME_OFFSET_X)) {
+ OM_uint32 offset;
+ time_t t;
+
+ maj_stat = get_int32(minor_status, value, &offset);
+ if (maj_stat != GSS_S_COMPLETE)
+ return maj_stat;
+
+ t = time(NULL) + offset;
+
+ krb5_set_real_time(context, t, 0);
+
+ *minor_status = 0;
+ return GSS_S_COMPLETE;
+ } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_TIME_OFFSET_X)) {
+ krb5_timestamp sec;
+ int32_t usec;
+ time_t t;
+
+ t = time(NULL);
+
+ krb5_us_timeofday (context, &sec, &usec);
+
+ maj_stat = set_int32(minor_status, value, sec - t);
+ if (maj_stat != GSS_S_COMPLETE)
+ return maj_stat;
+
+ *minor_status = 0;
+ return GSS_S_COMPLETE;
+ } else if (gss_oid_equal(desired_object, GSS_KRB5_PLUGIN_REGISTER_X)) {
+ struct gsskrb5_krb5_plugin c;
+
+ if (value->length != sizeof(c)) {
+ *minor_status = EINVAL;
+ return GSS_S_FAILURE;
+ }
+ memcpy(&c, value->value, sizeof(c));
+ krb5_plugin_register(context, c.type, c.name, c.symbol);
+
+ *minor_status = 0;
+ return GSS_S_COMPLETE;
+ }
+
+ *minor_status = EINVAL;
+ return GSS_S_FAILURE;
+}
diff --git a/source4/heimdal/lib/gssapi/krb5/unwrap.c b/source4/heimdal/lib/gssapi/krb5/unwrap.c
new file mode 100644
index 0000000000..f34f72542e
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/krb5/unwrap.c
@@ -0,0 +1,437 @@
+/*
+ * Copyright (c) 1997 - 2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5/gsskrb5_locl.h"
+
+RCSID("$Id$");
+
+static OM_uint32
+unwrap_des
+ (OM_uint32 * minor_status,
+ const gsskrb5_ctx context_handle,
+ const gss_buffer_t input_message_buffer,
+ gss_buffer_t output_message_buffer,
+ int * conf_state,
+ gss_qop_t * qop_state,
+ krb5_keyblock *key
+ )
+{
+ u_char *p, *seq;
+ size_t len;
+ MD5_CTX md5;
+ u_char hash[16];
+ DES_key_schedule schedule;
+ DES_cblock deskey;
+ DES_cblock zero;
+ int i;
+ uint32_t seq_number;
+ size_t padlength;
+ OM_uint32 ret;
+ int cstate;
+ int cmp;
+ int token_len;
+
+ if (IS_DCE_STYLE(context_handle)) {
+ token_len = 22 + 8 + 15; /* 45 */
+ } else {
+ token_len = input_message_buffer->length;
+ }
+
+ p = input_message_buffer->value;
+ ret = _gsskrb5_verify_header (&p,
+ token_len,
+ "\x02\x01",
+ GSS_KRB5_MECHANISM);
+ if (ret)
+ return ret;
+
+ if (memcmp (p, "\x00\x00", 2) != 0)
+ return GSS_S_BAD_SIG;
+ p += 2;
+ if (memcmp (p, "\x00\x00", 2) == 0) {
+ cstate = 1;
+ } else if (memcmp (p, "\xFF\xFF", 2) == 0) {
+ cstate = 0;
+ } else
+ return GSS_S_BAD_MIC;
+ p += 2;
+ if(conf_state != NULL)
+ *conf_state = cstate;
+ if (memcmp (p, "\xff\xff", 2) != 0)
+ return GSS_S_DEFECTIVE_TOKEN;
+ p += 2;
+ p += 16;
+
+ len = p - (u_char *)input_message_buffer->value;
+
+ if(cstate) {
+ /* decrypt data */
+ memcpy (&deskey, key->keyvalue.data, sizeof(deskey));
+
+ for (i = 0; i < sizeof(deskey); ++i)
+ deskey[i] ^= 0xf0;
+ DES_set_key_unchecked (&deskey, &schedule);
+ memset (&zero, 0, sizeof(zero));
+ DES_cbc_encrypt ((void *)p,
+ (void *)p,
+ input_message_buffer->length - len,
+ &schedule,
+ &zero,
+ DES_DECRYPT);
+
+ memset (deskey, 0, sizeof(deskey));
+ memset (&schedule, 0, sizeof(schedule));
+ }
+
+ if (IS_DCE_STYLE(context_handle)) {
+ padlength = 0;
+ } else {
+ /* check pad */
+ ret = _gssapi_verify_pad(input_message_buffer,
+ input_message_buffer->length - len,
+ &padlength);
+ if (ret)
+ return ret;
+ }
+
+ MD5_Init (&md5);
+ MD5_Update (&md5, p - 24, 8);
+ MD5_Update (&md5, p, input_message_buffer->length - len);
+ MD5_Final (hash, &md5);
+
+ memset (&zero, 0, sizeof(zero));
+ memcpy (&deskey, key->keyvalue.data, sizeof(deskey));
+ DES_set_key_unchecked (&deskey, &schedule);
+ DES_cbc_cksum ((void *)hash, (void *)hash, sizeof(hash),
+ &schedule, &zero);
+ if (memcmp (p - 8, hash, 8) != 0)
+ return GSS_S_BAD_MIC;
+
+ /* verify sequence number */
+
+ HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex);
+
+ p -= 16;
+ DES_set_key_unchecked (&deskey, &schedule);
+ DES_cbc_encrypt ((void *)p, (void *)p, 8,
+ &schedule, (DES_cblock *)hash, DES_DECRYPT);
+
+ memset (deskey, 0, sizeof(deskey));
+ memset (&schedule, 0, sizeof(schedule));
+
+ seq = p;
+ _gsskrb5_decode_om_uint32(seq, &seq_number);
+
+ if (context_handle->more_flags & LOCAL)
+ cmp = memcmp(&seq[4], "\xff\xff\xff\xff", 4);
+ else
+ cmp = memcmp(&seq[4], "\x00\x00\x00\x00", 4);
+
+ if (cmp != 0) {
+ HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
+ return GSS_S_BAD_MIC;
+ }
+
+ ret = _gssapi_msg_order_check(context_handle->order, seq_number);
+ if (ret) {
+ HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
+ return ret;
+ }
+
+ HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
+
+ /* copy out data */
+
+ output_message_buffer->length = input_message_buffer->length
+ - len - padlength - 8;
+ output_message_buffer->value = malloc(output_message_buffer->length);
+ if(output_message_buffer->length != 0 && output_message_buffer->value == NULL)
+ return GSS_S_FAILURE;
+ memcpy (output_message_buffer->value,
+ p + 24,
+ output_message_buffer->length);
+ return GSS_S_COMPLETE;
+}
+
+static OM_uint32
+unwrap_des3
+ (OM_uint32 * minor_status,
+ const gsskrb5_ctx context_handle,
+ krb5_context context,
+ const gss_buffer_t input_message_buffer,
+ gss_buffer_t output_message_buffer,
+ int * conf_state,
+ gss_qop_t * qop_state,
+ krb5_keyblock *key
+ )
+{
+ u_char *p;
+ size_t len;
+ u_char *seq;
+ krb5_data seq_data;
+ u_char cksum[20];
+ uint32_t seq_number;
+ size_t padlength;
+ OM_uint32 ret;
+ int cstate;
+ krb5_crypto crypto;
+ Checksum csum;
+ int cmp;
+ int token_len;
+
+ if (IS_DCE_STYLE(context_handle)) {
+ token_len = 34 + 8 + 15; /* 57 */
+ } else {
+ token_len = input_message_buffer->length;
+ }
+
+ p = input_message_buffer->value;
+ ret = _gsskrb5_verify_header (&p,
+ token_len,
+ "\x02\x01",
+ GSS_KRB5_MECHANISM);
+ if (ret)
+ return ret;
+
+ if (memcmp (p, "\x04\x00", 2) != 0) /* HMAC SHA1 DES3_KD */
+ return GSS_S_BAD_SIG;
+ p += 2;
+ if (memcmp (p, "\x02\x00", 2) == 0) {
+ cstate = 1;
+ } else if (memcmp (p, "\xff\xff", 2) == 0) {
+ cstate = 0;
+ } else
+ return GSS_S_BAD_MIC;
+ p += 2;
+ if(conf_state != NULL)
+ *conf_state = cstate;
+ if (memcmp (p, "\xff\xff", 2) != 0)
+ return GSS_S_DEFECTIVE_TOKEN;
+ p += 2;
+ p += 28;
+
+ len = p - (u_char *)input_message_buffer->value;
+
+ if(cstate) {
+ /* decrypt data */
+ krb5_data tmp;
+
+ ret = krb5_crypto_init(context, key,
+ ETYPE_DES3_CBC_NONE, &crypto);
+ if (ret) {
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+ ret = krb5_decrypt(context, crypto, KRB5_KU_USAGE_SEAL,
+ p, input_message_buffer->length - len, &tmp);
+ krb5_crypto_destroy(context, crypto);
+ if (ret) {
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+ assert (tmp.length == input_message_buffer->length - len);
+
+ memcpy (p, tmp.data, tmp.length);
+ krb5_data_free(&tmp);
+ }
+
+ if (IS_DCE_STYLE(context_handle)) {
+ padlength = 0;
+ } else {
+ /* check pad */
+ ret = _gssapi_verify_pad(input_message_buffer,
+ input_message_buffer->length - len,
+ &padlength);
+ if (ret)
+ return ret;
+ }
+
+ /* verify sequence number */
+
+ HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex);
+
+ p -= 28;
+
+ ret = krb5_crypto_init(context, key,
+ ETYPE_DES3_CBC_NONE, &crypto);
+ if (ret) {
+ *minor_status = ret;
+ HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
+ return GSS_S_FAILURE;
+ }
+ {
+ DES_cblock ivec;
+
+ memcpy(&ivec, p + 8, 8);
+ ret = krb5_decrypt_ivec (context,
+ crypto,
+ KRB5_KU_USAGE_SEQ,
+ p, 8, &seq_data,
+ &ivec);
+ }
+ krb5_crypto_destroy (context, crypto);
+ if (ret) {
+ *minor_status = ret;
+ HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
+ return GSS_S_FAILURE;
+ }
+ if (seq_data.length != 8) {
+ krb5_data_free (&seq_data);
+ *minor_status = 0;
+ HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
+ return GSS_S_BAD_MIC;
+ }
+
+ seq = seq_data.data;
+ _gsskrb5_decode_om_uint32(seq, &seq_number);
+
+ if (context_handle->more_flags & LOCAL)
+ cmp = memcmp(&seq[4], "\xff\xff\xff\xff", 4);
+ else
+ cmp = memcmp(&seq[4], "\x00\x00\x00\x00", 4);
+
+ krb5_data_free (&seq_data);
+ if (cmp != 0) {
+ *minor_status = 0;
+ HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
+ return GSS_S_BAD_MIC;
+ }
+
+ ret = _gssapi_msg_order_check(context_handle->order, seq_number);
+ if (ret) {
+ *minor_status = 0;
+ HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
+ return ret;
+ }
+
+ HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
+
+ /* verify checksum */
+
+ memcpy (cksum, p + 8, 20);
+
+ memcpy (p + 20, p - 8, 8);
+
+ csum.cksumtype = CKSUMTYPE_HMAC_SHA1_DES3;
+ csum.checksum.length = 20;
+ csum.checksum.data = cksum;
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret) {
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+
+ ret = krb5_verify_checksum (context, crypto,
+ KRB5_KU_USAGE_SIGN,
+ p + 20,
+ input_message_buffer->length - len + 8,
+ &csum);
+ krb5_crypto_destroy (context, crypto);
+ if (ret) {
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+
+ /* copy out data */
+
+ output_message_buffer->length = input_message_buffer->length
+ - len - padlength - 8;
+ output_message_buffer->value = malloc(output_message_buffer->length);
+ if(output_message_buffer->length != 0 && output_message_buffer->value == NULL)
+ return GSS_S_FAILURE;
+ memcpy (output_message_buffer->value,
+ p + 36,
+ output_message_buffer->length);
+ return GSS_S_COMPLETE;
+}
+
+OM_uint32 _gsskrb5_unwrap
+ (OM_uint32 * minor_status,
+ const gss_ctx_id_t context_handle,
+ const gss_buffer_t input_message_buffer,
+ gss_buffer_t output_message_buffer,
+ int * conf_state,
+ gss_qop_t * qop_state
+ )
+{
+ krb5_keyblock *key;
+ krb5_context context;
+ OM_uint32 ret;
+ krb5_keytype keytype;
+ gsskrb5_ctx ctx = (gsskrb5_ctx) context_handle;
+
+ output_message_buffer->value = NULL;
+ output_message_buffer->length = 0;
+
+ GSSAPI_KRB5_INIT (&context);
+
+ if (qop_state != NULL)
+ *qop_state = GSS_C_QOP_DEFAULT;
+ HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
+ ret = _gsskrb5i_get_token_key(ctx, context, &key);
+ HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
+ if (ret) {
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+ krb5_enctype_to_keytype (context, key->keytype, &keytype);
+
+ *minor_status = 0;
+
+ switch (keytype) {
+ case KEYTYPE_DES :
+ ret = unwrap_des (minor_status, ctx,
+ input_message_buffer, output_message_buffer,
+ conf_state, qop_state, key);
+ break;
+ case KEYTYPE_DES3 :
+ ret = unwrap_des3 (minor_status, ctx, context,
+ input_message_buffer, output_message_buffer,
+ conf_state, qop_state, key);
+ break;
+ case KEYTYPE_ARCFOUR:
+ case KEYTYPE_ARCFOUR_56:
+ ret = _gssapi_unwrap_arcfour (minor_status, ctx, context,
+ input_message_buffer, output_message_buffer,
+ conf_state, qop_state, key);
+ break;
+ default :
+ ret = _gssapi_unwrap_cfx (minor_status, ctx, context,
+ input_message_buffer, output_message_buffer,
+ conf_state, qop_state, key);
+ break;
+ }
+ krb5_free_keyblock (context, key);
+ return ret;
+}
diff --git a/source4/heimdal/lib/gssapi/krb5/verify_mic.c b/source4/heimdal/lib/gssapi/krb5/verify_mic.c
new file mode 100644
index 0000000000..1832d35b5a
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/krb5/verify_mic.c
@@ -0,0 +1,344 @@
+/*
+ * Copyright (c) 1997 - 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5/gsskrb5_locl.h"
+
+RCSID("$Id$");
+
+static OM_uint32
+verify_mic_des
+ (OM_uint32 * minor_status,
+ const gsskrb5_ctx context_handle,
+ krb5_context context,
+ const gss_buffer_t message_buffer,
+ const gss_buffer_t token_buffer,
+ gss_qop_t * qop_state,
+ krb5_keyblock *key,
+ char *type
+ )
+{
+ u_char *p;
+ MD5_CTX md5;
+ u_char hash[16], *seq;
+ DES_key_schedule schedule;
+ DES_cblock zero;
+ DES_cblock deskey;
+ uint32_t seq_number;
+ OM_uint32 ret;
+ int cmp;
+
+ p = token_buffer->value;
+ ret = _gsskrb5_verify_header (&p,
+ token_buffer->length,
+ type,
+ GSS_KRB5_MECHANISM);
+ if (ret)
+ return ret;
+
+ if (memcmp(p, "\x00\x00", 2) != 0)
+ return GSS_S_BAD_SIG;
+ p += 2;
+ if (memcmp (p, "\xff\xff\xff\xff", 4) != 0)
+ return GSS_S_BAD_MIC;
+ p += 4;
+ p += 16;
+
+ /* verify checksum */
+ MD5_Init (&md5);
+ MD5_Update (&md5, p - 24, 8);
+ MD5_Update (&md5, message_buffer->value,
+ message_buffer->length);
+ MD5_Final (hash, &md5);
+
+ memset (&zero, 0, sizeof(zero));
+ memcpy (&deskey, key->keyvalue.data, sizeof(deskey));
+
+ DES_set_key_unchecked (&deskey, &schedule);
+ DES_cbc_cksum ((void *)hash, (void *)hash, sizeof(hash),
+ &schedule, &zero);
+ if (memcmp (p - 8, hash, 8) != 0) {
+ memset (deskey, 0, sizeof(deskey));
+ memset (&schedule, 0, sizeof(schedule));
+ return GSS_S_BAD_MIC;
+ }
+
+ /* verify sequence number */
+
+ HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex);
+
+ p -= 16;
+ DES_set_key_unchecked (&deskey, &schedule);
+ DES_cbc_encrypt ((void *)p, (void *)p, 8,
+ &schedule, (DES_cblock *)hash, DES_DECRYPT);
+
+ memset (deskey, 0, sizeof(deskey));
+ memset (&schedule, 0, sizeof(schedule));
+
+ seq = p;
+ _gsskrb5_decode_om_uint32(seq, &seq_number);
+
+ if (context_handle->more_flags & LOCAL)
+ cmp = memcmp(&seq[4], "\xff\xff\xff\xff", 4);
+ else
+ cmp = memcmp(&seq[4], "\x00\x00\x00\x00", 4);
+
+ if (cmp != 0) {
+ HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
+ return GSS_S_BAD_MIC;
+ }
+
+ ret = _gssapi_msg_order_check(context_handle->order, seq_number);
+ if (ret) {
+ HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
+ return ret;
+ }
+
+ HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
+
+ return GSS_S_COMPLETE;
+}
+
+static OM_uint32
+verify_mic_des3
+ (OM_uint32 * minor_status,
+ const gsskrb5_ctx context_handle,
+ krb5_context context,
+ const gss_buffer_t message_buffer,
+ const gss_buffer_t token_buffer,
+ gss_qop_t * qop_state,
+ krb5_keyblock *key,
+ char *type
+ )
+{
+ u_char *p;
+ u_char *seq;
+ uint32_t seq_number;
+ OM_uint32 ret;
+ krb5_crypto crypto;
+ krb5_data seq_data;
+ int cmp, docompat;
+ Checksum csum;
+ char *tmp;
+ char ivec[8];
+
+ p = token_buffer->value;
+ ret = _gsskrb5_verify_header (&p,
+ token_buffer->length,
+ type,
+ GSS_KRB5_MECHANISM);
+ if (ret)
+ return ret;
+
+ if (memcmp(p, "\x04\x00", 2) != 0) /* SGN_ALG = HMAC SHA1 DES3-KD */
+ return GSS_S_BAD_SIG;
+ p += 2;
+ if (memcmp (p, "\xff\xff\xff\xff", 4) != 0)
+ return GSS_S_BAD_MIC;
+ p += 4;
+
+ ret = krb5_crypto_init(context, key,
+ ETYPE_DES3_CBC_NONE, &crypto);
+ if (ret){
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+
+ /* verify sequence number */
+ docompat = 0;
+retry:
+ if (docompat)
+ memset(ivec, 0, 8);
+ else
+ memcpy(ivec, p + 8, 8);
+
+ ret = krb5_decrypt_ivec (context,
+ crypto,
+ KRB5_KU_USAGE_SEQ,
+ p, 8, &seq_data, ivec);
+ if (ret) {
+ if (docompat++) {
+ krb5_crypto_destroy (context, crypto);
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ } else
+ goto retry;
+ }
+
+ if (seq_data.length != 8) {
+ krb5_data_free (&seq_data);
+ if (docompat++) {
+ krb5_crypto_destroy (context, crypto);
+ return GSS_S_BAD_MIC;
+ } else
+ goto retry;
+ }
+
+ HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex);
+
+ seq = seq_data.data;
+ _gsskrb5_decode_om_uint32(seq, &seq_number);
+
+ if (context_handle->more_flags & LOCAL)
+ cmp = memcmp(&seq[4], "\xff\xff\xff\xff", 4);
+ else
+ cmp = memcmp(&seq[4], "\x00\x00\x00\x00", 4);
+
+ krb5_data_free (&seq_data);
+ if (cmp != 0) {
+ krb5_crypto_destroy (context, crypto);
+ *minor_status = 0;
+ HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
+ return GSS_S_BAD_MIC;
+ }
+
+ ret = _gssapi_msg_order_check(context_handle->order, seq_number);
+ if (ret) {
+ krb5_crypto_destroy (context, crypto);
+ *minor_status = 0;
+ HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
+ return ret;
+ }
+
+ /* verify checksum */
+
+ tmp = malloc (message_buffer->length + 8);
+ if (tmp == NULL) {
+ krb5_crypto_destroy (context, crypto);
+ HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+
+ memcpy (tmp, p - 8, 8);
+ memcpy (tmp + 8, message_buffer->value, message_buffer->length);
+
+ csum.cksumtype = CKSUMTYPE_HMAC_SHA1_DES3;
+ csum.checksum.length = 20;
+ csum.checksum.data = p + 8;
+
+ ret = krb5_verify_checksum (context, crypto,
+ KRB5_KU_USAGE_SIGN,
+ tmp, message_buffer->length + 8,
+ &csum);
+ free (tmp);
+ if (ret) {
+ krb5_crypto_destroy (context, crypto);
+ *minor_status = ret;
+ HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
+ return GSS_S_BAD_MIC;
+ }
+ HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
+
+ krb5_crypto_destroy (context, crypto);
+ return GSS_S_COMPLETE;
+}
+
+OM_uint32
+_gsskrb5_verify_mic_internal
+ (OM_uint32 * minor_status,
+ const gsskrb5_ctx context_handle,
+ krb5_context context,
+ const gss_buffer_t message_buffer,
+ const gss_buffer_t token_buffer,
+ gss_qop_t * qop_state,
+ char * type
+ )
+{
+ krb5_keyblock *key;
+ OM_uint32 ret;
+ krb5_keytype keytype;
+
+ HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex);
+ ret = _gsskrb5i_get_token_key(context_handle, context, &key);
+ HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
+ if (ret) {
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+ *minor_status = 0;
+ krb5_enctype_to_keytype (context, key->keytype, &keytype);
+ switch (keytype) {
+ case KEYTYPE_DES :
+ ret = verify_mic_des (minor_status, context_handle, context,
+ message_buffer, token_buffer, qop_state, key,
+ type);
+ break;
+ case KEYTYPE_DES3 :
+ ret = verify_mic_des3 (minor_status, context_handle, context,
+ message_buffer, token_buffer, qop_state, key,
+ type);
+ break;
+ case KEYTYPE_ARCFOUR :
+ case KEYTYPE_ARCFOUR_56 :
+ ret = _gssapi_verify_mic_arcfour (minor_status, context_handle,
+ context,
+ message_buffer, token_buffer,
+ qop_state, key, type);
+ break;
+ default :
+ ret = _gssapi_verify_mic_cfx (minor_status, context_handle,
+ context,
+ message_buffer, token_buffer, qop_state,
+ key);
+ break;
+ }
+ krb5_free_keyblock (context, key);
+
+ return ret;
+}
+
+OM_uint32
+_gsskrb5_verify_mic
+ (OM_uint32 * minor_status,
+ const gss_ctx_id_t context_handle,
+ const gss_buffer_t message_buffer,
+ const gss_buffer_t token_buffer,
+ gss_qop_t * qop_state
+ )
+{
+ krb5_context context;
+ OM_uint32 ret;
+
+ GSSAPI_KRB5_INIT (&context);
+
+ if (qop_state != NULL)
+ *qop_state = GSS_C_QOP_DEFAULT;
+
+ ret = _gsskrb5_verify_mic_internal(minor_status,
+ (gsskrb5_ctx)context_handle,
+ context,
+ message_buffer, token_buffer,
+ qop_state, "\x01\x01");
+
+ return ret;
+}
diff --git a/source4/heimdal/lib/gssapi/krb5/wrap.c b/source4/heimdal/lib/gssapi/krb5/wrap.c
new file mode 100644
index 0000000000..ad21bcb57b
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/krb5/wrap.c
@@ -0,0 +1,569 @@
+/*
+ * Copyright (c) 1997 - 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5/gsskrb5_locl.h"
+
+RCSID("$Id$");
+
+/*
+ * Return initiator subkey, or if that doesn't exists, the subkey.
+ */
+
+krb5_error_code
+_gsskrb5i_get_initiator_subkey(const gsskrb5_ctx ctx,
+ krb5_context context,
+ krb5_keyblock **key)
+{
+ krb5_error_code ret;
+ *key = NULL;
+
+ if (ctx->more_flags & LOCAL) {
+ ret = krb5_auth_con_getlocalsubkey(context,
+ ctx->auth_context,
+ key);
+ } else {
+ ret = krb5_auth_con_getremotesubkey(context,
+ ctx->auth_context,
+ key);
+ }
+ if (ret == 0 && *key == NULL)
+ ret = krb5_auth_con_getkey(context,
+ ctx->auth_context,
+ key);
+ if (ret == 0 && *key == NULL) {
+ krb5_set_error_message(context, 0, "No initiator subkey available");
+ return GSS_KRB5_S_KG_NO_SUBKEY;
+ }
+ return ret;
+}
+
+krb5_error_code
+_gsskrb5i_get_acceptor_subkey(const gsskrb5_ctx ctx,
+ krb5_context context,
+ krb5_keyblock **key)
+{
+ krb5_error_code ret;
+ *key = NULL;
+
+ if (ctx->more_flags & LOCAL) {
+ ret = krb5_auth_con_getremotesubkey(context,
+ ctx->auth_context,
+ key);
+ } else {
+ ret = krb5_auth_con_getlocalsubkey(context,
+ ctx->auth_context,
+ key);
+ }
+ if (ret == 0 && *key == NULL) {
+ krb5_set_error_message(context, 0, "No acceptor subkey available");
+ return GSS_KRB5_S_KG_NO_SUBKEY;
+ }
+ return ret;
+}
+
+OM_uint32
+_gsskrb5i_get_token_key(const gsskrb5_ctx ctx,
+ krb5_context context,
+ krb5_keyblock **key)
+{
+ _gsskrb5i_get_acceptor_subkey(ctx, context, key);
+ if(*key == NULL) {
+ /*
+ * Only use the initiator subkey or ticket session key if an
+ * acceptor subkey was not required.
+ */
+ if ((ctx->more_flags & ACCEPTOR_SUBKEY) == 0)
+ _gsskrb5i_get_initiator_subkey(ctx, context, key);
+ }
+ if (*key == NULL) {
+ krb5_set_error_message(context, 0, "No token key available");
+ return GSS_KRB5_S_KG_NO_SUBKEY;
+ }
+ return 0;
+}
+
+static OM_uint32
+sub_wrap_size (
+ OM_uint32 req_output_size,
+ OM_uint32 * max_input_size,
+ int blocksize,
+ int extrasize
+ )
+{
+ size_t len, total_len;
+
+ len = 8 + req_output_size + blocksize + extrasize;
+
+ _gsskrb5_encap_length(len, &len, &total_len, GSS_KRB5_MECHANISM);
+
+ total_len -= req_output_size; /* token length */
+ if (total_len < req_output_size) {
+ *max_input_size = (req_output_size - total_len);
+ (*max_input_size) &= (~(OM_uint32)(blocksize - 1));
+ } else {
+ *max_input_size = 0;
+ }
+ return GSS_S_COMPLETE;
+}
+
+OM_uint32
+_gsskrb5_wrap_size_limit (
+ OM_uint32 * minor_status,
+ const gss_ctx_id_t context_handle,
+ int conf_req_flag,
+ gss_qop_t qop_req,
+ OM_uint32 req_output_size,
+ OM_uint32 * max_input_size
+ )
+{
+ krb5_context context;
+ krb5_keyblock *key;
+ OM_uint32 ret;
+ krb5_keytype keytype;
+ const gsskrb5_ctx ctx = (const gsskrb5_ctx) context_handle;
+
+ GSSAPI_KRB5_INIT (&context);
+
+ HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
+ ret = _gsskrb5i_get_token_key(ctx, context, &key);
+ HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
+ if (ret) {
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+ krb5_enctype_to_keytype (context, key->keytype, &keytype);
+
+ switch (keytype) {
+ case KEYTYPE_DES :
+ ret = sub_wrap_size(req_output_size, max_input_size, 8, 22);
+ break;
+ case KEYTYPE_ARCFOUR:
+ case KEYTYPE_ARCFOUR_56:
+ ret = _gssapi_wrap_size_arcfour(minor_status, ctx, context,
+ conf_req_flag, qop_req,
+ req_output_size, max_input_size, key);
+ break;
+ case KEYTYPE_DES3 :
+ ret = sub_wrap_size(req_output_size, max_input_size, 8, 34);
+ break;
+ default :
+ ret = _gssapi_wrap_size_cfx(minor_status, ctx, context,
+ conf_req_flag, qop_req,
+ req_output_size, max_input_size, key);
+ break;
+ }
+ krb5_free_keyblock (context, key);
+ *minor_status = 0;
+ return ret;
+}
+
+static OM_uint32
+wrap_des
+ (OM_uint32 * minor_status,
+ const gsskrb5_ctx ctx,
+ krb5_context context,
+ int conf_req_flag,
+ gss_qop_t qop_req,
+ const gss_buffer_t input_message_buffer,
+ int * conf_state,
+ gss_buffer_t output_message_buffer,
+ krb5_keyblock *key
+ )
+{
+ u_char *p;
+ MD5_CTX md5;
+ u_char hash[16];
+ DES_key_schedule schedule;
+ DES_cblock deskey;
+ DES_cblock zero;
+ int i;
+ int32_t seq_number;
+ size_t len, total_len, padlength, datalen;
+
+ if (IS_DCE_STYLE(ctx)) {
+ padlength = 0;
+ datalen = input_message_buffer->length;
+ len = 22 + 8;
+ _gsskrb5_encap_length (len, &len, &total_len, GSS_KRB5_MECHANISM);
+ total_len += datalen;
+ datalen += 8;
+ } else {
+ padlength = 8 - (input_message_buffer->length % 8);
+ datalen = input_message_buffer->length + padlength + 8;
+ len = datalen + 22;
+ _gsskrb5_encap_length (len, &len, &total_len, GSS_KRB5_MECHANISM);
+ }
+
+ output_message_buffer->length = total_len;
+ output_message_buffer->value = malloc (total_len);
+ if (output_message_buffer->value == NULL) {
+ output_message_buffer->length = 0;
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+
+ p = _gsskrb5_make_header(output_message_buffer->value,
+ len,
+ "\x02\x01", /* TOK_ID */
+ GSS_KRB5_MECHANISM);
+
+ /* SGN_ALG */
+ memcpy (p, "\x00\x00", 2);
+ p += 2;
+ /* SEAL_ALG */
+ if(conf_req_flag)
+ memcpy (p, "\x00\x00", 2);
+ else
+ memcpy (p, "\xff\xff", 2);
+ p += 2;
+ /* Filler */
+ memcpy (p, "\xff\xff", 2);
+ p += 2;
+
+ /* fill in later */
+ memset (p, 0, 16);
+ p += 16;
+
+ /* confounder + data + pad */
+ krb5_generate_random_block(p, 8);
+ memcpy (p + 8, input_message_buffer->value,
+ input_message_buffer->length);
+ memset (p + 8 + input_message_buffer->length, padlength, padlength);
+
+ /* checksum */
+ MD5_Init (&md5);
+ MD5_Update (&md5, p - 24, 8);
+ MD5_Update (&md5, p, datalen);
+ MD5_Final (hash, &md5);
+
+ memset (&zero, 0, sizeof(zero));
+ memcpy (&deskey, key->keyvalue.data, sizeof(deskey));
+ DES_set_key_unchecked (&deskey, &schedule);
+ DES_cbc_cksum ((void *)hash, (void *)hash, sizeof(hash),
+ &schedule, &zero);
+ memcpy (p - 8, hash, 8);
+
+ /* sequence number */
+ HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
+ krb5_auth_con_getlocalseqnumber (context,
+ ctx->auth_context,
+ &seq_number);
+
+ p -= 16;
+ p[0] = (seq_number >> 0) & 0xFF;
+ p[1] = (seq_number >> 8) & 0xFF;
+ p[2] = (seq_number >> 16) & 0xFF;
+ p[3] = (seq_number >> 24) & 0xFF;
+ memset (p + 4,
+ (ctx->more_flags & LOCAL) ? 0 : 0xFF,
+ 4);
+
+ DES_set_key_unchecked (&deskey, &schedule);
+ DES_cbc_encrypt ((void *)p, (void *)p, 8,
+ &schedule, (DES_cblock *)(p + 8), DES_ENCRYPT);
+
+ krb5_auth_con_setlocalseqnumber (context,
+ ctx->auth_context,
+ ++seq_number);
+ HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
+
+ /* encrypt the data */
+ p += 16;
+
+ if(conf_req_flag) {
+ memcpy (&deskey, key->keyvalue.data, sizeof(deskey));
+
+ for (i = 0; i < sizeof(deskey); ++i)
+ deskey[i] ^= 0xf0;
+ DES_set_key_unchecked (&deskey, &schedule);
+ memset (&zero, 0, sizeof(zero));
+ DES_cbc_encrypt ((void *)p,
+ (void *)p,
+ datalen,
+ &schedule,
+ &zero,
+ DES_ENCRYPT);
+ }
+ memset (deskey, 0, sizeof(deskey));
+ memset (&schedule, 0, sizeof(schedule));
+
+ if(conf_state != NULL)
+ *conf_state = conf_req_flag;
+ *minor_status = 0;
+ return GSS_S_COMPLETE;
+}
+
+static OM_uint32
+wrap_des3
+ (OM_uint32 * minor_status,
+ const gsskrb5_ctx ctx,
+ krb5_context context,
+ int conf_req_flag,
+ gss_qop_t qop_req,
+ const gss_buffer_t input_message_buffer,
+ int * conf_state,
+ gss_buffer_t output_message_buffer,
+ krb5_keyblock *key
+ )
+{
+ u_char *p;
+ u_char seq[8];
+ int32_t seq_number;
+ size_t len, total_len, padlength, datalen;
+ uint32_t ret;
+ krb5_crypto crypto;
+ Checksum cksum;
+ krb5_data encdata;
+
+ if (IS_DCE_STYLE(ctx)) {
+ padlength = 0;
+ datalen = input_message_buffer->length;
+ len = 34 + 8;
+ _gsskrb5_encap_length (len, &len, &total_len, GSS_KRB5_MECHANISM);
+ total_len += datalen;
+ datalen += 8;
+ } else {
+ padlength = 8 - (input_message_buffer->length % 8);
+ datalen = input_message_buffer->length + padlength + 8;
+ len = datalen + 34;
+ _gsskrb5_encap_length (len, &len, &total_len, GSS_KRB5_MECHANISM);
+ }
+
+ output_message_buffer->length = total_len;
+ output_message_buffer->value = malloc (total_len);
+ if (output_message_buffer->value == NULL) {
+ output_message_buffer->length = 0;
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+
+ p = _gsskrb5_make_header(output_message_buffer->value,
+ len,
+ "\x02\x01", /* TOK_ID */
+ GSS_KRB5_MECHANISM);
+
+ /* SGN_ALG */
+ memcpy (p, "\x04\x00", 2); /* HMAC SHA1 DES3-KD */
+ p += 2;
+ /* SEAL_ALG */
+ if(conf_req_flag)
+ memcpy (p, "\x02\x00", 2); /* DES3-KD */
+ else
+ memcpy (p, "\xff\xff", 2);
+ p += 2;
+ /* Filler */
+ memcpy (p, "\xff\xff", 2);
+ p += 2;
+
+ /* calculate checksum (the above + confounder + data + pad) */
+
+ memcpy (p + 20, p - 8, 8);
+ krb5_generate_random_block(p + 28, 8);
+ memcpy (p + 28 + 8, input_message_buffer->value,
+ input_message_buffer->length);
+ memset (p + 28 + 8 + input_message_buffer->length, padlength, padlength);
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret) {
+ free (output_message_buffer->value);
+ output_message_buffer->length = 0;
+ output_message_buffer->value = NULL;
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+
+ ret = krb5_create_checksum (context,
+ crypto,
+ KRB5_KU_USAGE_SIGN,
+ 0,
+ p + 20,
+ datalen + 8,
+ &cksum);
+ krb5_crypto_destroy (context, crypto);
+ if (ret) {
+ free (output_message_buffer->value);
+ output_message_buffer->length = 0;
+ output_message_buffer->value = NULL;
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+
+ /* zero out SND_SEQ + SGN_CKSUM in case */
+ memset (p, 0, 28);
+
+ memcpy (p + 8, cksum.checksum.data, cksum.checksum.length);
+ free_Checksum (&cksum);
+
+ HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
+ /* sequence number */
+ krb5_auth_con_getlocalseqnumber (context,
+ ctx->auth_context,
+ &seq_number);
+
+ seq[0] = (seq_number >> 0) & 0xFF;
+ seq[1] = (seq_number >> 8) & 0xFF;
+ seq[2] = (seq_number >> 16) & 0xFF;
+ seq[3] = (seq_number >> 24) & 0xFF;
+ memset (seq + 4,
+ (ctx->more_flags & LOCAL) ? 0 : 0xFF,
+ 4);
+
+
+ ret = krb5_crypto_init(context, key, ETYPE_DES3_CBC_NONE,
+ &crypto);
+ if (ret) {
+ free (output_message_buffer->value);
+ output_message_buffer->length = 0;
+ output_message_buffer->value = NULL;
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+
+ {
+ DES_cblock ivec;
+
+ memcpy (&ivec, p + 8, 8);
+ ret = krb5_encrypt_ivec (context,
+ crypto,
+ KRB5_KU_USAGE_SEQ,
+ seq, 8, &encdata,
+ &ivec);
+ }
+ krb5_crypto_destroy (context, crypto);
+ if (ret) {
+ free (output_message_buffer->value);
+ output_message_buffer->length = 0;
+ output_message_buffer->value = NULL;
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+
+ assert (encdata.length == 8);
+
+ memcpy (p, encdata.data, encdata.length);
+ krb5_data_free (&encdata);
+
+ krb5_auth_con_setlocalseqnumber (context,
+ ctx->auth_context,
+ ++seq_number);
+ HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
+
+ /* encrypt the data */
+ p += 28;
+
+ if(conf_req_flag) {
+ krb5_data tmp;
+
+ ret = krb5_crypto_init(context, key,
+ ETYPE_DES3_CBC_NONE, &crypto);
+ if (ret) {
+ free (output_message_buffer->value);
+ output_message_buffer->length = 0;
+ output_message_buffer->value = NULL;
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+ ret = krb5_encrypt(context, crypto, KRB5_KU_USAGE_SEAL,
+ p, datalen, &tmp);
+ krb5_crypto_destroy(context, crypto);
+ if (ret) {
+ free (output_message_buffer->value);
+ output_message_buffer->length = 0;
+ output_message_buffer->value = NULL;
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+ assert (tmp.length == datalen);
+
+ memcpy (p, tmp.data, datalen);
+ krb5_data_free(&tmp);
+ }
+ if(conf_state != NULL)
+ *conf_state = conf_req_flag;
+ *minor_status = 0;
+ return GSS_S_COMPLETE;
+}
+
+OM_uint32 _gsskrb5_wrap
+ (OM_uint32 * minor_status,
+ const gss_ctx_id_t context_handle,
+ int conf_req_flag,
+ gss_qop_t qop_req,
+ const gss_buffer_t input_message_buffer,
+ int * conf_state,
+ gss_buffer_t output_message_buffer
+ )
+{
+ krb5_context context;
+ krb5_keyblock *key;
+ OM_uint32 ret;
+ krb5_keytype keytype;
+ const gsskrb5_ctx ctx = (const gsskrb5_ctx) context_handle;
+
+ GSSAPI_KRB5_INIT (&context);
+
+ HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
+ ret = _gsskrb5i_get_token_key(ctx, context, &key);
+ HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
+ if (ret) {
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+ krb5_enctype_to_keytype (context, key->keytype, &keytype);
+
+ switch (keytype) {
+ case KEYTYPE_DES :
+ ret = wrap_des (minor_status, ctx, context, conf_req_flag,
+ qop_req, input_message_buffer, conf_state,
+ output_message_buffer, key);
+ break;
+ case KEYTYPE_DES3 :
+ ret = wrap_des3 (minor_status, ctx, context, conf_req_flag,
+ qop_req, input_message_buffer, conf_state,
+ output_message_buffer, key);
+ break;
+ case KEYTYPE_ARCFOUR:
+ case KEYTYPE_ARCFOUR_56:
+ ret = _gssapi_wrap_arcfour (minor_status, ctx, context, conf_req_flag,
+ qop_req, input_message_buffer, conf_state,
+ output_message_buffer, key);
+ break;
+ default :
+ ret = _gssapi_wrap_cfx (minor_status, ctx, context, conf_req_flag,
+ qop_req, input_message_buffer, conf_state,
+ output_message_buffer, key);
+ break;
+ }
+ krb5_free_keyblock (context, key);
+ return ret;
+}
diff --git a/source4/heimdal/lib/gssapi/mech/context.c b/source4/heimdal/lib/gssapi/mech/context.c
new file mode 100644
index 0000000000..a06a1e9e37
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/context.c
@@ -0,0 +1,163 @@
+#include "mech/mech_locl.h"
+#include "heim_threads.h"
+
+RCSID("$Id$");
+
+struct mg_thread_ctx {
+ gss_OID mech;
+ OM_uint32 maj_stat;
+ OM_uint32 min_stat;
+ gss_buffer_desc maj_error;
+ gss_buffer_desc min_error;
+};
+
+static HEIMDAL_MUTEX context_mutex = HEIMDAL_MUTEX_INITIALIZER;
+static int created_key;
+static HEIMDAL_thread_key context_key;
+
+
+static void
+destroy_context(void *ptr)
+{
+ struct mg_thread_ctx *mg = ptr;
+ OM_uint32 junk;
+
+ if (mg == NULL)
+ return;
+
+ gss_release_buffer(&junk, &mg->maj_error);
+ gss_release_buffer(&junk, &mg->min_error);
+ free(mg);
+}
+
+
+static struct mg_thread_ctx *
+_gss_mechglue_thread(void)
+{
+ struct mg_thread_ctx *ctx;
+ int ret = 0;
+
+ HEIMDAL_MUTEX_lock(&context_mutex);
+
+ if (!created_key) {
+ HEIMDAL_key_create(&context_key, destroy_context, ret);
+ if (ret) {
+ HEIMDAL_MUTEX_unlock(&context_mutex);
+ return NULL;
+ }
+ created_key = 1;
+ }
+ HEIMDAL_MUTEX_unlock(&context_mutex);
+
+ ctx = HEIMDAL_getspecific(context_key);
+ if (ctx == NULL) {
+
+ ctx = calloc(1, sizeof(*ctx));
+ if (ctx == NULL)
+ return NULL;
+ HEIMDAL_setspecific(context_key, ctx, ret);
+ if (ret) {
+ free(ctx);
+ return NULL;
+ }
+ }
+ return ctx;
+}
+
+OM_uint32
+_gss_mg_get_error(const gss_OID mech, OM_uint32 type,
+ OM_uint32 value, gss_buffer_t string)
+{
+ struct mg_thread_ctx *mg;
+
+ mg = _gss_mechglue_thread();
+ if (mg == NULL)
+ return GSS_S_BAD_STATUS;
+
+#if 0
+ /*
+ * We cant check the mech here since a pseudo-mech might have
+ * called an lower layer and then the mech info is all broken
+ */
+ if (mech != NULL && gss_oid_equal(mg->mech, mech) == 0)
+ return GSS_S_BAD_STATUS;
+#endif
+
+ switch (type) {
+ case GSS_C_GSS_CODE: {
+ if (value != mg->maj_stat || mg->maj_error.length == 0)
+ break;
+ string->value = malloc(mg->maj_error.length);
+ string->length = mg->maj_error.length;
+ memcpy(string->value, mg->maj_error.value, mg->maj_error.length);
+ return GSS_S_COMPLETE;
+ }
+ case GSS_C_MECH_CODE: {
+ if (value != mg->min_stat || mg->min_error.length == 0)
+ break;
+ string->value = malloc(mg->min_error.length);
+ string->length = mg->min_error.length;
+ memcpy(string->value, mg->min_error.value, mg->min_error.length);
+ return GSS_S_COMPLETE;
+ }
+ }
+ string->value = NULL;
+ string->length = 0;
+ return GSS_S_BAD_STATUS;
+}
+
+void
+_gss_mg_error(gssapi_mech_interface m, OM_uint32 maj, OM_uint32 min)
+{
+ OM_uint32 major_status, minor_status;
+ OM_uint32 message_content;
+ struct mg_thread_ctx *mg;
+
+ /*
+ * Mechs without gss_display_status() does
+ * gss_mg_collect_error() by themself.
+ */
+ if (m->gm_display_status == NULL)
+ return ;
+
+ mg = _gss_mechglue_thread();
+ if (mg == NULL)
+ return;
+
+ gss_release_buffer(&minor_status, &mg->maj_error);
+ gss_release_buffer(&minor_status, &mg->min_error);
+
+ mg->mech = &m->gm_mech_oid;
+ mg->maj_stat = maj;
+ mg->min_stat = min;
+
+ major_status = m->gm_display_status(&minor_status,
+ maj,
+ GSS_C_GSS_CODE,
+ &m->gm_mech_oid,
+ &message_content,
+ &mg->maj_error);
+ if (GSS_ERROR(major_status)) {
+ mg->maj_error.value = NULL;
+ mg->maj_error.length = 0;
+ }
+ major_status = m->gm_display_status(&minor_status,
+ min,
+ GSS_C_MECH_CODE,
+ &m->gm_mech_oid,
+ &message_content,
+ &mg->min_error);
+ if (GSS_ERROR(major_status)) {
+ mg->min_error.value = NULL;
+ mg->min_error.length = 0;
+ }
+}
+
+void
+gss_mg_collect_error(gss_OID mech, OM_uint32 maj, OM_uint32 min)
+{
+ gssapi_mech_interface m = __gss_get_mechanism(mech);
+ if (m == NULL)
+ return;
+ _gss_mg_error(m, maj, min);
+}
diff --git a/source4/heimdal/lib/gssapi/mech/context.h b/source4/heimdal/lib/gssapi/mech/context.h
new file mode 100644
index 0000000000..f2a7009cda
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/context.h
@@ -0,0 +1,41 @@
+/*-
+ * Copyright (c) 2005 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libgssapi/context.h,v 1.1 2005/12/29 14:40:20 dfr Exp $
+ * $Id$
+ */
+
+#include <gssapi_mech.h>
+
+struct _gss_context {
+ gssapi_mech_interface gc_mech;
+ gss_ctx_id_t gc_ctx;
+};
+
+void
+_gss_mg_error(gssapi_mech_interface, OM_uint32, OM_uint32);
+
+OM_uint32
+_gss_mg_get_error(const gss_OID, OM_uint32, OM_uint32, gss_buffer_t);
diff --git a/source4/heimdal/lib/gssapi/mech/cred.h b/source4/heimdal/lib/gssapi/mech/cred.h
new file mode 100644
index 0000000000..01bd882dda
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/cred.h
@@ -0,0 +1,41 @@
+/*-
+ * Copyright (c) 2005 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libgssapi/cred.h,v 1.1 2005/12/29 14:40:20 dfr Exp $
+ * $Id$
+ */
+
+struct _gss_mechanism_cred {
+ SLIST_ENTRY(_gss_mechanism_cred) gmc_link;
+ gssapi_mech_interface gmc_mech; /* mechanism ops for MC */
+ gss_OID gmc_mech_oid; /* mechanism oid for MC */
+ gss_cred_id_t gmc_cred; /* underlying MC */
+};
+SLIST_HEAD(_gss_mechanism_cred_list, _gss_mechanism_cred);
+
+struct _gss_cred {
+ struct _gss_mechanism_cred_list gc_mc;
+};
+
diff --git a/source4/heimdal/lib/gssapi/mech/gss_accept_sec_context.c b/source4/heimdal/lib/gssapi/mech/gss_accept_sec_context.c
new file mode 100644
index 0000000000..50011a9b0d
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gss_accept_sec_context.c
@@ -0,0 +1,294 @@
+/*-
+ * Copyright (c) 2005 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libgssapi/gss_accept_sec_context.c,v 1.1 2005/12/29 14:40:20 dfr Exp $
+ */
+
+#include "mech_locl.h"
+RCSID("$Id$");
+
+static OM_uint32
+parse_header(const gss_buffer_t input_token, gss_OID mech_oid)
+{
+ unsigned char *p = input_token->value;
+ size_t len = input_token->length;
+ size_t a, b;
+
+ /*
+ * Token must start with [APPLICATION 0] SEQUENCE.
+ * But if it doesn't assume it is DCE-STYLE Kerberos!
+ */
+ if (len == 0)
+ return (GSS_S_DEFECTIVE_TOKEN);
+
+ p++;
+ len--;
+
+ /*
+ * Decode the length and make sure it agrees with the
+ * token length.
+ */
+ if (len == 0)
+ return (GSS_S_DEFECTIVE_TOKEN);
+ if ((*p & 0x80) == 0) {
+ a = *p;
+ p++;
+ len--;
+ } else {
+ b = *p & 0x7f;
+ p++;
+ len--;
+ if (len < b)
+ return (GSS_S_DEFECTIVE_TOKEN);
+ a = 0;
+ while (b) {
+ a = (a << 8) | *p;
+ p++;
+ len--;
+ b--;
+ }
+ }
+ if (a != len)
+ return (GSS_S_DEFECTIVE_TOKEN);
+
+ /*
+ * Decode the OID for the mechanism. Simplify life by
+ * assuming that the OID length is less than 128 bytes.
+ */
+ if (len < 2 || *p != 0x06)
+ return (GSS_S_DEFECTIVE_TOKEN);
+ if ((p[1] & 0x80) || p[1] > (len - 2))
+ return (GSS_S_DEFECTIVE_TOKEN);
+ mech_oid->length = p[1];
+ p += 2;
+ len -= 2;
+ mech_oid->elements = p;
+
+ return GSS_S_COMPLETE;
+}
+
+static gss_OID_desc krb5_mechanism =
+ {9, rk_UNCONST("\x2a\x86\x48\x86\xf7\x12\x01\x02\x02")};
+static gss_OID_desc ntlm_mechanism =
+ {10, rk_UNCONST("\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a")};
+static gss_OID_desc spnego_mechanism =
+ {6, rk_UNCONST("\x2b\x06\x01\x05\x05\x02")};
+
+static OM_uint32
+choose_mech(const gss_buffer_t input, gss_OID mech_oid)
+{
+ OM_uint32 status;
+
+ /*
+ * First try to parse the gssapi token header and see if it's a
+ * correct header, use that in the first hand.
+ */
+
+ status = parse_header(input, mech_oid);
+ if (status == GSS_S_COMPLETE)
+ return GSS_S_COMPLETE;
+
+ /*
+ * Lets guess what mech is really is, callback function to mech ??
+ */
+
+ if (input->length > 8 &&
+ memcmp((const char *)input->value, "NTLMSSP\x00", 8) == 0)
+ {
+ *mech_oid = ntlm_mechanism;
+ return GSS_S_COMPLETE;
+ } else if (input->length != 0 &&
+ ((const char *)input->value)[0] == 0x6E)
+ {
+ /* Could be a raw AP-REQ (check for APPLICATION tag) */
+ *mech_oid = krb5_mechanism;
+ return GSS_S_COMPLETE;
+ } else if (input->length == 0) {
+ /*
+ * There is the a wierd mode of SPNEGO (in CIFS and
+ * SASL GSS-SPENGO where the first token is zero
+ * length and the acceptor returns a mech_list, lets
+ * hope that is what is happening now.
+ *
+ * http://msdn.microsoft.com/en-us/library/cc213114.aspx
+ * "NegTokenInit2 Variation for Server-Initiation"
+ */
+ *mech_oid = spnego_mechanism;
+ return GSS_S_COMPLETE;
+ }
+ return status;
+}
+
+
+OM_uint32 gss_accept_sec_context(OM_uint32 *minor_status,
+ gss_ctx_id_t *context_handle,
+ const gss_cred_id_t acceptor_cred_handle,
+ const gss_buffer_t input_token,
+ const gss_channel_bindings_t input_chan_bindings,
+ gss_name_t *src_name,
+ gss_OID *mech_type,
+ gss_buffer_t output_token,
+ OM_uint32 *ret_flags,
+ OM_uint32 *time_rec,
+ gss_cred_id_t *delegated_cred_handle)
+{
+ OM_uint32 major_status, mech_ret_flags, junk;
+ gssapi_mech_interface m;
+ struct _gss_context *ctx = (struct _gss_context *) *context_handle;
+ struct _gss_cred *cred = (struct _gss_cred *) acceptor_cred_handle;
+ struct _gss_mechanism_cred *mc;
+ gss_cred_id_t acceptor_mc, delegated_mc;
+ gss_name_t src_mn;
+
+ *minor_status = 0;
+ if (src_name)
+ *src_name = GSS_C_NO_NAME;
+ if (mech_type)
+ *mech_type = GSS_C_NO_OID;
+ if (ret_flags)
+ *ret_flags = 0;
+ if (time_rec)
+ *time_rec = 0;
+ if (delegated_cred_handle)
+ *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
+ _mg_buffer_zero(output_token);
+
+
+ /*
+ * If this is the first call (*context_handle is NULL), we must
+ * parse the input token to figure out the mechanism to use.
+ */
+ if (*context_handle == GSS_C_NO_CONTEXT) {
+ gss_OID_desc mech_oid;
+
+ major_status = choose_mech(input_token, &mech_oid);
+ if (major_status != GSS_S_COMPLETE)
+ return major_status;
+
+ /*
+ * Now that we have a mechanism, we can find the
+ * implementation.
+ */
+ ctx = malloc(sizeof(struct _gss_context));
+ if (!ctx) {
+ *minor_status = ENOMEM;
+ return (GSS_S_DEFECTIVE_TOKEN);
+ }
+ memset(ctx, 0, sizeof(struct _gss_context));
+ m = ctx->gc_mech = __gss_get_mechanism(&mech_oid);
+ if (!m) {
+ free(ctx);
+ return (GSS_S_BAD_MECH);
+ }
+ *context_handle = (gss_ctx_id_t) ctx;
+ } else {
+ m = ctx->gc_mech;
+ }
+
+ if (cred) {
+ SLIST_FOREACH(mc, &cred->gc_mc, gmc_link)
+ if (mc->gmc_mech == m)
+ break;
+ if (!mc) {
+ gss_delete_sec_context(&junk, context_handle, NULL);
+ return (GSS_S_BAD_MECH);
+ }
+ acceptor_mc = mc->gmc_cred;
+ } else {
+ acceptor_mc = GSS_C_NO_CREDENTIAL;
+ }
+ delegated_mc = GSS_C_NO_CREDENTIAL;
+
+ mech_ret_flags = 0;
+ major_status = m->gm_accept_sec_context(minor_status,
+ &ctx->gc_ctx,
+ acceptor_mc,
+ input_token,
+ input_chan_bindings,
+ &src_mn,
+ mech_type,
+ output_token,
+ &mech_ret_flags,
+ time_rec,
+ &delegated_mc);
+ if (major_status != GSS_S_COMPLETE &&
+ major_status != GSS_S_CONTINUE_NEEDED)
+ {
+ _gss_mg_error(m, major_status, *minor_status);
+ gss_delete_sec_context(&junk, context_handle, NULL);
+ return (major_status);
+ }
+
+ if (src_name && src_mn) {
+ /*
+ * Make a new name and mark it as an MN.
+ */
+ struct _gss_name *name = _gss_make_name(m, src_mn);
+
+ if (!name) {
+ m->gm_release_name(minor_status, &src_mn);
+ gss_delete_sec_context(&junk, context_handle, NULL);
+ return (GSS_S_FAILURE);
+ }
+ *src_name = (gss_name_t) name;
+ } else if (src_mn) {
+ m->gm_release_name(minor_status, &src_mn);
+ }
+
+ if (mech_ret_flags & GSS_C_DELEG_FLAG) {
+ if (!delegated_cred_handle) {
+ m->gm_release_cred(minor_status, &delegated_mc);
+ *ret_flags &= ~GSS_C_DELEG_FLAG;
+ } else if (delegated_mc) {
+ struct _gss_cred *dcred;
+ struct _gss_mechanism_cred *dmc;
+
+ dcred = malloc(sizeof(struct _gss_cred));
+ if (!dcred) {
+ *minor_status = ENOMEM;
+ gss_delete_sec_context(&junk, context_handle, NULL);
+ return (GSS_S_FAILURE);
+ }
+ SLIST_INIT(&dcred->gc_mc);
+ dmc = malloc(sizeof(struct _gss_mechanism_cred));
+ if (!dmc) {
+ free(dcred);
+ *minor_status = ENOMEM;
+ gss_delete_sec_context(&junk, context_handle, NULL);
+ return (GSS_S_FAILURE);
+ }
+ dmc->gmc_mech = m;
+ dmc->gmc_mech_oid = &m->gm_mech_oid;
+ dmc->gmc_cred = delegated_mc;
+ SLIST_INSERT_HEAD(&dcred->gc_mc, dmc, gmc_link);
+
+ *delegated_cred_handle = (gss_cred_id_t) dcred;
+ }
+ }
+
+ if (ret_flags)
+ *ret_flags = mech_ret_flags;
+ return (major_status);
+}
diff --git a/source4/heimdal/lib/gssapi/mech/gss_acquire_cred.c b/source4/heimdal/lib/gssapi/mech/gss_acquire_cred.c
new file mode 100644
index 0000000000..b21b3f62e8
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gss_acquire_cred.c
@@ -0,0 +1,169 @@
+/*-
+ * Copyright (c) 2005 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libgssapi/gss_acquire_cred.c,v 1.1 2005/12/29 14:40:20 dfr Exp $
+ */
+
+#include "mech_locl.h"
+RCSID("$Id$");
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_acquire_cred(OM_uint32 *minor_status,
+ const gss_name_t desired_name,
+ OM_uint32 time_req,
+ const gss_OID_set desired_mechs,
+ gss_cred_usage_t cred_usage,
+ gss_cred_id_t *output_cred_handle,
+ gss_OID_set *actual_mechs,
+ OM_uint32 *time_rec)
+{
+ OM_uint32 major_status;
+ gss_OID_set mechs = desired_mechs;
+ gss_OID_set_desc set;
+ struct _gss_name *name = (struct _gss_name *) desired_name;
+ gssapi_mech_interface m;
+ struct _gss_cred *cred;
+ struct _gss_mechanism_cred *mc;
+ OM_uint32 min_time, cred_time;
+ int i;
+
+ *minor_status = 0;
+ if (output_cred_handle)
+ *output_cred_handle = GSS_C_NO_CREDENTIAL;
+ if (actual_mechs)
+ *actual_mechs = GSS_C_NO_OID_SET;
+ if (time_rec)
+ *time_rec = 0;
+
+ _gss_load_mech();
+
+ /*
+ * First make sure that at least one of the requested
+ * mechanisms is one that we support.
+ */
+ if (mechs) {
+ for (i = 0; i < mechs->count; i++) {
+ int t;
+ gss_test_oid_set_member(minor_status,
+ &mechs->elements[i], _gss_mech_oids, &t);
+ if (t)
+ break;
+ }
+ if (i == mechs->count) {
+ *minor_status = 0;
+ return (GSS_S_BAD_MECH);
+ }
+ }
+
+ if (actual_mechs) {
+ major_status = gss_create_empty_oid_set(minor_status,
+ actual_mechs);
+ if (major_status)
+ return (major_status);
+ }
+
+ cred = malloc(sizeof(struct _gss_cred));
+ if (!cred) {
+ if (actual_mechs)
+ gss_release_oid_set(minor_status, actual_mechs);
+ *minor_status = ENOMEM;
+ return (GSS_S_FAILURE);
+ }
+ SLIST_INIT(&cred->gc_mc);
+
+ if (mechs == GSS_C_NO_OID_SET)
+ mechs = _gss_mech_oids;
+
+ set.count = 1;
+ min_time = GSS_C_INDEFINITE;
+ for (i = 0; i < mechs->count; i++) {
+ struct _gss_mechanism_name *mn = NULL;
+
+ m = __gss_get_mechanism(&mechs->elements[i]);
+ if (!m)
+ continue;
+
+ if (desired_name != GSS_C_NO_NAME) {
+ major_status = _gss_find_mn(minor_status, name,
+ &mechs->elements[i], &mn);
+ if (major_status != GSS_S_COMPLETE)
+ continue;
+ }
+
+ mc = malloc(sizeof(struct _gss_mechanism_cred));
+ if (!mc) {
+ continue;
+ }
+ mc->gmc_mech = m;
+ mc->gmc_mech_oid = &m->gm_mech_oid;
+
+ /*
+ * XXX Probably need to do something with actual_mechs.
+ */
+ set.elements = &mechs->elements[i];
+ major_status = m->gm_acquire_cred(minor_status,
+ (desired_name != GSS_C_NO_NAME
+ ? mn->gmn_name : GSS_C_NO_NAME),
+ time_req, &set, cred_usage,
+ &mc->gmc_cred, NULL, &cred_time);
+ if (major_status) {
+ free(mc);
+ continue;
+ }
+ if (cred_time < min_time)
+ min_time = cred_time;
+
+ if (actual_mechs) {
+ major_status = gss_add_oid_set_member(minor_status,
+ mc->gmc_mech_oid, actual_mechs);
+ if (major_status) {
+ m->gm_release_cred(minor_status,
+ &mc->gmc_cred);
+ free(mc);
+ continue;
+ }
+ }
+
+ SLIST_INSERT_HEAD(&cred->gc_mc, mc, gmc_link);
+ }
+
+ /*
+ * If we didn't manage to create a single credential, return
+ * an error.
+ */
+ if (!SLIST_FIRST(&cred->gc_mc)) {
+ free(cred);
+ if (actual_mechs)
+ gss_release_oid_set(minor_status, actual_mechs);
+ *minor_status = 0;
+ return (GSS_S_NO_CRED);
+ }
+
+ if (time_rec)
+ *time_rec = min_time;
+ *output_cred_handle = (gss_cred_id_t) cred;
+ *minor_status = 0;
+ return (GSS_S_COMPLETE);
+}
diff --git a/source4/heimdal/lib/gssapi/mech/gss_add_cred.c b/source4/heimdal/lib/gssapi/mech/gss_add_cred.c
new file mode 100644
index 0000000000..d190852884
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gss_add_cred.c
@@ -0,0 +1,187 @@
+/*-
+ * Copyright (c) 2005 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libgssapi/gss_add_cred.c,v 1.1 2005/12/29 14:40:20 dfr Exp $
+ */
+
+#include "mech_locl.h"
+RCSID("$Id$");
+
+static struct _gss_mechanism_cred *
+_gss_copy_cred(struct _gss_mechanism_cred *mc)
+{
+ struct _gss_mechanism_cred *new_mc;
+ gssapi_mech_interface m = mc->gmc_mech;
+ OM_uint32 major_status, minor_status;
+ gss_name_t name;
+ gss_cred_id_t cred;
+ OM_uint32 initiator_lifetime, acceptor_lifetime;
+ gss_cred_usage_t cred_usage;
+
+ major_status = m->gm_inquire_cred_by_mech(&minor_status,
+ mc->gmc_cred, mc->gmc_mech_oid,
+ &name, &initiator_lifetime, &acceptor_lifetime, &cred_usage);
+ if (major_status) {
+ _gss_mg_error(m, major_status, minor_status);
+ return (0);
+ }
+
+ major_status = m->gm_add_cred(&minor_status,
+ GSS_C_NO_CREDENTIAL, name, mc->gmc_mech_oid,
+ cred_usage, initiator_lifetime, acceptor_lifetime,
+ &cred, 0, 0, 0);
+ m->gm_release_name(&minor_status, &name);
+
+ if (major_status) {
+ _gss_mg_error(m, major_status, minor_status);
+ return (0);
+ }
+
+ new_mc = malloc(sizeof(struct _gss_mechanism_cred));
+ if (!new_mc) {
+ m->gm_release_cred(&minor_status, &cred);
+ return (0);
+ }
+ new_mc->gmc_mech = m;
+ new_mc->gmc_mech_oid = &m->gm_mech_oid;
+ new_mc->gmc_cred = cred;
+
+ return (new_mc);
+}
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_add_cred(OM_uint32 *minor_status,
+ const gss_cred_id_t input_cred_handle,
+ const gss_name_t desired_name,
+ const gss_OID desired_mech,
+ gss_cred_usage_t cred_usage,
+ OM_uint32 initiator_time_req,
+ OM_uint32 acceptor_time_req,
+ gss_cred_id_t *output_cred_handle,
+ gss_OID_set *actual_mechs,
+ OM_uint32 *initiator_time_rec,
+ OM_uint32 *acceptor_time_rec)
+{
+ OM_uint32 major_status;
+ gssapi_mech_interface m;
+ struct _gss_cred *cred = (struct _gss_cred *) input_cred_handle;
+ struct _gss_cred *new_cred;
+ gss_cred_id_t release_cred;
+ struct _gss_mechanism_cred *mc, *target_mc, *copy_mc;
+ struct _gss_mechanism_name *mn;
+ OM_uint32 junk;
+
+ *minor_status = 0;
+ *output_cred_handle = GSS_C_NO_CREDENTIAL;
+ if (initiator_time_rec)
+ *initiator_time_rec = 0;
+ if (acceptor_time_rec)
+ *acceptor_time_rec = 0;
+ if (actual_mechs)
+ *actual_mechs = GSS_C_NO_OID_SET;
+
+ new_cred = malloc(sizeof(struct _gss_cred));
+ if (!new_cred) {
+ *minor_status = ENOMEM;
+ return (GSS_S_FAILURE);
+ }
+ SLIST_INIT(&new_cred->gc_mc);
+
+ /*
+ * We go through all the mc attached to the input_cred_handle
+ * and check the mechanism. If it matches, we call
+ * gss_add_cred for that mechanism, otherwise we copy the mc
+ * to new_cred.
+ */
+ target_mc = 0;
+ if (cred) {
+ SLIST_FOREACH(mc, &cred->gc_mc, gmc_link) {
+ if (gss_oid_equal(mc->gmc_mech_oid, desired_mech)) {
+ target_mc = mc;
+ }
+ copy_mc = _gss_copy_cred(mc);
+ if (!copy_mc) {
+ release_cred = (gss_cred_id_t)new_cred;
+ gss_release_cred(&junk, &release_cred);
+ *minor_status = ENOMEM;
+ return (GSS_S_FAILURE);
+ }
+ SLIST_INSERT_HEAD(&new_cred->gc_mc, copy_mc, gmc_link);
+ }
+ }
+
+ /*
+ * Figure out a suitable mn, if any.
+ */
+ if (desired_name) {
+ major_status = _gss_find_mn(minor_status,
+ (struct _gss_name *) desired_name,
+ desired_mech,
+ &mn);
+ if (major_status != GSS_S_COMPLETE) {
+ free(new_cred);
+ return major_status;
+ }
+ } else {
+ mn = 0;
+ }
+
+ m = __gss_get_mechanism(desired_mech);
+
+ mc = malloc(sizeof(struct _gss_mechanism_cred));
+ if (!mc) {
+ release_cred = (gss_cred_id_t)new_cred;
+ gss_release_cred(&junk, &release_cred);
+ *minor_status = ENOMEM;
+ return (GSS_S_FAILURE);
+ }
+ mc->gmc_mech = m;
+ mc->gmc_mech_oid = &m->gm_mech_oid;
+
+ major_status = m->gm_add_cred(minor_status,
+ target_mc ? target_mc->gmc_cred : GSS_C_NO_CREDENTIAL,
+ desired_name ? mn->gmn_name : GSS_C_NO_NAME,
+ desired_mech,
+ cred_usage,
+ initiator_time_req,
+ acceptor_time_req,
+ &mc->gmc_cred,
+ actual_mechs,
+ initiator_time_rec,
+ acceptor_time_rec);
+
+ if (major_status) {
+ _gss_mg_error(m, major_status, *minor_status);
+ release_cred = (gss_cred_id_t)new_cred;
+ gss_release_cred(&junk, &release_cred);
+ free(mc);
+ return (major_status);
+ }
+ SLIST_INSERT_HEAD(&new_cred->gc_mc, mc, gmc_link);
+ *output_cred_handle = (gss_cred_id_t) new_cred;
+
+ return (GSS_S_COMPLETE);
+}
+
diff --git a/source4/heimdal/lib/gssapi/mech/gss_add_oid_set_member.c b/source4/heimdal/lib/gssapi/mech/gss_add_oid_set_member.c
new file mode 100644
index 0000000000..d10b1e7e43
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gss_add_oid_set_member.c
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 1997 - 2001, 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "mech_locl.h"
+RCSID("$Id$");
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_add_oid_set_member (OM_uint32 * minor_status,
+ const gss_OID member_oid,
+ gss_OID_set * oid_set)
+{
+ gss_OID tmp;
+ size_t n;
+ OM_uint32 res;
+ int present;
+
+ res = gss_test_oid_set_member(minor_status, member_oid, *oid_set, &present);
+ if (res != GSS_S_COMPLETE)
+ return res;
+
+ if (present) {
+ *minor_status = 0;
+ return GSS_S_COMPLETE;
+ }
+
+ n = (*oid_set)->count + 1;
+ tmp = realloc ((*oid_set)->elements, n * sizeof(gss_OID_desc));
+ if (tmp == NULL) {
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+ (*oid_set)->elements = tmp;
+ (*oid_set)->count = n;
+ (*oid_set)->elements[n-1] = *member_oid;
+ *minor_status = 0;
+ return GSS_S_COMPLETE;
+}
diff --git a/source4/heimdal/lib/gssapi/mech/gss_buffer_set.c b/source4/heimdal/lib/gssapi/mech/gss_buffer_set.c
new file mode 100644
index 0000000000..9f0bb4cce3
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gss_buffer_set.c
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2004, PADL Software Pty Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of PADL Software nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "mech_locl.h"
+RCSID("$Id$");
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_create_empty_buffer_set
+ (OM_uint32 * minor_status,
+ gss_buffer_set_t *buffer_set)
+{
+ gss_buffer_set_t set;
+
+ set = (gss_buffer_set_desc *) malloc(sizeof(*set));
+ if (set == GSS_C_NO_BUFFER_SET) {
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+
+ set->count = 0;
+ set->elements = NULL;
+
+ *buffer_set = set;
+
+ *minor_status = 0;
+ return GSS_S_COMPLETE;
+}
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_add_buffer_set_member
+ (OM_uint32 * minor_status,
+ const gss_buffer_t member_buffer,
+ gss_buffer_set_t *buffer_set)
+{
+ gss_buffer_set_t set;
+ gss_buffer_t p;
+ OM_uint32 ret;
+
+ if (*buffer_set == GSS_C_NO_BUFFER_SET) {
+ ret = gss_create_empty_buffer_set(minor_status,
+ buffer_set);
+ if (ret) {
+ return ret;
+ }
+ }
+
+ set = *buffer_set;
+ set->elements = realloc(set->elements,
+ (set->count + 1) * sizeof(set->elements[0]));
+ if (set->elements == NULL) {
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+
+ p = &set->elements[set->count];
+
+ p->value = malloc(member_buffer->length);
+ if (p->value == NULL) {
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+ memcpy(p->value, member_buffer->value, member_buffer->length);
+ p->length = member_buffer->length;
+
+ set->count++;
+
+ *minor_status = 0;
+ return GSS_S_COMPLETE;
+}
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_release_buffer_set(OM_uint32 * minor_status,
+ gss_buffer_set_t *buffer_set)
+{
+ int i;
+ OM_uint32 minor;
+
+ *minor_status = 0;
+
+ if (*buffer_set == GSS_C_NO_BUFFER_SET)
+ return GSS_S_COMPLETE;
+
+ for (i = 0; i < (*buffer_set)->count; i++)
+ gss_release_buffer(&minor, &((*buffer_set)->elements[i]));
+
+ free((*buffer_set)->elements);
+
+ (*buffer_set)->elements = NULL;
+ (*buffer_set)->count = 0;
+
+ free(*buffer_set);
+ *buffer_set = GSS_C_NO_BUFFER_SET;
+
+ return GSS_S_COMPLETE;
+}
+
diff --git a/source4/heimdal/lib/gssapi/mech/gss_canonicalize_name.c b/source4/heimdal/lib/gssapi/mech/gss_canonicalize_name.c
new file mode 100644
index 0000000000..91a08fb2bc
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gss_canonicalize_name.c
@@ -0,0 +1,88 @@
+/*-
+ * Copyright (c) 2005 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libgssapi/gss_canonicalize_name.c,v 1.1 2005/12/29 14:40:20 dfr Exp $
+ */
+
+#include "mech_locl.h"
+RCSID("$Id$");
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_canonicalize_name(OM_uint32 *minor_status,
+ const gss_name_t input_name,
+ const gss_OID mech_type,
+ gss_name_t *output_name)
+{
+ OM_uint32 major_status;
+ struct _gss_name *name = (struct _gss_name *) input_name;
+ struct _gss_mechanism_name *mn;
+ gssapi_mech_interface m;
+ gss_name_t new_canonical_name;
+
+ *minor_status = 0;
+ *output_name = 0;
+
+ major_status = _gss_find_mn(minor_status, name, mech_type, &mn);
+ if (major_status)
+ return major_status;
+
+ m = mn->gmn_mech;
+ major_status = m->gm_canonicalize_name(minor_status,
+ mn->gmn_name, mech_type, &new_canonical_name);
+ if (major_status) {
+ _gss_mg_error(m, major_status, *minor_status);
+ return (major_status);
+ }
+
+ /*
+ * Now we make a new name and mark it as an MN.
+ */
+ *minor_status = 0;
+ name = malloc(sizeof(struct _gss_name));
+ if (!name) {
+ m->gm_release_name(minor_status, &new_canonical_name);
+ *minor_status = ENOMEM;
+ return (GSS_S_FAILURE);
+ }
+ memset(name, 0, sizeof(struct _gss_name));
+
+ mn = malloc(sizeof(struct _gss_mechanism_name));
+ if (!mn) {
+ m->gm_release_name(minor_status, &new_canonical_name);
+ free(name);
+ *minor_status = ENOMEM;
+ return (GSS_S_FAILURE);
+ }
+
+ SLIST_INIT(&name->gn_mn);
+ mn->gmn_mech = m;
+ mn->gmn_mech_oid = &m->gm_mech_oid;
+ mn->gmn_name = new_canonical_name;
+ SLIST_INSERT_HEAD(&name->gn_mn, mn, gmn_link);
+
+ *output_name = (gss_name_t) name;
+
+ return (GSS_S_COMPLETE);
+}
diff --git a/source4/heimdal/lib/gssapi/mech/gss_compare_name.c b/source4/heimdal/lib/gssapi/mech/gss_compare_name.c
new file mode 100644
index 0000000000..3f2d0013c5
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gss_compare_name.c
@@ -0,0 +1,77 @@
+/*-
+ * Copyright (c) 2005 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libgssapi/gss_compare_name.c,v 1.1 2005/12/29 14:40:20 dfr Exp $
+ */
+
+#include "mech_locl.h"
+RCSID("$Id$");
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_compare_name(OM_uint32 *minor_status,
+ const gss_name_t name1_arg,
+ const gss_name_t name2_arg,
+ int *name_equal)
+{
+ struct _gss_name *name1 = (struct _gss_name *) name1_arg;
+ struct _gss_name *name2 = (struct _gss_name *) name2_arg;
+
+ /*
+ * First check the implementation-independant name if both
+ * names have one. Otherwise, try to find common mechanism
+ * names and compare them.
+ */
+ if (name1->gn_value.value && name2->gn_value.value) {
+ *name_equal = 1;
+ if (!gss_oid_equal(&name1->gn_type, &name2->gn_type)) {
+ *name_equal = 0;
+ } else if (name1->gn_value.length != name2->gn_value.length ||
+ memcmp(name1->gn_value.value, name1->gn_value.value,
+ name1->gn_value.length)) {
+ *name_equal = 0;
+ }
+ } else {
+ struct _gss_mechanism_name *mn1;
+ struct _gss_mechanism_name *mn2;
+
+ SLIST_FOREACH(mn1, &name1->gn_mn, gmn_link) {
+ OM_uint32 major_status;
+
+ major_status = _gss_find_mn(minor_status, name2,
+ mn1->gmn_mech_oid, &mn2);
+ if (major_status == GSS_S_COMPLETE) {
+ return (mn1->gmn_mech->gm_compare_name(
+ minor_status,
+ mn1->gmn_name,
+ mn2->gmn_name,
+ name_equal));
+ }
+ }
+ *name_equal = 0;
+ }
+
+ *minor_status = 0;
+ return (GSS_S_COMPLETE);
+}
diff --git a/source4/heimdal/lib/gssapi/mech/gss_context_time.c b/source4/heimdal/lib/gssapi/mech/gss_context_time.c
new file mode 100644
index 0000000000..df89612060
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gss_context_time.c
@@ -0,0 +1,41 @@
+/*-
+ * Copyright (c) 2005 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libgssapi/gss_context_time.c,v 1.1 2005/12/29 14:40:20 dfr Exp $
+ */
+
+#include "mech_locl.h"
+RCSID("$Id$");
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_context_time(OM_uint32 *minor_status,
+ const gss_ctx_id_t context_handle,
+ OM_uint32 *time_rec)
+{
+ struct _gss_context *ctx = (struct _gss_context *) context_handle;
+ gssapi_mech_interface m = ctx->gc_mech;
+
+ return (m->gm_context_time(minor_status, ctx->gc_ctx, time_rec));
+}
diff --git a/source4/heimdal/lib/gssapi/mech/gss_create_empty_oid_set.c b/source4/heimdal/lib/gssapi/mech/gss_create_empty_oid_set.c
new file mode 100644
index 0000000000..8858f28498
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gss_create_empty_oid_set.c
@@ -0,0 +1,52 @@
+/*-
+ * Copyright (c) 2005 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libgssapi/gss_create_empty_oid_set.c,v 1.1 2005/12/29 14:40:20 dfr Exp $
+ */
+
+#include "mech_locl.h"
+RCSID("$Id$");
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_create_empty_oid_set(OM_uint32 *minor_status,
+ gss_OID_set *oid_set)
+{
+ gss_OID_set set;
+
+ *minor_status = 0;
+ *oid_set = GSS_C_NO_OID_SET;
+
+ set = malloc(sizeof(gss_OID_set_desc));
+ if (!set) {
+ *minor_status = ENOMEM;
+ return (GSS_S_FAILURE);
+ }
+
+ set->count = 0;
+ set->elements = 0;
+ *oid_set = set;
+
+ return (GSS_S_COMPLETE);
+}
diff --git a/source4/heimdal/lib/gssapi/mech/gss_decapsulate_token.c b/source4/heimdal/lib/gssapi/mech/gss_decapsulate_token.c
new file mode 100644
index 0000000000..6681fc5a34
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gss_decapsulate_token.c
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "mech_locl.h"
+RCSID("$Id$");
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_decapsulate_token(gss_buffer_t input_token,
+ gss_OID oid,
+ gss_buffer_t output_token)
+{
+ GSSAPIContextToken ct;
+ heim_oid o;
+ OM_uint32 status;
+ int ret;
+ size_t size;
+
+ _mg_buffer_zero(output_token);
+
+ ret = der_get_oid (oid->elements, oid->length, &o, &size);
+ if (ret)
+ return GSS_S_FAILURE;
+
+ ret = decode_GSSAPIContextToken(input_token->value, input_token->length,
+ &ct, NULL);
+ if (ret) {
+ der_free_oid(&o);
+ return GSS_S_FAILURE;
+ }
+
+ if (der_heim_oid_cmp(&ct.thisMech, &o) == 0) {
+ status = GSS_S_COMPLETE;
+ output_token->value = ct.innerContextToken.data;
+ output_token->length = ct.innerContextToken.length;
+ der_free_oid(&ct.thisMech);
+ } else {
+ free_GSSAPIContextToken(&ct);
+ status = GSS_S_FAILURE;
+ }
+ der_free_oid(&o);
+
+ return status;
+}
diff --git a/source4/heimdal/lib/gssapi/mech/gss_delete_sec_context.c b/source4/heimdal/lib/gssapi/mech/gss_delete_sec_context.c
new file mode 100644
index 0000000000..96abae6b33
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gss_delete_sec_context.c
@@ -0,0 +1,58 @@
+/*-
+ * Copyright (c) 2005 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libgssapi/gss_delete_sec_context.c,v 1.1 2005/12/29 14:40:20 dfr Exp $
+ */
+
+#include "mech_locl.h"
+RCSID("$Id$");
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_delete_sec_context(OM_uint32 *minor_status,
+ gss_ctx_id_t *context_handle,
+ gss_buffer_t output_token)
+{
+ OM_uint32 major_status;
+ struct _gss_context *ctx = (struct _gss_context *) *context_handle;
+
+ if (output_token)
+ _mg_buffer_zero(output_token);
+
+ *minor_status = 0;
+ if (ctx) {
+ /*
+ * If we have an implementation ctx, delete it,
+ * otherwise fake an empty token.
+ */
+ if (ctx->gc_ctx) {
+ major_status = ctx->gc_mech->gm_delete_sec_context(
+ minor_status, &ctx->gc_ctx, output_token);
+ }
+ free(ctx);
+ *context_handle = GSS_C_NO_CONTEXT;
+ }
+
+ return (GSS_S_COMPLETE);
+}
diff --git a/source4/heimdal/lib/gssapi/mech/gss_display_name.c b/source4/heimdal/lib/gssapi/mech/gss_display_name.c
new file mode 100644
index 0000000000..d720ffe880
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gss_display_name.c
@@ -0,0 +1,83 @@
+/*-
+ * Copyright (c) 2005 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libgssapi/gss_display_name.c,v 1.1 2005/12/29 14:40:20 dfr Exp $
+ */
+
+#include "mech_locl.h"
+RCSID("$Id$");
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_display_name(OM_uint32 *minor_status,
+ const gss_name_t input_name,
+ gss_buffer_t output_name_buffer,
+ gss_OID *output_name_type)
+{
+ OM_uint32 major_status;
+ struct _gss_name *name = (struct _gss_name *) input_name;
+ struct _gss_mechanism_name *mn;
+
+ _mg_buffer_zero(output_name_buffer);
+ if (output_name_type)
+ *output_name_type = GSS_C_NO_OID;
+
+ if (name == NULL) {
+ *minor_status = 0;
+ return (GSS_S_BAD_NAME);
+ }
+
+ /*
+ * If we know it, copy the buffer used to import the name in
+ * the first place. Otherwise, ask all the MNs in turn if
+ * they can display the thing.
+ */
+ if (name->gn_value.value) {
+ output_name_buffer->value = malloc(name->gn_value.length);
+ if (!output_name_buffer->value) {
+ *minor_status = ENOMEM;
+ return (GSS_S_FAILURE);
+ }
+ output_name_buffer->length = name->gn_value.length;
+ memcpy(output_name_buffer->value, name->gn_value.value,
+ output_name_buffer->length);
+ if (output_name_type)
+ *output_name_type = &name->gn_type;
+
+ *minor_status = 0;
+ return (GSS_S_COMPLETE);
+ } else {
+ SLIST_FOREACH(mn, &name->gn_mn, gmn_link) {
+ major_status = mn->gmn_mech->gm_display_name(
+ minor_status, mn->gmn_name,
+ output_name_buffer,
+ output_name_type);
+ if (major_status == GSS_S_COMPLETE)
+ return (GSS_S_COMPLETE);
+ }
+ }
+
+ *minor_status = 0;
+ return (GSS_S_FAILURE);
+}
diff --git a/source4/heimdal/lib/gssapi/mech/gss_display_status.c b/source4/heimdal/lib/gssapi/mech/gss_display_status.c
new file mode 100644
index 0000000000..daa62bfcd9
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gss_display_status.c
@@ -0,0 +1,210 @@
+/*-
+ * Copyright (c) 2005 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libgssapi/gss_display_status.c,v 1.1 2005/12/29 14:40:20 dfr Exp $
+ */
+/*
+ * Copyright (c) 1998 - 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "mech_locl.h"
+RCSID("$Id$");
+
+static const char *
+calling_error(OM_uint32 v)
+{
+ static const char *msgs[] = {
+ NULL, /* 0 */
+ "A required input parameter could not be read.", /* */
+ "A required output parameter could not be written.", /* */
+ "A parameter was malformed"
+ };
+
+ v >>= GSS_C_CALLING_ERROR_OFFSET;
+
+ if (v == 0)
+ return "";
+ else if (v >= sizeof(msgs)/sizeof(*msgs))
+ return "unknown calling error";
+ else
+ return msgs[v];
+}
+
+static const char *
+routine_error(OM_uint32 v)
+{
+ static const char *msgs[] = {
+ "Function completed successfully", /* 0 */
+ "An unsupported mechanism was requested",
+ "An invalid name was supplied",
+ "A supplied name was of an unsupported type",
+ "Incorrect channel bindings were supplied",
+ "An invalid status code was supplied",
+ "A token had an invalid MIC",
+ "No credentials were supplied, "
+ "or the credentials were unavailable or inaccessible.",
+ "No context has been established",
+ "A token was invalid",
+ "A credential was invalid",
+ "The referenced credentials have expired",
+ "The context has expired",
+ "Miscellaneous failure (see text)",
+ "The quality-of-protection requested could not be provide",
+ "The operation is forbidden by local security policy",
+ "The operation or option is not available",
+ "The requested credential element already exists",
+ "The provided name was not a mechanism name.",
+ };
+
+ v >>= GSS_C_ROUTINE_ERROR_OFFSET;
+
+ if (v >= sizeof(msgs)/sizeof(*msgs))
+ return "unknown routine error";
+ else
+ return msgs[v];
+}
+
+static const char *
+supplementary_error(OM_uint32 v)
+{
+ static const char *msgs[] = {
+ "normal completion",
+ "continuation call to routine required",
+ "duplicate per-message token detected",
+ "timed-out per-message token detected",
+ "reordered (early) per-message token detected",
+ "skipped predecessor token(s) detected"
+ };
+
+ v >>= GSS_C_SUPPLEMENTARY_OFFSET;
+
+ if (v >= sizeof(msgs)/sizeof(*msgs))
+ return "unknown routine error";
+ else
+ return msgs[v];
+}
+
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_display_status(OM_uint32 *minor_status,
+ OM_uint32 status_value,
+ int status_type,
+ const gss_OID mech_type,
+ OM_uint32 *message_content,
+ gss_buffer_t status_string)
+{
+ OM_uint32 major_status;
+
+ _mg_buffer_zero(status_string);
+ *message_content = 0;
+
+ major_status = _gss_mg_get_error(mech_type, status_type,
+ status_value, status_string);
+ if (major_status == GSS_S_COMPLETE) {
+
+ *message_content = 0;
+ *minor_status = 0;
+ return GSS_S_COMPLETE;
+ }
+
+ *minor_status = 0;
+ switch (status_type) {
+ case GSS_C_GSS_CODE: {
+ char *buf;
+
+ if (GSS_SUPPLEMENTARY_INFO(status_value))
+ asprintf(&buf, "%s", supplementary_error(
+ GSS_SUPPLEMENTARY_INFO(status_value)));
+ else
+ asprintf (&buf, "%s %s",
+ calling_error(GSS_CALLING_ERROR(status_value)),
+ routine_error(GSS_ROUTINE_ERROR(status_value)));
+
+ if (buf == NULL)
+ break;
+
+ status_string->length = strlen(buf);
+ status_string->value = buf;
+
+ return GSS_S_COMPLETE;
+ }
+ case GSS_C_MECH_CODE: {
+ OM_uint32 maj_junk, min_junk;
+ gss_buffer_desc oid;
+ char *buf;
+
+ maj_junk = gss_oid_to_str(&min_junk, mech_type, &oid);
+ if (maj_junk != GSS_S_COMPLETE) {
+ oid.value = rk_UNCONST("unknown");
+ oid.length = 7;
+ }
+
+ asprintf (&buf, "unknown mech-code %lu for mech %.*s",
+ (unsigned long)status_value,
+ (int)oid.length, (char *)oid.value);
+ if (maj_junk == GSS_S_COMPLETE)
+ gss_release_buffer(&min_junk, &oid);
+
+ if (buf == NULL)
+ break;
+
+ status_string->length = strlen(buf);
+ status_string->value = buf;
+
+ return GSS_S_COMPLETE;
+ }
+ }
+ _mg_buffer_zero(status_string);
+ return (GSS_S_BAD_STATUS);
+}
diff --git a/source4/heimdal/lib/gssapi/mech/gss_duplicate_name.c b/source4/heimdal/lib/gssapi/mech/gss_duplicate_name.c
new file mode 100644
index 0000000000..1690ae6c51
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gss_duplicate_name.c
@@ -0,0 +1,95 @@
+/*-
+ * Copyright (c) 2005 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libgssapi/gss_duplicate_name.c,v 1.1 2005/12/29 14:40:20 dfr Exp $
+ */
+
+#include "mech_locl.h"
+RCSID("$Id$");
+
+OM_uint32 gss_duplicate_name(OM_uint32 *minor_status,
+ const gss_name_t src_name,
+ gss_name_t *dest_name)
+{
+ OM_uint32 major_status;
+ struct _gss_name *name = (struct _gss_name *) src_name;
+ struct _gss_name *new_name;
+ struct _gss_mechanism_name *mn;
+
+ *minor_status = 0;
+ *dest_name = GSS_C_NO_NAME;
+
+ /*
+ * If this name has a value (i.e. it didn't come from
+ * gss_canonicalize_name(), we re-import the thing. Otherwise,
+ * we make copy of each mech names.
+ */
+ if (name->gn_value.value) {
+ major_status = gss_import_name(minor_status,
+ &name->gn_value, &name->gn_type, dest_name);
+ if (major_status != GSS_S_COMPLETE)
+ return (major_status);
+ new_name = (struct _gss_name *) *dest_name;
+
+ SLIST_FOREACH(mn, &name->gn_mn, gmn_link) {
+ struct _gss_mechanism_name *mn2;
+ _gss_find_mn(minor_status, new_name,
+ mn->gmn_mech_oid, &mn2);
+ }
+ } else {
+ new_name = malloc(sizeof(struct _gss_name));
+ if (!new_name) {
+ *minor_status = ENOMEM;
+ return (GSS_S_FAILURE);
+ }
+ memset(new_name, 0, sizeof(struct _gss_name));
+ SLIST_INIT(&new_name->gn_mn);
+ *dest_name = (gss_name_t) new_name;
+
+ SLIST_FOREACH(mn, &name->gn_mn, gmn_link) {
+ struct _gss_mechanism_name *new_mn;
+
+ new_mn = malloc(sizeof(*new_mn));
+ if (!new_mn) {
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+ new_mn->gmn_mech = mn->gmn_mech;
+ new_mn->gmn_mech_oid = mn->gmn_mech_oid;
+
+ major_status =
+ mn->gmn_mech->gm_duplicate_name(minor_status,
+ mn->gmn_name, &new_mn->gmn_name);
+ if (major_status != GSS_S_COMPLETE) {
+ free(new_mn);
+ continue;
+ }
+ SLIST_INSERT_HEAD(&new_name->gn_mn, new_mn, gmn_link);
+ }
+
+ }
+
+ return (GSS_S_COMPLETE);
+}
diff --git a/source4/heimdal/lib/gssapi/mech/gss_duplicate_oid.c b/source4/heimdal/lib/gssapi/mech/gss_duplicate_oid.c
new file mode 100644
index 0000000000..b6aa226fda
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gss_duplicate_oid.c
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 1997 - 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "mech_locl.h"
+RCSID("$Id$");
+
+OM_uint32 gss_duplicate_oid (
+ OM_uint32 *minor_status,
+ gss_OID src_oid,
+ gss_OID *dest_oid
+ )
+{
+ *minor_status = 0;
+
+ if (src_oid == GSS_C_NO_OID) {
+ *dest_oid = GSS_C_NO_OID;
+ return GSS_S_COMPLETE;
+ }
+
+ *dest_oid = malloc(sizeof(**dest_oid));
+ if (*dest_oid == GSS_C_NO_OID) {
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+
+ (*dest_oid)->elements = malloc(src_oid->length);
+ if ((*dest_oid)->elements == NULL) {
+ free(*dest_oid);
+ *dest_oid = GSS_C_NO_OID;
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+ memcpy((*dest_oid)->elements, src_oid->elements, src_oid->length);
+ (*dest_oid)->length = src_oid->length;
+
+ *minor_status = 0;
+ return GSS_S_COMPLETE;
+}
diff --git a/source4/heimdal/lib/gssapi/mech/gss_encapsulate_token.c b/source4/heimdal/lib/gssapi/mech/gss_encapsulate_token.c
new file mode 100644
index 0000000000..b5434be85a
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gss_encapsulate_token.c
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "mech_locl.h"
+RCSID("$Id$");
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_encapsulate_token(gss_buffer_t input_token,
+ gss_OID oid,
+ gss_buffer_t output_token)
+{
+ GSSAPIContextToken ct;
+ int ret;
+ size_t size;
+
+ ret = der_get_oid (oid->elements, oid->length, &ct.thisMech, &size);
+ if (ret) {
+ _mg_buffer_zero(output_token);
+ return GSS_S_FAILURE;
+ }
+
+ ct.innerContextToken.data = input_token->value;
+ ct.innerContextToken.length = input_token->length;
+
+ ASN1_MALLOC_ENCODE(GSSAPIContextToken,
+ output_token->value, output_token->length,
+ &ct, &size, ret);
+ der_free_oid(&ct.thisMech);
+ if (ret) {
+ _mg_buffer_zero(output_token);
+ return GSS_S_FAILURE;
+ }
+ if (output_token->length != size)
+ abort();
+
+ return GSS_S_COMPLETE;
+}
diff --git a/source4/heimdal/lib/gssapi/mech/gss_export_name.c b/source4/heimdal/lib/gssapi/mech/gss_export_name.c
new file mode 100644
index 0000000000..7c1e6791da
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gss_export_name.c
@@ -0,0 +1,55 @@
+/*-
+ * Copyright (c) 2005 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libgssapi/gss_export_name.c,v 1.1 2005/12/29 14:40:20 dfr Exp $
+ */
+
+#include "mech_locl.h"
+RCSID("$Id$");
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_export_name(OM_uint32 *minor_status,
+ const gss_name_t input_name,
+ gss_buffer_t exported_name)
+{
+ struct _gss_name *name = (struct _gss_name *) input_name;
+ struct _gss_mechanism_name *mn;
+
+ _mg_buffer_zero(exported_name);
+
+ /*
+ * If this name already has any attached MNs, export the first
+ * one, otherwise export based on the first mechanism in our
+ * list.
+ */
+ mn = SLIST_FIRST(&name->gn_mn);
+ if (!mn) {
+ *minor_status = 0;
+ return (GSS_S_NAME_NOT_MN);
+ }
+
+ return mn->gmn_mech->gm_export_name(minor_status,
+ mn->gmn_name, exported_name);
+}
diff --git a/source4/heimdal/lib/gssapi/mech/gss_export_sec_context.c b/source4/heimdal/lib/gssapi/mech/gss_export_sec_context.c
new file mode 100644
index 0000000000..f3a6dc4fb5
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gss_export_sec_context.c
@@ -0,0 +1,78 @@
+/*-
+ * Copyright (c) 2005 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libgssapi/gss_export_sec_context.c,v 1.1 2005/12/29 14:40:20 dfr Exp $
+ */
+
+#include "mech_locl.h"
+RCSID("$Id$");
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_export_sec_context(OM_uint32 *minor_status,
+ gss_ctx_id_t *context_handle,
+ gss_buffer_t interprocess_token)
+{
+ OM_uint32 major_status;
+ struct _gss_context *ctx = (struct _gss_context *) *context_handle;
+ gssapi_mech_interface m = ctx->gc_mech;
+ gss_buffer_desc buf;
+
+ _mg_buffer_zero(interprocess_token);
+
+ major_status = m->gm_export_sec_context(minor_status,
+ &ctx->gc_ctx, &buf);
+
+ if (major_status == GSS_S_COMPLETE) {
+ unsigned char *p;
+
+ free(ctx);
+ *context_handle = GSS_C_NO_CONTEXT;
+ interprocess_token->length = buf.length
+ + 2 + m->gm_mech_oid.length;
+ interprocess_token->value = malloc(interprocess_token->length);
+ if (!interprocess_token->value) {
+ /*
+ * We are in trouble here - the context is
+ * already gone. This is allowed as long as we
+ * set the caller's context_handle to
+ * GSS_C_NO_CONTEXT, which we did above.
+ * Return GSS_S_FAILURE.
+ */
+ _mg_buffer_zero(interprocess_token);
+ *minor_status = ENOMEM;
+ return (GSS_S_FAILURE);
+ }
+ p = interprocess_token->value;
+ p[0] = m->gm_mech_oid.length >> 8;
+ p[1] = m->gm_mech_oid.length;
+ memcpy(p + 2, m->gm_mech_oid.elements, m->gm_mech_oid.length);
+ memcpy(p + 2 + m->gm_mech_oid.length, buf.value, buf.length);
+ gss_release_buffer(minor_status, &buf);
+ } else {
+ _gss_mg_error(m, major_status, *minor_status);
+ }
+
+ return (major_status);
+}
diff --git a/source4/heimdal/lib/gssapi/mech/gss_get_mic.c b/source4/heimdal/lib/gssapi/mech/gss_get_mic.c
new file mode 100644
index 0000000000..9cd5060fc9
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gss_get_mic.c
@@ -0,0 +1,50 @@
+/*-
+ * Copyright (c) 2005 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libgssapi/gss_get_mic.c,v 1.1 2005/12/29 14:40:20 dfr Exp $
+ */
+
+#include "mech_locl.h"
+RCSID("$Id$");
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_get_mic(OM_uint32 *minor_status,
+ const gss_ctx_id_t context_handle,
+ gss_qop_t qop_req,
+ const gss_buffer_t message_buffer,
+ gss_buffer_t message_token)
+{
+ struct _gss_context *ctx = (struct _gss_context *) context_handle;
+ gssapi_mech_interface m = ctx->gc_mech;
+
+ _mg_buffer_zero(message_token);
+ if (ctx == NULL) {
+ *minor_status = 0;
+ return GSS_S_NO_CONTEXT;
+ }
+
+ return (m->gm_get_mic(minor_status, ctx->gc_ctx, qop_req,
+ message_buffer, message_token));
+}
diff --git a/source4/heimdal/lib/gssapi/mech/gss_import_name.c b/source4/heimdal/lib/gssapi/mech/gss_import_name.c
new file mode 100644
index 0000000000..040e228410
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gss_import_name.c
@@ -0,0 +1,218 @@
+/*-
+ * Copyright (c) 2005 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libgssapi/gss_import_name.c,v 1.1 2005/12/29 14:40:20 dfr Exp $
+ */
+
+#include "mech_locl.h"
+RCSID("$Id$");
+
+static OM_uint32
+_gss_import_export_name(OM_uint32 *minor_status,
+ const gss_buffer_t input_name_buffer,
+ gss_name_t *output_name)
+{
+ OM_uint32 major_status;
+ unsigned char *p = input_name_buffer->value;
+ size_t len = input_name_buffer->length;
+ size_t t;
+ gss_OID_desc mech_oid;
+ gssapi_mech_interface m;
+ struct _gss_name *name;
+ gss_name_t new_canonical_name;
+
+ *minor_status = 0;
+ *output_name = 0;
+
+ /*
+ * Make sure that TOK_ID is {4, 1}.
+ */
+ if (len < 2)
+ return (GSS_S_BAD_NAME);
+ if (p[0] != 4 || p[1] != 1)
+ return (GSS_S_BAD_NAME);
+ p += 2;
+ len -= 2;
+
+ /*
+ * Get the mech length and the name length and sanity
+ * check the size of of the buffer.
+ */
+ if (len < 2)
+ return (GSS_S_BAD_NAME);
+ t = (p[0] << 8) + p[1];
+ p += 2;
+ len -= 2;
+
+ /*
+ * Check the DER encoded OID to make sure it agrees with the
+ * length we just decoded.
+ */
+ if (p[0] != 6) /* 6=OID */
+ return (GSS_S_BAD_NAME);
+ p++;
+ len--;
+ t--;
+ if (p[0] & 0x80) {
+ int digits = p[0];
+ p++;
+ len--;
+ t--;
+ mech_oid.length = 0;
+ while (digits--) {
+ mech_oid.length = (mech_oid.length << 8) | p[0];
+ p++;
+ len--;
+ t--;
+ }
+ } else {
+ mech_oid.length = p[0];
+ p++;
+ len--;
+ t--;
+ }
+ if (mech_oid.length != t)
+ return (GSS_S_BAD_NAME);
+
+ mech_oid.elements = p;
+
+ if (len < t + 4)
+ return (GSS_S_BAD_NAME);
+ p += t;
+ len -= t;
+
+ t = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
+ p += 4;
+ len -= 4;
+
+ if (len != t)
+ return (GSS_S_BAD_NAME);
+
+ m = __gss_get_mechanism(&mech_oid);
+ if (!m)
+ return (GSS_S_BAD_MECH);
+
+ /*
+ * Ask the mechanism to import the name.
+ */
+ major_status = m->gm_import_name(minor_status,
+ input_name_buffer, GSS_C_NT_EXPORT_NAME, &new_canonical_name);
+ if (major_status != GSS_S_COMPLETE) {
+ _gss_mg_error(m, major_status, *minor_status);
+ return major_status;
+ }
+
+ /*
+ * Now we make a new name and mark it as an MN.
+ */
+ name = _gss_make_name(m, new_canonical_name);
+ if (!name) {
+ m->gm_release_name(minor_status, &new_canonical_name);
+ return (GSS_S_FAILURE);
+ }
+
+ *output_name = (gss_name_t) name;
+
+ *minor_status = 0;
+ return (GSS_S_COMPLETE);
+}
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_import_name(OM_uint32 *minor_status,
+ const gss_buffer_t input_name_buffer,
+ const gss_OID input_name_type,
+ gss_name_t *output_name)
+{
+ gss_OID name_type = input_name_type;
+ OM_uint32 major_status;
+ struct _gss_name *name;
+
+ *output_name = GSS_C_NO_NAME;
+
+ if (input_name_buffer->length == 0) {
+ *minor_status = 0;
+ return (GSS_S_BAD_NAME);
+ }
+
+ /*
+ * Use GSS_NT_USER_NAME as default name type.
+ */
+ if (name_type == GSS_C_NO_OID)
+ name_type = GSS_C_NT_USER_NAME;
+
+ /*
+ * If this is an exported name, we need to parse it to find
+ * the mechanism and then import it as an MN. See RFC 2743
+ * section 3.2 for a description of the format.
+ */
+ if (gss_oid_equal(name_type, GSS_C_NT_EXPORT_NAME)) {
+ return _gss_import_export_name(minor_status,
+ input_name_buffer, output_name);
+ }
+
+ /*
+ * Only allow certain name types. This is pretty bogus - we
+ * should figure out the list of supported name types using
+ * gss_inquire_names_for_mech.
+ */
+ if (!gss_oid_equal(name_type, GSS_C_NT_USER_NAME)
+ && !gss_oid_equal(name_type, GSS_C_NT_MACHINE_UID_NAME)
+ && !gss_oid_equal(name_type, GSS_C_NT_STRING_UID_NAME)
+ && !gss_oid_equal(name_type, GSS_C_NT_HOSTBASED_SERVICE_X)
+ && !gss_oid_equal(name_type, GSS_C_NT_HOSTBASED_SERVICE)
+ && !gss_oid_equal(name_type, GSS_C_NT_ANONYMOUS)
+ && !gss_oid_equal(name_type, GSS_KRB5_NT_PRINCIPAL_NAME)) {
+ *minor_status = 0;
+ return (GSS_S_BAD_NAMETYPE);
+ }
+
+ *minor_status = 0;
+ name = malloc(sizeof(struct _gss_name));
+ if (!name) {
+ *minor_status = ENOMEM;
+ return (GSS_S_FAILURE);
+ }
+ memset(name, 0, sizeof(struct _gss_name));
+
+ major_status = _gss_copy_oid(minor_status,
+ name_type, &name->gn_type);
+ if (major_status) {
+ free(name);
+ return (GSS_S_FAILURE);
+ }
+
+ major_status = _gss_copy_buffer(minor_status,
+ input_name_buffer, &name->gn_value);
+ if (major_status) {
+ gss_name_t rname = (gss_name_t)name;
+ gss_release_name(minor_status, &rname);
+ return (GSS_S_FAILURE);
+ }
+
+ SLIST_INIT(&name->gn_mn);
+
+ *output_name = (gss_name_t) name;
+ return (GSS_S_COMPLETE);
+}
diff --git a/source4/heimdal/lib/gssapi/mech/gss_import_sec_context.c b/source4/heimdal/lib/gssapi/mech/gss_import_sec_context.c
new file mode 100644
index 0000000000..01ca9f10df
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gss_import_sec_context.c
@@ -0,0 +1,83 @@
+/*-
+ * Copyright (c) 2005 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libgssapi/gss_import_sec_context.c,v 1.1 2005/12/29 14:40:20 dfr Exp $
+ */
+
+#include "mech_locl.h"
+RCSID("$Id$");
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_import_sec_context(OM_uint32 *minor_status,
+ const gss_buffer_t interprocess_token,
+ gss_ctx_id_t *context_handle)
+{
+ OM_uint32 major_status;
+ gssapi_mech_interface m;
+ struct _gss_context *ctx;
+ gss_OID_desc mech_oid;
+ gss_buffer_desc buf;
+ unsigned char *p;
+ size_t len;
+
+ *minor_status = 0;
+ *context_handle = GSS_C_NO_CONTEXT;
+
+ /*
+ * We added an oid to the front of the token in
+ * gss_export_sec_context.
+ */
+ p = interprocess_token->value;
+ len = interprocess_token->length;
+ if (len < 2)
+ return (GSS_S_DEFECTIVE_TOKEN);
+ mech_oid.length = (p[0] << 8) | p[1];
+ if (len < mech_oid.length + 2)
+ return (GSS_S_DEFECTIVE_TOKEN);
+ mech_oid.elements = p + 2;
+ buf.length = len - 2 - mech_oid.length;
+ buf.value = p + 2 + mech_oid.length;
+
+ m = __gss_get_mechanism(&mech_oid);
+ if (!m)
+ return (GSS_S_DEFECTIVE_TOKEN);
+
+ ctx = malloc(sizeof(struct _gss_context));
+ if (!ctx) {
+ *minor_status = ENOMEM;
+ return (GSS_S_FAILURE);
+ }
+ ctx->gc_mech = m;
+ major_status = m->gm_import_sec_context(minor_status,
+ &buf, &ctx->gc_ctx);
+ if (major_status != GSS_S_COMPLETE) {
+ _gss_mg_error(m, major_status, *minor_status);
+ free(ctx);
+ } else {
+ *context_handle = (gss_ctx_id_t) ctx;
+ }
+
+ return (major_status);
+}
diff --git a/source4/heimdal/lib/gssapi/mech/gss_indicate_mechs.c b/source4/heimdal/lib/gssapi/mech/gss_indicate_mechs.c
new file mode 100644
index 0000000000..34c0bb55d8
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gss_indicate_mechs.c
@@ -0,0 +1,65 @@
+/*-
+ * Copyright (c) 2005 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libgssapi/gss_indicate_mechs.c,v 1.1 2005/12/29 14:40:20 dfr Exp $
+ */
+
+#include "mech_locl.h"
+RCSID("$Id$");
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_indicate_mechs(OM_uint32 *minor_status,
+ gss_OID_set *mech_set)
+{
+ struct _gss_mech_switch *m;
+ OM_uint32 major_status;
+ gss_OID_set set;
+ int i;
+
+ _gss_load_mech();
+
+ major_status = gss_create_empty_oid_set(minor_status, mech_set);
+ if (major_status)
+ return (major_status);
+
+ SLIST_FOREACH(m, &_gss_mechs, gm_link) {
+ if (m->gm_mech.gm_indicate_mechs) {
+ major_status = m->gm_mech.gm_indicate_mechs(
+ minor_status, &set);
+ if (major_status)
+ continue;
+ for (i = 0; i < set->count; i++)
+ major_status = gss_add_oid_set_member(
+ minor_status, &set->elements[i], mech_set);
+ gss_release_oid_set(minor_status, &set);
+ } else {
+ major_status = gss_add_oid_set_member(
+ minor_status, &m->gm_mech_oid, mech_set);
+ }
+ }
+
+ *minor_status = 0;
+ return (GSS_S_COMPLETE);
+}
diff --git a/source4/heimdal/lib/gssapi/mech/gss_init_sec_context.c b/source4/heimdal/lib/gssapi/mech/gss_init_sec_context.c
new file mode 100644
index 0000000000..579000a7ec
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gss_init_sec_context.c
@@ -0,0 +1,149 @@
+/*-
+ * Copyright (c) 2005 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libgssapi/gss_init_sec_context.c,v 1.1 2005/12/29 14:40:20 dfr Exp $
+ */
+
+#include "mech_locl.h"
+RCSID("$Id$");
+
+static gss_cred_id_t
+_gss_mech_cred_find(gss_cred_id_t cred_handle, gss_OID mech_type)
+{
+ struct _gss_cred *cred = (struct _gss_cred *)cred_handle;
+ struct _gss_mechanism_cred *mc;
+
+ if (cred == NULL)
+ return GSS_C_NO_CREDENTIAL;
+
+ SLIST_FOREACH(mc, &cred->gc_mc, gmc_link) {
+ if (gss_oid_equal(mech_type, mc->gmc_mech_oid))
+ return mc->gmc_cred;
+ }
+ return GSS_C_NO_CREDENTIAL;
+}
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_init_sec_context(OM_uint32 * minor_status,
+ const gss_cred_id_t initiator_cred_handle,
+ gss_ctx_id_t * context_handle,
+ const gss_name_t target_name,
+ const gss_OID input_mech_type,
+ OM_uint32 req_flags,
+ OM_uint32 time_req,
+ const gss_channel_bindings_t input_chan_bindings,
+ const gss_buffer_t input_token,
+ gss_OID * actual_mech_type,
+ gss_buffer_t output_token,
+ OM_uint32 * ret_flags,
+ OM_uint32 * time_rec)
+{
+ OM_uint32 major_status;
+ gssapi_mech_interface m;
+ struct _gss_name *name = (struct _gss_name *) target_name;
+ struct _gss_mechanism_name *mn;
+ struct _gss_context *ctx = (struct _gss_context *) *context_handle;
+ gss_cred_id_t cred_handle;
+ int allocated_ctx;
+ gss_OID mech_type = input_mech_type;
+
+ *minor_status = 0;
+
+ _mg_buffer_zero(output_token);
+ if (actual_mech_type)
+ *actual_mech_type = GSS_C_NO_OID;
+ if (ret_flags)
+ *ret_flags = 0;
+ if (time_rec)
+ *time_rec = 0;
+
+ /*
+ * If we haven't allocated a context yet, do so now and lookup
+ * the mechanism switch table. If we have one already, make
+ * sure we use the same mechanism switch as before.
+ */
+ if (!ctx) {
+ if (mech_type == NULL)
+ mech_type = GSS_KRB5_MECHANISM;
+
+ ctx = malloc(sizeof(struct _gss_context));
+ if (!ctx) {
+ *minor_status = ENOMEM;
+ return (GSS_S_FAILURE);
+ }
+ memset(ctx, 0, sizeof(struct _gss_context));
+ m = ctx->gc_mech = __gss_get_mechanism(mech_type);
+ if (!m) {
+ free(ctx);
+ return (GSS_S_BAD_MECH);
+ }
+ allocated_ctx = 1;
+ } else {
+ m = ctx->gc_mech;
+ mech_type = &ctx->gc_mech->gm_mech_oid;
+ allocated_ctx = 0;
+ }
+
+ /*
+ * Find the MN for this mechanism.
+ */
+ major_status = _gss_find_mn(minor_status, name, mech_type, &mn);
+ if (major_status != GSS_S_COMPLETE) {
+ if (allocated_ctx)
+ free(ctx);
+ return major_status;
+ }
+
+ /*
+ * If we have a cred, find the cred for this mechanism.
+ */
+ cred_handle = _gss_mech_cred_find(initiator_cred_handle, mech_type);
+
+ major_status = m->gm_init_sec_context(minor_status,
+ cred_handle,
+ &ctx->gc_ctx,
+ mn->gmn_name,
+ mech_type,
+ req_flags,
+ time_req,
+ input_chan_bindings,
+ input_token,
+ actual_mech_type,
+ output_token,
+ ret_flags,
+ time_rec);
+
+ if (major_status != GSS_S_COMPLETE
+ && major_status != GSS_S_CONTINUE_NEEDED) {
+ if (allocated_ctx)
+ free(ctx);
+ _mg_buffer_zero(output_token);
+ _gss_mg_error(m, major_status, *minor_status);
+ } else {
+ *context_handle = (gss_ctx_id_t) ctx;
+ }
+
+ return (major_status);
+}
diff --git a/source4/heimdal/lib/gssapi/mech/gss_inquire_context.c b/source4/heimdal/lib/gssapi/mech/gss_inquire_context.c
new file mode 100644
index 0000000000..8872f121d0
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gss_inquire_context.c
@@ -0,0 +1,106 @@
+/*-
+ * Copyright (c) 2005 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libgssapi/gss_inquire_context.c,v 1.1 2005/12/29 14:40:20 dfr Exp $
+ */
+
+#include "mech_locl.h"
+RCSID("$Id$");
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_inquire_context(OM_uint32 *minor_status,
+ const gss_ctx_id_t context_handle,
+ gss_name_t *src_name,
+ gss_name_t *targ_name,
+ OM_uint32 *lifetime_rec,
+ gss_OID *mech_type,
+ OM_uint32 *ctx_flags,
+ int *locally_initiated,
+ int *open)
+{
+ OM_uint32 major_status;
+ struct _gss_context *ctx = (struct _gss_context *) context_handle;
+ gssapi_mech_interface m = ctx->gc_mech;
+ struct _gss_name *name;
+ gss_name_t src_mn, targ_mn;
+
+ if (locally_initiated)
+ *locally_initiated = 0;
+ if (open)
+ *open = 0;
+ if (lifetime_rec)
+ *lifetime_rec = 0;
+
+ if (src_name)
+ *src_name = GSS_C_NO_NAME;
+ if (targ_name)
+ *targ_name = GSS_C_NO_NAME;
+ if (mech_type)
+ *mech_type = GSS_C_NO_OID;
+ src_mn = targ_mn = GSS_C_NO_NAME;
+
+ major_status = m->gm_inquire_context(minor_status,
+ ctx->gc_ctx,
+ src_name ? &src_mn : NULL,
+ targ_name ? &targ_mn : NULL,
+ lifetime_rec,
+ mech_type,
+ ctx_flags,
+ locally_initiated,
+ open);
+
+ if (major_status != GSS_S_COMPLETE) {
+ _gss_mg_error(m, major_status, *minor_status);
+ return (major_status);
+ }
+
+ if (src_name) {
+ name = _gss_make_name(m, src_mn);
+ if (!name) {
+ if (mech_type)
+ *mech_type = GSS_C_NO_OID;
+ m->gm_release_name(minor_status, &src_mn);
+ *minor_status = 0;
+ return (GSS_S_FAILURE);
+ }
+ *src_name = (gss_name_t) name;
+ }
+
+ if (targ_name) {
+ name = _gss_make_name(m, targ_mn);
+ if (!name) {
+ if (mech_type)
+ *mech_type = GSS_C_NO_OID;
+ if (src_name)
+ gss_release_name(minor_status, src_name);
+ m->gm_release_name(minor_status, &targ_mn);
+ *minor_status = 0;
+ return (GSS_S_FAILURE);
+ }
+ *targ_name = (gss_name_t) name;
+ }
+
+ return (GSS_S_COMPLETE);
+}
diff --git a/source4/heimdal/lib/gssapi/mech/gss_inquire_cred.c b/source4/heimdal/lib/gssapi/mech/gss_inquire_cred.c
new file mode 100644
index 0000000000..3587572672
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gss_inquire_cred.c
@@ -0,0 +1,196 @@
+/*-
+ * Copyright (c) 2005 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libgssapi/gss_inquire_cred.c,v 1.1 2005/12/29 14:40:20 dfr Exp $
+ */
+
+#include "mech_locl.h"
+RCSID("$Id$");
+
+#define AUSAGE 1
+#define IUSAGE 2
+
+static void
+updateusage(gss_cred_usage_t usage, int *usagemask)
+{
+ if (usage == GSS_C_BOTH)
+ *usagemask |= AUSAGE | IUSAGE;
+ else if (usage == GSS_C_ACCEPT)
+ *usagemask |= AUSAGE;
+ else if (usage == GSS_C_INITIATE)
+ *usagemask |= IUSAGE;
+}
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_inquire_cred(OM_uint32 *minor_status,
+ const gss_cred_id_t cred_handle,
+ gss_name_t *name_ret,
+ OM_uint32 *lifetime,
+ gss_cred_usage_t *cred_usage,
+ gss_OID_set *mechanisms)
+{
+ OM_uint32 major_status;
+ struct _gss_mech_switch *m;
+ struct _gss_cred *cred = (struct _gss_cred *) cred_handle;
+ struct _gss_name *name;
+ struct _gss_mechanism_name *mn;
+ OM_uint32 min_lifetime;
+ int found = 0;
+ int usagemask = 0;
+ gss_cred_usage_t usage;
+
+ _gss_load_mech();
+
+ *minor_status = 0;
+ if (name_ret)
+ *name_ret = GSS_C_NO_NAME;
+ if (lifetime)
+ *lifetime = 0;
+ if (cred_usage)
+ *cred_usage = 0;
+ if (mechanisms)
+ *mechanisms = GSS_C_NO_OID_SET;
+
+ if (name_ret) {
+ name = calloc(1, sizeof(*name));
+ if (name == NULL) {
+ *minor_status = ENOMEM;
+ return (GSS_S_FAILURE);
+ }
+ SLIST_INIT(&name->gn_mn);
+ } else {
+ name = NULL;
+ }
+
+ if (mechanisms) {
+ major_status = gss_create_empty_oid_set(minor_status,
+ mechanisms);
+ if (major_status) {
+ if (name) free(name);
+ return (major_status);
+ }
+ }
+
+ min_lifetime = GSS_C_INDEFINITE;
+ if (cred) {
+ struct _gss_mechanism_cred *mc;
+
+ SLIST_FOREACH(mc, &cred->gc_mc, gmc_link) {
+ gss_name_t mc_name;
+ OM_uint32 mc_lifetime;
+
+ major_status = mc->gmc_mech->gm_inquire_cred(minor_status,
+ mc->gmc_cred, &mc_name, &mc_lifetime, &usage, NULL);
+ if (major_status)
+ continue;
+
+ updateusage(usage, &usagemask);
+ if (name) {
+ mn = malloc(sizeof(struct _gss_mechanism_name));
+ if (!mn) {
+ mc->gmc_mech->gm_release_name(minor_status,
+ &mc_name);
+ continue;
+ }
+ mn->gmn_mech = mc->gmc_mech;
+ mn->gmn_mech_oid = mc->gmc_mech_oid;
+ mn->gmn_name = mc_name;
+ SLIST_INSERT_HEAD(&name->gn_mn, mn, gmn_link);
+ } else {
+ mc->gmc_mech->gm_release_name(minor_status,
+ &mc_name);
+ }
+
+ if (mc_lifetime < min_lifetime)
+ min_lifetime = mc_lifetime;
+
+ if (mechanisms)
+ gss_add_oid_set_member(minor_status,
+ mc->gmc_mech_oid, mechanisms);
+ found++;
+ }
+ } else {
+ SLIST_FOREACH(m, &_gss_mechs, gm_link) {
+ gss_name_t mc_name;
+ OM_uint32 mc_lifetime;
+
+ major_status = m->gm_mech.gm_inquire_cred(minor_status,
+ GSS_C_NO_CREDENTIAL, &mc_name, &mc_lifetime,
+ &usage, NULL);
+ if (major_status)
+ continue;
+
+ updateusage(usage, &usagemask);
+ if (name && mc_name) {
+ mn = malloc(
+ sizeof(struct _gss_mechanism_name));
+ if (!mn) {
+ m->gm_mech.gm_release_name(
+ minor_status, &mc_name);
+ continue;
+ }
+ mn->gmn_mech = &m->gm_mech;
+ mn->gmn_mech_oid = &m->gm_mech_oid;
+ mn->gmn_name = mc_name;
+ SLIST_INSERT_HEAD(&name->gn_mn, mn, gmn_link);
+ } else if (mc_name) {
+ m->gm_mech.gm_release_name(minor_status,
+ &mc_name);
+ }
+
+ if (mc_lifetime < min_lifetime)
+ min_lifetime = mc_lifetime;
+
+ if (mechanisms)
+ gss_add_oid_set_member(minor_status,
+ &m->gm_mech_oid, mechanisms);
+ found++;
+ }
+ }
+
+ if (found == 0) {
+ gss_name_t n = (gss_name_t)name;
+ if (n)
+ gss_release_name(minor_status, &n);
+ gss_release_oid_set(minor_status, mechanisms);
+ *minor_status = 0;
+ return (GSS_S_NO_CRED);
+ }
+
+ *minor_status = 0;
+ if (name_ret)
+ *name_ret = (gss_name_t) name;
+ if (lifetime)
+ *lifetime = min_lifetime;
+ if (cred_usage) {
+ if ((usagemask & (AUSAGE|IUSAGE)) == (AUSAGE|IUSAGE))
+ *cred_usage = GSS_C_BOTH;
+ else if (usagemask & IUSAGE)
+ *cred_usage = GSS_C_INITIATE;
+ else if (usagemask & AUSAGE)
+ *cred_usage = GSS_C_ACCEPT;
+ }
+ return (GSS_S_COMPLETE);
+}
diff --git a/source4/heimdal/lib/gssapi/mech/gss_inquire_cred_by_mech.c b/source4/heimdal/lib/gssapi/mech/gss_inquire_cred_by_mech.c
new file mode 100644
index 0000000000..47a2eaf279
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gss_inquire_cred_by_mech.c
@@ -0,0 +1,93 @@
+/*-
+ * Copyright (c) 2005 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libgssapi/gss_inquire_cred_by_mech.c,v 1.1 2005/12/29 14:40:20 dfr Exp $
+ */
+
+#include "mech_locl.h"
+RCSID("$Id$");
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_inquire_cred_by_mech(OM_uint32 *minor_status,
+ const gss_cred_id_t cred_handle,
+ const gss_OID mech_type,
+ gss_name_t *cred_name,
+ OM_uint32 *initiator_lifetime,
+ OM_uint32 *acceptor_lifetime,
+ gss_cred_usage_t *cred_usage)
+{
+ OM_uint32 major_status;
+ gssapi_mech_interface m;
+ struct _gss_mechanism_cred *mcp;
+ gss_cred_id_t mc;
+ gss_name_t mn;
+ struct _gss_name *name;
+
+ *minor_status = 0;
+ if (cred_name)
+ *cred_name = GSS_C_NO_NAME;
+ if (initiator_lifetime)
+ *initiator_lifetime = 0;
+ if (acceptor_lifetime)
+ *acceptor_lifetime = 0;
+ if (cred_usage)
+ *cred_usage = 0;
+
+ m = __gss_get_mechanism(mech_type);
+ if (!m)
+ return (GSS_S_NO_CRED);
+
+ if (cred_handle != GSS_C_NO_CREDENTIAL) {
+ struct _gss_cred *cred = (struct _gss_cred *) cred_handle;
+ SLIST_FOREACH(mcp, &cred->gc_mc, gmc_link)
+ if (mcp->gmc_mech == m)
+ break;
+ if (!mcp)
+ return (GSS_S_NO_CRED);
+ mc = mcp->gmc_cred;
+ } else {
+ mc = GSS_C_NO_CREDENTIAL;
+ }
+
+ major_status = m->gm_inquire_cred_by_mech(minor_status, mc, mech_type,
+ &mn, initiator_lifetime, acceptor_lifetime, cred_usage);
+ if (major_status != GSS_S_COMPLETE) {
+ _gss_mg_error(m, major_status, *minor_status);
+ return (major_status);
+ }
+
+ if (cred_name) {
+ name = _gss_make_name(m, mn);
+ if (!name) {
+ m->gm_release_name(minor_status, &mn);
+ return (GSS_S_NO_CRED);
+ }
+ *cred_name = (gss_name_t) name;
+ } else
+ m->gm_release_name(minor_status, &mn);
+
+
+ return (GSS_S_COMPLETE);
+}
diff --git a/source4/heimdal/lib/gssapi/mech/gss_inquire_cred_by_oid.c b/source4/heimdal/lib/gssapi/mech/gss_inquire_cred_by_oid.c
new file mode 100644
index 0000000000..733d919fd9
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gss_inquire_cred_by_oid.c
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2004, PADL Software Pty Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of PADL Software nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "mech_locl.h"
+RCSID("$Id$");
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_inquire_cred_by_oid (OM_uint32 *minor_status,
+ const gss_cred_id_t cred_handle,
+ const gss_OID desired_object,
+ gss_buffer_set_t *data_set)
+{
+ struct _gss_cred *cred = (struct _gss_cred *) cred_handle;
+ OM_uint32 status = GSS_S_COMPLETE;
+ struct _gss_mechanism_cred *mc;
+ gssapi_mech_interface m;
+ gss_buffer_set_t set = GSS_C_NO_BUFFER_SET;
+
+ *minor_status = 0;
+ *data_set = GSS_C_NO_BUFFER_SET;
+
+ if (cred == NULL)
+ return GSS_S_NO_CRED;
+
+ SLIST_FOREACH(mc, &cred->gc_mc, gmc_link) {
+ gss_buffer_set_t rset = GSS_C_NO_BUFFER_SET;
+ int i;
+
+ m = mc->gmc_mech;
+ if (m == NULL) {
+ gss_release_buffer_set(minor_status, &set);
+ *minor_status = 0;
+ return GSS_S_BAD_MECH;
+ }
+
+ if (m->gm_inquire_cred_by_oid == NULL)
+ continue;
+
+ status = m->gm_inquire_cred_by_oid(minor_status,
+ mc->gmc_cred, desired_object, &rset);
+ if (status != GSS_S_COMPLETE)
+ continue;
+
+ for (i = 0; i < rset->count; i++) {
+ status = gss_add_buffer_set_member(minor_status,
+ &rset->elements[i], &set);
+ if (status != GSS_S_COMPLETE)
+ break;
+ }
+ gss_release_buffer_set(minor_status, &rset);
+ }
+ if (set == GSS_C_NO_BUFFER_SET)
+ status = GSS_S_FAILURE;
+ *data_set = set;
+ *minor_status = 0;
+ return status;
+}
+
diff --git a/source4/heimdal/lib/gssapi/mech/gss_inquire_mechs_for_name.c b/source4/heimdal/lib/gssapi/mech/gss_inquire_mechs_for_name.c
new file mode 100644
index 0000000000..e953ccb5da
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gss_inquire_mechs_for_name.c
@@ -0,0 +1,77 @@
+/*-
+ * Copyright (c) 2005 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libgssapi/gss_inquire_mechs_for_name.c,v 1.1 2005/12/29 14:40:20 dfr Exp $
+ */
+
+#include "mech_locl.h"
+RCSID("$Id$");
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_inquire_mechs_for_name(OM_uint32 *minor_status,
+ const gss_name_t input_name,
+ gss_OID_set *mech_types)
+{
+ OM_uint32 major_status;
+ struct _gss_name *name = (struct _gss_name *) input_name;
+ struct _gss_mech_switch *m;
+ gss_OID_set name_types;
+ int present;
+
+ *minor_status = 0;
+
+ _gss_load_mech();
+
+ major_status = gss_create_empty_oid_set(minor_status, mech_types);
+ if (major_status)
+ return (major_status);
+
+ /*
+ * We go through all the loaded mechanisms and see if this
+ * name's type is supported by the mechanism. If it is, add
+ * the mechanism to the set.
+ */
+ SLIST_FOREACH(m, &_gss_mechs, gm_link) {
+ major_status = gss_inquire_names_for_mech(minor_status,
+ &m->gm_mech_oid, &name_types);
+ if (major_status) {
+ gss_release_oid_set(minor_status, mech_types);
+ return (major_status);
+ }
+ gss_test_oid_set_member(minor_status,
+ &name->gn_type, name_types, &present);
+ gss_release_oid_set(minor_status, &name_types);
+ if (present) {
+ major_status = gss_add_oid_set_member(minor_status,
+ &m->gm_mech_oid, mech_types);
+ if (major_status) {
+ gss_release_oid_set(minor_status, mech_types);
+ return (major_status);
+ }
+ }
+ }
+
+ return (GSS_S_COMPLETE);
+}
diff --git a/source4/heimdal/lib/gssapi/mech/gss_inquire_names_for_mech.c b/source4/heimdal/lib/gssapi/mech/gss_inquire_names_for_mech.c
new file mode 100644
index 0000000000..a630d76216
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gss_inquire_names_for_mech.c
@@ -0,0 +1,74 @@
+/*-
+ * Copyright (c) 2005 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libgssapi/gss_inquire_names_for_mech.c,v 1.1 2005/12/29 14:40:20 dfr Exp $
+ */
+
+#include "mech_locl.h"
+RCSID("$Id$");
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_inquire_names_for_mech(OM_uint32 *minor_status,
+ const gss_OID mechanism,
+ gss_OID_set *name_types)
+{
+ OM_uint32 major_status;
+ gssapi_mech_interface m = __gss_get_mechanism(mechanism);
+
+ *minor_status = 0;
+ *name_types = GSS_C_NO_OID_SET;
+ if (!m)
+ return (GSS_S_BAD_MECH);
+
+ /*
+ * If the implementation can do it, ask it for a list of
+ * names, otherwise fake it.
+ */
+ if (m->gm_inquire_names_for_mech) {
+ return (m->gm_inquire_names_for_mech(minor_status,
+ mechanism, name_types));
+ } else {
+ major_status = gss_create_empty_oid_set(minor_status,
+ name_types);
+ if (major_status)
+ return (major_status);
+ major_status = gss_add_oid_set_member(minor_status,
+ GSS_C_NT_HOSTBASED_SERVICE, name_types);
+ if (major_status) {
+ OM_uint32 junk;
+ gss_release_oid_set(&junk, name_types);
+ return (major_status);
+ }
+ major_status = gss_add_oid_set_member(minor_status,
+ GSS_C_NT_USER_NAME, name_types);
+ if (major_status) {
+ OM_uint32 junk;
+ gss_release_oid_set(&junk, name_types);
+ return (major_status);
+ }
+ }
+
+ return (GSS_S_COMPLETE);
+}
diff --git a/source4/heimdal/lib/gssapi/mech/gss_inquire_sec_context_by_oid.c b/source4/heimdal/lib/gssapi/mech/gss_inquire_sec_context_by_oid.c
new file mode 100644
index 0000000000..9ba892dc0e
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gss_inquire_sec_context_by_oid.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2004, PADL Software Pty Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of PADL Software nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "mech_locl.h"
+RCSID("$Id$");
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_inquire_sec_context_by_oid (OM_uint32 *minor_status,
+ const gss_ctx_id_t context_handle,
+ const gss_OID desired_object,
+ gss_buffer_set_t *data_set)
+{
+ struct _gss_context *ctx = (struct _gss_context *) context_handle;
+ OM_uint32 major_status;
+ gssapi_mech_interface m;
+
+ *minor_status = 0;
+ *data_set = GSS_C_NO_BUFFER_SET;
+ if (ctx == NULL)
+ return GSS_S_NO_CONTEXT;
+
+ /*
+ * select the approprate underlying mechanism routine and
+ * call it.
+ */
+
+ m = ctx->gc_mech;
+
+ if (m == NULL)
+ return GSS_S_BAD_MECH;
+
+ if (m->gm_inquire_sec_context_by_oid != NULL) {
+ major_status = m->gm_inquire_sec_context_by_oid(minor_status,
+ ctx->gc_ctx, desired_object, data_set);
+ if (major_status != GSS_S_COMPLETE)
+ _gss_mg_error(m, major_status, *minor_status);
+ } else
+ major_status = GSS_S_BAD_MECH;
+
+ return major_status;
+}
+
diff --git a/source4/heimdal/lib/gssapi/mech/gss_krb5.c b/source4/heimdal/lib/gssapi/mech/gss_krb5.c
new file mode 100644
index 0000000000..5318f6cdba
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gss_krb5.c
@@ -0,0 +1,944 @@
+/*-
+ * Copyright (c) 2005 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libgssapi/gss_krb5.c,v 1.1 2005/12/29 14:40:20 dfr Exp $
+ */
+
+#include "mech_locl.h"
+RCSID("$Id$");
+
+#include <krb5.h>
+#include <roken.h>
+
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_krb5_copy_ccache(OM_uint32 *minor_status,
+ gss_cred_id_t cred,
+ krb5_ccache out)
+{
+ gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
+ krb5_context context;
+ krb5_error_code kret;
+ krb5_ccache id;
+ OM_uint32 ret;
+ char *str;
+
+ ret = gss_inquire_cred_by_oid(minor_status,
+ cred,
+ GSS_KRB5_COPY_CCACHE_X,
+ &data_set);
+ if (ret)
+ return ret;
+
+ if (data_set == GSS_C_NO_BUFFER_SET || data_set->count < 1) {
+ gss_release_buffer_set(minor_status, &data_set);
+ *minor_status = EINVAL;
+ return GSS_S_FAILURE;
+ }
+
+ kret = krb5_init_context(&context);
+ if (kret) {
+ *minor_status = kret;
+ gss_release_buffer_set(minor_status, &data_set);
+ return GSS_S_FAILURE;
+ }
+
+ kret = asprintf(&str, "%.*s", (int)data_set->elements[0].length,
+ (char *)data_set->elements[0].value);
+ gss_release_buffer_set(minor_status, &data_set);
+ if (kret == -1) {
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+
+ kret = krb5_cc_resolve(context, str, &id);
+ free(str);
+ if (kret) {
+ *minor_status = kret;
+ return GSS_S_FAILURE;
+ }
+
+ kret = krb5_cc_copy_cache(context, id, out);
+ krb5_cc_close(context, id);
+ krb5_free_context(context);
+ if (kret) {
+ *minor_status = kret;
+ return GSS_S_FAILURE;
+ }
+
+ return ret;
+}
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_krb5_import_cred(OM_uint32 *minor_status,
+ krb5_ccache id,
+ krb5_principal keytab_principal,
+ krb5_keytab keytab,
+ gss_cred_id_t *cred)
+{
+ gss_buffer_desc buffer;
+ OM_uint32 major_status;
+ krb5_context context;
+ krb5_error_code ret;
+ krb5_storage *sp;
+ krb5_data data;
+ char *str;
+
+ *cred = GSS_C_NO_CREDENTIAL;
+
+ ret = krb5_init_context(&context);
+ if (ret) {
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+
+ sp = krb5_storage_emem();
+ if (sp == NULL) {
+ *minor_status = ENOMEM;
+ major_status = GSS_S_FAILURE;
+ goto out;
+ }
+
+ if (id) {
+ ret = krb5_cc_get_full_name(context, id, &str);
+ if (ret == 0) {
+ ret = krb5_store_string(sp, str);
+ free(str);
+ }
+ } else
+ ret = krb5_store_string(sp, "");
+ if (ret) {
+ *minor_status = ret;
+ major_status = GSS_S_FAILURE;
+ goto out;
+ }
+
+ if (keytab_principal) {
+ ret = krb5_unparse_name(context, keytab_principal, &str);
+ if (ret == 0) {
+ ret = krb5_store_string(sp, str);
+ free(str);
+ }
+ } else
+ krb5_store_string(sp, "");
+ if (ret) {
+ *minor_status = ret;
+ major_status = GSS_S_FAILURE;
+ goto out;
+ }
+
+
+ if (keytab) {
+ ret = krb5_kt_get_full_name(context, keytab, &str);
+ if (ret == 0) {
+ ret = krb5_store_string(sp, str);
+ free(str);
+ }
+ } else
+ krb5_store_string(sp, "");
+ if (ret) {
+ *minor_status = ret;
+ major_status = GSS_S_FAILURE;
+ goto out;
+ }
+
+ ret = krb5_storage_to_data(sp, &data);
+ if (ret) {
+ *minor_status = ret;
+ major_status = GSS_S_FAILURE;
+ goto out;
+ }
+
+ buffer.value = data.data;
+ buffer.length = data.length;
+
+ major_status = gss_set_cred_option(minor_status,
+ cred,
+ GSS_KRB5_IMPORT_CRED_X,
+ &buffer);
+ krb5_data_free(&data);
+out:
+ if (sp)
+ krb5_storage_free(sp);
+ krb5_free_context(context);
+ return major_status;
+}
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gsskrb5_register_acceptor_identity(const char *identity)
+{
+ struct _gss_mech_switch *m;
+ gss_buffer_desc buffer;
+ OM_uint32 junk;
+
+ _gss_load_mech();
+
+ buffer.value = rk_UNCONST(identity);
+ buffer.length = strlen(identity);
+
+ SLIST_FOREACH(m, &_gss_mechs, gm_link) {
+ if (m->gm_mech.gm_set_sec_context_option == NULL)
+ continue;
+ m->gm_mech.gm_set_sec_context_option(&junk, NULL,
+ GSS_KRB5_REGISTER_ACCEPTOR_IDENTITY_X, &buffer);
+ }
+
+ return (GSS_S_COMPLETE);
+}
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+krb5_gss_register_acceptor_identity(const char *identity)
+{
+ return gsskrb5_register_acceptor_identity(identity);
+}
+
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gsskrb5_set_dns_canonicalize(int flag)
+{
+ struct _gss_mech_switch *m;
+ gss_buffer_desc buffer;
+ OM_uint32 junk;
+ char b = (flag != 0);
+
+ _gss_load_mech();
+
+ buffer.value = &b;
+ buffer.length = sizeof(b);
+
+ SLIST_FOREACH(m, &_gss_mechs, gm_link) {
+ if (m->gm_mech.gm_set_sec_context_option == NULL)
+ continue;
+ m->gm_mech.gm_set_sec_context_option(&junk, NULL,
+ GSS_KRB5_SET_DNS_CANONICALIZE_X, &buffer);
+ }
+
+ return (GSS_S_COMPLETE);
+}
+
+
+
+static krb5_error_code
+set_key(krb5_keyblock *keyblock, gss_krb5_lucid_key_t *key)
+{
+ key->type = keyblock->keytype;
+ key->length = keyblock->keyvalue.length;
+ key->data = malloc(key->length);
+ if (key->data == NULL && key->length != 0)
+ return ENOMEM;
+ memcpy(key->data, keyblock->keyvalue.data, key->length);
+ return 0;
+}
+
+static void
+free_key(gss_krb5_lucid_key_t *key)
+{
+ memset(key->data, 0, key->length);
+ free(key->data);
+ memset(key, 0, sizeof(*key));
+}
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_krb5_export_lucid_sec_context(OM_uint32 *minor_status,
+ gss_ctx_id_t *context_handle,
+ OM_uint32 version,
+ void **rctx)
+{
+ krb5_context context = NULL;
+ krb5_error_code ret;
+ gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
+ OM_uint32 major_status;
+ gss_krb5_lucid_context_v1_t *ctx = NULL;
+ krb5_storage *sp = NULL;
+ uint32_t num;
+
+ if (context_handle == NULL
+ || *context_handle == GSS_C_NO_CONTEXT
+ || version != 1)
+ {
+ ret = EINVAL;
+ return GSS_S_FAILURE;
+ }
+
+ major_status =
+ gss_inquire_sec_context_by_oid (minor_status,
+ *context_handle,
+ GSS_KRB5_EXPORT_LUCID_CONTEXT_V1_X,
+ &data_set);
+ if (major_status)
+ return major_status;
+
+ if (data_set == GSS_C_NO_BUFFER_SET || data_set->count != 1) {
+ gss_release_buffer_set(minor_status, &data_set);
+ *minor_status = EINVAL;
+ return GSS_S_FAILURE;
+ }
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ goto out;
+
+ ctx = calloc(1, sizeof(*ctx));
+ if (ctx == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+
+ sp = krb5_storage_from_mem(data_set->elements[0].value,
+ data_set->elements[0].length);
+ if (sp == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+
+ ret = krb5_ret_uint32(sp, &num);
+ if (ret) goto out;
+ if (num != 1) {
+ ret = EINVAL;
+ goto out;
+ }
+ ctx->version = 1;
+ /* initiator */
+ ret = krb5_ret_uint32(sp, &ctx->initiate);
+ if (ret) goto out;
+ /* endtime */
+ ret = krb5_ret_uint32(sp, &ctx->endtime);
+ if (ret) goto out;
+ /* send_seq */
+ ret = krb5_ret_uint32(sp, &num);
+ if (ret) goto out;
+ ctx->send_seq = ((uint64_t)num) << 32;
+ ret = krb5_ret_uint32(sp, &num);
+ if (ret) goto out;
+ ctx->send_seq |= num;
+ /* recv_seq */
+ ret = krb5_ret_uint32(sp, &num);
+ if (ret) goto out;
+ ctx->recv_seq = ((uint64_t)num) << 32;
+ ret = krb5_ret_uint32(sp, &num);
+ if (ret) goto out;
+ ctx->recv_seq |= num;
+ /* protocol */
+ ret = krb5_ret_uint32(sp, &ctx->protocol);
+ if (ret) goto out;
+ if (ctx->protocol == 0) {
+ krb5_keyblock key;
+
+ /* sign_alg */
+ ret = krb5_ret_uint32(sp, &ctx->rfc1964_kd.sign_alg);
+ if (ret) goto out;
+ /* seal_alg */
+ ret = krb5_ret_uint32(sp, &ctx->rfc1964_kd.seal_alg);
+ if (ret) goto out;
+ /* ctx_key */
+ ret = krb5_ret_keyblock(sp, &key);
+ if (ret) goto out;
+ ret = set_key(&key, &ctx->rfc1964_kd.ctx_key);
+ krb5_free_keyblock_contents(context, &key);
+ if (ret) goto out;
+ } else if (ctx->protocol == 1) {
+ krb5_keyblock key;
+
+ /* acceptor_subkey */
+ ret = krb5_ret_uint32(sp, &ctx->cfx_kd.have_acceptor_subkey);
+ if (ret) goto out;
+ /* ctx_key */
+ ret = krb5_ret_keyblock(sp, &key);
+ if (ret) goto out;
+ ret = set_key(&key, &ctx->cfx_kd.ctx_key);
+ krb5_free_keyblock_contents(context, &key);
+ if (ret) goto out;
+ /* acceptor_subkey */
+ if (ctx->cfx_kd.have_acceptor_subkey) {
+ ret = krb5_ret_keyblock(sp, &key);
+ if (ret) goto out;
+ ret = set_key(&key, &ctx->cfx_kd.acceptor_subkey);
+ krb5_free_keyblock_contents(context, &key);
+ if (ret) goto out;
+ }
+ } else {
+ ret = EINVAL;
+ goto out;
+ }
+
+ *rctx = ctx;
+
+out:
+ gss_release_buffer_set(minor_status, &data_set);
+ if (sp)
+ krb5_storage_free(sp);
+ if (context)
+ krb5_free_context(context);
+
+ if (ret) {
+ if (ctx)
+ gss_krb5_free_lucid_sec_context(NULL, ctx);
+
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+ *minor_status = 0;
+ return GSS_S_COMPLETE;
+}
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_krb5_free_lucid_sec_context(OM_uint32 *minor_status, void *c)
+{
+ gss_krb5_lucid_context_v1_t *ctx = c;
+
+ if (ctx->version != 1) {
+ if (minor_status)
+ *minor_status = 0;
+ return GSS_S_FAILURE;
+ }
+
+ if (ctx->protocol == 0) {
+ free_key(&ctx->rfc1964_kd.ctx_key);
+ } else if (ctx->protocol == 1) {
+ free_key(&ctx->cfx_kd.ctx_key);
+ if (ctx->cfx_kd.have_acceptor_subkey)
+ free_key(&ctx->cfx_kd.acceptor_subkey);
+ }
+ free(ctx);
+ if (minor_status)
+ *minor_status = 0;
+ return GSS_S_COMPLETE;
+}
+
+/*
+ *
+ */
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_krb5_set_allowable_enctypes(OM_uint32 *minor_status,
+ gss_cred_id_t cred,
+ OM_uint32 num_enctypes,
+ int32_t *enctypes)
+{
+ krb5_error_code ret;
+ OM_uint32 maj_status;
+ gss_buffer_desc buffer;
+ krb5_storage *sp;
+ krb5_data data;
+ int i;
+
+ sp = krb5_storage_emem();
+ if (sp == NULL) {
+ *minor_status = ENOMEM;
+ maj_status = GSS_S_FAILURE;
+ goto out;
+ }
+
+ for (i = 0; i < num_enctypes; i++) {
+ ret = krb5_store_int32(sp, enctypes[i]);
+ if (ret) {
+ *minor_status = ret;
+ maj_status = GSS_S_FAILURE;
+ goto out;
+ }
+ }
+
+ ret = krb5_storage_to_data(sp, &data);
+ if (ret) {
+ *minor_status = ret;
+ maj_status = GSS_S_FAILURE;
+ goto out;
+ }
+
+ buffer.value = data.data;
+ buffer.length = data.length;
+
+ maj_status = gss_set_cred_option(minor_status,
+ &cred,
+ GSS_KRB5_SET_ALLOWABLE_ENCTYPES_X,
+ &buffer);
+ krb5_data_free(&data);
+out:
+ if (sp)
+ krb5_storage_free(sp);
+ return maj_status;
+}
+
+/*
+ *
+ */
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gsskrb5_set_send_to_kdc(struct gsskrb5_send_to_kdc *c)
+{
+ struct _gss_mech_switch *m;
+ gss_buffer_desc buffer;
+ OM_uint32 junk;
+
+ _gss_load_mech();
+
+ if (c) {
+ buffer.value = c;
+ buffer.length = sizeof(*c);
+ } else {
+ buffer.value = NULL;
+ buffer.length = 0;
+ }
+
+ SLIST_FOREACH(m, &_gss_mechs, gm_link) {
+ if (m->gm_mech.gm_set_sec_context_option == NULL)
+ continue;
+ m->gm_mech.gm_set_sec_context_option(&junk, NULL,
+ GSS_KRB5_SEND_TO_KDC_X, &buffer);
+ }
+
+ return (GSS_S_COMPLETE);
+}
+
+/*
+ *
+ */
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_krb5_ccache_name(OM_uint32 *minor_status,
+ const char *name,
+ const char **out_name)
+{
+ struct _gss_mech_switch *m;
+ gss_buffer_desc buffer;
+ OM_uint32 junk;
+
+ _gss_load_mech();
+
+ if (out_name)
+ *out_name = NULL;
+
+ buffer.value = rk_UNCONST(name);
+ buffer.length = strlen(name);
+
+ SLIST_FOREACH(m, &_gss_mechs, gm_link) {
+ if (m->gm_mech.gm_set_sec_context_option == NULL)
+ continue;
+ m->gm_mech.gm_set_sec_context_option(&junk, NULL,
+ GSS_KRB5_CCACHE_NAME_X, &buffer);
+ }
+
+ return (GSS_S_COMPLETE);
+}
+
+
+/*
+ *
+ */
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gsskrb5_extract_authtime_from_sec_context(OM_uint32 *minor_status,
+ gss_ctx_id_t context_handle,
+ time_t *authtime)
+{
+ gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
+ OM_uint32 maj_stat;
+
+ if (context_handle == GSS_C_NO_CONTEXT) {
+ *minor_status = EINVAL;
+ return GSS_S_FAILURE;
+ }
+
+ maj_stat =
+ gss_inquire_sec_context_by_oid (minor_status,
+ context_handle,
+ GSS_KRB5_GET_AUTHTIME_X,
+ &data_set);
+ if (maj_stat)
+ return maj_stat;
+
+ if (data_set == GSS_C_NO_BUFFER_SET) {
+ gss_release_buffer_set(minor_status, &data_set);
+ *minor_status = EINVAL;
+ return GSS_S_FAILURE;
+ }
+
+ if (data_set->count != 1) {
+ gss_release_buffer_set(minor_status, &data_set);
+ *minor_status = EINVAL;
+ return GSS_S_FAILURE;
+ }
+
+ if (data_set->elements[0].length != 4) {
+ gss_release_buffer_set(minor_status, &data_set);
+ *minor_status = EINVAL;
+ return GSS_S_FAILURE;
+ }
+
+ {
+ unsigned char *buf = data_set->elements[0].value;
+ *authtime = (buf[3] <<24) | (buf[2] << 16) |
+ (buf[1] << 8) | (buf[0] << 0);
+ }
+
+ gss_release_buffer_set(minor_status, &data_set);
+
+ *minor_status = 0;
+ return GSS_S_COMPLETE;
+}
+
+/*
+ *
+ */
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gsskrb5_extract_authz_data_from_sec_context(OM_uint32 *minor_status,
+ gss_ctx_id_t context_handle,
+ int ad_type,
+ gss_buffer_t ad_data)
+{
+ gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
+ OM_uint32 maj_stat;
+ gss_OID_desc oid_flat;
+ heim_oid baseoid, oid;
+ size_t size;
+
+ if (context_handle == GSS_C_NO_CONTEXT) {
+ *minor_status = EINVAL;
+ return GSS_S_FAILURE;
+ }
+
+ /* All this to append an integer to an oid... */
+
+ if (der_get_oid(GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_X->elements,
+ GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_X->length,
+ &baseoid, NULL) != 0) {
+ *minor_status = EINVAL;
+ return GSS_S_FAILURE;
+ }
+
+ oid.length = baseoid.length + 1;
+ oid.components = calloc(oid.length, sizeof(*oid.components));
+ if (oid.components == NULL) {
+ der_free_oid(&baseoid);
+
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+
+ memcpy(oid.components, baseoid.components,
+ baseoid.length * sizeof(*baseoid.components));
+
+ der_free_oid(&baseoid);
+
+ oid.components[oid.length - 1] = ad_type;
+
+ oid_flat.length = der_length_oid(&oid);
+ oid_flat.elements = malloc(oid_flat.length);
+ if (oid_flat.elements == NULL) {
+ free(oid.components);
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+
+ if (der_put_oid((unsigned char *)oid_flat.elements + oid_flat.length - 1,
+ oid_flat.length, &oid, &size) != 0) {
+ free(oid.components);
+ free(oid_flat.elements);
+ *minor_status = EINVAL;
+ return GSS_S_FAILURE;
+ }
+ if (oid_flat.length != size)
+ abort();
+
+ free(oid.components);
+
+ /* FINALLY, we have the OID */
+
+ maj_stat = gss_inquire_sec_context_by_oid (minor_status,
+ context_handle,
+ &oid_flat,
+ &data_set);
+
+ free(oid_flat.elements);
+
+ if (maj_stat)
+ return maj_stat;
+
+ if (data_set == GSS_C_NO_BUFFER_SET || data_set->count != 1) {
+ gss_release_buffer_set(minor_status, &data_set);
+ *minor_status = EINVAL;
+ return GSS_S_FAILURE;
+ }
+
+ ad_data->value = malloc(data_set->elements[0].length);
+ if (ad_data->value == NULL) {
+ gss_release_buffer_set(minor_status, &data_set);
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+
+ ad_data->length = data_set->elements[0].length;
+ memcpy(ad_data->value, data_set->elements[0].value, ad_data->length);
+ gss_release_buffer_set(minor_status, &data_set);
+
+ *minor_status = 0;
+ return GSS_S_COMPLETE;
+}
+
+/*
+ *
+ */
+
+static OM_uint32
+gsskrb5_extract_key(OM_uint32 *minor_status,
+ gss_ctx_id_t context_handle,
+ const gss_OID oid,
+ krb5_keyblock **keyblock)
+{
+ krb5_error_code ret;
+ gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
+ OM_uint32 major_status;
+ krb5_context context = NULL;
+ krb5_storage *sp = NULL;
+
+ if (context_handle == GSS_C_NO_CONTEXT) {
+ ret = EINVAL;
+ return GSS_S_FAILURE;
+ }
+
+ ret = krb5_init_context(&context);
+ if(ret) {
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+
+ major_status =
+ gss_inquire_sec_context_by_oid (minor_status,
+ context_handle,
+ oid,
+ &data_set);
+ if (major_status)
+ return major_status;
+
+ if (data_set == GSS_C_NO_BUFFER_SET || data_set->count != 1) {
+ gss_release_buffer_set(minor_status, &data_set);
+ *minor_status = EINVAL;
+ return GSS_S_FAILURE;
+ }
+
+ sp = krb5_storage_from_mem(data_set->elements[0].value,
+ data_set->elements[0].length);
+ if (sp == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+
+ *keyblock = calloc(1, sizeof(**keyblock));
+ if (keyblock == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+
+ ret = krb5_ret_keyblock(sp, *keyblock);
+
+out:
+ gss_release_buffer_set(minor_status, &data_set);
+ if (sp)
+ krb5_storage_free(sp);
+ if (ret && keyblock) {
+ krb5_free_keyblock(context, *keyblock);
+ *keyblock = NULL;
+ }
+ if (context)
+ krb5_free_context(context);
+
+ *minor_status = ret;
+ if (ret)
+ return GSS_S_FAILURE;
+
+ return GSS_S_COMPLETE;
+}
+
+/*
+ *
+ */
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gsskrb5_extract_service_keyblock(OM_uint32 *minor_status,
+ gss_ctx_id_t context_handle,
+ krb5_keyblock **keyblock)
+{
+ return gsskrb5_extract_key(minor_status,
+ context_handle,
+ GSS_KRB5_GET_SERVICE_KEYBLOCK_X,
+ keyblock);
+}
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gsskrb5_get_initiator_subkey(OM_uint32 *minor_status,
+ gss_ctx_id_t context_handle,
+ krb5_keyblock **keyblock)
+{
+ return gsskrb5_extract_key(minor_status,
+ context_handle,
+ GSS_KRB5_GET_INITIATOR_SUBKEY_X,
+ keyblock);
+}
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gsskrb5_get_subkey(OM_uint32 *minor_status,
+ gss_ctx_id_t context_handle,
+ krb5_keyblock **keyblock)
+{
+ return gsskrb5_extract_key(minor_status,
+ context_handle,
+ GSS_KRB5_GET_SUBKEY_X,
+ keyblock);
+}
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gsskrb5_set_default_realm(const char *realm)
+{
+ struct _gss_mech_switch *m;
+ gss_buffer_desc buffer;
+ OM_uint32 junk;
+
+ _gss_load_mech();
+
+ buffer.value = rk_UNCONST(realm);
+ buffer.length = strlen(realm);
+
+ SLIST_FOREACH(m, &_gss_mechs, gm_link) {
+ if (m->gm_mech.gm_set_sec_context_option == NULL)
+ continue;
+ m->gm_mech.gm_set_sec_context_option(&junk, NULL,
+ GSS_KRB5_SET_DEFAULT_REALM_X, &buffer);
+ }
+
+ return (GSS_S_COMPLETE);
+}
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_krb5_get_tkt_flags(OM_uint32 *minor_status,
+ gss_ctx_id_t context_handle,
+ OM_uint32 *tkt_flags)
+{
+
+ OM_uint32 major_status;
+ gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
+
+ if (context_handle == GSS_C_NO_CONTEXT) {
+ *minor_status = EINVAL;
+ return GSS_S_FAILURE;
+ }
+
+ major_status =
+ gss_inquire_sec_context_by_oid (minor_status,
+ context_handle,
+ GSS_KRB5_GET_TKT_FLAGS_X,
+ &data_set);
+ if (major_status)
+ return major_status;
+
+ if (data_set == GSS_C_NO_BUFFER_SET ||
+ data_set->count != 1 ||
+ data_set->elements[0].length < 4) {
+ gss_release_buffer_set(minor_status, &data_set);
+ *minor_status = EINVAL;
+ return GSS_S_FAILURE;
+ }
+
+ {
+ const u_char *p = data_set->elements[0].value;
+ *tkt_flags = (p[0] << 0) | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
+ }
+
+ gss_release_buffer_set(minor_status, &data_set);
+ return GSS_S_COMPLETE;
+}
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gsskrb5_set_time_offset(int offset)
+{
+ struct _gss_mech_switch *m;
+ gss_buffer_desc buffer;
+ OM_uint32 junk;
+ int32_t o = offset;
+
+ _gss_load_mech();
+
+ buffer.value = &o;
+ buffer.length = sizeof(o);
+
+ SLIST_FOREACH(m, &_gss_mechs, gm_link) {
+ if (m->gm_mech.gm_set_sec_context_option == NULL)
+ continue;
+ m->gm_mech.gm_set_sec_context_option(&junk, NULL,
+ GSS_KRB5_SET_TIME_OFFSET_X, &buffer);
+ }
+
+ return (GSS_S_COMPLETE);
+}
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gsskrb5_get_time_offset(int *offset)
+{
+ struct _gss_mech_switch *m;
+ gss_buffer_desc buffer;
+ OM_uint32 maj_stat, junk;
+ int32_t o;
+
+ _gss_load_mech();
+
+ buffer.value = &o;
+ buffer.length = sizeof(o);
+
+ SLIST_FOREACH(m, &_gss_mechs, gm_link) {
+ if (m->gm_mech.gm_set_sec_context_option == NULL)
+ continue;
+ maj_stat = m->gm_mech.gm_set_sec_context_option(&junk, NULL,
+ GSS_KRB5_GET_TIME_OFFSET_X, &buffer);
+
+ if (maj_stat == GSS_S_COMPLETE) {
+ *offset = o;
+ return maj_stat;
+ }
+ }
+
+ return (GSS_S_UNAVAILABLE);
+}
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gsskrb5_plugin_register(struct gsskrb5_krb5_plugin *c)
+{
+ struct _gss_mech_switch *m;
+ gss_buffer_desc buffer;
+ OM_uint32 junk;
+
+ _gss_load_mech();
+
+ buffer.value = c;
+ buffer.length = sizeof(*c);
+
+ SLIST_FOREACH(m, &_gss_mechs, gm_link) {
+ if (m->gm_mech.gm_set_sec_context_option == NULL)
+ continue;
+ m->gm_mech.gm_set_sec_context_option(&junk, NULL,
+ GSS_KRB5_PLUGIN_REGISTER_X, &buffer);
+ }
+
+ return (GSS_S_COMPLETE);
+}
diff --git a/source4/heimdal/lib/gssapi/mech/gss_mech_switch.c b/source4/heimdal/lib/gssapi/mech/gss_mech_switch.c
new file mode 100644
index 0000000000..fc2e8816c5
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gss_mech_switch.c
@@ -0,0 +1,337 @@
+/*-
+ * Copyright (c) 2005 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libgssapi/gss_mech_switch.c,v 1.2 2006/02/04 09:40:21 dfr Exp $
+ */
+
+#include "mech_locl.h"
+#include <heim_threads.h>
+RCSID("$Id$");
+
+#ifndef _PATH_GSS_MECH
+#define _PATH_GSS_MECH "/etc/gss/mech"
+#endif
+
+struct _gss_mech_switch_list _gss_mechs = { NULL } ;
+gss_OID_set _gss_mech_oids;
+static HEIMDAL_MUTEX _gss_mech_mutex = HEIMDAL_MUTEX_INITIALIZER;
+
+/*
+ * Convert a string containing an OID in 'dot' form
+ * (e.g. 1.2.840.113554.1.2.2) to a gss_OID.
+ */
+static int
+_gss_string_to_oid(const char* s, gss_OID oid)
+{
+ int number_count, i, j;
+ size_t byte_count;
+ const char *p, *q;
+ char *res;
+
+ oid->length = 0;
+ oid->elements = NULL;
+
+ /*
+ * First figure out how many numbers in the oid, then
+ * calculate the compiled oid size.
+ */
+ number_count = 0;
+ for (p = s; p; p = q) {
+ q = strchr(p, '.');
+ if (q) q = q + 1;
+ number_count++;
+ }
+
+ /*
+ * The first two numbers are in the first byte and each
+ * subsequent number is encoded in a variable byte sequence.
+ */
+ if (number_count < 2)
+ return (EINVAL);
+
+ /*
+ * We do this in two passes. The first pass, we just figure
+ * out the size. Second time around, we actually encode the
+ * number.
+ */
+ res = 0;
+ for (i = 0; i < 2; i++) {
+ byte_count = 0;
+ for (p = s, j = 0; p; p = q, j++) {
+ unsigned int number = 0;
+
+ /*
+ * Find the end of this number.
+ */
+ q = strchr(p, '.');
+ if (q) q = q + 1;
+
+ /*
+ * Read the number of of the string. Don't
+ * bother with anything except base ten.
+ */
+ while (*p && *p != '.') {
+ number = 10 * number + (*p - '0');
+ p++;
+ }
+
+ /*
+ * Encode the number. The first two numbers
+ * are packed into the first byte. Subsequent
+ * numbers are encoded in bytes seven bits at
+ * a time with the last byte having the high
+ * bit set.
+ */
+ if (j == 0) {
+ if (res)
+ *res = number * 40;
+ } else if (j == 1) {
+ if (res) {
+ *res += number;
+ res++;
+ }
+ byte_count++;
+ } else if (j >= 2) {
+ /*
+ * The number is encoded in seven bit chunks.
+ */
+ unsigned int t;
+ unsigned int bytes;
+
+ bytes = 0;
+ for (t = number; t; t >>= 7)
+ bytes++;
+ if (bytes == 0) bytes = 1;
+ while (bytes) {
+ if (res) {
+ int bit = 7*(bytes-1);
+
+ *res = (number >> bit) & 0x7f;
+ if (bytes != 1)
+ *res |= 0x80;
+ res++;
+ }
+ byte_count++;
+ bytes--;
+ }
+ }
+ }
+ if (!res) {
+ res = malloc(byte_count);
+ if (!res)
+ return (ENOMEM);
+ oid->length = byte_count;
+ oid->elements = res;
+ }
+ }
+
+ return (0);
+}
+
+#define SYM(name) \
+do { \
+ m->gm_mech.gm_ ## name = dlsym(so, "gss_" #name); \
+ if (!m->gm_mech.gm_ ## name) { \
+ fprintf(stderr, "can't find symbol gss_" #name "\n"); \
+ goto bad; \
+ } \
+} while (0)
+
+#define OPTSYM(name) \
+do { \
+ m->gm_mech.gm_ ## name = dlsym(so, "gss_" #name); \
+} while (0)
+
+/*
+ *
+ */
+static int
+add_builtin(gssapi_mech_interface mech)
+{
+ struct _gss_mech_switch *m;
+ OM_uint32 minor_status;
+
+ /* not registering any mech is ok */
+ if (mech == NULL)
+ return 0;
+
+ m = malloc(sizeof(*m));
+ if (m == NULL)
+ return 1;
+ m->gm_so = NULL;
+ m->gm_mech = *mech;
+ m->gm_mech_oid = mech->gm_mech_oid; /* XXX */
+ gss_add_oid_set_member(&minor_status,
+ &m->gm_mech.gm_mech_oid, &_gss_mech_oids);
+
+ SLIST_INSERT_HEAD(&_gss_mechs, m, gm_link);
+ return 0;
+}
+
+/*
+ * Load the mechanisms file (/etc/gss/mech).
+ */
+void
+_gss_load_mech(void)
+{
+ OM_uint32 major_status, minor_status;
+ FILE *fp;
+ char buf[256];
+ char *p;
+ char *name, *oid, *lib, *kobj;
+ struct _gss_mech_switch *m;
+ void *so;
+
+
+ HEIMDAL_MUTEX_lock(&_gss_mech_mutex);
+
+ if (SLIST_FIRST(&_gss_mechs)) {
+ HEIMDAL_MUTEX_unlock(&_gss_mech_mutex);
+ return;
+ }
+
+ major_status = gss_create_empty_oid_set(&minor_status,
+ &_gss_mech_oids);
+ if (major_status) {
+ HEIMDAL_MUTEX_unlock(&_gss_mech_mutex);
+ return;
+ }
+
+ add_builtin(__gss_krb5_initialize());
+ add_builtin(__gss_spnego_initialize());
+#ifndef HEIMDAL_SMALLER
+ add_builtin(__gss_ntlm_initialize());
+#endif
+
+#ifdef HAVE_DLOPEN
+ fp = fopen(_PATH_GSS_MECH, "r");
+ if (!fp) {
+ HEIMDAL_MUTEX_unlock(&_gss_mech_mutex);
+ return;
+ }
+ rk_cloexec_file(fp);
+
+ while (fgets(buf, sizeof(buf), fp)) {
+ if (*buf == '#')
+ continue;
+ p = buf;
+ name = strsep(&p, "\t\n ");
+ if (p) while (isspace((unsigned char)*p)) p++;
+ oid = strsep(&p, "\t\n ");
+ if (p) while (isspace((unsigned char)*p)) p++;
+ lib = strsep(&p, "\t\n ");
+ if (p) while (isspace((unsigned char)*p)) p++;
+ kobj = strsep(&p, "\t\n ");
+ if (!name || !oid || !lib || !kobj)
+ continue;
+
+#ifndef RTLD_LOCAL
+#define RTLD_LOCAL 0
+#endif
+
+ so = dlopen(lib, RTLD_LAZY | RTLD_LOCAL);
+ if (!so) {
+/* fprintf(stderr, "dlopen: %s\n", dlerror()); */
+ continue;
+ }
+
+ m = malloc(sizeof(*m));
+ if (!m)
+ break;
+ m->gm_so = so;
+ if (_gss_string_to_oid(oid, &m->gm_mech.gm_mech_oid)) {
+ free(m);
+ continue;
+ }
+
+ major_status = gss_add_oid_set_member(&minor_status,
+ &m->gm_mech.gm_mech_oid, &_gss_mech_oids);
+ if (major_status) {
+ free(m->gm_mech.gm_mech_oid.elements);
+ free(m);
+ continue;
+ }
+
+ SYM(acquire_cred);
+ SYM(release_cred);
+ SYM(init_sec_context);
+ SYM(accept_sec_context);
+ SYM(process_context_token);
+ SYM(delete_sec_context);
+ SYM(context_time);
+ SYM(get_mic);
+ SYM(verify_mic);
+ SYM(wrap);
+ SYM(unwrap);
+ SYM(display_status);
+ SYM(indicate_mechs);
+ SYM(compare_name);
+ SYM(display_name);
+ SYM(import_name);
+ SYM(export_name);
+ SYM(release_name);
+ SYM(inquire_cred);
+ SYM(inquire_context);
+ SYM(wrap_size_limit);
+ SYM(add_cred);
+ SYM(inquire_cred_by_mech);
+ SYM(export_sec_context);
+ SYM(import_sec_context);
+ SYM(inquire_names_for_mech);
+ SYM(inquire_mechs_for_name);
+ SYM(canonicalize_name);
+ SYM(duplicate_name);
+ OPTSYM(inquire_cred_by_oid);
+ OPTSYM(inquire_sec_context_by_oid);
+ OPTSYM(set_sec_context_option);
+ OPTSYM(set_cred_option);
+ OPTSYM(pseudo_random);
+
+ SLIST_INSERT_HEAD(&_gss_mechs, m, gm_link);
+ continue;
+
+ bad:
+ free(m->gm_mech.gm_mech_oid.elements);
+ free(m);
+ dlclose(so);
+ continue;
+ }
+ fclose(fp);
+#endif
+ HEIMDAL_MUTEX_unlock(&_gss_mech_mutex);
+}
+
+gssapi_mech_interface
+__gss_get_mechanism(gss_OID mech)
+{
+ struct _gss_mech_switch *m;
+
+ _gss_load_mech();
+ SLIST_FOREACH(m, &_gss_mechs, gm_link) {
+ if (gss_oid_equal(&m->gm_mech.gm_mech_oid, mech))
+ return &m->gm_mech;
+ }
+ return NULL;
+}
diff --git a/source4/heimdal/lib/gssapi/mech/gss_names.c b/source4/heimdal/lib/gssapi/mech/gss_names.c
new file mode 100644
index 0000000000..ada53d0bfd
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gss_names.c
@@ -0,0 +1,111 @@
+/*-
+ * Copyright (c) 2005 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libgssapi/gss_names.c,v 1.1 2005/12/29 14:40:20 dfr Exp $
+ */
+
+#include "mech_locl.h"
+RCSID("$Id$");
+
+OM_uint32
+_gss_find_mn(OM_uint32 *minor_status, struct _gss_name *name, gss_OID mech,
+ struct _gss_mechanism_name **output_mn)
+{
+ OM_uint32 major_status;
+ gssapi_mech_interface m;
+ struct _gss_mechanism_name *mn;
+
+ *output_mn = NULL;
+
+ SLIST_FOREACH(mn, &name->gn_mn, gmn_link) {
+ if (gss_oid_equal(mech, mn->gmn_mech_oid))
+ break;
+ }
+
+ if (!mn) {
+ /*
+ * If this name is canonical (i.e. there is only an
+ * MN but it is from a different mech), give up now.
+ */
+ if (!name->gn_value.value)
+ return GSS_S_BAD_NAME;
+
+ m = __gss_get_mechanism(mech);
+ if (!m)
+ return (GSS_S_BAD_MECH);
+
+ mn = malloc(sizeof(struct _gss_mechanism_name));
+ if (!mn)
+ return GSS_S_FAILURE;
+
+ major_status = m->gm_import_name(minor_status,
+ &name->gn_value,
+ (name->gn_type.elements
+ ? &name->gn_type : GSS_C_NO_OID),
+ &mn->gmn_name);
+ if (major_status != GSS_S_COMPLETE) {
+ _gss_mg_error(m, major_status, *minor_status);
+ free(mn);
+ return major_status;
+ }
+
+ mn->gmn_mech = m;
+ mn->gmn_mech_oid = &m->gm_mech_oid;
+ SLIST_INSERT_HEAD(&name->gn_mn, mn, gmn_link);
+ }
+ *output_mn = mn;
+ return 0;
+}
+
+
+/*
+ * Make a name from an MN.
+ */
+struct _gss_name *
+_gss_make_name(gssapi_mech_interface m, gss_name_t new_mn)
+{
+ struct _gss_name *name;
+ struct _gss_mechanism_name *mn;
+
+ name = malloc(sizeof(struct _gss_name));
+ if (!name)
+ return (0);
+ memset(name, 0, sizeof(struct _gss_name));
+
+ mn = malloc(sizeof(struct _gss_mechanism_name));
+ if (!mn) {
+ free(name);
+ return (0);
+ }
+
+ SLIST_INIT(&name->gn_mn);
+ mn->gmn_mech = m;
+ mn->gmn_mech_oid = &m->gm_mech_oid;
+ mn->gmn_name = new_mn;
+ SLIST_INSERT_HEAD(&name->gn_mn, mn, gmn_link);
+
+ return (name);
+}
+
diff --git a/source4/heimdal/lib/gssapi/mech/gss_oid_equal.c b/source4/heimdal/lib/gssapi/mech/gss_oid_equal.c
new file mode 100644
index 0000000000..a99eb09e50
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gss_oid_equal.c
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "mech_locl.h"
+RCSID("$Id$");
+
+int GSSAPI_LIB_FUNCTION
+gss_oid_equal(const gss_OID a, const gss_OID b)
+{
+ if (a == b)
+ return 1;
+ if (a == GSS_C_NO_OID || b == GSS_C_NO_OID || a->length != b->length)
+ return 0;
+ return memcmp(a->elements, b->elements, a->length) == 0;
+}
diff --git a/source4/heimdal/lib/gssapi/mech/gss_oid_to_str.c b/source4/heimdal/lib/gssapi/mech/gss_oid_to_str.c
new file mode 100644
index 0000000000..92d997bed9
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gss_oid_to_str.c
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "mech_locl.h"
+RCSID("$Id$");
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_oid_to_str(OM_uint32 *minor_status, gss_OID oid, gss_buffer_t oid_str)
+{
+ int ret;
+ size_t size;
+ heim_oid o;
+ char *p;
+
+ _mg_buffer_zero(oid_str);
+
+ if (oid == GSS_C_NULL_OID)
+ return GSS_S_FAILURE;
+
+ ret = der_get_oid (oid->elements, oid->length, &o, &size);
+ if (ret) {
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+
+ ret = der_print_heim_oid(&o, ' ', &p);
+ der_free_oid(&o);
+ if (ret) {
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+
+ oid_str->value = p;
+ oid_str->length = strlen(p);
+
+ *minor_status = 0;
+ return GSS_S_COMPLETE;
+}
diff --git a/source4/heimdal/lib/gssapi/mech/gss_process_context_token.c b/source4/heimdal/lib/gssapi/mech/gss_process_context_token.c
new file mode 100644
index 0000000000..9dc3f5b904
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gss_process_context_token.c
@@ -0,0 +1,42 @@
+/*-
+ * Copyright (c) 2005 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libgssapi/gss_process_context_token.c,v 1.1 2005/12/29 14:40:20 dfr Exp $
+ */
+
+#include "mech_locl.h"
+RCSID("$Id$");
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_process_context_token(OM_uint32 *minor_status,
+ const gss_ctx_id_t context_handle,
+ const gss_buffer_t token_buffer)
+{
+ struct _gss_context *ctx = (struct _gss_context *) context_handle;
+ gssapi_mech_interface m = ctx->gc_mech;
+
+ return (m->gm_process_context_token(minor_status, ctx->gc_ctx,
+ token_buffer));
+}
diff --git a/source4/heimdal/lib/gssapi/mech/gss_pseudo_random.c b/source4/heimdal/lib/gssapi/mech/gss_pseudo_random.c
new file mode 100644
index 0000000000..b907f94038
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gss_pseudo_random.c
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#include "mech_locl.h"
+RCSID("$Id$");
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_pseudo_random(OM_uint32 *minor_status,
+ gss_ctx_id_t context,
+ int prf_key,
+ const gss_buffer_t prf_in,
+ ssize_t desired_output_len,
+ gss_buffer_t prf_out)
+{
+ struct _gss_context *ctx = (struct _gss_context *) context;
+ gssapi_mech_interface m = ctx->gc_mech;
+ OM_uint32 major_status;
+
+ _mg_buffer_zero(prf_out);
+ *minor_status = 0;
+
+ if (ctx == NULL) {
+ *minor_status = 0;
+ return GSS_S_NO_CONTEXT;
+ }
+
+ if (m->gm_pseudo_random == NULL)
+ return GSS_S_UNAVAILABLE;
+
+ major_status = (*m->gm_pseudo_random)(minor_status, ctx->gc_ctx,
+ prf_key, prf_in, desired_output_len,
+ prf_out);
+ if (major_status != GSS_S_COMPLETE)
+ _gss_mg_error(m, major_status, *minor_status);
+
+ return major_status;
+}
diff --git a/source4/heimdal/lib/gssapi/mech/gss_release_buffer.c b/source4/heimdal/lib/gssapi/mech/gss_release_buffer.c
new file mode 100644
index 0000000000..1af5289157
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gss_release_buffer.c
@@ -0,0 +1,43 @@
+/*-
+ * Copyright (c) 2005 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libgssapi/gss_release_buffer.c,v 1.1 2005/12/29 14:40:20 dfr Exp $
+ */
+
+#include "mech_locl.h"
+RCSID("$Id$");
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_release_buffer(OM_uint32 *minor_status,
+ gss_buffer_t buffer)
+{
+
+ *minor_status = 0;
+ if (buffer->value)
+ free(buffer->value);
+ _mg_buffer_zero(buffer);
+
+ return (GSS_S_COMPLETE);
+}
diff --git a/source4/heimdal/lib/gssapi/mech/gss_release_cred.c b/source4/heimdal/lib/gssapi/mech/gss_release_cred.c
new file mode 100644
index 0000000000..40777fa2a1
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gss_release_cred.c
@@ -0,0 +1,52 @@
+/*-
+ * Copyright (c) 2005 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libgssapi/gss_release_cred.c,v 1.1 2005/12/29 14:40:20 dfr Exp $
+ */
+
+#include "mech_locl.h"
+RCSID("$Id$");
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_release_cred(OM_uint32 *minor_status, gss_cred_id_t *cred_handle)
+{
+ struct _gss_cred *cred = (struct _gss_cred *) *cred_handle;
+ struct _gss_mechanism_cred *mc;
+
+ if (*cred_handle == GSS_C_NO_CREDENTIAL)
+ return (GSS_S_COMPLETE);
+
+ while (SLIST_FIRST(&cred->gc_mc)) {
+ mc = SLIST_FIRST(&cred->gc_mc);
+ SLIST_REMOVE_HEAD(&cred->gc_mc, gmc_link);
+ mc->gmc_mech->gm_release_cred(minor_status, &mc->gmc_cred);
+ free(mc);
+ }
+ free(cred);
+
+ *minor_status = 0;
+ *cred_handle = GSS_C_NO_CREDENTIAL;
+ return (GSS_S_COMPLETE);
+}
diff --git a/source4/heimdal/lib/gssapi/mech/gss_release_name.c b/source4/heimdal/lib/gssapi/mech/gss_release_name.c
new file mode 100644
index 0000000000..ad07c60bda
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gss_release_name.c
@@ -0,0 +1,55 @@
+/*-
+ * Copyright (c) 2005 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libgssapi/gss_release_name.c,v 1.1 2005/12/29 14:40:20 dfr Exp $
+ */
+
+#include "mech_locl.h"
+RCSID("$Id$");
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_release_name(OM_uint32 *minor_status,
+ gss_name_t *input_name)
+{
+ struct _gss_name *name = (struct _gss_name *) *input_name;
+
+ *minor_status = 0;
+ if (name) {
+ if (name->gn_type.elements)
+ free(name->gn_type.elements);
+ while (SLIST_FIRST(&name->gn_mn)) {
+ struct _gss_mechanism_name *mn;
+ mn = SLIST_FIRST(&name->gn_mn);
+ SLIST_REMOVE_HEAD(&name->gn_mn, gmn_link);
+ mn->gmn_mech->gm_release_name(minor_status,
+ &mn->gmn_name);
+ free(mn);
+ }
+ gss_release_buffer(minor_status, &name->gn_value);
+ free(name);
+ *input_name = GSS_C_NO_NAME;
+ }
+ return (GSS_S_COMPLETE);
+}
diff --git a/source4/heimdal/lib/gssapi/mech/gss_release_oid.c b/source4/heimdal/lib/gssapi/mech/gss_release_oid.c
new file mode 100644
index 0000000000..8d0ea4367c
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gss_release_oid.c
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "mech_locl.h"
+
+RCSID("$Id$");
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_release_oid(OM_uint32 *minor_status, gss_OID *oid)
+{
+ gss_OID o = *oid;
+
+ *oid = GSS_C_NO_OID;
+
+ if (minor_status != NULL)
+ *minor_status = 0;
+
+ if (o == GSS_C_NO_OID)
+ return GSS_S_COMPLETE;
+
+ if (o->elements != NULL) {
+ free(o->elements);
+ o->elements = NULL;
+ }
+ o->length = 0;
+ free(o);
+
+ return GSS_S_COMPLETE;
+}
diff --git a/source4/heimdal/lib/gssapi/mech/gss_release_oid_set.c b/source4/heimdal/lib/gssapi/mech/gss_release_oid_set.c
new file mode 100644
index 0000000000..0ccb9e4dc6
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gss_release_oid_set.c
@@ -0,0 +1,45 @@
+/*-
+ * Copyright (c) 2005 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libgssapi/gss_release_oid_set.c,v 1.1 2005/12/29 14:40:20 dfr Exp $
+ */
+
+#include "mech_locl.h"
+RCSID("$Id$");
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_release_oid_set(OM_uint32 *minor_status,
+ gss_OID_set *set)
+{
+
+ *minor_status = 0;
+ if (set && *set) {
+ if ((*set)->elements)
+ free((*set)->elements);
+ free(*set);
+ *set = GSS_C_NO_OID_SET;
+ }
+ return (GSS_S_COMPLETE);
+}
diff --git a/source4/heimdal/lib/gssapi/mech/gss_seal.c b/source4/heimdal/lib/gssapi/mech/gss_seal.c
new file mode 100644
index 0000000000..f6636456ea
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gss_seal.c
@@ -0,0 +1,46 @@
+/*-
+ * Copyright (c) 2005 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libgssapi/gss_seal.c,v 1.1 2005/12/29 14:40:20 dfr Exp $
+ */
+
+#include "mech_locl.h"
+RCSID("$Id$");
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_seal(OM_uint32 *minor_status,
+ gss_ctx_id_t context_handle,
+ int conf_req_flag,
+ int qop_req,
+ gss_buffer_t input_message_buffer,
+ int *conf_state,
+ gss_buffer_t output_message_buffer)
+{
+
+ return (gss_wrap(minor_status,
+ context_handle, conf_req_flag, qop_req,
+ input_message_buffer, conf_state,
+ output_message_buffer));
+}
diff --git a/source4/heimdal/lib/gssapi/mech/gss_set_cred_option.c b/source4/heimdal/lib/gssapi/mech/gss_set_cred_option.c
new file mode 100644
index 0000000000..20eaa14d9e
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gss_set_cred_option.c
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2004, PADL Software Pty Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of PADL Software nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "mech_locl.h"
+RCSID("$Id$");
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_set_cred_option (OM_uint32 *minor_status,
+ gss_cred_id_t *cred_handle,
+ const gss_OID object,
+ const gss_buffer_t value)
+{
+ struct _gss_cred *cred = (struct _gss_cred *) *cred_handle;
+ OM_uint32 major_status = GSS_S_COMPLETE;
+ struct _gss_mechanism_cred *mc;
+ int one_ok = 0;
+
+ *minor_status = 0;
+
+ _gss_load_mech();
+
+ if (cred == NULL) {
+ struct _gss_mech_switch *m;
+
+ cred = malloc(sizeof(*cred));
+ if (cred == NULL)
+ return GSS_S_FAILURE;
+
+ SLIST_INIT(&cred->gc_mc);
+
+ SLIST_FOREACH(m, &_gss_mechs, gm_link) {
+
+ if (m->gm_mech.gm_set_cred_option == NULL)
+ continue;
+
+ mc = malloc(sizeof(*mc));
+ if (mc == NULL) {
+ *cred_handle = (gss_cred_id_t)cred;
+ gss_release_cred(minor_status, cred_handle);
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+
+ mc->gmc_mech = &m->gm_mech;
+ mc->gmc_mech_oid = &m->gm_mech_oid;
+ mc->gmc_cred = GSS_C_NO_CREDENTIAL;
+
+ major_status = m->gm_mech.gm_set_cred_option(
+ minor_status, &mc->gmc_cred, object, value);
+
+ if (major_status) {
+ free(mc);
+ continue;
+ }
+ one_ok = 1;
+ SLIST_INSERT_HEAD(&cred->gc_mc, mc, gmc_link);
+ }
+ *cred_handle = (gss_cred_id_t)cred;
+ if (!one_ok) {
+ OM_uint32 junk;
+ gss_release_cred(&junk, cred_handle);
+ }
+ } else {
+ gssapi_mech_interface m;
+
+ SLIST_FOREACH(mc, &cred->gc_mc, gmc_link) {
+ m = mc->gmc_mech;
+
+ if (m == NULL)
+ return GSS_S_BAD_MECH;
+
+ if (m->gm_set_cred_option == NULL)
+ continue;
+
+ major_status = m->gm_set_cred_option(minor_status,
+ &mc->gmc_cred, object, value);
+ if (major_status == GSS_S_COMPLETE)
+ one_ok = 1;
+ else
+ _gss_mg_error(m, major_status, *minor_status);
+
+ }
+ }
+ if (one_ok) {
+ *minor_status = 0;
+ return GSS_S_COMPLETE;
+ }
+ return major_status;
+}
+
diff --git a/source4/heimdal/lib/gssapi/mech/gss_set_sec_context_option.c b/source4/heimdal/lib/gssapi/mech/gss_set_sec_context_option.c
new file mode 100644
index 0000000000..735d59322e
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gss_set_sec_context_option.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2004, PADL Software Pty Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of PADL Software nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "mech_locl.h"
+RCSID("$Id$");
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_set_sec_context_option (OM_uint32 *minor_status,
+ gss_ctx_id_t *context_handle,
+ const gss_OID object,
+ const gss_buffer_t value)
+{
+ struct _gss_context *ctx;
+ OM_uint32 major_status;
+ gssapi_mech_interface m;
+
+ *minor_status = 0;
+
+ if (context_handle == NULL)
+ return GSS_S_NO_CONTEXT;
+
+ ctx = (struct _gss_context *) *context_handle;
+
+ if (ctx == NULL)
+ return GSS_S_NO_CONTEXT;
+
+ m = ctx->gc_mech;
+
+ if (m == NULL)
+ return GSS_S_BAD_MECH;
+
+ if (m->gm_set_sec_context_option != NULL) {
+ major_status = m->gm_set_sec_context_option(minor_status,
+ &ctx->gc_ctx, object, value);
+ if (major_status != GSS_S_COMPLETE)
+ _gss_mg_error(m, major_status, *minor_status);
+ } else
+ major_status = GSS_S_BAD_MECH;
+
+ return major_status;
+}
+
diff --git a/source4/heimdal/lib/gssapi/mech/gss_sign.c b/source4/heimdal/lib/gssapi/mech/gss_sign.c
new file mode 100644
index 0000000000..1d73641355
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gss_sign.c
@@ -0,0 +1,42 @@
+/*-
+ * Copyright (c) 2005 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libgssapi/gss_sign.c,v 1.1 2005/12/29 14:40:20 dfr Exp $
+ */
+
+#include "mech_locl.h"
+RCSID("$Id$");
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_sign(OM_uint32 *minor_status,
+ gss_ctx_id_t context_handle,
+ int qop_req,
+ gss_buffer_t message_buffer,
+ gss_buffer_t message_token)
+{
+
+ return gss_get_mic(minor_status,
+ context_handle, qop_req, message_buffer, message_token);
+}
diff --git a/source4/heimdal/lib/gssapi/mech/gss_test_oid_set_member.c b/source4/heimdal/lib/gssapi/mech/gss_test_oid_set_member.c
new file mode 100644
index 0000000000..ca1dca8fad
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gss_test_oid_set_member.c
@@ -0,0 +1,47 @@
+/*-
+ * Copyright (c) 2005 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libgssapi/gss_test_oid_set_member.c,v 1.1 2005/12/29 14:40:20 dfr Exp $
+ */
+
+#include "mech_locl.h"
+RCSID("$Id$");
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_test_oid_set_member(OM_uint32 *minor_status,
+ const gss_OID member,
+ const gss_OID_set set,
+ int *present)
+{
+ int i;
+
+ *present = 0;
+ for (i = 0; i < set->count; i++)
+ if (gss_oid_equal(member, &set->elements[i]))
+ *present = 1;
+
+ *minor_status = 0;
+ return (GSS_S_COMPLETE);
+}
diff --git a/source4/heimdal/lib/gssapi/mech/gss_unseal.c b/source4/heimdal/lib/gssapi/mech/gss_unseal.c
new file mode 100644
index 0000000000..539e65a01c
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gss_unseal.c
@@ -0,0 +1,44 @@
+/*-
+ * Copyright (c) 2005 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libgssapi/gss_unseal.c,v 1.1 2005/12/29 14:40:20 dfr Exp $
+ */
+
+#include "mech_locl.h"
+RCSID("$Id$");
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_unseal(OM_uint32 *minor_status,
+ gss_ctx_id_t context_handle,
+ gss_buffer_t input_message_buffer,
+ gss_buffer_t output_message_buffer,
+ int *conf_state,
+ int *qop_state)
+{
+
+ return (gss_unwrap(minor_status,
+ context_handle, input_message_buffer,
+ output_message_buffer, conf_state, (gss_qop_t *)qop_state));
+}
diff --git a/source4/heimdal/lib/gssapi/mech/gss_unwrap.c b/source4/heimdal/lib/gssapi/mech/gss_unwrap.c
new file mode 100644
index 0000000000..693bbe020b
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gss_unwrap.c
@@ -0,0 +1,46 @@
+/*-
+ * Copyright (c) 2005 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libgssapi/gss_unwrap.c,v 1.1 2005/12/29 14:40:20 dfr Exp $
+ */
+
+#include "mech_locl.h"
+RCSID("$Id$");
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_unwrap(OM_uint32 *minor_status,
+ const gss_ctx_id_t context_handle,
+ const gss_buffer_t input_message_buffer,
+ gss_buffer_t output_message_buffer,
+ int *conf_state,
+ gss_qop_t *qop_state)
+{
+ struct _gss_context *ctx = (struct _gss_context *) context_handle;
+ gssapi_mech_interface m = ctx->gc_mech;
+
+ return (m->gm_unwrap(minor_status, ctx->gc_ctx,
+ input_message_buffer, output_message_buffer,
+ conf_state, qop_state));
+}
diff --git a/source4/heimdal/lib/gssapi/mech/gss_utils.c b/source4/heimdal/lib/gssapi/mech/gss_utils.c
new file mode 100644
index 0000000000..6e05acff03
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gss_utils.c
@@ -0,0 +1,79 @@
+/*-
+ * Copyright (c) 2005 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libgssapi/gss_utils.c,v 1.1 2005/12/29 14:40:20 dfr Exp $
+ */
+
+#include "mech_locl.h"
+RCSID("$Id$");
+
+OM_uint32
+_gss_copy_oid(OM_uint32 *minor_status,
+ const gss_OID from_oid, gss_OID to_oid)
+{
+ size_t len = from_oid->length;
+
+ *minor_status = 0;
+ to_oid->elements = malloc(len);
+ if (!to_oid->elements) {
+ to_oid->length = 0;
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+ to_oid->length = len;
+ memcpy(to_oid->elements, from_oid->elements, len);
+ return (GSS_S_COMPLETE);
+}
+
+OM_uint32
+_gss_free_oid(OM_uint32 *minor_status, gss_OID oid)
+{
+ *minor_status = 0;
+ if (oid->elements) {
+ free(oid->elements);
+ oid->elements = NULL;
+ oid->length = 0;
+ }
+ return (GSS_S_COMPLETE);
+}
+
+OM_uint32
+_gss_copy_buffer(OM_uint32 *minor_status,
+ const gss_buffer_t from_buf, gss_buffer_t to_buf)
+{
+ size_t len = from_buf->length;
+
+ *minor_status = 0;
+ to_buf->value = malloc(len);
+ if (!to_buf->value) {
+ *minor_status = ENOMEM;
+ to_buf->length = 0;
+ return GSS_S_FAILURE;
+ }
+ to_buf->length = len;
+ memcpy(to_buf->value, from_buf->value, len);
+ return (GSS_S_COMPLETE);
+}
+
diff --git a/source4/heimdal/lib/gssapi/mech/gss_verify.c b/source4/heimdal/lib/gssapi/mech/gss_verify.c
new file mode 100644
index 0000000000..f287cb4816
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gss_verify.c
@@ -0,0 +1,43 @@
+/*-
+ * Copyright (c) 2005 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libgssapi/gss_verify.c,v 1.1 2005/12/29 14:40:20 dfr Exp $
+ */
+
+#include "mech_locl.h"
+RCSID("$Id$");
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_verify(OM_uint32 *minor_status,
+ gss_ctx_id_t context_handle,
+ gss_buffer_t message_buffer,
+ gss_buffer_t token_buffer,
+ int *qop_state)
+{
+
+ return (gss_verify_mic(minor_status,
+ context_handle, message_buffer, token_buffer,
+ (gss_qop_t *)qop_state));
+}
diff --git a/source4/heimdal/lib/gssapi/mech/gss_verify_mic.c b/source4/heimdal/lib/gssapi/mech/gss_verify_mic.c
new file mode 100644
index 0000000000..1a411729c6
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gss_verify_mic.c
@@ -0,0 +1,51 @@
+/*-
+ * Copyright (c) 2005 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libgssapi/gss_verify_mic.c,v 1.1 2005/12/29 14:40:20 dfr Exp $
+ */
+
+#include "mech_locl.h"
+RCSID("$Id$");
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_verify_mic(OM_uint32 *minor_status,
+ const gss_ctx_id_t context_handle,
+ const gss_buffer_t message_buffer,
+ const gss_buffer_t token_buffer,
+ gss_qop_t *qop_state)
+{
+ struct _gss_context *ctx = (struct _gss_context *) context_handle;
+ gssapi_mech_interface m = ctx->gc_mech;
+
+ if (qop_state)
+ *qop_state = 0;
+ if (ctx == NULL) {
+ *minor_status = 0;
+ return GSS_S_NO_CONTEXT;
+ }
+
+ return (m->gm_verify_mic(minor_status, ctx->gc_ctx,
+ message_buffer, token_buffer, qop_state));
+}
diff --git a/source4/heimdal/lib/gssapi/mech/gss_wrap.c b/source4/heimdal/lib/gssapi/mech/gss_wrap.c
new file mode 100644
index 0000000000..b3363d3f20
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gss_wrap.c
@@ -0,0 +1,55 @@
+/*-
+ * Copyright (c) 2005 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libgssapi/gss_wrap.c,v 1.1 2005/12/29 14:40:20 dfr Exp $
+ */
+
+#include "mech_locl.h"
+RCSID("$Id$");
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_wrap(OM_uint32 *minor_status,
+ const gss_ctx_id_t context_handle,
+ int conf_req_flag,
+ gss_qop_t qop_req,
+ const gss_buffer_t input_message_buffer,
+ int *conf_state,
+ gss_buffer_t output_message_buffer)
+{
+ struct _gss_context *ctx = (struct _gss_context *) context_handle;
+ gssapi_mech_interface m = ctx->gc_mech;
+
+ if (conf_state)
+ *conf_state = 0;
+ _mg_buffer_zero(output_message_buffer);
+ if (ctx == NULL) {
+ *minor_status = 0;
+ return GSS_S_NO_CONTEXT;
+ }
+
+ return (m->gm_wrap(minor_status, ctx->gc_ctx,
+ conf_req_flag, qop_req, input_message_buffer,
+ conf_state, output_message_buffer));
+}
diff --git a/source4/heimdal/lib/gssapi/mech/gss_wrap_size_limit.c b/source4/heimdal/lib/gssapi/mech/gss_wrap_size_limit.c
new file mode 100644
index 0000000000..15b86a9367
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gss_wrap_size_limit.c
@@ -0,0 +1,51 @@
+/*-
+ * Copyright (c) 2005 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libgssapi/gss_wrap_size_limit.c,v 1.1 2005/12/29 14:40:20 dfr Exp $
+ */
+
+#include "mech_locl.h"
+RCSID("$Id$");
+
+OM_uint32 GSSAPI_LIB_FUNCTION
+gss_wrap_size_limit(OM_uint32 *minor_status,
+ const gss_ctx_id_t context_handle,
+ int conf_req_flag,
+ gss_qop_t qop_req,
+ OM_uint32 req_output_size,
+ OM_uint32 *max_input_size)
+{
+ struct _gss_context *ctx = (struct _gss_context *) context_handle;
+ gssapi_mech_interface m = ctx->gc_mech;
+
+ *max_input_size = 0;
+ if (ctx == NULL) {
+ *minor_status = 0;
+ return GSS_S_NO_CONTEXT;
+ }
+
+ return (m->gm_wrap_size_limit(minor_status, ctx->gc_ctx,
+ conf_req_flag, qop_req, req_output_size, max_input_size));
+}
diff --git a/source4/heimdal/lib/gssapi/mech/gssapi.asn1 b/source4/heimdal/lib/gssapi/mech/gssapi.asn1
new file mode 100644
index 0000000000..1ba7b40637
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/gssapi.asn1
@@ -0,0 +1,12 @@
+-- $Id$
+
+GSS-API DEFINITIONS ::= BEGIN
+
+IMPORTS heim_any_set FROM heim;
+
+GSSAPIContextToken ::= [APPLICATION 0] IMPLICIT SEQUENCE {
+ thisMech OBJECT IDENTIFIER,
+ innerContextToken heim_any_set
+}
+
+END \ No newline at end of file
diff --git a/source4/heimdal/lib/gssapi/mech/mech_locl.h b/source4/heimdal/lib/gssapi/mech/mech_locl.h
new file mode 100644
index 0000000000..42c069eb2d
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/mech_locl.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#include <config.h>
+
+#include <krb5-types.h>
+
+#include <sys/types.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <dlfcn.h>
+#include <errno.h>
+
+#include <gssapi_asn1.h>
+#include <der.h>
+
+#include <roken.h>
+
+#include <gssapi.h>
+#include <gssapi_mech.h>
+
+#include "mechqueue.h"
+
+#include "context.h"
+#include "cred.h"
+#include "mech_switch.h"
+#include "name.h"
+#include "utils.h"
+
+#define _mg_buffer_zero(buffer) \
+ do { (buffer)->value = NULL; (buffer)->length = 0; } while(0)
diff --git a/source4/heimdal/lib/gssapi/mech/mech_switch.h b/source4/heimdal/lib/gssapi/mech/mech_switch.h
new file mode 100644
index 0000000000..e83a4c8a5a
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/mech_switch.h
@@ -0,0 +1,42 @@
+/*-
+ * Copyright (c) 2005 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libgssapi/mech_switch.h,v 1.1 2005/12/29 14:40:20 dfr Exp $
+ * $Id$
+ */
+
+#include <gssapi_mech.h>
+
+struct _gss_mech_switch {
+ SLIST_ENTRY(_gss_mech_switch) gm_link;
+ gss_OID_desc gm_mech_oid;
+ void *gm_so;
+ gssapi_mech_interface_desc gm_mech;
+};
+SLIST_HEAD(_gss_mech_switch_list, _gss_mech_switch);
+extern struct _gss_mech_switch_list _gss_mechs;
+extern gss_OID_set _gss_mech_oids;
+
+void _gss_load_mech(void);
diff --git a/source4/heimdal/lib/gssapi/mech/mechqueue.h b/source4/heimdal/lib/gssapi/mech/mechqueue.h
new file mode 100644
index 0000000000..7a05c94bf8
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/mechqueue.h
@@ -0,0 +1,101 @@
+/* $NetBSD: queue.h,v 1.39 2004/04/18 14:25:34 lukem Exp $ */
+
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)queue.h 8.5 (Berkeley) 8/20/94
+ */
+
+#ifndef _MECHQUEUE_H_
+#define _MECHQUEUE_H_
+
+#ifndef SLIST_HEAD
+
+/*
+ * Singly-linked List definitions.
+ */
+#define SLIST_HEAD(name, type) \
+struct name { \
+ struct type *slh_first; /* first element */ \
+}
+
+#define SLIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define SLIST_ENTRY(type) \
+struct { \
+ struct type *sle_next; /* next element */ \
+}
+
+/*
+ * Singly-linked List functions.
+ */
+#define SLIST_INIT(head) do { \
+ (head)->slh_first = NULL; \
+} while (/*CONSTCOND*/0)
+
+#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
+ (elm)->field.sle_next = (slistelm)->field.sle_next; \
+ (slistelm)->field.sle_next = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define SLIST_INSERT_HEAD(head, elm, field) do { \
+ (elm)->field.sle_next = (head)->slh_first; \
+ (head)->slh_first = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define SLIST_REMOVE_HEAD(head, field) do { \
+ (head)->slh_first = (head)->slh_first->field.sle_next; \
+} while (/*CONSTCOND*/0)
+
+#define SLIST_REMOVE(head, elm, type, field) do { \
+ if ((head)->slh_first == (elm)) { \
+ SLIST_REMOVE_HEAD((head), field); \
+ } \
+ else { \
+ struct type *curelm = (head)->slh_first; \
+ while(curelm->field.sle_next != (elm)) \
+ curelm = curelm->field.sle_next; \
+ curelm->field.sle_next = \
+ curelm->field.sle_next->field.sle_next; \
+ } \
+} while (/*CONSTCOND*/0)
+
+#define SLIST_FOREACH(var, head, field) \
+ for((var) = (head)->slh_first; (var); (var) = (var)->field.sle_next)
+
+/*
+ * Singly-linked List access methods.
+ */
+#define SLIST_EMPTY(head) ((head)->slh_first == NULL)
+#define SLIST_FIRST(head) ((head)->slh_first)
+#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
+
+#endif /* SLIST_HEAD */
+
+#endif /* !_MECHQUEUE_H_ */
diff --git a/source4/heimdal/lib/gssapi/mech/name.h b/source4/heimdal/lib/gssapi/mech/name.h
new file mode 100644
index 0000000000..baf8e06147
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/name.h
@@ -0,0 +1,48 @@
+/*-
+ * Copyright (c) 2005 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libgssapi/name.h,v 1.1 2005/12/29 14:40:20 dfr Exp $
+ * $Id$
+ */
+
+struct _gss_mechanism_name {
+ SLIST_ENTRY(_gss_mechanism_name) gmn_link;
+ gssapi_mech_interface gmn_mech; /* mechanism ops for MN */
+ gss_OID gmn_mech_oid; /* mechanism oid for MN */
+ gss_name_t gmn_name; /* underlying MN */
+};
+SLIST_HEAD(_gss_mechanism_name_list, _gss_mechanism_name);
+
+struct _gss_name {
+ gss_OID_desc gn_type; /* type of name */
+ gss_buffer_desc gn_value; /* value (as imported) */
+ struct _gss_mechanism_name_list gn_mn; /* list of MNs */
+};
+
+OM_uint32
+ _gss_find_mn(OM_uint32 *, struct _gss_name *, gss_OID,
+ struct _gss_mechanism_name **);
+struct _gss_name *
+ _gss_make_name(gssapi_mech_interface m, gss_name_t new_mn);
diff --git a/source4/heimdal/lib/gssapi/mech/utils.h b/source4/heimdal/lib/gssapi/mech/utils.h
new file mode 100644
index 0000000000..7b27d38f3c
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/mech/utils.h
@@ -0,0 +1,33 @@
+/*-
+ * Copyright (c) 2005 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libgssapi/utils.h,v 1.1 2005/12/29 14:40:20 dfr Exp $
+ * $Id$
+ */
+
+OM_uint32 _gss_free_oid(OM_uint32 *, gss_OID);
+OM_uint32 _gss_copy_oid(OM_uint32 *, const gss_OID, gss_OID);
+OM_uint32 _gss_copy_buffer(OM_uint32 *minor_status,
+ const gss_buffer_t from_buf, gss_buffer_t to_buf);
diff --git a/source4/heimdal/lib/gssapi/spnego/accept_sec_context.c b/source4/heimdal/lib/gssapi/spnego/accept_sec_context.c
new file mode 100644
index 0000000000..cabd806fbf
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/spnego/accept_sec_context.c
@@ -0,0 +1,1013 @@
+/*
+ * Copyright (c) 1997 - 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * Portions Copyright (c) 2004 PADL Software Pty Ltd.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "spnego/spnego_locl.h"
+
+RCSID("$Id$");
+
+static OM_uint32
+send_reject (OM_uint32 *minor_status,
+ gss_buffer_t output_token)
+{
+ NegotiationToken nt;
+ size_t size;
+
+ nt.element = choice_NegotiationToken_negTokenResp;
+
+ ALLOC(nt.u.negTokenResp.negResult, 1);
+ if (nt.u.negTokenResp.negResult == NULL) {
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+ *(nt.u.negTokenResp.negResult) = reject;
+ nt.u.negTokenResp.supportedMech = NULL;
+ nt.u.negTokenResp.responseToken = NULL;
+ nt.u.negTokenResp.mechListMIC = NULL;
+
+ ASN1_MALLOC_ENCODE(NegotiationToken,
+ output_token->value, output_token->length, &nt,
+ &size, *minor_status);
+ free_NegotiationToken(&nt);
+ if (*minor_status != 0)
+ return GSS_S_FAILURE;
+
+ return GSS_S_BAD_MECH;
+}
+
+static OM_uint32
+acceptor_approved(gss_name_t target_name, gss_OID mech)
+{
+ gss_cred_id_t cred = GSS_C_NO_CREDENTIAL;
+ gss_OID_set oidset;
+ OM_uint32 junk, ret;
+
+ if (target_name == GSS_C_NO_NAME)
+ return GSS_S_COMPLETE;
+
+ gss_create_empty_oid_set(&junk, &oidset);
+ gss_add_oid_set_member(&junk, mech, &oidset);
+
+ ret = gss_acquire_cred(&junk, target_name, GSS_C_INDEFINITE, oidset,
+ GSS_C_ACCEPT, &cred, NULL, NULL);
+ gss_release_oid_set(&junk, &oidset);
+ if (ret != GSS_S_COMPLETE)
+ return ret;
+ gss_release_cred(&junk, &cred);
+
+ return GSS_S_COMPLETE;
+}
+
+static OM_uint32
+send_supported_mechs (OM_uint32 *minor_status,
+ gss_buffer_t output_token)
+{
+ NegotiationTokenWin nt;
+ char hostname[MAXHOSTNAMELEN + 1], *p;
+ gss_buffer_desc name_buf;
+ gss_OID name_type;
+ gss_name_t target_princ;
+ gss_name_t canon_princ;
+ OM_uint32 minor;
+ size_t buf_len;
+ gss_buffer_desc data;
+ OM_uint32 ret;
+
+ memset(&nt, 0, sizeof(nt));
+
+ nt.element = choice_NegotiationTokenWin_negTokenInit;
+ nt.u.negTokenInit.reqFlags = NULL;
+ nt.u.negTokenInit.mechToken = NULL;
+ nt.u.negTokenInit.negHints = NULL;
+
+ ret = _gss_spnego_indicate_mechtypelist(minor_status, GSS_C_NO_NAME,
+ acceptor_approved, 1, NULL,
+ &nt.u.negTokenInit.mechTypes, NULL);
+ if (ret != GSS_S_COMPLETE) {
+ return ret;
+ }
+
+ memset(&target_princ, 0, sizeof(target_princ));
+ if (gethostname(hostname, sizeof(hostname) - 2) != 0) {
+ *minor_status = errno;
+ free_NegotiationTokenWin(&nt);
+ return GSS_S_FAILURE;
+ }
+ hostname[sizeof(hostname) - 1] = '\0';
+
+ /* Send the constructed SAM name for this host */
+ for (p = hostname; *p != '\0' && *p != '.'; p++) {
+ *p = toupper((unsigned char)*p);
+ }
+ *p++ = '$';
+ *p = '\0';
+
+ name_buf.length = strlen(hostname);
+ name_buf.value = hostname;
+
+ ret = gss_import_name(minor_status, &name_buf,
+ GSS_C_NO_OID,
+ &target_princ);
+ if (ret != GSS_S_COMPLETE) {
+ free_NegotiationTokenWin(&nt);
+ return ret;
+ }
+
+ name_buf.length = 0;
+ name_buf.value = NULL;
+
+ /* Canonicalize the name using the preferred mechanism */
+ ret = gss_canonicalize_name(minor_status,
+ target_princ,
+ GSS_C_NO_OID,
+ &canon_princ);
+ if (ret != GSS_S_COMPLETE) {
+ free_NegotiationTokenWin(&nt);
+ gss_release_name(&minor, &target_princ);
+ return ret;
+ }
+
+ ret = gss_display_name(minor_status, canon_princ,
+ &name_buf, &name_type);
+ if (ret != GSS_S_COMPLETE) {
+ free_NegotiationTokenWin(&nt);
+ gss_release_name(&minor, &canon_princ);
+ gss_release_name(&minor, &target_princ);
+ return ret;
+ }
+
+ gss_release_name(&minor, &canon_princ);
+ gss_release_name(&minor, &target_princ);
+
+ ALLOC(nt.u.negTokenInit.negHints, 1);
+ if (nt.u.negTokenInit.negHints == NULL) {
+ *minor_status = ENOMEM;
+ gss_release_buffer(&minor, &name_buf);
+ free_NegotiationTokenWin(&nt);
+ return GSS_S_FAILURE;
+ }
+
+ ALLOC(nt.u.negTokenInit.negHints->hintName, 1);
+ if (nt.u.negTokenInit.negHints->hintName == NULL) {
+ *minor_status = ENOMEM;
+ gss_release_buffer(&minor, &name_buf);
+ free_NegotiationTokenWin(&nt);
+ return GSS_S_FAILURE;
+ }
+
+ *(nt.u.negTokenInit.negHints->hintName) = name_buf.value;
+ name_buf.value = NULL;
+ nt.u.negTokenInit.negHints->hintAddress = NULL;
+
+ ASN1_MALLOC_ENCODE(NegotiationTokenWin,
+ data.value, data.length, &nt, &buf_len, ret);
+ free_NegotiationTokenWin(&nt);
+ if (ret) {
+ return ret;
+ }
+ if (data.length != buf_len)
+ abort();
+
+ ret = gss_encapsulate_token(&data, GSS_SPNEGO_MECHANISM, output_token);
+
+ free (data.value);
+
+ if (ret != GSS_S_COMPLETE)
+ return ret;
+
+ *minor_status = 0;
+
+ return GSS_S_CONTINUE_NEEDED;
+}
+
+static OM_uint32
+send_accept (OM_uint32 *minor_status,
+ gssspnego_ctx context_handle,
+ gss_buffer_t mech_token,
+ int initial_response,
+ gss_buffer_t mech_buf,
+ gss_buffer_t output_token)
+{
+ NegotiationToken nt;
+ OM_uint32 ret;
+ gss_buffer_desc mech_mic_buf;
+ size_t size;
+
+ memset(&nt, 0, sizeof(nt));
+
+ nt.element = choice_NegotiationToken_negTokenResp;
+
+ ALLOC(nt.u.negTokenResp.negResult, 1);
+ if (nt.u.negTokenResp.negResult == NULL) {
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+
+ if (context_handle->open) {
+ if (mech_token != GSS_C_NO_BUFFER
+ && mech_token->length != 0
+ && mech_buf != GSS_C_NO_BUFFER)
+ *(nt.u.negTokenResp.negResult) = accept_incomplete;
+ else
+ *(nt.u.negTokenResp.negResult) = accept_completed;
+ } else {
+ if (initial_response && context_handle->require_mic)
+ *(nt.u.negTokenResp.negResult) = request_mic;
+ else
+ *(nt.u.negTokenResp.negResult) = accept_incomplete;
+ }
+
+ if (initial_response) {
+ ALLOC(nt.u.negTokenResp.supportedMech, 1);
+ if (nt.u.negTokenResp.supportedMech == NULL) {
+ free_NegotiationToken(&nt);
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+
+ ret = der_get_oid(context_handle->preferred_mech_type->elements,
+ context_handle->preferred_mech_type->length,
+ nt.u.negTokenResp.supportedMech,
+ NULL);
+ if (ret) {
+ free_NegotiationToken(&nt);
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+ } else {
+ nt.u.negTokenResp.supportedMech = NULL;
+ }
+
+ if (mech_token != GSS_C_NO_BUFFER && mech_token->length != 0) {
+ ALLOC(nt.u.negTokenResp.responseToken, 1);
+ if (nt.u.negTokenResp.responseToken == NULL) {
+ free_NegotiationToken(&nt);
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+ nt.u.negTokenResp.responseToken->length = mech_token->length;
+ nt.u.negTokenResp.responseToken->data = mech_token->value;
+ mech_token->length = 0;
+ mech_token->value = NULL;
+ } else {
+ nt.u.negTokenResp.responseToken = NULL;
+ }
+
+ if (mech_buf != GSS_C_NO_BUFFER) {
+ ret = gss_get_mic(minor_status,
+ context_handle->negotiated_ctx_id,
+ 0,
+ mech_buf,
+ &mech_mic_buf);
+ if (ret == GSS_S_COMPLETE) {
+ ALLOC(nt.u.negTokenResp.mechListMIC, 1);
+ if (nt.u.negTokenResp.mechListMIC == NULL) {
+ gss_release_buffer(minor_status, &mech_mic_buf);
+ free_NegotiationToken(&nt);
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+ nt.u.negTokenResp.mechListMIC->length = mech_mic_buf.length;
+ nt.u.negTokenResp.mechListMIC->data = mech_mic_buf.value;
+ } else if (ret == GSS_S_UNAVAILABLE) {
+ nt.u.negTokenResp.mechListMIC = NULL;
+ } else {
+ free_NegotiationToken(&nt);
+ return ret;
+ }
+
+ } else
+ nt.u.negTokenResp.mechListMIC = NULL;
+
+ ASN1_MALLOC_ENCODE(NegotiationToken,
+ output_token->value, output_token->length,
+ &nt, &size, ret);
+ if (ret) {
+ free_NegotiationToken(&nt);
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+
+ /*
+ * The response should not be encapsulated, because
+ * it is a SubsequentContextToken (note though RFC 1964
+ * specifies encapsulation for all _Kerberos_ tokens).
+ */
+
+ if (*(nt.u.negTokenResp.negResult) == accept_completed)
+ ret = GSS_S_COMPLETE;
+ else
+ ret = GSS_S_CONTINUE_NEEDED;
+ free_NegotiationToken(&nt);
+ return ret;
+}
+
+
+static OM_uint32
+verify_mechlist_mic
+ (OM_uint32 *minor_status,
+ gssspnego_ctx context_handle,
+ gss_buffer_t mech_buf,
+ heim_octet_string *mechListMIC
+ )
+{
+ OM_uint32 ret;
+ gss_buffer_desc mic_buf;
+
+ if (context_handle->verified_mic) {
+ /* This doesn't make sense, we've already verified it? */
+ *minor_status = 0;
+ return GSS_S_DUPLICATE_TOKEN;
+ }
+
+ if (mechListMIC == NULL) {
+ *minor_status = 0;
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+
+ mic_buf.length = mechListMIC->length;
+ mic_buf.value = mechListMIC->data;
+
+ ret = gss_verify_mic(minor_status,
+ context_handle->negotiated_ctx_id,
+ mech_buf,
+ &mic_buf,
+ NULL);
+
+ if (ret != GSS_S_COMPLETE)
+ ret = GSS_S_DEFECTIVE_TOKEN;
+
+ return ret;
+}
+
+static OM_uint32
+select_mech(OM_uint32 *minor_status, MechType *mechType, int verify_p,
+ gss_OID *mech_p)
+{
+ char mechbuf[64];
+ size_t mech_len;
+ gss_OID_desc oid;
+ gss_OID oidp;
+ gss_OID_set mechs;
+ int i;
+ OM_uint32 ret, junk;
+
+ ret = der_put_oid ((unsigned char *)mechbuf + sizeof(mechbuf) - 1,
+ sizeof(mechbuf),
+ mechType,
+ &mech_len);
+ if (ret) {
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+
+ oid.length = mech_len;
+ oid.elements = mechbuf + sizeof(mechbuf) - mech_len;
+
+ if (gss_oid_equal(&oid, GSS_SPNEGO_MECHANISM)) {
+ return GSS_S_BAD_MECH;
+ }
+
+ *minor_status = 0;
+
+ /* Translate broken MS Kebreros OID */
+ if (gss_oid_equal(&oid, &_gss_spnego_mskrb_mechanism_oid_desc))
+ oidp = &_gss_spnego_krb5_mechanism_oid_desc;
+ else
+ oidp = &oid;
+
+
+ ret = gss_indicate_mechs(&junk, &mechs);
+ if (ret)
+ return (ret);
+
+ for (i = 0; i < mechs->count; i++)
+ if (gss_oid_equal(&mechs->elements[i], oidp))
+ break;
+
+ if (i == mechs->count) {
+ gss_release_oid_set(&junk, &mechs);
+ return GSS_S_BAD_MECH;
+ }
+ gss_release_oid_set(&junk, &mechs);
+
+ ret = gss_duplicate_oid(minor_status,
+ &oid, /* possibly this should be oidp */
+ mech_p);
+
+ if (verify_p) {
+ gss_name_t name = GSS_C_NO_NAME;
+ gss_buffer_desc namebuf;
+ char *str = NULL, *host, hostname[MAXHOSTNAMELEN];
+
+ host = getenv("GSSAPI_SPNEGO_NAME");
+ if (host == NULL || issuid()) {
+ if (gethostname(hostname, sizeof(hostname)) != 0) {
+ *minor_status = errno;
+ return GSS_S_FAILURE;
+ }
+ asprintf(&str, "host@%s", hostname);
+ host = str;
+ }
+
+ namebuf.length = strlen(host);
+ namebuf.value = host;
+
+ ret = gss_import_name(minor_status, &namebuf,
+ GSS_C_NT_HOSTBASED_SERVICE, &name);
+ if (str)
+ free(str);
+ if (ret != GSS_S_COMPLETE)
+ return ret;
+
+ ret = acceptor_approved(name, *mech_p);
+ gss_release_name(&junk, &name);
+ }
+
+ return ret;
+}
+
+
+static OM_uint32
+acceptor_complete(OM_uint32 * minor_status,
+ gssspnego_ctx ctx,
+ int *get_mic,
+ gss_buffer_t mech_buf,
+ gss_buffer_t mech_input_token,
+ gss_buffer_t mech_output_token,
+ heim_octet_string *mic,
+ gss_buffer_t output_token)
+{
+ OM_uint32 ret;
+ int require_mic, verify_mic;
+ gss_buffer_desc buf;
+
+ buf.length = 0;
+ buf.value = NULL;
+
+ ret = _gss_spnego_require_mechlist_mic(minor_status, ctx, &require_mic);
+ if (ret)
+ return ret;
+
+ ctx->require_mic = require_mic;
+
+ if (mic != NULL)
+ require_mic = 1;
+
+ if (ctx->open && require_mic) {
+ if (mech_input_token == GSS_C_NO_BUFFER) { /* Even/One */
+ verify_mic = 1;
+ *get_mic = 0;
+ } else if (mech_output_token != GSS_C_NO_BUFFER &&
+ mech_output_token->length == 0) { /* Odd */
+ *get_mic = verify_mic = 1;
+ } else { /* Even/One */
+ verify_mic = 0;
+ *get_mic = 1;
+ }
+
+ if (verify_mic || get_mic) {
+ int eret;
+ size_t buf_len;
+
+ ASN1_MALLOC_ENCODE(MechTypeList,
+ mech_buf->value, mech_buf->length,
+ &ctx->initiator_mech_types, &buf_len, eret);
+ if (eret) {
+ *minor_status = eret;
+ return GSS_S_FAILURE;
+ }
+ if (buf.length != buf_len)
+ abort();
+ }
+
+ if (verify_mic) {
+ ret = verify_mechlist_mic(minor_status, ctx, mech_buf, mic);
+ if (ret) {
+ if (get_mic)
+ send_reject (minor_status, output_token);
+ if (buf.value)
+ free(buf.value);
+ return ret;
+ }
+ ctx->verified_mic = 1;
+ }
+ if (buf.value)
+ free(buf.value);
+
+ } else
+ *get_mic = 0;
+
+ return GSS_S_COMPLETE;
+}
+
+
+static OM_uint32
+acceptor_start
+ (OM_uint32 * minor_status,
+ gss_ctx_id_t * context_handle,
+ const gss_cred_id_t acceptor_cred_handle,
+ const gss_buffer_t input_token_buffer,
+ const gss_channel_bindings_t input_chan_bindings,
+ gss_name_t * src_name,
+ gss_OID * mech_type,
+ gss_buffer_t output_token,
+ OM_uint32 * ret_flags,
+ OM_uint32 * time_rec,
+ gss_cred_id_t *delegated_cred_handle
+ )
+{
+ OM_uint32 ret, junk;
+ NegotiationToken nt;
+ size_t nt_len;
+ NegTokenInit *ni;
+ int i;
+ gss_buffer_desc data;
+ gss_buffer_t mech_input_token = GSS_C_NO_BUFFER;
+ gss_buffer_desc mech_output_token;
+ gss_buffer_desc mech_buf;
+ gss_OID preferred_mech_type = GSS_C_NO_OID;
+ gssspnego_ctx ctx;
+ gssspnego_cred acceptor_cred = (gssspnego_cred)acceptor_cred_handle;
+ int get_mic = 0;
+ int first_ok = 0;
+
+ mech_output_token.value = NULL;
+ mech_output_token.length = 0;
+ mech_buf.value = NULL;
+
+ if (input_token_buffer->length == 0)
+ return send_supported_mechs (minor_status, output_token);
+
+ ret = _gss_spnego_alloc_sec_context(minor_status, context_handle);
+ if (ret != GSS_S_COMPLETE)
+ return ret;
+
+ ctx = (gssspnego_ctx)*context_handle;
+
+ /*
+ * The GSS-API encapsulation is only present on the initial
+ * context token (negTokenInit).
+ */
+ ret = gss_decapsulate_token (input_token_buffer,
+ GSS_SPNEGO_MECHANISM,
+ &data);
+ if (ret)
+ return ret;
+
+ ret = decode_NegotiationToken(data.value, data.length, &nt, &nt_len);
+ gss_release_buffer(minor_status, &data);
+ if (ret) {
+ *minor_status = ret;
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+ if (nt.element != choice_NegotiationToken_negTokenInit) {
+ *minor_status = 0;
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+ ni = &nt.u.negTokenInit;
+
+ if (ni->mechTypes.len < 1) {
+ free_NegotiationToken(&nt);
+ *minor_status = 0;
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+
+ HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
+
+ ret = copy_MechTypeList(&ni->mechTypes, &ctx->initiator_mech_types);
+ if (ret) {
+ HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
+ free_NegotiationToken(&nt);
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+
+ /*
+ * First we try the opportunistic token if we have support for it,
+ * don't try to verify we have credential for the token,
+ * gss_accept_sec_context() will (hopefully) tell us that.
+ * If that failes,
+ */
+
+ ret = select_mech(minor_status,
+ &ni->mechTypes.val[0],
+ 0,
+ &preferred_mech_type);
+
+ if (ret == 0 && ni->mechToken != NULL) {
+ gss_cred_id_t mech_delegated_cred = GSS_C_NO_CREDENTIAL;
+ gss_cred_id_t mech_cred;
+ gss_buffer_desc ibuf;
+
+ ibuf.length = ni->mechToken->length;
+ ibuf.value = ni->mechToken->data;
+ mech_input_token = &ibuf;
+
+ if (acceptor_cred != NULL)
+ mech_cred = acceptor_cred->negotiated_cred_id;
+ else
+ mech_cred = GSS_C_NO_CREDENTIAL;
+
+ if (ctx->mech_src_name != GSS_C_NO_NAME)
+ gss_release_name(&junk, &ctx->mech_src_name);
+
+ ret = gss_accept_sec_context(minor_status,
+ &ctx->negotiated_ctx_id,
+ mech_cred,
+ mech_input_token,
+ input_chan_bindings,
+ &ctx->mech_src_name,
+ &ctx->negotiated_mech_type,
+ &mech_output_token,
+ &ctx->mech_flags,
+ &ctx->mech_time_rec,
+ &mech_delegated_cred);
+
+ if (mech_delegated_cred && delegated_cred_handle) {
+ _gss_spnego_alloc_cred(&junk,
+ mech_delegated_cred,
+ delegated_cred_handle);
+ } else if (mech_delegated_cred != GSS_C_NO_CREDENTIAL)
+ gss_release_cred(&junk, &mech_delegated_cred);
+
+ if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) {
+ ctx->preferred_mech_type = preferred_mech_type;
+ ctx->negotiated_mech_type = preferred_mech_type;
+ if (ret == GSS_S_COMPLETE)
+ ctx->open = 1;
+
+ ret = acceptor_complete(minor_status,
+ ctx,
+ &get_mic,
+ &mech_buf,
+ mech_input_token,
+ &mech_output_token,
+ ni->mechListMIC,
+ output_token);
+ if (ret != GSS_S_COMPLETE)
+ goto out;
+
+ first_ok = 1;
+ } else {
+ gss_mg_collect_error(preferred_mech_type, ret, *minor_status);
+ }
+ }
+
+ /*
+ * If opportunistic token failed, lets try the other mechs.
+ */
+
+ if (!first_ok && ni->mechToken != NULL) {
+
+ preferred_mech_type = GSS_C_NO_OID;
+
+ /* Call glue layer to find first mech we support */
+ for (i = 1; i < ni->mechTypes.len; ++i) {
+ ret = select_mech(minor_status,
+ &ni->mechTypes.val[i],
+ 1,
+ &preferred_mech_type);
+ if (ret == 0)
+ break;
+ }
+ if (preferred_mech_type == GSS_C_NO_OID) {
+ HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
+ free_NegotiationToken(&nt);
+ return ret;
+ }
+
+ ctx->preferred_mech_type = preferred_mech_type;
+ ctx->negotiated_mech_type = preferred_mech_type;
+ }
+
+ /*
+ * The initial token always have a response
+ */
+
+ ret = send_accept (minor_status,
+ ctx,
+ &mech_output_token,
+ 1,
+ get_mic ? &mech_buf : NULL,
+ output_token);
+ if (ret)
+ goto out;
+
+out:
+ if (mech_output_token.value != NULL)
+ gss_release_buffer(&junk, &mech_output_token);
+ if (mech_buf.value != NULL) {
+ free(mech_buf.value);
+ mech_buf.value = NULL;
+ }
+ free_NegotiationToken(&nt);
+
+
+ if (ret == GSS_S_COMPLETE) {
+ if (src_name != NULL && ctx->mech_src_name != NULL) {
+ spnego_name name;
+
+ name = calloc(1, sizeof(*name));
+ if (name) {
+ name->mech = ctx->mech_src_name;
+ ctx->mech_src_name = NULL;
+ *src_name = (gss_name_t)name;
+ }
+ }
+ }
+
+ if (mech_type != NULL)
+ *mech_type = ctx->negotiated_mech_type;
+ if (ret_flags != NULL)
+ *ret_flags = ctx->mech_flags;
+ if (time_rec != NULL)
+ *time_rec = ctx->mech_time_rec;
+
+ if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) {
+ HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
+ return ret;
+ }
+
+ _gss_spnego_internal_delete_sec_context(&junk, context_handle,
+ GSS_C_NO_BUFFER);
+
+ return ret;
+}
+
+
+static OM_uint32
+acceptor_continue
+ (OM_uint32 * minor_status,
+ gss_ctx_id_t * context_handle,
+ const gss_cred_id_t acceptor_cred_handle,
+ const gss_buffer_t input_token_buffer,
+ const gss_channel_bindings_t input_chan_bindings,
+ gss_name_t * src_name,
+ gss_OID * mech_type,
+ gss_buffer_t output_token,
+ OM_uint32 * ret_flags,
+ OM_uint32 * time_rec,
+ gss_cred_id_t *delegated_cred_handle
+ )
+{
+ OM_uint32 ret, ret2, minor, junk;
+ NegotiationToken nt;
+ size_t nt_len;
+ NegTokenResp *na;
+ unsigned int negResult = accept_incomplete;
+ gss_buffer_t mech_input_token = GSS_C_NO_BUFFER;
+ gss_buffer_t mech_output_token = GSS_C_NO_BUFFER;
+ gss_buffer_desc mech_buf;
+ gssspnego_ctx ctx;
+ gssspnego_cred acceptor_cred = (gssspnego_cred)acceptor_cred_handle;
+
+ mech_buf.value = NULL;
+
+ ctx = (gssspnego_ctx)*context_handle;
+
+ /*
+ * The GSS-API encapsulation is only present on the initial
+ * context token (negTokenInit).
+ */
+
+ ret = decode_NegotiationToken(input_token_buffer->value,
+ input_token_buffer->length,
+ &nt, &nt_len);
+ if (ret) {
+ *minor_status = ret;
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+ if (nt.element != choice_NegotiationToken_negTokenResp) {
+ *minor_status = 0;
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+ na = &nt.u.negTokenResp;
+
+ if (na->negResult != NULL) {
+ negResult = *(na->negResult);
+ }
+
+ HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
+
+ {
+ gss_buffer_desc ibuf, obuf;
+ int require_mic, get_mic = 0;
+ int require_response;
+ heim_octet_string *mic;
+
+ if (na->responseToken != NULL) {
+ ibuf.length = na->responseToken->length;
+ ibuf.value = na->responseToken->data;
+ mech_input_token = &ibuf;
+ } else {
+ ibuf.value = NULL;
+ ibuf.length = 0;
+ }
+
+ if (mech_input_token != GSS_C_NO_BUFFER) {
+ gss_cred_id_t mech_cred;
+ gss_cred_id_t mech_delegated_cred = GSS_C_NO_CREDENTIAL;
+
+ if (acceptor_cred != NULL)
+ mech_cred = acceptor_cred->negotiated_cred_id;
+ else
+ mech_cred = GSS_C_NO_CREDENTIAL;
+
+ if (ctx->mech_src_name != GSS_C_NO_NAME)
+ gss_release_name(&minor, &ctx->mech_src_name);
+
+ ret = gss_accept_sec_context(&minor,
+ &ctx->negotiated_ctx_id,
+ mech_cred,
+ mech_input_token,
+ input_chan_bindings,
+ &ctx->mech_src_name,
+ &ctx->negotiated_mech_type,
+ &obuf,
+ &ctx->mech_flags,
+ &ctx->mech_time_rec,
+ &mech_delegated_cred);
+
+ if (mech_delegated_cred && delegated_cred_handle) {
+ _gss_spnego_alloc_cred(&junk,
+ mech_delegated_cred,
+ delegated_cred_handle);
+ } else if (mech_delegated_cred != GSS_C_NO_CREDENTIAL)
+ gss_release_cred(&junk, &mech_delegated_cred);
+
+ if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) {
+ mech_output_token = &obuf;
+ }
+ if (ret != GSS_S_COMPLETE && ret != GSS_S_CONTINUE_NEEDED) {
+ free_NegotiationToken(&nt);
+ gss_mg_collect_error(ctx->negotiated_mech_type, ret, minor);
+ send_reject (minor_status, output_token);
+ HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
+ return ret;
+ }
+ if (ret == GSS_S_COMPLETE)
+ ctx->open = 1;
+ } else
+ ret = GSS_S_COMPLETE;
+
+ ret2 = _gss_spnego_require_mechlist_mic(minor_status,
+ ctx,
+ &require_mic);
+ if (ret2)
+ goto out;
+
+ ctx->require_mic = require_mic;
+
+ mic = na->mechListMIC;
+ if (mic != NULL)
+ require_mic = 1;
+
+ if (ret == GSS_S_COMPLETE)
+ ret = acceptor_complete(minor_status,
+ ctx,
+ &get_mic,
+ &mech_buf,
+ mech_input_token,
+ mech_output_token,
+ na->mechListMIC,
+ output_token);
+
+ if (ctx->mech_flags & GSS_C_DCE_STYLE)
+ require_response = (negResult != accept_completed);
+ else
+ require_response = 0;
+
+ /*
+ * Check whether we need to send a result: there should be only
+ * one accept_completed response sent in the entire negotiation
+ */
+ if ((mech_output_token != GSS_C_NO_BUFFER &&
+ mech_output_token->length != 0)
+ || (ctx->open && negResult == accept_incomplete)
+ || require_response
+ || get_mic) {
+ ret2 = send_accept (minor_status,
+ ctx,
+ mech_output_token,
+ 0,
+ get_mic ? &mech_buf : NULL,
+ output_token);
+ if (ret2)
+ goto out;
+ }
+
+ out:
+ if (ret2 != GSS_S_COMPLETE)
+ ret = ret2;
+ if (mech_output_token != NULL)
+ gss_release_buffer(&minor, mech_output_token);
+ if (mech_buf.value != NULL)
+ free(mech_buf.value);
+ free_NegotiationToken(&nt);
+ }
+
+ if (ret == GSS_S_COMPLETE) {
+ if (src_name != NULL && ctx->mech_src_name != NULL) {
+ spnego_name name;
+
+ name = calloc(1, sizeof(*name));
+ if (name) {
+ name->mech = ctx->mech_src_name;
+ ctx->mech_src_name = NULL;
+ *src_name = (gss_name_t)name;
+ }
+ }
+ }
+
+ if (mech_type != NULL)
+ *mech_type = ctx->negotiated_mech_type;
+ if (ret_flags != NULL)
+ *ret_flags = ctx->mech_flags;
+ if (time_rec != NULL)
+ *time_rec = ctx->mech_time_rec;
+
+ if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) {
+ HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
+ return ret;
+ }
+
+ _gss_spnego_internal_delete_sec_context(&minor, context_handle,
+ GSS_C_NO_BUFFER);
+
+ return ret;
+}
+
+OM_uint32
+_gss_spnego_accept_sec_context
+ (OM_uint32 * minor_status,
+ gss_ctx_id_t * context_handle,
+ const gss_cred_id_t acceptor_cred_handle,
+ const gss_buffer_t input_token_buffer,
+ const gss_channel_bindings_t input_chan_bindings,
+ gss_name_t * src_name,
+ gss_OID * mech_type,
+ gss_buffer_t output_token,
+ OM_uint32 * ret_flags,
+ OM_uint32 * time_rec,
+ gss_cred_id_t *delegated_cred_handle
+ )
+{
+ _gss_accept_sec_context_t *func;
+
+ *minor_status = 0;
+
+ output_token->length = 0;
+ output_token->value = NULL;
+
+ if (src_name != NULL)
+ *src_name = GSS_C_NO_NAME;
+ if (mech_type != NULL)
+ *mech_type = GSS_C_NO_OID;
+ if (ret_flags != NULL)
+ *ret_flags = 0;
+ if (time_rec != NULL)
+ *time_rec = 0;
+ if (delegated_cred_handle != NULL)
+ *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
+
+
+ if (*context_handle == GSS_C_NO_CONTEXT)
+ func = acceptor_start;
+ else
+ func = acceptor_continue;
+
+
+ return (*func)(minor_status, context_handle, acceptor_cred_handle,
+ input_token_buffer, input_chan_bindings,
+ src_name, mech_type, output_token, ret_flags,
+ time_rec, delegated_cred_handle);
+}
diff --git a/source4/heimdal/lib/gssapi/spnego/compat.c b/source4/heimdal/lib/gssapi/spnego/compat.c
new file mode 100644
index 0000000000..67d9b202a7
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/spnego/compat.c
@@ -0,0 +1,319 @@
+/*
+ * Copyright (c) 2004, PADL Software Pty Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of PADL Software nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "spnego/spnego_locl.h"
+
+RCSID("$Id$");
+
+/*
+ * Apparently Microsoft got the OID wrong, and used
+ * 1.2.840.48018.1.2.2 instead. We need both this and
+ * the correct Kerberos OID here in order to deal with
+ * this. Because this is manifest in SPNEGO only I'd
+ * prefer to deal with this here rather than inside the
+ * Kerberos mechanism.
+ */
+gss_OID_desc _gss_spnego_mskrb_mechanism_oid_desc =
+ {9, (void *)"\x2a\x86\x48\x82\xf7\x12\x01\x02\x02"};
+
+gss_OID_desc _gss_spnego_krb5_mechanism_oid_desc =
+ {9, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"};
+
+/*
+ * Allocate a SPNEGO context handle
+ */
+OM_uint32 _gss_spnego_alloc_sec_context (OM_uint32 * minor_status,
+ gss_ctx_id_t *context_handle)
+{
+ gssspnego_ctx ctx;
+
+ ctx = calloc(1, sizeof(*ctx));
+ if (ctx == NULL) {
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+
+ ctx->initiator_mech_types.len = 0;
+ ctx->initiator_mech_types.val = NULL;
+ ctx->preferred_mech_type = GSS_C_NO_OID;
+ ctx->negotiated_mech_type = GSS_C_NO_OID;
+ ctx->negotiated_ctx_id = GSS_C_NO_CONTEXT;
+
+ /*
+ * Cache these so we can return them before returning
+ * GSS_S_COMPLETE, even if the mechanism has itself
+ * completed earlier
+ */
+ ctx->mech_flags = 0;
+ ctx->mech_time_rec = 0;
+ ctx->mech_src_name = GSS_C_NO_NAME;
+
+ ctx->open = 0;
+ ctx->local = 0;
+ ctx->require_mic = 0;
+ ctx->verified_mic = 0;
+
+ HEIMDAL_MUTEX_init(&ctx->ctx_id_mutex);
+
+ *context_handle = (gss_ctx_id_t)ctx;
+
+ return GSS_S_COMPLETE;
+}
+
+/*
+ * Free a SPNEGO context handle. The caller must have acquired
+ * the lock before this is called.
+ */
+OM_uint32 _gss_spnego_internal_delete_sec_context
+ (OM_uint32 *minor_status,
+ gss_ctx_id_t *context_handle,
+ gss_buffer_t output_token
+ )
+{
+ gssspnego_ctx ctx;
+ OM_uint32 ret, minor;
+
+ *minor_status = 0;
+
+ if (context_handle == NULL) {
+ return GSS_S_NO_CONTEXT;
+ }
+
+ if (output_token != GSS_C_NO_BUFFER) {
+ output_token->length = 0;
+ output_token->value = NULL;
+ }
+
+ ctx = (gssspnego_ctx)*context_handle;
+ *context_handle = GSS_C_NO_CONTEXT;
+
+ if (ctx == NULL) {
+ return GSS_S_NO_CONTEXT;
+ }
+
+ if (ctx->initiator_mech_types.val != NULL)
+ free_MechTypeList(&ctx->initiator_mech_types);
+
+ gss_release_oid(&minor, &ctx->preferred_mech_type);
+ ctx->negotiated_mech_type = GSS_C_NO_OID;
+
+ gss_release_name(&minor, &ctx->target_name);
+ gss_release_name(&minor, &ctx->mech_src_name);
+
+ if (ctx->negotiated_ctx_id != GSS_C_NO_CONTEXT) {
+ ret = gss_delete_sec_context(minor_status,
+ &ctx->negotiated_ctx_id,
+ output_token);
+ ctx->negotiated_ctx_id = GSS_C_NO_CONTEXT;
+ } else {
+ ret = GSS_S_COMPLETE;
+ }
+
+ HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
+ HEIMDAL_MUTEX_destroy(&ctx->ctx_id_mutex);
+
+ free(ctx);
+ *context_handle = NULL;
+
+ return ret;
+}
+
+/*
+ * For compatability with the Windows SPNEGO implementation, the
+ * default is to ignore the mechListMIC unless CFX is used and
+ * a non-preferred mechanism was negotiated
+ */
+
+OM_uint32
+_gss_spnego_require_mechlist_mic(OM_uint32 *minor_status,
+ gssspnego_ctx ctx,
+ int *require_mic)
+{
+ gss_buffer_set_t buffer_set = GSS_C_NO_BUFFER_SET;
+ OM_uint32 minor;
+
+ *minor_status = 0;
+ *require_mic = 0;
+
+ if (ctx == NULL) {
+ return GSS_S_COMPLETE;
+ }
+
+ if (ctx->require_mic) {
+ /* Acceptor requested it: mandatory to honour */
+ *require_mic = 1;
+ return GSS_S_COMPLETE;
+ }
+
+ /*
+ * Check whether peer indicated implicit support for updated SPNEGO
+ * (eg. in the Kerberos case by using CFX)
+ */
+ if (gss_inquire_sec_context_by_oid(&minor, ctx->negotiated_ctx_id,
+ GSS_C_PEER_HAS_UPDATED_SPNEGO,
+ &buffer_set) == GSS_S_COMPLETE) {
+ *require_mic = 1;
+ gss_release_buffer_set(&minor, &buffer_set);
+ }
+
+ /* Safe-to-omit MIC rules follow */
+ if (*require_mic) {
+ if (gss_oid_equal(ctx->negotiated_mech_type, ctx->preferred_mech_type)) {
+ *require_mic = 0;
+ } else if (gss_oid_equal(ctx->negotiated_mech_type, &_gss_spnego_krb5_mechanism_oid_desc) &&
+ gss_oid_equal(ctx->preferred_mech_type, &_gss_spnego_mskrb_mechanism_oid_desc)) {
+ *require_mic = 0;
+ }
+ }
+
+ return GSS_S_COMPLETE;
+}
+
+static int
+add_mech_type(gss_OID mech_type,
+ int includeMSCompatOID,
+ MechTypeList *mechtypelist)
+{
+ MechType mech;
+ int ret;
+
+ if (gss_oid_equal(mech_type, GSS_SPNEGO_MECHANISM))
+ return 0;
+
+ if (includeMSCompatOID &&
+ gss_oid_equal(mech_type, &_gss_spnego_krb5_mechanism_oid_desc)) {
+ ret = der_get_oid(_gss_spnego_mskrb_mechanism_oid_desc.elements,
+ _gss_spnego_mskrb_mechanism_oid_desc.length,
+ &mech,
+ NULL);
+ if (ret)
+ return ret;
+ ret = add_MechTypeList(mechtypelist, &mech);
+ free_MechType(&mech);
+ if (ret)
+ return ret;
+ }
+ ret = der_get_oid(mech_type->elements, mech_type->length, &mech, NULL);
+ if (ret)
+ return ret;
+ ret = add_MechTypeList(mechtypelist, &mech);
+ free_MechType(&mech);
+ return ret;
+}
+
+
+OM_uint32
+_gss_spnego_indicate_mechtypelist (OM_uint32 *minor_status,
+ gss_name_t target_name,
+ OM_uint32 (*func)(gss_name_t, gss_OID),
+ int includeMSCompatOID,
+ const gssspnego_cred cred_handle,
+ MechTypeList *mechtypelist,
+ gss_OID *preferred_mech)
+{
+ gss_OID_set supported_mechs = GSS_C_NO_OID_SET;
+ gss_OID first_mech = GSS_C_NO_OID;
+ OM_uint32 ret;
+ int i;
+
+ mechtypelist->len = 0;
+ mechtypelist->val = NULL;
+
+ if (cred_handle != NULL) {
+ ret = gss_inquire_cred(minor_status,
+ cred_handle->negotiated_cred_id,
+ NULL,
+ NULL,
+ NULL,
+ &supported_mechs);
+ } else {
+ ret = gss_indicate_mechs(minor_status, &supported_mechs);
+ }
+
+ if (ret != GSS_S_COMPLETE) {
+ return ret;
+ }
+
+ if (supported_mechs->count == 0) {
+ *minor_status = ENOENT;
+ gss_release_oid_set(minor_status, &supported_mechs);
+ return GSS_S_FAILURE;
+ }
+
+ ret = (*func)(target_name, GSS_KRB5_MECHANISM);
+ if (ret == GSS_S_COMPLETE) {
+ ret = add_mech_type(GSS_KRB5_MECHANISM,
+ includeMSCompatOID,
+ mechtypelist);
+ if (!GSS_ERROR(ret))
+ first_mech = GSS_KRB5_MECHANISM;
+ }
+ ret = GSS_S_COMPLETE;
+
+ for (i = 0; i < supported_mechs->count; i++) {
+ OM_uint32 subret;
+ if (gss_oid_equal(&supported_mechs->elements[i], GSS_SPNEGO_MECHANISM))
+ continue;
+ if (gss_oid_equal(&supported_mechs->elements[i], GSS_KRB5_MECHANISM))
+ continue;
+
+ subret = (*func)(target_name, &supported_mechs->elements[i]);
+ if (subret != GSS_S_COMPLETE)
+ continue;
+
+ ret = add_mech_type(&supported_mechs->elements[i],
+ includeMSCompatOID,
+ mechtypelist);
+ if (ret != 0) {
+ *minor_status = ret;
+ ret = GSS_S_FAILURE;
+ break;
+ }
+ if (first_mech == GSS_C_NO_OID)
+ first_mech = &supported_mechs->elements[i];
+ }
+
+ if (mechtypelist->len == 0) {
+ gss_release_oid_set(minor_status, &supported_mechs);
+ *minor_status = 0;
+ return GSS_S_BAD_MECH;
+ }
+
+ if (preferred_mech != NULL) {
+ ret = gss_duplicate_oid(minor_status, first_mech, preferred_mech);
+ if (ret != GSS_S_COMPLETE)
+ free_MechTypeList(mechtypelist);
+ }
+ gss_release_oid_set(minor_status, &supported_mechs);
+
+ return ret;
+}
diff --git a/source4/heimdal/lib/gssapi/spnego/context_stubs.c b/source4/heimdal/lib/gssapi/spnego/context_stubs.c
new file mode 100644
index 0000000000..5bc1a48656
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/spnego/context_stubs.c
@@ -0,0 +1,949 @@
+/*
+ * Copyright (c) 2004, PADL Software Pty Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of PADL Software nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "spnego/spnego_locl.h"
+
+RCSID("$Id$");
+
+static OM_uint32
+spnego_supported_mechs(OM_uint32 *minor_status, gss_OID_set *mechs)
+{
+ OM_uint32 ret, junk;
+ gss_OID_set m;
+ int i;
+
+ ret = gss_indicate_mechs(minor_status, &m);
+ if (ret != GSS_S_COMPLETE)
+ return ret;
+
+ ret = gss_create_empty_oid_set(minor_status, mechs);
+ if (ret != GSS_S_COMPLETE) {
+ gss_release_oid_set(&junk, &m);
+ return ret;
+ }
+
+ for (i = 0; i < m->count; i++) {
+ if (gss_oid_equal(&m->elements[i], GSS_SPNEGO_MECHANISM))
+ continue;
+
+ ret = gss_add_oid_set_member(minor_status, &m->elements[i], mechs);
+ if (ret) {
+ gss_release_oid_set(&junk, &m);
+ gss_release_oid_set(&junk, mechs);
+ return ret;
+ }
+ }
+ return ret;
+}
+
+
+
+OM_uint32 _gss_spnego_process_context_token
+ (OM_uint32 *minor_status,
+ const gss_ctx_id_t context_handle,
+ const gss_buffer_t token_buffer
+ )
+{
+ gss_ctx_id_t context ;
+ gssspnego_ctx ctx;
+ OM_uint32 ret;
+
+ if (context_handle == GSS_C_NO_CONTEXT)
+ return GSS_S_NO_CONTEXT;
+
+ context = context_handle;
+ ctx = (gssspnego_ctx)context_handle;
+
+ HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
+
+ ret = gss_process_context_token(minor_status,
+ ctx->negotiated_ctx_id,
+ token_buffer);
+ if (ret != GSS_S_COMPLETE) {
+ HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
+ return ret;
+ }
+
+ ctx->negotiated_ctx_id = GSS_C_NO_CONTEXT;
+
+ return _gss_spnego_internal_delete_sec_context(minor_status,
+ &context,
+ GSS_C_NO_BUFFER);
+}
+
+OM_uint32 _gss_spnego_delete_sec_context
+ (OM_uint32 *minor_status,
+ gss_ctx_id_t *context_handle,
+ gss_buffer_t output_token
+ )
+{
+ gssspnego_ctx ctx;
+
+ if (context_handle == NULL || *context_handle == GSS_C_NO_CONTEXT)
+ return GSS_S_NO_CONTEXT;
+
+ ctx = (gssspnego_ctx)*context_handle;
+
+ HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
+
+ return _gss_spnego_internal_delete_sec_context(minor_status,
+ context_handle,
+ output_token);
+}
+
+OM_uint32 _gss_spnego_context_time
+ (OM_uint32 *minor_status,
+ const gss_ctx_id_t context_handle,
+ OM_uint32 *time_rec
+ )
+{
+ gssspnego_ctx ctx;
+ *minor_status = 0;
+
+ if (context_handle == GSS_C_NO_CONTEXT) {
+ return GSS_S_NO_CONTEXT;
+ }
+
+ ctx = (gssspnego_ctx)context_handle;
+
+ if (ctx->negotiated_ctx_id == GSS_C_NO_CONTEXT) {
+ return GSS_S_NO_CONTEXT;
+ }
+
+ return gss_context_time(minor_status,
+ ctx->negotiated_ctx_id,
+ time_rec);
+}
+
+OM_uint32 _gss_spnego_get_mic
+ (OM_uint32 *minor_status,
+ const gss_ctx_id_t context_handle,
+ gss_qop_t qop_req,
+ const gss_buffer_t message_buffer,
+ gss_buffer_t message_token
+ )
+{
+ gssspnego_ctx ctx;
+
+ *minor_status = 0;
+
+ if (context_handle == GSS_C_NO_CONTEXT) {
+ return GSS_S_NO_CONTEXT;
+ }
+
+ ctx = (gssspnego_ctx)context_handle;
+
+ if (ctx->negotiated_ctx_id == GSS_C_NO_CONTEXT) {
+ return GSS_S_NO_CONTEXT;
+ }
+
+ return gss_get_mic(minor_status, ctx->negotiated_ctx_id,
+ qop_req, message_buffer, message_token);
+}
+
+OM_uint32 _gss_spnego_verify_mic
+ (OM_uint32 * minor_status,
+ const gss_ctx_id_t context_handle,
+ const gss_buffer_t message_buffer,
+ const gss_buffer_t token_buffer,
+ gss_qop_t * qop_state
+ )
+{
+ gssspnego_ctx ctx;
+
+ *minor_status = 0;
+
+ if (context_handle == GSS_C_NO_CONTEXT) {
+ return GSS_S_NO_CONTEXT;
+ }
+
+ ctx = (gssspnego_ctx)context_handle;
+
+ if (ctx->negotiated_ctx_id == GSS_C_NO_CONTEXT) {
+ return GSS_S_NO_CONTEXT;
+ }
+
+ return gss_verify_mic(minor_status,
+ ctx->negotiated_ctx_id,
+ message_buffer,
+ token_buffer,
+ qop_state);
+}
+
+OM_uint32 _gss_spnego_wrap
+ (OM_uint32 * minor_status,
+ const gss_ctx_id_t context_handle,
+ int conf_req_flag,
+ gss_qop_t qop_req,
+ const gss_buffer_t input_message_buffer,
+ int * conf_state,
+ gss_buffer_t output_message_buffer
+ )
+{
+ gssspnego_ctx ctx;
+
+ *minor_status = 0;
+
+ if (context_handle == GSS_C_NO_CONTEXT) {
+ return GSS_S_NO_CONTEXT;
+ }
+
+ ctx = (gssspnego_ctx)context_handle;
+
+ if (ctx->negotiated_ctx_id == GSS_C_NO_CONTEXT) {
+ return GSS_S_NO_CONTEXT;
+ }
+
+ return gss_wrap(minor_status,
+ ctx->negotiated_ctx_id,
+ conf_req_flag,
+ qop_req,
+ input_message_buffer,
+ conf_state,
+ output_message_buffer);
+}
+
+OM_uint32 _gss_spnego_unwrap
+ (OM_uint32 * minor_status,
+ const gss_ctx_id_t context_handle,
+ const gss_buffer_t input_message_buffer,
+ gss_buffer_t output_message_buffer,
+ int * conf_state,
+ gss_qop_t * qop_state
+ )
+{
+ gssspnego_ctx ctx;
+
+ *minor_status = 0;
+
+ if (context_handle == GSS_C_NO_CONTEXT) {
+ return GSS_S_NO_CONTEXT;
+ }
+
+ ctx = (gssspnego_ctx)context_handle;
+
+ if (ctx->negotiated_ctx_id == GSS_C_NO_CONTEXT) {
+ return GSS_S_NO_CONTEXT;
+ }
+
+ return gss_unwrap(minor_status,
+ ctx->negotiated_ctx_id,
+ input_message_buffer,
+ output_message_buffer,
+ conf_state,
+ qop_state);
+}
+
+OM_uint32 _gss_spnego_compare_name
+ (OM_uint32 *minor_status,
+ const gss_name_t name1,
+ const gss_name_t name2,
+ int * name_equal
+ )
+{
+ spnego_name n1 = (spnego_name)name1;
+ spnego_name n2 = (spnego_name)name2;
+
+ *name_equal = 0;
+
+ if (!gss_oid_equal(&n1->type, &n2->type))
+ return GSS_S_COMPLETE;
+ if (n1->value.length != n2->value.length)
+ return GSS_S_COMPLETE;
+ if (memcmp(n1->value.value, n2->value.value, n2->value.length) != 0)
+ return GSS_S_COMPLETE;
+
+ *name_equal = 1;
+
+ return GSS_S_COMPLETE;
+}
+
+OM_uint32 _gss_spnego_display_name
+ (OM_uint32 * minor_status,
+ const gss_name_t input_name,
+ gss_buffer_t output_name_buffer,
+ gss_OID * output_name_type
+ )
+{
+ spnego_name name = (spnego_name)input_name;
+
+ *minor_status = 0;
+
+ if (name == NULL || name->mech == GSS_C_NO_NAME)
+ return GSS_S_FAILURE;
+
+ return gss_display_name(minor_status, name->mech,
+ output_name_buffer, output_name_type);
+}
+
+OM_uint32 _gss_spnego_import_name
+ (OM_uint32 * minor_status,
+ const gss_buffer_t name_buffer,
+ const gss_OID name_type,
+ gss_name_t * output_name
+ )
+{
+ spnego_name name;
+ OM_uint32 maj_stat;
+
+ *minor_status = 0;
+
+ name = calloc(1, sizeof(*name));
+ if (name == NULL) {
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+
+ maj_stat = _gss_copy_oid(minor_status, name_type, &name->type);
+ if (maj_stat) {
+ free(name);
+ return GSS_S_FAILURE;
+ }
+
+ maj_stat = _gss_copy_buffer(minor_status, name_buffer, &name->value);
+ if (maj_stat) {
+ gss_name_t rname = (gss_name_t)name;
+ _gss_spnego_release_name(minor_status, &rname);
+ return GSS_S_FAILURE;
+ }
+ name->mech = GSS_C_NO_NAME;
+ *output_name = (gss_name_t)name;
+
+ return GSS_S_COMPLETE;
+}
+
+OM_uint32 _gss_spnego_export_name
+ (OM_uint32 * minor_status,
+ const gss_name_t input_name,
+ gss_buffer_t exported_name
+ )
+{
+ spnego_name name;
+ *minor_status = 0;
+
+ if (input_name == GSS_C_NO_NAME)
+ return GSS_S_BAD_NAME;
+
+ name = (spnego_name)input_name;
+ if (name->mech == GSS_C_NO_NAME)
+ return GSS_S_BAD_NAME;
+
+ return gss_export_name(minor_status, name->mech, exported_name);
+}
+
+OM_uint32 _gss_spnego_release_name
+ (OM_uint32 * minor_status,
+ gss_name_t * input_name
+ )
+{
+ *minor_status = 0;
+
+ if (*input_name != GSS_C_NO_NAME) {
+ OM_uint32 junk;
+ spnego_name name = (spnego_name)*input_name;
+ _gss_free_oid(&junk, &name->type);
+ gss_release_buffer(&junk, &name->value);
+ if (name->mech != GSS_C_NO_NAME)
+ gss_release_name(&junk, &name->mech);
+ free(name);
+
+ *input_name = GSS_C_NO_NAME;
+ }
+ return GSS_S_COMPLETE;
+}
+
+OM_uint32 _gss_spnego_inquire_context (
+ OM_uint32 * minor_status,
+ const gss_ctx_id_t context_handle,
+ gss_name_t * src_name,
+ gss_name_t * targ_name,
+ OM_uint32 * lifetime_rec,
+ gss_OID * mech_type,
+ OM_uint32 * ctx_flags,
+ int * locally_initiated,
+ int * open_context
+ )
+{
+ gssspnego_ctx ctx;
+ OM_uint32 maj_stat, junk;
+ gss_name_t src_mn, targ_mn;
+
+ *minor_status = 0;
+
+ if (context_handle == GSS_C_NO_CONTEXT)
+ return GSS_S_NO_CONTEXT;
+
+ ctx = (gssspnego_ctx)context_handle;
+
+ if (ctx->negotiated_ctx_id == GSS_C_NO_CONTEXT)
+ return GSS_S_NO_CONTEXT;
+
+ maj_stat = gss_inquire_context(minor_status,
+ ctx->negotiated_ctx_id,
+ &src_mn,
+ &targ_mn,
+ lifetime_rec,
+ mech_type,
+ ctx_flags,
+ locally_initiated,
+ open_context);
+ if (maj_stat != GSS_S_COMPLETE)
+ return maj_stat;
+
+ if (src_name) {
+ spnego_name name = calloc(1, sizeof(*name));
+ if (name == NULL)
+ goto enomem;
+ name->mech = src_mn;
+ *src_name = (gss_name_t)name;
+ } else
+ gss_release_name(&junk, &src_mn);
+
+ if (targ_name) {
+ spnego_name name = calloc(1, sizeof(*name));
+ if (name == NULL) {
+ gss_release_name(minor_status, src_name);
+ goto enomem;
+ }
+ name->mech = targ_mn;
+ *targ_name = (gss_name_t)name;
+ } else
+ gss_release_name(&junk, &targ_mn);
+
+ return GSS_S_COMPLETE;
+
+enomem:
+ gss_release_name(&junk, &targ_mn);
+ gss_release_name(&junk, &src_mn);
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+}
+
+OM_uint32 _gss_spnego_wrap_size_limit (
+ OM_uint32 * minor_status,
+ const gss_ctx_id_t context_handle,
+ int conf_req_flag,
+ gss_qop_t qop_req,
+ OM_uint32 req_output_size,
+ OM_uint32 * max_input_size
+ )
+{
+ gssspnego_ctx ctx;
+
+ *minor_status = 0;
+
+ if (context_handle == GSS_C_NO_CONTEXT) {
+ return GSS_S_NO_CONTEXT;
+ }
+
+ ctx = (gssspnego_ctx)context_handle;
+
+ if (ctx->negotiated_ctx_id == GSS_C_NO_CONTEXT) {
+ return GSS_S_NO_CONTEXT;
+ }
+
+ return gss_wrap_size_limit(minor_status,
+ ctx->negotiated_ctx_id,
+ conf_req_flag,
+ qop_req,
+ req_output_size,
+ max_input_size);
+}
+
+OM_uint32 _gss_spnego_export_sec_context (
+ OM_uint32 * minor_status,
+ gss_ctx_id_t * context_handle,
+ gss_buffer_t interprocess_token
+ )
+{
+ gssspnego_ctx ctx;
+ OM_uint32 ret;
+
+ *minor_status = 0;
+
+ if (context_handle == NULL) {
+ return GSS_S_NO_CONTEXT;
+ }
+
+ ctx = (gssspnego_ctx)*context_handle;
+
+ if (ctx == NULL)
+ return GSS_S_NO_CONTEXT;
+
+ HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
+
+ if (ctx->negotiated_ctx_id == GSS_C_NO_CONTEXT) {
+ HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
+ return GSS_S_NO_CONTEXT;
+ }
+
+ ret = gss_export_sec_context(minor_status,
+ &ctx->negotiated_ctx_id,
+ interprocess_token);
+ if (ret == GSS_S_COMPLETE) {
+ ret = _gss_spnego_internal_delete_sec_context(minor_status,
+ context_handle,
+ GSS_C_NO_BUFFER);
+ if (ret == GSS_S_COMPLETE)
+ return GSS_S_COMPLETE;
+ }
+
+ HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
+
+ return ret;
+}
+
+OM_uint32 _gss_spnego_import_sec_context (
+ OM_uint32 * minor_status,
+ const gss_buffer_t interprocess_token,
+ gss_ctx_id_t *context_handle
+ )
+{
+ OM_uint32 ret, minor;
+ gss_ctx_id_t context;
+ gssspnego_ctx ctx;
+
+ ret = _gss_spnego_alloc_sec_context(minor_status, &context);
+ if (ret != GSS_S_COMPLETE) {
+ return ret;
+ }
+ ctx = (gssspnego_ctx)context;
+
+ HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
+
+ ret = gss_import_sec_context(minor_status,
+ interprocess_token,
+ &ctx->negotiated_ctx_id);
+ if (ret != GSS_S_COMPLETE) {
+ _gss_spnego_internal_delete_sec_context(&minor, context_handle, GSS_C_NO_BUFFER);
+ return ret;
+ }
+
+ ctx->open = 1;
+ /* don't bother filling in the rest of the fields */
+
+ HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
+
+ *context_handle = (gss_ctx_id_t)ctx;
+
+ return GSS_S_COMPLETE;
+}
+
+OM_uint32 _gss_spnego_inquire_names_for_mech (
+ OM_uint32 * minor_status,
+ const gss_OID mechanism,
+ gss_OID_set * name_types
+ )
+{
+ gss_OID_set mechs, names, n;
+ OM_uint32 ret, junk;
+ int i, j;
+
+ *name_types = NULL;
+
+ ret = spnego_supported_mechs(minor_status, &mechs);
+ if (ret != GSS_S_COMPLETE)
+ return ret;
+
+ ret = gss_create_empty_oid_set(minor_status, &names);
+ if (ret != GSS_S_COMPLETE)
+ goto out;
+
+ for (i = 0; i < mechs->count; i++) {
+ ret = gss_inquire_names_for_mech(minor_status,
+ &mechs->elements[i],
+ &n);
+ if (ret)
+ continue;
+
+ for (j = 0; j < n->count; j++)
+ gss_add_oid_set_member(minor_status,
+ &n->elements[j],
+ &names);
+ gss_release_oid_set(&junk, &n);
+ }
+
+ ret = GSS_S_COMPLETE;
+ *name_types = names;
+out:
+
+ gss_release_oid_set(&junk, &mechs);
+
+ return GSS_S_COMPLETE;
+}
+
+OM_uint32 _gss_spnego_inquire_mechs_for_name (
+ OM_uint32 * minor_status,
+ const gss_name_t input_name,
+ gss_OID_set * mech_types
+ )
+{
+ OM_uint32 ret, junk;
+
+ ret = gss_create_empty_oid_set(minor_status, mech_types);
+ if (ret)
+ return ret;
+
+ ret = gss_add_oid_set_member(minor_status,
+ GSS_SPNEGO_MECHANISM,
+ mech_types);
+ if (ret)
+ gss_release_oid_set(&junk, mech_types);
+
+ return ret;
+}
+
+OM_uint32 _gss_spnego_canonicalize_name (
+ OM_uint32 * minor_status,
+ const gss_name_t input_name,
+ const gss_OID mech_type,
+ gss_name_t * output_name
+ )
+{
+ /* XXX */
+ return gss_duplicate_name(minor_status, input_name, output_name);
+}
+
+OM_uint32 _gss_spnego_duplicate_name (
+ OM_uint32 * minor_status,
+ const gss_name_t src_name,
+ gss_name_t * dest_name
+ )
+{
+ return gss_duplicate_name(minor_status, src_name, dest_name);
+}
+
+OM_uint32 _gss_spnego_sign
+ (OM_uint32 * minor_status,
+ gss_ctx_id_t context_handle,
+ int qop_req,
+ gss_buffer_t message_buffer,
+ gss_buffer_t message_token
+ )
+{
+ gssspnego_ctx ctx;
+
+ *minor_status = 0;
+
+ if (context_handle == GSS_C_NO_CONTEXT) {
+ return GSS_S_NO_CONTEXT;
+ }
+
+ ctx = (gssspnego_ctx)context_handle;
+
+ if (ctx->negotiated_ctx_id == GSS_C_NO_CONTEXT) {
+ return GSS_S_NO_CONTEXT;
+ }
+
+ return gss_sign(minor_status,
+ ctx->negotiated_ctx_id,
+ qop_req,
+ message_buffer,
+ message_token);
+}
+
+OM_uint32 _gss_spnego_verify
+ (OM_uint32 * minor_status,
+ gss_ctx_id_t context_handle,
+ gss_buffer_t message_buffer,
+ gss_buffer_t token_buffer,
+ int * qop_state
+ )
+{
+ gssspnego_ctx ctx;
+
+ *minor_status = 0;
+
+ if (context_handle == GSS_C_NO_CONTEXT) {
+ return GSS_S_NO_CONTEXT;
+ }
+
+ ctx = (gssspnego_ctx)context_handle;
+
+ if (ctx->negotiated_ctx_id == GSS_C_NO_CONTEXT) {
+ return GSS_S_NO_CONTEXT;
+ }
+
+ return gss_verify(minor_status,
+ ctx->negotiated_ctx_id,
+ message_buffer,
+ token_buffer,
+ qop_state);
+}
+
+OM_uint32 _gss_spnego_seal
+ (OM_uint32 * minor_status,
+ gss_ctx_id_t context_handle,
+ int conf_req_flag,
+ int qop_req,
+ gss_buffer_t input_message_buffer,
+ int * conf_state,
+ gss_buffer_t output_message_buffer
+ )
+{
+ gssspnego_ctx ctx;
+
+ *minor_status = 0;
+
+ if (context_handle == GSS_C_NO_CONTEXT) {
+ return GSS_S_NO_CONTEXT;
+ }
+
+ ctx = (gssspnego_ctx)context_handle;
+
+ if (ctx->negotiated_ctx_id == GSS_C_NO_CONTEXT) {
+ return GSS_S_NO_CONTEXT;
+ }
+
+ return gss_seal(minor_status,
+ ctx->negotiated_ctx_id,
+ conf_req_flag,
+ qop_req,
+ input_message_buffer,
+ conf_state,
+ output_message_buffer);
+}
+
+OM_uint32 _gss_spnego_unseal
+ (OM_uint32 * minor_status,
+ gss_ctx_id_t context_handle,
+ gss_buffer_t input_message_buffer,
+ gss_buffer_t output_message_buffer,
+ int * conf_state,
+ int * qop_state
+ )
+{
+ gssspnego_ctx ctx;
+
+ *minor_status = 0;
+
+ if (context_handle == GSS_C_NO_CONTEXT) {
+ return GSS_S_NO_CONTEXT;
+ }
+
+ ctx = (gssspnego_ctx)context_handle;
+
+ if (ctx->negotiated_ctx_id == GSS_C_NO_CONTEXT) {
+ return GSS_S_NO_CONTEXT;
+ }
+
+ return gss_unseal(minor_status,
+ ctx->negotiated_ctx_id,
+ input_message_buffer,
+ output_message_buffer,
+ conf_state,
+ qop_state);
+}
+
+#if 0
+OM_uint32 _gss_spnego_unwrap_ex
+ (OM_uint32 * minor_status,
+ const gss_ctx_id_t context_handle,
+ const gss_buffer_t token_header_buffer,
+ const gss_buffer_t associated_data_buffer,
+ const gss_buffer_t input_message_buffer,
+ gss_buffer_t output_message_buffer,
+ int * conf_state,
+ gss_qop_t * qop_state)
+{
+ gssspnego_ctx ctx;
+
+ *minor_status = 0;
+
+ if (context_handle == GSS_C_NO_CONTEXT) {
+ return GSS_S_NO_CONTEXT;
+ }
+
+ ctx = (gssspnego_ctx)context_handle;
+
+ if (ctx->negotiated_ctx_id == GSS_C_NO_CONTEXT) {
+ return GSS_S_NO_CONTEXT;
+ }
+
+ return gss_unwrap_ex(minor_status,
+ ctx->negotiated_ctx_id,
+ token_header_buffer,
+ associated_data_buffer,
+ input_message_buffer,
+ output_message_buffer,
+ conf_state,
+ qop_state);
+}
+
+OM_uint32 _gss_spnego_wrap_ex
+ (OM_uint32 * minor_status,
+ const gss_ctx_id_t context_handle,
+ int conf_req_flag,
+ gss_qop_t qop_req,
+ const gss_buffer_t associated_data_buffer,
+ const gss_buffer_t input_message_buffer,
+ int * conf_state,
+ gss_buffer_t output_token_buffer,
+ gss_buffer_t output_message_buffer
+ )
+{
+ gssspnego_ctx ctx;
+
+ *minor_status = 0;
+
+ if (context_handle == GSS_C_NO_CONTEXT) {
+ return GSS_S_NO_CONTEXT;
+ }
+
+ ctx = (gssspnego_ctx)context_handle;
+
+ if (ctx->negotiated_ctx_id == GSS_C_NO_CONTEXT) {
+ return GSS_S_NO_CONTEXT;
+ }
+
+ if ((ctx->mech_flags & GSS_C_DCE_STYLE) == 0 &&
+ associated_data_buffer->length != input_message_buffer->length) {
+ *minor_status = EINVAL;
+ return GSS_S_BAD_QOP;
+ }
+
+ return gss_wrap_ex(minor_status,
+ ctx->negotiated_ctx_id,
+ conf_req_flag,
+ qop_req,
+ associated_data_buffer,
+ input_message_buffer,
+ conf_state,
+ output_token_buffer,
+ output_message_buffer);
+}
+
+OM_uint32 _gss_spnego_complete_auth_token
+ (OM_uint32 * minor_status,
+ const gss_ctx_id_t context_handle,
+ gss_buffer_t input_message_buffer)
+{
+ gssspnego_ctx ctx;
+
+ *minor_status = 0;
+
+ if (context_handle == GSS_C_NO_CONTEXT) {
+ return GSS_S_NO_CONTEXT;
+ }
+
+ ctx = (gssspnego_ctx)context_handle;
+
+ if (ctx->negotiated_ctx_id == GSS_C_NO_CONTEXT) {
+ return GSS_S_NO_CONTEXT;
+ }
+
+ return gss_complete_auth_token(minor_status,
+ ctx->negotiated_ctx_id,
+ input_message_buffer);
+}
+#endif
+
+OM_uint32 _gss_spnego_inquire_sec_context_by_oid
+ (OM_uint32 * minor_status,
+ const gss_ctx_id_t context_handle,
+ const gss_OID desired_object,
+ gss_buffer_set_t *data_set)
+{
+ gssspnego_ctx ctx;
+
+ *minor_status = 0;
+
+ if (context_handle == GSS_C_NO_CONTEXT) {
+ return GSS_S_NO_CONTEXT;
+ }
+
+ ctx = (gssspnego_ctx)context_handle;
+
+ if (ctx->negotiated_ctx_id == GSS_C_NO_CONTEXT) {
+ return GSS_S_NO_CONTEXT;
+ }
+
+ return gss_inquire_sec_context_by_oid(minor_status,
+ ctx->negotiated_ctx_id,
+ desired_object,
+ data_set);
+}
+
+OM_uint32 _gss_spnego_set_sec_context_option
+ (OM_uint32 * minor_status,
+ gss_ctx_id_t * context_handle,
+ const gss_OID desired_object,
+ const gss_buffer_t value)
+{
+ gssspnego_ctx ctx;
+
+ *minor_status = 0;
+
+ if (context_handle == NULL || *context_handle == GSS_C_NO_CONTEXT) {
+ return GSS_S_NO_CONTEXT;
+ }
+
+ ctx = (gssspnego_ctx)*context_handle;
+
+ if (ctx->negotiated_ctx_id == GSS_C_NO_CONTEXT) {
+ return GSS_S_NO_CONTEXT;
+ }
+
+ return gss_set_sec_context_option(minor_status,
+ &ctx->negotiated_ctx_id,
+ desired_object,
+ value);
+}
+
+
+OM_uint32
+_gss_spnego_pseudo_random(OM_uint32 *minor_status,
+ gss_ctx_id_t context_handle,
+ int prf_key,
+ const gss_buffer_t prf_in,
+ ssize_t desired_output_len,
+ gss_buffer_t prf_out)
+{
+ gssspnego_ctx ctx;
+
+ *minor_status = 0;
+
+ if (context_handle == GSS_C_NO_CONTEXT)
+ return GSS_S_NO_CONTEXT;
+
+ ctx = (gssspnego_ctx)context_handle;
+
+ if (ctx->negotiated_ctx_id == GSS_C_NO_CONTEXT)
+ return GSS_S_NO_CONTEXT;
+
+ return gss_pseudo_random(minor_status,
+ ctx->negotiated_ctx_id,
+ prf_key,
+ prf_in,
+ desired_output_len,
+ prf_out);
+}
diff --git a/source4/heimdal/lib/gssapi/spnego/cred_stubs.c b/source4/heimdal/lib/gssapi/spnego/cred_stubs.c
new file mode 100644
index 0000000000..f6b3fecaa0
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/spnego/cred_stubs.c
@@ -0,0 +1,356 @@
+/*
+ * Copyright (c) 2004, PADL Software Pty Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of PADL Software nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "spnego/spnego_locl.h"
+
+RCSID("$Id$");
+
+OM_uint32
+_gss_spnego_release_cred(OM_uint32 *minor_status, gss_cred_id_t *cred_handle)
+{
+ gssspnego_cred cred;
+ OM_uint32 ret;
+
+ *minor_status = 0;
+
+ if (*cred_handle == GSS_C_NO_CREDENTIAL) {
+ return GSS_S_COMPLETE;
+ }
+ cred = (gssspnego_cred)*cred_handle;
+
+ ret = gss_release_cred(minor_status, &cred->negotiated_cred_id);
+
+ free(cred);
+ *cred_handle = GSS_C_NO_CREDENTIAL;
+
+ return ret;
+}
+
+OM_uint32
+_gss_spnego_alloc_cred(OM_uint32 *minor_status,
+ gss_cred_id_t mech_cred_handle,
+ gss_cred_id_t *cred_handle)
+{
+ gssspnego_cred cred;
+
+ if (*cred_handle != GSS_C_NO_CREDENTIAL) {
+ *minor_status = EINVAL;
+ return GSS_S_FAILURE;
+ }
+
+ cred = calloc(1, sizeof(*cred));
+ if (cred == NULL) {
+ *cred_handle = GSS_C_NO_CREDENTIAL;
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+
+ cred->negotiated_cred_id = mech_cred_handle;
+
+ *cred_handle = (gss_cred_id_t)cred;
+
+ return GSS_S_COMPLETE;
+}
+
+/*
+ * For now, just a simple wrapper that avoids recursion. When
+ * we support gss_{get,set}_neg_mechs() we will need to expose
+ * more functionality.
+ */
+OM_uint32 _gss_spnego_acquire_cred
+(OM_uint32 *minor_status,
+ const gss_name_t desired_name,
+ OM_uint32 time_req,
+ const gss_OID_set desired_mechs,
+ gss_cred_usage_t cred_usage,
+ gss_cred_id_t * output_cred_handle,
+ gss_OID_set * actual_mechs,
+ OM_uint32 * time_rec
+ )
+{
+ const spnego_name dname = (const spnego_name)desired_name;
+ gss_name_t name = GSS_C_NO_NAME;
+ OM_uint32 ret, tmp;
+ gss_OID_set_desc actual_desired_mechs;
+ gss_OID_set mechs;
+ int i, j;
+ gss_cred_id_t cred_handle = GSS_C_NO_CREDENTIAL;
+ gssspnego_cred cred;
+
+ *output_cred_handle = GSS_C_NO_CREDENTIAL;
+
+ if (dname) {
+ ret = gss_import_name(minor_status, &dname->value, &dname->type, &name);
+ if (ret) {
+ return ret;
+ }
+ }
+
+ ret = gss_indicate_mechs(minor_status, &mechs);
+ if (ret != GSS_S_COMPLETE) {
+ gss_release_name(minor_status, &name);
+ return ret;
+ }
+
+ /* Remove ourselves from this list */
+ actual_desired_mechs.count = mechs->count;
+ actual_desired_mechs.elements = malloc(actual_desired_mechs.count *
+ sizeof(gss_OID_desc));
+ if (actual_desired_mechs.elements == NULL) {
+ *minor_status = ENOMEM;
+ ret = GSS_S_FAILURE;
+ goto out;
+ }
+
+ for (i = 0, j = 0; i < mechs->count; i++) {
+ if (gss_oid_equal(&mechs->elements[i], GSS_SPNEGO_MECHANISM))
+ continue;
+
+ actual_desired_mechs.elements[j] = mechs->elements[i];
+ j++;
+ }
+ actual_desired_mechs.count = j;
+
+ ret = _gss_spnego_alloc_cred(minor_status, GSS_C_NO_CREDENTIAL,
+ &cred_handle);
+ if (ret != GSS_S_COMPLETE)
+ goto out;
+
+ cred = (gssspnego_cred)cred_handle;
+ ret = gss_acquire_cred(minor_status, name,
+ time_req, &actual_desired_mechs,
+ cred_usage,
+ &cred->negotiated_cred_id,
+ actual_mechs, time_rec);
+ if (ret != GSS_S_COMPLETE)
+ goto out;
+
+ *output_cred_handle = cred_handle;
+
+out:
+ gss_release_name(minor_status, &name);
+ gss_release_oid_set(&tmp, &mechs);
+ if (actual_desired_mechs.elements != NULL) {
+ free(actual_desired_mechs.elements);
+ }
+ if (ret != GSS_S_COMPLETE) {
+ _gss_spnego_release_cred(&tmp, &cred_handle);
+ }
+
+ return ret;
+}
+
+OM_uint32 _gss_spnego_inquire_cred
+ (OM_uint32 * minor_status,
+ const gss_cred_id_t cred_handle,
+ gss_name_t * name,
+ OM_uint32 * lifetime,
+ gss_cred_usage_t * cred_usage,
+ gss_OID_set * mechanisms
+ )
+{
+ gssspnego_cred cred;
+ spnego_name sname = NULL;
+ OM_uint32 ret;
+
+ if (cred_handle == GSS_C_NO_CREDENTIAL) {
+ *minor_status = 0;
+ return GSS_S_NO_CRED;
+ }
+
+ if (name) {
+ sname = calloc(1, sizeof(*sname));
+ if (sname == NULL) {
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+ }
+
+ cred = (gssspnego_cred)cred_handle;
+
+ ret = gss_inquire_cred(minor_status,
+ cred->negotiated_cred_id,
+ sname ? &sname->mech : NULL,
+ lifetime,
+ cred_usage,
+ mechanisms);
+ if (ret) {
+ if (sname)
+ free(sname);
+ return ret;
+ }
+ if (name)
+ *name = (gss_name_t)sname;
+
+ return ret;
+}
+
+OM_uint32 _gss_spnego_add_cred (
+ OM_uint32 * minor_status,
+ const gss_cred_id_t input_cred_handle,
+ const gss_name_t desired_name,
+ const gss_OID desired_mech,
+ gss_cred_usage_t cred_usage,
+ OM_uint32 initiator_time_req,
+ OM_uint32 acceptor_time_req,
+ gss_cred_id_t * output_cred_handle,
+ gss_OID_set * actual_mechs,
+ OM_uint32 * initiator_time_rec,
+ OM_uint32 * acceptor_time_rec
+ )
+{
+ gss_cred_id_t spnego_output_cred_handle = GSS_C_NO_CREDENTIAL;
+ OM_uint32 ret, tmp;
+ gssspnego_cred input_cred, output_cred;
+
+ *output_cred_handle = GSS_C_NO_CREDENTIAL;
+
+ ret = _gss_spnego_alloc_cred(minor_status, GSS_C_NO_CREDENTIAL,
+ &spnego_output_cred_handle);
+ if (ret)
+ return ret;
+
+ input_cred = (gssspnego_cred)input_cred_handle;
+ output_cred = (gssspnego_cred)spnego_output_cred_handle;
+
+ ret = gss_add_cred(minor_status,
+ input_cred->negotiated_cred_id,
+ desired_name,
+ desired_mech,
+ cred_usage,
+ initiator_time_req,
+ acceptor_time_req,
+ &output_cred->negotiated_cred_id,
+ actual_mechs,
+ initiator_time_rec,
+ acceptor_time_rec);
+ if (ret) {
+ _gss_spnego_release_cred(&tmp, &spnego_output_cred_handle);
+ return ret;
+ }
+
+ *output_cred_handle = spnego_output_cred_handle;
+
+ return GSS_S_COMPLETE;
+}
+
+OM_uint32 _gss_spnego_inquire_cred_by_mech (
+ OM_uint32 * minor_status,
+ const gss_cred_id_t cred_handle,
+ const gss_OID mech_type,
+ gss_name_t * name,
+ OM_uint32 * initiator_lifetime,
+ OM_uint32 * acceptor_lifetime,
+ gss_cred_usage_t * cred_usage
+ )
+{
+ gssspnego_cred cred;
+ spnego_name sname = NULL;
+ OM_uint32 ret;
+
+ if (cred_handle == GSS_C_NO_CREDENTIAL) {
+ *minor_status = 0;
+ return GSS_S_NO_CRED;
+ }
+
+ if (name) {
+ sname = calloc(1, sizeof(*sname));
+ if (sname == NULL) {
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+ }
+
+ cred = (gssspnego_cred)cred_handle;
+
+ ret = gss_inquire_cred_by_mech(minor_status,
+ cred->negotiated_cred_id,
+ mech_type,
+ sname ? &sname->mech : NULL,
+ initiator_lifetime,
+ acceptor_lifetime,
+ cred_usage);
+
+ if (ret) {
+ if (sname)
+ free(sname);
+ return ret;
+ }
+ if (name)
+ *name = (gss_name_t)sname;
+
+ return GSS_S_COMPLETE;
+}
+
+OM_uint32 _gss_spnego_inquire_cred_by_oid
+ (OM_uint32 * minor_status,
+ const gss_cred_id_t cred_handle,
+ const gss_OID desired_object,
+ gss_buffer_set_t *data_set)
+{
+ gssspnego_cred cred;
+ OM_uint32 ret;
+
+ if (cred_handle == GSS_C_NO_CREDENTIAL) {
+ *minor_status = 0;
+ return GSS_S_NO_CRED;
+ }
+ cred = (gssspnego_cred)cred_handle;
+
+ ret = gss_inquire_cred_by_oid(minor_status,
+ cred->negotiated_cred_id,
+ desired_object,
+ data_set);
+
+ return ret;
+}
+
+OM_uint32
+_gss_spnego_set_cred_option (OM_uint32 *minor_status,
+ gss_cred_id_t *cred_handle,
+ const gss_OID object,
+ const gss_buffer_t value)
+{
+ gssspnego_cred cred;
+
+ if (cred_handle == NULL || *cred_handle == GSS_C_NO_CREDENTIAL) {
+ *minor_status = 0;
+ return GSS_S_NO_CRED;
+ }
+
+ cred = (gssspnego_cred)*cred_handle;
+ return gss_set_cred_option(minor_status,
+ &cred->negotiated_cred_id,
+ object,
+ value);
+}
+
diff --git a/source4/heimdal/lib/gssapi/spnego/external.c b/source4/heimdal/lib/gssapi/spnego/external.c
new file mode 100644
index 0000000000..02404237a7
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/spnego/external.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2004, PADL Software Pty Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of PADL Software nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "spnego/spnego_locl.h"
+#include <gssapi_mech.h>
+
+RCSID("$Id$");
+
+/*
+ * RFC2478, SPNEGO:
+ * The security mechanism of the initial
+ * negotiation token is identified by the Object Identifier
+ * iso.org.dod.internet.security.mechanism.snego (1.3.6.1.5.5.2).
+ */
+
+static gssapi_mech_interface_desc spnego_mech = {
+ GMI_VERSION,
+ "spnego",
+ {6, (void *)"\x2b\x06\x01\x05\x05\x02"},
+ _gss_spnego_acquire_cred,
+ _gss_spnego_release_cred,
+ _gss_spnego_init_sec_context,
+ _gss_spnego_accept_sec_context,
+ _gss_spnego_process_context_token,
+ _gss_spnego_internal_delete_sec_context,
+ _gss_spnego_context_time,
+ _gss_spnego_get_mic,
+ _gss_spnego_verify_mic,
+ _gss_spnego_wrap,
+ _gss_spnego_unwrap,
+ NULL, /* gm_display_status */
+ NULL, /* gm_indicate_mechs */
+ _gss_spnego_compare_name,
+ _gss_spnego_display_name,
+ _gss_spnego_import_name,
+ _gss_spnego_export_name,
+ _gss_spnego_release_name,
+ _gss_spnego_inquire_cred,
+ _gss_spnego_inquire_context,
+ _gss_spnego_wrap_size_limit,
+ _gss_spnego_add_cred,
+ _gss_spnego_inquire_cred_by_mech,
+ _gss_spnego_export_sec_context,
+ _gss_spnego_import_sec_context,
+ _gss_spnego_inquire_names_for_mech,
+ _gss_spnego_inquire_mechs_for_name,
+ _gss_spnego_canonicalize_name,
+ _gss_spnego_duplicate_name,
+ _gss_spnego_inquire_sec_context_by_oid,
+ _gss_spnego_inquire_cred_by_oid,
+ _gss_spnego_set_sec_context_option,
+ _gss_spnego_set_cred_option,
+ _gss_spnego_pseudo_random
+};
+
+gssapi_mech_interface
+__gss_spnego_initialize(void)
+{
+ return &spnego_mech;
+}
+
+static gss_OID_desc _gss_spnego_mechanism_desc =
+ {6, (void *)"\x2b\x06\x01\x05\x05\x02"};
+
+gss_OID GSS_SPNEGO_MECHANISM = &_gss_spnego_mechanism_desc;
diff --git a/source4/heimdal/lib/gssapi/spnego/init_sec_context.c b/source4/heimdal/lib/gssapi/spnego/init_sec_context.c
new file mode 100644
index 0000000000..7a5814413b
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/spnego/init_sec_context.c
@@ -0,0 +1,668 @@
+/*
+ * Copyright (c) 1997 - 2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * Portions Copyright (c) 2004 PADL Software Pty Ltd.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "spnego/spnego_locl.h"
+
+RCSID("$Id$");
+
+/*
+ * Is target_name an sane target for `mech´.
+ */
+
+static OM_uint32
+initiator_approved(gss_name_t target_name, gss_OID mech)
+{
+ OM_uint32 min_stat, maj_stat;
+ gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
+ gss_buffer_desc out;
+
+ maj_stat = gss_init_sec_context(&min_stat,
+ GSS_C_NO_CREDENTIAL,
+ &ctx,
+ target_name,
+ mech,
+ 0,
+ GSS_C_INDEFINITE,
+ GSS_C_NO_CHANNEL_BINDINGS,
+ GSS_C_NO_BUFFER,
+ NULL,
+ &out,
+ NULL,
+ NULL);
+ if (GSS_ERROR(maj_stat)) {
+ gss_mg_collect_error(mech, maj_stat, min_stat);
+ return GSS_S_BAD_MECH;
+ }
+ gss_release_buffer(&min_stat, &out);
+ gss_delete_sec_context(&min_stat, &ctx, NULL);
+
+ return GSS_S_COMPLETE;
+}
+
+/*
+ * Send a reply. Note that we only need to send a reply if we
+ * need to send a MIC or a mechanism token. Otherwise, we can
+ * return an empty buffer.
+ *
+ * The return value of this will be returned to the API, so it
+ * must return GSS_S_CONTINUE_NEEDED if a token was generated.
+ */
+static OM_uint32
+spnego_reply_internal(OM_uint32 *minor_status,
+ gssspnego_ctx context_handle,
+ const gss_buffer_t mech_buf,
+ gss_buffer_t mech_token,
+ gss_buffer_t output_token)
+{
+ NegotiationToken nt;
+ gss_buffer_desc mic_buf;
+ OM_uint32 ret;
+ size_t size;
+
+ if (mech_buf == GSS_C_NO_BUFFER && mech_token->length == 0) {
+ output_token->length = 0;
+ output_token->value = NULL;
+
+ return context_handle->open ? GSS_S_COMPLETE : GSS_S_FAILURE;
+ }
+
+ memset(&nt, 0, sizeof(nt));
+
+ nt.element = choice_NegotiationToken_negTokenResp;
+
+ ALLOC(nt.u.negTokenResp.negResult, 1);
+ if (nt.u.negTokenResp.negResult == NULL) {
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+
+ nt.u.negTokenResp.supportedMech = NULL;
+
+ output_token->length = 0;
+ output_token->value = NULL;
+
+ if (mech_token->length == 0) {
+ nt.u.negTokenResp.responseToken = NULL;
+ *(nt.u.negTokenResp.negResult) = accept_completed;
+ } else {
+ ALLOC(nt.u.negTokenResp.responseToken, 1);
+ if (nt.u.negTokenResp.responseToken == NULL) {
+ free_NegotiationToken(&nt);
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+ nt.u.negTokenResp.responseToken->length = mech_token->length;
+ nt.u.negTokenResp.responseToken->data = mech_token->value;
+ mech_token->length = 0;
+ mech_token->value = NULL;
+
+ *(nt.u.negTokenResp.negResult) = accept_incomplete;
+ }
+
+ if (mech_buf != GSS_C_NO_BUFFER) {
+
+ ret = gss_get_mic(minor_status,
+ context_handle->negotiated_ctx_id,
+ 0,
+ mech_buf,
+ &mic_buf);
+ if (ret == GSS_S_COMPLETE) {
+ ALLOC(nt.u.negTokenResp.mechListMIC, 1);
+ if (nt.u.negTokenResp.mechListMIC == NULL) {
+ gss_release_buffer(minor_status, &mic_buf);
+ free_NegotiationToken(&nt);
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+
+ nt.u.negTokenResp.mechListMIC->length = mic_buf.length;
+ nt.u.negTokenResp.mechListMIC->data = mic_buf.value;
+ } else if (ret == GSS_S_UNAVAILABLE) {
+ nt.u.negTokenResp.mechListMIC = NULL;
+ } if (ret) {
+ free_NegotiationToken(&nt);
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+ } else {
+ nt.u.negTokenResp.mechListMIC = NULL;
+ }
+
+ ASN1_MALLOC_ENCODE(NegotiationToken,
+ output_token->value, output_token->length,
+ &nt, &size, ret);
+ if (ret) {
+ free_NegotiationToken(&nt);
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+
+ if (*(nt.u.negTokenResp.negResult) == accept_completed)
+ ret = GSS_S_COMPLETE;
+ else
+ ret = GSS_S_CONTINUE_NEEDED;
+
+ free_NegotiationToken(&nt);
+ return ret;
+}
+
+static OM_uint32
+spnego_initial
+ (OM_uint32 * minor_status,
+ gssspnego_cred cred,
+ gss_ctx_id_t * context_handle,
+ const gss_name_t target_name,
+ const gss_OID mech_type,
+ OM_uint32 req_flags,
+ OM_uint32 time_req,
+ const gss_channel_bindings_t input_chan_bindings,
+ const gss_buffer_t input_token,
+ gss_OID * actual_mech_type,
+ gss_buffer_t output_token,
+ OM_uint32 * ret_flags,
+ OM_uint32 * time_rec
+ )
+{
+ NegTokenInit ni;
+ int ret;
+ OM_uint32 sub, minor;
+ gss_buffer_desc mech_token;
+ u_char *buf;
+ size_t buf_size, buf_len;
+ gss_buffer_desc data;
+ size_t ni_len;
+ gss_ctx_id_t context;
+ gssspnego_ctx ctx;
+ spnego_name name = (spnego_name)target_name;
+
+ *minor_status = 0;
+
+ memset (&ni, 0, sizeof(ni));
+
+ *context_handle = GSS_C_NO_CONTEXT;
+
+ if (target_name == GSS_C_NO_NAME)
+ return GSS_S_BAD_NAME;
+
+ sub = _gss_spnego_alloc_sec_context(&minor, &context);
+ if (GSS_ERROR(sub)) {
+ *minor_status = minor;
+ return sub;
+ }
+ ctx = (gssspnego_ctx)context;
+
+ HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
+
+ ctx->local = 1;
+
+ sub = gss_import_name(&minor, &name->value, &name->type, &ctx->target_name);
+ if (GSS_ERROR(sub)) {
+ *minor_status = minor;
+ _gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER);
+ return sub;
+ }
+
+ sub = _gss_spnego_indicate_mechtypelist(&minor,
+ ctx->target_name,
+ initiator_approved,
+ 0,
+ cred,
+ &ni.mechTypes,
+ &ctx->preferred_mech_type);
+ if (GSS_ERROR(sub)) {
+ *minor_status = minor;
+ _gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER);
+ return sub;
+ }
+
+ ni.reqFlags = NULL;
+
+ /*
+ * If we have a credential handle, use it to select the mechanism
+ * that we will use
+ */
+
+ /* generate optimistic token */
+ sub = gss_init_sec_context(&minor,
+ (cred != NULL) ? cred->negotiated_cred_id :
+ GSS_C_NO_CREDENTIAL,
+ &ctx->negotiated_ctx_id,
+ ctx->target_name,
+ ctx->preferred_mech_type,
+ req_flags,
+ time_req,
+ input_chan_bindings,
+ input_token,
+ &ctx->negotiated_mech_type,
+ &mech_token,
+ &ctx->mech_flags,
+ &ctx->mech_time_rec);
+ if (GSS_ERROR(sub)) {
+ free_NegTokenInit(&ni);
+ *minor_status = minor;
+ gss_mg_collect_error(ctx->preferred_mech_type, sub, minor);
+ _gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER);
+ return sub;
+ }
+ if (sub == GSS_S_COMPLETE)
+ ctx->maybe_open = 1;
+
+ if (mech_token.length != 0) {
+ ALLOC(ni.mechToken, 1);
+ if (ni.mechToken == NULL) {
+ free_NegTokenInit(&ni);
+ gss_release_buffer(&minor, &mech_token);
+ _gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER);
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+ ni.mechToken->length = mech_token.length;
+ ni.mechToken->data = malloc(mech_token.length);
+ if (ni.mechToken->data == NULL && mech_token.length != 0) {
+ free_NegTokenInit(&ni);
+ gss_release_buffer(&minor, &mech_token);
+ *minor_status = ENOMEM;
+ _gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER);
+ return GSS_S_FAILURE;
+ }
+ memcpy(ni.mechToken->data, mech_token.value, mech_token.length);
+ gss_release_buffer(&minor, &mech_token);
+ } else
+ ni.mechToken = NULL;
+
+ ni.mechListMIC = NULL;
+
+ ni_len = length_NegTokenInit(&ni);
+ buf_size = 1 + der_length_len(ni_len) + ni_len;
+
+ buf = malloc(buf_size);
+ if (buf == NULL) {
+ free_NegTokenInit(&ni);
+ *minor_status = ENOMEM;
+ _gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER);
+ return GSS_S_FAILURE;
+ }
+
+ ret = encode_NegTokenInit(buf + buf_size - 1,
+ ni_len,
+ &ni, &buf_len);
+ if (ret == 0 && ni_len != buf_len)
+ abort();
+
+ if (ret == 0) {
+ size_t tmp;
+
+ ret = der_put_length_and_tag(buf + buf_size - buf_len - 1,
+ buf_size - buf_len,
+ buf_len,
+ ASN1_C_CONTEXT,
+ CONS,
+ 0,
+ &tmp);
+ if (ret == 0 && tmp + buf_len != buf_size)
+ abort();
+ }
+ if (ret) {
+ *minor_status = ret;
+ free(buf);
+ free_NegTokenInit(&ni);
+ _gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER);
+ return GSS_S_FAILURE;
+ }
+
+ data.value = buf;
+ data.length = buf_size;
+
+ ctx->initiator_mech_types.len = ni.mechTypes.len;
+ ctx->initiator_mech_types.val = ni.mechTypes.val;
+ ni.mechTypes.len = 0;
+ ni.mechTypes.val = NULL;
+
+ free_NegTokenInit(&ni);
+
+ sub = gss_encapsulate_token(&data,
+ GSS_SPNEGO_MECHANISM,
+ output_token);
+ free (buf);
+
+ if (sub) {
+ _gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER);
+ return sub;
+ }
+
+ if (actual_mech_type)
+ *actual_mech_type = ctx->negotiated_mech_type;
+ if (ret_flags)
+ *ret_flags = ctx->mech_flags;
+ if (time_rec)
+ *time_rec = ctx->mech_time_rec;
+
+ HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
+
+ *context_handle = context;
+
+ return GSS_S_CONTINUE_NEEDED;
+}
+
+static OM_uint32
+spnego_reply
+ (OM_uint32 * minor_status,
+ const gssspnego_cred cred,
+ gss_ctx_id_t * context_handle,
+ const gss_name_t target_name,
+ const gss_OID mech_type,
+ OM_uint32 req_flags,
+ OM_uint32 time_req,
+ const gss_channel_bindings_t input_chan_bindings,
+ const gss_buffer_t input_token,
+ gss_OID * actual_mech_type,
+ gss_buffer_t output_token,
+ OM_uint32 * ret_flags,
+ OM_uint32 * time_rec
+ )
+{
+ OM_uint32 ret, minor;
+ NegTokenResp resp;
+ size_t len, taglen;
+ gss_OID_desc mech;
+ int require_mic;
+ size_t buf_len;
+ gss_buffer_desc mic_buf, mech_buf;
+ gss_buffer_desc mech_output_token;
+ gssspnego_ctx ctx;
+
+ *minor_status = 0;
+
+ ctx = (gssspnego_ctx)*context_handle;
+
+ output_token->length = 0;
+ output_token->value = NULL;
+
+ mech_output_token.length = 0;
+ mech_output_token.value = NULL;
+
+ mech_buf.value = NULL;
+ mech_buf.length = 0;
+
+ ret = der_match_tag_and_length(input_token->value, input_token->length,
+ ASN1_C_CONTEXT, CONS, 1, &len, &taglen);
+ if (ret)
+ return ret;
+
+ if (len > input_token->length - taglen)
+ return ASN1_OVERRUN;
+
+ ret = decode_NegTokenResp((const unsigned char *)input_token->value+taglen,
+ len, &resp, NULL);
+ if (ret) {
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+
+ if (resp.negResult == NULL
+ || *(resp.negResult) == reject
+ /* || resp.supportedMech == NULL */
+ )
+ {
+ free_NegTokenResp(&resp);
+ return GSS_S_BAD_MECH;
+ }
+
+ /*
+ * Pick up the mechanism that the acceptor selected, only allow it
+ * to be sent in packet.
+ */
+
+ HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
+
+ if (resp.supportedMech) {
+
+ if (ctx->oidlen) {
+ free_NegTokenResp(&resp);
+ HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
+ return GSS_S_BAD_MECH;
+ }
+ ret = der_put_oid(ctx->oidbuf + sizeof(ctx->oidbuf) - 1,
+ sizeof(ctx->oidbuf),
+ resp.supportedMech,
+ &ctx->oidlen);
+ /* Avoid recursively embedded SPNEGO */
+ if (ret || (ctx->oidlen == GSS_SPNEGO_MECHANISM->length &&
+ memcmp(ctx->oidbuf + sizeof(ctx->oidbuf) - ctx->oidlen,
+ GSS_SPNEGO_MECHANISM->elements,
+ ctx->oidlen) == 0))
+ {
+ free_NegTokenResp(&resp);
+ HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
+ return GSS_S_BAD_MECH;
+ }
+
+ /* check if the acceptor took our optimistic token */
+ if (ctx->oidlen != ctx->preferred_mech_type->length ||
+ memcmp(ctx->oidbuf + sizeof(ctx->oidbuf) - ctx->oidlen,
+ ctx->preferred_mech_type->elements,
+ ctx->oidlen) != 0)
+ {
+ gss_delete_sec_context(&minor, &ctx->negotiated_ctx_id,
+ GSS_C_NO_BUFFER);
+ ctx->negotiated_ctx_id = GSS_C_NO_CONTEXT;
+ }
+ } else if (ctx->oidlen == 0) {
+ free_NegTokenResp(&resp);
+ HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
+ return GSS_S_BAD_MECH;
+ }
+
+ /* if a token (of non zero length), or no context, pass to underlaying mech */
+ if ((resp.responseToken != NULL && resp.responseToken->length) ||
+ ctx->negotiated_ctx_id == GSS_C_NO_CONTEXT) {
+ gss_buffer_desc mech_input_token;
+
+ if (resp.responseToken) {
+ mech_input_token.length = resp.responseToken->length;
+ mech_input_token.value = resp.responseToken->data;
+ } else {
+ mech_input_token.length = 0;
+ mech_input_token.value = NULL;
+ }
+
+
+ mech.length = ctx->oidlen;
+ mech.elements = ctx->oidbuf + sizeof(ctx->oidbuf) - ctx->oidlen;
+
+ /* Fall through as if the negotiated mechanism
+ was requested explicitly */
+ ret = gss_init_sec_context(&minor,
+ (cred != NULL) ? cred->negotiated_cred_id :
+ GSS_C_NO_CREDENTIAL,
+ &ctx->negotiated_ctx_id,
+ ctx->target_name,
+ &mech,
+ req_flags,
+ time_req,
+ input_chan_bindings,
+ &mech_input_token,
+ &ctx->negotiated_mech_type,
+ &mech_output_token,
+ &ctx->mech_flags,
+ &ctx->mech_time_rec);
+ if (GSS_ERROR(ret)) {
+ HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
+ free_NegTokenResp(&resp);
+ gss_mg_collect_error(&mech, ret, minor);
+ *minor_status = minor;
+ return ret;
+ }
+ if (ret == GSS_S_COMPLETE) {
+ ctx->open = 1;
+ }
+ } else if (*(resp.negResult) == accept_completed) {
+ if (ctx->maybe_open)
+ ctx->open = 1;
+ }
+
+ if (*(resp.negResult) == request_mic) {
+ ctx->require_mic = 1;
+ }
+
+ if (ctx->open) {
+ /*
+ * Verify the mechListMIC if one was provided or CFX was
+ * used and a non-preferred mechanism was selected
+ */
+ if (resp.mechListMIC != NULL) {
+ require_mic = 1;
+ } else {
+ ret = _gss_spnego_require_mechlist_mic(minor_status, ctx,
+ &require_mic);
+ if (ret) {
+ HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
+ free_NegTokenResp(&resp);
+ gss_release_buffer(&minor, &mech_output_token);
+ return ret;
+ }
+ }
+ } else {
+ require_mic = 0;
+ }
+
+ if (require_mic) {
+ ASN1_MALLOC_ENCODE(MechTypeList, mech_buf.value, mech_buf.length,
+ &ctx->initiator_mech_types, &buf_len, ret);
+ if (ret) {
+ HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
+ free_NegTokenResp(&resp);
+ gss_release_buffer(&minor, &mech_output_token);
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+ if (mech_buf.length != buf_len)
+ abort();
+
+ if (resp.mechListMIC == NULL) {
+ HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
+ free(mech_buf.value);
+ free_NegTokenResp(&resp);
+ *minor_status = 0;
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+ mic_buf.length = resp.mechListMIC->length;
+ mic_buf.value = resp.mechListMIC->data;
+
+ if (mech_output_token.length == 0) {
+ ret = gss_verify_mic(minor_status,
+ ctx->negotiated_ctx_id,
+ &mech_buf,
+ &mic_buf,
+ NULL);
+ if (ret) {
+ HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
+ free(mech_buf.value);
+ gss_release_buffer(&minor, &mech_output_token);
+ free_NegTokenResp(&resp);
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+ ctx->verified_mic = 1;
+ }
+ }
+
+ ret = spnego_reply_internal(minor_status, ctx,
+ require_mic ? &mech_buf : NULL,
+ &mech_output_token,
+ output_token);
+
+ if (mech_buf.value != NULL)
+ free(mech_buf.value);
+
+ free_NegTokenResp(&resp);
+ gss_release_buffer(&minor, &mech_output_token);
+
+ if (actual_mech_type)
+ *actual_mech_type = ctx->negotiated_mech_type;
+ if (ret_flags)
+ *ret_flags = ctx->mech_flags;
+ if (time_rec)
+ *time_rec = ctx->mech_time_rec;
+
+ HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
+ return ret;
+}
+
+OM_uint32 _gss_spnego_init_sec_context
+ (OM_uint32 * minor_status,
+ const gss_cred_id_t initiator_cred_handle,
+ gss_ctx_id_t * context_handle,
+ const gss_name_t target_name,
+ const gss_OID mech_type,
+ OM_uint32 req_flags,
+ OM_uint32 time_req,
+ const gss_channel_bindings_t input_chan_bindings,
+ const gss_buffer_t input_token,
+ gss_OID * actual_mech_type,
+ gss_buffer_t output_token,
+ OM_uint32 * ret_flags,
+ OM_uint32 * time_rec
+ )
+{
+ gssspnego_cred cred = (gssspnego_cred)initiator_cred_handle;
+
+ if (*context_handle == GSS_C_NO_CONTEXT)
+ return spnego_initial (minor_status,
+ cred,
+ context_handle,
+ target_name,
+ mech_type,
+ req_flags,
+ time_req,
+ input_chan_bindings,
+ input_token,
+ actual_mech_type,
+ output_token,
+ ret_flags,
+ time_rec);
+ else
+ return spnego_reply (minor_status,
+ cred,
+ context_handle,
+ target_name,
+ mech_type,
+ req_flags,
+ time_req,
+ input_chan_bindings,
+ input_token,
+ actual_mech_type,
+ output_token,
+ ret_flags,
+ time_rec);
+}
+
diff --git a/source4/heimdal/lib/gssapi/spnego/spnego.asn1 b/source4/heimdal/lib/gssapi/spnego/spnego.asn1
new file mode 100644
index 0000000000..048e86bb43
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/spnego/spnego.asn1
@@ -0,0 +1,63 @@
+-- $Id$
+
+SPNEGO DEFINITIONS ::=
+BEGIN
+
+MechType::= OBJECT IDENTIFIER
+
+MechTypeList ::= SEQUENCE OF MechType
+
+ContextFlags ::= BIT STRING {
+ delegFlag (0),
+ mutualFlag (1),
+ replayFlag (2),
+ sequenceFlag (3),
+ anonFlag (4),
+ confFlag (5),
+ integFlag (6)
+}
+
+NegHints ::= SEQUENCE {
+ hintName [0] GeneralString OPTIONAL,
+ hintAddress [1] OCTET STRING OPTIONAL
+}
+
+NegTokenInitWin ::= SEQUENCE {
+ mechTypes [0] MechTypeList,
+ reqFlags [1] ContextFlags OPTIONAL,
+ mechToken [2] OCTET STRING OPTIONAL,
+ negHints [3] NegHints OPTIONAL
+}
+
+NegTokenInit ::= SEQUENCE {
+ mechTypes [0] MechTypeList,
+ reqFlags [1] ContextFlags OPTIONAL,
+ mechToken [2] OCTET STRING OPTIONAL,
+ mechListMIC [3] OCTET STRING OPTIONAL,
+ ...
+}
+
+-- NB: negResult is not OPTIONAL in the new SPNEGO spec but
+-- Windows clients do not always send it
+NegTokenResp ::= SEQUENCE {
+ negResult [0] ENUMERATED {
+ accept_completed (0),
+ accept_incomplete (1),
+ reject (2),
+ request-mic (3) } OPTIONAL,
+ supportedMech [1] MechType OPTIONAL,
+ responseToken [2] OCTET STRING OPTIONAL,
+ mechListMIC [3] OCTET STRING OPTIONAL,
+ ...
+}
+
+NegotiationToken ::= CHOICE {
+ negTokenInit[0] NegTokenInit,
+ negTokenResp[1] NegTokenResp
+}
+
+NegotiationTokenWin ::= CHOICE {
+ negTokenInit[0] NegTokenInitWin
+}
+
+END
diff --git a/source4/heimdal/lib/gssapi/spnego/spnego_locl.h b/source4/heimdal/lib/gssapi/spnego/spnego_locl.h
new file mode 100644
index 0000000000..8344e750ae
--- /dev/null
+++ b/source4/heimdal/lib/gssapi/spnego/spnego_locl.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2004, PADL Software Pty Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of PADL Software nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef SPNEGO_LOCL_H
+#define SPNEGO_LOCL_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#ifdef HAVE_PTHREAD_H
+#include <pthread.h>
+#endif
+
+#include <gssapi/gssapi_spnego.h>
+#include <gssapi.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+
+#include <heim_threads.h>
+#include <asn1_err.h>
+
+#include <gssapi_mech.h>
+
+#include "spnego_asn1.h"
+#include "mech/utils.h"
+#include <der.h>
+
+#include <roken.h>
+
+#define ALLOC(X, N) (X) = calloc((N), sizeof(*(X)))
+
+typedef struct {
+ gss_cred_id_t negotiated_cred_id;
+} *gssspnego_cred;
+
+typedef struct {
+ MechTypeList initiator_mech_types;
+ gss_OID preferred_mech_type;
+ gss_OID negotiated_mech_type;
+ gss_ctx_id_t negotiated_ctx_id;
+ OM_uint32 mech_flags;
+ OM_uint32 mech_time_rec;
+ gss_name_t mech_src_name;
+ unsigned int open : 1;
+ unsigned int local : 1;
+ unsigned int require_mic : 1;
+ unsigned int verified_mic : 1;
+ unsigned int maybe_open : 1;
+ HEIMDAL_MUTEX ctx_id_mutex;
+
+ gss_name_t target_name;
+
+ u_char oidbuf[17];
+ size_t oidlen;
+
+} *gssspnego_ctx;
+
+typedef struct {
+ gss_OID_desc type;
+ gss_buffer_desc value;
+ gss_name_t mech;
+} *spnego_name;
+
+extern gss_OID_desc _gss_spnego_mskrb_mechanism_oid_desc;
+extern gss_OID_desc _gss_spnego_krb5_mechanism_oid_desc;
+
+#include <spnego/spnego-private.h>
+
+#endif /* SPNEGO_LOCL_H */
diff --git a/source4/heimdal/lib/hcrypto/aes.c b/source4/heimdal/lib/hcrypto/aes.c
new file mode 100644
index 0000000000..bc9c9ca074
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/aes.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+
+RCSID("$Id$");
+#endif
+
+#ifdef KRB5
+#include <krb5-types.h>
+#endif
+
+#include <string.h>
+
+#include "rijndael-alg-fst.h"
+#include "aes.h"
+
+int
+AES_set_encrypt_key(const unsigned char *userkey, const int bits, AES_KEY *key)
+{
+ key->rounds = rijndaelKeySetupEnc(key->key, userkey, bits);
+ if (key->rounds == 0)
+ return -1;
+ return 0;
+}
+
+int
+AES_set_decrypt_key(const unsigned char *userkey, const int bits, AES_KEY *key)
+{
+ key->rounds = rijndaelKeySetupDec(key->key, userkey, bits);
+ if (key->rounds == 0)
+ return -1;
+ return 0;
+}
+
+void
+AES_encrypt(const unsigned char *in, unsigned char *out, const AES_KEY *key)
+{
+ rijndaelEncrypt(key->key, key->rounds, in, out);
+}
+
+void
+AES_decrypt(const unsigned char *in, unsigned char *out, const AES_KEY *key)
+{
+ rijndaelDecrypt(key->key, key->rounds, in, out);
+}
+
+void
+AES_cbc_encrypt(const unsigned char *in, unsigned char *out,
+ unsigned long size, const AES_KEY *key,
+ unsigned char *iv, int forward_encrypt)
+{
+ unsigned char tmp[AES_BLOCK_SIZE];
+ int i;
+
+ if (forward_encrypt) {
+ while (size >= AES_BLOCK_SIZE) {
+ for (i = 0; i < AES_BLOCK_SIZE; i++)
+ tmp[i] = in[i] ^ iv[i];
+ AES_encrypt(tmp, out, key);
+ memcpy(iv, out, AES_BLOCK_SIZE);
+ size -= AES_BLOCK_SIZE;
+ in += AES_BLOCK_SIZE;
+ out += AES_BLOCK_SIZE;
+ }
+ if (size) {
+ for (i = 0; i < size; i++)
+ tmp[i] = in[i] ^ iv[i];
+ for (i = size; i < AES_BLOCK_SIZE; i++)
+ tmp[i] = iv[i];
+ AES_encrypt(tmp, out, key);
+ memcpy(iv, out, AES_BLOCK_SIZE);
+ }
+ } else {
+ while (size >= AES_BLOCK_SIZE) {
+ memcpy(tmp, in, AES_BLOCK_SIZE);
+ AES_decrypt(tmp, out, key);
+ for (i = 0; i < AES_BLOCK_SIZE; i++)
+ out[i] ^= iv[i];
+ memcpy(iv, tmp, AES_BLOCK_SIZE);
+ size -= AES_BLOCK_SIZE;
+ in += AES_BLOCK_SIZE;
+ out += AES_BLOCK_SIZE;
+ }
+ if (size) {
+ memcpy(tmp, in, AES_BLOCK_SIZE);
+ AES_decrypt(tmp, out, key);
+ for (i = 0; i < size; i++)
+ out[i] ^= iv[i];
+ memcpy(iv, tmp, AES_BLOCK_SIZE);
+ }
+ }
+}
diff --git a/source4/heimdal/lib/hcrypto/aes.h b/source4/heimdal/lib/hcrypto/aes.h
new file mode 100644
index 0000000000..23f8f5d0ab
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/aes.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2003-2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef HEIM_AES_H
+#define HEIM_AES_H 1
+
+/* symbol renaming */
+#define AES_set_encrypt_key hc_AES_set_encrypt_key
+#define AES_set_decrypt_key hc_AES_decrypt_key
+#define AES_encrypt hc_AES_encrypt
+#define AES_decrypt hc_AES_decrypt
+#define AES_cbc_encrypt hc_AES_cbc_encrypt
+
+/*
+ *
+ */
+
+#define AES_BLOCK_SIZE 16
+#define AES_MAXNR 14
+
+#define AES_ENCRYPT 1
+#define AES_DECRYPT 0
+
+typedef struct aes_key {
+ uint32_t key[(AES_MAXNR+1)*4];
+ int rounds;
+} AES_KEY;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int AES_set_encrypt_key(const unsigned char *, const int, AES_KEY *);
+int AES_set_decrypt_key(const unsigned char *, const int, AES_KEY *);
+
+void AES_encrypt(const unsigned char *, unsigned char *, const AES_KEY *);
+void AES_decrypt(const unsigned char *, unsigned char *, const AES_KEY *);
+
+void AES_cbc_encrypt(const unsigned char *, unsigned char *,
+ const unsigned long, const AES_KEY *,
+ unsigned char *, int);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* HEIM_AES_H */
diff --git a/source4/heimdal/lib/hcrypto/bn.c b/source4/heimdal/lib/hcrypto/bn.c
new file mode 100644
index 0000000000..b91a65a7bf
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/bn.c
@@ -0,0 +1,445 @@
+/*
+ * Copyright (c) 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+RCSID("$Id$");
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#include <krb5-types.h>
+#include <rfc2459_asn1.h> /* XXX */
+#include <der.h>
+
+#include <bn.h>
+#include <rand.h>
+#include <hex.h>
+
+BIGNUM *
+BN_new(void)
+{
+ heim_integer *hi;
+ hi = calloc(1, sizeof(*hi));
+ return (BIGNUM *)hi;
+}
+
+void
+BN_free(BIGNUM *bn)
+{
+ BN_clear(bn);
+ free(bn);
+}
+
+void
+BN_clear(BIGNUM *bn)
+{
+ heim_integer *hi = (heim_integer *)bn;
+ if (hi->data) {
+ memset(hi->data, 0, hi->length);
+ free(hi->data);
+ }
+ memset(hi, 0, sizeof(*hi));
+}
+
+void
+BN_clear_free(BIGNUM *bn)
+{
+ BN_free(bn);
+}
+
+BIGNUM *
+BN_dup(const BIGNUM *bn)
+{
+ BIGNUM *b = BN_new();
+ if (der_copy_heim_integer((const heim_integer *)bn, (heim_integer *)b)) {
+ BN_free(b);
+ return NULL;
+ }
+ return b;
+}
+
+/*
+ * If the caller really want to know the number of bits used, subtract
+ * one from the length, multiply by 8, and then lookup in the table
+ * how many bits the hightest byte uses.
+ */
+int
+BN_num_bits(const BIGNUM *bn)
+{
+ static unsigned char num2bits[256] = {
+ 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4, 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ };
+ const heim_integer *i = (const void *)bn;
+ if (i->length == 0)
+ return 0;
+ return (i->length - 1) * 8 + num2bits[((unsigned char *)i->data)[0]];
+}
+
+int
+BN_num_bytes(const BIGNUM *bn)
+{
+ return ((const heim_integer *)bn)->length;
+}
+
+/*
+ * Ignore negative flag.
+ */
+
+BIGNUM *
+BN_bin2bn(const void *s, int len, BIGNUM *bn)
+{
+ heim_integer *hi = (void *)bn;
+
+ if (len < 0)
+ return NULL;
+
+ if (hi == NULL) {
+ hi = (heim_integer *)BN_new();
+ if (hi == NULL)
+ return NULL;
+ }
+ if (hi->data)
+ BN_clear((BIGNUM *)hi);
+ hi->negative = 0;
+ hi->data = malloc(len);
+ if (hi->data == NULL && len != 0) {
+ if (bn == NULL)
+ BN_free((BIGNUM *)hi);
+ return NULL;
+ }
+ hi->length = len;
+ memcpy(hi->data, s, len);
+ return (BIGNUM *)hi;
+}
+
+int
+BN_bn2bin(const BIGNUM *bn, void *to)
+{
+ const heim_integer *hi = (const void *)bn;
+ memcpy(to, hi->data, hi->length);
+ return hi->length;
+}
+
+int
+BN_hex2bn(BIGNUM **bnp, const char *in)
+{
+ int negative;
+ ssize_t ret;
+ size_t len;
+ void *data;
+
+ len = strlen(in);
+ data = malloc(len);
+ if (data == NULL)
+ return 0;
+
+ if (*in == '-') {
+ negative = 1;
+ in++;
+ } else
+ negative = 0;
+
+ ret = hex_decode(in, data, len);
+ if (ret < 0) {
+ free(data);
+ return 0;
+ }
+
+ *bnp = BN_bin2bn(data, ret, NULL);
+ free(data);
+ if (*bnp == NULL)
+ return 0;
+ BN_set_negative(*bnp, negative);
+ return 1;
+}
+
+char *
+BN_bn2hex(const BIGNUM *bn)
+{
+ ssize_t ret;
+ size_t len;
+ void *data;
+ char *str;
+
+ len = BN_num_bytes(bn);
+ data = malloc(len);
+ if (data == NULL)
+ return 0;
+
+ len = BN_bn2bin(bn, data);
+
+ ret = hex_encode(data, len, &str);
+ free(data);
+ if (ret < 0)
+ return 0;
+
+ return str;
+}
+
+int
+BN_cmp(const BIGNUM *bn1, const BIGNUM *bn2)
+{
+ return der_heim_integer_cmp((const heim_integer *)bn1,
+ (const heim_integer *)bn2);
+}
+
+void
+BN_set_negative(BIGNUM *bn, int flag)
+{
+ ((heim_integer *)bn)->negative = (flag ? 1 : 0);
+}
+
+int
+BN_is_negative(const BIGNUM *bn)
+{
+ return ((const heim_integer *)bn)->negative ? 1 : 0;
+}
+
+static const unsigned char is_set[8] = { 1, 2, 4, 8, 16, 32, 64, 128 };
+
+int
+BN_is_bit_set(const BIGNUM *bn, int bit)
+{
+ heim_integer *hi = (heim_integer *)bn;
+ unsigned char *p = hi->data;
+
+ if ((bit / 8) > hi->length || hi->length == 0)
+ return 0;
+
+ return p[hi->length - 1 - (bit / 8)] & is_set[bit % 8];
+}
+
+int
+BN_set_bit(BIGNUM *bn, int bit)
+{
+ heim_integer *hi = (heim_integer *)bn;
+ unsigned char *p;
+
+ if ((bit / 8) > hi->length || hi->length == 0) {
+ size_t len = (bit + 7) / 8;
+ void *d = realloc(hi->data, len);
+ if (d == NULL)
+ return 0;
+ hi->data = d;
+ p = hi->data;
+ memset(&p[hi->length], 0, len);
+ hi->length = len;
+ } else
+ p = hi->data;
+
+ p[hi->length - 1 - (bit / 8)] |= is_set[bit % 8];
+ return 1;
+}
+
+int
+BN_clear_bit(BIGNUM *bn, int bit)
+{
+ heim_integer *hi = (heim_integer *)bn;
+ unsigned char *p = hi->data;
+
+ if ((bit / 8) > hi->length || hi->length == 0)
+ return 0;
+
+ p[hi->length - 1 - (bit / 8)] &= (unsigned char)(~(is_set[bit % 8]));
+
+ return 1;
+}
+
+int
+BN_set_word(BIGNUM *bn, unsigned long num)
+{
+ unsigned char p[sizeof(num)];
+ unsigned long num2;
+ int i, len;
+
+ for (num2 = num, i = 0; num2 > 0; i++)
+ num2 = num2 >> 8;
+
+ len = i;
+ for (; i > 0; i--) {
+ p[i - 1] = (num & 0xff);
+ num = num >> 8;
+ }
+
+ bn = BN_bin2bn(p, len, bn);
+ return bn != NULL;
+}
+
+unsigned long
+BN_get_word(const BIGNUM *bn)
+{
+ heim_integer *hi = (heim_integer *)bn;
+ unsigned long num = 0;
+ int i;
+
+ if (hi->negative || hi->length > sizeof(num))
+ return ULONG_MAX;
+
+ for (i = 0; i < hi->length; i++)
+ num = ((unsigned char *)hi->data)[i] | (num << 8);
+ return num;
+}
+
+int
+BN_rand(BIGNUM *bn, int bits, int top, int bottom)
+{
+ size_t len = (bits + 7) / 8;
+ heim_integer *i = (heim_integer *)bn;
+
+ BN_clear(bn);
+
+ i->negative = 0;
+ i->data = malloc(len);
+ if (i->data == NULL && len != 0)
+ return 0;
+ i->length = len;
+
+ if (RAND_bytes(i->data, i->length) != 1) {
+ free(i->data);
+ i->data = NULL;
+ return 0;
+ }
+
+ {
+ size_t j = len * 8;
+ while(j > bits) {
+ BN_clear_bit(bn, j - 1);
+ j--;
+ }
+ }
+
+ if (top == -1) {
+ ;
+ } else if (top == 0 && bits > 0) {
+ BN_set_bit(bn, bits - 1);
+ } else if (top == 1 && bits > 1) {
+ BN_set_bit(bn, bits - 1);
+ BN_set_bit(bn, bits - 2);
+ } else {
+ BN_clear(bn);
+ return 0;
+ }
+
+ if (bottom && bits > 0)
+ BN_set_bit(bn, 0);
+
+ return 1;
+}
+
+/*
+ *
+ */
+
+int
+BN_uadd(BIGNUM *res, const BIGNUM *a, const BIGNUM *b)
+{
+ const heim_integer *ai = (const heim_integer *)a;
+ const heim_integer *bi = (const heim_integer *)b;
+ const unsigned char *ap, *bp;
+ unsigned char *cp;
+ heim_integer ci;
+ int carry = 0;
+ ssize_t len;
+
+ if (ai->negative && bi->negative)
+ return 0;
+ if (ai->length < bi->length) {
+ const heim_integer *si = bi;
+ bi = ai; ai = si;
+ }
+
+ ci.negative = 0;
+ ci.length = ai->length + 1;
+ ci.data = malloc(ci.length);
+ if (ci.data == NULL)
+ return 0;
+
+ ap = &((const unsigned char *)ai->data)[ai->length - 1];
+ bp = &((const unsigned char *)bi->data)[bi->length - 1];
+ cp = &((unsigned char *)ci.data)[ci.length - 1];
+
+ for (len = bi->length; len > 0; len--) {
+ carry = *ap + *bp + carry;
+ *cp = carry & 0xff;
+ carry = (carry & ~0xff) ? 1 : 0;
+ ap--; bp--; cp--;
+ }
+ for (len = ai->length - bi->length; len > 0; len--) {
+ carry = *ap + carry;
+ *cp = carry & 0xff;
+ carry = (carry & ~0xff) ? 1 : 0;
+ ap--; cp--;
+ }
+ if (!carry)
+ memmove(cp, cp + 1, --ci.length);
+ else
+ *cp = carry;
+
+ BN_clear(res);
+ *((heim_integer *)res) = ci;
+
+ return 1;
+}
+
+
+/*
+ * Callback when doing slow generation of numbers, like primes.
+ */
+
+void
+BN_GENCB_set(BN_GENCB *gencb, int (*cb_2)(int, int, BN_GENCB *), void *ctx)
+{
+ gencb->ver = 2;
+ gencb->cb.cb_2 = cb_2;
+ gencb->arg = ctx;
+}
+
+int
+BN_GENCB_call(BN_GENCB *cb, int a, int b)
+{
+ if (cb == NULL || cb->cb.cb_2 == NULL)
+ return 1;
+ return cb->cb.cb_2(a, b, cb);
+}
diff --git a/source4/heimdal/lib/hcrypto/bn.h b/source4/heimdal/lib/hcrypto/bn.h
new file mode 100644
index 0000000000..aac770b5a8
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/bn.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id$
+ */
+
+#ifndef _HEIM_BN_H
+#define _HEIM_BN_H 1
+
+/* symbol renaming */
+#define BN_GENCB_call hc_BN_GENCB_call
+#define BN_GENCB_set hc_BN_GENCB_set
+#define BN_bin2bn hc_BN_bin2bn
+#define BN_bn2bin hc_BN_bn2bin
+#define BN_bn2hex hc_BN_bn2hex
+#define BN_clear hc_BN_clear
+#define BN_clear_bit hc_BN_clear_bit
+#define BN_clear_free hc_BN_clear_free
+#define BN_cmp hc_BN_cmp
+#define BN_dup hc_BN_dup
+#define BN_free hc_BN_free
+#define BN_is_negative hc_BN_is_negative
+#define BN_get_word hc_BN_get_word
+#define BN_hex2bn hc_BN_hex2bn
+#define BN_is_bit_set hc_BN_is_bit_set
+#define BN_new hc_BN_new
+#define BN_num_bits hc_BN_num_bits
+#define BN_num_bytes hc_BN_num_bytes
+#define BN_rand hc_BN_rand
+#define BN_set_bit hc_BN_set_bit
+#define BN_set_negative hc_BN_set_negative
+#define BN_set_word hc_BN_set_word
+#define BN_uadd hc_BN_uadd
+
+/*
+ *
+ */
+
+typedef void BIGNUM;
+typedef struct BN_GENCB BN_GENCB;
+typedef void BN_CTX;
+typedef void BN_MONT_CTX;
+typedef void BN_BLINDING;
+
+struct BN_GENCB {
+ unsigned int ver;
+ void *arg;
+ union {
+ int (*cb_2)(int, int, BN_GENCB *);
+ } cb;
+};
+
+/*
+ *
+ */
+
+BIGNUM *BN_new(void);
+void BN_free(BIGNUM *);
+void BN_clear_free(BIGNUM *);
+void BN_clear(BIGNUM *);
+BIGNUM *BN_dup(const BIGNUM *);
+
+int BN_num_bits(const BIGNUM *);
+int BN_num_bytes(const BIGNUM *);
+
+int BN_cmp(const BIGNUM *, const BIGNUM *);
+
+void BN_set_negative(BIGNUM *, int);
+int BN_is_negative(const BIGNUM *);
+
+int BN_is_bit_set(const BIGNUM *, int);
+int BN_set_bit(BIGNUM *, int);
+int BN_clear_bit(BIGNUM *, int);
+
+int BN_set_word(BIGNUM *, unsigned long);
+unsigned long BN_get_word(const BIGNUM *);
+
+BIGNUM *BN_bin2bn(const void *,int len,BIGNUM *);
+int BN_bn2bin(const BIGNUM *, void *);
+int BN_hex2bn(BIGNUM **, const char *);
+char * BN_bn2hex(const BIGNUM *);
+
+int BN_uadd(BIGNUM *, const BIGNUM *, const BIGNUM *);
+
+int BN_rand(BIGNUM *, int, int, int);
+
+void BN_GENCB_set(BN_GENCB *, int (*)(int, int, BN_GENCB *), void *);
+int BN_GENCB_call(BN_GENCB *, int, int);
+
+#endif
diff --git a/source4/heimdal/lib/hcrypto/camellia-ntt.c b/source4/heimdal/lib/hcrypto/camellia-ntt.c
new file mode 100644
index 0000000000..358221162f
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/camellia-ntt.c
@@ -0,0 +1,1464 @@
+/* camellia.h ver 1.2.0
+ *
+ * Copyright (C) 2006,2007
+ * NTT (Nippon Telegraph and Telephone Corporation).
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Algorithm Specification
+ * http://info.isl.ntt.co.jp/crypt/eng/camellia/specifications.html
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <roken.h>
+
+#include <string.h>
+#include <stdlib.h>
+
+#include <krb5-types.h>
+#include "camellia-ntt.h"
+
+/* key constants */
+
+#define CAMELLIA_SIGMA1L (0xA09E667FL)
+#define CAMELLIA_SIGMA1R (0x3BCC908BL)
+#define CAMELLIA_SIGMA2L (0xB67AE858L)
+#define CAMELLIA_SIGMA2R (0x4CAA73B2L)
+#define CAMELLIA_SIGMA3L (0xC6EF372FL)
+#define CAMELLIA_SIGMA3R (0xE94F82BEL)
+#define CAMELLIA_SIGMA4L (0x54FF53A5L)
+#define CAMELLIA_SIGMA4R (0xF1D36F1CL)
+#define CAMELLIA_SIGMA5L (0x10E527FAL)
+#define CAMELLIA_SIGMA5R (0xDE682D1DL)
+#define CAMELLIA_SIGMA6L (0xB05688C2L)
+#define CAMELLIA_SIGMA6R (0xB3E6C1FDL)
+
+/*
+ * macros
+ */
+
+
+#if defined(_MSC_VER)
+
+# define SWAP(x) (_lrotl(x, 8) & 0x00ff00ff | _lrotr(x, 8) & 0xff00ff00)
+# define GETU32(p) SWAP(*((u32 *)(p)))
+# define PUTU32(ct, st) {*((u32 *)(ct)) = SWAP((st));}
+
+#else /* not MS-VC */
+
+# define GETU32(pt) \
+ (((u32)(pt)[0] << 24) \
+ ^ ((u32)(pt)[1] << 16) \
+ ^ ((u32)(pt)[2] << 8) \
+ ^ ((u32)(pt)[3]))
+
+# define PUTU32(ct, st) { \
+ (ct)[0] = (u8)((st) >> 24); \
+ (ct)[1] = (u8)((st) >> 16); \
+ (ct)[2] = (u8)((st) >> 8); \
+ (ct)[3] = (u8)(st); }
+
+#endif
+
+#define CamelliaSubkeyL(INDEX) (subkey[(INDEX)*2])
+#define CamelliaSubkeyR(INDEX) (subkey[(INDEX)*2 + 1])
+
+/* rotation right shift 1byte */
+#define CAMELLIA_RR8(x) (((x) >> 8) + ((x) << 24))
+/* rotation left shift 1bit */
+#define CAMELLIA_RL1(x) (((x) << 1) + ((x) >> 31))
+/* rotation left shift 1byte */
+#define CAMELLIA_RL8(x) (((x) << 8) + ((x) >> 24))
+
+#define CAMELLIA_ROLDQ(ll, lr, rl, rr, w0, w1, bits) \
+ do { \
+ w0 = ll; \
+ ll = (ll << bits) + (lr >> (32 - bits)); \
+ lr = (lr << bits) + (rl >> (32 - bits)); \
+ rl = (rl << bits) + (rr >> (32 - bits)); \
+ rr = (rr << bits) + (w0 >> (32 - bits)); \
+ } while(0)
+
+#define CAMELLIA_ROLDQo32(ll, lr, rl, rr, w0, w1, bits) \
+ do { \
+ w0 = ll; \
+ w1 = lr; \
+ ll = (lr << (bits - 32)) + (rl >> (64 - bits)); \
+ lr = (rl << (bits - 32)) + (rr >> (64 - bits)); \
+ rl = (rr << (bits - 32)) + (w0 >> (64 - bits)); \
+ rr = (w0 << (bits - 32)) + (w1 >> (64 - bits)); \
+ } while(0)
+
+#define CAMELLIA_SP1110(INDEX) (camellia_sp1110[(INDEX)])
+#define CAMELLIA_SP0222(INDEX) (camellia_sp0222[(INDEX)])
+#define CAMELLIA_SP3033(INDEX) (camellia_sp3033[(INDEX)])
+#define CAMELLIA_SP4404(INDEX) (camellia_sp4404[(INDEX)])
+
+#define CAMELLIA_F(xl, xr, kl, kr, yl, yr, il, ir, t0, t1) \
+ do { \
+ il = xl ^ kl; \
+ ir = xr ^ kr; \
+ t0 = il >> 16; \
+ t1 = ir >> 16; \
+ yl = CAMELLIA_SP1110(ir & 0xff) \
+ ^ CAMELLIA_SP0222((t1 >> 8) & 0xff) \
+ ^ CAMELLIA_SP3033(t1 & 0xff) \
+ ^ CAMELLIA_SP4404((ir >> 8) & 0xff); \
+ yr = CAMELLIA_SP1110((t0 >> 8) & 0xff) \
+ ^ CAMELLIA_SP0222(t0 & 0xff) \
+ ^ CAMELLIA_SP3033((il >> 8) & 0xff) \
+ ^ CAMELLIA_SP4404(il & 0xff); \
+ yl ^= yr; \
+ yr = CAMELLIA_RR8(yr); \
+ yr ^= yl; \
+ } while(0)
+
+
+/*
+ * for speed up
+ *
+ */
+#define CAMELLIA_FLS(ll, lr, rl, rr, kll, klr, krl, krr, t0, t1, t2, t3) \
+ do { \
+ t0 = kll; \
+ t0 &= ll; \
+ lr ^= CAMELLIA_RL1(t0); \
+ t1 = klr; \
+ t1 |= lr; \
+ ll ^= t1; \
+ \
+ t2 = krr; \
+ t2 |= rr; \
+ rl ^= t2; \
+ t3 = krl; \
+ t3 &= rl; \
+ rr ^= CAMELLIA_RL1(t3); \
+ } while(0)
+
+#define CAMELLIA_ROUNDSM(xl, xr, kl, kr, yl, yr, il, ir, t0, t1) \
+ do { \
+ ir = CAMELLIA_SP1110(xr & 0xff) \
+ ^ CAMELLIA_SP0222((xr >> 24) & 0xff) \
+ ^ CAMELLIA_SP3033((xr >> 16) & 0xff) \
+ ^ CAMELLIA_SP4404((xr >> 8) & 0xff); \
+ il = CAMELLIA_SP1110((xl >> 24) & 0xff) \
+ ^ CAMELLIA_SP0222((xl >> 16) & 0xff) \
+ ^ CAMELLIA_SP3033((xl >> 8) & 0xff) \
+ ^ CAMELLIA_SP4404(xl & 0xff); \
+ il ^= kl; \
+ ir ^= kr; \
+ ir ^= il; \
+ il = CAMELLIA_RR8(il); \
+ il ^= ir; \
+ yl ^= ir; \
+ yr ^= il; \
+ } while(0)
+
+
+static const u32 camellia_sp1110[256] = {
+ 0x70707000,0x82828200,0x2c2c2c00,0xececec00,
+ 0xb3b3b300,0x27272700,0xc0c0c000,0xe5e5e500,
+ 0xe4e4e400,0x85858500,0x57575700,0x35353500,
+ 0xeaeaea00,0x0c0c0c00,0xaeaeae00,0x41414100,
+ 0x23232300,0xefefef00,0x6b6b6b00,0x93939300,
+ 0x45454500,0x19191900,0xa5a5a500,0x21212100,
+ 0xededed00,0x0e0e0e00,0x4f4f4f00,0x4e4e4e00,
+ 0x1d1d1d00,0x65656500,0x92929200,0xbdbdbd00,
+ 0x86868600,0xb8b8b800,0xafafaf00,0x8f8f8f00,
+ 0x7c7c7c00,0xebebeb00,0x1f1f1f00,0xcecece00,
+ 0x3e3e3e00,0x30303000,0xdcdcdc00,0x5f5f5f00,
+ 0x5e5e5e00,0xc5c5c500,0x0b0b0b00,0x1a1a1a00,
+ 0xa6a6a600,0xe1e1e100,0x39393900,0xcacaca00,
+ 0xd5d5d500,0x47474700,0x5d5d5d00,0x3d3d3d00,
+ 0xd9d9d900,0x01010100,0x5a5a5a00,0xd6d6d600,
+ 0x51515100,0x56565600,0x6c6c6c00,0x4d4d4d00,
+ 0x8b8b8b00,0x0d0d0d00,0x9a9a9a00,0x66666600,
+ 0xfbfbfb00,0xcccccc00,0xb0b0b000,0x2d2d2d00,
+ 0x74747400,0x12121200,0x2b2b2b00,0x20202000,
+ 0xf0f0f000,0xb1b1b100,0x84848400,0x99999900,
+ 0xdfdfdf00,0x4c4c4c00,0xcbcbcb00,0xc2c2c200,
+ 0x34343400,0x7e7e7e00,0x76767600,0x05050500,
+ 0x6d6d6d00,0xb7b7b700,0xa9a9a900,0x31313100,
+ 0xd1d1d100,0x17171700,0x04040400,0xd7d7d700,
+ 0x14141400,0x58585800,0x3a3a3a00,0x61616100,
+ 0xdedede00,0x1b1b1b00,0x11111100,0x1c1c1c00,
+ 0x32323200,0x0f0f0f00,0x9c9c9c00,0x16161600,
+ 0x53535300,0x18181800,0xf2f2f200,0x22222200,
+ 0xfefefe00,0x44444400,0xcfcfcf00,0xb2b2b200,
+ 0xc3c3c300,0xb5b5b500,0x7a7a7a00,0x91919100,
+ 0x24242400,0x08080800,0xe8e8e800,0xa8a8a800,
+ 0x60606000,0xfcfcfc00,0x69696900,0x50505000,
+ 0xaaaaaa00,0xd0d0d000,0xa0a0a000,0x7d7d7d00,
+ 0xa1a1a100,0x89898900,0x62626200,0x97979700,
+ 0x54545400,0x5b5b5b00,0x1e1e1e00,0x95959500,
+ 0xe0e0e000,0xffffff00,0x64646400,0xd2d2d200,
+ 0x10101000,0xc4c4c400,0x00000000,0x48484800,
+ 0xa3a3a300,0xf7f7f700,0x75757500,0xdbdbdb00,
+ 0x8a8a8a00,0x03030300,0xe6e6e600,0xdadada00,
+ 0x09090900,0x3f3f3f00,0xdddddd00,0x94949400,
+ 0x87878700,0x5c5c5c00,0x83838300,0x02020200,
+ 0xcdcdcd00,0x4a4a4a00,0x90909000,0x33333300,
+ 0x73737300,0x67676700,0xf6f6f600,0xf3f3f300,
+ 0x9d9d9d00,0x7f7f7f00,0xbfbfbf00,0xe2e2e200,
+ 0x52525200,0x9b9b9b00,0xd8d8d800,0x26262600,
+ 0xc8c8c800,0x37373700,0xc6c6c600,0x3b3b3b00,
+ 0x81818100,0x96969600,0x6f6f6f00,0x4b4b4b00,
+ 0x13131300,0xbebebe00,0x63636300,0x2e2e2e00,
+ 0xe9e9e900,0x79797900,0xa7a7a700,0x8c8c8c00,
+ 0x9f9f9f00,0x6e6e6e00,0xbcbcbc00,0x8e8e8e00,
+ 0x29292900,0xf5f5f500,0xf9f9f900,0xb6b6b600,
+ 0x2f2f2f00,0xfdfdfd00,0xb4b4b400,0x59595900,
+ 0x78787800,0x98989800,0x06060600,0x6a6a6a00,
+ 0xe7e7e700,0x46464600,0x71717100,0xbababa00,
+ 0xd4d4d400,0x25252500,0xababab00,0x42424200,
+ 0x88888800,0xa2a2a200,0x8d8d8d00,0xfafafa00,
+ 0x72727200,0x07070700,0xb9b9b900,0x55555500,
+ 0xf8f8f800,0xeeeeee00,0xacacac00,0x0a0a0a00,
+ 0x36363600,0x49494900,0x2a2a2a00,0x68686800,
+ 0x3c3c3c00,0x38383800,0xf1f1f100,0xa4a4a400,
+ 0x40404000,0x28282800,0xd3d3d300,0x7b7b7b00,
+ 0xbbbbbb00,0xc9c9c900,0x43434300,0xc1c1c100,
+ 0x15151500,0xe3e3e300,0xadadad00,0xf4f4f400,
+ 0x77777700,0xc7c7c700,0x80808000,0x9e9e9e00,
+};
+
+static const u32 camellia_sp0222[256] = {
+ 0x00e0e0e0,0x00050505,0x00585858,0x00d9d9d9,
+ 0x00676767,0x004e4e4e,0x00818181,0x00cbcbcb,
+ 0x00c9c9c9,0x000b0b0b,0x00aeaeae,0x006a6a6a,
+ 0x00d5d5d5,0x00181818,0x005d5d5d,0x00828282,
+ 0x00464646,0x00dfdfdf,0x00d6d6d6,0x00272727,
+ 0x008a8a8a,0x00323232,0x004b4b4b,0x00424242,
+ 0x00dbdbdb,0x001c1c1c,0x009e9e9e,0x009c9c9c,
+ 0x003a3a3a,0x00cacaca,0x00252525,0x007b7b7b,
+ 0x000d0d0d,0x00717171,0x005f5f5f,0x001f1f1f,
+ 0x00f8f8f8,0x00d7d7d7,0x003e3e3e,0x009d9d9d,
+ 0x007c7c7c,0x00606060,0x00b9b9b9,0x00bebebe,
+ 0x00bcbcbc,0x008b8b8b,0x00161616,0x00343434,
+ 0x004d4d4d,0x00c3c3c3,0x00727272,0x00959595,
+ 0x00ababab,0x008e8e8e,0x00bababa,0x007a7a7a,
+ 0x00b3b3b3,0x00020202,0x00b4b4b4,0x00adadad,
+ 0x00a2a2a2,0x00acacac,0x00d8d8d8,0x009a9a9a,
+ 0x00171717,0x001a1a1a,0x00353535,0x00cccccc,
+ 0x00f7f7f7,0x00999999,0x00616161,0x005a5a5a,
+ 0x00e8e8e8,0x00242424,0x00565656,0x00404040,
+ 0x00e1e1e1,0x00636363,0x00090909,0x00333333,
+ 0x00bfbfbf,0x00989898,0x00979797,0x00858585,
+ 0x00686868,0x00fcfcfc,0x00ececec,0x000a0a0a,
+ 0x00dadada,0x006f6f6f,0x00535353,0x00626262,
+ 0x00a3a3a3,0x002e2e2e,0x00080808,0x00afafaf,
+ 0x00282828,0x00b0b0b0,0x00747474,0x00c2c2c2,
+ 0x00bdbdbd,0x00363636,0x00222222,0x00383838,
+ 0x00646464,0x001e1e1e,0x00393939,0x002c2c2c,
+ 0x00a6a6a6,0x00303030,0x00e5e5e5,0x00444444,
+ 0x00fdfdfd,0x00888888,0x009f9f9f,0x00656565,
+ 0x00878787,0x006b6b6b,0x00f4f4f4,0x00232323,
+ 0x00484848,0x00101010,0x00d1d1d1,0x00515151,
+ 0x00c0c0c0,0x00f9f9f9,0x00d2d2d2,0x00a0a0a0,
+ 0x00555555,0x00a1a1a1,0x00414141,0x00fafafa,
+ 0x00434343,0x00131313,0x00c4c4c4,0x002f2f2f,
+ 0x00a8a8a8,0x00b6b6b6,0x003c3c3c,0x002b2b2b,
+ 0x00c1c1c1,0x00ffffff,0x00c8c8c8,0x00a5a5a5,
+ 0x00202020,0x00898989,0x00000000,0x00909090,
+ 0x00474747,0x00efefef,0x00eaeaea,0x00b7b7b7,
+ 0x00151515,0x00060606,0x00cdcdcd,0x00b5b5b5,
+ 0x00121212,0x007e7e7e,0x00bbbbbb,0x00292929,
+ 0x000f0f0f,0x00b8b8b8,0x00070707,0x00040404,
+ 0x009b9b9b,0x00949494,0x00212121,0x00666666,
+ 0x00e6e6e6,0x00cecece,0x00ededed,0x00e7e7e7,
+ 0x003b3b3b,0x00fefefe,0x007f7f7f,0x00c5c5c5,
+ 0x00a4a4a4,0x00373737,0x00b1b1b1,0x004c4c4c,
+ 0x00919191,0x006e6e6e,0x008d8d8d,0x00767676,
+ 0x00030303,0x002d2d2d,0x00dedede,0x00969696,
+ 0x00262626,0x007d7d7d,0x00c6c6c6,0x005c5c5c,
+ 0x00d3d3d3,0x00f2f2f2,0x004f4f4f,0x00191919,
+ 0x003f3f3f,0x00dcdcdc,0x00797979,0x001d1d1d,
+ 0x00525252,0x00ebebeb,0x00f3f3f3,0x006d6d6d,
+ 0x005e5e5e,0x00fbfbfb,0x00696969,0x00b2b2b2,
+ 0x00f0f0f0,0x00313131,0x000c0c0c,0x00d4d4d4,
+ 0x00cfcfcf,0x008c8c8c,0x00e2e2e2,0x00757575,
+ 0x00a9a9a9,0x004a4a4a,0x00575757,0x00848484,
+ 0x00111111,0x00454545,0x001b1b1b,0x00f5f5f5,
+ 0x00e4e4e4,0x000e0e0e,0x00737373,0x00aaaaaa,
+ 0x00f1f1f1,0x00dddddd,0x00595959,0x00141414,
+ 0x006c6c6c,0x00929292,0x00545454,0x00d0d0d0,
+ 0x00787878,0x00707070,0x00e3e3e3,0x00494949,
+ 0x00808080,0x00505050,0x00a7a7a7,0x00f6f6f6,
+ 0x00777777,0x00939393,0x00868686,0x00838383,
+ 0x002a2a2a,0x00c7c7c7,0x005b5b5b,0x00e9e9e9,
+ 0x00eeeeee,0x008f8f8f,0x00010101,0x003d3d3d,
+};
+
+static const u32 camellia_sp3033[256] = {
+ 0x38003838,0x41004141,0x16001616,0x76007676,
+ 0xd900d9d9,0x93009393,0x60006060,0xf200f2f2,
+ 0x72007272,0xc200c2c2,0xab00abab,0x9a009a9a,
+ 0x75007575,0x06000606,0x57005757,0xa000a0a0,
+ 0x91009191,0xf700f7f7,0xb500b5b5,0xc900c9c9,
+ 0xa200a2a2,0x8c008c8c,0xd200d2d2,0x90009090,
+ 0xf600f6f6,0x07000707,0xa700a7a7,0x27002727,
+ 0x8e008e8e,0xb200b2b2,0x49004949,0xde00dede,
+ 0x43004343,0x5c005c5c,0xd700d7d7,0xc700c7c7,
+ 0x3e003e3e,0xf500f5f5,0x8f008f8f,0x67006767,
+ 0x1f001f1f,0x18001818,0x6e006e6e,0xaf00afaf,
+ 0x2f002f2f,0xe200e2e2,0x85008585,0x0d000d0d,
+ 0x53005353,0xf000f0f0,0x9c009c9c,0x65006565,
+ 0xea00eaea,0xa300a3a3,0xae00aeae,0x9e009e9e,
+ 0xec00ecec,0x80008080,0x2d002d2d,0x6b006b6b,
+ 0xa800a8a8,0x2b002b2b,0x36003636,0xa600a6a6,
+ 0xc500c5c5,0x86008686,0x4d004d4d,0x33003333,
+ 0xfd00fdfd,0x66006666,0x58005858,0x96009696,
+ 0x3a003a3a,0x09000909,0x95009595,0x10001010,
+ 0x78007878,0xd800d8d8,0x42004242,0xcc00cccc,
+ 0xef00efef,0x26002626,0xe500e5e5,0x61006161,
+ 0x1a001a1a,0x3f003f3f,0x3b003b3b,0x82008282,
+ 0xb600b6b6,0xdb00dbdb,0xd400d4d4,0x98009898,
+ 0xe800e8e8,0x8b008b8b,0x02000202,0xeb00ebeb,
+ 0x0a000a0a,0x2c002c2c,0x1d001d1d,0xb000b0b0,
+ 0x6f006f6f,0x8d008d8d,0x88008888,0x0e000e0e,
+ 0x19001919,0x87008787,0x4e004e4e,0x0b000b0b,
+ 0xa900a9a9,0x0c000c0c,0x79007979,0x11001111,
+ 0x7f007f7f,0x22002222,0xe700e7e7,0x59005959,
+ 0xe100e1e1,0xda00dada,0x3d003d3d,0xc800c8c8,
+ 0x12001212,0x04000404,0x74007474,0x54005454,
+ 0x30003030,0x7e007e7e,0xb400b4b4,0x28002828,
+ 0x55005555,0x68006868,0x50005050,0xbe00bebe,
+ 0xd000d0d0,0xc400c4c4,0x31003131,0xcb00cbcb,
+ 0x2a002a2a,0xad00adad,0x0f000f0f,0xca00caca,
+ 0x70007070,0xff00ffff,0x32003232,0x69006969,
+ 0x08000808,0x62006262,0x00000000,0x24002424,
+ 0xd100d1d1,0xfb00fbfb,0xba00baba,0xed00eded,
+ 0x45004545,0x81008181,0x73007373,0x6d006d6d,
+ 0x84008484,0x9f009f9f,0xee00eeee,0x4a004a4a,
+ 0xc300c3c3,0x2e002e2e,0xc100c1c1,0x01000101,
+ 0xe600e6e6,0x25002525,0x48004848,0x99009999,
+ 0xb900b9b9,0xb300b3b3,0x7b007b7b,0xf900f9f9,
+ 0xce00cece,0xbf00bfbf,0xdf00dfdf,0x71007171,
+ 0x29002929,0xcd00cdcd,0x6c006c6c,0x13001313,
+ 0x64006464,0x9b009b9b,0x63006363,0x9d009d9d,
+ 0xc000c0c0,0x4b004b4b,0xb700b7b7,0xa500a5a5,
+ 0x89008989,0x5f005f5f,0xb100b1b1,0x17001717,
+ 0xf400f4f4,0xbc00bcbc,0xd300d3d3,0x46004646,
+ 0xcf00cfcf,0x37003737,0x5e005e5e,0x47004747,
+ 0x94009494,0xfa00fafa,0xfc00fcfc,0x5b005b5b,
+ 0x97009797,0xfe00fefe,0x5a005a5a,0xac00acac,
+ 0x3c003c3c,0x4c004c4c,0x03000303,0x35003535,
+ 0xf300f3f3,0x23002323,0xb800b8b8,0x5d005d5d,
+ 0x6a006a6a,0x92009292,0xd500d5d5,0x21002121,
+ 0x44004444,0x51005151,0xc600c6c6,0x7d007d7d,
+ 0x39003939,0x83008383,0xdc00dcdc,0xaa00aaaa,
+ 0x7c007c7c,0x77007777,0x56005656,0x05000505,
+ 0x1b001b1b,0xa400a4a4,0x15001515,0x34003434,
+ 0x1e001e1e,0x1c001c1c,0xf800f8f8,0x52005252,
+ 0x20002020,0x14001414,0xe900e9e9,0xbd00bdbd,
+ 0xdd00dddd,0xe400e4e4,0xa100a1a1,0xe000e0e0,
+ 0x8a008a8a,0xf100f1f1,0xd600d6d6,0x7a007a7a,
+ 0xbb00bbbb,0xe300e3e3,0x40004040,0x4f004f4f,
+};
+
+static const u32 camellia_sp4404[256] = {
+ 0x70700070,0x2c2c002c,0xb3b300b3,0xc0c000c0,
+ 0xe4e400e4,0x57570057,0xeaea00ea,0xaeae00ae,
+ 0x23230023,0x6b6b006b,0x45450045,0xa5a500a5,
+ 0xeded00ed,0x4f4f004f,0x1d1d001d,0x92920092,
+ 0x86860086,0xafaf00af,0x7c7c007c,0x1f1f001f,
+ 0x3e3e003e,0xdcdc00dc,0x5e5e005e,0x0b0b000b,
+ 0xa6a600a6,0x39390039,0xd5d500d5,0x5d5d005d,
+ 0xd9d900d9,0x5a5a005a,0x51510051,0x6c6c006c,
+ 0x8b8b008b,0x9a9a009a,0xfbfb00fb,0xb0b000b0,
+ 0x74740074,0x2b2b002b,0xf0f000f0,0x84840084,
+ 0xdfdf00df,0xcbcb00cb,0x34340034,0x76760076,
+ 0x6d6d006d,0xa9a900a9,0xd1d100d1,0x04040004,
+ 0x14140014,0x3a3a003a,0xdede00de,0x11110011,
+ 0x32320032,0x9c9c009c,0x53530053,0xf2f200f2,
+ 0xfefe00fe,0xcfcf00cf,0xc3c300c3,0x7a7a007a,
+ 0x24240024,0xe8e800e8,0x60600060,0x69690069,
+ 0xaaaa00aa,0xa0a000a0,0xa1a100a1,0x62620062,
+ 0x54540054,0x1e1e001e,0xe0e000e0,0x64640064,
+ 0x10100010,0x00000000,0xa3a300a3,0x75750075,
+ 0x8a8a008a,0xe6e600e6,0x09090009,0xdddd00dd,
+ 0x87870087,0x83830083,0xcdcd00cd,0x90900090,
+ 0x73730073,0xf6f600f6,0x9d9d009d,0xbfbf00bf,
+ 0x52520052,0xd8d800d8,0xc8c800c8,0xc6c600c6,
+ 0x81810081,0x6f6f006f,0x13130013,0x63630063,
+ 0xe9e900e9,0xa7a700a7,0x9f9f009f,0xbcbc00bc,
+ 0x29290029,0xf9f900f9,0x2f2f002f,0xb4b400b4,
+ 0x78780078,0x06060006,0xe7e700e7,0x71710071,
+ 0xd4d400d4,0xabab00ab,0x88880088,0x8d8d008d,
+ 0x72720072,0xb9b900b9,0xf8f800f8,0xacac00ac,
+ 0x36360036,0x2a2a002a,0x3c3c003c,0xf1f100f1,
+ 0x40400040,0xd3d300d3,0xbbbb00bb,0x43430043,
+ 0x15150015,0xadad00ad,0x77770077,0x80800080,
+ 0x82820082,0xecec00ec,0x27270027,0xe5e500e5,
+ 0x85850085,0x35350035,0x0c0c000c,0x41410041,
+ 0xefef00ef,0x93930093,0x19190019,0x21210021,
+ 0x0e0e000e,0x4e4e004e,0x65650065,0xbdbd00bd,
+ 0xb8b800b8,0x8f8f008f,0xebeb00eb,0xcece00ce,
+ 0x30300030,0x5f5f005f,0xc5c500c5,0x1a1a001a,
+ 0xe1e100e1,0xcaca00ca,0x47470047,0x3d3d003d,
+ 0x01010001,0xd6d600d6,0x56560056,0x4d4d004d,
+ 0x0d0d000d,0x66660066,0xcccc00cc,0x2d2d002d,
+ 0x12120012,0x20200020,0xb1b100b1,0x99990099,
+ 0x4c4c004c,0xc2c200c2,0x7e7e007e,0x05050005,
+ 0xb7b700b7,0x31310031,0x17170017,0xd7d700d7,
+ 0x58580058,0x61610061,0x1b1b001b,0x1c1c001c,
+ 0x0f0f000f,0x16160016,0x18180018,0x22220022,
+ 0x44440044,0xb2b200b2,0xb5b500b5,0x91910091,
+ 0x08080008,0xa8a800a8,0xfcfc00fc,0x50500050,
+ 0xd0d000d0,0x7d7d007d,0x89890089,0x97970097,
+ 0x5b5b005b,0x95950095,0xffff00ff,0xd2d200d2,
+ 0xc4c400c4,0x48480048,0xf7f700f7,0xdbdb00db,
+ 0x03030003,0xdada00da,0x3f3f003f,0x94940094,
+ 0x5c5c005c,0x02020002,0x4a4a004a,0x33330033,
+ 0x67670067,0xf3f300f3,0x7f7f007f,0xe2e200e2,
+ 0x9b9b009b,0x26260026,0x37370037,0x3b3b003b,
+ 0x96960096,0x4b4b004b,0xbebe00be,0x2e2e002e,
+ 0x79790079,0x8c8c008c,0x6e6e006e,0x8e8e008e,
+ 0xf5f500f5,0xb6b600b6,0xfdfd00fd,0x59590059,
+ 0x98980098,0x6a6a006a,0x46460046,0xbaba00ba,
+ 0x25250025,0x42420042,0xa2a200a2,0xfafa00fa,
+ 0x07070007,0x55550055,0xeeee00ee,0x0a0a000a,
+ 0x49490049,0x68680068,0x38380038,0xa4a400a4,
+ 0x28280028,0x7b7b007b,0xc9c900c9,0xc1c100c1,
+ 0xe3e300e3,0xf4f400f4,0xc7c700c7,0x9e9e009e,
+};
+
+
+/**
+ * Stuff related to the Camellia key schedule
+ */
+#define subl(x) subL[(x)]
+#define subr(x) subR[(x)]
+
+static void camellia_setup128(const unsigned char *key, u32 *subkey)
+{
+ u32 kll, klr, krl, krr;
+ u32 il, ir, t0, t1, w0, w1;
+ u32 kw4l, kw4r, dw, tl, tr;
+ u32 subL[26];
+ u32 subR[26];
+
+ /**
+ * k == kll || klr || krl || krr (|| is concatination)
+ */
+ kll = GETU32(key );
+ klr = GETU32(key + 4);
+ krl = GETU32(key + 8);
+ krr = GETU32(key + 12);
+ /**
+ * generate KL dependent subkeys
+ */
+ subl(0) = kll; subr(0) = klr;
+ subl(1) = krl; subr(1) = krr;
+ CAMELLIA_ROLDQ(kll, klr, krl, krr, w0, w1, 15);
+ subl(4) = kll; subr(4) = klr;
+ subl(5) = krl; subr(5) = krr;
+ CAMELLIA_ROLDQ(kll, klr, krl, krr, w0, w1, 30);
+ subl(10) = kll; subr(10) = klr;
+ subl(11) = krl; subr(11) = krr;
+ CAMELLIA_ROLDQ(kll, klr, krl, krr, w0, w1, 15);
+ subl(13) = krl; subr(13) = krr;
+ CAMELLIA_ROLDQ(kll, klr, krl, krr, w0, w1, 17);
+ subl(16) = kll; subr(16) = klr;
+ subl(17) = krl; subr(17) = krr;
+ CAMELLIA_ROLDQ(kll, klr, krl, krr, w0, w1, 17);
+ subl(18) = kll; subr(18) = klr;
+ subl(19) = krl; subr(19) = krr;
+ CAMELLIA_ROLDQ(kll, klr, krl, krr, w0, w1, 17);
+ subl(22) = kll; subr(22) = klr;
+ subl(23) = krl; subr(23) = krr;
+
+ /* generate KA */
+ kll = subl(0); klr = subr(0);
+ krl = subl(1); krr = subr(1);
+ CAMELLIA_F(kll, klr,
+ CAMELLIA_SIGMA1L, CAMELLIA_SIGMA1R,
+ w0, w1, il, ir, t0, t1);
+ krl ^= w0; krr ^= w1;
+ CAMELLIA_F(krl, krr,
+ CAMELLIA_SIGMA2L, CAMELLIA_SIGMA2R,
+ kll, klr, il, ir, t0, t1);
+ CAMELLIA_F(kll, klr,
+ CAMELLIA_SIGMA3L, CAMELLIA_SIGMA3R,
+ krl, krr, il, ir, t0, t1);
+ krl ^= w0; krr ^= w1;
+ CAMELLIA_F(krl, krr,
+ CAMELLIA_SIGMA4L, CAMELLIA_SIGMA4R,
+ w0, w1, il, ir, t0, t1);
+ kll ^= w0; klr ^= w1;
+
+ /* generate KA dependent subkeys */
+ subl(2) = kll; subr(2) = klr;
+ subl(3) = krl; subr(3) = krr;
+ CAMELLIA_ROLDQ(kll, klr, krl, krr, w0, w1, 15);
+ subl(6) = kll; subr(6) = klr;
+ subl(7) = krl; subr(7) = krr;
+ CAMELLIA_ROLDQ(kll, klr, krl, krr, w0, w1, 15);
+ subl(8) = kll; subr(8) = klr;
+ subl(9) = krl; subr(9) = krr;
+ CAMELLIA_ROLDQ(kll, klr, krl, krr, w0, w1, 15);
+ subl(12) = kll; subr(12) = klr;
+ CAMELLIA_ROLDQ(kll, klr, krl, krr, w0, w1, 15);
+ subl(14) = kll; subr(14) = klr;
+ subl(15) = krl; subr(15) = krr;
+ CAMELLIA_ROLDQo32(kll, klr, krl, krr, w0, w1, 34);
+ subl(20) = kll; subr(20) = klr;
+ subl(21) = krl; subr(21) = krr;
+ CAMELLIA_ROLDQ(kll, klr, krl, krr, w0, w1, 17);
+ subl(24) = kll; subr(24) = klr;
+ subl(25) = krl; subr(25) = krr;
+
+
+ /* absorb kw2 to other subkeys */
+ subl(3) ^= subl(1); subr(3) ^= subr(1);
+ subl(5) ^= subl(1); subr(5) ^= subr(1);
+ subl(7) ^= subl(1); subr(7) ^= subr(1);
+ subl(1) ^= subr(1) & ~subr(9);
+ dw = subl(1) & subl(9), subr(1) ^= CAMELLIA_RL1(dw);
+ subl(11) ^= subl(1); subr(11) ^= subr(1);
+ subl(13) ^= subl(1); subr(13) ^= subr(1);
+ subl(15) ^= subl(1); subr(15) ^= subr(1);
+ subl(1) ^= subr(1) & ~subr(17);
+ dw = subl(1) & subl(17), subr(1) ^= CAMELLIA_RL1(dw);
+ subl(19) ^= subl(1); subr(19) ^= subr(1);
+ subl(21) ^= subl(1); subr(21) ^= subr(1);
+ subl(23) ^= subl(1); subr(23) ^= subr(1);
+ subl(24) ^= subl(1); subr(24) ^= subr(1);
+
+ /* absorb kw4 to other subkeys */
+ kw4l = subl(25); kw4r = subr(25);
+ subl(22) ^= kw4l; subr(22) ^= kw4r;
+ subl(20) ^= kw4l; subr(20) ^= kw4r;
+ subl(18) ^= kw4l; subr(18) ^= kw4r;
+ kw4l ^= kw4r & ~subr(16);
+ dw = kw4l & subl(16), kw4r ^= CAMELLIA_RL1(dw);
+ subl(14) ^= kw4l; subr(14) ^= kw4r;
+ subl(12) ^= kw4l; subr(12) ^= kw4r;
+ subl(10) ^= kw4l; subr(10) ^= kw4r;
+ kw4l ^= kw4r & ~subr(8);
+ dw = kw4l & subl(8), kw4r ^= CAMELLIA_RL1(dw);
+ subl(6) ^= kw4l; subr(6) ^= kw4r;
+ subl(4) ^= kw4l; subr(4) ^= kw4r;
+ subl(2) ^= kw4l; subr(2) ^= kw4r;
+ subl(0) ^= kw4l; subr(0) ^= kw4r;
+
+ /* key XOR is end of F-function */
+ CamelliaSubkeyL(0) = subl(0) ^ subl(2);
+ CamelliaSubkeyR(0) = subr(0) ^ subr(2);
+ CamelliaSubkeyL(2) = subl(3);
+ CamelliaSubkeyR(2) = subr(3);
+ CamelliaSubkeyL(3) = subl(2) ^ subl(4);
+ CamelliaSubkeyR(3) = subr(2) ^ subr(4);
+ CamelliaSubkeyL(4) = subl(3) ^ subl(5);
+ CamelliaSubkeyR(4) = subr(3) ^ subr(5);
+ CamelliaSubkeyL(5) = subl(4) ^ subl(6);
+ CamelliaSubkeyR(5) = subr(4) ^ subr(6);
+ CamelliaSubkeyL(6) = subl(5) ^ subl(7);
+ CamelliaSubkeyR(6) = subr(5) ^ subr(7);
+ tl = subl(10) ^ (subr(10) & ~subr(8));
+ dw = tl & subl(8), tr = subr(10) ^ CAMELLIA_RL1(dw);
+ CamelliaSubkeyL(7) = subl(6) ^ tl;
+ CamelliaSubkeyR(7) = subr(6) ^ tr;
+ CamelliaSubkeyL(8) = subl(8);
+ CamelliaSubkeyR(8) = subr(8);
+ CamelliaSubkeyL(9) = subl(9);
+ CamelliaSubkeyR(9) = subr(9);
+ tl = subl(7) ^ (subr(7) & ~subr(9));
+ dw = tl & subl(9), tr = subr(7) ^ CAMELLIA_RL1(dw);
+ CamelliaSubkeyL(10) = tl ^ subl(11);
+ CamelliaSubkeyR(10) = tr ^ subr(11);
+ CamelliaSubkeyL(11) = subl(10) ^ subl(12);
+ CamelliaSubkeyR(11) = subr(10) ^ subr(12);
+ CamelliaSubkeyL(12) = subl(11) ^ subl(13);
+ CamelliaSubkeyR(12) = subr(11) ^ subr(13);
+ CamelliaSubkeyL(13) = subl(12) ^ subl(14);
+ CamelliaSubkeyR(13) = subr(12) ^ subr(14);
+ CamelliaSubkeyL(14) = subl(13) ^ subl(15);
+ CamelliaSubkeyR(14) = subr(13) ^ subr(15);
+ tl = subl(18) ^ (subr(18) & ~subr(16));
+ dw = tl & subl(16), tr = subr(18) ^ CAMELLIA_RL1(dw);
+ CamelliaSubkeyL(15) = subl(14) ^ tl;
+ CamelliaSubkeyR(15) = subr(14) ^ tr;
+ CamelliaSubkeyL(16) = subl(16);
+ CamelliaSubkeyR(16) = subr(16);
+ CamelliaSubkeyL(17) = subl(17);
+ CamelliaSubkeyR(17) = subr(17);
+ tl = subl(15) ^ (subr(15) & ~subr(17));
+ dw = tl & subl(17), tr = subr(15) ^ CAMELLIA_RL1(dw);
+ CamelliaSubkeyL(18) = tl ^ subl(19);
+ CamelliaSubkeyR(18) = tr ^ subr(19);
+ CamelliaSubkeyL(19) = subl(18) ^ subl(20);
+ CamelliaSubkeyR(19) = subr(18) ^ subr(20);
+ CamelliaSubkeyL(20) = subl(19) ^ subl(21);
+ CamelliaSubkeyR(20) = subr(19) ^ subr(21);
+ CamelliaSubkeyL(21) = subl(20) ^ subl(22);
+ CamelliaSubkeyR(21) = subr(20) ^ subr(22);
+ CamelliaSubkeyL(22) = subl(21) ^ subl(23);
+ CamelliaSubkeyR(22) = subr(21) ^ subr(23);
+ CamelliaSubkeyL(23) = subl(22);
+ CamelliaSubkeyR(23) = subr(22);
+ CamelliaSubkeyL(24) = subl(24) ^ subl(23);
+ CamelliaSubkeyR(24) = subr(24) ^ subr(23);
+
+ /* apply the inverse of the last half of P-function */
+ dw = CamelliaSubkeyL(2) ^ CamelliaSubkeyR(2), dw = CAMELLIA_RL8(dw);
+ CamelliaSubkeyR(2) = CamelliaSubkeyL(2) ^ dw, CamelliaSubkeyL(2) = dw;
+ dw = CamelliaSubkeyL(3) ^ CamelliaSubkeyR(3), dw = CAMELLIA_RL8(dw);
+ CamelliaSubkeyR(3) = CamelliaSubkeyL(3) ^ dw, CamelliaSubkeyL(3) = dw;
+ dw = CamelliaSubkeyL(4) ^ CamelliaSubkeyR(4), dw = CAMELLIA_RL8(dw);
+ CamelliaSubkeyR(4) = CamelliaSubkeyL(4) ^ dw, CamelliaSubkeyL(4) = dw;
+ dw = CamelliaSubkeyL(5) ^ CamelliaSubkeyR(5), dw = CAMELLIA_RL8(dw);
+ CamelliaSubkeyR(5) = CamelliaSubkeyL(5) ^ dw, CamelliaSubkeyL(5) = dw;
+ dw = CamelliaSubkeyL(6) ^ CamelliaSubkeyR(6), dw = CAMELLIA_RL8(dw);
+ CamelliaSubkeyR(6) = CamelliaSubkeyL(6) ^ dw, CamelliaSubkeyL(6) = dw;
+ dw = CamelliaSubkeyL(7) ^ CamelliaSubkeyR(7), dw = CAMELLIA_RL8(dw);
+ CamelliaSubkeyR(7) = CamelliaSubkeyL(7) ^ dw, CamelliaSubkeyL(7) = dw;
+ dw = CamelliaSubkeyL(10) ^ CamelliaSubkeyR(10), dw = CAMELLIA_RL8(dw);
+ CamelliaSubkeyR(10) = CamelliaSubkeyL(10) ^ dw, CamelliaSubkeyL(10) = dw;
+ dw = CamelliaSubkeyL(11) ^ CamelliaSubkeyR(11), dw = CAMELLIA_RL8(dw);
+ CamelliaSubkeyR(11) = CamelliaSubkeyL(11) ^ dw, CamelliaSubkeyL(11) = dw;
+ dw = CamelliaSubkeyL(12) ^ CamelliaSubkeyR(12), dw = CAMELLIA_RL8(dw);
+ CamelliaSubkeyR(12) = CamelliaSubkeyL(12) ^ dw, CamelliaSubkeyL(12) = dw;
+ dw = CamelliaSubkeyL(13) ^ CamelliaSubkeyR(13), dw = CAMELLIA_RL8(dw);
+ CamelliaSubkeyR(13) = CamelliaSubkeyL(13) ^ dw, CamelliaSubkeyL(13) = dw;
+ dw = CamelliaSubkeyL(14) ^ CamelliaSubkeyR(14), dw = CAMELLIA_RL8(dw);
+ CamelliaSubkeyR(14) = CamelliaSubkeyL(14) ^ dw, CamelliaSubkeyL(14) = dw;
+ dw = CamelliaSubkeyL(15) ^ CamelliaSubkeyR(15), dw = CAMELLIA_RL8(dw);
+ CamelliaSubkeyR(15) = CamelliaSubkeyL(15) ^ dw, CamelliaSubkeyL(15) = dw;
+ dw = CamelliaSubkeyL(18) ^ CamelliaSubkeyR(18), dw = CAMELLIA_RL8(dw);
+ CamelliaSubkeyR(18) = CamelliaSubkeyL(18) ^ dw, CamelliaSubkeyL(18) = dw;
+ dw = CamelliaSubkeyL(19) ^ CamelliaSubkeyR(19), dw = CAMELLIA_RL8(dw);
+ CamelliaSubkeyR(19) = CamelliaSubkeyL(19) ^ dw, CamelliaSubkeyL(19) = dw;
+ dw = CamelliaSubkeyL(20) ^ CamelliaSubkeyR(20), dw = CAMELLIA_RL8(dw);
+ CamelliaSubkeyR(20) = CamelliaSubkeyL(20) ^ dw, CamelliaSubkeyL(20) = dw;
+ dw = CamelliaSubkeyL(21) ^ CamelliaSubkeyR(21), dw = CAMELLIA_RL8(dw);
+ CamelliaSubkeyR(21) = CamelliaSubkeyL(21) ^ dw, CamelliaSubkeyL(21) = dw;
+ dw = CamelliaSubkeyL(22) ^ CamelliaSubkeyR(22), dw = CAMELLIA_RL8(dw);
+ CamelliaSubkeyR(22) = CamelliaSubkeyL(22) ^ dw, CamelliaSubkeyL(22) = dw;
+ dw = CamelliaSubkeyL(23) ^ CamelliaSubkeyR(23), dw = CAMELLIA_RL8(dw);
+ CamelliaSubkeyR(23) = CamelliaSubkeyL(23) ^ dw, CamelliaSubkeyL(23) = dw;
+
+ return;
+}
+
+static void camellia_setup256(const unsigned char *key, u32 *subkey)
+{
+ u32 kll,klr,krl,krr; /* left half of key */
+ u32 krll,krlr,krrl,krrr; /* right half of key */
+ u32 il, ir, t0, t1, w0, w1; /* temporary variables */
+ u32 kw4l, kw4r, dw, tl, tr;
+ u32 subL[34];
+ u32 subR[34];
+
+ /**
+ * key = (kll || klr || krl || krr || krll || krlr || krrl || krrr)
+ * (|| is concatination)
+ */
+
+ kll = GETU32(key );
+ klr = GETU32(key + 4);
+ krl = GETU32(key + 8);
+ krr = GETU32(key + 12);
+ krll = GETU32(key + 16);
+ krlr = GETU32(key + 20);
+ krrl = GETU32(key + 24);
+ krrr = GETU32(key + 28);
+
+ /* generate KL dependent subkeys */
+ subl(0) = kll; subr(0) = klr;
+ subl(1) = krl; subr(1) = krr;
+ CAMELLIA_ROLDQo32(kll, klr, krl, krr, w0, w1, 45);
+ subl(12) = kll; subr(12) = klr;
+ subl(13) = krl; subr(13) = krr;
+ CAMELLIA_ROLDQ(kll, klr, krl, krr, w0, w1, 15);
+ subl(16) = kll; subr(16) = klr;
+ subl(17) = krl; subr(17) = krr;
+ CAMELLIA_ROLDQ(kll, klr, krl, krr, w0, w1, 17);
+ subl(22) = kll; subr(22) = klr;
+ subl(23) = krl; subr(23) = krr;
+ CAMELLIA_ROLDQo32(kll, klr, krl, krr, w0, w1, 34);
+ subl(30) = kll; subr(30) = klr;
+ subl(31) = krl; subr(31) = krr;
+
+ /* generate KR dependent subkeys */
+ CAMELLIA_ROLDQ(krll, krlr, krrl, krrr, w0, w1, 15);
+ subl(4) = krll; subr(4) = krlr;
+ subl(5) = krrl; subr(5) = krrr;
+ CAMELLIA_ROLDQ(krll, krlr, krrl, krrr, w0, w1, 15);
+ subl(8) = krll; subr(8) = krlr;
+ subl(9) = krrl; subr(9) = krrr;
+ CAMELLIA_ROLDQ(krll, krlr, krrl, krrr, w0, w1, 30);
+ subl(18) = krll; subr(18) = krlr;
+ subl(19) = krrl; subr(19) = krrr;
+ CAMELLIA_ROLDQo32(krll, krlr, krrl, krrr, w0, w1, 34);
+ subl(26) = krll; subr(26) = krlr;
+ subl(27) = krrl; subr(27) = krrr;
+ CAMELLIA_ROLDQo32(krll, krlr, krrl, krrr, w0, w1, 34);
+
+ /* generate KA */
+ kll = subl(0) ^ krll; klr = subr(0) ^ krlr;
+ krl = subl(1) ^ krrl; krr = subr(1) ^ krrr;
+ CAMELLIA_F(kll, klr,
+ CAMELLIA_SIGMA1L, CAMELLIA_SIGMA1R,
+ w0, w1, il, ir, t0, t1);
+ krl ^= w0; krr ^= w1;
+ CAMELLIA_F(krl, krr,
+ CAMELLIA_SIGMA2L, CAMELLIA_SIGMA2R,
+ kll, klr, il, ir, t0, t1);
+ kll ^= krll; klr ^= krlr;
+ CAMELLIA_F(kll, klr,
+ CAMELLIA_SIGMA3L, CAMELLIA_SIGMA3R,
+ krl, krr, il, ir, t0, t1);
+ krl ^= w0 ^ krrl; krr ^= w1 ^ krrr;
+ CAMELLIA_F(krl, krr,
+ CAMELLIA_SIGMA4L, CAMELLIA_SIGMA4R,
+ w0, w1, il, ir, t0, t1);
+ kll ^= w0; klr ^= w1;
+
+ /* generate KB */
+ krll ^= kll; krlr ^= klr;
+ krrl ^= krl; krrr ^= krr;
+ CAMELLIA_F(krll, krlr,
+ CAMELLIA_SIGMA5L, CAMELLIA_SIGMA5R,
+ w0, w1, il, ir, t0, t1);
+ krrl ^= w0; krrr ^= w1;
+ CAMELLIA_F(krrl, krrr,
+ CAMELLIA_SIGMA6L, CAMELLIA_SIGMA6R,
+ w0, w1, il, ir, t0, t1);
+ krll ^= w0; krlr ^= w1;
+
+ /* generate KA dependent subkeys */
+ CAMELLIA_ROLDQ(kll, klr, krl, krr, w0, w1, 15);
+ subl(6) = kll; subr(6) = klr;
+ subl(7) = krl; subr(7) = krr;
+ CAMELLIA_ROLDQ(kll, klr, krl, krr, w0, w1, 30);
+ subl(14) = kll; subr(14) = klr;
+ subl(15) = krl; subr(15) = krr;
+ subl(24) = klr; subr(24) = krl;
+ subl(25) = krr; subr(25) = kll;
+ CAMELLIA_ROLDQo32(kll, klr, krl, krr, w0, w1, 49);
+ subl(28) = kll; subr(28) = klr;
+ subl(29) = krl; subr(29) = krr;
+
+ /* generate KB dependent subkeys */
+ subl(2) = krll; subr(2) = krlr;
+ subl(3) = krrl; subr(3) = krrr;
+ CAMELLIA_ROLDQ(krll, krlr, krrl, krrr, w0, w1, 30);
+ subl(10) = krll; subr(10) = krlr;
+ subl(11) = krrl; subr(11) = krrr;
+ CAMELLIA_ROLDQ(krll, krlr, krrl, krrr, w0, w1, 30);
+ subl(20) = krll; subr(20) = krlr;
+ subl(21) = krrl; subr(21) = krrr;
+ CAMELLIA_ROLDQo32(krll, krlr, krrl, krrr, w0, w1, 51);
+ subl(32) = krll; subr(32) = krlr;
+ subl(33) = krrl; subr(33) = krrr;
+
+ /* absorb kw2 to other subkeys */
+ subl(3) ^= subl(1); subr(3) ^= subr(1);
+ subl(5) ^= subl(1); subr(5) ^= subr(1);
+ subl(7) ^= subl(1); subr(7) ^= subr(1);
+ subl(1) ^= subr(1) & ~subr(9);
+ dw = subl(1) & subl(9), subr(1) ^= CAMELLIA_RL1(dw);
+ subl(11) ^= subl(1); subr(11) ^= subr(1);
+ subl(13) ^= subl(1); subr(13) ^= subr(1);
+ subl(15) ^= subl(1); subr(15) ^= subr(1);
+ subl(1) ^= subr(1) & ~subr(17);
+ dw = subl(1) & subl(17), subr(1) ^= CAMELLIA_RL1(dw);
+ subl(19) ^= subl(1); subr(19) ^= subr(1);
+ subl(21) ^= subl(1); subr(21) ^= subr(1);
+ subl(23) ^= subl(1); subr(23) ^= subr(1);
+ subl(1) ^= subr(1) & ~subr(25);
+ dw = subl(1) & subl(25), subr(1) ^= CAMELLIA_RL1(dw);
+ subl(27) ^= subl(1); subr(27) ^= subr(1);
+ subl(29) ^= subl(1); subr(29) ^= subr(1);
+ subl(31) ^= subl(1); subr(31) ^= subr(1);
+ subl(32) ^= subl(1); subr(32) ^= subr(1);
+
+ /* absorb kw4 to other subkeys */
+ kw4l = subl(33); kw4r = subr(33);
+ subl(30) ^= kw4l; subr(30) ^= kw4r;
+ subl(28) ^= kw4l; subr(28) ^= kw4r;
+ subl(26) ^= kw4l; subr(26) ^= kw4r;
+ kw4l ^= kw4r & ~subr(24);
+ dw = kw4l & subl(24), kw4r ^= CAMELLIA_RL1(dw);
+ subl(22) ^= kw4l; subr(22) ^= kw4r;
+ subl(20) ^= kw4l; subr(20) ^= kw4r;
+ subl(18) ^= kw4l; subr(18) ^= kw4r;
+ kw4l ^= kw4r & ~subr(16);
+ dw = kw4l & subl(16), kw4r ^= CAMELLIA_RL1(dw);
+ subl(14) ^= kw4l; subr(14) ^= kw4r;
+ subl(12) ^= kw4l; subr(12) ^= kw4r;
+ subl(10) ^= kw4l; subr(10) ^= kw4r;
+ kw4l ^= kw4r & ~subr(8);
+ dw = kw4l & subl(8), kw4r ^= CAMELLIA_RL1(dw);
+ subl(6) ^= kw4l; subr(6) ^= kw4r;
+ subl(4) ^= kw4l; subr(4) ^= kw4r;
+ subl(2) ^= kw4l; subr(2) ^= kw4r;
+ subl(0) ^= kw4l; subr(0) ^= kw4r;
+
+ /* key XOR is end of F-function */
+ CamelliaSubkeyL(0) = subl(0) ^ subl(2);
+ CamelliaSubkeyR(0) = subr(0) ^ subr(2);
+ CamelliaSubkeyL(2) = subl(3);
+ CamelliaSubkeyR(2) = subr(3);
+ CamelliaSubkeyL(3) = subl(2) ^ subl(4);
+ CamelliaSubkeyR(3) = subr(2) ^ subr(4);
+ CamelliaSubkeyL(4) = subl(3) ^ subl(5);
+ CamelliaSubkeyR(4) = subr(3) ^ subr(5);
+ CamelliaSubkeyL(5) = subl(4) ^ subl(6);
+ CamelliaSubkeyR(5) = subr(4) ^ subr(6);
+ CamelliaSubkeyL(6) = subl(5) ^ subl(7);
+ CamelliaSubkeyR(6) = subr(5) ^ subr(7);
+ tl = subl(10) ^ (subr(10) & ~subr(8));
+ dw = tl & subl(8), tr = subr(10) ^ CAMELLIA_RL1(dw);
+ CamelliaSubkeyL(7) = subl(6) ^ tl;
+ CamelliaSubkeyR(7) = subr(6) ^ tr;
+ CamelliaSubkeyL(8) = subl(8);
+ CamelliaSubkeyR(8) = subr(8);
+ CamelliaSubkeyL(9) = subl(9);
+ CamelliaSubkeyR(9) = subr(9);
+ tl = subl(7) ^ (subr(7) & ~subr(9));
+ dw = tl & subl(9), tr = subr(7) ^ CAMELLIA_RL1(dw);
+ CamelliaSubkeyL(10) = tl ^ subl(11);
+ CamelliaSubkeyR(10) = tr ^ subr(11);
+ CamelliaSubkeyL(11) = subl(10) ^ subl(12);
+ CamelliaSubkeyR(11) = subr(10) ^ subr(12);
+ CamelliaSubkeyL(12) = subl(11) ^ subl(13);
+ CamelliaSubkeyR(12) = subr(11) ^ subr(13);
+ CamelliaSubkeyL(13) = subl(12) ^ subl(14);
+ CamelliaSubkeyR(13) = subr(12) ^ subr(14);
+ CamelliaSubkeyL(14) = subl(13) ^ subl(15);
+ CamelliaSubkeyR(14) = subr(13) ^ subr(15);
+ tl = subl(18) ^ (subr(18) & ~subr(16));
+ dw = tl & subl(16), tr = subr(18) ^ CAMELLIA_RL1(dw);
+ CamelliaSubkeyL(15) = subl(14) ^ tl;
+ CamelliaSubkeyR(15) = subr(14) ^ tr;
+ CamelliaSubkeyL(16) = subl(16);
+ CamelliaSubkeyR(16) = subr(16);
+ CamelliaSubkeyL(17) = subl(17);
+ CamelliaSubkeyR(17) = subr(17);
+ tl = subl(15) ^ (subr(15) & ~subr(17));
+ dw = tl & subl(17), tr = subr(15) ^ CAMELLIA_RL1(dw);
+ CamelliaSubkeyL(18) = tl ^ subl(19);
+ CamelliaSubkeyR(18) = tr ^ subr(19);
+ CamelliaSubkeyL(19) = subl(18) ^ subl(20);
+ CamelliaSubkeyR(19) = subr(18) ^ subr(20);
+ CamelliaSubkeyL(20) = subl(19) ^ subl(21);
+ CamelliaSubkeyR(20) = subr(19) ^ subr(21);
+ CamelliaSubkeyL(21) = subl(20) ^ subl(22);
+ CamelliaSubkeyR(21) = subr(20) ^ subr(22);
+ CamelliaSubkeyL(22) = subl(21) ^ subl(23);
+ CamelliaSubkeyR(22) = subr(21) ^ subr(23);
+ tl = subl(26) ^ (subr(26) & ~subr(24));
+ dw = tl & subl(24), tr = subr(26) ^ CAMELLIA_RL1(dw);
+ CamelliaSubkeyL(23) = subl(22) ^ tl;
+ CamelliaSubkeyR(23) = subr(22) ^ tr;
+ CamelliaSubkeyL(24) = subl(24);
+ CamelliaSubkeyR(24) = subr(24);
+ CamelliaSubkeyL(25) = subl(25);
+ CamelliaSubkeyR(25) = subr(25);
+ tl = subl(23) ^ (subr(23) & ~subr(25));
+ dw = tl & subl(25), tr = subr(23) ^ CAMELLIA_RL1(dw);
+ CamelliaSubkeyL(26) = tl ^ subl(27);
+ CamelliaSubkeyR(26) = tr ^ subr(27);
+ CamelliaSubkeyL(27) = subl(26) ^ subl(28);
+ CamelliaSubkeyR(27) = subr(26) ^ subr(28);
+ CamelliaSubkeyL(28) = subl(27) ^ subl(29);
+ CamelliaSubkeyR(28) = subr(27) ^ subr(29);
+ CamelliaSubkeyL(29) = subl(28) ^ subl(30);
+ CamelliaSubkeyR(29) = subr(28) ^ subr(30);
+ CamelliaSubkeyL(30) = subl(29) ^ subl(31);
+ CamelliaSubkeyR(30) = subr(29) ^ subr(31);
+ CamelliaSubkeyL(31) = subl(30);
+ CamelliaSubkeyR(31) = subr(30);
+ CamelliaSubkeyL(32) = subl(32) ^ subl(31);
+ CamelliaSubkeyR(32) = subr(32) ^ subr(31);
+
+ /* apply the inverse of the last half of P-function */
+ dw = CamelliaSubkeyL(2) ^ CamelliaSubkeyR(2), dw = CAMELLIA_RL8(dw);
+ CamelliaSubkeyR(2) = CamelliaSubkeyL(2) ^ dw, CamelliaSubkeyL(2) = dw;
+ dw = CamelliaSubkeyL(3) ^ CamelliaSubkeyR(3), dw = CAMELLIA_RL8(dw);
+ CamelliaSubkeyR(3) = CamelliaSubkeyL(3) ^ dw, CamelliaSubkeyL(3) = dw;
+ dw = CamelliaSubkeyL(4) ^ CamelliaSubkeyR(4), dw = CAMELLIA_RL8(dw);
+ CamelliaSubkeyR(4) = CamelliaSubkeyL(4) ^ dw, CamelliaSubkeyL(4) = dw;
+ dw = CamelliaSubkeyL(5) ^ CamelliaSubkeyR(5), dw = CAMELLIA_RL8(dw);
+ CamelliaSubkeyR(5) = CamelliaSubkeyL(5) ^ dw, CamelliaSubkeyL(5) = dw;
+ dw = CamelliaSubkeyL(6) ^ CamelliaSubkeyR(6), dw = CAMELLIA_RL8(dw);
+ CamelliaSubkeyR(6) = CamelliaSubkeyL(6) ^ dw, CamelliaSubkeyL(6) = dw;
+ dw = CamelliaSubkeyL(7) ^ CamelliaSubkeyR(7), dw = CAMELLIA_RL8(dw);
+ CamelliaSubkeyR(7) = CamelliaSubkeyL(7) ^ dw, CamelliaSubkeyL(7) = dw;
+ dw = CamelliaSubkeyL(10) ^ CamelliaSubkeyR(10), dw = CAMELLIA_RL8(dw);
+ CamelliaSubkeyR(10) = CamelliaSubkeyL(10) ^ dw, CamelliaSubkeyL(10) = dw;
+ dw = CamelliaSubkeyL(11) ^ CamelliaSubkeyR(11), dw = CAMELLIA_RL8(dw);
+ CamelliaSubkeyR(11) = CamelliaSubkeyL(11) ^ dw, CamelliaSubkeyL(11) = dw;
+ dw = CamelliaSubkeyL(12) ^ CamelliaSubkeyR(12), dw = CAMELLIA_RL8(dw);
+ CamelliaSubkeyR(12) = CamelliaSubkeyL(12) ^ dw, CamelliaSubkeyL(12) = dw;
+ dw = CamelliaSubkeyL(13) ^ CamelliaSubkeyR(13), dw = CAMELLIA_RL8(dw);
+ CamelliaSubkeyR(13) = CamelliaSubkeyL(13) ^ dw, CamelliaSubkeyL(13) = dw;
+ dw = CamelliaSubkeyL(14) ^ CamelliaSubkeyR(14), dw = CAMELLIA_RL8(dw);
+ CamelliaSubkeyR(14) = CamelliaSubkeyL(14) ^ dw, CamelliaSubkeyL(14) = dw;
+ dw = CamelliaSubkeyL(15) ^ CamelliaSubkeyR(15), dw = CAMELLIA_RL8(dw);
+ CamelliaSubkeyR(15) = CamelliaSubkeyL(15) ^ dw, CamelliaSubkeyL(15) = dw;
+ dw = CamelliaSubkeyL(18) ^ CamelliaSubkeyR(18), dw = CAMELLIA_RL8(dw);
+ CamelliaSubkeyR(18) = CamelliaSubkeyL(18) ^ dw, CamelliaSubkeyL(18) = dw;
+ dw = CamelliaSubkeyL(19) ^ CamelliaSubkeyR(19), dw = CAMELLIA_RL8(dw);
+ CamelliaSubkeyR(19) = CamelliaSubkeyL(19) ^ dw, CamelliaSubkeyL(19) = dw;
+ dw = CamelliaSubkeyL(20) ^ CamelliaSubkeyR(20), dw = CAMELLIA_RL8(dw);
+ CamelliaSubkeyR(20) = CamelliaSubkeyL(20) ^ dw, CamelliaSubkeyL(20) = dw;
+ dw = CamelliaSubkeyL(21) ^ CamelliaSubkeyR(21), dw = CAMELLIA_RL8(dw);
+ CamelliaSubkeyR(21) = CamelliaSubkeyL(21) ^ dw, CamelliaSubkeyL(21) = dw;
+ dw = CamelliaSubkeyL(22) ^ CamelliaSubkeyR(22), dw = CAMELLIA_RL8(dw);
+ CamelliaSubkeyR(22) = CamelliaSubkeyL(22) ^ dw, CamelliaSubkeyL(22) = dw;
+ dw = CamelliaSubkeyL(23) ^ CamelliaSubkeyR(23), dw = CAMELLIA_RL8(dw);
+ CamelliaSubkeyR(23) = CamelliaSubkeyL(23) ^ dw, CamelliaSubkeyL(23) = dw;
+ dw = CamelliaSubkeyL(26) ^ CamelliaSubkeyR(26), dw = CAMELLIA_RL8(dw);
+ CamelliaSubkeyR(26) = CamelliaSubkeyL(26) ^ dw, CamelliaSubkeyL(26) = dw;
+ dw = CamelliaSubkeyL(27) ^ CamelliaSubkeyR(27), dw = CAMELLIA_RL8(dw);
+ CamelliaSubkeyR(27) = CamelliaSubkeyL(27) ^ dw, CamelliaSubkeyL(27) = dw;
+ dw = CamelliaSubkeyL(28) ^ CamelliaSubkeyR(28), dw = CAMELLIA_RL8(dw);
+ CamelliaSubkeyR(28) = CamelliaSubkeyL(28) ^ dw, CamelliaSubkeyL(28) = dw;
+ dw = CamelliaSubkeyL(29) ^ CamelliaSubkeyR(29), dw = CAMELLIA_RL8(dw);
+ CamelliaSubkeyR(29) = CamelliaSubkeyL(29) ^ dw, CamelliaSubkeyL(29) = dw;
+ dw = CamelliaSubkeyL(30) ^ CamelliaSubkeyR(30), dw = CAMELLIA_RL8(dw);
+ CamelliaSubkeyR(30) = CamelliaSubkeyL(30) ^ dw, CamelliaSubkeyL(30) = dw;
+ dw = CamelliaSubkeyL(31) ^ CamelliaSubkeyR(31), dw = CAMELLIA_RL8(dw);
+ CamelliaSubkeyR(31) = CamelliaSubkeyL(31) ^ dw,CamelliaSubkeyL(31) = dw;
+
+ return;
+}
+
+static void camellia_setup192(const unsigned char *key, u32 *subkey)
+{
+ unsigned char kk[32];
+ u32 krll, krlr, krrl,krrr;
+
+ memcpy(kk, key, 24);
+ memcpy((unsigned char *)&krll, key+16,4);
+ memcpy((unsigned char *)&krlr, key+20,4);
+ krrl = ~krll;
+ krrr = ~krlr;
+ memcpy(kk+24, (unsigned char *)&krrl, 4);
+ memcpy(kk+28, (unsigned char *)&krrr, 4);
+ camellia_setup256(kk, subkey);
+ return;
+}
+
+
+/**
+ * Stuff related to camellia encryption/decryption
+ *
+ * "io" must be 4byte aligned and big-endian data.
+ */
+static void camellia_encrypt128(const u32 *subkey, u32 *io)
+{
+ u32 il, ir, t0, t1;
+
+ /* pre whitening but absorb kw2*/
+ io[0] ^= CamelliaSubkeyL(0);
+ io[1] ^= CamelliaSubkeyR(0);
+ /* main iteration */
+
+ CAMELLIA_ROUNDSM(io[0],io[1],
+ CamelliaSubkeyL(2),CamelliaSubkeyR(2),
+ io[2],io[3],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[2],io[3],
+ CamelliaSubkeyL(3),CamelliaSubkeyR(3),
+ io[0],io[1],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[0],io[1],
+ CamelliaSubkeyL(4),CamelliaSubkeyR(4),
+ io[2],io[3],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[2],io[3],
+ CamelliaSubkeyL(5),CamelliaSubkeyR(5),
+ io[0],io[1],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[0],io[1],
+ CamelliaSubkeyL(6),CamelliaSubkeyR(6),
+ io[2],io[3],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[2],io[3],
+ CamelliaSubkeyL(7),CamelliaSubkeyR(7),
+ io[0],io[1],il,ir,t0,t1);
+
+ CAMELLIA_FLS(io[0],io[1],io[2],io[3],
+ CamelliaSubkeyL(8),CamelliaSubkeyR(8),
+ CamelliaSubkeyL(9),CamelliaSubkeyR(9),
+ t0,t1,il,ir);
+
+ CAMELLIA_ROUNDSM(io[0],io[1],
+ CamelliaSubkeyL(10),CamelliaSubkeyR(10),
+ io[2],io[3],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[2],io[3],
+ CamelliaSubkeyL(11),CamelliaSubkeyR(11),
+ io[0],io[1],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[0],io[1],
+ CamelliaSubkeyL(12),CamelliaSubkeyR(12),
+ io[2],io[3],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[2],io[3],
+ CamelliaSubkeyL(13),CamelliaSubkeyR(13),
+ io[0],io[1],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[0],io[1],
+ CamelliaSubkeyL(14),CamelliaSubkeyR(14),
+ io[2],io[3],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[2],io[3],
+ CamelliaSubkeyL(15),CamelliaSubkeyR(15),
+ io[0],io[1],il,ir,t0,t1);
+
+ CAMELLIA_FLS(io[0],io[1],io[2],io[3],
+ CamelliaSubkeyL(16),CamelliaSubkeyR(16),
+ CamelliaSubkeyL(17),CamelliaSubkeyR(17),
+ t0,t1,il,ir);
+
+ CAMELLIA_ROUNDSM(io[0],io[1],
+ CamelliaSubkeyL(18),CamelliaSubkeyR(18),
+ io[2],io[3],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[2],io[3],
+ CamelliaSubkeyL(19),CamelliaSubkeyR(19),
+ io[0],io[1],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[0],io[1],
+ CamelliaSubkeyL(20),CamelliaSubkeyR(20),
+ io[2],io[3],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[2],io[3],
+ CamelliaSubkeyL(21),CamelliaSubkeyR(21),
+ io[0],io[1],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[0],io[1],
+ CamelliaSubkeyL(22),CamelliaSubkeyR(22),
+ io[2],io[3],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[2],io[3],
+ CamelliaSubkeyL(23),CamelliaSubkeyR(23),
+ io[0],io[1],il,ir,t0,t1);
+
+ /* post whitening but kw4 */
+ io[2] ^= CamelliaSubkeyL(24);
+ io[3] ^= CamelliaSubkeyR(24);
+
+ t0 = io[0];
+ t1 = io[1];
+ io[0] = io[2];
+ io[1] = io[3];
+ io[2] = t0;
+ io[3] = t1;
+
+ return;
+}
+
+static void camellia_decrypt128(const u32 *subkey, u32 *io)
+{
+ u32 il,ir,t0,t1; /* temporary valiables */
+
+ /* pre whitening but absorb kw2*/
+ io[0] ^= CamelliaSubkeyL(24);
+ io[1] ^= CamelliaSubkeyR(24);
+
+ /* main iteration */
+ CAMELLIA_ROUNDSM(io[0],io[1],
+ CamelliaSubkeyL(23),CamelliaSubkeyR(23),
+ io[2],io[3],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[2],io[3],
+ CamelliaSubkeyL(22),CamelliaSubkeyR(22),
+ io[0],io[1],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[0],io[1],
+ CamelliaSubkeyL(21),CamelliaSubkeyR(21),
+ io[2],io[3],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[2],io[3],
+ CamelliaSubkeyL(20),CamelliaSubkeyR(20),
+ io[0],io[1],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[0],io[1],
+ CamelliaSubkeyL(19),CamelliaSubkeyR(19),
+ io[2],io[3],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[2],io[3],
+ CamelliaSubkeyL(18),CamelliaSubkeyR(18),
+ io[0],io[1],il,ir,t0,t1);
+
+ CAMELLIA_FLS(io[0],io[1],io[2],io[3],
+ CamelliaSubkeyL(17),CamelliaSubkeyR(17),
+ CamelliaSubkeyL(16),CamelliaSubkeyR(16),
+ t0,t1,il,ir);
+
+ CAMELLIA_ROUNDSM(io[0],io[1],
+ CamelliaSubkeyL(15),CamelliaSubkeyR(15),
+ io[2],io[3],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[2],io[3],
+ CamelliaSubkeyL(14),CamelliaSubkeyR(14),
+ io[0],io[1],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[0],io[1],
+ CamelliaSubkeyL(13),CamelliaSubkeyR(13),
+ io[2],io[3],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[2],io[3],
+ CamelliaSubkeyL(12),CamelliaSubkeyR(12),
+ io[0],io[1],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[0],io[1],
+ CamelliaSubkeyL(11),CamelliaSubkeyR(11),
+ io[2],io[3],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[2],io[3],
+ CamelliaSubkeyL(10),CamelliaSubkeyR(10),
+ io[0],io[1],il,ir,t0,t1);
+
+ CAMELLIA_FLS(io[0],io[1],io[2],io[3],
+ CamelliaSubkeyL(9),CamelliaSubkeyR(9),
+ CamelliaSubkeyL(8),CamelliaSubkeyR(8),
+ t0,t1,il,ir);
+
+ CAMELLIA_ROUNDSM(io[0],io[1],
+ CamelliaSubkeyL(7),CamelliaSubkeyR(7),
+ io[2],io[3],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[2],io[3],
+ CamelliaSubkeyL(6),CamelliaSubkeyR(6),
+ io[0],io[1],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[0],io[1],
+ CamelliaSubkeyL(5),CamelliaSubkeyR(5),
+ io[2],io[3],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[2],io[3],
+ CamelliaSubkeyL(4),CamelliaSubkeyR(4),
+ io[0],io[1],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[0],io[1],
+ CamelliaSubkeyL(3),CamelliaSubkeyR(3),
+ io[2],io[3],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[2],io[3],
+ CamelliaSubkeyL(2),CamelliaSubkeyR(2),
+ io[0],io[1],il,ir,t0,t1);
+
+ /* post whitening but kw4 */
+ io[2] ^= CamelliaSubkeyL(0);
+ io[3] ^= CamelliaSubkeyR(0);
+
+ t0 = io[0];
+ t1 = io[1];
+ io[0] = io[2];
+ io[1] = io[3];
+ io[2] = t0;
+ io[3] = t1;
+
+ return;
+}
+
+/**
+ * stuff for 192 and 256bit encryption/decryption
+ */
+static void camellia_encrypt256(const u32 *subkey, u32 *io)
+{
+ u32 il,ir,t0,t1; /* temporary valiables */
+
+ /* pre whitening but absorb kw2*/
+ io[0] ^= CamelliaSubkeyL(0);
+ io[1] ^= CamelliaSubkeyR(0);
+
+ /* main iteration */
+ CAMELLIA_ROUNDSM(io[0],io[1],
+ CamelliaSubkeyL(2),CamelliaSubkeyR(2),
+ io[2],io[3],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[2],io[3],
+ CamelliaSubkeyL(3),CamelliaSubkeyR(3),
+ io[0],io[1],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[0],io[1],
+ CamelliaSubkeyL(4),CamelliaSubkeyR(4),
+ io[2],io[3],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[2],io[3],
+ CamelliaSubkeyL(5),CamelliaSubkeyR(5),
+ io[0],io[1],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[0],io[1],
+ CamelliaSubkeyL(6),CamelliaSubkeyR(6),
+ io[2],io[3],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[2],io[3],
+ CamelliaSubkeyL(7),CamelliaSubkeyR(7),
+ io[0],io[1],il,ir,t0,t1);
+
+ CAMELLIA_FLS(io[0],io[1],io[2],io[3],
+ CamelliaSubkeyL(8),CamelliaSubkeyR(8),
+ CamelliaSubkeyL(9),CamelliaSubkeyR(9),
+ t0,t1,il,ir);
+
+ CAMELLIA_ROUNDSM(io[0],io[1],
+ CamelliaSubkeyL(10),CamelliaSubkeyR(10),
+ io[2],io[3],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[2],io[3],
+ CamelliaSubkeyL(11),CamelliaSubkeyR(11),
+ io[0],io[1],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[0],io[1],
+ CamelliaSubkeyL(12),CamelliaSubkeyR(12),
+ io[2],io[3],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[2],io[3],
+ CamelliaSubkeyL(13),CamelliaSubkeyR(13),
+ io[0],io[1],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[0],io[1],
+ CamelliaSubkeyL(14),CamelliaSubkeyR(14),
+ io[2],io[3],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[2],io[3],
+ CamelliaSubkeyL(15),CamelliaSubkeyR(15),
+ io[0],io[1],il,ir,t0,t1);
+
+ CAMELLIA_FLS(io[0],io[1],io[2],io[3],
+ CamelliaSubkeyL(16),CamelliaSubkeyR(16),
+ CamelliaSubkeyL(17),CamelliaSubkeyR(17),
+ t0,t1,il,ir);
+
+ CAMELLIA_ROUNDSM(io[0],io[1],
+ CamelliaSubkeyL(18),CamelliaSubkeyR(18),
+ io[2],io[3],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[2],io[3],
+ CamelliaSubkeyL(19),CamelliaSubkeyR(19),
+ io[0],io[1],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[0],io[1],
+ CamelliaSubkeyL(20),CamelliaSubkeyR(20),
+ io[2],io[3],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[2],io[3],
+ CamelliaSubkeyL(21),CamelliaSubkeyR(21),
+ io[0],io[1],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[0],io[1],
+ CamelliaSubkeyL(22),CamelliaSubkeyR(22),
+ io[2],io[3],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[2],io[3],
+ CamelliaSubkeyL(23),CamelliaSubkeyR(23),
+ io[0],io[1],il,ir,t0,t1);
+
+ CAMELLIA_FLS(io[0],io[1],io[2],io[3],
+ CamelliaSubkeyL(24),CamelliaSubkeyR(24),
+ CamelliaSubkeyL(25),CamelliaSubkeyR(25),
+ t0,t1,il,ir);
+
+ CAMELLIA_ROUNDSM(io[0],io[1],
+ CamelliaSubkeyL(26),CamelliaSubkeyR(26),
+ io[2],io[3],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[2],io[3],
+ CamelliaSubkeyL(27),CamelliaSubkeyR(27),
+ io[0],io[1],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[0],io[1],
+ CamelliaSubkeyL(28),CamelliaSubkeyR(28),
+ io[2],io[3],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[2],io[3],
+ CamelliaSubkeyL(29),CamelliaSubkeyR(29),
+ io[0],io[1],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[0],io[1],
+ CamelliaSubkeyL(30),CamelliaSubkeyR(30),
+ io[2],io[3],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[2],io[3],
+ CamelliaSubkeyL(31),CamelliaSubkeyR(31),
+ io[0],io[1],il,ir,t0,t1);
+
+ /* post whitening but kw4 */
+ io[2] ^= CamelliaSubkeyL(32);
+ io[3] ^= CamelliaSubkeyR(32);
+
+ t0 = io[0];
+ t1 = io[1];
+ io[0] = io[2];
+ io[1] = io[3];
+ io[2] = t0;
+ io[3] = t1;
+
+ return;
+}
+
+static void camellia_decrypt256(const u32 *subkey, u32 *io)
+{
+ u32 il,ir,t0,t1; /* temporary valiables */
+
+ /* pre whitening but absorb kw2*/
+ io[0] ^= CamelliaSubkeyL(32);
+ io[1] ^= CamelliaSubkeyR(32);
+
+ /* main iteration */
+ CAMELLIA_ROUNDSM(io[0],io[1],
+ CamelliaSubkeyL(31),CamelliaSubkeyR(31),
+ io[2],io[3],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[2],io[3],
+ CamelliaSubkeyL(30),CamelliaSubkeyR(30),
+ io[0],io[1],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[0],io[1],
+ CamelliaSubkeyL(29),CamelliaSubkeyR(29),
+ io[2],io[3],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[2],io[3],
+ CamelliaSubkeyL(28),CamelliaSubkeyR(28),
+ io[0],io[1],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[0],io[1],
+ CamelliaSubkeyL(27),CamelliaSubkeyR(27),
+ io[2],io[3],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[2],io[3],
+ CamelliaSubkeyL(26),CamelliaSubkeyR(26),
+ io[0],io[1],il,ir,t0,t1);
+
+ CAMELLIA_FLS(io[0],io[1],io[2],io[3],
+ CamelliaSubkeyL(25),CamelliaSubkeyR(25),
+ CamelliaSubkeyL(24),CamelliaSubkeyR(24),
+ t0,t1,il,ir);
+
+ CAMELLIA_ROUNDSM(io[0],io[1],
+ CamelliaSubkeyL(23),CamelliaSubkeyR(23),
+ io[2],io[3],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[2],io[3],
+ CamelliaSubkeyL(22),CamelliaSubkeyR(22),
+ io[0],io[1],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[0],io[1],
+ CamelliaSubkeyL(21),CamelliaSubkeyR(21),
+ io[2],io[3],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[2],io[3],
+ CamelliaSubkeyL(20),CamelliaSubkeyR(20),
+ io[0],io[1],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[0],io[1],
+ CamelliaSubkeyL(19),CamelliaSubkeyR(19),
+ io[2],io[3],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[2],io[3],
+ CamelliaSubkeyL(18),CamelliaSubkeyR(18),
+ io[0],io[1],il,ir,t0,t1);
+
+ CAMELLIA_FLS(io[0],io[1],io[2],io[3],
+ CamelliaSubkeyL(17),CamelliaSubkeyR(17),
+ CamelliaSubkeyL(16),CamelliaSubkeyR(16),
+ t0,t1,il,ir);
+
+ CAMELLIA_ROUNDSM(io[0],io[1],
+ CamelliaSubkeyL(15),CamelliaSubkeyR(15),
+ io[2],io[3],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[2],io[3],
+ CamelliaSubkeyL(14),CamelliaSubkeyR(14),
+ io[0],io[1],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[0],io[1],
+ CamelliaSubkeyL(13),CamelliaSubkeyR(13),
+ io[2],io[3],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[2],io[3],
+ CamelliaSubkeyL(12),CamelliaSubkeyR(12),
+ io[0],io[1],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[0],io[1],
+ CamelliaSubkeyL(11),CamelliaSubkeyR(11),
+ io[2],io[3],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[2],io[3],
+ CamelliaSubkeyL(10),CamelliaSubkeyR(10),
+ io[0],io[1],il,ir,t0,t1);
+
+ CAMELLIA_FLS(io[0],io[1],io[2],io[3],
+ CamelliaSubkeyL(9),CamelliaSubkeyR(9),
+ CamelliaSubkeyL(8),CamelliaSubkeyR(8),
+ t0,t1,il,ir);
+
+ CAMELLIA_ROUNDSM(io[0],io[1],
+ CamelliaSubkeyL(7),CamelliaSubkeyR(7),
+ io[2],io[3],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[2],io[3],
+ CamelliaSubkeyL(6),CamelliaSubkeyR(6),
+ io[0],io[1],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[0],io[1],
+ CamelliaSubkeyL(5),CamelliaSubkeyR(5),
+ io[2],io[3],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[2],io[3],
+ CamelliaSubkeyL(4),CamelliaSubkeyR(4),
+ io[0],io[1],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[0],io[1],
+ CamelliaSubkeyL(3),CamelliaSubkeyR(3),
+ io[2],io[3],il,ir,t0,t1);
+ CAMELLIA_ROUNDSM(io[2],io[3],
+ CamelliaSubkeyL(2),CamelliaSubkeyR(2),
+ io[0],io[1],il,ir,t0,t1);
+
+ /* post whitening but kw4 */
+ io[2] ^= CamelliaSubkeyL(0);
+ io[3] ^= CamelliaSubkeyR(0);
+
+ t0 = io[0];
+ t1 = io[1];
+ io[0] = io[2];
+ io[1] = io[3];
+ io[2] = t0;
+ io[3] = t1;
+
+ return;
+}
+
+/***
+ *
+ * API for compatibility
+ */
+
+void Camellia_Ekeygen(const int keyBitLength,
+ const unsigned char *rawKey,
+ KEY_TABLE_TYPE keyTable)
+{
+ switch(keyBitLength) {
+ case 128:
+ camellia_setup128(rawKey, keyTable);
+ break;
+ case 192:
+ camellia_setup192(rawKey, keyTable);
+ break;
+ case 256:
+ camellia_setup256(rawKey, keyTable);
+ break;
+ default:
+ break;
+ }
+}
+
+
+void Camellia_EncryptBlock(const int keyBitLength,
+ const unsigned char *plaintext,
+ const KEY_TABLE_TYPE keyTable,
+ unsigned char *ciphertext)
+{
+ u32 tmp[4];
+
+ tmp[0] = GETU32(plaintext);
+ tmp[1] = GETU32(plaintext + 4);
+ tmp[2] = GETU32(plaintext + 8);
+ tmp[3] = GETU32(plaintext + 12);
+
+ switch (keyBitLength) {
+ case 128:
+ camellia_encrypt128(keyTable, tmp);
+ break;
+ case 192:
+ /* fall through */
+ case 256:
+ camellia_encrypt256(keyTable, tmp);
+ break;
+ default:
+ break;
+ }
+
+ PUTU32(ciphertext, tmp[0]);
+ PUTU32(ciphertext + 4, tmp[1]);
+ PUTU32(ciphertext + 8, tmp[2]);
+ PUTU32(ciphertext + 12, tmp[3]);
+}
+
+void Camellia_DecryptBlock(const int keyBitLength,
+ const unsigned char *ciphertext,
+ const KEY_TABLE_TYPE keyTable,
+ unsigned char *plaintext)
+{
+ u32 tmp[4];
+
+ tmp[0] = GETU32(ciphertext);
+ tmp[1] = GETU32(ciphertext + 4);
+ tmp[2] = GETU32(ciphertext + 8);
+ tmp[3] = GETU32(ciphertext + 12);
+
+ switch (keyBitLength) {
+ case 128:
+ camellia_decrypt128(keyTable, tmp);
+ break;
+ case 192:
+ /* fall through */
+ case 256:
+ camellia_decrypt256(keyTable, tmp);
+ break;
+ default:
+ break;
+ }
+ PUTU32(plaintext, tmp[0]);
+ PUTU32(plaintext + 4, tmp[1]);
+ PUTU32(plaintext + 8, tmp[2]);
+ PUTU32(plaintext + 12, tmp[3]);
+}
diff --git a/source4/heimdal/lib/hcrypto/camellia-ntt.h b/source4/heimdal/lib/hcrypto/camellia-ntt.h
new file mode 100644
index 0000000000..3f93cc0611
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/camellia-ntt.h
@@ -0,0 +1,58 @@
+/* camellia.h ver 1.2.0
+ *
+ * Copyright (C) 2006,2007
+ * NTT (Nippon Telegraph and Telephone Corporation).
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef HEADER_CAMELLIA_H
+#define HEADER_CAMELLIA_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define CAMELLIA_BLOCK_SIZE 16
+#define CAMELLIA_TABLE_BYTE_LEN 272
+#define CAMELLIA_TABLE_WORD_LEN (CAMELLIA_TABLE_BYTE_LEN / 4)
+
+/* u32 must be 32bit word */
+typedef uint32_t u32;
+typedef unsigned char u8;
+
+typedef u32 KEY_TABLE_TYPE[CAMELLIA_TABLE_WORD_LEN];
+
+
+void Camellia_Ekeygen(const int keyBitLength,
+ const unsigned char *rawKey,
+ KEY_TABLE_TYPE keyTable);
+
+void Camellia_EncryptBlock(const int keyBitLength,
+ const unsigned char *plaintext,
+ const KEY_TABLE_TYPE keyTable,
+ unsigned char *cipherText);
+
+void Camellia_DecryptBlock(const int keyBitLength,
+ const unsigned char *cipherText,
+ const KEY_TABLE_TYPE keyTable,
+ unsigned char *plaintext);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* HEADER_CAMELLIA_H */
diff --git a/source4/heimdal/lib/hcrypto/camellia.c b/source4/heimdal/lib/hcrypto/camellia.c
new file mode 100644
index 0000000000..d78bbd4c0e
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/camellia.c
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+
+RCSID("$Id: aes.c 20466 2007-04-20 08:29:05Z lha $");
+#endif
+
+#include <roken.h>
+
+#ifdef KRB5
+#include <krb5-types.h>
+#endif
+
+#include <string.h>
+
+#include "camellia-ntt.h"
+#include "camellia.h"
+
+int
+CAMELLIA_set_key(const unsigned char *userkey,
+ const int bits, CAMELLIA_KEY *key)
+{
+ key->bits = bits;
+ Camellia_Ekeygen(bits, userkey, key->key);
+ return 1;
+}
+
+void
+CAMELLIA_encrypt(const unsigned char *in, unsigned char *out,
+ const CAMELLIA_KEY *key)
+{
+ Camellia_EncryptBlock(key->bits, in, key->key, out);
+
+}
+
+void
+CAMELLIA_decrypt(const unsigned char *in, unsigned char *out,
+ const CAMELLIA_KEY *key)
+{
+ Camellia_DecryptBlock(key->bits, in, key->key, out);
+}
+
+void
+CAMELLIA_cbc_encrypt(const unsigned char *in, unsigned char *out,
+ unsigned long size, const CAMELLIA_KEY *key,
+ unsigned char *iv, int mode_encrypt)
+{
+ unsigned char tmp[CAMELLIA_BLOCK_SIZE];
+ int i;
+
+ if (mode_encrypt) {
+ while (size >= CAMELLIA_BLOCK_SIZE) {
+ for (i = 0; i < CAMELLIA_BLOCK_SIZE; i++)
+ tmp[i] = in[i] ^ iv[i];
+ CAMELLIA_encrypt(tmp, out, key);
+ memcpy(iv, out, CAMELLIA_BLOCK_SIZE);
+ size -= CAMELLIA_BLOCK_SIZE;
+ in += CAMELLIA_BLOCK_SIZE;
+ out += CAMELLIA_BLOCK_SIZE;
+ }
+ if (size) {
+ for (i = 0; i < size; i++)
+ tmp[i] = in[i] ^ iv[i];
+ for (i = size; i < CAMELLIA_BLOCK_SIZE; i++)
+ tmp[i] = iv[i];
+ CAMELLIA_encrypt(tmp, out, key);
+ memcpy(iv, out, CAMELLIA_BLOCK_SIZE);
+ }
+ } else {
+ while (size >= CAMELLIA_BLOCK_SIZE) {
+ memcpy(tmp, in, CAMELLIA_BLOCK_SIZE);
+ CAMELLIA_decrypt(tmp, out, key);
+ for (i = 0; i < CAMELLIA_BLOCK_SIZE; i++)
+ out[i] ^= iv[i];
+ memcpy(iv, tmp, CAMELLIA_BLOCK_SIZE);
+ size -= CAMELLIA_BLOCK_SIZE;
+ in += CAMELLIA_BLOCK_SIZE;
+ out += CAMELLIA_BLOCK_SIZE;
+ }
+ if (size) {
+ memcpy(tmp, in, CAMELLIA_BLOCK_SIZE);
+ CAMELLIA_decrypt(tmp, out, key);
+ for (i = 0; i < size; i++)
+ out[i] ^= iv[i];
+ memcpy(iv, tmp, CAMELLIA_BLOCK_SIZE);
+ }
+ }
+}
diff --git a/source4/heimdal/lib/hcrypto/camellia.h b/source4/heimdal/lib/hcrypto/camellia.h
new file mode 100644
index 0000000000..feabae1aa3
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/camellia.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef HEIM_CAMELLIA_H
+#define HEIM_CAMELLIA_H 1
+
+/* symbol renaming */
+#define CAMELLIA_set_key hc_CAMELLIA_set_encrypt_key
+#define CAMELLIA_encrypt hc_CAMELLIA_encrypt
+#define CAMELLIA_decrypt hc_CAMELLIA_decrypt
+#define CAMELLIA_cbc_encrypt hc_CAMELLIA_cbc_encrypt
+
+/*
+ *
+ */
+
+#define CAMELLIA_BLOCK_SIZE 16
+#define CAMELLIA_TABLE_BYTE_LEN 272
+#define CAMELLIA_TABLE_WORD_LEN (CAMELLIA_TABLE_BYTE_LEN / 4)
+
+#define CAMELLIA_ENCRYPT 1
+#define CAMELLIA_DECRYPT 0
+
+typedef struct camellia_key {
+ unsigned int bits;
+ uint32_t key[CAMELLIA_TABLE_WORD_LEN];
+} CAMELLIA_KEY;
+
+int CAMELLIA_set_key(const unsigned char *, const int, CAMELLIA_KEY *);
+
+void CAMELLIA_encrypt(const unsigned char *, unsigned char *,
+ const CAMELLIA_KEY *);
+void CAMELLIA_decrypt(const unsigned char *, unsigned char *,
+ const CAMELLIA_KEY *);
+
+void CAMELLIA_cbc_encrypt(const unsigned char *, unsigned char *,
+ const unsigned long, const CAMELLIA_KEY *,
+ unsigned char *, int);
+
+#endif /* HEIM_CAMELLIA_H */
diff --git a/source4/heimdal/lib/hcrypto/des-tables.h b/source4/heimdal/lib/hcrypto/des-tables.h
new file mode 100644
index 0000000000..95f3711747
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/des-tables.h
@@ -0,0 +1,196 @@
+/* GENERATE FILE from gen-des.pl, do not edit */
+
+/* pc1_c_3 bit pattern 5 13 21 */
+static int pc1_c_3[8] = {
+ 0x00000000, 0x00000010, 0x00001000, 0x00001010,
+ 0x00100000, 0x00100010, 0x00101000, 0x00101010
+};
+/* pc1_c_4 bit pattern 1 9 17 25 */
+static int pc1_c_4[16] = {
+ 0x00000000, 0x00000001, 0x00000100, 0x00000101,
+ 0x00010000, 0x00010001, 0x00010100, 0x00010101,
+ 0x01000000, 0x01000001, 0x01000100, 0x01000101,
+ 0x01010000, 0x01010001, 0x01010100, 0x01010101
+};
+/* pc1_d_3 bit pattern 49 41 33 */
+static int pc1_d_3[8] = {
+ 0x00000000, 0x01000000, 0x00010000, 0x01010000,
+ 0x00000100, 0x01000100, 0x00010100, 0x01010100
+};
+/* pc1_d_4 bit pattern 57 53 45 37 */
+static int pc1_d_4[16] = {
+ 0x00000000, 0x00100000, 0x00001000, 0x00101000,
+ 0x00000010, 0x00100010, 0x00001010, 0x00101010,
+ 0x00000001, 0x00100001, 0x00001001, 0x00101001,
+ 0x00000011, 0x00100011, 0x00001011, 0x00101011
+};
+/* pc2_c_1 bit pattern 5 24 7 16 6 10 */
+static int pc2_c_1[64] = {
+ 0x00000000, 0x00004000, 0x00040000, 0x00044000,
+ 0x00000100, 0x00004100, 0x00040100, 0x00044100,
+ 0x00020000, 0x00024000, 0x00060000, 0x00064000,
+ 0x00020100, 0x00024100, 0x00060100, 0x00064100,
+ 0x00000001, 0x00004001, 0x00040001, 0x00044001,
+ 0x00000101, 0x00004101, 0x00040101, 0x00044101,
+ 0x00020001, 0x00024001, 0x00060001, 0x00064001,
+ 0x00020101, 0x00024101, 0x00060101, 0x00064101,
+ 0x00080000, 0x00084000, 0x000c0000, 0x000c4000,
+ 0x00080100, 0x00084100, 0x000c0100, 0x000c4100,
+ 0x000a0000, 0x000a4000, 0x000e0000, 0x000e4000,
+ 0x000a0100, 0x000a4100, 0x000e0100, 0x000e4100,
+ 0x00080001, 0x00084001, 0x000c0001, 0x000c4001,
+ 0x00080101, 0x00084101, 0x000c0101, 0x000c4101,
+ 0x000a0001, 0x000a4001, 0x000e0001, 0x000e4001,
+ 0x000a0101, 0x000a4101, 0x000e0101, 0x000e4101
+};
+/* pc2_c_2 bit pattern 20 18 12 3 15 23 */
+static int pc2_c_2[64] = {
+ 0x00000000, 0x00000002, 0x00000200, 0x00000202,
+ 0x00200000, 0x00200002, 0x00200200, 0x00200202,
+ 0x00001000, 0x00001002, 0x00001200, 0x00001202,
+ 0x00201000, 0x00201002, 0x00201200, 0x00201202,
+ 0x00000040, 0x00000042, 0x00000240, 0x00000242,
+ 0x00200040, 0x00200042, 0x00200240, 0x00200242,
+ 0x00001040, 0x00001042, 0x00001240, 0x00001242,
+ 0x00201040, 0x00201042, 0x00201240, 0x00201242,
+ 0x00000010, 0x00000012, 0x00000210, 0x00000212,
+ 0x00200010, 0x00200012, 0x00200210, 0x00200212,
+ 0x00001010, 0x00001012, 0x00001210, 0x00001212,
+ 0x00201010, 0x00201012, 0x00201210, 0x00201212,
+ 0x00000050, 0x00000052, 0x00000250, 0x00000252,
+ 0x00200050, 0x00200052, 0x00200250, 0x00200252,
+ 0x00001050, 0x00001052, 0x00001250, 0x00001252,
+ 0x00201050, 0x00201052, 0x00201250, 0x00201252
+};
+/* pc2_c_3 bit pattern 1 9 19 2 14 22 */
+static int pc2_c_3[64] = {
+ 0x00000000, 0x00000004, 0x00000400, 0x00000404,
+ 0x00400000, 0x00400004, 0x00400400, 0x00400404,
+ 0x00000020, 0x00000024, 0x00000420, 0x00000424,
+ 0x00400020, 0x00400024, 0x00400420, 0x00400424,
+ 0x00008000, 0x00008004, 0x00008400, 0x00008404,
+ 0x00408000, 0x00408004, 0x00408400, 0x00408404,
+ 0x00008020, 0x00008024, 0x00008420, 0x00008424,
+ 0x00408020, 0x00408024, 0x00408420, 0x00408424,
+ 0x00800000, 0x00800004, 0x00800400, 0x00800404,
+ 0x00c00000, 0x00c00004, 0x00c00400, 0x00c00404,
+ 0x00800020, 0x00800024, 0x00800420, 0x00800424,
+ 0x00c00020, 0x00c00024, 0x00c00420, 0x00c00424,
+ 0x00808000, 0x00808004, 0x00808400, 0x00808404,
+ 0x00c08000, 0x00c08004, 0x00c08400, 0x00c08404,
+ 0x00808020, 0x00808024, 0x00808420, 0x00808424,
+ 0x00c08020, 0x00c08024, 0x00c08420, 0x00c08424
+};
+/* pc2_c_4 bit pattern 11 13 4 17 21 8 */
+static int pc2_c_4[64] = {
+ 0x00000000, 0x00010000, 0x00000008, 0x00010008,
+ 0x00000080, 0x00010080, 0x00000088, 0x00010088,
+ 0x00100000, 0x00110000, 0x00100008, 0x00110008,
+ 0x00100080, 0x00110080, 0x00100088, 0x00110088,
+ 0x00000800, 0x00010800, 0x00000808, 0x00010808,
+ 0x00000880, 0x00010880, 0x00000888, 0x00010888,
+ 0x00100800, 0x00110800, 0x00100808, 0x00110808,
+ 0x00100880, 0x00110880, 0x00100888, 0x00110888,
+ 0x00002000, 0x00012000, 0x00002008, 0x00012008,
+ 0x00002080, 0x00012080, 0x00002088, 0x00012088,
+ 0x00102000, 0x00112000, 0x00102008, 0x00112008,
+ 0x00102080, 0x00112080, 0x00102088, 0x00112088,
+ 0x00002800, 0x00012800, 0x00002808, 0x00012808,
+ 0x00002880, 0x00012880, 0x00002888, 0x00012888,
+ 0x00102800, 0x00112800, 0x00102808, 0x00112808,
+ 0x00102880, 0x00112880, 0x00102888, 0x00112888
+};
+/* pc2_d_1 bit pattern 51 35 31 52 39 45 */
+static int pc2_d_1[64] = {
+ 0x00000000, 0x00000080, 0x00002000, 0x00002080,
+ 0x00000001, 0x00000081, 0x00002001, 0x00002081,
+ 0x00200000, 0x00200080, 0x00202000, 0x00202080,
+ 0x00200001, 0x00200081, 0x00202001, 0x00202081,
+ 0x00020000, 0x00020080, 0x00022000, 0x00022080,
+ 0x00020001, 0x00020081, 0x00022001, 0x00022081,
+ 0x00220000, 0x00220080, 0x00222000, 0x00222080,
+ 0x00220001, 0x00220081, 0x00222001, 0x00222081,
+ 0x00000002, 0x00000082, 0x00002002, 0x00002082,
+ 0x00000003, 0x00000083, 0x00002003, 0x00002083,
+ 0x00200002, 0x00200082, 0x00202002, 0x00202082,
+ 0x00200003, 0x00200083, 0x00202003, 0x00202083,
+ 0x00020002, 0x00020082, 0x00022002, 0x00022082,
+ 0x00020003, 0x00020083, 0x00022003, 0x00022083,
+ 0x00220002, 0x00220082, 0x00222002, 0x00222082,
+ 0x00220003, 0x00220083, 0x00222003, 0x00222083
+};
+/* pc2_d_2 bit pattern 50 32 43 36 29 48 */
+static int pc2_d_2[64] = {
+ 0x00000000, 0x00000010, 0x00800000, 0x00800010,
+ 0x00010000, 0x00010010, 0x00810000, 0x00810010,
+ 0x00000200, 0x00000210, 0x00800200, 0x00800210,
+ 0x00010200, 0x00010210, 0x00810200, 0x00810210,
+ 0x00100000, 0x00100010, 0x00900000, 0x00900010,
+ 0x00110000, 0x00110010, 0x00910000, 0x00910010,
+ 0x00100200, 0x00100210, 0x00900200, 0x00900210,
+ 0x00110200, 0x00110210, 0x00910200, 0x00910210,
+ 0x00000004, 0x00000014, 0x00800004, 0x00800014,
+ 0x00010004, 0x00010014, 0x00810004, 0x00810014,
+ 0x00000204, 0x00000214, 0x00800204, 0x00800214,
+ 0x00010204, 0x00010214, 0x00810204, 0x00810214,
+ 0x00100004, 0x00100014, 0x00900004, 0x00900014,
+ 0x00110004, 0x00110014, 0x00910004, 0x00910014,
+ 0x00100204, 0x00100214, 0x00900204, 0x00900214,
+ 0x00110204, 0x00110214, 0x00910204, 0x00910214
+};
+/* pc2_d_3 bit pattern 41 38 47 33 40 42 */
+static int pc2_d_3[64] = {
+ 0x00000000, 0x00000400, 0x00001000, 0x00001400,
+ 0x00080000, 0x00080400, 0x00081000, 0x00081400,
+ 0x00000020, 0x00000420, 0x00001020, 0x00001420,
+ 0x00080020, 0x00080420, 0x00081020, 0x00081420,
+ 0x00004000, 0x00004400, 0x00005000, 0x00005400,
+ 0x00084000, 0x00084400, 0x00085000, 0x00085400,
+ 0x00004020, 0x00004420, 0x00005020, 0x00005420,
+ 0x00084020, 0x00084420, 0x00085020, 0x00085420,
+ 0x00000800, 0x00000c00, 0x00001800, 0x00001c00,
+ 0x00080800, 0x00080c00, 0x00081800, 0x00081c00,
+ 0x00000820, 0x00000c20, 0x00001820, 0x00001c20,
+ 0x00080820, 0x00080c20, 0x00081820, 0x00081c20,
+ 0x00004800, 0x00004c00, 0x00005800, 0x00005c00,
+ 0x00084800, 0x00084c00, 0x00085800, 0x00085c00,
+ 0x00004820, 0x00004c20, 0x00005820, 0x00005c20,
+ 0x00084820, 0x00084c20, 0x00085820, 0x00085c20
+};
+/* pc2_d_4 bit pattern 49 37 30 46 34 44 */
+static int pc2_d_4[64] = {
+ 0x00000000, 0x00000100, 0x00040000, 0x00040100,
+ 0x00000040, 0x00000140, 0x00040040, 0x00040140,
+ 0x00400000, 0x00400100, 0x00440000, 0x00440100,
+ 0x00400040, 0x00400140, 0x00440040, 0x00440140,
+ 0x00008000, 0x00008100, 0x00048000, 0x00048100,
+ 0x00008040, 0x00008140, 0x00048040, 0x00048140,
+ 0x00408000, 0x00408100, 0x00448000, 0x00448100,
+ 0x00408040, 0x00408140, 0x00448040, 0x00448140,
+ 0x00000008, 0x00000108, 0x00040008, 0x00040108,
+ 0x00000048, 0x00000148, 0x00040048, 0x00040148,
+ 0x00400008, 0x00400108, 0x00440008, 0x00440108,
+ 0x00400048, 0x00400148, 0x00440048, 0x00440148,
+ 0x00008008, 0x00008108, 0x00048008, 0x00048108,
+ 0x00008048, 0x00008148, 0x00048048, 0x00048148,
+ 0x00408008, 0x00408108, 0x00448008, 0x00448108,
+ 0x00408048, 0x00408148, 0x00448048, 0x00448148
+};
+static unsigned char odd_parity[256] = {
+ 1, 1, 2, 2, 4, 4, 7, 7, 8, 8, 11, 11, 13, 13, 14, 14,
+ 16, 16, 19, 19, 21, 21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31,
+ 32, 32, 35, 35, 37, 37, 38, 38, 41, 41, 42, 42, 44, 44, 47, 47,
+ 49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 59, 59, 61, 61, 62, 62,
+ 64, 64, 67, 67, 69, 69, 70, 70, 73, 73, 74, 74, 76, 76, 79, 79,
+ 81, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91, 91, 93, 93, 94, 94,
+ 97, 97, 98, 98,100,100,103,103,104,104,107,107,109,109,110,110,
+112,112,115,115,117,117,118,118,121,121,122,122,124,124,127,127,
+128,128,131,131,133,133,134,134,137,137,138,138,140,140,143,143,
+145,145,146,146,148,148,151,151,152,152,155,155,157,157,158,158,
+161,161,162,162,164,164,167,167,168,168,171,171,173,173,174,174,
+176,176,179,179,181,181,182,182,185,185,186,186,188,188,191,191,
+193,193,194,194,196,196,199,199,200,200,203,203,205,205,206,206,
+208,208,211,211,213,213,214,214,217,217,218,218,220,220,223,223,
+224,224,227,227,229,229,230,230,233,233,234,234,236,236,239,239,
+241,241,242,242,244,244,247,247,248,248,251,251,253,253,254,254,
+ };
diff --git a/source4/heimdal/lib/hcrypto/des.c b/source4/heimdal/lib/hcrypto/des.c
new file mode 100644
index 0000000000..5e258dfbcc
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/des.c
@@ -0,0 +1,1185 @@
+/*
+ * Copyright (c) 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/**
+ * @page page_des DES - Data Encryption Standard crypto interface
+ *
+ * See the library functions here: @ref hcrypto_des
+ *
+ * DES was created by IBM, modififed by NSA and then adopted by NBS
+ * (now NIST) and published ad FIPS PUB 46 (updated by FIPS 46-1).
+ *
+ * Since the 19th May 2005 DES was withdrawn by NIST and should no
+ * longer be used. See @ref page_evp for replacement encryption
+ * algorithms and interfaces.
+ *
+ * Read more the iteresting history of DES on Wikipedia
+ * http://www.wikipedia.org/wiki/Data_Encryption_Standard .
+ *
+ * @section des_keygen DES key generation
+ *
+ * To generate a DES key safely you have to use the code-snippet
+ * below. This is because the DES_random_key() can fail with an
+ * abort() in case of and failure to start the random generator.
+ *
+ * There is a replacement function DES_new_random_key(), however that
+ * function does not exists in OpenSSL.
+ *
+ * @code
+ * DES_cblock key;
+ * do {
+ * if (RAND_rand(&key, sizeof(key)) != 1)
+ * goto failure;
+ * DES_set_odd_parity(key);
+ * } while (DES_is_weak_key(&key));
+ * @endcode
+ *
+ * @section des_impl DES implementation history
+ *
+ * There was no complete BSD licensed, fast, GPL compatible
+ * implementation of DES, so Love wrote the part that was missing,
+ * fast key schedule setup and adapted the interface to the orignal
+ * libdes.
+ *
+ * The document that got me started for real was "Efficient
+ * Implementation of the Data Encryption Standard" by Dag Arne Osvik.
+ * I never got to the PC1 transformation was working, instead I used
+ * table-lookup was used for all key schedule setup. The document was
+ * very useful since it de-mystified other implementations for me.
+ *
+ * The core DES function (SBOX + P transformation) is from Richard
+ * Outerbridge public domain DES implementation. My sanity is saved
+ * thanks to his work. Thank you Richard.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+RCSID("$Id$");
+#endif
+
+#define HC_DEPRECATED
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <krb5-types.h>
+#include <assert.h>
+
+#include "des.h"
+#include "ui.h"
+
+static void desx(uint32_t [2], DES_key_schedule *, int);
+static void IP(uint32_t [2]);
+static void FP(uint32_t [2]);
+
+#include "des-tables.h"
+
+#define ROTATE_LEFT28(x,one) \
+ if (one) { \
+ x = ( ((x)<<(1)) & 0xffffffe) | ((x) >> 27); \
+ } else { \
+ x = ( ((x)<<(2)) & 0xffffffc) | ((x) >> 26); \
+ }
+
+/**
+ * Set the parity of the key block, used to generate a des key from a
+ * random key. See @ref des_keygen.
+ *
+ * @param key key to fixup the parity for.
+ * @ingroup hcrypto_des
+ */
+
+void
+DES_set_odd_parity(DES_cblock *key)
+{
+ unsigned int i;
+ for (i = 0; i < DES_CBLOCK_LEN; i++)
+ (*key)[i] = odd_parity[(*key)[i]];
+}
+
+/**
+ * Check if the key have correct parity.
+ *
+ * @param key key to check the parity.
+ * @return 1 on success, 0 on failure.
+ * @ingroup hcrypto_des
+ */
+
+int HC_DEPRECATED
+DES_check_key_parity(DES_cblock *key)
+{
+ unsigned int i;
+
+ for (i = 0; i < DES_CBLOCK_LEN; i++)
+ if ((*key)[i] != odd_parity[(*key)[i]])
+ return 0;
+ return 1;
+}
+
+/*
+ *
+ */
+
+/* FIPS 74 */
+static DES_cblock weak_keys[] = {
+ {0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01}, /* weak keys */
+ {0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE},
+ {0x1F,0x1F,0x1F,0x1F,0x0E,0x0E,0x0E,0x0E},
+ {0xE0,0xE0,0xE0,0xE0,0xF1,0xF1,0xF1,0xF1},
+ {0x01,0xFE,0x01,0xFE,0x01,0xFE,0x01,0xFE}, /* semi-weak keys */
+ {0xFE,0x01,0xFE,0x01,0xFE,0x01,0xFE,0x01},
+ {0x1F,0xE0,0x1F,0xE0,0x0E,0xF1,0x0E,0xF1},
+ {0xE0,0x1F,0xE0,0x1F,0xF1,0x0E,0xF1,0x0E},
+ {0x01,0xE0,0x01,0xE0,0x01,0xF1,0x01,0xF1},
+ {0xE0,0x01,0xE0,0x01,0xF1,0x01,0xF1,0x01},
+ {0x1F,0xFE,0x1F,0xFE,0x0E,0xFE,0x0E,0xFE},
+ {0xFE,0x1F,0xFE,0x1F,0xFE,0x0E,0xFE,0x0E},
+ {0x01,0x1F,0x01,0x1F,0x01,0x0E,0x01,0x0E},
+ {0x1F,0x01,0x1F,0x01,0x0E,0x01,0x0E,0x01},
+ {0xE0,0xFE,0xE0,0xFE,0xF1,0xFE,0xF1,0xFE},
+ {0xFE,0xE0,0xFE,0xE0,0xFE,0xF1,0xFE,0xF1}
+};
+
+/**
+ * Checks if the key is any of the weaks keys that makes DES attacks
+ * trival.
+ *
+ * @param key key to check.
+ *
+ * @return 1 if the key is weak, 0 otherwise.
+ * @ingroup hcrypto_des
+ */
+
+int
+DES_is_weak_key(DES_cblock *key)
+{
+ int i;
+
+ for (i = 0; i < sizeof(weak_keys)/sizeof(weak_keys[0]); i++) {
+ if (memcmp(weak_keys[i], key, DES_CBLOCK_LEN) == 0)
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * Setup a des key schedule from a key. Deprecated function, use
+ * DES_set_key_unchecked() or DES_set_key_checked() instead.
+ *
+ * @param key a key to initialize the key schedule with.
+ * @param ks a key schedule to initialize.
+ *
+ * @return 0 on success
+ * @ingroup hcrypto_des
+ */
+
+int HC_DEPRECATED
+DES_set_key(DES_cblock *key, DES_key_schedule *ks)
+{
+ return DES_set_key_checked(key, ks);
+}
+
+/**
+ * Setup a des key schedule from a key. The key is no longer needed
+ * after this transaction and can cleared.
+ *
+ * Does NOT check that the key is weak for or have wrong parity.
+ *
+ * @param key a key to initialize the key schedule with.
+ * @param ks a key schedule to initialize.
+ *
+ * @return 0 on success
+ * @ingroup hcrypto_des
+ */
+
+int
+DES_set_key_unchecked(DES_cblock *key, DES_key_schedule *ks)
+{
+ uint32_t t1, t2;
+ uint32_t c, d;
+ int shifts[16] = { 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1 };
+ uint32_t *k = &ks->ks[0];
+ int i;
+
+ t1 = (*key)[0] << 24 | (*key)[1] << 16 | (*key)[2] << 8 | (*key)[3];
+ t2 = (*key)[4] << 24 | (*key)[5] << 16 | (*key)[6] << 8 | (*key)[7];
+
+ c = (pc1_c_3[(t1 >> (5 )) & 0x7] << 3)
+ | (pc1_c_3[(t1 >> (5 + 8 )) & 0x7] << 2)
+ | (pc1_c_3[(t1 >> (5 + 8 + 8 )) & 0x7] << 1)
+ | (pc1_c_3[(t1 >> (5 + 8 + 8 + 8)) & 0x7] << 0)
+ | (pc1_c_4[(t2 >> (4 )) & 0xf] << 3)
+ | (pc1_c_4[(t2 >> (4 + 8 )) & 0xf] << 2)
+ | (pc1_c_4[(t2 >> (4 + 8 + 8 )) & 0xf] << 1)
+ | (pc1_c_4[(t2 >> (4 + 8 + 8 + 8)) & 0xf] << 0);
+
+
+ d = (pc1_d_3[(t2 >> (1 )) & 0x7] << 3)
+ | (pc1_d_3[(t2 >> (1 + 8 )) & 0x7] << 2)
+ | (pc1_d_3[(t2 >> (1 + 8 + 8 )) & 0x7] << 1)
+ | (pc1_d_3[(t2 >> (1 + 8 + 8 + 8)) & 0x7] << 0)
+ | (pc1_d_4[(t1 >> (1 )) & 0xf] << 3)
+ | (pc1_d_4[(t1 >> (1 + 8 )) & 0xf] << 2)
+ | (pc1_d_4[(t1 >> (1 + 8 + 8 )) & 0xf] << 1)
+ | (pc1_d_4[(t1 >> (1 + 8 + 8 + 8)) & 0xf] << 0);
+
+ for (i = 0; i < 16; i++) {
+ uint32_t kc, kd;
+
+ ROTATE_LEFT28(c, shifts[i]);
+ ROTATE_LEFT28(d, shifts[i]);
+
+ kc = pc2_c_1[(c >> 22) & 0x3f] |
+ pc2_c_2[((c >> 16) & 0x30) | ((c >> 15) & 0xf)] |
+ pc2_c_3[((c >> 9 ) & 0x3c) | ((c >> 8 ) & 0x3)] |
+ pc2_c_4[((c >> 2 ) & 0x20) | ((c >> 1) & 0x18) | (c & 0x7)];
+ kd = pc2_d_1[(d >> 22) & 0x3f] |
+ pc2_d_2[((d >> 15) & 0x30) | ((d >> 14) & 0xf)] |
+ pc2_d_3[ (d >> 7 ) & 0x3f] |
+ pc2_d_4[((d >> 1 ) & 0x3c) | ((d ) & 0x3)];
+
+ /* Change to byte order used by the S boxes */
+ *k = (kc & 0x00fc0000L) << 6;
+ *k |= (kc & 0x00000fc0L) << 10;
+ *k |= (kd & 0x00fc0000L) >> 10;
+ *k++ |= (kd & 0x00000fc0L) >> 6;
+ *k = (kc & 0x0003f000L) << 12;
+ *k |= (kc & 0x0000003fL) << 16;
+ *k |= (kd & 0x0003f000L) >> 4;
+ *k++ |= (kd & 0x0000003fL);
+ }
+
+ return 0;
+}
+
+/**
+ * Just like DES_set_key_unchecked() except checking that the key is
+ * not weak for or have correct parity.
+ *
+ * @param key a key to initialize the key schedule with.
+ * @param ks a key schedule to initialize.
+ *
+ * @return 0 on success, -1 on invalid parity, -2 on weak key.
+ * @ingroup hcrypto_des
+ */
+
+int
+DES_set_key_checked(DES_cblock *key, DES_key_schedule *ks)
+{
+ if (!DES_check_key_parity(key)) {
+ memset(ks, 0, sizeof(*ks));
+ return -1;
+ }
+ if (DES_is_weak_key(key)) {
+ memset(ks, 0, sizeof(*ks));
+ return -2;
+ }
+ return DES_set_key_unchecked(key, ks);
+}
+
+/**
+ * Compatibility function for eay libdes, works just like
+ * DES_set_key_checked().
+ *
+ * @param key a key to initialize the key schedule with.
+ * @param ks a key schedule to initialize.
+ *
+ * @return 0 on success, -1 on invalid parity, -2 on weak key.
+ * @ingroup hcrypto_des
+ */
+
+int
+DES_key_sched(DES_cblock *key, DES_key_schedule *ks)
+{
+ return DES_set_key_checked(key, ks);
+}
+
+/*
+ *
+ */
+
+static void
+load(const unsigned char *b, uint32_t v[2])
+{
+ v[0] = b[0] << 24;
+ v[0] |= b[1] << 16;
+ v[0] |= b[2] << 8;
+ v[0] |= b[3] << 0;
+ v[1] = b[4] << 24;
+ v[1] |= b[5] << 16;
+ v[1] |= b[6] << 8;
+ v[1] |= b[7] << 0;
+}
+
+static void
+store(const uint32_t v[2], unsigned char *b)
+{
+ b[0] = (v[0] >> 24) & 0xff;
+ b[1] = (v[0] >> 16) & 0xff;
+ b[2] = (v[0] >> 8) & 0xff;
+ b[3] = (v[0] >> 0) & 0xff;
+ b[4] = (v[1] >> 24) & 0xff;
+ b[5] = (v[1] >> 16) & 0xff;
+ b[6] = (v[1] >> 8) & 0xff;
+ b[7] = (v[1] >> 0) & 0xff;
+}
+
+/**
+ * Encrypt/decrypt a block using DES. Also called ECB mode
+ *
+ * @param u data to encrypt
+ * @param ks key schedule to use
+ * @param encp if non zero, encrypt. if zero, decrypt.
+ *
+ * @ingroup hcrypto_des
+ */
+
+void
+DES_encrypt(uint32_t u[2], DES_key_schedule *ks, int encp)
+{
+ IP(u);
+ desx(u, ks, encp);
+ FP(u);
+}
+
+/**
+ * Encrypt/decrypt a block using DES.
+ *
+ * @param input data to encrypt
+ * @param output data to encrypt
+ * @param ks key schedule to use
+ * @param encp if non zero, encrypt. if zero, decrypt.
+ *
+ * @ingroup hcrypto_des
+ */
+
+void
+DES_ecb_encrypt(DES_cblock *input, DES_cblock *output,
+ DES_key_schedule *ks, int encp)
+{
+ uint32_t u[2];
+ load(*input, u);
+ DES_encrypt(u, ks, encp);
+ store(u, *output);
+}
+
+/**
+ * Encrypt/decrypt a block using DES in Chain Block Cipher mode (cbc).
+ *
+ * The IV must always be diffrent for diffrent input data blocks.
+ *
+ * @param in data to encrypt
+ * @param out data to encrypt
+ * @param length length of data
+ * @param ks key schedule to use
+ * @param iv initial vector to use
+ * @param encp if non zero, encrypt. if zero, decrypt.
+ *
+ * @ingroup hcrypto_des
+ */
+
+void
+DES_cbc_encrypt(const void *in, void *out, long length,
+ DES_key_schedule *ks, DES_cblock *iv, int encp)
+{
+ const unsigned char *input = in;
+ unsigned char *output = out;
+ uint32_t u[2];
+ uint32_t uiv[2];
+
+ load(*iv, uiv);
+
+ if (encp) {
+ while (length >= DES_CBLOCK_LEN) {
+ load(input, u);
+ u[0] ^= uiv[0]; u[1] ^= uiv[1];
+ DES_encrypt(u, ks, 1);
+ uiv[0] = u[0]; uiv[1] = u[1];
+ store(u, output);
+
+ length -= DES_CBLOCK_LEN;
+ input += DES_CBLOCK_LEN;
+ output += DES_CBLOCK_LEN;
+ }
+ if (length) {
+ unsigned char tmp[DES_CBLOCK_LEN];
+ memcpy(tmp, input, length);
+ memset(tmp + length, 0, DES_CBLOCK_LEN - length);
+ load(tmp, u);
+ u[0] ^= uiv[0]; u[1] ^= uiv[1];
+ DES_encrypt(u, ks, 1);
+ store(u, output);
+ }
+ } else {
+ uint32_t t[2];
+ while (length >= DES_CBLOCK_LEN) {
+ load(input, u);
+ t[0] = u[0]; t[1] = u[1];
+ DES_encrypt(u, ks, 0);
+ u[0] ^= uiv[0]; u[1] ^= uiv[1];
+ store(u, output);
+ uiv[0] = t[0]; uiv[1] = t[1];
+
+ length -= DES_CBLOCK_LEN;
+ input += DES_CBLOCK_LEN;
+ output += DES_CBLOCK_LEN;
+ }
+ if (length) {
+ unsigned char tmp[DES_CBLOCK_LEN];
+ memcpy(tmp, input, length);
+ memset(tmp + length, 0, DES_CBLOCK_LEN - length);
+ load(tmp, u);
+ DES_encrypt(u, ks, 0);
+ u[0] ^= uiv[0]; u[1] ^= uiv[1];
+ store(u, output);
+ }
+ }
+ uiv[0] = 0; u[0] = 0; uiv[1] = 0; u[1] = 0;
+}
+
+/**
+ * Encrypt/decrypt a block using DES in Propagating Cipher Block
+ * Chaining mode. This mode is only used for Kerberos 4, and it should
+ * stay that way.
+ *
+ * The IV must always be diffrent for diffrent input data blocks.
+ *
+ * @param in data to encrypt
+ * @param out data to encrypt
+ * @param length length of data
+ * @param ks key schedule to use
+ * @param iv initial vector to use
+ * @param encp if non zero, encrypt. if zero, decrypt.
+ *
+ * @ingroup hcrypto_des
+ */
+
+void
+DES_pcbc_encrypt(const void *in, void *out, long length,
+ DES_key_schedule *ks, DES_cblock *iv, int encp)
+{
+ const unsigned char *input = in;
+ unsigned char *output = out;
+ uint32_t u[2];
+ uint32_t uiv[2];
+
+ load(*iv, uiv);
+
+ if (encp) {
+ uint32_t t[2];
+ while (length >= DES_CBLOCK_LEN) {
+ load(input, u);
+ t[0] = u[0]; t[1] = u[1];
+ u[0] ^= uiv[0]; u[1] ^= uiv[1];
+ DES_encrypt(u, ks, 1);
+ uiv[0] = u[0] ^ t[0]; uiv[1] = u[1] ^ t[1];
+ store(u, output);
+
+ length -= DES_CBLOCK_LEN;
+ input += DES_CBLOCK_LEN;
+ output += DES_CBLOCK_LEN;
+ }
+ if (length) {
+ unsigned char tmp[DES_CBLOCK_LEN];
+ memcpy(tmp, input, length);
+ memset(tmp + length, 0, DES_CBLOCK_LEN - length);
+ load(tmp, u);
+ u[0] ^= uiv[0]; u[1] ^= uiv[1];
+ DES_encrypt(u, ks, 1);
+ store(u, output);
+ }
+ } else {
+ uint32_t t[2];
+ while (length >= DES_CBLOCK_LEN) {
+ load(input, u);
+ t[0] = u[0]; t[1] = u[1];
+ DES_encrypt(u, ks, 0);
+ u[0] ^= uiv[0]; u[1] ^= uiv[1];
+ store(u, output);
+ uiv[0] = t[0] ^ u[0]; uiv[1] = t[1] ^ u[1];
+
+ length -= DES_CBLOCK_LEN;
+ input += DES_CBLOCK_LEN;
+ output += DES_CBLOCK_LEN;
+ }
+ if (length) {
+ unsigned char tmp[DES_CBLOCK_LEN];
+ memcpy(tmp, input, length);
+ memset(tmp + length, 0, DES_CBLOCK_LEN - length);
+ load(tmp, u);
+ DES_encrypt(u, ks, 0);
+ u[0] ^= uiv[0]; u[1] ^= uiv[1];
+ }
+ }
+ uiv[0] = 0; u[0] = 0; uiv[1] = 0; u[1] = 0;
+}
+
+/*
+ *
+ */
+
+static void
+_des3_encrypt(uint32_t u[2], DES_key_schedule *ks1, DES_key_schedule *ks2,
+ DES_key_schedule *ks3, int encp)
+{
+ IP(u);
+ if (encp) {
+ desx(u, ks1, 1); /* IP + FP cancel out each other */
+ desx(u, ks2, 0);
+ desx(u, ks3, 1);
+ } else {
+ desx(u, ks3, 0);
+ desx(u, ks2, 1);
+ desx(u, ks1, 0);
+ }
+ FP(u);
+}
+
+/**
+ * Encrypt/decrypt a block using triple DES using EDE mode,
+ * encrypt/decrypt/encrypt.
+ *
+ * @param input data to encrypt
+ * @param output data to encrypt
+ * @param ks1 key schedule to use
+ * @param ks2 key schedule to use
+ * @param ks3 key schedule to use
+ * @param encp if non zero, encrypt. if zero, decrypt.
+ *
+ * @ingroup hcrypto_des
+ */
+
+void
+DES_ecb3_encrypt(DES_cblock *input,
+ DES_cblock *output,
+ DES_key_schedule *ks1,
+ DES_key_schedule *ks2,
+ DES_key_schedule *ks3,
+ int encp)
+{
+ uint32_t u[2];
+ load(*input, u);
+ _des3_encrypt(u, ks1, ks2, ks3, encp);
+ store(u, *output);
+ return;
+}
+
+/**
+ * Encrypt/decrypt using Triple DES in Chain Block Cipher mode (cbc).
+ *
+ * The IV must always be diffrent for diffrent input data blocks.
+ *
+ * @param in data to encrypt
+ * @param out data to encrypt
+ * @param length length of data
+ * @param ks1 key schedule to use
+ * @param ks2 key schedule to use
+ * @param ks3 key schedule to use
+ * @param iv initial vector to use
+ * @param encp if non zero, encrypt. if zero, decrypt.
+ *
+ * @ingroup hcrypto_des
+ */
+
+void
+DES_ede3_cbc_encrypt(const void *in, void *out,
+ long length, DES_key_schedule *ks1,
+ DES_key_schedule *ks2, DES_key_schedule *ks3,
+ DES_cblock *iv, int encp)
+{
+ const unsigned char *input = in;
+ unsigned char *output = out;
+ uint32_t u[2];
+ uint32_t uiv[2];
+
+ load(*iv, uiv);
+
+ if (encp) {
+ while (length >= DES_CBLOCK_LEN) {
+ load(input, u);
+ u[0] ^= uiv[0]; u[1] ^= uiv[1];
+ _des3_encrypt(u, ks1, ks2, ks3, 1);
+ uiv[0] = u[0]; uiv[1] = u[1];
+ store(u, output);
+
+ length -= DES_CBLOCK_LEN;
+ input += DES_CBLOCK_LEN;
+ output += DES_CBLOCK_LEN;
+ }
+ if (length) {
+ unsigned char tmp[DES_CBLOCK_LEN];
+ memcpy(tmp, input, length);
+ memset(tmp + length, 0, DES_CBLOCK_LEN - length);
+ load(tmp, u);
+ u[0] ^= uiv[0]; u[1] ^= uiv[1];
+ _des3_encrypt(u, ks1, ks2, ks3, 1);
+ store(u, output);
+ }
+ } else {
+ uint32_t t[2];
+ while (length >= DES_CBLOCK_LEN) {
+ load(input, u);
+ t[0] = u[0]; t[1] = u[1];
+ _des3_encrypt(u, ks1, ks2, ks3, 0);
+ u[0] ^= uiv[0]; u[1] ^= uiv[1];
+ store(u, output);
+ uiv[0] = t[0]; uiv[1] = t[1];
+
+ length -= DES_CBLOCK_LEN;
+ input += DES_CBLOCK_LEN;
+ output += DES_CBLOCK_LEN;
+ }
+ if (length) {
+ unsigned char tmp[DES_CBLOCK_LEN];
+ memcpy(tmp, input, length);
+ memset(tmp + length, 0, DES_CBLOCK_LEN - length);
+ load(tmp, u);
+ _des3_encrypt(u, ks1, ks2, ks3, 0);
+ u[0] ^= uiv[0]; u[1] ^= uiv[1];
+ store(u, output);
+ }
+ }
+ store(uiv, *iv);
+ uiv[0] = 0; u[0] = 0; uiv[1] = 0; u[1] = 0;
+}
+
+/**
+ * Encrypt/decrypt using DES in cipher feedback mode with 64 bit
+ * feedback.
+ *
+ * The IV must always be diffrent for diffrent input data blocks.
+ *
+ * @param in data to encrypt
+ * @param out data to encrypt
+ * @param length length of data
+ * @param ks key schedule to use
+ * @param iv initial vector to use
+ * @param num offset into in cipher block encryption/decryption stop last time.
+ * @param encp if non zero, encrypt. if zero, decrypt.
+ *
+ * @ingroup hcrypto_des
+ */
+
+void
+DES_cfb64_encrypt(const void *in, void *out,
+ long length, DES_key_schedule *ks, DES_cblock *iv,
+ int *num, int encp)
+{
+ const unsigned char *input = in;
+ unsigned char *output = out;
+ unsigned char tmp[DES_CBLOCK_LEN];
+ uint32_t uiv[2];
+
+ load(*iv, uiv);
+
+ assert(*num >= 0 && *num < DES_CBLOCK_LEN);
+
+ if (encp) {
+ int i = *num;
+
+ while (length > 0) {
+ if (i == 0)
+ DES_encrypt(uiv, ks, 1);
+ store(uiv, tmp);
+ for (; i < DES_CBLOCK_LEN && i < length; i++) {
+ output[i] = tmp[i] ^ input[i];
+ }
+ if (i == DES_CBLOCK_LEN)
+ load(output, uiv);
+ output += i;
+ input += i;
+ length -= i;
+ if (i == DES_CBLOCK_LEN)
+ i = 0;
+ }
+ store(uiv, *iv);
+ *num = i;
+ } else {
+ int i = *num;
+ unsigned char c;
+
+ while (length > 0) {
+ if (i == 0) {
+ DES_encrypt(uiv, ks, 1);
+ store(uiv, tmp);
+ }
+ for (; i < DES_CBLOCK_LEN && i < length; i++) {
+ c = input[i];
+ output[i] = tmp[i] ^ input[i];
+ (*iv)[i] = c;
+ }
+ output += i;
+ input += i;
+ length -= i;
+ if (i == DES_CBLOCK_LEN) {
+ i = 0;
+ load(*iv, uiv);
+ }
+ }
+ store(uiv, *iv);
+ *num = i;
+ }
+}
+
+/**
+ * Crete a checksum using DES in CBC encryption mode. This mode is
+ * only used for Kerberos 4, and it should stay that way.
+ *
+ * The IV must always be diffrent for diffrent input data blocks.
+ *
+ * @param in data to checksum
+ * @param output the checksum
+ * @param length length of data
+ * @param ks key schedule to use
+ * @param iv initial vector to use
+ *
+ * @ingroup hcrypto_des
+ */
+
+uint32_t
+DES_cbc_cksum(const void *in, DES_cblock *output,
+ long length, DES_key_schedule *ks, DES_cblock *iv)
+{
+ const unsigned char *input = in;
+ uint32_t uiv[2];
+ uint32_t u[2] = { 0, 0 };
+
+ load(*iv, uiv);
+
+ while (length >= DES_CBLOCK_LEN) {
+ load(input, u);
+ u[0] ^= uiv[0]; u[1] ^= uiv[1];
+ DES_encrypt(u, ks, 1);
+ uiv[0] = u[0]; uiv[1] = u[1];
+
+ length -= DES_CBLOCK_LEN;
+ input += DES_CBLOCK_LEN;
+ }
+ if (length) {
+ unsigned char tmp[DES_CBLOCK_LEN];
+ memcpy(tmp, input, length);
+ memset(tmp + length, 0, DES_CBLOCK_LEN - length);
+ load(tmp, u);
+ u[0] ^= uiv[0]; u[1] ^= uiv[1];
+ DES_encrypt(u, ks, 1);
+ }
+ if (output)
+ store(u, *output);
+
+ uiv[0] = 0; u[0] = 0; uiv[1] = 0;
+ return u[1];
+}
+
+/*
+ *
+ */
+
+static unsigned char
+bitswap8(unsigned char b)
+{
+ unsigned char r = 0;
+ int i;
+ for (i = 0; i < 8; i++) {
+ r = r << 1 | (b & 1);
+ b = b >> 1;
+ }
+ return r;
+}
+
+/**
+ * Convert a string to a DES key. Use something like
+ * PKCS5_PBKDF2_HMAC_SHA1() to create key from passwords.
+ *
+ * @param str The string to convert to a key
+ * @param key the resulting key
+ *
+ * @ingroup hcrypto_des
+ */
+
+void
+DES_string_to_key(const char *str, DES_cblock *key)
+{
+ const unsigned char *s;
+ unsigned char *k;
+ DES_key_schedule ks;
+ size_t i, len;
+
+ memset(key, 0, sizeof(*key));
+ k = *key;
+ s = (const unsigned char *)str;
+
+ len = strlen(str);
+ for (i = 0; i < len; i++) {
+ if ((i % 16) < 8)
+ k[i % 8] ^= s[i] << 1;
+ else
+ k[7 - (i % 8)] ^= bitswap8(s[i]);
+ }
+ DES_set_odd_parity(key);
+ if (DES_is_weak_key(key))
+ k[7] ^= 0xF0;
+ DES_set_key(key, &ks);
+ DES_cbc_cksum(s, key, len, &ks, key);
+ memset(&ks, 0, sizeof(ks));
+ DES_set_odd_parity(key);
+ if (DES_is_weak_key(key))
+ k[7] ^= 0xF0;
+}
+
+/**
+ * Read password from prompt and create a DES key. Internal uses
+ * DES_string_to_key(). Really, go use a really string2key function
+ * like PKCS5_PBKDF2_HMAC_SHA1().
+ *
+ * @param key key to convert to
+ * @param prompt prompt to display user
+ * @param verify prompt twice.
+ *
+ * @return 1 on success, non 1 on failure.
+ */
+
+int
+DES_read_password(DES_cblock *key, char *prompt, int verify)
+{
+ char buf[512];
+ int ret;
+
+ ret = UI_UTIL_read_pw_string(buf, sizeof(buf) - 1, prompt, verify);
+ if (ret == 1)
+ DES_string_to_key(buf, key);
+ return ret;
+}
+
+/*
+ *
+ */
+
+
+void
+_DES_ipfp_test(void)
+{
+ DES_cblock k = "\x01\x02\x04\x08\x10\x20\x40\x80", k2;
+ uint32_t u[2] = { 1, 0 };
+ IP(u);
+ FP(u);
+ IP(u);
+ FP(u);
+ if (u[0] != 1 || u[1] != 0)
+ abort();
+
+ load(k, u);
+ store(u, k2);
+ if (memcmp(k, k2, 8) != 0)
+ abort();
+}
+
+/* D3DES (V5.09) -
+ *
+ * A portable, public domain, version of the Data Encryption Standard.
+ *
+ * Written with Symantec's THINK (Lightspeed) C by Richard Outerbridge.
+ * Thanks to: Dan Hoey for his excellent Initial and Inverse permutation
+ * code; Jim Gillogly & Phil Karn for the DES key schedule code; Dennis
+ * Ferguson, Eric Young and Dana How for comparing notes; and Ray Lau,
+ * for humouring me on.
+ *
+ * Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge.
+ * (GEnie : OUTER; CIS : [71755,204]) Graven Imagery, 1992.
+ */
+
+static uint32_t SP1[64] = {
+ 0x01010400L, 0x00000000L, 0x00010000L, 0x01010404L,
+ 0x01010004L, 0x00010404L, 0x00000004L, 0x00010000L,
+ 0x00000400L, 0x01010400L, 0x01010404L, 0x00000400L,
+ 0x01000404L, 0x01010004L, 0x01000000L, 0x00000004L,
+ 0x00000404L, 0x01000400L, 0x01000400L, 0x00010400L,
+ 0x00010400L, 0x01010000L, 0x01010000L, 0x01000404L,
+ 0x00010004L, 0x01000004L, 0x01000004L, 0x00010004L,
+ 0x00000000L, 0x00000404L, 0x00010404L, 0x01000000L,
+ 0x00010000L, 0x01010404L, 0x00000004L, 0x01010000L,
+ 0x01010400L, 0x01000000L, 0x01000000L, 0x00000400L,
+ 0x01010004L, 0x00010000L, 0x00010400L, 0x01000004L,
+ 0x00000400L, 0x00000004L, 0x01000404L, 0x00010404L,
+ 0x01010404L, 0x00010004L, 0x01010000L, 0x01000404L,
+ 0x01000004L, 0x00000404L, 0x00010404L, 0x01010400L,
+ 0x00000404L, 0x01000400L, 0x01000400L, 0x00000000L,
+ 0x00010004L, 0x00010400L, 0x00000000L, 0x01010004L };
+
+static uint32_t SP2[64] = {
+ 0x80108020L, 0x80008000L, 0x00008000L, 0x00108020L,
+ 0x00100000L, 0x00000020L, 0x80100020L, 0x80008020L,
+ 0x80000020L, 0x80108020L, 0x80108000L, 0x80000000L,
+ 0x80008000L, 0x00100000L, 0x00000020L, 0x80100020L,
+ 0x00108000L, 0x00100020L, 0x80008020L, 0x00000000L,
+ 0x80000000L, 0x00008000L, 0x00108020L, 0x80100000L,
+ 0x00100020L, 0x80000020L, 0x00000000L, 0x00108000L,
+ 0x00008020L, 0x80108000L, 0x80100000L, 0x00008020L,
+ 0x00000000L, 0x00108020L, 0x80100020L, 0x00100000L,
+ 0x80008020L, 0x80100000L, 0x80108000L, 0x00008000L,
+ 0x80100000L, 0x80008000L, 0x00000020L, 0x80108020L,
+ 0x00108020L, 0x00000020L, 0x00008000L, 0x80000000L,
+ 0x00008020L, 0x80108000L, 0x00100000L, 0x80000020L,
+ 0x00100020L, 0x80008020L, 0x80000020L, 0x00100020L,
+ 0x00108000L, 0x00000000L, 0x80008000L, 0x00008020L,
+ 0x80000000L, 0x80100020L, 0x80108020L, 0x00108000L };
+
+static uint32_t SP3[64] = {
+ 0x00000208L, 0x08020200L, 0x00000000L, 0x08020008L,
+ 0x08000200L, 0x00000000L, 0x00020208L, 0x08000200L,
+ 0x00020008L, 0x08000008L, 0x08000008L, 0x00020000L,
+ 0x08020208L, 0x00020008L, 0x08020000L, 0x00000208L,
+ 0x08000000L, 0x00000008L, 0x08020200L, 0x00000200L,
+ 0x00020200L, 0x08020000L, 0x08020008L, 0x00020208L,
+ 0x08000208L, 0x00020200L, 0x00020000L, 0x08000208L,
+ 0x00000008L, 0x08020208L, 0x00000200L, 0x08000000L,
+ 0x08020200L, 0x08000000L, 0x00020008L, 0x00000208L,
+ 0x00020000L, 0x08020200L, 0x08000200L, 0x00000000L,
+ 0x00000200L, 0x00020008L, 0x08020208L, 0x08000200L,
+ 0x08000008L, 0x00000200L, 0x00000000L, 0x08020008L,
+ 0x08000208L, 0x00020000L, 0x08000000L, 0x08020208L,
+ 0x00000008L, 0x00020208L, 0x00020200L, 0x08000008L,
+ 0x08020000L, 0x08000208L, 0x00000208L, 0x08020000L,
+ 0x00020208L, 0x00000008L, 0x08020008L, 0x00020200L };
+
+static uint32_t SP4[64] = {
+ 0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L,
+ 0x00802080L, 0x00800081L, 0x00800001L, 0x00002001L,
+ 0x00000000L, 0x00802000L, 0x00802000L, 0x00802081L,
+ 0x00000081L, 0x00000000L, 0x00800080L, 0x00800001L,
+ 0x00000001L, 0x00002000L, 0x00800000L, 0x00802001L,
+ 0x00000080L, 0x00800000L, 0x00002001L, 0x00002080L,
+ 0x00800081L, 0x00000001L, 0x00002080L, 0x00800080L,
+ 0x00002000L, 0x00802080L, 0x00802081L, 0x00000081L,
+ 0x00800080L, 0x00800001L, 0x00802000L, 0x00802081L,
+ 0x00000081L, 0x00000000L, 0x00000000L, 0x00802000L,
+ 0x00002080L, 0x00800080L, 0x00800081L, 0x00000001L,
+ 0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L,
+ 0x00802081L, 0x00000081L, 0x00000001L, 0x00002000L,
+ 0x00800001L, 0x00002001L, 0x00802080L, 0x00800081L,
+ 0x00002001L, 0x00002080L, 0x00800000L, 0x00802001L,
+ 0x00000080L, 0x00800000L, 0x00002000L, 0x00802080L };
+
+static uint32_t SP5[64] = {
+ 0x00000100L, 0x02080100L, 0x02080000L, 0x42000100L,
+ 0x00080000L, 0x00000100L, 0x40000000L, 0x02080000L,
+ 0x40080100L, 0x00080000L, 0x02000100L, 0x40080100L,
+ 0x42000100L, 0x42080000L, 0x00080100L, 0x40000000L,
+ 0x02000000L, 0x40080000L, 0x40080000L, 0x00000000L,
+ 0x40000100L, 0x42080100L, 0x42080100L, 0x02000100L,
+ 0x42080000L, 0x40000100L, 0x00000000L, 0x42000000L,
+ 0x02080100L, 0x02000000L, 0x42000000L, 0x00080100L,
+ 0x00080000L, 0x42000100L, 0x00000100L, 0x02000000L,
+ 0x40000000L, 0x02080000L, 0x42000100L, 0x40080100L,
+ 0x02000100L, 0x40000000L, 0x42080000L, 0x02080100L,
+ 0x40080100L, 0x00000100L, 0x02000000L, 0x42080000L,
+ 0x42080100L, 0x00080100L, 0x42000000L, 0x42080100L,
+ 0x02080000L, 0x00000000L, 0x40080000L, 0x42000000L,
+ 0x00080100L, 0x02000100L, 0x40000100L, 0x00080000L,
+ 0x00000000L, 0x40080000L, 0x02080100L, 0x40000100L };
+
+static uint32_t SP6[64] = {
+ 0x20000010L, 0x20400000L, 0x00004000L, 0x20404010L,
+ 0x20400000L, 0x00000010L, 0x20404010L, 0x00400000L,
+ 0x20004000L, 0x00404010L, 0x00400000L, 0x20000010L,
+ 0x00400010L, 0x20004000L, 0x20000000L, 0x00004010L,
+ 0x00000000L, 0x00400010L, 0x20004010L, 0x00004000L,
+ 0x00404000L, 0x20004010L, 0x00000010L, 0x20400010L,
+ 0x20400010L, 0x00000000L, 0x00404010L, 0x20404000L,
+ 0x00004010L, 0x00404000L, 0x20404000L, 0x20000000L,
+ 0x20004000L, 0x00000010L, 0x20400010L, 0x00404000L,
+ 0x20404010L, 0x00400000L, 0x00004010L, 0x20000010L,
+ 0x00400000L, 0x20004000L, 0x20000000L, 0x00004010L,
+ 0x20000010L, 0x20404010L, 0x00404000L, 0x20400000L,
+ 0x00404010L, 0x20404000L, 0x00000000L, 0x20400010L,
+ 0x00000010L, 0x00004000L, 0x20400000L, 0x00404010L,
+ 0x00004000L, 0x00400010L, 0x20004010L, 0x00000000L,
+ 0x20404000L, 0x20000000L, 0x00400010L, 0x20004010L };
+
+static uint32_t SP7[64] = {
+ 0x00200000L, 0x04200002L, 0x04000802L, 0x00000000L,
+ 0x00000800L, 0x04000802L, 0x00200802L, 0x04200800L,
+ 0x04200802L, 0x00200000L, 0x00000000L, 0x04000002L,
+ 0x00000002L, 0x04000000L, 0x04200002L, 0x00000802L,
+ 0x04000800L, 0x00200802L, 0x00200002L, 0x04000800L,
+ 0x04000002L, 0x04200000L, 0x04200800L, 0x00200002L,
+ 0x04200000L, 0x00000800L, 0x00000802L, 0x04200802L,
+ 0x00200800L, 0x00000002L, 0x04000000L, 0x00200800L,
+ 0x04000000L, 0x00200800L, 0x00200000L, 0x04000802L,
+ 0x04000802L, 0x04200002L, 0x04200002L, 0x00000002L,
+ 0x00200002L, 0x04000000L, 0x04000800L, 0x00200000L,
+ 0x04200800L, 0x00000802L, 0x00200802L, 0x04200800L,
+ 0x00000802L, 0x04000002L, 0x04200802L, 0x04200000L,
+ 0x00200800L, 0x00000000L, 0x00000002L, 0x04200802L,
+ 0x00000000L, 0x00200802L, 0x04200000L, 0x00000800L,
+ 0x04000002L, 0x04000800L, 0x00000800L, 0x00200002L };
+
+static uint32_t SP8[64] = {
+ 0x10001040L, 0x00001000L, 0x00040000L, 0x10041040L,
+ 0x10000000L, 0x10001040L, 0x00000040L, 0x10000000L,
+ 0x00040040L, 0x10040000L, 0x10041040L, 0x00041000L,
+ 0x10041000L, 0x00041040L, 0x00001000L, 0x00000040L,
+ 0x10040000L, 0x10000040L, 0x10001000L, 0x00001040L,
+ 0x00041000L, 0x00040040L, 0x10040040L, 0x10041000L,
+ 0x00001040L, 0x00000000L, 0x00000000L, 0x10040040L,
+ 0x10000040L, 0x10001000L, 0x00041040L, 0x00040000L,
+ 0x00041040L, 0x00040000L, 0x10041000L, 0x00001000L,
+ 0x00000040L, 0x10040040L, 0x00001000L, 0x00041040L,
+ 0x10001000L, 0x00000040L, 0x10000040L, 0x10040000L,
+ 0x10040040L, 0x10000000L, 0x00040000L, 0x10001040L,
+ 0x00000000L, 0x10041040L, 0x00040040L, 0x10000040L,
+ 0x10040000L, 0x10001000L, 0x10001040L, 0x00000000L,
+ 0x10041040L, 0x00041000L, 0x00041000L, 0x00001040L,
+ 0x00001040L, 0x00040040L, 0x10000000L, 0x10041000L };
+
+static void
+IP(uint32_t v[2])
+{
+ uint32_t work;
+
+ work = ((v[0] >> 4) ^ v[1]) & 0x0f0f0f0fL;
+ v[1] ^= work;
+ v[0] ^= (work << 4);
+ work = ((v[0] >> 16) ^ v[1]) & 0x0000ffffL;
+ v[1] ^= work;
+ v[0] ^= (work << 16);
+ work = ((v[1] >> 2) ^ v[0]) & 0x33333333L;
+ v[0] ^= work;
+ v[1] ^= (work << 2);
+ work = ((v[1] >> 8) ^ v[0]) & 0x00ff00ffL;
+ v[0] ^= work;
+ v[1] ^= (work << 8);
+ v[1] = ((v[1] << 1) | ((v[1] >> 31) & 1L)) & 0xffffffffL;
+ work = (v[0] ^ v[1]) & 0xaaaaaaaaL;
+ v[0] ^= work;
+ v[1] ^= work;
+ v[0] = ((v[0] << 1) | ((v[0] >> 31) & 1L)) & 0xffffffffL;
+}
+
+static void
+FP(uint32_t v[2])
+{
+ uint32_t work;
+
+ v[0] = (v[0] << 31) | (v[0] >> 1);
+ work = (v[1] ^ v[0]) & 0xaaaaaaaaL;
+ v[1] ^= work;
+ v[0] ^= work;
+ v[1] = (v[1] << 31) | (v[1] >> 1);
+ work = ((v[1] >> 8) ^ v[0]) & 0x00ff00ffL;
+ v[0] ^= work;
+ v[1] ^= (work << 8);
+ work = ((v[1] >> 2) ^ v[0]) & 0x33333333L;
+ v[0] ^= work;
+ v[1] ^= (work << 2);
+ work = ((v[0] >> 16) ^ v[1]) & 0x0000ffffL;
+ v[1] ^= work;
+ v[0] ^= (work << 16);
+ work = ((v[0] >> 4) ^ v[1]) & 0x0f0f0f0fL;
+ v[1] ^= work;
+ v[0] ^= (work << 4);
+}
+
+static void
+desx(uint32_t block[2], DES_key_schedule *ks, int encp)
+{
+ uint32_t *keys;
+ uint32_t fval, work, right, left;
+ int round;
+
+ left = block[0];
+ right = block[1];
+
+ if (encp) {
+ keys = &ks->ks[0];
+
+ for( round = 0; round < 8; round++ ) {
+ work = (right << 28) | (right >> 4);
+ work ^= *keys++;
+ fval = SP7[ work & 0x3fL];
+ fval |= SP5[(work >> 8) & 0x3fL];
+ fval |= SP3[(work >> 16) & 0x3fL];
+ fval |= SP1[(work >> 24) & 0x3fL];
+ work = right ^ *keys++;
+ fval |= SP8[ work & 0x3fL];
+ fval |= SP6[(work >> 8) & 0x3fL];
+ fval |= SP4[(work >> 16) & 0x3fL];
+ fval |= SP2[(work >> 24) & 0x3fL];
+ left ^= fval;
+ work = (left << 28) | (left >> 4);
+ work ^= *keys++;
+ fval = SP7[ work & 0x3fL];
+ fval |= SP5[(work >> 8) & 0x3fL];
+ fval |= SP3[(work >> 16) & 0x3fL];
+ fval |= SP1[(work >> 24) & 0x3fL];
+ work = left ^ *keys++;
+ fval |= SP8[ work & 0x3fL];
+ fval |= SP6[(work >> 8) & 0x3fL];
+ fval |= SP4[(work >> 16) & 0x3fL];
+ fval |= SP2[(work >> 24) & 0x3fL];
+ right ^= fval;
+ }
+ } else {
+ keys = &ks->ks[30];
+
+ for( round = 0; round < 8; round++ ) {
+ work = (right << 28) | (right >> 4);
+ work ^= *keys++;
+ fval = SP7[ work & 0x3fL];
+ fval |= SP5[(work >> 8) & 0x3fL];
+ fval |= SP3[(work >> 16) & 0x3fL];
+ fval |= SP1[(work >> 24) & 0x3fL];
+ work = right ^ *keys++;
+ fval |= SP8[ work & 0x3fL];
+ fval |= SP6[(work >> 8) & 0x3fL];
+ fval |= SP4[(work >> 16) & 0x3fL];
+ fval |= SP2[(work >> 24) & 0x3fL];
+ left ^= fval;
+ work = (left << 28) | (left >> 4);
+ keys -= 4;
+ work ^= *keys++;
+ fval = SP7[ work & 0x3fL];
+ fval |= SP5[(work >> 8) & 0x3fL];
+ fval |= SP3[(work >> 16) & 0x3fL];
+ fval |= SP1[(work >> 24) & 0x3fL];
+ work = left ^ *keys++;
+ fval |= SP8[ work & 0x3fL];
+ fval |= SP6[(work >> 8) & 0x3fL];
+ fval |= SP4[(work >> 16) & 0x3fL];
+ fval |= SP2[(work >> 24) & 0x3fL];
+ right ^= fval;
+ keys -= 4;
+ }
+ }
+ block[0] = right;
+ block[1] = left;
+}
diff --git a/source4/heimdal/lib/hcrypto/des.h b/source4/heimdal/lib/hcrypto/des.h
new file mode 100644
index 0000000000..14402d4b1c
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/des.h
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef _DESperate_H
+#define _DESperate_H 1
+
+/* symbol renaming */
+#define _DES_ipfp_test _hc_DES_ipfp_test
+#define DES_cbc_cksum hc_DES_cbc_cksum
+#define DES_cbc_encrypt hc_DES_cbc_encrypt
+#define DES_cfb64_encrypt hc_DES_cfb64_encrypt
+#define DES_check_key_parity hc_DES_check_key_parity
+#define DES_ecb3_encrypt hc_DES_ecb3_encrypt
+#define DES_ecb_encrypt hc_DES_ecb_encrypt
+#define DES_ede3_cbc_encrypt hc_DES_ede3_cbc_encrypt
+#define DES_encrypt hc_DES_encrypt
+#define DES_generate_random_block hc_DES_generate_random_block
+#define DES_init_random_number_generator hc_DES_init_random_number_generator
+#define DES_is_weak_key hc_DES_is_weak_key
+#define DES_key_sched hc_DES_key_sched
+#define DES_new_random_key hc_DES_new_random_key
+#define DES_pcbc_encrypt hc_DES_pcbc_encrypt
+#define DES_rand_data hc_DES_rand_data
+#define DES_random_key hc_DES_random_key
+#define DES_read_password hc_DES_read_password
+#define DES_set_key hc_DES_set_key
+#define DES_set_key_checked hc_DES_set_key_checked
+#define DES_set_key_unchecked hc_DES_set_key_unchecked
+#define DES_set_key_sched hc_DES_set_key_sched
+#define DES_set_odd_parity hc_DES_set_odd_parity
+#define DES_set_random_generator_seed hc_DES_set_random_generator_seed
+#define DES_set_sequence_number hc_DES_set_sequence_number
+#define DES_string_to_key hc_DES_string_to_key
+
+/*
+ *
+ */
+
+#define DES_CBLOCK_LEN 8
+#define DES_KEY_SZ 8
+
+#define DES_ENCRYPT 1
+#define DES_DECRYPT 0
+
+typedef unsigned char DES_cblock[DES_CBLOCK_LEN];
+typedef struct DES_key_schedule
+{
+ uint32_t ks[32];
+} DES_key_schedule;
+
+/*
+ *
+ */
+
+#if !defined(__GNUC__) && !defined(__attribute__)
+#define __attribute__(x)
+#endif
+
+#ifndef HC_DEPRECATED
+#define HC_DEPRECATED __attribute__((deprecated))
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void DES_set_odd_parity(DES_cblock *);
+int DES_check_key_parity(DES_cblock *);
+int DES_is_weak_key(DES_cblock *);
+int HC_DEPRECATED DES_set_key(DES_cblock *, DES_key_schedule *);
+int DES_set_key_checked(DES_cblock *, DES_key_schedule *);
+int DES_set_key_unchecked(DES_cblock *, DES_key_schedule *);
+int DES_key_sched(DES_cblock *, DES_key_schedule *);
+void DES_string_to_key(const char *, DES_cblock *);
+int DES_read_password(DES_cblock *, char *, int);
+
+void HC_DEPRECATED DES_rand_data(void *, int);
+void HC_DEPRECATED DES_set_random_generator_seed(DES_cblock *);
+void HC_DEPRECATED DES_generate_random_block(DES_cblock *);
+void HC_DEPRECATED DES_set_sequence_number(void *);
+void HC_DEPRECATED DES_init_random_number_generator(DES_cblock *);
+void HC_DEPRECATED DES_random_key(DES_cblock *);
+int HC_DEPRECATED DES_new_random_key(DES_cblock *);
+
+
+void DES_encrypt(uint32_t [2], DES_key_schedule *, int);
+void DES_ecb_encrypt(DES_cblock *, DES_cblock *, DES_key_schedule *, int);
+void DES_ecb3_encrypt(DES_cblock *,DES_cblock *, DES_key_schedule *,
+ DES_key_schedule *, DES_key_schedule *, int);
+void DES_pcbc_encrypt(const void *, void *, long,
+ DES_key_schedule *, DES_cblock *, int);
+void DES_cbc_encrypt(const void *, void *, long,
+ DES_key_schedule *, DES_cblock *, int);
+void DES_ede3_cbc_encrypt(const void *, void *, long,
+ DES_key_schedule *, DES_key_schedule *,
+ DES_key_schedule *, DES_cblock *, int);
+void DES_cfb64_encrypt(const void *, void *, long,
+ DES_key_schedule *, DES_cblock *, int *, int);
+
+
+uint32_t DES_cbc_cksum(const void *, DES_cblock *,
+ long, DES_key_schedule *, DES_cblock *);
+
+
+void _DES_ipfp_test(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* _DESperate_H */
diff --git a/source4/heimdal/lib/hcrypto/dh-imath.c b/source4/heimdal/lib/hcrypto/dh-imath.c
new file mode 100644
index 0000000000..4725281d19
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/dh-imath.c
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <dh.h>
+
+#include <roken.h>
+
+#include "imath/imath.h"
+
+RCSID("$Id$");
+
+static void
+BN2mpz(mpz_t *s, const BIGNUM *bn)
+{
+ size_t len;
+ void *p;
+
+ len = BN_num_bytes(bn);
+ p = malloc(len);
+ BN_bn2bin(bn, p);
+ mp_int_read_unsigned(s, p, len);
+ free(p);
+}
+
+
+static BIGNUM *
+mpz2BN(mpz_t *s)
+{
+ size_t size;
+ BIGNUM *bn;
+ void *p;
+
+ size = mp_int_unsigned_len(s);
+ p = malloc(size);
+ if (p == NULL && size != 0)
+ return NULL;
+ mp_int_to_unsigned(s, p, size);
+
+ bn = BN_bin2bn(p, size, NULL);
+ free(p);
+ return bn;
+}
+
+/*
+ *
+ */
+
+#define DH_NUM_TRIES 10
+
+static int
+dh_generate_key(DH *dh)
+{
+ mpz_t pub, priv_key, g, p;
+ int have_private_key = (dh->priv_key != NULL);
+ int codes, times = 0;
+ mp_result res;
+
+ if (dh->p == NULL || dh->g == NULL)
+ return 0;
+
+ while (times++ < DH_NUM_TRIES) {
+ if (!have_private_key) {
+ size_t bits = BN_num_bits(dh->p);
+
+ if (dh->priv_key)
+ BN_free(dh->priv_key);
+
+ dh->priv_key = BN_new();
+ if (dh->priv_key == NULL)
+ return 0;
+ if (!BN_rand(dh->priv_key, bits - 1, 0, 0)) {
+ BN_clear_free(dh->priv_key);
+ dh->priv_key = NULL;
+ return 0;
+ }
+ }
+ if (dh->pub_key)
+ BN_free(dh->pub_key);
+
+ mp_int_init(&pub);
+ mp_int_init(&priv_key);
+ mp_int_init(&g);
+ mp_int_init(&p);
+
+ BN2mpz(&priv_key, dh->priv_key);
+ BN2mpz(&g, dh->g);
+ BN2mpz(&p, dh->p);
+
+ res = mp_int_exptmod(&g, &priv_key, &p, &pub);
+
+ mp_int_clear(&priv_key);
+ mp_int_clear(&g);
+ mp_int_clear(&p);
+ if (res != MP_OK)
+ continue;
+
+ dh->pub_key = mpz2BN(&pub);
+ mp_int_clear(&pub);
+ if (dh->pub_key == NULL)
+ return 0;
+
+ if (DH_check_pubkey(dh, dh->pub_key, &codes) && codes == 0)
+ break;
+ if (have_private_key)
+ return 0;
+ }
+
+ if (times >= DH_NUM_TRIES) {
+ if (!have_private_key && dh->priv_key) {
+ BN_free(dh->priv_key);
+ dh->priv_key = NULL;
+ }
+ if (dh->pub_key) {
+ BN_free(dh->pub_key);
+ dh->pub_key = NULL;
+ }
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+dh_compute_key(unsigned char *shared, const BIGNUM * pub, DH *dh)
+{
+ mpz_t s, priv_key, p, peer_pub;
+ size_t size = 0;
+ mp_result res;
+
+ if (dh->pub_key == NULL || dh->g == NULL || dh->priv_key == NULL)
+ return -1;
+
+ mp_int_init(&p);
+ BN2mpz(&p, dh->p);
+
+ mp_int_init(&peer_pub);
+ BN2mpz(&peer_pub, pub);
+
+ /* check if peers pubkey is reasonable */
+ if (MP_SIGN(&peer_pub) == MP_NEG
+ || mp_int_compare(&peer_pub, &p) >= 0
+ || mp_int_compare_value(&peer_pub, 1) <= 0)
+ {
+ mp_int_clear(&p);
+ mp_int_clear(&peer_pub);
+ return -1;
+ }
+
+ mp_int_init(&priv_key);
+ BN2mpz(&priv_key, dh->priv_key);
+
+ mp_int_init(&s);
+
+ mp_int_exptmod(&peer_pub, &priv_key, &p, &s);
+
+ mp_int_clear(&p);
+ mp_int_clear(&peer_pub);
+ mp_int_clear(&priv_key);
+
+ size = mp_int_unsigned_len(&s);
+ res = mp_int_to_unsigned(&s, shared, size);
+ mp_int_clear(&s);
+
+ return (res == MP_OK) ? size : -1;
+}
+
+static int
+dh_generate_params(DH *dh, int a, int b, BN_GENCB *callback)
+{
+ /* groups should already be known, we don't care about this */
+ return 0;
+}
+
+static int
+dh_init(DH *dh)
+{
+ return 1;
+}
+
+static int
+dh_finish(DH *dh)
+{
+ return 1;
+}
+
+
+/*
+ *
+ */
+
+const DH_METHOD _hc_dh_imath_method = {
+ "hcrypto imath DH",
+ dh_generate_key,
+ dh_compute_key,
+ NULL,
+ dh_init,
+ dh_finish,
+ 0,
+ NULL,
+ dh_generate_params
+};
+
+/**
+ * DH implementation using libimath.
+ *
+ * @return the DH_METHOD for the DH implementation using libimath.
+ *
+ * @ingroup hcrypto_dh
+ */
+
+const DH_METHOD *
+DH_imath_method(void)
+{
+ return &_hc_dh_imath_method;
+}
diff --git a/source4/heimdal/lib/hcrypto/dh.c b/source4/heimdal/lib/hcrypto/dh.c
new file mode 100644
index 0000000000..b0299395a2
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/dh.c
@@ -0,0 +1,491 @@
+/*
+ * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+RCSID("$Id$");
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <dh.h>
+
+#include <roken.h>
+
+/**
+ * @page page_dh DH - Diffie-Hellman key exchange
+ *
+ * Diffie-Hellman key exchange is a protocol that allows two parties
+ * to establish a shared secret key.
+ *
+ * Include and example how to use DH_new() and friends here.
+ *
+ * See the library functions here: @ref hcrypto_dh
+ */
+
+/**
+ * Create a new DH object using DH_new_method(NULL), see DH_new_method().
+ *
+ * @return a newly allocated DH object.
+ *
+ * @ingroup hcrypto_dh
+ */
+
+DH *
+DH_new(void)
+{
+ return DH_new_method(NULL);
+}
+
+/**
+ * Create a new DH object from the given engine, if the NULL is used,
+ * the default engine is used. Free the DH object with DH_free().
+ *
+ * @param engine The engine to use to allocate the DH object.
+ *
+ * @return a newly allocated DH object.
+ *
+ * @ingroup hcrypto_dh
+ */
+
+DH *
+DH_new_method(ENGINE *engine)
+{
+ DH *dh;
+
+ dh = calloc(1, sizeof(*dh));
+ if (dh == NULL)
+ return NULL;
+
+ dh->references = 1;
+
+ if (engine) {
+ ENGINE_up_ref(engine);
+ dh->engine = engine;
+ } else {
+ dh->engine = ENGINE_get_default_DH();
+ }
+
+ if (dh->engine) {
+ dh->meth = ENGINE_get_DH(dh->engine);
+ if (dh->meth == NULL) {
+ ENGINE_finish(engine);
+ free(dh);
+ return 0;
+ }
+ }
+
+ if (dh->meth == NULL)
+ dh->meth = DH_get_default_method();
+
+ (*dh->meth->init)(dh);
+
+ return dh;
+}
+
+/**
+ * Free a DH object and release related resources, like ENGINE, that
+ * the object was using.
+ *
+ * @param dh object to be freed.
+ *
+ * @ingroup hcrypto_dh
+ */
+
+void
+DH_free(DH *dh)
+{
+ if (dh->references <= 0)
+ abort();
+
+ if (--dh->references > 0)
+ return;
+
+ (*dh->meth->finish)(dh);
+
+ if (dh->engine)
+ ENGINE_finish(dh->engine);
+
+#define free_if(f) if (f) { BN_free(f); }
+ free_if(dh->p);
+ free_if(dh->g);
+ free_if(dh->pub_key);
+ free_if(dh->priv_key);
+ free_if(dh->q);
+ free_if(dh->j);
+ free_if(dh->counter);
+#undef free_if
+
+ memset(dh, 0, sizeof(*dh));
+ free(dh);
+}
+
+/**
+ * Add a reference to the DH object. The object should be free with
+ * DH_free() to drop the reference.
+ *
+ * @param dh the object to increase the reference count too.
+ *
+ * @return the updated reference count, can't safely be used except
+ * for debug printing.
+ *
+ * @ingroup hcrypto_dh
+ */
+
+int
+DH_up_ref(DH *dh)
+{
+ return ++dh->references;
+}
+
+/**
+ * The maximum output size of the DH_compute_key() function.
+ *
+ * @param dh The DH object to get the size from.
+ *
+ * @return the maximum size in bytes of the out data.
+ *
+ * @ingroup hcrypto_dh
+ */
+
+int
+DH_size(const DH *dh)
+{
+ return BN_num_bytes(dh->p);
+}
+
+/**
+ * Set the data index idx in the DH object to data.
+ *
+ * @param dh DH object.
+ * @param idx index to set the data for.
+ * @param data data to store for the index idx.
+ *
+ * @return 1 on success.
+ *
+ * @ingroup hcrypto_dh
+ */
+
+int
+DH_set_ex_data(DH *dh, int idx, void *data)
+{
+ dh->ex_data.sk = data;
+ return 1;
+}
+
+/**
+ * Get the data for index idx in the DH object.
+ *
+ * @param dh DH object.
+ * @param idx index to get the data for.
+ *
+ * @return the object store in index idx
+ *
+ * @ingroup hcrypto_dh
+ */
+
+void *
+DH_get_ex_data(DH *dh, int idx)
+{
+ return dh->ex_data.sk;
+}
+
+/**
+ * Generate DH parameters for the DH object give parameters.
+ *
+ * @param dh The DH object to generate parameters for.
+ * @param prime_len length of the prime
+ * @param generator generator, g
+ * @param cb Callback parameters to show progress, can be NULL.
+ *
+ * @return the maximum size in bytes of the out data.
+ *
+ * @ingroup hcrypto_dh
+ */
+
+int
+DH_generate_parameters_ex(DH *dh, int prime_len, int generator, BN_GENCB *cb)
+{
+ if (dh->meth->generate_params)
+ return dh->meth->generate_params(dh, prime_len, generator, cb);
+ return 0;
+}
+
+/**
+ * Check that the public key is sane.
+ *
+ * @param dh the local peer DH parameters.
+ * @param pub_key the remote peer public key parameters.
+ * @param codes return that the failures of the pub_key are.
+ *
+ * @return 1 on success, 0 on failure and *codes is set the the
+ * combined fail check for the public key
+ *
+ * @ingroup hcrypto_dh
+ */
+
+int
+DH_check_pubkey(const DH *dh, const BIGNUM *pub_key, int *codes)
+{
+ BIGNUM *bn = NULL, *sum = NULL;
+ int ret = 0;
+
+ *codes = 0;
+
+ /**
+ * Checks that the function performs are:
+ * - pub_key is not negative
+ */
+
+ if (BN_is_negative(pub_key))
+ goto out;
+
+ /**
+ * - pub_key > 1 and pub_key < p - 1,
+ * to avoid small subgroups attack.
+ */
+
+ bn = BN_new();
+ if (bn == NULL)
+ goto out;
+
+ if (!BN_set_word(bn, 1))
+ goto out;
+
+ if (BN_cmp(bn, pub_key) >= 0)
+ *codes |= DH_CHECK_PUBKEY_TOO_SMALL;
+
+ sum = BN_new();
+ if (sum == NULL)
+ goto out;
+
+ BN_uadd(sum, pub_key, bn);
+
+ if (BN_cmp(sum, dh->p) >= 0)
+ *codes |= DH_CHECK_PUBKEY_TOO_LARGE;
+
+ /**
+ * - if g == 2, pub_key have more then one bit set,
+ * if bits set is 1, log_2(pub_key) is trival
+ */
+
+ if (!BN_set_word(bn, 2))
+ goto out;
+
+ if (BN_cmp(bn, pub_key) == 0) {
+ unsigned i, n = BN_num_bits(pub_key);
+ unsigned bits = 0;
+
+ for (i = 0; i <= n; i++)
+ if (BN_is_bit_set(pub_key, i))
+ bits++;
+
+ if (bits > 1) {
+ *codes |= DH_CHECK_PUBKEY_TOO_SMALL;
+ goto out;
+ }
+ }
+
+ ret = 1;
+out:
+ if (bn)
+ BN_free(bn);
+ if (sum)
+ BN_free(sum);
+
+ return ret;
+}
+
+/**
+ * Generate a new DH private-public key pair. The dh parameter must be
+ * allocted first with DH_new(). dh->p and dp->g must be set.
+ *
+ * @param dh dh parameter.
+ *
+ * @return 1 on success.
+ *
+ * @ingroup hcrypto_dh
+ */
+
+int
+DH_generate_key(DH *dh)
+{
+ return dh->meth->generate_key(dh);
+}
+
+/**
+ * Complute the shared secret key.
+ *
+ * @param shared_key the resulting shared key, need to be at least
+ * DH_size() large.
+ * @param peer_pub_key the peer's public key.
+ * @param dh the dh key pair.
+ *
+ * @return 1 on success.
+ *
+ * @ingroup hcrypto_dh
+ */
+
+int
+DH_compute_key(unsigned char *shared_key,
+ const BIGNUM *peer_pub_key, DH *dh)
+{
+ int codes;
+
+ /**
+ * Checks that the pubkey passed in is valid using
+ * DH_check_pubkey().
+ */
+
+ if (!DH_check_pubkey(dh, peer_pub_key, &codes) || codes != 0)
+ return -1;
+
+ return dh->meth->compute_key(shared_key, peer_pub_key, dh);
+}
+
+/**
+ * Set a new method for the DH keypair.
+ *
+ * @param dh dh parameter.
+ * @param method the new method for the DH parameter.
+ *
+ * @return 1 on success.
+ *
+ * @ingroup hcrypto_dh
+ */
+
+int
+DH_set_method(DH *dh, const DH_METHOD *method)
+{
+ (*dh->meth->finish)(dh);
+ if (dh->engine) {
+ ENGINE_finish(dh->engine);
+ dh->engine = NULL;
+ }
+ dh->meth = method;
+ (*dh->meth->init)(dh);
+ return 1;
+}
+
+/*
+ *
+ */
+
+static int
+dh_null_generate_key(DH *dh)
+{
+ return 0;
+}
+
+static int
+dh_null_compute_key(unsigned char *shared,const BIGNUM *pub, DH *dh)
+{
+ return 0;
+}
+
+static int
+dh_null_init(DH *dh)
+{
+ return 1;
+}
+
+static int
+dh_null_finish(DH *dh)
+{
+ return 1;
+}
+
+static int
+dh_null_generate_params(DH *dh, int prime_num, int len, BN_GENCB *cb)
+{
+ return 0;
+}
+
+static const DH_METHOD dh_null_method = {
+ "hcrypto null DH",
+ dh_null_generate_key,
+ dh_null_compute_key,
+ NULL,
+ dh_null_init,
+ dh_null_finish,
+ 0,
+ NULL,
+ dh_null_generate_params
+};
+
+extern const DH_METHOD _hc_dh_imath_method;
+static const DH_METHOD *dh_default_method = &_hc_dh_imath_method;
+
+/**
+ * Return the dummy DH implementation.
+ *
+ * @return pointer to a DH_METHOD.
+ *
+ * @ingroup hcrypto_dh
+ */
+
+const DH_METHOD *
+DH_null_method(void)
+{
+ return &dh_null_method;
+}
+
+/**
+ * Set the default DH implementation.
+ *
+ * @param meth pointer to a DH_METHOD.
+ *
+ * @ingroup hcrypto_dh
+ */
+
+void
+DH_set_default_method(const DH_METHOD *meth)
+{
+ dh_default_method = meth;
+}
+
+/**
+ * Return the default DH implementation.
+ *
+ * @return pointer to a DH_METHOD.
+ *
+ * @ingroup hcrypto_dh
+ */
+
+const DH_METHOD *
+DH_get_default_method(void)
+{
+ return dh_default_method;
+}
+
diff --git a/source4/heimdal/lib/hcrypto/dh.h b/source4/heimdal/lib/hcrypto/dh.h
new file mode 100644
index 0000000000..2522bfe39f
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/dh.h
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id$
+ */
+
+#ifndef _HEIM_DH_H
+#define _HEIM_DH_H 1
+
+/* symbol renaming */
+#define DH_null_method hc_DH_null_method
+#define DH_imath_method hc_DH_imath_method
+#define DH_new hc_DH_new
+#define DH_new_method hc_DH_new_method
+#define DH_free hc_DH_free
+#define DH_up_ref hc_DH_up_ref
+#define DH_size hc_DH_size
+#define DH_set_default_method hc_DH_set_default_method
+#define DH_get_default_method hc_DH_get_default_method
+#define DH_set_method hc_DH_set_method
+#define DH_get_method hc_DH_get_method
+#define DH_set_ex_data hc_DH_set_ex_data
+#define DH_get_ex_data hc_DH_get_ex_data
+#define DH_generate_parameters_ex hc_DH_generate_parameters_ex
+#define DH_check_pubkey hc_DH_check_pubkey
+#define DH_generate_key hc_DH_generate_key
+#define DH_compute_key hc_DH_compute_key
+
+/*
+ *
+ */
+
+typedef struct DH DH;
+typedef struct DH_METHOD DH_METHOD;
+
+#include <hcrypto/bn.h>
+#include <hcrypto/engine.h>
+
+struct DH_METHOD {
+ const char *name;
+ int (*generate_key)(DH *);
+ int (*compute_key)(unsigned char *,const BIGNUM *,DH *);
+ int (*bn_mod_exp)(const DH *, BIGNUM *, const BIGNUM *,
+ const BIGNUM *, const BIGNUM *, BN_CTX *,
+ BN_MONT_CTX *);
+ int (*init)(DH *);
+ int (*finish)(DH *);
+ int flags;
+ void *app_data;
+ int (*generate_params)(DH *, int, int, BN_GENCB *);
+};
+
+struct DH {
+ int pad;
+ int version;
+ BIGNUM *p;
+ BIGNUM *g;
+ long length;
+ BIGNUM *pub_key;
+ BIGNUM *priv_key;
+ int flags;
+ void *method_mont_p;
+ BIGNUM *q;
+ BIGNUM *j;
+ void *seed;
+ int seedlen;
+ BIGNUM *counter;
+ int references;
+ struct CRYPTO_EX_DATA {
+ void *sk;
+ int dummy;
+ } ex_data;
+ const DH_METHOD *meth;
+ ENGINE *engine;
+};
+
+/* DH_check_pubkey return codes in `codes' argument. */
+#define DH_CHECK_PUBKEY_TOO_SMALL 1
+#define DH_CHECK_PUBKEY_TOO_LARGE 2
+
+/*
+ *
+ */
+
+const DH_METHOD *DH_null_method(void);
+const DH_METHOD *DH_imath_method(void);
+
+DH * DH_new(void);
+DH * DH_new_method(ENGINE *);
+void DH_free(DH *);
+int DH_up_ref(DH *);
+
+int DH_size(const DH *);
+
+
+void DH_set_default_method(const DH_METHOD *);
+const DH_METHOD *
+ DH_get_default_method(void);
+int DH_set_method(DH *, const DH_METHOD *);
+
+int DH_set_ex_data(DH *, int, void *);
+void * DH_get_ex_data(DH *, int);
+
+int DH_generate_parameters_ex(DH *, int, int, BN_GENCB *);
+int DH_check_pubkey(const DH *, const BIGNUM *, int *);
+int DH_generate_key(DH *);
+int DH_compute_key(unsigned char *,const BIGNUM *,DH *);
+
+#endif /* _HEIM_DH_H */
+
diff --git a/source4/heimdal/lib/hcrypto/dsa.c b/source4/heimdal/lib/hcrypto/dsa.c
new file mode 100644
index 0000000000..6606a5e7c1
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/dsa.c
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+RCSID("$Id$");
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <dsa.h>
+
+#include <roken.h>
+
+/*
+ *
+ */
+
+DSA *
+DSA_new(void)
+{
+ DSA *dsa = calloc(1, sizeof(*dsa));
+ dsa->meth = rk_UNCONST(DSA_get_default_method());
+ dsa->references = 1;
+ return dsa;
+}
+
+void
+DSA_free(DSA *dsa)
+{
+ if (dsa->references <= 0)
+ abort();
+
+ if (--dsa->references > 0)
+ return;
+
+ (*dsa->meth->finish)(dsa);
+
+#define free_if(f) if (f) { BN_free(f); }
+ free_if(dsa->p);
+ free_if(dsa->q);
+ free_if(dsa->g);
+ free_if(dsa->pub_key);
+ free_if(dsa->priv_key);
+ free_if(dsa->kinv);
+ free_if(dsa->r);
+#undef free_if
+
+ memset(dsa, 0, sizeof(*dsa));
+ free(dsa);
+
+}
+
+int
+DSA_up_ref(DSA *dsa)
+{
+ return ++dsa->references;
+}
+
+/*
+ *
+ */
+
+static const DSA_METHOD dsa_null_method = {
+ "hcrypto null DSA"
+};
+
+const DSA_METHOD *
+DSA_null_method(void)
+{
+ return &dsa_null_method;
+}
+
+
+const DSA_METHOD *dsa_default_mech = &dsa_null_method;
+
+void
+DSA_set_default_method(const DSA_METHOD *mech)
+{
+ dsa_default_mech = mech;
+}
+
+const DSA_METHOD *
+DSA_get_default_method(void)
+{
+ return dsa_default_mech;
+}
+
+int
+DSA_verify(int type, const unsigned char * digest, int digest_len,
+ const unsigned char *sig, int sig_len, DSA *dsa)
+{
+ return -1;
+}
diff --git a/source4/heimdal/lib/hcrypto/dsa.h b/source4/heimdal/lib/hcrypto/dsa.h
new file mode 100644
index 0000000000..686d050874
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/dsa.h
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id$
+ */
+
+#ifndef _HEIM_DSA_H
+#define _HEIM_DSA_H 1
+
+#include <hcrypto/bn.h>
+
+/* symbol renaming */
+#define DSA_null_method hc_DSA_null_method
+#define DSA_new hc_DSA_new
+#define DSA_free hc_DSA_free
+#define DSA_up_ref hc_DSA_up_ref
+#define DSA_set_default_method hc_DSA_set_default_method
+#define DSA_get_default_method hc_DSA_get_default_method
+#define DSA_set_method hc_DSA_set_method
+#define DSA_get_method hc_DSA_get_method
+#define DSA_set_app_data hc_DSA_set_app_data
+#define DSA_get_app_data hc_DSA_get_app_data
+#define DSA_size hc_DSA_size
+#define DSA_verify hc_DSA_verify
+
+/*
+ *
+ */
+
+
+typedef struct DSA DSA;
+typedef struct DSA_METHOD DSA_METHOD;
+typedef struct DSA_SIG DSA_SIG;
+
+struct DSA_SIG {
+ BIGNUM *r;
+ BIGNUM *s;
+};
+
+struct DSA_METHOD {
+ const char *name;
+ DSA_SIG * (*dsa_do_sign)(const unsigned char *, int, DSA *);
+ int (*dsa_sign_setup)(DSA *, BN_CTX *, BIGNUM **, BIGNUM **);
+ int (*dsa_do_verify)(const unsigned char *, int, DSA_SIG *, DSA *);
+ int (*dsa_mod_exp)(DSA *, BIGNUM *, BIGNUM *, BIGNUM *,
+ BIGNUM *, BIGNUM *, BIGNUM *, BN_CTX *,
+ BN_MONT_CTX *);
+ int (*bn_mod_exp)(DSA *, BIGNUM *, BIGNUM *, const BIGNUM *,
+ const BIGNUM *, BN_CTX *,
+ BN_MONT_CTX *);
+ int (*init)(DSA *);
+ int (*finish)(DSA *);
+ int flags;
+ void *app_data;
+};
+
+struct DSA {
+ int pad;
+ long version;
+ int write_params;
+ BIGNUM *p;
+ BIGNUM *q;
+ BIGNUM *g;
+
+ BIGNUM *pub_key;
+ BIGNUM *priv_key;
+
+ BIGNUM *kinv;
+ BIGNUM *r;
+ int flags;
+ void *method_mont_p;
+ int references;
+ struct dsa_CRYPTO_EX_DATA {
+ void *sk;
+ int dummy;
+ } ex_data;
+ const DSA_METHOD *meth;
+ void *engine;
+};
+
+/*
+ *
+ */
+
+const DSA_METHOD *DSA_null_method(void);
+
+/*
+ *
+ */
+
+DSA * DSA_new(void);
+void DSA_free(DSA *);
+int DSA_up_ref(DSA *);
+
+void DSA_set_default_method(const DSA_METHOD *);
+const DSA_METHOD * DSA_get_default_method(void);
+
+const DSA_METHOD * DSA_get_method(const DSA *);
+int DSA_set_method(DSA *, const DSA_METHOD *);
+
+void DSA_set_app_data(DSA *, void *arg);
+void * DSA_get_app_data(DSA *);
+
+int DSA_size(const DSA *);
+
+int DSA_verify(int, const unsigned char *, int,
+ const unsigned char *, int, DSA *);
+
+#endif /* _HEIM_DSA_H */
diff --git a/source4/heimdal/lib/hcrypto/engine.c b/source4/heimdal/lib/hcrypto/engine.c
new file mode 100644
index 0000000000..61d5f93825
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/engine.c
@@ -0,0 +1,325 @@
+/*
+ * Copyright (c) 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+RCSID("$Id$");
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <engine.h>
+
+#ifdef HAVE_DLFCN_H
+#include <dlfcn.h>
+#ifndef RTLD_NOW
+#define RTLD_NOW 0
+#endif
+#endif
+
+struct hc_engine {
+ int references;
+ char *name;
+ char *id;
+ void (*destroy)(ENGINE *);
+ const RSA_METHOD *rsa;
+ const DH_METHOD *dh;
+ const RAND_METHOD *rand;
+};
+
+int
+ENGINE_finish(ENGINE *engine)
+{
+ if (engine->references-- <= 0)
+ abort();
+ if (engine->references > 0)
+ return 1;
+
+ if (engine->name)
+ free(engine->name);
+ if (engine->id)
+ free(engine->id);
+ if(engine->destroy)
+ (*engine->destroy)(engine);
+
+ memset(engine, 0, sizeof(engine));
+ engine->references = -1;
+
+
+ free(engine);
+ return 1;
+}
+
+int
+ENGINE_up_ref(ENGINE *engine)
+{
+ if (engine->references < 0)
+ abort();
+ engine->references++;
+ return 1;
+}
+
+int
+ENGINE_set_id(ENGINE *engine, const char *id)
+{
+ engine->id = strdup(id);
+ return (engine->id == NULL) ? 0 : 1;
+}
+
+int
+ENGINE_set_name(ENGINE *engine, const char *name)
+{
+ engine->name = strdup(name);
+ return (engine->name == NULL) ? 0 : 1;
+}
+
+int
+ENGINE_set_RSA(ENGINE *engine, const RSA_METHOD *method)
+{
+ engine->rsa = method;
+ return 1;
+}
+
+int
+ENGINE_set_DH(ENGINE *engine, const DH_METHOD *method)
+{
+ engine->dh = method;
+ return 1;
+}
+
+int
+ENGINE_set_destroy_function(ENGINE *e, void (*destroy)(ENGINE *))
+{
+ e->destroy = destroy;
+ return 1;
+}
+
+const char *
+ENGINE_get_id(const ENGINE *engine)
+{
+ return engine->id;
+}
+
+const char *
+ENGINE_get_name(const ENGINE *engine)
+{
+ return engine->name;
+}
+
+const RSA_METHOD *
+ENGINE_get_RSA(const ENGINE *engine)
+{
+ return engine->rsa;
+}
+
+const DH_METHOD *
+ENGINE_get_DH(const ENGINE *engine)
+{
+ return engine->dh;
+}
+
+const RAND_METHOD *
+ENGINE_get_RAND(const ENGINE *engine)
+{
+ return engine->rand;
+}
+
+/*
+ *
+ */
+
+#define SG_default_engine(type) \
+static ENGINE *type##_engine; \
+int \
+ENGINE_set_default_##type(ENGINE *engine) \
+{ \
+ if (type##_engine) \
+ ENGINE_finish(type##_engine); \
+ type##_engine = engine; \
+ if (type##_engine) \
+ ENGINE_up_ref(type##_engine); \
+ return 1; \
+} \
+ENGINE * \
+ENGINE_get_default_##type(void) \
+{ \
+ if (type##_engine) \
+ ENGINE_up_ref(type##_engine); \
+ return type##_engine; \
+}
+
+SG_default_engine(RSA)
+SG_default_engine(DH)
+
+#undef SG_default_engine
+
+/*
+ *
+ */
+
+static ENGINE **engines;
+static unsigned int num_engines;
+
+static int
+add_engine(ENGINE *engine)
+{
+ ENGINE **d, *dup;
+
+ dup = ENGINE_by_id(engine->id);
+ if (dup) {
+ ENGINE_finish(dup);
+ return 0;
+ }
+
+ d = realloc(engines, (num_engines + 1) * sizeof(*engines));
+ if (d == NULL)
+ return 1;
+ engines = d;
+ engines[num_engines++] = engine;
+
+ return 1;
+}
+
+void
+ENGINE_load_builtin_engines(void)
+{
+ ENGINE *engine;
+ int ret;
+
+ engine = calloc(1, sizeof(*engine));
+ if (engine == NULL)
+ return;
+
+ ENGINE_set_id(engine, "builtin");
+ ENGINE_set_name(engine,
+ "Heimdal crypto builtin engine version " PACKAGE_VERSION);
+ ENGINE_set_RSA(engine, RSA_imath_method());
+ ENGINE_set_DH(engine, DH_imath_method());
+
+ ret = add_engine(engine);
+ if (ret != 1)
+ ENGINE_finish(engine);
+}
+
+ENGINE *
+ENGINE_by_dso(const char *path, const char *id)
+{
+#ifdef HAVE_DLOPEN
+ ENGINE *engine;
+ void *handle;
+ int ret;
+
+ engine = calloc(1, sizeof(*engine));
+ if (engine == NULL)
+ return NULL;
+
+ handle = dlopen(path, RTLD_NOW);
+ if (handle == NULL) {
+ /* printf("error: %s\n", dlerror()); */
+ free(engine);
+ return NULL;
+ }
+
+ {
+ unsigned long version;
+ openssl_v_check v_check;
+
+ v_check = (openssl_v_check)dlsym(handle, "v_check");
+ if (v_check == NULL) {
+ dlclose(handle);
+ free(engine);
+ return NULL;
+ }
+
+ version = (*v_check)(OPENSSL_DYNAMIC_VERSION);
+ if (version == 0) {
+ dlclose(handle);
+ free(engine);
+ return NULL;
+ }
+ }
+
+ {
+ openssl_bind_engine bind_engine;
+
+ bind_engine = (openssl_bind_engine)dlsym(handle, "bind_engine");
+ if (bind_engine == NULL) {
+ dlclose(handle);
+ free(engine);
+ return NULL;
+ }
+
+ ret = (*bind_engine)(engine, id, NULL); /* XXX fix third arg */
+ if (ret != 1) {
+ dlclose(handle);
+ free(engine);
+ return NULL;
+ }
+ }
+
+ ENGINE_up_ref(engine);
+
+ ret = add_engine(engine);
+ if (ret != 1) {
+ dlclose(handle);
+ ENGINE_finish(engine);
+ return NULL;
+ }
+
+ return engine;
+#else
+ return NULL;
+#endif
+}
+
+ENGINE *
+ENGINE_by_id(const char *id)
+{
+ int i;
+
+ for (i = 0; i < num_engines; i++) {
+ if (strcmp(id, engines[i]->id) == 0) {
+ ENGINE_up_ref(engines[i]);
+ return engines[i];
+ }
+ }
+ return NULL;
+}
+
+void
+ENGINE_add_conf_module(void)
+{
+}
diff --git a/source4/heimdal/lib/hcrypto/engine.h b/source4/heimdal/lib/hcrypto/engine.h
new file mode 100644
index 0000000000..d3a1479ce2
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/engine.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id$
+ */
+
+#ifndef _HEIM_ENGINE_H
+#define _HEIM_ENGINE_H 1
+
+/* symbol renaming */
+#define ENGINE_add_conf_module hc_ENGINE_add_conf_module
+#define ENGINE_by_dso hc_ENGINE_by_dso
+#define ENGINE_by_id hc_ENGINE_by_id
+#define ENGINE_finish hc_ENGINE_finish
+#define ENGINE_get_DH hc_ENGINE_get_DH
+#define ENGINE_get_RSA hc_ENGINE_get_RSA
+#define ENGINE_get_RAND hc_ENGINE_get_RAND
+#define ENGINE_get_id hc_ENGINE_get_id
+#define ENGINE_get_name hc_ENGINE_get_name
+#define ENGINE_load_builtin_engines hc_ENGINE_load_builtin_engines
+#define ENGINE_set_DH hc_ENGINE_set_DH
+#define ENGINE_set_RSA hc_ENGINE_set_RSA
+#define ENGINE_set_id hc_ENGINE_set_id
+#define ENGINE_set_name hc_ENGINE_set_name
+#define ENGINE_set_destroy_function hc_ENGINE_set_destroy_function
+#define ENGINE_up_ref hc_ENGINE_up_ref
+#define ENGINE_get_default_DH hc_ENGINE_get_default_DH
+#define ENGINE_get_default_RSA hc_ENGINE_get_default_RSA
+#define ENGINE_set_default_DH hc_ENGINE_set_default_DH
+#define ENGINE_set_default_RSA hc_ENGINE_set_default_RSA
+
+/*
+ *
+ */
+
+typedef struct hc_engine ENGINE;
+
+#include <hcrypto/rsa.h>
+#include <hcrypto/dsa.h>
+#include <hcrypto/dh.h>
+#include <hcrypto/rand.h>
+
+#define OPENSSL_DYNAMIC_VERSION (unsigned long)0x00020000
+
+typedef int (*openssl_bind_engine)(ENGINE *, const char *, const void *);
+typedef unsigned long (*openssl_v_check)(unsigned long);
+
+void ENGINE_add_conf_module(void);
+void ENGINE_load_builtin_engines(void);
+ENGINE *ENGINE_by_id(const char *);
+ENGINE *ENGINE_by_dso(const char *, const char *);
+int ENGINE_finish(ENGINE *);
+int ENGINE_up_ref(ENGINE *);
+int ENGINE_set_id(ENGINE *, const char *);
+int ENGINE_set_name(ENGINE *, const char *);
+int ENGINE_set_RSA(ENGINE *, const RSA_METHOD *);
+int ENGINE_set_DH(ENGINE *, const DH_METHOD *);
+int ENGINE_set_destroy_function(ENGINE *, void (*)(ENGINE *));
+
+const char * ENGINE_get_id(const ENGINE *);
+const char * ENGINE_get_name(const ENGINE *);
+const RSA_METHOD * ENGINE_get_RSA(const ENGINE *);
+const DH_METHOD * ENGINE_get_DH(const ENGINE *);
+const RAND_METHOD * ENGINE_get_RAND(const ENGINE *);
+
+int ENGINE_set_default_RSA(ENGINE *);
+ENGINE * ENGINE_get_default_RSA(void);
+int ENGINE_set_default_DH(ENGINE *);
+ENGINE * ENGINE_get_default_DH(void);
+
+
+#endif /* _HEIM_ENGINE_H */
diff --git a/source4/heimdal/lib/hcrypto/evp-aes-cts.c b/source4/heimdal/lib/hcrypto/evp-aes-cts.c
new file mode 100644
index 0000000000..685dcac18b
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/evp-aes-cts.c
@@ -0,0 +1,277 @@
+/*
+ * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+RCSID("$Id$");
+
+#define HC_DEPRECATED
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+
+#include <krb5-types.h>
+
+#if defined(BUILD_KRB5_LIB) && defined(HAVE_OPENSSL)
+#include <openssl/evp.h>
+#include <openssl/aes.h>
+
+#define _hc_EVP_hcrypto_aes_128_cts _krb5_EVP_hcrypto_aes_128_cts
+#define _hc_EVP_hcrypto_aes_192_cts _krb5_EVP_hcrypto_aes_192_cts
+#define _hc_EVP_hcrypto_aes_256_cts _krb5_EVP_hcrypto_aes_256_cts
+
+const EVP_CIPHER * _krb5_EVP_hcrypto_aes_128_cts(void);
+const EVP_CIPHER * _krb5_EVP_hcrypto_aes_192_cts(void);
+const EVP_CIPHER * _krb5_EVP_hcrypto_aes_256_cts(void);
+
+#else
+#include <evp.h>
+#include <aes.h>
+
+#define _hc_EVP_hcrypto_aes_128_cts hc_EVP_hcrypto_aes_128_cts
+#define _hc_EVP_hcrypto_aes_192_cts hc_EVP_hcrypto_aes_192_cts
+#define _hc_EVP_hcrypto_aes_256_cts hc_EVP_hcrypto_aes_256_cts
+
+#endif
+
+/*
+ *
+ */
+
+static int
+aes_cts_init(EVP_CIPHER_CTX *ctx,
+ const unsigned char * key,
+ const unsigned char * iv,
+ int encp)
+{
+ AES_KEY *k = ctx->cipher_data;
+ if (ctx->encrypt)
+ AES_set_encrypt_key(key, ctx->cipher->key_len * 8, k);
+ else
+ AES_set_decrypt_key(key, ctx->cipher->key_len * 8, k);
+ return 1;
+}
+
+static void
+_krb5_aes_cts_encrypt(const unsigned char *in, unsigned char *out,
+ size_t len, const AES_KEY *key,
+ unsigned char *ivec, const int encryptp)
+{
+ unsigned char tmp[AES_BLOCK_SIZE];
+ int i;
+
+ /*
+ * In the framework of kerberos, the length can never be shorter
+ * then at least one blocksize.
+ */
+
+ if (encryptp) {
+
+ while(len > AES_BLOCK_SIZE) {
+ for (i = 0; i < AES_BLOCK_SIZE; i++)
+ tmp[i] = in[i] ^ ivec[i];
+ AES_encrypt(tmp, out, key);
+ memcpy(ivec, out, AES_BLOCK_SIZE);
+ len -= AES_BLOCK_SIZE;
+ in += AES_BLOCK_SIZE;
+ out += AES_BLOCK_SIZE;
+ }
+
+ for (i = 0; i < len; i++)
+ tmp[i] = in[i] ^ ivec[i];
+ for (; i < AES_BLOCK_SIZE; i++)
+ tmp[i] = 0 ^ ivec[i];
+
+ AES_encrypt(tmp, out - AES_BLOCK_SIZE, key);
+
+ memcpy(out, ivec, len);
+ memcpy(ivec, out - AES_BLOCK_SIZE, AES_BLOCK_SIZE);
+
+ } else {
+ unsigned char tmp2[AES_BLOCK_SIZE];
+ unsigned char tmp3[AES_BLOCK_SIZE];
+
+ while(len > AES_BLOCK_SIZE * 2) {
+ memcpy(tmp, in, AES_BLOCK_SIZE);
+ AES_decrypt(in, out, key);
+ for (i = 0; i < AES_BLOCK_SIZE; i++)
+ out[i] ^= ivec[i];
+ memcpy(ivec, tmp, AES_BLOCK_SIZE);
+ len -= AES_BLOCK_SIZE;
+ in += AES_BLOCK_SIZE;
+ out += AES_BLOCK_SIZE;
+ }
+
+ len -= AES_BLOCK_SIZE;
+
+ memcpy(tmp, in, AES_BLOCK_SIZE); /* save last iv */
+ AES_decrypt(in, tmp2, key);
+
+ memcpy(tmp3, in + AES_BLOCK_SIZE, len);
+ memcpy(tmp3 + len, tmp2 + len, AES_BLOCK_SIZE - len); /* xor 0 */
+
+ for (i = 0; i < len; i++)
+ out[i + AES_BLOCK_SIZE] = tmp2[i] ^ tmp3[i];
+
+ AES_decrypt(tmp3, out, key);
+ for (i = 0; i < AES_BLOCK_SIZE; i++)
+ out[i] ^= ivec[i];
+ memcpy(ivec, tmp, AES_BLOCK_SIZE);
+ }
+}
+
+static int
+aes_cts_do_cipher(EVP_CIPHER_CTX *ctx,
+ unsigned char *out,
+ const unsigned char *in,
+ unsigned int len)
+{
+ AES_KEY *k = ctx->cipher_data;
+
+ if (len < AES_BLOCK_SIZE)
+ abort(); /* krb5_abortx(context, "invalid use of AES_CTS_encrypt"); */
+ if (len == AES_BLOCK_SIZE) {
+ if (ctx->encrypt)
+ AES_encrypt(in, out, k);
+ else
+ AES_decrypt(in, out, k);
+ } else {
+ _krb5_aes_cts_encrypt(in, out, len, k, ctx->iv, ctx->encrypt);
+ }
+
+ return 1;
+}
+
+
+static int
+aes_cts_cleanup(EVP_CIPHER_CTX *ctx)
+{
+ memset(ctx->cipher_data, 0, sizeof(AES_KEY));
+ return 1;
+}
+
+/**
+ * The AES-128 cts cipher type (hcrypto)
+ *
+ * @return the AES-128 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+const EVP_CIPHER *
+_hc_EVP_hcrypto_aes_128_cts(void)
+{
+ static const EVP_CIPHER aes_128_cts = {
+ 0,
+ 1,
+ 16,
+ 16,
+ EVP_CIPH_CBC_MODE,
+ aes_cts_init,
+ aes_cts_do_cipher,
+ aes_cts_cleanup,
+ sizeof(AES_KEY),
+ NULL,
+ NULL,
+ NULL,
+ NULL
+ };
+
+ return &aes_128_cts;
+}
+
+/**
+ * The AES-192 cts cipher type (hcrypto)
+ *
+ * @return the AES-192 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+const EVP_CIPHER *
+_hc_EVP_hcrypto_aes_192_cts(void)
+{
+ static const EVP_CIPHER aes_192_cts = {
+ 0,
+ 1,
+ 24,
+ 16,
+ EVP_CIPH_CBC_MODE,
+ aes_cts_init,
+ aes_cts_do_cipher,
+ aes_cts_cleanup,
+ sizeof(AES_KEY),
+ NULL,
+ NULL,
+ NULL,
+ NULL
+ };
+
+ return &aes_192_cts;
+}
+
+/**
+ * The AES-256 cts cipher type (hcrypto)
+ *
+ * @return the AES-256 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+const EVP_CIPHER *
+_hc_EVP_hcrypto_aes_256_cts(void)
+{
+ static const EVP_CIPHER aes_256_cts = {
+ 0,
+ 1,
+ 32,
+ 16,
+ EVP_CIPH_CBC_MODE,
+ aes_cts_init,
+ aes_cts_do_cipher,
+ aes_cts_cleanup,
+ sizeof(AES_KEY),
+ NULL,
+ NULL,
+ NULL,
+ NULL
+ };
+
+ return &aes_256_cts;
+}
diff --git a/source4/heimdal/lib/hcrypto/evp-hcrypto.c b/source4/heimdal/lib/hcrypto/evp-hcrypto.c
new file mode 100644
index 0000000000..6897385619
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/evp-hcrypto.c
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 2006 - 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+RCSID("$Id$");
+
+#define HC_DEPRECATED
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include <evp.h>
+
+#include <krb5-types.h>
+
+#include <aes.h>
+
+/*
+ *
+ */
+
+static int
+aes_init(EVP_CIPHER_CTX *ctx,
+ const unsigned char * key,
+ const unsigned char * iv,
+ int encp)
+{
+ AES_KEY *k = ctx->cipher_data;
+ if (ctx->encrypt)
+ AES_set_encrypt_key(key, ctx->cipher->key_len * 8, k);
+ else
+ AES_set_decrypt_key(key, ctx->cipher->key_len * 8, k);
+ return 1;
+}
+
+static int
+aes_do_cipher(EVP_CIPHER_CTX *ctx,
+ unsigned char *out,
+ const unsigned char *in,
+ unsigned int size)
+{
+ AES_KEY *k = ctx->cipher_data;
+ AES_cbc_encrypt(in, out, size, k, ctx->iv, ctx->encrypt);
+ return 1;
+}
+
+static int
+aes_cleanup(EVP_CIPHER_CTX *ctx)
+{
+ memset(ctx->cipher_data, 0, sizeof(AES_KEY));
+ return 1;
+}
+
+/**
+ * The AES-128 cipher type (hcrypto)
+ *
+ * @return the AES-128 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+const EVP_CIPHER *
+EVP_hcrypto_aes_128_cbc(void)
+{
+ static const EVP_CIPHER aes_128_cbc = {
+ 0,
+ 16,
+ 16,
+ 16,
+ EVP_CIPH_CBC_MODE,
+ aes_init,
+ aes_do_cipher,
+ aes_cleanup,
+ sizeof(AES_KEY),
+ NULL,
+ NULL,
+ NULL,
+ NULL
+ };
+
+ return &aes_128_cbc;
+}
+
+/**
+ * The AES-192 cipher type (hcrypto)
+ *
+ * @return the AES-192 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+const EVP_CIPHER *
+EVP_hcrypto_aes_192_cbc(void)
+{
+ static const EVP_CIPHER aes_192_cbc = {
+ 0,
+ 16,
+ 24,
+ 16,
+ EVP_CIPH_CBC_MODE,
+ aes_init,
+ aes_do_cipher,
+ aes_cleanup,
+ sizeof(AES_KEY),
+ NULL,
+ NULL,
+ NULL,
+ NULL
+ };
+ return &aes_192_cbc;
+}
+
+/**
+ * The AES-256 cipher type (hcrypto)
+ *
+ * @return the AES-256 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+const EVP_CIPHER *
+EVP_hcrypto_aes_256_cbc(void)
+{
+ static const EVP_CIPHER aes_256_cbc = {
+ 0,
+ 16,
+ 32,
+ 16,
+ EVP_CIPH_CBC_MODE,
+ aes_init,
+ aes_do_cipher,
+ aes_cleanup,
+ sizeof(AES_KEY),
+ NULL,
+ NULL,
+ NULL,
+ NULL
+ };
+ return &aes_256_cbc;
+}
diff --git a/source4/heimdal/lib/hcrypto/evp.c b/source4/heimdal/lib/hcrypto/evp.c
new file mode 100644
index 0000000000..517ca2a2bc
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/evp.c
@@ -0,0 +1,1701 @@
+/*
+ * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+RCSID("$Id$");
+
+#define HC_DEPRECATED
+#define HC_DEPRECATED_CRYPTO
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include <evp.h>
+
+#include <krb5-types.h>
+
+#include "camellia.h"
+#include <des.h>
+#include <sha.h>
+#include <rc2.h>
+#include <rc4.h>
+#include <md2.h>
+#include <md4.h>
+#include <md5.h>
+
+/**
+ * @page page_evp EVP - generic crypto interface
+ *
+ * See the library functions here: @ref hcrypto_evp
+ *
+ * @section evp_cipher EVP Cipher
+ *
+ * The use of EVP_CipherInit_ex() and EVP_Cipher() is pretty easy to
+ * understand forward, then EVP_CipherUpdate() and
+ * EVP_CipherFinal_ex() really needs an example to explain @ref
+ * example_evp_cipher.c .
+ *
+ * @example example_evp_cipher.c
+ *
+ * This is an example how to use EVP_CipherInit_ex(),
+ * EVP_CipherUpdate() and EVP_CipherFinal_ex().
+ */
+
+struct hc_EVP_MD_CTX {
+ const EVP_MD *md;
+ ENGINE *engine;
+ void *ptr;
+};
+
+
+/**
+ * Return the output size of the message digest function.
+ *
+ * @param md the evp message
+ *
+ * @return size output size of the message digest function.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+size_t
+EVP_MD_size(const EVP_MD *md)
+{
+ return md->hash_size;
+}
+
+/**
+ * Return the blocksize of the message digest function.
+ *
+ * @param md the evp message
+ *
+ * @return size size of the message digest block size
+ *
+ * @ingroup hcrypto_evp
+ */
+
+size_t
+EVP_MD_block_size(const EVP_MD *md)
+{
+ return md->block_size;
+}
+
+/**
+ * Allocate a messsage digest context object. Free with
+ * EVP_MD_CTX_destroy().
+ *
+ * @return a newly allocated message digest context object.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+EVP_MD_CTX *
+EVP_MD_CTX_create(void)
+{
+ return calloc(1, sizeof(EVP_MD_CTX));
+}
+
+/**
+ * Initiate a messsage digest context object. Deallocate with
+ * EVP_MD_CTX_cleanup(). Please use EVP_MD_CTX_create() instead.
+ *
+ * @param ctx variable to initiate.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+void HC_DEPRECATED
+EVP_MD_CTX_init(EVP_MD_CTX *ctx)
+{
+ memset(ctx, 0, sizeof(*ctx));
+}
+
+/**
+ * Free a messsage digest context object.
+ *
+ * @param ctx context to free.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+void
+EVP_MD_CTX_destroy(EVP_MD_CTX *ctx)
+{
+ EVP_MD_CTX_cleanup(ctx);
+ free(ctx);
+}
+
+/**
+ * Free the resources used by the EVP_MD context.
+ *
+ * @param ctx the context to free the resources from.
+ *
+ * @return 1 on success.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+int HC_DEPRECATED
+EVP_MD_CTX_cleanup(EVP_MD_CTX *ctx)
+{
+ if (ctx->md && ctx->md->cleanup)
+ (ctx->md->cleanup)(ctx);
+ ctx->md = NULL;
+ ctx->engine = NULL;
+ free(ctx->ptr);
+ memset(ctx, 0, sizeof(*ctx));
+ return 1;
+}
+
+/**
+ * Get the EVP_MD use for a specified context.
+ *
+ * @param ctx the EVP_MD context to get the EVP_MD for.
+ *
+ * @return the EVP_MD used for the context.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+const EVP_MD *
+EVP_MD_CTX_md(EVP_MD_CTX *ctx)
+{
+ return ctx->md;
+}
+
+/**
+ * Return the output size of the message digest function.
+ *
+ * @param ctx the evp message digest context
+ *
+ * @return size output size of the message digest function.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+size_t
+EVP_MD_CTX_size(EVP_MD_CTX *ctx)
+{
+ return EVP_MD_size(ctx->md);
+}
+
+/**
+ * Return the blocksize of the message digest function.
+ *
+ * @param ctx the evp message digest context
+ *
+ * @return size size of the message digest block size
+ *
+ * @ingroup hcrypto_evp
+ */
+
+size_t
+EVP_MD_CTX_block_size(EVP_MD_CTX *ctx)
+{
+ return EVP_MD_block_size(ctx->md);
+}
+
+/**
+ * Init a EVP_MD_CTX for use a specific message digest and engine.
+ *
+ * @param ctx the message digest context to init.
+ * @param md the message digest to use.
+ * @param engine the engine to use, NULL to use the default engine.
+ *
+ * @return 1 on success.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+int
+EVP_DigestInit_ex(EVP_MD_CTX *ctx, const EVP_MD *md, ENGINE *engine)
+{
+ if (ctx->md != md || ctx->engine != engine) {
+ EVP_MD_CTX_cleanup(ctx);
+ ctx->md = md;
+ ctx->engine = engine;
+
+ ctx->ptr = calloc(1, md->ctx_size);
+ if (ctx->ptr == NULL)
+ return 0;
+ }
+ (ctx->md->init)(ctx->ptr);
+ return 1;
+}
+
+/**
+ * Update the digest with some data.
+ *
+ * @param ctx the context to update
+ * @param data the data to update the context with
+ * @param size length of data
+ *
+ * @return 1 on success.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+int
+EVP_DigestUpdate(EVP_MD_CTX *ctx, const void *data, size_t size)
+{
+ (ctx->md->update)(ctx->ptr, data, size);
+ return 1;
+}
+
+/**
+ * Complete the message digest.
+ *
+ * @param ctx the context to complete.
+ * @param hash the output of the message digest function. At least
+ * EVP_MD_size().
+ * @param size the output size of hash.
+ *
+ * @return 1 on success.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+int
+EVP_DigestFinal_ex(EVP_MD_CTX *ctx, void *hash, unsigned int *size)
+{
+ (ctx->md->final)(hash, ctx->ptr);
+ if (size)
+ *size = ctx->md->hash_size;
+ return 1;
+}
+
+/**
+ * Do the whole EVP_MD_CTX_create(), EVP_DigestInit_ex(),
+ * EVP_DigestUpdate(), EVP_DigestFinal_ex(), EVP_MD_CTX_destroy()
+ * dance in one call.
+ *
+ * @param data the data to update the context with
+ * @param dsize length of data
+ * @param hash output data of at least EVP_MD_size() length.
+ * @param hsize output length of hash.
+ * @param md message digest to use
+ * @param engine engine to use, NULL for default engine.
+ *
+ * @return 1 on success.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+int
+EVP_Digest(const void *data, size_t dsize, void *hash, unsigned int *hsize,
+ const EVP_MD *md, ENGINE *engine)
+{
+ EVP_MD_CTX *ctx;
+ int ret;
+
+ ctx = EVP_MD_CTX_create();
+ if (ctx == NULL)
+ return 0;
+ ret = EVP_DigestInit_ex(ctx, md, engine);
+ if (ret != 1) {
+ EVP_MD_CTX_destroy(ctx);
+ return ret;
+ }
+ ret = EVP_DigestUpdate(ctx, data, dsize);
+ if (ret != 1) {
+ EVP_MD_CTX_destroy(ctx);
+ return ret;
+ }
+ ret = EVP_DigestFinal_ex(ctx, hash, hsize);
+ EVP_MD_CTX_destroy(ctx);
+ return ret;
+}
+
+/**
+ * The message digest SHA256
+ *
+ * @return the message digest type.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+const EVP_MD *
+EVP_sha256(void)
+{
+ static const struct hc_evp_md sha256 = {
+ 32,
+ 64,
+ sizeof(SHA256_CTX),
+ (hc_evp_md_init)SHA256_Init,
+ (hc_evp_md_update)SHA256_Update,
+ (hc_evp_md_final)SHA256_Final,
+ NULL
+ };
+ return &sha256;
+}
+
+static const struct hc_evp_md sha1 = {
+ 20,
+ 64,
+ sizeof(SHA_CTX),
+ (hc_evp_md_init)SHA1_Init,
+ (hc_evp_md_update)SHA1_Update,
+ (hc_evp_md_final)SHA1_Final,
+ NULL
+};
+
+/**
+ * The message digest SHA1
+ *
+ * @return the message digest type.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+const EVP_MD *
+EVP_sha1(void)
+{
+ return &sha1;
+}
+
+/**
+ * The message digest SHA1
+ *
+ * @return the message digest type.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+const EVP_MD *
+EVP_sha(void)
+{
+ return &sha1;
+}
+
+/**
+ * The message digest MD5
+ *
+ * @return the message digest type.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+const EVP_MD *
+EVP_md5(void)
+{
+ static const struct hc_evp_md md5 = {
+ 16,
+ 64,
+ sizeof(MD5_CTX),
+ (hc_evp_md_init)MD5_Init,
+ (hc_evp_md_update)MD5_Update,
+ (hc_evp_md_final)MD5_Final,
+ NULL
+ };
+ return &md5;
+}
+
+/**
+ * The message digest MD4
+ *
+ * @return the message digest type.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+const EVP_MD *
+EVP_md4(void)
+{
+ static const struct hc_evp_md md4 = {
+ 16,
+ 64,
+ sizeof(MD4_CTX),
+ (hc_evp_md_init)MD4_Init,
+ (hc_evp_md_update)MD4_Update,
+ (hc_evp_md_final)MD4_Final,
+ NULL
+ };
+ return &md4;
+}
+
+/**
+ * The message digest MD2
+ *
+ * @return the message digest type.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+const EVP_MD *
+EVP_md2(void)
+{
+ static const struct hc_evp_md md2 = {
+ 16,
+ 16,
+ sizeof(MD2_CTX),
+ (hc_evp_md_init)MD2_Init,
+ (hc_evp_md_update)MD2_Update,
+ (hc_evp_md_final)MD2_Final,
+ NULL
+ };
+ return &md2;
+}
+
+/*
+ *
+ */
+
+static void
+null_Init (void *m)
+{
+}
+static void
+null_Update (void *m, const void * data, size_t size)
+{
+}
+static void
+null_Final(void *res, void *m)
+{
+}
+
+/**
+ * The null message digest
+ *
+ * @return the message digest type.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+const EVP_MD *
+EVP_md_null(void)
+{
+ static const struct hc_evp_md null = {
+ 0,
+ 0,
+ 0,
+ (hc_evp_md_init)null_Init,
+ (hc_evp_md_update)null_Update,
+ (hc_evp_md_final)null_Final,
+ NULL
+ };
+ return &null;
+}
+
+#if 0
+int EVP_DigestInit(EVP_MD_CTX *ctx, const EVP_MD *type);
+int EVP_DigestFinal(EVP_MD_CTX *ctx,unsigned char *md,unsigned int *s);
+int EVP_SignFinal(EVP_MD_CTX *, void *, size_t *, EVP_PKEY *);
+int EVP_VerifyFinal(EVP_MD_CTX *, const void *, size_t, EVP_PKEY *);
+#endif
+
+/**
+ * Return the block size of the cipher.
+ *
+ * @param c cipher to get the block size from.
+ *
+ * @return the block size of the cipher.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+size_t
+EVP_CIPHER_block_size(const EVP_CIPHER *c)
+{
+ return c->block_size;
+}
+
+/**
+ * Return the key size of the cipher.
+ *
+ * @param c cipher to get the key size from.
+ *
+ * @return the key size of the cipher.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+size_t
+EVP_CIPHER_key_length(const EVP_CIPHER *c)
+{
+ return c->key_len;
+}
+
+/**
+ * Return the IV size of the cipher.
+ *
+ * @param c cipher to get the IV size from.
+ *
+ * @return the IV size of the cipher.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+size_t
+EVP_CIPHER_iv_length(const EVP_CIPHER *c)
+{
+ return c->iv_len;
+}
+
+/**
+ * Initiate a EVP_CIPHER_CTX context. Clean up with
+ * EVP_CIPHER_CTX_cleanup().
+ *
+ * @param c the cipher initiate.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+void
+EVP_CIPHER_CTX_init(EVP_CIPHER_CTX *c)
+{
+ memset(c, 0, sizeof(*c));
+}
+
+/**
+ * Clean up the EVP_CIPHER_CTX context.
+ *
+ * @param c the cipher to clean up.
+ *
+ * @return 1 on success.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+int
+EVP_CIPHER_CTX_cleanup(EVP_CIPHER_CTX *c)
+{
+ if (c->cipher && c->cipher->cleanup)
+ c->cipher->cleanup(c);
+ if (c->cipher_data) {
+ free(c->cipher_data);
+ c->cipher_data = NULL;
+ }
+ return 1;
+}
+
+#if 0
+int
+EVP_CIPHER_CTX_set_key_length(EVP_CIPHER_CTX *c, int length)
+{
+ return 0;
+}
+
+int
+EVP_CIPHER_CTX_set_padding(EVP_CIPHER_CTX *c, int pad)
+{
+ return 0;
+}
+#endif
+
+/**
+ * Return the EVP_CIPHER for a EVP_CIPHER_CTX context.
+ *
+ * @param ctx the context to get the cipher type from.
+ *
+ * @return the EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+const EVP_CIPHER *
+EVP_CIPHER_CTX_cipher(EVP_CIPHER_CTX *ctx)
+{
+ return ctx->cipher;
+}
+
+/**
+ * Return the block size of the cipher context.
+ *
+ * @param ctx cipher context to get the block size from.
+ *
+ * @return the block size of the cipher context.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+size_t
+EVP_CIPHER_CTX_block_size(const EVP_CIPHER_CTX *ctx)
+{
+ return EVP_CIPHER_block_size(ctx->cipher);
+}
+
+/**
+ * Return the key size of the cipher context.
+ *
+ * @param ctx cipher context to get the key size from.
+ *
+ * @return the key size of the cipher context.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+size_t
+EVP_CIPHER_CTX_key_length(const EVP_CIPHER_CTX *ctx)
+{
+ return EVP_CIPHER_key_length(ctx->cipher);
+}
+
+/**
+ * Return the IV size of the cipher context.
+ *
+ * @param ctx cipher context to get the IV size from.
+ *
+ * @return the IV size of the cipher context.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+size_t
+EVP_CIPHER_CTX_iv_length(const EVP_CIPHER_CTX *ctx)
+{
+ return EVP_CIPHER_iv_length(ctx->cipher);
+}
+
+/**
+ * Get the flags for an EVP_CIPHER_CTX context.
+ *
+ * @param ctx the EVP_CIPHER_CTX to get the flags from
+ *
+ * @return the flags for an EVP_CIPHER_CTX.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+unsigned long
+EVP_CIPHER_CTX_flags(const EVP_CIPHER_CTX *ctx)
+{
+ return ctx->cipher->flags;
+}
+
+/**
+ * Get the mode for an EVP_CIPHER_CTX context.
+ *
+ * @param ctx the EVP_CIPHER_CTX to get the mode from
+ *
+ * @return the mode for an EVP_CIPHER_CTX.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+int
+EVP_CIPHER_CTX_mode(const EVP_CIPHER_CTX *ctx)
+{
+ return EVP_CIPHER_CTX_flags(ctx) & EVP_CIPH_MODE;
+}
+
+/**
+ * Get the app data for an EVP_CIPHER_CTX context.
+ *
+ * @param ctx the EVP_CIPHER_CTX to get the app data from
+ *
+ * @return the app data for an EVP_CIPHER_CTX.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+void *
+EVP_CIPHER_CTX_get_app_data(EVP_CIPHER_CTX *ctx)
+{
+ return ctx->app_data;
+}
+
+/**
+ * Set the app data for an EVP_CIPHER_CTX context.
+ *
+ * @param ctx the EVP_CIPHER_CTX to set the app data for
+ * @param data the app data to set for an EVP_CIPHER_CTX.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+void
+EVP_CIPHER_CTX_set_app_data(EVP_CIPHER_CTX *ctx, void *data)
+{
+ ctx->app_data = data;
+}
+
+/**
+ * Initiate the EVP_CIPHER_CTX context to encrypt or decrypt data.
+ * Clean up with EVP_CIPHER_CTX_cleanup().
+ *
+ * @param ctx context to initiate
+ * @param c cipher to use.
+ * @param engine crypto engine to use, NULL to select default.
+ * @param key the crypto key to use, NULL will use the previous value.
+ * @param iv the IV to use, NULL will use the previous value.
+ * @param encp non zero will encrypt, -1 use the previous value.
+ *
+ * @return 1 on success.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+int
+EVP_CipherInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *c, ENGINE *engine,
+ const void *key, const void *iv, int encp)
+{
+ ctx->buf_len = 0;
+
+ if (encp == -1)
+ encp = ctx->encrypt;
+ else
+ ctx->encrypt = (encp ? 1 : 0);
+
+ if (c && (c != ctx->cipher)) {
+ EVP_CIPHER_CTX_cleanup(ctx);
+ ctx->cipher = c;
+ ctx->key_len = c->key_len;
+
+ ctx->cipher_data = malloc(c->ctx_size);
+ if (ctx->cipher_data == NULL && c->ctx_size != 0)
+ return 0;
+
+ /* assume block size is a multiple of 2 */
+ ctx->block_mask = EVP_CIPHER_block_size(c) - 1;
+
+ } else if (ctx->cipher == NULL) {
+ /* reuse of cipher, but not any cipher ever set! */
+ return 0;
+ }
+
+ switch (EVP_CIPHER_CTX_flags(ctx)) {
+ case EVP_CIPH_CBC_MODE:
+
+ assert(EVP_CIPHER_CTX_iv_length(ctx) <= sizeof(ctx->iv));
+
+ if (iv)
+ memcpy(ctx->oiv, iv, EVP_CIPHER_CTX_iv_length(ctx));
+ memcpy(ctx->iv, ctx->oiv, EVP_CIPHER_CTX_iv_length(ctx));
+ break;
+ default:
+ return 0;
+ }
+
+ if (key || (ctx->cipher->flags & EVP_CIPH_ALWAYS_CALL_INIT))
+ ctx->cipher->init(ctx, key, iv, encp);
+
+ return 1;
+}
+
+/**
+ * Encipher/decipher partial data
+ *
+ * @param ctx the cipher context.
+ * @param out output data from the operation.
+ * @param outlen output length
+ * @param in input data to the operation.
+ * @param inlen length of data.
+ *
+ * The output buffer length should at least be EVP_CIPHER_block_size()
+ * byte longer then the input length.
+ *
+ * See @ref evp_cipher for an example how to use this function.
+ *
+ * @return 1 on success.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+int
+EVP_CipherUpdate(EVP_CIPHER_CTX *ctx, void *out, int *outlen,
+ void *in, size_t inlen)
+{
+ int ret, left, blocksize;
+
+ *outlen = 0;
+
+ /**
+ * If there in no spare bytes in the left from last Update and the
+ * input length is on the block boundery, the EVP_CipherUpdate()
+ * function can take a shortcut (and preformance gain) and
+ * directly encrypt the data, otherwise we hav to fix it up and
+ * store extra it the EVP_CIPHER_CTX.
+ */
+ if (ctx->buf_len == 0 && (inlen & ctx->block_mask) == 0) {
+ ret = (*ctx->cipher->do_cipher)(ctx, out, in, inlen);
+ if (ret == 1)
+ *outlen = inlen;
+ else
+ *outlen = 0;
+ return ret;
+ }
+
+
+ blocksize = EVP_CIPHER_CTX_block_size(ctx);
+ left = blocksize - ctx->buf_len;
+ assert(left > 0);
+
+ if (ctx->buf_len) {
+
+ /* if total buffer is smaller then input, store locally */
+ if (inlen < left) {
+ memcpy(ctx->buf + ctx->buf_len, in, inlen);
+ ctx->buf_len += inlen;
+ return 1;
+ }
+
+ /* fill in local buffer and encrypt */
+ memcpy(ctx->buf + ctx->buf_len, in, left);
+ ret = (*ctx->cipher->do_cipher)(ctx, out, ctx->buf, blocksize);
+ memset(ctx->buf, 0, blocksize);
+ if (ret != 1)
+ return ret;
+
+ *outlen += blocksize;
+ inlen -= left;
+ in = ((unsigned char *)in) + left;
+ out = ((unsigned char *)out) + blocksize;
+ ctx->buf_len = 0;
+ }
+
+ if (inlen) {
+ ctx->buf_len = (inlen & ctx->block_mask);
+ inlen &= ~ctx->block_mask;
+
+ ret = (*ctx->cipher->do_cipher)(ctx, out, in, inlen);
+ if (ret != 1)
+ return ret;
+
+ *outlen += inlen;
+
+ in = ((unsigned char *)in) + inlen;
+ memcpy(ctx->buf, in, ctx->buf_len);
+ }
+
+ return 1;
+}
+
+/**
+ * Encipher/decipher final data
+ *
+ * @param ctx the cipher context.
+ * @param out output data from the operation.
+ * @param outlen output length
+ *
+ * The input length needs to be at least EVP_CIPHER_block_size() bytes
+ * long.
+ *
+ * See @ref evp_cipher for an example how to use this function.
+ *
+ * @return 1 on success.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+int
+EVP_CipherFinal_ex(EVP_CIPHER_CTX *ctx, void *out, int *outlen)
+{
+ *outlen = 0;
+
+ if (ctx->buf_len) {
+ int ret, left, blocksize;
+
+ blocksize = EVP_CIPHER_CTX_block_size(ctx);
+
+ left = blocksize - ctx->buf_len;
+ assert(left > 0);
+
+ /* zero fill local buffer */
+ memset(ctx->buf + ctx->buf_len, 0, left);
+ ret = (*ctx->cipher->do_cipher)(ctx, out, ctx->buf, blocksize);
+ memset(ctx->buf, 0, blocksize);
+ if (ret != 1)
+ return ret;
+
+ *outlen += blocksize;
+ }
+
+ return 1;
+}
+
+/**
+ * Encipher/decipher data
+ *
+ * @param ctx the cipher context.
+ * @param out out data from the operation.
+ * @param in in data to the operation.
+ * @param size length of data.
+ *
+ * @return 1 on success.
+ */
+
+int
+EVP_Cipher(EVP_CIPHER_CTX *ctx, void *out, const void *in,size_t size)
+{
+ return ctx->cipher->do_cipher(ctx, out, in, size);
+}
+
+/*
+ *
+ */
+
+static int
+enc_null_init(EVP_CIPHER_CTX *ctx,
+ const unsigned char * key,
+ const unsigned char * iv,
+ int encp)
+{
+ return 1;
+}
+
+static int
+enc_null_do_cipher(EVP_CIPHER_CTX *ctx,
+ unsigned char *out,
+ const unsigned char *in,
+ unsigned int size)
+{
+ memmove(out, in, size);
+ return 1;
+}
+
+static int
+enc_null_cleanup(EVP_CIPHER_CTX *ctx)
+{
+ return 1;
+}
+
+/**
+ * The NULL cipher type, does no encryption/decryption.
+ *
+ * @return the null EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+const EVP_CIPHER *
+EVP_enc_null(void)
+{
+ static const EVP_CIPHER enc_null = {
+ 0,
+ 0,
+ 0,
+ 0,
+ EVP_CIPH_CBC_MODE,
+ enc_null_init,
+ enc_null_do_cipher,
+ enc_null_cleanup,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+ };
+ return &enc_null;
+}
+
+/*
+ *
+ */
+
+struct rc2_cbc {
+ unsigned int maximum_effective_key;
+ RC2_KEY key;
+};
+
+static int
+rc2_init(EVP_CIPHER_CTX *ctx,
+ const unsigned char * key,
+ const unsigned char * iv,
+ int encp)
+{
+ struct rc2_cbc *k = ctx->cipher_data;
+ k->maximum_effective_key = EVP_CIPHER_CTX_key_length(ctx) * 8;
+ RC2_set_key(&k->key,
+ EVP_CIPHER_CTX_key_length(ctx),
+ key,
+ k->maximum_effective_key);
+ return 1;
+}
+
+static int
+rc2_do_cipher(EVP_CIPHER_CTX *ctx,
+ unsigned char *out,
+ const unsigned char *in,
+ unsigned int size)
+{
+ struct rc2_cbc *k = ctx->cipher_data;
+ RC2_cbc_encrypt(in, out, size, &k->key, ctx->iv, ctx->encrypt);
+ return 1;
+}
+
+static int
+rc2_cleanup(EVP_CIPHER_CTX *ctx)
+{
+ memset(ctx->cipher_data, 0, sizeof(struct rc2_cbc));
+ return 1;
+}
+
+/**
+ * The RC2 cipher type
+ *
+ * @return the RC2 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+const EVP_CIPHER *
+EVP_rc2_cbc(void)
+{
+ static const EVP_CIPHER rc2_cbc = {
+ 0,
+ RC2_BLOCK_SIZE,
+ RC2_KEY_LENGTH,
+ RC2_BLOCK_SIZE,
+ EVP_CIPH_CBC_MODE,
+ rc2_init,
+ rc2_do_cipher,
+ rc2_cleanup,
+ sizeof(struct rc2_cbc),
+ NULL,
+ NULL,
+ NULL,
+ NULL
+ };
+ return &rc2_cbc;
+}
+
+/**
+ * The RC2-40 cipher type
+ *
+ * @return the RC2-40 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+const EVP_CIPHER *
+EVP_rc2_40_cbc(void)
+{
+ static const EVP_CIPHER rc2_40_cbc = {
+ 0,
+ RC2_BLOCK_SIZE,
+ 5,
+ RC2_BLOCK_SIZE,
+ EVP_CIPH_CBC_MODE,
+ rc2_init,
+ rc2_do_cipher,
+ rc2_cleanup,
+ sizeof(struct rc2_cbc),
+ NULL,
+ NULL,
+ NULL,
+ NULL
+ };
+ return &rc2_40_cbc;
+}
+
+/**
+ * The RC2-64 cipher type
+ *
+ * @return the RC2-64 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+const EVP_CIPHER *
+EVP_rc2_64_cbc(void)
+{
+ static const EVP_CIPHER rc2_64_cbc = {
+ 0,
+ RC2_BLOCK_SIZE,
+ 8,
+ RC2_BLOCK_SIZE,
+ EVP_CIPH_CBC_MODE,
+ rc2_init,
+ rc2_do_cipher,
+ rc2_cleanup,
+ sizeof(struct rc2_cbc),
+ NULL,
+ NULL,
+ NULL,
+ NULL
+ };
+ return &rc2_64_cbc;
+}
+
+/**
+ * The RC4 cipher type
+ *
+ * @return the RC4 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+const EVP_CIPHER *
+EVP_rc4(void)
+{
+ printf("evp rc4\n");
+ abort();
+ return NULL;
+}
+
+/**
+ * The RC4-40 cipher type
+ *
+ * @return the RC4-40 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+const EVP_CIPHER *
+EVP_rc4_40(void)
+{
+ printf("evp rc4_40\n");
+ abort();
+ return NULL;
+}
+
+/*
+ *
+ */
+
+static int
+des_cbc_init(EVP_CIPHER_CTX *ctx,
+ const unsigned char * key,
+ const unsigned char * iv,
+ int encp)
+{
+ DES_key_schedule *k = ctx->cipher_data;
+ DES_cblock deskey;
+ memcpy(&deskey, key, sizeof(deskey));
+ DES_set_key_unchecked(&deskey, k);
+ return 1;
+}
+
+static int
+des_cbc_do_cipher(EVP_CIPHER_CTX *ctx,
+ unsigned char *out,
+ const unsigned char *in,
+ unsigned int size)
+{
+ DES_key_schedule *k = ctx->cipher_data;
+ DES_cbc_encrypt(in, out, size,
+ k, (DES_cblock *)ctx->iv, ctx->encrypt);
+ return 1;
+}
+
+static int
+des_cbc_cleanup(EVP_CIPHER_CTX *ctx)
+{
+ memset(ctx->cipher_data, 0, sizeof(struct DES_key_schedule));
+ return 1;
+}
+
+/**
+ * The DES cipher type
+ *
+ * @return the DES-CBC EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+const EVP_CIPHER *
+EVP_des_cbc(void)
+{
+ static const EVP_CIPHER des_ede3_cbc = {
+ 0,
+ 8,
+ 8,
+ 8,
+ EVP_CIPH_CBC_MODE,
+ des_cbc_init,
+ des_cbc_do_cipher,
+ des_cbc_cleanup,
+ sizeof(DES_key_schedule),
+ NULL,
+ NULL,
+ NULL,
+ NULL
+ };
+ return &des_ede3_cbc;
+}
+
+/*
+ *
+ */
+
+struct des_ede3_cbc {
+ DES_key_schedule ks[3];
+};
+
+static int
+des_ede3_cbc_init(EVP_CIPHER_CTX *ctx,
+ const unsigned char * key,
+ const unsigned char * iv,
+ int encp)
+{
+ struct des_ede3_cbc *k = ctx->cipher_data;
+ DES_cblock deskey;
+
+ memcpy(&deskey, key, sizeof(deskey));
+ DES_set_odd_parity(&deskey);
+ DES_set_key_unchecked(&deskey, &k->ks[0]);
+
+ memcpy(&deskey, key + 8, sizeof(deskey));
+ DES_set_odd_parity(&deskey);
+ DES_set_key_unchecked(&deskey, &k->ks[1]);
+
+ memcpy(&deskey, key + 16, sizeof(deskey));
+ DES_set_odd_parity(&deskey);
+ DES_set_key_unchecked(&deskey, &k->ks[2]);
+
+ return 1;
+}
+
+static int
+des_ede3_cbc_do_cipher(EVP_CIPHER_CTX *ctx,
+ unsigned char *out,
+ const unsigned char *in,
+ unsigned int size)
+{
+ struct des_ede3_cbc *k = ctx->cipher_data;
+ DES_ede3_cbc_encrypt(in, out, size,
+ &k->ks[0], &k->ks[1], &k->ks[2],
+ (DES_cblock *)ctx->iv, ctx->encrypt);
+ return 1;
+}
+
+static int
+des_ede3_cbc_cleanup(EVP_CIPHER_CTX *ctx)
+{
+ memset(ctx->cipher_data, 0, sizeof(struct des_ede3_cbc));
+ return 1;
+}
+
+/**
+ * The tripple DES cipher type
+ *
+ * @return the DES-EDE3-CBC EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+const EVP_CIPHER *
+EVP_des_ede3_cbc(void)
+{
+ static const EVP_CIPHER des_ede3_cbc = {
+ 0,
+ 8,
+ 24,
+ 8,
+ EVP_CIPH_CBC_MODE,
+ des_ede3_cbc_init,
+ des_ede3_cbc_do_cipher,
+ des_ede3_cbc_cleanup,
+ sizeof(struct des_ede3_cbc),
+ NULL,
+ NULL,
+ NULL,
+ NULL
+ };
+ return &des_ede3_cbc;
+}
+
+/**
+ * The AES-128 cipher type
+ *
+ * @return the AES-128 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+const EVP_CIPHER *
+EVP_aes_128_cbc(void)
+{
+ return EVP_hcrypto_aes_128_cbc();
+}
+
+/**
+ * The AES-192 cipher type
+ *
+ * @return the AES-192 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+const EVP_CIPHER *
+EVP_aes_192_cbc(void)
+{
+ return EVP_hcrypto_aes_192_cbc();
+}
+
+/**
+ * The AES-256 cipher type
+ *
+ * @return the AES-256 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+const EVP_CIPHER *
+EVP_aes_256_cbc(void)
+{
+ return EVP_hcrypto_aes_256_cbc();
+}
+
+static int
+camellia_init(EVP_CIPHER_CTX *ctx,
+ const unsigned char * key,
+ const unsigned char * iv,
+ int encp)
+{
+ CAMELLIA_KEY *k = ctx->cipher_data;
+ k->bits = ctx->cipher->key_len * 8;
+ CAMELLIA_set_key(key, ctx->cipher->key_len * 8, k);
+ return 1;
+}
+
+static int
+camellia_do_cipher(EVP_CIPHER_CTX *ctx,
+ unsigned char *out,
+ const unsigned char *in,
+ unsigned int size)
+{
+ CAMELLIA_KEY *k = ctx->cipher_data;
+ CAMELLIA_cbc_encrypt(in, out, size, k, ctx->iv, ctx->encrypt);
+ return 1;
+}
+
+static int
+camellia_cleanup(EVP_CIPHER_CTX *ctx)
+{
+ memset(ctx->cipher_data, 0, sizeof(CAMELLIA_KEY));
+ return 1;
+}
+
+/**
+ * The Camellia-128 cipher type
+ *
+ * @return the Camellia-128 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+const EVP_CIPHER *
+EVP_camellia_128_cbc(void)
+{
+ static const EVP_CIPHER cipher = {
+ 0,
+ 16,
+ 16,
+ 16,
+ EVP_CIPH_CBC_MODE,
+ camellia_init,
+ camellia_do_cipher,
+ camellia_cleanup,
+ sizeof(CAMELLIA_KEY),
+ NULL,
+ NULL,
+ NULL,
+ NULL
+ };
+ return &cipher;
+}
+
+/**
+ * The Camellia-198 cipher type
+ *
+ * @return the Camellia-198 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+const EVP_CIPHER *
+EVP_camellia_192_cbc(void)
+{
+ static const EVP_CIPHER cipher = {
+ 0,
+ 16,
+ 24,
+ 16,
+ EVP_CIPH_CBC_MODE,
+ camellia_init,
+ camellia_do_cipher,
+ camellia_cleanup,
+ sizeof(CAMELLIA_KEY),
+ NULL,
+ NULL,
+ NULL,
+ NULL
+ };
+ return &cipher;
+}
+
+/**
+ * The Camellia-256 cipher type
+ *
+ * @return the Camellia-256 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+const EVP_CIPHER *
+EVP_camellia_256_cbc(void)
+{
+ static const EVP_CIPHER cipher = {
+ 0,
+ 16,
+ 32,
+ 16,
+ EVP_CIPH_CBC_MODE,
+ camellia_init,
+ camellia_do_cipher,
+ camellia_cleanup,
+ sizeof(CAMELLIA_KEY),
+ NULL,
+ NULL,
+ NULL,
+ NULL
+ };
+ return &cipher;
+}
+
+/*
+ *
+ */
+
+static const struct cipher_name {
+ const char *name;
+ const EVP_CIPHER *(*func)(void);
+} cipher_name[] = {
+ { "des-ede3-cbc", EVP_des_ede3_cbc },
+ { "aes-128-cbc", EVP_aes_128_cbc },
+ { "aes-192-cbc", EVP_aes_192_cbc },
+ { "aes-256-cbc", EVP_aes_256_cbc },
+ { "camellia-128-cbc", EVP_camellia_128_cbc },
+ { "camellia-192-cbc", EVP_camellia_192_cbc },
+ { "camellia-256-cbc", EVP_camellia_256_cbc }
+};
+
+/**
+ * Get the cipher type using their name.
+ *
+ * @param name the name of the cipher.
+ *
+ * @return the selected EVP_CIPHER pointer or NULL if not found.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+const EVP_CIPHER *
+EVP_get_cipherbyname(const char *name)
+{
+ int i;
+ for (i = 0; i < sizeof(cipher_name)/sizeof(cipher_name[0]); i++) {
+ if (strcasecmp(cipher_name[i].name, name) == 0)
+ return (*cipher_name[i].func)();
+ }
+ return NULL;
+}
+
+
+/*
+ *
+ */
+
+#ifndef min
+#define min(a,b) (((a)>(b))?(b):(a))
+#endif
+
+/**
+ * Provides a legancy string to key function, used in PEM files.
+ *
+ * New protocols should use new string to key functions like NIST
+ * SP56-800A or PKCS#5 v2.0 (see PKCS5_PBKDF2_HMAC_SHA1()).
+ *
+ * @param type type of cipher to use
+ * @param md message digest to use
+ * @param salt salt salt string, should be an binary 8 byte buffer.
+ * @param data the password/input key string.
+ * @param datalen length of data parameter.
+ * @param count iteration counter.
+ * @param keydata output keydata, needs to of the size EVP_CIPHER_key_length().
+ * @param ivdata output ivdata, needs to of the size EVP_CIPHER_block_size().
+ *
+ * @return the size of derived key.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+int
+EVP_BytesToKey(const EVP_CIPHER *type,
+ const EVP_MD *md,
+ const void *salt,
+ const void *data, size_t datalen,
+ unsigned int count,
+ void *keydata,
+ void *ivdata)
+{
+ int ivlen, keylen, first = 0;
+ unsigned int mds = 0, i;
+ unsigned char *key = keydata;
+ unsigned char *iv = ivdata;
+ unsigned char *buf;
+ EVP_MD_CTX c;
+
+ keylen = EVP_CIPHER_key_length(type);
+ ivlen = EVP_CIPHER_iv_length(type);
+
+ if (data == NULL)
+ return keylen;
+
+ buf = malloc(EVP_MD_size(md));
+ if (buf == NULL)
+ return -1;
+
+ EVP_MD_CTX_init(&c);
+
+ first = 1;
+ while (1) {
+ EVP_DigestInit_ex(&c, md, NULL);
+ if (!first)
+ EVP_DigestUpdate(&c, buf, mds);
+ first = 0;
+ EVP_DigestUpdate(&c,data,datalen);
+
+#define PKCS5_SALT_LEN 8
+
+ if (salt)
+ EVP_DigestUpdate(&c, salt, PKCS5_SALT_LEN);
+
+ EVP_DigestFinal_ex(&c, buf, &mds);
+ assert(mds == EVP_MD_size(md));
+
+ for (i = 1; i < count; i++) {
+ EVP_DigestInit_ex(&c, md, NULL);
+ EVP_DigestUpdate(&c, buf, mds);
+ EVP_DigestFinal_ex(&c, buf, &mds);
+ assert(mds == EVP_MD_size(md));
+ }
+
+ i = 0;
+ if (keylen) {
+ size_t sz = min(keylen, mds);
+ if (key) {
+ memcpy(key, buf, sz);
+ key += sz;
+ }
+ keylen -= sz;
+ i += sz;
+ }
+ if (ivlen && mds > i) {
+ size_t sz = min(ivlen, (mds - i));
+ if (iv) {
+ memcpy(iv, &buf[i], sz);
+ iv += sz;
+ }
+ ivlen -= sz;
+ }
+ if (keylen == 0 && ivlen == 0)
+ break;
+ }
+
+ EVP_MD_CTX_cleanup(&c);
+ free(buf);
+
+ return EVP_CIPHER_key_length(type);
+}
+
+/**
+ * Generate a random key for the specificed EVP_CIPHER.
+ *
+ * @param ctx EVP_CIPHER_CTX type to build the key for.
+ * @param key return key, must be at least EVP_CIPHER_key_length() byte long.
+ *
+ * @return 1 for success, 0 for failure.
+ *
+ * @ingroup hcrypto_core
+ */
+
+int
+EVP_CIPHER_CTX_rand_key(EVP_CIPHER_CTX *ctx, void *key)
+{
+ if (ctx->cipher->flags & EVP_CIPH_RAND_KEY)
+ return EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_RAND_KEY, 0, key);
+ if (RAND_bytes(key, ctx->key_len) != 1)
+ return 0;
+ return 1;
+}
+
+/**
+ * Perform a operation on a ctx
+ *
+ * @return 1 for success, 0 for failure.
+ *
+ * @ingroup hcrypto_core
+ */
+
+int
+EVP_CIPHER_CTX_ctrl(EVP_CIPHER_CTX *ctx, int type, int arg, void *data)
+{
+ if (ctx->cipher == NULL || ctx->cipher->ctrl == NULL)
+ return 0;
+ return (*ctx->cipher->ctrl)(ctx, type, arg, data);
+}
+
+/**
+ * Add all algorithms to the crypto core.
+ *
+ * @ingroup hcrypto_core
+ */
+
+void
+OpenSSL_add_all_algorithms(void)
+{
+ return;
+}
+
+/**
+ * Add all algorithms to the crypto core using configuration file.
+ *
+ * @ingroup hcrypto_core
+ */
+
+void
+OpenSSL_add_all_algorithms_conf(void)
+{
+ return;
+}
+
+/**
+ * Add all algorithms to the crypto core, but don't use the
+ * configuration file.
+ *
+ * @ingroup hcrypto_core
+ */
+
+void
+OpenSSL_add_all_algorithms_noconf(void)
+{
+ return;
+}
diff --git a/source4/heimdal/lib/hcrypto/evp.h b/source4/heimdal/lib/hcrypto/evp.h
new file mode 100644
index 0000000000..a7c8fac900
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/evp.h
@@ -0,0 +1,318 @@
+/*
+ * Copyright (c) 2005 - 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef HEIM_EVP_H
+#define HEIM_EVP_H 1
+
+#include <hcrypto/engine.h>
+
+/* symbol renaming */
+#define EVP_CIPHER_CTX_block_size hc_EVP_CIPHER_CTX_block_size
+#define EVP_CIPHER_CTX_cipher hc_EVP_CIPHER_CTX_cipher
+#define EVP_CIPHER_CTX_cleanup hc_EVP_CIPHER_CTX_cleanup
+#define EVP_CIPHER_CTX_flags hc_EVP_CIPHER_CTX_flags
+#define EVP_CIPHER_CTX_get_app_data hc_EVP_CIPHER_CTX_get_app_data
+#define EVP_CIPHER_CTX_init hc_EVP_CIPHER_CTX_init
+#define EVP_CIPHER_CTX_iv_length hc_EVP_CIPHER_CTX_iv_length
+#define EVP_CIPHER_CTX_key_length hc_EVP_CIPHER_CTX_key_length
+#define EVP_CIPHER_CTX_mode hc_EVP_CIPHER_CTX_mode
+#define EVP_CIPHER_CTX_set_app_data hc_EVP_CIPHER_CTX_set_app_data
+#define EVP_CIPHER_CTX_set_key_length hc_EVP_CIPHER_CTX_set_key_length
+#define EVP_CIPHER_CTX_set_padding hc_EVP_CIPHER_CTX_set_padding
+#define EVP_CIPHER_block_size hc_EVP_CIPHER_block_size
+#define EVP_CIPHER_iv_length hc_EVP_CIPHER_iv_length
+#define EVP_CIPHER_key_length hc_EVP_CIPHER_key_length
+#define EVP_Cipher hc_EVP_Cipher
+#define EVP_CipherInit_ex hc_EVP_CipherInit_ex
+#define EVP_CipherUpdate hc_EVP_CipherUpdate
+#define EVP_CipherFinal_ex hc_EVP_CipherFinal_ex
+#define EVP_Digest hc_EVP_Digest
+#define EVP_DigestFinal_ex hc_EVP_DigestFinal_ex
+#define EVP_DigestInit_ex hc_EVP_DigestInit_ex
+#define EVP_DigestUpdate hc_EVP_DigestUpdate
+#define EVP_MD_CTX_block_size hc_EVP_MD_CTX_block_size
+#define EVP_MD_CTX_cleanup hc_EVP_MD_CTX_cleanup
+#define EVP_MD_CTX_create hc_EVP_MD_CTX_create
+#define EVP_MD_CTX_init hc_EVP_MD_CTX_init
+#define EVP_MD_CTX_destroy hc_EVP_MD_CTX_destroy
+#define EVP_MD_CTX_md hc_EVP_MD_CTX_md
+#define EVP_MD_CTX_size hc_EVP_MD_CTX_size
+#define EVP_MD_block_size hc_EVP_MD_block_size
+#define EVP_MD_size hc_EVP_MD_size
+#define EVP_aes_128_cbc hc_EVP_aes_128_cbc
+#define EVP_aes_192_cbc hc_EVP_aes_192_cbc
+#define EVP_aes_256_cbc hc_EVP_aes_256_cbc
+#define EVP_hcrypto_aes_128_cbc hc_EVP_hcrypto_aes_128_cbc
+#define EVP_hcrypto_aes_192_cbc hc_EVP_hcrypto_aes_192_cbc
+#define EVP_hcrypto_aes_256_cbc hc_EVP_hcrypto_aes_256_cbc
+#define EVP_hcrypto_aes_128_cts hc_EVP_hcrypto_aes_128_cts
+#define EVP_hcrypto_aes_192_cts hc_EVP_hcrypto_aes_192_cts
+#define EVP_hcrypto_aes_256_cts hc_EVP_hcrypto_aes_256_cts
+#define EVP_des_cbc hc_EVP_des_cbc
+#define EVP_des_ede3_cbc hc_EVP_des_ede3_cbc
+#define EVP_enc_null hc_EVP_enc_null
+#define EVP_md2 hc_EVP_md2
+#define EVP_md4 hc_EVP_md4
+#define EVP_md5 hc_EVP_md5
+#define EVP_md_null hc_EVP_md_null
+#define EVP_rc2_40_cbc hc_EVP_rc2_40_cbc
+#define EVP_rc2_64_cbc hc_EVP_rc2_64_cbc
+#define EVP_rc2_cbc hc_EVP_rc2_cbc
+#define EVP_rc4 hc_EVP_rc4
+#define EVP_rc4_40 hc_EVP_rc4_40
+#define EVP_camellia_128_cbc hc_EVP_camellia_128_cbc
+#define EVP_camellia_192_cbc hc_EVP_camellia_192_cbc
+#define EVP_camellia_256_cbc hc_EVP_camellia_256_cbc
+#define EVP_sha hc_EVP_sha
+#define EVP_sha1 hc_EVP_sha1
+#define EVP_sha256 hc_EVP_sha256
+#define PKCS5_PBKDF2_HMAC_SHA1 hc_PKCS5_PBKDF2_HMAC_SHA1
+#define EVP_BytesToKey hc_EVP_BytesToKey
+#define EVP_get_cipherbyname hc_EVP_get_cipherbyname
+#define OpenSSL_add_all_algorithms hc_OpenSSL_add_all_algorithms
+#define OpenSSL_add_all_algorithms_conf hc_OpenSSL_add_all_algorithms_conf
+#define OpenSSL_add_all_algorithms_noconf hc_OpenSSL_add_all_algorithms_noconf
+#define EVP_CIPHER_CTX_ctrl hc_EVP_CIPHER_CTX_ctrl
+#define EVP_CIPHER_CTX_rand_key hc_EVP_CIPHER_CTX_rand_key
+
+/*
+ *
+ */
+
+typedef struct hc_EVP_MD_CTX EVP_MD_CTX;
+typedef struct hc_evp_pkey EVP_PKEY;
+typedef struct hc_evp_md EVP_MD;
+typedef struct hc_CIPHER EVP_CIPHER;
+typedef struct hc_CIPHER_CTX EVP_CIPHER_CTX;
+
+#define EVP_MAX_IV_LENGTH 16
+#define EVP_MAX_BLOCK_LENGTH 32
+
+#define EVP_MAX_MD_SIZE 64
+
+struct hc_CIPHER {
+ int nid;
+ int block_size;
+ int key_len;
+ int iv_len;
+ unsigned long flags;
+ /* The lowest 3 bits is used as integer field for the mode the
+ * cipher is used in (use EVP_CIPHER.._mode() to extract the
+ * mode). The rest of the flag field is a bitfield.
+ */
+#define EVP_CIPH_STREAM_CIPHER 0
+#define EVP_CIPH_CBC_MODE 2
+#define EVP_CIPH_MODE 0x7
+
+#define EVP_CIPH_ALWAYS_CALL_INIT 0x020
+#define EVP_CIPH_RAND_KEY 0x200
+
+ int (*init)(EVP_CIPHER_CTX*,const unsigned char*,const unsigned char*,int);
+ int (*do_cipher)(EVP_CIPHER_CTX *, unsigned char *,
+ const unsigned char *, unsigned int);
+ int (*cleanup)(EVP_CIPHER_CTX *);
+ int ctx_size;
+ void *set_asn1_parameters;
+ void *get_asn1_parameters;
+ int (*ctrl)(EVP_CIPHER_CTX *, int type, int arg, void *ptr);
+#define EVP_CTRL_RAND_KEY 0x6
+
+ void *app_data;
+};
+
+struct hc_CIPHER_CTX {
+ const EVP_CIPHER *cipher;
+ ENGINE *engine;
+ int encrypt;
+ int buf_len; /* bytes stored in buf for EVP_CipherUpdate */
+ unsigned char oiv[EVP_MAX_IV_LENGTH];
+ unsigned char iv[EVP_MAX_IV_LENGTH];
+ unsigned char buf[EVP_MAX_BLOCK_LENGTH];
+ int num;
+ void *app_data;
+ int key_len;
+ unsigned long flags;
+ void *cipher_data;
+ int final_used;
+ int block_mask;
+ unsigned char final[EVP_MAX_BLOCK_LENGTH];
+};
+
+typedef int (*hc_evp_md_init)(EVP_MD_CTX *);
+typedef int (*hc_evp_md_update)(EVP_MD_CTX *,const void *, size_t);
+typedef int (*hc_evp_md_final)(void *, EVP_MD_CTX *);
+typedef int (*hc_evp_md_cleanup)(EVP_MD_CTX *);
+
+struct hc_evp_md {
+ int hash_size;
+ int block_size;
+ int ctx_size;
+ hc_evp_md_init init;
+ hc_evp_md_update update;
+ hc_evp_md_final final;
+ hc_evp_md_cleanup cleanup;
+};
+
+#if !defined(__GNUC__) && !defined(__attribute__)
+#define __attribute__(x)
+#endif
+
+#ifndef HC_DEPRECATED
+#define HC_DEPRECATED __attribute__((deprecated))
+#endif
+#ifndef HC_DEPRECATED_CRYPTO
+#define HC_DEPRECATED_CRYPTO __attribute__((deprecated))
+#endif
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Avaible crypto algs
+ */
+
+const EVP_MD *EVP_md_null(void);
+const EVP_MD *EVP_md2(void) HC_DEPRECATED_CRYPTO;
+const EVP_MD *EVP_md4(void) HC_DEPRECATED_CRYPTO;
+const EVP_MD *EVP_md5(void) HC_DEPRECATED_CRYPTO;
+const EVP_MD *EVP_sha(void);
+const EVP_MD *EVP_sha1(void);
+const EVP_MD *EVP_sha256(void);
+
+const EVP_CIPHER * EVP_aes_128_cbc(void);
+const EVP_CIPHER * EVP_aes_192_cbc(void);
+const EVP_CIPHER * EVP_aes_256_cbc(void);
+const EVP_CIPHER * EVP_hcrypto_aes_128_cbc(void);
+const EVP_CIPHER * EVP_hcrypto_aes_192_cbc(void);
+const EVP_CIPHER * EVP_hcrypto_aes_256_cbc(void);
+const EVP_CIPHER * EVP_hcrypto_aes_128_cts(void);
+const EVP_CIPHER * EVP_hcrypto_aes_192_cts(void);
+const EVP_CIPHER * EVP_hcrypto_aes_256_cts(void);
+const EVP_CIPHER * EVP_des_cbc(void) HC_DEPRECATED_CRYPTO;
+const EVP_CIPHER * EVP_des_ede3_cbc(void);
+const EVP_CIPHER * EVP_enc_null(void);
+const EVP_CIPHER * EVP_rc2_40_cbc(void) HC_DEPRECATED_CRYPTO;
+const EVP_CIPHER * EVP_rc2_64_cbc(void) HC_DEPRECATED_CRYPTO;
+const EVP_CIPHER * EVP_rc2_cbc(void) HC_DEPRECATED_CRYPTO;
+const EVP_CIPHER * EVP_rc4(void);
+const EVP_CIPHER * EVP_rc4_40(void) HC_DEPRECATED_CRYPTO;
+const EVP_CIPHER * EVP_camellia_128_cbc(void);
+const EVP_CIPHER * EVP_camellia_192_cbc(void);
+const EVP_CIPHER * EVP_camellia_256_cbc(void);
+
+/*
+ *
+ */
+
+size_t EVP_MD_size(const EVP_MD *);
+size_t EVP_MD_block_size(const EVP_MD *);
+
+const EVP_MD *
+ EVP_MD_CTX_md(EVP_MD_CTX *);
+size_t EVP_MD_CTX_size(EVP_MD_CTX *);
+size_t EVP_MD_CTX_block_size(EVP_MD_CTX *);
+
+EVP_MD_CTX *
+ EVP_MD_CTX_create(void);
+void HC_DEPRECATED EVP_MD_CTX_init(EVP_MD_CTX *);
+void EVP_MD_CTX_destroy(EVP_MD_CTX *);
+int HC_DEPRECATED EVP_MD_CTX_cleanup(EVP_MD_CTX *);
+
+int EVP_DigestInit_ex(EVP_MD_CTX *, const EVP_MD *, ENGINE *);
+int EVP_DigestUpdate(EVP_MD_CTX *,const void *, size_t);
+int EVP_DigestFinal_ex(EVP_MD_CTX *, void *, unsigned int *);
+int EVP_Digest(const void *, size_t, void *, unsigned int *,
+ const EVP_MD *, ENGINE *);
+/*
+ *
+ */
+
+const EVP_CIPHER *
+ EVP_get_cipherbyname(const char *);
+
+size_t EVP_CIPHER_block_size(const EVP_CIPHER *);
+size_t EVP_CIPHER_key_length(const EVP_CIPHER *);
+size_t EVP_CIPHER_iv_length(const EVP_CIPHER *);
+
+void EVP_CIPHER_CTX_init(EVP_CIPHER_CTX *);
+int EVP_CIPHER_CTX_cleanup(EVP_CIPHER_CTX *);
+int EVP_CIPHER_CTX_set_key_length(EVP_CIPHER_CTX *, int);
+int EVP_CIPHER_CTX_set_padding(EVP_CIPHER_CTX *, int);
+unsigned long
+ EVP_CIPHER_CTX_flags(const EVP_CIPHER_CTX *);
+int EVP_CIPHER_CTX_mode(const EVP_CIPHER_CTX *);
+
+const EVP_CIPHER *
+ EVP_CIPHER_CTX_cipher(EVP_CIPHER_CTX *);
+size_t EVP_CIPHER_CTX_block_size(const EVP_CIPHER_CTX *);
+size_t EVP_CIPHER_CTX_key_length(const EVP_CIPHER_CTX *);
+size_t EVP_CIPHER_CTX_iv_length(const EVP_CIPHER_CTX *);
+void * EVP_CIPHER_CTX_get_app_data(EVP_CIPHER_CTX *);
+void EVP_CIPHER_CTX_set_app_data(EVP_CIPHER_CTX *, void *);
+
+int EVP_CIPHER_CTX_ctrl(EVP_CIPHER_CTX *, int, int, void *);
+int EVP_CIPHER_CTX_rand_key(EVP_CIPHER_CTX *, void *);
+
+
+int EVP_CipherInit_ex(EVP_CIPHER_CTX *,const EVP_CIPHER *, ENGINE *,
+ const void *, const void *, int);
+int EVP_CipherUpdate(EVP_CIPHER_CTX *, void *, int *, void *, size_t);
+int EVP_CipherFinal_ex(EVP_CIPHER_CTX *, void *, int *);
+
+int EVP_Cipher(EVP_CIPHER_CTX *,void *,const void *,size_t);
+
+int PKCS5_PBKDF2_HMAC_SHA1(const void *, size_t, const void *, size_t,
+ unsigned long, size_t, void *);
+
+int EVP_BytesToKey(const EVP_CIPHER *, const EVP_MD *,
+ const void *, const void *, size_t,
+ unsigned int, void *, void *);
+
+
+/*
+ *
+ */
+
+void OpenSSL_add_all_algorithms(void);
+void OpenSSL_add_all_algorithms_conf(void);
+void OpenSSL_add_all_algorithms_noconf(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* HEIM_EVP_H */
diff --git a/source4/heimdal/lib/hcrypto/hash.h b/source4/heimdal/lib/hcrypto/hash.h
new file mode 100644
index 0000000000..b8d5d45606
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/hash.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 1999 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of KTH nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+/* $Id$ */
+
+/* stuff in common between md4, md5, and sha1 */
+
+#ifndef __hash_h__
+#define __hash_h__
+
+#include <stdlib.h>
+#include <string.h>
+#include <stddef.h>
+#ifdef KRB5
+#include <krb5-types.h>
+#endif
+
+#ifndef min
+#define min(a,b) (((a)>(b))?(b):(a))
+#endif
+
+/* Vector Crays doesn't have a good 32-bit type, or more precisely,
+ int32_t as defined by <bind/bitypes.h> isn't 32 bits, and we don't
+ want to depend in being able to redefine this type. To cope with
+ this we have to clamp the result in some places to [0,2^32); no
+ need to do this on other machines. Did I say this was a mess?
+ */
+
+#ifdef _CRAY
+#define CRAYFIX(X) ((X) & 0xffffffff)
+#else
+#define CRAYFIX(X) (X)
+#endif
+
+static inline uint32_t
+cshift (uint32_t x, unsigned int n)
+{
+ x = CRAYFIX(x);
+ return CRAYFIX((x << n) | (x >> (32 - n)));
+}
+
+#endif /* __hash_h__ */
diff --git a/source4/heimdal/lib/hcrypto/hmac.c b/source4/heimdal/lib/hcrypto/hmac.c
new file mode 100644
index 0000000000..282dc38113
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/hmac.c
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <hmac.h>
+
+void
+HMAC_CTX_init(HMAC_CTX *ctx)
+{
+ memset(ctx, 0, sizeof(*ctx));
+}
+
+void
+HMAC_CTX_cleanup(HMAC_CTX *ctx)
+{
+ if (ctx->buf) {
+ memset(ctx->buf, 0, ctx->key_length);
+ free(ctx->buf);
+ ctx->buf = NULL;
+ }
+ if (ctx->opad) {
+ memset(ctx->ipad, 0, ctx->key_length);
+ free(ctx->opad);
+ ctx->opad = NULL;
+ }
+ if (ctx->ipad) {
+ memset(ctx->ipad, 0, ctx->key_length);
+ free(ctx->ipad);
+ ctx->ipad = NULL;
+ }
+ if (ctx->ctx) {
+ EVP_MD_CTX_destroy(ctx->ctx);
+ ctx->ctx = NULL;
+ }
+}
+
+size_t
+HMAC_size(const HMAC_CTX *ctx)
+{
+ return EVP_MD_size(ctx->md);
+}
+
+void
+HMAC_Init_ex(HMAC_CTX *ctx,
+ const void *key,
+ size_t keylen,
+ const EVP_MD *md,
+ ENGINE *engine)
+{
+ unsigned char *p;
+ size_t i;
+
+ if (ctx->md != md) {
+ ctx->md = md;
+ if (ctx->buf) {
+ memset(ctx->buf, 0, ctx->key_length);
+ free (ctx->buf);
+ }
+ ctx->key_length = EVP_MD_size(ctx->md);
+ ctx->buf = malloc(ctx->key_length);
+ }
+#if 0
+ ctx->engine = engine;
+#endif
+
+ if (keylen > EVP_MD_block_size(ctx->md)) {
+ EVP_Digest(key, keylen, ctx->buf, NULL, ctx->md, engine);
+ key = ctx->buf;
+ keylen = EVP_MD_size(ctx->md);
+ }
+
+ if (ctx->opad) {
+ memset(ctx->opad, 0, ctx->key_length);
+ free(ctx->opad);
+ }
+ if (ctx->ipad) {
+ memset(ctx->ipad, 0, ctx->key_length);
+ free(ctx->ipad);
+ }
+
+ ctx->opad = malloc(EVP_MD_block_size(ctx->md));
+ ctx->ipad = malloc(EVP_MD_block_size(ctx->md));
+ memset(ctx->ipad, 0x36, EVP_MD_block_size(ctx->md));
+ memset(ctx->opad, 0x5c, EVP_MD_block_size(ctx->md));
+
+ for (i = 0, p = ctx->ipad; i < keylen; i++)
+ p[i] ^= ((const unsigned char *)key)[i];
+ for (i = 0, p = ctx->opad; i < keylen; i++)
+ p[i] ^= ((const unsigned char *)key)[i];
+
+ ctx->ctx = EVP_MD_CTX_create();
+
+ EVP_DigestInit_ex(ctx->ctx, ctx->md, ctx->engine);
+ EVP_DigestUpdate(ctx->ctx, ctx->ipad, EVP_MD_block_size(ctx->md));
+}
+
+void
+HMAC_Update(HMAC_CTX *ctx, const void *data, size_t len)
+{
+ EVP_DigestUpdate(ctx->ctx, data, len);
+}
+
+void
+HMAC_Final(HMAC_CTX *ctx, void *md, unsigned int *len)
+{
+ EVP_DigestFinal_ex(ctx->ctx, ctx->buf, NULL);
+
+ EVP_DigestInit_ex(ctx->ctx, ctx->md, ctx->engine);
+ EVP_DigestUpdate(ctx->ctx, ctx->opad, EVP_MD_block_size(ctx->md));
+ EVP_DigestUpdate(ctx->ctx, ctx->buf, ctx->key_length);
+ EVP_DigestFinal_ex(ctx->ctx, md, len);
+}
+
+void *
+HMAC(const EVP_MD *md,
+ const void *key, size_t key_size,
+ const void *data, size_t data_size,
+ void *hash, unsigned int *hash_len)
+{
+ HMAC_CTX ctx;
+
+ HMAC_CTX_init(&ctx);
+ HMAC_Init_ex(&ctx, key, key_size, md, NULL);
+ HMAC_Update(&ctx, data, data_size);
+ HMAC_Final(&ctx, hash, hash_len);
+ HMAC_CTX_cleanup(&ctx);
+ return hash;
+}
diff --git a/source4/heimdal/lib/hcrypto/hmac.h b/source4/heimdal/lib/hcrypto/hmac.h
new file mode 100644
index 0000000000..3ea17a93f0
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/hmac.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef HEIM_HMAC_H
+#define HEIM_HMAC_H 1
+
+#include <hcrypto/evp.h>
+
+/* symbol renaming */
+#define HMAC_CTX_init hc_HMAC_CTX_init
+#define HMAC_CTX_cleanup hc_HMAC_CTX_cleanup
+#define HMAC_size hc_HMAC_size
+#define HMAC_Init_ex hc_HMAC_Init_ex
+#define HMAC_Update hc_HMAC_Update
+#define HMAC_Final hc_HMAC_Final
+#define HMAC hc_HMAC
+
+/*
+ *
+ */
+
+#define HMAC_MAX_MD_CBLOCK 64
+
+typedef struct hc_HMAC_CTX HMAC_CTX;
+
+struct hc_HMAC_CTX {
+ const EVP_MD *md;
+ ENGINE *engine;
+ EVP_MD_CTX *ctx;
+ size_t key_length;
+ void *opad;
+ void *ipad;
+ void *buf;
+};
+
+
+void HMAC_CTX_init(HMAC_CTX *);
+void HMAC_CTX_cleanup(HMAC_CTX *ctx);
+
+size_t HMAC_size(const HMAC_CTX *ctx);
+
+void HMAC_Init_ex(HMAC_CTX *, const void *, size_t,
+ const EVP_MD *, ENGINE *);
+void HMAC_Update(HMAC_CTX *ctx, const void *data, size_t len);
+void HMAC_Final(HMAC_CTX *ctx, void *md, unsigned int *len);
+
+void * HMAC(const EVP_MD *evp_md, const void *key, size_t key_len,
+ const void *data, size_t n, void *md, unsigned int *md_len);
+
+#endif /* HEIM_HMAC_H */
diff --git a/source4/heimdal/lib/hcrypto/imath/LICENSE b/source4/heimdal/lib/hcrypto/imath/LICENSE
new file mode 100644
index 0000000000..5b0104fa1b
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/imath/LICENSE
@@ -0,0 +1,21 @@
+IMath is Copyright © 2002-2008 Michael J. Fromberger
+You may use it subject to the following Licensing Terms:
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/source4/heimdal/lib/hcrypto/imath/imath.c b/source4/heimdal/lib/hcrypto/imath/imath.c
new file mode 100644
index 0000000000..f8e4154cdc
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/imath/imath.c
@@ -0,0 +1,3352 @@
+/*
+ Name: imath.c
+ Purpose: Arbitrary precision integer arithmetic routines.
+ Author: M. J. Fromberger <http://spinning-yarns.org/michael/>
+ Info: $Id: imath.c 645 2008-08-03 04:00:30Z sting $
+
+ Copyright (C) 2002-2007 Michael J. Fromberger, All Rights Reserved.
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation files
+ (the "Software"), to deal in the Software without restriction,
+ including without limitation the rights to use, copy, modify, merge,
+ publish, distribute, sublicense, and/or sell copies of the Software,
+ and to permit persons to whom the Software is furnished to do so,
+ subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+ */
+
+#include "imath.h"
+
+#if DEBUG
+#include <stdio.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <assert.h>
+
+#if DEBUG
+#define static
+#endif
+
+/* {{{ Constants */
+
+const mp_result MP_OK = 0; /* no error, all is well */
+const mp_result MP_FALSE = 0; /* boolean false */
+const mp_result MP_TRUE = -1; /* boolean true */
+const mp_result MP_MEMORY = -2; /* out of memory */
+const mp_result MP_RANGE = -3; /* argument out of range */
+const mp_result MP_UNDEF = -4; /* result undefined */
+const mp_result MP_TRUNC = -5; /* output truncated */
+const mp_result MP_BADARG = -6; /* invalid null argument */
+const mp_result MP_MINERR = -6;
+
+const mp_sign MP_NEG = 1; /* value is strictly negative */
+const mp_sign MP_ZPOS = 0; /* value is non-negative */
+
+static const char *s_unknown_err = "unknown result code";
+static const char *s_error_msg[] = {
+ "error code 0",
+ "boolean true",
+ "out of memory",
+ "argument out of range",
+ "result undefined",
+ "output truncated",
+ "invalid argument",
+ NULL
+};
+
+/* }}} */
+
+/* Argument checking macros
+ Use CHECK() where a return value is required; NRCHECK() elsewhere */
+#define CHECK(TEST) assert(TEST)
+#define NRCHECK(TEST) assert(TEST)
+
+/* {{{ Logarithm table for computing output sizes */
+
+/* The ith entry of this table gives the value of log_i(2).
+
+ An integer value n requires ceil(log_i(n)) digits to be represented
+ in base i. Since it is easy to compute lg(n), by counting bits, we
+ can compute log_i(n) = lg(n) * log_i(2).
+
+ The use of this table eliminates a dependency upon linkage against
+ the standard math libraries.
+ */
+static const double s_log2[] = {
+ 0.000000000, 0.000000000, 1.000000000, 0.630929754, /* 0 1 2 3 */
+ 0.500000000, 0.430676558, 0.386852807, 0.356207187, /* 4 5 6 7 */
+ 0.333333333, 0.315464877, 0.301029996, 0.289064826, /* 8 9 10 11 */
+ 0.278942946, 0.270238154, 0.262649535, 0.255958025, /* 12 13 14 15 */
+ 0.250000000, 0.244650542, 0.239812467, 0.235408913, /* 16 17 18 19 */
+ 0.231378213, 0.227670249, 0.224243824, 0.221064729, /* 20 21 22 23 */
+ 0.218104292, 0.215338279, 0.212746054, 0.210309918, /* 24 25 26 27 */
+ 0.208014598, 0.205846832, 0.203795047, 0.201849087, /* 28 29 30 31 */
+ 0.200000000, 0.198239863, 0.196561632, 0.194959022, /* 32 33 34 35 */
+ 0.193426404, /* 36 */
+};
+
+/* }}} */
+/* {{{ Various macros */
+
+/* Return the number of digits needed to represent a static value */
+#define MP_VALUE_DIGITS(V) \
+((sizeof(V)+(sizeof(mp_digit)-1))/sizeof(mp_digit))
+
+/* Round precision P to nearest word boundary */
+#define ROUND_PREC(P) ((mp_size)(2*(((P)+1)/2)))
+
+/* Set array P of S digits to zero */
+#define ZERO(P, S) \
+do{mp_size i__=(S)*sizeof(mp_digit);mp_digit *p__=(P);memset(p__,0,i__);}while(0)
+
+/* Copy S digits from array P to array Q */
+#define COPY(P, Q, S) \
+do{mp_size i__=(S)*sizeof(mp_digit);mp_digit *p__=(P),*q__=(Q);\
+memcpy(q__,p__,i__);}while(0)
+
+/* Reverse N elements of type T in array A */
+#define REV(T, A, N) \
+do{T *u_=(A),*v_=u_+(N)-1;while(u_<v_){T xch=*u_;*u_++=*v_;*v_--=xch;}}while(0)
+
+#define CLAMP(Z) \
+do{mp_int z_=(Z);mp_size uz_=MP_USED(z_);mp_digit *dz_=MP_DIGITS(z_)+uz_-1;\
+while(uz_ > 1 && (*dz_-- == 0)) --uz_;MP_USED(z_)=uz_;}while(0)
+
+/* Select min/max. Do not provide expressions for which multiple
+ evaluation would be problematic, e.g. x++ */
+#define MIN(A, B) ((B)<(A)?(B):(A))
+#define MAX(A, B) ((B)>(A)?(B):(A))
+
+/* Exchange lvalues A and B of type T, e.g.
+ SWAP(int, x, y) where x and y are variables of type int. */
+#define SWAP(T, A, B) do{T t_=(A);A=(B);B=t_;}while(0)
+
+/* Used to set up and access simple temp stacks within functions. */
+#define TEMP(K) (temp + (K))
+#define SETUP(E, C) \
+do{if((res = (E)) != MP_OK) goto CLEANUP; ++(C);}while(0)
+
+/* Compare value to zero. */
+#define CMPZ(Z) \
+(((Z)->used==1&&(Z)->digits[0]==0)?0:((Z)->sign==MP_NEG)?-1:1)
+
+/* Multiply X by Y into Z, ignoring signs. Requires that Z have
+ enough storage preallocated to hold the result. */
+#define UMUL(X, Y, Z) \
+do{mp_size ua_=MP_USED(X),ub_=MP_USED(Y);mp_size o_=ua_+ub_;\
+ZERO(MP_DIGITS(Z),o_);\
+(void) s_kmul(MP_DIGITS(X),MP_DIGITS(Y),MP_DIGITS(Z),ua_,ub_);\
+MP_USED(Z)=o_;CLAMP(Z);}while(0)
+
+/* Square X into Z. Requires that Z have enough storage to hold the
+ result. */
+#define USQR(X, Z) \
+do{mp_size ua_=MP_USED(X),o_=ua_+ua_;ZERO(MP_DIGITS(Z),o_);\
+(void) s_ksqr(MP_DIGITS(X),MP_DIGITS(Z),ua_);MP_USED(Z)=o_;CLAMP(Z);}while(0)
+
+#define UPPER_HALF(W) ((mp_word)((W) >> MP_DIGIT_BIT))
+#define LOWER_HALF(W) ((mp_digit)(W))
+#define HIGH_BIT_SET(W) ((W) >> (MP_WORD_BIT - 1))
+#define ADD_WILL_OVERFLOW(W, V) ((MP_WORD_MAX - (V)) < (W))
+
+/* }}} */
+/* {{{ Default configuration settings */
+
+/* Default number of digits allocated to a new mp_int */
+#if IMATH_TEST
+mp_size default_precision = MP_DEFAULT_PREC;
+#else
+static const mp_size default_precision = MP_DEFAULT_PREC;
+#endif
+
+/* Minimum number of digits to invoke recursive multiply */
+#if IMATH_TEST
+mp_size multiply_threshold = MP_MULT_THRESH;
+#else
+static const mp_size multiply_threshold = MP_MULT_THRESH;
+#endif
+
+/* }}} */
+
+/* Allocate a buffer of (at least) num digits, or return
+ NULL if that couldn't be done. */
+static mp_digit *s_alloc(mp_size num);
+
+/* Release a buffer of digits allocated by s_alloc(). */
+static void s_free(void *ptr);
+
+/* Insure that z has at least min digits allocated, resizing if
+ necessary. Returns true if successful, false if out of memory. */
+static int s_pad(mp_int z, mp_size min);
+
+/* Fill in a "fake" mp_int on the stack with a given value */
+static void s_fake(mp_int z, mp_small value, mp_digit vbuf[]);
+
+/* Compare two runs of digits of given length, returns <0, 0, >0 */
+static int s_cdig(mp_digit *da, mp_digit *db, mp_size len);
+
+/* Pack the unsigned digits of v into array t */
+static int s_vpack(mp_small v, mp_digit t[]);
+
+/* Compare magnitudes of a and b, returns <0, 0, >0 */
+static int s_ucmp(mp_int a, mp_int b);
+
+/* Compare magnitudes of a and v, returns <0, 0, >0 */
+static int s_vcmp(mp_int a, mp_small v);
+
+/* Unsigned magnitude addition; assumes dc is big enough.
+ Carry out is returned (no memory allocated). */
+static mp_digit s_uadd(mp_digit *da, mp_digit *db, mp_digit *dc,
+ mp_size size_a, mp_size size_b);
+
+/* Unsigned magnitude subtraction. Assumes dc is big enough. */
+static void s_usub(mp_digit *da, mp_digit *db, mp_digit *dc,
+ mp_size size_a, mp_size size_b);
+
+/* Unsigned recursive multiplication. Assumes dc is big enough. */
+static int s_kmul(mp_digit *da, mp_digit *db, mp_digit *dc,
+ mp_size size_a, mp_size size_b);
+
+/* Unsigned magnitude multiplication. Assumes dc is big enough. */
+static void s_umul(mp_digit *da, mp_digit *db, mp_digit *dc,
+ mp_size size_a, mp_size size_b);
+
+/* Unsigned recursive squaring. Assumes dc is big enough. */
+static int s_ksqr(mp_digit *da, mp_digit *dc, mp_size size_a);
+
+/* Unsigned magnitude squaring. Assumes dc is big enough. */
+static void s_usqr(mp_digit *da, mp_digit *dc, mp_size size_a);
+
+/* Single digit addition. Assumes a is big enough. */
+static void s_dadd(mp_int a, mp_digit b);
+
+/* Single digit multiplication. Assumes a is big enough. */
+static void s_dmul(mp_int a, mp_digit b);
+
+/* Single digit multiplication on buffers; assumes dc is big enough. */
+static void s_dbmul(mp_digit *da, mp_digit b, mp_digit *dc,
+ mp_size size_a);
+
+/* Single digit division. Replaces a with the quotient,
+ returns the remainder. */
+static mp_digit s_ddiv(mp_int a, mp_digit b);
+
+/* Quick division by a power of 2, replaces z (no allocation) */
+static void s_qdiv(mp_int z, mp_size p2);
+
+/* Quick remainder by a power of 2, replaces z (no allocation) */
+static void s_qmod(mp_int z, mp_size p2);
+
+/* Quick multiplication by a power of 2, replaces z.
+ Allocates if necessary; returns false in case this fails. */
+static int s_qmul(mp_int z, mp_size p2);
+
+/* Quick subtraction from a power of 2, replaces z.
+ Allocates if necessary; returns false in case this fails. */
+static int s_qsub(mp_int z, mp_size p2);
+
+/* Return maximum k such that 2^k divides z. */
+static int s_dp2k(mp_int z);
+
+/* Return k >= 0 such that z = 2^k, or -1 if there is no such k. */
+static int s_isp2(mp_int z);
+
+/* Set z to 2^k. May allocate; returns false in case this fails. */
+static int s_2expt(mp_int z, mp_small k);
+
+/* Normalize a and b for division, returns normalization constant */
+static int s_norm(mp_int a, mp_int b);
+
+/* Compute constant mu for Barrett reduction, given modulus m, result
+ replaces z, m is untouched. */
+static mp_result s_brmu(mp_int z, mp_int m);
+
+/* Reduce a modulo m, using Barrett's algorithm. */
+static int s_reduce(mp_int x, mp_int m, mp_int mu, mp_int q1, mp_int q2);
+
+/* Modular exponentiation, using Barrett reduction */
+static mp_result s_embar(mp_int a, mp_int b, mp_int m, mp_int mu, mp_int c);
+
+/* Unsigned magnitude division. Assumes |a| > |b|. Allocates
+ temporaries; overwrites a with quotient, b with remainder. */
+static mp_result s_udiv(mp_int a, mp_int b);
+
+/* Compute the number of digits in radix r required to represent the
+ given value. Does not account for sign flags, terminators, etc. */
+static int s_outlen(mp_int z, mp_size r);
+
+/* Guess how many digits of precision will be needed to represent a
+ radix r value of the specified number of digits. Returns a value
+ guaranteed to be no smaller than the actual number required. */
+static mp_size s_inlen(int len, mp_size r);
+
+/* Convert a character to a digit value in radix r, or
+ -1 if out of range */
+static int s_ch2val(char c, int r);
+
+/* Convert a digit value to a character */
+static char s_val2ch(int v, int caps);
+
+/* Take 2's complement of a buffer in place */
+static void s_2comp(unsigned char *buf, int len);
+
+/* Convert a value to binary, ignoring sign. On input, *limpos is the
+ bound on how many bytes should be written to buf; on output, *limpos
+ is set to the number of bytes actually written. */
+static mp_result s_tobin(mp_int z, unsigned char *buf, int *limpos, int pad);
+
+#if DEBUG
+/* Dump a representation of the mp_int to standard output */
+void s_print(char *tag, mp_int z);
+void s_print_buf(char *tag, mp_digit *buf, mp_size num);
+#endif
+
+/* {{{ mp_int_init(z) */
+
+mp_result mp_int_init(mp_int z)
+{
+ if(z == NULL)
+ return MP_BADARG;
+
+ z->single = 0;
+ z->digits = &(z->single);
+ z->alloc = 1;
+ z->used = 1;
+ z->sign = MP_ZPOS;
+
+ return MP_OK;
+}
+
+/* }}} */
+
+/* {{{ mp_int_alloc() */
+
+mp_int mp_int_alloc(void)
+{
+ mp_int out = malloc(sizeof(mpz_t));
+
+ if(out != NULL)
+ mp_int_init(out);
+
+ return out;
+}
+
+/* }}} */
+
+/* {{{ mp_int_init_size(z, prec) */
+
+mp_result mp_int_init_size(mp_int z, mp_size prec)
+{
+ CHECK(z != NULL);
+
+ if(prec == 0)
+ prec = default_precision;
+ else if(prec == 1)
+ return mp_int_init(z);
+ else
+ prec = (mp_size) ROUND_PREC(prec);
+
+ if((MP_DIGITS(z) = s_alloc(prec)) == NULL)
+ return MP_MEMORY;
+
+ z->digits[0] = 0;
+ MP_USED(z) = 1;
+ MP_ALLOC(z) = prec;
+ MP_SIGN(z) = MP_ZPOS;
+
+ return MP_OK;
+}
+
+/* }}} */
+
+/* {{{ mp_int_init_copy(z, old) */
+
+mp_result mp_int_init_copy(mp_int z, mp_int old)
+{
+ mp_result res;
+ mp_size uold;
+
+ CHECK(z != NULL && old != NULL);
+
+ uold = MP_USED(old);
+ if(uold == 1) {
+ mp_int_init(z);
+ }
+ else {
+ mp_size target = MAX(uold, default_precision);
+
+ if((res = mp_int_init_size(z, target)) != MP_OK)
+ return res;
+ }
+
+ MP_USED(z) = uold;
+ MP_SIGN(z) = MP_SIGN(old);
+ COPY(MP_DIGITS(old), MP_DIGITS(z), uold);
+
+ return MP_OK;
+}
+
+/* }}} */
+
+/* {{{ mp_int_init_value(z, value) */
+
+mp_result mp_int_init_value(mp_int z, mp_small value)
+{
+ mpz_t vtmp;
+ mp_digit vbuf[MP_VALUE_DIGITS(value)];
+
+ s_fake(&vtmp, value, vbuf);
+ return mp_int_init_copy(z, &vtmp);
+}
+
+/* }}} */
+
+/* {{{ mp_int_set_value(z, value) */
+
+mp_result mp_int_set_value(mp_int z, mp_small value)
+{
+ mpz_t vtmp;
+ mp_digit vbuf[MP_VALUE_DIGITS(value)];
+
+ s_fake(&vtmp, value, vbuf);
+ return mp_int_copy(&vtmp, z);
+}
+
+/* }}} */
+
+/* {{{ mp_int_clear(z) */
+
+void mp_int_clear(mp_int z)
+{
+ if(z == NULL)
+ return;
+
+ if(MP_DIGITS(z) != NULL) {
+ if((void *) MP_DIGITS(z) != (void *) z)
+ s_free(MP_DIGITS(z));
+
+ MP_DIGITS(z) = NULL;
+ }
+}
+
+/* }}} */
+
+/* {{{ mp_int_free(z) */
+
+void mp_int_free(mp_int z)
+{
+ NRCHECK(z != NULL);
+
+ mp_int_clear(z);
+ free(z); /* note: NOT s_free() */
+}
+
+/* }}} */
+
+/* {{{ mp_int_copy(a, c) */
+
+mp_result mp_int_copy(mp_int a, mp_int c)
+{
+ CHECK(a != NULL && c != NULL);
+
+ if(a != c) {
+ mp_size ua = MP_USED(a);
+ mp_digit *da, *dc;
+
+ if(!s_pad(c, ua))
+ return MP_MEMORY;
+
+ da = MP_DIGITS(a); dc = MP_DIGITS(c);
+ COPY(da, dc, ua);
+
+ MP_USED(c) = ua;
+ MP_SIGN(c) = MP_SIGN(a);
+ }
+
+ return MP_OK;
+}
+
+/* }}} */
+
+/* {{{ mp_int_swap(a, c) */
+
+void mp_int_swap(mp_int a, mp_int c)
+{
+ if(a != c) {
+ mpz_t tmp = *a;
+
+ *a = *c;
+ *c = tmp;
+ }
+}
+
+/* }}} */
+
+/* {{{ mp_int_zero(z) */
+
+void mp_int_zero(mp_int z)
+{
+ NRCHECK(z != NULL);
+
+ z->digits[0] = 0;
+ MP_USED(z) = 1;
+ MP_SIGN(z) = MP_ZPOS;
+}
+
+/* }}} */
+
+/* {{{ mp_int_abs(a, c) */
+
+mp_result mp_int_abs(mp_int a, mp_int c)
+{
+ mp_result res;
+
+ CHECK(a != NULL && c != NULL);
+
+ if((res = mp_int_copy(a, c)) != MP_OK)
+ return res;
+
+ MP_SIGN(c) = MP_ZPOS;
+ return MP_OK;
+}
+
+/* }}} */
+
+/* {{{ mp_int_neg(a, c) */
+
+mp_result mp_int_neg(mp_int a, mp_int c)
+{
+ mp_result res;
+
+ CHECK(a != NULL && c != NULL);
+
+ if((res = mp_int_copy(a, c)) != MP_OK)
+ return res;
+
+ if(CMPZ(c) != 0)
+ MP_SIGN(c) = 1 - MP_SIGN(a);
+
+ return MP_OK;
+}
+
+/* }}} */
+
+/* {{{ mp_int_add(a, b, c) */
+
+mp_result mp_int_add(mp_int a, mp_int b, mp_int c)
+{
+ mp_size ua, ub, uc, max;
+
+ CHECK(a != NULL && b != NULL && c != NULL);
+
+ ua = MP_USED(a); ub = MP_USED(b); uc = MP_USED(c);
+ max = MAX(ua, ub);
+
+ if(MP_SIGN(a) == MP_SIGN(b)) {
+ /* Same sign -- add magnitudes, preserve sign of addends */
+ mp_digit carry;
+
+ if(!s_pad(c, max))
+ return MP_MEMORY;
+
+ carry = s_uadd(MP_DIGITS(a), MP_DIGITS(b), MP_DIGITS(c), ua, ub);
+ uc = max;
+
+ if(carry) {
+ if(!s_pad(c, max + 1))
+ return MP_MEMORY;
+
+ c->digits[max] = carry;
+ ++uc;
+ }
+
+ MP_USED(c) = uc;
+ MP_SIGN(c) = MP_SIGN(a);
+
+ }
+ else {
+ /* Different signs -- subtract magnitudes, preserve sign of greater */
+ mp_int x, y;
+ int cmp = s_ucmp(a, b); /* magnitude comparision, sign ignored */
+
+ /* Set x to max(a, b), y to min(a, b) to simplify later code.
+ A special case yields zero for equal magnitudes.
+ */
+ if(cmp == 0) {
+ mp_int_zero(c);
+ return MP_OK;
+ }
+ else if(cmp < 0) {
+ x = b; y = a;
+ }
+ else {
+ x = a; y = b;
+ }
+
+ if(!s_pad(c, MP_USED(x)))
+ return MP_MEMORY;
+
+ /* Subtract smaller from larger */
+ s_usub(MP_DIGITS(x), MP_DIGITS(y), MP_DIGITS(c), MP_USED(x), MP_USED(y));
+ MP_USED(c) = MP_USED(x);
+ CLAMP(c);
+
+ /* Give result the sign of the larger */
+ MP_SIGN(c) = MP_SIGN(x);
+ }
+
+ return MP_OK;
+}
+
+/* }}} */
+
+/* {{{ mp_int_add_value(a, value, c) */
+
+mp_result mp_int_add_value(mp_int a, mp_small value, mp_int c)
+{
+ mpz_t vtmp;
+ mp_digit vbuf[MP_VALUE_DIGITS(value)];
+
+ s_fake(&vtmp, value, vbuf);
+
+ return mp_int_add(a, &vtmp, c);
+}
+
+/* }}} */
+
+/* {{{ mp_int_sub(a, b, c) */
+
+mp_result mp_int_sub(mp_int a, mp_int b, mp_int c)
+{
+ mp_size ua, ub, uc, max;
+
+ CHECK(a != NULL && b != NULL && c != NULL);
+
+ ua = MP_USED(a); ub = MP_USED(b); uc = MP_USED(c);
+ max = MAX(ua, ub);
+
+ if(MP_SIGN(a) != MP_SIGN(b)) {
+ /* Different signs -- add magnitudes and keep sign of a */
+ mp_digit carry;
+
+ if(!s_pad(c, max))
+ return MP_MEMORY;
+
+ carry = s_uadd(MP_DIGITS(a), MP_DIGITS(b), MP_DIGITS(c), ua, ub);
+ uc = max;
+
+ if(carry) {
+ if(!s_pad(c, max + 1))
+ return MP_MEMORY;
+
+ c->digits[max] = carry;
+ ++uc;
+ }
+
+ MP_USED(c) = uc;
+ MP_SIGN(c) = MP_SIGN(a);
+
+ }
+ else {
+ /* Same signs -- subtract magnitudes */
+ mp_int x, y;
+ mp_sign osign;
+ int cmp = s_ucmp(a, b);
+
+ if(!s_pad(c, max))
+ return MP_MEMORY;
+
+ if(cmp >= 0) {
+ x = a; y = b; osign = MP_ZPOS;
+ }
+ else {
+ x = b; y = a; osign = MP_NEG;
+ }
+
+ if(MP_SIGN(a) == MP_NEG && cmp != 0)
+ osign = 1 - osign;
+
+ s_usub(MP_DIGITS(x), MP_DIGITS(y), MP_DIGITS(c), MP_USED(x), MP_USED(y));
+ MP_USED(c) = MP_USED(x);
+ CLAMP(c);
+
+ MP_SIGN(c) = osign;
+ }
+
+ return MP_OK;
+}
+
+/* }}} */
+
+/* {{{ mp_int_sub_value(a, value, c) */
+
+mp_result mp_int_sub_value(mp_int a, mp_small value, mp_int c)
+{
+ mpz_t vtmp;
+ mp_digit vbuf[MP_VALUE_DIGITS(value)];
+
+ s_fake(&vtmp, value, vbuf);
+
+ return mp_int_sub(a, &vtmp, c);
+}
+
+/* }}} */
+
+/* {{{ mp_int_mul(a, b, c) */
+
+mp_result mp_int_mul(mp_int a, mp_int b, mp_int c)
+{
+ mp_digit *out;
+ mp_size osize, ua, ub, p = 0;
+ mp_sign osign;
+
+ CHECK(a != NULL && b != NULL && c != NULL);
+
+ /* If either input is zero, we can shortcut multiplication */
+ if(mp_int_compare_zero(a) == 0 || mp_int_compare_zero(b) == 0) {
+ mp_int_zero(c);
+ return MP_OK;
+ }
+
+ /* Output is positive if inputs have same sign, otherwise negative */
+ osign = (MP_SIGN(a) == MP_SIGN(b)) ? MP_ZPOS : MP_NEG;
+
+ /* If the output is not identical to any of the inputs, we'll write
+ the results directly; otherwise, allocate a temporary space. */
+ ua = MP_USED(a); ub = MP_USED(b);
+ osize = MAX(ua, ub);
+ osize = 4 * ((osize + 1) / 2);
+
+ if(c == a || c == b) {
+ p = ROUND_PREC(osize);
+ p = MAX(p, default_precision);
+
+ if((out = s_alloc(p)) == NULL)
+ return MP_MEMORY;
+ }
+ else {
+ if(!s_pad(c, osize))
+ return MP_MEMORY;
+
+ out = MP_DIGITS(c);
+ }
+ ZERO(out, osize);
+
+ if(!s_kmul(MP_DIGITS(a), MP_DIGITS(b), out, ua, ub))
+ return MP_MEMORY;
+
+ /* If we allocated a new buffer, get rid of whatever memory c was
+ already using, and fix up its fields to reflect that.
+ */
+ if(out != MP_DIGITS(c)) {
+ if((void *) MP_DIGITS(c) != (void *) c)
+ s_free(MP_DIGITS(c));
+ MP_DIGITS(c) = out;
+ MP_ALLOC(c) = p;
+ }
+
+ MP_USED(c) = osize; /* might not be true, but we'll fix it ... */
+ CLAMP(c); /* ... right here */
+ MP_SIGN(c) = osign;
+
+ return MP_OK;
+}
+
+/* }}} */
+
+/* {{{ mp_int_mul_value(a, value, c) */
+
+mp_result mp_int_mul_value(mp_int a, mp_small value, mp_int c)
+{
+ mpz_t vtmp;
+ mp_digit vbuf[MP_VALUE_DIGITS(value)];
+
+ s_fake(&vtmp, value, vbuf);
+
+ return mp_int_mul(a, &vtmp, c);
+}
+
+/* }}} */
+
+/* {{{ mp_int_mul_pow2(a, p2, c) */
+
+mp_result mp_int_mul_pow2(mp_int a, mp_small p2, mp_int c)
+{
+ mp_result res;
+ CHECK(a != NULL && c != NULL && p2 >= 0);
+
+ if((res = mp_int_copy(a, c)) != MP_OK)
+ return res;
+
+ if(s_qmul(c, (mp_size) p2))
+ return MP_OK;
+ else
+ return MP_MEMORY;
+}
+
+/* }}} */
+
+/* {{{ mp_int_sqr(a, c) */
+
+mp_result mp_int_sqr(mp_int a, mp_int c)
+{
+ mp_digit *out;
+ mp_size osize, p = 0;
+
+ CHECK(a != NULL && c != NULL);
+
+ /* Get a temporary buffer big enough to hold the result */
+ osize = (mp_size) 4 * ((MP_USED(a) + 1) / 2);
+ if(a == c) {
+ p = ROUND_PREC(osize);
+ p = MAX(p, default_precision);
+
+ if((out = s_alloc(p)) == NULL)
+ return MP_MEMORY;
+ }
+ else {
+ if(!s_pad(c, osize))
+ return MP_MEMORY;
+
+ out = MP_DIGITS(c);
+ }
+ ZERO(out, osize);
+
+ s_ksqr(MP_DIGITS(a), out, MP_USED(a));
+
+ /* Get rid of whatever memory c was already using, and fix up its
+ fields to reflect the new digit array it's using
+ */
+ if(out != MP_DIGITS(c)) {
+ if((void *) MP_DIGITS(c) != (void *) c)
+ s_free(MP_DIGITS(c));
+ MP_DIGITS(c) = out;
+ MP_ALLOC(c) = p;
+ }
+
+ MP_USED(c) = osize; /* might not be true, but we'll fix it ... */
+ CLAMP(c); /* ... right here */
+ MP_SIGN(c) = MP_ZPOS;
+
+ return MP_OK;
+}
+
+/* }}} */
+
+/* {{{ mp_int_div(a, b, q, r) */
+
+mp_result mp_int_div(mp_int a, mp_int b, mp_int q, mp_int r)
+{
+ int cmp, last = 0, lg;
+ mp_result res = MP_OK;
+ mpz_t temp[2];
+ mp_int qout, rout;
+ mp_sign sa = MP_SIGN(a), sb = MP_SIGN(b);
+
+ CHECK(a != NULL && b != NULL && q != r);
+
+ if(CMPZ(b) == 0)
+ return MP_UNDEF;
+ else if((cmp = s_ucmp(a, b)) < 0) {
+ /* If |a| < |b|, no division is required:
+ q = 0, r = a
+ */
+ if(r && (res = mp_int_copy(a, r)) != MP_OK)
+ return res;
+
+ if(q)
+ mp_int_zero(q);
+
+ return MP_OK;
+ }
+ else if(cmp == 0) {
+ /* If |a| = |b|, no division is required:
+ q = 1 or -1, r = 0
+ */
+ if(r)
+ mp_int_zero(r);
+
+ if(q) {
+ mp_int_zero(q);
+ q->digits[0] = 1;
+
+ if(sa != sb)
+ MP_SIGN(q) = MP_NEG;
+ }
+
+ return MP_OK;
+ }
+
+ /* When |a| > |b|, real division is required. We need someplace to
+ store quotient and remainder, but q and r are allowed to be NULL
+ or to overlap with the inputs.
+ */
+ if((lg = s_isp2(b)) < 0) {
+ if(q && b != q) {
+ if((res = mp_int_copy(a, q)) != MP_OK)
+ goto CLEANUP;
+ else
+ qout = q;
+ }
+ else {
+ qout = TEMP(last);
+ SETUP(mp_int_init_copy(TEMP(last), a), last);
+ }
+
+ if(r && a != r) {
+ if((res = mp_int_copy(b, r)) != MP_OK)
+ goto CLEANUP;
+ else
+ rout = r;
+ }
+ else {
+ rout = TEMP(last);
+ SETUP(mp_int_init_copy(TEMP(last), b), last);
+ }
+
+ if((res = s_udiv(qout, rout)) != MP_OK) goto CLEANUP;
+ }
+ else {
+ if(q && (res = mp_int_copy(a, q)) != MP_OK) goto CLEANUP;
+ if(r && (res = mp_int_copy(a, r)) != MP_OK) goto CLEANUP;
+
+ if(q) s_qdiv(q, (mp_size) lg); qout = q;
+ if(r) s_qmod(r, (mp_size) lg); rout = r;
+ }
+
+ /* Recompute signs for output */
+ if(rout) {
+ MP_SIGN(rout) = sa;
+ if(CMPZ(rout) == 0)
+ MP_SIGN(rout) = MP_ZPOS;
+ }
+ if(qout) {
+ MP_SIGN(qout) = (sa == sb) ? MP_ZPOS : MP_NEG;
+ if(CMPZ(qout) == 0)
+ MP_SIGN(qout) = MP_ZPOS;
+ }
+
+ if(q && (res = mp_int_copy(qout, q)) != MP_OK) goto CLEANUP;
+ if(r && (res = mp_int_copy(rout, r)) != MP_OK) goto CLEANUP;
+
+ CLEANUP:
+ while(--last >= 0)
+ mp_int_clear(TEMP(last));
+
+ return res;
+}
+
+/* }}} */
+
+/* {{{ mp_int_mod(a, m, c) */
+
+mp_result mp_int_mod(mp_int a, mp_int m, mp_int c)
+{
+ mp_result res;
+ mpz_t tmp;
+ mp_int out;
+
+ if(m == c) {
+ mp_int_init(&tmp);
+ out = &tmp;
+ }
+ else {
+ out = c;
+ }
+
+ if((res = mp_int_div(a, m, NULL, out)) != MP_OK)
+ goto CLEANUP;
+
+ if(CMPZ(out) < 0)
+ res = mp_int_add(out, m, c);
+ else
+ res = mp_int_copy(out, c);
+
+ CLEANUP:
+ if(out != c)
+ mp_int_clear(&tmp);
+
+ return res;
+}
+
+/* }}} */
+
+/* {{{ mp_int_div_value(a, value, q, r) */
+
+mp_result mp_int_div_value(mp_int a, mp_small value, mp_int q, mp_small *r)
+{
+ mpz_t vtmp, rtmp;
+ mp_digit vbuf[MP_VALUE_DIGITS(value)];
+ mp_result res;
+
+ mp_int_init(&rtmp);
+ s_fake(&vtmp, value, vbuf);
+
+ if((res = mp_int_div(a, &vtmp, q, &rtmp)) != MP_OK)
+ goto CLEANUP;
+
+ if(r)
+ (void) mp_int_to_int(&rtmp, r); /* can't fail */
+
+ CLEANUP:
+ mp_int_clear(&rtmp);
+ return res;
+}
+
+/* }}} */
+
+/* {{{ mp_int_div_pow2(a, p2, q, r) */
+
+mp_result mp_int_div_pow2(mp_int a, mp_small p2, mp_int q, mp_int r)
+{
+ mp_result res = MP_OK;
+
+ CHECK(a != NULL && p2 >= 0 && q != r);
+
+ if(q != NULL && (res = mp_int_copy(a, q)) == MP_OK)
+ s_qdiv(q, (mp_size) p2);
+
+ if(res == MP_OK && r != NULL && (res = mp_int_copy(a, r)) == MP_OK)
+ s_qmod(r, (mp_size) p2);
+
+ return res;
+}
+
+/* }}} */
+
+/* {{{ mp_int_expt(a, b, c) */
+
+mp_result mp_int_expt(mp_int a, mp_small b, mp_int c)
+{
+ mpz_t t;
+ mp_result res;
+ unsigned int v = abs(b);
+
+ CHECK(b >= 0 && c != NULL);
+
+ if((res = mp_int_init_copy(&t, a)) != MP_OK)
+ return res;
+
+ (void) mp_int_set_value(c, 1);
+ while(v != 0) {
+ if(v & 1) {
+ if((res = mp_int_mul(c, &t, c)) != MP_OK)
+ goto CLEANUP;
+ }
+
+ v >>= 1;
+ if(v == 0) break;
+
+ if((res = mp_int_sqr(&t, &t)) != MP_OK)
+ goto CLEANUP;
+ }
+
+ CLEANUP:
+ mp_int_clear(&t);
+ return res;
+}
+
+/* }}} */
+
+/* {{{ mp_int_expt_value(a, b, c) */
+
+mp_result mp_int_expt_value(mp_small a, mp_small b, mp_int c)
+{
+ mpz_t t;
+ mp_result res;
+ unsigned int v = abs(b);
+
+ CHECK(b >= 0 && c != NULL);
+
+ if((res = mp_int_init_value(&t, a)) != MP_OK)
+ return res;
+
+ (void) mp_int_set_value(c, 1);
+ while(v != 0) {
+ if(v & 1) {
+ if((res = mp_int_mul(c, &t, c)) != MP_OK)
+ goto CLEANUP;
+ }
+
+ v >>= 1;
+ if(v == 0) break;
+
+ if((res = mp_int_sqr(&t, &t)) != MP_OK)
+ goto CLEANUP;
+ }
+
+ CLEANUP:
+ mp_int_clear(&t);
+ return res;
+}
+
+/* }}} */
+
+/* {{{ mp_int_compare(a, b) */
+
+int mp_int_compare(mp_int a, mp_int b)
+{
+ mp_sign sa;
+
+ CHECK(a != NULL && b != NULL);
+
+ sa = MP_SIGN(a);
+ if(sa == MP_SIGN(b)) {
+ int cmp = s_ucmp(a, b);
+
+ /* If they're both zero or positive, the normal comparison
+ applies; if both negative, the sense is reversed. */
+ if(sa == MP_ZPOS)
+ return cmp;
+ else
+ return -cmp;
+
+ }
+ else {
+ if(sa == MP_ZPOS)
+ return 1;
+ else
+ return -1;
+ }
+}
+
+/* }}} */
+
+/* {{{ mp_int_compare_unsigned(a, b) */
+
+int mp_int_compare_unsigned(mp_int a, mp_int b)
+{
+ NRCHECK(a != NULL && b != NULL);
+
+ return s_ucmp(a, b);
+}
+
+/* }}} */
+
+/* {{{ mp_int_compare_zero(z) */
+
+int mp_int_compare_zero(mp_int z)
+{
+ NRCHECK(z != NULL);
+
+ if(MP_USED(z) == 1 && z->digits[0] == 0)
+ return 0;
+ else if(MP_SIGN(z) == MP_ZPOS)
+ return 1;
+ else
+ return -1;
+}
+
+/* }}} */
+
+/* {{{ mp_int_compare_value(z, value) */
+
+int mp_int_compare_value(mp_int z, mp_small value)
+{
+ mp_sign vsign = (value < 0) ? MP_NEG : MP_ZPOS;
+ int cmp;
+
+ CHECK(z != NULL);
+
+ if(vsign == MP_SIGN(z)) {
+ cmp = s_vcmp(z, value);
+
+ if(vsign == MP_ZPOS)
+ return cmp;
+ else
+ return -cmp;
+ }
+ else {
+ if(value < 0)
+ return 1;
+ else
+ return -1;
+ }
+}
+
+/* }}} */
+
+/* {{{ mp_int_exptmod(a, b, m, c) */
+
+mp_result mp_int_exptmod(mp_int a, mp_int b, mp_int m, mp_int c)
+{
+ mp_result res;
+ mp_size um;
+ mpz_t temp[3];
+ mp_int s;
+ int last = 0;
+
+ CHECK(a != NULL && b != NULL && c != NULL && m != NULL);
+
+ /* Zero moduli and negative exponents are not considered. */
+ if(CMPZ(m) == 0)
+ return MP_UNDEF;
+ if(CMPZ(b) < 0)
+ return MP_RANGE;
+
+ um = MP_USED(m);
+ SETUP(mp_int_init_size(TEMP(0), 2 * um), last);
+ SETUP(mp_int_init_size(TEMP(1), 2 * um), last);
+
+ if(c == b || c == m) {
+ SETUP(mp_int_init_size(TEMP(2), 2 * um), last);
+ s = TEMP(2);
+ }
+ else {
+ s = c;
+ }
+
+ if((res = mp_int_mod(a, m, TEMP(0))) != MP_OK) goto CLEANUP;
+
+ if((res = s_brmu(TEMP(1), m)) != MP_OK) goto CLEANUP;
+
+ if((res = s_embar(TEMP(0), b, m, TEMP(1), s)) != MP_OK)
+ goto CLEANUP;
+
+ res = mp_int_copy(s, c);
+
+ CLEANUP:
+ while(--last >= 0)
+ mp_int_clear(TEMP(last));
+
+ return res;
+}
+
+/* }}} */
+
+/* {{{ mp_int_exptmod_evalue(a, value, m, c) */
+
+mp_result mp_int_exptmod_evalue(mp_int a, mp_small value, mp_int m, mp_int c)
+{
+ mpz_t vtmp;
+ mp_digit vbuf[MP_VALUE_DIGITS(value)];
+
+ s_fake(&vtmp, value, vbuf);
+
+ return mp_int_exptmod(a, &vtmp, m, c);
+}
+
+/* }}} */
+
+/* {{{ mp_int_exptmod_bvalue(v, b, m, c) */
+
+mp_result mp_int_exptmod_bvalue(mp_small value, mp_int b,
+ mp_int m, mp_int c)
+{
+ mpz_t vtmp;
+ mp_digit vbuf[MP_VALUE_DIGITS(value)];
+
+ s_fake(&vtmp, value, vbuf);
+
+ return mp_int_exptmod(&vtmp, b, m, c);
+}
+
+/* }}} */
+
+/* {{{ mp_int_exptmod_known(a, b, m, mu, c) */
+
+mp_result mp_int_exptmod_known(mp_int a, mp_int b, mp_int m, mp_int mu, mp_int c)
+{
+ mp_result res;
+ mp_size um;
+ mpz_t temp[2];
+ mp_int s;
+ int last = 0;
+
+ CHECK(a && b && m && c);
+
+ /* Zero moduli and negative exponents are not considered. */
+ if(CMPZ(m) == 0)
+ return MP_UNDEF;
+ if(CMPZ(b) < 0)
+ return MP_RANGE;
+
+ um = MP_USED(m);
+ SETUP(mp_int_init_size(TEMP(0), 2 * um), last);
+
+ if(c == b || c == m) {
+ SETUP(mp_int_init_size(TEMP(1), 2 * um), last);
+ s = TEMP(1);
+ }
+ else {
+ s = c;
+ }
+
+ if((res = mp_int_mod(a, m, TEMP(0))) != MP_OK) goto CLEANUP;
+
+ if((res = s_embar(TEMP(0), b, m, mu, s)) != MP_OK)
+ goto CLEANUP;
+
+ res = mp_int_copy(s, c);
+
+ CLEANUP:
+ while(--last >= 0)
+ mp_int_clear(TEMP(last));
+
+ return res;
+}
+
+/* }}} */
+
+/* {{{ mp_int_redux_const(m, c) */
+
+mp_result mp_int_redux_const(mp_int m, mp_int c)
+{
+ CHECK(m != NULL && c != NULL && m != c);
+
+ return s_brmu(c, m);
+}
+
+/* }}} */
+
+/* {{{ mp_int_invmod(a, m, c) */
+
+mp_result mp_int_invmod(mp_int a, mp_int m, mp_int c)
+{
+ mp_result res;
+ mp_sign sa;
+ int last = 0;
+ mpz_t temp[2];
+
+ CHECK(a != NULL && m != NULL && c != NULL);
+
+ if(CMPZ(a) == 0 || CMPZ(m) <= 0)
+ return MP_RANGE;
+
+ sa = MP_SIGN(a); /* need this for the result later */
+
+ for(last = 0; last < 2; ++last)
+ mp_int_init(TEMP(last));
+
+ if((res = mp_int_egcd(a, m, TEMP(0), TEMP(1), NULL)) != MP_OK)
+ goto CLEANUP;
+
+ if(mp_int_compare_value(TEMP(0), 1) != 0) {
+ res = MP_UNDEF;
+ goto CLEANUP;
+ }
+
+ /* It is first necessary to constrain the value to the proper range */
+ if((res = mp_int_mod(TEMP(1), m, TEMP(1))) != MP_OK)
+ goto CLEANUP;
+
+ /* Now, if 'a' was originally negative, the value we have is
+ actually the magnitude of the negative representative; to get the
+ positive value we have to subtract from the modulus. Otherwise,
+ the value is okay as it stands.
+ */
+ if(sa == MP_NEG)
+ res = mp_int_sub(m, TEMP(1), c);
+ else
+ res = mp_int_copy(TEMP(1), c);
+
+ CLEANUP:
+ while(--last >= 0)
+ mp_int_clear(TEMP(last));
+
+ return res;
+}
+
+/* }}} */
+
+/* {{{ mp_int_gcd(a, b, c) */
+
+/* Binary GCD algorithm due to Josef Stein, 1961 */
+mp_result mp_int_gcd(mp_int a, mp_int b, mp_int c)
+{
+ int ca, cb, k = 0;
+ mpz_t u, v, t;
+ mp_result res;
+
+ CHECK(a != NULL && b != NULL && c != NULL);
+
+ ca = CMPZ(a);
+ cb = CMPZ(b);
+ if(ca == 0 && cb == 0)
+ return MP_UNDEF;
+ else if(ca == 0)
+ return mp_int_abs(b, c);
+ else if(cb == 0)
+ return mp_int_abs(a, c);
+
+ mp_int_init(&t);
+ if((res = mp_int_init_copy(&u, a)) != MP_OK)
+ goto U;
+ if((res = mp_int_init_copy(&v, b)) != MP_OK)
+ goto V;
+
+ MP_SIGN(&u) = MP_ZPOS; MP_SIGN(&v) = MP_ZPOS;
+
+ { /* Divide out common factors of 2 from u and v */
+ int div2_u = s_dp2k(&u), div2_v = s_dp2k(&v);
+
+ k = MIN(div2_u, div2_v);
+ s_qdiv(&u, (mp_size) k);
+ s_qdiv(&v, (mp_size) k);
+ }
+
+ if(mp_int_is_odd(&u)) {
+ if((res = mp_int_neg(&v, &t)) != MP_OK)
+ goto CLEANUP;
+ }
+ else {
+ if((res = mp_int_copy(&u, &t)) != MP_OK)
+ goto CLEANUP;
+ }
+
+ for(;;) {
+ s_qdiv(&t, s_dp2k(&t));
+
+ if(CMPZ(&t) > 0) {
+ if((res = mp_int_copy(&t, &u)) != MP_OK)
+ goto CLEANUP;
+ }
+ else {
+ if((res = mp_int_neg(&t, &v)) != MP_OK)
+ goto CLEANUP;
+ }
+
+ if((res = mp_int_sub(&u, &v, &t)) != MP_OK)
+ goto CLEANUP;
+
+ if(CMPZ(&t) == 0)
+ break;
+ }
+
+ if((res = mp_int_abs(&u, c)) != MP_OK)
+ goto CLEANUP;
+ if(!s_qmul(c, (mp_size) k))
+ res = MP_MEMORY;
+
+ CLEANUP:
+ mp_int_clear(&v);
+ V: mp_int_clear(&u);
+ U: mp_int_clear(&t);
+
+ return res;
+}
+
+/* }}} */
+
+/* {{{ mp_int_egcd(a, b, c, x, y) */
+
+/* This is the binary GCD algorithm again, but this time we keep track
+ of the elementary matrix operations as we go, so we can get values
+ x and y satisfying c = ax + by.
+ */
+mp_result mp_int_egcd(mp_int a, mp_int b, mp_int c,
+ mp_int x, mp_int y)
+{
+ int k, last = 0, ca, cb;
+ mpz_t temp[8];
+ mp_result res;
+
+ CHECK(a != NULL && b != NULL && c != NULL &&
+ (x != NULL || y != NULL));
+
+ ca = CMPZ(a);
+ cb = CMPZ(b);
+ if(ca == 0 && cb == 0)
+ return MP_UNDEF;
+ else if(ca == 0) {
+ if((res = mp_int_abs(b, c)) != MP_OK) return res;
+ mp_int_zero(x); (void) mp_int_set_value(y, 1); return MP_OK;
+ }
+ else if(cb == 0) {
+ if((res = mp_int_abs(a, c)) != MP_OK) return res;
+ (void) mp_int_set_value(x, 1); mp_int_zero(y); return MP_OK;
+ }
+
+ /* Initialize temporaries:
+ A:0, B:1, C:2, D:3, u:4, v:5, ou:6, ov:7 */
+ for(last = 0; last < 4; ++last)
+ mp_int_init(TEMP(last));
+ TEMP(0)->digits[0] = 1;
+ TEMP(3)->digits[0] = 1;
+
+ SETUP(mp_int_init_copy(TEMP(4), a), last);
+ SETUP(mp_int_init_copy(TEMP(5), b), last);
+
+ /* We will work with absolute values here */
+ MP_SIGN(TEMP(4)) = MP_ZPOS;
+ MP_SIGN(TEMP(5)) = MP_ZPOS;
+
+ { /* Divide out common factors of 2 from u and v */
+ int div2_u = s_dp2k(TEMP(4)), div2_v = s_dp2k(TEMP(5));
+
+ k = MIN(div2_u, div2_v);
+ s_qdiv(TEMP(4), k);
+ s_qdiv(TEMP(5), k);
+ }
+
+ SETUP(mp_int_init_copy(TEMP(6), TEMP(4)), last);
+ SETUP(mp_int_init_copy(TEMP(7), TEMP(5)), last);
+
+ for(;;) {
+ while(mp_int_is_even(TEMP(4))) {
+ s_qdiv(TEMP(4), 1);
+
+ if(mp_int_is_odd(TEMP(0)) || mp_int_is_odd(TEMP(1))) {
+ if((res = mp_int_add(TEMP(0), TEMP(7), TEMP(0))) != MP_OK)
+ goto CLEANUP;
+ if((res = mp_int_sub(TEMP(1), TEMP(6), TEMP(1))) != MP_OK)
+ goto CLEANUP;
+ }
+
+ s_qdiv(TEMP(0), 1);
+ s_qdiv(TEMP(1), 1);
+ }
+
+ while(mp_int_is_even(TEMP(5))) {
+ s_qdiv(TEMP(5), 1);
+
+ if(mp_int_is_odd(TEMP(2)) || mp_int_is_odd(TEMP(3))) {
+ if((res = mp_int_add(TEMP(2), TEMP(7), TEMP(2))) != MP_OK)
+ goto CLEANUP;
+ if((res = mp_int_sub(TEMP(3), TEMP(6), TEMP(3))) != MP_OK)
+ goto CLEANUP;
+ }
+
+ s_qdiv(TEMP(2), 1);
+ s_qdiv(TEMP(3), 1);
+ }
+
+ if(mp_int_compare(TEMP(4), TEMP(5)) >= 0) {
+ if((res = mp_int_sub(TEMP(4), TEMP(5), TEMP(4))) != MP_OK) goto CLEANUP;
+ if((res = mp_int_sub(TEMP(0), TEMP(2), TEMP(0))) != MP_OK) goto CLEANUP;
+ if((res = mp_int_sub(TEMP(1), TEMP(3), TEMP(1))) != MP_OK) goto CLEANUP;
+ }
+ else {
+ if((res = mp_int_sub(TEMP(5), TEMP(4), TEMP(5))) != MP_OK) goto CLEANUP;
+ if((res = mp_int_sub(TEMP(2), TEMP(0), TEMP(2))) != MP_OK) goto CLEANUP;
+ if((res = mp_int_sub(TEMP(3), TEMP(1), TEMP(3))) != MP_OK) goto CLEANUP;
+ }
+
+ if(CMPZ(TEMP(4)) == 0) {
+ if(x && (res = mp_int_copy(TEMP(2), x)) != MP_OK) goto CLEANUP;
+ if(y && (res = mp_int_copy(TEMP(3), y)) != MP_OK) goto CLEANUP;
+ if(c) {
+ if(!s_qmul(TEMP(5), k)) {
+ res = MP_MEMORY;
+ goto CLEANUP;
+ }
+
+ res = mp_int_copy(TEMP(5), c);
+ }
+
+ break;
+ }
+ }
+
+ CLEANUP:
+ while(--last >= 0)
+ mp_int_clear(TEMP(last));
+
+ return res;
+}
+
+/* }}} */
+
+/* {{{ mp_int_lcm(a, b, c) */
+
+mp_result mp_int_lcm(mp_int a, mp_int b, mp_int c)
+{
+ mpz_t lcm;
+ mp_result res;
+
+ CHECK(a != NULL && b != NULL && c != NULL);
+
+ /* Since a * b = gcd(a, b) * lcm(a, b), we can compute
+ lcm(a, b) = (a / gcd(a, b)) * b.
+
+ This formulation insures everything works even if the input
+ variables share space.
+ */
+ if((res = mp_int_init(&lcm)) != MP_OK)
+ return res;
+ if((res = mp_int_gcd(a, b, &lcm)) != MP_OK)
+ goto CLEANUP;
+ if((res = mp_int_div(a, &lcm, &lcm, NULL)) != MP_OK)
+ goto CLEANUP;
+ if((res = mp_int_mul(&lcm, b, &lcm)) != MP_OK)
+ goto CLEANUP;
+
+ res = mp_int_copy(&lcm, c);
+
+ CLEANUP:
+ mp_int_clear(&lcm);
+
+ return res;
+}
+
+/* }}} */
+
+/* {{{ mp_int_divisible_value(a, v) */
+
+int mp_int_divisible_value(mp_int a, mp_small v)
+{
+ mp_small rem = 0;
+
+ if(mp_int_div_value(a, v, NULL, &rem) != MP_OK)
+ return 0;
+
+ return rem == 0;
+}
+
+/* }}} */
+
+/* {{{ mp_int_is_pow2(z) */
+
+int mp_int_is_pow2(mp_int z)
+{
+ CHECK(z != NULL);
+
+ return s_isp2(z);
+}
+
+/* }}} */
+
+/* {{{ mp_int_root(a, b, c) */
+
+/* Implementation of Newton's root finding method, based loosely on a
+ patch contributed by Hal Finkel <half@halssoftware.com>
+ modified by M. J. Fromberger.
+ */
+mp_result mp_int_root(mp_int a, mp_small b, mp_int c)
+{
+ mp_result res = MP_OK;
+ mpz_t temp[5];
+ int last = 0;
+ int flips = 0;
+
+ CHECK(a != NULL && c != NULL && b > 0);
+
+ if(b == 1) {
+ return mp_int_copy(a, c);
+ }
+ if(MP_SIGN(a) == MP_NEG) {
+ if(b % 2 == 0)
+ return MP_UNDEF; /* root does not exist for negative a with even b */
+ else
+ flips = 1;
+ }
+
+ SETUP(mp_int_init_copy(TEMP(last), a), last);
+ SETUP(mp_int_init_copy(TEMP(last), a), last);
+ SETUP(mp_int_init(TEMP(last)), last);
+ SETUP(mp_int_init(TEMP(last)), last);
+ SETUP(mp_int_init(TEMP(last)), last);
+
+ (void) mp_int_abs(TEMP(0), TEMP(0));
+ (void) mp_int_abs(TEMP(1), TEMP(1));
+
+ for(;;) {
+ if((res = mp_int_expt(TEMP(1), b, TEMP(2))) != MP_OK)
+ goto CLEANUP;
+
+ if(mp_int_compare_unsigned(TEMP(2), TEMP(0)) <= 0)
+ break;
+
+ if((res = mp_int_sub(TEMP(2), TEMP(0), TEMP(2))) != MP_OK)
+ goto CLEANUP;
+ if((res = mp_int_expt(TEMP(1), b - 1, TEMP(3))) != MP_OK)
+ goto CLEANUP;
+ if((res = mp_int_mul_value(TEMP(3), b, TEMP(3))) != MP_OK)
+ goto CLEANUP;
+ if((res = mp_int_div(TEMP(2), TEMP(3), TEMP(4), NULL)) != MP_OK)
+ goto CLEANUP;
+ if((res = mp_int_sub(TEMP(1), TEMP(4), TEMP(4))) != MP_OK)
+ goto CLEANUP;
+
+ if(mp_int_compare_unsigned(TEMP(1), TEMP(4)) == 0) {
+ if((res = mp_int_sub_value(TEMP(4), 1, TEMP(4))) != MP_OK)
+ goto CLEANUP;
+ }
+ if((res = mp_int_copy(TEMP(4), TEMP(1))) != MP_OK)
+ goto CLEANUP;
+ }
+
+ if((res = mp_int_copy(TEMP(1), c)) != MP_OK)
+ goto CLEANUP;
+
+ /* If the original value of a was negative, flip the output sign. */
+ if(flips)
+ (void) mp_int_neg(c, c); /* cannot fail */
+
+ CLEANUP:
+ while(--last >= 0)
+ mp_int_clear(TEMP(last));
+
+ return res;
+}
+
+/* }}} */
+
+/* {{{ mp_int_to_int(z, out) */
+
+mp_result mp_int_to_int(mp_int z, mp_small *out)
+{
+ mp_usmall uv = 0;
+ mp_size uz;
+ mp_digit *dz;
+ mp_sign sz;
+
+ CHECK(z != NULL);
+
+ /* Make sure the value is representable as an int */
+ sz = MP_SIGN(z);
+ if((sz == MP_ZPOS && mp_int_compare_value(z, MP_SMALL_MAX) > 0) ||
+ mp_int_compare_value(z, MP_SMALL_MIN) < 0)
+ return MP_RANGE;
+
+ uz = MP_USED(z);
+ dz = MP_DIGITS(z) + uz - 1;
+
+ while(uz > 0) {
+ uv <<= MP_DIGIT_BIT/2;
+ uv = (uv << (MP_DIGIT_BIT/2)) | *dz--;
+ --uz;
+ }
+
+ if(out)
+ *out = (sz == MP_NEG) ? -(mp_small)uv : (mp_small)uv;
+
+ return MP_OK;
+}
+
+/* }}} */
+
+/* {{{ mp_int_to_uint(z, *out) */
+
+mp_result mp_int_to_uint(mp_int z, mp_usmall *out)
+{
+ mp_usmall uv = 0;
+ mp_size uz;
+ mp_digit *dz;
+ mp_sign sz;
+
+ CHECK(z != NULL);
+
+ /* Make sure the value is representable as an int */
+ sz = MP_SIGN(z);
+ if(!(sz == MP_ZPOS && mp_int_compare_value(z, UINT_MAX) <= 0))
+ return MP_RANGE;
+
+ uz = MP_USED(z);
+ dz = MP_DIGITS(z) + uz - 1;
+
+ while(uz > 0) {
+ uv <<= MP_DIGIT_BIT/2;
+ uv = (uv << (MP_DIGIT_BIT/2)) | *dz--;
+ --uz;
+ }
+
+ if(out)
+ *out = uv;
+
+ return MP_OK;
+}
+
+/* }}} */
+
+/* {{{ mp_int_to_string(z, radix, str, limit) */
+
+mp_result mp_int_to_string(mp_int z, mp_size radix,
+ char *str, int limit)
+{
+ mp_result res;
+ int cmp = 0;
+
+ CHECK(z != NULL && str != NULL && limit >= 2);
+
+ if(radix < MP_MIN_RADIX || radix > MP_MAX_RADIX)
+ return MP_RANGE;
+
+ if(CMPZ(z) == 0) {
+ *str++ = s_val2ch(0, 1);
+ }
+ else {
+ mpz_t tmp;
+ char *h, *t;
+
+ if((res = mp_int_init_copy(&tmp, z)) != MP_OK)
+ return res;
+
+ if(MP_SIGN(z) == MP_NEG) {
+ *str++ = '-';
+ --limit;
+ }
+ h = str;
+
+ /* Generate digits in reverse order until finished or limit reached */
+ for(/* */; limit > 0; --limit) {
+ mp_digit d;
+
+ if((cmp = CMPZ(&tmp)) == 0)
+ break;
+
+ d = s_ddiv(&tmp, (mp_digit)radix);
+ *str++ = s_val2ch(d, 1);
+ }
+ t = str - 1;
+
+ /* Put digits back in correct output order */
+ while(h < t) {
+ char tc = *h;
+ *h++ = *t;
+ *t-- = tc;
+ }
+
+ mp_int_clear(&tmp);
+ }
+
+ *str = '\0';
+ if(cmp == 0)
+ return MP_OK;
+ else
+ return MP_TRUNC;
+}
+
+/* }}} */
+
+/* {{{ mp_int_string_len(z, radix) */
+
+mp_result mp_int_string_len(mp_int z, mp_size radix)
+{
+ int len;
+
+ CHECK(z != NULL);
+
+ if(radix < MP_MIN_RADIX || radix > MP_MAX_RADIX)
+ return MP_RANGE;
+
+ len = s_outlen(z, radix) + 1; /* for terminator */
+
+ /* Allow for sign marker on negatives */
+ if(MP_SIGN(z) == MP_NEG)
+ len += 1;
+
+ return len;
+}
+
+/* }}} */
+
+/* {{{ mp_int_read_string(z, radix, *str) */
+
+/* Read zero-terminated string into z */
+mp_result mp_int_read_string(mp_int z, mp_size radix, const char *str)
+{
+ return mp_int_read_cstring(z, radix, str, NULL);
+
+}
+
+/* }}} */
+
+/* {{{ mp_int_read_cstring(z, radix, *str, **end) */
+
+mp_result mp_int_read_cstring(mp_int z, mp_size radix, const char *str, char **end)
+{
+ int ch;
+
+ CHECK(z != NULL && str != NULL);
+
+ if(radix < MP_MIN_RADIX || radix > MP_MAX_RADIX)
+ return MP_RANGE;
+
+ /* Skip leading whitespace */
+ while(isspace((int)*str))
+ ++str;
+
+ /* Handle leading sign tag (+/-, positive default) */
+ switch(*str) {
+ case '-':
+ MP_SIGN(z) = MP_NEG;
+ ++str;
+ break;
+ case '+':
+ ++str; /* fallthrough */
+ default:
+ MP_SIGN(z) = MP_ZPOS;
+ break;
+ }
+
+ /* Skip leading zeroes */
+ while((ch = s_ch2val(*str, radix)) == 0)
+ ++str;
+
+ /* Make sure there is enough space for the value */
+ if(!s_pad(z, s_inlen(strlen(str), radix)))
+ return MP_MEMORY;
+
+ MP_USED(z) = 1; z->digits[0] = 0;
+
+ while(*str != '\0' && ((ch = s_ch2val(*str, radix)) >= 0)) {
+ s_dmul(z, (mp_digit)radix);
+ s_dadd(z, (mp_digit)ch);
+ ++str;
+ }
+
+ CLAMP(z);
+
+ /* Override sign for zero, even if negative specified. */
+ if(CMPZ(z) == 0)
+ MP_SIGN(z) = MP_ZPOS;
+
+ if(end != NULL)
+ *end = (char *)str;
+
+ /* Return a truncation error if the string has unprocessed
+ characters remaining, so the caller can tell if the whole string
+ was done */
+ if(*str != '\0')
+ return MP_TRUNC;
+ else
+ return MP_OK;
+}
+
+/* }}} */
+
+/* {{{ mp_int_count_bits(z) */
+
+mp_result mp_int_count_bits(mp_int z)
+{
+ mp_size nbits = 0, uz;
+ mp_digit d;
+
+ CHECK(z != NULL);
+
+ uz = MP_USED(z);
+ if(uz == 1 && z->digits[0] == 0)
+ return 1;
+
+ --uz;
+ nbits = uz * MP_DIGIT_BIT;
+ d = z->digits[uz];
+
+ while(d != 0) {
+ d >>= 1;
+ ++nbits;
+ }
+
+ return nbits;
+}
+
+/* }}} */
+
+/* {{{ mp_int_to_binary(z, buf, limit) */
+
+mp_result mp_int_to_binary(mp_int z, unsigned char *buf, int limit)
+{
+ static const int PAD_FOR_2C = 1;
+
+ mp_result res;
+ int limpos = limit;
+
+ CHECK(z != NULL && buf != NULL);
+
+ res = s_tobin(z, buf, &limpos, PAD_FOR_2C);
+
+ if(MP_SIGN(z) == MP_NEG)
+ s_2comp(buf, limpos);
+
+ return res;
+}
+
+/* }}} */
+
+/* {{{ mp_int_read_binary(z, buf, len) */
+
+mp_result mp_int_read_binary(mp_int z, unsigned char *buf, int len)
+{
+ mp_size need, i;
+ unsigned char *tmp;
+ mp_digit *dz;
+
+ CHECK(z != NULL && buf != NULL && len > 0);
+
+ /* Figure out how many digits are needed to represent this value */
+ need = ((len * CHAR_BIT) + (MP_DIGIT_BIT - 1)) / MP_DIGIT_BIT;
+ if(!s_pad(z, need))
+ return MP_MEMORY;
+
+ mp_int_zero(z);
+
+ /* If the high-order bit is set, take the 2's complement before
+ reading the value (it will be restored afterward) */
+ if(buf[0] >> (CHAR_BIT - 1)) {
+ MP_SIGN(z) = MP_NEG;
+ s_2comp(buf, len);
+ }
+
+ dz = MP_DIGITS(z);
+ for(tmp = buf, i = len; i > 0; --i, ++tmp) {
+ s_qmul(z, (mp_size) CHAR_BIT);
+ *dz |= *tmp;
+ }
+
+ /* Restore 2's complement if we took it before */
+ if(MP_SIGN(z) == MP_NEG)
+ s_2comp(buf, len);
+
+ return MP_OK;
+}
+
+/* }}} */
+
+/* {{{ mp_int_binary_len(z) */
+
+mp_result mp_int_binary_len(mp_int z)
+{
+ mp_result res = mp_int_count_bits(z);
+ int bytes = mp_int_unsigned_len(z);
+
+ if(res <= 0)
+ return res;
+
+ bytes = (res + (CHAR_BIT - 1)) / CHAR_BIT;
+
+ /* If the highest-order bit falls exactly on a byte boundary, we
+ need to pad with an extra byte so that the sign will be read
+ correctly when reading it back in. */
+ if(bytes * CHAR_BIT == res)
+ ++bytes;
+
+ return bytes;
+}
+
+/* }}} */
+
+/* {{{ mp_int_to_unsigned(z, buf, limit) */
+
+mp_result mp_int_to_unsigned(mp_int z, unsigned char *buf, int limit)
+{
+ static const int NO_PADDING = 0;
+
+ CHECK(z != NULL && buf != NULL);
+
+ return s_tobin(z, buf, &limit, NO_PADDING);
+}
+
+/* }}} */
+
+/* {{{ mp_int_read_unsigned(z, buf, len) */
+
+mp_result mp_int_read_unsigned(mp_int z, unsigned char *buf, int len)
+{
+ mp_size need, i;
+ unsigned char *tmp;
+ mp_digit *dz;
+
+ CHECK(z != NULL && buf != NULL && len > 0);
+
+ /* Figure out how many digits are needed to represent this value */
+ need = ((len * CHAR_BIT) + (MP_DIGIT_BIT - 1)) / MP_DIGIT_BIT;
+ if(!s_pad(z, need))
+ return MP_MEMORY;
+
+ mp_int_zero(z);
+
+ dz = MP_DIGITS(z);
+ for(tmp = buf, i = len; i > 0; --i, ++tmp) {
+ (void) s_qmul(z, CHAR_BIT);
+ *dz |= *tmp;
+ }
+
+ return MP_OK;
+}
+
+/* }}} */
+
+/* {{{ mp_int_unsigned_len(z) */
+
+mp_result mp_int_unsigned_len(mp_int z)
+{
+ mp_result res = mp_int_count_bits(z);
+ int bytes;
+
+ if(res <= 0)
+ return res;
+
+ bytes = (res + (CHAR_BIT - 1)) / CHAR_BIT;
+
+ return bytes;
+}
+
+/* }}} */
+
+/* {{{ mp_error_string(res) */
+
+const char *mp_error_string(mp_result res)
+{
+ int ix;
+ if(res > 0)
+ return s_unknown_err;
+
+ res = -res;
+ for(ix = 0; ix < res && s_error_msg[ix] != NULL; ++ix)
+ ;
+
+ if(s_error_msg[ix] != NULL)
+ return s_error_msg[ix];
+ else
+ return s_unknown_err;
+}
+
+/* }}} */
+
+/*------------------------------------------------------------------------*/
+/* Private functions for internal use. These make assumptions. */
+
+/* {{{ s_alloc(num) */
+
+static mp_digit *s_alloc(mp_size num)
+{
+ mp_digit *out = malloc(num * sizeof(mp_digit));
+
+ assert(out != NULL); /* for debugging */
+#if DEBUG > 1
+ {
+ mp_digit v = (mp_digit) 0xdeadbeef;
+ int ix;
+
+ for(ix = 0; ix < num; ++ix)
+ out[ix] = v;
+ }
+#endif
+
+ return out;
+}
+
+/* }}} */
+
+/* {{{ s_realloc(old, osize, nsize) */
+
+static mp_digit *s_realloc(mp_digit *old, mp_size osize, mp_size nsize)
+{
+#if DEBUG > 1
+ mp_digit *new = s_alloc(nsize);
+ int ix;
+
+ for(ix = 0; ix < nsize; ++ix)
+ new[ix] = (mp_digit) 0xdeadbeef;
+
+ memcpy(new, old, osize * sizeof(mp_digit));
+#else
+ mp_digit *new = realloc(old, nsize * sizeof(mp_digit));
+
+ assert(new != NULL); /* for debugging */
+#endif
+ return new;
+}
+
+/* }}} */
+
+/* {{{ s_free(ptr) */
+
+static void s_free(void *ptr)
+{
+ free(ptr);
+}
+
+/* }}} */
+
+/* {{{ s_pad(z, min) */
+
+static int s_pad(mp_int z, mp_size min)
+{
+ if(MP_ALLOC(z) < min) {
+ mp_size nsize = ROUND_PREC(min);
+ mp_digit *tmp;
+
+ if((void *)z->digits == (void *)z) {
+ if((tmp = s_alloc(nsize)) == NULL)
+ return 0;
+
+ COPY(MP_DIGITS(z), tmp, MP_USED(z));
+ }
+ else if((tmp = s_realloc(MP_DIGITS(z), MP_ALLOC(z), nsize)) == NULL)
+ return 0;
+
+ MP_DIGITS(z) = tmp;
+ MP_ALLOC(z) = nsize;
+ }
+
+ return 1;
+}
+
+/* }}} */
+
+/* {{{ s_fake(z, value, vbuf) */
+
+static void s_fake(mp_int z, mp_small value, mp_digit vbuf[])
+{
+ mp_size uv = (mp_size) s_vpack(value, vbuf);
+
+ z->used = uv;
+ z->alloc = MP_VALUE_DIGITS(value);
+ z->sign = (value < 0) ? MP_NEG : MP_ZPOS;
+ z->digits = vbuf;
+}
+
+/* }}} */
+
+/* {{{ s_cdig(da, db, len) */
+
+static int s_cdig(mp_digit *da, mp_digit *db, mp_size len)
+{
+ mp_digit *dat = da + len - 1, *dbt = db + len - 1;
+
+ for(/* */; len != 0; --len, --dat, --dbt) {
+ if(*dat > *dbt)
+ return 1;
+ else if(*dat < *dbt)
+ return -1;
+ }
+
+ return 0;
+}
+
+/* }}} */
+
+/* {{{ s_vpack(v, t[]) */
+
+static int s_vpack(mp_small v, mp_digit t[])
+{
+ mp_usmall uv = (mp_usmall) ((v < 0) ? -v : v);
+ int ndig = 0;
+
+ if(uv == 0)
+ t[ndig++] = 0;
+ else {
+ while(uv != 0) {
+ t[ndig++] = (mp_digit) uv;
+ uv >>= MP_DIGIT_BIT/2;
+ uv >>= MP_DIGIT_BIT/2;
+ }
+ }
+
+ return ndig;
+}
+
+/* }}} */
+
+/* {{{ s_ucmp(a, b) */
+
+static int s_ucmp(mp_int a, mp_int b)
+{
+ mp_size ua = MP_USED(a), ub = MP_USED(b);
+
+ if(ua > ub)
+ return 1;
+ else if(ub > ua)
+ return -1;
+ else
+ return s_cdig(MP_DIGITS(a), MP_DIGITS(b), ua);
+}
+
+/* }}} */
+
+/* {{{ s_vcmp(a, v) */
+
+static int s_vcmp(mp_int a, mp_small v)
+{
+ mp_digit vdig[MP_VALUE_DIGITS(v)];
+ int ndig = 0;
+ mp_size ua = MP_USED(a);
+
+ ndig = s_vpack(v, vdig);
+
+ if(ua > ndig)
+ return 1;
+ else if(ua < ndig)
+ return -1;
+ else
+ return s_cdig(MP_DIGITS(a), vdig, ndig);
+}
+
+/* }}} */
+
+/* {{{ s_uadd(da, db, dc, size_a, size_b) */
+
+static mp_digit s_uadd(mp_digit *da, mp_digit *db, mp_digit *dc,
+ mp_size size_a, mp_size size_b)
+{
+ mp_size pos;
+ mp_word w = 0;
+
+ /* Insure that da is the longer of the two to simplify later code */
+ if(size_b > size_a) {
+ SWAP(mp_digit *, da, db);
+ SWAP(mp_size, size_a, size_b);
+ }
+
+ /* Add corresponding digits until the shorter number runs out */
+ for(pos = 0; pos < size_b; ++pos, ++da, ++db, ++dc) {
+ w = w + (mp_word) *da + (mp_word) *db;
+ *dc = LOWER_HALF(w);
+ w = UPPER_HALF(w);
+ }
+
+ /* Propagate carries as far as necessary */
+ for(/* */; pos < size_a; ++pos, ++da, ++dc) {
+ w = w + *da;
+
+ *dc = LOWER_HALF(w);
+ w = UPPER_HALF(w);
+ }
+
+ /* Return carry out */
+ return (mp_digit)w;
+}
+
+/* }}} */
+
+/* {{{ s_usub(da, db, dc, size_a, size_b) */
+
+static void s_usub(mp_digit *da, mp_digit *db, mp_digit *dc,
+ mp_size size_a, mp_size size_b)
+{
+ mp_size pos;
+ mp_word w = 0;
+
+ /* We assume that |a| >= |b| so this should definitely hold */
+ assert(size_a >= size_b);
+
+ /* Subtract corresponding digits and propagate borrow */
+ for(pos = 0; pos < size_b; ++pos, ++da, ++db, ++dc) {
+ w = ((mp_word)MP_DIGIT_MAX + 1 + /* MP_RADIX */
+ (mp_word)*da) - w - (mp_word)*db;
+
+ *dc = LOWER_HALF(w);
+ w = (UPPER_HALF(w) == 0);
+ }
+
+ /* Finish the subtraction for remaining upper digits of da */
+ for(/* */; pos < size_a; ++pos, ++da, ++dc) {
+ w = ((mp_word)MP_DIGIT_MAX + 1 + /* MP_RADIX */
+ (mp_word)*da) - w;
+
+ *dc = LOWER_HALF(w);
+ w = (UPPER_HALF(w) == 0);
+ }
+
+ /* If there is a borrow out at the end, it violates the precondition */
+ assert(w == 0);
+}
+
+/* }}} */
+
+/* {{{ s_kmul(da, db, dc, size_a, size_b) */
+
+static int s_kmul(mp_digit *da, mp_digit *db, mp_digit *dc,
+ mp_size size_a, mp_size size_b)
+{
+ mp_size bot_size;
+
+ /* Make sure b is the smaller of the two input values */
+ if(size_b > size_a) {
+ SWAP(mp_digit *, da, db);
+ SWAP(mp_size, size_a, size_b);
+ }
+
+ /* Insure that the bottom is the larger half in an odd-length split;
+ the code below relies on this being true.
+ */
+ bot_size = (size_a + 1) / 2;
+
+ /* If the values are big enough to bother with recursion, use the
+ Karatsuba algorithm to compute the product; otherwise use the
+ normal multiplication algorithm
+ */
+ if(multiply_threshold &&
+ size_a >= multiply_threshold &&
+ size_b > bot_size) {
+
+ mp_digit *t1, *t2, *t3, carry;
+
+ mp_digit *a_top = da + bot_size;
+ mp_digit *b_top = db + bot_size;
+
+ mp_size at_size = size_a - bot_size;
+ mp_size bt_size = size_b - bot_size;
+ mp_size buf_size = 2 * bot_size;
+
+ /* Do a single allocation for all three temporary buffers needed;
+ each buffer must be big enough to hold the product of two
+ bottom halves, and one buffer needs space for the completed
+ product; twice the space is plenty.
+ */
+ if((t1 = s_alloc(4 * buf_size)) == NULL) return 0;
+ t2 = t1 + buf_size;
+ t3 = t2 + buf_size;
+ ZERO(t1, 4 * buf_size);
+
+ /* t1 and t2 are initially used as temporaries to compute the inner product
+ (a1 + a0)(b1 + b0) = a1b1 + a1b0 + a0b1 + a0b0
+ */
+ carry = s_uadd(da, a_top, t1, bot_size, at_size); /* t1 = a1 + a0 */
+ t1[bot_size] = carry;
+
+ carry = s_uadd(db, b_top, t2, bot_size, bt_size); /* t2 = b1 + b0 */
+ t2[bot_size] = carry;
+
+ (void) s_kmul(t1, t2, t3, bot_size + 1, bot_size + 1); /* t3 = t1 * t2 */
+
+ /* Now we'll get t1 = a0b0 and t2 = a1b1, and subtract them out so that
+ we're left with only the pieces we want: t3 = a1b0 + a0b1
+ */
+ ZERO(t1, buf_size);
+ ZERO(t2, buf_size);
+ (void) s_kmul(da, db, t1, bot_size, bot_size); /* t1 = a0 * b0 */
+ (void) s_kmul(a_top, b_top, t2, at_size, bt_size); /* t2 = a1 * b1 */
+
+ /* Subtract out t1 and t2 to get the inner product */
+ s_usub(t3, t1, t3, buf_size + 2, buf_size);
+ s_usub(t3, t2, t3, buf_size + 2, buf_size);
+
+ /* Assemble the output value */
+ COPY(t1, dc, buf_size);
+ carry = s_uadd(t3, dc + bot_size, dc + bot_size,
+ buf_size + 1, buf_size);
+ assert(carry == 0);
+
+ carry = s_uadd(t2, dc + 2*bot_size, dc + 2*bot_size,
+ buf_size, buf_size);
+ assert(carry == 0);
+
+ s_free(t1); /* note t2 and t3 are just internal pointers to t1 */
+ }
+ else {
+ s_umul(da, db, dc, size_a, size_b);
+ }
+
+ return 1;
+}
+
+/* }}} */
+
+/* {{{ s_umul(da, db, dc, size_a, size_b) */
+
+static void s_umul(mp_digit *da, mp_digit *db, mp_digit *dc,
+ mp_size size_a, mp_size size_b)
+{
+ mp_size a, b;
+ mp_word w;
+
+ for(a = 0; a < size_a; ++a, ++dc, ++da) {
+ mp_digit *dct = dc;
+ mp_digit *dbt = db;
+
+ if(*da == 0)
+ continue;
+
+ w = 0;
+ for(b = 0; b < size_b; ++b, ++dbt, ++dct) {
+ w = (mp_word)*da * (mp_word)*dbt + w + (mp_word)*dct;
+
+ *dct = LOWER_HALF(w);
+ w = UPPER_HALF(w);
+ }
+
+ *dct = (mp_digit)w;
+ }
+}
+
+/* }}} */
+
+/* {{{ s_ksqr(da, dc, size_a) */
+
+static int s_ksqr(mp_digit *da, mp_digit *dc, mp_size size_a)
+{
+ if(multiply_threshold && size_a > multiply_threshold) {
+ mp_size bot_size = (size_a + 1) / 2;
+ mp_digit *a_top = da + bot_size;
+ mp_digit *t1, *t2, *t3, carry;
+ mp_size at_size = size_a - bot_size;
+ mp_size buf_size = 2 * bot_size;
+
+ if((t1 = s_alloc(4 * buf_size)) == NULL) return 0;
+ t2 = t1 + buf_size;
+ t3 = t2 + buf_size;
+ ZERO(t1, 4 * buf_size);
+
+ (void) s_ksqr(da, t1, bot_size); /* t1 = a0 ^ 2 */
+ (void) s_ksqr(a_top, t2, at_size); /* t2 = a1 ^ 2 */
+
+ (void) s_kmul(da, a_top, t3, bot_size, at_size); /* t3 = a0 * a1 */
+
+ /* Quick multiply t3 by 2, shifting left (can't overflow) */
+ {
+ int i, top = bot_size + at_size;
+ mp_word w, save = 0;
+
+ for(i = 0; i < top; ++i) {
+ w = t3[i];
+ w = (w << 1) | save;
+ t3[i] = LOWER_HALF(w);
+ save = UPPER_HALF(w);
+ }
+ t3[i] = LOWER_HALF(save);
+ }
+
+ /* Assemble the output value */
+ COPY(t1, dc, 2 * bot_size);
+ carry = s_uadd(t3, dc + bot_size, dc + bot_size,
+ buf_size + 1, buf_size);
+ assert(carry == 0);
+
+ carry = s_uadd(t2, dc + 2*bot_size, dc + 2*bot_size,
+ buf_size, buf_size);
+ assert(carry == 0);
+
+ s_free(t1); /* note that t2 and t2 are internal pointers only */
+
+ }
+ else {
+ s_usqr(da, dc, size_a);
+ }
+
+ return 1;
+}
+
+/* }}} */
+
+/* {{{ s_usqr(da, dc, size_a) */
+
+static void s_usqr(mp_digit *da, mp_digit *dc, mp_size size_a)
+{
+ mp_size i, j;
+ mp_word w;
+
+ for(i = 0; i < size_a; ++i, dc += 2, ++da) {
+ mp_digit *dct = dc, *dat = da;
+
+ if(*da == 0)
+ continue;
+
+ /* Take care of the first digit, no rollover */
+ w = (mp_word)*dat * (mp_word)*dat + (mp_word)*dct;
+ *dct = LOWER_HALF(w);
+ w = UPPER_HALF(w);
+ ++dat; ++dct;
+
+ for(j = i + 1; j < size_a; ++j, ++dat, ++dct) {
+ mp_word t = (mp_word)*da * (mp_word)*dat;
+ mp_word u = w + (mp_word)*dct, ov = 0;
+
+ /* Check if doubling t will overflow a word */
+ if(HIGH_BIT_SET(t))
+ ov = 1;
+
+ w = t + t;
+
+ /* Check if adding u to w will overflow a word */
+ if(ADD_WILL_OVERFLOW(w, u))
+ ov = 1;
+
+ w += u;
+
+ *dct = LOWER_HALF(w);
+ w = UPPER_HALF(w);
+ if(ov) {
+ w += MP_DIGIT_MAX; /* MP_RADIX */
+ ++w;
+ }
+ }
+
+ w = w + *dct;
+ *dct = (mp_digit)w;
+ while((w = UPPER_HALF(w)) != 0) {
+ ++dct; w = w + *dct;
+ *dct = LOWER_HALF(w);
+ }
+
+ assert(w == 0);
+ }
+}
+
+/* }}} */
+
+/* {{{ s_dadd(a, b) */
+
+static void s_dadd(mp_int a, mp_digit b)
+{
+ mp_word w = 0;
+ mp_digit *da = MP_DIGITS(a);
+ mp_size ua = MP_USED(a);
+
+ w = (mp_word)*da + b;
+ *da++ = LOWER_HALF(w);
+ w = UPPER_HALF(w);
+
+ for(ua -= 1; ua > 0; --ua, ++da) {
+ w = (mp_word)*da + w;
+
+ *da = LOWER_HALF(w);
+ w = UPPER_HALF(w);
+ }
+
+ if(w) {
+ *da = (mp_digit)w;
+ MP_USED(a) += 1;
+ }
+}
+
+/* }}} */
+
+/* {{{ s_dmul(a, b) */
+
+static void s_dmul(mp_int a, mp_digit b)
+{
+ mp_word w = 0;
+ mp_digit *da = MP_DIGITS(a);
+ mp_size ua = MP_USED(a);
+
+ while(ua > 0) {
+ w = (mp_word)*da * b + w;
+ *da++ = LOWER_HALF(w);
+ w = UPPER_HALF(w);
+ --ua;
+ }
+
+ if(w) {
+ *da = (mp_digit)w;
+ MP_USED(a) += 1;
+ }
+}
+
+/* }}} */
+
+/* {{{ s_dbmul(da, b, dc, size_a) */
+
+static void s_dbmul(mp_digit *da, mp_digit b, mp_digit *dc, mp_size size_a)
+{
+ mp_word w = 0;
+
+ while(size_a > 0) {
+ w = (mp_word)*da++ * (mp_word)b + w;
+
+ *dc++ = LOWER_HALF(w);
+ w = UPPER_HALF(w);
+ --size_a;
+ }
+
+ if(w)
+ *dc = LOWER_HALF(w);
+}
+
+/* }}} */
+
+/* {{{ s_ddiv(da, d, dc, size_a) */
+
+static mp_digit s_ddiv(mp_int a, mp_digit b)
+{
+ mp_word w = 0, qdigit;
+ mp_size ua = MP_USED(a);
+ mp_digit *da = MP_DIGITS(a) + ua - 1;
+
+ for(/* */; ua > 0; --ua, --da) {
+ w = (w << MP_DIGIT_BIT) | *da;
+
+ if(w >= b) {
+ qdigit = w / b;
+ w = w % b;
+ }
+ else {
+ qdigit = 0;
+ }
+
+ *da = (mp_digit)qdigit;
+ }
+
+ CLAMP(a);
+ return (mp_digit)w;
+}
+
+/* }}} */
+
+/* {{{ s_qdiv(z, p2) */
+
+static void s_qdiv(mp_int z, mp_size p2)
+{
+ mp_size ndig = p2 / MP_DIGIT_BIT, nbits = p2 % MP_DIGIT_BIT;
+ mp_size uz = MP_USED(z);
+
+ if(ndig) {
+ mp_size mark;
+ mp_digit *to, *from;
+
+ if(ndig >= uz) {
+ mp_int_zero(z);
+ return;
+ }
+
+ to = MP_DIGITS(z); from = to + ndig;
+
+ for(mark = ndig; mark < uz; ++mark)
+ *to++ = *from++;
+
+ MP_USED(z) = uz - ndig;
+ }
+
+ if(nbits) {
+ mp_digit d = 0, *dz, save;
+ mp_size up = MP_DIGIT_BIT - nbits;
+
+ uz = MP_USED(z);
+ dz = MP_DIGITS(z) + uz - 1;
+
+ for(/* */; uz > 0; --uz, --dz) {
+ save = *dz;
+
+ *dz = (*dz >> nbits) | (d << up);
+ d = save;
+ }
+
+ CLAMP(z);
+ }
+
+ if(MP_USED(z) == 1 && z->digits[0] == 0)
+ MP_SIGN(z) = MP_ZPOS;
+}
+
+/* }}} */
+
+/* {{{ s_qmod(z, p2) */
+
+static void s_qmod(mp_int z, mp_size p2)
+{
+ mp_size start = p2 / MP_DIGIT_BIT + 1, rest = p2 % MP_DIGIT_BIT;
+ mp_size uz = MP_USED(z);
+ mp_digit mask = (1 << rest) - 1;
+
+ if(start <= uz) {
+ MP_USED(z) = start;
+ z->digits[start - 1] &= mask;
+ CLAMP(z);
+ }
+}
+
+/* }}} */
+
+/* {{{ s_qmul(z, p2) */
+
+static int s_qmul(mp_int z, mp_size p2)
+{
+ mp_size uz, need, rest, extra, i;
+ mp_digit *from, *to, d;
+
+ if(p2 == 0)
+ return 1;
+
+ uz = MP_USED(z);
+ need = p2 / MP_DIGIT_BIT; rest = p2 % MP_DIGIT_BIT;
+
+ /* Figure out if we need an extra digit at the top end; this occurs
+ if the topmost `rest' bits of the high-order digit of z are not
+ zero, meaning they will be shifted off the end if not preserved */
+ extra = 0;
+ if(rest != 0) {
+ mp_digit *dz = MP_DIGITS(z) + uz - 1;
+
+ if((*dz >> (MP_DIGIT_BIT - rest)) != 0)
+ extra = 1;
+ }
+
+ if(!s_pad(z, uz + need + extra))
+ return 0;
+
+ /* If we need to shift by whole digits, do that in one pass, then
+ to back and shift by partial digits.
+ */
+ if(need > 0) {
+ from = MP_DIGITS(z) + uz - 1;
+ to = from + need;
+
+ for(i = 0; i < uz; ++i)
+ *to-- = *from--;
+
+ ZERO(MP_DIGITS(z), need);
+ uz += need;
+ }
+
+ if(rest) {
+ d = 0;
+ for(i = need, from = MP_DIGITS(z) + need; i < uz; ++i, ++from) {
+ mp_digit save = *from;
+
+ *from = (*from << rest) | (d >> (MP_DIGIT_BIT - rest));
+ d = save;
+ }
+
+ d >>= (MP_DIGIT_BIT - rest);
+ if(d != 0) {
+ *from = d;
+ uz += extra;
+ }
+ }
+
+ MP_USED(z) = uz;
+ CLAMP(z);
+
+ return 1;
+}
+
+/* }}} */
+
+/* {{{ s_qsub(z, p2) */
+
+/* Compute z = 2^p2 - |z|; requires that 2^p2 >= |z|
+ The sign of the result is always zero/positive.
+ */
+static int s_qsub(mp_int z, mp_size p2)
+{
+ mp_digit hi = (1 << (p2 % MP_DIGIT_BIT)), *zp;
+ mp_size tdig = (p2 / MP_DIGIT_BIT), pos;
+ mp_word w = 0;
+
+ if(!s_pad(z, tdig + 1))
+ return 0;
+
+ for(pos = 0, zp = MP_DIGITS(z); pos < tdig; ++pos, ++zp) {
+ w = ((mp_word) MP_DIGIT_MAX + 1) - w - (mp_word)*zp;
+
+ *zp = LOWER_HALF(w);
+ w = UPPER_HALF(w) ? 0 : 1;
+ }
+
+ w = ((mp_word) MP_DIGIT_MAX + 1 + hi) - w - (mp_word)*zp;
+ *zp = LOWER_HALF(w);
+
+ assert(UPPER_HALF(w) != 0); /* no borrow out should be possible */
+
+ MP_SIGN(z) = MP_ZPOS;
+ CLAMP(z);
+
+ return 1;
+}
+
+/* }}} */
+
+/* {{{ s_dp2k(z) */
+
+static int s_dp2k(mp_int z)
+{
+ int k = 0;
+ mp_digit *dp = MP_DIGITS(z), d;
+
+ if(MP_USED(z) == 1 && *dp == 0)
+ return 1;
+
+ while(*dp == 0) {
+ k += MP_DIGIT_BIT;
+ ++dp;
+ }
+
+ d = *dp;
+ while((d & 1) == 0) {
+ d >>= 1;
+ ++k;
+ }
+
+ return k;
+}
+
+/* }}} */
+
+/* {{{ s_isp2(z) */
+
+static int s_isp2(mp_int z)
+{
+ mp_size uz = MP_USED(z), k = 0;
+ mp_digit *dz = MP_DIGITS(z), d;
+
+ while(uz > 1) {
+ if(*dz++ != 0)
+ return -1;
+ k += MP_DIGIT_BIT;
+ --uz;
+ }
+
+ d = *dz;
+ while(d > 1) {
+ if(d & 1)
+ return -1;
+ ++k; d >>= 1;
+ }
+
+ return (int) k;
+}
+
+/* }}} */
+
+/* {{{ s_2expt(z, k) */
+
+static int s_2expt(mp_int z, mp_small k)
+{
+ mp_size ndig, rest;
+ mp_digit *dz;
+
+ ndig = (k + MP_DIGIT_BIT) / MP_DIGIT_BIT;
+ rest = k % MP_DIGIT_BIT;
+
+ if(!s_pad(z, ndig))
+ return 0;
+
+ dz = MP_DIGITS(z);
+ ZERO(dz, ndig);
+ *(dz + ndig - 1) = (1 << rest);
+ MP_USED(z) = ndig;
+
+ return 1;
+}
+
+/* }}} */
+
+/* {{{ s_norm(a, b) */
+
+static int s_norm(mp_int a, mp_int b)
+{
+ mp_digit d = b->digits[MP_USED(b) - 1];
+ int k = 0;
+
+ while(d < (mp_digit) (1 << (MP_DIGIT_BIT - 1))) { /* d < (MP_RADIX / 2) */
+ d <<= 1;
+ ++k;
+ }
+
+ /* These multiplications can't fail */
+ if(k != 0) {
+ (void) s_qmul(a, (mp_size) k);
+ (void) s_qmul(b, (mp_size) k);
+ }
+
+ return k;
+}
+
+/* }}} */
+
+/* {{{ s_brmu(z, m) */
+
+static mp_result s_brmu(mp_int z, mp_int m)
+{
+ mp_size um = MP_USED(m) * 2;
+
+ if(!s_pad(z, um))
+ return MP_MEMORY;
+
+ s_2expt(z, MP_DIGIT_BIT * um);
+ return mp_int_div(z, m, z, NULL);
+}
+
+/* }}} */
+
+/* {{{ s_reduce(x, m, mu, q1, q2) */
+
+static int s_reduce(mp_int x, mp_int m, mp_int mu, mp_int q1, mp_int q2)
+{
+ mp_size um = MP_USED(m), umb_p1, umb_m1;
+
+ umb_p1 = (um + 1) * MP_DIGIT_BIT;
+ umb_m1 = (um - 1) * MP_DIGIT_BIT;
+
+ if(mp_int_copy(x, q1) != MP_OK)
+ return 0;
+
+ /* Compute q2 = floor((floor(x / b^(k-1)) * mu) / b^(k+1)) */
+ s_qdiv(q1, umb_m1);
+ UMUL(q1, mu, q2);
+ s_qdiv(q2, umb_p1);
+
+ /* Set x = x mod b^(k+1) */
+ s_qmod(x, umb_p1);
+
+ /* Now, q is a guess for the quotient a / m.
+ Compute x - q * m mod b^(k+1), replacing x. This may be off
+ by a factor of 2m, but no more than that.
+ */
+ UMUL(q2, m, q1);
+ s_qmod(q1, umb_p1);
+ (void) mp_int_sub(x, q1, x); /* can't fail */
+
+ /* The result may be < 0; if it is, add b^(k+1) to pin it in the
+ proper range. */
+ if((CMPZ(x) < 0) && !s_qsub(x, umb_p1))
+ return 0;
+
+ /* If x > m, we need to back it off until it is in range.
+ This will be required at most twice. */
+ if(mp_int_compare(x, m) >= 0) {
+ (void) mp_int_sub(x, m, x);
+ if(mp_int_compare(x, m) >= 0)
+ (void) mp_int_sub(x, m, x);
+ }
+
+ /* At this point, x has been properly reduced. */
+ return 1;
+}
+
+/* }}} */
+
+/* {{{ s_embar(a, b, m, mu, c) */
+
+/* Perform modular exponentiation using Barrett's method, where mu is
+ the reduction constant for m. Assumes a < m, b > 0. */
+static mp_result s_embar(mp_int a, mp_int b, mp_int m, mp_int mu, mp_int c)
+{
+ mp_digit *db, *dbt, umu, d;
+ mpz_t temp[3];
+ mp_result res;
+ int last = 0;
+
+ umu = MP_USED(mu); db = MP_DIGITS(b); dbt = db + MP_USED(b) - 1;
+
+ while(last < 3) {
+ SETUP(mp_int_init_size(TEMP(last), 4 * umu), last);
+ ZERO(MP_DIGITS(TEMP(last - 1)), MP_ALLOC(TEMP(last - 1)));
+ }
+
+ (void) mp_int_set_value(c, 1);
+
+ /* Take care of low-order digits */
+ while(db < dbt) {
+ int i;
+
+ for(d = *db, i = MP_DIGIT_BIT; i > 0; --i, d >>= 1) {
+ if(d & 1) {
+ /* The use of a second temporary avoids allocation */
+ UMUL(c, a, TEMP(0));
+ if(!s_reduce(TEMP(0), m, mu, TEMP(1), TEMP(2))) {
+ res = MP_MEMORY; goto CLEANUP;
+ }
+ mp_int_copy(TEMP(0), c);
+ }
+
+
+ USQR(a, TEMP(0));
+ assert(MP_SIGN(TEMP(0)) == MP_ZPOS);
+ if(!s_reduce(TEMP(0), m, mu, TEMP(1), TEMP(2))) {
+ res = MP_MEMORY; goto CLEANUP;
+ }
+ assert(MP_SIGN(TEMP(0)) == MP_ZPOS);
+ mp_int_copy(TEMP(0), a);
+
+
+ }
+
+ ++db;
+ }
+
+ /* Take care of highest-order digit */
+ d = *dbt;
+ for(;;) {
+ if(d & 1) {
+ UMUL(c, a, TEMP(0));
+ if(!s_reduce(TEMP(0), m, mu, TEMP(1), TEMP(2))) {
+ res = MP_MEMORY; goto CLEANUP;
+ }
+ mp_int_copy(TEMP(0), c);
+ }
+
+ d >>= 1;
+ if(!d) break;
+
+ USQR(a, TEMP(0));
+ if(!s_reduce(TEMP(0), m, mu, TEMP(1), TEMP(2))) {
+ res = MP_MEMORY; goto CLEANUP;
+ }
+ (void) mp_int_copy(TEMP(0), a);
+ }
+
+ CLEANUP:
+ while(--last >= 0)
+ mp_int_clear(TEMP(last));
+
+ return res;
+}
+
+/* }}} */
+
+/* {{{ s_udiv(a, b) */
+
+/* Precondition: a >= b and b > 0
+ Postcondition: a' = a / b, b' = a % b
+ */
+static mp_result s_udiv(mp_int a, mp_int b)
+{
+ mpz_t q, r, t;
+ mp_size ua, ub, qpos = 0;
+ mp_digit *da, btop;
+ mp_result res = MP_OK;
+ int k, skip = 0;
+
+ /* Force signs to positive */
+ MP_SIGN(a) = MP_ZPOS;
+ MP_SIGN(b) = MP_ZPOS;
+
+ /* Normalize, per Knuth */
+ k = s_norm(a, b);
+
+ ua = MP_USED(a); ub = MP_USED(b); btop = b->digits[ub - 1];
+ if((res = mp_int_init_size(&q, ua)) != MP_OK) return res;
+ if((res = mp_int_init_size(&t, ua + 1)) != MP_OK) goto CLEANUP;
+
+ da = MP_DIGITS(a);
+ r.digits = da + ua - 1; /* The contents of r are shared with a */
+ r.used = 1;
+ r.sign = MP_ZPOS;
+ r.alloc = MP_ALLOC(a);
+ ZERO(t.digits, t.alloc);
+
+ /* Solve for quotient digits, store in q.digits in reverse order */
+ while(r.digits >= da) {
+ assert(qpos <= q.alloc);
+
+ if(s_ucmp(b, &r) > 0) {
+ r.digits -= 1;
+ r.used += 1;
+
+ if(++skip > 1 && qpos > 0)
+ q.digits[qpos++] = 0;
+
+ CLAMP(&r);
+ }
+ else {
+ mp_word pfx = r.digits[r.used - 1];
+ mp_word qdigit;
+
+ if(r.used > 1 && pfx <= btop) {
+ pfx <<= MP_DIGIT_BIT / 2;
+ pfx <<= MP_DIGIT_BIT / 2;
+ pfx |= r.digits[r.used - 2];
+ }
+
+ qdigit = pfx / btop;
+ if(qdigit > MP_DIGIT_MAX) {
+ if(qdigit & MP_DIGIT_MAX)
+ qdigit = MP_DIGIT_MAX;
+ else
+ qdigit = 1;
+ }
+
+ s_dbmul(MP_DIGITS(b), (mp_digit) qdigit, t.digits, ub);
+ t.used = ub + 1; CLAMP(&t);
+ while(s_ucmp(&t, &r) > 0) {
+ --qdigit;
+ (void) mp_int_sub(&t, b, &t); /* cannot fail */
+ }
+
+ s_usub(r.digits, t.digits, r.digits, r.used, t.used);
+ CLAMP(&r);
+
+ q.digits[qpos++] = (mp_digit) qdigit;
+ ZERO(t.digits, t.used);
+ skip = 0;
+ }
+ }
+
+ /* Put quotient digits in the correct order, and discard extra zeroes */
+ q.used = qpos;
+ REV(mp_digit, q.digits, qpos);
+ CLAMP(&q);
+
+ /* Denormalize the remainder */
+ CLAMP(a);
+ if(k != 0)
+ s_qdiv(a, k);
+
+ mp_int_copy(a, b); /* ok: 0 <= r < b */
+ mp_int_copy(&q, a); /* ok: q <= a */
+
+ mp_int_clear(&t);
+ CLEANUP:
+ mp_int_clear(&q);
+ return res;
+}
+
+/* }}} */
+
+/* {{{ s_outlen(z, r) */
+
+static int s_outlen(mp_int z, mp_size r)
+{
+ mp_result bits;
+ double raw;
+
+ assert(r >= MP_MIN_RADIX && r <= MP_MAX_RADIX);
+
+ bits = mp_int_count_bits(z);
+ raw = (double)bits * s_log2[r];
+
+ return (int)(raw + 0.999999);
+}
+
+/* }}} */
+
+/* {{{ s_inlen(len, r) */
+
+static mp_size s_inlen(int len, mp_size r)
+{
+ double raw = (double)len / s_log2[r];
+ mp_size bits = (mp_size)(raw + 0.5);
+
+ return (mp_size)((bits + (MP_DIGIT_BIT - 1)) / MP_DIGIT_BIT);
+}
+
+/* }}} */
+
+/* {{{ s_ch2val(c, r) */
+
+static int s_ch2val(char c, int r)
+{
+ int out;
+
+ if(isdigit((unsigned char) c))
+ out = c - '0';
+ else if(r > 10 && isalpha((unsigned char) c))
+ out = toupper(c) - 'A' + 10;
+ else
+ return -1;
+
+ return (out >= r) ? -1 : out;
+}
+
+/* }}} */
+
+/* {{{ s_val2ch(v, caps) */
+
+static char s_val2ch(int v, int caps)
+{
+ assert(v >= 0);
+
+ if(v < 10)
+ return v + '0';
+ else {
+ char out = (v - 10) + 'a';
+
+ if(caps)
+ return toupper(out);
+ else
+ return out;
+ }
+}
+
+/* }}} */
+
+/* {{{ s_2comp(buf, len) */
+
+static void s_2comp(unsigned char *buf, int len)
+{
+ int i;
+ unsigned short s = 1;
+
+ for(i = len - 1; i >= 0; --i) {
+ unsigned char c = ~buf[i];
+
+ s = c + s;
+ c = s & UCHAR_MAX;
+ s >>= CHAR_BIT;
+
+ buf[i] = c;
+ }
+
+ /* last carry out is ignored */
+}
+
+/* }}} */
+
+/* {{{ s_tobin(z, buf, *limpos) */
+
+static mp_result s_tobin(mp_int z, unsigned char *buf, int *limpos, int pad)
+{
+ mp_size uz;
+ mp_digit *dz;
+ int pos = 0, limit = *limpos;
+
+ uz = MP_USED(z); dz = MP_DIGITS(z);
+ while(uz > 0 && pos < limit) {
+ mp_digit d = *dz++;
+ int i;
+
+ for(i = sizeof(mp_digit); i > 0 && pos < limit; --i) {
+ buf[pos++] = (unsigned char)d;
+ d >>= CHAR_BIT;
+
+ /* Don't write leading zeroes */
+ if(d == 0 && uz == 1)
+ i = 0; /* exit loop without signaling truncation */
+ }
+
+ /* Detect truncation (loop exited with pos >= limit) */
+ if(i > 0) break;
+
+ --uz;
+ }
+
+ if(pad != 0 && (buf[pos - 1] >> (CHAR_BIT - 1))) {
+ if(pos < limit)
+ buf[pos++] = 0;
+ else
+ uz = 1;
+ }
+
+ /* Digits are in reverse order, fix that */
+ REV(unsigned char, buf, pos);
+
+ /* Return the number of bytes actually written */
+ *limpos = pos;
+
+ return (uz == 0) ? MP_OK : MP_TRUNC;
+}
+
+/* }}} */
+
+/* {{{ s_print(tag, z) */
+
+#if DEBUG
+void s_print(char *tag, mp_int z)
+{
+ int i;
+
+ fprintf(stderr, "%s: %c ", tag,
+ (MP_SIGN(z) == MP_NEG) ? '-' : '+');
+
+ for(i = MP_USED(z) - 1; i >= 0; --i)
+ fprintf(stderr, "%0*X", (int)(MP_DIGIT_BIT / 4), z->digits[i]);
+
+ fputc('\n', stderr);
+
+}
+
+void s_print_buf(char *tag, mp_digit *buf, mp_size num)
+{
+ int i;
+
+ fprintf(stderr, "%s: ", tag);
+
+ for(i = num - 1; i >= 0; --i)
+ fprintf(stderr, "%0*X", (int)(MP_DIGIT_BIT / 4), buf[i]);
+
+ fputc('\n', stderr);
+}
+#endif
+
+/* }}} */
+
+/* HERE THERE BE DRAGONS */
diff --git a/source4/heimdal/lib/hcrypto/imath/imath.h b/source4/heimdal/lib/hcrypto/imath/imath.h
new file mode 100644
index 0000000000..62b51c8fd8
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/imath/imath.h
@@ -0,0 +1,231 @@
+/*
+ Name: imath.h
+ Purpose: Arbitrary precision integer arithmetic routines.
+ Author: M. J. Fromberger <http://spinning-yarns.org/michael/>
+ Info: $Id: imath.h 635 2008-01-08 18:19:40Z sting $
+
+ Copyright (C) 2002-2007 Michael J. Fromberger, All Rights Reserved.
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation files
+ (the "Software"), to deal in the Software without restriction,
+ including without limitation the rights to use, copy, modify, merge,
+ publish, distribute, sublicense, and/or sell copies of the Software,
+ and to permit persons to whom the Software is furnished to do so,
+ subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+ */
+
+#ifndef IMATH_H_
+#define IMATH_H_
+
+#include <limits.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef unsigned char mp_sign;
+typedef unsigned int mp_size;
+typedef int mp_result;
+typedef long mp_small; /* must be a signed type */
+typedef unsigned long mp_usmall; /* must be an unsigned type */
+#ifdef USE_LONG_LONG
+typedef unsigned int mp_digit;
+typedef unsigned long long mp_word;
+#else
+typedef unsigned short mp_digit;
+typedef unsigned int mp_word;
+#endif
+
+typedef struct mpz {
+ mp_digit single;
+ mp_digit *digits;
+ mp_size alloc;
+ mp_size used;
+ mp_sign sign;
+} mpz_t, *mp_int;
+
+#define MP_DIGITS(Z) ((Z)->digits)
+#define MP_ALLOC(Z) ((Z)->alloc)
+#define MP_USED(Z) ((Z)->used)
+#define MP_SIGN(Z) ((Z)->sign)
+
+extern const mp_result MP_OK;
+extern const mp_result MP_FALSE;
+extern const mp_result MP_TRUE;
+extern const mp_result MP_MEMORY;
+extern const mp_result MP_RANGE;
+extern const mp_result MP_UNDEF;
+extern const mp_result MP_TRUNC;
+extern const mp_result MP_BADARG;
+extern const mp_result MP_MINERR;
+
+#define MP_DIGIT_BIT (sizeof(mp_digit) * CHAR_BIT)
+#define MP_WORD_BIT (sizeof(mp_word) * CHAR_BIT)
+#define MP_SMALL_MIN LONG_MIN
+#define MP_SMALL_MAX LONG_MAX
+#define MP_USMALL_MIN ULONG_MIN
+#define MP_USMALL_MAX ULONG_MAX
+
+#ifdef USE_LONG_LONG
+# ifndef ULONG_LONG_MAX
+# ifdef ULLONG_MAX
+# define ULONG_LONG_MAX ULLONG_MAX
+# else
+# error "Maximum value of unsigned long long not defined!"
+# endif
+# endif
+# define MP_DIGIT_MAX (ULONG_MAX * 1ULL)
+# define MP_WORD_MAX ULONG_LONG_MAX
+#else
+# define MP_DIGIT_MAX (USHRT_MAX * 1UL)
+# define MP_WORD_MAX (UINT_MAX * 1UL)
+#endif
+
+#define MP_MIN_RADIX 2
+#define MP_MAX_RADIX 36
+
+/* Values with fewer than this many significant digits use the
+ standard multiplication algorithm; otherwise, a recursive algorithm
+ is used. Choose a value to suit your platform.
+ */
+#define MP_MULT_THRESH 22
+
+#define MP_DEFAULT_PREC 8 /* default memory allocation, in digits */
+
+extern const mp_sign MP_NEG;
+extern const mp_sign MP_ZPOS;
+
+#define mp_int_is_odd(Z) ((Z)->digits[0] & 1)
+#define mp_int_is_even(Z) !((Z)->digits[0] & 1)
+
+mp_result mp_int_init(mp_int z);
+mp_int mp_int_alloc(void);
+mp_result mp_int_init_size(mp_int z, mp_size prec);
+mp_result mp_int_init_copy(mp_int z, mp_int old);
+mp_result mp_int_init_value(mp_int z, mp_small value);
+mp_result mp_int_set_value(mp_int z, mp_small value);
+void mp_int_clear(mp_int z);
+void mp_int_free(mp_int z);
+
+mp_result mp_int_copy(mp_int a, mp_int c); /* c = a */
+void mp_int_swap(mp_int a, mp_int c); /* swap a, c */
+void mp_int_zero(mp_int z); /* z = 0 */
+mp_result mp_int_abs(mp_int a, mp_int c); /* c = |a| */
+mp_result mp_int_neg(mp_int a, mp_int c); /* c = -a */
+mp_result mp_int_add(mp_int a, mp_int b, mp_int c); /* c = a + b */
+mp_result mp_int_add_value(mp_int a, mp_small value, mp_int c);
+mp_result mp_int_sub(mp_int a, mp_int b, mp_int c); /* c = a - b */
+mp_result mp_int_sub_value(mp_int a, mp_small value, mp_int c);
+mp_result mp_int_mul(mp_int a, mp_int b, mp_int c); /* c = a * b */
+mp_result mp_int_mul_value(mp_int a, mp_small value, mp_int c);
+mp_result mp_int_mul_pow2(mp_int a, mp_small p2, mp_int c);
+mp_result mp_int_sqr(mp_int a, mp_int c); /* c = a * a */
+mp_result mp_int_div(mp_int a, mp_int b, /* q = a / b */
+ mp_int q, mp_int r); /* r = a % b */
+mp_result mp_int_div_value(mp_int a, mp_small value, /* q = a / value */
+ mp_int q, mp_small *r); /* r = a % value */
+mp_result mp_int_div_pow2(mp_int a, mp_small p2, /* q = a / 2^p2 */
+ mp_int q, mp_int r); /* r = q % 2^p2 */
+mp_result mp_int_mod(mp_int a, mp_int m, mp_int c); /* c = a % m */
+#define mp_int_mod_value(A, V, R) mp_int_div_value((A), (V), 0, (R))
+mp_result mp_int_expt(mp_int a, mp_small b, mp_int c); /* c = a^b */
+mp_result mp_int_expt_value(mp_small a, mp_small b, mp_int c); /* c = a^b */
+
+int mp_int_compare(mp_int a, mp_int b); /* a <=> b */
+int mp_int_compare_unsigned(mp_int a, mp_int b); /* |a| <=> |b| */
+int mp_int_compare_zero(mp_int z); /* a <=> 0 */
+int mp_int_compare_value(mp_int z, mp_small value); /* a <=> v */
+
+/* Returns true if v|a, false otherwise (including errors) */
+int mp_int_divisible_value(mp_int a, mp_small v);
+
+/* Returns k >= 0 such that z = 2^k, if one exists; otherwise < 0 */
+int mp_int_is_pow2(mp_int z);
+
+mp_result mp_int_exptmod(mp_int a, mp_int b, mp_int m,
+ mp_int c); /* c = a^b (mod m) */
+mp_result mp_int_exptmod_evalue(mp_int a, mp_small value,
+ mp_int m, mp_int c); /* c = a^v (mod m) */
+mp_result mp_int_exptmod_bvalue(mp_small value, mp_int b,
+ mp_int m, mp_int c); /* c = v^b (mod m) */
+mp_result mp_int_exptmod_known(mp_int a, mp_int b,
+ mp_int m, mp_int mu,
+ mp_int c); /* c = a^b (mod m) */
+mp_result mp_int_redux_const(mp_int m, mp_int c);
+
+mp_result mp_int_invmod(mp_int a, mp_int m, mp_int c); /* c = 1/a (mod m) */
+
+mp_result mp_int_gcd(mp_int a, mp_int b, mp_int c); /* c = gcd(a, b) */
+
+mp_result mp_int_egcd(mp_int a, mp_int b, mp_int c, /* c = gcd(a, b) */
+ mp_int x, mp_int y); /* c = ax + by */
+
+mp_result mp_int_lcm(mp_int a, mp_int b, mp_int c); /* c = lcm(a, b) */
+
+mp_result mp_int_root(mp_int a, mp_small b, mp_int c); /* c = floor(a^{1/b}) */
+#define mp_int_sqrt(a, c) mp_int_root(a, 2, c) /* c = floor(sqrt(a)) */
+
+/* Convert to a small int, if representable; else MP_RANGE */
+mp_result mp_int_to_int(mp_int z, mp_small *out);
+mp_result mp_int_to_uint(mp_int z, mp_usmall *out);
+
+/* Convert to nul-terminated string with the specified radix, writing at
+ most limit characters including the nul terminator */
+mp_result mp_int_to_string(mp_int z, mp_size radix,
+ char *str, int limit);
+
+/* Return the number of characters required to represent
+ z in the given radix. May over-estimate. */
+mp_result mp_int_string_len(mp_int z, mp_size radix);
+
+/* Read zero-terminated string into z */
+mp_result mp_int_read_string(mp_int z, mp_size radix, const char *str);
+mp_result mp_int_read_cstring(mp_int z, mp_size radix, const char *str,
+ char **end);
+
+/* Return the number of significant bits in z */
+mp_result mp_int_count_bits(mp_int z);
+
+/* Convert z to two's complement binary, writing at most limit bytes */
+mp_result mp_int_to_binary(mp_int z, unsigned char *buf, int limit);
+
+/* Read a two's complement binary value into z from the given buffer */
+mp_result mp_int_read_binary(mp_int z, unsigned char *buf, int len);
+
+/* Return the number of bytes required to represent z in binary. */
+mp_result mp_int_binary_len(mp_int z);
+
+/* Convert z to unsigned binary, writing at most limit bytes */
+mp_result mp_int_to_unsigned(mp_int z, unsigned char *buf, int limit);
+
+/* Read an unsigned binary value into z from the given buffer */
+mp_result mp_int_read_unsigned(mp_int z, unsigned char *buf, int len);
+
+/* Return the number of bytes required to represent z as unsigned output */
+mp_result mp_int_unsigned_len(mp_int z);
+
+/* Return a statically allocated string describing error code res */
+const char *mp_error_string(mp_result res);
+
+#if DEBUG
+void s_print(char *tag, mp_int z);
+void s_print_buf(char *tag, mp_digit *buf, mp_size num);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* end IMATH_H_ */
diff --git a/source4/heimdal/lib/hcrypto/imath/iprime.c b/source4/heimdal/lib/hcrypto/imath/iprime.c
new file mode 100644
index 0000000000..2bc9e7a6d1
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/imath/iprime.c
@@ -0,0 +1,189 @@
+/*
+ Name: iprime.c
+ Purpose: Pseudoprimality testing routines
+ Author: M. J. Fromberger <http://spinning-yarns.org/michael/>
+ Info: $Id: iprime.c 635 2008-01-08 18:19:40Z sting $
+
+ Copyright (C) 2002-2008 Michael J. Fromberger, All Rights Reserved.
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation files
+ (the "Software"), to deal in the Software without restriction,
+ including without limitation the rights to use, copy, modify, merge,
+ publish, distribute, sublicense, and/or sell copies of the Software,
+ and to permit persons to whom the Software is furnished to do so,
+ subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+ */
+
+#include "iprime.h"
+#include <stdlib.h>
+
+static const int s_ptab[] = {
+ 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43,
+ 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101,
+ 103, 107, 109, 113, 127, 131, 137, 139, 149, 151,
+ 157, 163, 167, 173, 179, 181, 191, 193, 197, 199,
+ 211, 223, 227, 229, 233, 239, 241, 251, 257, 263,
+ 269, 271, 277, 281, 283, 293, 307, 311, 313, 317,
+ 331, 337, 347, 349, 353, 359, 367, 373, 379, 383,
+ 389, 397, 401, 409, 419, 421, 431, 433, 439, 443,
+ 449, 457, 461, 463, 467, 479, 487, 491, 499, 503,
+ 509, 521, 523, 541, 547, 557, 563, 569, 571, 577,
+ 587, 593, 599, 601, 607, 613, 617, 619, 631, 641,
+ 643, 647, 653, 659, 661, 673, 677, 683, 691, 701,
+ 709, 719, 727, 733, 739, 743, 751, 757, 761, 769,
+ 773, 787, 797, 809, 811, 821, 823, 827, 829, 839,
+ 853, 857, 859, 863, 877, 881, 883, 887, 907, 911,
+ 919, 929, 937, 941, 947, 953, 967, 971, 977, 983,
+ 991, 997
+#ifdef IMATH_LARGE_PRIME_TABLE
+ , 1009, 1013, 1019, 1021, 1031, 1033,
+ 1039, 1049, 1051, 1061, 1063, 1069, 1087, 1091,
+ 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151,
+ 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213,
+ 1217, 1223, 1229, 1231, 1237, 1249, 1259, 1277,
+ 1279, 1283, 1289, 1291, 1297, 1301, 1303, 1307,
+ 1319, 1321, 1327, 1361, 1367, 1373, 1381, 1399,
+ 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451,
+ 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493,
+ 1499, 1511, 1523, 1531, 1543, 1549, 1553, 1559,
+ 1567, 1571, 1579, 1583, 1597, 1601, 1607, 1609,
+ 1613, 1619, 1621, 1627, 1637, 1657, 1663, 1667,
+ 1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733,
+ 1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789,
+ 1801, 1811, 1823, 1831, 1847, 1861, 1867, 1871,
+ 1873, 1877, 1879, 1889, 1901, 1907, 1913, 1931,
+ 1933, 1949, 1951, 1973, 1979, 1987, 1993, 1997,
+ 1999, 2003, 2011, 2017, 2027, 2029, 2039, 2053,
+ 2063, 2069, 2081, 2083, 2087, 2089, 2099, 2111,
+ 2113, 2129, 2131, 2137, 2141, 2143, 2153, 2161,
+ 2179, 2203, 2207, 2213, 2221, 2237, 2239, 2243,
+ 2251, 2267, 2269, 2273, 2281, 2287, 2293, 2297,
+ 2309, 2311, 2333, 2339, 2341, 2347, 2351, 2357,
+ 2371, 2377, 2381, 2383, 2389, 2393, 2399, 2411,
+ 2417, 2423, 2437, 2441, 2447, 2459, 2467, 2473,
+ 2477, 2503, 2521, 2531, 2539, 2543, 2549, 2551,
+ 2557, 2579, 2591, 2593, 2609, 2617, 2621, 2633,
+ 2647, 2657, 2659, 2663, 2671, 2677, 2683, 2687,
+ 2689, 2693, 2699, 2707, 2711, 2713, 2719, 2729,
+ 2731, 2741, 2749, 2753, 2767, 2777, 2789, 2791,
+ 2797, 2801, 2803, 2819, 2833, 2837, 2843, 2851,
+ 2857, 2861, 2879, 2887, 2897, 2903, 2909, 2917,
+ 2927, 2939, 2953, 2957, 2963, 2969, 2971, 2999,
+ 3001, 3011, 3019, 3023, 3037, 3041, 3049, 3061,
+ 3067, 3079, 3083, 3089, 3109, 3119, 3121, 3137,
+ 3163, 3167, 3169, 3181, 3187, 3191, 3203, 3209,
+ 3217, 3221, 3229, 3251, 3253, 3257, 3259, 3271,
+ 3299, 3301, 3307, 3313, 3319, 3323, 3329, 3331,
+ 3343, 3347, 3359, 3361, 3371, 3373, 3389, 3391,
+ 3407, 3413, 3433, 3449, 3457, 3461, 3463, 3467,
+ 3469, 3491, 3499, 3511, 3517, 3527, 3529, 3533,
+ 3539, 3541, 3547, 3557, 3559, 3571, 3581, 3583,
+ 3593, 3607, 3613, 3617, 3623, 3631, 3637, 3643,
+ 3659, 3671, 3673, 3677, 3691, 3697, 3701, 3709,
+ 3719, 3727, 3733, 3739, 3761, 3767, 3769, 3779,
+ 3793, 3797, 3803, 3821, 3823, 3833, 3847, 3851,
+ 3853, 3863, 3877, 3881, 3889, 3907, 3911, 3917,
+ 3919, 3923, 3929, 3931, 3943, 3947, 3967, 3989,
+ 4001, 4003, 4007, 4013, 4019, 4021, 4027, 4049,
+ 4051, 4057, 4073, 4079, 4091, 4093, 4099, 4111,
+ 4127, 4129, 4133, 4139, 4153, 4157, 4159, 4177,
+ 4201, 4211, 4217, 4219, 4229, 4231, 4241, 4243,
+ 4253, 4259, 4261, 4271, 4273, 4283, 4289, 4297,
+ 4327, 4337, 4339, 4349, 4357, 4363, 4373, 4391,
+ 4397, 4409, 4421, 4423, 4441, 4447, 4451, 4457,
+ 4463, 4481, 4483, 4493, 4507, 4513, 4517, 4519,
+ 4523, 4547, 4549, 4561, 4567, 4583, 4591, 4597,
+ 4603, 4621, 4637, 4639, 4643, 4649, 4651, 4657,
+ 4663, 4673, 4679, 4691, 4703, 4721, 4723, 4729,
+ 4733, 4751, 4759, 4783, 4787, 4789, 4793, 4799,
+ 4801, 4813, 4817, 4831, 4861, 4871, 4877, 4889,
+ 4903, 4909, 4919, 4931, 4933, 4937, 4943, 4951,
+ 4957, 4967, 4969, 4973, 4987, 4993, 4999
+#endif
+};
+static const int s_ptab_size = sizeof(s_ptab)/sizeof(s_ptab[0]);
+
+/* {{{ mp_int_is_prime(z) */
+
+/* Test whether z is likely to be prime:
+ MP_TRUE means it is probably prime
+ MP_FALSE means it is definitely composite
+ */
+mp_result mp_int_is_prime(mp_int z)
+{
+ int i;
+ mp_small rem;
+ mp_result res;
+
+ /* First check for divisibility by small primes; this eliminates a
+ large number of composite candidates quickly
+ */
+ for(i = 0; i < s_ptab_size; ++i) {
+ if((res = mp_int_div_value(z, s_ptab[i], NULL, &rem)) != MP_OK)
+ return res;
+
+ if(rem == 0)
+ return MP_FALSE;
+ }
+
+ /* Now try Fermat's test for several prime witnesses (since we now
+ know from the above that z is not a multiple of any of them)
+ */
+ {
+ mpz_t tmp;
+
+ if((res = mp_int_init(&tmp)) != MP_OK) return res;
+
+ for(i = 0; i < 10 && i < s_ptab_size; ++i) {
+ if((res = mp_int_exptmod_bvalue(s_ptab[i], z, z, &tmp)) != MP_OK)
+ return res;
+
+ if(mp_int_compare_value(&tmp, s_ptab[i]) != 0) {
+ mp_int_clear(&tmp);
+ return MP_FALSE;
+ }
+ }
+
+ mp_int_clear(&tmp);
+ }
+
+ return MP_TRUE;
+}
+
+/* }}} */
+
+/* {{{ mp_int_find_prime(z) */
+
+/* Find the first apparent prime in ascending order from z */
+mp_result mp_int_find_prime(mp_int z)
+{
+ mp_result res;
+
+ if(mp_int_is_even(z) && ((res = mp_int_add_value(z, 1, z)) != MP_OK))
+ return res;
+
+ while((res = mp_int_is_prime(z)) == MP_FALSE) {
+ if((res = mp_int_add_value(z, 2, z)) != MP_OK)
+ break;
+
+ }
+
+ return res;
+}
+
+/* }}} */
+
+/* Here there be dragons */
diff --git a/source4/heimdal/lib/hcrypto/imath/iprime.h b/source4/heimdal/lib/hcrypto/imath/iprime.h
new file mode 100644
index 0000000000..6110dccb55
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/imath/iprime.h
@@ -0,0 +1,51 @@
+/*
+ Name: iprime.h
+ Purpose: Pseudoprimality testing routines
+ Author: M. J. Fromberger <http://spinning-yarns.org/michael/>
+ Info: $Id: iprime.h 635 2008-01-08 18:19:40Z sting $
+
+ Copyright (C) 2002-2008 Michael J. Fromberger, All Rights Reserved.
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation files
+ (the "Software"), to deal in the Software without restriction,
+ including without limitation the rights to use, copy, modify, merge,
+ publish, distribute, sublicense, and/or sell copies of the Software,
+ and to permit persons to whom the Software is furnished to do so,
+ subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+ */
+
+#ifndef IPRIME_H_
+#define IPRIME_H_
+
+#include "imath.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Test whether z is likely to be prime
+ MP_YES means it is probably prime
+ MP_NO means it is definitely composite
+ */
+mp_result mp_int_is_prime(mp_int z);
+
+/* Find the first apparent prime in ascending order from z */
+mp_result mp_int_find_prime(mp_int z);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* IPRIME_H_ */
diff --git a/source4/heimdal/lib/hcrypto/md2.c b/source4/heimdal/lib/hcrypto/md2.c
new file mode 100644
index 0000000000..e82169c705
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/md2.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+
+RCSID("$Id$");
+#endif
+
+#include "hash.h"
+#include "md2.h"
+
+static const unsigned char subst[256] = {
+ 41, 46, 67, 201, 162, 216, 124, 1, 61, 54, 84, 161, 236, 240, 6,
+ 19, 98, 167, 5, 243, 192, 199, 115, 140, 152, 147, 43, 217, 188,
+ 76, 130, 202, 30, 155, 87, 60, 253, 212, 224, 22, 103, 66, 111, 24,
+ 138, 23, 229, 18, 190, 78, 196, 214, 218, 158, 222, 73, 160, 251,
+ 245, 142, 187, 47, 238, 122, 169, 104, 121, 145, 21, 178, 7, 63,
+ 148, 194, 16, 137, 11, 34, 95, 33, 128, 127, 93, 154, 90, 144, 50,
+ 39, 53, 62, 204, 231, 191, 247, 151, 3, 255, 25, 48, 179, 72, 165,
+ 181, 209, 215, 94, 146, 42, 172, 86, 170, 198, 79, 184, 56, 210,
+ 150, 164, 125, 182, 118, 252, 107, 226, 156, 116, 4, 241, 69, 157,
+ 112, 89, 100, 113, 135, 32, 134, 91, 207, 101, 230, 45, 168, 2, 27,
+ 96, 37, 173, 174, 176, 185, 246, 28, 70, 97, 105, 52, 64, 126, 15,
+ 85, 71, 163, 35, 221, 81, 175, 58, 195, 92, 249, 206, 186, 197,
+ 234, 38, 44, 83, 13, 110, 133, 40, 132, 9, 211, 223, 205, 244, 65,
+ 129, 77, 82, 106, 220, 55, 200, 108, 193, 171, 250, 36, 225, 123,
+ 8, 12, 189, 177, 74, 120, 136, 149, 139, 227, 99, 232, 109, 233,
+ 203, 213, 254, 59, 0, 29, 57, 242, 239, 183, 14, 102, 88, 208, 228,
+ 166, 119, 114, 248, 235, 117, 75, 10, 49, 68, 80, 180, 143, 237,
+ 31, 26, 219, 153, 141, 51, 159, 17, 131, 20
+};
+
+void
+MD2_Init (struct md2 *m)
+{
+ memset(m, 0, sizeof(*m));
+}
+
+static void
+calc(struct md2 *m, const void *v)
+{
+ unsigned char x[48], L;
+ const unsigned char *p = v;
+ int i, j, t;
+
+ L = m->checksum[15];
+ for (i = 0; i < 16; i++)
+ L = m->checksum[i] ^= subst[p[i] ^ L];
+
+ for (i = 0; i < 16; i++) {
+ x[i] = m->state[i];
+ x[i + 16] = p[i];
+ x[i + 32] = x[i] ^ p[i];
+ }
+
+ t = 0;
+ for (i = 0; i < 18; i++) {
+ for (j = 0; j < 48; j++)
+ t = x[j] ^= subst[t];
+ t = (t + i) & 0xff;
+ }
+
+ memcpy(m->state, x, 16);
+ memset(x, 0, sizeof(x));
+}
+
+void
+MD2_Update (struct md2 *m, const void *v, size_t len)
+{
+ size_t idx = m->len & 0xf;
+ const unsigned char *p = v;
+
+ m->len += len;
+ if (len + idx >= 16) {
+ if (idx) {
+ memcpy(m->data + idx, p, 16 - idx);
+ calc(m, m->data);
+ p += 16;
+ len -= 16 - idx;
+ }
+ while (len >= 16) {
+ calc(m, p);
+ p += 16;
+ len -= 16;
+ }
+ idx = 0;
+ }
+
+ memcpy(m->data + idx, p, len);
+}
+
+void
+MD2_Final (void *res, struct md2 *m)
+{
+ unsigned char pad[16];
+ size_t padlen;
+
+ padlen = 16 - (m->len % 16);
+ memset(pad, padlen, padlen);
+
+ MD2_Update(m, pad, padlen);
+ memcpy(pad, m->checksum, 16);
+ MD2_Update(m, pad, 16);
+
+ memcpy(res, m->state, MD2_DIGEST_LENGTH);
+ memset(m, 0, sizeof(m));
+}
diff --git a/source4/heimdal/lib/hcrypto/md2.h b/source4/heimdal/lib/hcrypto/md2.h
new file mode 100644
index 0000000000..af765060aa
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/md2.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef HEIM_MD2_H
+#define HEIM_MD2_H 1
+
+/* symbol renaming */
+#define MD2_Init hc_MD2_Init
+#define MD2_Update hc_MD2_Update
+#define MD2_Final hc_MD2_Final
+
+/*
+ *
+ */
+
+#define MD2_DIGEST_LENGTH 16
+
+struct md2 {
+ size_t len;
+ unsigned char data[16]; /* stored unalligned data between Update's */
+ unsigned char checksum[16];
+ unsigned char state[16]; /* lower 16 bytes of X */
+};
+
+typedef struct md2 MD2_CTX;
+
+void MD2_Init (struct md2 *m);
+void MD2_Update (struct md2 *m, const void *p, size_t len);
+void MD2_Final (void *res, struct md2 *m);
+
+#endif /* HEIM_MD2_H */
diff --git a/source4/heimdal/lib/hcrypto/md4.c b/source4/heimdal/lib/hcrypto/md4.c
new file mode 100644
index 0000000000..56e2ac97c6
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/md4.c
@@ -0,0 +1,250 @@
+/*
+ * Copyright (c) 1995 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+
+RCSID("$Id$");
+#endif
+
+#include "hash.h"
+#include "md4.h"
+
+#define A m->counter[0]
+#define B m->counter[1]
+#define C m->counter[2]
+#define D m->counter[3]
+#define X data
+
+void
+MD4_Init (struct md4 *m)
+{
+ m->sz[0] = 0;
+ m->sz[1] = 0;
+ D = 0x10325476;
+ C = 0x98badcfe;
+ B = 0xefcdab89;
+ A = 0x67452301;
+}
+
+#define F(x,y,z) CRAYFIX((x & y) | (~x & z))
+#define G(x,y,z) ((x & y) | (x & z) | (y & z))
+#define H(x,y,z) (x ^ y ^ z)
+
+#define DOIT(a,b,c,d,k,s,i,OP) \
+a = cshift(a + OP(b,c,d) + X[k] + i, s)
+
+#define DO1(a,b,c,d,k,s,i) DOIT(a,b,c,d,k,s,i,F)
+#define DO2(a,b,c,d,k,s,i) DOIT(a,b,c,d,k,s,i,G)
+#define DO3(a,b,c,d,k,s,i) DOIT(a,b,c,d,k,s,i,H)
+
+static inline void
+calc (struct md4 *m, uint32_t *data)
+{
+ uint32_t AA, BB, CC, DD;
+
+ AA = A;
+ BB = B;
+ CC = C;
+ DD = D;
+
+ /* Round 1 */
+
+ DO1(A,B,C,D,0,3,0);
+ DO1(D,A,B,C,1,7,0);
+ DO1(C,D,A,B,2,11,0);
+ DO1(B,C,D,A,3,19,0);
+
+ DO1(A,B,C,D,4,3,0);
+ DO1(D,A,B,C,5,7,0);
+ DO1(C,D,A,B,6,11,0);
+ DO1(B,C,D,A,7,19,0);
+
+ DO1(A,B,C,D,8,3,0);
+ DO1(D,A,B,C,9,7,0);
+ DO1(C,D,A,B,10,11,0);
+ DO1(B,C,D,A,11,19,0);
+
+ DO1(A,B,C,D,12,3,0);
+ DO1(D,A,B,C,13,7,0);
+ DO1(C,D,A,B,14,11,0);
+ DO1(B,C,D,A,15,19,0);
+
+ /* Round 2 */
+
+ DO2(A,B,C,D,0,3,0x5A827999);
+ DO2(D,A,B,C,4,5,0x5A827999);
+ DO2(C,D,A,B,8,9,0x5A827999);
+ DO2(B,C,D,A,12,13,0x5A827999);
+
+ DO2(A,B,C,D,1,3,0x5A827999);
+ DO2(D,A,B,C,5,5,0x5A827999);
+ DO2(C,D,A,B,9,9,0x5A827999);
+ DO2(B,C,D,A,13,13,0x5A827999);
+
+ DO2(A,B,C,D,2,3,0x5A827999);
+ DO2(D,A,B,C,6,5,0x5A827999);
+ DO2(C,D,A,B,10,9,0x5A827999);
+ DO2(B,C,D,A,14,13,0x5A827999);
+
+ DO2(A,B,C,D,3,3,0x5A827999);
+ DO2(D,A,B,C,7,5,0x5A827999);
+ DO2(C,D,A,B,11,9,0x5A827999);
+ DO2(B,C,D,A,15,13,0x5A827999);
+
+ /* Round 3 */
+
+ DO3(A,B,C,D,0,3,0x6ED9EBA1);
+ DO3(D,A,B,C,8,9,0x6ED9EBA1);
+ DO3(C,D,A,B,4,11,0x6ED9EBA1);
+ DO3(B,C,D,A,12,15,0x6ED9EBA1);
+
+ DO3(A,B,C,D,2,3,0x6ED9EBA1);
+ DO3(D,A,B,C,10,9,0x6ED9EBA1);
+ DO3(C,D,A,B,6,11,0x6ED9EBA1);
+ DO3(B,C,D,A,14,15,0x6ED9EBA1);
+
+ DO3(A,B,C,D,1,3,0x6ED9EBA1);
+ DO3(D,A,B,C,9,9,0x6ED9EBA1);
+ DO3(C,D,A,B,5,11,0x6ED9EBA1);
+ DO3(B,C,D,A,13,15,0x6ED9EBA1);
+
+ DO3(A,B,C,D,3,3,0x6ED9EBA1);
+ DO3(D,A,B,C,11,9,0x6ED9EBA1);
+ DO3(C,D,A,B,7,11,0x6ED9EBA1);
+ DO3(B,C,D,A,15,15,0x6ED9EBA1);
+
+ A += AA;
+ B += BB;
+ C += CC;
+ D += DD;
+}
+
+/*
+ * From `Performance analysis of MD5' by Joseph D. Touch <touch@isi.edu>
+ */
+
+#if defined(WORDS_BIGENDIAN)
+static inline uint32_t
+swap_uint32_t (uint32_t t)
+{
+ uint32_t temp1, temp2;
+
+ temp1 = cshift(t, 16);
+ temp2 = temp1 >> 8;
+ temp1 &= 0x00ff00ff;
+ temp2 &= 0x00ff00ff;
+ temp1 <<= 8;
+ return temp1 | temp2;
+}
+#endif
+
+struct x32{
+ unsigned int a:32;
+ unsigned int b:32;
+};
+
+void
+MD4_Update (struct md4 *m, const void *v, size_t len)
+{
+ const unsigned char *p = v;
+ size_t old_sz = m->sz[0];
+ size_t offset;
+
+ m->sz[0] += len * 8;
+ if (m->sz[0] < old_sz)
+ ++m->sz[1];
+ offset = (old_sz / 8) % 64;
+ while(len > 0) {
+ size_t l = min(len, 64 - offset);
+ memcpy(m->save + offset, p, l);
+ offset += l;
+ p += l;
+ len -= l;
+ if(offset == 64) {
+#if defined(WORDS_BIGENDIAN)
+ int i;
+ uint32_t current[16];
+ struct x32 *u = (struct x32*)m->save;
+ for(i = 0; i < 8; i++){
+ current[2*i+0] = swap_uint32_t(u[i].a);
+ current[2*i+1] = swap_uint32_t(u[i].b);
+ }
+ calc(m, current);
+#else
+ calc(m, (uint32_t*)m->save);
+#endif
+ offset = 0;
+ }
+ }
+}
+
+void
+MD4_Final (void *res, struct md4 *m)
+{
+ unsigned char zeros[72];
+ unsigned offset = (m->sz[0] / 8) % 64;
+ unsigned int dstart = (120 - offset - 1) % 64 + 1;
+
+ *zeros = 0x80;
+ memset (zeros + 1, 0, sizeof(zeros) - 1);
+ zeros[dstart+0] = (m->sz[0] >> 0) & 0xff;
+ zeros[dstart+1] = (m->sz[0] >> 8) & 0xff;
+ zeros[dstart+2] = (m->sz[0] >> 16) & 0xff;
+ zeros[dstart+3] = (m->sz[0] >> 24) & 0xff;
+ zeros[dstart+4] = (m->sz[1] >> 0) & 0xff;
+ zeros[dstart+5] = (m->sz[1] >> 8) & 0xff;
+ zeros[dstart+6] = (m->sz[1] >> 16) & 0xff;
+ zeros[dstart+7] = (m->sz[1] >> 24) & 0xff;
+ MD4_Update (m, zeros, dstart + 8);
+ {
+ int i;
+ unsigned char *r = (unsigned char *)res;
+
+ for (i = 0; i < 4; ++i) {
+ r[4*i] = m->counter[i] & 0xFF;
+ r[4*i+1] = (m->counter[i] >> 8) & 0xFF;
+ r[4*i+2] = (m->counter[i] >> 16) & 0xFF;
+ r[4*i+3] = (m->counter[i] >> 24) & 0xFF;
+ }
+ }
+#if 0
+ {
+ int i;
+ uint32_t *r = (uint32_t *)res;
+
+ for (i = 0; i < 4; ++i)
+ r[i] = swap_uint32_t (m->counter[i]);
+ }
+#endif
+}
diff --git a/source4/heimdal/lib/hcrypto/md4.h b/source4/heimdal/lib/hcrypto/md4.h
new file mode 100644
index 0000000000..ce17d0f088
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/md4.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 1995 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef HEIM_MD4_H
+#define HEIM_MD4_H 1
+
+/* symbol renaming */
+#define MD4_Init hc_MD4_Init
+#define MD4_Update hc_MD4_Update
+#define MD4_Final hc_MD4_Final
+
+/*
+ *
+ */
+
+#define MD4_DIGEST_LENGTH 16
+
+struct md4 {
+ unsigned int sz[2];
+ uint32_t counter[4];
+ unsigned char save[64];
+};
+
+typedef struct md4 MD4_CTX;
+
+void MD4_Init (struct md4 *m);
+void MD4_Update (struct md4 *m, const void *p, size_t len);
+void MD4_Final (void *res, struct md4 *m);
+
+#endif /* HEIM_MD4_H */
diff --git a/source4/heimdal/lib/hcrypto/md5.c b/source4/heimdal/lib/hcrypto/md5.c
new file mode 100644
index 0000000000..ffc7bb9ec6
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/md5.c
@@ -0,0 +1,274 @@
+/*
+ * Copyright (c) 1995 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+
+RCSID("$Id$");
+#endif
+
+#include "hash.h"
+#include "md5.h"
+
+#define A m->counter[0]
+#define B m->counter[1]
+#define C m->counter[2]
+#define D m->counter[3]
+#define X data
+
+void
+MD5_Init (struct md5 *m)
+{
+ m->sz[0] = 0;
+ m->sz[1] = 0;
+ D = 0x10325476;
+ C = 0x98badcfe;
+ B = 0xefcdab89;
+ A = 0x67452301;
+}
+
+#define F(x,y,z) CRAYFIX((x & y) | (~x & z))
+#define G(x,y,z) CRAYFIX((x & z) | (y & ~z))
+#define H(x,y,z) (x ^ y ^ z)
+#define I(x,y,z) CRAYFIX(y ^ (x | ~z))
+
+#define DOIT(a,b,c,d,k,s,i,OP) \
+a = b + cshift(a + OP(b,c,d) + X[k] + (i), s)
+
+#define DO1(a,b,c,d,k,s,i) DOIT(a,b,c,d,k,s,i,F)
+#define DO2(a,b,c,d,k,s,i) DOIT(a,b,c,d,k,s,i,G)
+#define DO3(a,b,c,d,k,s,i) DOIT(a,b,c,d,k,s,i,H)
+#define DO4(a,b,c,d,k,s,i) DOIT(a,b,c,d,k,s,i,I)
+
+static inline void
+calc (struct md5 *m, uint32_t *data)
+{
+ uint32_t AA, BB, CC, DD;
+
+ AA = A;
+ BB = B;
+ CC = C;
+ DD = D;
+
+ /* Round 1 */
+
+ DO1(A,B,C,D,0,7,0xd76aa478);
+ DO1(D,A,B,C,1,12,0xe8c7b756);
+ DO1(C,D,A,B,2,17,0x242070db);
+ DO1(B,C,D,A,3,22,0xc1bdceee);
+
+ DO1(A,B,C,D,4,7,0xf57c0faf);
+ DO1(D,A,B,C,5,12,0x4787c62a);
+ DO1(C,D,A,B,6,17,0xa8304613);
+ DO1(B,C,D,A,7,22,0xfd469501);
+
+ DO1(A,B,C,D,8,7,0x698098d8);
+ DO1(D,A,B,C,9,12,0x8b44f7af);
+ DO1(C,D,A,B,10,17,0xffff5bb1);
+ DO1(B,C,D,A,11,22,0x895cd7be);
+
+ DO1(A,B,C,D,12,7,0x6b901122);
+ DO1(D,A,B,C,13,12,0xfd987193);
+ DO1(C,D,A,B,14,17,0xa679438e);
+ DO1(B,C,D,A,15,22,0x49b40821);
+
+ /* Round 2 */
+
+ DO2(A,B,C,D,1,5,0xf61e2562);
+ DO2(D,A,B,C,6,9,0xc040b340);
+ DO2(C,D,A,B,11,14,0x265e5a51);
+ DO2(B,C,D,A,0,20,0xe9b6c7aa);
+
+ DO2(A,B,C,D,5,5,0xd62f105d);
+ DO2(D,A,B,C,10,9,0x2441453);
+ DO2(C,D,A,B,15,14,0xd8a1e681);
+ DO2(B,C,D,A,4,20,0xe7d3fbc8);
+
+ DO2(A,B,C,D,9,5,0x21e1cde6);
+ DO2(D,A,B,C,14,9,0xc33707d6);
+ DO2(C,D,A,B,3,14,0xf4d50d87);
+ DO2(B,C,D,A,8,20,0x455a14ed);
+
+ DO2(A,B,C,D,13,5,0xa9e3e905);
+ DO2(D,A,B,C,2,9,0xfcefa3f8);
+ DO2(C,D,A,B,7,14,0x676f02d9);
+ DO2(B,C,D,A,12,20,0x8d2a4c8a);
+
+ /* Round 3 */
+
+ DO3(A,B,C,D,5,4,0xfffa3942);
+ DO3(D,A,B,C,8,11,0x8771f681);
+ DO3(C,D,A,B,11,16,0x6d9d6122);
+ DO3(B,C,D,A,14,23,0xfde5380c);
+
+ DO3(A,B,C,D,1,4,0xa4beea44);
+ DO3(D,A,B,C,4,11,0x4bdecfa9);
+ DO3(C,D,A,B,7,16,0xf6bb4b60);
+ DO3(B,C,D,A,10,23,0xbebfbc70);
+
+ DO3(A,B,C,D,13,4,0x289b7ec6);
+ DO3(D,A,B,C,0,11,0xeaa127fa);
+ DO3(C,D,A,B,3,16,0xd4ef3085);
+ DO3(B,C,D,A,6,23,0x4881d05);
+
+ DO3(A,B,C,D,9,4,0xd9d4d039);
+ DO3(D,A,B,C,12,11,0xe6db99e5);
+ DO3(C,D,A,B,15,16,0x1fa27cf8);
+ DO3(B,C,D,A,2,23,0xc4ac5665);
+
+ /* Round 4 */
+
+ DO4(A,B,C,D,0,6,0xf4292244);
+ DO4(D,A,B,C,7,10,0x432aff97);
+ DO4(C,D,A,B,14,15,0xab9423a7);
+ DO4(B,C,D,A,5,21,0xfc93a039);
+
+ DO4(A,B,C,D,12,6,0x655b59c3);
+ DO4(D,A,B,C,3,10,0x8f0ccc92);
+ DO4(C,D,A,B,10,15,0xffeff47d);
+ DO4(B,C,D,A,1,21,0x85845dd1);
+
+ DO4(A,B,C,D,8,6,0x6fa87e4f);
+ DO4(D,A,B,C,15,10,0xfe2ce6e0);
+ DO4(C,D,A,B,6,15,0xa3014314);
+ DO4(B,C,D,A,13,21,0x4e0811a1);
+
+ DO4(A,B,C,D,4,6,0xf7537e82);
+ DO4(D,A,B,C,11,10,0xbd3af235);
+ DO4(C,D,A,B,2,15,0x2ad7d2bb);
+ DO4(B,C,D,A,9,21,0xeb86d391);
+
+ A += AA;
+ B += BB;
+ C += CC;
+ D += DD;
+}
+
+/*
+ * From `Performance analysis of MD5' by Joseph D. Touch <touch@isi.edu>
+ */
+
+#if defined(WORDS_BIGENDIAN)
+static inline uint32_t
+swap_uint32_t (uint32_t t)
+{
+ uint32_t temp1, temp2;
+
+ temp1 = cshift(t, 16);
+ temp2 = temp1 >> 8;
+ temp1 &= 0x00ff00ff;
+ temp2 &= 0x00ff00ff;
+ temp1 <<= 8;
+ return temp1 | temp2;
+}
+#endif
+
+struct x32{
+ unsigned int a:32;
+ unsigned int b:32;
+};
+
+void
+MD5_Update (struct md5 *m, const void *v, size_t len)
+{
+ const unsigned char *p = v;
+ size_t old_sz = m->sz[0];
+ size_t offset;
+
+ m->sz[0] += len * 8;
+ if (m->sz[0] < old_sz)
+ ++m->sz[1];
+ offset = (old_sz / 8) % 64;
+ while(len > 0){
+ size_t l = min(len, 64 - offset);
+ memcpy(m->save + offset, p, l);
+ offset += l;
+ p += l;
+ len -= l;
+ if(offset == 64){
+#if defined(WORDS_BIGENDIAN)
+ int i;
+ uint32_t current[16];
+ struct x32 *u = (struct x32*)m->save;
+ for(i = 0; i < 8; i++){
+ current[2*i+0] = swap_uint32_t(u[i].a);
+ current[2*i+1] = swap_uint32_t(u[i].b);
+ }
+ calc(m, current);
+#else
+ calc(m, (uint32_t*)m->save);
+#endif
+ offset = 0;
+ }
+ }
+}
+
+void
+MD5_Final (void *res, struct md5 *m)
+{
+ unsigned char zeros[72];
+ unsigned offset = (m->sz[0] / 8) % 64;
+ unsigned int dstart = (120 - offset - 1) % 64 + 1;
+
+ *zeros = 0x80;
+ memset (zeros + 1, 0, sizeof(zeros) - 1);
+ zeros[dstart+0] = (m->sz[0] >> 0) & 0xff;
+ zeros[dstart+1] = (m->sz[0] >> 8) & 0xff;
+ zeros[dstart+2] = (m->sz[0] >> 16) & 0xff;
+ zeros[dstart+3] = (m->sz[0] >> 24) & 0xff;
+ zeros[dstart+4] = (m->sz[1] >> 0) & 0xff;
+ zeros[dstart+5] = (m->sz[1] >> 8) & 0xff;
+ zeros[dstart+6] = (m->sz[1] >> 16) & 0xff;
+ zeros[dstart+7] = (m->sz[1] >> 24) & 0xff;
+ MD5_Update (m, zeros, dstart + 8);
+ {
+ int i;
+ unsigned char *r = (unsigned char *)res;
+
+ for (i = 0; i < 4; ++i) {
+ r[4*i] = m->counter[i] & 0xFF;
+ r[4*i+1] = (m->counter[i] >> 8) & 0xFF;
+ r[4*i+2] = (m->counter[i] >> 16) & 0xFF;
+ r[4*i+3] = (m->counter[i] >> 24) & 0xFF;
+ }
+ }
+#if 0
+ {
+ int i;
+ uint32_t *r = (uint32_t *)res;
+
+ for (i = 0; i < 4; ++i)
+ r[i] = swap_uint32_t (m->counter[i]);
+ }
+#endif
+}
diff --git a/source4/heimdal/lib/hcrypto/md5.h b/source4/heimdal/lib/hcrypto/md5.h
new file mode 100644
index 0000000000..b2df6e56fc
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/md5.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 1995 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef HEIM_MD5_H
+#define HEIM_MD5_H 1
+
+/* symbol renaming */
+#define MD5_Init hc_MD5_Init
+#define MD5_Update hc_MD5_Update
+#define MD5_Final hc_MD5_Final
+
+/*
+ *
+ */
+
+#define MD5_DIGEST_LENGTH 16
+
+struct md5 {
+ unsigned int sz[2];
+ uint32_t counter[4];
+ unsigned char save[64];
+};
+
+typedef struct md5 MD5_CTX;
+
+void MD5_Init (struct md5 *m);
+void MD5_Update (struct md5 *m, const void *p, size_t len);
+void MD5_Final (void *res, struct md5 *m); /* uint32_t res[4] */
+
+#endif /* HEIM_MD5_H */
diff --git a/source4/heimdal/lib/hcrypto/pkcs12.c b/source4/heimdal/lib/hcrypto/pkcs12.c
new file mode 100644
index 0000000000..11afa0b68f
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/pkcs12.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+RCSID("$Id$");
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include <pkcs12.h>
+#include <bn.h>
+
+#include <roken.h>
+
+int
+PKCS12_key_gen(const void *key, size_t keylen,
+ const void *salt, size_t saltlen,
+ int id, int iteration, size_t outkeysize,
+ void *out, const EVP_MD *md)
+{
+ unsigned char *v, *I, hash[EVP_MAX_MD_SIZE];
+ unsigned int size, size_I = 0;
+ unsigned char idc = id;
+ EVP_MD_CTX *ctx;
+ unsigned char *outp = out;
+ int i, vlen;
+
+ ctx = EVP_MD_CTX_create();
+ if (ctx == NULL)
+ return 0;
+
+ vlen = EVP_MD_block_size(md);
+ v = malloc(vlen + 1);
+ if (v == NULL) {
+ EVP_MD_CTX_destroy(ctx);
+ return 0;
+ }
+
+ I = calloc(1, vlen * 2);
+ if (I == NULL) {
+ EVP_MD_CTX_destroy(ctx);
+ free(v);
+ return 0;
+ }
+
+ if (salt && saltlen > 0) {
+ for (i = 0; i < vlen; i++)
+ I[i] = ((unsigned char*)salt)[i % saltlen];
+ size_I += vlen;
+ }
+ /*
+ * There is a diffrence between the no password string and the
+ * empty string, in the empty string the UTF16 NUL terminator is
+ * included into the string.
+ */
+ if (key && keylen >= 0) {
+ for (i = 0; i < vlen / 2; i++) {
+ I[(i * 2) + size_I] = 0;
+ I[(i * 2) + size_I + 1] = ((unsigned char*)key)[i % (keylen + 1)];
+ }
+ size_I += vlen;
+ }
+
+ while (1) {
+ BIGNUM *bnB, *bnOne;
+
+ if (!EVP_DigestInit_ex(ctx, md, NULL)) {
+ EVP_MD_CTX_destroy(ctx);
+ free(I);
+ free(v);
+ return 0;
+ }
+ for (i = 0; i < vlen; i++)
+ EVP_DigestUpdate(ctx, &idc, 1);
+ EVP_DigestUpdate(ctx, I, size_I);
+ EVP_DigestFinal_ex(ctx, hash, &size);
+
+ for (i = 1; i < iteration; i++)
+ EVP_Digest(hash, size, hash, &size, md, NULL);
+
+ memcpy(outp, hash, min(outkeysize, size));
+ if (outkeysize < size)
+ break;
+ outkeysize -= size;
+ outp += size;
+
+ for (i = 0; i < vlen; i++)
+ v[i] = hash[i % size];
+
+ bnB = BN_bin2bn(v, vlen, NULL);
+ bnOne = BN_new();
+ BN_set_word(bnOne, 1);
+
+ BN_uadd(bnB, bnB, bnOne);
+
+ for (i = 0; i < vlen * 2; i += vlen) {
+ BIGNUM *bnI;
+ int j;
+
+ bnI = BN_bin2bn(I + i, vlen, NULL);
+
+ BN_uadd(bnI, bnI, bnB);
+
+ j = BN_num_bytes(bnI);
+ if (j > vlen) {
+ assert(j == vlen + 1);
+ BN_bn2bin(bnI, v);
+ memcpy(I + i, v + 1, vlen);
+ } else {
+ memset(I + i, 0, vlen - j);
+ BN_bn2bin(bnI, I + i + vlen - j);
+ }
+ BN_free(bnI);
+ }
+ BN_free(bnB);
+ BN_free(bnOne);
+ size_I = vlen * 2;
+ }
+
+ EVP_MD_CTX_destroy(ctx);
+ free(I);
+ free(v);
+
+ return 1;
+}
diff --git a/source4/heimdal/lib/hcrypto/pkcs12.h b/source4/heimdal/lib/hcrypto/pkcs12.h
new file mode 100644
index 0000000000..7e8214edcd
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/pkcs12.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id$
+ */
+
+#ifndef _HEIM_PKCS12_H
+#define _HEIM_PKCS12_H 1
+
+/* symbol renaming */
+#define PKCS12_key_gen hc_PKCS12_key_gen
+
+/*
+ *
+ */
+
+#include <hcrypto/evp.h>
+
+#define PKCS12_KEY_ID 1
+#define PKCS12_IV_ID 2
+
+int PKCS12_key_gen(const void *, size_t, const void *,
+ size_t, int, int, size_t, void *, const EVP_MD *);
+
+
+#endif /* _HEIM_PKCS12_H */
diff --git a/source4/heimdal/lib/hcrypto/pkcs5.c b/source4/heimdal/lib/hcrypto/pkcs5.c
new file mode 100644
index 0000000000..6537561e51
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/pkcs5.c
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+RCSID("$Id$");
+
+#ifdef KRB5
+#include <krb5-types.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <evp.h>
+#include <hmac.h>
+
+#include <roken.h>
+
+/**
+ * As descriped in PKCS5, convert a password, salt, and iteration counter into a crypto key.
+ *
+ * @param password Password.
+ * @param password_len Length of password.
+ * @param salt Salt
+ * @param salt_len Length of salt.
+ * @param iter iteration counter.
+ * @param keylen the output key length.
+ * @param key the output key.
+ *
+ * @return 1 on success, non 1 on failure.
+ *
+ * @ingroup hcrypto_misc
+ */
+
+int
+PKCS5_PBKDF2_HMAC_SHA1(const void * password, size_t password_len,
+ const void * salt, size_t salt_len,
+ unsigned long iter,
+ size_t keylen, void *key)
+{
+ size_t datalen, leftofkey, checksumsize;
+ char *data, *tmpcksum;
+ uint32_t keypart;
+ const EVP_MD *md;
+ unsigned long i;
+ int j;
+ char *p;
+ unsigned int hmacsize;
+
+ md = EVP_sha1();
+ checksumsize = EVP_MD_size(md);
+ datalen = salt_len + 4;
+
+ tmpcksum = malloc(checksumsize + datalen);
+ if (tmpcksum == NULL)
+ return 0;
+
+ data = &tmpcksum[checksumsize];
+
+ memcpy(data, salt, salt_len);
+
+ keypart = 1;
+ leftofkey = keylen;
+ p = key;
+
+ while (leftofkey) {
+ int len;
+
+ if (leftofkey > checksumsize)
+ len = checksumsize;
+ else
+ len = leftofkey;
+
+ data[datalen - 4] = (keypart >> 24) & 0xff;
+ data[datalen - 3] = (keypart >> 16) & 0xff;
+ data[datalen - 2] = (keypart >> 8) & 0xff;
+ data[datalen - 1] = (keypart) & 0xff;
+
+ HMAC(md, password, password_len, data, datalen,
+ tmpcksum, &hmacsize);
+
+ memcpy(p, tmpcksum, len);
+ for (i = 1; i < iter; i++) {
+ HMAC(md, password, password_len, tmpcksum, checksumsize,
+ tmpcksum, &hmacsize);
+
+ for (j = 0; j < len; j++)
+ p[j] ^= tmpcksum[j];
+ }
+
+ p += len;
+ leftofkey -= len;
+ keypart++;
+ }
+
+ free(tmpcksum);
+
+ return 1;
+}
diff --git a/source4/heimdal/lib/hcrypto/rand-egd.c b/source4/heimdal/lib/hcrypto/rand-egd.c
new file mode 100644
index 0000000000..168c15114a
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/rand-egd.c
@@ -0,0 +1,264 @@
+/*
+ * Copyright (c) 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+RCSID("$Id$");
+
+#include <sys/types.h>
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <assert.h>
+
+#include <rand.h>
+#include <randi.h>
+
+#include <roken.h>
+
+static const char *egd_path = "/var/run/egd-pool";
+
+#define MAX_EGD_DATA 255
+
+static int
+connect_egd(const char *path)
+{
+ struct sockaddr_un addr;
+ int fd;
+
+ memset(&addr, 0, sizeof(addr));
+
+ if (strlen(path) > sizeof(addr.sun_path))
+ return -1;
+
+ addr.sun_family = AF_UNIX;
+ strlcpy(addr.sun_path, path, sizeof(addr.sun_path));
+
+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd < 0)
+ return -1;
+
+ rk_cloexec(fd);
+
+ if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+static int
+get_entropy(int fd, void *data, size_t len)
+{
+ unsigned char msg[2];
+
+ assert(len <= MAX_EGD_DATA);
+
+ msg[0] = 0x02; /* read blocking data */
+ msg[1] = len; /* wanted length */
+
+ if (net_write(fd, msg, sizeof(msg)) != sizeof(msg))
+ return 0;
+
+ if (net_read(fd, data, len) != len)
+ return 0;
+
+ return 1;
+}
+
+static int
+put_entropy(int fd, const void *data, size_t len)
+{
+ unsigned char msg[4];
+
+ assert (len <= MAX_EGD_DATA);
+
+ msg[0] = 0x03; /* write data */
+ msg[1] = 0; /* dummy */
+ msg[2] = 0; /* entropy */
+ msg[3] = len; /* length */
+
+ if (net_write(fd, msg, sizeof(msg)) != sizeof(msg))
+ return 0;
+ if (net_write(fd, data, len) != len)
+ return 0;
+
+ return 1;
+}
+
+/*
+ *
+ */
+
+static void
+egd_seed(const void *indata, int size)
+{
+ size_t len;
+ int fd, ret = 1;
+
+ fd = connect_egd(egd_path);
+ if (fd < 0)
+ return;
+
+ while(size) {
+ len = size;
+ if (len > MAX_EGD_DATA)
+ len = MAX_EGD_DATA;
+ ret = put_entropy(fd, indata, len);
+ if (ret != 1)
+ break;
+ indata = ((unsigned char *)indata) + len;
+ size -= len;
+ }
+ close(fd);
+}
+
+static int
+get_bytes(const char *path, unsigned char *outdata, int size)
+{
+ size_t len;
+ int fd, ret = 1;
+
+ if (path == NULL)
+ path = egd_path;
+
+ fd = connect_egd(path);
+ if (fd < 0)
+ return 0;
+
+ while(size) {
+ len = size;
+ if (len > MAX_EGD_DATA)
+ len = MAX_EGD_DATA;
+ ret = get_entropy(fd, outdata, len);
+ if (ret != 1)
+ break;
+ outdata += len;
+ size -= len;
+ }
+ close(fd);
+
+ return ret;
+}
+
+static int
+egd_bytes(unsigned char *outdata, int size)
+{
+ return get_bytes(NULL, outdata, size);
+}
+
+static void
+egd_cleanup(void)
+{
+}
+
+static void
+egd_add(const void *indata, int size, double entropi)
+{
+ egd_seed(indata, size);
+}
+
+static int
+egd_pseudorand(unsigned char *outdata, int size)
+{
+ return get_bytes(NULL, outdata, size);
+}
+
+static int
+egd_status(void)
+{
+ int fd;
+ fd = connect_egd(egd_path);
+ if (fd < 0)
+ return 0;
+ close(fd);
+ return 1;
+}
+
+const RAND_METHOD hc_rand_egd_method = {
+ egd_seed,
+ egd_bytes,
+ egd_cleanup,
+ egd_add,
+ egd_pseudorand,
+ egd_status
+};
+
+const RAND_METHOD *
+RAND_egd_method(void)
+{
+ return &hc_rand_egd_method;
+}
+
+
+int
+RAND_egd(const char *filename)
+{
+ return RAND_egd_bytes(filename, 128);
+}
+
+int
+RAND_egd_bytes(const char *filename, int size)
+{
+ void *data;
+ int ret;
+
+ if (size <= 0)
+ return 0;
+
+ data = malloc(size);
+ if (data == NULL)
+ return 0;
+
+ ret = get_bytes(filename, data, size);
+ if (ret != 1) {
+ free(data);
+ return ret;
+ }
+
+ RAND_seed(data, size);
+
+ memset(data, 0, size);
+ free(data);
+
+ return 1;
+}
diff --git a/source4/heimdal/lib/hcrypto/rand-fortuna.c b/source4/heimdal/lib/hcrypto/rand-fortuna.c
new file mode 100644
index 0000000000..ebb4e6e180
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/rand-fortuna.c
@@ -0,0 +1,612 @@
+/*
+ * fortuna.c
+ * Fortuna-like PRNG.
+ *
+ * Copyright (c) 2005 Marko Kreen
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $PostgreSQL: pgsql/contrib/pgcrypto/fortuna.c,v 1.8 2006/10/04 00:29:46 momjian Exp $
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+RCSID("$Id$");
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <rand.h>
+
+#include <roken.h>
+
+#include "randi.h"
+#include "aes.h"
+#include "sha.h"
+
+/*
+ * Why Fortuna-like: There does not seem to be any definitive reference
+ * on Fortuna in the net. Instead this implementation is based on
+ * following references:
+ *
+ * http://en.wikipedia.org/wiki/Fortuna_(PRNG)
+ * - Wikipedia article
+ * http://jlcooke.ca/random/
+ * - Jean-Luc Cooke Fortuna-based /dev/random driver for Linux.
+ */
+
+/*
+ * There is some confusion about whether and how to carry forward
+ * the state of the pools. Seems like original Fortuna does not
+ * do it, resetting hash after each request. I guess expecting
+ * feeding to happen more often that requesting. This is absolutely
+ * unsuitable for pgcrypto, as nothing asynchronous happens here.
+ *
+ * J.L. Cooke fixed this by feeding previous hash to new re-initialized
+ * hash context.
+ *
+ * Fortuna predecessor Yarrow requires ability to query intermediate
+ * 'final result' from hash, without affecting it.
+ *
+ * This implementation uses the Yarrow method - asking intermediate
+ * results, but continuing with old state.
+ */
+
+
+/*
+ * Algorithm parameters
+ */
+
+#define NUM_POOLS 32
+
+/* in microseconds */
+#define RESEED_INTERVAL 100000 /* 0.1 sec */
+
+/* for one big request, reseed after this many bytes */
+#define RESEED_BYTES (1024*1024)
+
+/*
+ * Skip reseed if pool 0 has less than this many
+ * bytes added since last reseed.
+ */
+#define POOL0_FILL (256/8)
+
+/*
+ * Algorithm constants
+ */
+
+/* Both cipher key size and hash result size */
+#define BLOCK 32
+
+/* cipher block size */
+#define CIPH_BLOCK 16
+
+/* for internal wrappers */
+#define MD_CTX SHA256_CTX
+#define CIPH_CTX AES_KEY
+
+struct fortuna_state
+{
+ unsigned char counter[CIPH_BLOCK];
+ unsigned char result[CIPH_BLOCK];
+ unsigned char key[BLOCK];
+ MD_CTX pool[NUM_POOLS];
+ CIPH_CTX ciph;
+ unsigned reseed_count;
+ struct timeval last_reseed_time;
+ unsigned pool0_bytes;
+ unsigned rnd_pos;
+ int tricks_done;
+ pid_t pid;
+};
+typedef struct fortuna_state FState;
+
+
+/*
+ * Use our own wrappers here.
+ * - Need to get intermediate result from digest, without affecting it.
+ * - Need re-set key on a cipher context.
+ * - Algorithms are guaranteed to exist.
+ * - No memory allocations.
+ */
+
+static void
+ciph_init(CIPH_CTX * ctx, const unsigned char *key, int klen)
+{
+ AES_set_encrypt_key(key, klen * 8, ctx);
+}
+
+static void
+ciph_encrypt(CIPH_CTX * ctx, const unsigned char *in, unsigned char *out)
+{
+ AES_encrypt(in, out, ctx);
+}
+
+static void
+md_init(MD_CTX * ctx)
+{
+ SHA256_Init(ctx);
+}
+
+static void
+md_update(MD_CTX * ctx, const unsigned char *data, int len)
+{
+ SHA256_Update(ctx, data, len);
+}
+
+static void
+md_result(MD_CTX * ctx, unsigned char *dst)
+{
+ SHA256_CTX tmp;
+
+ memcpy(&tmp, ctx, sizeof(*ctx));
+ SHA256_Final(dst, &tmp);
+ memset(&tmp, 0, sizeof(tmp));
+}
+
+/*
+ * initialize state
+ */
+static void
+init_state(FState * st)
+{
+ int i;
+
+ memset(st, 0, sizeof(*st));
+ for (i = 0; i < NUM_POOLS; i++)
+ md_init(&st->pool[i]);
+ st->pid = getpid();
+}
+
+/*
+ * Endianess does not matter.
+ * It just needs to change without repeating.
+ */
+static void
+inc_counter(FState * st)
+{
+ uint32_t *val = (uint32_t *) st->counter;
+
+ if (++val[0])
+ return;
+ if (++val[1])
+ return;
+ if (++val[2])
+ return;
+ ++val[3];
+}
+
+/*
+ * This is called 'cipher in counter mode'.
+ */
+static void
+encrypt_counter(FState * st, unsigned char *dst)
+{
+ ciph_encrypt(&st->ciph, st->counter, dst);
+ inc_counter(st);
+}
+
+
+/*
+ * The time between reseed must be at least RESEED_INTERVAL
+ * microseconds.
+ */
+static int
+enough_time_passed(FState * st)
+{
+ int ok;
+ struct timeval tv;
+ struct timeval *last = &st->last_reseed_time;
+
+ gettimeofday(&tv, NULL);
+
+ /* check how much time has passed */
+ ok = 0;
+ if (tv.tv_sec > last->tv_sec + 1)
+ ok = 1;
+ else if (tv.tv_sec == last->tv_sec + 1)
+ {
+ if (1000000 + tv.tv_usec - last->tv_usec >= RESEED_INTERVAL)
+ ok = 1;
+ }
+ else if (tv.tv_usec - last->tv_usec >= RESEED_INTERVAL)
+ ok = 1;
+
+ /* reseed will happen, update last_reseed_time */
+ if (ok)
+ memcpy(last, &tv, sizeof(tv));
+
+ memset(&tv, 0, sizeof(tv));
+
+ return ok;
+}
+
+/*
+ * generate new key from all the pools
+ */
+static void
+reseed(FState * st)
+{
+ unsigned k;
+ unsigned n;
+ MD_CTX key_md;
+ unsigned char buf[BLOCK];
+
+ /* set pool as empty */
+ st->pool0_bytes = 0;
+
+ /*
+ * Both #0 and #1 reseed would use only pool 0. Just skip #0 then.
+ */
+ n = ++st->reseed_count;
+
+ /*
+ * The goal: use k-th pool only 1/(2^k) of the time.
+ */
+ md_init(&key_md);
+ for (k = 0; k < NUM_POOLS; k++)
+ {
+ md_result(&st->pool[k], buf);
+ md_update(&key_md, buf, BLOCK);
+
+ if (n & 1 || !n)
+ break;
+ n >>= 1;
+ }
+
+ /* add old key into mix too */
+ md_update(&key_md, st->key, BLOCK);
+
+ /* add pid to make output diverse after fork() */
+ md_update(&key_md, (const unsigned char *)&st->pid, sizeof(st->pid));
+
+ /* now we have new key */
+ md_result(&key_md, st->key);
+
+ /* use new key */
+ ciph_init(&st->ciph, st->key, BLOCK);
+
+ memset(&key_md, 0, sizeof(key_md));
+ memset(buf, 0, BLOCK);
+}
+
+/*
+ * Pick a random pool. This uses key bytes as random source.
+ */
+static unsigned
+get_rand_pool(FState * st)
+{
+ unsigned rnd;
+
+ /*
+ * This slightly prefers lower pools - thats OK.
+ */
+ rnd = st->key[st->rnd_pos] % NUM_POOLS;
+
+ st->rnd_pos++;
+ if (st->rnd_pos >= BLOCK)
+ st->rnd_pos = 0;
+
+ return rnd;
+}
+
+/*
+ * update pools
+ */
+static void
+add_entropy(FState * st, const unsigned char *data, unsigned len)
+{
+ unsigned pos;
+ unsigned char hash[BLOCK];
+ MD_CTX md;
+
+ /* hash given data */
+ md_init(&md);
+ md_update(&md, data, len);
+ md_result(&md, hash);
+
+ /*
+ * Make sure the pool 0 is initialized, then update randomly.
+ */
+ if (st->reseed_count == 0)
+ pos = 0;
+ else
+ pos = get_rand_pool(st);
+ md_update(&st->pool[pos], hash, BLOCK);
+
+ if (pos == 0)
+ st->pool0_bytes += len;
+
+ memset(hash, 0, BLOCK);
+ memset(&md, 0, sizeof(md));
+}
+
+/*
+ * Just take 2 next blocks as new key
+ */
+static void
+rekey(FState * st)
+{
+ encrypt_counter(st, st->key);
+ encrypt_counter(st, st->key + CIPH_BLOCK);
+ ciph_init(&st->ciph, st->key, BLOCK);
+}
+
+/*
+ * Hide public constants. (counter, pools > 0)
+ *
+ * This can also be viewed as spreading the startup
+ * entropy over all of the components.
+ */
+static void
+startup_tricks(FState * st)
+{
+ int i;
+ unsigned char buf[BLOCK];
+
+ /* Use next block as counter. */
+ encrypt_counter(st, st->counter);
+
+ /* Now shuffle pools, excluding #0 */
+ for (i = 1; i < NUM_POOLS; i++)
+ {
+ encrypt_counter(st, buf);
+ encrypt_counter(st, buf + CIPH_BLOCK);
+ md_update(&st->pool[i], buf, BLOCK);
+ }
+ memset(buf, 0, BLOCK);
+
+ /* Hide the key. */
+ rekey(st);
+
+ /* This can be done only once. */
+ st->tricks_done = 1;
+}
+
+static void
+extract_data(FState * st, unsigned count, unsigned char *dst)
+{
+ unsigned n;
+ unsigned block_nr = 0;
+ pid_t pid = getpid();
+
+ /* Should we reseed? */
+ if (st->pool0_bytes >= POOL0_FILL || st->reseed_count == 0)
+ if (enough_time_passed(st))
+ reseed(st);
+
+ /* Do some randomization on first call */
+ if (!st->tricks_done)
+ startup_tricks(st);
+
+ /* If we forked, force a reseed again */
+ if (pid != st->pid) {
+ st->pid = pid;
+ reseed(st);
+ }
+
+ while (count > 0)
+ {
+ /* produce bytes */
+ encrypt_counter(st, st->result);
+
+ /* copy result */
+ if (count > CIPH_BLOCK)
+ n = CIPH_BLOCK;
+ else
+ n = count;
+ memcpy(dst, st->result, n);
+ dst += n;
+ count -= n;
+
+ /* must not give out too many bytes with one key */
+ block_nr++;
+ if (block_nr > (RESEED_BYTES / CIPH_BLOCK))
+ {
+ rekey(st);
+ block_nr = 0;
+ }
+ }
+ /* Set new key for next request. */
+ rekey(st);
+}
+
+/*
+ * public interface
+ */
+
+static FState main_state;
+static int init_done;
+static int have_entropy;
+#define FORTUNA_RESEED_BYTE 10000
+static unsigned resend_bytes;
+
+/*
+ * Try our best to do an inital seed
+ */
+#define INIT_BYTES 128
+
+static int
+fortuna_reseed(void)
+{
+ int entropy_p = 0;
+
+ if (!init_done)
+ abort();
+
+ {
+ unsigned char buf[INIT_BYTES];
+ if ((*hc_rand_unix_method.bytes)(buf, sizeof(buf)) == 1) {
+ add_entropy(&main_state, buf, sizeof(buf));
+ entropy_p = 1;
+ memset(buf, 0, sizeof(buf));
+ }
+ }
+#ifdef HAVE_ARC4RANDOM
+ {
+ uint32_t buf[INIT_BYTES / sizeof(uint32_t)];
+ int i;
+
+ for (i = 0; i < sizeof(buf)/sizeof(buf[0]); i++)
+ buf[i] = arc4random();
+ add_entropy(&main_state, (void *)buf, sizeof(buf));
+ entropy_p = 1;
+ }
+#endif
+ /*
+ * Only to get egd entropy if /dev/random or arc4rand failed since
+ * it can be horribly slow to generate new bits.
+ */
+ if (!entropy_p) {
+ unsigned char buf[INIT_BYTES];
+ if ((*hc_rand_egd_method.bytes)(buf, sizeof(buf)) == 1) {
+ add_entropy(&main_state, buf, sizeof(buf));
+ entropy_p = 1;
+ memset(buf, 0, sizeof(buf));
+ }
+ }
+ /*
+ * Fall back to gattering data from timer and secret files, this
+ * is really the last resort.
+ */
+ if (!entropy_p) {
+ /* to save stackspace */
+ union {
+ unsigned char buf[INIT_BYTES];
+ unsigned char shad[1001];
+ } u;
+ int fd;
+
+ /* add timer info */
+ if ((*hc_rand_timer_method.bytes)(u.buf, sizeof(u.buf)) == 1)
+ add_entropy(&main_state, u.buf, sizeof(u.buf));
+ /* add /etc/shadow */
+ fd = open("/etc/shadow", O_RDONLY, 0);
+ if (fd >= 0) {
+ ssize_t n;
+ rk_cloexec(fd);
+ /* add_entropy will hash the buf */
+ while ((n = read(fd, (char *)u.shad, sizeof(u.shad))) > 0)
+ add_entropy(&main_state, u.shad, sizeof(u.shad));
+ close(fd);
+ }
+
+ memset(&u, 0, sizeof(u));
+
+ entropy_p = 1; /* sure about this ? */
+ }
+ {
+ pid_t pid = getpid();
+ add_entropy(&main_state, (void *)&pid, sizeof(pid));
+ }
+ {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ add_entropy(&main_state, (void *)&tv, sizeof(tv));
+ }
+ {
+ uid_t u = getuid();
+ add_entropy(&main_state, (void *)&u, sizeof(u));
+ }
+ return entropy_p;
+}
+
+static int
+fortuna_init(void)
+{
+ if (!init_done)
+ {
+ init_state(&main_state);
+ init_done = 1;
+ }
+ if (!have_entropy)
+ have_entropy = fortuna_reseed();
+ return (init_done && have_entropy);
+}
+
+
+
+static void
+fortuna_seed(const void *indata, int size)
+{
+ fortuna_init();
+ add_entropy(&main_state, indata, size);
+ if (size >= INIT_BYTES)
+ have_entropy = 1;
+}
+
+static int
+fortuna_bytes(unsigned char *outdata, int size)
+{
+ if (!fortuna_init())
+ return 0;
+ resend_bytes += size;
+ if (resend_bytes > FORTUNA_RESEED_BYTE || resend_bytes < size) {
+ resend_bytes = 0;
+ fortuna_reseed();
+ }
+ extract_data(&main_state, size, outdata);
+ return 1;
+}
+
+static void
+fortuna_cleanup(void)
+{
+ init_done = 0;
+ have_entropy = 0;
+ memset(&main_state, 0, sizeof(main_state));
+}
+
+static void
+fortuna_add(const void *indata, int size, double entropi)
+{
+ fortuna_seed(indata, size);
+}
+
+static int
+fortuna_pseudorand(unsigned char *outdata, int size)
+{
+ return fortuna_bytes(outdata, size);
+}
+
+static int
+fortuna_status(void)
+{
+ return fortuna_init() ? 1 : 0;
+}
+
+const RAND_METHOD hc_rand_fortuna_method = {
+ fortuna_seed,
+ fortuna_bytes,
+ fortuna_cleanup,
+ fortuna_add,
+ fortuna_pseudorand,
+ fortuna_status
+};
+
+const RAND_METHOD *
+RAND_fortuna_method(void)
+{
+ return &hc_rand_fortuna_method;
+}
diff --git a/source4/heimdal/lib/hcrypto/rand-timer.c b/source4/heimdal/lib/hcrypto/rand-timer.c
new file mode 100644
index 0000000000..86ff22c300
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/rand-timer.c
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 1995, 1996, 1997, 1999, 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+RCSID("$Id$");
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <rand.h>
+
+#include <roken.h>
+
+#include "randi.h"
+
+#ifndef WIN32 /* don't bother with this on windows */
+
+static volatile int counter;
+static volatile unsigned char *gdata; /* Global data */
+static volatile int igdata; /* Index into global data */
+static int gsize;
+
+static
+RETSIGTYPE
+sigALRM(int sig)
+{
+ if (igdata < gsize)
+ gdata[igdata++] ^= counter & 0xff;
+
+#ifndef HAVE_SIGACTION
+ signal(SIGALRM, sigALRM); /* Reinstall SysV signal handler */
+#endif
+ SIGRETURN(0);
+}
+
+#ifndef HAVE_SETITIMER
+static void
+pacemaker(struct timeval *tv)
+{
+ fd_set fds;
+ pid_t pid;
+ pid = getppid();
+ while(1){
+ FD_ZERO(&fds);
+ FD_SET(0, &fds);
+ select(1, &fds, NULL, NULL, tv);
+ kill(pid, SIGALRM);
+ }
+}
+#endif
+
+#ifdef HAVE_SIGACTION
+/* XXX ugly hack, should perhaps use function from roken */
+static RETSIGTYPE
+(*fake_signal(int sig, RETSIGTYPE (*f)(int)))(int)
+{
+ struct sigaction sa, osa;
+ sa.sa_handler = f;
+ sa.sa_flags = 0;
+ sigemptyset(&sa.sa_mask);
+ sigaction(sig, &sa, &osa);
+ return osa.sa_handler;
+}
+#define signal(S, F) fake_signal((S), (F))
+#endif
+
+#endif /* WIN32*/
+
+/*
+ *
+ */
+
+static void
+timer_seed(const void *indata, int size)
+{
+}
+
+static int
+timer_bytes(unsigned char *outdata, int size)
+{
+#ifdef WIN32
+ return 0;
+#else /* WIN32 */
+ struct itimerval tv, otv;
+ RETSIGTYPE (*osa)(int);
+ int i, j;
+#ifndef HAVE_SETITIMER
+ RETSIGTYPE (*ochld)(int);
+ pid_t pid;
+#endif
+
+ gdata = outdata;
+ gsize = size;
+ igdata = 0;
+
+ osa = signal(SIGALRM, sigALRM);
+
+ /* Start timer */
+ tv.it_value.tv_sec = 0;
+ tv.it_value.tv_usec = 10 * 1000; /* 10 ms */
+ tv.it_interval = tv.it_value;
+#ifdef HAVE_SETITIMER
+ setitimer(ITIMER_REAL, &tv, &otv);
+#else
+ ochld = signal(SIGCHLD, SIG_IGN);
+ pid = fork();
+ if(pid == -1){
+ signal(SIGCHLD, ochld != SIG_ERR ? ochld : SIG_DFL);
+ des_not_rand_data(data, size);
+ return;
+ }
+ if(pid == 0)
+ pacemaker(&tv.it_interval);
+#endif
+
+ for(i = 0; i < 4; i++) {
+ for (igdata = 0; igdata < size;) /* igdata++ in sigALRM */
+ counter++;
+ for (j = 0; j < size; j++) /* Only use 2 bits each lap */
+ gdata[j] = (gdata[j]>>2) | (gdata[j]<<6);
+ }
+#ifdef HAVE_SETITIMER
+ setitimer(ITIMER_REAL, &otv, 0);
+#else
+ kill(pid, SIGKILL);
+ while(waitpid(pid, NULL, 0) != pid);
+ signal(SIGCHLD, ochld != SIG_ERR ? ochld : SIG_DFL);
+#endif
+ signal(SIGALRM, osa != SIG_ERR ? osa : SIG_DFL);
+
+ return 1;
+#endif
+}
+
+static void
+timer_cleanup(void)
+{
+}
+
+static void
+timer_add(const void *indata, int size, double entropi)
+{
+}
+
+static int
+timer_pseudorand(unsigned char *outdata, int size)
+{
+ return timer_bytes(outdata, size);
+}
+
+static int
+timer_status(void)
+{
+#ifdef WIN32
+ return 0;
+#else
+ return 1;
+#endif
+}
+
+const RAND_METHOD hc_rand_timer_method = {
+ timer_seed,
+ timer_bytes,
+ timer_cleanup,
+ timer_add,
+ timer_pseudorand,
+ timer_status
+};
+
+const RAND_METHOD *
+RAND_timer_method(void)
+{
+ return &hc_rand_timer_method;
+}
diff --git a/source4/heimdal/lib/hcrypto/rand-unix.c b/source4/heimdal/lib/hcrypto/rand-unix.c
new file mode 100644
index 0000000000..0c2185776c
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/rand-unix.c
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+RCSID("$Id$");
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <rand.h>
+
+#include <roken.h>
+
+#include "randi.h"
+
+/*
+ * Unix /dev/random
+ */
+
+static int
+get_device_fd(int flags)
+{
+ static const char *rnd_devices[] = {
+ "/dev/urandom",
+ "/dev/random",
+ "/dev/srandom",
+ "/dev/arandom",
+ NULL
+ };
+ const char **p;
+
+ for(p = rnd_devices; *p; p++) {
+ int fd = open(*p, flags | O_NDELAY);
+ if(fd >= 0) {
+ rk_cloexec(fd);
+ return fd;
+ }
+ }
+ return -1;
+}
+
+static void
+unix_seed(const void *indata, int size)
+{
+ int fd;
+
+ if (size <= 0)
+ return;
+
+ fd = get_device_fd(O_WRONLY);
+ if (fd < 0)
+ return;
+
+ write(fd, indata, size);
+ close(fd);
+
+}
+
+static int
+unix_bytes(unsigned char *outdata, int size)
+{
+ ssize_t count;
+ int fd;
+
+ if (size <= 0)
+ return 0;
+
+ fd = get_device_fd(O_RDONLY);
+ if (fd < 0)
+ return 0;
+
+ while (size > 0) {
+ count = read (fd, outdata, size);
+ if (count < 0 && errno == EINTR)
+ continue;
+ else if (count <= 0) {
+ close(fd);
+ return 0;
+ }
+ outdata += count;
+ size -= count;
+ }
+ close(fd);
+
+ return 1;
+}
+
+static void
+unix_cleanup(void)
+{
+}
+
+static void
+unix_add(const void *indata, int size, double entropi)
+{
+ unix_seed(indata, size);
+}
+
+static int
+unix_pseudorand(unsigned char *outdata, int size)
+{
+ return unix_bytes(outdata, size);
+}
+
+static int
+unix_status(void)
+{
+ int fd;
+
+ fd = get_device_fd(O_RDONLY);
+ if (fd < 0)
+ return 0;
+ close(fd);
+
+ return 1;
+}
+
+const RAND_METHOD hc_rand_unix_method = {
+ unix_seed,
+ unix_bytes,
+ unix_cleanup,
+ unix_add,
+ unix_pseudorand,
+ unix_status
+};
+
+const RAND_METHOD *
+RAND_unix_method(void)
+{
+ return &hc_rand_unix_method;
+}
diff --git a/source4/heimdal/lib/hcrypto/rand.c b/source4/heimdal/lib/hcrypto/rand.c
new file mode 100644
index 0000000000..b8ac2155d1
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/rand.c
@@ -0,0 +1,368 @@
+/*
+ * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+RCSID("$Id$");
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <rand.h>
+#include <randi.h>
+
+#include <roken.h>
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+/**
+ * @page page_rand RAND - random number
+ *
+ * See the library functions here: @ref hcrypto_rand
+ */
+
+const static RAND_METHOD *selected_meth = NULL;
+static ENGINE *selected_engine = NULL;
+
+static void
+init_method(void)
+{
+ if (selected_meth != NULL)
+ return;
+ selected_meth = &hc_rand_fortuna_method;
+}
+
+/**
+ * Seed that random number generator. Secret material can securely be
+ * feed into the function, they will never be returned.
+ *
+ * @param indata seed data
+ * @param size length seed data
+ *
+ * @ingroup hcrypto_rand
+ */
+
+void
+RAND_seed(const void *indata, size_t size)
+{
+ init_method();
+ (*selected_meth->seed)(indata, size);
+}
+
+/**
+ * Get a random block from the random generator, can be used for key material.
+ *
+ * @param outdata random data
+ * @param size length random data
+ *
+ * @return 1 on success, 0 on failure.
+ *
+ * @ingroup hcrypto_rand
+ */
+int
+RAND_bytes(void *outdata, size_t size)
+{
+ init_method();
+ return (*selected_meth->bytes)(outdata, size);
+}
+
+/**
+ * Reset and free memory used by the random generator.
+ *
+ * @ingroup hcrypto_rand
+ */
+
+void
+RAND_cleanup(void)
+{
+ const RAND_METHOD *meth = selected_meth;
+ ENGINE *engine = selected_engine;
+
+ selected_meth = NULL;
+ selected_engine = NULL;
+
+ if (meth)
+ (*meth->cleanup)();
+ if (engine)
+ ENGINE_finish(engine);
+}
+
+/**
+ * Seed that random number generator. Secret material can securely be
+ * feed into the function, they will never be returned.
+ *
+ * @param indata the input data.
+ * @param size size of in data.
+ * @param entropi entropi in data.
+ *
+ *
+ * @ingroup hcrypto_rand
+ */
+
+void
+RAND_add(const void *indata, size_t size, double entropi)
+{
+ init_method();
+ (*selected_meth->add)(indata, size, entropi);
+}
+
+/**
+ * Get a random block from the random generator, should NOT be used for key material.
+ *
+ * @param outdata random data
+ * @param size length random data
+ *
+ * @return 1 on success, 0 on failure.
+ *
+ * @ingroup hcrypto_rand
+ */
+
+int
+RAND_pseudo_bytes(void *outdata, size_t size)
+{
+ init_method();
+ return (*selected_meth->pseudorand)(outdata, size);
+}
+
+/**
+ * Return status of the random generator
+ *
+ * @return 1 if the random generator can deliver random data.
+ *
+ * @ingroup hcrypto_rand
+ */
+
+int
+RAND_status(void)
+{
+ init_method();
+ return (*selected_meth->status)();
+}
+
+/**
+ * Set the default random method.
+ *
+ * @param meth set the new default method.
+ *
+ * @return 1 on success.
+ *
+ * @ingroup hcrypto_rand
+ */
+
+int
+RAND_set_rand_method(const RAND_METHOD *meth)
+{
+ const RAND_METHOD *old = selected_meth;
+ selected_meth = meth;
+ if (old)
+ (*old->cleanup)();
+ if (selected_engine) {
+ ENGINE_finish(selected_engine);
+ selected_engine = NULL;
+ }
+ return 1;
+}
+
+/**
+ * Get the default random method.
+ *
+ * @ingroup hcrypto_rand
+ */
+
+const RAND_METHOD *
+RAND_get_rand_method(void)
+{
+ init_method();
+ return selected_meth;
+}
+
+/**
+ * Set the default random method from engine.
+ *
+ * @param engine use engine, if NULL is passed it, old method and engine is cleared.
+ *
+ * @return 1 on success, 0 on failure.
+ *
+ * @ingroup hcrypto_rand
+ */
+
+int
+RAND_set_rand_engine(ENGINE *engine)
+{
+ const RAND_METHOD *meth, *old = selected_meth;
+
+ if (engine) {
+ ENGINE_up_ref(engine);
+ meth = ENGINE_get_RAND(engine);
+ if (meth == NULL) {
+ ENGINE_finish(engine);
+ return 0;
+ }
+ } else {
+ meth = NULL;
+ }
+
+ if (old)
+ (*old->cleanup)();
+
+ if (selected_engine)
+ ENGINE_finish(selected_engine);
+
+ selected_engine = engine;
+ selected_meth = meth;
+
+ return 1;
+}
+
+#define RAND_FILE_SIZE 1024
+
+/**
+ * Load a a file and feed it into RAND_seed().
+ *
+ * @param filename name of file to read.
+ * @param size minimum size to read.
+ *
+ * @ingroup hcrypto_rand
+ */
+
+int
+RAND_load_file(const char *filename, size_t size)
+{
+ unsigned char buf[128];
+ size_t len;
+ ssize_t slen;
+ int fd;
+
+ fd = open(filename, O_RDONLY | O_BINARY, 0600);
+ if (fd < 0)
+ return 0;
+ rk_cloexec(fd);
+ len = 0;
+ while(len < size) {
+ slen = read(fd, buf, sizeof(buf));
+ if (slen <= 0)
+ break;
+ RAND_seed(buf, slen);
+ len += slen;
+ }
+ close(fd);
+
+ return len ? 1 : 0;
+}
+
+/**
+ * Write of random numbers to a file to store for later initiation with RAND_load_file().
+ *
+ * @param filename name of file to write.
+ *
+ * @return 1 on success and non-one on failure.
+ * @ingroup hcrypto_rand
+ */
+
+int
+RAND_write_file(const char *filename)
+{
+ unsigned char buf[128];
+ size_t len;
+ int res = 0, fd;
+
+ fd = open(filename, O_WRONLY | O_CREAT | O_BINARY, 0600);
+ if (fd < 0)
+ return 0;
+ rk_cloexec(fd);
+
+ len = 0;
+ while(len < RAND_FILE_SIZE) {
+ res = RAND_bytes(buf, sizeof(buf));
+ if (res != 1)
+ break;
+ if (write(fd, buf, sizeof(buf)) != sizeof(buf)) {
+ res = 0;
+ break;
+ }
+ len += sizeof(buf);
+ }
+
+ close(fd);
+
+ return res;
+}
+
+/**
+ * Return the default random state filename for a user to use for
+ * RAND_load_file(), and RAND_write_file().
+ *
+ * @param filename buffer to hold file name.
+ * @param size size of buffer filename.
+ *
+ * @return the buffer filename or NULL on failure.
+ *
+ * @ingroup hcrypto_rand
+ */
+
+const char *
+RAND_file_name(char *filename, size_t size)
+{
+ const char *e = NULL;
+ int pathp = 0, ret;
+
+ if (!issuid()) {
+ e = getenv("RANDFILE");
+ if (e == NULL) {
+ e = getenv("HOME");
+ if (e)
+ pathp = 1;
+ }
+ }
+ /*
+ * Here we really want to call getpwuid(getuid()) but this will
+ * cause recursive lookups if the nss library uses
+ * gssapi/krb5/hcrypto to authenticate to the ldap servers.
+ */
+
+ if (e == NULL)
+ return NULL;
+
+ if (pathp)
+ ret = snprintf(filename, size, "%s/.rnd", e);
+ else
+ ret = snprintf(filename, size, "%s", e);
+
+ if (ret <= 0 || ret >= size)
+ return NULL;
+
+ return filename;
+}
diff --git a/source4/heimdal/lib/hcrypto/rand.h b/source4/heimdal/lib/hcrypto/rand.h
new file mode 100644
index 0000000000..65800d6b99
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/rand.h
@@ -0,0 +1,108 @@
+
+/*
+ * Copyright (c) 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id$
+ */
+
+#ifndef _HEIM_RAND_H
+#define _HEIM_RAND_H 1
+
+typedef struct RAND_METHOD RAND_METHOD;
+
+#include <hcrypto/bn.h>
+#include <hcrypto/engine.h>
+
+/* symbol renaming */
+#define RAND_bytes hc_RAND_bytes
+#define RAND_pseudo_bytes hc_RAND_pseudo_bytes
+#define RAND_seed hc_RAND_seed
+#define RAND_cleanup hc_RAND_cleanup
+#define RAND_add hc_RAND_add
+#define RAND_set_rand_method hc_RAND_set_rand_method
+#define RAND_get_rand_method hc_RAND_get_rand_method
+#define RAND_set_rand_engine hc_RAND_set_rand_engine
+#define RAND_file_name hc_RAND_file_name
+#define RAND_load_file hc_RAND_load_file
+#define RAND_write_file hc_RAND_write_file
+#define RAND_status hc_RAND_status
+#define RAND_egd hc_RAND_egd
+#define RAND_egd_bytes hc_RAND_egd_bytes
+#define RAND_fortuna_method hc_RAND_fortuna_method
+#define RAND_egd_method hc_RAND_egd_method
+#define RAND_unix_method hc_RAND_unix_method
+
+/*
+ *
+ */
+
+struct RAND_METHOD
+{
+ void (*seed)(const void *, int);
+ int (*bytes)(unsigned char *, int);
+ void (*cleanup)(void);
+ void (*add)(const void *, int, double);
+ int (*pseudorand)(unsigned char *, int);
+ int (*status)(void);
+};
+
+/*
+ *
+ */
+
+int RAND_bytes(void *, size_t num);
+int RAND_pseudo_bytes(void *, size_t);
+void RAND_seed(const void *, size_t);
+void RAND_cleanup(void);
+void RAND_add(const void *, size_t, double);
+
+int RAND_set_rand_method(const RAND_METHOD *);
+const RAND_METHOD *
+ RAND_get_rand_method(void);
+int RAND_set_rand_engine(ENGINE *);
+
+const char *
+ RAND_file_name(char *, size_t);
+int RAND_load_file(const char *, size_t);
+int RAND_write_file(const char *);
+int RAND_status(void);
+int RAND_egd(const char *);
+int RAND_egd_bytes(const char *, int);
+
+
+const RAND_METHOD * RAND_fortuna_method(void);
+const RAND_METHOD * RAND_unix_method(void);
+const RAND_METHOD * RAND_egd_method(void);
+
+#endif /* _HEIM_RAND_H */
diff --git a/source4/heimdal/lib/hcrypto/randi.h b/source4/heimdal/lib/hcrypto/randi.h
new file mode 100644
index 0000000000..f8f6c39b3e
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/randi.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id$
+ */
+
+#ifndef _HEIM_RANDI_H
+#define _HEIM_RANDI_H 1
+
+extern const RAND_METHOD hc_rand_fortuna_method;
+extern const RAND_METHOD hc_rand_unix_method;
+extern const RAND_METHOD hc_rand_egd_method;
+extern const RAND_METHOD hc_rand_timer_method;
+
+const RAND_METHOD * RAND_timer_method(void);
+
+#endif /* _HEIM_RANDI_H */
diff --git a/source4/heimdal/lib/hcrypto/rc2.c b/source4/heimdal/lib/hcrypto/rc2.c
new file mode 100644
index 0000000000..917914968c
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/rc2.c
@@ -0,0 +1,245 @@
+/*
+ * Copyright (c) 2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+RCSID("$Id$");
+#endif
+
+#include "rc2.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * Implemented from Peter Gutmann's "Specification for Ron Rivests Cipher No.2"
+ * rfc2268 and "On the Design and Security of RC2" was also useful.
+ */
+
+static unsigned int Sbox[256] = {
+ 0xd9, 0x78, 0xf9, 0xc4, 0x19, 0xdd, 0xb5, 0xed,
+ 0x28, 0xe9, 0xfd, 0x79, 0x4a, 0xa0, 0xd8, 0x9d,
+ 0xc6, 0x7e, 0x37, 0x83, 0x2b, 0x76, 0x53, 0x8e,
+ 0x62, 0x4c, 0x64, 0x88, 0x44, 0x8b, 0xfb, 0xa2,
+ 0x17, 0x9a, 0x59, 0xf5, 0x87, 0xb3, 0x4f, 0x13,
+ 0x61, 0x45, 0x6d, 0x8d, 0x09, 0x81, 0x7d, 0x32,
+ 0xbd, 0x8f, 0x40, 0xeb, 0x86, 0xb7, 0x7b, 0x0b,
+ 0xf0, 0x95, 0x21, 0x22, 0x5c, 0x6b, 0x4e, 0x82,
+ 0x54, 0xd6, 0x65, 0x93, 0xce, 0x60, 0xb2, 0x1c,
+ 0x73, 0x56, 0xc0, 0x14, 0xa7, 0x8c, 0xf1, 0xdc,
+ 0x12, 0x75, 0xca, 0x1f, 0x3b, 0xbe, 0xe4, 0xd1,
+ 0x42, 0x3d, 0xd4, 0x30, 0xa3, 0x3c, 0xb6, 0x26,
+ 0x6f, 0xbf, 0x0e, 0xda, 0x46, 0x69, 0x07, 0x57,
+ 0x27, 0xf2, 0x1d, 0x9b, 0xbc, 0x94, 0x43, 0x03,
+ 0xf8, 0x11, 0xc7, 0xf6, 0x90, 0xef, 0x3e, 0xe7,
+ 0x06, 0xc3, 0xd5, 0x2f, 0xc8, 0x66, 0x1e, 0xd7,
+ 0x08, 0xe8, 0xea, 0xde, 0x80, 0x52, 0xee, 0xf7,
+ 0x84, 0xaa, 0x72, 0xac, 0x35, 0x4d, 0x6a, 0x2a,
+ 0x96, 0x1a, 0xd2, 0x71, 0x5a, 0x15, 0x49, 0x74,
+ 0x4b, 0x9f, 0xd0, 0x5e, 0x04, 0x18, 0xa4, 0xec,
+ 0xc2, 0xe0, 0x41, 0x6e, 0x0f, 0x51, 0xcb, 0xcc,
+ 0x24, 0x91, 0xaf, 0x50, 0xa1, 0xf4, 0x70, 0x39,
+ 0x99, 0x7c, 0x3a, 0x85, 0x23, 0xb8, 0xb4, 0x7a,
+ 0xfc, 0x02, 0x36, 0x5b, 0x25, 0x55, 0x97, 0x31,
+ 0x2d, 0x5d, 0xfa, 0x98, 0xe3, 0x8a, 0x92, 0xae,
+ 0x05, 0xdf, 0x29, 0x10, 0x67, 0x6c, 0xba, 0xc9,
+ 0xd3, 0x00, 0xe6, 0xcf, 0xe1, 0x9e, 0xa8, 0x2c,
+ 0x63, 0x16, 0x01, 0x3f, 0x58, 0xe2, 0x89, 0xa9,
+ 0x0d, 0x38, 0x34, 0x1b, 0xab, 0x33, 0xff, 0xb0,
+ 0xbb, 0x48, 0x0c, 0x5f, 0xb9, 0xb1, 0xcd, 0x2e,
+ 0xc5, 0xf3, 0xdb, 0x47, 0xe5, 0xa5, 0x9c, 0x77,
+ 0x0a, 0xa6, 0x20, 0x68, 0xfe, 0x7f, 0xc1, 0xad
+};
+
+void
+RC2_set_key(RC2_KEY *key, int len, const unsigned char *data, int bits)
+{
+ unsigned char k[128];
+ int j, T8, TM;
+
+ if (len <= 0)
+ abort();
+ if (len > 128)
+ len = 128;
+ if (bits <= 0 || bits > 1024)
+ bits = 1024;
+
+ for (j = 0; j < len; j++)
+ k[j] = data[j];
+ for (; j < 128; j++)
+ k[j] = Sbox[(k[j - len] + k[j - 1]) & 0xff];
+
+ T8 = (bits + 7) / 8;
+ j = (8*T8 - bits);
+ TM = 0xff >> j;
+
+ k[128 - T8] = Sbox[k[128 - T8] & TM];
+
+ for (j = 127 - T8; j >= 0; j--)
+ k[j] = Sbox[k[j + 1] ^ k[j + T8]];
+
+ for (j = 0; j < 64; j++)
+ key->data[j] = k[(j * 2) + 0] | (k[(j * 2) + 1] << 8);
+ memset(k, 0, sizeof(k));
+}
+
+#define ROT16L(w,n) ((w<<n)|(w>>(16-n)))
+#define ROT16R(w,n) ((w>>n)|(w<<(16-n)))
+
+void
+RC2_encryptc(unsigned char *in, unsigned char *out, const RC2_KEY *key)
+{
+ int i, j;
+ int w0, w1, w2, w3;
+ int t0, t1, t2, t3;
+
+ w0 = in[0] | (in[1] << 8);
+ w1 = in[2] | (in[3] << 8);
+ w2 = in[4] | (in[5] << 8);
+ w3 = in[6] | (in[7] << 8);
+
+ for (i = 0; i < 16; i++) {
+ j = i * 4;
+ t0 = (w0 + (w1 & ~w3) + (w2 & w3) + key->data[j + 0]) & 0xffff;
+ w0 = ROT16L(t0, 1);
+ t1 = (w1 + (w2 & ~w0) + (w3 & w0) + key->data[j + 1]) & 0xffff;
+ w1 = ROT16L(t1, 2);
+ t2 = (w2 + (w3 & ~w1) + (w0 & w1) + key->data[j + 2]) & 0xffff;
+ w2 = ROT16L(t2, 3);
+ t3 = (w3 + (w0 & ~w2) + (w1 & w2) + key->data[j + 3]) & 0xffff;
+ w3 = ROT16L(t3, 5);
+ if(i == 4 || i == 10) {
+ w0 += key->data[w3 & 63];
+ w1 += key->data[w0 & 63];
+ w2 += key->data[w1 & 63];
+ w3 += key->data[w2 & 63];
+ }
+ }
+
+ out[0] = w0 & 0xff;
+ out[1] = (w0 >> 8) & 0xff;
+ out[2] = w1 & 0xff;
+ out[3] = (w1 >> 8) & 0xff;
+ out[4] = w2 & 0xff;
+ out[5] = (w2 >> 8) & 0xff;
+ out[6] = w3 & 0xff;
+ out[7] = (w3 >> 8) & 0xff;
+}
+
+void
+RC2_decryptc(unsigned char *in, unsigned char *out, const RC2_KEY *key)
+{
+ int i, j;
+ int w0, w1, w2, w3;
+ int t0, t1, t2, t3;
+
+ w0 = in[0] | (in[1] << 8);
+ w1 = in[2] | (in[3] << 8);
+ w2 = in[4] | (in[5] << 8);
+ w3 = in[6] | (in[7] << 8);
+
+ for (i = 15; i >= 0; i--) {
+ j = i * 4;
+
+ if(i == 4 || i == 10) {
+ w3 = (w3 - key->data[w2 & 63]) & 0xffff;
+ w2 = (w2 - key->data[w1 & 63]) & 0xffff;
+ w1 = (w1 - key->data[w0 & 63]) & 0xffff;
+ w0 = (w0 - key->data[w3 & 63]) & 0xffff;
+ }
+
+ t3 = ROT16R(w3, 5);
+ w3 = (t3 - (w0 & ~w2) - (w1 & w2) - key->data[j + 3]) & 0xffff;
+ t2 = ROT16R(w2, 3);
+ w2 = (t2 - (w3 & ~w1) - (w0 & w1) - key->data[j + 2]) & 0xffff;
+ t1 = ROT16R(w1, 2);
+ w1 = (t1 - (w2 & ~w0) - (w3 & w0) - key->data[j + 1]) & 0xffff;
+ t0 = ROT16R(w0, 1);
+ w0 = (t0 - (w1 & ~w3) - (w2 & w3) - key->data[j + 0]) & 0xffff;
+
+ }
+ out[0] = w0 & 0xff;
+ out[1] = (w0 >> 8) & 0xff;
+ out[2] = w1 & 0xff;
+ out[3] = (w1 >> 8) & 0xff;
+ out[4] = w2 & 0xff;
+ out[5] = (w2 >> 8) & 0xff;
+ out[6] = w3 & 0xff;
+ out[7] = (w3 >> 8) & 0xff;
+}
+
+void
+RC2_cbc_encrypt(const unsigned char *in, unsigned char *out, long size,
+ RC2_KEY *key, unsigned char *iv, int forward_encrypt)
+{
+ unsigned char tmp[RC2_BLOCK_SIZE];
+ int i;
+
+ if (forward_encrypt) {
+ while (size >= RC2_BLOCK_SIZE) {
+ for (i = 0; i < RC2_BLOCK_SIZE; i++)
+ tmp[i] = in[i] ^ iv[i];
+ RC2_encryptc(tmp, out, key);
+ memcpy(iv, out, RC2_BLOCK_SIZE);
+ size -= RC2_BLOCK_SIZE;
+ in += RC2_BLOCK_SIZE;
+ out += RC2_BLOCK_SIZE;
+ }
+ if (size) {
+ for (i = 0; i < size; i++)
+ tmp[i] = in[i] ^ iv[i];
+ for (i = size; i < RC2_BLOCK_SIZE; i++)
+ tmp[i] = iv[i];
+ RC2_encryptc(tmp, out, key);
+ memcpy(iv, out, RC2_BLOCK_SIZE);
+ }
+ } else {
+ while (size >= RC2_BLOCK_SIZE) {
+ memcpy(tmp, in, RC2_BLOCK_SIZE);
+ RC2_decryptc(tmp, out, key);
+ for (i = 0; i < RC2_BLOCK_SIZE; i++)
+ out[i] ^= iv[i];
+ memcpy(iv, tmp, RC2_BLOCK_SIZE);
+ size -= RC2_BLOCK_SIZE;
+ in += RC2_BLOCK_SIZE;
+ out += RC2_BLOCK_SIZE;
+ }
+ if (size) {
+ memcpy(tmp, in, RC2_BLOCK_SIZE);
+ RC2_decryptc(tmp, out, key);
+ for (i = 0; i < size; i++)
+ out[i] ^= iv[i];
+ memcpy(iv, tmp, RC2_BLOCK_SIZE);
+ }
+ }
+}
diff --git a/source4/heimdal/lib/hcrypto/rc2.h b/source4/heimdal/lib/hcrypto/rc2.h
new file mode 100644
index 0000000000..5e479fbdec
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/rc2.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+/* symbol renaming */
+#define RC2_set_key hc_RC2_set_key
+#define RC2_encryptc hc_RC2_encryptc
+#define RC2_decryptc hc_RC2_decryptc
+#define RC2_cbc_encrypt hc_RC2_cbc_encrypt
+
+/*
+ *
+ */
+
+#define RC2_ENCRYPT 1
+#define RC2_DECRYPT 0
+
+#define RC2_BLOCK_SIZE 8
+#define RC2_BLOCK RC2_BLOCK_SIZE
+#define RC2_KEY_LENGTH 16
+
+typedef struct rc2_key {
+ unsigned int data[64];
+} RC2_KEY;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void RC2_set_key(RC2_KEY *, int, const unsigned char *,int);
+
+void RC2_encryptc(unsigned char *, unsigned char *, const RC2_KEY *);
+void RC2_decryptc(unsigned char *, unsigned char *, const RC2_KEY *);
+
+void RC2_cbc_encrypt(const unsigned char *, unsigned char *, long,
+ RC2_KEY *, unsigned char *, int);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source4/heimdal/lib/hcrypto/rc4.c b/source4/heimdal/lib/hcrypto/rc4.c
new file mode 100644
index 0000000000..9e696f78a2
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/rc4.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* implemented from description in draft-kaukonen-cipher-arcfour-03.txt */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+
+RCSID("$Id$");
+#endif
+
+#include <rc4.h>
+
+#define SWAP(k,x,y) \
+{ unsigned int _t; \
+ _t = k->state[x]; \
+ k->state[x] = k->state[y]; \
+ k->state[y] = _t; \
+}
+
+void
+RC4_set_key(RC4_KEY *key, const int len, unsigned char *data)
+{
+ int i, j;
+
+ for (i = 0; i < 256; i++)
+ key->state[i] = i;
+ for (i = 0, j = 0; i < 256; i++) {
+ j = (j + key->state[i] + data[i % len]) % 256;
+ SWAP(key, i, j);
+ }
+ key->x = key->y = 0;
+}
+
+void
+RC4(RC4_KEY *key, const int len, const unsigned char *in, unsigned char *out)
+{
+ int i, t;
+ unsigned x, y;
+
+ x = key->x;
+ y = key->y;
+ for (i = 0; i < len; i++) {
+ x = (x + 1) % 256;
+ y = (y + key->state[x]) % 256;
+ SWAP(key, x, y);
+ t = (key->state[x] + key->state[y]) % 256;
+ *out++ = key->state[t] ^ *in++;
+ }
+ key->x = x;
+ key->y = y;
+}
diff --git a/source4/heimdal/lib/hcrypto/rc4.h b/source4/heimdal/lib/hcrypto/rc4.h
new file mode 100644
index 0000000000..4633655786
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/rc4.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+/* symbol renaming */
+#define RC4_set_key hc_RC4_set_key
+#define RC4 hc_RC4
+
+typedef struct rc4_key {
+ unsigned int x, y;
+ unsigned int state[256];
+} RC4_KEY;
+
+void RC4_set_key(RC4_KEY *, const int, unsigned char *);
+void RC4(RC4_KEY *, const int, const unsigned char *, unsigned char *);
diff --git a/source4/heimdal/lib/hcrypto/resource.h b/source4/heimdal/lib/hcrypto/resource.h
new file mode 100644
index 0000000000..9074dc1e5a
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/resource.h
@@ -0,0 +1,18 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by passwd_dialog.rc
+//
+#define IDD_PASSWD_DIALOG 101
+#define IDC_EDIT1 1000
+#define IDC_PASSWD_EDIT 1001
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 102
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1002
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/source4/heimdal/lib/hcrypto/rijndael-alg-fst.c b/source4/heimdal/lib/hcrypto/rijndael-alg-fst.c
new file mode 100644
index 0000000000..57f13177df
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/rijndael-alg-fst.c
@@ -0,0 +1,1231 @@
+/* $NetBSD: rijndael-alg-fst.c,v 1.5 2001/11/13 01:40:10 lukem Exp $ */
+/* $KAME: rijndael-alg-fst.c,v 1.10 2003/07/15 10:47:16 itojun Exp $ */
+/**
+ * rijndael-alg-fst.c
+ *
+ * @version 3.0 (December 2000)
+ *
+ * Optimised ANSI C code for the Rijndael cipher (now AES)
+ *
+ * @author Vincent Rijmen <vincent.rijmen@esat.kuleuven.ac.be>
+ * @author Antoon Bosselaers <antoon.bosselaers@esat.kuleuven.ac.be>
+ * @author Paulo Barreto <paulo.barreto@terra.com.br>
+ *
+ * This code is hereby placed in the public domain.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* "$NetBSD: rijndael-alg-fst.c,v 1.5 2001/11/13 01:40:10 lukem Exp $" */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+
+RCSID("$Id$");
+#endif
+
+#ifdef KRB5
+#include <krb5-types.h>
+#endif
+
+#include <rijndael-alg-fst.h>
+
+/* the file should not be used from outside */
+typedef uint8_t u8;
+typedef uint16_t u16;
+typedef uint32_t u32;
+
+/*
+Te0[x] = S [x].[02, 01, 01, 03];
+Te1[x] = S [x].[03, 02, 01, 01];
+Te2[x] = S [x].[01, 03, 02, 01];
+Te3[x] = S [x].[01, 01, 03, 02];
+Te4[x] = S [x].[01, 01, 01, 01];
+
+Td0[x] = Si[x].[0e, 09, 0d, 0b];
+Td1[x] = Si[x].[0b, 0e, 09, 0d];
+Td2[x] = Si[x].[0d, 0b, 0e, 09];
+Td3[x] = Si[x].[09, 0d, 0b, 0e];
+Td4[x] = Si[x].[01, 01, 01, 01];
+*/
+
+static const u32 Te0[256] = {
+ 0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU,
+ 0xfff2f20dU, 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U,
+ 0x60303050U, 0x02010103U, 0xce6767a9U, 0x562b2b7dU,
+ 0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U, 0xec76769aU,
+ 0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U,
+ 0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU,
+ 0x41adadecU, 0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU,
+ 0x239c9cbfU, 0x53a4a4f7U, 0xe4727296U, 0x9bc0c05bU,
+ 0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU, 0x4c26266aU,
+ 0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU,
+ 0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U,
+ 0xe2717193U, 0xabd8d873U, 0x62313153U, 0x2a15153fU,
+ 0x0804040cU, 0x95c7c752U, 0x46232365U, 0x9dc3c35eU,
+ 0x30181828U, 0x379696a1U, 0x0a05050fU, 0x2f9a9ab5U,
+ 0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU,
+ 0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU,
+ 0x1209091bU, 0x1d83839eU, 0x582c2c74U, 0x341a1a2eU,
+ 0x361b1b2dU, 0xdc6e6eb2U, 0xb45a5aeeU, 0x5ba0a0fbU,
+ 0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U, 0x7db3b3ceU,
+ 0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U,
+ 0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU,
+ 0x40202060U, 0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU,
+ 0xd46a6abeU, 0x8dcbcb46U, 0x67bebed9U, 0x7239394bU,
+ 0x944a4adeU, 0x984c4cd4U, 0xb05858e8U, 0x85cfcf4aU,
+ 0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U,
+ 0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U,
+ 0x8a4545cfU, 0xe9f9f910U, 0x04020206U, 0xfe7f7f81U,
+ 0xa05050f0U, 0x783c3c44U, 0x259f9fbaU, 0x4ba8a8e3U,
+ 0xa25151f3U, 0x5da3a3feU, 0x804040c0U, 0x058f8f8aU,
+ 0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U,
+ 0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U,
+ 0x20101030U, 0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU,
+ 0x81cdcd4cU, 0x180c0c14U, 0x26131335U, 0xc3ecec2fU,
+ 0xbe5f5fe1U, 0x359797a2U, 0x884444ccU, 0x2e171739U,
+ 0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U,
+ 0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U,
+ 0xc06060a0U, 0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU,
+ 0x44222266U, 0x542a2a7eU, 0x3b9090abU, 0x0b888883U,
+ 0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U, 0x2814143cU,
+ 0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U,
+ 0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU,
+ 0x924949dbU, 0x0c06060aU, 0x4824246cU, 0xb85c5ce4U,
+ 0x9fc2c25dU, 0xbdd3d36eU, 0x43acacefU, 0xc46262a6U,
+ 0x399191a8U, 0x319595a4U, 0xd3e4e437U, 0xf279798bU,
+ 0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U,
+ 0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U,
+ 0xd86c6cb4U, 0xac5656faU, 0xf3f4f407U, 0xcfeaea25U,
+ 0xca6565afU, 0xf47a7a8eU, 0x47aeaee9U, 0x10080818U,
+ 0x6fbabad5U, 0xf0787888U, 0x4a25256fU, 0x5c2e2e72U,
+ 0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U,
+ 0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U,
+ 0x964b4bddU, 0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U,
+ 0xe0707090U, 0x7c3e3e42U, 0x71b5b5c4U, 0xcc6666aaU,
+ 0x904848d8U, 0x06030305U, 0xf7f6f601U, 0x1c0e0e12U,
+ 0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U,
+ 0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U,
+ 0xd9e1e138U, 0xebf8f813U, 0x2b9898b3U, 0x22111133U,
+ 0xd26969bbU, 0xa9d9d970U, 0x078e8e89U, 0x339494a7U,
+ 0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U, 0xc9e9e920U,
+ 0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU,
+ 0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U,
+ 0x65bfbfdaU, 0xd7e6e631U, 0x844242c6U, 0xd06868b8U,
+ 0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U,
+ 0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU,
+};
+static const u32 Te1[256] = {
+ 0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU,
+ 0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U,
+ 0x50603030U, 0x03020101U, 0xa9ce6767U, 0x7d562b2bU,
+ 0x19e7fefeU, 0x62b5d7d7U, 0xe64dababU, 0x9aec7676U,
+ 0x458fcacaU, 0x9d1f8282U, 0x4089c9c9U, 0x87fa7d7dU,
+ 0x15effafaU, 0xebb25959U, 0xc98e4747U, 0x0bfbf0f0U,
+ 0xec41adadU, 0x67b3d4d4U, 0xfd5fa2a2U, 0xea45afafU,
+ 0xbf239c9cU, 0xf753a4a4U, 0x96e47272U, 0x5b9bc0c0U,
+ 0xc275b7b7U, 0x1ce1fdfdU, 0xae3d9393U, 0x6a4c2626U,
+ 0x5a6c3636U, 0x417e3f3fU, 0x02f5f7f7U, 0x4f83ccccU,
+ 0x5c683434U, 0xf451a5a5U, 0x34d1e5e5U, 0x08f9f1f1U,
+ 0x93e27171U, 0x73abd8d8U, 0x53623131U, 0x3f2a1515U,
+ 0x0c080404U, 0x5295c7c7U, 0x65462323U, 0x5e9dc3c3U,
+ 0x28301818U, 0xa1379696U, 0x0f0a0505U, 0xb52f9a9aU,
+ 0x090e0707U, 0x36241212U, 0x9b1b8080U, 0x3ddfe2e2U,
+ 0x26cdebebU, 0x694e2727U, 0xcd7fb2b2U, 0x9fea7575U,
+ 0x1b120909U, 0x9e1d8383U, 0x74582c2cU, 0x2e341a1aU,
+ 0x2d361b1bU, 0xb2dc6e6eU, 0xeeb45a5aU, 0xfb5ba0a0U,
+ 0xf6a45252U, 0x4d763b3bU, 0x61b7d6d6U, 0xce7db3b3U,
+ 0x7b522929U, 0x3edde3e3U, 0x715e2f2fU, 0x97138484U,
+ 0xf5a65353U, 0x68b9d1d1U, 0x00000000U, 0x2cc1ededU,
+ 0x60402020U, 0x1fe3fcfcU, 0xc879b1b1U, 0xedb65b5bU,
+ 0xbed46a6aU, 0x468dcbcbU, 0xd967bebeU, 0x4b723939U,
+ 0xde944a4aU, 0xd4984c4cU, 0xe8b05858U, 0x4a85cfcfU,
+ 0x6bbbd0d0U, 0x2ac5efefU, 0xe54faaaaU, 0x16edfbfbU,
+ 0xc5864343U, 0xd79a4d4dU, 0x55663333U, 0x94118585U,
+ 0xcf8a4545U, 0x10e9f9f9U, 0x06040202U, 0x81fe7f7fU,
+ 0xf0a05050U, 0x44783c3cU, 0xba259f9fU, 0xe34ba8a8U,
+ 0xf3a25151U, 0xfe5da3a3U, 0xc0804040U, 0x8a058f8fU,
+ 0xad3f9292U, 0xbc219d9dU, 0x48703838U, 0x04f1f5f5U,
+ 0xdf63bcbcU, 0xc177b6b6U, 0x75afdadaU, 0x63422121U,
+ 0x30201010U, 0x1ae5ffffU, 0x0efdf3f3U, 0x6dbfd2d2U,
+ 0x4c81cdcdU, 0x14180c0cU, 0x35261313U, 0x2fc3ececU,
+ 0xe1be5f5fU, 0xa2359797U, 0xcc884444U, 0x392e1717U,
+ 0x5793c4c4U, 0xf255a7a7U, 0x82fc7e7eU, 0x477a3d3dU,
+ 0xacc86464U, 0xe7ba5d5dU, 0x2b321919U, 0x95e67373U,
+ 0xa0c06060U, 0x98198181U, 0xd19e4f4fU, 0x7fa3dcdcU,
+ 0x66442222U, 0x7e542a2aU, 0xab3b9090U, 0x830b8888U,
+ 0xca8c4646U, 0x29c7eeeeU, 0xd36bb8b8U, 0x3c281414U,
+ 0x79a7dedeU, 0xe2bc5e5eU, 0x1d160b0bU, 0x76addbdbU,
+ 0x3bdbe0e0U, 0x56643232U, 0x4e743a3aU, 0x1e140a0aU,
+ 0xdb924949U, 0x0a0c0606U, 0x6c482424U, 0xe4b85c5cU,
+ 0x5d9fc2c2U, 0x6ebdd3d3U, 0xef43acacU, 0xa6c46262U,
+ 0xa8399191U, 0xa4319595U, 0x37d3e4e4U, 0x8bf27979U,
+ 0x32d5e7e7U, 0x438bc8c8U, 0x596e3737U, 0xb7da6d6dU,
+ 0x8c018d8dU, 0x64b1d5d5U, 0xd29c4e4eU, 0xe049a9a9U,
+ 0xb4d86c6cU, 0xfaac5656U, 0x07f3f4f4U, 0x25cfeaeaU,
+ 0xafca6565U, 0x8ef47a7aU, 0xe947aeaeU, 0x18100808U,
+ 0xd56fbabaU, 0x88f07878U, 0x6f4a2525U, 0x725c2e2eU,
+ 0x24381c1cU, 0xf157a6a6U, 0xc773b4b4U, 0x5197c6c6U,
+ 0x23cbe8e8U, 0x7ca1ddddU, 0x9ce87474U, 0x213e1f1fU,
+ 0xdd964b4bU, 0xdc61bdbdU, 0x860d8b8bU, 0x850f8a8aU,
+ 0x90e07070U, 0x427c3e3eU, 0xc471b5b5U, 0xaacc6666U,
+ 0xd8904848U, 0x05060303U, 0x01f7f6f6U, 0x121c0e0eU,
+ 0xa3c26161U, 0x5f6a3535U, 0xf9ae5757U, 0xd069b9b9U,
+ 0x91178686U, 0x5899c1c1U, 0x273a1d1dU, 0xb9279e9eU,
+ 0x38d9e1e1U, 0x13ebf8f8U, 0xb32b9898U, 0x33221111U,
+ 0xbbd26969U, 0x70a9d9d9U, 0x89078e8eU, 0xa7339494U,
+ 0xb62d9b9bU, 0x223c1e1eU, 0x92158787U, 0x20c9e9e9U,
+ 0x4987ceceU, 0xffaa5555U, 0x78502828U, 0x7aa5dfdfU,
+ 0x8f038c8cU, 0xf859a1a1U, 0x80098989U, 0x171a0d0dU,
+ 0xda65bfbfU, 0x31d7e6e6U, 0xc6844242U, 0xb8d06868U,
+ 0xc3824141U, 0xb0299999U, 0x775a2d2dU, 0x111e0f0fU,
+ 0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU, 0x3a2c1616U,
+};
+static const u32 Te2[256] = {
+ 0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU,
+ 0xf20dfff2U, 0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U,
+ 0x30506030U, 0x01030201U, 0x67a9ce67U, 0x2b7d562bU,
+ 0xfe19e7feU, 0xd762b5d7U, 0xabe64dabU, 0x769aec76U,
+ 0xca458fcaU, 0x829d1f82U, 0xc94089c9U, 0x7d87fa7dU,
+ 0xfa15effaU, 0x59ebb259U, 0x47c98e47U, 0xf00bfbf0U,
+ 0xadec41adU, 0xd467b3d4U, 0xa2fd5fa2U, 0xafea45afU,
+ 0x9cbf239cU, 0xa4f753a4U, 0x7296e472U, 0xc05b9bc0U,
+ 0xb7c275b7U, 0xfd1ce1fdU, 0x93ae3d93U, 0x266a4c26U,
+ 0x365a6c36U, 0x3f417e3fU, 0xf702f5f7U, 0xcc4f83ccU,
+ 0x345c6834U, 0xa5f451a5U, 0xe534d1e5U, 0xf108f9f1U,
+ 0x7193e271U, 0xd873abd8U, 0x31536231U, 0x153f2a15U,
+ 0x040c0804U, 0xc75295c7U, 0x23654623U, 0xc35e9dc3U,
+ 0x18283018U, 0x96a13796U, 0x050f0a05U, 0x9ab52f9aU,
+ 0x07090e07U, 0x12362412U, 0x809b1b80U, 0xe23ddfe2U,
+ 0xeb26cdebU, 0x27694e27U, 0xb2cd7fb2U, 0x759fea75U,
+ 0x091b1209U, 0x839e1d83U, 0x2c74582cU, 0x1a2e341aU,
+ 0x1b2d361bU, 0x6eb2dc6eU, 0x5aeeb45aU, 0xa0fb5ba0U,
+ 0x52f6a452U, 0x3b4d763bU, 0xd661b7d6U, 0xb3ce7db3U,
+ 0x297b5229U, 0xe33edde3U, 0x2f715e2fU, 0x84971384U,
+ 0x53f5a653U, 0xd168b9d1U, 0x00000000U, 0xed2cc1edU,
+ 0x20604020U, 0xfc1fe3fcU, 0xb1c879b1U, 0x5bedb65bU,
+ 0x6abed46aU, 0xcb468dcbU, 0xbed967beU, 0x394b7239U,
+ 0x4ade944aU, 0x4cd4984cU, 0x58e8b058U, 0xcf4a85cfU,
+ 0xd06bbbd0U, 0xef2ac5efU, 0xaae54faaU, 0xfb16edfbU,
+ 0x43c58643U, 0x4dd79a4dU, 0x33556633U, 0x85941185U,
+ 0x45cf8a45U, 0xf910e9f9U, 0x02060402U, 0x7f81fe7fU,
+ 0x50f0a050U, 0x3c44783cU, 0x9fba259fU, 0xa8e34ba8U,
+ 0x51f3a251U, 0xa3fe5da3U, 0x40c08040U, 0x8f8a058fU,
+ 0x92ad3f92U, 0x9dbc219dU, 0x38487038U, 0xf504f1f5U,
+ 0xbcdf63bcU, 0xb6c177b6U, 0xda75afdaU, 0x21634221U,
+ 0x10302010U, 0xff1ae5ffU, 0xf30efdf3U, 0xd26dbfd2U,
+ 0xcd4c81cdU, 0x0c14180cU, 0x13352613U, 0xec2fc3ecU,
+ 0x5fe1be5fU, 0x97a23597U, 0x44cc8844U, 0x17392e17U,
+ 0xc45793c4U, 0xa7f255a7U, 0x7e82fc7eU, 0x3d477a3dU,
+ 0x64acc864U, 0x5de7ba5dU, 0x192b3219U, 0x7395e673U,
+ 0x60a0c060U, 0x81981981U, 0x4fd19e4fU, 0xdc7fa3dcU,
+ 0x22664422U, 0x2a7e542aU, 0x90ab3b90U, 0x88830b88U,
+ 0x46ca8c46U, 0xee29c7eeU, 0xb8d36bb8U, 0x143c2814U,
+ 0xde79a7deU, 0x5ee2bc5eU, 0x0b1d160bU, 0xdb76addbU,
+ 0xe03bdbe0U, 0x32566432U, 0x3a4e743aU, 0x0a1e140aU,
+ 0x49db9249U, 0x060a0c06U, 0x246c4824U, 0x5ce4b85cU,
+ 0xc25d9fc2U, 0xd36ebdd3U, 0xacef43acU, 0x62a6c462U,
+ 0x91a83991U, 0x95a43195U, 0xe437d3e4U, 0x798bf279U,
+ 0xe732d5e7U, 0xc8438bc8U, 0x37596e37U, 0x6db7da6dU,
+ 0x8d8c018dU, 0xd564b1d5U, 0x4ed29c4eU, 0xa9e049a9U,
+ 0x6cb4d86cU, 0x56faac56U, 0xf407f3f4U, 0xea25cfeaU,
+ 0x65afca65U, 0x7a8ef47aU, 0xaee947aeU, 0x08181008U,
+ 0xbad56fbaU, 0x7888f078U, 0x256f4a25U, 0x2e725c2eU,
+ 0x1c24381cU, 0xa6f157a6U, 0xb4c773b4U, 0xc65197c6U,
+ 0xe823cbe8U, 0xdd7ca1ddU, 0x749ce874U, 0x1f213e1fU,
+ 0x4bdd964bU, 0xbddc61bdU, 0x8b860d8bU, 0x8a850f8aU,
+ 0x7090e070U, 0x3e427c3eU, 0xb5c471b5U, 0x66aacc66U,
+ 0x48d89048U, 0x03050603U, 0xf601f7f6U, 0x0e121c0eU,
+ 0x61a3c261U, 0x355f6a35U, 0x57f9ae57U, 0xb9d069b9U,
+ 0x86911786U, 0xc15899c1U, 0x1d273a1dU, 0x9eb9279eU,
+ 0xe138d9e1U, 0xf813ebf8U, 0x98b32b98U, 0x11332211U,
+ 0x69bbd269U, 0xd970a9d9U, 0x8e89078eU, 0x94a73394U,
+ 0x9bb62d9bU, 0x1e223c1eU, 0x87921587U, 0xe920c9e9U,
+ 0xce4987ceU, 0x55ffaa55U, 0x28785028U, 0xdf7aa5dfU,
+ 0x8c8f038cU, 0xa1f859a1U, 0x89800989U, 0x0d171a0dU,
+ 0xbfda65bfU, 0xe631d7e6U, 0x42c68442U, 0x68b8d068U,
+ 0x41c38241U, 0x99b02999U, 0x2d775a2dU, 0x0f111e0fU,
+ 0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU, 0x163a2c16U,
+};
+static const u32 Te3[256] = {
+
+ 0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U,
+ 0xf2f20dffU, 0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U,
+ 0x30305060U, 0x01010302U, 0x6767a9ceU, 0x2b2b7d56U,
+ 0xfefe19e7U, 0xd7d762b5U, 0xababe64dU, 0x76769aecU,
+ 0xcaca458fU, 0x82829d1fU, 0xc9c94089U, 0x7d7d87faU,
+ 0xfafa15efU, 0x5959ebb2U, 0x4747c98eU, 0xf0f00bfbU,
+ 0xadadec41U, 0xd4d467b3U, 0xa2a2fd5fU, 0xafafea45U,
+ 0x9c9cbf23U, 0xa4a4f753U, 0x727296e4U, 0xc0c05b9bU,
+ 0xb7b7c275U, 0xfdfd1ce1U, 0x9393ae3dU, 0x26266a4cU,
+ 0x36365a6cU, 0x3f3f417eU, 0xf7f702f5U, 0xcccc4f83U,
+ 0x34345c68U, 0xa5a5f451U, 0xe5e534d1U, 0xf1f108f9U,
+ 0x717193e2U, 0xd8d873abU, 0x31315362U, 0x15153f2aU,
+ 0x04040c08U, 0xc7c75295U, 0x23236546U, 0xc3c35e9dU,
+ 0x18182830U, 0x9696a137U, 0x05050f0aU, 0x9a9ab52fU,
+ 0x0707090eU, 0x12123624U, 0x80809b1bU, 0xe2e23ddfU,
+ 0xebeb26cdU, 0x2727694eU, 0xb2b2cd7fU, 0x75759feaU,
+ 0x09091b12U, 0x83839e1dU, 0x2c2c7458U, 0x1a1a2e34U,
+ 0x1b1b2d36U, 0x6e6eb2dcU, 0x5a5aeeb4U, 0xa0a0fb5bU,
+ 0x5252f6a4U, 0x3b3b4d76U, 0xd6d661b7U, 0xb3b3ce7dU,
+ 0x29297b52U, 0xe3e33eddU, 0x2f2f715eU, 0x84849713U,
+ 0x5353f5a6U, 0xd1d168b9U, 0x00000000U, 0xeded2cc1U,
+ 0x20206040U, 0xfcfc1fe3U, 0xb1b1c879U, 0x5b5bedb6U,
+ 0x6a6abed4U, 0xcbcb468dU, 0xbebed967U, 0x39394b72U,
+ 0x4a4ade94U, 0x4c4cd498U, 0x5858e8b0U, 0xcfcf4a85U,
+ 0xd0d06bbbU, 0xefef2ac5U, 0xaaaae54fU, 0xfbfb16edU,
+ 0x4343c586U, 0x4d4dd79aU, 0x33335566U, 0x85859411U,
+ 0x4545cf8aU, 0xf9f910e9U, 0x02020604U, 0x7f7f81feU,
+ 0x5050f0a0U, 0x3c3c4478U, 0x9f9fba25U, 0xa8a8e34bU,
+ 0x5151f3a2U, 0xa3a3fe5dU, 0x4040c080U, 0x8f8f8a05U,
+ 0x9292ad3fU, 0x9d9dbc21U, 0x38384870U, 0xf5f504f1U,
+ 0xbcbcdf63U, 0xb6b6c177U, 0xdada75afU, 0x21216342U,
+ 0x10103020U, 0xffff1ae5U, 0xf3f30efdU, 0xd2d26dbfU,
+ 0xcdcd4c81U, 0x0c0c1418U, 0x13133526U, 0xecec2fc3U,
+ 0x5f5fe1beU, 0x9797a235U, 0x4444cc88U, 0x1717392eU,
+ 0xc4c45793U, 0xa7a7f255U, 0x7e7e82fcU, 0x3d3d477aU,
+ 0x6464acc8U, 0x5d5de7baU, 0x19192b32U, 0x737395e6U,
+ 0x6060a0c0U, 0x81819819U, 0x4f4fd19eU, 0xdcdc7fa3U,
+ 0x22226644U, 0x2a2a7e54U, 0x9090ab3bU, 0x8888830bU,
+ 0x4646ca8cU, 0xeeee29c7U, 0xb8b8d36bU, 0x14143c28U,
+ 0xdede79a7U, 0x5e5ee2bcU, 0x0b0b1d16U, 0xdbdb76adU,
+ 0xe0e03bdbU, 0x32325664U, 0x3a3a4e74U, 0x0a0a1e14U,
+ 0x4949db92U, 0x06060a0cU, 0x24246c48U, 0x5c5ce4b8U,
+ 0xc2c25d9fU, 0xd3d36ebdU, 0xacacef43U, 0x6262a6c4U,
+ 0x9191a839U, 0x9595a431U, 0xe4e437d3U, 0x79798bf2U,
+ 0xe7e732d5U, 0xc8c8438bU, 0x3737596eU, 0x6d6db7daU,
+ 0x8d8d8c01U, 0xd5d564b1U, 0x4e4ed29cU, 0xa9a9e049U,
+ 0x6c6cb4d8U, 0x5656faacU, 0xf4f407f3U, 0xeaea25cfU,
+ 0x6565afcaU, 0x7a7a8ef4U, 0xaeaee947U, 0x08081810U,
+ 0xbabad56fU, 0x787888f0U, 0x25256f4aU, 0x2e2e725cU,
+ 0x1c1c2438U, 0xa6a6f157U, 0xb4b4c773U, 0xc6c65197U,
+ 0xe8e823cbU, 0xdddd7ca1U, 0x74749ce8U, 0x1f1f213eU,
+ 0x4b4bdd96U, 0xbdbddc61U, 0x8b8b860dU, 0x8a8a850fU,
+ 0x707090e0U, 0x3e3e427cU, 0xb5b5c471U, 0x6666aaccU,
+ 0x4848d890U, 0x03030506U, 0xf6f601f7U, 0x0e0e121cU,
+ 0x6161a3c2U, 0x35355f6aU, 0x5757f9aeU, 0xb9b9d069U,
+ 0x86869117U, 0xc1c15899U, 0x1d1d273aU, 0x9e9eb927U,
+ 0xe1e138d9U, 0xf8f813ebU, 0x9898b32bU, 0x11113322U,
+ 0x6969bbd2U, 0xd9d970a9U, 0x8e8e8907U, 0x9494a733U,
+ 0x9b9bb62dU, 0x1e1e223cU, 0x87879215U, 0xe9e920c9U,
+ 0xcece4987U, 0x5555ffaaU, 0x28287850U, 0xdfdf7aa5U,
+ 0x8c8c8f03U, 0xa1a1f859U, 0x89898009U, 0x0d0d171aU,
+ 0xbfbfda65U, 0xe6e631d7U, 0x4242c684U, 0x6868b8d0U,
+ 0x4141c382U, 0x9999b029U, 0x2d2d775aU, 0x0f0f111eU,
+ 0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU, 0x16163a2cU,
+};
+static const u32 Te4[256] = {
+ 0x63636363U, 0x7c7c7c7cU, 0x77777777U, 0x7b7b7b7bU,
+ 0xf2f2f2f2U, 0x6b6b6b6bU, 0x6f6f6f6fU, 0xc5c5c5c5U,
+ 0x30303030U, 0x01010101U, 0x67676767U, 0x2b2b2b2bU,
+ 0xfefefefeU, 0xd7d7d7d7U, 0xababababU, 0x76767676U,
+ 0xcacacacaU, 0x82828282U, 0xc9c9c9c9U, 0x7d7d7d7dU,
+ 0xfafafafaU, 0x59595959U, 0x47474747U, 0xf0f0f0f0U,
+ 0xadadadadU, 0xd4d4d4d4U, 0xa2a2a2a2U, 0xafafafafU,
+ 0x9c9c9c9cU, 0xa4a4a4a4U, 0x72727272U, 0xc0c0c0c0U,
+ 0xb7b7b7b7U, 0xfdfdfdfdU, 0x93939393U, 0x26262626U,
+ 0x36363636U, 0x3f3f3f3fU, 0xf7f7f7f7U, 0xccccccccU,
+ 0x34343434U, 0xa5a5a5a5U, 0xe5e5e5e5U, 0xf1f1f1f1U,
+ 0x71717171U, 0xd8d8d8d8U, 0x31313131U, 0x15151515U,
+ 0x04040404U, 0xc7c7c7c7U, 0x23232323U, 0xc3c3c3c3U,
+ 0x18181818U, 0x96969696U, 0x05050505U, 0x9a9a9a9aU,
+ 0x07070707U, 0x12121212U, 0x80808080U, 0xe2e2e2e2U,
+ 0xebebebebU, 0x27272727U, 0xb2b2b2b2U, 0x75757575U,
+ 0x09090909U, 0x83838383U, 0x2c2c2c2cU, 0x1a1a1a1aU,
+ 0x1b1b1b1bU, 0x6e6e6e6eU, 0x5a5a5a5aU, 0xa0a0a0a0U,
+ 0x52525252U, 0x3b3b3b3bU, 0xd6d6d6d6U, 0xb3b3b3b3U,
+ 0x29292929U, 0xe3e3e3e3U, 0x2f2f2f2fU, 0x84848484U,
+ 0x53535353U, 0xd1d1d1d1U, 0x00000000U, 0xededededU,
+ 0x20202020U, 0xfcfcfcfcU, 0xb1b1b1b1U, 0x5b5b5b5bU,
+ 0x6a6a6a6aU, 0xcbcbcbcbU, 0xbebebebeU, 0x39393939U,
+ 0x4a4a4a4aU, 0x4c4c4c4cU, 0x58585858U, 0xcfcfcfcfU,
+ 0xd0d0d0d0U, 0xefefefefU, 0xaaaaaaaaU, 0xfbfbfbfbU,
+ 0x43434343U, 0x4d4d4d4dU, 0x33333333U, 0x85858585U,
+ 0x45454545U, 0xf9f9f9f9U, 0x02020202U, 0x7f7f7f7fU,
+ 0x50505050U, 0x3c3c3c3cU, 0x9f9f9f9fU, 0xa8a8a8a8U,
+ 0x51515151U, 0xa3a3a3a3U, 0x40404040U, 0x8f8f8f8fU,
+ 0x92929292U, 0x9d9d9d9dU, 0x38383838U, 0xf5f5f5f5U,
+ 0xbcbcbcbcU, 0xb6b6b6b6U, 0xdadadadaU, 0x21212121U,
+ 0x10101010U, 0xffffffffU, 0xf3f3f3f3U, 0xd2d2d2d2U,
+ 0xcdcdcdcdU, 0x0c0c0c0cU, 0x13131313U, 0xececececU,
+ 0x5f5f5f5fU, 0x97979797U, 0x44444444U, 0x17171717U,
+ 0xc4c4c4c4U, 0xa7a7a7a7U, 0x7e7e7e7eU, 0x3d3d3d3dU,
+ 0x64646464U, 0x5d5d5d5dU, 0x19191919U, 0x73737373U,
+ 0x60606060U, 0x81818181U, 0x4f4f4f4fU, 0xdcdcdcdcU,
+ 0x22222222U, 0x2a2a2a2aU, 0x90909090U, 0x88888888U,
+ 0x46464646U, 0xeeeeeeeeU, 0xb8b8b8b8U, 0x14141414U,
+ 0xdedededeU, 0x5e5e5e5eU, 0x0b0b0b0bU, 0xdbdbdbdbU,
+ 0xe0e0e0e0U, 0x32323232U, 0x3a3a3a3aU, 0x0a0a0a0aU,
+ 0x49494949U, 0x06060606U, 0x24242424U, 0x5c5c5c5cU,
+ 0xc2c2c2c2U, 0xd3d3d3d3U, 0xacacacacU, 0x62626262U,
+ 0x91919191U, 0x95959595U, 0xe4e4e4e4U, 0x79797979U,
+ 0xe7e7e7e7U, 0xc8c8c8c8U, 0x37373737U, 0x6d6d6d6dU,
+ 0x8d8d8d8dU, 0xd5d5d5d5U, 0x4e4e4e4eU, 0xa9a9a9a9U,
+ 0x6c6c6c6cU, 0x56565656U, 0xf4f4f4f4U, 0xeaeaeaeaU,
+ 0x65656565U, 0x7a7a7a7aU, 0xaeaeaeaeU, 0x08080808U,
+ 0xbabababaU, 0x78787878U, 0x25252525U, 0x2e2e2e2eU,
+ 0x1c1c1c1cU, 0xa6a6a6a6U, 0xb4b4b4b4U, 0xc6c6c6c6U,
+ 0xe8e8e8e8U, 0xddddddddU, 0x74747474U, 0x1f1f1f1fU,
+ 0x4b4b4b4bU, 0xbdbdbdbdU, 0x8b8b8b8bU, 0x8a8a8a8aU,
+ 0x70707070U, 0x3e3e3e3eU, 0xb5b5b5b5U, 0x66666666U,
+ 0x48484848U, 0x03030303U, 0xf6f6f6f6U, 0x0e0e0e0eU,
+ 0x61616161U, 0x35353535U, 0x57575757U, 0xb9b9b9b9U,
+ 0x86868686U, 0xc1c1c1c1U, 0x1d1d1d1dU, 0x9e9e9e9eU,
+ 0xe1e1e1e1U, 0xf8f8f8f8U, 0x98989898U, 0x11111111U,
+ 0x69696969U, 0xd9d9d9d9U, 0x8e8e8e8eU, 0x94949494U,
+ 0x9b9b9b9bU, 0x1e1e1e1eU, 0x87878787U, 0xe9e9e9e9U,
+ 0xcecececeU, 0x55555555U, 0x28282828U, 0xdfdfdfdfU,
+ 0x8c8c8c8cU, 0xa1a1a1a1U, 0x89898989U, 0x0d0d0d0dU,
+ 0xbfbfbfbfU, 0xe6e6e6e6U, 0x42424242U, 0x68686868U,
+ 0x41414141U, 0x99999999U, 0x2d2d2d2dU, 0x0f0f0f0fU,
+ 0xb0b0b0b0U, 0x54545454U, 0xbbbbbbbbU, 0x16161616U,
+};
+static const u32 Td0[256] = {
+ 0x51f4a750U, 0x7e416553U, 0x1a17a4c3U, 0x3a275e96U,
+ 0x3bab6bcbU, 0x1f9d45f1U, 0xacfa58abU, 0x4be30393U,
+ 0x2030fa55U, 0xad766df6U, 0x88cc7691U, 0xf5024c25U,
+ 0x4fe5d7fcU, 0xc52acbd7U, 0x26354480U, 0xb562a38fU,
+ 0xdeb15a49U, 0x25ba1b67U, 0x45ea0e98U, 0x5dfec0e1U,
+ 0xc32f7502U, 0x814cf012U, 0x8d4697a3U, 0x6bd3f9c6U,
+ 0x038f5fe7U, 0x15929c95U, 0xbf6d7aebU, 0x955259daU,
+ 0xd4be832dU, 0x587421d3U, 0x49e06929U, 0x8ec9c844U,
+ 0x75c2896aU, 0xf48e7978U, 0x99583e6bU, 0x27b971ddU,
+ 0xbee14fb6U, 0xf088ad17U, 0xc920ac66U, 0x7dce3ab4U,
+ 0x63df4a18U, 0xe51a3182U, 0x97513360U, 0x62537f45U,
+ 0xb16477e0U, 0xbb6bae84U, 0xfe81a01cU, 0xf9082b94U,
+ 0x70486858U, 0x8f45fd19U, 0x94de6c87U, 0x527bf8b7U,
+ 0xab73d323U, 0x724b02e2U, 0xe31f8f57U, 0x6655ab2aU,
+ 0xb2eb2807U, 0x2fb5c203U, 0x86c57b9aU, 0xd33708a5U,
+ 0x302887f2U, 0x23bfa5b2U, 0x02036abaU, 0xed16825cU,
+ 0x8acf1c2bU, 0xa779b492U, 0xf307f2f0U, 0x4e69e2a1U,
+ 0x65daf4cdU, 0x0605bed5U, 0xd134621fU, 0xc4a6fe8aU,
+ 0x342e539dU, 0xa2f355a0U, 0x058ae132U, 0xa4f6eb75U,
+ 0x0b83ec39U, 0x4060efaaU, 0x5e719f06U, 0xbd6e1051U,
+ 0x3e218af9U, 0x96dd063dU, 0xdd3e05aeU, 0x4de6bd46U,
+ 0x91548db5U, 0x71c45d05U, 0x0406d46fU, 0x605015ffU,
+ 0x1998fb24U, 0xd6bde997U, 0x894043ccU, 0x67d99e77U,
+ 0xb0e842bdU, 0x07898b88U, 0xe7195b38U, 0x79c8eedbU,
+ 0xa17c0a47U, 0x7c420fe9U, 0xf8841ec9U, 0x00000000U,
+ 0x09808683U, 0x322bed48U, 0x1e1170acU, 0x6c5a724eU,
+ 0xfd0efffbU, 0x0f853856U, 0x3daed51eU, 0x362d3927U,
+ 0x0a0fd964U, 0x685ca621U, 0x9b5b54d1U, 0x24362e3aU,
+ 0x0c0a67b1U, 0x9357e70fU, 0xb4ee96d2U, 0x1b9b919eU,
+ 0x80c0c54fU, 0x61dc20a2U, 0x5a774b69U, 0x1c121a16U,
+ 0xe293ba0aU, 0xc0a02ae5U, 0x3c22e043U, 0x121b171dU,
+ 0x0e090d0bU, 0xf28bc7adU, 0x2db6a8b9U, 0x141ea9c8U,
+ 0x57f11985U, 0xaf75074cU, 0xee99ddbbU, 0xa37f60fdU,
+ 0xf701269fU, 0x5c72f5bcU, 0x44663bc5U, 0x5bfb7e34U,
+ 0x8b432976U, 0xcb23c6dcU, 0xb6edfc68U, 0xb8e4f163U,
+ 0xd731dccaU, 0x42638510U, 0x13972240U, 0x84c61120U,
+ 0x854a247dU, 0xd2bb3df8U, 0xaef93211U, 0xc729a16dU,
+ 0x1d9e2f4bU, 0xdcb230f3U, 0x0d8652ecU, 0x77c1e3d0U,
+ 0x2bb3166cU, 0xa970b999U, 0x119448faU, 0x47e96422U,
+ 0xa8fc8cc4U, 0xa0f03f1aU, 0x567d2cd8U, 0x223390efU,
+ 0x87494ec7U, 0xd938d1c1U, 0x8ccaa2feU, 0x98d40b36U,
+ 0xa6f581cfU, 0xa57ade28U, 0xdab78e26U, 0x3fadbfa4U,
+ 0x2c3a9de4U, 0x5078920dU, 0x6a5fcc9bU, 0x547e4662U,
+ 0xf68d13c2U, 0x90d8b8e8U, 0x2e39f75eU, 0x82c3aff5U,
+ 0x9f5d80beU, 0x69d0937cU, 0x6fd52da9U, 0xcf2512b3U,
+ 0xc8ac993bU, 0x10187da7U, 0xe89c636eU, 0xdb3bbb7bU,
+ 0xcd267809U, 0x6e5918f4U, 0xec9ab701U, 0x834f9aa8U,
+ 0xe6956e65U, 0xaaffe67eU, 0x21bccf08U, 0xef15e8e6U,
+ 0xbae79bd9U, 0x4a6f36ceU, 0xea9f09d4U, 0x29b07cd6U,
+ 0x31a4b2afU, 0x2a3f2331U, 0xc6a59430U, 0x35a266c0U,
+ 0x744ebc37U, 0xfc82caa6U, 0xe090d0b0U, 0x33a7d815U,
+ 0xf104984aU, 0x41ecdaf7U, 0x7fcd500eU, 0x1791f62fU,
+ 0x764dd68dU, 0x43efb04dU, 0xccaa4d54U, 0xe49604dfU,
+ 0x9ed1b5e3U, 0x4c6a881bU, 0xc12c1fb8U, 0x4665517fU,
+ 0x9d5eea04U, 0x018c355dU, 0xfa877473U, 0xfb0b412eU,
+ 0xb3671d5aU, 0x92dbd252U, 0xe9105633U, 0x6dd64713U,
+ 0x9ad7618cU, 0x37a10c7aU, 0x59f8148eU, 0xeb133c89U,
+ 0xcea927eeU, 0xb761c935U, 0xe11ce5edU, 0x7a47b13cU,
+ 0x9cd2df59U, 0x55f2733fU, 0x1814ce79U, 0x73c737bfU,
+ 0x53f7cdeaU, 0x5ffdaa5bU, 0xdf3d6f14U, 0x7844db86U,
+ 0xcaaff381U, 0xb968c43eU, 0x3824342cU, 0xc2a3405fU,
+ 0x161dc372U, 0xbce2250cU, 0x283c498bU, 0xff0d9541U,
+ 0x39a80171U, 0x080cb3deU, 0xd8b4e49cU, 0x6456c190U,
+ 0x7bcb8461U, 0xd532b670U, 0x486c5c74U, 0xd0b85742U,
+};
+static const u32 Td1[256] = {
+ 0x5051f4a7U, 0x537e4165U, 0xc31a17a4U, 0x963a275eU,
+ 0xcb3bab6bU, 0xf11f9d45U, 0xabacfa58U, 0x934be303U,
+ 0x552030faU, 0xf6ad766dU, 0x9188cc76U, 0x25f5024cU,
+ 0xfc4fe5d7U, 0xd7c52acbU, 0x80263544U, 0x8fb562a3U,
+ 0x49deb15aU, 0x6725ba1bU, 0x9845ea0eU, 0xe15dfec0U,
+ 0x02c32f75U, 0x12814cf0U, 0xa38d4697U, 0xc66bd3f9U,
+ 0xe7038f5fU, 0x9515929cU, 0xebbf6d7aU, 0xda955259U,
+ 0x2dd4be83U, 0xd3587421U, 0x2949e069U, 0x448ec9c8U,
+ 0x6a75c289U, 0x78f48e79U, 0x6b99583eU, 0xdd27b971U,
+ 0xb6bee14fU, 0x17f088adU, 0x66c920acU, 0xb47dce3aU,
+ 0x1863df4aU, 0x82e51a31U, 0x60975133U, 0x4562537fU,
+ 0xe0b16477U, 0x84bb6baeU, 0x1cfe81a0U, 0x94f9082bU,
+ 0x58704868U, 0x198f45fdU, 0x8794de6cU, 0xb7527bf8U,
+ 0x23ab73d3U, 0xe2724b02U, 0x57e31f8fU, 0x2a6655abU,
+ 0x07b2eb28U, 0x032fb5c2U, 0x9a86c57bU, 0xa5d33708U,
+ 0xf2302887U, 0xb223bfa5U, 0xba02036aU, 0x5ced1682U,
+ 0x2b8acf1cU, 0x92a779b4U, 0xf0f307f2U, 0xa14e69e2U,
+ 0xcd65daf4U, 0xd50605beU, 0x1fd13462U, 0x8ac4a6feU,
+ 0x9d342e53U, 0xa0a2f355U, 0x32058ae1U, 0x75a4f6ebU,
+ 0x390b83ecU, 0xaa4060efU, 0x065e719fU, 0x51bd6e10U,
+ 0xf93e218aU, 0x3d96dd06U, 0xaedd3e05U, 0x464de6bdU,
+ 0xb591548dU, 0x0571c45dU, 0x6f0406d4U, 0xff605015U,
+ 0x241998fbU, 0x97d6bde9U, 0xcc894043U, 0x7767d99eU,
+ 0xbdb0e842U, 0x8807898bU, 0x38e7195bU, 0xdb79c8eeU,
+ 0x47a17c0aU, 0xe97c420fU, 0xc9f8841eU, 0x00000000U,
+ 0x83098086U, 0x48322bedU, 0xac1e1170U, 0x4e6c5a72U,
+ 0xfbfd0effU, 0x560f8538U, 0x1e3daed5U, 0x27362d39U,
+ 0x640a0fd9U, 0x21685ca6U, 0xd19b5b54U, 0x3a24362eU,
+ 0xb10c0a67U, 0x0f9357e7U, 0xd2b4ee96U, 0x9e1b9b91U,
+ 0x4f80c0c5U, 0xa261dc20U, 0x695a774bU, 0x161c121aU,
+ 0x0ae293baU, 0xe5c0a02aU, 0x433c22e0U, 0x1d121b17U,
+ 0x0b0e090dU, 0xadf28bc7U, 0xb92db6a8U, 0xc8141ea9U,
+ 0x8557f119U, 0x4caf7507U, 0xbbee99ddU, 0xfda37f60U,
+ 0x9ff70126U, 0xbc5c72f5U, 0xc544663bU, 0x345bfb7eU,
+ 0x768b4329U, 0xdccb23c6U, 0x68b6edfcU, 0x63b8e4f1U,
+ 0xcad731dcU, 0x10426385U, 0x40139722U, 0x2084c611U,
+ 0x7d854a24U, 0xf8d2bb3dU, 0x11aef932U, 0x6dc729a1U,
+ 0x4b1d9e2fU, 0xf3dcb230U, 0xec0d8652U, 0xd077c1e3U,
+ 0x6c2bb316U, 0x99a970b9U, 0xfa119448U, 0x2247e964U,
+ 0xc4a8fc8cU, 0x1aa0f03fU, 0xd8567d2cU, 0xef223390U,
+ 0xc787494eU, 0xc1d938d1U, 0xfe8ccaa2U, 0x3698d40bU,
+ 0xcfa6f581U, 0x28a57adeU, 0x26dab78eU, 0xa43fadbfU,
+ 0xe42c3a9dU, 0x0d507892U, 0x9b6a5fccU, 0x62547e46U,
+ 0xc2f68d13U, 0xe890d8b8U, 0x5e2e39f7U, 0xf582c3afU,
+ 0xbe9f5d80U, 0x7c69d093U, 0xa96fd52dU, 0xb3cf2512U,
+ 0x3bc8ac99U, 0xa710187dU, 0x6ee89c63U, 0x7bdb3bbbU,
+ 0x09cd2678U, 0xf46e5918U, 0x01ec9ab7U, 0xa8834f9aU,
+ 0x65e6956eU, 0x7eaaffe6U, 0x0821bccfU, 0xe6ef15e8U,
+ 0xd9bae79bU, 0xce4a6f36U, 0xd4ea9f09U, 0xd629b07cU,
+ 0xaf31a4b2U, 0x312a3f23U, 0x30c6a594U, 0xc035a266U,
+ 0x37744ebcU, 0xa6fc82caU, 0xb0e090d0U, 0x1533a7d8U,
+ 0x4af10498U, 0xf741ecdaU, 0x0e7fcd50U, 0x2f1791f6U,
+ 0x8d764dd6U, 0x4d43efb0U, 0x54ccaa4dU, 0xdfe49604U,
+ 0xe39ed1b5U, 0x1b4c6a88U, 0xb8c12c1fU, 0x7f466551U,
+ 0x049d5eeaU, 0x5d018c35U, 0x73fa8774U, 0x2efb0b41U,
+ 0x5ab3671dU, 0x5292dbd2U, 0x33e91056U, 0x136dd647U,
+ 0x8c9ad761U, 0x7a37a10cU, 0x8e59f814U, 0x89eb133cU,
+ 0xeecea927U, 0x35b761c9U, 0xede11ce5U, 0x3c7a47b1U,
+ 0x599cd2dfU, 0x3f55f273U, 0x791814ceU, 0xbf73c737U,
+ 0xea53f7cdU, 0x5b5ffdaaU, 0x14df3d6fU, 0x867844dbU,
+ 0x81caaff3U, 0x3eb968c4U, 0x2c382434U, 0x5fc2a340U,
+ 0x72161dc3U, 0x0cbce225U, 0x8b283c49U, 0x41ff0d95U,
+ 0x7139a801U, 0xde080cb3U, 0x9cd8b4e4U, 0x906456c1U,
+ 0x617bcb84U, 0x70d532b6U, 0x74486c5cU, 0x42d0b857U,
+};
+static const u32 Td2[256] = {
+ 0xa75051f4U, 0x65537e41U, 0xa4c31a17U, 0x5e963a27U,
+ 0x6bcb3babU, 0x45f11f9dU, 0x58abacfaU, 0x03934be3U,
+ 0xfa552030U, 0x6df6ad76U, 0x769188ccU, 0x4c25f502U,
+ 0xd7fc4fe5U, 0xcbd7c52aU, 0x44802635U, 0xa38fb562U,
+ 0x5a49deb1U, 0x1b6725baU, 0x0e9845eaU, 0xc0e15dfeU,
+ 0x7502c32fU, 0xf012814cU, 0x97a38d46U, 0xf9c66bd3U,
+ 0x5fe7038fU, 0x9c951592U, 0x7aebbf6dU, 0x59da9552U,
+ 0x832dd4beU, 0x21d35874U, 0x692949e0U, 0xc8448ec9U,
+ 0x896a75c2U, 0x7978f48eU, 0x3e6b9958U, 0x71dd27b9U,
+ 0x4fb6bee1U, 0xad17f088U, 0xac66c920U, 0x3ab47dceU,
+ 0x4a1863dfU, 0x3182e51aU, 0x33609751U, 0x7f456253U,
+ 0x77e0b164U, 0xae84bb6bU, 0xa01cfe81U, 0x2b94f908U,
+ 0x68587048U, 0xfd198f45U, 0x6c8794deU, 0xf8b7527bU,
+ 0xd323ab73U, 0x02e2724bU, 0x8f57e31fU, 0xab2a6655U,
+ 0x2807b2ebU, 0xc2032fb5U, 0x7b9a86c5U, 0x08a5d337U,
+ 0x87f23028U, 0xa5b223bfU, 0x6aba0203U, 0x825ced16U,
+ 0x1c2b8acfU, 0xb492a779U, 0xf2f0f307U, 0xe2a14e69U,
+ 0xf4cd65daU, 0xbed50605U, 0x621fd134U, 0xfe8ac4a6U,
+ 0x539d342eU, 0x55a0a2f3U, 0xe132058aU, 0xeb75a4f6U,
+ 0xec390b83U, 0xefaa4060U, 0x9f065e71U, 0x1051bd6eU,
+
+ 0x8af93e21U, 0x063d96ddU, 0x05aedd3eU, 0xbd464de6U,
+ 0x8db59154U, 0x5d0571c4U, 0xd46f0406U, 0x15ff6050U,
+ 0xfb241998U, 0xe997d6bdU, 0x43cc8940U, 0x9e7767d9U,
+ 0x42bdb0e8U, 0x8b880789U, 0x5b38e719U, 0xeedb79c8U,
+ 0x0a47a17cU, 0x0fe97c42U, 0x1ec9f884U, 0x00000000U,
+ 0x86830980U, 0xed48322bU, 0x70ac1e11U, 0x724e6c5aU,
+ 0xfffbfd0eU, 0x38560f85U, 0xd51e3daeU, 0x3927362dU,
+ 0xd9640a0fU, 0xa621685cU, 0x54d19b5bU, 0x2e3a2436U,
+ 0x67b10c0aU, 0xe70f9357U, 0x96d2b4eeU, 0x919e1b9bU,
+ 0xc54f80c0U, 0x20a261dcU, 0x4b695a77U, 0x1a161c12U,
+ 0xba0ae293U, 0x2ae5c0a0U, 0xe0433c22U, 0x171d121bU,
+ 0x0d0b0e09U, 0xc7adf28bU, 0xa8b92db6U, 0xa9c8141eU,
+ 0x198557f1U, 0x074caf75U, 0xddbbee99U, 0x60fda37fU,
+ 0x269ff701U, 0xf5bc5c72U, 0x3bc54466U, 0x7e345bfbU,
+ 0x29768b43U, 0xc6dccb23U, 0xfc68b6edU, 0xf163b8e4U,
+ 0xdccad731U, 0x85104263U, 0x22401397U, 0x112084c6U,
+ 0x247d854aU, 0x3df8d2bbU, 0x3211aef9U, 0xa16dc729U,
+ 0x2f4b1d9eU, 0x30f3dcb2U, 0x52ec0d86U, 0xe3d077c1U,
+ 0x166c2bb3U, 0xb999a970U, 0x48fa1194U, 0x642247e9U,
+ 0x8cc4a8fcU, 0x3f1aa0f0U, 0x2cd8567dU, 0x90ef2233U,
+ 0x4ec78749U, 0xd1c1d938U, 0xa2fe8ccaU, 0x0b3698d4U,
+ 0x81cfa6f5U, 0xde28a57aU, 0x8e26dab7U, 0xbfa43fadU,
+ 0x9de42c3aU, 0x920d5078U, 0xcc9b6a5fU, 0x4662547eU,
+ 0x13c2f68dU, 0xb8e890d8U, 0xf75e2e39U, 0xaff582c3U,
+ 0x80be9f5dU, 0x937c69d0U, 0x2da96fd5U, 0x12b3cf25U,
+ 0x993bc8acU, 0x7da71018U, 0x636ee89cU, 0xbb7bdb3bU,
+ 0x7809cd26U, 0x18f46e59U, 0xb701ec9aU, 0x9aa8834fU,
+ 0x6e65e695U, 0xe67eaaffU, 0xcf0821bcU, 0xe8e6ef15U,
+ 0x9bd9bae7U, 0x36ce4a6fU, 0x09d4ea9fU, 0x7cd629b0U,
+ 0xb2af31a4U, 0x23312a3fU, 0x9430c6a5U, 0x66c035a2U,
+ 0xbc37744eU, 0xcaa6fc82U, 0xd0b0e090U, 0xd81533a7U,
+ 0x984af104U, 0xdaf741ecU, 0x500e7fcdU, 0xf62f1791U,
+ 0xd68d764dU, 0xb04d43efU, 0x4d54ccaaU, 0x04dfe496U,
+ 0xb5e39ed1U, 0x881b4c6aU, 0x1fb8c12cU, 0x517f4665U,
+ 0xea049d5eU, 0x355d018cU, 0x7473fa87U, 0x412efb0bU,
+ 0x1d5ab367U, 0xd25292dbU, 0x5633e910U, 0x47136dd6U,
+ 0x618c9ad7U, 0x0c7a37a1U, 0x148e59f8U, 0x3c89eb13U,
+ 0x27eecea9U, 0xc935b761U, 0xe5ede11cU, 0xb13c7a47U,
+ 0xdf599cd2U, 0x733f55f2U, 0xce791814U, 0x37bf73c7U,
+ 0xcdea53f7U, 0xaa5b5ffdU, 0x6f14df3dU, 0xdb867844U,
+ 0xf381caafU, 0xc43eb968U, 0x342c3824U, 0x405fc2a3U,
+ 0xc372161dU, 0x250cbce2U, 0x498b283cU, 0x9541ff0dU,
+ 0x017139a8U, 0xb3de080cU, 0xe49cd8b4U, 0xc1906456U,
+ 0x84617bcbU, 0xb670d532U, 0x5c74486cU, 0x5742d0b8U,
+};
+static const u32 Td3[256] = {
+ 0xf4a75051U, 0x4165537eU, 0x17a4c31aU, 0x275e963aU,
+ 0xab6bcb3bU, 0x9d45f11fU, 0xfa58abacU, 0xe303934bU,
+ 0x30fa5520U, 0x766df6adU, 0xcc769188U, 0x024c25f5U,
+ 0xe5d7fc4fU, 0x2acbd7c5U, 0x35448026U, 0x62a38fb5U,
+ 0xb15a49deU, 0xba1b6725U, 0xea0e9845U, 0xfec0e15dU,
+ 0x2f7502c3U, 0x4cf01281U, 0x4697a38dU, 0xd3f9c66bU,
+ 0x8f5fe703U, 0x929c9515U, 0x6d7aebbfU, 0x5259da95U,
+ 0xbe832dd4U, 0x7421d358U, 0xe0692949U, 0xc9c8448eU,
+ 0xc2896a75U, 0x8e7978f4U, 0x583e6b99U, 0xb971dd27U,
+ 0xe14fb6beU, 0x88ad17f0U, 0x20ac66c9U, 0xce3ab47dU,
+ 0xdf4a1863U, 0x1a3182e5U, 0x51336097U, 0x537f4562U,
+ 0x6477e0b1U, 0x6bae84bbU, 0x81a01cfeU, 0x082b94f9U,
+ 0x48685870U, 0x45fd198fU, 0xde6c8794U, 0x7bf8b752U,
+ 0x73d323abU, 0x4b02e272U, 0x1f8f57e3U, 0x55ab2a66U,
+ 0xeb2807b2U, 0xb5c2032fU, 0xc57b9a86U, 0x3708a5d3U,
+ 0x2887f230U, 0xbfa5b223U, 0x036aba02U, 0x16825cedU,
+ 0xcf1c2b8aU, 0x79b492a7U, 0x07f2f0f3U, 0x69e2a14eU,
+ 0xdaf4cd65U, 0x05bed506U, 0x34621fd1U, 0xa6fe8ac4U,
+ 0x2e539d34U, 0xf355a0a2U, 0x8ae13205U, 0xf6eb75a4U,
+ 0x83ec390bU, 0x60efaa40U, 0x719f065eU, 0x6e1051bdU,
+ 0x218af93eU, 0xdd063d96U, 0x3e05aeddU, 0xe6bd464dU,
+ 0x548db591U, 0xc45d0571U, 0x06d46f04U, 0x5015ff60U,
+ 0x98fb2419U, 0xbde997d6U, 0x4043cc89U, 0xd99e7767U,
+ 0xe842bdb0U, 0x898b8807U, 0x195b38e7U, 0xc8eedb79U,
+ 0x7c0a47a1U, 0x420fe97cU, 0x841ec9f8U, 0x00000000U,
+ 0x80868309U, 0x2bed4832U, 0x1170ac1eU, 0x5a724e6cU,
+ 0x0efffbfdU, 0x8538560fU, 0xaed51e3dU, 0x2d392736U,
+ 0x0fd9640aU, 0x5ca62168U, 0x5b54d19bU, 0x362e3a24U,
+ 0x0a67b10cU, 0x57e70f93U, 0xee96d2b4U, 0x9b919e1bU,
+ 0xc0c54f80U, 0xdc20a261U, 0x774b695aU, 0x121a161cU,
+ 0x93ba0ae2U, 0xa02ae5c0U, 0x22e0433cU, 0x1b171d12U,
+ 0x090d0b0eU, 0x8bc7adf2U, 0xb6a8b92dU, 0x1ea9c814U,
+ 0xf1198557U, 0x75074cafU, 0x99ddbbeeU, 0x7f60fda3U,
+ 0x01269ff7U, 0x72f5bc5cU, 0x663bc544U, 0xfb7e345bU,
+ 0x4329768bU, 0x23c6dccbU, 0xedfc68b6U, 0xe4f163b8U,
+ 0x31dccad7U, 0x63851042U, 0x97224013U, 0xc6112084U,
+ 0x4a247d85U, 0xbb3df8d2U, 0xf93211aeU, 0x29a16dc7U,
+ 0x9e2f4b1dU, 0xb230f3dcU, 0x8652ec0dU, 0xc1e3d077U,
+ 0xb3166c2bU, 0x70b999a9U, 0x9448fa11U, 0xe9642247U,
+ 0xfc8cc4a8U, 0xf03f1aa0U, 0x7d2cd856U, 0x3390ef22U,
+ 0x494ec787U, 0x38d1c1d9U, 0xcaa2fe8cU, 0xd40b3698U,
+ 0xf581cfa6U, 0x7ade28a5U, 0xb78e26daU, 0xadbfa43fU,
+ 0x3a9de42cU, 0x78920d50U, 0x5fcc9b6aU, 0x7e466254U,
+ 0x8d13c2f6U, 0xd8b8e890U, 0x39f75e2eU, 0xc3aff582U,
+ 0x5d80be9fU, 0xd0937c69U, 0xd52da96fU, 0x2512b3cfU,
+ 0xac993bc8U, 0x187da710U, 0x9c636ee8U, 0x3bbb7bdbU,
+ 0x267809cdU, 0x5918f46eU, 0x9ab701ecU, 0x4f9aa883U,
+ 0x956e65e6U, 0xffe67eaaU, 0xbccf0821U, 0x15e8e6efU,
+ 0xe79bd9baU, 0x6f36ce4aU, 0x9f09d4eaU, 0xb07cd629U,
+ 0xa4b2af31U, 0x3f23312aU, 0xa59430c6U, 0xa266c035U,
+ 0x4ebc3774U, 0x82caa6fcU, 0x90d0b0e0U, 0xa7d81533U,
+ 0x04984af1U, 0xecdaf741U, 0xcd500e7fU, 0x91f62f17U,
+ 0x4dd68d76U, 0xefb04d43U, 0xaa4d54ccU, 0x9604dfe4U,
+ 0xd1b5e39eU, 0x6a881b4cU, 0x2c1fb8c1U, 0x65517f46U,
+ 0x5eea049dU, 0x8c355d01U, 0x877473faU, 0x0b412efbU,
+ 0x671d5ab3U, 0xdbd25292U, 0x105633e9U, 0xd647136dU,
+ 0xd7618c9aU, 0xa10c7a37U, 0xf8148e59U, 0x133c89ebU,
+ 0xa927eeceU, 0x61c935b7U, 0x1ce5ede1U, 0x47b13c7aU,
+ 0xd2df599cU, 0xf2733f55U, 0x14ce7918U, 0xc737bf73U,
+ 0xf7cdea53U, 0xfdaa5b5fU, 0x3d6f14dfU, 0x44db8678U,
+ 0xaff381caU, 0x68c43eb9U, 0x24342c38U, 0xa3405fc2U,
+ 0x1dc37216U, 0xe2250cbcU, 0x3c498b28U, 0x0d9541ffU,
+ 0xa8017139U, 0x0cb3de08U, 0xb4e49cd8U, 0x56c19064U,
+ 0xcb84617bU, 0x32b670d5U, 0x6c5c7448U, 0xb85742d0U,
+};
+static const u32 Td4[256] = {
+ 0x52525252U, 0x09090909U, 0x6a6a6a6aU, 0xd5d5d5d5U,
+ 0x30303030U, 0x36363636U, 0xa5a5a5a5U, 0x38383838U,
+ 0xbfbfbfbfU, 0x40404040U, 0xa3a3a3a3U, 0x9e9e9e9eU,
+ 0x81818181U, 0xf3f3f3f3U, 0xd7d7d7d7U, 0xfbfbfbfbU,
+ 0x7c7c7c7cU, 0xe3e3e3e3U, 0x39393939U, 0x82828282U,
+ 0x9b9b9b9bU, 0x2f2f2f2fU, 0xffffffffU, 0x87878787U,
+ 0x34343434U, 0x8e8e8e8eU, 0x43434343U, 0x44444444U,
+ 0xc4c4c4c4U, 0xdedededeU, 0xe9e9e9e9U, 0xcbcbcbcbU,
+ 0x54545454U, 0x7b7b7b7bU, 0x94949494U, 0x32323232U,
+ 0xa6a6a6a6U, 0xc2c2c2c2U, 0x23232323U, 0x3d3d3d3dU,
+ 0xeeeeeeeeU, 0x4c4c4c4cU, 0x95959595U, 0x0b0b0b0bU,
+ 0x42424242U, 0xfafafafaU, 0xc3c3c3c3U, 0x4e4e4e4eU,
+ 0x08080808U, 0x2e2e2e2eU, 0xa1a1a1a1U, 0x66666666U,
+ 0x28282828U, 0xd9d9d9d9U, 0x24242424U, 0xb2b2b2b2U,
+ 0x76767676U, 0x5b5b5b5bU, 0xa2a2a2a2U, 0x49494949U,
+ 0x6d6d6d6dU, 0x8b8b8b8bU, 0xd1d1d1d1U, 0x25252525U,
+ 0x72727272U, 0xf8f8f8f8U, 0xf6f6f6f6U, 0x64646464U,
+ 0x86868686U, 0x68686868U, 0x98989898U, 0x16161616U,
+ 0xd4d4d4d4U, 0xa4a4a4a4U, 0x5c5c5c5cU, 0xccccccccU,
+ 0x5d5d5d5dU, 0x65656565U, 0xb6b6b6b6U, 0x92929292U,
+ 0x6c6c6c6cU, 0x70707070U, 0x48484848U, 0x50505050U,
+ 0xfdfdfdfdU, 0xededededU, 0xb9b9b9b9U, 0xdadadadaU,
+ 0x5e5e5e5eU, 0x15151515U, 0x46464646U, 0x57575757U,
+ 0xa7a7a7a7U, 0x8d8d8d8dU, 0x9d9d9d9dU, 0x84848484U,
+ 0x90909090U, 0xd8d8d8d8U, 0xababababU, 0x00000000U,
+ 0x8c8c8c8cU, 0xbcbcbcbcU, 0xd3d3d3d3U, 0x0a0a0a0aU,
+ 0xf7f7f7f7U, 0xe4e4e4e4U, 0x58585858U, 0x05050505U,
+ 0xb8b8b8b8U, 0xb3b3b3b3U, 0x45454545U, 0x06060606U,
+ 0xd0d0d0d0U, 0x2c2c2c2cU, 0x1e1e1e1eU, 0x8f8f8f8fU,
+ 0xcacacacaU, 0x3f3f3f3fU, 0x0f0f0f0fU, 0x02020202U,
+ 0xc1c1c1c1U, 0xafafafafU, 0xbdbdbdbdU, 0x03030303U,
+ 0x01010101U, 0x13131313U, 0x8a8a8a8aU, 0x6b6b6b6bU,
+ 0x3a3a3a3aU, 0x91919191U, 0x11111111U, 0x41414141U,
+ 0x4f4f4f4fU, 0x67676767U, 0xdcdcdcdcU, 0xeaeaeaeaU,
+ 0x97979797U, 0xf2f2f2f2U, 0xcfcfcfcfU, 0xcecececeU,
+ 0xf0f0f0f0U, 0xb4b4b4b4U, 0xe6e6e6e6U, 0x73737373U,
+ 0x96969696U, 0xacacacacU, 0x74747474U, 0x22222222U,
+ 0xe7e7e7e7U, 0xadadadadU, 0x35353535U, 0x85858585U,
+ 0xe2e2e2e2U, 0xf9f9f9f9U, 0x37373737U, 0xe8e8e8e8U,
+ 0x1c1c1c1cU, 0x75757575U, 0xdfdfdfdfU, 0x6e6e6e6eU,
+ 0x47474747U, 0xf1f1f1f1U, 0x1a1a1a1aU, 0x71717171U,
+ 0x1d1d1d1dU, 0x29292929U, 0xc5c5c5c5U, 0x89898989U,
+ 0x6f6f6f6fU, 0xb7b7b7b7U, 0x62626262U, 0x0e0e0e0eU,
+ 0xaaaaaaaaU, 0x18181818U, 0xbebebebeU, 0x1b1b1b1bU,
+ 0xfcfcfcfcU, 0x56565656U, 0x3e3e3e3eU, 0x4b4b4b4bU,
+ 0xc6c6c6c6U, 0xd2d2d2d2U, 0x79797979U, 0x20202020U,
+ 0x9a9a9a9aU, 0xdbdbdbdbU, 0xc0c0c0c0U, 0xfefefefeU,
+ 0x78787878U, 0xcdcdcdcdU, 0x5a5a5a5aU, 0xf4f4f4f4U,
+ 0x1f1f1f1fU, 0xddddddddU, 0xa8a8a8a8U, 0x33333333U,
+ 0x88888888U, 0x07070707U, 0xc7c7c7c7U, 0x31313131U,
+ 0xb1b1b1b1U, 0x12121212U, 0x10101010U, 0x59595959U,
+ 0x27272727U, 0x80808080U, 0xececececU, 0x5f5f5f5fU,
+ 0x60606060U, 0x51515151U, 0x7f7f7f7fU, 0xa9a9a9a9U,
+ 0x19191919U, 0xb5b5b5b5U, 0x4a4a4a4aU, 0x0d0d0d0dU,
+ 0x2d2d2d2dU, 0xe5e5e5e5U, 0x7a7a7a7aU, 0x9f9f9f9fU,
+ 0x93939393U, 0xc9c9c9c9U, 0x9c9c9c9cU, 0xefefefefU,
+ 0xa0a0a0a0U, 0xe0e0e0e0U, 0x3b3b3b3bU, 0x4d4d4d4dU,
+ 0xaeaeaeaeU, 0x2a2a2a2aU, 0xf5f5f5f5U, 0xb0b0b0b0U,
+ 0xc8c8c8c8U, 0xebebebebU, 0xbbbbbbbbU, 0x3c3c3c3cU,
+ 0x83838383U, 0x53535353U, 0x99999999U, 0x61616161U,
+ 0x17171717U, 0x2b2b2b2bU, 0x04040404U, 0x7e7e7e7eU,
+ 0xbabababaU, 0x77777777U, 0xd6d6d6d6U, 0x26262626U,
+ 0xe1e1e1e1U, 0x69696969U, 0x14141414U, 0x63636363U,
+ 0x55555555U, 0x21212121U, 0x0c0c0c0cU, 0x7d7d7d7dU,
+};
+static const u32 rcon[] = {
+ 0x01000000, 0x02000000, 0x04000000, 0x08000000,
+ 0x10000000, 0x20000000, 0x40000000, 0x80000000,
+ 0x1B000000, 0x36000000, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */
+};
+
+#define SWAP(x) (_lrotl(x, 8) & 0x00ff00ff | _lrotr(x, 8) & 0xff00ff00)
+
+#ifdef _MSC_VER
+#define GETU32(p) SWAP(*((u32 *)(p)))
+#define PUTU32(ct, st) { *((u32 *)(ct)) = SWAP((st)); }
+#else
+#define GETU32(pt) (((u32)(pt)[0] << 24) ^ ((u32)(pt)[1] << 16) ^ ((u32)(pt)[2] << 8) ^ ((u32)(pt)[3]))
+#define PUTU32(ct, st) { (ct)[0] = (u8)((st) >> 24); (ct)[1] = (u8)((st) >> 16); (ct)[2] = (u8)((st) >> 8); (ct)[3] = (u8)(st); }
+#endif
+
+/**
+ * Expand the cipher key into the encryption key schedule.
+ *
+ * @return the number of rounds for the given cipher key size.
+ */
+int rijndaelKeySetupEnc(u32 rk[/*4*(Nr + 1)*/], const u8 cipherKey[], int keyBits) {
+ int i = 0;
+ u32 temp;
+
+ rk[0] = GETU32(cipherKey );
+ rk[1] = GETU32(cipherKey + 4);
+ rk[2] = GETU32(cipherKey + 8);
+ rk[3] = GETU32(cipherKey + 12);
+ if (keyBits == 128) {
+ for (;;) {
+ temp = rk[3];
+ rk[4] = rk[0] ^
+ (Te4[(temp >> 16) & 0xff] & 0xff000000) ^
+ (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^
+ (Te4[(temp ) & 0xff] & 0x0000ff00) ^
+ (Te4[(temp >> 24) ] & 0x000000ff) ^
+ rcon[i];
+ rk[5] = rk[1] ^ rk[4];
+ rk[6] = rk[2] ^ rk[5];
+ rk[7] = rk[3] ^ rk[6];
+ if (++i == 10) {
+ return 10;
+ }
+ rk += 4;
+ }
+ }
+ rk[4] = GETU32(cipherKey + 16);
+ rk[5] = GETU32(cipherKey + 20);
+ if (keyBits == 192) {
+ for (;;) {
+ temp = rk[ 5];
+ rk[ 6] = rk[ 0] ^
+ (Te4[(temp >> 16) & 0xff] & 0xff000000) ^
+ (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^
+ (Te4[(temp ) & 0xff] & 0x0000ff00) ^
+ (Te4[(temp >> 24) ] & 0x000000ff) ^
+ rcon[i];
+ rk[ 7] = rk[ 1] ^ rk[ 6];
+ rk[ 8] = rk[ 2] ^ rk[ 7];
+ rk[ 9] = rk[ 3] ^ rk[ 8];
+ if (++i == 8) {
+ return 12;
+ }
+ rk[10] = rk[ 4] ^ rk[ 9];
+ rk[11] = rk[ 5] ^ rk[10];
+ rk += 6;
+ }
+ }
+ rk[6] = GETU32(cipherKey + 24);
+ rk[7] = GETU32(cipherKey + 28);
+ if (keyBits == 256) {
+ for (;;) {
+ temp = rk[ 7];
+ rk[ 8] = rk[ 0] ^
+ (Te4[(temp >> 16) & 0xff] & 0xff000000) ^
+ (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^
+ (Te4[(temp ) & 0xff] & 0x0000ff00) ^
+ (Te4[(temp >> 24) ] & 0x000000ff) ^
+ rcon[i];
+ rk[ 9] = rk[ 1] ^ rk[ 8];
+ rk[10] = rk[ 2] ^ rk[ 9];
+ rk[11] = rk[ 3] ^ rk[10];
+ if (++i == 7) {
+ return 14;
+ }
+ temp = rk[11];
+ rk[12] = rk[ 4] ^
+ (Te4[(temp >> 24) ] & 0xff000000) ^
+ (Te4[(temp >> 16) & 0xff] & 0x00ff0000) ^
+ (Te4[(temp >> 8) & 0xff] & 0x0000ff00) ^
+ (Te4[(temp ) & 0xff] & 0x000000ff);
+ rk[13] = rk[ 5] ^ rk[12];
+ rk[14] = rk[ 6] ^ rk[13];
+ rk[15] = rk[ 7] ^ rk[14];
+
+ rk += 8;
+ }
+ }
+ return 0;
+}
+
+/**
+ * Expand the cipher key into the decryption key schedule.
+ *
+ * @return the number of rounds for the given cipher key size.
+ */
+int rijndaelKeySetupDec(u32 rk[/*4*(Nr + 1)*/], const u8 cipherKey[], int keyBits) {
+ int Nr, i, j;
+ u32 temp;
+
+ /* expand the cipher key: */
+ Nr = rijndaelKeySetupEnc(rk, cipherKey, keyBits);
+ /* invert the order of the round keys: */
+ for (i = 0, j = 4*Nr; i < j; i += 4, j -= 4) {
+ temp = rk[i ]; rk[i ] = rk[j ]; rk[j ] = temp;
+ temp = rk[i + 1]; rk[i + 1] = rk[j + 1]; rk[j + 1] = temp;
+ temp = rk[i + 2]; rk[i + 2] = rk[j + 2]; rk[j + 2] = temp;
+ temp = rk[i + 3]; rk[i + 3] = rk[j + 3]; rk[j + 3] = temp;
+ }
+ /* apply the inverse MixColumn transform to all round keys but the first and the last: */
+ for (i = 1; i < Nr; i++) {
+ rk += 4;
+ rk[0] =
+ Td0[Te4[(rk[0] >> 24) ] & 0xff] ^
+ Td1[Te4[(rk[0] >> 16) & 0xff] & 0xff] ^
+ Td2[Te4[(rk[0] >> 8) & 0xff] & 0xff] ^
+ Td3[Te4[(rk[0] ) & 0xff] & 0xff];
+ rk[1] =
+ Td0[Te4[(rk[1] >> 24) ] & 0xff] ^
+ Td1[Te4[(rk[1] >> 16) & 0xff] & 0xff] ^
+ Td2[Te4[(rk[1] >> 8) & 0xff] & 0xff] ^
+ Td3[Te4[(rk[1] ) & 0xff] & 0xff];
+ rk[2] =
+ Td0[Te4[(rk[2] >> 24) ] & 0xff] ^
+ Td1[Te4[(rk[2] >> 16) & 0xff] & 0xff] ^
+ Td2[Te4[(rk[2] >> 8) & 0xff] & 0xff] ^
+ Td3[Te4[(rk[2] ) & 0xff] & 0xff];
+ rk[3] =
+ Td0[Te4[(rk[3] >> 24) ] & 0xff] ^
+ Td1[Te4[(rk[3] >> 16) & 0xff] & 0xff] ^
+ Td2[Te4[(rk[3] >> 8) & 0xff] & 0xff] ^
+ Td3[Te4[(rk[3] ) & 0xff] & 0xff];
+ }
+ return Nr;
+}
+
+void rijndaelEncrypt(const u32 rk[/*4*(Nr + 1)*/], int Nr, const u8 pt[16], u8 ct[16]) {
+ u32 s0, s1, s2, s3, t0, t1, t2, t3;
+#ifndef FULL_UNROLL
+ int r;
+#endif /* ?FULL_UNROLL */
+
+ /*
+ * map byte array block to cipher state
+ * and add initial round key:
+ */
+ s0 = GETU32(pt ) ^ rk[0];
+ s1 = GETU32(pt + 4) ^ rk[1];
+ s2 = GETU32(pt + 8) ^ rk[2];
+ s3 = GETU32(pt + 12) ^ rk[3];
+#ifdef FULL_UNROLL
+ /* round 1: */
+ t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[ 4];
+ t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[ 5];
+ t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[ 6];
+ t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[ 7];
+ /* round 2: */
+ s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[ 8];
+ s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[ 9];
+ s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[10];
+ s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[11];
+ /* round 3: */
+ t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[12];
+ t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[13];
+ t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[14];
+ t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[15];
+ /* round 4: */
+ s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[16];
+ s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[17];
+ s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[18];
+ s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[19];
+ /* round 5: */
+ t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[20];
+ t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[21];
+ t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[22];
+ t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[23];
+ /* round 6: */
+ s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[24];
+ s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[25];
+ s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[26];
+ s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[27];
+ /* round 7: */
+ t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[28];
+ t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[29];
+ t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[30];
+ t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[31];
+ /* round 8: */
+ s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[32];
+ s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[33];
+ s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[34];
+ s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[35];
+ /* round 9: */
+ t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[36];
+ t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[37];
+ t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[38];
+ t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[39];
+ if (Nr > 10) {
+ /* round 10: */
+ s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[40];
+ s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[41];
+ s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[42];
+ s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[43];
+ /* round 11: */
+ t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[44];
+ t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[45];
+ t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[46];
+ t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[47];
+ if (Nr > 12) {
+ /* round 12: */
+ s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[48];
+ s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[49];
+ s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[50];
+ s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[51];
+ /* round 13: */
+ t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[52];
+ t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[53];
+ t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[54];
+ t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[55];
+ }
+ }
+ rk += Nr << 2;
+#else /* !FULL_UNROLL */
+ /*
+ * Nr - 1 full rounds:
+ */
+ r = Nr >> 1;
+ for (;;) {
+ t0 =
+ Te0[(s0 >> 24) ] ^
+ Te1[(s1 >> 16) & 0xff] ^
+ Te2[(s2 >> 8) & 0xff] ^
+ Te3[(s3 ) & 0xff] ^
+ rk[4];
+ t1 =
+ Te0[(s1 >> 24) ] ^
+ Te1[(s2 >> 16) & 0xff] ^
+ Te2[(s3 >> 8) & 0xff] ^
+ Te3[(s0 ) & 0xff] ^
+ rk[5];
+ t2 =
+ Te0[(s2 >> 24) ] ^
+ Te1[(s3 >> 16) & 0xff] ^
+ Te2[(s0 >> 8) & 0xff] ^
+ Te3[(s1 ) & 0xff] ^
+ rk[6];
+ t3 =
+ Te0[(s3 >> 24) ] ^
+ Te1[(s0 >> 16) & 0xff] ^
+ Te2[(s1 >> 8) & 0xff] ^
+ Te3[(s2 ) & 0xff] ^
+ rk[7];
+
+ rk += 8;
+ if (--r == 0) {
+ break;
+ }
+
+ s0 =
+ Te0[(t0 >> 24) ] ^
+ Te1[(t1 >> 16) & 0xff] ^
+ Te2[(t2 >> 8) & 0xff] ^
+ Te3[(t3 ) & 0xff] ^
+ rk[0];
+ s1 =
+ Te0[(t1 >> 24) ] ^
+ Te1[(t2 >> 16) & 0xff] ^
+ Te2[(t3 >> 8) & 0xff] ^
+ Te3[(t0 ) & 0xff] ^
+ rk[1];
+ s2 =
+ Te0[(t2 >> 24) ] ^
+ Te1[(t3 >> 16) & 0xff] ^
+ Te2[(t0 >> 8) & 0xff] ^
+ Te3[(t1 ) & 0xff] ^
+ rk[2];
+ s3 =
+ Te0[(t3 >> 24) ] ^
+ Te1[(t0 >> 16) & 0xff] ^
+ Te2[(t1 >> 8) & 0xff] ^
+ Te3[(t2 ) & 0xff] ^
+ rk[3];
+ }
+#endif /* ?FULL_UNROLL */
+ /*
+ * apply last round and
+ * map cipher state to byte array block:
+ */
+ s0 =
+ (Te4[(t0 >> 24) ] & 0xff000000) ^
+ (Te4[(t1 >> 16) & 0xff] & 0x00ff0000) ^
+ (Te4[(t2 >> 8) & 0xff] & 0x0000ff00) ^
+ (Te4[(t3 ) & 0xff] & 0x000000ff) ^
+ rk[0];
+ PUTU32(ct , s0);
+ s1 =
+ (Te4[(t1 >> 24) ] & 0xff000000) ^
+ (Te4[(t2 >> 16) & 0xff] & 0x00ff0000) ^
+ (Te4[(t3 >> 8) & 0xff] & 0x0000ff00) ^
+ (Te4[(t0 ) & 0xff] & 0x000000ff) ^
+ rk[1];
+ PUTU32(ct + 4, s1);
+ s2 =
+ (Te4[(t2 >> 24) ] & 0xff000000) ^
+ (Te4[(t3 >> 16) & 0xff] & 0x00ff0000) ^
+ (Te4[(t0 >> 8) & 0xff] & 0x0000ff00) ^
+ (Te4[(t1 ) & 0xff] & 0x000000ff) ^
+ rk[2];
+ PUTU32(ct + 8, s2);
+ s3 =
+ (Te4[(t3 >> 24) ] & 0xff000000) ^
+ (Te4[(t0 >> 16) & 0xff] & 0x00ff0000) ^
+ (Te4[(t1 >> 8) & 0xff] & 0x0000ff00) ^
+ (Te4[(t2 ) & 0xff] & 0x000000ff) ^
+ rk[3];
+ PUTU32(ct + 12, s3);
+}
+
+void rijndaelDecrypt(const u32 rk[/*4*(Nr + 1)*/], int Nr, const u8 ct[16], u8 pt[16]) {
+ u32 s0, s1, s2, s3, t0, t1, t2, t3;
+#ifndef FULL_UNROLL
+ int r;
+#endif /* ?FULL_UNROLL */
+
+ /*
+ * map byte array block to cipher state
+ * and add initial round key:
+ */
+ s0 = GETU32(ct ) ^ rk[0];
+ s1 = GETU32(ct + 4) ^ rk[1];
+ s2 = GETU32(ct + 8) ^ rk[2];
+ s3 = GETU32(ct + 12) ^ rk[3];
+#ifdef FULL_UNROLL
+ /* round 1: */
+ t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[ 4];
+ t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[ 5];
+ t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[ 6];
+ t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[ 7];
+ /* round 2: */
+ s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[ 8];
+ s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[ 9];
+ s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[10];
+ s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[11];
+ /* round 3: */
+ t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[12];
+ t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[13];
+ t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[14];
+ t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[15];
+ /* round 4: */
+ s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[16];
+ s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[17];
+ s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[18];
+ s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[19];
+ /* round 5: */
+ t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[20];
+ t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[21];
+ t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[22];
+ t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[23];
+ /* round 6: */
+ s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[24];
+ s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[25];
+ s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[26];
+ s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[27];
+ /* round 7: */
+ t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[28];
+ t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[29];
+ t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[30];
+ t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[31];
+ /* round 8: */
+ s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[32];
+ s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[33];
+ s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[34];
+ s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[35];
+ /* round 9: */
+ t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[36];
+ t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[37];
+ t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[38];
+ t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[39];
+ if (Nr > 10) {
+ /* round 10: */
+ s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[40];
+ s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[41];
+ s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[42];
+ s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[43];
+ /* round 11: */
+ t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[44];
+ t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[45];
+ t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[46];
+ t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[47];
+ if (Nr > 12) {
+ /* round 12: */
+ s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[48];
+ s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[49];
+ s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[50];
+ s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[51];
+ /* round 13: */
+ t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[52];
+ t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[53];
+ t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[54];
+ t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[55];
+ }
+ }
+ rk += Nr << 2;
+#else /* !FULL_UNROLL */
+ /*
+ * Nr - 1 full rounds:
+ */
+ r = Nr >> 1;
+ for (;;) {
+ t0 =
+ Td0[(s0 >> 24) ] ^
+ Td1[(s3 >> 16) & 0xff] ^
+ Td2[(s2 >> 8) & 0xff] ^
+ Td3[(s1 ) & 0xff] ^
+ rk[4];
+ t1 =
+ Td0[(s1 >> 24) ] ^
+ Td1[(s0 >> 16) & 0xff] ^
+ Td2[(s3 >> 8) & 0xff] ^
+ Td3[(s2 ) & 0xff] ^
+ rk[5];
+ t2 =
+ Td0[(s2 >> 24) ] ^
+ Td1[(s1 >> 16) & 0xff] ^
+ Td2[(s0 >> 8) & 0xff] ^
+ Td3[(s3 ) & 0xff] ^
+ rk[6];
+ t3 =
+ Td0[(s3 >> 24) ] ^
+ Td1[(s2 >> 16) & 0xff] ^
+ Td2[(s1 >> 8) & 0xff] ^
+ Td3[(s0 ) & 0xff] ^
+ rk[7];
+
+ rk += 8;
+ if (--r == 0) {
+ break;
+ }
+
+ s0 =
+ Td0[(t0 >> 24) ] ^
+ Td1[(t3 >> 16) & 0xff] ^
+ Td2[(t2 >> 8) & 0xff] ^
+ Td3[(t1 ) & 0xff] ^
+ rk[0];
+ s1 =
+ Td0[(t1 >> 24) ] ^
+ Td1[(t0 >> 16) & 0xff] ^
+ Td2[(t3 >> 8) & 0xff] ^
+ Td3[(t2 ) & 0xff] ^
+ rk[1];
+ s2 =
+ Td0[(t2 >> 24) ] ^
+ Td1[(t1 >> 16) & 0xff] ^
+ Td2[(t0 >> 8) & 0xff] ^
+ Td3[(t3 ) & 0xff] ^
+ rk[2];
+ s3 =
+ Td0[(t3 >> 24) ] ^
+ Td1[(t2 >> 16) & 0xff] ^
+ Td2[(t1 >> 8) & 0xff] ^
+ Td3[(t0 ) & 0xff] ^
+ rk[3];
+ }
+#endif /* ?FULL_UNROLL */
+ /*
+ * apply last round and
+ * map cipher state to byte array block:
+ */
+ s0 =
+ (Td4[(t0 >> 24) ] & 0xff000000) ^
+ (Td4[(t3 >> 16) & 0xff] & 0x00ff0000) ^
+ (Td4[(t2 >> 8) & 0xff] & 0x0000ff00) ^
+ (Td4[(t1 ) & 0xff] & 0x000000ff) ^
+ rk[0];
+ PUTU32(pt , s0);
+ s1 =
+ (Td4[(t1 >> 24) ] & 0xff000000) ^
+ (Td4[(t0 >> 16) & 0xff] & 0x00ff0000) ^
+ (Td4[(t3 >> 8) & 0xff] & 0x0000ff00) ^
+ (Td4[(t2 ) & 0xff] & 0x000000ff) ^
+ rk[1];
+ PUTU32(pt + 4, s1);
+ s2 =
+ (Td4[(t2 >> 24) ] & 0xff000000) ^
+ (Td4[(t1 >> 16) & 0xff] & 0x00ff0000) ^
+ (Td4[(t0 >> 8) & 0xff] & 0x0000ff00) ^
+ (Td4[(t3 ) & 0xff] & 0x000000ff) ^
+ rk[2];
+ PUTU32(pt + 8, s2);
+ s3 =
+ (Td4[(t3 >> 24) ] & 0xff000000) ^
+ (Td4[(t2 >> 16) & 0xff] & 0x00ff0000) ^
+ (Td4[(t1 >> 8) & 0xff] & 0x0000ff00) ^
+ (Td4[(t0 ) & 0xff] & 0x000000ff) ^
+ rk[3];
+ PUTU32(pt + 12, s3);
+}
diff --git a/source4/heimdal/lib/hcrypto/rijndael-alg-fst.h b/source4/heimdal/lib/hcrypto/rijndael-alg-fst.h
new file mode 100644
index 0000000000..7e2e1935fd
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/rijndael-alg-fst.h
@@ -0,0 +1,46 @@
+/* $NetBSD: rijndael-alg-fst.h,v 1.2 2000/10/02 17:19:15 itojun Exp $ */
+/* $KAME: rijndael-alg-fst.h,v 1.5 2003/07/15 10:47:16 itojun Exp $ */
+/**
+ * rijndael-alg-fst.h
+ *
+ * @version 3.0 (December 2000)
+ *
+ * Optimised ANSI C code for the Rijndael cipher (now AES)
+ *
+ * @author Vincent Rijmen <vincent.rijmen@esat.kuleuven.ac.be>
+ * @author Antoon Bosselaers <antoon.bosselaers@esat.kuleuven.ac.be>
+ * @author Paulo Barreto <paulo.barreto@terra.com.br>
+ *
+ * This code is hereby placed in the public domain.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef __RIJNDAEL_ALG_FST_H
+#define __RIJNDAEL_ALG_FST_H
+
+/* symbol renaming */
+#define rijndaelKeySetupEnc _hc_rijndaelKeySetupEnc
+#define rijndaelKeySetupDec _hc_rijndaelKeySetupDec
+#define rijndaelEncrypt _hc_rijndaelEncrypt
+#define rijndaelDecrypt _hc_rijndaelDecrypt
+
+#define RIJNDAEL_MAXKC (256/32)
+#define RIJNDAEL_MAXKB (256/8)
+#define RIJNDAEL_MAXNR 14
+
+int rijndaelKeySetupEnc(uint32_t rk[/*4*(Nr + 1)*/], const uint8_t cipherKey[], int keyBits);
+int rijndaelKeySetupDec(uint32_t rk[/*4*(Nr + 1)*/], const uint8_t cipherKey[], int keyBits);
+void rijndaelEncrypt(const uint32_t rk[/*4*(Nr + 1)*/], int Nr, const uint8_t pt[16], uint8_t ct[16]);
+void rijndaelDecrypt(const uint32_t rk[/*4*(Nr + 1)*/], int Nr, const uint8_t ct[16], uint8_t pt[16]);
+
+#endif /* __RIJNDAEL_ALG_FST_H */
diff --git a/source4/heimdal/lib/hcrypto/rnd_keys.c b/source4/heimdal/lib/hcrypto/rnd_keys.c
new file mode 100644
index 0000000000..94370513e2
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/rnd_keys.c
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 1995, 1996, 1997, 1999 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+
+RCSID("$Id$");
+#endif
+
+#define HC_DEPRECATED
+
+#ifdef KRB5
+#include <krb5-types.h>
+#endif
+#include <des.h>
+#include <rand.h>
+
+#include <stdlib.h>
+
+#undef __attribute__
+#define __attribute__(X)
+
+void HC_DEPRECATED
+DES_rand_data(void *outdata, int size)
+{
+ RAND_bytes(outdata, size);
+}
+
+void HC_DEPRECATED
+DES_generate_random_block(DES_cblock *block)
+{
+ RAND_bytes(block, sizeof(*block));
+}
+
+#define DES_rand_data_key hc_DES_rand_data_key
+
+void HC_DEPRECATED
+DES_rand_data_key(DES_cblock *key);
+
+/*
+ * Generate a random DES key.
+ */
+
+void HC_DEPRECATED
+DES_rand_data_key(DES_cblock *key)
+{
+ DES_new_random_key(key);
+}
+
+void HC_DEPRECATED
+DES_set_sequence_number(void *ll)
+{
+}
+
+void HC_DEPRECATED
+DES_set_random_generator_seed(DES_cblock *seed)
+{
+ RAND_seed(seed, sizeof(*seed));
+}
+
+/**
+ * Generate a random des key using a random block, fixup parity and
+ * skip weak keys.
+ *
+ * @param key is set to a random key.
+ *
+ * @return 0 on success, non zero on random number generator failure.
+ *
+ * @ingroup hcrypto_des
+ */
+
+int HC_DEPRECATED
+DES_new_random_key(DES_cblock *key)
+{
+ do {
+ if (RAND_bytes(key, sizeof(*key)) != 1)
+ return 1;
+ DES_set_odd_parity(key);
+ } while(DES_is_weak_key(key));
+
+ return(0);
+}
+
+/**
+ * Seed the random number generator. Deprecated, use @ref page_rand
+ *
+ * @param seed a seed to seed that random number generate with.
+ *
+ * @ingroup hcrypto_des
+ */
+
+void HC_DEPRECATED
+DES_init_random_number_generator(DES_cblock *seed)
+{
+ RAND_seed(seed, sizeof(*seed));
+}
+
+/**
+ * Generate a random key, deprecated since it doesn't return an error
+ * code, use DES_new_random_key().
+ *
+ * @param key is set to a random key.
+ *
+ * @ingroup hcrypto_des
+ */
+
+void HC_DEPRECATED
+DES_random_key(DES_cblock *key)
+{
+ if (DES_new_random_key(key))
+ abort();
+}
diff --git a/source4/heimdal/lib/hcrypto/rsa-imath.c b/source4/heimdal/lib/hcrypto/rsa-imath.c
new file mode 100644
index 0000000000..5240279761
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/rsa-imath.c
@@ -0,0 +1,660 @@
+/*
+ * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+RCSID("$Id$");
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <krb5-types.h>
+#include <assert.h>
+
+#include <rsa.h>
+
+#include <roken.h>
+
+#include "imath/imath.h"
+#include "imath/iprime.h"
+
+static void
+BN2mpz(mpz_t *s, const BIGNUM *bn)
+{
+ size_t len;
+ void *p;
+
+ mp_int_init(s);
+
+ len = BN_num_bytes(bn);
+ p = malloc(len);
+ BN_bn2bin(bn, p);
+ mp_int_read_unsigned(s, p, len);
+ free(p);
+}
+
+static BIGNUM *
+mpz2BN(mpz_t *s)
+{
+ size_t size;
+ BIGNUM *bn;
+ void *p;
+
+ size = mp_int_unsigned_len(s);
+ p = malloc(size);
+ if (p == NULL && size != 0)
+ return NULL;
+ mp_int_to_unsigned(s, p, size);
+
+ bn = BN_bin2bn(p, size, NULL);
+ free(p);
+ return bn;
+}
+
+static int random_num(mp_int, size_t);
+
+static void
+setup_blind(mp_int n, mp_int b, mp_int bi)
+{
+ mp_int_init(b);
+ mp_int_init(bi);
+ random_num(b, mp_int_count_bits(n));
+ mp_int_mod(b, n, b);
+ mp_int_invmod(b, n, bi);
+}
+
+static void
+blind(mp_int in, mp_int b, mp_int e, mp_int n)
+{
+ mpz_t t1;
+ mp_int_init(&t1);
+ /* in' = (in * b^e) mod n */
+ mp_int_exptmod(b, e, n, &t1);
+ mp_int_mul(&t1, in, in);
+ mp_int_mod(in, n, in);
+ mp_int_clear(&t1);
+}
+
+static void
+unblind(mp_int out, mp_int bi, mp_int n)
+{
+ /* out' = (out * 1/b) mod n */
+ mp_int_mul(out, bi, out);
+ mp_int_mod(out, n, out);
+}
+
+static mp_result
+rsa_private_calculate(mp_int in, mp_int p, mp_int q,
+ mp_int dmp1, mp_int dmq1, mp_int iqmp,
+ mp_int out)
+{
+ mpz_t vp, vq, u;
+ mp_int_init(&vp); mp_int_init(&vq); mp_int_init(&u);
+
+ /* vq = c ^ (d mod (q - 1)) mod q */
+ /* vp = c ^ (d mod (p - 1)) mod p */
+ mp_int_mod(in, p, &u);
+ mp_int_exptmod(&u, dmp1, p, &vp);
+ mp_int_mod(in, q, &u);
+ mp_int_exptmod(&u, dmq1, q, &vq);
+
+ /* C2 = 1/q mod p (iqmp) */
+ /* u = (vp - vq)C2 mod p. */
+ mp_int_sub(&vp, &vq, &u);
+ if (mp_int_compare_zero(&u) < 0)
+ mp_int_add(&u, p, &u);
+ mp_int_mul(&u, iqmp, &u);
+ mp_int_mod(&u, p, &u);
+
+ /* c ^ d mod n = vq + u q */
+ mp_int_mul(&u, q, &u);
+ mp_int_add(&u, &vq, out);
+
+ mp_int_clear(&vp);
+ mp_int_clear(&vq);
+ mp_int_clear(&u);
+
+ return MP_OK;
+}
+
+/*
+ *
+ */
+
+static int
+imath_rsa_public_encrypt(int flen, const unsigned char* from,
+ unsigned char* to, RSA* rsa, int padding)
+{
+ unsigned char *p, *p0;
+ mp_result res;
+ size_t size, padlen;
+ mpz_t enc, dec, n, e;
+
+ if (padding != RSA_PKCS1_PADDING)
+ return -1;
+
+ size = RSA_size(rsa);
+
+ if (size < RSA_PKCS1_PADDING_SIZE || size - RSA_PKCS1_PADDING_SIZE < flen)
+ return -2;
+
+ BN2mpz(&n, rsa->n);
+ BN2mpz(&e, rsa->e);
+
+ p = p0 = malloc(size - 1);
+ if (p0 == NULL) {
+ mp_int_clear(&e);
+ mp_int_clear(&n);
+ return -3;
+ }
+
+ padlen = size - flen - 3;
+
+ *p++ = 2;
+ if (RAND_bytes(p, padlen) != 1) {
+ mp_int_clear(&e);
+ mp_int_clear(&n);
+ free(p0);
+ return -4;
+ }
+ while(padlen) {
+ if (*p == 0)
+ *p = 1;
+ padlen--;
+ p++;
+ }
+ *p++ = 0;
+ memcpy(p, from, flen);
+ p += flen;
+ assert((p - p0) == size - 1);
+
+ mp_int_init(&enc);
+ mp_int_init(&dec);
+ mp_int_read_unsigned(&dec, p0, size - 1);
+ free(p0);
+
+ res = mp_int_exptmod(&dec, &e, &n, &enc);
+
+ mp_int_clear(&dec);
+ mp_int_clear(&e);
+ mp_int_clear(&n);
+ {
+ size_t ssize;
+ ssize = mp_int_unsigned_len(&enc);
+ assert(size >= ssize);
+ mp_int_to_unsigned(&enc, to, ssize);
+ size = ssize;
+ }
+ mp_int_clear(&enc);
+
+ return size;
+}
+
+static int
+imath_rsa_public_decrypt(int flen, const unsigned char* from,
+ unsigned char* to, RSA* rsa, int padding)
+{
+ unsigned char *p;
+ mp_result res;
+ size_t size;
+ mpz_t s, us, n, e;
+
+ if (padding != RSA_PKCS1_PADDING)
+ return -1;
+
+ if (flen > RSA_size(rsa))
+ return -2;
+
+ BN2mpz(&n, rsa->n);
+ BN2mpz(&e, rsa->e);
+
+#if 0
+ /* Check that the exponent is larger then 3 */
+ if (mp_int_compare_value(&e, 3) <= 0) {
+ mp_int_clear(&n);
+ mp_int_clear(&e);
+ return -3;
+ }
+#endif
+
+ mp_int_init(&s);
+ mp_int_init(&us);
+ mp_int_read_unsigned(&s, rk_UNCONST(from), flen);
+
+ if (mp_int_compare(&s, &n) >= 0) {
+ mp_int_clear(&n);
+ mp_int_clear(&e);
+ return -4;
+ }
+
+ res = mp_int_exptmod(&s, &e, &n, &us);
+
+ mp_int_clear(&s);
+ mp_int_clear(&n);
+ mp_int_clear(&e);
+
+ if (res != MP_OK)
+ return -5;
+ p = to;
+
+
+ size = mp_int_unsigned_len(&us);
+ assert(size <= RSA_size(rsa));
+ mp_int_to_unsigned(&us, p, size);
+
+ mp_int_clear(&us);
+
+ /* head zero was skipped by mp_int_to_unsigned */
+ if (*p == 0)
+ return -6;
+ if (*p != 1)
+ return -7;
+ size--; p++;
+ while (size && *p == 0xff) {
+ size--; p++;
+ }
+ if (size == 0 || *p != 0)
+ return -8;
+ size--; p++;
+
+ memmove(to, p, size);
+
+ return size;
+}
+
+static int
+imath_rsa_private_encrypt(int flen, const unsigned char* from,
+ unsigned char* to, RSA* rsa, int padding)
+{
+ unsigned char *p, *p0;
+ mp_result res;
+ size_t size;
+ mpz_t in, out, n, e, b, bi;
+ int blinding = (rsa->flags & RSA_FLAG_NO_BLINDING) == 0;
+
+ if (padding != RSA_PKCS1_PADDING)
+ return -1;
+
+ size = RSA_size(rsa);
+
+ if (size < RSA_PKCS1_PADDING_SIZE || size - RSA_PKCS1_PADDING_SIZE < flen)
+ return -2;
+
+ p0 = p = malloc(size);
+ *p++ = 0;
+ *p++ = 1;
+ memset(p, 0xff, size - flen - 3);
+ p += size - flen - 3;
+ *p++ = 0;
+ memcpy(p, from, flen);
+ p += flen;
+ assert((p - p0) == size);
+
+ BN2mpz(&n, rsa->n);
+ BN2mpz(&e, rsa->e);
+
+ mp_int_init(&in);
+ mp_int_init(&out);
+ mp_int_read_unsigned(&in, p0, size);
+ free(p0);
+
+ if(mp_int_compare_zero(&in) < 0 ||
+ mp_int_compare(&in, &n) >= 0) {
+ size = 0;
+ goto out;
+ }
+
+ if (blinding) {
+ setup_blind(&n, &b, &bi);
+ blind(&in, &b, &e, &n);
+ }
+
+ if (rsa->p && rsa->q && rsa->dmp1 && rsa->dmq1 && rsa->iqmp) {
+ mpz_t p, q, dmp1, dmq1, iqmp;
+
+ BN2mpz(&p, rsa->p);
+ BN2mpz(&q, rsa->q);
+ BN2mpz(&dmp1, rsa->dmp1);
+ BN2mpz(&dmq1, rsa->dmq1);
+ BN2mpz(&iqmp, rsa->iqmp);
+
+ res = rsa_private_calculate(&in, &p, &q, &dmp1, &dmq1, &iqmp, &out);
+
+ mp_int_clear(&p);
+ mp_int_clear(&q);
+ mp_int_clear(&dmp1);
+ mp_int_clear(&dmq1);
+ mp_int_clear(&iqmp);
+ } else {
+ mpz_t d;
+
+ BN2mpz(&d, rsa->d);
+ res = mp_int_exptmod(&in, &d, &n, &out);
+ mp_int_clear(&d);
+ if (res != MP_OK) {
+ size = 0;
+ goto out;
+ }
+ }
+
+ if (blinding) {
+ unblind(&out, &bi, &n);
+ mp_int_clear(&b);
+ mp_int_clear(&bi);
+ }
+
+ {
+ size_t ssize;
+ ssize = mp_int_unsigned_len(&out);
+ assert(size >= ssize);
+ mp_int_to_unsigned(&out, to, size);
+ size = ssize;
+ }
+
+out:
+ mp_int_clear(&e);
+ mp_int_clear(&n);
+ mp_int_clear(&in);
+ mp_int_clear(&out);
+
+ return size;
+}
+
+static int
+imath_rsa_private_decrypt(int flen, const unsigned char* from,
+ unsigned char* to, RSA* rsa, int padding)
+{
+ unsigned char *ptr;
+ mp_result res;
+ size_t size;
+ mpz_t in, out, n, e, b, bi;
+ int blinding = (rsa->flags & RSA_FLAG_NO_BLINDING) == 0;
+
+ if (padding != RSA_PKCS1_PADDING)
+ return -1;
+
+ size = RSA_size(rsa);
+ if (flen > size)
+ return -2;
+
+ mp_int_init(&in);
+ mp_int_init(&out);
+
+ BN2mpz(&n, rsa->n);
+ BN2mpz(&e, rsa->e);
+
+ res = mp_int_read_unsigned(&in, rk_UNCONST(from), flen);
+ if (res != MP_OK) {
+ size = -1;
+ goto out;
+ }
+
+ if(mp_int_compare_zero(&in) < 0 ||
+ mp_int_compare(&in, &n) >= 0) {
+ size = 0;
+ goto out;
+ }
+
+ if (blinding) {
+ setup_blind(&n, &b, &bi);
+ blind(&in, &b, &e, &n);
+ }
+
+ if (rsa->p && rsa->q && rsa->dmp1 && rsa->dmq1 && rsa->iqmp) {
+ mpz_t p, q, dmp1, dmq1, iqmp;
+
+ BN2mpz(&p, rsa->p);
+ BN2mpz(&q, rsa->q);
+ BN2mpz(&dmp1, rsa->dmp1);
+ BN2mpz(&dmq1, rsa->dmq1);
+ BN2mpz(&iqmp, rsa->iqmp);
+
+ res = rsa_private_calculate(&in, &p, &q, &dmp1, &dmq1, &iqmp, &out);
+
+ mp_int_clear(&p);
+ mp_int_clear(&q);
+ mp_int_clear(&dmp1);
+ mp_int_clear(&dmq1);
+ mp_int_clear(&iqmp);
+ } else {
+ mpz_t d;
+
+ if(mp_int_compare_zero(&in) < 0 ||
+ mp_int_compare(&in, &n) >= 0)
+ return MP_RANGE;
+
+ BN2mpz(&d, rsa->d);
+ res = mp_int_exptmod(&in, &d, &n, &out);
+ mp_int_clear(&d);
+ if (res != MP_OK) {
+ size = 0;
+ goto out;
+ }
+ }
+
+ if (blinding) {
+ unblind(&out, &bi, &n);
+ mp_int_clear(&b);
+ mp_int_clear(&bi);
+ }
+
+ ptr = to;
+ {
+ size_t ssize;
+ ssize = mp_int_unsigned_len(&out);
+ assert(size >= ssize);
+ mp_int_to_unsigned(&out, ptr, ssize);
+ size = ssize;
+ }
+
+ /* head zero was skipped by mp_int_to_unsigned */
+ if (*ptr != 2)
+ return -3;
+ size--; ptr++;
+ while (size && *ptr != 0) {
+ size--; ptr++;
+ }
+ if (size == 0)
+ return -4;
+ size--; ptr++;
+
+ memmove(to, ptr, size);
+
+out:
+ mp_int_clear(&e);
+ mp_int_clear(&n);
+ mp_int_clear(&in);
+ mp_int_clear(&out);
+
+ return size;
+}
+
+static int
+random_num(mp_int num, size_t len)
+{
+ unsigned char *p;
+ mp_result res;
+
+ len = (len + 7) / 8;
+ p = malloc(len);
+ if (p == NULL)
+ return 1;
+ if (RAND_bytes(p, len) != 1) {
+ free(p);
+ return 1;
+ }
+ res = mp_int_read_unsigned(num, p, len);
+ free(p);
+ if (res != MP_OK)
+ return 1;
+ return 0;
+}
+
+#define CHECK(f, v) if ((f) != (v)) { goto out; }
+
+static int
+imath_rsa_generate_key(RSA *rsa, int bits, BIGNUM *e, BN_GENCB *cb)
+{
+ mpz_t el, p, q, n, d, dmp1, dmq1, iqmp, t1, t2, t3;
+ int counter, ret;
+
+ if (bits < 789)
+ return -1;
+
+ ret = -1;
+
+ mp_int_init(&el);
+ mp_int_init(&p);
+ mp_int_init(&q);
+ mp_int_init(&n);
+ mp_int_init(&d);
+ mp_int_init(&dmp1);
+ mp_int_init(&dmq1);
+ mp_int_init(&iqmp);
+ mp_int_init(&t1);
+ mp_int_init(&t2);
+ mp_int_init(&t3);
+
+ BN2mpz(&el, e);
+
+ /* generate p and q so that p != q and bits(pq) ~ bits */
+ counter = 0;
+ do {
+ BN_GENCB_call(cb, 2, counter++);
+ CHECK(random_num(&p, bits / 2 + 1), 0);
+ CHECK(mp_int_find_prime(&p), MP_TRUE);
+
+ CHECK(mp_int_sub_value(&p, 1, &t1), MP_OK);
+ CHECK(mp_int_gcd(&t1, &el, &t2), MP_OK);
+ } while(mp_int_compare_value(&t2, 1) != 0);
+
+ BN_GENCB_call(cb, 3, 0);
+
+ counter = 0;
+ do {
+ BN_GENCB_call(cb, 2, counter++);
+ CHECK(random_num(&q, bits / 2 + 1), 0);
+ CHECK(mp_int_find_prime(&q), MP_TRUE);
+
+ if (mp_int_compare(&p, &q) == 0) /* don't let p and q be the same */
+ continue;
+
+ CHECK(mp_int_sub_value(&q, 1, &t1), MP_OK);
+ CHECK(mp_int_gcd(&t1, &el, &t2), MP_OK);
+ } while(mp_int_compare_value(&t2, 1) != 0);
+
+ /* make p > q */
+ if (mp_int_compare(&p, &q) < 0)
+ mp_int_swap(&p, &q);
+
+ BN_GENCB_call(cb, 3, 1);
+
+ /* calculate n, n = p * q */
+ CHECK(mp_int_mul(&p, &q, &n), MP_OK);
+
+ /* calculate d, d = 1/e mod (p - 1)(q - 1) */
+ CHECK(mp_int_sub_value(&p, 1, &t1), MP_OK);
+ CHECK(mp_int_sub_value(&q, 1, &t2), MP_OK);
+ CHECK(mp_int_mul(&t1, &t2, &t3), MP_OK);
+ CHECK(mp_int_invmod(&el, &t3, &d), MP_OK);
+
+ /* calculate dmp1 dmp1 = d mod (p-1) */
+ CHECK(mp_int_mod(&d, &t1, &dmp1), MP_OK);
+ /* calculate dmq1 dmq1 = d mod (q-1) */
+ CHECK(mp_int_mod(&d, &t2, &dmq1), MP_OK);
+ /* calculate iqmp iqmp = 1/q mod p */
+ CHECK(mp_int_invmod(&q, &p, &iqmp), MP_OK);
+
+ /* fill in RSA key */
+
+ rsa->e = mpz2BN(&el);
+ rsa->p = mpz2BN(&p);
+ rsa->q = mpz2BN(&q);
+ rsa->n = mpz2BN(&n);
+ rsa->d = mpz2BN(&d);
+ rsa->dmp1 = mpz2BN(&dmp1);
+ rsa->dmq1 = mpz2BN(&dmq1);
+ rsa->iqmp = mpz2BN(&iqmp);
+
+ ret = 1;
+out:
+ mp_int_clear(&el);
+ mp_int_clear(&p);
+ mp_int_clear(&q);
+ mp_int_clear(&n);
+ mp_int_clear(&d);
+ mp_int_clear(&dmp1);
+ mp_int_clear(&dmq1);
+ mp_int_clear(&iqmp);
+ mp_int_clear(&t1);
+ mp_int_clear(&t2);
+ mp_int_clear(&t3);
+
+ return ret;
+}
+
+static int
+imath_rsa_init(RSA *rsa)
+{
+ return 1;
+}
+
+static int
+imath_rsa_finish(RSA *rsa)
+{
+ return 1;
+}
+
+const RSA_METHOD hc_rsa_imath_method = {
+ "hcrypto imath RSA",
+ imath_rsa_public_encrypt,
+ imath_rsa_public_decrypt,
+ imath_rsa_private_encrypt,
+ imath_rsa_private_decrypt,
+ NULL,
+ NULL,
+ imath_rsa_init,
+ imath_rsa_finish,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ imath_rsa_generate_key
+};
+
+const RSA_METHOD *
+RSA_imath_method(void)
+{
+ return &hc_rsa_imath_method;
+}
diff --git a/source4/heimdal/lib/hcrypto/rsa.c b/source4/heimdal/lib/hcrypto/rsa.c
new file mode 100644
index 0000000000..f3095e7d3d
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/rsa.c
@@ -0,0 +1,565 @@
+/*
+ * Copyright (c) 2006 - 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+RCSID("$Id$");
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <krb5-types.h>
+#include <rfc2459_asn1.h>
+
+#include <rsa.h>
+
+#include <roken.h>
+
+/**
+ * @page page_rsa RSA - public-key cryptography
+ *
+ * RSA is named by its inventors (Ron Rivest, Adi Shamir, and Leonard
+ * Adleman) (published in 1977), patented expired in 21 September 2000.
+ *
+ * See the library functions here: @ref hcrypto_rsa
+ */
+
+/**
+ * Same as RSA_new_method() using NULL as engine.
+ *
+ * @return a newly allocated RSA object. Free with RSA_free().
+ *
+ * @ingroup hcrypto_rsa
+ */
+
+RSA *
+RSA_new(void)
+{
+ return RSA_new_method(NULL);
+}
+
+/**
+ * Allocate a new RSA object using the engine, if NULL is specified as
+ * the engine, use the default RSA engine as returned by
+ * ENGINE_get_default_RSA().
+ *
+ * @param engine Specific what ENGINE RSA provider should be used.
+ *
+ * @return a newly allocated RSA object. Free with RSA_free().
+ *
+ * @ingroup hcrypto_rsa
+ */
+
+RSA *
+RSA_new_method(ENGINE *engine)
+{
+ RSA *rsa;
+
+ rsa = calloc(1, sizeof(*rsa));
+ if (rsa == NULL)
+ return NULL;
+
+ rsa->references = 1;
+
+ if (engine) {
+ ENGINE_up_ref(engine);
+ rsa->engine = engine;
+ } else {
+ rsa->engine = ENGINE_get_default_RSA();
+ }
+
+ if (rsa->engine) {
+ rsa->meth = ENGINE_get_RSA(rsa->engine);
+ if (rsa->meth == NULL) {
+ ENGINE_finish(engine);
+ free(rsa);
+ return 0;
+ }
+ }
+
+ if (rsa->meth == NULL)
+ rsa->meth = rk_UNCONST(RSA_get_default_method());
+
+ (*rsa->meth->init)(rsa);
+
+ return rsa;
+}
+
+/**
+ * Free an allocation RSA object.
+ *
+ * @param rsa the RSA object to free.
+ * @ingroup hcrypto_rsa
+ */
+
+void
+RSA_free(RSA *rsa)
+{
+ if (rsa->references <= 0)
+ abort();
+
+ if (--rsa->references > 0)
+ return;
+
+ (*rsa->meth->finish)(rsa);
+
+ if (rsa->engine)
+ ENGINE_finish(rsa->engine);
+
+#define free_if(f) if (f) { BN_free(f); }
+ free_if(rsa->n);
+ free_if(rsa->e);
+ free_if(rsa->d);
+ free_if(rsa->p);
+ free_if(rsa->q);
+ free_if(rsa->dmp1);
+ free_if(rsa->dmq1);
+ free_if(rsa->iqmp);
+#undef free_if
+
+ memset(rsa, 0, sizeof(*rsa));
+ free(rsa);
+}
+
+/**
+ * Add an extra reference to the RSA object. The object should be free
+ * with RSA_free() to drop the reference.
+ *
+ * @param rsa the object to add reference counting too.
+ *
+ * @return the current reference count, can't safely be used except
+ * for debug printing.
+ *
+ * @ingroup hcrypto_rsa
+ */
+
+int
+RSA_up_ref(RSA *rsa)
+{
+ return ++rsa->references;
+}
+
+/**
+ * Return the RSA_METHOD used for this RSA object.
+ *
+ * @param rsa the object to get the method from.
+ *
+ * @return the method used for this RSA object.
+ *
+ * @ingroup hcrypto_rsa
+ */
+
+const RSA_METHOD *
+RSA_get_method(const RSA *rsa)
+{
+ return rsa->meth;
+}
+
+/**
+ * Set a new method for the RSA keypair.
+ *
+ * @param rsa rsa parameter.
+ * @param method the new method for the RSA parameter.
+ *
+ * @return 1 on success.
+ *
+ * @ingroup hcrypto_rsa
+ */
+
+int
+RSA_set_method(RSA *rsa, const RSA_METHOD *method)
+{
+ (*rsa->meth->finish)(rsa);
+
+ if (rsa->engine) {
+ ENGINE_finish(rsa->engine);
+ rsa->engine = NULL;
+ }
+
+ rsa->meth = method;
+ (*rsa->meth->init)(rsa);
+ return 1;
+}
+
+/**
+ * Set the application data for the RSA object.
+ *
+ * @param rsa the rsa object to set the parameter for
+ * @param arg the data object to store
+ *
+ * @return 1 on success.
+ *
+ * @ingroup hcrypto_rsa
+ */
+
+int
+RSA_set_app_data(RSA *rsa, void *arg)
+{
+ rsa->ex_data.sk = arg;
+ return 1;
+}
+
+/**
+ * Get the application data for the RSA object.
+ *
+ * @param rsa the rsa object to get the parameter for
+ *
+ * @return the data object
+ *
+ * @ingroup hcrypto_rsa
+ */
+
+void *
+RSA_get_app_data(RSA *rsa)
+{
+ return rsa->ex_data.sk;
+}
+
+int
+RSA_check_key(const RSA *key)
+{
+ static const unsigned char inbuf[] = "hello, world!";
+ RSA *rsa = rk_UNCONST(key);
+ void *buffer;
+ int ret;
+
+ /*
+ * XXX I have no clue how to implement this w/o a bignum library.
+ * Well, when we have a RSA key pair, we can try to encrypt/sign
+ * and then decrypt/verify.
+ */
+
+ if ((rsa->d == NULL || rsa->n == NULL) &&
+ (rsa->p == NULL || rsa->q || rsa->dmp1 == NULL || rsa->dmq1 == NULL || rsa->iqmp == NULL))
+ return 0;
+
+ buffer = malloc(RSA_size(rsa));
+ if (buffer == NULL)
+ return 0;
+
+ ret = RSA_private_encrypt(sizeof(inbuf), inbuf, buffer,
+ rsa, RSA_PKCS1_PADDING);
+ if (ret == -1) {
+ free(buffer);
+ return 0;
+ }
+
+ ret = RSA_public_decrypt(ret, buffer, buffer,
+ rsa, RSA_PKCS1_PADDING);
+ if (ret == -1) {
+ free(buffer);
+ return 0;
+ }
+
+ if (ret == sizeof(inbuf) && memcmp(buffer, inbuf, sizeof(inbuf)) == 0) {
+ free(buffer);
+ return 1;
+ }
+ free(buffer);
+ return 0;
+}
+
+int
+RSA_size(const RSA *rsa)
+{
+ return BN_num_bytes(rsa->n);
+}
+
+#define RSAFUNC(name, body) \
+int \
+name(int flen,const unsigned char* f, unsigned char* t, RSA* r, int p){\
+ return body; \
+}
+
+RSAFUNC(RSA_public_encrypt, (r)->meth->rsa_pub_enc(flen, f, t, r, p))
+RSAFUNC(RSA_public_decrypt, (r)->meth->rsa_pub_dec(flen, f, t, r, p))
+RSAFUNC(RSA_private_encrypt, (r)->meth->rsa_priv_enc(flen, f, t, r, p))
+RSAFUNC(RSA_private_decrypt, (r)->meth->rsa_priv_dec(flen, f, t, r, p))
+
+/* XXX */
+int
+RSA_sign(int type, const unsigned char *from, unsigned int flen,
+ unsigned char *to, unsigned int *tlen, RSA *rsa)
+{
+ return -1;
+}
+
+int
+RSA_verify(int type, const unsigned char *from, unsigned int flen,
+ unsigned char *to, unsigned int tlen, RSA *rsa)
+{
+ return -1;
+}
+
+/*
+ * A NULL RSA_METHOD that returns failure for all operations. This is
+ * used as the default RSA method if we don't have any native
+ * support.
+ */
+
+static RSAFUNC(null_rsa_public_encrypt, -1)
+static RSAFUNC(null_rsa_public_decrypt, -1)
+static RSAFUNC(null_rsa_private_encrypt, -1)
+static RSAFUNC(null_rsa_private_decrypt, -1)
+
+/*
+ *
+ */
+
+int
+RSA_generate_key_ex(RSA *r, int bits, BIGNUM *e, BN_GENCB *cb)
+{
+ if (r->meth->rsa_keygen)
+ return (*r->meth->rsa_keygen)(r, bits, e, cb);
+ return 0;
+}
+
+
+/*
+ *
+ */
+
+static int
+null_rsa_init(RSA *rsa)
+{
+ return 1;
+}
+
+static int
+null_rsa_finish(RSA *rsa)
+{
+ return 1;
+}
+
+static const RSA_METHOD rsa_null_method = {
+ "hcrypto null RSA",
+ null_rsa_public_encrypt,
+ null_rsa_public_decrypt,
+ null_rsa_private_encrypt,
+ null_rsa_private_decrypt,
+ NULL,
+ NULL,
+ null_rsa_init,
+ null_rsa_finish,
+ 0,
+ NULL,
+ NULL,
+ NULL
+};
+
+const RSA_METHOD *
+RSA_null_method(void)
+{
+ return &rsa_null_method;
+}
+
+extern const RSA_METHOD hc_rsa_imath_method;
+#ifdef HAVE_GMP
+static const RSA_METHOD *default_rsa_method = &hc_rsa_gmp_method;
+#else
+static const RSA_METHOD *default_rsa_method = &hc_rsa_imath_method;
+#endif
+
+const RSA_METHOD *
+RSA_get_default_method(void)
+{
+ return default_rsa_method;
+}
+
+void
+RSA_set_default_method(const RSA_METHOD *meth)
+{
+ default_rsa_method = meth;
+}
+
+/*
+ *
+ */
+
+static BIGNUM *
+heim_int2BN(const heim_integer *i)
+{
+ BIGNUM *bn;
+
+ bn = BN_bin2bn(i->data, i->length, NULL);
+ if (bn)
+ BN_set_negative(bn, i->negative);
+ return bn;
+}
+
+static int
+bn2heim_int(BIGNUM *bn, heim_integer *integer)
+{
+ integer->length = BN_num_bytes(bn);
+ integer->data = malloc(integer->length);
+ if (integer->data == NULL) {
+ integer->length = 0;
+ return ENOMEM;
+ }
+ BN_bn2bin(bn, integer->data);
+ integer->negative = BN_is_negative(bn);
+ return 0;
+}
+
+
+RSA *
+d2i_RSAPrivateKey(RSA *rsa, const unsigned char **pp, size_t len)
+{
+ RSAPrivateKey data;
+ RSA *k = rsa;
+ size_t size;
+ int ret;
+
+ ret = decode_RSAPrivateKey(*pp, len, &data, &size);
+ if (ret)
+ return NULL;
+
+ *pp += size;
+
+ if (k == NULL) {
+ k = RSA_new();
+ if (k == NULL) {
+ free_RSAPrivateKey(&data);
+ return NULL;
+ }
+ }
+
+ k->n = heim_int2BN(&data.modulus);
+ k->e = heim_int2BN(&data.publicExponent);
+ k->d = heim_int2BN(&data.privateExponent);
+ k->p = heim_int2BN(&data.prime1);
+ k->q = heim_int2BN(&data.prime2);
+ k->dmp1 = heim_int2BN(&data.exponent1);
+ k->dmq1 = heim_int2BN(&data.exponent2);
+ k->iqmp = heim_int2BN(&data.coefficient);
+ free_RSAPrivateKey(&data);
+
+ if (k->n == NULL || k->e == NULL || k->d == NULL || k->p == NULL ||
+ k->q == NULL || k->dmp1 == NULL || k->dmq1 == NULL || k->iqmp == NULL)
+ {
+ RSA_free(k);
+ return NULL;
+ }
+
+ return k;
+}
+
+int
+i2d_RSAPrivateKey(RSA *rsa, unsigned char **pp)
+{
+ RSAPrivateKey data;
+ size_t size;
+ int ret;
+
+ if (rsa->n == NULL || rsa->e == NULL || rsa->d == NULL || rsa->p == NULL ||
+ rsa->q == NULL || rsa->dmp1 == NULL || rsa->dmq1 == NULL ||
+ rsa->iqmp == NULL)
+ return -1;
+
+ memset(&data, 0, sizeof(data));
+
+ ret = bn2heim_int(rsa->n, &data.modulus);
+ ret |= bn2heim_int(rsa->e, &data.publicExponent);
+ ret |= bn2heim_int(rsa->d, &data.privateExponent);
+ ret |= bn2heim_int(rsa->p, &data.prime1);
+ ret |= bn2heim_int(rsa->q, &data.prime2);
+ ret |= bn2heim_int(rsa->dmp1, &data.exponent1);
+ ret |= bn2heim_int(rsa->dmq1, &data.exponent2);
+ ret |= bn2heim_int(rsa->iqmp, &data.coefficient);
+ if (ret) {
+ free_RSAPrivateKey(&data);
+ return -1;
+ }
+
+ if (pp == NULL) {
+ size = length_RSAPrivateKey(&data);
+ free_RSAPrivateKey(&data);
+ } else {
+ void *p;
+ size_t len;
+
+ ASN1_MALLOC_ENCODE(RSAPrivateKey, p, len, &data, &size, ret);
+ free_RSAPrivateKey(&data);
+ if (ret)
+ return -1;
+ if (len != size)
+ abort();
+
+ memcpy(*pp, p, size);
+ free(p);
+
+ *pp += size;
+
+ }
+ return size;
+}
+
+int
+i2d_RSAPublicKey(RSA *rsa, unsigned char **pp)
+{
+ RSAPublicKey data;
+ size_t size;
+ int ret;
+
+ memset(&data, 0, sizeof(data));
+
+ if (bn2heim_int(rsa->n, &data.modulus) ||
+ bn2heim_int(rsa->e, &data.publicExponent))
+ {
+ free_RSAPublicKey(&data);
+ return -1;
+ }
+
+ if (pp == NULL) {
+ size = length_RSAPublicKey(&data);
+ free_RSAPublicKey(&data);
+ } else {
+ void *p;
+ size_t len;
+
+ ASN1_MALLOC_ENCODE(RSAPublicKey, p, len, &data, &size, ret);
+ free_RSAPublicKey(&data);
+ if (ret)
+ return -1;
+ if (len != size)
+ abort();
+
+ memcpy(*pp, p, size);
+ free(p);
+
+ *pp += size;
+ }
+
+ return size;
+}
diff --git a/source4/heimdal/lib/hcrypto/rsa.h b/source4/heimdal/lib/hcrypto/rsa.h
new file mode 100644
index 0000000000..257e7f01c4
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/rsa.h
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id$
+ */
+
+#ifndef _HEIM_RSA_H
+#define _HEIM_RSA_H 1
+
+/* symbol renaming */
+#define RSA_null_method hc_RSA_null_method
+#define RSA_imath_method hc_RSA_imath_method
+#define RSA_gmp_method hc_RSA_gmp_method
+#define RSA_new hc_RSA_new
+#define RSA_new_method hc_RSA_new_method
+#define RSA_free hc_RSA_free
+#define RSA_up_ref hc_RSA_up_ref
+#define RSA_set_default_method hc_RSA_set_default_method
+#define RSA_get_default_method hc_RSA_get_default_method
+#define RSA_set_method hc_RSA_set_method
+#define RSA_get_method hc_RSA_get_method
+#define RSA_set_app_data hc_RSA_set_app_data
+#define RSA_get_app_data hc_RSA_get_app_data
+#define RSA_check_key hc_RSA_check_key
+#define RSA_size hc_RSA_size
+#define RSA_public_encrypt hc_RSA_public_encrypt
+#define RSA_public_decrypt hc_RSA_public_decrypt
+#define RSA_private_encrypt hc_RSA_private_encrypt
+#define RSA_private_decrypt hc_RSA_private_decrypt
+#define RSA_sign hc_RSA_sign
+#define RSA_verify hc_RSA_verify
+#define RSA_generate_key_ex hc_RSA_generate_key_ex
+#define d2i_RSAPrivateKey hc_d2i_RSAPrivateKey
+#define i2d_RSAPrivateKey hc_i2d_RSAPrivateKey
+#define i2d_RSAPublicKey hc_i2d_RSAPublicKey
+
+/*
+ *
+ */
+
+typedef struct RSA RSA;
+typedef struct RSA_METHOD RSA_METHOD;
+
+#include <hcrypto/bn.h>
+#include <hcrypto/engine.h>
+
+struct RSA_METHOD {
+ const char *name;
+ int (*rsa_pub_enc)(int,const unsigned char *, unsigned char *, RSA *,int);
+ int (*rsa_pub_dec)(int,const unsigned char *, unsigned char *, RSA *,int);
+ int (*rsa_priv_enc)(int,const unsigned char *, unsigned char *, RSA *,int);
+ int (*rsa_priv_dec)(int,const unsigned char *, unsigned char *, RSA *,int);
+ void *rsa_mod_exp;
+ void *bn_mod_exp;
+ int (*init)(RSA *rsa);
+ int (*finish)(RSA *rsa);
+ int flags;
+ char *app_data;
+ int (*rsa_sign)(int, const unsigned char *, unsigned int,
+ unsigned char *, unsigned int *, const RSA *);
+ int (*rsa_verify)(int, const unsigned char *, unsigned int,
+ unsigned char *, unsigned int, const RSA *);
+ int (*rsa_keygen)(RSA *, int, BIGNUM *, BN_GENCB *);
+};
+
+struct RSA {
+ int pad;
+ long version;
+ const RSA_METHOD *meth;
+ void *engine;
+ BIGNUM *n;
+ BIGNUM *e;
+ BIGNUM *d;
+ BIGNUM *p;
+ BIGNUM *q;
+ BIGNUM *dmp1;
+ BIGNUM *dmq1;
+ BIGNUM *iqmp;
+ struct rsa_CRYPTO_EX_DATA {
+ void *sk;
+ int dummy;
+ } ex_data;
+ int references;
+ int flags;
+ void *_method_mod_n;
+ void *_method_mod_p;
+ void *_method_mod_q;
+
+ char *bignum_data;
+ void *blinding;
+ void *mt_blinding;
+};
+
+#define RSA_FLAG_NO_BLINDING 0x0080
+
+#define RSA_PKCS1_PADDING 1
+#define RSA_PKCS1_OAEP_PADDING 4
+#define RSA_PKCS1_PADDING_SIZE 11
+
+/*
+ *
+ */
+
+const RSA_METHOD *RSA_null_method(void);
+const RSA_METHOD *RSA_imath_method(void);
+const RSA_METHOD *RSA_gmp_method(void);
+
+/*
+ *
+ */
+
+RSA * RSA_new(void);
+RSA * RSA_new_method(ENGINE *);
+void RSA_free(RSA *);
+int RSA_up_ref(RSA *);
+
+void RSA_set_default_method(const RSA_METHOD *);
+const RSA_METHOD * RSA_get_default_method(void);
+
+const RSA_METHOD * RSA_get_method(const RSA *);
+int RSA_set_method(RSA *, const RSA_METHOD *);
+
+int RSA_set_app_data(RSA *, void *arg);
+void * RSA_get_app_data(RSA *);
+
+int RSA_check_key(const RSA *);
+int RSA_size(const RSA *);
+
+int RSA_public_encrypt(int,const unsigned char*,unsigned char*,RSA *,int);
+int RSA_private_encrypt(int,const unsigned char*,unsigned char*,RSA *,int);
+int RSA_public_decrypt(int,const unsigned char*,unsigned char*,RSA *,int);
+int RSA_private_decrypt(int,const unsigned char*,unsigned char*,RSA *,int);
+
+int RSA_sign(int, const unsigned char *, unsigned int,
+ unsigned char *, unsigned int *, RSA *);
+int RSA_verify(int, const unsigned char *, unsigned int,
+ unsigned char *, unsigned int, RSA *);
+
+int RSA_generate_key_ex(RSA *, int, BIGNUM *, BN_GENCB *);
+
+RSA * d2i_RSAPrivateKey(RSA *, const unsigned char **, size_t);
+int i2d_RSAPrivateKey(RSA *, unsigned char **);
+
+int i2d_RSAPublicKey(RSA *, unsigned char **);
+
+#endif /* _HEIM_RSA_H */
diff --git a/source4/heimdal/lib/hcrypto/sha.c b/source4/heimdal/lib/hcrypto/sha.c
new file mode 100644
index 0000000000..fd48672784
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/sha.c
@@ -0,0 +1,300 @@
+/*
+ * Copyright (c) 1995 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+
+RCSID("$Id$");
+#endif
+
+#include "hash.h"
+#include "sha.h"
+
+#define A m->counter[0]
+#define B m->counter[1]
+#define C m->counter[2]
+#define D m->counter[3]
+#define E m->counter[4]
+#define X data
+
+void
+SHA1_Init (struct sha *m)
+{
+ m->sz[0] = 0;
+ m->sz[1] = 0;
+ A = 0x67452301;
+ B = 0xefcdab89;
+ C = 0x98badcfe;
+ D = 0x10325476;
+ E = 0xc3d2e1f0;
+}
+
+
+#define F0(x,y,z) CRAYFIX((x & y) | (~x & z))
+#define F1(x,y,z) (x ^ y ^ z)
+#define F2(x,y,z) ((x & y) | (x & z) | (y & z))
+#define F3(x,y,z) F1(x,y,z)
+
+#define K0 0x5a827999
+#define K1 0x6ed9eba1
+#define K2 0x8f1bbcdc
+#define K3 0xca62c1d6
+
+#define DO(t,f,k) \
+do { \
+ uint32_t temp; \
+ \
+ temp = cshift(AA, 5) + f(BB,CC,DD) + EE + data[t] + k; \
+ EE = DD; \
+ DD = CC; \
+ CC = cshift(BB, 30); \
+ BB = AA; \
+ AA = temp; \
+} while(0)
+
+static inline void
+calc (struct sha *m, uint32_t *in)
+{
+ uint32_t AA, BB, CC, DD, EE;
+ uint32_t data[80];
+ int i;
+
+ AA = A;
+ BB = B;
+ CC = C;
+ DD = D;
+ EE = E;
+
+ for (i = 0; i < 16; ++i)
+ data[i] = in[i];
+ for (i = 16; i < 80; ++i)
+ data[i] = cshift(data[i-3] ^ data[i-8] ^ data[i-14] ^ data[i-16], 1);
+
+ /* t=[0,19] */
+
+ DO(0,F0,K0);
+ DO(1,F0,K0);
+ DO(2,F0,K0);
+ DO(3,F0,K0);
+ DO(4,F0,K0);
+ DO(5,F0,K0);
+ DO(6,F0,K0);
+ DO(7,F0,K0);
+ DO(8,F0,K0);
+ DO(9,F0,K0);
+ DO(10,F0,K0);
+ DO(11,F0,K0);
+ DO(12,F0,K0);
+ DO(13,F0,K0);
+ DO(14,F0,K0);
+ DO(15,F0,K0);
+ DO(16,F0,K0);
+ DO(17,F0,K0);
+ DO(18,F0,K0);
+ DO(19,F0,K0);
+
+ /* t=[20,39] */
+
+ DO(20,F1,K1);
+ DO(21,F1,K1);
+ DO(22,F1,K1);
+ DO(23,F1,K1);
+ DO(24,F1,K1);
+ DO(25,F1,K1);
+ DO(26,F1,K1);
+ DO(27,F1,K1);
+ DO(28,F1,K1);
+ DO(29,F1,K1);
+ DO(30,F1,K1);
+ DO(31,F1,K1);
+ DO(32,F1,K1);
+ DO(33,F1,K1);
+ DO(34,F1,K1);
+ DO(35,F1,K1);
+ DO(36,F1,K1);
+ DO(37,F1,K1);
+ DO(38,F1,K1);
+ DO(39,F1,K1);
+
+ /* t=[40,59] */
+
+ DO(40,F2,K2);
+ DO(41,F2,K2);
+ DO(42,F2,K2);
+ DO(43,F2,K2);
+ DO(44,F2,K2);
+ DO(45,F2,K2);
+ DO(46,F2,K2);
+ DO(47,F2,K2);
+ DO(48,F2,K2);
+ DO(49,F2,K2);
+ DO(50,F2,K2);
+ DO(51,F2,K2);
+ DO(52,F2,K2);
+ DO(53,F2,K2);
+ DO(54,F2,K2);
+ DO(55,F2,K2);
+ DO(56,F2,K2);
+ DO(57,F2,K2);
+ DO(58,F2,K2);
+ DO(59,F2,K2);
+
+ /* t=[60,79] */
+
+ DO(60,F3,K3);
+ DO(61,F3,K3);
+ DO(62,F3,K3);
+ DO(63,F3,K3);
+ DO(64,F3,K3);
+ DO(65,F3,K3);
+ DO(66,F3,K3);
+ DO(67,F3,K3);
+ DO(68,F3,K3);
+ DO(69,F3,K3);
+ DO(70,F3,K3);
+ DO(71,F3,K3);
+ DO(72,F3,K3);
+ DO(73,F3,K3);
+ DO(74,F3,K3);
+ DO(75,F3,K3);
+ DO(76,F3,K3);
+ DO(77,F3,K3);
+ DO(78,F3,K3);
+ DO(79,F3,K3);
+
+ A += AA;
+ B += BB;
+ C += CC;
+ D += DD;
+ E += EE;
+}
+
+/*
+ * From `Performance analysis of MD5' by Joseph D. Touch <touch@isi.edu>
+ */
+
+#if !defined(WORDS_BIGENDIAN) || defined(_CRAY)
+static inline uint32_t
+swap_uint32_t (uint32_t t)
+{
+#define ROL(x,n) ((x)<<(n))|((x)>>(32-(n)))
+ uint32_t temp1, temp2;
+
+ temp1 = cshift(t, 16);
+ temp2 = temp1 >> 8;
+ temp1 &= 0x00ff00ff;
+ temp2 &= 0x00ff00ff;
+ temp1 <<= 8;
+ return temp1 | temp2;
+}
+#endif
+
+struct x32{
+ unsigned int a:32;
+ unsigned int b:32;
+};
+
+void
+SHA1_Update (struct sha *m, const void *v, size_t len)
+{
+ const unsigned char *p = v;
+ size_t old_sz = m->sz[0];
+ size_t offset;
+
+ m->sz[0] += len * 8;
+ if (m->sz[0] < old_sz)
+ ++m->sz[1];
+ offset = (old_sz / 8) % 64;
+ while(len > 0){
+ size_t l = min(len, 64 - offset);
+ memcpy(m->save + offset, p, l);
+ offset += l;
+ p += l;
+ len -= l;
+ if(offset == 64){
+#if !defined(WORDS_BIGENDIAN) || defined(_CRAY)
+ int i;
+ uint32_t current[16];
+ struct x32 *u = (struct x32*)m->save;
+ for(i = 0; i < 8; i++){
+ current[2*i+0] = swap_uint32_t(u[i].a);
+ current[2*i+1] = swap_uint32_t(u[i].b);
+ }
+ calc(m, current);
+#else
+ calc(m, (uint32_t*)m->save);
+#endif
+ offset = 0;
+ }
+ }
+}
+
+void
+SHA1_Final (void *res, struct sha *m)
+{
+ unsigned char zeros[72];
+ unsigned offset = (m->sz[0] / 8) % 64;
+ unsigned int dstart = (120 - offset - 1) % 64 + 1;
+
+ *zeros = 0x80;
+ memset (zeros + 1, 0, sizeof(zeros) - 1);
+ zeros[dstart+7] = (m->sz[0] >> 0) & 0xff;
+ zeros[dstart+6] = (m->sz[0] >> 8) & 0xff;
+ zeros[dstart+5] = (m->sz[0] >> 16) & 0xff;
+ zeros[dstart+4] = (m->sz[0] >> 24) & 0xff;
+ zeros[dstart+3] = (m->sz[1] >> 0) & 0xff;
+ zeros[dstart+2] = (m->sz[1] >> 8) & 0xff;
+ zeros[dstart+1] = (m->sz[1] >> 16) & 0xff;
+ zeros[dstart+0] = (m->sz[1] >> 24) & 0xff;
+ SHA1_Update (m, zeros, dstart + 8);
+ {
+ int i;
+ unsigned char *r = (unsigned char*)res;
+
+ for (i = 0; i < 5; ++i) {
+ r[4*i+3] = m->counter[i] & 0xFF;
+ r[4*i+2] = (m->counter[i] >> 8) & 0xFF;
+ r[4*i+1] = (m->counter[i] >> 16) & 0xFF;
+ r[4*i] = (m->counter[i] >> 24) & 0xFF;
+ }
+ }
+#if 0
+ {
+ int i;
+ uint32_t *r = (uint32_t *)res;
+
+ for (i = 0; i < 5; ++i)
+ r[i] = swap_uint32_t (m->counter[i]);
+ }
+#endif
+}
diff --git a/source4/heimdal/lib/hcrypto/sha.h b/source4/heimdal/lib/hcrypto/sha.h
new file mode 100644
index 0000000000..39e33cf8d0
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/sha.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 1995 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef HEIM_SHA_H
+#define HEIM_SHA_H 1
+
+/* symbol renaming */
+#define SHA1_Init hc_SHA1_Init
+#define SHA1_Update hc_SHA1_Update
+#define SHA1_Final hc_SHA1_Final
+#define SHA256_Init hc_SHA256_Init
+#define SHA256_Update hc_SHA256_Update
+#define SHA256_Final hc_SHA256_Final
+
+/*
+ * SHA-1
+ */
+
+#define SHA_DIGEST_LENGTH 20
+
+struct sha {
+ unsigned int sz[2];
+ uint32_t counter[5];
+ unsigned char save[64];
+};
+
+typedef struct sha SHA_CTX;
+
+void SHA1_Init (struct sha *m);
+void SHA1_Update (struct sha *m, const void *v, size_t len);
+void SHA1_Final (void *res, struct sha *m);
+
+/*
+ * SHA-2 256
+ */
+
+#define SHA256_DIGEST_LENGTH 32
+
+struct hc_sha256state {
+ unsigned int sz[2];
+ uint32_t counter[8];
+ unsigned char save[64];
+};
+
+typedef struct hc_sha256state SHA256_CTX;
+
+void SHA256_Init (SHA256_CTX *);
+void SHA256_Update (SHA256_CTX *, const void *, size_t);
+void SHA256_Final (void *, SHA256_CTX *);
+
+#endif /* HEIM_SHA_H */
diff --git a/source4/heimdal/lib/hcrypto/sha256.c b/source4/heimdal/lib/hcrypto/sha256.c
new file mode 100644
index 0000000000..922fb055af
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/sha256.c
@@ -0,0 +1,233 @@
+/*
+ * Copyright (c) 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+
+RCSID("$Id$");
+#endif
+
+#include "hash.h"
+#include "sha.h"
+
+#define Ch(x,y,z) (((x) & (y)) ^ ((~(x)) & (z)))
+#define Maj(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
+
+#define ROTR(x,n) (((x)>>(n)) | ((x) << (32 - (n))))
+
+#define Sigma0(x) (ROTR(x,2) ^ ROTR(x,13) ^ ROTR(x,22))
+#define Sigma1(x) (ROTR(x,6) ^ ROTR(x,11) ^ ROTR(x,25))
+#define sigma0(x) (ROTR(x,7) ^ ROTR(x,18) ^ ((x)>>3))
+#define sigma1(x) (ROTR(x,17) ^ ROTR(x,19) ^ ((x)>>10))
+
+#define A m->counter[0]
+#define B m->counter[1]
+#define C m->counter[2]
+#define D m->counter[3]
+#define E m->counter[4]
+#define F m->counter[5]
+#define G m->counter[6]
+#define H m->counter[7]
+
+static const uint32_t constant_256[64] = {
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
+ 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
+ 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
+ 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
+ 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
+ 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
+ 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
+ 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+ 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
+};
+
+void
+SHA256_Init (SHA256_CTX *m)
+{
+ m->sz[0] = 0;
+ m->sz[1] = 0;
+ A = 0x6a09e667;
+ B = 0xbb67ae85;
+ C = 0x3c6ef372;
+ D = 0xa54ff53a;
+ E = 0x510e527f;
+ F = 0x9b05688c;
+ G = 0x1f83d9ab;
+ H = 0x5be0cd19;
+}
+
+static void
+calc (SHA256_CTX *m, uint32_t *in)
+{
+ uint32_t AA, BB, CC, DD, EE, FF, GG, HH;
+ uint32_t data[64];
+ int i;
+
+ AA = A;
+ BB = B;
+ CC = C;
+ DD = D;
+ EE = E;
+ FF = F;
+ GG = G;
+ HH = H;
+
+ for (i = 0; i < 16; ++i)
+ data[i] = in[i];
+ for (i = 16; i < 64; ++i)
+ data[i] = sigma1(data[i-2]) + data[i-7] +
+ sigma0(data[i-15]) + data[i - 16];
+
+ for (i = 0; i < 64; i++) {
+ uint32_t T1, T2;
+
+ T1 = HH + Sigma1(EE) + Ch(EE, FF, GG) + constant_256[i] + data[i];
+ T2 = Sigma0(AA) + Maj(AA,BB,CC);
+
+ HH = GG;
+ GG = FF;
+ FF = EE;
+ EE = DD + T1;
+ DD = CC;
+ CC = BB;
+ BB = AA;
+ AA = T1 + T2;
+ }
+
+ A += AA;
+ B += BB;
+ C += CC;
+ D += DD;
+ E += EE;
+ F += FF;
+ G += GG;
+ H += HH;
+}
+
+/*
+ * From `Performance analysis of MD5' by Joseph D. Touch <touch@isi.edu>
+ */
+
+#if !defined(WORDS_BIGENDIAN) || defined(_CRAY)
+static inline uint32_t
+swap_uint32_t (uint32_t t)
+{
+#define ROL(x,n) ((x)<<(n))|((x)>>(32-(n)))
+ uint32_t temp1, temp2;
+
+ temp1 = cshift(t, 16);
+ temp2 = temp1 >> 8;
+ temp1 &= 0x00ff00ff;
+ temp2 &= 0x00ff00ff;
+ temp1 <<= 8;
+ return temp1 | temp2;
+}
+#endif
+
+struct x32{
+ unsigned int a:32;
+ unsigned int b:32;
+};
+
+void
+SHA256_Update (SHA256_CTX *m, const void *v, size_t len)
+{
+ const unsigned char *p = v;
+ size_t old_sz = m->sz[0];
+ size_t offset;
+
+ m->sz[0] += len * 8;
+ if (m->sz[0] < old_sz)
+ ++m->sz[1];
+ offset = (old_sz / 8) % 64;
+ while(len > 0){
+ size_t l = min(len, 64 - offset);
+ memcpy(m->save + offset, p, l);
+ offset += l;
+ p += l;
+ len -= l;
+ if(offset == 64){
+#if !defined(WORDS_BIGENDIAN) || defined(_CRAY)
+ int i;
+ uint32_t current[16];
+ struct x32 *u = (struct x32*)m->save;
+ for(i = 0; i < 8; i++){
+ current[2*i+0] = swap_uint32_t(u[i].a);
+ current[2*i+1] = swap_uint32_t(u[i].b);
+ }
+ calc(m, current);
+#else
+ calc(m, (uint32_t*)m->save);
+#endif
+ offset = 0;
+ }
+ }
+}
+
+void
+SHA256_Final (void *res, SHA256_CTX *m)
+{
+ unsigned char zeros[72];
+ unsigned offset = (m->sz[0] / 8) % 64;
+ unsigned int dstart = (120 - offset - 1) % 64 + 1;
+
+ *zeros = 0x80;
+ memset (zeros + 1, 0, sizeof(zeros) - 1);
+ zeros[dstart+7] = (m->sz[0] >> 0) & 0xff;
+ zeros[dstart+6] = (m->sz[0] >> 8) & 0xff;
+ zeros[dstart+5] = (m->sz[0] >> 16) & 0xff;
+ zeros[dstart+4] = (m->sz[0] >> 24) & 0xff;
+ zeros[dstart+3] = (m->sz[1] >> 0) & 0xff;
+ zeros[dstart+2] = (m->sz[1] >> 8) & 0xff;
+ zeros[dstart+1] = (m->sz[1] >> 16) & 0xff;
+ zeros[dstart+0] = (m->sz[1] >> 24) & 0xff;
+ SHA256_Update (m, zeros, dstart + 8);
+ {
+ int i;
+ unsigned char *r = (unsigned char*)res;
+
+ for (i = 0; i < 8; ++i) {
+ r[4*i+3] = m->counter[i] & 0xFF;
+ r[4*i+2] = (m->counter[i] >> 8) & 0xFF;
+ r[4*i+1] = (m->counter[i] >> 16) & 0xFF;
+ r[4*i] = (m->counter[i] >> 24) & 0xFF;
+ }
+ }
+}
diff --git a/source4/heimdal/lib/hcrypto/ui.c b/source4/heimdal/lib/hcrypto/ui.c
new file mode 100644
index 0000000000..91abf76371
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/ui.c
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 1997 - 2000, 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+RCSID("$Id$");
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <termios.h>
+#include <roken.h>
+
+#include <ui.h>
+
+static sig_atomic_t intr_flag;
+
+static void
+intr(int sig)
+{
+ intr_flag++;
+}
+
+#ifndef NSIG
+#define NSIG 47
+#endif
+
+static int
+read_string(const char *preprompt, const char *prompt,
+ char *buf, size_t len, int echo)
+{
+ struct sigaction sigs[NSIG];
+ int oksigs[NSIG];
+ struct sigaction sa;
+ FILE *tty;
+ int ret = 0;
+ int of = 0;
+ int i;
+ int c;
+ char *p;
+
+ struct termios t_new, t_old;
+
+ memset(&oksigs, 0, sizeof(oksigs));
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = intr;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ for(i = 1; i < sizeof(sigs) / sizeof(sigs[0]); i++)
+ if (i != SIGALRM)
+ if (sigaction(i, &sa, &sigs[i]) == 0)
+ oksigs[i] = 1;
+
+ if((tty = fopen("/dev/tty", "r")) != NULL)
+ rk_cloexec_file(tty);
+ else
+ tty = stdin;
+
+ fprintf(stderr, "%s%s", preprompt, prompt);
+ fflush(stderr);
+
+ if(echo == 0){
+ tcgetattr(fileno(tty), &t_old);
+ memcpy(&t_new, &t_old, sizeof(t_new));
+ t_new.c_lflag &= ~ECHO;
+ tcsetattr(fileno(tty), TCSANOW, &t_new);
+ }
+ intr_flag = 0;
+ p = buf;
+ while(intr_flag == 0){
+ c = getc(tty);
+ if(c == EOF){
+ if(!ferror(tty))
+ ret = 1;
+ break;
+ }
+ if(c == '\n')
+ break;
+ if(of == 0)
+ *p++ = c;
+ of = (p == buf + len);
+ }
+ if(of)
+ p--;
+ *p = 0;
+
+ if(echo == 0){
+ fprintf(stderr, "\n");
+ tcsetattr(fileno(tty), TCSANOW, &t_old);
+ }
+
+ if(tty != stdin)
+ fclose(tty);
+
+ for(i = 1; i < sizeof(sigs) / sizeof(sigs[0]); i++)
+ if (oksigs[i])
+ sigaction(i, &sigs[i], NULL);
+
+ if(ret)
+ return -3;
+ if(intr_flag)
+ return -2;
+ if(of)
+ return -1;
+ return 0;
+}
+
+int
+UI_UTIL_read_pw_string(char *buf, int length, const char *prompt, int verify)
+{
+ int ret;
+
+ ret = read_string("", prompt, buf, length, 0);
+ if (ret)
+ return ret;
+
+ if (verify) {
+ char *buf2;
+ buf2 = malloc(length);
+ if (buf2 == NULL)
+ return 1;
+
+ ret = read_string("Verify password - ", prompt, buf2, length, 0);
+ if (ret) {
+ free(buf2);
+ return ret;
+ }
+ if (strcmp(buf2, buf) != 0)
+ ret = 1;
+ free(buf2);
+ }
+ return ret;
+}
diff --git a/source4/heimdal/lib/hcrypto/ui.h b/source4/heimdal/lib/hcrypto/ui.h
new file mode 100644
index 0000000000..6b4d6d8c4d
--- /dev/null
+++ b/source4/heimdal/lib/hcrypto/ui.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef _HEIM_UI_H
+#define _HEIM_UI_H 1
+
+/* symbol renaming */
+#define UI_UTIL_read_pw_string hc_UI_UTIL_read_pw_string
+
+int UI_UTIL_read_pw_string(char *, int, const char *, int); /* XXX */
+
+#endif /* _HEIM_UI_H */
+
diff --git a/source4/heimdal/lib/hdb/db.c b/source4/heimdal/lib/hdb/db.c
new file mode 100644
index 0000000000..556833d1c4
--- /dev/null
+++ b/source4/heimdal/lib/hdb/db.c
@@ -0,0 +1,337 @@
+/*
+ * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hdb_locl.h"
+
+RCSID("$Id$");
+
+#if HAVE_DB1
+
+#if defined(HAVE_DB_185_H)
+#include <db_185.h>
+#elif defined(HAVE_DB_H)
+#include <db.h>
+#endif
+
+static krb5_error_code
+DB_close(krb5_context context, HDB *db)
+{
+ DB *d = (DB*)db->hdb_db;
+ (*d->close)(d);
+ return 0;
+}
+
+static krb5_error_code
+DB_destroy(krb5_context context, HDB *db)
+{
+ krb5_error_code ret;
+
+ ret = hdb_clear_master_key (context, db);
+ free(db->hdb_name);
+ free(db);
+ return ret;
+}
+
+static krb5_error_code
+DB_lock(krb5_context context, HDB *db, int operation)
+{
+ DB *d = (DB*)db->hdb_db;
+ int fd = (*d->fd)(d);
+ if(fd < 0) {
+ krb5_set_error_message(context, HDB_ERR_CANT_LOCK_DB,
+ "Can't lock database: %s", db->hdb_name);
+ return HDB_ERR_CANT_LOCK_DB;
+ }
+ return hdb_lock(fd, operation);
+}
+
+static krb5_error_code
+DB_unlock(krb5_context context, HDB *db)
+{
+ DB *d = (DB*)db->hdb_db;
+ int fd = (*d->fd)(d);
+ if(fd < 0) {
+ krb5_set_error_message(context, HDB_ERR_CANT_LOCK_DB,
+ "Can't unlock database: %s", db->hdb_name);
+ return HDB_ERR_CANT_LOCK_DB;
+ }
+ return hdb_unlock(fd);
+}
+
+
+static krb5_error_code
+DB_seq(krb5_context context, HDB *db,
+ unsigned flags, hdb_entry_ex *entry, int flag)
+{
+ DB *d = (DB*)db->hdb_db;
+ DBT key, value;
+ krb5_data key_data, data;
+ int code;
+
+ code = db->hdb_lock(context, db, HDB_RLOCK);
+ if(code == -1) {
+ krb5_set_error_message(context, HDB_ERR_DB_INUSE, "Database %s in use", db->hdb_name);
+ return HDB_ERR_DB_INUSE;
+ }
+ code = (*d->seq)(d, &key, &value, flag);
+ db->hdb_unlock(context, db); /* XXX check value */
+ if(code == -1) {
+ code = errno;
+ krb5_set_error_message(context, code, "Database %s seq error: %s",
+ db->hdb_name, strerror(code));
+ return code;
+ }
+ if(code == 1) {
+ krb5_clear_error_message(context);
+ return HDB_ERR_NOENTRY;
+ }
+
+ key_data.data = key.data;
+ key_data.length = key.size;
+ data.data = value.data;
+ data.length = value.size;
+ memset(entry, 0, sizeof(*entry));
+ if (hdb_value2entry(context, &data, &entry->entry))
+ return DB_seq(context, db, flags, entry, R_NEXT);
+ if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) {
+ code = hdb_unseal_keys (context, db, &entry->entry);
+ if (code)
+ hdb_free_entry (context, entry);
+ }
+ if (code == 0 && entry->entry.principal == NULL) {
+ entry->entry.principal = malloc(sizeof(*entry->entry.principal));
+ if (entry->entry.principal == NULL) {
+ code = ENOMEM;
+ krb5_set_error_message(context, code, "malloc: out of memory");
+ hdb_free_entry (context, entry);
+ } else {
+ hdb_key2principal(context, &key_data, entry->entry.principal);
+ }
+ }
+ return code;
+}
+
+
+static krb5_error_code
+DB_firstkey(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry)
+{
+ return DB_seq(context, db, flags, entry, R_FIRST);
+}
+
+
+static krb5_error_code
+DB_nextkey(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry)
+{
+ return DB_seq(context, db, flags, entry, R_NEXT);
+}
+
+static krb5_error_code
+DB_rename(krb5_context context, HDB *db, const char *new_name)
+{
+ int ret;
+ char *old, *new;
+
+ asprintf(&old, "%s.db", db->hdb_name);
+ asprintf(&new, "%s.db", new_name);
+ ret = rename(old, new);
+ free(old);
+ free(new);
+ if(ret)
+ return errno;
+
+ free(db->hdb_name);
+ db->hdb_name = strdup(new_name);
+ return 0;
+}
+
+static krb5_error_code
+DB__get(krb5_context context, HDB *db, krb5_data key, krb5_data *reply)
+{
+ DB *d = (DB*)db->hdb_db;
+ DBT k, v;
+ int code;
+
+ k.data = key.data;
+ k.size = key.length;
+ code = db->hdb_lock(context, db, HDB_RLOCK);
+ if(code)
+ return code;
+ code = (*d->get)(d, &k, &v, 0);
+ db->hdb_unlock(context, db);
+ if(code < 0) {
+ code = errno;
+ krb5_set_error_message(context, code, "Database %s get error: %s",
+ db->hdb_name, strerror(code));
+ return code;
+ }
+ if(code == 1) {
+ krb5_clear_error_message(context);
+ return HDB_ERR_NOENTRY;
+ }
+
+ krb5_data_copy(reply, v.data, v.size);
+ return 0;
+}
+
+static krb5_error_code
+DB__put(krb5_context context, HDB *db, int replace,
+ krb5_data key, krb5_data value)
+{
+ DB *d = (DB*)db->hdb_db;
+ DBT k, v;
+ int code;
+
+ k.data = key.data;
+ k.size = key.length;
+ v.data = value.data;
+ v.size = value.length;
+ code = db->hdb_lock(context, db, HDB_WLOCK);
+ if(code)
+ return code;
+ code = (*d->put)(d, &k, &v, replace ? 0 : R_NOOVERWRITE);
+ db->hdb_unlock(context, db);
+ if(code < 0) {
+ code = errno;
+ krb5_set_error_message(context, code, "Database %s put error: %s",
+ db->hdb_name, strerror(code));
+ return code;
+ }
+ if(code == 1) {
+ krb5_clear_error_message(context);
+ return HDB_ERR_EXISTS;
+ }
+ return 0;
+}
+
+static krb5_error_code
+DB__del(krb5_context context, HDB *db, krb5_data key)
+{
+ DB *d = (DB*)db->hdb_db;
+ DBT k;
+ krb5_error_code code;
+ k.data = key.data;
+ k.size = key.length;
+ code = db->hdb_lock(context, db, HDB_WLOCK);
+ if(code)
+ return code;
+ code = (*d->del)(d, &k, 0);
+ db->hdb_unlock(context, db);
+ if(code == 1) {
+ code = errno;
+ krb5_set_error_message(context, code, "Database %s put error: %s",
+ db->hdb_name, strerror(code));
+ return code;
+ }
+ if(code < 0)
+ return errno;
+ return 0;
+}
+
+static krb5_error_code
+DB_open(krb5_context context, HDB *db, int flags, mode_t mode)
+{
+ char *fn;
+ krb5_error_code ret;
+
+ asprintf(&fn, "%s.db", db->hdb_name);
+ if (fn == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ db->hdb_db = dbopen(fn, flags, mode, DB_BTREE, NULL);
+ free(fn);
+ /* try to open without .db extension */
+ if(db->hdb_db == NULL && errno == ENOENT)
+ db->hdb_db = dbopen(db->hdb_name, flags, mode, DB_BTREE, NULL);
+ if(db->hdb_db == NULL) {
+ ret = errno;
+ krb5_set_error_message(context, ret, "dbopen (%s): %s",
+ db->hdb_name, strerror(ret));
+ return ret;
+ }
+ if((flags & O_ACCMODE) == O_RDONLY)
+ ret = hdb_check_db_format(context, db);
+ else
+ ret = hdb_init_db(context, db);
+ if(ret == HDB_ERR_NOENTRY) {
+ krb5_clear_error_message(context);
+ return 0;
+ }
+ if (ret) {
+ DB_close(context, db);
+ krb5_set_error_message(context, ret, "hdb_open: failed %s database %s",
+ (flags & O_ACCMODE) == O_RDONLY ?
+ "checking format of" : "initialize",
+ db->hdb_name);
+ }
+ return ret;
+}
+
+krb5_error_code
+hdb_db_create(krb5_context context, HDB **db,
+ const char *filename)
+{
+ *db = calloc(1, sizeof(**db));
+ if (*db == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+
+ (*db)->hdb_db = NULL;
+ (*db)->hdb_name = strdup(filename);
+ if ((*db)->hdb_name == NULL) {
+ free(*db);
+ *db = NULL;
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ (*db)->hdb_master_key_set = 0;
+ (*db)->hdb_openp = 0;
+ (*db)->hdb_open = DB_open;
+ (*db)->hdb_close = DB_close;
+ (*db)->hdb_fetch = _hdb_fetch;
+ (*db)->hdb_store = _hdb_store;
+ (*db)->hdb_remove = _hdb_remove;
+ (*db)->hdb_firstkey = DB_firstkey;
+ (*db)->hdb_nextkey= DB_nextkey;
+ (*db)->hdb_lock = DB_lock;
+ (*db)->hdb_unlock = DB_unlock;
+ (*db)->hdb_rename = DB_rename;
+ (*db)->hdb__get = DB__get;
+ (*db)->hdb__put = DB__put;
+ (*db)->hdb__del = DB__del;
+ (*db)->hdb_destroy = DB_destroy;
+ return 0;
+}
+
+#endif /* HAVE_DB1 */
diff --git a/source4/heimdal/lib/hdb/dbinfo.c b/source4/heimdal/lib/hdb/dbinfo.c
new file mode 100644
index 0000000000..2121577bb1
--- /dev/null
+++ b/source4/heimdal/lib/hdb/dbinfo.c
@@ -0,0 +1,266 @@
+/*
+ * Copyright (c) 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hdb_locl.h"
+
+RCSID("$Id$");
+
+struct hdb_dbinfo {
+ char *label;
+ char *realm;
+ char *dbname;
+ char *mkey_file;
+ char *acl_file;
+ char *log_file;
+ const krb5_config_binding *binding;
+ struct hdb_dbinfo *next;
+};
+
+static int
+get_dbinfo(krb5_context context,
+ const krb5_config_binding *db_binding,
+ const char *label,
+ struct hdb_dbinfo **db)
+{
+ struct hdb_dbinfo *di;
+ const char *p;
+
+ *db = NULL;
+
+ p = krb5_config_get_string(context, db_binding, "dbname", NULL);
+ if(p == NULL)
+ return 0;
+
+ di = calloc(1, sizeof(*di));
+ if (di == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ di->label = strdup(label);
+ di->dbname = strdup(p);
+
+ p = krb5_config_get_string(context, db_binding, "realm", NULL);
+ if(p)
+ di->realm = strdup(p);
+ p = krb5_config_get_string(context, db_binding, "mkey_file", NULL);
+ if(p)
+ di->mkey_file = strdup(p);
+ p = krb5_config_get_string(context, db_binding, "acl_file", NULL);
+ if(p)
+ di->acl_file = strdup(p);
+ p = krb5_config_get_string(context, db_binding, "log_file", NULL);
+ if(p)
+ di->log_file = strdup(p);
+
+ di->binding = db_binding;
+
+ *db = di;
+ return 0;
+}
+
+
+int
+hdb_get_dbinfo(krb5_context context, struct hdb_dbinfo **dbp)
+{
+ const krb5_config_binding *db_binding;
+ struct hdb_dbinfo *di, **dt, *databases;
+ const char *default_dbname = HDB_DEFAULT_DB;
+ const char *default_mkey = HDB_DB_DIR "/m-key";
+ const char *default_acl = HDB_DB_DIR "/kadmind.acl";
+ const char *p;
+ int ret;
+
+ *dbp = NULL;
+ dt = NULL;
+ databases = NULL;
+
+ db_binding = krb5_config_get(context, NULL, krb5_config_list,
+ "kdc",
+ "database",
+ NULL);
+ if (db_binding) {
+
+ ret = get_dbinfo(context, db_binding, "default", &di);
+ if (ret == 0 && di) {
+ databases = di;
+ dt = &di->next;
+ }
+
+ for ( ; db_binding != NULL; db_binding = db_binding->next) {
+
+ if (db_binding->type != krb5_config_list)
+ continue;
+
+ ret = get_dbinfo(context, db_binding->u.list,
+ db_binding->name, &di);
+ if (ret)
+ krb5_err(context, 1, ret, "failed getting realm");
+
+ if (di == NULL)
+ continue;
+
+ if (dt)
+ *dt = di;
+ else
+ databases = di;
+ dt = &di->next;
+
+ }
+ }
+
+ if(databases == NULL) {
+ /* if there are none specified, create one and use defaults */
+ di = calloc(1, sizeof(*di));
+ databases = di;
+ di->label = strdup("default");
+ }
+
+ for(di = databases; di; di = di->next) {
+ if(di->dbname == NULL) {
+ di->dbname = strdup(default_dbname);
+ if (di->mkey_file == NULL)
+ di->mkey_file = strdup(default_mkey);
+ }
+ if(di->mkey_file == NULL) {
+ p = strrchr(di->dbname, '.');
+ if(p == NULL || strchr(p, '/') != NULL)
+ /* final pathname component does not contain a . */
+ asprintf(&di->mkey_file, "%s.mkey", di->dbname);
+ else
+ /* the filename is something.else, replace .else with
+ .mkey */
+ asprintf(&di->mkey_file, "%.*s.mkey",
+ (int)(p - di->dbname), di->dbname);
+ }
+ if(di->acl_file == NULL)
+ di->acl_file = strdup(default_acl);
+ }
+ *dbp = databases;
+ return 0;
+}
+
+
+struct hdb_dbinfo *
+hdb_dbinfo_get_next(struct hdb_dbinfo *dbp, struct hdb_dbinfo *dbprevp)
+{
+ if (dbprevp == NULL)
+ return dbp;
+ else
+ return dbprevp->next;
+}
+
+const char *
+hdb_dbinfo_get_label(krb5_context context, struct hdb_dbinfo *dbp)
+{
+ return dbp->label;
+}
+
+const char *
+hdb_dbinfo_get_realm(krb5_context context, struct hdb_dbinfo *dbp)
+{
+ return dbp->realm;
+}
+
+const char *
+hdb_dbinfo_get_dbname(krb5_context context, struct hdb_dbinfo *dbp)
+{
+ return dbp->dbname;
+}
+
+const char *
+hdb_dbinfo_get_mkey_file(krb5_context context, struct hdb_dbinfo *dbp)
+{
+ return dbp->mkey_file;
+}
+
+const char *
+hdb_dbinfo_get_acl_file(krb5_context context, struct hdb_dbinfo *dbp)
+{
+ return dbp->acl_file;
+}
+
+const char *
+hdb_dbinfo_get_log_file(krb5_context context, struct hdb_dbinfo *dbp)
+{
+ return dbp->log_file;
+}
+
+const krb5_config_binding *
+hdb_dbinfo_get_binding(krb5_context context, struct hdb_dbinfo *dbp)
+{
+ return dbp->binding;
+}
+
+void
+hdb_free_dbinfo(krb5_context context, struct hdb_dbinfo **dbp)
+{
+ struct hdb_dbinfo *di, *ndi;
+
+ for(di = *dbp; di != NULL; di = ndi) {
+ ndi = di->next;
+ free (di->realm);
+ free (di->dbname);
+ if (di->mkey_file)
+ free (di->mkey_file);
+ free(di);
+ }
+ *dbp = NULL;
+}
+
+/**
+ * Return the directory where the hdb database resides.
+ *
+ * @param context Kerberos 5 context.
+ *
+ * @return string pointing to directory.
+ */
+
+const char *
+hdb_db_dir(krb5_context context)
+{
+ return HDB_DB_DIR;
+}
+
+/**
+ * Return the default hdb database resides.
+ *
+ * @param context Kerberos 5 context.
+ *
+ * @return string pointing to directory.
+ */
+
+const char *
+hdb_default_db(krb5_context context)
+{
+ return HDB_DEFAULT_DB;
+}
diff --git a/source4/heimdal/lib/hdb/ext.c b/source4/heimdal/lib/hdb/ext.c
new file mode 100644
index 0000000000..f792b85c09
--- /dev/null
+++ b/source4/heimdal/lib/hdb/ext.c
@@ -0,0 +1,421 @@
+/*
+ * Copyright (c) 2004 - 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hdb_locl.h"
+#include <der.h>
+
+RCSID("$Id$");
+
+krb5_error_code
+hdb_entry_check_mandatory(krb5_context context, const hdb_entry *ent)
+{
+ int i;
+
+ if (ent->extensions == NULL)
+ return 0;
+
+ /*
+ * check for unknown extensions and if they where tagged mandatory
+ */
+
+ for (i = 0; i < ent->extensions->len; i++) {
+ if (ent->extensions->val[i].data.element !=
+ choice_HDB_extension_data_asn1_ellipsis)
+ continue;
+ if (ent->extensions->val[i].mandatory) {
+ krb5_set_error_message(context, HDB_ERR_MANDATORY_OPTION,
+ "Principal have unknown "
+ "mandatory extension");
+ return HDB_ERR_MANDATORY_OPTION;
+ }
+ }
+ return 0;
+}
+
+HDB_extension *
+hdb_find_extension(const hdb_entry *entry, int type)
+{
+ int i;
+
+ if (entry->extensions == NULL)
+ return NULL;
+
+ for (i = 0; i < entry->extensions->len; i++)
+ if (entry->extensions->val[i].data.element == type)
+ return &entry->extensions->val[i];
+ return NULL;
+}
+
+/*
+ * Replace the extension `ext' in `entry'. Make a copy of the
+ * extension, so the caller must still free `ext' on both success and
+ * failure. Returns 0 or error code.
+ */
+
+krb5_error_code
+hdb_replace_extension(krb5_context context,
+ hdb_entry *entry,
+ const HDB_extension *ext)
+{
+ HDB_extension *ext2;
+ HDB_extension *es;
+ int ret;
+
+ ext2 = NULL;
+
+ if (entry->extensions == NULL) {
+ entry->extensions = calloc(1, sizeof(*entry->extensions));
+ if (entry->extensions == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ } else if (ext->data.element != choice_HDB_extension_data_asn1_ellipsis) {
+ ext2 = hdb_find_extension(entry, ext->data.element);
+ } else {
+ /*
+ * This is an unknown extention, and we are asked to replace a
+ * possible entry in `entry' that is of the same type. This
+ * might seem impossible, but ASN.1 CHOICE comes to our
+ * rescue. The first tag in each branch in the CHOICE is
+ * unique, so just find the element in the list that have the
+ * same tag was we are putting into the list.
+ */
+ Der_class replace_class, list_class;
+ Der_type replace_type, list_type;
+ unsigned int replace_tag, list_tag;
+ size_t size;
+ int i;
+
+ ret = der_get_tag(ext->data.u.asn1_ellipsis.data,
+ ext->data.u.asn1_ellipsis.length,
+ &replace_class, &replace_type, &replace_tag,
+ &size);
+ if (ret) {
+ krb5_set_error_message(context, ret, "hdb: failed to decode "
+ "replacement hdb extention");
+ return ret;
+ }
+
+ for (i = 0; i < entry->extensions->len; i++) {
+ HDB_extension *ext3 = &entry->extensions->val[i];
+
+ if (ext3->data.element != choice_HDB_extension_data_asn1_ellipsis)
+ continue;
+
+ ret = der_get_tag(ext3->data.u.asn1_ellipsis.data,
+ ext3->data.u.asn1_ellipsis.length,
+ &list_class, &list_type, &list_tag,
+ &size);
+ if (ret) {
+ krb5_set_error_message(context, ret, "hdb: failed to decode "
+ "present hdb extention");
+ return ret;
+ }
+
+ if (MAKE_TAG(replace_class,replace_type,replace_type) ==
+ MAKE_TAG(list_class,list_type,list_type)) {
+ ext2 = ext3;
+ break;
+ }
+ }
+ }
+
+ if (ext2) {
+ free_HDB_extension(ext2);
+ ret = copy_HDB_extension(ext, ext2);
+ if (ret)
+ krb5_set_error_message(context, ret, "hdb: failed to copy replacement "
+ "hdb extention");
+ return ret;
+ }
+
+ es = realloc(entry->extensions->val,
+ (entry->extensions->len+1)*sizeof(entry->extensions->val[0]));
+ if (es == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ entry->extensions->val = es;
+
+ ret = copy_HDB_extension(ext,
+ &entry->extensions->val[entry->extensions->len]);
+ if (ret == 0)
+ entry->extensions->len++;
+ else
+ krb5_set_error_message(context, ret, "hdb: failed to copy new extension");
+
+ return ret;
+}
+
+krb5_error_code
+hdb_clear_extension(krb5_context context,
+ hdb_entry *entry,
+ int type)
+{
+ int i;
+
+ if (entry->extensions == NULL)
+ return 0;
+
+ for (i = 0; i < entry->extensions->len; i++) {
+ if (entry->extensions->val[i].data.element == type) {
+ free_HDB_extension(&entry->extensions->val[i]);
+ memmove(&entry->extensions->val[i],
+ &entry->extensions->val[i + 1],
+ sizeof(entry->extensions->val[i]) * (entry->extensions->len - i - 1));
+ entry->extensions->len--;
+ }
+ }
+ if (entry->extensions->len == 0) {
+ free(entry->extensions->val);
+ free(entry->extensions);
+ entry->extensions = NULL;
+ }
+
+ return 0;
+}
+
+
+krb5_error_code
+hdb_entry_get_pkinit_acl(const hdb_entry *entry, const HDB_Ext_PKINIT_acl **a)
+{
+ const HDB_extension *ext;
+
+ ext = hdb_find_extension(entry, choice_HDB_extension_data_pkinit_acl);
+ if (ext)
+ *a = &ext->data.u.pkinit_acl;
+ else
+ *a = NULL;
+
+ return 0;
+}
+
+krb5_error_code
+hdb_entry_get_pkinit_hash(const hdb_entry *entry, const HDB_Ext_PKINIT_hash **a)
+{
+ const HDB_extension *ext;
+
+ ext = hdb_find_extension(entry, choice_HDB_extension_data_pkinit_cert_hash);
+ if (ext)
+ *a = &ext->data.u.pkinit_cert_hash;
+ else
+ *a = NULL;
+
+ return 0;
+}
+
+krb5_error_code
+hdb_entry_get_pw_change_time(const hdb_entry *entry, time_t *t)
+{
+ const HDB_extension *ext;
+
+ ext = hdb_find_extension(entry, choice_HDB_extension_data_last_pw_change);
+ if (ext)
+ *t = ext->data.u.last_pw_change;
+ else
+ *t = 0;
+
+ return 0;
+}
+
+krb5_error_code
+hdb_entry_set_pw_change_time(krb5_context context,
+ hdb_entry *entry,
+ time_t t)
+{
+ HDB_extension ext;
+
+ ext.mandatory = FALSE;
+ ext.data.element = choice_HDB_extension_data_last_pw_change;
+ if (t == 0)
+ t = time(NULL);
+ ext.data.u.last_pw_change = t;
+
+ return hdb_replace_extension(context, entry, &ext);
+}
+
+int
+hdb_entry_get_password(krb5_context context, HDB *db,
+ const hdb_entry *entry, char **p)
+{
+ HDB_extension *ext;
+ char *str;
+ int ret;
+
+ ext = hdb_find_extension(entry, choice_HDB_extension_data_password);
+ if (ext) {
+ heim_utf8_string str;
+ heim_octet_string pw;
+
+ if (db->hdb_master_key_set && ext->data.u.password.mkvno) {
+ hdb_master_key key;
+
+ key = _hdb_find_master_key(ext->data.u.password.mkvno,
+ db->hdb_master_key);
+
+ if (key == NULL) {
+ krb5_set_error_message(context, HDB_ERR_NO_MKEY,
+ "master key %d missing",
+ *ext->data.u.password.mkvno);
+ return HDB_ERR_NO_MKEY;
+ }
+
+ ret = _hdb_mkey_decrypt(context, key, HDB_KU_MKEY,
+ ext->data.u.password.password.data,
+ ext->data.u.password.password.length,
+ &pw);
+ } else {
+ ret = der_copy_octet_string(&ext->data.u.password.password, &pw);
+ }
+ if (ret) {
+ krb5_clear_error_message(context);
+ return ret;
+ }
+
+ str = pw.data;
+ if (str[pw.length - 1] != '\0') {
+ krb5_set_error_message(context, EINVAL, "password malformated");
+ return EINVAL;
+ }
+
+ *p = strdup(str);
+
+ der_free_octet_string(&pw);
+ if (*p == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ return 0;
+ }
+
+ ret = krb5_unparse_name(context, entry->principal, &str);
+ if (ret == 0) {
+ krb5_set_error_message(context, ENOENT, "no password attributefor %s", str);
+ free(str);
+ } else
+ krb5_clear_error_message(context);
+
+ return ENOENT;
+}
+
+int
+hdb_entry_set_password(krb5_context context, HDB *db,
+ hdb_entry *entry, const char *p)
+{
+ HDB_extension ext;
+ hdb_master_key key;
+ int ret;
+
+ ext.mandatory = FALSE;
+ ext.data.element = choice_HDB_extension_data_password;
+
+ if (db->hdb_master_key_set) {
+
+ key = _hdb_find_master_key(NULL, db->hdb_master_key);
+ if (key == NULL) {
+ krb5_set_error_message(context, HDB_ERR_NO_MKEY,
+ "hdb_entry_set_password: "
+ "failed to find masterkey");
+ return HDB_ERR_NO_MKEY;
+ }
+
+ ret = _hdb_mkey_encrypt(context, key, HDB_KU_MKEY,
+ p, strlen(p) + 1,
+ &ext.data.u.password.password);
+ if (ret)
+ return ret;
+
+ ext.data.u.password.mkvno =
+ malloc(sizeof(*ext.data.u.password.mkvno));
+ if (ext.data.u.password.mkvno == NULL) {
+ free_HDB_extension(&ext);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ *ext.data.u.password.mkvno = _hdb_mkey_version(key);
+
+ } else {
+ ext.data.u.password.mkvno = NULL;
+
+ ret = krb5_data_copy(&ext.data.u.password.password,
+ p, strlen(p) + 1);
+ if (ret) {
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ free_HDB_extension(&ext);
+ return ret;
+ }
+ }
+
+ ret = hdb_replace_extension(context, entry, &ext);
+
+ free_HDB_extension(&ext);
+
+ return ret;
+}
+
+int
+hdb_entry_clear_password(krb5_context context, hdb_entry *entry)
+{
+ return hdb_clear_extension(context, entry,
+ choice_HDB_extension_data_password);
+}
+
+krb5_error_code
+hdb_entry_get_ConstrainedDelegACL(const hdb_entry *entry,
+ const HDB_Ext_Constrained_delegation_acl **a)
+{
+ const HDB_extension *ext;
+
+ ext = hdb_find_extension(entry,
+ choice_HDB_extension_data_allowed_to_delegate_to);
+ if (ext)
+ *a = &ext->data.u.allowed_to_delegate_to;
+ else
+ *a = NULL;
+
+ return 0;
+}
+
+krb5_error_code
+hdb_entry_get_aliases(const hdb_entry *entry, const HDB_Ext_Aliases **a)
+{
+ const HDB_extension *ext;
+
+ ext = hdb_find_extension(entry, choice_HDB_extension_data_aliases);
+ if (ext)
+ *a = &ext->data.u.aliases;
+ else
+ *a = NULL;
+
+ return 0;
+}
diff --git a/source4/heimdal/lib/hdb/hdb.asn1 b/source4/heimdal/lib/hdb/hdb.asn1
new file mode 100644
index 0000000000..5cddf8f1d0
--- /dev/null
+++ b/source4/heimdal/lib/hdb/hdb.asn1
@@ -0,0 +1,127 @@
+-- $Id$
+HDB DEFINITIONS ::=
+BEGIN
+
+IMPORTS EncryptionKey, KerberosTime, Principal FROM krb5;
+
+HDB_DB_FORMAT INTEGER ::= 2 -- format of database,
+ -- update when making changes
+
+-- these must have the same value as the pa-* counterparts
+hdb-pw-salt INTEGER ::= 3
+hdb-afs3-salt INTEGER ::= 10
+
+Salt ::= SEQUENCE {
+ type[0] INTEGER (0..4294967295),
+ salt[1] OCTET STRING
+}
+
+Key ::= SEQUENCE {
+ mkvno[0] INTEGER (0..4294967295) OPTIONAL, -- master key version number
+ key[1] EncryptionKey,
+ salt[2] Salt OPTIONAL
+}
+
+Event ::= SEQUENCE {
+ time[0] KerberosTime,
+ principal[1] Principal OPTIONAL
+}
+
+HDBFlags ::= BIT STRING {
+ initial(0), -- require as-req
+ forwardable(1), -- may issue forwardable
+ proxiable(2), -- may issue proxiable
+ renewable(3), -- may issue renewable
+ postdate(4), -- may issue postdatable
+ server(5), -- may be server
+ client(6), -- may be client
+ invalid(7), -- entry is invalid
+ require-preauth(8), -- must use preauth
+ change-pw(9), -- change password service
+ require-hwauth(10), -- must use hwauth
+ ok-as-delegate(11), -- as in TicketFlags
+ user-to-user(12), -- may use user-to-user auth
+ immutable(13), -- may not be deleted
+ trusted-for-delegation(14), -- Trusted to print forwardabled tickets
+ allow-kerberos4(15), -- Allow Kerberos 4 requests
+ allow-digest(16) -- Allow digest requests
+}
+
+GENERATION ::= SEQUENCE {
+ time[0] KerberosTime, -- timestamp
+ usec[1] INTEGER (0..4294967295), -- microseconds
+ gen[2] INTEGER (0..4294967295) -- generation number
+}
+
+HDB-Ext-PKINIT-acl ::= SEQUENCE OF SEQUENCE {
+ subject[0] UTF8String,
+ issuer[1] UTF8String OPTIONAL,
+ anchor[2] UTF8String OPTIONAL
+}
+
+HDB-Ext-PKINIT-hash ::= SEQUENCE OF SEQUENCE {
+ digest-type[0] OBJECT IDENTIFIER,
+ digest[1] OCTET STRING
+}
+
+HDB-Ext-Constrained-delegation-acl ::= SEQUENCE OF Principal
+
+-- hdb-ext-referrals ::= PA-SERVER-REFERRAL-DATA
+
+HDB-Ext-Lan-Manager-OWF ::= OCTET STRING
+
+HDB-Ext-Password ::= SEQUENCE {
+ mkvno[0] INTEGER (0..4294967295) OPTIONAL, -- master key version number
+ password OCTET STRING
+}
+
+HDB-Ext-Aliases ::= SEQUENCE {
+ case-insensitive[0] BOOLEAN, -- case insensitive name allowed
+ aliases[1] SEQUENCE OF Principal -- all names, inc primary
+}
+
+
+HDB-extension ::= SEQUENCE {
+ mandatory[0] BOOLEAN, -- kdc MUST understand this extension,
+ -- if not the whole entry must
+ -- be rejected
+ data[1] CHOICE {
+ pkinit-acl[0] HDB-Ext-PKINIT-acl,
+ pkinit-cert-hash[1] HDB-Ext-PKINIT-hash,
+ allowed-to-delegate-to[2] HDB-Ext-Constrained-delegation-acl,
+-- referral-info[3] HDB-Ext-Referrals,
+ lm-owf[4] HDB-Ext-Lan-Manager-OWF,
+ password[5] HDB-Ext-Password,
+ aliases[6] HDB-Ext-Aliases,
+ last-pw-change[7] KerberosTime,
+ ...
+ },
+ ...
+}
+
+HDB-extensions ::= SEQUENCE OF HDB-extension
+
+
+hdb_entry ::= SEQUENCE {
+ principal[0] Principal OPTIONAL, -- this is optional only
+ -- for compatibility with libkrb5
+ kvno[1] INTEGER (0..4294967295),
+ keys[2] SEQUENCE OF Key,
+ created-by[3] Event,
+ modified-by[4] Event OPTIONAL,
+ valid-start[5] KerberosTime OPTIONAL,
+ valid-end[6] KerberosTime OPTIONAL,
+ pw-end[7] KerberosTime OPTIONAL,
+ max-life[8] INTEGER (0..4294967295) OPTIONAL,
+ max-renew[9] INTEGER (0..4294967295) OPTIONAL,
+ flags[10] HDBFlags,
+ etypes[11] SEQUENCE OF INTEGER (0..4294967295) OPTIONAL,
+ generation[12] GENERATION OPTIONAL,
+ extensions[13] HDB-extensions OPTIONAL
+}
+
+hdb_entry_alias ::= [APPLICATION 0] SEQUENCE {
+ principal[0] Principal OPTIONAL
+}
+
+END
diff --git a/source4/heimdal/lib/hdb/hdb.c b/source4/heimdal/lib/hdb/hdb.c
new file mode 100644
index 0000000000..ad2c35a43a
--- /dev/null
+++ b/source4/heimdal/lib/hdb/hdb.c
@@ -0,0 +1,431 @@
+/*
+ * Copyright (c) 1997 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "krb5.h"
+#include "krb5_locl.h"
+#include "hdb_locl.h"
+RCSID("$Id$");
+
+#ifdef HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+static struct hdb_method methods[] = {
+#if HAVE_DB1 || HAVE_DB3
+ {HDB_INTERFACE_VERSION, "db:", hdb_db_create},
+#endif
+#if HAVE_NDBM
+ {HDB_INTERFACE_VERSION, "ndbm:", hdb_ndbm_create},
+#endif
+#if defined(OPENLDAP) && !defined(OPENLDAP_MODULE)
+ {HDB_INTERFACE_VERSION, "ldap:", hdb_ldap_create},
+ {HDB_INTERFACE_VERSION, "ldapi:", hdb_ldapi_create},
+#endif
+ {0, NULL, NULL}
+};
+
+#if HAVE_DB1 || HAVE_DB3
+static struct hdb_method dbmetod = {"", hdb_db_create };
+#elif defined(HAVE_NDBM)
+static struct hdb_method dbmetod = {"", hdb_ndbm_create };
+#endif
+
+
+krb5_error_code
+hdb_next_enctype2key(krb5_context context,
+ const hdb_entry *e,
+ krb5_enctype enctype,
+ Key **key)
+{
+ Key *k;
+
+ for (k = *key ? (*key) + 1 : e->keys.val;
+ k < e->keys.val + e->keys.len;
+ k++)
+ {
+ if(k->key.keytype == enctype){
+ *key = k;
+ return 0;
+ }
+ }
+ krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
+ "No next enctype %d for hdb-entry",
+ (int)enctype);
+ return KRB5_PROG_ETYPE_NOSUPP; /* XXX */
+}
+
+krb5_error_code
+hdb_enctype2key(krb5_context context,
+ hdb_entry *e,
+ krb5_enctype enctype,
+ Key **key)
+{
+ *key = NULL;
+ return hdb_next_enctype2key(context, e, enctype, key);
+}
+
+void
+hdb_free_key(Key *key)
+{
+ memset(key->key.keyvalue.data,
+ 0,
+ key->key.keyvalue.length);
+ free_Key(key);
+ free(key);
+}
+
+
+krb5_error_code
+hdb_lock(int fd, int operation)
+{
+ int i, code = 0;
+
+ for(i = 0; i < 3; i++){
+ code = flock(fd, (operation == HDB_RLOCK ? LOCK_SH : LOCK_EX) | LOCK_NB);
+ if(code == 0 || errno != EWOULDBLOCK)
+ break;
+ sleep(1);
+ }
+ if(code == 0)
+ return 0;
+ if(errno == EWOULDBLOCK)
+ return HDB_ERR_DB_INUSE;
+ return HDB_ERR_CANT_LOCK_DB;
+}
+
+krb5_error_code
+hdb_unlock(int fd)
+{
+ int code;
+ code = flock(fd, LOCK_UN);
+ if(code)
+ return 4711 /* XXX */;
+ return 0;
+}
+
+void
+hdb_free_entry(krb5_context context, hdb_entry_ex *ent)
+{
+ int i;
+
+ if (ent->free_entry)
+ (*ent->free_entry)(context, ent);
+
+ for(i = 0; i < ent->entry.keys.len; ++i) {
+ Key *k = &ent->entry.keys.val[i];
+
+ memset (k->key.keyvalue.data, 0, k->key.keyvalue.length);
+ }
+ free_hdb_entry(&ent->entry);
+}
+
+krb5_error_code
+hdb_foreach(krb5_context context,
+ HDB *db,
+ unsigned flags,
+ hdb_foreach_func_t func,
+ void *data)
+{
+ krb5_error_code ret;
+ hdb_entry_ex entry;
+ ret = db->hdb_firstkey(context, db, flags, &entry);
+ if (ret == 0)
+ krb5_clear_error_message(context);
+ while(ret == 0){
+ ret = (*func)(context, db, &entry, data);
+ hdb_free_entry(context, &entry);
+ if(ret == 0)
+ ret = db->hdb_nextkey(context, db, flags, &entry);
+ }
+ if(ret == HDB_ERR_NOENTRY)
+ ret = 0;
+ return ret;
+}
+
+krb5_error_code
+hdb_check_db_format(krb5_context context, HDB *db)
+{
+ krb5_data tag;
+ krb5_data version;
+ krb5_error_code ret, ret2;
+ unsigned ver;
+ int foo;
+
+ ret = db->hdb_lock(context, db, HDB_RLOCK);
+ if (ret)
+ return ret;
+
+ tag.data = HDB_DB_FORMAT_ENTRY;
+ tag.length = strlen(tag.data);
+ ret = (*db->hdb__get)(context, db, tag, &version);
+ ret2 = db->hdb_unlock(context, db);
+ if(ret)
+ return ret;
+ if (ret2)
+ return ret2;
+ foo = sscanf(version.data, "%u", &ver);
+ krb5_data_free (&version);
+ if (foo != 1)
+ return HDB_ERR_BADVERSION;
+ if(ver != HDB_DB_FORMAT)
+ return HDB_ERR_BADVERSION;
+ return 0;
+}
+
+krb5_error_code
+hdb_init_db(krb5_context context, HDB *db)
+{
+ krb5_error_code ret, ret2;
+ krb5_data tag;
+ krb5_data version;
+ char ver[32];
+
+ ret = hdb_check_db_format(context, db);
+ if(ret != HDB_ERR_NOENTRY)
+ return ret;
+
+ ret = db->hdb_lock(context, db, HDB_WLOCK);
+ if (ret)
+ return ret;
+
+ tag.data = HDB_DB_FORMAT_ENTRY;
+ tag.length = strlen(tag.data);
+ snprintf(ver, sizeof(ver), "%u", HDB_DB_FORMAT);
+ version.data = ver;
+ version.length = strlen(version.data) + 1; /* zero terminated */
+ ret = (*db->hdb__put)(context, db, 0, tag, version);
+ ret2 = db->hdb_unlock(context, db);
+ if (ret) {
+ if (ret2)
+ krb5_clear_error_message(context);
+ return ret;
+ }
+ return ret2;
+}
+
+#ifdef HAVE_DLOPEN
+
+ /*
+ * Load a dynamic backend from /usr/heimdal/lib/hdb_NAME.so,
+ * looking for the hdb_NAME_create symbol.
+ */
+
+static const struct hdb_method *
+find_dynamic_method (krb5_context context,
+ const char *filename,
+ const char **rest)
+{
+ static struct hdb_method method;
+ struct hdb_so_method *mso;
+ char *prefix, *path, *symbol;
+ const char *p;
+ void *dl;
+ size_t len;
+
+ p = strchr(filename, ':');
+
+ /* if no prefix, don't know what module to load, just ignore it */
+ if (p == NULL)
+ return NULL;
+
+ len = p - filename;
+ *rest = filename + len + 1;
+
+ prefix = strndup(filename, len);
+ if (prefix == NULL)
+ krb5_errx(context, 1, "out of memory");
+
+ if (asprintf(&path, LIBDIR "/hdb_%s.so", prefix) == -1)
+ krb5_errx(context, 1, "out of memory");
+
+#ifndef RTLD_NOW
+#define RTLD_NOW 0
+#endif
+#ifndef RTLD_GLOBAL
+#define RTLD_GLOBAL 0
+#endif
+
+ dl = dlopen(path, RTLD_NOW | RTLD_GLOBAL);
+ if (dl == NULL) {
+ krb5_warnx(context, "error trying to load dynamic module %s: %s\n",
+ path, dlerror());
+ free(prefix);
+ free(path);
+ return NULL;
+ }
+
+ if (asprintf(&symbol, "hdb_%s_interface", prefix) == -1)
+ krb5_errx(context, 1, "out of memory");
+
+ mso = dlsym(dl, symbol);
+ if (mso == NULL) {
+ krb5_warnx(context, "error finding symbol %s in %s: %s\n",
+ symbol, path, dlerror());
+ dlclose(dl);
+ free(symbol);
+ free(prefix);
+ free(path);
+ return NULL;
+ }
+ free(path);
+ free(symbol);
+
+ if (mso->version != HDB_INTERFACE_VERSION) {
+ krb5_warnx(context,
+ "error wrong version in shared module %s "
+ "version: %d should have been %d\n",
+ prefix, mso->version, HDB_INTERFACE_VERSION);
+ dlclose(dl);
+ free(prefix);
+ return NULL;
+ }
+
+ if (mso->create == NULL) {
+ krb5_errx(context, 1,
+ "no entry point function in shared mod %s ",
+ prefix);
+ dlclose(dl);
+ free(prefix);
+ return NULL;
+ }
+
+ method.create = mso->create;
+ method.prefix = prefix;
+
+ return &method;
+}
+#endif /* HAVE_DLOPEN */
+
+/*
+ * find the relevant method for `filename', returning a pointer to the
+ * rest in `rest'.
+ * return NULL if there's no such method.
+ */
+
+static const struct hdb_method *
+find_method (const char *filename, const char **rest)
+{
+ const struct hdb_method *h;
+
+ for (h = methods; h->prefix != NULL; ++h) {
+ if (strncmp (filename, h->prefix, strlen(h->prefix)) == 0) {
+ *rest = filename + strlen(h->prefix);
+ return h;
+ }
+ }
+#if defined(HAVE_DB1) || defined(HAVE_DB3) || defined(HAVE_NDBM)
+ if (strncmp(filename, "/", 1) == 0
+ || strncmp(filename, "./", 2) == 0
+ || strncmp(filename, "../", 3) == 0)
+ {
+ *rest = filename;
+ return &dbmetod;
+ }
+#endif
+
+ return NULL;
+}
+
+krb5_error_code
+hdb_list_builtin(krb5_context context, char **list)
+{
+ const struct hdb_method *h;
+ size_t len = 0;
+ char *buf = NULL;
+
+ for (h = methods; h->prefix != NULL; ++h) {
+ if (h->prefix[0] == '\0')
+ continue;
+ len += strlen(h->prefix) + 2;
+ }
+
+ len += 1;
+ buf = malloc(len);
+ if (buf == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ buf[0] = '\0';
+
+ for (h = methods; h->prefix != NULL; ++h) {
+ if (h != methods)
+ strlcat(buf, ", ", len);
+ strlcat(buf, h->prefix, len);
+ }
+ *list = buf;
+ return 0;
+}
+
+krb5_error_code
+hdb_create(krb5_context context, HDB **db, const char *filename)
+{
+ const struct hdb_method *h;
+ const char *residual;
+ krb5_error_code ret;
+ struct krb5_plugin *list = NULL, *e;
+
+ if(filename == NULL)
+ filename = HDB_DEFAULT_DB;
+ krb5_add_et_list(context, initialize_hdb_error_table_r);
+ h = find_method (filename, &residual);
+
+ if (h == NULL) {
+ ret = _krb5_plugin_find(context, PLUGIN_TYPE_DATA, "hdb", &list);
+ if(ret == 0 && list != NULL) {
+ for (e = list; e != NULL; e = _krb5_plugin_get_next(e)) {
+ h = _krb5_plugin_get_symbol(e);
+ if (strncmp (filename, h->prefix, strlen(h->prefix)) == 0
+ && h->interface_version == HDB_INTERFACE_VERSION) {
+ residual = filename + strlen(h->prefix);
+ break;
+ }
+ }
+ if (e == NULL) {
+ h = NULL;
+ _krb5_plugin_free(list);
+ }
+ }
+ }
+
+#ifdef HAVE_DLOPEN
+ if (h == NULL)
+ h = find_dynamic_method (context, filename, &residual);
+#endif
+ if (h == NULL)
+ krb5_errx(context, 1, "No database support for %s", filename);
+ return (*h->create)(context, db, residual);
+}
diff --git a/source4/heimdal/lib/hdb/hdb.h b/source4/heimdal/lib/hdb/hdb.h
new file mode 100644
index 0000000000..53b1defc96
--- /dev/null
+++ b/source4/heimdal/lib/hdb/hdb.h
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 1997 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef __HDB_H__
+#define __HDB_H__
+
+#include <hdb_err.h>
+
+#include <heim_asn1.h>
+#include <hdb_asn1.h>
+
+struct hdb_dbinfo;
+
+enum hdb_lockop{ HDB_RLOCK, HDB_WLOCK };
+
+/* flags for various functions */
+#define HDB_F_DECRYPT 1 /* decrypt keys */
+#define HDB_F_REPLACE 2 /* replace entry */
+#define HDB_F_GET_CLIENT 4 /* fetch client */
+#define HDB_F_GET_SERVER 8 /* fetch server */
+#define HDB_F_GET_KRBTGT 16 /* fetch krbtgt */
+#define HDB_F_GET_ANY 28 /* fetch any of client,server,krbtgt */
+#define HDB_F_CANON 32 /* want canonicalition */
+
+/* key usage for master key */
+#define HDB_KU_MKEY 0x484442
+
+typedef struct hdb_master_key_data *hdb_master_key;
+
+typedef struct hdb_entry_ex {
+ void *ctx;
+ hdb_entry entry;
+ void (*free_entry)(krb5_context, struct hdb_entry_ex *);
+} hdb_entry_ex;
+
+
+typedef struct HDB{
+ void *hdb_db;
+ void *hdb_dbc;
+ char *hdb_name;
+ int hdb_master_key_set;
+ hdb_master_key hdb_master_key;
+ int hdb_openp;
+
+ krb5_error_code (*hdb_open)(krb5_context,
+ struct HDB*,
+ int,
+ mode_t);
+ krb5_error_code (*hdb_close)(krb5_context,
+ struct HDB*);
+ void (*hdb_free)(krb5_context,
+ struct HDB*,
+ hdb_entry_ex*);
+ krb5_error_code (*hdb_fetch)(krb5_context,
+ struct HDB*,
+ krb5_const_principal,
+ unsigned,
+ hdb_entry_ex*);
+ krb5_error_code (*hdb_store)(krb5_context,
+ struct HDB*,
+ unsigned,
+ hdb_entry_ex*);
+ krb5_error_code (*hdb_remove)(krb5_context,
+ struct HDB*,
+ krb5_const_principal);
+ krb5_error_code (*hdb_firstkey)(krb5_context,
+ struct HDB*,
+ unsigned,
+ hdb_entry_ex*);
+ krb5_error_code (*hdb_nextkey)(krb5_context,
+ struct HDB*,
+ unsigned,
+ hdb_entry_ex*);
+ krb5_error_code (*hdb_lock)(krb5_context,
+ struct HDB*,
+ int operation);
+ krb5_error_code (*hdb_unlock)(krb5_context,
+ struct HDB*);
+ krb5_error_code (*hdb_rename)(krb5_context,
+ struct HDB*,
+ const char*);
+ krb5_error_code (*hdb__get)(krb5_context,
+ struct HDB*,
+ krb5_data,
+ krb5_data*);
+ krb5_error_code (*hdb__put)(krb5_context,
+ struct HDB*,
+ int,
+ krb5_data,
+ krb5_data);
+ krb5_error_code (*hdb__del)(krb5_context,
+ struct HDB*,
+ krb5_data);
+ krb5_error_code (*hdb_destroy)(krb5_context,
+ struct HDB*);
+}HDB;
+
+#define HDB_INTERFACE_VERSION 4
+
+struct hdb_so_method {
+ int version;
+ const char *prefix;
+ krb5_error_code (*create)(krb5_context, HDB **, const char *filename);
+};
+
+typedef krb5_error_code (*hdb_foreach_func_t)(krb5_context, HDB*,
+ hdb_entry_ex*, void*);
+extern krb5_kt_ops hdb_kt_ops;
+
+struct hdb_method {
+ int interface_version;
+ const char *prefix;
+ krb5_error_code (*create)(krb5_context, HDB **, const char *filename);
+};
+
+#include <hdb-protos.h>
+
+#endif /* __HDB_H__ */
diff --git a/source4/heimdal/lib/hdb/hdb_err.et b/source4/heimdal/lib/hdb/hdb_err.et
new file mode 100644
index 0000000000..64f79fc84e
--- /dev/null
+++ b/source4/heimdal/lib/hdb/hdb_err.et
@@ -0,0 +1,28 @@
+#
+# Error messages for the hdb library
+#
+# This might look like a com_err file, but is not
+#
+id "$Id$"
+
+error_table hdb
+
+prefix HDB_ERR
+
+index 1
+#error_code INUSE, "Entry already exists in database"
+error_code UK_SERROR, "Database store error"
+error_code UK_RERROR, "Database read error"
+error_code NOENTRY, "No such entry in the database"
+error_code DB_INUSE, "Database is locked or in use--try again later"
+error_code DB_CHANGED, "Database was modified during read"
+error_code RECURSIVELOCK, "Attempt to lock database twice"
+error_code NOTLOCKED, "Attempt to unlock database when not locked"
+error_code BADLOCKMODE, "Invalid kdb lock mode"
+error_code CANT_LOCK_DB, "Insufficient access to lock database"
+error_code EXISTS, "Entry already exists in database"
+error_code BADVERSION, "Wrong database version"
+error_code NO_MKEY, "No correct master key"
+error_code MANDATORY_OPTION, "Entry contains unknown mandatory extension"
+
+end
diff --git a/source4/heimdal/lib/hdb/hdb_locl.h b/source4/heimdal/lib/hdb/hdb_locl.h
new file mode 100644
index 0000000000..e896b58025
--- /dev/null
+++ b/source4/heimdal/lib/hdb/hdb_locl.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 1997-2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef __HDB_LOCL_H__
+#define __HDB_LOCL_H__
+
+#include <config.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#include <roken.h>
+
+#include "crypto-headers.h"
+#include <krb5.h>
+#include <hdb.h>
+#include <hdb-private.h>
+
+#define HDB_DEFAULT_DB HDB_DB_DIR "/heimdal"
+#define HDB_DB_FORMAT_ENTRY "hdb/db-format"
+
+#endif /* __HDB_LOCL_H__ */
diff --git a/source4/heimdal/lib/hdb/keys.c b/source4/heimdal/lib/hdb/keys.c
new file mode 100644
index 0000000000..b9f294e2eb
--- /dev/null
+++ b/source4/heimdal/lib/hdb/keys.c
@@ -0,0 +1,401 @@
+/*
+ * Copyright (c) 1997 - 2001, 2003 - 2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hdb_locl.h"
+
+RCSID("$Id$");
+
+/*
+ * free all the memory used by (len, keys)
+ */
+
+void
+hdb_free_keys (krb5_context context, int len, Key *keys)
+{
+ int i;
+
+ for (i = 0; i < len; i++) {
+ free(keys[i].mkvno);
+ keys[i].mkvno = NULL;
+ if (keys[i].salt != NULL) {
+ free_Salt(keys[i].salt);
+ free(keys[i].salt);
+ keys[i].salt = NULL;
+ }
+ krb5_free_keyblock_contents(context, &keys[i].key);
+ }
+ free (keys);
+}
+
+/*
+ * for each entry in `default_keys' try to parse it as a sequence
+ * of etype:salttype:salt, syntax of this if something like:
+ * [(des|des3|etype):](pw-salt|afs3)[:string], if etype is omitted it
+ * means all etypes, and if string is omitted is means the default
+ * string (for that principal). Additional special values:
+ * v5 == pw-salt, and
+ * v4 == des:pw-salt:
+ * afs or afs3 == des:afs3-salt
+ */
+
+static const krb5_enctype des_etypes[] = {
+ ETYPE_DES_CBC_MD5,
+ ETYPE_DES_CBC_MD4,
+ ETYPE_DES_CBC_CRC
+};
+
+static const krb5_enctype all_etypes[] = {
+ ETYPE_AES256_CTS_HMAC_SHA1_96,
+ ETYPE_ARCFOUR_HMAC_MD5,
+ ETYPE_DES3_CBC_SHA1
+};
+
+static krb5_error_code
+parse_key_set(krb5_context context, const char *key,
+ krb5_enctype **ret_enctypes, size_t *ret_num_enctypes,
+ krb5_salt *salt, krb5_principal principal)
+{
+ const char *p;
+ char buf[3][256];
+ int num_buf = 0;
+ int i, num_enctypes = 0;
+ krb5_enctype e;
+ const krb5_enctype *enctypes = NULL;
+ krb5_error_code ret;
+
+ p = key;
+
+ *ret_enctypes = NULL;
+ *ret_num_enctypes = 0;
+
+ /* split p in a list of :-separated strings */
+ for(num_buf = 0; num_buf < 3; num_buf++)
+ if(strsep_copy(&p, ":", buf[num_buf], sizeof(buf[num_buf])) == -1)
+ break;
+
+ salt->saltvalue.data = NULL;
+ salt->saltvalue.length = 0;
+
+ for(i = 0; i < num_buf; i++) {
+ if(enctypes == NULL && num_buf > 1) {
+ /* this might be a etype specifier */
+ /* XXX there should be a string_to_etypes handling
+ special cases like `des' and `all' */
+ if(strcmp(buf[i], "des") == 0) {
+ enctypes = des_etypes;
+ num_enctypes = sizeof(des_etypes)/sizeof(des_etypes[0]);
+ } else if(strcmp(buf[i], "des3") == 0) {
+ e = ETYPE_DES3_CBC_SHA1;
+ enctypes = &e;
+ num_enctypes = 1;
+ } else {
+ ret = krb5_string_to_enctype(context, buf[i], &e);
+ if (ret == 0) {
+ enctypes = &e;
+ num_enctypes = 1;
+ } else
+ return ret;
+ }
+ continue;
+ }
+ if(salt->salttype == 0) {
+ /* interpret string as a salt specifier, if no etype
+ is set, this sets default values */
+ /* XXX should perhaps use string_to_salttype, but that
+ interface sucks */
+ if(strcmp(buf[i], "pw-salt") == 0) {
+ if(enctypes == NULL) {
+ enctypes = all_etypes;
+ num_enctypes = sizeof(all_etypes)/sizeof(all_etypes[0]);
+ }
+ salt->salttype = KRB5_PW_SALT;
+ } else if(strcmp(buf[i], "afs3-salt") == 0) {
+ if(enctypes == NULL) {
+ enctypes = des_etypes;
+ num_enctypes = sizeof(des_etypes)/sizeof(des_etypes[0]);
+ }
+ salt->salttype = KRB5_AFS3_SALT;
+ }
+ continue;
+ }
+
+ {
+ /* if there is a final string, use it as the string to
+ salt with, this is mostly useful with null salt for
+ v4 compat, and a cell name for afs compat */
+ salt->saltvalue.data = strdup(buf[i]);
+ if (salt->saltvalue.data == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ salt->saltvalue.length = strlen(buf[i]);
+ }
+ }
+
+ if(enctypes == NULL || salt->salttype == 0) {
+ krb5_set_error_message(context, EINVAL, "bad value for default_keys `%s'", key);
+ return EINVAL;
+ }
+
+ /* if no salt was specified make up default salt */
+ if(salt->saltvalue.data == NULL) {
+ if(salt->salttype == KRB5_PW_SALT)
+ ret = krb5_get_pw_salt(context, principal, salt);
+ else if(salt->salttype == KRB5_AFS3_SALT) {
+ krb5_realm *realm = krb5_princ_realm(context, principal);
+ salt->saltvalue.data = strdup(*realm);
+ if(salt->saltvalue.data == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ "out of memory while "
+ "parsing salt specifiers");
+ return ENOMEM;
+ }
+ strlwr(salt->saltvalue.data);
+ salt->saltvalue.length = strlen(*realm);
+ }
+ }
+
+ *ret_enctypes = malloc(sizeof(enctypes[0]) * num_enctypes);
+ if (*ret_enctypes == NULL) {
+ krb5_free_salt(context, *salt);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ memcpy(*ret_enctypes, enctypes, sizeof(enctypes[0]) * num_enctypes);
+ *ret_num_enctypes = num_enctypes;
+
+ return 0;
+}
+
+static krb5_error_code
+add_enctype_to_key_set(Key **key_set, size_t *nkeyset,
+ krb5_enctype enctype, krb5_salt *salt)
+{
+ krb5_error_code ret;
+ Key key, *tmp;
+
+ memset(&key, 0, sizeof(key));
+
+ tmp = realloc(*key_set, (*nkeyset + 1) * sizeof((*key_set)[0]));
+ if (tmp == NULL)
+ return ENOMEM;
+
+ *key_set = tmp;
+
+ key.key.keytype = enctype;
+ key.key.keyvalue.length = 0;
+ key.key.keyvalue.data = NULL;
+
+ if (salt) {
+ key.salt = malloc(sizeof(*key.salt));
+ if (key.salt == NULL) {
+ free_Key(&key);
+ return ENOMEM;
+ }
+
+ key.salt->type = salt->salttype;
+ krb5_data_zero (&key.salt->salt);
+
+ ret = krb5_data_copy(&key.salt->salt,
+ salt->saltvalue.data,
+ salt->saltvalue.length);
+ if (ret) {
+ free_Key(&key);
+ return ret;
+ }
+ } else
+ key.salt = NULL;
+
+ (*key_set)[*nkeyset] = key;
+
+ *nkeyset += 1;
+
+ return 0;
+}
+
+
+/*
+ * Generate the `key_set' from the [kadmin]default_keys statement. If
+ * `no_salt' is set, salt is not important (and will not be set) since
+ * it's random keys that is going to be created.
+ */
+
+krb5_error_code
+hdb_generate_key_set(krb5_context context, krb5_principal principal,
+ Key **ret_key_set, size_t *nkeyset, int no_salt)
+{
+ char **ktypes, **kp;
+ krb5_error_code ret;
+ Key *k, *key_set;
+ int i, j;
+ char *default_keytypes[] = {
+ "des:pw-salt",
+ "aes256-cts-hmac-sha1-96:pw-salt",
+ "des3-cbc-sha1:pw-salt",
+ "arcfour-hmac-md5:pw-salt",
+ NULL
+ };
+
+ ktypes = krb5_config_get_strings(context, NULL, "kadmin",
+ "default_keys", NULL);
+ if (ktypes == NULL)
+ ktypes = default_keytypes;
+
+ if (ktypes == NULL)
+ abort();
+
+ *ret_key_set = key_set = NULL;
+ *nkeyset = 0;
+
+ ret = 0;
+
+ for(kp = ktypes; kp && *kp; kp++) {
+ const char *p;
+ krb5_salt salt;
+ krb5_enctype *enctypes;
+ size_t num_enctypes;
+
+ p = *kp;
+ /* check alias */
+ if(strcmp(p, "v5") == 0)
+ p = "pw-salt";
+ else if(strcmp(p, "v4") == 0)
+ p = "des:pw-salt:";
+ else if(strcmp(p, "afs") == 0 || strcmp(p, "afs3") == 0)
+ p = "des:afs3-salt";
+ else if (strcmp(p, "arcfour-hmac-md5") == 0)
+ p = "arcfour-hmac-md5:pw-salt";
+
+ memset(&salt, 0, sizeof(salt));
+
+ ret = parse_key_set(context, p,
+ &enctypes, &num_enctypes, &salt, principal);
+ if (ret) {
+ krb5_warn(context, ret, "bad value for default_keys `%s'", *kp);
+ ret = 0;
+ continue;
+ }
+
+ for (i = 0; i < num_enctypes; i++) {
+ /* find duplicates */
+ for (j = 0; j < *nkeyset; j++) {
+
+ k = &key_set[j];
+
+ if (k->key.keytype == enctypes[i]) {
+ if (no_salt)
+ break;
+ if (k->salt == NULL && salt.salttype == KRB5_PW_SALT)
+ break;
+ if (k->salt->type == salt.salttype &&
+ k->salt->salt.length == salt.saltvalue.length &&
+ memcmp(k->salt->salt.data, salt.saltvalue.data,
+ salt.saltvalue.length) == 0)
+ break;
+ }
+ }
+ /* not a duplicate, lets add it */
+ if (j == *nkeyset) {
+ ret = add_enctype_to_key_set(&key_set, nkeyset, enctypes[i],
+ no_salt ? NULL : &salt);
+ if (ret) {
+ free(enctypes);
+ krb5_free_salt(context, salt);
+ goto out;
+ }
+ }
+ }
+ free(enctypes);
+ krb5_free_salt(context, salt);
+ }
+
+ *ret_key_set = key_set;
+
+ out:
+ if (ktypes != default_keytypes)
+ krb5_config_free_strings(ktypes);
+
+ if (ret) {
+ krb5_warn(context, ret,
+ "failed to parse the [kadmin]default_keys values");
+
+ for (i = 0; i < *nkeyset; i++)
+ free_Key(&key_set[i]);
+ free(key_set);
+ } else if (*nkeyset == 0) {
+ krb5_warnx(context,
+ "failed to parse any of the [kadmin]default_keys values");
+ ret = EINVAL; /* XXX */
+ }
+
+ return ret;
+}
+
+
+krb5_error_code
+hdb_generate_key_set_password(krb5_context context,
+ krb5_principal principal,
+ const char *password,
+ Key **keys, size_t *num_keys)
+{
+ krb5_error_code ret;
+ int i;
+
+ ret = hdb_generate_key_set(context, principal,
+ keys, num_keys, 0);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < (*num_keys); i++) {
+ krb5_salt salt;
+
+ salt.salttype = (*keys)[i].salt->type;
+ salt.saltvalue.length = (*keys)[i].salt->salt.length;
+ salt.saltvalue.data = (*keys)[i].salt->salt.data;
+
+ ret = krb5_string_to_key_salt (context,
+ (*keys)[i].key.keytype,
+ password,
+ salt,
+ &(*keys)[i].key);
+
+ if(ret)
+ break;
+ }
+
+ if(ret) {
+ hdb_free_keys (context, *num_keys, *keys);
+ return ret;
+ }
+ return ret;
+}
diff --git a/source4/heimdal/lib/hdb/keytab.c b/source4/heimdal/lib/hdb/keytab.c
new file mode 100644
index 0000000000..a890ba62cd
--- /dev/null
+++ b/source4/heimdal/lib/hdb/keytab.c
@@ -0,0 +1,272 @@
+/*
+ * Copyright (c) 1999 - 2002 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hdb_locl.h"
+
+/* keytab backend for HDB databases */
+
+RCSID("$Id$");
+
+struct hdb_data {
+ char *dbname;
+ char *mkey;
+};
+
+/*
+ * the format for HDB keytabs is:
+ * HDB:[database:file:mkey]
+ */
+
+static krb5_error_code
+hdb_resolve(krb5_context context, const char *name, krb5_keytab id)
+{
+ struct hdb_data *d;
+ const char *db, *mkey;
+
+ d = malloc(sizeof(*d));
+ if(d == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ db = name;
+ mkey = strrchr(name, ':');
+ if(mkey == NULL || mkey[1] == '\0') {
+ if(*name == '\0')
+ d->dbname = NULL;
+ else {
+ d->dbname = strdup(name);
+ if(d->dbname == NULL) {
+ free(d);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ }
+ d->mkey = NULL;
+ } else {
+ if((mkey - db) == 0) {
+ d->dbname = NULL;
+ } else {
+ d->dbname = malloc(mkey - db + 1);
+ if(d->dbname == NULL) {
+ free(d);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ memmove(d->dbname, db, mkey - db);
+ d->dbname[mkey - db] = '\0';
+ }
+ d->mkey = strdup(mkey + 1);
+ if(d->mkey == NULL) {
+ free(d->dbname);
+ free(d);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ }
+ id->data = d;
+ return 0;
+}
+
+static krb5_error_code
+hdb_close(krb5_context context, krb5_keytab id)
+{
+ struct hdb_data *d = id->data;
+
+ free(d->dbname);
+ free(d->mkey);
+ free(d);
+ return 0;
+}
+
+static krb5_error_code
+hdb_get_name(krb5_context context,
+ krb5_keytab id,
+ char *name,
+ size_t namesize)
+{
+ struct hdb_data *d = id->data;
+
+ snprintf(name, namesize, "%s%s%s",
+ d->dbname ? d->dbname : "",
+ (d->dbname || d->mkey) ? ":" : "",
+ d->mkey ? d->mkey : "");
+ return 0;
+}
+
+static void
+set_config (krb5_context context,
+ const krb5_config_binding *binding,
+ const char **dbname,
+ const char **mkey)
+{
+ *dbname = krb5_config_get_string(context, binding, "dbname", NULL);
+ *mkey = krb5_config_get_string(context, binding, "mkey_file", NULL);
+}
+
+/*
+ * try to figure out the database (`dbname') and master-key (`mkey')
+ * that should be used for `principal'.
+ */
+
+static void
+find_db (krb5_context context,
+ const char **dbname,
+ const char **mkey,
+ krb5_const_principal principal)
+{
+ const krb5_config_binding *top_bind = NULL;
+ const krb5_config_binding *default_binding = NULL;
+ const krb5_config_binding *db;
+ krb5_realm *prealm = krb5_princ_realm(context, rk_UNCONST(principal));
+
+ *dbname = *mkey = NULL;
+
+ while ((db =
+ krb5_config_get_next(context,
+ NULL,
+ &top_bind,
+ krb5_config_list,
+ "kdc",
+ "database",
+ NULL)) != NULL) {
+ const char *p;
+
+ p = krb5_config_get_string (context, db, "realm", NULL);
+ if (p == NULL) {
+ if(default_binding) {
+ krb5_warnx(context, "WARNING: more than one realm-less "
+ "database specification");
+ krb5_warnx(context, "WARNING: using the first encountered");
+ } else
+ default_binding = db;
+ } else if (strcmp (*prealm, p) == 0) {
+ set_config (context, db, dbname, mkey);
+ break;
+ }
+ }
+ if (*dbname == NULL && default_binding != NULL)
+ set_config (context, default_binding, dbname, mkey);
+ if (*dbname == NULL)
+ *dbname = HDB_DEFAULT_DB;
+}
+
+/*
+ * find the keytab entry in `id' for `principal, kvno, enctype' and return
+ * it in `entry'. return 0 or an error code
+ */
+
+static krb5_error_code
+hdb_get_entry(krb5_context context,
+ krb5_keytab id,
+ krb5_const_principal principal,
+ krb5_kvno kvno,
+ krb5_enctype enctype,
+ krb5_keytab_entry *entry)
+{
+ hdb_entry_ex ent;
+ krb5_error_code ret;
+ struct hdb_data *d = id->data;
+ int i;
+ HDB *db;
+ const char *dbname = d->dbname;
+ const char *mkey = d->mkey;
+
+ memset(&ent, 0, sizeof(ent));
+
+ if (dbname == NULL)
+ find_db (context, &dbname, &mkey, principal);
+
+ ret = hdb_create (context, &db, dbname);
+ if (ret)
+ return ret;
+ ret = hdb_set_master_keyfile (context, db, mkey);
+ if (ret) {
+ (*db->hdb_destroy)(context, db);
+ return ret;
+ }
+
+ ret = (*db->hdb_open)(context, db, O_RDONLY, 0);
+ if (ret) {
+ (*db->hdb_destroy)(context, db);
+ return ret;
+ }
+ ret = (*db->hdb_fetch)(context, db, principal,
+ HDB_F_DECRYPT|
+ HDB_F_GET_CLIENT|HDB_F_GET_SERVER|HDB_F_GET_KRBTGT,
+ &ent);
+
+ if(ret == HDB_ERR_NOENTRY) {
+ ret = KRB5_KT_NOTFOUND;
+ goto out;
+ }else if(ret)
+ goto out;
+
+ if(kvno && ent.entry.kvno != kvno) {
+ hdb_free_entry(context, &ent);
+ ret = KRB5_KT_NOTFOUND;
+ goto out;
+ }
+ if(enctype == 0)
+ if(ent.entry.keys.len > 0)
+ enctype = ent.entry.keys.val[0].key.keytype;
+ ret = KRB5_KT_NOTFOUND;
+ for(i = 0; i < ent.entry.keys.len; i++) {
+ if(ent.entry.keys.val[i].key.keytype == enctype) {
+ krb5_copy_principal(context, principal, &entry->principal);
+ entry->vno = ent.entry.kvno;
+ krb5_copy_keyblock_contents(context,
+ &ent.entry.keys.val[i].key,
+ &entry->keyblock);
+ ret = 0;
+ break;
+ }
+ }
+ hdb_free_entry(context, &ent);
+out:
+ (*db->hdb_close)(context, db);
+ (*db->hdb_destroy)(context, db);
+ return ret;
+}
+
+krb5_kt_ops hdb_kt_ops = {
+ "HDB",
+ hdb_resolve,
+ hdb_get_name,
+ hdb_close,
+ hdb_get_entry,
+ NULL, /* start_seq_get */
+ NULL, /* next_entry */
+ NULL, /* end_seq_get */
+ NULL, /* add */
+ NULL /* remove */
+};
diff --git a/source4/heimdal/lib/hdb/mkey.c b/source4/heimdal/lib/hdb/mkey.c
new file mode 100644
index 0000000000..ce1aa9aff1
--- /dev/null
+++ b/source4/heimdal/lib/hdb/mkey.c
@@ -0,0 +1,604 @@
+/*
+ * Copyright (c) 2000 - 2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hdb_locl.h"
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+RCSID("$Id$");
+
+struct hdb_master_key_data {
+ krb5_keytab_entry keytab;
+ krb5_crypto crypto;
+ struct hdb_master_key_data *next;
+};
+
+void
+hdb_free_master_key(krb5_context context, hdb_master_key mkey)
+{
+ struct hdb_master_key_data *ptr;
+ while(mkey) {
+ krb5_kt_free_entry(context, &mkey->keytab);
+ if (mkey->crypto)
+ krb5_crypto_destroy(context, mkey->crypto);
+ ptr = mkey;
+ mkey = mkey->next;
+ free(ptr);
+ }
+}
+
+krb5_error_code
+hdb_process_master_key(krb5_context context,
+ int kvno, krb5_keyblock *key, krb5_enctype etype,
+ hdb_master_key *mkey)
+{
+ krb5_error_code ret;
+
+ *mkey = calloc(1, sizeof(**mkey));
+ if(*mkey == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ (*mkey)->keytab.vno = kvno;
+ ret = krb5_parse_name(context, "K/M", &(*mkey)->keytab.principal);
+ if(ret)
+ goto fail;
+ ret = krb5_copy_keyblock_contents(context, key, &(*mkey)->keytab.keyblock);
+ if(ret)
+ goto fail;
+ if(etype != 0)
+ (*mkey)->keytab.keyblock.keytype = etype;
+ (*mkey)->keytab.timestamp = time(NULL);
+ ret = krb5_crypto_init(context, key, etype, &(*mkey)->crypto);
+ if(ret)
+ goto fail;
+ return 0;
+ fail:
+ hdb_free_master_key(context, *mkey);
+ *mkey = NULL;
+ return ret;
+}
+
+krb5_error_code
+hdb_add_master_key(krb5_context context, krb5_keyblock *key,
+ hdb_master_key *inout)
+{
+ int vno = 0;
+ hdb_master_key p;
+ krb5_error_code ret;
+
+ for(p = *inout; p; p = p->next)
+ vno = max(vno, p->keytab.vno);
+ vno++;
+ ret = hdb_process_master_key(context, vno, key, 0, &p);
+ if(ret)
+ return ret;
+ p->next = *inout;
+ *inout = p;
+ return 0;
+}
+
+static krb5_error_code
+read_master_keytab(krb5_context context, const char *filename,
+ hdb_master_key *mkey)
+{
+ krb5_error_code ret;
+ krb5_keytab id;
+ krb5_kt_cursor cursor;
+ krb5_keytab_entry entry;
+ hdb_master_key p;
+
+ ret = krb5_kt_resolve(context, filename, &id);
+ if(ret)
+ return ret;
+
+ ret = krb5_kt_start_seq_get(context, id, &cursor);
+ if(ret)
+ goto out;
+ *mkey = NULL;
+ while(krb5_kt_next_entry(context, id, &entry, &cursor) == 0) {
+ p = calloc(1, sizeof(*p));
+ if(p == NULL) {
+ krb5_kt_end_seq_get(context, id, &cursor);
+ ret = ENOMEM;
+ goto out;
+ }
+ p->keytab = entry;
+ ret = krb5_crypto_init(context, &p->keytab.keyblock, 0, &p->crypto);
+ p->next = *mkey;
+ *mkey = p;
+ }
+ krb5_kt_end_seq_get(context, id, &cursor);
+ out:
+ krb5_kt_close(context, id);
+ return ret;
+}
+
+/* read a MIT master keyfile */
+static krb5_error_code
+read_master_mit(krb5_context context, const char *filename,
+ hdb_master_key *mkey)
+{
+ int fd;
+ krb5_error_code ret;
+ krb5_storage *sp;
+ int16_t enctype;
+ krb5_keyblock key;
+
+ fd = open(filename, O_RDONLY | O_BINARY);
+ if(fd < 0) {
+ int save_errno = errno;
+ krb5_set_error_message(context, save_errno, "failed to open %s: %s",
+ filename, strerror(save_errno));
+ return save_errno;
+ }
+ sp = krb5_storage_from_fd(fd);
+ if(sp == NULL) {
+ close(fd);
+ return errno;
+ }
+ krb5_storage_set_flags(sp, KRB5_STORAGE_HOST_BYTEORDER);
+#if 0
+ /* could possibly use ret_keyblock here, but do it with more
+ checks for now */
+ ret = krb5_ret_keyblock(sp, &key);
+#else
+ ret = krb5_ret_int16(sp, &enctype);
+ if((htons(enctype) & 0xff00) == 0x3000) {
+ ret = HEIM_ERR_BAD_MKEY;
+ krb5_set_error_message(context, ret, "unknown keytype in %s: %#x, expected %#x",
+ filename, htons(enctype), 0x3000);
+ goto out;
+ }
+ key.keytype = enctype;
+ ret = krb5_ret_data(sp, &key.keyvalue);
+ if(ret)
+ goto out;
+#endif
+ ret = hdb_process_master_key(context, 0, &key, 0, mkey);
+ krb5_free_keyblock_contents(context, &key);
+ out:
+ krb5_storage_free(sp);
+ close(fd);
+ return ret;
+}
+
+/* read an old master key file */
+static krb5_error_code
+read_master_encryptionkey(krb5_context context, const char *filename,
+ hdb_master_key *mkey)
+{
+ int fd;
+ krb5_keyblock key;
+ krb5_error_code ret;
+ unsigned char buf[256];
+ ssize_t len;
+ size_t ret_len;
+
+ fd = open(filename, O_RDONLY | O_BINARY);
+ if(fd < 0) {
+ int save_errno = errno;
+ krb5_set_error_message(context, save_errno, "failed to open %s: %s",
+ filename, strerror(save_errno));
+ return save_errno;
+ }
+
+ len = read(fd, buf, sizeof(buf));
+ close(fd);
+ if(len < 0) {
+ int save_errno = errno;
+ krb5_set_error_message(context, save_errno, "error reading %s: %s",
+ filename, strerror(save_errno));
+ return save_errno;
+ }
+
+ ret = decode_EncryptionKey(buf, len, &key, &ret_len);
+ memset(buf, 0, sizeof(buf));
+ if(ret)
+ return ret;
+
+ /* Originally, the keytype was just that, and later it got changed
+ to des-cbc-md5, but we always used des in cfb64 mode. This
+ should cover all cases, but will break if someone has hacked
+ this code to really use des-cbc-md5 -- but then that's not my
+ problem. */
+ if(key.keytype == KEYTYPE_DES || key.keytype == ETYPE_DES_CBC_MD5)
+ key.keytype = ETYPE_DES_CFB64_NONE;
+
+ ret = hdb_process_master_key(context, 0, &key, 0, mkey);
+ krb5_free_keyblock_contents(context, &key);
+ return ret;
+}
+
+/* read a krb4 /.k style file */
+static krb5_error_code
+read_master_krb4(krb5_context context, const char *filename,
+ hdb_master_key *mkey)
+{
+ int fd;
+ krb5_keyblock key;
+ krb5_error_code ret;
+ unsigned char buf[256];
+ ssize_t len;
+
+ fd = open(filename, O_RDONLY | O_BINARY);
+ if(fd < 0) {
+ int save_errno = errno;
+ krb5_set_error_message(context, save_errno, "failed to open %s: %s",
+ filename, strerror(save_errno));
+ return save_errno;
+ }
+
+ len = read(fd, buf, sizeof(buf));
+ close(fd);
+ if(len < 0) {
+ int save_errno = errno;
+ krb5_set_error_message(context, save_errno, "error reading %s: %s",
+ filename, strerror(save_errno));
+ return save_errno;
+ }
+ if(len != 8) {
+ krb5_set_error_message(context, HEIM_ERR_EOF,
+ "bad contents of %s", filename);
+ return HEIM_ERR_EOF; /* XXX file might be too large */
+ }
+
+ memset(&key, 0, sizeof(key));
+ key.keytype = ETYPE_DES_PCBC_NONE;
+ ret = krb5_data_copy(&key.keyvalue, buf, len);
+ memset(buf, 0, sizeof(buf));
+ if(ret)
+ return ret;
+
+ ret = hdb_process_master_key(context, 0, &key, 0, mkey);
+ krb5_free_keyblock_contents(context, &key);
+ return ret;
+}
+
+krb5_error_code
+hdb_read_master_key(krb5_context context, const char *filename,
+ hdb_master_key *mkey)
+{
+ FILE *f;
+ unsigned char buf[16];
+ krb5_error_code ret;
+
+ off_t len;
+
+ *mkey = NULL;
+
+ if(filename == NULL)
+ filename = HDB_DB_DIR "/m-key";
+
+ f = fopen(filename, "r");
+ if(f == NULL) {
+ int save_errno = errno;
+ krb5_set_error_message(context, save_errno, "failed to open %s: %s",
+ filename, strerror(save_errno));
+ return save_errno;
+ }
+
+ if(fread(buf, 1, 2, f) != 2) {
+ fclose(f);
+ krb5_set_error_message(context, HEIM_ERR_EOF, "end of file reading %s", filename);
+ return HEIM_ERR_EOF;
+ }
+
+ fseek(f, 0, SEEK_END);
+ len = ftell(f);
+
+ if(fclose(f) != 0)
+ return errno;
+
+ if(len < 0)
+ return errno;
+
+ if(len == 8) {
+ ret = read_master_krb4(context, filename, mkey);
+ } else if(buf[0] == 0x30 && len <= 127 && buf[1] == len - 2) {
+ ret = read_master_encryptionkey(context, filename, mkey);
+ } else if(buf[0] == 5 && buf[1] >= 1 && buf[1] <= 2) {
+ ret = read_master_keytab(context, filename, mkey);
+ } else {
+ ret = read_master_mit(context, filename, mkey);
+ }
+ return ret;
+}
+
+krb5_error_code
+hdb_write_master_key(krb5_context context, const char *filename,
+ hdb_master_key mkey)
+{
+ krb5_error_code ret;
+ hdb_master_key p;
+ krb5_keytab kt;
+
+ if(filename == NULL)
+ filename = HDB_DB_DIR "/m-key";
+
+ ret = krb5_kt_resolve(context, filename, &kt);
+ if(ret)
+ return ret;
+
+ for(p = mkey; p; p = p->next) {
+ ret = krb5_kt_add_entry(context, kt, &p->keytab);
+ }
+
+ krb5_kt_close(context, kt);
+
+ return ret;
+}
+
+hdb_master_key
+_hdb_find_master_key(uint32_t *mkvno, hdb_master_key mkey)
+{
+ hdb_master_key ret = NULL;
+ while(mkey) {
+ if(ret == NULL && mkey->keytab.vno == 0)
+ ret = mkey;
+ if(mkvno == NULL) {
+ if(ret == NULL || mkey->keytab.vno > ret->keytab.vno)
+ ret = mkey;
+ } else if(mkey->keytab.vno == *mkvno)
+ return mkey;
+ mkey = mkey->next;
+ }
+ return ret;
+}
+
+int
+_hdb_mkey_version(hdb_master_key mkey)
+{
+ return mkey->keytab.vno;
+}
+
+int
+_hdb_mkey_decrypt(krb5_context context, hdb_master_key key,
+ krb5_key_usage usage,
+ void *ptr, size_t size, krb5_data *res)
+{
+ return krb5_decrypt(context, key->crypto, usage,
+ ptr, size, res);
+}
+
+int
+_hdb_mkey_encrypt(krb5_context context, hdb_master_key key,
+ krb5_key_usage usage,
+ const void *ptr, size_t size, krb5_data *res)
+{
+ return krb5_encrypt(context, key->crypto, usage,
+ ptr, size, res);
+}
+
+krb5_error_code
+hdb_unseal_key_mkey(krb5_context context, Key *k, hdb_master_key mkey)
+{
+
+ krb5_error_code ret;
+ krb5_data res;
+ size_t keysize;
+
+ hdb_master_key key;
+
+ if(k->mkvno == NULL)
+ return 0;
+
+ key = _hdb_find_master_key(k->mkvno, mkey);
+
+ if (key == NULL)
+ return HDB_ERR_NO_MKEY;
+
+ ret = _hdb_mkey_decrypt(context, key, HDB_KU_MKEY,
+ k->key.keyvalue.data,
+ k->key.keyvalue.length,
+ &res);
+ if(ret == KRB5KRB_AP_ERR_BAD_INTEGRITY) {
+ /* try to decrypt with MIT key usage */
+ ret = _hdb_mkey_decrypt(context, key, 0,
+ k->key.keyvalue.data,
+ k->key.keyvalue.length,
+ &res);
+ }
+ if (ret)
+ return ret;
+
+ /* fixup keylength if the key got padded when encrypting it */
+ ret = krb5_enctype_keysize(context, k->key.keytype, &keysize);
+ if (ret) {
+ krb5_data_free(&res);
+ return ret;
+ }
+ if (keysize > res.length) {
+ krb5_data_free(&res);
+ return KRB5_BAD_KEYSIZE;
+ }
+
+ memset(k->key.keyvalue.data, 0, k->key.keyvalue.length);
+ free(k->key.keyvalue.data);
+ k->key.keyvalue = res;
+ k->key.keyvalue.length = keysize;
+ free(k->mkvno);
+ k->mkvno = NULL;
+
+ return 0;
+}
+
+krb5_error_code
+hdb_unseal_keys_mkey(krb5_context context, hdb_entry *ent, hdb_master_key mkey)
+{
+ int i;
+
+ for(i = 0; i < ent->keys.len; i++){
+ krb5_error_code ret;
+
+ ret = hdb_unseal_key_mkey(context, &ent->keys.val[i], mkey);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+krb5_error_code
+hdb_unseal_keys(krb5_context context, HDB *db, hdb_entry *ent)
+{
+ if (db->hdb_master_key_set == 0)
+ return 0;
+ return hdb_unseal_keys_mkey(context, ent, db->hdb_master_key);
+}
+
+krb5_error_code
+hdb_unseal_key(krb5_context context, HDB *db, Key *k)
+{
+ if (db->hdb_master_key_set == 0)
+ return 0;
+ return hdb_unseal_key_mkey(context, k, db->hdb_master_key);
+}
+
+krb5_error_code
+hdb_seal_key_mkey(krb5_context context, Key *k, hdb_master_key mkey)
+{
+ krb5_error_code ret;
+ krb5_data res;
+ hdb_master_key key;
+
+ if(k->mkvno != NULL)
+ return 0;
+
+ key = _hdb_find_master_key(k->mkvno, mkey);
+
+ if (key == NULL)
+ return HDB_ERR_NO_MKEY;
+
+ ret = _hdb_mkey_encrypt(context, key, HDB_KU_MKEY,
+ k->key.keyvalue.data,
+ k->key.keyvalue.length,
+ &res);
+ if (ret)
+ return ret;
+
+ memset(k->key.keyvalue.data, 0, k->key.keyvalue.length);
+ free(k->key.keyvalue.data);
+ k->key.keyvalue = res;
+
+ if (k->mkvno == NULL) {
+ k->mkvno = malloc(sizeof(*k->mkvno));
+ if (k->mkvno == NULL)
+ return ENOMEM;
+ }
+ *k->mkvno = key->keytab.vno;
+
+ return 0;
+}
+
+krb5_error_code
+hdb_seal_keys_mkey(krb5_context context, hdb_entry *ent, hdb_master_key mkey)
+{
+ int i;
+ for(i = 0; i < ent->keys.len; i++){
+ krb5_error_code ret;
+
+ ret = hdb_seal_key_mkey(context, &ent->keys.val[i], mkey);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+krb5_error_code
+hdb_seal_keys(krb5_context context, HDB *db, hdb_entry *ent)
+{
+ if (db->hdb_master_key_set == 0)
+ return 0;
+
+ return hdb_seal_keys_mkey(context, ent, db->hdb_master_key);
+}
+
+krb5_error_code
+hdb_seal_key(krb5_context context, HDB *db, Key *k)
+{
+ if (db->hdb_master_key_set == 0)
+ return 0;
+
+ return hdb_seal_key_mkey(context, k, db->hdb_master_key);
+}
+
+krb5_error_code
+hdb_set_master_key (krb5_context context,
+ HDB *db,
+ krb5_keyblock *key)
+{
+ krb5_error_code ret;
+ hdb_master_key mkey;
+
+ ret = hdb_process_master_key(context, 0, key, 0, &mkey);
+ if (ret)
+ return ret;
+ db->hdb_master_key = mkey;
+#if 0 /* XXX - why? */
+ des_set_random_generator_seed(key.keyvalue.data);
+#endif
+ db->hdb_master_key_set = 1;
+ return 0;
+}
+
+krb5_error_code
+hdb_set_master_keyfile (krb5_context context,
+ HDB *db,
+ const char *keyfile)
+{
+ hdb_master_key key;
+ krb5_error_code ret;
+
+ ret = hdb_read_master_key(context, keyfile, &key);
+ if (ret) {
+ if (ret != ENOENT)
+ return ret;
+ krb5_clear_error_message(context);
+ return 0;
+ }
+ db->hdb_master_key = key;
+ db->hdb_master_key_set = 1;
+ return ret;
+}
+
+krb5_error_code
+hdb_clear_master_key (krb5_context context,
+ HDB *db)
+{
+ if (db->hdb_master_key_set) {
+ hdb_free_master_key(context, db->hdb_master_key);
+ db->hdb_master_key_set = 0;
+ }
+ return 0;
+}
diff --git a/source4/heimdal/lib/hdb/ndbm.c b/source4/heimdal/lib/hdb/ndbm.c
new file mode 100644
index 0000000000..e71125c61f
--- /dev/null
+++ b/source4/heimdal/lib/hdb/ndbm.c
@@ -0,0 +1,370 @@
+/*
+ * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hdb_locl.h"
+
+RCSID("$Id$");
+
+#if HAVE_NDBM
+
+#if defined(HAVE_GDBM_NDBM_H)
+#include <gdbm/ndbm.h>
+#elif defined(HAVE_NDBM_H)
+#include <ndbm.h>
+#elif defined(HAVE_DBM_H)
+#include <dbm.h>
+#endif
+
+struct ndbm_db {
+ DBM *db;
+ int lock_fd;
+};
+
+static krb5_error_code
+NDBM_destroy(krb5_context context, HDB *db)
+{
+ krb5_error_code ret;
+
+ ret = hdb_clear_master_key (context, db);
+ free(db->hdb_name);
+ free(db);
+ return 0;
+}
+
+static krb5_error_code
+NDBM_lock(krb5_context context, HDB *db, int operation)
+{
+ struct ndbm_db *d = db->hdb_db;
+ return hdb_lock(d->lock_fd, operation);
+}
+
+static krb5_error_code
+NDBM_unlock(krb5_context context, HDB *db)
+{
+ struct ndbm_db *d = db->hdb_db;
+ return hdb_unlock(d->lock_fd);
+}
+
+static krb5_error_code
+NDBM_seq(krb5_context context, HDB *db,
+ unsigned flags, hdb_entry_ex *entry, int first)
+
+{
+ struct ndbm_db *d = (struct ndbm_db *)db->hdb_db;
+ datum key, value;
+ krb5_data key_data, data;
+ krb5_error_code ret = 0;
+
+ if(first)
+ key = dbm_firstkey(d->db);
+ else
+ key = dbm_nextkey(d->db);
+ if(key.dptr == NULL)
+ return HDB_ERR_NOENTRY;
+ key_data.data = key.dptr;
+ key_data.length = key.dsize;
+ ret = db->hdb_lock(context, db, HDB_RLOCK);
+ if(ret) return ret;
+ value = dbm_fetch(d->db, key);
+ db->hdb_unlock(context, db);
+ data.data = value.dptr;
+ data.length = value.dsize;
+ memset(entry, 0, sizeof(*entry));
+ if(hdb_value2entry(context, &data, &entry->entry))
+ return NDBM_seq(context, db, flags, entry, 0);
+ if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) {
+ ret = hdb_unseal_keys (context, db, &entry->entry);
+ if (ret)
+ hdb_free_entry (context, entry);
+ }
+ if (ret == 0 && entry->entry.principal == NULL) {
+ entry->entry.principal = malloc (sizeof(*entry->entry.principal));
+ if (entry->entry.principal == NULL) {
+ hdb_free_entry (context, entry);
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ } else {
+ hdb_key2principal (context, &key_data, entry->entry.principal);
+ }
+ }
+ return ret;
+}
+
+
+static krb5_error_code
+NDBM_firstkey(krb5_context context, HDB *db,unsigned flags,hdb_entry_ex *entry)
+{
+ return NDBM_seq(context, db, flags, entry, 1);
+}
+
+
+static krb5_error_code
+NDBM_nextkey(krb5_context context, HDB *db, unsigned flags,hdb_entry_ex *entry)
+{
+ return NDBM_seq(context, db, flags, entry, 0);
+}
+
+static krb5_error_code
+NDBM_rename(krb5_context context, HDB *db, const char *new_name)
+{
+ /* XXX this function will break */
+ struct ndbm_db *d = db->hdb_db;
+
+ int ret;
+ char *old_dir, *old_pag, *new_dir, *new_pag;
+ char *new_lock;
+ int lock_fd;
+
+ /* lock old and new databases */
+ ret = db->hdb_lock(context, db, HDB_WLOCK);
+ if(ret)
+ return ret;
+ asprintf(&new_lock, "%s.lock", new_name);
+ if(new_lock == NULL) {
+ db->hdb_unlock(context, db);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ lock_fd = open(new_lock, O_RDWR | O_CREAT, 0600);
+ if(lock_fd < 0) {
+ ret = errno;
+ db->hdb_unlock(context, db);
+ krb5_set_error_message(context, ret, "open(%s): %s", new_lock,
+ strerror(ret));
+ free(new_lock);
+ return ret;
+ }
+ free(new_lock);
+ ret = hdb_lock(lock_fd, HDB_WLOCK);
+ if(ret) {
+ db->hdb_unlock(context, db);
+ close(lock_fd);
+ return ret;
+ }
+
+ asprintf(&old_dir, "%s.dir", db->hdb_name);
+ asprintf(&old_pag, "%s.pag", db->hdb_name);
+ asprintf(&new_dir, "%s.dir", new_name);
+ asprintf(&new_pag, "%s.pag", new_name);
+
+ ret = rename(old_dir, new_dir) || rename(old_pag, new_pag);
+ free(old_dir);
+ free(old_pag);
+ free(new_dir);
+ free(new_pag);
+ hdb_unlock(lock_fd);
+ db->hdb_unlock(context, db);
+
+ if(ret) {
+ ret = errno;
+ close(lock_fd);
+ krb5_set_error_message(context, ret, "rename: %s", strerror(ret));
+ return ret;
+ }
+
+ close(d->lock_fd);
+ d->lock_fd = lock_fd;
+
+ free(db->hdb_name);
+ db->hdb_name = strdup(new_name);
+ return 0;
+}
+
+static krb5_error_code
+NDBM__get(krb5_context context, HDB *db, krb5_data key, krb5_data *reply)
+{
+ struct ndbm_db *d = (struct ndbm_db *)db->hdb_db;
+ datum k, v;
+ int code;
+
+ k.dptr = key.data;
+ k.dsize = key.length;
+ code = db->hdb_lock(context, db, HDB_RLOCK);
+ if(code)
+ return code;
+ v = dbm_fetch(d->db, k);
+ db->hdb_unlock(context, db);
+ if(v.dptr == NULL)
+ return HDB_ERR_NOENTRY;
+
+ krb5_data_copy(reply, v.dptr, v.dsize);
+ return 0;
+}
+
+static krb5_error_code
+NDBM__put(krb5_context context, HDB *db, int replace,
+ krb5_data key, krb5_data value)
+{
+ struct ndbm_db *d = (struct ndbm_db *)db->hdb_db;
+ datum k, v;
+ int code;
+
+ k.dptr = key.data;
+ k.dsize = key.length;
+ v.dptr = value.data;
+ v.dsize = value.length;
+
+ code = db->hdb_lock(context, db, HDB_WLOCK);
+ if(code)
+ return code;
+ code = dbm_store(d->db, k, v, replace ? DBM_REPLACE : DBM_INSERT);
+ db->hdb_unlock(context, db);
+ if(code == 1)
+ return HDB_ERR_EXISTS;
+ if (code < 0)
+ return code;
+ return 0;
+}
+
+static krb5_error_code
+NDBM__del(krb5_context context, HDB *db, krb5_data key)
+{
+ struct ndbm_db *d = (struct ndbm_db *)db->hdb_db;
+ datum k;
+ int code;
+ krb5_error_code ret;
+
+ k.dptr = key.data;
+ k.dsize = key.length;
+ ret = db->hdb_lock(context, db, HDB_WLOCK);
+ if(ret) return ret;
+ code = dbm_delete(d->db, k);
+ db->hdb_unlock(context, db);
+ if(code < 0)
+ return errno;
+ return 0;
+}
+
+
+static krb5_error_code
+NDBM_close(krb5_context context, HDB *db)
+{
+ struct ndbm_db *d = db->hdb_db;
+ dbm_close(d->db);
+ close(d->lock_fd);
+ free(d);
+ return 0;
+}
+
+static krb5_error_code
+NDBM_open(krb5_context context, HDB *db, int flags, mode_t mode)
+{
+ krb5_error_code ret;
+ struct ndbm_db *d = malloc(sizeof(*d));
+ char *lock_file;
+
+ if(d == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ asprintf(&lock_file, "%s.lock", (char*)db->hdb_name);
+ if(lock_file == NULL) {
+ free(d);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ d->db = dbm_open((char*)db->hdb_name, flags, mode);
+ if(d->db == NULL){
+ ret = errno;
+ free(d);
+ free(lock_file);
+ krb5_set_error_message(context, ret, "dbm_open(%s): %s", db->hdb_name,
+ strerror(ret));
+ return ret;
+ }
+ d->lock_fd = open(lock_file, O_RDWR | O_CREAT, 0600);
+ if(d->lock_fd < 0){
+ ret = errno;
+ dbm_close(d->db);
+ free(d);
+ krb5_set_error_message(context, ret, "open(%s): %s", lock_file,
+ strerror(ret));
+ free(lock_file);
+ return ret;
+ }
+ free(lock_file);
+ db->hdb_db = d;
+ if((flags & O_ACCMODE) == O_RDONLY)
+ ret = hdb_check_db_format(context, db);
+ else
+ ret = hdb_init_db(context, db);
+ if(ret == HDB_ERR_NOENTRY)
+ return 0;
+ if (ret) {
+ NDBM_close(context, db);
+ krb5_set_error_message(context, ret, "hdb_open: failed %s database %s",
+ (flags & O_ACCMODE) == O_RDONLY ?
+ "checking format of" : "initialize",
+ db->hdb_name);
+ }
+ return ret;
+}
+
+krb5_error_code
+hdb_ndbm_create(krb5_context context, HDB **db,
+ const char *filename)
+{
+ *db = calloc(1, sizeof(**db));
+ if (*db == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+
+ (*db)->hdb_db = NULL;
+ (*db)->hdb_name = strdup(filename);
+ if ((*db)->hdb_name == NULL) {
+ free(*db);
+ *db = NULL;
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ (*db)->hdb_master_key_set = 0;
+ (*db)->hdb_openp = 0;
+ (*db)->hdb_open = NDBM_open;
+ (*db)->hdb_close = NDBM_close;
+ (*db)->hdb_fetch = _hdb_fetch;
+ (*db)->hdb_store = _hdb_store;
+ (*db)->hdb_remove = _hdb_remove;
+ (*db)->hdb_firstkey = NDBM_firstkey;
+ (*db)->hdb_nextkey= NDBM_nextkey;
+ (*db)->hdb_lock = NDBM_lock;
+ (*db)->hdb_unlock = NDBM_unlock;
+ (*db)->hdb_rename = NDBM_rename;
+ (*db)->hdb__get = NDBM__get;
+ (*db)->hdb__put = NDBM__put;
+ (*db)->hdb__del = NDBM__del;
+ (*db)->hdb_destroy = NDBM_destroy;
+ return 0;
+}
+
+#endif /* HAVE_NDBM */
diff --git a/source4/heimdal/lib/hx509/ca.c b/source4/heimdal/lib/hx509/ca.c
new file mode 100644
index 0000000000..cbd58ebd01
--- /dev/null
+++ b/source4/heimdal/lib/hx509/ca.c
@@ -0,0 +1,1518 @@
+/*
+ * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hx_locl.h"
+#include <pkinit_asn1.h>
+RCSID("$Id$");
+
+/**
+ * @page page_ca Hx509 CA functions
+ *
+ * See the library functions here: @ref hx509_ca
+ */
+
+struct hx509_ca_tbs {
+ hx509_name subject;
+ SubjectPublicKeyInfo spki;
+ ExtKeyUsage eku;
+ GeneralNames san;
+ unsigned key_usage;
+ heim_integer serial;
+ struct {
+ unsigned int proxy:1;
+ unsigned int ca:1;
+ unsigned int key:1;
+ unsigned int serial:1;
+ unsigned int domaincontroller:1;
+ } flags;
+ time_t notBefore;
+ time_t notAfter;
+ int pathLenConstraint; /* both for CA and Proxy */
+ CRLDistributionPoints crldp;
+};
+
+/**
+ * Allocate an to-be-signed certificate object that will be converted
+ * into an certificate.
+ *
+ * @param context A hx509 context.
+ * @param tbs returned to-be-signed certicate object, free with
+ * hx509_ca_tbs_free().
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+int
+hx509_ca_tbs_init(hx509_context context, hx509_ca_tbs *tbs)
+{
+ *tbs = calloc(1, sizeof(**tbs));
+ if (*tbs == NULL)
+ return ENOMEM;
+
+ (*tbs)->subject = NULL;
+ (*tbs)->san.len = 0;
+ (*tbs)->san.val = NULL;
+ (*tbs)->eku.len = 0;
+ (*tbs)->eku.val = NULL;
+ (*tbs)->pathLenConstraint = 0;
+ (*tbs)->crldp.len = 0;
+ (*tbs)->crldp.val = NULL;
+
+ return 0;
+}
+
+/**
+ * Free an To Be Signed object.
+ *
+ * @param tbs object to free.
+ *
+ * @ingroup hx509_ca
+ */
+
+void
+hx509_ca_tbs_free(hx509_ca_tbs *tbs)
+{
+ if (tbs == NULL || *tbs == NULL)
+ return;
+
+ free_SubjectPublicKeyInfo(&(*tbs)->spki);
+ free_GeneralNames(&(*tbs)->san);
+ free_ExtKeyUsage(&(*tbs)->eku);
+ der_free_heim_integer(&(*tbs)->serial);
+ free_CRLDistributionPoints(&(*tbs)->crldp);
+
+ hx509_name_free(&(*tbs)->subject);
+
+ memset(*tbs, 0, sizeof(**tbs));
+ free(*tbs);
+ *tbs = NULL;
+}
+
+/**
+ * Set the absolute time when the certificate is valid from. If not
+ * set the current time will be used.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param t time the certificated will start to be valid
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+int
+hx509_ca_tbs_set_notBefore(hx509_context context,
+ hx509_ca_tbs tbs,
+ time_t t)
+{
+ tbs->notBefore = t;
+ return 0;
+}
+
+/**
+ * Set the absolute time when the certificate is valid to.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param t time when the certificate will expire
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+int
+hx509_ca_tbs_set_notAfter(hx509_context context,
+ hx509_ca_tbs tbs,
+ time_t t)
+{
+ tbs->notAfter = t;
+ return 0;
+}
+
+/**
+ * Set the relative time when the certificiate is going to expire.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param delta seconds to the certificate is going to expire.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+int
+hx509_ca_tbs_set_notAfter_lifetime(hx509_context context,
+ hx509_ca_tbs tbs,
+ time_t delta)
+{
+ return hx509_ca_tbs_set_notAfter(context, tbs, time(NULL) + delta);
+}
+
+static const struct units templatebits[] = {
+ { "ExtendedKeyUsage", HX509_CA_TEMPLATE_EKU },
+ { "KeyUsage", HX509_CA_TEMPLATE_KU },
+ { "SPKI", HX509_CA_TEMPLATE_SPKI },
+ { "notAfter", HX509_CA_TEMPLATE_NOTAFTER },
+ { "notBefore", HX509_CA_TEMPLATE_NOTBEFORE },
+ { "serial", HX509_CA_TEMPLATE_SERIAL },
+ { "subject", HX509_CA_TEMPLATE_SUBJECT },
+ { NULL, 0 }
+};
+
+/**
+ * Make of template units, use to build flags argument to
+ * hx509_ca_tbs_set_template() with parse_units().
+ *
+ * @return an units structure.
+ *
+ * @ingroup hx509_ca
+ */
+
+const struct units *
+hx509_ca_tbs_template_units(void)
+{
+ return templatebits;
+}
+
+/**
+ * Initialize the to-be-signed certificate object from a template certifiate.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param flags bit field selecting what to copy from the template
+ * certifiate.
+ * @param cert template certificate.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+int
+hx509_ca_tbs_set_template(hx509_context context,
+ hx509_ca_tbs tbs,
+ int flags,
+ hx509_cert cert)
+{
+ int ret;
+
+ if (flags & HX509_CA_TEMPLATE_SUBJECT) {
+ if (tbs->subject)
+ hx509_name_free(&tbs->subject);
+ ret = hx509_cert_get_subject(cert, &tbs->subject);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to get subject from template");
+ return ret;
+ }
+ }
+ if (flags & HX509_CA_TEMPLATE_SERIAL) {
+ der_free_heim_integer(&tbs->serial);
+ ret = hx509_cert_get_serialnumber(cert, &tbs->serial);
+ tbs->flags.serial = !ret;
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to copy serial number");
+ return ret;
+ }
+ }
+ if (flags & HX509_CA_TEMPLATE_NOTBEFORE)
+ tbs->notBefore = hx509_cert_get_notBefore(cert);
+ if (flags & HX509_CA_TEMPLATE_NOTAFTER)
+ tbs->notAfter = hx509_cert_get_notAfter(cert);
+ if (flags & HX509_CA_TEMPLATE_SPKI) {
+ free_SubjectPublicKeyInfo(&tbs->spki);
+ ret = hx509_cert_get_SPKI(context, cert, &tbs->spki);
+ tbs->flags.key = !ret;
+ if (ret)
+ return ret;
+ }
+ if (flags & HX509_CA_TEMPLATE_KU) {
+ KeyUsage ku;
+ ret = _hx509_cert_get_keyusage(context, cert, &ku);
+ if (ret)
+ return ret;
+ tbs->key_usage = KeyUsage2int(ku);
+ }
+ if (flags & HX509_CA_TEMPLATE_EKU) {
+ ExtKeyUsage eku;
+ int i;
+ ret = _hx509_cert_get_eku(context, cert, &eku);
+ if (ret)
+ return ret;
+ for (i = 0; i < eku.len; i++) {
+ ret = hx509_ca_tbs_add_eku(context, tbs, &eku.val[i]);
+ if (ret) {
+ free_ExtKeyUsage(&eku);
+ return ret;
+ }
+ }
+ free_ExtKeyUsage(&eku);
+ }
+ return 0;
+}
+
+/**
+ * Make the to-be-signed certificate object a CA certificate. If the
+ * pathLenConstraint is negative path length constraint is used.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param pathLenConstraint path length constraint, negative, no
+ * constraint.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+int
+hx509_ca_tbs_set_ca(hx509_context context,
+ hx509_ca_tbs tbs,
+ int pathLenConstraint)
+{
+ tbs->flags.ca = 1;
+ tbs->pathLenConstraint = pathLenConstraint;
+ return 0;
+}
+
+/**
+ * Make the to-be-signed certificate object a proxy certificate. If the
+ * pathLenConstraint is negative path length constraint is used.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param pathLenConstraint path length constraint, negative, no
+ * constraint.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+int
+hx509_ca_tbs_set_proxy(hx509_context context,
+ hx509_ca_tbs tbs,
+ int pathLenConstraint)
+{
+ tbs->flags.proxy = 1;
+ tbs->pathLenConstraint = pathLenConstraint;
+ return 0;
+}
+
+
+/**
+ * Make the to-be-signed certificate object a windows domain controller certificate.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+int
+hx509_ca_tbs_set_domaincontroller(hx509_context context,
+ hx509_ca_tbs tbs)
+{
+ tbs->flags.domaincontroller = 1;
+ return 0;
+}
+
+/**
+ * Set the subject public key info (SPKI) in the to-be-signed certificate
+ * object. SPKI is the public key and key related parameters in the
+ * certificate.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param spki subject public key info to use for the to-be-signed certificate object.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+int
+hx509_ca_tbs_set_spki(hx509_context context,
+ hx509_ca_tbs tbs,
+ const SubjectPublicKeyInfo *spki)
+{
+ int ret;
+ free_SubjectPublicKeyInfo(&tbs->spki);
+ ret = copy_SubjectPublicKeyInfo(spki, &tbs->spki);
+ tbs->flags.key = !ret;
+ return ret;
+}
+
+/**
+ * Set the serial number to use for to-be-signed certificate object.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param serialNumber serial number to use for the to-be-signed
+ * certificate object.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+int
+hx509_ca_tbs_set_serialnumber(hx509_context context,
+ hx509_ca_tbs tbs,
+ const heim_integer *serialNumber)
+{
+ int ret;
+ der_free_heim_integer(&tbs->serial);
+ ret = der_copy_heim_integer(serialNumber, &tbs->serial);
+ tbs->flags.serial = !ret;
+ return ret;
+}
+
+/**
+ * An an extended key usage to the to-be-signed certificate object.
+ * Duplicates will detected and not added.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param oid extended key usage to add.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+int
+hx509_ca_tbs_add_eku(hx509_context context,
+ hx509_ca_tbs tbs,
+ const heim_oid *oid)
+{
+ void *ptr;
+ int ret;
+ unsigned i;
+
+ /* search for duplicates */
+ for (i = 0; i < tbs->eku.len; i++) {
+ if (der_heim_oid_cmp(oid, &tbs->eku.val[i]) == 0)
+ return 0;
+ }
+
+ ptr = realloc(tbs->eku.val, sizeof(tbs->eku.val[0]) * (tbs->eku.len + 1));
+ if (ptr == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+ tbs->eku.val = ptr;
+ ret = der_copy_oid(oid, &tbs->eku.val[tbs->eku.len]);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "out of memory");
+ return ret;
+ }
+ tbs->eku.len += 1;
+ return 0;
+}
+
+/**
+ * Add CRL distribution point URI to the to-be-signed certificate
+ * object.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param uri uri to the CRL.
+ * @param issuername name of the issuer.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+int
+hx509_ca_tbs_add_crl_dp_uri(hx509_context context,
+ hx509_ca_tbs tbs,
+ const char *uri,
+ hx509_name issuername)
+{
+ DistributionPoint dp;
+ int ret;
+
+ memset(&dp, 0, sizeof(dp));
+
+ dp.distributionPoint = ecalloc(1, sizeof(*dp.distributionPoint));
+
+ {
+ DistributionPointName name;
+ GeneralName gn;
+ size_t size;
+
+ name.element = choice_DistributionPointName_fullName;
+ name.u.fullName.len = 1;
+ name.u.fullName.val = &gn;
+
+ gn.element = choice_GeneralName_uniformResourceIdentifier;
+ gn.u.uniformResourceIdentifier = rk_UNCONST(uri);
+
+ ASN1_MALLOC_ENCODE(DistributionPointName,
+ dp.distributionPoint->data,
+ dp.distributionPoint->length,
+ &name, &size, ret);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to encoded DistributionPointName");
+ goto out;
+ }
+ if (dp.distributionPoint->length != size)
+ _hx509_abort("internal ASN.1 encoder error");
+ }
+
+ if (issuername) {
+#if 1
+ /**
+ * issuername not supported
+ */
+ hx509_set_error_string(context, 0, EINVAL,
+ "CRLDistributionPoints.name.issuername not yet supported");
+ return EINVAL;
+#else
+ GeneralNames *crlissuer;
+ GeneralName gn;
+ Name n;
+
+ crlissuer = calloc(1, sizeof(*crlissuer));
+ if (crlissuer == NULL) {
+ return ENOMEM;
+ }
+ memset(&gn, 0, sizeof(gn));
+
+ gn.element = choice_GeneralName_directoryName;
+ ret = hx509_name_to_Name(issuername, &n);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "out of memory");
+ goto out;
+ }
+
+ gn.u.directoryName.element = n.element;
+ gn.u.directoryName.u.rdnSequence = n.u.rdnSequence;
+
+ ret = add_GeneralNames(&crlissuer, &gn);
+ free_Name(&n);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "out of memory");
+ goto out;
+ }
+
+ dp.cRLIssuer = &crlissuer;
+#endif
+ }
+
+ ret = add_CRLDistributionPoints(&tbs->crldp, &dp);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "out of memory");
+ goto out;
+ }
+
+out:
+ free_DistributionPoint(&dp);
+
+ return ret;
+}
+
+/**
+ * Add Subject Alternative Name otherName to the to-be-signed
+ * certificate object.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param oid the oid of the OtherName.
+ * @param os data in the other name.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+int
+hx509_ca_tbs_add_san_otherName(hx509_context context,
+ hx509_ca_tbs tbs,
+ const heim_oid *oid,
+ const heim_octet_string *os)
+{
+ GeneralName gn;
+
+ memset(&gn, 0, sizeof(gn));
+ gn.element = choice_GeneralName_otherName;
+ gn.u.otherName.type_id = *oid;
+ gn.u.otherName.value = *os;
+
+ return add_GeneralNames(&tbs->san, &gn);
+}
+
+/**
+ * Add Kerberos Subject Alternative Name to the to-be-signed
+ * certificate object. The principal string is a UTF8 string.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param principal Kerberos principal to add to the certificate.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+int
+hx509_ca_tbs_add_san_pkinit(hx509_context context,
+ hx509_ca_tbs tbs,
+ const char *principal)
+{
+ heim_octet_string os;
+ KRB5PrincipalName p;
+ size_t size;
+ int ret;
+ char *s = NULL;
+
+ memset(&p, 0, sizeof(p));
+
+ /* parse principal */
+ {
+ const char *str;
+ char *q;
+ int n;
+
+ /* count number of component */
+ n = 1;
+ for(str = principal; *str != '\0' && *str != '@'; str++){
+ if(*str=='\\'){
+ if(str[1] == '\0' || str[1] == '@') {
+ ret = HX509_PARSING_NAME_FAILED;
+ hx509_set_error_string(context, 0, ret,
+ "trailing \\ in principal name");
+ goto out;
+ }
+ str++;
+ } else if(*str == '/')
+ n++;
+ }
+ p.principalName.name_string.val =
+ calloc(n, sizeof(*p.principalName.name_string.val));
+ if (p.principalName.name_string.val == NULL) {
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret, "malloc: out of memory");
+ goto out;
+ }
+ p.principalName.name_string.len = n;
+
+ p.principalName.name_type = KRB5_NT_PRINCIPAL;
+ q = s = strdup(principal);
+ if (q == NULL) {
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret, "malloc: out of memory");
+ goto out;
+ }
+ p.realm = strrchr(q, '@');
+ if (p.realm == NULL) {
+ ret = HX509_PARSING_NAME_FAILED;
+ hx509_set_error_string(context, 0, ret, "Missing @ in principal");
+ goto out;
+ };
+ *p.realm++ = '\0';
+
+ n = 0;
+ while (q) {
+ p.principalName.name_string.val[n++] = q;
+ q = strchr(q, '/');
+ if (q)
+ *q++ = '\0';
+ }
+ }
+
+ ASN1_MALLOC_ENCODE(KRB5PrincipalName, os.data, os.length, &p, &size, ret);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+ if (size != os.length)
+ _hx509_abort("internal ASN.1 encoder error");
+
+ ret = hx509_ca_tbs_add_san_otherName(context,
+ tbs,
+ oid_id_pkinit_san(),
+ &os);
+ free(os.data);
+out:
+ if (p.principalName.name_string.val)
+ free (p.principalName.name_string.val);
+ if (s)
+ free(s);
+ return ret;
+}
+
+/*
+ *
+ */
+
+static int
+add_utf8_san(hx509_context context,
+ hx509_ca_tbs tbs,
+ const heim_oid *oid,
+ const char *string)
+{
+ const PKIXXmppAddr ustring = (const PKIXXmppAddr)string;
+ heim_octet_string os;
+ size_t size;
+ int ret;
+
+ os.length = 0;
+ os.data = NULL;
+
+ ASN1_MALLOC_ENCODE(PKIXXmppAddr, os.data, os.length, &ustring, &size, ret);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+ if (size != os.length)
+ _hx509_abort("internal ASN.1 encoder error");
+
+ ret = hx509_ca_tbs_add_san_otherName(context,
+ tbs,
+ oid,
+ &os);
+ free(os.data);
+out:
+ return ret;
+}
+
+/**
+ * Add Microsoft UPN Subject Alternative Name to the to-be-signed
+ * certificate object. The principal string is a UTF8 string.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param principal Microsoft UPN string.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+int
+hx509_ca_tbs_add_san_ms_upn(hx509_context context,
+ hx509_ca_tbs tbs,
+ const char *principal)
+{
+ return add_utf8_san(context, tbs, oid_id_pkinit_ms_san(), principal);
+}
+
+/**
+ * Add a Jabber/XMPP jid Subject Alternative Name to the to-be-signed
+ * certificate object. The jid is an UTF8 string.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param jid string of an a jabber id in UTF8.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+int
+hx509_ca_tbs_add_san_jid(hx509_context context,
+ hx509_ca_tbs tbs,
+ const char *jid)
+{
+ return add_utf8_san(context, tbs, oid_id_pkix_on_xmppAddr(), jid);
+}
+
+
+/**
+ * Add a Subject Alternative Name hostname to to-be-signed certificate
+ * object. A domain match starts with ., an exact match does not.
+ *
+ * Example of a an domain match: .domain.se matches the hostname
+ * host.domain.se.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param dnsname a hostame.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+int
+hx509_ca_tbs_add_san_hostname(hx509_context context,
+ hx509_ca_tbs tbs,
+ const char *dnsname)
+{
+ GeneralName gn;
+
+ memset(&gn, 0, sizeof(gn));
+ gn.element = choice_GeneralName_dNSName;
+ gn.u.dNSName = rk_UNCONST(dnsname);
+
+ return add_GeneralNames(&tbs->san, &gn);
+}
+
+/**
+ * Add a Subject Alternative Name rfc822 (email address) to
+ * to-be-signed certificate object.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param rfc822Name a string to a email address.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+int
+hx509_ca_tbs_add_san_rfc822name(hx509_context context,
+ hx509_ca_tbs tbs,
+ const char *rfc822Name)
+{
+ GeneralName gn;
+
+ memset(&gn, 0, sizeof(gn));
+ gn.element = choice_GeneralName_rfc822Name;
+ gn.u.rfc822Name = rk_UNCONST(rfc822Name);
+
+ return add_GeneralNames(&tbs->san, &gn);
+}
+
+/**
+ * Set the subject name of a to-be-signed certificate object.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param subject the name to set a subject.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+int
+hx509_ca_tbs_set_subject(hx509_context context,
+ hx509_ca_tbs tbs,
+ hx509_name subject)
+{
+ if (tbs->subject)
+ hx509_name_free(&tbs->subject);
+ return hx509_name_copy(context, subject, &tbs->subject);
+}
+
+/**
+ * Expand the the subject name in the to-be-signed certificate object
+ * using hx509_name_expand().
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param env enviroment variable to expand variables in the subject
+ * name, see hx509_env_init().
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+int
+hx509_ca_tbs_subject_expand(hx509_context context,
+ hx509_ca_tbs tbs,
+ hx509_env env)
+{
+ return hx509_name_expand(context, tbs->subject, env);
+}
+
+static int
+add_extension(hx509_context context,
+ TBSCertificate *tbsc,
+ int critical_flag,
+ const heim_oid *oid,
+ const heim_octet_string *data)
+{
+ Extension ext;
+ int ret;
+
+ memset(&ext, 0, sizeof(ext));
+
+ if (critical_flag) {
+ ext.critical = malloc(sizeof(*ext.critical));
+ if (ext.critical == NULL) {
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+ *ext.critical = TRUE;
+ }
+
+ ret = der_copy_oid(oid, &ext.extnID);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+ ret = der_copy_octet_string(data, &ext.extnValue);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+ ret = add_Extensions(tbsc->extensions, &ext);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+out:
+ free_Extension(&ext);
+ return ret;
+}
+
+static int
+build_proxy_prefix(hx509_context context, const Name *issuer, Name *subject)
+{
+ char *tstr;
+ time_t t;
+ int ret;
+
+ ret = copy_Name(issuer, subject);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to copy subject name");
+ return ret;
+ }
+
+ t = time(NULL);
+ asprintf(&tstr, "ts-%lu", (unsigned long)t);
+ if (tstr == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM,
+ "Failed to copy subject name");
+ return ENOMEM;
+ }
+ /* prefix with CN=<ts>,...*/
+ ret = _hx509_name_modify(context, subject, 1, oid_id_at_commonName(), tstr);
+ free(tstr);
+ if (ret)
+ free_Name(subject);
+ return ret;
+}
+
+static int
+ca_sign(hx509_context context,
+ hx509_ca_tbs tbs,
+ hx509_private_key signer,
+ const AuthorityKeyIdentifier *ai,
+ const Name *issuername,
+ hx509_cert *certificate)
+{
+ heim_octet_string data;
+ Certificate c;
+ TBSCertificate *tbsc;
+ size_t size;
+ int ret;
+ const AlgorithmIdentifier *sigalg;
+ time_t notBefore;
+ time_t notAfter;
+ unsigned key_usage;
+
+ sigalg = _hx509_crypto_default_sig_alg;
+
+ memset(&c, 0, sizeof(c));
+
+ /*
+ * Default values are: Valid since 24h ago, valid one year into
+ * the future, KeyUsage digitalSignature and keyEncipherment set,
+ * and keyCertSign for CA certificates.
+ */
+ notBefore = tbs->notBefore;
+ if (notBefore == 0)
+ notBefore = time(NULL) - 3600 * 24;
+ notAfter = tbs->notAfter;
+ if (notAfter == 0)
+ notAfter = time(NULL) + 3600 * 24 * 365;
+
+ key_usage = tbs->key_usage;
+ if (key_usage == 0) {
+ KeyUsage ku;
+ memset(&ku, 0, sizeof(ku));
+ ku.digitalSignature = 1;
+ ku.keyEncipherment = 1;
+ key_usage = KeyUsage2int(ku);
+ }
+
+ if (tbs->flags.ca) {
+ KeyUsage ku;
+ memset(&ku, 0, sizeof(ku));
+ ku.keyCertSign = 1;
+ ku.cRLSign = 1;
+ key_usage |= KeyUsage2int(ku);
+ }
+
+ /*
+ *
+ */
+
+ tbsc = &c.tbsCertificate;
+
+ if (tbs->flags.key == 0) {
+ ret = EINVAL;
+ hx509_set_error_string(context, 0, ret, "No public key set");
+ return ret;
+ }
+ /*
+ * Don't put restrictions on proxy certificate's subject name, it
+ * will be generated below.
+ */
+ if (!tbs->flags.proxy) {
+ if (tbs->subject == NULL) {
+ hx509_set_error_string(context, 0, EINVAL, "No subject name set");
+ return EINVAL;
+ }
+ if (hx509_name_is_null_p(tbs->subject) && tbs->san.len == 0) {
+ hx509_set_error_string(context, 0, EINVAL,
+ "NULL subject and no SubjectAltNames");
+ return EINVAL;
+ }
+ }
+ if (tbs->flags.ca && tbs->flags.proxy) {
+ hx509_set_error_string(context, 0, EINVAL, "Can't be proxy and CA "
+ "at the same time");
+ return EINVAL;
+ }
+ if (tbs->flags.proxy) {
+ if (tbs->san.len > 0) {
+ hx509_set_error_string(context, 0, EINVAL,
+ "Proxy certificate is not allowed "
+ "to have SubjectAltNames");
+ return EINVAL;
+ }
+ }
+
+ /* version [0] Version OPTIONAL, -- EXPLICIT nnn DEFAULT 1, */
+ tbsc->version = calloc(1, sizeof(*tbsc->version));
+ if (tbsc->version == NULL) {
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+ *tbsc->version = rfc3280_version_3;
+ /* serialNumber CertificateSerialNumber, */
+ if (tbs->flags.serial) {
+ ret = der_copy_heim_integer(&tbs->serial, &tbsc->serialNumber);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+ } else {
+ tbsc->serialNumber.length = 20;
+ tbsc->serialNumber.data = malloc(tbsc->serialNumber.length);
+ if (tbsc->serialNumber.data == NULL){
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+ /* XXX diffrent */
+ RAND_bytes(tbsc->serialNumber.data, tbsc->serialNumber.length);
+ ((unsigned char *)tbsc->serialNumber.data)[0] &= 0x7f;
+ }
+ /* signature AlgorithmIdentifier, */
+ ret = copy_AlgorithmIdentifier(sigalg, &tbsc->signature);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Failed to copy sigature alg");
+ goto out;
+ }
+ /* issuer Name, */
+ if (issuername)
+ ret = copy_Name(issuername, &tbsc->issuer);
+ else
+ ret = hx509_name_to_Name(tbs->subject, &tbsc->issuer);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Failed to copy issuer name");
+ goto out;
+ }
+ /* validity Validity, */
+ tbsc->validity.notBefore.element = choice_Time_generalTime;
+ tbsc->validity.notBefore.u.generalTime = notBefore;
+ tbsc->validity.notAfter.element = choice_Time_generalTime;
+ tbsc->validity.notAfter.u.generalTime = notAfter;
+ /* subject Name, */
+ if (tbs->flags.proxy) {
+ ret = build_proxy_prefix(context, &tbsc->issuer, &tbsc->subject);
+ if (ret)
+ goto out;
+ } else {
+ ret = hx509_name_to_Name(tbs->subject, &tbsc->subject);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to copy subject name");
+ goto out;
+ }
+ }
+ /* subjectPublicKeyInfo SubjectPublicKeyInfo, */
+ ret = copy_SubjectPublicKeyInfo(&tbs->spki, &tbsc->subjectPublicKeyInfo);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Failed to copy spki");
+ goto out;
+ }
+ /* issuerUniqueID [1] IMPLICIT BIT STRING OPTIONAL */
+ /* subjectUniqueID [2] IMPLICIT BIT STRING OPTIONAL */
+ /* extensions [3] EXPLICIT Extensions OPTIONAL */
+ tbsc->extensions = calloc(1, sizeof(*tbsc->extensions));
+ if (tbsc->extensions == NULL) {
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+
+ /* Add the text BMP string Domaincontroller to the cert */
+ if (tbs->flags.domaincontroller) {
+ data.data = rk_UNCONST("\x1e\x20\x00\x44\x00\x6f\x00\x6d"
+ "\x00\x61\x00\x69\x00\x6e\x00\x43"
+ "\x00\x6f\x00\x6e\x00\x74\x00\x72"
+ "\x00\x6f\x00\x6c\x00\x6c\x00\x65"
+ "\x00\x72");
+ data.length = 34;
+
+ ret = add_extension(context, tbsc, 0,
+ oid_id_ms_cert_enroll_domaincontroller(),
+ &data);
+ if (ret)
+ goto out;
+ }
+
+ /* add KeyUsage */
+ {
+ KeyUsage ku;
+
+ ku = int2KeyUsage(key_usage);
+ ASN1_MALLOC_ENCODE(KeyUsage, data.data, data.length, &ku, &size, ret);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+ if (size != data.length)
+ _hx509_abort("internal ASN.1 encoder error");
+ ret = add_extension(context, tbsc, 1,
+ oid_id_x509_ce_keyUsage(), &data);
+ free(data.data);
+ if (ret)
+ goto out;
+ }
+
+ /* add ExtendedKeyUsage */
+ if (tbs->eku.len > 0) {
+ ASN1_MALLOC_ENCODE(ExtKeyUsage, data.data, data.length,
+ &tbs->eku, &size, ret);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+ if (size != data.length)
+ _hx509_abort("internal ASN.1 encoder error");
+ ret = add_extension(context, tbsc, 0,
+ oid_id_x509_ce_extKeyUsage(), &data);
+ free(data.data);
+ if (ret)
+ goto out;
+ }
+
+ /* add Subject Alternative Name */
+ if (tbs->san.len > 0) {
+ ASN1_MALLOC_ENCODE(GeneralNames, data.data, data.length,
+ &tbs->san, &size, ret);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+ if (size != data.length)
+ _hx509_abort("internal ASN.1 encoder error");
+ ret = add_extension(context, tbsc, 0,
+ oid_id_x509_ce_subjectAltName(),
+ &data);
+ free(data.data);
+ if (ret)
+ goto out;
+ }
+
+ /* Add Authority Key Identifier */
+ if (ai) {
+ ASN1_MALLOC_ENCODE(AuthorityKeyIdentifier, data.data, data.length,
+ ai, &size, ret);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+ if (size != data.length)
+ _hx509_abort("internal ASN.1 encoder error");
+ ret = add_extension(context, tbsc, 0,
+ oid_id_x509_ce_authorityKeyIdentifier(),
+ &data);
+ free(data.data);
+ if (ret)
+ goto out;
+ }
+
+ /* Add Subject Key Identifier */
+ {
+ SubjectKeyIdentifier si;
+ unsigned char hash[SHA_DIGEST_LENGTH];
+
+ {
+ SHA_CTX m;
+
+ SHA1_Init(&m);
+ SHA1_Update(&m, tbs->spki.subjectPublicKey.data,
+ tbs->spki.subjectPublicKey.length / 8);
+ SHA1_Final (hash, &m);
+ }
+
+ si.data = hash;
+ si.length = sizeof(hash);
+
+ ASN1_MALLOC_ENCODE(SubjectKeyIdentifier, data.data, data.length,
+ &si, &size, ret);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+ if (size != data.length)
+ _hx509_abort("internal ASN.1 encoder error");
+ ret = add_extension(context, tbsc, 0,
+ oid_id_x509_ce_subjectKeyIdentifier(),
+ &data);
+ free(data.data);
+ if (ret)
+ goto out;
+ }
+
+ /* Add BasicConstraints */
+ {
+ BasicConstraints bc;
+ int aCA = 1;
+ unsigned int path;
+
+ memset(&bc, 0, sizeof(bc));
+
+ if (tbs->flags.ca) {
+ bc.cA = &aCA;
+ if (tbs->pathLenConstraint >= 0) {
+ path = tbs->pathLenConstraint;
+ bc.pathLenConstraint = &path;
+ }
+ }
+
+ ASN1_MALLOC_ENCODE(BasicConstraints, data.data, data.length,
+ &bc, &size, ret);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+ if (size != data.length)
+ _hx509_abort("internal ASN.1 encoder error");
+ /* Critical if this is a CA */
+ ret = add_extension(context, tbsc, tbs->flags.ca,
+ oid_id_x509_ce_basicConstraints(),
+ &data);
+ free(data.data);
+ if (ret)
+ goto out;
+ }
+
+ /* add Proxy */
+ if (tbs->flags.proxy) {
+ ProxyCertInfo info;
+
+ memset(&info, 0, sizeof(info));
+
+ if (tbs->pathLenConstraint >= 0) {
+ info.pCPathLenConstraint =
+ malloc(sizeof(*info.pCPathLenConstraint));
+ if (info.pCPathLenConstraint == NULL) {
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+ *info.pCPathLenConstraint = tbs->pathLenConstraint;
+ }
+
+ ret = der_copy_oid(oid_id_pkix_ppl_inheritAll(),
+ &info.proxyPolicy.policyLanguage);
+ if (ret) {
+ free_ProxyCertInfo(&info);
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+
+ ASN1_MALLOC_ENCODE(ProxyCertInfo, data.data, data.length,
+ &info, &size, ret);
+ free_ProxyCertInfo(&info);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+ if (size != data.length)
+ _hx509_abort("internal ASN.1 encoder error");
+ ret = add_extension(context, tbsc, 0,
+ oid_id_pkix_pe_proxyCertInfo(),
+ &data);
+ free(data.data);
+ if (ret)
+ goto out;
+ }
+
+ if (tbs->crldp.len) {
+
+ ASN1_MALLOC_ENCODE(CRLDistributionPoints, data.data, data.length,
+ &tbs->crldp, &size, ret);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+ if (size != data.length)
+ _hx509_abort("internal ASN.1 encoder error");
+ ret = add_extension(context, tbsc, FALSE,
+ oid_id_x509_ce_cRLDistributionPoints(),
+ &data);
+ free(data.data);
+ if (ret)
+ goto out;
+ }
+
+ ASN1_MALLOC_ENCODE(TBSCertificate, data.data, data.length,tbsc, &size, ret);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "malloc out of memory");
+ goto out;
+ }
+ if (data.length != size)
+ _hx509_abort("internal ASN.1 encoder error");
+
+ ret = _hx509_create_signature_bitstring(context,
+ signer,
+ sigalg,
+ &data,
+ &c.signatureAlgorithm,
+ &c.signatureValue);
+ free(data.data);
+ if (ret)
+ goto out;
+
+ ret = hx509_cert_init(context, &c, certificate);
+ if (ret)
+ goto out;
+
+ free_Certificate(&c);
+
+ return 0;
+
+out:
+ free_Certificate(&c);
+ return ret;
+}
+
+static int
+get_AuthorityKeyIdentifier(hx509_context context,
+ const Certificate *certificate,
+ AuthorityKeyIdentifier *ai)
+{
+ SubjectKeyIdentifier si;
+ int ret;
+
+ ret = _hx509_find_extension_subject_key_id(certificate, &si);
+ if (ret == 0) {
+ ai->keyIdentifier = calloc(1, sizeof(*ai->keyIdentifier));
+ if (ai->keyIdentifier == NULL) {
+ free_SubjectKeyIdentifier(&si);
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+ ret = der_copy_octet_string(&si, ai->keyIdentifier);
+ free_SubjectKeyIdentifier(&si);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+ } else {
+ GeneralNames gns;
+ GeneralName gn;
+ Name name;
+
+ memset(&gn, 0, sizeof(gn));
+ memset(&gns, 0, sizeof(gns));
+ memset(&name, 0, sizeof(name));
+
+ ai->authorityCertIssuer =
+ calloc(1, sizeof(*ai->authorityCertIssuer));
+ if (ai->authorityCertIssuer == NULL) {
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+ ai->authorityCertSerialNumber =
+ calloc(1, sizeof(*ai->authorityCertSerialNumber));
+ if (ai->authorityCertSerialNumber == NULL) {
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+
+ /*
+ * XXX unbreak when asn1 compiler handle IMPLICIT
+ *
+ * This is so horrible.
+ */
+
+ ret = copy_Name(&certificate->tbsCertificate.subject, &name);
+ if (ai->authorityCertSerialNumber == NULL) {
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+
+ memset(&gn, 0, sizeof(gn));
+ gn.element = choice_GeneralName_directoryName;
+ gn.u.directoryName.element =
+ choice_GeneralName_directoryName_rdnSequence;
+ gn.u.directoryName.u.rdnSequence = name.u.rdnSequence;
+
+ ret = add_GeneralNames(&gns, &gn);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+
+ ai->authorityCertIssuer->val = gns.val;
+ ai->authorityCertIssuer->len = gns.len;
+
+ ret = der_copy_heim_integer(&certificate->tbsCertificate.serialNumber,
+ ai->authorityCertSerialNumber);
+ if (ai->authorityCertSerialNumber == NULL) {
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+ }
+out:
+ if (ret)
+ free_AuthorityKeyIdentifier(ai);
+ return ret;
+}
+
+
+/**
+ * Sign a to-be-signed certificate object with a issuer certificate.
+ *
+ * The caller needs to at least have called the following functions on the
+ * to-be-signed certificate object:
+ * - hx509_ca_tbs_init()
+ * - hx509_ca_tbs_set_subject()
+ * - hx509_ca_tbs_set_spki()
+ *
+ * When done the to-be-signed certificate object should be freed with
+ * hx509_ca_tbs_free().
+ *
+ * When creating self-signed certificate use hx509_ca_sign_self() instead.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param signer the CA certificate object to sign with (need private key).
+ * @param certificate return cerificate, free with hx509_cert_free().
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+int
+hx509_ca_sign(hx509_context context,
+ hx509_ca_tbs tbs,
+ hx509_cert signer,
+ hx509_cert *certificate)
+{
+ const Certificate *signer_cert;
+ AuthorityKeyIdentifier ai;
+ int ret;
+
+ memset(&ai, 0, sizeof(ai));
+
+ signer_cert = _hx509_get_cert(signer);
+
+ ret = get_AuthorityKeyIdentifier(context, signer_cert, &ai);
+ if (ret)
+ goto out;
+
+ ret = ca_sign(context,
+ tbs,
+ _hx509_cert_private_key(signer),
+ &ai,
+ &signer_cert->tbsCertificate.subject,
+ certificate);
+
+out:
+ free_AuthorityKeyIdentifier(&ai);
+
+ return ret;
+}
+
+/**
+ * Work just like hx509_ca_sign() but signs it-self.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param signer private key to sign with.
+ * @param certificate return cerificate, free with hx509_cert_free().
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+int
+hx509_ca_sign_self(hx509_context context,
+ hx509_ca_tbs tbs,
+ hx509_private_key signer,
+ hx509_cert *certificate)
+{
+ return ca_sign(context,
+ tbs,
+ signer,
+ NULL,
+ NULL,
+ certificate);
+}
diff --git a/source4/heimdal/lib/hx509/cert.c b/source4/heimdal/lib/hx509/cert.c
new file mode 100644
index 0000000000..121847faaa
--- /dev/null
+++ b/source4/heimdal/lib/hx509/cert.c
@@ -0,0 +1,3360 @@
+/*
+ * Copyright (c) 2004 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hx_locl.h"
+RCSID("$Id$");
+#include "crypto-headers.h"
+#include <rtbl.h>
+
+/**
+ * @page page_cert The basic certificate
+ *
+ * The basic hx509 cerificate object in hx509 is hx509_cert. The
+ * hx509_cert object is representing one X509/PKIX certificate and
+ * associated attributes; like private key, friendly name, etc.
+ *
+ * A hx509_cert object is usully found via the keyset interfaces (@ref
+ * page_keyset), but its also possible to create a certificate
+ * directly from a parsed object with hx509_cert_init() and
+ * hx509_cert_init_data().
+ *
+ * See the library functions here: @ref hx509_cert
+ */
+
+struct hx509_verify_ctx_data {
+ hx509_certs trust_anchors;
+ int flags;
+#define HX509_VERIFY_CTX_F_TIME_SET 1
+#define HX509_VERIFY_CTX_F_ALLOW_PROXY_CERTIFICATE 2
+#define HX509_VERIFY_CTX_F_REQUIRE_RFC3280 4
+#define HX509_VERIFY_CTX_F_CHECK_TRUST_ANCHORS 8
+#define HX509_VERIFY_CTX_F_NO_DEFAULT_ANCHORS 16
+ time_t time_now;
+ unsigned int max_depth;
+#define HX509_VERIFY_MAX_DEPTH 30
+ hx509_revoke_ctx revoke_ctx;
+};
+
+#define REQUIRE_RFC3280(ctx) ((ctx)->flags & HX509_VERIFY_CTX_F_REQUIRE_RFC3280)
+#define CHECK_TA(ctx) ((ctx)->flags & HX509_VERIFY_CTX_F_CHECK_TRUST_ANCHORS)
+#define ALLOW_DEF_TA(ctx) (((ctx)->flags & HX509_VERIFY_CTX_F_NO_DEFAULT_ANCHORS) == 0)
+
+struct _hx509_cert_attrs {
+ size_t len;
+ hx509_cert_attribute *val;
+};
+
+struct hx509_cert_data {
+ unsigned int ref;
+ char *friendlyname;
+ Certificate *data;
+ hx509_private_key private_key;
+ struct _hx509_cert_attrs attrs;
+ hx509_name basename;
+ _hx509_cert_release_func release;
+ void *ctx;
+};
+
+typedef struct hx509_name_constraints {
+ NameConstraints *val;
+ size_t len;
+} hx509_name_constraints;
+
+#define GeneralSubtrees_SET(g,var) \
+ (g)->len = (var)->len, (g)->val = (var)->val;
+
+/**
+ * Creates a hx509 context that most functions in the library
+ * uses. The context is only allowed to be used by one thread at each
+ * moment. Free the context with hx509_context_free().
+ *
+ * @param context Returns a pointer to new hx509 context.
+ *
+ * @return Returns an hx509 error code.
+ *
+ * @ingroup hx509
+ */
+
+int
+hx509_context_init(hx509_context *context)
+{
+ *context = calloc(1, sizeof(**context));
+ if (*context == NULL)
+ return ENOMEM;
+
+ _hx509_ks_null_register(*context);
+ _hx509_ks_mem_register(*context);
+ _hx509_ks_file_register(*context);
+ _hx509_ks_pkcs12_register(*context);
+ _hx509_ks_pkcs11_register(*context);
+ _hx509_ks_dir_register(*context);
+ _hx509_ks_keychain_register(*context);
+
+ ENGINE_add_conf_module();
+ OpenSSL_add_all_algorithms();
+
+ (*context)->ocsp_time_diff = HX509_DEFAULT_OCSP_TIME_DIFF;
+
+ initialize_hx_error_table_r(&(*context)->et_list);
+ initialize_asn1_error_table_r(&(*context)->et_list);
+
+#ifdef HX509_DEFAULT_ANCHORS
+ (void)hx509_certs_init(*context, HX509_DEFAULT_ANCHORS, 0,
+ NULL, &(*context)->default_trust_anchors);
+#endif
+
+ return 0;
+}
+
+/**
+ * Selects if the hx509_revoke_verify() function is going to require
+ * the existans of a revokation method (OCSP, CRL) or not. Note that
+ * hx509_verify_path(), hx509_cms_verify_signed(), and other function
+ * call hx509_revoke_verify().
+ *
+ * @param context hx509 context to change the flag for.
+ * @param flag zero, revokation method required, non zero missing
+ * revokation method ok
+ *
+ * @ingroup hx509_verify
+ */
+
+void
+hx509_context_set_missing_revoke(hx509_context context, int flag)
+{
+ if (flag)
+ context->flags |= HX509_CTX_VERIFY_MISSING_OK;
+ else
+ context->flags &= ~HX509_CTX_VERIFY_MISSING_OK;
+}
+
+/**
+ * Free the context allocated by hx509_context_init().
+ *
+ * @param context context to be freed.
+ *
+ * @ingroup hx509
+ */
+
+void
+hx509_context_free(hx509_context *context)
+{
+ hx509_clear_error_string(*context);
+ if ((*context)->ks_ops) {
+ free((*context)->ks_ops);
+ (*context)->ks_ops = NULL;
+ }
+ (*context)->ks_num_ops = 0;
+ free_error_table ((*context)->et_list);
+ if ((*context)->querystat)
+ free((*context)->querystat);
+ memset(*context, 0, sizeof(**context));
+ free(*context);
+ *context = NULL;
+}
+
+/*
+ *
+ */
+
+Certificate *
+_hx509_get_cert(hx509_cert cert)
+{
+ return cert->data;
+}
+
+/*
+ *
+ */
+
+int
+_hx509_cert_get_version(const Certificate *t)
+{
+ return t->tbsCertificate.version ? *t->tbsCertificate.version + 1 : 1;
+}
+
+/**
+ * Allocate and init an hx509 certificate object from the decoded
+ * certificate `c´.
+ *
+ * @param context A hx509 context.
+ * @param c
+ * @param cert
+ *
+ * @return Returns an hx509 error code.
+ *
+ * @ingroup hx509_cert
+ */
+
+int
+hx509_cert_init(hx509_context context, const Certificate *c, hx509_cert *cert)
+{
+ int ret;
+
+ *cert = malloc(sizeof(**cert));
+ if (*cert == NULL)
+ return ENOMEM;
+ (*cert)->ref = 1;
+ (*cert)->friendlyname = NULL;
+ (*cert)->attrs.len = 0;
+ (*cert)->attrs.val = NULL;
+ (*cert)->private_key = NULL;
+ (*cert)->basename = NULL;
+ (*cert)->release = NULL;
+ (*cert)->ctx = NULL;
+
+ (*cert)->data = calloc(1, sizeof(*(*cert)->data));
+ if ((*cert)->data == NULL) {
+ free(*cert);
+ return ENOMEM;
+ }
+ ret = copy_Certificate(c, (*cert)->data);
+ if (ret) {
+ free((*cert)->data);
+ free(*cert);
+ *cert = NULL;
+ }
+ return ret;
+}
+
+/**
+ * Just like hx509_cert_init(), but instead of a decode certificate
+ * takes an pointer and length to a memory region that contains a
+ * DER/BER encoded certificate.
+ *
+ * If the memory region doesn't contain just the certificate and
+ * nothing more the function will fail with
+ * HX509_EXTRA_DATA_AFTER_STRUCTURE.
+ *
+ * @param context A hx509 context.
+ * @param ptr pointer to memory region containing encoded certificate.
+ * @param len length of memory region.
+ * @param cert a return pointer to a hx509 certificate object, will
+ * contain NULL on error.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
+int
+hx509_cert_init_data(hx509_context context,
+ const void *ptr,
+ size_t len,
+ hx509_cert *cert)
+{
+ Certificate t;
+ size_t size;
+ int ret;
+
+ ret = decode_Certificate(ptr, len, &t, &size);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Failed to decode certificate");
+ return ret;
+ }
+ if (size != len) {
+ hx509_set_error_string(context, 0, HX509_EXTRA_DATA_AFTER_STRUCTURE,
+ "Extra data after certificate");
+ return HX509_EXTRA_DATA_AFTER_STRUCTURE;
+ }
+
+ ret = hx509_cert_init(context, &t, cert);
+ free_Certificate(&t);
+ return ret;
+}
+
+void
+_hx509_cert_set_release(hx509_cert cert,
+ _hx509_cert_release_func release,
+ void *ctx)
+{
+ cert->release = release;
+ cert->ctx = ctx;
+}
+
+
+/* Doesn't make a copy of `private_key'. */
+
+int
+_hx509_cert_assign_key(hx509_cert cert, hx509_private_key private_key)
+{
+ if (cert->private_key)
+ _hx509_private_key_free(&cert->private_key);
+ cert->private_key = _hx509_private_key_ref(private_key);
+ return 0;
+}
+
+/**
+ * Free reference to the hx509 certificate object, if the refcounter
+ * reaches 0, the object if freed. Its allowed to pass in NULL.
+ *
+ * @param cert the cert to free.
+ *
+ * @ingroup hx509_cert
+ */
+
+void
+hx509_cert_free(hx509_cert cert)
+{
+ int i;
+
+ if (cert == NULL)
+ return;
+
+ if (cert->ref <= 0)
+ _hx509_abort("cert refcount <= 0 on free");
+ if (--cert->ref > 0)
+ return;
+
+ if (cert->release)
+ (cert->release)(cert, cert->ctx);
+
+ if (cert->private_key)
+ _hx509_private_key_free(&cert->private_key);
+
+ free_Certificate(cert->data);
+ free(cert->data);
+
+ for (i = 0; i < cert->attrs.len; i++) {
+ der_free_octet_string(&cert->attrs.val[i]->data);
+ der_free_oid(&cert->attrs.val[i]->oid);
+ free(cert->attrs.val[i]);
+ }
+ free(cert->attrs.val);
+ free(cert->friendlyname);
+ if (cert->basename)
+ hx509_name_free(&cert->basename);
+ memset(cert, 0, sizeof(cert));
+ free(cert);
+}
+
+/**
+ * Add a reference to a hx509 certificate object.
+ *
+ * @param cert a pointer to an hx509 certificate object.
+ *
+ * @return the same object as is passed in.
+ *
+ * @ingroup hx509_cert
+ */
+
+hx509_cert
+hx509_cert_ref(hx509_cert cert)
+{
+ if (cert == NULL)
+ return NULL;
+ if (cert->ref <= 0)
+ _hx509_abort("cert refcount <= 0");
+ cert->ref++;
+ if (cert->ref == 0)
+ _hx509_abort("cert refcount == 0");
+ return cert;
+}
+
+/**
+ * Allocate an verification context that is used fo control the
+ * verification process.
+ *
+ * @param context A hx509 context.
+ * @param ctx returns a pointer to a hx509_verify_ctx object.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_verify
+ */
+
+int
+hx509_verify_init_ctx(hx509_context context, hx509_verify_ctx *ctx)
+{
+ hx509_verify_ctx c;
+
+ c = calloc(1, sizeof(*c));
+ if (c == NULL)
+ return ENOMEM;
+
+ c->max_depth = HX509_VERIFY_MAX_DEPTH;
+
+ *ctx = c;
+
+ return 0;
+}
+
+/**
+ * Free an hx509 verification context.
+ *
+ * @param ctx the context to be freed.
+ *
+ * @ingroup hx509_verify
+ */
+
+void
+hx509_verify_destroy_ctx(hx509_verify_ctx ctx)
+{
+ if (ctx) {
+ hx509_certs_free(&ctx->trust_anchors);
+ hx509_revoke_free(&ctx->revoke_ctx);
+ memset(ctx, 0, sizeof(*ctx));
+ }
+ free(ctx);
+}
+
+/**
+ * Set the trust anchors in the verification context, makes an
+ * reference to the keyset, so the consumer can free the keyset
+ * independent of the destruction of the verification context (ctx).
+ *
+ * @param ctx a verification context
+ * @param set a keyset containing the trust anchors.
+ *
+ * @ingroup hx509_verify
+ */
+
+void
+hx509_verify_attach_anchors(hx509_verify_ctx ctx, hx509_certs set)
+{
+ ctx->trust_anchors = _hx509_certs_ref(set);
+}
+
+/**
+ * Attach an revocation context to the verfication context, , makes an
+ * reference to the revoke context, so the consumer can free the
+ * revoke context independent of the destruction of the verification
+ * context. If there is no revoke context, the verification process is
+ * NOT going to check any verification status.
+ *
+ * @param ctx a verification context.
+ * @param revoke_ctx a revoke context.
+ *
+ * @ingroup hx509_verify
+ */
+
+void
+hx509_verify_attach_revoke(hx509_verify_ctx ctx, hx509_revoke_ctx revoke_ctx)
+{
+ if (ctx->revoke_ctx)
+ hx509_revoke_free(&ctx->revoke_ctx);
+ ctx->revoke_ctx = _hx509_revoke_ref(revoke_ctx);
+}
+
+/**
+ * Set the clock time the the verification process is going to
+ * use. Used to check certificate in the past and future time. If not
+ * set the current time will be used.
+ *
+ * @param ctx a verification context.
+ * @param t the time the verifiation is using.
+ *
+ *
+ * @ingroup hx509_verify
+ */
+
+void
+hx509_verify_set_time(hx509_verify_ctx ctx, time_t t)
+{
+ ctx->flags |= HX509_VERIFY_CTX_F_TIME_SET;
+ ctx->time_now = t;
+}
+
+time_t
+_hx509_verify_get_time(hx509_verify_ctx ctx)
+{
+ return ctx->time_now;
+}
+
+/**
+ * Set the maximum depth of the certificate chain that the path
+ * builder is going to try.
+ *
+ * @param ctx a verification context
+ * @param max_depth maxium depth of the certificate chain, include
+ * trust anchor.
+ *
+ * @ingroup hx509_verify
+ */
+
+void
+hx509_verify_set_max_depth(hx509_verify_ctx ctx, unsigned int max_depth)
+{
+ ctx->max_depth = max_depth;
+}
+
+/**
+ * Allow or deny the use of proxy certificates
+ *
+ * @param ctx a verification context
+ * @param boolean if non zero, allow proxy certificates.
+ *
+ * @ingroup hx509_verify
+ */
+
+void
+hx509_verify_set_proxy_certificate(hx509_verify_ctx ctx, int boolean)
+{
+ if (boolean)
+ ctx->flags |= HX509_VERIFY_CTX_F_ALLOW_PROXY_CERTIFICATE;
+ else
+ ctx->flags &= ~HX509_VERIFY_CTX_F_ALLOW_PROXY_CERTIFICATE;
+}
+
+/**
+ * Select strict RFC3280 verification of certificiates. This means
+ * checking key usage on CA certificates, this will make version 1
+ * certificiates unuseable.
+ *
+ * @param ctx a verification context
+ * @param boolean if non zero, use strict verification.
+ *
+ * @ingroup hx509_verify
+ */
+
+void
+hx509_verify_set_strict_rfc3280_verification(hx509_verify_ctx ctx, int boolean)
+{
+ if (boolean)
+ ctx->flags |= HX509_VERIFY_CTX_F_REQUIRE_RFC3280;
+ else
+ ctx->flags &= ~HX509_VERIFY_CTX_F_REQUIRE_RFC3280;
+}
+
+/**
+ * Allow using the operating system builtin trust anchors if no other
+ * trust anchors are configured.
+ *
+ * @param ctx a verification context
+ * @param boolean if non zero, useing the operating systems builtin
+ * trust anchors.
+ *
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
+void
+hx509_verify_ctx_f_allow_default_trustanchors(hx509_verify_ctx ctx, int boolean)
+{
+ if (boolean)
+ ctx->flags &= ~HX509_VERIFY_CTX_F_NO_DEFAULT_ANCHORS;
+ else
+ ctx->flags |= HX509_VERIFY_CTX_F_NO_DEFAULT_ANCHORS;
+}
+
+static const Extension *
+find_extension(const Certificate *cert, const heim_oid *oid, int *idx)
+{
+ const TBSCertificate *c = &cert->tbsCertificate;
+
+ if (c->version == NULL || *c->version < 2 || c->extensions == NULL)
+ return NULL;
+
+ for (;*idx < c->extensions->len; (*idx)++) {
+ if (der_heim_oid_cmp(&c->extensions->val[*idx].extnID, oid) == 0)
+ return &c->extensions->val[(*idx)++];
+ }
+ return NULL;
+}
+
+static int
+find_extension_auth_key_id(const Certificate *subject,
+ AuthorityKeyIdentifier *ai)
+{
+ const Extension *e;
+ size_t size;
+ int i = 0;
+
+ memset(ai, 0, sizeof(*ai));
+
+ e = find_extension(subject, oid_id_x509_ce_authorityKeyIdentifier(), &i);
+ if (e == NULL)
+ return HX509_EXTENSION_NOT_FOUND;
+
+ return decode_AuthorityKeyIdentifier(e->extnValue.data,
+ e->extnValue.length,
+ ai, &size);
+}
+
+int
+_hx509_find_extension_subject_key_id(const Certificate *issuer,
+ SubjectKeyIdentifier *si)
+{
+ const Extension *e;
+ size_t size;
+ int i = 0;
+
+ memset(si, 0, sizeof(*si));
+
+ e = find_extension(issuer, oid_id_x509_ce_subjectKeyIdentifier(), &i);
+ if (e == NULL)
+ return HX509_EXTENSION_NOT_FOUND;
+
+ return decode_SubjectKeyIdentifier(e->extnValue.data,
+ e->extnValue.length,
+ si, &size);
+}
+
+static int
+find_extension_name_constraints(const Certificate *subject,
+ NameConstraints *nc)
+{
+ const Extension *e;
+ size_t size;
+ int i = 0;
+
+ memset(nc, 0, sizeof(*nc));
+
+ e = find_extension(subject, oid_id_x509_ce_nameConstraints(), &i);
+ if (e == NULL)
+ return HX509_EXTENSION_NOT_FOUND;
+
+ return decode_NameConstraints(e->extnValue.data,
+ e->extnValue.length,
+ nc, &size);
+}
+
+static int
+find_extension_subject_alt_name(const Certificate *cert, int *i,
+ GeneralNames *sa)
+{
+ const Extension *e;
+ size_t size;
+
+ memset(sa, 0, sizeof(*sa));
+
+ e = find_extension(cert, oid_id_x509_ce_subjectAltName(), i);
+ if (e == NULL)
+ return HX509_EXTENSION_NOT_FOUND;
+
+ return decode_GeneralNames(e->extnValue.data,
+ e->extnValue.length,
+ sa, &size);
+}
+
+static int
+find_extension_eku(const Certificate *cert, ExtKeyUsage *eku)
+{
+ const Extension *e;
+ size_t size;
+ int i = 0;
+
+ memset(eku, 0, sizeof(*eku));
+
+ e = find_extension(cert, oid_id_x509_ce_extKeyUsage(), &i);
+ if (e == NULL)
+ return HX509_EXTENSION_NOT_FOUND;
+
+ return decode_ExtKeyUsage(e->extnValue.data,
+ e->extnValue.length,
+ eku, &size);
+}
+
+static int
+add_to_list(hx509_octet_string_list *list, const heim_octet_string *entry)
+{
+ void *p;
+ int ret;
+
+ p = realloc(list->val, (list->len + 1) * sizeof(list->val[0]));
+ if (p == NULL)
+ return ENOMEM;
+ list->val = p;
+ ret = der_copy_octet_string(entry, &list->val[list->len]);
+ if (ret)
+ return ret;
+ list->len++;
+ return 0;
+}
+
+/**
+ * Free a list of octet strings returned by another hx509 library
+ * function.
+ *
+ * @param list list to be freed.
+ *
+ * @ingroup hx509_misc
+ */
+
+void
+hx509_free_octet_string_list(hx509_octet_string_list *list)
+{
+ int i;
+ for (i = 0; i < list->len; i++)
+ der_free_octet_string(&list->val[i]);
+ free(list->val);
+ list->val = NULL;
+ list->len = 0;
+}
+
+/**
+ * Return a list of subjectAltNames specified by oid in the
+ * certificate. On error the
+ *
+ * The returned list of octet string should be freed with
+ * hx509_free_octet_string_list().
+ *
+ * @param context A hx509 context.
+ * @param cert a hx509 certificate object.
+ * @param oid an oid to for SubjectAltName.
+ * @param list list of matching SubjectAltName.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
+int
+hx509_cert_find_subjectAltName_otherName(hx509_context context,
+ hx509_cert cert,
+ const heim_oid *oid,
+ hx509_octet_string_list *list)
+{
+ GeneralNames sa;
+ int ret, i, j;
+
+ list->val = NULL;
+ list->len = 0;
+
+ i = 0;
+ while (1) {
+ ret = find_extension_subject_alt_name(_hx509_get_cert(cert), &i, &sa);
+ i++;
+ if (ret == HX509_EXTENSION_NOT_FOUND) {
+ ret = 0;
+ break;
+ } else if (ret != 0) {
+ hx509_set_error_string(context, 0, ret, "Error searching for SAN");
+ hx509_free_octet_string_list(list);
+ return ret;
+ }
+
+ for (j = 0; j < sa.len; j++) {
+ if (sa.val[j].element == choice_GeneralName_otherName &&
+ der_heim_oid_cmp(&sa.val[j].u.otherName.type_id, oid) == 0)
+ {
+ ret = add_to_list(list, &sa.val[j].u.otherName.value);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Error adding an exra SAN to "
+ "return list");
+ hx509_free_octet_string_list(list);
+ free_GeneralNames(&sa);
+ return ret;
+ }
+ }
+ }
+ free_GeneralNames(&sa);
+ }
+ return 0;
+}
+
+
+static int
+check_key_usage(hx509_context context, const Certificate *cert,
+ unsigned flags, int req_present)
+{
+ const Extension *e;
+ KeyUsage ku;
+ size_t size;
+ int ret, i = 0;
+ unsigned ku_flags;
+
+ if (_hx509_cert_get_version(cert) < 3)
+ return 0;
+
+ e = find_extension(cert, oid_id_x509_ce_keyUsage(), &i);
+ if (e == NULL) {
+ if (req_present) {
+ hx509_set_error_string(context, 0, HX509_KU_CERT_MISSING,
+ "Required extension key "
+ "usage missing from certifiate");
+ return HX509_KU_CERT_MISSING;
+ }
+ return 0;
+ }
+
+ ret = decode_KeyUsage(e->extnValue.data, e->extnValue.length, &ku, &size);
+ if (ret)
+ return ret;
+ ku_flags = KeyUsage2int(ku);
+ if ((ku_flags & flags) != flags) {
+ unsigned missing = (~ku_flags) & flags;
+ char buf[256], *name;
+
+ unparse_flags(missing, asn1_KeyUsage_units(), buf, sizeof(buf));
+ _hx509_unparse_Name(&cert->tbsCertificate.subject, &name);
+ hx509_set_error_string(context, 0, HX509_KU_CERT_MISSING,
+ "Key usage %s required but missing "
+ "from certifiate %s", buf, name);
+ free(name);
+ return HX509_KU_CERT_MISSING;
+ }
+ return 0;
+}
+
+/*
+ * Return 0 on matching key usage 'flags' for 'cert', otherwise return
+ * an error code. If 'req_present' the existance is required of the
+ * KeyUsage extension.
+ */
+
+int
+_hx509_check_key_usage(hx509_context context, hx509_cert cert,
+ unsigned flags, int req_present)
+{
+ return check_key_usage(context, _hx509_get_cert(cert), flags, req_present);
+}
+
+enum certtype { PROXY_CERT, EE_CERT, CA_CERT };
+
+static int
+check_basic_constraints(hx509_context context, const Certificate *cert,
+ enum certtype type, int depth)
+{
+ BasicConstraints bc;
+ const Extension *e;
+ size_t size;
+ int ret, i = 0;
+
+ if (_hx509_cert_get_version(cert) < 3)
+ return 0;
+
+ e = find_extension(cert, oid_id_x509_ce_basicConstraints(), &i);
+ if (e == NULL) {
+ switch(type) {
+ case PROXY_CERT:
+ case EE_CERT:
+ return 0;
+ case CA_CERT: {
+ char *name;
+ ret = _hx509_unparse_Name(&cert->tbsCertificate.subject, &name);
+ assert(ret == 0);
+ hx509_set_error_string(context, 0, HX509_EXTENSION_NOT_FOUND,
+ "basicConstraints missing from "
+ "CA certifiacte %s", name);
+ free(name);
+ return HX509_EXTENSION_NOT_FOUND;
+ }
+ }
+ }
+
+ ret = decode_BasicConstraints(e->extnValue.data,
+ e->extnValue.length, &bc,
+ &size);
+ if (ret)
+ return ret;
+ switch(type) {
+ case PROXY_CERT:
+ if (bc.cA != NULL && *bc.cA)
+ ret = HX509_PARENT_IS_CA;
+ break;
+ case EE_CERT:
+ ret = 0;
+ break;
+ case CA_CERT:
+ if (bc.cA == NULL || !*bc.cA)
+ ret = HX509_PARENT_NOT_CA;
+ else if (bc.pathLenConstraint)
+ if (depth - 1 > *bc.pathLenConstraint)
+ ret = HX509_CA_PATH_TOO_DEEP;
+ break;
+ }
+ free_BasicConstraints(&bc);
+ return ret;
+}
+
+int
+_hx509_cert_is_parent_cmp(const Certificate *subject,
+ const Certificate *issuer,
+ int allow_self_signed)
+{
+ int diff;
+ AuthorityKeyIdentifier ai;
+ SubjectKeyIdentifier si;
+ int ret_ai, ret_si, ret;
+
+ ret = _hx509_name_cmp(&issuer->tbsCertificate.subject,
+ &subject->tbsCertificate.issuer,
+ &diff);
+ if (ret)
+ return ret;
+ if (diff)
+ return diff;
+
+ memset(&ai, 0, sizeof(ai));
+ memset(&si, 0, sizeof(si));
+
+ /*
+ * Try to find AuthorityKeyIdentifier, if it's not present in the
+ * subject certificate nor the parent.
+ */
+
+ ret_ai = find_extension_auth_key_id(subject, &ai);
+ if (ret_ai && ret_ai != HX509_EXTENSION_NOT_FOUND)
+ return 1;
+ ret_si = _hx509_find_extension_subject_key_id(issuer, &si);
+ if (ret_si && ret_si != HX509_EXTENSION_NOT_FOUND)
+ return -1;
+
+ if (ret_si && ret_ai)
+ goto out;
+ if (ret_ai)
+ goto out;
+ if (ret_si) {
+ if (allow_self_signed) {
+ diff = 0;
+ goto out;
+ } else if (ai.keyIdentifier) {
+ diff = -1;
+ goto out;
+ }
+ }
+
+ if (ai.keyIdentifier == NULL) {
+ Name name;
+
+ if (ai.authorityCertIssuer == NULL)
+ return -1;
+ if (ai.authorityCertSerialNumber == NULL)
+ return -1;
+
+ diff = der_heim_integer_cmp(ai.authorityCertSerialNumber,
+ &issuer->tbsCertificate.serialNumber);
+ if (diff)
+ return diff;
+ if (ai.authorityCertIssuer->len != 1)
+ return -1;
+ if (ai.authorityCertIssuer->val[0].element != choice_GeneralName_directoryName)
+ return -1;
+
+ name.element =
+ ai.authorityCertIssuer->val[0].u.directoryName.element;
+ name.u.rdnSequence =
+ ai.authorityCertIssuer->val[0].u.directoryName.u.rdnSequence;
+
+ ret = _hx509_name_cmp(&issuer->tbsCertificate.subject,
+ &name,
+ &diff);
+ if (ret)
+ return ret;
+ if (diff)
+ return diff;
+ diff = 0;
+ } else
+ diff = der_heim_octet_string_cmp(ai.keyIdentifier, &si);
+ if (diff)
+ goto out;
+
+ out:
+ free_AuthorityKeyIdentifier(&ai);
+ free_SubjectKeyIdentifier(&si);
+ return diff;
+}
+
+static int
+certificate_is_anchor(hx509_context context,
+ hx509_certs trust_anchors,
+ const hx509_cert cert)
+{
+ hx509_query q;
+ hx509_cert c;
+ int ret;
+
+ if (trust_anchors == NULL)
+ return 0;
+
+ _hx509_query_clear(&q);
+
+ q.match = HX509_QUERY_MATCH_CERTIFICATE;
+ q.certificate = _hx509_get_cert(cert);
+
+ ret = hx509_certs_find(context, trust_anchors, &q, &c);
+ if (ret == 0)
+ hx509_cert_free(c);
+ return ret == 0;
+}
+
+static int
+certificate_is_self_signed(hx509_context context,
+ const Certificate *cert,
+ int *self_signed)
+{
+ int ret, diff;
+ ret = _hx509_name_cmp(&cert->tbsCertificate.subject,
+ &cert->tbsCertificate.issuer, &diff);
+ *self_signed = (diff == 0);
+ if (ret)
+ hx509_set_error_string(context, 0, ret,
+ "Failed to check if self signed");
+ return ret;
+}
+
+/*
+ * The subjectName is "null" when it's empty set of relative DBs.
+ */
+
+static int
+subject_null_p(const Certificate *c)
+{
+ return c->tbsCertificate.subject.u.rdnSequence.len == 0;
+}
+
+
+static int
+find_parent(hx509_context context,
+ time_t time_now,
+ hx509_certs trust_anchors,
+ hx509_path *path,
+ hx509_certs pool,
+ hx509_cert current,
+ hx509_cert *parent)
+{
+ AuthorityKeyIdentifier ai;
+ hx509_query q;
+ int ret;
+
+ *parent = NULL;
+ memset(&ai, 0, sizeof(ai));
+
+ _hx509_query_clear(&q);
+
+ if (!subject_null_p(current->data)) {
+ q.match |= HX509_QUERY_FIND_ISSUER_CERT;
+ q.subject = _hx509_get_cert(current);
+ } else {
+ ret = find_extension_auth_key_id(current->data, &ai);
+ if (ret) {
+ hx509_set_error_string(context, 0, HX509_CERTIFICATE_MALFORMED,
+ "Subjectless certificate missing AuthKeyID");
+ return HX509_CERTIFICATE_MALFORMED;
+ }
+
+ if (ai.keyIdentifier == NULL) {
+ free_AuthorityKeyIdentifier(&ai);
+ hx509_set_error_string(context, 0, HX509_CERTIFICATE_MALFORMED,
+ "Subjectless certificate missing keyIdentifier "
+ "inside AuthKeyID");
+ return HX509_CERTIFICATE_MALFORMED;
+ }
+
+ q.subject_id = ai.keyIdentifier;
+ q.match = HX509_QUERY_MATCH_SUBJECT_KEY_ID;
+ }
+
+ q.path = path;
+ q.match |= HX509_QUERY_NO_MATCH_PATH;
+
+ if (pool) {
+ q.timenow = time_now;
+ q.match |= HX509_QUERY_MATCH_TIME;
+
+ ret = hx509_certs_find(context, pool, &q, parent);
+ if (ret == 0) {
+ free_AuthorityKeyIdentifier(&ai);
+ return 0;
+ }
+ q.match &= ~HX509_QUERY_MATCH_TIME;
+ }
+
+ if (trust_anchors) {
+ ret = hx509_certs_find(context, trust_anchors, &q, parent);
+ if (ret == 0) {
+ free_AuthorityKeyIdentifier(&ai);
+ return ret;
+ }
+ }
+ free_AuthorityKeyIdentifier(&ai);
+
+ {
+ hx509_name name;
+ char *str;
+
+ ret = hx509_cert_get_subject(current, &name);
+ if (ret) {
+ hx509_clear_error_string(context);
+ return HX509_ISSUER_NOT_FOUND;
+ }
+ ret = hx509_name_to_string(name, &str);
+ hx509_name_free(&name);
+ if (ret) {
+ hx509_clear_error_string(context);
+ return HX509_ISSUER_NOT_FOUND;
+ }
+
+ hx509_set_error_string(context, 0, HX509_ISSUER_NOT_FOUND,
+ "Failed to find issuer for "
+ "certificate with subject: '%s'", str);
+ free(str);
+ }
+ return HX509_ISSUER_NOT_FOUND;
+}
+
+/*
+ *
+ */
+
+static int
+is_proxy_cert(hx509_context context,
+ const Certificate *cert,
+ ProxyCertInfo *rinfo)
+{
+ ProxyCertInfo info;
+ const Extension *e;
+ size_t size;
+ int ret, i = 0;
+
+ if (rinfo)
+ memset(rinfo, 0, sizeof(*rinfo));
+
+ e = find_extension(cert, oid_id_pkix_pe_proxyCertInfo(), &i);
+ if (e == NULL) {
+ hx509_clear_error_string(context);
+ return HX509_EXTENSION_NOT_FOUND;
+ }
+
+ ret = decode_ProxyCertInfo(e->extnValue.data,
+ e->extnValue.length,
+ &info,
+ &size);
+ if (ret) {
+ hx509_clear_error_string(context);
+ return ret;
+ }
+ if (size != e->extnValue.length) {
+ free_ProxyCertInfo(&info);
+ hx509_clear_error_string(context);
+ return HX509_EXTRA_DATA_AFTER_STRUCTURE;
+ }
+ if (rinfo == NULL)
+ free_ProxyCertInfo(&info);
+ else
+ *rinfo = info;
+
+ return 0;
+}
+
+/*
+ * Path operations are like MEMORY based keyset, but with exposed
+ * internal so we can do easy searches.
+ */
+
+int
+_hx509_path_append(hx509_context context, hx509_path *path, hx509_cert cert)
+{
+ hx509_cert *val;
+ val = realloc(path->val, (path->len + 1) * sizeof(path->val[0]));
+ if (val == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+
+ path->val = val;
+ path->val[path->len] = hx509_cert_ref(cert);
+ path->len++;
+
+ return 0;
+}
+
+void
+_hx509_path_free(hx509_path *path)
+{
+ unsigned i;
+
+ for (i = 0; i < path->len; i++)
+ hx509_cert_free(path->val[i]);
+ free(path->val);
+ path->val = NULL;
+ path->len = 0;
+}
+
+/*
+ * Find path by looking up issuer for the top certificate and continue
+ * until an anchor certificate is found or max limit is found. A
+ * certificate never included twice in the path.
+ *
+ * If the trust anchors are not given, calculate optimistic path, just
+ * follow the chain upward until we no longer find a parent or we hit
+ * the max path limit. In this case, a failure will always be returned
+ * depending on what error condition is hit first.
+ *
+ * The path includes a path from the top certificate to the anchor
+ * certificate.
+ *
+ * The caller needs to free `path´ both on successful built path and
+ * failure.
+ */
+
+int
+_hx509_calculate_path(hx509_context context,
+ int flags,
+ time_t time_now,
+ hx509_certs anchors,
+ unsigned int max_depth,
+ hx509_cert cert,
+ hx509_certs pool,
+ hx509_path *path)
+{
+ hx509_cert parent, current;
+ int ret;
+
+ if (max_depth == 0)
+ max_depth = HX509_VERIFY_MAX_DEPTH;
+
+ ret = _hx509_path_append(context, path, cert);
+ if (ret)
+ return ret;
+
+ current = hx509_cert_ref(cert);
+
+ while (!certificate_is_anchor(context, anchors, current)) {
+
+ ret = find_parent(context, time_now, anchors, path,
+ pool, current, &parent);
+ hx509_cert_free(current);
+ if (ret)
+ return ret;
+
+ ret = _hx509_path_append(context, path, parent);
+ if (ret)
+ return ret;
+ current = parent;
+
+ if (path->len > max_depth) {
+ hx509_cert_free(current);
+ hx509_set_error_string(context, 0, HX509_PATH_TOO_LONG,
+ "Path too long while bulding "
+ "certificate chain");
+ return HX509_PATH_TOO_LONG;
+ }
+ }
+
+ if ((flags & HX509_CALCULATE_PATH_NO_ANCHOR) &&
+ path->len > 0 &&
+ certificate_is_anchor(context, anchors, path->val[path->len - 1]))
+ {
+ hx509_cert_free(path->val[path->len - 1]);
+ path->len--;
+ }
+
+ hx509_cert_free(current);
+ return 0;
+}
+
+int
+_hx509_AlgorithmIdentifier_cmp(const AlgorithmIdentifier *p,
+ const AlgorithmIdentifier *q)
+{
+ int diff;
+ diff = der_heim_oid_cmp(&p->algorithm, &q->algorithm);
+ if (diff)
+ return diff;
+ if (p->parameters) {
+ if (q->parameters)
+ return heim_any_cmp(p->parameters,
+ q->parameters);
+ else
+ return 1;
+ } else {
+ if (q->parameters)
+ return -1;
+ else
+ return 0;
+ }
+}
+
+int
+_hx509_Certificate_cmp(const Certificate *p, const Certificate *q)
+{
+ int diff;
+ diff = der_heim_bit_string_cmp(&p->signatureValue, &q->signatureValue);
+ if (diff)
+ return diff;
+ diff = _hx509_AlgorithmIdentifier_cmp(&p->signatureAlgorithm,
+ &q->signatureAlgorithm);
+ if (diff)
+ return diff;
+ diff = der_heim_octet_string_cmp(&p->tbsCertificate._save,
+ &q->tbsCertificate._save);
+ return diff;
+}
+
+/**
+ * Compare to hx509 certificate object, useful for sorting.
+ *
+ * @param p a hx509 certificate object.
+ * @param q a hx509 certificate object.
+ *
+ * @return 0 the objects are the same, returns > 0 is p is "larger"
+ * then q, < 0 if p is "smaller" then q.
+ *
+ * @ingroup hx509_cert
+ */
+
+int
+hx509_cert_cmp(hx509_cert p, hx509_cert q)
+{
+ return _hx509_Certificate_cmp(p->data, q->data);
+}
+
+/**
+ * Return the name of the issuer of the hx509 certificate.
+ *
+ * @param p a hx509 certificate object.
+ * @param name a pointer to a hx509 name, should be freed by
+ * hx509_name_free().
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
+int
+hx509_cert_get_issuer(hx509_cert p, hx509_name *name)
+{
+ return _hx509_name_from_Name(&p->data->tbsCertificate.issuer, name);
+}
+
+/**
+ * Return the name of the subject of the hx509 certificate.
+ *
+ * @param p a hx509 certificate object.
+ * @param name a pointer to a hx509 name, should be freed by
+ * hx509_name_free(). See also hx509_cert_get_base_subject().
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
+int
+hx509_cert_get_subject(hx509_cert p, hx509_name *name)
+{
+ return _hx509_name_from_Name(&p->data->tbsCertificate.subject, name);
+}
+
+/**
+ * Return the name of the base subject of the hx509 certificate. If
+ * the certiicate is a verified proxy certificate, the this function
+ * return the base certificate (root of the proxy chain). If the proxy
+ * certificate is not verified with the base certificate
+ * HX509_PROXY_CERTIFICATE_NOT_CANONICALIZED is returned.
+ *
+ * @param context a hx509 context.
+ * @param c a hx509 certificate object.
+ * @param name a pointer to a hx509 name, should be freed by
+ * hx509_name_free(). See also hx509_cert_get_subject().
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
+int
+hx509_cert_get_base_subject(hx509_context context, hx509_cert c,
+ hx509_name *name)
+{
+ if (c->basename)
+ return hx509_name_copy(context, c->basename, name);
+ if (is_proxy_cert(context, c->data, NULL) == 0) {
+ int ret = HX509_PROXY_CERTIFICATE_NOT_CANONICALIZED;
+ hx509_set_error_string(context, 0, ret,
+ "Proxy certificate have not been "
+ "canonicalize yet, no base name");
+ return ret;
+ }
+ return _hx509_name_from_Name(&c->data->tbsCertificate.subject, name);
+}
+
+/**
+ * Get serial number of the certificate.
+ *
+ * @param p a hx509 certificate object.
+ * @param i serial number, should be freed ith der_free_heim_integer().
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
+int
+hx509_cert_get_serialnumber(hx509_cert p, heim_integer *i)
+{
+ return der_copy_heim_integer(&p->data->tbsCertificate.serialNumber, i);
+}
+
+/**
+ * Get notBefore time of the certificate.
+ *
+ * @param p a hx509 certificate object.
+ *
+ * @return return not before time
+ *
+ * @ingroup hx509_cert
+ */
+
+time_t
+hx509_cert_get_notBefore(hx509_cert p)
+{
+ return _hx509_Time2time_t(&p->data->tbsCertificate.validity.notBefore);
+}
+
+/**
+ * Get notAfter time of the certificate.
+ *
+ * @param p a hx509 certificate object.
+ *
+ * @return return not after time.
+ *
+ * @ingroup hx509_cert
+ */
+
+time_t
+hx509_cert_get_notAfter(hx509_cert p)
+{
+ return _hx509_Time2time_t(&p->data->tbsCertificate.validity.notAfter);
+}
+
+/**
+ * Get the SubjectPublicKeyInfo structure from the hx509 certificate.
+ *
+ * @param context a hx509 context.
+ * @param p a hx509 certificate object.
+ * @param spki SubjectPublicKeyInfo, should be freed with
+ * free_SubjectPublicKeyInfo().
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
+int
+hx509_cert_get_SPKI(hx509_context context, hx509_cert p, SubjectPublicKeyInfo *spki)
+{
+ int ret;
+
+ ret = copy_SubjectPublicKeyInfo(&p->data->tbsCertificate.subjectPublicKeyInfo, spki);
+ if (ret)
+ hx509_set_error_string(context, 0, ret, "Failed to copy SPKI");
+ return ret;
+}
+
+/**
+ * Get the AlgorithmIdentifier from the hx509 certificate.
+ *
+ * @param context a hx509 context.
+ * @param p a hx509 certificate object.
+ * @param alg AlgorithmIdentifier, should be freed with
+ * free_AlgorithmIdentifier().
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
+int
+hx509_cert_get_SPKI_AlgorithmIdentifier(hx509_context context,
+ hx509_cert p,
+ AlgorithmIdentifier *alg)
+{
+ int ret;
+
+ ret = copy_AlgorithmIdentifier(&p->data->tbsCertificate.subjectPublicKeyInfo.algorithm, alg);
+ if (ret)
+ hx509_set_error_string(context, 0, ret,
+ "Failed to copy SPKI AlgorithmIdentifier");
+ return ret;
+}
+
+
+hx509_private_key
+_hx509_cert_private_key(hx509_cert p)
+{
+ return p->private_key;
+}
+
+int
+hx509_cert_have_private_key(hx509_cert p)
+{
+ return p->private_key ? 1 : 0;
+}
+
+
+int
+_hx509_cert_private_key_exportable(hx509_cert p)
+{
+ if (p->private_key == NULL)
+ return 0;
+ return _hx509_private_key_exportable(p->private_key);
+}
+
+int
+_hx509_cert_private_decrypt(hx509_context context,
+ const heim_octet_string *ciphertext,
+ const heim_oid *encryption_oid,
+ hx509_cert p,
+ heim_octet_string *cleartext)
+{
+ cleartext->data = NULL;
+ cleartext->length = 0;
+
+ if (p->private_key == NULL) {
+ hx509_set_error_string(context, 0, HX509_PRIVATE_KEY_MISSING,
+ "Private key missing");
+ return HX509_PRIVATE_KEY_MISSING;
+ }
+
+ return _hx509_private_key_private_decrypt(context,
+ ciphertext,
+ encryption_oid,
+ p->private_key,
+ cleartext);
+}
+
+int
+_hx509_cert_public_encrypt(hx509_context context,
+ const heim_octet_string *cleartext,
+ const hx509_cert p,
+ heim_oid *encryption_oid,
+ heim_octet_string *ciphertext)
+{
+ return _hx509_public_encrypt(context,
+ cleartext, p->data,
+ encryption_oid, ciphertext);
+}
+
+/*
+ *
+ */
+
+time_t
+_hx509_Time2time_t(const Time *t)
+{
+ switch(t->element) {
+ case choice_Time_utcTime:
+ return t->u.utcTime;
+ case choice_Time_generalTime:
+ return t->u.generalTime;
+ }
+ return 0;
+}
+
+/*
+ *
+ */
+
+static int
+init_name_constraints(hx509_name_constraints *nc)
+{
+ memset(nc, 0, sizeof(*nc));
+ return 0;
+}
+
+static int
+add_name_constraints(hx509_context context, const Certificate *c, int not_ca,
+ hx509_name_constraints *nc)
+{
+ NameConstraints tnc;
+ int ret;
+
+ ret = find_extension_name_constraints(c, &tnc);
+ if (ret == HX509_EXTENSION_NOT_FOUND)
+ return 0;
+ else if (ret) {
+ hx509_set_error_string(context, 0, ret, "Failed getting NameConstraints");
+ return ret;
+ } else if (not_ca) {
+ ret = HX509_VERIFY_CONSTRAINTS;
+ hx509_set_error_string(context, 0, ret, "Not a CA and "
+ "have NameConstraints");
+ } else {
+ NameConstraints *val;
+ val = realloc(nc->val, sizeof(nc->val[0]) * (nc->len + 1));
+ if (val == NULL) {
+ hx509_clear_error_string(context);
+ ret = ENOMEM;
+ goto out;
+ }
+ nc->val = val;
+ ret = copy_NameConstraints(&tnc, &nc->val[nc->len]);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+ nc->len += 1;
+ }
+out:
+ free_NameConstraints(&tnc);
+ return ret;
+}
+
+static int
+match_RDN(const RelativeDistinguishedName *c,
+ const RelativeDistinguishedName *n)
+{
+ int i;
+
+ if (c->len != n->len)
+ return HX509_NAME_CONSTRAINT_ERROR;
+
+ for (i = 0; i < n->len; i++) {
+ int diff, ret;
+
+ if (der_heim_oid_cmp(&c->val[i].type, &n->val[i].type) != 0)
+ return HX509_NAME_CONSTRAINT_ERROR;
+ ret = _hx509_name_ds_cmp(&c->val[i].value, &n->val[i].value, &diff);
+ if (ret)
+ return ret;
+ if (diff != 0)
+ return HX509_NAME_CONSTRAINT_ERROR;
+ }
+ return 0;
+}
+
+static int
+match_X501Name(const Name *c, const Name *n)
+{
+ int i, ret;
+
+ if (c->element != choice_Name_rdnSequence
+ || n->element != choice_Name_rdnSequence)
+ return 0;
+ if (c->u.rdnSequence.len > n->u.rdnSequence.len)
+ return HX509_NAME_CONSTRAINT_ERROR;
+ for (i = 0; i < c->u.rdnSequence.len; i++) {
+ ret = match_RDN(&c->u.rdnSequence.val[i], &n->u.rdnSequence.val[i]);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+
+static int
+match_general_name(const GeneralName *c, const GeneralName *n, int *match)
+{
+ /*
+ * Name constraints only apply to the same name type, see RFC3280,
+ * 4.2.1.11.
+ */
+ assert(c->element == n->element);
+
+ switch(c->element) {
+ case choice_GeneralName_otherName:
+ if (der_heim_oid_cmp(&c->u.otherName.type_id,
+ &n->u.otherName.type_id) != 0)
+ return HX509_NAME_CONSTRAINT_ERROR;
+ if (heim_any_cmp(&c->u.otherName.value,
+ &n->u.otherName.value) != 0)
+ return HX509_NAME_CONSTRAINT_ERROR;
+ *match = 1;
+ return 0;
+ case choice_GeneralName_rfc822Name: {
+ const char *s;
+ size_t len1, len2;
+ s = strchr(c->u.rfc822Name, '@');
+ if (s) {
+ if (strcasecmp(c->u.rfc822Name, n->u.rfc822Name) != 0)
+ return HX509_NAME_CONSTRAINT_ERROR;
+ } else {
+ s = strchr(n->u.rfc822Name, '@');
+ if (s == NULL)
+ return HX509_NAME_CONSTRAINT_ERROR;
+ len1 = strlen(c->u.rfc822Name);
+ len2 = strlen(s + 1);
+ if (len1 > len2)
+ return HX509_NAME_CONSTRAINT_ERROR;
+ if (strcasecmp(s + 1 + len2 - len1, c->u.rfc822Name) != 0)
+ return HX509_NAME_CONSTRAINT_ERROR;
+ if (len1 < len2 && s[len2 - len1 + 1] != '.')
+ return HX509_NAME_CONSTRAINT_ERROR;
+ }
+ *match = 1;
+ return 0;
+ }
+ case choice_GeneralName_dNSName: {
+ size_t lenc, lenn;
+
+ lenc = strlen(c->u.dNSName);
+ lenn = strlen(n->u.dNSName);
+ if (lenc > lenn)
+ return HX509_NAME_CONSTRAINT_ERROR;
+ if (strcasecmp(&n->u.dNSName[lenn - lenc], c->u.dNSName) != 0)
+ return HX509_NAME_CONSTRAINT_ERROR;
+ if (lenc != lenn && n->u.dNSName[lenn - lenc - 1] != '.')
+ return HX509_NAME_CONSTRAINT_ERROR;
+ *match = 1;
+ return 0;
+ }
+ case choice_GeneralName_directoryName: {
+ Name c_name, n_name;
+ int ret;
+
+ c_name._save.data = NULL;
+ c_name._save.length = 0;
+ c_name.element = c->u.directoryName.element;
+ c_name.u.rdnSequence = c->u.directoryName.u.rdnSequence;
+
+ n_name._save.data = NULL;
+ n_name._save.length = 0;
+ n_name.element = n->u.directoryName.element;
+ n_name.u.rdnSequence = n->u.directoryName.u.rdnSequence;
+
+ ret = match_X501Name(&c_name, &n_name);
+ if (ret == 0)
+ *match = 1;
+ return ret;
+ }
+ case choice_GeneralName_uniformResourceIdentifier:
+ case choice_GeneralName_iPAddress:
+ case choice_GeneralName_registeredID:
+ default:
+ return HX509_NAME_CONSTRAINT_ERROR;
+ }
+}
+
+static int
+match_alt_name(const GeneralName *n, const Certificate *c,
+ int *same, int *match)
+{
+ GeneralNames sa;
+ int ret, i, j;
+
+ i = 0;
+ do {
+ ret = find_extension_subject_alt_name(c, &i, &sa);
+ if (ret == HX509_EXTENSION_NOT_FOUND) {
+ ret = 0;
+ break;
+ } else if (ret != 0)
+ break;
+
+ for (j = 0; j < sa.len; j++) {
+ if (n->element == sa.val[j].element) {
+ *same = 1;
+ ret = match_general_name(n, &sa.val[j], match);
+ }
+ }
+ free_GeneralNames(&sa);
+ } while (1);
+ return ret;
+}
+
+
+static int
+match_tree(const GeneralSubtrees *t, const Certificate *c, int *match)
+{
+ int name, alt_name, same;
+ unsigned int i;
+ int ret = 0;
+
+ name = alt_name = same = *match = 0;
+ for (i = 0; i < t->len; i++) {
+ if (t->val[i].minimum && t->val[i].maximum)
+ return HX509_RANGE;
+
+ /*
+ * If the constraint apply to directoryNames, test is with
+ * subjectName of the certificate if the certificate have a
+ * non-null (empty) subjectName.
+ */
+
+ if (t->val[i].base.element == choice_GeneralName_directoryName
+ && !subject_null_p(c))
+ {
+ GeneralName certname;
+
+ memset(&certname, 0, sizeof(certname));
+ certname.element = choice_GeneralName_directoryName;
+ certname.u.directoryName.element =
+ c->tbsCertificate.subject.element;
+ certname.u.directoryName.u.rdnSequence =
+ c->tbsCertificate.subject.u.rdnSequence;
+
+ ret = match_general_name(&t->val[i].base, &certname, &name);
+ }
+
+ /* Handle subjectAltNames, this is icky since they
+ * restrictions only apply if the subjectAltName is of the
+ * same type. So if there have been a match of type, require
+ * altname to be set.
+ */
+ ret = match_alt_name(&t->val[i].base, c, &same, &alt_name);
+ }
+ if (name && (!same || alt_name))
+ *match = 1;
+ return ret;
+}
+
+static int
+check_name_constraints(hx509_context context,
+ const hx509_name_constraints *nc,
+ const Certificate *c)
+{
+ int match, ret;
+ int i;
+
+ for (i = 0 ; i < nc->len; i++) {
+ GeneralSubtrees gs;
+
+ if (nc->val[i].permittedSubtrees) {
+ GeneralSubtrees_SET(&gs, nc->val[i].permittedSubtrees);
+ ret = match_tree(&gs, c, &match);
+ if (ret) {
+ hx509_clear_error_string(context);
+ return ret;
+ }
+ /* allow null subjectNames, they wont matches anything */
+ if (match == 0 && !subject_null_p(c)) {
+ hx509_set_error_string(context, 0, HX509_VERIFY_CONSTRAINTS,
+ "Error verify constraints, "
+ "certificate didn't match any "
+ "permitted subtree");
+ return HX509_VERIFY_CONSTRAINTS;
+ }
+ }
+ if (nc->val[i].excludedSubtrees) {
+ GeneralSubtrees_SET(&gs, nc->val[i].excludedSubtrees);
+ ret = match_tree(&gs, c, &match);
+ if (ret) {
+ hx509_clear_error_string(context);
+ return ret;
+ }
+ if (match) {
+ hx509_set_error_string(context, 0, HX509_VERIFY_CONSTRAINTS,
+ "Error verify constraints, "
+ "certificate included in excluded "
+ "subtree");
+ return HX509_VERIFY_CONSTRAINTS;
+ }
+ }
+ }
+ return 0;
+}
+
+static void
+free_name_constraints(hx509_name_constraints *nc)
+{
+ int i;
+
+ for (i = 0 ; i < nc->len; i++)
+ free_NameConstraints(&nc->val[i]);
+ free(nc->val);
+}
+
+/**
+ * Build and verify the path for the certificate to the trust anchor
+ * specified in the verify context. The path is constructed from the
+ * certificate, the pool and the trust anchors.
+ *
+ * @param context A hx509 context.
+ * @param ctx A hx509 verification context.
+ * @param cert the certificate to build the path from.
+ * @param pool A keyset of certificates to build the chain from.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_verify
+ */
+
+int
+hx509_verify_path(hx509_context context,
+ hx509_verify_ctx ctx,
+ hx509_cert cert,
+ hx509_certs pool)
+{
+ hx509_name_constraints nc;
+ hx509_path path;
+ int ret, i, proxy_cert_depth, selfsigned_depth, diff;
+ enum certtype type;
+ Name proxy_issuer;
+ hx509_certs anchors = NULL;
+
+ memset(&proxy_issuer, 0, sizeof(proxy_issuer));
+
+ ret = init_name_constraints(&nc);
+ if (ret)
+ return ret;
+
+ path.val = NULL;
+ path.len = 0;
+
+ if ((ctx->flags & HX509_VERIFY_CTX_F_TIME_SET) == 0)
+ ctx->time_now = time(NULL);
+
+ /*
+ *
+ */
+ if (ctx->trust_anchors)
+ anchors = _hx509_certs_ref(ctx->trust_anchors);
+ else if (context->default_trust_anchors && ALLOW_DEF_TA(ctx))
+ anchors = _hx509_certs_ref(context->default_trust_anchors);
+ else {
+ ret = hx509_certs_init(context, "MEMORY:no-TA", 0, NULL, &anchors);
+ if (ret)
+ goto out;
+ }
+
+ /*
+ * Calculate the path from the certificate user presented to the
+ * to an anchor.
+ */
+ ret = _hx509_calculate_path(context, 0, ctx->time_now,
+ anchors, ctx->max_depth,
+ cert, pool, &path);
+ if (ret)
+ goto out;
+
+ /*
+ * Check CA and proxy certificate chain from the top of the
+ * certificate chain. Also check certificate is valid with respect
+ * to the current time.
+ *
+ */
+
+ proxy_cert_depth = 0;
+ selfsigned_depth = 0;
+
+ if (ctx->flags & HX509_VERIFY_CTX_F_ALLOW_PROXY_CERTIFICATE)
+ type = PROXY_CERT;
+ else
+ type = EE_CERT;
+
+ for (i = 0; i < path.len; i++) {
+ Certificate *c;
+ time_t t;
+
+ c = _hx509_get_cert(path.val[i]);
+
+ /*
+ * Lets do some basic check on issuer like
+ * keyUsage.keyCertSign and basicConstraints.cA bit depending
+ * on what type of certificate this is.
+ */
+
+ switch (type) {
+ case CA_CERT:
+
+ /* XXX make constants for keyusage */
+ ret = check_key_usage(context, c, 1 << 5,
+ REQUIRE_RFC3280(ctx) ? TRUE : FALSE);
+ if (ret) {
+ hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
+ "Key usage missing from CA certificate");
+ goto out;
+ }
+
+ /* self signed cert doesn't add to path length */
+ if (i + 1 != path.len) {
+ int selfsigned;
+
+ ret = certificate_is_self_signed(context, c, &selfsigned);
+ if (ret)
+ goto out;
+ if (selfsigned)
+ selfsigned_depth++;
+ }
+
+ break;
+ case PROXY_CERT: {
+ ProxyCertInfo info;
+
+ if (is_proxy_cert(context, c, &info) == 0) {
+ int j;
+
+ if (info.pCPathLenConstraint != NULL &&
+ *info.pCPathLenConstraint < i)
+ {
+ free_ProxyCertInfo(&info);
+ ret = HX509_PATH_TOO_LONG;
+ hx509_set_error_string(context, 0, ret,
+ "Proxy certificate chain "
+ "longer then allowed");
+ goto out;
+ }
+ /* XXX MUST check info.proxyPolicy */
+ free_ProxyCertInfo(&info);
+
+ j = 0;
+ if (find_extension(c, oid_id_x509_ce_subjectAltName(), &j)) {
+ ret = HX509_PROXY_CERT_INVALID;
+ hx509_set_error_string(context, 0, ret,
+ "Proxy certificate have explicity "
+ "forbidden subjectAltName");
+ goto out;
+ }
+
+ j = 0;
+ if (find_extension(c, oid_id_x509_ce_issuerAltName(), &j)) {
+ ret = HX509_PROXY_CERT_INVALID;
+ hx509_set_error_string(context, 0, ret,
+ "Proxy certificate have explicity "
+ "forbidden issuerAltName");
+ goto out;
+ }
+
+ /*
+ * The subject name of the proxy certificate should be
+ * CN=XXX,<proxy issuer>, prune of CN and check if its
+ * the same over the whole chain of proxy certs and
+ * then check with the EE cert when we get to it.
+ */
+
+ if (proxy_cert_depth) {
+ ret = _hx509_name_cmp(&proxy_issuer, &c->tbsCertificate.subject, &diff);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+ if (diff) {
+ ret = HX509_PROXY_CERT_NAME_WRONG;
+ hx509_set_error_string(context, 0, ret,
+ "Base proxy name not right");
+ goto out;
+ }
+ }
+
+ free_Name(&proxy_issuer);
+
+ ret = copy_Name(&c->tbsCertificate.subject, &proxy_issuer);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+
+ j = proxy_issuer.u.rdnSequence.len;
+ if (proxy_issuer.u.rdnSequence.len < 2
+ || proxy_issuer.u.rdnSequence.val[j - 1].len > 1
+ || der_heim_oid_cmp(&proxy_issuer.u.rdnSequence.val[j - 1].val[0].type,
+ oid_id_at_commonName()))
+ {
+ ret = HX509_PROXY_CERT_NAME_WRONG;
+ hx509_set_error_string(context, 0, ret,
+ "Proxy name too short or "
+ "does not have Common name "
+ "at the top");
+ goto out;
+ }
+
+ free_RelativeDistinguishedName(&proxy_issuer.u.rdnSequence.val[j - 1]);
+ proxy_issuer.u.rdnSequence.len -= 1;
+
+ ret = _hx509_name_cmp(&proxy_issuer, &c->tbsCertificate.issuer, &diff);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Out of memory");
+ goto out;
+ }
+ if (diff != 0) {
+ ret = HX509_PROXY_CERT_NAME_WRONG;
+ hx509_set_error_string(context, 0, ret,
+ "Proxy issuer name not as expected");
+ goto out;
+ }
+
+ break;
+ } else {
+ /*
+ * Now we are done with the proxy certificates, this
+ * cert was an EE cert and we we will fall though to
+ * EE checking below.
+ */
+ type = EE_CERT;
+ /* FALLTHOUGH */
+ }
+ }
+ case EE_CERT:
+ /*
+ * If there where any proxy certificates in the chain
+ * (proxy_cert_depth > 0), check that the proxy issuer
+ * matched proxy certificates "base" subject.
+ */
+ if (proxy_cert_depth) {
+
+ ret = _hx509_name_cmp(&proxy_issuer,
+ &c->tbsCertificate.subject, &diff);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "out of memory");
+ goto out;
+ }
+ if (diff) {
+ ret = HX509_PROXY_CERT_NAME_WRONG;
+ hx509_clear_error_string(context);
+ goto out;
+ }
+ if (cert->basename)
+ hx509_name_free(&cert->basename);
+
+ ret = _hx509_name_from_Name(&proxy_issuer, &cert->basename);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+ }
+
+ break;
+ }
+
+ ret = check_basic_constraints(context, c, type,
+ i - proxy_cert_depth - selfsigned_depth);
+ if (ret)
+ goto out;
+
+ /*
+ * Don't check the trust anchors expiration time since they
+ * are transported out of band, from RFC3820.
+ */
+ if (i + 1 != path.len || CHECK_TA(ctx)) {
+
+ t = _hx509_Time2time_t(&c->tbsCertificate.validity.notBefore);
+ if (t > ctx->time_now) {
+ ret = HX509_CERT_USED_BEFORE_TIME;
+ hx509_clear_error_string(context);
+ goto out;
+ }
+ t = _hx509_Time2time_t(&c->tbsCertificate.validity.notAfter);
+ if (t < ctx->time_now) {
+ ret = HX509_CERT_USED_AFTER_TIME;
+ hx509_clear_error_string(context);
+ goto out;
+ }
+ }
+
+ if (type == EE_CERT)
+ type = CA_CERT;
+ else if (type == PROXY_CERT)
+ proxy_cert_depth++;
+ }
+
+ /*
+ * Verify constraints, do this backward so path constraints are
+ * checked in the right order.
+ */
+
+ for (ret = 0, i = path.len - 1; i >= 0; i--) {
+ Certificate *c;
+ int selfsigned;
+
+ c = _hx509_get_cert(path.val[i]);
+
+ ret = certificate_is_self_signed(context, c, &selfsigned);
+ if (ret)
+ goto out;
+
+ /* verify name constraints, not for selfsigned and anchor */
+ if (!selfsigned || i + 1 != path.len) {
+ ret = check_name_constraints(context, &nc, c);
+ if (ret) {
+ goto out;
+ }
+ }
+ ret = add_name_constraints(context, c, i == 0, &nc);
+ if (ret)
+ goto out;
+
+ /* XXX verify all other silly constraints */
+
+ }
+
+ /*
+ * Verify that no certificates has been revoked.
+ */
+
+ if (ctx->revoke_ctx) {
+ hx509_certs certs;
+
+ ret = hx509_certs_init(context, "MEMORY:revoke-certs", 0,
+ NULL, &certs);
+ if (ret)
+ goto out;
+
+ for (i = 0; i < path.len; i++) {
+ ret = hx509_certs_add(context, certs, path.val[i]);
+ if (ret) {
+ hx509_certs_free(&certs);
+ goto out;
+ }
+ }
+ ret = hx509_certs_merge(context, certs, pool);
+ if (ret) {
+ hx509_certs_free(&certs);
+ goto out;
+ }
+
+ for (i = 0; i < path.len - 1; i++) {
+ int parent = (i < path.len - 1) ? i + 1 : i;
+
+ ret = hx509_revoke_verify(context,
+ ctx->revoke_ctx,
+ certs,
+ ctx->time_now,
+ path.val[i],
+ path.val[parent]);
+ if (ret) {
+ hx509_certs_free(&certs);
+ goto out;
+ }
+ }
+ hx509_certs_free(&certs);
+ }
+
+ /*
+ * Verify signatures, do this backward so public key working
+ * parameter is passed up from the anchor up though the chain.
+ */
+
+ for (i = path.len - 1; i >= 0; i--) {
+ Certificate *signer, *c;
+
+ c = _hx509_get_cert(path.val[i]);
+
+ /* is last in chain (trust anchor) */
+ if (i + 1 == path.len) {
+ int selfsigned;
+
+ signer = path.val[i]->data;
+
+ ret = certificate_is_self_signed(context, signer, &selfsigned);
+ if (ret)
+ goto out;
+
+ /* if trust anchor is not self signed, don't check sig */
+ if (!selfsigned)
+ continue;
+ } else {
+ /* take next certificate in chain */
+ signer = path.val[i + 1]->data;
+ }
+
+ /* verify signatureValue */
+ ret = _hx509_verify_signature_bitstring(context,
+ signer,
+ &c->signatureAlgorithm,
+ &c->tbsCertificate._save,
+ &c->signatureValue);
+ if (ret) {
+ hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
+ "Failed to verify signature of certificate");
+ goto out;
+ }
+ }
+
+out:
+ hx509_certs_free(&anchors);
+ free_Name(&proxy_issuer);
+ free_name_constraints(&nc);
+ _hx509_path_free(&path);
+
+ return ret;
+}
+
+/**
+ * Verify a signature made using the private key of an certificate.
+ *
+ * @param context A hx509 context.
+ * @param signer the certificate that made the signature.
+ * @param alg algorthm that was used to sign the data.
+ * @param data the data that was signed.
+ * @param sig the sigature to verify.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_crypto
+ */
+
+int
+hx509_verify_signature(hx509_context context,
+ const hx509_cert signer,
+ const AlgorithmIdentifier *alg,
+ const heim_octet_string *data,
+ const heim_octet_string *sig)
+{
+ return _hx509_verify_signature(context, signer->data, alg, data, sig);
+}
+
+
+/**
+ * Verify that the certificate is allowed to be used for the hostname
+ * and address.
+ *
+ * @param context A hx509 context.
+ * @param cert the certificate to match with
+ * @param flags Flags to modify the behavior:
+ * - HX509_VHN_F_ALLOW_NO_MATCH no match is ok
+ * @param type type of hostname:
+ * - HX509_HN_HOSTNAME for plain hostname.
+ * - HX509_HN_DNSSRV for DNS SRV names.
+ * @param hostname the hostname to check
+ * @param sa address of the host
+ * @param sa_size length of address
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
+int
+hx509_verify_hostname(hx509_context context,
+ const hx509_cert cert,
+ int flags,
+ hx509_hostname_type type,
+ const char *hostname,
+ const struct sockaddr *sa,
+ /* XXX krb5_socklen_t */ int sa_size)
+{
+ GeneralNames san;
+ int ret, i, j;
+
+ if (sa && sa_size <= 0)
+ return EINVAL;
+
+ memset(&san, 0, sizeof(san));
+
+ i = 0;
+ do {
+ ret = find_extension_subject_alt_name(cert->data, &i, &san);
+ if (ret == HX509_EXTENSION_NOT_FOUND) {
+ ret = 0;
+ break;
+ } else if (ret != 0)
+ break;
+
+ for (j = 0; j < san.len; j++) {
+ switch (san.val[j].element) {
+ case choice_GeneralName_dNSName:
+ if (strcasecmp(san.val[j].u.dNSName, hostname) == 0) {
+ free_GeneralNames(&san);
+ return 0;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ free_GeneralNames(&san);
+ } while (1);
+
+ {
+ const Name *name = &cert->data->tbsCertificate.subject;
+
+ /* match if first component is a CN= */
+ if (name->u.rdnSequence.len > 0
+ && name->u.rdnSequence.val[0].len == 1
+ && der_heim_oid_cmp(&name->u.rdnSequence.val[0].val[0].type,
+ oid_id_at_commonName()) == 0)
+ {
+ DirectoryString *ds = &name->u.rdnSequence.val[0].val[0].value;
+
+ switch (ds->element) {
+ case choice_DirectoryString_printableString:
+ if (strcasecmp(ds->u.printableString, hostname) == 0)
+ return 0;
+ break;
+ case choice_DirectoryString_ia5String:
+ if (strcasecmp(ds->u.ia5String, hostname) == 0)
+ return 0;
+ break;
+ case choice_DirectoryString_utf8String:
+ if (strcasecmp(ds->u.utf8String, hostname) == 0)
+ return 0;
+ default:
+ break;
+ }
+ }
+ }
+
+ if ((flags & HX509_VHN_F_ALLOW_NO_MATCH) == 0)
+ ret = HX509_NAME_CONSTRAINT_ERROR;
+
+ return ret;
+}
+
+int
+_hx509_set_cert_attribute(hx509_context context,
+ hx509_cert cert,
+ const heim_oid *oid,
+ const heim_octet_string *attr)
+{
+ hx509_cert_attribute a;
+ void *d;
+
+ if (hx509_cert_get_attribute(cert, oid) != NULL)
+ return 0;
+
+ d = realloc(cert->attrs.val,
+ sizeof(cert->attrs.val[0]) * (cert->attrs.len + 1));
+ if (d == NULL) {
+ hx509_clear_error_string(context);
+ return ENOMEM;
+ }
+ cert->attrs.val = d;
+
+ a = malloc(sizeof(*a));
+ if (a == NULL)
+ return ENOMEM;
+
+ der_copy_octet_string(attr, &a->data);
+ der_copy_oid(oid, &a->oid);
+
+ cert->attrs.val[cert->attrs.len] = a;
+ cert->attrs.len++;
+
+ return 0;
+}
+
+/**
+ * Get an external attribute for the certificate, examples are
+ * friendly name and id.
+ *
+ * @param cert hx509 certificate object to search
+ * @param oid an oid to search for.
+ *
+ * @return an hx509_cert_attribute, only valid as long as the
+ * certificate is referenced.
+ *
+ * @ingroup hx509_cert
+ */
+
+hx509_cert_attribute
+hx509_cert_get_attribute(hx509_cert cert, const heim_oid *oid)
+{
+ int i;
+ for (i = 0; i < cert->attrs.len; i++)
+ if (der_heim_oid_cmp(oid, &cert->attrs.val[i]->oid) == 0)
+ return cert->attrs.val[i];
+ return NULL;
+}
+
+/**
+ * Set the friendly name on the certificate.
+ *
+ * @param cert The certificate to set the friendly name on
+ * @param name Friendly name.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
+int
+hx509_cert_set_friendly_name(hx509_cert cert, const char *name)
+{
+ if (cert->friendlyname)
+ free(cert->friendlyname);
+ cert->friendlyname = strdup(name);
+ if (cert->friendlyname == NULL)
+ return ENOMEM;
+ return 0;
+}
+
+/**
+ * Get friendly name of the certificate.
+ *
+ * @param cert cert to get the friendly name from.
+ *
+ * @return an friendly name or NULL if there is. The friendly name is
+ * only valid as long as the certificate is referenced.
+ *
+ * @ingroup hx509_cert
+ */
+
+const char *
+hx509_cert_get_friendly_name(hx509_cert cert)
+{
+ hx509_cert_attribute a;
+ PKCS9_friendlyName n;
+ size_t sz;
+ int ret, i;
+
+ if (cert->friendlyname)
+ return cert->friendlyname;
+
+ a = hx509_cert_get_attribute(cert, oid_id_pkcs_9_at_friendlyName());
+ if (a == NULL) {
+ hx509_name name;
+
+ ret = hx509_cert_get_subject(cert, &name);
+ if (ret)
+ return NULL;
+ ret = hx509_name_to_string(name, &cert->friendlyname);
+ hx509_name_free(&name);
+ if (ret)
+ return NULL;
+ return cert->friendlyname;
+ }
+
+ ret = decode_PKCS9_friendlyName(a->data.data, a->data.length, &n, &sz);
+ if (ret)
+ return NULL;
+
+ if (n.len != 1) {
+ free_PKCS9_friendlyName(&n);
+ return NULL;
+ }
+
+ cert->friendlyname = malloc(n.val[0].length + 1);
+ if (cert->friendlyname == NULL) {
+ free_PKCS9_friendlyName(&n);
+ return NULL;
+ }
+
+ for (i = 0; i < n.val[0].length; i++) {
+ if (n.val[0].data[i] <= 0xff)
+ cert->friendlyname[i] = n.val[0].data[i] & 0xff;
+ else
+ cert->friendlyname[i] = 'X';
+ }
+ cert->friendlyname[i] = '\0';
+ free_PKCS9_friendlyName(&n);
+
+ return cert->friendlyname;
+}
+
+void
+_hx509_query_clear(hx509_query *q)
+{
+ memset(q, 0, sizeof(*q));
+}
+
+/**
+ * Allocate an query controller. Free using hx509_query_free().
+ *
+ * @param context A hx509 context.
+ * @param q return pointer to a hx509_query.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
+int
+hx509_query_alloc(hx509_context context, hx509_query **q)
+{
+ *q = calloc(1, sizeof(**q));
+ if (*q == NULL)
+ return ENOMEM;
+ return 0;
+}
+
+
+/**
+ * Set match options for the hx509 query controller.
+ *
+ * @param q query controller.
+ * @param option options to control the query controller.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
+void
+hx509_query_match_option(hx509_query *q, hx509_query_option option)
+{
+ switch(option) {
+ case HX509_QUERY_OPTION_PRIVATE_KEY:
+ q->match |= HX509_QUERY_PRIVATE_KEY;
+ break;
+ case HX509_QUERY_OPTION_KU_ENCIPHERMENT:
+ q->match |= HX509_QUERY_KU_ENCIPHERMENT;
+ break;
+ case HX509_QUERY_OPTION_KU_DIGITALSIGNATURE:
+ q->match |= HX509_QUERY_KU_DIGITALSIGNATURE;
+ break;
+ case HX509_QUERY_OPTION_KU_KEYCERTSIGN:
+ q->match |= HX509_QUERY_KU_KEYCERTSIGN;
+ break;
+ case HX509_QUERY_OPTION_END:
+ default:
+ break;
+ }
+}
+
+/**
+ * Set the issuer and serial number of match in the query
+ * controller. The function make copies of the isser and serial number.
+ *
+ * @param q a hx509 query controller
+ * @param issuer issuer to search for
+ * @param serialNumber the serialNumber of the issuer.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
+int
+hx509_query_match_issuer_serial(hx509_query *q,
+ const Name *issuer,
+ const heim_integer *serialNumber)
+{
+ int ret;
+ if (q->serial) {
+ der_free_heim_integer(q->serial);
+ free(q->serial);
+ }
+ q->serial = malloc(sizeof(*q->serial));
+ if (q->serial == NULL)
+ return ENOMEM;
+ ret = der_copy_heim_integer(serialNumber, q->serial);
+ if (ret) {
+ free(q->serial);
+ q->serial = NULL;
+ return ret;
+ }
+ if (q->issuer_name) {
+ free_Name(q->issuer_name);
+ free(q->issuer_name);
+ }
+ q->issuer_name = malloc(sizeof(*q->issuer_name));
+ if (q->issuer_name == NULL)
+ return ENOMEM;
+ ret = copy_Name(issuer, q->issuer_name);
+ if (ret) {
+ free(q->issuer_name);
+ q->issuer_name = NULL;
+ return ret;
+ }
+ q->match |= HX509_QUERY_MATCH_SERIALNUMBER|HX509_QUERY_MATCH_ISSUER_NAME;
+ return 0;
+}
+
+/**
+ * Set the query controller to match on a friendly name
+ *
+ * @param q a hx509 query controller.
+ * @param name a friendly name to match on
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
+int
+hx509_query_match_friendly_name(hx509_query *q, const char *name)
+{
+ if (q->friendlyname)
+ free(q->friendlyname);
+ q->friendlyname = strdup(name);
+ if (q->friendlyname == NULL)
+ return ENOMEM;
+ q->match |= HX509_QUERY_MATCH_FRIENDLY_NAME;
+ return 0;
+}
+
+/**
+ * Set the query controller to require an one specific EKU (extended
+ * key usage). Any previous EKU matching is overwitten. If NULL is
+ * passed in as the eku, the EKU requirement is reset.
+ *
+ * @param q a hx509 query controller.
+ * @param eku an EKU to match on.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
+int
+hx509_query_match_eku(hx509_query *q, const heim_oid *eku)
+{
+ int ret;
+
+ if (eku == NULL) {
+ if (q->eku) {
+ der_free_oid(q->eku);
+ free(q->eku);
+ q->eku = NULL;
+ }
+ q->match &= ~HX509_QUERY_MATCH_EKU;
+ } else {
+ if (q->eku) {
+ der_free_oid(q->eku);
+ } else {
+ q->eku = calloc(1, sizeof(*q->eku));
+ if (q->eku == NULL)
+ return ENOMEM;
+ }
+ ret = der_copy_oid(eku, q->eku);
+ if (ret) {
+ free(q->eku);
+ q->eku = NULL;
+ return ret;
+ }
+ q->match |= HX509_QUERY_MATCH_EKU;
+ }
+ return 0;
+}
+
+int
+hx509_query_match_expr(hx509_context context, hx509_query *q, const char *expr)
+{
+ if (q->expr) {
+ _hx509_expr_free(q->expr);
+ q->expr = NULL;
+ }
+
+ if (expr == NULL) {
+ q->match &= ~HX509_QUERY_MATCH_EXPR;
+ } else {
+ q->expr = _hx509_expr_parse(expr);
+ if (q->expr)
+ q->match |= HX509_QUERY_MATCH_EXPR;
+ }
+
+ return 0;
+}
+
+/**
+ * Set the query controller to match using a specific match function.
+ *
+ * @param q a hx509 query controller.
+ * @param func function to use for matching, if the argument is NULL,
+ * the match function is removed.
+ * @param ctx context passed to the function.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
+int
+hx509_query_match_cmp_func(hx509_query *q,
+ int (*func)(void *, hx509_cert),
+ void *ctx)
+{
+ if (func)
+ q->match |= HX509_QUERY_MATCH_FUNCTION;
+ else
+ q->match &= ~HX509_QUERY_MATCH_FUNCTION;
+ q->cmp_func = func;
+ q->cmp_func_ctx = ctx;
+ return 0;
+}
+
+/**
+ * Free the query controller.
+ *
+ * @param context A hx509 context.
+ * @param q a pointer to the query controller.
+ *
+ * @ingroup hx509_cert
+ */
+
+void
+hx509_query_free(hx509_context context, hx509_query *q)
+{
+ if (q == NULL)
+ return;
+
+ if (q->serial) {
+ der_free_heim_integer(q->serial);
+ free(q->serial);
+ }
+ if (q->issuer_name) {
+ free_Name(q->issuer_name);
+ free(q->issuer_name);
+ }
+ if (q->eku) {
+ der_free_oid(q->eku);
+ free(q->eku);
+ }
+ if (q->friendlyname)
+ free(q->friendlyname);
+ if (q->expr)
+ _hx509_expr_free(q->expr);
+
+ memset(q, 0, sizeof(*q));
+ free(q);
+}
+
+int
+_hx509_query_match_cert(hx509_context context, const hx509_query *q, hx509_cert cert)
+{
+ Certificate *c = _hx509_get_cert(cert);
+ int ret, diff;
+
+ _hx509_query_statistic(context, 1, q);
+
+ if ((q->match & HX509_QUERY_FIND_ISSUER_CERT) &&
+ _hx509_cert_is_parent_cmp(q->subject, c, 0) != 0)
+ return 0;
+
+ if ((q->match & HX509_QUERY_MATCH_CERTIFICATE) &&
+ _hx509_Certificate_cmp(q->certificate, c) != 0)
+ return 0;
+
+ if ((q->match & HX509_QUERY_MATCH_SERIALNUMBER)
+ && der_heim_integer_cmp(&c->tbsCertificate.serialNumber, q->serial) != 0)
+ return 0;
+
+ if (q->match & HX509_QUERY_MATCH_ISSUER_NAME) {
+ ret = _hx509_name_cmp(&c->tbsCertificate.issuer, q->issuer_name, &diff);
+ if (ret || diff)
+ return 0;
+ }
+
+ if (q->match & HX509_QUERY_MATCH_SUBJECT_NAME) {
+ ret = _hx509_name_cmp(&c->tbsCertificate.subject, q->subject_name, &diff);
+ if (ret || diff)
+ return 0;
+ }
+
+ if (q->match & HX509_QUERY_MATCH_SUBJECT_KEY_ID) {
+ SubjectKeyIdentifier si;
+
+ ret = _hx509_find_extension_subject_key_id(c, &si);
+ if (ret == 0) {
+ if (der_heim_octet_string_cmp(&si, q->subject_id) != 0)
+ ret = 1;
+ free_SubjectKeyIdentifier(&si);
+ }
+ if (ret)
+ return 0;
+ }
+ if ((q->match & HX509_QUERY_MATCH_ISSUER_ID))
+ return 0;
+ if ((q->match & HX509_QUERY_PRIVATE_KEY) &&
+ _hx509_cert_private_key(cert) == NULL)
+ return 0;
+
+ {
+ unsigned ku = 0;
+ if (q->match & HX509_QUERY_KU_DIGITALSIGNATURE)
+ ku |= (1 << 0);
+ if (q->match & HX509_QUERY_KU_NONREPUDIATION)
+ ku |= (1 << 1);
+ if (q->match & HX509_QUERY_KU_ENCIPHERMENT)
+ ku |= (1 << 2);
+ if (q->match & HX509_QUERY_KU_DATAENCIPHERMENT)
+ ku |= (1 << 3);
+ if (q->match & HX509_QUERY_KU_KEYAGREEMENT)
+ ku |= (1 << 4);
+ if (q->match & HX509_QUERY_KU_KEYCERTSIGN)
+ ku |= (1 << 5);
+ if (q->match & HX509_QUERY_KU_CRLSIGN)
+ ku |= (1 << 6);
+ if (ku && check_key_usage(context, c, ku, TRUE))
+ return 0;
+ }
+ if ((q->match & HX509_QUERY_ANCHOR))
+ return 0;
+
+ if (q->match & HX509_QUERY_MATCH_LOCAL_KEY_ID) {
+ hx509_cert_attribute a;
+
+ a = hx509_cert_get_attribute(cert, oid_id_pkcs_9_at_localKeyId());
+ if (a == NULL)
+ return 0;
+ if (der_heim_octet_string_cmp(&a->data, q->local_key_id) != 0)
+ return 0;
+ }
+
+ if (q->match & HX509_QUERY_NO_MATCH_PATH) {
+ size_t i;
+
+ for (i = 0; i < q->path->len; i++)
+ if (hx509_cert_cmp(q->path->val[i], cert) == 0)
+ return 0;
+ }
+ if (q->match & HX509_QUERY_MATCH_FRIENDLY_NAME) {
+ const char *name = hx509_cert_get_friendly_name(cert);
+ if (name == NULL)
+ return 0;
+ if (strcasecmp(q->friendlyname, name) != 0)
+ return 0;
+ }
+ if (q->match & HX509_QUERY_MATCH_FUNCTION) {
+ ret = (*q->cmp_func)(q->cmp_func_ctx, cert);
+ if (ret != 0)
+ return 0;
+ }
+
+ if (q->match & HX509_QUERY_MATCH_KEY_HASH_SHA1) {
+ heim_octet_string os;
+
+ os.data = c->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.data;
+ os.length =
+ c->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.length / 8;
+
+ ret = _hx509_verify_signature(context,
+ NULL,
+ hx509_signature_sha1(),
+ &os,
+ q->keyhash_sha1);
+ if (ret != 0)
+ return 0;
+ }
+
+ if (q->match & HX509_QUERY_MATCH_TIME) {
+ time_t t;
+ t = _hx509_Time2time_t(&c->tbsCertificate.validity.notBefore);
+ if (t > q->timenow)
+ return 0;
+ t = _hx509_Time2time_t(&c->tbsCertificate.validity.notAfter);
+ if (t < q->timenow)
+ return 0;
+ }
+
+ /* If an EKU is required, check the cert for it. */
+ if ((q->match & HX509_QUERY_MATCH_EKU) &&
+ hx509_cert_check_eku(context, cert, q->eku, 0))
+ return 0;
+
+ if ((q->match & HX509_QUERY_MATCH_EXPR)) {
+ hx509_env env = NULL;
+
+ ret = _hx509_cert_to_env(context, cert, &env);
+ if (ret)
+ return 0;
+
+ ret = _hx509_expr_eval(context, env, q->expr);
+ hx509_env_free(&env);
+ if (ret == 0)
+ return 0;
+ }
+
+ if (q->match & ~HX509_QUERY_MASK)
+ return 0;
+
+ return 1;
+}
+
+/**
+ * Set a statistic file for the query statistics.
+ *
+ * @param context A hx509 context.
+ * @param fn statistics file name
+ *
+ * @ingroup hx509_cert
+ */
+
+void
+hx509_query_statistic_file(hx509_context context, const char *fn)
+{
+ if (context->querystat)
+ free(context->querystat);
+ context->querystat = strdup(fn);
+}
+
+void
+_hx509_query_statistic(hx509_context context, int type, const hx509_query *q)
+{
+ FILE *f;
+ if (context->querystat == NULL)
+ return;
+ f = fopen(context->querystat, "a");
+ if (f == NULL)
+ return;
+ rk_cloexec_file(f);
+ fprintf(f, "%d %d\n", type, q->match);
+ fclose(f);
+}
+
+static const char *statname[] = {
+ "find issuer cert",
+ "match serialnumber",
+ "match issuer name",
+ "match subject name",
+ "match subject key id",
+ "match issuer id",
+ "private key",
+ "ku encipherment",
+ "ku digitalsignature",
+ "ku keycertsign",
+ "ku crlsign",
+ "ku nonrepudiation",
+ "ku keyagreement",
+ "ku dataencipherment",
+ "anchor",
+ "match certificate",
+ "match local key id",
+ "no match path",
+ "match friendly name",
+ "match function",
+ "match key hash sha1",
+ "match time"
+};
+
+struct stat_el {
+ unsigned long stats;
+ unsigned int index;
+};
+
+
+static int
+stat_sort(const void *a, const void *b)
+{
+ const struct stat_el *ae = a;
+ const struct stat_el *be = b;
+ return be->stats - ae->stats;
+}
+
+/**
+ * Unparse the statistics file and print the result on a FILE descriptor.
+ *
+ * @param context A hx509 context.
+ * @param printtype tyep to print
+ * @param out the FILE to write the data on.
+ *
+ * @ingroup hx509_cert
+ */
+
+void
+hx509_query_unparse_stats(hx509_context context, int printtype, FILE *out)
+{
+ rtbl_t t;
+ FILE *f;
+ int type, mask, i, num;
+ unsigned long multiqueries = 0, totalqueries = 0;
+ struct stat_el stats[32];
+
+ if (context->querystat == NULL)
+ return;
+ f = fopen(context->querystat, "r");
+ if (f == NULL) {
+ fprintf(out, "No statistic file %s: %s.\n",
+ context->querystat, strerror(errno));
+ return;
+ }
+ rk_cloexec_file(f);
+
+ for (i = 0; i < sizeof(stats)/sizeof(stats[0]); i++) {
+ stats[i].index = i;
+ stats[i].stats = 0;
+ }
+
+ while (fscanf(f, "%d %d\n", &type, &mask) == 2) {
+ if (type != printtype)
+ continue;
+ num = i = 0;
+ while (mask && i < sizeof(stats)/sizeof(stats[0])) {
+ if (mask & 1) {
+ stats[i].stats++;
+ num++;
+ }
+ mask = mask >>1 ;
+ i++;
+ }
+ if (num > 1)
+ multiqueries++;
+ totalqueries++;
+ }
+ fclose(f);
+
+ qsort(stats, sizeof(stats)/sizeof(stats[0]), sizeof(stats[0]), stat_sort);
+
+ t = rtbl_create();
+ if (t == NULL)
+ errx(1, "out of memory");
+
+ rtbl_set_separator (t, " ");
+
+ rtbl_add_column_by_id (t, 0, "Name", 0);
+ rtbl_add_column_by_id (t, 1, "Counter", 0);
+
+
+ for (i = 0; i < sizeof(stats)/sizeof(stats[0]); i++) {
+ char str[10];
+
+ if (stats[i].index < sizeof(statname)/sizeof(statname[0]))
+ rtbl_add_column_entry_by_id (t, 0, statname[stats[i].index]);
+ else {
+ snprintf(str, sizeof(str), "%d", stats[i].index);
+ rtbl_add_column_entry_by_id (t, 0, str);
+ }
+ snprintf(str, sizeof(str), "%lu", stats[i].stats);
+ rtbl_add_column_entry_by_id (t, 1, str);
+ }
+
+ rtbl_format(t, out);
+ rtbl_destroy(t);
+
+ fprintf(out, "\nQueries: multi %lu total %lu\n",
+ multiqueries, totalqueries);
+}
+
+/**
+ * Check the extended key usage on the hx509 certificate.
+ *
+ * @param context A hx509 context.
+ * @param cert A hx509 context.
+ * @param eku the EKU to check for
+ * @param allow_any_eku if the any EKU is set, allow that to be a
+ * substitute.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
+int
+hx509_cert_check_eku(hx509_context context, hx509_cert cert,
+ const heim_oid *eku, int allow_any_eku)
+{
+ ExtKeyUsage e;
+ int ret, i;
+
+ ret = find_extension_eku(_hx509_get_cert(cert), &e);
+ if (ret) {
+ hx509_clear_error_string(context);
+ return ret;
+ }
+
+ for (i = 0; i < e.len; i++) {
+ if (der_heim_oid_cmp(eku, &e.val[i]) == 0) {
+ free_ExtKeyUsage(&e);
+ return 0;
+ }
+ if (allow_any_eku) {
+#if 0
+ if (der_heim_oid_cmp(id_any_eku, &e.val[i]) == 0) {
+ free_ExtKeyUsage(&e);
+ return 0;
+ }
+#endif
+ }
+ }
+ free_ExtKeyUsage(&e);
+ hx509_clear_error_string(context);
+ return HX509_CERTIFICATE_MISSING_EKU;
+}
+
+int
+_hx509_cert_get_keyusage(hx509_context context,
+ hx509_cert c,
+ KeyUsage *ku)
+{
+ Certificate *cert;
+ const Extension *e;
+ size_t size;
+ int ret, i = 0;
+
+ memset(ku, 0, sizeof(*ku));
+
+ cert = _hx509_get_cert(c);
+
+ if (_hx509_cert_get_version(cert) < 3)
+ return 0;
+
+ e = find_extension(cert, oid_id_x509_ce_keyUsage(), &i);
+ if (e == NULL)
+ return HX509_KU_CERT_MISSING;
+
+ ret = decode_KeyUsage(e->extnValue.data, e->extnValue.length, ku, &size);
+ if (ret)
+ return ret;
+ return 0;
+}
+
+int
+_hx509_cert_get_eku(hx509_context context,
+ hx509_cert cert,
+ ExtKeyUsage *e)
+{
+ int ret;
+
+ memset(e, 0, sizeof(*e));
+
+ ret = find_extension_eku(_hx509_get_cert(cert), e);
+ if (ret && ret != HX509_EXTENSION_NOT_FOUND) {
+ hx509_clear_error_string(context);
+ return ret;
+ }
+ return 0;
+}
+
+/**
+ * Encodes the hx509 certificate as a DER encode binary.
+ *
+ * @param context A hx509 context.
+ * @param c the certificate to encode.
+ * @param os the encode certificate, set to NULL, 0 on case of
+ * error. Free the returned structure with hx509_xfree().
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
+int
+hx509_cert_binary(hx509_context context, hx509_cert c, heim_octet_string *os)
+{
+ size_t size;
+ int ret;
+
+ os->data = NULL;
+ os->length = 0;
+
+ ASN1_MALLOC_ENCODE(Certificate, os->data, os->length,
+ _hx509_get_cert(c), &size, ret);
+ if (ret) {
+ os->data = NULL;
+ os->length = 0;
+ return ret;
+ }
+ if (os->length != size)
+ _hx509_abort("internal ASN.1 encoder error");
+
+ return ret;
+}
+
+/*
+ * Last to avoid lost __attribute__s due to #undef.
+ */
+
+#undef __attribute__
+#define __attribute__(X)
+
+void
+_hx509_abort(const char *fmt, ...)
+ __attribute__ ((noreturn, format (printf, 1, 2)))
+{
+ va_list ap;
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+ printf("\n");
+ fflush(stdout);
+ abort();
+}
+
+/**
+ * Free a data element allocated in the library.
+ *
+ * @param ptr data to be freed.
+ *
+ * @ingroup hx509_misc
+ */
+
+void
+hx509_xfree(void *ptr)
+{
+ free(ptr);
+}
+
+/**
+ *
+ */
+
+int
+_hx509_cert_to_env(hx509_context context, hx509_cert cert, hx509_env *env)
+{
+ ExtKeyUsage eku;
+ hx509_name name;
+ char *buf;
+ int ret;
+ hx509_env envcert = NULL;
+
+ *env = NULL;
+
+ /* version */
+ asprintf(&buf, "%d", _hx509_cert_get_version(_hx509_get_cert(cert)));
+ ret = hx509_env_add(context, &envcert, "version", buf);
+ free(buf);
+ if (ret)
+ goto out;
+
+ /* subject */
+ ret = hx509_cert_get_subject(cert, &name);
+ if (ret)
+ goto out;
+
+ ret = hx509_name_to_string(name, &buf);
+ if (ret) {
+ hx509_name_free(&name);
+ goto out;
+ }
+
+ ret = hx509_env_add(context, &envcert, "subject", buf);
+ hx509_name_free(&name);
+ if (ret)
+ goto out;
+
+ /* issuer */
+ ret = hx509_cert_get_issuer(cert, &name);
+ if (ret)
+ goto out;
+
+ ret = hx509_name_to_string(name, &buf);
+ hx509_name_free(&name);
+ if (ret)
+ goto out;
+
+ ret = hx509_env_add(context, &envcert, "issuer", buf);
+ hx509_xfree(buf);
+ if (ret)
+ goto out;
+
+ /* eku */
+
+ ret = _hx509_cert_get_eku(context, cert, &eku);
+ if (ret == HX509_EXTENSION_NOT_FOUND)
+ ;
+ else if (ret != 0)
+ goto out;
+ else {
+ int i;
+ hx509_env enveku = NULL;
+
+ for (i = 0; i < eku.len; i++) {
+
+ ret = der_print_heim_oid(&eku.val[i], '.', &buf);
+ if (ret) {
+ free_ExtKeyUsage(&eku);
+ hx509_env_free(&enveku);
+ goto out;
+ }
+ ret = hx509_env_add(context, &enveku, buf, "oid-name-here");
+ free(buf);
+ if (ret) {
+ free_ExtKeyUsage(&eku);
+ hx509_env_free(&enveku);
+ goto out;
+ }
+ }
+ free_ExtKeyUsage(&eku);
+
+ ret = hx509_env_add_binding(context, &envcert, "eku", enveku);
+ if (ret) {
+ hx509_env_free(&enveku);
+ goto out;
+ }
+ }
+
+ ret = hx509_env_add_binding(context, env, "certificate", envcert);
+ if (ret)
+ goto out;
+
+ return 0;
+
+out:
+ hx509_env_free(&envcert);
+ return ret;
+}
diff --git a/source4/heimdal/lib/hx509/cms.c b/source4/heimdal/lib/hx509/cms.c
new file mode 100644
index 0000000000..ba1800ddf2
--- /dev/null
+++ b/source4/heimdal/lib/hx509/cms.c
@@ -0,0 +1,1434 @@
+/*
+ * Copyright (c) 2003 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hx_locl.h"
+RCSID("$Id$");
+
+/**
+ * @page page_cms CMS/PKCS7 message functions.
+ *
+ * CMS is defined in RFC 3369 and is an continuation of the RSA Labs
+ * standard PKCS7. The basic messages in CMS is
+ *
+ * - SignedData
+ * Data signed with private key (RSA, DSA, ECDSA) or secret
+ * (symmetric) key
+ * - EnvelopedData
+ * Data encrypted with private key (RSA)
+ * - EncryptedData
+ * Data encrypted with secret (symmetric) key.
+ * - ContentInfo
+ * Wrapper structure including type and data.
+ *
+ *
+ * See the library functions here: @ref hx509_cms
+ */
+
+#define ALLOC(X, N) (X) = calloc((N), sizeof(*(X)))
+#define ALLOC_SEQ(X, N) do { (X)->len = (N); ALLOC((X)->val, (N)); } while(0)
+
+/**
+ * Wrap data and oid in a ContentInfo and encode it.
+ *
+ * @param oid type of the content.
+ * @param buf data to be wrapped. If a NULL pointer is passed in, the
+ * optional content field in the ContentInfo is not going be filled
+ * in.
+ * @param res the encoded buffer, the result should be freed with
+ * der_free_octet_string().
+ *
+ * @return Returns an hx509 error code.
+ *
+ * @ingroup hx509_cms
+ */
+
+int
+hx509_cms_wrap_ContentInfo(const heim_oid *oid,
+ const heim_octet_string *buf,
+ heim_octet_string *res)
+{
+ ContentInfo ci;
+ size_t size;
+ int ret;
+
+ memset(res, 0, sizeof(*res));
+ memset(&ci, 0, sizeof(ci));
+
+ ret = der_copy_oid(oid, &ci.contentType);
+ if (ret)
+ return ret;
+ if (buf) {
+ ALLOC(ci.content, 1);
+ if (ci.content == NULL) {
+ free_ContentInfo(&ci);
+ return ENOMEM;
+ }
+ ci.content->data = malloc(buf->length);
+ if (ci.content->data == NULL) {
+ free_ContentInfo(&ci);
+ return ENOMEM;
+ }
+ memcpy(ci.content->data, buf->data, buf->length);
+ ci.content->length = buf->length;
+ }
+
+ ASN1_MALLOC_ENCODE(ContentInfo, res->data, res->length, &ci, &size, ret);
+ free_ContentInfo(&ci);
+ if (ret)
+ return ret;
+ if (res->length != size)
+ _hx509_abort("internal ASN.1 encoder error");
+
+ return 0;
+}
+
+/**
+ * Decode an ContentInfo and unwrap data and oid it.
+ *
+ * @param in the encoded buffer.
+ * @param oid type of the content.
+ * @param out data to be wrapped.
+ * @param have_data since the data is optional, this flags show dthe
+ * diffrence between no data and the zero length data.
+ *
+ * @return Returns an hx509 error code.
+ *
+ * @ingroup hx509_cms
+ */
+
+int
+hx509_cms_unwrap_ContentInfo(const heim_octet_string *in,
+ heim_oid *oid,
+ heim_octet_string *out,
+ int *have_data)
+{
+ ContentInfo ci;
+ size_t size;
+ int ret;
+
+ memset(oid, 0, sizeof(*oid));
+ memset(out, 0, sizeof(*out));
+
+ ret = decode_ContentInfo(in->data, in->length, &ci, &size);
+ if (ret)
+ return ret;
+
+ ret = der_copy_oid(&ci.contentType, oid);
+ if (ret) {
+ free_ContentInfo(&ci);
+ return ret;
+ }
+ if (ci.content) {
+ ret = der_copy_octet_string(ci.content, out);
+ if (ret) {
+ der_free_oid(oid);
+ free_ContentInfo(&ci);
+ return ret;
+ }
+ } else
+ memset(out, 0, sizeof(*out));
+
+ if (have_data)
+ *have_data = (ci.content != NULL) ? 1 : 0;
+
+ free_ContentInfo(&ci);
+
+ return 0;
+}
+
+#define CMS_ID_SKI 0
+#define CMS_ID_NAME 1
+
+static int
+fill_CMSIdentifier(const hx509_cert cert,
+ int type,
+ CMSIdentifier *id)
+{
+ int ret;
+
+ switch (type) {
+ case CMS_ID_SKI:
+ id->element = choice_CMSIdentifier_subjectKeyIdentifier;
+ ret = _hx509_find_extension_subject_key_id(_hx509_get_cert(cert),
+ &id->u.subjectKeyIdentifier);
+ if (ret == 0)
+ break;
+ /* FALL THOUGH */
+ case CMS_ID_NAME: {
+ hx509_name name;
+
+ id->element = choice_CMSIdentifier_issuerAndSerialNumber;
+ ret = hx509_cert_get_issuer(cert, &name);
+ if (ret)
+ return ret;
+ ret = hx509_name_to_Name(name, &id->u.issuerAndSerialNumber.issuer);
+ hx509_name_free(&name);
+ if (ret)
+ return ret;
+
+ ret = hx509_cert_get_serialnumber(cert, &id->u.issuerAndSerialNumber.serialNumber);
+ break;
+ }
+ default:
+ _hx509_abort("CMS fill identifier with unknown type");
+ }
+ return ret;
+}
+
+static int
+unparse_CMSIdentifier(hx509_context context,
+ CMSIdentifier *id,
+ char **str)
+{
+ int ret;
+
+ *str = NULL;
+ switch (id->element) {
+ case choice_CMSIdentifier_issuerAndSerialNumber: {
+ IssuerAndSerialNumber *iasn;
+ char *serial, *name;
+
+ iasn = &id->u.issuerAndSerialNumber;
+
+ ret = _hx509_Name_to_string(&iasn->issuer, &name);
+ if(ret)
+ return ret;
+ ret = der_print_hex_heim_integer(&iasn->serialNumber, &serial);
+ if (ret) {
+ free(name);
+ return ret;
+ }
+ asprintf(str, "certificate issued by %s with serial number %s",
+ name, serial);
+ free(name);
+ free(serial);
+ break;
+ }
+ case choice_CMSIdentifier_subjectKeyIdentifier: {
+ KeyIdentifier *ki = &id->u.subjectKeyIdentifier;
+ char *keyid;
+ ssize_t len;
+
+ len = hex_encode(ki->data, ki->length, &keyid);
+ if (len < 0)
+ return ENOMEM;
+
+ asprintf(str, "certificate with id %s", keyid);
+ free(keyid);
+ break;
+ }
+ default:
+ asprintf(str, "certificate have unknown CMSidentifier type");
+ break;
+ }
+ if (*str == NULL)
+ return ENOMEM;
+ return 0;
+}
+
+static int
+find_CMSIdentifier(hx509_context context,
+ CMSIdentifier *client,
+ hx509_certs certs,
+ time_t time_now,
+ hx509_cert *signer_cert,
+ int match)
+{
+ hx509_query q;
+ hx509_cert cert;
+ Certificate c;
+ int ret;
+
+ memset(&c, 0, sizeof(c));
+ _hx509_query_clear(&q);
+
+ *signer_cert = NULL;
+
+ switch (client->element) {
+ case choice_CMSIdentifier_issuerAndSerialNumber:
+ q.serial = &client->u.issuerAndSerialNumber.serialNumber;
+ q.issuer_name = &client->u.issuerAndSerialNumber.issuer;
+ q.match = HX509_QUERY_MATCH_SERIALNUMBER|HX509_QUERY_MATCH_ISSUER_NAME;
+ break;
+ case choice_CMSIdentifier_subjectKeyIdentifier:
+ q.subject_id = &client->u.subjectKeyIdentifier;
+ q.match = HX509_QUERY_MATCH_SUBJECT_KEY_ID;
+ break;
+ default:
+ hx509_set_error_string(context, 0, HX509_CMS_NO_RECIPIENT_CERTIFICATE,
+ "unknown CMS identifier element");
+ return HX509_CMS_NO_RECIPIENT_CERTIFICATE;
+ }
+
+ q.match |= match;
+
+ q.match |= HX509_QUERY_MATCH_TIME;
+ if (time_now)
+ q.timenow = time_now;
+ else
+ q.timenow = time(NULL);
+
+ ret = hx509_certs_find(context, certs, &q, &cert);
+ if (ret == HX509_CERT_NOT_FOUND) {
+ char *str;
+
+ ret = unparse_CMSIdentifier(context, client, &str);
+ if (ret == 0) {
+ hx509_set_error_string(context, 0,
+ HX509_CMS_NO_RECIPIENT_CERTIFICATE,
+ "Failed to find %s", str);
+ } else
+ hx509_clear_error_string(context);
+ return HX509_CMS_NO_RECIPIENT_CERTIFICATE;
+ } else if (ret) {
+ hx509_set_error_string(context, HX509_ERROR_APPEND,
+ HX509_CMS_NO_RECIPIENT_CERTIFICATE,
+ "Failed to find CMS id in cert store");
+ return HX509_CMS_NO_RECIPIENT_CERTIFICATE;
+ }
+
+ *signer_cert = cert;
+
+ return 0;
+}
+
+/**
+ * Decode and unencrypt EnvelopedData.
+ *
+ * Extract data and parameteres from from the EnvelopedData. Also
+ * supports using detached EnvelopedData.
+ *
+ * @param context A hx509 context.
+ * @param certs Certificate that can decrypt the EnvelopedData
+ * encryption key.
+ * @param flags HX509_CMS_UE flags to control the behavior.
+ * @param data pointer the structure the contains the DER/BER encoded
+ * EnvelopedData stucture.
+ * @param length length of the data that data point to.
+ * @param encryptedContent in case of detached signature, this
+ * contains the actual encrypted data, othersize its should be NULL.
+ * @param time_now set the current time, if zero the library uses now as the date.
+ * @param contentType output type oid, should be freed with der_free_oid().
+ * @param content the data, free with der_free_octet_string().
+ *
+ * @ingroup hx509_cms
+ */
+
+int
+hx509_cms_unenvelope(hx509_context context,
+ hx509_certs certs,
+ int flags,
+ const void *data,
+ size_t length,
+ const heim_octet_string *encryptedContent,
+ time_t time_now,
+ heim_oid *contentType,
+ heim_octet_string *content)
+{
+ heim_octet_string key;
+ EnvelopedData ed;
+ hx509_cert cert;
+ AlgorithmIdentifier *ai;
+ const heim_octet_string *enccontent;
+ heim_octet_string *params, params_data;
+ heim_octet_string ivec;
+ size_t size;
+ int ret, i, matched = 0, findflags = 0;
+
+
+ memset(&key, 0, sizeof(key));
+ memset(&ed, 0, sizeof(ed));
+ memset(&ivec, 0, sizeof(ivec));
+ memset(content, 0, sizeof(*content));
+ memset(contentType, 0, sizeof(*contentType));
+
+ if ((flags & HX509_CMS_UE_DONT_REQUIRE_KU_ENCIPHERMENT) == 0)
+ findflags |= HX509_QUERY_KU_ENCIPHERMENT;
+
+ ret = decode_EnvelopedData(data, length, &ed, &size);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to decode EnvelopedData");
+ return ret;
+ }
+
+ if (ed.recipientInfos.len == 0) {
+ ret = HX509_CMS_NO_RECIPIENT_CERTIFICATE;
+ hx509_set_error_string(context, 0, ret,
+ "No recipient info in enveloped data");
+ goto out;
+ }
+
+ enccontent = ed.encryptedContentInfo.encryptedContent;
+ if (enccontent == NULL) {
+ if (encryptedContent == NULL) {
+ ret = HX509_CMS_NO_DATA_AVAILABLE;
+ hx509_set_error_string(context, 0, ret,
+ "Content missing from encrypted data");
+ goto out;
+ }
+ enccontent = encryptedContent;
+ } else if (encryptedContent != NULL) {
+ ret = HX509_CMS_NO_DATA_AVAILABLE;
+ hx509_set_error_string(context, 0, ret,
+ "Both internal and external encrypted data");
+ goto out;
+ }
+
+ cert = NULL;
+ for (i = 0; i < ed.recipientInfos.len; i++) {
+ KeyTransRecipientInfo *ri;
+ char *str;
+ int ret2;
+
+ ri = &ed.recipientInfos.val[i];
+
+ ret = find_CMSIdentifier(context, &ri->rid, certs,
+ time_now, &cert,
+ HX509_QUERY_PRIVATE_KEY|findflags);
+ if (ret)
+ continue;
+
+ matched = 1; /* found a matching certificate, let decrypt */
+
+ ret = _hx509_cert_private_decrypt(context,
+ &ri->encryptedKey,
+ &ri->keyEncryptionAlgorithm.algorithm,
+ cert, &key);
+
+ hx509_cert_free(cert);
+ if (ret == 0)
+ break; /* succuessfully decrypted cert */
+ cert = NULL;
+ ret2 = unparse_CMSIdentifier(context, &ri->rid, &str);
+ if (ret2 == 0) {
+ hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
+ "Failed to decrypt with %s", str);
+ free(str);
+ }
+ }
+
+ if (!matched) {
+ ret = HX509_CMS_NO_RECIPIENT_CERTIFICATE;
+ hx509_set_error_string(context, 0, ret,
+ "No private key matched any certificate");
+ goto out;
+ }
+
+ if (cert == NULL) {
+ ret = HX509_CMS_NO_RECIPIENT_CERTIFICATE;
+ hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
+ "No private key decrypted the transfer key");
+ goto out;
+ }
+
+ ret = der_copy_oid(&ed.encryptedContentInfo.contentType, contentType);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to copy EnvelopedData content oid");
+ goto out;
+ }
+
+ ai = &ed.encryptedContentInfo.contentEncryptionAlgorithm;
+ if (ai->parameters) {
+ params_data.data = ai->parameters->data;
+ params_data.length = ai->parameters->length;
+ params = &params_data;
+ } else
+ params = NULL;
+
+ {
+ hx509_crypto crypto;
+
+ ret = hx509_crypto_init(context, NULL, &ai->algorithm, &crypto);
+ if (ret)
+ goto out;
+
+ if (params) {
+ ret = hx509_crypto_set_params(context, crypto, params, &ivec);
+ if (ret) {
+ hx509_crypto_destroy(crypto);
+ goto out;
+ }
+ }
+
+ ret = hx509_crypto_set_key_data(crypto, key.data, key.length);
+ if (ret) {
+ hx509_crypto_destroy(crypto);
+ hx509_set_error_string(context, 0, ret,
+ "Failed to set key for decryption "
+ "of EnvelopedData");
+ goto out;
+ }
+
+ ret = hx509_crypto_decrypt(crypto,
+ enccontent->data,
+ enccontent->length,
+ ivec.length ? &ivec : NULL,
+ content);
+ hx509_crypto_destroy(crypto);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to decrypt EnvelopedData");
+ goto out;
+ }
+ }
+
+out:
+
+ free_EnvelopedData(&ed);
+ der_free_octet_string(&key);
+ if (ivec.length)
+ der_free_octet_string(&ivec);
+ if (ret) {
+ der_free_oid(contentType);
+ der_free_octet_string(content);
+ }
+
+ return ret;
+}
+
+/**
+ * Encrypt end encode EnvelopedData.
+ *
+ * Encrypt and encode EnvelopedData. The data is encrypted with a
+ * random key and the the random key is encrypted with the
+ * certificates private key. This limits what private key type can be
+ * used to RSA.
+ *
+ * @param context A hx509 context.
+ * @param flags flags to control the behavior, no flags today
+ * @param cert Certificate to encrypt the EnvelopedData encryption key
+ * with.
+ * @param data pointer the data to encrypt.
+ * @param length length of the data that data point to.
+ * @param encryption_type Encryption cipher to use for the bulk data,
+ * use NULL to get default.
+ * @param contentType type of the data that is encrypted
+ * @param content the output of the function,
+ * free with der_free_octet_string().
+ *
+ * @ingroup hx509_cms
+ */
+
+int
+hx509_cms_envelope_1(hx509_context context,
+ int flags,
+ hx509_cert cert,
+ const void *data,
+ size_t length,
+ const heim_oid *encryption_type,
+ const heim_oid *contentType,
+ heim_octet_string *content)
+{
+ KeyTransRecipientInfo *ri;
+ heim_octet_string ivec;
+ heim_octet_string key;
+ hx509_crypto crypto = NULL;
+ EnvelopedData ed;
+ size_t size;
+ int ret;
+
+ memset(&ivec, 0, sizeof(ivec));
+ memset(&key, 0, sizeof(key));
+ memset(&ed, 0, sizeof(ed));
+ memset(content, 0, sizeof(*content));
+
+ if (encryption_type == NULL)
+ encryption_type = oid_id_aes_256_cbc();
+
+ ret = _hx509_check_key_usage(context, cert, 1 << 2, TRUE);
+ if (ret)
+ goto out;
+
+ ret = hx509_crypto_init(context, NULL, encryption_type, &crypto);
+ if (ret)
+ goto out;
+
+ ret = hx509_crypto_set_random_key(crypto, &key);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Create random key for EnvelopedData content");
+ goto out;
+ }
+
+ ret = hx509_crypto_random_iv(crypto, &ivec);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to create a random iv");
+ goto out;
+ }
+
+ ret = hx509_crypto_encrypt(crypto,
+ data,
+ length,
+ &ivec,
+ &ed.encryptedContentInfo.encryptedContent);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to encrypt EnvelopedData content");
+ goto out;
+ }
+
+ {
+ AlgorithmIdentifier *enc_alg;
+ enc_alg = &ed.encryptedContentInfo.contentEncryptionAlgorithm;
+ ret = der_copy_oid(encryption_type, &enc_alg->algorithm);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to set crypto oid "
+ "for EnvelopedData");
+ goto out;
+ }
+ ALLOC(enc_alg->parameters, 1);
+ if (enc_alg->parameters == NULL) {
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret,
+ "Failed to allocate crypto paramaters "
+ "for EnvelopedData");
+ goto out;
+ }
+
+ ret = hx509_crypto_get_params(context,
+ crypto,
+ &ivec,
+ enc_alg->parameters);
+ if (ret) {
+ goto out;
+ }
+ }
+
+ ALLOC_SEQ(&ed.recipientInfos, 1);
+ if (ed.recipientInfos.val == NULL) {
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret,
+ "Failed to allocate recipients info "
+ "for EnvelopedData");
+ goto out;
+ }
+
+ ri = &ed.recipientInfos.val[0];
+
+ ri->version = 0;
+ ret = fill_CMSIdentifier(cert, CMS_ID_SKI, &ri->rid);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to set CMS identifier info "
+ "for EnvelopedData");
+ goto out;
+ }
+
+ ret = _hx509_cert_public_encrypt(context,
+ &key, cert,
+ &ri->keyEncryptionAlgorithm.algorithm,
+ &ri->encryptedKey);
+ if (ret) {
+ hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
+ "Failed to encrypt transport key for "
+ "EnvelopedData");
+ goto out;
+ }
+
+ /*
+ *
+ */
+
+ ed.version = 0;
+ ed.originatorInfo = NULL;
+
+ ret = der_copy_oid(contentType, &ed.encryptedContentInfo.contentType);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to copy content oid for "
+ "EnvelopedData");
+ goto out;
+ }
+
+ ed.unprotectedAttrs = NULL;
+
+ ASN1_MALLOC_ENCODE(EnvelopedData, content->data, content->length,
+ &ed, &size, ret);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to encode EnvelopedData");
+ goto out;
+ }
+ if (size != content->length)
+ _hx509_abort("internal ASN.1 encoder error");
+
+out:
+ if (crypto)
+ hx509_crypto_destroy(crypto);
+ if (ret)
+ der_free_octet_string(content);
+ der_free_octet_string(&key);
+ der_free_octet_string(&ivec);
+ free_EnvelopedData(&ed);
+
+ return ret;
+}
+
+static int
+any_to_certs(hx509_context context, const SignedData *sd, hx509_certs certs)
+{
+ int ret, i;
+
+ if (sd->certificates == NULL)
+ return 0;
+
+ for (i = 0; i < sd->certificates->len; i++) {
+ hx509_cert c;
+
+ ret = hx509_cert_init_data(context,
+ sd->certificates->val[i].data,
+ sd->certificates->val[i].length,
+ &c);
+ if (ret)
+ return ret;
+ ret = hx509_certs_add(context, certs, c);
+ hx509_cert_free(c);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static const Attribute *
+find_attribute(const CMSAttributes *attr, const heim_oid *oid)
+{
+ int i;
+ for (i = 0; i < attr->len; i++)
+ if (der_heim_oid_cmp(&attr->val[i].type, oid) == 0)
+ return &attr->val[i];
+ return NULL;
+}
+
+/**
+ * Decode SignedData and verify that the signature is correct.
+ *
+ * @param context A hx509 context.
+ * @param ctx a hx509 version context
+ * @param data
+ * @param length length of the data that data point to.
+ * @param signedContent
+ * @param pool certificate pool to build certificates paths.
+ * @param contentType free with der_free_oid()
+ * @param content the output of the function, free with
+ * der_free_octet_string().
+ * @param signer_certs list of the cerficates used to sign this
+ * request, free with hx509_certs_free().
+ *
+ * @ingroup hx509_cms
+ */
+
+int
+hx509_cms_verify_signed(hx509_context context,
+ hx509_verify_ctx ctx,
+ const void *data,
+ size_t length,
+ const heim_octet_string *signedContent,
+ hx509_certs pool,
+ heim_oid *contentType,
+ heim_octet_string *content,
+ hx509_certs *signer_certs)
+{
+ SignerInfo *signer_info;
+ hx509_cert cert = NULL;
+ hx509_certs certs = NULL;
+ SignedData sd;
+ size_t size;
+ int ret, i, found_valid_sig;
+
+ *signer_certs = NULL;
+ content->data = NULL;
+ content->length = 0;
+ contentType->length = 0;
+ contentType->components = NULL;
+
+ memset(&sd, 0, sizeof(sd));
+
+ ret = decode_SignedData(data, length, &sd, &size);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to decode SignedData");
+ goto out;
+ }
+
+ if (sd.encapContentInfo.eContent == NULL && signedContent == NULL) {
+ ret = HX509_CMS_NO_DATA_AVAILABLE;
+ hx509_set_error_string(context, 0, ret,
+ "No content data in SignedData");
+ goto out;
+ }
+ if (sd.encapContentInfo.eContent && signedContent) {
+ ret = HX509_CMS_NO_DATA_AVAILABLE;
+ hx509_set_error_string(context, 0, ret,
+ "Both external and internal SignedData");
+ goto out;
+ }
+ if (sd.encapContentInfo.eContent)
+ signedContent = sd.encapContentInfo.eContent;
+
+ ret = hx509_certs_init(context, "MEMORY:cms-cert-buffer",
+ 0, NULL, &certs);
+ if (ret)
+ goto out;
+
+ ret = hx509_certs_init(context, "MEMORY:cms-signer-certs",
+ 0, NULL, signer_certs);
+ if (ret)
+ goto out;
+
+ /* XXX Check CMS version */
+
+ ret = any_to_certs(context, &sd, certs);
+ if (ret)
+ goto out;
+
+ if (pool) {
+ ret = hx509_certs_merge(context, certs, pool);
+ if (ret)
+ goto out;
+ }
+
+ for (found_valid_sig = 0, i = 0; i < sd.signerInfos.len; i++) {
+ heim_octet_string *signed_data;
+ const heim_oid *match_oid;
+ heim_oid decode_oid;
+
+ signer_info = &sd.signerInfos.val[i];
+ match_oid = NULL;
+
+ if (signer_info->signature.length == 0) {
+ ret = HX509_CMS_MISSING_SIGNER_DATA;
+ hx509_set_error_string(context, 0, ret,
+ "SignerInfo %d in SignedData "
+ "missing sigature", i);
+ continue;
+ }
+
+ ret = find_CMSIdentifier(context, &signer_info->sid, certs,
+ _hx509_verify_get_time(ctx), &cert,
+ HX509_QUERY_KU_DIGITALSIGNATURE);
+ if (ret)
+ continue;
+
+ if (signer_info->signedAttrs) {
+ const Attribute *attr;
+
+ CMSAttributes sa;
+ heim_octet_string os;
+
+ sa.val = signer_info->signedAttrs->val;
+ sa.len = signer_info->signedAttrs->len;
+
+ /* verify that sigature exists */
+ attr = find_attribute(&sa, oid_id_pkcs9_messageDigest());
+ if (attr == NULL) {
+ ret = HX509_CRYPTO_SIGNATURE_MISSING;
+ hx509_set_error_string(context, 0, ret,
+ "SignerInfo have signed attributes "
+ "but messageDigest (signature) "
+ "is missing");
+ goto next_sigature;
+ }
+ if (attr->value.len != 1) {
+ ret = HX509_CRYPTO_SIGNATURE_MISSING;
+ hx509_set_error_string(context, 0, ret,
+ "SignerInfo have more then one "
+ "messageDigest (signature)");
+ goto next_sigature;
+ }
+
+ ret = decode_MessageDigest(attr->value.val[0].data,
+ attr->value.val[0].length,
+ &os,
+ &size);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to decode "
+ "messageDigest (signature)");
+ goto next_sigature;
+ }
+
+ ret = _hx509_verify_signature(context,
+ NULL,
+ &signer_info->digestAlgorithm,
+ signedContent,
+ &os);
+ der_free_octet_string(&os);
+ if (ret) {
+ hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
+ "Failed to verify messageDigest");
+ goto next_sigature;
+ }
+
+ /*
+ * Fetch content oid inside signedAttrs or set it to
+ * id-pkcs7-data.
+ */
+ attr = find_attribute(&sa, oid_id_pkcs9_contentType());
+ if (attr == NULL) {
+ match_oid = oid_id_pkcs7_data();
+ } else {
+ if (attr->value.len != 1) {
+ ret = HX509_CMS_DATA_OID_MISMATCH;
+ hx509_set_error_string(context, 0, ret,
+ "More then one oid in signedAttrs");
+ goto next_sigature;
+
+ }
+ ret = decode_ContentType(attr->value.val[0].data,
+ attr->value.val[0].length,
+ &decode_oid,
+ &size);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to decode "
+ "oid in signedAttrs");
+ goto next_sigature;
+ }
+ match_oid = &decode_oid;
+ }
+
+ ALLOC(signed_data, 1);
+ if (signed_data == NULL) {
+ if (match_oid == &decode_oid)
+ der_free_oid(&decode_oid);
+ ret = ENOMEM;
+ hx509_clear_error_string(context);
+ goto next_sigature;
+ }
+
+ ASN1_MALLOC_ENCODE(CMSAttributes,
+ signed_data->data,
+ signed_data->length,
+ &sa,
+ &size, ret);
+ if (ret) {
+ if (match_oid == &decode_oid)
+ der_free_oid(&decode_oid);
+ free(signed_data);
+ hx509_clear_error_string(context);
+ goto next_sigature;
+ }
+ if (size != signed_data->length)
+ _hx509_abort("internal ASN.1 encoder error");
+
+ } else {
+ signed_data = rk_UNCONST(signedContent);
+ match_oid = oid_id_pkcs7_data();
+ }
+
+ if (der_heim_oid_cmp(match_oid, &sd.encapContentInfo.eContentType)) {
+ ret = HX509_CMS_DATA_OID_MISMATCH;
+ hx509_set_error_string(context, 0, ret,
+ "Oid in message mismatch from the expected");
+ }
+ if (match_oid == &decode_oid)
+ der_free_oid(&decode_oid);
+
+ if (ret == 0) {
+ ret = hx509_verify_signature(context,
+ cert,
+ &signer_info->signatureAlgorithm,
+ signed_data,
+ &signer_info->signature);
+ if (ret)
+ hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
+ "Failed to verify sigature in "
+ "CMS SignedData");
+ }
+ if (signed_data != signedContent) {
+ der_free_octet_string(signed_data);
+ free(signed_data);
+ }
+ if (ret)
+ goto next_sigature;
+
+ ret = hx509_verify_path(context, ctx, cert, certs);
+ if (ret)
+ goto next_sigature;
+
+ ret = hx509_certs_add(context, *signer_certs, cert);
+ if (ret)
+ goto next_sigature;
+
+ found_valid_sig++;
+
+ next_sigature:
+ if (cert)
+ hx509_cert_free(cert);
+ cert = NULL;
+ }
+ if (found_valid_sig == 0) {
+ if (ret == 0) {
+ ret = HX509_CMS_SIGNER_NOT_FOUND;
+ hx509_set_error_string(context, 0, ret,
+ "No signers where found");
+ }
+ goto out;
+ }
+
+ ret = der_copy_oid(&sd.encapContentInfo.eContentType, contentType);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+
+ content->data = malloc(signedContent->length);
+ if (content->data == NULL) {
+ hx509_clear_error_string(context);
+ ret = ENOMEM;
+ goto out;
+ }
+ content->length = signedContent->length;
+ memcpy(content->data, signedContent->data, content->length);
+
+out:
+ free_SignedData(&sd);
+ if (certs)
+ hx509_certs_free(&certs);
+ if (ret) {
+ if (*signer_certs)
+ hx509_certs_free(signer_certs);
+ der_free_oid(contentType);
+ der_free_octet_string(content);
+ }
+
+ return ret;
+}
+
+static int
+add_one_attribute(Attribute **attr,
+ unsigned int *len,
+ const heim_oid *oid,
+ heim_octet_string *data)
+{
+ void *d;
+ int ret;
+
+ d = realloc(*attr, sizeof((*attr)[0]) * (*len + 1));
+ if (d == NULL)
+ return ENOMEM;
+ (*attr) = d;
+
+ ret = der_copy_oid(oid, &(*attr)[*len].type);
+ if (ret)
+ return ret;
+
+ ALLOC_SEQ(&(*attr)[*len].value, 1);
+ if ((*attr)[*len].value.val == NULL) {
+ der_free_oid(&(*attr)[*len].type);
+ return ENOMEM;
+ }
+
+ (*attr)[*len].value.val[0].data = data->data;
+ (*attr)[*len].value.val[0].length = data->length;
+
+ *len += 1;
+
+ return 0;
+}
+
+/**
+ * Decode SignedData and verify that the signature is correct.
+ *
+ * @param context A hx509 context.
+ * @param flags
+ * @param eContentType the type of the data.
+ * @param data data to sign
+ * @param length length of the data that data point to.
+ * @param digest_alg digest algorithm to use, use NULL to get the
+ * default or the peer determined algorithm.
+ * @param cert certificate to use for sign the data.
+ * @param peer info about the peer the message to send the message to,
+ * like what digest algorithm to use.
+ * @param anchors trust anchors that the client will use, used to
+ * polulate the certificates included in the message
+ * @param pool certificates to use in try to build the path to the
+ * trust anchors.
+ * @param signed_data the output of the function, free with
+ * der_free_octet_string().
+ *
+ * @ingroup hx509_cms
+ */
+
+int
+hx509_cms_create_signed_1(hx509_context context,
+ int flags,
+ const heim_oid *eContentType,
+ const void *data, size_t length,
+ const AlgorithmIdentifier *digest_alg,
+ hx509_cert cert,
+ hx509_peer_info peer,
+ hx509_certs anchors,
+ hx509_certs pool,
+ heim_octet_string *signed_data)
+{
+ AlgorithmIdentifier digest;
+ hx509_name name;
+ SignerInfo *signer_info;
+ heim_octet_string buf, content, sigdata = { 0, NULL };
+ SignedData sd;
+ int ret;
+ size_t size;
+ hx509_path path;
+ int cmsidflag = CMS_ID_SKI;
+
+ memset(&sd, 0, sizeof(sd));
+ memset(&name, 0, sizeof(name));
+ memset(&path, 0, sizeof(path));
+ memset(&digest, 0, sizeof(digest));
+
+ content.data = rk_UNCONST(data);
+ content.length = length;
+
+ if (flags & HX509_CMS_SIGATURE_ID_NAME)
+ cmsidflag = CMS_ID_NAME;
+
+ if (_hx509_cert_private_key(cert) == NULL) {
+ hx509_set_error_string(context, 0, HX509_PRIVATE_KEY_MISSING,
+ "Private key missing for signing");
+ return HX509_PRIVATE_KEY_MISSING;
+ }
+
+ if (digest_alg == NULL) {
+ ret = hx509_crypto_select(context, HX509_SELECT_DIGEST,
+ _hx509_cert_private_key(cert), peer, &digest);
+ } else {
+ ret = copy_AlgorithmIdentifier(digest_alg, &digest);
+ if (ret)
+ hx509_clear_error_string(context);
+ }
+ if (ret)
+ goto out;
+
+ sd.version = CMSVersion_v3;
+
+ if (eContentType == NULL)
+ eContentType = oid_id_pkcs7_data();
+
+ der_copy_oid(eContentType, &sd.encapContentInfo.eContentType);
+
+ /* */
+ if ((flags & HX509_CMS_SIGATURE_DETACHED) == 0) {
+ ALLOC(sd.encapContentInfo.eContent, 1);
+ if (sd.encapContentInfo.eContent == NULL) {
+ hx509_clear_error_string(context);
+ ret = ENOMEM;
+ goto out;
+ }
+
+ sd.encapContentInfo.eContent->data = malloc(length);
+ if (sd.encapContentInfo.eContent->data == NULL) {
+ hx509_clear_error_string(context);
+ ret = ENOMEM;
+ goto out;
+ }
+ memcpy(sd.encapContentInfo.eContent->data, data, length);
+ sd.encapContentInfo.eContent->length = length;
+ }
+
+ ALLOC_SEQ(&sd.signerInfos, 1);
+ if (sd.signerInfos.val == NULL) {
+ hx509_clear_error_string(context);
+ ret = ENOMEM;
+ goto out;
+ }
+
+ signer_info = &sd.signerInfos.val[0];
+
+ signer_info->version = 1;
+
+ ret = fill_CMSIdentifier(cert, cmsidflag, &signer_info->sid);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+
+ signer_info->signedAttrs = NULL;
+ signer_info->unsignedAttrs = NULL;
+
+
+ ret = copy_AlgorithmIdentifier(&digest, &signer_info->digestAlgorithm);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+
+ /*
+ * If it isn't pkcs7-data send signedAttributes
+ */
+
+ if (der_heim_oid_cmp(eContentType, oid_id_pkcs7_data()) != 0) {
+ CMSAttributes sa;
+ heim_octet_string sig;
+
+ ALLOC(signer_info->signedAttrs, 1);
+ if (signer_info->signedAttrs == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+
+ ret = _hx509_create_signature(context,
+ NULL,
+ &digest,
+ &content,
+ NULL,
+ &sig);
+ if (ret)
+ goto out;
+
+ ASN1_MALLOC_ENCODE(MessageDigest,
+ buf.data,
+ buf.length,
+ &sig,
+ &size,
+ ret);
+ der_free_octet_string(&sig);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+ if (size != buf.length)
+ _hx509_abort("internal ASN.1 encoder error");
+
+ ret = add_one_attribute(&signer_info->signedAttrs->val,
+ &signer_info->signedAttrs->len,
+ oid_id_pkcs9_messageDigest(),
+ &buf);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+
+
+ ASN1_MALLOC_ENCODE(ContentType,
+ buf.data,
+ buf.length,
+ eContentType,
+ &size,
+ ret);
+ if (ret)
+ goto out;
+ if (size != buf.length)
+ _hx509_abort("internal ASN.1 encoder error");
+
+ ret = add_one_attribute(&signer_info->signedAttrs->val,
+ &signer_info->signedAttrs->len,
+ oid_id_pkcs9_contentType(),
+ &buf);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+
+ sa.val = signer_info->signedAttrs->val;
+ sa.len = signer_info->signedAttrs->len;
+
+ ASN1_MALLOC_ENCODE(CMSAttributes,
+ sigdata.data,
+ sigdata.length,
+ &sa,
+ &size,
+ ret);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+ if (size != sigdata.length)
+ _hx509_abort("internal ASN.1 encoder error");
+ } else {
+ sigdata.data = content.data;
+ sigdata.length = content.length;
+ }
+
+
+ {
+ AlgorithmIdentifier sigalg;
+
+ ret = hx509_crypto_select(context, HX509_SELECT_PUBLIC_SIG,
+ _hx509_cert_private_key(cert), peer,
+ &sigalg);
+ if (ret)
+ goto out;
+
+ ret = _hx509_create_signature(context,
+ _hx509_cert_private_key(cert),
+ &sigalg,
+ &sigdata,
+ &signer_info->signatureAlgorithm,
+ &signer_info->signature);
+ free_AlgorithmIdentifier(&sigalg);
+ if (ret)
+ goto out;
+ }
+
+ ALLOC_SEQ(&sd.digestAlgorithms, 1);
+ if (sd.digestAlgorithms.val == NULL) {
+ ret = ENOMEM;
+ hx509_clear_error_string(context);
+ goto out;
+ }
+
+ ret = copy_AlgorithmIdentifier(&digest, &sd.digestAlgorithms.val[0]);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+
+ /*
+ * Provide best effort path
+ */
+ if (pool) {
+ _hx509_calculate_path(context,
+ HX509_CALCULATE_PATH_NO_ANCHOR,
+ time(NULL),
+ anchors,
+ 0,
+ cert,
+ pool,
+ &path);
+ } else
+ _hx509_path_append(context, &path, cert);
+
+
+ if (path.len) {
+ int i;
+
+ ALLOC(sd.certificates, 1);
+ if (sd.certificates == NULL) {
+ hx509_clear_error_string(context);
+ ret = ENOMEM;
+ goto out;
+ }
+ ALLOC_SEQ(sd.certificates, path.len);
+ if (sd.certificates->val == NULL) {
+ hx509_clear_error_string(context);
+ ret = ENOMEM;
+ goto out;
+ }
+
+ for (i = 0; i < path.len; i++) {
+ ret = hx509_cert_binary(context, path.val[i],
+ &sd.certificates->val[i]);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+ }
+ }
+
+ ASN1_MALLOC_ENCODE(SignedData,
+ signed_data->data, signed_data->length,
+ &sd, &size, ret);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+ if (signed_data->length != size)
+ _hx509_abort("internal ASN.1 encoder error");
+
+out:
+ if (sigdata.data != content.data)
+ der_free_octet_string(&sigdata);
+ free_AlgorithmIdentifier(&digest);
+ _hx509_path_free(&path);
+ free_SignedData(&sd);
+
+ return ret;
+}
+
+int
+hx509_cms_decrypt_encrypted(hx509_context context,
+ hx509_lock lock,
+ const void *data,
+ size_t length,
+ heim_oid *contentType,
+ heim_octet_string *content)
+{
+ heim_octet_string cont;
+ CMSEncryptedData ed;
+ AlgorithmIdentifier *ai;
+ int ret;
+
+ memset(content, 0, sizeof(*content));
+ memset(&cont, 0, sizeof(cont));
+
+ ret = decode_CMSEncryptedData(data, length, &ed, NULL);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to decode CMSEncryptedData");
+ return ret;
+ }
+
+ if (ed.encryptedContentInfo.encryptedContent == NULL) {
+ ret = HX509_CMS_NO_DATA_AVAILABLE;
+ hx509_set_error_string(context, 0, ret,
+ "No content in EncryptedData");
+ goto out;
+ }
+
+ ret = der_copy_oid(&ed.encryptedContentInfo.contentType, contentType);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+
+ ai = &ed.encryptedContentInfo.contentEncryptionAlgorithm;
+ if (ai->parameters == NULL) {
+ ret = HX509_ALG_NOT_SUPP;
+ hx509_clear_error_string(context);
+ goto out;
+ }
+
+ ret = _hx509_pbe_decrypt(context,
+ lock,
+ ai,
+ ed.encryptedContentInfo.encryptedContent,
+ &cont);
+ if (ret)
+ goto out;
+
+ *content = cont;
+
+out:
+ if (ret) {
+ if (cont.data)
+ free(cont.data);
+ }
+ free_CMSEncryptedData(&ed);
+ return ret;
+}
diff --git a/source4/heimdal/lib/hx509/collector.c b/source4/heimdal/lib/hx509/collector.c
new file mode 100644
index 0000000000..b59052bb4e
--- /dev/null
+++ b/source4/heimdal/lib/hx509/collector.c
@@ -0,0 +1,329 @@
+/*
+ * Copyright (c) 2004 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hx_locl.h"
+RCSID("$Id$");
+
+struct private_key {
+ AlgorithmIdentifier alg;
+ hx509_private_key private_key;
+ heim_octet_string localKeyId;
+};
+
+struct hx509_collector {
+ hx509_lock lock;
+ hx509_certs unenvelop_certs;
+ hx509_certs certs;
+ struct {
+ struct private_key **data;
+ size_t len;
+ } val;
+};
+
+
+int
+_hx509_collector_alloc(hx509_context context, hx509_lock lock, struct hx509_collector **collector)
+{
+ struct hx509_collector *c;
+ int ret;
+
+ *collector = NULL;
+
+ c = calloc(1, sizeof(*c));
+ if (c == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+ c->lock = lock;
+
+ ret = hx509_certs_init(context, "MEMORY:collector-unenvelop-cert",
+ 0,NULL, &c->unenvelop_certs);
+ if (ret) {
+ free(c);
+ return ret;
+ }
+ c->val.data = NULL;
+ c->val.len = 0;
+ ret = hx509_certs_init(context, "MEMORY:collector-tmp-store",
+ 0, NULL, &c->certs);
+ if (ret) {
+ hx509_certs_free(&c->unenvelop_certs);
+ free(c);
+ return ret;
+ }
+
+ *collector = c;
+ return 0;
+}
+
+hx509_lock
+_hx509_collector_get_lock(struct hx509_collector *c)
+{
+ return c->lock;
+}
+
+
+int
+_hx509_collector_certs_add(hx509_context context,
+ struct hx509_collector *c,
+ hx509_cert cert)
+{
+ return hx509_certs_add(context, c->certs, cert);
+}
+
+static void
+free_private_key(struct private_key *key)
+{
+ free_AlgorithmIdentifier(&key->alg);
+ if (key->private_key)
+ _hx509_private_key_free(&key->private_key);
+ der_free_octet_string(&key->localKeyId);
+ free(key);
+}
+
+int
+_hx509_collector_private_key_add(hx509_context context,
+ struct hx509_collector *c,
+ const AlgorithmIdentifier *alg,
+ hx509_private_key private_key,
+ const heim_octet_string *key_data,
+ const heim_octet_string *localKeyId)
+{
+ struct private_key *key;
+ void *d;
+ int ret;
+
+ key = calloc(1, sizeof(*key));
+ if (key == NULL)
+ return ENOMEM;
+
+ d = realloc(c->val.data, (c->val.len + 1) * sizeof(c->val.data[0]));
+ if (d == NULL) {
+ free(key);
+ hx509_set_error_string(context, 0, ENOMEM, "Out of memory");
+ return ENOMEM;
+ }
+ c->val.data = d;
+
+ ret = copy_AlgorithmIdentifier(alg, &key->alg);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Failed to copy "
+ "AlgorithmIdentifier");
+ goto out;
+ }
+ if (private_key) {
+ key->private_key = private_key;
+ } else {
+ ret = _hx509_parse_private_key(context, &alg->algorithm,
+ key_data->data, key_data->length,
+ &key->private_key);
+ if (ret)
+ goto out;
+ }
+ if (localKeyId) {
+ ret = der_copy_octet_string(localKeyId, &key->localKeyId);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to copy localKeyId");
+ goto out;
+ }
+ } else
+ memset(&key->localKeyId, 0, sizeof(key->localKeyId));
+
+ c->val.data[c->val.len] = key;
+ c->val.len++;
+
+out:
+ if (ret)
+ free_private_key(key);
+
+ return ret;
+}
+
+static int
+match_localkeyid(hx509_context context,
+ struct private_key *value,
+ hx509_certs certs)
+{
+ hx509_cert cert;
+ hx509_query q;
+ int ret;
+
+ if (value->localKeyId.length == 0) {
+ hx509_set_error_string(context, 0, HX509_LOCAL_ATTRIBUTE_MISSING,
+ "No local key attribute on private key");
+ return HX509_LOCAL_ATTRIBUTE_MISSING;
+ }
+
+ _hx509_query_clear(&q);
+ q.match |= HX509_QUERY_MATCH_LOCAL_KEY_ID;
+
+ q.local_key_id = &value->localKeyId;
+
+ ret = hx509_certs_find(context, certs, &q, &cert);
+ if (ret == 0) {
+
+ if (value->private_key)
+ _hx509_cert_assign_key(cert, value->private_key);
+ hx509_cert_free(cert);
+ }
+ return ret;
+}
+
+static int
+match_keys(hx509_context context, struct private_key *value, hx509_certs certs)
+{
+ hx509_cursor cursor;
+ hx509_cert c;
+ int ret, found = HX509_CERT_NOT_FOUND;
+
+ if (value->private_key == NULL) {
+ hx509_set_error_string(context, 0, HX509_PRIVATE_KEY_MISSING,
+ "No private key to compare with");
+ return HX509_PRIVATE_KEY_MISSING;
+ }
+
+ ret = hx509_certs_start_seq(context, certs, &cursor);
+ if (ret)
+ return ret;
+
+ c = NULL;
+ while (1) {
+ ret = hx509_certs_next_cert(context, certs, cursor, &c);
+ if (ret)
+ break;
+ if (c == NULL)
+ break;
+ if (_hx509_cert_private_key(c)) {
+ hx509_cert_free(c);
+ continue;
+ }
+
+ ret = _hx509_match_keys(c, value->private_key);
+ if (ret) {
+ _hx509_cert_assign_key(c, value->private_key);
+ hx509_cert_free(c);
+ found = 0;
+ break;
+ }
+ hx509_cert_free(c);
+ }
+
+ hx509_certs_end_seq(context, certs, cursor);
+
+ if (found)
+ hx509_clear_error_string(context);
+
+ return found;
+}
+
+int
+_hx509_collector_collect_certs(hx509_context context,
+ struct hx509_collector *c,
+ hx509_certs *ret_certs)
+{
+ hx509_certs certs;
+ int ret, i;
+
+ *ret_certs = NULL;
+
+ ret = hx509_certs_init(context, "MEMORY:collector-store", 0, NULL, &certs);
+ if (ret)
+ return ret;
+
+ ret = hx509_certs_merge(context, certs, c->certs);
+ if (ret) {
+ hx509_certs_free(&certs);
+ return ret;
+ }
+
+ for (i = 0; i < c->val.len; i++) {
+ ret = match_localkeyid(context, c->val.data[i], certs);
+ if (ret == 0)
+ continue;
+ ret = match_keys(context, c->val.data[i], certs);
+ if (ret == 0)
+ continue;
+ }
+
+ *ret_certs = certs;
+
+ return 0;
+}
+
+int
+_hx509_collector_collect_private_keys(hx509_context context,
+ struct hx509_collector *c,
+ hx509_private_key **keys)
+{
+ int i, nkeys;
+
+ *keys = NULL;
+
+ for (i = 0, nkeys = 0; i < c->val.len; i++)
+ if (c->val.data[i]->private_key)
+ nkeys++;
+
+ *keys = calloc(nkeys + 1, sizeof(**keys));
+ if (*keys == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "malloc - out of memory");
+ return ENOMEM;
+ }
+
+ for (i = 0, nkeys = 0; i < c->val.len; i++) {
+ if (c->val.data[i]->private_key) {
+ (*keys)[nkeys++] = c->val.data[i]->private_key;
+ c->val.data[i]->private_key = NULL;
+ }
+ }
+ (*keys)[nkeys++] = NULL;
+
+ return 0;
+}
+
+
+void
+_hx509_collector_free(struct hx509_collector *c)
+{
+ int i;
+
+ if (c->unenvelop_certs)
+ hx509_certs_free(&c->unenvelop_certs);
+ if (c->certs)
+ hx509_certs_free(&c->certs);
+ for (i = 0; i < c->val.len; i++)
+ free_private_key(c->val.data[i]);
+ if (c->val.data)
+ free(c->val.data);
+ free(c);
+}
diff --git a/source4/heimdal/lib/hx509/crmf.asn1 b/source4/heimdal/lib/hx509/crmf.asn1
new file mode 100644
index 0000000000..3d8403c8e8
--- /dev/null
+++ b/source4/heimdal/lib/hx509/crmf.asn1
@@ -0,0 +1,113 @@
+-- $Id$
+PKCS10 DEFINITIONS ::=
+
+BEGIN
+
+IMPORTS
+ Time,
+ GeneralName,
+ SubjectPublicKeyInfo,
+ RelativeDistinguishedName,
+ AttributeTypeAndValue,
+ Extension,
+ AlgorithmIdentifier
+ FROM rfc2459
+ heim_any
+ FROM heim;
+
+CRMFRDNSequence ::= SEQUENCE OF RelativeDistinguishedName
+
+Controls ::= SEQUENCE -- SIZE(1..MAX) -- OF AttributeTypeAndValue
+
+-- XXX IMPLICIT brokenness
+POPOSigningKey ::= SEQUENCE {
+ poposkInput [0] IMPLICIT POPOSigningKeyInput OPTIONAL,
+ algorithmIdentifier AlgorithmIdentifier,
+ signature BIT STRING }
+
+PKMACValue ::= SEQUENCE {
+ algId AlgorithmIdentifier,
+ value BIT STRING
+}
+
+-- XXX IMPLICIT brokenness
+POPOSigningKeyInput ::= SEQUENCE {
+ authInfo CHOICE {
+ sender [0] IMPLICIT GeneralName,
+ publicKeyMAC PKMACValue
+ },
+ publicKey SubjectPublicKeyInfo
+} -- from CertTemplate
+
+
+PBMParameter ::= SEQUENCE {
+ salt OCTET STRING,
+ owf AlgorithmIdentifier,
+ iterationCount INTEGER,
+ mac AlgorithmIdentifier
+}
+
+SubsequentMessage ::= INTEGER {
+ encrCert (0),
+ challengeResp (1)
+}
+
+-- XXX IMPLICIT brokenness
+POPOPrivKey ::= CHOICE {
+ thisMessage [0] BIT STRING, -- Deprecated
+ subsequentMessage [1] IMPLICIT SubsequentMessage,
+ dhMAC [2] BIT STRING, -- Deprecated
+ agreeMAC [3] IMPLICIT PKMACValue,
+ encryptedKey [4] heim_any
+}
+
+-- XXX IMPLICIT brokenness
+ProofOfPossession ::= CHOICE {
+ raVerified [0] NULL,
+ signature [1] POPOSigningKey,
+ keyEncipherment [2] POPOPrivKey,
+ keyAgreement [3] POPOPrivKey
+}
+
+CertTemplate ::= SEQUENCE {
+ version [0] INTEGER OPTIONAL,
+ serialNumber [1] INTEGER OPTIONAL,
+ signingAlg [2] SEQUENCE {
+ algorithm OBJECT IDENTIFIER,
+ parameters heim_any OPTIONAL
+ } -- AlgorithmIdentifier -- OPTIONAL,
+ issuer [3] IMPLICIT CHOICE {
+ rdnSequence CRMFRDNSequence
+ } -- Name -- OPTIONAL,
+ validity [4] SEQUENCE {
+ notBefore [0] Time OPTIONAL,
+ notAfter [1] Time OPTIONAL
+ } -- OptionalValidity -- OPTIONAL,
+ subject [5] IMPLICIT CHOICE {
+ rdnSequence CRMFRDNSequence
+ } -- Name -- OPTIONAL,
+ publicKey [6] IMPLICIT SEQUENCE {
+ algorithm AlgorithmIdentifier,
+ subjectPublicKey BIT STRING OPTIONAL
+ } -- SubjectPublicKeyInfo -- OPTIONAL,
+ issuerUID [7] IMPLICIT BIT STRING OPTIONAL,
+ subjectUID [8] IMPLICIT BIT STRING OPTIONAL,
+ extensions [9] IMPLICIT SEQUENCE OF Extension OPTIONAL
+}
+
+CertRequest ::= SEQUENCE {
+ certReqId INTEGER,
+ certTemplate CertTemplate,
+ controls Controls OPTIONAL
+}
+
+CertReqMsg ::= SEQUENCE {
+ certReq CertRequest,
+ popo ProofOfPossession OPTIONAL,
+ regInfo SEQUENCE OF AttributeTypeAndValue OPTIONAL }
+
+CertReqMessages ::= SEQUENCE OF CertReqMsg
+
+
+END
+
diff --git a/source4/heimdal/lib/hx509/crypto.c b/source4/heimdal/lib/hx509/crypto.c
new file mode 100644
index 0000000000..4a8ec8f756
--- /dev/null
+++ b/source4/heimdal/lib/hx509/crypto.c
@@ -0,0 +1,2706 @@
+/*
+ * Copyright (c) 2004 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hx_locl.h"
+RCSID("$Id$");
+
+struct hx509_crypto;
+
+struct signature_alg;
+
+enum crypto_op_type {
+ COT_SIGN
+};
+
+struct hx509_generate_private_context {
+ const heim_oid *key_oid;
+ int isCA;
+ unsigned long num_bits;
+};
+
+struct hx509_private_key_ops {
+ const char *pemtype;
+ const heim_oid *(*key_oid)(void);
+ int (*get_spki)(hx509_context,
+ const hx509_private_key,
+ SubjectPublicKeyInfo *);
+ int (*export)(hx509_context context,
+ const hx509_private_key,
+ heim_octet_string *);
+ int (*import)(hx509_context,
+ const void *data,
+ size_t len,
+ hx509_private_key private_key);
+ int (*generate_private_key)(hx509_context,
+ struct hx509_generate_private_context *,
+ hx509_private_key);
+ BIGNUM *(*get_internal)(hx509_context, hx509_private_key, const char *);
+ int (*handle_alg)(const hx509_private_key,
+ const AlgorithmIdentifier *,
+ enum crypto_op_type);
+ int (*sign)(hx509_context context,
+ const hx509_private_key,
+ const AlgorithmIdentifier *,
+ const heim_octet_string *,
+ AlgorithmIdentifier *,
+ heim_octet_string *);
+#if 0
+ const AlgorithmIdentifier *(*preferred_sig_alg)
+ (const hx509_private_key,
+ const hx509_peer_info);
+ int (*unwrap)(hx509_context context,
+ const hx509_private_key,
+ const AlgorithmIdentifier *,
+ const heim_octet_string *,
+ heim_octet_string *);
+#endif
+};
+
+struct hx509_private_key {
+ unsigned int ref;
+ const struct signature_alg *md;
+ const heim_oid *signature_alg;
+ union {
+ RSA *rsa;
+ void *keydata;
+ } private_key;
+ /* new crypto layer */
+ hx509_private_key_ops *ops;
+};
+
+/*
+ *
+ */
+
+struct signature_alg {
+ const char *name;
+ const heim_oid *(*sig_oid)(void);
+ const AlgorithmIdentifier *(*sig_alg)(void);
+ const heim_oid *(*key_oid)(void);
+ const heim_oid *(*digest_oid)(void);
+ int flags;
+#define PROVIDE_CONF 1
+#define REQUIRE_SIGNER 2
+
+#define SIG_DIGEST 0x100
+#define SIG_PUBLIC_SIG 0x200
+#define SIG_SECRET 0x400
+
+#define RA_RSA_USES_DIGEST_INFO 0x1000000
+
+
+ int (*verify_signature)(hx509_context context,
+ const struct signature_alg *,
+ const Certificate *,
+ const AlgorithmIdentifier *,
+ const heim_octet_string *,
+ const heim_octet_string *);
+ int (*create_signature)(hx509_context,
+ const struct signature_alg *,
+ const hx509_private_key,
+ const AlgorithmIdentifier *,
+ const heim_octet_string *,
+ AlgorithmIdentifier *,
+ heim_octet_string *);
+};
+
+/*
+ *
+ */
+
+static BIGNUM *
+heim_int2BN(const heim_integer *i)
+{
+ BIGNUM *bn;
+
+ bn = BN_bin2bn(i->data, i->length, NULL);
+ BN_set_negative(bn, i->negative);
+ return bn;
+}
+
+/*
+ *
+ */
+
+static int
+set_digest_alg(DigestAlgorithmIdentifier *id,
+ const heim_oid *oid,
+ const void *param, size_t length)
+{
+ int ret;
+ if (param) {
+ id->parameters = malloc(sizeof(*id->parameters));
+ if (id->parameters == NULL)
+ return ENOMEM;
+ id->parameters->data = malloc(length);
+ if (id->parameters->data == NULL) {
+ free(id->parameters);
+ id->parameters = NULL;
+ return ENOMEM;
+ }
+ memcpy(id->parameters->data, param, length);
+ id->parameters->length = length;
+ } else
+ id->parameters = NULL;
+ ret = der_copy_oid(oid, &id->algorithm);
+ if (ret) {
+ if (id->parameters) {
+ free(id->parameters->data);
+ free(id->parameters);
+ id->parameters = NULL;
+ }
+ return ret;
+ }
+ return 0;
+}
+
+/*
+ *
+ */
+
+static int
+rsa_verify_signature(hx509_context context,
+ const struct signature_alg *sig_alg,
+ const Certificate *signer,
+ const AlgorithmIdentifier *alg,
+ const heim_octet_string *data,
+ const heim_octet_string *sig)
+{
+ const SubjectPublicKeyInfo *spi;
+ DigestInfo di;
+ unsigned char *to;
+ int tosize, retsize;
+ int ret;
+ RSA *rsa;
+ RSAPublicKey pk;
+ size_t size;
+
+ memset(&di, 0, sizeof(di));
+
+ spi = &signer->tbsCertificate.subjectPublicKeyInfo;
+
+ rsa = RSA_new();
+ if (rsa == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+ ret = decode_RSAPublicKey(spi->subjectPublicKey.data,
+ spi->subjectPublicKey.length / 8,
+ &pk, &size);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Failed to decode RSAPublicKey");
+ goto out;
+ }
+
+ rsa->n = heim_int2BN(&pk.modulus);
+ rsa->e = heim_int2BN(&pk.publicExponent);
+
+ free_RSAPublicKey(&pk);
+
+ if (rsa->n == NULL || rsa->e == NULL) {
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret, "out of memory");
+ goto out;
+ }
+
+ tosize = RSA_size(rsa);
+ to = malloc(tosize);
+ if (to == NULL) {
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret, "out of memory");
+ goto out;
+ }
+
+ retsize = RSA_public_decrypt(sig->length, (unsigned char *)sig->data,
+ to, rsa, RSA_PKCS1_PADDING);
+ if (retsize <= 0) {
+ ret = HX509_CRYPTO_SIG_INVALID_FORMAT;
+ hx509_set_error_string(context, 0, ret,
+ "RSA public decrypt failed: %d", retsize);
+ free(to);
+ goto out;
+ }
+ if (retsize > tosize)
+ _hx509_abort("internal rsa decryption failure: ret > tosize");
+
+ if (sig_alg->flags & RA_RSA_USES_DIGEST_INFO) {
+
+ ret = decode_DigestInfo(to, retsize, &di, &size);
+ free(to);
+ if (ret) {
+ goto out;
+ }
+
+ /* Check for extra data inside the sigature */
+ if (size != retsize) {
+ ret = HX509_CRYPTO_SIG_INVALID_FORMAT;
+ hx509_set_error_string(context, 0, ret, "size from decryption mismatch");
+ goto out;
+ }
+
+ if (sig_alg->digest_oid &&
+ der_heim_oid_cmp(&di.digestAlgorithm.algorithm,
+ (*sig_alg->digest_oid)()) != 0)
+ {
+ ret = HX509_CRYPTO_OID_MISMATCH;
+ hx509_set_error_string(context, 0, ret, "object identifier in RSA sig mismatch");
+ goto out;
+ }
+
+ /* verify that the parameters are NULL or the NULL-type */
+ if (di.digestAlgorithm.parameters != NULL &&
+ (di.digestAlgorithm.parameters->length != 2 ||
+ memcmp(di.digestAlgorithm.parameters->data, "\x05\x00", 2) != 0))
+ {
+ ret = HX509_CRYPTO_SIG_INVALID_FORMAT;
+ hx509_set_error_string(context, 0, ret, "Extra parameters inside RSA signature");
+ goto out;
+ }
+
+ ret = _hx509_verify_signature(context,
+ NULL,
+ &di.digestAlgorithm,
+ data,
+ &di.digest);
+ } else {
+ if (retsize != data->length ||
+ memcmp(to, data->data, retsize) != 0)
+ {
+ ret = HX509_CRYPTO_SIG_INVALID_FORMAT;
+ hx509_set_error_string(context, 0, ret, "RSA Signature incorrect");
+ goto out;
+ }
+ free(to);
+ }
+
+ out:
+ free_DigestInfo(&di);
+ RSA_free(rsa);
+ return ret;
+}
+
+static int
+rsa_create_signature(hx509_context context,
+ const struct signature_alg *sig_alg,
+ const hx509_private_key signer,
+ const AlgorithmIdentifier *alg,
+ const heim_octet_string *data,
+ AlgorithmIdentifier *signatureAlgorithm,
+ heim_octet_string *sig)
+{
+ const AlgorithmIdentifier *digest_alg;
+ heim_octet_string indata;
+ const heim_oid *sig_oid;
+ size_t size;
+ int ret;
+
+ if (alg)
+ sig_oid = &alg->algorithm;
+ else
+ sig_oid = signer->signature_alg;
+
+ if (der_heim_oid_cmp(sig_oid, oid_id_pkcs1_sha256WithRSAEncryption()) == 0) {
+ digest_alg = hx509_signature_sha256();
+ } else if (der_heim_oid_cmp(sig_oid, oid_id_pkcs1_sha1WithRSAEncryption()) == 0) {
+ digest_alg = hx509_signature_sha1();
+ } else if (der_heim_oid_cmp(sig_oid, oid_id_pkcs1_md5WithRSAEncryption()) == 0) {
+ digest_alg = hx509_signature_md5();
+ } else if (der_heim_oid_cmp(sig_oid, oid_id_pkcs1_md5WithRSAEncryption()) == 0) {
+ digest_alg = hx509_signature_md5();
+ } else if (der_heim_oid_cmp(sig_oid, oid_id_dsa_with_sha1()) == 0) {
+ digest_alg = hx509_signature_sha1();
+ } else if (der_heim_oid_cmp(sig_oid, oid_id_pkcs1_rsaEncryption()) == 0) {
+ digest_alg = hx509_signature_sha1();
+ } else if (der_heim_oid_cmp(sig_oid, oid_id_heim_rsa_pkcs1_x509()) == 0) {
+ digest_alg = NULL;
+ } else
+ return HX509_ALG_NOT_SUPP;
+
+ if (signatureAlgorithm) {
+ ret = set_digest_alg(signatureAlgorithm, sig_oid, "\x05\x00", 2);
+ if (ret) {
+ hx509_clear_error_string(context);
+ return ret;
+ }
+ }
+
+ if (digest_alg) {
+ DigestInfo di;
+ memset(&di, 0, sizeof(di));
+
+ ret = _hx509_create_signature(context,
+ NULL,
+ digest_alg,
+ data,
+ &di.digestAlgorithm,
+ &di.digest);
+ if (ret)
+ return ret;
+ ASN1_MALLOC_ENCODE(DigestInfo,
+ indata.data,
+ indata.length,
+ &di,
+ &size,
+ ret);
+ free_DigestInfo(&di);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "out of memory");
+ return ret;
+ }
+ if (indata.length != size)
+ _hx509_abort("internal ASN.1 encoder error");
+ } else {
+ indata = *data;
+ }
+
+ sig->length = RSA_size(signer->private_key.rsa);
+ sig->data = malloc(sig->length);
+ if (sig->data == NULL) {
+ der_free_octet_string(&indata);
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+
+ ret = RSA_private_encrypt(indata.length, indata.data,
+ sig->data,
+ signer->private_key.rsa,
+ RSA_PKCS1_PADDING);
+ if (indata.data != data->data)
+ der_free_octet_string(&indata);
+ if (ret <= 0) {
+ ret = HX509_CMS_FAILED_CREATE_SIGATURE;
+ hx509_set_error_string(context, 0, ret,
+ "RSA private decrypt failed: %d", ret);
+ return ret;
+ }
+ if (ret > sig->length)
+ _hx509_abort("RSA signature prelen longer the output len");
+
+ sig->length = ret;
+
+ return 0;
+}
+
+static int
+rsa_private_key_import(hx509_context context,
+ const void *data,
+ size_t len,
+ hx509_private_key private_key)
+{
+ const unsigned char *p = data;
+
+ private_key->private_key.rsa =
+ d2i_RSAPrivateKey(NULL, &p, len);
+ if (private_key->private_key.rsa == NULL) {
+ hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,
+ "Failed to parse RSA key");
+ return HX509_PARSING_KEY_FAILED;
+ }
+ private_key->signature_alg = oid_id_pkcs1_sha1WithRSAEncryption();
+
+ return 0;
+}
+
+static int
+rsa_private_key2SPKI(hx509_context context,
+ hx509_private_key private_key,
+ SubjectPublicKeyInfo *spki)
+{
+ int len, ret;
+
+ memset(spki, 0, sizeof(*spki));
+
+ len = i2d_RSAPublicKey(private_key->private_key.rsa, NULL);
+
+ spki->subjectPublicKey.data = malloc(len);
+ if (spki->subjectPublicKey.data == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "malloc - out of memory");
+ return ENOMEM;
+ }
+ spki->subjectPublicKey.length = len * 8;
+
+ ret = set_digest_alg(&spki->algorithm,oid_id_pkcs1_rsaEncryption(),
+ "\x05\x00", 2);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "malloc - out of memory");
+ free(spki->subjectPublicKey.data);
+ spki->subjectPublicKey.data = NULL;
+ spki->subjectPublicKey.length = 0;
+ return ret;
+ }
+
+ {
+ unsigned char *pp = spki->subjectPublicKey.data;
+ i2d_RSAPublicKey(private_key->private_key.rsa, &pp);
+ }
+
+ return 0;
+}
+
+static int
+rsa_generate_private_key(hx509_context context,
+ struct hx509_generate_private_context *ctx,
+ hx509_private_key private_key)
+{
+ BIGNUM *e;
+ int ret;
+ unsigned long bits;
+
+ static const int default_rsa_e = 65537;
+ static const int default_rsa_bits = 1024;
+
+ private_key->private_key.rsa = RSA_new();
+ if (private_key->private_key.rsa == NULL) {
+ hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,
+ "Failed to generate RSA key");
+ return HX509_PARSING_KEY_FAILED;
+ }
+
+ e = BN_new();
+ BN_set_word(e, default_rsa_e);
+
+ bits = default_rsa_bits;
+
+ if (ctx->num_bits)
+ bits = ctx->num_bits;
+ else if (ctx->isCA)
+ bits *= 2;
+
+ ret = RSA_generate_key_ex(private_key->private_key.rsa, bits, e, NULL);
+ BN_free(e);
+ if (ret != 1) {
+ hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,
+ "Failed to generate RSA key");
+ return HX509_PARSING_KEY_FAILED;
+ }
+ private_key->signature_alg = oid_id_pkcs1_sha1WithRSAEncryption();
+
+ return 0;
+}
+
+static int
+rsa_private_key_export(hx509_context context,
+ const hx509_private_key key,
+ heim_octet_string *data)
+{
+ int ret;
+
+ data->data = NULL;
+ data->length = 0;
+
+ ret = i2d_RSAPrivateKey(key->private_key.rsa, NULL);
+ if (ret <= 0) {
+ ret = EINVAL;
+ hx509_set_error_string(context, 0, ret,
+ "Private key is not exportable");
+ return ret;
+ }
+
+ data->data = malloc(ret);
+ if (data->data == NULL) {
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret, "malloc out of memory");
+ return ret;
+ }
+ data->length = ret;
+
+ {
+ unsigned char *p = data->data;
+ i2d_RSAPrivateKey(key->private_key.rsa, &p);
+ }
+
+ return 0;
+}
+
+static BIGNUM *
+rsa_get_internal(hx509_context context, hx509_private_key key, const char *type)
+{
+ if (strcasecmp(type, "rsa-modulus") == 0) {
+ return BN_dup(key->private_key.rsa->n);
+ } else if (strcasecmp(type, "rsa-exponent") == 0) {
+ return BN_dup(key->private_key.rsa->e);
+ } else
+ return NULL;
+}
+
+
+
+static hx509_private_key_ops rsa_private_key_ops = {
+ "RSA PRIVATE KEY",
+ oid_id_pkcs1_rsaEncryption,
+ rsa_private_key2SPKI,
+ rsa_private_key_export,
+ rsa_private_key_import,
+ rsa_generate_private_key,
+ rsa_get_internal
+};
+
+
+/*
+ *
+ */
+
+static int
+dsa_verify_signature(hx509_context context,
+ const struct signature_alg *sig_alg,
+ const Certificate *signer,
+ const AlgorithmIdentifier *alg,
+ const heim_octet_string *data,
+ const heim_octet_string *sig)
+{
+ const SubjectPublicKeyInfo *spi;
+ DSAPublicKey pk;
+ DSAParams param;
+ size_t size;
+ DSA *dsa;
+ int ret;
+
+ spi = &signer->tbsCertificate.subjectPublicKeyInfo;
+
+ dsa = DSA_new();
+ if (dsa == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+
+ ret = decode_DSAPublicKey(spi->subjectPublicKey.data,
+ spi->subjectPublicKey.length / 8,
+ &pk, &size);
+ if (ret)
+ goto out;
+
+ dsa->pub_key = heim_int2BN(&pk);
+
+ free_DSAPublicKey(&pk);
+
+ if (dsa->pub_key == NULL) {
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret, "out of memory");
+ goto out;
+ }
+
+ if (spi->algorithm.parameters == NULL) {
+ ret = HX509_CRYPTO_SIG_INVALID_FORMAT;
+ hx509_set_error_string(context, 0, ret, "DSA parameters missing");
+ goto out;
+ }
+
+ ret = decode_DSAParams(spi->algorithm.parameters->data,
+ spi->algorithm.parameters->length,
+ &param,
+ &size);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "DSA parameters failed to decode");
+ goto out;
+ }
+
+ dsa->p = heim_int2BN(&param.p);
+ dsa->q = heim_int2BN(&param.q);
+ dsa->g = heim_int2BN(&param.g);
+
+ free_DSAParams(&param);
+
+ if (dsa->p == NULL || dsa->q == NULL || dsa->g == NULL) {
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret, "out of memory");
+ goto out;
+ }
+
+ ret = DSA_verify(-1, data->data, data->length,
+ (unsigned char*)sig->data, sig->length,
+ dsa);
+ if (ret == 1)
+ ret = 0;
+ else if (ret == 0 || ret == -1) {
+ ret = HX509_CRYPTO_BAD_SIGNATURE;
+ hx509_set_error_string(context, 0, ret, "BAD DSA sigature");
+ } else {
+ ret = HX509_CRYPTO_SIG_INVALID_FORMAT;
+ hx509_set_error_string(context, 0, ret, "Invalid format of DSA sigature");
+ }
+
+ out:
+ DSA_free(dsa);
+
+ return ret;
+}
+
+#if 0
+static int
+dsa_parse_private_key(hx509_context context,
+ const void *data,
+ size_t len,
+ hx509_private_key private_key)
+{
+ const unsigned char *p = data;
+
+ private_key->private_key.dsa =
+ d2i_DSAPrivateKey(NULL, &p, len);
+ if (private_key->private_key.dsa == NULL)
+ return EINVAL;
+ private_key->signature_alg = oid_id_dsa_with_sha1();
+
+ return 0;
+/* else */
+ hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,
+ "No support to parse DSA keys");
+ return HX509_PARSING_KEY_FAILED;
+}
+#endif
+
+
+static int
+sha1_verify_signature(hx509_context context,
+ const struct signature_alg *sig_alg,
+ const Certificate *signer,
+ const AlgorithmIdentifier *alg,
+ const heim_octet_string *data,
+ const heim_octet_string *sig)
+{
+ unsigned char digest[SHA_DIGEST_LENGTH];
+ SHA_CTX m;
+
+ if (sig->length != SHA_DIGEST_LENGTH) {
+ hx509_set_error_string(context, 0, HX509_CRYPTO_SIG_INVALID_FORMAT,
+ "SHA1 sigature have wrong length");
+ return HX509_CRYPTO_SIG_INVALID_FORMAT;
+ }
+
+ SHA1_Init(&m);
+ SHA1_Update(&m, data->data, data->length);
+ SHA1_Final (digest, &m);
+
+ if (memcmp(digest, sig->data, SHA_DIGEST_LENGTH) != 0) {
+ hx509_set_error_string(context, 0, HX509_CRYPTO_BAD_SIGNATURE,
+ "Bad SHA1 sigature");
+ return HX509_CRYPTO_BAD_SIGNATURE;
+ }
+
+ return 0;
+}
+
+static int
+sha256_create_signature(hx509_context context,
+ const struct signature_alg *sig_alg,
+ const hx509_private_key signer,
+ const AlgorithmIdentifier *alg,
+ const heim_octet_string *data,
+ AlgorithmIdentifier *signatureAlgorithm,
+ heim_octet_string *sig)
+{
+ SHA256_CTX m;
+
+ memset(sig, 0, sizeof(*sig));
+
+ if (signatureAlgorithm) {
+ int ret;
+ ret = set_digest_alg(signatureAlgorithm, (*sig_alg->sig_oid)(),
+ "\x05\x00", 2);
+ if (ret)
+ return ret;
+ }
+
+
+ sig->data = malloc(SHA256_DIGEST_LENGTH);
+ if (sig->data == NULL) {
+ sig->length = 0;
+ return ENOMEM;
+ }
+ sig->length = SHA256_DIGEST_LENGTH;
+
+ SHA256_Init(&m);
+ SHA256_Update(&m, data->data, data->length);
+ SHA256_Final (sig->data, &m);
+
+ return 0;
+}
+
+static int
+sha256_verify_signature(hx509_context context,
+ const struct signature_alg *sig_alg,
+ const Certificate *signer,
+ const AlgorithmIdentifier *alg,
+ const heim_octet_string *data,
+ const heim_octet_string *sig)
+{
+ unsigned char digest[SHA256_DIGEST_LENGTH];
+ SHA256_CTX m;
+
+ if (sig->length != SHA256_DIGEST_LENGTH) {
+ hx509_set_error_string(context, 0, HX509_CRYPTO_SIG_INVALID_FORMAT,
+ "SHA256 sigature have wrong length");
+ return HX509_CRYPTO_SIG_INVALID_FORMAT;
+ }
+
+ SHA256_Init(&m);
+ SHA256_Update(&m, data->data, data->length);
+ SHA256_Final (digest, &m);
+
+ if (memcmp(digest, sig->data, SHA256_DIGEST_LENGTH) != 0) {
+ hx509_set_error_string(context, 0, HX509_CRYPTO_BAD_SIGNATURE,
+ "Bad SHA256 sigature");
+ return HX509_CRYPTO_BAD_SIGNATURE;
+ }
+
+ return 0;
+}
+
+static int
+sha1_create_signature(hx509_context context,
+ const struct signature_alg *sig_alg,
+ const hx509_private_key signer,
+ const AlgorithmIdentifier *alg,
+ const heim_octet_string *data,
+ AlgorithmIdentifier *signatureAlgorithm,
+ heim_octet_string *sig)
+{
+ SHA_CTX m;
+
+ memset(sig, 0, sizeof(*sig));
+
+ if (signatureAlgorithm) {
+ int ret;
+ ret = set_digest_alg(signatureAlgorithm, (*sig_alg->sig_oid)(),
+ "\x05\x00", 2);
+ if (ret)
+ return ret;
+ }
+
+
+ sig->data = malloc(SHA_DIGEST_LENGTH);
+ if (sig->data == NULL) {
+ sig->length = 0;
+ return ENOMEM;
+ }
+ sig->length = SHA_DIGEST_LENGTH;
+
+ SHA1_Init(&m);
+ SHA1_Update(&m, data->data, data->length);
+ SHA1_Final (sig->data, &m);
+
+ return 0;
+}
+
+static int
+md5_verify_signature(hx509_context context,
+ const struct signature_alg *sig_alg,
+ const Certificate *signer,
+ const AlgorithmIdentifier *alg,
+ const heim_octet_string *data,
+ const heim_octet_string *sig)
+{
+ unsigned char digest[MD5_DIGEST_LENGTH];
+ MD5_CTX m;
+
+ if (sig->length != MD5_DIGEST_LENGTH) {
+ hx509_set_error_string(context, 0, HX509_CRYPTO_SIG_INVALID_FORMAT,
+ "MD5 sigature have wrong length");
+ return HX509_CRYPTO_SIG_INVALID_FORMAT;
+ }
+
+ MD5_Init(&m);
+ MD5_Update(&m, data->data, data->length);
+ MD5_Final (digest, &m);
+
+ if (memcmp(digest, sig->data, MD5_DIGEST_LENGTH) != 0) {
+ hx509_set_error_string(context, 0, HX509_CRYPTO_BAD_SIGNATURE,
+ "Bad MD5 sigature");
+ return HX509_CRYPTO_BAD_SIGNATURE;
+ }
+
+ return 0;
+}
+
+static int
+md2_verify_signature(hx509_context context,
+ const struct signature_alg *sig_alg,
+ const Certificate *signer,
+ const AlgorithmIdentifier *alg,
+ const heim_octet_string *data,
+ const heim_octet_string *sig)
+{
+ unsigned char digest[MD2_DIGEST_LENGTH];
+ MD2_CTX m;
+
+ if (sig->length != MD2_DIGEST_LENGTH) {
+ hx509_set_error_string(context, 0, HX509_CRYPTO_SIG_INVALID_FORMAT,
+ "MD2 sigature have wrong length");
+ return HX509_CRYPTO_SIG_INVALID_FORMAT;
+ }
+
+ MD2_Init(&m);
+ MD2_Update(&m, data->data, data->length);
+ MD2_Final (digest, &m);
+
+ if (memcmp(digest, sig->data, MD2_DIGEST_LENGTH) != 0) {
+ hx509_set_error_string(context, 0, HX509_CRYPTO_BAD_SIGNATURE,
+ "Bad MD2 sigature");
+ return HX509_CRYPTO_BAD_SIGNATURE;
+ }
+
+ return 0;
+}
+
+static const struct signature_alg heim_rsa_pkcs1_x509 = {
+ "rsa-pkcs1-x509",
+ oid_id_heim_rsa_pkcs1_x509,
+ hx509_signature_rsa_pkcs1_x509,
+ oid_id_pkcs1_rsaEncryption,
+ NULL,
+ PROVIDE_CONF|REQUIRE_SIGNER|SIG_PUBLIC_SIG,
+ rsa_verify_signature,
+ rsa_create_signature
+};
+
+static const struct signature_alg pkcs1_rsa_sha1_alg = {
+ "rsa",
+ oid_id_pkcs1_rsaEncryption,
+ hx509_signature_rsa_with_sha1,
+ oid_id_pkcs1_rsaEncryption,
+ NULL,
+ PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|SIG_PUBLIC_SIG,
+ rsa_verify_signature,
+ rsa_create_signature
+};
+
+static const struct signature_alg rsa_with_sha256_alg = {
+ "rsa-with-sha256",
+ oid_id_pkcs1_sha256WithRSAEncryption,
+ hx509_signature_rsa_with_sha256,
+ oid_id_pkcs1_rsaEncryption,
+ oid_id_sha256,
+ PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|SIG_PUBLIC_SIG,
+ rsa_verify_signature,
+ rsa_create_signature
+};
+
+static const struct signature_alg rsa_with_sha1_alg = {
+ "rsa-with-sha1",
+ oid_id_pkcs1_sha1WithRSAEncryption,
+ hx509_signature_rsa_with_sha1,
+ oid_id_pkcs1_rsaEncryption,
+ oid_id_secsig_sha_1,
+ PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|SIG_PUBLIC_SIG,
+ rsa_verify_signature,
+ rsa_create_signature
+};
+
+static const struct signature_alg rsa_with_md5_alg = {
+ "rsa-with-md5",
+ oid_id_pkcs1_md5WithRSAEncryption,
+ hx509_signature_rsa_with_md5,
+ oid_id_pkcs1_rsaEncryption,
+ oid_id_rsa_digest_md5,
+ PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|SIG_PUBLIC_SIG,
+ rsa_verify_signature,
+ rsa_create_signature
+};
+
+static const struct signature_alg rsa_with_md2_alg = {
+ "rsa-with-md2",
+ oid_id_pkcs1_md2WithRSAEncryption,
+ hx509_signature_rsa_with_md2,
+ oid_id_pkcs1_rsaEncryption,
+ oid_id_rsa_digest_md2,
+ PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|SIG_PUBLIC_SIG,
+ rsa_verify_signature,
+ rsa_create_signature
+};
+
+static const struct signature_alg dsa_sha1_alg = {
+ "dsa-with-sha1",
+ oid_id_dsa_with_sha1,
+ NULL,
+ oid_id_dsa,
+ oid_id_secsig_sha_1,
+ PROVIDE_CONF|REQUIRE_SIGNER|SIG_PUBLIC_SIG,
+ dsa_verify_signature,
+ /* create_signature */ NULL,
+};
+
+static const struct signature_alg sha256_alg = {
+ "sha-256",
+ oid_id_sha256,
+ hx509_signature_sha256,
+ NULL,
+ NULL,
+ SIG_DIGEST,
+ sha256_verify_signature,
+ sha256_create_signature
+};
+
+static const struct signature_alg sha1_alg = {
+ "sha1",
+ oid_id_secsig_sha_1,
+ hx509_signature_sha1,
+ NULL,
+ NULL,
+ SIG_DIGEST,
+ sha1_verify_signature,
+ sha1_create_signature
+};
+
+static const struct signature_alg md5_alg = {
+ "rsa-md5",
+ oid_id_rsa_digest_md5,
+ hx509_signature_md5,
+ NULL,
+ NULL,
+ SIG_DIGEST,
+ md5_verify_signature
+};
+
+static const struct signature_alg md2_alg = {
+ "rsa-md2",
+ oid_id_rsa_digest_md2,
+ hx509_signature_md2,
+ NULL,
+ NULL,
+ SIG_DIGEST,
+ md2_verify_signature
+};
+
+/*
+ * Order matter in this structure, "best" first for each "key
+ * compatible" type (type is RSA, DSA, none, etc)
+ */
+
+static const struct signature_alg *sig_algs[] = {
+ &rsa_with_sha256_alg,
+ &rsa_with_sha1_alg,
+ &pkcs1_rsa_sha1_alg,
+ &rsa_with_md5_alg,
+ &rsa_with_md2_alg,
+ &heim_rsa_pkcs1_x509,
+ &dsa_sha1_alg,
+ &sha256_alg,
+ &sha1_alg,
+ &md5_alg,
+ &md2_alg,
+ NULL
+};
+
+static const struct signature_alg *
+find_sig_alg(const heim_oid *oid)
+{
+ int i;
+ for (i = 0; sig_algs[i]; i++)
+ if (der_heim_oid_cmp((*sig_algs[i]->sig_oid)(), oid) == 0)
+ return sig_algs[i];
+ return NULL;
+}
+
+/*
+ *
+ */
+
+static struct hx509_private_key_ops *private_algs[] = {
+ &rsa_private_key_ops,
+ NULL
+};
+
+static hx509_private_key_ops *
+find_private_alg(const heim_oid *oid)
+{
+ int i;
+ for (i = 0; private_algs[i]; i++) {
+ if (private_algs[i]->key_oid == NULL)
+ continue;
+ if (der_heim_oid_cmp((*private_algs[i]->key_oid)(), oid) == 0)
+ return private_algs[i];
+ }
+ return NULL;
+}
+
+
+int
+_hx509_verify_signature(hx509_context context,
+ const Certificate *signer,
+ const AlgorithmIdentifier *alg,
+ const heim_octet_string *data,
+ const heim_octet_string *sig)
+{
+ const struct signature_alg *md;
+
+ md = find_sig_alg(&alg->algorithm);
+ if (md == NULL) {
+ hx509_clear_error_string(context);
+ return HX509_SIG_ALG_NO_SUPPORTED;
+ }
+ if (signer && (md->flags & PROVIDE_CONF) == 0) {
+ hx509_clear_error_string(context);
+ return HX509_CRYPTO_SIG_NO_CONF;
+ }
+ if (signer == NULL && (md->flags & REQUIRE_SIGNER)) {
+ hx509_clear_error_string(context);
+ return HX509_CRYPTO_SIGNATURE_WITHOUT_SIGNER;
+ }
+ if (md->key_oid && signer) {
+ const SubjectPublicKeyInfo *spi;
+ spi = &signer->tbsCertificate.subjectPublicKeyInfo;
+
+ if (der_heim_oid_cmp(&spi->algorithm.algorithm, (*md->key_oid)()) != 0) {
+ hx509_clear_error_string(context);
+ return HX509_SIG_ALG_DONT_MATCH_KEY_ALG;
+ }
+ }
+ return (*md->verify_signature)(context, md, signer, alg, data, sig);
+}
+
+int
+_hx509_verify_signature_bitstring(hx509_context context,
+ const Certificate *signer,
+ const AlgorithmIdentifier *alg,
+ const heim_octet_string *data,
+ const heim_bit_string *sig)
+{
+ heim_octet_string os;
+
+ if (sig->length & 7) {
+ hx509_set_error_string(context, 0, HX509_CRYPTO_SIG_INVALID_FORMAT,
+ "signature not multiple of 8 bits");
+ return HX509_CRYPTO_SIG_INVALID_FORMAT;
+ }
+
+ os.data = sig->data;
+ os.length = sig->length / 8;
+
+ return _hx509_verify_signature(context, signer, alg, data, &os);
+}
+
+int
+_hx509_create_signature(hx509_context context,
+ const hx509_private_key signer,
+ const AlgorithmIdentifier *alg,
+ const heim_octet_string *data,
+ AlgorithmIdentifier *signatureAlgorithm,
+ heim_octet_string *sig)
+{
+ const struct signature_alg *md;
+
+ if (signer && signer->ops && signer->ops->handle_alg &&
+ (*signer->ops->handle_alg)(signer, alg, COT_SIGN))
+ {
+ return (*signer->ops->sign)(context, signer, alg, data,
+ signatureAlgorithm, sig);
+ }
+
+ md = find_sig_alg(&alg->algorithm);
+ if (md == NULL) {
+ hx509_set_error_string(context, 0, HX509_SIG_ALG_NO_SUPPORTED,
+ "algorithm no supported");
+ return HX509_SIG_ALG_NO_SUPPORTED;
+ }
+
+ if (signer && (md->flags & PROVIDE_CONF) == 0) {
+ hx509_set_error_string(context, 0, HX509_SIG_ALG_NO_SUPPORTED,
+ "algorithm provides no conf");
+ return HX509_CRYPTO_SIG_NO_CONF;
+ }
+
+ return (*md->create_signature)(context, md, signer, alg, data,
+ signatureAlgorithm, sig);
+}
+
+int
+_hx509_create_signature_bitstring(hx509_context context,
+ const hx509_private_key signer,
+ const AlgorithmIdentifier *alg,
+ const heim_octet_string *data,
+ AlgorithmIdentifier *signatureAlgorithm,
+ heim_bit_string *sig)
+{
+ heim_octet_string os;
+ int ret;
+
+ ret = _hx509_create_signature(context, signer, alg,
+ data, signatureAlgorithm, &os);
+ if (ret)
+ return ret;
+ sig->data = os.data;
+ sig->length = os.length * 8;
+ return 0;
+}
+
+int
+_hx509_public_encrypt(hx509_context context,
+ const heim_octet_string *cleartext,
+ const Certificate *cert,
+ heim_oid *encryption_oid,
+ heim_octet_string *ciphertext)
+{
+ const SubjectPublicKeyInfo *spi;
+ unsigned char *to;
+ int tosize;
+ int ret;
+ RSA *rsa;
+ RSAPublicKey pk;
+ size_t size;
+
+ ciphertext->data = NULL;
+ ciphertext->length = 0;
+
+ spi = &cert->tbsCertificate.subjectPublicKeyInfo;
+
+ rsa = RSA_new();
+ if (rsa == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+
+ ret = decode_RSAPublicKey(spi->subjectPublicKey.data,
+ spi->subjectPublicKey.length / 8,
+ &pk, &size);
+ if (ret) {
+ RSA_free(rsa);
+ hx509_set_error_string(context, 0, ret, "RSAPublicKey decode failure");
+ return ret;
+ }
+ rsa->n = heim_int2BN(&pk.modulus);
+ rsa->e = heim_int2BN(&pk.publicExponent);
+
+ free_RSAPublicKey(&pk);
+
+ if (rsa->n == NULL || rsa->e == NULL) {
+ RSA_free(rsa);
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+
+ tosize = RSA_size(rsa);
+ to = malloc(tosize);
+ if (to == NULL) {
+ RSA_free(rsa);
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+
+ ret = RSA_public_encrypt(cleartext->length,
+ (unsigned char *)cleartext->data,
+ to, rsa, RSA_PKCS1_PADDING);
+ RSA_free(rsa);
+ if (ret <= 0) {
+ free(to);
+ hx509_set_error_string(context, 0, HX509_CRYPTO_RSA_PUBLIC_ENCRYPT,
+ "RSA public encrypt failed with %d", ret);
+ return HX509_CRYPTO_RSA_PUBLIC_ENCRYPT;
+ }
+ if (ret > tosize)
+ _hx509_abort("internal rsa decryption failure: ret > tosize");
+
+ ciphertext->length = ret;
+ ciphertext->data = to;
+
+ ret = der_copy_oid(oid_id_pkcs1_rsaEncryption(), encryption_oid);
+ if (ret) {
+ der_free_octet_string(ciphertext);
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+
+ return 0;
+}
+
+int
+_hx509_private_key_private_decrypt(hx509_context context,
+ const heim_octet_string *ciphertext,
+ const heim_oid *encryption_oid,
+ hx509_private_key p,
+ heim_octet_string *cleartext)
+{
+ int ret;
+
+ cleartext->data = NULL;
+ cleartext->length = 0;
+
+ if (p->private_key.rsa == NULL) {
+ hx509_set_error_string(context, 0, HX509_PRIVATE_KEY_MISSING,
+ "Private RSA key missing");
+ return HX509_PRIVATE_KEY_MISSING;
+ }
+
+ cleartext->length = RSA_size(p->private_key.rsa);
+ cleartext->data = malloc(cleartext->length);
+ if (cleartext->data == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+ ret = RSA_private_decrypt(ciphertext->length, ciphertext->data,
+ cleartext->data,
+ p->private_key.rsa,
+ RSA_PKCS1_PADDING);
+ if (ret <= 0) {
+ der_free_octet_string(cleartext);
+ hx509_set_error_string(context, 0, HX509_CRYPTO_RSA_PRIVATE_DECRYPT,
+ "Failed to decrypt using private key: %d", ret);
+ return HX509_CRYPTO_RSA_PRIVATE_DECRYPT;
+ }
+ if (cleartext->length < ret)
+ _hx509_abort("internal rsa decryption failure: ret > tosize");
+
+ cleartext->length = ret;
+
+ return 0;
+}
+
+
+int
+_hx509_parse_private_key(hx509_context context,
+ const heim_oid *key_oid,
+ const void *data,
+ size_t len,
+ hx509_private_key *private_key)
+{
+ struct hx509_private_key_ops *ops;
+ int ret;
+
+ *private_key = NULL;
+
+ ops = find_private_alg(key_oid);
+ if (ops == NULL) {
+ hx509_clear_error_string(context);
+ return HX509_SIG_ALG_NO_SUPPORTED;
+ }
+
+ ret = _hx509_private_key_init(private_key, ops, NULL);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "out of memory");
+ return ret;
+ }
+
+ ret = (*ops->import)(context, data, len, *private_key);
+ if (ret)
+ _hx509_private_key_free(private_key);
+
+ return ret;
+}
+
+/*
+ *
+ */
+
+int
+_hx509_private_key2SPKI(hx509_context context,
+ hx509_private_key private_key,
+ SubjectPublicKeyInfo *spki)
+{
+ const struct hx509_private_key_ops *ops = private_key->ops;
+ if (ops == NULL || ops->get_spki == NULL) {
+ hx509_set_error_string(context, 0, HX509_UNIMPLEMENTED_OPERATION,
+ "Private key have no key2SPKI function");
+ return HX509_UNIMPLEMENTED_OPERATION;
+ }
+ return (*ops->get_spki)(context, private_key, spki);
+}
+
+int
+_hx509_generate_private_key_init(hx509_context context,
+ const heim_oid *oid,
+ struct hx509_generate_private_context **ctx)
+{
+ *ctx = NULL;
+
+ if (der_heim_oid_cmp(oid, oid_id_pkcs1_rsaEncryption()) != 0) {
+ hx509_set_error_string(context, 0, EINVAL,
+ "private key not an RSA key");
+ return EINVAL;
+ }
+
+ *ctx = calloc(1, sizeof(**ctx));
+ if (*ctx == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+ (*ctx)->key_oid = oid;
+
+ return 0;
+}
+
+int
+_hx509_generate_private_key_is_ca(hx509_context context,
+ struct hx509_generate_private_context *ctx)
+{
+ ctx->isCA = 1;
+ return 0;
+}
+
+int
+_hx509_generate_private_key_bits(hx509_context context,
+ struct hx509_generate_private_context *ctx,
+ unsigned long bits)
+{
+ ctx->num_bits = bits;
+ return 0;
+}
+
+
+void
+_hx509_generate_private_key_free(struct hx509_generate_private_context **ctx)
+{
+ free(*ctx);
+ *ctx = NULL;
+}
+
+int
+_hx509_generate_private_key(hx509_context context,
+ struct hx509_generate_private_context *ctx,
+ hx509_private_key *private_key)
+{
+ struct hx509_private_key_ops *ops;
+ int ret;
+
+ *private_key = NULL;
+
+ ops = find_private_alg(ctx->key_oid);
+ if (ops == NULL) {
+ hx509_clear_error_string(context);
+ return HX509_SIG_ALG_NO_SUPPORTED;
+ }
+
+ ret = _hx509_private_key_init(private_key, ops, NULL);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "out of memory");
+ return ret;
+ }
+
+ ret = (*ops->generate_private_key)(context, ctx, *private_key);
+ if (ret)
+ _hx509_private_key_free(private_key);
+
+ return ret;
+}
+
+
+/*
+ *
+ */
+
+static const heim_octet_string null_entry_oid = { 2, rk_UNCONST("\x05\x00") };
+
+static const unsigned sha512_oid_tree[] = { 2, 16, 840, 1, 101, 3, 4, 2, 3 };
+const AlgorithmIdentifier _hx509_signature_sha512_data = {
+ { 9, rk_UNCONST(sha512_oid_tree) }, rk_UNCONST(&null_entry_oid)
+};
+
+static const unsigned sha384_oid_tree[] = { 2, 16, 840, 1, 101, 3, 4, 2, 2 };
+const AlgorithmIdentifier _hx509_signature_sha384_data = {
+ { 9, rk_UNCONST(sha384_oid_tree) }, rk_UNCONST(&null_entry_oid)
+};
+
+static const unsigned sha256_oid_tree[] = { 2, 16, 840, 1, 101, 3, 4, 2, 1 };
+const AlgorithmIdentifier _hx509_signature_sha256_data = {
+ { 9, rk_UNCONST(sha256_oid_tree) }, rk_UNCONST(&null_entry_oid)
+};
+
+static const unsigned sha1_oid_tree[] = { 1, 3, 14, 3, 2, 26 };
+const AlgorithmIdentifier _hx509_signature_sha1_data = {
+ { 6, rk_UNCONST(sha1_oid_tree) }, rk_UNCONST(&null_entry_oid)
+};
+
+static const unsigned md5_oid_tree[] = { 1, 2, 840, 113549, 2, 5 };
+const AlgorithmIdentifier _hx509_signature_md5_data = {
+ { 6, rk_UNCONST(md5_oid_tree) }, rk_UNCONST(&null_entry_oid)
+};
+
+static const unsigned md2_oid_tree[] = { 1, 2, 840, 113549, 2, 2 };
+const AlgorithmIdentifier _hx509_signature_md2_data = {
+ { 6, rk_UNCONST(md2_oid_tree) }, rk_UNCONST(&null_entry_oid)
+};
+
+static const unsigned rsa_with_sha512_oid[] ={ 1, 2, 840, 113549, 1, 1, 13 };
+const AlgorithmIdentifier _hx509_signature_rsa_with_sha512_data = {
+ { 7, rk_UNCONST(rsa_with_sha512_oid) }, NULL
+};
+
+static const unsigned rsa_with_sha384_oid[] ={ 1, 2, 840, 113549, 1, 1, 12 };
+const AlgorithmIdentifier _hx509_signature_rsa_with_sha384_data = {
+ { 7, rk_UNCONST(rsa_with_sha384_oid) }, NULL
+};
+
+static const unsigned rsa_with_sha256_oid[] ={ 1, 2, 840, 113549, 1, 1, 11 };
+const AlgorithmIdentifier _hx509_signature_rsa_with_sha256_data = {
+ { 7, rk_UNCONST(rsa_with_sha256_oid) }, NULL
+};
+
+static const unsigned rsa_with_sha1_oid[] ={ 1, 2, 840, 113549, 1, 1, 5 };
+const AlgorithmIdentifier _hx509_signature_rsa_with_sha1_data = {
+ { 7, rk_UNCONST(rsa_with_sha1_oid) }, NULL
+};
+
+static const unsigned rsa_with_md5_oid[] ={ 1, 2, 840, 113549, 1, 1, 4 };
+const AlgorithmIdentifier _hx509_signature_rsa_with_md5_data = {
+ { 7, rk_UNCONST(rsa_with_md5_oid) }, NULL
+};
+
+static const unsigned rsa_with_md2_oid[] ={ 1, 2, 840, 113549, 1, 1, 2 };
+const AlgorithmIdentifier _hx509_signature_rsa_with_md2_data = {
+ { 7, rk_UNCONST(rsa_with_md2_oid) }, NULL
+};
+
+static const unsigned rsa_oid[] ={ 1, 2, 840, 113549, 1, 1, 1 };
+const AlgorithmIdentifier _hx509_signature_rsa_data = {
+ { 7, rk_UNCONST(rsa_oid) }, NULL
+};
+
+static const unsigned rsa_pkcs1_x509_oid[] ={ 1, 2, 752, 43, 16, 1 };
+const AlgorithmIdentifier _hx509_signature_rsa_pkcs1_x509_data = {
+ { 6, rk_UNCONST(rsa_pkcs1_x509_oid) }, NULL
+};
+
+static const unsigned des_rsdi_ede3_cbc_oid[] ={ 1, 2, 840, 113549, 3, 7 };
+const AlgorithmIdentifier _hx509_des_rsdi_ede3_cbc_oid = {
+ { 6, rk_UNCONST(des_rsdi_ede3_cbc_oid) }, NULL
+};
+
+static const unsigned aes128_cbc_oid[] ={ 2, 16, 840, 1, 101, 3, 4, 1, 2 };
+const AlgorithmIdentifier _hx509_crypto_aes128_cbc_data = {
+ { 9, rk_UNCONST(aes128_cbc_oid) }, NULL
+};
+
+static const unsigned aes256_cbc_oid[] ={ 2, 16, 840, 1, 101, 3, 4, 1, 42 };
+const AlgorithmIdentifier _hx509_crypto_aes256_cbc_data = {
+ { 9, rk_UNCONST(aes256_cbc_oid) }, NULL
+};
+
+const AlgorithmIdentifier *
+hx509_signature_sha512(void)
+{ return &_hx509_signature_sha512_data; }
+
+const AlgorithmIdentifier *
+hx509_signature_sha384(void)
+{ return &_hx509_signature_sha384_data; }
+
+const AlgorithmIdentifier *
+hx509_signature_sha256(void)
+{ return &_hx509_signature_sha256_data; }
+
+const AlgorithmIdentifier *
+hx509_signature_sha1(void)
+{ return &_hx509_signature_sha1_data; }
+
+const AlgorithmIdentifier *
+hx509_signature_md5(void)
+{ return &_hx509_signature_md5_data; }
+
+const AlgorithmIdentifier *
+hx509_signature_md2(void)
+{ return &_hx509_signature_md2_data; }
+
+const AlgorithmIdentifier *
+hx509_signature_rsa_with_sha512(void)
+{ return &_hx509_signature_rsa_with_sha512_data; }
+
+const AlgorithmIdentifier *
+hx509_signature_rsa_with_sha384(void)
+{ return &_hx509_signature_rsa_with_sha384_data; }
+
+const AlgorithmIdentifier *
+hx509_signature_rsa_with_sha256(void)
+{ return &_hx509_signature_rsa_with_sha256_data; }
+
+const AlgorithmIdentifier *
+hx509_signature_rsa_with_sha1(void)
+{ return &_hx509_signature_rsa_with_sha1_data; }
+
+const AlgorithmIdentifier *
+hx509_signature_rsa_with_md5(void)
+{ return &_hx509_signature_rsa_with_md5_data; }
+
+const AlgorithmIdentifier *
+hx509_signature_rsa_with_md2(void)
+{ return &_hx509_signature_rsa_with_md2_data; }
+
+const AlgorithmIdentifier *
+hx509_signature_rsa(void)
+{ return &_hx509_signature_rsa_data; }
+
+const AlgorithmIdentifier *
+hx509_signature_rsa_pkcs1_x509(void)
+{ return &_hx509_signature_rsa_pkcs1_x509_data; }
+
+const AlgorithmIdentifier *
+hx509_crypto_des_rsdi_ede3_cbc(void)
+{ return &_hx509_des_rsdi_ede3_cbc_oid; }
+
+const AlgorithmIdentifier *
+hx509_crypto_aes128_cbc(void)
+{ return &_hx509_crypto_aes128_cbc_data; }
+
+const AlgorithmIdentifier *
+hx509_crypto_aes256_cbc(void)
+{ return &_hx509_crypto_aes256_cbc_data; }
+
+/*
+ *
+ */
+
+const AlgorithmIdentifier * _hx509_crypto_default_sig_alg =
+ &_hx509_signature_rsa_with_sha1_data;
+const AlgorithmIdentifier * _hx509_crypto_default_digest_alg =
+ &_hx509_signature_sha1_data;
+const AlgorithmIdentifier * _hx509_crypto_default_secret_alg =
+ &_hx509_crypto_aes128_cbc_data;
+
+/*
+ *
+ */
+
+int
+_hx509_private_key_init(hx509_private_key *key,
+ hx509_private_key_ops *ops,
+ void *keydata)
+{
+ *key = calloc(1, sizeof(**key));
+ if (*key == NULL)
+ return ENOMEM;
+ (*key)->ref = 1;
+ (*key)->ops = ops;
+ (*key)->private_key.keydata = keydata;
+ return 0;
+}
+
+hx509_private_key
+_hx509_private_key_ref(hx509_private_key key)
+{
+ if (key->ref == 0)
+ _hx509_abort("key refcount <= 0 on ref");
+ key->ref++;
+ if (key->ref == UINT_MAX)
+ _hx509_abort("key refcount == UINT_MAX on ref");
+ return key;
+}
+
+const char *
+_hx509_private_pem_name(hx509_private_key key)
+{
+ return key->ops->pemtype;
+}
+
+int
+_hx509_private_key_free(hx509_private_key *key)
+{
+ if (key == NULL || *key == NULL)
+ return 0;
+
+ if ((*key)->ref == 0)
+ _hx509_abort("key refcount == 0 on free");
+ if (--(*key)->ref > 0)
+ return 0;
+
+ if ((*key)->private_key.rsa)
+ RSA_free((*key)->private_key.rsa);
+ (*key)->private_key.rsa = NULL;
+ free(*key);
+ *key = NULL;
+ return 0;
+}
+
+void
+_hx509_private_key_assign_rsa(hx509_private_key key, void *ptr)
+{
+ if (key->private_key.rsa)
+ RSA_free(key->private_key.rsa);
+ key->private_key.rsa = ptr;
+ key->signature_alg = oid_id_pkcs1_sha1WithRSAEncryption();
+ key->md = &pkcs1_rsa_sha1_alg;
+}
+
+int
+_hx509_private_key_oid(hx509_context context,
+ const hx509_private_key key,
+ heim_oid *data)
+{
+ int ret;
+ ret = der_copy_oid((*key->ops->key_oid)(), data);
+ if (ret)
+ hx509_set_error_string(context, 0, ret, "malloc out of memory");
+ return ret;
+}
+
+int
+_hx509_private_key_exportable(hx509_private_key key)
+{
+ if (key->ops->export == NULL)
+ return 0;
+ return 1;
+}
+
+BIGNUM *
+_hx509_private_key_get_internal(hx509_context context,
+ hx509_private_key key,
+ const char *type)
+{
+ if (key->ops->get_internal == NULL)
+ return NULL;
+ return (*key->ops->get_internal)(context, key, type);
+}
+
+int
+_hx509_private_key_export(hx509_context context,
+ const hx509_private_key key,
+ heim_octet_string *data)
+{
+ if (key->ops->export == NULL) {
+ hx509_clear_error_string(context);
+ return HX509_UNIMPLEMENTED_OPERATION;
+ }
+ return (*key->ops->export)(context, key, data);
+}
+
+/*
+ *
+ */
+
+struct hx509cipher {
+ const char *name;
+ const heim_oid *(*oid_func)(void);
+ const AlgorithmIdentifier *(*ai_func)(void);
+ const EVP_CIPHER *(*evp_func)(void);
+ int (*get_params)(hx509_context, const hx509_crypto,
+ const heim_octet_string *, heim_octet_string *);
+ int (*set_params)(hx509_context, const heim_octet_string *,
+ hx509_crypto, heim_octet_string *);
+};
+
+struct hx509_crypto_data {
+ char *name;
+ const struct hx509cipher *cipher;
+ const EVP_CIPHER *c;
+ heim_octet_string key;
+ heim_oid oid;
+ void *param;
+};
+
+/*
+ *
+ */
+
+static const heim_oid *
+oid_private_rc2_40(void)
+{
+ static unsigned oid_data[] = { 127, 1 };
+ static const heim_oid oid = { 2, oid_data };
+
+ return &oid;
+}
+
+
+/*
+ *
+ */
+
+static int
+CMSCBCParam_get(hx509_context context, const hx509_crypto crypto,
+ const heim_octet_string *ivec, heim_octet_string *param)
+{
+ size_t size;
+ int ret;
+
+ assert(crypto->param == NULL);
+ if (ivec == NULL)
+ return 0;
+
+ ASN1_MALLOC_ENCODE(CMSCBCParameter, param->data, param->length,
+ ivec, &size, ret);
+ if (ret == 0 && size != param->length)
+ _hx509_abort("Internal asn1 encoder failure");
+ if (ret)
+ hx509_clear_error_string(context);
+ return ret;
+}
+
+static int
+CMSCBCParam_set(hx509_context context, const heim_octet_string *param,
+ hx509_crypto crypto, heim_octet_string *ivec)
+{
+ int ret;
+ if (ivec == NULL)
+ return 0;
+
+ ret = decode_CMSCBCParameter(param->data, param->length, ivec, NULL);
+ if (ret)
+ hx509_clear_error_string(context);
+
+ return ret;
+}
+
+struct _RC2_params {
+ int maximum_effective_key;
+};
+
+static int
+CMSRC2CBCParam_get(hx509_context context, const hx509_crypto crypto,
+ const heim_octet_string *ivec, heim_octet_string *param)
+{
+ CMSRC2CBCParameter rc2params;
+ const struct _RC2_params *p = crypto->param;
+ int maximum_effective_key = 128;
+ size_t size;
+ int ret;
+
+ memset(&rc2params, 0, sizeof(rc2params));
+
+ if (p)
+ maximum_effective_key = p->maximum_effective_key;
+
+ switch(maximum_effective_key) {
+ case 40:
+ rc2params.rc2ParameterVersion = 160;
+ break;
+ case 64:
+ rc2params.rc2ParameterVersion = 120;
+ break;
+ case 128:
+ rc2params.rc2ParameterVersion = 58;
+ break;
+ }
+ rc2params.iv = *ivec;
+
+ ASN1_MALLOC_ENCODE(CMSRC2CBCParameter, param->data, param->length,
+ &rc2params, &size, ret);
+ if (ret == 0 && size != param->length)
+ _hx509_abort("Internal asn1 encoder failure");
+
+ return ret;
+}
+
+static int
+CMSRC2CBCParam_set(hx509_context context, const heim_octet_string *param,
+ hx509_crypto crypto, heim_octet_string *ivec)
+{
+ CMSRC2CBCParameter rc2param;
+ struct _RC2_params *p;
+ size_t size;
+ int ret;
+
+ ret = decode_CMSRC2CBCParameter(param->data, param->length,
+ &rc2param, &size);
+ if (ret) {
+ hx509_clear_error_string(context);
+ return ret;
+ }
+
+ p = calloc(1, sizeof(*p));
+ if (p == NULL) {
+ free_CMSRC2CBCParameter(&rc2param);
+ hx509_clear_error_string(context);
+ return ENOMEM;
+ }
+ switch(rc2param.rc2ParameterVersion) {
+ case 160:
+ crypto->c = EVP_rc2_40_cbc();
+ p->maximum_effective_key = 40;
+ break;
+ case 120:
+ crypto->c = EVP_rc2_64_cbc();
+ p->maximum_effective_key = 64;
+ break;
+ case 58:
+ crypto->c = EVP_rc2_cbc();
+ p->maximum_effective_key = 128;
+ break;
+ default:
+ free(p);
+ free_CMSRC2CBCParameter(&rc2param);
+ return HX509_CRYPTO_SIG_INVALID_FORMAT;
+ }
+ if (ivec)
+ ret = der_copy_octet_string(&rc2param.iv, ivec);
+ free_CMSRC2CBCParameter(&rc2param);
+ if (ret) {
+ free(p);
+ hx509_clear_error_string(context);
+ } else
+ crypto->param = p;
+
+ return ret;
+}
+
+/*
+ *
+ */
+
+static const struct hx509cipher ciphers[] = {
+ {
+ "rc2-cbc",
+ oid_id_pkcs3_rc2_cbc,
+ NULL,
+ EVP_rc2_cbc,
+ CMSRC2CBCParam_get,
+ CMSRC2CBCParam_set
+ },
+ {
+ "rc2-cbc",
+ oid_id_rsadsi_rc2_cbc,
+ NULL,
+ EVP_rc2_cbc,
+ CMSRC2CBCParam_get,
+ CMSRC2CBCParam_set
+ },
+ {
+ "rc2-40-cbc",
+ oid_private_rc2_40,
+ NULL,
+ EVP_rc2_40_cbc,
+ CMSRC2CBCParam_get,
+ CMSRC2CBCParam_set
+ },
+ {
+ "des-ede3-cbc",
+ oid_id_pkcs3_des_ede3_cbc,
+ NULL,
+ EVP_des_ede3_cbc,
+ CMSCBCParam_get,
+ CMSCBCParam_set
+ },
+ {
+ "des-ede3-cbc",
+ oid_id_rsadsi_des_ede3_cbc,
+ hx509_crypto_des_rsdi_ede3_cbc,
+ EVP_des_ede3_cbc,
+ CMSCBCParam_get,
+ CMSCBCParam_set
+ },
+ {
+ "aes-128-cbc",
+ oid_id_aes_128_cbc,
+ hx509_crypto_aes128_cbc,
+ EVP_aes_128_cbc,
+ CMSCBCParam_get,
+ CMSCBCParam_set
+ },
+ {
+ "aes-192-cbc",
+ oid_id_aes_192_cbc,
+ NULL,
+ EVP_aes_192_cbc,
+ CMSCBCParam_get,
+ CMSCBCParam_set
+ },
+ {
+ "aes-256-cbc",
+ oid_id_aes_256_cbc,
+ hx509_crypto_aes256_cbc,
+ EVP_aes_256_cbc,
+ CMSCBCParam_get,
+ CMSCBCParam_set
+ }
+};
+
+static const struct hx509cipher *
+find_cipher_by_oid(const heim_oid *oid)
+{
+ int i;
+
+ for (i = 0; i < sizeof(ciphers)/sizeof(ciphers[0]); i++)
+ if (der_heim_oid_cmp(oid, (*ciphers[i].oid_func)()) == 0)
+ return &ciphers[i];
+
+ return NULL;
+}
+
+static const struct hx509cipher *
+find_cipher_by_name(const char *name)
+{
+ int i;
+
+ for (i = 0; i < sizeof(ciphers)/sizeof(ciphers[0]); i++)
+ if (strcasecmp(name, ciphers[i].name) == 0)
+ return &ciphers[i];
+
+ return NULL;
+}
+
+
+const heim_oid *
+hx509_crypto_enctype_by_name(const char *name)
+{
+ const struct hx509cipher *cipher;
+
+ cipher = find_cipher_by_name(name);
+ if (cipher == NULL)
+ return NULL;
+ return (*cipher->oid_func)();
+}
+
+int
+hx509_crypto_init(hx509_context context,
+ const char *provider,
+ const heim_oid *enctype,
+ hx509_crypto *crypto)
+{
+ const struct hx509cipher *cipher;
+
+ *crypto = NULL;
+
+ cipher = find_cipher_by_oid(enctype);
+ if (cipher == NULL) {
+ hx509_set_error_string(context, 0, HX509_ALG_NOT_SUPP,
+ "Algorithm not supported");
+ return HX509_ALG_NOT_SUPP;
+ }
+
+ *crypto = calloc(1, sizeof(**crypto));
+ if (*crypto == NULL) {
+ hx509_clear_error_string(context);
+ return ENOMEM;
+ }
+
+ (*crypto)->cipher = cipher;
+ (*crypto)->c = (*cipher->evp_func)();
+
+ if (der_copy_oid(enctype, &(*crypto)->oid)) {
+ hx509_crypto_destroy(*crypto);
+ *crypto = NULL;
+ hx509_clear_error_string(context);
+ return ENOMEM;
+ }
+
+ return 0;
+}
+
+const char *
+hx509_crypto_provider(hx509_crypto crypto)
+{
+ return "unknown";
+}
+
+void
+hx509_crypto_destroy(hx509_crypto crypto)
+{
+ if (crypto->name)
+ free(crypto->name);
+ if (crypto->key.data)
+ free(crypto->key.data);
+ if (crypto->param)
+ free(crypto->param);
+ der_free_oid(&crypto->oid);
+ memset(crypto, 0, sizeof(*crypto));
+ free(crypto);
+}
+
+int
+hx509_crypto_set_key_name(hx509_crypto crypto, const char *name)
+{
+ return 0;
+}
+
+int
+hx509_crypto_set_key_data(hx509_crypto crypto, const void *data, size_t length)
+{
+ if (EVP_CIPHER_key_length(crypto->c) > length)
+ return HX509_CRYPTO_INTERNAL_ERROR;
+
+ if (crypto->key.data) {
+ free(crypto->key.data);
+ crypto->key.data = NULL;
+ crypto->key.length = 0;
+ }
+ crypto->key.data = malloc(length);
+ if (crypto->key.data == NULL)
+ return ENOMEM;
+ memcpy(crypto->key.data, data, length);
+ crypto->key.length = length;
+
+ return 0;
+}
+
+int
+hx509_crypto_set_random_key(hx509_crypto crypto, heim_octet_string *key)
+{
+ if (crypto->key.data) {
+ free(crypto->key.data);
+ crypto->key.length = 0;
+ }
+
+ crypto->key.length = EVP_CIPHER_key_length(crypto->c);
+ crypto->key.data = malloc(crypto->key.length);
+ if (crypto->key.data == NULL) {
+ crypto->key.length = 0;
+ return ENOMEM;
+ }
+ if (RAND_bytes(crypto->key.data, crypto->key.length) <= 0) {
+ free(crypto->key.data);
+ crypto->key.data = NULL;
+ crypto->key.length = 0;
+ return HX509_CRYPTO_INTERNAL_ERROR;
+ }
+ if (key)
+ return der_copy_octet_string(&crypto->key, key);
+ else
+ return 0;
+}
+
+int
+hx509_crypto_set_params(hx509_context context,
+ hx509_crypto crypto,
+ const heim_octet_string *param,
+ heim_octet_string *ivec)
+{
+ return (*crypto->cipher->set_params)(context, param, crypto, ivec);
+}
+
+int
+hx509_crypto_get_params(hx509_context context,
+ hx509_crypto crypto,
+ const heim_octet_string *ivec,
+ heim_octet_string *param)
+{
+ return (*crypto->cipher->get_params)(context, crypto, ivec, param);
+}
+
+int
+hx509_crypto_random_iv(hx509_crypto crypto, heim_octet_string *ivec)
+{
+ ivec->length = EVP_CIPHER_iv_length(crypto->c);
+ ivec->data = malloc(ivec->length);
+ if (ivec->data == NULL) {
+ ivec->length = 0;
+ return ENOMEM;
+ }
+
+ if (RAND_bytes(ivec->data, ivec->length) <= 0) {
+ free(ivec->data);
+ ivec->data = NULL;
+ ivec->length = 0;
+ return HX509_CRYPTO_INTERNAL_ERROR;
+ }
+ return 0;
+}
+
+int
+hx509_crypto_encrypt(hx509_crypto crypto,
+ const void *data,
+ const size_t length,
+ const heim_octet_string *ivec,
+ heim_octet_string **ciphertext)
+{
+ EVP_CIPHER_CTX evp;
+ size_t padsize;
+ int ret;
+
+ *ciphertext = NULL;
+
+ assert(EVP_CIPHER_iv_length(crypto->c) == ivec->length);
+
+ EVP_CIPHER_CTX_init(&evp);
+
+ ret = EVP_CipherInit_ex(&evp, crypto->c, NULL,
+ crypto->key.data, ivec->data, 1);
+ if (ret != 1) {
+ EVP_CIPHER_CTX_cleanup(&evp);
+ ret = HX509_CRYPTO_INTERNAL_ERROR;
+ goto out;
+ }
+
+ *ciphertext = calloc(1, sizeof(**ciphertext));
+ if (*ciphertext == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+
+ if (EVP_CIPHER_block_size(crypto->c) == 1) {
+ padsize = 0;
+ } else {
+ int bsize = EVP_CIPHER_block_size(crypto->c);
+ padsize = bsize - (length % bsize);
+ }
+ (*ciphertext)->length = length + padsize;
+ (*ciphertext)->data = malloc(length + padsize);
+ if ((*ciphertext)->data == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+
+ memcpy((*ciphertext)->data, data, length);
+ if (padsize) {
+ int i;
+ unsigned char *p = (*ciphertext)->data;
+ p += length;
+ for (i = 0; i < padsize; i++)
+ *p++ = padsize;
+ }
+
+ ret = EVP_Cipher(&evp, (*ciphertext)->data,
+ (*ciphertext)->data,
+ length + padsize);
+ if (ret != 1) {
+ ret = HX509_CRYPTO_INTERNAL_ERROR;
+ goto out;
+ }
+ ret = 0;
+
+ out:
+ if (ret) {
+ if (*ciphertext) {
+ if ((*ciphertext)->data) {
+ free((*ciphertext)->data);
+ }
+ free(*ciphertext);
+ *ciphertext = NULL;
+ }
+ }
+ EVP_CIPHER_CTX_cleanup(&evp);
+
+ return ret;
+}
+
+int
+hx509_crypto_decrypt(hx509_crypto crypto,
+ const void *data,
+ const size_t length,
+ heim_octet_string *ivec,
+ heim_octet_string *clear)
+{
+ EVP_CIPHER_CTX evp;
+ void *idata = NULL;
+ int ret;
+
+ clear->data = NULL;
+ clear->length = 0;
+
+ if (ivec && EVP_CIPHER_iv_length(crypto->c) < ivec->length)
+ return HX509_CRYPTO_INTERNAL_ERROR;
+
+ if (crypto->key.data == NULL)
+ return HX509_CRYPTO_INTERNAL_ERROR;
+
+ if (ivec)
+ idata = ivec->data;
+
+ EVP_CIPHER_CTX_init(&evp);
+
+ ret = EVP_CipherInit_ex(&evp, crypto->c, NULL,
+ crypto->key.data, idata, 0);
+ if (ret != 1) {
+ EVP_CIPHER_CTX_cleanup(&evp);
+ return HX509_CRYPTO_INTERNAL_ERROR;
+ }
+
+ clear->length = length;
+ clear->data = malloc(length);
+ if (clear->data == NULL) {
+ EVP_CIPHER_CTX_cleanup(&evp);
+ clear->length = 0;
+ return ENOMEM;
+ }
+
+ if (EVP_Cipher(&evp, clear->data, data, length) != 1) {
+ return HX509_CRYPTO_INTERNAL_ERROR;
+ }
+ EVP_CIPHER_CTX_cleanup(&evp);
+
+ if (EVP_CIPHER_block_size(crypto->c) > 1) {
+ int padsize;
+ unsigned char *p;
+ int j, bsize = EVP_CIPHER_block_size(crypto->c);
+
+ if (clear->length < bsize) {
+ ret = HX509_CMS_PADDING_ERROR;
+ goto out;
+ }
+
+ p = clear->data;
+ p += clear->length - 1;
+ padsize = *p;
+ if (padsize > bsize) {
+ ret = HX509_CMS_PADDING_ERROR;
+ goto out;
+ }
+ clear->length -= padsize;
+ for (j = 0; j < padsize; j++) {
+ if (*p-- != padsize) {
+ ret = HX509_CMS_PADDING_ERROR;
+ goto out;
+ }
+ }
+ }
+
+ return 0;
+
+ out:
+ if (clear->data)
+ free(clear->data);
+ clear->data = NULL;
+ clear->length = 0;
+ return ret;
+}
+
+typedef int (*PBE_string2key_func)(hx509_context,
+ const char *,
+ const heim_octet_string *,
+ hx509_crypto *, heim_octet_string *,
+ heim_octet_string *,
+ const heim_oid *, const EVP_MD *);
+
+static int
+PBE_string2key(hx509_context context,
+ const char *password,
+ const heim_octet_string *parameters,
+ hx509_crypto *crypto,
+ heim_octet_string *key, heim_octet_string *iv,
+ const heim_oid *enc_oid,
+ const EVP_MD *md)
+{
+ PKCS12_PBEParams p12params;
+ int passwordlen;
+ hx509_crypto c;
+ int iter, saltlen, ret;
+ unsigned char *salt;
+
+ passwordlen = password ? strlen(password) : 0;
+
+ if (parameters == NULL)
+ return HX509_ALG_NOT_SUPP;
+
+ ret = decode_PKCS12_PBEParams(parameters->data,
+ parameters->length,
+ &p12params, NULL);
+ if (ret)
+ goto out;
+
+ if (p12params.iterations)
+ iter = *p12params.iterations;
+ else
+ iter = 1;
+ salt = p12params.salt.data;
+ saltlen = p12params.salt.length;
+
+ if (!PKCS12_key_gen (password, passwordlen, salt, saltlen,
+ PKCS12_KEY_ID, iter, key->length, key->data, md)) {
+ ret = HX509_CRYPTO_INTERNAL_ERROR;
+ goto out;
+ }
+
+ if (!PKCS12_key_gen (password, passwordlen, salt, saltlen,
+ PKCS12_IV_ID, iter, iv->length, iv->data, md)) {
+ ret = HX509_CRYPTO_INTERNAL_ERROR;
+ goto out;
+ }
+
+ ret = hx509_crypto_init(context, NULL, enc_oid, &c);
+ if (ret)
+ goto out;
+
+ ret = hx509_crypto_set_key_data(c, key->data, key->length);
+ if (ret) {
+ hx509_crypto_destroy(c);
+ goto out;
+ }
+
+ *crypto = c;
+out:
+ free_PKCS12_PBEParams(&p12params);
+ return ret;
+}
+
+static const heim_oid *
+find_string2key(const heim_oid *oid,
+ const EVP_CIPHER **c,
+ const EVP_MD **md,
+ PBE_string2key_func *s2k)
+{
+ if (der_heim_oid_cmp(oid, oid_id_pbewithSHAAnd40BitRC2_CBC()) == 0) {
+ *c = EVP_rc2_40_cbc();
+ *md = EVP_sha1();
+ *s2k = PBE_string2key;
+ return oid_private_rc2_40();
+ } else if (der_heim_oid_cmp(oid, oid_id_pbeWithSHAAnd128BitRC2_CBC()) == 0) {
+ *c = EVP_rc2_cbc();
+ *md = EVP_sha1();
+ *s2k = PBE_string2key;
+ return oid_id_pkcs3_rc2_cbc();
+#if 0
+ } else if (der_heim_oid_cmp(oid, oid_id_pbeWithSHAAnd40BitRC4()) == 0) {
+ *c = EVP_rc4_40();
+ *md = EVP_sha1();
+ *s2k = PBE_string2key;
+ return NULL;
+ } else if (der_heim_oid_cmp(oid, oid_id_pbeWithSHAAnd128BitRC4()) == 0) {
+ *c = EVP_rc4();
+ *md = EVP_sha1();
+ *s2k = PBE_string2key;
+ return oid_id_pkcs3_rc4();
+#endif
+ } else if (der_heim_oid_cmp(oid, oid_id_pbeWithSHAAnd3_KeyTripleDES_CBC()) == 0) {
+ *c = EVP_des_ede3_cbc();
+ *md = EVP_sha1();
+ *s2k = PBE_string2key;
+ return oid_id_pkcs3_des_ede3_cbc();
+ }
+
+ return NULL;
+}
+
+/*
+ *
+ */
+
+int
+_hx509_pbe_encrypt(hx509_context context,
+ hx509_lock lock,
+ const AlgorithmIdentifier *ai,
+ const heim_octet_string *content,
+ heim_octet_string *econtent)
+{
+ hx509_clear_error_string(context);
+ return EINVAL;
+}
+
+/*
+ *
+ */
+
+int
+_hx509_pbe_decrypt(hx509_context context,
+ hx509_lock lock,
+ const AlgorithmIdentifier *ai,
+ const heim_octet_string *econtent,
+ heim_octet_string *content)
+{
+ const struct _hx509_password *pw;
+ heim_octet_string key, iv;
+ const heim_oid *enc_oid;
+ const EVP_CIPHER *c;
+ const EVP_MD *md;
+ PBE_string2key_func s2k;
+ int i, ret = 0;
+
+ memset(&key, 0, sizeof(key));
+ memset(&iv, 0, sizeof(iv));
+
+ memset(content, 0, sizeof(*content));
+
+ enc_oid = find_string2key(&ai->algorithm, &c, &md, &s2k);
+ if (enc_oid == NULL) {
+ hx509_set_error_string(context, 0, HX509_ALG_NOT_SUPP,
+ "String to key algorithm not supported");
+ ret = HX509_ALG_NOT_SUPP;
+ goto out;
+ }
+
+ key.length = EVP_CIPHER_key_length(c);
+ key.data = malloc(key.length);
+ if (key.data == NULL) {
+ ret = ENOMEM;
+ hx509_clear_error_string(context);
+ goto out;
+ }
+
+ iv.length = EVP_CIPHER_iv_length(c);
+ iv.data = malloc(iv.length);
+ if (iv.data == NULL) {
+ ret = ENOMEM;
+ hx509_clear_error_string(context);
+ goto out;
+ }
+
+ pw = _hx509_lock_get_passwords(lock);
+
+ ret = HX509_CRYPTO_INTERNAL_ERROR;
+ for (i = 0; i < pw->len + 1; i++) {
+ hx509_crypto crypto;
+ const char *password;
+
+ if (i < pw->len)
+ password = pw->val[i];
+ else if (i < pw->len + 1)
+ password = "";
+ else
+ password = NULL;
+
+ ret = (*s2k)(context, password, ai->parameters, &crypto,
+ &key, &iv, enc_oid, md);
+ if (ret)
+ goto out;
+
+ ret = hx509_crypto_decrypt(crypto,
+ econtent->data,
+ econtent->length,
+ &iv,
+ content);
+ hx509_crypto_destroy(crypto);
+ if (ret == 0)
+ goto out;
+
+ }
+out:
+ if (key.data)
+ der_free_octet_string(&key);
+ if (iv.data)
+ der_free_octet_string(&iv);
+ return ret;
+}
+
+/*
+ *
+ */
+
+
+int
+_hx509_match_keys(hx509_cert c, hx509_private_key private_key)
+{
+ const Certificate *cert;
+ const SubjectPublicKeyInfo *spi;
+ RSAPublicKey pk;
+ RSA *rsa;
+ size_t size;
+ int ret;
+
+ if (private_key->private_key.rsa == NULL)
+ return 0;
+
+ rsa = private_key->private_key.rsa;
+ if (rsa->d == NULL || rsa->p == NULL || rsa->q == NULL)
+ return 0;
+
+ cert = _hx509_get_cert(c);
+ spi = &cert->tbsCertificate.subjectPublicKeyInfo;
+
+ rsa = RSA_new();
+ if (rsa == NULL)
+ return 0;
+
+ ret = decode_RSAPublicKey(spi->subjectPublicKey.data,
+ spi->subjectPublicKey.length / 8,
+ &pk, &size);
+ if (ret) {
+ RSA_free(rsa);
+ return 0;
+ }
+ rsa->n = heim_int2BN(&pk.modulus);
+ rsa->e = heim_int2BN(&pk.publicExponent);
+
+ free_RSAPublicKey(&pk);
+
+ rsa->d = BN_dup(private_key->private_key.rsa->d);
+ rsa->p = BN_dup(private_key->private_key.rsa->p);
+ rsa->q = BN_dup(private_key->private_key.rsa->q);
+ rsa->dmp1 = BN_dup(private_key->private_key.rsa->dmp1);
+ rsa->dmq1 = BN_dup(private_key->private_key.rsa->dmq1);
+ rsa->iqmp = BN_dup(private_key->private_key.rsa->iqmp);
+
+ if (rsa->n == NULL || rsa->e == NULL ||
+ rsa->d == NULL || rsa->p == NULL|| rsa->q == NULL ||
+ rsa->dmp1 == NULL || rsa->dmq1 == NULL) {
+ RSA_free(rsa);
+ return 0;
+ }
+
+ ret = RSA_check_key(rsa);
+ RSA_free(rsa);
+
+ return ret == 1;
+}
+
+static const heim_oid *
+find_keytype(const hx509_private_key key)
+{
+ const struct signature_alg *md;
+
+ if (key == NULL)
+ return NULL;
+
+ md = find_sig_alg(key->signature_alg);
+ if (md == NULL)
+ return NULL;
+ return (*md->key_oid)();
+}
+
+
+int
+hx509_crypto_select(const hx509_context context,
+ int type,
+ const hx509_private_key source,
+ hx509_peer_info peer,
+ AlgorithmIdentifier *selected)
+{
+ const AlgorithmIdentifier *def;
+ size_t i, j;
+ int ret, bits;
+
+ memset(selected, 0, sizeof(*selected));
+
+ if (type == HX509_SELECT_DIGEST) {
+ bits = SIG_DIGEST;
+ def = _hx509_crypto_default_digest_alg;
+ } else if (type == HX509_SELECT_PUBLIC_SIG) {
+ bits = SIG_PUBLIC_SIG;
+ /* XXX depend on `source´ and `peer´ */
+ def = _hx509_crypto_default_sig_alg;
+ } else if (type == HX509_SELECT_SECRET_ENC) {
+ bits = SIG_SECRET;
+ def = _hx509_crypto_default_secret_alg;
+ } else {
+ hx509_set_error_string(context, 0, EINVAL,
+ "Unknown type %d of selection", type);
+ return EINVAL;
+ }
+
+ if (peer) {
+ const heim_oid *keytype = NULL;
+
+ keytype = find_keytype(source);
+
+ for (i = 0; i < peer->len; i++) {
+ for (j = 0; sig_algs[j]; j++) {
+ if ((sig_algs[j]->flags & bits) != bits)
+ continue;
+ if (der_heim_oid_cmp((*sig_algs[j]->sig_oid)(),
+ &peer->val[i].algorithm) != 0)
+ continue;
+ if (keytype && sig_algs[j]->key_oid &&
+ der_heim_oid_cmp(keytype, (*sig_algs[j]->key_oid)()))
+ continue;
+
+ /* found one, use that */
+ ret = copy_AlgorithmIdentifier(&peer->val[i], selected);
+ if (ret)
+ hx509_clear_error_string(context);
+ return ret;
+ }
+ if (bits & SIG_SECRET) {
+ const struct hx509cipher *cipher;
+
+ cipher = find_cipher_by_oid(&peer->val[i].algorithm);
+ if (cipher == NULL)
+ continue;
+ if (cipher->ai_func == NULL)
+ continue;
+ ret = copy_AlgorithmIdentifier(cipher->ai_func(), selected);
+ if (ret)
+ hx509_clear_error_string(context);
+ return ret;
+ }
+ }
+ }
+
+ /* use default */
+ ret = copy_AlgorithmIdentifier(def, selected);
+ if (ret)
+ hx509_clear_error_string(context);
+ return ret;
+}
+
+int
+hx509_crypto_available(hx509_context context,
+ int type,
+ hx509_cert source,
+ AlgorithmIdentifier **val,
+ unsigned int *plen)
+{
+ const heim_oid *keytype = NULL;
+ unsigned int len, i;
+ void *ptr;
+ int bits, ret;
+
+ *val = NULL;
+
+ if (type == HX509_SELECT_ALL) {
+ bits = SIG_DIGEST | SIG_PUBLIC_SIG | SIG_SECRET;
+ } else if (type == HX509_SELECT_DIGEST) {
+ bits = SIG_DIGEST;
+ } else if (type == HX509_SELECT_PUBLIC_SIG) {
+ bits = SIG_PUBLIC_SIG;
+ } else {
+ hx509_set_error_string(context, 0, EINVAL,
+ "Unknown type %d of available", type);
+ return EINVAL;
+ }
+
+ if (source)
+ keytype = find_keytype(_hx509_cert_private_key(source));
+
+ len = 0;
+ for (i = 0; sig_algs[i]; i++) {
+ if ((sig_algs[i]->flags & bits) == 0)
+ continue;
+ if (sig_algs[i]->sig_alg == NULL)
+ continue;
+ if (keytype && sig_algs[i]->key_oid &&
+ der_heim_oid_cmp((*sig_algs[i]->key_oid)(), keytype))
+ continue;
+
+ /* found one, add that to the list */
+ ptr = realloc(*val, sizeof(**val) * (len + 1));
+ if (ptr == NULL)
+ goto out;
+ *val = ptr;
+
+ ret = copy_AlgorithmIdentifier((*sig_algs[i]->sig_alg)(), &(*val)[len]);
+ if (ret)
+ goto out;
+ len++;
+ }
+
+ /* Add AES */
+ if (bits & SIG_SECRET) {
+
+ for (i = 0; i < sizeof(ciphers)/sizeof(ciphers[0]); i++) {
+
+ if (ciphers[i].ai_func == NULL)
+ continue;
+
+ ptr = realloc(*val, sizeof(**val) * (len + 1));
+ if (ptr == NULL)
+ goto out;
+ *val = ptr;
+
+ ret = copy_AlgorithmIdentifier((ciphers[i].ai_func)(), &(*val)[len]);
+ if (ret)
+ goto out;
+ len++;
+ }
+ }
+
+ *plen = len;
+ return 0;
+
+out:
+ for (i = 0; i < len; i++)
+ free_AlgorithmIdentifier(&(*val)[i]);
+ free(*val);
+ *val = NULL;
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+}
+
+void
+hx509_crypto_free_algs(AlgorithmIdentifier *val,
+ unsigned int len)
+{
+ unsigned int i;
+ for (i = 0; i < len; i++)
+ free_AlgorithmIdentifier(&val[i]);
+ free(val);
+}
diff --git a/source4/heimdal/lib/hx509/env.c b/source4/heimdal/lib/hx509/env.c
new file mode 100644
index 0000000000..0b0a68ceae
--- /dev/null
+++ b/source4/heimdal/lib/hx509/env.c
@@ -0,0 +1,246 @@
+/*
+ * Copyright (c) 2007 - 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hx_locl.h"
+RCSID("$Id$");
+
+/**
+ * @page page_env Hx509 enviroment functions
+ *
+ * See the library functions here: @ref hx509_env
+ */
+
+/**
+ * Add a new key/value pair to the hx509_env.
+ *
+ * @param context A hx509 context.
+ * @param env enviroment to add the enviroment variable too.
+ * @param key key to add
+ * @param value value to add
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_env
+ */
+
+int
+hx509_env_add(hx509_context context, hx509_env *env,
+ const char *key, const char *value)
+{
+ hx509_env n;
+
+ n = malloc(sizeof(*n));
+ if (n == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+
+ n->type = env_string;
+ n->next = NULL;
+ n->name = strdup(key);
+ if (n->name == NULL) {
+ free(n);
+ return ENOMEM;
+ }
+ n->u.string = strdup(value);
+ if (n->u.string == NULL) {
+ free(n->name);
+ free(n);
+ return ENOMEM;
+ }
+
+ /* add to tail */
+ if (*env) {
+ hx509_env e = *env;
+ while (e->next)
+ e = e->next;
+ e->next = n;
+ } else
+ *env = n;
+
+ return 0;
+}
+
+/**
+ * Add a new key/binding pair to the hx509_env.
+ *
+ * @param context A hx509 context.
+ * @param env enviroment to add the enviroment variable too.
+ * @param key key to add
+ * @param list binding list to add
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_env
+ */
+
+int
+hx509_env_add_binding(hx509_context context, hx509_env *env,
+ const char *key, hx509_env list)
+{
+ hx509_env n;
+
+ n = malloc(sizeof(*n));
+ if (n == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+
+ n->type = env_list;
+ n->next = NULL;
+ n->name = strdup(key);
+ if (n->name == NULL) {
+ free(n);
+ return ENOMEM;
+ }
+ n->u.list = list;
+
+ /* add to tail */
+ if (*env) {
+ hx509_env e = *env;
+ while (e->next)
+ e = e->next;
+ e->next = n;
+ } else
+ *env = n;
+
+ return 0;
+}
+
+
+/**
+ * Search the hx509_env for a length based key.
+ *
+ * @param context A hx509 context.
+ * @param env enviroment to add the enviroment variable too.
+ * @param key key to search for.
+ * @param len length of key.
+ *
+ * @return the value if the key is found, NULL otherwise.
+ *
+ * @ingroup hx509_env
+ */
+
+const char *
+hx509_env_lfind(hx509_context context, hx509_env env,
+ const char *key, size_t len)
+{
+ while(env) {
+ if (strncmp(key, env->name ,len) == 0
+ && env->name[len] == '\0' && env->type == env_string)
+ return env->u.string;
+ env = env->next;
+ }
+ return NULL;
+}
+
+/**
+ * Search the hx509_env for a key.
+ *
+ * @param context A hx509 context.
+ * @param env enviroment to add the enviroment variable too.
+ * @param key key to search for.
+ *
+ * @return the value if the key is found, NULL otherwise.
+ *
+ * @ingroup hx509_env
+ */
+
+const char *
+hx509_env_find(hx509_context context, hx509_env env, const char *key)
+{
+ while(env) {
+ if (strcmp(key, env->name) == 0 && env->type == env_string)
+ return env->u.string;
+ env = env->next;
+ }
+ return NULL;
+}
+
+/**
+ * Search the hx509_env for a binding.
+ *
+ * @param context A hx509 context.
+ * @param env enviroment to add the enviroment variable too.
+ * @param key key to search for.
+ *
+ * @return the binding if the key is found, NULL if not found.
+ *
+ * @ingroup hx509_env
+ */
+
+hx509_env
+hx509_env_find_binding(hx509_context context,
+ hx509_env env,
+ const char *key)
+{
+ while(env) {
+ if (strcmp(key, env->name) == 0 && env->type == env_list)
+ return env->u.list;
+ env = env->next;
+ }
+ return NULL;
+}
+
+static void
+env_free(hx509_env b)
+{
+ while(b) {
+ hx509_env next = b->next;
+
+ if (b->type == env_string)
+ free(b->u.string);
+ else if (b->type == env_list)
+ env_free(b->u.list);
+
+ free(b->name);
+ free(b);
+ b = next;
+ }
+}
+
+/**
+ * Free an hx509_env enviroment context.
+ *
+ * @param env the enviroment to free.
+ *
+ * @ingroup hx509_env
+ */
+
+void
+hx509_env_free(hx509_env *env)
+{
+ if (*env)
+ env_free(*env);
+ *env = NULL;
+}
diff --git a/source4/heimdal/lib/hx509/error.c b/source4/heimdal/lib/hx509/error.c
new file mode 100644
index 0000000000..6f25404145
--- /dev/null
+++ b/source4/heimdal/lib/hx509/error.c
@@ -0,0 +1,223 @@
+/*
+ * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hx_locl.h"
+RCSID("$Id$");
+
+/**
+ * @page page_error Hx509 error reporting functions
+ *
+ * See the library functions here: @ref hx509_error
+ */
+
+struct hx509_error_data {
+ hx509_error next;
+ int code;
+ char *msg;
+};
+
+static void
+free_error_string(hx509_error msg)
+{
+ while(msg) {
+ hx509_error m2 = msg->next;
+ free(msg->msg);
+ free(msg);
+ msg = m2;
+ }
+}
+
+/**
+ * Resets the error strings the hx509 context.
+ *
+ * @param context A hx509 context.
+ *
+ * @ingroup hx509_error
+ */
+
+void
+hx509_clear_error_string(hx509_context context)
+{
+ free_error_string(context->error);
+ context->error = NULL;
+}
+
+/**
+ * Add an error message to the hx509 context.
+ *
+ * @param context A hx509 context.
+ * @param flags
+ * - HX509_ERROR_APPEND appends the error string to the old messages
+ (code is updated).
+ * @param code error code related to error message
+ * @param fmt error message format
+ * @param ap arguments to error message format
+ *
+ * @ingroup hx509_error
+ */
+
+void
+hx509_set_error_stringv(hx509_context context, int flags, int code,
+ const char *fmt, va_list ap)
+{
+ hx509_error msg;
+
+ msg = calloc(1, sizeof(*msg));
+ if (msg == NULL) {
+ hx509_clear_error_string(context);
+ return;
+ }
+
+ if (vasprintf(&msg->msg, fmt, ap) == -1) {
+ hx509_clear_error_string(context);
+ free(msg);
+ return;
+ }
+ msg->code = code;
+
+ if (flags & HX509_ERROR_APPEND) {
+ msg->next = context->error;
+ context->error = msg;
+ } else {
+ free_error_string(context->error);
+ context->error = msg;
+ }
+}
+
+/**
+ * See hx509_set_error_stringv().
+ *
+ * @param context A hx509 context.
+ * @param flags
+ * - HX509_ERROR_APPEND appends the error string to the old messages
+ (code is updated).
+ * @param code error code related to error message
+ * @param fmt error message format
+ * @param ... arguments to error message format
+ *
+ * @ingroup hx509_error
+ */
+
+void
+hx509_set_error_string(hx509_context context, int flags, int code,
+ const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ hx509_set_error_stringv(context, flags, code, fmt, ap);
+ va_end(ap);
+}
+
+/**
+ * Get an error string from context associated with error_code.
+ *
+ * @param context A hx509 context.
+ * @param error_code Get error message for this error code.
+ *
+ * @return error string, free with hx509_free_error_string().
+ *
+ * @ingroup hx509_error
+ */
+
+char *
+hx509_get_error_string(hx509_context context, int error_code)
+{
+ struct rk_strpool *p = NULL;
+ hx509_error msg = context->error;
+
+ if (msg == NULL || msg->code != error_code) {
+ const char *cstr;
+ char *str;
+
+ cstr = com_right(context->et_list, error_code);
+ if (cstr)
+ return strdup(cstr);
+ cstr = strerror(error_code);
+ if (cstr)
+ return strdup(cstr);
+ if (asprintf(&str, "<unknown error: %d>", error_code) == -1)
+ return NULL;
+ return str;
+ }
+
+ for (msg = context->error; msg; msg = msg->next)
+ p = rk_strpoolprintf(p, "%s%s", msg->msg,
+ msg->next != NULL ? "; " : "");
+
+ return rk_strpoolcollect(p);
+}
+
+/**
+ * Free error string returned by hx509_get_error_string().
+ *
+ * @param str error string to free.
+ *
+ * @ingroup hx509_error
+ */
+
+void
+hx509_free_error_string(char *str)
+{
+ free(str);
+}
+
+/**
+ * Print error message and fatally exit from error code
+ *
+ * @param context A hx509 context.
+ * @param exit_code exit() code from process.
+ * @param error_code Error code for the reason to exit.
+ * @param fmt format string with the exit message.
+ * @param ... argument to format string.
+ *
+ * @ingroup hx509_error
+ */
+
+void
+hx509_err(hx509_context context, int exit_code,
+ int error_code, const char *fmt, ...)
+{
+ va_list ap;
+ const char *msg;
+ char *str;
+
+ va_start(ap, fmt);
+ vasprintf(&str, fmt, ap);
+ va_end(ap);
+ msg = hx509_get_error_string(context, error_code);
+ if (msg == NULL)
+ msg = "no error";
+
+ errx(exit_code, "%s: %s", str, msg);
+}
diff --git a/source4/heimdal/lib/hx509/file.c b/source4/heimdal/lib/hx509/file.c
new file mode 100644
index 0000000000..a364dd2179
--- /dev/null
+++ b/source4/heimdal/lib/hx509/file.c
@@ -0,0 +1,303 @@
+/*
+ * Copyright (c) 2005 - 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hx_locl.h"
+RCSID("$ID$");
+
+int
+_hx509_map_file_os(const char *fn, heim_octet_string *os)
+{
+ size_t length;
+ void *data;
+ int ret;
+
+ ret = rk_undumpdata(fn, &data, &length);
+
+ os->data = data;
+ os->length = length;
+
+ return ret;
+}
+
+void
+_hx509_unmap_file_os(heim_octet_string *os)
+{
+ rk_xfree(os->data);
+}
+
+int
+_hx509_write_file(const char *fn, const void *data, size_t length)
+{
+ rk_dumpdata(fn, data, length);
+ return 0;
+}
+
+/*
+ *
+ */
+
+static void
+header(FILE *f, const char *type, const char *str)
+{
+ fprintf(f, "-----%s %s-----\n", type, str);
+}
+
+int
+hx509_pem_write(hx509_context context, const char *type,
+ hx509_pem_header *headers, FILE *f,
+ const void *data, size_t size)
+{
+ const char *p = data;
+ size_t length;
+ char *line;
+
+#define ENCODE_LINE_LENGTH 54
+
+ header(f, "BEGIN", type);
+
+ while (headers) {
+ fprintf(f, "%s: %s\n%s",
+ headers->header, headers->value,
+ headers->next ? "" : "\n");
+ headers = headers->next;
+ }
+
+ while (size > 0) {
+ ssize_t l;
+
+ length = size;
+ if (length > ENCODE_LINE_LENGTH)
+ length = ENCODE_LINE_LENGTH;
+
+ l = base64_encode(p, length, &line);
+ if (l < 0) {
+ hx509_set_error_string(context, 0, ENOMEM,
+ "malloc - out of memory");
+ return ENOMEM;
+ }
+ size -= length;
+ fprintf(f, "%s\n", line);
+ p += length;
+ free(line);
+ }
+
+ header(f, "END", type);
+
+ return 0;
+}
+
+/*
+ *
+ */
+
+int
+hx509_pem_add_header(hx509_pem_header **headers,
+ const char *header, const char *value)
+{
+ hx509_pem_header *h;
+
+ h = calloc(1, sizeof(*h));
+ if (h == NULL)
+ return ENOMEM;
+ h->header = strdup(header);
+ if (h->header == NULL) {
+ free(h);
+ return ENOMEM;
+ }
+ h->value = strdup(value);
+ if (h->value == NULL) {
+ free(h->header);
+ free(h);
+ return ENOMEM;
+ }
+
+ h->next = *headers;
+ *headers = h;
+
+ return 0;
+}
+
+void
+hx509_pem_free_header(hx509_pem_header *headers)
+{
+ hx509_pem_header *h;
+ while (headers) {
+ h = headers;
+ headers = headers->next;
+ free(h->header);
+ free(h->value);
+ free(h);
+ }
+}
+
+/*
+ *
+ */
+
+const char *
+hx509_pem_find_header(const hx509_pem_header *h, const char *header)
+{
+ while(h) {
+ if (strcmp(header, h->header) == 0)
+ return h->value;
+ h = h->next;
+ }
+ return NULL;
+}
+
+
+/*
+ *
+ */
+
+int
+hx509_pem_read(hx509_context context,
+ FILE *f,
+ hx509_pem_read_func func,
+ void *ctx)
+{
+ hx509_pem_header *headers = NULL;
+ char *type = NULL;
+ void *data = NULL;
+ size_t len = 0;
+ char buf[1024];
+ int ret = HX509_PARSING_KEY_FAILED;
+
+ enum { BEFORE, SEARCHHEADER, INHEADER, INDATA, DONE } where;
+
+ where = BEFORE;
+
+ while (fgets(buf, sizeof(buf), f) != NULL) {
+ char *p;
+ int i;
+
+ i = strcspn(buf, "\n");
+ if (buf[i] == '\n') {
+ buf[i] = '\0';
+ if (i > 0)
+ i--;
+ }
+ if (buf[i] == '\r') {
+ buf[i] = '\0';
+ if (i > 0)
+ i--;
+ }
+
+ switch (where) {
+ case BEFORE:
+ if (strncmp("-----BEGIN ", buf, 11) == 0) {
+ type = strdup(buf + 11);
+ if (type == NULL)
+ break;
+ p = strchr(type, '-');
+ if (p)
+ *p = '\0';
+ where = SEARCHHEADER;
+ }
+ break;
+ case SEARCHHEADER:
+ p = strchr(buf, ':');
+ if (p == NULL) {
+ where = INDATA;
+ goto indata;
+ }
+ /* FALLTHOUGH */
+ case INHEADER:
+ if (buf[0] == '\0') {
+ where = INDATA;
+ break;
+ }
+ p = strchr(buf, ':');
+ if (p) {
+ *p++ = '\0';
+ while (isspace((int)*p))
+ p++;
+ ret = hx509_pem_add_header(&headers, buf, p);
+ if (ret)
+ abort();
+ }
+ break;
+ case INDATA:
+ indata:
+
+ if (strncmp("-----END ", buf, 9) == 0) {
+ where = DONE;
+ break;
+ }
+
+ p = emalloc(i);
+ i = base64_decode(buf, p);
+ if (i < 0) {
+ free(p);
+ goto out;
+ }
+
+ data = erealloc(data, len + i);
+ memcpy(((char *)data) + len, p, i);
+ free(p);
+ len += i;
+ break;
+ case DONE:
+ abort();
+ }
+
+ if (where == DONE) {
+ ret = (*func)(context, type, headers, data, len, ctx);
+ out:
+ free(data);
+ data = NULL;
+ len = 0;
+ free(type);
+ type = NULL;
+ where = BEFORE;
+ hx509_pem_free_header(headers);
+ headers = NULL;
+ if (ret)
+ break;
+ }
+ }
+
+ if (where != BEFORE) {
+ hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,
+ "File ends before end of PEM end tag");
+ ret = HX509_PARSING_KEY_FAILED;
+ }
+ if (data)
+ free(data);
+ if (type)
+ free(type);
+ if (headers)
+ hx509_pem_free_header(headers);
+
+ return ret;
+}
diff --git a/source4/heimdal/lib/hx509/hx509.h b/source4/heimdal/lib/hx509/hx509.h
new file mode 100644
index 0000000000..5e5a2f811b
--- /dev/null
+++ b/source4/heimdal/lib/hx509/hx509.h
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2004 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef HEIMDAL_HX509_H
+#define HEIMDAL_HX509_H 1
+
+#include <heim_asn1.h>
+#include <rfc2459_asn1.h>
+
+typedef struct hx509_cert_attribute_data *hx509_cert_attribute;
+typedef struct hx509_cert_data *hx509_cert;
+typedef struct hx509_certs_data *hx509_certs;
+typedef struct hx509_context_data *hx509_context;
+typedef struct hx509_crypto_data *hx509_crypto;
+typedef struct hx509_lock_data *hx509_lock;
+typedef struct hx509_name_data *hx509_name;
+typedef struct hx509_private_key *hx509_private_key;
+typedef struct hx509_validate_ctx_data *hx509_validate_ctx;
+typedef struct hx509_verify_ctx_data *hx509_verify_ctx;
+typedef struct hx509_revoke_ctx_data *hx509_revoke_ctx;
+typedef struct hx509_query_data hx509_query;
+typedef void * hx509_cursor;
+typedef struct hx509_request_data *hx509_request;
+typedef struct hx509_error_data *hx509_error;
+typedef struct hx509_peer_info *hx509_peer_info;
+typedef struct hx509_ca_tbs *hx509_ca_tbs;
+typedef struct hx509_env_data *hx509_env;
+typedef struct hx509_crl *hx509_crl;
+
+typedef void (*hx509_vprint_func)(void *, const char *, va_list);
+
+enum {
+ HX509_VHN_F_ALLOW_NO_MATCH = 1
+};
+
+enum {
+ HX509_VALIDATE_F_VALIDATE = 1,
+ HX509_VALIDATE_F_VERBOSE = 2
+};
+
+struct hx509_cert_attribute_data {
+ heim_oid oid;
+ heim_octet_string data;
+};
+
+typedef enum {
+ HX509_PROMPT_TYPE_PASSWORD = 0x1, /* password, hidden */
+ HX509_PROMPT_TYPE_QUESTION = 0x2, /* question, not hidden */
+ HX509_PROMPT_TYPE_INFO = 0x4 /* infomation, reply doesn't matter */
+} hx509_prompt_type;
+
+typedef struct hx509_prompt {
+ const char *prompt;
+ hx509_prompt_type type;
+ heim_octet_string reply;
+} hx509_prompt;
+
+typedef int (*hx509_prompter_fct)(void *, const hx509_prompt *);
+
+typedef struct hx509_octet_string_list {
+ size_t len;
+ heim_octet_string *val;
+} hx509_octet_string_list;
+
+typedef struct hx509_pem_header {
+ struct hx509_pem_header *next;
+ char *header;
+ char *value;
+} hx509_pem_header;
+
+typedef int
+(*hx509_pem_read_func)(hx509_context, const char *, const hx509_pem_header *,
+ const void *, size_t, void *ctx);
+
+/*
+ * Options passed to hx509_query_match_option.
+ */
+typedef enum {
+ HX509_QUERY_OPTION_PRIVATE_KEY = 1,
+ HX509_QUERY_OPTION_KU_ENCIPHERMENT = 2,
+ HX509_QUERY_OPTION_KU_DIGITALSIGNATURE = 3,
+ HX509_QUERY_OPTION_KU_KEYCERTSIGN = 4,
+ HX509_QUERY_OPTION_END = 0xffff
+} hx509_query_option;
+
+/* flags to hx509_certs_init */
+#define HX509_CERTS_CREATE 0x01
+#define HX509_CERTS_UNPROTECT_ALL 0x02
+
+/* flags to hx509_set_error_string */
+#define HX509_ERROR_APPEND 0x01
+
+/* flags to hx509_cms_unenvelope */
+#define HX509_CMS_UE_DONT_REQUIRE_KU_ENCIPHERMENT 0x01
+
+/* selectors passed to hx509_crypto_select and hx509_crypto_available */
+#define HX509_SELECT_ALL 0
+#define HX509_SELECT_DIGEST 1
+#define HX509_SELECT_PUBLIC_SIG 2
+#define HX509_SELECT_PUBLIC_ENC 3
+#define HX509_SELECT_SECRET_ENC 4
+
+/* flags to hx509_ca_tbs_set_template */
+#define HX509_CA_TEMPLATE_SUBJECT 1
+#define HX509_CA_TEMPLATE_SERIAL 2
+#define HX509_CA_TEMPLATE_NOTBEFORE 4
+#define HX509_CA_TEMPLATE_NOTAFTER 8
+#define HX509_CA_TEMPLATE_SPKI 16
+#define HX509_CA_TEMPLATE_KU 32
+#define HX509_CA_TEMPLATE_EKU 64
+
+/* flags hx509_cms_create_signed* */
+#define HX509_CMS_SIGATURE_DETACHED 1
+#define HX509_CMS_SIGATURE_ID_NAME 2
+
+/* hx509_verify_hostname nametype */
+typedef enum {
+ HX509_HN_HOSTNAME = 0,
+ HX509_HN_DNSSRV
+} hx509_hostname_type;
+
+#include <hx509-protos.h>
+#include <hx509_err.h>
+
+#endif /* HEIMDAL_HX509_H */
diff --git a/source4/heimdal/lib/hx509/hx509_err.et b/source4/heimdal/lib/hx509/hx509_err.et
new file mode 100644
index 0000000000..c1dfaf587e
--- /dev/null
+++ b/source4/heimdal/lib/hx509/hx509_err.et
@@ -0,0 +1,101 @@
+#
+# Error messages for the hx509 library
+#
+# This might look like a com_err file, but is not
+#
+id "$Id$"
+
+error_table hx
+prefix HX509
+
+# path validateion and construction related errors
+error_code BAD_TIMEFORMAT, "ASN.1 failed call to system time library"
+error_code EXTENSION_NOT_FOUND, "Extension not found"
+error_code NO_PATH, "Certification path not found"
+error_code PARENT_NOT_CA, "Parent certificate is not a CA"
+error_code CA_PATH_TOO_DEEP, "CA path too deep"
+error_code SIG_ALG_NO_SUPPORTED, "Signature algorithm not supported"
+error_code SIG_ALG_DONT_MATCH_KEY_ALG, "Signature algorithm doesn't match certificate key"
+error_code CERT_USED_BEFORE_TIME, "Certificate used before it became valid"
+error_code CERT_USED_AFTER_TIME, "Certificate used after it became invalid"
+error_code PRIVATE_KEY_MISSING, "Private key required for the operation is missing"
+error_code ALG_NOT_SUPP, "Algorithm not supported"
+error_code ISSUER_NOT_FOUND, "Issuer couldn't be found"
+error_code VERIFY_CONSTRAINTS, "Error verifing constraints"
+error_code RANGE, "Number too large"
+error_code NAME_CONSTRAINT_ERROR, "Error while verifing name constraints"
+error_code PATH_TOO_LONG, "Path is too long, failed to find valid anchor"
+error_code KU_CERT_MISSING, "Required keyusage for this certificate is missing"
+error_code CERT_NOT_FOUND, "Certificate not found"
+error_code UNKNOWN_LOCK_COMMAND, "Unknown lock command"
+error_code PARENT_IS_CA, "Parent certificate is a CA"
+error_code EXTRA_DATA_AFTER_STRUCTURE, "Extra data was found after the structure"
+error_code PROXY_CERT_INVALID, "Proxy certificate is invalid"
+error_code PROXY_CERT_NAME_WRONG, "Proxy certificate name is wrong"
+error_code NAME_MALFORMED, "Name is malformated"
+error_code CERTIFICATE_MALFORMED, "Certificate is malformated"
+error_code CERTIFICATE_MISSING_EKU, "Certificate is missing a required EKU"
+error_code PROXY_CERTIFICATE_NOT_CANONICALIZED, "Proxy certificate not canonicalize"
+
+# cms related errors
+index 32
+prefix HX509_CMS
+error_code FAILED_CREATE_SIGATURE, "Failed to create signature"
+error_code MISSING_SIGNER_DATA, "Missing signer data"
+error_code SIGNER_NOT_FOUND, "Couldn't find signers certificate"
+error_code NO_DATA_AVAILABLE, "No data to perform the operation on"
+error_code INVALID_DATA, "Data in the message is invalid"
+error_code PADDING_ERROR, "Padding in the message invalid"
+error_code NO_RECIPIENT_CERTIFICATE, "Couldn't find recipient certificate"
+error_code DATA_OID_MISMATCH, "Mismatch bewteen signed type and unsigned type"
+
+# crypto related errors
+index 64
+prefix HX509_CRYPTO
+error_code INTERNAL_ERROR, "Internal error in the crypto engine"
+error_code EXTERNAL_ERROR, "External error in the crypto engine"
+error_code SIGNATURE_MISSING, "Signature missing for data"
+error_code BAD_SIGNATURE, "Signature is not valid"
+error_code SIG_NO_CONF, "Sigature doesn't provide confidentiality"
+error_code SIG_INVALID_FORMAT, "Invalid format on signature"
+error_code OID_MISMATCH, "Mismatch bewteen oids"
+error_code NO_PROMPTER, "No prompter function defined"
+error_code SIGNATURE_WITHOUT_SIGNER, "Signature require signer, but non available"
+error_code RSA_PUBLIC_ENCRYPT, "RSA public encyption failed"
+error_code RSA_PRIVATE_ENCRYPT, "RSA public encyption failed"
+error_code RSA_PUBLIC_DECRYPT, "RSA private decryption failed"
+error_code RSA_PRIVATE_DECRYPT, "RSA private decryption failed"
+
+# revoke related errors
+index 96
+prefix HX509
+error_code CRL_USED_BEFORE_TIME, "CRL used before it became valid"
+error_code CRL_USED_AFTER_TIME, "CRL used after it became invalid"
+error_code CRL_INVALID_FORMAT, "CRL have invalid format"
+error_code CERT_REVOKED, "Certificate is revoked"
+error_code REVOKE_STATUS_MISSING, "No revoke status found for certificates"
+error_code CRL_UNKNOWN_EXTENSION, "Unknown extension"
+error_code REVOKE_WRONG_DATA, "Got wrong CRL/OCSP data from server"
+error_code REVOKE_NOT_SAME_PARENT, "Doesn't have same parent as other certificates"
+error_code CERT_NOT_IN_OCSP, "Certificates not in OCSP reply"
+
+# misc error
+index 108
+error_code LOCAL_ATTRIBUTE_MISSING, "No local key attribute"
+error_code PARSING_KEY_FAILED, "Failed to parse key"
+error_code UNSUPPORTED_OPERATION, "Unsupported operation"
+error_code UNIMPLEMENTED_OPERATION, "Unimplemented operation"
+error_code PARSING_NAME_FAILED, "Failed to parse name"
+
+# keystore related error
+index 128
+prefix HX509_PKCS11
+error_code NO_SLOT, "No smartcard reader/device found"
+error_code NO_TOKEN, "No smartcard in reader"
+error_code NO_MECH, "No supported mech(s)"
+error_code TOKEN_CONFUSED, "Token or slot failed in inconsistent way"
+error_code OPEN_SESSION, "Failed to open session to slot"
+error_code LOGIN, "Failed to login to slot"
+error_code LOAD, "Failed to load PKCS module"
+
+end
diff --git a/source4/heimdal/lib/hx509/hx_locl.h b/source4/heimdal/lib/hx509/hx_locl.h
new file mode 100644
index 0000000000..8de2353f15
--- /dev/null
+++ b/source4/heimdal/lib/hx509/hx_locl.h
@@ -0,0 +1,220 @@
+/*
+ * Copyright (c) 2004 - 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+#include <strings.h>
+#include <assert.h>
+#include <stdarg.h>
+#include <err.h>
+#include <limits.h>
+
+#include <getarg.h>
+#include <base64.h>
+#include <hex.h>
+#include <roken.h>
+#include <com_err.h>
+#include <parse_units.h>
+#include <parse_bytes.h>
+
+#include <krb5-types.h>
+
+#include <rfc2459_asn1.h>
+#include <cms_asn1.h>
+#include <pkcs8_asn1.h>
+#include <pkcs9_asn1.h>
+#include <pkcs12_asn1.h>
+#include <ocsp_asn1.h>
+#include <pkcs10_asn1.h>
+#include <asn1_err.h>
+#include <pkinit_asn1.h>
+
+#include <der.h>
+
+#define HC_DEPRECATED_CRYPTO
+#include "crypto-headers.h"
+
+struct hx509_keyset_ops;
+struct hx509_collector;
+struct hx509_generate_private_context;
+typedef struct hx509_path hx509_path;
+
+#include <hx509.h>
+
+typedef void (*_hx509_cert_release_func)(struct hx509_cert_data *, void *);
+
+typedef struct hx509_private_key_ops hx509_private_key_ops;
+
+#include "sel.h"
+
+#include <hx509-private.h>
+#include <hx509_err.h>
+
+struct hx509_peer_info {
+ hx509_cert cert;
+ AlgorithmIdentifier *val;
+ size_t len;
+};
+
+#define HX509_CERTS_FIND_SERIALNUMBER 1
+#define HX509_CERTS_FIND_ISSUER 2
+#define HX509_CERTS_FIND_SUBJECT 4
+#define HX509_CERTS_FIND_ISSUER_KEY_ID 8
+#define HX509_CERTS_FIND_SUBJECT_KEY_ID 16
+
+struct hx509_name_data {
+ Name der_name;
+};
+
+struct hx509_path {
+ size_t len;
+ hx509_cert *val;
+};
+
+struct hx509_query_data {
+ int match;
+#define HX509_QUERY_FIND_ISSUER_CERT 0x000001
+#define HX509_QUERY_MATCH_SERIALNUMBER 0x000002
+#define HX509_QUERY_MATCH_ISSUER_NAME 0x000004
+#define HX509_QUERY_MATCH_SUBJECT_NAME 0x000008
+#define HX509_QUERY_MATCH_SUBJECT_KEY_ID 0x000010
+#define HX509_QUERY_MATCH_ISSUER_ID 0x000020
+#define HX509_QUERY_PRIVATE_KEY 0x000040
+#define HX509_QUERY_KU_ENCIPHERMENT 0x000080
+#define HX509_QUERY_KU_DIGITALSIGNATURE 0x000100
+#define HX509_QUERY_KU_KEYCERTSIGN 0x000200
+#define HX509_QUERY_KU_CRLSIGN 0x000400
+#define HX509_QUERY_KU_NONREPUDIATION 0x000800
+#define HX509_QUERY_KU_KEYAGREEMENT 0x001000
+#define HX509_QUERY_KU_DATAENCIPHERMENT 0x002000
+#define HX509_QUERY_ANCHOR 0x004000
+#define HX509_QUERY_MATCH_CERTIFICATE 0x008000
+#define HX509_QUERY_MATCH_LOCAL_KEY_ID 0x010000
+#define HX509_QUERY_NO_MATCH_PATH 0x020000
+#define HX509_QUERY_MATCH_FRIENDLY_NAME 0x040000
+#define HX509_QUERY_MATCH_FUNCTION 0x080000
+#define HX509_QUERY_MATCH_KEY_HASH_SHA1 0x100000
+#define HX509_QUERY_MATCH_TIME 0x200000
+#define HX509_QUERY_MATCH_EKU 0x400000
+#define HX509_QUERY_MATCH_EXPR 0x800000
+#define HX509_QUERY_MASK 0xffffff
+ Certificate *subject;
+ Certificate *certificate;
+ heim_integer *serial;
+ heim_octet_string *subject_id;
+ heim_octet_string *local_key_id;
+ Name *issuer_name;
+ Name *subject_name;
+ hx509_path *path;
+ char *friendlyname;
+ int (*cmp_func)(void *, hx509_cert);
+ void *cmp_func_ctx;
+ heim_octet_string *keyhash_sha1;
+ time_t timenow;
+ heim_oid *eku;
+ struct hx_expr *expr;
+};
+
+struct hx509_keyset_ops {
+ const char *name;
+ int flags;
+ int (*init)(hx509_context, hx509_certs, void **,
+ int, const char *, hx509_lock);
+ int (*store)(hx509_context, hx509_certs, void *, int, hx509_lock);
+ int (*free)(hx509_certs, void *);
+ int (*add)(hx509_context, hx509_certs, void *, hx509_cert);
+ int (*query)(hx509_context, hx509_certs, void *,
+ const hx509_query *, hx509_cert *);
+ int (*iter_start)(hx509_context, hx509_certs, void *, void **);
+ int (*iter)(hx509_context, hx509_certs, void *, void *, hx509_cert *);
+ int (*iter_end)(hx509_context, hx509_certs, void *, void *);
+ int (*printinfo)(hx509_context, hx509_certs,
+ void *, int (*)(void *, const char *), void *);
+ int (*getkeys)(hx509_context, hx509_certs, void *, hx509_private_key **);
+ int (*addkey)(hx509_context, hx509_certs, void *, hx509_private_key);
+};
+
+struct _hx509_password {
+ size_t len;
+ char **val;
+};
+
+extern hx509_lock _hx509_empty_lock;
+
+struct hx509_context_data {
+ struct hx509_keyset_ops **ks_ops;
+ int ks_num_ops;
+ int flags;
+#define HX509_CTX_VERIFY_MISSING_OK 1
+ int ocsp_time_diff;
+#define HX509_DEFAULT_OCSP_TIME_DIFF (5*60)
+ hx509_error error;
+ struct et_list *et_list;
+ char *querystat;
+ hx509_certs default_trust_anchors;
+};
+
+/* _hx509_calculate_path flag field */
+#define HX509_CALCULATE_PATH_NO_ANCHOR 1
+
+/* environment */
+struct hx509_env_data {
+ enum { env_string, env_list } type;
+ char *name;
+ struct hx509_env_data *next;
+ union {
+ char *string;
+ struct hx509_env_data *list;
+ } u;
+};
+
+
+extern const AlgorithmIdentifier * _hx509_crypto_default_sig_alg;
+extern const AlgorithmIdentifier * _hx509_crypto_default_digest_alg;
+extern const AlgorithmIdentifier * _hx509_crypto_default_secret_alg;
+
+/*
+ * Configurable options
+ */
+
+#ifdef __APPLE__
+#define HX509_DEFAULT_ANCHORS "KEYCHAIN:system-anchors"
+#endif
diff --git a/source4/heimdal/lib/hx509/keyset.c b/source4/heimdal/lib/hx509/keyset.c
new file mode 100644
index 0000000000..b68064b512
--- /dev/null
+++ b/source4/heimdal/lib/hx509/keyset.c
@@ -0,0 +1,677 @@
+/*
+ * Copyright (c) 2004 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hx_locl.h"
+RCSID("$Id$");
+
+/**
+ * @page page_keyset Certificate store operations
+ *
+ * Type of certificates store:
+ * - MEMORY
+ * In memory based format. Doesnt support storing.
+ * - FILE
+ * FILE supports raw DER certicates and PEM certicates. When PEM is
+ * used the file can contain may certificates and match private
+ * keys. Support storing the certificates. DER format only supports
+ * on certificate and no private key.
+ * - PEM-FILE
+ * Same as FILE, defaulting to PEM encoded certificates.
+ * - PEM-FILE
+ * Same as FILE, defaulting to DER encoded certificates.
+ * - PKCS11
+ * - PKCS12
+ * - DIR
+ * - KEYCHAIN
+ * Apple Mac OS X KeyChain backed keychain object.
+ *
+ * See the library functions here: @ref hx509_keyset
+ */
+
+struct hx509_certs_data {
+ unsigned int ref;
+ struct hx509_keyset_ops *ops;
+ void *ops_data;
+};
+
+static struct hx509_keyset_ops *
+_hx509_ks_type(hx509_context context, const char *type)
+{
+ int i;
+
+ for (i = 0; i < context->ks_num_ops; i++)
+ if (strcasecmp(type, context->ks_ops[i]->name) == 0)
+ return context->ks_ops[i];
+
+ return NULL;
+}
+
+void
+_hx509_ks_register(hx509_context context, struct hx509_keyset_ops *ops)
+{
+ struct hx509_keyset_ops **val;
+
+ if (_hx509_ks_type(context, ops->name))
+ return;
+
+ val = realloc(context->ks_ops,
+ (context->ks_num_ops + 1) * sizeof(context->ks_ops[0]));
+ if (val == NULL)
+ return;
+ val[context->ks_num_ops] = ops;
+ context->ks_ops = val;
+ context->ks_num_ops++;
+}
+
+/**
+ * Open or creates a new hx509 certificate store.
+ *
+ * @param context A hx509 context
+ * @param name name of the store, format is TYPE:type-specific-string,
+ * if NULL is used the MEMORY store is used.
+ * @param flags list of flags:
+ * - HX509_CERTS_CREATE create a new keystore of the specific TYPE.
+ * - HX509_CERTS_UNPROTECT_ALL fails if any private key failed to be extracted.
+ * @param lock a lock that unlocks the certificates store, use NULL to
+ * select no password/certifictes/prompt lock (see @ref page_lock).
+ * @param certs return pointer, free with hx509_certs_free().
+ *
+ * @ingroup hx509_keyset
+ */
+
+int
+hx509_certs_init(hx509_context context,
+ const char *name, int flags,
+ hx509_lock lock, hx509_certs *certs)
+{
+ struct hx509_keyset_ops *ops;
+ const char *residue;
+ hx509_certs c;
+ char *type;
+ int ret;
+
+ *certs = NULL;
+
+ residue = strchr(name, ':');
+ if (residue) {
+ type = malloc(residue - name + 1);
+ if (type)
+ strlcpy(type, name, residue - name + 1);
+ residue++;
+ if (residue[0] == '\0')
+ residue = NULL;
+ } else {
+ type = strdup("MEMORY");
+ residue = name;
+ }
+ if (type == NULL) {
+ hx509_clear_error_string(context);
+ return ENOMEM;
+ }
+
+ ops = _hx509_ks_type(context, type);
+ if (ops == NULL) {
+ hx509_set_error_string(context, 0, ENOENT,
+ "Keyset type %s is not supported", type);
+ free(type);
+ return ENOENT;
+ }
+ free(type);
+ c = calloc(1, sizeof(*c));
+ if (c == NULL) {
+ hx509_clear_error_string(context);
+ return ENOMEM;
+ }
+ c->ops = ops;
+ c->ref = 1;
+
+ ret = (*ops->init)(context, c, &c->ops_data, flags, residue, lock);
+ if (ret) {
+ free(c);
+ return ret;
+ }
+
+ *certs = c;
+ return 0;
+}
+
+/**
+ * Write the certificate store to stable storage.
+ *
+ * @param context A hx509 context.
+ * @param certs a certificate store to store.
+ * @param flags currently unused, use 0.
+ * @param lock a lock that unlocks the certificates store, use NULL to
+ * select no password/certifictes/prompt lock (see @ref page_lock).
+ *
+ * @return Returns an hx509 error code. HX509_UNSUPPORTED_OPERATION if
+ * the certificate store doesn't support the store operation.
+ *
+ * @ingroup hx509_keyset
+ */
+
+int
+hx509_certs_store(hx509_context context,
+ hx509_certs certs,
+ int flags,
+ hx509_lock lock)
+{
+ if (certs->ops->store == NULL) {
+ hx509_set_error_string(context, 0, HX509_UNSUPPORTED_OPERATION,
+ "keystore if type %s doesn't support "
+ "store operation",
+ certs->ops->name);
+ return HX509_UNSUPPORTED_OPERATION;
+ }
+
+ return (*certs->ops->store)(context, certs, certs->ops_data, flags, lock);
+}
+
+
+hx509_certs
+_hx509_certs_ref(hx509_certs certs)
+{
+ if (certs == NULL)
+ return NULL;
+ if (certs->ref == 0)
+ _hx509_abort("certs refcount == 0 on ref");
+ if (certs->ref == UINT_MAX)
+ _hx509_abort("certs refcount == UINT_MAX on ref");
+ certs->ref++;
+ return certs;
+}
+
+/**
+ * Free a certificate store.
+ *
+ * @param certs certificate store to free.
+ *
+ * @ingroup hx509_keyset
+ */
+
+void
+hx509_certs_free(hx509_certs *certs)
+{
+ if (*certs) {
+ if ((*certs)->ref == 0)
+ _hx509_abort("cert refcount == 0 on free");
+ if (--(*certs)->ref > 0)
+ return;
+
+ (*(*certs)->ops->free)(*certs, (*certs)->ops_data);
+ free(*certs);
+ *certs = NULL;
+ }
+}
+
+/**
+ * Start the integration
+ *
+ * @param context a hx509 context.
+ * @param certs certificate store to iterate over
+ * @param cursor cursor that will keep track of progress, free with
+ * hx509_certs_end_seq().
+ *
+ * @return Returns an hx509 error code. HX509_UNSUPPORTED_OPERATION is
+ * returned if the certificate store doesn't support the iteration
+ * operation.
+ *
+ * @ingroup hx509_keyset
+ */
+
+int
+hx509_certs_start_seq(hx509_context context,
+ hx509_certs certs,
+ hx509_cursor *cursor)
+{
+ int ret;
+
+ if (certs->ops->iter_start == NULL) {
+ hx509_set_error_string(context, 0, HX509_UNSUPPORTED_OPERATION,
+ "Keyset type %s doesn't support iteration",
+ certs->ops->name);
+ return HX509_UNSUPPORTED_OPERATION;
+ }
+
+ ret = (*certs->ops->iter_start)(context, certs, certs->ops_data, cursor);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+/**
+ * Get next ceritificate from the certificate keystore pointed out by
+ * cursor.
+ *
+ * @param context a hx509 context.
+ * @param certs certificate store to iterate over.
+ * @param cursor cursor that keeps track of progress.
+ * @param cert return certificate next in store, NULL if the store
+ * contains no more certificates. Free with hx509_cert_free().
+ *
+ * @return Returns an hx509 error code.
+ *
+ * @ingroup hx509_keyset
+ */
+
+int
+hx509_certs_next_cert(hx509_context context,
+ hx509_certs certs,
+ hx509_cursor cursor,
+ hx509_cert *cert)
+{
+ *cert = NULL;
+ return (*certs->ops->iter)(context, certs, certs->ops_data, cursor, cert);
+}
+
+/**
+ * End the iteration over certificates.
+ *
+ * @param context a hx509 context.
+ * @param certs certificate store to iterate over.
+ * @param cursor cursor that will keep track of progress, freed.
+ *
+ * @return Returns an hx509 error code.
+ *
+ * @ingroup hx509_keyset
+ */
+
+int
+hx509_certs_end_seq(hx509_context context,
+ hx509_certs certs,
+ hx509_cursor cursor)
+{
+ (*certs->ops->iter_end)(context, certs, certs->ops_data, cursor);
+ return 0;
+}
+
+/**
+ * Iterate over all certificates in a keystore and call an function
+ * for each fo them.
+ *
+ * @param context a hx509 context.
+ * @param certs certificate store to iterate over.
+ * @param func function to call for each certificate. The function
+ * should return non-zero to abort the iteration, that value is passed
+ * back to te caller of hx509_certs_iter().
+ * @param ctx context variable that will passed to the function.
+ *
+ * @return Returns an hx509 error code.
+ *
+ * @ingroup hx509_keyset
+ */
+
+int
+hx509_certs_iter(hx509_context context,
+ hx509_certs certs,
+ int (*func)(hx509_context, void *, hx509_cert),
+ void *ctx)
+{
+ hx509_cursor cursor;
+ hx509_cert c;
+ int ret;
+
+ ret = hx509_certs_start_seq(context, certs, &cursor);
+ if (ret)
+ return ret;
+
+ while (1) {
+ ret = hx509_certs_next_cert(context, certs, cursor, &c);
+ if (ret)
+ break;
+ if (c == NULL) {
+ ret = 0;
+ break;
+ }
+ ret = (*func)(context, ctx, c);
+ hx509_cert_free(c);
+ if (ret)
+ break;
+ }
+
+ hx509_certs_end_seq(context, certs, cursor);
+
+ return ret;
+}
+
+
+/**
+ * Function to use to hx509_certs_iter() as a function argument, the
+ * ctx variable to hx509_certs_iter() should be a FILE file descriptor.
+ *
+ * @param context a hx509 context.
+ * @param ctx used by hx509_certs_iter().
+ * @param c a certificate
+ *
+ * @return Returns an hx509 error code.
+ *
+ * @ingroup hx509_keyset
+ */
+
+int
+hx509_ci_print_names(hx509_context context, void *ctx, hx509_cert c)
+{
+ Certificate *cert;
+ hx509_name n;
+ char *s, *i;
+
+ cert = _hx509_get_cert(c);
+
+ _hx509_name_from_Name(&cert->tbsCertificate.subject, &n);
+ hx509_name_to_string(n, &s);
+ hx509_name_free(&n);
+ _hx509_name_from_Name(&cert->tbsCertificate.issuer, &n);
+ hx509_name_to_string(n, &i);
+ hx509_name_free(&n);
+ fprintf(ctx, "subject: %s\nissuer: %s\n", s, i);
+ free(s);
+ free(i);
+ return 0;
+}
+
+/**
+ * Add a certificate to the certificiate store.
+ *
+ * The receiving keyset certs will either increase reference counter
+ * of the cert or make a deep copy, either way, the caller needs to
+ * free the cert itself.
+ *
+ * @param context a hx509 context.
+ * @param certs certificate store to add the certificate to.
+ * @param cert certificate to add.
+ *
+ * @return Returns an hx509 error code.
+ *
+ * @ingroup hx509_keyset
+ */
+
+int
+hx509_certs_add(hx509_context context, hx509_certs certs, hx509_cert cert)
+{
+ if (certs->ops->add == NULL) {
+ hx509_set_error_string(context, 0, ENOENT,
+ "Keyset type %s doesn't support add operation",
+ certs->ops->name);
+ return ENOENT;
+ }
+
+ return (*certs->ops->add)(context, certs, certs->ops_data, cert);
+}
+
+/**
+ * Find a certificate matching the query.
+ *
+ * @param context a hx509 context.
+ * @param certs certificate store to search.
+ * @param q query allocated with @ref hx509_query functions.
+ * @param r return certificate (or NULL on error), should be freed
+ * with hx509_cert_free().
+ *
+ * @return Returns an hx509 error code.
+ *
+ * @ingroup hx509_keyset
+ */
+
+int
+hx509_certs_find(hx509_context context,
+ hx509_certs certs,
+ const hx509_query *q,
+ hx509_cert *r)
+{
+ hx509_cursor cursor;
+ hx509_cert c;
+ int ret;
+
+ *r = NULL;
+
+ _hx509_query_statistic(context, 0, q);
+
+ if (certs->ops->query)
+ return (*certs->ops->query)(context, certs, certs->ops_data, q, r);
+
+ ret = hx509_certs_start_seq(context, certs, &cursor);
+ if (ret)
+ return ret;
+
+ c = NULL;
+ while (1) {
+ ret = hx509_certs_next_cert(context, certs, cursor, &c);
+ if (ret)
+ break;
+ if (c == NULL)
+ break;
+ if (_hx509_query_match_cert(context, q, c)) {
+ *r = c;
+ break;
+ }
+ hx509_cert_free(c);
+ }
+
+ hx509_certs_end_seq(context, certs, cursor);
+ if (ret)
+ return ret;
+ if (c == NULL) {
+ hx509_clear_error_string(context);
+ return HX509_CERT_NOT_FOUND;
+ }
+
+ return 0;
+}
+
+static int
+certs_merge_func(hx509_context context, void *ctx, hx509_cert c)
+{
+ return hx509_certs_add(context, (hx509_certs)ctx, c);
+}
+
+/**
+ * Merge a certificate store into another. The from store is keep
+ * intact.
+ *
+ * @param context a hx509 context.
+ * @param to the store to merge into.
+ * @param from the store to copy the object from.
+ *
+ * @return Returns an hx509 error code.
+ *
+ * @ingroup hx509_keyset
+ */
+
+int
+hx509_certs_merge(hx509_context context, hx509_certs to, hx509_certs from)
+{
+ if (from == NULL)
+ return 0;
+ return hx509_certs_iter(context, from, certs_merge_func, to);
+}
+
+/**
+ * Same a hx509_certs_merge() but use a lock and name to describe the
+ * from source.
+ *
+ * @param context a hx509 context.
+ * @param to the store to merge into.
+ * @param lock a lock that unlocks the certificates store, use NULL to
+ * select no password/certifictes/prompt lock (see @ref page_lock).
+ * @param name name of the source store
+ *
+ * @return Returns an hx509 error code.
+ *
+ * @ingroup hx509_keyset
+ */
+
+int
+hx509_certs_append(hx509_context context,
+ hx509_certs to,
+ hx509_lock lock,
+ const char *name)
+{
+ hx509_certs s;
+ int ret;
+
+ ret = hx509_certs_init(context, name, 0, lock, &s);
+ if (ret)
+ return ret;
+ ret = hx509_certs_merge(context, to, s);
+ hx509_certs_free(&s);
+ return ret;
+}
+
+/**
+ * Get one random certificate from the certificate store.
+ *
+ * @param context a hx509 context.
+ * @param certs a certificate store to get the certificate from.
+ * @param c return certificate, should be freed with hx509_cert_free().
+ *
+ * @return Returns an hx509 error code.
+ *
+ * @ingroup hx509_keyset
+ */
+
+int
+hx509_get_one_cert(hx509_context context, hx509_certs certs, hx509_cert *c)
+{
+ hx509_cursor cursor;
+ int ret;
+
+ *c = NULL;
+
+ ret = hx509_certs_start_seq(context, certs, &cursor);
+ if (ret)
+ return ret;
+
+ ret = hx509_certs_next_cert(context, certs, cursor, c);
+ if (ret)
+ return ret;
+
+ hx509_certs_end_seq(context, certs, cursor);
+ return 0;
+}
+
+static int
+certs_info_stdio(void *ctx, const char *str)
+{
+ FILE *f = ctx;
+ fprintf(f, "%s\n", str);
+ return 0;
+}
+
+/**
+ * Print some info about the certificate store.
+ *
+ * @param context a hx509 context.
+ * @param certs certificate store to print information about.
+ * @param func function that will get each line of the information, if
+ * NULL is used the data is printed on a FILE descriptor that should
+ * be passed in ctx, if ctx also is NULL, stdout is used.
+ * @param ctx parameter to func.
+ *
+ * @return Returns an hx509 error code.
+ *
+ * @ingroup hx509_keyset
+ */
+
+int
+hx509_certs_info(hx509_context context,
+ hx509_certs certs,
+ int (*func)(void *, const char *),
+ void *ctx)
+{
+ if (func == NULL) {
+ func = certs_info_stdio;
+ if (ctx == NULL)
+ ctx = stdout;
+ }
+ if (certs->ops->printinfo == NULL) {
+ (*func)(ctx, "No info function for certs");
+ return 0;
+ }
+ return (*certs->ops->printinfo)(context, certs, certs->ops_data,
+ func, ctx);
+}
+
+void
+_hx509_pi_printf(int (*func)(void *, const char *), void *ctx,
+ const char *fmt, ...)
+{
+ va_list ap;
+ char *str;
+
+ va_start(ap, fmt);
+ vasprintf(&str, fmt, ap);
+ va_end(ap);
+ if (str == NULL)
+ return;
+ (*func)(ctx, str);
+ free(str);
+}
+
+int
+_hx509_certs_keys_get(hx509_context context,
+ hx509_certs certs,
+ hx509_private_key **keys)
+{
+ if (certs->ops->getkeys == NULL) {
+ *keys = NULL;
+ return 0;
+ }
+ return (*certs->ops->getkeys)(context, certs, certs->ops_data, keys);
+}
+
+int
+_hx509_certs_keys_add(hx509_context context,
+ hx509_certs certs,
+ hx509_private_key key)
+{
+ if (certs->ops->addkey == NULL) {
+ hx509_set_error_string(context, 0, EINVAL,
+ "keystore if type %s doesn't support "
+ "key add operation",
+ certs->ops->name);
+ return EINVAL;
+ }
+ return (*certs->ops->addkey)(context, certs, certs->ops_data, key);
+}
+
+
+void
+_hx509_certs_keys_free(hx509_context context,
+ hx509_private_key *keys)
+{
+ int i;
+ for (i = 0; keys[i]; i++)
+ _hx509_private_key_free(&keys[i]);
+ free(keys);
+}
diff --git a/source4/heimdal/lib/hx509/ks_dir.c b/source4/heimdal/lib/hx509/ks_dir.c
new file mode 100644
index 0000000000..76c0c42633
--- /dev/null
+++ b/source4/heimdal/lib/hx509/ks_dir.c
@@ -0,0 +1,224 @@
+/*
+ * Copyright (c) 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hx_locl.h"
+RCSID("$Id$");
+#include <dirent.h>
+
+/*
+ * The DIR keyset module is strange compared to the other modules
+ * since it does lazy evaluation and really doesn't keep any local
+ * state except for the directory iteration and cert iteration of
+ * files. DIR ignores most errors so that the consumer doesn't get
+ * failes for stray files in directories.
+ */
+
+struct dircursor {
+ DIR *dir;
+ hx509_certs certs;
+ void *iter;
+};
+
+/*
+ *
+ */
+
+static int
+dir_init(hx509_context context,
+ hx509_certs certs, void **data, int flags,
+ const char *residue, hx509_lock lock)
+{
+ *data = NULL;
+
+ {
+ struct stat sb;
+ int ret;
+
+ ret = stat(residue, &sb);
+ if (ret == -1) {
+ hx509_set_error_string(context, 0, ENOENT,
+ "No such file %s", residue);
+ return ENOENT;
+ }
+
+ if ((sb.st_mode & S_IFDIR) == 0) {
+ hx509_set_error_string(context, 0, ENOTDIR,
+ "%s is not a directory", residue);
+ return ENOTDIR;
+ }
+ }
+
+ *data = strdup(residue);
+ if (*data == NULL) {
+ hx509_clear_error_string(context);
+ return ENOMEM;
+ }
+
+ return 0;
+}
+
+static int
+dir_free(hx509_certs certs, void *data)
+{
+ free(data);
+ return 0;
+}
+
+
+
+static int
+dir_iter_start(hx509_context context,
+ hx509_certs certs, void *data, void **cursor)
+{
+ struct dircursor *d;
+
+ *cursor = NULL;
+
+ d = calloc(1, sizeof(*d));
+ if (d == NULL) {
+ hx509_clear_error_string(context);
+ return ENOMEM;
+ }
+
+ d->dir = opendir(data);
+ if (d->dir == NULL) {
+ hx509_clear_error_string(context);
+ free(d);
+ return errno;
+ }
+ rk_cloexec(dirfd(d->dir));
+ d->certs = NULL;
+ d->iter = NULL;
+
+ *cursor = d;
+ return 0;
+}
+
+static int
+dir_iter(hx509_context context,
+ hx509_certs certs, void *data, void *iter, hx509_cert *cert)
+{
+ struct dircursor *d = iter;
+ int ret = 0;
+
+ *cert = NULL;
+
+ do {
+ struct dirent *dir;
+ char *fn;
+
+ if (d->certs) {
+ ret = hx509_certs_next_cert(context, d->certs, d->iter, cert);
+ if (ret) {
+ hx509_certs_end_seq(context, d->certs, d->iter);
+ d->iter = NULL;
+ hx509_certs_free(&d->certs);
+ return ret;
+ }
+ if (*cert) {
+ ret = 0;
+ break;
+ }
+ hx509_certs_end_seq(context, d->certs, d->iter);
+ d->iter = NULL;
+ hx509_certs_free(&d->certs);
+ }
+
+ dir = readdir(d->dir);
+ if (dir == NULL) {
+ ret = 0;
+ break;
+ }
+ if (strcmp(dir->d_name, ".") == 0 || strcmp(dir->d_name, "..") == 0)
+ continue;
+
+ if (asprintf(&fn, "FILE:%s/%s", (char *)data, dir->d_name) == -1)
+ return ENOMEM;
+
+ ret = hx509_certs_init(context, fn, 0, NULL, &d->certs);
+ if (ret == 0) {
+
+ ret = hx509_certs_start_seq(context, d->certs, &d->iter);
+ if (ret)
+ hx509_certs_free(&d->certs);
+ }
+ /* ignore errors */
+ if (ret) {
+ d->certs = NULL;
+ ret = 0;
+ }
+
+ free(fn);
+ } while(ret == 0);
+
+ return ret;
+}
+
+
+static int
+dir_iter_end(hx509_context context,
+ hx509_certs certs,
+ void *data,
+ void *cursor)
+{
+ struct dircursor *d = cursor;
+
+ if (d->certs) {
+ hx509_certs_end_seq(context, d->certs, d->iter);
+ d->iter = NULL;
+ hx509_certs_free(&d->certs);
+ }
+ closedir(d->dir);
+ free(d);
+ return 0;
+}
+
+
+static struct hx509_keyset_ops keyset_dir = {
+ "DIR",
+ 0,
+ dir_init,
+ NULL,
+ dir_free,
+ NULL,
+ NULL,
+ dir_iter_start,
+ dir_iter,
+ dir_iter_end
+};
+
+void
+_hx509_ks_dir_register(hx509_context context)
+{
+ _hx509_ks_register(context, &keyset_dir);
+}
diff --git a/source4/heimdal/lib/hx509/ks_file.c b/source4/heimdal/lib/hx509/ks_file.c
new file mode 100644
index 0000000000..ca0171f8b9
--- /dev/null
+++ b/source4/heimdal/lib/hx509/ks_file.c
@@ -0,0 +1,645 @@
+/*
+ * Copyright (c) 2005 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hx_locl.h"
+RCSID("$Id$");
+
+typedef enum { USE_PEM, USE_DER } outformat;
+
+struct ks_file {
+ hx509_certs certs;
+ char *fn;
+ outformat format;
+};
+
+/*
+ *
+ */
+
+static int
+parse_certificate(hx509_context context, const char *fn,
+ struct hx509_collector *c,
+ const hx509_pem_header *headers,
+ const void *data, size_t len)
+{
+ hx509_cert cert;
+ int ret;
+
+ ret = hx509_cert_init_data(context, data, len, &cert);
+ if (ret)
+ return ret;
+
+ ret = _hx509_collector_certs_add(context, c, cert);
+ hx509_cert_free(cert);
+ return ret;
+}
+
+static int
+try_decrypt(hx509_context context,
+ struct hx509_collector *collector,
+ const AlgorithmIdentifier *alg,
+ const EVP_CIPHER *c,
+ const void *ivdata,
+ const void *password,
+ size_t passwordlen,
+ const void *cipher,
+ size_t len)
+{
+ heim_octet_string clear;
+ size_t keylen;
+ void *key;
+ int ret;
+
+ keylen = EVP_CIPHER_key_length(c);
+
+ key = malloc(keylen);
+ if (key == NULL) {
+ hx509_clear_error_string(context);
+ return ENOMEM;
+ }
+
+ ret = EVP_BytesToKey(c, EVP_md5(), ivdata,
+ password, passwordlen,
+ 1, key, NULL);
+ if (ret <= 0) {
+ hx509_set_error_string(context, 0, HX509_CRYPTO_INTERNAL_ERROR,
+ "Failed to do string2key for private key");
+ return HX509_CRYPTO_INTERNAL_ERROR;
+ }
+
+ clear.data = malloc(len);
+ if (clear.data == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM,
+ "Out of memory to decrypt for private key");
+ ret = ENOMEM;
+ goto out;
+ }
+ clear.length = len;
+
+ {
+ EVP_CIPHER_CTX ctx;
+ EVP_CIPHER_CTX_init(&ctx);
+ EVP_CipherInit_ex(&ctx, c, NULL, key, ivdata, 0);
+ EVP_Cipher(&ctx, clear.data, cipher, len);
+ EVP_CIPHER_CTX_cleanup(&ctx);
+ }
+
+ ret = _hx509_collector_private_key_add(context,
+ collector,
+ alg,
+ NULL,
+ &clear,
+ NULL);
+
+ memset(clear.data, 0, clear.length);
+ free(clear.data);
+out:
+ memset(key, 0, keylen);
+ free(key);
+ return ret;
+}
+
+static int
+parse_rsa_private_key(hx509_context context, const char *fn,
+ struct hx509_collector *c,
+ const hx509_pem_header *headers,
+ const void *data, size_t len)
+{
+ int ret = 0;
+ const char *enc;
+
+ enc = hx509_pem_find_header(headers, "Proc-Type");
+ if (enc) {
+ const char *dek;
+ char *type, *iv;
+ ssize_t ssize, size;
+ void *ivdata;
+ const EVP_CIPHER *cipher;
+ const struct _hx509_password *pw;
+ hx509_lock lock;
+ int i, decrypted = 0;
+
+ lock = _hx509_collector_get_lock(c);
+ if (lock == NULL) {
+ hx509_set_error_string(context, 0, HX509_ALG_NOT_SUPP,
+ "Failed to get password for "
+ "password protected file %s", fn);
+ return HX509_ALG_NOT_SUPP;
+ }
+
+ if (strcmp(enc, "4,ENCRYPTED") != 0) {
+ hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,
+ "RSA key encrypted in unknown method %s "
+ "in file",
+ enc, fn);
+ hx509_clear_error_string(context);
+ return HX509_PARSING_KEY_FAILED;
+ }
+
+ dek = hx509_pem_find_header(headers, "DEK-Info");
+ if (dek == NULL) {
+ hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,
+ "Encrypted RSA missing DEK-Info");
+ return HX509_PARSING_KEY_FAILED;
+ }
+
+ type = strdup(dek);
+ if (type == NULL) {
+ hx509_clear_error_string(context);
+ return ENOMEM;
+ }
+
+ iv = strchr(type, ',');
+ if (iv == NULL) {
+ free(type);
+ hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,
+ "IV missing");
+ return HX509_PARSING_KEY_FAILED;
+ }
+
+ *iv++ = '\0';
+
+ size = strlen(iv);
+ ivdata = malloc(size);
+ if (ivdata == NULL) {
+ hx509_clear_error_string(context);
+ free(type);
+ return ENOMEM;
+ }
+
+ cipher = EVP_get_cipherbyname(type);
+ if (cipher == NULL) {
+ free(ivdata);
+ hx509_set_error_string(context, 0, HX509_ALG_NOT_SUPP,
+ "RSA key encrypted with "
+ "unsupported cipher: %s",
+ type);
+ free(type);
+ return HX509_ALG_NOT_SUPP;
+ }
+
+#define PKCS5_SALT_LEN 8
+
+ ssize = hex_decode(iv, ivdata, size);
+ free(type);
+ type = NULL;
+ iv = NULL;
+
+ if (ssize < 0 || ssize < PKCS5_SALT_LEN || ssize < EVP_CIPHER_iv_length(cipher)) {
+ free(ivdata);
+ hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,
+ "Salt have wrong length in RSA key file");
+ return HX509_PARSING_KEY_FAILED;
+ }
+
+ pw = _hx509_lock_get_passwords(lock);
+ if (pw != NULL) {
+ const void *password;
+ size_t passwordlen;
+
+ for (i = 0; i < pw->len; i++) {
+ password = pw->val[i];
+ passwordlen = strlen(password);
+
+ ret = try_decrypt(context, c, hx509_signature_rsa(),
+ cipher, ivdata, password, passwordlen,
+ data, len);
+ if (ret == 0) {
+ decrypted = 1;
+ break;
+ }
+ }
+ }
+ if (!decrypted) {
+ hx509_prompt prompt;
+ char password[128];
+
+ memset(&prompt, 0, sizeof(prompt));
+
+ prompt.prompt = "Password for keyfile: ";
+ prompt.type = HX509_PROMPT_TYPE_PASSWORD;
+ prompt.reply.data = password;
+ prompt.reply.length = sizeof(password);
+
+ ret = hx509_lock_prompt(lock, &prompt);
+ if (ret == 0)
+ ret = try_decrypt(context, c, hx509_signature_rsa(),
+ cipher, ivdata, password, strlen(password),
+ data, len);
+ /* XXX add password to lock password collection ? */
+ memset(password, 0, sizeof(password));
+ }
+ free(ivdata);
+
+ } else {
+ heim_octet_string keydata;
+
+ keydata.data = rk_UNCONST(data);
+ keydata.length = len;
+
+ ret = _hx509_collector_private_key_add(context,
+ c,
+ hx509_signature_rsa(),
+ NULL,
+ &keydata,
+ NULL);
+ }
+
+ return ret;
+}
+
+
+struct pem_formats {
+ const char *name;
+ int (*func)(hx509_context, const char *, struct hx509_collector *,
+ const hx509_pem_header *, const void *, size_t);
+} formats[] = {
+ { "CERTIFICATE", parse_certificate },
+ { "RSA PRIVATE KEY", parse_rsa_private_key }
+};
+
+
+struct pem_ctx {
+ int flags;
+ struct hx509_collector *c;
+};
+
+static int
+pem_func(hx509_context context, const char *type,
+ const hx509_pem_header *header,
+ const void *data, size_t len, void *ctx)
+{
+ struct pem_ctx *pem_ctx = (struct pem_ctx*)ctx;
+ int ret = 0, j;
+
+ for (j = 0; j < sizeof(formats)/sizeof(formats[0]); j++) {
+ const char *q = formats[j].name;
+ if (strcasecmp(type, q) == 0) {
+ ret = (*formats[j].func)(context, NULL, pem_ctx->c, header, data, len);
+ if (ret == 0)
+ break;
+ }
+ }
+ if (j == sizeof(formats)/sizeof(formats[0])) {
+ ret = HX509_UNSUPPORTED_OPERATION;
+ hx509_set_error_string(context, 0, ret,
+ "Found no matching PEM format for %s", type);
+ return ret;
+ }
+ if (ret && (pem_ctx->flags & HX509_CERTS_UNPROTECT_ALL))
+ return ret;
+ return 0;
+}
+
+/*
+ *
+ */
+
+static int
+file_init_common(hx509_context context,
+ hx509_certs certs, void **data, int flags,
+ const char *residue, hx509_lock lock, outformat format)
+{
+ char *p, *pnext;
+ struct ks_file *f = NULL;
+ hx509_private_key *keys = NULL;
+ int ret;
+ struct pem_ctx pem_ctx;
+
+ pem_ctx.flags = flags;
+ pem_ctx.c = NULL;
+
+ *data = NULL;
+
+ if (lock == NULL)
+ lock = _hx509_empty_lock;
+
+ f = calloc(1, sizeof(*f));
+ if (f == NULL) {
+ hx509_clear_error_string(context);
+ return ENOMEM;
+ }
+ f->format = format;
+
+ f->fn = strdup(residue);
+ if (f->fn == NULL) {
+ hx509_clear_error_string(context);
+ ret = ENOMEM;
+ goto out;
+ }
+
+ /*
+ * XXX this is broken, the function should parse the file before
+ * overwriting it
+ */
+
+ if (flags & HX509_CERTS_CREATE) {
+ ret = hx509_certs_init(context, "MEMORY:ks-file-create",
+ 0, lock, &f->certs);
+ if (ret)
+ goto out;
+ *data = f;
+ return 0;
+ }
+
+ ret = _hx509_collector_alloc(context, lock, &pem_ctx.c);
+ if (ret)
+ goto out;
+
+ for (p = f->fn; p != NULL; p = pnext) {
+ FILE *f;
+
+ pnext = strchr(p, ',');
+ if (pnext)
+ *pnext++ = '\0';
+
+
+ if ((f = fopen(p, "r")) == NULL) {
+ ret = ENOENT;
+ hx509_set_error_string(context, 0, ret,
+ "Failed to open PEM file \"%s\": %s",
+ p, strerror(errno));
+ goto out;
+ }
+ rk_cloexec_file(f);
+
+ ret = hx509_pem_read(context, f, pem_func, &pem_ctx);
+ fclose(f);
+ if (ret != 0 && ret != HX509_PARSING_KEY_FAILED)
+ goto out;
+ else if (ret == HX509_PARSING_KEY_FAILED) {
+ size_t length;
+ void *ptr;
+ int i;
+
+ ret = rk_undumpdata(p, &ptr, &length);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+
+ for (i = 0; i < sizeof(formats)/sizeof(formats[0]); i++) {
+ ret = (*formats[i].func)(context, p, pem_ctx.c, NULL, ptr, length);
+ if (ret == 0)
+ break;
+ }
+ rk_xfree(ptr);
+ if (ret)
+ goto out;
+ }
+ }
+
+ ret = _hx509_collector_collect_certs(context, pem_ctx.c, &f->certs);
+ if (ret)
+ goto out;
+
+ ret = _hx509_collector_collect_private_keys(context, pem_ctx.c, &keys);
+ if (ret == 0) {
+ int i;
+
+ for (i = 0; keys[i]; i++)
+ _hx509_certs_keys_add(context, f->certs, keys[i]);
+ _hx509_certs_keys_free(context, keys);
+ }
+
+out:
+ if (ret == 0)
+ *data = f;
+ else {
+ if (f->fn)
+ free(f->fn);
+ free(f);
+ }
+ if (pem_ctx.c)
+ _hx509_collector_free(pem_ctx.c);
+
+ return ret;
+}
+
+static int
+file_init_pem(hx509_context context,
+ hx509_certs certs, void **data, int flags,
+ const char *residue, hx509_lock lock)
+{
+ return file_init_common(context, certs, data, flags, residue, lock, USE_PEM);
+}
+
+static int
+file_init_der(hx509_context context,
+ hx509_certs certs, void **data, int flags,
+ const char *residue, hx509_lock lock)
+{
+ return file_init_common(context, certs, data, flags, residue, lock, USE_DER);
+}
+
+static int
+file_free(hx509_certs certs, void *data)
+{
+ struct ks_file *f = data;
+ hx509_certs_free(&f->certs);
+ free(f->fn);
+ free(f);
+ return 0;
+}
+
+struct store_ctx {
+ FILE *f;
+ outformat format;
+};
+
+static int
+store_func(hx509_context context, void *ctx, hx509_cert c)
+{
+ struct store_ctx *sc = ctx;
+ heim_octet_string data;
+ int ret;
+
+ ret = hx509_cert_binary(context, c, &data);
+ if (ret)
+ return ret;
+
+ switch (sc->format) {
+ case USE_DER:
+ fwrite(data.data, data.length, 1, sc->f);
+ free(data.data);
+ break;
+ case USE_PEM:
+ hx509_pem_write(context, "CERTIFICATE", NULL, sc->f,
+ data.data, data.length);
+ free(data.data);
+ if (_hx509_cert_private_key_exportable(c)) {
+ hx509_private_key key = _hx509_cert_private_key(c);
+ ret = _hx509_private_key_export(context, key, &data);
+ if (ret)
+ break;
+ hx509_pem_write(context, _hx509_private_pem_name(key), NULL, sc->f,
+ data.data, data.length);
+ free(data.data);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static int
+file_store(hx509_context context,
+ hx509_certs certs, void *data, int flags, hx509_lock lock)
+{
+ struct ks_file *f = data;
+ struct store_ctx sc;
+ int ret;
+
+ sc.f = fopen(f->fn, "w");
+ if (sc.f == NULL) {
+ hx509_set_error_string(context, 0, ENOENT,
+ "Failed to open file %s for writing");
+ return ENOENT;
+ }
+ rk_cloexec_file(sc.f);
+ sc.format = f->format;
+
+ ret = hx509_certs_iter(context, f->certs, store_func, &sc);
+ fclose(sc.f);
+ return ret;
+}
+
+static int
+file_add(hx509_context context, hx509_certs certs, void *data, hx509_cert c)
+{
+ struct ks_file *f = data;
+ return hx509_certs_add(context, f->certs, c);
+}
+
+static int
+file_iter_start(hx509_context context,
+ hx509_certs certs, void *data, void **cursor)
+{
+ struct ks_file *f = data;
+ return hx509_certs_start_seq(context, f->certs, cursor);
+}
+
+static int
+file_iter(hx509_context context,
+ hx509_certs certs, void *data, void *iter, hx509_cert *cert)
+{
+ struct ks_file *f = data;
+ return hx509_certs_next_cert(context, f->certs, iter, cert);
+}
+
+static int
+file_iter_end(hx509_context context,
+ hx509_certs certs,
+ void *data,
+ void *cursor)
+{
+ struct ks_file *f = data;
+ return hx509_certs_end_seq(context, f->certs, cursor);
+}
+
+static int
+file_getkeys(hx509_context context,
+ hx509_certs certs,
+ void *data,
+ hx509_private_key **keys)
+{
+ struct ks_file *f = data;
+ return _hx509_certs_keys_get(context, f->certs, keys);
+}
+
+static int
+file_addkey(hx509_context context,
+ hx509_certs certs,
+ void *data,
+ hx509_private_key key)
+{
+ struct ks_file *f = data;
+ return _hx509_certs_keys_add(context, f->certs, key);
+}
+
+static struct hx509_keyset_ops keyset_file = {
+ "FILE",
+ 0,
+ file_init_pem,
+ file_store,
+ file_free,
+ file_add,
+ NULL,
+ file_iter_start,
+ file_iter,
+ file_iter_end,
+ NULL,
+ file_getkeys,
+ file_addkey
+};
+
+static struct hx509_keyset_ops keyset_pemfile = {
+ "PEM-FILE",
+ 0,
+ file_init_pem,
+ file_store,
+ file_free,
+ file_add,
+ NULL,
+ file_iter_start,
+ file_iter,
+ file_iter_end,
+ NULL,
+ file_getkeys,
+ file_addkey
+};
+
+static struct hx509_keyset_ops keyset_derfile = {
+ "DER-FILE",
+ 0,
+ file_init_der,
+ file_store,
+ file_free,
+ file_add,
+ NULL,
+ file_iter_start,
+ file_iter,
+ file_iter_end,
+ NULL,
+ file_getkeys,
+ file_addkey
+};
+
+
+void
+_hx509_ks_file_register(hx509_context context)
+{
+ _hx509_ks_register(context, &keyset_file);
+ _hx509_ks_register(context, &keyset_pemfile);
+ _hx509_ks_register(context, &keyset_derfile);
+}
diff --git a/source4/heimdal/lib/hx509/ks_keychain.c b/source4/heimdal/lib/hx509/ks_keychain.c
new file mode 100644
index 0000000000..2dc0721563
--- /dev/null
+++ b/source4/heimdal/lib/hx509/ks_keychain.c
@@ -0,0 +1,548 @@
+/*
+ * Copyright (c) 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hx_locl.h"
+RCSID("$Id$");
+
+#ifdef HAVE_FRAMEWORK_SECURITY
+
+#include <Security/Security.h>
+
+/* Missing function decls in pre Leopard */
+#ifdef NEED_SECKEYGETCSPHANDLE_PROTO
+OSStatus SecKeyGetCSPHandle(SecKeyRef, CSSM_CSP_HANDLE *);
+OSStatus SecKeyGetCredentials(SecKeyRef, CSSM_ACL_AUTHORIZATION_TAG,
+ int, const CSSM_ACCESS_CREDENTIALS **);
+#define kSecCredentialTypeDefault 0
+#endif
+
+
+static int
+getAttribute(SecKeychainItemRef itemRef, SecItemAttr item,
+ SecKeychainAttributeList **attrs)
+{
+ SecKeychainAttributeInfo attrInfo;
+ UInt32 attrFormat = 0;
+ OSStatus ret;
+
+ *attrs = NULL;
+
+ attrInfo.count = 1;
+ attrInfo.tag = &item;
+ attrInfo.format = &attrFormat;
+
+ ret = SecKeychainItemCopyAttributesAndData(itemRef, &attrInfo, NULL,
+ attrs, NULL, NULL);
+ if (ret)
+ return EINVAL;
+ return 0;
+}
+
+
+/*
+ *
+ */
+
+struct kc_rsa {
+ SecKeychainItemRef item;
+ size_t keysize;
+};
+
+
+static int
+kc_rsa_public_encrypt(int flen,
+ const unsigned char *from,
+ unsigned char *to,
+ RSA *rsa,
+ int padding)
+{
+ return -1;
+}
+
+static int
+kc_rsa_public_decrypt(int flen,
+ const unsigned char *from,
+ unsigned char *to,
+ RSA *rsa,
+ int padding)
+{
+ return -1;
+}
+
+
+static int
+kc_rsa_private_encrypt(int flen,
+ const unsigned char *from,
+ unsigned char *to,
+ RSA *rsa,
+ int padding)
+{
+ struct kc_rsa *kc = RSA_get_app_data(rsa);
+
+ CSSM_RETURN cret;
+ OSStatus ret;
+ const CSSM_ACCESS_CREDENTIALS *creds;
+ SecKeyRef privKeyRef = (SecKeyRef)kc->item;
+ CSSM_CSP_HANDLE cspHandle;
+ const CSSM_KEY *cssmKey;
+ CSSM_CC_HANDLE sigHandle = 0;
+ CSSM_DATA sig, in;
+ int fret = 0;
+
+
+ cret = SecKeyGetCSSMKey(privKeyRef, &cssmKey);
+ if(cret) abort();
+
+ cret = SecKeyGetCSPHandle(privKeyRef, &cspHandle);
+ if(cret) abort();
+
+ ret = SecKeyGetCredentials(privKeyRef, CSSM_ACL_AUTHORIZATION_SIGN,
+ kSecCredentialTypeDefault, &creds);
+ if(ret) abort();
+
+ ret = CSSM_CSP_CreateSignatureContext(cspHandle, CSSM_ALGID_RSA,
+ creds, cssmKey, &sigHandle);
+ if(ret) abort();
+
+ in.Data = (uint8 *)from;
+ in.Length = flen;
+
+ sig.Data = (uint8 *)to;
+ sig.Length = kc->keysize;
+
+ cret = CSSM_SignData(sigHandle, &in, 1, CSSM_ALGID_NONE, &sig);
+ if(cret) {
+ /* cssmErrorString(cret); */
+ fret = -1;
+ } else
+ fret = sig.Length;
+
+ if(sigHandle)
+ CSSM_DeleteContext(sigHandle);
+
+ return fret;
+}
+
+static int
+kc_rsa_private_decrypt(int flen, const unsigned char *from, unsigned char *to,
+ RSA * rsa, int padding)
+{
+ return -1;
+}
+
+static int
+kc_rsa_init(RSA *rsa)
+{
+ return 1;
+}
+
+static int
+kc_rsa_finish(RSA *rsa)
+{
+ struct kc_rsa *kc_rsa = RSA_get_app_data(rsa);
+ CFRelease(kc_rsa->item);
+ memset(kc_rsa, 0, sizeof(*kc_rsa));
+ free(kc_rsa);
+ return 1;
+}
+
+static const RSA_METHOD kc_rsa_pkcs1_method = {
+ "hx509 Keychain PKCS#1 RSA",
+ kc_rsa_public_encrypt,
+ kc_rsa_public_decrypt,
+ kc_rsa_private_encrypt,
+ kc_rsa_private_decrypt,
+ NULL,
+ NULL,
+ kc_rsa_init,
+ kc_rsa_finish,
+ 0,
+ NULL,
+ NULL,
+ NULL
+};
+
+static int
+set_private_key(hx509_context context,
+ SecKeychainItemRef itemRef,
+ hx509_cert cert)
+{
+ struct kc_rsa *kc;
+ hx509_private_key key;
+ RSA *rsa;
+ int ret;
+
+ ret = _hx509_private_key_init(&key, NULL, NULL);
+ if (ret)
+ return ret;
+
+ kc = calloc(1, sizeof(*kc));
+ if (kc == NULL)
+ _hx509_abort("out of memory");
+
+ kc->item = itemRef;
+
+ rsa = RSA_new();
+ if (rsa == NULL)
+ _hx509_abort("out of memory");
+
+ /* Argh, fake modulus since OpenSSL API is on crack */
+ {
+ SecKeychainAttributeList *attrs = NULL;
+ uint32_t size;
+ void *data;
+
+ rsa->n = BN_new();
+ if (rsa->n == NULL) abort();
+
+ ret = getAttribute(itemRef, kSecKeyKeySizeInBits, &attrs);
+ if (ret) abort();
+
+ size = *(uint32_t *)attrs->attr[0].data;
+ SecKeychainItemFreeAttributesAndData(attrs, NULL);
+
+ kc->keysize = (size + 7) / 8;
+
+ data = malloc(kc->keysize);
+ memset(data, 0xe0, kc->keysize);
+ BN_bin2bn(data, kc->keysize, rsa->n);
+ free(data);
+ }
+ rsa->e = NULL;
+
+ RSA_set_method(rsa, &kc_rsa_pkcs1_method);
+ ret = RSA_set_app_data(rsa, kc);
+ if (ret != 1)
+ _hx509_abort("RSA_set_app_data");
+
+ _hx509_private_key_assign_rsa(key, rsa);
+ _hx509_cert_assign_key(cert, key);
+
+ return 0;
+}
+
+/*
+ *
+ */
+
+struct ks_keychain {
+ int anchors;
+ SecKeychainRef keychain;
+};
+
+static int
+keychain_init(hx509_context context,
+ hx509_certs certs, void **data, int flags,
+ const char *residue, hx509_lock lock)
+{
+ struct ks_keychain *ctx;
+
+ ctx = calloc(1, sizeof(*ctx));
+ if (ctx == NULL) {
+ hx509_clear_error_string(context);
+ return ENOMEM;
+ }
+
+ if (residue) {
+ if (strcasecmp(residue, "system-anchors") == 0) {
+ ctx->anchors = 1;
+ } else if (strncasecmp(residue, "FILE:", 5) == 0) {
+ OSStatus ret;
+
+ ret = SecKeychainOpen(residue + 5, &ctx->keychain);
+ if (ret != noErr) {
+ hx509_set_error_string(context, 0, ENOENT,
+ "Failed to open %s", residue);
+ return ENOENT;
+ }
+ } else {
+ hx509_set_error_string(context, 0, ENOENT,
+ "Unknown subtype %s", residue);
+ return ENOENT;
+ }
+ }
+
+ *data = ctx;
+ return 0;
+}
+
+/*
+ *
+ */
+
+static int
+keychain_free(hx509_certs certs, void *data)
+{
+ struct ks_keychain *ctx = data;
+ if (ctx->keychain)
+ CFRelease(ctx->keychain);
+ memset(ctx, 0, sizeof(*ctx));
+ free(ctx);
+ return 0;
+}
+
+/*
+ *
+ */
+
+struct iter {
+ hx509_certs certs;
+ void *cursor;
+ SecKeychainSearchRef searchRef;
+};
+
+static int
+keychain_iter_start(hx509_context context,
+ hx509_certs certs, void *data, void **cursor)
+{
+ struct ks_keychain *ctx = data;
+ struct iter *iter;
+
+ iter = calloc(1, sizeof(*iter));
+ if (iter == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+
+ if (ctx->anchors) {
+ CFArrayRef anchors;
+ int ret;
+ int i;
+
+ ret = hx509_certs_init(context, "MEMORY:ks-file-create",
+ 0, NULL, &iter->certs);
+ if (ret) {
+ free(iter);
+ return ret;
+ }
+
+ ret = SecTrustCopyAnchorCertificates(&anchors);
+ if (ret != 0) {
+ hx509_certs_free(&iter->certs);
+ free(iter);
+ hx509_set_error_string(context, 0, ENOMEM,
+ "Can't get trust anchors from Keychain");
+ return ENOMEM;
+ }
+ for (i = 0; i < CFArrayGetCount(anchors); i++) {
+ SecCertificateRef cr;
+ hx509_cert cert;
+ CSSM_DATA cssm;
+
+ cr = (SecCertificateRef)CFArrayGetValueAtIndex(anchors, i);
+
+ SecCertificateGetData(cr, &cssm);
+
+ ret = hx509_cert_init_data(context, cssm.Data, cssm.Length, &cert);
+ if (ret)
+ continue;
+
+ ret = hx509_certs_add(context, iter->certs, cert);
+ hx509_cert_free(cert);
+ }
+ CFRelease(anchors);
+ }
+
+ if (iter->certs) {
+ int ret;
+ ret = hx509_certs_start_seq(context, iter->certs, &iter->cursor);
+ if (ret) {
+ hx509_certs_free(&iter->certs);
+ free(iter);
+ return ret;
+ }
+ } else {
+ OSStatus ret;
+
+ ret = SecKeychainSearchCreateFromAttributes(ctx->keychain,
+ kSecCertificateItemClass,
+ NULL,
+ &iter->searchRef);
+ if (ret) {
+ free(iter);
+ hx509_set_error_string(context, 0, ret,
+ "Failed to start search for attributes");
+ return ENOMEM;
+ }
+ }
+
+ *cursor = iter;
+ return 0;
+}
+
+/*
+ *
+ */
+
+static int
+keychain_iter(hx509_context context,
+ hx509_certs certs, void *data, void *cursor, hx509_cert *cert)
+{
+ SecKeychainAttributeList *attrs = NULL;
+ SecKeychainAttributeInfo attrInfo;
+ UInt32 attrFormat[1] = { 0 };
+ SecKeychainItemRef itemRef;
+ SecItemAttr item[1];
+ struct iter *iter = cursor;
+ OSStatus ret;
+ UInt32 len;
+ void *ptr = NULL;
+
+ if (iter->certs)
+ return hx509_certs_next_cert(context, iter->certs, iter->cursor, cert);
+
+ *cert = NULL;
+
+ ret = SecKeychainSearchCopyNext(iter->searchRef, &itemRef);
+ if (ret == errSecItemNotFound)
+ return 0;
+ else if (ret != 0)
+ return EINVAL;
+
+ /*
+ * Pick out certificate and matching "keyid"
+ */
+
+ item[0] = kSecPublicKeyHashItemAttr;
+
+ attrInfo.count = 1;
+ attrInfo.tag = item;
+ attrInfo.format = attrFormat;
+
+ ret = SecKeychainItemCopyAttributesAndData(itemRef, &attrInfo, NULL,
+ &attrs, &len, &ptr);
+ if (ret)
+ return EINVAL;
+
+ ret = hx509_cert_init_data(context, ptr, len, cert);
+ if (ret)
+ goto out;
+
+ /*
+ * Find related private key if there is one by looking at
+ * kSecPublicKeyHashItemAttr == kSecKeyLabel
+ */
+ {
+ SecKeychainSearchRef search;
+ SecKeychainAttribute attrKeyid;
+ SecKeychainAttributeList attrList;
+
+ attrKeyid.tag = kSecKeyLabel;
+ attrKeyid.length = attrs->attr[0].length;
+ attrKeyid.data = attrs->attr[0].data;
+
+ attrList.count = 1;
+ attrList.attr = &attrKeyid;
+
+ ret = SecKeychainSearchCreateFromAttributes(NULL,
+ CSSM_DL_DB_RECORD_PRIVATE_KEY,
+ &attrList,
+ &search);
+ if (ret) {
+ ret = 0;
+ goto out;
+ }
+
+ ret = SecKeychainSearchCopyNext(search, &itemRef);
+ CFRelease(search);
+ if (ret == errSecItemNotFound) {
+ ret = 0;
+ goto out;
+ } else if (ret) {
+ ret = EINVAL;
+ goto out;
+ }
+ set_private_key(context, itemRef, *cert);
+ }
+
+out:
+ SecKeychainItemFreeAttributesAndData(attrs, ptr);
+
+ return ret;
+}
+
+/*
+ *
+ */
+
+static int
+keychain_iter_end(hx509_context context,
+ hx509_certs certs,
+ void *data,
+ void *cursor)
+{
+ struct iter *iter = cursor;
+
+ if (iter->certs) {
+ int ret;
+ ret = hx509_certs_end_seq(context, iter->certs, iter->cursor);
+ hx509_certs_free(&iter->certs);
+ } else {
+ CFRelease(iter->searchRef);
+ }
+
+ memset(iter, 0, sizeof(*iter));
+ free(iter);
+ return 0;
+}
+
+/*
+ *
+ */
+
+struct hx509_keyset_ops keyset_keychain = {
+ "KEYCHAIN",
+ 0,
+ keychain_init,
+ NULL,
+ keychain_free,
+ NULL,
+ NULL,
+ keychain_iter_start,
+ keychain_iter,
+ keychain_iter_end
+};
+
+#endif /* HAVE_FRAMEWORK_SECURITY */
+
+/*
+ *
+ */
+
+void
+_hx509_ks_keychain_register(hx509_context context)
+{
+#ifdef HAVE_FRAMEWORK_SECURITY
+ _hx509_ks_register(context, &keyset_keychain);
+#endif
+}
diff --git a/source4/heimdal/lib/hx509/ks_mem.c b/source4/heimdal/lib/hx509/ks_mem.c
new file mode 100644
index 0000000000..bf952fbeee
--- /dev/null
+++ b/source4/heimdal/lib/hx509/ks_mem.c
@@ -0,0 +1,224 @@
+/*
+ * Copyright (c) 2005 - 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hx_locl.h"
+RCSID("Id$");
+
+/*
+ * Should use two hash/tree certificates intead of a array. Criteria
+ * should be subject and subjectKeyIdentifier since those two are
+ * commonly seached on in CMS and path building.
+ */
+
+struct mem_data {
+ char *name;
+ struct {
+ unsigned long len;
+ hx509_cert *val;
+ } certs;
+ hx509_private_key *keys;
+};
+
+static int
+mem_init(hx509_context context,
+ hx509_certs certs, void **data, int flags,
+ const char *residue, hx509_lock lock)
+{
+ struct mem_data *mem;
+ mem = calloc(1, sizeof(*mem));
+ if (mem == NULL)
+ return ENOMEM;
+ if (residue == NULL || residue[0] == '\0')
+ residue = "anonymous";
+ mem->name = strdup(residue);
+ if (mem->name == NULL) {
+ free(mem);
+ return ENOMEM;
+ }
+ *data = mem;
+ return 0;
+}
+
+static int
+mem_free(hx509_certs certs, void *data)
+{
+ struct mem_data *mem = data;
+ unsigned long i;
+
+ for (i = 0; i < mem->certs.len; i++)
+ hx509_cert_free(mem->certs.val[i]);
+ free(mem->certs.val);
+ for (i = 0; mem->keys && mem->keys[i]; i++)
+ _hx509_private_key_free(&mem->keys[i]);
+ free(mem->keys);
+ free(mem->name);
+ free(mem);
+
+ return 0;
+}
+
+static int
+mem_add(hx509_context context, hx509_certs certs, void *data, hx509_cert c)
+{
+ struct mem_data *mem = data;
+ hx509_cert *val;
+
+ val = realloc(mem->certs.val,
+ (mem->certs.len + 1) * sizeof(mem->certs.val[0]));
+ if (val == NULL)
+ return ENOMEM;
+
+ mem->certs.val = val;
+ mem->certs.val[mem->certs.len] = hx509_cert_ref(c);
+ mem->certs.len++;
+
+ return 0;
+}
+
+static int
+mem_iter_start(hx509_context context,
+ hx509_certs certs,
+ void *data,
+ void **cursor)
+{
+ unsigned long *iter = malloc(sizeof(*iter));
+
+ if (iter == NULL)
+ return ENOMEM;
+
+ *iter = 0;
+ *cursor = iter;
+
+ return 0;
+}
+
+static int
+mem_iter(hx509_context contexst,
+ hx509_certs certs,
+ void *data,
+ void *cursor,
+ hx509_cert *cert)
+{
+ unsigned long *iter = cursor;
+ struct mem_data *mem = data;
+
+ if (*iter >= mem->certs.len) {
+ *cert = NULL;
+ return 0;
+ }
+
+ *cert = hx509_cert_ref(mem->certs.val[*iter]);
+ (*iter)++;
+ return 0;
+}
+
+static int
+mem_iter_end(hx509_context context,
+ hx509_certs certs,
+ void *data,
+ void *cursor)
+{
+ free(cursor);
+ return 0;
+}
+
+static int
+mem_getkeys(hx509_context context,
+ hx509_certs certs,
+ void *data,
+ hx509_private_key **keys)
+{
+ struct mem_data *mem = data;
+ int i;
+
+ for (i = 0; mem->keys && mem->keys[i]; i++)
+ ;
+ *keys = calloc(i + 1, sizeof(**keys));
+ for (i = 0; mem->keys && mem->keys[i]; i++) {
+ (*keys)[i] = _hx509_private_key_ref(mem->keys[i]);
+ if ((*keys)[i] == NULL) {
+ while (--i >= 0)
+ _hx509_private_key_free(&(*keys)[i]);
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+ }
+ (*keys)[i] = NULL;
+ return 0;
+}
+
+static int
+mem_addkey(hx509_context context,
+ hx509_certs certs,
+ void *data,
+ hx509_private_key key)
+{
+ struct mem_data *mem = data;
+ void *ptr;
+ int i;
+
+ for (i = 0; mem->keys && mem->keys[i]; i++)
+ ;
+ ptr = realloc(mem->keys, (i + 2) * sizeof(*mem->keys));
+ if (ptr == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+ mem->keys = ptr;
+ mem->keys[i] = _hx509_private_key_ref(key);
+ mem->keys[i + 1] = NULL;
+ return 0;
+}
+
+
+static struct hx509_keyset_ops keyset_mem = {
+ "MEMORY",
+ 0,
+ mem_init,
+ NULL,
+ mem_free,
+ mem_add,
+ NULL,
+ mem_iter_start,
+ mem_iter,
+ mem_iter_end,
+ NULL,
+ mem_getkeys,
+ mem_addkey
+};
+
+void
+_hx509_ks_mem_register(hx509_context context)
+{
+ _hx509_ks_register(context, &keyset_mem);
+}
diff --git a/source4/heimdal/lib/hx509/ks_null.c b/source4/heimdal/lib/hx509/ks_null.c
new file mode 100644
index 0000000000..fae631fb3f
--- /dev/null
+++ b/source4/heimdal/lib/hx509/ks_null.c
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2005 - 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hx_locl.h"
+RCSID("$Id$");
+
+
+static int
+null_init(hx509_context context,
+ hx509_certs certs, void **data, int flags,
+ const char *residue, hx509_lock lock)
+{
+ *data = NULL;
+ return 0;
+}
+
+static int
+null_free(hx509_certs certs, void *data)
+{
+ assert(data == NULL);
+ return 0;
+}
+
+static int
+null_iter_start(hx509_context context,
+ hx509_certs certs, void *data, void **cursor)
+{
+ *cursor = NULL;
+ return 0;
+}
+
+static int
+null_iter(hx509_context context,
+ hx509_certs certs, void *data, void *iter, hx509_cert *cert)
+{
+ *cert = NULL;
+ return ENOENT;
+}
+
+static int
+null_iter_end(hx509_context context,
+ hx509_certs certs,
+ void *data,
+ void *cursor)
+{
+ assert(cursor == NULL);
+ return 0;
+}
+
+
+struct hx509_keyset_ops keyset_null = {
+ "NULL",
+ 0,
+ null_init,
+ NULL,
+ null_free,
+ NULL,
+ NULL,
+ null_iter_start,
+ null_iter,
+ null_iter_end
+};
+
+void
+_hx509_ks_null_register(hx509_context context)
+{
+ _hx509_ks_register(context, &keyset_null);
+}
diff --git a/source4/heimdal/lib/hx509/ks_p11.c b/source4/heimdal/lib/hx509/ks_p11.c
new file mode 100644
index 0000000000..652cdc2210
--- /dev/null
+++ b/source4/heimdal/lib/hx509/ks_p11.c
@@ -0,0 +1,1194 @@
+/*
+ * Copyright (c) 2004 - 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hx_locl.h"
+RCSID("$Id$");
+#ifdef HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#ifdef HAVE_DLOPEN
+
+#include "pkcs11.h"
+
+struct p11_slot {
+ int flags;
+#define P11_SESSION 1
+#define P11_SESSION_IN_USE 2
+#define P11_LOGIN_REQ 4
+#define P11_LOGIN_DONE 8
+#define P11_TOKEN_PRESENT 16
+ CK_SESSION_HANDLE session;
+ CK_SLOT_ID id;
+ CK_BBOOL token;
+ char *name;
+ hx509_certs certs;
+ char *pin;
+ struct {
+ CK_MECHANISM_TYPE_PTR list;
+ CK_ULONG num;
+ CK_MECHANISM_INFO_PTR *infos;
+ } mechs;
+};
+
+struct p11_module {
+ void *dl_handle;
+ CK_FUNCTION_LIST_PTR funcs;
+ CK_ULONG num_slots;
+ unsigned int ref;
+ struct p11_slot *slot;
+};
+
+#define P11FUNC(module,f,args) (*(module)->funcs->C_##f)args
+
+static int p11_get_session(hx509_context,
+ struct p11_module *,
+ struct p11_slot *,
+ hx509_lock,
+ CK_SESSION_HANDLE *);
+static int p11_put_session(struct p11_module *,
+ struct p11_slot *,
+ CK_SESSION_HANDLE);
+static void p11_release_module(struct p11_module *);
+
+static int p11_list_keys(hx509_context,
+ struct p11_module *,
+ struct p11_slot *,
+ CK_SESSION_HANDLE,
+ hx509_lock,
+ hx509_certs *);
+
+/*
+ *
+ */
+
+struct p11_rsa {
+ struct p11_module *p;
+ struct p11_slot *slot;
+ CK_OBJECT_HANDLE private_key;
+ CK_OBJECT_HANDLE public_key;
+};
+
+static int
+p11_rsa_public_encrypt(int flen,
+ const unsigned char *from,
+ unsigned char *to,
+ RSA *rsa,
+ int padding)
+{
+ return -1;
+}
+
+static int
+p11_rsa_public_decrypt(int flen,
+ const unsigned char *from,
+ unsigned char *to,
+ RSA *rsa,
+ int padding)
+{
+ return -1;
+}
+
+
+static int
+p11_rsa_private_encrypt(int flen,
+ const unsigned char *from,
+ unsigned char *to,
+ RSA *rsa,
+ int padding)
+{
+ struct p11_rsa *p11rsa = RSA_get_app_data(rsa);
+ CK_OBJECT_HANDLE key = p11rsa->private_key;
+ CK_SESSION_HANDLE session;
+ CK_MECHANISM mechanism;
+ CK_ULONG ck_sigsize;
+ int ret;
+
+ if (padding != RSA_PKCS1_PADDING)
+ return -1;
+
+ memset(&mechanism, 0, sizeof(mechanism));
+ mechanism.mechanism = CKM_RSA_PKCS;
+
+ ck_sigsize = RSA_size(rsa);
+
+ ret = p11_get_session(NULL, p11rsa->p, p11rsa->slot, NULL, &session);
+ if (ret)
+ return -1;
+
+ ret = P11FUNC(p11rsa->p, SignInit, (session, &mechanism, key));
+ if (ret != CKR_OK) {
+ p11_put_session(p11rsa->p, p11rsa->slot, session);
+ return -1;
+ }
+
+ ret = P11FUNC(p11rsa->p, Sign,
+ (session, (CK_BYTE *)from, flen, to, &ck_sigsize));
+ p11_put_session(p11rsa->p, p11rsa->slot, session);
+ if (ret != CKR_OK)
+ return -1;
+
+ return ck_sigsize;
+}
+
+static int
+p11_rsa_private_decrypt(int flen, const unsigned char *from, unsigned char *to,
+ RSA * rsa, int padding)
+{
+ struct p11_rsa *p11rsa = RSA_get_app_data(rsa);
+ CK_OBJECT_HANDLE key = p11rsa->private_key;
+ CK_SESSION_HANDLE session;
+ CK_MECHANISM mechanism;
+ CK_ULONG ck_sigsize;
+ int ret;
+
+ if (padding != RSA_PKCS1_PADDING)
+ return -1;
+
+ memset(&mechanism, 0, sizeof(mechanism));
+ mechanism.mechanism = CKM_RSA_PKCS;
+
+ ck_sigsize = RSA_size(rsa);
+
+ ret = p11_get_session(NULL, p11rsa->p, p11rsa->slot, NULL, &session);
+ if (ret)
+ return -1;
+
+ ret = P11FUNC(p11rsa->p, DecryptInit, (session, &mechanism, key));
+ if (ret != CKR_OK) {
+ p11_put_session(p11rsa->p, p11rsa->slot, session);
+ return -1;
+ }
+
+ ret = P11FUNC(p11rsa->p, Decrypt,
+ (session, (CK_BYTE *)from, flen, to, &ck_sigsize));
+ p11_put_session(p11rsa->p, p11rsa->slot, session);
+ if (ret != CKR_OK)
+ return -1;
+
+ return ck_sigsize;
+}
+
+static int
+p11_rsa_init(RSA *rsa)
+{
+ return 1;
+}
+
+static int
+p11_rsa_finish(RSA *rsa)
+{
+ struct p11_rsa *p11rsa = RSA_get_app_data(rsa);
+ p11_release_module(p11rsa->p);
+ free(p11rsa);
+ return 1;
+}
+
+static const RSA_METHOD p11_rsa_pkcs1_method = {
+ "hx509 PKCS11 PKCS#1 RSA",
+ p11_rsa_public_encrypt,
+ p11_rsa_public_decrypt,
+ p11_rsa_private_encrypt,
+ p11_rsa_private_decrypt,
+ NULL,
+ NULL,
+ p11_rsa_init,
+ p11_rsa_finish,
+ 0,
+ NULL,
+ NULL,
+ NULL
+};
+
+/*
+ *
+ */
+
+static int
+p11_mech_info(hx509_context context,
+ struct p11_module *p,
+ struct p11_slot *slot,
+ int num)
+{
+ CK_ULONG i;
+ int ret;
+
+ ret = P11FUNC(p, GetMechanismList, (slot->id, NULL_PTR, &i));
+ if (ret) {
+ hx509_set_error_string(context, 0, HX509_PKCS11_NO_MECH,
+ "Failed to get mech list count for slot %d",
+ num);
+ return HX509_PKCS11_NO_MECH;
+ }
+ if (i == 0) {
+ hx509_set_error_string(context, 0, HX509_PKCS11_NO_MECH,
+ "no mech supported for slot %d", num);
+ return HX509_PKCS11_NO_MECH;
+ }
+ slot->mechs.list = calloc(i, sizeof(slot->mechs.list[0]));
+ if (slot->mechs.list == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM,
+ "out of memory");
+ return ENOMEM;
+ }
+ slot->mechs.num = i;
+ ret = P11FUNC(p, GetMechanismList, (slot->id, slot->mechs.list, &i));
+ if (ret) {
+ hx509_set_error_string(context, 0, HX509_PKCS11_NO_MECH,
+ "Failed to get mech list for slot %d",
+ num);
+ return HX509_PKCS11_NO_MECH;
+ }
+ assert(i == slot->mechs.num);
+
+ slot->mechs.infos = calloc(i, sizeof(*slot->mechs.infos));
+ if (slot->mechs.list == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM,
+ "out of memory");
+ return ENOMEM;
+ }
+
+ for (i = 0; i < slot->mechs.num; i++) {
+ slot->mechs.infos[i] = calloc(1, sizeof(*(slot->mechs.infos[0])));
+ if (slot->mechs.infos[i] == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM,
+ "out of memory");
+ return ENOMEM;
+ }
+ ret = P11FUNC(p, GetMechanismInfo, (slot->id, slot->mechs.list[i],
+ slot->mechs.infos[i]));
+ if (ret) {
+ hx509_set_error_string(context, 0, HX509_PKCS11_NO_MECH,
+ "Failed to get mech info for slot %d",
+ num);
+ return HX509_PKCS11_NO_MECH;
+ }
+ }
+
+ return 0;
+}
+
+static int
+p11_init_slot(hx509_context context,
+ struct p11_module *p,
+ hx509_lock lock,
+ CK_SLOT_ID id,
+ int num,
+ struct p11_slot *slot)
+{
+ CK_SESSION_HANDLE session;
+ CK_SLOT_INFO slot_info;
+ CK_TOKEN_INFO token_info;
+ size_t i;
+ int ret;
+
+ slot->certs = NULL;
+ slot->id = id;
+
+ ret = P11FUNC(p, GetSlotInfo, (slot->id, &slot_info));
+ if (ret) {
+ hx509_set_error_string(context, 0, HX509_PKCS11_TOKEN_CONFUSED,
+ "Failed to init PKCS11 slot %d",
+ num);
+ return HX509_PKCS11_TOKEN_CONFUSED;
+ }
+
+ for (i = sizeof(slot_info.slotDescription) - 1; i > 0; i--) {
+ char c = slot_info.slotDescription[i];
+ if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\0')
+ continue;
+ i++;
+ break;
+ }
+
+ asprintf(&slot->name, "%.*s",
+ i, slot_info.slotDescription);
+
+ if ((slot_info.flags & CKF_TOKEN_PRESENT) == 0)
+ return 0;
+
+ ret = P11FUNC(p, GetTokenInfo, (slot->id, &token_info));
+ if (ret) {
+ hx509_set_error_string(context, 0, HX509_PKCS11_NO_TOKEN,
+ "Failed to init PKCS11 slot %d "
+ "with error 0x08x",
+ num, ret);
+ return HX509_PKCS11_NO_TOKEN;
+ }
+ slot->flags |= P11_TOKEN_PRESENT;
+
+ if (token_info.flags & CKF_LOGIN_REQUIRED)
+ slot->flags |= P11_LOGIN_REQ;
+
+ ret = p11_get_session(context, p, slot, lock, &session);
+ if (ret)
+ return ret;
+
+ ret = p11_mech_info(context, p, slot, num);
+ if (ret)
+ goto out;
+
+ ret = p11_list_keys(context, p, slot, session, lock, &slot->certs);
+ out:
+ p11_put_session(p, slot, session);
+
+ return ret;
+}
+
+static int
+p11_get_session(hx509_context context,
+ struct p11_module *p,
+ struct p11_slot *slot,
+ hx509_lock lock,
+ CK_SESSION_HANDLE *psession)
+{
+ CK_RV ret;
+
+ if (slot->flags & P11_SESSION_IN_USE)
+ _hx509_abort("slot already in session");
+
+ if (slot->flags & P11_SESSION) {
+ slot->flags |= P11_SESSION_IN_USE;
+ *psession = slot->session;
+ return 0;
+ }
+
+ ret = P11FUNC(p, OpenSession, (slot->id,
+ CKF_SERIAL_SESSION,
+ NULL,
+ NULL,
+ &slot->session));
+ if (ret != CKR_OK) {
+ if (context)
+ hx509_set_error_string(context, 0, HX509_PKCS11_OPEN_SESSION,
+ "Failed to OpenSession for slot id %d "
+ "with error: 0x%08x",
+ (int)slot->id, ret);
+ return HX509_PKCS11_OPEN_SESSION;
+ }
+
+ slot->flags |= P11_SESSION;
+
+ /*
+ * If we have have to login, and haven't tried before and have a
+ * prompter or known to work pin code.
+ *
+ * This code is very conversative and only uses the prompter in
+ * the hx509_lock, the reason is that it's bad to try many
+ * passwords on a pkcs11 token, it might lock up and have to be
+ * unlocked by a administrator.
+ *
+ * XXX try harder to not use pin several times on the same card.
+ */
+
+ if ( (slot->flags & P11_LOGIN_REQ)
+ && (slot->flags & P11_LOGIN_DONE) == 0
+ && (lock || slot->pin))
+ {
+ hx509_prompt prompt;
+ char pin[20];
+ char *str;
+
+ if (slot->pin == NULL) {
+
+ memset(&prompt, 0, sizeof(prompt));
+
+ asprintf(&str, "PIN code for %s: ", slot->name);
+ prompt.prompt = str;
+ prompt.type = HX509_PROMPT_TYPE_PASSWORD;
+ prompt.reply.data = pin;
+ prompt.reply.length = sizeof(pin);
+
+ ret = hx509_lock_prompt(lock, &prompt);
+ if (ret) {
+ free(str);
+ if (context)
+ hx509_set_error_string(context, 0, ret,
+ "Failed to get pin code for slot "
+ "id %d with error: %d",
+ (int)slot->id, ret);
+ return ret;
+ }
+ free(str);
+ } else {
+ strlcpy(pin, slot->pin, sizeof(pin));
+ }
+
+ ret = P11FUNC(p, Login, (slot->session, CKU_USER,
+ (unsigned char*)pin, strlen(pin)));
+ if (ret != CKR_OK) {
+ if (context)
+ hx509_set_error_string(context, 0, HX509_PKCS11_LOGIN,
+ "Failed to login on slot id %d "
+ "with error: 0x%08x",
+ (int)slot->id, ret);
+ return HX509_PKCS11_LOGIN;
+ } else
+ slot->flags |= P11_LOGIN_DONE;
+
+ if (slot->pin == NULL) {
+ slot->pin = strdup(pin);
+ if (slot->pin == NULL) {
+ if (context)
+ hx509_set_error_string(context, 0, ENOMEM,
+ "out of memory");
+ return ENOMEM;
+ }
+ }
+ } else
+ slot->flags |= P11_LOGIN_DONE;
+
+ slot->flags |= P11_SESSION_IN_USE;
+
+ *psession = slot->session;
+
+ return 0;
+}
+
+static int
+p11_put_session(struct p11_module *p,
+ struct p11_slot *slot,
+ CK_SESSION_HANDLE session)
+{
+ if ((slot->flags & P11_SESSION_IN_USE) == 0)
+ _hx509_abort("slot not in session");
+ slot->flags &= ~P11_SESSION_IN_USE;
+
+ return 0;
+}
+
+static int
+iterate_entries(hx509_context context,
+ struct p11_module *p, struct p11_slot *slot,
+ CK_SESSION_HANDLE session,
+ CK_ATTRIBUTE *search_data, int num_search_data,
+ CK_ATTRIBUTE *query, int num_query,
+ int (*func)(hx509_context,
+ struct p11_module *, struct p11_slot *,
+ CK_SESSION_HANDLE session,
+ CK_OBJECT_HANDLE object,
+ void *, CK_ATTRIBUTE *, int), void *ptr)
+{
+ CK_OBJECT_HANDLE object;
+ CK_ULONG object_count;
+ int ret, ret2, i;
+
+ ret = P11FUNC(p, FindObjectsInit, (session, search_data, num_search_data));
+ if (ret != CKR_OK) {
+ return -1;
+ }
+ while (1) {
+ ret = P11FUNC(p, FindObjects, (session, &object, 1, &object_count));
+ if (ret != CKR_OK) {
+ return -1;
+ }
+ if (object_count == 0)
+ break;
+
+ for (i = 0; i < num_query; i++)
+ query[i].pValue = NULL;
+
+ ret = P11FUNC(p, GetAttributeValue,
+ (session, object, query, num_query));
+ if (ret != CKR_OK) {
+ return -1;
+ }
+ for (i = 0; i < num_query; i++) {
+ query[i].pValue = malloc(query[i].ulValueLen);
+ if (query[i].pValue == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+ }
+ ret = P11FUNC(p, GetAttributeValue,
+ (session, object, query, num_query));
+ if (ret != CKR_OK) {
+ ret = -1;
+ goto out;
+ }
+
+ ret = (*func)(context, p, slot, session, object, ptr, query, num_query);
+ if (ret)
+ goto out;
+
+ for (i = 0; i < num_query; i++) {
+ if (query[i].pValue)
+ free(query[i].pValue);
+ query[i].pValue = NULL;
+ }
+ }
+ out:
+
+ for (i = 0; i < num_query; i++) {
+ if (query[i].pValue)
+ free(query[i].pValue);
+ query[i].pValue = NULL;
+ }
+
+ ret2 = P11FUNC(p, FindObjectsFinal, (session));
+ if (ret2 != CKR_OK) {
+ return ret2;
+ }
+
+ return ret;
+}
+
+static BIGNUM *
+getattr_bn(struct p11_module *p,
+ struct p11_slot *slot,
+ CK_SESSION_HANDLE session,
+ CK_OBJECT_HANDLE object,
+ unsigned int type)
+{
+ CK_ATTRIBUTE query;
+ BIGNUM *bn;
+ int ret;
+
+ query.type = type;
+ query.pValue = NULL;
+ query.ulValueLen = 0;
+
+ ret = P11FUNC(p, GetAttributeValue,
+ (session, object, &query, 1));
+ if (ret != CKR_OK)
+ return NULL;
+
+ query.pValue = malloc(query.ulValueLen);
+
+ ret = P11FUNC(p, GetAttributeValue,
+ (session, object, &query, 1));
+ if (ret != CKR_OK) {
+ free(query.pValue);
+ return NULL;
+ }
+ bn = BN_bin2bn(query.pValue, query.ulValueLen, NULL);
+ free(query.pValue);
+
+ return bn;
+}
+
+static int
+collect_private_key(hx509_context context,
+ struct p11_module *p, struct p11_slot *slot,
+ CK_SESSION_HANDLE session,
+ CK_OBJECT_HANDLE object,
+ void *ptr, CK_ATTRIBUTE *query, int num_query)
+{
+ struct hx509_collector *collector = ptr;
+ hx509_private_key key;
+ heim_octet_string localKeyId;
+ int ret;
+ RSA *rsa;
+ struct p11_rsa *p11rsa;
+
+ localKeyId.data = query[0].pValue;
+ localKeyId.length = query[0].ulValueLen;
+
+ ret = _hx509_private_key_init(&key, NULL, NULL);
+ if (ret)
+ return ret;
+
+ rsa = RSA_new();
+ if (rsa == NULL)
+ _hx509_abort("out of memory");
+
+ /*
+ * The exponent and modulus should always be present according to
+ * the pkcs11 specification, but some smartcards leaves it out,
+ * let ignore any failure to fetch it.
+ */
+ rsa->n = getattr_bn(p, slot, session, object, CKA_MODULUS);
+ rsa->e = getattr_bn(p, slot, session, object, CKA_PUBLIC_EXPONENT);
+
+ p11rsa = calloc(1, sizeof(*p11rsa));
+ if (p11rsa == NULL)
+ _hx509_abort("out of memory");
+
+ p11rsa->p = p;
+ p11rsa->slot = slot;
+ p11rsa->private_key = object;
+
+ if (p->ref == 0)
+ _hx509_abort("pkcs11 ref == 0 on alloc");
+ p->ref++;
+ if (p->ref == UINT_MAX)
+ _hx509_abort("pkcs11 ref == UINT_MAX on alloc");
+
+ RSA_set_method(rsa, &p11_rsa_pkcs1_method);
+ ret = RSA_set_app_data(rsa, p11rsa);
+ if (ret != 1)
+ _hx509_abort("RSA_set_app_data");
+
+ _hx509_private_key_assign_rsa(key, rsa);
+
+ ret = _hx509_collector_private_key_add(context,
+ collector,
+ hx509_signature_rsa(),
+ key,
+ NULL,
+ &localKeyId);
+
+ if (ret) {
+ _hx509_private_key_free(&key);
+ return ret;
+ }
+ return 0;
+}
+
+static void
+p11_cert_release(hx509_cert cert, void *ctx)
+{
+ struct p11_module *p = ctx;
+ p11_release_module(p);
+}
+
+
+static int
+collect_cert(hx509_context context,
+ struct p11_module *p, struct p11_slot *slot,
+ CK_SESSION_HANDLE session,
+ CK_OBJECT_HANDLE object,
+ void *ptr, CK_ATTRIBUTE *query, int num_query)
+{
+ struct hx509_collector *collector = ptr;
+ hx509_cert cert;
+ int ret;
+
+ if ((CK_LONG)query[0].ulValueLen == -1 ||
+ (CK_LONG)query[1].ulValueLen == -1)
+ {
+ return 0;
+ }
+
+ ret = hx509_cert_init_data(context, query[1].pValue,
+ query[1].ulValueLen, &cert);
+ if (ret)
+ return ret;
+
+ if (p->ref == 0)
+ _hx509_abort("pkcs11 ref == 0 on alloc");
+ p->ref++;
+ if (p->ref == UINT_MAX)
+ _hx509_abort("pkcs11 ref to high");
+
+ _hx509_cert_set_release(cert, p11_cert_release, p);
+
+ {
+ heim_octet_string data;
+
+ data.data = query[0].pValue;
+ data.length = query[0].ulValueLen;
+
+ _hx509_set_cert_attribute(context,
+ cert,
+ oid_id_pkcs_9_at_localKeyId(),
+ &data);
+ }
+
+ if ((CK_LONG)query[2].ulValueLen != -1) {
+ char *str;
+
+ asprintf(&str, "%.*s",
+ (int)query[2].ulValueLen, (char *)query[2].pValue);
+ if (str) {
+ hx509_cert_set_friendly_name(cert, str);
+ free(str);
+ }
+ }
+
+ ret = _hx509_collector_certs_add(context, collector, cert);
+ hx509_cert_free(cert);
+
+ return ret;
+}
+
+
+static int
+p11_list_keys(hx509_context context,
+ struct p11_module *p,
+ struct p11_slot *slot,
+ CK_SESSION_HANDLE session,
+ hx509_lock lock,
+ hx509_certs *certs)
+{
+ struct hx509_collector *collector;
+ CK_OBJECT_CLASS key_class;
+ CK_ATTRIBUTE search_data[] = {
+ {CKA_CLASS, NULL, 0},
+ };
+ CK_ATTRIBUTE query_data[3] = {
+ {CKA_ID, NULL, 0},
+ {CKA_VALUE, NULL, 0},
+ {CKA_LABEL, NULL, 0}
+ };
+ int ret;
+
+ search_data[0].pValue = &key_class;
+ search_data[0].ulValueLen = sizeof(key_class);
+
+ if (lock == NULL)
+ lock = _hx509_empty_lock;
+
+ ret = _hx509_collector_alloc(context, lock, &collector);
+ if (ret)
+ return ret;
+
+ key_class = CKO_PRIVATE_KEY;
+ ret = iterate_entries(context, p, slot, session,
+ search_data, 1,
+ query_data, 1,
+ collect_private_key, collector);
+ if (ret)
+ goto out;
+
+ key_class = CKO_CERTIFICATE;
+ ret = iterate_entries(context, p, slot, session,
+ search_data, 1,
+ query_data, 3,
+ collect_cert, collector);
+ if (ret)
+ goto out;
+
+ ret = _hx509_collector_collect_certs(context, collector, &slot->certs);
+
+out:
+ _hx509_collector_free(collector);
+
+ return ret;
+}
+
+
+static int
+p11_init(hx509_context context,
+ hx509_certs certs, void **data, int flags,
+ const char *residue, hx509_lock lock)
+{
+ CK_C_GetFunctionList getFuncs;
+ struct p11_module *p;
+ char *list, *str;
+ int ret;
+
+ *data = NULL;
+
+ list = strdup(residue);
+ if (list == NULL)
+ return ENOMEM;
+
+ p = calloc(1, sizeof(*p));
+ if (p == NULL) {
+ free(list);
+ return ENOMEM;
+ }
+
+ p->ref = 1;
+
+ str = strchr(list, ',');
+ if (str)
+ *str++ = '\0';
+ while (str) {
+ char *strnext;
+ strnext = strchr(str, ',');
+ if (strnext)
+ *strnext++ = '\0';
+#if 0
+ if (strncasecmp(str, "slot=", 5) == 0)
+ p->selected_slot = atoi(str + 5);
+#endif
+ str = strnext;
+ }
+
+ p->dl_handle = dlopen(list, RTLD_NOW);
+ free(list);
+ if (p->dl_handle == NULL) {
+ ret = HX509_PKCS11_LOAD;
+ hx509_set_error_string(context, 0, ret,
+ "Failed to open %s: %s", list, dlerror());
+ goto out;
+ }
+
+ getFuncs = dlsym(p->dl_handle, "C_GetFunctionList");
+ if (getFuncs == NULL) {
+ ret = HX509_PKCS11_LOAD;
+ hx509_set_error_string(context, 0, ret,
+ "C_GetFunctionList missing in %s: %s",
+ list, dlerror());
+ goto out;
+ }
+
+ ret = (*getFuncs)(&p->funcs);
+ if (ret) {
+ ret = HX509_PKCS11_LOAD;
+ hx509_set_error_string(context, 0, ret,
+ "C_GetFunctionList failed in %s", list);
+ goto out;
+ }
+
+ ret = P11FUNC(p, Initialize, (NULL_PTR));
+ if (ret != CKR_OK) {
+ ret = HX509_PKCS11_TOKEN_CONFUSED;
+ hx509_set_error_string(context, 0, ret,
+ "Failed initialize the PKCS11 module");
+ goto out;
+ }
+
+ ret = P11FUNC(p, GetSlotList, (FALSE, NULL, &p->num_slots));
+ if (ret) {
+ ret = HX509_PKCS11_TOKEN_CONFUSED;
+ hx509_set_error_string(context, 0, ret,
+ "Failed to get number of PKCS11 slots");
+ goto out;
+ }
+
+ if (p->num_slots == 0) {
+ ret = HX509_PKCS11_NO_SLOT;
+ hx509_set_error_string(context, 0, ret,
+ "Selected PKCS11 module have no slots");
+ goto out;
+ }
+
+
+ {
+ CK_SLOT_ID_PTR slot_ids;
+ int i, num_tokens = 0;
+
+ slot_ids = malloc(p->num_slots * sizeof(*slot_ids));
+ if (slot_ids == NULL) {
+ hx509_clear_error_string(context);
+ ret = ENOMEM;
+ goto out;
+ }
+
+ ret = P11FUNC(p, GetSlotList, (FALSE, slot_ids, &p->num_slots));
+ if (ret) {
+ free(slot_ids);
+ hx509_set_error_string(context, 0, HX509_PKCS11_TOKEN_CONFUSED,
+ "Failed getting slot-list from "
+ "PKCS11 module");
+ ret = HX509_PKCS11_TOKEN_CONFUSED;
+ goto out;
+ }
+
+ p->slot = calloc(p->num_slots, sizeof(p->slot[0]));
+ if (p->slot == NULL) {
+ free(slot_ids);
+ hx509_set_error_string(context, 0, ENOMEM,
+ "Failed to get memory for slot-list");
+ ret = ENOMEM;
+ goto out;
+ }
+
+ for (i = 0; i < p->num_slots; i++) {
+ ret = p11_init_slot(context, p, lock, slot_ids[i], i, &p->slot[i]);
+ if (ret)
+ break;
+ if (p->slot[i].flags & P11_TOKEN_PRESENT)
+ num_tokens++;
+ }
+ free(slot_ids);
+ if (ret)
+ goto out;
+ if (num_tokens == 0) {
+ ret = HX509_PKCS11_NO_TOKEN;
+ goto out;
+ }
+ }
+
+ *data = p;
+
+ return 0;
+ out:
+ p11_release_module(p);
+ return ret;
+}
+
+static void
+p11_release_module(struct p11_module *p)
+{
+ int i;
+
+ if (p->ref == 0)
+ _hx509_abort("pkcs11 ref to low");
+ if (--p->ref > 0)
+ return;
+
+ for (i = 0; i < p->num_slots; i++) {
+ if (p->slot[i].flags & P11_SESSION_IN_USE)
+ _hx509_abort("pkcs11 module release while session in use");
+ if (p->slot[i].flags & P11_SESSION) {
+ int ret;
+
+ ret = P11FUNC(p, CloseSession, (p->slot[i].session));
+ if (ret != CKR_OK)
+ ;
+ }
+
+ if (p->slot[i].name)
+ free(p->slot[i].name);
+ if (p->slot[i].pin) {
+ memset(p->slot[i].pin, 0, strlen(p->slot[i].pin));
+ free(p->slot[i].pin);
+ }
+ if (p->slot[i].mechs.num) {
+ free(p->slot[i].mechs.list);
+
+ if (p->slot[i].mechs.infos) {
+ int j;
+
+ for (j = 0 ; j < p->slot[i].mechs.num ; j++)
+ free(p->slot[i].mechs.infos[j]);
+ free(p->slot[i].mechs.infos);
+ }
+ }
+ }
+ free(p->slot);
+
+ if (p->funcs)
+ P11FUNC(p, Finalize, (NULL));
+
+ if (p->dl_handle)
+ dlclose(p->dl_handle);
+
+ memset(p, 0, sizeof(*p));
+ free(p);
+}
+
+static int
+p11_free(hx509_certs certs, void *data)
+{
+ struct p11_module *p = data;
+ int i;
+
+ for (i = 0; i < p->num_slots; i++) {
+ if (p->slot[i].certs)
+ hx509_certs_free(&p->slot[i].certs);
+ }
+ p11_release_module(p);
+ return 0;
+}
+
+struct p11_cursor {
+ hx509_certs certs;
+ void *cursor;
+};
+
+static int
+p11_iter_start(hx509_context context,
+ hx509_certs certs, void *data, void **cursor)
+{
+ struct p11_module *p = data;
+ struct p11_cursor *c;
+ int ret, i;
+
+ c = malloc(sizeof(*c));
+ if (c == NULL) {
+ hx509_clear_error_string(context);
+ return ENOMEM;
+ }
+ ret = hx509_certs_init(context, "MEMORY:pkcs11-iter", 0, NULL, &c->certs);
+ if (ret) {
+ free(c);
+ return ret;
+ }
+
+ for (i = 0 ; i < p->num_slots; i++) {
+ if (p->slot[i].certs == NULL)
+ continue;
+ ret = hx509_certs_merge(context, c->certs, p->slot[i].certs);
+ if (ret) {
+ hx509_certs_free(&c->certs);
+ free(c);
+ return ret;
+ }
+ }
+
+ ret = hx509_certs_start_seq(context, c->certs, &c->cursor);
+ if (ret) {
+ hx509_certs_free(&c->certs);
+ free(c);
+ return 0;
+ }
+ *cursor = c;
+
+ return 0;
+}
+
+static int
+p11_iter(hx509_context context,
+ hx509_certs certs, void *data, void *cursor, hx509_cert *cert)
+{
+ struct p11_cursor *c = cursor;
+ return hx509_certs_next_cert(context, c->certs, c->cursor, cert);
+}
+
+static int
+p11_iter_end(hx509_context context,
+ hx509_certs certs, void *data, void *cursor)
+{
+ struct p11_cursor *c = cursor;
+ int ret;
+ ret = hx509_certs_end_seq(context, c->certs, c->cursor);
+ hx509_certs_free(&c->certs);
+ free(c);
+ return ret;
+}
+
+#define MECHFLAG(x) { "unknown-flag-" #x, x }
+static struct units mechflags[] = {
+ MECHFLAG(0x80000000),
+ MECHFLAG(0x40000000),
+ MECHFLAG(0x20000000),
+ MECHFLAG(0x10000000),
+ MECHFLAG(0x08000000),
+ MECHFLAG(0x04000000),
+ {"ec-compress", 0x2000000 },
+ {"ec-uncompress", 0x1000000 },
+ {"ec-namedcurve", 0x0800000 },
+ {"ec-ecparameters", 0x0400000 },
+ {"ec-f-2m", 0x0200000 },
+ {"ec-f-p", 0x0100000 },
+ {"derive", 0x0080000 },
+ {"unwrap", 0x0040000 },
+ {"wrap", 0x0020000 },
+ {"genereate-key-pair", 0x0010000 },
+ {"generate", 0x0008000 },
+ {"verify-recover", 0x0004000 },
+ {"verify", 0x0002000 },
+ {"sign-recover", 0x0001000 },
+ {"sign", 0x0000800 },
+ {"digest", 0x0000400 },
+ {"decrypt", 0x0000200 },
+ {"encrypt", 0x0000100 },
+ MECHFLAG(0x00080),
+ MECHFLAG(0x00040),
+ MECHFLAG(0x00020),
+ MECHFLAG(0x00010),
+ MECHFLAG(0x00008),
+ MECHFLAG(0x00004),
+ MECHFLAG(0x00002),
+ {"hw", 0x0000001 },
+ { NULL, 0x0000000 }
+};
+#undef MECHFLAG
+
+static int
+p11_printinfo(hx509_context context,
+ hx509_certs certs,
+ void *data,
+ int (*func)(void *, const char *),
+ void *ctx)
+{
+ struct p11_module *p = data;
+ int i, j;
+
+ _hx509_pi_printf(func, ctx, "pkcs11 driver with %d slot%s",
+ p->num_slots, p->num_slots > 1 ? "s" : "");
+
+ for (i = 0; i < p->num_slots; i++) {
+ struct p11_slot *s = &p->slot[i];
+
+ _hx509_pi_printf(func, ctx, "slot %d: id: %d name: %s flags: %08x",
+ i, (int)s->id, s->name, s->flags);
+
+ _hx509_pi_printf(func, ctx, "number of supported mechanisms: %lu",
+ (unsigned long)s->mechs.num);
+ for (j = 0; j < s->mechs.num; j++) {
+ const char *mechname = "unknown";
+ char flags[256], unknownname[40];
+#define MECHNAME(s,n) case s: mechname = n; break
+ switch(s->mechs.list[j]) {
+ MECHNAME(CKM_RSA_PKCS_KEY_PAIR_GEN, "rsa-pkcs-key-pair-gen");
+ MECHNAME(CKM_RSA_PKCS, "rsa-pkcs");
+ MECHNAME(CKM_RSA_X_509, "rsa-x-509");
+ MECHNAME(CKM_MD5_RSA_PKCS, "md5-rsa-pkcs");
+ MECHNAME(CKM_SHA1_RSA_PKCS, "sha1-rsa-pkcs");
+ MECHNAME(CKM_SHA256_RSA_PKCS, "sha256-rsa-pkcs");
+ MECHNAME(CKM_SHA384_RSA_PKCS, "sha384-rsa-pkcs");
+ MECHNAME(CKM_SHA512_RSA_PKCS, "sha512-rsa-pkcs");
+ MECHNAME(CKM_RIPEMD160_RSA_PKCS, "ripemd160-rsa-pkcs");
+ MECHNAME(CKM_RSA_PKCS_OAEP, "rsa-pkcs-oaep");
+ MECHNAME(CKM_SHA512_HMAC, "sha512-hmac");
+ MECHNAME(CKM_SHA512, "sha512");
+ MECHNAME(CKM_SHA384_HMAC, "sha384-hmac");
+ MECHNAME(CKM_SHA384, "sha384");
+ MECHNAME(CKM_SHA256_HMAC, "sha256-hmac");
+ MECHNAME(CKM_SHA256, "sha256");
+ MECHNAME(CKM_SHA_1, "sha1");
+ MECHNAME(CKM_MD5, "md5");
+ MECHNAME(CKM_MD2, "md2");
+ MECHNAME(CKM_RIPEMD160, "ripemd-160");
+ MECHNAME(CKM_DES_ECB, "des-ecb");
+ MECHNAME(CKM_DES_CBC, "des-cbc");
+ MECHNAME(CKM_AES_ECB, "aes-ecb");
+ MECHNAME(CKM_AES_CBC, "aes-cbc");
+ MECHNAME(CKM_DH_PKCS_PARAMETER_GEN, "dh-pkcs-parameter-gen");
+ default:
+ snprintf(unknownname, sizeof(unknownname),
+ "unknown-mech-%lu",
+ (unsigned long)s->mechs.list[j]);
+ mechname = unknownname;
+ break;
+ }
+#undef MECHNAME
+ unparse_flags(s->mechs.infos[j]->flags, mechflags,
+ flags, sizeof(flags));
+
+ _hx509_pi_printf(func, ctx, " %s: %s", mechname, flags);
+ }
+ }
+
+ return 0;
+}
+
+static struct hx509_keyset_ops keyset_pkcs11 = {
+ "PKCS11",
+ 0,
+ p11_init,
+ NULL,
+ p11_free,
+ NULL,
+ NULL,
+ p11_iter_start,
+ p11_iter,
+ p11_iter_end,
+ p11_printinfo
+};
+
+#endif /* HAVE_DLOPEN */
+
+void
+_hx509_ks_pkcs11_register(hx509_context context)
+{
+#ifdef HAVE_DLOPEN
+ _hx509_ks_register(context, &keyset_pkcs11);
+#endif
+}
diff --git a/source4/heimdal/lib/hx509/ks_p12.c b/source4/heimdal/lib/hx509/ks_p12.c
new file mode 100644
index 0000000000..b59bd215f0
--- /dev/null
+++ b/source4/heimdal/lib/hx509/ks_p12.c
@@ -0,0 +1,705 @@
+/*
+ * Copyright (c) 2004 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hx_locl.h"
+RCSID("$Id$");
+
+struct ks_pkcs12 {
+ hx509_certs certs;
+ char *fn;
+};
+
+typedef int (*collector_func)(hx509_context,
+ struct hx509_collector *,
+ const void *, size_t,
+ const PKCS12_Attributes *);
+
+struct type {
+ const heim_oid * (*oid)(void);
+ collector_func func;
+};
+
+static void
+parse_pkcs12_type(hx509_context, struct hx509_collector *, const heim_oid *,
+ const void *, size_t, const PKCS12_Attributes *);
+
+
+static const PKCS12_Attribute *
+find_attribute(const PKCS12_Attributes *attrs, const heim_oid *oid)
+{
+ int i;
+ if (attrs == NULL)
+ return NULL;
+ for (i = 0; i < attrs->len; i++)
+ if (der_heim_oid_cmp(oid, &attrs->val[i].attrId) == 0)
+ return &attrs->val[i];
+ return NULL;
+}
+
+static int
+keyBag_parser(hx509_context context,
+ struct hx509_collector *c,
+ const void *data, size_t length,
+ const PKCS12_Attributes *attrs)
+{
+ const PKCS12_Attribute *attr;
+ PKCS8PrivateKeyInfo ki;
+ const heim_octet_string *os = NULL;
+ int ret;
+
+ attr = find_attribute(attrs, oid_id_pkcs_9_at_localKeyId());
+ if (attr)
+ os = &attr->attrValues;
+
+ ret = decode_PKCS8PrivateKeyInfo(data, length, &ki, NULL);
+ if (ret)
+ return ret;
+
+ _hx509_collector_private_key_add(context,
+ c,
+ &ki.privateKeyAlgorithm,
+ NULL,
+ &ki.privateKey,
+ os);
+ free_PKCS8PrivateKeyInfo(&ki);
+ return 0;
+}
+
+static int
+ShroudedKeyBag_parser(hx509_context context,
+ struct hx509_collector *c,
+ const void *data, size_t length,
+ const PKCS12_Attributes *attrs)
+{
+ PKCS8EncryptedPrivateKeyInfo pk;
+ heim_octet_string content;
+ int ret;
+
+ memset(&pk, 0, sizeof(pk));
+
+ ret = decode_PKCS8EncryptedPrivateKeyInfo(data, length, &pk, NULL);
+ if (ret)
+ return ret;
+
+ ret = _hx509_pbe_decrypt(context,
+ _hx509_collector_get_lock(c),
+ &pk.encryptionAlgorithm,
+ &pk.encryptedData,
+ &content);
+ free_PKCS8EncryptedPrivateKeyInfo(&pk);
+ if (ret)
+ return ret;
+
+ ret = keyBag_parser(context, c, content.data, content.length, attrs);
+ der_free_octet_string(&content);
+ return ret;
+}
+
+static int
+certBag_parser(hx509_context context,
+ struct hx509_collector *c,
+ const void *data, size_t length,
+ const PKCS12_Attributes *attrs)
+{
+ heim_octet_string os;
+ hx509_cert cert;
+ PKCS12_CertBag cb;
+ int ret;
+
+ ret = decode_PKCS12_CertBag(data, length, &cb, NULL);
+ if (ret)
+ return ret;
+
+ if (der_heim_oid_cmp(oid_id_pkcs_9_at_certTypes_x509(), &cb.certType)) {
+ free_PKCS12_CertBag(&cb);
+ return 0;
+ }
+
+ ret = decode_PKCS12_OctetString(cb.certValue.data,
+ cb.certValue.length,
+ &os,
+ NULL);
+ free_PKCS12_CertBag(&cb);
+ if (ret)
+ return ret;
+
+ ret = hx509_cert_init_data(context, os.data, os.length, &cert);
+ der_free_octet_string(&os);
+ if (ret)
+ return ret;
+
+ ret = _hx509_collector_certs_add(context, c, cert);
+ if (ret) {
+ hx509_cert_free(cert);
+ return ret;
+ }
+
+ {
+ const PKCS12_Attribute *attr;
+ const heim_oid * (*oids[])(void) = {
+ oid_id_pkcs_9_at_localKeyId, oid_id_pkcs_9_at_friendlyName
+ };
+ int i;
+
+ for (i = 0; i < sizeof(oids)/sizeof(oids[0]); i++) {
+ const heim_oid *oid = (*(oids[i]))();
+ attr = find_attribute(attrs, oid);
+ if (attr)
+ _hx509_set_cert_attribute(context, cert, oid,
+ &attr->attrValues);
+ }
+ }
+
+ hx509_cert_free(cert);
+
+ return 0;
+}
+
+static int
+parse_safe_content(hx509_context context,
+ struct hx509_collector *c,
+ const unsigned char *p, size_t len)
+{
+ PKCS12_SafeContents sc;
+ int ret, i;
+
+ memset(&sc, 0, sizeof(sc));
+
+ ret = decode_PKCS12_SafeContents(p, len, &sc, NULL);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < sc.len ; i++)
+ parse_pkcs12_type(context,
+ c,
+ &sc.val[i].bagId,
+ sc.val[i].bagValue.data,
+ sc.val[i].bagValue.length,
+ sc.val[i].bagAttributes);
+
+ free_PKCS12_SafeContents(&sc);
+ return 0;
+}
+
+static int
+safeContent_parser(hx509_context context,
+ struct hx509_collector *c,
+ const void *data, size_t length,
+ const PKCS12_Attributes *attrs)
+{
+ heim_octet_string os;
+ int ret;
+
+ ret = decode_PKCS12_OctetString(data, length, &os, NULL);
+ if (ret)
+ return ret;
+ ret = parse_safe_content(context, c, os.data, os.length);
+ der_free_octet_string(&os);
+ return ret;
+}
+
+static int
+encryptedData_parser(hx509_context context,
+ struct hx509_collector *c,
+ const void *data, size_t length,
+ const PKCS12_Attributes *attrs)
+{
+ heim_octet_string content;
+ heim_oid contentType;
+ int ret;
+
+ memset(&contentType, 0, sizeof(contentType));
+
+ ret = hx509_cms_decrypt_encrypted(context,
+ _hx509_collector_get_lock(c),
+ data, length,
+ &contentType,
+ &content);
+ if (ret)
+ return ret;
+
+ if (der_heim_oid_cmp(&contentType, oid_id_pkcs7_data()) == 0)
+ ret = parse_safe_content(context, c, content.data, content.length);
+
+ der_free_octet_string(&content);
+ der_free_oid(&contentType);
+ return ret;
+}
+
+static int
+envelopedData_parser(hx509_context context,
+ struct hx509_collector *c,
+ const void *data, size_t length,
+ const PKCS12_Attributes *attrs)
+{
+ heim_octet_string content;
+ heim_oid contentType;
+ hx509_lock lock;
+ int ret;
+
+ memset(&contentType, 0, sizeof(contentType));
+
+ lock = _hx509_collector_get_lock(c);
+
+ ret = hx509_cms_unenvelope(context,
+ _hx509_lock_unlock_certs(lock),
+ 0,
+ data, length,
+ NULL,
+ 0,
+ &contentType,
+ &content);
+ if (ret) {
+ hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
+ "PKCS12 failed to unenvelope");
+ return ret;
+ }
+
+ if (der_heim_oid_cmp(&contentType, oid_id_pkcs7_data()) == 0)
+ ret = parse_safe_content(context, c, content.data, content.length);
+
+ der_free_octet_string(&content);
+ der_free_oid(&contentType);
+
+ return ret;
+}
+
+
+struct type bagtypes[] = {
+ { oid_id_pkcs12_keyBag, keyBag_parser },
+ { oid_id_pkcs12_pkcs8ShroudedKeyBag, ShroudedKeyBag_parser },
+ { oid_id_pkcs12_certBag, certBag_parser },
+ { oid_id_pkcs7_data, safeContent_parser },
+ { oid_id_pkcs7_encryptedData, encryptedData_parser },
+ { oid_id_pkcs7_envelopedData, envelopedData_parser }
+};
+
+static void
+parse_pkcs12_type(hx509_context context,
+ struct hx509_collector *c,
+ const heim_oid *oid,
+ const void *data, size_t length,
+ const PKCS12_Attributes *attrs)
+{
+ int i;
+
+ for (i = 0; i < sizeof(bagtypes)/sizeof(bagtypes[0]); i++)
+ if (der_heim_oid_cmp((*bagtypes[i].oid)(), oid) == 0)
+ (*bagtypes[i].func)(context, c, data, length, attrs);
+}
+
+static int
+p12_init(hx509_context context,
+ hx509_certs certs, void **data, int flags,
+ const char *residue, hx509_lock lock)
+{
+ struct ks_pkcs12 *p12;
+ size_t len;
+ void *buf;
+ PKCS12_PFX pfx;
+ PKCS12_AuthenticatedSafe as;
+ int ret, i;
+ struct hx509_collector *c;
+
+ *data = NULL;
+
+ if (lock == NULL)
+ lock = _hx509_empty_lock;
+
+ ret = _hx509_collector_alloc(context, lock, &c);
+ if (ret)
+ return ret;
+
+ p12 = calloc(1, sizeof(*p12));
+ if (p12 == NULL) {
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret, "out of memory");
+ goto out;
+ }
+
+ p12->fn = strdup(residue);
+ if (p12->fn == NULL) {
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret, "out of memory");
+ goto out;
+ }
+
+ if (flags & HX509_CERTS_CREATE) {
+ ret = hx509_certs_init(context, "MEMORY:ks-file-create",
+ 0, lock, &p12->certs);
+ if (ret == 0)
+ *data = p12;
+ goto out;
+ }
+
+ ret = rk_undumpdata(residue, &buf, &len);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+
+ ret = decode_PKCS12_PFX(buf, len, &pfx, NULL);
+ rk_xfree(buf);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to decode the PFX in %s", residue);
+ goto out;
+ }
+
+ if (der_heim_oid_cmp(&pfx.authSafe.contentType, oid_id_pkcs7_data()) != 0) {
+ free_PKCS12_PFX(&pfx);
+ ret = EINVAL;
+ hx509_set_error_string(context, 0, ret,
+ "PKCS PFX isn't a pkcs7-data container");
+ goto out;
+ }
+
+ if (pfx.authSafe.content == NULL) {
+ free_PKCS12_PFX(&pfx);
+ ret = EINVAL;
+ hx509_set_error_string(context, 0, ret,
+ "PKCS PFX missing data");
+ goto out;
+ }
+
+ {
+ heim_octet_string asdata;
+
+ ret = decode_PKCS12_OctetString(pfx.authSafe.content->data,
+ pfx.authSafe.content->length,
+ &asdata,
+ NULL);
+ free_PKCS12_PFX(&pfx);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+ ret = decode_PKCS12_AuthenticatedSafe(asdata.data,
+ asdata.length,
+ &as,
+ NULL);
+ der_free_octet_string(&asdata);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+ }
+
+ for (i = 0; i < as.len; i++)
+ parse_pkcs12_type(context,
+ c,
+ &as.val[i].contentType,
+ as.val[i].content->data,
+ as.val[i].content->length,
+ NULL);
+
+ free_PKCS12_AuthenticatedSafe(&as);
+
+ ret = _hx509_collector_collect_certs(context, c, &p12->certs);
+ if (ret == 0)
+ *data = p12;
+
+out:
+ _hx509_collector_free(c);
+
+ if (ret && p12) {
+ if (p12->fn)
+ free(p12->fn);
+ if (p12->certs)
+ hx509_certs_free(&p12->certs);
+ free(p12);
+ }
+
+ return ret;
+}
+
+static int
+addBag(hx509_context context,
+ PKCS12_AuthenticatedSafe *as,
+ const heim_oid *oid,
+ void *data,
+ size_t length)
+{
+ void *ptr;
+ int ret;
+
+ ptr = realloc(as->val, sizeof(as->val[0]) * (as->len + 1));
+ if (ptr == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+ as->val = ptr;
+
+ ret = der_copy_oid(oid, &as->val[as->len].contentType);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "out of memory");
+ return ret;
+ }
+
+ as->val[as->len].content = calloc(1, sizeof(*as->val[0].content));
+ if (as->val[as->len].content == NULL) {
+ der_free_oid(&as->val[as->len].contentType);
+ hx509_set_error_string(context, 0, ENOMEM, "malloc out of memory");
+ return ENOMEM;
+ }
+
+ as->val[as->len].content->data = data;
+ as->val[as->len].content->length = length;
+
+ as->len++;
+
+ return 0;
+}
+
+static int
+store_func(hx509_context context, void *ctx, hx509_cert c)
+{
+ PKCS12_AuthenticatedSafe *as = ctx;
+ PKCS12_OctetString os;
+ PKCS12_CertBag cb;
+ size_t size;
+ int ret;
+
+ memset(&os, 0, sizeof(os));
+ memset(&cb, 0, sizeof(cb));
+
+ os.data = NULL;
+ os.length = 0;
+
+ ret = hx509_cert_binary(context, c, &os);
+ if (ret)
+ return ret;
+
+ ASN1_MALLOC_ENCODE(PKCS12_OctetString,
+ cb.certValue.data,cb.certValue.length,
+ &os, &size, ret);
+ free(os.data);
+ if (ret)
+ goto out;
+ ret = der_copy_oid(oid_id_pkcs_9_at_certTypes_x509(), &cb.certType);
+ if (ret) {
+ free_PKCS12_CertBag(&cb);
+ goto out;
+ }
+ ASN1_MALLOC_ENCODE(PKCS12_CertBag, os.data, os.length,
+ &cb, &size, ret);
+ free_PKCS12_CertBag(&cb);
+ if (ret)
+ goto out;
+
+ ret = addBag(context, as, oid_id_pkcs12_certBag(), os.data, os.length);
+
+ if (_hx509_cert_private_key_exportable(c)) {
+ hx509_private_key key = _hx509_cert_private_key(c);
+ PKCS8PrivateKeyInfo pki;
+
+ memset(&pki, 0, sizeof(pki));
+
+ ret = der_parse_hex_heim_integer("00", &pki.version);
+ if (ret)
+ return ret;
+ ret = _hx509_private_key_oid(context, key,
+ &pki.privateKeyAlgorithm.algorithm);
+ if (ret) {
+ free_PKCS8PrivateKeyInfo(&pki);
+ return ret;
+ }
+ ret = _hx509_private_key_export(context,
+ _hx509_cert_private_key(c),
+ &pki.privateKey);
+ if (ret) {
+ free_PKCS8PrivateKeyInfo(&pki);
+ return ret;
+ }
+ /* set attribute, oid_id_pkcs_9_at_localKeyId() */
+
+ ASN1_MALLOC_ENCODE(PKCS8PrivateKeyInfo, os.data, os.length,
+ &pki, &size, ret);
+ free_PKCS8PrivateKeyInfo(&pki);
+ if (ret)
+ return ret;
+
+ ret = addBag(context, as, oid_id_pkcs12_keyBag(), os.data, os.length);
+ if (ret)
+ return ret;
+ }
+
+out:
+ return ret;
+}
+
+static int
+p12_store(hx509_context context,
+ hx509_certs certs, void *data, int flags, hx509_lock lock)
+{
+ struct ks_pkcs12 *p12 = data;
+ PKCS12_PFX pfx;
+ PKCS12_AuthenticatedSafe as;
+ PKCS12_OctetString asdata;
+ size_t size;
+ int ret;
+
+ memset(&as, 0, sizeof(as));
+ memset(&pfx, 0, sizeof(pfx));
+
+ ret = hx509_certs_iter(context, p12->certs, store_func, &as);
+ if (ret)
+ goto out;
+
+ ASN1_MALLOC_ENCODE(PKCS12_AuthenticatedSafe, asdata.data, asdata.length,
+ &as, &size, ret);
+ free_PKCS12_AuthenticatedSafe(&as);
+ if (ret)
+ return ret;
+
+ ret = der_parse_hex_heim_integer("03", &pfx.version);
+ if (ret) {
+ free(asdata.data);
+ goto out;
+ }
+
+ pfx.authSafe.content = calloc(1, sizeof(*pfx.authSafe.content));
+
+ ASN1_MALLOC_ENCODE(PKCS12_OctetString,
+ pfx.authSafe.content->data,
+ pfx.authSafe.content->length,
+ &asdata, &size, ret);
+ free(asdata.data);
+ if (ret)
+ goto out;
+
+ ret = der_copy_oid(oid_id_pkcs7_data(), &pfx.authSafe.contentType);
+ if (ret)
+ goto out;
+
+ ASN1_MALLOC_ENCODE(PKCS12_PFX, asdata.data, asdata.length,
+ &pfx, &size, ret);
+ if (ret)
+ goto out;
+
+#if 0
+ const struct _hx509_password *pw;
+
+ pw = _hx509_lock_get_passwords(lock);
+ if (pw != NULL) {
+ pfx.macData = calloc(1, sizeof(*pfx.macData));
+ if (pfx.macData == NULL) {
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret, "malloc out of memory");
+ return ret;
+ }
+ if (pfx.macData == NULL) {
+ free(asdata.data);
+ goto out;
+ }
+ }
+ ret = calculate_hash(&aspath, pw, pfx.macData);
+#endif
+
+ rk_dumpdata(p12->fn, asdata.data, asdata.length);
+ free(asdata.data);
+
+out:
+ free_PKCS12_AuthenticatedSafe(&as);
+ free_PKCS12_PFX(&pfx);
+
+ return ret;
+}
+
+
+static int
+p12_free(hx509_certs certs, void *data)
+{
+ struct ks_pkcs12 *p12 = data;
+ hx509_certs_free(&p12->certs);
+ free(p12->fn);
+ free(p12);
+ return 0;
+}
+
+static int
+p12_add(hx509_context context, hx509_certs certs, void *data, hx509_cert c)
+{
+ struct ks_pkcs12 *p12 = data;
+ return hx509_certs_add(context, p12->certs, c);
+}
+
+static int
+p12_iter_start(hx509_context context,
+ hx509_certs certs,
+ void *data,
+ void **cursor)
+{
+ struct ks_pkcs12 *p12 = data;
+ return hx509_certs_start_seq(context, p12->certs, cursor);
+}
+
+static int
+p12_iter(hx509_context context,
+ hx509_certs certs,
+ void *data,
+ void *cursor,
+ hx509_cert *cert)
+{
+ struct ks_pkcs12 *p12 = data;
+ return hx509_certs_next_cert(context, p12->certs, cursor, cert);
+}
+
+static int
+p12_iter_end(hx509_context context,
+ hx509_certs certs,
+ void *data,
+ void *cursor)
+{
+ struct ks_pkcs12 *p12 = data;
+ return hx509_certs_end_seq(context, p12->certs, cursor);
+}
+
+static struct hx509_keyset_ops keyset_pkcs12 = {
+ "PKCS12",
+ 0,
+ p12_init,
+ p12_store,
+ p12_free,
+ p12_add,
+ NULL,
+ p12_iter_start,
+ p12_iter,
+ p12_iter_end
+};
+
+void
+_hx509_ks_pkcs12_register(hx509_context context)
+{
+ _hx509_ks_register(context, &keyset_pkcs12);
+}
diff --git a/source4/heimdal/lib/hx509/lock.c b/source4/heimdal/lib/hx509/lock.c
new file mode 100644
index 0000000000..e2ceedecb8
--- /dev/null
+++ b/source4/heimdal/lib/hx509/lock.c
@@ -0,0 +1,248 @@
+/*
+ * Copyright (c) 2005 - 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hx_locl.h"
+RCSID("$Id$");
+
+/**
+ * @page page_lock Locking and unlocking certificates and encrypted data.
+ *
+ * See the library functions here: @ref hx509_lock
+ */
+
+struct hx509_lock_data {
+ struct _hx509_password password;
+ hx509_certs certs;
+ hx509_prompter_fct prompt;
+ void *prompt_data;
+};
+
+static struct hx509_lock_data empty_lock_data = {
+ { 0, NULL }
+};
+
+hx509_lock _hx509_empty_lock = &empty_lock_data;
+
+/*
+ *
+ */
+
+int
+hx509_lock_init(hx509_context context, hx509_lock *lock)
+{
+ hx509_lock l;
+ int ret;
+
+ *lock = NULL;
+
+ l = calloc(1, sizeof(*l));
+ if (l == NULL)
+ return ENOMEM;
+
+ ret = hx509_certs_init(context,
+ "MEMORY:locks-internal",
+ 0,
+ NULL,
+ &l->certs);
+ if (ret) {
+ free(l);
+ return ret;
+ }
+
+ *lock = l;
+
+ return 0;
+}
+
+int
+hx509_lock_add_password(hx509_lock lock, const char *password)
+{
+ void *d;
+ char *s;
+
+ s = strdup(password);
+ if (s == NULL)
+ return ENOMEM;
+
+ d = realloc(lock->password.val,
+ (lock->password.len + 1) * sizeof(lock->password.val[0]));
+ if (d == NULL) {
+ free(s);
+ return ENOMEM;
+ }
+ lock->password.val = d;
+ lock->password.val[lock->password.len] = s;
+ lock->password.len++;
+
+ return 0;
+}
+
+const struct _hx509_password *
+_hx509_lock_get_passwords(hx509_lock lock)
+{
+ return &lock->password;
+}
+
+hx509_certs
+_hx509_lock_unlock_certs(hx509_lock lock)
+{
+ return lock->certs;
+}
+
+void
+hx509_lock_reset_passwords(hx509_lock lock)
+{
+ int i;
+ for (i = 0; i < lock->password.len; i++)
+ free(lock->password.val[i]);
+ free(lock->password.val);
+ lock->password.val = NULL;
+ lock->password.len = 0;
+}
+
+int
+hx509_lock_add_cert(hx509_context context, hx509_lock lock, hx509_cert cert)
+{
+ return hx509_certs_add(context, lock->certs, cert);
+}
+
+int
+hx509_lock_add_certs(hx509_context context, hx509_lock lock, hx509_certs certs)
+{
+ return hx509_certs_merge(context, lock->certs, certs);
+}
+
+void
+hx509_lock_reset_certs(hx509_context context, hx509_lock lock)
+{
+ hx509_certs certs = lock->certs;
+ int ret;
+
+ ret = hx509_certs_init(context,
+ "MEMORY:locks-internal",
+ 0,
+ NULL,
+ &lock->certs);
+ if (ret == 0)
+ hx509_certs_free(&certs);
+ else
+ lock->certs = certs;
+}
+
+int
+_hx509_lock_find_cert(hx509_lock lock, const hx509_query *q, hx509_cert *c)
+{
+ *c = NULL;
+ return 0;
+}
+
+int
+hx509_lock_set_prompter(hx509_lock lock, hx509_prompter_fct prompt, void *data)
+{
+ lock->prompt = prompt;
+ lock->prompt_data = data;
+ return 0;
+}
+
+void
+hx509_lock_reset_promper(hx509_lock lock)
+{
+ lock->prompt = NULL;
+ lock->prompt_data = NULL;
+}
+
+static int
+default_prompter(void *data, const hx509_prompt *prompter)
+{
+ if (hx509_prompt_hidden(prompter->type)) {
+ if(UI_UTIL_read_pw_string(prompter->reply.data,
+ prompter->reply.length,
+ prompter->prompt,
+ 0))
+ return 1;
+ } else {
+ char *s = prompter->reply.data;
+
+ fputs (prompter->prompt, stdout);
+ fflush (stdout);
+ if(fgets(prompter->reply.data,
+ prompter->reply.length,
+ stdin) == NULL)
+ return 1;
+ s[strcspn(s, "\n")] = '\0';
+ }
+ return 0;
+}
+
+int
+hx509_lock_prompt(hx509_lock lock, hx509_prompt *prompt)
+{
+ if (lock->prompt == NULL)
+ return HX509_CRYPTO_NO_PROMPTER;
+ return (*lock->prompt)(lock->prompt_data, prompt);
+}
+
+void
+hx509_lock_free(hx509_lock lock)
+{
+ hx509_certs_free(&lock->certs);
+ hx509_lock_reset_passwords(lock);
+ memset(lock, 0, sizeof(*lock));
+ free(lock);
+}
+
+int
+hx509_prompt_hidden(hx509_prompt_type type)
+{
+ /* default to hidden if unknown */
+
+ switch (type) {
+ case HX509_PROMPT_TYPE_QUESTION:
+ case HX509_PROMPT_TYPE_INFO:
+ return 0;
+ default:
+ return 1;
+ }
+}
+
+int
+hx509_lock_command_string(hx509_lock lock, const char *string)
+{
+ if (strncasecmp(string, "PASS:", 5) == 0) {
+ hx509_lock_add_password(lock, string + 5);
+ } else if (strcasecmp(string, "PROMPT") == 0) {
+ hx509_lock_set_prompter(lock, default_prompter, NULL);
+ } else
+ return HX509_UNKNOWN_LOCK_COMMAND;
+ return 0;
+}
diff --git a/source4/heimdal/lib/hx509/name.c b/source4/heimdal/lib/hx509/name.c
new file mode 100644
index 0000000000..069eed6062
--- /dev/null
+++ b/source4/heimdal/lib/hx509/name.c
@@ -0,0 +1,991 @@
+/*
+ * Copyright (c) 2004 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hx_locl.h"
+#include <wind.h>
+RCSID("$Id$");
+
+/**
+ * @page page_name PKIX/X.509 Names
+ *
+ * There are several names in PKIX/X.509, GeneralName and Name.
+ *
+ * A Name consists of an ordered list of Relative Distinguished Names
+ * (RDN). Each RDN consists of an unordered list of typed strings. The
+ * types are defined by OID and have long and short description. For
+ * example id-at-commonName (2.5.4.3) have the long name CommonName
+ * and short name CN. The string itself can be of serveral encoding,
+ * UTF8, UTF16, Teltex string, etc. The type limit what encoding
+ * should be used.
+ *
+ * GeneralName is a broader nametype that can contains al kind of
+ * stuff like Name, IP addresses, partial Name, etc.
+ *
+ * Name is mapped into a hx509_name object.
+ *
+ * Parse and string name into a hx509_name object with hx509_parse_name(),
+ * make it back into string representation with hx509_name_to_string().
+ *
+ * Name string are defined rfc2253, rfc1779 and X.501.
+ *
+ * See the library functions here: @ref hx509_name
+ */
+
+static const struct {
+ const char *n;
+ const heim_oid *(*o)(void);
+ wind_profile_flags flags;
+} no[] = {
+ { "C", oid_id_at_countryName },
+ { "CN", oid_id_at_commonName },
+ { "DC", oid_id_domainComponent },
+ { "L", oid_id_at_localityName },
+ { "O", oid_id_at_organizationName },
+ { "OU", oid_id_at_organizationalUnitName },
+ { "S", oid_id_at_stateOrProvinceName },
+ { "STREET", oid_id_at_streetAddress },
+ { "UID", oid_id_Userid },
+ { "emailAddress", oid_id_pkcs9_emailAddress },
+ { "serialNumber", oid_id_at_serialNumber }
+};
+
+static char *
+quote_string(const char *f, size_t len, size_t *rlen)
+{
+ size_t i, j, tolen;
+ const char *from = f;
+ char *to;
+
+ tolen = len * 3 + 1;
+ to = malloc(tolen);
+ if (to == NULL)
+ return NULL;
+
+ for (i = 0, j = 0; i < len; i++) {
+ if (from[i] == ' ' && i + 1 < len)
+ to[j++] = from[i];
+ else if (from[i] == ',' || from[i] == '=' || from[i] == '+' ||
+ from[i] == '<' || from[i] == '>' || from[i] == '#' ||
+ from[i] == ';' || from[i] == ' ')
+ {
+ to[j++] = '\\';
+ to[j++] = from[i];
+ } else if (((unsigned char)from[i]) >= 32 && ((unsigned char)from[i]) <= 127) {
+ to[j++] = from[i];
+ } else {
+ int l = snprintf(&to[j], tolen - j - 1,
+ "#%02x", (unsigned char)from[i]);
+ j += l;
+ }
+ }
+ to[j] = '\0';
+ assert(j < tolen);
+ *rlen = j;
+ return to;
+}
+
+
+static int
+append_string(char **str, size_t *total_len, const char *ss,
+ size_t len, int quote)
+{
+ char *s, *qs;
+
+ if (quote)
+ qs = quote_string(ss, len, &len);
+ else
+ qs = rk_UNCONST(ss);
+
+ s = realloc(*str, len + *total_len + 1);
+ if (s == NULL)
+ _hx509_abort("allocation failure"); /* XXX */
+ memcpy(s + *total_len, qs, len);
+ if (qs != ss)
+ free(qs);
+ s[*total_len + len] = '\0';
+ *str = s;
+ *total_len += len;
+ return 0;
+}
+
+static char *
+oidtostring(const heim_oid *type)
+{
+ char *s;
+ size_t i;
+
+ for (i = 0; i < sizeof(no)/sizeof(no[0]); i++) {
+ if (der_heim_oid_cmp((*no[i].o)(), type) == 0)
+ return strdup(no[i].n);
+ }
+ if (der_print_heim_oid(type, '.', &s) != 0)
+ return NULL;
+ return s;
+}
+
+static int
+stringtooid(const char *name, size_t len, heim_oid *oid)
+{
+ int i, ret;
+ char *s;
+
+ memset(oid, 0, sizeof(*oid));
+
+ for (i = 0; i < sizeof(no)/sizeof(no[0]); i++) {
+ if (strncasecmp(no[i].n, name, len) == 0)
+ return der_copy_oid((*no[i].o)(), oid);
+ }
+ s = malloc(len + 1);
+ if (s == NULL)
+ return ENOMEM;
+ memcpy(s, name, len);
+ s[len] = '\0';
+ ret = der_parse_heim_oid(s, ".", oid);
+ free(s);
+ return ret;
+}
+
+/**
+ * Convert the hx509 name object into a printable string.
+ * The resulting string should be freed with free().
+ *
+ * @param name name to print
+ * @param str the string to return
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_name
+ */
+
+int
+hx509_name_to_string(const hx509_name name, char **str)
+{
+ return _hx509_Name_to_string(&name->der_name, str);
+}
+
+int
+_hx509_Name_to_string(const Name *n, char **str)
+{
+ size_t total_len = 0;
+ int i, j;
+
+ *str = strdup("");
+ if (*str == NULL)
+ return ENOMEM;
+
+ for (i = n->u.rdnSequence.len - 1 ; i >= 0 ; i--) {
+ int len;
+
+ for (j = 0; j < n->u.rdnSequence.val[i].len; j++) {
+ DirectoryString *ds = &n->u.rdnSequence.val[i].val[j].value;
+ char *oidname;
+ char *ss;
+
+ oidname = oidtostring(&n->u.rdnSequence.val[i].val[j].type);
+
+ switch(ds->element) {
+ case choice_DirectoryString_ia5String:
+ ss = ds->u.ia5String;
+ break;
+ case choice_DirectoryString_printableString:
+ ss = ds->u.printableString;
+ break;
+ case choice_DirectoryString_utf8String:
+ ss = ds->u.utf8String;
+ break;
+ case choice_DirectoryString_bmpString: {
+ uint16_t *bmp = ds->u.bmpString.data;
+ size_t bmplen = ds->u.bmpString.length;
+ size_t k;
+
+ ss = malloc(bmplen + 1);
+ if (ss == NULL)
+ _hx509_abort("allocation failure"); /* XXX */
+ for (k = 0; k < bmplen; k++)
+ ss[k] = bmp[k] & 0xff; /* XXX */
+ ss[k] = '\0';
+ break;
+ }
+ case choice_DirectoryString_teletexString:
+ ss = malloc(ds->u.teletexString.length + 1);
+ if (ss == NULL)
+ _hx509_abort("allocation failure"); /* XXX */
+ memcpy(ss, ds->u.teletexString.data, ds->u.teletexString.length);
+ ss[ds->u.teletexString.length] = '\0';
+ break;
+ case choice_DirectoryString_universalString: {
+ uint32_t *uni = ds->u.universalString.data;
+ size_t unilen = ds->u.universalString.length;
+ size_t k;
+
+ ss = malloc(unilen + 1);
+ if (ss == NULL)
+ _hx509_abort("allocation failure"); /* XXX */
+ for (k = 0; k < unilen; k++)
+ ss[k] = uni[k] & 0xff; /* XXX */
+ ss[k] = '\0';
+ break;
+ }
+ default:
+ _hx509_abort("unknown directory type: %d", ds->element);
+ exit(1);
+ }
+ append_string(str, &total_len, oidname, strlen(oidname), 0);
+ free(oidname);
+ append_string(str, &total_len, "=", 1, 0);
+ len = strlen(ss);
+ append_string(str, &total_len, ss, len, 1);
+ if (ds->element == choice_DirectoryString_universalString ||
+ ds->element == choice_DirectoryString_bmpString ||
+ ds->element == choice_DirectoryString_teletexString)
+ {
+ free(ss);
+ }
+ if (j + 1 < n->u.rdnSequence.val[i].len)
+ append_string(str, &total_len, "+", 1, 0);
+ }
+
+ if (i > 0)
+ append_string(str, &total_len, ",", 1, 0);
+ }
+ return 0;
+}
+
+#define COPYCHARARRAY(_ds,_el,_l,_n) \
+ (_l) = strlen(_ds->u._el); \
+ (_n) = malloc((_l) * sizeof((_n)[0])); \
+ if ((_n) == NULL) \
+ return ENOMEM; \
+ for (i = 0; i < (_l); i++) \
+ (_n)[i] = _ds->u._el[i]
+
+
+#define COPYVALARRAY(_ds,_el,_l,_n) \
+ (_l) = _ds->u._el.length; \
+ (_n) = malloc((_l) * sizeof((_n)[0])); \
+ if ((_n) == NULL) \
+ return ENOMEM; \
+ for (i = 0; i < (_l); i++) \
+ (_n)[i] = _ds->u._el.data[i]
+
+#define COPYVOIDARRAY(_ds,_el,_l,_n) \
+ (_l) = _ds->u._el.length; \
+ (_n) = malloc((_l) * sizeof((_n)[0])); \
+ if ((_n) == NULL) \
+ return ENOMEM; \
+ for (i = 0; i < (_l); i++) \
+ (_n)[i] = ((unsigned char *)_ds->u._el.data)[i]
+
+
+
+static int
+dsstringprep(const DirectoryString *ds, uint32_t **rname, size_t *rlen)
+{
+ wind_profile_flags flags = 0;
+ size_t i, len;
+ int ret;
+ uint32_t *name;
+
+ *rname = NULL;
+ *rlen = 0;
+
+ switch(ds->element) {
+ case choice_DirectoryString_ia5String:
+ COPYCHARARRAY(ds, ia5String, len, name);
+ break;
+ case choice_DirectoryString_printableString:
+ flags = WIND_PROFILE_LDAP_CASE_EXACT_ATTRIBUTE;
+ COPYCHARARRAY(ds, printableString, len, name);
+ break;
+ case choice_DirectoryString_teletexString:
+ COPYVOIDARRAY(ds, teletexString, len, name);
+ break;
+ case choice_DirectoryString_bmpString:
+ COPYVALARRAY(ds, bmpString, len, name);
+ break;
+ case choice_DirectoryString_universalString:
+ COPYVALARRAY(ds, universalString, len, name);
+ break;
+ case choice_DirectoryString_utf8String:
+ ret = wind_utf8ucs4_length(ds->u.utf8String, &len);
+ if (ret)
+ return ret;
+ name = malloc(len * sizeof(name[0]));
+ if (name == NULL)
+ return ENOMEM;
+ ret = wind_utf8ucs4(ds->u.utf8String, name, &len);
+ if (ret)
+ return ret;
+ break;
+ default:
+ _hx509_abort("unknown directory type: %d", ds->element);
+ }
+
+ *rlen = len;
+ /* try a couple of times to get the length right, XXX gross */
+ for (i = 0; i < 4; i++) {
+ *rlen = *rlen * 2;
+ *rname = malloc(*rlen * sizeof((*rname)[0]));
+
+ ret = wind_stringprep(name, len, *rname, rlen,
+ WIND_PROFILE_LDAP|flags);
+ if (ret == WIND_ERR_OVERRUN) {
+ free(*rname);
+ *rname = NULL;
+ continue;
+ } else
+ break;
+ }
+ free(name);
+ if (ret) {
+ if (*rname)
+ free(*rname);
+ *rname = NULL;
+ *rlen = 0;
+ return ret;
+ }
+
+ return 0;
+}
+
+int
+_hx509_name_ds_cmp(const DirectoryString *ds1,
+ const DirectoryString *ds2,
+ int *diff)
+{
+ uint32_t *ds1lp, *ds2lp;
+ size_t ds1len, ds2len;
+ int ret;
+
+ ret = dsstringprep(ds1, &ds1lp, &ds1len);
+ if (ret)
+ return ret;
+ ret = dsstringprep(ds2, &ds2lp, &ds2len);
+ if (ret) {
+ free(ds1lp);
+ return ret;
+ }
+
+ if (ds1len != ds2len)
+ *diff = ds1len - ds2len;
+ else
+ *diff = memcmp(ds1lp, ds2lp, ds1len * sizeof(ds1lp[0]));
+
+ free(ds1lp);
+ free(ds2lp);
+
+ return 0;
+}
+
+int
+_hx509_name_cmp(const Name *n1, const Name *n2, int *c)
+{
+ int ret, i, j;
+
+ *c = n1->u.rdnSequence.len - n2->u.rdnSequence.len;
+ if (*c)
+ return 0;
+
+ for (i = 0 ; i < n1->u.rdnSequence.len; i++) {
+ *c = n1->u.rdnSequence.val[i].len - n2->u.rdnSequence.val[i].len;
+ if (*c)
+ return 0;
+
+ for (j = 0; j < n1->u.rdnSequence.val[i].len; j++) {
+ *c = der_heim_oid_cmp(&n1->u.rdnSequence.val[i].val[j].type,
+ &n1->u.rdnSequence.val[i].val[j].type);
+ if (*c)
+ return 0;
+
+ ret = _hx509_name_ds_cmp(&n1->u.rdnSequence.val[i].val[j].value,
+ &n2->u.rdnSequence.val[i].val[j].value,
+ c);
+ if (ret)
+ return ret;
+ if (*c)
+ return 0;
+ }
+ }
+ *c = 0;
+ return 0;
+}
+
+/**
+ * Compare to hx509 name object, useful for sorting.
+ *
+ * @param n1 a hx509 name object.
+ * @param n2 a hx509 name object.
+ *
+ * @return 0 the objects are the same, returns > 0 is n2 is "larger"
+ * then n2, < 0 if n1 is "smaller" then n2.
+ *
+ * @ingroup hx509_name
+ */
+
+int
+hx509_name_cmp(hx509_name n1, hx509_name n2)
+{
+ int ret, diff;
+ ret = _hx509_name_cmp(&n1->der_name, &n2->der_name, &diff);
+ if (ret)
+ return ret;
+ return diff;
+}
+
+
+int
+_hx509_name_from_Name(const Name *n, hx509_name *name)
+{
+ int ret;
+ *name = calloc(1, sizeof(**name));
+ if (*name == NULL)
+ return ENOMEM;
+ ret = copy_Name(n, &(*name)->der_name);
+ if (ret) {
+ free(*name);
+ *name = NULL;
+ }
+ return ret;
+}
+
+int
+_hx509_name_modify(hx509_context context,
+ Name *name,
+ int append,
+ const heim_oid *oid,
+ const char *str)
+{
+ RelativeDistinguishedName *rdn;
+ int ret;
+ void *ptr;
+
+ ptr = realloc(name->u.rdnSequence.val,
+ sizeof(name->u.rdnSequence.val[0]) *
+ (name->u.rdnSequence.len + 1));
+ if (ptr == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "Out of memory");
+ return ENOMEM;
+ }
+ name->u.rdnSequence.val = ptr;
+
+ if (append) {
+ rdn = &name->u.rdnSequence.val[name->u.rdnSequence.len];
+ } else {
+ memmove(&name->u.rdnSequence.val[1],
+ &name->u.rdnSequence.val[0],
+ name->u.rdnSequence.len *
+ sizeof(name->u.rdnSequence.val[0]));
+
+ rdn = &name->u.rdnSequence.val[0];
+ }
+ rdn->val = malloc(sizeof(rdn->val[0]));
+ if (rdn->val == NULL)
+ return ENOMEM;
+ rdn->len = 1;
+ ret = der_copy_oid(oid, &rdn->val[0].type);
+ if (ret)
+ return ret;
+ rdn->val[0].value.element = choice_DirectoryString_utf8String;
+ rdn->val[0].value.u.utf8String = strdup(str);
+ if (rdn->val[0].value.u.utf8String == NULL)
+ return ENOMEM;
+ name->u.rdnSequence.len += 1;
+
+ return 0;
+}
+
+/**
+ * Parse a string into a hx509 name object.
+ *
+ * @param context A hx509 context.
+ * @param str a string to parse.
+ * @param name the resulting object, NULL in case of error.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_name
+ */
+
+int
+hx509_parse_name(hx509_context context, const char *str, hx509_name *name)
+{
+ const char *p, *q;
+ size_t len;
+ hx509_name n;
+ int ret;
+
+ *name = NULL;
+
+ n = calloc(1, sizeof(*n));
+ if (n == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+
+ n->der_name.element = choice_Name_rdnSequence;
+
+ p = str;
+
+ while (p != NULL && *p != '\0') {
+ heim_oid oid;
+ int last;
+
+ q = strchr(p, ',');
+ if (q) {
+ len = (q - p);
+ last = 1;
+ } else {
+ len = strlen(p);
+ last = 0;
+ }
+
+ q = strchr(p, '=');
+ if (q == NULL) {
+ ret = HX509_PARSING_NAME_FAILED;
+ hx509_set_error_string(context, 0, ret, "missing = in %s", p);
+ goto out;
+ }
+ if (q == p) {
+ ret = HX509_PARSING_NAME_FAILED;
+ hx509_set_error_string(context, 0, ret,
+ "missing name before = in %s", p);
+ goto out;
+ }
+
+ if ((q - p) > len) {
+ ret = HX509_PARSING_NAME_FAILED;
+ hx509_set_error_string(context, 0, ret, " = after , in %s", p);
+ goto out;
+ }
+
+ ret = stringtooid(p, q - p, &oid);
+ if (ret) {
+ ret = HX509_PARSING_NAME_FAILED;
+ hx509_set_error_string(context, 0, ret,
+ "unknown type: %.*s", (int)(q - p), p);
+ goto out;
+ }
+
+ {
+ size_t pstr_len = len - (q - p) - 1;
+ const char *pstr = p + (q - p) + 1;
+ char *r;
+
+ r = malloc(pstr_len + 1);
+ if (r == NULL) {
+ der_free_oid(&oid);
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret, "out of memory");
+ goto out;
+ }
+ memcpy(r, pstr, pstr_len);
+ r[pstr_len] = '\0';
+
+ ret = _hx509_name_modify(context, &n->der_name, 0, &oid, r);
+ free(r);
+ der_free_oid(&oid);
+ if(ret)
+ goto out;
+ }
+ p += len + last;
+ }
+
+ *name = n;
+
+ return 0;
+out:
+ hx509_name_free(&n);
+ return HX509_NAME_MALFORMED;
+}
+
+/**
+ * Copy a hx509 name object.
+ *
+ * @param context A hx509 cotext.
+ * @param from the name to copy from
+ * @param to the name to copy to
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_name
+ */
+
+int
+hx509_name_copy(hx509_context context, const hx509_name from, hx509_name *to)
+{
+ int ret;
+
+ *to = calloc(1, sizeof(**to));
+ if (*to == NULL)
+ return ENOMEM;
+ ret = copy_Name(&from->der_name, &(*to)->der_name);
+ if (ret) {
+ free(*to);
+ *to = NULL;
+ return ENOMEM;
+ }
+ return 0;
+}
+
+/**
+ * Convert a hx509_name into a Name.
+ *
+ * @param from the name to copy from
+ * @param to the name to copy to
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_name
+ */
+
+int
+hx509_name_to_Name(const hx509_name from, Name *to)
+{
+ return copy_Name(&from->der_name, to);
+}
+
+int
+hx509_name_normalize(hx509_context context, hx509_name name)
+{
+ return 0;
+}
+
+/**
+ * Expands variables in the name using env. Variables are on the form
+ * ${name}. Useful when dealing with certificate templates.
+ *
+ * @param context A hx509 cotext.
+ * @param name the name to expand.
+ * @param env environment variable to expand.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_name
+ */
+
+int
+hx509_name_expand(hx509_context context,
+ hx509_name name,
+ hx509_env env)
+{
+ Name *n = &name->der_name;
+ int i, j;
+
+ if (env == NULL)
+ return 0;
+
+ if (n->element != choice_Name_rdnSequence) {
+ hx509_set_error_string(context, 0, EINVAL, "RDN not of supported type");
+ return EINVAL;
+ }
+
+ for (i = 0 ; i < n->u.rdnSequence.len; i++) {
+ for (j = 0; j < n->u.rdnSequence.val[i].len; j++) {
+ /** Only UTF8String rdnSequence names are allowed */
+ /*
+ THIS SHOULD REALLY BE:
+ COMP = n->u.rdnSequence.val[i].val[j];
+ normalize COMP to utf8
+ check if there are variables
+ expand variables
+ convert back to orignal format, store in COMP
+ free normalized utf8 string
+ */
+ DirectoryString *ds = &n->u.rdnSequence.val[i].val[j].value;
+ char *p, *p2;
+ struct rk_strpool *strpool = NULL;
+
+ if (ds->element != choice_DirectoryString_utf8String) {
+ hx509_set_error_string(context, 0, EINVAL, "unsupported type");
+ return EINVAL;
+ }
+ p = strstr(ds->u.utf8String, "${");
+ if (p) {
+ strpool = rk_strpoolprintf(strpool, "%.*s",
+ (int)(p - ds->u.utf8String),
+ ds->u.utf8String);
+ if (strpool == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+ }
+ while (p != NULL) {
+ /* expand variables */
+ const char *value;
+ p2 = strchr(p, '}');
+ if (p2 == NULL) {
+ hx509_set_error_string(context, 0, EINVAL, "missing }");
+ rk_strpoolfree(strpool);
+ return EINVAL;
+ }
+ p += 2;
+ value = hx509_env_lfind(context, env, p, p2 - p);
+ if (value == NULL) {
+ hx509_set_error_string(context, 0, EINVAL,
+ "variable %.*s missing",
+ (int)(p2 - p), p);
+ rk_strpoolfree(strpool);
+ return EINVAL;
+ }
+ strpool = rk_strpoolprintf(strpool, "%s", value);
+ if (strpool == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+ p2++;
+
+ p = strstr(p2, "${");
+ if (p)
+ strpool = rk_strpoolprintf(strpool, "%.*s",
+ (int)(p - p2), p2);
+ else
+ strpool = rk_strpoolprintf(strpool, "%s", p2);
+ if (strpool == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+ }
+ if (strpool) {
+ free(ds->u.utf8String);
+ ds->u.utf8String = rk_strpoolcollect(strpool);
+ if (ds->u.utf8String == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+/**
+ * Free a hx509 name object, upond return *name will be NULL.
+ *
+ * @param name a hx509 name object to be freed.
+ *
+ * @ingroup hx509_name
+ */
+
+void
+hx509_name_free(hx509_name *name)
+{
+ free_Name(&(*name)->der_name);
+ memset(*name, 0, sizeof(**name));
+ free(*name);
+ *name = NULL;
+}
+
+/**
+ * Convert a DER encoded name info a string.
+ *
+ * @param data data to a DER/BER encoded name
+ * @param length length of data
+ * @param str the resulting string, is NULL on failure.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_name
+ */
+
+int
+hx509_unparse_der_name(const void *data, size_t length, char **str)
+{
+ Name name;
+ int ret;
+
+ *str = NULL;
+
+ ret = decode_Name(data, length, &name, NULL);
+ if (ret)
+ return ret;
+ ret = _hx509_Name_to_string(&name, str);
+ free_Name(&name);
+ return ret;
+}
+
+/**
+ * Convert a hx509_name object to DER encoded name.
+ *
+ * @param name name to concert
+ * @param os data to a DER encoded name, free the resulting octet
+ * string with hx509_xfree(os->data).
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_name
+ */
+
+int
+hx509_name_binary(const hx509_name name, heim_octet_string *os)
+{
+ size_t size;
+ int ret;
+
+ ASN1_MALLOC_ENCODE(Name, os->data, os->length, &name->der_name, &size, ret);
+ if (ret)
+ return ret;
+ if (os->length != size)
+ _hx509_abort("internal ASN.1 encoder error");
+
+ return 0;
+}
+
+int
+_hx509_unparse_Name(const Name *aname, char **str)
+{
+ hx509_name name;
+ int ret;
+
+ ret = _hx509_name_from_Name(aname, &name);
+ if (ret)
+ return ret;
+
+ ret = hx509_name_to_string(name, str);
+ hx509_name_free(&name);
+ return ret;
+}
+
+/**
+ * Unparse the hx509 name in name into a string.
+ *
+ * @param name the name to check if its empty/null.
+ *
+ * @return non zero if the name is empty/null.
+ *
+ * @ingroup hx509_name
+ */
+
+int
+hx509_name_is_null_p(const hx509_name name)
+{
+ return name->der_name.u.rdnSequence.len == 0;
+}
+
+/**
+ * Unparse the hx509 name in name into a string.
+ *
+ * @param name the name to print
+ * @param str an allocated string returns the name in string form
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_name
+ */
+
+int
+hx509_general_name_unparse(GeneralName *name, char **str)
+{
+ struct rk_strpool *strpool = NULL;
+
+ *str = NULL;
+
+ switch (name->element) {
+ case choice_GeneralName_otherName: {
+ char *str;
+ hx509_oid_sprint(&name->u.otherName.type_id, &str);
+ if (str == NULL)
+ return ENOMEM;
+ strpool = rk_strpoolprintf(strpool, "otherName: %s", str);
+ free(str);
+ break;
+ }
+ case choice_GeneralName_rfc822Name:
+ strpool = rk_strpoolprintf(strpool, "rfc822Name: %s\n",
+ name->u.rfc822Name);
+ break;
+ case choice_GeneralName_dNSName:
+ strpool = rk_strpoolprintf(strpool, "dNSName: %s\n",
+ name->u.dNSName);
+ break;
+ case choice_GeneralName_directoryName: {
+ Name dir;
+ char *s;
+ int ret;
+ memset(&dir, 0, sizeof(dir));
+ dir.element = name->u.directoryName.element;
+ dir.u.rdnSequence = name->u.directoryName.u.rdnSequence;
+ ret = _hx509_unparse_Name(&dir, &s);
+ if (ret)
+ return ret;
+ strpool = rk_strpoolprintf(strpool, "directoryName: %s", s);
+ free(s);
+ break;
+ }
+ case choice_GeneralName_uniformResourceIdentifier:
+ strpool = rk_strpoolprintf(strpool, "URI: %s",
+ name->u.uniformResourceIdentifier);
+ break;
+ case choice_GeneralName_iPAddress: {
+ unsigned char *a = name->u.iPAddress.data;
+
+ strpool = rk_strpoolprintf(strpool, "IPAddress: ");
+ if (strpool == NULL)
+ break;
+ if (name->u.iPAddress.length == 4)
+ strpool = rk_strpoolprintf(strpool, "%d.%d.%d.%d",
+ a[0], a[1], a[2], a[3]);
+ else if (name->u.iPAddress.length == 16)
+ strpool = rk_strpoolprintf(strpool,
+ "%02X:%02X:%02X:%02X:"
+ "%02X:%02X:%02X:%02X:"
+ "%02X:%02X:%02X:%02X:"
+ "%02X:%02X:%02X:%02X",
+ a[0], a[1], a[2], a[3],
+ a[4], a[5], a[6], a[7],
+ a[8], a[9], a[10], a[11],
+ a[12], a[13], a[14], a[15]);
+ else
+ strpool = rk_strpoolprintf(strpool,
+ "unknown IP address of length %lu",
+ (unsigned long)name->u.iPAddress.length);
+ break;
+ }
+ case choice_GeneralName_registeredID: {
+ char *str;
+ hx509_oid_sprint(&name->u.registeredID, &str);
+ if (str == NULL)
+ return ENOMEM;
+ strpool = rk_strpoolprintf(strpool, "registeredID: %s", str);
+ free(str);
+ break;
+ }
+ default:
+ return EINVAL;
+ }
+ if (strpool == NULL)
+ return ENOMEM;
+
+ *str = rk_strpoolcollect(strpool);
+
+ return 0;
+}
diff --git a/source4/heimdal/lib/hx509/ocsp.asn1 b/source4/heimdal/lib/hx509/ocsp.asn1
new file mode 100644
index 0000000000..eb090a4cc7
--- /dev/null
+++ b/source4/heimdal/lib/hx509/ocsp.asn1
@@ -0,0 +1,113 @@
+-- From rfc2560
+-- $Id$
+OCSP DEFINITIONS EXPLICIT TAGS::=
+
+BEGIN
+
+IMPORTS
+ Certificate, AlgorithmIdentifier, CRLReason,
+ Name, GeneralName, CertificateSerialNumber, Extensions
+ FROM rfc2459;
+
+OCSPVersion ::= INTEGER { ocsp-v1(0) }
+
+OCSPCertStatus ::= CHOICE {
+ good [0] IMPLICIT NULL,
+ revoked [1] IMPLICIT -- OCSPRevokedInfo -- SEQUENCE {
+ revocationTime GeneralizedTime,
+ revocationReason[0] EXPLICIT CRLReason OPTIONAL
+ },
+ unknown [2] IMPLICIT NULL }
+
+OCSPCertID ::= SEQUENCE {
+ hashAlgorithm AlgorithmIdentifier,
+ issuerNameHash OCTET STRING, -- Hash of Issuer's DN
+ issuerKeyHash OCTET STRING, -- Hash of Issuers public key
+ serialNumber CertificateSerialNumber }
+
+OCSPSingleResponse ::= SEQUENCE {
+ certID OCSPCertID,
+ certStatus OCSPCertStatus,
+ thisUpdate GeneralizedTime,
+ nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL,
+ singleExtensions [1] EXPLICIT Extensions OPTIONAL }
+
+OCSPInnerRequest ::= SEQUENCE {
+ reqCert OCSPCertID,
+ singleRequestExtensions [0] EXPLICIT Extensions OPTIONAL }
+
+OCSPTBSRequest ::= SEQUENCE {
+ version [0] EXPLICIT OCSPVersion -- DEFAULT v1 -- OPTIONAL,
+ requestorName [1] EXPLICIT GeneralName OPTIONAL,
+ requestList SEQUENCE OF OCSPInnerRequest,
+ requestExtensions [2] EXPLICIT Extensions OPTIONAL }
+
+OCSPSignature ::= SEQUENCE {
+ signatureAlgorithm AlgorithmIdentifier,
+ signature BIT STRING,
+ certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
+
+OCSPRequest ::= SEQUENCE {
+ tbsRequest OCSPTBSRequest,
+ optionalSignature [0] EXPLICIT OCSPSignature OPTIONAL }
+
+OCSPResponseBytes ::= SEQUENCE {
+ responseType OBJECT IDENTIFIER,
+ response OCTET STRING }
+
+OCSPResponseStatus ::= ENUMERATED {
+ successful (0), --Response has valid confirmations
+ malformedRequest (1), --Illegal confirmation request
+ internalError (2), --Internal error in issuer
+ tryLater (3), --Try again later
+ --(4) is not used
+ sigRequired (5), --Must sign the request
+ unauthorized (6) --Request unauthorized
+}
+
+OCSPResponse ::= SEQUENCE {
+ responseStatus OCSPResponseStatus,
+ responseBytes [0] EXPLICIT OCSPResponseBytes OPTIONAL }
+
+OCSPKeyHash ::= OCTET STRING --SHA-1 hash of responder's public key
+ --(excluding the tag and length fields)
+
+OCSPResponderID ::= CHOICE {
+ byName [1] Name,
+ byKey [2] OCSPKeyHash }
+
+OCSPResponseData ::= SEQUENCE {
+ version [0] EXPLICIT OCSPVersion -- DEFAULT v1 -- OPTIONAL,
+ responderID OCSPResponderID,
+ producedAt GeneralizedTime,
+ responses SEQUENCE OF OCSPSingleResponse,
+ responseExtensions [1] EXPLICIT Extensions OPTIONAL }
+
+OCSPBasicOCSPResponse ::= SEQUENCE {
+ tbsResponseData OCSPResponseData,
+ signatureAlgorithm AlgorithmIdentifier,
+ signature BIT STRING,
+ certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
+
+-- ArchiveCutoff ::= GeneralizedTime
+
+-- AcceptableResponses ::= SEQUENCE OF OBJECT IDENTIFIER
+
+-- Object Identifiers
+
+id-pkix-ocsp OBJECT IDENTIFIER ::= {
+ iso(1) identified-organization(3) dod(6) internet(1)
+ security(5) mechanisms(5) pkix(7) pkix-ad(48) 1
+}
+
+id-pkix-ocsp-basic OBJECT IDENTIFIER ::= { id-pkix-ocsp 1 }
+id-pkix-ocsp-nonce OBJECT IDENTIFIER ::= { id-pkix-ocsp 2 }
+-- id-pkix-ocsp-crl OBJECT IDENTIFIER ::= { id-pkix-ocsp 3 }
+-- id-pkix-ocsp-response OBJECT IDENTIFIER ::= { id-pkix-ocsp 4 }
+-- id-pkix-ocsp-nocheck OBJECT IDENTIFIER ::= { id-pkix-ocsp 5 }
+-- id-pkix-ocsp-archive-cutoff OBJECT IDENTIFIER ::= { id-pkix-ocsp 6 }
+-- id-pkix-ocsp-service-locator OBJECT IDENTIFIER ::= { id-pkix-ocsp 7 }
+
+
+END
+
diff --git a/source4/heimdal/lib/hx509/peer.c b/source4/heimdal/lib/hx509/peer.c
new file mode 100644
index 0000000000..f5841e497b
--- /dev/null
+++ b/source4/heimdal/lib/hx509/peer.c
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hx_locl.h"
+RCSID("$Id$");
+
+/**
+ * @page page_peer Hx509 crypto selecting functions
+ *
+ * Peer info structures are used togeter with hx509_crypto_select() to
+ * select the best avaible crypto algorithm to use.
+ *
+ * See the library functions here: @ref hx509_peer
+ */
+
+/**
+ * Allocate a new peer info structure an init it to default values.
+ *
+ * @param context A hx509 context.
+ * @param peer return an allocated peer, free with hx509_peer_info_free().
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_peer
+ */
+
+int
+hx509_peer_info_alloc(hx509_context context, hx509_peer_info *peer)
+{
+ *peer = calloc(1, sizeof(**peer));
+ if (*peer == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+ return 0;
+}
+
+
+static void
+free_cms_alg(hx509_peer_info peer)
+{
+ if (peer->val) {
+ size_t i;
+ for (i = 0; i < peer->len; i++)
+ free_AlgorithmIdentifier(&peer->val[i]);
+ free(peer->val);
+ peer->val = NULL;
+ peer->len = 0;
+ }
+}
+
+/**
+ * Free a peer info structure.
+ *
+ * @param peer peer info to be freed.
+ *
+ * @ingroup hx509_peer
+ */
+
+void
+hx509_peer_info_free(hx509_peer_info peer)
+{
+ if (peer == NULL)
+ return;
+ if (peer->cert)
+ hx509_cert_free(peer->cert);
+ free_cms_alg(peer);
+ memset(peer, 0, sizeof(*peer));
+ free(peer);
+}
+
+/**
+ * Set the certificate that remote peer is using.
+ *
+ * @param peer peer info to update
+ * @param cert cerificate of the remote peer.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_peer
+ */
+
+int
+hx509_peer_info_set_cert(hx509_peer_info peer,
+ hx509_cert cert)
+{
+ if (peer->cert)
+ hx509_cert_free(peer->cert);
+ peer->cert = hx509_cert_ref(cert);
+ return 0;
+}
+
+/**
+ * Set the algorithms that the peer supports.
+ *
+ * @param context A hx509 context.
+ * @param peer the peer to set the new algorithms for
+ * @param val array of supported AlgorithmsIdentiers
+ * @param len length of array val.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_peer
+ */
+
+int
+hx509_peer_info_set_cms_algs(hx509_context context,
+ hx509_peer_info peer,
+ const AlgorithmIdentifier *val,
+ size_t len)
+{
+ size_t i;
+
+ free_cms_alg(peer);
+
+ peer->val = calloc(len, sizeof(*peer->val));
+ if (peer->val == NULL) {
+ peer->len = 0;
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+ peer->len = len;
+ for (i = 0; i < len; i++) {
+ int ret;
+ ret = copy_AlgorithmIdentifier(&val[i], &peer->val[i]);
+ if (ret) {
+ hx509_clear_error_string(context);
+ free_cms_alg(peer);
+ return ret;
+ }
+ }
+ return 0;
+}
+
+#if 0
+
+/*
+ * S/MIME
+ */
+
+int
+hx509_peer_info_parse_smime(hx509_peer_info peer,
+ const heim_octet_string *data)
+{
+ return 0;
+}
+
+int
+hx509_peer_info_unparse_smime(hx509_peer_info peer,
+ heim_octet_string *data)
+{
+ return 0;
+}
+
+/*
+ * For storing hx509_peer_info to be able to cache them.
+ */
+
+int
+hx509_peer_info_parse(hx509_peer_info peer,
+ const heim_octet_string *data)
+{
+ return 0;
+}
+
+int
+hx509_peer_info_unparse(hx509_peer_info peer,
+ heim_octet_string *data)
+{
+ return 0;
+}
+#endif
diff --git a/source4/heimdal/lib/hx509/pkcs10.asn1 b/source4/heimdal/lib/hx509/pkcs10.asn1
new file mode 100644
index 0000000000..f3fe37b1bf
--- /dev/null
+++ b/source4/heimdal/lib/hx509/pkcs10.asn1
@@ -0,0 +1,25 @@
+-- $Id$
+PKCS10 DEFINITIONS ::=
+
+BEGIN
+
+IMPORTS
+ Name, SubjectPublicKeyInfo, Attribute, AlgorithmIdentifier
+ FROM rfc2459;
+
+
+CertificationRequestInfo ::= SEQUENCE {
+ version INTEGER { pkcs10-v1(0) },
+ subject Name,
+ subjectPKInfo SubjectPublicKeyInfo,
+ attributes [0] IMPLICIT SET OF Attribute OPTIONAL
+}
+
+CertificationRequest ::= SEQUENCE {
+ certificationRequestInfo CertificationRequestInfo,
+ signatureAlgorithm AlgorithmIdentifier,
+ signature BIT STRING
+}
+
+END
+
diff --git a/source4/heimdal/lib/hx509/print.c b/source4/heimdal/lib/hx509/print.c
new file mode 100644
index 0000000000..38d103905f
--- /dev/null
+++ b/source4/heimdal/lib/hx509/print.c
@@ -0,0 +1,1037 @@
+/*
+ * Copyright (c) 2004 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hx_locl.h"
+RCSID("$Id$");
+
+/**
+ * @page page_print Hx509 printing functions
+ *
+ * See the library functions here: @ref hx509_print
+ */
+
+struct hx509_validate_ctx_data {
+ int flags;
+ hx509_vprint_func vprint_func;
+ void *ctx;
+};
+
+struct cert_status {
+ unsigned int selfsigned:1;
+ unsigned int isca:1;
+ unsigned int isproxy:1;
+ unsigned int haveSAN:1;
+ unsigned int haveIAN:1;
+ unsigned int haveSKI:1;
+ unsigned int haveAKI:1;
+ unsigned int haveCRLDP:1;
+};
+
+
+/*
+ *
+ */
+
+static int
+Time2string(const Time *T, char **str)
+{
+ time_t t;
+ char *s;
+ struct tm *tm;
+
+ *str = NULL;
+ t = _hx509_Time2time_t(T);
+ tm = gmtime (&t);
+ s = malloc(30);
+ if (s == NULL)
+ return ENOMEM;
+ strftime(s, 30, "%Y-%m-%d %H:%M:%S", tm);
+ *str = s;
+ return 0;
+}
+
+/**
+ * Helper function to print on stdout for:
+ * - hx509_oid_print(),
+ * - hx509_bitstring_print(),
+ * - hx509_validate_ctx_set_print().
+ *
+ * @param ctx the context to the print function. If the ctx is NULL,
+ * stdout is used.
+ * @param fmt the printing format.
+ * @param va the argumet list.
+ *
+ * @ingroup hx509_print
+ */
+
+void
+hx509_print_stdout(void *ctx, const char *fmt, va_list va)
+{
+ FILE *f = ctx;
+ if (f == NULL)
+ f = stdout;
+ vfprintf(f, fmt, va);
+}
+
+static void
+print_func(hx509_vprint_func func, void *ctx, const char *fmt, ...)
+{
+ va_list va;
+ va_start(va, fmt);
+ (*func)(ctx, fmt, va);
+ va_end(va);
+}
+
+/**
+ * Print a oid to a string.
+ *
+ * @param oid oid to print
+ * @param str allocated string, free with hx509_xfree().
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_print
+ */
+
+int
+hx509_oid_sprint(const heim_oid *oid, char **str)
+{
+ return der_print_heim_oid(oid, '.', str);
+}
+
+/**
+ * Print a oid using a hx509_vprint_func function. To print to stdout
+ * use hx509_print_stdout().
+ *
+ * @param oid oid to print
+ * @param func hx509_vprint_func to print with.
+ * @param ctx context variable to hx509_vprint_func function.
+ *
+ * @ingroup hx509_print
+ */
+
+void
+hx509_oid_print(const heim_oid *oid, hx509_vprint_func func, void *ctx)
+{
+ char *str;
+ hx509_oid_sprint(oid, &str);
+ print_func(func, ctx, "%s", str);
+ free(str);
+}
+
+/**
+ * Print a bitstring using a hx509_vprint_func function. To print to
+ * stdout use hx509_print_stdout().
+ *
+ * @param b bit string to print.
+ * @param func hx509_vprint_func to print with.
+ * @param ctx context variable to hx509_vprint_func function.
+ *
+ * @ingroup hx509_print
+ */
+
+void
+hx509_bitstring_print(const heim_bit_string *b,
+ hx509_vprint_func func, void *ctx)
+{
+ int i;
+ print_func(func, ctx, "\tlength: %d\n\t", b->length);
+ for (i = 0; i < (b->length + 7) / 8; i++)
+ print_func(func, ctx, "%02x%s%s",
+ ((unsigned char *)b->data)[i],
+ i < (b->length - 7) / 8
+ && (i == 0 || (i % 16) != 15) ? ":" : "",
+ i != 0 && (i % 16) == 15 ?
+ (i <= ((b->length + 7) / 8 - 2) ? "\n\t" : "\n"):"");
+}
+
+/**
+ * Print certificate usage for a certificate to a string.
+ *
+ * @param context A hx509 context.
+ * @param c a certificate print the keyusage for.
+ * @param s the return string with the keysage printed in to, free
+ * with hx509_xfree().
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_print
+ */
+
+int
+hx509_cert_keyusage_print(hx509_context context, hx509_cert c, char **s)
+{
+ KeyUsage ku;
+ char buf[256];
+ int ret;
+
+ *s = NULL;
+
+ ret = _hx509_cert_get_keyusage(context, c, &ku);
+ if (ret)
+ return ret;
+ unparse_flags(KeyUsage2int(ku), asn1_KeyUsage_units(), buf, sizeof(buf));
+ *s = strdup(buf);
+ if (*s == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+
+ return 0;
+}
+
+/*
+ *
+ */
+
+static void
+validate_vprint(void *c, const char *fmt, va_list va)
+{
+ hx509_validate_ctx ctx = c;
+ if (ctx->vprint_func == NULL)
+ return;
+ (ctx->vprint_func)(ctx->ctx, fmt, va);
+}
+
+static void
+validate_print(hx509_validate_ctx ctx, int flags, const char *fmt, ...)
+{
+ va_list va;
+ if ((ctx->flags & flags) == 0)
+ return;
+ va_start(va, fmt);
+ validate_vprint(ctx, fmt, va);
+ va_end(va);
+}
+
+/*
+ * Dont Care, SHOULD critical, SHOULD NOT critical, MUST critical,
+ * MUST NOT critical
+ */
+enum critical_flag { D_C = 0, S_C, S_N_C, M_C, M_N_C };
+
+static int
+check_Null(hx509_validate_ctx ctx,
+ struct cert_status *status,
+ enum critical_flag cf, const Extension *e)
+{
+ switch(cf) {
+ case D_C:
+ break;
+ case S_C:
+ if (!e->critical)
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "\tCritical not set on SHOULD\n");
+ break;
+ case S_N_C:
+ if (e->critical)
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "\tCritical set on SHOULD NOT\n");
+ break;
+ case M_C:
+ if (!e->critical)
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "\tCritical not set on MUST\n");
+ break;
+ case M_N_C:
+ if (e->critical)
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "\tCritical set on MUST NOT\n");
+ break;
+ default:
+ _hx509_abort("internal check_Null state error");
+ }
+ return 0;
+}
+
+static int
+check_subjectKeyIdentifier(hx509_validate_ctx ctx,
+ struct cert_status *status,
+ enum critical_flag cf,
+ const Extension *e)
+{
+ SubjectKeyIdentifier si;
+ size_t size;
+ int ret;
+
+ status->haveSKI = 1;
+ check_Null(ctx, status, cf, e);
+
+ ret = decode_SubjectKeyIdentifier(e->extnValue.data,
+ e->extnValue.length,
+ &si, &size);
+ if (ret) {
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "Decoding SubjectKeyIdentifier failed: %d", ret);
+ return 1;
+ }
+ if (size != e->extnValue.length) {
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "Decoding SKI ahve extra bits on the end");
+ return 1;
+ }
+ if (si.length == 0)
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "SKI is too short (0 bytes)");
+ if (si.length > 20)
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "SKI is too long");
+
+ {
+ char *id;
+ hex_encode(si.data, si.length, &id);
+ if (id) {
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
+ "\tsubject key id: %s\n", id);
+ free(id);
+ }
+ }
+
+ free_SubjectKeyIdentifier(&si);
+
+ return 0;
+}
+
+static int
+check_authorityKeyIdentifier(hx509_validate_ctx ctx,
+ struct cert_status *status,
+ enum critical_flag cf,
+ const Extension *e)
+{
+ AuthorityKeyIdentifier ai;
+ size_t size;
+ int ret;
+
+ status->haveAKI = 1;
+ check_Null(ctx, status, cf, e);
+
+ ret = decode_AuthorityKeyIdentifier(e->extnValue.data,
+ e->extnValue.length,
+ &ai, &size);
+ if (ret) {
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "Decoding AuthorityKeyIdentifier failed: %d", ret);
+ return 1;
+ }
+ if (size != e->extnValue.length) {
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "Decoding SKI ahve extra bits on the end");
+ return 1;
+ }
+
+ if (ai.keyIdentifier) {
+ char *id;
+ hex_encode(ai.keyIdentifier->data, ai.keyIdentifier->length, &id);
+ if (id) {
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
+ "\tauthority key id: %s\n", id);
+ free(id);
+ }
+ }
+
+ return 0;
+}
+
+static int
+check_extKeyUsage(hx509_validate_ctx ctx,
+ struct cert_status *status,
+ enum critical_flag cf,
+ const Extension *e)
+{
+ ExtKeyUsage eku;
+ size_t size, i;
+ int ret;
+
+ check_Null(ctx, status, cf, e);
+
+ ret = decode_ExtKeyUsage(e->extnValue.data,
+ e->extnValue.length,
+ &eku, &size);
+ if (ret) {
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "Decoding ExtKeyUsage failed: %d", ret);
+ return 1;
+ }
+ if (size != e->extnValue.length) {
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "Padding data in EKU");
+ free_ExtKeyUsage(&eku);
+ return 1;
+ }
+ if (eku.len == 0) {
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "ExtKeyUsage length is 0");
+ return 1;
+ }
+
+ for (i = 0; i < eku.len; i++) {
+ char *str;
+ ret = der_print_heim_oid (&eku.val[i], '.', &str);
+ if (ret) {
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "\tEKU: failed to print oid %d", i);
+ free_ExtKeyUsage(&eku);
+ return 1;
+ }
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
+ "\teku-%d: %s\n", i, str);;
+ free(str);
+ }
+
+ free_ExtKeyUsage(&eku);
+
+ return 0;
+}
+
+static int
+check_pkinit_san(hx509_validate_ctx ctx, heim_any *a)
+{
+ KRB5PrincipalName kn;
+ unsigned i;
+ size_t size;
+ int ret;
+
+ ret = decode_KRB5PrincipalName(a->data, a->length, &kn, &size);
+ if (ret) {
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "Decoding kerberos name in SAN failed: %d", ret);
+ return 1;
+ }
+
+ if (size != a->length) {
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "Decoding kerberos name have extra bits on the end");
+ return 1;
+ }
+
+ /* print kerberos principal, add code to quote / within components */
+ for (i = 0; i < kn.principalName.name_string.len; i++) {
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "%s",
+ kn.principalName.name_string.val[i]);
+ if (i + 1 < kn.principalName.name_string.len)
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "/");
+ }
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "@");
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "%s", kn.realm);
+
+ free_KRB5PrincipalName(&kn);
+ return 0;
+}
+
+static int
+check_utf8_string_san(hx509_validate_ctx ctx, heim_any *a)
+{
+ PKIXXmppAddr jid;
+ size_t size;
+ int ret;
+
+ ret = decode_PKIXXmppAddr(a->data, a->length, &jid, &size);
+ if (ret) {
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "Decoding JID in SAN failed: %d", ret);
+ return 1;
+ }
+
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "%s", jid);
+ free_PKIXXmppAddr(&jid);
+
+ return 0;
+}
+
+static int
+check_altnull(hx509_validate_ctx ctx, heim_any *a)
+{
+ return 0;
+}
+
+static int
+check_CRLDistributionPoints(hx509_validate_ctx ctx,
+ struct cert_status *status,
+ enum critical_flag cf,
+ const Extension *e)
+{
+ CRLDistributionPoints dp;
+ size_t size;
+ int ret, i;
+
+ check_Null(ctx, status, cf, e);
+
+ ret = decode_CRLDistributionPoints(e->extnValue.data,
+ e->extnValue.length,
+ &dp, &size);
+ if (ret) {
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "Decoding CRL Distribution Points failed: %d\n", ret);
+ return 1;
+ }
+
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "CRL Distribution Points:\n");
+ for (i = 0 ; i < dp.len; i++) {
+ if (dp.val[i].distributionPoint) {
+ DistributionPointName dpname;
+ heim_any *data = dp.val[i].distributionPoint;
+ int j;
+
+ ret = decode_DistributionPointName(data->data, data->length,
+ &dpname, NULL);
+ if (ret) {
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "Failed to parse CRL Distribution Point Name: %d\n", ret);
+ continue;
+ }
+
+ switch (dpname.element) {
+ case choice_DistributionPointName_fullName:
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "Fullname:\n");
+
+ for (j = 0 ; j < dpname.u.fullName.len; j++) {
+ char *s;
+ GeneralName *name = &dpname.u.fullName.val[j];
+
+ ret = hx509_general_name_unparse(name, &s);
+ if (ret == 0 && s != NULL) {
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE, " %s\n", s);
+ free(s);
+ }
+ }
+ break;
+ case choice_DistributionPointName_nameRelativeToCRLIssuer:
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
+ "Unknown nameRelativeToCRLIssuer");
+ break;
+ default:
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "Unknown DistributionPointName");
+ break;
+ }
+ free_DistributionPointName(&dpname);
+ }
+ }
+ free_CRLDistributionPoints(&dp);
+
+ status->haveCRLDP = 1;
+
+ return 0;
+}
+
+
+struct {
+ const char *name;
+ const heim_oid *(*oid)(void);
+ int (*func)(hx509_validate_ctx, heim_any *);
+} check_altname[] = {
+ { "pk-init", oid_id_pkinit_san, check_pkinit_san },
+ { "jabber", oid_id_pkix_on_xmppAddr, check_utf8_string_san },
+ { "dns-srv", oid_id_pkix_on_dnsSRV, check_altnull },
+ { "card-id", oid_id_uspkicommon_card_id, check_altnull },
+ { "Microsoft NT-PRINCIPAL-NAME", oid_id_pkinit_ms_san, check_utf8_string_san }
+};
+
+static int
+check_altName(hx509_validate_ctx ctx,
+ struct cert_status *status,
+ const char *name,
+ enum critical_flag cf,
+ const Extension *e)
+{
+ GeneralNames gn;
+ size_t size;
+ int ret, i;
+
+ check_Null(ctx, status, cf, e);
+
+ if (e->extnValue.length == 0) {
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "%sAltName empty, not allowed", name);
+ return 1;
+ }
+ ret = decode_GeneralNames(e->extnValue.data, e->extnValue.length,
+ &gn, &size);
+ if (ret) {
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "\tret = %d while decoding %s GeneralNames\n",
+ ret, name);
+ return 1;
+ }
+ if (gn.len == 0) {
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "%sAltName generalName empty, not allowed\n", name);
+ return 1;
+ }
+
+ for (i = 0; i < gn.len; i++) {
+ switch (gn.val[i].element) {
+ case choice_GeneralName_otherName: {
+ unsigned j;
+
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
+ "%sAltName otherName ", name);
+
+ for (j = 0; j < sizeof(check_altname)/sizeof(check_altname[0]); j++) {
+ if (der_heim_oid_cmp((*check_altname[j].oid)(),
+ &gn.val[i].u.otherName.type_id) != 0)
+ continue;
+
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "%s: ",
+ check_altname[j].name);
+ (*check_altname[j].func)(ctx, &gn.val[i].u.otherName.value);
+ break;
+ }
+ if (j == sizeof(check_altname)/sizeof(check_altname[0])) {
+ hx509_oid_print(&gn.val[i].u.otherName.type_id,
+ validate_vprint, ctx);
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE, " unknown");
+ }
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "\n");
+ break;
+ }
+ default: {
+ char *s;
+ ret = hx509_general_name_unparse(&gn.val[i], &s);
+ if (ret) {
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "ret = %d unparsing GeneralName\n", ret);
+ return 1;
+ }
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "%s\n", s);
+ free(s);
+ break;
+ }
+ }
+ }
+
+ free_GeneralNames(&gn);
+
+ return 0;
+}
+
+static int
+check_subjectAltName(hx509_validate_ctx ctx,
+ struct cert_status *status,
+ enum critical_flag cf,
+ const Extension *e)
+{
+ status->haveSAN = 1;
+ return check_altName(ctx, status, "subject", cf, e);
+}
+
+static int
+check_issuerAltName(hx509_validate_ctx ctx,
+ struct cert_status *status,
+ enum critical_flag cf,
+ const Extension *e)
+{
+ status->haveIAN = 1;
+ return check_altName(ctx, status, "issuer", cf, e);
+}
+
+
+static int
+check_basicConstraints(hx509_validate_ctx ctx,
+ struct cert_status *status,
+ enum critical_flag cf,
+ const Extension *e)
+{
+ BasicConstraints b;
+ size_t size;
+ int ret;
+
+ check_Null(ctx, status, cf, e);
+
+ ret = decode_BasicConstraints(e->extnValue.data, e->extnValue.length,
+ &b, &size);
+ if (ret) {
+ printf("\tret = %d while decoding BasicConstraints\n", ret);
+ return 0;
+ }
+ if (size != e->extnValue.length)
+ printf("\tlength of der data isn't same as extension\n");
+
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
+ "\tis %sa CA\n", b.cA && *b.cA ? "" : "NOT ");
+ if (b.pathLenConstraint)
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
+ "\tpathLenConstraint: %d\n", *b.pathLenConstraint);
+
+ if (b.cA) {
+ if (*b.cA) {
+ if (!e->critical)
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "Is a CA and not BasicConstraints CRITICAL\n");
+ status->isca = 1;
+ }
+ else
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "cA is FALSE, not allowed to be\n");
+ }
+ free_BasicConstraints(&b);
+
+ return 0;
+}
+
+static int
+check_proxyCertInfo(hx509_validate_ctx ctx,
+ struct cert_status *status,
+ enum critical_flag cf,
+ const Extension *e)
+{
+ check_Null(ctx, status, cf, e);
+ status->isproxy = 1;
+ return 0;
+}
+
+static int
+check_authorityInfoAccess(hx509_validate_ctx ctx,
+ struct cert_status *status,
+ enum critical_flag cf,
+ const Extension *e)
+{
+ AuthorityInfoAccessSyntax aia;
+ size_t size;
+ int ret, i;
+
+ check_Null(ctx, status, cf, e);
+
+ ret = decode_AuthorityInfoAccessSyntax(e->extnValue.data,
+ e->extnValue.length,
+ &aia, &size);
+ if (ret) {
+ printf("\tret = %d while decoding AuthorityInfoAccessSyntax\n", ret);
+ return 0;
+ }
+
+ for (i = 0; i < aia.len; i++) {
+ char *str;
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
+ "\ttype: ");
+ hx509_oid_print(&aia.val[i].accessMethod, validate_vprint, ctx);
+ hx509_general_name_unparse(&aia.val[i].accessLocation, &str);
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
+ "\n\tdirname: %s\n", str);
+ free(str);
+ }
+ free_AuthorityInfoAccessSyntax(&aia);
+
+ return 0;
+}
+
+/*
+ *
+ */
+
+struct {
+ const char *name;
+ const heim_oid *(*oid)(void);
+ int (*func)(hx509_validate_ctx ctx,
+ struct cert_status *status,
+ enum critical_flag cf,
+ const Extension *);
+ enum critical_flag cf;
+} check_extension[] = {
+#define ext(name, checkname) #name, &oid_id_x509_ce_##name, check_##checkname
+ { ext(subjectDirectoryAttributes, Null), M_N_C },
+ { ext(subjectKeyIdentifier, subjectKeyIdentifier), M_N_C },
+ { ext(keyUsage, Null), S_C },
+ { ext(subjectAltName, subjectAltName), M_N_C },
+ { ext(issuerAltName, issuerAltName), S_N_C },
+ { ext(basicConstraints, basicConstraints), D_C },
+ { ext(cRLNumber, Null), M_N_C },
+ { ext(cRLReason, Null), M_N_C },
+ { ext(holdInstructionCode, Null), M_N_C },
+ { ext(invalidityDate, Null), M_N_C },
+ { ext(deltaCRLIndicator, Null), M_C },
+ { ext(issuingDistributionPoint, Null), M_C },
+ { ext(certificateIssuer, Null), M_C },
+ { ext(nameConstraints, Null), M_C },
+ { ext(cRLDistributionPoints, CRLDistributionPoints), S_N_C },
+ { ext(certificatePolicies, Null) },
+ { ext(policyMappings, Null), M_N_C },
+ { ext(authorityKeyIdentifier, authorityKeyIdentifier), M_N_C },
+ { ext(policyConstraints, Null), D_C },
+ { ext(extKeyUsage, extKeyUsage), D_C },
+ { ext(freshestCRL, Null), M_N_C },
+ { ext(inhibitAnyPolicy, Null), M_C },
+#undef ext
+#define ext(name, checkname) #name, &oid_id_pkix_pe_##name, check_##checkname
+ { ext(proxyCertInfo, proxyCertInfo), M_C },
+ { ext(authorityInfoAccess, authorityInfoAccess), M_C },
+#undef ext
+ { "US Fed PKI - PIV Interim", oid_id_uspkicommon_piv_interim,
+ check_Null, D_C },
+ { "Netscape cert comment", oid_id_netscape_cert_comment,
+ check_Null, D_C },
+ { NULL }
+};
+
+/**
+ * Allocate a hx509 validation/printing context.
+ *
+ * @param context A hx509 context.
+ * @param ctx a new allocated hx509 validation context, free with
+ * hx509_validate_ctx_free().
+
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_print
+ */
+
+int
+hx509_validate_ctx_init(hx509_context context, hx509_validate_ctx *ctx)
+{
+ *ctx = malloc(sizeof(**ctx));
+ if (*ctx == NULL)
+ return ENOMEM;
+ memset(*ctx, 0, sizeof(**ctx));
+ return 0;
+}
+
+/**
+ * Set the printing functions for the validation context.
+ *
+ * @param ctx a hx509 valication context.
+ * @param func the printing function to usea.
+ * @param c the context variable to the printing function.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_print
+ */
+
+void
+hx509_validate_ctx_set_print(hx509_validate_ctx ctx,
+ hx509_vprint_func func,
+ void *c)
+{
+ ctx->vprint_func = func;
+ ctx->ctx = c;
+}
+
+/**
+ * Add flags to control the behaivor of the hx509_validate_cert()
+ * function.
+ *
+ * @param ctx A hx509 validation context.
+ * @param flags flags to add to the validation context.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_print
+ */
+
+void
+hx509_validate_ctx_add_flags(hx509_validate_ctx ctx, int flags)
+{
+ ctx->flags |= flags;
+}
+
+/**
+ * Free an hx509 validate context.
+ *
+ * @param ctx the hx509 validate context to free.
+ *
+ * @ingroup hx509_print
+ */
+
+void
+hx509_validate_ctx_free(hx509_validate_ctx ctx)
+{
+ free(ctx);
+}
+
+/**
+ * Validate/Print the status of the certificate.
+ *
+ * @param context A hx509 context.
+ * @param ctx A hx509 validation context.
+ * @param cert the cerificate to validate/print.
+
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_print
+ */
+
+int
+hx509_validate_cert(hx509_context context,
+ hx509_validate_ctx ctx,
+ hx509_cert cert)
+{
+ Certificate *c = _hx509_get_cert(cert);
+ TBSCertificate *t = &c->tbsCertificate;
+ hx509_name issuer, subject;
+ char *str;
+ struct cert_status status;
+ int ret;
+
+ memset(&status, 0, sizeof(status));
+
+ if (_hx509_cert_get_version(c) != 3)
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
+ "Not version 3 certificate\n");
+
+ if ((t->version == NULL || *t->version < 2) && t->extensions)
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "Not version 3 certificate with extensions\n");
+
+ if (_hx509_cert_get_version(c) >= 3 && t->extensions == NULL)
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "Version 3 certificate without extensions\n");
+
+ ret = hx509_cert_get_subject(cert, &subject);
+ if (ret) abort();
+ hx509_name_to_string(subject, &str);
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
+ "subject name: %s\n", str);
+ free(str);
+
+ ret = hx509_cert_get_issuer(cert, &issuer);
+ if (ret) abort();
+ hx509_name_to_string(issuer, &str);
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
+ "issuer name: %s\n", str);
+ free(str);
+
+ if (hx509_name_cmp(subject, issuer) == 0) {
+ status.selfsigned = 1;
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
+ "\tis a self-signed certificate\n");
+ }
+
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
+ "Validity:\n");
+
+ Time2string(&t->validity.notBefore, &str);
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "\tnotBefore %s\n", str);
+ free(str);
+ Time2string(&t->validity.notAfter, &str);
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "\tnotAfter %s\n", str);
+ free(str);
+
+ if (t->extensions) {
+ int i, j;
+
+ if (t->extensions->len == 0) {
+ validate_print(ctx,
+ HX509_VALIDATE_F_VALIDATE|HX509_VALIDATE_F_VERBOSE,
+ "The empty extensions list is not "
+ "allowed by PKIX\n");
+ }
+
+ for (i = 0; i < t->extensions->len; i++) {
+
+ for (j = 0; check_extension[j].name; j++)
+ if (der_heim_oid_cmp((*check_extension[j].oid)(),
+ &t->extensions->val[i].extnID) == 0)
+ break;
+ if (check_extension[j].name == NULL) {
+ int flags = HX509_VALIDATE_F_VERBOSE;
+ if (t->extensions->val[i].critical)
+ flags |= HX509_VALIDATE_F_VALIDATE;
+ validate_print(ctx, flags, "don't know what ");
+ if (t->extensions->val[i].critical)
+ validate_print(ctx, flags, "and is CRITICAL ");
+ if (ctx->flags & flags)
+ hx509_oid_print(&t->extensions->val[i].extnID,
+ validate_vprint, ctx);
+ validate_print(ctx, flags, " is\n");
+ continue;
+ }
+ validate_print(ctx,
+ HX509_VALIDATE_F_VALIDATE|HX509_VALIDATE_F_VERBOSE,
+ "checking extention: %s\n",
+ check_extension[j].name);
+ (*check_extension[j].func)(ctx,
+ &status,
+ check_extension[j].cf,
+ &t->extensions->val[i]);
+ }
+ } else
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "no extentions\n");
+
+ if (status.isca) {
+ if (!status.haveSKI)
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "CA certificate have no SubjectKeyIdentifier\n");
+
+ } else {
+ if (!status.haveAKI)
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "Is not CA and doesn't have "
+ "AuthorityKeyIdentifier\n");
+ }
+
+
+ if (!status.haveSKI)
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "Doesn't have SubjectKeyIdentifier\n");
+
+ if (status.isproxy && status.isca)
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "Proxy and CA at the same time!\n");
+
+ if (status.isproxy) {
+ if (status.haveSAN)
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "Proxy and have SAN\n");
+ if (status.haveIAN)
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "Proxy and have IAN\n");
+ }
+
+ if (hx509_name_is_null_p(subject) && !status.haveSAN)
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "NULL subject DN and doesn't have a SAN\n");
+
+ if (!status.selfsigned && !status.haveCRLDP)
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "Not a CA nor PROXY and doesn't have"
+ "CRL Dist Point\n");
+
+ if (status.selfsigned) {
+ ret = _hx509_verify_signature_bitstring(context,
+ c,
+ &c->signatureAlgorithm,
+ &c->tbsCertificate._save,
+ &c->signatureValue);
+ if (ret == 0)
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE,
+ "Self-signed certificate was self-signed\n");
+ else
+ validate_print(ctx, HX509_VALIDATE_F_VALIDATE,
+ "Self-signed certificate NOT really self-signed!\n");
+ }
+
+ hx509_name_free(&subject);
+ hx509_name_free(&issuer);
+
+ return 0;
+}
diff --git a/source4/heimdal/lib/hx509/req.c b/source4/heimdal/lib/hx509/req.c
new file mode 100644
index 0000000000..9836777143
--- /dev/null
+++ b/source4/heimdal/lib/hx509/req.c
@@ -0,0 +1,325 @@
+/*
+ * Copyright (c) 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hx_locl.h"
+#include <pkcs10_asn1.h>
+RCSID("$Id$");
+
+struct hx509_request_data {
+ hx509_name name;
+ SubjectPublicKeyInfo key;
+ ExtKeyUsage eku;
+ GeneralNames san;
+};
+
+/*
+ *
+ */
+
+int
+_hx509_request_init(hx509_context context, hx509_request *req)
+{
+ *req = calloc(1, sizeof(**req));
+ if (*req == NULL)
+ return ENOMEM;
+
+ return 0;
+}
+
+void
+_hx509_request_free(hx509_request *req)
+{
+ if ((*req)->name)
+ hx509_name_free(&(*req)->name);
+ free_SubjectPublicKeyInfo(&(*req)->key);
+ free_ExtKeyUsage(&(*req)->eku);
+ free_GeneralNames(&(*req)->san);
+ memset(*req, 0, sizeof(**req));
+ free(*req);
+ *req = NULL;
+}
+
+int
+_hx509_request_set_name(hx509_context context,
+ hx509_request req,
+ hx509_name name)
+{
+ if (req->name)
+ hx509_name_free(&req->name);
+ if (name) {
+ int ret = hx509_name_copy(context, name, &req->name);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+int
+_hx509_request_get_name(hx509_context context,
+ hx509_request req,
+ hx509_name *name)
+{
+ if (req->name == NULL) {
+ hx509_set_error_string(context, 0, EINVAL, "Request have no name");
+ return EINVAL;
+ }
+ return hx509_name_copy(context, req->name, name);
+}
+
+int
+_hx509_request_set_SubjectPublicKeyInfo(hx509_context context,
+ hx509_request req,
+ const SubjectPublicKeyInfo *key)
+{
+ free_SubjectPublicKeyInfo(&req->key);
+ return copy_SubjectPublicKeyInfo(key, &req->key);
+}
+
+int
+_hx509_request_get_SubjectPublicKeyInfo(hx509_context context,
+ hx509_request req,
+ SubjectPublicKeyInfo *key)
+{
+ return copy_SubjectPublicKeyInfo(&req->key, key);
+}
+
+int
+_hx509_request_add_eku(hx509_context context,
+ hx509_request req,
+ const heim_oid *oid)
+{
+ void *val;
+ int ret;
+
+ val = realloc(req->eku.val, sizeof(req->eku.val[0]) * (req->eku.len + 1));
+ if (val == NULL)
+ return ENOMEM;
+ req->eku.val = val;
+
+ ret = der_copy_oid(oid, &req->eku.val[req->eku.len]);
+ if (ret)
+ return ret;
+
+ req->eku.len += 1;
+
+ return 0;
+}
+
+int
+_hx509_request_add_dns_name(hx509_context context,
+ hx509_request req,
+ const char *hostname)
+{
+ GeneralName name;
+
+ memset(&name, 0, sizeof(name));
+ name.element = choice_GeneralName_dNSName;
+ name.u.dNSName = rk_UNCONST(hostname);
+
+ return add_GeneralNames(&req->san, &name);
+}
+
+int
+_hx509_request_add_email(hx509_context context,
+ hx509_request req,
+ const char *email)
+{
+ GeneralName name;
+
+ memset(&name, 0, sizeof(name));
+ name.element = choice_GeneralName_rfc822Name;
+ name.u.dNSName = rk_UNCONST(email);
+
+ return add_GeneralNames(&req->san, &name);
+}
+
+
+
+int
+_hx509_request_to_pkcs10(hx509_context context,
+ const hx509_request req,
+ const hx509_private_key signer,
+ heim_octet_string *request)
+{
+ CertificationRequest r;
+ heim_octet_string data, os;
+ int ret;
+ size_t size;
+
+ if (req->name == NULL) {
+ hx509_set_error_string(context, 0, EINVAL,
+ "PKCS10 needs to have a subject");
+ return EINVAL;
+ }
+
+ memset(&r, 0, sizeof(r));
+ memset(request, 0, sizeof(*request));
+
+ r.certificationRequestInfo.version = pkcs10_v1;
+
+ ret = copy_Name(&req->name->der_name,
+ &r.certificationRequestInfo.subject);
+ if (ret)
+ goto out;
+ ret = copy_SubjectPublicKeyInfo(&req->key,
+ &r.certificationRequestInfo.subjectPKInfo);
+ if (ret)
+ goto out;
+ r.certificationRequestInfo.attributes =
+ calloc(1, sizeof(*r.certificationRequestInfo.attributes));
+ if (r.certificationRequestInfo.attributes == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+
+ ASN1_MALLOC_ENCODE(CertificationRequestInfo, data.data, data.length,
+ &r.certificationRequestInfo, &size, ret);
+ if (ret)
+ goto out;
+ if (data.length != size)
+ abort();
+
+ ret = _hx509_create_signature(context,
+ signer,
+ _hx509_crypto_default_sig_alg,
+ &data,
+ &r.signatureAlgorithm,
+ &os);
+ free(data.data);
+ if (ret)
+ goto out;
+ r.signature.data = os.data;
+ r.signature.length = os.length * 8;
+
+ ASN1_MALLOC_ENCODE(CertificationRequest, data.data, data.length,
+ &r, &size, ret);
+ if (ret)
+ goto out;
+ if (data.length != size)
+ abort();
+
+ *request = data;
+
+out:
+ free_CertificationRequest(&r);
+
+ return ret;
+}
+
+int
+_hx509_request_parse(hx509_context context,
+ const char *path,
+ hx509_request *req)
+{
+ CertificationRequest r;
+ CertificationRequestInfo *rinfo;
+ hx509_name subject;
+ size_t len, size;
+ void *p;
+ int ret;
+
+ if (strncmp(path, "PKCS10:", 7) != 0) {
+ hx509_set_error_string(context, 0, HX509_UNSUPPORTED_OPERATION,
+ "unsupport type in %s", path);
+ return HX509_UNSUPPORTED_OPERATION;
+ }
+ path += 7;
+
+ /* XXX PEM request */
+
+ ret = rk_undumpdata(path, &p, &len);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Failed to map file %s", path);
+ return ret;
+ }
+
+ ret = decode_CertificationRequest(p, len, &r, &size);
+ rk_xfree(p);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Failed to decode %s", path);
+ return ret;
+ }
+
+ ret = _hx509_request_init(context, req);
+ if (ret) {
+ free_CertificationRequest(&r);
+ return ret;
+ }
+
+ rinfo = &r.certificationRequestInfo;
+
+ ret = _hx509_request_set_SubjectPublicKeyInfo(context, *req,
+ &rinfo->subjectPKInfo);
+ if (ret) {
+ free_CertificationRequest(&r);
+ _hx509_request_free(req);
+ return ret;
+ }
+
+ ret = _hx509_name_from_Name(&rinfo->subject, &subject);
+ if (ret) {
+ free_CertificationRequest(&r);
+ _hx509_request_free(req);
+ return ret;
+ }
+ ret = _hx509_request_set_name(context, *req, subject);
+ hx509_name_free(&subject);
+ free_CertificationRequest(&r);
+ if (ret) {
+ _hx509_request_free(req);
+ return ret;
+ }
+
+ return 0;
+}
+
+
+int
+_hx509_request_print(hx509_context context, hx509_request req, FILE *f)
+{
+ int ret;
+
+ if (req->name) {
+ char *subject;
+ ret = hx509_name_to_string(req->name, &subject);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Failed to print name");
+ return ret;
+ }
+ fprintf(f, "name: %s\n", subject);
+ free(subject);
+ }
+
+ return 0;
+}
+
diff --git a/source4/heimdal/lib/hx509/revoke.c b/source4/heimdal/lib/hx509/revoke.c
new file mode 100644
index 0000000000..3f35b0d190
--- /dev/null
+++ b/source4/heimdal/lib/hx509/revoke.c
@@ -0,0 +1,1539 @@
+/*
+ * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/**
+ * @page page_revoke Revocation methods
+ *
+ * There are two revocation method for PKIX/X.509: CRL and OCSP.
+ * Revocation is needed if the private key is lost and
+ * stolen. Depending on how picky you are, you might want to make
+ * revocation for destroyed private keys too (smartcard broken), but
+ * that should not be a problem.
+ *
+ * CRL is a list of certifiates that have expired.
+ *
+ * OCSP is an online checking method where the requestor sends a list
+ * of certificates to the OCSP server to return a signed reply if they
+ * are valid or not. Some services sends a OCSP reply as part of the
+ * hand-shake to make the revoktion decision simpler/faster for the
+ * client.
+ */
+
+#include "hx_locl.h"
+RCSID("$Id$");
+
+struct revoke_crl {
+ char *path;
+ time_t last_modfied;
+ CRLCertificateList crl;
+ int verified;
+ int failed_verify;
+};
+
+struct revoke_ocsp {
+ char *path;
+ time_t last_modfied;
+ OCSPBasicOCSPResponse ocsp;
+ hx509_certs certs;
+ hx509_cert signer;
+};
+
+
+struct hx509_revoke_ctx_data {
+ unsigned int ref;
+ struct {
+ struct revoke_crl *val;
+ size_t len;
+ } crls;
+ struct {
+ struct revoke_ocsp *val;
+ size_t len;
+ } ocsps;
+};
+
+/**
+ * Allocate a revokation context. Free with hx509_revoke_free().
+ *
+ * @param context A hx509 context.
+ * @param ctx returns a newly allocated revokation context.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_revoke
+ */
+
+int
+hx509_revoke_init(hx509_context context, hx509_revoke_ctx *ctx)
+{
+ *ctx = calloc(1, sizeof(**ctx));
+ if (*ctx == NULL)
+ return ENOMEM;
+
+ (*ctx)->ref = 1;
+ (*ctx)->crls.len = 0;
+ (*ctx)->crls.val = NULL;
+ (*ctx)->ocsps.len = 0;
+ (*ctx)->ocsps.val = NULL;
+
+ return 0;
+}
+
+hx509_revoke_ctx
+_hx509_revoke_ref(hx509_revoke_ctx ctx)
+{
+ if (ctx == NULL)
+ return NULL;
+ if (ctx->ref == 0)
+ _hx509_abort("revoke ctx refcount == 0 on ref");
+ ctx->ref++;
+ if (ctx->ref == UINT_MAX)
+ _hx509_abort("revoke ctx refcount == UINT_MAX on ref");
+ return ctx;
+}
+
+static void
+free_ocsp(struct revoke_ocsp *ocsp)
+{
+ free(ocsp->path);
+ free_OCSPBasicOCSPResponse(&ocsp->ocsp);
+ hx509_certs_free(&ocsp->certs);
+ hx509_cert_free(ocsp->signer);
+}
+
+/**
+ * Free a hx509 revokation context.
+ *
+ * @param ctx context to be freed
+ *
+ * @ingroup hx509_revoke
+ */
+
+void
+hx509_revoke_free(hx509_revoke_ctx *ctx)
+{
+ size_t i ;
+
+ if (ctx == NULL || *ctx == NULL)
+ return;
+
+ if ((*ctx)->ref == 0)
+ _hx509_abort("revoke ctx refcount == 0 on free");
+ if (--(*ctx)->ref > 0)
+ return;
+
+ for (i = 0; i < (*ctx)->crls.len; i++) {
+ free((*ctx)->crls.val[i].path);
+ free_CRLCertificateList(&(*ctx)->crls.val[i].crl);
+ }
+
+ for (i = 0; i < (*ctx)->ocsps.len; i++)
+ free_ocsp(&(*ctx)->ocsps.val[i]);
+ free((*ctx)->ocsps.val);
+
+ free((*ctx)->crls.val);
+
+ memset(*ctx, 0, sizeof(**ctx));
+ free(*ctx);
+ *ctx = NULL;
+}
+
+static int
+verify_ocsp(hx509_context context,
+ struct revoke_ocsp *ocsp,
+ time_t time_now,
+ hx509_certs certs,
+ hx509_cert parent)
+{
+ hx509_cert signer = NULL;
+ hx509_query q;
+ int ret;
+
+ _hx509_query_clear(&q);
+
+ /*
+ * Need to match on issuer too in case there are two CA that have
+ * issued the same name to a certificate. One example of this is
+ * the www.openvalidation.org test's ocsp validator.
+ */
+
+ q.match = HX509_QUERY_MATCH_ISSUER_NAME;
+ q.issuer_name = &_hx509_get_cert(parent)->tbsCertificate.issuer;
+
+ switch(ocsp->ocsp.tbsResponseData.responderID.element) {
+ case choice_OCSPResponderID_byName:
+ q.match |= HX509_QUERY_MATCH_SUBJECT_NAME;
+ q.subject_name = &ocsp->ocsp.tbsResponseData.responderID.u.byName;
+ break;
+ case choice_OCSPResponderID_byKey:
+ q.match |= HX509_QUERY_MATCH_KEY_HASH_SHA1;
+ q.keyhash_sha1 = &ocsp->ocsp.tbsResponseData.responderID.u.byKey;
+ break;
+ }
+
+ ret = hx509_certs_find(context, certs, &q, &signer);
+ if (ret && ocsp->certs)
+ ret = hx509_certs_find(context, ocsp->certs, &q, &signer);
+ if (ret)
+ goto out;
+
+ /*
+ * If signer certificate isn't the CA certificate, lets check the
+ * it is the CA that signed the signer certificate and the OCSP EKU
+ * is set.
+ */
+ if (hx509_cert_cmp(signer, parent) != 0) {
+ Certificate *p = _hx509_get_cert(parent);
+ Certificate *s = _hx509_get_cert(signer);
+
+ ret = _hx509_cert_is_parent_cmp(s, p, 0);
+ if (ret != 0) {
+ ret = HX509_PARENT_NOT_CA;
+ hx509_set_error_string(context, 0, ret, "Revoke OCSP signer is "
+ "doesn't have CA as signer certificate");
+ goto out;
+ }
+
+ ret = _hx509_verify_signature_bitstring(context,
+ p,
+ &s->signatureAlgorithm,
+ &s->tbsCertificate._save,
+ &s->signatureValue);
+ if (ret) {
+ hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
+ "OCSP signer signature invalid");
+ goto out;
+ }
+
+ ret = hx509_cert_check_eku(context, signer,
+ oid_id_pkix_kp_OCSPSigning(), 0);
+ if (ret)
+ goto out;
+ }
+
+ ret = _hx509_verify_signature_bitstring(context,
+ _hx509_get_cert(signer),
+ &ocsp->ocsp.signatureAlgorithm,
+ &ocsp->ocsp.tbsResponseData._save,
+ &ocsp->ocsp.signature);
+ if (ret) {
+ hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
+ "OCSP signature invalid");
+ goto out;
+ }
+
+ ocsp->signer = signer;
+ signer = NULL;
+out:
+ if (signer)
+ hx509_cert_free(signer);
+
+ return ret;
+}
+
+/*
+ *
+ */
+
+static int
+parse_ocsp_basic(const void *data, size_t length, OCSPBasicOCSPResponse *basic)
+{
+ OCSPResponse resp;
+ size_t size;
+ int ret;
+
+ memset(basic, 0, sizeof(*basic));
+
+ ret = decode_OCSPResponse(data, length, &resp, &size);
+ if (ret)
+ return ret;
+ if (length != size) {
+ free_OCSPResponse(&resp);
+ return ASN1_EXTRA_DATA;
+ }
+
+ switch (resp.responseStatus) {
+ case successful:
+ break;
+ default:
+ free_OCSPResponse(&resp);
+ return HX509_REVOKE_WRONG_DATA;
+ }
+
+ if (resp.responseBytes == NULL) {
+ free_OCSPResponse(&resp);
+ return EINVAL;
+ }
+
+ ret = der_heim_oid_cmp(&resp.responseBytes->responseType,
+ oid_id_pkix_ocsp_basic());
+ if (ret != 0) {
+ free_OCSPResponse(&resp);
+ return HX509_REVOKE_WRONG_DATA;
+ }
+
+ ret = decode_OCSPBasicOCSPResponse(resp.responseBytes->response.data,
+ resp.responseBytes->response.length,
+ basic,
+ &size);
+ if (ret) {
+ free_OCSPResponse(&resp);
+ return ret;
+ }
+ if (size != resp.responseBytes->response.length) {
+ free_OCSPResponse(&resp);
+ free_OCSPBasicOCSPResponse(basic);
+ return ASN1_EXTRA_DATA;
+ }
+ free_OCSPResponse(&resp);
+
+ return 0;
+}
+
+/*
+ *
+ */
+
+static int
+load_ocsp(hx509_context context, struct revoke_ocsp *ocsp)
+{
+ OCSPBasicOCSPResponse basic;
+ hx509_certs certs = NULL;
+ size_t length;
+ struct stat sb;
+ void *data;
+ int ret;
+
+ ret = rk_undumpdata(ocsp->path, &data, &length);
+ if (ret)
+ return ret;
+
+ ret = stat(ocsp->path, &sb);
+ if (ret)
+ return errno;
+
+ ret = parse_ocsp_basic(data, length, &basic);
+ rk_xfree(data);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to parse OCSP response");
+ return ret;
+ }
+
+ if (basic.certs) {
+ int i;
+
+ ret = hx509_certs_init(context, "MEMORY:ocsp-certs", 0,
+ NULL, &certs);
+ if (ret) {
+ free_OCSPBasicOCSPResponse(&basic);
+ return ret;
+ }
+
+ for (i = 0; i < basic.certs->len; i++) {
+ hx509_cert c;
+
+ ret = hx509_cert_init(context, &basic.certs->val[i], &c);
+ if (ret)
+ continue;
+
+ ret = hx509_certs_add(context, certs, c);
+ hx509_cert_free(c);
+ if (ret)
+ continue;
+ }
+ }
+
+ ocsp->last_modfied = sb.st_mtime;
+
+ free_OCSPBasicOCSPResponse(&ocsp->ocsp);
+ hx509_certs_free(&ocsp->certs);
+ hx509_cert_free(ocsp->signer);
+
+ ocsp->ocsp = basic;
+ ocsp->certs = certs;
+ ocsp->signer = NULL;
+
+ return 0;
+}
+
+/**
+ * Add a OCSP file to the revokation context.
+ *
+ * @param context hx509 context
+ * @param ctx hx509 revokation context
+ * @param path path to file that is going to be added to the context.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_revoke
+ */
+
+int
+hx509_revoke_add_ocsp(hx509_context context,
+ hx509_revoke_ctx ctx,
+ const char *path)
+{
+ void *data;
+ int ret;
+ size_t i;
+
+ if (strncmp(path, "FILE:", 5) != 0) {
+ hx509_set_error_string(context, 0, HX509_UNSUPPORTED_OPERATION,
+ "unsupport type in %s", path);
+ return HX509_UNSUPPORTED_OPERATION;
+ }
+
+ path += 5;
+
+ for (i = 0; i < ctx->ocsps.len; i++) {
+ if (strcmp(ctx->ocsps.val[0].path, path) == 0)
+ return 0;
+ }
+
+ data = realloc(ctx->ocsps.val,
+ (ctx->ocsps.len + 1) * sizeof(ctx->ocsps.val[0]));
+ if (data == NULL) {
+ hx509_clear_error_string(context);
+ return ENOMEM;
+ }
+
+ ctx->ocsps.val = data;
+
+ memset(&ctx->ocsps.val[ctx->ocsps.len], 0,
+ sizeof(ctx->ocsps.val[0]));
+
+ ctx->ocsps.val[ctx->ocsps.len].path = strdup(path);
+ if (ctx->ocsps.val[ctx->ocsps.len].path == NULL) {
+ hx509_clear_error_string(context);
+ return ENOMEM;
+ }
+
+ ret = load_ocsp(context, &ctx->ocsps.val[ctx->ocsps.len]);
+ if (ret) {
+ free(ctx->ocsps.val[ctx->ocsps.len].path);
+ return ret;
+ }
+ ctx->ocsps.len++;
+
+ return ret;
+}
+
+/*
+ *
+ */
+
+static int
+verify_crl(hx509_context context,
+ hx509_revoke_ctx ctx,
+ CRLCertificateList *crl,
+ time_t time_now,
+ hx509_certs certs,
+ hx509_cert parent)
+{
+ hx509_cert signer;
+ hx509_query q;
+ time_t t;
+ int ret;
+
+ t = _hx509_Time2time_t(&crl->tbsCertList.thisUpdate);
+ if (t > time_now) {
+ hx509_set_error_string(context, 0, HX509_CRL_USED_BEFORE_TIME,
+ "CRL used before time");
+ return HX509_CRL_USED_BEFORE_TIME;
+ }
+
+ if (crl->tbsCertList.nextUpdate == NULL) {
+ hx509_set_error_string(context, 0, HX509_CRL_INVALID_FORMAT,
+ "CRL missing nextUpdate");
+ return HX509_CRL_INVALID_FORMAT;
+ }
+
+ t = _hx509_Time2time_t(crl->tbsCertList.nextUpdate);
+ if (t < time_now) {
+ hx509_set_error_string(context, 0, HX509_CRL_USED_AFTER_TIME,
+ "CRL used after time");
+ return HX509_CRL_USED_AFTER_TIME;
+ }
+
+ _hx509_query_clear(&q);
+
+ /*
+ * If it's the signer have CRLSIGN bit set, use that as the signer
+ * cert for the certificate, otherwise, search for a certificate.
+ */
+ if (_hx509_check_key_usage(context, parent, 1 << 6, FALSE) == 0) {
+ signer = hx509_cert_ref(parent);
+ } else {
+ q.match = HX509_QUERY_MATCH_SUBJECT_NAME;
+ q.match |= HX509_QUERY_KU_CRLSIGN;
+ q.subject_name = &crl->tbsCertList.issuer;
+
+ ret = hx509_certs_find(context, certs, &q, &signer);
+ if (ret) {
+ hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
+ "Failed to find certificate for CRL");
+ return ret;
+ }
+ }
+
+ ret = _hx509_verify_signature_bitstring(context,
+ _hx509_get_cert(signer),
+ &crl->signatureAlgorithm,
+ &crl->tbsCertList._save,
+ &crl->signatureValue);
+ if (ret) {
+ hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
+ "CRL signature invalid");
+ goto out;
+ }
+
+ /*
+ * If signer is not CA cert, need to check revoke status of this
+ * CRL signing cert too, this include all parent CRL signer cert
+ * up to the root *sigh*, assume root at least hve CERTSIGN flag
+ * set.
+ */
+ while (_hx509_check_key_usage(context, signer, 1 << 5, TRUE)) {
+ hx509_cert crl_parent;
+
+ _hx509_query_clear(&q);
+
+ q.match = HX509_QUERY_MATCH_SUBJECT_NAME;
+ q.match |= HX509_QUERY_KU_CRLSIGN;
+ q.subject_name = &_hx509_get_cert(signer)->tbsCertificate.issuer;
+
+ ret = hx509_certs_find(context, certs, &q, &crl_parent);
+ if (ret) {
+ hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
+ "Failed to find parent of CRL signer");
+ goto out;
+ }
+
+ ret = hx509_revoke_verify(context,
+ ctx,
+ certs,
+ time_now,
+ signer,
+ crl_parent);
+ hx509_cert_free(signer);
+ signer = crl_parent;
+ if (ret) {
+ hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
+ "Failed to verify revoke "
+ "status of CRL signer");
+ goto out;
+ }
+ }
+
+out:
+ hx509_cert_free(signer);
+
+ return ret;
+}
+
+static int
+load_crl(const char *path, time_t *t, CRLCertificateList *crl)
+{
+ size_t length, size;
+ struct stat sb;
+ void *data;
+ int ret;
+
+ memset(crl, 0, sizeof(*crl));
+
+ ret = rk_undumpdata(path, &data, &length);
+ if (ret)
+ return ret;
+
+ ret = stat(path, &sb);
+ if (ret)
+ return errno;
+
+ *t = sb.st_mtime;
+
+ ret = decode_CRLCertificateList(data, length, crl, &size);
+ rk_xfree(data);
+ if (ret)
+ return ret;
+
+ /* check signature is aligned */
+ if (crl->signatureValue.length & 7) {
+ free_CRLCertificateList(crl);
+ return HX509_CRYPTO_SIG_INVALID_FORMAT;
+ }
+ return 0;
+}
+
+/**
+ * Add a CRL file to the revokation context.
+ *
+ * @param context hx509 context
+ * @param ctx hx509 revokation context
+ * @param path path to file that is going to be added to the context.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_revoke
+ */
+
+int
+hx509_revoke_add_crl(hx509_context context,
+ hx509_revoke_ctx ctx,
+ const char *path)
+{
+ void *data;
+ size_t i;
+ int ret;
+
+ if (strncmp(path, "FILE:", 5) != 0) {
+ hx509_set_error_string(context, 0, HX509_UNSUPPORTED_OPERATION,
+ "unsupport type in %s", path);
+ return HX509_UNSUPPORTED_OPERATION;
+ }
+
+
+ path += 5;
+
+ for (i = 0; i < ctx->crls.len; i++) {
+ if (strcmp(ctx->crls.val[0].path, path) == 0)
+ return 0;
+ }
+
+ data = realloc(ctx->crls.val,
+ (ctx->crls.len + 1) * sizeof(ctx->crls.val[0]));
+ if (data == NULL) {
+ hx509_clear_error_string(context);
+ return ENOMEM;
+ }
+ ctx->crls.val = data;
+
+ memset(&ctx->crls.val[ctx->crls.len], 0, sizeof(ctx->crls.val[0]));
+
+ ctx->crls.val[ctx->crls.len].path = strdup(path);
+ if (ctx->crls.val[ctx->crls.len].path == NULL) {
+ hx509_clear_error_string(context);
+ return ENOMEM;
+ }
+
+ ret = load_crl(path,
+ &ctx->crls.val[ctx->crls.len].last_modfied,
+ &ctx->crls.val[ctx->crls.len].crl);
+ if (ret) {
+ free(ctx->crls.val[ctx->crls.len].path);
+ return ret;
+ }
+
+ ctx->crls.len++;
+
+ return ret;
+}
+
+/**
+ * Check that a certificate is not expired according to a revokation
+ * context. Also need the parent certificte to the check OCSP
+ * parent identifier.
+ *
+ * @param context hx509 context
+ * @param ctx hx509 revokation context
+ * @param certs
+ * @param now
+ * @param cert
+ * @param parent_cert
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_revoke
+ */
+
+
+int
+hx509_revoke_verify(hx509_context context,
+ hx509_revoke_ctx ctx,
+ hx509_certs certs,
+ time_t now,
+ hx509_cert cert,
+ hx509_cert parent_cert)
+{
+ const Certificate *c = _hx509_get_cert(cert);
+ const Certificate *p = _hx509_get_cert(parent_cert);
+ unsigned long i, j, k;
+ int ret;
+
+ hx509_clear_error_string(context);
+
+ for (i = 0; i < ctx->ocsps.len; i++) {
+ struct revoke_ocsp *ocsp = &ctx->ocsps.val[i];
+ struct stat sb;
+
+ /* check this ocsp apply to this cert */
+
+ /* check if there is a newer version of the file */
+ ret = stat(ocsp->path, &sb);
+ if (ret == 0 && ocsp->last_modfied != sb.st_mtime) {
+ ret = load_ocsp(context, ocsp);
+ if (ret)
+ continue;
+ }
+
+ /* verify signature in ocsp if not already done */
+ if (ocsp->signer == NULL) {
+ ret = verify_ocsp(context, ocsp, now, certs, parent_cert);
+ if (ret)
+ continue;
+ }
+
+ for (j = 0; j < ocsp->ocsp.tbsResponseData.responses.len; j++) {
+ heim_octet_string os;
+
+ ret = der_heim_integer_cmp(&ocsp->ocsp.tbsResponseData.responses.val[j].certID.serialNumber,
+ &c->tbsCertificate.serialNumber);
+ if (ret != 0)
+ continue;
+
+ /* verify issuer hashes hash */
+ ret = _hx509_verify_signature(context,
+ NULL,
+ &ocsp->ocsp.tbsResponseData.responses.val[i].certID.hashAlgorithm,
+ &c->tbsCertificate.issuer._save,
+ &ocsp->ocsp.tbsResponseData.responses.val[i].certID.issuerNameHash);
+ if (ret != 0)
+ continue;
+
+ os.data = p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.data;
+ os.length = p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.length / 8;
+
+ ret = _hx509_verify_signature(context,
+ NULL,
+ &ocsp->ocsp.tbsResponseData.responses.val[j].certID.hashAlgorithm,
+ &os,
+ &ocsp->ocsp.tbsResponseData.responses.val[j].certID.issuerKeyHash);
+ if (ret != 0)
+ continue;
+
+ switch (ocsp->ocsp.tbsResponseData.responses.val[j].certStatus.element) {
+ case choice_OCSPCertStatus_good:
+ break;
+ case choice_OCSPCertStatus_revoked:
+ hx509_set_error_string(context, 0,
+ HX509_CERT_REVOKED,
+ "Certificate revoked by issuer in OCSP");
+ return HX509_CERT_REVOKED;
+ case choice_OCSPCertStatus_unknown:
+ continue;
+ }
+
+ /* don't allow the update to be in the future */
+ if (ocsp->ocsp.tbsResponseData.responses.val[j].thisUpdate >
+ now + context->ocsp_time_diff)
+ continue;
+
+ /* don't allow the next update to be in the past */
+ if (ocsp->ocsp.tbsResponseData.responses.val[j].nextUpdate) {
+ if (*ocsp->ocsp.tbsResponseData.responses.val[j].nextUpdate < now)
+ continue;
+ } else
+ /* Should force a refetch, but can we ? */;
+
+ return 0;
+ }
+ }
+
+ for (i = 0; i < ctx->crls.len; i++) {
+ struct revoke_crl *crl = &ctx->crls.val[i];
+ struct stat sb;
+ int diff;
+
+ /* check if cert.issuer == crls.val[i].crl.issuer */
+ ret = _hx509_name_cmp(&c->tbsCertificate.issuer,
+ &crl->crl.tbsCertList.issuer, &diff);
+ if (ret || diff)
+ continue;
+
+ ret = stat(crl->path, &sb);
+ if (ret == 0 && crl->last_modfied != sb.st_mtime) {
+ CRLCertificateList cl;
+
+ ret = load_crl(crl->path, &crl->last_modfied, &cl);
+ if (ret == 0) {
+ free_CRLCertificateList(&crl->crl);
+ crl->crl = cl;
+ crl->verified = 0;
+ crl->failed_verify = 0;
+ }
+ }
+ if (crl->failed_verify)
+ continue;
+
+ /* verify signature in crl if not already done */
+ if (crl->verified == 0) {
+ ret = verify_crl(context, ctx, &crl->crl, now, certs, parent_cert);
+ if (ret) {
+ crl->failed_verify = 1;
+ continue;
+ }
+ crl->verified = 1;
+ }
+
+ if (crl->crl.tbsCertList.crlExtensions) {
+ for (j = 0; j < crl->crl.tbsCertList.crlExtensions->len; j++) {
+ if (crl->crl.tbsCertList.crlExtensions->val[j].critical) {
+ hx509_set_error_string(context, 0,
+ HX509_CRL_UNKNOWN_EXTENSION,
+ "Unknown CRL extension");
+ return HX509_CRL_UNKNOWN_EXTENSION;
+ }
+ }
+ }
+
+ if (crl->crl.tbsCertList.revokedCertificates == NULL)
+ return 0;
+
+ /* check if cert is in crl */
+ for (j = 0; j < crl->crl.tbsCertList.revokedCertificates->len; j++) {
+ time_t t;
+
+ ret = der_heim_integer_cmp(&crl->crl.tbsCertList.revokedCertificates->val[j].userCertificate,
+ &c->tbsCertificate.serialNumber);
+ if (ret != 0)
+ continue;
+
+ t = _hx509_Time2time_t(&crl->crl.tbsCertList.revokedCertificates->val[j].revocationDate);
+ if (t > now)
+ continue;
+
+ if (crl->crl.tbsCertList.revokedCertificates->val[j].crlEntryExtensions)
+ for (k = 0; k < crl->crl.tbsCertList.revokedCertificates->val[j].crlEntryExtensions->len; k++)
+ if (crl->crl.tbsCertList.revokedCertificates->val[j].crlEntryExtensions->val[k].critical)
+ return HX509_CRL_UNKNOWN_EXTENSION;
+
+ hx509_set_error_string(context, 0,
+ HX509_CERT_REVOKED,
+ "Certificate revoked by issuer in CRL");
+ return HX509_CERT_REVOKED;
+ }
+
+ return 0;
+ }
+
+
+ if (context->flags & HX509_CTX_VERIFY_MISSING_OK)
+ return 0;
+ hx509_set_error_string(context, HX509_ERROR_APPEND,
+ HX509_REVOKE_STATUS_MISSING,
+ "No revoke status found for "
+ "certificates");
+ return HX509_REVOKE_STATUS_MISSING;
+}
+
+struct ocsp_add_ctx {
+ OCSPTBSRequest *req;
+ hx509_certs certs;
+ const AlgorithmIdentifier *digest;
+ hx509_cert parent;
+};
+
+static int
+add_to_req(hx509_context context, void *ptr, hx509_cert cert)
+{
+ struct ocsp_add_ctx *ctx = ptr;
+ OCSPInnerRequest *one;
+ hx509_cert parent = NULL;
+ Certificate *p, *c = _hx509_get_cert(cert);
+ heim_octet_string os;
+ int ret;
+ hx509_query q;
+ void *d;
+
+ d = realloc(ctx->req->requestList.val,
+ sizeof(ctx->req->requestList.val[0]) *
+ (ctx->req->requestList.len + 1));
+ if (d == NULL)
+ return ENOMEM;
+ ctx->req->requestList.val = d;
+
+ one = &ctx->req->requestList.val[ctx->req->requestList.len];
+ memset(one, 0, sizeof(*one));
+
+ _hx509_query_clear(&q);
+
+ q.match |= HX509_QUERY_FIND_ISSUER_CERT;
+ q.subject = c;
+
+ ret = hx509_certs_find(context, ctx->certs, &q, &parent);
+ if (ret)
+ goto out;
+
+ if (ctx->parent) {
+ if (hx509_cert_cmp(ctx->parent, parent) != 0) {
+ ret = HX509_REVOKE_NOT_SAME_PARENT;
+ hx509_set_error_string(context, 0, ret,
+ "Not same parent certifate as "
+ "last certificate in request");
+ goto out;
+ }
+ } else
+ ctx->parent = hx509_cert_ref(parent);
+
+ p = _hx509_get_cert(parent);
+
+ ret = copy_AlgorithmIdentifier(ctx->digest, &one->reqCert.hashAlgorithm);
+ if (ret)
+ goto out;
+
+ ret = _hx509_create_signature(context,
+ NULL,
+ &one->reqCert.hashAlgorithm,
+ &c->tbsCertificate.issuer._save,
+ NULL,
+ &one->reqCert.issuerNameHash);
+ if (ret)
+ goto out;
+
+ os.data = p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.data;
+ os.length =
+ p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.length / 8;
+
+ ret = _hx509_create_signature(context,
+ NULL,
+ &one->reqCert.hashAlgorithm,
+ &os,
+ NULL,
+ &one->reqCert.issuerKeyHash);
+ if (ret)
+ goto out;
+
+ ret = copy_CertificateSerialNumber(&c->tbsCertificate.serialNumber,
+ &one->reqCert.serialNumber);
+ if (ret)
+ goto out;
+
+ ctx->req->requestList.len++;
+out:
+ hx509_cert_free(parent);
+ if (ret) {
+ free_OCSPInnerRequest(one);
+ memset(one, 0, sizeof(*one));
+ }
+
+ return ret;
+}
+
+/**
+ * Create an OCSP request for a set of certificates.
+ *
+ * @param context a hx509 context
+ * @param reqcerts list of certificates to request ocsp data for
+ * @param pool certificate pool to use when signing
+ * @param signer certificate to use to sign the request
+ * @param digest the signing algorithm in the request, if NULL use the
+ * default signature algorithm,
+ * @param request the encoded request, free with free_heim_octet_string().
+ * @param nonce nonce in the request, free with free_heim_octet_string().
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_revoke
+ */
+
+int
+hx509_ocsp_request(hx509_context context,
+ hx509_certs reqcerts,
+ hx509_certs pool,
+ hx509_cert signer,
+ const AlgorithmIdentifier *digest,
+ heim_octet_string *request,
+ heim_octet_string *nonce)
+{
+ OCSPRequest req;
+ size_t size;
+ int ret;
+ struct ocsp_add_ctx ctx;
+ Extensions *es;
+
+ memset(&req, 0, sizeof(req));
+
+ if (digest == NULL)
+ digest = _hx509_crypto_default_digest_alg;
+
+ ctx.req = &req.tbsRequest;
+ ctx.certs = pool;
+ ctx.digest = digest;
+ ctx.parent = NULL;
+
+ ret = hx509_certs_iter(context, reqcerts, add_to_req, &ctx);
+ hx509_cert_free(ctx.parent);
+ if (ret)
+ goto out;
+
+ if (nonce) {
+ req.tbsRequest.requestExtensions =
+ calloc(1, sizeof(*req.tbsRequest.requestExtensions));
+ if (req.tbsRequest.requestExtensions == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+
+ es = req.tbsRequest.requestExtensions;
+
+ es->val = calloc(es->len, sizeof(es->val[0]));
+ if (es->val == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+ es->len = 1;
+
+ ret = der_copy_oid(oid_id_pkix_ocsp_nonce(), &es->val[0].extnID);
+ if (ret) {
+ free_OCSPRequest(&req);
+ return ret;
+ }
+
+ es->val[0].extnValue.data = malloc(10);
+ if (es->val[0].extnValue.data == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+ es->val[0].extnValue.length = 10;
+
+ ret = RAND_bytes(es->val[0].extnValue.data,
+ es->val[0].extnValue.length);
+ if (ret != 1) {
+ ret = HX509_CRYPTO_INTERNAL_ERROR;
+ goto out;
+ }
+ ret = der_copy_octet_string(nonce, &es->val[0].extnValue);
+ if (ret) {
+ ret = ENOMEM;
+ goto out;
+ }
+ }
+
+ ASN1_MALLOC_ENCODE(OCSPRequest, request->data, request->length,
+ &req, &size, ret);
+ free_OCSPRequest(&req);
+ if (ret)
+ goto out;
+ if (size != request->length)
+ _hx509_abort("internal ASN.1 encoder error");
+
+ return 0;
+
+out:
+ free_OCSPRequest(&req);
+ return ret;
+}
+
+static char *
+printable_time(time_t t)
+{
+ static char s[128];
+ strlcpy(s, ctime(&t)+ 4, sizeof(s));
+ s[20] = 0;
+ return s;
+}
+
+/**
+ * Print the OCSP reply stored in a file.
+ *
+ * @param context a hx509 context
+ * @param path path to a file with a OCSP reply
+ * @param out the out FILE descriptor to print the reply on
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_revoke
+ */
+
+int
+hx509_revoke_ocsp_print(hx509_context context, const char *path, FILE *out)
+{
+ struct revoke_ocsp ocsp;
+ int ret, i;
+
+ if (out == NULL)
+ out = stdout;
+
+ memset(&ocsp, 0, sizeof(ocsp));
+
+ ocsp.path = strdup(path);
+ if (ocsp.path == NULL)
+ return ENOMEM;
+
+ ret = load_ocsp(context, &ocsp);
+ if (ret) {
+ free_ocsp(&ocsp);
+ return ret;
+ }
+
+ fprintf(out, "signer: ");
+
+ switch(ocsp.ocsp.tbsResponseData.responderID.element) {
+ case choice_OCSPResponderID_byName: {
+ hx509_name n;
+ char *s;
+ _hx509_name_from_Name(&ocsp.ocsp.tbsResponseData.responderID.u.byName, &n);
+ hx509_name_to_string(n, &s);
+ hx509_name_free(&n);
+ fprintf(out, " byName: %s\n", s);
+ free(s);
+ break;
+ }
+ case choice_OCSPResponderID_byKey: {
+ char *s;
+ hex_encode(ocsp.ocsp.tbsResponseData.responderID.u.byKey.data,
+ ocsp.ocsp.tbsResponseData.responderID.u.byKey.length,
+ &s);
+ fprintf(out, " byKey: %s\n", s);
+ free(s);
+ break;
+ }
+ default:
+ _hx509_abort("choice_OCSPResponderID unknown");
+ break;
+ }
+
+ fprintf(out, "producedAt: %s\n",
+ printable_time(ocsp.ocsp.tbsResponseData.producedAt));
+
+ fprintf(out, "replies: %d\n", ocsp.ocsp.tbsResponseData.responses.len);
+
+ for (i = 0; i < ocsp.ocsp.tbsResponseData.responses.len; i++) {
+ const char *status;
+ switch (ocsp.ocsp.tbsResponseData.responses.val[i].certStatus.element) {
+ case choice_OCSPCertStatus_good:
+ status = "good";
+ break;
+ case choice_OCSPCertStatus_revoked:
+ status = "revoked";
+ break;
+ case choice_OCSPCertStatus_unknown:
+ status = "unknown";
+ break;
+ default:
+ status = "element unknown";
+ }
+
+ fprintf(out, "\t%d. status: %s\n", i, status);
+
+ fprintf(out, "\tthisUpdate: %s\n",
+ printable_time(ocsp.ocsp.tbsResponseData.responses.val[i].thisUpdate));
+ if (ocsp.ocsp.tbsResponseData.responses.val[i].nextUpdate)
+ fprintf(out, "\tproducedAt: %s\n",
+ printable_time(ocsp.ocsp.tbsResponseData.responses.val[i].thisUpdate));
+
+ }
+
+ fprintf(out, "appended certs:\n");
+ if (ocsp.certs)
+ ret = hx509_certs_iter(context, ocsp.certs, hx509_ci_print_names, out);
+
+ free_ocsp(&ocsp);
+ return ret;
+}
+
+/**
+ * Verify that the certificate is part of the OCSP reply and it's not
+ * expired. Doesn't verify signature the OCSP reply or it's done by a
+ * authorized sender, that is assumed to be already done.
+ *
+ * @param context a hx509 context
+ * @param now the time right now, if 0, use the current time.
+ * @param cert the certificate to verify
+ * @param flags flags control the behavior
+ * @param data pointer to the encode ocsp reply
+ * @param length the length of the encode ocsp reply
+ * @param expiration return the time the OCSP will expire and need to
+ * be rechecked.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_verify
+ */
+
+int
+hx509_ocsp_verify(hx509_context context,
+ time_t now,
+ hx509_cert cert,
+ int flags,
+ const void *data, size_t length,
+ time_t *expiration)
+{
+ const Certificate *c = _hx509_get_cert(cert);
+ OCSPBasicOCSPResponse basic;
+ int ret, i;
+
+ if (now == 0)
+ now = time(NULL);
+
+ *expiration = 0;
+
+ ret = parse_ocsp_basic(data, length, &basic);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to parse OCSP response");
+ return ret;
+ }
+
+ for (i = 0; i < basic.tbsResponseData.responses.len; i++) {
+
+ ret = der_heim_integer_cmp(&basic.tbsResponseData.responses.val[i].certID.serialNumber,
+ &c->tbsCertificate.serialNumber);
+ if (ret != 0)
+ continue;
+
+ /* verify issuer hashes hash */
+ ret = _hx509_verify_signature(context,
+ NULL,
+ &basic.tbsResponseData.responses.val[i].certID.hashAlgorithm,
+ &c->tbsCertificate.issuer._save,
+ &basic.tbsResponseData.responses.val[i].certID.issuerNameHash);
+ if (ret != 0)
+ continue;
+
+ switch (basic.tbsResponseData.responses.val[i].certStatus.element) {
+ case choice_OCSPCertStatus_good:
+ break;
+ case choice_OCSPCertStatus_revoked:
+ case choice_OCSPCertStatus_unknown:
+ continue;
+ }
+
+ /* don't allow the update to be in the future */
+ if (basic.tbsResponseData.responses.val[i].thisUpdate >
+ now + context->ocsp_time_diff)
+ continue;
+
+ /* don't allow the next update to be in the past */
+ if (basic.tbsResponseData.responses.val[i].nextUpdate) {
+ if (*basic.tbsResponseData.responses.val[i].nextUpdate < now)
+ continue;
+ *expiration = *basic.tbsResponseData.responses.val[i].nextUpdate;
+ } else
+ *expiration = now;
+
+ free_OCSPBasicOCSPResponse(&basic);
+ return 0;
+ }
+
+ free_OCSPBasicOCSPResponse(&basic);
+
+ {
+ hx509_name name;
+ char *subject;
+
+ ret = hx509_cert_get_subject(cert, &name);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+ ret = hx509_name_to_string(name, &subject);
+ hx509_name_free(&name);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+ hx509_set_error_string(context, 0, HX509_CERT_NOT_IN_OCSP,
+ "Certificate %s not in OCSP response "
+ "or not good",
+ subject);
+ free(subject);
+ }
+out:
+ return HX509_CERT_NOT_IN_OCSP;
+}
+
+struct hx509_crl {
+ hx509_certs revoked;
+ time_t expire;
+};
+
+/**
+ * Create a CRL context. Use hx509_crl_free() to free the CRL context.
+ *
+ * @param context a hx509 context.
+ * @param crl return pointer to a newly allocated CRL context.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_verify
+ */
+
+int
+hx509_crl_alloc(hx509_context context, hx509_crl *crl)
+{
+ int ret;
+
+ *crl = calloc(1, sizeof(**crl));
+ if (*crl == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+
+ ret = hx509_certs_init(context, "MEMORY:crl", 0, NULL, &(*crl)->revoked);
+ if (ret) {
+ free(*crl);
+ *crl = NULL;
+ return ret;
+ }
+ (*crl)->expire = 0;
+ return ret;
+}
+
+/**
+ * Add revoked certificate to an CRL context.
+ *
+ * @param context a hx509 context.
+ * @param crl the CRL to add the revoked certificate to.
+ * @param certs keyset of certificate to revoke.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_verify
+ */
+
+int
+hx509_crl_add_revoked_certs(hx509_context context,
+ hx509_crl crl,
+ hx509_certs certs)
+{
+ return hx509_certs_merge(context, crl->revoked, certs);
+}
+
+/**
+ * Set the lifetime of a CRL context.
+ *
+ * @param context a hx509 context.
+ * @param crl a CRL context
+ * @param delta delta time the certificate is valid, library adds the
+ * current time to this.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_verify
+ */
+
+int
+hx509_crl_lifetime(hx509_context context, hx509_crl crl, int delta)
+{
+ crl->expire = time(NULL) + delta;
+ return 0;
+}
+
+/**
+ * Free a CRL context.
+ *
+ * @param context a hx509 context.
+ * @param crl a CRL context to free.
+ *
+ * @ingroup hx509_verify
+ */
+
+void
+hx509_crl_free(hx509_context context, hx509_crl *crl)
+{
+ if (*crl == NULL)
+ return;
+ hx509_certs_free(&(*crl)->revoked);
+ memset(*crl, 0, sizeof(**crl));
+ free(*crl);
+ *crl = NULL;
+}
+
+static int
+add_revoked(hx509_context context, void *ctx, hx509_cert cert)
+{
+ TBSCRLCertList *c = ctx;
+ unsigned int num;
+ void *ptr;
+ int ret;
+
+ num = c->revokedCertificates->len;
+ ptr = realloc(c->revokedCertificates->val,
+ (num + 1) * sizeof(c->revokedCertificates->val[0]));
+ if (ptr == NULL) {
+ hx509_clear_error_string(context);
+ return ENOMEM;
+ }
+ c->revokedCertificates->val = ptr;
+
+ ret = hx509_cert_get_serialnumber(cert,
+ &c->revokedCertificates->val[num].userCertificate);
+ if (ret) {
+ hx509_clear_error_string(context);
+ return ret;
+ }
+ c->revokedCertificates->val[num].revocationDate.element =
+ choice_Time_generalTime;
+ c->revokedCertificates->val[num].revocationDate.u.generalTime =
+ time(NULL) - 3600 * 24;
+ c->revokedCertificates->val[num].crlEntryExtensions = NULL;
+
+ c->revokedCertificates->len++;
+
+ return 0;
+}
+
+/**
+ * Sign a CRL and return an encode certificate.
+ *
+ * @param context a hx509 context.
+ * @param signer certificate to sign the CRL with
+ * @param crl the CRL to sign
+ * @param os return the signed and encoded CRL, free with
+ * free_heim_octet_string()
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_verify
+ */
+
+int
+hx509_crl_sign(hx509_context context,
+ hx509_cert signer,
+ hx509_crl crl,
+ heim_octet_string *os)
+{
+ const AlgorithmIdentifier *sigalg = _hx509_crypto_default_sig_alg;
+ CRLCertificateList c;
+ size_t size;
+ int ret;
+ hx509_private_key signerkey;
+
+ memset(&c, 0, sizeof(c));
+
+ signerkey = _hx509_cert_private_key(signer);
+ if (signerkey == NULL) {
+ ret = HX509_PRIVATE_KEY_MISSING;
+ hx509_set_error_string(context, 0, ret,
+ "Private key missing for CRL signing");
+ return ret;
+ }
+
+ c.tbsCertList.version = malloc(sizeof(*c.tbsCertList.version));
+ if (c.tbsCertList.version == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
+
+ *c.tbsCertList.version = 1;
+
+ ret = copy_AlgorithmIdentifier(sigalg, &c.tbsCertList.signature);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+
+ ret = copy_Name(&_hx509_get_cert(signer)->tbsCertificate.issuer,
+ &c.tbsCertList.issuer);
+ if (ret) {
+ hx509_clear_error_string(context);
+ goto out;
+ }
+
+ c.tbsCertList.thisUpdate.element = choice_Time_generalTime;
+ c.tbsCertList.thisUpdate.u.generalTime = time(NULL) - 24 * 3600;
+
+ c.tbsCertList.nextUpdate = malloc(sizeof(*c.tbsCertList.nextUpdate));
+ if (c.tbsCertList.nextUpdate == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ ret = ENOMEM;
+ goto out;
+ }
+
+ {
+ time_t next = crl->expire;
+ if (next == 0)
+ next = time(NULL) + 24 * 3600 * 365;
+
+ c.tbsCertList.nextUpdate->element = choice_Time_generalTime;
+ c.tbsCertList.nextUpdate->u.generalTime = next;
+ }
+
+ c.tbsCertList.revokedCertificates =
+ calloc(1, sizeof(*c.tbsCertList.revokedCertificates));
+ if (c.tbsCertList.revokedCertificates == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ ret = ENOMEM;
+ goto out;
+ }
+ c.tbsCertList.crlExtensions = NULL;
+
+ ret = hx509_certs_iter(context, crl->revoked, add_revoked, &c.tbsCertList);
+ if (ret)
+ goto out;
+
+ /* if not revoked certs, remove OPTIONAL entry */
+ if (c.tbsCertList.revokedCertificates->len == 0) {
+ free(c.tbsCertList.revokedCertificates);
+ c.tbsCertList.revokedCertificates = NULL;
+ }
+
+ ASN1_MALLOC_ENCODE(TBSCRLCertList, os->data, os->length,
+ &c.tbsCertList, &size, ret);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "failed to encode tbsCRL");
+ goto out;
+ }
+ if (size != os->length)
+ _hx509_abort("internal ASN.1 encoder error");
+
+
+ ret = _hx509_create_signature_bitstring(context,
+ signerkey,
+ sigalg,
+ os,
+ &c.signatureAlgorithm,
+ &c.signatureValue);
+ free(os->data);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "Failed to sign CRL");
+ goto out;
+ }
+
+ ASN1_MALLOC_ENCODE(CRLCertificateList, os->data, os->length,
+ &c, &size, ret);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret, "failed to encode CRL");
+ goto out;
+ }
+ if (size != os->length)
+ _hx509_abort("internal ASN.1 encoder error");
+
+ free_CRLCertificateList(&c);
+
+ return 0;
+
+out:
+ free_CRLCertificateList(&c);
+ return ret;
+}
diff --git a/source4/heimdal/lib/hx509/sel-gram.c b/source4/heimdal/lib/hx509/sel-gram.c
new file mode 100644
index 0000000000..4d9cf78c1b
--- /dev/null
+++ b/source4/heimdal/lib/hx509/sel-gram.c
@@ -0,0 +1,1714 @@
+/* A Bison parser, made by GNU Bison 2.3. */
+
+/* Skeleton implementation for Bison's Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+ Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU 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., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+ simplifying the original so-called "semantic" parser. */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+ infringing on user name space. This should be done even for local
+ variables, as they might otherwise be expanded by user macros.
+ There are some unavoidable exceptions within include files to
+ define necessary library symbols; they are noted "INFRINGES ON
+ USER NAME SPACE" below. */
+
+/* Identify Bison output. */
+#define YYBISON 1
+
+/* Bison version. */
+#define YYBISON_VERSION "2.3"
+
+/* Skeleton name. */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers. */
+#define YYPURE 0
+
+/* Using locations. */
+#define YYLSP_NEEDED 0
+
+
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ kw_TRUE = 258,
+ kw_FALSE = 259,
+ kw_AND = 260,
+ kw_OR = 261,
+ kw_IN = 262,
+ kw_TAILMATCH = 263,
+ NUMBER = 264,
+ STRING = 265,
+ IDENTIFIER = 266
+ };
+#endif
+/* Tokens. */
+#define kw_TRUE 258
+#define kw_FALSE 259
+#define kw_AND 260
+#define kw_OR 261
+#define kw_IN 262
+#define kw_TAILMATCH 263
+#define NUMBER 264
+#define STRING 265
+#define IDENTIFIER 266
+
+
+
+
+/* Copy the first part of user declarations. */
+#line 34 "heimdal/lib/hx509/sel-gram.y"
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <hx_locl.h>
+
+RCSID("$Id$");
+
+
+
+/* Enabling traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+
+/* Enabling verbose error messages. */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 0
+#endif
+
+/* Enabling the token table. */
+#ifndef YYTOKEN_TABLE
+# define YYTOKEN_TABLE 0
+#endif
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+#line 46 "heimdal/lib/hx509/sel-gram.y"
+{
+ char *string;
+ struct hx_expr *expr;
+}
+/* Line 187 of yacc.c. */
+#line 135 "heimdal/lib/hx509/sel-gram.y"
+ YYSTYPE;
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+
+
+/* Copy the second part of user declarations. */
+
+
+/* Line 216 of yacc.c. */
+#line 148 "heimdal/lib/hx509/sel-gram.y"
+
+#ifdef short
+# undef short
+#endif
+
+#ifdef YYTYPE_UINT8
+typedef YYTYPE_UINT8 yytype_uint8;
+#else
+typedef unsigned char yytype_uint8;
+#endif
+
+#ifdef YYTYPE_INT8
+typedef YYTYPE_INT8 yytype_int8;
+#elif (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+typedef signed char yytype_int8;
+#else
+typedef short int yytype_int8;
+#endif
+
+#ifdef YYTYPE_UINT16
+typedef YYTYPE_UINT16 yytype_uint16;
+#else
+typedef unsigned short int yytype_uint16;
+#endif
+
+#ifdef YYTYPE_INT16
+typedef YYTYPE_INT16 yytype_int16;
+#else
+typedef short int yytype_int16;
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+# define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+# define YYSIZE_T size_t
+# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+# else
+# define YYSIZE_T unsigned int
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+
+#ifndef YY_
+# if YYENABLE_NLS
+# if ENABLE_NLS
+# include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+# define YY_(msgid) dgettext ("bison-runtime", msgid)
+# endif
+# endif
+# ifndef YY_
+# define YY_(msgid) msgid
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E. */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(e) ((void) (e))
+#else
+# define YYUSE(e) /* empty */
+#endif
+
+/* Identity function, used to suppress warnings about constant conditions. */
+#ifndef lint
+# define YYID(n) (n)
+#else
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static int
+YYID (int i)
+#else
+static int
+YYID (i)
+ int i;
+#endif
+{
+ return i;
+}
+#endif
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols. */
+
+# ifdef YYSTACK_USE_ALLOCA
+# if YYSTACK_USE_ALLOCA
+# ifdef __GNUC__
+# define YYSTACK_ALLOC __builtin_alloca
+# elif defined __BUILTIN_VA_ARG_INCR
+# include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+# elif defined _AIX
+# define YYSTACK_ALLOC __alloca
+# elif defined _MSC_VER
+# include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+# define alloca _alloca
+# else
+# define YYSTACK_ALLOC alloca
+# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef _STDLIB_H
+# define _STDLIB_H 1
+# endif
+# endif
+# endif
+# endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+ /* Pacify GCC's `empty if-body' warning. */
+# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0))
+# ifndef YYSTACK_ALLOC_MAXIMUM
+ /* The OS might guarantee only one guard page at the bottom of the stack,
+ and a page size can be as small as 4096 bytes. So we cannot safely
+ invoke alloca (N) if N exceeds 4096. Use a slightly smaller number
+ to allow for a few compiler-allocated temporary stack slots. */
+# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+# endif
+# else
+# define YYSTACK_ALLOC YYMALLOC
+# define YYSTACK_FREE YYFREE
+# ifndef YYSTACK_ALLOC_MAXIMUM
+# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+# endif
+# if (defined __cplusplus && ! defined _STDLIB_H \
+ && ! ((defined YYMALLOC || defined malloc) \
+ && (defined YYFREE || defined free)))
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef _STDLIB_H
+# define _STDLIB_H 1
+# endif
+# endif
+# ifndef YYMALLOC
+# define YYMALLOC malloc
+# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# ifndef YYFREE
+# define YYFREE free
+# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+ && (! defined __cplusplus \
+ || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member. */
+union yyalloc
+{
+ yytype_int16 yyss;
+ YYSTYPE yyvs;
+ };
+
+/* The size of the maximum gap between one aligned stack and the next. */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+ N elements. */
+# define YYSTACK_BYTES(N) \
+ ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \
+ + YYSTACK_GAP_MAXIMUM)
+
+/* Copy COUNT objects from FROM to TO. The source and destination do
+ not overlap. */
+# ifndef YYCOPY
+# if defined __GNUC__ && 1 < __GNUC__
+# define YYCOPY(To, From, Count) \
+ __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+# else
+# define YYCOPY(To, From, Count) \
+ do \
+ { \
+ YYSIZE_T yyi; \
+ for (yyi = 0; yyi < (Count); yyi++) \
+ (To)[yyi] = (From)[yyi]; \
+ } \
+ while (YYID (0))
+# endif
+# endif
+
+/* Relocate STACK from its old location to the new one. The
+ local variables YYSIZE and YYSTACKSIZE give the old and new number of
+ elements in the stack, and YYPTR gives the new location of the
+ stack. Advance YYPTR to a properly aligned location for the next
+ stack. */
+# define YYSTACK_RELOCATE(Stack) \
+ do \
+ { \
+ YYSIZE_T yynewbytes; \
+ YYCOPY (&yyptr->Stack, Stack, yysize); \
+ Stack = &yyptr->Stack; \
+ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+ yyptr += yynewbytes / sizeof (*yyptr); \
+ } \
+ while (YYID (0))
+
+#endif
+
+/* YYFINAL -- State number of the termination state. */
+#define YYFINAL 21
+/* YYLAST -- Last index in YYTABLE. */
+#define YYLAST 50
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS 21
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS 11
+/* YYNRULES -- Number of rules. */
+#define YYNRULES 26
+/* YYNRULES -- Number of states. */
+#define YYNSTATES 50
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
+#define YYUNDEFTOK 2
+#define YYMAXUTOK 266
+
+#define YYTRANSLATE(YYX) \
+ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */
+static const yytype_uint8 yytranslate[] =
+{
+ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 12, 2, 2, 2, 17, 2, 2,
+ 13, 14, 2, 2, 15, 2, 20, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 16, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 18, 2, 19, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+ YYRHS. */
+static const yytype_uint8 yyprhs[] =
+{
+ 0, 0, 3, 5, 7, 9, 12, 16, 20, 24,
+ 26, 28, 32, 37, 42, 46, 52, 56, 58, 60,
+ 62, 64, 66, 68, 73, 78, 82
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS. */
+static const yytype_int8 yyrhs[] =
+{
+ 22, 0, -1, 23, -1, 3, -1, 4, -1, 12,
+ 23, -1, 23, 5, 23, -1, 23, 6, 23, -1,
+ 13, 23, 14, -1, 25, -1, 26, -1, 26, 15,
+ 24, -1, 26, 16, 16, 26, -1, 26, 12, 16,
+ 26, -1, 26, 8, 26, -1, 26, 7, 13, 24,
+ 14, -1, 26, 7, 30, -1, 27, -1, 28, -1,
+ 29, -1, 30, -1, 9, -1, 10, -1, 11, 13,
+ 24, 14, -1, 17, 18, 31, 19, -1, 11, 20,
+ 31, -1, 11, -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
+static const yytype_uint8 yyrline[] =
+{
+ 0, 74, 74, 76, 77, 78, 79, 80, 81, 82,
+ 85, 86, 89, 90, 91, 92, 93, 96, 97, 98,
+ 99, 102, 103, 105, 108, 111, 113
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+ First, the terminals, then, starting at YYNTOKENS, nonterminals. */
+static const char *const yytname[] =
+{
+ "$end", "error", "$undefined", "kw_TRUE", "kw_FALSE", "kw_AND", "kw_OR",
+ "kw_IN", "kw_TAILMATCH", "NUMBER", "STRING", "IDENTIFIER", "'!'", "'('",
+ "')'", "','", "'='", "'%'", "'{'", "'}'", "'.'", "$accept", "start",
+ "expr", "words", "comp", "word", "number", "string", "function",
+ "variable", "variables", 0
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+ token YYLEX-NUM. */
+static const yytype_uint16 yytoknum[] =
+{
+ 0, 256, 257, 258, 259, 260, 261, 262, 263, 264,
+ 265, 266, 33, 40, 41, 44, 61, 37, 123, 125,
+ 46
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
+static const yytype_uint8 yyr1[] =
+{
+ 0, 21, 22, 23, 23, 23, 23, 23, 23, 23,
+ 24, 24, 25, 25, 25, 25, 25, 26, 26, 26,
+ 26, 27, 28, 29, 30, 31, 31
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
+static const yytype_uint8 yyr2[] =
+{
+ 0, 2, 1, 1, 1, 2, 3, 3, 3, 1,
+ 1, 3, 4, 4, 3, 5, 3, 1, 1, 1,
+ 1, 1, 1, 4, 4, 3, 1
+};
+
+/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
+ STATE-NUM when YYTABLE doesn't specify something else to do. Zero
+ means the default is an error. */
+static const yytype_uint8 yydefact[] =
+{
+ 0, 3, 4, 21, 22, 0, 0, 0, 0, 0,
+ 2, 9, 0, 17, 18, 19, 20, 0, 5, 0,
+ 0, 1, 0, 0, 0, 0, 0, 0, 0, 10,
+ 8, 26, 0, 6, 7, 0, 16, 14, 0, 0,
+ 23, 0, 0, 24, 0, 13, 12, 11, 25, 15
+};
+
+/* YYDEFGOTO[NTERM-NUM]. */
+static const yytype_int8 yydefgoto[] =
+{
+ -1, 9, 10, 28, 11, 12, 13, 14, 15, 16,
+ 32
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+ STATE-NUM. */
+#define YYPACT_NINF -31
+static const yytype_int8 yypact[] =
+{
+ 22, -31, -31, -31, -31, -1, 22, 22, -11, 27,
+ 11, -31, -6, -31, -31, -31, -31, 19, 11, 9,
+ 26, -31, 22, 22, -4, 19, 24, 25, 28, 23,
+ -31, 29, 31, 11, 11, 19, -31, -31, 19, 19,
+ -31, 19, 26, -31, 30, -31, -31, -31, -31, -31
+};
+
+/* YYPGOTO[NTERM-NUM]. */
+static const yytype_int8 yypgoto[] =
+{
+ -31, -31, -3, -30, -31, -17, -31, -31, -31, 21,
+ 1
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If
+ positive, shift that token. If negative, reduce the rule which
+ number is the opposite. If zero, do what YYDEFACT says.
+ If YYTABLE_NINF, syntax error. */
+#define YYTABLE_NINF -1
+static const yytype_uint8 yytable[] =
+{
+ 29, 24, 25, 18, 19, 44, 26, 20, 37, 35,
+ 27, 47, 17, 8, 22, 23, 22, 23, 29, 33,
+ 34, 45, 46, 30, 29, 1, 2, 21, 3, 4,
+ 5, 3, 4, 5, 6, 7, 8, 31, 41, 8,
+ 38, 39, 40, 48, 49, 36, 0, 0, 0, 42,
+ 43
+};
+
+static const yytype_int8 yycheck[] =
+{
+ 17, 7, 8, 6, 7, 35, 12, 18, 25, 13,
+ 16, 41, 13, 17, 5, 6, 5, 6, 35, 22,
+ 23, 38, 39, 14, 41, 3, 4, 0, 9, 10,
+ 11, 9, 10, 11, 12, 13, 17, 11, 15, 17,
+ 16, 16, 14, 42, 14, 24, -1, -1, -1, 20,
+ 19
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+ symbol of state STATE-NUM. */
+static const yytype_uint8 yystos[] =
+{
+ 0, 3, 4, 9, 10, 11, 12, 13, 17, 22,
+ 23, 25, 26, 27, 28, 29, 30, 13, 23, 23,
+ 18, 0, 5, 6, 7, 8, 12, 16, 24, 26,
+ 14, 11, 31, 23, 23, 13, 30, 26, 16, 16,
+ 14, 15, 20, 19, 24, 26, 26, 24, 31, 14
+};
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY (-2)
+#define YYEOF 0
+
+#define YYACCEPT goto yyacceptlab
+#define YYABORT goto yyabortlab
+#define YYERROR goto yyerrorlab
+
+
+/* Like YYERROR except do call yyerror. This remains here temporarily
+ to ease the transition to the new meaning of YYERROR, for GCC.
+ Once GCC version 2 has supplanted version 1, this can go. */
+
+#define YYFAIL goto yyerrlab
+
+#define YYRECOVERING() (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value) \
+do \
+ if (yychar == YYEMPTY && yylen == 1) \
+ { \
+ yychar = (Token); \
+ yylval = (Value); \
+ yytoken = YYTRANSLATE (yychar); \
+ YYPOPSTACK (1); \
+ goto yybackup; \
+ } \
+ else \
+ { \
+ yyerror (YY_("syntax error: cannot back up")); \
+ YYERROR; \
+ } \
+while (YYID (0))
+
+
+#define YYTERROR 1
+#define YYERRCODE 256
+
+
+/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
+ If N is 0, then set CURRENT to the empty location which ends
+ the previous symbol: RHS[0] (always defined). */
+
+#define YYRHSLOC(Rhs, K) ((Rhs)[K])
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N) \
+ do \
+ if (YYID (N)) \
+ { \
+ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \
+ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \
+ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \
+ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \
+ } \
+ else \
+ { \
+ (Current).first_line = (Current).last_line = \
+ YYRHSLOC (Rhs, 0).last_line; \
+ (Current).first_column = (Current).last_column = \
+ YYRHSLOC (Rhs, 0).last_column; \
+ } \
+ while (YYID (0))
+#endif
+
+
+/* YY_LOCATION_PRINT -- Print the location on the stream.
+ This macro was not mandated originally: define only if we know
+ we won't break user code: when these are the locations we know. */
+
+#ifndef YY_LOCATION_PRINT
+# if YYLTYPE_IS_TRIVIAL
+# define YY_LOCATION_PRINT(File, Loc) \
+ fprintf (File, "%d.%d-%d.%d", \
+ (Loc).first_line, (Loc).first_column, \
+ (Loc).last_line, (Loc).last_column)
+# else
+# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+# endif
+#endif
+
+
+/* YYLEX -- calling `yylex' with the right arguments. */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (YYLEX_PARAM)
+#else
+# define YYLEX yylex ()
+#endif
+
+/* Enable debugging if requested. */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+# include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+# define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args) \
+do { \
+ if (yydebug) \
+ YYFPRINTF Args; \
+} while (YYID (0))
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \
+do { \
+ if (yydebug) \
+ { \
+ YYFPRINTF (stderr, "%s ", Title); \
+ yy_symbol_print (stderr, \
+ Type, Value); \
+ YYFPRINTF (stderr, "\n"); \
+ } \
+} while (YYID (0))
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+#else
+static void
+yy_symbol_value_print (yyoutput, yytype, yyvaluep)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+#endif
+{
+ if (!yyvaluep)
+ return;
+# ifdef YYPRINT
+ if (yytype < YYNTOKENS)
+ YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# else
+ YYUSE (yyoutput);
+# endif
+ switch (yytype)
+ {
+ default:
+ break;
+ }
+}
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+#else
+static void
+yy_symbol_print (yyoutput, yytype, yyvaluep)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+#endif
+{
+ if (yytype < YYNTOKENS)
+ YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+ else
+ YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+ yy_symbol_value_print (yyoutput, yytype, yyvaluep);
+ YYFPRINTF (yyoutput, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included). |
+`------------------------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_stack_print (yytype_int16 *bottom, yytype_int16 *top)
+#else
+static void
+yy_stack_print (bottom, top)
+ yytype_int16 *bottom;
+ yytype_int16 *top;
+#endif
+{
+ YYFPRINTF (stderr, "Stack now");
+ for (; bottom <= top; ++bottom)
+ YYFPRINTF (stderr, " %d", *bottom);
+ YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top) \
+do { \
+ if (yydebug) \
+ yy_stack_print ((Bottom), (Top)); \
+} while (YYID (0))
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced. |
+`------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_reduce_print (YYSTYPE *yyvsp, int yyrule)
+#else
+static void
+yy_reduce_print (yyvsp, yyrule)
+ YYSTYPE *yyvsp;
+ int yyrule;
+#endif
+{
+ int yynrhs = yyr2[yyrule];
+ int yyi;
+ unsigned long int yylno = yyrline[yyrule];
+ YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+ yyrule - 1, yylno);
+ /* The symbols being reduced. */
+ for (yyi = 0; yyi < yynrhs; yyi++)
+ {
+ fprintf (stderr, " $%d = ", yyi + 1);
+ yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
+ &(yyvsp[(yyi + 1) - (yynrhs)])
+ );
+ fprintf (stderr, "\n");
+ }
+}
+
+# define YY_REDUCE_PRINT(Rule) \
+do { \
+ if (yydebug) \
+ yy_reduce_print (yyvsp, Rule); \
+} while (YYID (0))
+
+/* Nonzero means print parse trace. It is left uninitialized so that
+ multiple parsers can coexist. */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks. */
+#ifndef YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+ if the built-in stack extension method is used).
+
+ Do not make this value too large; the results are undefined if
+ YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+ evaluated with infinite-precision integer arithmetic. */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+# if defined __GLIBC__ && defined _STRING_H
+# define yystrlen strlen
+# else
+/* Return the length of YYSTR. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static YYSIZE_T
+yystrlen (const char *yystr)
+#else
+static YYSIZE_T
+yystrlen (yystr)
+ const char *yystr;
+#endif
+{
+ YYSIZE_T yylen;
+ for (yylen = 0; yystr[yylen]; yylen++)
+ continue;
+ return yylen;
+}
+# endif
+# endif
+
+# ifndef yystpcpy
+# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+# define yystpcpy stpcpy
+# else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+ YYDEST. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static char *
+yystpcpy (char *yydest, const char *yysrc)
+#else
+static char *
+yystpcpy (yydest, yysrc)
+ char *yydest;
+ const char *yysrc;
+#endif
+{
+ char *yyd = yydest;
+ const char *yys = yysrc;
+
+ while ((*yyd++ = *yys++) != '\0')
+ continue;
+
+ return yyd - 1;
+}
+# endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+ quotes and backslashes, so that it's suitable for yyerror. The
+ heuristic is that double-quoting is unnecessary unless the string
+ contains an apostrophe, a comma, or backslash (other than
+ backslash-backslash). YYSTR is taken from yytname. If YYRES is
+ null, do not copy; instead, return the length of what the result
+ would have been. */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+ if (*yystr == '"')
+ {
+ YYSIZE_T yyn = 0;
+ char const *yyp = yystr;
+
+ for (;;)
+ switch (*++yyp)
+ {
+ case '\'':
+ case ',':
+ goto do_not_strip_quotes;
+
+ case '\\':
+ if (*++yyp != '\\')
+ goto do_not_strip_quotes;
+ /* Fall through. */
+ default:
+ if (yyres)
+ yyres[yyn] = *yyp;
+ yyn++;
+ break;
+
+ case '"':
+ if (yyres)
+ yyres[yyn] = '\0';
+ return yyn;
+ }
+ do_not_strip_quotes: ;
+ }
+
+ if (! yyres)
+ return yystrlen (yystr);
+
+ return yystpcpy (yyres, yystr) - yyres;
+}
+# endif
+
+/* Copy into YYRESULT an error message about the unexpected token
+ YYCHAR while in state YYSTATE. Return the number of bytes copied,
+ including the terminating null byte. If YYRESULT is null, do not
+ copy anything; just return the number of bytes that would be
+ copied. As a special case, return 0 if an ordinary "syntax error"
+ message will do. Return YYSIZE_MAXIMUM if overflow occurs during
+ size calculation. */
+static YYSIZE_T
+yysyntax_error (char *yyresult, int yystate, int yychar)
+{
+ int yyn = yypact[yystate];
+
+ if (! (YYPACT_NINF < yyn && yyn <= YYLAST))
+ return 0;
+ else
+ {
+ int yytype = YYTRANSLATE (yychar);
+ YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]);
+ YYSIZE_T yysize = yysize0;
+ YYSIZE_T yysize1;
+ int yysize_overflow = 0;
+ enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+ int yyx;
+
+# if 0
+ /* This is so xgettext sees the translatable formats that are
+ constructed on the fly. */
+ YY_("syntax error, unexpected %s");
+ YY_("syntax error, unexpected %s, expecting %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s or %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s");
+# endif
+ char *yyfmt;
+ char const *yyf;
+ static char const yyunexpected[] = "syntax error, unexpected %s";
+ static char const yyexpecting[] = ", expecting %s";
+ static char const yyor[] = " or %s";
+ char yyformat[sizeof yyunexpected
+ + sizeof yyexpecting - 1
+ + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2)
+ * (sizeof yyor - 1))];
+ char const *yyprefix = yyexpecting;
+
+ /* Start YYX at -YYN if negative to avoid negative indexes in
+ YYCHECK. */
+ int yyxbegin = yyn < 0 ? -yyn : 0;
+
+ /* Stay within bounds of both yycheck and yytname. */
+ int yychecklim = YYLAST - yyn + 1;
+ int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+ int yycount = 1;
+
+ yyarg[0] = yytname[yytype];
+ yyfmt = yystpcpy (yyformat, yyunexpected);
+
+ for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+ {
+ if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+ {
+ yycount = 1;
+ yysize = yysize0;
+ yyformat[sizeof yyunexpected - 1] = '\0';
+ break;
+ }
+ yyarg[yycount++] = yytname[yyx];
+ yysize1 = yysize + yytnamerr (0, yytname[yyx]);
+ yysize_overflow |= (yysize1 < yysize);
+ yysize = yysize1;
+ yyfmt = yystpcpy (yyfmt, yyprefix);
+ yyprefix = yyor;
+ }
+
+ yyf = YY_(yyformat);
+ yysize1 = yysize + yystrlen (yyf);
+ yysize_overflow |= (yysize1 < yysize);
+ yysize = yysize1;
+
+ if (yysize_overflow)
+ return YYSIZE_MAXIMUM;
+
+ if (yyresult)
+ {
+ /* Avoid sprintf, as that infringes on the user's name space.
+ Don't have undefined behavior even if the translation
+ produced a string with the wrong number of "%s"s. */
+ char *yyp = yyresult;
+ int yyi = 0;
+ while ((*yyp = *yyf) != '\0')
+ {
+ if (*yyp == '%' && yyf[1] == 's' && yyi < yycount)
+ {
+ yyp += yytnamerr (yyp, yyarg[yyi++]);
+ yyf += 2;
+ }
+ else
+ {
+ yyp++;
+ yyf++;
+ }
+ }
+ }
+ return yysize;
+ }
+}
+#endif /* YYERROR_VERBOSE */
+
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol. |
+`-----------------------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yydestruct (yymsg, yytype, yyvaluep)
+ const char *yymsg;
+ int yytype;
+ YYSTYPE *yyvaluep;
+#endif
+{
+ YYUSE (yyvaluep);
+
+ if (!yymsg)
+ yymsg = "Deleting";
+ YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+ switch (yytype)
+ {
+
+ default:
+ break;
+ }
+}
+
+
+/* Prevent warnings from -Wmissing-prototypes. */
+
+#ifdef YYPARSE_PARAM
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void *YYPARSE_PARAM);
+#else
+int yyparse ();
+#endif
+#else /* ! YYPARSE_PARAM */
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+
+
+/* The look-ahead symbol. */
+int yychar;
+
+/* The semantic value of the look-ahead symbol. */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far. */
+int yynerrs;
+
+
+
+/*----------.
+| yyparse. |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void *YYPARSE_PARAM)
+#else
+int
+yyparse (YYPARSE_PARAM)
+ void *YYPARSE_PARAM;
+#endif
+#else /* ! YYPARSE_PARAM */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void)
+#else
+int
+yyparse ()
+
+#endif
+#endif
+{
+
+ int yystate;
+ int yyn;
+ int yyresult;
+ /* Number of tokens to shift before error messages enabled. */
+ int yyerrstatus;
+ /* Look-ahead token as an internal (translated) token number. */
+ int yytoken = 0;
+#if YYERROR_VERBOSE
+ /* Buffer for error messages, and its allocated size. */
+ char yymsgbuf[128];
+ char *yymsg = yymsgbuf;
+ YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
+#endif
+
+ /* Three stacks and their tools:
+ `yyss': related to states,
+ `yyvs': related to semantic values,
+ `yyls': related to locations.
+
+ Refer to the stacks thru separate pointers, to allow yyoverflow
+ to reallocate them elsewhere. */
+
+ /* The state stack. */
+ yytype_int16 yyssa[YYINITDEPTH];
+ yytype_int16 *yyss = yyssa;
+ yytype_int16 *yyssp;
+
+ /* The semantic value stack. */
+ YYSTYPE yyvsa[YYINITDEPTH];
+ YYSTYPE *yyvs = yyvsa;
+ YYSTYPE *yyvsp;
+
+
+
+#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N))
+
+ YYSIZE_T yystacksize = YYINITDEPTH;
+
+ /* The variables used to return semantic value and location from the
+ action routines. */
+ YYSTYPE yyval;
+
+
+ /* The number of symbols on the RHS of the reduced rule.
+ Keep to zero when no symbol should be popped. */
+ int yylen = 0;
+
+ YYDPRINTF ((stderr, "Starting parse\n"));
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+
+ /* Initialize stack pointers.
+ Waste one element of value and location stack
+ so that they stay on the same level as the state stack.
+ The wasted elements are never initialized. */
+
+ yyssp = yyss;
+ yyvsp = yyvs;
+
+ goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate. |
+`------------------------------------------------------------*/
+ yynewstate:
+ /* In all cases, when you get here, the value and location stacks
+ have just been pushed. So pushing a state here evens the stacks. */
+ yyssp++;
+
+ yysetstate:
+ *yyssp = yystate;
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ {
+ /* Get the current used size of the three stacks, in elements. */
+ YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ {
+ /* Give user a chance to reallocate the stack. Use copies of
+ these so that the &'s don't force the real ones into
+ memory. */
+ YYSTYPE *yyvs1 = yyvs;
+ yytype_int16 *yyss1 = yyss;
+
+
+ /* Each stack pointer address is followed by the size of the
+ data in use in that stack, in bytes. This used to be a
+ conditional around just the two extra args, but that might
+ be undefined if yyoverflow is a macro. */
+ yyoverflow (YY_("memory exhausted"),
+ &yyss1, yysize * sizeof (*yyssp),
+ &yyvs1, yysize * sizeof (*yyvsp),
+
+ &yystacksize);
+
+ yyss = yyss1;
+ yyvs = yyvs1;
+ }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+ goto yyexhaustedlab;
+# else
+ /* Extend the stack our own way. */
+ if (YYMAXDEPTH <= yystacksize)
+ goto yyexhaustedlab;
+ yystacksize *= 2;
+ if (YYMAXDEPTH < yystacksize)
+ yystacksize = YYMAXDEPTH;
+
+ {
+ yytype_int16 *yyss1 = yyss;
+ union yyalloc *yyptr =
+ (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+ if (! yyptr)
+ goto yyexhaustedlab;
+ YYSTACK_RELOCATE (yyss);
+ YYSTACK_RELOCATE (yyvs);
+
+# undef YYSTACK_RELOCATE
+ if (yyss1 != yyssa)
+ YYSTACK_FREE (yyss1);
+ }
+# endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + yysize - 1;
+ yyvsp = yyvs + yysize - 1;
+
+
+ YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+ (unsigned long int) yystacksize));
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ YYABORT;
+ }
+
+ YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+ goto yybackup;
+
+/*-----------.
+| yybackup. |
+`-----------*/
+yybackup:
+
+ /* Do appropriate processing given the current state. Read a
+ look-ahead token if we need one and don't already have one. */
+
+ /* First try to decide what to do without reference to look-ahead token. */
+ yyn = yypact[yystate];
+ if (yyn == YYPACT_NINF)
+ goto yydefault;
+
+ /* Not known => get a look-ahead token if don't already have one. */
+
+ /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */
+ if (yychar == YYEMPTY)
+ {
+ YYDPRINTF ((stderr, "Reading a token: "));
+ yychar = YYLEX;
+ }
+
+ if (yychar <= YYEOF)
+ {
+ yychar = yytoken = YYEOF;
+ YYDPRINTF ((stderr, "Now at end of input.\n"));
+ }
+ else
+ {
+ yytoken = YYTRANSLATE (yychar);
+ YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+ }
+
+ /* If the proper action on seeing token YYTOKEN is to reduce or to
+ detect an error, take that action. */
+ yyn += yytoken;
+ if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+ goto yydefault;
+ yyn = yytable[yyn];
+ if (yyn <= 0)
+ {
+ if (yyn == 0 || yyn == YYTABLE_NINF)
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ /* Count tokens shifted since error; after three, turn off error
+ status. */
+ if (yyerrstatus)
+ yyerrstatus--;
+
+ /* Shift the look-ahead token. */
+ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+ /* Discard the shifted token unless it is eof. */
+ if (yychar != YYEOF)
+ yychar = YYEMPTY;
+
+ yystate = yyn;
+ *++yyvsp = yylval;
+
+ goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state. |
+`-----------------------------------------------------------*/
+yydefault:
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+ goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction. |
+`-----------------------------*/
+yyreduce:
+ /* yyn is the number of a rule to reduce with. */
+ yylen = yyr2[yyn];
+
+ /* If YYLEN is nonzero, implement the default value of the action:
+ `$$ = $1'.
+
+ Otherwise, the following line sets YYVAL to garbage.
+ This behavior is undocumented and Bison
+ users should not rely upon it. Assigning to YYVAL
+ unconditionally makes the parser a bit smaller, and it avoids a
+ GCC warning that YYVAL may be used uninitialized. */
+ yyval = yyvsp[1-yylen];
+
+
+ YY_REDUCE_PRINT (yyn);
+ switch (yyn)
+ {
+ case 2:
+#line 74 "heimdal/lib/hx509/sel-gram.y"
+ { _hx509_expr_input.expr = (yyvsp[(1) - (1)].expr); }
+ break;
+
+ case 3:
+#line 76 "heimdal/lib/hx509/sel-gram.y"
+ { (yyval.expr) = _hx509_make_expr(op_TRUE, NULL, NULL); }
+ break;
+
+ case 4:
+#line 77 "heimdal/lib/hx509/sel-gram.y"
+ { (yyval.expr) = _hx509_make_expr(op_FALSE, NULL, NULL); }
+ break;
+
+ case 5:
+#line 78 "heimdal/lib/hx509/sel-gram.y"
+ { (yyval.expr) = _hx509_make_expr(op_NOT, (yyvsp[(2) - (2)].expr), NULL); }
+ break;
+
+ case 6:
+#line 79 "heimdal/lib/hx509/sel-gram.y"
+ { (yyval.expr) = _hx509_make_expr(op_AND, (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr)); }
+ break;
+
+ case 7:
+#line 80 "heimdal/lib/hx509/sel-gram.y"
+ { (yyval.expr) = _hx509_make_expr(op_OR, (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr)); }
+ break;
+
+ case 8:
+#line 81 "heimdal/lib/hx509/sel-gram.y"
+ { (yyval.expr) = (yyvsp[(2) - (3)].expr); }
+ break;
+
+ case 9:
+#line 82 "heimdal/lib/hx509/sel-gram.y"
+ { (yyval.expr) = _hx509_make_expr(op_COMP, (yyvsp[(1) - (1)].expr), NULL); }
+ break;
+
+ case 10:
+#line 85 "heimdal/lib/hx509/sel-gram.y"
+ { (yyval.expr) = _hx509_make_expr(expr_WORDS, (yyvsp[(1) - (1)].expr), NULL); }
+ break;
+
+ case 11:
+#line 86 "heimdal/lib/hx509/sel-gram.y"
+ { (yyval.expr) = _hx509_make_expr(expr_WORDS, (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr)); }
+ break;
+
+ case 12:
+#line 89 "heimdal/lib/hx509/sel-gram.y"
+ { (yyval.expr) = _hx509_make_expr(comp_EQ, (yyvsp[(1) - (4)].expr), (yyvsp[(4) - (4)].expr)); }
+ break;
+
+ case 13:
+#line 90 "heimdal/lib/hx509/sel-gram.y"
+ { (yyval.expr) = _hx509_make_expr(comp_NE, (yyvsp[(1) - (4)].expr), (yyvsp[(4) - (4)].expr)); }
+ break;
+
+ case 14:
+#line 91 "heimdal/lib/hx509/sel-gram.y"
+ { (yyval.expr) = _hx509_make_expr(comp_TAILEQ, (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr)); }
+ break;
+
+ case 15:
+#line 92 "heimdal/lib/hx509/sel-gram.y"
+ { (yyval.expr) = _hx509_make_expr(comp_IN, (yyvsp[(1) - (5)].expr), (yyvsp[(4) - (5)].expr)); }
+ break;
+
+ case 16:
+#line 93 "heimdal/lib/hx509/sel-gram.y"
+ { (yyval.expr) = _hx509_make_expr(comp_IN, (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr)); }
+ break;
+
+ case 17:
+#line 96 "heimdal/lib/hx509/sel-gram.y"
+ { (yyval.expr) = (yyvsp[(1) - (1)].expr); }
+ break;
+
+ case 18:
+#line 97 "heimdal/lib/hx509/sel-gram.y"
+ { (yyval.expr) = (yyvsp[(1) - (1)].expr); }
+ break;
+
+ case 19:
+#line 98 "heimdal/lib/hx509/sel-gram.y"
+ { (yyval.expr) = (yyvsp[(1) - (1)].expr); }
+ break;
+
+ case 20:
+#line 99 "heimdal/lib/hx509/sel-gram.y"
+ { (yyval.expr) = (yyvsp[(1) - (1)].expr); }
+ break;
+
+ case 21:
+#line 102 "heimdal/lib/hx509/sel-gram.y"
+ { (yyval.expr) = _hx509_make_expr(expr_NUMBER, (yyvsp[(1) - (1)].string), NULL); }
+ break;
+
+ case 22:
+#line 103 "heimdal/lib/hx509/sel-gram.y"
+ { (yyval.expr) = _hx509_make_expr(expr_STRING, (yyvsp[(1) - (1)].string), NULL); }
+ break;
+
+ case 23:
+#line 105 "heimdal/lib/hx509/sel-gram.y"
+ {
+ (yyval.expr) = _hx509_make_expr(expr_FUNCTION, (yyvsp[(1) - (4)].string), (yyvsp[(3) - (4)].expr)); }
+ break;
+
+ case 24:
+#line 108 "heimdal/lib/hx509/sel-gram.y"
+ { (yyval.expr) = (yyvsp[(3) - (4)].expr); }
+ break;
+
+ case 25:
+#line 111 "heimdal/lib/hx509/sel-gram.y"
+ {
+ (yyval.expr) = _hx509_make_expr(expr_VAR, (yyvsp[(1) - (3)].string), (yyvsp[(3) - (3)].expr)); }
+ break;
+
+ case 26:
+#line 113 "heimdal/lib/hx509/sel-gram.y"
+ {
+ (yyval.expr) = _hx509_make_expr(expr_VAR, (yyvsp[(1) - (1)].string), NULL); }
+ break;
+
+
+/* Line 1267 of yacc.c. */
+#line 1501 "heimdal/lib/hx509/sel-gram.y"
+ default: break;
+ }
+ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+
+ *++yyvsp = yyval;
+
+
+ /* Now `shift' the result of the reduction. Determine what state
+ that goes to, based on the state we popped back to and the rule
+ number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+ if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTOKENS];
+
+ goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+ /* If not already recovering from an error, report this error. */
+ if (!yyerrstatus)
+ {
+ ++yynerrs;
+#if ! YYERROR_VERBOSE
+ yyerror (YY_("syntax error"));
+#else
+ {
+ YYSIZE_T yysize = yysyntax_error (0, yystate, yychar);
+ if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM)
+ {
+ YYSIZE_T yyalloc = 2 * yysize;
+ if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM))
+ yyalloc = YYSTACK_ALLOC_MAXIMUM;
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+ yymsg = (char *) YYSTACK_ALLOC (yyalloc);
+ if (yymsg)
+ yymsg_alloc = yyalloc;
+ else
+ {
+ yymsg = yymsgbuf;
+ yymsg_alloc = sizeof yymsgbuf;
+ }
+ }
+
+ if (0 < yysize && yysize <= yymsg_alloc)
+ {
+ (void) yysyntax_error (yymsg, yystate, yychar);
+ yyerror (yymsg);
+ }
+ else
+ {
+ yyerror (YY_("syntax error"));
+ if (yysize != 0)
+ goto yyexhaustedlab;
+ }
+ }
+#endif
+ }
+
+
+
+ if (yyerrstatus == 3)
+ {
+ /* If just tried and failed to reuse look-ahead token after an
+ error, discard it. */
+
+ if (yychar <= YYEOF)
+ {
+ /* Return failure if at end of input. */
+ if (yychar == YYEOF)
+ YYABORT;
+ }
+ else
+ {
+ yydestruct ("Error: discarding",
+ yytoken, &yylval);
+ yychar = YYEMPTY;
+ }
+ }
+
+ /* Else will try to reuse look-ahead token after shifting the error
+ token. */
+ goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR. |
+`---------------------------------------------------*/
+yyerrorlab:
+
+ /* Pacify compilers like GCC when the user code never invokes
+ YYERROR and the label yyerrorlab therefore never appears in user
+ code. */
+ if (/*CONSTCOND*/ 0)
+ goto yyerrorlab;
+
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYERROR. */
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+ yystate = *yyssp;
+ goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR. |
+`-------------------------------------------------------------*/
+yyerrlab1:
+ yyerrstatus = 3; /* Each real token shifted decrements this. */
+
+ for (;;)
+ {
+ yyn = yypact[yystate];
+ if (yyn != YYPACT_NINF)
+ {
+ yyn += YYTERROR;
+ if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+ {
+ yyn = yytable[yyn];
+ if (0 < yyn)
+ break;
+ }
+ }
+
+ /* Pop the current state because it cannot handle the error token. */
+ if (yyssp == yyss)
+ YYABORT;
+
+
+ yydestruct ("Error: popping",
+ yystos[yystate], yyvsp);
+ YYPOPSTACK (1);
+ yystate = *yyssp;
+ YY_STACK_PRINT (yyss, yyssp);
+ }
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ *++yyvsp = yylval;
+
+
+ /* Shift the error token. */
+ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here. |
+`-------------------------------------*/
+yyacceptlab:
+ yyresult = 0;
+ goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here. |
+`-----------------------------------*/
+yyabortlab:
+ yyresult = 1;
+ goto yyreturn;
+
+#ifndef yyoverflow
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here. |
+`-------------------------------------------------*/
+yyexhaustedlab:
+ yyerror (YY_("memory exhausted"));
+ yyresult = 2;
+ /* Fall through. */
+#endif
+
+yyreturn:
+ if (yychar != YYEOF && yychar != YYEMPTY)
+ yydestruct ("Cleanup: discarding lookahead",
+ yytoken, &yylval);
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYABORT or YYACCEPT. */
+ YYPOPSTACK (yylen);
+ YY_STACK_PRINT (yyss, yyssp);
+ while (yyssp != yyss)
+ {
+ yydestruct ("Cleanup: popping",
+ yystos[*yyssp], yyvsp);
+ YYPOPSTACK (1);
+ }
+#ifndef yyoverflow
+ if (yyss != yyssa)
+ YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+#endif
+ /* Make sure YYID is used. */
+ return YYID (yyresult);
+}
+
+
+
diff --git a/source4/heimdal/lib/hx509/sel-gram.h b/source4/heimdal/lib/hx509/sel-gram.h
new file mode 100644
index 0000000000..bb4a64d1c7
--- /dev/null
+++ b/source4/heimdal/lib/hx509/sel-gram.h
@@ -0,0 +1,83 @@
+/* A Bison parser, made by GNU Bison 2.3. */
+
+/* Skeleton interface for Bison's Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+ Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU 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., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ kw_TRUE = 258,
+ kw_FALSE = 259,
+ kw_AND = 260,
+ kw_OR = 261,
+ kw_IN = 262,
+ kw_TAILMATCH = 263,
+ NUMBER = 264,
+ STRING = 265,
+ IDENTIFIER = 266
+ };
+#endif
+/* Tokens. */
+#define kw_TRUE 258
+#define kw_FALSE 259
+#define kw_AND 260
+#define kw_OR 261
+#define kw_IN 262
+#define kw_TAILMATCH 263
+#define NUMBER 264
+#define STRING 265
+#define IDENTIFIER 266
+
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+#line 46 "heimdal/lib/hx509/sel-gram.y"
+{
+ char *string;
+ struct hx_expr *expr;
+}
+/* Line 1489 of yacc.c. */
+#line 76 "heimdal/lib/hx509/sel-gram.y"
+ YYSTYPE;
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+extern YYSTYPE yylval;
+
diff --git a/source4/heimdal/lib/hx509/sel-gram.y b/source4/heimdal/lib/hx509/sel-gram.y
new file mode 100644
index 0000000000..e529479724
--- /dev/null
+++ b/source4/heimdal/lib/hx509/sel-gram.y
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+%{
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <hx_locl.h>
+
+RCSID("$Id$");
+
+%}
+
+%union {
+ char *string;
+ struct hx_expr *expr;
+}
+
+%token kw_TRUE
+%token kw_FALSE
+%token kw_AND
+%token kw_OR
+%token kw_IN
+%token kw_TAILMATCH
+
+%type <expr> expr
+%type <expr> comp
+%type <expr> word words
+%type <expr> number
+%type <expr> string
+%type <expr> function
+%type <expr> variable variables
+
+%token <string> NUMBER
+%token <string> STRING
+%token <string> IDENTIFIER
+
+%start start
+
+%%
+
+start: expr { _hx509_expr_input.expr = $1; }
+
+expr : kw_TRUE { $$ = _hx509_make_expr(op_TRUE, NULL, NULL); }
+ | kw_FALSE { $$ = _hx509_make_expr(op_FALSE, NULL, NULL); }
+ | '!' expr { $$ = _hx509_make_expr(op_NOT, $2, NULL); }
+ | expr kw_AND expr { $$ = _hx509_make_expr(op_AND, $1, $3); }
+ | expr kw_OR expr { $$ = _hx509_make_expr(op_OR, $1, $3); }
+ | '(' expr ')' { $$ = $2; }
+ | comp { $$ = _hx509_make_expr(op_COMP, $1, NULL); }
+ ;
+
+words : word { $$ = _hx509_make_expr(expr_WORDS, $1, NULL); }
+ | word ',' words { $$ = _hx509_make_expr(expr_WORDS, $1, $3); }
+ ;
+
+comp : word '=' '=' word { $$ = _hx509_make_expr(comp_EQ, $1, $4); }
+ | word '!' '=' word { $$ = _hx509_make_expr(comp_NE, $1, $4); }
+ | word kw_TAILMATCH word { $$ = _hx509_make_expr(comp_TAILEQ, $1, $3); }
+ | word kw_IN '(' words ')' { $$ = _hx509_make_expr(comp_IN, $1, $4); }
+ | word kw_IN variable { $$ = _hx509_make_expr(comp_IN, $1, $3); }
+ ;
+
+word : number { $$ = $1; }
+ | string { $$ = $1; }
+ | function { $$ = $1; }
+ | variable { $$ = $1; }
+ ;
+
+number : NUMBER { $$ = _hx509_make_expr(expr_NUMBER, $1, NULL); };
+string : STRING { $$ = _hx509_make_expr(expr_STRING, $1, NULL); };
+
+function: IDENTIFIER '(' words ')' {
+ $$ = _hx509_make_expr(expr_FUNCTION, $1, $3); }
+ ;
+variable: '%' '{' variables '}' { $$ = $3; }
+ ;
+
+variables: IDENTIFIER '.' variables {
+ $$ = _hx509_make_expr(expr_VAR, $1, $3); }
+ | IDENTIFIER {
+ $$ = _hx509_make_expr(expr_VAR, $1, NULL); }
+ ;
diff --git a/source4/heimdal/lib/hx509/sel-lex.c b/source4/heimdal/lib/hx509/sel-lex.c
new file mode 100644
index 0000000000..75abf27aed
--- /dev/null
+++ b/source4/heimdal/lib/hx509/sel-lex.c
@@ -0,0 +1,1899 @@
+#include "config.h"
+
+#line 3 "heimdal/lib/hx509/sel-lex.c"
+
+#define YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+#define YY_FLEX_SUBMINOR_VERSION 34
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* First, we deal with platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types.
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t;
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+#endif /* ! C99 */
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX (4294967295U)
+#endif
+
+#endif /* ! FLEXINT_H */
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else /* ! __cplusplus */
+
+/* C99 requires __STDC__ to be defined as 1. */
+#if defined (__STDC__)
+
+#define YY_USE_CONST
+
+#endif /* defined (__STDC__) */
+#endif /* ! __cplusplus */
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index. If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* Enter a start condition. This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN (yy_start) = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state. The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START (((yy_start) - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE yyrestart(yyin )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#define YY_BUF_SIZE 16384
+#endif
+
+/* The state buf must be large enough to hold one state per character in the main buffer.
+ */
+#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+extern int yyleng;
+
+extern FILE *yyin, *yyout;
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+ #define YY_LESS_LINENO(n)
+
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ *yy_cp = (yy_hold_char); \
+ YY_RESTORE_YY_MORE_OFFSET \
+ (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+ YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+ } \
+ while ( 0 )
+
+#define unput(c) yyunput( c, (yytext_ptr) )
+
+/* The following is because we cannot portably get our hands on size_t
+ * (without autoconf's help, which isn't available because we want
+ * flex-generated scanners to compile on their own).
+ * Given that the standard has decreed that size_t exists since 1989,
+ * I guess we can afford to depend on it. Manoj.
+ */
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef size_t yy_size_t;
+#endif
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+ {
+ FILE *yy_input_file;
+
+ char *yy_ch_buf; /* input buffer */
+ char *yy_buf_pos; /* current position in input buffer */
+
+ /* Size of input buffer in bytes, not including room for EOB
+ * characters.
+ */
+ yy_size_t yy_buf_size;
+
+ /* Number of characters read into yy_ch_buf, not including EOB
+ * characters.
+ */
+ int yy_n_chars;
+
+ /* Whether we "own" the buffer - i.e., we know we created it,
+ * and can realloc() it to grow it, and should free() it to
+ * delete it.
+ */
+ int yy_is_our_buffer;
+
+ /* Whether this is an "interactive" input source; if so, and
+ * if we're using stdio for input, then we want to use getc()
+ * instead of fread(), to make sure we stop fetching input after
+ * each newline.
+ */
+ int yy_is_interactive;
+
+ /* Whether we're considered to be at the beginning of a line.
+ * If so, '^' rules will be active on the next match, otherwise
+ * not.
+ */
+ int yy_at_bol;
+
+ int yy_bs_lineno; /**< The line count. */
+ int yy_bs_column; /**< The column count. */
+
+ /* Whether to try to fill the input buffer when we reach the
+ * end of it.
+ */
+ int yy_fill_buffer;
+
+ int yy_buffer_status;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+ /* When an EOF's been seen but there's still some text to process
+ * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+ * shouldn't try reading from the input source any more. We might
+ * still have a bunch of tokens to match, though, because of
+ * possible backing-up.
+ *
+ * When we actually see the EOF, we change the status to "new"
+ * (via yyrestart()), so that the user can continue scanning by
+ * just pointing yyin at a new input file.
+ */
+#define YY_BUFFER_EOF_PENDING 2
+
+ };
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+/* Stack of input buffers. */
+static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */
+static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */
+static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \
+ ? (yy_buffer_stack)[(yy_buffer_stack_top)] \
+ : NULL)
+
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)]
+
+/* yy_hold_char holds the character lost when yytext is formed. */
+static char yy_hold_char;
+static int yy_n_chars; /* number of characters read into yy_ch_buf */
+int yyleng;
+
+/* Points to current character in buffer. */
+static char *yy_c_buf_p = (char *) 0;
+static int yy_init = 0; /* whether we need to initialize */
+static int yy_start = 0; /* start state number */
+
+/* Flag which is used to allow yywrap()'s to do buffer switches
+ * instead of setting up a fresh yyin. A bit of a hack ...
+ */
+static int yy_did_buffer_switch_on_eof;
+
+void yyrestart (FILE *input_file );
+void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer );
+YY_BUFFER_STATE yy_create_buffer (FILE *file,int size );
+void yy_delete_buffer (YY_BUFFER_STATE b );
+void yy_flush_buffer (YY_BUFFER_STATE b );
+void yypush_buffer_state (YY_BUFFER_STATE new_buffer );
+void yypop_buffer_state (void );
+
+static void yyensure_buffer_stack (void );
+static void yy_load_buffer_state (void );
+static void yy_init_buffer (YY_BUFFER_STATE b,FILE *file );
+
+#define YY_FLUSH_BUFFER yy_flush_buffer(YY_CURRENT_BUFFER )
+
+YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size );
+YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str );
+YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,int len );
+
+void *yyalloc (yy_size_t );
+void *yyrealloc (void *,yy_size_t );
+void yyfree (void * );
+
+#define yy_new_buffer yy_create_buffer
+
+#define yy_set_interactive(is_interactive) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){ \
+ yyensure_buffer_stack (); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ yy_create_buffer(yyin,YY_BUF_SIZE ); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+ }
+
+#define yy_set_bol(at_bol) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){\
+ yyensure_buffer_stack (); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ yy_create_buffer(yyin,YY_BUF_SIZE ); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+ }
+
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+/* Begin user sect3 */
+
+typedef unsigned char YY_CHAR;
+
+FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0;
+
+typedef int yy_state_type;
+
+extern int yylineno;
+
+int yylineno = 1;
+
+extern char *yytext;
+#define yytext_ptr yytext
+
+static yy_state_type yy_get_previous_state (void );
+static yy_state_type yy_try_NUL_trans (yy_state_type current_state );
+static int yy_get_next_buffer (void );
+static void yy_fatal_error (yyconst char msg[] );
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+ (yytext_ptr) = yy_bp; \
+ yyleng = (size_t) (yy_cp - yy_bp); \
+ (yy_hold_char) = *yy_cp; \
+ *yy_cp = '\0'; \
+ (yy_c_buf_p) = yy_cp;
+
+#define YY_NUM_RULES 12
+#define YY_END_OF_BUFFER 13
+/* This struct is not used in this scanner,
+ but its presence is necessary. */
+struct yy_trans_info
+ {
+ flex_int32_t yy_verify;
+ flex_int32_t yy_nxt;
+ };
+static yyconst flex_int16_t yy_accept[36] =
+ { 0,
+ 0, 0, 13, 12, 11, 9, 10, 8, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 5, 4, 7,
+ 7, 3, 7, 7, 7, 7, 7, 1, 2, 7,
+ 7, 7, 7, 6, 0
+ } ;
+
+static yyconst flex_int32_t yy_ec[256] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 2, 3,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 2, 4, 5, 1, 1, 4, 1, 1, 4,
+ 4, 1, 1, 4, 6, 4, 1, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 1, 1, 1,
+ 4, 1, 1, 1, 7, 8, 9, 10, 11, 12,
+ 8, 13, 14, 8, 8, 15, 16, 17, 18, 8,
+ 8, 19, 20, 21, 22, 8, 8, 8, 8, 8,
+ 1, 1, 1, 1, 6, 1, 8, 8, 8, 8,
+
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 4, 1, 4, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1
+ } ;
+
+static yyconst flex_int32_t yy_meta[23] =
+ { 0,
+ 1, 1, 1, 1, 1, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2
+ } ;
+
+static yyconst flex_int16_t yy_base[37] =
+ { 0,
+ 0, 0, 43, 44, 44, 44, 44, 44, 25, 0,
+ 34, 23, 20, 16, 0, 28, 22, 0, 0, 22,
+ 12, 0, 13, 17, 20, 19, 13, 0, 0, 21,
+ 6, 17, 12, 0, 44, 22
+ } ;
+
+static yyconst flex_int16_t yy_def[37] =
+ { 0,
+ 35, 1, 35, 35, 35, 35, 35, 35, 36, 36,
+ 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,
+ 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,
+ 36, 36, 36, 36, 0, 35
+ } ;
+
+static yyconst flex_int16_t yy_nxt[67] =
+ { 0,
+ 4, 5, 6, 7, 8, 4, 9, 10, 10, 10,
+ 10, 11, 10, 12, 10, 10, 10, 13, 10, 10,
+ 14, 10, 20, 15, 34, 33, 32, 31, 30, 29,
+ 28, 27, 26, 25, 21, 24, 23, 22, 19, 18,
+ 17, 16, 35, 3, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35
+ } ;
+
+static yyconst flex_int16_t yy_chk[67] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 14, 36, 33, 32, 31, 30, 27, 26,
+ 25, 24, 23, 21, 14, 20, 17, 16, 13, 12,
+ 11, 9, 3, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35
+ } ;
+
+static yy_state_type yy_last_accepting_state;
+static char *yy_last_accepting_cpos;
+
+extern int yy_flex_debug;
+int yy_flex_debug = 0;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+char *yytext;
+#line 1 "sel-lex.l"
+#line 2 "sel-lex.l"
+/*
+ * Copyright (c) 2004, 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#undef ECHO
+
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include "sel.h"
+#include "sel-gram.h"
+unsigned lineno = 1;
+
+static char * handle_string(void);
+static int lex_input(char *, int);
+
+struct hx_expr_input _hx509_expr_input;
+
+#define YY_NO_UNPUT 1
+
+#undef YY_INPUT
+#define YY_INPUT(buf,res,maxsize) (res = lex_input(buf, maxsize))
+
+#undef ECHO
+
+#line 541 "heimdal/lib/hx509/sel-lex.c"
+
+#define INITIAL 0
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+#include <unistd.h>
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+static int yy_init_globals (void );
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int yywrap (void );
+#else
+extern int yywrap (void );
+#endif
+#endif
+
+ static void yyunput (int c,char *buf_ptr );
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char *,yyconst char *,int );
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * );
+#endif
+
+#ifndef YY_NO_INPUT
+
+#ifdef __cplusplus
+static int yyinput (void );
+#else
+static int input (void );
+#endif
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO fwrite( yytext, yyleng, 1, yyout )
+#endif
+
+/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
+ { \
+ int c = '*'; \
+ int n; \
+ for ( n = 0; n < max_size && \
+ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
+ buf[n] = (char) c; \
+ if ( c == '\n' ) \
+ buf[n++] = (char) c; \
+ if ( c == EOF && ferror( yyin ) ) \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ result = n; \
+ } \
+ else \
+ { \
+ errno=0; \
+ while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \
+ { \
+ if( errno != EINTR) \
+ { \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ break; \
+ } \
+ errno=0; \
+ clearerr(yyin); \
+ } \
+ }\
+\
+
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg )
+#endif
+
+/* end tables serialization structures and prototypes */
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int yylex (void);
+
+#define YY_DECL int yylex (void)
+#endif /* !YY_DECL */
+
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+ YY_USER_ACTION
+
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp, *yy_bp;
+ register int yy_act;
+
+#line 64 "sel-lex.l"
+
+
+#line 697 "heimdal/lib/hx509/sel-lex.c"
+
+ if ( !(yy_init) )
+ {
+ (yy_init) = 1;
+
+#ifdef YY_USER_INIT
+ YY_USER_INIT;
+#endif
+
+ if ( ! (yy_start) )
+ (yy_start) = 1; /* first start state */
+
+ if ( ! yyin )
+ yyin = stdin;
+
+ if ( ! yyout )
+ yyout = stdout;
+
+ if ( ! YY_CURRENT_BUFFER ) {
+ yyensure_buffer_stack ();
+ YY_CURRENT_BUFFER_LVALUE =
+ yy_create_buffer(yyin,YY_BUF_SIZE );
+ }
+
+ yy_load_buffer_state( );
+ }
+
+ while ( 1 ) /* loops until end-of-file is reached */
+ {
+ yy_cp = (yy_c_buf_p);
+
+ /* Support of yytext. */
+ *yy_cp = (yy_hold_char);
+
+ /* yy_bp points to the position in yy_ch_buf of the start of
+ * the current run.
+ */
+ yy_bp = yy_cp;
+
+ yy_current_state = (yy_start);
+yy_match:
+ do
+ {
+ register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
+ if ( yy_accept[yy_current_state] )
+ {
+ (yy_last_accepting_state) = yy_current_state;
+ (yy_last_accepting_cpos) = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 36 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ ++yy_cp;
+ }
+ while ( yy_base[yy_current_state] != 44 );
+
+yy_find_action:
+ yy_act = yy_accept[yy_current_state];
+ if ( yy_act == 0 )
+ { /* have to back up */
+ yy_cp = (yy_last_accepting_cpos);
+ yy_current_state = (yy_last_accepting_state);
+ yy_act = yy_accept[yy_current_state];
+ }
+
+ YY_DO_BEFORE_ACTION;
+
+do_action: /* This label is used only to access EOF actions. */
+
+ switch ( yy_act )
+ { /* beginning of action switch */
+ case 0: /* must back up */
+ /* undo the effects of YY_DO_BEFORE_ACTION */
+ *yy_cp = (yy_hold_char);
+ yy_cp = (yy_last_accepting_cpos);
+ yy_current_state = (yy_last_accepting_state);
+ goto yy_find_action;
+
+case 1:
+YY_RULE_SETUP
+#line 66 "sel-lex.l"
+{ return kw_TRUE; }
+ YY_BREAK
+case 2:
+YY_RULE_SETUP
+#line 67 "sel-lex.l"
+{ return kw_FALSE; }
+ YY_BREAK
+case 3:
+YY_RULE_SETUP
+#line 68 "sel-lex.l"
+{ return kw_AND; }
+ YY_BREAK
+case 4:
+YY_RULE_SETUP
+#line 69 "sel-lex.l"
+{ return kw_OR; }
+ YY_BREAK
+case 5:
+YY_RULE_SETUP
+#line 70 "sel-lex.l"
+{ return kw_IN; }
+ YY_BREAK
+case 6:
+YY_RULE_SETUP
+#line 71 "sel-lex.l"
+{ return kw_TAILMATCH; }
+ YY_BREAK
+case 7:
+YY_RULE_SETUP
+#line 73 "sel-lex.l"
+{
+ yylval.string = strdup ((const char *)yytext);
+ return IDENTIFIER;
+ }
+ YY_BREAK
+case 8:
+YY_RULE_SETUP
+#line 77 "sel-lex.l"
+{ yylval.string = handle_string(); return STRING; }
+ YY_BREAK
+case 9:
+/* rule 9 can match eol */
+YY_RULE_SETUP
+#line 78 "sel-lex.l"
+{ ++lineno; }
+ YY_BREAK
+case 10:
+YY_RULE_SETUP
+#line 79 "sel-lex.l"
+{ return *yytext; }
+ YY_BREAK
+case 11:
+YY_RULE_SETUP
+#line 80 "sel-lex.l"
+;
+ YY_BREAK
+case 12:
+YY_RULE_SETUP
+#line 81 "sel-lex.l"
+ECHO;
+ YY_BREAK
+#line 844 "heimdal/lib/hx509/sel-lex.c"
+case YY_STATE_EOF(INITIAL):
+ yyterminate();
+
+ case YY_END_OF_BUFFER:
+ {
+ /* Amount of text matched not including the EOB char. */
+ int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1;
+
+ /* Undo the effects of YY_DO_BEFORE_ACTION. */
+ *yy_cp = (yy_hold_char);
+ YY_RESTORE_YY_MORE_OFFSET
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+ {
+ /* We're scanning a new file or input source. It's
+ * possible that this happened because the user
+ * just pointed yyin at a new source and called
+ * yylex(). If so, then we have to assure
+ * consistency between YY_CURRENT_BUFFER and our
+ * globals. Here is the right place to do so, because
+ * this is the first action (other than possibly a
+ * back-up) that will match for the new input source.
+ */
+ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+ }
+
+ /* Note that here we test for yy_c_buf_p "<=" to the position
+ * of the first EOB in the buffer, since yy_c_buf_p will
+ * already have been incremented past the NUL character
+ * (since all states make transitions on EOB to the
+ * end-of-buffer state). Contrast this with the test
+ * in input().
+ */
+ if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+ { /* This was really a NUL. */
+ yy_state_type yy_next_state;
+
+ (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( );
+
+ /* Okay, we're now positioned to make the NUL
+ * transition. We couldn't have
+ * yy_get_previous_state() go ahead and do it
+ * for us because it doesn't know how to deal
+ * with the possibility of jamming (and we don't
+ * want to build jamming into it because then it
+ * will run more slowly).
+ */
+
+ yy_next_state = yy_try_NUL_trans( yy_current_state );
+
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+
+ if ( yy_next_state )
+ {
+ /* Consume the NUL. */
+ yy_cp = ++(yy_c_buf_p);
+ yy_current_state = yy_next_state;
+ goto yy_match;
+ }
+
+ else
+ {
+ yy_cp = (yy_c_buf_p);
+ goto yy_find_action;
+ }
+ }
+
+ else switch ( yy_get_next_buffer( ) )
+ {
+ case EOB_ACT_END_OF_FILE:
+ {
+ (yy_did_buffer_switch_on_eof) = 0;
+
+ if ( yywrap( ) )
+ {
+ /* Note: because we've taken care in
+ * yy_get_next_buffer() to have set up
+ * yytext, we can now set up
+ * yy_c_buf_p so that if some total
+ * hoser (like flex itself) wants to
+ * call the scanner after we return the
+ * YY_NULL, it'll still work - another
+ * YY_NULL will get returned.
+ */
+ (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ;
+
+ yy_act = YY_STATE_EOF(YY_START);
+ goto do_action;
+ }
+
+ else
+ {
+ if ( ! (yy_did_buffer_switch_on_eof) )
+ YY_NEW_FILE;
+ }
+ break;
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ (yy_c_buf_p) =
+ (yytext_ptr) + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( );
+
+ yy_cp = (yy_c_buf_p);
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+ goto yy_match;
+
+ case EOB_ACT_LAST_MATCH:
+ (yy_c_buf_p) =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)];
+
+ yy_current_state = yy_get_previous_state( );
+
+ yy_cp = (yy_c_buf_p);
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+ goto yy_find_action;
+ }
+ break;
+ }
+
+ default:
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--no action found" );
+ } /* end of action switch */
+ } /* end of scanning one token */
+} /* end of yylex */
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ * EOB_ACT_LAST_MATCH -
+ * EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ * EOB_ACT_END_OF_FILE - end of file
+ */
+static int yy_get_next_buffer (void)
+{
+ register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+ register char *source = (yytext_ptr);
+ register int number_to_move, i;
+ int ret_val;
+
+ if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] )
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--end of buffer missed" );
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+ { /* Don't try to fill the buffer, so this is an EOF. */
+ if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 )
+ {
+ /* We matched a single character, the EOB, so
+ * treat this as a final EOF.
+ */
+ return EOB_ACT_END_OF_FILE;
+ }
+
+ else
+ {
+ /* We matched some text prior to the EOB, first
+ * process it.
+ */
+ return EOB_ACT_LAST_MATCH;
+ }
+ }
+
+ /* Try to read more data. */
+
+ /* First move last chars to start of buffer. */
+ number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1;
+
+ for ( i = 0; i < number_to_move; ++i )
+ *(dest++) = *(source++);
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+ /* don't do the read, it's not guaranteed to return an EOF,
+ * just force an EOF
+ */
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0;
+
+ else
+ {
+ int num_to_read =
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+ while ( num_to_read <= 0 )
+ { /* Not enough room in the buffer - grow it. */
+
+ /* just a shorter name for the current buffer */
+ YY_BUFFER_STATE b = YY_CURRENT_BUFFER;
+
+ int yy_c_buf_p_offset =
+ (int) ((yy_c_buf_p) - b->yy_ch_buf);
+
+ if ( b->yy_is_our_buffer )
+ {
+ int new_size = b->yy_buf_size * 2;
+
+ if ( new_size <= 0 )
+ b->yy_buf_size += b->yy_buf_size / 8;
+ else
+ b->yy_buf_size *= 2;
+
+ b->yy_ch_buf = (char *)
+ /* Include room in for 2 EOB chars. */
+ yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 );
+ }
+ else
+ /* Can't grow it, we don't own it. */
+ b->yy_ch_buf = 0;
+
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR(
+ "fatal error - scanner input buffer overflow" );
+
+ (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+ num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
+ number_to_move - 1;
+
+ }
+
+ if ( num_to_read > YY_READ_BUF_SIZE )
+ num_to_read = YY_READ_BUF_SIZE;
+
+ /* Read in more data. */
+ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+ (yy_n_chars), (size_t) num_to_read );
+
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ if ( (yy_n_chars) == 0 )
+ {
+ if ( number_to_move == YY_MORE_ADJ )
+ {
+ ret_val = EOB_ACT_END_OF_FILE;
+ yyrestart(yyin );
+ }
+
+ else
+ {
+ ret_val = EOB_ACT_LAST_MATCH;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+ YY_BUFFER_EOF_PENDING;
+ }
+ }
+
+ else
+ ret_val = EOB_ACT_CONTINUE_SCAN;
+
+ if ((yy_size_t) ((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
+ /* Extend the array by 50%, plus the number we really need. */
+ yy_size_t new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1);
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size );
+ if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
+ }
+
+ (yy_n_chars) += number_to_move;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR;
+
+ (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+ return ret_val;
+}
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+ static yy_state_type yy_get_previous_state (void)
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp;
+
+ yy_current_state = (yy_start);
+
+ for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp )
+ {
+ register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+ if ( yy_accept[yy_current_state] )
+ {
+ (yy_last_accepting_state) = yy_current_state;
+ (yy_last_accepting_cpos) = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 36 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ }
+
+ return yy_current_state;
+}
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ * next_state = yy_try_NUL_trans( current_state );
+ */
+ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state )
+{
+ register int yy_is_jam;
+ register char *yy_cp = (yy_c_buf_p);
+
+ register YY_CHAR yy_c = 1;
+ if ( yy_accept[yy_current_state] )
+ {
+ (yy_last_accepting_state) = yy_current_state;
+ (yy_last_accepting_cpos) = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 36 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ yy_is_jam = (yy_current_state == 35);
+
+ return yy_is_jam ? 0 : yy_current_state;
+}
+
+ static void yyunput (int c, register char * yy_bp )
+{
+ register char *yy_cp;
+
+ yy_cp = (yy_c_buf_p);
+
+ /* undo effects of setting up yytext */
+ *yy_cp = (yy_hold_char);
+
+ if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+ { /* need to shift things up to make room */
+ /* +2 for EOB chars. */
+ register int number_to_move = (yy_n_chars) + 2;
+ register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2];
+ register char *source =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move];
+
+ while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+ *--dest = *--source;
+
+ yy_cp += (int) (dest - source);
+ yy_bp += (int) (dest - source);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars =
+ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_buf_size;
+
+ if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+ YY_FATAL_ERROR( "flex scanner push-back overflow" );
+ }
+
+ *--yy_cp = (char) c;
+
+ (yytext_ptr) = yy_bp;
+ (yy_hold_char) = *yy_cp;
+ (yy_c_buf_p) = yy_cp;
+}
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+ static int yyinput (void)
+#else
+ static int input (void)
+#endif
+
+{
+ int c;
+
+ *(yy_c_buf_p) = (yy_hold_char);
+
+ if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR )
+ {
+ /* yy_c_buf_p now points to the character we want to return.
+ * If this occurs *before* the EOB characters, then it's a
+ * valid NUL; if not, then we've hit the end of the buffer.
+ */
+ if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+ /* This was really a NUL. */
+ *(yy_c_buf_p) = '\0';
+
+ else
+ { /* need more input */
+ int offset = (yy_c_buf_p) - (yytext_ptr);
+ ++(yy_c_buf_p);
+
+ switch ( yy_get_next_buffer( ) )
+ {
+ case EOB_ACT_LAST_MATCH:
+ /* This happens because yy_g_n_b()
+ * sees that we've accumulated a
+ * token and flags that we need to
+ * try matching the token before
+ * proceeding. But for input(),
+ * there's no matching to consider.
+ * So convert the EOB_ACT_LAST_MATCH
+ * to EOB_ACT_END_OF_FILE.
+ */
+
+ /* Reset buffer status. */
+ yyrestart(yyin );
+
+ /*FALLTHROUGH*/
+
+ case EOB_ACT_END_OF_FILE:
+ {
+ if ( yywrap( ) )
+ return EOF;
+
+ if ( ! (yy_did_buffer_switch_on_eof) )
+ YY_NEW_FILE;
+#ifdef __cplusplus
+ return yyinput();
+#else
+ return input();
+#endif
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ (yy_c_buf_p) = (yytext_ptr) + offset;
+ break;
+ }
+ }
+ }
+
+ c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */
+ *(yy_c_buf_p) = '\0'; /* preserve yytext */
+ (yy_hold_char) = *++(yy_c_buf_p);
+
+ return c;
+}
+#endif /* ifndef YY_NO_INPUT */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ *
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+ void yyrestart (FILE * input_file )
+{
+
+ if ( ! YY_CURRENT_BUFFER ){
+ yyensure_buffer_stack ();
+ YY_CURRENT_BUFFER_LVALUE =
+ yy_create_buffer(yyin,YY_BUF_SIZE );
+ }
+
+ yy_init_buffer(YY_CURRENT_BUFFER,input_file );
+ yy_load_buffer_state( );
+}
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ *
+ */
+ void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer )
+{
+
+ /* TODO. We should be able to replace this entire function body
+ * with
+ * yypop_buffer_state();
+ * yypush_buffer_state(new_buffer);
+ */
+ yyensure_buffer_stack ();
+ if ( YY_CURRENT_BUFFER == new_buffer )
+ return;
+
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *(yy_c_buf_p) = (yy_hold_char);
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+ yy_load_buffer_state( );
+
+ /* We don't actually know whether we did this switch during
+ * EOF (yywrap()) processing, but the only time this flag
+ * is looked at is after yywrap() is called, so it's safe
+ * to go ahead and always set it.
+ */
+ (yy_did_buffer_switch_on_eof) = 1;
+}
+
+static void yy_load_buffer_state (void)
+{
+ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+ yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+ (yy_hold_char) = *(yy_c_buf_p);
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ *
+ * @return the allocated buffer state.
+ */
+ YY_BUFFER_STATE yy_create_buffer (FILE * file, int size )
+{
+ YY_BUFFER_STATE b;
+
+ b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+ b->yy_buf_size = size;
+
+ /* yy_ch_buf has to be 2 characters longer than the size given because
+ * we need to put in 2 end-of-buffer characters.
+ */
+ b->yy_ch_buf = (char *) yyalloc(b->yy_buf_size + 2 );
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+ b->yy_is_our_buffer = 1;
+
+ yy_init_buffer(b,file );
+
+ return b;
+}
+
+/** Destroy the buffer.
+ * @param b a buffer created with yy_create_buffer()
+ *
+ */
+ void yy_delete_buffer (YY_BUFFER_STATE b )
+{
+
+ if ( ! b )
+ return;
+
+ if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+ if ( b->yy_is_our_buffer )
+ yyfree((void *) b->yy_ch_buf );
+
+ yyfree((void *) b );
+}
+
+#ifndef __cplusplus
+extern int isatty (int );
+#endif /* __cplusplus */
+
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a yyrestart() or at EOF.
+ */
+ static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file )
+
+{
+ int oerrno = errno;
+
+ yy_flush_buffer(b );
+
+ b->yy_input_file = file;
+ b->yy_fill_buffer = 1;
+
+ /* If b is the current buffer, then yy_init_buffer was _probably_
+ * called from yyrestart() or through yy_get_next_buffer.
+ * In that case, we don't want to reset the lineno or column.
+ */
+ if (b != YY_CURRENT_BUFFER){
+ b->yy_bs_lineno = 1;
+ b->yy_bs_column = 0;
+ }
+
+ b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+
+ errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ *
+ */
+ void yy_flush_buffer (YY_BUFFER_STATE b )
+{
+ if ( ! b )
+ return;
+
+ b->yy_n_chars = 0;
+
+ /* We always need two end-of-buffer characters. The first causes
+ * a transition to the end-of-buffer state. The second causes
+ * a jam in that state.
+ */
+ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+ b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+ b->yy_buf_pos = &b->yy_ch_buf[0];
+
+ b->yy_at_bol = 1;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ if ( b == YY_CURRENT_BUFFER )
+ yy_load_buffer_state( );
+}
+
+/** Pushes the new state onto the stack. The new state becomes
+ * the current state. This function will allocate the stack
+ * if necessary.
+ * @param new_buffer The new state.
+ *
+ */
+void yypush_buffer_state (YY_BUFFER_STATE new_buffer )
+{
+ if (new_buffer == NULL)
+ return;
+
+ yyensure_buffer_stack();
+
+ /* This block is copied from yy_switch_to_buffer. */
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *(yy_c_buf_p) = (yy_hold_char);
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ /* Only push if top exists. Otherwise, replace top. */
+ if (YY_CURRENT_BUFFER)
+ (yy_buffer_stack_top)++;
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+ /* copied from yy_switch_to_buffer. */
+ yy_load_buffer_state( );
+ (yy_did_buffer_switch_on_eof) = 1;
+}
+
+/** Removes and deletes the top of the stack, if present.
+ * The next element becomes the new top.
+ *
+ */
+void yypop_buffer_state (void)
+{
+ if (!YY_CURRENT_BUFFER)
+ return;
+
+ yy_delete_buffer(YY_CURRENT_BUFFER );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ if ((yy_buffer_stack_top) > 0)
+ --(yy_buffer_stack_top);
+
+ if (YY_CURRENT_BUFFER) {
+ yy_load_buffer_state( );
+ (yy_did_buffer_switch_on_eof) = 1;
+ }
+}
+
+/* Allocates the stack if it does not exist.
+ * Guarantees space for at least one push.
+ */
+static void yyensure_buffer_stack (void)
+{
+ int num_to_alloc;
+
+ if (!(yy_buffer_stack)) {
+
+ /* First allocation is just for 2 elements, since we don't know if this
+ * scanner will even need a stack. We use 2 instead of 1 to avoid an
+ * immediate realloc on the next call.
+ */
+ num_to_alloc = 1;
+ (yy_buffer_stack) = (struct yy_buffer_state**)yyalloc
+ (num_to_alloc * sizeof(struct yy_buffer_state*)
+ );
+ if ( ! (yy_buffer_stack) )
+ YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" );
+
+ memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+
+ (yy_buffer_stack_max) = num_to_alloc;
+ (yy_buffer_stack_top) = 0;
+ return;
+ }
+
+ if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){
+
+ /* Increase the buffer to prepare for a possible push. */
+ int grow_size = 8 /* arbitrary grow size */;
+
+ num_to_alloc = (yy_buffer_stack_max) + grow_size;
+ (yy_buffer_stack) = (struct yy_buffer_state**)yyrealloc
+ ((yy_buffer_stack),
+ num_to_alloc * sizeof(struct yy_buffer_state*)
+ );
+ if ( ! (yy_buffer_stack) )
+ YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" );
+
+ /* zero only the new slots.*/
+ memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*));
+ (yy_buffer_stack_max) = num_to_alloc;
+ }
+}
+
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ *
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size )
+{
+ YY_BUFFER_STATE b;
+
+ if ( size < 2 ||
+ base[size-2] != YY_END_OF_BUFFER_CHAR ||
+ base[size-1] != YY_END_OF_BUFFER_CHAR )
+ /* They forgot to leave room for the EOB's. */
+ return 0;
+
+ b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" );
+
+ b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */
+ b->yy_buf_pos = b->yy_ch_buf = base;
+ b->yy_is_our_buffer = 0;
+ b->yy_input_file = 0;
+ b->yy_n_chars = b->yy_buf_size;
+ b->yy_is_interactive = 0;
+ b->yy_at_bol = 1;
+ b->yy_fill_buffer = 0;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ yy_switch_to_buffer(b );
+
+ return b;
+}
+
+/** Setup the input buffer state to scan a string. The next call to yylex() will
+ * scan from a @e copy of @a str.
+ * @param yystr a NUL-terminated string to scan
+ *
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ * yy_scan_bytes() instead.
+ */
+YY_BUFFER_STATE yy_scan_string (yyconst char * yystr )
+{
+
+ return yy_scan_bytes(yystr,strlen(yystr) );
+}
+
+/** Setup the input buffer state to scan the given bytes. The next call to yylex() will
+ * scan from a @e copy of @a bytes.
+ * @param bytes the byte buffer to scan
+ * @param len the number of bytes in the buffer pointed to by @a bytes.
+ *
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE yy_scan_bytes (yyconst char * yybytes, int _yybytes_len )
+{
+ YY_BUFFER_STATE b;
+ char *buf;
+ yy_size_t n;
+ int i;
+
+ /* Get memory for full buffer, including space for trailing EOB's. */
+ n = _yybytes_len + 2;
+ buf = (char *) yyalloc(n );
+ if ( ! buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" );
+
+ for ( i = 0; i < _yybytes_len; ++i )
+ buf[i] = yybytes[i];
+
+ buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
+
+ b = yy_scan_buffer(buf,n );
+ if ( ! b )
+ YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" );
+
+ /* It's okay to grow etc. this buffer, and we should throw it
+ * away when we're done.
+ */
+ b->yy_is_our_buffer = 1;
+
+ return b;
+}
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+static void yy_fatal_error (yyconst char* msg )
+{
+ (void) fprintf( stderr, "%s\n", msg );
+ exit( YY_EXIT_FAILURE );
+}
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ yytext[yyleng] = (yy_hold_char); \
+ (yy_c_buf_p) = yytext + yyless_macro_arg; \
+ (yy_hold_char) = *(yy_c_buf_p); \
+ *(yy_c_buf_p) = '\0'; \
+ yyleng = yyless_macro_arg; \
+ } \
+ while ( 0 )
+
+/* Accessor methods (get/set functions) to struct members. */
+
+/** Get the current line number.
+ *
+ */
+int yyget_lineno (void)
+{
+
+ return yylineno;
+}
+
+/** Get the input stream.
+ *
+ */
+FILE *yyget_in (void)
+{
+ return yyin;
+}
+
+/** Get the output stream.
+ *
+ */
+FILE *yyget_out (void)
+{
+ return yyout;
+}
+
+/** Get the length of the current token.
+ *
+ */
+int yyget_leng (void)
+{
+ return yyleng;
+}
+
+/** Get the current token.
+ *
+ */
+
+char *yyget_text (void)
+{
+ return yytext;
+}
+
+/** Set the current line number.
+ * @param line_number
+ *
+ */
+void yyset_lineno (int line_number )
+{
+
+ yylineno = line_number;
+}
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param in_str A readable stream.
+ *
+ * @see yy_switch_to_buffer
+ */
+void yyset_in (FILE * in_str )
+{
+ yyin = in_str ;
+}
+
+void yyset_out (FILE * out_str )
+{
+ yyout = out_str ;
+}
+
+int yyget_debug (void)
+{
+ return yy_flex_debug;
+}
+
+void yyset_debug (int bdebug )
+{
+ yy_flex_debug = bdebug ;
+}
+
+static int yy_init_globals (void)
+{
+ /* Initialization is the same as for the non-reentrant scanner.
+ * This function is called from yylex_destroy(), so don't allocate here.
+ */
+
+ (yy_buffer_stack) = 0;
+ (yy_buffer_stack_top) = 0;
+ (yy_buffer_stack_max) = 0;
+ (yy_c_buf_p) = (char *) 0;
+ (yy_init) = 0;
+ (yy_start) = 0;
+
+/* Defined in main.c */
+#ifdef YY_STDINIT
+ yyin = stdin;
+ yyout = stdout;
+#else
+ yyin = (FILE *) 0;
+ yyout = (FILE *) 0;
+#endif
+
+ /* For future reference: Set errno on error, since we are called by
+ * yylex_init()
+ */
+ return 0;
+}
+
+/* yylex_destroy is for both reentrant and non-reentrant scanners. */
+int yylex_destroy (void)
+{
+
+ /* Pop the buffer stack, destroying each element. */
+ while(YY_CURRENT_BUFFER){
+ yy_delete_buffer(YY_CURRENT_BUFFER );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ yypop_buffer_state();
+ }
+
+ /* Destroy the stack itself. */
+ yyfree((yy_buffer_stack) );
+ (yy_buffer_stack) = NULL;
+
+ /* Reset the globals. This is important in a non-reentrant scanner so the next time
+ * yylex() is called, initialization will occur. */
+ yy_init_globals( );
+
+ return 0;
+}
+
+/*
+ * Internal utility routines.
+ */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, yyconst char * s2, int n )
+{
+ register int i;
+ for ( i = 0; i < n; ++i )
+ s1[i] = s2[i];
+}
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * s )
+{
+ register int n;
+ for ( n = 0; s[n]; ++n )
+ ;
+
+ return n;
+}
+#endif
+
+void *yyalloc (yy_size_t size )
+{
+ return (void *) malloc( size );
+}
+
+void *yyrealloc (void * ptr, yy_size_t size )
+{
+ /* The cast to (char *) in the following accommodates both
+ * implementations that use char* generic pointers, and those
+ * that use void* generic pointers. It works with the latter
+ * because both ANSI C and C++ allow castless assignment from
+ * any pointer type to void*, and deal with argument conversions
+ * as though doing an assignment.
+ */
+ return (void *) realloc( (char *) ptr, size );
+}
+
+void yyfree (void * ptr )
+{
+ free( (char *) ptr ); /* see yyrealloc() for (char *) cast */
+}
+
+#define YYTABLES_NAME "yytables"
+
+#line 81 "sel-lex.l"
+
+
+
+static char *
+handle_string(void)
+{
+ char x[1024];
+ int i = 0;
+ int c;
+ int quote = 0;
+ while((c = input()) != EOF){
+ if(quote) {
+ x[i++] = '\\';
+ x[i++] = c;
+ quote = 0;
+ continue;
+ }
+ if(c == '\n'){
+ _hx509_sel_yyerror("unterminated string");
+ lineno++;
+ break;
+ }
+ if(c == '\\'){
+ quote++;
+ continue;
+ }
+ if(c == '\"')
+ break;
+ x[i++] = c;
+ }
+ x[i] = '\0';
+ return strdup(x);
+}
+
+int
+yywrap ()
+{
+ return 1;
+}
+
+static int
+lex_input(char *buf, int max_size)
+{
+ int n;
+
+ n = _hx509_expr_input.length - _hx509_expr_input.offset;
+ if (max_size < n)
+ n = max_size;
+ if (n <= 0)
+ return YY_NULL;
+
+ memcpy(buf, _hx509_expr_input.buf + _hx509_expr_input.offset, n);
+ _hx509_expr_input.offset += n;
+
+ return n;
+}
+
diff --git a/source4/heimdal/lib/hx509/sel-lex.l b/source4/heimdal/lib/hx509/sel-lex.l
new file mode 100644
index 0000000000..e9bbbc6087
--- /dev/null
+++ b/source4/heimdal/lib/hx509/sel-lex.l
@@ -0,0 +1,135 @@
+%{
+/*
+ * Copyright (c) 2004, 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#undef ECHO
+
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include "sel.h"
+#include "sel-gram.h"
+unsigned lineno = 1;
+
+static char * handle_string(void);
+static int lex_input(char *, int);
+
+struct hx_expr_input _hx509_expr_input;
+
+#define YY_NO_UNPUT 1
+
+#undef YY_INPUT
+#define YY_INPUT(buf,res,maxsize) (res = lex_input(buf, maxsize))
+
+#undef ECHO
+
+%}
+%%
+
+TRUE { return kw_TRUE; }
+FALSE { return kw_FALSE; }
+AND { return kw_AND; }
+OR { return kw_OR; }
+IN { return kw_IN; }
+TAILMATCH { return kw_TAILMATCH; }
+
+[A-Za-z][-A-Za-z0-9_]* {
+ yylval.string = strdup ((const char *)yytext);
+ return IDENTIFIER;
+ }
+"\"" { yylval.string = handle_string(); return STRING; }
+\n { ++lineno; }
+[,.!={}()%] { return *yytext; }
+[ \t] ;
+%%
+
+static char *
+handle_string(void)
+{
+ char x[1024];
+ int i = 0;
+ int c;
+ int quote = 0;
+ while((c = input()) != EOF){
+ if(quote) {
+ x[i++] = '\\';
+ x[i++] = c;
+ quote = 0;
+ continue;
+ }
+ if(c == '\n'){
+ _hx509_sel_yyerror("unterminated string");
+ lineno++;
+ break;
+ }
+ if(c == '\\'){
+ quote++;
+ continue;
+ }
+ if(c == '\"')
+ break;
+ x[i++] = c;
+ }
+ x[i] = '\0';
+ return strdup(x);
+}
+
+int
+yywrap ()
+{
+ return 1;
+}
+
+static int
+lex_input(char *buf, int max_size)
+{
+ int n;
+
+ n = _hx509_expr_input.length - _hx509_expr_input.offset;
+ if (max_size < n)
+ n = max_size;
+ if (n <= 0)
+ return YY_NULL;
+
+ memcpy(buf, _hx509_expr_input.buf + _hx509_expr_input.offset, n);
+ _hx509_expr_input.offset += n;
+
+ return n;
+}
diff --git a/source4/heimdal/lib/hx509/sel.c b/source4/heimdal/lib/hx509/sel.c
new file mode 100644
index 0000000000..5932ce84c3
--- /dev/null
+++ b/source4/heimdal/lib/hx509/sel.c
@@ -0,0 +1,232 @@
+/*
+ * Copyright (c) 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hx_locl.h"
+
+struct hx_expr *
+_hx509_make_expr(enum hx_expr_op op, void *arg1, void *arg2)
+{
+ struct hx_expr *expr;
+
+ expr = malloc(sizeof(*expr));
+ if (expr == NULL)
+ return NULL;
+ expr->op = op;
+ expr->arg1 = arg1;
+ expr->arg2 = arg2;
+
+ return expr;
+}
+
+static const char *
+eval_word(hx509_context context, hx509_env env, struct hx_expr *word)
+{
+ switch (word->op) {
+ case expr_STRING:
+ return word->arg1;
+ case expr_VAR:
+ if (word->arg2 == NULL)
+ return hx509_env_find(context, env, word->arg1);
+
+ env = hx509_env_find_binding(context, env, word->arg1);
+ if (env == NULL)
+ return NULL;
+
+ return eval_word(context, env, word->arg2);
+ default:
+ return NULL;
+ }
+}
+
+static hx509_env
+find_variable(hx509_context context, hx509_env env, struct hx_expr *word)
+{
+ assert(word->op == expr_VAR);
+
+ if (word->arg2 == NULL)
+ return hx509_env_find_binding(context, env, word->arg1);
+
+ env = hx509_env_find_binding(context, env, word->arg1);
+ if (env == NULL)
+ return NULL;
+ return find_variable(context, env, word->arg2);
+}
+
+static int
+eval_comp(hx509_context context, hx509_env env, struct hx_expr *expr)
+{
+ switch (expr->op) {
+ case comp_NE:
+ case comp_EQ:
+ case comp_TAILEQ: {
+ const char *s1, *s2;
+ int ret;
+
+ s1 = eval_word(context, env, expr->arg1);
+ s2 = eval_word(context, env, expr->arg2);
+
+ if (s1 == NULL || s2 == NULL)
+ return FALSE;
+
+ if (expr->op == comp_TAILEQ) {
+ size_t len1 = strlen(s1);
+ size_t len2 = strlen(s2);
+
+ if (len1 < len2)
+ return 0;
+ ret = strcmp(s1 + (len1 - len2), s2) == 0;
+ } else {
+ ret = strcmp(s1, s2) == 0;
+ if (expr->op == comp_NE)
+ ret = !ret;
+ }
+ return ret;
+ }
+ case comp_IN: {
+ struct hx_expr *subexpr;
+ const char *w, *s1;
+
+ w = eval_word(context, env, expr->arg1);
+
+ subexpr = expr->arg2;
+
+ if (subexpr->op == expr_WORDS) {
+ while (subexpr) {
+ s1 = eval_word(context, env, subexpr->arg1);
+ if (strcmp(w, s1) == 0)
+ return TRUE;
+ subexpr = subexpr->arg2;
+ }
+ } else if (subexpr->op == expr_VAR) {
+ hx509_env subenv;
+
+ subenv = find_variable(context, env, subexpr);
+ if (subenv == NULL)
+ return FALSE;
+
+ while (subenv) {
+ if (subenv->type != env_string)
+ continue;
+ if (strcmp(w, subenv->name) == 0)
+ return TRUE;
+ if (strcmp(w, subenv->u.string) == 0)
+ return TRUE;
+ subenv = subenv->next;
+ }
+
+ } else
+ _hx509_abort("hx509 eval IN unknown op: %d", (int)subexpr->op);
+
+ return FALSE;
+ }
+ default:
+ _hx509_abort("hx509 eval expr with unknown op: %d", (int)expr->op);
+ }
+ return FALSE;
+}
+
+int
+_hx509_expr_eval(hx509_context context, hx509_env env, struct hx_expr *expr)
+{
+ switch (expr->op) {
+ case op_TRUE:
+ return 1;
+ case op_FALSE:
+ return 0;
+ case op_NOT:
+ return ! _hx509_expr_eval(context, env, expr->arg1);
+ case op_AND:
+ return _hx509_expr_eval(context, env, expr->arg1) &&
+ _hx509_expr_eval(context, env, expr->arg2);
+ case op_OR:
+ return _hx509_expr_eval(context, env, expr->arg1) ||
+ _hx509_expr_eval(context, env, expr->arg2);
+ case op_COMP:
+ return eval_comp(context, env, expr->arg1);
+ default:
+ _hx509_abort("hx509 eval expr with unknown op: %d", (int)expr->op);
+ }
+}
+
+void
+_hx509_expr_free(struct hx_expr *expr)
+{
+ switch (expr->op) {
+ case expr_STRING:
+ case expr_NUMBER:
+ free(expr->arg1);
+ break;
+ case expr_WORDS:
+ case expr_FUNCTION:
+ case expr_VAR:
+ free(expr->arg1);
+ if (expr->arg2)
+ _hx509_expr_free(expr->arg2);
+ break;
+ default:
+ if (expr->arg1)
+ _hx509_expr_free(expr->arg1);
+ if (expr->arg2)
+ _hx509_expr_free(expr->arg2);
+ break;
+ }
+ free(expr);
+}
+
+struct hx_expr *
+_hx509_expr_parse(const char *buf)
+{
+ _hx509_expr_input.buf = buf;
+ _hx509_expr_input.length = strlen(buf);
+ _hx509_expr_input.offset = 0;
+ _hx509_expr_input.expr = NULL;
+
+ if (_hx509_expr_input.error) {
+ free(_hx509_expr_input.error);
+ _hx509_expr_input.error = NULL;
+ }
+
+ yyparse();
+
+ return _hx509_expr_input.expr;
+}
+
+void
+_hx509_sel_yyerror (char *s)
+{
+ if (_hx509_expr_input.error)
+ free(_hx509_expr_input.error);
+
+ _hx509_expr_input.error = strdup(s);
+}
+
diff --git a/source4/heimdal/lib/hx509/sel.h b/source4/heimdal/lib/hx509/sel.h
new file mode 100644
index 0000000000..1dfc41818c
--- /dev/null
+++ b/source4/heimdal/lib/hx509/sel.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+enum hx_expr_op {
+ op_TRUE,
+ op_FALSE,
+ op_NOT,
+ op_AND,
+ op_OR,
+ op_COMP,
+
+ comp_EQ,
+ comp_NE,
+ comp_IN,
+ comp_TAILEQ,
+
+ expr_NUMBER,
+ expr_STRING,
+ expr_FUNCTION,
+ expr_VAR,
+ expr_WORDS
+};
+
+struct hx_expr {
+ enum hx_expr_op op;
+ void *arg1;
+ void *arg2;
+};
+
+struct hx_expr_input {
+ const char *buf;
+ size_t length;
+ size_t offset;
+ struct hx_expr *expr;
+ char *error;
+};
+
+extern struct hx_expr_input _hx509_expr_input;
+
+#define yyparse _hx509_sel_yyparse
+#define yylex _hx509_sel_yylex
+#define yyerror _hx509_sel_yyerror
+#define yylval _hx509_sel_yylval
+#define yychar _hx509_sel_yychar
+#define yydebug _hx509_sel_yydebug
+#define yynerrs _hx509_sel_yynerrs
+#define yywrap _hx509_sel_yywrap
+
+int _hx509_sel_yyparse(void);
+int _hx509_sel_yylex(void);
+void _hx509_sel_yyerror(char *);
+
diff --git a/source4/heimdal/lib/hx509/test_name.c b/source4/heimdal/lib/hx509/test_name.c
new file mode 100644
index 0000000000..da83e52786
--- /dev/null
+++ b/source4/heimdal/lib/hx509/test_name.c
@@ -0,0 +1,383 @@
+/*
+ * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hx_locl.h"
+RCSID("$Id$");
+
+static int
+test_name(hx509_context context, const char *name)
+{
+ hx509_name n;
+ char *s;
+ int ret;
+
+ ret = hx509_parse_name(context, name, &n);
+ if (ret)
+ return 1;
+
+ ret = hx509_name_to_string(n, &s);
+ if (ret)
+ return 1;
+
+ if (strcmp(s, name) != 0)
+ return 1;
+
+ hx509_name_free(&n);
+ free(s);
+
+ return 0;
+}
+
+static int
+test_name_fail(hx509_context context, const char *name)
+{
+ hx509_name n;
+
+ if (hx509_parse_name(context, name, &n) == HX509_NAME_MALFORMED)
+ return 0;
+ hx509_name_free(&n);
+ return 1;
+}
+
+static int
+test_expand(hx509_context context, const char *name, const char *expected)
+{
+ hx509_env env = NULL;
+ hx509_name n;
+ char *s;
+ int ret;
+
+ hx509_env_add(context, &env, "uid", "lha");
+
+ ret = hx509_parse_name(context, name, &n);
+ if (ret)
+ return 1;
+
+ ret = hx509_name_expand(context, n, env);
+ hx509_env_free(&env);
+ if (ret)
+ return 1;
+
+ ret = hx509_name_to_string(n, &s);
+ hx509_name_free(&n);
+ if (ret)
+ return 1;
+
+ ret = strcmp(s, expected) != 0;
+ free(s);
+ if (ret)
+ return 1;
+
+ return 0;
+}
+
+char certdata1[] =
+ "\x30\x82\x04\x1d\x30\x82\x03\x05\xa0\x03\x02\x01\x02\x02\x10\x4e"
+ "\x81\x2d\x8a\x82\x65\xe0\x0b\x02\xee\x3e\x35\x02\x46\xe5\x3d\x30"
+ "\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x30\x81"
+ "\x81\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13\x02\x47\x42\x31\x1b"
+ "\x30\x19\x06\x03\x55\x04\x08\x13\x12\x47\x72\x65\x61\x74\x65\x72"
+ "\x20\x4d\x61\x6e\x63\x68\x65\x73\x74\x65\x72\x31\x10\x30\x0e\x06"
+ "\x03\x55\x04\x07\x13\x07\x53\x61\x6c\x66\x6f\x72\x64\x31\x1a\x30"
+ "\x18\x06\x03\x55\x04\x0a\x13\x11\x43\x4f\x4d\x4f\x44\x4f\x20\x43"
+ "\x41\x20\x4c\x69\x6d\x69\x74\x65\x64\x31\x27\x30\x25\x06\x03\x55"
+ "\x04\x03\x13\x1e\x43\x4f\x4d\x4f\x44\x4f\x20\x43\x65\x72\x74\x69"
+ "\x66\x69\x63\x61\x74\x69\x6f\x6e\x20\x41\x75\x74\x68\x6f\x72\x69"
+ "\x74\x79\x30\x1e\x17\x0d\x30\x36\x31\x32\x30\x31\x30\x30\x30\x30"
+ "\x30\x30\x5a\x17\x0d\x32\x39\x31\x32\x33\x31\x32\x33\x35\x39\x35"
+ "\x39\x5a\x30\x81\x81\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13\x02"
+ "\x47\x42\x31\x1b\x30\x19\x06\x03\x55\x04\x08\x13\x12\x47\x72\x65"
+ "\x61\x74\x65\x72\x20\x4d\x61\x6e\x63\x68\x65\x73\x74\x65\x72\x31"
+ "\x10\x30\x0e\x06\x03\x55\x04\x07\x13\x07\x53\x61\x6c\x66\x6f\x72"
+ "\x64\x31\x1a\x30\x18\x06\x03\x55\x04\x0a\x13\x11\x43\x4f\x4d\x4f"
+ "\x44\x4f\x20\x43\x41\x20\x4c\x69\x6d\x69\x74\x65\x64\x31\x27\x30"
+ "\x25\x06\x03\x55\x04\x03\x13\x1e\x43\x4f\x4d\x4f\x44\x4f\x20\x43"
+ "\x65\x72\x74\x69\x66\x69\x63\x61\x74\x69\x6f\x6e\x20\x41\x75\x74"
+ "\x68\x6f\x72\x69\x74\x79\x30\x82\x01\x22\x30\x0d\x06\x09\x2a\x86"
+ "\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x82\x01\x0f\x00\x30\x82"
+ "\x01\x0a\x02\x82\x01\x01\x00\xd0\x40\x8b\x8b\x72\xe3\x91\x1b\xf7"
+ "\x51\xc1\x1b\x54\x04\x98\xd3\xa9\xbf\xc1\xe6\x8a\x5d\x3b\x87\xfb"
+ "\xbb\x88\xce\x0d\xe3\x2f\x3f\x06\x96\xf0\xa2\x29\x50\x99\xae\xdb"
+ "\x3b\xa1\x57\xb0\x74\x51\x71\xcd\xed\x42\x91\x4d\x41\xfe\xa9\xc8"
+ "\xd8\x6a\x86\x77\x44\xbb\x59\x66\x97\x50\x5e\xb4\xd4\x2c\x70\x44"
+ "\xcf\xda\x37\x95\x42\x69\x3c\x30\xc4\x71\xb3\x52\xf0\x21\x4d\xa1"
+ "\xd8\xba\x39\x7c\x1c\x9e\xa3\x24\x9d\xf2\x83\x16\x98\xaa\x16\x7c"
+ "\x43\x9b\x15\x5b\xb7\xae\x34\x91\xfe\xd4\x62\x26\x18\x46\x9a\x3f"
+ "\xeb\xc1\xf9\xf1\x90\x57\xeb\xac\x7a\x0d\x8b\xdb\x72\x30\x6a\x66"
+ "\xd5\xe0\x46\xa3\x70\xdc\x68\xd9\xff\x04\x48\x89\x77\xde\xb5\xe9"
+ "\xfb\x67\x6d\x41\xe9\xbc\x39\xbd\x32\xd9\x62\x02\xf1\xb1\xa8\x3d"
+ "\x6e\x37\x9c\xe2\x2f\xe2\xd3\xa2\x26\x8b\xc6\xb8\x55\x43\x88\xe1"
+ "\x23\x3e\xa5\xd2\x24\x39\x6a\x47\xab\x00\xd4\xa1\xb3\xa9\x25\xfe"
+ "\x0d\x3f\xa7\x1d\xba\xd3\x51\xc1\x0b\xa4\xda\xac\x38\xef\x55\x50"
+ "\x24\x05\x65\x46\x93\x34\x4f\x2d\x8d\xad\xc6\xd4\x21\x19\xd2\x8e"
+ "\xca\x05\x61\x71\x07\x73\x47\xe5\x8a\x19\x12\xbd\x04\x4d\xce\x4e"
+ "\x9c\xa5\x48\xac\xbb\x26\xf7\x02\x03\x01\x00\x01\xa3\x81\x8e\x30"
+ "\x81\x8b\x30\x1d\x06\x03\x55\x1d\x0e\x04\x16\x04\x14\x0b\x58\xe5"
+ "\x8b\xc6\x4c\x15\x37\xa4\x40\xa9\x30\xa9\x21\xbe\x47\x36\x5a\x56"
+ "\xff\x30\x0e\x06\x03\x55\x1d\x0f\x01\x01\xff\x04\x04\x03\x02\x01"
+ "\x06\x30\x0f\x06\x03\x55\x1d\x13\x01\x01\xff\x04\x05\x30\x03\x01"
+ "\x01\xff\x30\x49\x06\x03\x55\x1d\x1f\x04\x42\x30\x40\x30\x3e\xa0"
+ "\x3c\xa0\x3a\x86\x38\x68\x74\x74\x70\x3a\x2f\x2f\x63\x72\x6c\x2e"
+ "\x63\x6f\x6d\x6f\x64\x6f\x63\x61\x2e\x63\x6f\x6d\x2f\x43\x4f\x4d"
+ "\x4f\x44\x4f\x43\x65\x72\x74\x69\x66\x69\x63\x61\x74\x69\x6f\x6e"
+ "\x41\x75\x74\x68\x6f\x72\x69\x74\x79\x2e\x63\x72\x6c\x30\x0d\x06"
+ "\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x03\x82\x01\x01"
+ "\x00\x3e\x98\x9e\x9b\xf6\x1b\xe9\xd7\x39\xb7\x78\xae\x1d\x72\x18"
+ "\x49\xd3\x87\xe4\x43\x82\xeb\x3f\xc9\xaa\xf5\xa8\xb5\xef\x55\x7c"
+ "\x21\x52\x65\xf9\xd5\x0d\xe1\x6c\xf4\x3e\x8c\x93\x73\x91\x2e\x02"
+ "\xc4\x4e\x07\x71\x6f\xc0\x8f\x38\x61\x08\xa8\x1e\x81\x0a\xc0\x2f"
+ "\x20\x2f\x41\x8b\x91\xdc\x48\x45\xbc\xf1\xc6\xde\xba\x76\x6b\x33"
+ "\xc8\x00\x2d\x31\x46\x4c\xed\xe7\x9d\xcf\x88\x94\xff\x33\xc0\x56"
+ "\xe8\x24\x86\x26\xb8\xd8\x38\x38\xdf\x2a\x6b\xdd\x12\xcc\xc7\x3f"
+ "\x47\x17\x4c\xa2\xc2\x06\x96\x09\xd6\xdb\xfe\x3f\x3c\x46\x41\xdf"
+ "\x58\xe2\x56\x0f\x3c\x3b\xc1\x1c\x93\x35\xd9\x38\x52\xac\xee\xc8"
+ "\xec\x2e\x30\x4e\x94\x35\xb4\x24\x1f\x4b\x78\x69\xda\xf2\x02\x38"
+ "\xcc\x95\x52\x93\xf0\x70\x25\x59\x9c\x20\x67\xc4\xee\xf9\x8b\x57"
+ "\x61\xf4\x92\x76\x7d\x3f\x84\x8d\x55\xb7\xe8\xe5\xac\xd5\xf1\xf5"
+ "\x19\x56\xa6\x5a\xfb\x90\x1c\xaf\x93\xeb\xe5\x1c\xd4\x67\x97\x5d"
+ "\x04\x0e\xbe\x0b\x83\xa6\x17\x83\xb9\x30\x12\xa0\xc5\x33\x15\x05"
+ "\xb9\x0d\xfb\xc7\x05\x76\xe3\xd8\x4a\x8d\xfc\x34\x17\xa3\xc6\x21"
+ "\x28\xbe\x30\x45\x31\x1e\xc7\x78\xbe\x58\x61\x38\xac\x3b\xe2\x01"
+ "\x65";
+
+char certdata2[] =
+ "\x30\x82\x03\x02\x30\x82\x02\x6b\x02\x10\x39\xca\x54\x89\xfe\x50"
+ "\x22\x32\xfe\x32\xd9\xdb\xfb\x1b\x84\x19\x30\x0d\x06\x09\x2a\x86"
+ "\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x30\x81\xc1\x31\x0b\x30\x09"
+ "\x06\x03\x55\x04\x06\x13\x02\x55\x53\x31\x17\x30\x15\x06\x03\x55"
+ "\x04\x0a\x13\x0e\x56\x65\x72\x69\x53\x69\x67\x6e\x2c\x20\x49\x6e"
+ "\x63\x2e\x31\x3c\x30\x3a\x06\x03\x55\x04\x0b\x13\x33\x43\x6c\x61"
+ "\x73\x73\x20\x31\x20\x50\x75\x62\x6c\x69\x63\x20\x50\x72\x69\x6d"
+ "\x61\x72\x79\x20\x43\x65\x72\x74\x69\x66\x69\x63\x61\x74\x69\x6f"
+ "\x6e\x20\x41\x75\x74\x68\x6f\x72\x69\x74\x79\x20\x2d\x20\x47\x32"
+ "\x31\x3a\x30\x38\x06\x03\x55\x04\x0b\x13\x31\x28\x63\x29\x20\x31"
+ "\x39\x39\x38\x20\x56\x65\x72\x69\x53\x69\x67\x6e\x2c\x20\x49\x6e"
+ "\x63\x2e\x20\x2d\x20\x46\x6f\x72\x20\x61\x75\x74\x68\x6f\x72\x69"
+ "\x7a\x65\x64\x20\x75\x73\x65\x20\x6f\x6e\x6c\x79\x31\x1f\x30\x1d"
+ "\x06\x03\x55\x04\x0b\x13\x16\x56\x65\x72\x69\x53\x69\x67\x6e\x20"
+ "\x54\x72\x75\x73\x74\x20\x4e\x65\x74\x77\x6f\x72\x6b\x30\x1e\x17"
+ "\x0d\x39\x38\x30\x35\x31\x38\x30\x30\x30\x30\x30\x30\x5a\x17\x0d"
+ "\x31\x38\x30\x35\x31\x38\x32\x33\x35\x39\x35\x39\x5a\x30\x81\xc1"
+ "\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13\x02\x55\x53\x31\x17\x30"
+ "\x15\x06\x03\x55\x04\x0a\x13\x0e\x56\x65\x72\x69\x53\x69\x67\x6e"
+ "\x2c\x20\x49\x6e\x63\x2e\x31\x3c\x30\x3a\x06\x03\x55\x04\x0b\x13"
+ "\x33\x43\x6c\x61\x73\x73\x20\x31\x20\x50\x75\x62\x6c\x69\x63\x20"
+ "\x50\x72\x69\x6d\x61\x72\x79\x20\x43\x65\x72\x74\x69\x66\x69\x63"
+ "\x61\x74\x69\x6f\x6e\x20\x41\x75\x74\x68\x6f\x72\x69\x74\x79\x20"
+ "\x2d\x20\x47\x32\x31\x3a\x30\x38\x06\x03\x55\x04\x0b\x13\x31\x28"
+ "\x63\x29\x20\x31\x39\x39\x38\x20\x56\x65\x72\x69\x53\x69\x67\x6e"
+ "\x2c\x20\x49\x6e\x63\x2e\x20\x2d\x20\x46\x6f\x72\x20\x61\x75\x74"
+ "\x68\x6f\x72\x69\x7a\x65\x64\x20\x75\x73\x65\x20\x6f\x6e\x6c\x79"
+ "\x31\x1f\x30\x1d\x06\x03\x55\x04\x0b\x13\x16\x56\x65\x72\x69\x53"
+ "\x69\x67\x6e\x20\x54\x72\x75\x73\x74\x20\x4e\x65\x74\x77\x6f\x72"
+ "\x6b\x30\x81\x9f\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01"
+ "\x01\x05\x00\x03\x81\x8d\x00\x30\x81\x89\x02\x81\x81\x00\xaa\xd0"
+ "\xba\xbe\x16\x2d\xb8\x83\xd4\xca\xd2\x0f\xbc\x76\x31\xca\x94\xd8"
+ "\x1d\x93\x8c\x56\x02\xbc\xd9\x6f\x1a\x6f\x52\x36\x6e\x75\x56\x0a"
+ "\x55\xd3\xdf\x43\x87\x21\x11\x65\x8a\x7e\x8f\xbd\x21\xde\x6b\x32"
+ "\x3f\x1b\x84\x34\x95\x05\x9d\x41\x35\xeb\x92\xeb\x96\xdd\xaa\x59"
+ "\x3f\x01\x53\x6d\x99\x4f\xed\xe5\xe2\x2a\x5a\x90\xc1\xb9\xc4\xa6"
+ "\x15\xcf\xc8\x45\xeb\xa6\x5d\x8e\x9c\x3e\xf0\x64\x24\x76\xa5\xcd"
+ "\xab\x1a\x6f\xb6\xd8\x7b\x51\x61\x6e\xa6\x7f\x87\xc8\xe2\xb7\xe5"
+ "\x34\xdc\x41\x88\xea\x09\x40\xbe\x73\x92\x3d\x6b\xe7\x75\x02\x03"
+ "\x01\x00\x01\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05"
+ "\x05\x00\x03\x81\x81\x00\x8b\xf7\x1a\x10\xce\x76\x5c\x07\xab\x83"
+ "\x99\xdc\x17\x80\x6f\x34\x39\x5d\x98\x3e\x6b\x72\x2c\xe1\xc7\xa2"
+ "\x7b\x40\x29\xb9\x78\x88\xba\x4c\xc5\xa3\x6a\x5e\x9e\x6e\x7b\xe3"
+ "\xf2\x02\x41\x0c\x66\xbe\xad\xfb\xae\xa2\x14\xce\x92\xf3\xa2\x34"
+ "\x8b\xb4\xb2\xb6\x24\xf2\xe5\xd5\xe0\xc8\xe5\x62\x6d\x84\x7b\xcb"
+ "\xbe\xbb\x03\x8b\x7c\x57\xca\xf0\x37\xa9\x90\xaf\x8a\xee\x03\xbe"
+ "\x1d\x28\x9c\xd9\x26\x76\xa0\xcd\xc4\x9d\x4e\xf0\xae\x07\x16\xd5"
+ "\xbe\xaf\x57\x08\x6a\xd0\xa0\x42\x42\x42\x1e\xf4\x20\xcc\xa5\x78"
+ "\x82\x95\x26\x38\x8a\x47";
+
+char certdata3[] =
+ "\x30\x82\x04\x43\x30\x82\x03\x2b\xa0\x03\x02\x01\x02\x02\x01\x01"
+ "\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x30"
+ "\x7f\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13\x02\x47\x42\x31\x1b"
+ "\x30\x19\x06\x03\x55\x04\x08\x0c\x12\x47\x72\x65\x61\x74\x65\x72"
+ "\x20\x4d\x61\x6e\x63\x68\x65\x73\x74\x65\x72\x31\x10\x30\x0e\x06"
+ "\x03\x55\x04\x07\x0c\x07\x53\x61\x6c\x66\x6f\x72\x64\x31\x1a\x30"
+ "\x18\x06\x03\x55\x04\x0a\x0c\x11\x43\x6f\x6d\x6f\x64\x6f\x20\x43"
+ "\x41\x20\x4c\x69\x6d\x69\x74\x65\x64\x31\x25\x30\x23\x06\x03\x55"
+ "\x04\x03\x0c\x1c\x54\x72\x75\x73\x74\x65\x64\x20\x43\x65\x72\x74"
+ "\x69\x66\x69\x63\x61\x74\x65\x20\x53\x65\x72\x76\x69\x63\x65\x73"
+ "\x30\x1e\x17\x0d\x30\x34\x30\x31\x30\x31\x30\x30\x30\x30\x30\x30"
+ "\x5a\x17\x0d\x32\x38\x31\x32\x33\x31\x32\x33\x35\x39\x35\x39\x5a"
+ "\x30\x7f\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13\x02\x47\x42\x31"
+ "\x1b\x30\x19\x06\x03\x55\x04\x08\x0c\x12\x47\x72\x65\x61\x74\x65"
+ "\x72\x20\x4d\x61\x6e\x63\x68\x65\x73\x74\x65\x72\x31\x10\x30\x0e"
+ "\x06\x03\x55\x04\x07\x0c\x07\x53\x61\x6c\x66\x6f\x72\x64\x31\x1a"
+ "\x30\x18\x06\x03\x55\x04\x0a\x0c\x11\x43\x6f\x6d\x6f\x64\x6f\x20"
+ "\x43\x41\x20\x4c\x69\x6d\x69\x74\x65\x64\x31\x25\x30\x23\x06\x03"
+ "\x55\x04\x03\x0c\x1c\x54\x72\x75\x73\x74\x65\x64\x20\x43\x65\x72"
+ "\x74\x69\x66\x69\x63\x61\x74\x65\x20\x53\x65\x72\x76\x69\x63\x65"
+ "\x73\x30\x82\x01\x22\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01"
+ "\x01\x01\x05\x00\x03\x82\x01\x0f\x00\x30\x82\x01\x0a\x02\x82\x01"
+ "\x01\x00\xdf\x71\x6f\x36\x58\x53\x5a\xf2\x36\x54\x57\x80\xc4\x74"
+ "\x08\x20\xed\x18\x7f\x2a\x1d\xe6\x35\x9a\x1e\x25\xac\x9c\xe5\x96"
+ "\x7e\x72\x52\xa0\x15\x42\xdb\x59\xdd\x64\x7a\x1a\xd0\xb8\x7b\xdd"
+ "\x39\x15\xbc\x55\x48\xc4\xed\x3a\x00\xea\x31\x11\xba\xf2\x71\x74"
+ "\x1a\x67\xb8\xcf\x33\xcc\xa8\x31\xaf\xa3\xe3\xd7\x7f\xbf\x33\x2d"
+ "\x4c\x6a\x3c\xec\x8b\xc3\x92\xd2\x53\x77\x24\x74\x9c\x07\x6e\x70"
+ "\xfc\xbd\x0b\x5b\x76\xba\x5f\xf2\xff\xd7\x37\x4b\x4a\x60\x78\xf7"
+ "\xf0\xfa\xca\x70\xb4\xea\x59\xaa\xa3\xce\x48\x2f\xa9\xc3\xb2\x0b"
+ "\x7e\x17\x72\x16\x0c\xa6\x07\x0c\x1b\x38\xcf\xc9\x62\xb7\x3f\xa0"
+ "\x93\xa5\x87\x41\xf2\xb7\x70\x40\x77\xd8\xbe\x14\x7c\xe3\xa8\xc0"
+ "\x7a\x8e\xe9\x63\x6a\xd1\x0f\x9a\xc6\xd2\xf4\x8b\x3a\x14\x04\x56"
+ "\xd4\xed\xb8\xcc\x6e\xf5\xfb\xe2\x2c\x58\xbd\x7f\x4f\x6b\x2b\xf7"
+ "\x60\x24\x58\x24\xce\x26\xef\x34\x91\x3a\xd5\xe3\x81\xd0\xb2\xf0"
+ "\x04\x02\xd7\x5b\xb7\x3e\x92\xac\x6b\x12\x8a\xf9\xe4\x05\xb0\x3b"
+ "\x91\x49\x5c\xb2\xeb\x53\xea\xf8\x9f\x47\x86\xee\xbf\x95\xc0\xc0"
+ "\x06\x9f\xd2\x5b\x5e\x11\x1b\xf4\xc7\x04\x35\x29\xd2\x55\x5c\xe4"
+ "\xed\xeb\x02\x03\x01\x00\x01\xa3\x81\xc9\x30\x81\xc6\x30\x1d\x06"
+ "\x03\x55\x1d\x0e\x04\x16\x04\x14\xc5\x7b\x58\xbd\xed\xda\x25\x69"
+ "\xd2\xf7\x59\x16\xa8\xb3\x32\xc0\x7b\x27\x5b\xf4\x30\x0e\x06\x03"
+ "\x55\x1d\x0f\x01\x01\xff\x04\x04\x03\x02\x01\x06\x30\x0f\x06\x03"
+ "\x55\x1d\x13\x01\x01\xff\x04\x05\x30\x03\x01\x01\xff\x30\x81\x83"
+ "\x06\x03\x55\x1d\x1f\x04\x7c\x30\x7a\x30\x3c\xa0\x3a\xa0\x38\x86"
+ "\x36\x68\x74\x74\x70\x3a\x2f\x2f\x63\x72\x6c\x2e\x63\x6f\x6d\x6f"
+ "\x64\x6f\x63\x61\x2e\x63\x6f\x6d\x2f\x54\x72\x75\x73\x74\x65\x64"
+ "\x43\x65\x72\x74\x69\x66\x69\x63\x61\x74\x65\x53\x65\x72\x76\x69"
+ "\x63\x65\x73\x2e\x63\x72\x6c\x30\x3a\xa0\x38\xa0\x36\x86\x34\x68"
+ "\x74\x74\x70\x3a\x2f\x2f\x63\x72\x6c\x2e\x63\x6f\x6d\x6f\x64\x6f"
+ "\x2e\x6e\x65\x74\x2f\x54\x72\x75\x73\x74\x65\x64\x43\x65\x72\x74"
+ "\x69\x66\x69\x63\x61\x74\x65\x53\x65\x72\x76\x69\x63\x65\x73\x2e"
+ "\x63\x72\x6c\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05"
+ "\x05\x00\x03\x82\x01\x01\x00\xc8\x93\x81\x3b\x89\xb4\xaf\xb8\x84"
+ "\x12\x4c\x8d\xd2\xf0\xdb\x70\xba\x57\x86\x15\x34\x10\xb9\x2f\x7f"
+ "\x1e\xb0\xa8\x89\x60\xa1\x8a\xc2\x77\x0c\x50\x4a\x9b\x00\x8b\xd8"
+ "\x8b\xf4\x41\xe2\xd0\x83\x8a\x4a\x1c\x14\x06\xb0\xa3\x68\x05\x70"
+ "\x31\x30\xa7\x53\x9b\x0e\xe9\x4a\xa0\x58\x69\x67\x0e\xae\x9d\xf6"
+ "\xa5\x2c\x41\xbf\x3c\x06\x6b\xe4\x59\xcc\x6d\x10\xf1\x96\x6f\x1f"
+ "\xdf\xf4\x04\x02\xa4\x9f\x45\x3e\xc8\xd8\xfa\x36\x46\x44\x50\x3f"
+ "\x82\x97\x91\x1f\x28\xdb\x18\x11\x8c\x2a\xe4\x65\x83\x57\x12\x12"
+ "\x8c\x17\x3f\x94\x36\xfe\x5d\xb0\xc0\x04\x77\x13\xb8\xf4\x15\xd5"
+ "\x3f\x38\xcc\x94\x3a\x55\xd0\xac\x98\xf5\xba\x00\x5f\xe0\x86\x19"
+ "\x81\x78\x2f\x28\xc0\x7e\xd3\xcc\x42\x0a\xf5\xae\x50\xa0\xd1\x3e"
+ "\xc6\xa1\x71\xec\x3f\xa0\x20\x8c\x66\x3a\x89\xb4\x8e\xd4\xd8\xb1"
+ "\x4d\x25\x47\xee\x2f\x88\xc8\xb5\xe1\x05\x45\xc0\xbe\x14\x71\xde"
+ "\x7a\xfd\x8e\x7b\x7d\x4d\x08\x96\xa5\x12\x73\xf0\x2d\xca\x37\x27"
+ "\x74\x12\x27\x4c\xcb\xb6\x97\xe9\xd9\xae\x08\x6d\x5a\x39\x40\xdd"
+ "\x05\x47\x75\x6a\x5a\x21\xb3\xa3\x18\xcf\x4e\xf7\x2e\x57\xb7\x98"
+ "\x70\x5e\xc8\xc4\x78\xb0\x62";
+
+
+static int
+compare_subject(hx509_cert c1, hx509_cert c2, int *l)
+{
+ hx509_name n1, n2;
+ int ret;
+
+ ret = hx509_cert_get_subject(c1, &n1);
+ if (ret) return 1;
+ ret = hx509_cert_get_subject(c2, &n2);
+ if (ret) return 1;
+
+ *l = hx509_name_cmp(n1, n2);
+ hx509_name_free(&n1);
+ hx509_name_free(&n2);
+
+ return 0;
+}
+
+static int
+test_compare(hx509_context context)
+{
+ int ret;
+ hx509_cert c1, c2, c3;
+ int l0, l1, l2, l3;
+
+ /* check transative properties of name compare function */
+
+ ret = hx509_cert_init_data(context, certdata1, sizeof(certdata1) - 1, &c1);
+ if (ret) return 1;
+
+ ret = hx509_cert_init_data(context, certdata2, sizeof(certdata2) - 1, &c2);
+ if (ret) return 1;
+
+ ret = hx509_cert_init_data(context, certdata3, sizeof(certdata3) - 1, &c3);
+ if (ret) return 1;
+
+ ret = compare_subject(c1, c1, &l0);
+ if (ret) return 1;
+ ret = compare_subject(c1, c2, &l1);
+ if (ret) return 1;
+ ret = compare_subject(c1, c3, &l2);
+ if (ret) return 1;
+ ret = compare_subject(c2, c3, &l3);
+ if (ret) return 1;
+
+ if (l0 != 0) return 1;
+ if (l2 < l1) return 1;
+ if (l3 < l2) return 1;
+ if (l3 < l1) return 1;
+
+ hx509_cert_free(c1);
+ hx509_cert_free(c2);
+ hx509_cert_free(c3);
+
+ return 0;
+}
+
+
+int
+main(int argc, char **argv)
+{
+ hx509_context context;
+ int ret = 0;
+
+ ret = hx509_context_init(&context);
+ if (ret)
+ errx(1, "hx509_context_init failed with %d", ret);
+
+ ret += test_name(context, "CN=foo,C=SE");
+ ret += test_name(context, "CN=foo,CN=kaka,CN=FOO,DC=ad1,C=SE");
+ ret += test_name(context, "1.2.3.4=foo,C=SE");
+ ret += test_name_fail(context, "=");
+ ret += test_name_fail(context, "CN=foo,=foo");
+ ret += test_name_fail(context, "CN=foo,really-unknown-type=foo");
+
+ ret += test_expand(context, "UID=${uid},C=SE", "UID=lha,C=SE");
+ ret += test_expand(context, "UID=foo${uid},C=SE", "UID=foolha,C=SE");
+ ret += test_expand(context, "UID=${uid}bar,C=SE", "UID=lhabar,C=SE");
+ ret += test_expand(context, "UID=f${uid}b,C=SE", "UID=flhab,C=SE");
+ ret += test_expand(context, "UID=${uid}${uid},C=SE", "UID=lhalha,C=SE");
+ ret += test_expand(context, "UID=${uid}{uid},C=SE", "UID=lha{uid},C=SE");
+
+ ret += test_compare(context);
+
+ hx509_context_free(&context);
+
+ return ret;
+}
diff --git a/source4/heimdal/lib/krb5/acache.c b/source4/heimdal/lib/krb5/acache.c
new file mode 100644
index 0000000000..bd0a9846e4
--- /dev/null
+++ b/source4/heimdal/lib/krb5/acache.c
@@ -0,0 +1,1059 @@
+/*
+ * Copyright (c) 2004 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+#include <krb5_ccapi.h>
+#ifdef HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+RCSID("$Id$");
+
+/* XXX should we fetch these for each open ? */
+static HEIMDAL_MUTEX acc_mutex = HEIMDAL_MUTEX_INITIALIZER;
+static cc_initialize_func init_func;
+
+#ifdef HAVE_DLOPEN
+static void *cc_handle;
+#endif
+
+typedef struct krb5_acc {
+ char *cache_name;
+ cc_context_t context;
+ cc_ccache_t ccache;
+} krb5_acc;
+
+static krb5_error_code acc_close(krb5_context, krb5_ccache);
+
+#define ACACHE(X) ((krb5_acc *)(X)->data.data)
+
+static const struct {
+ cc_int32 error;
+ krb5_error_code ret;
+} cc_errors[] = {
+ { ccErrBadName, KRB5_CC_BADNAME },
+ { ccErrCredentialsNotFound, KRB5_CC_NOTFOUND },
+ { ccErrCCacheNotFound, KRB5_FCC_NOFILE },
+ { ccErrContextNotFound, KRB5_CC_NOTFOUND },
+ { ccIteratorEnd, KRB5_CC_END },
+ { ccErrNoMem, KRB5_CC_NOMEM },
+ { ccErrServerUnavailable, KRB5_CC_NOSUPP },
+ { ccErrInvalidCCache, KRB5_CC_BADNAME },
+ { ccNoError, 0 }
+};
+
+static krb5_error_code
+translate_cc_error(krb5_context context, cc_int32 error)
+{
+ int i;
+ krb5_clear_error_message(context);
+ for(i = 0; i < sizeof(cc_errors)/sizeof(cc_errors[0]); i++)
+ if (cc_errors[i].error == error)
+ return cc_errors[i].ret;
+ return KRB5_FCC_INTERNAL;
+}
+
+static krb5_error_code
+init_ccapi(krb5_context context)
+{
+ const char *lib;
+
+ HEIMDAL_MUTEX_lock(&acc_mutex);
+ if (init_func) {
+ HEIMDAL_MUTEX_unlock(&acc_mutex);
+ krb5_clear_error_message(context);
+ return 0;
+ }
+
+ lib = krb5_config_get_string(context, NULL,
+ "libdefaults", "ccapi_library",
+ NULL);
+ if (lib == NULL) {
+#ifdef __APPLE__
+ lib = "/System/Library/Frameworks/Kerberos.framework/Kerberos";
+#else
+ lib = "/usr/lib/libkrb5_cc.so";
+#endif
+ }
+
+#ifdef HAVE_DLOPEN
+
+#ifndef RTLD_LAZY
+#define RTLD_LAZY 0
+#endif
+
+ cc_handle = dlopen(lib, RTLD_LAZY);
+ if (cc_handle == NULL) {
+ HEIMDAL_MUTEX_unlock(&acc_mutex);
+ krb5_set_error_message(context, KRB5_CC_NOSUPP,
+ N_("Failed to load API cache module %s", "file"),
+ lib);
+ return KRB5_CC_NOSUPP;
+ }
+
+ init_func = (cc_initialize_func)dlsym(cc_handle, "cc_initialize");
+ HEIMDAL_MUTEX_unlock(&acc_mutex);
+ if (init_func == NULL) {
+ krb5_set_error_message(context, KRB5_CC_NOSUPP,
+ N_("Failed to find cc_initialize"
+ "in %s: %s", "file, error"), lib, dlerror());
+ dlclose(cc_handle);
+ return KRB5_CC_NOSUPP;
+ }
+
+ return 0;
+#else
+ HEIMDAL_MUTEX_unlock(&acc_mutex);
+ krb5_set_error_message(context, KRB5_CC_NOSUPP,
+ N_("no support for shared object", "file, error"));
+ return KRB5_CC_NOSUPP;
+#endif
+}
+
+static krb5_error_code
+make_cred_from_ccred(krb5_context context,
+ const cc_credentials_v5_t *incred,
+ krb5_creds *cred)
+{
+ krb5_error_code ret;
+ unsigned int i;
+
+ memset(cred, 0, sizeof(*cred));
+
+ ret = krb5_parse_name(context, incred->client, &cred->client);
+ if (ret)
+ goto fail;
+
+ ret = krb5_parse_name(context, incred->server, &cred->server);
+ if (ret)
+ goto fail;
+
+ cred->session.keytype = incred->keyblock.type;
+ cred->session.keyvalue.length = incred->keyblock.length;
+ cred->session.keyvalue.data = malloc(incred->keyblock.length);
+ if (cred->session.keyvalue.data == NULL)
+ goto nomem;
+ memcpy(cred->session.keyvalue.data, incred->keyblock.data,
+ incred->keyblock.length);
+
+ cred->times.authtime = incred->authtime;
+ cred->times.starttime = incred->starttime;
+ cred->times.endtime = incred->endtime;
+ cred->times.renew_till = incred->renew_till;
+
+ ret = krb5_data_copy(&cred->ticket,
+ incred->ticket.data,
+ incred->ticket.length);
+ if (ret)
+ goto nomem;
+
+ ret = krb5_data_copy(&cred->second_ticket,
+ incred->second_ticket.data,
+ incred->second_ticket.length);
+ if (ret)
+ goto nomem;
+
+ cred->authdata.val = NULL;
+ cred->authdata.len = 0;
+
+ cred->addresses.val = NULL;
+ cred->addresses.len = 0;
+
+ for (i = 0; incred->authdata && incred->authdata[i]; i++)
+ ;
+
+ if (i) {
+ cred->authdata.val = calloc(i, sizeof(cred->authdata.val[0]));
+ if (cred->authdata.val == NULL)
+ goto nomem;
+ cred->authdata.len = i;
+ for (i = 0; i < cred->authdata.len; i++) {
+ cred->authdata.val[i].ad_type = incred->authdata[i]->type;
+ ret = krb5_data_copy(&cred->authdata.val[i].ad_data,
+ incred->authdata[i]->data,
+ incred->authdata[i]->length);
+ if (ret)
+ goto nomem;
+ }
+ }
+
+ for (i = 0; incred->addresses && incred->addresses[i]; i++)
+ ;
+
+ if (i) {
+ cred->addresses.val = calloc(i, sizeof(cred->addresses.val[0]));
+ if (cred->addresses.val == NULL)
+ goto nomem;
+ cred->addresses.len = i;
+
+ for (i = 0; i < cred->addresses.len; i++) {
+ cred->addresses.val[i].addr_type = incred->addresses[i]->type;
+ ret = krb5_data_copy(&cred->addresses.val[i].address,
+ incred->addresses[i]->data,
+ incred->addresses[i]->length);
+ if (ret)
+ goto nomem;
+ }
+ }
+
+ cred->flags.i = 0;
+ if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_FORWARDABLE)
+ cred->flags.b.forwardable = 1;
+ if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_FORWARDED)
+ cred->flags.b.forwarded = 1;
+ if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_PROXIABLE)
+ cred->flags.b.proxiable = 1;
+ if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_PROXY)
+ cred->flags.b.proxy = 1;
+ if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_MAY_POSTDATE)
+ cred->flags.b.may_postdate = 1;
+ if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_POSTDATED)
+ cred->flags.b.postdated = 1;
+ if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_INVALID)
+ cred->flags.b.invalid = 1;
+ if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_RENEWABLE)
+ cred->flags.b.renewable = 1;
+ if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_INITIAL)
+ cred->flags.b.initial = 1;
+ if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_PRE_AUTH)
+ cred->flags.b.pre_authent = 1;
+ if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_HW_AUTH)
+ cred->flags.b.hw_authent = 1;
+ if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_TRANSIT_POLICY_CHECKED)
+ cred->flags.b.transited_policy_checked = 1;
+ if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_OK_AS_DELEGATE)
+ cred->flags.b.ok_as_delegate = 1;
+ if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_ANONYMOUS)
+ cred->flags.b.anonymous = 1;
+
+ return 0;
+
+nomem:
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, N_("malloc: out of memory", "malloc"));
+
+fail:
+ krb5_free_cred_contents(context, cred);
+ return ret;
+}
+
+static void
+free_ccred(cc_credentials_v5_t *cred)
+{
+ int i;
+
+ if (cred->addresses) {
+ for (i = 0; cred->addresses[i] != 0; i++) {
+ if (cred->addresses[i]->data)
+ free(cred->addresses[i]->data);
+ free(cred->addresses[i]);
+ }
+ free(cred->addresses);
+ }
+ if (cred->server)
+ free(cred->server);
+ if (cred->client)
+ free(cred->client);
+ memset(cred, 0, sizeof(*cred));
+}
+
+static krb5_error_code
+make_ccred_from_cred(krb5_context context,
+ const krb5_creds *incred,
+ cc_credentials_v5_t *cred)
+{
+ krb5_error_code ret;
+ int i;
+
+ memset(cred, 0, sizeof(*cred));
+
+ ret = krb5_unparse_name(context, incred->client, &cred->client);
+ if (ret)
+ goto fail;
+
+ ret = krb5_unparse_name(context, incred->server, &cred->server);
+ if (ret)
+ goto fail;
+
+ cred->keyblock.type = incred->session.keytype;
+ cred->keyblock.length = incred->session.keyvalue.length;
+ cred->keyblock.data = incred->session.keyvalue.data;
+
+ cred->authtime = incred->times.authtime;
+ cred->starttime = incred->times.starttime;
+ cred->endtime = incred->times.endtime;
+ cred->renew_till = incred->times.renew_till;
+
+ cred->ticket.length = incred->ticket.length;
+ cred->ticket.data = incred->ticket.data;
+
+ cred->second_ticket.length = incred->second_ticket.length;
+ cred->second_ticket.data = incred->second_ticket.data;
+
+ /* XXX this one should also be filled in */
+ cred->authdata = NULL;
+
+ cred->addresses = calloc(incred->addresses.len + 1,
+ sizeof(cred->addresses[0]));
+ if (cred->addresses == NULL) {
+
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ for (i = 0; i < incred->addresses.len; i++) {
+ cc_data *addr;
+ addr = malloc(sizeof(*addr));
+ if (addr == NULL) {
+ ret = ENOMEM;
+ goto fail;
+ }
+ addr->type = incred->addresses.val[i].addr_type;
+ addr->length = incred->addresses.val[i].address.length;
+ addr->data = malloc(addr->length);
+ if (addr->data == NULL) {
+ ret = ENOMEM;
+ goto fail;
+ }
+ memcpy(addr->data, incred->addresses.val[i].address.data,
+ addr->length);
+ cred->addresses[i] = addr;
+ }
+ cred->addresses[i] = NULL;
+
+ cred->ticket_flags = 0;
+ if (incred->flags.b.forwardable)
+ cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_FORWARDABLE;
+ if (incred->flags.b.forwarded)
+ cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_FORWARDED;
+ if (incred->flags.b.proxiable)
+ cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_PROXIABLE;
+ if (incred->flags.b.proxy)
+ cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_PROXY;
+ if (incred->flags.b.may_postdate)
+ cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_MAY_POSTDATE;
+ if (incred->flags.b.postdated)
+ cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_POSTDATED;
+ if (incred->flags.b.invalid)
+ cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_INVALID;
+ if (incred->flags.b.renewable)
+ cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_RENEWABLE;
+ if (incred->flags.b.initial)
+ cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_INITIAL;
+ if (incred->flags.b.pre_authent)
+ cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_PRE_AUTH;
+ if (incred->flags.b.hw_authent)
+ cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_HW_AUTH;
+ if (incred->flags.b.transited_policy_checked)
+ cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_TRANSIT_POLICY_CHECKED;
+ if (incred->flags.b.ok_as_delegate)
+ cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_OK_AS_DELEGATE;
+ if (incred->flags.b.anonymous)
+ cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_ANONYMOUS;
+
+ return 0;
+
+fail:
+ free_ccred(cred);
+
+ krb5_clear_error_message(context);
+ return ret;
+}
+
+static cc_int32
+get_cc_name(krb5_acc *a)
+{
+ cc_string_t name;
+ cc_int32 error;
+
+ error = (*a->ccache->func->get_name)(a->ccache, &name);
+ if (error)
+ return error;
+
+ a->cache_name = strdup(name->data);
+ (*name->func->release)(name);
+ if (a->cache_name == NULL)
+ return ccErrNoMem;
+ return ccNoError;
+}
+
+
+static const char*
+acc_get_name(krb5_context context,
+ krb5_ccache id)
+{
+ krb5_acc *a = ACACHE(id);
+ int32_t error;
+
+ if (a->cache_name == NULL) {
+ krb5_error_code ret;
+ krb5_principal principal;
+ char *name;
+
+ ret = _krb5_get_default_principal_local(context, &principal);
+ if (ret)
+ return NULL;
+
+ ret = krb5_unparse_name(context, principal, &name);
+ krb5_free_principal(context, principal);
+ if (ret)
+ return NULL;
+
+ error = (*a->context->func->create_new_ccache)(a->context,
+ cc_credentials_v5,
+ name,
+ &a->ccache);
+ krb5_xfree(name);
+ if (error)
+ return NULL;
+
+ error = get_cc_name(a);
+ if (error)
+ return NULL;
+ }
+
+ return a->cache_name;
+}
+
+static krb5_error_code
+acc_alloc(krb5_context context, krb5_ccache *id)
+{
+ krb5_error_code ret;
+ cc_int32 error;
+ krb5_acc *a;
+
+ ret = init_ccapi(context);
+ if (ret)
+ return ret;
+
+ ret = krb5_data_alloc(&(*id)->data, sizeof(*a));
+ if (ret) {
+ krb5_clear_error_message(context);
+ return ret;
+ }
+
+ a = ACACHE(*id);
+
+ error = (*init_func)(&a->context, ccapi_version_3, NULL, NULL);
+ if (error) {
+ krb5_data_free(&(*id)->data);
+ return translate_cc_error(context, error);
+ }
+
+ a->cache_name = NULL;
+
+ return 0;
+}
+
+static krb5_error_code
+acc_resolve(krb5_context context, krb5_ccache *id, const char *res)
+{
+ krb5_error_code ret;
+ cc_int32 error;
+ krb5_acc *a;
+
+ ret = acc_alloc(context, id);
+ if (ret)
+ return ret;
+
+ a = ACACHE(*id);
+
+ error = (*a->context->func->open_ccache)(a->context, res, &a->ccache);
+ if (error == ccNoError) {
+ error = get_cc_name(a);
+ if (error != ccNoError) {
+ acc_close(context, *id);
+ *id = NULL;
+ return translate_cc_error(context, error);
+ }
+ } else if (error == ccErrCCacheNotFound) {
+ a->ccache = NULL;
+ a->cache_name = NULL;
+ error = 0;
+ } else {
+ *id = NULL;
+ return translate_cc_error(context, error);
+ }
+
+ return 0;
+}
+
+static krb5_error_code
+acc_gen_new(krb5_context context, krb5_ccache *id)
+{
+ krb5_error_code ret;
+ krb5_acc *a;
+
+ ret = acc_alloc(context, id);
+ if (ret)
+ return ret;
+
+ a = ACACHE(*id);
+
+ a->ccache = NULL;
+ a->cache_name = NULL;
+
+ return 0;
+}
+
+static krb5_error_code
+acc_initialize(krb5_context context,
+ krb5_ccache id,
+ krb5_principal primary_principal)
+{
+ krb5_acc *a = ACACHE(id);
+ krb5_error_code ret;
+ int32_t error;
+ char *name;
+
+ ret = krb5_unparse_name(context, primary_principal, &name);
+ if (ret)
+ return ret;
+
+ if (a->cache_name == NULL) {
+ error = (*a->context->func->create_new_ccache)(a->context,
+ cc_credentials_v5,
+ name,
+ &a->ccache);
+ free(name);
+ if (error == ccNoError)
+ error = get_cc_name(a);
+ } else {
+ cc_credentials_iterator_t iter;
+ cc_credentials_t ccred;
+
+ error = (*a->ccache->func->new_credentials_iterator)(a->ccache, &iter);
+ if (error) {
+ free(name);
+ return translate_cc_error(context, error);
+ }
+
+ while (1) {
+ error = (*iter->func->next)(iter, &ccred);
+ if (error)
+ break;
+ (*a->ccache->func->remove_credentials)(a->ccache, ccred);
+ (*ccred->func->release)(ccred);
+ }
+ (*iter->func->release)(iter);
+
+ error = (*a->ccache->func->set_principal)(a->ccache,
+ cc_credentials_v5,
+ name);
+ }
+
+ return translate_cc_error(context, error);
+}
+
+static krb5_error_code
+acc_close(krb5_context context,
+ krb5_ccache id)
+{
+ krb5_acc *a = ACACHE(id);
+
+ if (a->ccache) {
+ (*a->ccache->func->release)(a->ccache);
+ a->ccache = NULL;
+ }
+ if (a->cache_name) {
+ free(a->cache_name);
+ a->cache_name = NULL;
+ }
+ if (a->context) {
+ (*a->context->func->release)(a->context);
+ a->context = NULL;
+ }
+ krb5_data_free(&id->data);
+ return 0;
+}
+
+static krb5_error_code
+acc_destroy(krb5_context context,
+ krb5_ccache id)
+{
+ krb5_acc *a = ACACHE(id);
+ cc_int32 error = 0;
+
+ if (a->ccache) {
+ error = (*a->ccache->func->destroy)(a->ccache);
+ a->ccache = NULL;
+ }
+ if (a->context) {
+ error = (a->context->func->release)(a->context);
+ a->context = NULL;
+ }
+ return translate_cc_error(context, error);
+}
+
+static krb5_error_code
+acc_store_cred(krb5_context context,
+ krb5_ccache id,
+ krb5_creds *creds)
+{
+ krb5_acc *a = ACACHE(id);
+ cc_credentials_union cred;
+ cc_credentials_v5_t v5cred;
+ krb5_error_code ret;
+ cc_int32 error;
+
+ if (a->ccache == NULL) {
+ krb5_set_error_message(context, KRB5_CC_NOTFOUND,
+ N_("No API credential found", ""));
+ return KRB5_CC_NOTFOUND;
+ }
+
+ cred.version = cc_credentials_v5;
+ cred.credentials.credentials_v5 = &v5cred;
+
+ ret = make_ccred_from_cred(context,
+ creds,
+ &v5cred);
+ if (ret)
+ return ret;
+
+ error = (*a->ccache->func->store_credentials)(a->ccache, &cred);
+ if (error)
+ ret = translate_cc_error(context, error);
+
+ free_ccred(&v5cred);
+
+ return ret;
+}
+
+static krb5_error_code
+acc_get_principal(krb5_context context,
+ krb5_ccache id,
+ krb5_principal *principal)
+{
+ krb5_acc *a = ACACHE(id);
+ krb5_error_code ret;
+ int32_t error;
+ cc_string_t name;
+
+ if (a->ccache == NULL) {
+ krb5_set_error_message(context, KRB5_CC_NOTFOUND,
+ N_("No API credential found", ""));
+ return KRB5_CC_NOTFOUND;
+ }
+
+ error = (*a->ccache->func->get_principal)(a->ccache,
+ cc_credentials_v5,
+ &name);
+ if (error)
+ return translate_cc_error(context, error);
+
+ ret = krb5_parse_name(context, name->data, principal);
+
+ (*name->func->release)(name);
+ return ret;
+}
+
+static krb5_error_code
+acc_get_first (krb5_context context,
+ krb5_ccache id,
+ krb5_cc_cursor *cursor)
+{
+ cc_credentials_iterator_t iter;
+ krb5_acc *a = ACACHE(id);
+ int32_t error;
+
+ if (a->ccache == NULL) {
+ krb5_set_error_message(context, KRB5_CC_NOTFOUND,
+ N_("No API credential found", ""));
+ return KRB5_CC_NOTFOUND;
+ }
+
+ error = (*a->ccache->func->new_credentials_iterator)(a->ccache, &iter);
+ if (error) {
+ krb5_clear_error_message(context);
+ return ENOENT;
+ }
+ *cursor = iter;
+ return 0;
+}
+
+
+static krb5_error_code
+acc_get_next (krb5_context context,
+ krb5_ccache id,
+ krb5_cc_cursor *cursor,
+ krb5_creds *creds)
+{
+ cc_credentials_iterator_t iter = *cursor;
+ cc_credentials_t cred;
+ krb5_error_code ret;
+ int32_t error;
+
+ while (1) {
+ error = (*iter->func->next)(iter, &cred);
+ if (error)
+ return translate_cc_error(context, error);
+ if (cred->data->version == cc_credentials_v5)
+ break;
+ (*cred->func->release)(cred);
+ }
+
+ ret = make_cred_from_ccred(context,
+ cred->data->credentials.credentials_v5,
+ creds);
+ (*cred->func->release)(cred);
+ return ret;
+}
+
+static krb5_error_code
+acc_end_get (krb5_context context,
+ krb5_ccache id,
+ krb5_cc_cursor *cursor)
+{
+ cc_credentials_iterator_t iter = *cursor;
+ (*iter->func->release)(iter);
+ return 0;
+}
+
+static krb5_error_code
+acc_remove_cred(krb5_context context,
+ krb5_ccache id,
+ krb5_flags which,
+ krb5_creds *cred)
+{
+ cc_credentials_iterator_t iter;
+ krb5_acc *a = ACACHE(id);
+ cc_credentials_t ccred;
+ krb5_error_code ret;
+ cc_int32 error;
+ char *client, *server;
+
+ if (a->ccache == NULL) {
+ krb5_set_error_message(context, KRB5_CC_NOTFOUND,
+ N_("No API credential found", ""));
+ return KRB5_CC_NOTFOUND;
+ }
+
+ if (cred->client) {
+ ret = krb5_unparse_name(context, cred->client, &client);
+ if (ret)
+ return ret;
+ } else
+ client = NULL;
+
+ ret = krb5_unparse_name(context, cred->server, &server);
+ if (ret) {
+ free(client);
+ return ret;
+ }
+
+ error = (*a->ccache->func->new_credentials_iterator)(a->ccache, &iter);
+ if (error) {
+ free(server);
+ free(client);
+ return translate_cc_error(context, error);
+ }
+
+ ret = KRB5_CC_NOTFOUND;
+ while (1) {
+ cc_credentials_v5_t *v5cred;
+
+ error = (*iter->func->next)(iter, &ccred);
+ if (error)
+ break;
+
+ if (ccred->data->version != cc_credentials_v5)
+ goto next;
+
+ v5cred = ccred->data->credentials.credentials_v5;
+
+ if (client && strcmp(v5cred->client, client) != 0)
+ goto next;
+
+ if (strcmp(v5cred->server, server) != 0)
+ goto next;
+
+ (*a->ccache->func->remove_credentials)(a->ccache, ccred);
+ ret = 0;
+ next:
+ (*ccred->func->release)(ccred);
+ }
+
+ (*iter->func->release)(iter);
+
+ if (ret)
+ krb5_set_error_message(context, ret,
+ N_("Can't find credential %s in cache",
+ "principal"), server);
+ free(server);
+ free(client);
+
+ return ret;
+}
+
+static krb5_error_code
+acc_set_flags(krb5_context context,
+ krb5_ccache id,
+ krb5_flags flags)
+{
+ return 0;
+}
+
+static int
+acc_get_version(krb5_context context,
+ krb5_ccache id)
+{
+ return 0;
+}
+
+struct cache_iter {
+ cc_context_t context;
+ cc_ccache_iterator_t iter;
+};
+
+static krb5_error_code
+acc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
+{
+ struct cache_iter *iter;
+ krb5_error_code ret;
+ cc_int32 error;
+
+ ret = init_ccapi(context);
+ if (ret)
+ return ret;
+
+ iter = calloc(1, sizeof(*iter));
+ if (iter == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+
+ error = (*init_func)(&iter->context, ccapi_version_3, NULL, NULL);
+ if (error) {
+ free(iter);
+ return translate_cc_error(context, error);
+ }
+
+ error = (*iter->context->func->new_ccache_iterator)(iter->context,
+ &iter->iter);
+ if (error) {
+ free(iter);
+ krb5_clear_error_message(context);
+ return ENOENT;
+ }
+ *cursor = iter;
+ return 0;
+}
+
+static krb5_error_code
+acc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
+{
+ struct cache_iter *iter = cursor;
+ cc_ccache_t cache;
+ krb5_acc *a;
+ krb5_error_code ret;
+ int32_t error;
+
+ error = (*iter->iter->func->next)(iter->iter, &cache);
+ if (error)
+ return translate_cc_error(context, error);
+
+ ret = _krb5_cc_allocate(context, &krb5_acc_ops, id);
+ if (ret) {
+ (*cache->func->release)(cache);
+ return ret;
+ }
+
+ ret = acc_alloc(context, id);
+ if (ret) {
+ (*cache->func->release)(cache);
+ free(*id);
+ return ret;
+ }
+
+ a = ACACHE(*id);
+ a->ccache = cache;
+
+ error = get_cc_name(a);
+ if (error) {
+ acc_close(context, *id);
+ *id = NULL;
+ return translate_cc_error(context, error);
+ }
+ return 0;
+}
+
+static krb5_error_code
+acc_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
+{
+ struct cache_iter *iter = cursor;
+
+ (*iter->iter->func->release)(iter->iter);
+ iter->iter = NULL;
+ (*iter->context->func->release)(iter->context);
+ iter->context = NULL;
+ free(iter);
+ return 0;
+}
+
+static krb5_error_code
+acc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
+{
+ krb5_acc *afrom = ACACHE(from);
+ krb5_acc *ato = ACACHE(to);
+ int32_t error;
+
+ if (ato->ccache == NULL) {
+ cc_string_t name;
+
+ error = (*afrom->ccache->func->get_principal)(afrom->ccache,
+ cc_credentials_v5,
+ &name);
+ if (error)
+ return translate_cc_error(context, error);
+
+ error = (*ato->context->func->create_new_ccache)(ato->context,
+ cc_credentials_v5,
+ name->data,
+ &ato->ccache);
+ (*name->func->release)(name);
+ if (error)
+ return translate_cc_error(context, error);
+ }
+
+
+ error = (*ato->ccache->func->move)(afrom->ccache, ato->ccache);
+ return translate_cc_error(context, error);
+}
+
+static krb5_error_code
+acc_get_default_name(krb5_context context, char **str)
+{
+ krb5_error_code ret;
+ cc_context_t cc;
+ cc_string_t name;
+ int32_t error;
+
+ ret = init_ccapi(context);
+ if (ret)
+ return ret;
+
+ error = (*init_func)(&cc, ccapi_version_3, NULL, NULL);
+ if (error)
+ return translate_cc_error(context, error);
+
+ error = (*cc->func->get_default_ccache_name)(cc, &name);
+ if (error) {
+ (*cc->func->release)(cc);
+ return translate_cc_error(context, error);
+ }
+
+ asprintf(str, "API:%s", name->data);
+ (*name->func->release)(name);
+ (*cc->func->release)(cc);
+
+ if (*str == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ return 0;
+}
+
+static krb5_error_code
+acc_set_default(krb5_context context, krb5_ccache id)
+{
+ krb5_acc *a = ACACHE(id);
+ cc_int32 error;
+
+ if (a->ccache == NULL) {
+ krb5_set_error_message(context, KRB5_CC_NOTFOUND,
+ N_("No API credential found", ""));
+ return KRB5_CC_NOTFOUND;
+ }
+
+ error = (*a->ccache->func->set_default)(a->ccache);
+ if (error)
+ return translate_cc_error(context, error);
+
+ return 0;
+}
+
+static krb5_error_code
+acc_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime)
+{
+ krb5_acc *a = ACACHE(id);
+ cc_int32 error;
+ cc_time_t t;
+
+ if (a->ccache == NULL) {
+ krb5_set_error_message(context, KRB5_CC_NOTFOUND,
+ N_("No API credential found", ""));
+ return KRB5_CC_NOTFOUND;
+ }
+
+ error = (*a->ccache->func->get_change_time)(a->ccache, &t);
+ if (error)
+ return translate_cc_error(context, error);
+
+ *mtime = t;
+
+ return 0;
+}
+
+/**
+ * Variable containing the API based credential cache implemention.
+ *
+ * @ingroup krb5_ccache
+ */
+
+KRB5_LIB_VARIABLE const krb5_cc_ops krb5_acc_ops = {
+ KRB5_CC_OPS_VERSION,
+ "API",
+ acc_get_name,
+ acc_resolve,
+ acc_gen_new,
+ acc_initialize,
+ acc_destroy,
+ acc_close,
+ acc_store_cred,
+ NULL, /* acc_retrieve */
+ acc_get_principal,
+ acc_get_first,
+ acc_get_next,
+ acc_end_get,
+ acc_remove_cred,
+ acc_set_flags,
+ acc_get_version,
+ acc_get_cache_first,
+ acc_get_cache_next,
+ acc_end_cache_get,
+ acc_move,
+ acc_get_default_name,
+ acc_set_default,
+ acc_lastchange
+};
diff --git a/source4/heimdal/lib/krb5/add_et_list.c b/source4/heimdal/lib/krb5/add_et_list.c
new file mode 100644
index 0000000000..f08c0fe718
--- /dev/null
+++ b/source4/heimdal/lib/krb5/add_et_list.c
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 1999 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+/**
+ * Add a specified list of error messages to the et list in context.
+ * Call func (probably a comerr-generated function) with a pointer to
+ * the current et_list.
+ *
+ * @param context A kerberos context.
+ * @param func The generated com_err et function.
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_add_et_list (krb5_context context,
+ void (*func)(struct et_list **))
+{
+ (*func)(&context->et_list);
+ return 0;
+}
diff --git a/source4/heimdal/lib/krb5/addr_families.c b/source4/heimdal/lib/krb5/addr_families.c
new file mode 100644
index 0000000000..9e2fb3d63a
--- /dev/null
+++ b/source4/heimdal/lib/krb5/addr_families.c
@@ -0,0 +1,1492 @@
+/*
+ * Copyright (c) 1997-2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+struct addr_operations {
+ int af;
+ krb5_address_type atype;
+ size_t max_sockaddr_size;
+ krb5_error_code (*sockaddr2addr)(const struct sockaddr *, krb5_address *);
+ krb5_error_code (*sockaddr2port)(const struct sockaddr *, int16_t *);
+ void (*addr2sockaddr)(const krb5_address *, struct sockaddr *,
+ krb5_socklen_t *sa_size, int port);
+ void (*h_addr2sockaddr)(const char *, struct sockaddr *, krb5_socklen_t *, int);
+ krb5_error_code (*h_addr2addr)(const char *, krb5_address *);
+ krb5_boolean (*uninteresting)(const struct sockaddr *);
+ void (*anyaddr)(struct sockaddr *, krb5_socklen_t *, int);
+ int (*print_addr)(const krb5_address *, char *, size_t);
+ int (*parse_addr)(krb5_context, const char*, krb5_address *);
+ int (*order_addr)(krb5_context, const krb5_address*, const krb5_address*);
+ int (*free_addr)(krb5_context, krb5_address*);
+ int (*copy_addr)(krb5_context, const krb5_address*, krb5_address*);
+ int (*mask_boundary)(krb5_context, const krb5_address*, unsigned long,
+ krb5_address*, krb5_address*);
+};
+
+/*
+ * AF_INET - aka IPv4 implementation
+ */
+
+static krb5_error_code
+ipv4_sockaddr2addr (const struct sockaddr *sa, krb5_address *a)
+{
+ const struct sockaddr_in *sin4 = (const struct sockaddr_in *)sa;
+ unsigned char buf[4];
+
+ a->addr_type = KRB5_ADDRESS_INET;
+ memcpy (buf, &sin4->sin_addr, 4);
+ return krb5_data_copy(&a->address, buf, 4);
+}
+
+static krb5_error_code
+ipv4_sockaddr2port (const struct sockaddr *sa, int16_t *port)
+{
+ const struct sockaddr_in *sin4 = (const struct sockaddr_in *)sa;
+
+ *port = sin4->sin_port;
+ return 0;
+}
+
+static void
+ipv4_addr2sockaddr (const krb5_address *a,
+ struct sockaddr *sa,
+ krb5_socklen_t *sa_size,
+ int port)
+{
+ struct sockaddr_in tmp;
+
+ memset (&tmp, 0, sizeof(tmp));
+ tmp.sin_family = AF_INET;
+ memcpy (&tmp.sin_addr, a->address.data, 4);
+ tmp.sin_port = port;
+ memcpy(sa, &tmp, min(sizeof(tmp), *sa_size));
+ *sa_size = sizeof(tmp);
+}
+
+static void
+ipv4_h_addr2sockaddr(const char *addr,
+ struct sockaddr *sa,
+ krb5_socklen_t *sa_size,
+ int port)
+{
+ struct sockaddr_in tmp;
+
+ memset (&tmp, 0, sizeof(tmp));
+ tmp.sin_family = AF_INET;
+ tmp.sin_port = port;
+ tmp.sin_addr = *((const struct in_addr *)addr);
+ memcpy(sa, &tmp, min(sizeof(tmp), *sa_size));
+ *sa_size = sizeof(tmp);
+}
+
+static krb5_error_code
+ipv4_h_addr2addr (const char *addr,
+ krb5_address *a)
+{
+ unsigned char buf[4];
+
+ a->addr_type = KRB5_ADDRESS_INET;
+ memcpy(buf, addr, 4);
+ return krb5_data_copy(&a->address, buf, 4);
+}
+
+/*
+ * Are there any addresses that should be considered `uninteresting'?
+ */
+
+static krb5_boolean
+ipv4_uninteresting (const struct sockaddr *sa)
+{
+ const struct sockaddr_in *sin4 = (const struct sockaddr_in *)sa;
+
+ if (sin4->sin_addr.s_addr == INADDR_ANY)
+ return TRUE;
+
+ return FALSE;
+}
+
+static void
+ipv4_anyaddr (struct sockaddr *sa, krb5_socklen_t *sa_size, int port)
+{
+ struct sockaddr_in tmp;
+
+ memset (&tmp, 0, sizeof(tmp));
+ tmp.sin_family = AF_INET;
+ tmp.sin_port = port;
+ tmp.sin_addr.s_addr = INADDR_ANY;
+ memcpy(sa, &tmp, min(sizeof(tmp), *sa_size));
+ *sa_size = sizeof(tmp);
+}
+
+static int
+ipv4_print_addr (const krb5_address *addr, char *str, size_t len)
+{
+ struct in_addr ia;
+
+ memcpy (&ia, addr->address.data, 4);
+
+ return snprintf (str, len, "IPv4:%s", inet_ntoa(ia));
+}
+
+static int
+ipv4_parse_addr (krb5_context context, const char *address, krb5_address *addr)
+{
+ const char *p;
+ struct in_addr a;
+
+ p = strchr(address, ':');
+ if(p) {
+ p++;
+ if(strncasecmp(address, "ip:", p - address) != 0 &&
+ strncasecmp(address, "ip4:", p - address) != 0 &&
+ strncasecmp(address, "ipv4:", p - address) != 0 &&
+ strncasecmp(address, "inet:", p - address) != 0)
+ return -1;
+ } else
+ p = address;
+#ifdef HAVE_INET_ATON
+ if(inet_aton(p, &a) == 0)
+ return -1;
+#elif defined(HAVE_INET_ADDR)
+ a.s_addr = inet_addr(p);
+ if(a.s_addr == INADDR_NONE)
+ return -1;
+#else
+ return -1;
+#endif
+ addr->addr_type = KRB5_ADDRESS_INET;
+ if(krb5_data_alloc(&addr->address, 4) != 0)
+ return -1;
+ _krb5_put_int(addr->address.data, ntohl(a.s_addr), addr->address.length);
+ return 0;
+}
+
+static int
+ipv4_mask_boundary(krb5_context context, const krb5_address *inaddr,
+ unsigned long len, krb5_address *low, krb5_address *high)
+{
+ unsigned long ia;
+ uint32_t l, h, m = 0xffffffff;
+
+ if (len > 32) {
+ krb5_set_error_message(context, KRB5_PROG_ATYPE_NOSUPP,
+ N_("IPv4 prefix too large (%ld)", "len"), len);
+ return KRB5_PROG_ATYPE_NOSUPP;
+ }
+ m = m << (32 - len);
+
+ _krb5_get_int(inaddr->address.data, &ia, inaddr->address.length);
+
+ l = ia & m;
+ h = l | ~m;
+
+ low->addr_type = KRB5_ADDRESS_INET;
+ if(krb5_data_alloc(&low->address, 4) != 0)
+ return -1;
+ _krb5_put_int(low->address.data, l, low->address.length);
+
+ high->addr_type = KRB5_ADDRESS_INET;
+ if(krb5_data_alloc(&high->address, 4) != 0) {
+ krb5_free_address(context, low);
+ return -1;
+ }
+ _krb5_put_int(high->address.data, h, high->address.length);
+
+ return 0;
+}
+
+
+/*
+ * AF_INET6 - aka IPv6 implementation
+ */
+
+#ifdef HAVE_IPV6
+
+static krb5_error_code
+ipv6_sockaddr2addr (const struct sockaddr *sa, krb5_address *a)
+{
+ const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sa;
+
+ if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
+ unsigned char buf[4];
+
+ a->addr_type = KRB5_ADDRESS_INET;
+#ifndef IN6_ADDR_V6_TO_V4
+#ifdef IN6_EXTRACT_V4ADDR
+#define IN6_ADDR_V6_TO_V4(x) (&IN6_EXTRACT_V4ADDR(x))
+#else
+#define IN6_ADDR_V6_TO_V4(x) ((const struct in_addr *)&(x)->s6_addr[12])
+#endif
+#endif
+ memcpy (buf, IN6_ADDR_V6_TO_V4(&sin6->sin6_addr), 4);
+ return krb5_data_copy(&a->address, buf, 4);
+ } else {
+ a->addr_type = KRB5_ADDRESS_INET6;
+ return krb5_data_copy(&a->address,
+ &sin6->sin6_addr,
+ sizeof(sin6->sin6_addr));
+ }
+}
+
+static krb5_error_code
+ipv6_sockaddr2port (const struct sockaddr *sa, int16_t *port)
+{
+ const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sa;
+
+ *port = sin6->sin6_port;
+ return 0;
+}
+
+static void
+ipv6_addr2sockaddr (const krb5_address *a,
+ struct sockaddr *sa,
+ krb5_socklen_t *sa_size,
+ int port)
+{
+ struct sockaddr_in6 tmp;
+
+ memset (&tmp, 0, sizeof(tmp));
+ tmp.sin6_family = AF_INET6;
+ memcpy (&tmp.sin6_addr, a->address.data, sizeof(tmp.sin6_addr));
+ tmp.sin6_port = port;
+ memcpy(sa, &tmp, min(sizeof(tmp), *sa_size));
+ *sa_size = sizeof(tmp);
+}
+
+static void
+ipv6_h_addr2sockaddr(const char *addr,
+ struct sockaddr *sa,
+ krb5_socklen_t *sa_size,
+ int port)
+{
+ struct sockaddr_in6 tmp;
+
+ memset (&tmp, 0, sizeof(tmp));
+ tmp.sin6_family = AF_INET6;
+ tmp.sin6_port = port;
+ tmp.sin6_addr = *((const struct in6_addr *)addr);
+ memcpy(sa, &tmp, min(sizeof(tmp), *sa_size));
+ *sa_size = sizeof(tmp);
+}
+
+static krb5_error_code
+ipv6_h_addr2addr (const char *addr,
+ krb5_address *a)
+{
+ a->addr_type = KRB5_ADDRESS_INET6;
+ return krb5_data_copy(&a->address, addr, sizeof(struct in6_addr));
+}
+
+/*
+ *
+ */
+
+static krb5_boolean
+ipv6_uninteresting (const struct sockaddr *sa)
+{
+ const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sa;
+ const struct in6_addr *in6 = (const struct in6_addr *)&sin6->sin6_addr;
+
+ return
+ IN6_IS_ADDR_LINKLOCAL(in6)
+ || IN6_IS_ADDR_V4COMPAT(in6);
+}
+
+static void
+ipv6_anyaddr (struct sockaddr *sa, krb5_socklen_t *sa_size, int port)
+{
+ struct sockaddr_in6 tmp;
+
+ memset (&tmp, 0, sizeof(tmp));
+ tmp.sin6_family = AF_INET6;
+ tmp.sin6_port = port;
+ tmp.sin6_addr = in6addr_any;
+ *sa_size = sizeof(tmp);
+}
+
+static int
+ipv6_print_addr (const krb5_address *addr, char *str, size_t len)
+{
+ char buf[128], buf2[3];
+#ifdef HAVE_INET_NTOP
+ if(inet_ntop(AF_INET6, addr->address.data, buf, sizeof(buf)) == NULL)
+#endif
+ {
+ /* XXX this is pretty ugly, but better than abort() */
+ int i;
+ unsigned char *p = addr->address.data;
+ buf[0] = '\0';
+ for(i = 0; i < addr->address.length; i++) {
+ snprintf(buf2, sizeof(buf2), "%02x", p[i]);
+ if(i > 0 && (i & 1) == 0)
+ strlcat(buf, ":", sizeof(buf));
+ strlcat(buf, buf2, sizeof(buf));
+ }
+ }
+ return snprintf(str, len, "IPv6:%s", buf);
+}
+
+static int
+ipv6_parse_addr (krb5_context context, const char *address, krb5_address *addr)
+{
+ int ret;
+ struct in6_addr in6;
+ const char *p;
+
+ p = strchr(address, ':');
+ if(p) {
+ p++;
+ if(strncasecmp(address, "ip6:", p - address) == 0 ||
+ strncasecmp(address, "ipv6:", p - address) == 0 ||
+ strncasecmp(address, "inet6:", p - address) == 0)
+ address = p;
+ }
+
+ ret = inet_pton(AF_INET6, address, &in6.s6_addr);
+ if(ret == 1) {
+ addr->addr_type = KRB5_ADDRESS_INET6;
+ ret = krb5_data_alloc(&addr->address, sizeof(in6.s6_addr));
+ if (ret)
+ return -1;
+ memcpy(addr->address.data, in6.s6_addr, sizeof(in6.s6_addr));
+ return 0;
+ }
+ return -1;
+}
+
+static int
+ipv6_mask_boundary(krb5_context context, const krb5_address *inaddr,
+ unsigned long len, krb5_address *low, krb5_address *high)
+{
+ struct in6_addr addr, laddr, haddr;
+ uint32_t m;
+ int i, sub_len;
+
+ if (len > 128) {
+ krb5_set_error_message(context, KRB5_PROG_ATYPE_NOSUPP,
+ N_("IPv6 prefix too large (%ld)", "length"), len);
+ return KRB5_PROG_ATYPE_NOSUPP;
+ }
+
+ if (inaddr->address.length != sizeof(addr)) {
+ krb5_set_error_message(context, KRB5_PROG_ATYPE_NOSUPP,
+ N_("IPv6 addr bad length", ""));
+ return KRB5_PROG_ATYPE_NOSUPP;
+ }
+
+ memcpy(&addr, inaddr->address.data, inaddr->address.length);
+
+ for (i = 0; i < 16; i++) {
+ sub_len = min(8, len);
+
+ m = 0xff << (8 - sub_len);
+
+ laddr.s6_addr[i] = addr.s6_addr[i] & m;
+ haddr.s6_addr[i] = (addr.s6_addr[i] & m) | ~m;
+
+ if (len > 8)
+ len -= 8;
+ else
+ len = 0;
+ }
+
+ low->addr_type = KRB5_ADDRESS_INET6;
+ if (krb5_data_alloc(&low->address, sizeof(laddr.s6_addr)) != 0)
+ return -1;
+ memcpy(low->address.data, laddr.s6_addr, sizeof(laddr.s6_addr));
+
+ high->addr_type = KRB5_ADDRESS_INET6;
+ if (krb5_data_alloc(&high->address, sizeof(haddr.s6_addr)) != 0) {
+ krb5_free_address(context, low);
+ return -1;
+ }
+ memcpy(high->address.data, haddr.s6_addr, sizeof(haddr.s6_addr));
+
+ return 0;
+}
+
+#endif /* IPv6 */
+
+#ifndef HEIMDAL_SMALLER
+
+/*
+ * table
+ */
+
+#define KRB5_ADDRESS_ARANGE (-100)
+
+struct arange {
+ krb5_address low;
+ krb5_address high;
+};
+
+static int
+arange_parse_addr (krb5_context context,
+ const char *address, krb5_address *addr)
+{
+ char buf[1024], *p;
+ krb5_address low0, high0;
+ struct arange *a;
+ krb5_error_code ret;
+
+ if(strncasecmp(address, "RANGE:", 6) != 0)
+ return -1;
+
+ address += 6;
+
+ p = strrchr(address, '/');
+ if (p) {
+ krb5_addresses addrmask;
+ char *q;
+ long num;
+
+ if (strlcpy(buf, address, sizeof(buf)) > sizeof(buf))
+ return -1;
+ buf[p - address] = '\0';
+ ret = krb5_parse_address(context, buf, &addrmask);
+ if (ret)
+ return ret;
+ if(addrmask.len != 1) {
+ krb5_free_addresses(context, &addrmask);
+ return -1;
+ }
+
+ address += p - address + 1;
+
+ num = strtol(address, &q, 10);
+ if (q == address || *q != '\0' || num < 0) {
+ krb5_free_addresses(context, &addrmask);
+ return -1;
+ }
+
+ ret = krb5_address_prefixlen_boundary(context, &addrmask.val[0], num,
+ &low0, &high0);
+ krb5_free_addresses(context, &addrmask);
+ if (ret)
+ return ret;
+
+ } else {
+ krb5_addresses low, high;
+
+ strsep_copy(&address, "-", buf, sizeof(buf));
+ ret = krb5_parse_address(context, buf, &low);
+ if(ret)
+ return ret;
+ if(low.len != 1) {
+ krb5_free_addresses(context, &low);
+ return -1;
+ }
+
+ strsep_copy(&address, "-", buf, sizeof(buf));
+ ret = krb5_parse_address(context, buf, &high);
+ if(ret) {
+ krb5_free_addresses(context, &low);
+ return ret;
+ }
+
+ if(high.len != 1 && high.val[0].addr_type != low.val[0].addr_type) {
+ krb5_free_addresses(context, &low);
+ krb5_free_addresses(context, &high);
+ return -1;
+ }
+
+ ret = krb5_copy_address(context, &high.val[0], &high0);
+ if (ret == 0) {
+ ret = krb5_copy_address(context, &low.val[0], &low0);
+ if (ret)
+ krb5_free_address(context, &high0);
+ }
+ krb5_free_addresses(context, &low);
+ krb5_free_addresses(context, &high);
+ if (ret)
+ return ret;
+ }
+
+ krb5_data_alloc(&addr->address, sizeof(*a));
+ addr->addr_type = KRB5_ADDRESS_ARANGE;
+ a = addr->address.data;
+
+ if(krb5_address_order(context, &low0, &high0) < 0) {
+ a->low = low0;
+ a->high = high0;
+ } else {
+ a->low = high0;
+ a->high = low0;
+ }
+ return 0;
+}
+
+static int
+arange_free (krb5_context context, krb5_address *addr)
+{
+ struct arange *a;
+ a = addr->address.data;
+ krb5_free_address(context, &a->low);
+ krb5_free_address(context, &a->high);
+ krb5_data_free(&addr->address);
+ return 0;
+}
+
+
+static int
+arange_copy (krb5_context context, const krb5_address *inaddr,
+ krb5_address *outaddr)
+{
+ krb5_error_code ret;
+ struct arange *i, *o;
+
+ outaddr->addr_type = KRB5_ADDRESS_ARANGE;
+ ret = krb5_data_alloc(&outaddr->address, sizeof(*o));
+ if(ret)
+ return ret;
+ i = inaddr->address.data;
+ o = outaddr->address.data;
+ ret = krb5_copy_address(context, &i->low, &o->low);
+ if(ret) {
+ krb5_data_free(&outaddr->address);
+ return ret;
+ }
+ ret = krb5_copy_address(context, &i->high, &o->high);
+ if(ret) {
+ krb5_free_address(context, &o->low);
+ krb5_data_free(&outaddr->address);
+ return ret;
+ }
+ return 0;
+}
+
+static int
+arange_print_addr (const krb5_address *addr, char *str, size_t len)
+{
+ struct arange *a;
+ krb5_error_code ret;
+ size_t l, size, ret_len;
+
+ a = addr->address.data;
+
+ l = strlcpy(str, "RANGE:", len);
+ ret_len = l;
+ if (l > len)
+ l = len;
+ size = l;
+
+ ret = krb5_print_address (&a->low, str + size, len - size, &l);
+ if (ret)
+ return ret;
+ ret_len += l;
+ if (len - size > l)
+ size += l;
+ else
+ size = len;
+
+ l = strlcat(str + size, "-", len - size);
+ ret_len += l;
+ if (len - size > l)
+ size += l;
+ else
+ size = len;
+
+ ret = krb5_print_address (&a->high, str + size, len - size, &l);
+ if (ret)
+ return ret;
+ ret_len += l;
+
+ return ret_len;
+}
+
+static int
+arange_order_addr(krb5_context context,
+ const krb5_address *addr1,
+ const krb5_address *addr2)
+{
+ int tmp1, tmp2, sign;
+ struct arange *a;
+ const krb5_address *a2;
+
+ if(addr1->addr_type == KRB5_ADDRESS_ARANGE) {
+ a = addr1->address.data;
+ a2 = addr2;
+ sign = 1;
+ } else if(addr2->addr_type == KRB5_ADDRESS_ARANGE) {
+ a = addr2->address.data;
+ a2 = addr1;
+ sign = -1;
+ } else
+ abort();
+
+ if(a2->addr_type == KRB5_ADDRESS_ARANGE) {
+ struct arange *b = a2->address.data;
+ tmp1 = krb5_address_order(context, &a->low, &b->low);
+ if(tmp1 != 0)
+ return sign * tmp1;
+ return sign * krb5_address_order(context, &a->high, &b->high);
+ } else if(a2->addr_type == a->low.addr_type) {
+ tmp1 = krb5_address_order(context, &a->low, a2);
+ if(tmp1 > 0)
+ return sign;
+ tmp2 = krb5_address_order(context, &a->high, a2);
+ if(tmp2 < 0)
+ return -sign;
+ return 0;
+ } else {
+ return sign * (addr1->addr_type - addr2->addr_type);
+ }
+}
+
+#endif /* HEIMDAL_SMALLER */
+
+static int
+addrport_print_addr (const krb5_address *addr, char *str, size_t len)
+{
+ krb5_error_code ret;
+ krb5_address addr1, addr2;
+ uint16_t port = 0;
+ size_t ret_len = 0, l, size = 0;
+ krb5_storage *sp;
+
+ sp = krb5_storage_from_data((krb5_data*)rk_UNCONST(&addr->address));
+ /* for totally obscure reasons, these are not in network byteorder */
+ krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_LE);
+
+ krb5_storage_seek(sp, 2, SEEK_CUR); /* skip first two bytes */
+ krb5_ret_address(sp, &addr1);
+
+ krb5_storage_seek(sp, 2, SEEK_CUR); /* skip two bytes */
+ krb5_ret_address(sp, &addr2);
+ krb5_storage_free(sp);
+ if(addr2.addr_type == KRB5_ADDRESS_IPPORT && addr2.address.length == 2) {
+ unsigned long value;
+ _krb5_get_int(addr2.address.data, &value, 2);
+ port = value;
+ }
+ l = strlcpy(str, "ADDRPORT:", len);
+ ret_len += l;
+ if (len > l)
+ size += l;
+ else
+ size = len;
+
+ ret = krb5_print_address(&addr1, str + size, len - size, &l);
+ if (ret)
+ return ret;
+ ret_len += l;
+ if (len - size > l)
+ size += l;
+ else
+ size = len;
+
+ ret = snprintf(str + size, len - size, ",PORT=%u", port);
+ if (ret < 0)
+ return EINVAL;
+ ret_len += ret;
+ return ret_len;
+}
+
+static struct addr_operations at[] = {
+ {AF_INET, KRB5_ADDRESS_INET, sizeof(struct sockaddr_in),
+ ipv4_sockaddr2addr,
+ ipv4_sockaddr2port,
+ ipv4_addr2sockaddr,
+ ipv4_h_addr2sockaddr,
+ ipv4_h_addr2addr,
+ ipv4_uninteresting, ipv4_anyaddr, ipv4_print_addr, ipv4_parse_addr,
+ NULL, NULL, NULL, ipv4_mask_boundary },
+#ifdef HAVE_IPV6
+ {AF_INET6, KRB5_ADDRESS_INET6, sizeof(struct sockaddr_in6),
+ ipv6_sockaddr2addr,
+ ipv6_sockaddr2port,
+ ipv6_addr2sockaddr,
+ ipv6_h_addr2sockaddr,
+ ipv6_h_addr2addr,
+ ipv6_uninteresting, ipv6_anyaddr, ipv6_print_addr, ipv6_parse_addr,
+ NULL, NULL, NULL, ipv6_mask_boundary } ,
+#endif
+#ifndef HEIMDAL_SMALLER
+ /* fake address type */
+ {KRB5_ADDRESS_ARANGE, KRB5_ADDRESS_ARANGE, sizeof(struct arange),
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ arange_print_addr, arange_parse_addr,
+ arange_order_addr, arange_free, arange_copy },
+#endif
+ {KRB5_ADDRESS_ADDRPORT, KRB5_ADDRESS_ADDRPORT, 0,
+ NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, addrport_print_addr, NULL, NULL, NULL, NULL }
+};
+
+static int num_addrs = sizeof(at) / sizeof(at[0]);
+
+static size_t max_sockaddr_size = 0;
+
+/*
+ * generic functions
+ */
+
+static struct addr_operations *
+find_af(int af)
+{
+ struct addr_operations *a;
+
+ for (a = at; a < at + num_addrs; ++a)
+ if (af == a->af)
+ return a;
+ return NULL;
+}
+
+static struct addr_operations *
+find_atype(int atype)
+{
+ struct addr_operations *a;
+
+ for (a = at; a < at + num_addrs; ++a)
+ if (atype == a->atype)
+ return a;
+ return NULL;
+}
+
+/**
+ * krb5_sockaddr2address stores a address a "struct sockaddr" sa in
+ * the krb5_address addr.
+ *
+ * @param context a Keberos context
+ * @param sa a struct sockaddr to extract the address from
+ * @param addr an Kerberos 5 address to store the address in.
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_address
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_sockaddr2address (krb5_context context,
+ const struct sockaddr *sa, krb5_address *addr)
+{
+ struct addr_operations *a = find_af(sa->sa_family);
+ if (a == NULL) {
+ krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP,
+ N_("Address family %d not supported", ""),
+ sa->sa_family);
+ return KRB5_PROG_ATYPE_NOSUPP;
+ }
+ return (*a->sockaddr2addr)(sa, addr);
+}
+
+/**
+ * krb5_sockaddr2port extracts a port (if possible) from a "struct
+ * sockaddr.
+ *
+ * @param context a Keberos context
+ * @param sa a struct sockaddr to extract the port from
+ * @param port a pointer to an int16_t store the port in.
+ *
+ * @return Return an error code or 0. Will return
+ * KRB5_PROG_ATYPE_NOSUPP in case address type is not supported.
+ *
+ * @ingroup krb5_address
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_sockaddr2port (krb5_context context,
+ const struct sockaddr *sa, int16_t *port)
+{
+ struct addr_operations *a = find_af(sa->sa_family);
+ if (a == NULL) {
+ krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP,
+ N_("Address family %d not supported", ""),
+ sa->sa_family);
+ return KRB5_PROG_ATYPE_NOSUPP;
+ }
+ return (*a->sockaddr2port)(sa, port);
+}
+
+/**
+ * krb5_addr2sockaddr sets the "struct sockaddr sockaddr" from addr
+ * and port. The argument sa_size should initially contain the size of
+ * the sa and after the call, it will contain the actual length of the
+ * address. In case of the sa is too small to fit the whole address,
+ * the up to *sa_size will be stored, and then *sa_size will be set to
+ * the required length.
+ *
+ * @param context a Keberos context
+ * @param addr the address to copy the from
+ * @param sa the struct sockaddr that will be filled in
+ * @param sa_size pointer to length of sa, and after the call, it will
+ * contain the actual length of the address.
+ * @param port set port in sa.
+ *
+ * @return Return an error code or 0. Will return
+ * KRB5_PROG_ATYPE_NOSUPP in case address type is not supported.
+ *
+ * @ingroup krb5_address
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_addr2sockaddr (krb5_context context,
+ const krb5_address *addr,
+ struct sockaddr *sa,
+ krb5_socklen_t *sa_size,
+ int port)
+{
+ struct addr_operations *a = find_atype(addr->addr_type);
+
+ if (a == NULL) {
+ krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP,
+ N_("Address type %d not supported",
+ "krb5_address type"),
+ addr->addr_type);
+ return KRB5_PROG_ATYPE_NOSUPP;
+ }
+ if (a->addr2sockaddr == NULL) {
+ krb5_set_error_message (context,
+ KRB5_PROG_ATYPE_NOSUPP,
+ N_("Can't convert address type %d to sockaddr", ""),
+ addr->addr_type);
+ return KRB5_PROG_ATYPE_NOSUPP;
+ }
+ (*a->addr2sockaddr)(addr, sa, sa_size, port);
+ return 0;
+}
+
+/**
+ * krb5_max_sockaddr_size returns the max size of the .Li struct
+ * sockaddr that the Kerberos library will return.
+ *
+ * @return Return an size_t of the maximum struct sockaddr.
+ *
+ * @ingroup krb5_address
+ */
+
+size_t KRB5_LIB_FUNCTION
+krb5_max_sockaddr_size (void)
+{
+ if (max_sockaddr_size == 0) {
+ struct addr_operations *a;
+
+ for(a = at; a < at + num_addrs; ++a)
+ max_sockaddr_size = max(max_sockaddr_size, a->max_sockaddr_size);
+ }
+ return max_sockaddr_size;
+}
+
+/**
+ * krb5_sockaddr_uninteresting returns TRUE for all .Fa sa that the
+ * kerberos library thinks are uninteresting. One example are link
+ * local addresses.
+ *
+ * @param sa pointer to struct sockaddr that might be interesting.
+ *
+ * @return Return a non zero for uninteresting addresses.
+ *
+ * @ingroup krb5_address
+ */
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_sockaddr_uninteresting(const struct sockaddr *sa)
+{
+ struct addr_operations *a = find_af(sa->sa_family);
+ if (a == NULL || a->uninteresting == NULL)
+ return TRUE;
+ return (*a->uninteresting)(sa);
+}
+
+/**
+ * krb5_h_addr2sockaddr initializes a "struct sockaddr sa" from af and
+ * the "struct hostent" (see gethostbyname(3) ) h_addr_list
+ * component. The argument sa_size should initially contain the size
+ * of the sa, and after the call, it will contain the actual length of
+ * the address.
+ *
+ * @param context a Keberos context
+ * @param af addresses
+ * @param addr address
+ * @param sa returned struct sockaddr
+ * @param sa_size size of sa
+ * @param port port to set in sa.
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_address
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_h_addr2sockaddr (krb5_context context,
+ int af,
+ const char *addr, struct sockaddr *sa,
+ krb5_socklen_t *sa_size,
+ int port)
+{
+ struct addr_operations *a = find_af(af);
+ if (a == NULL) {
+ krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP,
+ "Address family %d not supported", af);
+ return KRB5_PROG_ATYPE_NOSUPP;
+ }
+ (*a->h_addr2sockaddr)(addr, sa, sa_size, port);
+ return 0;
+}
+
+/**
+ * krb5_h_addr2addr works like krb5_h_addr2sockaddr with the exception
+ * that it operates on a krb5_address instead of a struct sockaddr.
+ *
+ * @param context a Keberos context
+ * @param af address family
+ * @param haddr host address from struct hostent.
+ * @param addr returned krb5_address.
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_address
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_h_addr2addr (krb5_context context,
+ int af,
+ const char *haddr, krb5_address *addr)
+{
+ struct addr_operations *a = find_af(af);
+ if (a == NULL) {
+ krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP,
+ N_("Address family %d not supported", ""), af);
+ return KRB5_PROG_ATYPE_NOSUPP;
+ }
+ return (*a->h_addr2addr)(haddr, addr);
+}
+
+/**
+ * krb5_anyaddr fills in a "struct sockaddr sa" that can be used to
+ * bind(2) to. The argument sa_size should initially contain the size
+ * of the sa, and after the call, it will contain the actual length
+ * of the address.
+ *
+ * @param context a Keberos context
+ * @param af address family
+ * @param sa sockaddr
+ * @param sa_size lenght of sa.
+ * @param port for to fill into sa.
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_address
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_anyaddr (krb5_context context,
+ int af,
+ struct sockaddr *sa,
+ krb5_socklen_t *sa_size,
+ int port)
+{
+ struct addr_operations *a = find_af (af);
+
+ if (a == NULL) {
+ krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP,
+ N_("Address family %d not supported", ""), af);
+ return KRB5_PROG_ATYPE_NOSUPP;
+ }
+
+ (*a->anyaddr)(sa, sa_size, port);
+ return 0;
+}
+
+/**
+ * krb5_print_address prints the address in addr to the string string
+ * that have the length len. If ret_len is not NULL, it will be filled
+ * with the length of the string if size were unlimited (not including
+ * the final NUL) .
+ *
+ * @param addr address to be printed
+ * @param str pointer string to print the address into
+ * @param len length that will fit into area pointed to by "str".
+ * @param ret_len return length the str.
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_address
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_print_address (const krb5_address *addr,
+ char *str, size_t len, size_t *ret_len)
+{
+ struct addr_operations *a = find_atype(addr->addr_type);
+ int ret;
+
+ if (a == NULL || a->print_addr == NULL) {
+ char *s;
+ int l;
+ int i;
+
+ s = str;
+ l = snprintf(s, len, "TYPE_%d:", addr->addr_type);
+ if (l < 0 || l >= len)
+ return EINVAL;
+ s += l;
+ len -= l;
+ for(i = 0; i < addr->address.length; i++) {
+ l = snprintf(s, len, "%02x", ((char*)addr->address.data)[i]);
+ if (l < 0 || l >= len)
+ return EINVAL;
+ len -= l;
+ s += l;
+ }
+ if(ret_len != NULL)
+ *ret_len = s - str;
+ return 0;
+ }
+ ret = (*a->print_addr)(addr, str, len);
+ if (ret < 0)
+ return EINVAL;
+ if(ret_len != NULL)
+ *ret_len = ret;
+ return 0;
+}
+
+/**
+ * krb5_parse_address returns the resolved hostname in string to the
+ * krb5_addresses addresses .
+ *
+ * @param context a Keberos context
+ * @param string
+ * @param addresses
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_address
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_parse_address(krb5_context context,
+ const char *string,
+ krb5_addresses *addresses)
+{
+ int i, n;
+ struct addrinfo *ai, *a;
+ int error;
+ int save_errno;
+
+ addresses->len = 0;
+ addresses->val = NULL;
+
+ for(i = 0; i < num_addrs; i++) {
+ if(at[i].parse_addr) {
+ krb5_address addr;
+ if((*at[i].parse_addr)(context, string, &addr) == 0) {
+ ALLOC_SEQ(addresses, 1);
+ if (addresses->val == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ addresses->val[0] = addr;
+ return 0;
+ }
+ }
+ }
+
+ error = getaddrinfo (string, NULL, NULL, &ai);
+ if (error) {
+ krb5_error_code ret2;
+ save_errno = errno;
+ ret2 = krb5_eai_to_heim_errno(error, save_errno);
+ krb5_set_error_message (context, ret2, "%s: %s",
+ string, gai_strerror(error));
+ return ret2;
+ }
+
+ n = 0;
+ for (a = ai; a != NULL; a = a->ai_next)
+ ++n;
+
+ ALLOC_SEQ(addresses, n);
+ if (addresses->val == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ freeaddrinfo(ai);
+ return ENOMEM;
+ }
+
+ addresses->len = 0;
+ for (a = ai, i = 0; a != NULL; a = a->ai_next) {
+ if (krb5_sockaddr2address (context, ai->ai_addr, &addresses->val[i]))
+ continue;
+ if(krb5_address_search(context, &addresses->val[i], addresses))
+ continue;
+ addresses->len = i;
+ i++;
+ }
+ freeaddrinfo (ai);
+ return 0;
+}
+
+/**
+ * krb5_address_order compares the addresses addr1 and addr2 so that
+ * it can be used for sorting addresses. If the addresses are the same
+ * address krb5_address_order will return 0. Behavies like memcmp(2).
+ *
+ * @param context a Keberos context
+ * @param addr1 krb5_address to compare
+ * @param addr2 krb5_address to compare
+ *
+ * @return < 0 if address addr1 in "less" then addr2. 0 if addr1 and
+ * addr2 is the same address, > 0 if addr2 is "less" then addr1.
+ *
+ * @ingroup krb5_address
+ */
+
+int KRB5_LIB_FUNCTION
+krb5_address_order(krb5_context context,
+ const krb5_address *addr1,
+ const krb5_address *addr2)
+{
+ /* this sucks; what if both addresses have order functions, which
+ should we call? this works for now, though */
+ struct addr_operations *a;
+ a = find_atype(addr1->addr_type);
+ if(a == NULL) {
+ krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP,
+ N_("Address family %d not supported", ""),
+ addr1->addr_type);
+ return KRB5_PROG_ATYPE_NOSUPP;
+ }
+ if(a->order_addr != NULL)
+ return (*a->order_addr)(context, addr1, addr2);
+ a = find_atype(addr2->addr_type);
+ if(a == NULL) {
+ krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP,
+ N_("Address family %d not supported", ""),
+ addr2->addr_type);
+ return KRB5_PROG_ATYPE_NOSUPP;
+ }
+ if(a->order_addr != NULL)
+ return (*a->order_addr)(context, addr1, addr2);
+
+ if(addr1->addr_type != addr2->addr_type)
+ return addr1->addr_type - addr2->addr_type;
+ if(addr1->address.length != addr2->address.length)
+ return addr1->address.length - addr2->address.length;
+ return memcmp (addr1->address.data,
+ addr2->address.data,
+ addr1->address.length);
+}
+
+/**
+ * krb5_address_compare compares the addresses addr1 and addr2.
+ * Returns TRUE if the two addresses are the same.
+ *
+ * @param context a Keberos context
+ * @param addr1 address to compare
+ * @param addr2 address to compare
+ *
+ * @return Return an TRUE is the address are the same FALSE if not
+ *
+ * @ingroup krb5_address
+ */
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_address_compare(krb5_context context,
+ const krb5_address *addr1,
+ const krb5_address *addr2)
+{
+ return krb5_address_order (context, addr1, addr2) == 0;
+}
+
+/**
+ * krb5_address_search checks if the address addr is a member of the
+ * address set list addrlist .
+ *
+ * @param context a Keberos context.
+ * @param addr address to search for.
+ * @param addrlist list of addresses to look in for addr.
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_address
+ */
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_address_search(krb5_context context,
+ const krb5_address *addr,
+ const krb5_addresses *addrlist)
+{
+ int i;
+
+ for (i = 0; i < addrlist->len; ++i)
+ if (krb5_address_compare (context, addr, &addrlist->val[i]))
+ return TRUE;
+ return FALSE;
+}
+
+/**
+ * krb5_free_address frees the data stored in the address that is
+ * alloced with any of the krb5_address functions.
+ *
+ * @param context a Keberos context
+ * @param address addresss to be freed.
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_address
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_free_address(krb5_context context,
+ krb5_address *address)
+{
+ struct addr_operations *a = find_atype (address->addr_type);
+ if(a != NULL && a->free_addr != NULL)
+ return (*a->free_addr)(context, address);
+ krb5_data_free (&address->address);
+ memset(address, 0, sizeof(*address));
+ return 0;
+}
+
+/**
+ * krb5_free_addresses frees the data stored in the address that is
+ * alloced with any of the krb5_address functions.
+ *
+ * @param context a Keberos context
+ * @param addresses addressses to be freed.
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_address
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_free_addresses(krb5_context context,
+ krb5_addresses *addresses)
+{
+ int i;
+ for(i = 0; i < addresses->len; i++)
+ krb5_free_address(context, &addresses->val[i]);
+ free(addresses->val);
+ addresses->len = 0;
+ addresses->val = NULL;
+ return 0;
+}
+
+/**
+ * krb5_copy_address copies the content of address
+ * inaddr to outaddr.
+ *
+ * @param context a Keberos context
+ * @param inaddr pointer to source address
+ * @param outaddr pointer to destination address
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_address
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_copy_address(krb5_context context,
+ const krb5_address *inaddr,
+ krb5_address *outaddr)
+{
+ struct addr_operations *a = find_af (inaddr->addr_type);
+ if(a != NULL && a->copy_addr != NULL)
+ return (*a->copy_addr)(context, inaddr, outaddr);
+ return copy_HostAddress(inaddr, outaddr);
+}
+
+/**
+ * krb5_copy_addresses copies the content of addresses
+ * inaddr to outaddr.
+ *
+ * @param context a Keberos context
+ * @param inaddr pointer to source addresses
+ * @param outaddr pointer to destination addresses
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_address
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_copy_addresses(krb5_context context,
+ const krb5_addresses *inaddr,
+ krb5_addresses *outaddr)
+{
+ int i;
+ ALLOC_SEQ(outaddr, inaddr->len);
+ if(inaddr->len > 0 && outaddr->val == NULL)
+ return ENOMEM;
+ for(i = 0; i < inaddr->len; i++)
+ krb5_copy_address(context, &inaddr->val[i], &outaddr->val[i]);
+ return 0;
+}
+
+/**
+ * krb5_append_addresses adds the set of addresses in source to
+ * dest. While copying the addresses, duplicates are also sorted out.
+ *
+ * @param context a Keberos context
+ * @param dest destination of copy operation
+ * @param source adresses that are going to be added to dest
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_address
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_append_addresses(krb5_context context,
+ krb5_addresses *dest,
+ const krb5_addresses *source)
+{
+ krb5_address *tmp;
+ krb5_error_code ret;
+ int i;
+ if(source->len > 0) {
+ tmp = realloc(dest->val, (dest->len + source->len) * sizeof(*tmp));
+ if(tmp == NULL) {
+ krb5_set_error_message (context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ dest->val = tmp;
+ for(i = 0; i < source->len; i++) {
+ /* skip duplicates */
+ if(krb5_address_search(context, &source->val[i], dest))
+ continue;
+ ret = krb5_copy_address(context,
+ &source->val[i],
+ &dest->val[dest->len]);
+ if(ret)
+ return ret;
+ dest->len++;
+ }
+ }
+ return 0;
+}
+
+/**
+ * Create an address of type KRB5_ADDRESS_ADDRPORT from (addr, port)
+ *
+ * @param context a Keberos context
+ * @param res built address from addr/port
+ * @param addr address to use
+ * @param port port to use
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_address
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_make_addrport (krb5_context context,
+ krb5_address **res, const krb5_address *addr, int16_t port)
+{
+ krb5_error_code ret;
+ size_t len = addr->address.length + 2 + 4 * 4;
+ u_char *p;
+
+ *res = malloc (sizeof(**res));
+ if (*res == NULL) {
+ krb5_set_error_message (context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ (*res)->addr_type = KRB5_ADDRESS_ADDRPORT;
+ ret = krb5_data_alloc (&(*res)->address, len);
+ if (ret) {
+ krb5_set_error_message (context, ret,
+ N_("malloc: out of memory", ""));
+ free (*res);
+ *res = NULL;
+ return ret;
+ }
+ p = (*res)->address.data;
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = (addr->addr_type ) & 0xFF;
+ *p++ = (addr->addr_type >> 8) & 0xFF;
+
+ *p++ = (addr->address.length ) & 0xFF;
+ *p++ = (addr->address.length >> 8) & 0xFF;
+ *p++ = (addr->address.length >> 16) & 0xFF;
+ *p++ = (addr->address.length >> 24) & 0xFF;
+
+ memcpy (p, addr->address.data, addr->address.length);
+ p += addr->address.length;
+
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = (KRB5_ADDRESS_IPPORT ) & 0xFF;
+ *p++ = (KRB5_ADDRESS_IPPORT >> 8) & 0xFF;
+
+ *p++ = (2 ) & 0xFF;
+ *p++ = (2 >> 8) & 0xFF;
+ *p++ = (2 >> 16) & 0xFF;
+ *p++ = (2 >> 24) & 0xFF;
+
+ memcpy (p, &port, 2);
+ p += 2;
+
+ return 0;
+}
+
+/**
+ * Calculate the boundary addresses of `inaddr'/`prefixlen' and store
+ * them in `low' and `high'.
+ *
+ * @param context a Keberos context
+ * @param inaddr address in prefixlen that the bondery searched
+ * @param prefixlen width of boundery
+ * @param low lowest address
+ * @param high highest address
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_address
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_address_prefixlen_boundary(krb5_context context,
+ const krb5_address *inaddr,
+ unsigned long prefixlen,
+ krb5_address *low,
+ krb5_address *high)
+{
+ struct addr_operations *a = find_atype (inaddr->addr_type);
+ if(a != NULL && a->mask_boundary != NULL)
+ return (*a->mask_boundary)(context, inaddr, prefixlen, low, high);
+ krb5_set_error_message(context, KRB5_PROG_ATYPE_NOSUPP,
+ N_("Address family %d doesn't support "
+ "address mask operation", ""),
+ inaddr->addr_type);
+ return KRB5_PROG_ATYPE_NOSUPP;
+}
diff --git a/source4/heimdal/lib/krb5/appdefault.c b/source4/heimdal/lib/krb5/appdefault.c
new file mode 100644
index 0000000000..d49fc4997a
--- /dev/null
+++ b/source4/heimdal/lib/krb5/appdefault.c
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2000 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+void KRB5_LIB_FUNCTION
+krb5_appdefault_boolean(krb5_context context, const char *appname,
+ krb5_const_realm realm, const char *option,
+ krb5_boolean def_val, krb5_boolean *ret_val)
+{
+
+ if(appname == NULL)
+ appname = getprogname();
+
+ def_val = krb5_config_get_bool_default(context, NULL, def_val,
+ "libdefaults", option, NULL);
+ if(realm != NULL)
+ def_val = krb5_config_get_bool_default(context, NULL, def_val,
+ "realms", realm, option, NULL);
+
+ def_val = krb5_config_get_bool_default(context, NULL, def_val,
+ "appdefaults",
+ option,
+ NULL);
+ if(realm != NULL)
+ def_val = krb5_config_get_bool_default(context, NULL, def_val,
+ "appdefaults",
+ realm,
+ option,
+ NULL);
+ if(appname != NULL) {
+ def_val = krb5_config_get_bool_default(context, NULL, def_val,
+ "appdefaults",
+ appname,
+ option,
+ NULL);
+ if(realm != NULL)
+ def_val = krb5_config_get_bool_default(context, NULL, def_val,
+ "appdefaults",
+ appname,
+ realm,
+ option,
+ NULL);
+ }
+ *ret_val = def_val;
+}
+
+void KRB5_LIB_FUNCTION
+krb5_appdefault_string(krb5_context context, const char *appname,
+ krb5_const_realm realm, const char *option,
+ const char *def_val, char **ret_val)
+{
+ if(appname == NULL)
+ appname = getprogname();
+
+ def_val = krb5_config_get_string_default(context, NULL, def_val,
+ "libdefaults", option, NULL);
+ if(realm != NULL)
+ def_val = krb5_config_get_string_default(context, NULL, def_val,
+ "realms", realm, option, NULL);
+
+ def_val = krb5_config_get_string_default(context, NULL, def_val,
+ "appdefaults",
+ option,
+ NULL);
+ if(realm != NULL)
+ def_val = krb5_config_get_string_default(context, NULL, def_val,
+ "appdefaults",
+ realm,
+ option,
+ NULL);
+ if(appname != NULL) {
+ def_val = krb5_config_get_string_default(context, NULL, def_val,
+ "appdefaults",
+ appname,
+ option,
+ NULL);
+ if(realm != NULL)
+ def_val = krb5_config_get_string_default(context, NULL, def_val,
+ "appdefaults",
+ appname,
+ realm,
+ option,
+ NULL);
+ }
+ if(def_val != NULL)
+ *ret_val = strdup(def_val);
+ else
+ *ret_val = NULL;
+}
+
+void KRB5_LIB_FUNCTION
+krb5_appdefault_time(krb5_context context, const char *appname,
+ krb5_const_realm realm, const char *option,
+ time_t def_val, time_t *ret_val)
+{
+ krb5_deltat t;
+ char *val;
+
+ krb5_appdefault_string(context, appname, realm, option, NULL, &val);
+ if (val == NULL) {
+ *ret_val = def_val;
+ return;
+ }
+ if (krb5_string_to_deltat(val, &t))
+ *ret_val = def_val;
+ else
+ *ret_val = t;
+ free(val);
+}
diff --git a/source4/heimdal/lib/krb5/asn1_glue.c b/source4/heimdal/lib/krb5/asn1_glue.c
new file mode 100644
index 0000000000..cb86c324fb
--- /dev/null
+++ b/source4/heimdal/lib/krb5/asn1_glue.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 1997 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ *
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+krb5_error_code KRB5_LIB_FUNCTION
+_krb5_principal2principalname (PrincipalName *p,
+ const krb5_principal from)
+{
+ return copy_PrincipalName(&from->name, p);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+_krb5_principalname2krb5_principal (krb5_context context,
+ krb5_principal *principal,
+ const PrincipalName from,
+ const Realm realm)
+{
+ krb5_principal p = malloc(sizeof(*p));
+ if (p == NULL)
+ return ENOMEM;
+ copy_PrincipalName(&from, &p->name);
+ p->realm = strdup(realm);
+ if (p->realm == NULL)
+ return ENOMEM;
+ *principal = p;
+ return 0;
+}
diff --git a/source4/heimdal/lib/krb5/auth_context.c b/source4/heimdal/lib/krb5/auth_context.c
new file mode 100644
index 0000000000..66eccbbc07
--- /dev/null
+++ b/source4/heimdal/lib/krb5/auth_context.c
@@ -0,0 +1,521 @@
+/*
+ * Copyright (c) 1997 - 2002 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_init(krb5_context context,
+ krb5_auth_context *auth_context)
+{
+ krb5_auth_context p;
+
+ ALLOC(p, 1);
+ if(!p) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ memset(p, 0, sizeof(*p));
+ ALLOC(p->authenticator, 1);
+ if (!p->authenticator) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ free(p);
+ return ENOMEM;
+ }
+ memset (p->authenticator, 0, sizeof(*p->authenticator));
+ p->flags = KRB5_AUTH_CONTEXT_DO_TIME;
+
+ p->local_address = NULL;
+ p->remote_address = NULL;
+ p->local_port = 0;
+ p->remote_port = 0;
+ p->keytype = KEYTYPE_NULL;
+ p->cksumtype = CKSUMTYPE_NONE;
+ *auth_context = p;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_free(krb5_context context,
+ krb5_auth_context auth_context)
+{
+ if (auth_context != NULL) {
+ krb5_free_authenticator(context, &auth_context->authenticator);
+ if(auth_context->local_address){
+ free_HostAddress(auth_context->local_address);
+ free(auth_context->local_address);
+ }
+ if(auth_context->remote_address){
+ free_HostAddress(auth_context->remote_address);
+ free(auth_context->remote_address);
+ }
+ krb5_free_keyblock(context, auth_context->keyblock);
+ krb5_free_keyblock(context, auth_context->remote_subkey);
+ krb5_free_keyblock(context, auth_context->local_subkey);
+ free (auth_context);
+ }
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_setflags(krb5_context context,
+ krb5_auth_context auth_context,
+ int32_t flags)
+{
+ auth_context->flags = flags;
+ return 0;
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_getflags(krb5_context context,
+ krb5_auth_context auth_context,
+ int32_t *flags)
+{
+ *flags = auth_context->flags;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_addflags(krb5_context context,
+ krb5_auth_context auth_context,
+ int32_t addflags,
+ int32_t *flags)
+{
+ if (flags)
+ *flags = auth_context->flags;
+ auth_context->flags |= addflags;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_removeflags(krb5_context context,
+ krb5_auth_context auth_context,
+ int32_t removeflags,
+ int32_t *flags)
+{
+ if (flags)
+ *flags = auth_context->flags;
+ auth_context->flags &= ~removeflags;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_setaddrs(krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_address *local_addr,
+ krb5_address *remote_addr)
+{
+ if (local_addr) {
+ if (auth_context->local_address)
+ krb5_free_address (context, auth_context->local_address);
+ else
+ if ((auth_context->local_address = malloc(sizeof(krb5_address))) == NULL)
+ return ENOMEM;
+ krb5_copy_address(context, local_addr, auth_context->local_address);
+ }
+ if (remote_addr) {
+ if (auth_context->remote_address)
+ krb5_free_address (context, auth_context->remote_address);
+ else
+ if ((auth_context->remote_address = malloc(sizeof(krb5_address))) == NULL)
+ return ENOMEM;
+ krb5_copy_address(context, remote_addr, auth_context->remote_address);
+ }
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_genaddrs(krb5_context context,
+ krb5_auth_context auth_context,
+ int fd, int flags)
+{
+ krb5_error_code ret;
+ krb5_address local_k_address, remote_k_address;
+ krb5_address *lptr = NULL, *rptr = NULL;
+ struct sockaddr_storage ss_local, ss_remote;
+ struct sockaddr *local = (struct sockaddr *)&ss_local;
+ struct sockaddr *remote = (struct sockaddr *)&ss_remote;
+ socklen_t len;
+
+ if(flags & KRB5_AUTH_CONTEXT_GENERATE_LOCAL_ADDR) {
+ if (auth_context->local_address == NULL) {
+ len = sizeof(ss_local);
+ if(getsockname(fd, local, &len) < 0) {
+ ret = errno;
+ krb5_set_error_message(context, ret,
+ "getsockname: %s",
+ strerror(ret));
+ goto out;
+ }
+ ret = krb5_sockaddr2address (context, local, &local_k_address);
+ if(ret) goto out;
+ if(flags & KRB5_AUTH_CONTEXT_GENERATE_LOCAL_FULL_ADDR) {
+ krb5_sockaddr2port (context, local, &auth_context->local_port);
+ } else
+ auth_context->local_port = 0;
+ lptr = &local_k_address;
+ }
+ }
+ if(flags & KRB5_AUTH_CONTEXT_GENERATE_REMOTE_ADDR) {
+ len = sizeof(ss_remote);
+ if(getpeername(fd, remote, &len) < 0) {
+ ret = errno;
+ krb5_set_error_message(context, ret,
+ "getpeername: %s", strerror(ret));
+ goto out;
+ }
+ ret = krb5_sockaddr2address (context, remote, &remote_k_address);
+ if(ret) goto out;
+ if(flags & KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR) {
+ krb5_sockaddr2port (context, remote, &auth_context->remote_port);
+ } else
+ auth_context->remote_port = 0;
+ rptr = &remote_k_address;
+ }
+ ret = krb5_auth_con_setaddrs (context,
+ auth_context,
+ lptr,
+ rptr);
+ out:
+ if (lptr)
+ krb5_free_address (context, lptr);
+ if (rptr)
+ krb5_free_address (context, rptr);
+ return ret;
+
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_setaddrs_from_fd (krb5_context context,
+ krb5_auth_context auth_context,
+ void *p_fd)
+{
+ int fd = *(int*)p_fd;
+ int flags = 0;
+ if(auth_context->local_address == NULL)
+ flags |= KRB5_AUTH_CONTEXT_GENERATE_LOCAL_FULL_ADDR;
+ if(auth_context->remote_address == NULL)
+ flags |= KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR;
+ return krb5_auth_con_genaddrs(context, auth_context, fd, flags);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_getaddrs(krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_address **local_addr,
+ krb5_address **remote_addr)
+{
+ if(*local_addr)
+ krb5_free_address (context, *local_addr);
+ *local_addr = malloc (sizeof(**local_addr));
+ if (*local_addr == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ krb5_copy_address(context,
+ auth_context->local_address,
+ *local_addr);
+
+ if(*remote_addr)
+ krb5_free_address (context, *remote_addr);
+ *remote_addr = malloc (sizeof(**remote_addr));
+ if (*remote_addr == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ krb5_free_address (context, *local_addr);
+ *local_addr = NULL;
+ return ENOMEM;
+ }
+ krb5_copy_address(context,
+ auth_context->remote_address,
+ *remote_addr);
+ return 0;
+}
+
+static krb5_error_code
+copy_key(krb5_context context,
+ krb5_keyblock *in,
+ krb5_keyblock **out)
+{
+ if(in)
+ return krb5_copy_keyblock(context, in, out);
+ *out = NULL; /* is this right? */
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_getkey(krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_keyblock **keyblock)
+{
+ return copy_key(context, auth_context->keyblock, keyblock);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_getlocalsubkey(krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_keyblock **keyblock)
+{
+ return copy_key(context, auth_context->local_subkey, keyblock);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_getremotesubkey(krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_keyblock **keyblock)
+{
+ return copy_key(context, auth_context->remote_subkey, keyblock);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_setkey(krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_keyblock *keyblock)
+{
+ if(auth_context->keyblock)
+ krb5_free_keyblock(context, auth_context->keyblock);
+ return copy_key(context, keyblock, &auth_context->keyblock);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_setlocalsubkey(krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_keyblock *keyblock)
+{
+ if(auth_context->local_subkey)
+ krb5_free_keyblock(context, auth_context->local_subkey);
+ return copy_key(context, keyblock, &auth_context->local_subkey);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_generatelocalsubkey(krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_keyblock *key)
+{
+ krb5_error_code ret;
+ krb5_keyblock *subkey;
+
+ ret = krb5_generate_subkey_extended (context, key,
+ auth_context->keytype,
+ &subkey);
+ if(ret)
+ return ret;
+ if(auth_context->local_subkey)
+ krb5_free_keyblock(context, auth_context->local_subkey);
+ auth_context->local_subkey = subkey;
+ return 0;
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_setremotesubkey(krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_keyblock *keyblock)
+{
+ if(auth_context->remote_subkey)
+ krb5_free_keyblock(context, auth_context->remote_subkey);
+ return copy_key(context, keyblock, &auth_context->remote_subkey);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_setcksumtype(krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_cksumtype cksumtype)
+{
+ auth_context->cksumtype = cksumtype;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_getcksumtype(krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_cksumtype *cksumtype)
+{
+ *cksumtype = auth_context->cksumtype;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_setkeytype (krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_keytype keytype)
+{
+ auth_context->keytype = keytype;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_getkeytype (krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_keytype *keytype)
+{
+ *keytype = auth_context->keytype;
+ return 0;
+}
+
+#if 0
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_setenctype(krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_enctype etype)
+{
+ if(auth_context->keyblock)
+ krb5_free_keyblock(context, auth_context->keyblock);
+ ALLOC(auth_context->keyblock, 1);
+ if(auth_context->keyblock == NULL)
+ return ENOMEM;
+ auth_context->keyblock->keytype = etype;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_getenctype(krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_enctype *etype)
+{
+ krb5_abortx(context, "unimplemented krb5_auth_getenctype called");
+}
+#endif
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_getlocalseqnumber(krb5_context context,
+ krb5_auth_context auth_context,
+ int32_t *seqnumber)
+{
+ *seqnumber = auth_context->local_seqnumber;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_setlocalseqnumber (krb5_context context,
+ krb5_auth_context auth_context,
+ int32_t seqnumber)
+{
+ auth_context->local_seqnumber = seqnumber;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_getremoteseqnumber(krb5_context context,
+ krb5_auth_context auth_context,
+ int32_t *seqnumber)
+{
+ *seqnumber = auth_context->remote_seqnumber;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_setremoteseqnumber (krb5_context context,
+ krb5_auth_context auth_context,
+ int32_t seqnumber)
+{
+ auth_context->remote_seqnumber = seqnumber;
+ return 0;
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_getauthenticator(krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_authenticator *authenticator)
+{
+ *authenticator = malloc(sizeof(**authenticator));
+ if (*authenticator == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+
+ copy_Authenticator(auth_context->authenticator,
+ *authenticator);
+ return 0;
+}
+
+
+void KRB5_LIB_FUNCTION
+krb5_free_authenticator(krb5_context context,
+ krb5_authenticator *authenticator)
+{
+ free_Authenticator (*authenticator);
+ free (*authenticator);
+ *authenticator = NULL;
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_setuserkey(krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_keyblock *keyblock)
+{
+ if(auth_context->keyblock)
+ krb5_free_keyblock(context, auth_context->keyblock);
+ return krb5_copy_keyblock(context, keyblock, &auth_context->keyblock);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_getrcache(krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_rcache *rcache)
+{
+ *rcache = auth_context->rcache;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_setrcache(krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_rcache rcache)
+{
+ auth_context->rcache = rcache;
+ return 0;
+}
+
+#if 0 /* not implemented */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_initivector(krb5_context context,
+ krb5_auth_context auth_context)
+{
+ krb5_abortx(context, "unimplemented krb5_auth_con_initivector called");
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_auth_con_setivector(krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_pointer ivector)
+{
+ krb5_abortx(context, "unimplemented krb5_auth_con_setivector called");
+}
+
+#endif /* not implemented */
diff --git a/source4/heimdal/lib/krb5/build_ap_req.c b/source4/heimdal/lib/krb5/build_ap_req.c
new file mode 100644
index 0000000000..92c03cb782
--- /dev/null
+++ b/source4/heimdal/lib/krb5/build_ap_req.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 1997 - 2002 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <krb5_locl.h>
+
+RCSID("$Id$");
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_build_ap_req (krb5_context context,
+ krb5_enctype enctype,
+ krb5_creds *cred,
+ krb5_flags ap_options,
+ krb5_data authenticator,
+ krb5_data *retdata)
+{
+ krb5_error_code ret = 0;
+ AP_REQ ap;
+ Ticket t;
+ size_t len;
+
+ ap.pvno = 5;
+ ap.msg_type = krb_ap_req;
+ memset(&ap.ap_options, 0, sizeof(ap.ap_options));
+ ap.ap_options.use_session_key = (ap_options & AP_OPTS_USE_SESSION_KEY) > 0;
+ ap.ap_options.mutual_required = (ap_options & AP_OPTS_MUTUAL_REQUIRED) > 0;
+
+ ap.ticket.tkt_vno = 5;
+ copy_Realm(&cred->server->realm, &ap.ticket.realm);
+ copy_PrincipalName(&cred->server->name, &ap.ticket.sname);
+
+ decode_Ticket(cred->ticket.data, cred->ticket.length, &t, &len);
+ copy_EncryptedData(&t.enc_part, &ap.ticket.enc_part);
+ free_Ticket(&t);
+
+ ap.authenticator.etype = enctype;
+ ap.authenticator.kvno = NULL;
+ ap.authenticator.cipher = authenticator;
+
+ ASN1_MALLOC_ENCODE(AP_REQ, retdata->data, retdata->length,
+ &ap, &len, ret);
+ if(ret == 0 && retdata->length != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+ free_AP_REQ(&ap);
+ return ret;
+
+}
diff --git a/source4/heimdal/lib/krb5/build_auth.c b/source4/heimdal/lib/krb5/build_auth.c
new file mode 100644
index 0000000000..bbf4f274af
--- /dev/null
+++ b/source4/heimdal/lib/krb5/build_auth.c
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) 1997 - 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <krb5_locl.h>
+
+RCSID("$Id$");
+
+static krb5_error_code
+make_etypelist(krb5_context context,
+ krb5_authdata **auth_data)
+{
+ EtypeList etypes;
+ krb5_error_code ret;
+ krb5_authdata ad;
+ u_char *buf;
+ size_t len;
+ size_t buf_size;
+
+ ret = krb5_init_etype(context, &etypes.len, &etypes.val, NULL);
+ if (ret)
+ return ret;
+
+ ASN1_MALLOC_ENCODE(EtypeList, buf, buf_size, &etypes, &len, ret);
+ if (ret) {
+ free_EtypeList(&etypes);
+ return ret;
+ }
+ if(buf_size != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+ free_EtypeList(&etypes);
+
+ ALLOC_SEQ(&ad, 1);
+ if (ad.val == NULL) {
+ free(buf);
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+
+ ad.val[0].ad_type = KRB5_AUTHDATA_GSS_API_ETYPE_NEGOTIATION;
+ ad.val[0].ad_data.length = len;
+ ad.val[0].ad_data.data = buf;
+
+ ASN1_MALLOC_ENCODE(AD_IF_RELEVANT, buf, buf_size, &ad, &len, ret);
+ if (ret) {
+ free_AuthorizationData(&ad);
+ return ret;
+ }
+ if(buf_size != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+ free_AuthorizationData(&ad);
+
+ ALLOC(*auth_data, 1);
+ if (*auth_data == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+
+ ALLOC_SEQ(*auth_data, 1);
+ if ((*auth_data)->val == NULL) {
+ free(buf);
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+
+ (*auth_data)->val[0].ad_type = KRB5_AUTHDATA_IF_RELEVANT;
+ (*auth_data)->val[0].ad_data.length = len;
+ (*auth_data)->val[0].ad_data.data = buf;
+
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_build_authenticator (krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_enctype enctype,
+ krb5_creds *cred,
+ Checksum *cksum,
+ Authenticator **auth_result,
+ krb5_data *result,
+ krb5_key_usage usage)
+{
+ Authenticator *auth;
+ u_char *buf = NULL;
+ size_t buf_size;
+ size_t len;
+ krb5_error_code ret;
+ krb5_crypto crypto;
+
+ auth = calloc(1, sizeof(*auth));
+ if (auth == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+
+ auth->authenticator_vno = 5;
+ copy_Realm(&cred->client->realm, &auth->crealm);
+ copy_PrincipalName(&cred->client->name, &auth->cname);
+
+ krb5_us_timeofday (context, &auth->ctime, &auth->cusec);
+
+ ret = krb5_auth_con_getlocalsubkey(context, auth_context, &auth->subkey);
+ if(ret)
+ goto fail;
+
+ if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) {
+ if(auth_context->local_seqnumber == 0)
+ krb5_generate_seq_number (context,
+ &cred->session,
+ &auth_context->local_seqnumber);
+ ALLOC(auth->seq_number, 1);
+ if(auth->seq_number == NULL) {
+ ret = ENOMEM;
+ goto fail;
+ }
+ *auth->seq_number = auth_context->local_seqnumber;
+ } else
+ auth->seq_number = NULL;
+ auth->authorization_data = NULL;
+ auth->cksum = cksum;
+
+ if (cksum != NULL && cksum->cksumtype == CKSUMTYPE_GSSAPI) {
+ /*
+ * This is not GSS-API specific, we only enable it for
+ * GSS for now
+ */
+ ret = make_etypelist(context, &auth->authorization_data);
+ if (ret)
+ goto fail;
+ }
+
+ /* XXX - Copy more to auth_context? */
+
+ auth_context->authenticator->ctime = auth->ctime;
+ auth_context->authenticator->cusec = auth->cusec;
+
+ ASN1_MALLOC_ENCODE(Authenticator, buf, buf_size, auth, &len, ret);
+ if (ret)
+ goto fail;
+ if(buf_size != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+
+ ret = krb5_crypto_init(context, &cred->session, enctype, &crypto);
+ if (ret)
+ goto fail;
+ ret = krb5_encrypt (context,
+ crypto,
+ usage /* KRB5_KU_AP_REQ_AUTH */,
+ buf + buf_size - len,
+ len,
+ result);
+ krb5_crypto_destroy(context, crypto);
+
+ if (ret)
+ goto fail;
+
+ free (buf);
+
+ if (auth_result)
+ *auth_result = auth;
+ else {
+ /* Don't free the `cksum', it's allocated by the caller */
+ auth->cksum = NULL;
+ free_Authenticator (auth);
+ free (auth);
+ }
+ return ret;
+ fail:
+ free_Authenticator (auth);
+ free (auth);
+ free (buf);
+ return ret;
+}
diff --git a/source4/heimdal/lib/krb5/cache.c b/source4/heimdal/lib/krb5/cache.c
new file mode 100644
index 0000000000..80b755cd27
--- /dev/null
+++ b/source4/heimdal/lib/krb5/cache.c
@@ -0,0 +1,1489 @@
+/*
+ * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+/**
+ * Add a new ccache type with operations `ops', overwriting any
+ * existing one if `override'.
+ *
+ * @param context a Keberos context
+ * @param ops type of plugin symbol
+ * @param override flag to select if the registration is to overide
+ * an existing ops with the same name.
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_register(krb5_context context,
+ const krb5_cc_ops *ops,
+ krb5_boolean override)
+{
+ int i;
+
+ for(i = 0; i < context->num_cc_ops && context->cc_ops[i].prefix; i++) {
+ if(strcmp(context->cc_ops[i].prefix, ops->prefix) == 0) {
+ if(!override) {
+ krb5_set_error_message(context,
+ KRB5_CC_TYPE_EXISTS,
+ N_("cache type %s already exists", "type"),
+ ops->prefix);
+ return KRB5_CC_TYPE_EXISTS;
+ }
+ break;
+ }
+ }
+ if(i == context->num_cc_ops) {
+ krb5_cc_ops *o = realloc(context->cc_ops,
+ (context->num_cc_ops + 1) *
+ sizeof(*context->cc_ops));
+ if(o == NULL) {
+ krb5_set_error_message(context, KRB5_CC_NOMEM,
+ N_("malloc: out of memory", ""));
+ return KRB5_CC_NOMEM;
+ }
+ context->num_cc_ops++;
+ context->cc_ops = o;
+ memset(context->cc_ops + i, 0,
+ (context->num_cc_ops - i) * sizeof(*context->cc_ops));
+ }
+ memcpy(&context->cc_ops[i], ops, sizeof(context->cc_ops[i]));
+ return 0;
+}
+
+/*
+ * Allocate the memory for a `id' and the that function table to
+ * `ops'. Returns 0 or and error code.
+ */
+
+krb5_error_code
+_krb5_cc_allocate(krb5_context context,
+ const krb5_cc_ops *ops,
+ krb5_ccache *id)
+{
+ krb5_ccache p;
+
+ p = malloc (sizeof(*p));
+ if(p == NULL) {
+ krb5_set_error_message(context, KRB5_CC_NOMEM,
+ N_("malloc: out of memory", ""));
+ return KRB5_CC_NOMEM;
+ }
+ p->ops = ops;
+ *id = p;
+
+ return 0;
+}
+
+/*
+ * Allocate memory for a new ccache in `id' with operations `ops'
+ * and name `residual'. Return 0 or an error code.
+ */
+
+static krb5_error_code
+allocate_ccache (krb5_context context,
+ const krb5_cc_ops *ops,
+ const char *residual,
+ krb5_ccache *id)
+{
+ krb5_error_code ret;
+
+ ret = _krb5_cc_allocate(context, ops, id);
+ if (ret)
+ return ret;
+ ret = (*id)->ops->resolve(context, id, residual);
+ if(ret)
+ free(*id);
+ return ret;
+}
+
+/**
+ * Find and allocate a ccache in `id' from the specification in `residual'.
+ * If the ccache name doesn't contain any colon, interpret it as a file name.
+ *
+ * @param context a Keberos context.
+ * @param name string name of a credential cache.
+ * @param id return pointer to a found credential cache.
+ *
+ * @return Return 0 or an error code. In case of an error, id is set
+ * to NULL, see krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_resolve(krb5_context context,
+ const char *name,
+ krb5_ccache *id)
+{
+ int i;
+
+ *id = NULL;
+
+ for(i = 0; i < context->num_cc_ops && context->cc_ops[i].prefix; i++) {
+ size_t prefix_len = strlen(context->cc_ops[i].prefix);
+
+ if(strncmp(context->cc_ops[i].prefix, name, prefix_len) == 0
+ && name[prefix_len] == ':') {
+ return allocate_ccache (context, &context->cc_ops[i],
+ name + prefix_len + 1,
+ id);
+ }
+ }
+ if (strchr (name, ':') == NULL)
+ return allocate_ccache (context, &krb5_fcc_ops, name, id);
+ else {
+ krb5_set_error_message(context, KRB5_CC_UNKNOWN_TYPE,
+ N_("unknown ccache type %s", "name"), name);
+ return KRB5_CC_UNKNOWN_TYPE;
+ }
+}
+
+/**
+ * Generate a new ccache of type `ops' in `id'.
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_gen_new(krb5_context context,
+ const krb5_cc_ops *ops,
+ krb5_ccache *id)
+{
+ return krb5_cc_new_unique(context, ops->prefix, NULL, id);
+}
+
+/**
+ * Generates a new unique ccache of `type` in `id'. If `type' is NULL,
+ * the library chooses the default credential cache type. The supplied
+ * `hint' (that can be NULL) is a string that the credential cache
+ * type can use to base the name of the credential on, this is to make
+ * it easier for the user to differentiate the credentials.
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_new_unique(krb5_context context, const char *type,
+ const char *hint, krb5_ccache *id)
+{
+ const krb5_cc_ops *ops;
+ krb5_error_code ret;
+
+ ops = krb5_cc_get_prefix_ops(context, type);
+ if (ops == NULL) {
+ krb5_set_error_message(context, KRB5_CC_UNKNOWN_TYPE,
+ "Credential cache type %s is unknown", type);
+ return KRB5_CC_UNKNOWN_TYPE;
+ }
+
+ ret = _krb5_cc_allocate(context, ops, id);
+ if (ret)
+ return ret;
+ return (*id)->ops->gen_new(context, id);
+}
+
+/**
+ * Return the name of the ccache `id'
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+const char* KRB5_LIB_FUNCTION
+krb5_cc_get_name(krb5_context context,
+ krb5_ccache id)
+{
+ return id->ops->get_name(context, id);
+}
+
+/**
+ * Return the type of the ccache `id'.
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+const char* KRB5_LIB_FUNCTION
+krb5_cc_get_type(krb5_context context,
+ krb5_ccache id)
+{
+ return id->ops->prefix;
+}
+
+/**
+ * Return the complete resolvable name the ccache `id' in `str´.
+ * `str` should be freed with free(3).
+ * Returns 0 or an error (and then *str is set to NULL).
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_get_full_name(krb5_context context,
+ krb5_ccache id,
+ char **str)
+{
+ const char *type, *name;
+
+ *str = NULL;
+
+ type = krb5_cc_get_type(context, id);
+ if (type == NULL) {
+ krb5_set_error_message(context, KRB5_CC_UNKNOWN_TYPE,
+ "cache have no name of type");
+ return KRB5_CC_UNKNOWN_TYPE;
+ }
+
+ name = krb5_cc_get_name(context, id);
+ if (name == NULL) {
+ krb5_set_error_message(context, KRB5_CC_BADNAME,
+ "cache of type %s have no name", type);
+ return KRB5_CC_BADNAME;
+ }
+
+ if (asprintf(str, "%s:%s", type, name) == -1) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ *str = NULL;
+ return ENOMEM;
+ }
+ return 0;
+}
+
+/**
+ * Return krb5_cc_ops of a the ccache `id'.
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+const krb5_cc_ops *
+krb5_cc_get_ops(krb5_context context, krb5_ccache id)
+{
+ return id->ops;
+}
+
+/*
+ * Expand variables in `str' into `res'
+ */
+
+krb5_error_code
+_krb5_expand_default_cc_name(krb5_context context, const char *str, char **res)
+{
+ size_t tlen, len = 0;
+ char *tmp, *tmp2, *append;
+
+ *res = NULL;
+
+ while (str && *str) {
+ tmp = strstr(str, "%{");
+ if (tmp && tmp != str) {
+ append = malloc((tmp - str) + 1);
+ if (append) {
+ memcpy(append, str, tmp - str);
+ append[tmp - str] = '\0';
+ }
+ str = tmp;
+ } else if (tmp) {
+ tmp2 = strchr(tmp, '}');
+ if (tmp2 == NULL) {
+ free(*res);
+ *res = NULL;
+ krb5_set_error_message(context, KRB5_CONFIG_BADFORMAT,
+ "variable missing }");
+ return KRB5_CONFIG_BADFORMAT;
+ }
+ if (strncasecmp(tmp, "%{uid}", 6) == 0)
+ asprintf(&append, "%u", (unsigned)getuid());
+ else if (strncasecmp(tmp, "%{null}", 7) == 0)
+ append = strdup("");
+ else {
+ free(*res);
+ *res = NULL;
+ krb5_set_error_message(context,
+ KRB5_CONFIG_BADFORMAT,
+ "expand default cache unknown "
+ "variable \"%.*s\"",
+ (int)(tmp2 - tmp) - 2, tmp + 2);
+ return KRB5_CONFIG_BADFORMAT;
+ }
+ str = tmp2 + 1;
+ } else {
+ append = strdup(str);
+ str = NULL;
+ }
+ if (append == NULL) {
+ free(*res);
+ *res = NULL;
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+
+ tlen = strlen(append);
+ tmp = realloc(*res, len + tlen + 1);
+ if (tmp == NULL) {
+ free(append);
+ free(*res);
+ *res = NULL;
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ *res = tmp;
+ memcpy(*res + len, append, tlen + 1);
+ len = len + tlen;
+ free(append);
+ }
+ return 0;
+}
+
+/*
+ * Return non-zero if envirnoment that will determine default krb5cc
+ * name has changed.
+ */
+
+static int
+environment_changed(krb5_context context)
+{
+ const char *e;
+
+ /* if the cc name was set, don't change it */
+ if (context->default_cc_name_set)
+ return 0;
+
+ if(issuid())
+ return 0;
+
+ e = getenv("KRB5CCNAME");
+ if (e == NULL) {
+ if (context->default_cc_name_env) {
+ free(context->default_cc_name_env);
+ context->default_cc_name_env = NULL;
+ return 1;
+ }
+ } else {
+ if (context->default_cc_name_env == NULL)
+ return 1;
+ if (strcmp(e, context->default_cc_name_env) != 0)
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * Switch the default default credential cache for a specific
+ * credcache type (and name for some implementations).
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
+krb5_error_code
+krb5_cc_switch(krb5_context context, krb5_ccache id)
+{
+
+ if (id->ops->set_default == NULL)
+ return 0;
+
+ return (*id->ops->set_default)(context, id);
+}
+
+/**
+ * Set the default cc name for `context' to `name'.
+ *
+ * @ingroup krb5_ccache
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_set_default_name(krb5_context context, const char *name)
+{
+ krb5_error_code ret = 0;
+ char *p;
+
+ if (name == NULL) {
+ const char *e = NULL;
+
+ if(!issuid()) {
+ e = getenv("KRB5CCNAME");
+ if (e) {
+ p = strdup(e);
+ if (context->default_cc_name_env)
+ free(context->default_cc_name_env);
+ context->default_cc_name_env = strdup(e);
+ }
+ }
+ if (e == NULL) {
+ e = krb5_config_get_string(context, NULL, "libdefaults",
+ "default_cc_name", NULL);
+ if (e) {
+ ret = _krb5_expand_default_cc_name(context, e, &p);
+ if (ret)
+ return ret;
+ }
+ if (e == NULL) {
+ const krb5_cc_ops *ops = KRB5_DEFAULT_CCTYPE;
+ e = krb5_config_get_string(context, NULL, "libdefaults",
+ "default_cc_type", NULL);
+ if (e) {
+ ops = krb5_cc_get_prefix_ops(context, e);
+ if (ops == NULL) {
+ krb5_set_error_message(context,
+ KRB5_CC_UNKNOWN_TYPE,
+ "Credential cache type %s "
+ "is unknown", e);
+ return KRB5_CC_UNKNOWN_TYPE;
+ }
+ }
+ ret = (*ops->get_default_name)(context, &p);
+ if (ret)
+ return ret;
+ }
+ }
+ context->default_cc_name_set = 0;
+ } else {
+ p = strdup(name);
+ context->default_cc_name_set = 1;
+ }
+
+ if (p == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+
+ if (context->default_cc_name)
+ free(context->default_cc_name);
+
+ context->default_cc_name = p;
+
+ return ret;
+}
+
+/**
+ * Return a pointer to a context static string containing the default
+ * ccache name.
+ *
+ * @return String to the default credential cache name.
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+const char* KRB5_LIB_FUNCTION
+krb5_cc_default_name(krb5_context context)
+{
+ if (context->default_cc_name == NULL || environment_changed(context))
+ krb5_cc_set_default_name(context, NULL);
+
+ return context->default_cc_name;
+}
+
+/**
+ * Open the default ccache in `id'.
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_default(krb5_context context,
+ krb5_ccache *id)
+{
+ const char *p = krb5_cc_default_name(context);
+
+ if (p == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ return krb5_cc_resolve(context, p, id);
+}
+
+/**
+ * Create a new ccache in `id' for `primary_principal'.
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_initialize(krb5_context context,
+ krb5_ccache id,
+ krb5_principal primary_principal)
+{
+ return (*id->ops->init)(context, id, primary_principal);
+}
+
+
+/**
+ * Remove the ccache `id'.
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_destroy(krb5_context context,
+ krb5_ccache id)
+{
+ krb5_error_code ret;
+
+ ret = (*id->ops->destroy)(context, id);
+ krb5_cc_close (context, id);
+ return ret;
+}
+
+/**
+ * Stop using the ccache `id' and free the related resources.
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_close(krb5_context context,
+ krb5_ccache id)
+{
+ krb5_error_code ret;
+ ret = (*id->ops->close)(context, id);
+ free(id);
+ return ret;
+}
+
+/**
+ * Store `creds' in the ccache `id'.
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_store_cred(krb5_context context,
+ krb5_ccache id,
+ krb5_creds *creds)
+{
+ return (*id->ops->store)(context, id, creds);
+}
+
+/**
+ * Retrieve the credential identified by `mcreds' (and `whichfields')
+ * from `id' in `creds'. 'creds' must be free by the caller using
+ * krb5_free_cred_contents.
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_retrieve_cred(krb5_context context,
+ krb5_ccache id,
+ krb5_flags whichfields,
+ const krb5_creds *mcreds,
+ krb5_creds *creds)
+{
+ krb5_error_code ret;
+ krb5_cc_cursor cursor;
+
+ if (id->ops->retrieve != NULL) {
+ return (*id->ops->retrieve)(context, id, whichfields,
+ mcreds, creds);
+ }
+
+ ret = krb5_cc_start_seq_get(context, id, &cursor);
+ if (ret)
+ return ret;
+ while((ret = krb5_cc_next_cred(context, id, &cursor, creds)) == 0){
+ if(krb5_compare_creds(context, whichfields, mcreds, creds)){
+ ret = 0;
+ break;
+ }
+ krb5_free_cred_contents (context, creds);
+ }
+ krb5_cc_end_seq_get(context, id, &cursor);
+ return ret;
+}
+
+/**
+ * Return the principal of `id' in `principal'.
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_get_principal(krb5_context context,
+ krb5_ccache id,
+ krb5_principal *principal)
+{
+ return (*id->ops->get_princ)(context, id, principal);
+}
+
+/**
+ * Start iterating over `id', `cursor' is initialized to the
+ * beginning.
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_start_seq_get (krb5_context context,
+ const krb5_ccache id,
+ krb5_cc_cursor *cursor)
+{
+ return (*id->ops->get_first)(context, id, cursor);
+}
+
+/**
+ * Retrieve the next cred pointed to by (`id', `cursor') in `creds'
+ * and advance `cursor'.
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_next_cred (krb5_context context,
+ const krb5_ccache id,
+ krb5_cc_cursor *cursor,
+ krb5_creds *creds)
+{
+ return (*id->ops->get_next)(context, id, cursor, creds);
+}
+
+/**
+ * Like krb5_cc_next_cred, but allow for selective retrieval
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_next_cred_match(krb5_context context,
+ const krb5_ccache id,
+ krb5_cc_cursor * cursor,
+ krb5_creds * creds,
+ krb5_flags whichfields,
+ const krb5_creds * mcreds)
+{
+ krb5_error_code ret;
+ while (1) {
+ ret = krb5_cc_next_cred(context, id, cursor, creds);
+ if (ret)
+ return ret;
+ if (mcreds == NULL || krb5_compare_creds(context, whichfields, mcreds, creds))
+ return 0;
+ krb5_free_cred_contents(context, creds);
+ }
+}
+
+/**
+ * Destroy the cursor `cursor'.
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_end_seq_get (krb5_context context,
+ const krb5_ccache id,
+ krb5_cc_cursor *cursor)
+{
+ return (*id->ops->end_get)(context, id, cursor);
+}
+
+/**
+ * Remove the credential identified by `cred', `which' from `id'.
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_remove_cred(krb5_context context,
+ krb5_ccache id,
+ krb5_flags which,
+ krb5_creds *cred)
+{
+ if(id->ops->remove_cred == NULL) {
+ krb5_set_error_message(context,
+ EACCES,
+ "ccache %s does not support remove_cred",
+ id->ops->prefix);
+ return EACCES; /* XXX */
+ }
+ return (*id->ops->remove_cred)(context, id, which, cred);
+}
+
+/**
+ * Set the flags of `id' to `flags'.
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_set_flags(krb5_context context,
+ krb5_ccache id,
+ krb5_flags flags)
+{
+ return (*id->ops->set_flags)(context, id, flags);
+}
+
+/**
+ * Get the flags of `id', store them in `flags'.
+ *
+ * @ingroup krb5_ccache
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_get_flags(krb5_context context,
+ krb5_ccache id,
+ krb5_flags *flags)
+{
+ *flags = 0;
+ return 0;
+}
+
+/**
+ * Copy the contents of `from' to `to'.
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_copy_cache_match(krb5_context context,
+ const krb5_ccache from,
+ krb5_ccache to,
+ krb5_flags whichfields,
+ const krb5_creds * mcreds,
+ unsigned int *matched)
+{
+ krb5_error_code ret;
+ krb5_cc_cursor cursor;
+ krb5_creds cred;
+ krb5_principal princ;
+
+ ret = krb5_cc_get_principal(context, from, &princ);
+ if (ret)
+ return ret;
+ ret = krb5_cc_initialize(context, to, princ);
+ if (ret) {
+ krb5_free_principal(context, princ);
+ return ret;
+ }
+ ret = krb5_cc_start_seq_get(context, from, &cursor);
+ if (ret) {
+ krb5_free_principal(context, princ);
+ return ret;
+ }
+ if (matched)
+ *matched = 0;
+ while (ret == 0 &&
+ krb5_cc_next_cred_match(context, from, &cursor, &cred,
+ whichfields, mcreds) == 0) {
+ if (matched)
+ (*matched)++;
+ ret = krb5_cc_store_cred(context, to, &cred);
+ krb5_free_cred_contents(context, &cred);
+ }
+ krb5_cc_end_seq_get(context, from, &cursor);
+ krb5_free_principal(context, princ);
+ return ret;
+}
+
+
+/**
+ * Just like krb5_cc_copy_cache_match, but copy everything.
+ *
+ * @ingroup @krb5_ccache
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_copy_cache(krb5_context context,
+ const krb5_ccache from,
+ krb5_ccache to)
+{
+ return krb5_cc_copy_cache_match(context, from, to, 0, NULL, NULL);
+}
+
+/**
+ * MIT compat glue
+ *
+ * @ingroup krb5_ccache
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_copy_creds(krb5_context context,
+ const krb5_ccache from,
+ krb5_ccache to)
+{
+ return krb5_cc_copy_cache(context, from, to);
+}
+
+/**
+ * Return the version of `id'.
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_get_version(krb5_context context,
+ const krb5_ccache id)
+{
+ if(id->ops->get_version)
+ return (*id->ops->get_version)(context, id);
+ else
+ return 0;
+}
+
+/**
+ * Clear `mcreds' so it can be used with krb5_cc_retrieve_cred
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+void KRB5_LIB_FUNCTION
+krb5_cc_clear_mcred(krb5_creds *mcred)
+{
+ memset(mcred, 0, sizeof(*mcred));
+}
+
+/**
+ * Get the cc ops that is registered in `context' to handle the
+ * prefix. prefix can be a complete credential cache name or a
+ * prefix, the function will only use part up to the first colon (:)
+ * if there is one. If prefix the argument is NULL, the default ccache
+ * implemtation is returned.
+ *
+ * @return Returns NULL if ops not found.
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+const krb5_cc_ops *
+krb5_cc_get_prefix_ops(krb5_context context, const char *prefix)
+{
+ char *p, *p1;
+ int i;
+
+ if (prefix == NULL)
+ return KRB5_DEFAULT_CCTYPE;
+ if (prefix[0] == '/')
+ return &krb5_fcc_ops;
+
+ p = strdup(prefix);
+ if (p == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return NULL;
+ }
+ p1 = strchr(p, ':');
+ if (p1)
+ *p1 = '\0';
+
+ for(i = 0; i < context->num_cc_ops && context->cc_ops[i].prefix; i++) {
+ if(strcmp(context->cc_ops[i].prefix, p) == 0) {
+ free(p);
+ return &context->cc_ops[i];
+ }
+ }
+ free(p);
+ return NULL;
+}
+
+struct krb5_cc_cache_cursor_data {
+ const krb5_cc_ops *ops;
+ krb5_cc_cursor cursor;
+};
+
+/**
+ * Start iterating over all caches of specified type. See also
+ * krb5_cccol_cursor_new().
+
+ * @param context A Kerberos 5 context
+ * @param type optional type to iterate over, if NULL, the default cache is used.
+ * @param cursor cursor should be freed with krb5_cc_cache_end_seq_get().
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_cache_get_first (krb5_context context,
+ const char *type,
+ krb5_cc_cache_cursor *cursor)
+{
+ const krb5_cc_ops *ops;
+ krb5_error_code ret;
+
+ if (type == NULL)
+ type = krb5_cc_default_name(context);
+
+ ops = krb5_cc_get_prefix_ops(context, type);
+ if (ops == NULL) {
+ krb5_set_error_message(context, KRB5_CC_UNKNOWN_TYPE,
+ "Unknown type \"%s\" when iterating "
+ "trying to iterate the credential caches", type);
+ return KRB5_CC_UNKNOWN_TYPE;
+ }
+
+ if (ops->get_cache_first == NULL) {
+ krb5_set_error_message(context, KRB5_CC_NOSUPP,
+ N_("Credential cache type %s doesn't support "
+ "iterations over caches", "type"),
+ ops->prefix);
+ return KRB5_CC_NOSUPP;
+ }
+
+ *cursor = calloc(1, sizeof(**cursor));
+ if (*cursor == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+
+ (*cursor)->ops = ops;
+
+ ret = ops->get_cache_first(context, &(*cursor)->cursor);
+ if (ret) {
+ free(*cursor);
+ *cursor = NULL;
+ }
+ return ret;
+}
+
+/**
+ * Retrieve the next cache pointed to by (`cursor') in `id'
+ * and advance `cursor'.
+ *
+ * @return Return 0 or an error code. Returns KRB5_CC_END when the end
+ * of caches is reached, see krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_cache_next (krb5_context context,
+ krb5_cc_cache_cursor cursor,
+ krb5_ccache *id)
+{
+ return cursor->ops->get_cache_next(context, cursor->cursor, id);
+}
+
+/**
+ * Destroy the cursor `cursor'.
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_cache_end_seq_get (krb5_context context,
+ krb5_cc_cache_cursor cursor)
+{
+ krb5_error_code ret;
+ ret = cursor->ops->end_cache_get(context, cursor->cursor);
+ cursor->ops = NULL;
+ free(cursor);
+ return ret;
+}
+
+/**
+ * Search for a matching credential cache of type `type' that have the
+ * `principal' as the default principal. On success, `id' needs to be
+ * freed with krb5_cc_close() or krb5_cc_destroy().
+ *
+ * @return On failure, error code is returned and `id' is set to NULL.
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_cache_match (krb5_context context,
+ krb5_principal client,
+ krb5_ccache *id)
+{
+ krb5_cccol_cursor cursor;
+ krb5_error_code ret;
+ krb5_ccache cache = NULL;
+
+ *id = NULL;
+
+ ret = krb5_cccol_cursor_new (context, &cursor);
+ if (ret)
+ return ret;
+
+ while ((ret = krb5_cccol_cursor_next (context, cursor, &cache)) == 0) {
+ krb5_principal principal;
+
+ ret = krb5_cc_get_principal(context, cache, &principal);
+ if (ret == 0) {
+ krb5_boolean match;
+
+ match = krb5_principal_compare(context, principal, client);
+ krb5_free_principal(context, principal);
+ if (match)
+ break;
+ }
+
+ krb5_cc_close(context, cache);
+ cache = NULL;
+ }
+
+ krb5_cccol_cursor_free(context, &cursor);
+
+ if (cache == NULL) {
+ char *str;
+
+ krb5_unparse_name(context, client, &str);
+
+ krb5_set_error_message(context, KRB5_CC_NOTFOUND,
+ N_("Principal %s not found in a "
+ "credential cache", ""),
+ str ? str : "<out of memory>");
+ if (str)
+ free(str);
+ return KRB5_CC_NOTFOUND;
+ }
+ *id = cache;
+
+ return 0;
+}
+
+/**
+ * Move the content from one credential cache to another. The
+ * operation is an atomic switch.
+ *
+ * @param context a Keberos context
+ * @param from the credential cache to move the content from
+ * @param to the credential cache to move the content to
+
+ * @return On sucess, from is freed. On failure, error code is
+ * returned and from and to are both still allocated, see krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
+krb5_error_code
+krb5_cc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
+{
+ krb5_error_code ret;
+
+ if (strcmp(from->ops->prefix, to->ops->prefix) != 0) {
+ krb5_set_error_message(context, KRB5_CC_NOSUPP,
+ N_("Moving credentials between diffrent "
+ "types not yet supported", ""));
+ return KRB5_CC_NOSUPP;
+ }
+
+ ret = (*to->ops->move)(context, from, to);
+ if (ret == 0) {
+ memset(from, 0, sizeof(*from));
+ free(from);
+ }
+ return ret;
+}
+
+#define KRB5_CONF_NAME "krb5_ccache_conf_data"
+#define KRB5_REALM_NAME "X-CACHECONF:"
+
+static krb5_error_code
+build_conf_principals(krb5_context context, krb5_ccache id,
+ krb5_const_principal principal,
+ const char *name, krb5_creds *cred)
+{
+ krb5_principal client;
+ krb5_error_code ret;
+ char *pname = NULL;
+
+ memset(cred, 0, sizeof(*cred));
+
+ ret = krb5_cc_get_principal(context, id, &client);
+ if (ret)
+ return ret;
+
+ if (principal) {
+ ret = krb5_unparse_name(context, principal, &pname);
+ if (ret)
+ return ret;
+ }
+
+ ret = krb5_make_principal(context, &cred->server,
+ krb5_principal_get_realm(context, client),
+ KRB5_CONF_NAME, name, pname, NULL);
+ free(pname);
+ if (ret) {
+ krb5_free_principal(context, client);
+ return ret;
+ }
+ ret = krb5_copy_principal(context, client, &cred->client);
+ krb5_free_principal(context, client);
+ return ret;
+}
+
+/**
+ * Return TRUE (non zero) if the principal is a configuration
+ * principal (generated part of krb5_cc_set_config()). Returns FALSE
+ * (zero) if not a configuration principal.
+ *
+ * @param context a Keberos context
+ * @param principal principal to check if it a configuration principal
+ *
+ * @ingroup krb5_ccache
+ */
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_is_config_principal(krb5_context context,
+ krb5_const_principal principal)
+{
+ if (strcmp(principal->realm, KRB5_REALM_NAME) != 0)
+ return FALSE;
+
+ if (principal->name.name_string.len == 0 ||
+ strcmp(principal->name.name_string.val[0], KRB5_CONF_NAME) != 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+/**
+ * Store some configuration for the credential cache in the cache.
+ * Existing configuration under the same name is over-written.
+ *
+ * @param context a Keberos context
+ * @param id the credential cache to store the data for
+ * @param principal configuration for a specific principal, if
+ * NULL, global for the whole cache.
+ * @param name name under which the configuraion is stored.
+ * @param data data to store
+ *
+ * @ingroup krb5_ccache
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_set_config(krb5_context context, krb5_ccache id,
+ krb5_const_principal principal,
+ const char *name, krb5_data *data)
+{
+ krb5_error_code ret;
+ krb5_creds cred;
+
+ ret = build_conf_principals(context, id, principal, name, &cred);
+ if (ret)
+ goto out;
+
+ /* Remove old configuration */
+ ret = krb5_cc_remove_cred(context, id, 0, &cred);
+ if (ret && ret != KRB5_CC_NOTFOUND)
+ goto out;
+
+ /* not that anyone care when this expire */
+ cred.times.authtime = time(NULL);
+ cred.times.endtime = cred.times.authtime + 3600 * 24 * 30;
+
+ ret = krb5_data_copy(&cred.ticket, data->data, data->length);
+ if (ret)
+ goto out;
+
+ ret = krb5_cc_store_cred(context, id, &cred);
+
+out:
+ krb5_free_cred_contents (context, &cred);
+ return ret;
+}
+
+/**
+ * Get some configuration for the credential cache in the cache.
+ *
+ * @param context a Keberos context
+ * @param id the credential cache to store the data for
+ * @param principal configuration for a specific principal, if
+ * NULL, global for the whole cache.
+ * @param name name under which the configuraion is stored.
+ * @param data data to fetched, free with krb5_data_free()
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_get_config(krb5_context context, krb5_ccache id,
+ krb5_const_principal principal,
+ const char *name, krb5_data *data)
+{
+ krb5_creds mcred, cred;
+ krb5_error_code ret;
+
+ memset(&cred, 0, sizeof(cred));
+ krb5_data_zero(data);
+
+ ret = build_conf_principals(context, id, principal, name, &mcred);
+ if (ret)
+ goto out;
+
+ ret = krb5_cc_retrieve_cred(context, id, 0, &mcred, &cred);
+ if (ret)
+ goto out;
+
+ ret = krb5_data_copy(data, cred.ticket.data, cred.ticket.length);
+
+out:
+ krb5_free_cred_contents (context, &cred);
+ krb5_free_cred_contents (context, &mcred);
+ return ret;
+}
+
+/*
+ *
+ */
+
+struct krb5_cccol_cursor {
+ int idx;
+ krb5_cc_cache_cursor cursor;
+};
+
+/**
+ * Get a new cache interation cursor that will interate over all
+ * credentials caches independent of type.
+ *
+ * @param context a Keberos context
+ * @param cursor passed into krb5_cccol_cursor_next() and free with krb5_cccol_cursor_free().
+ *
+ * @return Returns 0 or and error code, see krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cccol_cursor_new(krb5_context context, krb5_cccol_cursor *cursor)
+{
+ *cursor = calloc(1, sizeof(**cursor));
+ if (*cursor == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ (*cursor)->idx = 0;
+ (*cursor)->cursor = NULL;
+
+ return 0;
+}
+
+/**
+ * Get next credential cache from the iteration.
+ *
+ * @param context A Kerberos 5 context
+ * @param cursor the iteration cursor
+ * @param cache the returned cursor, pointer is set to NULL on failure
+ * and a cache on success. The returned cache needs to be freed
+ * with krb5_cc_close() or destroyed with krb5_cc_destroy().
+ *
+ * @return Return 0 or and error, KRB5_CC_END is returned at the end
+ * of iteration. See krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cccol_cursor_next(krb5_context context, krb5_cccol_cursor cursor,
+ krb5_ccache *cache)
+{
+ krb5_error_code ret;
+
+ *cache = NULL;
+
+ while (cursor->idx < context->num_cc_ops) {
+
+ if (cursor->cursor == NULL) {
+ ret = krb5_cc_cache_get_first (context,
+ context->cc_ops[cursor->idx].prefix,
+ &cursor->cursor);
+ if (ret) {
+ cursor->idx++;
+ continue;
+ }
+ }
+ ret = krb5_cc_cache_next(context, cursor->cursor, cache);
+ if (ret == 0)
+ break;
+
+ krb5_cc_cache_end_seq_get(context, cursor->cursor);
+ cursor->cursor = NULL;
+ if (ret != KRB5_CC_END)
+ break;
+
+ cursor->idx++;
+ }
+ if (cursor->idx >= context->num_cc_ops) {
+ krb5_set_error_message(context, KRB5_CC_END,
+ N_("Reached end of credential caches", ""));
+ return KRB5_CC_END;
+ }
+
+ return 0;
+}
+
+/**
+ * End an iteration and free all resources, can be done before end is reached.
+ *
+ * @param context A Kerberos 5 context
+ * @param cursor the iteration cursor to be freed.
+ *
+ * @return Return 0 or and error, KRB5_CC_END is returned at the end
+ * of iteration. See krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cccol_cursor_free(krb5_context context, krb5_cccol_cursor *cursor)
+{
+ krb5_cccol_cursor c = *cursor;
+
+ *cursor = NULL;
+ if (c) {
+ if (c->cursor)
+ krb5_cc_cache_end_seq_get(context, c->cursor);
+ free(c);
+ }
+ return 0;
+}
+
+/**
+ * Return the last time the credential cache was modified.
+ *
+ * @param context A Kerberos 5 context
+ * @param id The credential cache to probe
+ * @param mtime the last modification time, set to 0 on error.
+
+ * @return Return 0 or and error. See krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cc_last_change_time(krb5_context context,
+ krb5_ccache id,
+ krb5_timestamp *mtime)
+{
+ *mtime = 0;
+ return (*id->ops->lastchange)(context, id, mtime);
+}
+
+/**
+ * Return the last modfication time for a cache collection. The query
+ * can be limited to a specific cache type. If the function return 0
+ * and mtime is 0, there was no credentials in the caches.
+ *
+ * @param context A Kerberos 5 context
+ * @param id The credential cache to probe
+ * @param mtime the last modification time, set to 0 on error.
+
+ * @return Return 0 or and error. See krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cccol_last_change_time(krb5_context context,
+ const char *type,
+ krb5_timestamp *mtime)
+{
+ krb5_cccol_cursor cursor;
+ krb5_error_code ret;
+ krb5_ccache id;
+ krb5_timestamp t = 0;
+
+ *mtime = 0;
+
+ ret = krb5_cccol_cursor_new (context, &cursor);
+ if (ret)
+ return ret;
+
+ while ((ret = krb5_cccol_cursor_next (context, cursor, &id)) == 0) {
+
+ if (type && strcmp(krb5_cc_get_type(context, id), type) != 0)
+ continue;
+
+ ret = krb5_cc_last_change_time(context, id, &t);
+ krb5_cc_close(context, id);
+ if (ret)
+ continue;
+ if (t > *mtime)
+ *mtime = t;
+ }
+
+ krb5_cccol_cursor_free(context, &cursor);
+
+ return 0;
+}
diff --git a/source4/heimdal/lib/krb5/changepw.c b/source4/heimdal/lib/krb5/changepw.c
new file mode 100644
index 0000000000..91ed9c5ba0
--- /dev/null
+++ b/source4/heimdal/lib/krb5/changepw.c
@@ -0,0 +1,862 @@
+/*
+ * Copyright (c) 1997 - 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <krb5_locl.h>
+
+RCSID("$Id$");
+
+#undef __attribute__
+#define __attribute__(X)
+
+
+static void
+str2data (krb5_data *d,
+ const char *fmt,
+ ...) __attribute__ ((format (printf, 2, 3)));
+
+static void
+str2data (krb5_data *d,
+ const char *fmt,
+ ...)
+{
+ va_list args;
+ char *str;
+
+ va_start(args, fmt);
+ d->length = vasprintf (&str, fmt, args);
+ va_end(args);
+ d->data = str;
+}
+
+/*
+ * Change password protocol defined by
+ * draft-ietf-cat-kerb-chg-password-02.txt
+ *
+ * Share the response part of the protocol with MS set password
+ * (RFC3244)
+ */
+
+static krb5_error_code
+chgpw_send_request (krb5_context context,
+ krb5_auth_context *auth_context,
+ krb5_creds *creds,
+ krb5_principal targprinc,
+ int is_stream,
+ int sock,
+ const char *passwd,
+ const char *host)
+{
+ krb5_error_code ret;
+ krb5_data ap_req_data;
+ krb5_data krb_priv_data;
+ krb5_data passwd_data;
+ size_t len;
+ u_char header[6];
+ u_char *p;
+ struct iovec iov[3];
+ struct msghdr msghdr;
+
+ if (is_stream)
+ return KRB5_KPASSWD_MALFORMED;
+
+ if (targprinc &&
+ krb5_principal_compare(context, creds->client, targprinc) != TRUE)
+ return KRB5_KPASSWD_MALFORMED;
+
+ krb5_data_zero (&ap_req_data);
+
+ ret = krb5_mk_req_extended (context,
+ auth_context,
+ AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_USE_SUBKEY,
+ NULL, /* in_data */
+ creds,
+ &ap_req_data);
+ if (ret)
+ return ret;
+
+ passwd_data.data = rk_UNCONST(passwd);
+ passwd_data.length = strlen(passwd);
+
+ krb5_data_zero (&krb_priv_data);
+
+ ret = krb5_mk_priv (context,
+ *auth_context,
+ &passwd_data,
+ &krb_priv_data,
+ NULL);
+ if (ret)
+ goto out2;
+
+ len = 6 + ap_req_data.length + krb_priv_data.length;
+ p = header;
+ *p++ = (len >> 8) & 0xFF;
+ *p++ = (len >> 0) & 0xFF;
+ *p++ = 0;
+ *p++ = 1;
+ *p++ = (ap_req_data.length >> 8) & 0xFF;
+ *p++ = (ap_req_data.length >> 0) & 0xFF;
+
+ memset(&msghdr, 0, sizeof(msghdr));
+ msghdr.msg_name = NULL;
+ msghdr.msg_namelen = 0;
+ msghdr.msg_iov = iov;
+ msghdr.msg_iovlen = sizeof(iov)/sizeof(*iov);
+#if 0
+ msghdr.msg_control = NULL;
+ msghdr.msg_controllen = 0;
+#endif
+
+ iov[0].iov_base = (void*)header;
+ iov[0].iov_len = 6;
+ iov[1].iov_base = ap_req_data.data;
+ iov[1].iov_len = ap_req_data.length;
+ iov[2].iov_base = krb_priv_data.data;
+ iov[2].iov_len = krb_priv_data.length;
+
+ if (sendmsg (sock, &msghdr, 0) < 0) {
+ ret = errno;
+ krb5_set_error_message(context, ret, "sendmsg %s: %s",
+ host, strerror(ret));
+ }
+
+ krb5_data_free (&krb_priv_data);
+out2:
+ krb5_data_free (&ap_req_data);
+ return ret;
+}
+
+/*
+ * Set password protocol as defined by RFC3244 --
+ * Microsoft Windows 2000 Kerberos Change Password and Set Password Protocols
+ */
+
+static krb5_error_code
+setpw_send_request (krb5_context context,
+ krb5_auth_context *auth_context,
+ krb5_creds *creds,
+ krb5_principal targprinc,
+ int is_stream,
+ int sock,
+ const char *passwd,
+ const char *host)
+{
+ krb5_error_code ret;
+ krb5_data ap_req_data;
+ krb5_data krb_priv_data;
+ krb5_data pwd_data;
+ ChangePasswdDataMS chpw;
+ size_t len;
+ u_char header[4 + 6];
+ u_char *p;
+ struct iovec iov[3];
+ struct msghdr msghdr;
+
+ krb5_data_zero (&ap_req_data);
+
+ ret = krb5_mk_req_extended (context,
+ auth_context,
+ AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_USE_SUBKEY,
+ NULL, /* in_data */
+ creds,
+ &ap_req_data);
+ if (ret)
+ return ret;
+
+ chpw.newpasswd.length = strlen(passwd);
+ chpw.newpasswd.data = rk_UNCONST(passwd);
+ if (targprinc) {
+ chpw.targname = &targprinc->name;
+ chpw.targrealm = &targprinc->realm;
+ } else {
+ chpw.targname = NULL;
+ chpw.targrealm = NULL;
+ }
+
+ ASN1_MALLOC_ENCODE(ChangePasswdDataMS, pwd_data.data, pwd_data.length,
+ &chpw, &len, ret);
+ if (ret) {
+ krb5_data_free (&ap_req_data);
+ return ret;
+ }
+
+ if(pwd_data.length != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+
+ ret = krb5_mk_priv (context,
+ *auth_context,
+ &pwd_data,
+ &krb_priv_data,
+ NULL);
+ if (ret)
+ goto out2;
+
+ len = 6 + ap_req_data.length + krb_priv_data.length;
+ p = header;
+ if (is_stream) {
+ _krb5_put_int(p, len, 4);
+ p += 4;
+ }
+ *p++ = (len >> 8) & 0xFF;
+ *p++ = (len >> 0) & 0xFF;
+ *p++ = 0xff;
+ *p++ = 0x80;
+ *p++ = (ap_req_data.length >> 8) & 0xFF;
+ *p++ = (ap_req_data.length >> 0) & 0xFF;
+
+ memset(&msghdr, 0, sizeof(msghdr));
+ msghdr.msg_name = NULL;
+ msghdr.msg_namelen = 0;
+ msghdr.msg_iov = iov;
+ msghdr.msg_iovlen = sizeof(iov)/sizeof(*iov);
+#if 0
+ msghdr.msg_control = NULL;
+ msghdr.msg_controllen = 0;
+#endif
+
+ iov[0].iov_base = (void*)header;
+ if (is_stream)
+ iov[0].iov_len = 10;
+ else
+ iov[0].iov_len = 6;
+ iov[1].iov_base = ap_req_data.data;
+ iov[1].iov_len = ap_req_data.length;
+ iov[2].iov_base = krb_priv_data.data;
+ iov[2].iov_len = krb_priv_data.length;
+
+ if (sendmsg (sock, &msghdr, 0) < 0) {
+ ret = errno;
+ krb5_set_error_message(context, ret, "sendmsg %s: %s",
+ host, strerror(ret));
+ }
+
+ krb5_data_free (&krb_priv_data);
+out2:
+ krb5_data_free (&ap_req_data);
+ krb5_data_free (&pwd_data);
+ return ret;
+}
+
+static krb5_error_code
+process_reply (krb5_context context,
+ krb5_auth_context auth_context,
+ int is_stream,
+ int sock,
+ int *result_code,
+ krb5_data *result_code_string,
+ krb5_data *result_string,
+ const char *host)
+{
+ krb5_error_code ret;
+ u_char reply[1024 * 3];
+ ssize_t len;
+ uint16_t pkt_len, pkt_ver;
+ krb5_data ap_rep_data;
+ int save_errno;
+
+ len = 0;
+ if (is_stream) {
+ while (len < sizeof(reply)) {
+ unsigned long size;
+
+ ret = recvfrom (sock, reply + len, sizeof(reply) - len,
+ 0, NULL, NULL);
+ if (ret < 0) {
+ save_errno = errno;
+ krb5_set_error_message(context, save_errno,
+ "recvfrom %s: %s",
+ host, strerror(save_errno));
+ return save_errno;
+ } else if (ret == 0) {
+ krb5_set_error_message(context, 1,"recvfrom timeout %s", host);
+ return 1;
+ }
+ len += ret;
+ if (len < 4)
+ continue;
+ _krb5_get_int(reply, &size, 4);
+ if (size + 4 < len)
+ continue;
+ memmove(reply, reply + 4, size);
+ len = size;
+ break;
+ }
+ if (len == sizeof(reply)) {
+ krb5_set_error_message(context, ENOMEM,
+ N_("Message too large from %s", "host"),
+ host);
+ return ENOMEM;
+ }
+ } else {
+ ret = recvfrom (sock, reply, sizeof(reply), 0, NULL, NULL);
+ if (ret < 0) {
+ save_errno = errno;
+ krb5_set_error_message(context, save_errno,
+ "recvfrom %s: %s",
+ host, strerror(save_errno));
+ return save_errno;
+ }
+ len = ret;
+ }
+
+ if (len < 6) {
+ str2data (result_string, "server %s sent to too short message "
+ "(%ld bytes)", host, (long)len);
+ *result_code = KRB5_KPASSWD_MALFORMED;
+ return 0;
+ }
+
+ pkt_len = (reply[0] << 8) | (reply[1]);
+ pkt_ver = (reply[2] << 8) | (reply[3]);
+
+ if ((pkt_len != len) || (reply[1] == 0x7e || reply[1] == 0x5e)) {
+ KRB_ERROR error;
+ size_t size;
+ u_char *p;
+
+ memset(&error, 0, sizeof(error));
+
+ ret = decode_KRB_ERROR(reply, len, &error, &size);
+ if (ret)
+ return ret;
+
+ if (error.e_data->length < 2) {
+ str2data(result_string, "server %s sent too short "
+ "e_data to print anything usable", host);
+ free_KRB_ERROR(&error);
+ *result_code = KRB5_KPASSWD_MALFORMED;
+ return 0;
+ }
+
+ p = error.e_data->data;
+ *result_code = (p[0] << 8) | p[1];
+ if (error.e_data->length == 2)
+ str2data(result_string, "server only sent error code");
+ else
+ krb5_data_copy (result_string,
+ p + 2,
+ error.e_data->length - 2);
+ free_KRB_ERROR(&error);
+ return 0;
+ }
+
+ if (pkt_len != len) {
+ str2data (result_string, "client: wrong len in reply");
+ *result_code = KRB5_KPASSWD_MALFORMED;
+ return 0;
+ }
+ if (pkt_ver != KRB5_KPASSWD_VERS_CHANGEPW) {
+ str2data (result_string,
+ "client: wrong version number (%d)", pkt_ver);
+ *result_code = KRB5_KPASSWD_MALFORMED;
+ return 0;
+ }
+
+ ap_rep_data.data = reply + 6;
+ ap_rep_data.length = (reply[4] << 8) | (reply[5]);
+
+ if (reply + len < (u_char *)ap_rep_data.data + ap_rep_data.length) {
+ str2data (result_string, "client: wrong AP len in reply");
+ *result_code = KRB5_KPASSWD_MALFORMED;
+ return 0;
+ }
+
+ if (ap_rep_data.length) {
+ krb5_ap_rep_enc_part *ap_rep;
+ krb5_data priv_data;
+ u_char *p;
+
+ priv_data.data = (u_char*)ap_rep_data.data + ap_rep_data.length;
+ priv_data.length = len - ap_rep_data.length - 6;
+
+ ret = krb5_rd_rep (context,
+ auth_context,
+ &ap_rep_data,
+ &ap_rep);
+ if (ret)
+ return ret;
+
+ krb5_free_ap_rep_enc_part (context, ap_rep);
+
+ ret = krb5_rd_priv (context,
+ auth_context,
+ &priv_data,
+ result_code_string,
+ NULL);
+ if (ret) {
+ krb5_data_free (result_code_string);
+ return ret;
+ }
+
+ if (result_code_string->length < 2) {
+ *result_code = KRB5_KPASSWD_MALFORMED;
+ str2data (result_string,
+ "client: bad length in result");
+ return 0;
+ }
+
+ p = result_code_string->data;
+
+ *result_code = (p[0] << 8) | p[1];
+ krb5_data_copy (result_string,
+ (unsigned char*)result_code_string->data + 2,
+ result_code_string->length - 2);
+ return 0;
+ } else {
+ KRB_ERROR error;
+ size_t size;
+ u_char *p;
+
+ ret = decode_KRB_ERROR(reply + 6, len - 6, &error, &size);
+ if (ret) {
+ return ret;
+ }
+ if (error.e_data->length < 2) {
+ krb5_warnx (context, "too short e_data to print anything usable");
+ return 1; /* XXX */
+ }
+
+ p = error.e_data->data;
+ *result_code = (p[0] << 8) | p[1];
+ krb5_data_copy (result_string,
+ p + 2,
+ error.e_data->length - 2);
+ return 0;
+ }
+}
+
+
+/*
+ * change the password using the credentials in `creds' (for the
+ * principal indicated in them) to `newpw', storing the result of
+ * the operation in `result_*' and an error code or 0.
+ */
+
+typedef krb5_error_code (*kpwd_send_request) (krb5_context,
+ krb5_auth_context *,
+ krb5_creds *,
+ krb5_principal,
+ int,
+ int,
+ const char *,
+ const char *);
+typedef krb5_error_code (*kpwd_process_reply) (krb5_context,
+ krb5_auth_context,
+ int,
+ int,
+ int *,
+ krb5_data *,
+ krb5_data *,
+ const char *);
+
+static struct kpwd_proc {
+ const char *name;
+ int flags;
+#define SUPPORT_TCP 1
+#define SUPPORT_UDP 2
+ kpwd_send_request send_req;
+ kpwd_process_reply process_rep;
+} procs[] = {
+ {
+ "MS set password",
+ SUPPORT_TCP|SUPPORT_UDP,
+ setpw_send_request,
+ process_reply
+ },
+ {
+ "change password",
+ SUPPORT_UDP,
+ chgpw_send_request,
+ process_reply
+ },
+ { NULL }
+};
+
+/*
+ *
+ */
+
+static krb5_error_code
+change_password_loop (krb5_context context,
+ krb5_creds *creds,
+ krb5_principal targprinc,
+ const char *newpw,
+ int *result_code,
+ krb5_data *result_code_string,
+ krb5_data *result_string,
+ struct kpwd_proc *proc)
+{
+ krb5_error_code ret;
+ krb5_auth_context auth_context = NULL;
+ krb5_krbhst_handle handle = NULL;
+ krb5_krbhst_info *hi;
+ int sock;
+ unsigned int i;
+ int done = 0;
+ krb5_realm realm;
+
+ if (targprinc)
+ realm = targprinc->realm;
+ else
+ realm = creds->client->realm;
+
+ ret = krb5_auth_con_init (context, &auth_context);
+ if (ret)
+ return ret;
+
+ krb5_auth_con_setflags (context, auth_context,
+ KRB5_AUTH_CONTEXT_DO_SEQUENCE);
+
+ ret = krb5_krbhst_init (context, realm, KRB5_KRBHST_CHANGEPW, &handle);
+ if (ret)
+ goto out;
+
+ while (!done && (ret = krb5_krbhst_next(context, handle, &hi)) == 0) {
+ struct addrinfo *ai, *a;
+ int is_stream;
+
+ switch (hi->proto) {
+ case KRB5_KRBHST_UDP:
+ if ((proc->flags & SUPPORT_UDP) == 0)
+ continue;
+ is_stream = 0;
+ break;
+ case KRB5_KRBHST_TCP:
+ if ((proc->flags & SUPPORT_TCP) == 0)
+ continue;
+ is_stream = 1;
+ break;
+ default:
+ continue;
+ }
+
+ ret = krb5_krbhst_get_addrinfo(context, hi, &ai);
+ if (ret)
+ continue;
+
+ for (a = ai; !done && a != NULL; a = a->ai_next) {
+ int replied = 0;
+
+ sock = socket (a->ai_family, a->ai_socktype | SOCK_CLOEXEC, a->ai_protocol);
+ if (sock < 0)
+ continue;
+ rk_cloexec(sock);
+
+ ret = connect(sock, a->ai_addr, a->ai_addrlen);
+ if (ret < 0) {
+ close (sock);
+ goto out;
+ }
+
+ ret = krb5_auth_con_genaddrs (context, auth_context, sock,
+ KRB5_AUTH_CONTEXT_GENERATE_LOCAL_ADDR);
+ if (ret) {
+ close (sock);
+ goto out;
+ }
+
+ for (i = 0; !done && i < 5; ++i) {
+ fd_set fdset;
+ struct timeval tv;
+
+ if (!replied) {
+ replied = 0;
+
+ ret = (*proc->send_req) (context,
+ &auth_context,
+ creds,
+ targprinc,
+ is_stream,
+ sock,
+ newpw,
+ hi->hostname);
+ if (ret) {
+ close(sock);
+ goto out;
+ }
+ }
+
+ if (sock >= FD_SETSIZE) {
+ ret = ERANGE;
+ krb5_set_error_message(context, ret,
+ "fd %d too large", sock);
+ close (sock);
+ goto out;
+ }
+
+ FD_ZERO(&fdset);
+ FD_SET(sock, &fdset);
+ tv.tv_usec = 0;
+ tv.tv_sec = 1 + (1 << i);
+
+ ret = select (sock + 1, &fdset, NULL, NULL, &tv);
+ if (ret < 0 && errno != EINTR) {
+ close(sock);
+ goto out;
+ }
+ if (ret == 1) {
+ ret = (*proc->process_rep) (context,
+ auth_context,
+ is_stream,
+ sock,
+ result_code,
+ result_code_string,
+ result_string,
+ hi->hostname);
+ if (ret == 0)
+ done = 1;
+ else if (i > 0 && ret == KRB5KRB_AP_ERR_MUT_FAIL)
+ replied = 1;
+ } else {
+ ret = KRB5_KDC_UNREACH;
+ }
+ }
+ close (sock);
+ }
+ }
+
+ out:
+ krb5_krbhst_free (context, handle);
+ krb5_auth_con_free (context, auth_context);
+
+ if (ret == KRB5_KDC_UNREACH) {
+ krb5_set_error_message(context,
+ ret,
+ N_("Unable to reach any changepw server "
+ " in realm %s", "realm"), realm);
+ *result_code = KRB5_KPASSWD_HARDERROR;
+ }
+ return ret;
+}
+
+#ifndef HEIMDAL_SMALLER
+
+static struct kpwd_proc *
+find_chpw_proto(const char *name)
+{
+ struct kpwd_proc *p;
+ for (p = procs; p->name != NULL; p++) {
+ if (strcmp(p->name, name) == 0)
+ return p;
+ }
+ return NULL;
+}
+
+/**
+ * krb5_change_password() is deprecated, use krb5_set_password().
+ *
+ * @param context a Keberos context
+ * @param creds
+ * @param newpw
+ * @param result_code
+ * @param result_code_string
+ * @param result_string
+ *
+ * @return On sucess password is changed.
+
+ * @ingroup @krb5_deprecated
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_change_password (krb5_context context,
+ krb5_creds *creds,
+ const char *newpw,
+ int *result_code,
+ krb5_data *result_code_string,
+ krb5_data *result_string)
+ __attribute__((deprecated))
+{
+ struct kpwd_proc *p = find_chpw_proto("change password");
+
+ *result_code = KRB5_KPASSWD_MALFORMED;
+ result_code_string->data = result_string->data = NULL;
+ result_code_string->length = result_string->length = 0;
+
+ if (p == NULL)
+ return KRB5_KPASSWD_MALFORMED;
+
+ return change_password_loop(context, creds, NULL, newpw,
+ result_code, result_code_string,
+ result_string, p);
+}
+#endif /* HEIMDAL_SMALLER */
+
+/**
+ * Change passwrod using creds.
+ *
+ * @param context a Keberos context
+ * @param creds The initial kadmin/passwd for the principal or an admin principal
+ * @param newpw The new password to set
+ * @param targprinc if unset, the default principal is used.
+ * @param result_code Result code, KRB5_KPASSWD_SUCCESS is when password is changed.
+ * @param result_code_string binary message from the server, contains
+ * at least the result_code.
+ * @param result_string A message from the kpasswd service or the
+ * library in human printable form. The string is NUL terminated.
+ *
+ * @return On sucess and *result_code is KRB5_KPASSWD_SUCCESS, the password is changed.
+
+ * @ingroup @krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_set_password(krb5_context context,
+ krb5_creds *creds,
+ const char *newpw,
+ krb5_principal targprinc,
+ int *result_code,
+ krb5_data *result_code_string,
+ krb5_data *result_string)
+{
+ krb5_principal principal = NULL;
+ krb5_error_code ret = 0;
+ int i;
+
+ *result_code = KRB5_KPASSWD_MALFORMED;
+ krb5_data_zero(result_code_string);
+ krb5_data_zero(result_string);
+
+ if (targprinc == NULL) {
+ ret = krb5_get_default_principal(context, &principal);
+ if (ret)
+ return ret;
+ } else
+ principal = targprinc;
+
+ for (i = 0; procs[i].name != NULL; i++) {
+ *result_code = 0;
+ ret = change_password_loop(context, creds, principal, newpw,
+ result_code, result_code_string,
+ result_string,
+ &procs[i]);
+ if (ret == 0 && *result_code == 0)
+ break;
+ }
+
+ if (targprinc == NULL)
+ krb5_free_principal(context, principal);
+ return ret;
+}
+
+#ifndef HEIMDAL_SMALLER
+
+/*
+ *
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_set_password_using_ccache(krb5_context context,
+ krb5_ccache ccache,
+ const char *newpw,
+ krb5_principal targprinc,
+ int *result_code,
+ krb5_data *result_code_string,
+ krb5_data *result_string)
+{
+ krb5_creds creds, *credsp;
+ krb5_error_code ret;
+ krb5_principal principal = NULL;
+
+ *result_code = KRB5_KPASSWD_MALFORMED;
+ result_code_string->data = result_string->data = NULL;
+ result_code_string->length = result_string->length = 0;
+
+ memset(&creds, 0, sizeof(creds));
+
+ if (targprinc == NULL) {
+ ret = krb5_cc_get_principal(context, ccache, &principal);
+ if (ret)
+ return ret;
+ } else
+ principal = targprinc;
+
+ ret = krb5_make_principal(context, &creds.server,
+ krb5_principal_get_realm(context, principal),
+ "kadmin", "changepw", NULL);
+ if (ret)
+ goto out;
+
+ ret = krb5_cc_get_principal(context, ccache, &creds.client);
+ if (ret) {
+ krb5_free_principal(context, creds.server);
+ goto out;
+ }
+
+ ret = krb5_get_credentials(context, 0, ccache, &creds, &credsp);
+ krb5_free_principal(context, creds.server);
+ krb5_free_principal(context, creds.client);
+ if (ret)
+ goto out;
+
+ ret = krb5_set_password(context,
+ credsp,
+ newpw,
+ principal,
+ result_code,
+ result_code_string,
+ result_string);
+
+ krb5_free_creds(context, credsp);
+
+ return ret;
+ out:
+ if (targprinc == NULL)
+ krb5_free_principal(context, principal);
+ return ret;
+}
+
+#endif /* !HEIMDAL_SMALLER */
+
+/*
+ *
+ */
+
+const char* KRB5_LIB_FUNCTION
+krb5_passwd_result_to_string (krb5_context context,
+ int result)
+{
+ static const char *strings[] = {
+ "Success",
+ "Malformed",
+ "Hard error",
+ "Auth error",
+ "Soft error" ,
+ "Access denied",
+ "Bad version",
+ "Initial flag needed"
+ };
+
+ if (result < 0 || result > KRB5_KPASSWD_INITIAL_FLAG_NEEDED)
+ return "unknown result code";
+ else
+ return strings[result];
+}
diff --git a/source4/heimdal/lib/krb5/codec.c b/source4/heimdal/lib/krb5/codec.c
new file mode 100644
index 0000000000..bd0dcc5371
--- /dev/null
+++ b/source4/heimdal/lib/krb5/codec.c
@@ -0,0 +1,219 @@
+/*
+ * Copyright (c) 1998 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+#undef __attribute__
+#define __attribute__(x)
+
+RCSID("$Id$");
+
+#ifndef HEIMDAL_SMALLER
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_decode_EncTicketPart (krb5_context context,
+ const void *data,
+ size_t length,
+ EncTicketPart *t,
+ size_t *len)
+ __attribute__((deprecated))
+{
+ return decode_EncTicketPart(data, length, t, len);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_encode_EncTicketPart (krb5_context context,
+ void *data,
+ size_t length,
+ EncTicketPart *t,
+ size_t *len)
+ __attribute__((deprecated))
+{
+ return encode_EncTicketPart(data, length, t, len);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_decode_EncASRepPart (krb5_context context,
+ const void *data,
+ size_t length,
+ EncASRepPart *t,
+ size_t *len)
+ __attribute__((deprecated))
+{
+ return decode_EncASRepPart(data, length, t, len);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_encode_EncASRepPart (krb5_context context,
+ void *data,
+ size_t length,
+ EncASRepPart *t,
+ size_t *len)
+ __attribute__((deprecated))
+{
+ return encode_EncASRepPart(data, length, t, len);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_decode_EncTGSRepPart (krb5_context context,
+ const void *data,
+ size_t length,
+ EncTGSRepPart *t,
+ size_t *len)
+ __attribute__((deprecated))
+{
+ return decode_EncTGSRepPart(data, length, t, len);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_encode_EncTGSRepPart (krb5_context context,
+ void *data,
+ size_t length,
+ EncTGSRepPart *t,
+ size_t *len)
+ __attribute__((deprecated))
+{
+ return encode_EncTGSRepPart(data, length, t, len);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_decode_EncAPRepPart (krb5_context context,
+ const void *data,
+ size_t length,
+ EncAPRepPart *t,
+ size_t *len)
+ __attribute__((deprecated))
+{
+ return decode_EncAPRepPart(data, length, t, len);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_encode_EncAPRepPart (krb5_context context,
+ void *data,
+ size_t length,
+ EncAPRepPart *t,
+ size_t *len)
+ __attribute__((deprecated))
+{
+ return encode_EncAPRepPart(data, length, t, len);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_decode_Authenticator (krb5_context context,
+ const void *data,
+ size_t length,
+ Authenticator *t,
+ size_t *len)
+ __attribute__((deprecated))
+{
+ return decode_Authenticator(data, length, t, len);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_encode_Authenticator (krb5_context context,
+ void *data,
+ size_t length,
+ Authenticator *t,
+ size_t *len)
+ __attribute__((deprecated))
+{
+ return encode_Authenticator(data, length, t, len);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_decode_EncKrbCredPart (krb5_context context,
+ const void *data,
+ size_t length,
+ EncKrbCredPart *t,
+ size_t *len)
+ __attribute__((deprecated))
+{
+ return decode_EncKrbCredPart(data, length, t, len);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_encode_EncKrbCredPart (krb5_context context,
+ void *data,
+ size_t length,
+ EncKrbCredPart *t,
+ size_t *len)
+ __attribute__((deprecated))
+{
+ return encode_EncKrbCredPart (data, length, t, len);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_decode_ETYPE_INFO (krb5_context context,
+ const void *data,
+ size_t length,
+ ETYPE_INFO *t,
+ size_t *len)
+ __attribute__((deprecated))
+{
+ return decode_ETYPE_INFO(data, length, t, len);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_encode_ETYPE_INFO (krb5_context context,
+ void *data,
+ size_t length,
+ ETYPE_INFO *t,
+ size_t *len)
+ __attribute__((deprecated))
+{
+ return encode_ETYPE_INFO (data, length, t, len);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_decode_ETYPE_INFO2 (krb5_context context,
+ const void *data,
+ size_t length,
+ ETYPE_INFO2 *t,
+ size_t *len)
+ __attribute__((deprecated))
+{
+ return decode_ETYPE_INFO2(data, length, t, len);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_encode_ETYPE_INFO2 (krb5_context context,
+ void *data,
+ size_t length,
+ ETYPE_INFO2 *t,
+ size_t *len)
+ __attribute__((deprecated))
+{
+ return encode_ETYPE_INFO2 (data, length, t, len);
+}
+
+#endif /* HEIMDAL_SMALLER */
diff --git a/source4/heimdal/lib/krb5/config_file.c b/source4/heimdal/lib/krb5/config_file.c
new file mode 100644
index 0000000000..75c48a001b
--- /dev/null
+++ b/source4/heimdal/lib/krb5/config_file.c
@@ -0,0 +1,809 @@
+/*
+ * Copyright (c) 1997 - 2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+RCSID("$Id$");
+
+#ifndef HAVE_NETINFO
+
+/* Gaah! I want a portable funopen */
+struct fileptr {
+ const char *s;
+ FILE *f;
+};
+
+static char *
+config_fgets(char *str, size_t len, struct fileptr *ptr)
+{
+ /* XXX this is not correct, in that they don't do the same if the
+ line is longer than len */
+ if(ptr->f != NULL)
+ return fgets(str, len, ptr->f);
+ else {
+ /* this is almost strsep_copy */
+ const char *p;
+ ssize_t l;
+ if(*ptr->s == '\0')
+ return NULL;
+ p = ptr->s + strcspn(ptr->s, "\n");
+ if(*p == '\n')
+ p++;
+ l = min(len, p - ptr->s);
+ if(len > 0) {
+ memcpy(str, ptr->s, l);
+ str[l] = '\0';
+ }
+ ptr->s = p;
+ return str;
+ }
+}
+
+static krb5_error_code parse_section(char *p, krb5_config_section **s,
+ krb5_config_section **res,
+ const char **error_message);
+static krb5_error_code parse_binding(struct fileptr *f, unsigned *lineno, char *p,
+ krb5_config_binding **b,
+ krb5_config_binding **parent,
+ const char **error_message);
+static krb5_error_code parse_list(struct fileptr *f, unsigned *lineno,
+ krb5_config_binding **parent,
+ const char **error_message);
+
+static krb5_config_section *
+get_entry(krb5_config_section **parent, const char *name, int type)
+{
+ krb5_config_section **q;
+
+ for(q = parent; *q != NULL; q = &(*q)->next)
+ if(type == krb5_config_list &&
+ type == (*q)->type &&
+ strcmp(name, (*q)->name) == 0)
+ return *q;
+ *q = calloc(1, sizeof(**q));
+ if(*q == NULL)
+ return NULL;
+ (*q)->name = strdup(name);
+ (*q)->type = type;
+ if((*q)->name == NULL) {
+ free(*q);
+ *q = NULL;
+ return NULL;
+ }
+ return *q;
+}
+
+/*
+ * Parse a section:
+ *
+ * [section]
+ * foo = bar
+ * b = {
+ * a
+ * }
+ * ...
+ *
+ * starting at the line in `p', storing the resulting structure in
+ * `s' and hooking it into `parent'.
+ * Store the error message in `error_message'.
+ */
+
+static krb5_error_code
+parse_section(char *p, krb5_config_section **s, krb5_config_section **parent,
+ const char **error_message)
+{
+ char *p1;
+ krb5_config_section *tmp;
+
+ p1 = strchr (p + 1, ']');
+ if (p1 == NULL) {
+ *error_message = "missing ]";
+ return KRB5_CONFIG_BADFORMAT;
+ }
+ *p1 = '\0';
+ tmp = get_entry(parent, p + 1, krb5_config_list);
+ if(tmp == NULL) {
+ *error_message = "out of memory";
+ return KRB5_CONFIG_BADFORMAT;
+ }
+ *s = tmp;
+ return 0;
+}
+
+/*
+ * Parse a brace-enclosed list from `f', hooking in the structure at
+ * `parent'.
+ * Store the error message in `error_message'.
+ */
+
+static krb5_error_code
+parse_list(struct fileptr *f, unsigned *lineno, krb5_config_binding **parent,
+ const char **error_message)
+{
+ char buf[BUFSIZ];
+ krb5_error_code ret;
+ krb5_config_binding *b = NULL;
+ unsigned beg_lineno = *lineno;
+
+ while(config_fgets(buf, sizeof(buf), f) != NULL) {
+ char *p;
+
+ ++*lineno;
+ buf[strcspn(buf, "\r\n")] = '\0';
+ p = buf;
+ while(isspace((unsigned char)*p))
+ ++p;
+ if (*p == '#' || *p == ';' || *p == '\0')
+ continue;
+ while(isspace((unsigned char)*p))
+ ++p;
+ if (*p == '}')
+ return 0;
+ if (*p == '\0')
+ continue;
+ ret = parse_binding (f, lineno, p, &b, parent, error_message);
+ if (ret)
+ return ret;
+ }
+ *lineno = beg_lineno;
+ *error_message = "unclosed {";
+ return KRB5_CONFIG_BADFORMAT;
+}
+
+/*
+ *
+ */
+
+static krb5_error_code
+parse_binding(struct fileptr *f, unsigned *lineno, char *p,
+ krb5_config_binding **b, krb5_config_binding **parent,
+ const char **error_message)
+{
+ krb5_config_binding *tmp;
+ char *p1, *p2;
+ krb5_error_code ret = 0;
+
+ p1 = p;
+ while (*p && *p != '=' && !isspace((unsigned char)*p))
+ ++p;
+ if (*p == '\0') {
+ *error_message = "missing =";
+ return KRB5_CONFIG_BADFORMAT;
+ }
+ p2 = p;
+ while (isspace((unsigned char)*p))
+ ++p;
+ if (*p != '=') {
+ *error_message = "missing =";
+ return KRB5_CONFIG_BADFORMAT;
+ }
+ ++p;
+ while(isspace((unsigned char)*p))
+ ++p;
+ *p2 = '\0';
+ if (*p == '{') {
+ tmp = get_entry(parent, p1, krb5_config_list);
+ if (tmp == NULL) {
+ *error_message = "out of memory";
+ return KRB5_CONFIG_BADFORMAT;
+ }
+ ret = parse_list (f, lineno, &tmp->u.list, error_message);
+ } else {
+ tmp = get_entry(parent, p1, krb5_config_string);
+ if (tmp == NULL) {
+ *error_message = "out of memory";
+ return KRB5_CONFIG_BADFORMAT;
+ }
+ p1 = p;
+ p = p1 + strlen(p1);
+ while(p > p1 && isspace((unsigned char)*(p-1)))
+ --p;
+ *p = '\0';
+ tmp->u.string = strdup(p1);
+ }
+ *b = tmp;
+ return ret;
+}
+
+/*
+ * Parse the config file `fname', generating the structures into `res'
+ * returning error messages in `error_message'
+ */
+
+static krb5_error_code
+krb5_config_parse_debug (struct fileptr *f,
+ krb5_config_section **res,
+ unsigned *lineno,
+ const char **error_message)
+{
+ krb5_config_section *s = NULL;
+ krb5_config_binding *b = NULL;
+ char buf[BUFSIZ];
+ krb5_error_code ret;
+
+ while (config_fgets(buf, sizeof(buf), f) != NULL) {
+ char *p;
+
+ ++*lineno;
+ buf[strcspn(buf, "\r\n")] = '\0';
+ p = buf;
+ while(isspace((unsigned char)*p))
+ ++p;
+ if (*p == '#' || *p == ';')
+ continue;
+ if (*p == '[') {
+ ret = parse_section(p, &s, res, error_message);
+ if (ret)
+ return ret;
+ b = NULL;
+ } else if (*p == '}') {
+ *error_message = "unmatched }";
+ return EINVAL; /* XXX */
+ } else if(*p != '\0') {
+ if (s == NULL) {
+ *error_message = "binding before section";
+ return EINVAL;
+ }
+ ret = parse_binding(f, lineno, p, &b, &s->u.list, error_message);
+ if (ret)
+ return ret;
+ }
+ }
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_config_parse_string_multi(krb5_context context,
+ const char *string,
+ krb5_config_section **res)
+{
+ const char *str;
+ unsigned lineno = 0;
+ krb5_error_code ret;
+ struct fileptr f;
+ f.f = NULL;
+ f.s = string;
+
+ ret = krb5_config_parse_debug (&f, res, &lineno, &str);
+ if (ret) {
+ krb5_set_error_message (context, ret, "%s:%u: %s",
+ "<constant>", lineno, str);
+ return ret;
+ }
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_config_parse_file_multi (krb5_context context,
+ const char *fname,
+ krb5_config_section **res)
+{
+ const char *str;
+ unsigned lineno = 0;
+ krb5_error_code ret;
+ struct fileptr f;
+ f.f = fopen(fname, "r");
+ f.s = NULL;
+ if(f.f == NULL) {
+ ret = errno;
+ krb5_set_error_message (context, ret, "open %s: %s",
+ fname, strerror(ret));
+ return ret;
+ }
+
+ ret = krb5_config_parse_debug (&f, res, &lineno, &str);
+ fclose(f.f);
+ if (ret) {
+ krb5_set_error_message (context, ret, "%s:%u: %s", fname, lineno, str);
+ return ret;
+ }
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_config_parse_file (krb5_context context,
+ const char *fname,
+ krb5_config_section **res)
+{
+ *res = NULL;
+ return krb5_config_parse_file_multi(context, fname, res);
+}
+
+#endif /* !HAVE_NETINFO */
+
+static void
+free_binding (krb5_context context, krb5_config_binding *b)
+{
+ krb5_config_binding *next_b;
+
+ while (b) {
+ free (b->name);
+ if (b->type == krb5_config_string)
+ free (b->u.string);
+ else if (b->type == krb5_config_list)
+ free_binding (context, b->u.list);
+ else
+ krb5_abortx(context, "unknown binding type (%d) in free_binding",
+ b->type);
+ next_b = b->next;
+ free (b);
+ b = next_b;
+ }
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_config_file_free (krb5_context context, krb5_config_section *s)
+{
+ free_binding (context, s);
+ return 0;
+}
+
+krb5_error_code
+_krb5_config_copy(krb5_context context,
+ krb5_config_section *c,
+ krb5_config_section **head)
+{
+ krb5_config_binding *d, *previous = NULL;
+
+ *head = NULL;
+
+ while (c) {
+ d = calloc(1, sizeof(*d));
+
+ if (*head == NULL)
+ *head = d;
+
+ d->name = strdup(c->name);
+ d->type = c->type;
+ if (d->type == krb5_config_string)
+ d->u.string = strdup(c->u.string);
+ else if (d->type == krb5_config_list)
+ _krb5_config_copy (context, c->u.list, &d->u.list);
+ else
+ krb5_abortx(context,
+ "unknown binding type (%d) in krb5_config_copy",
+ d->type);
+ if (previous)
+ previous->next = d;
+
+ previous = d;
+ c = c->next;
+ }
+ return 0;
+}
+
+
+
+const void *
+krb5_config_get_next (krb5_context context,
+ const krb5_config_section *c,
+ const krb5_config_binding **pointer,
+ int type,
+ ...)
+{
+ const char *ret;
+ va_list args;
+
+ va_start(args, type);
+ ret = krb5_config_vget_next (context, c, pointer, type, args);
+ va_end(args);
+ return ret;
+}
+
+static const void *
+vget_next(krb5_context context,
+ const krb5_config_binding *b,
+ const krb5_config_binding **pointer,
+ int type,
+ const char *name,
+ va_list args)
+{
+ const char *p = va_arg(args, const char *);
+ while(b != NULL) {
+ if(strcmp(b->name, name) == 0) {
+ if(b->type == type && p == NULL) {
+ *pointer = b;
+ return b->u.generic;
+ } else if(b->type == krb5_config_list && p != NULL) {
+ return vget_next(context, b->u.list, pointer, type, p, args);
+ }
+ }
+ b = b->next;
+ }
+ return NULL;
+}
+
+const void *
+krb5_config_vget_next (krb5_context context,
+ const krb5_config_section *c,
+ const krb5_config_binding **pointer,
+ int type,
+ va_list args)
+{
+ const krb5_config_binding *b;
+ const char *p;
+
+ if(c == NULL)
+ c = context->cf;
+
+ if (c == NULL)
+ return NULL;
+
+ if (*pointer == NULL) {
+ /* first time here, walk down the tree looking for the right
+ section */
+ p = va_arg(args, const char *);
+ if (p == NULL)
+ return NULL;
+ return vget_next(context, c, pointer, type, p, args);
+ }
+
+ /* we were called again, so just look for more entries with the
+ same name and type */
+ for (b = (*pointer)->next; b != NULL; b = b->next) {
+ if(strcmp(b->name, (*pointer)->name) == 0 && b->type == type) {
+ *pointer = b;
+ return b->u.generic;
+ }
+ }
+ return NULL;
+}
+
+const void *
+krb5_config_get (krb5_context context,
+ const krb5_config_section *c,
+ int type,
+ ...)
+{
+ const void *ret;
+ va_list args;
+
+ va_start(args, type);
+ ret = krb5_config_vget (context, c, type, args);
+ va_end(args);
+ return ret;
+}
+
+const void *
+krb5_config_vget (krb5_context context,
+ const krb5_config_section *c,
+ int type,
+ va_list args)
+{
+ const krb5_config_binding *foo = NULL;
+
+ return krb5_config_vget_next (context, c, &foo, type, args);
+}
+
+const krb5_config_binding *
+krb5_config_get_list (krb5_context context,
+ const krb5_config_section *c,
+ ...)
+{
+ const krb5_config_binding *ret;
+ va_list args;
+
+ va_start(args, c);
+ ret = krb5_config_vget_list (context, c, args);
+ va_end(args);
+ return ret;
+}
+
+const krb5_config_binding *
+krb5_config_vget_list (krb5_context context,
+ const krb5_config_section *c,
+ va_list args)
+{
+ return krb5_config_vget (context, c, krb5_config_list, args);
+}
+
+const char* KRB5_LIB_FUNCTION
+krb5_config_get_string (krb5_context context,
+ const krb5_config_section *c,
+ ...)
+{
+ const char *ret;
+ va_list args;
+
+ va_start(args, c);
+ ret = krb5_config_vget_string (context, c, args);
+ va_end(args);
+ return ret;
+}
+
+const char* KRB5_LIB_FUNCTION
+krb5_config_vget_string (krb5_context context,
+ const krb5_config_section *c,
+ va_list args)
+{
+ return krb5_config_vget (context, c, krb5_config_string, args);
+}
+
+const char* KRB5_LIB_FUNCTION
+krb5_config_vget_string_default (krb5_context context,
+ const krb5_config_section *c,
+ const char *def_value,
+ va_list args)
+{
+ const char *ret;
+
+ ret = krb5_config_vget_string (context, c, args);
+ if (ret == NULL)
+ ret = def_value;
+ return ret;
+}
+
+const char* KRB5_LIB_FUNCTION
+krb5_config_get_string_default (krb5_context context,
+ const krb5_config_section *c,
+ const char *def_value,
+ ...)
+{
+ const char *ret;
+ va_list args;
+
+ va_start(args, def_value);
+ ret = krb5_config_vget_string_default (context, c, def_value, args);
+ va_end(args);
+ return ret;
+}
+
+char ** KRB5_LIB_FUNCTION
+krb5_config_vget_strings(krb5_context context,
+ const krb5_config_section *c,
+ va_list args)
+{
+ char **strings = NULL;
+ int nstr = 0;
+ const krb5_config_binding *b = NULL;
+ const char *p;
+
+ while((p = krb5_config_vget_next(context, c, &b,
+ krb5_config_string, args))) {
+ char *tmp = strdup(p);
+ char *pos = NULL;
+ char *s;
+ if(tmp == NULL)
+ goto cleanup;
+ s = strtok_r(tmp, " \t", &pos);
+ while(s){
+ char **tmp2 = realloc(strings, (nstr + 1) * sizeof(*strings));
+ if(tmp2 == NULL)
+ goto cleanup;
+ strings = tmp2;
+ strings[nstr] = strdup(s);
+ nstr++;
+ if(strings[nstr-1] == NULL)
+ goto cleanup;
+ s = strtok_r(NULL, " \t", &pos);
+ }
+ free(tmp);
+ }
+ if(nstr){
+ char **tmp = realloc(strings, (nstr + 1) * sizeof(*strings));
+ if(tmp == NULL)
+ goto cleanup;
+ strings = tmp;
+ strings[nstr] = NULL;
+ }
+ return strings;
+cleanup:
+ while(nstr--)
+ free(strings[nstr]);
+ free(strings);
+ return NULL;
+
+}
+
+char**
+krb5_config_get_strings(krb5_context context,
+ const krb5_config_section *c,
+ ...)
+{
+ va_list ap;
+ char **ret;
+ va_start(ap, c);
+ ret = krb5_config_vget_strings(context, c, ap);
+ va_end(ap);
+ return ret;
+}
+
+void KRB5_LIB_FUNCTION
+krb5_config_free_strings(char **strings)
+{
+ char **s = strings;
+ while(s && *s){
+ free(*s);
+ s++;
+ }
+ free(strings);
+}
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_config_vget_bool_default (krb5_context context,
+ const krb5_config_section *c,
+ krb5_boolean def_value,
+ va_list args)
+{
+ const char *str;
+ str = krb5_config_vget_string (context, c, args);
+ if(str == NULL)
+ return def_value;
+ if(strcasecmp(str, "yes") == 0 ||
+ strcasecmp(str, "true") == 0 ||
+ atoi(str)) return TRUE;
+ return FALSE;
+}
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_config_vget_bool (krb5_context context,
+ const krb5_config_section *c,
+ va_list args)
+{
+ return krb5_config_vget_bool_default (context, c, FALSE, args);
+}
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_config_get_bool_default (krb5_context context,
+ const krb5_config_section *c,
+ krb5_boolean def_value,
+ ...)
+{
+ va_list ap;
+ krb5_boolean ret;
+ va_start(ap, def_value);
+ ret = krb5_config_vget_bool_default(context, c, def_value, ap);
+ va_end(ap);
+ return ret;
+}
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_config_get_bool (krb5_context context,
+ const krb5_config_section *c,
+ ...)
+{
+ va_list ap;
+ krb5_boolean ret;
+ va_start(ap, c);
+ ret = krb5_config_vget_bool (context, c, ap);
+ va_end(ap);
+ return ret;
+}
+
+int KRB5_LIB_FUNCTION
+krb5_config_vget_time_default (krb5_context context,
+ const krb5_config_section *c,
+ int def_value,
+ va_list args)
+{
+ const char *str;
+ krb5_deltat t;
+
+ str = krb5_config_vget_string (context, c, args);
+ if(str == NULL)
+ return def_value;
+ if (krb5_string_to_deltat(str, &t))
+ return def_value;
+ return t;
+}
+
+int KRB5_LIB_FUNCTION
+krb5_config_vget_time (krb5_context context,
+ const krb5_config_section *c,
+ va_list args)
+{
+ return krb5_config_vget_time_default (context, c, -1, args);
+}
+
+int KRB5_LIB_FUNCTION
+krb5_config_get_time_default (krb5_context context,
+ const krb5_config_section *c,
+ int def_value,
+ ...)
+{
+ va_list ap;
+ int ret;
+ va_start(ap, def_value);
+ ret = krb5_config_vget_time_default(context, c, def_value, ap);
+ va_end(ap);
+ return ret;
+}
+
+int KRB5_LIB_FUNCTION
+krb5_config_get_time (krb5_context context,
+ const krb5_config_section *c,
+ ...)
+{
+ va_list ap;
+ int ret;
+ va_start(ap, c);
+ ret = krb5_config_vget_time (context, c, ap);
+ va_end(ap);
+ return ret;
+}
+
+
+int KRB5_LIB_FUNCTION
+krb5_config_vget_int_default (krb5_context context,
+ const krb5_config_section *c,
+ int def_value,
+ va_list args)
+{
+ const char *str;
+ str = krb5_config_vget_string (context, c, args);
+ if(str == NULL)
+ return def_value;
+ else {
+ char *endptr;
+ long l;
+ l = strtol(str, &endptr, 0);
+ if (endptr == str)
+ return def_value;
+ else
+ return l;
+ }
+}
+
+int KRB5_LIB_FUNCTION
+krb5_config_vget_int (krb5_context context,
+ const krb5_config_section *c,
+ va_list args)
+{
+ return krb5_config_vget_int_default (context, c, -1, args);
+}
+
+int KRB5_LIB_FUNCTION
+krb5_config_get_int_default (krb5_context context,
+ const krb5_config_section *c,
+ int def_value,
+ ...)
+{
+ va_list ap;
+ int ret;
+ va_start(ap, def_value);
+ ret = krb5_config_vget_int_default(context, c, def_value, ap);
+ va_end(ap);
+ return ret;
+}
+
+int KRB5_LIB_FUNCTION
+krb5_config_get_int (krb5_context context,
+ const krb5_config_section *c,
+ ...)
+{
+ va_list ap;
+ int ret;
+ va_start(ap, c);
+ ret = krb5_config_vget_int (context, c, ap);
+ va_end(ap);
+ return ret;
+}
diff --git a/source4/heimdal/lib/krb5/config_file_netinfo.c b/source4/heimdal/lib/krb5/config_file_netinfo.c
new file mode 100644
index 0000000000..e6993bbb4a
--- /dev/null
+++ b/source4/heimdal/lib/krb5/config_file_netinfo.c
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+RCSID("$Id$");
+
+/*
+ * Netinfo implementation from Luke Howard <lukeh@xedoc.com.au>
+ */
+
+#ifdef HAVE_NETINFO
+#include <netinfo/ni.h>
+static ni_status
+ni_proplist2binding(ni_proplist *pl, krb5_config_section **ret)
+{
+ int i, j;
+ krb5_config_section **next = NULL;
+
+ for (i = 0; i < pl->ni_proplist_len; i++) {
+ if (!strcmp(pl->nipl_val[i].nip_name, "name"))
+ continue;
+
+ for (j = 0; j < pl->nipl_val[i].nip_val.ni_namelist_len; j++) {
+ krb5_config_binding *b;
+
+ b = malloc(sizeof(*b));
+ if (b == NULL)
+ return NI_FAILED;
+
+ b->next = NULL;
+ b->type = krb5_config_string;
+ b->name = ni_name_dup(pl->nipl_val[i].nip_name);
+ b->u.string = ni_name_dup(pl->nipl_val[i].nip_val.ninl_val[j]);
+
+ if (next == NULL) {
+ *ret = b;
+ } else {
+ *next = b;
+ }
+ next = &b->next;
+ }
+ }
+ return NI_OK;
+}
+
+static ni_status
+ni_idlist2binding(void *ni, ni_idlist *idlist, krb5_config_section **ret)
+{
+ int i;
+ ni_status nis;
+ krb5_config_section **next;
+
+ for (i = 0; i < idlist->ni_idlist_len; i++) {
+ ni_proplist pl;
+ ni_id nid;
+ ni_idlist children;
+ krb5_config_binding *b;
+ ni_index index;
+
+ nid.nii_instance = 0;
+ nid.nii_object = idlist->ni_idlist_val[i];
+
+ nis = ni_read(ni, &nid, &pl);
+
+ if (nis != NI_OK) {
+ return nis;
+ }
+ index = ni_proplist_match(pl, "name", NULL);
+ b = malloc(sizeof(*b));
+ if (b == NULL) return NI_FAILED;
+
+ if (i == 0) {
+ *ret = b;
+ } else {
+ *next = b;
+ }
+
+ b->type = krb5_config_list;
+ b->name = ni_name_dup(pl.nipl_val[index].nip_val.ninl_val[0]);
+ b->next = NULL;
+ b->u.list = NULL;
+
+ /* get the child directories */
+ nis = ni_children(ni, &nid, &children);
+ if (nis == NI_OK) {
+ nis = ni_idlist2binding(ni, &children, &b->u.list);
+ if (nis != NI_OK) {
+ return nis;
+ }
+ }
+
+ nis = ni_proplist2binding(&pl, b->u.list == NULL ? &b->u.list : &b->u.list->next);
+ ni_proplist_free(&pl);
+ if (nis != NI_OK) {
+ return nis;
+ }
+ next = &b->next;
+ }
+ ni_idlist_free(idlist);
+ return NI_OK;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_config_parse_file (krb5_context context,
+ const char *fname,
+ krb5_config_section **res)
+{
+ void *ni = NULL, *lastni = NULL;
+ int i;
+ ni_status nis;
+ ni_id nid;
+ ni_idlist children;
+
+ krb5_config_section *s;
+ int ret;
+
+ s = NULL;
+
+ for (i = 0; i < 256; i++) {
+ if (i == 0) {
+ nis = ni_open(NULL, ".", &ni);
+ } else {
+ if (lastni != NULL) ni_free(lastni);
+ lastni = ni;
+ nis = ni_open(lastni, "..", &ni);
+ }
+ if (nis != NI_OK)
+ break;
+ nis = ni_pathsearch(ni, &nid, "/locations/kerberos");
+ if (nis == NI_OK) {
+ nis = ni_children(ni, &nid, &children);
+ if (nis != NI_OK)
+ break;
+ nis = ni_idlist2binding(ni, &children, &s);
+ break;
+ }
+ }
+
+ if (ni != NULL) ni_free(ni);
+ if (ni != lastni && lastni != NULL) ni_free(lastni);
+
+ ret = (nis == NI_OK) ? 0 : -1;
+ if (ret == 0) {
+ *res = s;
+ } else {
+ *res = NULL;
+ }
+ return ret;
+}
+#endif /* HAVE_NETINFO */
diff --git a/source4/heimdal/lib/krb5/constants.c b/source4/heimdal/lib/krb5/constants.c
new file mode 100644
index 0000000000..b41fb3f663
--- /dev/null
+++ b/source4/heimdal/lib/krb5/constants.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 1997-2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+KRB5_LIB_VARIABLE const char *krb5_config_file =
+#ifdef __APPLE__
+"/Library/Preferences/edu.mit.Kerberos:"
+#endif
+SYSCONFDIR "/krb5.conf:/etc/krb5.conf";
+KRB5_LIB_VARIABLE const char *krb5_defkeyname = KEYTAB_DEFAULT;
diff --git a/source4/heimdal/lib/krb5/context.c b/source4/heimdal/lib/krb5/context.c
new file mode 100644
index 0000000000..127dfa117d
--- /dev/null
+++ b/source4/heimdal/lib/krb5/context.c
@@ -0,0 +1,1228 @@
+/*
+ * Copyright (c) 1997 - 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+#include <com_err.h>
+
+RCSID("$Id$");
+
+#define INIT_FIELD(C, T, E, D, F) \
+ (C)->E = krb5_config_get_ ## T ## _default ((C), NULL, (D), \
+ "libdefaults", F, NULL)
+
+#define INIT_FLAG(C, O, V, D, F) \
+ do { \
+ if (krb5_config_get_bool_default((C), NULL, (D),"libdefaults", F, NULL)) { \
+ (C)->O |= V; \
+ } \
+ } while(0)
+
+/*
+ * Set the list of etypes `ret_etypes' from the configuration variable
+ * `name'
+ */
+
+static krb5_error_code
+set_etypes (krb5_context context,
+ const char *name,
+ krb5_enctype **ret_enctypes)
+{
+ char **etypes_str;
+ krb5_enctype *etypes = NULL;
+
+ etypes_str = krb5_config_get_strings(context, NULL, "libdefaults",
+ name, NULL);
+ if(etypes_str){
+ int i, j, k;
+ for(i = 0; etypes_str[i]; i++);
+ etypes = malloc((i+1) * sizeof(*etypes));
+ if (etypes == NULL) {
+ krb5_config_free_strings (etypes_str);
+ krb5_set_error_message (context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ for(j = 0, k = 0; j < i; j++) {
+ krb5_enctype e;
+ if(krb5_string_to_enctype(context, etypes_str[j], &e) != 0)
+ continue;
+ if (krb5_enctype_valid(context, e) != 0)
+ continue;
+ etypes[k++] = e;
+ }
+ etypes[k] = ETYPE_NULL;
+ krb5_config_free_strings(etypes_str);
+ }
+ *ret_enctypes = etypes;
+ return 0;
+}
+
+/*
+ *
+ */
+
+static krb5_error_code
+copy_etypes (krb5_context context,
+ krb5_enctype *enctypes,
+ krb5_enctype **ret_enctypes)
+{
+ unsigned int i;
+
+ for (i = 0; enctypes[i]; i++)
+ ;
+ i++;
+
+ *ret_enctypes = malloc(sizeof(ret_enctypes[0]) * i);
+ if (*ret_enctypes == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ memcpy(*ret_enctypes, enctypes, sizeof(ret_enctypes[0]) * i);
+ return 0;
+}
+
+
+/*
+ * read variables from the configuration file and set in `context'
+ */
+
+static krb5_error_code
+init_context_from_config_file(krb5_context context)
+{
+ krb5_error_code ret;
+ const char * tmp;
+ krb5_enctype *tmptypes;
+
+ INIT_FIELD(context, time, max_skew, 5 * 60, "clockskew");
+ INIT_FIELD(context, time, kdc_timeout, 3, "kdc_timeout");
+ INIT_FIELD(context, int, max_retries, 3, "max_retries");
+
+ INIT_FIELD(context, string, http_proxy, NULL, "http_proxy");
+
+ ret = set_etypes (context, "default_etypes", &tmptypes);
+ if(ret)
+ return ret;
+ free(context->etypes);
+ context->etypes = tmptypes;
+
+ ret = set_etypes (context, "default_etypes_des", &tmptypes);
+ if(ret)
+ return ret;
+ free(context->etypes_des);
+ context->etypes_des = tmptypes;
+
+ /* default keytab name */
+ tmp = NULL;
+ if(!issuid())
+ tmp = getenv("KRB5_KTNAME");
+ if(tmp != NULL)
+ context->default_keytab = tmp;
+ else
+ INIT_FIELD(context, string, default_keytab,
+ KEYTAB_DEFAULT, "default_keytab_name");
+
+ INIT_FIELD(context, string, default_keytab_modify,
+ NULL, "default_keytab_modify_name");
+
+ INIT_FIELD(context, string, time_fmt,
+ "%Y-%m-%dT%H:%M:%S", "time_format");
+
+ INIT_FIELD(context, string, date_fmt,
+ "%Y-%m-%d", "date_format");
+
+ INIT_FIELD(context, bool, log_utc,
+ FALSE, "log_utc");
+
+
+
+ /* init dns-proxy slime */
+ tmp = krb5_config_get_string(context, NULL, "libdefaults",
+ "dns_proxy", NULL);
+ if(tmp)
+ roken_gethostby_setup(context->http_proxy, tmp);
+ krb5_free_host_realm (context, context->default_realms);
+ context->default_realms = NULL;
+
+ {
+ krb5_addresses addresses;
+ char **adr, **a;
+
+ krb5_set_extra_addresses(context, NULL);
+ adr = krb5_config_get_strings(context, NULL,
+ "libdefaults",
+ "extra_addresses",
+ NULL);
+ memset(&addresses, 0, sizeof(addresses));
+ for(a = adr; a && *a; a++) {
+ ret = krb5_parse_address(context, *a, &addresses);
+ if (ret == 0) {
+ krb5_add_extra_addresses(context, &addresses);
+ krb5_free_addresses(context, &addresses);
+ }
+ }
+ krb5_config_free_strings(adr);
+
+ krb5_set_ignore_addresses(context, NULL);
+ adr = krb5_config_get_strings(context, NULL,
+ "libdefaults",
+ "ignore_addresses",
+ NULL);
+ memset(&addresses, 0, sizeof(addresses));
+ for(a = adr; a && *a; a++) {
+ ret = krb5_parse_address(context, *a, &addresses);
+ if (ret == 0) {
+ krb5_add_ignore_addresses(context, &addresses);
+ krb5_free_addresses(context, &addresses);
+ }
+ }
+ krb5_config_free_strings(adr);
+ }
+
+ INIT_FIELD(context, bool, scan_interfaces, TRUE, "scan_interfaces");
+ INIT_FIELD(context, int, fcache_vno, 0, "fcache_version");
+ /* prefer dns_lookup_kdc over srv_lookup. */
+ INIT_FIELD(context, bool, srv_lookup, TRUE, "srv_lookup");
+ INIT_FIELD(context, bool, srv_lookup, context->srv_lookup, "dns_lookup_kdc");
+ INIT_FIELD(context, int, large_msg_size, 1400, "large_message_size");
+ INIT_FLAG(context, flags, KRB5_CTX_F_DNS_CANONICALIZE_HOSTNAME, TRUE, "dns_canonicalize_hostname");
+ INIT_FLAG(context, flags, KRB5_CTX_F_CHECK_PAC, TRUE, "check_pac");
+ context->default_cc_name = NULL;
+ context->default_cc_name_set = 0;
+
+ ret = krb5_config_get_bool_default(context, NULL, FALSE,
+ "libdefaults",
+ "allow_weak_crypto", NULL);
+ if (ret) {
+ krb5_enctype_enable(context, ETYPE_DES_CBC_CRC);
+ krb5_enctype_enable(context, ETYPE_DES_CBC_MD4);
+ krb5_enctype_enable(context, ETYPE_DES_CBC_MD5);
+ krb5_enctype_enable(context, ETYPE_DES_CBC_NONE);
+ krb5_enctype_enable(context, ETYPE_DES_CFB64_NONE);
+ krb5_enctype_enable(context, ETYPE_DES_PCBC_NONE);
+ }
+
+ return 0;
+}
+
+static krb5_error_code
+cc_ops_register(krb5_context context)
+{
+ context->cc_ops = NULL;
+ context->num_cc_ops = 0;
+
+ krb5_cc_register(context, &krb5_acc_ops, TRUE);
+ krb5_cc_register(context, &krb5_fcc_ops, TRUE);
+ krb5_cc_register(context, &krb5_mcc_ops, TRUE);
+#ifdef HAVE_SQLITE
+ krb5_cc_register(context, &krb5_scc_ops, TRUE);
+#endif
+#ifdef HAVE_KCM
+ krb5_cc_register(context, &krb5_kcm_ops, TRUE);
+#endif
+ return 0;
+}
+
+static krb5_error_code
+kt_ops_register(krb5_context context)
+{
+ context->num_kt_types = 0;
+ context->kt_types = NULL;
+
+ krb5_kt_register (context, &krb5_fkt_ops);
+ krb5_kt_register (context, &krb5_wrfkt_ops);
+ krb5_kt_register (context, &krb5_javakt_ops);
+ krb5_kt_register (context, &krb5_mkt_ops);
+#ifndef HEIMDAL_SMALLER
+ krb5_kt_register (context, &krb5_akf_ops);
+#endif
+ krb5_kt_register (context, &krb5_any_ops);
+ return 0;
+}
+
+
+/**
+ * Initializes the context structure and reads the configuration file
+ * /etc/krb5.conf. The structure should be freed by calling
+ * krb5_free_context() when it is no longer being used.
+ *
+ * @param context pointer to returned context
+ *
+ * @return Returns 0 to indicate success. Otherwise an errno code is
+ * returned. Failure means either that something bad happened during
+ * initialization (typically ENOMEM) or that Kerberos should not be
+ * used ENXIO.
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_init_context(krb5_context *context)
+{
+ krb5_context p;
+ krb5_error_code ret;
+ char **files;
+
+ *context = NULL;
+
+ /* should have a run_once */
+#if defined(HEIMDAL_LOCALEDIR)
+ bindtextdomain(HEIMDAL_TEXTDOMAIN, HEIMDAL_LOCALEDIR);
+#endif
+
+ p = calloc(1, sizeof(*p));
+ if(!p)
+ return ENOMEM;
+
+ p->mutex = malloc(sizeof(HEIMDAL_MUTEX));
+ if (p->mutex == NULL) {
+ free(p);
+ return ENOMEM;
+ }
+ HEIMDAL_MUTEX_init(p->mutex);
+
+ ret = krb5_get_default_config_files(&files);
+ if(ret)
+ goto out;
+ ret = krb5_set_config_files(p, files);
+ krb5_free_config_files(files);
+ if(ret)
+ goto out;
+
+ /* init error tables */
+ krb5_init_ets(p);
+ cc_ops_register(p);
+ kt_ops_register(p);
+
+out:
+ if(ret) {
+ krb5_free_context(p);
+ p = NULL;
+ }
+ *context = p;
+ return ret;
+}
+
+/**
+ * Make a copy for the Kerberos 5 context, allocated krb5_contex shoud
+ * be freed with krb5_free_context().
+ *
+ * @param in the Kerberos context to copy
+ * @param out the copy of the Kerberos, set to NULL error.
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_copy_context(krb5_context context, krb5_context *out)
+{
+ krb5_error_code ret;
+ krb5_context p;
+
+ *out = NULL;
+
+ p = calloc(1, sizeof(*p));
+ if (p == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+
+ p->mutex = malloc(sizeof(HEIMDAL_MUTEX));
+ if (p->mutex == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ free(p);
+ return ENOMEM;
+ }
+ HEIMDAL_MUTEX_init(p->mutex);
+
+
+ if (context->default_cc_name)
+ p->default_cc_name = strdup(context->default_cc_name);
+ if (context->default_cc_name_env)
+ p->default_cc_name_env = strdup(context->default_cc_name_env);
+
+ if (context->etypes) {
+ ret = copy_etypes(context, context->etypes, &p->etypes);
+ if (ret)
+ goto out;
+ }
+ if (context->etypes_des) {
+ ret = copy_etypes(context, context->etypes_des, &p->etypes_des);
+ if (ret)
+ goto out;
+ }
+
+ if (context->default_realms) {
+ ret = krb5_copy_host_realm(context,
+ context->default_realms, &p->default_realms);
+ if (ret)
+ goto out;
+ }
+
+ ret = _krb5_config_copy(context, context->cf, &p->cf);
+ if (ret)
+ goto out;
+
+ /* XXX should copy */
+ krb5_init_ets(p);
+ cc_ops_register(p);
+ kt_ops_register(p);
+
+#if 0 /* XXX */
+ if(context->warn_dest != NULL)
+ ;
+#endif
+
+ ret = krb5_set_extra_addresses(p, context->extra_addresses);
+ if (ret)
+ goto out;
+ ret = krb5_set_extra_addresses(p, context->ignore_addresses);
+ if (ret)
+ goto out;
+
+ ret = _krb5_copy_send_to_kdc_func(p, context);
+ if (ret)
+ goto out;
+
+ *out = p;
+
+ return 0;
+
+ out:
+ krb5_free_context(p);
+ return ret;
+}
+
+/**
+ * Frees the krb5_context allocated by krb5_init_context().
+ *
+ * @param context context to be freed.
+ *
+ * @ingroup krb5
+ */
+
+void KRB5_LIB_FUNCTION
+krb5_free_context(krb5_context context)
+{
+ if (context->default_cc_name)
+ free(context->default_cc_name);
+ if (context->default_cc_name_env)
+ free(context->default_cc_name_env);
+ free(context->etypes);
+ free(context->etypes_des);
+ krb5_free_host_realm (context, context->default_realms);
+ krb5_config_file_free (context, context->cf);
+ free_error_table (context->et_list);
+ free(context->cc_ops);
+ free(context->kt_types);
+ krb5_clear_error_message(context);
+ if(context->warn_dest != NULL)
+ krb5_closelog(context, context->warn_dest);
+ krb5_set_extra_addresses(context, NULL);
+ krb5_set_ignore_addresses(context, NULL);
+ krb5_set_send_to_kdc_func(context, NULL, NULL);
+ if (context->mutex != NULL) {
+ HEIMDAL_MUTEX_destroy(context->mutex);
+ free(context->mutex);
+ }
+ memset(context, 0, sizeof(*context));
+ free(context);
+}
+
+/**
+ * Reinit the context from a new set of filenames.
+ *
+ * @param context context to add configuration too.
+ * @param filenames array of filenames, end of list is indicated with a NULL filename.
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_set_config_files(krb5_context context, char **filenames)
+{
+ krb5_error_code ret;
+ krb5_config_binding *tmp = NULL;
+ while(filenames != NULL && *filenames != NULL && **filenames != '\0') {
+ ret = krb5_config_parse_file_multi(context, *filenames, &tmp);
+ if(ret != 0 && ret != ENOENT && ret != EACCES) {
+ krb5_config_file_free(context, tmp);
+ return ret;
+ }
+ filenames++;
+ }
+#if 0
+ /* with this enabled and if there are no config files, Kerberos is
+ considererd disabled */
+ if(tmp == NULL)
+ return ENXIO;
+#endif
+ krb5_config_file_free(context, context->cf);
+ context->cf = tmp;
+ ret = init_context_from_config_file(context);
+ return ret;
+}
+
+static krb5_error_code
+add_file(char ***pfilenames, int *len, char *file)
+{
+ char **pp = *pfilenames;
+ int i;
+
+ for(i = 0; i < *len; i++) {
+ if(strcmp(pp[i], file) == 0) {
+ free(file);
+ return 0;
+ }
+ }
+
+ pp = realloc(*pfilenames, (*len + 2) * sizeof(*pp));
+ if (pp == NULL) {
+ free(file);
+ return ENOMEM;
+ }
+
+ pp[*len] = file;
+ pp[*len + 1] = NULL;
+ *pfilenames = pp;
+ *len += 1;
+ return 0;
+}
+
+/*
+ * `pq' isn't free, it's up the the caller
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_prepend_config_files(const char *filelist, char **pq, char ***ret_pp)
+{
+ krb5_error_code ret;
+ const char *p, *q;
+ char **pp;
+ int len;
+ char *fn;
+
+ pp = NULL;
+
+ len = 0;
+ p = filelist;
+ while(1) {
+ ssize_t l;
+ q = p;
+ l = strsep_copy(&q, ":", NULL, 0);
+ if(l == -1)
+ break;
+ fn = malloc(l + 1);
+ if(fn == NULL) {
+ krb5_free_config_files(pp);
+ return ENOMEM;
+ }
+ l = strsep_copy(&p, ":", fn, l + 1);
+ ret = add_file(&pp, &len, fn);
+ if (ret) {
+ krb5_free_config_files(pp);
+ return ret;
+ }
+ }
+
+ if (pq != NULL) {
+ int i;
+
+ for (i = 0; pq[i] != NULL; i++) {
+ fn = strdup(pq[i]);
+ if (fn == NULL) {
+ krb5_free_config_files(pp);
+ return ENOMEM;
+ }
+ ret = add_file(&pp, &len, fn);
+ if (ret) {
+ krb5_free_config_files(pp);
+ return ret;
+ }
+ }
+ }
+
+ *ret_pp = pp;
+ return 0;
+}
+
+/**
+ * Prepend the filename to the global configuration list.
+ *
+ * @param filelist a filename to add to the default list of filename
+ * @param pfilenames return array of filenames, should be freed with krb5_free_config_files().
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_prepend_config_files_default(const char *filelist, char ***pfilenames)
+{
+ krb5_error_code ret;
+ char **defpp, **pp = NULL;
+
+ ret = krb5_get_default_config_files(&defpp);
+ if (ret)
+ return ret;
+
+ ret = krb5_prepend_config_files(filelist, defpp, &pp);
+ krb5_free_config_files(defpp);
+ if (ret) {
+ return ret;
+ }
+ *pfilenames = pp;
+ return 0;
+}
+
+/**
+ * Get the global configuration list.
+ *
+ * @param pfilenames return array of filenames, should be freed with krb5_free_config_files().
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_default_config_files(char ***pfilenames)
+{
+ const char *files = NULL;
+
+ if (pfilenames == NULL)
+ return EINVAL;
+ if(!issuid())
+ files = getenv("KRB5_CONFIG");
+ if (files == NULL)
+ files = krb5_config_file;
+
+ return krb5_prepend_config_files(files, NULL, pfilenames);
+}
+
+/**
+ * Free a list of configuration files.
+ *
+ * @param filenames list to be freed.
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
+void KRB5_LIB_FUNCTION
+krb5_free_config_files(char **filenames)
+{
+ char **p;
+ for(p = filenames; *p != NULL; p++)
+ free(*p);
+ free(filenames);
+}
+
+/**
+ * Returns the list of Kerberos encryption types sorted in order of
+ * most preferred to least preferred encryption type. Note that some
+ * encryption types might be disabled, so you need to check with
+ * krb5_enctype_valid() before using the encryption type.
+ *
+ * @return list of enctypes, terminated with ETYPE_NULL. Its a static
+ * array completed into the Kerberos library so the content doesn't
+ * need to be freed.
+ *
+ * @ingroup krb5
+ */
+
+const krb5_enctype * KRB5_LIB_FUNCTION
+krb5_kerberos_enctypes(krb5_context context)
+{
+ static const krb5_enctype p[] = {
+ ETYPE_AES256_CTS_HMAC_SHA1_96,
+ ETYPE_AES128_CTS_HMAC_SHA1_96,
+ ETYPE_DES3_CBC_SHA1,
+ ETYPE_DES3_CBC_MD5,
+ ETYPE_ARCFOUR_HMAC_MD5,
+ ETYPE_DES_CBC_MD5,
+ ETYPE_DES_CBC_MD4,
+ ETYPE_DES_CBC_CRC,
+ ETYPE_NULL
+ };
+ return p;
+}
+
+/*
+ * set `etype' to a malloced list of the default enctypes
+ */
+
+static krb5_error_code
+default_etypes(krb5_context context, krb5_enctype **etype)
+{
+ const krb5_enctype *p;
+ krb5_enctype *e = NULL, *ep;
+ int i, n = 0;
+
+ p = krb5_kerberos_enctypes(context);
+
+ for (i = 0; p[i] != ETYPE_NULL; i++) {
+ if (krb5_enctype_valid(context, p[i]) != 0)
+ continue;
+ ep = realloc(e, (n + 2) * sizeof(*e));
+ if (ep == NULL) {
+ free(e);
+ krb5_set_error_message (context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ e = ep;
+ e[n] = p[i];
+ e[n + 1] = ETYPE_NULL;
+ n++;
+ }
+ *etype = e;
+ return 0;
+}
+
+/**
+ * Set the default encryption types that will be use in communcation
+ * with the KDC, clients and servers.
+ *
+ * @param context Kerberos 5 context.
+ * @param etypes Encryption types, array terminated with ETYPE_NULL (0).
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_set_default_in_tkt_etypes(krb5_context context,
+ const krb5_enctype *etypes)
+{
+ krb5_enctype *p = NULL;
+ int i;
+
+ if(etypes) {
+ for (i = 0; etypes[i]; ++i) {
+ krb5_error_code ret;
+ ret = krb5_enctype_valid(context, etypes[i]);
+ if (ret)
+ return ret;
+ }
+ ++i;
+ ALLOC(p, i);
+ if(!p) {
+ krb5_set_error_message (context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ memmove(p, etypes, i * sizeof(krb5_enctype));
+ }
+ if(context->etypes)
+ free(context->etypes);
+ context->etypes = p;
+ return 0;
+}
+
+/**
+ * Get the default encryption types that will be use in communcation
+ * with the KDC, clients and servers.
+ *
+ * @param context Kerberos 5 context.
+ * @param etypes Encryption types, array terminated with
+ * ETYPE_NULL(0), caller should free array with krb5_xfree():
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_default_in_tkt_etypes(krb5_context context,
+ krb5_enctype **etypes)
+{
+ krb5_enctype *p;
+ int i;
+ krb5_error_code ret;
+
+ if(context->etypes) {
+ for(i = 0; context->etypes[i]; i++);
+ ++i;
+ ALLOC(p, i);
+ if(!p) {
+ krb5_set_error_message (context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ memmove(p, context->etypes, i * sizeof(krb5_enctype));
+ } else {
+ ret = default_etypes(context, &p);
+ if (ret)
+ return ret;
+ }
+ *etypes = p;
+ return 0;
+}
+
+/**
+ * Return the error string for the error code. The caller must not
+ * free the string.
+ *
+ * @param context Kerberos 5 context.
+ * @param code Kerberos error code.
+ *
+ * @return the error message matching code
+ *
+ * @ingroup krb5
+ */
+
+const char* KRB5_LIB_FUNCTION
+krb5_get_err_text(krb5_context context, krb5_error_code code)
+{
+ const char *p = NULL;
+ if(context != NULL)
+ p = com_right(context->et_list, code);
+ if(p == NULL)
+ p = strerror(code);
+ if (p == NULL)
+ p = "Unknown error";
+ return p;
+}
+
+/**
+ * Init the built-in ets in the Kerberos library.
+ *
+ * @param context kerberos context to add the ets too
+ *
+ * @ingroup krb5
+ */
+
+void KRB5_LIB_FUNCTION
+krb5_init_ets(krb5_context context)
+{
+ if(context->et_list == NULL){
+ krb5_add_et_list(context, initialize_krb5_error_table_r);
+#if defined(HEIMDAL_LOCALEDIR)
+ bindtextdomain(COM_ERR_BINDDOMAIN_krb5, HEIMDAL_LOCALEDIR);
+#endif
+
+ krb5_add_et_list(context, initialize_asn1_error_table_r);
+#if defined(HEIMDAL_LOCALEDIR)
+ bindtextdomain(COM_ERR_BINDDOMAIN_asn1, HEIMDAL_LOCALEDIR);
+#endif
+
+ krb5_add_et_list(context, initialize_heim_error_table_r);
+#if defined(HEIMDAL_LOCALEDIR)
+ bindtextdomain(COM_ERR_BINDDOMAIN_heim, HEIMDAL_LOCALEDIR);
+#endif
+
+ krb5_add_et_list(context, initialize_k524_error_table_r);
+#if defined(HEIMDAL_LOCALEDIR)
+ bindtextdomain(COM_ERR_BINDDOMAIN_k524, HEIMDAL_LOCALEDIR);
+#endif
+
+#ifdef PKINIT
+ krb5_add_et_list(context, initialize_hx_error_table_r);
+#if defined(HEIMDAL_LOCALEDIR)
+ bindtextdomain(COM_ERR_BINDDOMAIN_hx, HEIMDAL_LOCALEDIR);
+#endif
+#endif
+ }
+}
+
+/**
+ * Make the kerberos library default to the admin KDC.
+ *
+ * @param context Kerberos 5 context.
+ * @param flag boolean flag to select if the use the admin KDC or not.
+ *
+ * @ingroup krb5
+ */
+
+void KRB5_LIB_FUNCTION
+krb5_set_use_admin_kdc (krb5_context context, krb5_boolean flag)
+{
+ context->use_admin_kdc = flag;
+}
+
+/**
+ * Make the kerberos library default to the admin KDC.
+ *
+ * @param context Kerberos 5 context.
+ *
+ * @return boolean flag to telling the context will use admin KDC as the default KDC.
+ *
+ * @ingroup krb5
+ */
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_get_use_admin_kdc (krb5_context context)
+{
+ return context->use_admin_kdc;
+}
+
+/**
+ * Add extra address to the address list that the library will add to
+ * the client's address list when communicating with the KDC.
+ *
+ * @param context Kerberos 5 context.
+ * @param addresses addreses to add
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_add_extra_addresses(krb5_context context, krb5_addresses *addresses)
+{
+
+ if(context->extra_addresses)
+ return krb5_append_addresses(context,
+ context->extra_addresses, addresses);
+ else
+ return krb5_set_extra_addresses(context, addresses);
+}
+
+/**
+ * Set extra address to the address list that the library will add to
+ * the client's address list when communicating with the KDC.
+ *
+ * @param context Kerberos 5 context.
+ * @param addresses addreses to set
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_set_extra_addresses(krb5_context context, const krb5_addresses *addresses)
+{
+ if(context->extra_addresses)
+ krb5_free_addresses(context, context->extra_addresses);
+
+ if(addresses == NULL) {
+ if(context->extra_addresses != NULL) {
+ free(context->extra_addresses);
+ context->extra_addresses = NULL;
+ }
+ return 0;
+ }
+ if(context->extra_addresses == NULL) {
+ context->extra_addresses = malloc(sizeof(*context->extra_addresses));
+ if(context->extra_addresses == NULL) {
+ krb5_set_error_message (context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ }
+ return krb5_copy_addresses(context, addresses, context->extra_addresses);
+}
+
+/**
+ * Get extra address to the address list that the library will add to
+ * the client's address list when communicating with the KDC.
+ *
+ * @param context Kerberos 5 context.
+ * @param addresses addreses to set
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_extra_addresses(krb5_context context, krb5_addresses *addresses)
+{
+ if(context->extra_addresses == NULL) {
+ memset(addresses, 0, sizeof(*addresses));
+ return 0;
+ }
+ return krb5_copy_addresses(context,context->extra_addresses, addresses);
+}
+
+/**
+ * Add extra addresses to ignore when fetching addresses from the
+ * underlaying operating system.
+ *
+ * @param context Kerberos 5 context.
+ * @param addresses addreses to ignore
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_add_ignore_addresses(krb5_context context, krb5_addresses *addresses)
+{
+
+ if(context->ignore_addresses)
+ return krb5_append_addresses(context,
+ context->ignore_addresses, addresses);
+ else
+ return krb5_set_ignore_addresses(context, addresses);
+}
+
+/**
+ * Set extra addresses to ignore when fetching addresses from the
+ * underlaying operating system.
+ *
+ * @param context Kerberos 5 context.
+ * @param addresses addreses to ignore
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_set_ignore_addresses(krb5_context context, const krb5_addresses *addresses)
+{
+ if(context->ignore_addresses)
+ krb5_free_addresses(context, context->ignore_addresses);
+ if(addresses == NULL) {
+ if(context->ignore_addresses != NULL) {
+ free(context->ignore_addresses);
+ context->ignore_addresses = NULL;
+ }
+ return 0;
+ }
+ if(context->ignore_addresses == NULL) {
+ context->ignore_addresses = malloc(sizeof(*context->ignore_addresses));
+ if(context->ignore_addresses == NULL) {
+ krb5_set_error_message (context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ }
+ return krb5_copy_addresses(context, addresses, context->ignore_addresses);
+}
+
+/**
+ * Get extra addresses to ignore when fetching addresses from the
+ * underlaying operating system.
+ *
+ * @param context Kerberos 5 context.
+ * @param addresses list addreses ignored
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_ignore_addresses(krb5_context context, krb5_addresses *addresses)
+{
+ if(context->ignore_addresses == NULL) {
+ memset(addresses, 0, sizeof(*addresses));
+ return 0;
+ }
+ return krb5_copy_addresses(context, context->ignore_addresses, addresses);
+}
+
+/**
+ * Set version of fcache that the library should use.
+ *
+ * @param context Kerberos 5 context.
+ * @param version version number.
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_set_fcache_version(krb5_context context, int version)
+{
+ context->fcache_vno = version;
+ return 0;
+}
+
+/**
+ * Get version of fcache that the library should use.
+ *
+ * @param context Kerberos 5 context.
+ * @param version version number.
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_fcache_version(krb5_context context, int *version)
+{
+ *version = context->fcache_vno;
+ return 0;
+}
+
+/**
+ * Runtime check if the Kerberos library was complied with thread support.
+ *
+ * @return TRUE if the library was compiled with thread support, FALSE if not.
+ *
+ * @ingroup krb5
+ */
+
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_is_thread_safe(void)
+{
+#ifdef ENABLE_PTHREAD_SUPPORT
+ return TRUE;
+#else
+ return FALSE;
+#endif
+}
+
+/**
+ * Set if the library should use DNS to canonicalize hostnames.
+ *
+ * @param context Kerberos 5 context.
+ * @param flag if its dns canonicalizion is used or not.
+ *
+ * @ingroup krb5
+ */
+
+void KRB5_LIB_FUNCTION
+krb5_set_dns_canonicalize_hostname (krb5_context context, krb5_boolean flag)
+{
+ if (flag)
+ context->flags |= KRB5_CTX_F_DNS_CANONICALIZE_HOSTNAME;
+ else
+ context->flags &= ~KRB5_CTX_F_DNS_CANONICALIZE_HOSTNAME;
+}
+
+/**
+ * Get if the library uses DNS to canonicalize hostnames.
+ *
+ * @param context Kerberos 5 context.
+ *
+ * @return return non zero if the library uses DNS to canonicalize hostnames.
+ *
+ * @ingroup krb5
+ */
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_get_dns_canonicalize_hostname (krb5_context context)
+{
+ return (context->flags & KRB5_CTX_F_DNS_CANONICALIZE_HOSTNAME) ? 1 : 0;
+}
+
+/**
+ * Get current offset in time to the KDC.
+ *
+ * @param context Kerberos 5 context.
+ * @param sec seconds part of offset.
+ * @param usec micro seconds part of offset.
+ *
+ * @return returns zero
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_kdc_sec_offset (krb5_context context, int32_t *sec, int32_t *usec)
+{
+ if (sec)
+ *sec = context->kdc_sec_offset;
+ if (usec)
+ *usec = context->kdc_usec_offset;
+ return 0;
+}
+
+/**
+ * Set current offset in time to the KDC.
+ *
+ * @param context Kerberos 5 context.
+ * @param sec seconds part of offset.
+ * @param usec micro seconds part of offset.
+ *
+ * @return returns zero
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_set_kdc_sec_offset (krb5_context context, int32_t sec, int32_t usec)
+{
+ context->kdc_sec_offset = sec;
+ if (usec >= 0)
+ context->kdc_usec_offset = usec;
+ return 0;
+}
+
+/**
+ * Get max time skew allowed.
+ *
+ * @param context Kerberos 5 context.
+ *
+ * @return timeskew in seconds.
+ *
+ * @ingroup krb5
+ */
+
+time_t KRB5_LIB_FUNCTION
+krb5_get_max_time_skew (krb5_context context)
+{
+ return context->max_skew;
+}
+
+/**
+ * Set max time skew allowed.
+ *
+ * @param context Kerberos 5 context.
+ * @param t timeskew in seconds.
+ *
+ * @ingroup krb5
+ */
+
+void KRB5_LIB_FUNCTION
+krb5_set_max_time_skew (krb5_context context, time_t t)
+{
+ context->max_skew = t;
+}
diff --git a/source4/heimdal/lib/krb5/convert_creds.c b/source4/heimdal/lib/krb5/convert_creds.c
new file mode 100644
index 0000000000..fc81d96bec
--- /dev/null
+++ b/source4/heimdal/lib/krb5/convert_creds.c
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 1997 - 2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+RCSID("$Id$");
+
+#include "krb5-v4compat.h"
+
+static krb5_error_code
+check_ticket_flags(TicketFlags f)
+{
+ return 0; /* maybe add some more tests here? */
+}
+
+/**
+ * Convert the v5 credentials in in_cred to v4-dito in v4creds. This
+ * is done by sending them to the 524 function in the KDC. If
+ * `in_cred' doesn't contain a DES session key, then a new one is
+ * gotten from the KDC and stored in the cred cache `ccache'.
+ *
+ * @param context Kerberos 5 context.
+ * @param in_cred the credential to convert
+ * @param v4creds the converted credential
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5_v4compat
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb524_convert_creds_kdc(krb5_context context,
+ krb5_creds *in_cred,
+ struct credentials *v4creds)
+{
+ krb5_error_code ret;
+ krb5_data reply;
+ krb5_storage *sp;
+ int32_t tmp;
+ krb5_data ticket;
+ char realm[REALM_SZ];
+ krb5_creds *v5_creds = in_cred;
+
+ ret = check_ticket_flags(v5_creds->flags.b);
+ if(ret)
+ goto out2;
+
+ {
+ krb5_krbhst_handle handle;
+
+ ret = krb5_krbhst_init(context,
+ krb5_principal_get_realm(context,
+ v5_creds->server),
+ KRB5_KRBHST_KRB524,
+ &handle);
+ if (ret)
+ goto out2;
+
+ ret = krb5_sendto (context,
+ &v5_creds->ticket,
+ handle,
+ &reply);
+ krb5_krbhst_free(context, handle);
+ if (ret)
+ goto out2;
+ }
+ sp = krb5_storage_from_mem(reply.data, reply.length);
+ if(sp == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message (context, ENOMEM, N_("malloc: out of memory", ""));
+ goto out2;
+ }
+ krb5_ret_int32(sp, &tmp);
+ ret = tmp;
+ if(ret == 0) {
+ memset(v4creds, 0, sizeof(*v4creds));
+ ret = krb5_ret_int32(sp, &tmp);
+ if(ret)
+ goto out;
+ v4creds->kvno = tmp;
+ ret = krb5_ret_data(sp, &ticket);
+ if(ret)
+ goto out;
+ v4creds->ticket_st.length = ticket.length;
+ memcpy(v4creds->ticket_st.dat, ticket.data, ticket.length);
+ krb5_data_free(&ticket);
+ ret = krb5_524_conv_principal(context,
+ v5_creds->server,
+ v4creds->service,
+ v4creds->instance,
+ v4creds->realm);
+ if(ret)
+ goto out;
+ v4creds->issue_date = v5_creds->times.starttime;
+ v4creds->lifetime = _krb5_krb_time_to_life(v4creds->issue_date,
+ v5_creds->times.endtime);
+ ret = krb5_524_conv_principal(context, v5_creds->client,
+ v4creds->pname,
+ v4creds->pinst,
+ realm);
+ if(ret)
+ goto out;
+ memcpy(v4creds->session, v5_creds->session.keyvalue.data, 8);
+ } else {
+ krb5_set_error_message (context, ret,
+ N_("converting credentials: %s",
+ "already localized"),
+ krb5_get_err_text(context, ret));
+ }
+out:
+ krb5_storage_free(sp);
+ krb5_data_free(&reply);
+out2:
+ if (v5_creds != in_cred)
+ krb5_free_creds (context, v5_creds);
+ return ret;
+}
+
+/**
+ * Convert the v5 credentials in in_cred to v4-dito in v4creds,
+ * check the credential cache ccache before checking with the KDC.
+ *
+ * @param context Kerberos 5 context.
+ * @param ccache credential cache used to check for des-ticket.
+ * @param in_cred the credential to convert
+ * @param v4creds the converted credential
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5_v4compat
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb524_convert_creds_kdc_ccache(krb5_context context,
+ krb5_ccache ccache,
+ krb5_creds *in_cred,
+ struct credentials *v4creds)
+{
+ krb5_error_code ret;
+ krb5_creds *v5_creds = in_cred;
+ krb5_keytype keytype;
+
+ keytype = v5_creds->session.keytype;
+
+ if (keytype != ENCTYPE_DES_CBC_CRC) {
+ /* MIT krb524d doesn't like nothing but des-cbc-crc tickets,
+ so go get one */
+ krb5_creds template;
+
+ memset (&template, 0, sizeof(template));
+ template.session.keytype = ENCTYPE_DES_CBC_CRC;
+ ret = krb5_copy_principal (context, in_cred->client, &template.client);
+ if (ret) {
+ krb5_free_cred_contents (context, &template);
+ return ret;
+ }
+ ret = krb5_copy_principal (context, in_cred->server, &template.server);
+ if (ret) {
+ krb5_free_cred_contents (context, &template);
+ return ret;
+ }
+
+ ret = krb5_get_credentials (context, 0, ccache,
+ &template, &v5_creds);
+ krb5_free_cred_contents (context, &template);
+ if (ret)
+ return ret;
+ }
+
+ ret = krb524_convert_creds_kdc(context, v5_creds, v4creds);
+
+ if (v5_creds != in_cred)
+ krb5_free_creds (context, v5_creds);
+ return ret;
+}
diff --git a/source4/heimdal/lib/krb5/copy_host_realm.c b/source4/heimdal/lib/krb5/copy_host_realm.c
new file mode 100644
index 0000000000..37e27110b6
--- /dev/null
+++ b/source4/heimdal/lib/krb5/copy_host_realm.c
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 1999 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+/**
+ * Copy the list of realms from `from' to `to'.
+ *
+ * @param context Kerberos 5 context.
+ * @param from list of realms to copy from.
+ * @param to list of realms to copy to, free list of krb5_free_host_realm().
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_copy_host_realm(krb5_context context,
+ const krb5_realm *from,
+ krb5_realm **to)
+{
+ unsigned int n, i;
+ const krb5_realm *p;
+
+ for (n = 1, p = from; *p != NULL; ++p)
+ ++n;
+
+ *to = calloc (n, sizeof(**to));
+ if (*to == NULL) {
+ krb5_set_error_message (context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+
+ for (i = 0, p = from; *p != NULL; ++p, ++i) {
+ (*to)[i] = strdup(*p);
+ if ((*to)[i] == NULL) {
+ krb5_free_host_realm (context, *to);
+ krb5_set_error_message (context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ }
+ return 0;
+}
diff --git a/source4/heimdal/lib/krb5/crc.c b/source4/heimdal/lib/krb5/crc.c
new file mode 100644
index 0000000000..a900cabbba
--- /dev/null
+++ b/source4/heimdal/lib/krb5/crc.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 1997 - 2000 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+static u_long table[256];
+
+#define CRC_GEN 0xEDB88320L
+
+void
+_krb5_crc_init_table(void)
+{
+ static int flag = 0;
+ unsigned long crc, poly;
+ unsigned int i, j;
+
+ if(flag) return;
+ poly = CRC_GEN;
+ for (i = 0; i < 256; i++) {
+ crc = i;
+ for (j = 8; j > 0; j--) {
+ if (crc & 1) {
+ crc = (crc >> 1) ^ poly;
+ } else {
+ crc >>= 1;
+ }
+ }
+ table[i] = crc;
+ }
+ flag = 1;
+}
+
+uint32_t
+_krb5_crc_update (const char *p, size_t len, uint32_t res)
+{
+ while (len--)
+ res = table[(res ^ *p++) & 0xFF] ^ (res >> 8);
+ return res & 0xFFFFFFFF;
+}
diff --git a/source4/heimdal/lib/krb5/creds.c b/source4/heimdal/lib/krb5/creds.c
new file mode 100644
index 0000000000..087a4850eb
--- /dev/null
+++ b/source4/heimdal/lib/krb5/creds.c
@@ -0,0 +1,292 @@
+/*
+ * Copyright (c) 1997 - 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+#undef __attribute__
+#define __attribute__(X)
+
+#ifndef HEIMDAL_SMALLER
+
+/* keep this for compatibility with older code */
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_free_creds_contents (krb5_context context, krb5_creds *c)
+ __attribute__((deprecated))
+{
+ return krb5_free_cred_contents (context, c);
+}
+
+#endif /* HEIMDAL_SMALLER */
+
+/**
+ * Free content of krb5_creds.
+ *
+ * @param context Kerberos 5 context.
+ * @param c krb5_creds to free.
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_free_cred_contents (krb5_context context, krb5_creds *c)
+{
+ krb5_free_principal (context, c->client);
+ c->client = NULL;
+ krb5_free_principal (context, c->server);
+ c->server = NULL;
+ krb5_free_keyblock_contents (context, &c->session);
+ krb5_data_free (&c->ticket);
+ krb5_data_free (&c->second_ticket);
+ free_AuthorizationData (&c->authdata);
+ krb5_free_addresses (context, &c->addresses);
+ memset(c, 0, sizeof(*c));
+ return 0;
+}
+
+/**
+ * Copy content of krb5_creds.
+ *
+ * @param context Kerberos 5 context.
+ * @param incred source credential
+ * @param c destination credential, free with krb5_free_cred_contents().
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_copy_creds_contents (krb5_context context,
+ const krb5_creds *incred,
+ krb5_creds *c)
+{
+ krb5_error_code ret;
+
+ memset(c, 0, sizeof(*c));
+ ret = krb5_copy_principal (context, incred->client, &c->client);
+ if (ret)
+ goto fail;
+ ret = krb5_copy_principal (context, incred->server, &c->server);
+ if (ret)
+ goto fail;
+ ret = krb5_copy_keyblock_contents (context, &incred->session, &c->session);
+ if (ret)
+ goto fail;
+ c->times = incred->times;
+ ret = krb5_data_copy (&c->ticket,
+ incred->ticket.data,
+ incred->ticket.length);
+ if (ret)
+ goto fail;
+ ret = krb5_data_copy (&c->second_ticket,
+ incred->second_ticket.data,
+ incred->second_ticket.length);
+ if (ret)
+ goto fail;
+ ret = copy_AuthorizationData(&incred->authdata, &c->authdata);
+ if (ret)
+ goto fail;
+ ret = krb5_copy_addresses (context,
+ &incred->addresses,
+ &c->addresses);
+ if (ret)
+ goto fail;
+ c->flags = incred->flags;
+ return 0;
+
+fail:
+ krb5_free_cred_contents (context, c);
+ return ret;
+}
+
+/**
+ * Copy krb5_creds.
+ *
+ * @param context Kerberos 5 context.
+ * @param incred source credential
+ * @param outcred destination credential, free with krb5_free_creds().
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_copy_creds (krb5_context context,
+ const krb5_creds *incred,
+ krb5_creds **outcred)
+{
+ krb5_creds *c;
+
+ c = malloc (sizeof (*c));
+ if (c == NULL) {
+ krb5_set_error_message (context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ memset (c, 0, sizeof(*c));
+ *outcred = c;
+ return krb5_copy_creds_contents (context, incred, c);
+}
+
+/**
+ * Free krb5_creds.
+ *
+ * @param context Kerberos 5 context.
+ * @param c krb5_creds to free.
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_free_creds (krb5_context context, krb5_creds *c)
+{
+ krb5_free_cred_contents (context, c);
+ free (c);
+ return 0;
+}
+
+/* XXX this do not belong here */
+static krb5_boolean
+krb5_times_equal(const krb5_times *a, const krb5_times *b)
+{
+ return a->starttime == b->starttime &&
+ a->authtime == b->authtime &&
+ a->endtime == b->endtime &&
+ a->renew_till == b->renew_till;
+}
+
+/**
+ * Return TRUE if `mcreds' and `creds' are equal (`whichfields'
+ * determines what equal means).
+ *
+ * @param context Kerberos 5 context.
+ * @param whichfields which fields to compare.
+ * @param mcreds cred to compare with.
+ * @param creds cred to compare with.
+ *
+ * @return return TRUE if mcred and creds are equal, FALSE if not.
+ *
+ * @ingroup krb5
+ */
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_compare_creds(krb5_context context, krb5_flags whichfields,
+ const krb5_creds * mcreds, const krb5_creds * creds)
+{
+ krb5_boolean match = TRUE;
+
+ if (match && mcreds->server) {
+ if (whichfields & (KRB5_TC_DONT_MATCH_REALM | KRB5_TC_MATCH_SRV_NAMEONLY))
+ match = krb5_principal_compare_any_realm (context, mcreds->server,
+ creds->server);
+ else
+ match = krb5_principal_compare (context, mcreds->server,
+ creds->server);
+ }
+
+ if (match && mcreds->client) {
+ if(whichfields & KRB5_TC_DONT_MATCH_REALM)
+ match = krb5_principal_compare_any_realm (context, mcreds->client,
+ creds->client);
+ else
+ match = krb5_principal_compare (context, mcreds->client,
+ creds->client);
+ }
+
+ if (match && (whichfields & KRB5_TC_MATCH_KEYTYPE))
+ match = krb5_enctypes_compatible_keys(context,
+ mcreds->session.keytype,
+ creds->session.keytype);
+
+ if (match && (whichfields & KRB5_TC_MATCH_FLAGS_EXACT))
+ match = mcreds->flags.i == creds->flags.i;
+
+ if (match && (whichfields & KRB5_TC_MATCH_FLAGS))
+ match = (creds->flags.i & mcreds->flags.i) == mcreds->flags.i;
+
+ if (match && (whichfields & KRB5_TC_MATCH_TIMES_EXACT))
+ match = krb5_times_equal(&mcreds->times, &creds->times);
+
+ if (match && (whichfields & KRB5_TC_MATCH_TIMES))
+ /* compare only expiration times */
+ match = (mcreds->times.renew_till <= creds->times.renew_till) &&
+ (mcreds->times.endtime <= creds->times.endtime);
+
+ if (match && (whichfields & KRB5_TC_MATCH_AUTHDATA)) {
+ unsigned int i;
+ if(mcreds->authdata.len != creds->authdata.len)
+ match = FALSE;
+ else
+ for(i = 0; match && i < mcreds->authdata.len; i++)
+ match = (mcreds->authdata.val[i].ad_type ==
+ creds->authdata.val[i].ad_type) &&
+ (krb5_data_cmp(&mcreds->authdata.val[i].ad_data,
+ &creds->authdata.val[i].ad_data) == 0);
+ }
+ if (match && (whichfields & KRB5_TC_MATCH_2ND_TKT))
+ match = (krb5_data_cmp(&mcreds->second_ticket, &creds->second_ticket) == 0);
+
+ if (match && (whichfields & KRB5_TC_MATCH_IS_SKEY))
+ match = ((mcreds->second_ticket.length == 0) ==
+ (creds->second_ticket.length == 0));
+
+ return match;
+}
+
+/**
+ * Returns the ticket flags for the credentials in creds.
+ * See also krb5_ticket_get_flags().
+ *
+ * @param creds credential to get ticket flags from
+ *
+ * @return ticket flags
+ *
+ * @ingroup krb5
+ */
+
+unsigned long
+krb5_creds_get_ticket_flags(krb5_creds *creds)
+{
+ return TicketFlags2int(creds->flags.b);
+}
diff --git a/source4/heimdal/lib/krb5/crypto.c b/source4/heimdal/lib/krb5/crypto.c
new file mode 100644
index 0000000000..bc6512cf1a
--- /dev/null
+++ b/source4/heimdal/lib/krb5/crypto.c
@@ -0,0 +1,4713 @@
+/*
+ * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+RCSID("$Id$");
+#include <pkinit_asn1.h>
+
+#undef __attribute__
+#define __attribute__(X)
+
+#define WEAK_ENCTYPES 1
+
+#ifndef HEIMDAL_SMALLER
+#define DES3_OLD_ENCTYPE 1
+#endif
+
+
+#ifdef HAVE_OPENSSL /* XXX forward decl for hcrypto glue */
+const EVP_CIPHER * _krb5_EVP_hcrypto_aes_128_cts(void);
+const EVP_CIPHER * _krb5_EVP_hcrypto_aes_256_cts(void);
+#define EVP_hcrypto_aes_128_cts _krb5_EVP_hcrypto_aes_128_cts
+#define EVP_hcrypto_aes_256_cts _krb5_EVP_hcrypto_aes_256_cts
+#endif
+
+struct key_data {
+ krb5_keyblock *key;
+ krb5_data *schedule;
+};
+
+struct key_usage {
+ unsigned usage;
+ struct key_data key;
+};
+
+struct krb5_crypto_data {
+ struct encryption_type *et;
+ struct key_data key;
+ int num_key_usage;
+ struct key_usage *key_usage;
+};
+
+#define CRYPTO_ETYPE(C) ((C)->et->type)
+
+/* bits for `flags' below */
+#define F_KEYED 1 /* checksum is keyed */
+#define F_CPROOF 2 /* checksum is collision proof */
+#define F_DERIVED 4 /* uses derived keys */
+#define F_VARIANT 8 /* uses `variant' keys (6.4.3) */
+#define F_PSEUDO 16 /* not a real protocol type */
+#define F_SPECIAL 32 /* backwards */
+#define F_DISABLED 64 /* enctype/checksum disabled */
+
+struct salt_type {
+ krb5_salttype type;
+ const char *name;
+ krb5_error_code (*string_to_key)(krb5_context, krb5_enctype, krb5_data,
+ krb5_salt, krb5_data, krb5_keyblock*);
+};
+
+struct key_type {
+ krb5_keytype type; /* XXX */
+ const char *name;
+ size_t bits;
+ size_t size;
+ size_t schedule_size;
+ void (*random_key)(krb5_context, krb5_keyblock*);
+ void (*schedule)(krb5_context, struct key_type *, struct key_data *);
+ struct salt_type *string_to_key;
+ void (*random_to_key)(krb5_context, krb5_keyblock*, const void*, size_t);
+ void (*cleanup)(krb5_context, struct key_data *);
+ const EVP_CIPHER *(*evp)(void);
+};
+
+struct checksum_type {
+ krb5_cksumtype type;
+ const char *name;
+ size_t blocksize;
+ size_t checksumsize;
+ unsigned flags;
+ krb5_enctype (*checksum)(krb5_context context,
+ struct key_data *key,
+ const void *buf, size_t len,
+ unsigned usage,
+ Checksum *csum);
+ krb5_error_code (*verify)(krb5_context context,
+ struct key_data *key,
+ const void *buf, size_t len,
+ unsigned usage,
+ Checksum *csum);
+};
+
+struct encryption_type {
+ krb5_enctype type;
+ const char *name;
+ size_t blocksize;
+ size_t padsize;
+ size_t confoundersize;
+ struct key_type *keytype;
+ struct checksum_type *checksum;
+ struct checksum_type *keyed_checksum;
+ unsigned flags;
+ krb5_error_code (*encrypt)(krb5_context context,
+ struct key_data *key,
+ void *data, size_t len,
+ krb5_boolean encryptp,
+ int usage,
+ void *ivec);
+ size_t prf_length;
+ krb5_error_code (*prf)(krb5_context,
+ krb5_crypto, const krb5_data *, krb5_data *);
+};
+
+#define ENCRYPTION_USAGE(U) (((U) << 8) | 0xAA)
+#define INTEGRITY_USAGE(U) (((U) << 8) | 0x55)
+#define CHECKSUM_USAGE(U) (((U) << 8) | 0x99)
+
+static struct checksum_type *_find_checksum(krb5_cksumtype type);
+static struct encryption_type *_find_enctype(krb5_enctype type);
+static krb5_error_code _get_derived_key(krb5_context, krb5_crypto,
+ unsigned, struct key_data**);
+static struct key_data *_new_derived_key(krb5_crypto crypto, unsigned usage);
+static krb5_error_code derive_key(krb5_context context,
+ struct encryption_type *et,
+ struct key_data *key,
+ const void *constant,
+ size_t len);
+static krb5_error_code hmac(krb5_context context,
+ struct checksum_type *cm,
+ const void *data,
+ size_t len,
+ unsigned usage,
+ struct key_data *keyblock,
+ Checksum *result);
+static void free_key_data(krb5_context,
+ struct key_data *,
+ struct encryption_type *);
+static krb5_error_code usage2arcfour (krb5_context, unsigned *);
+static void xor (DES_cblock *, const unsigned char *);
+
+/************************************************************
+ * *
+ ************************************************************/
+
+struct evp_schedule {
+ EVP_CIPHER_CTX ectx;
+ EVP_CIPHER_CTX dctx;
+};
+
+
+static HEIMDAL_MUTEX crypto_mutex = HEIMDAL_MUTEX_INITIALIZER;
+
+#ifdef WEAK_ENCTYPES
+static void
+krb5_DES_random_key(krb5_context context,
+ krb5_keyblock *key)
+{
+ DES_cblock *k = key->keyvalue.data;
+ do {
+ krb5_generate_random_block(k, sizeof(DES_cblock));
+ DES_set_odd_parity(k);
+ } while(DES_is_weak_key(k));
+}
+
+static void
+krb5_DES_schedule_old(krb5_context context,
+ struct key_type *kt,
+ struct key_data *key)
+{
+ DES_set_key_unchecked(key->key->keyvalue.data, key->schedule->data);
+}
+
+#ifdef ENABLE_AFS_STRING_TO_KEY
+
+/* This defines the Andrew string_to_key function. It accepts a password
+ * string as input and converts it via a one-way encryption algorithm to a DES
+ * encryption key. It is compatible with the original Andrew authentication
+ * service password database.
+ */
+
+/*
+ * Short passwords, i.e 8 characters or less.
+ */
+static void
+krb5_DES_AFS3_CMU_string_to_key (krb5_data pw,
+ krb5_data cell,
+ DES_cblock *key)
+{
+ char password[8+1]; /* crypt is limited to 8 chars anyway */
+ int i;
+
+ for(i = 0; i < 8; i++) {
+ char c = ((i < pw.length) ? ((char*)pw.data)[i] : 0) ^
+ ((i < cell.length) ?
+ tolower(((unsigned char*)cell.data)[i]) : 0);
+ password[i] = c ? c : 'X';
+ }
+ password[8] = '\0';
+
+ memcpy(key, crypt(password, "p1") + 2, sizeof(DES_cblock));
+
+ /* parity is inserted into the LSB so left shift each byte up one
+ bit. This allows ascii characters with a zero MSB to retain as
+ much significance as possible. */
+ for (i = 0; i < sizeof(DES_cblock); i++)
+ ((unsigned char*)key)[i] <<= 1;
+ DES_set_odd_parity (key);
+}
+
+/*
+ * Long passwords, i.e 9 characters or more.
+ */
+static void
+krb5_DES_AFS3_Transarc_string_to_key (krb5_data pw,
+ krb5_data cell,
+ DES_cblock *key)
+{
+ DES_key_schedule schedule;
+ DES_cblock temp_key;
+ DES_cblock ivec;
+ char password[512];
+ size_t passlen;
+
+ memcpy(password, pw.data, min(pw.length, sizeof(password)));
+ if(pw.length < sizeof(password)) {
+ int len = min(cell.length, sizeof(password) - pw.length);
+ int i;
+
+ memcpy(password + pw.length, cell.data, len);
+ for (i = pw.length; i < pw.length + len; ++i)
+ password[i] = tolower((unsigned char)password[i]);
+ }
+ passlen = min(sizeof(password), pw.length + cell.length);
+ memcpy(&ivec, "kerberos", 8);
+ memcpy(&temp_key, "kerberos", 8);
+ DES_set_odd_parity (&temp_key);
+ DES_set_key_unchecked (&temp_key, &schedule);
+ DES_cbc_cksum ((void*)password, &ivec, passlen, &schedule, &ivec);
+
+ memcpy(&temp_key, &ivec, 8);
+ DES_set_odd_parity (&temp_key);
+ DES_set_key_unchecked (&temp_key, &schedule);
+ DES_cbc_cksum ((void*)password, key, passlen, &schedule, &ivec);
+ memset(&schedule, 0, sizeof(schedule));
+ memset(&temp_key, 0, sizeof(temp_key));
+ memset(&ivec, 0, sizeof(ivec));
+ memset(password, 0, sizeof(password));
+
+ DES_set_odd_parity (key);
+}
+
+static krb5_error_code
+DES_AFS3_string_to_key(krb5_context context,
+ krb5_enctype enctype,
+ krb5_data password,
+ krb5_salt salt,
+ krb5_data opaque,
+ krb5_keyblock *key)
+{
+ DES_cblock tmp;
+ if(password.length > 8)
+ krb5_DES_AFS3_Transarc_string_to_key(password, salt.saltvalue, &tmp);
+ else
+ krb5_DES_AFS3_CMU_string_to_key(password, salt.saltvalue, &tmp);
+ key->keytype = enctype;
+ krb5_data_copy(&key->keyvalue, tmp, sizeof(tmp));
+ memset(&key, 0, sizeof(key));
+ return 0;
+}
+#endif /* ENABLE_AFS_STRING_TO_KEY */
+
+static void
+DES_string_to_key_int(unsigned char *data, size_t length, DES_cblock *key)
+{
+ DES_key_schedule schedule;
+ int i;
+ int reverse = 0;
+ unsigned char *p;
+
+ unsigned char swap[] = { 0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe,
+ 0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf };
+ memset(key, 0, 8);
+
+ p = (unsigned char*)key;
+ for (i = 0; i < length; i++) {
+ unsigned char tmp = data[i];
+ if (!reverse)
+ *p++ ^= (tmp << 1);
+ else
+ *--p ^= (swap[tmp & 0xf] << 4) | swap[(tmp & 0xf0) >> 4];
+ if((i % 8) == 7)
+ reverse = !reverse;
+ }
+ DES_set_odd_parity(key);
+ if(DES_is_weak_key(key))
+ (*key)[7] ^= 0xF0;
+ DES_set_key_unchecked(key, &schedule);
+ DES_cbc_cksum((void*)data, key, length, &schedule, key);
+ memset(&schedule, 0, sizeof(schedule));
+ DES_set_odd_parity(key);
+ if(DES_is_weak_key(key))
+ (*key)[7] ^= 0xF0;
+}
+
+static krb5_error_code
+krb5_DES_string_to_key(krb5_context context,
+ krb5_enctype enctype,
+ krb5_data password,
+ krb5_salt salt,
+ krb5_data opaque,
+ krb5_keyblock *key)
+{
+ unsigned char *s;
+ size_t len;
+ DES_cblock tmp;
+
+#ifdef ENABLE_AFS_STRING_TO_KEY
+ if (opaque.length == 1) {
+ unsigned long v;
+ _krb5_get_int(opaque.data, &v, 1);
+ if (v == 1)
+ return DES_AFS3_string_to_key(context, enctype, password,
+ salt, opaque, key);
+ }
+#endif
+
+ len = password.length + salt.saltvalue.length;
+ s = malloc(len);
+ if(len > 0 && s == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ memcpy(s, password.data, password.length);
+ memcpy(s + password.length, salt.saltvalue.data, salt.saltvalue.length);
+ DES_string_to_key_int(s, len, &tmp);
+ key->keytype = enctype;
+ krb5_data_copy(&key->keyvalue, tmp, sizeof(tmp));
+ memset(&tmp, 0, sizeof(tmp));
+ memset(s, 0, len);
+ free(s);
+ return 0;
+}
+
+static void
+krb5_DES_random_to_key(krb5_context context,
+ krb5_keyblock *key,
+ const void *data,
+ size_t size)
+{
+ DES_cblock *k = key->keyvalue.data;
+ memcpy(k, data, key->keyvalue.length);
+ DES_set_odd_parity(k);
+ if(DES_is_weak_key(k))
+ xor(k, (const unsigned char*)"\0\0\0\0\0\0\0\xf0");
+}
+#endif
+
+/*
+ *
+ */
+
+static void
+DES3_random_key(krb5_context context,
+ krb5_keyblock *key)
+{
+ DES_cblock *k = key->keyvalue.data;
+ do {
+ krb5_generate_random_block(k, 3 * sizeof(DES_cblock));
+ DES_set_odd_parity(&k[0]);
+ DES_set_odd_parity(&k[1]);
+ DES_set_odd_parity(&k[2]);
+ } while(DES_is_weak_key(&k[0]) ||
+ DES_is_weak_key(&k[1]) ||
+ DES_is_weak_key(&k[2]));
+}
+
+/*
+ * A = A xor B. A & B are 8 bytes.
+ */
+
+static void
+xor (DES_cblock *key, const unsigned char *b)
+{
+ unsigned char *a = (unsigned char*)key;
+ a[0] ^= b[0];
+ a[1] ^= b[1];
+ a[2] ^= b[2];
+ a[3] ^= b[3];
+ a[4] ^= b[4];
+ a[5] ^= b[5];
+ a[6] ^= b[6];
+ a[7] ^= b[7];
+}
+
+#ifdef DES3_OLD_ENCTYPE
+static krb5_error_code
+DES3_string_to_key(krb5_context context,
+ krb5_enctype enctype,
+ krb5_data password,
+ krb5_salt salt,
+ krb5_data opaque,
+ krb5_keyblock *key)
+{
+ char *str;
+ size_t len;
+ unsigned char tmp[24];
+ DES_cblock keys[3];
+ krb5_error_code ret;
+
+ len = password.length + salt.saltvalue.length;
+ str = malloc(len);
+ if(len != 0 && str == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ memcpy(str, password.data, password.length);
+ memcpy(str + password.length, salt.saltvalue.data, salt.saltvalue.length);
+ {
+ DES_cblock ivec;
+ DES_key_schedule s[3];
+ int i;
+
+ ret = _krb5_n_fold(str, len, tmp, 24);
+ if (ret) {
+ memset(str, 0, len);
+ free(str);
+ krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ return ret;
+ }
+
+ for(i = 0; i < 3; i++){
+ memcpy(keys + i, tmp + i * 8, sizeof(keys[i]));
+ DES_set_odd_parity(keys + i);
+ if(DES_is_weak_key(keys + i))
+ xor(keys + i, (const unsigned char*)"\0\0\0\0\0\0\0\xf0");
+ DES_set_key_unchecked(keys + i, &s[i]);
+ }
+ memset(&ivec, 0, sizeof(ivec));
+ DES_ede3_cbc_encrypt(tmp,
+ tmp, sizeof(tmp),
+ &s[0], &s[1], &s[2], &ivec, DES_ENCRYPT);
+ memset(s, 0, sizeof(s));
+ memset(&ivec, 0, sizeof(ivec));
+ for(i = 0; i < 3; i++){
+ memcpy(keys + i, tmp + i * 8, sizeof(keys[i]));
+ DES_set_odd_parity(keys + i);
+ if(DES_is_weak_key(keys + i))
+ xor(keys + i, (const unsigned char*)"\0\0\0\0\0\0\0\xf0");
+ }
+ memset(tmp, 0, sizeof(tmp));
+ }
+ key->keytype = enctype;
+ krb5_data_copy(&key->keyvalue, keys, sizeof(keys));
+ memset(keys, 0, sizeof(keys));
+ memset(str, 0, len);
+ free(str);
+ return 0;
+}
+#endif
+
+static krb5_error_code
+DES3_string_to_key_derived(krb5_context context,
+ krb5_enctype enctype,
+ krb5_data password,
+ krb5_salt salt,
+ krb5_data opaque,
+ krb5_keyblock *key)
+{
+ krb5_error_code ret;
+ size_t len = password.length + salt.saltvalue.length;
+ char *s;
+
+ s = malloc(len);
+ if(len != 0 && s == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ memcpy(s, password.data, password.length);
+ memcpy(s + password.length, salt.saltvalue.data, salt.saltvalue.length);
+ ret = krb5_string_to_key_derived(context,
+ s,
+ len,
+ enctype,
+ key);
+ memset(s, 0, len);
+ free(s);
+ return ret;
+}
+
+static void
+DES3_random_to_key(krb5_context context,
+ krb5_keyblock *key,
+ const void *data,
+ size_t size)
+{
+ unsigned char *x = key->keyvalue.data;
+ const u_char *q = data;
+ DES_cblock *k;
+ int i, j;
+
+ memset(x, 0, sizeof(x));
+ for (i = 0; i < 3; ++i) {
+ unsigned char foo;
+ for (j = 0; j < 7; ++j) {
+ unsigned char b = q[7 * i + j];
+
+ x[8 * i + j] = b;
+ }
+ foo = 0;
+ for (j = 6; j >= 0; --j) {
+ foo |= q[7 * i + j] & 1;
+ foo <<= 1;
+ }
+ x[8 * i + 7] = foo;
+ }
+ k = key->keyvalue.data;
+ for (i = 0; i < 3; i++) {
+ DES_set_odd_parity(&k[i]);
+ if(DES_is_weak_key(&k[i]))
+ xor(&k[i], (const unsigned char*)"\0\0\0\0\0\0\0\xf0");
+ }
+}
+
+/*
+ * ARCFOUR
+ */
+
+static void
+ARCFOUR_schedule(krb5_context context,
+ struct key_type *kt,
+ struct key_data *kd)
+{
+ RC4_set_key (kd->schedule->data,
+ kd->key->keyvalue.length, kd->key->keyvalue.data);
+}
+
+static krb5_error_code
+ARCFOUR_string_to_key(krb5_context context,
+ krb5_enctype enctype,
+ krb5_data password,
+ krb5_salt salt,
+ krb5_data opaque,
+ krb5_keyblock *key)
+{
+ krb5_error_code ret;
+ uint16_t *s = NULL;
+ size_t len, i;
+ EVP_MD_CTX *m;
+
+ m = EVP_MD_CTX_create();
+ if (m == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ goto out;
+ }
+
+ EVP_DigestInit_ex(m, EVP_md4(), NULL);
+
+ ret = wind_utf8ucs2_length(password.data, &len);
+ if (ret) {
+ krb5_set_error_message (context, ret,
+ N_("Password not an UCS2 string", ""));
+ goto out;
+ }
+
+ s = malloc (len * sizeof(s[0]));
+ if (len != 0 && s == NULL) {
+ krb5_set_error_message (context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ ret = ENOMEM;
+ goto out;
+ }
+
+ ret = wind_utf8ucs2(password.data, s, &len);
+ if (ret) {
+ krb5_set_error_message (context, ret,
+ N_("Password not an UCS2 string", ""));
+ goto out;
+ }
+
+ /* LE encoding */
+ for (i = 0; i < len; i++) {
+ unsigned char p;
+ p = (s[i] & 0xff);
+ EVP_DigestUpdate (m, &p, 1);
+ p = (s[i] >> 8) & 0xff;
+ EVP_DigestUpdate (m, &p, 1);
+ }
+
+ key->keytype = enctype;
+ ret = krb5_data_alloc (&key->keyvalue, 16);
+ if (ret) {
+ krb5_set_error_message (context, ENOMEM, N_("malloc: out of memory", ""));
+ goto out;
+ }
+ EVP_DigestFinal_ex (m, key->keyvalue.data, NULL);
+
+ out:
+ EVP_MD_CTX_destroy(m);
+ if (s)
+ memset (s, 0, len);
+ free (s);
+ return ret;
+}
+
+/*
+ * AES
+ */
+
+int _krb5_AES_string_to_default_iterator = 4096;
+
+static krb5_error_code
+AES_string_to_key(krb5_context context,
+ krb5_enctype enctype,
+ krb5_data password,
+ krb5_salt salt,
+ krb5_data opaque,
+ krb5_keyblock *key)
+{
+ krb5_error_code ret;
+ uint32_t iter;
+ struct encryption_type *et;
+ struct key_data kd;
+
+ if (opaque.length == 0)
+ iter = _krb5_AES_string_to_default_iterator;
+ else if (opaque.length == 4) {
+ unsigned long v;
+ _krb5_get_int(opaque.data, &v, 4);
+ iter = ((uint32_t)v);
+ } else
+ return KRB5_PROG_KEYTYPE_NOSUPP; /* XXX */
+
+ et = _find_enctype(enctype);
+ if (et == NULL)
+ return KRB5_PROG_KEYTYPE_NOSUPP;
+
+ kd.schedule = NULL;
+ ALLOC(kd.key, 1);
+ if(kd.key == NULL) {
+ krb5_set_error_message (context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ kd.key->keytype = enctype;
+ ret = krb5_data_alloc(&kd.key->keyvalue, et->keytype->size);
+ if (ret) {
+ krb5_set_error_message (context, ret, N_("malloc: out of memory", ""));
+ return ret;
+ }
+
+ ret = PKCS5_PBKDF2_HMAC_SHA1(password.data, password.length,
+ salt.saltvalue.data, salt.saltvalue.length,
+ iter,
+ et->keytype->size, kd.key->keyvalue.data);
+ if (ret != 1) {
+ free_key_data(context, &kd, et);
+ krb5_set_error_message(context, KRB5_PROG_KEYTYPE_NOSUPP,
+ "Error calculating s2k");
+ return KRB5_PROG_KEYTYPE_NOSUPP;
+ }
+
+ ret = derive_key(context, et, &kd, "kerberos", strlen("kerberos"));
+ if (ret == 0)
+ ret = krb5_copy_keyblock_contents(context, kd.key, key);
+ free_key_data(context, &kd, et);
+
+ return ret;
+}
+
+static void
+evp_schedule(krb5_context context, struct key_type *kt, struct key_data *kd)
+{
+ struct evp_schedule *key = kd->schedule->data;
+ const EVP_CIPHER *c = (*kt->evp)();
+
+ EVP_CIPHER_CTX_init(&key->ectx);
+ EVP_CIPHER_CTX_init(&key->dctx);
+
+ EVP_CipherInit_ex(&key->ectx, c, NULL, kd->key->keyvalue.data, NULL, 1);
+ EVP_CipherInit_ex(&key->dctx, c, NULL, kd->key->keyvalue.data, NULL, 0);
+}
+
+static void
+evp_cleanup(krb5_context context, struct key_data *kd)
+{
+ struct evp_schedule *key = kd->schedule->data;
+ EVP_CIPHER_CTX_cleanup(&key->ectx);
+ EVP_CIPHER_CTX_cleanup(&key->dctx);
+}
+
+/*
+ *
+ */
+
+#ifdef WEAK_ENCTYPES
+static struct salt_type des_salt[] = {
+ {
+ KRB5_PW_SALT,
+ "pw-salt",
+ krb5_DES_string_to_key
+ },
+#ifdef ENABLE_AFS_STRING_TO_KEY
+ {
+ KRB5_AFS3_SALT,
+ "afs3-salt",
+ DES_AFS3_string_to_key
+ },
+#endif
+ { 0 }
+};
+#endif
+
+#ifdef DES3_OLD_ENCTYPE
+static struct salt_type des3_salt[] = {
+ {
+ KRB5_PW_SALT,
+ "pw-salt",
+ DES3_string_to_key
+ },
+ { 0 }
+};
+#endif
+
+static struct salt_type des3_salt_derived[] = {
+ {
+ KRB5_PW_SALT,
+ "pw-salt",
+ DES3_string_to_key_derived
+ },
+ { 0 }
+};
+
+static struct salt_type AES_salt[] = {
+ {
+ KRB5_PW_SALT,
+ "pw-salt",
+ AES_string_to_key
+ },
+ { 0 }
+};
+
+static struct salt_type arcfour_salt[] = {
+ {
+ KRB5_PW_SALT,
+ "pw-salt",
+ ARCFOUR_string_to_key
+ },
+ { 0 }
+};
+
+/*
+ *
+ */
+
+static struct key_type keytype_null = {
+ KEYTYPE_NULL,
+ "null",
+ 0,
+ 0,
+ 0,
+ NULL,
+ NULL,
+ NULL
+};
+
+#ifdef WEAK_ENCTYPES
+static struct key_type keytype_des_old = {
+ KEYTYPE_DES,
+ "des-old",
+ 56,
+ 8,
+ sizeof(DES_key_schedule),
+ krb5_DES_random_key,
+ krb5_DES_schedule_old,
+ des_salt,
+ krb5_DES_random_to_key
+};
+
+static struct key_type keytype_des = {
+ KEYTYPE_DES,
+ "des",
+ 56,
+ 8,
+ sizeof(struct evp_schedule),
+ krb5_DES_random_key,
+ evp_schedule,
+ des_salt,
+ krb5_DES_random_to_key,
+ evp_cleanup,
+ EVP_des_cbc
+};
+#endif /* WEAK_ENCTYPES */
+
+#ifdef DES3_OLD_ENCTYPE
+static struct key_type keytype_des3 = {
+ KEYTYPE_DES3,
+ "des3",
+ 168,
+ 24,
+ sizeof(struct evp_schedule),
+ DES3_random_key,
+ evp_schedule,
+ des3_salt,
+ DES3_random_to_key,
+ evp_cleanup,
+ EVP_des_ede3_cbc
+};
+#endif
+
+static struct key_type keytype_des3_derived = {
+ KEYTYPE_DES3,
+ "des3",
+ 168,
+ 24,
+ sizeof(struct evp_schedule),
+ DES3_random_key,
+ evp_schedule,
+ des3_salt_derived,
+ DES3_random_to_key,
+ evp_cleanup,
+ EVP_des_ede3_cbc
+};
+
+static struct key_type keytype_aes128 = {
+ KEYTYPE_AES128,
+ "aes-128",
+ 128,
+ 16,
+ sizeof(struct evp_schedule),
+ NULL,
+ evp_schedule,
+ AES_salt,
+ NULL,
+ evp_cleanup,
+ EVP_hcrypto_aes_128_cts
+};
+
+static struct key_type keytype_aes256 = {
+ KEYTYPE_AES256,
+ "aes-256",
+ 256,
+ 32,
+ sizeof(struct evp_schedule),
+ NULL,
+ evp_schedule,
+ AES_salt,
+ NULL,
+ evp_cleanup,
+ EVP_hcrypto_aes_256_cts
+};
+
+static struct key_type keytype_arcfour = {
+ KEYTYPE_ARCFOUR,
+ "arcfour",
+ 128,
+ 16,
+ sizeof(RC4_KEY),
+ NULL,
+ ARCFOUR_schedule,
+ arcfour_salt
+};
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_salttype_to_string (krb5_context context,
+ krb5_enctype etype,
+ krb5_salttype stype,
+ char **string)
+{
+ struct encryption_type *e;
+ struct salt_type *st;
+
+ e = _find_enctype (etype);
+ if (e == NULL) {
+ krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
+ "encryption type %d not supported",
+ etype);
+ return KRB5_PROG_ETYPE_NOSUPP;
+ }
+ for (st = e->keytype->string_to_key; st && st->type; st++) {
+ if (st->type == stype) {
+ *string = strdup (st->name);
+ if (*string == NULL) {
+ krb5_set_error_message (context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ return 0;
+ }
+ }
+ krb5_set_error_message (context, HEIM_ERR_SALTTYPE_NOSUPP,
+ "salttype %d not supported", stype);
+ return HEIM_ERR_SALTTYPE_NOSUPP;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_string_to_salttype (krb5_context context,
+ krb5_enctype etype,
+ const char *string,
+ krb5_salttype *salttype)
+{
+ struct encryption_type *e;
+ struct salt_type *st;
+
+ e = _find_enctype (etype);
+ if (e == NULL) {
+ krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
+ N_("encryption type %d not supported", ""),
+ etype);
+ return KRB5_PROG_ETYPE_NOSUPP;
+ }
+ for (st = e->keytype->string_to_key; st && st->type; st++) {
+ if (strcasecmp (st->name, string) == 0) {
+ *salttype = st->type;
+ return 0;
+ }
+ }
+ krb5_set_error_message(context, HEIM_ERR_SALTTYPE_NOSUPP,
+ N_("salttype %s not supported", ""), string);
+ return HEIM_ERR_SALTTYPE_NOSUPP;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_pw_salt(krb5_context context,
+ krb5_const_principal principal,
+ krb5_salt *salt)
+{
+ size_t len;
+ int i;
+ krb5_error_code ret;
+ char *p;
+
+ salt->salttype = KRB5_PW_SALT;
+ len = strlen(principal->realm);
+ for (i = 0; i < principal->name.name_string.len; ++i)
+ len += strlen(principal->name.name_string.val[i]);
+ ret = krb5_data_alloc (&salt->saltvalue, len);
+ if (ret)
+ return ret;
+ p = salt->saltvalue.data;
+ memcpy (p, principal->realm, strlen(principal->realm));
+ p += strlen(principal->realm);
+ for (i = 0; i < principal->name.name_string.len; ++i) {
+ memcpy (p,
+ principal->name.name_string.val[i],
+ strlen(principal->name.name_string.val[i]));
+ p += strlen(principal->name.name_string.val[i]);
+ }
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_free_salt(krb5_context context,
+ krb5_salt salt)
+{
+ krb5_data_free(&salt.saltvalue);
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_string_to_key_data (krb5_context context,
+ krb5_enctype enctype,
+ krb5_data password,
+ krb5_principal principal,
+ krb5_keyblock *key)
+{
+ krb5_error_code ret;
+ krb5_salt salt;
+
+ ret = krb5_get_pw_salt(context, principal, &salt);
+ if(ret)
+ return ret;
+ ret = krb5_string_to_key_data_salt(context, enctype, password, salt, key);
+ krb5_free_salt(context, salt);
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_string_to_key (krb5_context context,
+ krb5_enctype enctype,
+ const char *password,
+ krb5_principal principal,
+ krb5_keyblock *key)
+{
+ krb5_data pw;
+ pw.data = rk_UNCONST(password);
+ pw.length = strlen(password);
+ return krb5_string_to_key_data(context, enctype, pw, principal, key);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_string_to_key_data_salt (krb5_context context,
+ krb5_enctype enctype,
+ krb5_data password,
+ krb5_salt salt,
+ krb5_keyblock *key)
+{
+ krb5_data opaque;
+ krb5_data_zero(&opaque);
+ return krb5_string_to_key_data_salt_opaque(context, enctype, password,
+ salt, opaque, key);
+}
+
+/*
+ * Do a string -> key for encryption type `enctype' operation on
+ * `password' (with salt `salt' and the enctype specific data string
+ * `opaque'), returning the resulting key in `key'
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_string_to_key_data_salt_opaque (krb5_context context,
+ krb5_enctype enctype,
+ krb5_data password,
+ krb5_salt salt,
+ krb5_data opaque,
+ krb5_keyblock *key)
+{
+ struct encryption_type *et =_find_enctype(enctype);
+ struct salt_type *st;
+ if(et == NULL) {
+ krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
+ N_("encryption type %d not supported", ""),
+ enctype);
+ return KRB5_PROG_ETYPE_NOSUPP;
+ }
+ for(st = et->keytype->string_to_key; st && st->type; st++)
+ if(st->type == salt.salttype)
+ return (*st->string_to_key)(context, enctype, password,
+ salt, opaque, key);
+ krb5_set_error_message(context, HEIM_ERR_SALTTYPE_NOSUPP,
+ N_("salt type %d not supported", ""),
+ salt.salttype);
+ return HEIM_ERR_SALTTYPE_NOSUPP;
+}
+
+/*
+ * Do a string -> key for encryption type `enctype' operation on the
+ * string `password' (with salt `salt'), returning the resulting key
+ * in `key'
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_string_to_key_salt (krb5_context context,
+ krb5_enctype enctype,
+ const char *password,
+ krb5_salt salt,
+ krb5_keyblock *key)
+{
+ krb5_data pw;
+ pw.data = rk_UNCONST(password);
+ pw.length = strlen(password);
+ return krb5_string_to_key_data_salt(context, enctype, pw, salt, key);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_string_to_key_salt_opaque (krb5_context context,
+ krb5_enctype enctype,
+ const char *password,
+ krb5_salt salt,
+ krb5_data opaque,
+ krb5_keyblock *key)
+{
+ krb5_data pw;
+ pw.data = rk_UNCONST(password);
+ pw.length = strlen(password);
+ return krb5_string_to_key_data_salt_opaque(context, enctype,
+ pw, salt, opaque, key);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_enctype_keysize(krb5_context context,
+ krb5_enctype type,
+ size_t *keysize)
+{
+ struct encryption_type *et = _find_enctype(type);
+ if(et == NULL) {
+ krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
+ N_("encryption type %d not supported", ""),
+ type);
+ return KRB5_PROG_ETYPE_NOSUPP;
+ }
+ *keysize = et->keytype->size;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_enctype_keybits(krb5_context context,
+ krb5_enctype type,
+ size_t *keybits)
+{
+ struct encryption_type *et = _find_enctype(type);
+ if(et == NULL) {
+ krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
+ "encryption type %d not supported",
+ type);
+ return KRB5_PROG_ETYPE_NOSUPP;
+ }
+ *keybits = et->keytype->bits;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_generate_random_keyblock(krb5_context context,
+ krb5_enctype type,
+ krb5_keyblock *key)
+{
+ krb5_error_code ret;
+ struct encryption_type *et = _find_enctype(type);
+ if(et == NULL) {
+ krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
+ N_("encryption type %d not supported", ""),
+ type);
+ return KRB5_PROG_ETYPE_NOSUPP;
+ }
+ ret = krb5_data_alloc(&key->keyvalue, et->keytype->size);
+ if(ret)
+ return ret;
+ key->keytype = type;
+ if(et->keytype->random_key)
+ (*et->keytype->random_key)(context, key);
+ else
+ krb5_generate_random_block(key->keyvalue.data,
+ key->keyvalue.length);
+ return 0;
+}
+
+static krb5_error_code
+_key_schedule(krb5_context context,
+ struct key_data *key)
+{
+ krb5_error_code ret;
+ struct encryption_type *et = _find_enctype(key->key->keytype);
+ struct key_type *kt = et->keytype;
+
+ if(kt->schedule == NULL)
+ return 0;
+ if (key->schedule != NULL)
+ return 0;
+ ALLOC(key->schedule, 1);
+ if(key->schedule == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ ret = krb5_data_alloc(key->schedule, kt->schedule_size);
+ if(ret) {
+ free(key->schedule);
+ key->schedule = NULL;
+ return ret;
+ }
+ (*kt->schedule)(context, kt, key);
+ return 0;
+}
+
+/************************************************************
+ * *
+ ************************************************************/
+
+static krb5_error_code
+NONE_checksum(krb5_context context,
+ struct key_data *key,
+ const void *data,
+ size_t len,
+ unsigned usage,
+ Checksum *C)
+{
+ return 0;
+}
+
+static krb5_error_code
+CRC32_checksum(krb5_context context,
+ struct key_data *key,
+ const void *data,
+ size_t len,
+ unsigned usage,
+ Checksum *C)
+{
+ uint32_t crc;
+ unsigned char *r = C->checksum.data;
+ _krb5_crc_init_table ();
+ crc = _krb5_crc_update (data, len, 0);
+ r[0] = crc & 0xff;
+ r[1] = (crc >> 8) & 0xff;
+ r[2] = (crc >> 16) & 0xff;
+ r[3] = (crc >> 24) & 0xff;
+ return 0;
+}
+
+static krb5_error_code
+RSA_MD4_checksum(krb5_context context,
+ struct key_data *key,
+ const void *data,
+ size_t len,
+ unsigned usage,
+ Checksum *C)
+{
+ if (EVP_Digest(data, len, C->checksum.data, NULL, EVP_md4(), NULL) != 1)
+ krb5_abortx(context, "md4 checksum failed");
+ return 0;
+}
+
+static krb5_error_code
+des_checksum(krb5_context context,
+ const EVP_MD *evp_md,
+ struct key_data *key,
+ const void *data,
+ size_t len,
+ Checksum *cksum)
+{
+ struct evp_schedule *ctx = key->schedule->data;
+ EVP_MD_CTX *m;
+ DES_cblock ivec;
+ unsigned char *p = cksum->checksum.data;
+
+ krb5_generate_random_block(p, 8);
+
+ m = EVP_MD_CTX_create();
+ if (m == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+
+ EVP_DigestInit_ex(m, evp_md, NULL);
+ EVP_DigestUpdate(m, p, 8);
+ EVP_DigestUpdate(m, data, len);
+ EVP_DigestFinal_ex (m, p + 8, NULL);
+ EVP_MD_CTX_destroy(m);
+ memset (&ivec, 0, sizeof(ivec));
+ EVP_CipherInit_ex(&ctx->ectx, NULL, NULL, NULL, (void *)&ivec, -1);
+ EVP_Cipher(&ctx->ectx, p, p, 24);
+
+ return 0;
+}
+
+static krb5_error_code
+des_verify(krb5_context context,
+ const EVP_MD *evp_md,
+ struct key_data *key,
+ const void *data,
+ size_t len,
+ Checksum *C)
+{
+ struct evp_schedule *ctx = key->schedule->data;
+ EVP_MD_CTX *m;
+ unsigned char tmp[24];
+ unsigned char res[16];
+ DES_cblock ivec;
+ krb5_error_code ret = 0;
+
+ m = EVP_MD_CTX_create();
+ if (m == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+
+ memset(&ivec, 0, sizeof(ivec));
+ EVP_CipherInit_ex(&ctx->dctx, NULL, NULL, NULL, (void *)&ivec, -1);
+ EVP_Cipher(&ctx->dctx, tmp, C->checksum.data, 24);
+
+ EVP_DigestInit_ex(m, evp_md, NULL);
+ EVP_DigestUpdate(m, tmp, 8); /* confounder */
+ EVP_DigestUpdate(m, data, len);
+ EVP_DigestFinal_ex (m, res, NULL);
+ EVP_MD_CTX_destroy(m);
+ if(memcmp(res, tmp + 8, sizeof(res)) != 0) {
+ krb5_clear_error_message (context);
+ ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
+ }
+ memset(tmp, 0, sizeof(tmp));
+ memset(res, 0, sizeof(res));
+ return ret;
+}
+
+static krb5_error_code
+RSA_MD4_DES_checksum(krb5_context context,
+ struct key_data *key,
+ const void *data,
+ size_t len,
+ unsigned usage,
+ Checksum *cksum)
+{
+ return des_checksum(context, EVP_md4(), key, data, len, cksum);
+}
+
+static krb5_error_code
+RSA_MD4_DES_verify(krb5_context context,
+ struct key_data *key,
+ const void *data,
+ size_t len,
+ unsigned usage,
+ Checksum *C)
+{
+ return des_verify(context, EVP_md5(), key, data, len, C);
+}
+
+static krb5_error_code
+RSA_MD5_checksum(krb5_context context,
+ struct key_data *key,
+ const void *data,
+ size_t len,
+ unsigned usage,
+ Checksum *C)
+{
+ if (EVP_Digest(data, len, C->checksum.data, NULL, EVP_md5(), NULL) != 1)
+ krb5_abortx(context, "md5 checksum failed");
+ return 0;
+}
+
+static krb5_error_code
+RSA_MD5_DES_checksum(krb5_context context,
+ struct key_data *key,
+ const void *data,
+ size_t len,
+ unsigned usage,
+ Checksum *C)
+{
+ return des_checksum(context, EVP_md5(), key, data, len, C);
+}
+
+static krb5_error_code
+RSA_MD5_DES_verify(krb5_context context,
+ struct key_data *key,
+ const void *data,
+ size_t len,
+ unsigned usage,
+ Checksum *C)
+{
+ return des_verify(context, EVP_md5(), key, data, len, C);
+}
+
+#ifdef DES3_OLD_ENCTYPE
+static krb5_error_code
+RSA_MD5_DES3_checksum(krb5_context context,
+ struct key_data *key,
+ const void *data,
+ size_t len,
+ unsigned usage,
+ Checksum *C)
+{
+ return des_checksum(context, EVP_md5(), key, data, len, C);
+}
+
+static krb5_error_code
+RSA_MD5_DES3_verify(krb5_context context,
+ struct key_data *key,
+ const void *data,
+ size_t len,
+ unsigned usage,
+ Checksum *C)
+{
+ return des_verify(context, EVP_md5(), key, data, len, C);
+}
+#endif
+
+static krb5_error_code
+SHA1_checksum(krb5_context context,
+ struct key_data *key,
+ const void *data,
+ size_t len,
+ unsigned usage,
+ Checksum *C)
+{
+ if (EVP_Digest(data, len, C->checksum.data, NULL, EVP_sha1(), NULL) != 1)
+ krb5_abortx(context, "sha1 checksum failed");
+ return 0;
+}
+
+/* HMAC according to RFC2104 */
+static krb5_error_code
+hmac(krb5_context context,
+ struct checksum_type *cm,
+ const void *data,
+ size_t len,
+ unsigned usage,
+ struct key_data *keyblock,
+ Checksum *result)
+{
+ unsigned char *ipad, *opad;
+ unsigned char *key;
+ size_t key_len;
+ int i;
+
+ ipad = malloc(cm->blocksize + len);
+ if (ipad == NULL)
+ return ENOMEM;
+ opad = malloc(cm->blocksize + cm->checksumsize);
+ if (opad == NULL) {
+ free(ipad);
+ return ENOMEM;
+ }
+ memset(ipad, 0x36, cm->blocksize);
+ memset(opad, 0x5c, cm->blocksize);
+
+ if(keyblock->key->keyvalue.length > cm->blocksize){
+ (*cm->checksum)(context,
+ keyblock,
+ keyblock->key->keyvalue.data,
+ keyblock->key->keyvalue.length,
+ usage,
+ result);
+ key = result->checksum.data;
+ key_len = result->checksum.length;
+ } else {
+ key = keyblock->key->keyvalue.data;
+ key_len = keyblock->key->keyvalue.length;
+ }
+ for(i = 0; i < key_len; i++){
+ ipad[i] ^= key[i];
+ opad[i] ^= key[i];
+ }
+ memcpy(ipad + cm->blocksize, data, len);
+ (*cm->checksum)(context, keyblock, ipad, cm->blocksize + len,
+ usage, result);
+ memcpy(opad + cm->blocksize, result->checksum.data,
+ result->checksum.length);
+ (*cm->checksum)(context, keyblock, opad,
+ cm->blocksize + cm->checksumsize, usage, result);
+ memset(ipad, 0, cm->blocksize + len);
+ free(ipad);
+ memset(opad, 0, cm->blocksize + cm->checksumsize);
+ free(opad);
+
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_hmac(krb5_context context,
+ krb5_cksumtype cktype,
+ const void *data,
+ size_t len,
+ unsigned usage,
+ krb5_keyblock *key,
+ Checksum *result)
+{
+ struct checksum_type *c = _find_checksum(cktype);
+ struct key_data kd;
+ krb5_error_code ret;
+
+ if (c == NULL) {
+ krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP,
+ N_("checksum type %d not supported", ""),
+ cktype);
+ return KRB5_PROG_SUMTYPE_NOSUPP;
+ }
+
+ kd.key = key;
+ kd.schedule = NULL;
+
+ ret = hmac(context, c, data, len, usage, &kd, result);
+
+ if (kd.schedule)
+ krb5_free_data(context, kd.schedule);
+
+ return ret;
+}
+
+static krb5_error_code
+SP_HMAC_SHA1_checksum(krb5_context context,
+ struct key_data *key,
+ const void *data,
+ size_t len,
+ unsigned usage,
+ Checksum *result)
+{
+ struct checksum_type *c = _find_checksum(CKSUMTYPE_SHA1);
+ Checksum res;
+ char sha1_data[20];
+ krb5_error_code ret;
+
+ res.checksum.data = sha1_data;
+ res.checksum.length = sizeof(sha1_data);
+
+ ret = hmac(context, c, data, len, usage, key, &res);
+ if (ret)
+ krb5_abortx(context, "hmac failed");
+ memcpy(result->checksum.data, res.checksum.data, result->checksum.length);
+ return 0;
+}
+
+/*
+ * checksum according to section 5. of draft-brezak-win2k-krb-rc4-hmac-03.txt
+ */
+
+static krb5_error_code
+HMAC_MD5_checksum(krb5_context context,
+ struct key_data *key,
+ const void *data,
+ size_t len,
+ unsigned usage,
+ Checksum *result)
+{
+ EVP_MD_CTX *m;
+ struct checksum_type *c = _find_checksum (CKSUMTYPE_RSA_MD5);
+ const char signature[] = "signaturekey";
+ Checksum ksign_c;
+ struct key_data ksign;
+ krb5_keyblock kb;
+ unsigned char t[4];
+ unsigned char tmp[16];
+ unsigned char ksign_c_data[16];
+ krb5_error_code ret;
+
+ m = EVP_MD_CTX_create();
+ if (m == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ ksign_c.checksum.length = sizeof(ksign_c_data);
+ ksign_c.checksum.data = ksign_c_data;
+ ret = hmac(context, c, signature, sizeof(signature), 0, key, &ksign_c);
+ if (ret) {
+ EVP_MD_CTX_destroy(m);
+ return ret;
+ }
+ ksign.key = &kb;
+ kb.keyvalue = ksign_c.checksum;
+ EVP_DigestInit_ex(m, EVP_md5(), NULL);
+ t[0] = (usage >> 0) & 0xFF;
+ t[1] = (usage >> 8) & 0xFF;
+ t[2] = (usage >> 16) & 0xFF;
+ t[3] = (usage >> 24) & 0xFF;
+ EVP_DigestUpdate(m, t, 4);
+ EVP_DigestUpdate(m, data, len);
+ EVP_DigestFinal_ex (m, tmp, NULL);
+ EVP_MD_CTX_destroy(m);
+
+ ret = hmac(context, c, tmp, sizeof(tmp), 0, &ksign, result);
+ if (ret)
+ return ret;
+ return 0;
+}
+
+static struct checksum_type checksum_none = {
+ CKSUMTYPE_NONE,
+ "none",
+ 1,
+ 0,
+ 0,
+ NONE_checksum,
+ NULL
+};
+static struct checksum_type checksum_crc32 = {
+ CKSUMTYPE_CRC32,
+ "crc32",
+ 1,
+ 4,
+ 0,
+ CRC32_checksum,
+ NULL
+};
+static struct checksum_type checksum_rsa_md4 = {
+ CKSUMTYPE_RSA_MD4,
+ "rsa-md4",
+ 64,
+ 16,
+ F_CPROOF,
+ RSA_MD4_checksum,
+ NULL
+};
+static struct checksum_type checksum_rsa_md4_des = {
+ CKSUMTYPE_RSA_MD4_DES,
+ "rsa-md4-des",
+ 64,
+ 24,
+ F_KEYED | F_CPROOF | F_VARIANT,
+ RSA_MD4_DES_checksum,
+ RSA_MD4_DES_verify
+};
+static struct checksum_type checksum_rsa_md5 = {
+ CKSUMTYPE_RSA_MD5,
+ "rsa-md5",
+ 64,
+ 16,
+ F_CPROOF,
+ RSA_MD5_checksum,
+ NULL
+};
+static struct checksum_type checksum_rsa_md5_des = {
+ CKSUMTYPE_RSA_MD5_DES,
+ "rsa-md5-des",
+ 64,
+ 24,
+ F_KEYED | F_CPROOF | F_VARIANT,
+ RSA_MD5_DES_checksum,
+ RSA_MD5_DES_verify
+};
+#ifdef DES3_OLD_ENCTYPE
+static struct checksum_type checksum_rsa_md5_des3 = {
+ CKSUMTYPE_RSA_MD5_DES3,
+ "rsa-md5-des3",
+ 64,
+ 24,
+ F_KEYED | F_CPROOF | F_VARIANT,
+ RSA_MD5_DES3_checksum,
+ RSA_MD5_DES3_verify
+};
+#endif
+static struct checksum_type checksum_sha1 = {
+ CKSUMTYPE_SHA1,
+ "sha1",
+ 64,
+ 20,
+ F_CPROOF,
+ SHA1_checksum,
+ NULL
+};
+static struct checksum_type checksum_hmac_sha1_des3 = {
+ CKSUMTYPE_HMAC_SHA1_DES3,
+ "hmac-sha1-des3",
+ 64,
+ 20,
+ F_KEYED | F_CPROOF | F_DERIVED,
+ SP_HMAC_SHA1_checksum,
+ NULL
+};
+
+static struct checksum_type checksum_hmac_sha1_aes128 = {
+ CKSUMTYPE_HMAC_SHA1_96_AES_128,
+ "hmac-sha1-96-aes128",
+ 64,
+ 12,
+ F_KEYED | F_CPROOF | F_DERIVED,
+ SP_HMAC_SHA1_checksum,
+ NULL
+};
+
+static struct checksum_type checksum_hmac_sha1_aes256 = {
+ CKSUMTYPE_HMAC_SHA1_96_AES_256,
+ "hmac-sha1-96-aes256",
+ 64,
+ 12,
+ F_KEYED | F_CPROOF | F_DERIVED,
+ SP_HMAC_SHA1_checksum,
+ NULL
+};
+
+static struct checksum_type checksum_hmac_md5 = {
+ CKSUMTYPE_HMAC_MD5,
+ "hmac-md5",
+ 64,
+ 16,
+ F_KEYED | F_CPROOF,
+ HMAC_MD5_checksum,
+ NULL
+};
+
+static struct checksum_type *checksum_types[] = {
+ &checksum_none,
+ &checksum_crc32,
+ &checksum_rsa_md4,
+ &checksum_rsa_md4_des,
+ &checksum_rsa_md5,
+ &checksum_rsa_md5_des,
+#ifdef DES3_OLD_ENCTYPE
+ &checksum_rsa_md5_des3,
+#endif
+ &checksum_sha1,
+ &checksum_hmac_sha1_des3,
+ &checksum_hmac_sha1_aes128,
+ &checksum_hmac_sha1_aes256,
+ &checksum_hmac_md5
+};
+
+static int num_checksums = sizeof(checksum_types) / sizeof(checksum_types[0]);
+
+static struct checksum_type *
+_find_checksum(krb5_cksumtype type)
+{
+ int i;
+ for(i = 0; i < num_checksums; i++)
+ if(checksum_types[i]->type == type)
+ return checksum_types[i];
+ return NULL;
+}
+
+static krb5_error_code
+get_checksum_key(krb5_context context,
+ krb5_crypto crypto,
+ unsigned usage, /* not krb5_key_usage */
+ struct checksum_type *ct,
+ struct key_data **key)
+{
+ krb5_error_code ret = 0;
+
+ if(ct->flags & F_DERIVED)
+ ret = _get_derived_key(context, crypto, usage, key);
+ else if(ct->flags & F_VARIANT) {
+ int i;
+
+ *key = _new_derived_key(crypto, 0xff/* KRB5_KU_RFC1510_VARIANT */);
+ if(*key == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ ret = krb5_copy_keyblock(context, crypto->key.key, &(*key)->key);
+ if(ret)
+ return ret;
+ for(i = 0; i < (*key)->key->keyvalue.length; i++)
+ ((unsigned char*)(*key)->key->keyvalue.data)[i] ^= 0xF0;
+ } else {
+ *key = &crypto->key;
+ }
+ if(ret == 0)
+ ret = _key_schedule(context, *key);
+ return ret;
+}
+
+static krb5_error_code
+create_checksum (krb5_context context,
+ struct checksum_type *ct,
+ krb5_crypto crypto,
+ unsigned usage,
+ void *data,
+ size_t len,
+ Checksum *result)
+{
+ krb5_error_code ret;
+ struct key_data *dkey;
+ int keyed_checksum;
+
+ if (ct->flags & F_DISABLED) {
+ krb5_clear_error_message (context);
+ return KRB5_PROG_SUMTYPE_NOSUPP;
+ }
+ keyed_checksum = (ct->flags & F_KEYED) != 0;
+ if(keyed_checksum && crypto == NULL) {
+ krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP,
+ N_("Checksum type %s is keyed but no "
+ "crypto context (key) was passed in", ""),
+ ct->name);
+ return KRB5_PROG_SUMTYPE_NOSUPP; /* XXX */
+ }
+ if(keyed_checksum) {
+ ret = get_checksum_key(context, crypto, usage, ct, &dkey);
+ if (ret)
+ return ret;
+ } else
+ dkey = NULL;
+ result->cksumtype = ct->type;
+ ret = krb5_data_alloc(&result->checksum, ct->checksumsize);
+ if (ret)
+ return (ret);
+ return (*ct->checksum)(context, dkey, data, len, usage, result);
+}
+
+static int
+arcfour_checksum_p(struct checksum_type *ct, krb5_crypto crypto)
+{
+ return (ct->type == CKSUMTYPE_HMAC_MD5) &&
+ (crypto->key.key->keytype == KEYTYPE_ARCFOUR);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_create_checksum(krb5_context context,
+ krb5_crypto crypto,
+ krb5_key_usage usage,
+ int type,
+ void *data,
+ size_t len,
+ Checksum *result)
+{
+ struct checksum_type *ct = NULL;
+ unsigned keyusage;
+
+ /* type 0 -> pick from crypto */
+ if (type) {
+ ct = _find_checksum(type);
+ } else if (crypto) {
+ ct = crypto->et->keyed_checksum;
+ if (ct == NULL)
+ ct = crypto->et->checksum;
+ }
+
+ if(ct == NULL) {
+ krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP,
+ N_("checksum type %d not supported", ""),
+ type);
+ return KRB5_PROG_SUMTYPE_NOSUPP;
+ }
+
+ if (arcfour_checksum_p(ct, crypto)) {
+ keyusage = usage;
+ usage2arcfour(context, &keyusage);
+ } else
+ keyusage = CHECKSUM_USAGE(usage);
+
+ return create_checksum(context, ct, crypto, keyusage,
+ data, len, result);
+}
+
+static krb5_error_code
+verify_checksum(krb5_context context,
+ krb5_crypto crypto,
+ unsigned usage, /* not krb5_key_usage */
+ void *data,
+ size_t len,
+ Checksum *cksum)
+{
+ krb5_error_code ret;
+ struct key_data *dkey;
+ int keyed_checksum;
+ Checksum c;
+ struct checksum_type *ct;
+
+ ct = _find_checksum(cksum->cksumtype);
+ if (ct == NULL || (ct->flags & F_DISABLED)) {
+ krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP,
+ N_("checksum type %d not supported", ""),
+ cksum->cksumtype);
+ return KRB5_PROG_SUMTYPE_NOSUPP;
+ }
+ if(ct->checksumsize != cksum->checksum.length) {
+ krb5_clear_error_message (context);
+ return KRB5KRB_AP_ERR_BAD_INTEGRITY; /* XXX */
+ }
+ keyed_checksum = (ct->flags & F_KEYED) != 0;
+ if(keyed_checksum && crypto == NULL) {
+ krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP,
+ N_("Checksum type %s is keyed but no "
+ "crypto context (key) was passed in", ""),
+ ct->name);
+ return KRB5_PROG_SUMTYPE_NOSUPP; /* XXX */
+ }
+ if(keyed_checksum) {
+ ret = get_checksum_key(context, crypto, usage, ct, &dkey);
+ if (ret)
+ return ret;
+ } else
+ dkey = NULL;
+ if(ct->verify)
+ return (*ct->verify)(context, dkey, data, len, usage, cksum);
+
+ ret = krb5_data_alloc (&c.checksum, ct->checksumsize);
+ if (ret)
+ return ret;
+
+ ret = (*ct->checksum)(context, dkey, data, len, usage, &c);
+ if (ret) {
+ krb5_data_free(&c.checksum);
+ return ret;
+ }
+
+ if(c.checksum.length != cksum->checksum.length ||
+ memcmp(c.checksum.data, cksum->checksum.data, c.checksum.length)) {
+ krb5_clear_error_message (context);
+ ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
+ } else {
+ ret = 0;
+ }
+ krb5_data_free (&c.checksum);
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_verify_checksum(krb5_context context,
+ krb5_crypto crypto,
+ krb5_key_usage usage,
+ void *data,
+ size_t len,
+ Checksum *cksum)
+{
+ struct checksum_type *ct;
+ unsigned keyusage;
+
+ ct = _find_checksum(cksum->cksumtype);
+ if(ct == NULL) {
+ krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP,
+ N_("checksum type %d not supported", ""),
+ cksum->cksumtype);
+ return KRB5_PROG_SUMTYPE_NOSUPP;
+ }
+
+ if (arcfour_checksum_p(ct, crypto)) {
+ keyusage = usage;
+ usage2arcfour(context, &keyusage);
+ } else
+ keyusage = CHECKSUM_USAGE(usage);
+
+ return verify_checksum(context, crypto, keyusage,
+ data, len, cksum);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_crypto_get_checksum_type(krb5_context context,
+ krb5_crypto crypto,
+ krb5_cksumtype *type)
+{
+ struct checksum_type *ct = NULL;
+
+ if (crypto != NULL) {
+ ct = crypto->et->keyed_checksum;
+ if (ct == NULL)
+ ct = crypto->et->checksum;
+ }
+
+ if (ct == NULL) {
+ krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP,
+ N_("checksum type not found", ""));
+ return KRB5_PROG_SUMTYPE_NOSUPP;
+ }
+
+ *type = ct->type;
+
+ return 0;
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_checksumsize(krb5_context context,
+ krb5_cksumtype type,
+ size_t *size)
+{
+ struct checksum_type *ct = _find_checksum(type);
+ if(ct == NULL) {
+ krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP,
+ N_("checksum type %d not supported", ""),
+ type);
+ return KRB5_PROG_SUMTYPE_NOSUPP;
+ }
+ *size = ct->checksumsize;
+ return 0;
+}
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_checksum_is_keyed(krb5_context context,
+ krb5_cksumtype type)
+{
+ struct checksum_type *ct = _find_checksum(type);
+ if(ct == NULL) {
+ if (context)
+ krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP,
+ N_("checksum type %d not supported", ""),
+ type);
+ return KRB5_PROG_SUMTYPE_NOSUPP;
+ }
+ return ct->flags & F_KEYED;
+}
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_checksum_is_collision_proof(krb5_context context,
+ krb5_cksumtype type)
+{
+ struct checksum_type *ct = _find_checksum(type);
+ if(ct == NULL) {
+ if (context)
+ krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP,
+ N_("checksum type %d not supported", ""),
+ type);
+ return KRB5_PROG_SUMTYPE_NOSUPP;
+ }
+ return ct->flags & F_CPROOF;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_checksum_disable(krb5_context context,
+ krb5_cksumtype type)
+{
+ struct checksum_type *ct = _find_checksum(type);
+ if(ct == NULL) {
+ if (context)
+ krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP,
+ N_("checksum type %d not supported", ""),
+ type);
+ return KRB5_PROG_SUMTYPE_NOSUPP;
+ }
+ ct->flags |= F_DISABLED;
+ return 0;
+}
+
+/************************************************************
+ * *
+ ************************************************************/
+
+static krb5_error_code
+NULL_encrypt(krb5_context context,
+ struct key_data *key,
+ void *data,
+ size_t len,
+ krb5_boolean encryptp,
+ int usage,
+ void *ivec)
+{
+ return 0;
+}
+
+static krb5_error_code
+evp_encrypt(krb5_context context,
+ struct key_data *key,
+ void *data,
+ size_t len,
+ krb5_boolean encryptp,
+ int usage,
+ void *ivec)
+{
+ struct evp_schedule *ctx = key->schedule->data;
+ EVP_CIPHER_CTX *c;
+ c = encryptp ? &ctx->ectx : &ctx->dctx;
+ if (ivec == NULL) {
+ /* alloca ? */
+ size_t len = EVP_CIPHER_CTX_iv_length(c);
+ void *loiv = malloc(len);
+ if (loiv == NULL) {
+ krb5_clear_error_message(context);
+ return ENOMEM;
+ }
+ memset(loiv, 0, len);
+ EVP_CipherInit_ex(c, NULL, NULL, NULL, loiv, -1);
+ free(loiv);
+ } else
+ EVP_CipherInit_ex(c, NULL, NULL, NULL, ivec, -1);
+ EVP_Cipher(c, data, data, len);
+ return 0;
+}
+
+#ifdef WEAK_ENCTYPES
+static krb5_error_code
+evp_des_encrypt_null_ivec(krb5_context context,
+ struct key_data *key,
+ void *data,
+ size_t len,
+ krb5_boolean encryptp,
+ int usage,
+ void *ignore_ivec)
+{
+ struct evp_schedule *ctx = key->schedule->data;
+ EVP_CIPHER_CTX *c;
+ DES_cblock ivec;
+ memset(&ivec, 0, sizeof(ivec));
+ c = encryptp ? &ctx->ectx : &ctx->dctx;
+ EVP_CipherInit_ex(c, NULL, NULL, NULL, (void *)&ivec, -1);
+ EVP_Cipher(c, data, data, len);
+ return 0;
+}
+
+static krb5_error_code
+evp_des_encrypt_key_ivec(krb5_context context,
+ struct key_data *key,
+ void *data,
+ size_t len,
+ krb5_boolean encryptp,
+ int usage,
+ void *ignore_ivec)
+{
+ struct evp_schedule *ctx = key->schedule->data;
+ EVP_CIPHER_CTX *c;
+ DES_cblock ivec;
+ memcpy(&ivec, key->key->keyvalue.data, sizeof(ivec));
+ c = encryptp ? &ctx->ectx : &ctx->dctx;
+ EVP_CipherInit_ex(c, NULL, NULL, NULL, (void *)&ivec, -1);
+ EVP_Cipher(c, data, data, len);
+ return 0;
+}
+
+static krb5_error_code
+DES_CFB64_encrypt_null_ivec(krb5_context context,
+ struct key_data *key,
+ void *data,
+ size_t len,
+ krb5_boolean encryptp,
+ int usage,
+ void *ignore_ivec)
+{
+ DES_cblock ivec;
+ int num = 0;
+ DES_key_schedule *s = key->schedule->data;
+ memset(&ivec, 0, sizeof(ivec));
+
+ DES_cfb64_encrypt(data, data, len, s, &ivec, &num, encryptp);
+ return 0;
+}
+
+static krb5_error_code
+DES_PCBC_encrypt_key_ivec(krb5_context context,
+ struct key_data *key,
+ void *data,
+ size_t len,
+ krb5_boolean encryptp,
+ int usage,
+ void *ignore_ivec)
+{
+ DES_cblock ivec;
+ DES_key_schedule *s = key->schedule->data;
+ memcpy(&ivec, key->key->keyvalue.data, sizeof(ivec));
+
+ DES_pcbc_encrypt(data, data, len, s, &ivec, encryptp);
+ return 0;
+}
+#endif
+
+/*
+ * section 6 of draft-brezak-win2k-krb-rc4-hmac-03
+ *
+ * warning: not for small children
+ */
+
+static krb5_error_code
+ARCFOUR_subencrypt(krb5_context context,
+ struct key_data *key,
+ void *data,
+ size_t len,
+ unsigned usage,
+ void *ivec)
+{
+ struct checksum_type *c = _find_checksum (CKSUMTYPE_RSA_MD5);
+ Checksum k1_c, k2_c, k3_c, cksum;
+ struct key_data ke;
+ krb5_keyblock kb;
+ unsigned char t[4];
+ RC4_KEY rc4_key;
+ unsigned char *cdata = data;
+ unsigned char k1_c_data[16], k2_c_data[16], k3_c_data[16];
+ krb5_error_code ret;
+
+ t[0] = (usage >> 0) & 0xFF;
+ t[1] = (usage >> 8) & 0xFF;
+ t[2] = (usage >> 16) & 0xFF;
+ t[3] = (usage >> 24) & 0xFF;
+
+ k1_c.checksum.length = sizeof(k1_c_data);
+ k1_c.checksum.data = k1_c_data;
+
+ ret = hmac(NULL, c, t, sizeof(t), 0, key, &k1_c);
+ if (ret)
+ krb5_abortx(context, "hmac failed");
+
+ memcpy (k2_c_data, k1_c_data, sizeof(k1_c_data));
+
+ k2_c.checksum.length = sizeof(k2_c_data);
+ k2_c.checksum.data = k2_c_data;
+
+ ke.key = &kb;
+ kb.keyvalue = k2_c.checksum;
+
+ cksum.checksum.length = 16;
+ cksum.checksum.data = data;
+
+ ret = hmac(NULL, c, cdata + 16, len - 16, 0, &ke, &cksum);
+ if (ret)
+ krb5_abortx(context, "hmac failed");
+
+ ke.key = &kb;
+ kb.keyvalue = k1_c.checksum;
+
+ k3_c.checksum.length = sizeof(k3_c_data);
+ k3_c.checksum.data = k3_c_data;
+
+ ret = hmac(NULL, c, data, 16, 0, &ke, &k3_c);
+ if (ret)
+ krb5_abortx(context, "hmac failed");
+
+ RC4_set_key (&rc4_key, k3_c.checksum.length, k3_c.checksum.data);
+ RC4 (&rc4_key, len - 16, cdata + 16, cdata + 16);
+ memset (k1_c_data, 0, sizeof(k1_c_data));
+ memset (k2_c_data, 0, sizeof(k2_c_data));
+ memset (k3_c_data, 0, sizeof(k3_c_data));
+ return 0;
+}
+
+static krb5_error_code
+ARCFOUR_subdecrypt(krb5_context context,
+ struct key_data *key,
+ void *data,
+ size_t len,
+ unsigned usage,
+ void *ivec)
+{
+ struct checksum_type *c = _find_checksum (CKSUMTYPE_RSA_MD5);
+ Checksum k1_c, k2_c, k3_c, cksum;
+ struct key_data ke;
+ krb5_keyblock kb;
+ unsigned char t[4];
+ RC4_KEY rc4_key;
+ unsigned char *cdata = data;
+ unsigned char k1_c_data[16], k2_c_data[16], k3_c_data[16];
+ unsigned char cksum_data[16];
+ krb5_error_code ret;
+
+ t[0] = (usage >> 0) & 0xFF;
+ t[1] = (usage >> 8) & 0xFF;
+ t[2] = (usage >> 16) & 0xFF;
+ t[3] = (usage >> 24) & 0xFF;
+
+ k1_c.checksum.length = sizeof(k1_c_data);
+ k1_c.checksum.data = k1_c_data;
+
+ ret = hmac(NULL, c, t, sizeof(t), 0, key, &k1_c);
+ if (ret)
+ krb5_abortx(context, "hmac failed");
+
+ memcpy (k2_c_data, k1_c_data, sizeof(k1_c_data));
+
+ k2_c.checksum.length = sizeof(k2_c_data);
+ k2_c.checksum.data = k2_c_data;
+
+ ke.key = &kb;
+ kb.keyvalue = k1_c.checksum;
+
+ k3_c.checksum.length = sizeof(k3_c_data);
+ k3_c.checksum.data = k3_c_data;
+
+ ret = hmac(NULL, c, cdata, 16, 0, &ke, &k3_c);
+ if (ret)
+ krb5_abortx(context, "hmac failed");
+
+ RC4_set_key (&rc4_key, k3_c.checksum.length, k3_c.checksum.data);
+ RC4 (&rc4_key, len - 16, cdata + 16, cdata + 16);
+
+ ke.key = &kb;
+ kb.keyvalue = k2_c.checksum;
+
+ cksum.checksum.length = 16;
+ cksum.checksum.data = cksum_data;
+
+ ret = hmac(NULL, c, cdata + 16, len - 16, 0, &ke, &cksum);
+ if (ret)
+ krb5_abortx(context, "hmac failed");
+
+ memset (k1_c_data, 0, sizeof(k1_c_data));
+ memset (k2_c_data, 0, sizeof(k2_c_data));
+ memset (k3_c_data, 0, sizeof(k3_c_data));
+
+ if (memcmp (cksum.checksum.data, data, 16) != 0) {
+ krb5_clear_error_message (context);
+ return KRB5KRB_AP_ERR_BAD_INTEGRITY;
+ } else {
+ return 0;
+ }
+}
+
+/*
+ * convert the usage numbers used in
+ * draft-ietf-cat-kerb-key-derivation-00.txt to the ones in
+ * draft-brezak-win2k-krb-rc4-hmac-04.txt
+ */
+
+static krb5_error_code
+usage2arcfour (krb5_context context, unsigned *usage)
+{
+ switch (*usage) {
+ case KRB5_KU_AS_REP_ENC_PART : /* 3 */
+ case KRB5_KU_TGS_REP_ENC_PART_SUB_KEY : /* 9 */
+ *usage = 8;
+ return 0;
+ case KRB5_KU_USAGE_SEAL : /* 22 */
+ *usage = 13;
+ return 0;
+ case KRB5_KU_USAGE_SIGN : /* 23 */
+ *usage = 15;
+ return 0;
+ case KRB5_KU_USAGE_SEQ: /* 24 */
+ *usage = 0;
+ return 0;
+ default :
+ return 0;
+ }
+}
+
+static krb5_error_code
+ARCFOUR_encrypt(krb5_context context,
+ struct key_data *key,
+ void *data,
+ size_t len,
+ krb5_boolean encryptp,
+ int usage,
+ void *ivec)
+{
+ krb5_error_code ret;
+ unsigned keyusage = usage;
+
+ if((ret = usage2arcfour (context, &keyusage)) != 0)
+ return ret;
+
+ if (encryptp)
+ return ARCFOUR_subencrypt (context, key, data, len, keyusage, ivec);
+ else
+ return ARCFOUR_subdecrypt (context, key, data, len, keyusage, ivec);
+}
+
+
+/*
+ *
+ */
+
+static krb5_error_code
+AES_PRF(krb5_context context,
+ krb5_crypto crypto,
+ const krb5_data *in,
+ krb5_data *out)
+{
+ struct checksum_type *ct = crypto->et->checksum;
+ krb5_error_code ret;
+ Checksum result;
+ krb5_keyblock *derived;
+
+ result.cksumtype = ct->type;
+ ret = krb5_data_alloc(&result.checksum, ct->checksumsize);
+ if (ret) {
+ krb5_set_error_message(context, ret, N_("malloc: out memory", ""));
+ return ret;
+ }
+
+ ret = (*ct->checksum)(context, NULL, in->data, in->length, 0, &result);
+ if (ret) {
+ krb5_data_free(&result.checksum);
+ return ret;
+ }
+
+ if (result.checksum.length < crypto->et->blocksize)
+ krb5_abortx(context, "internal prf error");
+
+ derived = NULL;
+ ret = krb5_derive_key(context, crypto->key.key,
+ crypto->et->type, "prf", 3, &derived);
+ if (ret)
+ krb5_abortx(context, "krb5_derive_key");
+
+ ret = krb5_data_alloc(out, crypto->et->blocksize);
+ if (ret)
+ krb5_abortx(context, "malloc failed");
+
+ {
+ const EVP_CIPHER *c = (*crypto->et->keytype->evp)();
+ EVP_CIPHER_CTX ctx;
+ /* XXX blksz 1 for cts, so we can't use that */
+ EVP_CIPHER_CTX_init(&ctx); /* ivec all zero */
+ EVP_CipherInit_ex(&ctx, c, NULL, derived->keyvalue.data, NULL, 1);
+ EVP_Cipher(&ctx, out->data, result.checksum.data, 16);
+ EVP_CIPHER_CTX_cleanup(&ctx);
+ }
+
+ krb5_data_free(&result.checksum);
+ krb5_free_keyblock(context, derived);
+
+ return ret;
+}
+
+/*
+ * these should currently be in reverse preference order.
+ * (only relevant for !F_PSEUDO) */
+
+static struct encryption_type enctype_null = {
+ ETYPE_NULL,
+ "null",
+ 1,
+ 1,
+ 0,
+ &keytype_null,
+ &checksum_none,
+ NULL,
+ F_DISABLED,
+ NULL_encrypt,
+ 0,
+ NULL
+};
+static struct encryption_type enctype_arcfour_hmac_md5 = {
+ ETYPE_ARCFOUR_HMAC_MD5,
+ "arcfour-hmac-md5",
+ 1,
+ 1,
+ 8,
+ &keytype_arcfour,
+ &checksum_hmac_md5,
+ NULL,
+ F_SPECIAL,
+ ARCFOUR_encrypt,
+ 0,
+ NULL
+};
+#ifdef DES3_OLD_ENCTYPE
+static struct encryption_type enctype_des3_cbc_md5 = {
+ ETYPE_DES3_CBC_MD5,
+ "des3-cbc-md5",
+ 8,
+ 8,
+ 8,
+ &keytype_des3,
+ &checksum_rsa_md5,
+ &checksum_rsa_md5_des3,
+ 0,
+ evp_encrypt,
+ 0,
+ NULL
+};
+#endif
+static struct encryption_type enctype_des3_cbc_sha1 = {
+ ETYPE_DES3_CBC_SHA1,
+ "des3-cbc-sha1",
+ 8,
+ 8,
+ 8,
+ &keytype_des3_derived,
+ &checksum_sha1,
+ &checksum_hmac_sha1_des3,
+ F_DERIVED,
+ evp_encrypt,
+ 0,
+ NULL
+};
+#ifdef DES3_OLD_ENCTYPE
+static struct encryption_type enctype_old_des3_cbc_sha1 = {
+ ETYPE_OLD_DES3_CBC_SHA1,
+ "old-des3-cbc-sha1",
+ 8,
+ 8,
+ 8,
+ &keytype_des3,
+ &checksum_sha1,
+ &checksum_hmac_sha1_des3,
+ 0,
+ evp_encrypt,
+ 0,
+ NULL
+};
+#endif
+static struct encryption_type enctype_aes128_cts_hmac_sha1 = {
+ ETYPE_AES128_CTS_HMAC_SHA1_96,
+ "aes128-cts-hmac-sha1-96",
+ 16,
+ 1,
+ 16,
+ &keytype_aes128,
+ &checksum_sha1,
+ &checksum_hmac_sha1_aes128,
+ F_DERIVED,
+ evp_encrypt,
+ 16,
+ AES_PRF
+};
+static struct encryption_type enctype_aes256_cts_hmac_sha1 = {
+ ETYPE_AES256_CTS_HMAC_SHA1_96,
+ "aes256-cts-hmac-sha1-96",
+ 16,
+ 1,
+ 16,
+ &keytype_aes256,
+ &checksum_sha1,
+ &checksum_hmac_sha1_aes256,
+ F_DERIVED,
+ evp_encrypt,
+ 16,
+ AES_PRF
+};
+static struct encryption_type enctype_des3_cbc_none = {
+ ETYPE_DES3_CBC_NONE,
+ "des3-cbc-none",
+ 8,
+ 8,
+ 0,
+ &keytype_des3_derived,
+ &checksum_none,
+ NULL,
+ F_PSEUDO,
+ evp_encrypt,
+ 0,
+ NULL
+};
+#ifdef WEAK_ENCTYPES
+static struct encryption_type enctype_des_cbc_crc = {
+ ETYPE_DES_CBC_CRC,
+ "des-cbc-crc",
+ 8,
+ 8,
+ 8,
+ &keytype_des,
+ &checksum_crc32,
+ NULL,
+ F_DISABLED,
+ evp_des_encrypt_key_ivec,
+ 0,
+ NULL
+};
+static struct encryption_type enctype_des_cbc_md4 = {
+ ETYPE_DES_CBC_MD4,
+ "des-cbc-md4",
+ 8,
+ 8,
+ 8,
+ &keytype_des,
+ &checksum_rsa_md4,
+ &checksum_rsa_md4_des,
+ F_DISABLED,
+ evp_des_encrypt_null_ivec,
+ 0,
+ NULL
+};
+static struct encryption_type enctype_des_cbc_md5 = {
+ ETYPE_DES_CBC_MD5,
+ "des-cbc-md5",
+ 8,
+ 8,
+ 8,
+ &keytype_des,
+ &checksum_rsa_md5,
+ &checksum_rsa_md5_des,
+ F_DISABLED,
+ evp_des_encrypt_null_ivec,
+ 0,
+ NULL
+};
+static struct encryption_type enctype_des_cbc_none = {
+ ETYPE_DES_CBC_NONE,
+ "des-cbc-none",
+ 8,
+ 8,
+ 0,
+ &keytype_des,
+ &checksum_none,
+ NULL,
+ F_PSEUDO|F_DISABLED,
+ evp_des_encrypt_null_ivec,
+ 0,
+ NULL
+};
+static struct encryption_type enctype_des_cfb64_none = {
+ ETYPE_DES_CFB64_NONE,
+ "des-cfb64-none",
+ 1,
+ 1,
+ 0,
+ &keytype_des_old,
+ &checksum_none,
+ NULL,
+ F_PSEUDO|F_DISABLED,
+ DES_CFB64_encrypt_null_ivec,
+ 0,
+ NULL
+};
+static struct encryption_type enctype_des_pcbc_none = {
+ ETYPE_DES_PCBC_NONE,
+ "des-pcbc-none",
+ 8,
+ 8,
+ 0,
+ &keytype_des_old,
+ &checksum_none,
+ NULL,
+ F_PSEUDO|F_DISABLED,
+ DES_PCBC_encrypt_key_ivec,
+ 0,
+ NULL
+};
+#endif /* WEAK_ENCTYPES */
+
+static struct encryption_type *etypes[] = {
+ &enctype_aes256_cts_hmac_sha1,
+ &enctype_aes128_cts_hmac_sha1,
+ &enctype_des3_cbc_sha1,
+ &enctype_des3_cbc_none, /* used by the gss-api mech */
+ &enctype_arcfour_hmac_md5,
+#ifdef DES3_OLD_ENCTYPE
+ &enctype_des3_cbc_md5,
+ &enctype_old_des3_cbc_sha1,
+#endif
+#ifdef WEAK_ENCTYPES
+ &enctype_des_cbc_crc,
+ &enctype_des_cbc_md4,
+ &enctype_des_cbc_md5,
+ &enctype_des_cbc_none,
+ &enctype_des_cfb64_none,
+ &enctype_des_pcbc_none,
+#endif
+ &enctype_null
+};
+
+static unsigned num_etypes = sizeof(etypes) / sizeof(etypes[0]);
+
+
+static struct encryption_type *
+_find_enctype(krb5_enctype type)
+{
+ int i;
+ for(i = 0; i < num_etypes; i++)
+ if(etypes[i]->type == type)
+ return etypes[i];
+ return NULL;
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_enctype_to_string(krb5_context context,
+ krb5_enctype etype,
+ char **string)
+{
+ struct encryption_type *e;
+ e = _find_enctype(etype);
+ if(e == NULL) {
+ krb5_set_error_message (context, KRB5_PROG_ETYPE_NOSUPP,
+ N_("encryption type %d not supported", ""),
+ etype);
+ *string = NULL;
+ return KRB5_PROG_ETYPE_NOSUPP;
+ }
+ *string = strdup(e->name);
+ if(*string == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_string_to_enctype(krb5_context context,
+ const char *string,
+ krb5_enctype *etype)
+{
+ int i;
+ for(i = 0; i < num_etypes; i++)
+ if(strcasecmp(etypes[i]->name, string) == 0){
+ *etype = etypes[i]->type;
+ return 0;
+ }
+ krb5_set_error_message (context, KRB5_PROG_ETYPE_NOSUPP,
+ N_("encryption type %s not supported", ""),
+ string);
+ return KRB5_PROG_ETYPE_NOSUPP;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_enctype_to_keytype(krb5_context context,
+ krb5_enctype etype,
+ krb5_keytype *keytype)
+{
+ struct encryption_type *e = _find_enctype(etype);
+ if(e == NULL) {
+ krb5_set_error_message (context, KRB5_PROG_ETYPE_NOSUPP,
+ N_("encryption type %d not supported", ""),
+ etype);
+ return KRB5_PROG_ETYPE_NOSUPP;
+ }
+ *keytype = e->keytype->type; /* XXX */
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_enctype_valid(krb5_context context,
+ krb5_enctype etype)
+{
+ struct encryption_type *e = _find_enctype(etype);
+ if(e == NULL) {
+ krb5_set_error_message (context, KRB5_PROG_ETYPE_NOSUPP,
+ N_("encryption type %d not supported", ""),
+ etype);
+ return KRB5_PROG_ETYPE_NOSUPP;
+ }
+ if (e->flags & F_DISABLED) {
+ krb5_set_error_message (context, KRB5_PROG_ETYPE_NOSUPP,
+ N_("encryption type %s is disabled", ""),
+ e->name);
+ return KRB5_PROG_ETYPE_NOSUPP;
+ }
+ return 0;
+}
+
+/**
+ * Return the coresponding encryption type for a checksum type.
+ *
+ * @param context Kerberos context
+ * @param ctype The checksum type to get the result enctype for
+ * @param etype The returned encryption, when the matching etype is
+ * not found, etype is set to ETYPE_NULL.
+ *
+ * @return Return an error code for an failure or 0 on success.
+ * @ingroup krb5_crypto
+ */
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cksumtype_to_enctype(krb5_context context,
+ krb5_cksumtype ctype,
+ krb5_enctype *etype)
+{
+ int i;
+
+ *etype = ETYPE_NULL;
+
+ for(i = 0; i < num_etypes; i++) {
+ if(etypes[i]->keyed_checksum &&
+ etypes[i]->keyed_checksum->type == ctype)
+ {
+ *etype = etypes[i]->type;
+ return 0;
+ }
+ }
+
+ krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP,
+ N_("checksum type %d not supported", ""),
+ (int)ctype);
+ return KRB5_PROG_SUMTYPE_NOSUPP;
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_cksumtype_valid(krb5_context context,
+ krb5_cksumtype ctype)
+{
+ struct checksum_type *c = _find_checksum(ctype);
+ if (c == NULL) {
+ krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP,
+ N_("checksum type %d not supported", ""),
+ ctype);
+ return KRB5_PROG_SUMTYPE_NOSUPP;
+ }
+ if (c->flags & F_DISABLED) {
+ krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP,
+ N_("checksum type %s is disabled", ""),
+ c->name);
+ return KRB5_PROG_SUMTYPE_NOSUPP;
+ }
+ return 0;
+}
+
+
+/* if two enctypes have compatible keys */
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_enctypes_compatible_keys(krb5_context context,
+ krb5_enctype etype1,
+ krb5_enctype etype2)
+{
+ struct encryption_type *e1 = _find_enctype(etype1);
+ struct encryption_type *e2 = _find_enctype(etype2);
+ return e1 != NULL && e2 != NULL && e1->keytype == e2->keytype;
+}
+
+static krb5_boolean
+derived_crypto(krb5_context context,
+ krb5_crypto crypto)
+{
+ return (crypto->et->flags & F_DERIVED) != 0;
+}
+
+static krb5_boolean
+special_crypto(krb5_context context,
+ krb5_crypto crypto)
+{
+ return (crypto->et->flags & F_SPECIAL) != 0;
+}
+
+#define CHECKSUMSIZE(C) ((C)->checksumsize)
+#define CHECKSUMTYPE(C) ((C)->type)
+
+static krb5_error_code
+encrypt_internal_derived(krb5_context context,
+ krb5_crypto crypto,
+ unsigned usage,
+ const void *data,
+ size_t len,
+ krb5_data *result,
+ void *ivec)
+{
+ size_t sz, block_sz, checksum_sz, total_sz;
+ Checksum cksum;
+ unsigned char *p, *q;
+ krb5_error_code ret;
+ struct key_data *dkey;
+ const struct encryption_type *et = crypto->et;
+
+ checksum_sz = CHECKSUMSIZE(et->keyed_checksum);
+
+ sz = et->confoundersize + len;
+ block_sz = (sz + et->padsize - 1) &~ (et->padsize - 1); /* pad */
+ total_sz = block_sz + checksum_sz;
+ p = calloc(1, total_sz);
+ if(p == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+
+ q = p;
+ krb5_generate_random_block(q, et->confoundersize); /* XXX */
+ q += et->confoundersize;
+ memcpy(q, data, len);
+
+ ret = create_checksum(context,
+ et->keyed_checksum,
+ crypto,
+ INTEGRITY_USAGE(usage),
+ p,
+ block_sz,
+ &cksum);
+ if(ret == 0 && cksum.checksum.length != checksum_sz) {
+ free_Checksum (&cksum);
+ krb5_clear_error_message (context);
+ ret = KRB5_CRYPTO_INTERNAL;
+ }
+ if(ret)
+ goto fail;
+ memcpy(p + block_sz, cksum.checksum.data, cksum.checksum.length);
+ free_Checksum (&cksum);
+ ret = _get_derived_key(context, crypto, ENCRYPTION_USAGE(usage), &dkey);
+ if(ret)
+ goto fail;
+ ret = _key_schedule(context, dkey);
+ if(ret)
+ goto fail;
+ ret = (*et->encrypt)(context, dkey, p, block_sz, 1, usage, ivec);
+ if (ret)
+ goto fail;
+ result->data = p;
+ result->length = total_sz;
+ return 0;
+ fail:
+ memset(p, 0, total_sz);
+ free(p);
+ return ret;
+}
+
+
+static krb5_error_code
+encrypt_internal(krb5_context context,
+ krb5_crypto crypto,
+ const void *data,
+ size_t len,
+ krb5_data *result,
+ void *ivec)
+{
+ size_t sz, block_sz, checksum_sz;
+ Checksum cksum;
+ unsigned char *p, *q;
+ krb5_error_code ret;
+ const struct encryption_type *et = crypto->et;
+
+ checksum_sz = CHECKSUMSIZE(et->checksum);
+
+ sz = et->confoundersize + checksum_sz + len;
+ block_sz = (sz + et->padsize - 1) &~ (et->padsize - 1); /* pad */
+ p = calloc(1, block_sz);
+ if(p == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+
+ q = p;
+ krb5_generate_random_block(q, et->confoundersize); /* XXX */
+ q += et->confoundersize;
+ memset(q, 0, checksum_sz);
+ q += checksum_sz;
+ memcpy(q, data, len);
+
+ ret = create_checksum(context,
+ et->checksum,
+ crypto,
+ 0,
+ p,
+ block_sz,
+ &cksum);
+ if(ret == 0 && cksum.checksum.length != checksum_sz) {
+ krb5_clear_error_message (context);
+ free_Checksum(&cksum);
+ ret = KRB5_CRYPTO_INTERNAL;
+ }
+ if(ret)
+ goto fail;
+ memcpy(p + et->confoundersize, cksum.checksum.data, cksum.checksum.length);
+ free_Checksum(&cksum);
+ ret = _key_schedule(context, &crypto->key);
+ if(ret)
+ goto fail;
+ ret = (*et->encrypt)(context, &crypto->key, p, block_sz, 1, 0, ivec);
+ if (ret) {
+ memset(p, 0, block_sz);
+ free(p);
+ return ret;
+ }
+ result->data = p;
+ result->length = block_sz;
+ return 0;
+ fail:
+ memset(p, 0, block_sz);
+ free(p);
+ return ret;
+}
+
+static krb5_error_code
+encrypt_internal_special(krb5_context context,
+ krb5_crypto crypto,
+ int usage,
+ const void *data,
+ size_t len,
+ krb5_data *result,
+ void *ivec)
+{
+ struct encryption_type *et = crypto->et;
+ size_t cksum_sz = CHECKSUMSIZE(et->checksum);
+ size_t sz = len + cksum_sz + et->confoundersize;
+ char *tmp, *p;
+ krb5_error_code ret;
+
+ tmp = malloc (sz);
+ if (tmp == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ p = tmp;
+ memset (p, 0, cksum_sz);
+ p += cksum_sz;
+ krb5_generate_random_block(p, et->confoundersize);
+ p += et->confoundersize;
+ memcpy (p, data, len);
+ ret = (*et->encrypt)(context, &crypto->key, tmp, sz, TRUE, usage, ivec);
+ if (ret) {
+ memset(tmp, 0, sz);
+ free(tmp);
+ return ret;
+ }
+ result->data = tmp;
+ result->length = sz;
+ return 0;
+}
+
+static krb5_error_code
+decrypt_internal_derived(krb5_context context,
+ krb5_crypto crypto,
+ unsigned usage,
+ void *data,
+ size_t len,
+ krb5_data *result,
+ void *ivec)
+{
+ size_t checksum_sz;
+ Checksum cksum;
+ unsigned char *p;
+ krb5_error_code ret;
+ struct key_data *dkey;
+ struct encryption_type *et = crypto->et;
+ unsigned long l;
+
+ checksum_sz = CHECKSUMSIZE(et->keyed_checksum);
+ if (len < checksum_sz + et->confoundersize) {
+ krb5_set_error_message(context, KRB5_BAD_MSIZE,
+ N_("Encrypted data shorter then "
+ "checksum + confunder", ""));
+ return KRB5_BAD_MSIZE;
+ }
+
+ if (((len - checksum_sz) % et->padsize) != 0) {
+ krb5_clear_error_message(context);
+ return KRB5_BAD_MSIZE;
+ }
+
+ p = malloc(len);
+ if(len != 0 && p == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ memcpy(p, data, len);
+
+ len -= checksum_sz;
+
+ ret = _get_derived_key(context, crypto, ENCRYPTION_USAGE(usage), &dkey);
+ if(ret) {
+ free(p);
+ return ret;
+ }
+ ret = _key_schedule(context, dkey);
+ if(ret) {
+ free(p);
+ return ret;
+ }
+ ret = (*et->encrypt)(context, dkey, p, len, 0, usage, ivec);
+ if (ret) {
+ free(p);
+ return ret;
+ }
+
+ cksum.checksum.data = p + len;
+ cksum.checksum.length = checksum_sz;
+ cksum.cksumtype = CHECKSUMTYPE(et->keyed_checksum);
+
+ ret = verify_checksum(context,
+ crypto,
+ INTEGRITY_USAGE(usage),
+ p,
+ len,
+ &cksum);
+ if(ret) {
+ free(p);
+ return ret;
+ }
+ l = len - et->confoundersize;
+ memmove(p, p + et->confoundersize, l);
+ result->data = realloc(p, l);
+ if(result->data == NULL && l != 0) {
+ free(p);
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ result->length = l;
+ return 0;
+}
+
+static krb5_error_code
+decrypt_internal(krb5_context context,
+ krb5_crypto crypto,
+ void *data,
+ size_t len,
+ krb5_data *result,
+ void *ivec)
+{
+ krb5_error_code ret;
+ unsigned char *p;
+ Checksum cksum;
+ size_t checksum_sz, l;
+ struct encryption_type *et = crypto->et;
+
+ if ((len % et->padsize) != 0) {
+ krb5_clear_error_message(context);
+ return KRB5_BAD_MSIZE;
+ }
+
+ checksum_sz = CHECKSUMSIZE(et->checksum);
+ p = malloc(len);
+ if(len != 0 && p == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ memcpy(p, data, len);
+
+ ret = _key_schedule(context, &crypto->key);
+ if(ret) {
+ free(p);
+ return ret;
+ }
+ ret = (*et->encrypt)(context, &crypto->key, p, len, 0, 0, ivec);
+ if (ret) {
+ free(p);
+ return ret;
+ }
+ ret = krb5_data_copy(&cksum.checksum, p + et->confoundersize, checksum_sz);
+ if(ret) {
+ free(p);
+ return ret;
+ }
+ memset(p + et->confoundersize, 0, checksum_sz);
+ cksum.cksumtype = CHECKSUMTYPE(et->checksum);
+ ret = verify_checksum(context, NULL, 0, p, len, &cksum);
+ free_Checksum(&cksum);
+ if(ret) {
+ free(p);
+ return ret;
+ }
+ l = len - et->confoundersize - checksum_sz;
+ memmove(p, p + et->confoundersize + checksum_sz, l);
+ result->data = realloc(p, l);
+ if(result->data == NULL && l != 0) {
+ free(p);
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ result->length = l;
+ return 0;
+}
+
+static krb5_error_code
+decrypt_internal_special(krb5_context context,
+ krb5_crypto crypto,
+ int usage,
+ void *data,
+ size_t len,
+ krb5_data *result,
+ void *ivec)
+{
+ struct encryption_type *et = crypto->et;
+ size_t cksum_sz = CHECKSUMSIZE(et->checksum);
+ size_t sz = len - cksum_sz - et->confoundersize;
+ unsigned char *p;
+ krb5_error_code ret;
+
+ if ((len % et->padsize) != 0) {
+ krb5_clear_error_message(context);
+ return KRB5_BAD_MSIZE;
+ }
+
+ p = malloc (len);
+ if (p == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ memcpy(p, data, len);
+
+ ret = (*et->encrypt)(context, &crypto->key, p, len, FALSE, usage, ivec);
+ if (ret) {
+ free(p);
+ return ret;
+ }
+
+ memmove (p, p + cksum_sz + et->confoundersize, sz);
+ result->data = realloc(p, sz);
+ if(result->data == NULL && sz != 0) {
+ free(p);
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ result->length = sz;
+ return 0;
+}
+
+static krb5_crypto_iov *
+find_iv(krb5_crypto_iov *data, int num_data, int type)
+{
+ int i;
+ for (i = 0; i < num_data; i++)
+ if (data[i].flags == type)
+ return &data[i];
+ return NULL;
+}
+
+/**
+ * Inline encrypt a kerberos message
+ *
+ * @param context Kerberos context
+ * @param crypto Kerberos crypto context
+ * @param usage Key usage for this buffer
+ * @param data array of buffers to process
+ * @param num_data length of array
+ * @param ivec initial cbc/cts vector
+ *
+ * @return Return an error code or 0.
+ * @ingroup krb5_crypto
+ *
+ * Kerberos encrypted data look like this:
+ *
+ * 1. KRB5_CRYPTO_TYPE_HEADER
+ * 2. array KRB5_CRYPTO_TYPE_DATA and KRB5_CRYPTO_TYPE_SIGN_ONLY in
+ * any order, however the receiver have to aware of the
+ * order. KRB5_CRYPTO_TYPE_SIGN_ONLY is commonly used headers and
+ * trailers.
+ * 3. KRB5_CRYPTO_TYPE_PADDING, at least on padsize long if padsize > 1
+ * 4. KRB5_CRYPTO_TYPE_TRAILER
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_encrypt_iov_ivec(krb5_context context,
+ krb5_crypto crypto,
+ unsigned usage,
+ krb5_crypto_iov *data,
+ size_t num_data,
+ void *ivec)
+{
+ size_t headersz, trailersz, len;
+ size_t i, sz, block_sz, pad_sz;
+ Checksum cksum;
+ unsigned char *p, *q;
+ krb5_error_code ret;
+ struct key_data *dkey;
+ const struct encryption_type *et = crypto->et;
+ krb5_crypto_iov *tiv, *piv, *hiv;
+
+ if(!derived_crypto(context, crypto)) {
+ krb5_clear_error_message(context);
+ return KRB5_CRYPTO_INTERNAL;
+ }
+
+ headersz = et->confoundersize;
+ trailersz = CHECKSUMSIZE(et->keyed_checksum);
+
+ for (len = 0, i = 0; i < num_data; i++) {
+ if (data[i].flags != KRB5_CRYPTO_TYPE_HEADER &&
+ data[i].flags == KRB5_CRYPTO_TYPE_DATA) {
+ len += data[i].data.length;
+ }
+ }
+
+ sz = headersz + len;
+ block_sz = (sz + et->padsize - 1) &~ (et->padsize - 1); /* pad */
+
+ pad_sz = block_sz - sz;
+ trailersz += pad_sz;
+
+ /* header */
+
+ hiv = find_iv(data, num_data, KRB5_CRYPTO_TYPE_HEADER);
+ if (hiv == NULL || hiv->data.length != headersz)
+ return KRB5_BAD_MSIZE;
+
+ krb5_generate_random_block(hiv->data.data, hiv->data.length);
+
+ /* padding */
+
+ piv = find_iv(data, num_data, KRB5_CRYPTO_TYPE_PADDING);
+ /* its ok to have no TYPE_PADDING if there is no padding */
+ if (piv == NULL && pad_sz != 0)
+ return KRB5_BAD_MSIZE;
+ if (piv) {
+ if (piv->data.length < pad_sz)
+ return KRB5_BAD_MSIZE;
+ piv->data.length = pad_sz;
+ }
+
+
+ /* trailer */
+
+ tiv = find_iv(data, num_data, KRB5_CRYPTO_TYPE_TRAILER);
+ if (tiv == NULL || tiv->data.length != trailersz)
+ return KRB5_BAD_MSIZE;
+
+
+ /*
+ * XXX replace with EVP_Sign? at least make create_checksum an iov
+ * function.
+ * XXX CTS EVP is broken, can't handle multi buffers :(
+ */
+
+ len = hiv->data.length;
+ for (i = 0; i < num_data; i++) {
+ if (data[i].flags != KRB5_CRYPTO_TYPE_DATA &&
+ data[i].flags != KRB5_CRYPTO_TYPE_SIGN_ONLY)
+ continue;
+ len += data[i].data.length;
+ }
+
+ p = q = malloc(len);
+
+ memcpy(q, hiv->data.data, hiv->data.length);
+ q += hiv->data.length;
+ for (i = 0; i < num_data; i++) {
+ if (data[i].flags != KRB5_CRYPTO_TYPE_DATA &&
+ data[i].flags != KRB5_CRYPTO_TYPE_SIGN_ONLY)
+ continue;
+ memcpy(q, data[i].data.data, data[i].data.length);
+ q += data[i].data.length;
+ }
+
+ ret = create_checksum(context,
+ et->keyed_checksum,
+ crypto,
+ INTEGRITY_USAGE(usage),
+ p,
+ len,
+ &cksum);
+ free(p);
+ if(ret == 0 && cksum.checksum.length != trailersz) {
+ free_Checksum (&cksum);
+ krb5_clear_error_message (context);
+ ret = KRB5_CRYPTO_INTERNAL;
+ }
+ if(ret)
+ return ret;
+
+ /* save cksum at end */
+ memcpy(tiv->data.data, cksum.checksum.data, cksum.checksum.length);
+ free_Checksum (&cksum);
+
+ /* now encrypt data */
+
+ ret = _get_derived_key(context, crypto, ENCRYPTION_USAGE(usage), &dkey);
+ if(ret)
+ return ret;
+ ret = _key_schedule(context, dkey);
+ if(ret)
+ return ret;
+
+ /* XXX replace with EVP_Cipher */
+
+ len = hiv->data.length;
+ for (i = 0; i < num_data; i++) {
+ if (data[i].flags != KRB5_CRYPTO_TYPE_DATA &&
+ data[i].flags != KRB5_CRYPTO_TYPE_PADDING)
+ continue;
+ len += data[i].data.length;
+ }
+
+ p = q = malloc(len);
+ if(p == NULL)
+ return ENOMEM;
+
+ memcpy(q, hiv->data.data, hiv->data.length);
+ q += hiv->data.length;
+ for (i = 0; i < num_data; i++) {
+ if (data[i].flags != KRB5_CRYPTO_TYPE_DATA &&
+ data[i].flags != KRB5_CRYPTO_TYPE_PADDING)
+ continue;
+ memcpy(q, data[i].data.data, data[i].data.length);
+ q += data[i].data.length;
+ }
+
+ ret = _get_derived_key(context, crypto, ENCRYPTION_USAGE(usage), &dkey);
+ if(ret) {
+ free(p);
+ return ret;
+ }
+ ret = _key_schedule(context, dkey);
+ if(ret) {
+ free(p);
+ return ret;
+ }
+
+ ret = (*et->encrypt)(context, dkey, p, len, 1, usage, ivec);
+ if (ret) {
+ free(p);
+ return ret;
+ }
+
+ /* now copy data back to buffers */
+ q = p;
+ memcpy(hiv->data.data, q, hiv->data.length);
+ q += hiv->data.length;
+
+ for (i = 0; i < num_data; i++) {
+ if (data[i].flags != KRB5_CRYPTO_TYPE_DATA &&
+ data[i].flags != KRB5_CRYPTO_TYPE_PADDING)
+ continue;
+ memcpy(data[i].data.data, q, data[i].data.length);
+ q += data[i].data.length;
+ }
+ free(p);
+
+ return ret;
+}
+
+/**
+ * Inline decrypt a Kerberos message.
+ *
+ * @param context Kerberos context
+ * @param crypto Kerberos crypto context
+ * @param usage Key usage for this buffer
+ * @param data array of buffers to process
+ * @param num_data length of array
+ * @param ivec initial cbc/cts vector
+ *
+ * @return Return an error code or 0.
+ * @ingroup krb5_crypto
+ *
+ * 1. KRB5_CRYPTO_TYPE_HEADER
+ * 2. array KRB5_CRYPTO_TYPE_DATA and KRB5_CRYPTO_TYPE_SIGN_ONLY in
+ * any order, however the receiver have to aware of the
+ * order. KRB5_CRYPTO_TYPE_SIGN_ONLY is commonly used unencrypoted
+ * protocol headers and trailers. The output data will be of same
+ * size as the input data or shorter.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_decrypt_iov_ivec(krb5_context context,
+ krb5_crypto crypto,
+ unsigned usage,
+ krb5_crypto_iov *data,
+ size_t num_data,
+ void *ivec)
+{
+ size_t headersz, trailersz, len;
+ size_t i, sz, block_sz, pad_sz;
+ Checksum cksum;
+ unsigned char *p, *q;
+ krb5_error_code ret;
+ struct key_data *dkey;
+ struct encryption_type *et = crypto->et;
+ krb5_crypto_iov *tiv, *hiv;
+
+ if(!derived_crypto(context, crypto)) {
+ krb5_clear_error_message(context);
+ return KRB5_CRYPTO_INTERNAL;
+ }
+
+ headersz = et->confoundersize;
+ trailersz = CHECKSUMSIZE(et->keyed_checksum);
+
+ for (len = 0, i = 0; i < num_data; i++)
+ if (data[i].flags == KRB5_CRYPTO_TYPE_DATA)
+ len += data[i].data.length;
+
+ sz = headersz + len;
+ block_sz = (sz + et->padsize - 1) &~ (et->padsize - 1); /* pad */
+
+ pad_sz = block_sz - sz;
+ trailersz += pad_sz;
+
+ /* header */
+
+ hiv = find_iv(data, num_data, KRB5_CRYPTO_TYPE_HEADER);
+ if (hiv == NULL || hiv->data.length < headersz)
+ return KRB5_BAD_MSIZE;
+ hiv->data.length = headersz;
+
+ /* trailer */
+
+ tiv = find_iv(data, num_data, KRB5_CRYPTO_TYPE_TRAILER);
+ if (tiv == NULL || tiv->data.length < trailersz)
+ return KRB5_BAD_MSIZE;
+ tiv->data.length = trailersz;
+
+ /* body */
+
+ /* XXX replace with EVP_Cipher */
+
+ for (len = 0, i = 0; i < num_data; i++) {
+ if (data[i].flags != KRB5_CRYPTO_TYPE_HEADER &&
+ data[i].flags != KRB5_CRYPTO_TYPE_DATA)
+ continue;
+ len += data[i].data.length;
+ }
+
+ p = q = malloc(len);
+ if (p == NULL)
+ return ENOMEM;
+
+ memcpy(q, hiv->data.data, hiv->data.length);
+ q += hiv->data.length;
+ for (i = 0; i < num_data; i++) {
+ if (data[i].flags != KRB5_CRYPTO_TYPE_DATA)
+ continue;
+ memcpy(q, data[i].data.data, data[i].data.length);
+ q += data[i].data.length;
+ }
+
+ ret = _get_derived_key(context, crypto, ENCRYPTION_USAGE(usage), &dkey);
+ if(ret) {
+ free(p);
+ return ret;
+ }
+ ret = _key_schedule(context, dkey);
+ if(ret) {
+ free(p);
+ return ret;
+ }
+
+ ret = (*et->encrypt)(context, dkey, p, len, 0, usage, ivec);
+ if (ret) {
+ free(p);
+ return ret;
+ }
+
+ /* XXX now copy data back to buffers */
+ q = p;
+ memcpy(hiv->data.data, q, hiv->data.length);
+ q += hiv->data.length;
+ len -= hiv->data.length;
+
+ for (i = 0; i < num_data; i++) {
+ if (data[i].flags != KRB5_CRYPTO_TYPE_DATA)
+ continue;
+ if (len < data[i].data.length)
+ data[i].data.length = len;
+ memcpy(data[i].data.data, q, data[i].data.length);
+ q += data[i].data.length;
+ len -= data[i].data.length;
+ }
+ free(p);
+ if (len)
+ krb5_abortx(context, "data still in the buffer");
+
+ len = hiv->data.length;
+ for (i = 0; i < num_data; i++) {
+ if (data[i].flags != KRB5_CRYPTO_TYPE_DATA &&
+ data[i].flags != KRB5_CRYPTO_TYPE_SIGN_ONLY)
+ continue;
+ len += data[i].data.length;
+ }
+
+ p = q = malloc(len);
+
+ memcpy(q, hiv->data.data, hiv->data.length);
+ q += hiv->data.length;
+ for (i = 0; i < num_data; i++) {
+ if (data[i].flags != KRB5_CRYPTO_TYPE_DATA &&
+ data[i].flags != KRB5_CRYPTO_TYPE_SIGN_ONLY)
+ continue;
+ memcpy(q, data[i].data.data, data[i].data.length);
+ q += data[i].data.length;
+ }
+
+ cksum.checksum.data = tiv->data.data;
+ cksum.checksum.length = tiv->data.length;
+ cksum.cksumtype = CHECKSUMTYPE(et->keyed_checksum);
+
+ ret = verify_checksum(context,
+ crypto,
+ INTEGRITY_USAGE(usage),
+ p,
+ len,
+ &cksum);
+ free(p);
+ return ret;
+}
+
+/**
+ * Create a Kerberos message checksum.
+ *
+ * @param context Kerberos context
+ * @param crypto Kerberos crypto context
+ * @param usage Key usage for this buffer
+ * @param data array of buffers to process
+ * @param num_data length of array
+ * @param result output data
+ *
+ * @return Return an error code or 0.
+ * @ingroup krb5_crypto
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_create_checksum_iov(krb5_context context,
+ krb5_crypto crypto,
+ unsigned usage,
+ krb5_crypto_iov *data,
+ size_t num_data,
+ krb5_cksumtype *type)
+{
+ Checksum cksum;
+ krb5_crypto_iov *civ;
+ krb5_error_code ret;
+ unsigned int i;
+ size_t len;
+ char *p, *q;
+
+ if(!derived_crypto(context, crypto)) {
+ krb5_clear_error_message(context);
+ return KRB5_CRYPTO_INTERNAL;
+ }
+
+ civ = find_iv(data, num_data, KRB5_CRYPTO_TYPE_CHECKSUM);
+ if (civ == NULL)
+ return KRB5_BAD_MSIZE;
+
+ len = 0;
+ for (i = 0; i < num_data; i++) {
+ if (data[i].flags != KRB5_CRYPTO_TYPE_DATA &&
+ data[i].flags != KRB5_CRYPTO_TYPE_SIGN_ONLY)
+ continue;
+ len += data[i].data.length;
+ }
+
+ p = q = malloc(len);
+
+ for (i = 0; i < num_data; i++) {
+ if (data[i].flags != KRB5_CRYPTO_TYPE_DATA &&
+ data[i].flags != KRB5_CRYPTO_TYPE_SIGN_ONLY)
+ continue;
+ memcpy(q, data[i].data.data, data[i].data.length);
+ q += data[i].data.length;
+ }
+
+ ret = krb5_create_checksum(context, crypto, usage, 0, p, len, &cksum);
+ free(p);
+ if (ret)
+ return ret;
+
+ if (type)
+ *type = cksum.cksumtype;
+
+ if (cksum.checksum.length > civ->data.length) {
+ krb5_set_error_message(context, KRB5_BAD_MSIZE,
+ N_("Checksum larger then input buffer", ""));
+ free_Checksum(&cksum);
+ return KRB5_BAD_MSIZE;
+ }
+
+ civ->data.length = cksum.checksum.length;
+ memcpy(civ->data.data, cksum.checksum.data, civ->data.length);
+ free_Checksum(&cksum);
+
+ return 0;
+}
+
+
+size_t KRB5_LIB_FUNCTION
+krb5_crypto_length(krb5_context context,
+ krb5_crypto crypto,
+ int type)
+{
+ if (!derived_crypto(context, crypto))
+ return (size_t)-1;
+ switch(type) {
+ case KRB5_CRYPTO_TYPE_EMPTY:
+ return 0;
+ case KRB5_CRYPTO_TYPE_HEADER:
+ return crypto->et->blocksize;
+ case KRB5_CRYPTO_TYPE_PADDING:
+ if (crypto->et->padsize > 1)
+ return crypto->et->padsize;
+ return 0;
+ case KRB5_CRYPTO_TYPE_TRAILER:
+ return CHECKSUMSIZE(crypto->et->keyed_checksum);
+ case KRB5_CRYPTO_TYPE_CHECKSUM:
+ if (crypto->et->keyed_checksum)
+ return CHECKSUMSIZE(crypto->et->keyed_checksum);
+ return CHECKSUMSIZE(crypto->et->checksum);
+ }
+ return (size_t)-1;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_encrypt_ivec(krb5_context context,
+ krb5_crypto crypto,
+ unsigned usage,
+ const void *data,
+ size_t len,
+ krb5_data *result,
+ void *ivec)
+{
+ if(derived_crypto(context, crypto))
+ return encrypt_internal_derived(context, crypto, usage,
+ data, len, result, ivec);
+ else if (special_crypto(context, crypto))
+ return encrypt_internal_special (context, crypto, usage,
+ data, len, result, ivec);
+ else
+ return encrypt_internal(context, crypto, data, len, result, ivec);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_encrypt(krb5_context context,
+ krb5_crypto crypto,
+ unsigned usage,
+ const void *data,
+ size_t len,
+ krb5_data *result)
+{
+ return krb5_encrypt_ivec(context, crypto, usage, data, len, result, NULL);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_encrypt_EncryptedData(krb5_context context,
+ krb5_crypto crypto,
+ unsigned usage,
+ void *data,
+ size_t len,
+ int kvno,
+ EncryptedData *result)
+{
+ result->etype = CRYPTO_ETYPE(crypto);
+ if(kvno){
+ ALLOC(result->kvno, 1);
+ *result->kvno = kvno;
+ }else
+ result->kvno = NULL;
+ return krb5_encrypt(context, crypto, usage, data, len, &result->cipher);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_decrypt_ivec(krb5_context context,
+ krb5_crypto crypto,
+ unsigned usage,
+ void *data,
+ size_t len,
+ krb5_data *result,
+ void *ivec)
+{
+ if(derived_crypto(context, crypto))
+ return decrypt_internal_derived(context, crypto, usage,
+ data, len, result, ivec);
+ else if (special_crypto (context, crypto))
+ return decrypt_internal_special(context, crypto, usage,
+ data, len, result, ivec);
+ else
+ return decrypt_internal(context, crypto, data, len, result, ivec);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_decrypt(krb5_context context,
+ krb5_crypto crypto,
+ unsigned usage,
+ void *data,
+ size_t len,
+ krb5_data *result)
+{
+ return krb5_decrypt_ivec (context, crypto, usage, data, len, result,
+ NULL);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_decrypt_EncryptedData(krb5_context context,
+ krb5_crypto crypto,
+ unsigned usage,
+ const EncryptedData *e,
+ krb5_data *result)
+{
+ return krb5_decrypt(context, crypto, usage,
+ e->cipher.data, e->cipher.length, result);
+}
+
+/************************************************************
+ * *
+ ************************************************************/
+
+#define ENTROPY_NEEDED 128
+
+static int
+seed_something(void)
+{
+ char buf[1024], seedfile[256];
+
+ /* If there is a seed file, load it. But such a file cannot be trusted,
+ so use 0 for the entropy estimate */
+ if (RAND_file_name(seedfile, sizeof(seedfile))) {
+ int fd;
+ fd = open(seedfile, O_RDONLY | O_BINARY | O_CLOEXEC);
+ if (fd >= 0) {
+ ssize_t ret;
+ rk_cloexec(fd);
+ ret = read(fd, buf, sizeof(buf));
+ if (ret > 0)
+ RAND_add(buf, ret, 0.0);
+ close(fd);
+ } else
+ seedfile[0] = '\0';
+ } else
+ seedfile[0] = '\0';
+
+ /* Calling RAND_status() will try to use /dev/urandom if it exists so
+ we do not have to deal with it. */
+ if (RAND_status() != 1) {
+ krb5_context context;
+ const char *p;
+
+ /* Try using egd */
+ if (!krb5_init_context(&context)) {
+ p = krb5_config_get_string(context, NULL, "libdefaults",
+ "egd_socket", NULL);
+ if (p != NULL)
+ RAND_egd_bytes(p, ENTROPY_NEEDED);
+ krb5_free_context(context);
+ }
+ }
+
+ if (RAND_status() == 1) {
+ /* Update the seed file */
+ if (seedfile[0])
+ RAND_write_file(seedfile);
+
+ return 0;
+ } else
+ return -1;
+}
+
+void KRB5_LIB_FUNCTION
+krb5_generate_random_block(void *buf, size_t len)
+{
+ static int rng_initialized = 0;
+
+ HEIMDAL_MUTEX_lock(&crypto_mutex);
+ if (!rng_initialized) {
+ if (seed_something())
+ krb5_abortx(NULL, "Fatal: could not seed the "
+ "random number generator");
+
+ rng_initialized = 1;
+ }
+ HEIMDAL_MUTEX_unlock(&crypto_mutex);
+ if (RAND_bytes(buf, len) != 1)
+ krb5_abortx(NULL, "Failed to generate random block");
+}
+
+static void
+DES3_postproc(krb5_context context,
+ unsigned char *k, size_t len, struct key_data *key)
+{
+ DES3_random_to_key(context, key->key, k, len);
+
+ if (key->schedule) {
+ krb5_free_data(context, key->schedule);
+ key->schedule = NULL;
+ }
+}
+
+static krb5_error_code
+derive_key(krb5_context context,
+ struct encryption_type *et,
+ struct key_data *key,
+ const void *constant,
+ size_t len)
+{
+ unsigned char *k;
+ unsigned int nblocks = 0, i;
+ krb5_error_code ret = 0;
+ struct key_type *kt = et->keytype;
+
+ ret = _key_schedule(context, key);
+ if(ret)
+ return ret;
+ if(et->blocksize * 8 < kt->bits || len != et->blocksize) {
+ nblocks = (kt->bits + et->blocksize * 8 - 1) / (et->blocksize * 8);
+ k = malloc(nblocks * et->blocksize);
+ if(k == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ ret = _krb5_n_fold(constant, len, k, et->blocksize);
+ if (ret) {
+ free(k);
+ krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ return ret;
+ }
+ for(i = 0; i < nblocks; i++) {
+ if(i > 0)
+ memcpy(k + i * et->blocksize,
+ k + (i - 1) * et->blocksize,
+ et->blocksize);
+ (*et->encrypt)(context, key, k + i * et->blocksize, et->blocksize,
+ 1, 0, NULL);
+ }
+ } else {
+ /* this case is probably broken, but won't be run anyway */
+ void *c = malloc(len);
+ size_t res_len = (kt->bits + 7) / 8;
+
+ if(len != 0 && c == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ memcpy(c, constant, len);
+ (*et->encrypt)(context, key, c, len, 1, 0, NULL);
+ k = malloc(res_len);
+ if(res_len != 0 && k == NULL) {
+ free(c);
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ ret = _krb5_n_fold(c, len, k, res_len);
+ if (ret) {
+ free(k);
+ krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ return ret;
+ }
+ free(c);
+ }
+
+ /* XXX keytype dependent post-processing */
+ switch(kt->type) {
+ case KEYTYPE_DES3:
+ DES3_postproc(context, k, nblocks * et->blocksize, key);
+ break;
+ case KEYTYPE_AES128:
+ case KEYTYPE_AES256:
+ memcpy(key->key->keyvalue.data, k, key->key->keyvalue.length);
+ break;
+ default:
+ ret = KRB5_CRYPTO_INTERNAL;
+ krb5_set_error_message(context, ret,
+ N_("derive_key() called with unknown keytype (%u)", ""),
+ kt->type);
+ break;
+ }
+ if (key->schedule) {
+ krb5_free_data(context, key->schedule);
+ key->schedule = NULL;
+ }
+ memset(k, 0, nblocks * et->blocksize);
+ free(k);
+ return ret;
+}
+
+static struct key_data *
+_new_derived_key(krb5_crypto crypto, unsigned usage)
+{
+ struct key_usage *d = crypto->key_usage;
+ d = realloc(d, (crypto->num_key_usage + 1) * sizeof(*d));
+ if(d == NULL)
+ return NULL;
+ crypto->key_usage = d;
+ d += crypto->num_key_usage++;
+ memset(d, 0, sizeof(*d));
+ d->usage = usage;
+ return &d->key;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_derive_key(krb5_context context,
+ const krb5_keyblock *key,
+ krb5_enctype etype,
+ const void *constant,
+ size_t constant_len,
+ krb5_keyblock **derived_key)
+{
+ krb5_error_code ret;
+ struct encryption_type *et;
+ struct key_data d;
+
+ *derived_key = NULL;
+
+ et = _find_enctype (etype);
+ if (et == NULL) {
+ krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
+ N_("encryption type %d not supported", ""),
+ etype);
+ return KRB5_PROG_ETYPE_NOSUPP;
+ }
+
+ ret = krb5_copy_keyblock(context, key, &d.key);
+ if (ret)
+ return ret;
+
+ d.schedule = NULL;
+ ret = derive_key(context, et, &d, constant, constant_len);
+ if (ret == 0)
+ ret = krb5_copy_keyblock(context, d.key, derived_key);
+ free_key_data(context, &d, et);
+ return ret;
+}
+
+static krb5_error_code
+_get_derived_key(krb5_context context,
+ krb5_crypto crypto,
+ unsigned usage,
+ struct key_data **key)
+{
+ int i;
+ struct key_data *d;
+ unsigned char constant[5];
+
+ for(i = 0; i < crypto->num_key_usage; i++)
+ if(crypto->key_usage[i].usage == usage) {
+ *key = &crypto->key_usage[i].key;
+ return 0;
+ }
+ d = _new_derived_key(crypto, usage);
+ if(d == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ krb5_copy_keyblock(context, crypto->key.key, &d->key);
+ _krb5_put_int(constant, usage, 5);
+ derive_key(context, crypto->et, d, constant, sizeof(constant));
+ *key = d;
+ return 0;
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_crypto_init(krb5_context context,
+ const krb5_keyblock *key,
+ krb5_enctype etype,
+ krb5_crypto *crypto)
+{
+ krb5_error_code ret;
+ ALLOC(*crypto, 1);
+ if(*crypto == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ if(etype == ETYPE_NULL)
+ etype = key->keytype;
+ (*crypto)->et = _find_enctype(etype);
+ if((*crypto)->et == NULL || ((*crypto)->et->flags & F_DISABLED)) {
+ free(*crypto);
+ *crypto = NULL;
+ krb5_set_error_message (context, KRB5_PROG_ETYPE_NOSUPP,
+ N_("encryption type %d not supported", ""),
+ etype);
+ return KRB5_PROG_ETYPE_NOSUPP;
+ }
+ if((*crypto)->et->keytype->size != key->keyvalue.length) {
+ free(*crypto);
+ *crypto = NULL;
+ krb5_set_error_message (context, KRB5_BAD_KEYSIZE,
+ "encryption key has bad length");
+ return KRB5_BAD_KEYSIZE;
+ }
+ ret = krb5_copy_keyblock(context, key, &(*crypto)->key.key);
+ if(ret) {
+ free(*crypto);
+ *crypto = NULL;
+ return ret;
+ }
+ (*crypto)->key.schedule = NULL;
+ (*crypto)->num_key_usage = 0;
+ (*crypto)->key_usage = NULL;
+ return 0;
+}
+
+static void
+free_key_data(krb5_context context, struct key_data *key,
+ struct encryption_type *et)
+{
+ krb5_free_keyblock(context, key->key);
+ if(key->schedule) {
+ if (et->keytype->cleanup)
+ (*et->keytype->cleanup)(context, key);
+ memset(key->schedule->data, 0, key->schedule->length);
+ krb5_free_data(context, key->schedule);
+ }
+}
+
+static void
+free_key_usage(krb5_context context, struct key_usage *ku,
+ struct encryption_type *et)
+{
+ free_key_data(context, &ku->key, et);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_crypto_destroy(krb5_context context,
+ krb5_crypto crypto)
+{
+ int i;
+
+ for(i = 0; i < crypto->num_key_usage; i++)
+ free_key_usage(context, &crypto->key_usage[i], crypto->et);
+ free(crypto->key_usage);
+ free_key_data(context, &crypto->key, crypto->et);
+ free (crypto);
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_crypto_getblocksize(krb5_context context,
+ krb5_crypto crypto,
+ size_t *blocksize)
+{
+ *blocksize = crypto->et->blocksize;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_crypto_getenctype(krb5_context context,
+ krb5_crypto crypto,
+ krb5_enctype *enctype)
+{
+ *enctype = crypto->et->type;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_crypto_getpadsize(krb5_context context,
+ krb5_crypto crypto,
+ size_t *padsize)
+{
+ *padsize = crypto->et->padsize;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_crypto_getconfoundersize(krb5_context context,
+ krb5_crypto crypto,
+ size_t *confoundersize)
+{
+ *confoundersize = crypto->et->confoundersize;
+ return 0;
+}
+
+
+/**
+ * Disable encryption type
+ *
+ * @param context Kerberos 5 context
+ * @param enctype encryption type to disable
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_crypto
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_enctype_disable(krb5_context context,
+ krb5_enctype enctype)
+{
+ struct encryption_type *et = _find_enctype(enctype);
+ if(et == NULL) {
+ if (context)
+ krb5_set_error_message (context, KRB5_PROG_ETYPE_NOSUPP,
+ N_("encryption type %d not supported", ""),
+ enctype);
+ return KRB5_PROG_ETYPE_NOSUPP;
+ }
+ et->flags |= F_DISABLED;
+ return 0;
+}
+
+/**
+ * Enable encryption type
+ *
+ * @param context Kerberos 5 context
+ * @param enctype encryption type to enable
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_crypto
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_enctype_enable(krb5_context context,
+ krb5_enctype enctype)
+{
+ struct encryption_type *et = _find_enctype(enctype);
+ if(et == NULL) {
+ if (context)
+ krb5_set_error_message (context, KRB5_PROG_ETYPE_NOSUPP,
+ N_("encryption type %d not supported", ""),
+ enctype);
+ return KRB5_PROG_ETYPE_NOSUPP;
+ }
+ et->flags &= ~F_DISABLED;
+ return 0;
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_string_to_key_derived(krb5_context context,
+ const void *str,
+ size_t len,
+ krb5_enctype etype,
+ krb5_keyblock *key)
+{
+ struct encryption_type *et = _find_enctype(etype);
+ krb5_error_code ret;
+ struct key_data kd;
+ size_t keylen;
+ u_char *tmp;
+
+ if(et == NULL) {
+ krb5_set_error_message (context, KRB5_PROG_ETYPE_NOSUPP,
+ N_("encryption type %d not supported", ""),
+ etype);
+ return KRB5_PROG_ETYPE_NOSUPP;
+ }
+ keylen = et->keytype->bits / 8;
+
+ ALLOC(kd.key, 1);
+ if(kd.key == NULL) {
+ krb5_set_error_message (context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ ret = krb5_data_alloc(&kd.key->keyvalue, et->keytype->size);
+ if(ret) {
+ free(kd.key);
+ return ret;
+ }
+ kd.key->keytype = etype;
+ tmp = malloc (keylen);
+ if(tmp == NULL) {
+ krb5_free_keyblock(context, kd.key);
+ krb5_set_error_message (context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ ret = _krb5_n_fold(str, len, tmp, keylen);
+ if (ret) {
+ free(tmp);
+ krb5_set_error_message (context, ENOMEM, N_("malloc: out of memory", ""));
+ return ret;
+ }
+ kd.schedule = NULL;
+ DES3_postproc (context, tmp, keylen, &kd); /* XXX */
+ memset(tmp, 0, keylen);
+ free(tmp);
+ ret = derive_key(context,
+ et,
+ &kd,
+ "kerberos", /* XXX well known constant */
+ strlen("kerberos"));
+ if (ret) {
+ free_key_data(context, &kd, et);
+ return ret;
+ }
+ ret = krb5_copy_keyblock_contents(context, kd.key, key);
+ free_key_data(context, &kd, et);
+ return ret;
+}
+
+static size_t
+wrapped_length (krb5_context context,
+ krb5_crypto crypto,
+ size_t data_len)
+{
+ struct encryption_type *et = crypto->et;
+ size_t padsize = et->padsize;
+ size_t checksumsize = CHECKSUMSIZE(et->checksum);
+ size_t res;
+
+ res = et->confoundersize + checksumsize + data_len;
+ res = (res + padsize - 1) / padsize * padsize;
+ return res;
+}
+
+static size_t
+wrapped_length_dervied (krb5_context context,
+ krb5_crypto crypto,
+ size_t data_len)
+{
+ struct encryption_type *et = crypto->et;
+ size_t padsize = et->padsize;
+ size_t res;
+
+ res = et->confoundersize + data_len;
+ res = (res + padsize - 1) / padsize * padsize;
+ if (et->keyed_checksum)
+ res += et->keyed_checksum->checksumsize;
+ else
+ res += et->checksum->checksumsize;
+ return res;
+}
+
+/*
+ * Return the size of an encrypted packet of length `data_len'
+ */
+
+size_t
+krb5_get_wrapped_length (krb5_context context,
+ krb5_crypto crypto,
+ size_t data_len)
+{
+ if (derived_crypto (context, crypto))
+ return wrapped_length_dervied (context, crypto, data_len);
+ else
+ return wrapped_length (context, crypto, data_len);
+}
+
+/*
+ * Return the size of an encrypted packet of length `data_len'
+ */
+
+static size_t
+crypto_overhead (krb5_context context,
+ krb5_crypto crypto)
+{
+ struct encryption_type *et = crypto->et;
+ size_t res;
+
+ res = CHECKSUMSIZE(et->checksum);
+ res += et->confoundersize;
+ if (et->padsize > 1)
+ res += et->padsize;
+ return res;
+}
+
+static size_t
+crypto_overhead_dervied (krb5_context context,
+ krb5_crypto crypto)
+{
+ struct encryption_type *et = crypto->et;
+ size_t res;
+
+ if (et->keyed_checksum)
+ res = CHECKSUMSIZE(et->keyed_checksum);
+ else
+ res = CHECKSUMSIZE(et->checksum);
+ res += et->confoundersize;
+ if (et->padsize > 1)
+ res += et->padsize;
+ return res;
+}
+
+size_t
+krb5_crypto_overhead (krb5_context context, krb5_crypto crypto)
+{
+ if (derived_crypto (context, crypto))
+ return crypto_overhead_dervied (context, crypto);
+ else
+ return crypto_overhead (context, crypto);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_random_to_key(krb5_context context,
+ krb5_enctype type,
+ const void *data,
+ size_t size,
+ krb5_keyblock *key)
+{
+ krb5_error_code ret;
+ struct encryption_type *et = _find_enctype(type);
+ if(et == NULL) {
+ krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
+ N_("encryption type %d not supported", ""),
+ type);
+ return KRB5_PROG_ETYPE_NOSUPP;
+ }
+ if ((et->keytype->bits + 7) / 8 > size) {
+ krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
+ N_("encryption key %s needs %d bytes "
+ "of random to make an encryption key "
+ "out of it", ""),
+ et->name, (int)et->keytype->size);
+ return KRB5_PROG_ETYPE_NOSUPP;
+ }
+ ret = krb5_data_alloc(&key->keyvalue, et->keytype->size);
+ if(ret)
+ return ret;
+ key->keytype = type;
+ if (et->keytype->random_to_key)
+ (*et->keytype->random_to_key)(context, key, data, size);
+ else
+ memcpy(key->keyvalue.data, data, et->keytype->size);
+
+ return 0;
+}
+
+krb5_error_code
+_krb5_pk_octetstring2key(krb5_context context,
+ krb5_enctype type,
+ const void *dhdata,
+ size_t dhsize,
+ const heim_octet_string *c_n,
+ const heim_octet_string *k_n,
+ krb5_keyblock *key)
+{
+ struct encryption_type *et = _find_enctype(type);
+ krb5_error_code ret;
+ size_t keylen, offset;
+ void *keydata;
+ unsigned char counter;
+ unsigned char shaoutput[20];
+
+ if(et == NULL) {
+ krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
+ N_("encryption type %d not supported", ""),
+ type);
+ return KRB5_PROG_ETYPE_NOSUPP;
+ }
+ keylen = (et->keytype->bits + 7) / 8;
+
+ keydata = malloc(keylen);
+ if (keydata == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+
+ counter = 0;
+ offset = 0;
+ do {
+ SHA_CTX m;
+
+ SHA1_Init(&m);
+ SHA1_Update(&m, &counter, 1);
+ SHA1_Update(&m, dhdata, dhsize);
+ if (c_n)
+ SHA1_Update(&m, c_n->data, c_n->length);
+ if (k_n)
+ SHA1_Update(&m, k_n->data, k_n->length);
+ SHA1_Final(shaoutput, &m);
+
+ memcpy((unsigned char *)keydata + offset,
+ shaoutput,
+ min(keylen - offset, sizeof(shaoutput)));
+
+ offset += sizeof(shaoutput);
+ counter++;
+ } while(offset < keylen);
+ memset(shaoutput, 0, sizeof(shaoutput));
+
+ ret = krb5_random_to_key(context, type, keydata, keylen, key);
+ memset(keydata, 0, sizeof(keylen));
+ free(keydata);
+ return ret;
+}
+
+static krb5_error_code
+encode_uvinfo(krb5_context context, krb5_const_principal p, krb5_data *data)
+{
+ KRB5PrincipalName pn;
+ krb5_error_code ret;
+ size_t size;
+
+ pn.principalName = p->name;
+ pn.realm = p->realm;
+
+ ASN1_MALLOC_ENCODE(KRB5PrincipalName, data->data, data->length,
+ &pn, &size, ret);
+ if (ret) {
+ krb5_data_zero(data);
+ krb5_set_error_message(context, ret,
+ N_("Failed to encode KRB5PrincipalName", ""));
+ return ret;
+ }
+ if (data->length != size)
+ krb5_abortx(context, "asn1 compiler internal error");
+ return 0;
+}
+
+static krb5_error_code
+encode_otherinfo(krb5_context context,
+ const AlgorithmIdentifier *ai,
+ krb5_const_principal client,
+ krb5_const_principal server,
+ krb5_enctype enctype,
+ const krb5_data *as_req,
+ const krb5_data *pk_as_rep,
+ const Ticket *ticket,
+ krb5_data *other)
+{
+ PkinitSP80056AOtherInfo otherinfo;
+ PkinitSuppPubInfo pubinfo;
+ krb5_error_code ret;
+ krb5_data pub;
+ size_t size;
+
+ krb5_data_zero(other);
+ memset(&otherinfo, 0, sizeof(otherinfo));
+ memset(&pubinfo, 0, sizeof(pubinfo));
+
+ pubinfo.enctype = enctype;
+ pubinfo.as_REQ = *as_req;
+ pubinfo.pk_as_rep = *pk_as_rep;
+ pubinfo.ticket = *ticket;
+ ASN1_MALLOC_ENCODE(PkinitSuppPubInfo, pub.data, pub.length,
+ &pubinfo, &size, ret);
+ if (ret) {
+ krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ return ret;
+ }
+ if (pub.length != size)
+ krb5_abortx(context, "asn1 compiler internal error");
+
+ ret = encode_uvinfo(context, client, &otherinfo.partyUInfo);
+ if (ret) {
+ free(pub.data);
+ return ret;
+ }
+ ret = encode_uvinfo(context, server, &otherinfo.partyVInfo);
+ if (ret) {
+ free(otherinfo.partyUInfo.data);
+ free(pub.data);
+ return ret;
+ }
+
+ otherinfo.algorithmID = *ai;
+ otherinfo.suppPubInfo = &pub;
+
+ ASN1_MALLOC_ENCODE(PkinitSP80056AOtherInfo, other->data, other->length,
+ &otherinfo, &size, ret);
+ free(otherinfo.partyUInfo.data);
+ free(otherinfo.partyVInfo.data);
+ free(pub.data);
+ if (ret) {
+ krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ return ret;
+ }
+ if (other->length != size)
+ krb5_abortx(context, "asn1 compiler internal error");
+
+ return 0;
+}
+
+krb5_error_code
+_krb5_pk_kdf(krb5_context context,
+ const struct AlgorithmIdentifier *ai,
+ const void *dhdata,
+ size_t dhsize,
+ krb5_const_principal client,
+ krb5_const_principal server,
+ krb5_enctype enctype,
+ const krb5_data *as_req,
+ const krb5_data *pk_as_rep,
+ const Ticket *ticket,
+ krb5_keyblock *key)
+{
+ struct encryption_type *et;
+ krb5_error_code ret;
+ krb5_data other;
+ size_t keylen, offset;
+ uint32_t counter;
+ unsigned char *keydata;
+ unsigned char shaoutput[20];
+
+ if (der_heim_oid_cmp(oid_id_pkinit_kdf_ah_sha1(), &ai->algorithm) != 0) {
+ krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
+ N_("KDF not supported", ""));
+ return KRB5_PROG_ETYPE_NOSUPP;
+ }
+ if (ai->parameters != NULL &&
+ (ai->parameters->length != 2 ||
+ memcmp(ai->parameters->data, "\x05\x00", 2) != 0))
+ {
+ krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
+ N_("kdf params not NULL or the NULL-type",
+ ""));
+ return KRB5_PROG_ETYPE_NOSUPP;
+ }
+
+ et = _find_enctype(enctype);
+ if(et == NULL) {
+ krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
+ N_("encryption type %d not supported", ""),
+ enctype);
+ return KRB5_PROG_ETYPE_NOSUPP;
+ }
+ keylen = (et->keytype->bits + 7) / 8;
+
+ keydata = malloc(keylen);
+ if (keydata == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+
+ ret = encode_otherinfo(context, ai, client, server,
+ enctype, as_req, pk_as_rep, ticket, &other);
+ if (ret) {
+ free(keydata);
+ return ret;
+ }
+
+ offset = 0;
+ counter = 1;
+ do {
+ unsigned char cdata[4];
+ SHA_CTX m;
+
+ SHA1_Init(&m);
+ _krb5_put_int(cdata, counter, 4);
+ SHA1_Update(&m, cdata, 4);
+ SHA1_Update(&m, dhdata, dhsize);
+ SHA1_Update(&m, other.data, other.length);
+ SHA1_Final(shaoutput, &m);
+
+ memcpy((unsigned char *)keydata + offset,
+ shaoutput,
+ min(keylen - offset, sizeof(shaoutput)));
+
+ offset += sizeof(shaoutput);
+ counter++;
+ } while(offset < keylen);
+ memset(shaoutput, 0, sizeof(shaoutput));
+
+ free(other.data);
+
+ ret = krb5_random_to_key(context, enctype, keydata, keylen, key);
+ memset(keydata, 0, sizeof(keylen));
+ free(keydata);
+
+ return ret;
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_crypto_prf_length(krb5_context context,
+ krb5_enctype type,
+ size_t *length)
+{
+ struct encryption_type *et = _find_enctype(type);
+
+ if(et == NULL || et->prf_length == 0) {
+ krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
+ N_("encryption type %d not supported", ""),
+ type);
+ return KRB5_PROG_ETYPE_NOSUPP;
+ }
+
+ *length = et->prf_length;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_crypto_prf(krb5_context context,
+ const krb5_crypto crypto,
+ const krb5_data *input,
+ krb5_data *output)
+{
+ struct encryption_type *et = crypto->et;
+
+ krb5_data_zero(output);
+
+ if(et->prf == NULL) {
+ krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
+ "kerberos prf for %s not supported",
+ et->name);
+ return KRB5_PROG_ETYPE_NOSUPP;
+ }
+
+ return (*et->prf)(context, crypto, input, output);
+}
+
+#ifndef HEIMDAL_SMALLER
+
+static struct key_type *keytypes[] = {
+ &keytype_null,
+ &keytype_des,
+ &keytype_des3_derived,
+#ifdef DES3_OLD_ENCTYPE
+ &keytype_des3,
+#endif
+ &keytype_aes128,
+ &keytype_aes256,
+ &keytype_arcfour
+};
+
+static int num_keytypes = sizeof(keytypes) / sizeof(keytypes[0]);
+
+
+static struct key_type *
+_find_keytype(krb5_keytype type)
+{
+ int i;
+ for(i = 0; i < num_keytypes; i++)
+ if(keytypes[i]->type == type)
+ return keytypes[i];
+ return NULL;
+}
+
+/*
+ * First take the configured list of etypes for `keytype' if available,
+ * else, do `krb5_keytype_to_enctypes'.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_keytype_to_enctypes_default (krb5_context context,
+ krb5_keytype keytype,
+ unsigned *len,
+ krb5_enctype **val)
+ __attribute__((deprecated))
+{
+ unsigned int i, n;
+ krb5_enctype *ret;
+
+ if (keytype != KEYTYPE_DES || context->etypes_des == NULL)
+ return krb5_keytype_to_enctypes (context, keytype, len, val);
+
+ for (n = 0; context->etypes_des[n]; ++n)
+ ;
+ ret = malloc (n * sizeof(*ret));
+ if (ret == NULL && n != 0) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ for (i = 0; i < n; ++i)
+ ret[i] = context->etypes_des[i];
+ *len = n;
+ *val = ret;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_keytype_to_string(krb5_context context,
+ krb5_keytype keytype,
+ char **string)
+ __attribute__((deprecated))
+{
+ struct key_type *kt = _find_keytype(keytype);
+ if(kt == NULL) {
+ krb5_set_error_message(context, KRB5_PROG_KEYTYPE_NOSUPP,
+ "key type %d not supported", keytype);
+ return KRB5_PROG_KEYTYPE_NOSUPP;
+ }
+ *string = strdup(kt->name);
+ if(*string == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ return 0;
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_string_to_keytype(krb5_context context,
+ const char *string,
+ krb5_keytype *keytype)
+ __attribute__((deprecated))
+{
+ char *end;
+ int i;
+
+ for(i = 0; i < num_keytypes; i++)
+ if(strcasecmp(keytypes[i]->name, string) == 0){
+ *keytype = keytypes[i]->type;
+ return 0;
+ }
+
+ /* check if the enctype is a number */
+ *keytype = strtol(string, &end, 0);
+ if(*end == '\0' && *keytype != 0) {
+ if (krb5_enctype_valid(context, *keytype) == 0)
+ return 0;
+ }
+
+ krb5_set_error_message(context, KRB5_PROG_KEYTYPE_NOSUPP,
+ "key type %s not supported", string);
+ return KRB5_PROG_KEYTYPE_NOSUPP;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_keytype_to_enctypes (krb5_context context,
+ krb5_keytype keytype,
+ unsigned *len,
+ krb5_enctype **val)
+{
+ int i;
+ unsigned n = 0;
+ krb5_enctype *ret;
+
+ for (i = num_etypes - 1; i >= 0; --i) {
+ if (etypes[i]->keytype->type == keytype
+ && !(etypes[i]->flags & F_PSEUDO))
+ ++n;
+ }
+ ret = malloc(n * sizeof(*ret));
+ if (ret == NULL && n != 0) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ n = 0;
+ for (i = num_etypes - 1; i >= 0; --i) {
+ if (etypes[i]->keytype->type == keytype
+ && !(etypes[i]->flags & F_PSEUDO))
+ ret[n++] = etypes[i]->type;
+ }
+ *len = n;
+ *val = ret;
+ return 0;
+}
+
+#endif /* HEIMDAL_SMALLER */
diff --git a/source4/heimdal/lib/krb5/data.c b/source4/heimdal/lib/krb5/data.c
new file mode 100644
index 0000000000..d6099c3c6c
--- /dev/null
+++ b/source4/heimdal/lib/krb5/data.c
@@ -0,0 +1,224 @@
+/*
+ * Copyright (c) 1997 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+/**
+ * Reset the (potentially uninitalized) krb5_data structure.
+ *
+ * @param p krb5_data to reset.
+ *
+ * @ingroup krb5
+ */
+
+void KRB5_LIB_FUNCTION
+krb5_data_zero(krb5_data *p)
+{
+ p->length = 0;
+ p->data = NULL;
+}
+
+/**
+ * Free the content of krb5_data structure, its ok to free a zeroed
+ * structure. When done, the structure will be zeroed.
+ *
+ * @param p krb5_data to free.
+ *
+ * @ingroup krb5
+ */
+
+void KRB5_LIB_FUNCTION
+krb5_data_free(krb5_data *p)
+{
+ if(p->data != NULL)
+ free(p->data);
+ krb5_data_zero(p);
+}
+
+/**
+ * Same as krb5_data_free().
+ *
+ * @param context Kerberos 5 context.
+ * @param data krb5_data to free.
+ *
+ * @ingroup krb5
+ */
+
+void KRB5_LIB_FUNCTION
+krb5_free_data_contents(krb5_context context, krb5_data *data)
+{
+ krb5_data_free(data);
+}
+
+/**
+ * Free krb5_data (and its content).
+ *
+ * @param context Kerberos 5 context.
+ * @param p krb5_data to free.
+ *
+ * @ingroup krb5
+ */
+
+void KRB5_LIB_FUNCTION
+krb5_free_data(krb5_context context,
+ krb5_data *p)
+{
+ krb5_data_free(p);
+ free(p);
+}
+
+/**
+ * Allocate data of and krb5_data.
+ *
+ * @param p krb5_data to free.
+ * @param len size to allocate.
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned.
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_data_alloc(krb5_data *p, int len)
+{
+ p->data = malloc(len);
+ if(len && p->data == NULL)
+ return ENOMEM;
+ p->length = len;
+ return 0;
+}
+
+/**
+ * Grow (or shrink) the content of krb5_data to a new size.
+ *
+ * @param p krb5_data to free.
+ * @param len new size.
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned.
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_data_realloc(krb5_data *p, int len)
+{
+ void *tmp;
+ tmp = realloc(p->data, len);
+ if(len && !tmp)
+ return ENOMEM;
+ p->data = tmp;
+ p->length = len;
+ return 0;
+}
+
+/**
+ * Copy the data of len into the krb5_data.
+ *
+ * @param p krb5_data to copy into.
+ * @param data data to copy..
+ * @param len new size.
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned.
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_data_copy(krb5_data *p, const void *data, size_t len)
+{
+ if (len) {
+ if(krb5_data_alloc(p, len))
+ return ENOMEM;
+ memmove(p->data, data, len);
+ } else
+ p->data = NULL;
+ p->length = len;
+ return 0;
+}
+
+/**
+ * Copy the data into a newly allocated krb5_data.
+ *
+ * @param context Kerberos 5 context.
+ * @param indata the krb5_data data to copy
+ * @param outdata new krb5_date to copy too. Free with krb5_free_data().
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned.
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_copy_data(krb5_context context,
+ const krb5_data *indata,
+ krb5_data **outdata)
+{
+ krb5_error_code ret;
+ ALLOC(*outdata, 1);
+ if(*outdata == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ ret = der_copy_octet_string(indata, *outdata);
+ if(ret) {
+ krb5_clear_error_message (context);
+ free(*outdata);
+ *outdata = NULL;
+ }
+ return ret;
+}
+
+/**
+ * Compare to data.
+ *
+ * @param data1 krb5_data to compare
+ * @param data2 krb5_data to compare
+ *
+ * @return return the same way as memcmp(), useful when sorting.
+ *
+ * @ingroup krb5
+ */
+
+int KRB5_LIB_FUNCTION
+krb5_data_cmp(const krb5_data *data1, const krb5_data *data2)
+{
+ if (data1->length != data2->length)
+ return data1->length - data2->length;
+ return memcmp(data1->data, data2->data, data1->length);
+}
diff --git a/source4/heimdal/lib/krb5/eai_to_heim_errno.c b/source4/heimdal/lib/krb5/eai_to_heim_errno.c
new file mode 100644
index 0000000000..594f998e26
--- /dev/null
+++ b/source4/heimdal/lib/krb5/eai_to_heim_errno.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2000 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <krb5_locl.h>
+
+RCSID("$Id$");
+
+/**
+ * Convert the getaddrinfo() error code to a Kerberos et error code.
+ *
+ * @param eai_errno contains the error code from getaddrinfo().
+ * @param system_error should have the value of errno after the failed getaddrinfo().
+ *
+ * @return Kerberos error code representing the EAI errors.
+ *
+ * @ingroup krb5_error
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_eai_to_heim_errno(int eai_errno, int system_error)
+{
+ switch(eai_errno) {
+ case EAI_NOERROR:
+ return 0;
+#ifdef EAI_ADDRFAMILY
+ case EAI_ADDRFAMILY:
+ return HEIM_EAI_ADDRFAMILY;
+#endif
+ case EAI_AGAIN:
+ return HEIM_EAI_AGAIN;
+ case EAI_BADFLAGS:
+ return HEIM_EAI_BADFLAGS;
+ case EAI_FAIL:
+ return HEIM_EAI_FAIL;
+ case EAI_FAMILY:
+ return HEIM_EAI_FAMILY;
+ case EAI_MEMORY:
+ return HEIM_EAI_MEMORY;
+#if defined(EAI_NODATA) && EAI_NODATA != EAI_NONAME
+ case EAI_NODATA:
+ return HEIM_EAI_NODATA;
+#endif
+ case EAI_NONAME:
+ return HEIM_EAI_NONAME;
+ case EAI_SERVICE:
+ return HEIM_EAI_SERVICE;
+ case EAI_SOCKTYPE:
+ return HEIM_EAI_SOCKTYPE;
+ case EAI_SYSTEM:
+ return system_error;
+ default:
+ return HEIM_EAI_UNKNOWN; /* XXX */
+ }
+}
+
+/**
+ * Convert the gethostname() error code (h_error) to a Kerberos et
+ * error code.
+ *
+ * @param eai_errno contains the error code from gethostname().
+ *
+ * @return Kerberos error code representing the gethostname errors.
+ *
+ * @ingroup krb5_error
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_h_errno_to_heim_errno(int eai_errno)
+{
+ switch(eai_errno) {
+ case 0:
+ return 0;
+ case HOST_NOT_FOUND:
+ return HEIM_EAI_NONAME;
+ case TRY_AGAIN:
+ return HEIM_EAI_AGAIN;
+ case NO_RECOVERY:
+ return HEIM_EAI_FAIL;
+ case NO_DATA:
+ return HEIM_EAI_NONAME;
+ default:
+ return HEIM_EAI_UNKNOWN; /* XXX */
+ }
+}
diff --git a/source4/heimdal/lib/krb5/error_string.c b/source4/heimdal/lib/krb5/error_string.c
new file mode 100644
index 0000000000..6374fa17ae
--- /dev/null
+++ b/source4/heimdal/lib/krb5/error_string.c
@@ -0,0 +1,277 @@
+/*
+ * Copyright (c) 2001, 2003, 2005 - 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+#undef __attribute__
+#define __attribute__(X)
+
+/**
+ * Clears the error message from the Kerberos 5 context.
+ *
+ * @param context The Kerberos 5 context to clear
+ *
+ * @ingroup krb5_error
+ */
+
+void KRB5_LIB_FUNCTION
+krb5_clear_error_message(krb5_context context)
+{
+ HEIMDAL_MUTEX_lock(context->mutex);
+ if (context->error_string)
+ free(context->error_string);
+ context->error_code = 0;
+ context->error_string = NULL;
+ HEIMDAL_MUTEX_unlock(context->mutex);
+}
+
+/**
+ * Set the context full error string for a specific error code.
+ * The error that is stored should be internationalized.
+ *
+ * @param context Kerberos 5 context
+ * @param ret The error code
+ * @param fmt Error string for the error code
+ * @param ... printf(3) style parameters.
+ *
+ * @ingroup krb5_error
+ */
+
+void KRB5_LIB_FUNCTION
+krb5_set_error_message(krb5_context context, krb5_error_code ret,
+ const char *fmt, ...)
+ __attribute__ ((format (printf, 3, 4)))
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ krb5_vset_error_message (context, ret, fmt, ap);
+ va_end(ap);
+}
+
+/**
+ * Set the context full error string for a specific error code.
+ *
+ * @param context Kerberos 5 context
+ * @param ret The error code
+ * @param fmt Error string for the error code
+ * @param args printf(3) style parameters.
+ *
+ * @ingroup krb5_error
+ */
+
+
+void KRB5_LIB_FUNCTION
+krb5_vset_error_message (krb5_context context, krb5_error_code ret,
+ const char *fmt, va_list args)
+ __attribute__ ((format (printf, 3, 0)))
+{
+
+ krb5_clear_error_message(context);
+ HEIMDAL_MUTEX_lock(context->mutex);
+ context->error_code = ret;
+ vasprintf(&context->error_string, fmt, args);
+ HEIMDAL_MUTEX_unlock(context->mutex);
+}
+
+
+/**
+ * Return the error message in context. On error or no error string,
+ * the function returns NULL.
+ *
+ * @param context Kerberos 5 context
+ *
+ * @return an error string, needs to be freed with
+ * krb5_free_error_message(). The functions return NULL on error.
+ *
+ * @ingroup krb5_error
+ */
+
+char * KRB5_LIB_FUNCTION
+krb5_get_error_string(krb5_context context)
+{
+ char *ret = NULL;
+
+ HEIMDAL_MUTEX_lock(context->mutex);
+ if (context->error_string)
+ ret = strdup(context->error_string);
+ HEIMDAL_MUTEX_unlock(context->mutex);
+ return ret;
+}
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_have_error_string(krb5_context context)
+{
+ char *str;
+ HEIMDAL_MUTEX_lock(context->mutex);
+ str = context->error_string;
+ HEIMDAL_MUTEX_unlock(context->mutex);
+ return str != NULL;
+}
+
+/**
+ * Return the error message for `code' in context. On memory
+ * allocation error the function returns NULL.
+ *
+ * @param context Kerberos 5 context
+ * @param code Error code related to the error
+ *
+ * @return an error string, needs to be freed with
+ * krb5_free_error_message(). The functions return NULL on error.
+ *
+ * @ingroup krb5_error
+ */
+
+const char * KRB5_LIB_FUNCTION
+krb5_get_error_message(krb5_context context, krb5_error_code code)
+{
+ const char *cstr;
+ char *str;
+
+ HEIMDAL_MUTEX_lock(context->mutex);
+ if (context->error_string &&
+ (code == context->error_code || context->error_code == 0))
+ {
+ str = strdup(context->error_string);
+ if (str) {
+ HEIMDAL_MUTEX_unlock(context->mutex);
+ return str;
+ }
+ }
+ HEIMDAL_MUTEX_unlock(context->mutex);
+
+ cstr = krb5_get_err_text(context, code);
+ if (cstr)
+ return strdup(cstr);
+
+ if (asprintf(&str, "<unknown error: %d>", (int)code) == -1)
+ return NULL;
+
+ return str;
+}
+
+
+/**
+ * Free the error message returned by krb5_get_error_message().
+ *
+ * @param context Kerberos context
+ * @param msg error message to free, returned byg
+ * krb5_get_error_message().
+ *
+ * @ingroup krb5_error
+ */
+
+void KRB5_LIB_FUNCTION
+krb5_free_error_message(krb5_context context, const char *msg)
+{
+ free(rk_UNCONST(msg));
+}
+
+#ifndef HEIMDAL_SMALLER
+
+/**
+ * Free the error message returned by krb5_get_error_string(),
+ * deprecated, use krb5_free_error_message().
+ *
+ * @param context Kerberos context
+ * @param msg error message to free
+ *
+ * @ingroup krb5_deprecated
+ */
+
+void KRB5_LIB_FUNCTION
+krb5_free_error_string(krb5_context context, char *str)
+ __attribute__((deprecated))
+{
+ krb5_free_error_message(context, str);
+}
+
+/**
+ * Set the error message returned by krb5_get_error_string(),
+ * deprecated, use krb5_set_error_message().
+ *
+ * @param context Kerberos context
+ * @param msg error message to free
+ *
+ * @ingroup krb5_deprecated
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_set_error_string(krb5_context context, const char *fmt, ...)
+ __attribute__((format (printf, 2, 3))) __attribute__((deprecated))
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ krb5_vset_error_message (context, 0, fmt, ap);
+ va_end(ap);
+ return 0;
+}
+
+/**
+ * Set the error message returned by krb5_get_error_string(),
+ * deprecated, use krb5_set_error_message().
+ *
+ * @param context Kerberos context
+ * @param msg error message to free
+ *
+ * @ingroup krb5_deprecated
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_vset_error_string(krb5_context context, const char *fmt, va_list args)
+ __attribute__ ((format (printf, 2, 0))) __attribute__((deprecated))
+{
+ krb5_vset_error_message(context, 0, fmt, args);
+ return 0;
+}
+
+/**
+ * Clar the error message returned by krb5_get_error_string(),
+ * deprecated, use krb5_clear_error_message().
+ *
+ * @param context Kerberos context
+ *
+ * @ingroup krb5_deprecated
+ */
+
+void KRB5_LIB_FUNCTION
+krb5_clear_error_string(krb5_context context)
+ __attribute__((deprecated))
+{
+ krb5_clear_error_message(context);
+}
+
+#endif /* !HEIMDAL_SMALLER */
diff --git a/source4/heimdal/lib/krb5/expand_hostname.c b/source4/heimdal/lib/krb5/expand_hostname.c
new file mode 100644
index 0000000000..a712d9c83a
--- /dev/null
+++ b/source4/heimdal/lib/krb5/expand_hostname.c
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 1999 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+static krb5_error_code
+copy_hostname(krb5_context context,
+ const char *orig_hostname,
+ char **new_hostname)
+{
+ *new_hostname = strdup (orig_hostname);
+ if (*new_hostname == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ strlwr (*new_hostname);
+ return 0;
+}
+
+/*
+ * Try to make `orig_hostname' into a more canonical one in the newly
+ * allocated space returned in `new_hostname'.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_expand_hostname (krb5_context context,
+ const char *orig_hostname,
+ char **new_hostname)
+{
+ struct addrinfo *ai, *a, hints;
+ int error;
+
+ if ((context->flags & KRB5_CTX_F_DNS_CANONICALIZE_HOSTNAME) == 0)
+ return copy_hostname (context, orig_hostname, new_hostname);
+
+ memset (&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_CANONNAME;
+
+ error = getaddrinfo (orig_hostname, NULL, &hints, &ai);
+ if (error)
+ return copy_hostname (context, orig_hostname, new_hostname);
+ for (a = ai; a != NULL; a = a->ai_next) {
+ if (a->ai_canonname != NULL) {
+ *new_hostname = strdup (a->ai_canonname);
+ freeaddrinfo (ai);
+ if (*new_hostname == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ } else {
+ return 0;
+ }
+ }
+ }
+ freeaddrinfo (ai);
+ return copy_hostname (context, orig_hostname, new_hostname);
+}
+
+/*
+ * handle the case of the hostname being unresolvable and thus identical
+ */
+
+static krb5_error_code
+vanilla_hostname (krb5_context context,
+ const char *orig_hostname,
+ char **new_hostname,
+ char ***realms)
+{
+ krb5_error_code ret;
+
+ ret = copy_hostname (context, orig_hostname, new_hostname);
+ if (ret)
+ return ret;
+ strlwr (*new_hostname);
+
+ ret = krb5_get_host_realm (context, *new_hostname, realms);
+ if (ret) {
+ free (*new_hostname);
+ return ret;
+ }
+ return 0;
+}
+
+/*
+ * expand `hostname' to a name we believe to be a hostname in newly
+ * allocated space in `host' and return realms in `realms'.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_expand_hostname_realms (krb5_context context,
+ const char *orig_hostname,
+ char **new_hostname,
+ char ***realms)
+{
+ struct addrinfo *ai, *a, hints;
+ int error;
+ krb5_error_code ret = 0;
+
+ if ((context->flags & KRB5_CTX_F_DNS_CANONICALIZE_HOSTNAME) == 0)
+ return vanilla_hostname (context, orig_hostname, new_hostname,
+ realms);
+
+ memset (&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_CANONNAME;
+
+ error = getaddrinfo (orig_hostname, NULL, &hints, &ai);
+ if (error)
+ return vanilla_hostname (context, orig_hostname, new_hostname,
+ realms);
+
+ for (a = ai; a != NULL; a = a->ai_next) {
+ if (a->ai_canonname != NULL) {
+ ret = copy_hostname (context, a->ai_canonname, new_hostname);
+ if (ret) {
+ freeaddrinfo (ai);
+ return ret;
+ }
+ strlwr (*new_hostname);
+ ret = krb5_get_host_realm (context, *new_hostname, realms);
+ if (ret == 0) {
+ freeaddrinfo (ai);
+ return 0;
+ }
+ free (*new_hostname);
+ }
+ }
+ freeaddrinfo(ai);
+ return vanilla_hostname (context, orig_hostname, new_hostname, realms);
+}
diff --git a/source4/heimdal/lib/krb5/fcache.c b/source4/heimdal/lib/krb5/fcache.c
new file mode 100644
index 0000000000..b745c67e11
--- /dev/null
+++ b/source4/heimdal/lib/krb5/fcache.c
@@ -0,0 +1,983 @@
+/*
+ * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+typedef struct krb5_fcache{
+ char *filename;
+ int version;
+}krb5_fcache;
+
+struct fcc_cursor {
+ int fd;
+ krb5_storage *sp;
+};
+
+#define KRB5_FCC_FVNO_1 1
+#define KRB5_FCC_FVNO_2 2
+#define KRB5_FCC_FVNO_3 3
+#define KRB5_FCC_FVNO_4 4
+
+#define FCC_TAG_DELTATIME 1
+
+#define FCACHE(X) ((krb5_fcache*)(X)->data.data)
+
+#define FILENAME(X) (FCACHE(X)->filename)
+
+#define FCC_CURSOR(C) ((struct fcc_cursor*)(C))
+
+static const char*
+fcc_get_name(krb5_context context,
+ krb5_ccache id)
+{
+ return FILENAME(id);
+}
+
+int
+_krb5_xlock(krb5_context context, int fd, krb5_boolean exclusive,
+ const char *filename)
+{
+ int ret;
+#ifdef HAVE_FCNTL
+ struct flock l;
+
+ l.l_start = 0;
+ l.l_len = 0;
+ l.l_type = exclusive ? F_WRLCK : F_RDLCK;
+ l.l_whence = SEEK_SET;
+ ret = fcntl(fd, F_SETLKW, &l);
+#else
+ ret = flock(fd, exclusive ? LOCK_EX : LOCK_SH);
+#endif
+ if(ret < 0)
+ ret = errno;
+ if(ret == EACCES) /* fcntl can return EACCES instead of EAGAIN */
+ ret = EAGAIN;
+
+ switch (ret) {
+ case 0:
+ break;
+ case EINVAL: /* filesystem doesn't support locking, let the user have it */
+ ret = 0;
+ break;
+ case EAGAIN:
+ krb5_set_error_message(context, ret,
+ N_("timed out locking cache file %s", "file"),
+ filename);
+ break;
+ default:
+ krb5_set_error_message(context, ret,
+ N_("error locking cache file %s: %s",
+ "file, error"),
+ filename, strerror(ret));
+ break;
+ }
+ return ret;
+}
+
+int
+_krb5_xunlock(krb5_context context, int fd)
+{
+ int ret;
+#ifdef HAVE_FCNTL
+ struct flock l;
+ l.l_start = 0;
+ l.l_len = 0;
+ l.l_type = F_UNLCK;
+ l.l_whence = SEEK_SET;
+ ret = fcntl(fd, F_SETLKW, &l);
+#else
+ ret = flock(fd, LOCK_UN);
+#endif
+ if (ret < 0)
+ ret = errno;
+ switch (ret) {
+ case 0:
+ break;
+ case EINVAL: /* filesystem doesn't support locking, let the user have it */
+ ret = 0;
+ break;
+ default:
+ krb5_set_error_message(context, ret,
+ N_("Failed to unlock file: %s", ""),
+ strerror(ret));
+ break;
+ }
+ return ret;
+}
+
+static krb5_error_code
+write_storage(krb5_context context, krb5_storage *sp, int fd)
+{
+ krb5_error_code ret;
+ krb5_data data;
+ ssize_t sret;
+
+ ret = krb5_storage_to_data(sp, &data);
+ if (ret) {
+ krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ return ret;
+ }
+ sret = write(fd, data.data, data.length);
+ ret = (sret != data.length);
+ krb5_data_free(&data);
+ if (ret) {
+ ret = errno;
+ krb5_set_error_message(context, ret,
+ N_("Failed to write FILE credential data", ""));
+ return ret;
+ }
+ return 0;
+}
+
+
+static krb5_error_code
+fcc_lock(krb5_context context, krb5_ccache id,
+ int fd, krb5_boolean exclusive)
+{
+ return _krb5_xlock(context, fd, exclusive, fcc_get_name(context, id));
+}
+
+static krb5_error_code
+fcc_unlock(krb5_context context, int fd)
+{
+ return _krb5_xunlock(context, fd);
+}
+
+static krb5_error_code
+fcc_resolve(krb5_context context, krb5_ccache *id, const char *res)
+{
+ krb5_fcache *f;
+ f = malloc(sizeof(*f));
+ if(f == NULL) {
+ krb5_set_error_message(context, KRB5_CC_NOMEM,
+ N_("malloc: out of memory", ""));
+ return KRB5_CC_NOMEM;
+ }
+ f->filename = strdup(res);
+ if(f->filename == NULL){
+ free(f);
+ krb5_set_error_message(context, KRB5_CC_NOMEM,
+ N_("malloc: out of memory", ""));
+ return KRB5_CC_NOMEM;
+ }
+ f->version = 0;
+ (*id)->data.data = f;
+ (*id)->data.length = sizeof(*f);
+ return 0;
+}
+
+/*
+ * Try to scrub the contents of `filename' safely.
+ */
+
+static int
+scrub_file (int fd)
+{
+ off_t pos;
+ char buf[128];
+
+ pos = lseek(fd, 0, SEEK_END);
+ if (pos < 0)
+ return errno;
+ if (lseek(fd, 0, SEEK_SET) < 0)
+ return errno;
+ memset(buf, 0, sizeof(buf));
+ while(pos > 0) {
+ ssize_t tmp = write(fd, buf, min(sizeof(buf), pos));
+
+ if (tmp < 0)
+ return errno;
+ pos -= tmp;
+ }
+ fsync (fd);
+ return 0;
+}
+
+/*
+ * Erase `filename' if it exists, trying to remove the contents if
+ * it's `safe'. We always try to remove the file, it it exists. It's
+ * only overwritten if it's a regular file (not a symlink and not a
+ * hardlink)
+ */
+
+static krb5_error_code
+erase_file(krb5_context context, const char *filename)
+{
+ int fd;
+ struct stat sb1, sb2;
+ int ret;
+
+ ret = lstat (filename, &sb1);
+ if (ret < 0)
+ return errno;
+
+ fd = open(filename, O_RDWR | O_BINARY);
+ if(fd < 0) {
+ if(errno == ENOENT)
+ return 0;
+ else
+ return errno;
+ }
+ rk_cloexec(fd);
+ ret = _krb5_xlock(context, fd, 1, filename);
+ if (ret) {
+ close(fd);
+ return ret;
+ }
+ if (unlink(filename) < 0) {
+ _krb5_xunlock(context, fd);
+ close (fd);
+ return errno;
+ }
+ ret = fstat (fd, &sb2);
+ if (ret < 0) {
+ _krb5_xunlock(context, fd);
+ close (fd);
+ return errno;
+ }
+
+ /* check if someone was playing with symlinks */
+
+ if (sb1.st_dev != sb2.st_dev || sb1.st_ino != sb2.st_ino) {
+ _krb5_xunlock(context, fd);
+ close (fd);
+ return EPERM;
+ }
+
+ /* there are still hard links to this file */
+
+ if (sb2.st_nlink != 0) {
+ _krb5_xunlock(context, fd);
+ close (fd);
+ return 0;
+ }
+
+ ret = scrub_file (fd);
+ if (ret) {
+ _krb5_xunlock(context, fd);
+ close(fd);
+ return ret;
+ }
+ ret = _krb5_xunlock(context, fd);
+ close (fd);
+ return ret;
+}
+
+static krb5_error_code
+fcc_gen_new(krb5_context context, krb5_ccache *id)
+{
+ krb5_fcache *f;
+ int fd;
+ char *file;
+
+ f = malloc(sizeof(*f));
+ if(f == NULL) {
+ krb5_set_error_message(context, KRB5_CC_NOMEM,
+ N_("malloc: out of memory", ""));
+ return KRB5_CC_NOMEM;
+ }
+ asprintf (&file, "%sXXXXXX", KRB5_DEFAULT_CCFILE_ROOT);
+ if(file == NULL) {
+ free(f);
+ krb5_set_error_message(context, KRB5_CC_NOMEM,
+ N_("malloc: out of memory", ""));
+ return KRB5_CC_NOMEM;
+ }
+ fd = mkstemp(file);
+ if(fd < 0) {
+ int ret = errno;
+ krb5_set_error_message(context, ret, N_("mkstemp %s failed", ""), file);
+ free(f);
+ free(file);
+ return ret;
+ }
+ close(fd);
+ f->filename = file;
+ f->version = 0;
+ (*id)->data.data = f;
+ (*id)->data.length = sizeof(*f);
+ return 0;
+}
+
+static void
+storage_set_flags(krb5_context context, krb5_storage *sp, int vno)
+{
+ int flags = 0;
+ switch(vno) {
+ case KRB5_FCC_FVNO_1:
+ flags |= KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS;
+ flags |= KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE;
+ flags |= KRB5_STORAGE_HOST_BYTEORDER;
+ break;
+ case KRB5_FCC_FVNO_2:
+ flags |= KRB5_STORAGE_HOST_BYTEORDER;
+ break;
+ case KRB5_FCC_FVNO_3:
+ flags |= KRB5_STORAGE_KEYBLOCK_KEYTYPE_TWICE;
+ break;
+ case KRB5_FCC_FVNO_4:
+ break;
+ default:
+ krb5_abortx(context,
+ "storage_set_flags called with bad vno (%x)", vno);
+ }
+ krb5_storage_set_flags(sp, flags);
+}
+
+static krb5_error_code
+fcc_open(krb5_context context,
+ krb5_ccache id,
+ int *fd_ret,
+ int flags,
+ mode_t mode)
+{
+ krb5_boolean exclusive = ((flags | O_WRONLY) == flags ||
+ (flags | O_RDWR) == flags);
+ krb5_error_code ret;
+ const char *filename = FILENAME(id);
+ int fd;
+ fd = open(filename, flags, mode);
+ if(fd < 0) {
+ ret = errno;
+ krb5_set_error_message(context, ret, N_("open(%s): %s", "file, error"),
+ filename, strerror(ret));
+ return ret;
+ }
+ rk_cloexec(fd);
+
+ if((ret = fcc_lock(context, id, fd, exclusive)) != 0) {
+ close(fd);
+ return ret;
+ }
+ *fd_ret = fd;
+ return 0;
+}
+
+static krb5_error_code
+fcc_initialize(krb5_context context,
+ krb5_ccache id,
+ krb5_principal primary_principal)
+{
+ krb5_fcache *f = FCACHE(id);
+ int ret = 0;
+ int fd;
+ char *filename = f->filename;
+
+ unlink (filename);
+
+ ret = fcc_open(context, id, &fd, O_RDWR | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC, 0600);
+ if(ret)
+ return ret;
+ {
+ krb5_storage *sp;
+ sp = krb5_storage_emem();
+ krb5_storage_set_eof_code(sp, KRB5_CC_END);
+ if(context->fcache_vno != 0)
+ f->version = context->fcache_vno;
+ else
+ f->version = KRB5_FCC_FVNO_4;
+ ret |= krb5_store_int8(sp, 5);
+ ret |= krb5_store_int8(sp, f->version);
+ storage_set_flags(context, sp, f->version);
+ if(f->version == KRB5_FCC_FVNO_4 && ret == 0) {
+ /* V4 stuff */
+ if (context->kdc_sec_offset) {
+ ret |= krb5_store_int16 (sp, 12); /* length */
+ ret |= krb5_store_int16 (sp, FCC_TAG_DELTATIME); /* Tag */
+ ret |= krb5_store_int16 (sp, 8); /* length of data */
+ ret |= krb5_store_int32 (sp, context->kdc_sec_offset);
+ ret |= krb5_store_int32 (sp, context->kdc_usec_offset);
+ } else {
+ ret |= krb5_store_int16 (sp, 0);
+ }
+ }
+ ret |= krb5_store_principal(sp, primary_principal);
+
+ ret |= write_storage(context, sp, fd);
+
+ krb5_storage_free(sp);
+ }
+ fcc_unlock(context, fd);
+ if (close(fd) < 0)
+ if (ret == 0) {
+ ret = errno;
+ krb5_set_error_message (context, ret, N_("close %s: %s", ""),
+ FILENAME(id), strerror(ret));
+ }
+ return ret;
+}
+
+static krb5_error_code
+fcc_close(krb5_context context,
+ krb5_ccache id)
+{
+ free (FILENAME(id));
+ krb5_data_free(&id->data);
+ return 0;
+}
+
+static krb5_error_code
+fcc_destroy(krb5_context context,
+ krb5_ccache id)
+{
+ erase_file(context, FILENAME(id));
+ return 0;
+}
+
+static krb5_error_code
+fcc_store_cred(krb5_context context,
+ krb5_ccache id,
+ krb5_creds *creds)
+{
+ int ret;
+ int fd;
+
+ ret = fcc_open(context, id, &fd, O_WRONLY | O_APPEND | O_BINARY | O_CLOEXEC, 0);
+ if(ret)
+ return ret;
+ {
+ krb5_storage *sp;
+
+ sp = krb5_storage_emem();
+ krb5_storage_set_eof_code(sp, KRB5_CC_END);
+ storage_set_flags(context, sp, FCACHE(id)->version);
+ if (!krb5_config_get_bool_default(context, NULL, TRUE,
+ "libdefaults",
+ "fcc-mit-ticketflags",
+ NULL))
+ krb5_storage_set_flags(sp, KRB5_STORAGE_CREDS_FLAGS_WRONG_BITORDER);
+ ret = krb5_store_creds(sp, creds);
+ if (ret == 0)
+ ret = write_storage(context, sp, fd);
+ krb5_storage_free(sp);
+ }
+ fcc_unlock(context, fd);
+ if (close(fd) < 0) {
+ if (ret == 0) {
+ ret = errno;
+ krb5_set_error_message (context, ret, N_("close %s: %s", ""),
+ FILENAME(id), strerror(ret));
+ }
+ }
+ return ret;
+}
+
+static krb5_error_code
+init_fcc (krb5_context context,
+ krb5_ccache id,
+ krb5_storage **ret_sp,
+ int *ret_fd)
+{
+ int fd;
+ int8_t pvno, tag;
+ krb5_storage *sp;
+ krb5_error_code ret;
+
+ ret = fcc_open(context, id, &fd, O_RDONLY | O_BINARY | O_CLOEXEC, 0);
+ if(ret)
+ return ret;
+
+ sp = krb5_storage_from_fd(fd);
+ if(sp == NULL) {
+ krb5_clear_error_message(context);
+ ret = ENOMEM;
+ goto out;
+ }
+ krb5_storage_set_eof_code(sp, KRB5_CC_END);
+ ret = krb5_ret_int8(sp, &pvno);
+ if(ret != 0) {
+ if(ret == KRB5_CC_END) {
+ ret = ENOENT;
+ krb5_set_error_message(context, ret,
+ N_("Empty credential cache file: %s", ""),
+ FILENAME(id));
+ } else
+ krb5_set_error_message(context, ret, N_("Error reading pvno "
+ "in cache file: %s", ""),
+ FILENAME(id));
+ goto out;
+ }
+ if(pvno != 5) {
+ ret = KRB5_CCACHE_BADVNO;
+ krb5_set_error_message(context, ret, N_("Bad version number in credential "
+ "cache file: %s", ""),
+ FILENAME(id));
+ goto out;
+ }
+ ret = krb5_ret_int8(sp, &tag); /* should not be host byte order */
+ if(ret != 0) {
+ ret = KRB5_CC_FORMAT;
+ krb5_set_error_message(context, ret, "Error reading tag in "
+ "cache file: %s", FILENAME(id));
+ goto out;
+ }
+ FCACHE(id)->version = tag;
+ storage_set_flags(context, sp, FCACHE(id)->version);
+ switch (tag) {
+ case KRB5_FCC_FVNO_4: {
+ int16_t length;
+
+ ret = krb5_ret_int16 (sp, &length);
+ if(ret) {
+ ret = KRB5_CC_FORMAT;
+ krb5_set_error_message(context, ret,
+ N_("Error reading tag length in "
+ "cache file: %s", ""), FILENAME(id));
+ goto out;
+ }
+ while(length > 0) {
+ int16_t dtag, data_len;
+ int i;
+ int8_t dummy;
+
+ ret = krb5_ret_int16 (sp, &dtag);
+ if(ret) {
+ ret = KRB5_CC_FORMAT;
+ krb5_set_error_message(context, ret, N_("Error reading dtag in "
+ "cache file: %s", ""),
+ FILENAME(id));
+ goto out;
+ }
+ ret = krb5_ret_int16 (sp, &data_len);
+ if(ret) {
+ ret = KRB5_CC_FORMAT;
+ krb5_set_error_message(context, ret,
+ N_("Error reading dlength "
+ "in cache file: %s",""),
+ FILENAME(id));
+ goto out;
+ }
+ switch (dtag) {
+ case FCC_TAG_DELTATIME :
+ ret = krb5_ret_int32 (sp, &context->kdc_sec_offset);
+ if(ret) {
+ ret = KRB5_CC_FORMAT;
+ krb5_set_error_message(context, ret,
+ N_("Error reading kdc_sec in "
+ "cache file: %s", ""),
+ FILENAME(id));
+ goto out;
+ }
+ ret = krb5_ret_int32 (sp, &context->kdc_usec_offset);
+ if(ret) {
+ ret = KRB5_CC_FORMAT;
+ krb5_set_error_message(context, ret,
+ N_("Error reading kdc_usec in "
+ "cache file: %s", ""),
+ FILENAME(id));
+ goto out;
+ }
+ break;
+ default :
+ for (i = 0; i < data_len; ++i) {
+ ret = krb5_ret_int8 (sp, &dummy);
+ if(ret) {
+ ret = KRB5_CC_FORMAT;
+ krb5_set_error_message(context, ret,
+ N_("Error reading unknown "
+ "tag in cache file: %s", ""),
+ FILENAME(id));
+ goto out;
+ }
+ }
+ break;
+ }
+ length -= 4 + data_len;
+ }
+ break;
+ }
+ case KRB5_FCC_FVNO_3:
+ case KRB5_FCC_FVNO_2:
+ case KRB5_FCC_FVNO_1:
+ break;
+ default :
+ ret = KRB5_CCACHE_BADVNO;
+ krb5_set_error_message(context, ret,
+ N_("Unknown version number (%d) in "
+ "credential cache file: %s", ""),
+ (int)tag, FILENAME(id));
+ goto out;
+ }
+ *ret_sp = sp;
+ *ret_fd = fd;
+
+ return 0;
+ out:
+ if(sp != NULL)
+ krb5_storage_free(sp);
+ fcc_unlock(context, fd);
+ close(fd);
+ return ret;
+}
+
+static krb5_error_code
+fcc_get_principal(krb5_context context,
+ krb5_ccache id,
+ krb5_principal *principal)
+{
+ krb5_error_code ret;
+ int fd;
+ krb5_storage *sp;
+
+ ret = init_fcc (context, id, &sp, &fd);
+ if (ret)
+ return ret;
+ ret = krb5_ret_principal(sp, principal);
+ if (ret)
+ krb5_clear_error_message(context);
+ krb5_storage_free(sp);
+ fcc_unlock(context, fd);
+ close(fd);
+ return ret;
+}
+
+static krb5_error_code
+fcc_end_get (krb5_context context,
+ krb5_ccache id,
+ krb5_cc_cursor *cursor);
+
+static krb5_error_code
+fcc_get_first (krb5_context context,
+ krb5_ccache id,
+ krb5_cc_cursor *cursor)
+{
+ krb5_error_code ret;
+ krb5_principal principal;
+
+ *cursor = malloc(sizeof(struct fcc_cursor));
+ if (*cursor == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ memset(*cursor, 0, sizeof(struct fcc_cursor));
+
+ ret = init_fcc (context, id, &FCC_CURSOR(*cursor)->sp,
+ &FCC_CURSOR(*cursor)->fd);
+ if (ret) {
+ free(*cursor);
+ *cursor = NULL;
+ return ret;
+ }
+ ret = krb5_ret_principal (FCC_CURSOR(*cursor)->sp, &principal);
+ if(ret) {
+ krb5_clear_error_message(context);
+ fcc_end_get(context, id, cursor);
+ return ret;
+ }
+ krb5_free_principal (context, principal);
+ fcc_unlock(context, FCC_CURSOR(*cursor)->fd);
+ return 0;
+}
+
+static krb5_error_code
+fcc_get_next (krb5_context context,
+ krb5_ccache id,
+ krb5_cc_cursor *cursor,
+ krb5_creds *creds)
+{
+ krb5_error_code ret;
+ if((ret = fcc_lock(context, id, FCC_CURSOR(*cursor)->fd, FALSE)) != 0)
+ return ret;
+
+ ret = krb5_ret_creds(FCC_CURSOR(*cursor)->sp, creds);
+ if (ret)
+ krb5_clear_error_message(context);
+
+ fcc_unlock(context, FCC_CURSOR(*cursor)->fd);
+ return ret;
+}
+
+static krb5_error_code
+fcc_end_get (krb5_context context,
+ krb5_ccache id,
+ krb5_cc_cursor *cursor)
+{
+ krb5_storage_free(FCC_CURSOR(*cursor)->sp);
+ close (FCC_CURSOR(*cursor)->fd);
+ free(*cursor);
+ *cursor = NULL;
+ return 0;
+}
+
+static krb5_error_code
+fcc_remove_cred(krb5_context context,
+ krb5_ccache id,
+ krb5_flags which,
+ krb5_creds *cred)
+{
+ krb5_error_code ret;
+ krb5_ccache copy, newfile;
+
+ ret = krb5_cc_gen_new(context, &krb5_mcc_ops, &copy);
+ if (ret)
+ return ret;
+
+ ret = krb5_cc_copy_cache(context, id, copy);
+ if (ret) {
+ krb5_cc_destroy(context, copy);
+ return ret;
+ }
+
+ ret = krb5_cc_remove_cred(context, copy, which, cred);
+ if (ret) {
+ krb5_cc_destroy(context, copy);
+ return ret;
+ }
+
+ ret = krb5_cc_gen_new(context, &krb5_fcc_ops, &newfile);
+ if (ret) {
+ krb5_cc_destroy(context, copy);
+ return ret;
+ }
+
+ ret = krb5_cc_copy_cache(context, copy, newfile);
+ krb5_cc_destroy(context, copy);
+ if (ret) {
+ krb5_cc_destroy(context, newfile);
+ return ret;
+ }
+
+ return krb5_cc_move(context, newfile, id);
+}
+
+static krb5_error_code
+fcc_set_flags(krb5_context context,
+ krb5_ccache id,
+ krb5_flags flags)
+{
+ return 0; /* XXX */
+}
+
+static int
+fcc_get_version(krb5_context context,
+ krb5_ccache id)
+{
+ return FCACHE(id)->version;
+}
+
+struct fcache_iter {
+ int first;
+};
+
+static krb5_error_code
+fcc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
+{
+ struct fcache_iter *iter;
+
+ iter = calloc(1, sizeof(*iter));
+ if (iter == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ iter->first = 1;
+ *cursor = iter;
+ return 0;
+}
+
+static krb5_error_code
+fcc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
+{
+ struct fcache_iter *iter = cursor;
+ krb5_error_code ret;
+ const char *fn;
+ char *expandedfn = NULL;
+
+ if (!iter->first) {
+ krb5_clear_error_message(context);
+ return KRB5_CC_END;
+ }
+ iter->first = 0;
+
+ fn = krb5_cc_default_name(context);
+ if (strncasecmp(fn, "FILE:", 5) != 0) {
+ ret = _krb5_expand_default_cc_name(context,
+ KRB5_DEFAULT_CCNAME_FILE,
+ &expandedfn);
+ if (ret)
+ return ret;
+ }
+ ret = krb5_cc_resolve(context, fn, id);
+ if (expandedfn)
+ free(expandedfn);
+
+ return ret;
+}
+
+static krb5_error_code
+fcc_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
+{
+ struct fcache_iter *iter = cursor;
+ free(iter);
+ return 0;
+}
+
+static krb5_error_code
+fcc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
+{
+ krb5_error_code ret = 0;
+
+ ret = rename(FILENAME(from), FILENAME(to));
+ if (ret && errno != EXDEV) {
+ ret = errno;
+ krb5_set_error_message(context, ret,
+ N_("Rename of file from %s "
+ "to %s failed: %s", ""),
+ FILENAME(from), FILENAME(to),
+ strerror(ret));
+ return ret;
+ } else if (ret && errno == EXDEV) {
+ /* make a copy and delete the orignal */
+ krb5_ssize_t sz1, sz2;
+ int fd1, fd2;
+ char buf[BUFSIZ];
+
+ ret = fcc_open(context, from, &fd1, O_RDONLY | O_BINARY | O_CLOEXEC, 0);
+ if(ret)
+ return ret;
+
+ unlink(FILENAME(to));
+
+ ret = fcc_open(context, to, &fd2,
+ O_WRONLY | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC, 0600);
+ if(ret)
+ goto out1;
+
+ while((sz1 = read(fd1, buf, sizeof(buf))) > 0) {
+ sz2 = write(fd2, buf, sz1);
+ if (sz1 != sz2) {
+ ret = EIO;
+ krb5_set_error_message(context, ret,
+ N_("Failed to write data from one file "
+ "credential cache to the other", ""));
+ goto out2;
+ }
+ }
+ if (sz1 < 0) {
+ ret = EIO;
+ krb5_set_error_message(context, ret,
+ N_("Failed to read data from one file "
+ "credential cache to the other", ""));
+ goto out2;
+ }
+ out2:
+ fcc_unlock(context, fd2);
+ close(fd2);
+
+ out1:
+ fcc_unlock(context, fd1);
+ close(fd1);
+
+ erase_file(context, FILENAME(from));
+
+ if (ret) {
+ erase_file(context, FILENAME(to));
+ return ret;
+ }
+ }
+
+ /* make sure ->version is uptodate */
+ {
+ krb5_storage *sp;
+ int fd;
+ ret = init_fcc (context, to, &sp, &fd);
+ krb5_storage_free(sp);
+ fcc_unlock(context, fd);
+ close(fd);
+ }
+ return ret;
+}
+
+static krb5_error_code
+fcc_get_default_name(krb5_context context, char **str)
+{
+ return _krb5_expand_default_cc_name(context,
+ KRB5_DEFAULT_CCNAME_FILE,
+ str);
+}
+
+static krb5_error_code
+fcc_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime)
+{
+ krb5_error_code ret;
+ struct stat sb;
+ int fd;
+
+ ret = fcc_open(context, id, &fd, O_RDONLY | O_BINARY | O_CLOEXEC, 0);
+ if(ret)
+ return ret;
+ ret = fstat(fd, &sb);
+ close(fd);
+ if (ret) {
+ ret = errno;
+ krb5_set_error_message(context, ret, N_("Failed to stat cache file", ""));
+ return ret;
+ }
+ *mtime = sb.st_mtime;
+ return 0;
+}
+
+/**
+ * Variable containing the FILE based credential cache implemention.
+ *
+ * @ingroup krb5_ccache
+ */
+
+KRB5_LIB_VARIABLE const krb5_cc_ops krb5_fcc_ops = {
+ KRB5_CC_OPS_VERSION,
+ "FILE",
+ fcc_get_name,
+ fcc_resolve,
+ fcc_gen_new,
+ fcc_initialize,
+ fcc_destroy,
+ fcc_close,
+ fcc_store_cred,
+ NULL, /* fcc_retrieve */
+ fcc_get_principal,
+ fcc_get_first,
+ fcc_get_next,
+ fcc_end_get,
+ fcc_remove_cred,
+ fcc_set_flags,
+ fcc_get_version,
+ fcc_get_cache_first,
+ fcc_get_cache_next,
+ fcc_end_cache_get,
+ fcc_move,
+ fcc_get_default_name,
+ NULL,
+ fcc_lastchange
+};
diff --git a/source4/heimdal/lib/krb5/free.c b/source4/heimdal/lib/krb5/free.c
new file mode 100644
index 0000000000..da1eb1de1c
--- /dev/null
+++ b/source4/heimdal/lib/krb5/free.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 1997 - 1999, 2004 - 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_free_kdc_rep(krb5_context context, krb5_kdc_rep *rep)
+{
+ free_KDC_REP(&rep->kdc_rep);
+ free_EncTGSRepPart(&rep->enc_part);
+ free_KRB_ERROR(&rep->error);
+ memset(rep, 0, sizeof(*rep));
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_xfree (void *ptr)
+{
+ free (ptr);
+ return 0;
+}
diff --git a/source4/heimdal/lib/krb5/free_host_realm.c b/source4/heimdal/lib/krb5/free_host_realm.c
new file mode 100644
index 0000000000..581b61a15b
--- /dev/null
+++ b/source4/heimdal/lib/krb5/free_host_realm.c
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 1997, 1999 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+/*
+ * Free all memory allocated by `realmlist'
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_free_host_realm(krb5_context context,
+ krb5_realm *realmlist)
+{
+ krb5_realm *p;
+
+ if(realmlist == NULL)
+ return 0;
+ for (p = realmlist; *p; ++p)
+ free (*p);
+ free (realmlist);
+ return 0;
+}
diff --git a/source4/heimdal/lib/krb5/generate_seq_number.c b/source4/heimdal/lib/krb5/generate_seq_number.c
new file mode 100644
index 0000000000..99745b8305
--- /dev/null
+++ b/source4/heimdal/lib/krb5/generate_seq_number.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <krb5_locl.h>
+
+RCSID("$Id$");
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_generate_seq_number(krb5_context context,
+ const krb5_keyblock *key,
+ uint32_t *seqno)
+{
+ krb5_error_code ret;
+ krb5_keyblock *subkey;
+ uint32_t q;
+ u_char *p;
+ int i;
+
+ ret = krb5_generate_subkey (context, key, &subkey);
+ if (ret)
+ return ret;
+
+ q = 0;
+ for (p = (u_char *)subkey->keyvalue.data, i = 0;
+ i < subkey->keyvalue.length;
+ ++i, ++p)
+ q = (q << 8) | *p;
+ q &= 0xffffffff;
+ *seqno = q;
+ krb5_free_keyblock (context, subkey);
+ return 0;
+}
diff --git a/source4/heimdal/lib/krb5/generate_subkey.c b/source4/heimdal/lib/krb5/generate_subkey.c
new file mode 100644
index 0000000000..4ab4b9bf6c
--- /dev/null
+++ b/source4/heimdal/lib/krb5/generate_subkey.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <krb5_locl.h>
+
+RCSID("$Id$");
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_generate_subkey(krb5_context context,
+ const krb5_keyblock *key,
+ krb5_keyblock **subkey)
+{
+ return krb5_generate_subkey_extended(context, key, key->keytype, subkey);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_generate_subkey_extended(krb5_context context,
+ const krb5_keyblock *key,
+ krb5_enctype etype,
+ krb5_keyblock **subkey)
+{
+ krb5_error_code ret;
+
+ ALLOC(*subkey, 1);
+ if (*subkey == NULL) {
+ krb5_set_error_message(context, ENOMEM,N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+
+ if (etype == ETYPE_NULL)
+ etype = key->keytype; /* use session key etype */
+
+ /* XXX should we use the session key as input to the RF? */
+ ret = krb5_generate_random_keyblock(context, etype, *subkey);
+ if (ret != 0) {
+ free(*subkey);
+ *subkey = NULL;
+ }
+
+ return ret;
+}
+
diff --git a/source4/heimdal/lib/krb5/get_addrs.c b/source4/heimdal/lib/krb5/get_addrs.c
new file mode 100644
index 0000000000..ce16785319
--- /dev/null
+++ b/source4/heimdal/lib/krb5/get_addrs.c
@@ -0,0 +1,294 @@
+/*
+ * Copyright (c) 1997 - 2002 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id: get_addrs.c 23815 2008-09-13 09:21:03Z lha $");
+
+#ifdef __osf__
+/* hate */
+struct rtentry;
+struct mbuf;
+#endif
+#ifdef HAVE_NET_IF_H
+#include <net/if.h>
+#endif
+#ifdef HAVE_IFADDR_H
+#include <ifaddrs.h>
+#endif
+
+static krb5_error_code
+gethostname_fallback (krb5_context context, krb5_addresses *res)
+{
+ krb5_error_code ret;
+ char hostname[MAXHOSTNAMELEN];
+ struct hostent *hostent;
+
+ if (gethostname (hostname, sizeof(hostname))) {
+ ret = errno;
+ krb5_set_error_message(context, ret, "gethostname: %s", strerror(ret));
+ return ret;
+ }
+ hostent = roken_gethostbyname (hostname);
+ if (hostent == NULL) {
+ ret = errno;
+ krb5_set_error_message (context, ret, "gethostbyname %s: %s",
+ hostname, strerror(ret));
+ return ret;
+ }
+ res->len = 1;
+ res->val = malloc (sizeof(*res->val));
+ if (res->val == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ res->val[0].addr_type = hostent->h_addrtype;
+ res->val[0].address.data = NULL;
+ res->val[0].address.length = 0;
+ ret = krb5_data_copy (&res->val[0].address,
+ hostent->h_addr,
+ hostent->h_length);
+ if (ret) {
+ free (res->val);
+ return ret;
+ }
+ return 0;
+}
+
+enum {
+ LOOP = 1, /* do include loopback interfaces */
+ LOOP_IF_NONE = 2, /* include loopback if no other if's */
+ EXTRA_ADDRESSES = 4, /* include extra addresses */
+ SCAN_INTERFACES = 8 /* scan interfaces for addresses */
+};
+
+/*
+ * Try to figure out the addresses of all configured interfaces with a
+ * lot of magic ioctls.
+ */
+
+static krb5_error_code
+find_all_addresses (krb5_context context, krb5_addresses *res, int flags)
+{
+ struct sockaddr sa_zero;
+ struct ifaddrs *ifa0, *ifa;
+ krb5_error_code ret = ENXIO;
+ unsigned int num, idx;
+ krb5_addresses ignore_addresses;
+
+ res->val = NULL;
+
+ if (getifaddrs(&ifa0) == -1) {
+ ret = errno;
+ krb5_set_error_message(context, ret, "getifaddrs: %s", strerror(ret));
+ return (ret);
+ }
+
+ memset(&sa_zero, 0, sizeof(sa_zero));
+
+ /* First, count all the ifaddrs. */
+ for (ifa = ifa0, num = 0; ifa != NULL; ifa = ifa->ifa_next, num++)
+ /* nothing */;
+
+ if (num == 0) {
+ freeifaddrs(ifa0);
+ krb5_set_error_message(context, ENXIO, N_("no addresses found", ""));
+ return (ENXIO);
+ }
+
+ if (flags & EXTRA_ADDRESSES) {
+ /* we'll remove the addresses we don't care about */
+ ret = krb5_get_ignore_addresses(context, &ignore_addresses);
+ if(ret)
+ return ret;
+ }
+
+ /* Allocate storage for them. */
+ res->val = calloc(num, sizeof(*res->val));
+ if (res->val == NULL) {
+ krb5_free_addresses(context, &ignore_addresses);
+ freeifaddrs(ifa0);
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+
+ /* Now traverse the list. */
+ for (ifa = ifa0, idx = 0; ifa != NULL; ifa = ifa->ifa_next) {
+ if ((ifa->ifa_flags & IFF_UP) == 0)
+ continue;
+ if (ifa->ifa_addr == NULL)
+ continue;
+ if (memcmp(ifa->ifa_addr, &sa_zero, sizeof(sa_zero)) == 0)
+ continue;
+ if (krb5_sockaddr_uninteresting(ifa->ifa_addr))
+ continue;
+ if ((ifa->ifa_flags & IFF_LOOPBACK) != 0) {
+ /* We'll deal with the LOOP_IF_NONE case later. */
+ if ((flags & LOOP) == 0)
+ continue;
+ }
+
+ ret = krb5_sockaddr2address(context, ifa->ifa_addr, &res->val[idx]);
+ if (ret) {
+ /*
+ * The most likely error here is going to be "Program
+ * lacks support for address type". This is no big
+ * deal -- just continue, and we'll listen on the
+ * addresses who's type we *do* support.
+ */
+ continue;
+ }
+ /* possibly skip this address? */
+ if((flags & EXTRA_ADDRESSES) &&
+ krb5_address_search(context, &res->val[idx], &ignore_addresses)) {
+ krb5_free_address(context, &res->val[idx]);
+ flags &= ~LOOP_IF_NONE; /* we actually found an address,
+ so don't add any loop-back
+ addresses */
+ continue;
+ }
+
+ idx++;
+ }
+
+ /*
+ * If no addresses were found, and LOOP_IF_NONE is set, then find
+ * the loopback addresses and add them to our list.
+ */
+ if ((flags & LOOP_IF_NONE) != 0 && idx == 0) {
+ for (ifa = ifa0; ifa != NULL; ifa = ifa->ifa_next) {
+ if ((ifa->ifa_flags & IFF_UP) == 0)
+ continue;
+ if (ifa->ifa_addr == NULL)
+ continue;
+ if (memcmp(ifa->ifa_addr, &sa_zero, sizeof(sa_zero)) == 0)
+ continue;
+ if (krb5_sockaddr_uninteresting(ifa->ifa_addr))
+ continue;
+
+ if ((ifa->ifa_flags & IFF_LOOPBACK) != 0) {
+ ret = krb5_sockaddr2address(context,
+ ifa->ifa_addr, &res->val[idx]);
+ if (ret) {
+ /*
+ * See comment above.
+ */
+ continue;
+ }
+ if((flags & EXTRA_ADDRESSES) &&
+ krb5_address_search(context, &res->val[idx],
+ &ignore_addresses)) {
+ krb5_free_address(context, &res->val[idx]);
+ continue;
+ }
+ idx++;
+ }
+ }
+ }
+
+ if (flags & EXTRA_ADDRESSES)
+ krb5_free_addresses(context, &ignore_addresses);
+ freeifaddrs(ifa0);
+ if (ret) {
+ free(res->val);
+ res->val = NULL;
+ } else
+ res->len = idx; /* Now a count. */
+ return (ret);
+}
+
+static krb5_error_code
+get_addrs_int (krb5_context context, krb5_addresses *res, int flags)
+{
+ krb5_error_code ret = -1;
+
+ if (flags & SCAN_INTERFACES) {
+ ret = find_all_addresses (context, res, flags);
+ if(ret || res->len == 0)
+ ret = gethostname_fallback (context, res);
+ } else {
+ res->len = 0;
+ res->val = NULL;
+ ret = 0;
+ }
+
+ if(ret == 0 && (flags & EXTRA_ADDRESSES)) {
+ krb5_addresses a;
+ /* append user specified addresses */
+ ret = krb5_get_extra_addresses(context, &a);
+ if(ret) {
+ krb5_free_addresses(context, res);
+ return ret;
+ }
+ ret = krb5_append_addresses(context, res, &a);
+ if(ret) {
+ krb5_free_addresses(context, res);
+ return ret;
+ }
+ krb5_free_addresses(context, &a);
+ }
+ if(res->len == 0) {
+ free(res->val);
+ res->val = NULL;
+ }
+ return ret;
+}
+
+/*
+ * Try to get all addresses, but return the one corresponding to
+ * `hostname' if we fail.
+ *
+ * Only include loopback address if there are no other.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_all_client_addrs (krb5_context context, krb5_addresses *res)
+{
+ int flags = LOOP_IF_NONE | EXTRA_ADDRESSES;
+
+ if (context->scan_interfaces)
+ flags |= SCAN_INTERFACES;
+
+ return get_addrs_int (context, res, flags);
+}
+
+/*
+ * Try to get all local addresses that a server should listen to.
+ * If that fails, we return the address corresponding to `hostname'.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_all_server_addrs (krb5_context context, krb5_addresses *res)
+{
+ return get_addrs_int (context, res, LOOP | SCAN_INTERFACES);
+}
diff --git a/source4/heimdal/lib/krb5/get_cred.c b/source4/heimdal/lib/krb5/get_cred.c
new file mode 100644
index 0000000000..97e0022ee1
--- /dev/null
+++ b/source4/heimdal/lib/krb5/get_cred.c
@@ -0,0 +1,1464 @@
+/*
+ * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <krb5_locl.h>
+
+RCSID("$Id$");
+
+/*
+ * Take the `body' and encode it into `padata' using the credentials
+ * in `creds'.
+ */
+
+static krb5_error_code
+make_pa_tgs_req(krb5_context context,
+ krb5_auth_context ac,
+ KDC_REQ_BODY *body,
+ PA_DATA *padata,
+ krb5_creds *creds)
+{
+ u_char *buf;
+ size_t buf_size;
+ size_t len;
+ krb5_data in_data;
+ krb5_error_code ret;
+
+ ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, body, &len, ret);
+ if (ret)
+ goto out;
+ if(buf_size != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+
+ in_data.length = len;
+ in_data.data = buf;
+ ret = _krb5_mk_req_internal(context, &ac, 0, &in_data, creds,
+ &padata->padata_value,
+ KRB5_KU_TGS_REQ_AUTH_CKSUM,
+ KRB5_KU_TGS_REQ_AUTH);
+ out:
+ free (buf);
+ if(ret)
+ return ret;
+ padata->padata_type = KRB5_PADATA_TGS_REQ;
+ return 0;
+}
+
+/*
+ * Set the `enc-authorization-data' in `req_body' based on `authdata'
+ */
+
+static krb5_error_code
+set_auth_data (krb5_context context,
+ KDC_REQ_BODY *req_body,
+ krb5_authdata *authdata,
+ krb5_keyblock *key)
+{
+ if(authdata->len) {
+ size_t len, buf_size;
+ unsigned char *buf;
+ krb5_crypto crypto;
+ krb5_error_code ret;
+
+ ASN1_MALLOC_ENCODE(AuthorizationData, buf, buf_size, authdata,
+ &len, ret);
+ if (ret)
+ return ret;
+ if (buf_size != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+
+ ALLOC(req_body->enc_authorization_data, 1);
+ if (req_body->enc_authorization_data == NULL) {
+ free (buf);
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret) {
+ free (buf);
+ free (req_body->enc_authorization_data);
+ req_body->enc_authorization_data = NULL;
+ return ret;
+ }
+ krb5_encrypt_EncryptedData(context,
+ crypto,
+ KRB5_KU_TGS_REQ_AUTH_DAT_SUBKEY,
+ /* KRB5_KU_TGS_REQ_AUTH_DAT_SESSION? */
+ buf,
+ len,
+ 0,
+ req_body->enc_authorization_data);
+ free (buf);
+ krb5_crypto_destroy(context, crypto);
+ } else {
+ req_body->enc_authorization_data = NULL;
+ }
+ return 0;
+}
+
+/*
+ * Create a tgs-req in `t' with `addresses', `flags', `second_ticket'
+ * (if not-NULL), `in_creds', `krbtgt', and returning the generated
+ * subkey in `subkey'.
+ */
+
+static krb5_error_code
+init_tgs_req (krb5_context context,
+ krb5_ccache ccache,
+ krb5_addresses *addresses,
+ krb5_kdc_flags flags,
+ Ticket *second_ticket,
+ krb5_creds *in_creds,
+ krb5_creds *krbtgt,
+ unsigned nonce,
+ const METHOD_DATA *padata,
+ krb5_keyblock **subkey,
+ TGS_REQ *t)
+{
+ krb5_error_code ret = 0;
+
+ memset(t, 0, sizeof(*t));
+ t->pvno = 5;
+ t->msg_type = krb_tgs_req;
+ if (in_creds->session.keytype) {
+ ALLOC_SEQ(&t->req_body.etype, 1);
+ if(t->req_body.etype.val == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret,
+ N_("malloc: out of memory", ""));
+ goto fail;
+ }
+ t->req_body.etype.val[0] = in_creds->session.keytype;
+ } else {
+ ret = krb5_init_etype(context,
+ &t->req_body.etype.len,
+ &t->req_body.etype.val,
+ NULL);
+ }
+ if (ret)
+ goto fail;
+ t->req_body.addresses = addresses;
+ t->req_body.kdc_options = flags.b;
+ ret = copy_Realm(&in_creds->server->realm, &t->req_body.realm);
+ if (ret)
+ goto fail;
+ ALLOC(t->req_body.sname, 1);
+ if (t->req_body.sname == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ goto fail;
+ }
+
+ /* some versions of some code might require that the client be
+ present in TGS-REQs, but this is clearly against the spec */
+
+ ret = copy_PrincipalName(&in_creds->server->name, t->req_body.sname);
+ if (ret)
+ goto fail;
+
+ /* req_body.till should be NULL if there is no endtime specified,
+ but old MIT code (like DCE secd) doesn't like that */
+ ALLOC(t->req_body.till, 1);
+ if(t->req_body.till == NULL){
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ goto fail;
+ }
+ *t->req_body.till = in_creds->times.endtime;
+
+ t->req_body.nonce = nonce;
+ if(second_ticket){
+ ALLOC(t->req_body.additional_tickets, 1);
+ if (t->req_body.additional_tickets == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret,
+ N_("malloc: out of memory", ""));
+ goto fail;
+ }
+ ALLOC_SEQ(t->req_body.additional_tickets, 1);
+ if (t->req_body.additional_tickets->val == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret,
+ N_("malloc: out of memory", ""));
+ goto fail;
+ }
+ ret = copy_Ticket(second_ticket, t->req_body.additional_tickets->val);
+ if (ret)
+ goto fail;
+ }
+ ALLOC(t->padata, 1);
+ if (t->padata == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ goto fail;
+ }
+ ALLOC_SEQ(t->padata, 1 + padata->len);
+ if (t->padata->val == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ goto fail;
+ }
+ {
+ int i;
+ for (i = 0; i < padata->len; i++) {
+ ret = copy_PA_DATA(&padata->val[i], &t->padata->val[i + 1]);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("malloc: out of memory", ""));
+ goto fail;
+ }
+ }
+ }
+
+ {
+ krb5_auth_context ac;
+ krb5_keyblock *key = NULL;
+
+ ret = krb5_auth_con_init(context, &ac);
+ if(ret)
+ goto fail;
+
+ if (krb5_config_get_bool_default(context, NULL, FALSE,
+ "realms",
+ krbtgt->server->realm,
+ "tgs_require_subkey",
+ NULL))
+ {
+ ret = krb5_generate_subkey (context, &krbtgt->session, &key);
+ if (ret) {
+ krb5_auth_con_free (context, ac);
+ goto fail;
+ }
+
+ ret = krb5_auth_con_setlocalsubkey(context, ac, key);
+ if (ret) {
+ if (key)
+ krb5_free_keyblock (context, key);
+ krb5_auth_con_free (context, ac);
+ goto fail;
+ }
+ }
+
+ ret = set_auth_data (context, &t->req_body, &in_creds->authdata,
+ key ? key : &krbtgt->session);
+ if (ret) {
+ if (key)
+ krb5_free_keyblock (context, key);
+ krb5_auth_con_free (context, ac);
+ goto fail;
+ }
+
+ ret = make_pa_tgs_req(context,
+ ac,
+ &t->req_body,
+ &t->padata->val[0],
+ krbtgt);
+ if(ret) {
+ if (key)
+ krb5_free_keyblock (context, key);
+ krb5_auth_con_free(context, ac);
+ goto fail;
+ }
+ *subkey = key;
+
+ krb5_auth_con_free(context, ac);
+ }
+fail:
+ if (ret) {
+ t->req_body.addresses = NULL;
+ free_TGS_REQ (t);
+ }
+ return ret;
+}
+
+krb5_error_code
+_krb5_get_krbtgt(krb5_context context,
+ krb5_ccache id,
+ krb5_realm realm,
+ krb5_creds **cred)
+{
+ krb5_error_code ret;
+ krb5_creds tmp_cred;
+
+ memset(&tmp_cred, 0, sizeof(tmp_cred));
+
+ ret = krb5_cc_get_principal(context, id, &tmp_cred.client);
+ if (ret)
+ return ret;
+
+ ret = krb5_make_principal(context,
+ &tmp_cred.server,
+ realm,
+ KRB5_TGS_NAME,
+ realm,
+ NULL);
+ if(ret) {
+ krb5_free_principal(context, tmp_cred.client);
+ return ret;
+ }
+ ret = krb5_get_credentials(context,
+ KRB5_GC_CACHED,
+ id,
+ &tmp_cred,
+ cred);
+ krb5_free_principal(context, tmp_cred.client);
+ krb5_free_principal(context, tmp_cred.server);
+ if(ret)
+ return ret;
+ return 0;
+}
+
+/* DCE compatible decrypt proc */
+static krb5_error_code
+decrypt_tkt_with_subkey (krb5_context context,
+ krb5_keyblock *key,
+ krb5_key_usage usage,
+ krb5_const_pointer subkey,
+ krb5_kdc_rep *dec_rep)
+{
+ krb5_error_code ret;
+ krb5_data data;
+ size_t size;
+ krb5_crypto crypto;
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret)
+ return ret;
+ ret = krb5_decrypt_EncryptedData (context,
+ crypto,
+ usage,
+ &dec_rep->kdc_rep.enc_part,
+ &data);
+ krb5_crypto_destroy(context, crypto);
+ if(ret && subkey){
+ /* DCE compat -- try to decrypt with subkey */
+ ret = krb5_crypto_init(context, subkey, 0, &crypto);
+ if (ret)
+ return ret;
+ ret = krb5_decrypt_EncryptedData (context,
+ crypto,
+ KRB5_KU_TGS_REP_ENC_PART_SUB_KEY,
+ &dec_rep->kdc_rep.enc_part,
+ &data);
+ krb5_crypto_destroy(context, crypto);
+ }
+ if (ret)
+ return ret;
+
+ ret = krb5_decode_EncASRepPart(context,
+ data.data,
+ data.length,
+ &dec_rep->enc_part,
+ &size);
+ if (ret)
+ ret = krb5_decode_EncTGSRepPart(context,
+ data.data,
+ data.length,
+ &dec_rep->enc_part,
+ &size);
+ krb5_data_free (&data);
+ return ret;
+}
+
+static krb5_error_code
+get_cred_kdc(krb5_context context,
+ krb5_ccache id,
+ krb5_kdc_flags flags,
+ krb5_addresses *addresses,
+ krb5_creds *in_creds,
+ krb5_creds *krbtgt,
+ krb5_principal impersonate_principal,
+ Ticket *second_ticket,
+ krb5_creds *out_creds)
+{
+ TGS_REQ req;
+ krb5_data enc;
+ krb5_data resp;
+ krb5_kdc_rep rep;
+ KRB_ERROR error;
+ krb5_error_code ret;
+ unsigned nonce;
+ krb5_keyblock *subkey = NULL;
+ size_t len;
+ Ticket second_ticket_data;
+ METHOD_DATA padata;
+
+ krb5_data_zero(&resp);
+ krb5_data_zero(&enc);
+ padata.val = NULL;
+ padata.len = 0;
+
+ krb5_generate_random_block(&nonce, sizeof(nonce));
+ nonce &= 0xffffffff;
+
+ if(flags.b.enc_tkt_in_skey && second_ticket == NULL){
+ ret = decode_Ticket(in_creds->second_ticket.data,
+ in_creds->second_ticket.length,
+ &second_ticket_data, &len);
+ if(ret)
+ return ret;
+ second_ticket = &second_ticket_data;
+ }
+
+
+ if (impersonate_principal) {
+ krb5_crypto crypto;
+ PA_S4U2Self self;
+ krb5_data data;
+ void *buf;
+ size_t size;
+
+ self.name = impersonate_principal->name;
+ self.realm = impersonate_principal->realm;
+ self.auth = estrdup("Kerberos");
+
+ ret = _krb5_s4u2self_to_checksumdata(context, &self, &data);
+ if (ret) {
+ free(self.auth);
+ goto out;
+ }
+
+ ret = krb5_crypto_init(context, &krbtgt->session, 0, &crypto);
+ if (ret) {
+ free(self.auth);
+ krb5_data_free(&data);
+ goto out;
+ }
+
+ ret = krb5_create_checksum(context,
+ crypto,
+ KRB5_KU_OTHER_CKSUM,
+ 0,
+ data.data,
+ data.length,
+ &self.cksum);
+ krb5_crypto_destroy(context, crypto);
+ krb5_data_free(&data);
+ if (ret) {
+ free(self.auth);
+ goto out;
+ }
+
+ ASN1_MALLOC_ENCODE(PA_S4U2Self, buf, len, &self, &size, ret);
+ free(self.auth);
+ free_Checksum(&self.cksum);
+ if (ret)
+ goto out;
+ if (len != size)
+ krb5_abortx(context, "internal asn1 error");
+
+ ret = krb5_padata_add(context, &padata, KRB5_PADATA_S4U2SELF, buf, len);
+ if (ret)
+ goto out;
+ }
+
+ ret = init_tgs_req (context,
+ id,
+ addresses,
+ flags,
+ second_ticket,
+ in_creds,
+ krbtgt,
+ nonce,
+ &padata,
+ &subkey,
+ &req);
+ if (ret)
+ goto out;
+
+ ASN1_MALLOC_ENCODE(TGS_REQ, enc.data, enc.length, &req, &len, ret);
+ if (ret)
+ goto out;
+ if(enc.length != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+
+ /* don't free addresses */
+ req.req_body.addresses = NULL;
+ free_TGS_REQ(&req);
+
+ /*
+ * Send and receive
+ */
+ {
+ krb5_sendto_ctx stctx;
+ ret = krb5_sendto_ctx_alloc(context, &stctx);
+ if (ret)
+ return ret;
+ krb5_sendto_ctx_set_func(stctx, _krb5_kdc_retry, NULL);
+
+ ret = krb5_sendto_context (context, stctx, &enc,
+ krbtgt->server->name.name_string.val[1],
+ &resp);
+ krb5_sendto_ctx_free(context, stctx);
+ }
+ if(ret)
+ goto out;
+
+ memset(&rep, 0, sizeof(rep));
+ if(decode_TGS_REP(resp.data, resp.length, &rep.kdc_rep, &len) == 0) {
+ unsigned eflags = 0;
+
+ ret = krb5_copy_principal(context,
+ in_creds->client,
+ &out_creds->client);
+ if(ret)
+ goto out2;
+ ret = krb5_copy_principal(context,
+ in_creds->server,
+ &out_creds->server);
+ if(ret)
+ goto out2;
+ /* this should go someplace else */
+ out_creds->times.endtime = in_creds->times.endtime;
+
+ /* XXX should do better testing */
+ if (flags.b.constrained_delegation || impersonate_principal)
+ eflags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH;
+
+ ret = _krb5_extract_ticket(context,
+ &rep,
+ out_creds,
+ &krbtgt->session,
+ NULL,
+ KRB5_KU_TGS_REP_ENC_PART_SESSION,
+ &krbtgt->addresses,
+ nonce,
+ eflags,
+ decrypt_tkt_with_subkey,
+ subkey);
+ out2:
+ krb5_free_kdc_rep(context, &rep);
+ } else if(krb5_rd_error(context, &resp, &error) == 0) {
+ ret = krb5_error_from_rd_error(context, &error, in_creds);
+ krb5_free_error_contents(context, &error);
+ } else if(resp.data && ((char*)resp.data)[0] == 4) {
+ ret = KRB5KRB_AP_ERR_V4_REPLY;
+ krb5_clear_error_message(context);
+ } else {
+ ret = KRB5KRB_AP_ERR_MSG_TYPE;
+ krb5_clear_error_message(context);
+ }
+
+out:
+ if (second_ticket == &second_ticket_data)
+ free_Ticket(&second_ticket_data);
+ free_METHOD_DATA(&padata);
+ krb5_data_free(&resp);
+ krb5_data_free(&enc);
+ if(subkey){
+ krb5_free_keyblock_contents(context, subkey);
+ free(subkey);
+ }
+ return ret;
+
+}
+
+/*
+ * same as above, just get local addresses first if the krbtgt have
+ * them and the realm is not addressless
+ */
+
+static krb5_error_code
+get_cred_kdc_address(krb5_context context,
+ krb5_ccache id,
+ krb5_kdc_flags flags,
+ krb5_addresses *addrs,
+ krb5_creds *in_creds,
+ krb5_creds *krbtgt,
+ krb5_principal impersonate_principal,
+ Ticket *second_ticket,
+ krb5_creds *out_creds)
+{
+ krb5_error_code ret;
+ krb5_addresses addresses = { 0, NULL };
+
+ /*
+ * Inherit the address-ness of the krbtgt if the address is not
+ * specified.
+ */
+
+ if (addrs == NULL && krbtgt->addresses.len != 0) {
+ krb5_boolean noaddr;
+
+ krb5_appdefault_boolean(context, NULL, krbtgt->server->realm,
+ "no-addresses", FALSE, &noaddr);
+
+ if (!noaddr) {
+ krb5_get_all_client_addrs(context, &addresses);
+ /* XXX this sucks. */
+ addrs = &addresses;
+ if(addresses.len == 0)
+ addrs = NULL;
+ }
+ }
+ ret = get_cred_kdc(context, id, flags, addrs, in_creds,
+ krbtgt, impersonate_principal,
+ second_ticket, out_creds);
+ krb5_free_addresses(context, &addresses);
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_kdc_cred(krb5_context context,
+ krb5_ccache id,
+ krb5_kdc_flags flags,
+ krb5_addresses *addresses,
+ Ticket *second_ticket,
+ krb5_creds *in_creds,
+ krb5_creds **out_creds
+ )
+{
+ krb5_error_code ret;
+ krb5_creds *krbtgt;
+
+ *out_creds = calloc(1, sizeof(**out_creds));
+ if(*out_creds == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ ret = _krb5_get_krbtgt (context,
+ id,
+ in_creds->server->realm,
+ &krbtgt);
+ if(ret) {
+ free(*out_creds);
+ return ret;
+ }
+ ret = get_cred_kdc(context, id, flags, addresses,
+ in_creds, krbtgt, NULL, NULL, *out_creds);
+ krb5_free_creds (context, krbtgt);
+ if(ret)
+ free(*out_creds);
+ return ret;
+}
+
+static int
+not_found(krb5_context context, krb5_const_principal p, krb5_error_code code)
+{
+ krb5_error_code ret;
+ char *str;
+
+ ret = krb5_unparse_name(context, p, &str);
+ if(ret) {
+ krb5_clear_error_message(context);
+ return code;
+ }
+ krb5_set_error_message(context, code,
+ N_("Matching credential (%s) not found", ""), str);
+ free(str);
+ return code;
+}
+
+static krb5_error_code
+find_cred(krb5_context context,
+ krb5_ccache id,
+ krb5_principal server,
+ krb5_creds **tgts,
+ krb5_creds *out_creds)
+{
+ krb5_error_code ret;
+ krb5_creds mcreds;
+
+ krb5_cc_clear_mcred(&mcreds);
+ mcreds.server = server;
+ ret = krb5_cc_retrieve_cred(context, id, KRB5_TC_DONT_MATCH_REALM,
+ &mcreds, out_creds);
+ if(ret == 0)
+ return 0;
+ while(tgts && *tgts){
+ if(krb5_compare_creds(context, KRB5_TC_DONT_MATCH_REALM,
+ &mcreds, *tgts)){
+ ret = krb5_copy_creds_contents(context, *tgts, out_creds);
+ return ret;
+ }
+ tgts++;
+ }
+ return not_found(context, server, KRB5_CC_NOTFOUND);
+}
+
+static krb5_error_code
+add_cred(krb5_context context, krb5_creds const *tkt, krb5_creds ***tgts)
+{
+ int i;
+ krb5_error_code ret;
+ krb5_creds **tmp = *tgts;
+
+ for(i = 0; tmp && tmp[i]; i++); /* XXX */
+ tmp = realloc(tmp, (i+2)*sizeof(*tmp));
+ if(tmp == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ *tgts = tmp;
+ ret = krb5_copy_creds(context, tkt, &tmp[i]);
+ tmp[i+1] = NULL;
+ return ret;
+}
+
+/*
+get_cred(server)
+ creds = cc_get_cred(server)
+ if(creds) return creds
+ tgt = cc_get_cred(krbtgt/server_realm@any_realm)
+ if(tgt)
+ return get_cred_tgt(server, tgt)
+ if(client_realm == server_realm)
+ return NULL
+ tgt = get_cred(krbtgt/server_realm@client_realm)
+ while(tgt_inst != server_realm)
+ tgt = get_cred(krbtgt/server_realm@tgt_inst)
+ return get_cred_tgt(server, tgt)
+ */
+
+static krb5_error_code
+get_cred_kdc_capath(krb5_context context,
+ krb5_kdc_flags flags,
+ krb5_ccache ccache,
+ krb5_creds *in_creds,
+ krb5_principal impersonate_principal,
+ Ticket *second_ticket,
+ krb5_creds **out_creds,
+ krb5_creds ***ret_tgts)
+{
+ krb5_error_code ret;
+ krb5_creds *tgt, tmp_creds;
+ krb5_const_realm client_realm, server_realm, try_realm;
+ int ok_as_delegate = 1;
+
+ *out_creds = NULL;
+
+ client_realm = krb5_principal_get_realm(context, in_creds->client);
+ server_realm = krb5_principal_get_realm(context, in_creds->server);
+ memset(&tmp_creds, 0, sizeof(tmp_creds));
+ ret = krb5_copy_principal(context, in_creds->client, &tmp_creds.client);
+ if(ret)
+ return ret;
+
+ try_realm = krb5_config_get_string(context, NULL, "capaths",
+ client_realm, server_realm, NULL);
+ if (try_realm == NULL)
+ try_realm = client_realm;
+
+ ret = krb5_make_principal(context,
+ &tmp_creds.server,
+ try_realm,
+ KRB5_TGS_NAME,
+ server_realm,
+ NULL);
+ if(ret){
+ krb5_free_principal(context, tmp_creds.client);
+ return ret;
+ }
+ {
+ krb5_creds tgts;
+
+ ret = find_cred(context, ccache, tmp_creds.server,
+ *ret_tgts, &tgts);
+ if(ret == 0){
+ if (try_realm != client_realm)
+ ok_as_delegate = tgts.flags.b.ok_as_delegate;
+
+ *out_creds = calloc(1, sizeof(**out_creds));
+ if(*out_creds == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret,
+ N_("malloc: out of memory", ""));
+ } else {
+ ret = get_cred_kdc_address(context, ccache, flags, NULL,
+ in_creds, &tgts,
+ impersonate_principal,
+ second_ticket,
+ *out_creds);
+ if (ret) {
+ free (*out_creds);
+ *out_creds = NULL;
+ } else if (ok_as_delegate == 0)
+ (*out_creds)->flags.b.ok_as_delegate = 0;
+ }
+ krb5_free_cred_contents(context, &tgts);
+ krb5_free_principal(context, tmp_creds.server);
+ krb5_free_principal(context, tmp_creds.client);
+ return ret;
+ }
+ }
+ if(krb5_realm_compare(context, in_creds->client, in_creds->server))
+ return not_found(context, in_creds->server, KRB5_CC_NOTFOUND);
+
+ /* XXX this can loop forever */
+ while(1){
+ heim_general_string tgt_inst;
+
+ ret = get_cred_kdc_capath(context, flags, ccache, &tmp_creds,
+ NULL, NULL, &tgt, ret_tgts);
+ if(ret) {
+ krb5_free_principal(context, tmp_creds.server);
+ krb5_free_principal(context, tmp_creds.client);
+ return ret;
+ }
+ /*
+ * if either of the chain or the ok_as_delegate was stripped
+ * by the kdc, make sure we strip it too.
+ */
+ if (ok_as_delegate == 0 || tgt->flags.b.ok_as_delegate == 0) {
+ ok_as_delegate = 0;
+ tgt->flags.b.ok_as_delegate = 0;
+ }
+
+ ret = add_cred(context, tgt, ret_tgts);
+ if(ret) {
+ krb5_free_principal(context, tmp_creds.server);
+ krb5_free_principal(context, tmp_creds.client);
+ return ret;
+ }
+ tgt_inst = tgt->server->name.name_string.val[1];
+ if(strcmp(tgt_inst, server_realm) == 0)
+ break;
+ krb5_free_principal(context, tmp_creds.server);
+ ret = krb5_make_principal(context, &tmp_creds.server,
+ tgt_inst, KRB5_TGS_NAME, server_realm, NULL);
+ if(ret) {
+ krb5_free_principal(context, tmp_creds.server);
+ krb5_free_principal(context, tmp_creds.client);
+ return ret;
+ }
+ ret = krb5_free_creds(context, tgt);
+ if(ret) {
+ krb5_free_principal(context, tmp_creds.server);
+ krb5_free_principal(context, tmp_creds.client);
+ return ret;
+ }
+ }
+
+ krb5_free_principal(context, tmp_creds.server);
+ krb5_free_principal(context, tmp_creds.client);
+ *out_creds = calloc(1, sizeof(**out_creds));
+ if(*out_creds == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ } else {
+ ret = get_cred_kdc_address (context, ccache, flags, NULL,
+ in_creds, tgt, impersonate_principal,
+ second_ticket, *out_creds);
+ if (ret) {
+ free (*out_creds);
+ *out_creds = NULL;
+ }
+ }
+ krb5_free_creds(context, tgt);
+ return ret;
+}
+
+static krb5_error_code
+get_cred_kdc_referral(krb5_context context,
+ krb5_kdc_flags flags,
+ krb5_ccache ccache,
+ krb5_creds *in_creds,
+ krb5_principal impersonate_principal,
+ Ticket *second_ticket,
+ krb5_creds **out_creds,
+ krb5_creds ***ret_tgts)
+{
+ krb5_const_realm client_realm;
+ krb5_error_code ret;
+ krb5_creds tgt, referral, ticket;
+ int loop = 0;
+ int ok_as_delegate = 1;
+
+ memset(&tgt, 0, sizeof(tgt));
+ memset(&ticket, 0, sizeof(ticket));
+
+ flags.b.canonicalize = 1;
+
+ *out_creds = NULL;
+
+ client_realm = krb5_principal_get_realm(context, in_creds->client);
+
+ /* find tgt for the clients base realm */
+ {
+ krb5_principal tgtname;
+
+ ret = krb5_make_principal(context, &tgtname,
+ client_realm,
+ KRB5_TGS_NAME,
+ client_realm,
+ NULL);
+ if(ret)
+ return ret;
+
+ ret = find_cred(context, ccache, tgtname, *ret_tgts, &tgt);
+ krb5_free_principal(context, tgtname);
+ if (ret)
+ return ret;
+ }
+
+ referral = *in_creds;
+ ret = krb5_copy_principal(context, in_creds->server, &referral.server);
+ if (ret) {
+ krb5_free_cred_contents(context, &tgt);
+ return ret;
+ }
+ ret = krb5_principal_set_realm(context, referral.server, client_realm);
+ if (ret) {
+ krb5_free_cred_contents(context, &tgt);
+ krb5_free_principal(context, referral.server);
+ return ret;
+ }
+
+ while (loop++ < 17) {
+ krb5_creds **tickets;
+ krb5_creds mcreds;
+ char *referral_realm;
+
+ /* Use cache if we are not doing impersonation or contrainte deleg */
+ if (impersonate_principal == NULL || flags.b.constrained_delegation) {
+ krb5_cc_clear_mcred(&mcreds);
+ mcreds.server = referral.server;
+ ret = krb5_cc_retrieve_cred(context, ccache, 0, &mcreds, &ticket);
+ } else
+ ret = EINVAL;
+
+ if (ret) {
+ ret = get_cred_kdc_address (context, ccache, flags, NULL,
+ &referral, &tgt, impersonate_principal,
+ second_ticket, &ticket);
+ if (ret)
+ goto out;
+ }
+
+ /* Did we get the right ticket ? */
+ if (krb5_principal_compare_any_realm(context,
+ referral.server,
+ ticket.server))
+ break;
+
+ if (ticket.server->name.name_string.len != 2 &&
+ strcmp(ticket.server->name.name_string.val[0], KRB5_TGS_NAME) != 0)
+ {
+ krb5_set_error_message(context, KRB5KRB_AP_ERR_NOT_US,
+ N_("Got back an non krbtgt "
+ "ticket referrals", ""));
+ krb5_free_cred_contents(context, &ticket);
+ return KRB5KRB_AP_ERR_NOT_US;
+ }
+
+ referral_realm = ticket.server->name.name_string.val[1];
+
+ /* check that there are no referrals loops */
+ tickets = *ret_tgts;
+
+ krb5_cc_clear_mcred(&mcreds);
+ mcreds.server = ticket.server;
+
+ while(tickets && *tickets){
+ if(krb5_compare_creds(context,
+ KRB5_TC_DONT_MATCH_REALM,
+ &mcreds,
+ *tickets))
+ {
+ krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
+ N_("Referral from %s "
+ "loops back to realm %s", ""),
+ tgt.server->realm,
+ referral_realm);
+ krb5_free_cred_contents(context, &ticket);
+ return KRB5_GET_IN_TKT_LOOP;
+ }
+ tickets++;
+ }
+
+ /*
+ * if either of the chain or the ok_as_delegate was stripped
+ * by the kdc, make sure we strip it too.
+ */
+
+ if (ok_as_delegate == 0 || ticket.flags.b.ok_as_delegate == 0) {
+ ok_as_delegate = 0;
+ ticket.flags.b.ok_as_delegate = 0;
+ }
+
+ ret = add_cred(context, &ticket, ret_tgts);
+ if (ret) {
+ krb5_free_cred_contents(context, &ticket);
+ goto out;
+ }
+
+ /* try realm in the referral */
+ ret = krb5_principal_set_realm(context,
+ referral.server,
+ referral_realm);
+ krb5_free_cred_contents(context, &tgt);
+ tgt = ticket;
+ memset(&ticket, 0, sizeof(ticket));
+ if (ret)
+ goto out;
+ }
+
+ ret = krb5_copy_creds(context, &ticket, out_creds);
+
+out:
+ krb5_free_principal(context, referral.server);
+ krb5_free_cred_contents(context, &tgt);
+ return ret;
+}
+
+
+/*
+ * Glue function between referrals version and old client chasing
+ * codebase.
+ */
+
+static krb5_error_code
+get_cred_kdc_any(krb5_context context,
+ krb5_kdc_flags flags,
+ krb5_ccache ccache,
+ krb5_creds *in_creds,
+ krb5_principal impersonate_principal,
+ Ticket *second_ticket,
+ krb5_creds **out_creds,
+ krb5_creds ***ret_tgts)
+{
+ krb5_error_code ret;
+
+ ret = get_cred_kdc_referral(context,
+ flags,
+ ccache,
+ in_creds,
+ impersonate_principal,
+ second_ticket,
+ out_creds,
+ ret_tgts);
+ if (ret == 0 || flags.b.canonicalize)
+ return ret;
+ return get_cred_kdc_capath(context,
+ flags,
+ ccache,
+ in_creds,
+ impersonate_principal,
+ second_ticket,
+ out_creds,
+ ret_tgts);
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_cred_from_kdc_opt(krb5_context context,
+ krb5_ccache ccache,
+ krb5_creds *in_creds,
+ krb5_creds **out_creds,
+ krb5_creds ***ret_tgts,
+ krb5_flags flags)
+{
+ krb5_kdc_flags f;
+ f.i = flags;
+ return get_cred_kdc_any(context, f, ccache,
+ in_creds, NULL, NULL,
+ out_creds, ret_tgts);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_cred_from_kdc(krb5_context context,
+ krb5_ccache ccache,
+ krb5_creds *in_creds,
+ krb5_creds **out_creds,
+ krb5_creds ***ret_tgts)
+{
+ return krb5_get_cred_from_kdc_opt(context, ccache,
+ in_creds, out_creds, ret_tgts, 0);
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_credentials_with_flags(krb5_context context,
+ krb5_flags options,
+ krb5_kdc_flags flags,
+ krb5_ccache ccache,
+ krb5_creds *in_creds,
+ krb5_creds **out_creds)
+{
+ krb5_error_code ret;
+ krb5_creds **tgts;
+ krb5_creds *res_creds;
+ int i;
+
+ *out_creds = NULL;
+ res_creds = calloc(1, sizeof(*res_creds));
+ if (res_creds == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+
+ if (in_creds->session.keytype)
+ options |= KRB5_TC_MATCH_KEYTYPE;
+
+ /*
+ * If we got a credential, check if credential is expired before
+ * returning it.
+ */
+ ret = krb5_cc_retrieve_cred(context,
+ ccache,
+ in_creds->session.keytype ?
+ KRB5_TC_MATCH_KEYTYPE : 0,
+ in_creds, res_creds);
+ /*
+ * If we got a credential, check if credential is expired before
+ * returning it, but only if KRB5_GC_EXPIRED_OK is not set.
+ */
+ if (ret == 0) {
+ krb5_timestamp timeret;
+
+ /* If expired ok, don't bother checking */
+ if(options & KRB5_GC_EXPIRED_OK) {
+ *out_creds = res_creds;
+ return 0;
+ }
+
+ krb5_timeofday(context, &timeret);
+ if(res_creds->times.endtime > timeret) {
+ *out_creds = res_creds;
+ return 0;
+ }
+ if(options & KRB5_GC_CACHED)
+ krb5_cc_remove_cred(context, ccache, 0, res_creds);
+
+ } else if(ret != KRB5_CC_END) {
+ free(res_creds);
+ return ret;
+ }
+ free(res_creds);
+ if(options & KRB5_GC_CACHED)
+ return not_found(context, in_creds->server, KRB5_CC_NOTFOUND);
+
+ if(options & KRB5_GC_USER_USER)
+ flags.b.enc_tkt_in_skey = 1;
+ if (flags.b.enc_tkt_in_skey)
+ options |= KRB5_GC_NO_STORE;
+
+ tgts = NULL;
+ ret = get_cred_kdc_any(context, flags, ccache,
+ in_creds, NULL, NULL, out_creds, &tgts);
+ for(i = 0; tgts && tgts[i]; i++) {
+ krb5_cc_store_cred(context, ccache, tgts[i]);
+ krb5_free_creds(context, tgts[i]);
+ }
+ free(tgts);
+ if(ret == 0 && (options & KRB5_GC_NO_STORE) == 0)
+ krb5_cc_store_cred(context, ccache, *out_creds);
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_credentials(krb5_context context,
+ krb5_flags options,
+ krb5_ccache ccache,
+ krb5_creds *in_creds,
+ krb5_creds **out_creds)
+{
+ krb5_kdc_flags flags;
+ flags.i = 0;
+ return krb5_get_credentials_with_flags(context, options, flags,
+ ccache, in_creds, out_creds);
+}
+
+struct krb5_get_creds_opt_data {
+ krb5_principal self;
+ krb5_flags options;
+ krb5_enctype enctype;
+ Ticket *ticket;
+};
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_creds_opt_alloc(krb5_context context, krb5_get_creds_opt *opt)
+{
+ *opt = calloc(1, sizeof(**opt));
+ if (*opt == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ return 0;
+}
+
+void KRB5_LIB_FUNCTION
+krb5_get_creds_opt_free(krb5_context context, krb5_get_creds_opt opt)
+{
+ if (opt->self)
+ krb5_free_principal(context, opt->self);
+ memset(opt, 0, sizeof(*opt));
+ free(opt);
+}
+
+void KRB5_LIB_FUNCTION
+krb5_get_creds_opt_set_options(krb5_context context,
+ krb5_get_creds_opt opt,
+ krb5_flags options)
+{
+ opt->options = options;
+}
+
+void KRB5_LIB_FUNCTION
+krb5_get_creds_opt_add_options(krb5_context context,
+ krb5_get_creds_opt opt,
+ krb5_flags options)
+{
+ opt->options |= options;
+}
+
+void KRB5_LIB_FUNCTION
+krb5_get_creds_opt_set_enctype(krb5_context context,
+ krb5_get_creds_opt opt,
+ krb5_enctype enctype)
+{
+ opt->enctype = enctype;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_creds_opt_set_impersonate(krb5_context context,
+ krb5_get_creds_opt opt,
+ krb5_const_principal self)
+{
+ if (opt->self)
+ krb5_free_principal(context, opt->self);
+ return krb5_copy_principal(context, self, &opt->self);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_creds_opt_set_ticket(krb5_context context,
+ krb5_get_creds_opt opt,
+ const Ticket *ticket)
+{
+ if (opt->ticket) {
+ free_Ticket(opt->ticket);
+ free(opt->ticket);
+ opt->ticket = NULL;
+ }
+ if (ticket) {
+ krb5_error_code ret;
+
+ opt->ticket = malloc(sizeof(*ticket));
+ if (opt->ticket == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ ret = copy_Ticket(ticket, opt->ticket);
+ if (ret) {
+ free(opt->ticket);
+ opt->ticket = NULL;
+ krb5_set_error_message(context, ret,
+ N_("malloc: out of memory", ""));
+ return ret;
+ }
+ }
+ return 0;
+}
+
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_creds(krb5_context context,
+ krb5_get_creds_opt opt,
+ krb5_ccache ccache,
+ krb5_const_principal inprinc,
+ krb5_creds **out_creds)
+{
+ krb5_kdc_flags flags;
+ krb5_flags options;
+ krb5_creds in_creds;
+ krb5_error_code ret;
+ krb5_creds **tgts;
+ krb5_creds *res_creds;
+ int i;
+
+ memset(&in_creds, 0, sizeof(in_creds));
+ in_creds.server = rk_UNCONST(inprinc);
+
+ ret = krb5_cc_get_principal(context, ccache, &in_creds.client);
+ if (ret)
+ return ret;
+
+ options = opt->options;
+ flags.i = 0;
+
+ *out_creds = NULL;
+ res_creds = calloc(1, sizeof(*res_creds));
+ if (res_creds == NULL) {
+ krb5_free_principal(context, in_creds.client);
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+
+ if (opt->enctype) {
+ in_creds.session.keytype = opt->enctype;
+ options |= KRB5_TC_MATCH_KEYTYPE;
+ }
+
+ /*
+ * If we got a credential, check if credential is expired before
+ * returning it.
+ */
+ ret = krb5_cc_retrieve_cred(context,
+ ccache,
+ opt->enctype ? KRB5_TC_MATCH_KEYTYPE : 0,
+ &in_creds, res_creds);
+ /*
+ * If we got a credential, check if credential is expired before
+ * returning it, but only if KRB5_GC_EXPIRED_OK is not set.
+ */
+ if (ret == 0) {
+ krb5_timestamp timeret;
+
+ /* If expired ok, don't bother checking */
+ if(options & KRB5_GC_EXPIRED_OK) {
+ *out_creds = res_creds;
+ krb5_free_principal(context, in_creds.client);
+ return 0;
+ }
+
+ krb5_timeofday(context, &timeret);
+ if(res_creds->times.endtime > timeret) {
+ *out_creds = res_creds;
+ krb5_free_principal(context, in_creds.client);
+ return 0;
+ }
+ if(options & KRB5_GC_CACHED)
+ krb5_cc_remove_cred(context, ccache, 0, res_creds);
+
+ } else if(ret != KRB5_CC_END) {
+ free(res_creds);
+ krb5_free_principal(context, in_creds.client);
+ return ret;
+ }
+ free(res_creds);
+ if(options & KRB5_GC_CACHED) {
+ krb5_free_principal(context, in_creds.client);
+ return not_found(context, in_creds.server, KRB5_CC_NOTFOUND);
+ }
+ if(options & KRB5_GC_USER_USER) {
+ flags.b.enc_tkt_in_skey = 1;
+ options |= KRB5_GC_NO_STORE;
+ }
+ if (options & KRB5_GC_FORWARDABLE)
+ flags.b.forwardable = 1;
+ if (options & KRB5_GC_NO_TRANSIT_CHECK)
+ flags.b.disable_transited_check = 1;
+ if (options & KRB5_GC_CONSTRAINED_DELEGATION) {
+ flags.b.request_anonymous = 1; /* XXX ARGH confusion */
+ flags.b.constrained_delegation = 1;
+ }
+ if (options & KRB5_GC_CANONICALIZE)
+ flags.b.canonicalize = 1;
+
+ tgts = NULL;
+ ret = get_cred_kdc_any(context, flags, ccache,
+ &in_creds, opt->self, opt->ticket,
+ out_creds, &tgts);
+ krb5_free_principal(context, in_creds.client);
+ for(i = 0; tgts && tgts[i]; i++) {
+ krb5_cc_store_cred(context, ccache, tgts[i]);
+ krb5_free_creds(context, tgts[i]);
+ }
+ free(tgts);
+ if(ret == 0 && (options & KRB5_GC_NO_STORE) == 0)
+ krb5_cc_store_cred(context, ccache, *out_creds);
+ return ret;
+}
+
+/*
+ *
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_renewed_creds(krb5_context context,
+ krb5_creds *creds,
+ krb5_const_principal client,
+ krb5_ccache ccache,
+ const char *in_tkt_service)
+{
+ krb5_error_code ret;
+ krb5_kdc_flags flags;
+ krb5_creds in, *template, *out = NULL;
+
+ memset(&in, 0, sizeof(in));
+ memset(creds, 0, sizeof(*creds));
+
+ ret = krb5_copy_principal(context, client, &in.client);
+ if (ret)
+ return ret;
+
+ if (in_tkt_service) {
+ ret = krb5_parse_name(context, in_tkt_service, &in.server);
+ if (ret) {
+ krb5_free_principal(context, in.client);
+ return ret;
+ }
+ } else {
+ const char *realm = krb5_principal_get_realm(context, client);
+
+ ret = krb5_make_principal(context, &in.server, realm, KRB5_TGS_NAME,
+ realm, NULL);
+ if (ret) {
+ krb5_free_principal(context, in.client);
+ return ret;
+ }
+ }
+
+ flags.i = 0;
+ flags.b.renewable = flags.b.renew = 1;
+
+ /*
+ * Get template from old credential cache for the same entry, if
+ * this failes, no worries.
+ */
+ ret = krb5_get_credentials(context, KRB5_GC_CACHED, ccache, &in, &template);
+ if (ret == 0) {
+ flags.b.forwardable = template->flags.b.forwardable;
+ flags.b.proxiable = template->flags.b.proxiable;
+ krb5_free_creds (context, template);
+ }
+
+ ret = krb5_get_kdc_cred(context, ccache, flags, NULL, NULL, &in, &out);
+ krb5_free_principal(context, in.client);
+ krb5_free_principal(context, in.server);
+ if (ret)
+ return ret;
+
+ ret = krb5_copy_creds_contents(context, out, creds);
+ krb5_free_creds(context, out);
+
+ return ret;
+}
diff --git a/source4/heimdal/lib/krb5/get_default_principal.c b/source4/heimdal/lib/krb5/get_default_principal.c
new file mode 100644
index 0000000000..c804ab9e56
--- /dev/null
+++ b/source4/heimdal/lib/krb5/get_default_principal.c
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+/*
+ * Try to find out what's a reasonable default principal.
+ */
+
+static const char*
+get_env_user(void)
+{
+ const char *user = getenv("USER");
+ if(user == NULL)
+ user = getenv("LOGNAME");
+ if(user == NULL)
+ user = getenv("USERNAME");
+ return user;
+}
+
+/*
+ * Will only use operating-system dependant operation to get the
+ * default principal, for use of functions that in ccache layer to
+ * avoid recursive calls.
+ */
+
+krb5_error_code
+_krb5_get_default_principal_local (krb5_context context,
+ krb5_principal *princ)
+{
+ krb5_error_code ret;
+ const char *user;
+ uid_t uid;
+
+ *princ = NULL;
+
+ uid = getuid();
+ if(uid == 0) {
+ user = getlogin();
+ if(user == NULL)
+ user = get_env_user();
+ if(user != NULL && strcmp(user, "root") != 0)
+ ret = krb5_make_principal(context, princ, NULL, user, "root", NULL);
+ else
+ ret = krb5_make_principal(context, princ, NULL, "root", NULL);
+ } else {
+ struct passwd *pw = getpwuid(uid);
+ if(pw != NULL)
+ user = pw->pw_name;
+ else {
+ user = get_env_user();
+ if(user == NULL)
+ user = getlogin();
+ }
+ if(user == NULL) {
+ krb5_set_error_message(context, ENOTTY,
+ N_("unable to figure out current "
+ "principal", ""));
+ return ENOTTY; /* XXX */
+ }
+ ret = krb5_make_principal(context, princ, NULL, user, NULL);
+ }
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_default_principal (krb5_context context,
+ krb5_principal *princ)
+{
+ krb5_error_code ret;
+ krb5_ccache id;
+
+ *princ = NULL;
+
+ ret = krb5_cc_default (context, &id);
+ if (ret == 0) {
+ ret = krb5_cc_get_principal (context, id, princ);
+ krb5_cc_close (context, id);
+ if (ret == 0)
+ return 0;
+ }
+
+ return _krb5_get_default_principal_local(context, princ);
+}
diff --git a/source4/heimdal/lib/krb5/get_default_realm.c b/source4/heimdal/lib/krb5/get_default_realm.c
new file mode 100644
index 0000000000..a2518bbab7
--- /dev/null
+++ b/source4/heimdal/lib/krb5/get_default_realm.c
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 1997 - 2001, 2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+/*
+ * Return a NULL-terminated list of default realms in `realms'.
+ * Free this memory with krb5_free_host_realm.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_default_realms (krb5_context context,
+ krb5_realm **realms)
+{
+ if (context->default_realms == NULL) {
+ krb5_error_code ret = krb5_set_default_realm (context, NULL);
+ if (ret)
+ return KRB5_CONFIG_NODEFREALM;
+ }
+
+ return krb5_copy_host_realm (context,
+ context->default_realms,
+ realms);
+}
+
+/*
+ * Return the first default realm. For compatibility.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_default_realm(krb5_context context,
+ krb5_realm *realm)
+{
+ krb5_error_code ret;
+ char *res;
+
+ if (context->default_realms == NULL
+ || context->default_realms[0] == NULL) {
+ krb5_clear_error_message(context);
+ ret = krb5_set_default_realm (context, NULL);
+ if (ret)
+ return ret;
+ }
+
+ res = strdup (context->default_realms[0]);
+ if (res == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ *realm = res;
+ return 0;
+}
diff --git a/source4/heimdal/lib/krb5/get_for_creds.c b/source4/heimdal/lib/krb5/get_for_creds.c
new file mode 100644
index 0000000000..a7072a0136
--- /dev/null
+++ b/source4/heimdal/lib/krb5/get_for_creds.c
@@ -0,0 +1,462 @@
+/*
+ * Copyright (c) 1997 - 2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <krb5_locl.h>
+
+RCSID("$Id$");
+
+static krb5_error_code
+add_addrs(krb5_context context,
+ krb5_addresses *addr,
+ struct addrinfo *ai)
+{
+ krb5_error_code ret;
+ unsigned n, i;
+ void *tmp;
+ struct addrinfo *a;
+
+ n = 0;
+ for (a = ai; a != NULL; a = a->ai_next)
+ ++n;
+
+ tmp = realloc(addr->val, (addr->len + n) * sizeof(*addr->val));
+ if (tmp == NULL && (addr->len + n) != 0) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ goto fail;
+ }
+ addr->val = tmp;
+ for (i = addr->len; i < (addr->len + n); ++i) {
+ addr->val[i].addr_type = 0;
+ krb5_data_zero(&addr->val[i].address);
+ }
+ i = addr->len;
+ for (a = ai; a != NULL; a = a->ai_next) {
+ krb5_address ad;
+
+ ret = krb5_sockaddr2address (context, a->ai_addr, &ad);
+ if (ret == 0) {
+ if (krb5_address_search(context, &ad, addr))
+ krb5_free_address(context, &ad);
+ else
+ addr->val[i++] = ad;
+ }
+ else if (ret == KRB5_PROG_ATYPE_NOSUPP)
+ krb5_clear_error_message (context);
+ else
+ goto fail;
+ addr->len = i;
+ }
+ return 0;
+fail:
+ krb5_free_addresses (context, addr);
+ return ret;
+}
+
+/**
+ * Forward credentials for client to host hostname , making them
+ * forwardable if forwardable, and returning the blob of data to sent
+ * in out_data. If hostname == NULL, pick it from server.
+ *
+ * @param context A kerberos 5 context.
+ * @param auth_context the auth context with the key to encrypt the out_data.
+ * @param hostname the host to forward the tickets too.
+ * @param client the client to delegate from.
+ * @param server the server to delegate the credential too.
+ * @param ccache credential cache to use.
+ * @param forwardable make the forwarded ticket forwabledable.
+ * @param out_data the resulting credential.
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_credential
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_fwd_tgt_creds (krb5_context context,
+ krb5_auth_context auth_context,
+ const char *hostname,
+ krb5_principal client,
+ krb5_principal server,
+ krb5_ccache ccache,
+ int forwardable,
+ krb5_data *out_data)
+{
+ krb5_flags flags = 0;
+ krb5_creds creds;
+ krb5_error_code ret;
+ krb5_const_realm client_realm;
+
+ flags |= KDC_OPT_FORWARDED;
+
+ if (forwardable)
+ flags |= KDC_OPT_FORWARDABLE;
+
+ if (hostname == NULL &&
+ krb5_principal_get_type(context, server) == KRB5_NT_SRV_HST) {
+ const char *inst = krb5_principal_get_comp_string(context, server, 0);
+ const char *host = krb5_principal_get_comp_string(context, server, 1);
+
+ if (inst != NULL &&
+ strcmp(inst, "host") == 0 &&
+ host != NULL &&
+ krb5_principal_get_comp_string(context, server, 2) == NULL)
+ hostname = host;
+ }
+
+ client_realm = krb5_principal_get_realm(context, client);
+
+ memset (&creds, 0, sizeof(creds));
+ creds.client = client;
+
+ ret = krb5_build_principal(context,
+ &creds.server,
+ strlen(client_realm),
+ client_realm,
+ KRB5_TGS_NAME,
+ client_realm,
+ NULL);
+ if (ret)
+ return ret;
+
+ ret = krb5_get_forwarded_creds (context,
+ auth_context,
+ ccache,
+ flags,
+ hostname,
+ &creds,
+ out_data);
+ return ret;
+}
+
+/**
+ * Gets tickets forwarded to hostname. If the tickets that are
+ * forwarded are address-less, the forwarded tickets will also be
+ * address-less.
+ *
+ * If the ticket have any address, hostname will be used for figure
+ * out the address to forward the ticket too. This since this might
+ * use DNS, its insecure and also doesn't represent configured all
+ * addresses of the host. For example, the host might have two
+ * adresses, one IPv4 and one IPv6 address where the later is not
+ * published in DNS. This IPv6 address might be used communications
+ * and thus the resulting ticket useless.
+ *
+ * @param context A kerberos 5 context.
+ * @param auth_context the auth context with the key to encrypt the out_data.
+ * @param ccache credential cache to use
+ * @param flags the flags to control the resulting ticket flags
+ * @param hostname the host to forward the tickets too.
+ * @param in_creds the in client and server ticket names. The client
+ * and server components forwarded to the remote host.
+ * @param out_data the resulting credential.
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_credential
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_forwarded_creds (krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_ccache ccache,
+ krb5_flags flags,
+ const char *hostname,
+ krb5_creds *in_creds,
+ krb5_data *out_data)
+{
+ krb5_error_code ret;
+ krb5_creds *out_creds;
+ krb5_addresses addrs, *paddrs;
+ KRB_CRED cred;
+ KrbCredInfo *krb_cred_info;
+ EncKrbCredPart enc_krb_cred_part;
+ size_t len;
+ unsigned char *buf;
+ size_t buf_size;
+ krb5_kdc_flags kdc_flags;
+ krb5_crypto crypto;
+ struct addrinfo *ai;
+ krb5_creds *ticket;
+
+ paddrs = NULL;
+ addrs.len = 0;
+ addrs.val = NULL;
+
+ ret = krb5_get_credentials(context, 0, ccache, in_creds, &ticket);
+ if(ret == 0) {
+ if (ticket->addresses.len)
+ paddrs = &addrs;
+ krb5_free_creds (context, ticket);
+ } else {
+ krb5_boolean noaddr;
+ krb5_appdefault_boolean(context, NULL,
+ krb5_principal_get_realm(context,
+ in_creds->client),
+ "no-addresses", KRB5_ADDRESSLESS_DEFAULT,
+ &noaddr);
+ if (!noaddr)
+ paddrs = &addrs;
+ }
+
+ /*
+ * If tickets have addresses, get the address of the remote host.
+ */
+
+ if (paddrs != NULL) {
+
+ ret = getaddrinfo (hostname, NULL, NULL, &ai);
+ if (ret) {
+ krb5_error_code ret2 = krb5_eai_to_heim_errno(ret, errno);
+ krb5_set_error_message(context, ret2,
+ N_("resolving host %s failed: %s",
+ "hostname, error"),
+ hostname, gai_strerror(ret));
+ return ret2;
+ }
+
+ ret = add_addrs (context, &addrs, ai);
+ freeaddrinfo (ai);
+ if (ret)
+ return ret;
+ }
+
+ kdc_flags.b = int2KDCOptions(flags);
+
+ ret = krb5_get_kdc_cred (context,
+ ccache,
+ kdc_flags,
+ paddrs,
+ NULL,
+ in_creds,
+ &out_creds);
+ krb5_free_addresses (context, &addrs);
+ if (ret)
+ return ret;
+
+ memset (&cred, 0, sizeof(cred));
+ cred.pvno = 5;
+ cred.msg_type = krb_cred;
+ ALLOC_SEQ(&cred.tickets, 1);
+ if (cred.tickets.val == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ goto out2;
+ }
+ ret = decode_Ticket(out_creds->ticket.data,
+ out_creds->ticket.length,
+ cred.tickets.val, &len);
+ if (ret)
+ goto out3;
+
+ memset (&enc_krb_cred_part, 0, sizeof(enc_krb_cred_part));
+ ALLOC_SEQ(&enc_krb_cred_part.ticket_info, 1);
+ if (enc_krb_cred_part.ticket_info.val == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ goto out4;
+ }
+
+ if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_TIME) {
+ krb5_timestamp sec;
+ int32_t usec;
+
+ krb5_us_timeofday (context, &sec, &usec);
+
+ ALLOC(enc_krb_cred_part.timestamp, 1);
+ if (enc_krb_cred_part.timestamp == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ goto out4;
+ }
+ *enc_krb_cred_part.timestamp = sec;
+ ALLOC(enc_krb_cred_part.usec, 1);
+ if (enc_krb_cred_part.usec == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ goto out4;
+ }
+ *enc_krb_cred_part.usec = usec;
+ } else {
+ enc_krb_cred_part.timestamp = NULL;
+ enc_krb_cred_part.usec = NULL;
+ }
+
+ if (auth_context->local_address && auth_context->local_port && paddrs) {
+
+ ret = krb5_make_addrport (context,
+ &enc_krb_cred_part.s_address,
+ auth_context->local_address,
+ auth_context->local_port);
+ if (ret)
+ goto out4;
+ }
+
+ if (auth_context->remote_address) {
+ if (auth_context->remote_port) {
+ krb5_boolean noaddr;
+ krb5_const_realm srealm;
+
+ srealm = krb5_principal_get_realm(context, out_creds->server);
+ /* Is this correct, and should we use the paddrs == NULL
+ trick here as well? Having an address-less ticket may
+ indicate that we don't know our own global address, but
+ it does not necessary mean that we don't know the
+ server's. */
+ krb5_appdefault_boolean(context, NULL, srealm, "no-addresses",
+ FALSE, &noaddr);
+ if (!noaddr) {
+ ret = krb5_make_addrport (context,
+ &enc_krb_cred_part.r_address,
+ auth_context->remote_address,
+ auth_context->remote_port);
+ if (ret)
+ goto out4;
+ }
+ } else {
+ ALLOC(enc_krb_cred_part.r_address, 1);
+ if (enc_krb_cred_part.r_address == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret,
+ N_("malloc: out of memory", ""));
+ goto out4;
+ }
+
+ ret = krb5_copy_address (context, auth_context->remote_address,
+ enc_krb_cred_part.r_address);
+ if (ret)
+ goto out4;
+ }
+ }
+
+ /* fill ticket_info.val[0] */
+
+ enc_krb_cred_part.ticket_info.len = 1;
+
+ krb_cred_info = enc_krb_cred_part.ticket_info.val;
+
+ copy_EncryptionKey (&out_creds->session, &krb_cred_info->key);
+ ALLOC(krb_cred_info->prealm, 1);
+ copy_Realm (&out_creds->client->realm, krb_cred_info->prealm);
+ ALLOC(krb_cred_info->pname, 1);
+ copy_PrincipalName(&out_creds->client->name, krb_cred_info->pname);
+ ALLOC(krb_cred_info->flags, 1);
+ *krb_cred_info->flags = out_creds->flags.b;
+ ALLOC(krb_cred_info->authtime, 1);
+ *krb_cred_info->authtime = out_creds->times.authtime;
+ ALLOC(krb_cred_info->starttime, 1);
+ *krb_cred_info->starttime = out_creds->times.starttime;
+ ALLOC(krb_cred_info->endtime, 1);
+ *krb_cred_info->endtime = out_creds->times.endtime;
+ ALLOC(krb_cred_info->renew_till, 1);
+ *krb_cred_info->renew_till = out_creds->times.renew_till;
+ ALLOC(krb_cred_info->srealm, 1);
+ copy_Realm (&out_creds->server->realm, krb_cred_info->srealm);
+ ALLOC(krb_cred_info->sname, 1);
+ copy_PrincipalName (&out_creds->server->name, krb_cred_info->sname);
+ ALLOC(krb_cred_info->caddr, 1);
+ copy_HostAddresses (&out_creds->addresses, krb_cred_info->caddr);
+
+ krb5_free_creds (context, out_creds);
+
+ /* encode EncKrbCredPart */
+
+ ASN1_MALLOC_ENCODE(EncKrbCredPart, buf, buf_size,
+ &enc_krb_cred_part, &len, ret);
+ free_EncKrbCredPart (&enc_krb_cred_part);
+ if (ret) {
+ free_KRB_CRED(&cred);
+ return ret;
+ }
+ if(buf_size != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+
+ /**
+ * Some older of the MIT gssapi library used clear-text tickets
+ * (warped inside AP-REQ encryption), use the krb5_auth_context
+ * flag KRB5_AUTH_CONTEXT_CLEAR_FORWARDED_CRED to support those
+ * tickets. The session key is used otherwise to encrypt the
+ * forwarded ticket.
+ */
+
+ if (auth_context->flags & KRB5_AUTH_CONTEXT_CLEAR_FORWARDED_CRED) {
+ cred.enc_part.etype = ENCTYPE_NULL;
+ cred.enc_part.kvno = NULL;
+ cred.enc_part.cipher.data = buf;
+ cred.enc_part.cipher.length = buf_size;
+ } else {
+ /*
+ * Here older versions then 0.7.2 of Heimdal used the local or
+ * remote subkey. That is wrong, the session key should be
+ * used. Heimdal 0.7.2 and newer have code to try both in the
+ * receiving end.
+ */
+
+ ret = krb5_crypto_init(context, auth_context->keyblock, 0, &crypto);
+ if (ret) {
+ free(buf);
+ free_KRB_CRED(&cred);
+ return ret;
+ }
+ ret = krb5_encrypt_EncryptedData (context,
+ crypto,
+ KRB5_KU_KRB_CRED,
+ buf,
+ len,
+ 0,
+ &cred.enc_part);
+ free(buf);
+ krb5_crypto_destroy(context, crypto);
+ if (ret) {
+ free_KRB_CRED(&cred);
+ return ret;
+ }
+ }
+
+ ASN1_MALLOC_ENCODE(KRB_CRED, buf, buf_size, &cred, &len, ret);
+ free_KRB_CRED (&cred);
+ if (ret)
+ return ret;
+ if(buf_size != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+ out_data->length = len;
+ out_data->data = buf;
+ return 0;
+ out4:
+ free_EncKrbCredPart(&enc_krb_cred_part);
+ out3:
+ free_KRB_CRED(&cred);
+ out2:
+ krb5_free_creds (context, out_creds);
+ return ret;
+}
diff --git a/source4/heimdal/lib/krb5/get_host_realm.c b/source4/heimdal/lib/krb5/get_host_realm.c
new file mode 100644
index 0000000000..2ea075f6c5
--- /dev/null
+++ b/source4/heimdal/lib/krb5/get_host_realm.c
@@ -0,0 +1,260 @@
+/*
+ * Copyright (c) 1997 - 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+#include <resolve.h>
+
+RCSID("$Id$");
+
+/* To automagically find the correct realm of a host (without
+ * [domain_realm] in krb5.conf) add a text record for your domain with
+ * the name of your realm, like this:
+ *
+ * _kerberos IN TXT "FOO.SE"
+ *
+ * The search is recursive, so you can add entries for specific
+ * hosts. To find the realm of host a.b.c, it first tries
+ * _kerberos.a.b.c, then _kerberos.b.c and so on.
+ *
+ * This method is described in draft-ietf-cat-krb-dns-locate-03.txt.
+ *
+ */
+
+static int
+copy_txt_to_realms (struct resource_record *head,
+ krb5_realm **realms)
+{
+ struct resource_record *rr;
+ unsigned int n, i;
+
+ for(n = 0, rr = head; rr; rr = rr->next)
+ if (rr->type == T_TXT)
+ ++n;
+
+ if (n == 0)
+ return -1;
+
+ *realms = malloc ((n + 1) * sizeof(krb5_realm));
+ if (*realms == NULL)
+ return -1;
+
+ for (i = 0; i < n + 1; ++i)
+ (*realms)[i] = NULL;
+
+ for (i = 0, rr = head; rr; rr = rr->next) {
+ if (rr->type == T_TXT) {
+ char *tmp;
+
+ tmp = strdup(rr->u.txt);
+ if (tmp == NULL) {
+ for (i = 0; i < n; ++i)
+ free ((*realms)[i]);
+ free (*realms);
+ return -1;
+ }
+ (*realms)[i] = tmp;
+ ++i;
+ }
+ }
+ return 0;
+}
+
+static int
+dns_find_realm(krb5_context context,
+ const char *domain,
+ krb5_realm **realms)
+{
+ static const char *default_labels[] = { "_kerberos", NULL };
+ char dom[MAXHOSTNAMELEN];
+ struct dns_reply *r;
+ const char **labels;
+ char **config_labels;
+ int i, ret;
+
+ config_labels = krb5_config_get_strings(context, NULL, "libdefaults",
+ "dns_lookup_realm_labels", NULL);
+ if(config_labels != NULL)
+ labels = (const char **)config_labels;
+ else
+ labels = default_labels;
+ if(*domain == '.')
+ domain++;
+ for (i = 0; labels[i] != NULL; i++) {
+ ret = snprintf(dom, sizeof(dom), "%s.%s.", labels[i], domain);
+ if(ret < 0 || ret >= sizeof(dom)) {
+ if (config_labels)
+ krb5_config_free_strings(config_labels);
+ return -1;
+ }
+ r = dns_lookup(dom, "TXT");
+ if(r != NULL) {
+ ret = copy_txt_to_realms (r->head, realms);
+ dns_free_data(r);
+ if(ret == 0) {
+ if (config_labels)
+ krb5_config_free_strings(config_labels);
+ return 0;
+ }
+ }
+ }
+ if (config_labels)
+ krb5_config_free_strings(config_labels);
+ return -1;
+}
+
+/*
+ * Try to figure out what realms host in `domain' belong to from the
+ * configuration file.
+ */
+
+static int
+config_find_realm(krb5_context context,
+ const char *domain,
+ krb5_realm **realms)
+{
+ char **tmp = krb5_config_get_strings (context, NULL,
+ "domain_realm",
+ domain,
+ NULL);
+
+ if (tmp == NULL)
+ return -1;
+ *realms = tmp;
+ return 0;
+}
+
+/*
+ * This function assumes that `host' is a FQDN (and doesn't handle the
+ * special case of host == NULL either).
+ * Try to find mapping in the config file or DNS and it that fails,
+ * fall back to guessing
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+_krb5_get_host_realm_int (krb5_context context,
+ const char *host,
+ krb5_boolean use_dns,
+ krb5_realm **realms)
+{
+ const char *p, *q;
+ krb5_boolean dns_locate_enable;
+
+ dns_locate_enable = krb5_config_get_bool_default(context, NULL, TRUE,
+ "libdefaults", "dns_lookup_realm", NULL);
+ for (p = host; p != NULL; p = strchr (p + 1, '.')) {
+ if(config_find_realm(context, p, realms) == 0) {
+ if(strcasecmp(*realms[0], "dns_locate") == 0) {
+ if(use_dns)
+ for (q = host; q != NULL; q = strchr(q + 1, '.'))
+ if(dns_find_realm(context, q, realms) == 0)
+ return 0;
+ continue;
+ } else
+ return 0;
+ }
+ else if(use_dns && dns_locate_enable) {
+ if(dns_find_realm(context, p, realms) == 0)
+ return 0;
+ }
+ }
+ p = strchr(host, '.');
+ if(p != NULL) {
+ p++;
+ *realms = malloc(2 * sizeof(krb5_realm));
+ if (*realms == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+
+ (*realms)[0] = strdup(p);
+ if((*realms)[0] == NULL) {
+ free(*realms);
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ strupr((*realms)[0]);
+ (*realms)[1] = NULL;
+ return 0;
+ }
+ krb5_set_error_message(context, KRB5_ERR_HOST_REALM_UNKNOWN,
+ N_("unable to find realm of host %s", ""),
+ host);
+ return KRB5_ERR_HOST_REALM_UNKNOWN;
+}
+
+/*
+ * Return the realm(s) of `host' as a NULL-terminated list in
+ * `realms'. Free `realms' with krb5_free_host_realm().
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_host_realm(krb5_context context,
+ const char *targethost,
+ krb5_realm **realms)
+{
+ const char *host = targethost;
+ char hostname[MAXHOSTNAMELEN];
+ krb5_error_code ret;
+ int use_dns;
+
+ if (host == NULL) {
+ if (gethostname (hostname, sizeof(hostname))) {
+ *realms = NULL;
+ return errno;
+ }
+ host = hostname;
+ }
+
+ /*
+ * If our local hostname is without components, don't even try to dns.
+ */
+
+ use_dns = (strchr(host, '.') != NULL);
+
+ ret = _krb5_get_host_realm_int (context, host, use_dns, realms);
+ if (ret && targethost != NULL) {
+ /*
+ * If there was no realm mapping for the host (and we wasn't
+ * looking for ourself), guess at the local realm, maybe our
+ * KDC knows better then we do and we get a referral back.
+ */
+ ret = krb5_get_default_realms(context, realms);
+ if (ret) {
+ krb5_set_error_message(context, KRB5_ERR_HOST_REALM_UNKNOWN,
+ N_("Unable to find realm of host %s", ""),
+ host);
+ return KRB5_ERR_HOST_REALM_UNKNOWN;
+ }
+ }
+ return ret;
+}
diff --git a/source4/heimdal/lib/krb5/get_in_tkt.c b/source4/heimdal/lib/krb5/get_in_tkt.c
new file mode 100644
index 0000000000..cc49e16030
--- /dev/null
+++ b/source4/heimdal/lib/krb5/get_in_tkt.c
@@ -0,0 +1,1078 @@
+/*
+ * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_init_etype (krb5_context context,
+ unsigned *len,
+ krb5_enctype **val,
+ const krb5_enctype *etypes)
+{
+ unsigned int i;
+ krb5_error_code ret;
+ krb5_enctype *tmp = NULL;
+
+ ret = 0;
+ if (etypes == NULL) {
+ ret = krb5_get_default_in_tkt_etypes(context,
+ &tmp);
+ if (ret)
+ return ret;
+ etypes = tmp;
+ }
+
+ for (i = 0; etypes[i]; ++i)
+ ;
+ *len = i;
+ *val = malloc(i * sizeof(**val));
+ if (i != 0 && *val == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ goto cleanup;
+ }
+ memmove (*val,
+ etypes,
+ i * sizeof(*tmp));
+cleanup:
+ if (tmp != NULL)
+ free (tmp);
+ return ret;
+}
+
+static krb5_error_code
+check_server_referral(krb5_context context,
+ krb5_kdc_rep *rep,
+ unsigned flags,
+ krb5_const_principal requested,
+ krb5_const_principal returned,
+ const krb5_keyblock const * key)
+{
+ krb5_error_code ret;
+ PA_ServerReferralData ref;
+ krb5_crypto session;
+ EncryptedData ed;
+ size_t len;
+ krb5_data data;
+ PA_DATA *pa;
+ int i = 0, cmp;
+
+ if (rep->kdc_rep.padata == NULL)
+ goto noreferral;
+
+ pa = krb5_find_padata(rep->kdc_rep.padata->val,
+ rep->kdc_rep.padata->len,
+ KRB5_PADATA_SERVER_REFERRAL, &i);
+ if (pa == NULL)
+ goto noreferral;
+
+ memset(&ed, 0, sizeof(ed));
+ memset(&ref, 0, sizeof(ref));
+
+ ret = decode_EncryptedData(pa->padata_value.data,
+ pa->padata_value.length,
+ &ed, &len);
+ if (ret)
+ return ret;
+ if (len != pa->padata_value.length) {
+ free_EncryptedData(&ed);
+ krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
+ N_("Referral EncryptedData wrong for realm %s",
+ "realm"), requested->realm);
+ return KRB5KRB_AP_ERR_MODIFIED;
+ }
+
+ ret = krb5_crypto_init(context, key, 0, &session);
+ if (ret) {
+ free_EncryptedData(&ed);
+ return ret;
+ }
+
+ ret = krb5_decrypt_EncryptedData(context, session,
+ KRB5_KU_PA_SERVER_REFERRAL,
+ &ed, &data);
+ free_EncryptedData(&ed);
+ krb5_crypto_destroy(context, session);
+ if (ret)
+ return ret;
+
+ ret = decode_PA_ServerReferralData(data.data, data.length, &ref, &len);
+ if (ret) {
+ krb5_data_free(&data);
+ return ret;
+ }
+ krb5_data_free(&data);
+
+ if (strcmp(requested->realm, returned->realm) != 0) {
+ free_PA_ServerReferralData(&ref);
+ krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
+ N_("server ref realm mismatch, "
+ "requested realm %s got back %s", ""),
+ requested->realm, returned->realm);
+ return KRB5KRB_AP_ERR_MODIFIED;
+ }
+
+ if (returned->name.name_string.len == 2 &&
+ strcmp(returned->name.name_string.val[0], KRB5_TGS_NAME) == 0)
+ {
+ const char *realm = returned->name.name_string.val[1];
+
+ if (ref.referred_realm == NULL
+ || strcmp(*ref.referred_realm, realm) != 0)
+ {
+ free_PA_ServerReferralData(&ref);
+ krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
+ N_("tgt returned with wrong ref", ""));
+ return KRB5KRB_AP_ERR_MODIFIED;
+ }
+ } else if (krb5_principal_compare(context, returned, requested) == 0) {
+ free_PA_ServerReferralData(&ref);
+ krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
+ N_("req princ no same as returned", ""));
+ return KRB5KRB_AP_ERR_MODIFIED;
+ }
+
+ if (ref.requested_principal_name) {
+ cmp = _krb5_principal_compare_PrincipalName(context,
+ requested,
+ ref.requested_principal_name);
+ if (!cmp) {
+ free_PA_ServerReferralData(&ref);
+ krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
+ N_("referred principal not same "
+ "as requested", ""));
+ return KRB5KRB_AP_ERR_MODIFIED;
+ }
+ } else if (flags & EXTRACT_TICKET_AS_REQ) {
+ free_PA_ServerReferralData(&ref);
+ krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
+ N_("Requested principal missing on AS-REQ", ""));
+ return KRB5KRB_AP_ERR_MODIFIED;
+ }
+
+ free_PA_ServerReferralData(&ref);
+
+ return ret;
+noreferral:
+ if (krb5_principal_compare(context, requested, returned) == FALSE) {
+ krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
+ N_("Not same server principal returned "
+ "as requested", ""));
+ return KRB5KRB_AP_ERR_MODIFIED;
+ }
+ return 0;
+}
+
+
+/*
+ * Verify referral data
+ */
+
+
+static krb5_error_code
+check_client_referral(krb5_context context,
+ krb5_kdc_rep *rep,
+ krb5_const_principal requested,
+ krb5_const_principal mapped,
+ krb5_keyblock const * key)
+{
+ krb5_error_code ret;
+ PA_ClientCanonicalized canon;
+ krb5_crypto crypto;
+ krb5_data data;
+ PA_DATA *pa;
+ size_t len;
+ int i = 0;
+
+ if (rep->kdc_rep.padata == NULL)
+ goto noreferral;
+
+ pa = krb5_find_padata(rep->kdc_rep.padata->val,
+ rep->kdc_rep.padata->len,
+ KRB5_PADATA_CLIENT_CANONICALIZED, &i);
+ if (pa == NULL)
+ goto noreferral;
+
+ ret = decode_PA_ClientCanonicalized(pa->padata_value.data,
+ pa->padata_value.length,
+ &canon, &len);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("Failed to decode ClientCanonicalized "
+ "from realm %s", ""), requested->realm);
+ return ret;
+ }
+
+ ASN1_MALLOC_ENCODE(PA_ClientCanonicalizedNames, data.data, data.length,
+ &canon.names, &len, ret);
+ if (ret) {
+ free_PA_ClientCanonicalized(&canon);
+ return ret;
+ }
+ if (data.length != len)
+ krb5_abortx(context, "internal asn.1 error");
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret) {
+ free(data.data);
+ free_PA_ClientCanonicalized(&canon);
+ return ret;
+ }
+
+ ret = krb5_verify_checksum(context, crypto, KRB5_KU_CANONICALIZED_NAMES,
+ data.data, data.length,
+ &canon.canon_checksum);
+ krb5_crypto_destroy(context, crypto);
+ free(data.data);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("Failed to verify client canonicalized "
+ "data from realm %s", ""),
+ requested->realm);
+ free_PA_ClientCanonicalized(&canon);
+ return ret;
+ }
+
+ if (!_krb5_principal_compare_PrincipalName(context,
+ requested,
+ &canon.names.requested_name))
+ {
+ free_PA_ClientCanonicalized(&canon);
+ krb5_set_error_message(context, KRB5_PRINC_NOMATCH,
+ N_("Requested name doesn't match"
+ " in client referral", ""));
+ return KRB5_PRINC_NOMATCH;
+ }
+ if (!_krb5_principal_compare_PrincipalName(context,
+ mapped,
+ &canon.names.mapped_name))
+ {
+ free_PA_ClientCanonicalized(&canon);
+ krb5_set_error_message(context, KRB5_PRINC_NOMATCH,
+ N_("Mapped name doesn't match"
+ " in client referral", ""));
+ return KRB5_PRINC_NOMATCH;
+ }
+
+ return 0;
+
+noreferral:
+ if (krb5_principal_compare(context, requested, mapped) == FALSE) {
+ krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
+ N_("Not same client principal returned "
+ "as requested", ""));
+ return KRB5KRB_AP_ERR_MODIFIED;
+ }
+ return 0;
+}
+
+
+
+static krb5_error_code
+decrypt_tkt (krb5_context context,
+ krb5_keyblock *key,
+ krb5_key_usage usage,
+ krb5_const_pointer decrypt_arg,
+ krb5_kdc_rep *dec_rep)
+{
+ krb5_error_code ret;
+ krb5_data data;
+ size_t size;
+ krb5_crypto crypto;
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret)
+ return ret;
+
+ ret = krb5_decrypt_EncryptedData (context,
+ crypto,
+ usage,
+ &dec_rep->kdc_rep.enc_part,
+ &data);
+ krb5_crypto_destroy(context, crypto);
+
+ if (ret)
+ return ret;
+
+ ret = krb5_decode_EncASRepPart(context,
+ data.data,
+ data.length,
+ &dec_rep->enc_part,
+ &size);
+ if (ret)
+ ret = krb5_decode_EncTGSRepPart(context,
+ data.data,
+ data.length,
+ &dec_rep->enc_part,
+ &size);
+ krb5_data_free (&data);
+ if (ret)
+ return ret;
+ return 0;
+}
+
+int
+_krb5_extract_ticket(krb5_context context,
+ krb5_kdc_rep *rep,
+ krb5_creds *creds,
+ krb5_keyblock *key,
+ krb5_const_pointer keyseed,
+ krb5_key_usage key_usage,
+ krb5_addresses *addrs,
+ unsigned nonce,
+ unsigned flags,
+ krb5_decrypt_proc decrypt_proc,
+ krb5_const_pointer decryptarg)
+{
+ krb5_error_code ret;
+ krb5_principal tmp_principal;
+ size_t len;
+ time_t tmp_time;
+ krb5_timestamp sec_now;
+
+ /* decrypt */
+
+ if (decrypt_proc == NULL)
+ decrypt_proc = decrypt_tkt;
+
+ ret = (*decrypt_proc)(context, key, key_usage, decryptarg, rep);
+ if (ret)
+ goto out;
+
+ /* save session key */
+
+ creds->session.keyvalue.length = 0;
+ creds->session.keyvalue.data = NULL;
+ creds->session.keytype = rep->enc_part.key.keytype;
+ ret = krb5_data_copy (&creds->session.keyvalue,
+ rep->enc_part.key.keyvalue.data,
+ rep->enc_part.key.keyvalue.length);
+ if (ret) {
+ krb5_clear_error_message(context);
+ goto out;
+ }
+
+ /*
+ * HACK:
+ * this is really a ugly hack, to support using the Netbios Domain Name
+ * as realm against windows KDC's, they always return the full realm
+ * based on the DNS Name.
+ */
+ flags |= EXTRACT_TICKET_ALLOW_SERVER_MISMATCH;
+ flags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH;
+
+ /* compare client and save */
+ ret = _krb5_principalname2krb5_principal (context,
+ &tmp_principal,
+ rep->kdc_rep.cname,
+ rep->kdc_rep.crealm);
+ if (ret)
+ goto out;
+
+ /* check client referral and save principal */
+ /* anonymous here ? */
+ if((flags & EXTRACT_TICKET_ALLOW_CNAME_MISMATCH) == 0) {
+ ret = check_client_referral(context, rep,
+ creds->client,
+ tmp_principal,
+ &creds->session);
+ if (ret) {
+ krb5_free_principal (context, tmp_principal);
+ goto out;
+ }
+ }
+ krb5_free_principal (context, creds->client);
+ creds->client = tmp_principal;
+
+ /* check server referral and save principal */
+ ret = _krb5_principalname2krb5_principal (context,
+ &tmp_principal,
+ rep->kdc_rep.ticket.sname,
+ rep->kdc_rep.ticket.realm);
+ if (ret)
+ goto out;
+ if((flags & EXTRACT_TICKET_ALLOW_SERVER_MISMATCH) == 0){
+ ret = check_server_referral(context,
+ rep,
+ flags,
+ creds->server,
+ tmp_principal,
+ &creds->session);
+ if (ret) {
+ krb5_free_principal (context, tmp_principal);
+ goto out;
+ }
+ }
+ krb5_free_principal(context, creds->server);
+ creds->server = tmp_principal;
+
+ /* verify names */
+ if(flags & EXTRACT_TICKET_MATCH_REALM){
+ const char *srealm = krb5_principal_get_realm(context, creds->server);
+ const char *crealm = krb5_principal_get_realm(context, creds->client);
+
+ if (strcmp(rep->enc_part.srealm, srealm) != 0 ||
+ strcmp(rep->enc_part.srealm, crealm) != 0)
+ {
+ ret = KRB5KRB_AP_ERR_MODIFIED;
+ krb5_clear_error_message(context);
+ goto out;
+ }
+ }
+
+ /* compare nonces */
+
+ if (nonce != rep->enc_part.nonce) {
+ ret = KRB5KRB_AP_ERR_MODIFIED;
+ krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ goto out;
+ }
+
+ /* set kdc-offset */
+
+ krb5_timeofday (context, &sec_now);
+ if (rep->enc_part.flags.initial
+ && context->kdc_sec_offset == 0
+ && krb5_config_get_bool (context, NULL,
+ "libdefaults",
+ "kdc_timesync",
+ NULL)) {
+ context->kdc_sec_offset = rep->enc_part.authtime - sec_now;
+ krb5_timeofday (context, &sec_now);
+ }
+
+ /* check all times */
+
+ if (rep->enc_part.starttime) {
+ tmp_time = *rep->enc_part.starttime;
+ } else
+ tmp_time = rep->enc_part.authtime;
+
+ if (creds->times.starttime == 0
+ && abs(tmp_time - sec_now) > context->max_skew) {
+ ret = KRB5KRB_AP_ERR_SKEW;
+ krb5_set_error_message (context, ret,
+ N_("time skew (%d) larger than max (%d)", ""),
+ abs(tmp_time - sec_now),
+ (int)context->max_skew);
+ goto out;
+ }
+
+ if (creds->times.starttime != 0
+ && tmp_time != creds->times.starttime) {
+ krb5_clear_error_message (context);
+ ret = KRB5KRB_AP_ERR_MODIFIED;
+ goto out;
+ }
+
+ creds->times.starttime = tmp_time;
+
+ if (rep->enc_part.renew_till) {
+ tmp_time = *rep->enc_part.renew_till;
+ } else
+ tmp_time = 0;
+
+ if (creds->times.renew_till != 0
+ && tmp_time > creds->times.renew_till) {
+ krb5_clear_error_message (context);
+ ret = KRB5KRB_AP_ERR_MODIFIED;
+ goto out;
+ }
+
+ creds->times.renew_till = tmp_time;
+
+ creds->times.authtime = rep->enc_part.authtime;
+
+ if (creds->times.endtime != 0
+ && rep->enc_part.endtime > creds->times.endtime) {
+ krb5_clear_error_message (context);
+ ret = KRB5KRB_AP_ERR_MODIFIED;
+ goto out;
+ }
+
+ creds->times.endtime = rep->enc_part.endtime;
+
+ if(rep->enc_part.caddr)
+ krb5_copy_addresses (context, rep->enc_part.caddr, &creds->addresses);
+ else if(addrs)
+ krb5_copy_addresses (context, addrs, &creds->addresses);
+ else {
+ creds->addresses.len = 0;
+ creds->addresses.val = NULL;
+ }
+ creds->flags.b = rep->enc_part.flags;
+
+ creds->authdata.len = 0;
+ creds->authdata.val = NULL;
+
+ /* extract ticket */
+ ASN1_MALLOC_ENCODE(Ticket, creds->ticket.data, creds->ticket.length,
+ &rep->kdc_rep.ticket, &len, ret);
+ if(ret)
+ goto out;
+ if (creds->ticket.length != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+ creds->second_ticket.length = 0;
+ creds->second_ticket.data = NULL;
+
+
+out:
+ memset (rep->enc_part.key.keyvalue.data, 0,
+ rep->enc_part.key.keyvalue.length);
+ return ret;
+}
+
+
+static krb5_error_code
+make_pa_enc_timestamp(krb5_context context, PA_DATA *pa,
+ krb5_enctype etype, krb5_keyblock *key)
+{
+ PA_ENC_TS_ENC p;
+ unsigned char *buf;
+ size_t buf_size;
+ size_t len;
+ EncryptedData encdata;
+ krb5_error_code ret;
+ int32_t usec;
+ int usec2;
+ krb5_crypto crypto;
+
+ krb5_us_timeofday (context, &p.patimestamp, &usec);
+ usec2 = usec;
+ p.pausec = &usec2;
+
+ ASN1_MALLOC_ENCODE(PA_ENC_TS_ENC, buf, buf_size, &p, &len, ret);
+ if (ret)
+ return ret;
+ if(buf_size != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret) {
+ free(buf);
+ return ret;
+ }
+ ret = krb5_encrypt_EncryptedData(context,
+ crypto,
+ KRB5_KU_PA_ENC_TIMESTAMP,
+ buf,
+ len,
+ 0,
+ &encdata);
+ free(buf);
+ krb5_crypto_destroy(context, crypto);
+ if (ret)
+ return ret;
+
+ ASN1_MALLOC_ENCODE(EncryptedData, buf, buf_size, &encdata, &len, ret);
+ free_EncryptedData(&encdata);
+ if (ret)
+ return ret;
+ if(buf_size != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+ pa->padata_type = KRB5_PADATA_ENC_TIMESTAMP;
+ pa->padata_value.length = len;
+ pa->padata_value.data = buf;
+ return 0;
+}
+
+static krb5_error_code
+add_padata(krb5_context context,
+ METHOD_DATA *md,
+ krb5_principal client,
+ krb5_key_proc key_proc,
+ krb5_const_pointer keyseed,
+ krb5_enctype *enctypes,
+ unsigned netypes,
+ krb5_salt *salt)
+{
+ krb5_error_code ret;
+ PA_DATA *pa2;
+ krb5_salt salt2;
+ krb5_enctype *ep;
+ int i;
+
+ if(salt == NULL) {
+ /* default to standard salt */
+ ret = krb5_get_pw_salt (context, client, &salt2);
+ salt = &salt2;
+ }
+ if (!enctypes) {
+ enctypes = context->etypes;
+ netypes = 0;
+ for (ep = enctypes; *ep != ETYPE_NULL; ep++)
+ netypes++;
+ }
+ pa2 = realloc (md->val, (md->len + netypes) * sizeof(*md->val));
+ if (pa2 == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ md->val = pa2;
+
+ for (i = 0; i < netypes; ++i) {
+ krb5_keyblock *key;
+
+ ret = (*key_proc)(context, enctypes[i], *salt, keyseed, &key);
+ if (ret)
+ continue;
+ ret = make_pa_enc_timestamp (context, &md->val[md->len],
+ enctypes[i], key);
+ krb5_free_keyblock (context, key);
+ if (ret)
+ return ret;
+ ++md->len;
+ }
+ if(salt == &salt2)
+ krb5_free_salt(context, salt2);
+ return 0;
+}
+
+static krb5_error_code
+init_as_req (krb5_context context,
+ KDCOptions opts,
+ krb5_creds *creds,
+ const krb5_addresses *addrs,
+ const krb5_enctype *etypes,
+ const krb5_preauthtype *ptypes,
+ const krb5_preauthdata *preauth,
+ krb5_key_proc key_proc,
+ krb5_const_pointer keyseed,
+ unsigned nonce,
+ AS_REQ *a)
+{
+ krb5_error_code ret;
+ krb5_salt salt;
+
+ memset(a, 0, sizeof(*a));
+
+ a->pvno = 5;
+ a->msg_type = krb_as_req;
+ a->req_body.kdc_options = opts;
+ a->req_body.cname = malloc(sizeof(*a->req_body.cname));
+ if (a->req_body.cname == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ goto fail;
+ }
+ a->req_body.sname = malloc(sizeof(*a->req_body.sname));
+ if (a->req_body.sname == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ goto fail;
+ }
+ ret = _krb5_principal2principalname (a->req_body.cname, creds->client);
+ if (ret)
+ goto fail;
+ ret = _krb5_principal2principalname (a->req_body.sname, creds->server);
+ if (ret)
+ goto fail;
+ ret = copy_Realm(&creds->client->realm, &a->req_body.realm);
+ if (ret)
+ goto fail;
+
+ if(creds->times.starttime) {
+ a->req_body.from = malloc(sizeof(*a->req_body.from));
+ if (a->req_body.from == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ goto fail;
+ }
+ *a->req_body.from = creds->times.starttime;
+ }
+ if(creds->times.endtime){
+ ALLOC(a->req_body.till, 1);
+ *a->req_body.till = creds->times.endtime;
+ }
+ if(creds->times.renew_till){
+ a->req_body.rtime = malloc(sizeof(*a->req_body.rtime));
+ if (a->req_body.rtime == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ goto fail;
+ }
+ *a->req_body.rtime = creds->times.renew_till;
+ }
+ a->req_body.nonce = nonce;
+ ret = krb5_init_etype (context,
+ &a->req_body.etype.len,
+ &a->req_body.etype.val,
+ etypes);
+ if (ret)
+ goto fail;
+
+ /*
+ * This means no addresses
+ */
+
+ if (addrs && addrs->len == 0) {
+ a->req_body.addresses = NULL;
+ } else {
+ a->req_body.addresses = malloc(sizeof(*a->req_body.addresses));
+ if (a->req_body.addresses == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ goto fail;
+ }
+
+ if (addrs)
+ ret = krb5_copy_addresses(context, addrs, a->req_body.addresses);
+ else {
+ ret = krb5_get_all_client_addrs (context, a->req_body.addresses);
+ if(ret == 0 && a->req_body.addresses->len == 0) {
+ free(a->req_body.addresses);
+ a->req_body.addresses = NULL;
+ }
+ }
+ if (ret)
+ return ret;
+ }
+
+ a->req_body.enc_authorization_data = NULL;
+ a->req_body.additional_tickets = NULL;
+
+ if(preauth != NULL) {
+ int i;
+ ALLOC(a->padata, 1);
+ if(a->padata == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ goto fail;
+ }
+ a->padata->val = NULL;
+ a->padata->len = 0;
+ for(i = 0; i < preauth->len; i++) {
+ if(preauth->val[i].type == KRB5_PADATA_ENC_TIMESTAMP){
+ int j;
+
+ for(j = 0; j < preauth->val[i].info.len; j++) {
+ krb5_salt *sp = &salt;
+ if(preauth->val[i].info.val[j].salttype)
+ salt.salttype = *preauth->val[i].info.val[j].salttype;
+ else
+ salt.salttype = KRB5_PW_SALT;
+ if(preauth->val[i].info.val[j].salt)
+ salt.saltvalue = *preauth->val[i].info.val[j].salt;
+ else
+ if(salt.salttype == KRB5_PW_SALT)
+ sp = NULL;
+ else
+ krb5_data_zero(&salt.saltvalue);
+ ret = add_padata(context, a->padata, creds->client,
+ key_proc, keyseed,
+ &preauth->val[i].info.val[j].etype, 1,
+ sp);
+ if (ret == 0)
+ break;
+ }
+ }
+ }
+ } else
+ /* not sure this is the way to use `ptypes' */
+ if (ptypes == NULL || *ptypes == KRB5_PADATA_NONE)
+ a->padata = NULL;
+ else if (*ptypes == KRB5_PADATA_ENC_TIMESTAMP) {
+ ALLOC(a->padata, 1);
+ if (a->padata == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ goto fail;
+ }
+ a->padata->len = 0;
+ a->padata->val = NULL;
+
+ /* make a v5 salted pa-data */
+ add_padata(context, a->padata, creds->client,
+ key_proc, keyseed, a->req_body.etype.val,
+ a->req_body.etype.len, NULL);
+
+ /* make a v4 salted pa-data */
+ salt.salttype = KRB5_PW_SALT;
+ krb5_data_zero(&salt.saltvalue);
+ add_padata(context, a->padata, creds->client,
+ key_proc, keyseed, a->req_body.etype.val,
+ a->req_body.etype.len, &salt);
+ } else {
+ ret = KRB5_PREAUTH_BAD_TYPE;
+ krb5_set_error_message (context, ret,
+ N_("pre-auth type %d not supported", ""),
+ *ptypes);
+ goto fail;
+ }
+ return 0;
+fail:
+ free_AS_REQ(a);
+ return ret;
+}
+
+static int
+set_ptypes(krb5_context context,
+ KRB_ERROR *error,
+ const krb5_preauthtype **ptypes,
+ krb5_preauthdata **preauth)
+{
+ static krb5_preauthdata preauth2;
+ static krb5_preauthtype ptypes2[] = { KRB5_PADATA_ENC_TIMESTAMP, KRB5_PADATA_NONE };
+
+ if(error->e_data) {
+ METHOD_DATA md;
+ int i;
+ decode_METHOD_DATA(error->e_data->data,
+ error->e_data->length,
+ &md,
+ NULL);
+ for(i = 0; i < md.len; i++){
+ switch(md.val[i].padata_type){
+ case KRB5_PADATA_ENC_TIMESTAMP:
+ *ptypes = ptypes2;
+ break;
+ case KRB5_PADATA_ETYPE_INFO:
+ *preauth = &preauth2;
+ ALLOC_SEQ(*preauth, 1);
+ (*preauth)->val[0].type = KRB5_PADATA_ENC_TIMESTAMP;
+ krb5_decode_ETYPE_INFO(context,
+ md.val[i].padata_value.data,
+ md.val[i].padata_value.length,
+ &(*preauth)->val[0].info,
+ NULL);
+ break;
+ default:
+ break;
+ }
+ }
+ free_METHOD_DATA(&md);
+ } else {
+ *ptypes = ptypes2;
+ }
+ return(1);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_in_cred(krb5_context context,
+ krb5_flags options,
+ const krb5_addresses *addrs,
+ const krb5_enctype *etypes,
+ const krb5_preauthtype *ptypes,
+ const krb5_preauthdata *preauth,
+ krb5_key_proc key_proc,
+ krb5_const_pointer keyseed,
+ krb5_decrypt_proc decrypt_proc,
+ krb5_const_pointer decryptarg,
+ krb5_creds *creds,
+ krb5_kdc_rep *ret_as_reply)
+{
+ krb5_error_code ret;
+ AS_REQ a;
+ krb5_kdc_rep rep;
+ krb5_data req, resp;
+ size_t len;
+ krb5_salt salt;
+ krb5_keyblock *key;
+ size_t size;
+ KDCOptions opts;
+ PA_DATA *pa;
+ krb5_enctype etype;
+ krb5_preauthdata *my_preauth = NULL;
+ unsigned nonce;
+ int done;
+
+ opts = int2KDCOptions(options);
+
+ krb5_generate_random_block (&nonce, sizeof(nonce));
+ nonce &= 0xffffffff;
+
+ do {
+ done = 1;
+ ret = init_as_req (context,
+ opts,
+ creds,
+ addrs,
+ etypes,
+ ptypes,
+ preauth,
+ key_proc,
+ keyseed,
+ nonce,
+ &a);
+ if (my_preauth) {
+ free_ETYPE_INFO(&my_preauth->val[0].info);
+ free (my_preauth->val);
+ my_preauth = NULL;
+ }
+ if (ret)
+ return ret;
+
+ ASN1_MALLOC_ENCODE(AS_REQ, req.data, req.length, &a, &len, ret);
+ free_AS_REQ(&a);
+ if (ret)
+ return ret;
+ if(len != req.length)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+
+ ret = krb5_sendto_kdc (context, &req, &creds->client->realm, &resp);
+ krb5_data_free(&req);
+ if (ret)
+ return ret;
+
+ memset (&rep, 0, sizeof(rep));
+ ret = decode_AS_REP(resp.data, resp.length, &rep.kdc_rep, &size);
+ if(ret) {
+ /* let's try to parse it as a KRB-ERROR */
+ KRB_ERROR error;
+ int ret2;
+
+ ret2 = krb5_rd_error(context, &resp, &error);
+ if(ret2 && resp.data && ((char*)resp.data)[0] == 4)
+ ret = KRB5KRB_AP_ERR_V4_REPLY;
+ krb5_data_free(&resp);
+ if (ret2 == 0) {
+ ret = krb5_error_from_rd_error(context, &error, creds);
+ /* if no preauth was set and KDC requires it, give it
+ one more try */
+ if (!ptypes && !preauth
+ && ret == KRB5KDC_ERR_PREAUTH_REQUIRED
+#if 0
+ || ret == KRB5KDC_ERR_BADOPTION
+#endif
+ && set_ptypes(context, &error, &ptypes, &my_preauth)) {
+ done = 0;
+ preauth = my_preauth;
+ krb5_free_error_contents(context, &error);
+ krb5_clear_error_message(context);
+ continue;
+ }
+ if(ret_as_reply)
+ ret_as_reply->error = error;
+ else
+ free_KRB_ERROR (&error);
+ return ret;
+ }
+ return ret;
+ }
+ krb5_data_free(&resp);
+ } while(!done);
+
+ pa = NULL;
+ etype = rep.kdc_rep.enc_part.etype;
+ if(rep.kdc_rep.padata){
+ int i = 0;
+ pa = krb5_find_padata(rep.kdc_rep.padata->val, rep.kdc_rep.padata->len,
+ KRB5_PADATA_PW_SALT, &i);
+ if(pa == NULL) {
+ i = 0;
+ pa = krb5_find_padata(rep.kdc_rep.padata->val,
+ rep.kdc_rep.padata->len,
+ KRB5_PADATA_AFS3_SALT, &i);
+ }
+ }
+ if(pa) {
+ salt.salttype = pa->padata_type;
+ salt.saltvalue = pa->padata_value;
+
+ ret = (*key_proc)(context, etype, salt, keyseed, &key);
+ } else {
+ /* make a v5 salted pa-data */
+ ret = krb5_get_pw_salt (context, creds->client, &salt);
+
+ if (ret)
+ goto out;
+ ret = (*key_proc)(context, etype, salt, keyseed, &key);
+ krb5_free_salt(context, salt);
+ }
+ if (ret)
+ goto out;
+
+ {
+ unsigned flags = 0;
+ if (opts.request_anonymous)
+ flags |= EXTRACT_TICKET_ALLOW_SERVER_MISMATCH;
+
+ ret = _krb5_extract_ticket(context,
+ &rep,
+ creds,
+ key,
+ keyseed,
+ KRB5_KU_AS_REP_ENC_PART,
+ NULL,
+ nonce,
+ flags,
+ decrypt_proc,
+ decryptarg);
+ }
+ memset (key->keyvalue.data, 0, key->keyvalue.length);
+ krb5_free_keyblock_contents (context, key);
+ free (key);
+
+out:
+ if (ret == 0 && ret_as_reply)
+ *ret_as_reply = rep;
+ else
+ krb5_free_kdc_rep (context, &rep);
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_in_tkt(krb5_context context,
+ krb5_flags options,
+ const krb5_addresses *addrs,
+ const krb5_enctype *etypes,
+ const krb5_preauthtype *ptypes,
+ krb5_key_proc key_proc,
+ krb5_const_pointer keyseed,
+ krb5_decrypt_proc decrypt_proc,
+ krb5_const_pointer decryptarg,
+ krb5_creds *creds,
+ krb5_ccache ccache,
+ krb5_kdc_rep *ret_as_reply)
+{
+ krb5_error_code ret;
+
+ ret = krb5_get_in_cred (context,
+ options,
+ addrs,
+ etypes,
+ ptypes,
+ NULL,
+ key_proc,
+ keyseed,
+ decrypt_proc,
+ decryptarg,
+ creds,
+ ret_as_reply);
+ if(ret)
+ return ret;
+ if (ccache)
+ ret = krb5_cc_store_cred (context, ccache, creds);
+ return ret;
+}
diff --git a/source4/heimdal/lib/krb5/get_in_tkt_with_keytab.c b/source4/heimdal/lib/krb5/get_in_tkt_with_keytab.c
new file mode 100644
index 0000000000..0dedbefd2c
--- /dev/null
+++ b/source4/heimdal/lib/krb5/get_in_tkt_with_keytab.c
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_keytab_key_proc (krb5_context context,
+ krb5_enctype enctype,
+ krb5_salt salt,
+ krb5_const_pointer keyseed,
+ krb5_keyblock **key)
+{
+ krb5_keytab_key_proc_args *args = rk_UNCONST(keyseed);
+ krb5_keytab keytab = args->keytab;
+ krb5_principal principal = args->principal;
+ krb5_error_code ret;
+ krb5_keytab real_keytab;
+ krb5_keytab_entry entry;
+
+ if(keytab == NULL)
+ krb5_kt_default(context, &real_keytab);
+ else
+ real_keytab = keytab;
+
+ ret = krb5_kt_get_entry (context, real_keytab, principal,
+ 0, enctype, &entry);
+
+ if (keytab == NULL)
+ krb5_kt_close (context, real_keytab);
+
+ if (ret)
+ return ret;
+
+ ret = krb5_copy_keyblock (context, &entry.keyblock, key);
+ krb5_kt_free_entry(context, &entry);
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_in_tkt_with_keytab (krb5_context context,
+ krb5_flags options,
+ krb5_addresses *addrs,
+ const krb5_enctype *etypes,
+ const krb5_preauthtype *pre_auth_types,
+ krb5_keytab keytab,
+ krb5_ccache ccache,
+ krb5_creds *creds,
+ krb5_kdc_rep *ret_as_reply)
+{
+ krb5_keytab_key_proc_args a;
+
+ a.principal = creds->client;
+ a.keytab = keytab;
+
+ return krb5_get_in_tkt (context,
+ options,
+ addrs,
+ etypes,
+ pre_auth_types,
+ krb5_keytab_key_proc,
+ &a,
+ NULL,
+ NULL,
+ creds,
+ ccache,
+ ret_as_reply);
+}
diff --git a/source4/heimdal/lib/krb5/get_port.c b/source4/heimdal/lib/krb5/get_port.c
new file mode 100644
index 0000000000..c9869eb450
--- /dev/null
+++ b/source4/heimdal/lib/krb5/get_port.c
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 1997-2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <krb5_locl.h>
+
+RCSID("$Id$");
+
+int KRB5_LIB_FUNCTION
+krb5_getportbyname (krb5_context context,
+ const char *service,
+ const char *proto,
+ int default_port)
+{
+ struct servent *sp;
+
+ if ((sp = roken_getservbyname (service, proto)) == NULL) {
+#if 0
+ krb5_warnx(context, "%s/%s unknown service, using default port %d",
+ service, proto, default_port);
+#endif
+ return htons(default_port);
+ } else
+ return sp->s_port;
+}
diff --git a/source4/heimdal/lib/krb5/heim_err.et b/source4/heimdal/lib/krb5/heim_err.et
new file mode 100644
index 0000000000..547a14e04c
--- /dev/null
+++ b/source4/heimdal/lib/krb5/heim_err.et
@@ -0,0 +1,44 @@
+#
+# Error messages for the krb5 library
+#
+# This might look like a com_err file, but is not
+#
+id "$Id$"
+
+error_table heim
+
+prefix HEIM_ERR
+
+error_code LOG_PARSE, "Error parsing log destination"
+error_code V4_PRINC_NO_CONV, "Failed to convert v4 principal"
+error_code SALTTYPE_NOSUPP, "Salt type is not supported by enctype"
+error_code NOHOST, "Host not found"
+error_code OPNOTSUPP, "Operation not supported"
+error_code EOF, "End of file"
+error_code BAD_MKEY, "Failed to get the master key"
+error_code SERVICE_NOMATCH, "Unacceptable service used"
+
+index 64
+prefix HEIM_PKINIT
+error_code NO_CERTIFICATE, "Certificate missing"
+error_code NO_PRIVATE_KEY, "Private key missing"
+error_code NO_VALID_CA, "No valid certificate authority"
+error_code CERTIFICATE_INVALID, "Certificate invalid"
+error_code PRIVATE_KEY_INVALID, "Private key invalid"
+
+index 128
+prefix HEIM_EAI
+#error_code NOERROR, "no error"
+error_code UNKNOWN, "unknown error from getaddrinfo"
+error_code ADDRFAMILY, "address family for nodename not supported"
+error_code AGAIN, "temporary failure in name resolution"
+error_code BADFLAGS, "invalid value for ai_flags"
+error_code FAIL, "non-recoverable failure in name resolution"
+error_code FAMILY, "ai_family not supported"
+error_code MEMORY, "memory allocation failure"
+error_code NODATA, "no address associated with nodename"
+error_code NONAME, "nodename nor servname provided, or not known"
+error_code SERVICE, "servname not supported for ai_socktype"
+error_code SOCKTYPE, "ai_socktype not supported"
+error_code SYSTEM, "system error returned in errno"
+end
diff --git a/source4/heimdal/lib/krb5/heim_threads.h b/source4/heimdal/lib/krb5/heim_threads.h
new file mode 100644
index 0000000000..c4f841fb61
--- /dev/null
+++ b/source4/heimdal/lib/krb5/heim_threads.h
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+/*
+ * Provide wrapper macros for thread synchronization primitives so we
+ * can use native thread functions for those operating system that
+ * supports it.
+ *
+ * This is so libkrb5.so (or more importantly, libgssapi.so) can have
+ * thread support while the program that that dlopen(3)s the library
+ * don't need to be linked to libpthread.
+ */
+
+#ifndef HEIM_THREADS_H
+#define HEIM_THREADS_H 1
+
+/* assume headers already included */
+
+#if defined(__NetBSD__) && __NetBSD_Version__ >= 106120000 && __NetBSD_Version__< 299001200 && defined(ENABLE_PTHREAD_SUPPORT)
+
+/*
+ * NetBSD have a thread lib that we can use that part of libc that
+ * works regardless if application are linked to pthreads or not.
+ * NetBSD newer then 2.99.11 just use pthread.h, and the same thing
+ * will happen.
+ */
+#include <threadlib.h>
+
+#define HEIMDAL_MUTEX mutex_t
+#define HEIMDAL_MUTEX_INITIALIZER MUTEX_INITIALIZER
+#define HEIMDAL_MUTEX_init(m) mutex_init(m, NULL)
+#define HEIMDAL_MUTEX_lock(m) mutex_lock(m)
+#define HEIMDAL_MUTEX_unlock(m) mutex_unlock(m)
+#define HEIMDAL_MUTEX_destroy(m) mutex_destroy(m)
+
+#define HEIMDAL_RWLOCK rwlock_t
+#define HEIMDAL_RWLOCK_INITIALIZER RWLOCK_INITIALIZER
+#define HEIMDAL_RWLOCK_init(l) rwlock_init(l, NULL)
+#define HEIMDAL_RWLOCK_rdlock(l) rwlock_rdlock(l)
+#define HEIMDAL_RWLOCK_wrlock(l) rwlock_wrlock(l)
+#define HEIMDAL_RWLOCK_tryrdlock(l) rwlock_tryrdlock(l)
+#define HEIMDAL_RWLOCK_trywrlock(l) rwlock_trywrlock(l)
+#define HEIMDAL_RWLOCK_unlock(l) rwlock_unlock(l)
+#define HEIMDAL_RWLOCK_destroy(l) rwlock_destroy(l)
+
+#define HEIMDAL_thread_key thread_key_t
+#define HEIMDAL_key_create(k,d,r) do { r = thr_keycreate(k,d); } while(0)
+#define HEIMDAL_setspecific(k,s,r) do { r = thr_setspecific(k,s); } while(0)
+#define HEIMDAL_getspecific(k) thr_getspecific(k)
+#define HEIMDAL_key_delete(k) thr_keydelete(k)
+
+#elif defined(ENABLE_PTHREAD_SUPPORT) && (!defined(__NetBSD__) || __NetBSD_Version__ >= 299001200)
+
+#include <pthread.h>
+
+#define HEIMDAL_MUTEX pthread_mutex_t
+#define HEIMDAL_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER
+#define HEIMDAL_MUTEX_init(m) pthread_mutex_init(m, NULL)
+#define HEIMDAL_MUTEX_lock(m) pthread_mutex_lock(m)
+#define HEIMDAL_MUTEX_unlock(m) pthread_mutex_unlock(m)
+#define HEIMDAL_MUTEX_destroy(m) pthread_mutex_destroy(m)
+
+#define HEIMDAL_RWLOCK rwlock_t
+#define HEIMDAL_RWLOCK_INITIALIZER RWLOCK_INITIALIZER
+#define HEIMDAL_RWLOCK_init(l) pthread_rwlock_init(l, NULL)
+#define HEIMDAL_RWLOCK_rdlock(l) pthread_rwlock_rdlock(l)
+#define HEIMDAL_RWLOCK_wrlock(l) pthread_rwlock_wrlock(l)
+#define HEIMDAL_RWLOCK_tryrdlock(l) pthread_rwlock_tryrdlock(l)
+#define HEIMDAL_RWLOCK_trywrlock(l) pthread_rwlock_trywrlock(l)
+#define HEIMDAL_RWLOCK_unlock(l) pthread_rwlock_unlock(l)
+#define HEIMDAL_RWLOCK_destroy(l) pthread_rwlock_destroy(l)
+
+#define HEIMDAL_thread_key pthread_key_t
+#define HEIMDAL_key_create(k,d,r) do { r = pthread_key_create(k,d); } while(0)
+#define HEIMDAL_setspecific(k,s,r) do { r = pthread_setspecific(k,s); } while(0)
+#define HEIMDAL_getspecific(k) pthread_getspecific(k)
+#define HEIMDAL_key_delete(k) pthread_key_delete(k)
+
+#elif defined(HEIMDAL_DEBUG_THREADS)
+
+/* no threads support, just do consistency checks */
+#include <stdlib.h>
+
+#define HEIMDAL_MUTEX int
+#define HEIMDAL_MUTEX_INITIALIZER 0
+#define HEIMDAL_MUTEX_init(m) do { (*(m)) = 0; } while(0)
+#define HEIMDAL_MUTEX_lock(m) do { if ((*(m))++ != 0) abort(); } while(0)
+#define HEIMDAL_MUTEX_unlock(m) do { if ((*(m))-- != 1) abort(); } while(0)
+#define HEIMDAL_MUTEX_destroy(m) do {if ((*(m)) != 0) abort(); } while(0)
+
+#define HEIMDAL_RWLOCK rwlock_t int
+#define HEIMDAL_RWLOCK_INITIALIZER 0
+#define HEIMDAL_RWLOCK_init(l) do { } while(0)
+#define HEIMDAL_RWLOCK_rdlock(l) do { } while(0)
+#define HEIMDAL_RWLOCK_wrlock(l) do { } while(0)
+#define HEIMDAL_RWLOCK_tryrdlock(l) do { } while(0)
+#define HEIMDAL_RWLOCK_trywrlock(l) do { } while(0)
+#define HEIMDAL_RWLOCK_unlock(l) do { } while(0)
+#define HEIMDAL_RWLOCK_destroy(l) do { } while(0)
+
+#define HEIMDAL_internal_thread_key 1
+
+#else /* no thread support, no debug case */
+
+#define HEIMDAL_MUTEX int
+#define HEIMDAL_MUTEX_INITIALIZER 0
+#define HEIMDAL_MUTEX_init(m) do { (void)(m); } while(0)
+#define HEIMDAL_MUTEX_lock(m) do { (void)(m); } while(0)
+#define HEIMDAL_MUTEX_unlock(m) do { (void)(m); } while(0)
+#define HEIMDAL_MUTEX_destroy(m) do { (void)(m); } while(0)
+
+#define HEIMDAL_RWLOCK rwlock_t int
+#define HEIMDAL_RWLOCK_INITIALIZER 0
+#define HEIMDAL_RWLOCK_init(l) do { } while(0)
+#define HEIMDAL_RWLOCK_rdlock(l) do { } while(0)
+#define HEIMDAL_RWLOCK_wrlock(l) do { } while(0)
+#define HEIMDAL_RWLOCK_tryrdlock(l) do { } while(0)
+#define HEIMDAL_RWLOCK_trywrlock(l) do { } while(0)
+#define HEIMDAL_RWLOCK_unlock(l) do { } while(0)
+#define HEIMDAL_RWLOCK_destroy(l) do { } while(0)
+
+#define HEIMDAL_internal_thread_key 1
+
+#endif /* no thread support */
+
+#ifdef HEIMDAL_internal_thread_key
+
+typedef struct heim_thread_key {
+ void *value;
+ void (*destructor)(void *);
+} heim_thread_key;
+
+#define HEIMDAL_thread_key heim_thread_key
+#define HEIMDAL_key_create(k,d,r) \
+ do { (k)->value = NULL; (k)->destructor = (d); r = 0; } while(0)
+#define HEIMDAL_setspecific(k,s,r) do { (k).value = s ; r = 0; } while(0)
+#define HEIMDAL_getspecific(k) ((k).value)
+#define HEIMDAL_key_delete(k) do { (*(k).destructor)((k).value); } while(0)
+
+#undef HEIMDAL_internal_thread_key
+#endif /* HEIMDAL_internal_thread_key */
+
+#endif /* HEIM_THREADS_H */
diff --git a/source4/heimdal/lib/krb5/init_creds.c b/source4/heimdal/lib/krb5/init_creds.c
new file mode 100644
index 0000000000..89ea3004ed
--- /dev/null
+++ b/source4/heimdal/lib/krb5/init_creds.c
@@ -0,0 +1,447 @@
+/*
+ * Copyright (c) 1997 - 2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+#undef __attribute__
+#define __attribute__(x)
+
+RCSID("$Id$");
+
+void KRB5_LIB_FUNCTION
+krb5_get_init_creds_opt_init(krb5_get_init_creds_opt *opt)
+ __attribute__((deprecated))
+{
+ memset (opt, 0, sizeof(*opt));
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_init_creds_opt_alloc(krb5_context context,
+ krb5_get_init_creds_opt **opt)
+{
+ krb5_get_init_creds_opt *o;
+
+ *opt = NULL;
+ o = calloc(1, sizeof(*o));
+ if (o == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+
+ o->opt_private = calloc(1, sizeof(*o->opt_private));
+ if (o->opt_private == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ free(o);
+ return ENOMEM;
+ }
+ o->opt_private->refcount = 1;
+ *opt = o;
+ return 0;
+}
+
+krb5_error_code
+_krb5_get_init_creds_opt_copy(krb5_context context,
+ const krb5_get_init_creds_opt *in,
+ krb5_get_init_creds_opt **out)
+{
+ krb5_get_init_creds_opt *opt;
+
+ *out = NULL;
+ opt = calloc(1, sizeof(*opt));
+ if (opt == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ if (in)
+ *opt = *in;
+ if(opt->opt_private == NULL) {
+ opt->opt_private = calloc(1, sizeof(*opt->opt_private));
+ if (opt->opt_private == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ free(opt);
+ return ENOMEM;
+ }
+ opt->opt_private->refcount = 1;
+ } else
+ opt->opt_private->refcount++;
+ *out = opt;
+ return 0;
+}
+
+void KRB5_LIB_FUNCTION
+_krb5_get_init_creds_opt_free_krb5_error(krb5_get_init_creds_opt *opt)
+{
+ if (opt->opt_private == NULL || opt->opt_private->error == NULL)
+ return;
+ free_KRB_ERROR(opt->opt_private->error);
+ free(opt->opt_private->error);
+ opt->opt_private->error = NULL;
+}
+
+void KRB5_LIB_FUNCTION
+_krb5_get_init_creds_opt_set_krb5_error(krb5_context context,
+ krb5_get_init_creds_opt *opt,
+ const KRB_ERROR *error)
+{
+ krb5_error_code ret;
+
+ if (opt->opt_private == NULL)
+ return;
+
+ _krb5_get_init_creds_opt_free_krb5_error(opt);
+
+ opt->opt_private->error = malloc(sizeof(*opt->opt_private->error));
+ if (opt->opt_private->error == NULL)
+ return;
+ ret = copy_KRB_ERROR(error, opt->opt_private->error);
+ if (ret) {
+ free(opt->opt_private->error);
+ opt->opt_private->error = NULL;
+ }
+}
+
+
+void KRB5_LIB_FUNCTION
+krb5_get_init_creds_opt_free(krb5_context context,
+ krb5_get_init_creds_opt *opt)
+{
+ if (opt == NULL || opt->opt_private == NULL)
+ return;
+ if (opt->opt_private->refcount < 1) /* abort ? */
+ return;
+ if (--opt->opt_private->refcount == 0) {
+ _krb5_get_init_creds_opt_free_krb5_error(opt);
+ _krb5_get_init_creds_opt_free_pkinit(opt);
+ free(opt->opt_private);
+ }
+ memset(opt, 0, sizeof(*opt));
+ free(opt);
+}
+
+static int
+get_config_time (krb5_context context,
+ const char *realm,
+ const char *name,
+ int def)
+{
+ int ret;
+
+ ret = krb5_config_get_time (context, NULL,
+ "realms",
+ realm,
+ name,
+ NULL);
+ if (ret >= 0)
+ return ret;
+ ret = krb5_config_get_time (context, NULL,
+ "libdefaults",
+ name,
+ NULL);
+ if (ret >= 0)
+ return ret;
+ return def;
+}
+
+static krb5_boolean
+get_config_bool (krb5_context context,
+ const char *realm,
+ const char *name)
+{
+ return krb5_config_get_bool (context,
+ NULL,
+ "realms",
+ realm,
+ name,
+ NULL)
+ || krb5_config_get_bool (context,
+ NULL,
+ "libdefaults",
+ name,
+ NULL);
+}
+
+/*
+ * set all the values in `opt' to the appropriate values for
+ * application `appname' (default to getprogname() if NULL), and realm
+ * `realm'. First looks in [appdefaults] but falls back to
+ * [realms] or [libdefaults] for some of the values.
+ */
+
+void KRB5_LIB_FUNCTION
+krb5_get_init_creds_opt_set_default_flags(krb5_context context,
+ const char *appname,
+ krb5_const_realm realm,
+ krb5_get_init_creds_opt *opt)
+{
+ krb5_boolean b;
+ time_t t;
+
+ b = get_config_bool (context, realm, "forwardable");
+ krb5_appdefault_boolean(context, appname, realm, "forwardable", b, &b);
+ krb5_get_init_creds_opt_set_forwardable(opt, b);
+
+ b = get_config_bool (context, realm, "proxiable");
+ krb5_appdefault_boolean(context, appname, realm, "proxiable", b, &b);
+ krb5_get_init_creds_opt_set_proxiable (opt, b);
+
+ krb5_appdefault_time(context, appname, realm, "ticket_lifetime", 0, &t);
+ if (t == 0)
+ t = get_config_time (context, realm, "ticket_lifetime", 0);
+ if(t != 0)
+ krb5_get_init_creds_opt_set_tkt_life(opt, t);
+
+ krb5_appdefault_time(context, appname, realm, "renew_lifetime", 0, &t);
+ if (t == 0)
+ t = get_config_time (context, realm, "renew_lifetime", 0);
+ if(t != 0)
+ krb5_get_init_creds_opt_set_renew_life(opt, t);
+
+ krb5_appdefault_boolean(context, appname, realm, "no-addresses",
+ KRB5_ADDRESSLESS_DEFAULT, &b);
+ krb5_get_init_creds_opt_set_addressless (context, opt, b);
+
+#if 0
+ krb5_appdefault_boolean(context, appname, realm, "anonymous", FALSE, &b);
+ krb5_get_init_creds_opt_set_anonymous (opt, b);
+
+ krb5_get_init_creds_opt_set_etype_list(opt, enctype,
+ etype_str.num_strings);
+
+ krb5_get_init_creds_opt_set_salt(krb5_get_init_creds_opt *opt,
+ krb5_data *salt);
+
+ krb5_get_init_creds_opt_set_preauth_list(krb5_get_init_creds_opt *opt,
+ krb5_preauthtype *preauth_list,
+ int preauth_list_length);
+#endif
+}
+
+
+void KRB5_LIB_FUNCTION
+krb5_get_init_creds_opt_set_tkt_life(krb5_get_init_creds_opt *opt,
+ krb5_deltat tkt_life)
+{
+ opt->flags |= KRB5_GET_INIT_CREDS_OPT_TKT_LIFE;
+ opt->tkt_life = tkt_life;
+}
+
+void KRB5_LIB_FUNCTION
+krb5_get_init_creds_opt_set_renew_life(krb5_get_init_creds_opt *opt,
+ krb5_deltat renew_life)
+{
+ opt->flags |= KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE;
+ opt->renew_life = renew_life;
+}
+
+void KRB5_LIB_FUNCTION
+krb5_get_init_creds_opt_set_forwardable(krb5_get_init_creds_opt *opt,
+ int forwardable)
+{
+ opt->flags |= KRB5_GET_INIT_CREDS_OPT_FORWARDABLE;
+ opt->forwardable = forwardable;
+}
+
+void KRB5_LIB_FUNCTION
+krb5_get_init_creds_opt_set_proxiable(krb5_get_init_creds_opt *opt,
+ int proxiable)
+{
+ opt->flags |= KRB5_GET_INIT_CREDS_OPT_PROXIABLE;
+ opt->proxiable = proxiable;
+}
+
+void KRB5_LIB_FUNCTION
+krb5_get_init_creds_opt_set_etype_list(krb5_get_init_creds_opt *opt,
+ krb5_enctype *etype_list,
+ int etype_list_length)
+{
+ opt->flags |= KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST;
+ opt->etype_list = etype_list;
+ opt->etype_list_length = etype_list_length;
+}
+
+void KRB5_LIB_FUNCTION
+krb5_get_init_creds_opt_set_address_list(krb5_get_init_creds_opt *opt,
+ krb5_addresses *addresses)
+{
+ opt->flags |= KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST;
+ opt->address_list = addresses;
+}
+
+void KRB5_LIB_FUNCTION
+krb5_get_init_creds_opt_set_preauth_list(krb5_get_init_creds_opt *opt,
+ krb5_preauthtype *preauth_list,
+ int preauth_list_length)
+{
+ opt->flags |= KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST;
+ opt->preauth_list_length = preauth_list_length;
+ opt->preauth_list = preauth_list;
+}
+
+void KRB5_LIB_FUNCTION
+krb5_get_init_creds_opt_set_salt(krb5_get_init_creds_opt *opt,
+ krb5_data *salt)
+{
+ opt->flags |= KRB5_GET_INIT_CREDS_OPT_SALT;
+ opt->salt = salt;
+}
+
+void KRB5_LIB_FUNCTION
+krb5_get_init_creds_opt_set_anonymous(krb5_get_init_creds_opt *opt,
+ int anonymous)
+{
+ opt->flags |= KRB5_GET_INIT_CREDS_OPT_ANONYMOUS;
+ opt->anonymous = anonymous;
+}
+
+static krb5_error_code
+require_ext_opt(krb5_context context,
+ krb5_get_init_creds_opt *opt,
+ const char *type)
+{
+ if (opt->opt_private == NULL) {
+ krb5_set_error_message(context, EINVAL,
+ N_("%s on non extendable opt", ""), type);
+ return EINVAL;
+ }
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_init_creds_opt_set_pa_password(krb5_context context,
+ krb5_get_init_creds_opt *opt,
+ const char *password,
+ krb5_s2k_proc key_proc)
+{
+ krb5_error_code ret;
+ ret = require_ext_opt(context, opt, "init_creds_opt_set_pa_password");
+ if (ret)
+ return ret;
+ opt->opt_private->password = password;
+ opt->opt_private->key_proc = key_proc;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_init_creds_opt_set_pac_request(krb5_context context,
+ krb5_get_init_creds_opt *opt,
+ krb5_boolean req_pac)
+{
+ krb5_error_code ret;
+ ret = require_ext_opt(context, opt, "init_creds_opt_set_pac_req");
+ if (ret)
+ return ret;
+ opt->opt_private->req_pac = req_pac ?
+ KRB5_INIT_CREDS_TRISTATE_TRUE :
+ KRB5_INIT_CREDS_TRISTATE_FALSE;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_init_creds_opt_get_error(krb5_context context,
+ krb5_get_init_creds_opt *opt,
+ KRB_ERROR **error)
+{
+ krb5_error_code ret;
+
+ *error = NULL;
+
+ ret = require_ext_opt(context, opt, "init_creds_opt_get_error");
+ if (ret)
+ return ret;
+
+ if (opt->opt_private->error == NULL)
+ return 0;
+
+ *error = malloc(sizeof(**error));
+ if (*error == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+
+ ret = copy_KRB_ERROR(opt->opt_private->error, *error);
+ if (ret)
+ krb5_clear_error_message(context);
+
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_init_creds_opt_set_addressless(krb5_context context,
+ krb5_get_init_creds_opt *opt,
+ krb5_boolean addressless)
+{
+ krb5_error_code ret;
+ ret = require_ext_opt(context, opt, "init_creds_opt_set_pac_req");
+ if (ret)
+ return ret;
+ if (addressless)
+ opt->opt_private->addressless = KRB5_INIT_CREDS_TRISTATE_TRUE;
+ else
+ opt->opt_private->addressless = KRB5_INIT_CREDS_TRISTATE_FALSE;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_init_creds_opt_set_canonicalize(krb5_context context,
+ krb5_get_init_creds_opt *opt,
+ krb5_boolean req)
+{
+ krb5_error_code ret;
+ ret = require_ext_opt(context, opt, "init_creds_opt_set_canonicalize");
+ if (ret)
+ return ret;
+ if (req)
+ opt->opt_private->flags |= KRB5_INIT_CREDS_CANONICALIZE;
+ else
+ opt->opt_private->flags &= ~KRB5_INIT_CREDS_CANONICALIZE;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_init_creds_opt_set_win2k(krb5_context context,
+ krb5_get_init_creds_opt *opt,
+ krb5_boolean req)
+{
+ krb5_error_code ret;
+ ret = require_ext_opt(context, opt, "init_creds_opt_set_win2k");
+ if (ret)
+ return ret;
+ if (req)
+ opt->opt_private->flags |= KRB5_INIT_CREDS_NO_C_CANON_CHECK;
+ else
+ opt->opt_private->flags &= ~KRB5_INIT_CREDS_NO_C_CANON_CHECK;
+ return 0;
+}
+
diff --git a/source4/heimdal/lib/krb5/init_creds_pw.c b/source4/heimdal/lib/krb5/init_creds_pw.c
new file mode 100644
index 0000000000..0b75522e9d
--- /dev/null
+++ b/source4/heimdal/lib/krb5/init_creds_pw.c
@@ -0,0 +1,1604 @@
+/*
+ * Copyright (c) 1997 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+typedef struct krb5_get_init_creds_ctx {
+ KDCOptions flags;
+ krb5_creds cred;
+ krb5_addresses *addrs;
+ krb5_enctype *etypes;
+ krb5_preauthtype *pre_auth_types;
+ const char *in_tkt_service;
+ unsigned nonce;
+ unsigned pk_nonce;
+
+ krb5_data req_buffer;
+ AS_REQ as_req;
+ int pa_counter;
+
+ const char *password;
+ krb5_s2k_proc key_proc;
+
+ krb5_get_init_creds_tristate req_pac;
+
+ krb5_pk_init_ctx pk_init_ctx;
+ int ic_flags;
+} krb5_get_init_creds_ctx;
+
+static krb5_error_code
+default_s2k_func(krb5_context context, krb5_enctype type,
+ krb5_const_pointer keyseed,
+ krb5_salt salt, krb5_data *s2kparms,
+ krb5_keyblock **key)
+{
+ krb5_error_code ret;
+ krb5_data password;
+ krb5_data opaque;
+
+ password.data = rk_UNCONST(keyseed);
+ password.length = strlen(keyseed);
+ if (s2kparms)
+ opaque = *s2kparms;
+ else
+ krb5_data_zero(&opaque);
+
+ *key = malloc(sizeof(**key));
+ if (*key == NULL)
+ return ENOMEM;
+ ret = krb5_string_to_key_data_salt_opaque(context, type, password,
+ salt, opaque, *key);
+ if (ret) {
+ free(*key);
+ *key = NULL;
+ }
+ return ret;
+}
+
+static void
+free_init_creds_ctx(krb5_context context, krb5_get_init_creds_ctx *ctx)
+{
+ if (ctx->etypes)
+ free(ctx->etypes);
+ if (ctx->pre_auth_types)
+ free (ctx->pre_auth_types);
+ free_AS_REQ(&ctx->as_req);
+ memset(&ctx->as_req, 0, sizeof(ctx->as_req));
+}
+
+static int
+get_config_time (krb5_context context,
+ const char *realm,
+ const char *name,
+ int def)
+{
+ int ret;
+
+ ret = krb5_config_get_time (context, NULL,
+ "realms",
+ realm,
+ name,
+ NULL);
+ if (ret >= 0)
+ return ret;
+ ret = krb5_config_get_time (context, NULL,
+ "libdefaults",
+ name,
+ NULL);
+ if (ret >= 0)
+ return ret;
+ return def;
+}
+
+static krb5_error_code
+init_cred (krb5_context context,
+ krb5_creds *cred,
+ krb5_principal client,
+ krb5_deltat start_time,
+ const char *in_tkt_service,
+ krb5_get_init_creds_opt *options)
+{
+ krb5_error_code ret;
+ krb5_const_realm client_realm;
+ int tmp;
+ krb5_timestamp now;
+
+ krb5_timeofday (context, &now);
+
+ memset (cred, 0, sizeof(*cred));
+
+ if (client)
+ krb5_copy_principal(context, client, &cred->client);
+ else {
+ ret = krb5_get_default_principal (context,
+ &cred->client);
+ if (ret)
+ goto out;
+ }
+
+ client_realm = krb5_principal_get_realm (context, cred->client);
+
+ if (start_time)
+ cred->times.starttime = now + start_time;
+
+ if (options->flags & KRB5_GET_INIT_CREDS_OPT_TKT_LIFE)
+ tmp = options->tkt_life;
+ else
+ tmp = 10 * 60 * 60;
+ cred->times.endtime = now + tmp;
+
+ if ((options->flags & KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE) &&
+ options->renew_life > 0) {
+ cred->times.renew_till = now + options->renew_life;
+ }
+
+ if (in_tkt_service) {
+ ret = krb5_parse_name (context, in_tkt_service, &cred->server);
+ if (ret)
+ goto out;
+ krb5_principal_set_realm (context, cred->server, client_realm);
+ } else {
+ ret = krb5_make_principal(context, &cred->server,
+ client_realm, KRB5_TGS_NAME, client_realm,
+ NULL);
+ if (ret)
+ goto out;
+ }
+ return 0;
+
+out:
+ krb5_free_cred_contents (context, cred);
+ return ret;
+}
+
+/*
+ * Print a message (str) to the user about the expiration in `lr'
+ */
+
+static void
+report_expiration (krb5_context context,
+ krb5_prompter_fct prompter,
+ krb5_data *data,
+ const char *str,
+ time_t now)
+{
+ char *p;
+
+ asprintf (&p, "%s%s", str, ctime(&now));
+ (*prompter) (context, data, NULL, p, 0, NULL);
+ free (p);
+}
+
+/*
+ * Parse the last_req data and show it to the user if it's interesting
+ */
+
+static void
+print_expire (krb5_context context,
+ krb5_const_realm realm,
+ krb5_kdc_rep *rep,
+ krb5_prompter_fct prompter,
+ krb5_data *data)
+{
+ int i;
+ LastReq *lr = &rep->enc_part.last_req;
+ krb5_timestamp sec;
+ time_t t;
+ krb5_boolean reported = FALSE;
+
+ krb5_timeofday (context, &sec);
+
+ t = sec + get_config_time (context,
+ realm,
+ "warn_pwexpire",
+ 7 * 24 * 60 * 60);
+
+ for (i = 0; i < lr->len; ++i) {
+ if (lr->val[i].lr_value <= t) {
+ switch (abs(lr->val[i].lr_type)) {
+ case LR_PW_EXPTIME :
+ report_expiration(context, prompter, data,
+ "Your password will expire at ",
+ lr->val[i].lr_value);
+ reported = TRUE;
+ break;
+ case LR_ACCT_EXPTIME :
+ report_expiration(context, prompter, data,
+ "Your account will expire at ",
+ lr->val[i].lr_value);
+ reported = TRUE;
+ break;
+ }
+ }
+ }
+
+ if (!reported
+ && rep->enc_part.key_expiration
+ && *rep->enc_part.key_expiration <= t) {
+ report_expiration(context, prompter, data,
+ "Your password/account will expire at ",
+ *rep->enc_part.key_expiration);
+ }
+}
+
+static krb5_addresses no_addrs = { 0, NULL };
+
+static krb5_error_code
+get_init_creds_common(krb5_context context,
+ krb5_principal client,
+ krb5_deltat start_time,
+ const char *in_tkt_service,
+ krb5_get_init_creds_opt *options,
+ krb5_get_init_creds_ctx *ctx)
+{
+ krb5_get_init_creds_opt default_opt;
+ krb5_error_code ret;
+ krb5_enctype *etypes;
+ krb5_preauthtype *pre_auth_types;
+
+ memset(ctx, 0, sizeof(*ctx));
+
+ if (options == NULL) {
+ krb5_get_init_creds_opt_init (&default_opt);
+ options = &default_opt;
+ } else {
+ _krb5_get_init_creds_opt_free_krb5_error(options);
+ }
+
+ if (options->opt_private) {
+ ctx->password = options->opt_private->password;
+ ctx->key_proc = options->opt_private->key_proc;
+ ctx->req_pac = options->opt_private->req_pac;
+ ctx->pk_init_ctx = options->opt_private->pk_init_ctx;
+ ctx->ic_flags = options->opt_private->flags;
+ } else
+ ctx->req_pac = KRB5_INIT_CREDS_TRISTATE_UNSET;
+
+ if (ctx->key_proc == NULL)
+ ctx->key_proc = default_s2k_func;
+
+ if (ctx->ic_flags & KRB5_INIT_CREDS_CANONICALIZE)
+ ctx->flags.canonicalize = 1;
+
+ ctx->pre_auth_types = NULL;
+ ctx->addrs = NULL;
+ ctx->etypes = NULL;
+ ctx->pre_auth_types = NULL;
+ ctx->in_tkt_service = in_tkt_service;
+
+ ret = init_cred (context, &ctx->cred, client, start_time,
+ in_tkt_service, options);
+ if (ret)
+ return ret;
+
+ if (options->flags & KRB5_GET_INIT_CREDS_OPT_FORWARDABLE)
+ ctx->flags.forwardable = options->forwardable;
+
+ if (options->flags & KRB5_GET_INIT_CREDS_OPT_PROXIABLE)
+ ctx->flags.proxiable = options->proxiable;
+
+ if (start_time)
+ ctx->flags.postdated = 1;
+ if (ctx->cred.times.renew_till)
+ ctx->flags.renewable = 1;
+ if (options->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST) {
+ ctx->addrs = options->address_list;
+ } else if (options->opt_private) {
+ switch (options->opt_private->addressless) {
+ case KRB5_INIT_CREDS_TRISTATE_UNSET:
+#if KRB5_ADDRESSLESS_DEFAULT == TRUE
+ ctx->addrs = &no_addrs;
+#else
+ ctx->addrs = NULL;
+#endif
+ break;
+ case KRB5_INIT_CREDS_TRISTATE_FALSE:
+ ctx->addrs = NULL;
+ break;
+ case KRB5_INIT_CREDS_TRISTATE_TRUE:
+ ctx->addrs = &no_addrs;
+ break;
+ }
+ }
+ if (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST) {
+ etypes = malloc((options->etype_list_length + 1)
+ * sizeof(krb5_enctype));
+ if (etypes == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ memcpy (etypes, options->etype_list,
+ options->etype_list_length * sizeof(krb5_enctype));
+ etypes[options->etype_list_length] = ETYPE_NULL;
+ ctx->etypes = etypes;
+ }
+ if (options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST) {
+ pre_auth_types = malloc((options->preauth_list_length + 1)
+ * sizeof(krb5_preauthtype));
+ if (pre_auth_types == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ memcpy (pre_auth_types, options->preauth_list,
+ options->preauth_list_length * sizeof(krb5_preauthtype));
+ pre_auth_types[options->preauth_list_length] = KRB5_PADATA_NONE;
+ ctx->pre_auth_types = pre_auth_types;
+ }
+ if (options->flags & KRB5_GET_INIT_CREDS_OPT_SALT)
+ ; /* XXX */
+ if (options->flags & KRB5_GET_INIT_CREDS_OPT_ANONYMOUS)
+ ctx->flags.request_anonymous = options->anonymous;
+ return 0;
+}
+
+static krb5_error_code
+change_password (krb5_context context,
+ krb5_principal client,
+ const char *password,
+ char *newpw,
+ size_t newpw_sz,
+ krb5_prompter_fct prompter,
+ void *data,
+ krb5_get_init_creds_opt *old_options)
+{
+ krb5_prompt prompts[2];
+ krb5_error_code ret;
+ krb5_creds cpw_cred;
+ char buf1[BUFSIZ], buf2[BUFSIZ];
+ krb5_data password_data[2];
+ int result_code;
+ krb5_data result_code_string;
+ krb5_data result_string;
+ char *p;
+ krb5_get_init_creds_opt options;
+
+ memset (&cpw_cred, 0, sizeof(cpw_cred));
+
+ krb5_get_init_creds_opt_init (&options);
+ krb5_get_init_creds_opt_set_tkt_life (&options, 60);
+ krb5_get_init_creds_opt_set_forwardable (&options, FALSE);
+ krb5_get_init_creds_opt_set_proxiable (&options, FALSE);
+ if (old_options && old_options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST)
+ krb5_get_init_creds_opt_set_preauth_list (&options,
+ old_options->preauth_list,
+ old_options->preauth_list_length);
+
+ krb5_data_zero (&result_code_string);
+ krb5_data_zero (&result_string);
+
+ ret = krb5_get_init_creds_password (context,
+ &cpw_cred,
+ client,
+ password,
+ prompter,
+ data,
+ 0,
+ "kadmin/changepw",
+ &options);
+ if (ret)
+ goto out;
+
+ for(;;) {
+ password_data[0].data = buf1;
+ password_data[0].length = sizeof(buf1);
+
+ prompts[0].hidden = 1;
+ prompts[0].prompt = "New password: ";
+ prompts[0].reply = &password_data[0];
+ prompts[0].type = KRB5_PROMPT_TYPE_NEW_PASSWORD;
+
+ password_data[1].data = buf2;
+ password_data[1].length = sizeof(buf2);
+
+ prompts[1].hidden = 1;
+ prompts[1].prompt = "Repeat new password: ";
+ prompts[1].reply = &password_data[1];
+ prompts[1].type = KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN;
+
+ ret = (*prompter) (context, data, NULL, "Changing password",
+ 2, prompts);
+ if (ret) {
+ memset (buf1, 0, sizeof(buf1));
+ memset (buf2, 0, sizeof(buf2));
+ goto out;
+ }
+
+ if (strcmp (buf1, buf2) == 0)
+ break;
+ memset (buf1, 0, sizeof(buf1));
+ memset (buf2, 0, sizeof(buf2));
+ }
+
+ ret = krb5_set_password (context,
+ &cpw_cred,
+ buf1,
+ client,
+ &result_code,
+ &result_code_string,
+ &result_string);
+ if (ret)
+ goto out;
+ asprintf (&p, "%s: %.*s\n",
+ result_code ? "Error" : "Success",
+ (int)result_string.length,
+ result_string.length > 0 ? (char*)result_string.data : "");
+
+ ret = (*prompter) (context, data, NULL, p, 0, NULL);
+ free (p);
+ if (result_code == 0) {
+ strlcpy (newpw, buf1, newpw_sz);
+ ret = 0;
+ } else {
+ ret = ENOTTY;
+ krb5_set_error_message(context, ret,
+ N_("failed changing password", ""));
+ }
+
+out:
+ memset (buf1, 0, sizeof(buf1));
+ memset (buf2, 0, sizeof(buf2));
+ krb5_data_free (&result_string);
+ krb5_data_free (&result_code_string);
+ krb5_free_cred_contents (context, &cpw_cred);
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_keyblock_key_proc (krb5_context context,
+ krb5_keytype type,
+ krb5_data *salt,
+ krb5_const_pointer keyseed,
+ krb5_keyblock **key)
+{
+ return krb5_copy_keyblock (context, keyseed, key);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_init_creds_keytab(krb5_context context,
+ krb5_creds *creds,
+ krb5_principal client,
+ krb5_keytab keytab,
+ krb5_deltat start_time,
+ const char *in_tkt_service,
+ krb5_get_init_creds_opt *options)
+{
+ krb5_get_init_creds_ctx ctx;
+ krb5_error_code ret;
+ krb5_keytab_key_proc_args *a;
+
+ ret = get_init_creds_common(context, client, start_time,
+ in_tkt_service, options, &ctx);
+ if (ret)
+ goto out;
+
+ a = malloc (sizeof(*a));
+ if (a == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ goto out;
+ }
+ a->principal = ctx.cred.client;
+ a->keytab = keytab;
+
+ ret = krb5_get_in_cred (context,
+ KDCOptions2int(ctx.flags),
+ ctx.addrs,
+ ctx.etypes,
+ ctx.pre_auth_types,
+ NULL,
+ krb5_keytab_key_proc,
+ a,
+ NULL,
+ NULL,
+ &ctx.cred,
+ NULL);
+ free (a);
+
+ if (ret == 0 && creds)
+ *creds = ctx.cred;
+ else
+ krb5_free_cred_contents (context, &ctx.cred);
+
+ out:
+ free_init_creds_ctx(context, &ctx);
+ return ret;
+}
+
+/*
+ *
+ */
+
+static krb5_error_code
+init_creds_init_as_req (krb5_context context,
+ KDCOptions opts,
+ const krb5_creds *creds,
+ const krb5_addresses *addrs,
+ const krb5_enctype *etypes,
+ AS_REQ *a)
+{
+ krb5_error_code ret;
+
+ memset(a, 0, sizeof(*a));
+
+ a->pvno = 5;
+ a->msg_type = krb_as_req;
+ a->req_body.kdc_options = opts;
+ a->req_body.cname = malloc(sizeof(*a->req_body.cname));
+ if (a->req_body.cname == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ goto fail;
+ }
+ a->req_body.sname = malloc(sizeof(*a->req_body.sname));
+ if (a->req_body.sname == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ goto fail;
+ }
+
+ ret = _krb5_principal2principalname (a->req_body.cname, creds->client);
+ if (ret)
+ goto fail;
+ ret = copy_Realm(&creds->client->realm, &a->req_body.realm);
+ if (ret)
+ goto fail;
+
+ ret = _krb5_principal2principalname (a->req_body.sname, creds->server);
+ if (ret)
+ goto fail;
+
+ if(creds->times.starttime) {
+ a->req_body.from = malloc(sizeof(*a->req_body.from));
+ if (a->req_body.from == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ goto fail;
+ }
+ *a->req_body.from = creds->times.starttime;
+ }
+ if(creds->times.endtime){
+ ALLOC(a->req_body.till, 1);
+ *a->req_body.till = creds->times.endtime;
+ }
+ if(creds->times.renew_till){
+ a->req_body.rtime = malloc(sizeof(*a->req_body.rtime));
+ if (a->req_body.rtime == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ goto fail;
+ }
+ *a->req_body.rtime = creds->times.renew_till;
+ }
+ a->req_body.nonce = 0;
+ ret = krb5_init_etype (context,
+ &a->req_body.etype.len,
+ &a->req_body.etype.val,
+ etypes);
+ if (ret)
+ goto fail;
+
+ /*
+ * This means no addresses
+ */
+
+ if (addrs && addrs->len == 0) {
+ a->req_body.addresses = NULL;
+ } else {
+ a->req_body.addresses = malloc(sizeof(*a->req_body.addresses));
+ if (a->req_body.addresses == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ goto fail;
+ }
+
+ if (addrs)
+ ret = krb5_copy_addresses(context, addrs, a->req_body.addresses);
+ else {
+ ret = krb5_get_all_client_addrs (context, a->req_body.addresses);
+ if(ret == 0 && a->req_body.addresses->len == 0) {
+ free(a->req_body.addresses);
+ a->req_body.addresses = NULL;
+ }
+ }
+ if (ret)
+ goto fail;
+ }
+
+ a->req_body.enc_authorization_data = NULL;
+ a->req_body.additional_tickets = NULL;
+
+ a->padata = NULL;
+
+ return 0;
+ fail:
+ free_AS_REQ(a);
+ memset(a, 0, sizeof(*a));
+ return ret;
+}
+
+struct pa_info_data {
+ krb5_enctype etype;
+ krb5_salt salt;
+ krb5_data *s2kparams;
+};
+
+static void
+free_paid(krb5_context context, struct pa_info_data *ppaid)
+{
+ krb5_free_salt(context, ppaid->salt);
+ if (ppaid->s2kparams)
+ krb5_free_data(context, ppaid->s2kparams);
+}
+
+
+static krb5_error_code
+set_paid(struct pa_info_data *paid, krb5_context context,
+ krb5_enctype etype,
+ krb5_salttype salttype, void *salt_string, size_t salt_len,
+ krb5_data *s2kparams)
+{
+ paid->etype = etype;
+ paid->salt.salttype = salttype;
+ paid->salt.saltvalue.data = malloc(salt_len + 1);
+ if (paid->salt.saltvalue.data == NULL) {
+ krb5_clear_error_message(context);
+ return ENOMEM;
+ }
+ memcpy(paid->salt.saltvalue.data, salt_string, salt_len);
+ ((char *)paid->salt.saltvalue.data)[salt_len] = '\0';
+ paid->salt.saltvalue.length = salt_len;
+ if (s2kparams) {
+ krb5_error_code ret;
+
+ ret = krb5_copy_data(context, s2kparams, &paid->s2kparams);
+ if (ret) {
+ krb5_clear_error_message(context);
+ krb5_free_salt(context, paid->salt);
+ return ret;
+ }
+ } else
+ paid->s2kparams = NULL;
+
+ return 0;
+}
+
+static struct pa_info_data *
+pa_etype_info2(krb5_context context,
+ const krb5_principal client,
+ const AS_REQ *asreq,
+ struct pa_info_data *paid,
+ heim_octet_string *data)
+{
+ krb5_error_code ret;
+ ETYPE_INFO2 e;
+ size_t sz;
+ int i, j;
+
+ memset(&e, 0, sizeof(e));
+ ret = decode_ETYPE_INFO2(data->data, data->length, &e, &sz);
+ if (ret)
+ goto out;
+ if (e.len == 0)
+ goto out;
+ for (j = 0; j < asreq->req_body.etype.len; j++) {
+ for (i = 0; i < e.len; i++) {
+ if (asreq->req_body.etype.val[j] == e.val[i].etype) {
+ krb5_salt salt;
+ if (e.val[i].salt == NULL)
+ ret = krb5_get_pw_salt(context, client, &salt);
+ else {
+ salt.saltvalue.data = *e.val[i].salt;
+ salt.saltvalue.length = strlen(*e.val[i].salt);
+ ret = 0;
+ }
+ if (ret == 0)
+ ret = set_paid(paid, context, e.val[i].etype,
+ KRB5_PW_SALT,
+ salt.saltvalue.data,
+ salt.saltvalue.length,
+ e.val[i].s2kparams);
+ if (e.val[i].salt == NULL)
+ krb5_free_salt(context, salt);
+ if (ret == 0) {
+ free_ETYPE_INFO2(&e);
+ return paid;
+ }
+ }
+ }
+ }
+ out:
+ free_ETYPE_INFO2(&e);
+ return NULL;
+}
+
+static struct pa_info_data *
+pa_etype_info(krb5_context context,
+ const krb5_principal client,
+ const AS_REQ *asreq,
+ struct pa_info_data *paid,
+ heim_octet_string *data)
+{
+ krb5_error_code ret;
+ ETYPE_INFO e;
+ size_t sz;
+ int i, j;
+
+ memset(&e, 0, sizeof(e));
+ ret = decode_ETYPE_INFO(data->data, data->length, &e, &sz);
+ if (ret)
+ goto out;
+ if (e.len == 0)
+ goto out;
+ for (j = 0; j < asreq->req_body.etype.len; j++) {
+ for (i = 0; i < e.len; i++) {
+ if (asreq->req_body.etype.val[j] == e.val[i].etype) {
+ krb5_salt salt;
+ salt.salttype = KRB5_PW_SALT;
+ if (e.val[i].salt == NULL)
+ ret = krb5_get_pw_salt(context, client, &salt);
+ else {
+ salt.saltvalue = *e.val[i].salt;
+ ret = 0;
+ }
+ if (e.val[i].salttype)
+ salt.salttype = *e.val[i].salttype;
+ if (ret == 0) {
+ ret = set_paid(paid, context, e.val[i].etype,
+ salt.salttype,
+ salt.saltvalue.data,
+ salt.saltvalue.length,
+ NULL);
+ if (e.val[i].salt == NULL)
+ krb5_free_salt(context, salt);
+ }
+ if (ret == 0) {
+ free_ETYPE_INFO(&e);
+ return paid;
+ }
+ }
+ }
+ }
+ out:
+ free_ETYPE_INFO(&e);
+ return NULL;
+}
+
+static struct pa_info_data *
+pa_pw_or_afs3_salt(krb5_context context,
+ const krb5_principal client,
+ const AS_REQ *asreq,
+ struct pa_info_data *paid,
+ heim_octet_string *data)
+{
+ krb5_error_code ret;
+ if (paid->etype == ENCTYPE_NULL)
+ return NULL;
+ ret = set_paid(paid, context,
+ paid->etype,
+ paid->salt.salttype,
+ data->data,
+ data->length,
+ NULL);
+ if (ret)
+ return NULL;
+ return paid;
+}
+
+
+struct pa_info {
+ krb5_preauthtype type;
+ struct pa_info_data *(*salt_info)(krb5_context,
+ const krb5_principal,
+ const AS_REQ *,
+ struct pa_info_data *,
+ heim_octet_string *);
+};
+
+static struct pa_info pa_prefs[] = {
+ { KRB5_PADATA_ETYPE_INFO2, pa_etype_info2 },
+ { KRB5_PADATA_ETYPE_INFO, pa_etype_info },
+ { KRB5_PADATA_PW_SALT, pa_pw_or_afs3_salt },
+ { KRB5_PADATA_AFS3_SALT, pa_pw_or_afs3_salt }
+};
+
+static PA_DATA *
+find_pa_data(const METHOD_DATA *md, int type)
+{
+ int i;
+ if (md == NULL)
+ return NULL;
+ for (i = 0; i < md->len; i++)
+ if (md->val[i].padata_type == type)
+ return &md->val[i];
+ return NULL;
+}
+
+static struct pa_info_data *
+process_pa_info(krb5_context context,
+ const krb5_principal client,
+ const AS_REQ *asreq,
+ struct pa_info_data *paid,
+ METHOD_DATA *md)
+{
+ struct pa_info_data *p = NULL;
+ int i;
+
+ for (i = 0; p == NULL && i < sizeof(pa_prefs)/sizeof(pa_prefs[0]); i++) {
+ PA_DATA *pa = find_pa_data(md, pa_prefs[i].type);
+ if (pa == NULL)
+ continue;
+ paid->salt.salttype = pa_prefs[i].type;
+ p = (*pa_prefs[i].salt_info)(context, client, asreq,
+ paid, &pa->padata_value);
+ }
+ return p;
+}
+
+static krb5_error_code
+make_pa_enc_timestamp(krb5_context context, METHOD_DATA *md,
+ krb5_enctype etype, krb5_keyblock *key)
+{
+ PA_ENC_TS_ENC p;
+ unsigned char *buf;
+ size_t buf_size;
+ size_t len;
+ EncryptedData encdata;
+ krb5_error_code ret;
+ int32_t usec;
+ int usec2;
+ krb5_crypto crypto;
+
+ krb5_us_timeofday (context, &p.patimestamp, &usec);
+ usec2 = usec;
+ p.pausec = &usec2;
+
+ ASN1_MALLOC_ENCODE(PA_ENC_TS_ENC, buf, buf_size, &p, &len, ret);
+ if (ret)
+ return ret;
+ if(buf_size != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret) {
+ free(buf);
+ return ret;
+ }
+ ret = krb5_encrypt_EncryptedData(context,
+ crypto,
+ KRB5_KU_PA_ENC_TIMESTAMP,
+ buf,
+ len,
+ 0,
+ &encdata);
+ free(buf);
+ krb5_crypto_destroy(context, crypto);
+ if (ret)
+ return ret;
+
+ ASN1_MALLOC_ENCODE(EncryptedData, buf, buf_size, &encdata, &len, ret);
+ free_EncryptedData(&encdata);
+ if (ret)
+ return ret;
+ if(buf_size != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+
+ ret = krb5_padata_add(context, md, KRB5_PADATA_ENC_TIMESTAMP, buf, len);
+ if (ret)
+ free(buf);
+ return ret;
+}
+
+static krb5_error_code
+add_enc_ts_padata(krb5_context context,
+ METHOD_DATA *md,
+ krb5_principal client,
+ krb5_s2k_proc key_proc,
+ krb5_const_pointer keyseed,
+ krb5_enctype *enctypes,
+ unsigned netypes,
+ krb5_salt *salt,
+ krb5_data *s2kparams)
+{
+ krb5_error_code ret;
+ krb5_salt salt2;
+ krb5_enctype *ep;
+ int i;
+
+ if(salt == NULL) {
+ /* default to standard salt */
+ ret = krb5_get_pw_salt (context, client, &salt2);
+ salt = &salt2;
+ }
+ if (!enctypes) {
+ enctypes = context->etypes;
+ netypes = 0;
+ for (ep = enctypes; *ep != ETYPE_NULL; ep++)
+ netypes++;
+ }
+
+ for (i = 0; i < netypes; ++i) {
+ krb5_keyblock *key;
+
+ ret = (*key_proc)(context, enctypes[i], keyseed,
+ *salt, s2kparams, &key);
+ if (ret)
+ continue;
+ ret = make_pa_enc_timestamp (context, md, enctypes[i], key);
+ krb5_free_keyblock (context, key);
+ if (ret)
+ return ret;
+ }
+ if(salt == &salt2)
+ krb5_free_salt(context, salt2);
+ return 0;
+}
+
+static krb5_error_code
+pa_data_to_md_ts_enc(krb5_context context,
+ const AS_REQ *a,
+ const krb5_principal client,
+ krb5_get_init_creds_ctx *ctx,
+ struct pa_info_data *ppaid,
+ METHOD_DATA *md)
+{
+ if (ctx->key_proc == NULL || ctx->password == NULL)
+ return 0;
+
+ if (ppaid) {
+ add_enc_ts_padata(context, md, client,
+ ctx->key_proc, ctx->password,
+ &ppaid->etype, 1,
+ &ppaid->salt, ppaid->s2kparams);
+ } else {
+ krb5_salt salt;
+
+ /* make a v5 salted pa-data */
+ add_enc_ts_padata(context, md, client,
+ ctx->key_proc, ctx->password,
+ a->req_body.etype.val, a->req_body.etype.len,
+ NULL, NULL);
+
+ /* make a v4 salted pa-data */
+ salt.salttype = KRB5_PW_SALT;
+ krb5_data_zero(&salt.saltvalue);
+ add_enc_ts_padata(context, md, client,
+ ctx->key_proc, ctx->password,
+ a->req_body.etype.val, a->req_body.etype.len,
+ &salt, NULL);
+ }
+ return 0;
+}
+
+static krb5_error_code
+pa_data_to_key_plain(krb5_context context,
+ const krb5_principal client,
+ krb5_get_init_creds_ctx *ctx,
+ krb5_salt salt,
+ krb5_data *s2kparams,
+ krb5_enctype etype,
+ krb5_keyblock **key)
+{
+ krb5_error_code ret;
+
+ ret = (*ctx->key_proc)(context, etype, ctx->password,
+ salt, s2kparams, key);
+ return ret;
+}
+
+
+static krb5_error_code
+pa_data_to_md_pkinit(krb5_context context,
+ const AS_REQ *a,
+ const krb5_principal client,
+ krb5_get_init_creds_ctx *ctx,
+ METHOD_DATA *md)
+{
+ if (ctx->pk_init_ctx == NULL)
+ return 0;
+#ifdef PKINIT
+ return _krb5_pk_mk_padata(context,
+ ctx->pk_init_ctx,
+ &a->req_body,
+ ctx->pk_nonce,
+ md);
+#else
+ krb5_set_error_message(context, EINVAL,
+ N_("no support for PKINIT compiled in", ""));
+ return EINVAL;
+#endif
+}
+
+static krb5_error_code
+pa_data_add_pac_request(krb5_context context,
+ krb5_get_init_creds_ctx *ctx,
+ METHOD_DATA *md)
+{
+ size_t len, length;
+ krb5_error_code ret;
+ PA_PAC_REQUEST req;
+ void *buf;
+
+ switch (ctx->req_pac) {
+ case KRB5_INIT_CREDS_TRISTATE_UNSET:
+ return 0; /* don't bother */
+ case KRB5_INIT_CREDS_TRISTATE_TRUE:
+ req.include_pac = 1;
+ break;
+ case KRB5_INIT_CREDS_TRISTATE_FALSE:
+ req.include_pac = 0;
+ }
+
+ ASN1_MALLOC_ENCODE(PA_PAC_REQUEST, buf, length,
+ &req, &len, ret);
+ if (ret)
+ return ret;
+ if(len != length)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+
+ ret = krb5_padata_add(context, md, KRB5_PADATA_PA_PAC_REQUEST, buf, len);
+ if (ret)
+ free(buf);
+
+ return 0;
+}
+
+/*
+ * Assumes caller always will free `out_md', even on error.
+ */
+
+static krb5_error_code
+process_pa_data_to_md(krb5_context context,
+ const krb5_creds *creds,
+ const AS_REQ *a,
+ krb5_get_init_creds_ctx *ctx,
+ METHOD_DATA *in_md,
+ METHOD_DATA **out_md,
+ krb5_prompter_fct prompter,
+ void *prompter_data)
+{
+ krb5_error_code ret;
+
+ ALLOC(*out_md, 1);
+ if (*out_md == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ (*out_md)->len = 0;
+ (*out_md)->val = NULL;
+
+ /*
+ * Make sure we don't sent both ENC-TS and PK-INIT pa data, no
+ * need to expose our password protecting our PKCS12 key.
+ */
+
+ if (ctx->pk_init_ctx) {
+
+ ret = pa_data_to_md_pkinit(context, a, creds->client, ctx, *out_md);
+ if (ret)
+ return ret;
+
+ } else if (in_md->len != 0) {
+ struct pa_info_data paid, *ppaid;
+
+ memset(&paid, 0, sizeof(paid));
+
+ paid.etype = ENCTYPE_NULL;
+ ppaid = process_pa_info(context, creds->client, a, &paid, in_md);
+
+ pa_data_to_md_ts_enc(context, a, creds->client, ctx, ppaid, *out_md);
+ if (ppaid)
+ free_paid(context, ppaid);
+ }
+
+ pa_data_add_pac_request(context, ctx, *out_md);
+
+ if ((*out_md)->len == 0) {
+ free(*out_md);
+ *out_md = NULL;
+ }
+
+ return 0;
+}
+
+static krb5_error_code
+process_pa_data_to_key(krb5_context context,
+ krb5_get_init_creds_ctx *ctx,
+ krb5_creds *creds,
+ AS_REQ *a,
+ krb5_kdc_rep *rep,
+ const krb5_krbhst_info *hi,
+ krb5_keyblock **key)
+{
+ struct pa_info_data paid, *ppaid = NULL;
+ krb5_error_code ret;
+ krb5_enctype etype;
+ PA_DATA *pa;
+
+ memset(&paid, 0, sizeof(paid));
+
+ etype = rep->kdc_rep.enc_part.etype;
+
+ if (rep->kdc_rep.padata) {
+ paid.etype = etype;
+ ppaid = process_pa_info(context, creds->client, a, &paid,
+ rep->kdc_rep.padata);
+ }
+ if (ppaid == NULL) {
+ ret = krb5_get_pw_salt (context, creds->client, &paid.salt);
+ if (ret)
+ return ret;
+ paid.etype = etype;
+ paid.s2kparams = NULL;
+ }
+
+ pa = NULL;
+ if (rep->kdc_rep.padata) {
+ int idx = 0;
+ pa = krb5_find_padata(rep->kdc_rep.padata->val,
+ rep->kdc_rep.padata->len,
+ KRB5_PADATA_PK_AS_REP,
+ &idx);
+ if (pa == NULL) {
+ idx = 0;
+ pa = krb5_find_padata(rep->kdc_rep.padata->val,
+ rep->kdc_rep.padata->len,
+ KRB5_PADATA_PK_AS_REP_19,
+ &idx);
+ }
+ }
+ if (pa && ctx->pk_init_ctx) {
+#ifdef PKINIT
+ ret = _krb5_pk_rd_pa_reply(context,
+ a->req_body.realm,
+ ctx->pk_init_ctx,
+ etype,
+ hi,
+ ctx->pk_nonce,
+ &ctx->req_buffer,
+ pa,
+ key);
+#else
+ ret = EINVAL;
+ krb5_set_error_message(context, ret, N_("no support for PKINIT compiled in", ""));
+#endif
+ } else if (ctx->password)
+ ret = pa_data_to_key_plain(context, creds->client, ctx,
+ paid.salt, paid.s2kparams, etype, key);
+ else {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret, N_("No usable pa data type", ""));
+ }
+
+ free_paid(context, &paid);
+ return ret;
+}
+
+static krb5_error_code
+init_cred_loop(krb5_context context,
+ krb5_get_init_creds_opt *init_cred_opts,
+ const krb5_prompter_fct prompter,
+ void *prompter_data,
+ krb5_get_init_creds_ctx *ctx,
+ krb5_creds *creds,
+ krb5_kdc_rep *ret_as_reply)
+{
+ krb5_error_code ret;
+ krb5_kdc_rep rep;
+ METHOD_DATA md;
+ krb5_data resp;
+ size_t len;
+ size_t size;
+ krb5_krbhst_info *hi = NULL;
+ krb5_sendto_ctx stctx = NULL;
+
+
+ memset(&md, 0, sizeof(md));
+ memset(&rep, 0, sizeof(rep));
+
+ _krb5_get_init_creds_opt_free_krb5_error(init_cred_opts);
+
+ if (ret_as_reply)
+ memset(ret_as_reply, 0, sizeof(*ret_as_reply));
+
+ ret = init_creds_init_as_req(context, ctx->flags, creds,
+ ctx->addrs, ctx->etypes, &ctx->as_req);
+ if (ret)
+ return ret;
+
+ ret = krb5_sendto_ctx_alloc(context, &stctx);
+ if (ret)
+ goto out;
+ krb5_sendto_ctx_set_func(stctx, _krb5_kdc_retry, NULL);
+
+ /* Set a new nonce. */
+ krb5_generate_random_block (&ctx->nonce, sizeof(ctx->nonce));
+ ctx->nonce &= 0xffffffff;
+ /* XXX these just needs to be the same when using Windows PK-INIT */
+ ctx->pk_nonce = ctx->nonce;
+
+ /*
+ * Increase counter when we want other pre-auth types then
+ * KRB5_PA_ENC_TIMESTAMP.
+ */
+#define MAX_PA_COUNTER 3
+
+ ctx->pa_counter = 0;
+ while (ctx->pa_counter < MAX_PA_COUNTER) {
+
+ ctx->pa_counter++;
+
+ if (ctx->as_req.padata) {
+ free_METHOD_DATA(ctx->as_req.padata);
+ free(ctx->as_req.padata);
+ ctx->as_req.padata = NULL;
+ }
+
+ /* Set a new nonce. */
+ ctx->as_req.req_body.nonce = ctx->nonce;
+
+ /* fill_in_md_data */
+ ret = process_pa_data_to_md(context, creds, &ctx->as_req, ctx,
+ &md, &ctx->as_req.padata,
+ prompter, prompter_data);
+ if (ret)
+ goto out;
+
+ krb5_data_free(&ctx->req_buffer);
+
+ ASN1_MALLOC_ENCODE(AS_REQ,
+ ctx->req_buffer.data, ctx->req_buffer.length,
+ &ctx->as_req, &len, ret);
+ if (ret)
+ goto out;
+ if(len != ctx->req_buffer.length)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+
+ ret = krb5_sendto_context (context, stctx, &ctx->req_buffer,
+ creds->client->realm, &resp);
+ if (ret)
+ goto out;
+
+ memset (&rep, 0, sizeof(rep));
+ ret = decode_AS_REP(resp.data, resp.length, &rep.kdc_rep, &size);
+ if (ret == 0) {
+ krb5_data_free(&resp);
+ krb5_clear_error_message(context);
+ break;
+ } else {
+ /* let's try to parse it as a KRB-ERROR */
+ KRB_ERROR error;
+
+ ret = krb5_rd_error(context, &resp, &error);
+ if(ret && resp.data && ((char*)resp.data)[0] == 4)
+ ret = KRB5KRB_AP_ERR_V4_REPLY;
+ krb5_data_free(&resp);
+ if (ret)
+ goto out;
+
+ ret = krb5_error_from_rd_error(context, &error, creds);
+
+ /*
+ * If no preauth was set and KDC requires it, give it one
+ * more try.
+ */
+
+ if (ret == KRB5KDC_ERR_PREAUTH_REQUIRED) {
+ free_METHOD_DATA(&md);
+ memset(&md, 0, sizeof(md));
+
+ if (error.e_data) {
+ ret = decode_METHOD_DATA(error.e_data->data,
+ error.e_data->length,
+ &md,
+ NULL);
+ if (ret)
+ krb5_set_error_message(context, ret,
+ N_("failed to decode METHOD DATA", ""));
+ } else {
+ /* XXX guess what the server want here add add md */
+ }
+ krb5_free_error_contents(context, &error);
+ if (ret)
+ goto out;
+ } else {
+ _krb5_get_init_creds_opt_set_krb5_error(context,
+ init_cred_opts,
+ &error);
+ if (ret_as_reply)
+ rep.error = error;
+ else
+ krb5_free_error_contents(context, &error);
+ goto out;
+ }
+ }
+ }
+
+ {
+ krb5_keyblock *key = NULL;
+ unsigned flags = EXTRACT_TICKET_AS_REQ;
+
+ if (ctx->flags.request_anonymous)
+ flags |= EXTRACT_TICKET_ALLOW_SERVER_MISMATCH;
+ if (ctx->flags.canonicalize) {
+ flags |= EXTRACT_TICKET_ALLOW_SERVER_MISMATCH;
+ flags |= EXTRACT_TICKET_MATCH_REALM;
+ }
+ if (ctx->ic_flags & KRB5_INIT_CREDS_NO_C_CANON_CHECK)
+ flags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH;
+
+ ret = process_pa_data_to_key(context, ctx, creds,
+ &ctx->as_req, &rep, hi, &key);
+ if (ret)
+ goto out;
+
+ ret = _krb5_extract_ticket(context,
+ &rep,
+ creds,
+ key,
+ NULL,
+ KRB5_KU_AS_REP_ENC_PART,
+ NULL,
+ ctx->nonce,
+ flags,
+ NULL,
+ NULL);
+ krb5_free_keyblock(context, key);
+ }
+out:
+ if (stctx)
+ krb5_sendto_ctx_free(context, stctx);
+ krb5_data_free(&ctx->req_buffer);
+ free_METHOD_DATA(&md);
+ memset(&md, 0, sizeof(md));
+
+ if (ret == 0 && ret_as_reply)
+ *ret_as_reply = rep;
+ else
+ krb5_free_kdc_rep (context, &rep);
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_init_creds(krb5_context context,
+ krb5_creds *creds,
+ krb5_principal client,
+ krb5_prompter_fct prompter,
+ void *data,
+ krb5_deltat start_time,
+ const char *in_tkt_service,
+ krb5_get_init_creds_opt *options)
+{
+ krb5_get_init_creds_ctx ctx;
+ krb5_kdc_rep kdc_reply;
+ krb5_error_code ret;
+ char buf[BUFSIZ];
+ int done;
+
+ memset(&kdc_reply, 0, sizeof(kdc_reply));
+
+ ret = get_init_creds_common(context, client, start_time,
+ in_tkt_service, options, &ctx);
+ if (ret)
+ goto out;
+
+ done = 0;
+ while(!done) {
+ memset(&kdc_reply, 0, sizeof(kdc_reply));
+
+ ret = init_cred_loop(context,
+ options,
+ prompter,
+ data,
+ &ctx,
+ &ctx.cred,
+ &kdc_reply);
+
+ switch (ret) {
+ case 0 :
+ done = 1;
+ break;
+ case KRB5KDC_ERR_KEY_EXPIRED :
+ /* try to avoid recursion */
+
+ /* don't try to change password where then where none */
+ if (prompter == NULL || ctx.password == NULL)
+ goto out;
+
+ krb5_clear_error_message (context);
+
+ if (ctx.in_tkt_service != NULL
+ && strcmp (ctx.in_tkt_service, "kadmin/changepw") == 0)
+ goto out;
+
+ ret = change_password (context,
+ client,
+ ctx.password,
+ buf,
+ sizeof(buf),
+ prompter,
+ data,
+ options);
+ if (ret)
+ goto out;
+ ctx.password = buf;
+ break;
+ default:
+ goto out;
+ }
+ }
+
+ if (prompter)
+ print_expire (context,
+ krb5_principal_get_realm (context, ctx.cred.client),
+ &kdc_reply,
+ prompter,
+ data);
+
+ out:
+ memset (buf, 0, sizeof(buf));
+ free_init_creds_ctx(context, &ctx);
+ krb5_free_kdc_rep (context, &kdc_reply);
+ if (ret == 0)
+ *creds = ctx.cred;
+ else
+ krb5_free_cred_contents (context, &ctx.cred);
+
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_init_creds_password(krb5_context context,
+ krb5_creds *creds,
+ krb5_principal client,
+ const char *password,
+ krb5_prompter_fct prompter,
+ void *data,
+ krb5_deltat start_time,
+ const char *in_tkt_service,
+ krb5_get_init_creds_opt *in_options)
+{
+ krb5_get_init_creds_opt *options;
+ char buf[BUFSIZ];
+ krb5_error_code ret;
+
+ if (in_options == NULL) {
+ const char *realm = krb5_principal_get_realm(context, client);
+ ret = krb5_get_init_creds_opt_alloc(context, &options);
+ if (ret == 0)
+ krb5_get_init_creds_opt_set_default_flags(context,
+ NULL,
+ realm,
+ options);
+ } else
+ ret = _krb5_get_init_creds_opt_copy(context, in_options, &options);
+ if (ret)
+ return ret;
+
+ if (password == NULL &&
+ options->opt_private->password == NULL &&
+ options->opt_private->pk_init_ctx == NULL)
+ {
+ krb5_prompt prompt;
+ krb5_data password_data;
+ char *p, *q;
+
+ krb5_unparse_name (context, client, &p);
+ asprintf (&q, "%s's Password: ", p);
+ free (p);
+ prompt.prompt = q;
+ password_data.data = buf;
+ password_data.length = sizeof(buf);
+ prompt.hidden = 1;
+ prompt.reply = &password_data;
+ prompt.type = KRB5_PROMPT_TYPE_PASSWORD;
+
+ ret = (*prompter) (context, data, NULL, NULL, 1, &prompt);
+ free (q);
+ if (ret) {
+ memset (buf, 0, sizeof(buf));
+ krb5_get_init_creds_opt_free(context, options);
+ ret = KRB5_LIBOS_PWDINTR;
+ krb5_clear_error_message (context);
+ return ret;
+ }
+ password = password_data.data;
+ }
+
+ if (options->opt_private->password == NULL) {
+ ret = krb5_get_init_creds_opt_set_pa_password(context, options,
+ password, NULL);
+ if (ret) {
+ krb5_get_init_creds_opt_free(context, options);
+ memset(buf, 0, sizeof(buf));
+ return ret;
+ }
+ }
+
+ ret = krb5_get_init_creds(context, creds, client, prompter,
+ data, start_time, in_tkt_service, options);
+ krb5_get_init_creds_opt_free(context, options);
+ memset(buf, 0, sizeof(buf));
+ return ret;
+}
+
+static krb5_error_code
+init_creds_keyblock_key_proc (krb5_context context,
+ krb5_enctype type,
+ krb5_salt salt,
+ krb5_const_pointer keyseed,
+ krb5_keyblock **key)
+{
+ return krb5_copy_keyblock (context, keyseed, key);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_init_creds_keyblock(krb5_context context,
+ krb5_creds *creds,
+ krb5_principal client,
+ krb5_keyblock *keyblock,
+ krb5_deltat start_time,
+ const char *in_tkt_service,
+ krb5_get_init_creds_opt *options)
+{
+ struct krb5_get_init_creds_ctx ctx;
+ krb5_error_code ret;
+
+ ret = get_init_creds_common(context, client, start_time,
+ in_tkt_service, options, &ctx);
+ if (ret)
+ goto out;
+
+ ret = krb5_get_in_cred (context,
+ KDCOptions2int(ctx.flags),
+ ctx.addrs,
+ ctx.etypes,
+ ctx.pre_auth_types,
+ NULL,
+ init_creds_keyblock_key_proc,
+ keyblock,
+ NULL,
+ NULL,
+ &ctx.cred,
+ NULL);
+
+ if (ret == 0 && creds)
+ *creds = ctx.cred;
+ else
+ krb5_free_cred_contents (context, &ctx.cred);
+
+ out:
+ free_init_creds_ctx(context, &ctx);
+ return ret;
+}
diff --git a/source4/heimdal/lib/krb5/k524_err.et b/source4/heimdal/lib/krb5/k524_err.et
new file mode 100644
index 0000000000..4827b397af
--- /dev/null
+++ b/source4/heimdal/lib/krb5/k524_err.et
@@ -0,0 +1,20 @@
+#
+# Error messages for the k524 functions
+#
+# This might look like a com_err file, but is not
+#
+id "$Id$"
+
+error_table k524
+
+prefix KRB524
+error_code BADKEY, "wrong keytype in ticket"
+error_code BADADDR, "incorrect network address"
+error_code BADPRINC, "cannot convert V5 principal" #unused
+error_code BADREALM, "V5 realm name longer than V4 maximum" #unused
+error_code V4ERR, "kerberos V4 error server"
+error_code ENCFULL, "encoding too large at server"
+error_code DECEMPTY, "decoding out of data" #unused
+error_code NOTRESP, "service not responding" #unused
+end
+
diff --git a/source4/heimdal/lib/krb5/kcm.c b/source4/heimdal/lib/krb5/kcm.c
new file mode 100644
index 0000000000..8a8f1efc11
--- /dev/null
+++ b/source4/heimdal/lib/krb5/kcm.c
@@ -0,0 +1,1159 @@
+/*
+ * Copyright (c) 2005, PADL Software Pty Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of PADL Software nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+#ifdef HAVE_KCM
+/*
+ * Client library for Kerberos Credentials Manager (KCM) daemon
+ */
+
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+
+#include "kcm.h"
+
+RCSID("$Id$");
+
+typedef struct krb5_kcmcache {
+ char *name;
+ struct sockaddr_un path;
+ char *door_path;
+} krb5_kcmcache;
+
+#define KCMCACHE(X) ((krb5_kcmcache *)(X)->data.data)
+#define CACHENAME(X) (KCMCACHE(X)->name)
+#define KCMCURSOR(C) (*(uint32_t *)(C))
+
+static krb5_error_code
+try_door(krb5_context context,
+ krb5_kcmcache *k,
+ krb5_data *request_data,
+ krb5_data *response_data)
+{
+#ifdef HAVE_DOOR_CREATE
+ door_arg_t arg;
+ int fd;
+ int ret;
+
+ memset(&arg, 0, sizeof(arg));
+
+ fd = open(k->door_path, O_RDWR);
+ if (fd < 0)
+ return KRB5_CC_IO;
+ rk_cloexec(fd);
+
+ arg.data_ptr = request_data->data;
+ arg.data_size = request_data->length;
+ arg.desc_ptr = NULL;
+ arg.desc_num = 0;
+ arg.rbuf = NULL;
+ arg.rsize = 0;
+
+ ret = door_call(fd, &arg);
+ close(fd);
+ if (ret != 0)
+ return KRB5_CC_IO;
+
+ ret = krb5_data_copy(response_data, arg.rbuf, arg.rsize);
+ munmap(arg.rbuf, arg.rsize);
+ if (ret)
+ return ret;
+
+ return 0;
+#else
+ return KRB5_CC_IO;
+#endif
+}
+
+static krb5_error_code
+try_unix_socket(krb5_context context,
+ krb5_kcmcache *k,
+ krb5_data *request_data,
+ krb5_data *response_data)
+{
+ krb5_error_code ret;
+ int fd;
+
+ fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
+ if (fd < 0)
+ return KRB5_CC_IO;
+ rk_cloexec(fd);
+
+ if (connect(fd, rk_UNCONST(&k->path), sizeof(k->path)) != 0) {
+ close(fd);
+ return KRB5_CC_IO;
+ }
+
+ ret = _krb5_send_and_recv_tcp(fd, context->kdc_timeout,
+ request_data, response_data);
+ close(fd);
+ return ret;
+}
+
+static krb5_error_code
+kcm_send_request(krb5_context context,
+ krb5_kcmcache *k,
+ krb5_storage *request,
+ krb5_data *response_data)
+{
+ krb5_error_code ret;
+ krb5_data request_data;
+ int i;
+
+ response_data->data = NULL;
+ response_data->length = 0;
+
+ ret = krb5_storage_to_data(request, &request_data);
+ if (ret) {
+ krb5_clear_error_message(context);
+ return KRB5_CC_NOMEM;
+ }
+
+ ret = KRB5_CC_NOSUPP;
+
+ for (i = 0; i < context->max_retries; i++) {
+ ret = try_door(context, k, &request_data, response_data);
+ if (ret == 0 && response_data->length != 0)
+ break;
+ ret = try_unix_socket(context, k, &request_data, response_data);
+ if (ret == 0 && response_data->length != 0)
+ break;
+ }
+
+ krb5_data_free(&request_data);
+
+ if (ret) {
+ krb5_clear_error_message(context);
+ ret = KRB5_CC_NOSUPP;
+ }
+
+ return ret;
+}
+
+static krb5_error_code
+kcm_storage_request(krb5_context context,
+ kcm_operation opcode,
+ krb5_storage **storage_p)
+{
+ krb5_storage *sp;
+ krb5_error_code ret;
+
+ *storage_p = NULL;
+
+ sp = krb5_storage_emem();
+ if (sp == NULL) {
+ krb5_set_error_message(context, KRB5_CC_NOMEM, N_("malloc: out of memory", ""));
+ return KRB5_CC_NOMEM;
+ }
+
+ /* Send MAJOR | VERSION | OPCODE */
+ ret = krb5_store_int8(sp, KCM_PROTOCOL_VERSION_MAJOR);
+ if (ret)
+ goto fail;
+ ret = krb5_store_int8(sp, KCM_PROTOCOL_VERSION_MINOR);
+ if (ret)
+ goto fail;
+ ret = krb5_store_int16(sp, opcode);
+ if (ret)
+ goto fail;
+
+ *storage_p = sp;
+ fail:
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("Failed to encode KCM request", ""));
+ krb5_storage_free(sp);
+ }
+
+ return ret;
+}
+
+static krb5_error_code
+kcm_alloc(krb5_context context, const char *name, krb5_ccache *id)
+{
+ krb5_kcmcache *k;
+ const char *path;
+
+ k = malloc(sizeof(*k));
+ if (k == NULL) {
+ krb5_set_error_message(context, KRB5_CC_NOMEM, N_("malloc: out of memory", ""));
+ return KRB5_CC_NOMEM;
+ }
+
+ if (name != NULL) {
+ k->name = strdup(name);
+ if (k->name == NULL) {
+ free(k);
+ krb5_set_error_message(context, KRB5_CC_NOMEM,
+ N_("malloc: out of memory", ""));
+ return KRB5_CC_NOMEM;
+ }
+ } else
+ k->name = NULL;
+
+ path = krb5_config_get_string_default(context, NULL,
+ _PATH_KCM_SOCKET,
+ "libdefaults",
+ "kcm_socket",
+ NULL);
+
+ k->path.sun_family = AF_UNIX;
+ strlcpy(k->path.sun_path, path, sizeof(k->path.sun_path));
+
+ path = krb5_config_get_string_default(context, NULL,
+ _PATH_KCM_DOOR,
+ "libdefaults",
+ "kcm_door",
+ NULL);
+ k->door_path = strdup(path);
+
+ (*id)->data.data = k;
+ (*id)->data.length = sizeof(*k);
+
+ return 0;
+}
+
+static krb5_error_code
+kcm_call(krb5_context context,
+ krb5_kcmcache *k,
+ krb5_storage *request,
+ krb5_storage **response_p,
+ krb5_data *response_data_p)
+{
+ krb5_data response_data;
+ krb5_error_code ret;
+ int32_t status;
+ krb5_storage *response;
+
+ if (response_p != NULL)
+ *response_p = NULL;
+
+ ret = kcm_send_request(context, k, request, &response_data);
+ if (ret) {
+ return ret;
+ }
+
+ response = krb5_storage_from_data(&response_data);
+ if (response == NULL) {
+ krb5_data_free(&response_data);
+ return KRB5_CC_IO;
+ }
+
+ ret = krb5_ret_int32(response, &status);
+ if (ret) {
+ krb5_storage_free(response);
+ krb5_data_free(&response_data);
+ return KRB5_CC_FORMAT;
+ }
+
+ if (status) {
+ krb5_storage_free(response);
+ krb5_data_free(&response_data);
+ return status;
+ }
+
+ if (response_p != NULL) {
+ *response_data_p = response_data;
+ *response_p = response;
+
+ return 0;
+ }
+
+ krb5_storage_free(response);
+ krb5_data_free(&response_data);
+
+ return 0;
+}
+
+static void
+kcm_free(krb5_context context, krb5_ccache *id)
+{
+ krb5_kcmcache *k = KCMCACHE(*id);
+
+ if (k != NULL) {
+ if (k->name != NULL)
+ free(k->name);
+ if (k->door_path)
+ free(k->door_path);
+ memset(k, 0, sizeof(*k));
+ krb5_data_free(&(*id)->data);
+ }
+
+ *id = NULL;
+}
+
+static const char *
+kcm_get_name(krb5_context context,
+ krb5_ccache id)
+{
+ return CACHENAME(id);
+}
+
+static krb5_error_code
+kcm_resolve(krb5_context context, krb5_ccache *id, const char *res)
+{
+ return kcm_alloc(context, res, id);
+}
+
+/*
+ * Request:
+ *
+ * Response:
+ * NameZ
+ */
+static krb5_error_code
+kcm_gen_new(krb5_context context, krb5_ccache *id)
+{
+ krb5_kcmcache *k;
+ krb5_error_code ret;
+ krb5_storage *request, *response;
+ krb5_data response_data;
+
+ ret = kcm_alloc(context, NULL, id);
+ if (ret)
+ return ret;
+
+ k = KCMCACHE(*id);
+
+ ret = kcm_storage_request(context, KCM_OP_GEN_NEW, &request);
+ if (ret) {
+ kcm_free(context, id);
+ return ret;
+ }
+
+ ret = kcm_call(context, k, request, &response, &response_data);
+ if (ret) {
+ krb5_storage_free(request);
+ kcm_free(context, id);
+ return ret;
+ }
+
+ ret = krb5_ret_stringz(response, &k->name);
+ if (ret)
+ ret = KRB5_CC_IO;
+
+ krb5_storage_free(request);
+ krb5_storage_free(response);
+ krb5_data_free(&response_data);
+
+ if (ret)
+ kcm_free(context, id);
+
+ return ret;
+}
+
+/*
+ * Request:
+ * NameZ
+ * Principal
+ *
+ * Response:
+ *
+ */
+static krb5_error_code
+kcm_initialize(krb5_context context,
+ krb5_ccache id,
+ krb5_principal primary_principal)
+{
+ krb5_error_code ret;
+ krb5_kcmcache *k = KCMCACHE(id);
+ krb5_storage *request;
+
+ ret = kcm_storage_request(context, KCM_OP_INITIALIZE, &request);
+ if (ret)
+ return ret;
+
+ ret = krb5_store_stringz(request, k->name);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = krb5_store_principal(request, primary_principal);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = kcm_call(context, k, request, NULL, NULL);
+
+ krb5_storage_free(request);
+ return ret;
+}
+
+static krb5_error_code
+kcm_close(krb5_context context,
+ krb5_ccache id)
+{
+ kcm_free(context, &id);
+ return 0;
+}
+
+/*
+ * Request:
+ * NameZ
+ *
+ * Response:
+ *
+ */
+static krb5_error_code
+kcm_destroy(krb5_context context,
+ krb5_ccache id)
+{
+ krb5_error_code ret;
+ krb5_kcmcache *k = KCMCACHE(id);
+ krb5_storage *request;
+
+ ret = kcm_storage_request(context, KCM_OP_DESTROY, &request);
+ if (ret)
+ return ret;
+
+ ret = krb5_store_stringz(request, k->name);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = kcm_call(context, k, request, NULL, NULL);
+
+ krb5_storage_free(request);
+ return ret;
+}
+
+/*
+ * Request:
+ * NameZ
+ * Creds
+ *
+ * Response:
+ *
+ */
+static krb5_error_code
+kcm_store_cred(krb5_context context,
+ krb5_ccache id,
+ krb5_creds *creds)
+{
+ krb5_error_code ret;
+ krb5_kcmcache *k = KCMCACHE(id);
+ krb5_storage *request;
+
+ ret = kcm_storage_request(context, KCM_OP_STORE, &request);
+ if (ret)
+ return ret;
+
+ ret = krb5_store_stringz(request, k->name);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = krb5_store_creds(request, creds);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = kcm_call(context, k, request, NULL, NULL);
+
+ krb5_storage_free(request);
+ return ret;
+}
+
+/*
+ * Request:
+ * NameZ
+ * WhichFields
+ * MatchCreds
+ *
+ * Response:
+ * Creds
+ *
+ */
+static krb5_error_code
+kcm_retrieve(krb5_context context,
+ krb5_ccache id,
+ krb5_flags which,
+ const krb5_creds *mcred,
+ krb5_creds *creds)
+{
+ krb5_error_code ret;
+ krb5_kcmcache *k = KCMCACHE(id);
+ krb5_storage *request, *response;
+ krb5_data response_data;
+
+ ret = kcm_storage_request(context, KCM_OP_RETRIEVE, &request);
+ if (ret)
+ return ret;
+
+ ret = krb5_store_stringz(request, k->name);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = krb5_store_int32(request, which);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = krb5_store_creds_tag(request, rk_UNCONST(mcred));
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = kcm_call(context, k, request, &response, &response_data);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = krb5_ret_creds(response, creds);
+ if (ret)
+ ret = KRB5_CC_IO;
+
+ krb5_storage_free(request);
+ krb5_storage_free(response);
+ krb5_data_free(&response_data);
+
+ return ret;
+}
+
+/*
+ * Request:
+ * NameZ
+ *
+ * Response:
+ * Principal
+ */
+static krb5_error_code
+kcm_get_principal(krb5_context context,
+ krb5_ccache id,
+ krb5_principal *principal)
+{
+ krb5_error_code ret;
+ krb5_kcmcache *k = KCMCACHE(id);
+ krb5_storage *request, *response;
+ krb5_data response_data;
+
+ ret = kcm_storage_request(context, KCM_OP_GET_PRINCIPAL, &request);
+ if (ret)
+ return ret;
+
+ ret = krb5_store_stringz(request, k->name);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = kcm_call(context, k, request, &response, &response_data);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = krb5_ret_principal(response, principal);
+ if (ret)
+ ret = KRB5_CC_IO;
+
+ krb5_storage_free(request);
+ krb5_storage_free(response);
+ krb5_data_free(&response_data);
+
+ return ret;
+}
+
+/*
+ * Request:
+ * NameZ
+ *
+ * Response:
+ * Cursor
+ *
+ */
+static krb5_error_code
+kcm_get_first (krb5_context context,
+ krb5_ccache id,
+ krb5_cc_cursor *cursor)
+{
+ krb5_error_code ret;
+ krb5_kcmcache *k = KCMCACHE(id);
+ krb5_storage *request, *response;
+ krb5_data response_data;
+ int32_t tmp;
+
+ ret = kcm_storage_request(context, KCM_OP_GET_FIRST, &request);
+ if (ret)
+ return ret;
+
+ ret = krb5_store_stringz(request, k->name);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = kcm_call(context, k, request, &response, &response_data);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = krb5_ret_int32(response, &tmp);
+ if (ret || tmp < 0)
+ ret = KRB5_CC_IO;
+
+ krb5_storage_free(request);
+ krb5_storage_free(response);
+ krb5_data_free(&response_data);
+
+ if (ret)
+ return ret;
+
+ *cursor = malloc(sizeof(tmp));
+ if (*cursor == NULL)
+ return KRB5_CC_NOMEM;
+
+ KCMCURSOR(*cursor) = tmp;
+
+ return 0;
+}
+
+/*
+ * Request:
+ * NameZ
+ * Cursor
+ *
+ * Response:
+ * Creds
+ */
+static krb5_error_code
+kcm_get_next (krb5_context context,
+ krb5_ccache id,
+ krb5_cc_cursor *cursor,
+ krb5_creds *creds)
+{
+ krb5_error_code ret;
+ krb5_kcmcache *k = KCMCACHE(id);
+ krb5_storage *request, *response;
+ krb5_data response_data;
+
+ ret = kcm_storage_request(context, KCM_OP_GET_NEXT, &request);
+ if (ret)
+ return ret;
+
+ ret = krb5_store_stringz(request, k->name);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = krb5_store_int32(request, KCMCURSOR(*cursor));
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = kcm_call(context, k, request, &response, &response_data);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = krb5_ret_creds(response, creds);
+ if (ret)
+ ret = KRB5_CC_IO;
+
+ krb5_storage_free(request);
+ krb5_storage_free(response);
+ krb5_data_free(&response_data);
+
+ return ret;
+}
+
+/*
+ * Request:
+ * NameZ
+ * Cursor
+ *
+ * Response:
+ *
+ */
+static krb5_error_code
+kcm_end_get (krb5_context context,
+ krb5_ccache id,
+ krb5_cc_cursor *cursor)
+{
+ krb5_error_code ret;
+ krb5_kcmcache *k = KCMCACHE(id);
+ krb5_storage *request;
+
+ ret = kcm_storage_request(context, KCM_OP_END_GET, &request);
+ if (ret)
+ return ret;
+
+ ret = krb5_store_stringz(request, k->name);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = krb5_store_int32(request, KCMCURSOR(*cursor));
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = kcm_call(context, k, request, NULL, NULL);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ krb5_storage_free(request);
+
+ KCMCURSOR(*cursor) = 0;
+ free(*cursor);
+ *cursor = NULL;
+
+ return ret;
+}
+
+/*
+ * Request:
+ * NameZ
+ * WhichFields
+ * MatchCreds
+ *
+ * Response:
+ *
+ */
+static krb5_error_code
+kcm_remove_cred(krb5_context context,
+ krb5_ccache id,
+ krb5_flags which,
+ krb5_creds *cred)
+{
+ krb5_error_code ret;
+ krb5_kcmcache *k = KCMCACHE(id);
+ krb5_storage *request;
+
+ ret = kcm_storage_request(context, KCM_OP_REMOVE_CRED, &request);
+ if (ret)
+ return ret;
+
+ ret = krb5_store_stringz(request, k->name);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = krb5_store_int32(request, which);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = krb5_store_creds_tag(request, cred);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = kcm_call(context, k, request, NULL, NULL);
+
+ krb5_storage_free(request);
+ return ret;
+}
+
+static krb5_error_code
+kcm_set_flags(krb5_context context,
+ krb5_ccache id,
+ krb5_flags flags)
+{
+ krb5_error_code ret;
+ krb5_kcmcache *k = KCMCACHE(id);
+ krb5_storage *request;
+
+ ret = kcm_storage_request(context, KCM_OP_SET_FLAGS, &request);
+ if (ret)
+ return ret;
+
+ ret = krb5_store_stringz(request, k->name);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = krb5_store_int32(request, flags);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = kcm_call(context, k, request, NULL, NULL);
+
+ krb5_storage_free(request);
+ return ret;
+}
+
+static int
+kcm_get_version(krb5_context context,
+ krb5_ccache id)
+{
+ return 0;
+}
+
+static krb5_error_code
+kcm_move(krb5_context context, krb5_ccache from, krb5_ccache to)
+{
+ krb5_error_code ret;
+ krb5_kcmcache *oldk = KCMCACHE(from);
+ krb5_kcmcache *newk = KCMCACHE(to);
+ krb5_storage *request;
+
+ ret = kcm_storage_request(context, KCM_OP_MOVE_CACHE, &request);
+ if (ret)
+ return ret;
+
+ ret = krb5_store_stringz(request, oldk->name);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = krb5_store_stringz(request, newk->name);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+ ret = kcm_call(context, oldk, request, NULL, NULL);
+
+ krb5_storage_free(request);
+ return ret;
+}
+
+static krb5_error_code
+kcm_default_name(krb5_context context, char **str)
+{
+ return _krb5_expand_default_cc_name(context,
+ KRB5_DEFAULT_CCNAME_KCM,
+ str);
+}
+
+static krb5_error_code
+kcm_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime)
+{
+ *mtime = time(NULL);
+ return 0;
+}
+
+/**
+ * Variable containing the KCM based credential cache implemention.
+ *
+ * @ingroup krb5_ccache
+ */
+
+KRB5_LIB_VARIABLE const krb5_cc_ops krb5_kcm_ops = {
+ KRB5_CC_OPS_VERSION,
+ "KCM",
+ kcm_get_name,
+ kcm_resolve,
+ kcm_gen_new,
+ kcm_initialize,
+ kcm_destroy,
+ kcm_close,
+ kcm_store_cred,
+ kcm_retrieve,
+ kcm_get_principal,
+ kcm_get_first,
+ kcm_get_next,
+ kcm_end_get,
+ kcm_remove_cred,
+ kcm_set_flags,
+ kcm_get_version,
+ NULL,
+ NULL,
+ NULL,
+ kcm_move,
+ kcm_default_name,
+ NULL,
+ kcm_lastchange
+};
+
+krb5_boolean
+_krb5_kcm_is_running(krb5_context context)
+{
+ krb5_error_code ret;
+ krb5_ccache_data ccdata;
+ krb5_ccache id = &ccdata;
+ krb5_boolean running;
+
+ ret = kcm_alloc(context, NULL, &id);
+ if (ret)
+ return 0;
+
+ running = (_krb5_kcm_noop(context, id) == 0);
+
+ kcm_free(context, &id);
+
+ return running;
+}
+
+/*
+ * Request:
+ *
+ * Response:
+ *
+ */
+krb5_error_code
+_krb5_kcm_noop(krb5_context context,
+ krb5_ccache id)
+{
+ krb5_error_code ret;
+ krb5_kcmcache *k = KCMCACHE(id);
+ krb5_storage *request;
+
+ ret = kcm_storage_request(context, KCM_OP_NOOP, &request);
+ if (ret)
+ return ret;
+
+ ret = kcm_call(context, k, request, NULL, NULL);
+
+ krb5_storage_free(request);
+ return ret;
+}
+
+
+/*
+ * Request:
+ * NameZ
+ * Mode
+ *
+ * Response:
+ *
+ */
+krb5_error_code
+_krb5_kcm_chmod(krb5_context context,
+ krb5_ccache id,
+ uint16_t mode)
+{
+ krb5_error_code ret;
+ krb5_kcmcache *k = KCMCACHE(id);
+ krb5_storage *request;
+
+ ret = kcm_storage_request(context, KCM_OP_CHMOD, &request);
+ if (ret)
+ return ret;
+
+ ret = krb5_store_stringz(request, k->name);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = krb5_store_int16(request, mode);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = kcm_call(context, k, request, NULL, NULL);
+
+ krb5_storage_free(request);
+ return ret;
+}
+
+
+/*
+ * Request:
+ * NameZ
+ * UID
+ * GID
+ *
+ * Response:
+ *
+ */
+krb5_error_code
+_krb5_kcm_chown(krb5_context context,
+ krb5_ccache id,
+ uint32_t uid,
+ uint32_t gid)
+{
+ krb5_error_code ret;
+ krb5_kcmcache *k = KCMCACHE(id);
+ krb5_storage *request;
+
+ ret = kcm_storage_request(context, KCM_OP_CHOWN, &request);
+ if (ret)
+ return ret;
+
+ ret = krb5_store_stringz(request, k->name);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = krb5_store_int32(request, uid);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = krb5_store_int32(request, gid);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = kcm_call(context, k, request, NULL, NULL);
+
+ krb5_storage_free(request);
+ return ret;
+}
+
+
+/*
+ * Request:
+ * NameZ
+ * ServerPrincipalPresent
+ * ServerPrincipal OPTIONAL
+ * Key
+ *
+ * Repsonse:
+ *
+ */
+krb5_error_code
+_krb5_kcm_get_initial_ticket(krb5_context context,
+ krb5_ccache id,
+ krb5_principal server,
+ krb5_keyblock *key)
+{
+ krb5_error_code ret;
+ krb5_kcmcache *k = KCMCACHE(id);
+ krb5_storage *request;
+
+ ret = kcm_storage_request(context, KCM_OP_GET_INITIAL_TICKET, &request);
+ if (ret)
+ return ret;
+
+ ret = krb5_store_stringz(request, k->name);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = krb5_store_int8(request, (server == NULL) ? 0 : 1);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ if (server != NULL) {
+ ret = krb5_store_principal(request, server);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+ }
+
+ ret = krb5_store_keyblock(request, *key);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = kcm_call(context, k, request, NULL, NULL);
+
+ krb5_storage_free(request);
+ return ret;
+}
+
+
+/*
+ * Request:
+ * NameZ
+ * KDCFlags
+ * EncryptionType
+ * ServerPrincipal
+ *
+ * Repsonse:
+ *
+ */
+krb5_error_code
+_krb5_kcm_get_ticket(krb5_context context,
+ krb5_ccache id,
+ krb5_kdc_flags flags,
+ krb5_enctype enctype,
+ krb5_principal server)
+{
+ krb5_error_code ret;
+ krb5_kcmcache *k = KCMCACHE(id);
+ krb5_storage *request;
+
+ ret = kcm_storage_request(context, KCM_OP_GET_TICKET, &request);
+ if (ret)
+ return ret;
+
+ ret = krb5_store_stringz(request, k->name);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = krb5_store_int32(request, flags.i);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = krb5_store_int32(request, enctype);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = krb5_store_principal(request, server);
+ if (ret) {
+ krb5_storage_free(request);
+ return ret;
+ }
+
+ ret = kcm_call(context, k, request, NULL, NULL);
+
+ krb5_storage_free(request);
+ return ret;
+}
+
+#endif /* HAVE_KCM */
diff --git a/source4/heimdal/lib/krb5/keyblock.c b/source4/heimdal/lib/krb5/keyblock.c
new file mode 100644
index 0000000000..aa6353d7c8
--- /dev/null
+++ b/source4/heimdal/lib/krb5/keyblock.c
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+void KRB5_LIB_FUNCTION
+krb5_keyblock_zero(krb5_keyblock *keyblock)
+{
+ keyblock->keytype = 0;
+ krb5_data_zero(&keyblock->keyvalue);
+}
+
+void KRB5_LIB_FUNCTION
+krb5_free_keyblock_contents(krb5_context context,
+ krb5_keyblock *keyblock)
+{
+ if(keyblock) {
+ if (keyblock->keyvalue.data != NULL)
+ memset(keyblock->keyvalue.data, 0, keyblock->keyvalue.length);
+ krb5_data_free (&keyblock->keyvalue);
+ keyblock->keytype = ENCTYPE_NULL;
+ }
+}
+
+void KRB5_LIB_FUNCTION
+krb5_free_keyblock(krb5_context context,
+ krb5_keyblock *keyblock)
+{
+ if(keyblock){
+ krb5_free_keyblock_contents(context, keyblock);
+ free(keyblock);
+ }
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_copy_keyblock_contents (krb5_context context,
+ const krb5_keyblock *inblock,
+ krb5_keyblock *to)
+{
+ return copy_EncryptionKey(inblock, to);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_copy_keyblock (krb5_context context,
+ const krb5_keyblock *inblock,
+ krb5_keyblock **to)
+{
+ krb5_keyblock *k;
+
+ k = malloc (sizeof(*k));
+ if (k == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ *to = k;
+ return krb5_copy_keyblock_contents (context, inblock, k);
+}
+
+krb5_enctype
+krb5_keyblock_get_enctype(const krb5_keyblock *block)
+{
+ return block->keytype;
+}
+
+/*
+ * Fill in `key' with key data of type `enctype' from `data' of length
+ * `size'. Key should be freed using krb5_free_keyblock_contents.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_keyblock_init(krb5_context context,
+ krb5_enctype type,
+ const void *data,
+ size_t size,
+ krb5_keyblock *key)
+{
+ krb5_error_code ret;
+ size_t len;
+
+ memset(key, 0, sizeof(*key));
+
+ ret = krb5_enctype_keysize(context, type, &len);
+ if (ret)
+ return ret;
+
+ if (len != size) {
+ krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
+ "Encryption key %d is %lu bytes "
+ "long, %lu was passed in",
+ type, (unsigned long)len, (unsigned long)size);
+ return KRB5_PROG_ETYPE_NOSUPP;
+ }
+ ret = krb5_data_copy(&key->keyvalue, data, len);
+ if(ret) {
+ krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ return ret;
+ }
+ key->keytype = type;
+
+ return 0;
+}
diff --git a/source4/heimdal/lib/krb5/keytab.c b/source4/heimdal/lib/krb5/keytab.c
new file mode 100644
index 0000000000..aa7c77ce46
--- /dev/null
+++ b/source4/heimdal/lib/krb5/keytab.c
@@ -0,0 +1,536 @@
+/*
+ * Copyright (c) 1997 - 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+/*
+ * Register a new keytab in `ops'
+ * Return 0 or an error.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_kt_register(krb5_context context,
+ const krb5_kt_ops *ops)
+{
+ struct krb5_keytab_data *tmp;
+
+ if (strlen(ops->prefix) > KRB5_KT_PREFIX_MAX_LEN - 1) {
+ krb5_set_error_message(context, KRB5_KT_BADNAME,
+ N_("can't register cache type, prefix too long", ""));
+ return KRB5_KT_BADNAME;
+ }
+
+ tmp = realloc(context->kt_types,
+ (context->num_kt_types + 1) * sizeof(*context->kt_types));
+ if(tmp == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ memcpy(&tmp[context->num_kt_types], ops,
+ sizeof(tmp[context->num_kt_types]));
+ context->kt_types = tmp;
+ context->num_kt_types++;
+ return 0;
+}
+
+/*
+ * Resolve the keytab name (of the form `type:residual') in `name'
+ * into a keytab in `id'.
+ * Return 0 or an error
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_kt_resolve(krb5_context context,
+ const char *name,
+ krb5_keytab *id)
+{
+ krb5_keytab k;
+ int i;
+ const char *type, *residual;
+ size_t type_len;
+ krb5_error_code ret;
+
+ residual = strchr(name, ':');
+ if(residual == NULL) {
+ type = "FILE";
+ type_len = strlen(type);
+ residual = name;
+ } else {
+ type = name;
+ type_len = residual - name;
+ residual++;
+ }
+
+ for(i = 0; i < context->num_kt_types; i++) {
+ if(strncasecmp(type, context->kt_types[i].prefix, type_len) == 0)
+ break;
+ }
+ if(i == context->num_kt_types) {
+ krb5_set_error_message(context, KRB5_KT_UNKNOWN_TYPE,
+ N_("unknown keytab type %.*s", "type"),
+ (int)type_len, type);
+ return KRB5_KT_UNKNOWN_TYPE;
+ }
+
+ k = malloc (sizeof(*k));
+ if (k == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ memcpy(k, &context->kt_types[i], sizeof(*k));
+ k->data = NULL;
+ ret = (*k->resolve)(context, residual, k);
+ if(ret) {
+ free(k);
+ k = NULL;
+ }
+ *id = k;
+ return ret;
+}
+
+/*
+ * copy the name of the default keytab into `name'.
+ * Return 0 or KRB5_CONFIG_NOTENUFSPACE if `namesize' is too short.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_kt_default_name(krb5_context context, char *name, size_t namesize)
+{
+ if (strlcpy (name, context->default_keytab, namesize) >= namesize) {
+ krb5_clear_error_message (context);
+ return KRB5_CONFIG_NOTENUFSPACE;
+ }
+ return 0;
+}
+
+/*
+ * copy the name of the default modify keytab into `name'.
+ * Return 0 or KRB5_CONFIG_NOTENUFSPACE if `namesize' is too short.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_kt_default_modify_name(krb5_context context, char *name, size_t namesize)
+{
+ const char *kt = NULL;
+ if(context->default_keytab_modify == NULL) {
+ if(strncasecmp(context->default_keytab, "ANY:", 4) != 0)
+ kt = context->default_keytab;
+ else {
+ size_t len = strcspn(context->default_keytab + 4, ",");
+ if(len >= namesize) {
+ krb5_clear_error_message(context);
+ return KRB5_CONFIG_NOTENUFSPACE;
+ }
+ strlcpy(name, context->default_keytab + 4, namesize);
+ name[len] = '\0';
+ return 0;
+ }
+ } else
+ kt = context->default_keytab_modify;
+ if (strlcpy (name, kt, namesize) >= namesize) {
+ krb5_clear_error_message (context);
+ return KRB5_CONFIG_NOTENUFSPACE;
+ }
+ return 0;
+}
+
+/*
+ * Set `id' to the default keytab.
+ * Return 0 or an error.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_kt_default(krb5_context context, krb5_keytab *id)
+{
+ return krb5_kt_resolve (context, context->default_keytab, id);
+}
+
+/*
+ * Read the key identified by `(principal, vno, enctype)' from the
+ * keytab in `keyprocarg' (the default if == NULL) into `*key'.
+ * Return 0 or an error.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_kt_read_service_key(krb5_context context,
+ krb5_pointer keyprocarg,
+ krb5_principal principal,
+ krb5_kvno vno,
+ krb5_enctype enctype,
+ krb5_keyblock **key)
+{
+ krb5_keytab keytab;
+ krb5_keytab_entry entry;
+ krb5_error_code ret;
+
+ if (keyprocarg)
+ ret = krb5_kt_resolve (context, keyprocarg, &keytab);
+ else
+ ret = krb5_kt_default (context, &keytab);
+
+ if (ret)
+ return ret;
+
+ ret = krb5_kt_get_entry (context, keytab, principal, vno, enctype, &entry);
+ krb5_kt_close (context, keytab);
+ if (ret)
+ return ret;
+ ret = krb5_copy_keyblock (context, &entry.keyblock, key);
+ krb5_kt_free_entry(context, &entry);
+ return ret;
+}
+
+/*
+ * Return the type of the `keytab' in the string `prefix of length
+ * `prefixsize'.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_kt_get_type(krb5_context context,
+ krb5_keytab keytab,
+ char *prefix,
+ size_t prefixsize)
+{
+ strlcpy(prefix, keytab->prefix, prefixsize);
+ return 0;
+}
+
+/*
+ * Retrieve the name of the keytab `keytab' into `name', `namesize'
+ * Return 0 or an error.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_kt_get_name(krb5_context context,
+ krb5_keytab keytab,
+ char *name,
+ size_t namesize)
+{
+ return (*keytab->get_name)(context, keytab, name, namesize);
+}
+
+/*
+ * Retrieve the full name of the keytab `keytab' and store the name in
+ * `str'. `str' needs to be freed by the caller using free(3).
+ * Returns 0 or an error. On error, *str is set to NULL.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_kt_get_full_name(krb5_context context,
+ krb5_keytab keytab,
+ char **str)
+{
+ char type[KRB5_KT_PREFIX_MAX_LEN];
+ char name[MAXPATHLEN];
+ krb5_error_code ret;
+
+ *str = NULL;
+
+ ret = krb5_kt_get_type(context, keytab, type, sizeof(type));
+ if (ret)
+ return ret;
+
+ ret = krb5_kt_get_name(context, keytab, name, sizeof(name));
+ if (ret)
+ return ret;
+
+ if (asprintf(str, "%s:%s", type, name) == -1) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ *str = NULL;
+ return ENOMEM;
+ }
+
+ return 0;
+}
+
+/*
+ * Finish using the keytab in `id'. All resources will be released,
+ * even on errors. Return 0 or an error.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_kt_close(krb5_context context,
+ krb5_keytab id)
+{
+ krb5_error_code ret;
+
+ ret = (*id->close)(context, id);
+ memset(id, 0, sizeof(*id));
+ free(id);
+ return ret;
+}
+
+/*
+ * Compare `entry' against `principal, vno, enctype'.
+ * Any of `principal, vno, enctype' might be 0 which acts as a wildcard.
+ * Return TRUE if they compare the same, FALSE otherwise.
+ */
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_kt_compare(krb5_context context,
+ krb5_keytab_entry *entry,
+ krb5_const_principal principal,
+ krb5_kvno vno,
+ krb5_enctype enctype)
+{
+ if(principal != NULL &&
+ !krb5_principal_compare(context, entry->principal, principal))
+ return FALSE;
+ if(vno && vno != entry->vno)
+ return FALSE;
+ if(enctype && enctype != entry->keyblock.keytype)
+ return FALSE;
+ return TRUE;
+}
+
+/*
+ * Retrieve the keytab entry for `principal, kvno, enctype' into `entry'
+ * from the keytab `id'.
+ * kvno == 0 is a wildcard and gives the keytab with the highest vno.
+ * Return 0 or an error.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_kt_get_entry(krb5_context context,
+ krb5_keytab id,
+ krb5_const_principal principal,
+ krb5_kvno kvno,
+ krb5_enctype enctype,
+ krb5_keytab_entry *entry)
+{
+ krb5_keytab_entry tmp;
+ krb5_error_code ret;
+ krb5_kt_cursor cursor;
+
+ if(id->get)
+ return (*id->get)(context, id, principal, kvno, enctype, entry);
+
+ ret = krb5_kt_start_seq_get (context, id, &cursor);
+ if (ret) {
+ /* This is needed for krb5_verify_init_creds, but keep error
+ * string from previous error for the human. */
+ context->error_code = KRB5_KT_NOTFOUND;
+ return KRB5_KT_NOTFOUND;
+ }
+
+ entry->vno = 0;
+ while (krb5_kt_next_entry(context, id, &tmp, &cursor) == 0) {
+ if (krb5_kt_compare(context, &tmp, principal, 0, enctype)) {
+ /* the file keytab might only store the lower 8 bits of
+ the kvno, so only compare those bits */
+ if (kvno == tmp.vno
+ || (tmp.vno < 256 && kvno % 256 == tmp.vno)) {
+ krb5_kt_copy_entry_contents (context, &tmp, entry);
+ krb5_kt_free_entry (context, &tmp);
+ krb5_kt_end_seq_get(context, id, &cursor);
+ return 0;
+ } else if (kvno == 0 && tmp.vno > entry->vno) {
+ if (entry->vno)
+ krb5_kt_free_entry (context, entry);
+ krb5_kt_copy_entry_contents (context, &tmp, entry);
+ }
+ }
+ krb5_kt_free_entry(context, &tmp);
+ }
+ krb5_kt_end_seq_get (context, id, &cursor);
+ if (entry->vno) {
+ return 0;
+ } else {
+ char princ[256], kvno_str[25], *kt_name;
+ char *enctype_str = NULL;
+
+ krb5_unparse_name_fixed (context, principal, princ, sizeof(princ));
+ krb5_kt_get_full_name (context, id, &kt_name);
+ krb5_enctype_to_string(context, enctype, &enctype_str);
+
+ if (kvno)
+ snprintf(kvno_str, sizeof(kvno_str), "(kvno %d)", kvno);
+ else
+ kvno_str[0] = '\0';
+
+ krb5_set_error_message (context, KRB5_KT_NOTFOUND,
+ N_("Failed to find %s%s in keytab %s (%s)",
+ "principal, kvno, keytab file, enctype"),
+ princ,
+ kvno_str,
+ kt_name ? kt_name : "unknown keytab",
+ enctype_str ? enctype_str : "unknown enctype");
+ free(kt_name);
+ free(enctype_str);
+ return KRB5_KT_NOTFOUND;
+ }
+}
+
+/*
+ * Copy the contents of `in' into `out'.
+ * Return 0 or an error. */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_kt_copy_entry_contents(krb5_context context,
+ const krb5_keytab_entry *in,
+ krb5_keytab_entry *out)
+{
+ krb5_error_code ret;
+
+ memset(out, 0, sizeof(*out));
+ out->vno = in->vno;
+
+ ret = krb5_copy_principal (context, in->principal, &out->principal);
+ if (ret)
+ goto fail;
+ ret = krb5_copy_keyblock_contents (context,
+ &in->keyblock,
+ &out->keyblock);
+ if (ret)
+ goto fail;
+ out->timestamp = in->timestamp;
+ return 0;
+fail:
+ krb5_kt_free_entry (context, out);
+ return ret;
+}
+
+/*
+ * Free the contents of `entry'.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_kt_free_entry(krb5_context context,
+ krb5_keytab_entry *entry)
+{
+ krb5_free_principal (context, entry->principal);
+ krb5_free_keyblock_contents (context, &entry->keyblock);
+ memset(entry, 0, sizeof(*entry));
+ return 0;
+}
+
+/*
+ * Set `cursor' to point at the beginning of `id'.
+ * Return 0 or an error.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_kt_start_seq_get(krb5_context context,
+ krb5_keytab id,
+ krb5_kt_cursor *cursor)
+{
+ if(id->start_seq_get == NULL) {
+ krb5_set_error_message(context, HEIM_ERR_OPNOTSUPP,
+ N_("start_seq_get is not supported "
+ "in the %s keytab type", ""),
+ id->prefix);
+ return HEIM_ERR_OPNOTSUPP;
+ }
+ return (*id->start_seq_get)(context, id, cursor);
+}
+
+/*
+ * Get the next entry from `id' pointed to by `cursor' and advance the
+ * `cursor'.
+ * Return 0 or an error.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_kt_next_entry(krb5_context context,
+ krb5_keytab id,
+ krb5_keytab_entry *entry,
+ krb5_kt_cursor *cursor)
+{
+ if(id->next_entry == NULL) {
+ krb5_set_error_message(context, HEIM_ERR_OPNOTSUPP,
+ N_("next_entry is not supported in the %s "
+ " keytab", ""),
+ id->prefix);
+ return HEIM_ERR_OPNOTSUPP;
+ }
+ return (*id->next_entry)(context, id, entry, cursor);
+}
+
+/*
+ * Release all resources associated with `cursor'.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_kt_end_seq_get(krb5_context context,
+ krb5_keytab id,
+ krb5_kt_cursor *cursor)
+{
+ if(id->end_seq_get == NULL) {
+ krb5_set_error_message(context, HEIM_ERR_OPNOTSUPP,
+ "end_seq_get is not supported in the %s "
+ " keytab", id->prefix);
+ return HEIM_ERR_OPNOTSUPP;
+ }
+ return (*id->end_seq_get)(context, id, cursor);
+}
+
+/*
+ * Add the entry in `entry' to the keytab `id'.
+ * Return 0 or an error.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_kt_add_entry(krb5_context context,
+ krb5_keytab id,
+ krb5_keytab_entry *entry)
+{
+ if(id->add == NULL) {
+ krb5_set_error_message(context, KRB5_KT_NOWRITE,
+ N_("Add is not supported in the %s keytab", ""),
+ id->prefix);
+ return KRB5_KT_NOWRITE;
+ }
+ entry->timestamp = time(NULL);
+ return (*id->add)(context, id,entry);
+}
+
+/*
+ * Remove the entry `entry' from the keytab `id'.
+ * Return 0 or an error.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_kt_remove_entry(krb5_context context,
+ krb5_keytab id,
+ krb5_keytab_entry *entry)
+{
+ if(id->remove == NULL) {
+ krb5_set_error_message(context, KRB5_KT_NOWRITE,
+ N_("Remove is not supported in the %s keytab", ""),
+ id->prefix);
+ return KRB5_KT_NOWRITE;
+ }
+ return (*id->remove)(context, id, entry);
+}
diff --git a/source4/heimdal/lib/krb5/keytab_any.c b/source4/heimdal/lib/krb5/keytab_any.c
new file mode 100644
index 0000000000..7a2d9b9f70
--- /dev/null
+++ b/source4/heimdal/lib/krb5/keytab_any.c
@@ -0,0 +1,262 @@
+/*
+ * Copyright (c) 2001-2002 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+struct any_data {
+ krb5_keytab kt;
+ char *name;
+ struct any_data *next;
+};
+
+static void
+free_list (krb5_context context, struct any_data *a)
+{
+ struct any_data *next;
+
+ for (; a != NULL; a = next) {
+ next = a->next;
+ free (a->name);
+ if(a->kt)
+ krb5_kt_close(context, a->kt);
+ free (a);
+ }
+}
+
+static krb5_error_code
+any_resolve(krb5_context context, const char *name, krb5_keytab id)
+{
+ struct any_data *a, *a0 = NULL, *prev = NULL;
+ krb5_error_code ret;
+ char buf[256];
+
+ while (strsep_copy(&name, ",", buf, sizeof(buf)) != -1) {
+ a = malloc(sizeof(*a));
+ if (a == NULL) {
+ ret = ENOMEM;
+ goto fail;
+ }
+ if (a0 == NULL) {
+ a0 = a;
+ a->name = strdup(buf);
+ if (a->name == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ goto fail;
+ }
+ } else
+ a->name = NULL;
+ if (prev != NULL)
+ prev->next = a;
+ a->next = NULL;
+ ret = krb5_kt_resolve (context, buf, &a->kt);
+ if (ret)
+ goto fail;
+ prev = a;
+ }
+ if (a0 == NULL) {
+ krb5_set_error_message(context, ENOENT, N_("empty ANY: keytab", ""));
+ return ENOENT;
+ }
+ id->data = a0;
+ return 0;
+ fail:
+ free_list (context, a0);
+ return ret;
+}
+
+static krb5_error_code
+any_get_name (krb5_context context,
+ krb5_keytab id,
+ char *name,
+ size_t namesize)
+{
+ struct any_data *a = id->data;
+ strlcpy(name, a->name, namesize);
+ return 0;
+}
+
+static krb5_error_code
+any_close (krb5_context context,
+ krb5_keytab id)
+{
+ struct any_data *a = id->data;
+
+ free_list (context, a);
+ return 0;
+}
+
+struct any_cursor_extra_data {
+ struct any_data *a;
+ krb5_kt_cursor cursor;
+};
+
+static krb5_error_code
+any_start_seq_get(krb5_context context,
+ krb5_keytab id,
+ krb5_kt_cursor *c)
+{
+ struct any_data *a = id->data;
+ struct any_cursor_extra_data *ed;
+ krb5_error_code ret;
+
+ c->data = malloc (sizeof(struct any_cursor_extra_data));
+ if(c->data == NULL){
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ ed = (struct any_cursor_extra_data *)c->data;
+ for (ed->a = a; ed->a != NULL; ed->a = ed->a->next) {
+ ret = krb5_kt_start_seq_get(context, ed->a->kt, &ed->cursor);
+ if (ret == 0)
+ break;
+ }
+ if (ed->a == NULL) {
+ free (c->data);
+ c->data = NULL;
+ krb5_clear_error_message (context);
+ return KRB5_KT_END;
+ }
+ return 0;
+}
+
+static krb5_error_code
+any_next_entry (krb5_context context,
+ krb5_keytab id,
+ krb5_keytab_entry *entry,
+ krb5_kt_cursor *cursor)
+{
+ krb5_error_code ret, ret2;
+ struct any_cursor_extra_data *ed;
+
+ ed = (struct any_cursor_extra_data *)cursor->data;
+ do {
+ ret = krb5_kt_next_entry(context, ed->a->kt, entry, &ed->cursor);
+ if (ret == 0)
+ return 0;
+ else if (ret != KRB5_KT_END)
+ return ret;
+
+ ret2 = krb5_kt_end_seq_get (context, ed->a->kt, &ed->cursor);
+ if (ret2)
+ return ret2;
+ while ((ed->a = ed->a->next) != NULL) {
+ ret2 = krb5_kt_start_seq_get(context, ed->a->kt, &ed->cursor);
+ if (ret2 == 0)
+ break;
+ }
+ if (ed->a == NULL) {
+ krb5_clear_error_message (context);
+ return KRB5_KT_END;
+ }
+ } while (1);
+}
+
+static krb5_error_code
+any_end_seq_get(krb5_context context,
+ krb5_keytab id,
+ krb5_kt_cursor *cursor)
+{
+ krb5_error_code ret = 0;
+ struct any_cursor_extra_data *ed;
+
+ ed = (struct any_cursor_extra_data *)cursor->data;
+ if (ed->a != NULL)
+ ret = krb5_kt_end_seq_get(context, ed->a->kt, &ed->cursor);
+ free (ed);
+ cursor->data = NULL;
+ return ret;
+}
+
+static krb5_error_code
+any_add_entry(krb5_context context,
+ krb5_keytab id,
+ krb5_keytab_entry *entry)
+{
+ struct any_data *a = id->data;
+ krb5_error_code ret;
+ while(a != NULL) {
+ ret = krb5_kt_add_entry(context, a->kt, entry);
+ if(ret != 0 && ret != KRB5_KT_NOWRITE) {
+ krb5_set_error_message(context, ret,
+ N_("failed to add entry to %s", ""),
+ a->name);
+ return ret;
+ }
+ a = a->next;
+ }
+ return 0;
+}
+
+static krb5_error_code
+any_remove_entry(krb5_context context,
+ krb5_keytab id,
+ krb5_keytab_entry *entry)
+{
+ struct any_data *a = id->data;
+ krb5_error_code ret;
+ int found = 0;
+ while(a != NULL) {
+ ret = krb5_kt_remove_entry(context, a->kt, entry);
+ if(ret == 0)
+ found++;
+ else {
+ if(ret != KRB5_KT_NOWRITE && ret != KRB5_KT_NOTFOUND) {
+ krb5_set_error_message(context, ret,
+ N_("Failed to remove keytab "
+ "entry from %s", "keytab name"),
+ a->name);
+ return ret;
+ }
+ }
+ a = a->next;
+ }
+ if(!found)
+ return KRB5_KT_NOTFOUND;
+ return 0;
+}
+
+const krb5_kt_ops krb5_any_ops = {
+ "ANY",
+ any_resolve,
+ any_get_name,
+ any_close,
+ NULL, /* get */
+ any_start_seq_get,
+ any_next_entry,
+ any_end_seq_get,
+ any_add_entry,
+ any_remove_entry
+};
diff --git a/source4/heimdal/lib/krb5/keytab_file.c b/source4/heimdal/lib/krb5/keytab_file.c
new file mode 100644
index 0000000000..f494cac253
--- /dev/null
+++ b/source4/heimdal/lib/krb5/keytab_file.c
@@ -0,0 +1,779 @@
+/*
+ * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+#define KRB5_KT_VNO_1 1
+#define KRB5_KT_VNO_2 2
+#define KRB5_KT_VNO KRB5_KT_VNO_2
+
+#define KRB5_KT_FL_JAVA 1
+
+
+/* file operations -------------------------------------------- */
+
+struct fkt_data {
+ char *filename;
+ int flags;
+};
+
+static krb5_error_code
+krb5_kt_ret_data(krb5_context context,
+ krb5_storage *sp,
+ krb5_data *data)
+{
+ int ret;
+ int16_t size;
+ ret = krb5_ret_int16(sp, &size);
+ if(ret)
+ return ret;
+ data->length = size;
+ data->data = malloc(size);
+ if (data->data == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ ret = krb5_storage_read(sp, data->data, size);
+ if(ret != size)
+ return (ret < 0)? errno : KRB5_KT_END;
+ return 0;
+}
+
+static krb5_error_code
+krb5_kt_ret_string(krb5_context context,
+ krb5_storage *sp,
+ heim_general_string *data)
+{
+ int ret;
+ int16_t size;
+ ret = krb5_ret_int16(sp, &size);
+ if(ret)
+ return ret;
+ *data = malloc(size + 1);
+ if (*data == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ ret = krb5_storage_read(sp, *data, size);
+ (*data)[size] = '\0';
+ if(ret != size)
+ return (ret < 0)? errno : KRB5_KT_END;
+ return 0;
+}
+
+static krb5_error_code
+krb5_kt_store_data(krb5_context context,
+ krb5_storage *sp,
+ krb5_data data)
+{
+ int ret;
+ ret = krb5_store_int16(sp, data.length);
+ if(ret < 0)
+ return ret;
+ ret = krb5_storage_write(sp, data.data, data.length);
+ if(ret != data.length){
+ if(ret < 0)
+ return errno;
+ return KRB5_KT_END;
+ }
+ return 0;
+}
+
+static krb5_error_code
+krb5_kt_store_string(krb5_storage *sp,
+ heim_general_string data)
+{
+ int ret;
+ size_t len = strlen(data);
+ ret = krb5_store_int16(sp, len);
+ if(ret < 0)
+ return ret;
+ ret = krb5_storage_write(sp, data, len);
+ if(ret != len){
+ if(ret < 0)
+ return errno;
+ return KRB5_KT_END;
+ }
+ return 0;
+}
+
+static krb5_error_code
+krb5_kt_ret_keyblock(krb5_context context,
+ struct fkt_data *fkt,
+ krb5_storage *sp,
+ krb5_keyblock *p)
+{
+ int ret;
+ int16_t tmp;
+
+ ret = krb5_ret_int16(sp, &tmp); /* keytype + etype */
+ if(ret) {
+ krb5_set_error_message(context, ret,
+ N_("Cant read keyblock from file %s", ""),
+ fkt->filename);
+ return ret;
+ }
+ p->keytype = tmp;
+ ret = krb5_kt_ret_data(context, sp, &p->keyvalue);
+ if (ret)
+ krb5_set_error_message(context, ret,
+ N_("Cant read keyblock from file %s", ""),
+ fkt->filename);
+ return ret;
+}
+
+static krb5_error_code
+krb5_kt_store_keyblock(krb5_context context,
+ struct fkt_data *fkt,
+ krb5_storage *sp,
+ krb5_keyblock *p)
+{
+ int ret;
+
+ ret = krb5_store_int16(sp, p->keytype); /* keytype + etype */
+ if(ret) {
+ krb5_set_error_message(context, ret,
+ N_("Cant store keyblock to file %s", ""),
+ fkt->filename);
+ return ret;
+ }
+ ret = krb5_kt_store_data(context, sp, p->keyvalue);
+ if (ret)
+ krb5_set_error_message(context, ret,
+ N_("Cant store keyblock to file %s", ""),
+ fkt->filename);
+ return ret;
+}
+
+
+static krb5_error_code
+krb5_kt_ret_principal(krb5_context context,
+ struct fkt_data *fkt,
+ krb5_storage *sp,
+ krb5_principal *princ)
+{
+ int i;
+ int ret;
+ krb5_principal p;
+ int16_t len;
+
+ ALLOC(p, 1);
+ if(p == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+
+ ret = krb5_ret_int16(sp, &len);
+ if(ret) {
+ krb5_set_error_message(context, ret,
+ N_("Failed decoding length of "
+ "keytab principal in keytab file %s", ""),
+ fkt->filename);
+ goto out;
+ }
+ if(krb5_storage_is_flags(sp, KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS))
+ len--;
+ if (len < 0) {
+ ret = KRB5_KT_END;
+ krb5_set_error_message(context, ret,
+ N_("Keytab principal contains "
+ "invalid length in keytab %s", ""),
+ fkt->filename);
+ goto out;
+ }
+ ret = krb5_kt_ret_string(context, sp, &p->realm);
+ if(ret) {
+ krb5_set_error_message(context, ret,
+ N_("Can't read realm from keytab: %s", ""),
+ fkt->filename);
+ goto out;
+ }
+ p->name.name_string.val = calloc(len, sizeof(*p->name.name_string.val));
+ if(p->name.name_string.val == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ goto out;
+ }
+ p->name.name_string.len = len;
+ for(i = 0; i < p->name.name_string.len; i++){
+ ret = krb5_kt_ret_string(context, sp, p->name.name_string.val + i);
+ if(ret) {
+ krb5_set_error_message(context, ret,
+ N_("Can't read principal from "
+ "keytab: %s", ""),
+ fkt->filename);
+ goto out;
+ }
+ }
+ if (krb5_storage_is_flags(sp, KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE))
+ p->name.name_type = KRB5_NT_UNKNOWN;
+ else {
+ int32_t tmp32;
+ ret = krb5_ret_int32(sp, &tmp32);
+ p->name.name_type = tmp32;
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("Can't read name-type from "
+ "keytab: %s", ""),
+ fkt->filename);
+ goto out;
+ }
+ }
+ *princ = p;
+ return 0;
+out:
+ krb5_free_principal(context, p);
+ return ret;
+}
+
+static krb5_error_code
+krb5_kt_store_principal(krb5_context context,
+ krb5_storage *sp,
+ krb5_principal p)
+{
+ int i;
+ int ret;
+
+ if(krb5_storage_is_flags(sp, KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS))
+ ret = krb5_store_int16(sp, p->name.name_string.len + 1);
+ else
+ ret = krb5_store_int16(sp, p->name.name_string.len);
+ if(ret) return ret;
+ ret = krb5_kt_store_string(sp, p->realm);
+ if(ret) return ret;
+ for(i = 0; i < p->name.name_string.len; i++){
+ ret = krb5_kt_store_string(sp, p->name.name_string.val[i]);
+ if(ret)
+ return ret;
+ }
+ if(!krb5_storage_is_flags(sp, KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE)) {
+ ret = krb5_store_int32(sp, p->name.name_type);
+ if(ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static krb5_error_code
+fkt_resolve(krb5_context context, const char *name, krb5_keytab id)
+{
+ struct fkt_data *d;
+
+ d = malloc(sizeof(*d));
+ if(d == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ d->filename = strdup(name);
+ if(d->filename == NULL) {
+ free(d);
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ d->flags = 0;
+ id->data = d;
+ return 0;
+}
+
+static krb5_error_code
+fkt_resolve_java14(krb5_context context, const char *name, krb5_keytab id)
+{
+ krb5_error_code ret;
+
+ ret = fkt_resolve(context, name, id);
+ if (ret == 0) {
+ struct fkt_data *d = id->data;
+ d->flags |= KRB5_KT_FL_JAVA;
+ }
+ return ret;
+}
+
+static krb5_error_code
+fkt_close(krb5_context context, krb5_keytab id)
+{
+ struct fkt_data *d = id->data;
+ free(d->filename);
+ free(d);
+ return 0;
+}
+
+static krb5_error_code
+fkt_get_name(krb5_context context,
+ krb5_keytab id,
+ char *name,
+ size_t namesize)
+{
+ /* This function is XXX */
+ struct fkt_data *d = id->data;
+ strlcpy(name, d->filename, namesize);
+ return 0;
+}
+
+static void
+storage_set_flags(krb5_context context, krb5_storage *sp, int vno)
+{
+ int flags = 0;
+ switch(vno) {
+ case KRB5_KT_VNO_1:
+ flags |= KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS;
+ flags |= KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE;
+ flags |= KRB5_STORAGE_HOST_BYTEORDER;
+ break;
+ case KRB5_KT_VNO_2:
+ break;
+ default:
+ krb5_warnx(context,
+ "storage_set_flags called with bad vno (%d)", vno);
+ }
+ krb5_storage_set_flags(sp, flags);
+}
+
+static krb5_error_code
+fkt_start_seq_get_int(krb5_context context,
+ krb5_keytab id,
+ int flags,
+ int exclusive,
+ krb5_kt_cursor *c)
+{
+ int8_t pvno, tag;
+ krb5_error_code ret;
+ struct fkt_data *d = id->data;
+
+ c->fd = open (d->filename, flags);
+ if (c->fd < 0) {
+ ret = errno;
+ krb5_set_error_message(context, ret,
+ N_("keytab %s open failed: %s", ""),
+ d->filename, strerror(ret));
+ return ret;
+ }
+ rk_cloexec(c->fd);
+ ret = _krb5_xlock(context, c->fd, exclusive, d->filename);
+ if (ret) {
+ close(c->fd);
+ return ret;
+ }
+ c->sp = krb5_storage_from_fd(c->fd);
+ if (c->sp == NULL) {
+ _krb5_xunlock(context, c->fd);
+ close(c->fd);
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ krb5_storage_set_eof_code(c->sp, KRB5_KT_END);
+ ret = krb5_ret_int8(c->sp, &pvno);
+ if(ret) {
+ krb5_storage_free(c->sp);
+ _krb5_xunlock(context, c->fd);
+ close(c->fd);
+ krb5_clear_error_message(context);
+ return ret;
+ }
+ if(pvno != 5) {
+ krb5_storage_free(c->sp);
+ _krb5_xunlock(context, c->fd);
+ close(c->fd);
+ krb5_clear_error_message (context);
+ return KRB5_KEYTAB_BADVNO;
+ }
+ ret = krb5_ret_int8(c->sp, &tag);
+ if (ret) {
+ krb5_storage_free(c->sp);
+ _krb5_xunlock(context, c->fd);
+ close(c->fd);
+ krb5_clear_error_message(context);
+ return ret;
+ }
+ id->version = tag;
+ storage_set_flags(context, c->sp, id->version);
+ return 0;
+}
+
+static krb5_error_code
+fkt_start_seq_get(krb5_context context,
+ krb5_keytab id,
+ krb5_kt_cursor *c)
+{
+ return fkt_start_seq_get_int(context, id, O_RDONLY | O_BINARY | O_CLOEXEC, 0, c);
+}
+
+static krb5_error_code
+fkt_next_entry_int(krb5_context context,
+ krb5_keytab id,
+ krb5_keytab_entry *entry,
+ krb5_kt_cursor *cursor,
+ off_t *start,
+ off_t *end)
+{
+ struct fkt_data *d = id->data;
+ int32_t len;
+ int ret;
+ int8_t tmp8;
+ int32_t tmp32;
+ off_t pos, curpos;
+
+ pos = krb5_storage_seek(cursor->sp, 0, SEEK_CUR);
+loop:
+ ret = krb5_ret_int32(cursor->sp, &len);
+ if (ret)
+ return ret;
+ if(len < 0) {
+ pos = krb5_storage_seek(cursor->sp, -len, SEEK_CUR);
+ goto loop;
+ }
+ ret = krb5_kt_ret_principal (context, d, cursor->sp, &entry->principal);
+ if (ret)
+ goto out;
+ ret = krb5_ret_int32(cursor->sp, &tmp32);
+ entry->timestamp = tmp32;
+ if (ret)
+ goto out;
+ ret = krb5_ret_int8(cursor->sp, &tmp8);
+ if (ret)
+ goto out;
+ entry->vno = tmp8;
+ ret = krb5_kt_ret_keyblock (context, d, cursor->sp, &entry->keyblock);
+ if (ret)
+ goto out;
+ /* there might be a 32 bit kvno here
+ * if it's zero, assume that the 8bit one was right,
+ * otherwise trust the new value */
+ curpos = krb5_storage_seek(cursor->sp, 0, SEEK_CUR);
+ if(len + 4 + pos - curpos >= 4) {
+ ret = krb5_ret_int32(cursor->sp, &tmp32);
+ if (ret == 0 && tmp32 != 0) {
+ entry->vno = tmp32;
+ }
+ }
+ if(start) *start = pos;
+ if(end) *end = pos + 4 + len;
+ out:
+ krb5_storage_seek(cursor->sp, pos + 4 + len, SEEK_SET);
+ return ret;
+}
+
+static krb5_error_code
+fkt_next_entry(krb5_context context,
+ krb5_keytab id,
+ krb5_keytab_entry *entry,
+ krb5_kt_cursor *cursor)
+{
+ return fkt_next_entry_int(context, id, entry, cursor, NULL, NULL);
+}
+
+static krb5_error_code
+fkt_end_seq_get(krb5_context context,
+ krb5_keytab id,
+ krb5_kt_cursor *cursor)
+{
+ krb5_storage_free(cursor->sp);
+ _krb5_xunlock(context, cursor->fd);
+ close(cursor->fd);
+ return 0;
+}
+
+static krb5_error_code
+fkt_setup_keytab(krb5_context context,
+ krb5_keytab id,
+ krb5_storage *sp)
+{
+ krb5_error_code ret;
+ ret = krb5_store_int8(sp, 5);
+ if(ret)
+ return ret;
+ if(id->version == 0)
+ id->version = KRB5_KT_VNO;
+ return krb5_store_int8 (sp, id->version);
+}
+
+static krb5_error_code
+fkt_add_entry(krb5_context context,
+ krb5_keytab id,
+ krb5_keytab_entry *entry)
+{
+ int ret;
+ int fd;
+ krb5_storage *sp;
+ struct fkt_data *d = id->data;
+ krb5_data keytab;
+ int32_t len;
+
+ fd = open (d->filename, O_RDWR | O_BINARY | O_CLOEXEC);
+ if (fd < 0) {
+ fd = open (d->filename, O_RDWR | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC, 0600);
+ if (fd < 0) {
+ ret = errno;
+ krb5_set_error_message(context, ret,
+ N_("open(%s): %s", ""), d->filename,
+ strerror(ret));
+ return ret;
+ }
+ rk_cloexec(fd);
+
+ ret = _krb5_xlock(context, fd, 1, d->filename);
+ if (ret) {
+ close(fd);
+ return ret;
+ }
+ sp = krb5_storage_from_fd(fd);
+ krb5_storage_set_eof_code(sp, KRB5_KT_END);
+ ret = fkt_setup_keytab(context, id, sp);
+ if(ret) {
+ goto out;
+ }
+ storage_set_flags(context, sp, id->version);
+ } else {
+ int8_t pvno, tag;
+
+ rk_cloexec(fd);
+
+ ret = _krb5_xlock(context, fd, 1, d->filename);
+ if (ret) {
+ close(fd);
+ return ret;
+ }
+ sp = krb5_storage_from_fd(fd);
+ krb5_storage_set_eof_code(sp, KRB5_KT_END);
+ ret = krb5_ret_int8(sp, &pvno);
+ if(ret) {
+ /* we probably have a zero byte file, so try to set it up
+ properly */
+ ret = fkt_setup_keytab(context, id, sp);
+ if(ret) {
+ krb5_set_error_message(context, ret,
+ N_("%s: keytab is corrupted: %s", ""),
+ d->filename, strerror(ret));
+ goto out;
+ }
+ storage_set_flags(context, sp, id->version);
+ } else {
+ if(pvno != 5) {
+ ret = KRB5_KEYTAB_BADVNO;
+ krb5_set_error_message(context, ret,
+ N_("Bad version in keytab %s", ""),
+ d->filename);
+ goto out;
+ }
+ ret = krb5_ret_int8 (sp, &tag);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("failed reading tag from "
+ "keytab %s", ""),
+ d->filename);
+ goto out;
+ }
+ id->version = tag;
+ storage_set_flags(context, sp, id->version);
+ }
+ }
+
+ {
+ krb5_storage *emem;
+ emem = krb5_storage_emem();
+ if(emem == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret,
+ N_("malloc: out of memory", ""));
+ goto out;
+ }
+ ret = krb5_kt_store_principal(context, emem, entry->principal);
+ if(ret) {
+ krb5_set_error_message(context, ret,
+ N_("Failed storing principal "
+ "in keytab %s", ""),
+ d->filename);
+ krb5_storage_free(emem);
+ goto out;
+ }
+ ret = krb5_store_int32 (emem, entry->timestamp);
+ if(ret) {
+ krb5_set_error_message(context, ret,
+ N_("Failed storing timpstamp "
+ "in keytab %s", ""),
+ d->filename);
+ krb5_storage_free(emem);
+ goto out;
+ }
+ ret = krb5_store_int8 (emem, entry->vno % 256);
+ if(ret) {
+ krb5_set_error_message(context, ret,
+ N_("Failed storing kvno "
+ "in keytab %s", ""),
+ d->filename);
+ krb5_storage_free(emem);
+ goto out;
+ }
+ ret = krb5_kt_store_keyblock (context, d, emem, &entry->keyblock);
+ if(ret) {
+ krb5_storage_free(emem);
+ goto out;
+ }
+ if ((d->flags & KRB5_KT_FL_JAVA) == 0) {
+ ret = krb5_store_int32 (emem, entry->vno);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("Failed storing extended kvno "
+ "in keytab %s", ""),
+ d->filename);
+ krb5_storage_free(emem);
+ goto out;
+ }
+ }
+
+ ret = krb5_storage_to_data(emem, &keytab);
+ krb5_storage_free(emem);
+ if(ret) {
+ krb5_set_error_message(context, ret,
+ N_("Failed converting keytab entry "
+ "to memory block for keytab %s", ""),
+ d->filename);
+ goto out;
+ }
+ }
+
+ while(1) {
+ ret = krb5_ret_int32(sp, &len);
+ if(ret == KRB5_KT_END) {
+ len = keytab.length;
+ break;
+ }
+ if(len < 0) {
+ len = -len;
+ if(len >= keytab.length) {
+ krb5_storage_seek(sp, -4, SEEK_CUR);
+ break;
+ }
+ }
+ krb5_storage_seek(sp, len, SEEK_CUR);
+ }
+ ret = krb5_store_int32(sp, len);
+ if(krb5_storage_write(sp, keytab.data, keytab.length) < 0) {
+ ret = errno;
+ krb5_set_error_message(context, ret,
+ N_("Failed writing keytab block "
+ "in keytab %s: %s", ""),
+ d->filename, strerror(ret));
+ }
+ memset(keytab.data, 0, keytab.length);
+ krb5_data_free(&keytab);
+ out:
+ krb5_storage_free(sp);
+ _krb5_xunlock(context, fd);
+ close(fd);
+ return ret;
+}
+
+static krb5_error_code
+fkt_remove_entry(krb5_context context,
+ krb5_keytab id,
+ krb5_keytab_entry *entry)
+{
+ krb5_keytab_entry e;
+ krb5_kt_cursor cursor;
+ off_t pos_start, pos_end;
+ int found = 0;
+ krb5_error_code ret;
+
+ ret = fkt_start_seq_get_int(context, id, O_RDWR | O_BINARY | O_CLOEXEC, 1, &cursor);
+ if(ret != 0)
+ goto out; /* return other error here? */
+ while(fkt_next_entry_int(context, id, &e, &cursor,
+ &pos_start, &pos_end) == 0) {
+ if(krb5_kt_compare(context, &e, entry->principal,
+ entry->vno, entry->keyblock.keytype)) {
+ int32_t len;
+ unsigned char buf[128];
+ found = 1;
+ krb5_storage_seek(cursor.sp, pos_start, SEEK_SET);
+ len = pos_end - pos_start - 4;
+ krb5_store_int32(cursor.sp, -len);
+ memset(buf, 0, sizeof(buf));
+ while(len > 0) {
+ krb5_storage_write(cursor.sp, buf, min(len, sizeof(buf)));
+ len -= min(len, sizeof(buf));
+ }
+ }
+ krb5_kt_free_entry(context, &e);
+ }
+ krb5_kt_end_seq_get(context, id, &cursor);
+ out:
+ if (!found) {
+ krb5_clear_error_message (context);
+ return KRB5_KT_NOTFOUND;
+ }
+ return 0;
+}
+
+const krb5_kt_ops krb5_fkt_ops = {
+ "FILE",
+ fkt_resolve,
+ fkt_get_name,
+ fkt_close,
+ NULL, /* get */
+ fkt_start_seq_get,
+ fkt_next_entry,
+ fkt_end_seq_get,
+ fkt_add_entry,
+ fkt_remove_entry
+};
+
+const krb5_kt_ops krb5_wrfkt_ops = {
+ "WRFILE",
+ fkt_resolve,
+ fkt_get_name,
+ fkt_close,
+ NULL, /* get */
+ fkt_start_seq_get,
+ fkt_next_entry,
+ fkt_end_seq_get,
+ fkt_add_entry,
+ fkt_remove_entry
+};
+
+const krb5_kt_ops krb5_javakt_ops = {
+ "JAVA14",
+ fkt_resolve_java14,
+ fkt_get_name,
+ fkt_close,
+ NULL, /* get */
+ fkt_start_seq_get,
+ fkt_next_entry,
+ fkt_end_seq_get,
+ fkt_add_entry,
+ fkt_remove_entry
+};
diff --git a/source4/heimdal/lib/krb5/keytab_keyfile.c b/source4/heimdal/lib/krb5/keytab_keyfile.c
new file mode 100644
index 0000000000..71d3d89d58
--- /dev/null
+++ b/source4/heimdal/lib/krb5/keytab_keyfile.c
@@ -0,0 +1,451 @@
+/*
+ * Copyright (c) 1997 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+#ifndef HEIMDAL_SMALLER
+
+/* afs keyfile operations --------------------------------------- */
+
+/*
+ * Minimum tools to handle the AFS KeyFile.
+ *
+ * Format of the KeyFile is:
+ * <int32_t numkeys> {[<int32_t kvno> <char[8] deskey>] * numkeys}
+ *
+ * It just adds to the end of the keyfile, deleting isn't implemented.
+ * Use your favorite text/hex editor to delete keys.
+ *
+ */
+
+#define AFS_SERVERTHISCELL "/usr/afs/etc/ThisCell"
+#define AFS_SERVERMAGICKRBCONF "/usr/afs/etc/krb.conf"
+
+struct akf_data {
+ uint32_t num_entries;
+ char *filename;
+ char *cell;
+ char *realm;
+};
+
+/*
+ * set `d->cell' and `d->realm'
+ */
+
+static int
+get_cell_and_realm (krb5_context context, struct akf_data *d)
+{
+ FILE *f;
+ char buf[BUFSIZ], *cp;
+ int ret;
+
+ f = fopen (AFS_SERVERTHISCELL, "r");
+ if (f == NULL) {
+ ret = errno;
+ krb5_set_error_message (context, ret,
+ N_("Open ThisCell %s: %s", ""),
+ AFS_SERVERTHISCELL,
+ strerror(ret));
+ return ret;
+ }
+ if (fgets (buf, sizeof(buf), f) == NULL) {
+ fclose (f);
+ krb5_set_error_message (context, EINVAL,
+ N_("No cell in ThisCell file %s", ""),
+ AFS_SERVERTHISCELL);
+ return EINVAL;
+ }
+ buf[strcspn(buf, "\n")] = '\0';
+ fclose(f);
+
+ d->cell = strdup (buf);
+ if (d->cell == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+
+ f = fopen (AFS_SERVERMAGICKRBCONF, "r");
+ if (f != NULL) {
+ if (fgets (buf, sizeof(buf), f) == NULL) {
+ free (d->cell);
+ d->cell = NULL;
+ fclose (f);
+ krb5_set_error_message (context, EINVAL,
+ N_("No realm in ThisCell file %s", ""),
+ AFS_SERVERMAGICKRBCONF);
+ return EINVAL;
+ }
+ buf[strcspn(buf, "\n")] = '\0';
+ fclose(f);
+ }
+ /* uppercase */
+ for (cp = buf; *cp != '\0'; cp++)
+ *cp = toupper((unsigned char)*cp);
+
+ d->realm = strdup (buf);
+ if (d->realm == NULL) {
+ free (d->cell);
+ d->cell = NULL;
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ return 0;
+}
+
+/*
+ * init and get filename
+ */
+
+static krb5_error_code
+akf_resolve(krb5_context context, const char *name, krb5_keytab id)
+{
+ int ret;
+ struct akf_data *d = malloc(sizeof (struct akf_data));
+
+ if (d == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+
+ d->num_entries = 0;
+ ret = get_cell_and_realm (context, d);
+ if (ret) {
+ free (d);
+ return ret;
+ }
+ d->filename = strdup (name);
+ if (d->filename == NULL) {
+ free (d->cell);
+ free (d->realm);
+ free (d);
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ id->data = d;
+
+ return 0;
+}
+
+/*
+ * cleanup
+ */
+
+static krb5_error_code
+akf_close(krb5_context context, krb5_keytab id)
+{
+ struct akf_data *d = id->data;
+
+ free (d->filename);
+ free (d->cell);
+ free (d);
+ return 0;
+}
+
+/*
+ * Return filename
+ */
+
+static krb5_error_code
+akf_get_name(krb5_context context,
+ krb5_keytab id,
+ char *name,
+ size_t name_sz)
+{
+ struct akf_data *d = id->data;
+
+ strlcpy (name, d->filename, name_sz);
+ return 0;
+}
+
+/*
+ * Init
+ */
+
+static krb5_error_code
+akf_start_seq_get(krb5_context context,
+ krb5_keytab id,
+ krb5_kt_cursor *c)
+{
+ int32_t ret;
+ struct akf_data *d = id->data;
+
+ c->fd = open (d->filename, O_RDONLY | O_BINARY | O_CLOEXEC, 0600);
+ if (c->fd < 0) {
+ ret = errno;
+ krb5_set_error_message(context, ret,
+ N_("keytab afs keyfile open %s failed: %s", ""),
+ d->filename, strerror(ret));
+ return ret;
+ }
+
+ c->sp = krb5_storage_from_fd(c->fd);
+ ret = krb5_ret_uint32(c->sp, &d->num_entries);
+ if(ret) {
+ krb5_storage_free(c->sp);
+ close(c->fd);
+ krb5_clear_error_message (context);
+ if(ret == KRB5_KT_END)
+ return KRB5_KT_NOTFOUND;
+ return ret;
+ }
+
+ return 0;
+}
+
+static krb5_error_code
+akf_next_entry(krb5_context context,
+ krb5_keytab id,
+ krb5_keytab_entry *entry,
+ krb5_kt_cursor *cursor)
+{
+ struct akf_data *d = id->data;
+ int32_t kvno;
+ off_t pos;
+ int ret;
+
+ pos = krb5_storage_seek(cursor->sp, 0, SEEK_CUR);
+
+ if ((pos - 4) / (4 + 8) >= d->num_entries)
+ return KRB5_KT_END;
+
+ ret = krb5_make_principal (context, &entry->principal,
+ d->realm, "afs", d->cell, NULL);
+ if (ret)
+ goto out;
+
+ ret = krb5_ret_int32(cursor->sp, &kvno);
+ if (ret) {
+ krb5_free_principal (context, entry->principal);
+ goto out;
+ }
+
+ entry->vno = kvno;
+
+ entry->keyblock.keytype = ETYPE_DES_CBC_MD5;
+ entry->keyblock.keyvalue.length = 8;
+ entry->keyblock.keyvalue.data = malloc (8);
+ if (entry->keyblock.keyvalue.data == NULL) {
+ krb5_free_principal (context, entry->principal);
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ ret = ENOMEM;
+ goto out;
+ }
+
+ ret = krb5_storage_read(cursor->sp, entry->keyblock.keyvalue.data, 8);
+ if(ret != 8)
+ ret = (ret < 0) ? errno : KRB5_KT_END;
+ else
+ ret = 0;
+
+ entry->timestamp = time(NULL);
+
+ out:
+ krb5_storage_seek(cursor->sp, pos + 4 + 8, SEEK_SET);
+ return ret;
+}
+
+static krb5_error_code
+akf_end_seq_get(krb5_context context,
+ krb5_keytab id,
+ krb5_kt_cursor *cursor)
+{
+ krb5_storage_free(cursor->sp);
+ close(cursor->fd);
+ return 0;
+}
+
+static krb5_error_code
+akf_add_entry(krb5_context context,
+ krb5_keytab id,
+ krb5_keytab_entry *entry)
+{
+ struct akf_data *d = id->data;
+ int fd, created = 0;
+ krb5_error_code ret;
+ int32_t len;
+ krb5_storage *sp;
+
+
+ if (entry->keyblock.keyvalue.length != 8)
+ return 0;
+ switch(entry->keyblock.keytype) {
+ case ETYPE_DES_CBC_CRC:
+ case ETYPE_DES_CBC_MD4:
+ case ETYPE_DES_CBC_MD5:
+ break;
+ default:
+ return 0;
+ }
+
+ fd = open (d->filename, O_RDWR | O_BINARY | O_CLOEXEC);
+ if (fd < 0) {
+ fd = open (d->filename,
+ O_RDWR | O_BINARY | O_CREAT | O_EXCL | O_CLOEXEC, 0600);
+ if (fd < 0) {
+ ret = errno;
+ krb5_set_error_message(context, ret,
+ N_("open keyfile(%s): %s", ""),
+ d->filename,
+ strerror(ret));
+ return ret;
+ }
+ created = 1;
+ }
+
+ sp = krb5_storage_from_fd(fd);
+ if(sp == NULL) {
+ close(fd);
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ if (created)
+ len = 0;
+ else {
+ if(krb5_storage_seek(sp, 0, SEEK_SET) < 0) {
+ ret = errno;
+ krb5_storage_free(sp);
+ close(fd);
+ krb5_set_error_message(context, ret,
+ N_("seeking in keyfile: %s", ""),
+ strerror(ret));
+ return ret;
+ }
+
+ ret = krb5_ret_int32(sp, &len);
+ if(ret) {
+ krb5_storage_free(sp);
+ close(fd);
+ return ret;
+ }
+ }
+
+ /*
+ * Make sure we don't add the entry twice, assumes the DES
+ * encryption types are all the same key.
+ */
+ if (len > 0) {
+ int32_t kvno;
+ int i;
+
+ for (i = 0; i < len; i++) {
+ ret = krb5_ret_int32(sp, &kvno);
+ if (ret) {
+ krb5_set_error_message (context, ret,
+ N_("Failed getting kvno from keyfile", ""));
+ goto out;
+ }
+ if(krb5_storage_seek(sp, 8, SEEK_CUR) < 0) {
+ ret = errno;
+ krb5_set_error_message (context, ret,
+ N_("Failed seeing in keyfile: %s", ""),
+ strerror(ret));
+ goto out;
+ }
+ if (kvno == entry->vno) {
+ ret = 0;
+ goto out;
+ }
+ }
+ }
+
+ len++;
+
+ if(krb5_storage_seek(sp, 0, SEEK_SET) < 0) {
+ ret = errno;
+ krb5_set_error_message (context, ret,
+ N_("Failed seeing in keyfile: %s", ""),
+ strerror(ret));
+ goto out;
+ }
+
+ ret = krb5_store_int32(sp, len);
+ if(ret) {
+ ret = errno;
+ krb5_set_error_message (context, ret,
+ N_("keytab keyfile failed new length", ""));
+ return ret;
+ }
+
+ if(krb5_storage_seek(sp, (len - 1) * (8 + 4), SEEK_CUR) < 0) {
+ ret = errno;
+ krb5_set_error_message (context, ret,
+ N_("seek to end: %s", ""), strerror(ret));
+ goto out;
+ }
+
+ ret = krb5_store_int32(sp, entry->vno);
+ if(ret) {
+ krb5_set_error_message(context, ret,
+ N_("keytab keyfile failed store kvno", ""));
+ goto out;
+ }
+ ret = krb5_storage_write(sp, entry->keyblock.keyvalue.data,
+ entry->keyblock.keyvalue.length);
+ if(ret != entry->keyblock.keyvalue.length) {
+ if (ret < 0)
+ ret = errno;
+ else
+ ret = ENOTTY;
+ krb5_set_error_message(context, ret,
+ N_("keytab keyfile failed to add key", ""));
+ goto out;
+ }
+ ret = 0;
+out:
+ krb5_storage_free(sp);
+ close (fd);
+ return ret;
+}
+
+const krb5_kt_ops krb5_akf_ops = {
+ "AFSKEYFILE",
+ akf_resolve,
+ akf_get_name,
+ akf_close,
+ NULL, /* get */
+ akf_start_seq_get,
+ akf_next_entry,
+ akf_end_seq_get,
+ akf_add_entry,
+ NULL /* remove */
+};
+
+#endif /* HEIMDAL_SMALLER */
diff --git a/source4/heimdal/lib/krb5/keytab_memory.c b/source4/heimdal/lib/krb5/keytab_memory.c
new file mode 100644
index 0000000000..defd10d67c
--- /dev/null
+++ b/source4/heimdal/lib/krb5/keytab_memory.c
@@ -0,0 +1,237 @@
+/*
+ * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+/* memory operations -------------------------------------------- */
+
+struct mkt_data {
+ krb5_keytab_entry *entries;
+ int num_entries;
+ char *name;
+ int refcount;
+ struct mkt_data *next;
+};
+
+/* this mutex protects mkt_head, ->refcount, and ->next
+ * content is not protected (name is static and need no protection)
+ */
+static HEIMDAL_MUTEX mkt_mutex = HEIMDAL_MUTEX_INITIALIZER;
+static struct mkt_data *mkt_head;
+
+
+static krb5_error_code
+mkt_resolve(krb5_context context, const char *name, krb5_keytab id)
+{
+ struct mkt_data *d;
+
+ HEIMDAL_MUTEX_lock(&mkt_mutex);
+
+ for (d = mkt_head; d != NULL; d = d->next)
+ if (strcmp(d->name, name) == 0)
+ break;
+ if (d) {
+ if (d->refcount < 1)
+ krb5_abortx(context, "Double close on memory keytab, "
+ "refcount < 1 %d", d->refcount);
+ d->refcount++;
+ id->data = d;
+ HEIMDAL_MUTEX_unlock(&mkt_mutex);
+ return 0;
+ }
+
+ d = calloc(1, sizeof(*d));
+ if(d == NULL) {
+ HEIMDAL_MUTEX_unlock(&mkt_mutex);
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ d->name = strdup(name);
+ if (d->name == NULL) {
+ HEIMDAL_MUTEX_unlock(&mkt_mutex);
+ free(d);
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ d->entries = NULL;
+ d->num_entries = 0;
+ d->refcount = 1;
+ d->next = mkt_head;
+ mkt_head = d;
+ HEIMDAL_MUTEX_unlock(&mkt_mutex);
+ id->data = d;
+ return 0;
+}
+
+static krb5_error_code
+mkt_close(krb5_context context, krb5_keytab id)
+{
+ struct mkt_data *d = id->data, **dp;
+ int i;
+
+ HEIMDAL_MUTEX_lock(&mkt_mutex);
+ if (d->refcount < 1)
+ krb5_abortx(context,
+ "krb5 internal error, memory keytab refcount < 1 on close");
+
+ if (--d->refcount > 0) {
+ HEIMDAL_MUTEX_unlock(&mkt_mutex);
+ return 0;
+ }
+ for (dp = &mkt_head; *dp != NULL; dp = &(*dp)->next) {
+ if (*dp == d) {
+ *dp = d->next;
+ break;
+ }
+ }
+ HEIMDAL_MUTEX_unlock(&mkt_mutex);
+
+ free(d->name);
+ for(i = 0; i < d->num_entries; i++)
+ krb5_kt_free_entry(context, &d->entries[i]);
+ free(d->entries);
+ free(d);
+ return 0;
+}
+
+static krb5_error_code
+mkt_get_name(krb5_context context,
+ krb5_keytab id,
+ char *name,
+ size_t namesize)
+{
+ struct mkt_data *d = id->data;
+ strlcpy(name, d->name, namesize);
+ return 0;
+}
+
+static krb5_error_code
+mkt_start_seq_get(krb5_context context,
+ krb5_keytab id,
+ krb5_kt_cursor *c)
+{
+ /* XXX */
+ c->fd = 0;
+ return 0;
+}
+
+static krb5_error_code
+mkt_next_entry(krb5_context context,
+ krb5_keytab id,
+ krb5_keytab_entry *entry,
+ krb5_kt_cursor *c)
+{
+ struct mkt_data *d = id->data;
+ if(c->fd >= d->num_entries)
+ return KRB5_KT_END;
+ return krb5_kt_copy_entry_contents(context, &d->entries[c->fd++], entry);
+}
+
+static krb5_error_code
+mkt_end_seq_get(krb5_context context,
+ krb5_keytab id,
+ krb5_kt_cursor *cursor)
+{
+ return 0;
+}
+
+static krb5_error_code
+mkt_add_entry(krb5_context context,
+ krb5_keytab id,
+ krb5_keytab_entry *entry)
+{
+ struct mkt_data *d = id->data;
+ krb5_keytab_entry *tmp;
+ tmp = realloc(d->entries, (d->num_entries + 1) * sizeof(*d->entries));
+ if(tmp == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ d->entries = tmp;
+ return krb5_kt_copy_entry_contents(context, entry,
+ &d->entries[d->num_entries++]);
+}
+
+static krb5_error_code
+mkt_remove_entry(krb5_context context,
+ krb5_keytab id,
+ krb5_keytab_entry *entry)
+{
+ struct mkt_data *d = id->data;
+ krb5_keytab_entry *e, *end;
+ int found = 0;
+
+ if (d->num_entries == 0) {
+ krb5_clear_error_message(context);
+ return KRB5_KT_NOTFOUND;
+ }
+
+ /* do this backwards to minimize copying */
+ for(end = d->entries + d->num_entries, e = end - 1; e >= d->entries; e--) {
+ if(krb5_kt_compare(context, e, entry->principal,
+ entry->vno, entry->keyblock.keytype)) {
+ krb5_kt_free_entry(context, e);
+ memmove(e, e + 1, (end - e - 1) * sizeof(*e));
+ memset(end - 1, 0, sizeof(*end));
+ d->num_entries--;
+ end--;
+ found = 1;
+ }
+ }
+ if (!found) {
+ krb5_clear_error_message (context);
+ return KRB5_KT_NOTFOUND;
+ }
+ e = realloc(d->entries, d->num_entries * sizeof(*d->entries));
+ if(e != NULL || d->num_entries == 0)
+ d->entries = e;
+ return 0;
+}
+
+const krb5_kt_ops krb5_mkt_ops = {
+ "MEMORY",
+ mkt_resolve,
+ mkt_get_name,
+ mkt_close,
+ NULL, /* get */
+ mkt_start_seq_get,
+ mkt_next_entry,
+ mkt_end_seq_get,
+ mkt_add_entry,
+ mkt_remove_entry
+};
diff --git a/source4/heimdal/lib/krb5/krb5-v4compat.h b/source4/heimdal/lib/krb5/krb5-v4compat.h
new file mode 100644
index 0000000000..dde5fa9cad
--- /dev/null
+++ b/source4/heimdal/lib/krb5/krb5-v4compat.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 1997 - 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef __KRB5_V4COMPAT_H__
+#define __KRB5_V4COMPAT_H__
+
+#include "krb_err.h"
+
+/*
+ * This file must only be included with v4 compat glue stuff in
+ * heimdal sources.
+ *
+ * It MUST NOT be installed.
+ */
+
+#define KRB_PROT_VERSION 4
+
+#define AUTH_MSG_KDC_REQUEST (1<<1)
+#define AUTH_MSG_KDC_REPLY (2<<1)
+#define AUTH_MSG_APPL_REQUEST (3<<1)
+#define AUTH_MSG_APPL_REQUEST_MUTUAL (4<<1)
+#define AUTH_MSG_ERR_REPLY (5<<1)
+#define AUTH_MSG_PRIVATE (6<<1)
+#define AUTH_MSG_SAFE (7<<1)
+#define AUTH_MSG_APPL_ERR (8<<1)
+#define AUTH_MSG_KDC_FORWARD (9<<1)
+#define AUTH_MSG_KDC_RENEW (10<<1)
+#define AUTH_MSG_DIE (63<<1)
+
+/* General definitions */
+#define KSUCCESS 0
+#define KFAILURE 255
+
+/* */
+
+#define MAX_KTXT_LEN 1250
+
+#define ANAME_SZ 40
+#define REALM_SZ 40
+#define SNAME_SZ 40
+#define INST_SZ 40
+
+struct ktext {
+ unsigned int length; /* Length of the text */
+ unsigned char dat[MAX_KTXT_LEN]; /* The data itself */
+ uint32_t mbz; /* zero to catch runaway strings */
+};
+
+struct credentials {
+ char service[ANAME_SZ]; /* Service name */
+ char instance[INST_SZ]; /* Instance */
+ char realm[REALM_SZ]; /* Auth domain */
+ char session[8]; /* Session key */
+ int lifetime; /* Lifetime */
+ int kvno; /* Key version number */
+ struct ktext ticket_st; /* The ticket itself */
+ int32_t issue_date; /* The issue time */
+ char pname[ANAME_SZ]; /* Principal's name */
+ char pinst[INST_SZ]; /* Principal's instance */
+};
+
+#define TKTLIFENUMFIXED 64
+#define TKTLIFEMINFIXED 0x80
+#define TKTLIFEMAXFIXED 0xBF
+#define TKTLIFENOEXPIRE 0xFF
+#define MAXTKTLIFETIME (30*24*3600) /* 30 days */
+#ifndef NEVERDATE
+#define NEVERDATE ((time_t)0x7fffffffL)
+#endif
+
+#define KERB_ERR_NULL_KEY 10
+
+#define CLOCK_SKEW 5*60
+
+#ifndef TKT_ROOT
+#define TKT_ROOT "/tmp/tkt"
+#endif
+
+struct _krb5_krb_auth_data {
+ int8_t k_flags; /* Flags from ticket */
+ char *pname; /* Principal's name */
+ char *pinst; /* His Instance */
+ char *prealm; /* His Realm */
+ uint32_t checksum; /* Data checksum (opt) */
+ krb5_keyblock session; /* Session Key */
+ unsigned char life; /* Life of ticket */
+ uint32_t time_sec; /* Time ticket issued */
+ uint32_t address; /* Address in ticket */
+};
+
+time_t _krb5_krb_life_to_time (int, int);
+int _krb5_krb_time_to_life (time_t, time_t);
+krb5_error_code _krb5_krb_tf_setup (krb5_context, struct credentials *,
+ const char *, int);
+krb5_error_code _krb5_krb_dest_tkt(krb5_context, const char *);
+
+#define krb_time_to_life _krb5_krb_time_to_life
+#define krb_life_to_time _krb5_krb_life_to_time
+
+#endif /* __KRB5_V4COMPAT_H__ */
diff --git a/source4/heimdal/lib/krb5/krb5.h b/source4/heimdal/lib/krb5/krb5.h
new file mode 100644
index 0000000000..0ba4e7b54a
--- /dev/null
+++ b/source4/heimdal/lib/krb5/krb5.h
@@ -0,0 +1,814 @@
+/*
+ * Copyright (c) 1997 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef __KRB5_H__
+#define __KRB5_H__
+
+#include <time.h>
+#include <krb5-types.h>
+
+#include <asn1_err.h>
+#include <krb5_err.h>
+#include <heim_err.h>
+#include <k524_err.h>
+
+#include <krb5_asn1.h>
+
+/* name confusion with MIT */
+#ifndef KRB5KDC_ERR_KEY_EXP
+#define KRB5KDC_ERR_KEY_EXP KRB5KDC_ERR_KEY_EXPIRED
+#endif
+
+/* simple constants */
+
+#ifndef TRUE
+#define TRUE 1
+#define FALSE 0
+#endif
+
+typedef int krb5_boolean;
+
+typedef int32_t krb5_error_code;
+
+typedef int krb5_kvno;
+
+typedef uint32_t krb5_flags;
+
+typedef void *krb5_pointer;
+typedef const void *krb5_const_pointer;
+
+struct krb5_crypto_data;
+typedef struct krb5_crypto_data *krb5_crypto;
+
+struct krb5_get_creds_opt_data;
+typedef struct krb5_get_creds_opt_data *krb5_get_creds_opt;
+
+struct krb5_digest_data;
+typedef struct krb5_digest_data *krb5_digest;
+struct krb5_ntlm_data;
+typedef struct krb5_ntlm_data *krb5_ntlm;
+
+struct krb5_pac_data;
+typedef struct krb5_pac_data *krb5_pac;
+
+typedef struct krb5_rd_req_in_ctx_data *krb5_rd_req_in_ctx;
+typedef struct krb5_rd_req_out_ctx_data *krb5_rd_req_out_ctx;
+
+typedef CKSUMTYPE krb5_cksumtype;
+
+typedef Checksum krb5_checksum;
+
+typedef ENCTYPE krb5_enctype;
+
+typedef heim_octet_string krb5_data;
+
+/* PKINIT related forward declarations */
+struct ContentInfo;
+struct krb5_pk_identity;
+struct krb5_pk_cert;
+
+/* krb5_enc_data is a mit compat structure */
+typedef struct krb5_enc_data {
+ krb5_enctype enctype;
+ krb5_kvno kvno;
+ krb5_data ciphertext;
+} krb5_enc_data;
+
+/* alternative names */
+enum {
+ ENCTYPE_NULL = ETYPE_NULL,
+ ENCTYPE_DES_CBC_CRC = ETYPE_DES_CBC_CRC,
+ ENCTYPE_DES_CBC_MD4 = ETYPE_DES_CBC_MD4,
+ ENCTYPE_DES_CBC_MD5 = ETYPE_DES_CBC_MD5,
+ ENCTYPE_DES3_CBC_MD5 = ETYPE_DES3_CBC_MD5,
+ ENCTYPE_OLD_DES3_CBC_SHA1 = ETYPE_OLD_DES3_CBC_SHA1,
+ ENCTYPE_SIGN_DSA_GENERATE = ETYPE_SIGN_DSA_GENERATE,
+ ENCTYPE_ENCRYPT_RSA_PRIV = ETYPE_ENCRYPT_RSA_PRIV,
+ ENCTYPE_ENCRYPT_RSA_PUB = ETYPE_ENCRYPT_RSA_PUB,
+ ENCTYPE_DES3_CBC_SHA1 = ETYPE_DES3_CBC_SHA1,
+ ENCTYPE_AES128_CTS_HMAC_SHA1_96 = ETYPE_AES128_CTS_HMAC_SHA1_96,
+ ENCTYPE_AES256_CTS_HMAC_SHA1_96 = ETYPE_AES256_CTS_HMAC_SHA1_96,
+ ENCTYPE_ARCFOUR_HMAC = ETYPE_ARCFOUR_HMAC_MD5,
+ ENCTYPE_ARCFOUR_HMAC_MD5 = ETYPE_ARCFOUR_HMAC_MD5,
+ ENCTYPE_ARCFOUR_HMAC_MD5_56 = ETYPE_ARCFOUR_HMAC_MD5_56,
+ ENCTYPE_ENCTYPE_PK_CROSS = ETYPE_ENCTYPE_PK_CROSS,
+ ENCTYPE_DES_CBC_NONE = ETYPE_DES_CBC_NONE,
+ ENCTYPE_DES3_CBC_NONE = ETYPE_DES3_CBC_NONE,
+ ENCTYPE_DES_CFB64_NONE = ETYPE_DES_CFB64_NONE,
+ ENCTYPE_DES_PCBC_NONE = ETYPE_DES_PCBC_NONE
+};
+
+typedef PADATA_TYPE krb5_preauthtype;
+
+typedef enum krb5_key_usage {
+ KRB5_KU_PA_ENC_TIMESTAMP = 1,
+ /* AS-REQ PA-ENC-TIMESTAMP padata timestamp, encrypted with the
+ client key (section 5.4.1) */
+ KRB5_KU_TICKET = 2,
+ /* AS-REP Ticket and TGS-REP Ticket (includes tgs session key or
+ application session key), encrypted with the service key
+ (section 5.4.2) */
+ KRB5_KU_AS_REP_ENC_PART = 3,
+ /* AS-REP encrypted part (includes tgs session key or application
+ session key), encrypted with the client key (section 5.4.2) */
+ KRB5_KU_TGS_REQ_AUTH_DAT_SESSION = 4,
+ /* TGS-REQ KDC-REQ-BODY AuthorizationData, encrypted with the tgs
+ session key (section 5.4.1) */
+ KRB5_KU_TGS_REQ_AUTH_DAT_SUBKEY = 5,
+ /* TGS-REQ KDC-REQ-BODY AuthorizationData, encrypted with the tgs
+ authenticator subkey (section 5.4.1) */
+ KRB5_KU_TGS_REQ_AUTH_CKSUM = 6,
+ /* TGS-REQ PA-TGS-REQ padata AP-REQ Authenticator cksum, keyed
+ with the tgs session key (sections 5.3.2, 5.4.1) */
+ KRB5_KU_TGS_REQ_AUTH = 7,
+ /* TGS-REQ PA-TGS-REQ padata AP-REQ Authenticator (includes tgs
+ authenticator subkey), encrypted with the tgs session key
+ (section 5.3.2) */
+ KRB5_KU_TGS_REP_ENC_PART_SESSION = 8,
+ /* TGS-REP encrypted part (includes application session key),
+ encrypted with the tgs session key (section 5.4.2) */
+ KRB5_KU_TGS_REP_ENC_PART_SUB_KEY = 9,
+ /* TGS-REP encrypted part (includes application session key),
+ encrypted with the tgs authenticator subkey (section 5.4.2) */
+ KRB5_KU_AP_REQ_AUTH_CKSUM = 10,
+ /* AP-REQ Authenticator cksum, keyed with the application session
+ key (section 5.3.2) */
+ KRB5_KU_AP_REQ_AUTH = 11,
+ /* AP-REQ Authenticator (includes application authenticator
+ subkey), encrypted with the application session key (section
+ 5.3.2) */
+ KRB5_KU_AP_REQ_ENC_PART = 12,
+ /* AP-REP encrypted part (includes application session subkey),
+ encrypted with the application session key (section 5.5.2) */
+ KRB5_KU_KRB_PRIV = 13,
+ /* KRB-PRIV encrypted part, encrypted with a key chosen by the
+ application (section 5.7.1) */
+ KRB5_KU_KRB_CRED = 14,
+ /* KRB-CRED encrypted part, encrypted with a key chosen by the
+ application (section 5.8.1) */
+ KRB5_KU_KRB_SAFE_CKSUM = 15,
+ /* KRB-SAFE cksum, keyed with a key chosen by the application
+ (section 5.6.1) */
+ KRB5_KU_OTHER_ENCRYPTED = 16,
+ /* Data which is defined in some specification outside of
+ Kerberos to be encrypted using an RFC1510 encryption type. */
+ KRB5_KU_OTHER_CKSUM = 17,
+ /* Data which is defined in some specification outside of
+ Kerberos to be checksummed using an RFC1510 checksum type. */
+ KRB5_KU_KRB_ERROR = 18,
+ /* Krb-error checksum */
+ KRB5_KU_AD_KDC_ISSUED = 19,
+ /* AD-KDCIssued checksum */
+ KRB5_KU_MANDATORY_TICKET_EXTENSION = 20,
+ /* Checksum for Mandatory Ticket Extensions */
+ KRB5_KU_AUTH_DATA_TICKET_EXTENSION = 21,
+ /* Checksum in Authorization Data in Ticket Extensions */
+ KRB5_KU_USAGE_SEAL = 22,
+ /* seal in GSSAPI krb5 mechanism */
+ KRB5_KU_USAGE_SIGN = 23,
+ /* sign in GSSAPI krb5 mechanism */
+ KRB5_KU_USAGE_SEQ = 24,
+ /* SEQ in GSSAPI krb5 mechanism */
+ KRB5_KU_USAGE_ACCEPTOR_SEAL = 22,
+ /* acceptor sign in GSSAPI CFX krb5 mechanism */
+ KRB5_KU_USAGE_ACCEPTOR_SIGN = 23,
+ /* acceptor seal in GSSAPI CFX krb5 mechanism */
+ KRB5_KU_USAGE_INITIATOR_SEAL = 24,
+ /* initiator sign in GSSAPI CFX krb5 mechanism */
+ KRB5_KU_USAGE_INITIATOR_SIGN = 25,
+ /* initiator seal in GSSAPI CFX krb5 mechanism */
+ KRB5_KU_PA_SERVER_REFERRAL_DATA = 22,
+ /* encrypted server referral data */
+ KRB5_KU_SAM_CHECKSUM = 25,
+ /* Checksum for the SAM-CHECKSUM field */
+ KRB5_KU_SAM_ENC_TRACK_ID = 26,
+ /* Encryption of the SAM-TRACK-ID field */
+ KRB5_KU_PA_SERVER_REFERRAL = 26,
+ /* Keyusage for the server referral in a TGS req */
+ KRB5_KU_SAM_ENC_NONCE_SAD = 27,
+ /* Encryption of the SAM-NONCE-OR-SAD field */
+ KRB5_KU_DIGEST_ENCRYPT = -18,
+ /* Encryption key usage used in the digest encryption field */
+ KRB5_KU_DIGEST_OPAQUE = -19,
+ /* Checksum key usage used in the digest opaque field */
+ KRB5_KU_KRB5SIGNEDPATH = -21,
+ /* Checksum key usage on KRB5SignedPath */
+ KRB5_KU_CANONICALIZED_NAMES = -23
+ /* Checksum key usage on PA-CANONICALIZED */
+} krb5_key_usage;
+
+typedef krb5_key_usage krb5_keyusage;
+
+typedef enum krb5_salttype {
+ KRB5_PW_SALT = KRB5_PADATA_PW_SALT,
+ KRB5_AFS3_SALT = KRB5_PADATA_AFS3_SALT
+}krb5_salttype;
+
+typedef struct krb5_salt {
+ krb5_salttype salttype;
+ krb5_data saltvalue;
+} krb5_salt;
+
+typedef ETYPE_INFO krb5_preauthinfo;
+
+typedef struct {
+ krb5_preauthtype type;
+ krb5_preauthinfo info; /* list of preauthinfo for this type */
+} krb5_preauthdata_entry;
+
+typedef struct krb5_preauthdata {
+ unsigned len;
+ krb5_preauthdata_entry *val;
+}krb5_preauthdata;
+
+typedef enum krb5_address_type {
+ KRB5_ADDRESS_INET = 2,
+ KRB5_ADDRESS_NETBIOS = 20,
+ KRB5_ADDRESS_INET6 = 24,
+ KRB5_ADDRESS_ADDRPORT = 256,
+ KRB5_ADDRESS_IPPORT = 257
+} krb5_address_type;
+
+enum {
+ AP_OPTS_USE_SESSION_KEY = 1,
+ AP_OPTS_MUTUAL_REQUIRED = 2,
+ AP_OPTS_USE_SUBKEY = 4 /* library internal */
+};
+
+typedef HostAddress krb5_address;
+
+typedef HostAddresses krb5_addresses;
+
+typedef enum krb5_keytype {
+ KEYTYPE_NULL = 0,
+ KEYTYPE_DES = 1,
+ KEYTYPE_DES3 = 7,
+ KEYTYPE_AES128 = 17,
+ KEYTYPE_AES256 = 18,
+ KEYTYPE_ARCFOUR = 23,
+ KEYTYPE_ARCFOUR_56 = 24
+} krb5_keytype;
+
+typedef EncryptionKey krb5_keyblock;
+
+typedef AP_REQ krb5_ap_req;
+
+struct krb5_cc_ops;
+
+#define KRB5_DEFAULT_CCFILE_ROOT "/tmp/krb5cc_"
+
+#define KRB5_DEFAULT_CCROOT "FILE:" KRB5_DEFAULT_CCFILE_ROOT
+
+#define KRB5_ACCEPT_NULL_ADDRESSES(C) \
+ krb5_config_get_bool_default((C), NULL, TRUE, \
+ "libdefaults", "accept_null_addresses", \
+ NULL)
+
+typedef void *krb5_cc_cursor;
+typedef struct krb5_cccol_cursor *krb5_cccol_cursor;
+
+typedef struct krb5_ccache_data {
+ const struct krb5_cc_ops *ops;
+ krb5_data data;
+}krb5_ccache_data;
+
+typedef struct krb5_ccache_data *krb5_ccache;
+
+typedef struct krb5_context_data *krb5_context;
+
+typedef Realm krb5_realm;
+typedef const char *krb5_const_realm; /* stupid language */
+
+#define krb5_realm_length(r) strlen(r)
+#define krb5_realm_data(r) (r)
+
+typedef Principal krb5_principal_data;
+typedef struct Principal *krb5_principal;
+typedef const struct Principal *krb5_const_principal;
+
+typedef time_t krb5_deltat;
+typedef time_t krb5_timestamp;
+
+typedef struct krb5_times {
+ krb5_timestamp authtime;
+ krb5_timestamp starttime;
+ krb5_timestamp endtime;
+ krb5_timestamp renew_till;
+} krb5_times;
+
+typedef union {
+ TicketFlags b;
+ krb5_flags i;
+} krb5_ticket_flags;
+
+/* options for krb5_get_in_tkt() */
+#define KDC_OPT_FORWARDABLE (1 << 1)
+#define KDC_OPT_FORWARDED (1 << 2)
+#define KDC_OPT_PROXIABLE (1 << 3)
+#define KDC_OPT_PROXY (1 << 4)
+#define KDC_OPT_ALLOW_POSTDATE (1 << 5)
+#define KDC_OPT_POSTDATED (1 << 6)
+#define KDC_OPT_RENEWABLE (1 << 8)
+#define KDC_OPT_REQUEST_ANONYMOUS (1 << 14)
+#define KDC_OPT_DISABLE_TRANSITED_CHECK (1 << 26)
+#define KDC_OPT_RENEWABLE_OK (1 << 27)
+#define KDC_OPT_ENC_TKT_IN_SKEY (1 << 28)
+#define KDC_OPT_RENEW (1 << 30)
+#define KDC_OPT_VALIDATE (1 << 31)
+
+typedef union {
+ KDCOptions b;
+ krb5_flags i;
+} krb5_kdc_flags;
+
+/* flags for krb5_verify_ap_req */
+
+#define KRB5_VERIFY_AP_REQ_IGNORE_INVALID (1 << 0)
+
+#define KRB5_GC_CACHED (1U << 0)
+#define KRB5_GC_USER_USER (1U << 1)
+#define KRB5_GC_EXPIRED_OK (1U << 2)
+#define KRB5_GC_NO_STORE (1U << 3)
+#define KRB5_GC_FORWARDABLE (1U << 4)
+#define KRB5_GC_NO_TRANSIT_CHECK (1U << 5)
+#define KRB5_GC_CONSTRAINED_DELEGATION (1U << 6)
+#define KRB5_GC_CANONICALIZE (1U << 7)
+
+/* constants for compare_creds (and cc_retrieve_cred) */
+#define KRB5_TC_DONT_MATCH_REALM (1U << 31)
+#define KRB5_TC_MATCH_KEYTYPE (1U << 30)
+#define KRB5_TC_MATCH_KTYPE KRB5_TC_MATCH_KEYTYPE /* MIT name */
+#define KRB5_TC_MATCH_SRV_NAMEONLY (1 << 29)
+#define KRB5_TC_MATCH_FLAGS_EXACT (1 << 28)
+#define KRB5_TC_MATCH_FLAGS (1 << 27)
+#define KRB5_TC_MATCH_TIMES_EXACT (1 << 26)
+#define KRB5_TC_MATCH_TIMES (1 << 25)
+#define KRB5_TC_MATCH_AUTHDATA (1 << 24)
+#define KRB5_TC_MATCH_2ND_TKT (1 << 23)
+#define KRB5_TC_MATCH_IS_SKEY (1 << 22)
+
+typedef AuthorizationData krb5_authdata;
+
+typedef KRB_ERROR krb5_error;
+
+typedef struct krb5_creds {
+ krb5_principal client;
+ krb5_principal server;
+ krb5_keyblock session;
+ krb5_times times;
+ krb5_data ticket;
+ krb5_data second_ticket;
+ krb5_authdata authdata;
+ krb5_addresses addresses;
+ krb5_ticket_flags flags;
+} krb5_creds;
+
+typedef struct krb5_cc_cache_cursor_data *krb5_cc_cache_cursor;
+
+#define KRB5_CC_OPS_VERSION 2
+
+typedef struct krb5_cc_ops {
+ int version;
+ const char *prefix;
+ const char* (*get_name)(krb5_context, krb5_ccache);
+ krb5_error_code (*resolve)(krb5_context, krb5_ccache *, const char *);
+ krb5_error_code (*gen_new)(krb5_context, krb5_ccache *);
+ krb5_error_code (*init)(krb5_context, krb5_ccache, krb5_principal);
+ krb5_error_code (*destroy)(krb5_context, krb5_ccache);
+ krb5_error_code (*close)(krb5_context, krb5_ccache);
+ krb5_error_code (*store)(krb5_context, krb5_ccache, krb5_creds*);
+ krb5_error_code (*retrieve)(krb5_context, krb5_ccache,
+ krb5_flags, const krb5_creds*, krb5_creds *);
+ krb5_error_code (*get_princ)(krb5_context, krb5_ccache, krb5_principal*);
+ krb5_error_code (*get_first)(krb5_context, krb5_ccache, krb5_cc_cursor *);
+ krb5_error_code (*get_next)(krb5_context, krb5_ccache,
+ krb5_cc_cursor*, krb5_creds*);
+ krb5_error_code (*end_get)(krb5_context, krb5_ccache, krb5_cc_cursor*);
+ krb5_error_code (*remove_cred)(krb5_context, krb5_ccache,
+ krb5_flags, krb5_creds*);
+ krb5_error_code (*set_flags)(krb5_context, krb5_ccache, krb5_flags);
+ int (*get_version)(krb5_context, krb5_ccache);
+ krb5_error_code (*get_cache_first)(krb5_context, krb5_cc_cursor *);
+ krb5_error_code (*get_cache_next)(krb5_context, krb5_cc_cursor, krb5_ccache *);
+ krb5_error_code (*end_cache_get)(krb5_context, krb5_cc_cursor);
+ krb5_error_code (*move)(krb5_context, krb5_ccache, krb5_ccache);
+ krb5_error_code (*get_default_name)(krb5_context, char **);
+ krb5_error_code (*set_default)(krb5_context, krb5_ccache);
+ krb5_error_code (*lastchange)(krb5_context, krb5_ccache, krb5_timestamp *);
+} krb5_cc_ops;
+
+struct krb5_log_facility;
+
+struct krb5_config_binding {
+ enum { krb5_config_string, krb5_config_list } type;
+ char *name;
+ struct krb5_config_binding *next;
+ union {
+ char *string;
+ struct krb5_config_binding *list;
+ void *generic;
+ } u;
+};
+
+typedef struct krb5_config_binding krb5_config_binding;
+
+typedef krb5_config_binding krb5_config_section;
+
+typedef struct krb5_ticket {
+ EncTicketPart ticket;
+ krb5_principal client;
+ krb5_principal server;
+} krb5_ticket;
+
+typedef Authenticator krb5_authenticator_data;
+
+typedef krb5_authenticator_data *krb5_authenticator;
+
+struct krb5_rcache_data;
+typedef struct krb5_rcache_data *krb5_rcache;
+typedef Authenticator krb5_donot_replay;
+
+#define KRB5_STORAGE_HOST_BYTEORDER 0x01 /* old */
+#define KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS 0x02
+#define KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE 0x04
+#define KRB5_STORAGE_KEYBLOCK_KEYTYPE_TWICE 0x08
+#define KRB5_STORAGE_BYTEORDER_MASK 0x60
+#define KRB5_STORAGE_BYTEORDER_BE 0x00 /* default */
+#define KRB5_STORAGE_BYTEORDER_LE 0x20
+#define KRB5_STORAGE_BYTEORDER_HOST 0x40
+#define KRB5_STORAGE_CREDS_FLAGS_WRONG_BITORDER 0x80
+
+struct krb5_storage_data;
+typedef struct krb5_storage_data krb5_storage;
+
+typedef struct krb5_keytab_entry {
+ krb5_principal principal;
+ krb5_kvno vno;
+ krb5_keyblock keyblock;
+ uint32_t timestamp;
+} krb5_keytab_entry;
+
+typedef struct krb5_kt_cursor {
+ int fd;
+ krb5_storage *sp;
+ void *data;
+} krb5_kt_cursor;
+
+struct krb5_keytab_data;
+
+typedef struct krb5_keytab_data *krb5_keytab;
+
+#define KRB5_KT_PREFIX_MAX_LEN 30
+
+struct krb5_keytab_data {
+ const char *prefix;
+ krb5_error_code (*resolve)(krb5_context, const char*, krb5_keytab);
+ krb5_error_code (*get_name)(krb5_context, krb5_keytab, char*, size_t);
+ krb5_error_code (*close)(krb5_context, krb5_keytab);
+ krb5_error_code (*get)(krb5_context, krb5_keytab, krb5_const_principal,
+ krb5_kvno, krb5_enctype, krb5_keytab_entry*);
+ krb5_error_code (*start_seq_get)(krb5_context, krb5_keytab, krb5_kt_cursor*);
+ krb5_error_code (*next_entry)(krb5_context, krb5_keytab,
+ krb5_keytab_entry*, krb5_kt_cursor*);
+ krb5_error_code (*end_seq_get)(krb5_context, krb5_keytab, krb5_kt_cursor*);
+ krb5_error_code (*add)(krb5_context, krb5_keytab, krb5_keytab_entry*);
+ krb5_error_code (*remove)(krb5_context, krb5_keytab, krb5_keytab_entry*);
+ void *data;
+ int32_t version;
+};
+
+typedef struct krb5_keytab_data krb5_kt_ops;
+
+struct krb5_keytab_key_proc_args {
+ krb5_keytab keytab;
+ krb5_principal principal;
+};
+
+typedef struct krb5_keytab_key_proc_args krb5_keytab_key_proc_args;
+
+typedef struct krb5_replay_data {
+ krb5_timestamp timestamp;
+ int32_t usec;
+ uint32_t seq;
+} krb5_replay_data;
+
+/* flags for krb5_auth_con_setflags */
+enum {
+ KRB5_AUTH_CONTEXT_DO_TIME = 1,
+ KRB5_AUTH_CONTEXT_RET_TIME = 2,
+ KRB5_AUTH_CONTEXT_DO_SEQUENCE = 4,
+ KRB5_AUTH_CONTEXT_RET_SEQUENCE = 8,
+ KRB5_AUTH_CONTEXT_PERMIT_ALL = 16,
+ KRB5_AUTH_CONTEXT_USE_SUBKEY = 32,
+ KRB5_AUTH_CONTEXT_CLEAR_FORWARDED_CRED = 64
+};
+
+/* flags for krb5_auth_con_genaddrs */
+enum {
+ KRB5_AUTH_CONTEXT_GENERATE_LOCAL_ADDR = 1,
+ KRB5_AUTH_CONTEXT_GENERATE_LOCAL_FULL_ADDR = 3,
+ KRB5_AUTH_CONTEXT_GENERATE_REMOTE_ADDR = 4,
+ KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR = 12
+};
+
+typedef struct krb5_auth_context_data {
+ unsigned int flags;
+
+ krb5_address *local_address;
+ krb5_address *remote_address;
+ int16_t local_port;
+ int16_t remote_port;
+ krb5_keyblock *keyblock;
+ krb5_keyblock *local_subkey;
+ krb5_keyblock *remote_subkey;
+
+ uint32_t local_seqnumber;
+ uint32_t remote_seqnumber;
+
+ krb5_authenticator authenticator;
+
+ krb5_pointer i_vector;
+
+ krb5_rcache rcache;
+
+ krb5_keytype keytype; /* ¿requested key type ? */
+ krb5_cksumtype cksumtype; /* ¡requested checksum type! */
+
+}krb5_auth_context_data, *krb5_auth_context;
+
+typedef struct {
+ KDC_REP kdc_rep;
+ EncKDCRepPart enc_part;
+ KRB_ERROR error;
+} krb5_kdc_rep;
+
+extern const char *heimdal_version, *heimdal_long_version;
+
+typedef void (*krb5_log_log_func_t)(const char*, const char*, void*);
+typedef void (*krb5_log_close_func_t)(void*);
+
+typedef struct krb5_log_facility {
+ char *program;
+ int len;
+ struct facility *val;
+} krb5_log_facility;
+
+typedef EncAPRepPart krb5_ap_rep_enc_part;
+
+#define KRB5_RECVAUTH_IGNORE_VERSION 1
+
+#define KRB5_SENDAUTH_VERSION "KRB5_SENDAUTH_V1.0"
+
+#define KRB5_TGS_NAME_SIZE (6)
+#define KRB5_TGS_NAME ("krbtgt")
+
+#define KRB5_DIGEST_NAME ("digest")
+
+typedef enum {
+ KRB5_PROMPT_TYPE_PASSWORD = 0x1,
+ KRB5_PROMPT_TYPE_NEW_PASSWORD = 0x2,
+ KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN = 0x3,
+ KRB5_PROMPT_TYPE_PREAUTH = 0x4,
+ KRB5_PROMPT_TYPE_INFO = 0x5
+} krb5_prompt_type;
+
+typedef struct _krb5_prompt {
+ const char *prompt;
+ int hidden;
+ krb5_data *reply;
+ krb5_prompt_type type;
+} krb5_prompt;
+
+typedef int (*krb5_prompter_fct)(krb5_context /*context*/,
+ void * /*data*/,
+ const char * /*name*/,
+ const char * /*banner*/,
+ int /*num_prompts*/,
+ krb5_prompt /*prompts*/[]);
+typedef krb5_error_code (*krb5_key_proc)(krb5_context /*context*/,
+ krb5_enctype /*type*/,
+ krb5_salt /*salt*/,
+ krb5_const_pointer /*keyseed*/,
+ krb5_keyblock ** /*key*/);
+typedef krb5_error_code (*krb5_decrypt_proc)(krb5_context /*context*/,
+ krb5_keyblock * /*key*/,
+ krb5_key_usage /*usage*/,
+ krb5_const_pointer /*decrypt_arg*/,
+ krb5_kdc_rep * /*dec_rep*/);
+typedef krb5_error_code (*krb5_s2k_proc)(krb5_context /*context*/,
+ krb5_enctype /*type*/,
+ krb5_const_pointer /*keyseed*/,
+ krb5_salt /*salt*/,
+ krb5_data * /*s2kparms*/,
+ krb5_keyblock ** /*key*/);
+
+struct _krb5_get_init_creds_opt_private;
+
+typedef struct _krb5_get_init_creds_opt {
+ krb5_flags flags;
+ krb5_deltat tkt_life;
+ krb5_deltat renew_life;
+ int forwardable;
+ int proxiable;
+ int anonymous;
+ krb5_enctype *etype_list;
+ int etype_list_length;
+ krb5_addresses *address_list;
+ /* XXX the next three should not be used, as they may be
+ removed later */
+ krb5_preauthtype *preauth_list;
+ int preauth_list_length;
+ krb5_data *salt;
+ struct _krb5_get_init_creds_opt_private *opt_private;
+} krb5_get_init_creds_opt;
+
+#define KRB5_GET_INIT_CREDS_OPT_TKT_LIFE 0x0001
+#define KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE 0x0002
+#define KRB5_GET_INIT_CREDS_OPT_FORWARDABLE 0x0004
+#define KRB5_GET_INIT_CREDS_OPT_PROXIABLE 0x0008
+#define KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST 0x0010
+#define KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST 0x0020
+#define KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST 0x0040
+#define KRB5_GET_INIT_CREDS_OPT_SALT 0x0080
+#define KRB5_GET_INIT_CREDS_OPT_ANONYMOUS 0x0100
+#define KRB5_GET_INIT_CREDS_OPT_DISABLE_TRANSITED_CHECK 0x0200
+
+typedef struct _krb5_verify_init_creds_opt {
+ krb5_flags flags;
+ int ap_req_nofail;
+} krb5_verify_init_creds_opt;
+
+#define KRB5_VERIFY_INIT_CREDS_OPT_AP_REQ_NOFAIL 0x0001
+
+typedef struct krb5_verify_opt {
+ unsigned int flags;
+ krb5_ccache ccache;
+ krb5_keytab keytab;
+ krb5_boolean secure;
+ const char *service;
+} krb5_verify_opt;
+
+#define KRB5_VERIFY_LREALMS 1
+#define KRB5_VERIFY_NO_ADDRESSES 2
+
+#define KRB5_KPASSWD_VERS_CHANGEPW 1
+#define KRB5_KPASSWD_VERS_SETPW 0xff80
+
+#define KRB5_KPASSWD_SUCCESS 0
+#define KRB5_KPASSWD_MALFORMED 1
+#define KRB5_KPASSWD_HARDERROR 2
+#define KRB5_KPASSWD_AUTHERROR 3
+#define KRB5_KPASSWD_SOFTERROR 4
+#define KRB5_KPASSWD_ACCESSDENIED 5
+#define KRB5_KPASSWD_BAD_VERSION 6
+#define KRB5_KPASSWD_INITIAL_FLAG_NEEDED 7
+
+#define KPASSWD_PORT 464
+
+/* types for the new krbhst interface */
+struct krb5_krbhst_data;
+typedef struct krb5_krbhst_data *krb5_krbhst_handle;
+
+#define KRB5_KRBHST_KDC 1
+#define KRB5_KRBHST_ADMIN 2
+#define KRB5_KRBHST_CHANGEPW 3
+#define KRB5_KRBHST_KRB524 4
+#define KRB5_KRBHST_KCA 5
+
+typedef struct krb5_krbhst_info {
+ enum { KRB5_KRBHST_UDP,
+ KRB5_KRBHST_TCP,
+ KRB5_KRBHST_HTTP } proto;
+ unsigned short port;
+ unsigned short def_port;
+ struct addrinfo *ai;
+ struct krb5_krbhst_info *next;
+ char hostname[1]; /* has to come last */
+} krb5_krbhst_info;
+
+/* flags for krb5_krbhst_init_flags (and krb5_send_to_kdc_flags) */
+enum {
+ KRB5_KRBHST_FLAGS_MASTER = 1,
+ KRB5_KRBHST_FLAGS_LARGE_MSG = 2
+};
+
+typedef krb5_error_code (*krb5_send_to_kdc_func)(krb5_context,
+ void *,
+ krb5_krbhst_info *,
+ time_t timeout,
+ const krb5_data *,
+ krb5_data *);
+
+/* flags for krb5_parse_name_flags */
+enum {
+ KRB5_PRINCIPAL_PARSE_NO_REALM = 1,
+ KRB5_PRINCIPAL_PARSE_MUST_REALM = 2,
+ KRB5_PRINCIPAL_PARSE_ENTERPRISE = 4
+};
+
+/* flags for krb5_unparse_name_flags */
+enum {
+ KRB5_PRINCIPAL_UNPARSE_SHORT = 1,
+ KRB5_PRINCIPAL_UNPARSE_NO_REALM = 2,
+ KRB5_PRINCIPAL_UNPARSE_DISPLAY = 4
+};
+
+typedef struct krb5_sendto_ctx_data *krb5_sendto_ctx;
+
+#define KRB5_SENDTO_DONE 0
+#define KRB5_SENDTO_RESTART 1
+#define KRB5_SENDTO_CONTINUE 2
+
+typedef krb5_error_code (*krb5_sendto_ctx_func)(krb5_context, krb5_sendto_ctx, void *, const krb5_data *, int *);
+
+struct krb5_plugin;
+enum krb5_plugin_type {
+ PLUGIN_TYPE_DATA = 1,
+ PLUGIN_TYPE_FUNC
+};
+
+struct credentials; /* this is to keep the compiler happy */
+struct getargs;
+struct sockaddr;
+
+/**
+ * Semi private, not stable yet
+ */
+
+typedef struct krb5_crypto_iov {
+ unsigned int flags;
+ /* ignored */
+#define KRB5_CRYPTO_TYPE_EMPTY 0
+ /* OUT krb5_crypto_length(KRB5_CRYPTO_TYPE_HEADER) */
+#define KRB5_CRYPTO_TYPE_HEADER 1
+ /* IN and OUT */
+#define KRB5_CRYPTO_TYPE_DATA 2
+ /* IN */
+#define KRB5_CRYPTO_TYPE_SIGN_ONLY 3
+ /* (only for encryption) OUT krb5_crypto_length(KRB5_CRYPTO_TYPE_TRAILER) */
+#define KRB5_CRYPTO_TYPE_PADDING 4
+ /* OUT krb5_crypto_length(KRB5_CRYPTO_TYPE_TRAILER) */
+#define KRB5_CRYPTO_TYPE_TRAILER 5
+ /* OUT krb5_crypto_length(KRB5_CRYPTO_TYPE_CHECKSUM) */
+#define KRB5_CRYPTO_TYPE_CHECKSUM 6
+ krb5_data data;
+} krb5_crypto_iov;
+
+
+#include <krb5-protos.h>
+
+/* variables */
+
+extern KRB5_LIB_VARIABLE const char *krb5_config_file;
+extern KRB5_LIB_VARIABLE const char *krb5_defkeyname;
+
+
+extern KRB5_LIB_VARIABLE const krb5_cc_ops krb5_acc_ops;
+extern KRB5_LIB_VARIABLE const krb5_cc_ops krb5_fcc_ops;
+extern KRB5_LIB_VARIABLE const krb5_cc_ops krb5_mcc_ops;
+extern KRB5_LIB_VARIABLE const krb5_cc_ops krb5_kcm_ops;
+extern KRB5_LIB_VARIABLE const krb5_cc_ops krb5_scc_ops;
+
+extern KRB5_LIB_VARIABLE const krb5_kt_ops krb5_fkt_ops;
+extern KRB5_LIB_VARIABLE const krb5_kt_ops krb5_wrfkt_ops;
+extern KRB5_LIB_VARIABLE const krb5_kt_ops krb5_javakt_ops;
+extern KRB5_LIB_VARIABLE const krb5_kt_ops krb5_mkt_ops;
+extern KRB5_LIB_VARIABLE const krb5_kt_ops krb5_akf_ops;
+extern KRB5_LIB_VARIABLE const krb5_kt_ops krb4_fkt_ops;
+extern KRB5_LIB_VARIABLE const krb5_kt_ops krb5_srvtab_fkt_ops;
+extern KRB5_LIB_VARIABLE const krb5_kt_ops krb5_any_ops;
+
+#endif /* __KRB5_H__ */
+
diff --git a/source4/heimdal/lib/krb5/krb5_ccapi.h b/source4/heimdal/lib/krb5/krb5_ccapi.h
new file mode 100644
index 0000000000..ec0cb3bc0b
--- /dev/null
+++ b/source4/heimdal/lib/krb5/krb5_ccapi.h
@@ -0,0 +1,230 @@
+/*
+ * Copyright (c) 2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef KRB5_CCAPI_H
+#define KRB5_CCAPI_H 1
+
+#include <krb5-types.h>
+
+enum {
+ cc_credentials_v5 = 2
+};
+
+enum {
+ ccapi_version_3 = 3,
+ ccapi_version_4 = 4
+};
+
+enum {
+ ccNoError = 0,
+
+ ccIteratorEnd = 201,
+ ccErrBadParam,
+ ccErrNoMem,
+ ccErrInvalidContext,
+ ccErrInvalidCCache,
+
+ ccErrInvalidString, /* 206 */
+ ccErrInvalidCredentials,
+ ccErrInvalidCCacheIterator,
+ ccErrInvalidCredentialsIterator,
+ ccErrInvalidLock,
+
+ ccErrBadName, /* 211 */
+ ccErrBadCredentialsVersion,
+ ccErrBadAPIVersion,
+ ccErrContextLocked,
+ ccErrContextUnlocked,
+
+ ccErrCCacheLocked, /* 216 */
+ ccErrCCacheUnlocked,
+ ccErrBadLockType,
+ ccErrNeverDefault,
+ ccErrCredentialsNotFound,
+
+ ccErrCCacheNotFound, /* 221 */
+ ccErrContextNotFound,
+ ccErrServerUnavailable,
+ ccErrServerInsecure,
+ ccErrServerCantBecomeUID,
+
+ ccErrTimeOffsetNotSet /* 226 */
+};
+
+typedef int32_t cc_int32;
+typedef uint32_t cc_uint32;
+typedef struct cc_context_t *cc_context_t;
+typedef struct cc_ccache_t *cc_ccache_t;
+typedef struct cc_ccache_iterator_t *cc_ccache_iterator_t;
+typedef struct cc_credentials_v5_t cc_credentials_v5_t;
+typedef struct cc_credentials_t *cc_credentials_t;
+typedef struct cc_credentials_iterator_t *cc_credentials_iterator_t;
+typedef struct cc_string_t *cc_string_t;
+typedef time_t cc_time_t;
+
+typedef struct cc_data {
+ cc_uint32 type;
+ cc_uint32 length;
+ void *data;
+} cc_data;
+
+struct cc_credentials_v5_t {
+ char *client;
+ char *server;
+ cc_data keyblock;
+ cc_time_t authtime;
+ cc_time_t starttime;
+ cc_time_t endtime;
+ cc_time_t renew_till;
+ cc_uint32 is_skey;
+ cc_uint32 ticket_flags;
+#define KRB5_CCAPI_TKT_FLG_FORWARDABLE 0x40000000
+#define KRB5_CCAPI_TKT_FLG_FORWARDED 0x20000000
+#define KRB5_CCAPI_TKT_FLG_PROXIABLE 0x10000000
+#define KRB5_CCAPI_TKT_FLG_PROXY 0x08000000
+#define KRB5_CCAPI_TKT_FLG_MAY_POSTDATE 0x04000000
+#define KRB5_CCAPI_TKT_FLG_POSTDATED 0x02000000
+#define KRB5_CCAPI_TKT_FLG_INVALID 0x01000000
+#define KRB5_CCAPI_TKT_FLG_RENEWABLE 0x00800000
+#define KRB5_CCAPI_TKT_FLG_INITIAL 0x00400000
+#define KRB5_CCAPI_TKT_FLG_PRE_AUTH 0x00200000
+#define KRB5_CCAPI_TKT_FLG_HW_AUTH 0x00100000
+#define KRB5_CCAPI_TKT_FLG_TRANSIT_POLICY_CHECKED 0x00080000
+#define KRB5_CCAPI_TKT_FLG_OK_AS_DELEGATE 0x00040000
+#define KRB5_CCAPI_TKT_FLG_ANONYMOUS 0x00020000
+ cc_data **addresses;
+ cc_data ticket;
+ cc_data second_ticket;
+ cc_data **authdata;
+};
+
+
+typedef struct cc_string_functions {
+ cc_int32 (*release)(cc_string_t);
+} cc_string_functions;
+
+struct cc_string_t {
+ const char *data;
+ const cc_string_functions *func;
+};
+
+typedef struct cc_credentials_union {
+ cc_int32 version;
+ union {
+ cc_credentials_v5_t* credentials_v5;
+ } credentials;
+} cc_credentials_union;
+
+struct cc_credentials_functions {
+ cc_int32 (*release)(cc_credentials_t);
+ cc_int32 (*compare)(cc_credentials_t, cc_credentials_t, cc_uint32*);
+};
+
+struct cc_credentials_t {
+ const cc_credentials_union* data;
+ const struct cc_credentials_functions* func;
+};
+
+struct cc_credentials_iterator_functions {
+ cc_int32 (*release)(cc_credentials_iterator_t);
+ cc_int32 (*next)(cc_credentials_iterator_t, cc_credentials_t*);
+};
+
+struct cc_credentials_iterator_t {
+ const struct cc_credentials_iterator_functions *func;
+};
+
+struct cc_ccache_iterator_functions {
+ cc_int32 (*release) (cc_ccache_iterator_t);
+ cc_int32 (*next)(cc_ccache_iterator_t, cc_ccache_t*);
+};
+
+struct cc_ccache_iterator_t {
+ const struct cc_ccache_iterator_functions* func;
+};
+
+typedef struct cc_ccache_functions {
+ cc_int32 (*release)(cc_ccache_t);
+ cc_int32 (*destroy)(cc_ccache_t);
+ cc_int32 (*set_default)(cc_ccache_t);
+ cc_int32 (*get_credentials_version)(cc_ccache_t, cc_uint32*);
+ cc_int32 (*get_name)(cc_ccache_t, cc_string_t*);
+ cc_int32 (*get_principal)(cc_ccache_t, cc_uint32, cc_string_t*);
+ cc_int32 (*set_principal)(cc_ccache_t, cc_uint32, const char*);
+ cc_int32 (*store_credentials)(cc_ccache_t, const cc_credentials_union*);
+ cc_int32 (*remove_credentials)(cc_ccache_t, cc_credentials_t);
+ cc_int32 (*new_credentials_iterator)(cc_ccache_t,
+ cc_credentials_iterator_t*);
+ cc_int32 (*move)(cc_ccache_t, cc_ccache_t);
+ cc_int32 (*lock)(cc_ccache_t, cc_uint32, cc_uint32);
+ cc_int32 (*unlock)(cc_ccache_t);
+ cc_int32 (*get_last_default_time)(cc_ccache_t, cc_time_t*);
+ cc_int32 (*get_change_time)(cc_ccache_t, cc_time_t*);
+ cc_int32 (*compare)(cc_ccache_t, cc_ccache_t, cc_uint32*);
+ cc_int32 (*get_kdc_time_offset)(cc_ccache_t, cc_int32, cc_time_t *);
+ cc_int32 (*set_kdc_time_offset)(cc_ccache_t, cc_int32, cc_time_t);
+ cc_int32 (*clear_kdc_time_offset)(cc_ccache_t, cc_int32);
+} cc_ccache_functions;
+
+struct cc_ccache_t {
+ const cc_ccache_functions *func;
+};
+
+struct cc_context_functions {
+ cc_int32 (*release)(cc_context_t);
+ cc_int32 (*get_change_time)(cc_context_t, cc_time_t *);
+ cc_int32 (*get_default_ccache_name)(cc_context_t, cc_string_t*);
+ cc_int32 (*open_ccache)(cc_context_t, const char*, cc_ccache_t *);
+ cc_int32 (*open_default_ccache)(cc_context_t, cc_ccache_t*);
+ cc_int32 (*create_ccache)(cc_context_t,const char*, cc_uint32,
+ const char*, cc_ccache_t*);
+ cc_int32 (*create_default_ccache)(cc_context_t, cc_uint32,
+ const char*, cc_ccache_t*);
+ cc_int32 (*create_new_ccache)(cc_context_t, cc_uint32,
+ const char*, cc_ccache_t*);
+ cc_int32 (*new_ccache_iterator)(cc_context_t, cc_ccache_iterator_t*);
+ cc_int32 (*lock)(cc_context_t, cc_uint32, cc_uint32);
+ cc_int32 (*unlock)(cc_context_t);
+ cc_int32 (*compare)(cc_context_t, cc_context_t, cc_uint32*);
+};
+
+struct cc_context_t {
+ const struct cc_context_functions* func;
+};
+
+typedef cc_int32
+(*cc_initialize_func)(cc_context_t*, cc_int32, cc_int32 *, char const **);
+
+#endif /* KRB5_CCAPI_H */
diff --git a/source4/heimdal/lib/krb5/krb5_err.et b/source4/heimdal/lib/krb5/krb5_err.et
new file mode 100644
index 0000000000..c076992d0b
--- /dev/null
+++ b/source4/heimdal/lib/krb5/krb5_err.et
@@ -0,0 +1,268 @@
+#
+# Error messages for the krb5 library
+#
+# This might look like a com_err file, but is not
+#
+id "$Id$"
+
+error_table krb5
+
+prefix KRB5KDC_ERR
+error_code NONE, "No error"
+error_code NAME_EXP, "Client's entry in database has expired"
+error_code SERVICE_EXP, "Server's entry in database has expired"
+error_code BAD_PVNO, "Requested protocol version not supported"
+error_code C_OLD_MAST_KVNO, "Client's key is encrypted in an old master key"
+error_code S_OLD_MAST_KVNO, "Server's key is encrypted in an old master key"
+error_code C_PRINCIPAL_UNKNOWN, "Client not found in Kerberos database"
+error_code S_PRINCIPAL_UNKNOWN, "Server not found in Kerberos database"
+error_code PRINCIPAL_NOT_UNIQUE,"Principal has multiple entries in Kerberos database"
+error_code NULL_KEY, "Client or server has a null key"
+error_code CANNOT_POSTDATE, "Ticket is ineligible for postdating"
+error_code NEVER_VALID, "Requested effective lifetime is negative or too short"
+error_code POLICY, "KDC policy rejects request"
+error_code BADOPTION, "KDC can't fulfill requested option"
+error_code ETYPE_NOSUPP, "KDC has no support for encryption type"
+error_code SUMTYPE_NOSUPP, "KDC has no support for checksum type"
+error_code PADATA_TYPE_NOSUPP, "KDC has no support for padata type"
+error_code TRTYPE_NOSUPP, "KDC has no support for transited type"
+error_code CLIENT_REVOKED, "Clients credentials have been revoked"
+error_code SERVICE_REVOKED, "Credentials for server have been revoked"
+error_code TGT_REVOKED, "TGT has been revoked"
+error_code CLIENT_NOTYET, "Client not yet valid - try again later"
+error_code SERVICE_NOTYET, "Server not yet valid - try again later"
+error_code KEY_EXPIRED, "Password has expired"
+error_code PREAUTH_FAILED, "Preauthentication failed"
+error_code PREAUTH_REQUIRED, "Additional pre-authentication required"
+error_code SERVER_NOMATCH, "Requested server and ticket don't match"
+error_code KDC_ERR_MUST_USE_USER2USER, "Server principal valid for user2user only"
+error_code PATH_NOT_ACCEPTED, "KDC Policy rejects transited path"
+error_code SVC_UNAVAILABLE, "A service is not available"
+
+index 31
+prefix KRB5KRB_AP
+error_code ERR_BAD_INTEGRITY, "Decrypt integrity check failed"
+error_code ERR_TKT_EXPIRED, "Ticket expired"
+error_code ERR_TKT_NYV, "Ticket not yet valid"
+error_code ERR_REPEAT, "Request is a replay"
+error_code ERR_NOT_US, "The ticket isn't for us"
+error_code ERR_BADMATCH, "Ticket/authenticator don't match"
+error_code ERR_SKEW, "Clock skew too great"
+error_code ERR_BADADDR, "Incorrect net address"
+error_code ERR_BADVERSION, "Protocol version mismatch"
+error_code ERR_MSG_TYPE, "Invalid message type"
+error_code ERR_MODIFIED, "Message stream modified"
+error_code ERR_BADORDER, "Message out of order"
+error_code ERR_ILL_CR_TKT, "Invalid cross-realm ticket"
+error_code ERR_BADKEYVER, "Key version is not available"
+error_code ERR_NOKEY, "Service key not available"
+error_code ERR_MUT_FAIL, "Mutual authentication failed"
+error_code ERR_BADDIRECTION, "Incorrect message direction"
+error_code ERR_METHOD, "Alternative authentication method required"
+error_code ERR_BADSEQ, "Incorrect sequence number in message"
+error_code ERR_INAPP_CKSUM, "Inappropriate type of checksum in message"
+error_code PATH_NOT_ACCEPTED, "Policy rejects transited path"
+
+prefix KRB5KRB_ERR
+error_code RESPONSE_TOO_BIG, "Response too big for UDP, retry with TCP"
+# 53-59 are reserved
+index 60
+error_code GENERIC, "Generic error (see e-text)"
+error_code FIELD_TOOLONG, "Field is too long for this implementation"
+
+# pkinit
+index 62
+prefix KRB5_KDC_ERR
+error_code CLIENT_NOT_TRUSTED, "Client not trusted"
+error_code KDC_NOT_TRUSTED, "KDC not trusted"
+error_code INVALID_SIG, "Invalid signature"
+error_code DH_KEY_PARAMETERS_NOT_ACCEPTED, "DH parameters not accepted"
+
+index 68
+prefix KRB5_KDC_ERR
+error_code WRONG_REALM, "Wrong realm"
+
+index 69
+prefix KRB5_AP_ERR
+error_code USER_TO_USER_REQUIRED, "User to user required"
+
+index 70
+prefix KRB5_KDC_ERR
+error_code CANT_VERIFY_CERTIFICATE, "Cannot verify certificate"
+error_code INVALID_CERTIFICATE, "Certificate invalid"
+error_code REVOKED_CERTIFICATE, "Certificate revoked"
+error_code REVOCATION_STATUS_UNKNOWN, "Revocation status unknown"
+error_code REVOCATION_STATUS_UNAVAILABLE, "Revocation status unavaible"
+error_code CLIENT_NAME_MISMATCH, "Client name mismatch in certificate"
+error_code INCONSISTENT_KEY_PURPOSE, "Inconsistent key purpose"
+error_code DIGEST_IN_CERT_NOT_ACCEPTED, "Digest in certificate not accepted"
+error_code PA_CHECKSUM_MUST_BE_INCLUDED, "paChecksum must be included"
+error_code DIGEST_IN_SIGNED_DATA_NOT_ACCEPTED, "Digest in signedData not accepted"
+error_code PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED, "Public key encryption not supported"
+
+## these are never used
+#index 80
+#prefix KRB5_IAKERB
+#error_code ERR_KDC_NOT_FOUND, "IAKERB proxy could not find a KDC"
+#error_code ERR_KDC_NO_RESPONSE, "IAKERB proxy never reeived a response from a KDC"
+
+# 82-127 are reserved
+
+index 128
+prefix
+error_code KRB5_ERR_RCSID, "$Id$"
+
+error_code KRB5_LIBOS_BADLOCKFLAG, "Invalid flag for file lock mode"
+error_code KRB5_LIBOS_CANTREADPWD, "Cannot read password"
+error_code KRB5_LIBOS_BADPWDMATCH, "Password mismatch"
+error_code KRB5_LIBOS_PWDINTR, "Password read interrupted"
+
+error_code KRB5_PARSE_ILLCHAR, "Invalid character in component name"
+error_code KRB5_PARSE_MALFORMED, "Malformed representation of principal"
+
+error_code KRB5_CONFIG_CANTOPEN, "Can't open/find configuration file"
+error_code KRB5_CONFIG_BADFORMAT, "Improper format of configuration file"
+error_code KRB5_CONFIG_NOTENUFSPACE, "Insufficient space to return complete information"
+
+error_code KRB5_BADMSGTYPE, "Invalid message type specified for encoding"
+
+error_code KRB5_CC_BADNAME, "Credential cache name malformed"
+error_code KRB5_CC_UNKNOWN_TYPE, "Unknown credential cache type"
+error_code KRB5_CC_NOTFOUND, "Matching credential not found"
+error_code KRB5_CC_END, "End of credential cache reached"
+
+error_code KRB5_NO_TKT_SUPPLIED, "Request did not supply a ticket"
+
+error_code KRB5KRB_AP_WRONG_PRINC, "Wrong principal in request"
+error_code KRB5KRB_AP_ERR_TKT_INVALID, "Ticket has invalid flag set"
+
+error_code KRB5_PRINC_NOMATCH, "Requested principal and ticket don't match"
+error_code KRB5_KDCREP_MODIFIED, "KDC reply did not match expectations"
+error_code KRB5_KDCREP_SKEW, "Clock skew too great in KDC reply"
+error_code KRB5_IN_TKT_REALM_MISMATCH, "Client/server realm mismatch in initial ticket request"
+
+error_code KRB5_PROG_ETYPE_NOSUPP, "Program lacks support for encryption type"
+error_code KRB5_PROG_KEYTYPE_NOSUPP, "Program lacks support for key type"
+error_code KRB5_WRONG_ETYPE, "Requested encryption type not used in message"
+error_code KRB5_PROG_SUMTYPE_NOSUPP, "Program lacks support for checksum type"
+
+error_code KRB5_REALM_UNKNOWN, "Cannot find KDC for requested realm"
+error_code KRB5_SERVICE_UNKNOWN, "Kerberos service unknown"
+error_code KRB5_KDC_UNREACH, "Cannot contact any KDC for requested realm"
+error_code KRB5_NO_LOCALNAME, "No local name found for principal name"
+
+error_code KRB5_MUTUAL_FAILED, "Mutual authentication failed"
+
+# some of these should be combined/supplanted by system codes
+
+error_code KRB5_RC_TYPE_EXISTS, "Replay cache type is already registered"
+error_code KRB5_RC_MALLOC, "No more memory to allocate (in replay cache code)"
+error_code KRB5_RC_TYPE_NOTFOUND, "Replay cache type is unknown"
+error_code KRB5_RC_UNKNOWN, "Generic unknown RC error"
+error_code KRB5_RC_REPLAY, "Message is a replay"
+error_code KRB5_RC_IO, "Replay I/O operation failed XXX"
+error_code KRB5_RC_NOIO, "Replay cache type does not support non-volatile storage"
+error_code KRB5_RC_PARSE, "Replay cache name parse/format error"
+
+error_code KRB5_RC_IO_EOF, "End-of-file on replay cache I/O"
+error_code KRB5_RC_IO_MALLOC, "No more memory to allocate (in replay cache I/O code)"
+error_code KRB5_RC_IO_PERM, "Permission denied in replay cache code"
+error_code KRB5_RC_IO_IO, "I/O error in replay cache i/o code"
+error_code KRB5_RC_IO_UNKNOWN, "Generic unknown RC/IO error"
+error_code KRB5_RC_IO_SPACE, "Insufficient system space to store replay information"
+
+error_code KRB5_TRANS_CANTOPEN, "Can't open/find realm translation file"
+error_code KRB5_TRANS_BADFORMAT, "Improper format of realm translation file"
+
+error_code KRB5_LNAME_CANTOPEN, "Can't open/find lname translation database"
+error_code KRB5_LNAME_NOTRANS, "No translation available for requested principal"
+error_code KRB5_LNAME_BADFORMAT, "Improper format of translation database entry"
+
+error_code KRB5_CRYPTO_INTERNAL, "Cryptosystem internal error"
+
+error_code KRB5_KT_BADNAME, "Key table name malformed"
+error_code KRB5_KT_UNKNOWN_TYPE, "Unknown Key table type"
+error_code KRB5_KT_NOTFOUND, "Key table entry not found"
+error_code KRB5_KT_END, "End of key table reached"
+error_code KRB5_KT_NOWRITE, "Cannot write to specified key table"
+error_code KRB5_KT_IOERR, "Error writing to key table"
+
+error_code KRB5_NO_TKT_IN_RLM, "Cannot find ticket for requested realm"
+error_code KRB5DES_BAD_KEYPAR, "DES key has bad parity"
+error_code KRB5DES_WEAK_KEY, "DES key is a weak key"
+
+error_code KRB5_BAD_ENCTYPE, "Bad encryption type"
+error_code KRB5_BAD_KEYSIZE, "Key size is incompatible with encryption type"
+error_code KRB5_BAD_MSIZE, "Message size is incompatible with encryption type"
+
+error_code KRB5_CC_TYPE_EXISTS, "Credentials cache type is already registered."
+error_code KRB5_KT_TYPE_EXISTS, "Key table type is already registered."
+
+error_code KRB5_CC_IO, "Credentials cache I/O operation failed XXX"
+error_code KRB5_FCC_PERM, "Credentials cache file permissions incorrect"
+error_code KRB5_FCC_NOFILE, "No credentials cache file found"
+error_code KRB5_FCC_INTERNAL, "Internal file credentials cache error"
+error_code KRB5_CC_WRITE, "Error writing to credentials cache file"
+error_code KRB5_CC_NOMEM, "No more memory to allocate (in credentials cache code)"
+error_code KRB5_CC_FORMAT, "Bad format in credentials cache"
+error_code KRB5_CC_NOT_KTYPE, "No credentials found with supported encryption types"
+
+# errors for dual tgt library calls
+error_code KRB5_INVALID_FLAGS, "Invalid KDC option combination (library internal error)"
+error_code KRB5_NO_2ND_TKT, "Request missing second ticket"
+
+error_code KRB5_NOCREDS_SUPPLIED, "No credentials supplied to library routine"
+
+# errors for sendauth (and recvauth)
+
+error_code KRB5_SENDAUTH_BADAUTHVERS, "Bad sendauth version was sent"
+error_code KRB5_SENDAUTH_BADAPPLVERS, "Bad application version was sent (via sendauth)"
+error_code KRB5_SENDAUTH_BADRESPONSE, "Bad response (during sendauth exchange)"
+error_code KRB5_SENDAUTH_REJECTED, "Server rejected authentication (during sendauth exchange)"
+
+# errors for preauthentication
+
+error_code KRB5_PREAUTH_BAD_TYPE, "Unsupported preauthentication type"
+error_code KRB5_PREAUTH_NO_KEY, "Required preauthentication key not supplied"
+error_code KRB5_PREAUTH_FAILED, "Generic preauthentication failure"
+
+# version number errors
+
+error_code KRB5_RCACHE_BADVNO, "Unsupported replay cache format version number"
+error_code KRB5_CCACHE_BADVNO, "Unsupported credentials cache format version number"
+error_code KRB5_KEYTAB_BADVNO, "Unsupported key table format version number"
+
+#
+#
+
+error_code KRB5_PROG_ATYPE_NOSUPP, "Program lacks support for address type"
+error_code KRB5_RC_REQUIRED, "Message replay detection requires rcache parameter"
+error_code KRB5_ERR_BAD_HOSTNAME, "Hostname cannot be canonicalized"
+error_code KRB5_ERR_HOST_REALM_UNKNOWN, "Cannot determine realm for host"
+error_code KRB5_SNAME_UNSUPP_NAMETYPE, "Conversion to service principal undefined for name type"
+
+error_code KRB5KRB_AP_ERR_V4_REPLY, "Initial Ticket response appears to be Version 4"
+error_code KRB5_REALM_CANT_RESOLVE, "Cannot resolve KDC for requested realm"
+error_code KRB5_TKT_NOT_FORWARDABLE, "Requesting ticket can't get forwardable tickets"
+error_code KRB5_FWD_BAD_PRINCIPAL, "Bad principal name while trying to forward credentials"
+
+error_code KRB5_GET_IN_TKT_LOOP, "Looping detected inside krb5_get_in_tkt"
+error_code KRB5_CONFIG_NODEFREALM, "Configuration file does not specify default realm"
+
+error_code KRB5_SAM_UNSUPPORTED, "Bad SAM flags in obtain_sam_padata"
+error_code KRB5_SAM_INVALID_ETYPE, "Invalid encryption type in SAM challenge"
+error_code KRB5_SAM_NO_CHECKSUM, "Missing checksum in SAM challenge"
+error_code KRB5_SAM_BAD_CHECKSUM, "Bad checksum in SAM challenge"
+
+index 238
+error_code KRB5_OBSOLETE_FN, "Program called an obsolete, deleted function"
+
+index 245
+error_code KRB5_ERR_BAD_S2K_PARAMS, "Invalid key generation parameters from KDC"
+error_code KRB5_ERR_NO_SERVICE, "Service not available"
+error_code KRB5_CC_NOSUPP, "Credential cache function not supported"
+error_code KRB5_DELTAT_BADFORMAT, "Invalid format of Kerberos lifetime or clock skew string"
+error_code KRB5_PLUGIN_NO_HANDLE, "Supplied data not handled by this plugin"
+error_code KRB5_PLUGIN_OP_NOTSUPP, "Plugin does not support the operaton"
+
+end
diff --git a/source4/heimdal/lib/krb5/krb5_locl.h b/source4/heimdal/lib/krb5/krb5_locl.h
new file mode 100644
index 0000000000..ced722f2d9
--- /dev/null
+++ b/source4/heimdal/lib/krb5/krb5_locl.h
@@ -0,0 +1,310 @@
+/*
+ * Copyright (c) 1997-2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef __KRB5_LOCL_H__
+#define __KRB5_LOCL_H__
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#if defined(HAVE_SYS_IOCTL_H) && SunOS != 40
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_PWD_H
+#undef _POSIX_PTHREAD_SEMANTICS
+/* This gets us the 5-arg getpwnam_r on Solaris 9. */
+#define _POSIX_PTHREAD_SEMANTICS
+#include <pwd.h>
+#endif
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#include <time.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETINET_IN6_H
+#include <netinet/in6.h>
+#endif
+#ifdef HAVE_NETINET6_IN6_H
+#include <netinet6/in6.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef _AIX
+struct ether_addr;
+struct mbuf;
+struct sockaddr_dl;
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_ARPA_NAMESER_H
+#include <arpa/nameser.h>
+#endif
+#ifdef HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#endif
+#ifdef HAVE_SYS_FILIO_H
+#include <sys/filio.h>
+#endif
+#ifdef HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif
+
+#define HEIMDAL_TEXTDOMAIN "heimdal_krb5"
+
+#ifdef HAVE_LIBINTL_H
+#include <libintl.h>
+#define N_(x,y) dgettext(HEIMDAL_TEXTDOMAIN, x)
+#else
+#define N_(x,y) (x)
+#define bindtextdomain(package, localedir)
+#endif
+
+
+#ifdef HAVE_CRYPT_H
+#undef des_encrypt
+#define des_encrypt wingless_pigs_mostly_fail_to_fly
+#include <crypt.h>
+#undef des_encrypt
+#endif
+
+#ifdef HAVE_DOOR_CREATE
+#include <door.h>
+#endif
+
+#include <roken.h>
+#include <parse_time.h>
+#include <base64.h>
+
+#include <wind.h>
+
+#define HC_DEPRECATED_CRYPTO
+#include "crypto-headers.h"
+
+
+#include <krb5_asn1.h>
+
+struct send_to_kdc;
+
+/* XXX glue for pkinit */
+struct krb5_pk_identity;
+struct krb5_pk_cert;
+struct ContentInfo;
+struct AlgorithmIdentifier;
+typedef struct krb5_pk_init_ctx_data *krb5_pk_init_ctx;
+struct krb5_dh_moduli;
+
+/* v4 glue */
+struct _krb5_krb_auth_data;
+
+#include <der.h>
+
+#include <krb5.h>
+#include <krb5_err.h>
+#include <asn1_err.h>
+#ifdef PKINIT
+#include <hx509.h>
+#endif
+#include <krb5-private.h>
+
+#include "heim_threads.h"
+
+#define ALLOC(X, N) (X) = calloc((N), sizeof(*(X)))
+#define ALLOC_SEQ(X, N) do { (X)->len = (N); ALLOC((X)->val, (N)); } while(0)
+
+/* should this be public? */
+#define KEYTAB_DEFAULT "FILE:" SYSCONFDIR "/krb5.keytab"
+#define KEYTAB_DEFAULT_MODIFY "FILE:" SYSCONFDIR "/krb5.keytab"
+
+#define MODULI_FILE SYSCONFDIR "/krb5.moduli"
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#endif
+
+#ifndef SOCK_CLOEXEC
+#define SOCK_CLOEXEC 0
+#endif
+
+
+#define KRB5_BUFSIZ 1024
+
+typedef enum {
+ KRB5_INIT_CREDS_TRISTATE_UNSET = 0,
+ KRB5_INIT_CREDS_TRISTATE_TRUE,
+ KRB5_INIT_CREDS_TRISTATE_FALSE
+} krb5_get_init_creds_tristate;
+
+struct _krb5_get_init_creds_opt_private {
+ int refcount;
+ /* ENC_TIMESTAMP */
+ const char *password;
+ krb5_s2k_proc key_proc;
+ /* PA_PAC_REQUEST */
+ krb5_get_init_creds_tristate req_pac;
+ /* PKINIT */
+ krb5_pk_init_ctx pk_init_ctx;
+ KRB_ERROR *error;
+ krb5_get_init_creds_tristate addressless;
+ int flags;
+#define KRB5_INIT_CREDS_CANONICALIZE 1
+#define KRB5_INIT_CREDS_NO_C_CANON_CHECK 2
+};
+
+typedef struct krb5_context_data {
+ krb5_enctype *etypes;
+ krb5_enctype *etypes_des;
+ char **default_realms;
+ time_t max_skew;
+ time_t kdc_timeout;
+ unsigned max_retries;
+ int32_t kdc_sec_offset;
+ int32_t kdc_usec_offset;
+ krb5_config_section *cf;
+ struct et_list *et_list;
+ struct krb5_log_facility *warn_dest;
+ krb5_cc_ops *cc_ops;
+ int num_cc_ops;
+ const char *http_proxy;
+ const char *time_fmt;
+ krb5_boolean log_utc;
+ const char *default_keytab;
+ const char *default_keytab_modify;
+ krb5_boolean use_admin_kdc;
+ krb5_addresses *extra_addresses;
+ krb5_boolean scan_interfaces; /* `ifconfig -a' */
+ krb5_boolean srv_lookup; /* do SRV lookups */
+ krb5_boolean srv_try_txt; /* try TXT records also */
+ int32_t fcache_vno; /* create cache files w/ this
+ version */
+ int num_kt_types; /* # of registered keytab types */
+ struct krb5_keytab_data *kt_types; /* registered keytab types */
+ const char *date_fmt;
+ char *error_string;
+ krb5_error_code error_code;
+ krb5_addresses *ignore_addresses;
+ char *default_cc_name;
+ char *default_cc_name_env;
+ int default_cc_name_set;
+ void *mutex; /* protects error_string/error_buf */
+ int large_msg_size;
+ int flags;
+#define KRB5_CTX_F_DNS_CANONICALIZE_HOSTNAME 1
+#define KRB5_CTX_F_CHECK_PAC 2
+ struct send_to_kdc *send_to_kdc;
+} krb5_context_data;
+
+#define KRB5_DEFAULT_CCNAME_FILE "FILE:/tmp/krb5cc_%{uid}"
+#define KRB5_DEFAULT_CCNAME_API "API:"
+#define KRB5_DEFAULT_CCNAME_KCM "KCM:%{uid}"
+
+#define EXTRACT_TICKET_ALLOW_CNAME_MISMATCH 1
+#define EXTRACT_TICKET_ALLOW_SERVER_MISMATCH 2
+#define EXTRACT_TICKET_MATCH_REALM 4
+#define EXTRACT_TICKET_AS_REQ 8
+
+/*
+ * Configurable options
+ */
+
+#ifndef KRB5_DEFAULT_CCTYPE
+#ifdef __APPLE__
+#define KRB5_DEFAULT_CCTYPE (&krb5_acc_ops)
+#else
+#define KRB5_DEFAULT_CCTYPE (&krb5_fcc_ops)
+#endif
+#endif
+
+#ifndef KRB5_ADDRESSLESS_DEFAULT
+#define KRB5_ADDRESSLESS_DEFAULT TRUE
+#endif
+
+#ifdef PKINIT
+
+struct krb5_pk_identity {
+ hx509_context hx509ctx;
+ hx509_verify_ctx verify_ctx;
+ hx509_certs certs;
+ hx509_certs anchors;
+ hx509_certs certpool;
+ hx509_revoke_ctx revokectx;
+};
+
+enum krb5_pk_type {
+ PKINIT_WIN2K = 1,
+ PKINIT_27 = 2
+};
+
+#endif /* PKINIT */
+
+#endif /* __KRB5_LOCL_H__ */
diff --git a/source4/heimdal/lib/krb5/krb_err.et b/source4/heimdal/lib/krb5/krb_err.et
new file mode 100644
index 0000000000..f7dbb6ce7a
--- /dev/null
+++ b/source4/heimdal/lib/krb5/krb_err.et
@@ -0,0 +1,63 @@
+#
+# Error messages for the krb4 library
+#
+# This might look like a com_err file, but is not
+#
+id "$Id: krb_err.et,v 1.7 1998/03/29 14:19:52 bg Exp $"
+
+error_table krb
+
+prefix KRB4ET
+ec KSUCCESS, "Kerberos 4 successful"
+ec KDC_NAME_EXP, "Kerberos 4 principal expired"
+ec KDC_SERVICE_EXP, "Kerberos 4 service expired"
+ec KDC_AUTH_EXP, "Kerberos 4 auth expired"
+ec KDC_PKT_VER, "Incorrect Kerberos 4 master key version"
+ec KDC_P_MKEY_VER, "Incorrect Kerberos 4 master key version"
+ec KDC_S_MKEY_VER, "Incorrect Kerberos 4 master key version"
+ec KDC_BYTE_ORDER, "Kerberos 4 byte order unknown"
+ec KDC_PR_UNKNOWN, "Kerberos 4 principal unknown"
+ec KDC_PR_N_UNIQUE, "Kerberos 4 principal not unique"
+ec KDC_NULL_KEY, "Kerberos 4 principal has null key"
+index 20
+ec KDC_GEN_ERR, "Generic error from KDC (Kerberos 4)"
+ec GC_TKFIL, "Can't read Kerberos 4 ticket file"
+ec GC_NOTKT, "Can't find Kerberos 4 ticket or TGT"
+index 26
+ec MK_AP_TGTEXP, "Kerberos 4 TGT Expired"
+index 31
+ec RD_AP_UNDEC, "Kerberos 4: Can't decode authenticator"
+ec RD_AP_EXP, "Kerberos 4 ticket expired"
+ec RD_AP_NYV, "Kerberos 4 ticket not yet valid"
+ec RD_AP_REPEAT, "Kerberos 4: Repeated request"
+ec RD_AP_NOT_US, "The Kerberos 4 ticket isn't for us"
+ec RD_AP_INCON, "Kerberos 4 request inconsistent"
+ec RD_AP_TIME, "Kerberos 4: delta_t too big"
+ec RD_AP_BADD, "Kerberos 4: incorrect net address"
+ec RD_AP_VERSION, "Kerberos protocol not version 4"
+ec RD_AP_MSG_TYPE, "Kerberos 4: invalid msg type"
+ec RD_AP_MODIFIED, "Kerberos 4: message stream modified"
+ec RD_AP_ORDER, "Kerberos 4: message out of order"
+ec RD_AP_UNAUTHOR, "Kerberos 4: unauthorized request"
+index 51
+ec GT_PW_NULL, "Kerberos 4: current PW is null"
+ec GT_PW_BADPW, "Kerberos 4: Incorrect current password"
+ec GT_PW_PROT, "Kerberos 4 protocol error"
+ec GT_PW_KDCERR, "Error returned by KDC (Kerberos 4)"
+ec GT_PW_NULLTKT, "Null Kerberos 4 ticket returned by KDC"
+ec SKDC_RETRY, "Kerberos 4: Retry count exceeded"
+ec SKDC_CANT, "Kerberos 4: Can't send request"
+index 61
+ec INTK_W_NOTALL, "Kerberos 4: not all tickets returned"
+ec INTK_BADPW, "Kerberos 4: incorrect password"
+ec INTK_PROT, "Kerberos 4: Protocol Error"
+index 70
+ec INTK_ERR, "Other error in Kerberos 4"
+ec AD_NOTGT, "Don't have Kerberos 4 ticket-granting ticket"
+index 76
+ec NO_TKT_FIL, "No Kerberos 4 ticket file found"
+ec TKT_FIL_ACC, "Couldn't access Kerberos 4 ticket file"
+ec TKT_FIL_LCK, "Couldn't lock Kerberos 4 ticket file"
+ec TKT_FIL_FMT, "Bad Kerberos 4 ticket file format"
+ec TKT_FIL_INI, "Kerberos 4: tf_init not called first"
+ec KNAME_FMT, "Bad Kerberos 4 name format"
diff --git a/source4/heimdal/lib/krb5/krbhst.c b/source4/heimdal/lib/krb5/krbhst.c
new file mode 100644
index 0000000000..7348ac3f00
--- /dev/null
+++ b/source4/heimdal/lib/krb5/krbhst.c
@@ -0,0 +1,1019 @@
+/*
+ * Copyright (c) 2001 - 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+#include <resolve.h>
+#include "locate_plugin.h"
+
+RCSID("$Id$");
+
+static int
+string_to_proto(const char *string)
+{
+ if(strcasecmp(string, "udp") == 0)
+ return KRB5_KRBHST_UDP;
+ else if(strcasecmp(string, "tcp") == 0)
+ return KRB5_KRBHST_TCP;
+ else if(strcasecmp(string, "http") == 0)
+ return KRB5_KRBHST_HTTP;
+ return -1;
+}
+
+/*
+ * set `res' and `count' to the result of looking up SRV RR in DNS for
+ * `proto', `proto', `realm' using `dns_type'.
+ * if `port' != 0, force that port number
+ */
+
+static krb5_error_code
+srv_find_realm(krb5_context context, krb5_krbhst_info ***res, int *count,
+ const char *realm, const char *dns_type,
+ const char *proto, const char *service, int port)
+{
+ char domain[1024];
+ struct dns_reply *r;
+ struct resource_record *rr;
+ int num_srv;
+ int proto_num;
+ int def_port;
+
+ *res = NULL;
+ *count = 0;
+
+ proto_num = string_to_proto(proto);
+ if(proto_num < 0) {
+ krb5_set_error_message(context, EINVAL,
+ N_("unknown protocol `%s' to lookup", ""),
+ proto);
+ return EINVAL;
+ }
+
+ if(proto_num == KRB5_KRBHST_HTTP)
+ def_port = ntohs(krb5_getportbyname (context, "http", "tcp", 80));
+ else if(port == 0)
+ def_port = ntohs(krb5_getportbyname (context, service, proto, 88));
+ else
+ def_port = port;
+
+ snprintf(domain, sizeof(domain), "_%s._%s.%s.", service, proto, realm);
+
+ r = dns_lookup(domain, dns_type);
+ if(r == NULL)
+ return KRB5_KDC_UNREACH;
+
+ for(num_srv = 0, rr = r->head; rr; rr = rr->next)
+ if(rr->type == T_SRV)
+ num_srv++;
+
+ *res = malloc(num_srv * sizeof(**res));
+ if(*res == NULL) {
+ dns_free_data(r);
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+
+ dns_srv_order(r);
+
+ for(num_srv = 0, rr = r->head; rr; rr = rr->next)
+ if(rr->type == T_SRV) {
+ krb5_krbhst_info *hi;
+ size_t len = strlen(rr->u.srv->target);
+
+ hi = calloc(1, sizeof(*hi) + len);
+ if(hi == NULL) {
+ dns_free_data(r);
+ while(--num_srv >= 0)
+ free((*res)[num_srv]);
+ free(*res);
+ *res = NULL;
+ return ENOMEM;
+ }
+ (*res)[num_srv++] = hi;
+
+ hi->proto = proto_num;
+
+ hi->def_port = def_port;
+ if (port != 0)
+ hi->port = port;
+ else
+ hi->port = rr->u.srv->port;
+
+ strlcpy(hi->hostname, rr->u.srv->target, len + 1);
+ }
+
+ *count = num_srv;
+
+ dns_free_data(r);
+ return 0;
+}
+
+
+struct krb5_krbhst_data {
+ char *realm;
+ unsigned int flags;
+ int def_port;
+ int port; /* hardwired port number if != 0 */
+#define KD_CONFIG 1
+#define KD_SRV_UDP 2
+#define KD_SRV_TCP 4
+#define KD_SRV_HTTP 8
+#define KD_FALLBACK 16
+#define KD_CONFIG_EXISTS 32
+#define KD_LARGE_MSG 64
+#define KD_PLUGIN 128
+ krb5_error_code (*get_next)(krb5_context, struct krb5_krbhst_data *,
+ krb5_krbhst_info**);
+
+ unsigned int fallback_count;
+
+ struct krb5_krbhst_info *hosts, **index, **end;
+};
+
+static krb5_boolean
+krbhst_empty(const struct krb5_krbhst_data *kd)
+{
+ return kd->index == &kd->hosts;
+}
+
+/*
+ * Return the default protocol for the `kd' (either TCP or UDP)
+ */
+
+static int
+krbhst_get_default_proto(struct krb5_krbhst_data *kd)
+{
+ if (kd->flags & KD_LARGE_MSG)
+ return KRB5_KRBHST_TCP;
+ return KRB5_KRBHST_UDP;
+}
+
+
+/*
+ * parse `spec' into a krb5_krbhst_info, defaulting the port to `def_port'
+ * and forcing it to `port' if port != 0
+ */
+
+static struct krb5_krbhst_info*
+parse_hostspec(krb5_context context, struct krb5_krbhst_data *kd,
+ const char *spec, int def_port, int port)
+{
+ const char *p = spec;
+ struct krb5_krbhst_info *hi;
+
+ hi = calloc(1, sizeof(*hi) + strlen(spec));
+ if(hi == NULL)
+ return NULL;
+
+ hi->proto = krbhst_get_default_proto(kd);
+
+ if(strncmp(p, "http://", 7) == 0){
+ hi->proto = KRB5_KRBHST_HTTP;
+ p += 7;
+ } else if(strncmp(p, "http/", 5) == 0) {
+ hi->proto = KRB5_KRBHST_HTTP;
+ p += 5;
+ def_port = ntohs(krb5_getportbyname (context, "http", "tcp", 80));
+ }else if(strncmp(p, "tcp/", 4) == 0){
+ hi->proto = KRB5_KRBHST_TCP;
+ p += 4;
+ } else if(strncmp(p, "udp/", 4) == 0) {
+ p += 4;
+ }
+
+ if(strsep_copy(&p, ":", hi->hostname, strlen(spec) + 1) < 0) {
+ free(hi);
+ return NULL;
+ }
+ /* get rid of trailing /, and convert to lower case */
+ hi->hostname[strcspn(hi->hostname, "/")] = '\0';
+ strlwr(hi->hostname);
+
+ hi->port = hi->def_port = def_port;
+ if(p != NULL) {
+ char *end;
+ hi->port = strtol(p, &end, 0);
+ if(end == p) {
+ free(hi);
+ return NULL;
+ }
+ }
+ if (port)
+ hi->port = port;
+ return hi;
+}
+
+void
+_krb5_free_krbhst_info(krb5_krbhst_info *hi)
+{
+ if (hi->ai != NULL)
+ freeaddrinfo(hi->ai);
+ free(hi);
+}
+
+krb5_error_code
+_krb5_krbhost_info_move(krb5_context context,
+ krb5_krbhst_info *from,
+ krb5_krbhst_info **to)
+{
+ size_t hostnamelen = strlen(from->hostname);
+ /* trailing NUL is included in structure */
+ *to = calloc(1, sizeof(**to) + hostnamelen);
+ if(*to == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+
+ (*to)->proto = from->proto;
+ (*to)->port = from->port;
+ (*to)->def_port = from->def_port;
+ (*to)->ai = from->ai;
+ from->ai = NULL;
+ (*to)->next = NULL;
+ memcpy((*to)->hostname, from->hostname, hostnamelen + 1);
+ return 0;
+}
+
+
+static void
+append_host_hostinfo(struct krb5_krbhst_data *kd, struct krb5_krbhst_info *host)
+{
+ struct krb5_krbhst_info *h;
+
+ for(h = kd->hosts; h; h = h->next)
+ if(h->proto == host->proto &&
+ h->port == host->port &&
+ strcmp(h->hostname, host->hostname) == 0) {
+ _krb5_free_krbhst_info(host);
+ return;
+ }
+ *kd->end = host;
+ kd->end = &host->next;
+}
+
+static krb5_error_code
+append_host_string(krb5_context context, struct krb5_krbhst_data *kd,
+ const char *host, int def_port, int port)
+{
+ struct krb5_krbhst_info *hi;
+
+ hi = parse_hostspec(context, kd, host, def_port, port);
+ if(hi == NULL)
+ return ENOMEM;
+
+ append_host_hostinfo(kd, hi);
+ return 0;
+}
+
+/*
+ * return a readable representation of `host' in `hostname, hostlen'
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_krbhst_format_string(krb5_context context, const krb5_krbhst_info *host,
+ char *hostname, size_t hostlen)
+{
+ const char *proto = "";
+ char portstr[7] = "";
+ if(host->proto == KRB5_KRBHST_TCP)
+ proto = "tcp/";
+ else if(host->proto == KRB5_KRBHST_HTTP)
+ proto = "http://";
+ if(host->port != host->def_port)
+ snprintf(portstr, sizeof(portstr), ":%d", host->port);
+ snprintf(hostname, hostlen, "%s%s%s", proto, host->hostname, portstr);
+ return 0;
+}
+
+/*
+ * create a getaddrinfo `hints' based on `proto'
+ */
+
+static void
+make_hints(struct addrinfo *hints, int proto)
+{
+ memset(hints, 0, sizeof(*hints));
+ hints->ai_family = AF_UNSPEC;
+ switch(proto) {
+ case KRB5_KRBHST_UDP :
+ hints->ai_socktype = SOCK_DGRAM;
+ break;
+ case KRB5_KRBHST_HTTP :
+ case KRB5_KRBHST_TCP :
+ hints->ai_socktype = SOCK_STREAM;
+ break;
+ }
+}
+
+/*
+ * return an `struct addrinfo *' in `ai' corresponding to the information
+ * in `host'. free:ing is handled by krb5_krbhst_free.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_krbhst_get_addrinfo(krb5_context context, krb5_krbhst_info *host,
+ struct addrinfo **ai)
+{
+ struct addrinfo hints;
+ char portstr[NI_MAXSERV];
+ int ret;
+
+ if (host->ai == NULL) {
+ make_hints(&hints, host->proto);
+ snprintf (portstr, sizeof(portstr), "%d", host->port);
+ ret = getaddrinfo(host->hostname, portstr, &hints, &host->ai);
+ if (ret)
+ return krb5_eai_to_heim_errno(ret, errno);
+ }
+ *ai = host->ai;
+ return 0;
+}
+
+static krb5_boolean
+get_next(struct krb5_krbhst_data *kd, krb5_krbhst_info **host)
+{
+ struct krb5_krbhst_info *hi = *kd->index;
+ if(hi != NULL) {
+ *host = hi;
+ kd->index = &(*kd->index)->next;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void
+srv_get_hosts(krb5_context context, struct krb5_krbhst_data *kd,
+ const char *proto, const char *service)
+{
+ krb5_krbhst_info **res;
+ int count, i;
+
+ if (srv_find_realm(context, &res, &count, kd->realm, "SRV", proto, service,
+ kd->port))
+ return;
+ for(i = 0; i < count; i++)
+ append_host_hostinfo(kd, res[i]);
+ free(res);
+}
+
+/*
+ * read the configuration for `conf_string', defaulting to kd->def_port and
+ * forcing it to `kd->port' if kd->port != 0
+ */
+
+static void
+config_get_hosts(krb5_context context, struct krb5_krbhst_data *kd,
+ const char *conf_string)
+{
+ int i;
+
+ char **hostlist;
+ hostlist = krb5_config_get_strings(context, NULL,
+ "realms", kd->realm, conf_string, NULL);
+
+ if(hostlist == NULL)
+ return;
+ kd->flags |= KD_CONFIG_EXISTS;
+ for(i = 0; hostlist && hostlist[i] != NULL; i++)
+ append_host_string(context, kd, hostlist[i], kd->def_port, kd->port);
+
+ krb5_config_free_strings(hostlist);
+}
+
+/*
+ * as a fallback, look for `serv_string.kd->realm' (typically
+ * kerberos.REALM, kerberos-1.REALM, ...
+ * `port' is the default port for the service, and `proto' the
+ * protocol
+ */
+
+static krb5_error_code
+fallback_get_hosts(krb5_context context, struct krb5_krbhst_data *kd,
+ const char *serv_string, int port, int proto)
+{
+ char *host;
+ int ret;
+ struct addrinfo *ai;
+ struct addrinfo hints;
+ char portstr[NI_MAXSERV];
+
+ /*
+ * Don't try forever in case the DNS server keep returning us
+ * entries (like wildcard entries or the .nu TLD)
+ */
+ if(kd->fallback_count >= 5) {
+ kd->flags |= KD_FALLBACK;
+ return 0;
+ }
+
+ if(kd->fallback_count == 0)
+ asprintf(&host, "%s.%s.", serv_string, kd->realm);
+ else
+ asprintf(&host, "%s-%d.%s.",
+ serv_string, kd->fallback_count, kd->realm);
+
+ if (host == NULL)
+ return ENOMEM;
+
+ make_hints(&hints, proto);
+ snprintf(portstr, sizeof(portstr), "%d", port);
+ ret = getaddrinfo(host, portstr, &hints, &ai);
+ if (ret) {
+ /* no more hosts, so we're done here */
+ free(host);
+ kd->flags |= KD_FALLBACK;
+ } else {
+ struct krb5_krbhst_info *hi;
+ size_t hostlen = strlen(host);
+
+ hi = calloc(1, sizeof(*hi) + hostlen);
+ if(hi == NULL) {
+ free(host);
+ return ENOMEM;
+ }
+
+ hi->proto = proto;
+ hi->port = hi->def_port = port;
+ hi->ai = ai;
+ memmove(hi->hostname, host, hostlen);
+ hi->hostname[hostlen] = '\0';
+ free(host);
+ append_host_hostinfo(kd, hi);
+ kd->fallback_count++;
+ }
+ return 0;
+}
+
+/*
+ * Fetch hosts from plugin
+ */
+
+static krb5_error_code
+add_locate(void *ctx, int type, struct sockaddr *addr)
+{
+ struct krb5_krbhst_info *hi;
+ struct krb5_krbhst_data *kd = ctx;
+ char host[NI_MAXHOST], port[NI_MAXSERV];
+ struct addrinfo hints, *ai;
+ socklen_t socklen;
+ size_t hostlen;
+ int ret;
+
+ socklen = socket_sockaddr_size(addr);
+
+ ret = getnameinfo(addr, socklen, host, sizeof(host), port, sizeof(port),
+ NI_NUMERICHOST|NI_NUMERICSERV);
+ if (ret != 0)
+ return 0;
+
+ make_hints(&hints, krbhst_get_default_proto(kd));
+ ret = getaddrinfo(host, port, &hints, &ai);
+ if (ret)
+ return 0;
+
+ hostlen = strlen(host);
+
+ hi = calloc(1, sizeof(*hi) + hostlen);
+ if(hi == NULL)
+ return ENOMEM;
+
+ hi->proto = krbhst_get_default_proto(kd);
+ hi->port = hi->def_port = socket_get_port(addr);
+ hi->ai = ai;
+ memmove(hi->hostname, host, hostlen);
+ hi->hostname[hostlen] = '\0';
+ append_host_hostinfo(kd, hi);
+
+ return 0;
+}
+
+static void
+plugin_get_hosts(krb5_context context,
+ struct krb5_krbhst_data *kd,
+ enum locate_service_type type)
+{
+ struct krb5_plugin *list = NULL, *e;
+ krb5_error_code ret;
+
+ ret = _krb5_plugin_find(context, PLUGIN_TYPE_DATA,
+ KRB5_PLUGIN_LOCATE, &list);
+ if(ret != 0 || list == NULL)
+ return;
+
+ for (e = list; e != NULL; e = _krb5_plugin_get_next(e)) {
+ krb5plugin_service_locate_ftable *service;
+ void *ctx;
+
+ service = _krb5_plugin_get_symbol(e);
+ if (service->minor_version != 0)
+ continue;
+
+ (*service->init)(context, &ctx);
+ ret = (*service->lookup)(ctx, type, kd->realm, 0, 0, add_locate, kd);
+ (*service->fini)(ctx);
+ if (ret && ret != KRB5_PLUGIN_NO_HANDLE) {
+ krb5_set_error_message(context, ret,
+ N_("Locate plugin failed to lookup realm %s: %d", ""),
+ kd->realm, ret);
+ break;
+ } else if (ret == 0)
+ kd->flags |= KD_CONFIG_EXISTS;
+
+ }
+ _krb5_plugin_free(list);
+}
+
+/*
+ *
+ */
+
+static krb5_error_code
+kdc_get_next(krb5_context context,
+ struct krb5_krbhst_data *kd,
+ krb5_krbhst_info **host)
+{
+ krb5_error_code ret;
+
+ if ((kd->flags & KD_PLUGIN) == 0) {
+ plugin_get_hosts(context, kd, locate_service_kdc);
+ kd->flags |= KD_PLUGIN;
+ if(get_next(kd, host))
+ return 0;
+ }
+
+ if((kd->flags & KD_CONFIG) == 0) {
+ config_get_hosts(context, kd, "kdc");
+ kd->flags |= KD_CONFIG;
+ if(get_next(kd, host))
+ return 0;
+ }
+
+ if (kd->flags & KD_CONFIG_EXISTS)
+ return KRB5_KDC_UNREACH; /* XXX */
+
+ if(context->srv_lookup) {
+ if((kd->flags & KD_SRV_UDP) == 0 && (kd->flags & KD_LARGE_MSG) == 0) {
+ srv_get_hosts(context, kd, "udp", "kerberos");
+ kd->flags |= KD_SRV_UDP;
+ if(get_next(kd, host))
+ return 0;
+ }
+
+ if((kd->flags & KD_SRV_TCP) == 0) {
+ srv_get_hosts(context, kd, "tcp", "kerberos");
+ kd->flags |= KD_SRV_TCP;
+ if(get_next(kd, host))
+ return 0;
+ }
+ if((kd->flags & KD_SRV_HTTP) == 0) {
+ srv_get_hosts(context, kd, "http", "kerberos");
+ kd->flags |= KD_SRV_HTTP;
+ if(get_next(kd, host))
+ return 0;
+ }
+ }
+
+ while((kd->flags & KD_FALLBACK) == 0) {
+ ret = fallback_get_hosts(context, kd, "kerberos",
+ kd->def_port,
+ krbhst_get_default_proto(kd));
+ if(ret)
+ return ret;
+ if(get_next(kd, host))
+ return 0;
+ }
+
+ return KRB5_KDC_UNREACH; /* XXX */
+}
+
+static krb5_error_code
+admin_get_next(krb5_context context,
+ struct krb5_krbhst_data *kd,
+ krb5_krbhst_info **host)
+{
+ krb5_error_code ret;
+
+ if ((kd->flags & KD_PLUGIN) == 0) {
+ plugin_get_hosts(context, kd, locate_service_kadmin);
+ kd->flags |= KD_PLUGIN;
+ if(get_next(kd, host))
+ return 0;
+ }
+
+ if((kd->flags & KD_CONFIG) == 0) {
+ config_get_hosts(context, kd, "admin_server");
+ kd->flags |= KD_CONFIG;
+ if(get_next(kd, host))
+ return 0;
+ }
+
+ if (kd->flags & KD_CONFIG_EXISTS)
+ return KRB5_KDC_UNREACH; /* XXX */
+
+ if(context->srv_lookup) {
+ if((kd->flags & KD_SRV_TCP) == 0) {
+ srv_get_hosts(context, kd, "tcp", "kerberos-adm");
+ kd->flags |= KD_SRV_TCP;
+ if(get_next(kd, host))
+ return 0;
+ }
+ }
+
+ if (krbhst_empty(kd)
+ && (kd->flags & KD_FALLBACK) == 0) {
+ ret = fallback_get_hosts(context, kd, "kerberos",
+ kd->def_port,
+ krbhst_get_default_proto(kd));
+ if(ret)
+ return ret;
+ kd->flags |= KD_FALLBACK;
+ if(get_next(kd, host))
+ return 0;
+ }
+
+ return KRB5_KDC_UNREACH; /* XXX */
+}
+
+static krb5_error_code
+kpasswd_get_next(krb5_context context,
+ struct krb5_krbhst_data *kd,
+ krb5_krbhst_info **host)
+{
+ krb5_error_code ret;
+
+ if ((kd->flags & KD_PLUGIN) == 0) {
+ plugin_get_hosts(context, kd, locate_service_kpasswd);
+ kd->flags |= KD_PLUGIN;
+ if(get_next(kd, host))
+ return 0;
+ }
+
+ if((kd->flags & KD_CONFIG) == 0) {
+ config_get_hosts(context, kd, "kpasswd_server");
+ kd->flags |= KD_CONFIG;
+ if(get_next(kd, host))
+ return 0;
+ }
+
+ if (kd->flags & KD_CONFIG_EXISTS)
+ return KRB5_KDC_UNREACH; /* XXX */
+
+ if(context->srv_lookup) {
+ if((kd->flags & KD_SRV_UDP) == 0) {
+ srv_get_hosts(context, kd, "udp", "kpasswd");
+ kd->flags |= KD_SRV_UDP;
+ if(get_next(kd, host))
+ return 0;
+ }
+ if((kd->flags & KD_SRV_TCP) == 0) {
+ srv_get_hosts(context, kd, "tcp", "kpasswd");
+ kd->flags |= KD_SRV_TCP;
+ if(get_next(kd, host))
+ return 0;
+ }
+ }
+
+ /* no matches -> try admin */
+
+ if (krbhst_empty(kd)) {
+ kd->flags = 0;
+ kd->port = kd->def_port;
+ kd->get_next = admin_get_next;
+ ret = (*kd->get_next)(context, kd, host);
+ if (ret == 0)
+ (*host)->proto = krbhst_get_default_proto(kd);
+ return ret;
+ }
+
+ return KRB5_KDC_UNREACH; /* XXX */
+}
+
+static krb5_error_code
+krb524_get_next(krb5_context context,
+ struct krb5_krbhst_data *kd,
+ krb5_krbhst_info **host)
+{
+ if ((kd->flags & KD_PLUGIN) == 0) {
+ plugin_get_hosts(context, kd, locate_service_krb524);
+ kd->flags |= KD_PLUGIN;
+ if(get_next(kd, host))
+ return 0;
+ }
+
+ if((kd->flags & KD_CONFIG) == 0) {
+ config_get_hosts(context, kd, "krb524_server");
+ if(get_next(kd, host))
+ return 0;
+ kd->flags |= KD_CONFIG;
+ }
+
+ if (kd->flags & KD_CONFIG_EXISTS)
+ return KRB5_KDC_UNREACH; /* XXX */
+
+ if(context->srv_lookup) {
+ if((kd->flags & KD_SRV_UDP) == 0) {
+ srv_get_hosts(context, kd, "udp", "krb524");
+ kd->flags |= KD_SRV_UDP;
+ if(get_next(kd, host))
+ return 0;
+ }
+
+ if((kd->flags & KD_SRV_TCP) == 0) {
+ srv_get_hosts(context, kd, "tcp", "krb524");
+ kd->flags |= KD_SRV_TCP;
+ if(get_next(kd, host))
+ return 0;
+ }
+ }
+
+ /* no matches -> try kdc */
+
+ if (krbhst_empty(kd)) {
+ kd->flags = 0;
+ kd->port = kd->def_port;
+ kd->get_next = kdc_get_next;
+ return (*kd->get_next)(context, kd, host);
+ }
+
+ return KRB5_KDC_UNREACH; /* XXX */
+}
+
+static struct krb5_krbhst_data*
+common_init(krb5_context context,
+ const char *realm,
+ int flags)
+{
+ struct krb5_krbhst_data *kd;
+
+ if((kd = calloc(1, sizeof(*kd))) == NULL)
+ return NULL;
+
+ if((kd->realm = strdup(realm)) == NULL) {
+ free(kd);
+ return NULL;
+ }
+
+ /* For 'realms' without a . do not even think of going to DNS */
+ if (!strchr(realm, '.'))
+ kd->flags |= KD_CONFIG_EXISTS;
+
+ if (flags & KRB5_KRBHST_FLAGS_LARGE_MSG)
+ kd->flags |= KD_LARGE_MSG;
+ kd->end = kd->index = &kd->hosts;
+ return kd;
+}
+
+/*
+ * initialize `handle' to look for hosts of type `type' in realm `realm'
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_krbhst_init(krb5_context context,
+ const char *realm,
+ unsigned int type,
+ krb5_krbhst_handle *handle)
+{
+ return krb5_krbhst_init_flags(context, realm, type, 0, handle);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_krbhst_init_flags(krb5_context context,
+ const char *realm,
+ unsigned int type,
+ int flags,
+ krb5_krbhst_handle *handle)
+{
+ struct krb5_krbhst_data *kd;
+ krb5_error_code (*next)(krb5_context, struct krb5_krbhst_data *,
+ krb5_krbhst_info **);
+ int def_port;
+
+ switch(type) {
+ case KRB5_KRBHST_KDC:
+ next = kdc_get_next;
+ def_port = ntohs(krb5_getportbyname (context, "kerberos", "udp", 88));
+ break;
+ case KRB5_KRBHST_ADMIN:
+ next = admin_get_next;
+ def_port = ntohs(krb5_getportbyname (context, "kerberos-adm",
+ "tcp", 749));
+ break;
+ case KRB5_KRBHST_CHANGEPW:
+ next = kpasswd_get_next;
+ def_port = ntohs(krb5_getportbyname (context, "kpasswd", "udp",
+ KPASSWD_PORT));
+ break;
+ case KRB5_KRBHST_KRB524:
+ next = krb524_get_next;
+ def_port = ntohs(krb5_getportbyname (context, "krb524", "udp", 4444));
+ break;
+ default:
+ krb5_set_error_message(context, ENOTTY,
+ N_("unknown krbhst type (%u)", ""), type);
+ return ENOTTY;
+ }
+ if((kd = common_init(context, realm, flags)) == NULL)
+ return ENOMEM;
+ kd->get_next = next;
+ kd->def_port = def_port;
+ *handle = kd;
+ return 0;
+}
+
+/*
+ * return the next host information from `handle' in `host'
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_krbhst_next(krb5_context context,
+ krb5_krbhst_handle handle,
+ krb5_krbhst_info **host)
+{
+ if(get_next(handle, host))
+ return 0;
+
+ return (*handle->get_next)(context, handle, host);
+}
+
+/*
+ * return the next host information from `handle' as a host name
+ * in `hostname' (or length `hostlen)
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_krbhst_next_as_string(krb5_context context,
+ krb5_krbhst_handle handle,
+ char *hostname,
+ size_t hostlen)
+{
+ krb5_error_code ret;
+ krb5_krbhst_info *host;
+ ret = krb5_krbhst_next(context, handle, &host);
+ if(ret)
+ return ret;
+ return krb5_krbhst_format_string(context, host, hostname, hostlen);
+}
+
+
+void KRB5_LIB_FUNCTION
+krb5_krbhst_reset(krb5_context context, krb5_krbhst_handle handle)
+{
+ handle->index = &handle->hosts;
+}
+
+void KRB5_LIB_FUNCTION
+krb5_krbhst_free(krb5_context context, krb5_krbhst_handle handle)
+{
+ krb5_krbhst_info *h, *next;
+
+ if (handle == NULL)
+ return;
+
+ for (h = handle->hosts; h != NULL; h = next) {
+ next = h->next;
+ _krb5_free_krbhst_info(h);
+ }
+
+ free(handle->realm);
+ free(handle);
+}
+
+/* backwards compatibility ahead */
+
+static krb5_error_code
+gethostlist(krb5_context context, const char *realm,
+ unsigned int type, char ***hostlist)
+{
+ krb5_error_code ret;
+ int nhost = 0;
+ krb5_krbhst_handle handle;
+ char host[MAXHOSTNAMELEN];
+ krb5_krbhst_info *hostinfo;
+
+ ret = krb5_krbhst_init(context, realm, type, &handle);
+ if (ret)
+ return ret;
+
+ while(krb5_krbhst_next(context, handle, &hostinfo) == 0)
+ nhost++;
+ if(nhost == 0) {
+ krb5_set_error_message(context, KRB5_KDC_UNREACH,
+ N_("No KDC found for realm %s", ""), realm);
+ return KRB5_KDC_UNREACH;
+ }
+ *hostlist = calloc(nhost + 1, sizeof(**hostlist));
+ if(*hostlist == NULL) {
+ krb5_krbhst_free(context, handle);
+ return ENOMEM;
+ }
+
+ krb5_krbhst_reset(context, handle);
+ nhost = 0;
+ while(krb5_krbhst_next_as_string(context, handle,
+ host, sizeof(host)) == 0) {
+ if(((*hostlist)[nhost++] = strdup(host)) == NULL) {
+ krb5_free_krbhst(context, *hostlist);
+ krb5_krbhst_free(context, handle);
+ return ENOMEM;
+ }
+ }
+ (*hostlist)[nhost++] = NULL;
+ krb5_krbhst_free(context, handle);
+ return 0;
+}
+
+/*
+ * return an malloced list of kadmin-hosts for `realm' in `hostlist'
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_krb_admin_hst (krb5_context context,
+ const krb5_realm *realm,
+ char ***hostlist)
+{
+ return gethostlist(context, *realm, KRB5_KRBHST_ADMIN, hostlist);
+}
+
+/*
+ * return an malloced list of changepw-hosts for `realm' in `hostlist'
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_krb_changepw_hst (krb5_context context,
+ const krb5_realm *realm,
+ char ***hostlist)
+{
+ return gethostlist(context, *realm, KRB5_KRBHST_CHANGEPW, hostlist);
+}
+
+/*
+ * return an malloced list of 524-hosts for `realm' in `hostlist'
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_krb524hst (krb5_context context,
+ const krb5_realm *realm,
+ char ***hostlist)
+{
+ return gethostlist(context, *realm, KRB5_KRBHST_KRB524, hostlist);
+}
+
+
+/*
+ * return an malloced list of KDC's for `realm' in `hostlist'
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_krbhst (krb5_context context,
+ const krb5_realm *realm,
+ char ***hostlist)
+{
+ return gethostlist(context, *realm, KRB5_KRBHST_KDC, hostlist);
+}
+
+/*
+ * free all the memory allocated in `hostlist'
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_free_krbhst (krb5_context context,
+ char **hostlist)
+{
+ char **p;
+
+ for (p = hostlist; *p; ++p)
+ free (*p);
+ free (hostlist);
+ return 0;
+}
diff --git a/source4/heimdal/lib/krb5/locate_plugin.h b/source4/heimdal/lib/krb5/locate_plugin.h
new file mode 100644
index 0000000000..529488ddfd
--- /dev/null
+++ b/source4/heimdal/lib/krb5/locate_plugin.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef HEIMDAL_KRB5_LOCATE_PLUGIN_H
+#define HEIMDAL_KRB5_LOCATE_PLUGIN_H 1
+
+#include <krb5.h>
+
+#define KRB5_PLUGIN_LOCATE "resolve"
+
+enum locate_service_type {
+ locate_service_kdc = 1,
+ locate_service_master_kdc,
+ locate_service_kadmin,
+ locate_service_krb524,
+ locate_service_kpasswd
+};
+
+typedef krb5_error_code
+(*krb5plugin_service_locate_lookup) (void *, enum locate_service_type,
+ const char *, int, int,
+ int (*)(void *,int,struct sockaddr *),
+ void *);
+
+
+typedef struct krb5plugin_service_locate_ftable {
+ int minor_version;
+ krb5_error_code (*init)(krb5_context, void **);
+ void (*fini)(void *);
+ krb5plugin_service_locate_lookup lookup;
+} krb5plugin_service_locate_ftable;
+
+#endif /* HEIMDAL_KRB5_LOCATE_PLUGIN_H */
+
diff --git a/source4/heimdal/lib/krb5/log.c b/source4/heimdal/lib/krb5/log.c
new file mode 100644
index 0000000000..587cf7ed97
--- /dev/null
+++ b/source4/heimdal/lib/krb5/log.c
@@ -0,0 +1,482 @@
+/*
+ * Copyright (c) 1997-2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+struct facility {
+ int min;
+ int max;
+ krb5_log_log_func_t log_func;
+ krb5_log_close_func_t close_func;
+ void *data;
+};
+
+static struct facility*
+log_realloc(krb5_log_facility *f)
+{
+ struct facility *fp;
+ fp = realloc(f->val, (f->len + 1) * sizeof(*f->val));
+ if(fp == NULL)
+ return NULL;
+ f->len++;
+ f->val = fp;
+ fp += f->len - 1;
+ return fp;
+}
+
+struct s2i {
+ const char *s;
+ int val;
+};
+
+#define L(X) { #X, LOG_ ## X }
+
+static struct s2i syslogvals[] = {
+ L(EMERG),
+ L(ALERT),
+ L(CRIT),
+ L(ERR),
+ L(WARNING),
+ L(NOTICE),
+ L(INFO),
+ L(DEBUG),
+
+ L(AUTH),
+#ifdef LOG_AUTHPRIV
+ L(AUTHPRIV),
+#endif
+#ifdef LOG_CRON
+ L(CRON),
+#endif
+ L(DAEMON),
+#ifdef LOG_FTP
+ L(FTP),
+#endif
+ L(KERN),
+ L(LPR),
+ L(MAIL),
+#ifdef LOG_NEWS
+ L(NEWS),
+#endif
+ L(SYSLOG),
+ L(USER),
+#ifdef LOG_UUCP
+ L(UUCP),
+#endif
+ L(LOCAL0),
+ L(LOCAL1),
+ L(LOCAL2),
+ L(LOCAL3),
+ L(LOCAL4),
+ L(LOCAL5),
+ L(LOCAL6),
+ L(LOCAL7),
+ { NULL, -1 }
+};
+
+static int
+find_value(const char *s, struct s2i *table)
+{
+ while(table->s && strcasecmp(table->s, s))
+ table++;
+ return table->val;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_initlog(krb5_context context,
+ const char *program,
+ krb5_log_facility **fac)
+{
+ krb5_log_facility *f = calloc(1, sizeof(*f));
+ if(f == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ f->program = strdup(program);
+ if(f->program == NULL){
+ free(f);
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ *fac = f;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_addlog_func(krb5_context context,
+ krb5_log_facility *fac,
+ int min,
+ int max,
+ krb5_log_log_func_t log_func,
+ krb5_log_close_func_t close_func,
+ void *data)
+{
+ struct facility *fp = log_realloc(fac);
+ if(fp == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ fp->min = min;
+ fp->max = max;
+ fp->log_func = log_func;
+ fp->close_func = close_func;
+ fp->data = data;
+ return 0;
+}
+
+
+struct _heimdal_syslog_data{
+ int priority;
+};
+
+static void
+log_syslog(const char *timestr,
+ const char *msg,
+ void *data)
+
+{
+ struct _heimdal_syslog_data *s = data;
+ syslog(s->priority, "%s", msg);
+}
+
+static void
+close_syslog(void *data)
+{
+ free(data);
+ closelog();
+}
+
+static krb5_error_code
+open_syslog(krb5_context context,
+ krb5_log_facility *facility, int min, int max,
+ const char *sev, const char *fac)
+{
+ struct _heimdal_syslog_data *sd = malloc(sizeof(*sd));
+ int i;
+
+ if(sd == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ i = find_value(sev, syslogvals);
+ if(i == -1)
+ i = LOG_ERR;
+ sd->priority = i;
+ i = find_value(fac, syslogvals);
+ if(i == -1)
+ i = LOG_AUTH;
+ sd->priority |= i;
+ roken_openlog(facility->program, LOG_PID | LOG_NDELAY, i);
+ return krb5_addlog_func(context, facility, min, max,
+ log_syslog, close_syslog, sd);
+}
+
+struct file_data{
+ const char *filename;
+ const char *mode;
+ FILE *fd;
+ int keep_open;
+};
+
+static void
+log_file(const char *timestr,
+ const char *msg,
+ void *data)
+{
+ struct file_data *f = data;
+ if(f->keep_open == 0)
+ f->fd = fopen(f->filename, f->mode);
+ if(f->fd == NULL)
+ return;
+ fprintf(f->fd, "%s %s\n", timestr, msg);
+ if(f->keep_open == 0) {
+ fclose(f->fd);
+ f->fd = NULL;
+ }
+}
+
+static void
+close_file(void *data)
+{
+ struct file_data *f = data;
+ if(f->keep_open && f->filename)
+ fclose(f->fd);
+ free(data);
+}
+
+static krb5_error_code
+open_file(krb5_context context, krb5_log_facility *fac, int min, int max,
+ const char *filename, const char *mode, FILE *f, int keep_open)
+{
+ struct file_data *fd = malloc(sizeof(*fd));
+ if(fd == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ fd->filename = filename;
+ fd->mode = mode;
+ fd->fd = f;
+ fd->keep_open = keep_open;
+
+ return krb5_addlog_func(context, fac, min, max, log_file, close_file, fd);
+}
+
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_addlog_dest(krb5_context context, krb5_log_facility *f, const char *orig)
+{
+ krb5_error_code ret = 0;
+ int min = 0, max = -1, n;
+ char c;
+ const char *p = orig;
+
+ n = sscanf(p, "%d%c%d/", &min, &c, &max);
+ if(n == 2){
+ if(c == '/') {
+ if(min < 0){
+ max = -min;
+ min = 0;
+ }else{
+ max = min;
+ }
+ }
+ }
+ if(n){
+ p = strchr(p, '/');
+ if(p == NULL) {
+ krb5_set_error_message(context, HEIM_ERR_LOG_PARSE,
+ N_("failed to parse \"%s\"", ""), orig);
+ return HEIM_ERR_LOG_PARSE;
+ }
+ p++;
+ }
+ if(strcmp(p, "STDERR") == 0){
+ ret = open_file(context, f, min, max, NULL, NULL, stderr, 1);
+ }else if(strcmp(p, "CONSOLE") == 0){
+ ret = open_file(context, f, min, max, "/dev/console", "w", NULL, 0);
+ }else if(strncmp(p, "FILE", 4) == 0 && (p[4] == ':' || p[4] == '=')){
+ char *fn;
+ FILE *file = NULL;
+ int keep_open = 0;
+ fn = strdup(p + 5);
+ if(fn == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ if(p[4] == '='){
+ int i = open(fn, O_WRONLY | O_CREAT |
+ O_TRUNC | O_APPEND, 0666);
+ if(i < 0) {
+ ret = errno;
+ krb5_set_error_message(context, ret,
+ N_("open(%s) logile: %s", ""), fn,
+ strerror(ret));
+ free(fn);
+ return ret;
+ }
+ rk_cloexec(i);
+ file = fdopen(i, "a");
+ if(file == NULL){
+ ret = errno;
+ close(i);
+ krb5_set_error_message(context, ret,
+ N_("fdopen(%s) logfile: %s", ""),
+ fn, strerror(ret));
+ free(fn);
+ return ret;
+ }
+ keep_open = 1;
+ }
+ ret = open_file(context, f, min, max, fn, "a", file, keep_open);
+ }else if(strncmp(p, "DEVICE", 6) == 0 && (p[6] == ':' || p[6] == '=')){
+ ret = open_file(context, f, min, max, strdup(p + 7), "w", NULL, 0);
+ }else if(strncmp(p, "SYSLOG", 6) == 0 && (p[6] == '\0' || p[6] == ':')){
+ char severity[128] = "";
+ char facility[128] = "";
+ p += 6;
+ if(*p != '\0')
+ p++;
+ if(strsep_copy(&p, ":", severity, sizeof(severity)) != -1)
+ strsep_copy(&p, ":", facility, sizeof(facility));
+ if(*severity == '\0')
+ strlcpy(severity, "ERR", sizeof(severity));
+ if(*facility == '\0')
+ strlcpy(facility, "AUTH", sizeof(facility));
+ ret = open_syslog(context, f, min, max, severity, facility);
+ }else{
+ ret = HEIM_ERR_LOG_PARSE; /* XXX */
+ krb5_set_error_message (context, ret,
+ N_("unknown log type: %s", ""), p);
+ }
+ return ret;
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_openlog(krb5_context context,
+ const char *program,
+ krb5_log_facility **fac)
+{
+ krb5_error_code ret;
+ char **p, **q;
+
+ ret = krb5_initlog(context, program, fac);
+ if(ret)
+ return ret;
+
+ p = krb5_config_get_strings(context, NULL, "logging", program, NULL);
+ if(p == NULL)
+ p = krb5_config_get_strings(context, NULL, "logging", "default", NULL);
+ if(p){
+ for(q = p; *q && ret == 0; q++)
+ ret = krb5_addlog_dest(context, *fac, *q);
+ krb5_config_free_strings(p);
+ }else
+ ret = krb5_addlog_dest(context, *fac, "SYSLOG");
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_closelog(krb5_context context,
+ krb5_log_facility *fac)
+{
+ int i;
+ for(i = 0; i < fac->len; i++)
+ (*fac->val[i].close_func)(fac->val[i].data);
+ free(fac->val);
+ free(fac->program);
+ fac->val = NULL;
+ fac->len = 0;
+ fac->program = NULL;
+ free(fac);
+ return 0;
+}
+
+#undef __attribute__
+#define __attribute__(X)
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_vlog_msg(krb5_context context,
+ krb5_log_facility *fac,
+ char **reply,
+ int level,
+ const char *fmt,
+ va_list ap)
+ __attribute__((format (printf, 5, 0)))
+{
+
+ char *msg = NULL;
+ const char *actual = NULL;
+ char buf[64];
+ time_t t = 0;
+ int i;
+
+ for(i = 0; fac && i < fac->len; i++)
+ if(fac->val[i].min <= level &&
+ (fac->val[i].max < 0 || fac->val[i].max >= level)) {
+ if(t == 0) {
+ t = time(NULL);
+ krb5_format_time(context, t, buf, sizeof(buf), TRUE);
+ }
+ if(actual == NULL) {
+ vasprintf(&msg, fmt, ap);
+ if(msg == NULL)
+ actual = fmt;
+ else
+ actual = msg;
+ }
+ (*fac->val[i].log_func)(buf, actual, fac->val[i].data);
+ }
+ if(reply == NULL)
+ free(msg);
+ else
+ *reply = msg;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_vlog(krb5_context context,
+ krb5_log_facility *fac,
+ int level,
+ const char *fmt,
+ va_list ap)
+ __attribute__((format (printf, 4, 0)))
+{
+ return krb5_vlog_msg(context, fac, NULL, level, fmt, ap);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_log_msg(krb5_context context,
+ krb5_log_facility *fac,
+ int level,
+ char **reply,
+ const char *fmt,
+ ...)
+ __attribute__((format (printf, 5, 6)))
+{
+ va_list ap;
+ krb5_error_code ret;
+
+ va_start(ap, fmt);
+ ret = krb5_vlog_msg(context, fac, reply, level, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_log(krb5_context context,
+ krb5_log_facility *fac,
+ int level,
+ const char *fmt,
+ ...)
+ __attribute__((format (printf, 4, 5)))
+{
+ va_list ap;
+ krb5_error_code ret;
+
+ va_start(ap, fmt);
+ ret = krb5_vlog(context, fac, level, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+
diff --git a/source4/heimdal/lib/krb5/mcache.c b/source4/heimdal/lib/krb5/mcache.c
new file mode 100644
index 0000000000..752608069d
--- /dev/null
+++ b/source4/heimdal/lib/krb5/mcache.c
@@ -0,0 +1,499 @@
+/*
+ * Copyright (c) 1997-2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+typedef struct krb5_mcache {
+ char *name;
+ unsigned int refcnt;
+ int dead;
+ krb5_principal primary_principal;
+ struct link {
+ krb5_creds cred;
+ struct link *next;
+ } *creds;
+ struct krb5_mcache *next;
+ time_t mtime;
+} krb5_mcache;
+
+static HEIMDAL_MUTEX mcc_mutex = HEIMDAL_MUTEX_INITIALIZER;
+static struct krb5_mcache *mcc_head;
+
+#define MCACHE(X) ((krb5_mcache *)(X)->data.data)
+
+#define MISDEAD(X) ((X)->dead)
+
+static const char*
+mcc_get_name(krb5_context context,
+ krb5_ccache id)
+{
+ return MCACHE(id)->name;
+}
+
+static krb5_mcache *
+mcc_alloc(const char *name)
+{
+ krb5_mcache *m, *m_c;
+
+ ALLOC(m, 1);
+ if(m == NULL)
+ return NULL;
+ if(name == NULL)
+ asprintf(&m->name, "%p", m);
+ else
+ m->name = strdup(name);
+ if(m->name == NULL) {
+ free(m);
+ return NULL;
+ }
+ /* check for dups first */
+ HEIMDAL_MUTEX_lock(&mcc_mutex);
+ for (m_c = mcc_head; m_c != NULL; m_c = m_c->next)
+ if (strcmp(m->name, m_c->name) == 0)
+ break;
+ if (m_c) {
+ free(m->name);
+ free(m);
+ HEIMDAL_MUTEX_unlock(&mcc_mutex);
+ return NULL;
+ }
+
+ m->dead = 0;
+ m->refcnt = 1;
+ m->primary_principal = NULL;
+ m->creds = NULL;
+ m->mtime = time(NULL);
+ m->next = mcc_head;
+ mcc_head = m;
+ HEIMDAL_MUTEX_unlock(&mcc_mutex);
+ return m;
+}
+
+static krb5_error_code
+mcc_resolve(krb5_context context, krb5_ccache *id, const char *res)
+{
+ krb5_mcache *m;
+
+ HEIMDAL_MUTEX_lock(&mcc_mutex);
+ for (m = mcc_head; m != NULL; m = m->next)
+ if (strcmp(m->name, res) == 0)
+ break;
+ HEIMDAL_MUTEX_unlock(&mcc_mutex);
+
+ if (m != NULL) {
+ m->refcnt++;
+ (*id)->data.data = m;
+ (*id)->data.length = sizeof(*m);
+ return 0;
+ }
+
+ m = mcc_alloc(res);
+ if (m == NULL) {
+ krb5_set_error_message(context, KRB5_CC_NOMEM,
+ N_("malloc: out of memory", ""));
+ return KRB5_CC_NOMEM;
+ }
+
+ (*id)->data.data = m;
+ (*id)->data.length = sizeof(*m);
+
+ return 0;
+}
+
+
+static krb5_error_code
+mcc_gen_new(krb5_context context, krb5_ccache *id)
+{
+ krb5_mcache *m;
+
+ m = mcc_alloc(NULL);
+
+ if (m == NULL) {
+ krb5_set_error_message(context, KRB5_CC_NOMEM,
+ N_("malloc: out of memory", ""));
+ return KRB5_CC_NOMEM;
+ }
+
+ (*id)->data.data = m;
+ (*id)->data.length = sizeof(*m);
+
+ return 0;
+}
+
+static krb5_error_code
+mcc_initialize(krb5_context context,
+ krb5_ccache id,
+ krb5_principal primary_principal)
+{
+ krb5_mcache *m = MCACHE(id);
+ m->dead = 0;
+ m->mtime = time(NULL);
+ return krb5_copy_principal (context,
+ primary_principal,
+ &m->primary_principal);
+}
+
+static int
+mcc_close_internal(krb5_mcache *m)
+{
+ if (--m->refcnt != 0)
+ return 0;
+
+ if (MISDEAD(m)) {
+ free (m->name);
+ return 1;
+ }
+ return 0;
+}
+
+static krb5_error_code
+mcc_close(krb5_context context,
+ krb5_ccache id)
+{
+ if (mcc_close_internal(MCACHE(id)))
+ krb5_data_free(&id->data);
+ return 0;
+}
+
+static krb5_error_code
+mcc_destroy(krb5_context context,
+ krb5_ccache id)
+{
+ krb5_mcache **n, *m = MCACHE(id);
+ struct link *l;
+
+ if (m->refcnt == 0)
+ krb5_abortx(context, "mcc_destroy: refcnt already 0");
+
+ if (!MISDEAD(m)) {
+ /* if this is an active mcache, remove it from the linked
+ list, and free all data */
+ HEIMDAL_MUTEX_lock(&mcc_mutex);
+ for(n = &mcc_head; n && *n; n = &(*n)->next) {
+ if(m == *n) {
+ *n = m->next;
+ break;
+ }
+ }
+ HEIMDAL_MUTEX_unlock(&mcc_mutex);
+ if (m->primary_principal != NULL) {
+ krb5_free_principal (context, m->primary_principal);
+ m->primary_principal = NULL;
+ }
+ m->dead = 1;
+
+ l = m->creds;
+ while (l != NULL) {
+ struct link *old;
+
+ krb5_free_cred_contents (context, &l->cred);
+ old = l;
+ l = l->next;
+ free (old);
+ }
+ m->creds = NULL;
+ }
+ return 0;
+}
+
+static krb5_error_code
+mcc_store_cred(krb5_context context,
+ krb5_ccache id,
+ krb5_creds *creds)
+{
+ krb5_mcache *m = MCACHE(id);
+ krb5_error_code ret;
+ struct link *l;
+
+ if (MISDEAD(m))
+ return ENOENT;
+
+ l = malloc (sizeof(*l));
+ if (l == NULL) {
+ krb5_set_error_message(context, KRB5_CC_NOMEM,
+ N_("malloc: out of memory", ""));
+ return KRB5_CC_NOMEM;
+ }
+ l->next = m->creds;
+ m->creds = l;
+ memset (&l->cred, 0, sizeof(l->cred));
+ ret = krb5_copy_creds_contents (context, creds, &l->cred);
+ if (ret) {
+ m->creds = l->next;
+ free (l);
+ return ret;
+ }
+ m->mtime = time(NULL);
+ return 0;
+}
+
+static krb5_error_code
+mcc_get_principal(krb5_context context,
+ krb5_ccache id,
+ krb5_principal *principal)
+{
+ krb5_mcache *m = MCACHE(id);
+
+ if (MISDEAD(m) || m->primary_principal == NULL)
+ return ENOENT;
+ return krb5_copy_principal (context,
+ m->primary_principal,
+ principal);
+}
+
+static krb5_error_code
+mcc_get_first (krb5_context context,
+ krb5_ccache id,
+ krb5_cc_cursor *cursor)
+{
+ krb5_mcache *m = MCACHE(id);
+
+ if (MISDEAD(m))
+ return ENOENT;
+
+ *cursor = m->creds;
+ return 0;
+}
+
+static krb5_error_code
+mcc_get_next (krb5_context context,
+ krb5_ccache id,
+ krb5_cc_cursor *cursor,
+ krb5_creds *creds)
+{
+ krb5_mcache *m = MCACHE(id);
+ struct link *l;
+
+ if (MISDEAD(m))
+ return ENOENT;
+
+ l = *cursor;
+ if (l != NULL) {
+ *cursor = l->next;
+ return krb5_copy_creds_contents (context,
+ &l->cred,
+ creds);
+ } else
+ return KRB5_CC_END;
+}
+
+static krb5_error_code
+mcc_end_get (krb5_context context,
+ krb5_ccache id,
+ krb5_cc_cursor *cursor)
+{
+ return 0;
+}
+
+static krb5_error_code
+mcc_remove_cred(krb5_context context,
+ krb5_ccache id,
+ krb5_flags which,
+ krb5_creds *mcreds)
+{
+ krb5_mcache *m = MCACHE(id);
+ struct link **q, *p;
+ for(q = &m->creds, p = *q; p; p = *q) {
+ if(krb5_compare_creds(context, which, mcreds, &p->cred)) {
+ *q = p->next;
+ krb5_free_cred_contents(context, &p->cred);
+ free(p);
+ m->mtime = time(NULL);
+ } else
+ q = &p->next;
+ }
+ return 0;
+}
+
+static krb5_error_code
+mcc_set_flags(krb5_context context,
+ krb5_ccache id,
+ krb5_flags flags)
+{
+ return 0; /* XXX */
+}
+
+struct mcache_iter {
+ krb5_mcache *cache;
+};
+
+static krb5_error_code
+mcc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
+{
+ struct mcache_iter *iter;
+
+ iter = calloc(1, sizeof(*iter));
+ if (iter == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+
+ HEIMDAL_MUTEX_lock(&mcc_mutex);
+ iter->cache = mcc_head;
+ if (iter->cache)
+ iter->cache->refcnt++;
+ HEIMDAL_MUTEX_unlock(&mcc_mutex);
+
+ *cursor = iter;
+ return 0;
+}
+
+static krb5_error_code
+mcc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
+{
+ struct mcache_iter *iter = cursor;
+ krb5_error_code ret;
+ krb5_mcache *m;
+
+ if (iter->cache == NULL)
+ return KRB5_CC_END;
+
+ HEIMDAL_MUTEX_lock(&mcc_mutex);
+ m = iter->cache;
+ if (m->next)
+ m->next->refcnt++;
+ iter->cache = m->next;
+ HEIMDAL_MUTEX_unlock(&mcc_mutex);
+
+ ret = _krb5_cc_allocate(context, &krb5_mcc_ops, id);
+ if (ret)
+ return ret;
+
+ (*id)->data.data = m;
+ (*id)->data.length = sizeof(*m);
+
+ return 0;
+}
+
+static krb5_error_code
+mcc_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
+{
+ struct mcache_iter *iter = cursor;
+
+ if (iter->cache)
+ mcc_close_internal(iter->cache);
+ iter->cache = NULL;
+ free(iter);
+ return 0;
+}
+
+static krb5_error_code
+mcc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
+{
+ krb5_mcache *mfrom = MCACHE(from), *mto = MCACHE(to);
+ struct link *creds;
+ krb5_principal principal;
+ krb5_mcache **n;
+
+ HEIMDAL_MUTEX_lock(&mcc_mutex);
+
+ /* drop the from cache from the linked list to avoid lookups */
+ for(n = &mcc_head; n && *n; n = &(*n)->next) {
+ if(mfrom == *n) {
+ *n = mfrom->next;
+ break;
+ }
+ }
+
+ /* swap creds */
+ creds = mto->creds;
+ mto->creds = mfrom->creds;
+ mfrom->creds = creds;
+ /* swap principal */
+ principal = mto->primary_principal;
+ mto->primary_principal = mfrom->primary_principal;
+ mfrom->primary_principal = principal;
+
+ mto->mtime = mfrom->mtime = time(NULL);
+
+ HEIMDAL_MUTEX_unlock(&mcc_mutex);
+ mcc_destroy(context, from);
+
+ return 0;
+}
+
+static krb5_error_code
+mcc_default_name(krb5_context context, char **str)
+{
+ *str = strdup("MEMORY:");
+ if (*str == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ return 0;
+}
+
+static krb5_error_code
+mcc_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime)
+{
+ *mtime = MCACHE(id)->mtime;
+ return 0;
+}
+
+
+/**
+ * Variable containing the MEMORY based credential cache implemention.
+ *
+ * @ingroup krb5_ccache
+ */
+
+KRB5_LIB_VARIABLE const krb5_cc_ops krb5_mcc_ops = {
+ KRB5_CC_OPS_VERSION,
+ "MEMORY",
+ mcc_get_name,
+ mcc_resolve,
+ mcc_gen_new,
+ mcc_initialize,
+ mcc_destroy,
+ mcc_close,
+ mcc_store_cred,
+ NULL, /* mcc_retrieve */
+ mcc_get_principal,
+ mcc_get_first,
+ mcc_get_next,
+ mcc_end_get,
+ mcc_remove_cred,
+ mcc_set_flags,
+ NULL,
+ mcc_get_cache_first,
+ mcc_get_cache_next,
+ mcc_end_cache_get,
+ mcc_move,
+ mcc_default_name,
+ NULL,
+ mcc_lastchange
+};
diff --git a/source4/heimdal/lib/krb5/misc.c b/source4/heimdal/lib/krb5/misc.c
new file mode 100644
index 0000000000..4cee5e22e1
--- /dev/null
+++ b/source4/heimdal/lib/krb5/misc.c
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 1997 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+krb5_error_code KRB5_LIB_FUNCTION
+_krb5_s4u2self_to_checksumdata(krb5_context context,
+ const PA_S4U2Self *self,
+ krb5_data *data)
+{
+ krb5_error_code ret;
+ krb5_ssize_t ssize;
+ krb5_storage *sp;
+ size_t size;
+ int i;
+
+ sp = krb5_storage_emem();
+ if (sp == NULL) {
+ krb5_clear_error_message(context);
+ return ENOMEM;
+ }
+ krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
+ ret = krb5_store_int32(sp, self->name.name_type);
+ if (ret)
+ goto out;
+ for (i = 0; i < self->name.name_string.len; i++) {
+ size = strlen(self->name.name_string.val[i]);
+ ssize = krb5_storage_write(sp, self->name.name_string.val[i], size);
+ if (ssize != size) {
+ ret = ENOMEM;
+ goto out;
+ }
+ }
+ size = strlen(self->realm);
+ ssize = krb5_storage_write(sp, self->realm, size);
+ if (ssize != size) {
+ ret = ENOMEM;
+ goto out;
+ }
+ size = strlen(self->auth);
+ ssize = krb5_storage_write(sp, self->auth, size);
+ if (ssize != size) {
+ ret = ENOMEM;
+ goto out;
+ }
+
+ ret = krb5_storage_to_data(sp, data);
+ krb5_storage_free(sp);
+ return ret;
+
+out:
+ krb5_clear_error_message(context);
+ return ret;
+}
diff --git a/source4/heimdal/lib/krb5/mit_glue.c b/source4/heimdal/lib/krb5/mit_glue.c
new file mode 100644
index 0000000000..f8f13922f5
--- /dev/null
+++ b/source4/heimdal/lib/krb5/mit_glue.c
@@ -0,0 +1,373 @@
+/*
+ * Copyright (c) 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+RCSID("$Id$");
+
+#ifndef HEIMDAL_SMALLER
+
+/*
+ * Glue for MIT API
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_c_make_checksum(krb5_context context,
+ krb5_cksumtype cksumtype,
+ const krb5_keyblock *key,
+ krb5_keyusage usage,
+ const krb5_data *input,
+ krb5_checksum *cksum)
+{
+ krb5_error_code ret;
+ krb5_crypto crypto;
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret)
+ return ret;
+
+ ret = krb5_create_checksum(context, crypto, usage, cksumtype,
+ input->data, input->length, cksum);
+ krb5_crypto_destroy(context, crypto);
+
+ return ret ;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_c_verify_checksum(krb5_context context, const krb5_keyblock *key,
+ krb5_keyusage usage, const krb5_data *data,
+ const krb5_checksum *cksum, krb5_boolean *valid)
+{
+ krb5_error_code ret;
+ krb5_checksum data_cksum;
+
+ *valid = 0;
+
+ ret = krb5_c_make_checksum(context, cksum->cksumtype,
+ key, usage, data, &data_cksum);
+ if (ret)
+ return ret;
+
+ if (data_cksum.cksumtype == cksum->cksumtype
+ && data_cksum.checksum.length == cksum->checksum.length
+ && memcmp(data_cksum.checksum.data, cksum->checksum.data, cksum->checksum.length) == 0)
+ *valid = 1;
+
+ krb5_free_checksum_contents(context, &data_cksum);
+
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_c_get_checksum(krb5_context context, const krb5_checksum *cksum,
+ krb5_cksumtype *type, krb5_data **data)
+{
+ krb5_error_code ret;
+
+ if (type)
+ *type = cksum->cksumtype;
+ if (data) {
+ *data = malloc(sizeof(**data));
+ if (*data == NULL)
+ return ENOMEM;
+
+ ret = der_copy_octet_string(&cksum->checksum, *data);
+ if (ret) {
+ free(*data);
+ *data = NULL;
+ return ret;
+ }
+ }
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_c_set_checksum(krb5_context context, krb5_checksum *cksum,
+ krb5_cksumtype type, const krb5_data *data)
+{
+ cksum->cksumtype = type;
+ return der_copy_octet_string(data, &cksum->checksum);
+}
+
+void KRB5_LIB_FUNCTION
+krb5_free_checksum (krb5_context context, krb5_checksum *cksum)
+{
+ krb5_checksum_free(context, cksum);
+ free(cksum);
+}
+
+void KRB5_LIB_FUNCTION
+krb5_free_checksum_contents(krb5_context context, krb5_checksum *cksum)
+{
+ krb5_checksum_free(context, cksum);
+ memset(cksum, 0, sizeof(*cksum));
+}
+
+void KRB5_LIB_FUNCTION
+krb5_checksum_free(krb5_context context, krb5_checksum *cksum)
+{
+ free_Checksum(cksum);
+}
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_c_valid_enctype (krb5_enctype etype)
+{
+ return krb5_enctype_valid(NULL, etype);
+}
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_c_valid_cksumtype(krb5_cksumtype ctype)
+{
+ return krb5_cksumtype_valid(NULL, ctype);
+}
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_c_is_coll_proof_cksum(krb5_cksumtype ctype)
+{
+ return krb5_checksum_is_collision_proof(NULL, ctype);
+}
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_c_is_keyed_cksum(krb5_cksumtype ctype)
+{
+ return krb5_checksum_is_keyed(NULL, ctype);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_copy_checksum (krb5_context context,
+ const krb5_checksum *old,
+ krb5_checksum **new)
+{
+ *new = malloc(sizeof(**new));
+ if (*new == NULL)
+ return ENOMEM;
+ return copy_Checksum(old, *new);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_c_checksum_length (krb5_context context, krb5_cksumtype cksumtype,
+ size_t *length)
+{
+ return krb5_checksumsize(context, cksumtype, length);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_c_block_size(krb5_context context,
+ krb5_enctype enctype,
+ size_t *blocksize)
+{
+ krb5_error_code ret;
+ krb5_crypto crypto;
+ krb5_keyblock key;
+
+ ret = krb5_generate_random_keyblock(context, enctype, &key);
+ if (ret)
+ return ret;
+
+ ret = krb5_crypto_init(context, &key, 0, &crypto);
+ krb5_free_keyblock_contents(context, &key);
+ if (ret)
+ return ret;
+ ret = krb5_crypto_getblocksize(context, crypto, blocksize);
+ krb5_crypto_destroy(context, crypto);
+
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_c_decrypt(krb5_context context,
+ const krb5_keyblock key,
+ krb5_keyusage usage,
+ const krb5_data *ivec,
+ krb5_enc_data *input,
+ krb5_data *output)
+{
+ krb5_error_code ret;
+ krb5_crypto crypto;
+
+ ret = krb5_crypto_init(context, &key, input->enctype, &crypto);
+ if (ret)
+ return ret;
+
+ if (ivec) {
+ size_t blocksize;
+
+ ret = krb5_crypto_getblocksize(context, crypto, &blocksize);
+ if (ret) {
+ krb5_crypto_destroy(context, crypto);
+ return ret;
+ }
+
+ if (blocksize > ivec->length) {
+ krb5_crypto_destroy(context, crypto);
+ return KRB5_BAD_MSIZE;
+ }
+ }
+
+ ret = krb5_decrypt_ivec(context, crypto, usage,
+ input->ciphertext.data, input->ciphertext.length,
+ output,
+ ivec ? ivec->data : NULL);
+
+ krb5_crypto_destroy(context, crypto);
+
+ return ret ;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_c_encrypt(krb5_context context,
+ const krb5_keyblock *key,
+ krb5_keyusage usage,
+ const krb5_data *ivec,
+ const krb5_data *input,
+ krb5_enc_data *output)
+{
+ krb5_error_code ret;
+ krb5_crypto crypto;
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret)
+ return ret;
+
+ if (ivec) {
+ size_t blocksize;
+
+ ret = krb5_crypto_getblocksize(context, crypto, &blocksize);
+ if (ret) {
+ krb5_crypto_destroy(context, crypto);
+ return ret;
+ }
+
+ if (blocksize > ivec->length) {
+ krb5_crypto_destroy(context, crypto);
+ return KRB5_BAD_MSIZE;
+ }
+ }
+
+ ret = krb5_encrypt_ivec(context, crypto, usage,
+ input->data, input->length,
+ &output->ciphertext,
+ ivec ? ivec->data : NULL);
+ output->kvno = 0;
+ krb5_crypto_getenctype(context, crypto, &output->enctype);
+
+ krb5_crypto_destroy(context, crypto);
+
+ return ret ;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_c_encrypt_length(krb5_context context,
+ krb5_enctype enctype,
+ size_t inputlen,
+ size_t *length)
+{
+ krb5_error_code ret;
+ krb5_crypto crypto;
+ krb5_keyblock key;
+
+ ret = krb5_generate_random_keyblock(context, enctype, &key);
+ if (ret)
+ return ret;
+
+ ret = krb5_crypto_init(context, &key, 0, &crypto);
+ krb5_free_keyblock_contents(context, &key);
+ if (ret)
+ return ret;
+
+ *length = krb5_get_wrapped_length(context, crypto, inputlen);
+ krb5_crypto_destroy(context, crypto);
+
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_c_enctype_compare(krb5_context context,
+ krb5_enctype e1,
+ krb5_enctype e2,
+ krb5_boolean *similar)
+{
+ *similar = krb5_enctypes_compatible_keys(context, e1, e2);
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_c_make_random_key(krb5_context context,
+ krb5_enctype enctype,
+ krb5_keyblock *random_key)
+{
+ return krb5_generate_random_keyblock(context, enctype, random_key);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_c_keylengths(krb5_context context,
+ krb5_enctype enctype,
+ size_t *ilen,
+ size_t *keylen)
+{
+ krb5_error_code ret;
+
+ ret = krb5_enctype_keybits(context, enctype, ilen);
+ if (ret)
+ return ret;
+ *ilen = (*ilen + 7) / 8;
+ return krb5_enctype_keysize(context, enctype, keylen);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_c_prf_length(krb5_context context,
+ krb5_enctype type,
+ size_t *length)
+{
+ return krb5_crypto_prf_length(context, type, length);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_c_prf(krb5_context context,
+ const krb5_keyblock *key,
+ const krb5_data *input,
+ krb5_data *output)
+{
+ krb5_crypto crypto;
+ krb5_error_code ret;
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret)
+ return ret;
+
+ ret = krb5_crypto_prf(context, crypto, input, output);
+ krb5_crypto_destroy(context, crypto);
+
+ return ret;
+}
+
+#endif /* HEIMDAL_SMALLER */
diff --git a/source4/heimdal/lib/krb5/mk_error.c b/source4/heimdal/lib/krb5/mk_error.c
new file mode 100644
index 0000000000..989aa23d75
--- /dev/null
+++ b/source4/heimdal/lib/krb5/mk_error.c
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 1997 - 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_mk_error(krb5_context context,
+ krb5_error_code error_code,
+ const char *e_text,
+ const krb5_data *e_data,
+ const krb5_principal client,
+ const krb5_principal server,
+ time_t *client_time,
+ int *client_usec,
+ krb5_data *reply)
+{
+ KRB_ERROR msg;
+ krb5_timestamp sec;
+ int32_t usec;
+ size_t len;
+ krb5_error_code ret = 0;
+
+ krb5_us_timeofday (context, &sec, &usec);
+
+ memset(&msg, 0, sizeof(msg));
+ msg.pvno = 5;
+ msg.msg_type = krb_error;
+ msg.stime = sec;
+ msg.susec = usec;
+ msg.ctime = client_time;
+ msg.cusec = client_usec;
+ /* Make sure we only send `protocol' error codes */
+ if(error_code < KRB5KDC_ERR_NONE || error_code >= KRB5_ERR_RCSID) {
+ if(e_text == NULL)
+ e_text = krb5_get_err_text(context, error_code);
+ error_code = KRB5KRB_ERR_GENERIC;
+ }
+ msg.error_code = error_code - KRB5KDC_ERR_NONE;
+ if (e_text)
+ msg.e_text = rk_UNCONST(&e_text);
+ if (e_data)
+ msg.e_data = rk_UNCONST(e_data);
+ if(server){
+ msg.realm = server->realm;
+ msg.sname = server->name;
+ }else{
+ msg.realm = "<unspecified realm>";
+ }
+ if(client){
+ msg.crealm = &client->realm;
+ msg.cname = &client->name;
+ }
+
+ ASN1_MALLOC_ENCODE(KRB_ERROR, reply->data, reply->length, &msg, &len, ret);
+ if (ret)
+ return ret;
+ if(reply->length != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+ return 0;
+}
diff --git a/source4/heimdal/lib/krb5/mk_priv.c b/source4/heimdal/lib/krb5/mk_priv.c
new file mode 100644
index 0000000000..86a6b669b1
--- /dev/null
+++ b/source4/heimdal/lib/krb5/mk_priv.c
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 1997 - 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <krb5_locl.h>
+
+RCSID("$Id$");
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_mk_priv(krb5_context context,
+ krb5_auth_context auth_context,
+ const krb5_data *userdata,
+ krb5_data *outbuf,
+ krb5_replay_data *outdata)
+{
+ krb5_error_code ret;
+ KRB_PRIV s;
+ EncKrbPrivPart part;
+ u_char *buf = NULL;
+ size_t buf_size;
+ size_t len;
+ krb5_crypto crypto;
+ krb5_keyblock *key;
+ krb5_replay_data rdata;
+
+ if ((auth_context->flags &
+ (KRB5_AUTH_CONTEXT_RET_TIME | KRB5_AUTH_CONTEXT_RET_SEQUENCE)) &&
+ outdata == NULL)
+ return KRB5_RC_REQUIRED; /* XXX better error, MIT returns this */
+
+ if (auth_context->local_subkey)
+ key = auth_context->local_subkey;
+ else if (auth_context->remote_subkey)
+ key = auth_context->remote_subkey;
+ else
+ key = auth_context->keyblock;
+
+ memset(&rdata, 0, sizeof(rdata));
+
+ part.user_data = *userdata;
+
+ krb5_us_timeofday (context, &rdata.timestamp, &rdata.usec);
+
+ if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_TIME) {
+ part.timestamp = &rdata.timestamp;
+ part.usec = &rdata.usec;
+ } else {
+ part.timestamp = NULL;
+ part.usec = NULL;
+ }
+
+ if (auth_context->flags & KRB5_AUTH_CONTEXT_RET_TIME) {
+ outdata->timestamp = rdata.timestamp;
+ outdata->usec = rdata.usec;
+ }
+
+ if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) {
+ rdata.seq = auth_context->local_seqnumber;
+ part.seq_number = &rdata.seq;
+ } else
+ part.seq_number = NULL;
+
+ if (auth_context->flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)
+ outdata->seq = auth_context->local_seqnumber;
+
+ part.s_address = auth_context->local_address;
+ part.r_address = auth_context->remote_address;
+
+ krb5_data_zero (&s.enc_part.cipher);
+
+ ASN1_MALLOC_ENCODE(EncKrbPrivPart, buf, buf_size, &part, &len, ret);
+ if (ret)
+ goto fail;
+ if (buf_size != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+
+ s.pvno = 5;
+ s.msg_type = krb_priv;
+ s.enc_part.etype = key->keytype;
+ s.enc_part.kvno = NULL;
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret) {
+ free (buf);
+ return ret;
+ }
+ ret = krb5_encrypt (context,
+ crypto,
+ KRB5_KU_KRB_PRIV,
+ buf + buf_size - len,
+ len,
+ &s.enc_part.cipher);
+ krb5_crypto_destroy(context, crypto);
+ if (ret) {
+ free(buf);
+ return ret;
+ }
+ free(buf);
+
+
+ ASN1_MALLOC_ENCODE(KRB_PRIV, buf, buf_size, &s, &len, ret);
+ if (ret)
+ goto fail;
+ if (buf_size != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+
+ krb5_data_free (&s.enc_part.cipher);
+
+ ret = krb5_data_copy(outbuf, buf + buf_size - len, len);
+ if (ret) {
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ free(buf);
+ return ENOMEM;
+ }
+ free (buf);
+ if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE)
+ auth_context->local_seqnumber =
+ (auth_context->local_seqnumber + 1) & 0xFFFFFFFF;
+ return 0;
+
+ fail:
+ free (buf);
+ krb5_data_free (&s.enc_part.cipher);
+ return ret;
+}
diff --git a/source4/heimdal/lib/krb5/mk_rep.c b/source4/heimdal/lib/krb5/mk_rep.c
new file mode 100644
index 0000000000..bba276183a
--- /dev/null
+++ b/source4/heimdal/lib/krb5/mk_rep.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 1997 - 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <krb5_locl.h>
+
+RCSID("$Id$");
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_mk_rep(krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_data *outbuf)
+{
+ krb5_error_code ret;
+ AP_REP ap;
+ EncAPRepPart body;
+ u_char *buf = NULL;
+ size_t buf_size;
+ size_t len;
+ krb5_crypto crypto;
+
+ ap.pvno = 5;
+ ap.msg_type = krb_ap_rep;
+
+ memset (&body, 0, sizeof(body));
+
+ body.ctime = auth_context->authenticator->ctime;
+ body.cusec = auth_context->authenticator->cusec;
+ if (auth_context->flags & KRB5_AUTH_CONTEXT_USE_SUBKEY) {
+ if (auth_context->local_subkey == NULL) {
+ ret = krb5_auth_con_generatelocalsubkey(context,
+ auth_context,
+ auth_context->keyblock);
+ if(ret) {
+ free_EncAPRepPart(&body);
+ return ret;
+ }
+ }
+ ret = krb5_copy_keyblock(context, auth_context->local_subkey,
+ &body.subkey);
+ if (ret) {
+ free_EncAPRepPart(&body);
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ } else
+ body.subkey = NULL;
+ if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) {
+ if(auth_context->local_seqnumber == 0)
+ krb5_generate_seq_number (context,
+ auth_context->keyblock,
+ &auth_context->local_seqnumber);
+ ALLOC(body.seq_number, 1);
+ if (body.seq_number == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ free_EncAPRepPart(&body);
+ return ENOMEM;
+ }
+ *(body.seq_number) = auth_context->local_seqnumber;
+ } else
+ body.seq_number = NULL;
+
+ ap.enc_part.etype = auth_context->keyblock->keytype;
+ ap.enc_part.kvno = NULL;
+
+ ASN1_MALLOC_ENCODE(EncAPRepPart, buf, buf_size, &body, &len, ret);
+ free_EncAPRepPart (&body);
+ if(ret)
+ return ret;
+ if (buf_size != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+ ret = krb5_crypto_init(context, auth_context->keyblock,
+ 0 /* ap.enc_part.etype */, &crypto);
+ if (ret) {
+ free (buf);
+ return ret;
+ }
+ ret = krb5_encrypt (context,
+ crypto,
+ KRB5_KU_AP_REQ_ENC_PART,
+ buf + buf_size - len,
+ len,
+ &ap.enc_part.cipher);
+ krb5_crypto_destroy(context, crypto);
+ free(buf);
+ if (ret)
+ return ret;
+
+ ASN1_MALLOC_ENCODE(AP_REP, outbuf->data, outbuf->length, &ap, &len, ret);
+ if (ret == 0 && outbuf->length != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+ free_AP_REP (&ap);
+ return ret;
+}
diff --git a/source4/heimdal/lib/krb5/mk_req.c b/source4/heimdal/lib/krb5/mk_req.c
new file mode 100644
index 0000000000..1570637738
--- /dev/null
+++ b/source4/heimdal/lib/krb5/mk_req.c
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 1997 - 2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <krb5_locl.h>
+
+RCSID("$Id$");
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_mk_req_exact(krb5_context context,
+ krb5_auth_context *auth_context,
+ const krb5_flags ap_req_options,
+ const krb5_principal server,
+ krb5_data *in_data,
+ krb5_ccache ccache,
+ krb5_data *outbuf)
+{
+ krb5_error_code ret;
+ krb5_creds this_cred, *cred;
+
+ memset(&this_cred, 0, sizeof(this_cred));
+
+ ret = krb5_cc_get_principal(context, ccache, &this_cred.client);
+
+ if(ret)
+ return ret;
+
+ ret = krb5_copy_principal (context, server, &this_cred.server);
+ if (ret) {
+ krb5_free_cred_contents (context, &this_cred);
+ return ret;
+ }
+
+ this_cred.times.endtime = 0;
+ if (auth_context && *auth_context && (*auth_context)->keytype)
+ this_cred.session.keytype = (*auth_context)->keytype;
+
+ ret = krb5_get_credentials (context, 0, ccache, &this_cred, &cred);
+ krb5_free_cred_contents(context, &this_cred);
+ if (ret)
+ return ret;
+
+ ret = krb5_mk_req_extended (context,
+ auth_context,
+ ap_req_options,
+ in_data,
+ cred,
+ outbuf);
+ krb5_free_creds(context, cred);
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_mk_req(krb5_context context,
+ krb5_auth_context *auth_context,
+ const krb5_flags ap_req_options,
+ const char *service,
+ const char *hostname,
+ krb5_data *in_data,
+ krb5_ccache ccache,
+ krb5_data *outbuf)
+{
+ krb5_error_code ret;
+ char **realms;
+ char *real_hostname;
+ krb5_principal server;
+
+ ret = krb5_expand_hostname_realms (context, hostname,
+ &real_hostname, &realms);
+ if (ret)
+ return ret;
+
+ ret = krb5_build_principal (context, &server,
+ strlen(*realms),
+ *realms,
+ service,
+ real_hostname,
+ NULL);
+ free (real_hostname);
+ krb5_free_host_realm (context, realms);
+ if (ret)
+ return ret;
+ ret = krb5_mk_req_exact (context, auth_context, ap_req_options,
+ server, in_data, ccache, outbuf);
+ krb5_free_principal (context, server);
+ return ret;
+}
diff --git a/source4/heimdal/lib/krb5/mk_req_ext.c b/source4/heimdal/lib/krb5/mk_req_ext.c
new file mode 100644
index 0000000000..aba804716c
--- /dev/null
+++ b/source4/heimdal/lib/krb5/mk_req_ext.c
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 1997 - 2002 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <krb5_locl.h>
+
+RCSID("$Id$");
+
+krb5_error_code
+_krb5_mk_req_internal(krb5_context context,
+ krb5_auth_context *auth_context,
+ const krb5_flags ap_req_options,
+ krb5_data *in_data,
+ krb5_creds *in_creds,
+ krb5_data *outbuf,
+ krb5_key_usage checksum_usage,
+ krb5_key_usage encrypt_usage)
+{
+ krb5_error_code ret;
+ krb5_data authenticator;
+ Checksum c;
+ Checksum *c_opt;
+ krb5_auth_context ac;
+
+ if(auth_context) {
+ if(*auth_context == NULL)
+ ret = krb5_auth_con_init(context, auth_context);
+ else
+ ret = 0;
+ ac = *auth_context;
+ } else
+ ret = krb5_auth_con_init(context, &ac);
+ if(ret)
+ return ret;
+
+ if(ac->local_subkey == NULL && (ap_req_options & AP_OPTS_USE_SUBKEY)) {
+ ret = krb5_auth_con_generatelocalsubkey(context,
+ ac,
+ &in_creds->session);
+ if(ret)
+ goto out;
+ }
+
+ krb5_free_keyblock(context, ac->keyblock);
+ ret = krb5_copy_keyblock(context, &in_creds->session, &ac->keyblock);
+ if (ret)
+ goto out;
+
+ /* it's unclear what type of checksum we can use. try the best one, except:
+ * a) if it's configured differently for the current realm, or
+ * b) if the session key is des-cbc-crc
+ */
+
+ if (in_data) {
+ if(ac->keyblock->keytype == ETYPE_DES_CBC_CRC) {
+ /* this is to make DCE secd (and older MIT kdcs?) happy */
+ ret = krb5_create_checksum(context,
+ NULL,
+ 0,
+ CKSUMTYPE_RSA_MD4,
+ in_data->data,
+ in_data->length,
+ &c);
+ } else if(ac->keyblock->keytype == ETYPE_ARCFOUR_HMAC_MD5 ||
+ ac->keyblock->keytype == ETYPE_ARCFOUR_HMAC_MD5_56 ||
+ ac->keyblock->keytype == ETYPE_DES_CBC_MD4 ||
+ ac->keyblock->keytype == ETYPE_DES_CBC_MD5) {
+ /* this is to make MS kdc happy */
+ ret = krb5_create_checksum(context,
+ NULL,
+ 0,
+ CKSUMTYPE_RSA_MD5,
+ in_data->data,
+ in_data->length,
+ &c);
+ } else {
+ krb5_crypto crypto;
+
+ ret = krb5_crypto_init(context, ac->keyblock, 0, &crypto);
+ if (ret)
+ goto out;
+ ret = krb5_create_checksum(context,
+ crypto,
+ checksum_usage,
+ 0,
+ in_data->data,
+ in_data->length,
+ &c);
+ krb5_crypto_destroy(context, crypto);
+ }
+ c_opt = &c;
+ } else {
+ c_opt = NULL;
+ }
+
+ if (ret)
+ goto out;
+
+ ret = krb5_build_authenticator (context,
+ ac,
+ ac->keyblock->keytype,
+ in_creds,
+ c_opt,
+ NULL,
+ &authenticator,
+ encrypt_usage);
+ if (c_opt)
+ free_Checksum (c_opt);
+ if (ret)
+ goto out;
+
+ ret = krb5_build_ap_req (context, ac->keyblock->keytype,
+ in_creds, ap_req_options, authenticator, outbuf);
+out:
+ if(auth_context == NULL)
+ krb5_auth_con_free(context, ac);
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_mk_req_extended(krb5_context context,
+ krb5_auth_context *auth_context,
+ const krb5_flags ap_req_options,
+ krb5_data *in_data,
+ krb5_creds *in_creds,
+ krb5_data *outbuf)
+{
+ return _krb5_mk_req_internal (context,
+ auth_context,
+ ap_req_options,
+ in_data,
+ in_creds,
+ outbuf,
+ KRB5_KU_AP_REQ_AUTH_CKSUM,
+ KRB5_KU_AP_REQ_AUTH);
+}
diff --git a/source4/heimdal/lib/krb5/n-fold.c b/source4/heimdal/lib/krb5/n-fold.c
new file mode 100644
index 0000000000..fa45b09f18
--- /dev/null
+++ b/source4/heimdal/lib/krb5/n-fold.c
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 1999 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of KTH nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+static krb5_error_code
+rr13(unsigned char *buf, size_t len)
+{
+ unsigned char *tmp;
+ int bytes = (len + 7) / 8;
+ int i;
+ if(len == 0)
+ return 0;
+ {
+ const int bits = 13 % len;
+ const int lbit = len % 8;
+
+ tmp = malloc(bytes);
+ if (tmp == NULL)
+ return ENOMEM;
+ memcpy(tmp, buf, bytes);
+ if(lbit) {
+ /* pad final byte with inital bits */
+ tmp[bytes - 1] &= 0xff << (8 - lbit);
+ for(i = lbit; i < 8; i += len)
+ tmp[bytes - 1] |= buf[0] >> i;
+ }
+ for(i = 0; i < bytes; i++) {
+ int bb;
+ int b1, s1, b2, s2;
+ /* calculate first bit position of this byte */
+ bb = 8 * i - bits;
+ while(bb < 0)
+ bb += len;
+ /* byte offset and shift count */
+ b1 = bb / 8;
+ s1 = bb % 8;
+
+ if(bb + 8 > bytes * 8)
+ /* watch for wraparound */
+ s2 = (len + 8 - s1) % 8;
+ else
+ s2 = 8 - s1;
+ b2 = (b1 + 1) % bytes;
+ buf[i] = (tmp[b1] << s1) | (tmp[b2] >> s2);
+ }
+ free(tmp);
+ }
+ return 0;
+}
+
+/* Add `b' to `a', both being one's complement numbers. */
+static void
+add1(unsigned char *a, unsigned char *b, size_t len)
+{
+ int i;
+ int carry = 0;
+ for(i = len - 1; i >= 0; i--){
+ int x = a[i] + b[i] + carry;
+ carry = x > 0xff;
+ a[i] = x & 0xff;
+ }
+ for(i = len - 1; carry && i >= 0; i--){
+ int x = a[i] + carry;
+ carry = x > 0xff;
+ a[i] = x & 0xff;
+ }
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+_krb5_n_fold(const void *str, size_t len, void *key, size_t size)
+{
+ /* if len < size we need at most N * len bytes, ie < 2 * size;
+ if len > size we need at most 2 * len */
+ krb5_error_code ret = 0;
+ size_t maxlen = 2 * max(size, len);
+ size_t l = 0;
+ unsigned char *tmp = malloc(maxlen);
+ unsigned char *buf = malloc(len);
+
+ if (tmp == NULL || buf == NULL)
+ return ENOMEM;
+
+ memcpy(buf, str, len);
+ memset(key, 0, size);
+ do {
+ memcpy(tmp + l, buf, len);
+ l += len;
+ ret = rr13(buf, len * 8);
+ if (ret)
+ goto out;
+ while(l >= size) {
+ add1(key, tmp, size);
+ l -= size;
+ if(l == 0)
+ break;
+ memmove(tmp, tmp + size, l);
+ }
+ } while(l != 0);
+out:
+ memset(buf, 0, len);
+ free(buf);
+ memset(tmp, 0, maxlen);
+ free(tmp);
+ return ret;
+}
diff --git a/source4/heimdal/lib/krb5/pac.c b/source4/heimdal/lib/krb5/pac.c
new file mode 100644
index 0000000000..3c55eb3dc3
--- /dev/null
+++ b/source4/heimdal/lib/krb5/pac.c
@@ -0,0 +1,1062 @@
+/*
+ * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+#include <wind.h>
+
+RCSID("$Id$");
+
+struct PAC_INFO_BUFFER {
+ uint32_t type;
+ uint32_t buffersize;
+ uint32_t offset_hi;
+ uint32_t offset_lo;
+};
+
+struct PACTYPE {
+ uint32_t numbuffers;
+ uint32_t version;
+ struct PAC_INFO_BUFFER buffers[1];
+};
+
+struct krb5_pac_data {
+ struct PACTYPE *pac;
+ krb5_data data;
+ struct PAC_INFO_BUFFER *server_checksum;
+ struct PAC_INFO_BUFFER *privsvr_checksum;
+ struct PAC_INFO_BUFFER *logon_name;
+};
+
+#define PAC_ALIGNMENT 8
+
+#define PACTYPE_SIZE 8
+#define PAC_INFO_BUFFER_SIZE 16
+
+#define PAC_SERVER_CHECKSUM 6
+#define PAC_PRIVSVR_CHECKSUM 7
+#define PAC_LOGON_NAME 10
+#define PAC_CONSTRAINED_DELEGATION 11
+
+#define CHECK(r,f,l) \
+ do { \
+ if (((r) = f ) != 0) { \
+ krb5_clear_error_message(context); \
+ goto l; \
+ } \
+ } while(0)
+
+static const char zeros[PAC_ALIGNMENT] = { 0 };
+
+/*
+ *
+ */
+
+krb5_error_code
+krb5_pac_parse(krb5_context context, const void *ptr, size_t len,
+ krb5_pac *pac)
+{
+ krb5_error_code ret;
+ krb5_pac p;
+ krb5_storage *sp = NULL;
+ uint32_t i, tmp, tmp2, header_end;
+
+ p = calloc(1, sizeof(*p));
+ if (p == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ goto out;
+ }
+
+ sp = krb5_storage_from_readonly_mem(ptr, len);
+ if (sp == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ goto out;
+ }
+ krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
+
+ CHECK(ret, krb5_ret_uint32(sp, &tmp), out);
+ CHECK(ret, krb5_ret_uint32(sp, &tmp2), out);
+ if (tmp < 1) {
+ ret = EINVAL; /* Too few buffers */
+ krb5_set_error_message(context, ret, N_("PAC have too few buffer", ""));
+ goto out;
+ }
+ if (tmp2 != 0) {
+ ret = EINVAL; /* Wrong version */
+ krb5_set_error_message(context, ret,
+ N_("PAC have wrong version %d", ""),
+ (int)tmp2);
+ goto out;
+ }
+
+ p->pac = calloc(1,
+ sizeof(*p->pac) + (sizeof(p->pac->buffers[0]) * (tmp - 1)));
+ if (p->pac == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ goto out;
+ }
+
+ p->pac->numbuffers = tmp;
+ p->pac->version = tmp2;
+
+ header_end = PACTYPE_SIZE + (PAC_INFO_BUFFER_SIZE * p->pac->numbuffers);
+ if (header_end > len) {
+ ret = EINVAL;
+ goto out;
+ }
+
+ for (i = 0; i < p->pac->numbuffers; i++) {
+ CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].type), out);
+ CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].buffersize), out);
+ CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].offset_lo), out);
+ CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].offset_hi), out);
+
+ /* consistency checks */
+ if (p->pac->buffers[i].offset_lo & (PAC_ALIGNMENT - 1)) {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret,
+ N_("PAC out of allignment", ""));
+ goto out;
+ }
+ if (p->pac->buffers[i].offset_hi) {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret,
+ N_("PAC high offset set", ""));
+ goto out;
+ }
+ if (p->pac->buffers[i].offset_lo > len) {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret,
+ N_("PAC offset off end", ""));
+ goto out;
+ }
+ if (p->pac->buffers[i].offset_lo < header_end) {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret,
+ N_("PAC offset inside header: %lu %lu", ""),
+ (unsigned long)p->pac->buffers[i].offset_lo,
+ (unsigned long)header_end);
+ goto out;
+ }
+ if (p->pac->buffers[i].buffersize > len - p->pac->buffers[i].offset_lo){
+ ret = EINVAL;
+ krb5_set_error_message(context, ret, N_("PAC length off end", ""));
+ goto out;
+ }
+
+ /* let save pointer to data we need later */
+ if (p->pac->buffers[i].type == PAC_SERVER_CHECKSUM) {
+ if (p->server_checksum) {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret,
+ N_("PAC have two server checksums", ""));
+ goto out;
+ }
+ p->server_checksum = &p->pac->buffers[i];
+ } else if (p->pac->buffers[i].type == PAC_PRIVSVR_CHECKSUM) {
+ if (p->privsvr_checksum) {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret,
+ N_("PAC have two KDC checksums", ""));
+ goto out;
+ }
+ p->privsvr_checksum = &p->pac->buffers[i];
+ } else if (p->pac->buffers[i].type == PAC_LOGON_NAME) {
+ if (p->logon_name) {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret,
+ N_("PAC have two logon names", ""));
+ goto out;
+ }
+ p->logon_name = &p->pac->buffers[i];
+ }
+ }
+
+ ret = krb5_data_copy(&p->data, ptr, len);
+ if (ret)
+ goto out;
+
+ krb5_storage_free(sp);
+
+ *pac = p;
+ return 0;
+
+out:
+ if (sp)
+ krb5_storage_free(sp);
+ if (p) {
+ if (p->pac)
+ free(p->pac);
+ free(p);
+ }
+ *pac = NULL;
+
+ return ret;
+}
+
+krb5_error_code
+krb5_pac_init(krb5_context context, krb5_pac *pac)
+{
+ krb5_error_code ret;
+ krb5_pac p;
+
+ p = calloc(1, sizeof(*p));
+ if (p == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+
+ p->pac = calloc(1, sizeof(*p->pac));
+ if (p->pac == NULL) {
+ free(p);
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+
+ ret = krb5_data_alloc(&p->data, PACTYPE_SIZE);
+ if (ret) {
+ free (p->pac);
+ free(p);
+ krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ return ret;
+ }
+
+
+ *pac = p;
+ return 0;
+}
+
+krb5_error_code
+krb5_pac_add_buffer(krb5_context context, krb5_pac p,
+ uint32_t type, const krb5_data *data)
+{
+ krb5_error_code ret;
+ void *ptr;
+ size_t len, offset, header_end, old_end;
+ uint32_t i;
+
+ len = p->pac->numbuffers;
+
+ ptr = realloc(p->pac,
+ sizeof(*p->pac) + (sizeof(p->pac->buffers[0]) * len));
+ if (ptr == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ p->pac = ptr;
+
+ for (i = 0; i < len; i++)
+ p->pac->buffers[i].offset_lo += PAC_INFO_BUFFER_SIZE;
+
+ offset = p->data.length + PAC_INFO_BUFFER_SIZE;
+
+ p->pac->buffers[len].type = type;
+ p->pac->buffers[len].buffersize = data->length;
+ p->pac->buffers[len].offset_lo = offset;
+ p->pac->buffers[len].offset_hi = 0;
+
+ old_end = p->data.length;
+ len = p->data.length + data->length + PAC_INFO_BUFFER_SIZE;
+ if (len < p->data.length) {
+ krb5_set_error_message(context, EINVAL, "integer overrun");
+ return EINVAL;
+ }
+
+ /* align to PAC_ALIGNMENT */
+ len = ((len + PAC_ALIGNMENT - 1) / PAC_ALIGNMENT) * PAC_ALIGNMENT;
+
+ ret = krb5_data_realloc(&p->data, len);
+ if (ret) {
+ krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ return ret;
+ }
+
+ /*
+ * make place for new PAC INFO BUFFER header
+ */
+ header_end = PACTYPE_SIZE + (PAC_INFO_BUFFER_SIZE * p->pac->numbuffers);
+ memmove((unsigned char *)p->data.data + header_end + PAC_INFO_BUFFER_SIZE,
+ (unsigned char *)p->data.data + header_end ,
+ old_end - header_end);
+ memset((unsigned char *)p->data.data + header_end, 0, PAC_INFO_BUFFER_SIZE);
+
+ /*
+ * copy in new data part
+ */
+
+ memcpy((unsigned char *)p->data.data + offset,
+ data->data, data->length);
+ memset((unsigned char *)p->data.data + offset + data->length,
+ 0, p->data.length - offset - data->length);
+
+ p->pac->numbuffers += 1;
+
+ return 0;
+}
+
+/**
+ * Get the PAC buffer of specific type from the pac.
+ *
+ * @param context Kerberos 5 context.
+ * @param p the pac structure returned by krb5_pac_parse().
+ * @param type type of buffer to get
+ * @param data return data, free with krb5_data_free().
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5_pac
+ */
+
+krb5_error_code
+krb5_pac_get_buffer(krb5_context context, krb5_pac p,
+ uint32_t type, krb5_data *data)
+{
+ krb5_error_code ret;
+ uint32_t i;
+
+ for (i = 0; i < p->pac->numbuffers; i++) {
+ const size_t len = p->pac->buffers[i].buffersize;
+ const size_t offset = p->pac->buffers[i].offset_lo;
+
+ if (p->pac->buffers[i].type != type)
+ continue;
+
+ ret = krb5_data_copy(data, (unsigned char *)p->data.data + offset, len);
+ if (ret) {
+ krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ return ret;
+ }
+ return 0;
+ }
+ krb5_set_error_message(context, ENOENT, "No PAC buffer of type %lu was found",
+ (unsigned long)type);
+ return ENOENT;
+}
+
+/*
+ *
+ */
+
+krb5_error_code
+krb5_pac_get_types(krb5_context context,
+ krb5_pac p,
+ size_t *len,
+ uint32_t **types)
+{
+ size_t i;
+
+ *types = calloc(p->pac->numbuffers, sizeof(*types));
+ if (*types == NULL) {
+ *len = 0;
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ for (i = 0; i < p->pac->numbuffers; i++)
+ (*types)[i] = p->pac->buffers[i].type;
+ *len = p->pac->numbuffers;
+
+ return 0;
+}
+
+/*
+ *
+ */
+
+void
+krb5_pac_free(krb5_context context, krb5_pac pac)
+{
+ krb5_data_free(&pac->data);
+ free(pac->pac);
+ free(pac);
+}
+
+/*
+ *
+ */
+
+static krb5_error_code
+verify_checksum(krb5_context context,
+ const struct PAC_INFO_BUFFER *sig,
+ const krb5_data *data,
+ void *ptr, size_t len,
+ const krb5_keyblock *key)
+{
+ krb5_crypto crypto = NULL;
+ krb5_storage *sp = NULL;
+ uint32_t type;
+ krb5_error_code ret;
+ Checksum cksum;
+
+ memset(&cksum, 0, sizeof(cksum));
+
+ sp = krb5_storage_from_mem((char *)data->data + sig->offset_lo,
+ sig->buffersize);
+ if (sp == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
+
+ CHECK(ret, krb5_ret_uint32(sp, &type), out);
+ cksum.cksumtype = type;
+ cksum.checksum.length =
+ sig->buffersize - krb5_storage_seek(sp, 0, SEEK_CUR);
+ cksum.checksum.data = malloc(cksum.checksum.length);
+ if (cksum.checksum.data == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ goto out;
+ }
+ ret = krb5_storage_read(sp, cksum.checksum.data, cksum.checksum.length);
+ if (ret != cksum.checksum.length) {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret, "PAC checksum missing checksum");
+ goto out;
+ }
+
+ if (!krb5_checksum_is_keyed(context, cksum.cksumtype)) {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret, "Checksum type %d not keyed",
+ cksum.cksumtype);
+ goto out;
+ }
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret)
+ goto out;
+
+ ret = krb5_verify_checksum(context, crypto, KRB5_KU_OTHER_CKSUM,
+ ptr, len, &cksum);
+ free(cksum.checksum.data);
+ krb5_crypto_destroy(context, crypto);
+ krb5_storage_free(sp);
+
+ return ret;
+
+out:
+ if (cksum.checksum.data)
+ free(cksum.checksum.data);
+ if (sp)
+ krb5_storage_free(sp);
+ if (crypto)
+ krb5_crypto_destroy(context, crypto);
+ return ret;
+}
+
+static krb5_error_code
+create_checksum(krb5_context context,
+ const krb5_keyblock *key,
+ void *data, size_t datalen,
+ void *sig, size_t siglen)
+{
+ krb5_crypto crypto = NULL;
+ krb5_error_code ret;
+ Checksum cksum;
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret)
+ return ret;
+
+ ret = krb5_create_checksum(context, crypto, KRB5_KU_OTHER_CKSUM, 0,
+ data, datalen, &cksum);
+ krb5_crypto_destroy(context, crypto);
+ if (ret)
+ return ret;
+
+ if (cksum.checksum.length != siglen) {
+ krb5_set_error_message(context, EINVAL, "pac checksum wrong length");
+ free_Checksum(&cksum);
+ return EINVAL;
+ }
+
+ memcpy(sig, cksum.checksum.data, siglen);
+ free_Checksum(&cksum);
+
+ return 0;
+}
+
+
+/*
+ *
+ */
+
+#define NTTIME_EPOCH 0x019DB1DED53E8000LL
+
+static uint64_t
+unix2nttime(time_t unix_time)
+{
+ long long wt;
+ wt = unix_time * (uint64_t)10000000 + (uint64_t)NTTIME_EPOCH;
+ return wt;
+}
+
+static krb5_error_code
+verify_logonname(krb5_context context,
+ const struct PAC_INFO_BUFFER *logon_name,
+ const krb5_data *data,
+ time_t authtime,
+ krb5_const_principal principal)
+{
+ krb5_error_code ret;
+ krb5_principal p2;
+ uint32_t time1, time2;
+ krb5_storage *sp;
+ uint16_t len;
+ char *s;
+
+ sp = krb5_storage_from_readonly_mem((const char *)data->data + logon_name->offset_lo,
+ logon_name->buffersize);
+ if (sp == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+
+ krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
+
+ CHECK(ret, krb5_ret_uint32(sp, &time1), out);
+ CHECK(ret, krb5_ret_uint32(sp, &time2), out);
+
+ {
+ uint64_t t1, t2;
+ t1 = unix2nttime(authtime);
+ t2 = ((uint64_t)time2 << 32) | time1;
+ if (t1 != t2) {
+ krb5_storage_free(sp);
+ krb5_set_error_message(context, EINVAL, "PAC timestamp mismatch");
+ return EINVAL;
+ }
+ }
+ CHECK(ret, krb5_ret_uint16(sp, &len), out);
+ if (len == 0) {
+ krb5_storage_free(sp);
+ krb5_set_error_message(context, EINVAL, "PAC logon name length missing");
+ return EINVAL;
+ }
+
+ s = malloc(len);
+ if (s == NULL) {
+ krb5_storage_free(sp);
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ ret = krb5_storage_read(sp, s, len);
+ if (ret != len) {
+ krb5_storage_free(sp);
+ krb5_set_error_message(context, EINVAL, "Failed to read PAC logon name");
+ return EINVAL;
+ }
+ krb5_storage_free(sp);
+ {
+ size_t ucs2len = len / 2;
+ uint16_t *ucs2;
+ size_t u8len;
+ unsigned int flags = WIND_RW_LE;
+
+ ucs2 = malloc(sizeof(ucs2[0]) * ucs2len);
+ if (ucs2 == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ ret = wind_ucs2read(s, len, &flags, ucs2, &ucs2len);
+ free(s);
+ if (ret) {
+ free(ucs2);
+ krb5_set_error_message(context, ret, "Failed to convert string to UCS-2");
+ return ret;
+ }
+ ret = wind_ucs2utf8_length(ucs2, ucs2len, &u8len);
+ if (ret) {
+ free(ucs2);
+ krb5_set_error_message(context, ret, "Failed to count length of UCS-2 string");
+ return ret;
+ }
+ u8len += 1; /* Add space for NUL */
+ s = malloc(u8len);
+ if (s == NULL) {
+ free(ucs2);
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ ret = wind_ucs2utf8(ucs2, ucs2len, s, &u8len);
+ free(ucs2);
+ if (ret) {
+ krb5_set_error_message(context, ret, "Failed to convert to UTF-8");
+ return ret;
+ }
+ }
+ ret = krb5_parse_name_flags(context, s, KRB5_PRINCIPAL_PARSE_NO_REALM, &p2);
+ free(s);
+ if (ret)
+ return ret;
+
+ if (krb5_principal_compare_any_realm(context, principal, p2) != TRUE) {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret, "PAC logon name mismatch");
+ }
+ krb5_free_principal(context, p2);
+ return ret;
+out:
+ return ret;
+}
+
+/*
+ *
+ */
+
+static krb5_error_code
+build_logon_name(krb5_context context,
+ time_t authtime,
+ krb5_const_principal principal,
+ krb5_data *logon)
+{
+ krb5_error_code ret;
+ krb5_storage *sp;
+ uint64_t t;
+ char *s, *s2;
+ size_t i, len;
+
+ t = unix2nttime(authtime);
+
+ krb5_data_zero(logon);
+
+ sp = krb5_storage_emem();
+ if (sp == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
+
+ CHECK(ret, krb5_store_uint32(sp, t & 0xffffffff), out);
+ CHECK(ret, krb5_store_uint32(sp, t >> 32), out);
+
+ ret = krb5_unparse_name_flags(context, principal,
+ KRB5_PRINCIPAL_UNPARSE_NO_REALM, &s);
+ if (ret)
+ goto out;
+
+ len = strlen(s);
+
+ CHECK(ret, krb5_store_uint16(sp, len * 2), out);
+
+#if 1 /* cheat for now */
+ s2 = malloc(len * 2);
+ if (s2 == NULL) {
+ ret = ENOMEM;
+ free(s);
+ goto out;
+ }
+ for (i = 0; i < len; i++) {
+ s2[i * 2] = s[i];
+ s2[i * 2 + 1] = 0;
+ }
+ free(s);
+#else
+ /* write libwind code here */
+#endif
+
+ ret = krb5_storage_write(sp, s2, len * 2);
+ free(s2);
+ if (ret != len * 2) {
+ ret = ENOMEM;
+ goto out;
+ }
+ ret = krb5_storage_to_data(sp, logon);
+ if (ret)
+ goto out;
+ krb5_storage_free(sp);
+
+ return 0;
+out:
+ krb5_storage_free(sp);
+ return ret;
+}
+
+
+/**
+ * Verify the PAC.
+ *
+ * @param context Kerberos 5 context.
+ * @param pac the pac structure returned by krb5_pac_parse().
+ * @param authtime The time of the ticket the PAC belongs to.
+ * @param principal the principal to verify.
+ * @param server The service key, most always be given.
+ * @param privsvr The KDC key, may be given.
+
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5_pac
+ */
+
+krb5_error_code
+krb5_pac_verify(krb5_context context,
+ const krb5_pac pac,
+ time_t authtime,
+ krb5_const_principal principal,
+ const krb5_keyblock *server,
+ const krb5_keyblock *privsvr)
+{
+ krb5_error_code ret;
+
+ if (pac->server_checksum == NULL) {
+ krb5_set_error_message(context, EINVAL, "PAC missing server checksum");
+ return EINVAL;
+ }
+ if (pac->privsvr_checksum == NULL) {
+ krb5_set_error_message(context, EINVAL, "PAC missing kdc checksum");
+ return EINVAL;
+ }
+ if (pac->logon_name == NULL) {
+ krb5_set_error_message(context, EINVAL, "PAC missing logon name");
+ return EINVAL;
+ }
+
+ ret = verify_logonname(context,
+ pac->logon_name,
+ &pac->data,
+ authtime,
+ principal);
+ if (ret)
+ return ret;
+
+ /*
+ * in the service case, clean out data option of the privsvr and
+ * server checksum before checking the checksum.
+ */
+ {
+ krb5_data *copy;
+
+ ret = krb5_copy_data(context, &pac->data, &copy);
+ if (ret)
+ return ret;
+
+ if (pac->server_checksum->buffersize < 4)
+ return EINVAL;
+ if (pac->privsvr_checksum->buffersize < 4)
+ return EINVAL;
+
+ memset((char *)copy->data + pac->server_checksum->offset_lo + 4,
+ 0,
+ pac->server_checksum->buffersize - 4);
+
+ memset((char *)copy->data + pac->privsvr_checksum->offset_lo + 4,
+ 0,
+ pac->privsvr_checksum->buffersize - 4);
+
+ ret = verify_checksum(context,
+ pac->server_checksum,
+ &pac->data,
+ copy->data,
+ copy->length,
+ server);
+ krb5_free_data(context, copy);
+ if (ret)
+ return ret;
+ }
+ if (privsvr) {
+ /* The priv checksum covers the server checksum */
+ ret = verify_checksum(context,
+ pac->privsvr_checksum,
+ &pac->data,
+ (char *)pac->data.data
+ + pac->server_checksum->offset_lo + 4,
+ pac->server_checksum->buffersize - 4,
+ privsvr);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ *
+ */
+
+static krb5_error_code
+fill_zeros(krb5_context context, krb5_storage *sp, size_t len)
+{
+ ssize_t sret;
+ size_t l;
+
+ while (len) {
+ l = len;
+ if (l > sizeof(zeros))
+ l = sizeof(zeros);
+ sret = krb5_storage_write(sp, zeros, l);
+ if (sret <= 0) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ len -= sret;
+ }
+ return 0;
+}
+
+static krb5_error_code
+pac_checksum(krb5_context context,
+ const krb5_keyblock *key,
+ uint32_t *cksumtype,
+ size_t *cksumsize)
+{
+ krb5_cksumtype cktype;
+ krb5_error_code ret;
+ krb5_crypto crypto = NULL;
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret)
+ return ret;
+
+ ret = krb5_crypto_get_checksum_type(context, crypto, &cktype);
+ krb5_crypto_destroy(context, crypto);
+ if (ret)
+ return ret;
+
+ if (krb5_checksum_is_keyed(context, cktype) == FALSE) {
+ krb5_set_error_message(context, EINVAL, "PAC checksum type is not keyed");
+ return EINVAL;
+ }
+
+ ret = krb5_checksumsize(context, cktype, cksumsize);
+ if (ret)
+ return ret;
+
+ *cksumtype = (uint32_t)cktype;
+
+ return 0;
+}
+
+krb5_error_code
+_krb5_pac_sign(krb5_context context,
+ krb5_pac p,
+ time_t authtime,
+ krb5_principal principal,
+ const krb5_keyblock *server_key,
+ const krb5_keyblock *priv_key,
+ krb5_data *data)
+{
+ krb5_error_code ret;
+ krb5_storage *sp = NULL, *spdata = NULL;
+ uint32_t end;
+ size_t server_size, priv_size;
+ uint32_t server_offset = 0, priv_offset = 0;
+ uint32_t server_cksumtype = 0, priv_cksumtype = 0;
+ int i, num = 0;
+ krb5_data logon, d;
+
+ krb5_data_zero(&logon);
+
+ if (p->logon_name == NULL)
+ num++;
+ if (p->server_checksum == NULL)
+ num++;
+ if (p->privsvr_checksum == NULL)
+ num++;
+
+ if (num) {
+ void *ptr;
+
+ ptr = realloc(p->pac, sizeof(*p->pac) + (sizeof(p->pac->buffers[0]) * (p->pac->numbuffers + num - 1)));
+ if (ptr == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ p->pac = ptr;
+
+ if (p->logon_name == NULL) {
+ p->logon_name = &p->pac->buffers[p->pac->numbuffers++];
+ memset(p->logon_name, 0, sizeof(*p->logon_name));
+ p->logon_name->type = PAC_LOGON_NAME;
+ }
+ if (p->server_checksum == NULL) {
+ p->server_checksum = &p->pac->buffers[p->pac->numbuffers++];
+ memset(p->server_checksum, 0, sizeof(*p->server_checksum));
+ p->server_checksum->type = PAC_SERVER_CHECKSUM;
+ }
+ if (p->privsvr_checksum == NULL) {
+ p->privsvr_checksum = &p->pac->buffers[p->pac->numbuffers++];
+ memset(p->privsvr_checksum, 0, sizeof(*p->privsvr_checksum));
+ p->privsvr_checksum->type = PAC_PRIVSVR_CHECKSUM;
+ }
+ }
+
+ /* Calculate LOGON NAME */
+ ret = build_logon_name(context, authtime, principal, &logon);
+ if (ret)
+ goto out;
+
+ /* Set lengths for checksum */
+ ret = pac_checksum(context, server_key, &server_cksumtype, &server_size);
+ if (ret)
+ goto out;
+ ret = pac_checksum(context, priv_key, &priv_cksumtype, &priv_size);
+ if (ret)
+ goto out;
+
+ /* Encode PAC */
+ sp = krb5_storage_emem();
+ if (sp == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
+
+ spdata = krb5_storage_emem();
+ if (spdata == NULL) {
+ krb5_storage_free(sp);
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ krb5_storage_set_flags(spdata, KRB5_STORAGE_BYTEORDER_LE);
+
+ CHECK(ret, krb5_store_uint32(sp, p->pac->numbuffers), out);
+ CHECK(ret, krb5_store_uint32(sp, p->pac->version), out);
+
+ end = PACTYPE_SIZE + (PAC_INFO_BUFFER_SIZE * p->pac->numbuffers);
+
+ for (i = 0; i < p->pac->numbuffers; i++) {
+ uint32_t len;
+ size_t sret;
+ void *ptr = NULL;
+
+ /* store data */
+
+ if (p->pac->buffers[i].type == PAC_SERVER_CHECKSUM) {
+ len = server_size + 4;
+ server_offset = end + 4;
+ CHECK(ret, krb5_store_uint32(spdata, server_cksumtype), out);
+ CHECK(ret, fill_zeros(context, spdata, server_size), out);
+ } else if (p->pac->buffers[i].type == PAC_PRIVSVR_CHECKSUM) {
+ len = priv_size + 4;
+ priv_offset = end + 4;
+ CHECK(ret, krb5_store_uint32(spdata, priv_cksumtype), out);
+ CHECK(ret, fill_zeros(context, spdata, priv_size), out);
+ } else if (p->pac->buffers[i].type == PAC_LOGON_NAME) {
+ len = krb5_storage_write(spdata, logon.data, logon.length);
+ if (logon.length != len) {
+ ret = EINVAL;
+ goto out;
+ }
+ } else {
+ len = p->pac->buffers[i].buffersize;
+ ptr = (char *)p->data.data + p->pac->buffers[i].offset_lo;
+
+ sret = krb5_storage_write(spdata, ptr, len);
+ if (sret != len) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ goto out;
+ }
+ /* XXX if not aligned, fill_zeros */
+ }
+
+ /* write header */
+ CHECK(ret, krb5_store_uint32(sp, p->pac->buffers[i].type), out);
+ CHECK(ret, krb5_store_uint32(sp, len), out);
+ CHECK(ret, krb5_store_uint32(sp, end), out);
+ CHECK(ret, krb5_store_uint32(sp, 0), out);
+
+ /* advance data endpointer and align */
+ {
+ int32_t e;
+
+ end += len;
+ e = ((end + PAC_ALIGNMENT - 1) / PAC_ALIGNMENT) * PAC_ALIGNMENT;
+ if (end != e) {
+ CHECK(ret, fill_zeros(context, spdata, e - end), out);
+ }
+ end = e;
+ }
+
+ }
+
+ /* assert (server_offset != 0 && priv_offset != 0); */
+
+ /* export PAC */
+ ret = krb5_storage_to_data(spdata, &d);
+ if (ret) {
+ krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ goto out;
+ }
+ ret = krb5_storage_write(sp, d.data, d.length);
+ if (ret != d.length) {
+ krb5_data_free(&d);
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ goto out;
+ }
+ krb5_data_free(&d);
+
+ ret = krb5_storage_to_data(sp, &d);
+ if (ret) {
+ krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ goto out;
+ }
+
+ /* sign */
+
+ ret = create_checksum(context, server_key,
+ d.data, d.length,
+ (char *)d.data + server_offset, server_size);
+ if (ret) {
+ krb5_data_free(&d);
+ goto out;
+ }
+
+ ret = create_checksum(context, priv_key,
+ (char *)d.data + server_offset, server_size,
+ (char *)d.data + priv_offset, priv_size);
+ if (ret) {
+ krb5_data_free(&d);
+ goto out;
+ }
+
+ /* done */
+ *data = d;
+
+ krb5_data_free(&logon);
+ krb5_storage_free(sp);
+ krb5_storage_free(spdata);
+
+ return 0;
+out:
+ krb5_data_free(&logon);
+ if (sp)
+ krb5_storage_free(sp);
+ if (spdata)
+ krb5_storage_free(spdata);
+ return ret;
+}
diff --git a/source4/heimdal/lib/krb5/padata.c b/source4/heimdal/lib/krb5/padata.c
new file mode 100644
index 0000000000..022260e709
--- /dev/null
+++ b/source4/heimdal/lib/krb5/padata.c
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 1997 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+PA_DATA *
+krb5_find_padata(PA_DATA *val, unsigned len, int type, int *idx)
+{
+ for(; *idx < len; (*idx)++)
+ if(val[*idx].padata_type == type)
+ return val + *idx;
+ return NULL;
+}
+
+int KRB5_LIB_FUNCTION
+krb5_padata_add(krb5_context context, METHOD_DATA *md,
+ int type, void *buf, size_t len)
+{
+ PA_DATA *pa;
+
+ pa = realloc (md->val, (md->len + 1) * sizeof(*md->val));
+ if (pa == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ md->val = pa;
+
+ pa[md->len].padata_type = type;
+ pa[md->len].padata_value.length = len;
+ pa[md->len].padata_value.data = buf;
+ md->len++;
+
+ return 0;
+}
diff --git a/source4/heimdal/lib/krb5/pkinit.c b/source4/heimdal/lib/krb5/pkinit.c
new file mode 100644
index 0000000000..de5e90a68e
--- /dev/null
+++ b/source4/heimdal/lib/krb5/pkinit.c
@@ -0,0 +1,2154 @@
+/*
+ * Copyright (c) 2003 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+struct krb5_dh_moduli {
+ char *name;
+ unsigned long bits;
+ heim_integer p;
+ heim_integer g;
+ heim_integer q;
+};
+
+#ifdef PKINIT
+
+#include <cms_asn1.h>
+#include <pkcs8_asn1.h>
+#include <pkcs9_asn1.h>
+#include <pkcs12_asn1.h>
+#include <pkinit_asn1.h>
+#include <asn1_err.h>
+
+#include <der.h>
+
+struct krb5_pk_cert {
+ hx509_cert cert;
+};
+
+struct krb5_pk_init_ctx_data {
+ struct krb5_pk_identity *id;
+ DH *dh;
+ krb5_data *clientDHNonce;
+ struct krb5_dh_moduli **m;
+ hx509_peer_info peer;
+ enum krb5_pk_type type;
+ unsigned int require_binding:1;
+ unsigned int require_eku:1;
+ unsigned int require_krbtgt_otherName:1;
+ unsigned int require_hostname_match:1;
+ unsigned int trustedCertifiers:1;
+};
+
+static void
+pk_copy_error(krb5_context context,
+ hx509_context hx509ctx,
+ int hxret,
+ const char *fmt,
+ ...)
+ __attribute__ ((format (printf, 4, 5)));
+
+/*
+ *
+ */
+
+void KRB5_LIB_FUNCTION
+_krb5_pk_cert_free(struct krb5_pk_cert *cert)
+{
+ if (cert->cert) {
+ hx509_cert_free(cert->cert);
+ }
+ free(cert);
+}
+
+static krb5_error_code
+BN_to_integer(krb5_context context, BIGNUM *bn, heim_integer *integer)
+{
+ integer->length = BN_num_bytes(bn);
+ integer->data = malloc(integer->length);
+ if (integer->data == NULL) {
+ krb5_clear_error_message(context);
+ return ENOMEM;
+ }
+ BN_bn2bin(bn, integer->data);
+ integer->negative = BN_is_negative(bn);
+ return 0;
+}
+
+static BIGNUM *
+integer_to_BN(krb5_context context, const char *field, const heim_integer *f)
+{
+ BIGNUM *bn;
+
+ bn = BN_bin2bn((const unsigned char *)f->data, f->length, NULL);
+ if (bn == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ N_("PKINIT: parsing BN failed %s", ""), field);
+ return NULL;
+ }
+ BN_set_negative(bn, f->negative);
+ return bn;
+}
+
+struct certfind {
+ const char *type;
+ const heim_oid *oid;
+};
+
+/*
+ * Try searchin the key by to use by first looking for for PK-INIT
+ * EKU, then the Microsoft smart card EKU and last, no special EKU at all.
+ */
+
+static krb5_error_code
+find_cert(krb5_context context, struct krb5_pk_identity *id,
+ hx509_query *q, hx509_cert *cert)
+{
+ struct certfind cf[3] = {
+ { "PKINIT EKU" },
+ { "MS EKU" },
+ { "no" }
+ };
+ int i, ret;
+
+ cf[0].oid = oid_id_pkekuoid();
+ cf[1].oid = oid_id_pkinit_ms_eku();
+ cf[2].oid = NULL;
+
+ for (i = 0; i < sizeof(cf)/sizeof(cf[0]); i++) {
+ ret = hx509_query_match_eku(q, cf[i].oid);
+ if (ret) {
+ pk_copy_error(context, id->hx509ctx, ret,
+ "Failed setting %s OID", cf[i].type);
+ return ret;
+ }
+
+ ret = hx509_certs_find(id->hx509ctx, id->certs, q, cert);
+ if (ret == 0)
+ break;
+ pk_copy_error(context, id->hx509ctx, ret,
+ "Failed cert for finding %s OID", cf[i].type);
+ }
+ return ret;
+}
+
+
+static krb5_error_code
+create_signature(krb5_context context,
+ const heim_oid *eContentType,
+ krb5_data *eContent,
+ struct krb5_pk_identity *id,
+ hx509_peer_info peer,
+ krb5_data *sd_data)
+{
+ hx509_cert cert = NULL;
+ hx509_query *q = NULL;
+ int ret;
+
+ ret = hx509_query_alloc(id->hx509ctx, &q);
+ if (ret) {
+ pk_copy_error(context, id->hx509ctx, ret,
+ "Allocate query to find signing certificate");
+ return ret;
+ }
+
+ hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
+ hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
+
+ ret = find_cert(context, id, q, &cert);
+ hx509_query_free(id->hx509ctx, q);
+ if (ret)
+ return ret;
+
+ ret = hx509_cms_create_signed_1(id->hx509ctx,
+ 0,
+ eContentType,
+ eContent->data,
+ eContent->length,
+ NULL,
+ cert,
+ peer,
+ NULL,
+ id->certs,
+ sd_data);
+ hx509_cert_free(cert);
+ if (ret) {
+ pk_copy_error(context, id->hx509ctx, ret,
+ "Create CMS signedData");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int
+cert2epi(hx509_context context, void *ctx, hx509_cert c)
+{
+ ExternalPrincipalIdentifiers *ids = ctx;
+ ExternalPrincipalIdentifier id;
+ hx509_name subject = NULL;
+ void *p;
+ int ret;
+
+ memset(&id, 0, sizeof(id));
+
+ ret = hx509_cert_get_subject(c, &subject);
+ if (ret)
+ return ret;
+
+ if (hx509_name_is_null_p(subject) != 0) {
+
+ id.subjectName = calloc(1, sizeof(*id.subjectName));
+ if (id.subjectName == NULL) {
+ hx509_name_free(&subject);
+ free_ExternalPrincipalIdentifier(&id);
+ return ENOMEM;
+ }
+
+ ret = hx509_name_binary(subject, id.subjectName);
+ if (ret) {
+ hx509_name_free(&subject);
+ free_ExternalPrincipalIdentifier(&id);
+ return ret;
+ }
+ }
+ hx509_name_free(&subject);
+
+
+ id.issuerAndSerialNumber = calloc(1, sizeof(*id.issuerAndSerialNumber));
+ if (id.issuerAndSerialNumber == NULL) {
+ free_ExternalPrincipalIdentifier(&id);
+ return ENOMEM;
+ }
+
+ {
+ IssuerAndSerialNumber iasn;
+ hx509_name issuer;
+ size_t size;
+
+ memset(&iasn, 0, sizeof(iasn));
+
+ ret = hx509_cert_get_issuer(c, &issuer);
+ if (ret) {
+ free_ExternalPrincipalIdentifier(&id);
+ return ret;
+ }
+
+ ret = hx509_name_to_Name(issuer, &iasn.issuer);
+ hx509_name_free(&issuer);
+ if (ret) {
+ free_ExternalPrincipalIdentifier(&id);
+ return ret;
+ }
+
+ ret = hx509_cert_get_serialnumber(c, &iasn.serialNumber);
+ if (ret) {
+ free_IssuerAndSerialNumber(&iasn);
+ free_ExternalPrincipalIdentifier(&id);
+ return ret;
+ }
+
+ ASN1_MALLOC_ENCODE(IssuerAndSerialNumber,
+ id.issuerAndSerialNumber->data,
+ id.issuerAndSerialNumber->length,
+ &iasn, &size, ret);
+ free_IssuerAndSerialNumber(&iasn);
+ if (ret)
+ return ret;
+ if (id.issuerAndSerialNumber->length != size)
+ abort();
+ }
+
+ id.subjectKeyIdentifier = NULL;
+
+ p = realloc(ids->val, sizeof(ids->val[0]) * (ids->len + 1));
+ if (p == NULL) {
+ free_ExternalPrincipalIdentifier(&id);
+ return ENOMEM;
+ }
+
+ ids->val = p;
+ ids->val[ids->len] = id;
+ ids->len++;
+
+ return 0;
+}
+
+static krb5_error_code
+build_edi(krb5_context context,
+ hx509_context hx509ctx,
+ hx509_certs certs,
+ ExternalPrincipalIdentifiers *ids)
+{
+ return hx509_certs_iter(hx509ctx, certs, cert2epi, ids);
+}
+
+static krb5_error_code
+build_auth_pack(krb5_context context,
+ unsigned nonce,
+ krb5_pk_init_ctx ctx,
+ DH *dh,
+ const KDC_REQ_BODY *body,
+ AuthPack *a)
+{
+ size_t buf_size, len;
+ krb5_error_code ret;
+ void *buf;
+ krb5_timestamp sec;
+ int32_t usec;
+ Checksum checksum;
+
+ krb5_clear_error_message(context);
+
+ memset(&checksum, 0, sizeof(checksum));
+
+ krb5_us_timeofday(context, &sec, &usec);
+ a->pkAuthenticator.ctime = sec;
+ a->pkAuthenticator.nonce = nonce;
+
+ ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, body, &len, ret);
+ if (ret)
+ return ret;
+ if (buf_size != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+
+ ret = krb5_create_checksum(context,
+ NULL,
+ 0,
+ CKSUMTYPE_SHA1,
+ buf,
+ len,
+ &checksum);
+ free(buf);
+ if (ret)
+ return ret;
+
+ ALLOC(a->pkAuthenticator.paChecksum, 1);
+ if (a->pkAuthenticator.paChecksum == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+
+ ret = krb5_data_copy(a->pkAuthenticator.paChecksum,
+ checksum.checksum.data, checksum.checksum.length);
+ free_Checksum(&checksum);
+ if (ret)
+ return ret;
+
+ if (dh) {
+ DomainParameters dp;
+ heim_integer dh_pub_key;
+ krb5_data dhbuf;
+ size_t size;
+
+ if (1 /* support_cached_dh */) {
+ ALLOC(a->clientDHNonce, 1);
+ if (a->clientDHNonce == NULL) {
+ krb5_clear_error_message(context);
+ return ENOMEM;
+ }
+ ret = krb5_data_alloc(a->clientDHNonce, 40);
+ if (a->clientDHNonce == NULL) {
+ krb5_clear_error_message(context);
+ return ret;
+ }
+ memset(a->clientDHNonce->data, 0, a->clientDHNonce->length);
+ ret = krb5_copy_data(context, a->clientDHNonce,
+ &ctx->clientDHNonce);
+ if (ret)
+ return ret;
+ }
+
+ ALLOC(a->clientPublicValue, 1);
+ if (a->clientPublicValue == NULL)
+ return ENOMEM;
+ ret = der_copy_oid(oid_id_dhpublicnumber(),
+ &a->clientPublicValue->algorithm.algorithm);
+ if (ret)
+ return ret;
+
+ memset(&dp, 0, sizeof(dp));
+
+ ret = BN_to_integer(context, dh->p, &dp.p);
+ if (ret) {
+ free_DomainParameters(&dp);
+ return ret;
+ }
+ ret = BN_to_integer(context, dh->g, &dp.g);
+ if (ret) {
+ free_DomainParameters(&dp);
+ return ret;
+ }
+ ret = BN_to_integer(context, dh->q, &dp.q);
+ if (ret) {
+ free_DomainParameters(&dp);
+ return ret;
+ }
+ dp.j = NULL;
+ dp.validationParms = NULL;
+
+ a->clientPublicValue->algorithm.parameters =
+ malloc(sizeof(*a->clientPublicValue->algorithm.parameters));
+ if (a->clientPublicValue->algorithm.parameters == NULL) {
+ free_DomainParameters(&dp);
+ return ret;
+ }
+
+ ASN1_MALLOC_ENCODE(DomainParameters,
+ a->clientPublicValue->algorithm.parameters->data,
+ a->clientPublicValue->algorithm.parameters->length,
+ &dp, &size, ret);
+ free_DomainParameters(&dp);
+ if (ret)
+ return ret;
+ if (size != a->clientPublicValue->algorithm.parameters->length)
+ krb5_abortx(context, "Internal ASN1 encoder error");
+
+ ret = BN_to_integer(context, dh->pub_key, &dh_pub_key);
+ if (ret)
+ return ret;
+
+ ASN1_MALLOC_ENCODE(DHPublicKey, dhbuf.data, dhbuf.length,
+ &dh_pub_key, &size, ret);
+ der_free_heim_integer(&dh_pub_key);
+ if (ret)
+ return ret;
+ if (size != dhbuf.length)
+ krb5_abortx(context, "asn1 internal error");
+
+ a->clientPublicValue->subjectPublicKey.length = dhbuf.length * 8;
+ a->clientPublicValue->subjectPublicKey.data = dhbuf.data;
+ }
+
+ {
+ a->supportedCMSTypes = calloc(1, sizeof(*a->supportedCMSTypes));
+ if (a->supportedCMSTypes == NULL)
+ return ENOMEM;
+
+ ret = hx509_crypto_available(ctx->id->hx509ctx, HX509_SELECT_ALL, NULL,
+ &a->supportedCMSTypes->val,
+ &a->supportedCMSTypes->len);
+ if (ret)
+ return ret;
+ }
+
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+_krb5_pk_mk_ContentInfo(krb5_context context,
+ const krb5_data *buf,
+ const heim_oid *oid,
+ struct ContentInfo *content_info)
+{
+ krb5_error_code ret;
+
+ ret = der_copy_oid(oid, &content_info->contentType);
+ if (ret)
+ return ret;
+ ALLOC(content_info->content, 1);
+ if (content_info->content == NULL)
+ return ENOMEM;
+ content_info->content->data = malloc(buf->length);
+ if (content_info->content->data == NULL)
+ return ENOMEM;
+ memcpy(content_info->content->data, buf->data, buf->length);
+ content_info->content->length = buf->length;
+ return 0;
+}
+
+static krb5_error_code
+pk_mk_padata(krb5_context context,
+ krb5_pk_init_ctx ctx,
+ const KDC_REQ_BODY *req_body,
+ unsigned nonce,
+ METHOD_DATA *md)
+{
+ struct ContentInfo content_info;
+ krb5_error_code ret;
+ const heim_oid *oid;
+ size_t size;
+ krb5_data buf, sd_buf;
+ int pa_type;
+
+ krb5_data_zero(&buf);
+ krb5_data_zero(&sd_buf);
+ memset(&content_info, 0, sizeof(content_info));
+
+ if (ctx->type == PKINIT_WIN2K) {
+ AuthPack_Win2k ap;
+ krb5_timestamp sec;
+ int32_t usec;
+
+ memset(&ap, 0, sizeof(ap));
+
+ /* fill in PKAuthenticator */
+ ret = copy_PrincipalName(req_body->sname, &ap.pkAuthenticator.kdcName);
+ if (ret) {
+ free_AuthPack_Win2k(&ap);
+ krb5_clear_error_message(context);
+ goto out;
+ }
+ ret = copy_Realm(&req_body->realm, &ap.pkAuthenticator.kdcRealm);
+ if (ret) {
+ free_AuthPack_Win2k(&ap);
+ krb5_clear_error_message(context);
+ goto out;
+ }
+
+ krb5_us_timeofday(context, &sec, &usec);
+ ap.pkAuthenticator.ctime = sec;
+ ap.pkAuthenticator.cusec = usec;
+ ap.pkAuthenticator.nonce = nonce;
+
+ ASN1_MALLOC_ENCODE(AuthPack_Win2k, buf.data, buf.length,
+ &ap, &size, ret);
+ free_AuthPack_Win2k(&ap);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("Failed encoding AuthPackWin: %d", ""),
+ (int)ret);
+ goto out;
+ }
+ if (buf.length != size)
+ krb5_abortx(context, "internal ASN1 encoder error");
+
+ oid = oid_id_pkcs7_data();
+ } else if (ctx->type == PKINIT_27) {
+ AuthPack ap;
+
+ memset(&ap, 0, sizeof(ap));
+
+ ret = build_auth_pack(context, nonce, ctx, ctx->dh, req_body, &ap);
+ if (ret) {
+ free_AuthPack(&ap);
+ goto out;
+ }
+
+ ASN1_MALLOC_ENCODE(AuthPack, buf.data, buf.length, &ap, &size, ret);
+ free_AuthPack(&ap);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("Failed encoding AuthPack: %d", ""),
+ (int)ret);
+ goto out;
+ }
+ if (buf.length != size)
+ krb5_abortx(context, "internal ASN1 encoder error");
+
+ oid = oid_id_pkauthdata();
+ } else
+ krb5_abortx(context, "internal pkinit error");
+
+ ret = create_signature(context, oid, &buf, ctx->id,
+ ctx->peer, &sd_buf);
+ krb5_data_free(&buf);
+ if (ret)
+ goto out;
+
+ ret = hx509_cms_wrap_ContentInfo(oid_id_pkcs7_signedData(), &sd_buf, &buf);
+ krb5_data_free(&sd_buf);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("ContentInfo wrapping of signedData failed",""));
+ goto out;
+ }
+
+ if (ctx->type == PKINIT_WIN2K) {
+ PA_PK_AS_REQ_Win2k winreq;
+
+ pa_type = KRB5_PADATA_PK_AS_REQ_WIN;
+
+ memset(&winreq, 0, sizeof(winreq));
+
+ winreq.signed_auth_pack = buf;
+
+ ASN1_MALLOC_ENCODE(PA_PK_AS_REQ_Win2k, buf.data, buf.length,
+ &winreq, &size, ret);
+ free_PA_PK_AS_REQ_Win2k(&winreq);
+
+ } else if (ctx->type == PKINIT_27) {
+ PA_PK_AS_REQ req;
+
+ pa_type = KRB5_PADATA_PK_AS_REQ;
+
+ memset(&req, 0, sizeof(req));
+ req.signedAuthPack = buf;
+
+ if (ctx->trustedCertifiers) {
+
+ req.trustedCertifiers = calloc(1, sizeof(*req.trustedCertifiers));
+ if (req.trustedCertifiers == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret,
+ N_("malloc: out of memory", ""));
+ free_PA_PK_AS_REQ(&req);
+ goto out;
+ }
+ ret = build_edi(context, ctx->id->hx509ctx,
+ ctx->id->anchors, req.trustedCertifiers);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("pk-init: failed to build "
+ "trustedCertifiers", ""));
+ free_PA_PK_AS_REQ(&req);
+ goto out;
+ }
+ }
+ req.kdcPkId = NULL;
+
+ ASN1_MALLOC_ENCODE(PA_PK_AS_REQ, buf.data, buf.length,
+ &req, &size, ret);
+
+ free_PA_PK_AS_REQ(&req);
+
+ } else
+ krb5_abortx(context, "internal pkinit error");
+ if (ret) {
+ krb5_set_error_message(context, ret, "PA-PK-AS-REQ %d", (int)ret);
+ goto out;
+ }
+ if (buf.length != size)
+ krb5_abortx(context, "Internal ASN1 encoder error");
+
+ ret = krb5_padata_add(context, md, pa_type, buf.data, buf.length);
+ if (ret)
+ free(buf.data);
+
+ if (ret == 0 && ctx->type == PKINIT_WIN2K)
+ krb5_padata_add(context, md, KRB5_PADATA_PK_AS_09_BINDING, NULL, 0);
+
+ out:
+ free_ContentInfo(&content_info);
+
+ return ret;
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+_krb5_pk_mk_padata(krb5_context context,
+ void *c,
+ const KDC_REQ_BODY *req_body,
+ unsigned nonce,
+ METHOD_DATA *md)
+{
+ krb5_pk_init_ctx ctx = c;
+ int win2k_compat;
+
+ win2k_compat = krb5_config_get_bool_default(context, NULL,
+ FALSE,
+ "realms",
+ req_body->realm,
+ "pkinit_win2k",
+ NULL);
+
+ if (win2k_compat) {
+ ctx->require_binding =
+ krb5_config_get_bool_default(context, NULL,
+ FALSE,
+ "realms",
+ req_body->realm,
+ "pkinit_win2k_require_binding",
+ NULL);
+ ctx->type = PKINIT_WIN2K;
+ } else
+ ctx->type = PKINIT_27;
+
+ ctx->require_eku =
+ krb5_config_get_bool_default(context, NULL,
+ TRUE,
+ "realms",
+ req_body->realm,
+ "pkinit_require_eku",
+ NULL);
+ ctx->require_krbtgt_otherName =
+ krb5_config_get_bool_default(context, NULL,
+ TRUE,
+ "realms",
+ req_body->realm,
+ "pkinit_require_krbtgt_otherName",
+ NULL);
+
+ ctx->require_hostname_match =
+ krb5_config_get_bool_default(context, NULL,
+ FALSE,
+ "realms",
+ req_body->realm,
+ "pkinit_require_hostname_match",
+ NULL);
+
+ ctx->trustedCertifiers =
+ krb5_config_get_bool_default(context, NULL,
+ TRUE,
+ "realms",
+ req_body->realm,
+ "pkinit_trustedCertifiers",
+ NULL);
+
+ return pk_mk_padata(context, ctx, req_body, nonce, md);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+_krb5_pk_verify_sign(krb5_context context,
+ const void *data,
+ size_t length,
+ struct krb5_pk_identity *id,
+ heim_oid *contentType,
+ krb5_data *content,
+ struct krb5_pk_cert **signer)
+{
+ hx509_certs signer_certs;
+ int ret;
+
+ *signer = NULL;
+
+ ret = hx509_cms_verify_signed(id->hx509ctx,
+ id->verify_ctx,
+ data,
+ length,
+ NULL,
+ id->certpool,
+ contentType,
+ content,
+ &signer_certs);
+ if (ret) {
+ pk_copy_error(context, id->hx509ctx, ret,
+ "CMS verify signed failed");
+ return ret;
+ }
+
+ *signer = calloc(1, sizeof(**signer));
+ if (*signer == NULL) {
+ krb5_clear_error_message(context);
+ ret = ENOMEM;
+ goto out;
+ }
+
+ ret = hx509_get_one_cert(id->hx509ctx, signer_certs, &(*signer)->cert);
+ if (ret) {
+ pk_copy_error(context, id->hx509ctx, ret,
+ "Failed to get on of the signer certs");
+ goto out;
+ }
+
+ out:
+ hx509_certs_free(&signer_certs);
+ if (ret) {
+ if (*signer) {
+ hx509_cert_free((*signer)->cert);
+ free(*signer);
+ *signer = NULL;
+ }
+ }
+
+ return ret;
+}
+
+static krb5_error_code
+get_reply_key_win(krb5_context context,
+ const krb5_data *content,
+ unsigned nonce,
+ krb5_keyblock **key)
+{
+ ReplyKeyPack_Win2k key_pack;
+ krb5_error_code ret;
+ size_t size;
+
+ ret = decode_ReplyKeyPack_Win2k(content->data,
+ content->length,
+ &key_pack,
+ &size);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("PKINIT decoding reply key failed", ""));
+ free_ReplyKeyPack_Win2k(&key_pack);
+ return ret;
+ }
+
+ if (key_pack.nonce != nonce) {
+ krb5_set_error_message(context, ret,
+ N_("PKINIT enckey nonce is wrong", ""));
+ free_ReplyKeyPack_Win2k(&key_pack);
+ return KRB5KRB_AP_ERR_MODIFIED;
+ }
+
+ *key = malloc (sizeof (**key));
+ if (*key == NULL) {
+ free_ReplyKeyPack_Win2k(&key_pack);
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+
+ ret = copy_EncryptionKey(&key_pack.replyKey, *key);
+ free_ReplyKeyPack_Win2k(&key_pack);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("PKINIT failed copying reply key", ""));
+ free(*key);
+ *key = NULL;
+ }
+
+ return ret;
+}
+
+static krb5_error_code
+get_reply_key(krb5_context context,
+ const krb5_data *content,
+ const krb5_data *req_buffer,
+ krb5_keyblock **key)
+{
+ ReplyKeyPack key_pack;
+ krb5_error_code ret;
+ size_t size;
+
+ ret = decode_ReplyKeyPack(content->data,
+ content->length,
+ &key_pack,
+ &size);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("PKINIT decoding reply key failed", ""));
+ free_ReplyKeyPack(&key_pack);
+ return ret;
+ }
+
+ {
+ krb5_crypto crypto;
+
+ /*
+ * XXX Verify kp.replyKey is a allowed enctype in the
+ * configuration file
+ */
+
+ ret = krb5_crypto_init(context, &key_pack.replyKey, 0, &crypto);
+ if (ret) {
+ free_ReplyKeyPack(&key_pack);
+ return ret;
+ }
+
+ ret = krb5_verify_checksum(context, crypto, 6,
+ req_buffer->data, req_buffer->length,
+ &key_pack.asChecksum);
+ krb5_crypto_destroy(context, crypto);
+ if (ret) {
+ free_ReplyKeyPack(&key_pack);
+ return ret;
+ }
+ }
+
+ *key = malloc (sizeof (**key));
+ if (*key == NULL) {
+ free_ReplyKeyPack(&key_pack);
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+
+ ret = copy_EncryptionKey(&key_pack.replyKey, *key);
+ free_ReplyKeyPack(&key_pack);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("PKINIT failed copying reply key", ""));
+ free(*key);
+ *key = NULL;
+ }
+
+ return ret;
+}
+
+
+static krb5_error_code
+pk_verify_host(krb5_context context,
+ const char *realm,
+ const krb5_krbhst_info *hi,
+ struct krb5_pk_init_ctx_data *ctx,
+ struct krb5_pk_cert *host)
+{
+ krb5_error_code ret = 0;
+
+ if (ctx->require_eku) {
+ ret = hx509_cert_check_eku(ctx->id->hx509ctx, host->cert,
+ oid_id_pkkdcekuoid(), 0);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("No PK-INIT KDC EKU in kdc certificate", ""));
+ return ret;
+ }
+ }
+ if (ctx->require_krbtgt_otherName) {
+ hx509_octet_string_list list;
+ int i;
+
+ ret = hx509_cert_find_subjectAltName_otherName(ctx->id->hx509ctx,
+ host->cert,
+ oid_id_pkinit_san(),
+ &list);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("Failed to find the PK-INIT "
+ "subjectAltName in the KDC "
+ "certificate", ""));
+
+ return ret;
+ }
+
+ for (i = 0; i < list.len; i++) {
+ KRB5PrincipalName r;
+
+ ret = decode_KRB5PrincipalName(list.val[i].data,
+ list.val[i].length,
+ &r,
+ NULL);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("Failed to decode the PK-INIT "
+ "subjectAltName in the "
+ "KDC certificate", ""));
+
+ break;
+ }
+
+ if (r.principalName.name_string.len != 2 ||
+ strcmp(r.principalName.name_string.val[0], KRB5_TGS_NAME) != 0 ||
+ strcmp(r.principalName.name_string.val[1], realm) != 0 ||
+ strcmp(r.realm, realm) != 0)
+ {
+ ret = KRB5_KDC_ERR_INVALID_CERTIFICATE;
+ krb5_set_error_message(context, ret,
+ N_("KDC have wrong realm name in "
+ "the certificate", ""));
+ }
+
+ free_KRB5PrincipalName(&r);
+ if (ret)
+ break;
+ }
+ hx509_free_octet_string_list(&list);
+ }
+ if (ret)
+ return ret;
+
+ if (hi) {
+ ret = hx509_verify_hostname(ctx->id->hx509ctx, host->cert,
+ ctx->require_hostname_match,
+ HX509_HN_HOSTNAME,
+ hi->hostname,
+ hi->ai->ai_addr, hi->ai->ai_addrlen);
+
+ if (ret)
+ krb5_set_error_message(context, ret,
+ N_("Address mismatch in "
+ "the KDC certificate", ""));
+ }
+ return ret;
+}
+
+static krb5_error_code
+pk_rd_pa_reply_enckey(krb5_context context,
+ int type,
+ const heim_octet_string *indata,
+ const heim_oid *dataType,
+ const char *realm,
+ krb5_pk_init_ctx ctx,
+ krb5_enctype etype,
+ const krb5_krbhst_info *hi,
+ unsigned nonce,
+ const krb5_data *req_buffer,
+ PA_DATA *pa,
+ krb5_keyblock **key)
+{
+ krb5_error_code ret;
+ struct krb5_pk_cert *host = NULL;
+ krb5_data content;
+ heim_oid contentType = { 0, NULL };
+
+ if (der_heim_oid_cmp(oid_id_pkcs7_envelopedData(), dataType)) {
+ krb5_set_error_message(context, EINVAL,
+ N_("PKINIT: Invalid content type", ""));
+ return EINVAL;
+ }
+
+ ret = hx509_cms_unenvelope(ctx->id->hx509ctx,
+ ctx->id->certs,
+ HX509_CMS_UE_DONT_REQUIRE_KU_ENCIPHERMENT,
+ indata->data,
+ indata->length,
+ NULL,
+ 0,
+ &contentType,
+ &content);
+ if (ret) {
+ pk_copy_error(context, ctx->id->hx509ctx, ret,
+ "Failed to unenvelope CMS data in PK-INIT reply");
+ return ret;
+ }
+ der_free_oid(&contentType);
+
+#if 0 /* windows LH with interesting CMS packets, leaks memory */
+ {
+ size_t ph = 1 + der_length_len (length);
+ unsigned char *ptr = malloc(length + ph);
+ size_t l;
+
+ memcpy(ptr + ph, p, length);
+
+ ret = der_put_length_and_tag (ptr + ph - 1, ph, length,
+ ASN1_C_UNIV, CONS, UT_Sequence, &l);
+ if (ret)
+ return ret;
+ ptr += ph - l;
+ length += l;
+ p = ptr;
+ }
+#endif
+
+ /* win2k uses ContentInfo */
+ if (type == PKINIT_WIN2K) {
+ heim_oid type;
+ heim_octet_string out;
+
+ ret = hx509_cms_unwrap_ContentInfo(&content, &type, &out, NULL);
+ if (der_heim_oid_cmp(&type, oid_id_pkcs7_signedData())) {
+ ret = EINVAL; /* XXX */
+ krb5_set_error_message(context, ret,
+ N_("PKINIT: Invalid content type", ""));
+ der_free_oid(&type);
+ der_free_octet_string(&out);
+ goto out;
+ }
+ der_free_oid(&type);
+ krb5_data_free(&content);
+ ret = krb5_data_copy(&content, out.data, out.length);
+ der_free_octet_string(&out);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("malloc: out of memory", ""));
+ goto out;
+ }
+ }
+
+ ret = _krb5_pk_verify_sign(context,
+ content.data,
+ content.length,
+ ctx->id,
+ &contentType,
+ &content,
+ &host);
+ if (ret)
+ goto out;
+
+ /* make sure that it is the kdc's certificate */
+ ret = pk_verify_host(context, realm, hi, ctx, host);
+ if (ret) {
+ goto out;
+ }
+
+#if 0
+ if (type == PKINIT_WIN2K) {
+ if (der_heim_oid_cmp(&contentType, oid_id_pkcs7_data()) != 0) {
+ ret = KRB5KRB_AP_ERR_MSG_TYPE;
+ krb5_set_error_message(context, ret, "PKINIT: reply key, wrong oid");
+ goto out;
+ }
+ } else {
+ if (der_heim_oid_cmp(&contentType, oid_id_pkrkeydata()) != 0) {
+ ret = KRB5KRB_AP_ERR_MSG_TYPE;
+ krb5_set_error_message(context, ret, "PKINIT: reply key, wrong oid");
+ goto out;
+ }
+ }
+#endif
+
+ switch(type) {
+ case PKINIT_WIN2K:
+ ret = get_reply_key(context, &content, req_buffer, key);
+ if (ret != 0 && ctx->require_binding == 0)
+ ret = get_reply_key_win(context, &content, nonce, key);
+ break;
+ case PKINIT_27:
+ ret = get_reply_key(context, &content, req_buffer, key);
+ break;
+ }
+ if (ret)
+ goto out;
+
+ /* XXX compare given etype with key->etype */
+
+ out:
+ if (host)
+ _krb5_pk_cert_free(host);
+ der_free_oid(&contentType);
+ krb5_data_free(&content);
+
+ return ret;
+}
+
+static krb5_error_code
+pk_rd_pa_reply_dh(krb5_context context,
+ const heim_octet_string *indata,
+ const heim_oid *dataType,
+ const char *realm,
+ krb5_pk_init_ctx ctx,
+ krb5_enctype etype,
+ const krb5_krbhst_info *hi,
+ const DHNonce *c_n,
+ const DHNonce *k_n,
+ unsigned nonce,
+ PA_DATA *pa,
+ krb5_keyblock **key)
+{
+ unsigned char *p, *dh_gen_key = NULL;
+ struct krb5_pk_cert *host = NULL;
+ BIGNUM *kdc_dh_pubkey = NULL;
+ KDCDHKeyInfo kdc_dh_info;
+ heim_oid contentType = { 0, NULL };
+ krb5_data content;
+ krb5_error_code ret;
+ int dh_gen_keylen;
+ size_t size;
+
+ krb5_data_zero(&content);
+ memset(&kdc_dh_info, 0, sizeof(kdc_dh_info));
+
+ if (der_heim_oid_cmp(oid_id_pkcs7_signedData(), dataType)) {
+ krb5_set_error_message(context, EINVAL,
+ N_("PKINIT: Invalid content type", ""));
+ return EINVAL;
+ }
+
+ ret = _krb5_pk_verify_sign(context,
+ indata->data,
+ indata->length,
+ ctx->id,
+ &contentType,
+ &content,
+ &host);
+ if (ret)
+ goto out;
+
+ /* make sure that it is the kdc's certificate */
+ ret = pk_verify_host(context, realm, hi, ctx, host);
+ if (ret)
+ goto out;
+
+ if (der_heim_oid_cmp(&contentType, oid_id_pkdhkeydata())) {
+ ret = KRB5KRB_AP_ERR_MSG_TYPE;
+ krb5_set_error_message(context, ret,
+ N_("pkinit - dh reply contains wrong oid", ""));
+ goto out;
+ }
+
+ ret = decode_KDCDHKeyInfo(content.data,
+ content.length,
+ &kdc_dh_info,
+ &size);
+
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("pkinit - failed to decode "
+ "KDC DH Key Info", ""));
+ goto out;
+ }
+
+ if (kdc_dh_info.nonce != nonce) {
+ ret = KRB5KRB_AP_ERR_MODIFIED;
+ krb5_set_error_message(context, ret,
+ N_("PKINIT: DH nonce is wrong", ""));
+ goto out;
+ }
+
+ if (kdc_dh_info.dhKeyExpiration) {
+ if (k_n == NULL) {
+ ret = KRB5KRB_ERR_GENERIC;
+ krb5_set_error_message(context, ret,
+ N_("pkinit; got key expiration "
+ "without server nonce", ""));
+ goto out;
+ }
+ if (c_n == NULL) {
+ ret = KRB5KRB_ERR_GENERIC;
+ krb5_set_error_message(context, ret,
+ N_("pkinit; got DH reuse but no "
+ "client nonce", ""));
+ goto out;
+ }
+ } else {
+ if (k_n) {
+ ret = KRB5KRB_ERR_GENERIC;
+ krb5_set_error_message(context, ret,
+ N_("pkinit: got server nonce "
+ "without key expiration", ""));
+ goto out;
+ }
+ c_n = NULL;
+ }
+
+
+ p = kdc_dh_info.subjectPublicKey.data;
+ size = (kdc_dh_info.subjectPublicKey.length + 7) / 8;
+
+ {
+ DHPublicKey k;
+ ret = decode_DHPublicKey(p, size, &k, NULL);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("pkinit: can't decode "
+ "without key expiration", ""));
+ goto out;
+ }
+
+ kdc_dh_pubkey = integer_to_BN(context, "DHPublicKey", &k);
+ free_DHPublicKey(&k);
+ if (kdc_dh_pubkey == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+ }
+
+ dh_gen_keylen = DH_size(ctx->dh);
+ size = BN_num_bytes(ctx->dh->p);
+ if (size < dh_gen_keylen)
+ size = dh_gen_keylen;
+
+ dh_gen_key = malloc(size);
+ if (dh_gen_key == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ goto out;
+ }
+ memset(dh_gen_key, 0, size - dh_gen_keylen);
+
+ dh_gen_keylen = DH_compute_key(dh_gen_key + (size - dh_gen_keylen),
+ kdc_dh_pubkey, ctx->dh);
+ if (dh_gen_keylen == -1) {
+ ret = KRB5KRB_ERR_GENERIC;
+ krb5_set_error_message(context, ret,
+ N_("PKINIT: Can't compute Diffie-Hellman key", ""));
+ goto out;
+ }
+
+ *key = malloc (sizeof (**key));
+ if (*key == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret,
+ N_("malloc: out of memory", ""));
+ goto out;
+ }
+
+ ret = _krb5_pk_octetstring2key(context,
+ etype,
+ dh_gen_key, dh_gen_keylen,
+ c_n, k_n,
+ *key);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("PKINIT: can't create key from DH key", ""));
+ free(*key);
+ *key = NULL;
+ goto out;
+ }
+
+ out:
+ if (kdc_dh_pubkey)
+ BN_free(kdc_dh_pubkey);
+ if (dh_gen_key) {
+ memset(dh_gen_key, 0, DH_size(ctx->dh));
+ free(dh_gen_key);
+ }
+ if (host)
+ _krb5_pk_cert_free(host);
+ if (content.data)
+ krb5_data_free(&content);
+ der_free_oid(&contentType);
+ free_KDCDHKeyInfo(&kdc_dh_info);
+
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+_krb5_pk_rd_pa_reply(krb5_context context,
+ const char *realm,
+ void *c,
+ krb5_enctype etype,
+ const krb5_krbhst_info *hi,
+ unsigned nonce,
+ const krb5_data *req_buffer,
+ PA_DATA *pa,
+ krb5_keyblock **key)
+{
+ krb5_pk_init_ctx ctx = c;
+ krb5_error_code ret;
+ size_t size;
+
+ /* Check for IETF PK-INIT first */
+ if (ctx->type == PKINIT_27) {
+ PA_PK_AS_REP rep;
+ heim_octet_string os, data;
+ heim_oid oid;
+
+ if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
+ krb5_set_error_message(context, EINVAL,
+ N_("PKINIT: wrong padata recv", ""));
+ return EINVAL;
+ }
+
+ ret = decode_PA_PK_AS_REP(pa->padata_value.data,
+ pa->padata_value.length,
+ &rep,
+ &size);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("Failed to decode pkinit AS rep", ""));
+ return ret;
+ }
+
+ switch (rep.element) {
+ case choice_PA_PK_AS_REP_dhInfo:
+ os = rep.u.dhInfo.dhSignedData;
+ break;
+ case choice_PA_PK_AS_REP_encKeyPack:
+ os = rep.u.encKeyPack;
+ break;
+ default:
+ free_PA_PK_AS_REP(&rep);
+ krb5_set_error_message(context, EINVAL,
+ N_("PKINIT: -27 reply "
+ "invalid content type", ""));
+ return EINVAL;
+ }
+
+ ret = hx509_cms_unwrap_ContentInfo(&os, &oid, &data, NULL);
+ if (ret) {
+ free_PA_PK_AS_REP(&rep);
+ krb5_set_error_message(context, ret,
+ N_("PKINIT: failed to unwrap CI", ""));
+ return ret;
+ }
+
+ switch (rep.element) {
+ case choice_PA_PK_AS_REP_dhInfo:
+ ret = pk_rd_pa_reply_dh(context, &data, &oid, realm, ctx, etype, hi,
+ ctx->clientDHNonce,
+ rep.u.dhInfo.serverDHNonce,
+ nonce, pa, key);
+ break;
+ case choice_PA_PK_AS_REP_encKeyPack:
+ ret = pk_rd_pa_reply_enckey(context, PKINIT_27, &data, &oid, realm,
+ ctx, etype, hi, nonce, req_buffer, pa, key);
+ break;
+ default:
+ krb5_abortx(context, "pk-init as-rep case not possible to happen");
+ }
+ der_free_octet_string(&data);
+ der_free_oid(&oid);
+ free_PA_PK_AS_REP(&rep);
+
+ } else if (ctx->type == PKINIT_WIN2K) {
+ PA_PK_AS_REP_Win2k w2krep;
+
+ /* Check for Windows encoding of the AS-REP pa data */
+
+#if 0 /* should this be ? */
+ if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
+ krb5_set_error_message(context, EINVAL,
+ "PKINIT: wrong padata recv");
+ return EINVAL;
+ }
+#endif
+
+ memset(&w2krep, 0, sizeof(w2krep));
+
+ ret = decode_PA_PK_AS_REP_Win2k(pa->padata_value.data,
+ pa->padata_value.length,
+ &w2krep,
+ &size);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("PKINIT: Failed decoding windows "
+ "pkinit reply %d", ""), (int)ret);
+ return ret;
+ }
+
+ krb5_clear_error_message(context);
+
+ switch (w2krep.element) {
+ case choice_PA_PK_AS_REP_Win2k_encKeyPack: {
+ heim_octet_string data;
+ heim_oid oid;
+
+ ret = hx509_cms_unwrap_ContentInfo(&w2krep.u.encKeyPack,
+ &oid, &data, NULL);
+ free_PA_PK_AS_REP_Win2k(&w2krep);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("PKINIT: failed to unwrap CI", ""));
+ return ret;
+ }
+
+ ret = pk_rd_pa_reply_enckey(context, PKINIT_WIN2K, &data, &oid, realm,
+ ctx, etype, hi, nonce, req_buffer, pa, key);
+ der_free_octet_string(&data);
+ der_free_oid(&oid);
+
+ break;
+ }
+ default:
+ free_PA_PK_AS_REP_Win2k(&w2krep);
+ ret = EINVAL;
+ krb5_set_error_message(context, ret,
+ N_("PKINIT: win2k reply invalid "
+ "content type", ""));
+ break;
+ }
+
+ } else {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret,
+ N_("PKINIT: unknown reply type", ""));
+ }
+
+ return ret;
+}
+
+struct prompter {
+ krb5_context context;
+ krb5_prompter_fct prompter;
+ void *prompter_data;
+};
+
+static int
+hx_pass_prompter(void *data, const hx509_prompt *prompter)
+{
+ krb5_error_code ret;
+ krb5_prompt prompt;
+ krb5_data password_data;
+ struct prompter *p = data;
+
+ password_data.data = prompter->reply.data;
+ password_data.length = prompter->reply.length;
+
+ prompt.prompt = prompter->prompt;
+ prompt.hidden = hx509_prompt_hidden(prompter->type);
+ prompt.reply = &password_data;
+
+ switch (prompter->type) {
+ case HX509_PROMPT_TYPE_INFO:
+ prompt.type = KRB5_PROMPT_TYPE_INFO;
+ break;
+ case HX509_PROMPT_TYPE_PASSWORD:
+ case HX509_PROMPT_TYPE_QUESTION:
+ default:
+ prompt.type = KRB5_PROMPT_TYPE_PASSWORD;
+ break;
+ }
+
+ ret = (*p->prompter)(p->context, p->prompter_data, NULL, NULL, 1, &prompt);
+ if (ret) {
+ memset (prompter->reply.data, 0, prompter->reply.length);
+ return 1;
+ }
+ return 0;
+}
+
+
+void KRB5_LIB_FUNCTION
+_krb5_pk_allow_proxy_certificate(struct krb5_pk_identity *id,
+ int boolean)
+{
+ hx509_verify_set_proxy_certificate(id->verify_ctx, boolean);
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+_krb5_pk_load_id(krb5_context context,
+ struct krb5_pk_identity **ret_id,
+ const char *user_id,
+ const char *anchor_id,
+ char * const *chain_list,
+ char * const *revoke_list,
+ krb5_prompter_fct prompter,
+ void *prompter_data,
+ char *password)
+{
+ struct krb5_pk_identity *id = NULL;
+ hx509_lock lock = NULL;
+ struct prompter p;
+ int ret;
+
+ *ret_id = NULL;
+
+ if (anchor_id == NULL) {
+ krb5_set_error_message(context, HEIM_PKINIT_NO_VALID_CA,
+ N_("PKINIT: No anchor given", ""));
+ return HEIM_PKINIT_NO_VALID_CA;
+ }
+
+ if (user_id == NULL) {
+ krb5_set_error_message(context, HEIM_PKINIT_NO_PRIVATE_KEY,
+ N_("PKINIT: No user certificate given", ""));
+ return HEIM_PKINIT_NO_PRIVATE_KEY;
+ }
+
+ /* load cert */
+
+ id = calloc(1, sizeof(*id));
+ if (id == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+
+ ret = hx509_context_init(&id->hx509ctx);
+ if (ret)
+ goto out;
+
+ ret = hx509_lock_init(id->hx509ctx, &lock);
+ if (password && password[0])
+ hx509_lock_add_password(lock, password);
+
+ if (prompter) {
+ p.context = context;
+ p.prompter = prompter;
+ p.prompter_data = prompter_data;
+
+ ret = hx509_lock_set_prompter(lock, hx_pass_prompter, &p);
+ if (ret)
+ goto out;
+ }
+
+ ret = hx509_certs_init(id->hx509ctx, user_id, 0, lock, &id->certs);
+ if (ret) {
+ pk_copy_error(context, id->hx509ctx, ret,
+ "Failed to init cert certs");
+ goto out;
+ }
+
+ ret = hx509_certs_init(id->hx509ctx, anchor_id, 0, NULL, &id->anchors);
+ if (ret) {
+ pk_copy_error(context, id->hx509ctx, ret,
+ "Failed to init anchors");
+ goto out;
+ }
+
+ ret = hx509_certs_init(id->hx509ctx, "MEMORY:pkinit-cert-chain",
+ 0, NULL, &id->certpool);
+ if (ret) {
+ pk_copy_error(context, id->hx509ctx, ret,
+ "Failed to init chain");
+ goto out;
+ }
+
+ while (chain_list && *chain_list) {
+ ret = hx509_certs_append(id->hx509ctx, id->certpool,
+ NULL, *chain_list);
+ if (ret) {
+ pk_copy_error(context, id->hx509ctx, ret,
+ "Failed to laod chain %s",
+ *chain_list);
+ goto out;
+ }
+ chain_list++;
+ }
+
+ if (revoke_list) {
+ ret = hx509_revoke_init(id->hx509ctx, &id->revokectx);
+ if (ret) {
+ pk_copy_error(context, id->hx509ctx, ret,
+ "Failed init revoke list");
+ goto out;
+ }
+
+ while (*revoke_list) {
+ ret = hx509_revoke_add_crl(id->hx509ctx,
+ id->revokectx,
+ *revoke_list);
+ if (ret) {
+ pk_copy_error(context, id->hx509ctx, ret,
+ "Failed load revoke list");
+ goto out;
+ }
+ revoke_list++;
+ }
+ } else
+ hx509_context_set_missing_revoke(id->hx509ctx, 1);
+
+ ret = hx509_verify_init_ctx(id->hx509ctx, &id->verify_ctx);
+ if (ret) {
+ pk_copy_error(context, id->hx509ctx, ret,
+ "Failed init verify context");
+ goto out;
+ }
+
+ hx509_verify_attach_anchors(id->verify_ctx, id->anchors);
+ hx509_verify_attach_revoke(id->verify_ctx, id->revokectx);
+
+ out:
+ if (ret) {
+ hx509_verify_destroy_ctx(id->verify_ctx);
+ hx509_certs_free(&id->certs);
+ hx509_certs_free(&id->anchors);
+ hx509_certs_free(&id->certpool);
+ hx509_revoke_free(&id->revokectx);
+ hx509_context_free(&id->hx509ctx);
+ free(id);
+ } else
+ *ret_id = id;
+
+ hx509_lock_free(lock);
+
+ return ret;
+}
+
+static krb5_error_code
+select_dh_group(krb5_context context, DH *dh, unsigned long bits,
+ struct krb5_dh_moduli **moduli)
+{
+ const struct krb5_dh_moduli *m;
+
+ if (bits == 0) {
+ m = moduli[1]; /* XXX */
+ if (m == NULL)
+ m = moduli[0]; /* XXX */
+ } else {
+ int i;
+ for (i = 0; moduli[i] != NULL; i++) {
+ if (bits < moduli[i]->bits)
+ break;
+ }
+ if (moduli[i] == NULL) {
+ krb5_set_error_message(context, EINVAL,
+ N_("Did not find a DH group parameter "
+ "matching requirement of %lu bits", ""),
+ bits);
+ return EINVAL;
+ }
+ m = moduli[i];
+ }
+
+ dh->p = integer_to_BN(context, "p", &m->p);
+ if (dh->p == NULL)
+ return ENOMEM;
+ dh->g = integer_to_BN(context, "g", &m->g);
+ if (dh->g == NULL)
+ return ENOMEM;
+ dh->q = integer_to_BN(context, "q", &m->q);
+ if (dh->q == NULL)
+ return ENOMEM;
+
+ return 0;
+}
+
+/*
+ *
+ */
+
+static void
+pk_copy_error(krb5_context context,
+ hx509_context hx509ctx,
+ int hxret,
+ const char *fmt,
+ ...)
+{
+ va_list va;
+ char *s, *f;
+
+ va_start(va, fmt);
+ vasprintf(&f, fmt, va);
+ va_end(va);
+ if (f == NULL) {
+ krb5_clear_error_message(context);
+ return;
+ }
+
+ s = hx509_get_error_string(hx509ctx, hxret);
+ if (s == NULL) {
+ krb5_clear_error_message(context);
+ free(f);
+ return;
+ }
+ krb5_set_error_message(context, hxret, "%s: %s", f, s);
+ free(s);
+ free(f);
+}
+
+static int
+parse_integer(krb5_context context, char **p, const char *file, int lineno,
+ const char *name, heim_integer *integer)
+{
+ int ret;
+ char *p1;
+ p1 = strsep(p, " \t");
+ if (p1 == NULL) {
+ krb5_set_error_message(context, EINVAL,
+ N_("moduli file %s missing %s on line %d", ""),
+ file, name, lineno);
+ return EINVAL;
+ }
+ ret = der_parse_hex_heim_integer(p1, integer);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("moduli file %s failed parsing %s "
+ "on line %d", ""),
+ file, name, lineno);
+ return ret;
+ }
+
+ return 0;
+}
+
+krb5_error_code
+_krb5_parse_moduli_line(krb5_context context,
+ const char *file,
+ int lineno,
+ char *p,
+ struct krb5_dh_moduli **m)
+{
+ struct krb5_dh_moduli *m1;
+ char *p1;
+ int ret;
+
+ *m = NULL;
+
+ m1 = calloc(1, sizeof(*m1));
+ if (m1 == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+
+ while (isspace((unsigned char)*p))
+ p++;
+ if (*p == '#')
+ return 0;
+ ret = EINVAL;
+
+ p1 = strsep(&p, " \t");
+ if (p1 == NULL) {
+ krb5_set_error_message(context, ret,
+ N_("moduli file %s missing name on line %d", ""),
+ file, lineno);
+ goto out;
+ }
+ m1->name = strdup(p1);
+ if (p1 == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, N_("malloc: out of memeory", ""));
+ goto out;
+ }
+
+ p1 = strsep(&p, " \t");
+ if (p1 == NULL) {
+ krb5_set_error_message(context, ret,
+ N_("moduli file %s missing bits on line %d", ""),
+ file, lineno);
+ goto out;
+ }
+
+ m1->bits = atoi(p1);
+ if (m1->bits == 0) {
+ krb5_set_error_message(context, ret,
+ N_("moduli file %s have un-parsable "
+ "bits on line %d", ""), file, lineno);
+ goto out;
+ }
+
+ ret = parse_integer(context, &p, file, lineno, "p", &m1->p);
+ if (ret)
+ goto out;
+ ret = parse_integer(context, &p, file, lineno, "g", &m1->g);
+ if (ret)
+ goto out;
+ ret = parse_integer(context, &p, file, lineno, "q", &m1->q);
+ if (ret)
+ goto out;
+
+ *m = m1;
+
+ return 0;
+ out:
+ free(m1->name);
+ der_free_heim_integer(&m1->p);
+ der_free_heim_integer(&m1->g);
+ der_free_heim_integer(&m1->q);
+ free(m1);
+ return ret;
+}
+
+void
+_krb5_free_moduli(struct krb5_dh_moduli **moduli)
+{
+ int i;
+ for (i = 0; moduli[i] != NULL; i++) {
+ free(moduli[i]->name);
+ der_free_heim_integer(&moduli[i]->p);
+ der_free_heim_integer(&moduli[i]->g);
+ der_free_heim_integer(&moduli[i]->q);
+ free(moduli[i]);
+ }
+ free(moduli);
+}
+
+static const char *default_moduli_RFC2412_MODP_group2 =
+ /* name */
+ "RFC2412-MODP-group2 "
+ /* bits */
+ "1024 "
+ /* p */
+ "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
+ "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
+ "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
+ "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
+ "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381"
+ "FFFFFFFF" "FFFFFFFF "
+ /* g */
+ "02 "
+ /* q */
+ "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
+ "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
+ "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
+ "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
+ "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F67329C0"
+ "FFFFFFFF" "FFFFFFFF";
+
+static const char *default_moduli_rfc3526_MODP_group14 =
+ /* name */
+ "rfc3526-MODP-group14 "
+ /* bits */
+ "1760 "
+ /* p */
+ "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
+ "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
+ "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
+ "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
+ "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE45B3D"
+ "C2007CB8" "A163BF05" "98DA4836" "1C55D39A" "69163FA8" "FD24CF5F"
+ "83655D23" "DCA3AD96" "1C62F356" "208552BB" "9ED52907" "7096966D"
+ "670C354E" "4ABC9804" "F1746C08" "CA18217C" "32905E46" "2E36CE3B"
+ "E39E772C" "180E8603" "9B2783A2" "EC07A28F" "B5C55DF0" "6F4C52C9"
+ "DE2BCBF6" "95581718" "3995497C" "EA956AE5" "15D22618" "98FA0510"
+ "15728E5A" "8AACAA68" "FFFFFFFF" "FFFFFFFF "
+ /* g */
+ "02 "
+ /* q */
+ "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
+ "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
+ "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
+ "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
+ "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F6722D9E"
+ "E1003E5C" "50B1DF82" "CC6D241B" "0E2AE9CD" "348B1FD4" "7E9267AF"
+ "C1B2AE91" "EE51D6CB" "0E3179AB" "1042A95D" "CF6A9483" "B84B4B36"
+ "B3861AA7" "255E4C02" "78BA3604" "650C10BE" "19482F23" "171B671D"
+ "F1CF3B96" "0C074301" "CD93C1D1" "7603D147" "DAE2AEF8" "37A62964"
+ "EF15E5FB" "4AAC0B8C" "1CCAA4BE" "754AB572" "8AE9130C" "4C7D0288"
+ "0AB9472D" "45565534" "7FFFFFFF" "FFFFFFFF";
+
+krb5_error_code
+_krb5_parse_moduli(krb5_context context, const char *file,
+ struct krb5_dh_moduli ***moduli)
+{
+ /* name bits P G Q */
+ krb5_error_code ret;
+ struct krb5_dh_moduli **m = NULL, **m2;
+ char buf[4096];
+ FILE *f;
+ int lineno = 0, n = 0;
+
+ *moduli = NULL;
+
+ m = calloc(1, sizeof(m[0]) * 3);
+ if (m == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+
+ strlcpy(buf, default_moduli_rfc3526_MODP_group14, sizeof(buf));
+ ret = _krb5_parse_moduli_line(context, "builtin", 1, buf, &m[0]);
+ if (ret) {
+ _krb5_free_moduli(m);
+ return ret;
+ }
+ n++;
+
+ strlcpy(buf, default_moduli_RFC2412_MODP_group2, sizeof(buf));
+ ret = _krb5_parse_moduli_line(context, "builtin", 1, buf, &m[1]);
+ if (ret) {
+ _krb5_free_moduli(m);
+ return ret;
+ }
+ n++;
+
+
+ if (file == NULL)
+ file = MODULI_FILE;
+
+ f = fopen(file, "r");
+ if (f == NULL) {
+ *moduli = m;
+ return 0;
+ }
+ rk_cloexec_file(f);
+
+ while(fgets(buf, sizeof(buf), f) != NULL) {
+ struct krb5_dh_moduli *element;
+
+ buf[strcspn(buf, "\n")] = '\0';
+ lineno++;
+
+ m2 = realloc(m, (n + 2) * sizeof(m[0]));
+ if (m2 == NULL) {
+ _krb5_free_moduli(m);
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ m = m2;
+
+ m[n] = NULL;
+
+ ret = _krb5_parse_moduli_line(context, file, lineno, buf, &element);
+ if (ret) {
+ _krb5_free_moduli(m);
+ return ret;
+ }
+ if (element == NULL)
+ continue;
+
+ m[n] = element;
+ m[n + 1] = NULL;
+ n++;
+ }
+ *moduli = m;
+ return 0;
+}
+
+krb5_error_code
+_krb5_dh_group_ok(krb5_context context, unsigned long bits,
+ heim_integer *p, heim_integer *g, heim_integer *q,
+ struct krb5_dh_moduli **moduli,
+ char **name)
+{
+ int i;
+
+ if (name)
+ *name = NULL;
+
+ for (i = 0; moduli[i] != NULL; i++) {
+ if (der_heim_integer_cmp(&moduli[i]->g, g) == 0 &&
+ der_heim_integer_cmp(&moduli[i]->p, p) == 0 &&
+ (q == NULL || der_heim_integer_cmp(&moduli[i]->q, q) == 0))
+ {
+ if (bits && bits > moduli[i]->bits) {
+ krb5_set_error_message(context,
+ KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED,
+ N_("PKINIT: DH group parameter %s "
+ "no accepted, not enough bits "
+ "generated", ""),
+ moduli[i]->name);
+ return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
+ }
+ if (name)
+ *name = strdup(moduli[i]->name);
+ return 0;
+ }
+ }
+ krb5_set_error_message(context,
+ KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED,
+ N_("PKINIT: DH group parameter no ok", ""));
+ return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
+}
+#endif /* PKINIT */
+
+void KRB5_LIB_FUNCTION
+_krb5_get_init_creds_opt_free_pkinit(krb5_get_init_creds_opt *opt)
+{
+#ifdef PKINIT
+ krb5_pk_init_ctx ctx;
+
+ if (opt->opt_private == NULL || opt->opt_private->pk_init_ctx == NULL)
+ return;
+ ctx = opt->opt_private->pk_init_ctx;
+ if (ctx->dh)
+ DH_free(ctx->dh);
+ ctx->dh = NULL;
+ if (ctx->id) {
+ hx509_verify_destroy_ctx(ctx->id->verify_ctx);
+ hx509_certs_free(&ctx->id->certs);
+ hx509_certs_free(&ctx->id->anchors);
+ hx509_certs_free(&ctx->id->certpool);
+ hx509_context_free(&ctx->id->hx509ctx);
+
+ if (ctx->clientDHNonce) {
+ krb5_free_data(NULL, ctx->clientDHNonce);
+ ctx->clientDHNonce = NULL;
+ }
+ if (ctx->m)
+ _krb5_free_moduli(ctx->m);
+ free(ctx->id);
+ ctx->id = NULL;
+ }
+ free(opt->opt_private->pk_init_ctx);
+ opt->opt_private->pk_init_ctx = NULL;
+#endif
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_init_creds_opt_set_pkinit(krb5_context context,
+ krb5_get_init_creds_opt *opt,
+ krb5_principal principal,
+ const char *user_id,
+ const char *x509_anchors,
+ char * const * pool,
+ char * const * pki_revoke,
+ int flags,
+ krb5_prompter_fct prompter,
+ void *prompter_data,
+ char *password)
+{
+#ifdef PKINIT
+ krb5_error_code ret;
+ char *anchors = NULL;
+
+ if (opt->opt_private == NULL) {
+ krb5_set_error_message(context, EINVAL,
+ N_("PKINIT: on non extendable opt", ""));
+ return EINVAL;
+ }
+
+ opt->opt_private->pk_init_ctx =
+ calloc(1, sizeof(*opt->opt_private->pk_init_ctx));
+ if (opt->opt_private->pk_init_ctx == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ opt->opt_private->pk_init_ctx->dh = NULL;
+ opt->opt_private->pk_init_ctx->id = NULL;
+ opt->opt_private->pk_init_ctx->clientDHNonce = NULL;
+ opt->opt_private->pk_init_ctx->require_binding = 0;
+ opt->opt_private->pk_init_ctx->require_eku = 1;
+ opt->opt_private->pk_init_ctx->require_krbtgt_otherName = 1;
+ opt->opt_private->pk_init_ctx->peer = NULL;
+
+ /* XXX implement krb5_appdefault_strings */
+ if (pool == NULL)
+ pool = krb5_config_get_strings(context, NULL,
+ "appdefaults",
+ "pkinit_pool",
+ NULL);
+
+ if (pki_revoke == NULL)
+ pki_revoke = krb5_config_get_strings(context, NULL,
+ "appdefaults",
+ "pkinit_revoke",
+ NULL);
+
+ if (x509_anchors == NULL) {
+ krb5_appdefault_string(context, "kinit",
+ krb5_principal_get_realm(context, principal),
+ "pkinit_anchors", NULL, &anchors);
+ x509_anchors = anchors;
+ }
+
+ ret = _krb5_pk_load_id(context,
+ &opt->opt_private->pk_init_ctx->id,
+ user_id,
+ x509_anchors,
+ pool,
+ pki_revoke,
+ prompter,
+ prompter_data,
+ password);
+ if (ret) {
+ free(opt->opt_private->pk_init_ctx);
+ opt->opt_private->pk_init_ctx = NULL;
+ return ret;
+ }
+
+ if ((flags & 2) == 0) {
+ const char *moduli_file;
+ unsigned long dh_min_bits;
+
+ moduli_file = krb5_config_get_string(context, NULL,
+ "libdefaults",
+ "moduli",
+ NULL);
+
+ dh_min_bits =
+ krb5_config_get_int_default(context, NULL, 0,
+ "libdefaults",
+ "pkinit_dh_min_bits",
+ NULL);
+
+ ret = _krb5_parse_moduli(context, moduli_file,
+ &opt->opt_private->pk_init_ctx->m);
+ if (ret) {
+ _krb5_get_init_creds_opt_free_pkinit(opt);
+ return ret;
+ }
+
+ opt->opt_private->pk_init_ctx->dh = DH_new();
+ if (opt->opt_private->pk_init_ctx->dh == NULL) {
+ _krb5_get_init_creds_opt_free_pkinit(opt);
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+
+ ret = select_dh_group(context, opt->opt_private->pk_init_ctx->dh,
+ dh_min_bits,
+ opt->opt_private->pk_init_ctx->m);
+ if (ret) {
+ _krb5_get_init_creds_opt_free_pkinit(opt);
+ return ret;
+ }
+
+ if (DH_generate_key(opt->opt_private->pk_init_ctx->dh) != 1) {
+ _krb5_get_init_creds_opt_free_pkinit(opt);
+ krb5_set_error_message(context, ENOMEM,
+ N_("pkinit: failed to generate DH key", ""));
+ return ENOMEM;
+ }
+ }
+
+ return 0;
+#else
+ krb5_set_error_message(context, EINVAL,
+ N_("no support for PKINIT compiled in", ""));
+ return EINVAL;
+#endif
+}
diff --git a/source4/heimdal/lib/krb5/plugin.c b/source4/heimdal/lib/krb5/plugin.c
new file mode 100644
index 0000000000..a71dd8b6f7
--- /dev/null
+++ b/source4/heimdal/lib/krb5/plugin.c
@@ -0,0 +1,270 @@
+/*
+ * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+RCSID("$Id$");
+#ifdef HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+#include <dirent.h>
+
+struct krb5_plugin {
+ void *symbol;
+ void *dsohandle;
+ struct krb5_plugin *next;
+};
+
+struct plugin {
+ enum krb5_plugin_type type;
+ void *name;
+ void *symbol;
+ struct plugin *next;
+};
+
+static HEIMDAL_MUTEX plugin_mutex = HEIMDAL_MUTEX_INITIALIZER;
+static struct plugin *registered = NULL;
+
+static const char *plugin_dir = LIBDIR "/plugin/krb5";
+
+/*
+ *
+ */
+
+void *
+_krb5_plugin_get_symbol(struct krb5_plugin *p)
+{
+ return p->symbol;
+}
+
+struct krb5_plugin *
+_krb5_plugin_get_next(struct krb5_plugin *p)
+{
+ return p->next;
+}
+
+/*
+ *
+ */
+
+#ifdef HAVE_DLOPEN
+
+static krb5_error_code
+loadlib(krb5_context context,
+ enum krb5_plugin_type type,
+ const char *name,
+ const char *lib,
+ struct krb5_plugin **e)
+{
+ *e = calloc(1, sizeof(**e));
+ if (*e == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+
+#ifndef RTLD_LAZY
+#define RTLD_LAZY 0
+#endif
+
+ (*e)->dsohandle = dlopen(lib, RTLD_LAZY);
+ if ((*e)->dsohandle == NULL) {
+ free(*e);
+ *e = NULL;
+ krb5_set_error_message(context, ENOMEM, "Failed to load %s: %s",
+ lib, dlerror());
+ return ENOMEM;
+ }
+
+ /* dlsym doesn't care about the type */
+ (*e)->symbol = dlsym((*e)->dsohandle, name);
+ if ((*e)->symbol == NULL) {
+ dlclose((*e)->dsohandle);
+ free(*e);
+ krb5_clear_error_message(context);
+ return ENOMEM;
+ }
+
+ return 0;
+}
+#endif /* HAVE_DLOPEN */
+
+/**
+ * Register a plugin symbol name of specific type.
+ * @param context a Keberos context
+ * @param type type of plugin symbol
+ * @param name name of plugin symbol
+ * @param symbol a pointer to the named symbol
+ * @return In case of error a non zero error com_err error is returned
+ * and the Kerberos error string is set.
+ *
+ * @ingroup krb5_support
+ */
+
+krb5_error_code
+krb5_plugin_register(krb5_context context,
+ enum krb5_plugin_type type,
+ const char *name,
+ void *symbol)
+{
+ struct plugin *e;
+
+ /* check for duplicates */
+ for (e = registered; e != NULL; e = e->next)
+ if (e->type == type && strcmp(e->name,name)== 0 && e->symbol == symbol)
+ return 0;
+
+ e = calloc(1, sizeof(*e));
+ if (e == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ e->type = type;
+ e->name = strdup(name);
+ if (e->name == NULL) {
+ free(e);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ e->symbol = symbol;
+
+ HEIMDAL_MUTEX_lock(&plugin_mutex);
+ e->next = registered;
+ registered = e;
+ HEIMDAL_MUTEX_unlock(&plugin_mutex);
+
+ return 0;
+}
+
+krb5_error_code
+_krb5_plugin_find(krb5_context context,
+ enum krb5_plugin_type type,
+ const char *name,
+ struct krb5_plugin **list)
+{
+ struct krb5_plugin *e;
+ struct plugin *p;
+ krb5_error_code ret;
+ char *sysdirs[2] = { NULL, NULL };
+ char **dirs = NULL, **di;
+ struct dirent *entry;
+ char *path;
+ DIR *d = NULL;
+
+ *list = NULL;
+
+ HEIMDAL_MUTEX_lock(&plugin_mutex);
+
+ for (p = registered; p != NULL; p = p->next) {
+ if (p->type != type || strcmp(p->name, name) != 0)
+ continue;
+
+ e = calloc(1, sizeof(*e));
+ if (e == NULL) {
+ HEIMDAL_MUTEX_unlock(&plugin_mutex);
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto out;
+ }
+ e->symbol = p->symbol;
+ e->dsohandle = NULL;
+ e->next = *list;
+ *list = e;
+ }
+ HEIMDAL_MUTEX_unlock(&plugin_mutex);
+
+#ifdef HAVE_DLOPEN
+
+ dirs = krb5_config_get_strings(context, NULL, "libdefaults",
+ "plugin_dir", NULL);
+ if (dirs == NULL) {
+ sysdirs[0] = rk_UNCONST(plugin_dir);
+ dirs = sysdirs;
+ }
+
+ for (di = dirs; *di != NULL; di++) {
+
+ d = opendir(*di);
+ if (d == NULL)
+ continue;
+ rk_cloexec(dirfd(d));
+
+ while ((entry = readdir(d)) != NULL) {
+ asprintf(&path, "%s/%s", *di, entry->d_name);
+ if (path == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto out;
+ }
+ ret = loadlib(context, type, name, path, &e);
+ free(path);
+ if (ret)
+ continue;
+
+ e->next = *list;
+ *list = e;
+ }
+ closedir(d);
+ }
+ if (dirs != sysdirs)
+ krb5_config_free_strings(dirs);
+#endif /* HAVE_DLOPEN */
+
+ if (*list == NULL) {
+ krb5_set_error_message(context, ENOENT, "Did not find a plugin for %s", name);
+ return ENOENT;
+ }
+
+ return 0;
+
+out:
+ if (dirs && dirs != sysdirs)
+ krb5_config_free_strings(dirs);
+ if (d)
+ closedir(d);
+ _krb5_plugin_free(*list);
+ *list = NULL;
+
+ return ret;
+}
+
+void
+_krb5_plugin_free(struct krb5_plugin *list)
+{
+ struct krb5_plugin *next;
+ while (list) {
+ next = list->next;
+ if (list->dsohandle)
+ dlclose(list->dsohandle);
+ free(list);
+ list = next;
+ }
+}
diff --git a/source4/heimdal/lib/krb5/principal.c b/source4/heimdal/lib/krb5/principal.c
new file mode 100644
index 0000000000..f27355f2d8
--- /dev/null
+++ b/source4/heimdal/lib/krb5/principal.c
@@ -0,0 +1,1376 @@
+/*
+ * Copyright (c) 1997-2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/**
+ * @page page_principal The principal handing functions.
+ *
+ * A Kerberos principal is a email address looking string that
+ * contains to parts separeted by a @. The later part is the kerbero
+ * realm the principal belongs to and the former is a list of 0 or
+ * more components. For example
+ * @verbatim
+lha@SU.SE
+host/hummel.it.su.se@SU.SE
+host/admin@H5L.ORG
+@endverbatim
+ *
+ * See the library functions here: @ref krb5_principal
+ */
+
+#include "krb5_locl.h"
+#ifdef HAVE_RES_SEARCH
+#define USE_RESOLVER
+#endif
+#ifdef HAVE_ARPA_NAMESER_H
+#include <arpa/nameser.h>
+#endif
+#include <fnmatch.h>
+#include "resolve.h"
+
+RCSID("$Id$");
+
+#define princ_num_comp(P) ((P)->name.name_string.len)
+#define princ_type(P) ((P)->name.name_type)
+#define princ_comp(P) ((P)->name.name_string.val)
+#define princ_ncomp(P, N) ((P)->name.name_string.val[(N)])
+#define princ_realm(P) ((P)->realm)
+
+/**
+ * Frees a Kerberos principal allocated by the library with
+ * krb5_parse_name(), krb5_make_principal() or any other related
+ * principal functions.
+ *
+ * @param context A Kerberos context.
+ * @param p a principal to free.
+ *
+ * @return An krb5 error code, see krb5_get_error_message().
+ *
+ * @ingroup krb5_principal
+ */
+
+
+
+void KRB5_LIB_FUNCTION
+krb5_free_principal(krb5_context context,
+ krb5_principal p)
+{
+ if(p){
+ free_Principal(p);
+ free(p);
+ }
+}
+
+void KRB5_LIB_FUNCTION
+krb5_principal_set_type(krb5_context context,
+ krb5_principal principal,
+ int type)
+{
+ princ_type(principal) = type;
+}
+
+int KRB5_LIB_FUNCTION
+krb5_principal_get_type(krb5_context context,
+ krb5_const_principal principal)
+{
+ return princ_type(principal);
+}
+
+const char* KRB5_LIB_FUNCTION
+krb5_principal_get_realm(krb5_context context,
+ krb5_const_principal principal)
+{
+ return princ_realm(principal);
+}
+
+const char* KRB5_LIB_FUNCTION
+krb5_principal_get_comp_string(krb5_context context,
+ krb5_const_principal principal,
+ unsigned int component)
+{
+ if(component >= princ_num_comp(principal))
+ return NULL;
+ return princ_ncomp(principal, component);
+}
+
+/**
+ * Get number of component is principal.
+ *
+ * @param context Kerberos 5 context
+ * @param principal principal to query
+ * @return number of components in string
+ * @ingroup krb5
+ */
+
+unsigned int KRB5_LIB_FUNCTION
+krb5_principal_get_num_comp(krb5_context context,
+ krb5_const_principal principal)
+{
+ return princ_num_comp(principal);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_parse_name_flags(krb5_context context,
+ const char *name,
+ int flags,
+ krb5_principal *principal)
+{
+ krb5_error_code ret;
+ heim_general_string *comp;
+ heim_general_string realm = NULL;
+ int ncomp;
+
+ const char *p;
+ char *q;
+ char *s;
+ char *start;
+
+ int n;
+ char c;
+ int got_realm = 0;
+ int first_at = 1;
+ int enterprise = (flags & KRB5_PRINCIPAL_PARSE_ENTERPRISE);
+
+ *principal = NULL;
+
+#define RFLAGS (KRB5_PRINCIPAL_PARSE_NO_REALM|KRB5_PRINCIPAL_PARSE_MUST_REALM)
+
+ if ((flags & RFLAGS) == RFLAGS) {
+ krb5_set_error_message(context, KRB5_ERR_NO_SERVICE,
+ N_("Can't require both realm and "
+ "no realm at the same time", ""));
+ return KRB5_ERR_NO_SERVICE;
+ }
+#undef RFLAGS
+
+ /* count number of component,
+ * enterprise names only have one component
+ */
+ ncomp = 1;
+ if (!enterprise) {
+ for(p = name; *p; p++){
+ if(*p=='\\'){
+ if(!p[1]) {
+ krb5_set_error_message(context, KRB5_PARSE_MALFORMED,
+ N_("trailing \\ in principal name", ""));
+ return KRB5_PARSE_MALFORMED;
+ }
+ p++;
+ } else if(*p == '/')
+ ncomp++;
+ else if(*p == '@')
+ break;
+ }
+ }
+ comp = calloc(ncomp, sizeof(*comp));
+ if (comp == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+
+ n = 0;
+ p = start = q = s = strdup(name);
+ if (start == NULL) {
+ free (comp);
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ while(*p){
+ c = *p++;
+ if(c == '\\'){
+ c = *p++;
+ if(c == 'n')
+ c = '\n';
+ else if(c == 't')
+ c = '\t';
+ else if(c == 'b')
+ c = '\b';
+ else if(c == '0')
+ c = '\0';
+ else if(c == '\0') {
+ ret = KRB5_PARSE_MALFORMED;
+ krb5_set_error_message(context, ret,
+ N_("trailing \\ in principal name", ""));
+ goto exit;
+ }
+ }else if(enterprise && first_at) {
+ if (c == '@')
+ first_at = 0;
+ }else if((c == '/' && !enterprise) || c == '@'){
+ if(got_realm){
+ ret = KRB5_PARSE_MALFORMED;
+ krb5_set_error_message(context, ret,
+ N_("part after realm in principal name", ""));
+ goto exit;
+ }else{
+ comp[n] = malloc(q - start + 1);
+ if (comp[n] == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret,
+ N_("malloc: out of memory", ""));
+ goto exit;
+ }
+ memcpy(comp[n], start, q - start);
+ comp[n][q - start] = 0;
+ n++;
+ }
+ if(c == '@')
+ got_realm = 1;
+ start = q;
+ continue;
+ }
+ if(got_realm && (c == '/' || c == '\0')) {
+ ret = KRB5_PARSE_MALFORMED;
+ krb5_set_error_message(context, ret,
+ N_("part after realm in principal name", ""));
+ goto exit;
+ }
+ *q++ = c;
+ }
+ if(got_realm){
+ if (flags & KRB5_PRINCIPAL_PARSE_NO_REALM) {
+ ret = KRB5_PARSE_MALFORMED;
+ krb5_set_error_message(context, ret,
+ N_("realm found in 'short' principal "
+ "expected to be without one", ""));
+ goto exit;
+ }
+ realm = malloc(q - start + 1);
+ if (realm == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret,
+ N_("malloc: out of memory", ""));
+ goto exit;
+ }
+ memcpy(realm, start, q - start);
+ realm[q - start] = 0;
+ }else{
+ if (flags & KRB5_PRINCIPAL_PARSE_MUST_REALM) {
+ ret = KRB5_PARSE_MALFORMED;
+ krb5_set_error_message(context, ret,
+ N_("realm NOT found in principal "
+ "expected to be with one", ""));
+ goto exit;
+ } else if (flags & KRB5_PRINCIPAL_PARSE_NO_REALM) {
+ realm = NULL;
+ } else {
+ ret = krb5_get_default_realm (context, &realm);
+ if (ret)
+ goto exit;
+ }
+
+ comp[n] = malloc(q - start + 1);
+ if (comp[n] == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret,
+ N_("malloc: out of memory", ""));
+ goto exit;
+ }
+ memcpy(comp[n], start, q - start);
+ comp[n][q - start] = 0;
+ n++;
+ }
+ *principal = malloc(sizeof(**principal));
+ if (*principal == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret,
+ N_("malloc: out of memory", ""));
+ goto exit;
+ }
+ if (enterprise)
+ (*principal)->name.name_type = KRB5_NT_ENTERPRISE_PRINCIPAL;
+ else
+ (*principal)->name.name_type = KRB5_NT_PRINCIPAL;
+ (*principal)->name.name_string.val = comp;
+ princ_num_comp(*principal) = n;
+ (*principal)->realm = realm;
+ free(s);
+ return 0;
+exit:
+ while(n>0){
+ free(comp[--n]);
+ }
+ free(comp);
+ free(realm);
+ free(s);
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_parse_name(krb5_context context,
+ const char *name,
+ krb5_principal *principal)
+{
+ return krb5_parse_name_flags(context, name, 0, principal);
+}
+
+static const char quotable_chars[] = " \n\t\b\\/@";
+static const char replace_chars[] = " ntb\\/@";
+static const char nq_chars[] = " \\/@";
+
+#define add_char(BASE, INDEX, LEN, C) do { if((INDEX) < (LEN)) (BASE)[(INDEX)++] = (C); }while(0);
+
+static size_t
+quote_string(const char *s, char *out, size_t idx, size_t len, int display)
+{
+ const char *p, *q;
+ for(p = s; *p && idx < len; p++){
+ q = strchr(quotable_chars, *p);
+ if (q && display) {
+ add_char(out, idx, len, replace_chars[q - quotable_chars]);
+ } else if (q) {
+ add_char(out, idx, len, '\\');
+ add_char(out, idx, len, replace_chars[q - quotable_chars]);
+ }else
+ add_char(out, idx, len, *p);
+ }
+ if(idx < len)
+ out[idx] = '\0';
+ return idx;
+}
+
+
+static krb5_error_code
+unparse_name_fixed(krb5_context context,
+ krb5_const_principal principal,
+ char *name,
+ size_t len,
+ int flags)
+{
+ size_t idx = 0;
+ int i;
+ int short_form = (flags & KRB5_PRINCIPAL_UNPARSE_SHORT) != 0;
+ int no_realm = (flags & KRB5_PRINCIPAL_UNPARSE_NO_REALM) != 0;
+ int display = (flags & KRB5_PRINCIPAL_UNPARSE_DISPLAY) != 0;
+
+ if (!no_realm && princ_realm(principal) == NULL) {
+ krb5_set_error_message(context, ERANGE,
+ N_("Realm missing from principal, "
+ "can't unparse", ""));
+ return ERANGE;
+ }
+
+ for(i = 0; i < princ_num_comp(principal); i++){
+ if(i)
+ add_char(name, idx, len, '/');
+ idx = quote_string(princ_ncomp(principal, i), name, idx, len, display);
+ if(idx == len) {
+ krb5_set_error_message(context, ERANGE,
+ N_("Out of space printing principal", ""));
+ return ERANGE;
+ }
+ }
+ /* add realm if different from default realm */
+ if(short_form && !no_realm) {
+ krb5_realm r;
+ krb5_error_code ret;
+ ret = krb5_get_default_realm(context, &r);
+ if(ret)
+ return ret;
+ if(strcmp(princ_realm(principal), r) != 0)
+ short_form = 0;
+ free(r);
+ }
+ if(!short_form && !no_realm) {
+ add_char(name, idx, len, '@');
+ idx = quote_string(princ_realm(principal), name, idx, len, display);
+ if(idx == len) {
+ krb5_set_error_message(context, ERANGE,
+ N_("Out of space printing "
+ "realm of principal", ""));
+ return ERANGE;
+ }
+ }
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_unparse_name_fixed(krb5_context context,
+ krb5_const_principal principal,
+ char *name,
+ size_t len)
+{
+ return unparse_name_fixed(context, principal, name, len, 0);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_unparse_name_fixed_short(krb5_context context,
+ krb5_const_principal principal,
+ char *name,
+ size_t len)
+{
+ return unparse_name_fixed(context, principal, name, len,
+ KRB5_PRINCIPAL_UNPARSE_SHORT);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_unparse_name_fixed_flags(krb5_context context,
+ krb5_const_principal principal,
+ int flags,
+ char *name,
+ size_t len)
+{
+ return unparse_name_fixed(context, principal, name, len, flags);
+}
+
+static krb5_error_code
+unparse_name(krb5_context context,
+ krb5_const_principal principal,
+ char **name,
+ int flags)
+{
+ size_t len = 0, plen;
+ int i;
+ krb5_error_code ret;
+ /* count length */
+ if (princ_realm(principal)) {
+ plen = strlen(princ_realm(principal));
+
+ if(strcspn(princ_realm(principal), quotable_chars) == plen)
+ len += plen;
+ else
+ len += 2*plen;
+ len++; /* '@' */
+ }
+ for(i = 0; i < princ_num_comp(principal); i++){
+ plen = strlen(princ_ncomp(principal, i));
+ if(strcspn(princ_ncomp(principal, i), quotable_chars) == plen)
+ len += plen;
+ else
+ len += 2*plen;
+ len++;
+ }
+ len++; /* '\0' */
+ *name = malloc(len);
+ if(*name == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ ret = unparse_name_fixed(context, principal, *name, len, flags);
+ if(ret) {
+ free(*name);
+ *name = NULL;
+ }
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_unparse_name(krb5_context context,
+ krb5_const_principal principal,
+ char **name)
+{
+ return unparse_name(context, principal, name, 0);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_unparse_name_flags(krb5_context context,
+ krb5_const_principal principal,
+ int flags,
+ char **name)
+{
+ return unparse_name(context, principal, name, flags);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_unparse_name_short(krb5_context context,
+ krb5_const_principal principal,
+ char **name)
+{
+ return unparse_name(context, principal, name, KRB5_PRINCIPAL_UNPARSE_SHORT);
+}
+
+#if 0 /* not implemented */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_unparse_name_ext(krb5_context context,
+ krb5_const_principal principal,
+ char **name,
+ size_t *size)
+{
+ krb5_abortx(context, "unimplemented krb5_unparse_name_ext called");
+}
+
+#endif
+
+krb5_realm * KRB5_LIB_FUNCTION
+krb5_princ_realm(krb5_context context,
+ krb5_principal principal)
+{
+ return &princ_realm(principal);
+}
+
+
+void KRB5_LIB_FUNCTION
+krb5_princ_set_realm(krb5_context context,
+ krb5_principal principal,
+ krb5_realm *realm)
+{
+ princ_realm(principal) = *realm;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_principal_set_realm(krb5_context context,
+ krb5_principal principal,
+ krb5_const_realm realm)
+{
+ if (princ_realm(principal))
+ free(princ_realm(principal));
+
+ princ_realm(principal) = strdup(realm);
+ if (princ_realm(principal) == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ return 0;
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_build_principal(krb5_context context,
+ krb5_principal *principal,
+ int rlen,
+ krb5_const_realm realm,
+ ...)
+{
+ krb5_error_code ret;
+ va_list ap;
+ va_start(ap, realm);
+ ret = krb5_build_principal_va(context, principal, rlen, realm, ap);
+ va_end(ap);
+ return ret;
+}
+
+static krb5_error_code
+append_component(krb5_context context, krb5_principal p,
+ const char *comp,
+ size_t comp_len)
+{
+ heim_general_string *tmp;
+ size_t len = princ_num_comp(p);
+
+ tmp = realloc(princ_comp(p), (len + 1) * sizeof(*tmp));
+ if(tmp == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ princ_comp(p) = tmp;
+ princ_ncomp(p, len) = malloc(comp_len + 1);
+ if (princ_ncomp(p, len) == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ memcpy (princ_ncomp(p, len), comp, comp_len);
+ princ_ncomp(p, len)[comp_len] = '\0';
+ princ_num_comp(p)++;
+ return 0;
+}
+
+static void
+va_ext_princ(krb5_context context, krb5_principal p, va_list ap)
+{
+ while(1){
+ const char *s;
+ int len;
+ len = va_arg(ap, int);
+ if(len == 0)
+ break;
+ s = va_arg(ap, const char*);
+ append_component(context, p, s, len);
+ }
+}
+
+static void
+va_princ(krb5_context context, krb5_principal p, va_list ap)
+{
+ while(1){
+ const char *s;
+ s = va_arg(ap, const char*);
+ if(s == NULL)
+ break;
+ append_component(context, p, s, strlen(s));
+ }
+}
+
+
+static krb5_error_code
+build_principal(krb5_context context,
+ krb5_principal *principal,
+ int rlen,
+ krb5_const_realm realm,
+ void (*func)(krb5_context, krb5_principal, va_list),
+ va_list ap)
+{
+ krb5_principal p;
+
+ p = calloc(1, sizeof(*p));
+ if (p == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ princ_type(p) = KRB5_NT_PRINCIPAL;
+
+ princ_realm(p) = strdup(realm);
+ if(p->realm == NULL){
+ free(p);
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+
+ (*func)(context, p, ap);
+ *principal = p;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_make_principal(krb5_context context,
+ krb5_principal *principal,
+ krb5_const_realm realm,
+ ...)
+{
+ krb5_error_code ret;
+ krb5_realm r = NULL;
+ va_list ap;
+ if(realm == NULL) {
+ ret = krb5_get_default_realm(context, &r);
+ if(ret)
+ return ret;
+ realm = r;
+ }
+ va_start(ap, realm);
+ ret = krb5_build_principal_va(context, principal, strlen(realm), realm, ap);
+ va_end(ap);
+ if(r)
+ free(r);
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_build_principal_va(krb5_context context,
+ krb5_principal *principal,
+ int rlen,
+ krb5_const_realm realm,
+ va_list ap)
+{
+ return build_principal(context, principal, rlen, realm, va_princ, ap);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_build_principal_va_ext(krb5_context context,
+ krb5_principal *principal,
+ int rlen,
+ krb5_const_realm realm,
+ va_list ap)
+{
+ return build_principal(context, principal, rlen, realm, va_ext_princ, ap);
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_build_principal_ext(krb5_context context,
+ krb5_principal *principal,
+ int rlen,
+ krb5_const_realm realm,
+ ...)
+{
+ krb5_error_code ret;
+ va_list ap;
+ va_start(ap, realm);
+ ret = krb5_build_principal_va_ext(context, principal, rlen, realm, ap);
+ va_end(ap);
+ return ret;
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_copy_principal(krb5_context context,
+ krb5_const_principal inprinc,
+ krb5_principal *outprinc)
+{
+ krb5_principal p = malloc(sizeof(*p));
+ if (p == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ if(copy_Principal(inprinc, p)) {
+ free(p);
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ *outprinc = p;
+ return 0;
+}
+
+/*
+ * return TRUE iff princ1 == princ2 (without considering the realm)
+ */
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_principal_compare_any_realm(krb5_context context,
+ krb5_const_principal princ1,
+ krb5_const_principal princ2)
+{
+ int i;
+ if(princ_num_comp(princ1) != princ_num_comp(princ2))
+ return FALSE;
+ for(i = 0; i < princ_num_comp(princ1); i++){
+ if(strcmp(princ_ncomp(princ1, i), princ_ncomp(princ2, i)) != 0)
+ return FALSE;
+ }
+ return TRUE;
+}
+
+krb5_boolean KRB5_LIB_FUNCTION
+_krb5_principal_compare_PrincipalName(krb5_context context,
+ krb5_const_principal princ1,
+ PrincipalName *princ2)
+{
+ int i;
+ if (princ_num_comp(princ1) != princ2->name_string.len)
+ return FALSE;
+ for(i = 0; i < princ_num_comp(princ1); i++){
+ if(strcmp(princ_ncomp(princ1, i), princ2->name_string.val[i]) != 0)
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+/*
+ * return TRUE iff princ1 == princ2
+ */
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_principal_compare(krb5_context context,
+ krb5_const_principal princ1,
+ krb5_const_principal princ2)
+{
+ if(!krb5_realm_compare(context, princ1, princ2))
+ return FALSE;
+ return krb5_principal_compare_any_realm(context, princ1, princ2);
+}
+
+/*
+ * return TRUE iff realm(princ1) == realm(princ2)
+ */
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_realm_compare(krb5_context context,
+ krb5_const_principal princ1,
+ krb5_const_principal princ2)
+{
+ return strcmp(princ_realm(princ1), princ_realm(princ2)) == 0;
+}
+
+/*
+ * return TRUE iff princ matches pattern
+ */
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_principal_match(krb5_context context,
+ krb5_const_principal princ,
+ krb5_const_principal pattern)
+{
+ int i;
+ if(princ_num_comp(princ) != princ_num_comp(pattern))
+ return FALSE;
+ if(fnmatch(princ_realm(pattern), princ_realm(princ), 0) != 0)
+ return FALSE;
+ for(i = 0; i < princ_num_comp(princ); i++){
+ if(fnmatch(princ_ncomp(pattern, i), princ_ncomp(princ, i), 0) != 0)
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+static struct v4_name_convert {
+ const char *from;
+ const char *to;
+} default_v4_name_convert[] = {
+ { "ftp", "ftp" },
+ { "hprop", "hprop" },
+ { "pop", "pop" },
+ { "imap", "imap" },
+ { "rcmd", "host" },
+ { "smtp", "smtp" },
+ { NULL, NULL }
+};
+
+/*
+ * return the converted instance name of `name' in `realm'.
+ * look in the configuration file and then in the default set above.
+ * return NULL if no conversion is appropriate.
+ */
+
+static const char*
+get_name_conversion(krb5_context context, const char *realm, const char *name)
+{
+ struct v4_name_convert *q;
+ const char *p;
+
+ p = krb5_config_get_string(context, NULL, "realms", realm,
+ "v4_name_convert", "host", name, NULL);
+ if(p == NULL)
+ p = krb5_config_get_string(context, NULL, "libdefaults",
+ "v4_name_convert", "host", name, NULL);
+ if(p)
+ return p;
+
+ /* XXX should be possible to override default list */
+ p = krb5_config_get_string(context, NULL,
+ "realms",
+ realm,
+ "v4_name_convert",
+ "plain",
+ name,
+ NULL);
+ if(p)
+ return NULL;
+ p = krb5_config_get_string(context, NULL,
+ "libdefaults",
+ "v4_name_convert",
+ "plain",
+ name,
+ NULL);
+ if(p)
+ return NULL;
+ for(q = default_v4_name_convert; q->from; q++)
+ if(strcmp(q->from, name) == 0)
+ return q->to;
+ return NULL;
+}
+
+/*
+ * convert the v4 principal `name.instance@realm' to a v5 principal in `princ'.
+ * if `resolve', use DNS.
+ * if `func', use that function for validating the conversion
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_425_conv_principal_ext2(krb5_context context,
+ const char *name,
+ const char *instance,
+ const char *realm,
+ krb5_boolean (*func)(krb5_context,
+ void *, krb5_principal),
+ void *funcctx,
+ krb5_boolean resolve,
+ krb5_principal *princ)
+{
+ const char *p;
+ krb5_error_code ret;
+ krb5_principal pr;
+ char host[MAXHOSTNAMELEN];
+ char local_hostname[MAXHOSTNAMELEN];
+
+ /* do the following: if the name is found in the
+ `v4_name_convert:host' part, is assumed to be a `host' type
+ principal, and the instance is looked up in the
+ `v4_instance_convert' part. if not found there the name is
+ (optionally) looked up as a hostname, and if that doesn't yield
+ anything, the `default_domain' is appended to the instance
+ */
+
+ if(instance == NULL)
+ goto no_host;
+ if(instance[0] == 0){
+ instance = NULL;
+ goto no_host;
+ }
+ p = get_name_conversion(context, realm, name);
+ if(p == NULL)
+ goto no_host;
+ name = p;
+ p = krb5_config_get_string(context, NULL, "realms", realm,
+ "v4_instance_convert", instance, NULL);
+ if(p){
+ instance = p;
+ ret = krb5_make_principal(context, &pr, realm, name, instance, NULL);
+ if(func == NULL || (*func)(context, funcctx, pr)){
+ *princ = pr;
+ return 0;
+ }
+ krb5_free_principal(context, pr);
+ *princ = NULL;
+ krb5_clear_error_message (context);
+ return HEIM_ERR_V4_PRINC_NO_CONV;
+ }
+ if(resolve){
+ krb5_boolean passed = FALSE;
+ char *inst = NULL;
+#ifdef USE_RESOLVER
+ struct dns_reply *r;
+
+ r = dns_lookup(instance, "aaaa");
+ if (r) {
+ if (r->head && r->head->type == T_AAAA) {
+ inst = strdup(r->head->domain);
+ passed = TRUE;
+ }
+ dns_free_data(r);
+ } else {
+ r = dns_lookup(instance, "a");
+ if (r) {
+ if(r->head && r->head->type == T_A) {
+ inst = strdup(r->head->domain);
+ passed = TRUE;
+ }
+ dns_free_data(r);
+ }
+ }
+#else
+ struct addrinfo hints, *ai;
+
+ memset (&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_CANONNAME;
+ ret = getaddrinfo(instance, NULL, &hints, &ai);
+ if (ret == 0) {
+ const struct addrinfo *a;
+ for (a = ai; a != NULL; a = a->ai_next) {
+ if (a->ai_canonname != NULL) {
+ inst = strdup (a->ai_canonname);
+ passed = TRUE;
+ break;
+ }
+ }
+ freeaddrinfo (ai);
+ }
+#endif
+ if (passed) {
+ if (inst == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ strlwr(inst);
+ ret = krb5_make_principal(context, &pr, realm, name, inst,
+ NULL);
+ free (inst);
+ if(ret == 0) {
+ if(func == NULL || (*func)(context, funcctx, pr)){
+ *princ = pr;
+ return 0;
+ }
+ krb5_free_principal(context, pr);
+ }
+ }
+ }
+ if(func != NULL) {
+ snprintf(host, sizeof(host), "%s.%s", instance, realm);
+ strlwr(host);
+ ret = krb5_make_principal(context, &pr, realm, name, host, NULL);
+ if((*func)(context, funcctx, pr)){
+ *princ = pr;
+ return 0;
+ }
+ krb5_free_principal(context, pr);
+ }
+
+ /*
+ * if the instance is the first component of the local hostname,
+ * the converted host should be the long hostname.
+ */
+
+ if (func == NULL &&
+ gethostname (local_hostname, sizeof(local_hostname)) == 0 &&
+ strncmp(instance, local_hostname, strlen(instance)) == 0 &&
+ local_hostname[strlen(instance)] == '.') {
+ strlcpy(host, local_hostname, sizeof(host));
+ goto local_host;
+ }
+
+ {
+ char **domains, **d;
+ domains = krb5_config_get_strings(context, NULL, "realms", realm,
+ "v4_domains", NULL);
+ for(d = domains; d && *d; d++){
+ snprintf(host, sizeof(host), "%s.%s", instance, *d);
+ ret = krb5_make_principal(context, &pr, realm, name, host, NULL);
+ if(func == NULL || (*func)(context, funcctx, pr)){
+ *princ = pr;
+ krb5_config_free_strings(domains);
+ return 0;
+ }
+ krb5_free_principal(context, pr);
+ }
+ krb5_config_free_strings(domains);
+ }
+
+
+ p = krb5_config_get_string(context, NULL, "realms", realm,
+ "default_domain", NULL);
+ if(p == NULL){
+ /* this should be an error, just faking a name is not good */
+ krb5_clear_error_message (context);
+ return HEIM_ERR_V4_PRINC_NO_CONV;
+ }
+
+ if (*p == '.')
+ ++p;
+ snprintf(host, sizeof(host), "%s.%s", instance, p);
+local_host:
+ ret = krb5_make_principal(context, &pr, realm, name, host, NULL);
+ if(func == NULL || (*func)(context, funcctx, pr)){
+ *princ = pr;
+ return 0;
+ }
+ krb5_free_principal(context, pr);
+ krb5_clear_error_message (context);
+ return HEIM_ERR_V4_PRINC_NO_CONV;
+no_host:
+ p = krb5_config_get_string(context, NULL,
+ "realms",
+ realm,
+ "v4_name_convert",
+ "plain",
+ name,
+ NULL);
+ if(p == NULL)
+ p = krb5_config_get_string(context, NULL,
+ "libdefaults",
+ "v4_name_convert",
+ "plain",
+ name,
+ NULL);
+ if(p)
+ name = p;
+
+ ret = krb5_make_principal(context, &pr, realm, name, instance, NULL);
+ if(func == NULL || (*func)(context, funcctx, pr)){
+ *princ = pr;
+ return 0;
+ }
+ krb5_free_principal(context, pr);
+ krb5_clear_error_message (context);
+ return HEIM_ERR_V4_PRINC_NO_CONV;
+}
+
+static krb5_boolean
+convert_func(krb5_context conxtext, void *funcctx, krb5_principal principal)
+{
+ krb5_boolean (*func)(krb5_context, krb5_principal) = funcctx;
+ return (*func)(conxtext, principal);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_425_conv_principal_ext(krb5_context context,
+ const char *name,
+ const char *instance,
+ const char *realm,
+ krb5_boolean (*func)(krb5_context, krb5_principal),
+ krb5_boolean resolve,
+ krb5_principal *principal)
+{
+ return krb5_425_conv_principal_ext2(context,
+ name,
+ instance,
+ realm,
+ func ? convert_func : NULL,
+ func,
+ resolve,
+ principal);
+}
+
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_425_conv_principal(krb5_context context,
+ const char *name,
+ const char *instance,
+ const char *realm,
+ krb5_principal *princ)
+{
+ krb5_boolean resolve = krb5_config_get_bool(context,
+ NULL,
+ "libdefaults",
+ "v4_instance_resolve",
+ NULL);
+
+ return krb5_425_conv_principal_ext(context, name, instance, realm,
+ NULL, resolve, princ);
+}
+
+
+static int
+check_list(const krb5_config_binding *l, const char *name, const char **out)
+{
+ while(l){
+ if (l->type != krb5_config_string)
+ continue;
+ if(strcmp(name, l->u.string) == 0) {
+ *out = l->name;
+ return 1;
+ }
+ l = l->next;
+ }
+ return 0;
+}
+
+static int
+name_convert(krb5_context context, const char *name, const char *realm,
+ const char **out)
+{
+ const krb5_config_binding *l;
+ l = krb5_config_get_list (context,
+ NULL,
+ "realms",
+ realm,
+ "v4_name_convert",
+ "host",
+ NULL);
+ if(l && check_list(l, name, out))
+ return KRB5_NT_SRV_HST;
+ l = krb5_config_get_list (context,
+ NULL,
+ "libdefaults",
+ "v4_name_convert",
+ "host",
+ NULL);
+ if(l && check_list(l, name, out))
+ return KRB5_NT_SRV_HST;
+ l = krb5_config_get_list (context,
+ NULL,
+ "realms",
+ realm,
+ "v4_name_convert",
+ "plain",
+ NULL);
+ if(l && check_list(l, name, out))
+ return KRB5_NT_UNKNOWN;
+ l = krb5_config_get_list (context,
+ NULL,
+ "libdefaults",
+ "v4_name_convert",
+ "host",
+ NULL);
+ if(l && check_list(l, name, out))
+ return KRB5_NT_UNKNOWN;
+
+ /* didn't find it in config file, try built-in list */
+ {
+ struct v4_name_convert *q;
+ for(q = default_v4_name_convert; q->from; q++) {
+ if(strcmp(name, q->to) == 0) {
+ *out = q->from;
+ return KRB5_NT_SRV_HST;
+ }
+ }
+ }
+ return -1;
+}
+
+/*
+ * convert the v5 principal in `principal' into a v4 corresponding one
+ * in `name, instance, realm'
+ * this is limited interface since there's no length given for these
+ * three parameters. They have to be 40 bytes each (ANAME_SZ).
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_524_conv_principal(krb5_context context,
+ const krb5_principal principal,
+ char *name,
+ char *instance,
+ char *realm)
+{
+ const char *n, *i, *r;
+ char tmpinst[40];
+ int type = princ_type(principal);
+ const int aname_sz = 40;
+
+ r = principal->realm;
+
+ switch(principal->name.name_string.len){
+ case 1:
+ n = principal->name.name_string.val[0];
+ i = "";
+ break;
+ case 2:
+ n = principal->name.name_string.val[0];
+ i = principal->name.name_string.val[1];
+ break;
+ default:
+ krb5_set_error_message(context, KRB5_PARSE_MALFORMED,
+ N_("cannot convert a %d "
+ "component principal", ""),
+ principal->name.name_string.len);
+ return KRB5_PARSE_MALFORMED;
+ }
+
+ {
+ const char *tmp;
+ int t = name_convert(context, n, r, &tmp);
+ if(t >= 0) {
+ type = t;
+ n = tmp;
+ }
+ }
+
+ if(type == KRB5_NT_SRV_HST){
+ char *p;
+
+ strlcpy (tmpinst, i, sizeof(tmpinst));
+ p = strchr(tmpinst, '.');
+ if(p)
+ *p = 0;
+ i = tmpinst;
+ }
+
+ if (strlcpy (name, n, aname_sz) >= aname_sz) {
+ krb5_set_error_message(context, KRB5_PARSE_MALFORMED,
+ N_("too long name component to convert", ""));
+ return KRB5_PARSE_MALFORMED;
+ }
+ if (strlcpy (instance, i, aname_sz) >= aname_sz) {
+ krb5_set_error_message(context, KRB5_PARSE_MALFORMED,
+ N_("too long instance component to convert", ""));
+ return KRB5_PARSE_MALFORMED;
+ }
+ if (strlcpy (realm, r, aname_sz) >= aname_sz) {
+ krb5_set_error_message(context, KRB5_PARSE_MALFORMED,
+ N_("too long realm component to convert", ""));
+ return KRB5_PARSE_MALFORMED;
+ }
+ return 0;
+}
+
+/**
+ * Create a principal for the service running on hostname. If
+ * KRB5_NT_SRV_HST is used, the hostname is canonization using DNS (or
+ * some other service), this is potentially insecure.
+ *
+ * @param context A Kerberos context.
+ * @param hostname hostname to use
+ * @param sname Service name to use
+ * @param type name type of pricipal, use KRB5_NT_SRV_HST or KRB5_NT_UNKNOWN.
+ * @param ret_princ return principal, free with krb5_free_principal().
+ *
+ * @return An krb5 error code, see krb5_get_error_message().
+ *
+ * @ingroup krb5_principal
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_sname_to_principal (krb5_context context,
+ const char *hostname,
+ const char *sname,
+ int32_t type,
+ krb5_principal *ret_princ)
+{
+ krb5_error_code ret;
+ char localhost[MAXHOSTNAMELEN];
+ char **realms, *host = NULL;
+
+ if(type != KRB5_NT_SRV_HST && type != KRB5_NT_UNKNOWN) {
+ krb5_set_error_message(context, KRB5_SNAME_UNSUPP_NAMETYPE,
+ N_("unsupported name type %d", ""),
+ (int)type);
+ return KRB5_SNAME_UNSUPP_NAMETYPE;
+ }
+ if(hostname == NULL) {
+ ret = gethostname(localhost, sizeof(localhost) - 1);
+ if (ret != 0) {
+ ret = errno;
+ krb5_set_error_message(context, ret,
+ N_("Failed to get local hostname", ""));
+ return ret;
+ }
+ localhost[sizeof(localhost) - 1] = '\0';
+ hostname = localhost;
+ }
+ if(sname == NULL)
+ sname = "host";
+ if(type == KRB5_NT_SRV_HST) {
+ ret = krb5_expand_hostname_realms (context, hostname,
+ &host, &realms);
+ if (ret)
+ return ret;
+ strlwr(host);
+ hostname = host;
+ } else {
+ ret = krb5_get_host_realm(context, hostname, &realms);
+ if(ret)
+ return ret;
+ }
+
+ ret = krb5_make_principal(context, ret_princ, realms[0], sname,
+ hostname, NULL);
+ if(host)
+ free(host);
+ krb5_free_host_realm(context, realms);
+ return ret;
+}
+
+static const struct {
+ const char *type;
+ int32_t value;
+} nametypes[] = {
+ { "UNKNOWN", KRB5_NT_UNKNOWN },
+ { "PRINCIPAL", KRB5_NT_PRINCIPAL },
+ { "SRV_INST", KRB5_NT_SRV_INST },
+ { "SRV_HST", KRB5_NT_SRV_HST },
+ { "SRV_XHST", KRB5_NT_SRV_XHST },
+ { "UID", KRB5_NT_UID },
+ { "X500_PRINCIPAL", KRB5_NT_X500_PRINCIPAL },
+ { "SMTP_NAME", KRB5_NT_SMTP_NAME },
+ { "ENTERPRISE_PRINCIPAL", KRB5_NT_ENTERPRISE_PRINCIPAL },
+ { "ENT_PRINCIPAL_AND_ID", KRB5_NT_ENT_PRINCIPAL_AND_ID },
+ { "MS_PRINCIPAL", KRB5_NT_MS_PRINCIPAL },
+ { "MS_PRINCIPAL_AND_ID", KRB5_NT_MS_PRINCIPAL_AND_ID },
+ { NULL }
+};
+
+krb5_error_code
+krb5_parse_nametype(krb5_context context, const char *str, int32_t *nametype)
+{
+ size_t i;
+
+ for(i = 0; nametypes[i].type; i++) {
+ if (strcasecmp(nametypes[i].type, str) == 0) {
+ *nametype = nametypes[i].value;
+ return 0;
+ }
+ }
+ krb5_set_error_message(context, KRB5_PARSE_MALFORMED,
+ N_("Failed to find name type %s", ""), str);
+ return KRB5_PARSE_MALFORMED;
+}
diff --git a/source4/heimdal/lib/krb5/prog_setup.c b/source4/heimdal/lib/krb5/prog_setup.c
new file mode 100644
index 0000000000..b368573b8d
--- /dev/null
+++ b/source4/heimdal/lib/krb5/prog_setup.c
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+#include <getarg.h>
+#include <err.h>
+
+RCSID("$Id$");
+
+void KRB5_LIB_FUNCTION
+krb5_std_usage(int code, struct getargs *args, int num_args)
+{
+ arg_printusage(args, num_args, NULL, "");
+ exit(code);
+}
+
+int KRB5_LIB_FUNCTION
+krb5_program_setup(krb5_context *context, int argc, char **argv,
+ struct getargs *args, int num_args,
+ void (*usage)(int, struct getargs*, int))
+{
+ krb5_error_code ret;
+ int optidx = 0;
+
+ if(usage == NULL)
+ usage = krb5_std_usage;
+
+ setprogname(argv[0]);
+ ret = krb5_init_context(context);
+ if (ret)
+ errx (1, "krb5_init_context failed: %d", ret);
+
+ if(getarg(args, num_args, argc, argv, &optidx))
+ (*usage)(1, args, num_args);
+ return optidx;
+}
diff --git a/source4/heimdal/lib/krb5/prompter_posix.c b/source4/heimdal/lib/krb5/prompter_posix.c
new file mode 100644
index 0000000000..7d63935423
--- /dev/null
+++ b/source4/heimdal/lib/krb5/prompter_posix.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+int KRB5_LIB_FUNCTION
+krb5_prompter_posix (krb5_context context,
+ void *data,
+ const char *name,
+ const char *banner,
+ int num_prompts,
+ krb5_prompt prompts[])
+{
+ int i;
+
+ if (name)
+ fprintf (stderr, "%s\n", name);
+ if (banner)
+ fprintf (stderr, "%s\n", banner);
+ if (name || banner)
+ fflush(stderr);
+ for (i = 0; i < num_prompts; ++i) {
+ if (prompts[i].hidden) {
+ if(UI_UTIL_read_pw_string(prompts[i].reply->data,
+ prompts[i].reply->length,
+ prompts[i].prompt,
+ 0))
+ return 1;
+ } else {
+ char *s = prompts[i].reply->data;
+
+ fputs (prompts[i].prompt, stdout);
+ fflush (stdout);
+ if(fgets(prompts[i].reply->data,
+ prompts[i].reply->length,
+ stdin) == NULL)
+ return 1;
+ s[strcspn(s, "\n")] = '\0';
+ }
+ }
+ return 0;
+}
diff --git a/source4/heimdal/lib/krb5/rd_cred.c b/source4/heimdal/lib/krb5/rd_cred.c
new file mode 100644
index 0000000000..dc51033019
--- /dev/null
+++ b/source4/heimdal/lib/krb5/rd_cred.c
@@ -0,0 +1,346 @@
+/*
+ * Copyright (c) 1997 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <krb5_locl.h>
+
+RCSID("$Id$");
+
+static krb5_error_code
+compare_addrs(krb5_context context,
+ krb5_address *a,
+ krb5_address *b,
+ const char *message)
+{
+ char a_str[64], b_str[64];
+ size_t len;
+
+ if(krb5_address_compare (context, a, b))
+ return 0;
+
+ krb5_print_address (a, a_str, sizeof(a_str), &len);
+ krb5_print_address (b, b_str, sizeof(b_str), &len);
+ krb5_set_error_message(context, KRB5KRB_AP_ERR_BADADDR,
+ "%s: %s != %s", message, b_str, a_str);
+ return KRB5KRB_AP_ERR_BADADDR;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_rd_cred(krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_data *in_data,
+ krb5_creds ***ret_creds,
+ krb5_replay_data *outdata)
+{
+ krb5_error_code ret;
+ size_t len;
+ KRB_CRED cred;
+ EncKrbCredPart enc_krb_cred_part;
+ krb5_data enc_krb_cred_part_data;
+ krb5_crypto crypto;
+ int i;
+
+ memset(&enc_krb_cred_part, 0, sizeof(enc_krb_cred_part));
+
+ if ((auth_context->flags &
+ (KRB5_AUTH_CONTEXT_RET_TIME | KRB5_AUTH_CONTEXT_RET_SEQUENCE)) &&
+ outdata == NULL)
+ return KRB5_RC_REQUIRED; /* XXX better error, MIT returns this */
+
+ *ret_creds = NULL;
+
+ ret = decode_KRB_CRED(in_data->data, in_data->length,
+ &cred, &len);
+ if(ret) {
+ krb5_clear_error_message(context);
+ return ret;
+ }
+
+ if (cred.pvno != 5) {
+ ret = KRB5KRB_AP_ERR_BADVERSION;
+ krb5_clear_error_message (context);
+ goto out;
+ }
+
+ if (cred.msg_type != krb_cred) {
+ ret = KRB5KRB_AP_ERR_MSG_TYPE;
+ krb5_clear_error_message (context);
+ goto out;
+ }
+
+ if (cred.enc_part.etype == ETYPE_NULL) {
+ /* DK: MIT GSS-API Compatibility */
+ enc_krb_cred_part_data.length = cred.enc_part.cipher.length;
+ enc_krb_cred_part_data.data = cred.enc_part.cipher.data;
+ } else {
+ /* Try both subkey and session key.
+ *
+ * RFC4120 claims we should use the session key, but Heimdal
+ * before 0.8 used the remote subkey if it was send in the
+ * auth_context.
+ */
+
+ if (auth_context->remote_subkey) {
+ ret = krb5_crypto_init(context, auth_context->remote_subkey,
+ 0, &crypto);
+ if (ret)
+ goto out;
+
+ ret = krb5_decrypt_EncryptedData(context,
+ crypto,
+ KRB5_KU_KRB_CRED,
+ &cred.enc_part,
+ &enc_krb_cred_part_data);
+
+ krb5_crypto_destroy(context, crypto);
+ }
+
+ /*
+ * If there was not subkey, or we failed using subkey,
+ * retry using the session key
+ */
+ if (auth_context->remote_subkey == NULL || ret == KRB5KRB_AP_ERR_BAD_INTEGRITY)
+ {
+
+ ret = krb5_crypto_init(context, auth_context->keyblock,
+ 0, &crypto);
+
+ if (ret)
+ goto out;
+
+ ret = krb5_decrypt_EncryptedData(context,
+ crypto,
+ KRB5_KU_KRB_CRED,
+ &cred.enc_part,
+ &enc_krb_cred_part_data);
+
+ krb5_crypto_destroy(context, crypto);
+ }
+ if (ret)
+ goto out;
+ }
+
+ ret = krb5_decode_EncKrbCredPart (context,
+ enc_krb_cred_part_data.data,
+ enc_krb_cred_part_data.length,
+ &enc_krb_cred_part,
+ &len);
+ if (enc_krb_cred_part_data.data != cred.enc_part.cipher.data)
+ krb5_data_free(&enc_krb_cred_part_data);
+ if (ret)
+ goto out;
+
+ /* check sender address */
+
+ if (enc_krb_cred_part.s_address
+ && auth_context->remote_address
+ && auth_context->remote_port) {
+ krb5_address *a;
+
+ ret = krb5_make_addrport (context, &a,
+ auth_context->remote_address,
+ auth_context->remote_port);
+ if (ret)
+ goto out;
+
+
+ ret = compare_addrs(context, a, enc_krb_cred_part.s_address,
+ N_("sender address is wrong "
+ "in received creds", ""));
+ krb5_free_address(context, a);
+ free(a);
+ if(ret)
+ goto out;
+ }
+
+ /* check receiver address */
+
+ if (enc_krb_cred_part.r_address
+ && auth_context->local_address) {
+ if(auth_context->local_port &&
+ enc_krb_cred_part.r_address->addr_type == KRB5_ADDRESS_ADDRPORT) {
+ krb5_address *a;
+ ret = krb5_make_addrport (context, &a,
+ auth_context->local_address,
+ auth_context->local_port);
+ if (ret)
+ goto out;
+
+ ret = compare_addrs(context, a, enc_krb_cred_part.r_address,
+ N_("receiver address is wrong "
+ "in received creds", ""));
+ krb5_free_address(context, a);
+ free(a);
+ if(ret)
+ goto out;
+ } else {
+ ret = compare_addrs(context, auth_context->local_address,
+ enc_krb_cred_part.r_address,
+ N_("receiver address is wrong "
+ "in received creds", ""));
+ if(ret)
+ goto out;
+ }
+ }
+
+ /* check timestamp */
+ if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_TIME) {
+ krb5_timestamp sec;
+
+ krb5_timeofday (context, &sec);
+
+ if (enc_krb_cred_part.timestamp == NULL ||
+ enc_krb_cred_part.usec == NULL ||
+ abs(*enc_krb_cred_part.timestamp - sec)
+ > context->max_skew) {
+ krb5_clear_error_message (context);
+ ret = KRB5KRB_AP_ERR_SKEW;
+ goto out;
+ }
+ }
+
+ if ((auth_context->flags &
+ (KRB5_AUTH_CONTEXT_RET_TIME | KRB5_AUTH_CONTEXT_RET_SEQUENCE))) {
+ /* if these fields are not present in the cred-part, silently
+ return zero */
+ memset(outdata, 0, sizeof(*outdata));
+ if(enc_krb_cred_part.timestamp)
+ outdata->timestamp = *enc_krb_cred_part.timestamp;
+ if(enc_krb_cred_part.usec)
+ outdata->usec = *enc_krb_cred_part.usec;
+ if(enc_krb_cred_part.nonce)
+ outdata->seq = *enc_krb_cred_part.nonce;
+ }
+
+ /* Convert to NULL terminated list of creds */
+
+ *ret_creds = calloc(enc_krb_cred_part.ticket_info.len + 1,
+ sizeof(**ret_creds));
+
+ if (*ret_creds == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret,
+ N_("malloc: out of memory", ""));
+ goto out;
+ }
+
+ for (i = 0; i < enc_krb_cred_part.ticket_info.len; ++i) {
+ KrbCredInfo *kci = &enc_krb_cred_part.ticket_info.val[i];
+ krb5_creds *creds;
+
+ creds = calloc(1, sizeof(*creds));
+ if(creds == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret,
+ N_("malloc: out of memory", ""));
+ goto out;
+ }
+
+ ASN1_MALLOC_ENCODE(Ticket, creds->ticket.data, creds->ticket.length,
+ &cred.tickets.val[i], &len, ret);
+ if (ret) {
+ free(creds);
+ goto out;
+ }
+ if(creds->ticket.length != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+ copy_EncryptionKey (&kci->key, &creds->session);
+ if (kci->prealm && kci->pname)
+ _krb5_principalname2krb5_principal (context,
+ &creds->client,
+ *kci->pname,
+ *kci->prealm);
+ if (kci->flags)
+ creds->flags.b = *kci->flags;
+ if (kci->authtime)
+ creds->times.authtime = *kci->authtime;
+ if (kci->starttime)
+ creds->times.starttime = *kci->starttime;
+ if (kci->endtime)
+ creds->times.endtime = *kci->endtime;
+ if (kci->renew_till)
+ creds->times.renew_till = *kci->renew_till;
+ if (kci->srealm && kci->sname)
+ _krb5_principalname2krb5_principal (context,
+ &creds->server,
+ *kci->sname,
+ *kci->srealm);
+ if (kci->caddr)
+ krb5_copy_addresses (context,
+ kci->caddr,
+ &creds->addresses);
+
+ (*ret_creds)[i] = creds;
+
+ }
+ (*ret_creds)[i] = NULL;
+
+ free_KRB_CRED (&cred);
+ free_EncKrbCredPart(&enc_krb_cred_part);
+
+ return 0;
+
+ out:
+ free_EncKrbCredPart(&enc_krb_cred_part);
+ free_KRB_CRED (&cred);
+ if(*ret_creds) {
+ for(i = 0; (*ret_creds)[i]; i++)
+ krb5_free_creds(context, (*ret_creds)[i]);
+ free(*ret_creds);
+ *ret_creds = NULL;
+ }
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_rd_cred2 (krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_ccache ccache,
+ krb5_data *in_data)
+{
+ krb5_error_code ret;
+ krb5_creds **creds;
+ int i;
+
+ ret = krb5_rd_cred(context, auth_context, in_data, &creds, NULL);
+ if(ret)
+ return ret;
+
+ /* Store the creds in the ccache */
+
+ for(i = 0; creds && creds[i]; i++) {
+ krb5_cc_store_cred(context, ccache, creds[i]);
+ krb5_free_creds(context, creds[i]);
+ }
+ free(creds);
+ return 0;
+}
diff --git a/source4/heimdal/lib/krb5/rd_error.c b/source4/heimdal/lib/krb5/rd_error.c
new file mode 100644
index 0000000000..75ae8b1e8a
--- /dev/null
+++ b/source4/heimdal/lib/krb5/rd_error.c
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_rd_error(krb5_context context,
+ const krb5_data *msg,
+ KRB_ERROR *result)
+{
+
+ size_t len;
+ krb5_error_code ret;
+
+ ret = decode_KRB_ERROR(msg->data, msg->length, result, &len);
+ if(ret) {
+ krb5_clear_error_message(context);
+ return ret;
+ }
+ result->error_code += KRB5KDC_ERR_NONE;
+ return 0;
+}
+
+void KRB5_LIB_FUNCTION
+krb5_free_error_contents (krb5_context context,
+ krb5_error *error)
+{
+ free_KRB_ERROR(error);
+ memset(error, 0, sizeof(*error));
+}
+
+void KRB5_LIB_FUNCTION
+krb5_free_error (krb5_context context,
+ krb5_error *error)
+{
+ krb5_free_error_contents (context, error);
+ free (error);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_error_from_rd_error(krb5_context context,
+ const krb5_error *error,
+ const krb5_creds *creds)
+{
+ krb5_error_code ret;
+
+ ret = error->error_code;
+ if (error->e_text != NULL) {
+ krb5_set_error_message(context, ret, "%s", *error->e_text);
+ } else {
+ char clientname[256], servername[256];
+
+ if (creds != NULL) {
+ krb5_unparse_name_fixed(context, creds->client,
+ clientname, sizeof(clientname));
+ krb5_unparse_name_fixed(context, creds->server,
+ servername, sizeof(servername));
+ }
+
+ switch (ret) {
+ case KRB5KDC_ERR_NAME_EXP :
+ krb5_set_error_message(context, ret,
+ N_("Client %s%s%s expired", ""),
+ creds ? "(" : "",
+ creds ? clientname : "",
+ creds ? ")" : "");
+ break;
+ case KRB5KDC_ERR_SERVICE_EXP :
+ krb5_set_error_message(context, ret,
+ N_("Server %s%s%s expired", ""),
+ creds ? "(" : "",
+ creds ? servername : "",
+ creds ? ")" : "");
+ break;
+ case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN :
+ krb5_set_error_message(context, ret,
+ N_("Client %s%s%s unknown", ""),
+ creds ? "(" : "",
+ creds ? clientname : "",
+ creds ? ")" : "");
+ break;
+ case KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN :
+ krb5_set_error_message(context, ret,
+ N_("Server %s%s%s unknown", ""),
+ creds ? "(" : "",
+ creds ? servername : "",
+ creds ? ")" : "");
+ break;
+ default :
+ krb5_clear_error_message(context);
+ break;
+ }
+ }
+ return ret;
+}
diff --git a/source4/heimdal/lib/krb5/rd_priv.c b/source4/heimdal/lib/krb5/rd_priv.c
new file mode 100644
index 0000000000..6778ccad88
--- /dev/null
+++ b/source4/heimdal/lib/krb5/rd_priv.c
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 1997-2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <krb5_locl.h>
+
+RCSID("$Id$");
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_rd_priv(krb5_context context,
+ krb5_auth_context auth_context,
+ const krb5_data *inbuf,
+ krb5_data *outbuf,
+ krb5_replay_data *outdata)
+{
+ krb5_error_code ret;
+ KRB_PRIV priv;
+ EncKrbPrivPart part;
+ size_t len;
+ krb5_data plain;
+ krb5_keyblock *key;
+ krb5_crypto crypto;
+
+ krb5_data_zero(outbuf);
+
+ if ((auth_context->flags &
+ (KRB5_AUTH_CONTEXT_RET_TIME | KRB5_AUTH_CONTEXT_RET_SEQUENCE)))
+ {
+ if (outdata == NULL) {
+ krb5_clear_error_message (context);
+ return KRB5_RC_REQUIRED; /* XXX better error, MIT returns this */
+ }
+ /* if these fields are not present in the priv-part, silently
+ return zero */
+ memset(outdata, 0, sizeof(*outdata));
+ }
+
+ memset(&priv, 0, sizeof(priv));
+ ret = decode_KRB_PRIV (inbuf->data, inbuf->length, &priv, &len);
+ if (ret) {
+ krb5_clear_error_message (context);
+ goto failure;
+ }
+ if (priv.pvno != 5) {
+ krb5_clear_error_message (context);
+ ret = KRB5KRB_AP_ERR_BADVERSION;
+ goto failure;
+ }
+ if (priv.msg_type != krb_priv) {
+ krb5_clear_error_message (context);
+ ret = KRB5KRB_AP_ERR_MSG_TYPE;
+ goto failure;
+ }
+
+ if (auth_context->remote_subkey)
+ key = auth_context->remote_subkey;
+ else if (auth_context->local_subkey)
+ key = auth_context->local_subkey;
+ else
+ key = auth_context->keyblock;
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret)
+ goto failure;
+ ret = krb5_decrypt_EncryptedData(context,
+ crypto,
+ KRB5_KU_KRB_PRIV,
+ &priv.enc_part,
+ &plain);
+ krb5_crypto_destroy(context, crypto);
+ if (ret)
+ goto failure;
+
+ ret = decode_EncKrbPrivPart (plain.data, plain.length, &part, &len);
+ krb5_data_free (&plain);
+ if (ret) {
+ krb5_clear_error_message (context);
+ goto failure;
+ }
+
+ /* check sender address */
+
+ if (part.s_address
+ && auth_context->remote_address
+ && !krb5_address_compare (context,
+ auth_context->remote_address,
+ part.s_address)) {
+ krb5_clear_error_message (context);
+ ret = KRB5KRB_AP_ERR_BADADDR;
+ goto failure_part;
+ }
+
+ /* check receiver address */
+
+ if (part.r_address
+ && auth_context->local_address
+ && !krb5_address_compare (context,
+ auth_context->local_address,
+ part.r_address)) {
+ krb5_clear_error_message (context);
+ ret = KRB5KRB_AP_ERR_BADADDR;
+ goto failure_part;
+ }
+
+ /* check timestamp */
+ if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_TIME) {
+ krb5_timestamp sec;
+
+ krb5_timeofday (context, &sec);
+ if (part.timestamp == NULL ||
+ part.usec == NULL ||
+ abs(*part.timestamp - sec) > context->max_skew) {
+ krb5_clear_error_message (context);
+ ret = KRB5KRB_AP_ERR_SKEW;
+ goto failure_part;
+ }
+ }
+
+ /* XXX - check replay cache */
+
+ /* check sequence number. since MIT krb5 cannot generate a sequence
+ number of zero but instead generates no sequence number, we accept that
+ */
+
+ if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) {
+ if ((part.seq_number == NULL
+ && auth_context->remote_seqnumber != 0)
+ || (part.seq_number != NULL
+ && *part.seq_number != auth_context->remote_seqnumber)) {
+ krb5_clear_error_message (context);
+ ret = KRB5KRB_AP_ERR_BADORDER;
+ goto failure_part;
+ }
+ auth_context->remote_seqnumber++;
+ }
+
+ ret = krb5_data_copy (outbuf, part.user_data.data, part.user_data.length);
+ if (ret)
+ goto failure_part;
+
+ if ((auth_context->flags &
+ (KRB5_AUTH_CONTEXT_RET_TIME | KRB5_AUTH_CONTEXT_RET_SEQUENCE))) {
+ if(part.timestamp)
+ outdata->timestamp = *part.timestamp;
+ if(part.usec)
+ outdata->usec = *part.usec;
+ if(part.seq_number)
+ outdata->seq = *part.seq_number;
+ }
+
+ failure_part:
+ free_EncKrbPrivPart (&part);
+
+ failure:
+ free_KRB_PRIV (&priv);
+ return ret;
+}
diff --git a/source4/heimdal/lib/krb5/rd_rep.c b/source4/heimdal/lib/krb5/rd_rep.c
new file mode 100644
index 0000000000..010726b180
--- /dev/null
+++ b/source4/heimdal/lib/krb5/rd_rep.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <krb5_locl.h>
+
+RCSID("$Id$");
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_rd_rep(krb5_context context,
+ krb5_auth_context auth_context,
+ const krb5_data *inbuf,
+ krb5_ap_rep_enc_part **repl)
+{
+ krb5_error_code ret;
+ AP_REP ap_rep;
+ size_t len;
+ krb5_data data;
+ krb5_crypto crypto;
+
+ krb5_data_zero (&data);
+ ret = 0;
+
+ ret = decode_AP_REP(inbuf->data, inbuf->length, &ap_rep, &len);
+ if (ret)
+ return ret;
+ if (ap_rep.pvno != 5) {
+ ret = KRB5KRB_AP_ERR_BADVERSION;
+ krb5_clear_error_message (context);
+ goto out;
+ }
+ if (ap_rep.msg_type != krb_ap_rep) {
+ ret = KRB5KRB_AP_ERR_MSG_TYPE;
+ krb5_clear_error_message (context);
+ goto out;
+ }
+
+ ret = krb5_crypto_init(context, auth_context->keyblock, 0, &crypto);
+ if (ret)
+ goto out;
+ ret = krb5_decrypt_EncryptedData (context,
+ crypto,
+ KRB5_KU_AP_REQ_ENC_PART,
+ &ap_rep.enc_part,
+ &data);
+ krb5_crypto_destroy(context, crypto);
+ if (ret)
+ goto out;
+
+ *repl = malloc(sizeof(**repl));
+ if (*repl == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ goto out;
+ }
+ ret = krb5_decode_EncAPRepPart(context,
+ data.data,
+ data.length,
+ *repl,
+ &len);
+ if (ret)
+ return ret;
+
+ if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_TIME) {
+ if ((*repl)->ctime != auth_context->authenticator->ctime ||
+ (*repl)->cusec != auth_context->authenticator->cusec)
+ {
+ krb5_free_ap_rep_enc_part(context, *repl);
+ *repl = NULL;
+ ret = KRB5KRB_AP_ERR_MUT_FAIL;
+ krb5_clear_error_message (context);
+ goto out;
+ }
+ }
+ if ((*repl)->seq_number)
+ krb5_auth_con_setremoteseqnumber(context, auth_context,
+ *((*repl)->seq_number));
+ if ((*repl)->subkey)
+ krb5_auth_con_setremotesubkey(context, auth_context, (*repl)->subkey);
+
+ out:
+ krb5_data_free (&data);
+ free_AP_REP (&ap_rep);
+ return ret;
+}
+
+void KRB5_LIB_FUNCTION
+krb5_free_ap_rep_enc_part (krb5_context context,
+ krb5_ap_rep_enc_part *val)
+{
+ if (val) {
+ free_EncAPRepPart (val);
+ free (val);
+ }
+}
diff --git a/source4/heimdal/lib/krb5/rd_req.c b/source4/heimdal/lib/krb5/rd_req.c
new file mode 100644
index 0000000000..a416f90c10
--- /dev/null
+++ b/source4/heimdal/lib/krb5/rd_req.c
@@ -0,0 +1,894 @@
+/*
+ * Copyright (c) 1997 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <krb5_locl.h>
+
+RCSID("$Id$");
+
+static krb5_error_code
+decrypt_tkt_enc_part (krb5_context context,
+ krb5_keyblock *key,
+ EncryptedData *enc_part,
+ EncTicketPart *decr_part)
+{
+ krb5_error_code ret;
+ krb5_data plain;
+ size_t len;
+ krb5_crypto crypto;
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret)
+ return ret;
+ ret = krb5_decrypt_EncryptedData (context,
+ crypto,
+ KRB5_KU_TICKET,
+ enc_part,
+ &plain);
+ krb5_crypto_destroy(context, crypto);
+ if (ret)
+ return ret;
+
+ ret = krb5_decode_EncTicketPart(context, plain.data, plain.length,
+ decr_part, &len);
+ krb5_data_free (&plain);
+ return ret;
+}
+
+static krb5_error_code
+decrypt_authenticator (krb5_context context,
+ EncryptionKey *key,
+ EncryptedData *enc_part,
+ Authenticator *authenticator,
+ krb5_key_usage usage)
+{
+ krb5_error_code ret;
+ krb5_data plain;
+ size_t len;
+ krb5_crypto crypto;
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret)
+ return ret;
+ ret = krb5_decrypt_EncryptedData (context,
+ crypto,
+ usage /* KRB5_KU_AP_REQ_AUTH */,
+ enc_part,
+ &plain);
+ /* for backwards compatibility, also try the old usage */
+ if (ret && usage == KRB5_KU_TGS_REQ_AUTH)
+ ret = krb5_decrypt_EncryptedData (context,
+ crypto,
+ KRB5_KU_AP_REQ_AUTH,
+ enc_part,
+ &plain);
+ krb5_crypto_destroy(context, crypto);
+ if (ret)
+ return ret;
+
+ ret = krb5_decode_Authenticator(context, plain.data, plain.length,
+ authenticator, &len);
+ krb5_data_free (&plain);
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_decode_ap_req(krb5_context context,
+ const krb5_data *inbuf,
+ krb5_ap_req *ap_req)
+{
+ krb5_error_code ret;
+ size_t len;
+ ret = decode_AP_REQ(inbuf->data, inbuf->length, ap_req, &len);
+ if (ret)
+ return ret;
+ if (ap_req->pvno != 5){
+ free_AP_REQ(ap_req);
+ krb5_clear_error_message (context);
+ return KRB5KRB_AP_ERR_BADVERSION;
+ }
+ if (ap_req->msg_type != krb_ap_req){
+ free_AP_REQ(ap_req);
+ krb5_clear_error_message (context);
+ return KRB5KRB_AP_ERR_MSG_TYPE;
+ }
+ if (ap_req->ticket.tkt_vno != 5){
+ free_AP_REQ(ap_req);
+ krb5_clear_error_message (context);
+ return KRB5KRB_AP_ERR_BADVERSION;
+ }
+ return 0;
+}
+
+static krb5_error_code
+check_transited(krb5_context context, Ticket *ticket, EncTicketPart *enc)
+{
+ char **realms;
+ unsigned int num_realms;
+ krb5_error_code ret;
+
+ /*
+ * Windows 2000 and 2003 uses this inside their TGT so it's normaly
+ * not seen by others, however, samba4 joined with a Windows AD as
+ * a Domain Controller gets exposed to this.
+ */
+ if(enc->transited.tr_type == 0 && enc->transited.contents.length == 0)
+ return 0;
+
+ if(enc->transited.tr_type != DOMAIN_X500_COMPRESS)
+ return KRB5KDC_ERR_TRTYPE_NOSUPP;
+
+ if(enc->transited.contents.length == 0)
+ return 0;
+
+ ret = krb5_domain_x500_decode(context, enc->transited.contents,
+ &realms, &num_realms,
+ enc->crealm,
+ ticket->realm);
+ if(ret)
+ return ret;
+ ret = krb5_check_transited(context, enc->crealm,
+ ticket->realm,
+ realms, num_realms, NULL);
+ free(realms);
+ return ret;
+}
+
+static krb5_error_code
+find_etypelist(krb5_context context,
+ krb5_auth_context auth_context,
+ EtypeList *etypes)
+{
+ krb5_error_code ret;
+ krb5_authdata *ad;
+ krb5_authdata adIfRelevant;
+ unsigned i;
+
+ adIfRelevant.len = 0;
+
+ etypes->len = 0;
+ etypes->val = NULL;
+
+ ad = auth_context->authenticator->authorization_data;
+ if (ad == NULL)
+ return 0;
+
+ for (i = 0; i < ad->len; i++) {
+ if (ad->val[i].ad_type == KRB5_AUTHDATA_IF_RELEVANT) {
+ ret = decode_AD_IF_RELEVANT(ad->val[i].ad_data.data,
+ ad->val[i].ad_data.length,
+ &adIfRelevant,
+ NULL);
+ if (ret)
+ return ret;
+
+ if (adIfRelevant.len == 1 &&
+ adIfRelevant.val[0].ad_type ==
+ KRB5_AUTHDATA_GSS_API_ETYPE_NEGOTIATION) {
+ break;
+ }
+ free_AD_IF_RELEVANT(&adIfRelevant);
+ adIfRelevant.len = 0;
+ }
+ }
+
+ if (adIfRelevant.len == 0)
+ return 0;
+
+ ret = decode_EtypeList(adIfRelevant.val[0].ad_data.data,
+ adIfRelevant.val[0].ad_data.length,
+ etypes,
+ NULL);
+ if (ret)
+ krb5_clear_error_message(context);
+
+ free_AD_IF_RELEVANT(&adIfRelevant);
+
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_decrypt_ticket(krb5_context context,
+ Ticket *ticket,
+ krb5_keyblock *key,
+ EncTicketPart *out,
+ krb5_flags flags)
+{
+ EncTicketPart t;
+ krb5_error_code ret;
+ ret = decrypt_tkt_enc_part (context, key, &ticket->enc_part, &t);
+ if (ret)
+ return ret;
+
+ {
+ krb5_timestamp now;
+ time_t start = t.authtime;
+
+ krb5_timeofday (context, &now);
+ if(t.starttime)
+ start = *t.starttime;
+ if(start - now > context->max_skew
+ || (t.flags.invalid
+ && !(flags & KRB5_VERIFY_AP_REQ_IGNORE_INVALID))) {
+ free_EncTicketPart(&t);
+ krb5_clear_error_message (context);
+ return KRB5KRB_AP_ERR_TKT_NYV;
+ }
+ if(now - t.endtime > context->max_skew) {
+ free_EncTicketPart(&t);
+ krb5_clear_error_message (context);
+ return KRB5KRB_AP_ERR_TKT_EXPIRED;
+ }
+
+ if(!t.flags.transited_policy_checked) {
+ ret = check_transited(context, ticket, &t);
+ if(ret) {
+ free_EncTicketPart(&t);
+ return ret;
+ }
+ }
+ }
+
+ if(out)
+ *out = t;
+ else
+ free_EncTicketPart(&t);
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_verify_authenticator_checksum(krb5_context context,
+ krb5_auth_context ac,
+ void *data,
+ size_t len)
+{
+ krb5_error_code ret;
+ krb5_keyblock *key;
+ krb5_authenticator authenticator;
+ krb5_crypto crypto;
+
+ ret = krb5_auth_con_getauthenticator (context,
+ ac,
+ &authenticator);
+ if(ret)
+ return ret;
+ if(authenticator->cksum == NULL) {
+ krb5_free_authenticator(context, &authenticator);
+ return -17;
+ }
+ ret = krb5_auth_con_getkey(context, ac, &key);
+ if(ret) {
+ krb5_free_authenticator(context, &authenticator);
+ return ret;
+ }
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if(ret)
+ goto out;
+ ret = krb5_verify_checksum (context,
+ crypto,
+ KRB5_KU_AP_REQ_AUTH_CKSUM,
+ data,
+ len,
+ authenticator->cksum);
+ krb5_crypto_destroy(context, crypto);
+out:
+ krb5_free_authenticator(context, &authenticator);
+ krb5_free_keyblock(context, key);
+ return ret;
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_verify_ap_req(krb5_context context,
+ krb5_auth_context *auth_context,
+ krb5_ap_req *ap_req,
+ krb5_const_principal server,
+ krb5_keyblock *keyblock,
+ krb5_flags flags,
+ krb5_flags *ap_req_options,
+ krb5_ticket **ticket)
+{
+ return krb5_verify_ap_req2 (context,
+ auth_context,
+ ap_req,
+ server,
+ keyblock,
+ flags,
+ ap_req_options,
+ ticket,
+ KRB5_KU_AP_REQ_AUTH);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_verify_ap_req2(krb5_context context,
+ krb5_auth_context *auth_context,
+ krb5_ap_req *ap_req,
+ krb5_const_principal server,
+ krb5_keyblock *keyblock,
+ krb5_flags flags,
+ krb5_flags *ap_req_options,
+ krb5_ticket **ticket,
+ krb5_key_usage usage)
+{
+ krb5_ticket *t;
+ krb5_auth_context ac;
+ krb5_error_code ret;
+ EtypeList etypes;
+
+ if (ticket)
+ *ticket = NULL;
+
+ if (auth_context && *auth_context) {
+ ac = *auth_context;
+ } else {
+ ret = krb5_auth_con_init (context, &ac);
+ if (ret)
+ return ret;
+ }
+
+ t = calloc(1, sizeof(*t));
+ if (t == NULL) {
+ ret = ENOMEM;
+ krb5_clear_error_message (context);
+ goto out;
+ }
+
+ if (ap_req->ap_options.use_session_key && ac->keyblock){
+ ret = krb5_decrypt_ticket(context, &ap_req->ticket,
+ ac->keyblock,
+ &t->ticket,
+ flags);
+ krb5_free_keyblock(context, ac->keyblock);
+ ac->keyblock = NULL;
+ }else
+ ret = krb5_decrypt_ticket(context, &ap_req->ticket,
+ keyblock,
+ &t->ticket,
+ flags);
+
+ if(ret)
+ goto out;
+
+ ret = _krb5_principalname2krb5_principal(context,
+ &t->server,
+ ap_req->ticket.sname,
+ ap_req->ticket.realm);
+ if (ret) goto out;
+ ret = _krb5_principalname2krb5_principal(context,
+ &t->client,
+ t->ticket.cname,
+ t->ticket.crealm);
+ if (ret) goto out;
+
+ ret = decrypt_authenticator (context,
+ &t->ticket.key,
+ &ap_req->authenticator,
+ ac->authenticator,
+ usage);
+ if (ret)
+ goto out;
+
+ {
+ krb5_principal p1, p2;
+ krb5_boolean res;
+
+ _krb5_principalname2krb5_principal(context,
+ &p1,
+ ac->authenticator->cname,
+ ac->authenticator->crealm);
+ _krb5_principalname2krb5_principal(context,
+ &p2,
+ t->ticket.cname,
+ t->ticket.crealm);
+ res = krb5_principal_compare (context, p1, p2);
+ krb5_free_principal (context, p1);
+ krb5_free_principal (context, p2);
+ if (!res) {
+ ret = KRB5KRB_AP_ERR_BADMATCH;
+ krb5_clear_error_message (context);
+ goto out;
+ }
+ }
+
+ /* check addresses */
+
+ if (t->ticket.caddr
+ && ac->remote_address
+ && !krb5_address_search (context,
+ ac->remote_address,
+ t->ticket.caddr)) {
+ ret = KRB5KRB_AP_ERR_BADADDR;
+ krb5_clear_error_message (context);
+ goto out;
+ }
+
+ /* check timestamp in authenticator */
+ {
+ krb5_timestamp now;
+
+ krb5_timeofday (context, &now);
+
+ if (abs(ac->authenticator->ctime - now) > context->max_skew) {
+ ret = KRB5KRB_AP_ERR_SKEW;
+ krb5_clear_error_message (context);
+ goto out;
+ }
+ }
+
+ if (ac->authenticator->seq_number)
+ krb5_auth_con_setremoteseqnumber(context, ac,
+ *ac->authenticator->seq_number);
+
+ /* XXX - Xor sequence numbers */
+
+ if (ac->authenticator->subkey) {
+ ret = krb5_auth_con_setremotesubkey(context, ac,
+ ac->authenticator->subkey);
+ if (ret)
+ goto out;
+ }
+
+ ret = find_etypelist(context, ac, &etypes);
+ if (ret)
+ goto out;
+
+ ac->keytype = ETYPE_NULL;
+
+ if (etypes.val) {
+ int i;
+
+ for (i = 0; i < etypes.len; i++) {
+ if (krb5_enctype_valid(context, etypes.val[i]) == 0) {
+ ac->keytype = etypes.val[i];
+ break;
+ }
+ }
+ }
+
+ /* save key */
+ ret = krb5_copy_keyblock(context, &t->ticket.key, &ac->keyblock);
+ if (ret) goto out;
+
+ if (ap_req_options) {
+ *ap_req_options = 0;
+ if (ac->keytype != ETYPE_NULL)
+ *ap_req_options |= AP_OPTS_USE_SUBKEY;
+ if (ap_req->ap_options.use_session_key)
+ *ap_req_options |= AP_OPTS_USE_SESSION_KEY;
+ if (ap_req->ap_options.mutual_required)
+ *ap_req_options |= AP_OPTS_MUTUAL_REQUIRED;
+ }
+
+ if(ticket)
+ *ticket = t;
+ else
+ krb5_free_ticket (context, t);
+ if (auth_context) {
+ if (*auth_context == NULL)
+ *auth_context = ac;
+ } else
+ krb5_auth_con_free (context, ac);
+ free_EtypeList(&etypes);
+ return 0;
+ out:
+ if (t)
+ krb5_free_ticket (context, t);
+ if (auth_context == NULL || *auth_context == NULL)
+ krb5_auth_con_free (context, ac);
+ return ret;
+}
+
+/*
+ *
+ */
+
+struct krb5_rd_req_in_ctx_data {
+ krb5_keytab keytab;
+ krb5_keyblock *keyblock;
+ krb5_boolean check_pac;
+};
+
+struct krb5_rd_req_out_ctx_data {
+ krb5_keyblock *keyblock;
+ krb5_flags ap_req_options;
+ krb5_ticket *ticket;
+};
+
+/*
+ *
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_rd_req_in_ctx_alloc(krb5_context context, krb5_rd_req_in_ctx *ctx)
+{
+ *ctx = calloc(1, sizeof(**ctx));
+ if (*ctx == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ (*ctx)->check_pac = (context->flags & KRB5_CTX_F_CHECK_PAC) ? 1 : 0;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_rd_req_in_set_keytab(krb5_context context,
+ krb5_rd_req_in_ctx in,
+ krb5_keytab keytab)
+{
+ in->keytab = keytab; /* XXX should make copy */
+ return 0;
+}
+
+/**
+ * Set if krb5_rq_red() is going to check the Windows PAC or not
+ *
+ * @param context Keberos 5 context.
+ * @param in krb5_rd_req_in_ctx to check the option on.
+ * @param flag flag to select if to check the pac (TRUE) or not (FALSE).
+ *
+ * @return Kerberos 5 error code, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_rd_req_in_set_pac_check(krb5_context context,
+ krb5_rd_req_in_ctx in,
+ krb5_boolean flag)
+{
+ in->check_pac = flag;
+ return 0;
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_rd_req_in_set_keyblock(krb5_context context,
+ krb5_rd_req_in_ctx in,
+ krb5_keyblock *keyblock)
+{
+ in->keyblock = keyblock; /* XXX should make copy */
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_rd_req_out_get_ap_req_options(krb5_context context,
+ krb5_rd_req_out_ctx out,
+ krb5_flags *ap_req_options)
+{
+ *ap_req_options = out->ap_req_options;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_rd_req_out_get_ticket(krb5_context context,
+ krb5_rd_req_out_ctx out,
+ krb5_ticket **ticket)
+{
+ return krb5_copy_ticket(context, out->ticket, ticket);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_rd_req_out_get_keyblock(krb5_context context,
+ krb5_rd_req_out_ctx out,
+ krb5_keyblock **keyblock)
+{
+ return krb5_copy_keyblock(context, out->keyblock, keyblock);
+}
+
+void KRB5_LIB_FUNCTION
+krb5_rd_req_in_ctx_free(krb5_context context, krb5_rd_req_in_ctx ctx)
+{
+ free(ctx);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+_krb5_rd_req_out_ctx_alloc(krb5_context context, krb5_rd_req_out_ctx *ctx)
+{
+ *ctx = calloc(1, sizeof(**ctx));
+ if (*ctx == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ return 0;
+}
+
+void KRB5_LIB_FUNCTION
+krb5_rd_req_out_ctx_free(krb5_context context, krb5_rd_req_out_ctx ctx)
+{
+ krb5_free_keyblock(context, ctx->keyblock);
+ free(ctx);
+}
+
+/*
+ *
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_rd_req(krb5_context context,
+ krb5_auth_context *auth_context,
+ const krb5_data *inbuf,
+ krb5_const_principal server,
+ krb5_keytab keytab,
+ krb5_flags *ap_req_options,
+ krb5_ticket **ticket)
+{
+ krb5_error_code ret;
+ krb5_rd_req_in_ctx in;
+ krb5_rd_req_out_ctx out;
+
+ ret = krb5_rd_req_in_ctx_alloc(context, &in);
+ if (ret)
+ return ret;
+
+ ret = krb5_rd_req_in_set_keytab(context, in, keytab);
+ if (ret) {
+ krb5_rd_req_in_ctx_free(context, in);
+ return ret;
+ }
+
+ ret = krb5_rd_req_ctx(context, auth_context, inbuf, server, in, &out);
+ krb5_rd_req_in_ctx_free(context, in);
+ if (ret)
+ return ret;
+
+ if (ap_req_options)
+ *ap_req_options = out->ap_req_options;
+ if (ticket) {
+ ret = krb5_copy_ticket(context, out->ticket, ticket);
+ if (ret)
+ goto out;
+ }
+
+out:
+ krb5_rd_req_out_ctx_free(context, out);
+ return ret;
+}
+
+/*
+ *
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_rd_req_with_keyblock(krb5_context context,
+ krb5_auth_context *auth_context,
+ const krb5_data *inbuf,
+ krb5_const_principal server,
+ krb5_keyblock *keyblock,
+ krb5_flags *ap_req_options,
+ krb5_ticket **ticket)
+{
+ krb5_error_code ret;
+ krb5_rd_req_in_ctx in;
+ krb5_rd_req_out_ctx out;
+
+ ret = krb5_rd_req_in_ctx_alloc(context, &in);
+ if (ret)
+ return ret;
+
+ ret = krb5_rd_req_in_set_keyblock(context, in, keyblock);
+ if (ret) {
+ krb5_rd_req_in_ctx_free(context, in);
+ return ret;
+ }
+
+ ret = krb5_rd_req_ctx(context, auth_context, inbuf, server, in, &out);
+ krb5_rd_req_in_ctx_free(context, in);
+ if (ret)
+ return ret;
+
+ if (ap_req_options)
+ *ap_req_options = out->ap_req_options;
+ if (ticket) {
+ ret = krb5_copy_ticket(context, out->ticket, ticket);
+ if (ret)
+ goto out;
+ }
+
+out:
+ krb5_rd_req_out_ctx_free(context, out);
+ return ret;
+}
+
+/*
+ *
+ */
+
+static krb5_error_code
+get_key_from_keytab(krb5_context context,
+ krb5_auth_context *auth_context,
+ krb5_ap_req *ap_req,
+ krb5_const_principal server,
+ krb5_keytab keytab,
+ krb5_keyblock **out_key)
+{
+ krb5_keytab_entry entry;
+ krb5_error_code ret;
+ int kvno;
+ krb5_keytab real_keytab;
+
+ if(keytab == NULL)
+ krb5_kt_default(context, &real_keytab);
+ else
+ real_keytab = keytab;
+
+ if (ap_req->ticket.enc_part.kvno)
+ kvno = *ap_req->ticket.enc_part.kvno;
+ else
+ kvno = 0;
+
+ ret = krb5_kt_get_entry (context,
+ real_keytab,
+ server,
+ kvno,
+ ap_req->ticket.enc_part.etype,
+ &entry);
+ if(ret)
+ goto out;
+ ret = krb5_copy_keyblock(context, &entry.keyblock, out_key);
+ krb5_kt_free_entry (context, &entry);
+out:
+ if(keytab == NULL)
+ krb5_kt_close(context, real_keytab);
+
+ return ret;
+}
+
+/*
+ *
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_rd_req_ctx(krb5_context context,
+ krb5_auth_context *auth_context,
+ const krb5_data *inbuf,
+ krb5_const_principal server,
+ krb5_rd_req_in_ctx inctx,
+ krb5_rd_req_out_ctx *outctx)
+{
+ krb5_error_code ret;
+ krb5_ap_req ap_req;
+ krb5_principal service = NULL;
+ krb5_rd_req_out_ctx o = NULL;
+
+ ret = _krb5_rd_req_out_ctx_alloc(context, &o);
+ if (ret)
+ goto out;
+
+ if (*auth_context == NULL) {
+ ret = krb5_auth_con_init(context, auth_context);
+ if (ret)
+ goto out;
+ }
+
+ ret = krb5_decode_ap_req(context, inbuf, &ap_req);
+ if(ret)
+ goto out;
+
+ if(server == NULL){
+ ret = _krb5_principalname2krb5_principal(context,
+ &service,
+ ap_req.ticket.sname,
+ ap_req.ticket.realm);
+ if (ret)
+ goto out;
+ server = service;
+ }
+ if (ap_req.ap_options.use_session_key &&
+ (*auth_context)->keyblock == NULL) {
+ ret = KRB5KRB_AP_ERR_NOKEY;
+ krb5_set_error_message(context, ret,
+ N_("krb5_rd_req: user to user auth "
+ "without session key given", ""));
+ goto out;
+ }
+
+ if((*auth_context)->keyblock){
+ ret = krb5_copy_keyblock(context,
+ (*auth_context)->keyblock,
+ &o->keyblock);
+ if (ret)
+ goto out;
+ } else if(inctx->keyblock){
+ ret = krb5_copy_keyblock(context,
+ inctx->keyblock,
+ &o->keyblock);
+ if (ret)
+ goto out;
+ } else {
+ krb5_keytab keytab = NULL;
+
+ if (inctx && inctx->keytab)
+ keytab = inctx->keytab;
+
+ ret = get_key_from_keytab(context,
+ auth_context,
+ &ap_req,
+ server,
+ keytab,
+ &o->keyblock);
+ if(ret)
+ goto out;
+ }
+
+ ret = krb5_verify_ap_req2(context,
+ auth_context,
+ &ap_req,
+ server,
+ o->keyblock,
+ 0,
+ &o->ap_req_options,
+ &o->ticket,
+ KRB5_KU_AP_REQ_AUTH);
+
+ if (ret)
+ goto out;
+
+ /* If there is a PAC, verify its server signature */
+ if (inctx->check_pac) {
+ krb5_pac pac;
+ krb5_data data;
+
+ ret = krb5_ticket_get_authorization_data_type(context,
+ o->ticket,
+ KRB5_AUTHDATA_WIN2K_PAC,
+ &data);
+ if (ret == 0) {
+ ret = krb5_pac_parse(context, data.data, data.length, &pac);
+ krb5_data_free(&data);
+ if (ret)
+ goto out;
+
+ ret = krb5_pac_verify(context,
+ pac,
+ o->ticket->ticket.authtime,
+ o->ticket->client,
+ o->keyblock,
+ NULL);
+ krb5_pac_free(context, pac);
+ if (ret)
+ goto out;
+ }
+ ret = 0;
+ }
+out:
+ if (ret || outctx == NULL) {
+ krb5_rd_req_out_ctx_free(context, o);
+ } else
+ *outctx = o;
+
+ free_AP_REQ(&ap_req);
+ if(service)
+ krb5_free_principal(context, service);
+ return ret;
+}
diff --git a/source4/heimdal/lib/krb5/replay.c b/source4/heimdal/lib/krb5/replay.c
new file mode 100644
index 0000000000..25a6da0262
--- /dev/null
+++ b/source4/heimdal/lib/krb5/replay.c
@@ -0,0 +1,321 @@
+/*
+ * Copyright (c) 1997-2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+#include <vis.h>
+
+RCSID("$Id$");
+
+struct krb5_rcache_data {
+ char *name;
+};
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_rc_resolve(krb5_context context,
+ krb5_rcache id,
+ const char *name)
+{
+ id->name = strdup(name);
+ if(id->name == NULL) {
+ krb5_set_error_message(context, KRB5_RC_MALLOC,
+ N_("malloc: out of memory", ""));
+ return KRB5_RC_MALLOC;
+ }
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_rc_resolve_type(krb5_context context,
+ krb5_rcache *id,
+ const char *type)
+{
+ *id = NULL;
+ if(strcmp(type, "FILE")) {
+ krb5_set_error_message (context, KRB5_RC_TYPE_NOTFOUND,
+ N_("replay cache type %s not supported", ""),
+ type);
+ return KRB5_RC_TYPE_NOTFOUND;
+ }
+ *id = calloc(1, sizeof(**id));
+ if(*id == NULL) {
+ krb5_set_error_message(context, KRB5_RC_MALLOC,
+ N_("malloc: out of memory", ""));
+ return KRB5_RC_MALLOC;
+ }
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_rc_resolve_full(krb5_context context,
+ krb5_rcache *id,
+ const char *string_name)
+{
+ krb5_error_code ret;
+
+ *id = NULL;
+
+ if(strncmp(string_name, "FILE:", 5)) {
+ krb5_set_error_message(context, KRB5_RC_TYPE_NOTFOUND,
+ N_("replay cache type %s not supported", ""),
+ string_name);
+ return KRB5_RC_TYPE_NOTFOUND;
+ }
+ ret = krb5_rc_resolve_type(context, id, "FILE");
+ if(ret)
+ return ret;
+ ret = krb5_rc_resolve(context, *id, string_name + 5);
+ if (ret) {
+ krb5_rc_close(context, *id);
+ *id = NULL;
+ }
+ return ret;
+}
+
+const char* KRB5_LIB_FUNCTION
+krb5_rc_default_name(krb5_context context)
+{
+ return "FILE:/var/run/default_rcache";
+}
+
+const char* KRB5_LIB_FUNCTION
+krb5_rc_default_type(krb5_context context)
+{
+ return "FILE";
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_rc_default(krb5_context context,
+ krb5_rcache *id)
+{
+ return krb5_rc_resolve_full(context, id, krb5_rc_default_name(context));
+}
+
+struct rc_entry{
+ time_t stamp;
+ unsigned char data[16];
+};
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_rc_initialize(krb5_context context,
+ krb5_rcache id,
+ krb5_deltat auth_lifespan)
+{
+ FILE *f = fopen(id->name, "w");
+ struct rc_entry tmp;
+ int ret;
+
+ if(f == NULL) {
+ ret = errno;
+ krb5_set_error_message(context, ret, "open(%s): %s", id->name,
+ strerror(ret));
+ return ret;
+ }
+ tmp.stamp = auth_lifespan;
+ fwrite(&tmp, 1, sizeof(tmp), f);
+ fclose(f);
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_rc_recover(krb5_context context,
+ krb5_rcache id)
+{
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_rc_destroy(krb5_context context,
+ krb5_rcache id)
+{
+ int ret;
+
+ if(remove(id->name) < 0) {
+ ret = errno;
+ krb5_set_error_message(context, ret, "remove(%s): %s", id->name,
+ strerror(ret));
+ return ret;
+ }
+ return krb5_rc_close(context, id);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_rc_close(krb5_context context,
+ krb5_rcache id)
+{
+ free(id->name);
+ free(id);
+ return 0;
+}
+
+static void
+checksum_authenticator(Authenticator *auth, void *data)
+{
+ MD5_CTX md5;
+ int i;
+
+ MD5_Init (&md5);
+ MD5_Update (&md5, auth->crealm, strlen(auth->crealm));
+ for(i = 0; i < auth->cname.name_string.len; i++)
+ MD5_Update(&md5, auth->cname.name_string.val[i],
+ strlen(auth->cname.name_string.val[i]));
+ MD5_Update (&md5, &auth->ctime, sizeof(auth->ctime));
+ MD5_Update (&md5, &auth->cusec, sizeof(auth->cusec));
+ MD5_Final (data, &md5);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_rc_store(krb5_context context,
+ krb5_rcache id,
+ krb5_donot_replay *rep)
+{
+ struct rc_entry ent, tmp;
+ time_t t;
+ FILE *f;
+ int ret;
+
+ ent.stamp = time(NULL);
+ checksum_authenticator(rep, ent.data);
+ f = fopen(id->name, "r");
+ if(f == NULL) {
+ ret = errno;
+ krb5_set_error_message(context, ret, "open(%s): %s", id->name,
+ strerror(ret));
+ return ret;
+ }
+ rk_cloexec_file(f);
+ fread(&tmp, sizeof(ent), 1, f);
+ t = ent.stamp - tmp.stamp;
+ while(fread(&tmp, sizeof(ent), 1, f)){
+ if(tmp.stamp < t)
+ continue;
+ if(memcmp(tmp.data, ent.data, sizeof(ent.data)) == 0){
+ fclose(f);
+ krb5_clear_error_message (context);
+ return KRB5_RC_REPLAY;
+ }
+ }
+ if(ferror(f)){
+ ret = errno;
+ fclose(f);
+ krb5_set_error_message(context, ret, "%s: %s",
+ id->name, strerror(ret));
+ return ret;
+ }
+ fclose(f);
+ f = fopen(id->name, "a");
+ if(f == NULL) {
+ krb5_set_error_message(context, KRB5_RC_IO_UNKNOWN,
+ "open(%s): %s", id->name,
+ strerror(errno));
+ return KRB5_RC_IO_UNKNOWN;
+ }
+ fwrite(&ent, 1, sizeof(ent), f);
+ fclose(f);
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_rc_expunge(krb5_context context,
+ krb5_rcache id)
+{
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_rc_get_lifespan(krb5_context context,
+ krb5_rcache id,
+ krb5_deltat *auth_lifespan)
+{
+ FILE *f = fopen(id->name, "r");
+ int r;
+ struct rc_entry ent;
+ r = fread(&ent, sizeof(ent), 1, f);
+ fclose(f);
+ if(r){
+ *auth_lifespan = ent.stamp;
+ return 0;
+ }
+ krb5_clear_error_message (context);
+ return KRB5_RC_IO_UNKNOWN;
+}
+
+const char* KRB5_LIB_FUNCTION
+krb5_rc_get_name(krb5_context context,
+ krb5_rcache id)
+{
+ return id->name;
+}
+
+const char* KRB5_LIB_FUNCTION
+krb5_rc_get_type(krb5_context context,
+ krb5_rcache id)
+{
+ return "FILE";
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_server_rcache(krb5_context context,
+ const krb5_data *piece,
+ krb5_rcache *id)
+{
+ krb5_rcache rcache;
+ krb5_error_code ret;
+
+ char *tmp = malloc(4 * piece->length + 1);
+ char *name;
+
+ if(tmp == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ strvisx(tmp, piece->data, piece->length, VIS_WHITE | VIS_OCTAL);
+#ifdef HAVE_GETEUID
+ asprintf(&name, "FILE:rc_%s_%u", tmp, (unsigned)geteuid());
+#else
+ asprintf(&name, "FILE:rc_%s", tmp);
+#endif
+ free(tmp);
+ if(name == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+
+ ret = krb5_rc_resolve_full(context, &rcache, name);
+ free(name);
+ if(ret)
+ return ret;
+ *id = rcache;
+ return ret;
+}
diff --git a/source4/heimdal/lib/krb5/send_to_kdc.c b/source4/heimdal/lib/krb5/send_to_kdc.c
new file mode 100644
index 0000000000..53c4a69a3f
--- /dev/null
+++ b/source4/heimdal/lib/krb5/send_to_kdc.c
@@ -0,0 +1,670 @@
+/*
+ * Copyright (c) 1997 - 2002 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+#include "send_to_kdc_plugin.h"
+
+RCSID("$Id$");
+
+struct send_to_kdc {
+ krb5_send_to_kdc_func func;
+ void *data;
+};
+
+/*
+ * send the data in `req' on the socket `fd' (which is datagram iff udp)
+ * waiting `tmout' for a reply and returning the reply in `rep'.
+ * iff limit read up to this many bytes
+ * returns 0 and data in `rep' if succesful, otherwise -1
+ */
+
+static int
+recv_loop (int fd,
+ time_t tmout,
+ int udp,
+ size_t limit,
+ krb5_data *rep)
+{
+ fd_set fdset;
+ struct timeval timeout;
+ int ret;
+ int nbytes;
+
+ if (fd >= FD_SETSIZE) {
+ return -1;
+ }
+
+ krb5_data_zero(rep);
+ do {
+ FD_ZERO(&fdset);
+ FD_SET(fd, &fdset);
+ timeout.tv_sec = tmout;
+ timeout.tv_usec = 0;
+ ret = select (fd + 1, &fdset, NULL, NULL, &timeout);
+ if (ret < 0) {
+ if (errno == EINTR)
+ continue;
+ return -1;
+ } else if (ret == 0) {
+ return 0;
+ } else {
+ void *tmp;
+
+ if (ioctl (fd, FIONREAD, &nbytes) < 0) {
+ krb5_data_free (rep);
+ return -1;
+ }
+ if(nbytes <= 0)
+ return 0;
+
+ if (limit)
+ nbytes = min(nbytes, limit - rep->length);
+
+ tmp = realloc (rep->data, rep->length + nbytes);
+ if (tmp == NULL) {
+ krb5_data_free (rep);
+ return -1;
+ }
+ rep->data = tmp;
+ ret = recv (fd, (char*)tmp + rep->length, nbytes, 0);
+ if (ret < 0) {
+ krb5_data_free (rep);
+ return -1;
+ }
+ rep->length += ret;
+ }
+ } while(!udp && (limit == 0 || rep->length < limit));
+ return 0;
+}
+
+/*
+ * Send kerberos requests and receive a reply on a udp or any other kind
+ * of a datagram socket. See `recv_loop'.
+ */
+
+static int
+send_and_recv_udp(int fd,
+ time_t tmout,
+ const krb5_data *req,
+ krb5_data *rep)
+{
+ if (send (fd, req->data, req->length, 0) < 0)
+ return -1;
+
+ return recv_loop(fd, tmout, 1, 0, rep);
+}
+
+/*
+ * `send_and_recv' for a TCP (or any other stream) socket.
+ * Since there are no record limits on a stream socket the protocol here
+ * is to prepend the request with 4 bytes of its length and the reply
+ * is similarly encoded.
+ */
+
+static int
+send_and_recv_tcp(int fd,
+ time_t tmout,
+ const krb5_data *req,
+ krb5_data *rep)
+{
+ unsigned char len[4];
+ unsigned long rep_len;
+ krb5_data len_data;
+
+ _krb5_put_int(len, req->length, 4);
+ if(net_write(fd, len, sizeof(len)) < 0)
+ return -1;
+ if(net_write(fd, req->data, req->length) < 0)
+ return -1;
+ if (recv_loop (fd, tmout, 0, 4, &len_data) < 0)
+ return -1;
+ if (len_data.length != 4) {
+ krb5_data_free (&len_data);
+ return -1;
+ }
+ _krb5_get_int(len_data.data, &rep_len, 4);
+ krb5_data_free (&len_data);
+ if (recv_loop (fd, tmout, 0, rep_len, rep) < 0)
+ return -1;
+ if(rep->length != rep_len) {
+ krb5_data_free (rep);
+ return -1;
+ }
+ return 0;
+}
+
+int
+_krb5_send_and_recv_tcp(int fd,
+ time_t tmout,
+ const krb5_data *req,
+ krb5_data *rep)
+{
+ return send_and_recv_tcp(fd, tmout, req, rep);
+}
+
+/*
+ * `send_and_recv' tailored for the HTTP protocol.
+ */
+
+static int
+send_and_recv_http(int fd,
+ time_t tmout,
+ const char *prefix,
+ const krb5_data *req,
+ krb5_data *rep)
+{
+ char *request;
+ char *str;
+ int ret;
+ int len = base64_encode(req->data, req->length, &str);
+
+ if(len < 0)
+ return -1;
+ asprintf(&request, "GET %s%s HTTP/1.0\r\n\r\n", prefix, str);
+ free(str);
+ if (request == NULL)
+ return -1;
+ ret = net_write (fd, request, strlen(request));
+ free (request);
+ if (ret < 0)
+ return ret;
+ ret = recv_loop(fd, tmout, 0, 0, rep);
+ if(ret)
+ return ret;
+ {
+ unsigned long rep_len;
+ char *s, *p;
+
+ s = realloc(rep->data, rep->length + 1);
+ if (s == NULL) {
+ krb5_data_free (rep);
+ return -1;
+ }
+ s[rep->length] = 0;
+ p = strstr(s, "\r\n\r\n");
+ if(p == NULL) {
+ krb5_data_zero(rep);
+ free(s);
+ return -1;
+ }
+ p += 4;
+ rep->data = s;
+ rep->length -= p - s;
+ if(rep->length < 4) { /* remove length */
+ krb5_data_zero(rep);
+ free(s);
+ return -1;
+ }
+ rep->length -= 4;
+ _krb5_get_int(p, &rep_len, 4);
+ if (rep_len != rep->length) {
+ krb5_data_zero(rep);
+ free(s);
+ return -1;
+ }
+ memmove(rep->data, p + 4, rep->length);
+ }
+ return 0;
+}
+
+static int
+init_port(const char *s, int fallback)
+{
+ if (s) {
+ int tmp;
+
+ sscanf (s, "%d", &tmp);
+ return htons(tmp);
+ } else
+ return fallback;
+}
+
+/*
+ * Return 0 if succesful, otherwise 1
+ */
+
+static int
+send_via_proxy (krb5_context context,
+ const krb5_krbhst_info *hi,
+ const krb5_data *send_data,
+ krb5_data *receive)
+{
+ char *proxy2 = strdup(context->http_proxy);
+ char *proxy = proxy2;
+ char *prefix;
+ char *colon;
+ struct addrinfo hints;
+ struct addrinfo *ai, *a;
+ int ret;
+ int s = -1;
+ char portstr[NI_MAXSERV];
+
+ if (proxy == NULL)
+ return ENOMEM;
+ if (strncmp (proxy, "http://", 7) == 0)
+ proxy += 7;
+
+ colon = strchr(proxy, ':');
+ if(colon != NULL)
+ *colon++ = '\0';
+ memset (&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ snprintf (portstr, sizeof(portstr), "%d",
+ ntohs(init_port (colon, htons(80))));
+ ret = getaddrinfo (proxy, portstr, &hints, &ai);
+ free (proxy2);
+ if (ret)
+ return krb5_eai_to_heim_errno(ret, errno);
+
+ for (a = ai; a != NULL; a = a->ai_next) {
+ s = socket (a->ai_family, a->ai_socktype, a->ai_protocol | SOCK_CLOEXEC);
+ if (s < 0)
+ continue;
+ rk_cloexec(s);
+ if (connect (s, a->ai_addr, a->ai_addrlen) < 0) {
+ close (s);
+ continue;
+ }
+ break;
+ }
+ if (a == NULL) {
+ freeaddrinfo (ai);
+ return 1;
+ }
+ freeaddrinfo (ai);
+
+ asprintf(&prefix, "http://%s/", hi->hostname);
+ if(prefix == NULL) {
+ close(s);
+ return 1;
+ }
+ ret = send_and_recv_http(s, context->kdc_timeout,
+ prefix, send_data, receive);
+ close (s);
+ free(prefix);
+ if(ret == 0 && receive->length != 0)
+ return 0;
+ return 1;
+}
+
+static krb5_error_code
+send_via_plugin(krb5_context context,
+ krb5_krbhst_info *hi,
+ time_t timeout,
+ const krb5_data *send_data,
+ krb5_data *receive)
+{
+ struct krb5_plugin *list = NULL, *e;
+ krb5_error_code ret;
+
+ ret = _krb5_plugin_find(context, PLUGIN_TYPE_DATA, KRB5_PLUGIN_SEND_TO_KDC, &list);
+ if(ret != 0 || list == NULL)
+ return KRB5_PLUGIN_NO_HANDLE;
+
+ for (e = list; e != NULL; e = _krb5_plugin_get_next(e)) {
+ krb5plugin_send_to_kdc_ftable *service;
+ void *ctx;
+
+ service = _krb5_plugin_get_symbol(e);
+ if (service->minor_version != 0)
+ continue;
+
+ (*service->init)(context, &ctx);
+ ret = (*service->send_to_kdc)(context, ctx, hi,
+ timeout, send_data, receive);
+ (*service->fini)(ctx);
+ if (ret == 0)
+ break;
+ if (ret != KRB5_PLUGIN_NO_HANDLE) {
+ krb5_set_error_message(context, ret,
+ N_("Plugin send_to_kdc failed to "
+ "lookup with error: %d", ""), ret);
+ break;
+ }
+ }
+ _krb5_plugin_free(list);
+ return KRB5_PLUGIN_NO_HANDLE;
+}
+
+
+/*
+ * Send the data `send' to one host from `handle` and get back the reply
+ * in `receive'.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_sendto (krb5_context context,
+ const krb5_data *send_data,
+ krb5_krbhst_handle handle,
+ krb5_data *receive)
+{
+ krb5_error_code ret;
+ int fd;
+ int i;
+
+ krb5_data_zero(receive);
+
+ for (i = 0; i < context->max_retries; ++i) {
+ krb5_krbhst_info *hi;
+
+ while (krb5_krbhst_next(context, handle, &hi) == 0) {
+ struct addrinfo *ai, *a;
+
+ if (context->send_to_kdc) {
+ struct send_to_kdc *s = context->send_to_kdc;
+
+ ret = (*s->func)(context, s->data,
+ hi, context->kdc_timeout, send_data, receive);
+ if (ret == 0 && receive->length != 0)
+ goto out;
+ continue;
+ }
+
+ ret = send_via_plugin(context, hi, context->kdc_timeout,
+ send_data, receive);
+ if (ret == 0 && receive->length != 0)
+ goto out;
+ else if (ret != KRB5_PLUGIN_NO_HANDLE)
+ continue;
+
+ if(hi->proto == KRB5_KRBHST_HTTP && context->http_proxy) {
+ if (send_via_proxy (context, hi, send_data, receive) == 0) {
+ ret = 0;
+ goto out;
+ }
+ continue;
+ }
+
+ ret = krb5_krbhst_get_addrinfo(context, hi, &ai);
+ if (ret)
+ continue;
+
+ for (a = ai; a != NULL; a = a->ai_next) {
+ fd = socket (a->ai_family, a->ai_socktype | SOCK_CLOEXEC, a->ai_protocol);
+ if (fd < 0)
+ continue;
+ rk_cloexec(fd);
+ if (connect (fd, a->ai_addr, a->ai_addrlen) < 0) {
+ close (fd);
+ continue;
+ }
+ switch (hi->proto) {
+ case KRB5_KRBHST_HTTP :
+ ret = send_and_recv_http(fd, context->kdc_timeout,
+ "", send_data, receive);
+ break;
+ case KRB5_KRBHST_TCP :
+ ret = send_and_recv_tcp (fd, context->kdc_timeout,
+ send_data, receive);
+ break;
+ case KRB5_KRBHST_UDP :
+ ret = send_and_recv_udp (fd, context->kdc_timeout,
+ send_data, receive);
+ break;
+ }
+ close (fd);
+ if(ret == 0 && receive->length != 0)
+ goto out;
+ }
+ }
+ krb5_krbhst_reset(context, handle);
+ }
+ krb5_clear_error_message (context);
+ ret = KRB5_KDC_UNREACH;
+out:
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_sendto_kdc(krb5_context context,
+ const krb5_data *send_data,
+ const krb5_realm *realm,
+ krb5_data *receive)
+{
+ return krb5_sendto_kdc_flags(context, send_data, realm, receive, 0);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_sendto_kdc_flags(krb5_context context,
+ const krb5_data *send_data,
+ const krb5_realm *realm,
+ krb5_data *receive,
+ int flags)
+{
+ krb5_error_code ret;
+ krb5_sendto_ctx ctx;
+
+ ret = krb5_sendto_ctx_alloc(context, &ctx);
+ if (ret)
+ return ret;
+ krb5_sendto_ctx_add_flags(ctx, flags);
+ krb5_sendto_ctx_set_func(ctx, _krb5_kdc_retry, NULL);
+
+ ret = krb5_sendto_context(context, ctx, send_data, *realm, receive);
+ krb5_sendto_ctx_free(context, ctx);
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_set_send_to_kdc_func(krb5_context context,
+ krb5_send_to_kdc_func func,
+ void *data)
+{
+ free(context->send_to_kdc);
+ if (func == NULL) {
+ context->send_to_kdc = NULL;
+ return 0;
+ }
+
+ context->send_to_kdc = malloc(sizeof(*context->send_to_kdc));
+ if (context->send_to_kdc == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+
+ context->send_to_kdc->func = func;
+ context->send_to_kdc->data = data;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+_krb5_copy_send_to_kdc_func(krb5_context context, krb5_context to)
+{
+ if (context->send_to_kdc)
+ return krb5_set_send_to_kdc_func(to,
+ context->send_to_kdc->func,
+ context->send_to_kdc->data);
+ else
+ return krb5_set_send_to_kdc_func(to, NULL, NULL);
+}
+
+
+
+struct krb5_sendto_ctx_data {
+ int flags;
+ int type;
+ krb5_sendto_ctx_func func;
+ void *data;
+};
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_sendto_ctx_alloc(krb5_context context, krb5_sendto_ctx *ctx)
+{
+ *ctx = calloc(1, sizeof(**ctx));
+ if (*ctx == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ return 0;
+}
+
+void KRB5_LIB_FUNCTION
+krb5_sendto_ctx_add_flags(krb5_sendto_ctx ctx, int flags)
+{
+ ctx->flags |= flags;
+}
+
+int KRB5_LIB_FUNCTION
+krb5_sendto_ctx_get_flags(krb5_sendto_ctx ctx)
+{
+ return ctx->flags;
+}
+
+void KRB5_LIB_FUNCTION
+krb5_sendto_ctx_set_type(krb5_sendto_ctx ctx, int type)
+{
+ ctx->type = type;
+}
+
+
+void KRB5_LIB_FUNCTION
+krb5_sendto_ctx_set_func(krb5_sendto_ctx ctx,
+ krb5_sendto_ctx_func func,
+ void *data)
+{
+ ctx->func = func;
+ ctx->data = data;
+}
+
+void KRB5_LIB_FUNCTION
+krb5_sendto_ctx_free(krb5_context context, krb5_sendto_ctx ctx)
+{
+ memset(ctx, 0, sizeof(*ctx));
+ free(ctx);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_sendto_context(krb5_context context,
+ krb5_sendto_ctx ctx,
+ const krb5_data *send_data,
+ const krb5_realm realm,
+ krb5_data *receive)
+{
+ krb5_error_code ret;
+ krb5_krbhst_handle handle = NULL;
+ int type, freectx = 0;
+ int action;
+
+ krb5_data_zero(receive);
+
+ if (ctx == NULL) {
+ freectx = 1;
+ ret = krb5_sendto_ctx_alloc(context, &ctx);
+ if (ret)
+ return ret;
+ }
+
+ type = ctx->type;
+ if (type == 0) {
+ if ((ctx->flags & KRB5_KRBHST_FLAGS_MASTER) || context->use_admin_kdc)
+ type = KRB5_KRBHST_ADMIN;
+ else
+ type = KRB5_KRBHST_KDC;
+ }
+
+ if (send_data->length > context->large_msg_size)
+ ctx->flags |= KRB5_KRBHST_FLAGS_LARGE_MSG;
+
+ /* loop until we get back a appropriate response */
+
+ do {
+ action = KRB5_SENDTO_DONE;
+
+ krb5_data_free(receive);
+
+ if (handle == NULL) {
+ ret = krb5_krbhst_init_flags(context, realm, type,
+ ctx->flags, &handle);
+ if (ret) {
+ if (freectx)
+ krb5_sendto_ctx_free(context, ctx);
+ return ret;
+ }
+ }
+
+ ret = krb5_sendto(context, send_data, handle, receive);
+ if (ret)
+ break;
+ if (ctx->func) {
+ ret = (*ctx->func)(context, ctx, ctx->data, receive, &action);
+ if (ret)
+ break;
+ }
+ if (action != KRB5_SENDTO_CONTINUE) {
+ krb5_krbhst_free(context, handle);
+ handle = NULL;
+ }
+ } while (action != KRB5_SENDTO_DONE);
+ if (handle)
+ krb5_krbhst_free(context, handle);
+ if (ret == KRB5_KDC_UNREACH)
+ krb5_set_error_message(context, ret,
+ N_("unable to reach any KDC in realm %s", ""),
+ realm);
+ if (ret)
+ krb5_data_free(receive);
+ if (freectx)
+ krb5_sendto_ctx_free(context, ctx);
+ return ret;
+}
+
+krb5_error_code
+_krb5_kdc_retry(krb5_context context, krb5_sendto_ctx ctx, void *data,
+ const krb5_data *reply, int *action)
+{
+ krb5_error_code ret;
+ KRB_ERROR error;
+
+ if(krb5_rd_error(context, reply, &error))
+ return 0;
+
+ ret = krb5_error_from_rd_error(context, &error, NULL);
+ krb5_free_error_contents(context, &error);
+
+ switch(ret) {
+ case KRB5KRB_ERR_RESPONSE_TOO_BIG: {
+ if (krb5_sendto_ctx_get_flags(ctx) & KRB5_KRBHST_FLAGS_LARGE_MSG)
+ break;
+ krb5_sendto_ctx_add_flags(ctx, KRB5_KRBHST_FLAGS_LARGE_MSG);
+ *action = KRB5_SENDTO_RESTART;
+ break;
+ }
+ case KRB5KDC_ERR_SVC_UNAVAILABLE:
+ *action = KRB5_SENDTO_CONTINUE;
+ break;
+ }
+ return 0;
+}
diff --git a/source4/heimdal/lib/krb5/send_to_kdc_plugin.h b/source4/heimdal/lib/krb5/send_to_kdc_plugin.h
new file mode 100644
index 0000000000..c729a1286b
--- /dev/null
+++ b/source4/heimdal/lib/krb5/send_to_kdc_plugin.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef HEIMDAL_KRB5_SEND_TO_KDC_PLUGIN_H
+#define HEIMDAL_KRB5_SEND_TO_KDC_PLUGIN_H 1
+
+#include <krb5.h>
+
+#define KRB5_PLUGIN_SEND_TO_KDC "send_to_kdc"
+
+typedef krb5_error_code
+(*krb5plugin_send_to_kdc_func)(krb5_context,
+ void *,
+ krb5_krbhst_info *,
+ time_t timeout,
+ const krb5_data *,
+ krb5_data *);
+
+typedef struct krb5plugin_send_to_kdc_ftable {
+ int minor_version;
+ krb5_error_code (*init)(krb5_context, void **);
+ void (*fini)(void *);
+ krb5plugin_send_to_kdc_func send_to_kdc;
+} krb5plugin_send_to_kdc_ftable;
+
+#endif /* HEIMDAL_KRB5_SEND_TO_KDC_PLUGIN_H */
diff --git a/source4/heimdal/lib/krb5/set_default_realm.c b/source4/heimdal/lib/krb5/set_default_realm.c
new file mode 100644
index 0000000000..6907b11d10
--- /dev/null
+++ b/source4/heimdal/lib/krb5/set_default_realm.c
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+/*
+ * Convert the simple string `s' into a NULL-terminated and freshly allocated
+ * list in `list'. Return an error code.
+ */
+
+static krb5_error_code
+string_to_list (krb5_context context, const char *s, krb5_realm **list)
+{
+
+ *list = malloc (2 * sizeof(**list));
+ if (*list == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ (*list)[0] = strdup (s);
+ if ((*list)[0] == NULL) {
+ free (*list);
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ (*list)[1] = NULL;
+ return 0;
+}
+
+/*
+ * Set the knowledge of the default realm(s) in `context'.
+ * If realm != NULL, that's the new default realm.
+ * Otherwise, the realm(s) are figured out from configuration or DNS.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_set_default_realm(krb5_context context,
+ const char *realm)
+{
+ krb5_error_code ret = 0;
+ krb5_realm *realms = NULL;
+
+ if (realm == NULL) {
+ realms = krb5_config_get_strings (context, NULL,
+ "libdefaults",
+ "default_realm",
+ NULL);
+ if (realms == NULL)
+ ret = krb5_get_host_realm(context, NULL, &realms);
+ } else {
+ ret = string_to_list (context, realm, &realms);
+ }
+ if (ret)
+ return ret;
+ krb5_free_host_realm (context, context->default_realms);
+ context->default_realms = realms;
+ return 0;
+}
diff --git a/source4/heimdal/lib/krb5/store-int.h b/source4/heimdal/lib/krb5/store-int.h
new file mode 100644
index 0000000000..8489f98453
--- /dev/null
+++ b/source4/heimdal/lib/krb5/store-int.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2002 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef __store_int_h__
+#define __store_int_h__
+
+struct krb5_storage_data {
+ void *data;
+ ssize_t (*fetch)(struct krb5_storage_data*, void*, size_t);
+ ssize_t (*store)(struct krb5_storage_data*, const void*, size_t);
+ off_t (*seek)(struct krb5_storage_data*, off_t, int);
+ void (*free)(struct krb5_storage_data*);
+ krb5_flags flags;
+ int eof_code;
+};
+
+#endif /* __store_int_h__ */
diff --git a/source4/heimdal/lib/krb5/store.c b/source4/heimdal/lib/krb5/store.c
new file mode 100644
index 0000000000..47f9abe1de
--- /dev/null
+++ b/source4/heimdal/lib/krb5/store.c
@@ -0,0 +1,1035 @@
+/*
+ * Copyright (c) 1997-2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+#include "store-int.h"
+
+RCSID("$Id$");
+
+#define BYTEORDER_IS(SP, V) (((SP)->flags & KRB5_STORAGE_BYTEORDER_MASK) == (V))
+#define BYTEORDER_IS_LE(SP) BYTEORDER_IS((SP), KRB5_STORAGE_BYTEORDER_LE)
+#define BYTEORDER_IS_BE(SP) BYTEORDER_IS((SP), KRB5_STORAGE_BYTEORDER_BE)
+#define BYTEORDER_IS_HOST(SP) (BYTEORDER_IS((SP), KRB5_STORAGE_BYTEORDER_HOST) || \
+ krb5_storage_is_flags((SP), KRB5_STORAGE_HOST_BYTEORDER))
+
+void KRB5_LIB_FUNCTION
+krb5_storage_set_flags(krb5_storage *sp, krb5_flags flags)
+{
+ sp->flags |= flags;
+}
+
+void KRB5_LIB_FUNCTION
+krb5_storage_clear_flags(krb5_storage *sp, krb5_flags flags)
+{
+ sp->flags &= ~flags;
+}
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_storage_is_flags(krb5_storage *sp, krb5_flags flags)
+{
+ return (sp->flags & flags) == flags;
+}
+
+void KRB5_LIB_FUNCTION
+krb5_storage_set_byteorder(krb5_storage *sp, krb5_flags byteorder)
+{
+ sp->flags &= ~KRB5_STORAGE_BYTEORDER_MASK;
+ sp->flags |= byteorder;
+}
+
+krb5_flags KRB5_LIB_FUNCTION
+krb5_storage_get_byteorder(krb5_storage *sp, krb5_flags byteorder)
+{
+ return sp->flags & KRB5_STORAGE_BYTEORDER_MASK;
+}
+
+off_t KRB5_LIB_FUNCTION
+krb5_storage_seek(krb5_storage *sp, off_t offset, int whence)
+{
+ return (*sp->seek)(sp, offset, whence);
+}
+
+krb5_ssize_t KRB5_LIB_FUNCTION
+krb5_storage_read(krb5_storage *sp, void *buf, size_t len)
+{
+ return sp->fetch(sp, buf, len);
+}
+
+krb5_ssize_t KRB5_LIB_FUNCTION
+krb5_storage_write(krb5_storage *sp, const void *buf, size_t len)
+{
+ return sp->store(sp, buf, len);
+}
+
+void KRB5_LIB_FUNCTION
+krb5_storage_set_eof_code(krb5_storage *sp, int code)
+{
+ sp->eof_code = code;
+}
+
+krb5_ssize_t KRB5_LIB_FUNCTION
+_krb5_put_int(void *buffer, unsigned long value, size_t size)
+{
+ unsigned char *p = buffer;
+ int i;
+ for (i = size - 1; i >= 0; i--) {
+ p[i] = value & 0xff;
+ value >>= 8;
+ }
+ return size;
+}
+
+krb5_ssize_t KRB5_LIB_FUNCTION
+_krb5_get_int(void *buffer, unsigned long *value, size_t size)
+{
+ unsigned char *p = buffer;
+ unsigned long v = 0;
+ int i;
+ for (i = 0; i < size; i++)
+ v = (v << 8) + p[i];
+ *value = v;
+ return size;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_storage_free(krb5_storage *sp)
+{
+ if(sp->free)
+ (*sp->free)(sp);
+ free(sp->data);
+ free(sp);
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_storage_to_data(krb5_storage *sp, krb5_data *data)
+{
+ off_t pos;
+ size_t size;
+ krb5_error_code ret;
+
+ pos = sp->seek(sp, 0, SEEK_CUR);
+ size = (size_t)sp->seek(sp, 0, SEEK_END);
+ ret = krb5_data_alloc (data, size);
+ if (ret) {
+ sp->seek(sp, pos, SEEK_SET);
+ return ret;
+ }
+ if (size) {
+ sp->seek(sp, 0, SEEK_SET);
+ sp->fetch(sp, data->data, data->length);
+ sp->seek(sp, pos, SEEK_SET);
+ }
+ return 0;
+}
+
+static krb5_error_code
+krb5_store_int(krb5_storage *sp,
+ int32_t value,
+ size_t len)
+{
+ int ret;
+ unsigned char v[16];
+
+ if(len > sizeof(v))
+ return EINVAL;
+ _krb5_put_int(v, value, len);
+ ret = sp->store(sp, v, len);
+ if (ret != len)
+ return (ret<0)?errno:sp->eof_code;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_store_int32(krb5_storage *sp,
+ int32_t value)
+{
+ if(BYTEORDER_IS_HOST(sp))
+ value = htonl(value);
+ else if(BYTEORDER_IS_LE(sp))
+ value = bswap32(value);
+ return krb5_store_int(sp, value, 4);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_store_uint32(krb5_storage *sp,
+ uint32_t value)
+{
+ return krb5_store_int32(sp, (int32_t)value);
+}
+
+static krb5_error_code
+krb5_ret_int(krb5_storage *sp,
+ int32_t *value,
+ size_t len)
+{
+ int ret;
+ unsigned char v[4];
+ unsigned long w;
+ ret = sp->fetch(sp, v, len);
+ if(ret != len)
+ return (ret<0)?errno:sp->eof_code;
+ _krb5_get_int(v, &w, len);
+ *value = w;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_ret_int32(krb5_storage *sp,
+ int32_t *value)
+{
+ krb5_error_code ret = krb5_ret_int(sp, value, 4);
+ if(ret)
+ return ret;
+ if(BYTEORDER_IS_HOST(sp))
+ *value = htonl(*value);
+ else if(BYTEORDER_IS_LE(sp))
+ *value = bswap32(*value);
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_ret_uint32(krb5_storage *sp,
+ uint32_t *value)
+{
+ krb5_error_code ret;
+ int32_t v;
+
+ ret = krb5_ret_int32(sp, &v);
+ if (ret == 0)
+ *value = (uint32_t)v;
+
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_store_int16(krb5_storage *sp,
+ int16_t value)
+{
+ if(BYTEORDER_IS_HOST(sp))
+ value = htons(value);
+ else if(BYTEORDER_IS_LE(sp))
+ value = bswap16(value);
+ return krb5_store_int(sp, value, 2);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_store_uint16(krb5_storage *sp,
+ uint16_t value)
+{
+ return krb5_store_int16(sp, (int16_t)value);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_ret_int16(krb5_storage *sp,
+ int16_t *value)
+{
+ int32_t v;
+ int ret;
+ ret = krb5_ret_int(sp, &v, 2);
+ if(ret)
+ return ret;
+ *value = v;
+ if(BYTEORDER_IS_HOST(sp))
+ *value = htons(*value);
+ else if(BYTEORDER_IS_LE(sp))
+ *value = bswap16(*value);
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_ret_uint16(krb5_storage *sp,
+ uint16_t *value)
+{
+ krb5_error_code ret;
+ int16_t v;
+
+ ret = krb5_ret_int16(sp, &v);
+ if (ret == 0)
+ *value = (uint16_t)v;
+
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_store_int8(krb5_storage *sp,
+ int8_t value)
+{
+ int ret;
+
+ ret = sp->store(sp, &value, sizeof(value));
+ if (ret != sizeof(value))
+ return (ret<0)?errno:sp->eof_code;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_store_uint8(krb5_storage *sp,
+ uint8_t value)
+{
+ return krb5_store_int8(sp, (int8_t)value);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_ret_int8(krb5_storage *sp,
+ int8_t *value)
+{
+ int ret;
+
+ ret = sp->fetch(sp, value, sizeof(*value));
+ if (ret != sizeof(*value))
+ return (ret<0)?errno:sp->eof_code;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_ret_uint8(krb5_storage *sp,
+ uint8_t *value)
+{
+ krb5_error_code ret;
+ int8_t v;
+
+ ret = krb5_ret_int8(sp, &v);
+ if (ret == 0)
+ *value = (uint8_t)v;
+
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_store_data(krb5_storage *sp,
+ krb5_data data)
+{
+ int ret;
+ ret = krb5_store_int32(sp, data.length);
+ if(ret < 0)
+ return ret;
+ ret = sp->store(sp, data.data, data.length);
+ if(ret != data.length){
+ if(ret < 0)
+ return errno;
+ return sp->eof_code;
+ }
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_ret_data(krb5_storage *sp,
+ krb5_data *data)
+{
+ int ret;
+ int32_t size;
+
+ ret = krb5_ret_int32(sp, &size);
+ if(ret)
+ return ret;
+ ret = krb5_data_alloc (data, size);
+ if (ret)
+ return ret;
+ if (size) {
+ ret = sp->fetch(sp, data->data, size);
+ if(ret != size)
+ return (ret < 0)? errno : sp->eof_code;
+ }
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_store_string(krb5_storage *sp, const char *s)
+{
+ krb5_data data;
+ data.length = strlen(s);
+ data.data = rk_UNCONST(s);
+ return krb5_store_data(sp, data);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_ret_string(krb5_storage *sp,
+ char **string)
+{
+ int ret;
+ krb5_data data;
+ ret = krb5_ret_data(sp, &data);
+ if(ret)
+ return ret;
+ *string = realloc(data.data, data.length + 1);
+ if(*string == NULL){
+ free(data.data);
+ return ENOMEM;
+ }
+ (*string)[data.length] = 0;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_store_stringz(krb5_storage *sp, const char *s)
+{
+ size_t len = strlen(s) + 1;
+ ssize_t ret;
+
+ ret = sp->store(sp, s, len);
+ if(ret != len) {
+ if(ret < 0)
+ return ret;
+ else
+ return sp->eof_code;
+ }
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_ret_stringz(krb5_storage *sp,
+ char **string)
+{
+ char c;
+ char *s = NULL;
+ size_t len = 0;
+ ssize_t ret;
+
+ while((ret = sp->fetch(sp, &c, 1)) == 1){
+ char *tmp;
+
+ len++;
+ tmp = realloc (s, len);
+ if (tmp == NULL) {
+ free (s);
+ return ENOMEM;
+ }
+ s = tmp;
+ s[len - 1] = c;
+ if(c == 0)
+ break;
+ }
+ if(ret != 1){
+ free(s);
+ if(ret == 0)
+ return sp->eof_code;
+ return ret;
+ }
+ *string = s;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_store_stringnl(krb5_storage *sp, const char *s)
+{
+ size_t len = strlen(s);
+ ssize_t ret;
+
+ ret = sp->store(sp, s, len);
+ if(ret != len) {
+ if(ret < 0)
+ return ret;
+ else
+ return sp->eof_code;
+ }
+ ret = sp->store(sp, "\n", 1);
+ if(ret != 1) {
+ if(ret < 0)
+ return ret;
+ else
+ return sp->eof_code;
+ }
+
+ return 0;
+
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_ret_stringnl(krb5_storage *sp,
+ char **string)
+{
+ int expect_nl = 0;
+ char c;
+ char *s = NULL;
+ size_t len = 0;
+ ssize_t ret;
+
+ while((ret = sp->fetch(sp, &c, 1)) == 1){
+ char *tmp;
+
+ if (c == '\r') {
+ expect_nl = 1;
+ continue;
+ }
+ if (expect_nl && c != '\n') {
+ free(s);
+ return KRB5_BADMSGTYPE;
+ }
+
+ len++;
+ tmp = realloc (s, len);
+ if (tmp == NULL) {
+ free (s);
+ return ENOMEM;
+ }
+ s = tmp;
+ if(c == '\n') {
+ s[len - 1] = '\0';
+ break;
+ }
+ s[len - 1] = c;
+ }
+ if(ret != 1){
+ free(s);
+ if(ret == 0)
+ return sp->eof_code;
+ return ret;
+ }
+ *string = s;
+ return 0;
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_store_principal(krb5_storage *sp,
+ krb5_const_principal p)
+{
+ int i;
+ int ret;
+
+ if(!krb5_storage_is_flags(sp, KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE)) {
+ ret = krb5_store_int32(sp, p->name.name_type);
+ if(ret) return ret;
+ }
+ if(krb5_storage_is_flags(sp, KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS))
+ ret = krb5_store_int32(sp, p->name.name_string.len + 1);
+ else
+ ret = krb5_store_int32(sp, p->name.name_string.len);
+
+ if(ret) return ret;
+ ret = krb5_store_string(sp, p->realm);
+ if(ret) return ret;
+ for(i = 0; i < p->name.name_string.len; i++){
+ ret = krb5_store_string(sp, p->name.name_string.val[i]);
+ if(ret) return ret;
+ }
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_ret_principal(krb5_storage *sp,
+ krb5_principal *princ)
+{
+ int i;
+ int ret;
+ krb5_principal p;
+ int32_t type;
+ int32_t ncomp;
+
+ p = calloc(1, sizeof(*p));
+ if(p == NULL)
+ return ENOMEM;
+
+ if(krb5_storage_is_flags(sp, KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE))
+ type = KRB5_NT_UNKNOWN;
+ else if((ret = krb5_ret_int32(sp, &type))){
+ free(p);
+ return ret;
+ }
+ if((ret = krb5_ret_int32(sp, &ncomp))){
+ free(p);
+ return ret;
+ }
+ if(krb5_storage_is_flags(sp, KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS))
+ ncomp--;
+ if (ncomp < 0) {
+ free(p);
+ return EINVAL;
+ }
+ p->name.name_type = type;
+ p->name.name_string.len = ncomp;
+ ret = krb5_ret_string(sp, &p->realm);
+ if(ret) {
+ free(p);
+ return ret;
+ }
+ p->name.name_string.val = calloc(ncomp, sizeof(*p->name.name_string.val));
+ if(p->name.name_string.val == NULL && ncomp != 0){
+ free(p->realm);
+ free(p);
+ return ENOMEM;
+ }
+ for(i = 0; i < ncomp; i++){
+ ret = krb5_ret_string(sp, &p->name.name_string.val[i]);
+ if(ret) {
+ while (i >= 0)
+ free(p->name.name_string.val[i--]);
+ free(p->realm);
+ free(p);
+ return ret;
+ }
+ }
+ *princ = p;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_store_keyblock(krb5_storage *sp, krb5_keyblock p)
+{
+ int ret;
+ ret = krb5_store_int16(sp, p.keytype);
+ if(ret) return ret;
+
+ if(krb5_storage_is_flags(sp, KRB5_STORAGE_KEYBLOCK_KEYTYPE_TWICE)){
+ /* this should really be enctype, but it is the same as
+ keytype nowadays */
+ ret = krb5_store_int16(sp, p.keytype);
+ if(ret) return ret;
+ }
+
+ ret = krb5_store_data(sp, p.keyvalue);
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_ret_keyblock(krb5_storage *sp, krb5_keyblock *p)
+{
+ int ret;
+ int16_t tmp;
+
+ ret = krb5_ret_int16(sp, &tmp);
+ if(ret) return ret;
+ p->keytype = tmp;
+
+ if(krb5_storage_is_flags(sp, KRB5_STORAGE_KEYBLOCK_KEYTYPE_TWICE)){
+ ret = krb5_ret_int16(sp, &tmp);
+ if(ret) return ret;
+ }
+
+ ret = krb5_ret_data(sp, &p->keyvalue);
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_store_times(krb5_storage *sp, krb5_times times)
+{
+ int ret;
+ ret = krb5_store_int32(sp, times.authtime);
+ if(ret) return ret;
+ ret = krb5_store_int32(sp, times.starttime);
+ if(ret) return ret;
+ ret = krb5_store_int32(sp, times.endtime);
+ if(ret) return ret;
+ ret = krb5_store_int32(sp, times.renew_till);
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_ret_times(krb5_storage *sp, krb5_times *times)
+{
+ int ret;
+ int32_t tmp;
+ ret = krb5_ret_int32(sp, &tmp);
+ times->authtime = tmp;
+ if(ret) return ret;
+ ret = krb5_ret_int32(sp, &tmp);
+ times->starttime = tmp;
+ if(ret) return ret;
+ ret = krb5_ret_int32(sp, &tmp);
+ times->endtime = tmp;
+ if(ret) return ret;
+ ret = krb5_ret_int32(sp, &tmp);
+ times->renew_till = tmp;
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_store_address(krb5_storage *sp, krb5_address p)
+{
+ int ret;
+ ret = krb5_store_int16(sp, p.addr_type);
+ if(ret) return ret;
+ ret = krb5_store_data(sp, p.address);
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_ret_address(krb5_storage *sp, krb5_address *adr)
+{
+ int16_t t;
+ int ret;
+ ret = krb5_ret_int16(sp, &t);
+ if(ret) return ret;
+ adr->addr_type = t;
+ ret = krb5_ret_data(sp, &adr->address);
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_store_addrs(krb5_storage *sp, krb5_addresses p)
+{
+ int i;
+ int ret;
+ ret = krb5_store_int32(sp, p.len);
+ if(ret) return ret;
+ for(i = 0; i<p.len; i++){
+ ret = krb5_store_address(sp, p.val[i]);
+ if(ret) break;
+ }
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_ret_addrs(krb5_storage *sp, krb5_addresses *adr)
+{
+ int i;
+ int ret;
+ int32_t tmp;
+
+ ret = krb5_ret_int32(sp, &tmp);
+ if(ret) return ret;
+ adr->len = tmp;
+ ALLOC(adr->val, adr->len);
+ if (adr->val == NULL && adr->len != 0)
+ return ENOMEM;
+ for(i = 0; i < adr->len; i++){
+ ret = krb5_ret_address(sp, &adr->val[i]);
+ if(ret) break;
+ }
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_store_authdata(krb5_storage *sp, krb5_authdata auth)
+{
+ krb5_error_code ret;
+ int i;
+ ret = krb5_store_int32(sp, auth.len);
+ if(ret) return ret;
+ for(i = 0; i < auth.len; i++){
+ ret = krb5_store_int16(sp, auth.val[i].ad_type);
+ if(ret) break;
+ ret = krb5_store_data(sp, auth.val[i].ad_data);
+ if(ret) break;
+ }
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_ret_authdata(krb5_storage *sp, krb5_authdata *auth)
+{
+ krb5_error_code ret;
+ int32_t tmp;
+ int16_t tmp2;
+ int i;
+ ret = krb5_ret_int32(sp, &tmp);
+ if(ret) return ret;
+ ALLOC_SEQ(auth, tmp);
+ if (auth->val == NULL && tmp != 0)
+ return ENOMEM;
+ for(i = 0; i < tmp; i++){
+ ret = krb5_ret_int16(sp, &tmp2);
+ if(ret) break;
+ auth->val[i].ad_type = tmp2;
+ ret = krb5_ret_data(sp, &auth->val[i].ad_data);
+ if(ret) break;
+ }
+ return ret;
+}
+
+static int32_t
+bitswap32(int32_t b)
+{
+ int32_t r = 0;
+ int i;
+ for (i = 0; i < 32; i++) {
+ r = r << 1 | (b & 1);
+ b = b >> 1;
+ }
+ return r;
+}
+
+
+/*
+ *
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_store_creds(krb5_storage *sp, krb5_creds *creds)
+{
+ int ret;
+
+ ret = krb5_store_principal(sp, creds->client);
+ if(ret)
+ return ret;
+ ret = krb5_store_principal(sp, creds->server);
+ if(ret)
+ return ret;
+ ret = krb5_store_keyblock(sp, creds->session);
+ if(ret)
+ return ret;
+ ret = krb5_store_times(sp, creds->times);
+ if(ret)
+ return ret;
+ ret = krb5_store_int8(sp, creds->second_ticket.length != 0); /* is_skey */
+ if(ret)
+ return ret;
+
+ if(krb5_storage_is_flags(sp, KRB5_STORAGE_CREDS_FLAGS_WRONG_BITORDER))
+ ret = krb5_store_int32(sp, creds->flags.i);
+ else
+ ret = krb5_store_int32(sp, bitswap32(TicketFlags2int(creds->flags.b)));
+ if(ret)
+ return ret;
+
+ ret = krb5_store_addrs(sp, creds->addresses);
+ if(ret)
+ return ret;
+ ret = krb5_store_authdata(sp, creds->authdata);
+ if(ret)
+ return ret;
+ ret = krb5_store_data(sp, creds->ticket);
+ if(ret)
+ return ret;
+ ret = krb5_store_data(sp, creds->second_ticket);
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_ret_creds(krb5_storage *sp, krb5_creds *creds)
+{
+ krb5_error_code ret;
+ int8_t dummy8;
+ int32_t dummy32;
+
+ memset(creds, 0, sizeof(*creds));
+ ret = krb5_ret_principal (sp, &creds->client);
+ if(ret) goto cleanup;
+ ret = krb5_ret_principal (sp, &creds->server);
+ if(ret) goto cleanup;
+ ret = krb5_ret_keyblock (sp, &creds->session);
+ if(ret) goto cleanup;
+ ret = krb5_ret_times (sp, &creds->times);
+ if(ret) goto cleanup;
+ ret = krb5_ret_int8 (sp, &dummy8);
+ if(ret) goto cleanup;
+ ret = krb5_ret_int32 (sp, &dummy32);
+ if(ret) goto cleanup;
+ /*
+ * Runtime detect the what is the higher bits of the bitfield. If
+ * any of the higher bits are set in the input data, it's either a
+ * new ticket flag (and this code need to be removed), or it's a
+ * MIT cache (or new Heimdal cache), lets change it to our current
+ * format.
+ */
+ {
+ uint32_t mask = 0xffff0000;
+ creds->flags.i = 0;
+ creds->flags.b.anonymous = 1;
+ if (creds->flags.i & mask)
+ mask = ~mask;
+ if (dummy32 & mask)
+ dummy32 = bitswap32(dummy32);
+ }
+ creds->flags.i = dummy32;
+ ret = krb5_ret_addrs (sp, &creds->addresses);
+ if(ret) goto cleanup;
+ ret = krb5_ret_authdata (sp, &creds->authdata);
+ if(ret) goto cleanup;
+ ret = krb5_ret_data (sp, &creds->ticket);
+ if(ret) goto cleanup;
+ ret = krb5_ret_data (sp, &creds->second_ticket);
+cleanup:
+ if(ret) {
+#if 0
+ krb5_free_cred_contents(context, creds); /* XXX */
+#endif
+ }
+ return ret;
+}
+
+#define SC_CLIENT_PRINCIPAL 0x0001
+#define SC_SERVER_PRINCIPAL 0x0002
+#define SC_SESSION_KEY 0x0004
+#define SC_TICKET 0x0008
+#define SC_SECOND_TICKET 0x0010
+#define SC_AUTHDATA 0x0020
+#define SC_ADDRESSES 0x0040
+
+/*
+ *
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_store_creds_tag(krb5_storage *sp, krb5_creds *creds)
+{
+ int ret;
+ int32_t header = 0;
+
+ if (creds->client)
+ header |= SC_CLIENT_PRINCIPAL;
+ if (creds->server)
+ header |= SC_SERVER_PRINCIPAL;
+ if (creds->session.keytype != ETYPE_NULL)
+ header |= SC_SESSION_KEY;
+ if (creds->ticket.data)
+ header |= SC_TICKET;
+ if (creds->second_ticket.length)
+ header |= SC_SECOND_TICKET;
+ if (creds->authdata.len)
+ header |= SC_AUTHDATA;
+ if (creds->addresses.len)
+ header |= SC_ADDRESSES;
+
+ ret = krb5_store_int32(sp, header);
+
+ if (creds->client) {
+ ret = krb5_store_principal(sp, creds->client);
+ if(ret)
+ return ret;
+ }
+
+ if (creds->server) {
+ ret = krb5_store_principal(sp, creds->server);
+ if(ret)
+ return ret;
+ }
+
+ if (creds->session.keytype != ETYPE_NULL) {
+ ret = krb5_store_keyblock(sp, creds->session);
+ if(ret)
+ return ret;
+ }
+
+ ret = krb5_store_times(sp, creds->times);
+ if(ret)
+ return ret;
+ ret = krb5_store_int8(sp, creds->second_ticket.length != 0); /* is_skey */
+ if(ret)
+ return ret;
+
+ ret = krb5_store_int32(sp, bitswap32(TicketFlags2int(creds->flags.b)));
+ if(ret)
+ return ret;
+
+ if (creds->addresses.len) {
+ ret = krb5_store_addrs(sp, creds->addresses);
+ if(ret)
+ return ret;
+ }
+
+ if (creds->authdata.len) {
+ ret = krb5_store_authdata(sp, creds->authdata);
+ if(ret)
+ return ret;
+ }
+
+ if (creds->ticket.data) {
+ ret = krb5_store_data(sp, creds->ticket);
+ if(ret)
+ return ret;
+ }
+
+ if (creds->second_ticket.data) {
+ ret = krb5_store_data(sp, creds->second_ticket);
+ if (ret)
+ return ret;
+ }
+
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_ret_creds_tag(krb5_storage *sp,
+ krb5_creds *creds)
+{
+ krb5_error_code ret;
+ int8_t dummy8;
+ int32_t dummy32, header;
+
+ memset(creds, 0, sizeof(*creds));
+
+ ret = krb5_ret_int32 (sp, &header);
+ if (ret) goto cleanup;
+
+ if (header & SC_CLIENT_PRINCIPAL) {
+ ret = krb5_ret_principal (sp, &creds->client);
+ if(ret) goto cleanup;
+ }
+ if (header & SC_SERVER_PRINCIPAL) {
+ ret = krb5_ret_principal (sp, &creds->server);
+ if(ret) goto cleanup;
+ }
+ if (header & SC_SESSION_KEY) {
+ ret = krb5_ret_keyblock (sp, &creds->session);
+ if(ret) goto cleanup;
+ }
+ ret = krb5_ret_times (sp, &creds->times);
+ if(ret) goto cleanup;
+ ret = krb5_ret_int8 (sp, &dummy8);
+ if(ret) goto cleanup;
+ ret = krb5_ret_int32 (sp, &dummy32);
+ if(ret) goto cleanup;
+ /*
+ * Runtime detect the what is the higher bits of the bitfield. If
+ * any of the higher bits are set in the input data, it's either a
+ * new ticket flag (and this code need to be removed), or it's a
+ * MIT cache (or new Heimdal cache), lets change it to our current
+ * format.
+ */
+ {
+ uint32_t mask = 0xffff0000;
+ creds->flags.i = 0;
+ creds->flags.b.anonymous = 1;
+ if (creds->flags.i & mask)
+ mask = ~mask;
+ if (dummy32 & mask)
+ dummy32 = bitswap32(dummy32);
+ }
+ creds->flags.i = dummy32;
+ if (header & SC_ADDRESSES) {
+ ret = krb5_ret_addrs (sp, &creds->addresses);
+ if(ret) goto cleanup;
+ }
+ if (header & SC_AUTHDATA) {
+ ret = krb5_ret_authdata (sp, &creds->authdata);
+ if(ret) goto cleanup;
+ }
+ if (header & SC_TICKET) {
+ ret = krb5_ret_data (sp, &creds->ticket);
+ if(ret) goto cleanup;
+ }
+ if (header & SC_SECOND_TICKET) {
+ ret = krb5_ret_data (sp, &creds->second_ticket);
+ if(ret) goto cleanup;
+ }
+
+cleanup:
+ if(ret) {
+#if 0
+ krb5_free_cred_contents(context, creds); /* XXX */
+#endif
+ }
+ return ret;
+}
diff --git a/source4/heimdal/lib/krb5/store_emem.c b/source4/heimdal/lib/krb5/store_emem.c
new file mode 100644
index 0000000000..8a587600fd
--- /dev/null
+++ b/source4/heimdal/lib/krb5/store_emem.c
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 1997 - 2002 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+#include "store-int.h"
+
+RCSID("$Id$");
+
+typedef struct emem_storage{
+ unsigned char *base;
+ size_t size;
+ size_t len;
+ unsigned char *ptr;
+}emem_storage;
+
+static ssize_t
+emem_fetch(krb5_storage *sp, void *data, size_t size)
+{
+ emem_storage *s = (emem_storage*)sp->data;
+ if(s->base + s->len - s->ptr < size)
+ size = s->base + s->len - s->ptr;
+ memmove(data, s->ptr, size);
+ sp->seek(sp, size, SEEK_CUR);
+ return size;
+}
+
+static ssize_t
+emem_store(krb5_storage *sp, const void *data, size_t size)
+{
+ emem_storage *s = (emem_storage*)sp->data;
+ if(size > s->base + s->size - s->ptr){
+ void *base;
+ size_t sz, off;
+ off = s->ptr - s->base;
+ sz = off + size;
+ if (sz < 4096)
+ sz *= 2;
+ base = realloc(s->base, sz);
+ if(base == NULL)
+ return 0;
+ s->size = sz;
+ s->base = base;
+ s->ptr = (unsigned char*)base + off;
+ }
+ memmove(s->ptr, data, size);
+ sp->seek(sp, size, SEEK_CUR);
+ return size;
+}
+
+static off_t
+emem_seek(krb5_storage *sp, off_t offset, int whence)
+{
+ emem_storage *s = (emem_storage*)sp->data;
+ switch(whence){
+ case SEEK_SET:
+ if(offset > s->size)
+ offset = s->size;
+ if(offset < 0)
+ offset = 0;
+ s->ptr = s->base + offset;
+ if(offset > s->len)
+ s->len = offset;
+ break;
+ case SEEK_CUR:
+ sp->seek(sp,s->ptr - s->base + offset, SEEK_SET);
+ break;
+ case SEEK_END:
+ sp->seek(sp, s->len + offset, SEEK_SET);
+ break;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+ return s->ptr - s->base;
+}
+
+static void
+emem_free(krb5_storage *sp)
+{
+ emem_storage *s = sp->data;
+ memset(s->base, 0, s->len);
+ free(s->base);
+}
+
+krb5_storage * KRB5_LIB_FUNCTION
+krb5_storage_emem(void)
+{
+ krb5_storage *sp;
+ emem_storage *s;
+
+ sp = malloc(sizeof(krb5_storage));
+ if (sp == NULL)
+ return NULL;
+
+ s = malloc(sizeof(*s));
+ if (s == NULL) {
+ free(sp);
+ return NULL;
+ }
+ sp->data = s;
+ sp->flags = 0;
+ sp->eof_code = HEIM_ERR_EOF;
+ s->size = 1024;
+ s->base = malloc(s->size);
+ if (s->base == NULL) {
+ free(sp);
+ free(s);
+ return NULL;
+ }
+ s->len = 0;
+ s->ptr = s->base;
+ sp->fetch = emem_fetch;
+ sp->store = emem_store;
+ sp->seek = emem_seek;
+ sp->free = emem_free;
+ return sp;
+}
diff --git a/source4/heimdal/lib/krb5/store_fd.c b/source4/heimdal/lib/krb5/store_fd.c
new file mode 100644
index 0000000000..fe3c513ee9
--- /dev/null
+++ b/source4/heimdal/lib/krb5/store_fd.c
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 1997 - 2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+#include "store-int.h"
+
+RCSID("$Id$");
+
+typedef struct fd_storage {
+ int fd;
+} fd_storage;
+
+#define FD(S) (((fd_storage*)(S)->data)->fd)
+
+static ssize_t
+fd_fetch(krb5_storage * sp, void *data, size_t size)
+{
+ return net_read(FD(sp), data, size);
+}
+
+static ssize_t
+fd_store(krb5_storage * sp, const void *data, size_t size)
+{
+ return net_write(FD(sp), data, size);
+}
+
+static off_t
+fd_seek(krb5_storage * sp, off_t offset, int whence)
+{
+ return lseek(FD(sp), offset, whence);
+}
+
+static void
+fd_free(krb5_storage * sp)
+{
+ close(FD(sp));
+}
+
+krb5_storage * KRB5_LIB_FUNCTION
+krb5_storage_from_fd(int fd)
+{
+ krb5_storage *sp;
+
+ fd = dup(fd);
+ if (fd < 0)
+ return NULL;
+
+ sp = malloc(sizeof(krb5_storage));
+ if (sp == NULL) {
+ close(fd);
+ return NULL;
+ }
+
+ sp->data = malloc(sizeof(fd_storage));
+ if (sp->data == NULL) {
+ close(fd);
+ free(sp);
+ return NULL;
+ }
+ sp->flags = 0;
+ sp->eof_code = HEIM_ERR_EOF;
+ FD(sp) = fd;
+ sp->fetch = fd_fetch;
+ sp->store = fd_store;
+ sp->seek = fd_seek;
+ sp->free = fd_free;
+ return sp;
+}
diff --git a/source4/heimdal/lib/krb5/store_mem.c b/source4/heimdal/lib/krb5/store_mem.c
new file mode 100644
index 0000000000..5c7cd17fba
--- /dev/null
+++ b/source4/heimdal/lib/krb5/store_mem.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 1997 - 2000, 2002 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+#include "store-int.h"
+
+RCSID("$Id$");
+
+typedef struct mem_storage{
+ unsigned char *base;
+ size_t size;
+ unsigned char *ptr;
+}mem_storage;
+
+static ssize_t
+mem_fetch(krb5_storage *sp, void *data, size_t size)
+{
+ mem_storage *s = (mem_storage*)sp->data;
+ if(size > s->base + s->size - s->ptr)
+ size = s->base + s->size - s->ptr;
+ memmove(data, s->ptr, size);
+ sp->seek(sp, size, SEEK_CUR);
+ return size;
+}
+
+static ssize_t
+mem_store(krb5_storage *sp, const void *data, size_t size)
+{
+ mem_storage *s = (mem_storage*)sp->data;
+ if(size > s->base + s->size - s->ptr)
+ size = s->base + s->size - s->ptr;
+ memmove(s->ptr, data, size);
+ sp->seek(sp, size, SEEK_CUR);
+ return size;
+}
+
+static ssize_t
+mem_no_store(krb5_storage *sp, const void *data, size_t size)
+{
+ return -1;
+}
+
+static off_t
+mem_seek(krb5_storage *sp, off_t offset, int whence)
+{
+ mem_storage *s = (mem_storage*)sp->data;
+ switch(whence){
+ case SEEK_SET:
+ if(offset > s->size)
+ offset = s->size;
+ if(offset < 0)
+ offset = 0;
+ s->ptr = s->base + offset;
+ break;
+ case SEEK_CUR:
+ return sp->seek(sp, s->ptr - s->base + offset, SEEK_SET);
+ case SEEK_END:
+ return sp->seek(sp, s->size + offset, SEEK_SET);
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+ return s->ptr - s->base;
+}
+
+krb5_storage * KRB5_LIB_FUNCTION
+krb5_storage_from_mem(void *buf, size_t len)
+{
+ krb5_storage *sp = malloc(sizeof(krb5_storage));
+ mem_storage *s;
+ if(sp == NULL)
+ return NULL;
+ s = malloc(sizeof(*s));
+ if(s == NULL) {
+ free(sp);
+ return NULL;
+ }
+ sp->data = s;
+ sp->flags = 0;
+ sp->eof_code = HEIM_ERR_EOF;
+ s->base = buf;
+ s->size = len;
+ s->ptr = buf;
+ sp->fetch = mem_fetch;
+ sp->store = mem_store;
+ sp->seek = mem_seek;
+ sp->free = NULL;
+ return sp;
+}
+
+krb5_storage * KRB5_LIB_FUNCTION
+krb5_storage_from_data(krb5_data *data)
+{
+ return krb5_storage_from_mem(data->data, data->length);
+}
+
+krb5_storage * KRB5_LIB_FUNCTION
+krb5_storage_from_readonly_mem(const void *buf, size_t len)
+{
+ krb5_storage *sp = malloc(sizeof(krb5_storage));
+ mem_storage *s;
+ if(sp == NULL)
+ return NULL;
+ s = malloc(sizeof(*s));
+ if(s == NULL) {
+ free(sp);
+ return NULL;
+ }
+ sp->data = s;
+ sp->flags = 0;
+ sp->eof_code = HEIM_ERR_EOF;
+ s->base = rk_UNCONST(buf);
+ s->size = len;
+ s->ptr = rk_UNCONST(buf);
+ sp->fetch = mem_fetch;
+ sp->store = mem_no_store;
+ sp->seek = mem_seek;
+ sp->free = NULL;
+ return sp;
+}
diff --git a/source4/heimdal/lib/krb5/ticket.c b/source4/heimdal/lib/krb5/ticket.c
new file mode 100644
index 0000000000..db78626570
--- /dev/null
+++ b/source4/heimdal/lib/krb5/ticket.c
@@ -0,0 +1,303 @@
+/*
+ * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_free_ticket(krb5_context context,
+ krb5_ticket *ticket)
+{
+ free_EncTicketPart(&ticket->ticket);
+ krb5_free_principal(context, ticket->client);
+ krb5_free_principal(context, ticket->server);
+ free(ticket);
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_copy_ticket(krb5_context context,
+ const krb5_ticket *from,
+ krb5_ticket **to)
+{
+ krb5_error_code ret;
+ krb5_ticket *tmp;
+
+ *to = NULL;
+ tmp = malloc(sizeof(*tmp));
+ if(tmp == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ if((ret = copy_EncTicketPart(&from->ticket, &tmp->ticket))){
+ free(tmp);
+ return ret;
+ }
+ ret = krb5_copy_principal(context, from->client, &tmp->client);
+ if(ret){
+ free_EncTicketPart(&tmp->ticket);
+ free(tmp);
+ return ret;
+ }
+ ret = krb5_copy_principal(context, from->server, &tmp->server);
+ if(ret){
+ krb5_free_principal(context, tmp->client);
+ free_EncTicketPart(&tmp->ticket);
+ free(tmp);
+ return ret;
+ }
+ *to = tmp;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_ticket_get_client(krb5_context context,
+ const krb5_ticket *ticket,
+ krb5_principal *client)
+{
+ return krb5_copy_principal(context, ticket->client, client);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_ticket_get_server(krb5_context context,
+ const krb5_ticket *ticket,
+ krb5_principal *server)
+{
+ return krb5_copy_principal(context, ticket->server, server);
+}
+
+time_t KRB5_LIB_FUNCTION
+krb5_ticket_get_endtime(krb5_context context,
+ const krb5_ticket *ticket)
+{
+ return ticket->ticket.endtime;
+}
+
+/**
+ * Get the flags from the Kerberos ticket
+ *
+ * @param context Kerberos context
+ * @param ticket Kerberos ticket
+ *
+ * @return ticket flags
+ *
+ * @ingroup krb5_ticket
+ */
+unsigned long
+krb5_ticket_get_flags(krb5_context context,
+ const krb5_ticket *ticket)
+{
+ return TicketFlags2int(ticket->ticket.flags);
+}
+
+static int
+find_type_in_ad(krb5_context context,
+ int type,
+ krb5_data *data,
+ krb5_boolean *found,
+ krb5_boolean failp,
+ krb5_keyblock *sessionkey,
+ const AuthorizationData *ad,
+ int level)
+{
+ krb5_error_code ret = 0;
+ int i;
+
+ if (level > 9) {
+ ret = ENOENT; /* XXX */
+ krb5_set_error_message(context, ret,
+ N_("Authorization data nested deeper "
+ "then %d levels, stop searching", ""),
+ level);
+ goto out;
+ }
+
+ /*
+ * Only copy out the element the first time we get to it, we need
+ * to run over the whole authorization data fields to check if
+ * there are any container clases we need to care about.
+ */
+ for (i = 0; i < ad->len; i++) {
+ if (!*found && ad->val[i].ad_type == type) {
+ ret = der_copy_octet_string(&ad->val[i].ad_data, data);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("malloc: out of memory", ""));
+ goto out;
+ }
+ *found = TRUE;
+ continue;
+ }
+ switch (ad->val[i].ad_type) {
+ case KRB5_AUTHDATA_IF_RELEVANT: {
+ AuthorizationData child;
+ ret = decode_AuthorizationData(ad->val[i].ad_data.data,
+ ad->val[i].ad_data.length,
+ &child,
+ NULL);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("Failed to decode "
+ "IF_RELEVANT with %d", ""),
+ (int)ret);
+ goto out;
+ }
+ ret = find_type_in_ad(context, type, data, found, FALSE,
+ sessionkey, &child, level + 1);
+ free_AuthorizationData(&child);
+ if (ret)
+ goto out;
+ break;
+ }
+#if 0 /* XXX test */
+ case KRB5_AUTHDATA_KDC_ISSUED: {
+ AD_KDCIssued child;
+
+ ret = decode_AD_KDCIssued(ad->val[i].ad_data.data,
+ ad->val[i].ad_data.length,
+ &child,
+ NULL);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("Failed to decode "
+ "AD_KDCIssued with %d", ""),
+ ret);
+ goto out;
+ }
+ if (failp) {
+ krb5_boolean valid;
+ krb5_data buf;
+ size_t len;
+
+ ASN1_MALLOC_ENCODE(AuthorizationData, buf.data, buf.length,
+ &child.elements, &len, ret);
+ if (ret) {
+ free_AD_KDCIssued(&child);
+ krb5_clear_error_message(context);
+ goto out;
+ }
+ if(buf.length != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+
+ ret = krb5_c_verify_checksum(context, sessionkey, 19, &buf,
+ &child.ad_checksum, &valid);
+ krb5_data_free(&buf);
+ if (ret) {
+ free_AD_KDCIssued(&child);
+ goto out;
+ }
+ if (!valid) {
+ krb5_clear_error_message(context);
+ ret = ENOENT;
+ free_AD_KDCIssued(&child);
+ goto out;
+ }
+ }
+ ret = find_type_in_ad(context, type, data, found, failp, sessionkey,
+ &child.elements, level + 1);
+ free_AD_KDCIssued(&child);
+ if (ret)
+ goto out;
+ break;
+ }
+#endif
+ case KRB5_AUTHDATA_AND_OR:
+ if (!failp)
+ break;
+ ret = ENOENT; /* XXX */
+ krb5_set_error_message(context, ret,
+ N_("Authorization data contains "
+ "AND-OR element that is unknown to the "
+ "application", ""));
+ goto out;
+ default:
+ if (!failp)
+ break;
+ ret = ENOENT; /* XXX */
+ krb5_set_error_message(context, ret,
+ N_("Authorization data contains "
+ "unknown type (%d) ", ""),
+ ad->val[i].ad_type);
+ goto out;
+ }
+ }
+out:
+ if (ret) {
+ if (*found) {
+ krb5_data_free(data);
+ *found = 0;
+ }
+ }
+ return ret;
+}
+
+/*
+ * Extract the authorization data type of `type' from the
+ * 'ticket'. Store the field in `data'. This function is to use for
+ * kerberos applications.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_ticket_get_authorization_data_type(krb5_context context,
+ krb5_ticket *ticket,
+ int type,
+ krb5_data *data)
+{
+ AuthorizationData *ad;
+ krb5_error_code ret;
+ krb5_boolean found = FALSE;
+
+ krb5_data_zero(data);
+
+ ad = ticket->ticket.authorization_data;
+ if (ticket->ticket.authorization_data == NULL) {
+ krb5_set_error_message(context, ENOENT,
+ N_("Ticket have not authorization data", ""));
+ return ENOENT; /* XXX */
+ }
+
+ ret = find_type_in_ad(context, type, data, &found, TRUE,
+ &ticket->ticket.key, ad, 0);
+ if (ret)
+ return ret;
+ if (!found) {
+ krb5_set_error_message(context, ENOENT,
+ N_("Ticket have not "
+ "authorization data of type %d", ""),
+ type);
+ return ENOENT; /* XXX */
+ }
+ return 0;
+}
diff --git a/source4/heimdal/lib/krb5/time.c b/source4/heimdal/lib/krb5/time.c
new file mode 100644
index 0000000000..cd786fedde
--- /dev/null
+++ b/source4/heimdal/lib/krb5/time.c
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 1997-2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+/**
+ * Set the absolute time that the caller knows the kdc has so the
+ * kerberos library can calculate the relative diffrence beteen the
+ * KDC time and local system time.
+ *
+ * @param context Keberos 5 context.
+ * @param sec The applications new of "now" in seconds
+ * @param usec The applications new of "now" in micro seconds
+
+ * @return Kerberos 5 error code, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_set_real_time (krb5_context context,
+ krb5_timestamp sec,
+ int32_t usec)
+{
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+
+ context->kdc_sec_offset = sec - tv.tv_sec;
+
+ /**
+ * If the caller passes in a negative usec, its assumed to be
+ * unknown and the function will use the current time usec.
+ */
+ if (usec >= 0) {
+ context->kdc_usec_offset = usec - tv.tv_usec;
+
+ if (context->kdc_usec_offset < 0) {
+ context->kdc_sec_offset--;
+ context->kdc_usec_offset += 1000000;
+ }
+ } else
+ context->kdc_usec_offset = tv.tv_usec;
+
+ return 0;
+}
+
+/*
+ * return ``corrected'' time in `timeret'.
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_timeofday (krb5_context context,
+ krb5_timestamp *timeret)
+{
+ *timeret = time(NULL) + context->kdc_sec_offset;
+ return 0;
+}
+
+/*
+ * like gettimeofday but with time correction to the KDC
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_us_timeofday (krb5_context context,
+ krb5_timestamp *sec,
+ int32_t *usec)
+{
+ struct timeval tv;
+
+ gettimeofday (&tv, NULL);
+
+ *sec = tv.tv_sec + context->kdc_sec_offset;
+ *usec = tv.tv_usec; /* XXX */
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_format_time(krb5_context context, time_t t,
+ char *s, size_t len, krb5_boolean include_time)
+{
+ struct tm *tm;
+ if(context->log_utc)
+ tm = gmtime (&t);
+ else
+ tm = localtime(&t);
+ if(tm == NULL ||
+ strftime(s, len, include_time ? context->time_fmt : context->date_fmt, tm) == 0)
+ snprintf(s, len, "%ld", (long)t);
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_string_to_deltat(const char *string, krb5_deltat *deltat)
+{
+ if((*deltat = parse_time(string, "s")) == -1)
+ return KRB5_DELTAT_BADFORMAT;
+ return 0;
+}
diff --git a/source4/heimdal/lib/krb5/transited.c b/source4/heimdal/lib/krb5/transited.c
new file mode 100644
index 0000000000..7e11d5579a
--- /dev/null
+++ b/source4/heimdal/lib/krb5/transited.c
@@ -0,0 +1,518 @@
+/*
+ * Copyright (c) 1997 - 2001, 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+/* this is an attempt at one of the most horrible `compression'
+ schemes that has ever been invented; it's so amazingly brain-dead
+ that words can not describe it, and all this just to save a few
+ silly bytes */
+
+struct tr_realm {
+ char *realm;
+ unsigned leading_space:1;
+ unsigned leading_slash:1;
+ unsigned trailing_dot:1;
+ struct tr_realm *next;
+};
+
+static void
+free_realms(struct tr_realm *r)
+{
+ struct tr_realm *p;
+ while(r){
+ p = r;
+ r = r->next;
+ free(p->realm);
+ free(p);
+ }
+}
+
+static int
+make_path(krb5_context context, struct tr_realm *r,
+ const char *from, const char *to)
+{
+ const char *p;
+ struct tr_realm *path = r->next;
+ struct tr_realm *tmp;
+
+ if(strlen(from) < strlen(to)){
+ const char *str;
+ str = from;
+ from = to;
+ to = str;
+ }
+
+ if(strcmp(from + strlen(from) - strlen(to), to) == 0){
+ p = from;
+ while(1){
+ p = strchr(p, '.');
+ if(p == NULL) {
+ krb5_clear_error_message (context);
+ return KRB5KDC_ERR_POLICY;
+ }
+ p++;
+ if(strcmp(p, to) == 0)
+ break;
+ tmp = calloc(1, sizeof(*tmp));
+ if(tmp == NULL){
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ tmp->next = path;
+ path = tmp;
+ path->realm = strdup(p);
+ if(path->realm == NULL){
+ r->next = path; /* XXX */
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;;
+ }
+ }
+ }else if(strncmp(from, to, strlen(to)) == 0){
+ p = from + strlen(from);
+ while(1){
+ while(p >= from && *p != '/') p--;
+ if(p == from) {
+ r->next = path; /* XXX */
+ return KRB5KDC_ERR_POLICY;
+ }
+ if(strncmp(to, from, p - from) == 0)
+ break;
+ tmp = calloc(1, sizeof(*tmp));
+ if(tmp == NULL){
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ tmp->next = path;
+ path = tmp;
+ path->realm = malloc(p - from + 1);
+ if(path->realm == NULL){
+ r->next = path; /* XXX */
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ memcpy(path->realm, from, p - from);
+ path->realm[p - from] = '\0';
+ p--;
+ }
+ } else {
+ krb5_clear_error_message (context);
+ return KRB5KDC_ERR_POLICY;
+ }
+ r->next = path;
+
+ return 0;
+}
+
+static int
+make_paths(krb5_context context,
+ struct tr_realm *realms, const char *client_realm,
+ const char *server_realm)
+{
+ struct tr_realm *r;
+ int ret;
+ const char *prev_realm = client_realm;
+ const char *next_realm = NULL;
+ for(r = realms; r; r = r->next){
+ /* it *might* be that you can have more than one empty
+ component in a row, at least that's how I interpret the
+ "," exception in 1510 */
+ if(r->realm[0] == '\0'){
+ while(r->next && r->next->realm[0] == '\0')
+ r = r->next;
+ if(r->next)
+ next_realm = r->next->realm;
+ else
+ next_realm = server_realm;
+ ret = make_path(context, r, prev_realm, next_realm);
+ if(ret){
+ free_realms(realms);
+ return ret;
+ }
+ }
+ prev_realm = r->realm;
+ }
+ return 0;
+}
+
+static int
+expand_realms(krb5_context context,
+ struct tr_realm *realms, const char *client_realm)
+{
+ struct tr_realm *r;
+ const char *prev_realm = NULL;
+ for(r = realms; r; r = r->next){
+ if(r->trailing_dot){
+ char *tmp;
+ size_t len;
+
+ if(prev_realm == NULL)
+ prev_realm = client_realm;
+
+ len = strlen(r->realm) + strlen(prev_realm) + 1;
+
+ tmp = realloc(r->realm, len);
+ if(tmp == NULL){
+ free_realms(realms);
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ r->realm = tmp;
+ strlcat(r->realm, prev_realm, len);
+ }else if(r->leading_slash && !r->leading_space && prev_realm){
+ /* yet another exception: if you use x500-names, the
+ leading realm doesn't have to be "quoted" with a space */
+ char *tmp;
+ size_t len = strlen(r->realm) + strlen(prev_realm) + 1;
+
+ tmp = malloc(len);
+ if(tmp == NULL){
+ free_realms(realms);
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ strlcpy(tmp, prev_realm, len);
+ strlcat(tmp, r->realm, len);
+ free(r->realm);
+ r->realm = tmp;
+ }
+ prev_realm = r->realm;
+ }
+ return 0;
+}
+
+static struct tr_realm *
+make_realm(char *realm)
+{
+ struct tr_realm *r;
+ char *p, *q;
+ int quote = 0;
+ r = calloc(1, sizeof(*r));
+ if(r == NULL){
+ free(realm);
+ return NULL;
+ }
+ r->realm = realm;
+ for(p = q = r->realm; *p; p++){
+ if(p == r->realm && *p == ' '){
+ r->leading_space = 1;
+ continue;
+ }
+ if(q == r->realm && *p == '/')
+ r->leading_slash = 1;
+ if(quote){
+ *q++ = *p;
+ quote = 0;
+ continue;
+ }
+ if(*p == '\\'){
+ quote = 1;
+ continue;
+ }
+ if(p[0] == '.' && p[1] == '\0')
+ r->trailing_dot = 1;
+ *q++ = *p;
+ }
+ *q = '\0';
+ return r;
+}
+
+static struct tr_realm*
+append_realm(struct tr_realm *head, struct tr_realm *r)
+{
+ struct tr_realm *p;
+ if(head == NULL){
+ r->next = NULL;
+ return r;
+ }
+ p = head;
+ while(p->next) p = p->next;
+ p->next = r;
+ return head;
+}
+
+static int
+decode_realms(krb5_context context,
+ const char *tr, int length, struct tr_realm **realms)
+{
+ struct tr_realm *r = NULL;
+
+ char *tmp;
+ int quote = 0;
+ const char *start = tr;
+ int i;
+
+ for(i = 0; i < length; i++){
+ if(quote){
+ quote = 0;
+ continue;
+ }
+ if(tr[i] == '\\'){
+ quote = 1;
+ continue;
+ }
+ if(tr[i] == ','){
+ tmp = malloc(tr + i - start + 1);
+ if(tmp == NULL){
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ memcpy(tmp, start, tr + i - start);
+ tmp[tr + i - start] = '\0';
+ r = make_realm(tmp);
+ if(r == NULL){
+ free_realms(*realms);
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ *realms = append_realm(*realms, r);
+ start = tr + i + 1;
+ }
+ }
+ tmp = malloc(tr + i - start + 1);
+ if(tmp == NULL){
+ free(*realms);
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ memcpy(tmp, start, tr + i - start);
+ tmp[tr + i - start] = '\0';
+ r = make_realm(tmp);
+ if(r == NULL){
+ free_realms(*realms);
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ *realms = append_realm(*realms, r);
+
+ return 0;
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_domain_x500_decode(krb5_context context,
+ krb5_data tr, char ***realms, unsigned int *num_realms,
+ const char *client_realm, const char *server_realm)
+{
+ struct tr_realm *r = NULL;
+ struct tr_realm *p, **q;
+ int ret;
+
+ if(tr.length == 0) {
+ *realms = NULL;
+ *num_realms = 0;
+ return 0;
+ }
+
+ /* split string in components */
+ ret = decode_realms(context, tr.data, tr.length, &r);
+ if(ret)
+ return ret;
+
+ /* apply prefix rule */
+ ret = expand_realms(context, r, client_realm);
+ if(ret)
+ return ret;
+
+ ret = make_paths(context, r, client_realm, server_realm);
+ if(ret)
+ return ret;
+
+ /* remove empty components and count realms */
+ q = &r;
+ *num_realms = 0;
+ for(p = r; p; ){
+ if(p->realm[0] == '\0'){
+ free(p->realm);
+ *q = p->next;
+ free(p);
+ p = *q;
+ }else{
+ q = &p->next;
+ p = p->next;
+ (*num_realms)++;
+ }
+ }
+ if (*num_realms < 0 || *num_realms + 1 > UINT_MAX/sizeof(**realms))
+ return ERANGE;
+
+ {
+ char **R;
+ R = malloc((*num_realms + 1) * sizeof(*R));
+ if (R == NULL)
+ return ENOMEM;
+ *realms = R;
+ while(r){
+ *R++ = r->realm;
+ p = r->next;
+ free(r);
+ r = p;
+ }
+ }
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_domain_x500_encode(char **realms, unsigned int num_realms,
+ krb5_data *encoding)
+{
+ char *s = NULL;
+ int len = 0;
+ unsigned int i;
+ krb5_data_zero(encoding);
+ if (num_realms == 0)
+ return 0;
+ for(i = 0; i < num_realms; i++){
+ len += strlen(realms[i]);
+ if(realms[i][0] == '/')
+ len++;
+ }
+ len += num_realms - 1;
+ s = malloc(len + 1);
+ if (s == NULL)
+ return ENOMEM;
+ *s = '\0';
+ for(i = 0; i < num_realms; i++){
+ if(i && i < num_realms - 1)
+ strlcat(s, ",", len + 1);
+ if(realms[i][0] == '/')
+ strlcat(s, " ", len + 1);
+ strlcat(s, realms[i], len + 1);
+ }
+ encoding->data = s;
+ encoding->length = strlen(s);
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_check_transited(krb5_context context,
+ krb5_const_realm client_realm,
+ krb5_const_realm server_realm,
+ krb5_realm *realms,
+ unsigned int num_realms,
+ int *bad_realm)
+{
+ char **tr_realms;
+ char **p;
+ int i;
+
+ if(num_realms == 0)
+ return 0;
+
+ tr_realms = krb5_config_get_strings(context, NULL,
+ "capaths",
+ client_realm,
+ server_realm,
+ NULL);
+ for(i = 0; i < num_realms; i++) {
+ for(p = tr_realms; p && *p; p++) {
+ if(strcmp(*p, realms[i]) == 0)
+ break;
+ }
+ if(p == NULL || *p == NULL) {
+ krb5_config_free_strings(tr_realms);
+ krb5_set_error_message (context, KRB5KRB_AP_ERR_ILL_CR_TKT,
+ N_("no transit allowed "
+ "through realm %s", ""),
+ realms[i]);
+ if(bad_realm)
+ *bad_realm = i;
+ return KRB5KRB_AP_ERR_ILL_CR_TKT;
+ }
+ }
+ krb5_config_free_strings(tr_realms);
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_check_transited_realms(krb5_context context,
+ const char *const *realms,
+ unsigned int num_realms,
+ int *bad_realm)
+{
+ int i;
+ int ret = 0;
+ char **bad_realms = krb5_config_get_strings(context, NULL,
+ "libdefaults",
+ "transited_realms_reject",
+ NULL);
+ if(bad_realms == NULL)
+ return 0;
+
+ for(i = 0; i < num_realms; i++) {
+ char **p;
+ for(p = bad_realms; *p; p++)
+ if(strcmp(*p, realms[i]) == 0) {
+ ret = KRB5KRB_AP_ERR_ILL_CR_TKT;
+ krb5_set_error_message (context, ret,
+ N_("no transit allowed "
+ "through realm %s", ""),
+ *p);
+ if(bad_realm)
+ *bad_realm = i;
+ break;
+ }
+ }
+ krb5_config_free_strings(bad_realms);
+ return ret;
+}
+
+#if 0
+int
+main(int argc, char **argv)
+{
+ krb5_data x;
+ char **r;
+ int num, i;
+ x.data = argv[1];
+ x.length = strlen(x.data);
+ if(domain_expand(x, &r, &num, argv[2], argv[3]))
+ exit(1);
+ for(i = 0; i < num; i++)
+ printf("%s\n", r[i]);
+ return 0;
+}
+#endif
+
diff --git a/source4/heimdal/lib/krb5/v4_glue.c b/source4/heimdal/lib/krb5/v4_glue.c
new file mode 100644
index 0000000000..6911cb20f8
--- /dev/null
+++ b/source4/heimdal/lib/krb5/v4_glue.c
@@ -0,0 +1,953 @@
+/*
+ * Copyright (c) 1997 - 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+RCSID("$Id$");
+
+#include "krb5-v4compat.h"
+
+#ifndef HEIMDAL_SMALLER
+
+/*
+ *
+ */
+
+#define RCHECK(r,func,label) \
+ do { (r) = func ; if (r) goto label; } while(0);
+
+
+/* include this here, to avoid dependencies on libkrb */
+
+static const int _tkt_lifetimes[TKTLIFENUMFIXED] = {
+ 38400, 41055, 43894, 46929, 50174, 53643, 57352, 61318,
+ 65558, 70091, 74937, 80119, 85658, 91581, 97914, 104684,
+ 111922, 119661, 127935, 136781, 146239, 156350, 167161, 178720,
+ 191077, 204289, 218415, 233517, 249664, 266926, 285383, 305116,
+ 326213, 348769, 372885, 398668, 426234, 455705, 487215, 520904,
+ 556921, 595430, 636601, 680618, 727680, 777995, 831789, 889303,
+ 950794, 1016537, 1086825, 1161973, 1242318, 1328218, 1420057, 1518247,
+ 1623226, 1735464, 1855462, 1983758, 2120925, 2267576, 2424367, 2592000
+};
+
+int KRB5_LIB_FUNCTION
+_krb5_krb_time_to_life(time_t start, time_t end)
+{
+ int i;
+ time_t life = end - start;
+
+ if (life > MAXTKTLIFETIME || life <= 0)
+ return 0;
+#if 0
+ if (krb_no_long_lifetimes)
+ return (life + 5*60 - 1)/(5*60);
+#endif
+
+ if (end >= NEVERDATE)
+ return TKTLIFENOEXPIRE;
+ if (life < _tkt_lifetimes[0])
+ return (life + 5*60 - 1)/(5*60);
+ for (i=0; i<TKTLIFENUMFIXED; i++)
+ if (life <= _tkt_lifetimes[i])
+ return i + TKTLIFEMINFIXED;
+ return 0;
+
+}
+
+time_t KRB5_LIB_FUNCTION
+_krb5_krb_life_to_time(int start, int life_)
+{
+ unsigned char life = (unsigned char) life_;
+
+#if 0
+ if (krb_no_long_lifetimes)
+ return start + life*5*60;
+#endif
+
+ if (life == TKTLIFENOEXPIRE)
+ return NEVERDATE;
+ if (life < TKTLIFEMINFIXED)
+ return start + life*5*60;
+ if (life > TKTLIFEMAXFIXED)
+ return start + MAXTKTLIFETIME;
+ return start + _tkt_lifetimes[life - TKTLIFEMINFIXED];
+}
+
+/*
+ * Get the name of the krb4 credentials cache, will use `tkfile' as
+ * the name if that is passed in. `cc' must be free()ed by caller,
+ */
+
+static krb5_error_code
+get_krb4_cc_name(const char *tkfile, char **cc)
+{
+
+ *cc = NULL;
+ if(tkfile == NULL) {
+ char *path;
+ if(!issuid()) {
+ path = getenv("KRBTKFILE");
+ if (path)
+ *cc = strdup(path);
+ }
+ if(*cc == NULL)
+ if (asprintf(cc, "%s%u", TKT_ROOT, (unsigned)getuid()) < 0)
+ return errno;
+ } else {
+ *cc = strdup(tkfile);
+ if (*cc == NULL)
+ return ENOMEM;
+ }
+ return 0;
+}
+
+/*
+ * Write a Kerberos 4 ticket file
+ */
+
+#define KRB5_TF_LCK_RETRY_COUNT 50
+#define KRB5_TF_LCK_RETRY 1
+
+static krb5_error_code
+write_v4_cc(krb5_context context, const char *tkfile,
+ krb5_storage *sp, int append)
+{
+ krb5_error_code ret;
+ struct stat sb;
+ krb5_data data;
+ char *path;
+ int fd, i;
+
+ ret = get_krb4_cc_name(tkfile, &path);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("Failed getting the krb4 credentials "
+ "cache name", ""));
+ return ret;
+ }
+
+ fd = open(path, O_WRONLY|O_CREAT, 0600);
+ if (fd < 0) {
+ ret = errno;
+ krb5_set_error_message(context, ret,
+ N_("Failed opening krb4 credential cache "
+ "%s: %s", "path, error"),
+ path, strerror(ret));
+ free(path);
+ return ret;
+ }
+ rk_cloexec(fd);
+
+ if (fstat(fd, &sb) != 0 || !S_ISREG(sb.st_mode)) {
+ krb5_set_error_message(context, ret,
+ N_("krb4 credential cache %s is not a file", ""),
+ path);
+ free(path);
+ close(fd);
+ return KRB5_FCC_PERM;
+ }
+
+ for (i = 0; i < KRB5_TF_LCK_RETRY_COUNT; i++) {
+ if (flock(fd, LOCK_EX | LOCK_NB) < 0) {
+ sleep(KRB5_TF_LCK_RETRY);
+ } else
+ break;
+ }
+ if (i == KRB5_TF_LCK_RETRY_COUNT) {
+ krb5_set_error_message(context, KRB5_FCC_PERM,
+ N_("Failed to lock credentail cache %s", ""),
+ path);
+ free(path);
+ close(fd);
+ return KRB5_FCC_PERM;
+ }
+
+ if (!append) {
+ ret = ftruncate(fd, 0);
+ if (ret < 0) {
+ flock(fd, LOCK_UN);
+ krb5_set_error_message(context, KRB5_FCC_PERM,
+ N_("Failed to truncate krb4 cc %s", ""),
+ path);
+ free(path);
+ close(fd);
+ return KRB5_FCC_PERM;
+ }
+ }
+ ret = lseek(fd, 0L, SEEK_END);
+ if (ret < 0) {
+ ret = errno;
+ flock(fd, LOCK_UN);
+ free(path);
+ close(fd);
+ return ret;
+ }
+
+ krb5_storage_to_data(sp, &data);
+
+ ret = write(fd, data.data, data.length);
+ if (ret != data.length)
+ ret = KRB5_CC_IO;
+
+ krb5_free_data_contents(context, &data);
+
+ flock(fd, LOCK_UN);
+ free(path);
+ close(fd);
+
+ return 0;
+}
+
+/*
+ *
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+_krb5_krb_tf_setup(krb5_context context,
+ struct credentials *v4creds,
+ const char *tkfile,
+ int append)
+{
+ krb5_error_code ret;
+ krb5_storage *sp;
+
+ sp = krb5_storage_emem();
+ if (sp == NULL)
+ return ENOMEM;
+
+ krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_HOST);
+ krb5_storage_set_eof_code(sp, KRB5_CC_IO);
+
+ krb5_clear_error_message(context);
+
+ if (!append) {
+ RCHECK(ret, krb5_store_stringz(sp, v4creds->pname), error);
+ RCHECK(ret, krb5_store_stringz(sp, v4creds->pinst), error);
+ }
+
+ /* cred */
+ RCHECK(ret, krb5_store_stringz(sp, v4creds->service), error);
+ RCHECK(ret, krb5_store_stringz(sp, v4creds->instance), error);
+ RCHECK(ret, krb5_store_stringz(sp, v4creds->realm), error);
+ ret = krb5_storage_write(sp, v4creds->session, 8);
+ if (ret != 8) {
+ ret = KRB5_CC_IO;
+ goto error;
+ }
+ RCHECK(ret, krb5_store_int32(sp, v4creds->lifetime), error);
+ RCHECK(ret, krb5_store_int32(sp, v4creds->kvno), error);
+ RCHECK(ret, krb5_store_int32(sp, v4creds->ticket_st.length), error);
+
+ ret = krb5_storage_write(sp, v4creds->ticket_st.dat,
+ v4creds->ticket_st.length);
+ if (ret != v4creds->ticket_st.length) {
+ ret = KRB5_CC_IO;
+ goto error;
+ }
+ RCHECK(ret, krb5_store_int32(sp, v4creds->issue_date), error);
+
+ ret = write_v4_cc(context, tkfile, sp, append);
+
+ error:
+ krb5_storage_free(sp);
+
+ return ret;
+}
+
+/*
+ *
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+_krb5_krb_dest_tkt(krb5_context context, const char *tkfile)
+{
+ krb5_error_code ret;
+ char *path;
+
+ ret = get_krb4_cc_name(tkfile, &path);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("Failed getting the krb4 credentials "
+ "cache name", ""));
+ return ret;
+ }
+
+ if (unlink(path) < 0) {
+ ret = errno;
+ krb5_set_error_message(context, ret,
+ N_("Failed removing the cache %s "
+ "with error %s", "path, error"),
+ path, strerror(ret));
+ }
+ free(path);
+
+ return ret;
+}
+
+/*
+ *
+ */
+
+static krb5_error_code
+decrypt_etext(krb5_context context, const krb5_keyblock *key,
+ const krb5_data *cdata, krb5_data *data)
+{
+ krb5_error_code ret;
+ krb5_crypto crypto;
+
+ ret = krb5_crypto_init(context, key, ETYPE_DES_PCBC_NONE, &crypto);
+ if (ret)
+ return ret;
+
+ ret = krb5_decrypt(context, crypto, 0, cdata->data, cdata->length, data);
+ krb5_crypto_destroy(context, crypto);
+
+ return ret;
+}
+
+
+/*
+ *
+ */
+
+static const char eightzeros[8] = "\x00\x00\x00\x00\x00\x00\x00\x00";
+
+static krb5_error_code
+storage_to_etext(krb5_context context,
+ krb5_storage *sp,
+ const krb5_keyblock *key,
+ krb5_data *enc_data)
+{
+ krb5_error_code ret;
+ krb5_crypto crypto;
+ krb5_ssize_t size;
+ krb5_data data;
+
+ /* multiple of eight bytes, don't round up */
+
+ size = krb5_storage_seek(sp, 0, SEEK_END);
+ if (size < 0)
+ return KRB4ET_RD_AP_UNDEC;
+ size = ((size+7) & ~7) - size;
+
+ ret = krb5_storage_write(sp, eightzeros, size);
+ if (ret != size)
+ return KRB4ET_RD_AP_UNDEC;
+
+ ret = krb5_storage_to_data(sp, &data);
+ if (ret)
+ return ret;
+
+ ret = krb5_crypto_init(context, key, ETYPE_DES_PCBC_NONE, &crypto);
+ if (ret) {
+ krb5_data_free(&data);
+ return ret;
+ }
+
+ ret = krb5_encrypt(context, crypto, 0, data.data, data.length, enc_data);
+
+ krb5_data_free(&data);
+ krb5_crypto_destroy(context, crypto);
+
+ return ret;
+}
+
+/*
+ *
+ */
+
+static krb5_error_code
+put_nir(krb5_storage *sp, const char *name,
+ const char *instance, const char *realm)
+{
+ krb5_error_code ret;
+
+ RCHECK(ret, krb5_store_stringz(sp, name), error);
+ RCHECK(ret, krb5_store_stringz(sp, instance), error);
+ if (realm) {
+ RCHECK(ret, krb5_store_stringz(sp, realm), error);
+ }
+ error:
+ return ret;
+}
+
+/*
+ *
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+_krb5_krb_create_ticket(krb5_context context,
+ unsigned char flags,
+ const char *pname,
+ const char *pinstance,
+ const char *prealm,
+ int32_t paddress,
+ const krb5_keyblock *session,
+ int16_t life,
+ int32_t life_sec,
+ const char *sname,
+ const char *sinstance,
+ const krb5_keyblock *key,
+ krb5_data *enc_data)
+{
+ krb5_error_code ret;
+ krb5_storage *sp;
+
+ krb5_data_zero(enc_data);
+
+ sp = krb5_storage_emem();
+ if (sp == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_BE);
+
+ RCHECK(ret, krb5_store_int8(sp, flags), error);
+ RCHECK(ret, put_nir(sp, pname, pinstance, prealm), error);
+ RCHECK(ret, krb5_store_int32(sp, ntohl(paddress)), error);
+
+ /* session key */
+ ret = krb5_storage_write(sp,
+ session->keyvalue.data,
+ session->keyvalue.length);
+ if (ret != session->keyvalue.length) {
+ ret = KRB4ET_INTK_PROT;
+ goto error;
+ }
+
+ RCHECK(ret, krb5_store_int8(sp, life), error);
+ RCHECK(ret, krb5_store_int32(sp, life_sec), error);
+ RCHECK(ret, put_nir(sp, sname, sinstance, NULL), error);
+
+ ret = storage_to_etext(context, sp, key, enc_data);
+
+ error:
+ krb5_storage_free(sp);
+ if (ret)
+ krb5_set_error_message(context, ret,
+ N_("Failed to encode kerberos 4 ticket", ""));
+
+ return ret;
+}
+
+/*
+ *
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+_krb5_krb_create_ciph(krb5_context context,
+ const krb5_keyblock *session,
+ const char *service,
+ const char *instance,
+ const char *realm,
+ uint32_t life,
+ unsigned char kvno,
+ const krb5_data *ticket,
+ uint32_t kdc_time,
+ const krb5_keyblock *key,
+ krb5_data *enc_data)
+{
+ krb5_error_code ret;
+ krb5_storage *sp;
+
+ krb5_data_zero(enc_data);
+
+ sp = krb5_storage_emem();
+ if (sp == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_BE);
+
+ /* session key */
+ ret = krb5_storage_write(sp,
+ session->keyvalue.data,
+ session->keyvalue.length);
+ if (ret != session->keyvalue.length) {
+ ret = KRB4ET_INTK_PROT;
+ goto error;
+ }
+
+ RCHECK(ret, put_nir(sp, service, instance, realm), error);
+ RCHECK(ret, krb5_store_int8(sp, life), error);
+ RCHECK(ret, krb5_store_int8(sp, kvno), error);
+ RCHECK(ret, krb5_store_int8(sp, ticket->length), error);
+ ret = krb5_storage_write(sp, ticket->data, ticket->length);
+ if (ret != ticket->length) {
+ ret = KRB4ET_INTK_PROT;
+ goto error;
+ }
+ RCHECK(ret, krb5_store_int32(sp, kdc_time), error);
+
+ ret = storage_to_etext(context, sp, key, enc_data);
+
+ error:
+ krb5_storage_free(sp);
+ if (ret)
+ krb5_set_error_message(context, ret,
+ N_("Failed to encode kerberos 4 ticket", ""));
+
+ return ret;
+}
+
+/*
+ *
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+_krb5_krb_create_auth_reply(krb5_context context,
+ const char *pname,
+ const char *pinst,
+ const char *prealm,
+ int32_t time_ws,
+ int n,
+ uint32_t x_date,
+ unsigned char kvno,
+ const krb5_data *cipher,
+ krb5_data *data)
+{
+ krb5_error_code ret;
+ krb5_storage *sp;
+
+ krb5_data_zero(data);
+
+ sp = krb5_storage_emem();
+ if (sp == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_BE);
+
+ RCHECK(ret, krb5_store_int8(sp, KRB_PROT_VERSION), error);
+ RCHECK(ret, krb5_store_int8(sp, AUTH_MSG_KDC_REPLY), error);
+ RCHECK(ret, put_nir(sp, pname, pinst, prealm), error);
+ RCHECK(ret, krb5_store_int32(sp, time_ws), error);
+ RCHECK(ret, krb5_store_int8(sp, n), error);
+ RCHECK(ret, krb5_store_int32(sp, x_date), error);
+ RCHECK(ret, krb5_store_int8(sp, kvno), error);
+ RCHECK(ret, krb5_store_int16(sp, cipher->length), error);
+ ret = krb5_storage_write(sp, cipher->data, cipher->length);
+ if (ret != cipher->length) {
+ ret = KRB4ET_INTK_PROT;
+ goto error;
+ }
+
+ ret = krb5_storage_to_data(sp, data);
+
+ error:
+ krb5_storage_free(sp);
+ if (ret)
+ krb5_set_error_message(context, ret,
+ N_("Failed to encode kerberos 4 ticket", ""));
+
+ return ret;
+}
+
+/*
+ *
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+_krb5_krb_cr_err_reply(krb5_context context,
+ const char *name,
+ const char *inst,
+ const char *realm,
+ uint32_t time_ws,
+ uint32_t e,
+ const char *e_string,
+ krb5_data *data)
+{
+ krb5_error_code ret;
+ krb5_storage *sp;
+
+ krb5_data_zero(data);
+
+ if (name == NULL) name = "";
+ if (inst == NULL) inst = "";
+ if (realm == NULL) realm = "";
+ if (e_string == NULL) e_string = "";
+
+ sp = krb5_storage_emem();
+ if (sp == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+ krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_BE);
+
+ RCHECK(ret, krb5_store_int8(sp, KRB_PROT_VERSION), error);
+ RCHECK(ret, krb5_store_int8(sp, AUTH_MSG_ERR_REPLY), error);
+ RCHECK(ret, put_nir(sp, name, inst, realm), error);
+ RCHECK(ret, krb5_store_int32(sp, time_ws), error);
+ /* If it is a Kerberos 4 error-code, remove the et BASE */
+ if (e >= ERROR_TABLE_BASE_krb && e <= ERROR_TABLE_BASE_krb + 255)
+ e -= ERROR_TABLE_BASE_krb;
+ RCHECK(ret, krb5_store_int32(sp, e), error);
+ RCHECK(ret, krb5_store_stringz(sp, e_string), error);
+
+ ret = krb5_storage_to_data(sp, data);
+
+ error:
+ krb5_storage_free(sp);
+ if (ret)
+ krb5_set_error_message(context, ret, "Failed to encode kerberos 4 error");
+
+ return 0;
+}
+
+static krb5_error_code
+get_v4_stringz(krb5_storage *sp, char **str, size_t max_len)
+{
+ krb5_error_code ret;
+
+ ret = krb5_ret_stringz(sp, str);
+ if (ret)
+ return ret;
+ if (strlen(*str) > max_len) {
+ free(*str);
+ *str = NULL;
+ return KRB4ET_INTK_PROT;
+ }
+ return 0;
+}
+
+/*
+ *
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+_krb5_krb_decomp_ticket(krb5_context context,
+ const krb5_data *enc_ticket,
+ const krb5_keyblock *key,
+ const char *local_realm,
+ char **sname,
+ char **sinstance,
+ struct _krb5_krb_auth_data *ad)
+{
+ krb5_error_code ret;
+ krb5_ssize_t size;
+ krb5_storage *sp = NULL;
+ krb5_data ticket;
+ unsigned char des_key[8];
+
+ memset(ad, 0, sizeof(*ad));
+ krb5_data_zero(&ticket);
+
+ *sname = NULL;
+ *sinstance = NULL;
+
+ RCHECK(ret, decrypt_etext(context, key, enc_ticket, &ticket), error);
+
+ sp = krb5_storage_from_data(&ticket);
+ if (sp == NULL) {
+ krb5_data_free(&ticket);
+ krb5_set_error_message(context, ENOMEM, "alloc: out of memory");
+ return ENOMEM;
+ }
+
+ krb5_storage_set_eof_code(sp, KRB4ET_INTK_PROT);
+
+ RCHECK(ret, krb5_ret_int8(sp, &ad->k_flags), error);
+ RCHECK(ret, get_v4_stringz(sp, &ad->pname, ANAME_SZ), error);
+ RCHECK(ret, get_v4_stringz(sp, &ad->pinst, INST_SZ), error);
+ RCHECK(ret, get_v4_stringz(sp, &ad->prealm, REALM_SZ), error);
+ RCHECK(ret, krb5_ret_uint32(sp, &ad->address), error);
+
+ size = krb5_storage_read(sp, des_key, sizeof(des_key));
+ if (size != sizeof(des_key)) {
+ ret = KRB4ET_INTK_PROT;
+ goto error;
+ }
+
+ RCHECK(ret, krb5_ret_uint8(sp, &ad->life), error);
+
+ if (ad->k_flags & 1)
+ krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_LE);
+ else
+ krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_BE);
+
+ RCHECK(ret, krb5_ret_uint32(sp, &ad->time_sec), error);
+
+ RCHECK(ret, get_v4_stringz(sp, sname, ANAME_SZ), error);
+ RCHECK(ret, get_v4_stringz(sp, sinstance, INST_SZ), error);
+
+ ret = krb5_keyblock_init(context, ETYPE_DES_PCBC_NONE,
+ des_key, sizeof(des_key), &ad->session);
+ if (ret)
+ goto error;
+
+ if (strlen(ad->prealm) == 0) {
+ free(ad->prealm);
+ ad->prealm = strdup(local_realm);
+ if (ad->prealm == NULL) {
+ ret = ENOMEM;
+ goto error;
+ }
+ }
+
+ error:
+ memset(des_key, 0, sizeof(des_key));
+ if (sp)
+ krb5_storage_free(sp);
+ krb5_data_free(&ticket);
+ if (ret) {
+ if (*sname) {
+ free(*sname);
+ *sname = NULL;
+ }
+ if (*sinstance) {
+ free(*sinstance);
+ *sinstance = NULL;
+ }
+ _krb5_krb_free_auth_data(context, ad);
+ krb5_set_error_message(context, ret, "Failed to decode v4 ticket");
+ }
+ return ret;
+}
+
+/*
+ *
+ */
+
+krb5_error_code KRB5_LIB_FUNCTION
+_krb5_krb_rd_req(krb5_context context,
+ krb5_data *authent,
+ const char *service,
+ const char *instance,
+ const char *local_realm,
+ int32_t from_addr,
+ const krb5_keyblock *key,
+ struct _krb5_krb_auth_data *ad)
+{
+ krb5_error_code ret;
+ krb5_storage *sp;
+ krb5_data ticket, eaut, aut;
+ krb5_ssize_t size;
+ int little_endian;
+ int8_t pvno;
+ int8_t type;
+ int8_t s_kvno;
+ uint8_t ticket_length;
+ uint8_t eaut_length;
+ uint8_t time_5ms;
+ char *realm = NULL;
+ char *sname = NULL;
+ char *sinstance = NULL;
+ char *r_realm = NULL;
+ char *r_name = NULL;
+ char *r_instance = NULL;
+
+ uint32_t r_time_sec; /* Coarse time from authenticator */
+ unsigned long delta_t; /* Time in authenticator - local time */
+ long tkt_age; /* Age of ticket */
+
+ struct timeval tv;
+
+ krb5_data_zero(&ticket);
+ krb5_data_zero(&eaut);
+ krb5_data_zero(&aut);
+
+ sp = krb5_storage_from_data(authent);
+ if (sp == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+
+ krb5_storage_set_eof_code(sp, KRB4ET_INTK_PROT);
+
+ ret = krb5_ret_int8(sp, &pvno);
+ if (ret) {
+ krb5_set_error_message(context, ret, N_("Failed reading v4 pvno", ""));
+ goto error;
+ }
+
+ if (pvno != KRB_PROT_VERSION) {
+ ret = KRB4ET_RD_AP_VERSION;
+ krb5_set_error_message(context, ret, N_("Failed v4 pvno not 4", ""));
+ goto error;
+ }
+
+ ret = krb5_ret_int8(sp, &type);
+ if (ret) {
+ krb5_set_error_message(context, ret, N_("Failed readin v4 type", ""));
+ goto error;
+ }
+
+ little_endian = type & 1;
+ type &= ~1;
+
+ if(type != AUTH_MSG_APPL_REQUEST && type != AUTH_MSG_APPL_REQUEST_MUTUAL) {
+ ret = KRB4ET_RD_AP_MSG_TYPE;
+ krb5_set_error_message(context, ret,
+ N_("Not a valid v4 request type", ""));
+ goto error;
+ }
+
+ RCHECK(ret, krb5_ret_int8(sp, &s_kvno), error);
+ RCHECK(ret, get_v4_stringz(sp, &realm, REALM_SZ), error);
+ RCHECK(ret, krb5_ret_uint8(sp, &ticket_length), error);
+ RCHECK(ret, krb5_ret_uint8(sp, &eaut_length), error);
+ RCHECK(ret, krb5_data_alloc(&ticket, ticket_length), error);
+
+ size = krb5_storage_read(sp, ticket.data, ticket.length);
+ if (size != ticket.length) {
+ ret = KRB4ET_INTK_PROT;
+ krb5_set_error_message(context, ret, N_("Failed reading v4 ticket", ""));
+ goto error;
+ }
+
+ /* Decrypt and take apart ticket */
+ ret = _krb5_krb_decomp_ticket(context, &ticket, key, local_realm,
+ &sname, &sinstance, ad);
+ if (ret)
+ goto error;
+
+ RCHECK(ret, krb5_data_alloc(&eaut, eaut_length), error);
+
+ size = krb5_storage_read(sp, eaut.data, eaut.length);
+ if (size != eaut.length) {
+ ret = KRB4ET_INTK_PROT;
+ krb5_set_error_message(context, ret,
+ N_("Failed reading v4 authenticator", ""));
+ goto error;
+ }
+
+ krb5_storage_free(sp);
+ sp = NULL;
+
+ ret = decrypt_etext(context, &ad->session, &eaut, &aut);
+ if (ret)
+ goto error;
+
+ sp = krb5_storage_from_data(&aut);
+ if (sp == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ goto error;
+ }
+
+ if (little_endian)
+ krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_LE);
+ else
+ krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_BE);
+
+ RCHECK(ret, get_v4_stringz(sp, &r_name, ANAME_SZ), error);
+ RCHECK(ret, get_v4_stringz(sp, &r_instance, INST_SZ), error);
+ RCHECK(ret, get_v4_stringz(sp, &r_realm, REALM_SZ), error);
+
+ RCHECK(ret, krb5_ret_uint32(sp, &ad->checksum), error);
+ RCHECK(ret, krb5_ret_uint8(sp, &time_5ms), error);
+ RCHECK(ret, krb5_ret_uint32(sp, &r_time_sec), error);
+
+ if (strcmp(ad->pname, r_name) != 0 ||
+ strcmp(ad->pinst, r_instance) != 0 ||
+ strcmp(ad->prealm, r_realm) != 0) {
+ ret = KRB4ET_RD_AP_INCON;
+ krb5_set_error_message(context, ret, N_("v4 principal mismatch", ""));
+ goto error;
+ }
+
+ if (from_addr && ad->address && from_addr != ad->address) {
+ ret = KRB4ET_RD_AP_BADD;
+ krb5_set_error_message(context, ret,
+ N_("v4 bad address in ticket", ""));
+ goto error;
+ }
+
+ gettimeofday(&tv, NULL);
+ delta_t = abs((int)(tv.tv_sec - r_time_sec));
+ if (delta_t > CLOCK_SKEW) {
+ ret = KRB4ET_RD_AP_TIME;
+ krb5_set_error_message(context, ret, N_("v4 clock skew", ""));
+ goto error;
+ }
+
+ /* Now check for expiration of ticket */
+
+ tkt_age = tv.tv_sec - ad->time_sec;
+
+ if ((tkt_age < 0) && (-tkt_age > CLOCK_SKEW)) {
+ ret = KRB4ET_RD_AP_NYV;
+ krb5_set_error_message(context, ret,
+ N_("v4 clock skew for expiration", ""));
+ goto error;
+ }
+
+ if (tv.tv_sec > _krb5_krb_life_to_time(ad->time_sec, ad->life)) {
+ ret = KRB4ET_RD_AP_EXP;
+ krb5_set_error_message(context, ret, N_("v4 ticket expired", ""));
+ goto error;
+ }
+
+ ret = 0;
+ error:
+ krb5_data_free(&ticket);
+ krb5_data_free(&eaut);
+ krb5_data_free(&aut);
+ if (realm)
+ free(realm);
+ if (sname)
+ free(sname);
+ if (sinstance)
+ free(sinstance);
+ if (r_name)
+ free(r_name);
+ if (r_instance)
+ free(r_instance);
+ if (r_realm)
+ free(r_realm);
+ if (sp)
+ krb5_storage_free(sp);
+
+ if (ret)
+ krb5_clear_error_message(context);
+
+ return ret;
+}
+
+/*
+ *
+ */
+
+void KRB5_LIB_FUNCTION
+_krb5_krb_free_auth_data(krb5_context context, struct _krb5_krb_auth_data *ad)
+{
+ if (ad->pname)
+ free(ad->pname);
+ if (ad->pinst)
+ free(ad->pinst);
+ if (ad->prealm)
+ free(ad->prealm);
+ krb5_free_keyblock_contents(context, &ad->session);
+ memset(ad, 0, sizeof(*ad));
+}
+
+#endif /* HEIMDAL_SMALLER */
diff --git a/source4/heimdal/lib/krb5/version.c b/source4/heimdal/lib/krb5/version.c
new file mode 100644
index 0000000000..d43b83e26e
--- /dev/null
+++ b/source4/heimdal/lib/krb5/version.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 1997 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+/* this is just to get a version stamp in the library file */
+
+#define heimdal_version __heimdal_version
+#define heimdal_long_version __heimdal_long_version
+#include "version.h"
+
diff --git a/source4/heimdal/lib/krb5/warn.c b/source4/heimdal/lib/krb5/warn.c
new file mode 100644
index 0000000000..a00ae80697
--- /dev/null
+++ b/source4/heimdal/lib/krb5/warn.c
@@ -0,0 +1,211 @@
+/*
+ * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+#include <err.h>
+
+RCSID("$Id$");
+
+static krb5_error_code _warnerr(krb5_context context, int do_errtext,
+ krb5_error_code code, int level, const char *fmt, va_list ap)
+ __attribute__((__format__(__printf__, 5, 0)));
+
+static krb5_error_code
+_warnerr(krb5_context context, int do_errtext,
+ krb5_error_code code, int level, const char *fmt, va_list ap)
+{
+ char xfmt[7] = "";
+ const char *args[2], **arg;
+ char *msg = NULL;
+ const char *err_str = NULL;
+
+ args[0] = args[1] = NULL;
+ arg = args;
+ if(fmt){
+ strlcat(xfmt, "%s", sizeof(xfmt));
+ if(do_errtext)
+ strlcat(xfmt, ": ", sizeof(xfmt));
+ vasprintf(&msg, fmt, ap);
+ if(msg == NULL)
+ return ENOMEM;
+ *arg++ = msg;
+ }
+ if(context && do_errtext){
+ const char *err_msg;
+
+ strlcat(xfmt, "%s", sizeof(xfmt));
+
+ err_str = krb5_get_error_message(context, code);
+ if (err_str != NULL) {
+ *arg++ = err_str;
+ } else {
+ err_msg = krb5_get_err_text(context, code);
+ if (err_msg)
+ *arg++ = err_msg;
+ else
+ *arg++ = "<unknown error>";
+ }
+ }
+
+ if(context && context->warn_dest)
+ krb5_log(context, context->warn_dest, level, xfmt, args[0], args[1]);
+ else
+ warnx(xfmt, args[0], args[1]);
+ free(msg);
+ krb5_free_error_message(context, err_str);
+ return 0;
+}
+
+#define FUNC(ETEXT, CODE, LEVEL) \
+ krb5_error_code ret; \
+ va_list ap; \
+ va_start(ap, fmt); \
+ ret = _warnerr(context, ETEXT, CODE, LEVEL, fmt, ap); \
+ va_end(ap);
+
+#undef __attribute__
+#define __attribute__(X)
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_vwarn(krb5_context context, krb5_error_code code,
+ const char *fmt, va_list ap)
+ __attribute__ ((format (printf, 3, 0)))
+{
+ return _warnerr(context, 1, code, 1, fmt, ap);
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_warn(krb5_context context, krb5_error_code code, const char *fmt, ...)
+ __attribute__ ((format (printf, 3, 4)))
+{
+ FUNC(1, code, 1);
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_vwarnx(krb5_context context, const char *fmt, va_list ap)
+ __attribute__ ((format (printf, 2, 0)))
+{
+ return _warnerr(context, 0, 0, 1, fmt, ap);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_warnx(krb5_context context, const char *fmt, ...)
+ __attribute__ ((format (printf, 2, 3)))
+{
+ FUNC(0, 0, 1);
+ return ret;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_verr(krb5_context context, int eval, krb5_error_code code,
+ const char *fmt, va_list ap)
+ __attribute__ ((noreturn, format (printf, 4, 0)))
+{
+ _warnerr(context, 1, code, 0, fmt, ap);
+ exit(eval);
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_err(krb5_context context, int eval, krb5_error_code code,
+ const char *fmt, ...)
+ __attribute__ ((noreturn, format (printf, 4, 5)))
+{
+ FUNC(1, code, 0);
+ exit(eval);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_verrx(krb5_context context, int eval, const char *fmt, va_list ap)
+ __attribute__ ((noreturn, format (printf, 3, 0)))
+{
+ _warnerr(context, 0, 0, 0, fmt, ap);
+ exit(eval);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_errx(krb5_context context, int eval, const char *fmt, ...)
+ __attribute__ ((noreturn, format (printf, 3, 4)))
+{
+ FUNC(0, 0, 0);
+ exit(eval);
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_vabort(krb5_context context, krb5_error_code code,
+ const char *fmt, va_list ap)
+ __attribute__ ((noreturn, format (printf, 3, 0)))
+{
+ _warnerr(context, 1, code, 0, fmt, ap);
+ abort();
+}
+
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_abort(krb5_context context, krb5_error_code code, const char *fmt, ...)
+ __attribute__ ((noreturn, format (printf, 3, 4)))
+{
+ FUNC(1, code, 0);
+ abort();
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_vabortx(krb5_context context, const char *fmt, va_list ap)
+ __attribute__ ((noreturn, format (printf, 2, 0)))
+{
+ _warnerr(context, 0, 0, 0, fmt, ap);
+ abort();
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_abortx(krb5_context context, const char *fmt, ...)
+ __attribute__ ((noreturn, format (printf, 2, 3)))
+{
+ FUNC(0, 0, 0);
+ abort();
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_set_warn_dest(krb5_context context, krb5_log_facility *fac)
+{
+ context->warn_dest = fac;
+ return 0;
+}
+
+krb5_log_facility * KRB5_LIB_FUNCTION
+krb5_get_warn_dest(krb5_context context)
+{
+ return context->warn_dest;
+}
diff --git a/source4/heimdal/lib/ntlm/heimntlm.h b/source4/heimdal/lib/ntlm/heimntlm.h
new file mode 100644
index 0000000000..c1ed23ec10
--- /dev/null
+++ b/source4/heimdal/lib/ntlm/heimntlm.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef HEIM_NTLM_H
+#define HEIM_NTLM_H
+
+/**
+ * Buffer for storing data in the NTLM library. When filled in by the
+ * library it should be freed with heim_ntlm_free_buf().
+ */
+struct ntlm_buf {
+ size_t length; /**< length buffer data */
+ void *data; /**< pointer to the data itself */
+};
+
+#define NTLM_NEG_UNICODE 0x00000001
+#define NTLM_NEG_TARGET 0x00000004
+#define NTLM_NEG_SIGN 0x00000010
+#define NTLM_NEG_SEAL 0x00000020
+#define NTLM_NEG_NTLM 0x00000200
+
+#define NTLM_SUPPLIED_DOMAIN 0x00001000
+#define NTLM_SUPPLIED_WORKSTAION 0x00002000
+
+#define NTLM_NEG_ALWAYS_SIGN 0x00008000
+#define NTLM_NEG_NTLM2_SESSION 0x00080000
+
+#define NTLM_TARGET_DOMAIN 0x00010000
+#define NTLM_TARGET_SERVER 0x00020000
+#define NTLM_ENC_128 0x20000000
+#define NTLM_NEG_KEYEX 0x40000000
+
+/**
+ * Struct for the NTLM target info, the strings is assumed to be in
+ * UTF8. When filled in by the library it should be freed with
+ * heim_ntlm_free_targetinfo().
+ */
+struct ntlm_targetinfo {
+ char *servername; /**< */
+ char *domainname; /**< */
+ char *dnsdomainname; /**< */
+ char *dnsservername; /**< */
+};
+
+/**
+ * Struct for the NTLM type1 message info, the strings is assumed to
+ * be in UTF8. When filled in by the library it should be freed with
+ * heim_ntlm_free_type1().
+ */
+
+struct ntlm_type1 {
+ uint32_t flags; /**< */
+ char *domain; /**< */
+ char *hostname; /**< */
+ uint32_t os[2]; /**< */
+};
+
+/**
+ * Struct for the NTLM type2 message info, the strings is assumed to
+ * be in UTF8. When filled in by the library it should be freed with
+ * heim_ntlm_free_type2().
+ */
+
+struct ntlm_type2 {
+ uint32_t flags; /**< */
+ char *targetname; /**< */
+ struct ntlm_buf targetinfo; /**< */
+ unsigned char challange[8]; /**< */
+ uint32_t context[2]; /**< */
+ uint32_t os[2]; /**< */
+};
+
+/**
+ * Struct for the NTLM type3 message info, the strings is assumed to
+ * be in UTF8. When filled in by the library it should be freed with
+ * heim_ntlm_free_type3().
+ */
+
+struct ntlm_type3 {
+ uint32_t flags; /**< */
+ char *username; /**< */
+ char *targetname; /**< */
+ struct ntlm_buf lm; /**< */
+ struct ntlm_buf ntlm; /**< */
+ struct ntlm_buf sessionkey; /**< */
+ char *ws; /**< */
+ uint32_t os[2]; /**< */
+};
+
+#include <heimntlm-protos.h>
+
+#endif /* NTLM_NTLM_H */
diff --git a/source4/heimdal/lib/ntlm/ntlm.c b/source4/heimdal/lib/ntlm/ntlm.c
new file mode 100644
index 0000000000..1002b67cc8
--- /dev/null
+++ b/source4/heimdal/lib/ntlm/ntlm.c
@@ -0,0 +1,1375 @@
+/*
+ * Copyright (c) 2006 - 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <config.h>
+
+RCSID("$Id$");
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+
+#include <krb5.h>
+#include <roken.h>
+
+#define HC_DEPRECATED_CRYPTO
+
+#include "krb5-types.h"
+#include "crypto-headers.h"
+
+#include <heimntlm.h>
+
+/*! \mainpage Heimdal NTLM library
+ *
+ * \section intro Introduction
+ *
+ * Heimdal libheimntlm library is a implementation of the NTLM
+ * protocol, both version 1 and 2. The GSS-API mech that uses this
+ * library adds support for transport encryption and integrity
+ * checking.
+ *
+ * NTLM is a protocol for mutual authentication, its still used in
+ * many protocol where Kerberos is not support, one example is
+ * EAP/X802.1x mechanism LEAP from Microsoft and Cisco.
+ *
+ * This is a support library for the core protocol, its used in
+ * Heimdal to implement and GSS-API mechanism. There is also support
+ * in the KDC to do remote digest authenticiation, this to allow
+ * services to authenticate users w/o direct access to the users ntlm
+ * hashes (same as Kerberos arcfour enctype keys).
+ *
+ * More information about the NTLM protocol can found here
+ * http://davenport.sourceforge.net/ntlm.html .
+ *
+ * The Heimdal projects web page: http://www.h5l.org/
+ *
+ * @section ntlm_example NTLM Example
+ *
+ * Example to to use @ref test_ntlm.c .
+ *
+ * @example test_ntlm.c
+ *
+ * Example how to use the NTLM primitives.
+ *
+ */
+
+/** @defgroup ntlm_core Heimdal NTLM library
+ *
+ * The NTLM core functions implement the string2key generation
+ * function, message encode and decode function, and the hash function
+ * functions.
+ */
+
+struct sec_buffer {
+ uint16_t length;
+ uint16_t allocated;
+ uint32_t offset;
+};
+
+static const unsigned char ntlmsigature[8] = "NTLMSSP\x00";
+
+/*
+ *
+ */
+
+#define CHECK(f, e) \
+ do { ret = f ; if (ret != (e)) { ret = EINVAL; goto out; } } while(0)
+
+/**
+ * heim_ntlm_free_buf frees the ntlm buffer
+ *
+ * @param p buffer to be freed
+ *
+ * @ingroup ntlm_core
+ */
+
+void
+heim_ntlm_free_buf(struct ntlm_buf *p)
+{
+ if (p->data)
+ free(p->data);
+ p->data = NULL;
+ p->length = 0;
+}
+
+
+static int
+ascii2ucs2le(const char *string, int up, struct ntlm_buf *buf)
+{
+ unsigned char *p;
+ size_t len, i;
+
+ len = strlen(string);
+ if (len / 2 > UINT_MAX)
+ return ERANGE;
+
+ buf->length = len * 2;
+ buf->data = malloc(buf->length);
+ if (buf->data == NULL && len != 0) {
+ heim_ntlm_free_buf(buf);
+ return ENOMEM;
+ }
+
+ p = buf->data;
+ for (i = 0; i < len; i++) {
+ unsigned char t = (unsigned char)string[i];
+ if (t & 0x80) {
+ heim_ntlm_free_buf(buf);
+ return EINVAL;
+ }
+ if (up)
+ t = toupper(t);
+ p[(i * 2) + 0] = t;
+ p[(i * 2) + 1] = 0;
+ }
+ return 0;
+}
+
+/*
+ *
+ */
+
+static krb5_error_code
+ret_sec_buffer(krb5_storage *sp, struct sec_buffer *buf)
+{
+ krb5_error_code ret;
+ CHECK(krb5_ret_uint16(sp, &buf->length), 0);
+ CHECK(krb5_ret_uint16(sp, &buf->allocated), 0);
+ CHECK(krb5_ret_uint32(sp, &buf->offset), 0);
+out:
+ return ret;
+}
+
+static krb5_error_code
+store_sec_buffer(krb5_storage *sp, const struct sec_buffer *buf)
+{
+ krb5_error_code ret;
+ CHECK(krb5_store_uint16(sp, buf->length), 0);
+ CHECK(krb5_store_uint16(sp, buf->allocated), 0);
+ CHECK(krb5_store_uint32(sp, buf->offset), 0);
+out:
+ return ret;
+}
+
+/*
+ * Strings are either OEM or UNICODE. The later is encoded as ucs2 on
+ * wire, but using utf8 in memory.
+ */
+
+static krb5_error_code
+len_string(int ucs2, const char *s)
+{
+ size_t len = strlen(s);
+ if (ucs2)
+ len *= 2;
+ return len;
+}
+
+static krb5_error_code
+ret_string(krb5_storage *sp, int ucs2, struct sec_buffer *desc, char **s)
+{
+ krb5_error_code ret;
+
+ *s = malloc(desc->length + 1);
+ CHECK(krb5_storage_seek(sp, desc->offset, SEEK_SET), desc->offset);
+ CHECK(krb5_storage_read(sp, *s, desc->length), desc->length);
+ (*s)[desc->length] = '\0';
+
+ if (ucs2) {
+ size_t i;
+ for (i = 0; i < desc->length / 2; i++) {
+ (*s)[i] = (*s)[i * 2];
+ if ((*s)[i * 2 + 1]) {
+ free(*s);
+ *s = NULL;
+ return EINVAL;
+ }
+ }
+ (*s)[i] = '\0';
+ }
+ ret = 0;
+out:
+ return ret;
+
+ return 0;
+}
+
+static krb5_error_code
+put_string(krb5_storage *sp, int ucs2, const char *s)
+{
+ krb5_error_code ret;
+ struct ntlm_buf buf;
+
+ if (ucs2) {
+ ret = ascii2ucs2le(s, 0, &buf);
+ if (ret)
+ return ret;
+ } else {
+ buf.data = rk_UNCONST(s);
+ buf.length = strlen(s);
+ }
+
+ CHECK(krb5_storage_write(sp, buf.data, buf.length), buf.length);
+ if (ucs2)
+ heim_ntlm_free_buf(&buf);
+ ret = 0;
+out:
+ return ret;
+}
+
+/*
+ *
+ */
+
+static krb5_error_code
+ret_buf(krb5_storage *sp, struct sec_buffer *desc, struct ntlm_buf *buf)
+{
+ krb5_error_code ret;
+
+ buf->data = malloc(desc->length);
+ buf->length = desc->length;
+ CHECK(krb5_storage_seek(sp, desc->offset, SEEK_SET), desc->offset);
+ CHECK(krb5_storage_read(sp, buf->data, buf->length), buf->length);
+ ret = 0;
+out:
+ return ret;
+}
+
+static krb5_error_code
+put_buf(krb5_storage *sp, const struct ntlm_buf *buf)
+{
+ krb5_error_code ret;
+ CHECK(krb5_storage_write(sp, buf->data, buf->length), buf->length);
+ ret = 0;
+out:
+ return ret;
+}
+
+/**
+ * Frees the ntlm_targetinfo message
+ *
+ * @param ti targetinfo to be freed
+ *
+ * @ingroup ntlm_core
+ */
+
+void
+heim_ntlm_free_targetinfo(struct ntlm_targetinfo *ti)
+{
+ free(ti->servername);
+ free(ti->domainname);
+ free(ti->dnsdomainname);
+ free(ti->dnsservername);
+ memset(ti, 0, sizeof(*ti));
+}
+
+static int
+encode_ti_blob(krb5_storage *out, uint16_t type, int ucs2, char *s)
+{
+ krb5_error_code ret;
+ CHECK(krb5_store_uint16(out, type), 0);
+ CHECK(krb5_store_uint16(out, len_string(ucs2, s)), 0);
+ CHECK(put_string(out, ucs2, s), 0);
+out:
+ return ret;
+}
+
+/**
+ * Encodes a ntlm_targetinfo message.
+ *
+ * @param ti the ntlm_targetinfo message to encode.
+ * @param ucs2 if the strings should be encoded with ucs2 (selected by flag in message).
+ * @param data is the return buffer with the encoded message, should be
+ * freed with heim_ntlm_free_buf().
+ *
+ * @return In case of success 0 is return, an errors, a errno in what
+ * went wrong.
+ *
+ * @ingroup ntlm_core
+ */
+
+int
+heim_ntlm_encode_targetinfo(const struct ntlm_targetinfo *ti,
+ int ucs2,
+ struct ntlm_buf *data)
+{
+ krb5_error_code ret;
+ krb5_storage *out;
+
+ data->data = NULL;
+ data->length = 0;
+
+ out = krb5_storage_emem();
+ if (out == NULL)
+ return ENOMEM;
+
+ if (ti->servername)
+ CHECK(encode_ti_blob(out, 1, ucs2, ti->servername), 0);
+ if (ti->domainname)
+ CHECK(encode_ti_blob(out, 2, ucs2, ti->domainname), 0);
+ if (ti->dnsservername)
+ CHECK(encode_ti_blob(out, 3, ucs2, ti->dnsservername), 0);
+ if (ti->dnsdomainname)
+ CHECK(encode_ti_blob(out, 4, ucs2, ti->dnsdomainname), 0);
+
+ /* end tag */
+ CHECK(krb5_store_int16(out, 0), 0);
+ CHECK(krb5_store_int16(out, 0), 0);
+
+ {
+ krb5_data d;
+ ret = krb5_storage_to_data(out, &d);
+ data->data = d.data;
+ data->length = d.length;
+ }
+out:
+ krb5_storage_free(out);
+ return ret;
+}
+
+/**
+ * Decodes an NTLM targetinfo message
+ *
+ * @param data input data buffer with the encode NTLM targetinfo message
+ * @param ucs2 if the strings should be encoded with ucs2 (selected by flag in message).
+ * @param ti the decoded target info, should be freed with heim_ntlm_free_targetinfo().
+ *
+ * @return In case of success 0 is return, an errors, a errno in what
+ * went wrong.
+ *
+ * @ingroup ntlm_core
+ */
+
+int
+heim_ntlm_decode_targetinfo(const struct ntlm_buf *data,
+ int ucs2,
+ struct ntlm_targetinfo *ti)
+{
+ memset(ti, 0, sizeof(*ti));
+ return 0;
+}
+
+/**
+ * Frees the ntlm_type1 message
+ *
+ * @param data message to be freed
+ *
+ * @ingroup ntlm_core
+ */
+
+void
+heim_ntlm_free_type1(struct ntlm_type1 *data)
+{
+ if (data->domain)
+ free(data->domain);
+ if (data->hostname)
+ free(data->hostname);
+ memset(data, 0, sizeof(*data));
+}
+
+int
+heim_ntlm_decode_type1(const struct ntlm_buf *buf, struct ntlm_type1 *data)
+{
+ krb5_error_code ret;
+ unsigned char sig[8];
+ uint32_t type;
+ struct sec_buffer domain, hostname;
+ krb5_storage *in;
+
+ memset(data, 0, sizeof(*data));
+
+ in = krb5_storage_from_readonly_mem(buf->data, buf->length);
+ if (in == NULL) {
+ ret = EINVAL;
+ goto out;
+ }
+ krb5_storage_set_byteorder(in, KRB5_STORAGE_BYTEORDER_LE);
+
+ CHECK(krb5_storage_read(in, sig, sizeof(sig)), sizeof(sig));
+ CHECK(memcmp(ntlmsigature, sig, sizeof(ntlmsigature)), 0);
+ CHECK(krb5_ret_uint32(in, &type), 0);
+ CHECK(type, 1);
+ CHECK(krb5_ret_uint32(in, &data->flags), 0);
+ if (data->flags & NTLM_SUPPLIED_DOMAIN)
+ CHECK(ret_sec_buffer(in, &domain), 0);
+ if (data->flags & NTLM_SUPPLIED_WORKSTAION)
+ CHECK(ret_sec_buffer(in, &hostname), 0);
+#if 0
+ if (domain.offset > 32) {
+ CHECK(krb5_ret_uint32(in, &data->os[0]), 0);
+ CHECK(krb5_ret_uint32(in, &data->os[1]), 0);
+ }
+#endif
+ if (data->flags & NTLM_SUPPLIED_DOMAIN)
+ CHECK(ret_string(in, 0, &domain, &data->domain), 0);
+ if (data->flags & NTLM_SUPPLIED_WORKSTAION)
+ CHECK(ret_string(in, 0, &hostname, &data->hostname), 0);
+
+out:
+ krb5_storage_free(in);
+ if (ret)
+ heim_ntlm_free_type1(data);
+
+ return ret;
+}
+
+/**
+ * Encodes an ntlm_type1 message.
+ *
+ * @param type1 the ntlm_type1 message to encode.
+ * @param data is the return buffer with the encoded message, should be
+ * freed with heim_ntlm_free_buf().
+ *
+ * @return In case of success 0 is return, an errors, a errno in what
+ * went wrong.
+ *
+ * @ingroup ntlm_core
+ */
+
+int
+heim_ntlm_encode_type1(const struct ntlm_type1 *type1, struct ntlm_buf *data)
+{
+ krb5_error_code ret;
+ struct sec_buffer domain, hostname;
+ krb5_storage *out;
+ uint32_t base, flags;
+
+ flags = type1->flags;
+ base = 16;
+
+ if (type1->domain) {
+ base += 8;
+ flags |= NTLM_SUPPLIED_DOMAIN;
+ }
+ if (type1->hostname) {
+ base += 8;
+ flags |= NTLM_SUPPLIED_WORKSTAION;
+ }
+ if (type1->os[0])
+ base += 8;
+
+ if (type1->domain) {
+ domain.offset = base;
+ domain.length = len_string(0, type1->domain);
+ domain.allocated = domain.length;
+ }
+ if (type1->hostname) {
+ hostname.offset = domain.allocated + domain.offset;
+ hostname.length = len_string(0, type1->hostname);
+ hostname.allocated = hostname.length;
+ }
+
+ out = krb5_storage_emem();
+ if (out == NULL)
+ return ENOMEM;
+
+ krb5_storage_set_byteorder(out, KRB5_STORAGE_BYTEORDER_LE);
+ CHECK(krb5_storage_write(out, ntlmsigature, sizeof(ntlmsigature)),
+ sizeof(ntlmsigature));
+ CHECK(krb5_store_uint32(out, 1), 0);
+ CHECK(krb5_store_uint32(out, flags), 0);
+
+ if (type1->domain)
+ CHECK(store_sec_buffer(out, &domain), 0);
+ if (type1->hostname)
+ CHECK(store_sec_buffer(out, &hostname), 0);
+ if (type1->os[0]) {
+ CHECK(krb5_store_uint32(out, type1->os[0]), 0);
+ CHECK(krb5_store_uint32(out, type1->os[1]), 0);
+ }
+ if (type1->domain)
+ CHECK(put_string(out, 0, type1->domain), 0);
+ if (type1->hostname)
+ CHECK(put_string(out, 0, type1->hostname), 0);
+
+ {
+ krb5_data d;
+ ret = krb5_storage_to_data(out, &d);
+ data->data = d.data;
+ data->length = d.length;
+ }
+out:
+ krb5_storage_free(out);
+
+ return ret;
+}
+
+/**
+ * Frees the ntlm_type2 message
+ *
+ * @param data message to be freed
+ *
+ * @ingroup ntlm_core
+ */
+
+void
+heim_ntlm_free_type2(struct ntlm_type2 *data)
+{
+ if (data->targetname)
+ free(data->targetname);
+ heim_ntlm_free_buf(&data->targetinfo);
+ memset(data, 0, sizeof(*data));
+}
+
+int
+heim_ntlm_decode_type2(const struct ntlm_buf *buf, struct ntlm_type2 *type2)
+{
+ krb5_error_code ret;
+ unsigned char sig[8];
+ uint32_t type, ctx[2];
+ struct sec_buffer targetname, targetinfo;
+ krb5_storage *in;
+ int ucs2 = 0;
+
+ memset(type2, 0, sizeof(*type2));
+
+ in = krb5_storage_from_readonly_mem(buf->data, buf->length);
+ if (in == NULL) {
+ ret = EINVAL;
+ goto out;
+ }
+ krb5_storage_set_byteorder(in, KRB5_STORAGE_BYTEORDER_LE);
+
+ CHECK(krb5_storage_read(in, sig, sizeof(sig)), sizeof(sig));
+ CHECK(memcmp(ntlmsigature, sig, sizeof(ntlmsigature)), 0);
+ CHECK(krb5_ret_uint32(in, &type), 0);
+ CHECK(type, 2);
+
+ CHECK(ret_sec_buffer(in, &targetname), 0);
+ CHECK(krb5_ret_uint32(in, &type2->flags), 0);
+ if (type2->flags & NTLM_NEG_UNICODE)
+ ucs2 = 1;
+ CHECK(krb5_storage_read(in, type2->challange, sizeof(type2->challange)),
+ sizeof(type2->challange));
+ CHECK(krb5_ret_uint32(in, &ctx[0]), 0); /* context */
+ CHECK(krb5_ret_uint32(in, &ctx[1]), 0);
+ CHECK(ret_sec_buffer(in, &targetinfo), 0);
+ /* os version */
+#if 0
+ CHECK(krb5_ret_uint32(in, &type2->os[0]), 0);
+ CHECK(krb5_ret_uint32(in, &type2->os[1]), 0);
+#endif
+
+ CHECK(ret_string(in, ucs2, &targetname, &type2->targetname), 0);
+ CHECK(ret_buf(in, &targetinfo, &type2->targetinfo), 0);
+ ret = 0;
+
+out:
+ krb5_storage_free(in);
+ if (ret)
+ heim_ntlm_free_type2(type2);
+
+ return ret;
+}
+
+/**
+ * Encodes an ntlm_type2 message.
+ *
+ * @param type2 the ntlm_type2 message to encode.
+ * @param data is the return buffer with the encoded message, should be
+ * freed with heim_ntlm_free_buf().
+ *
+ * @return In case of success 0 is return, an errors, a errno in what
+ * went wrong.
+ *
+ * @ingroup ntlm_core
+ */
+
+int
+heim_ntlm_encode_type2(const struct ntlm_type2 *type2, struct ntlm_buf *data)
+{
+ struct sec_buffer targetname, targetinfo;
+ krb5_error_code ret;
+ krb5_storage *out = NULL;
+ uint32_t base;
+ int ucs2 = 0;
+
+ if (type2->os[0])
+ base = 56;
+ else
+ base = 48;
+
+ if (type2->flags & NTLM_NEG_UNICODE)
+ ucs2 = 1;
+
+ targetname.offset = base;
+ targetname.length = len_string(ucs2, type2->targetname);
+ targetname.allocated = targetname.length;
+
+ targetinfo.offset = targetname.allocated + targetname.offset;
+ targetinfo.length = type2->targetinfo.length;
+ targetinfo.allocated = type2->targetinfo.length;
+
+ out = krb5_storage_emem();
+ if (out == NULL)
+ return ENOMEM;
+
+ krb5_storage_set_byteorder(out, KRB5_STORAGE_BYTEORDER_LE);
+ CHECK(krb5_storage_write(out, ntlmsigature, sizeof(ntlmsigature)),
+ sizeof(ntlmsigature));
+ CHECK(krb5_store_uint32(out, 2), 0);
+ CHECK(store_sec_buffer(out, &targetname), 0);
+ CHECK(krb5_store_uint32(out, type2->flags), 0);
+ CHECK(krb5_storage_write(out, type2->challange, sizeof(type2->challange)),
+ sizeof(type2->challange));
+ CHECK(krb5_store_uint32(out, 0), 0); /* context */
+ CHECK(krb5_store_uint32(out, 0), 0);
+ CHECK(store_sec_buffer(out, &targetinfo), 0);
+ /* os version */
+ if (type2->os[0]) {
+ CHECK(krb5_store_uint32(out, type2->os[0]), 0);
+ CHECK(krb5_store_uint32(out, type2->os[1]), 0);
+ }
+ CHECK(put_string(out, ucs2, type2->targetname), 0);
+ CHECK(krb5_storage_write(out, type2->targetinfo.data,
+ type2->targetinfo.length),
+ type2->targetinfo.length);
+
+ {
+ krb5_data d;
+ ret = krb5_storage_to_data(out, &d);
+ data->data = d.data;
+ data->length = d.length;
+ }
+
+out:
+ krb5_storage_free(out);
+
+ return ret;
+}
+
+/**
+ * Frees the ntlm_type3 message
+ *
+ * @param data message to be freed
+ *
+ * @ingroup ntlm_core
+ */
+
+void
+heim_ntlm_free_type3(struct ntlm_type3 *data)
+{
+ heim_ntlm_free_buf(&data->lm);
+ heim_ntlm_free_buf(&data->ntlm);
+ if (data->targetname)
+ free(data->targetname);
+ if (data->username)
+ free(data->username);
+ if (data->ws)
+ free(data->ws);
+ heim_ntlm_free_buf(&data->sessionkey);
+ memset(data, 0, sizeof(*data));
+}
+
+/*
+ *
+ */
+
+int
+heim_ntlm_decode_type3(const struct ntlm_buf *buf,
+ int ucs2,
+ struct ntlm_type3 *type3)
+{
+ krb5_error_code ret;
+ unsigned char sig[8];
+ uint32_t type;
+ krb5_storage *in;
+ struct sec_buffer lm, ntlm, target, username, sessionkey, ws;
+
+ memset(type3, 0, sizeof(*type3));
+ memset(&sessionkey, 0, sizeof(sessionkey));
+
+ in = krb5_storage_from_readonly_mem(buf->data, buf->length);
+ if (in == NULL) {
+ ret = EINVAL;
+ goto out;
+ }
+ krb5_storage_set_byteorder(in, KRB5_STORAGE_BYTEORDER_LE);
+
+ CHECK(krb5_storage_read(in, sig, sizeof(sig)), sizeof(sig));
+ CHECK(memcmp(ntlmsigature, sig, sizeof(ntlmsigature)), 0);
+ CHECK(krb5_ret_uint32(in, &type), 0);
+ CHECK(type, 3);
+ CHECK(ret_sec_buffer(in, &lm), 0);
+ CHECK(ret_sec_buffer(in, &ntlm), 0);
+ CHECK(ret_sec_buffer(in, &target), 0);
+ CHECK(ret_sec_buffer(in, &username), 0);
+ CHECK(ret_sec_buffer(in, &ws), 0);
+ if (lm.offset >= 60) {
+ CHECK(ret_sec_buffer(in, &sessionkey), 0);
+ }
+ if (lm.offset >= 64) {
+ CHECK(krb5_ret_uint32(in, &type3->flags), 0);
+ }
+ if (lm.offset >= 72) {
+ CHECK(krb5_ret_uint32(in, &type3->os[0]), 0);
+ CHECK(krb5_ret_uint32(in, &type3->os[1]), 0);
+ }
+ CHECK(ret_buf(in, &lm, &type3->lm), 0);
+ CHECK(ret_buf(in, &ntlm, &type3->ntlm), 0);
+ CHECK(ret_string(in, ucs2, &target, &type3->targetname), 0);
+ CHECK(ret_string(in, ucs2, &username, &type3->username), 0);
+ CHECK(ret_string(in, ucs2, &ws, &type3->ws), 0);
+ if (sessionkey.offset)
+ CHECK(ret_buf(in, &sessionkey, &type3->sessionkey), 0);
+
+out:
+ krb5_storage_free(in);
+ if (ret)
+ heim_ntlm_free_type3(type3);
+
+ return ret;
+}
+
+/**
+ * Encodes an ntlm_type3 message.
+ *
+ * @param type3 the ntlm_type3 message to encode.
+ * @param data is the return buffer with the encoded message, should be
+ * freed with heim_ntlm_free_buf().
+ *
+ * @return In case of success 0 is return, an errors, a errno in what
+ * went wrong.
+ *
+ * @ingroup ntlm_core
+ */
+
+int
+heim_ntlm_encode_type3(const struct ntlm_type3 *type3, struct ntlm_buf *data)
+{
+ struct sec_buffer lm, ntlm, target, username, sessionkey, ws;
+ krb5_error_code ret;
+ krb5_storage *out = NULL;
+ uint32_t base;
+ int ucs2 = 0;
+
+ memset(&lm, 0, sizeof(lm));
+ memset(&ntlm, 0, sizeof(ntlm));
+ memset(&target, 0, sizeof(target));
+ memset(&username, 0, sizeof(username));
+ memset(&ws, 0, sizeof(ws));
+ memset(&sessionkey, 0, sizeof(sessionkey));
+
+ base = 52;
+ if (type3->sessionkey.length) {
+ base += 8; /* sessionkey sec buf */
+ base += 4; /* flags */
+ }
+ if (type3->os[0]) {
+ base += 8;
+ }
+
+ if (type3->flags & NTLM_NEG_UNICODE)
+ ucs2 = 1;
+
+ lm.offset = base;
+ lm.length = type3->lm.length;
+ lm.allocated = type3->lm.length;
+
+ ntlm.offset = lm.offset + lm.allocated;
+ ntlm.length = type3->ntlm.length;
+ ntlm.allocated = ntlm.length;
+
+ target.offset = ntlm.offset + ntlm.allocated;
+ target.length = len_string(ucs2, type3->targetname);
+ target.allocated = target.length;
+
+ username.offset = target.offset + target.allocated;
+ username.length = len_string(ucs2, type3->username);
+ username.allocated = username.length;
+
+ ws.offset = username.offset + username.allocated;
+ ws.length = len_string(ucs2, type3->ws);
+ ws.allocated = ws.length;
+
+ sessionkey.offset = ws.offset + ws.allocated;
+ sessionkey.length = type3->sessionkey.length;
+ sessionkey.allocated = type3->sessionkey.length;
+
+ out = krb5_storage_emem();
+ if (out == NULL)
+ return ENOMEM;
+
+ krb5_storage_set_byteorder(out, KRB5_STORAGE_BYTEORDER_LE);
+ CHECK(krb5_storage_write(out, ntlmsigature, sizeof(ntlmsigature)),
+ sizeof(ntlmsigature));
+ CHECK(krb5_store_uint32(out, 3), 0);
+
+ CHECK(store_sec_buffer(out, &lm), 0);
+ CHECK(store_sec_buffer(out, &ntlm), 0);
+ CHECK(store_sec_buffer(out, &target), 0);
+ CHECK(store_sec_buffer(out, &username), 0);
+ CHECK(store_sec_buffer(out, &ws), 0);
+ /* optional */
+ if (type3->sessionkey.length) {
+ CHECK(store_sec_buffer(out, &sessionkey), 0);
+ CHECK(krb5_store_uint32(out, type3->flags), 0);
+ }
+#if 0
+ CHECK(krb5_store_uint32(out, 0), 0); /* os0 */
+ CHECK(krb5_store_uint32(out, 0), 0); /* os1 */
+#endif
+
+ CHECK(put_buf(out, &type3->lm), 0);
+ CHECK(put_buf(out, &type3->ntlm), 0);
+ CHECK(put_string(out, ucs2, type3->targetname), 0);
+ CHECK(put_string(out, ucs2, type3->username), 0);
+ CHECK(put_string(out, ucs2, type3->ws), 0);
+ CHECK(put_buf(out, &type3->sessionkey), 0);
+
+ {
+ krb5_data d;
+ ret = krb5_storage_to_data(out, &d);
+ data->data = d.data;
+ data->length = d.length;
+ }
+
+out:
+ krb5_storage_free(out);
+
+ return ret;
+}
+
+
+/*
+ *
+ */
+
+static void
+splitandenc(unsigned char *hash,
+ unsigned char *challange,
+ unsigned char *answer)
+{
+ DES_cblock key;
+ DES_key_schedule sched;
+
+ ((unsigned char*)key)[0] = hash[0];
+ ((unsigned char*)key)[1] = (hash[0] << 7) | (hash[1] >> 1);
+ ((unsigned char*)key)[2] = (hash[1] << 6) | (hash[2] >> 2);
+ ((unsigned char*)key)[3] = (hash[2] << 5) | (hash[3] >> 3);
+ ((unsigned char*)key)[4] = (hash[3] << 4) | (hash[4] >> 4);
+ ((unsigned char*)key)[5] = (hash[4] << 3) | (hash[5] >> 5);
+ ((unsigned char*)key)[6] = (hash[5] << 2) | (hash[6] >> 6);
+ ((unsigned char*)key)[7] = (hash[6] << 1);
+
+ DES_set_odd_parity(&key);
+ DES_set_key_unchecked(&key, &sched);
+ DES_ecb_encrypt((DES_cblock *)challange, (DES_cblock *)answer, &sched, 1);
+ memset(&sched, 0, sizeof(sched));
+ memset(key, 0, sizeof(key));
+}
+
+/**
+ * Calculate the NTLM key, the password is assumed to be in UTF8.
+ *
+ * @param password password to calcute the key for.
+ * @param key calcuted key, should be freed with heim_ntlm_free_buf().
+ *
+ * @return In case of success 0 is return, an errors, a errno in what
+ * went wrong.
+ *
+ * @ingroup ntlm_core
+ */
+
+int
+heim_ntlm_nt_key(const char *password, struct ntlm_buf *key)
+{
+ struct ntlm_buf buf;
+ MD4_CTX ctx;
+ int ret;
+
+ key->data = malloc(MD5_DIGEST_LENGTH);
+ if (key->data == NULL)
+ return ENOMEM;
+ key->length = MD5_DIGEST_LENGTH;
+
+ ret = ascii2ucs2le(password, 0, &buf);
+ if (ret) {
+ heim_ntlm_free_buf(key);
+ return ret;
+ }
+ MD4_Init(&ctx);
+ MD4_Update(&ctx, buf.data, buf.length);
+ MD4_Final(key->data, &ctx);
+ heim_ntlm_free_buf(&buf);
+ return 0;
+}
+
+/**
+ * Calculate NTLMv1 response hash
+ *
+ * @param key the ntlm v1 key
+ * @param len length of key
+ * @param challange sent by the server
+ * @param answer calculated answer, should be freed with heim_ntlm_free_buf().
+ *
+ * @return In case of success 0 is return, an errors, a errno in what
+ * went wrong.
+ *
+ * @ingroup ntlm_core
+ */
+
+int
+heim_ntlm_calculate_ntlm1(void *key, size_t len,
+ unsigned char challange[8],
+ struct ntlm_buf *answer)
+{
+ unsigned char res[21];
+
+ if (len != MD4_DIGEST_LENGTH)
+ return EINVAL;
+
+ memcpy(res, key, len);
+ memset(&res[MD4_DIGEST_LENGTH], 0, sizeof(res) - MD4_DIGEST_LENGTH);
+
+ answer->data = malloc(24);
+ if (answer->data == NULL)
+ return ENOMEM;
+ answer->length = 24;
+
+ splitandenc(&res[0], challange, ((unsigned char *)answer->data) + 0);
+ splitandenc(&res[7], challange, ((unsigned char *)answer->data) + 8);
+ splitandenc(&res[14], challange, ((unsigned char *)answer->data) + 16);
+
+ return 0;
+}
+
+/**
+ * Generates an NTLMv1 session random with assosited session master key.
+ *
+ * @param key the ntlm v1 key
+ * @param len length of key
+ * @param session generated session nonce, should be freed with heim_ntlm_free_buf().
+ * @param master calculated session master key, should be freed with heim_ntlm_free_buf().
+ *
+ * @return In case of success 0 is return, an errors, a errno in what
+ * went wrong.
+ *
+ * @ingroup ntlm_core
+ */
+
+int
+heim_ntlm_build_ntlm1_master(void *key, size_t len,
+ struct ntlm_buf *session,
+ struct ntlm_buf *master)
+{
+ RC4_KEY rc4;
+
+ memset(master, 0, sizeof(*master));
+ memset(session, 0, sizeof(*session));
+
+ if (len != MD4_DIGEST_LENGTH)
+ return EINVAL;
+
+ session->length = MD4_DIGEST_LENGTH;
+ session->data = malloc(session->length);
+ if (session->data == NULL) {
+ session->length = 0;
+ return EINVAL;
+ }
+ master->length = MD4_DIGEST_LENGTH;
+ master->data = malloc(master->length);
+ if (master->data == NULL) {
+ heim_ntlm_free_buf(master);
+ heim_ntlm_free_buf(session);
+ return EINVAL;
+ }
+
+ {
+ unsigned char sessionkey[MD4_DIGEST_LENGTH];
+ MD4_CTX ctx;
+
+ MD4_Init(&ctx);
+ MD4_Update(&ctx, key, len);
+ MD4_Final(sessionkey, &ctx);
+
+ RC4_set_key(&rc4, sizeof(sessionkey), sessionkey);
+ }
+
+ if (RAND_bytes(session->data, session->length) != 1) {
+ heim_ntlm_free_buf(master);
+ heim_ntlm_free_buf(session);
+ return EINVAL;
+ }
+
+ RC4(&rc4, master->length, session->data, master->data);
+ memset(&rc4, 0, sizeof(rc4));
+
+ return 0;
+}
+
+/**
+ * Generates an NTLMv2 session key.
+ *
+ * @param key the ntlm key
+ * @param len length of key
+ * @param username name of the user, as sent in the message, assumed to be in UTF8.
+ * @param target the name of the target, assumed to be in UTF8.
+ * @param ntlmv2 the ntlmv2 session key
+ *
+ * @ingroup ntlm_core
+ */
+
+void
+heim_ntlm_ntlmv2_key(const void *key, size_t len,
+ const char *username,
+ const char *target,
+ unsigned char ntlmv2[16])
+{
+ unsigned int hmaclen;
+ HMAC_CTX c;
+
+ HMAC_CTX_init(&c);
+ HMAC_Init_ex(&c, key, len, EVP_md5(), NULL);
+ {
+ struct ntlm_buf buf;
+ /* uppercase username and turn it into ucs2-le */
+ ascii2ucs2le(username, 1, &buf);
+ HMAC_Update(&c, buf.data, buf.length);
+ free(buf.data);
+ /* uppercase target and turn into ucs2-le */
+ ascii2ucs2le(target, 1, &buf);
+ HMAC_Update(&c, buf.data, buf.length);
+ free(buf.data);
+ }
+ HMAC_Final(&c, ntlmv2, &hmaclen);
+ HMAC_CTX_cleanup(&c);
+
+}
+
+/*
+ *
+ */
+
+#define NTTIME_EPOCH 0x019DB1DED53E8000LL
+
+static uint64_t
+unix2nttime(time_t unix_time)
+{
+ long long wt;
+ wt = unix_time * (uint64_t)10000000 + (uint64_t)NTTIME_EPOCH;
+ return wt;
+}
+
+static time_t
+nt2unixtime(uint64_t t)
+{
+ t = ((t - (uint64_t)NTTIME_EPOCH) / (uint64_t)10000000);
+ if (t > (((time_t)(~(uint64_t)0)) >> 1))
+ return 0;
+ return (time_t)t;
+}
+
+
+/**
+ * Calculate NTLMv2 response
+ *
+ * @param key the ntlm key
+ * @param len length of key
+ * @param username name of the user, as sent in the message, assumed to be in UTF8.
+ * @param target the name of the target, assumed to be in UTF8.
+ * @param serverchallange challange as sent by the server in the type2 message.
+ * @param infotarget infotarget as sent by the server in the type2 message.
+ * @param ntlmv2 calculated session key
+ * @param answer ntlm response answer, should be freed with heim_ntlm_free_buf().
+ *
+ * @return In case of success 0 is return, an errors, a errno in what
+ * went wrong.
+ *
+ * @ingroup ntlm_core
+ */
+
+int
+heim_ntlm_calculate_ntlm2(const void *key, size_t len,
+ const char *username,
+ const char *target,
+ const unsigned char serverchallange[8],
+ const struct ntlm_buf *infotarget,
+ unsigned char ntlmv2[16],
+ struct ntlm_buf *answer)
+{
+ krb5_error_code ret;
+ krb5_data data;
+ unsigned int hmaclen;
+ unsigned char ntlmv2answer[16];
+ krb5_storage *sp;
+ unsigned char clientchallange[8];
+ HMAC_CTX c;
+ uint64_t t;
+
+ t = unix2nttime(time(NULL));
+
+ if (RAND_bytes(clientchallange, sizeof(clientchallange)) != 1)
+ return EINVAL;
+
+ /* calculate ntlmv2 key */
+
+ heim_ntlm_ntlmv2_key(key, len, username, target, ntlmv2);
+
+ /* calculate and build ntlmv2 answer */
+
+ sp = krb5_storage_emem();
+ if (sp == NULL)
+ return ENOMEM;
+ krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
+
+ CHECK(krb5_store_uint32(sp, 0x00000101), 0);
+ CHECK(krb5_store_uint32(sp, 0), 0);
+ /* timestamp le 64 bit ts */
+ CHECK(krb5_store_uint32(sp, t & 0xffffffff), 0);
+ CHECK(krb5_store_uint32(sp, t >> 32), 0);
+
+ CHECK(krb5_storage_write(sp, clientchallange, 8), 8);
+
+ CHECK(krb5_store_uint32(sp, 0), 0); /* unknown but zero will work */
+ CHECK(krb5_storage_write(sp, infotarget->data, infotarget->length),
+ infotarget->length);
+ CHECK(krb5_store_uint32(sp, 0), 0); /* unknown but zero will work */
+
+ CHECK(krb5_storage_to_data(sp, &data), 0);
+ krb5_storage_free(sp);
+ sp = NULL;
+
+ HMAC_CTX_init(&c);
+ HMAC_Init_ex(&c, ntlmv2, 16, EVP_md5(), NULL);
+ HMAC_Update(&c, serverchallange, 8);
+ HMAC_Update(&c, data.data, data.length);
+ HMAC_Final(&c, ntlmv2answer, &hmaclen);
+ HMAC_CTX_cleanup(&c);
+
+ sp = krb5_storage_emem();
+ if (sp == NULL) {
+ krb5_data_free(&data);
+ return ENOMEM;
+ }
+
+ CHECK(krb5_storage_write(sp, ntlmv2answer, 16), 16);
+ CHECK(krb5_storage_write(sp, data.data, data.length), data.length);
+ krb5_data_free(&data);
+
+ CHECK(krb5_storage_to_data(sp, &data), 0);
+ krb5_storage_free(sp);
+ sp = NULL;
+
+ answer->data = data.data;
+ answer->length = data.length;
+
+ return 0;
+out:
+ if (sp)
+ krb5_storage_free(sp);
+ return ret;
+}
+
+static const int authtimediff = 3600 * 2; /* 2 hours */
+
+/**
+ * Verify NTLMv2 response.
+ *
+ * @param key the ntlm key
+ * @param len length of key
+ * @param username name of the user, as sent in the message, assumed to be in UTF8.
+ * @param target the name of the target, assumed to be in UTF8.
+ * @param now the time now (0 if the library should pick it up itself)
+ * @param serverchallange challange as sent by the server in the type2 message.
+ * @param answer ntlm response answer, should be freed with heim_ntlm_free_buf().
+ * @param infotarget infotarget as sent by the server in the type2 message.
+ * @param ntlmv2 calculated session key
+ *
+ * @return In case of success 0 is return, an errors, a errno in what
+ * went wrong.
+ *
+ * @ingroup ntlm_core
+ */
+
+int
+heim_ntlm_verify_ntlm2(const void *key, size_t len,
+ const char *username,
+ const char *target,
+ time_t now,
+ const unsigned char serverchallange[8],
+ const struct ntlm_buf *answer,
+ struct ntlm_buf *infotarget,
+ unsigned char ntlmv2[16])
+{
+ krb5_error_code ret;
+ unsigned int hmaclen;
+ unsigned char clientanswer[16];
+ unsigned char clientnonce[8];
+ unsigned char serveranswer[16];
+ krb5_storage *sp;
+ HMAC_CTX c;
+ uint64_t t;
+ time_t authtime;
+ uint32_t temp;
+
+ infotarget->length = 0;
+ infotarget->data = NULL;
+
+ if (answer->length < 16)
+ return EINVAL;
+
+ if (now == 0)
+ now = time(NULL);
+
+ /* calculate ntlmv2 key */
+
+ heim_ntlm_ntlmv2_key(key, len, username, target, ntlmv2);
+
+ /* calculate and build ntlmv2 answer */
+
+ sp = krb5_storage_from_readonly_mem(answer->data, answer->length);
+ if (sp == NULL)
+ return ENOMEM;
+ krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
+
+ CHECK(krb5_storage_read(sp, clientanswer, 16), 16);
+
+ CHECK(krb5_ret_uint32(sp, &temp), 0);
+ CHECK(temp, 0x00000101);
+ CHECK(krb5_ret_uint32(sp, &temp), 0);
+ CHECK(temp, 0);
+ /* timestamp le 64 bit ts */
+ CHECK(krb5_ret_uint32(sp, &temp), 0);
+ t = temp;
+ CHECK(krb5_ret_uint32(sp, &temp), 0);
+ t |= ((uint64_t)temp)<< 32;
+
+ authtime = nt2unixtime(t);
+
+ if (abs((int)(authtime - now)) > authtimediff) {
+ ret = EINVAL;
+ goto out;
+ }
+
+ /* client challange */
+ CHECK(krb5_storage_read(sp, clientnonce, 8), 8);
+
+ CHECK(krb5_ret_uint32(sp, &temp), 0); /* unknown */
+
+ /* should really unparse the infotarget, but lets pick up everything */
+ infotarget->length = answer->length - krb5_storage_seek(sp, 0, SEEK_CUR);
+ infotarget->data = malloc(infotarget->length);
+ if (infotarget->data == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+ CHECK(krb5_storage_read(sp, infotarget->data, infotarget->length),
+ infotarget->length);
+ /* XXX remove the unknown ?? */
+ krb5_storage_free(sp);
+ sp = NULL;
+
+ HMAC_CTX_init(&c);
+ HMAC_Init_ex(&c, ntlmv2, 16, EVP_md5(), NULL);
+ HMAC_Update(&c, serverchallange, 8);
+ HMAC_Update(&c, ((unsigned char *)answer->data) + 16, answer->length - 16);
+ HMAC_Final(&c, serveranswer, &hmaclen);
+ HMAC_CTX_cleanup(&c);
+
+ if (memcmp(serveranswer, clientanswer, 16) != 0) {
+ heim_ntlm_free_buf(infotarget);
+ return EINVAL;
+ }
+
+ return 0;
+out:
+ heim_ntlm_free_buf(infotarget);
+ if (sp)
+ krb5_storage_free(sp);
+ return ret;
+}
+
+
+/*
+ * Calculate the NTLM2 Session Response
+ *
+ * @param clnt_nonce client nonce
+ * @param svr_chal server challage
+ * @param ntlm2_hash ntlm hash
+ * @param lm The LM response, should be freed with heim_ntlm_free_buf().
+ * @param ntlm The NTLM response, should be freed with heim_ntlm_free_buf().
+ *
+ * @return In case of success 0 is return, an errors, a errno in what
+ * went wrong.
+ *
+ * @ingroup ntlm_core
+ */
+
+int
+heim_ntlm_calculate_ntlm2_sess(const unsigned char clnt_nonce[8],
+ const unsigned char svr_chal[8],
+ const unsigned char ntlm_hash[16],
+ struct ntlm_buf *lm,
+ struct ntlm_buf *ntlm)
+{
+ unsigned char ntlm2_sess_hash[MD5_DIGEST_LENGTH];
+ unsigned char res[21], *resp;
+ MD5_CTX md5;
+
+ lm->data = malloc(24);
+ if (lm->data == NULL)
+ return ENOMEM;
+ lm->length = 24;
+
+ ntlm->data = malloc(24);
+ if (ntlm->data == NULL) {
+ free(lm->data);
+ lm->data = NULL;
+ return ENOMEM;
+ }
+ ntlm->length = 24;
+
+ /* first setup the lm resp */
+ memset(lm->data, 0, 24);
+ memcpy(lm->data, clnt_nonce, 8);
+
+ MD5_Init(&md5);
+ MD5_Update(&md5, svr_chal, 8); /* session nonce part 1 */
+ MD5_Update(&md5, clnt_nonce, 8); /* session nonce part 2 */
+ MD5_Final(ntlm2_sess_hash, &md5); /* will only use first 8 bytes */
+
+ memset(res, 0, sizeof(res));
+ memcpy(res, ntlm_hash, 16);
+
+ resp = ntlm->data;
+ splitandenc(&res[0], ntlm2_sess_hash, resp + 0);
+ splitandenc(&res[7], ntlm2_sess_hash, resp + 8);
+ splitandenc(&res[14], ntlm2_sess_hash, resp + 16);
+
+ return 0;
+}
diff --git a/source4/heimdal/lib/roken/base64.c b/source4/heimdal/lib/roken/base64.c
new file mode 100644
index 0000000000..d0096447b3
--- /dev/null
+++ b/source4/heimdal/lib/roken/base64.c
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 1995-2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+RCSID("$Id$");
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include "base64.h"
+
+static const char base64_chars[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static int
+pos(char c)
+{
+ const char *p;
+ for (p = base64_chars; *p; p++)
+ if (*p == c)
+ return p - base64_chars;
+ return -1;
+}
+
+int ROKEN_LIB_FUNCTION
+base64_encode(const void *data, int size, char **str)
+{
+ char *s, *p;
+ int i;
+ int c;
+ const unsigned char *q;
+
+ p = s = (char *) malloc(size * 4 / 3 + 4);
+ if (p == NULL)
+ return -1;
+ q = (const unsigned char *) data;
+
+ for (i = 0; i < size;) {
+ c = q[i++];
+ c *= 256;
+ if (i < size)
+ c += q[i];
+ i++;
+ c *= 256;
+ if (i < size)
+ c += q[i];
+ i++;
+ p[0] = base64_chars[(c & 0x00fc0000) >> 18];
+ p[1] = base64_chars[(c & 0x0003f000) >> 12];
+ p[2] = base64_chars[(c & 0x00000fc0) >> 6];
+ p[3] = base64_chars[(c & 0x0000003f) >> 0];
+ if (i > size)
+ p[3] = '=';
+ if (i > size + 1)
+ p[2] = '=';
+ p += 4;
+ }
+ *p = 0;
+ *str = s;
+ return strlen(s);
+}
+
+#define DECODE_ERROR 0xffffffff
+
+static unsigned int
+token_decode(const char *token)
+{
+ int i;
+ unsigned int val = 0;
+ int marker = 0;
+ if (strlen(token) < 4)
+ return DECODE_ERROR;
+ for (i = 0; i < 4; i++) {
+ val *= 64;
+ if (token[i] == '=')
+ marker++;
+ else if (marker > 0)
+ return DECODE_ERROR;
+ else
+ val += pos(token[i]);
+ }
+ if (marker > 2)
+ return DECODE_ERROR;
+ return (marker << 24) | val;
+}
+
+int ROKEN_LIB_FUNCTION
+base64_decode(const char *str, void *data)
+{
+ const char *p;
+ unsigned char *q;
+
+ q = data;
+ for (p = str; *p && (*p == '=' || strchr(base64_chars, *p)); p += 4) {
+ unsigned int val = token_decode(p);
+ unsigned int marker = (val >> 24) & 0xff;
+ if (val == DECODE_ERROR)
+ return -1;
+ *q++ = (val >> 16) & 0xff;
+ if (marker < 2)
+ *q++ = (val >> 8) & 0xff;
+ if (marker < 1)
+ *q++ = val & 0xff;
+ }
+ return q - (unsigned char *) data;
+}
diff --git a/source4/heimdal/lib/roken/base64.h b/source4/heimdal/lib/roken/base64.h
new file mode 100644
index 0000000000..f42c0ba429
--- /dev/null
+++ b/source4/heimdal/lib/roken/base64.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 1995, 1996, 1997 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef _BASE64_H_
+#define _BASE64_H_
+
+#ifndef ROKEN_LIB_FUNCTION
+#ifdef _WIN32
+#define ROKEN_LIB_FUNCTION _stdcall
+#else
+#define ROKEN_LIB_FUNCTION
+#endif
+#endif
+
+int ROKEN_LIB_FUNCTION
+base64_encode(const void *, int, char **);
+
+int ROKEN_LIB_FUNCTION
+base64_decode(const char *, void *);
+
+#endif
diff --git a/source4/heimdal/lib/roken/bswap.c b/source4/heimdal/lib/roken/bswap.c
new file mode 100644
index 0000000000..a87345be3f
--- /dev/null
+++ b/source4/heimdal/lib/roken/bswap.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include "roken.h"
+
+RCSID("$Id$");
+
+#ifndef HAVE_BSWAP32
+
+unsigned int ROKEN_LIB_FUNCTION
+bswap32 (unsigned int val)
+{
+ return (val & 0xff) << 24 |
+ (val & 0xff00) << 8 |
+ (val & 0xff0000) >> 8 |
+ (val & 0xff000000) >> 24;
+}
+#endif
+
+#ifndef HAVE_BSWAP16
+
+unsigned short ROKEN_LIB_FUNCTION
+bswap16 (unsigned short val)
+{
+ return (val & 0xff) << 8 |
+ (val & 0xff00) >> 8;
+}
+#endif
diff --git a/source4/heimdal/lib/roken/cloexec.c b/source4/heimdal/lib/roken/cloexec.c
new file mode 100644
index 0000000000..7a64233309
--- /dev/null
+++ b/source4/heimdal/lib/roken/cloexec.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+RCSID("$Id$");
+#endif
+
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "roken.h"
+
+void ROKEN_LIB_FUNCTION
+rk_cloexec(int fd)
+{
+ int ret;
+
+ ret = fcntl(fd, F_GETFD);
+ if (ret == -1)
+ return;
+ if (fcntl(fd, F_SETFD, ret | FD_CLOEXEC) == -1)
+ return;
+}
+
+void ROKEN_LIB_FUNCTION
+rk_cloexec_file(FILE *f)
+{
+ rk_cloexec(fileno(f));
+}
diff --git a/source4/heimdal/lib/roken/closefrom.c b/source4/heimdal/lib/roken/closefrom.c
new file mode 100644
index 0000000000..9198c05e26
--- /dev/null
+++ b/source4/heimdal/lib/roken/closefrom.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+RCSID("$Id$");
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "roken.h"
+
+int ROKEN_LIB_FUNCTION
+closefrom(int fd)
+{
+ int num = getdtablesize();
+
+ if (num < 0)
+ num = 1024; /* XXX */
+
+ for (; fd <= num; fd++)
+ close(fd);
+
+ return 0;
+}
diff --git a/source4/heimdal/lib/roken/copyhostent.c b/source4/heimdal/lib/roken/copyhostent.c
new file mode 100644
index 0000000000..58f5a112f2
--- /dev/null
+++ b/source4/heimdal/lib/roken/copyhostent.c
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 1999 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+RCSID("$Id$");
+#endif
+
+#include "roken.h"
+
+/*
+ * return a malloced copy of `h'
+ */
+
+struct hostent * ROKEN_LIB_FUNCTION
+copyhostent (const struct hostent *h)
+{
+ struct hostent *res;
+ char **p;
+ int i, n;
+
+ res = malloc (sizeof (*res));
+ if (res == NULL)
+ return NULL;
+ res->h_name = NULL;
+ res->h_aliases = NULL;
+ res->h_addrtype = h->h_addrtype;
+ res->h_length = h->h_length;
+ res->h_addr_list = NULL;
+ res->h_name = strdup (h->h_name);
+ if (res->h_name == NULL) {
+ freehostent (res);
+ return NULL;
+ }
+ for (n = 0, p = h->h_aliases; *p != NULL; ++p)
+ ++n;
+ res->h_aliases = malloc ((n + 1) * sizeof(*res->h_aliases));
+ if (res->h_aliases == NULL) {
+ freehostent (res);
+ return NULL;
+ }
+ for (i = 0; i < n + 1; ++i)
+ res->h_aliases[i] = NULL;
+ for (i = 0; i < n; ++i) {
+ res->h_aliases[i] = strdup (h->h_aliases[i]);
+ if (res->h_aliases[i] == NULL) {
+ freehostent (res);
+ return NULL;
+ }
+ }
+
+ for (n = 0, p = h->h_addr_list; *p != NULL; ++p)
+ ++n;
+ res->h_addr_list = malloc ((n + 1) * sizeof(*res->h_addr_list));
+ if (res->h_addr_list == NULL) {
+ freehostent (res);
+ return NULL;
+ }
+ for (i = 0; i < n + 1; ++i) {
+ res->h_addr_list[i] = NULL;
+ }
+ for (i = 0; i < n; ++i) {
+ res->h_addr_list[i] = malloc (h->h_length);
+ if (res->h_addr_list[i] == NULL) {
+ freehostent (res);
+ return NULL;
+ }
+ memcpy (res->h_addr_list[i], h->h_addr_list[i], h->h_length);
+ }
+ return res;
+}
+
diff --git a/source4/heimdal/lib/roken/dumpdata.c b/source4/heimdal/lib/roken/dumpdata.c
new file mode 100644
index 0000000000..88a86815ef
--- /dev/null
+++ b/source4/heimdal/lib/roken/dumpdata.c
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+RCSID("$Id$");
+#endif
+
+#include <unistd.h>
+
+#include "roken.h"
+
+/*
+ * Write datablob to a filename, don't care about errors.
+ */
+
+void ROKEN_LIB_FUNCTION
+rk_dumpdata (const char *filename, const void *buf, size_t size)
+{
+ int fd;
+
+ fd = open(filename, O_WRONLY|O_TRUNC|O_CREAT, 0640);
+ if (fd < 0)
+ return;
+ net_write(fd, buf, size);
+ close(fd);
+}
+
+/*
+ * Read all data from a filename, care about errors.
+ */
+
+int ROKEN_LIB_FUNCTION
+rk_undumpdata(const char *filename, void **buf, size_t *size)
+{
+ struct stat sb;
+ int fd, ret;
+ ssize_t sret;
+
+ *buf = NULL;
+
+ fd = open(filename, O_RDONLY, 0);
+ if (fd < 0)
+ return errno;
+ if (fstat(fd, &sb) != 0){
+ ret = errno;
+ goto out;
+ }
+ *buf = malloc(sb.st_size);
+ if (*buf == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+ *size = sb.st_size;
+
+ sret = net_read(fd, *buf, *size);
+ if (sret < 0)
+ ret = errno;
+ else if (sret != *size) {
+ ret = EINVAL;
+ free(*buf);
+ *buf = NULL;
+ } else
+ ret = 0;
+
+ out:
+ close(fd);
+ return ret;
+}
diff --git a/source4/heimdal/lib/roken/ecalloc.c b/source4/heimdal/lib/roken/ecalloc.c
new file mode 100644
index 0000000000..6c579e99f3
--- /dev/null
+++ b/source4/heimdal/lib/roken/ecalloc.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 1999 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+RCSID("$Id$");
+#endif
+
+#include <stdlib.h>
+#include <err.h>
+
+#include "roken.h"
+
+/*
+ * Like calloc but never fails.
+ */
+
+void * ROKEN_LIB_FUNCTION
+ecalloc (size_t number, size_t size)
+{
+ void *tmp = calloc (number, size);
+
+ if (tmp == NULL && number * size != 0)
+ errx (1, "calloc %lu failed", (unsigned long)number * size);
+ return tmp;
+}
diff --git a/source4/heimdal/lib/roken/emalloc.c b/source4/heimdal/lib/roken/emalloc.c
new file mode 100644
index 0000000000..d033e16a07
--- /dev/null
+++ b/source4/heimdal/lib/roken/emalloc.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 1999 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+RCSID("$Id$");
+#endif
+
+#include <stdlib.h>
+#include <err.h>
+
+#include "roken.h"
+
+/*
+ * Like malloc but never fails.
+ */
+
+void * ROKEN_LIB_FUNCTION
+emalloc (size_t sz)
+{
+ void *tmp = malloc (sz);
+
+ if (tmp == NULL && sz != 0)
+ errx (1, "malloc %lu failed", (unsigned long)sz);
+ return tmp;
+}
diff --git a/source4/heimdal/lib/roken/erealloc.c b/source4/heimdal/lib/roken/erealloc.c
new file mode 100644
index 0000000000..239d6a0eab
--- /dev/null
+++ b/source4/heimdal/lib/roken/erealloc.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 1999 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+RCSID("$Id$");
+#endif
+
+#include <stdlib.h>
+#include <err.h>
+
+#include "roken.h"
+
+/*
+ * Like realloc but never fails.
+ */
+
+void * ROKEN_LIB_FUNCTION
+erealloc (void *ptr, size_t sz)
+{
+ void *tmp = realloc (ptr, sz);
+
+ if (tmp == NULL && sz != 0)
+ errx (1, "realloc %lu failed", (unsigned long)sz);
+ return tmp;
+}
diff --git a/source4/heimdal/lib/roken/err.hin b/source4/heimdal/lib/roken/err.hin
new file mode 100644
index 0000000000..9fd1856e2b
--- /dev/null
+++ b/source4/heimdal/lib/roken/err.hin
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 1995 - 2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef __ERR_H__
+#define __ERR_H__
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#if !defined(__GNUC__) && !defined(__attribute__)
+#define __attribute__(x)
+#endif
+
+#ifndef ROKEN_LIB_FUNCTION
+#ifdef _WIN32
+#define ROKEN_LIB_FUNCTION _stdcall
+#else
+#define ROKEN_LIB_FUNCTION
+#endif
+#endif
+
+void ROKEN_LIB_FUNCTION
+verr(int eval, const char *fmt, va_list ap)
+ __attribute__ ((noreturn, format (printf, 2, 0)));
+
+void ROKEN_LIB_FUNCTION
+err(int eval, const char *fmt, ...)
+ __attribute__ ((noreturn, format (printf, 2, 3)));
+
+void ROKEN_LIB_FUNCTION
+verrx(int eval, const char *fmt, va_list ap)
+ __attribute__ ((noreturn, format (printf, 2, 0)));
+
+void ROKEN_LIB_FUNCTION
+errx(int eval, const char *fmt, ...)
+ __attribute__ ((noreturn, format (printf, 2, 3)));
+void ROKEN_LIB_FUNCTION
+vwarn(const char *fmt, va_list ap)
+ __attribute__ ((format (printf, 1, 0)));
+
+void ROKEN_LIB_FUNCTION
+warn(const char *fmt, ...)
+ __attribute__ ((format (printf, 1, 2)));
+
+void ROKEN_LIB_FUNCTION
+vwarnx(const char *fmt, va_list ap)
+ __attribute__ ((format (printf, 1, 0)));
+
+void ROKEN_LIB_FUNCTION
+warnx(const char *fmt, ...)
+ __attribute__ ((format (printf, 1, 2)));
+
+#endif /* __ERR_H__ */
diff --git a/source4/heimdal/lib/roken/estrdup.c b/source4/heimdal/lib/roken/estrdup.c
new file mode 100644
index 0000000000..fb353d0d85
--- /dev/null
+++ b/source4/heimdal/lib/roken/estrdup.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 1999 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+RCSID("$Id$");
+#endif
+
+#include <stdlib.h>
+#include <err.h>
+
+#include "roken.h"
+
+/*
+ * Like strdup but never fails.
+ */
+
+char * ROKEN_LIB_FUNCTION
+estrdup (const char *str)
+{
+ char *tmp = strdup (str);
+
+ if (tmp == NULL)
+ errx (1, "strdup failed");
+ return tmp;
+}
diff --git a/source4/heimdal/lib/roken/freeaddrinfo.c b/source4/heimdal/lib/roken/freeaddrinfo.c
new file mode 100644
index 0000000000..1721d20aaf
--- /dev/null
+++ b/source4/heimdal/lib/roken/freeaddrinfo.c
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 1999 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+RCSID("$Id$");
+#endif
+
+#include "roken.h"
+
+/*
+ * free the list of `struct addrinfo' starting at `ai'
+ */
+
+void ROKEN_LIB_FUNCTION
+freeaddrinfo(struct addrinfo *ai)
+{
+ struct addrinfo *tofree;
+
+ while(ai != NULL) {
+ free (ai->ai_canonname);
+ free (ai->ai_addr);
+ tofree = ai;
+ ai = ai->ai_next;
+ free (tofree);
+ }
+}
diff --git a/source4/heimdal/lib/roken/freehostent.c b/source4/heimdal/lib/roken/freehostent.c
new file mode 100644
index 0000000000..38c27d3380
--- /dev/null
+++ b/source4/heimdal/lib/roken/freehostent.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 1999 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+RCSID("$Id$");
+#endif
+
+#include "roken.h"
+
+/*
+ * free a malloced hostent
+ */
+
+void ROKEN_LIB_FUNCTION
+freehostent (struct hostent *h)
+{
+ char **p;
+
+ free (h->h_name);
+ if (h->h_aliases != NULL) {
+ for (p = h->h_aliases; *p != NULL; ++p)
+ free (*p);
+ free (h->h_aliases);
+ }
+ if (h->h_addr_list != NULL) {
+ for (p = h->h_addr_list; *p != NULL; ++p)
+ free (*p);
+ free (h->h_addr_list);
+ }
+ free (h);
+}
diff --git a/source4/heimdal/lib/roken/gai_strerror.c b/source4/heimdal/lib/roken/gai_strerror.c
new file mode 100644
index 0000000000..084900a58a
--- /dev/null
+++ b/source4/heimdal/lib/roken/gai_strerror.c
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 1999 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+RCSID("$Id$");
+#endif
+
+#include "roken.h"
+
+static struct gai_error {
+ int code;
+ const char *str;
+} errors[] = {
+{EAI_NOERROR, "no error"},
+#ifdef EAI_ADDRFAMILY
+{EAI_ADDRFAMILY, "address family for nodename not supported"},
+#endif
+{EAI_AGAIN, "temporary failure in name resolution"},
+{EAI_BADFLAGS, "invalid value for ai_flags"},
+{EAI_FAIL, "non-recoverable failure in name resolution"},
+{EAI_FAMILY, "ai_family not supported"},
+{EAI_MEMORY, "memory allocation failure"},
+#ifdef EAI_NODATA
+{EAI_NODATA, "no address associated with nodename"},
+#endif
+{EAI_NONAME, "nodename nor servname provided, or not known"},
+{EAI_SERVICE, "servname not supported for ai_socktype"},
+{EAI_SOCKTYPE, "ai_socktype not supported"},
+{EAI_SYSTEM, "system error returned in errno"},
+{0, NULL},
+};
+
+/*
+ *
+ */
+
+const char * ROKEN_LIB_FUNCTION
+gai_strerror(int ecode)
+{
+ struct gai_error *g;
+
+ for (g = errors; g->str != NULL; ++g)
+ if (g->code == ecode)
+ return g->str;
+ return "unknown error code in gai_strerror";
+}
diff --git a/source4/heimdal/lib/roken/get_window_size.c b/source4/heimdal/lib/roken/get_window_size.c
new file mode 100644
index 0000000000..9b200c1159
--- /dev/null
+++ b/source4/heimdal/lib/roken/get_window_size.c
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 1995, 1996, 1997, 1998 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+RCSID("$Id$");
+#endif
+
+#include <stdlib.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#if 0 /* Where were those needed? /confused */
+#ifdef HAVE_SYS_PROC_H
+#include <sys/proc.h>
+#endif
+
+#ifdef HAVE_SYS_TTY_H
+#include <sys/tty.h>
+#endif
+#endif
+
+#ifdef HAVE_TERMIOS_H
+#include <termios.h>
+#endif
+
+#include "roken.h"
+
+int ROKEN_LIB_FUNCTION
+get_window_size(int fd, struct winsize *wp)
+{
+ int ret = -1;
+
+ memset(wp, 0, sizeof(*wp));
+
+#if defined(TIOCGWINSZ)
+ ret = ioctl(fd, TIOCGWINSZ, wp);
+#elif defined(TIOCGSIZE)
+ {
+ struct ttysize ts;
+
+ ret = ioctl(fd, TIOCGSIZE, &ts);
+ if(ret == 0) {
+ wp->ws_row = ts.ts_lines;
+ wp->ws_col = ts.ts_cols;
+ }
+ }
+#elif defined(HAVE__SCRSIZE)
+ {
+ int dst[2];
+
+ _scrsize(dst);
+ wp->ws_row = dst[1];
+ wp->ws_col = dst[0];
+ ret = 0;
+ }
+#endif
+ if (ret != 0) {
+ char *s;
+ if((s = getenv("COLUMNS")))
+ wp->ws_col = atoi(s);
+ if((s = getenv("LINES")))
+ wp->ws_row = atoi(s);
+ if(wp->ws_col > 0 && wp->ws_row > 0)
+ ret = 0;
+ }
+ return ret;
+}
diff --git a/source4/heimdal/lib/roken/getaddrinfo.c b/source4/heimdal/lib/roken/getaddrinfo.c
new file mode 100644
index 0000000000..a8fc029c32
--- /dev/null
+++ b/source4/heimdal/lib/roken/getaddrinfo.c
@@ -0,0 +1,417 @@
+/*
+ * Copyright (c) 1999 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+RCSID("$Id$");
+#endif
+
+#include "roken.h"
+
+/*
+ * uses hints->ai_socktype and hints->ai_protocol
+ */
+
+static int
+get_port_protocol_socktype (const char *servname,
+ const struct addrinfo *hints,
+ int *port,
+ int *protocol,
+ int *socktype)
+{
+ struct servent *se;
+ const char *proto_str = NULL;
+
+ *socktype = 0;
+
+ if (hints != NULL && hints->ai_protocol != 0) {
+ struct protoent *protoent = getprotobynumber (hints->ai_protocol);
+
+ if (protoent == NULL)
+ return EAI_SOCKTYPE; /* XXX */
+
+ proto_str = protoent->p_name;
+ *protocol = protoent->p_proto;
+ }
+
+ if (hints != NULL)
+ *socktype = hints->ai_socktype;
+
+ if (*socktype == SOCK_STREAM) {
+ se = getservbyname (servname, proto_str ? proto_str : "tcp");
+ if (proto_str == NULL)
+ *protocol = IPPROTO_TCP;
+ } else if (*socktype == SOCK_DGRAM) {
+ se = getservbyname (servname, proto_str ? proto_str : "udp");
+ if (proto_str == NULL)
+ *protocol = IPPROTO_UDP;
+ } else if (*socktype == 0) {
+ if (proto_str != NULL) {
+ se = getservbyname (servname, proto_str);
+ } else {
+ se = getservbyname (servname, "tcp");
+ *protocol = IPPROTO_TCP;
+ *socktype = SOCK_STREAM;
+ if (se == NULL) {
+ se = getservbyname (servname, "udp");
+ *protocol = IPPROTO_UDP;
+ *socktype = SOCK_DGRAM;
+ }
+ }
+ } else
+ return EAI_SOCKTYPE;
+
+ if (se == NULL) {
+ char *endstr;
+
+ *port = htons(strtol (servname, &endstr, 10));
+ if (servname == endstr)
+ return EAI_NONAME;
+ } else {
+ *port = se->s_port;
+ }
+ return 0;
+}
+
+static int
+add_one (int port, int protocol, int socktype,
+ struct addrinfo ***ptr,
+ int (*func)(struct addrinfo *, void *data, int port),
+ void *data,
+ char *canonname)
+{
+ struct addrinfo *a;
+ int ret;
+
+ a = malloc (sizeof (*a));
+ if (a == NULL)
+ return EAI_MEMORY;
+ memset (a, 0, sizeof(*a));
+ a->ai_flags = 0;
+ a->ai_next = NULL;
+ a->ai_protocol = protocol;
+ a->ai_socktype = socktype;
+ a->ai_canonname = canonname;
+ ret = (*func)(a, data, port);
+ if (ret) {
+ free (a);
+ return ret;
+ }
+ **ptr = a;
+ *ptr = &a->ai_next;
+ return 0;
+}
+
+static int
+const_v4 (struct addrinfo *a, void *data, int port)
+{
+ struct sockaddr_in *sin4;
+ struct in_addr *addr = (struct in_addr *)data;
+
+ a->ai_family = PF_INET;
+ a->ai_addrlen = sizeof(*sin4);
+ a->ai_addr = malloc (sizeof(*sin4));
+ if (a->ai_addr == NULL)
+ return EAI_MEMORY;
+ sin4 = (struct sockaddr_in *)a->ai_addr;
+ memset (sin4, 0, sizeof(*sin4));
+ sin4->sin_family = AF_INET;
+ sin4->sin_port = port;
+ sin4->sin_addr = *addr;
+ return 0;
+}
+
+#ifdef HAVE_IPV6
+static int
+const_v6 (struct addrinfo *a, void *data, int port)
+{
+ struct sockaddr_in6 *sin6;
+ struct in6_addr *addr = (struct in6_addr *)data;
+
+ a->ai_family = PF_INET6;
+ a->ai_addrlen = sizeof(*sin6);
+ a->ai_addr = malloc (sizeof(*sin6));
+ if (a->ai_addr == NULL)
+ return EAI_MEMORY;
+ sin6 = (struct sockaddr_in6 *)a->ai_addr;
+ memset (sin6, 0, sizeof(*sin6));
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = port;
+ sin6->sin6_addr = *addr;
+ return 0;
+}
+#endif
+
+/* this is mostly a hack for some versions of AIX that has a prototype
+ for in6addr_loopback but no actual symbol in libc */
+#if defined(HAVE_IPV6) && !defined(HAVE_IN6ADDR_LOOPBACK) && defined(IN6ADDR_LOOPBACK_INIT)
+#define in6addr_loopback _roken_in6addr_loopback
+struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT;
+#endif
+
+static int
+get_null (const struct addrinfo *hints,
+ int port, int protocol, int socktype,
+ struct addrinfo **res)
+{
+ struct in_addr v4_addr;
+#ifdef HAVE_IPV6
+ struct in6_addr v6_addr;
+#endif
+ struct addrinfo *first = NULL;
+ struct addrinfo **current = &first;
+ int family = PF_UNSPEC;
+ int ret;
+
+ if (hints != NULL)
+ family = hints->ai_family;
+
+ if (hints && hints->ai_flags & AI_PASSIVE) {
+ v4_addr.s_addr = INADDR_ANY;
+#ifdef HAVE_IPV6
+ v6_addr = in6addr_any;
+#endif
+ } else {
+ v4_addr.s_addr = htonl(INADDR_LOOPBACK);
+#ifdef HAVE_IPV6
+ v6_addr = in6addr_loopback;
+#endif
+ }
+
+#ifdef HAVE_IPV6
+ if (family == PF_INET6 || family == PF_UNSPEC) {
+ ret = add_one (port, protocol, socktype,
+ &current, const_v6, &v6_addr, NULL);
+ }
+#endif
+ if (family == PF_INET || family == PF_UNSPEC) {
+ ret = add_one (port, protocol, socktype,
+ &current, const_v4, &v4_addr, NULL);
+ }
+ *res = first;
+ return 0;
+}
+
+static int
+add_hostent (int port, int protocol, int socktype,
+ struct addrinfo ***current,
+ int (*func)(struct addrinfo *, void *data, int port),
+ struct hostent *he, int *flags)
+{
+ int ret;
+ char *canonname = NULL;
+ char **h;
+
+ if (*flags & AI_CANONNAME) {
+ struct hostent *he2 = NULL;
+ const char *tmp_canon;
+
+ tmp_canon = hostent_find_fqdn (he);
+ if (strchr (tmp_canon, '.') == NULL) {
+ int error;
+
+ he2 = getipnodebyaddr (he->h_addr_list[0], he->h_length,
+ he->h_addrtype, &error);
+ if (he2 != NULL) {
+ const char *tmp = hostent_find_fqdn (he2);
+
+ if (strchr (tmp, '.') != NULL)
+ tmp_canon = tmp;
+ }
+ }
+
+ canonname = strdup (tmp_canon);
+ if (he2 != NULL)
+ freehostent (he2);
+ if (canonname == NULL)
+ return EAI_MEMORY;
+ }
+
+ for (h = he->h_addr_list; *h != NULL; ++h) {
+ ret = add_one (port, protocol, socktype,
+ current, func, *h, canonname);
+ if (ret)
+ return ret;
+ if (*flags & AI_CANONNAME) {
+ *flags &= ~AI_CANONNAME;
+ canonname = NULL;
+ }
+ }
+ return 0;
+}
+
+static int
+get_number (const char *nodename,
+ const struct addrinfo *hints,
+ int port, int protocol, int socktype,
+ struct addrinfo **res)
+{
+ struct addrinfo *first = NULL;
+ struct addrinfo **current = &first;
+ int family = PF_UNSPEC;
+ int ret;
+
+ if (hints != NULL) {
+ family = hints->ai_family;
+ }
+
+#ifdef HAVE_IPV6
+ if (family == PF_INET6 || family == PF_UNSPEC) {
+ struct in6_addr v6_addr;
+
+ if (inet_pton (PF_INET6, nodename, &v6_addr) == 1) {
+ ret = add_one (port, protocol, socktype,
+ &current, const_v6, &v6_addr, NULL);
+ *res = first;
+ return ret;
+ }
+ }
+#endif
+ if (family == PF_INET || family == PF_UNSPEC) {
+ struct in_addr v4_addr;
+
+ if (inet_pton (PF_INET, nodename, &v4_addr) == 1) {
+ ret = add_one (port, protocol, socktype,
+ &current, const_v4, &v4_addr, NULL);
+ *res = first;
+ return ret;
+ }
+ }
+ return EAI_NONAME;
+}
+
+static int
+get_nodes (const char *nodename,
+ const struct addrinfo *hints,
+ int port, int protocol, int socktype,
+ struct addrinfo **res)
+{
+ struct addrinfo *first = NULL;
+ struct addrinfo **current = &first;
+ int family = PF_UNSPEC;
+ int flags = 0;
+ int ret = EAI_NONAME;
+ int error;
+
+ if (hints != NULL) {
+ family = hints->ai_family;
+ flags = hints->ai_flags;
+ }
+
+#ifdef HAVE_IPV6
+ if (family == PF_INET6 || family == PF_UNSPEC) {
+ struct hostent *he;
+
+ he = getipnodebyname (nodename, PF_INET6, 0, &error);
+
+ if (he != NULL) {
+ ret = add_hostent (port, protocol, socktype,
+ &current, const_v6, he, &flags);
+ freehostent (he);
+ }
+ }
+#endif
+ if (family == PF_INET || family == PF_UNSPEC) {
+ struct hostent *he;
+
+ he = getipnodebyname (nodename, PF_INET, 0, &error);
+
+ if (he != NULL) {
+ ret = add_hostent (port, protocol, socktype,
+ &current, const_v4, he, &flags);
+ freehostent (he);
+ }
+ }
+ *res = first;
+ return ret;
+}
+
+/*
+ * hints:
+ *
+ * struct addrinfo {
+ * int ai_flags;
+ * int ai_family;
+ * int ai_socktype;
+ * int ai_protocol;
+ * ...
+ * };
+ */
+
+int ROKEN_LIB_FUNCTION
+getaddrinfo(const char *nodename,
+ const char *servname,
+ const struct addrinfo *hints,
+ struct addrinfo **res)
+{
+ int ret;
+ int port = 0;
+ int protocol = 0;
+ int socktype = 0;
+
+ *res = NULL;
+
+ if (servname == NULL && nodename == NULL)
+ return EAI_NONAME;
+
+ if (hints != NULL
+ && hints->ai_family != PF_UNSPEC
+ && hints->ai_family != PF_INET
+#ifdef HAVE_IPV6
+ && hints->ai_family != PF_INET6
+#endif
+ )
+ return EAI_FAMILY;
+
+ if (servname != NULL) {
+ ret = get_port_protocol_socktype (servname, hints,
+ &port, &protocol, &socktype);
+ if (ret)
+ return ret;
+ }
+ if (nodename != NULL) {
+ ret = get_number (nodename, hints, port, protocol, socktype, res);
+ if (ret) {
+ if(hints && hints->ai_flags & AI_NUMERICHOST)
+ ret = EAI_NONAME;
+ else
+ ret = get_nodes (nodename, hints, port, protocol, socktype,
+ res);
+ }
+ } else {
+ ret = get_null (hints, port, protocol, socktype, res);
+ }
+ if (ret)
+ freeaddrinfo (*res);
+ return ret;
+}
diff --git a/source4/heimdal/lib/roken/getarg.c b/source4/heimdal/lib/roken/getarg.c
new file mode 100644
index 0000000000..3168ccc53d
--- /dev/null
+++ b/source4/heimdal/lib/roken/getarg.c
@@ -0,0 +1,625 @@
+/*
+ * Copyright (c) 1997 - 2002 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+RCSID("$Id$");
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "roken.h"
+#include "getarg.h"
+
+#define ISFLAG(X) ((X).type == arg_flag || (X).type == arg_negative_flag)
+
+static size_t
+print_arg (char *string,
+ size_t len,
+ int mdoc,
+ int longp,
+ struct getargs *arg,
+ char *(i18n)(const char *))
+{
+ const char *s;
+
+ *string = '\0';
+
+ if (ISFLAG(*arg) || (!longp && arg->type == arg_counter))
+ return 0;
+
+ if(mdoc){
+ if(longp)
+ strlcat(string, "= Ns", len);
+ strlcat(string, " Ar ", len);
+ } else {
+ if (longp)
+ strlcat (string, "=", len);
+ else
+ strlcat (string, " ", len);
+ }
+
+ if (arg->arg_help)
+ s = (*i18n)(arg->arg_help);
+ else if (arg->type == arg_integer || arg->type == arg_counter)
+ s = "integer";
+ else if (arg->type == arg_string)
+ s = "string";
+ else if (arg->type == arg_strings)
+ s = "strings";
+ else if (arg->type == arg_double)
+ s = "float";
+ else
+ s = "<undefined>";
+
+ strlcat(string, s, len);
+ return 1 + strlen(s);
+}
+
+static void
+mandoc_template(struct getargs *args,
+ size_t num_args,
+ const char *progname,
+ const char *extra_string,
+ char *(i18n)(const char *))
+{
+ int i;
+ char timestr[64], cmd[64];
+ char buf[128];
+ const char *p;
+ time_t t;
+
+ printf(".\\\" Things to fix:\n");
+ printf(".\\\" * correct section, and operating system\n");
+ printf(".\\\" * remove Op from mandatory flags\n");
+ printf(".\\\" * use better macros for arguments (like .Pa for files)\n");
+ printf(".\\\"\n");
+ t = time(NULL);
+ strftime(timestr, sizeof(timestr), "%B %e, %Y", localtime(&t));
+ printf(".Dd %s\n", timestr);
+ p = strrchr(progname, '/');
+ if(p) p++; else p = progname;
+ strlcpy(cmd, p, sizeof(cmd));
+ strupr(cmd);
+
+ printf(".Dt %s SECTION\n", cmd);
+ printf(".Os OPERATING_SYSTEM\n");
+ printf(".Sh NAME\n");
+ printf(".Nm %s\n", p);
+ printf(".Nd\n");
+ printf("in search of a description\n");
+ printf(".Sh SYNOPSIS\n");
+ printf(".Nm\n");
+ for(i = 0; i < num_args; i++){
+ /* we seem to hit a limit on number of arguments if doing
+ short and long flags with arguments -- split on two lines */
+ if(ISFLAG(args[i]) ||
+ args[i].short_name == 0 || args[i].long_name == NULL) {
+ printf(".Op ");
+
+ if(args[i].short_name) {
+ print_arg(buf, sizeof(buf), 1, 0, args + i, i18n);
+ printf("Fl %c%s", args[i].short_name, buf);
+ if(args[i].long_name)
+ printf(" | ");
+ }
+ if(args[i].long_name) {
+ print_arg(buf, sizeof(buf), 1, 1, args + i, i18n);
+ printf("Fl -%s%s%s",
+ args[i].type == arg_negative_flag ? "no-" : "",
+ args[i].long_name, buf);
+ }
+ printf("\n");
+ } else {
+ print_arg(buf, sizeof(buf), 1, 0, args + i, i18n);
+ printf(".Oo Fl %c%s \\*(Ba Xo\n", args[i].short_name, buf);
+ print_arg(buf, sizeof(buf), 1, 1, args + i, i18n);
+ printf(".Fl -%s%s\n.Xc\n.Oc\n", args[i].long_name, buf);
+ }
+ /*
+ if(args[i].type == arg_strings)
+ fprintf (stderr, "...");
+ */
+ }
+ if (extra_string && *extra_string)
+ printf (".Ar %s\n", extra_string);
+ printf(".Sh DESCRIPTION\n");
+ printf("Supported options:\n");
+ printf(".Bl -tag -width Ds\n");
+ for(i = 0; i < num_args; i++){
+ printf(".It Xo\n");
+ if(args[i].short_name){
+ printf(".Fl %c", args[i].short_name);
+ print_arg(buf, sizeof(buf), 1, 0, args + i, i18n);
+ printf("%s", buf);
+ if(args[i].long_name)
+ printf(" ,");
+ printf("\n");
+ }
+ if(args[i].long_name){
+ printf(".Fl -%s%s",
+ args[i].type == arg_negative_flag ? "no-" : "",
+ args[i].long_name);
+ print_arg(buf, sizeof(buf), 1, 1, args + i, i18n);
+ printf("%s\n", buf);
+ }
+ printf(".Xc\n");
+ if(args[i].help)
+ printf("%s\n", args[i].help);
+ /*
+ if(args[i].type == arg_strings)
+ fprintf (stderr, "...");
+ */
+ }
+ printf(".El\n");
+ printf(".\\\".Sh ENVIRONMENT\n");
+ printf(".\\\".Sh FILES\n");
+ printf(".\\\".Sh EXAMPLES\n");
+ printf(".\\\".Sh DIAGNOSTICS\n");
+ printf(".\\\".Sh SEE ALSO\n");
+ printf(".\\\".Sh STANDARDS\n");
+ printf(".\\\".Sh HISTORY\n");
+ printf(".\\\".Sh AUTHORS\n");
+ printf(".\\\".Sh BUGS\n");
+}
+
+static int
+check_column(FILE *f, int col, int len, int columns)
+{
+ if(col + len > columns) {
+ fprintf(f, "\n");
+ col = fprintf(f, " ");
+ }
+ return col;
+}
+
+static char *
+builtin_i18n(const char *str)
+{
+ return rk_UNCONST(str);
+}
+
+void ROKEN_LIB_FUNCTION
+arg_printusage (struct getargs *args,
+ size_t num_args,
+ const char *progname,
+ const char *extra_string)
+{
+ arg_printusage_i18n(args, num_args, "Usage",
+ progname, extra_string, builtin_i18n);
+}
+
+void ROKEN_LIB_FUNCTION
+arg_printusage_i18n (struct getargs *args,
+ size_t num_args,
+ const char *usage,
+ const char *progname,
+ const char *extra_string,
+ char *(i18n)(const char *))
+{
+ int i;
+ size_t max_len = 0;
+ char buf[128];
+ int col = 0, columns;
+ struct winsize ws;
+
+ if (progname == NULL)
+ progname = getprogname();
+
+ if (i18n == NULL)
+ i18n = builtin_i18n;
+
+ if(getenv("GETARGMANDOC")){
+ mandoc_template(args, num_args, progname, extra_string, i18n);
+ return;
+ }
+ if(get_window_size(2, &ws) == 0)
+ columns = ws.ws_col;
+ else
+ columns = 80;
+ col = 0;
+ col += fprintf (stderr, "%s: %s", usage, progname);
+ buf[0] = '\0';
+ for (i = 0; i < num_args; ++i) {
+ if(args[i].short_name && ISFLAG(args[i])) {
+ char s[2];
+ if(buf[0] == '\0')
+ strlcpy(buf, "[-", sizeof(buf));
+ s[0] = args[i].short_name;
+ s[1] = '\0';
+ strlcat(buf, s, sizeof(buf));
+ }
+ }
+ if(buf[0] != '\0') {
+ strlcat(buf, "]", sizeof(buf));
+ col = check_column(stderr, col, strlen(buf) + 1, columns);
+ col += fprintf(stderr, " %s", buf);
+ }
+
+ for (i = 0; i < num_args; ++i) {
+ size_t len = 0;
+
+ if (args[i].long_name) {
+ buf[0] = '\0';
+ strlcat(buf, "[--", sizeof(buf));
+ len += 2;
+ if(args[i].type == arg_negative_flag) {
+ strlcat(buf, "no-", sizeof(buf));
+ len += 3;
+ }
+ strlcat(buf, args[i].long_name, sizeof(buf));
+ len += strlen(args[i].long_name);
+ len += print_arg(buf + strlen(buf), sizeof(buf) - strlen(buf),
+ 0, 1, &args[i], i18n);
+ strlcat(buf, "]", sizeof(buf));
+ if(args[i].type == arg_strings)
+ strlcat(buf, "...", sizeof(buf));
+ col = check_column(stderr, col, strlen(buf) + 1, columns);
+ col += fprintf(stderr, " %s", buf);
+ }
+ if (args[i].short_name && !ISFLAG(args[i])) {
+ snprintf(buf, sizeof(buf), "[-%c", args[i].short_name);
+ len += 2;
+ len += print_arg(buf + strlen(buf), sizeof(buf) - strlen(buf),
+ 0, 0, &args[i], i18n);
+ strlcat(buf, "]", sizeof(buf));
+ if(args[i].type == arg_strings)
+ strlcat(buf, "...", sizeof(buf));
+ col = check_column(stderr, col, strlen(buf) + 1, columns);
+ col += fprintf(stderr, " %s", buf);
+ }
+ if (args[i].long_name && args[i].short_name)
+ len += 2; /* ", " */
+ max_len = max(max_len, len);
+ }
+ if (extra_string) {
+ check_column(stderr, col, strlen(extra_string) + 1, columns);
+ fprintf (stderr, " %s\n", extra_string);
+ } else
+ fprintf (stderr, "\n");
+ for (i = 0; i < num_args; ++i) {
+ if (args[i].help) {
+ size_t count = 0;
+
+ if (args[i].short_name) {
+ count += fprintf (stderr, "-%c", args[i].short_name);
+ print_arg (buf, sizeof(buf), 0, 0, &args[i], i18n);
+ count += fprintf(stderr, "%s", buf);
+ }
+ if (args[i].short_name && args[i].long_name)
+ count += fprintf (stderr, ", ");
+ if (args[i].long_name) {
+ count += fprintf (stderr, "--");
+ if (args[i].type == arg_negative_flag)
+ count += fprintf (stderr, "no-");
+ count += fprintf (stderr, "%s", args[i].long_name);
+ print_arg (buf, sizeof(buf), 0, 1, &args[i], i18n);
+ count += fprintf(stderr, "%s", buf);
+ }
+ while(count++ <= max_len)
+ putc (' ', stderr);
+ fprintf (stderr, "%s\n", (*i18n)(args[i].help));
+ }
+ }
+}
+
+static int
+add_string(getarg_strings *s, char *value)
+{
+ char **strings;
+
+ strings = realloc(s->strings, (s->num_strings + 1) * sizeof(*s->strings));
+ if (strings == NULL) {
+ free(s->strings);
+ s->strings = NULL;
+ s->num_strings = 0;
+ return ENOMEM;
+ }
+ s->strings = strings;
+ s->strings[s->num_strings] = value;
+ s->num_strings++;
+ return 0;
+}
+
+static int
+arg_match_long(struct getargs *args, size_t num_args,
+ char *argv, int argc, char **rargv, int *goptind)
+{
+ int i;
+ char *goptarg = NULL;
+ int negate = 0;
+ int partial_match = 0;
+ struct getargs *partial = NULL;
+ struct getargs *current = NULL;
+ int argv_len;
+ char *p;
+ int p_len;
+
+ argv_len = strlen(argv);
+ p = strchr (argv, '=');
+ if (p != NULL)
+ argv_len = p - argv;
+
+ for (i = 0; i < num_args; ++i) {
+ if(args[i].long_name) {
+ int len = strlen(args[i].long_name);
+ p = argv;
+ p_len = argv_len;
+ negate = 0;
+
+ for (;;) {
+ if (strncmp (args[i].long_name, p, p_len) == 0) {
+ if(p_len == len)
+ current = &args[i];
+ else {
+ ++partial_match;
+ partial = &args[i];
+ }
+ goptarg = p + p_len;
+ } else if (ISFLAG(args[i]) && strncmp (p, "no-", 3) == 0) {
+ negate = !negate;
+ p += 3;
+ p_len -= 3;
+ continue;
+ }
+ break;
+ }
+ if (current)
+ break;
+ }
+ }
+ if (current == NULL) {
+ if (partial_match == 1)
+ current = partial;
+ else
+ return ARG_ERR_NO_MATCH;
+ }
+
+ if(*goptarg == '\0'
+ && !ISFLAG(*current)
+ && current->type != arg_collect
+ && current->type != arg_counter)
+ return ARG_ERR_NO_MATCH;
+ switch(current->type){
+ case arg_integer:
+ {
+ int tmp;
+ if(sscanf(goptarg + 1, "%d", &tmp) != 1)
+ return ARG_ERR_BAD_ARG;
+ *(int*)current->value = tmp;
+ return 0;
+ }
+ case arg_string:
+ {
+ *(char**)current->value = goptarg + 1;
+ return 0;
+ }
+ case arg_strings:
+ {
+ return add_string((getarg_strings*)current->value, goptarg + 1);
+ }
+ case arg_flag:
+ case arg_negative_flag:
+ {
+ int *flag = current->value;
+ if(*goptarg == '\0' ||
+ strcmp(goptarg + 1, "yes") == 0 ||
+ strcmp(goptarg + 1, "true") == 0){
+ *flag = !negate;
+ return 0;
+ } else if (*goptarg && strcmp(goptarg + 1, "maybe") == 0) {
+#ifdef HAVE_RANDOM
+ *flag = random() & 1;
+#else
+ *flag = rand() & 1;
+#endif
+ } else {
+ *flag = negate;
+ return 0;
+ }
+ return ARG_ERR_BAD_ARG;
+ }
+ case arg_counter :
+ {
+ int val;
+
+ if (*goptarg == '\0')
+ val = 1;
+ else if(sscanf(goptarg + 1, "%d", &val) != 1)
+ return ARG_ERR_BAD_ARG;
+ *(int *)current->value += val;
+ return 0;
+ }
+ case arg_double:
+ {
+ double tmp;
+ if(sscanf(goptarg + 1, "%lf", &tmp) != 1)
+ return ARG_ERR_BAD_ARG;
+ *(double*)current->value = tmp;
+ return 0;
+ }
+ case arg_collect:{
+ struct getarg_collect_info *c = current->value;
+ int o = argv - rargv[*goptind];
+ return (*c->func)(FALSE, argc, rargv, goptind, &o, c->data);
+ }
+
+ default:
+ abort ();
+ }
+
+ /* not reached */
+ return ARG_ERR_NO_MATCH;
+}
+
+static int
+arg_match_short (struct getargs *args, size_t num_args,
+ char *argv, int argc, char **rargv, int *goptind)
+{
+ int j, k;
+
+ for(j = 1; j > 0 && j < strlen(rargv[*goptind]); j++) {
+ for(k = 0; k < num_args; k++) {
+ char *goptarg;
+
+ if(args[k].short_name == 0)
+ continue;
+ if(argv[j] == args[k].short_name) {
+ if(args[k].type == arg_flag) {
+ *(int*)args[k].value = 1;
+ break;
+ }
+ if(args[k].type == arg_negative_flag) {
+ *(int*)args[k].value = 0;
+ break;
+ }
+ if(args[k].type == arg_counter) {
+ ++*(int *)args[k].value;
+ break;
+ }
+ if(args[k].type == arg_collect) {
+ struct getarg_collect_info *c = args[k].value;
+
+ if((*c->func)(TRUE, argc, rargv, goptind, &j, c->data))
+ return ARG_ERR_BAD_ARG;
+ break;
+ }
+
+ if(argv[j + 1])
+ goptarg = &argv[j + 1];
+ else {
+ ++*goptind;
+ goptarg = rargv[*goptind];
+ }
+ if(goptarg == NULL) {
+ --*goptind;
+ return ARG_ERR_NO_ARG;
+ }
+ if(args[k].type == arg_integer) {
+ int tmp;
+ if(sscanf(goptarg, "%d", &tmp) != 1)
+ return ARG_ERR_BAD_ARG;
+ *(int*)args[k].value = tmp;
+ return 0;
+ } else if(args[k].type == arg_string) {
+ *(char**)args[k].value = goptarg;
+ return 0;
+ } else if(args[k].type == arg_strings) {
+ return add_string((getarg_strings*)args[k].value, goptarg);
+ } else if(args[k].type == arg_double) {
+ double tmp;
+ if(sscanf(goptarg, "%lf", &tmp) != 1)
+ return ARG_ERR_BAD_ARG;
+ *(double*)args[k].value = tmp;
+ return 0;
+ }
+ return ARG_ERR_BAD_ARG;
+ }
+ }
+ if (k == num_args)
+ return ARG_ERR_NO_MATCH;
+ }
+ return 0;
+}
+
+int ROKEN_LIB_FUNCTION
+getarg(struct getargs *args, size_t num_args,
+ int argc, char **argv, int *goptind)
+{
+ int i;
+ int ret = 0;
+
+#if defined(HAVE_SRANDOMDEV)
+ srandomdev();
+#elif defined(HAVE_RANDOM)
+ srandom(time(NULL));
+#else
+ srand (time(NULL));
+#endif
+ (*goptind)++;
+ for(i = *goptind; i < argc; i++) {
+ if(argv[i][0] != '-')
+ break;
+ if(argv[i][1] == '-'){
+ if(argv[i][2] == 0){
+ i++;
+ break;
+ }
+ ret = arg_match_long (args, num_args, argv[i] + 2,
+ argc, argv, &i);
+ } else {
+ ret = arg_match_short (args, num_args, argv[i],
+ argc, argv, &i);
+ }
+ if(ret)
+ break;
+ }
+ *goptind = i;
+ return ret;
+}
+
+void ROKEN_LIB_FUNCTION
+free_getarg_strings (getarg_strings *s)
+{
+ free (s->strings);
+}
+
+#if TEST
+int foo_flag = 2;
+int flag1 = 0;
+int flag2 = 0;
+int bar_int;
+char *baz_string;
+
+struct getargs args[] = {
+ { NULL, '1', arg_flag, &flag1, "one", NULL },
+ { NULL, '2', arg_flag, &flag2, "two", NULL },
+ { "foo", 'f', arg_negative_flag, &foo_flag, "foo", NULL },
+ { "bar", 'b', arg_integer, &bar_int, "bar", "seconds"},
+ { "baz", 'x', arg_string, &baz_string, "baz", "name" },
+};
+
+int main(int argc, char **argv)
+{
+ int goptind = 0;
+ while(getarg(args, 5, argc, argv, &goptind))
+ printf("Bad arg: %s\n", argv[goptind]);
+ printf("flag1 = %d\n", flag1);
+ printf("flag2 = %d\n", flag2);
+ printf("foo_flag = %d\n", foo_flag);
+ printf("bar_int = %d\n", bar_int);
+ printf("baz_flag = %s\n", baz_string);
+ arg_printusage (args, 5, argv[0], "nothing here");
+}
+#endif
diff --git a/source4/heimdal/lib/roken/getarg.h b/source4/heimdal/lib/roken/getarg.h
new file mode 100644
index 0000000000..64b65aa766
--- /dev/null
+++ b/source4/heimdal/lib/roken/getarg.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 1997 - 2002 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef __GETARG_H__
+#define __GETARG_H__
+
+#include <stddef.h>
+
+#ifndef ROKEN_LIB_FUNCTION
+#ifdef _WIN32
+#define ROKEN_LIB_FUNCTION _stdcall
+#else
+#define ROKEN_LIB_FUNCTION
+#endif
+#endif
+
+struct getargs{
+ const char *long_name;
+ char short_name;
+ enum { arg_integer,
+ arg_string,
+ arg_flag,
+ arg_negative_flag,
+ arg_strings,
+ arg_double,
+ arg_collect,
+ arg_counter
+ } type;
+ void *value;
+ const char *help;
+ const char *arg_help;
+};
+
+enum {
+ ARG_ERR_NO_MATCH = 1,
+ ARG_ERR_BAD_ARG,
+ ARG_ERR_NO_ARG
+};
+
+typedef struct getarg_strings {
+ int num_strings;
+ char **strings;
+} getarg_strings;
+
+typedef int (*getarg_collect_func)(int short_opt,
+ int argc,
+ char **argv,
+ int *goptind,
+ int *goptarg,
+ void *data);
+
+typedef struct getarg_collect_info {
+ getarg_collect_func func;
+ void *data;
+} getarg_collect_info;
+
+int ROKEN_LIB_FUNCTION
+getarg(struct getargs *args, size_t num_args,
+ int argc, char **argv, int *goptind);
+
+void ROKEN_LIB_FUNCTION
+arg_printusage (struct getargs *args,
+ size_t num_args,
+ const char *progname,
+ const char *extra_string);
+
+void ROKEN_LIB_FUNCTION
+arg_printusage_i18n (struct getargs *args,
+ size_t num_args,
+ const char *usage,
+ const char *progname,
+ const char *extra_string,
+ char *(i18n)(const char *));
+
+void ROKEN_LIB_FUNCTION
+free_getarg_strings (getarg_strings *);
+
+#endif /* __GETARG_H__ */
diff --git a/source4/heimdal/lib/roken/getipnodebyaddr.c b/source4/heimdal/lib/roken/getipnodebyaddr.c
new file mode 100644
index 0000000000..e3c2b50e32
--- /dev/null
+++ b/source4/heimdal/lib/roken/getipnodebyaddr.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 1999 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+RCSID("$Id$");
+#endif
+
+#include "roken.h"
+
+/*
+ * lookup `src, len' (address family `af') in DNS and return a pointer
+ * to a malloced struct hostent or NULL.
+ */
+
+struct hostent * ROKEN_LIB_FUNCTION
+getipnodebyaddr (const void *src, size_t len, int af, int *error_num)
+{
+ struct hostent *tmp;
+
+ tmp = gethostbyaddr (src, len, af);
+ if (tmp == NULL) {
+ switch (h_errno) {
+ case HOST_NOT_FOUND :
+ case TRY_AGAIN :
+ case NO_RECOVERY :
+ *error_num = h_errno;
+ break;
+ case NO_DATA :
+ *error_num = NO_ADDRESS;
+ break;
+ default :
+ *error_num = NO_RECOVERY;
+ break;
+ }
+ return NULL;
+ }
+ tmp = copyhostent (tmp);
+ if (tmp == NULL) {
+ *error_num = TRY_AGAIN;
+ return NULL;
+ }
+ return tmp;
+}
diff --git a/source4/heimdal/lib/roken/getipnodebyname.c b/source4/heimdal/lib/roken/getipnodebyname.c
new file mode 100644
index 0000000000..ec578937b7
--- /dev/null
+++ b/source4/heimdal/lib/roken/getipnodebyname.c
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 1999 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+RCSID("$Id$");
+#endif
+
+#include "roken.h"
+
+#ifndef HAVE_H_ERRNO
+static int h_errno = NO_RECOVERY;
+#endif
+
+/*
+ * lookup `name' (address family `af') in DNS and return a pointer
+ * to a malloced struct hostent or NULL.
+ */
+
+struct hostent * ROKEN_LIB_FUNCTION
+getipnodebyname (const char *name, int af, int flags, int *error_num)
+{
+ struct hostent *tmp;
+
+#ifdef HAVE_GETHOSTBYNAME2
+ tmp = gethostbyname2 (name, af);
+#else
+ if (af != AF_INET) {
+ *error_num = NO_ADDRESS;
+ return NULL;
+ }
+ tmp = gethostbyname (name);
+#endif
+ if (tmp == NULL) {
+ switch (h_errno) {
+ case HOST_NOT_FOUND :
+ case TRY_AGAIN :
+ case NO_RECOVERY :
+ *error_num = h_errno;
+ break;
+ case NO_DATA :
+ *error_num = NO_ADDRESS;
+ break;
+ default :
+ *error_num = NO_RECOVERY;
+ break;
+ }
+ return NULL;
+ }
+ tmp = copyhostent (tmp);
+ if (tmp == NULL) {
+ *error_num = TRY_AGAIN;
+ return NULL;
+ }
+ return tmp;
+}
diff --git a/source4/heimdal/lib/roken/getnameinfo.c b/source4/heimdal/lib/roken/getnameinfo.c
new file mode 100644
index 0000000000..865cc09dc8
--- /dev/null
+++ b/source4/heimdal/lib/roken/getnameinfo.c
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 1999 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+RCSID("$Id$");
+#endif
+
+#include "roken.h"
+
+static int
+doit (int af,
+ const void *addr,
+ size_t addrlen,
+ int port,
+ char *host, size_t hostlen,
+ char *serv, size_t servlen,
+ int flags)
+{
+ if (host != NULL) {
+ if (flags & NI_NUMERICHOST) {
+ if (inet_ntop (af, addr, host, hostlen) == NULL)
+ return EAI_SYSTEM;
+ } else {
+ struct hostent *he = gethostbyaddr (addr,
+ addrlen,
+ af);
+ if (he != NULL) {
+ strlcpy (host, hostent_find_fqdn(he), hostlen);
+ if (flags & NI_NOFQDN) {
+ char *dot = strchr (host, '.');
+ if (dot != NULL)
+ *dot = '\0';
+ }
+ } else if (flags & NI_NAMEREQD) {
+ return EAI_NONAME;
+ } else if (inet_ntop (af, addr, host, hostlen) == NULL)
+ return EAI_SYSTEM;
+ }
+ }
+
+ if (serv != NULL) {
+ if (flags & NI_NUMERICSERV) {
+ snprintf (serv, servlen, "%u", ntohs(port));
+ } else {
+ const char *proto = "tcp";
+ struct servent *se;
+
+ if (flags & NI_DGRAM)
+ proto = "udp";
+
+ se = getservbyport (port, proto);
+ if (se == NULL) {
+ snprintf (serv, servlen, "%u", ntohs(port));
+ } else {
+ strlcpy (serv, se->s_name, servlen);
+ }
+ }
+ }
+ return 0;
+}
+
+/*
+ *
+ */
+
+int ROKEN_LIB_FUNCTION
+getnameinfo(const struct sockaddr *sa, socklen_t salen,
+ char *host, size_t hostlen,
+ char *serv, size_t servlen,
+ int flags)
+{
+ switch (sa->sa_family) {
+#ifdef HAVE_IPV6
+ case AF_INET6 : {
+ const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sa;
+
+ return doit (AF_INET6, &sin6->sin6_addr, sizeof(sin6->sin6_addr),
+ sin6->sin6_port,
+ host, hostlen,
+ serv, servlen,
+ flags);
+ }
+#endif
+ case AF_INET : {
+ const struct sockaddr_in *sin4 = (const struct sockaddr_in *)sa;
+
+ return doit (AF_INET, &sin4->sin_addr, sizeof(sin4->sin_addr),
+ sin4->sin_port,
+ host, hostlen,
+ serv, servlen,
+ flags);
+ }
+ default :
+ return EAI_FAMILY;
+ }
+}
diff --git a/source4/heimdal/lib/roken/getprogname.c b/source4/heimdal/lib/roken/getprogname.c
new file mode 100644
index 0000000000..b01a3d7e45
--- /dev/null
+++ b/source4/heimdal/lib/roken/getprogname.c
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 1995-2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+RCSID("$Id$");
+#endif
+
+#include "roken.h"
+
+#ifndef HAVE___PROGNAME
+const char *__progname;
+#endif
+
+#ifndef HAVE_GETPROGNAME
+const char * ROKEN_LIB_FUNCTION
+getprogname(void)
+{
+ return __progname;
+}
+#endif /* HAVE_GETPROGNAME */
diff --git a/source4/heimdal/lib/roken/h_errno.c b/source4/heimdal/lib/roken/h_errno.c
new file mode 100644
index 0000000000..0cee02d472
--- /dev/null
+++ b/source4/heimdal/lib/roken/h_errno.c
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+RCSID("$Id$");
+#endif
+
+#ifndef HAVE_H_ERRNO
+int h_errno = -17; /* Some magic number */
+#endif
diff --git a/source4/heimdal/lib/roken/hex.c b/source4/heimdal/lib/roken/hex.c
new file mode 100644
index 0000000000..2167172f9f
--- /dev/null
+++ b/source4/heimdal/lib/roken/hex.c
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2004-2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+RCSID("$Id$");
+#endif
+#include "roken.h"
+#include <ctype.h>
+#include "hex.h"
+
+const static char hexchar[] = "0123456789ABCDEF";
+
+static int
+pos(char c)
+{
+ const char *p;
+ c = toupper((unsigned char)c);
+ for (p = hexchar; *p; p++)
+ if (*p == c)
+ return p - hexchar;
+ return -1;
+}
+
+ssize_t ROKEN_LIB_FUNCTION
+hex_encode(const void *data, size_t size, char **str)
+{
+ const unsigned char *q = data;
+ size_t i;
+ char *p;
+
+ /* check for overflow */
+ if (size * 2 < size)
+ return -1;
+
+ p = malloc(size * 2 + 1);
+ if (p == NULL)
+ return -1;
+
+ for (i = 0; i < size; i++) {
+ p[i * 2] = hexchar[(*q >> 4) & 0xf];
+ p[i * 2 + 1] = hexchar[*q & 0xf];
+ q++;
+ }
+ p[i * 2] = '\0';
+ *str = p;
+
+ return i * 2;
+}
+
+ssize_t ROKEN_LIB_FUNCTION
+hex_decode(const char *str, void *data, size_t len)
+{
+ size_t l;
+ unsigned char *p = data;
+ size_t i;
+
+ l = strlen(str);
+
+ /* check for overflow, same as (l+1)/2 but overflow safe */
+ if ((l/2) + (l&1) > len)
+ return -1;
+
+ i = 0;
+ if (l & 1) {
+ p[0] = pos(str[0]);
+ str++;
+ p++;
+ }
+ for (i = 0; i < l / 2; i++)
+ p[i] = pos(str[i * 2]) << 4 | pos(str[(i * 2) + 1]);
+ return i + (l & 1);
+}
diff --git a/source4/heimdal/lib/roken/hex.h b/source4/heimdal/lib/roken/hex.h
new file mode 100644
index 0000000000..b3c45511c8
--- /dev/null
+++ b/source4/heimdal/lib/roken/hex.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef _rk_HEX_H_
+#define _rk_HEX_H_ 1
+
+#ifndef ROKEN_LIB_FUNCTION
+#ifdef _WIN32
+#define ROKEN_LIB_FUNCTION _stdcall
+#else
+#define ROKEN_LIB_FUNCTION
+#endif
+#endif
+
+#define hex_encode rk_hex_encode
+#define hex_decode rk_hex_decode
+
+ssize_t ROKEN_LIB_FUNCTION
+ hex_encode(const void *, size_t, char **);
+ssize_t ROKEN_LIB_FUNCTION
+ hex_decode(const char *, void *, size_t);
+
+#endif /* _rk_HEX_H_ */
diff --git a/source4/heimdal/lib/roken/hostent_find_fqdn.c b/source4/heimdal/lib/roken/hostent_find_fqdn.c
new file mode 100644
index 0000000000..9376c1da36
--- /dev/null
+++ b/source4/heimdal/lib/roken/hostent_find_fqdn.c
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 1999 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+RCSID("$Id$");
+#endif
+
+#include "roken.h"
+
+/*
+ * Try to find a fqdn (with `.') in he if possible, else return h_name
+ */
+
+const char * ROKEN_LIB_FUNCTION
+hostent_find_fqdn (const struct hostent *he)
+{
+ const char *ret = he->h_name;
+ const char **h;
+
+ if (strchr (ret, '.') == NULL)
+ for (h = (const char **)he->h_aliases; *h != NULL; ++h) {
+ if (strchr (*h, '.') != NULL) {
+ ret = *h;
+ break;
+ }
+ }
+ return ret;
+}
diff --git a/source4/heimdal/lib/roken/inet_aton.c b/source4/heimdal/lib/roken/inet_aton.c
new file mode 100644
index 0000000000..c42697c63c
--- /dev/null
+++ b/source4/heimdal/lib/roken/inet_aton.c
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 1995, 1996, 1997, 1998, 1999 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+RCSID("$Id$");
+#endif
+
+#include "roken.h"
+
+/* Minimal implementation of inet_aton.
+ * Cannot distinguish between failure and a local broadcast address. */
+
+int ROKEN_LIB_FUNCTION
+inet_aton(const char *cp, struct in_addr *addr)
+{
+ addr->s_addr = inet_addr(cp);
+ return (addr->s_addr == INADDR_NONE) ? 0 : 1;
+}
diff --git a/source4/heimdal/lib/roken/inet_ntop.c b/source4/heimdal/lib/roken/inet_ntop.c
new file mode 100644
index 0000000000..5e4e4f9dc7
--- /dev/null
+++ b/source4/heimdal/lib/roken/inet_ntop.c
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 1999 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+RCSID("$Id$");
+#endif
+
+#include "roken.h"
+
+/*
+ *
+ */
+
+static const char *
+inet_ntop_v4 (const void *src, char *dst, size_t size)
+{
+ const char digits[] = "0123456789";
+ int i;
+ struct in_addr *addr = (struct in_addr *)src;
+ u_long a = ntohl(addr->s_addr);
+ const char *orig_dst = dst;
+
+ if (size < INET_ADDRSTRLEN) {
+ errno = ENOSPC;
+ return NULL;
+ }
+ for (i = 0; i < 4; ++i) {
+ int n = (a >> (24 - i * 8)) & 0xFF;
+ int non_zerop = 0;
+
+ if (non_zerop || n / 100 > 0) {
+ *dst++ = digits[n / 100];
+ n %= 100;
+ non_zerop = 1;
+ }
+ if (non_zerop || n / 10 > 0) {
+ *dst++ = digits[n / 10];
+ n %= 10;
+ non_zerop = 1;
+ }
+ *dst++ = digits[n];
+ if (i != 3)
+ *dst++ = '.';
+ }
+ *dst++ = '\0';
+ return orig_dst;
+}
+
+#ifdef HAVE_IPV6
+static const char *
+inet_ntop_v6 (const void *src, char *dst, size_t size)
+{
+ const char xdigits[] = "0123456789abcdef";
+ int i;
+ const struct in6_addr *addr = (struct in6_addr *)src;
+ const u_char *ptr = addr->s6_addr;
+ const char *orig_dst = dst;
+
+ if (size < INET6_ADDRSTRLEN) {
+ errno = ENOSPC;
+ return NULL;
+ }
+ for (i = 0; i < 8; ++i) {
+ int non_zerop = 0;
+
+ if (non_zerop || (ptr[0] >> 4)) {
+ *dst++ = xdigits[ptr[0] >> 4];
+ non_zerop = 1;
+ }
+ if (non_zerop || (ptr[0] & 0x0F)) {
+ *dst++ = xdigits[ptr[0] & 0x0F];
+ non_zerop = 1;
+ }
+ if (non_zerop || (ptr[1] >> 4)) {
+ *dst++ = xdigits[ptr[1] >> 4];
+ non_zerop = 1;
+ }
+ *dst++ = xdigits[ptr[1] & 0x0F];
+ if (i != 7)
+ *dst++ = ':';
+ ptr += 2;
+ }
+ *dst++ = '\0';
+ return orig_dst;
+}
+#endif /* HAVE_IPV6 */
+
+const char * ROKEN_LIB_FUNCTION
+inet_ntop(int af, const void *src, char *dst, size_t size)
+{
+ switch (af) {
+ case AF_INET :
+ return inet_ntop_v4 (src, dst, size);
+#ifdef HAVE_IPV6
+ case AF_INET6 :
+ return inet_ntop_v6 (src, dst, size);
+#endif
+ default :
+ errno = EAFNOSUPPORT;
+ return NULL;
+ }
+}
diff --git a/source4/heimdal/lib/roken/inet_pton.c b/source4/heimdal/lib/roken/inet_pton.c
new file mode 100644
index 0000000000..f2a9af3aa3
--- /dev/null
+++ b/source4/heimdal/lib/roken/inet_pton.c
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 1999 - 2000 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+RCSID("$Id$");
+#endif
+
+#include "roken.h"
+
+int ROKEN_LIB_FUNCTION
+inet_pton(int af, const char *src, void *dst)
+{
+ if (af != AF_INET) {
+ errno = EAFNOSUPPORT;
+ return -1;
+ }
+ return inet_aton (src, dst);
+}
diff --git a/source4/heimdal/lib/roken/issuid.c b/source4/heimdal/lib/roken/issuid.c
new file mode 100644
index 0000000000..257481cffb
--- /dev/null
+++ b/source4/heimdal/lib/roken/issuid.c
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 1998 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+RCSID("$Id$");
+#endif
+
+#include "roken.h"
+
+int ROKEN_LIB_FUNCTION
+issuid(void)
+{
+#if defined(HAVE_ISSETUGID)
+ return issetugid();
+#else /* !HAVE_ISSETUGID */
+
+#if defined(HAVE_GETUID) && defined(HAVE_GETEUID)
+ if(getuid() != geteuid())
+ return 1;
+#endif
+#if defined(HAVE_GETGID) && defined(HAVE_GETEGID)
+ if(getgid() != getegid())
+ return 2;
+#endif
+
+ return 0;
+#endif /* HAVE_ISSETUGID */
+}
diff --git a/source4/heimdal/lib/roken/net_read.c b/source4/heimdal/lib/roken/net_read.c
new file mode 100644
index 0000000000..ae025ffc71
--- /dev/null
+++ b/source4/heimdal/lib/roken/net_read.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 1995, 1996, 1997, 1998 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+RCSID("$Id$");
+#endif
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "roken.h"
+
+/*
+ * Like read but never return partial data.
+ */
+
+ssize_t ROKEN_LIB_FUNCTION
+net_read (int fd, void *buf, size_t nbytes)
+{
+ char *cbuf = (char *)buf;
+ ssize_t count;
+ size_t rem = nbytes;
+
+ while (rem > 0) {
+#ifdef WIN32
+ count = recv (fd, cbuf, rem, 0);
+#else
+ count = read (fd, cbuf, rem);
+#endif
+ if (count < 0) {
+ if (errno == EINTR)
+ continue;
+ else
+ return count;
+ } else if (count == 0) {
+ return count;
+ }
+ cbuf += count;
+ rem -= count;
+ }
+ return nbytes;
+}
diff --git a/source4/heimdal/lib/roken/net_write.c b/source4/heimdal/lib/roken/net_write.c
new file mode 100644
index 0000000000..11f5b8d7be
--- /dev/null
+++ b/source4/heimdal/lib/roken/net_write.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 1995, 1996, 1997, 1998 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+RCSID("$Id$");
+#endif
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "roken.h"
+
+/*
+ * Like write but never return partial data.
+ */
+
+ssize_t ROKEN_LIB_FUNCTION
+net_write (int fd, const void *buf, size_t nbytes)
+{
+ const char *cbuf = (const char *)buf;
+ ssize_t count;
+ size_t rem = nbytes;
+
+ while (rem > 0) {
+#ifdef WIN32
+ count = send (fd, cbuf, rem, 0);
+#else
+ count = write (fd, cbuf, rem);
+#endif
+ if (count < 0) {
+ if (errno == EINTR)
+ continue;
+ else
+ return count;
+ }
+ cbuf += count;
+ rem -= count;
+ }
+ return nbytes;
+}
diff --git a/source4/heimdal/lib/roken/parse_bytes.h b/source4/heimdal/lib/roken/parse_bytes.h
new file mode 100644
index 0000000000..7d389e808e
--- /dev/null
+++ b/source4/heimdal/lib/roken/parse_bytes.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 1999 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef __PARSE_BYTES_H__
+#define __PARSE_BYTES_H__
+
+#ifndef ROKEN_LIB_FUNCTION
+#ifdef _WIN32
+#define ROKEN_LIB_FUNCTION _stdcall
+#else
+#define ROKEN_LIB_FUNCTION
+#endif
+#endif
+
+int ROKEN_LIB_FUNCTION
+parse_bytes (const char *s, const char *def_unit);
+
+int ROKEN_LIB_FUNCTION
+unparse_bytes (int t, char *s, size_t len);
+
+int ROKEN_LIB_FUNCTION
+unparse_bytes_short (int t, char *s, size_t len);
+
+#endif /* __PARSE_BYTES_H__ */
diff --git a/source4/heimdal/lib/roken/parse_time.c b/source4/heimdal/lib/roken/parse_time.c
new file mode 100644
index 0000000000..6065eeb425
--- /dev/null
+++ b/source4/heimdal/lib/roken/parse_time.c
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 1997, 1998 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+RCSID("$Id$");
+#endif
+
+#include <parse_units.h>
+#include "parse_time.h"
+
+static struct units time_units[] = {
+ {"year", 365 * 24 * 60 * 60},
+ {"month", 30 * 24 * 60 * 60},
+ {"week", 7 * 24 * 60 * 60},
+ {"day", 24 * 60 * 60},
+ {"hour", 60 * 60},
+ {"h", 60 * 60},
+ {"minute", 60},
+ {"m", 60},
+ {"second", 1},
+ {"s", 1},
+ {NULL, 0},
+};
+
+int ROKEN_LIB_FUNCTION
+parse_time (const char *s, const char *def_unit)
+{
+ return parse_units (s, time_units, def_unit);
+}
+
+size_t ROKEN_LIB_FUNCTION
+unparse_time (int t, char *s, size_t len)
+{
+ return unparse_units (t, time_units, s, len);
+}
+
+size_t ROKEN_LIB_FUNCTION
+unparse_time_approx (int t, char *s, size_t len)
+{
+ return unparse_units_approx (t, time_units, s, len);
+}
+
+void ROKEN_LIB_FUNCTION
+print_time_table (FILE *f)
+{
+ print_units_table (time_units, f);
+}
diff --git a/source4/heimdal/lib/roken/parse_time.h b/source4/heimdal/lib/roken/parse_time.h
new file mode 100644
index 0000000000..23aae2fc97
--- /dev/null
+++ b/source4/heimdal/lib/roken/parse_time.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 1997 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef __PARSE_TIME_H__
+#define __PARSE_TIME_H__
+
+#ifndef ROKEN_LIB_FUNCTION
+#ifdef _WIN32
+#define ROKEN_LIB_FUNCTION _stdcall
+#else
+#define ROKEN_LIB_FUNCTION
+#endif
+#endif
+
+int
+parse_time (const char *s, const char *def_unit);
+
+size_t
+unparse_time (int t, char *s, size_t len);
+
+size_t
+unparse_time_approx (int t, char *s, size_t len);
+
+void
+print_time_table (FILE *f);
+
+#endif /* __PARSE_TIME_H__ */
diff --git a/source4/heimdal/lib/roken/parse_units.c b/source4/heimdal/lib/roken/parse_units.c
new file mode 100644
index 0000000000..4dbd7b489b
--- /dev/null
+++ b/source4/heimdal/lib/roken/parse_units.c
@@ -0,0 +1,330 @@
+/*
+ * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+RCSID("$Id$");
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include "roken.h"
+#include "parse_units.h"
+
+/*
+ * Parse string in `s' according to `units' and return value.
+ * def_unit defines the default unit.
+ */
+
+static int
+parse_something (const char *s, const struct units *units,
+ const char *def_unit,
+ int (*func)(int res, int val, unsigned mult),
+ int init,
+ int accept_no_val_p)
+{
+ const char *p;
+ int res = init;
+ unsigned def_mult = 1;
+
+ if (def_unit != NULL) {
+ const struct units *u;
+
+ for (u = units; u->name; ++u) {
+ if (strcasecmp (u->name, def_unit) == 0) {
+ def_mult = u->mult;
+ break;
+ }
+ }
+ if (u->name == NULL)
+ return -1;
+ }
+
+ p = s;
+ while (*p) {
+ double val;
+ char *next;
+ const struct units *u, *partial_unit;
+ size_t u_len;
+ unsigned partial;
+ int no_val_p = 0;
+
+ while(isspace((unsigned char)*p) || *p == ',')
+ ++p;
+
+ val = strtod (p, &next); /* strtol(p, &next, 0); */
+ if (p == next) {
+ val = 0;
+ if(!accept_no_val_p)
+ return -1;
+ no_val_p = 1;
+ }
+ p = next;
+ while (isspace((unsigned char)*p))
+ ++p;
+ if (*p == '\0') {
+ res = (*func)(res, val, def_mult);
+ if (res < 0)
+ return res;
+ break;
+ } else if (*p == '+') {
+ ++p;
+ val = 1;
+ } else if (*p == '-') {
+ ++p;
+ val = -1;
+ }
+ if (no_val_p && val == 0)
+ val = 1;
+ u_len = strcspn (p, ", \t");
+ partial = 0;
+ partial_unit = NULL;
+ if (u_len > 1 && p[u_len - 1] == 's')
+ --u_len;
+ for (u = units; u->name; ++u) {
+ if (strncasecmp (p, u->name, u_len) == 0) {
+ if (u_len == strlen (u->name)) {
+ p += u_len;
+ res = (*func)(res, val, u->mult);
+ if (res < 0)
+ return res;
+ break;
+ } else {
+ ++partial;
+ partial_unit = u;
+ }
+ }
+ }
+ if (u->name == NULL) {
+ if (partial == 1) {
+ p += u_len;
+ res = (*func)(res, val, partial_unit->mult);
+ if (res < 0)
+ return res;
+ } else {
+ return -1;
+ }
+ }
+ if (*p == 's')
+ ++p;
+ }
+ return res;
+}
+
+/*
+ * The string consists of a sequence of `n unit'
+ */
+
+static int
+acc_units(int res, int val, unsigned mult)
+{
+ return res + val * mult;
+}
+
+int ROKEN_LIB_FUNCTION
+parse_units (const char *s, const struct units *units,
+ const char *def_unit)
+{
+ return parse_something (s, units, def_unit, acc_units, 0, 0);
+}
+
+/*
+ * The string consists of a sequence of `[+-]flag'. `orig' consists
+ * the original set of flags, those are then modified and returned as
+ * the function value.
+ */
+
+static int
+acc_flags(int res, int val, unsigned mult)
+{
+ if(val == 1)
+ return res | mult;
+ else if(val == -1)
+ return res & ~mult;
+ else if (val == 0)
+ return mult;
+ else
+ return -1;
+}
+
+int ROKEN_LIB_FUNCTION
+parse_flags (const char *s, const struct units *units,
+ int orig)
+{
+ return parse_something (s, units, NULL, acc_flags, orig, 1);
+}
+
+/*
+ * Return a string representation according to `units' of `num' in `s'
+ * with maximum length `len'. The actual length is the function value.
+ */
+
+static int
+unparse_something (int num, const struct units *units, char *s, size_t len,
+ int (*print) (char *, size_t, int, const char *, int),
+ int (*update) (int, unsigned),
+ const char *zero_string)
+{
+ const struct units *u;
+ int ret = 0, tmp;
+
+ if (num == 0)
+ return snprintf (s, len, "%s", zero_string);
+
+ for (u = units; num > 0 && u->name; ++u) {
+ int divisor;
+
+ divisor = num / u->mult;
+ if (divisor) {
+ num = (*update) (num, u->mult);
+ tmp = (*print) (s, len, divisor, u->name, num);
+ if (tmp < 0)
+ return tmp;
+ if (tmp > len) {
+ len = 0;
+ s = NULL;
+ } else {
+ len -= tmp;
+ s += tmp;
+ }
+ ret += tmp;
+ }
+ }
+ return ret;
+}
+
+static int
+print_unit (char *s, size_t len, int divisor, const char *name, int rem)
+{
+ return snprintf (s, len, "%u %s%s%s",
+ divisor, name,
+ divisor == 1 ? "" : "s",
+ rem > 0 ? " " : "");
+}
+
+static int
+update_unit (int in, unsigned mult)
+{
+ return in % mult;
+}
+
+static int
+update_unit_approx (int in, unsigned mult)
+{
+ if (in / mult > 0)
+ return 0;
+ else
+ return update_unit (in, mult);
+}
+
+int ROKEN_LIB_FUNCTION
+unparse_units (int num, const struct units *units, char *s, size_t len)
+{
+ return unparse_something (num, units, s, len,
+ print_unit,
+ update_unit,
+ "0");
+}
+
+int ROKEN_LIB_FUNCTION
+unparse_units_approx (int num, const struct units *units, char *s, size_t len)
+{
+ return unparse_something (num, units, s, len,
+ print_unit,
+ update_unit_approx,
+ "0");
+}
+
+void ROKEN_LIB_FUNCTION
+print_units_table (const struct units *units, FILE *f)
+{
+ const struct units *u, *u2;
+ int max_sz = 0;
+
+ for (u = units; u->name; ++u) {
+ max_sz = max(max_sz, strlen(u->name));
+ }
+
+ for (u = units; u->name;) {
+ char buf[1024];
+ const struct units *next;
+
+ for (next = u + 1; next->name && next->mult == u->mult; ++next)
+ ;
+
+ if (next->name) {
+ for (u2 = next;
+ u2->name && u->mult % u2->mult != 0;
+ ++u2)
+ ;
+ if (u2->name == NULL)
+ --u2;
+ unparse_units (u->mult, u2, buf, sizeof(buf));
+ fprintf (f, "1 %*s = %s\n", max_sz, u->name, buf);
+ } else {
+ fprintf (f, "1 %s\n", u->name);
+ }
+ u = next;
+ }
+}
+
+static int
+print_flag (char *s, size_t len, int divisor, const char *name, int rem)
+{
+ return snprintf (s, len, "%s%s", name, rem > 0 ? ", " : "");
+}
+
+static int
+update_flag (int in, unsigned mult)
+{
+ return in - mult;
+}
+
+int ROKEN_LIB_FUNCTION
+unparse_flags (int num, const struct units *units, char *s, size_t len)
+{
+ return unparse_something (num, units, s, len,
+ print_flag,
+ update_flag,
+ "");
+}
+
+void ROKEN_LIB_FUNCTION
+print_flags_table (const struct units *units, FILE *f)
+{
+ const struct units *u;
+
+ for(u = units; u->name; ++u)
+ fprintf(f, "%s%s", u->name, (u+1)->name ? ", " : "\n");
+}
diff --git a/source4/heimdal/lib/roken/parse_units.h b/source4/heimdal/lib/roken/parse_units.h
new file mode 100644
index 0000000000..2f2235bb0d
--- /dev/null
+++ b/source4/heimdal/lib/roken/parse_units.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef __PARSE_UNITS_H__
+#define __PARSE_UNITS_H__
+
+#include <stdio.h>
+#include <stddef.h>
+
+#ifndef ROKEN_LIB_FUNCTION
+#ifdef _WIN32
+#define ROKEN_LIB_FUNCTION _stdcall
+#else
+#define ROKEN_LIB_FUNCTION
+#endif
+#endif
+
+struct units {
+ const char *name;
+ unsigned mult;
+};
+
+int ROKEN_LIB_FUNCTION
+parse_units (const char *s, const struct units *units,
+ const char *def_unit);
+
+void ROKEN_LIB_FUNCTION
+print_units_table (const struct units *units, FILE *f);
+
+int ROKEN_LIB_FUNCTION
+parse_flags (const char *s, const struct units *units,
+ int orig);
+
+int ROKEN_LIB_FUNCTION
+unparse_units (int num, const struct units *units, char *s, size_t len);
+
+int ROKEN_LIB_FUNCTION
+unparse_units_approx (int num, const struct units *units, char *s,
+ size_t len);
+
+int ROKEN_LIB_FUNCTION
+unparse_flags (int num, const struct units *units, char *s, size_t len);
+
+void ROKEN_LIB_FUNCTION
+print_flags_table (const struct units *units, FILE *f);
+
+#endif /* __PARSE_UNITS_H__ */
diff --git a/source4/heimdal/lib/roken/resolve.c b/source4/heimdal/lib/roken/resolve.c
new file mode 100644
index 0000000000..f358a5b266
--- /dev/null
+++ b/source4/heimdal/lib/roken/resolve.c
@@ -0,0 +1,712 @@
+/*
+ * Copyright (c) 1995 - 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include "roken.h"
+#ifdef HAVE_ARPA_NAMESER_H
+#include <arpa/nameser.h>
+#endif
+#ifdef HAVE_RESOLV_H
+#include <resolv.h>
+#endif
+#include "resolve.h"
+
+#include <assert.h>
+
+RCSID("$Id$");
+
+#ifdef _AIX /* AIX have broken res_nsearch() in 5.1 (5.0 also ?) */
+#undef HAVE_RES_NSEARCH
+#endif
+
+#define DECL(X) {#X, rk_ns_t_##X}
+
+static struct stot{
+ const char *name;
+ int type;
+}stot[] = {
+ DECL(a),
+ DECL(aaaa),
+ DECL(ns),
+ DECL(cname),
+ DECL(soa),
+ DECL(ptr),
+ DECL(mx),
+ DECL(txt),
+ DECL(afsdb),
+ DECL(sig),
+ DECL(key),
+ DECL(srv),
+ DECL(naptr),
+ DECL(sshfp),
+ DECL(ds),
+ {NULL, 0}
+};
+
+int _resolve_debug = 0;
+
+int ROKEN_LIB_FUNCTION
+dns_string_to_type(const char *name)
+{
+ struct stot *p = stot;
+ for(p = stot; p->name; p++)
+ if(strcasecmp(name, p->name) == 0)
+ return p->type;
+ return -1;
+}
+
+const char * ROKEN_LIB_FUNCTION
+dns_type_to_string(int type)
+{
+ struct stot *p = stot;
+ for(p = stot; p->name; p++)
+ if(type == p->type)
+ return p->name;
+ return NULL;
+}
+
+#if (defined(HAVE_RES_SEARCH) || defined(HAVE_RES_NSEARCH)) && defined(HAVE_DN_EXPAND)
+
+static void
+dns_free_rr(struct resource_record *rr)
+{
+ if(rr->domain)
+ free(rr->domain);
+ if(rr->u.data)
+ free(rr->u.data);
+ free(rr);
+}
+
+void ROKEN_LIB_FUNCTION
+dns_free_data(struct dns_reply *r)
+{
+ struct resource_record *rr;
+ if(r->q.domain)
+ free(r->q.domain);
+ for(rr = r->head; rr;){
+ struct resource_record *tmp = rr;
+ rr = rr->next;
+ dns_free_rr(tmp);
+ }
+ free (r);
+}
+
+static int
+parse_record(const unsigned char *data, const unsigned char *end_data,
+ const unsigned char **pp, struct resource_record **ret_rr)
+{
+ struct resource_record *rr;
+ int type, class, ttl;
+ unsigned size;
+ int status;
+ char host[MAXDNAME];
+ const unsigned char *p = *pp;
+
+ *ret_rr = NULL;
+
+ status = dn_expand(data, end_data, p, host, sizeof(host));
+ if(status < 0)
+ return -1;
+ if (p + status + 10 > end_data)
+ return -1;
+
+ p += status;
+ type = (p[0] << 8) | p[1];
+ p += 2;
+ class = (p[0] << 8) | p[1];
+ p += 2;
+ ttl = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
+ p += 4;
+ size = (p[0] << 8) | p[1];
+ p += 2;
+
+ if (p + size > end_data)
+ return -1;
+
+ rr = calloc(1, sizeof(*rr));
+ if(rr == NULL)
+ return -1;
+ rr->domain = strdup(host);
+ if(rr->domain == NULL) {
+ dns_free_rr(rr);
+ return -1;
+ }
+ rr->type = type;
+ rr->class = class;
+ rr->ttl = ttl;
+ rr->size = size;
+ switch(type){
+ case rk_ns_t_ns:
+ case rk_ns_t_cname:
+ case rk_ns_t_ptr:
+ status = dn_expand(data, end_data, p, host, sizeof(host));
+ if(status < 0) {
+ dns_free_rr(rr);
+ return -1;
+ }
+ rr->u.txt = strdup(host);
+ if(rr->u.txt == NULL) {
+ dns_free_rr(rr);
+ return -1;
+ }
+ break;
+ case rk_ns_t_mx:
+ case rk_ns_t_afsdb:{
+ size_t hostlen;
+
+ status = dn_expand(data, end_data, p + 2, host, sizeof(host));
+ if(status < 0){
+ dns_free_rr(rr);
+ return -1;
+ }
+ if (status + 2 > size) {
+ dns_free_rr(rr);
+ return -1;
+ }
+
+ hostlen = strlen(host);
+ rr->u.mx = (struct mx_record*)malloc(sizeof(struct mx_record) +
+ hostlen);
+ if(rr->u.mx == NULL) {
+ dns_free_rr(rr);
+ return -1;
+ }
+ rr->u.mx->preference = (p[0] << 8) | p[1];
+ strlcpy(rr->u.mx->domain, host, hostlen + 1);
+ break;
+ }
+ case rk_ns_t_srv:{
+ size_t hostlen;
+ status = dn_expand(data, end_data, p + 6, host, sizeof(host));
+ if(status < 0){
+ dns_free_rr(rr);
+ return -1;
+ }
+ if (status + 6 > size) {
+ dns_free_rr(rr);
+ return -1;
+ }
+
+ hostlen = strlen(host);
+ rr->u.srv =
+ (struct srv_record*)malloc(sizeof(struct srv_record) +
+ hostlen);
+ if(rr->u.srv == NULL) {
+ dns_free_rr(rr);
+ return -1;
+ }
+ rr->u.srv->priority = (p[0] << 8) | p[1];
+ rr->u.srv->weight = (p[2] << 8) | p[3];
+ rr->u.srv->port = (p[4] << 8) | p[5];
+ strlcpy(rr->u.srv->target, host, hostlen + 1);
+ break;
+ }
+ case rk_ns_t_txt:{
+ if(size == 0 || size < *p + 1) {
+ dns_free_rr(rr);
+ return -1;
+ }
+ rr->u.txt = (char*)malloc(*p + 1);
+ if(rr->u.txt == NULL) {
+ dns_free_rr(rr);
+ return -1;
+ }
+ strncpy(rr->u.txt, (const char*)(p + 1), *p);
+ rr->u.txt[*p] = '\0';
+ break;
+ }
+ case rk_ns_t_key : {
+ size_t key_len;
+
+ if (size < 4) {
+ dns_free_rr(rr);
+ return -1;
+ }
+
+ key_len = size - 4;
+ rr->u.key = malloc (sizeof(*rr->u.key) + key_len - 1);
+ if (rr->u.key == NULL) {
+ dns_free_rr(rr);
+ return -1;
+ }
+
+ rr->u.key->flags = (p[0] << 8) | p[1];
+ rr->u.key->protocol = p[2];
+ rr->u.key->algorithm = p[3];
+ rr->u.key->key_len = key_len;
+ memcpy (rr->u.key->key_data, p + 4, key_len);
+ break;
+ }
+ case rk_ns_t_sig : {
+ size_t sig_len, hostlen;
+
+ if(size <= 18) {
+ dns_free_rr(rr);
+ return -1;
+ }
+ status = dn_expand (data, end_data, p + 18, host, sizeof(host));
+ if (status < 0) {
+ dns_free_rr(rr);
+ return -1;
+ }
+ if (status + 18 > size) {
+ dns_free_rr(rr);
+ return -1;
+ }
+
+ /* the signer name is placed after the sig_data, to make it
+ easy to free this structure; the size calculation below
+ includes the zero-termination if the structure itself.
+ don't you just love C?
+ */
+ sig_len = size - 18 - status;
+ hostlen = strlen(host);
+ rr->u.sig = malloc(sizeof(*rr->u.sig)
+ + hostlen + sig_len);
+ if (rr->u.sig == NULL) {
+ dns_free_rr(rr);
+ return -1;
+ }
+ rr->u.sig->type = (p[0] << 8) | p[1];
+ rr->u.sig->algorithm = p[2];
+ rr->u.sig->labels = p[3];
+ rr->u.sig->orig_ttl = (p[4] << 24) | (p[5] << 16)
+ | (p[6] << 8) | p[7];
+ rr->u.sig->sig_expiration = (p[8] << 24) | (p[9] << 16)
+ | (p[10] << 8) | p[11];
+ rr->u.sig->sig_inception = (p[12] << 24) | (p[13] << 16)
+ | (p[14] << 8) | p[15];
+ rr->u.sig->key_tag = (p[16] << 8) | p[17];
+ rr->u.sig->sig_len = sig_len;
+ memcpy (rr->u.sig->sig_data, p + 18 + status, sig_len);
+ rr->u.sig->signer = &rr->u.sig->sig_data[sig_len];
+ strlcpy(rr->u.sig->signer, host, hostlen + 1);
+ break;
+ }
+
+ case rk_ns_t_cert : {
+ size_t cert_len;
+
+ if (size < 5) {
+ dns_free_rr(rr);
+ return -1;
+ }
+
+ cert_len = size - 5;
+ rr->u.cert = malloc (sizeof(*rr->u.cert) + cert_len - 1);
+ if (rr->u.cert == NULL) {
+ dns_free_rr(rr);
+ return -1;
+ }
+
+ rr->u.cert->type = (p[0] << 8) | p[1];
+ rr->u.cert->tag = (p[2] << 8) | p[3];
+ rr->u.cert->algorithm = p[4];
+ rr->u.cert->cert_len = cert_len;
+ memcpy (rr->u.cert->cert_data, p + 5, cert_len);
+ break;
+ }
+ case rk_ns_t_sshfp : {
+ size_t sshfp_len;
+
+ if (size < 2) {
+ dns_free_rr(rr);
+ return -1;
+ }
+
+ sshfp_len = size - 2;
+
+ rr->u.sshfp = malloc (sizeof(*rr->u.sshfp) + sshfp_len - 1);
+ if (rr->u.sshfp == NULL) {
+ dns_free_rr(rr);
+ return -1;
+ }
+
+ rr->u.sshfp->algorithm = p[0];
+ rr->u.sshfp->type = p[1];
+ rr->u.sshfp->sshfp_len = sshfp_len;
+ memcpy (rr->u.sshfp->sshfp_data, p + 2, sshfp_len);
+ break;
+ }
+ case rk_ns_t_ds: {
+ size_t digest_len;
+
+ if (size < 4) {
+ dns_free_rr(rr);
+ return -1;
+ }
+
+ digest_len = size - 4;
+
+ rr->u.ds = malloc (sizeof(*rr->u.ds) + digest_len - 1);
+ if (rr->u.ds == NULL) {
+ dns_free_rr(rr);
+ return -1;
+ }
+
+ rr->u.ds->key_tag = (p[0] << 8) | p[1];
+ rr->u.ds->algorithm = p[2];
+ rr->u.ds->digest_type = p[3];
+ rr->u.ds->digest_len = digest_len;
+ memcpy (rr->u.ds->digest_data, p + 4, digest_len);
+ break;
+ }
+ default:
+ rr->u.data = (unsigned char*)malloc(size);
+ if(size != 0 && rr->u.data == NULL) {
+ dns_free_rr(rr);
+ return -1;
+ }
+ if (size)
+ memcpy(rr->u.data, p, size);
+ }
+ *pp = p + size;
+ *ret_rr = rr;
+
+ return 0;
+}
+
+#ifndef TEST_RESOLVE
+static
+#endif
+struct dns_reply*
+parse_reply(const unsigned char *data, size_t len)
+{
+ const unsigned char *p;
+ int status;
+ int i;
+ char host[MAXDNAME];
+ const unsigned char *end_data = data + len;
+ struct dns_reply *r;
+ struct resource_record **rr;
+
+ r = calloc(1, sizeof(*r));
+ if (r == NULL)
+ return NULL;
+
+ p = data;
+
+ r->h.id = (p[0] << 8) | p[1];
+ r->h.flags = 0;
+ if (p[2] & 0x01)
+ r->h.flags |= rk_DNS_HEADER_RESPONSE_FLAG;
+ r->h.opcode = (p[2] >> 1) & 0xf;
+ if (p[2] & 0x20)
+ r->h.flags |= rk_DNS_HEADER_AUTHORITIVE_ANSWER;
+ if (p[2] & 0x40)
+ r->h.flags |= rk_DNS_HEADER_TRUNCATED_MESSAGE;
+ if (p[2] & 0x80)
+ r->h.flags |= rk_DNS_HEADER_RECURSION_DESIRED;
+ if (p[3] & 0x01)
+ r->h.flags |= rk_DNS_HEADER_RECURSION_AVAILABLE;
+ if (p[3] & 0x04)
+ r->h.flags |= rk_DNS_HEADER_AUTHORITIVE_ANSWER;
+ if (p[3] & 0x08)
+ r->h.flags |= rk_DNS_HEADER_CHECKING_DISABLED;
+ r->h.response_code = (p[3] >> 4) & 0xf;
+ r->h.qdcount = (p[4] << 8) | p[5];
+ r->h.ancount = (p[6] << 8) | p[7];
+ r->h.nscount = (p[8] << 8) | p[9];
+ r->h.arcount = (p[10] << 8) | p[11];
+
+ p += 12;
+
+ if(r->h.qdcount != 1) {
+ free(r);
+ return NULL;
+ }
+ status = dn_expand(data, end_data, p, host, sizeof(host));
+ if(status < 0){
+ dns_free_data(r);
+ return NULL;
+ }
+ r->q.domain = strdup(host);
+ if(r->q.domain == NULL) {
+ dns_free_data(r);
+ return NULL;
+ }
+ if (p + status + 4 > end_data) {
+ dns_free_data(r);
+ return NULL;
+ }
+ p += status;
+ r->q.type = (p[0] << 8 | p[1]);
+ p += 2;
+ r->q.class = (p[0] << 8 | p[1]);
+ p += 2;
+
+ rr = &r->head;
+ for(i = 0; i < r->h.ancount; i++) {
+ if(parse_record(data, end_data, &p, rr) != 0) {
+ dns_free_data(r);
+ return NULL;
+ }
+ rr = &(*rr)->next;
+ }
+ for(i = 0; i < r->h.nscount; i++) {
+ if(parse_record(data, end_data, &p, rr) != 0) {
+ dns_free_data(r);
+ return NULL;
+ }
+ rr = &(*rr)->next;
+ }
+ for(i = 0; i < r->h.arcount; i++) {
+ if(parse_record(data, end_data, &p, rr) != 0) {
+ dns_free_data(r);
+ return NULL;
+ }
+ rr = &(*rr)->next;
+ }
+ *rr = NULL;
+ return r;
+}
+
+#ifdef HAVE_RES_NSEARCH
+#ifdef HAVE_RES_NDESTROY
+#define rk_res_free(x) res_ndestroy(x)
+#else
+#define rk_res_free(x) res_nclose(x)
+#endif
+#endif
+
+static struct dns_reply *
+dns_lookup_int(const char *domain, int rr_class, int rr_type)
+{
+ struct dns_reply *r;
+ unsigned char *reply = NULL;
+ int size;
+ int len;
+#ifdef HAVE_RES_NSEARCH
+ struct __res_state state;
+ memset(&state, 0, sizeof(state));
+ if(res_ninit(&state))
+ return NULL; /* is this the best we can do? */
+#elif defined(HAVE__RES)
+ u_long old_options = 0;
+#endif
+
+ size = 0;
+ len = 1000;
+ do {
+ if (reply) {
+ free(reply);
+ reply = NULL;
+ }
+ if (size <= len)
+ size = len;
+ if (_resolve_debug) {
+#ifdef HAVE_RES_NSEARCH
+ state.options |= RES_DEBUG;
+#elif defined(HAVE__RES)
+ old_options = _res.options;
+ _res.options |= RES_DEBUG;
+#endif
+ fprintf(stderr, "dns_lookup(%s, %d, %s), buffer size %d\n", domain,
+ rr_class, dns_type_to_string(rr_type), size);
+ }
+ reply = malloc(size);
+ if (reply == NULL) {
+#ifdef HAVE_RES_NSEARCH
+ rk_res_free(&state);
+#endif
+ return NULL;
+ }
+#ifdef HAVE_RES_NSEARCH
+ len = res_nsearch(&state, domain, rr_class, rr_type, reply, size);
+#else
+ len = res_search(domain, rr_class, rr_type, reply, size);
+#endif
+ if (_resolve_debug) {
+#if defined(HAVE__RES) && !defined(HAVE_RES_NSEARCH)
+ _res.options = old_options;
+#endif
+ fprintf(stderr, "dns_lookup(%s, %d, %s) --> %d\n",
+ domain, rr_class, dns_type_to_string(rr_type), len);
+ }
+ if (len < 0) {
+#ifdef HAVE_RES_NSEARCH
+ rk_res_free(&state);
+#endif
+ free(reply);
+ return NULL;
+ }
+ } while (size < len && len < rk_DNS_MAX_PACKET_SIZE);
+#ifdef HAVE_RES_NSEARCH
+ rk_res_free(&state);
+#endif
+
+ len = min(len, size);
+ r = parse_reply(reply, len);
+ free(reply);
+ return r;
+}
+
+struct dns_reply * ROKEN_LIB_FUNCTION
+dns_lookup(const char *domain, const char *type_name)
+{
+ int type;
+
+ type = dns_string_to_type(type_name);
+ if(type == -1) {
+ if(_resolve_debug)
+ fprintf(stderr, "dns_lookup: unknown resource type: `%s'\n",
+ type_name);
+ return NULL;
+ }
+ return dns_lookup_int(domain, C_IN, type);
+}
+
+static int
+compare_srv(const void *a, const void *b)
+{
+ const struct resource_record *const* aa = a, *const* bb = b;
+
+ if((*aa)->u.srv->priority == (*bb)->u.srv->priority)
+ return ((*aa)->u.srv->weight - (*bb)->u.srv->weight);
+ return ((*aa)->u.srv->priority - (*bb)->u.srv->priority);
+}
+
+#ifndef HAVE_RANDOM
+#define random() rand()
+#endif
+
+/* try to rearrange the srv-records by the algorithm in RFC2782 */
+void ROKEN_LIB_FUNCTION
+dns_srv_order(struct dns_reply *r)
+{
+ struct resource_record **srvs, **ss, **headp;
+ struct resource_record *rr;
+ int num_srv = 0;
+
+#if defined(HAVE_INITSTATE) && defined(HAVE_SETSTATE)
+ int state[256 / sizeof(int)];
+ char *oldstate;
+#endif
+
+ for(rr = r->head; rr; rr = rr->next)
+ if(rr->type == rk_ns_t_srv)
+ num_srv++;
+
+ if(num_srv == 0)
+ return;
+
+ srvs = malloc(num_srv * sizeof(*srvs));
+ if(srvs == NULL)
+ return; /* XXX not much to do here */
+
+ /* unlink all srv-records from the linked list and put them in
+ a vector */
+ for(ss = srvs, headp = &r->head; *headp; )
+ if((*headp)->type == rk_ns_t_srv) {
+ *ss = *headp;
+ *headp = (*headp)->next;
+ (*ss)->next = NULL;
+ ss++;
+ } else
+ headp = &(*headp)->next;
+
+ /* sort them by priority and weight */
+ qsort(srvs, num_srv, sizeof(*srvs), compare_srv);
+
+#if defined(HAVE_INITSTATE) && defined(HAVE_SETSTATE)
+ oldstate = initstate(time(NULL), (char*)state, sizeof(state));
+#endif
+
+ headp = &r->head;
+
+ for(ss = srvs; ss < srvs + num_srv; ) {
+ int sum, rnd, count;
+ struct resource_record **ee, **tt;
+ /* find the last record with the same priority and count the
+ sum of all weights */
+ for(sum = 0, tt = ss; tt < srvs + num_srv; tt++) {
+ assert(*tt != NULL);
+ if((*tt)->u.srv->priority != (*ss)->u.srv->priority)
+ break;
+ sum += (*tt)->u.srv->weight;
+ }
+ ee = tt;
+ /* ss is now the first record of this priority and ee is the
+ first of the next */
+ while(ss < ee) {
+ rnd = random() % (sum + 1);
+ for(count = 0, tt = ss; ; tt++) {
+ if(*tt == NULL)
+ continue;
+ count += (*tt)->u.srv->weight;
+ if(count >= rnd)
+ break;
+ }
+
+ assert(tt < ee);
+
+ /* insert the selected record at the tail (of the head) of
+ the list */
+ (*tt)->next = *headp;
+ *headp = *tt;
+ headp = &(*tt)->next;
+ sum -= (*tt)->u.srv->weight;
+ *tt = NULL;
+ while(ss < ee && *ss == NULL)
+ ss++;
+ }
+ }
+
+#if defined(HAVE_INITSTATE) && defined(HAVE_SETSTATE)
+ setstate(oldstate);
+#endif
+ free(srvs);
+ return;
+}
+
+#else /* NOT defined(HAVE_RES_SEARCH) && defined(HAVE_DN_EXPAND) */
+
+struct dns_reply * ROKEN_LIB_FUNCTION
+dns_lookup(const char *domain, const char *type_name)
+{
+ return NULL;
+}
+
+void ROKEN_LIB_FUNCTION
+dns_free_data(struct dns_reply *r)
+{
+}
+
+void ROKEN_LIB_FUNCTION
+dns_srv_order(struct dns_reply *r)
+{
+}
+
+#endif
diff --git a/source4/heimdal/lib/roken/resolve.h b/source4/heimdal/lib/roken/resolve.h
new file mode 100644
index 0000000000..d181dfa070
--- /dev/null
+++ b/source4/heimdal/lib/roken/resolve.h
@@ -0,0 +1,298 @@
+/*
+ * Copyright (c) 1995 - 2002 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef __RESOLVE_H__
+#define __RESOLVE_H__
+
+#ifndef ROKEN_LIB_FUNCTION
+#ifdef _WIN32
+#define ROKEN_LIB_FUNCTION _stdcall
+#else
+#define ROKEN_LIB_FUNCTION
+#endif
+#endif
+
+typedef enum {
+ rk_ns_t_invalid = 0, /* Cookie. */
+ rk_ns_t_a = 1, /* Host address. */
+ rk_ns_t_ns = 2, /* Authoritative server. */
+ rk_ns_t_md = 3, /* Mail destination. */
+ rk_ns_t_mf = 4, /* Mail forwarder. */
+ rk_ns_t_cname = 5, /* Canonical name. */
+ rk_ns_t_soa = 6, /* Start of authority zone. */
+ rk_ns_t_mb = 7, /* Mailbox domain name. */
+ rk_ns_t_mg = 8, /* Mail group member. */
+ rk_ns_t_mr = 9, /* Mail rename name. */
+ rk_ns_t_null = 10, /* Null resource record. */
+ rk_ns_t_wks = 11, /* Well known service. */
+ rk_ns_t_ptr = 12, /* Domain name pointer. */
+ rk_ns_t_hinfo = 13, /* Host information. */
+ rk_ns_t_minfo = 14, /* Mailbox information. */
+ rk_ns_t_mx = 15, /* Mail routing information. */
+ rk_ns_t_txt = 16, /* Text strings. */
+ rk_ns_t_rp = 17, /* Responsible person. */
+ rk_ns_t_afsdb = 18, /* AFS cell database. */
+ rk_ns_t_x25 = 19, /* X_25 calling address. */
+ rk_ns_t_isdn = 20, /* ISDN calling address. */
+ rk_ns_t_rt = 21, /* Router. */
+ rk_ns_t_nsap = 22, /* NSAP address. */
+ rk_ns_t_nsap_ptr = 23, /* Reverse NSAP lookup (deprecated). */
+ rk_ns_t_sig = 24, /* Security signature. */
+ rk_ns_t_key = 25, /* Security key. */
+ rk_ns_t_px = 26, /* X.400 mail mapping. */
+ rk_ns_t_gpos = 27, /* Geographical position (withdrawn). */
+ rk_ns_t_aaaa = 28, /* Ip6 Address. */
+ rk_ns_t_loc = 29, /* Location Information. */
+ rk_ns_t_nxt = 30, /* Next domain (security). */
+ rk_ns_t_eid = 31, /* Endpoint identifier. */
+ rk_ns_t_nimloc = 32, /* Nimrod Locator. */
+ rk_ns_t_srv = 33, /* Server Selection. */
+ rk_ns_t_atma = 34, /* ATM Address */
+ rk_ns_t_naptr = 35, /* Naming Authority PoinTeR */
+ rk_ns_t_kx = 36, /* Key Exchange */
+ rk_ns_t_cert = 37, /* Certification record */
+ rk_ns_t_a6 = 38, /* IPv6 address (deprecates AAAA) */
+ rk_ns_t_dname = 39, /* Non-terminal DNAME (for IPv6) */
+ rk_ns_t_sink = 40, /* Kitchen sink (experimentatl) */
+ rk_ns_t_opt = 41, /* EDNS0 option (meta-RR) */
+ rk_ns_t_apl = 42, /* Address prefix list (RFC 3123) */
+ rk_ns_t_ds = 43, /* Delegation Signer (RFC 3658) */
+ rk_ns_t_sshfp = 44, /* SSH fingerprint */
+ rk_ns_t_tkey = 249, /* Transaction key */
+ rk_ns_t_tsig = 250, /* Transaction signature. */
+ rk_ns_t_ixfr = 251, /* Incremental zone transfer. */
+ rk_ns_t_axfr = 252, /* Transfer zone of authority. */
+ rk_ns_t_mailb = 253, /* Transfer mailbox records. */
+ rk_ns_t_maila = 254, /* Transfer mail agent records. */
+ rk_ns_t_any = 255, /* Wildcard match. */
+ rk_ns_t_zxfr = 256, /* BIND-specific, nonstandard. */
+ rk_ns_t_max = 65536
+} rk_ns_type;
+
+/* We use these, but they are not always present in <arpa/nameser.h> */
+
+#ifndef C_IN
+#define C_IN 1
+#endif
+
+#ifndef T_A
+#define T_A 1
+#endif
+#ifndef T_NS
+#define T_NS 2
+#endif
+#ifndef T_CNAME
+#define T_CNAME 5
+#endif
+#ifndef T_SOA
+#define T_SOA 5
+#endif
+#ifndef T_PTR
+#define T_PTR 12
+#endif
+#ifndef T_MX
+#define T_MX 15
+#endif
+#ifndef T_TXT
+#define T_TXT 16
+#endif
+#ifndef T_AFSDB
+#define T_AFSDB 18
+#endif
+#ifndef T_SIG
+#define T_SIG 24
+#endif
+#ifndef T_KEY
+#define T_KEY 25
+#endif
+#ifndef T_AAAA
+#define T_AAAA 28
+#endif
+#ifndef T_SRV
+#define T_SRV 33
+#endif
+#ifndef T_NAPTR
+#define T_NAPTR 35
+#endif
+#ifndef T_CERT
+#define T_CERT 37
+#endif
+#ifndef T_SSHFP
+#define T_SSHFP 44
+#endif
+
+#ifndef MAXDNAME
+#define MAXDNAME 1025
+#endif
+
+#define dns_query rk_dns_query
+#define mx_record rk_mx_record
+#define srv_record rk_srv_record
+#define key_record rk_key_record
+#define sig_record rk_sig_record
+#define cert_record rk_cert_record
+#define sshfp_record rk_sshfp_record
+#define resource_record rk_resource_record
+#define dns_reply rk_dns_reply
+
+#define dns_lookup rk_dns_lookup
+#define dns_free_data rk_dns_free_data
+#define dns_string_to_type rk_dns_string_to_type
+#define dns_type_to_string rk_dns_type_to_string
+#define dns_srv_order rk_dns_srv_order
+
+struct dns_query{
+ char *domain;
+ unsigned type;
+ unsigned class;
+};
+
+struct mx_record{
+ unsigned preference;
+ char domain[1];
+};
+
+struct srv_record{
+ unsigned priority;
+ unsigned weight;
+ unsigned port;
+ char target[1];
+};
+
+struct key_record {
+ unsigned flags;
+ unsigned protocol;
+ unsigned algorithm;
+ size_t key_len;
+ u_char key_data[1];
+};
+
+struct sig_record {
+ unsigned type;
+ unsigned algorithm;
+ unsigned labels;
+ unsigned orig_ttl;
+ unsigned sig_expiration;
+ unsigned sig_inception;
+ unsigned key_tag;
+ char *signer;
+ unsigned sig_len;
+ char sig_data[1]; /* also includes signer */
+};
+
+struct cert_record {
+ unsigned type;
+ unsigned tag;
+ unsigned algorithm;
+ size_t cert_len;
+ u_char cert_data[1];
+};
+
+struct sshfp_record {
+ unsigned algorithm;
+ unsigned type;
+ size_t sshfp_len;
+ u_char sshfp_data[1];
+};
+
+struct ds_record {
+ unsigned key_tag;
+ unsigned algorithm;
+ unsigned digest_type;
+ unsigned digest_len;
+ u_char digest_data[1];
+};
+
+struct resource_record{
+ char *domain;
+ unsigned type;
+ unsigned class;
+ unsigned ttl;
+ unsigned size;
+ union {
+ void *data;
+ struct mx_record *mx;
+ struct mx_record *afsdb; /* mx and afsdb are identical */
+ struct srv_record *srv;
+ struct in_addr *a;
+ char *txt;
+ struct key_record *key;
+ struct cert_record *cert;
+ struct sig_record *sig;
+ struct sshfp_record *sshfp;
+ struct ds_record *ds;
+ }u;
+ struct resource_record *next;
+};
+
+#define rk_DNS_MAX_PACKET_SIZE 0xffff
+
+struct dns_header {
+ unsigned id;
+ unsigned flags;
+#define rk_DNS_HEADER_RESPONSE_FLAG 1
+#define rk_DNS_HEADER_AUTHORITIVE_ANSWER 2
+#define rk_DNS_HEADER_TRUNCATED_MESSAGE 4
+#define rk_DNS_HEADER_RECURSION_DESIRED 8
+#define rk_DNS_HEADER_RECURSION_AVAILABLE 16
+#define rk_DNS_HEADER_AUTHENTIC_DATA 32
+#define rk_DNS_HEADER_CHECKING_DISABLED 64
+ unsigned opcode;
+ unsigned response_code;
+ unsigned qdcount;
+ unsigned ancount;
+ unsigned nscount;
+ unsigned arcount;
+};
+
+struct dns_reply{
+ struct dns_header h;
+ struct dns_query q;
+ struct resource_record *head;
+};
+
+
+struct dns_reply* ROKEN_LIB_FUNCTION
+ dns_lookup(const char *, const char *);
+void ROKEN_LIB_FUNCTION
+ dns_free_data(struct dns_reply *);
+int ROKEN_LIB_FUNCTION
+ dns_string_to_type(const char *name);
+const char *ROKEN_LIB_FUNCTION
+ dns_type_to_string(int type);
+void ROKEN_LIB_FUNCTION
+ dns_srv_order(struct dns_reply*);
+
+#endif /* __RESOLVE_H__ */
diff --git a/source4/heimdal/lib/roken/rkpty.c b/source4/heimdal/lib/roken/rkpty.c
new file mode 100644
index 0000000000..309469722b
--- /dev/null
+++ b/source4/heimdal/lib/roken/rkpty.c
@@ -0,0 +1,336 @@
+/*
+ * Copyright (c) 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#ifndef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#ifdef HAVE_PTY_H
+#include <pty.h>
+#endif
+#ifdef HAVE_UTIL_H
+#include <util.h>
+#endif
+
+#include "roken.h"
+#include <getarg.h>
+
+struct command {
+ enum { CMD_EXPECT = 0, CMD_SEND, CMD_PASSWORD } type;
+ unsigned int lineno;
+ char *str;
+ struct command *next;
+};
+
+/*
+ *
+ */
+
+static struct command *commands, **next = &commands;
+
+static sig_atomic_t alarmset = 0;
+
+static int timeout = 10;
+static int verbose;
+static int help_flag;
+static int version_flag;
+
+static int master;
+static int slave;
+static char line[256] = { 0 };
+
+static void
+caught_signal(int signo)
+{
+ alarmset = signo;
+}
+
+
+static void
+open_pty(void)
+{
+#if defined(HAVE_OPENPTY) || defined(__linux) || defined(__osf__) /* XXX */
+ if(openpty(&master, &slave, line, 0, 0) == 0)
+ return;
+#endif /* HAVE_OPENPTY .... */
+ /* more cases, like open /dev/ptmx, etc */
+
+ exit(77);
+}
+
+/*
+ *
+ */
+
+static char *
+iscmd(const char *buf, const char *s)
+{
+ size_t len = strlen(s);
+ if (strncmp(buf, s, len) != 0)
+ return NULL;
+ return estrdup(buf + len);
+}
+
+static void
+parse_configuration(const char *fn)
+{
+ struct command *c;
+ char s[1024];
+ char *str;
+ unsigned int lineno = 0;
+ FILE *cmd;
+
+ cmd = fopen(fn, "r");
+ if (cmd == NULL)
+ err(1, "open: %s", fn);
+
+ while (fgets(s, sizeof(s), cmd) != NULL) {
+
+ s[strcspn(s, "#\n")] = '\0';
+ lineno++;
+
+ c = calloc(1, sizeof(*c));
+ if (c == NULL)
+ errx(1, "malloc");
+
+ c->lineno = lineno;
+ (*next) = c;
+ next = &(c->next);
+
+ if ((str = iscmd(s, "expect ")) != NULL) {
+ c->type = CMD_EXPECT;
+ c->str = str;
+ } else if ((str = iscmd(s, "send ")) != NULL) {
+ c->type = CMD_SEND;
+ c->str = str;
+ } else if ((str = iscmd(s, "password ")) != NULL) {
+ c->type = CMD_PASSWORD;
+ c->str = str;
+ } else
+ errx(1, "Invalid command on line %d: %s", lineno, s);
+ }
+
+ fclose(cmd);
+}
+
+
+/*
+ *
+ */
+
+static int
+eval_parent(pid_t pid)
+{
+ struct command *c;
+ char in;
+ size_t len = 0;
+ ssize_t sret;
+
+ for (c = commands; c != NULL; c = c->next) {
+ switch(c->type) {
+ case CMD_EXPECT:
+ if (verbose)
+ printf("[expecting %s]", c->str);
+ len = 0;
+ alarm(timeout);
+ while((sret = read(master, &in, sizeof(in))) > 0) {
+ alarm(timeout);
+ printf("%c", in);
+ if (c->str[len] != in) {
+ len = 0;
+ continue;
+ }
+ len++;
+ if (c->str[len] == '\0')
+ break;
+ }
+ alarm(0);
+ if (alarmset == SIGALRM)
+ errx(1, "timeout waiting for %s (line %u)",
+ c->str, c->lineno);
+ else if (alarmset)
+ errx(1, "got a signal %d waiting for %s (line %u)",
+ alarmset, c->str, c->lineno);
+ if (sret <= 0)
+ errx(1, "end command while waiting for %s (line %u)",
+ c->str, c->lineno);
+ break;
+ case CMD_SEND:
+ case CMD_PASSWORD: {
+ size_t i = 0;
+ const char *msg = (c->type == CMD_PASSWORD) ? "****" : c->str;
+
+ if (verbose)
+ printf("[send %s]", msg);
+
+ len = strlen(c->str);
+
+ while (i < len) {
+ if (c->str[i] == '\\' && i < len - 1) {
+ char ctrl;
+ i++;
+ switch(c->str[i]) {
+ case 'n': ctrl = '\n'; break;
+ case 'r': ctrl = '\r'; break;
+ case 't': ctrl = '\t'; break;
+ default:
+ errx(1, "unknown control char %c (line %u)",
+ c->str[i], c->lineno);
+ }
+ if (net_write(master, &ctrl, 1) != 1)
+ errx(1, "command refused input (line %u)", c->lineno);
+ } else {
+ if (net_write(master, &c->str[i], 1) != 1)
+ errx(1, "command refused input (line %u)", c->lineno);
+ }
+ i++;
+ }
+ break;
+ }
+ default:
+ abort();
+ }
+ }
+ while(read(master, &in, sizeof(in)) > 0)
+ printf("%c", in);
+
+ if (verbose)
+ printf("[end of program]\n");
+
+ /*
+ * Fetch status from child
+ */
+ {
+ int ret, status;
+
+ ret = waitpid(pid, &status, 0);
+ if (ret == -1)
+ err(1, "waitpid");
+ if (WIFEXITED(status) && WEXITSTATUS(status))
+ return WEXITSTATUS(status);
+ else if (WIFSIGNALED(status)) {
+ printf("killed by signal: %d\n", WTERMSIG(status));
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+ *
+ */
+
+static struct getargs args[] = {
+ { "timeout", 't', arg_integer, &timeout, "timout", "seconds" },
+ { "verbose", 'v', arg_counter, &verbose, "verbose debugging" },
+ { "version", 0, arg_flag, &version_flag, "print version" },
+ { "help", 0, arg_flag, &help_flag, NULL }
+};
+
+static void
+usage(int ret)
+{
+ arg_printusage (args, sizeof(args)/sizeof(*args), NULL, "infile command..");
+ exit (ret);
+}
+
+int
+main(int argc, char **argv)
+{
+ int optidx = 0;
+ pid_t pid;
+
+ setprogname(argv[0]);
+
+ if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
+ usage(1);
+
+ if (help_flag)
+ usage (0);
+
+ if (version_flag) {
+ fprintf (stderr, "%s from %s-%s\n", getprogname(), PACKAGE, VERSION);
+ return 0;
+ }
+
+ argv += optidx;
+ argc -= optidx;
+
+ if (argc < 2)
+ usage(1);
+
+ parse_configuration(argv[0]);
+
+ argv += 1;
+ argc -= 1;
+
+ open_pty();
+
+ pid = fork();
+ switch (pid) {
+ case -1:
+ err(1, "Failed to fork");
+ case 0:
+
+ if(setsid()<0)
+ err(1, "setsid");
+
+ dup2(slave, STDIN_FILENO);
+ dup2(slave, STDOUT_FILENO);
+ dup2(slave, STDERR_FILENO);
+ closefrom(STDERR_FILENO + 1);
+
+ execvp(argv[0], argv); /* add NULL to end of array ? */
+ err(1, "Failed to exec: %s", argv[0]);
+ default:
+ close(slave);
+ {
+ struct sigaction sa;
+
+ sa.sa_handler = caught_signal;
+ sa.sa_flags = 0;
+ sigemptyset (&sa.sa_mask);
+
+ sigaction(SIGALRM, &sa, NULL);
+ }
+
+ return eval_parent(pid);
+ }
+}
diff --git a/source4/heimdal/lib/roken/roken-common.h b/source4/heimdal/lib/roken/roken-common.h
new file mode 100644
index 0000000000..1d341258fe
--- /dev/null
+++ b/source4/heimdal/lib/roken/roken-common.h
@@ -0,0 +1,418 @@
+/*
+ * Copyright (c) 1995 - 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef __ROKEN_COMMON_H__
+#define __ROKEN_COMMON_H__
+
+#ifndef ROKEN_LIB_FUNCTION
+#ifdef _WIN32
+#define ROKEN_LIB_FUNCTION _stdcall
+#else
+#define ROKEN_LIB_FUNCTION
+#endif
+#endif
+
+#ifdef __cplusplus
+#define ROKEN_CPP_START extern "C" {
+#define ROKEN_CPP_END }
+#else
+#define ROKEN_CPP_START
+#define ROKEN_CPP_END
+#endif
+
+#ifndef INADDR_NONE
+#define INADDR_NONE 0xffffffff
+#endif
+
+#ifndef INADDR_LOOPBACK
+#define INADDR_LOOPBACK 0x7f000001
+#endif
+
+#ifndef SOMAXCONN
+#define SOMAXCONN 5
+#endif
+
+#ifndef STDIN_FILENO
+#define STDIN_FILENO 0
+#endif
+
+#ifndef STDOUT_FILENO
+#define STDOUT_FILENO 1
+#endif
+
+#ifndef STDERR_FILENO
+#define STDERR_FILENO 2
+#endif
+
+#ifndef max
+#define max(a,b) (((a)>(b))?(a):(b))
+#endif
+
+#ifndef min
+#define min(a,b) (((a)<(b))?(a):(b))
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef LOG_DAEMON
+#define openlog(id,option,facility) openlog((id),(option))
+#define LOG_DAEMON 0
+#endif
+#ifndef LOG_ODELAY
+#define LOG_ODELAY 0
+#endif
+#ifndef LOG_NDELAY
+#define LOG_NDELAY 0x08
+#endif
+#ifndef LOG_CONS
+#define LOG_CONS 0
+#endif
+#ifndef LOG_AUTH
+#define LOG_AUTH 0
+#endif
+#ifndef LOG_AUTHPRIV
+#define LOG_AUTHPRIV LOG_AUTH
+#endif
+
+#ifndef F_OK
+#define F_OK 0
+#endif
+
+#ifndef O_ACCMODE
+#define O_ACCMODE 003
+#endif
+
+#ifndef _PATH_DEV
+#define _PATH_DEV "/dev/"
+#endif
+
+#ifndef _PATH_DEVNULL
+#define _PATH_DEVNULL "/dev/null"
+#endif
+
+#ifndef _PATH_HEQUIV
+#define _PATH_HEQUIV "/etc/hosts.equiv"
+#endif
+
+#ifndef _PATH_VARRUN
+#define _PATH_VARRUN "/var/run/"
+#endif
+
+#ifndef _PATH_BSHELL
+#define _PATH_BSHELL "/bin/sh"
+#endif
+
+#ifndef MAXPATHLEN
+#define MAXPATHLEN (1024+4)
+#endif
+
+#ifndef SIG_ERR
+#define SIG_ERR ((RETSIGTYPE (*)(int))-1)
+#endif
+
+/*
+ * error code for getipnodeby{name,addr}
+ */
+
+#ifndef HOST_NOT_FOUND
+#define HOST_NOT_FOUND 1
+#endif
+
+#ifndef TRY_AGAIN
+#define TRY_AGAIN 2
+#endif
+
+#ifndef NO_RECOVERY
+#define NO_RECOVERY 3
+#endif
+
+#ifndef NO_DATA
+#define NO_DATA 4
+#endif
+
+#ifndef NO_ADDRESS
+#define NO_ADDRESS NO_DATA
+#endif
+
+/*
+ * error code for getaddrinfo
+ */
+
+#ifndef EAI_NOERROR
+#define EAI_NOERROR 0 /* no error */
+#endif
+
+#ifndef EAI_NONAME
+
+#define EAI_ADDRFAMILY 1 /* address family for nodename not supported */
+#define EAI_AGAIN 2 /* temporary failure in name resolution */
+#define EAI_BADFLAGS 3 /* invalid value for ai_flags */
+#define EAI_FAIL 4 /* non-recoverable failure in name resolution */
+#define EAI_FAMILY 5 /* ai_family not supported */
+#define EAI_MEMORY 6 /* memory allocation failure */
+#define EAI_NODATA 7 /* no address associated with nodename */
+#define EAI_NONAME 8 /* nodename nor servname provided, or not known */
+#define EAI_SERVICE 9 /* servname not supported for ai_socktype */
+#define EAI_SOCKTYPE 10 /* ai_socktype not supported */
+#define EAI_SYSTEM 11 /* system error returned in errno */
+
+#endif /* EAI_NONAME */
+
+/* flags for getaddrinfo() */
+
+#ifndef AI_PASSIVE
+#define AI_PASSIVE 0x01
+#define AI_CANONNAME 0x02
+#endif /* AI_PASSIVE */
+
+#ifndef AI_NUMERICHOST
+#define AI_NUMERICHOST 0x04
+#endif
+
+/* flags for getnameinfo() */
+
+#ifndef NI_DGRAM
+#define NI_DGRAM 0x01
+#define NI_NAMEREQD 0x02
+#define NI_NOFQDN 0x04
+#define NI_NUMERICHOST 0x08
+#define NI_NUMERICSERV 0x10
+#endif
+
+/*
+ * constants for getnameinfo
+ */
+
+#ifndef NI_MAXHOST
+#define NI_MAXHOST 1025
+#define NI_MAXSERV 32
+#endif
+
+/*
+ * constants for inet_ntop
+ */
+
+#ifndef INET_ADDRSTRLEN
+#define INET_ADDRSTRLEN 16
+#endif
+
+#ifndef INET6_ADDRSTRLEN
+#define INET6_ADDRSTRLEN 46
+#endif
+
+/*
+ * for shutdown(2)
+ */
+
+#ifndef SHUT_RD
+#define SHUT_RD 0
+#endif
+
+#ifndef SHUT_WR
+#define SHUT_WR 1
+#endif
+
+#ifndef SHUT_RDWR
+#define SHUT_RDWR 2
+#endif
+
+#ifndef HAVE___ATTRIBUTE__
+#define __attribute__(x)
+#endif
+
+ROKEN_CPP_START
+
+#ifndef IRIX4 /* fix for compiler bug */
+#ifdef RETSIGTYPE
+typedef RETSIGTYPE (*SigAction)(int);
+SigAction signal(int iSig, SigAction pAction); /* BSD compatible */
+#endif
+#endif
+
+int ROKEN_LIB_FUNCTION
+simple_execve(const char*, char*const[], char*const[]);
+
+int ROKEN_LIB_FUNCTION
+simple_execve_timed(const char *, char *const[],
+ char *const [], time_t (*)(void *),
+ void *, time_t);
+int ROKEN_LIB_FUNCTION
+simple_execvp(const char*, char *const[]);
+
+int ROKEN_LIB_FUNCTION
+simple_execvp_timed(const char *, char *const[],
+ time_t (*)(void *), void *, time_t);
+int ROKEN_LIB_FUNCTION
+simple_execlp(const char*, ...);
+
+int ROKEN_LIB_FUNCTION
+simple_execle(const char*, ...);
+
+int ROKEN_LIB_FUNCTION
+simple_execl(const char *file, ...);
+
+int ROKEN_LIB_FUNCTION
+wait_for_process(pid_t);
+
+int ROKEN_LIB_FUNCTION
+wait_for_process_timed(pid_t, time_t (*)(void *),
+ void *, time_t);
+int ROKEN_LIB_FUNCTION
+pipe_execv(FILE**, FILE**, FILE**, const char*, ...);
+
+void ROKEN_LIB_FUNCTION
+print_version(const char *);
+
+ssize_t ROKEN_LIB_FUNCTION
+eread (int fd, void *buf, size_t nbytes);
+
+ssize_t ROKEN_LIB_FUNCTION
+ewrite (int fd, const void *buf, size_t nbytes);
+
+struct hostent;
+
+const char * ROKEN_LIB_FUNCTION
+hostent_find_fqdn (const struct hostent *);
+
+void ROKEN_LIB_FUNCTION
+esetenv(const char *, const char *, int);
+
+void ROKEN_LIB_FUNCTION
+socket_set_address_and_port (struct sockaddr *, const void *, int);
+
+size_t ROKEN_LIB_FUNCTION
+socket_addr_size (const struct sockaddr *);
+
+void ROKEN_LIB_FUNCTION
+socket_set_any (struct sockaddr *, int);
+
+size_t ROKEN_LIB_FUNCTION
+socket_sockaddr_size (const struct sockaddr *);
+
+void * ROKEN_LIB_FUNCTION
+socket_get_address (struct sockaddr *);
+
+int ROKEN_LIB_FUNCTION
+socket_get_port (const struct sockaddr *);
+
+void ROKEN_LIB_FUNCTION
+socket_set_port (struct sockaddr *, int);
+
+void ROKEN_LIB_FUNCTION
+socket_set_portrange (int, int, int);
+
+void ROKEN_LIB_FUNCTION
+socket_set_debug (int);
+
+void ROKEN_LIB_FUNCTION
+socket_set_tos (int, int);
+
+void ROKEN_LIB_FUNCTION
+socket_set_reuseaddr (int, int);
+
+void ROKEN_LIB_FUNCTION
+socket_set_ipv6only (int, int);
+
+char ** ROKEN_LIB_FUNCTION
+vstrcollect(va_list *ap);
+
+char ** ROKEN_LIB_FUNCTION
+strcollect(char *first, ...);
+
+void ROKEN_LIB_FUNCTION
+timevalfix(struct timeval *t1);
+
+void ROKEN_LIB_FUNCTION
+timevaladd(struct timeval *t1, const struct timeval *t2);
+
+void ROKEN_LIB_FUNCTION
+timevalsub(struct timeval *t1, const struct timeval *t2);
+
+char *ROKEN_LIB_FUNCTION
+pid_file_write (const char *progname);
+
+void ROKEN_LIB_FUNCTION
+pid_file_delete (char **);
+
+int ROKEN_LIB_FUNCTION
+read_environment(const char *file, char ***env);
+
+void ROKEN_LIB_FUNCTION
+free_environment(char **);
+
+void ROKEN_LIB_FUNCTION
+warnerr(int doerrno, const char *fmt, va_list ap)
+ __attribute__ ((format (printf, 2, 0)));
+
+void * ROKEN_LIB_FUNCTION
+rk_realloc(void *, size_t);
+
+struct rk_strpool;
+
+char * ROKEN_LIB_FUNCTION
+rk_strpoolcollect(struct rk_strpool *);
+
+struct rk_strpool * ROKEN_LIB_FUNCTION
+rk_strpoolprintf(struct rk_strpool *, const char *, ...)
+ __attribute__ ((format (printf, 2, 3)));
+
+void ROKEN_LIB_FUNCTION
+rk_strpoolfree(struct rk_strpool *);
+
+void ROKEN_LIB_FUNCTION
+rk_dumpdata (const char *, const void *, size_t);
+
+int ROKEN_LIB_FUNCTION
+rk_undumpdata (const char *, void **, size_t *);
+
+void ROKEN_LIB_FUNCTION
+rk_xfree (void *);
+
+void ROKEN_LIB_FUNCTION
+rk_cloexec(int);
+
+void ROKEN_LIB_FUNCTION
+rk_cloexec_file(FILE *);
+
+
+ROKEN_CPP_END
+
+#endif /* __ROKEN_COMMON_H__ */
diff --git a/source4/heimdal/lib/roken/roken.h.in b/source4/heimdal/lib/roken/roken.h.in
new file mode 100644
index 0000000000..d71bee7b46
--- /dev/null
+++ b/source4/heimdal/lib/roken/roken.h.in
@@ -0,0 +1,702 @@
+/* -*- C -*- */
+/*
+ * Copyright (c) 1995-2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#include <string.h>
+#include <signal.h>
+
+#ifdef _AIX
+struct ether_addr;
+struct sockaddr_dl;
+#endif
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_BITYPES_H
+#include <sys/bitypes.h>
+#endif
+#ifdef HAVE_BIND_BITYPES_H
+#include <bind/bitypes.h>
+#endif
+#ifdef HAVE_NETINET_IN6_MACHTYPES_H
+#include <netinet/in6_machtypes.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#endif
+#ifdef HAVE_GRP_H
+#include <grp.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETINET_IN6_H
+#include <netinet/in6.h>
+#endif
+#ifdef HAVE_NETINET6_IN6_H
+#include <netinet6/in6.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef HAVE_ARPA_NAMESER_H
+#include <arpa/nameser.h>
+#endif
+#ifdef HAVE_RESOLV_H
+#include <resolv.h>
+#endif
+#ifdef HAVE_SYSLOG_H
+#include <syslog.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <err.h>
+#ifdef HAVE_TERMIOS_H
+#include <termios.h>
+#endif
+#if defined(HAVE_SYS_IOCTL_H) && SunOS != 40
+#include <sys/ioctl.h>
+#endif
+#ifdef TIME_WITH_SYS_TIME
+#include <sys/time.h>
+#include <time.h>
+#elif defined(HAVE_SYS_TIME_H)
+#include <sys/time.h>
+#else
+#include <time.h>
+#endif
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+
+#ifdef HAVE_PATHS_H
+#include <paths.h>
+#endif
+
+#ifndef HAVE_SSIZE_T
+typedef int ssize_t;
+#endif
+
+#include <roken-common.h>
+
+ROKEN_CPP_START
+
+#ifdef HAVE_UINTPTR_T
+#define rk_UNCONST(x) ((void *)(uintptr_t)(const void *)(x))
+#else
+#define rk_UNCONST(x) ((void *)(unsigned long)(const void *)(x))
+#endif
+
+#if !defined(HAVE_SETSID) && defined(HAVE__SETSID)
+#define setsid _setsid
+#endif
+
+#ifndef HAVE_PUTENV
+int ROKEN_LIB_FUNCTION putenv(const char *);
+#endif
+
+#if !defined(HAVE_SETENV) || defined(NEED_SETENV_PROTO)
+int ROKEN_LIB_FUNCTION setenv(const char *, const char *, int);
+#endif
+
+#if !defined(HAVE_UNSETENV) || defined(NEED_UNSETENV_PROTO)
+void ROKEN_LIB_FUNCTION unsetenv(const char *);
+#endif
+
+#if !defined(HAVE_GETUSERSHELL) || defined(NEED_GETUSERSHELL_PROTO)
+char * ROKEN_LIB_FUNCTION getusershell(void);
+void ROKEN_LIB_FUNCTION endusershell(void);
+#endif
+
+#if !defined(HAVE_SNPRINTF) || defined(NEED_SNPRINTF_PROTO)
+int ROKEN_LIB_FUNCTION snprintf (char *, size_t, const char *, ...)
+ __attribute__ ((format (printf, 3, 4)));
+#endif
+
+#if !defined(HAVE_VSNPRINTF) || defined(NEED_VSNPRINTF_PROTO)
+int ROKEN_LIB_FUNCTION
+ vsnprintf (char *, size_t, const char *, va_list)
+ __attribute__((format (printf, 3, 0)));
+#endif
+
+#if !defined(HAVE_ASPRINTF) || defined(NEED_ASPRINTF_PROTO)
+int ROKEN_LIB_FUNCTION
+ asprintf (char **, const char *, ...)
+ __attribute__ ((format (printf, 2, 3)));
+#endif
+
+#if !defined(HAVE_VASPRINTF) || defined(NEED_VASPRINTF_PROTO)
+int ROKEN_LIB_FUNCTION
+ vasprintf (char **, const char *, va_list)
+ __attribute__((format (printf, 2, 0)));
+#endif
+
+#if !defined(HAVE_ASNPRINTF) || defined(NEED_ASNPRINTF_PROTO)
+int ROKEN_LIB_FUNCTION
+ asnprintf (char **, size_t, const char *, ...)
+ __attribute__ ((format (printf, 3, 4)));
+#endif
+
+#if !defined(HAVE_VASNPRINTF) || defined(NEED_VASNPRINTF_PROTO)
+int ROKEN_LIB_FUNCTION
+ vasnprintf (char **, size_t, const char *, va_list)
+ __attribute__((format (printf, 3, 0)));
+#endif
+
+#ifndef HAVE_STRDUP
+char * ROKEN_LIB_FUNCTION strdup(const char *);
+#endif
+
+#if !defined(HAVE_STRNDUP) || defined(NEED_STRNDUP_PROTO)
+char * ROKEN_LIB_FUNCTION strndup(const char *, size_t);
+#endif
+
+#ifndef HAVE_STRLWR
+char * ROKEN_LIB_FUNCTION strlwr(char *);
+#endif
+
+#ifndef HAVE_STRNLEN
+size_t ROKEN_LIB_FUNCTION strnlen(const char*, size_t);
+#endif
+
+#if !defined(HAVE_STRSEP) || defined(NEED_STRSEP_PROTO)
+char * ROKEN_LIB_FUNCTION strsep(char**, const char*);
+#endif
+
+#if !defined(HAVE_STRSEP_COPY) || defined(NEED_STRSEP_COPY_PROTO)
+ssize_t ROKEN_LIB_FUNCTION strsep_copy(const char**, const char*, char*, size_t);
+#endif
+
+#ifndef HAVE_STRCASECMP
+int ROKEN_LIB_FUNCTION strcasecmp(const char *, const char *);
+#endif
+
+#ifdef NEED_FCLOSE_PROTO
+int ROKEN_LIB_FUNCTION fclose(FILE *);
+#endif
+
+#ifdef NEED_STRTOK_R_PROTO
+char * ROKEN_LIB_FUNCTION strtok_r(char *, const char *, char **);
+#endif
+
+#ifndef HAVE_STRUPR
+char * ROKEN_LIB_FUNCTION strupr(char *);
+#endif
+
+#ifndef HAVE_STRLCPY
+size_t ROKEN_LIB_FUNCTION strlcpy (char *, const char *, size_t);
+#endif
+
+#ifndef HAVE_STRLCAT
+size_t ROKEN_LIB_FUNCTION strlcat (char *, const char *, size_t);
+#endif
+
+#ifndef HAVE_GETDTABLESIZE
+int ROKEN_LIB_FUNCTION getdtablesize(void);
+#endif
+
+#if !defined(HAVE_STRERROR) && !defined(strerror)
+char * ROKEN_LIB_FUNCTION strerror(int);
+#endif
+
+#if !defined(HAVE_HSTRERROR) || defined(NEED_HSTRERROR_PROTO)
+/* This causes a fatal error under Psoriasis */
+#if !(defined(SunOS) && (SunOS >= 50))
+const char * ROKEN_LIB_FUNCTION hstrerror(int);
+#endif
+#endif
+
+#if !HAVE_DECL_H_ERRNO
+extern int h_errno;
+#endif
+
+#if !defined(HAVE_INET_ATON) || defined(NEED_INET_ATON_PROTO)
+int ROKEN_LIB_FUNCTION inet_aton(const char *, struct in_addr *);
+#endif
+
+#ifndef HAVE_INET_NTOP
+const char * ROKEN_LIB_FUNCTION
+inet_ntop(int af, const void *src, char *dst, size_t size);
+#endif
+
+#ifndef HAVE_INET_PTON
+int ROKEN_LIB_FUNCTION
+inet_pton(int, const char *, void *);
+#endif
+
+#if !defined(HAVE_GETCWD)
+char* ROKEN_LIB_FUNCTION getcwd(char *, size_t);
+#endif
+
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+struct passwd * ROKEN_LIB_FUNCTION k_getpwnam (const char *);
+struct passwd * ROKEN_LIB_FUNCTION k_getpwuid (uid_t);
+#endif
+
+const char * ROKEN_LIB_FUNCTION get_default_username (void);
+
+#ifndef HAVE_SETEUID
+int ROKEN_LIB_FUNCTION seteuid(uid_t);
+#endif
+
+#ifndef HAVE_SETEGID
+int ROKEN_LIB_FUNCTION setegid(gid_t);
+#endif
+
+#ifndef HAVE_LSTAT
+int ROKEN_LIB_FUNCTION lstat(const char *, struct stat *);
+#endif
+
+#if !defined(HAVE_MKSTEMP) || defined(NEED_MKSTEMP_PROTO)
+int ROKEN_LIB_FUNCTION mkstemp(char *);
+#endif
+
+#ifndef HAVE_CGETENT
+int ROKEN_LIB_FUNCTION cgetent(char **, char **, const char *);
+int ROKEN_LIB_FUNCTION cgetstr(char *, const char *, char **);
+#endif
+
+#ifndef HAVE_INITGROUPS
+int ROKEN_LIB_FUNCTION initgroups(const char *, gid_t);
+#endif
+
+#ifndef HAVE_FCHOWN
+int ROKEN_LIB_FUNCTION fchown(int, uid_t, gid_t);
+#endif
+
+#if !defined(HAVE_DAEMON) || defined(NEED_DAEMON_PROTO)
+int ROKEN_LIB_FUNCTION daemon(int, int);
+#endif
+
+#ifndef HAVE_INNETGR
+int ROKEN_LIB_FUNCTION innetgr(const char *, const char *,
+ const char *, const char *);
+#endif
+
+#ifndef HAVE_CHOWN
+int ROKEN_LIB_FUNCTION chown(const char *, uid_t, gid_t);
+#endif
+
+#ifndef HAVE_RCMD
+int ROKEN_LIB_FUNCTION
+ rcmd(char **, unsigned short, const char *,
+ const char *, const char *, int *);
+#endif
+
+#if !defined(HAVE_INNETGR) || defined(NEED_INNETGR_PROTO)
+int ROKEN_LIB_FUNCTION innetgr(const char*, const char*,
+ const char*, const char*);
+#endif
+
+#ifndef HAVE_IRUSEROK
+int ROKEN_LIB_FUNCTION iruserok(unsigned, int,
+ const char *, const char *);
+#endif
+
+#if !defined(HAVE_GETHOSTNAME) || defined(NEED_GETHOSTNAME_PROTO)
+int ROKEN_LIB_FUNCTION gethostname(char *, int);
+#endif
+
+#ifndef HAVE_WRITEV
+ssize_t ROKEN_LIB_FUNCTION
+writev(int, const struct iovec *, int);
+#endif
+
+#ifndef HAVE_READV
+ssize_t ROKEN_LIB_FUNCTION
+readv(int, const struct iovec *, int);
+#endif
+
+#ifndef HAVE_MKSTEMP
+int ROKEN_LIB_FUNCTION
+mkstemp(char *);
+#endif
+
+#ifndef HAVE_PIDFILE
+void ROKEN_LIB_FUNCTION pidfile (const char*);
+#endif
+
+#ifndef HAVE_BSWAP32
+unsigned int ROKEN_LIB_FUNCTION bswap32(unsigned int);
+#endif
+
+#ifndef HAVE_BSWAP16
+unsigned short ROKEN_LIB_FUNCTION bswap16(unsigned short);
+#endif
+
+#ifndef HAVE_FLOCK
+#ifndef LOCK_SH
+#define LOCK_SH 1 /* Shared lock */
+#endif
+#ifndef LOCK_EX
+#define LOCK_EX 2 /* Exclusive lock */
+#endif
+#ifndef LOCK_NB
+#define LOCK_NB 4 /* Don't block when locking */
+#endif
+#ifndef LOCK_UN
+#define LOCK_UN 8 /* Unlock */
+#endif
+
+int flock(int fd, int operation);
+#endif /* HAVE_FLOCK */
+
+time_t ROKEN_LIB_FUNCTION tm2time (struct tm, int);
+
+int ROKEN_LIB_FUNCTION unix_verify_user(char *, char *);
+
+int ROKEN_LIB_FUNCTION roken_concat (char *, size_t, ...);
+
+size_t ROKEN_LIB_FUNCTION roken_mconcat (char **, size_t, ...);
+
+int ROKEN_LIB_FUNCTION roken_vconcat (char *, size_t, va_list);
+
+size_t ROKEN_LIB_FUNCTION
+ roken_vmconcat (char **, size_t, va_list);
+
+ssize_t ROKEN_LIB_FUNCTION net_write (int, const void *, size_t);
+
+ssize_t ROKEN_LIB_FUNCTION net_read (int, void *, size_t);
+
+int ROKEN_LIB_FUNCTION issuid(void);
+
+#ifndef HAVE_STRUCT_WINSIZE
+struct winsize {
+ unsigned short ws_row, ws_col;
+ unsigned short ws_xpixel, ws_ypixel;
+};
+#endif
+
+int ROKEN_LIB_FUNCTION get_window_size(int fd, struct winsize *);
+
+#ifndef HAVE_VSYSLOG
+void ROKEN_LIB_FUNCTION vsyslog(int, const char *, va_list);
+#endif
+
+#if !HAVE_DECL_OPTARG
+extern char *optarg;
+#endif
+#if !HAVE_DECL_OPTIND
+extern int optind;
+#endif
+#if !HAVE_DECL_OPTERR
+extern int opterr;
+#endif
+
+#ifndef HAVE_GETIPNODEBYNAME
+struct hostent * ROKEN_LIB_FUNCTION
+getipnodebyname (const char *, int, int, int *);
+#endif
+
+#ifndef HAVE_GETIPNODEBYADDR
+struct hostent * ROKEN_LIB_FUNCTION
+getipnodebyaddr (const void *, size_t, int, int *);
+#endif
+
+#ifndef HAVE_FREEHOSTENT
+void ROKEN_LIB_FUNCTION
+freehostent (struct hostent *);
+#endif
+
+#ifndef HAVE_COPYHOSTENT
+struct hostent * ROKEN_LIB_FUNCTION
+copyhostent (const struct hostent *);
+#endif
+
+#ifndef HAVE_SOCKLEN_T
+typedef int socklen_t;
+#endif
+
+#ifndef HAVE_STRUCT_SOCKADDR_STORAGE
+
+#ifndef HAVE_SA_FAMILY_T
+typedef unsigned short sa_family_t;
+#endif
+
+#ifdef HAVE_IPV6
+#define _SS_MAXSIZE sizeof(struct sockaddr_in6)
+#else
+#define _SS_MAXSIZE sizeof(struct sockaddr_in)
+#endif
+
+#define _SS_ALIGNSIZE sizeof(unsigned long)
+
+#if HAVE_STRUCT_SOCKADDR_SA_LEN
+
+typedef unsigned char roken_sa_family_t;
+
+#define _SS_PAD1SIZE ((2 * _SS_ALIGNSIZE - sizeof (roken_sa_family_t) - sizeof(unsigned char)) % _SS_ALIGNSIZE)
+#define _SS_PAD2SIZE (_SS_MAXSIZE - (sizeof (roken_sa_family_t) + sizeof(unsigned char) + _SS_PAD1SIZE + _SS_ALIGNSIZE))
+
+struct sockaddr_storage {
+ unsigned char ss_len;
+ roken_sa_family_t ss_family;
+ char __ss_pad1[_SS_PAD1SIZE];
+ unsigned long __ss_align[_SS_PAD2SIZE / sizeof(unsigned long) + 1];
+};
+
+#else /* !HAVE_STRUCT_SOCKADDR_SA_LEN */
+
+typedef unsigned short roken_sa_family_t;
+
+#define _SS_PAD1SIZE ((2 * _SS_ALIGNSIZE - sizeof (roken_sa_family_t)) % _SS_ALIGNSIZE)
+#define _SS_PAD2SIZE (_SS_MAXSIZE - (sizeof (roken_sa_family_t) + _SS_PAD1SIZE + _SS_ALIGNSIZE))
+
+struct sockaddr_storage {
+ roken_sa_family_t ss_family;
+ char __ss_pad1[_SS_PAD1SIZE];
+ unsigned long __ss_align[_SS_PAD2SIZE / sizeof(unsigned long) + 1];
+};
+
+#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
+
+#endif /* HAVE_STRUCT_SOCKADDR_STORAGE */
+
+#ifndef HAVE_STRUCT_ADDRINFO
+struct addrinfo {
+ int ai_flags;
+ int ai_family;
+ int ai_socktype;
+ int ai_protocol;
+ size_t ai_addrlen;
+ char *ai_canonname;
+ struct sockaddr *ai_addr;
+ struct addrinfo *ai_next;
+};
+#endif
+
+#ifndef HAVE_GETADDRINFO
+int ROKEN_LIB_FUNCTION
+getaddrinfo(const char *,
+ const char *,
+ const struct addrinfo *,
+ struct addrinfo **);
+#endif
+
+#ifndef HAVE_GETNAMEINFO
+int ROKEN_LIB_FUNCTION
+getnameinfo(const struct sockaddr *, socklen_t,
+ char *, size_t,
+ char *, size_t,
+ int);
+#endif
+
+#ifndef HAVE_FREEADDRINFO
+void ROKEN_LIB_FUNCTION
+freeaddrinfo(struct addrinfo *);
+#endif
+
+#ifndef HAVE_GAI_STRERROR
+const char * ROKEN_LIB_FUNCTION
+gai_strerror(int);
+#endif
+
+int ROKEN_LIB_FUNCTION
+getnameinfo_verified(const struct sockaddr *, socklen_t,
+ char *, size_t,
+ char *, size_t,
+ int);
+
+int ROKEN_LIB_FUNCTION
+roken_getaddrinfo_hostspec(const char *, int, struct addrinfo **);
+int ROKEN_LIB_FUNCTION
+roken_getaddrinfo_hostspec2(const char *, int, int, struct addrinfo **);
+
+#ifndef HAVE_STRFTIME
+size_t ROKEN_LIB_FUNCTION
+strftime (char *, size_t, const char *, const struct tm *);
+#endif
+
+#ifndef HAVE_STRPTIME
+char * ROKEN_LIB_FUNCTION
+strptime (const char *, const char *, struct tm *);
+#endif
+
+#ifndef HAVE_EMALLOC
+void * ROKEN_LIB_FUNCTION emalloc (size_t);
+#endif
+#ifndef HAVE_ECALLOC
+void * ROKEN_LIB_FUNCTION ecalloc(size_t, size_t);
+#endif
+#ifndef HAVE_EREALLOC
+void * ROKEN_LIB_FUNCTION erealloc (void *, size_t);
+#endif
+#ifndef HAVE_ESTRDUP
+char * ROKEN_LIB_FUNCTION estrdup (const char *);
+#endif
+
+/*
+ * kludges and such
+ */
+
+#if 1
+int ROKEN_LIB_FUNCTION
+roken_gethostby_setup(const char*, const char*);
+struct hostent* ROKEN_LIB_FUNCTION
+roken_gethostbyname(const char*);
+struct hostent* ROKEN_LIB_FUNCTION
+roken_gethostbyaddr(const void*, size_t, int);
+#else
+#ifdef GETHOSTBYNAME_PROTO_COMPATIBLE
+#define roken_gethostbyname(x) gethostbyname(x)
+#else
+#define roken_gethostbyname(x) gethostbyname((char *)x)
+#endif
+
+#ifdef GETHOSTBYADDR_PROTO_COMPATIBLE
+#define roken_gethostbyaddr(a, l, t) gethostbyaddr(a, l, t)
+#else
+#define roken_gethostbyaddr(a, l, t) gethostbyaddr((char *)a, l, t)
+#endif
+#endif
+
+#ifdef GETSERVBYNAME_PROTO_COMPATIBLE
+#define roken_getservbyname(x,y) getservbyname(x,y)
+#else
+#define roken_getservbyname(x,y) getservbyname((char *)x, (char *)y)
+#endif
+
+#ifdef OPENLOG_PROTO_COMPATIBLE
+#define roken_openlog(a,b,c) openlog(a,b,c)
+#else
+#define roken_openlog(a,b,c) openlog((char *)a,b,c)
+#endif
+
+#ifdef GETSOCKNAME_PROTO_COMPATIBLE
+#define roken_getsockname(a,b,c) getsockname(a,b,c)
+#else
+#define roken_getsockname(a,b,c) getsockname(a, b, (void*)c)
+#endif
+
+#ifndef HAVE_SETPROGNAME
+void ROKEN_LIB_FUNCTION setprogname(const char *);
+#endif
+
+#ifndef HAVE_GETPROGNAME
+const char * ROKEN_LIB_FUNCTION getprogname(void);
+#endif
+
+#if !defined(HAVE_SETPROGNAME) && !defined(HAVE_GETPROGNAME) && !HAVE_DECL___PROGNAME
+extern const char *__progname;
+#endif
+
+void ROKEN_LIB_FUNCTION mini_inetd_addrinfo (struct addrinfo*);
+void ROKEN_LIB_FUNCTION mini_inetd (int);
+
+#ifndef HAVE_LOCALTIME_R
+struct tm * ROKEN_LIB_FUNCTION
+localtime_r(const time_t *, struct tm *);
+#endif
+
+#if !defined(HAVE_STRSVIS) || defined(NEED_STRSVIS_PROTO)
+int ROKEN_LIB_FUNCTION
+strsvis(char *, const char *, int, const char *);
+#endif
+
+#if !defined(HAVE_STRUNVIS) || defined(NEED_STRUNVIS_PROTO)
+int ROKEN_LIB_FUNCTION
+strunvis(char *, const char *);
+#endif
+
+#if !defined(HAVE_STRVIS) || defined(NEED_STRVIS_PROTO)
+int ROKEN_LIB_FUNCTION
+strvis(char *, const char *, int);
+#endif
+
+#if !defined(HAVE_STRVISX) || defined(NEED_STRVISX_PROTO)
+int ROKEN_LIB_FUNCTION
+strvisx(char *, const char *, size_t, int);
+#endif
+
+#if !defined(HAVE_SVIS) || defined(NEED_SVIS_PROTO)
+char * ROKEN_LIB_FUNCTION
+svis(char *, int, int, int, const char *);
+#endif
+
+#if !defined(HAVE_UNVIS) || defined(NEED_UNVIS_PROTO)
+int ROKEN_LIB_FUNCTION
+unvis(char *, int, int *, int);
+#endif
+
+#if !defined(HAVE_VIS) || defined(NEED_VIS_PROTO)
+char * ROKEN_LIB_FUNCTION
+vis(char *, int, int, int);
+#endif
+
+#if !defined(HAVE_CLOSEFROM)
+int ROKEN_LIB_FUNCTION
+closefrom(int);
+#endif
+
+#if !defined(HAVE_TIMEGM)
+#define timegm rk_timegm
+time_t ROKEN_LIB_FUNCTION
+rk_timegm(struct tm *tm);
+#endif
+
+#ifdef SOCKET_WRAPPER_REPLACE
+#include <socket_wrapper.h>
+#endif
+
+ROKEN_CPP_END
diff --git a/source4/heimdal/lib/roken/roken_gethostby.c b/source4/heimdal/lib/roken/roken_gethostby.c
new file mode 100644
index 0000000000..12760456a4
--- /dev/null
+++ b/source4/heimdal/lib/roken/roken_gethostby.c
@@ -0,0 +1,274 @@
+/*
+ * Copyright (c) 1998 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+RCSID("$Id$");
+#endif
+
+#include "roken.h"
+
+#undef roken_gethostbyname
+#undef roken_gethostbyaddr
+
+static struct sockaddr_in dns_addr;
+static char *dns_req;
+
+static int
+make_address(const char *address, struct in_addr *ip)
+{
+ if(inet_aton(address, ip) == 0){
+ /* try to resolve as hostname, it might work if the address we
+ are trying to lookup is local, for instance a web proxy */
+ struct hostent *he = gethostbyname(address);
+ if(he) {
+ unsigned char *p = (unsigned char*)he->h_addr;
+ ip->s_addr = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
+ } else {
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int
+setup_int(const char *proxy_host, short proxy_port,
+ const char *dns_host, short dns_port,
+ const char *dns_path)
+{
+ memset(&dns_addr, 0, sizeof(dns_addr));
+ if(dns_req)
+ free(dns_req);
+ if(proxy_host) {
+ if(make_address(proxy_host, &dns_addr.sin_addr) != 0)
+ return -1;
+ dns_addr.sin_port = htons(proxy_port);
+ asprintf(&dns_req, "http://%s:%d%s", dns_host, dns_port, dns_path);
+ } else {
+ if(make_address(dns_host, &dns_addr.sin_addr) != 0)
+ return -1;
+ dns_addr.sin_port = htons(dns_port);
+ asprintf(&dns_req, "%s", dns_path);
+ }
+ dns_addr.sin_family = AF_INET;
+ return 0;
+}
+
+static void
+split_spec(const char *spec, char **host, int *port, char **path, int def_port)
+{
+ char *p;
+ *host = strdup(spec);
+ p = strchr(*host, ':');
+ if(p) {
+ *p++ = '\0';
+ if(sscanf(p, "%d", port) != 1)
+ *port = def_port;
+ } else
+ *port = def_port;
+ p = strchr(p ? p : *host, '/');
+ if(p) {
+ if(path)
+ *path = strdup(p);
+ *p = '\0';
+ }else
+ if(path)
+ *path = NULL;
+}
+
+
+int ROKEN_LIB_FUNCTION
+roken_gethostby_setup(const char *proxy_spec, const char *dns_spec)
+{
+ char *proxy_host = NULL;
+ int proxy_port = 0;
+ char *dns_host, *dns_path;
+ int dns_port;
+
+ int ret = -1;
+
+ split_spec(dns_spec, &dns_host, &dns_port, &dns_path, 80);
+ if(dns_path == NULL)
+ goto out;
+ if(proxy_spec)
+ split_spec(proxy_spec, &proxy_host, &proxy_port, NULL, 80);
+ ret = setup_int(proxy_host, proxy_port, dns_host, dns_port, dns_path);
+out:
+ free(proxy_host);
+ free(dns_host);
+ free(dns_path);
+ return ret;
+}
+
+
+/* Try to lookup a name or an ip-address using http as transport
+ mechanism. See the end of this file for an example program. */
+static struct hostent*
+roken_gethostby(const char *hostname)
+{
+ int s;
+ struct sockaddr_in addr;
+ char *request;
+ char buf[1024];
+ int offset = 0;
+ int n;
+ char *p, *foo;
+
+ if(dns_addr.sin_family == 0)
+ return NULL; /* no configured host */
+ addr = dns_addr;
+ asprintf(&request, "GET %s?%s HTTP/1.0\r\n\r\n", dns_req, hostname);
+ if(request == NULL)
+ return NULL;
+ s = socket(AF_INET, SOCK_STREAM, 0);
+ if(s < 0) {
+ free(request);
+ return NULL;
+ }
+ if(connect(s, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
+ close(s);
+ free(request);
+ return NULL;
+ }
+ if(write(s, request, strlen(request)) != strlen(request)) {
+ close(s);
+ free(request);
+ return NULL;
+ }
+ free(request);
+ while(1) {
+ n = read(s, buf + offset, sizeof(buf) - offset);
+ if(n <= 0)
+ break;
+ offset += n;
+ }
+ buf[offset] = '\0';
+ close(s);
+ p = strstr(buf, "\r\n\r\n"); /* find end of header */
+ if(p) p += 4;
+ else return NULL;
+ foo = NULL;
+ p = strtok_r(p, " \t\r\n", &foo);
+ if(p == NULL)
+ return NULL;
+ {
+ /* make a hostent to return */
+#define MAX_ADDRS 16
+ static struct hostent he;
+ static char addrs[4 * MAX_ADDRS];
+ static char *addr_list[MAX_ADDRS + 1];
+ int num_addrs = 0;
+
+ he.h_name = p;
+ he.h_aliases = NULL;
+ he.h_addrtype = AF_INET;
+ he.h_length = 4;
+
+ while((p = strtok_r(NULL, " \t\r\n", &foo)) && num_addrs < MAX_ADDRS) {
+ struct in_addr ip;
+ inet_aton(p, &ip);
+ ip.s_addr = ntohl(ip.s_addr);
+ addr_list[num_addrs] = &addrs[num_addrs * 4];
+ addrs[num_addrs * 4 + 0] = (ip.s_addr >> 24) & 0xff;
+ addrs[num_addrs * 4 + 1] = (ip.s_addr >> 16) & 0xff;
+ addrs[num_addrs * 4 + 2] = (ip.s_addr >> 8) & 0xff;
+ addrs[num_addrs * 4 + 3] = (ip.s_addr >> 0) & 0xff;
+ addr_list[++num_addrs] = NULL;
+ }
+ he.h_addr_list = addr_list;
+ return &he;
+ }
+}
+
+struct hostent*
+roken_gethostbyname(const char *hostname)
+{
+ struct hostent *he;
+ he = gethostbyname(hostname);
+ if(he)
+ return he;
+ return roken_gethostby(hostname);
+}
+
+struct hostent* ROKEN_LIB_FUNCTION
+roken_gethostbyaddr(const void *addr, size_t len, int type)
+{
+ struct in_addr a;
+ const char *p;
+ struct hostent *he;
+ he = gethostbyaddr(addr, len, type);
+ if(he)
+ return he;
+ if(type != AF_INET || len != 4)
+ return NULL;
+ p = addr;
+ a.s_addr = htonl((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
+ return roken_gethostby(inet_ntoa(a));
+}
+
+#if 0
+
+/* this program can be used as a cgi `script' to lookup names and
+ ip-addresses */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <netdb.h>
+#include <sys/param.h>
+
+int
+main(int argc, char **argv)
+{
+ char *query = getenv("QUERY_STRING");
+ char host[MAXHOSTNAMELEN];
+ int i;
+ struct hostent *he;
+
+ printf("Content-type: text/plain\n\n");
+ if(query == NULL)
+ exit(0);
+ he = gethostbyname(query);
+ strncpy(host, he->h_name, sizeof(host));
+ host[sizeof(host) - 1] = '\0';
+ he = gethostbyaddr(he->h_addr, he->h_length, AF_INET);
+ printf("%s\n", he->h_name);
+ for(i = 0; he->h_addr_list[i]; i++) {
+ struct in_addr ip;
+ unsigned char *p = (unsigned char*)he->h_addr_list[i];
+ ip.s_addr = htonl((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
+ printf("%s\n", inet_ntoa(ip));
+ }
+ exit(0);
+}
+
+#endif
diff --git a/source4/heimdal/lib/roken/rtbl.c b/source4/heimdal/lib/roken/rtbl.c
new file mode 100644
index 0000000000..cfb7657091
--- /dev/null
+++ b/source4/heimdal/lib/roken/rtbl.c
@@ -0,0 +1,489 @@
+/*
+ * Copyright (c) 2000, 2002, 2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+RCSID ("$Id$");
+#endif
+#include "roken.h"
+#include "rtbl.h"
+
+struct column_entry {
+ char *data;
+};
+
+struct column_data {
+ char *header;
+ char *prefix;
+ int width;
+ unsigned flags;
+ size_t num_rows;
+ struct column_entry *rows;
+ unsigned int column_id;
+ char *suffix;
+};
+
+struct rtbl_data {
+ char *column_prefix;
+ size_t num_columns;
+ struct column_data **columns;
+ unsigned int flags;
+ char *column_separator;
+};
+
+rtbl_t ROKEN_LIB_FUNCTION
+rtbl_create (void)
+{
+ return calloc (1, sizeof (struct rtbl_data));
+}
+
+void ROKEN_LIB_FUNCTION
+rtbl_set_flags (rtbl_t table, unsigned int flags)
+{
+ table->flags = flags;
+}
+
+unsigned int ROKEN_LIB_FUNCTION
+rtbl_get_flags (rtbl_t table)
+{
+ return table->flags;
+}
+
+static struct column_data *
+rtbl_get_column_by_id (rtbl_t table, unsigned int id)
+{
+ int i;
+ for(i = 0; i < table->num_columns; i++)
+ if(table->columns[i]->column_id == id)
+ return table->columns[i];
+ return NULL;
+}
+
+static struct column_data *
+rtbl_get_column (rtbl_t table, const char *column)
+{
+ int i;
+ for(i = 0; i < table->num_columns; i++)
+ if(strcmp(table->columns[i]->header, column) == 0)
+ return table->columns[i];
+ return NULL;
+}
+
+void ROKEN_LIB_FUNCTION
+rtbl_destroy (rtbl_t table)
+{
+ int i, j;
+
+ for (i = 0; i < table->num_columns; i++) {
+ struct column_data *c = table->columns[i];
+
+ for (j = 0; j < c->num_rows; j++)
+ free (c->rows[j].data);
+ free (c->rows);
+ free (c->header);
+ free (c->prefix);
+ free (c->suffix);
+ free (c);
+ }
+ free (table->column_prefix);
+ free (table->column_separator);
+ free (table->columns);
+ free (table);
+}
+
+int ROKEN_LIB_FUNCTION
+rtbl_add_column_by_id (rtbl_t table, unsigned int id,
+ const char *header, unsigned int flags)
+{
+ struct column_data *col, **tmp;
+
+ tmp = realloc (table->columns, (table->num_columns + 1) * sizeof (*tmp));
+ if (tmp == NULL)
+ return ENOMEM;
+ table->columns = tmp;
+ col = malloc (sizeof (*col));
+ if (col == NULL)
+ return ENOMEM;
+ col->header = strdup (header);
+ if (col->header == NULL) {
+ free (col);
+ return ENOMEM;
+ }
+ col->prefix = NULL;
+ col->width = 0;
+ col->flags = flags;
+ col->num_rows = 0;
+ col->rows = NULL;
+ col->column_id = id;
+ col->suffix = NULL;
+ table->columns[table->num_columns++] = col;
+ return 0;
+}
+
+int ROKEN_LIB_FUNCTION
+rtbl_add_column (rtbl_t table, const char *header, unsigned int flags)
+{
+ return rtbl_add_column_by_id(table, 0, header, flags);
+}
+
+int ROKEN_LIB_FUNCTION
+rtbl_new_row(rtbl_t table)
+{
+ size_t max_rows = 0;
+ size_t c;
+ for (c = 0; c < table->num_columns; c++)
+ if(table->columns[c]->num_rows > max_rows)
+ max_rows = table->columns[c]->num_rows;
+ for (c = 0; c < table->num_columns; c++) {
+ struct column_entry *tmp;
+
+ if(table->columns[c]->num_rows == max_rows)
+ continue;
+ tmp = realloc(table->columns[c]->rows,
+ max_rows * sizeof(table->columns[c]->rows));
+ if(tmp == NULL)
+ return ENOMEM;
+ table->columns[c]->rows = tmp;
+ while(table->columns[c]->num_rows < max_rows) {
+ if((tmp[table->columns[c]->num_rows++].data = strdup("")) == NULL)
+ return ENOMEM;
+ }
+ }
+ return 0;
+}
+
+static void
+column_compute_width (rtbl_t table, struct column_data *column)
+{
+ int i;
+
+ if(table->flags & RTBL_HEADER_STYLE_NONE)
+ column->width = 0;
+ else
+ column->width = strlen (column->header);
+ for (i = 0; i < column->num_rows; i++)
+ column->width = max (column->width, strlen (column->rows[i].data));
+}
+
+/* DEPRECATED */
+int ROKEN_LIB_FUNCTION
+rtbl_set_prefix (rtbl_t table, const char *prefix)
+{
+ if (table->column_prefix)
+ free (table->column_prefix);
+ table->column_prefix = strdup (prefix);
+ if (table->column_prefix == NULL)
+ return ENOMEM;
+ return 0;
+}
+
+int ROKEN_LIB_FUNCTION
+rtbl_set_separator (rtbl_t table, const char *separator)
+{
+ if (table->column_separator)
+ free (table->column_separator);
+ table->column_separator = strdup (separator);
+ if (table->column_separator == NULL)
+ return ENOMEM;
+ return 0;
+}
+
+int ROKEN_LIB_FUNCTION
+rtbl_set_column_prefix (rtbl_t table, const char *column,
+ const char *prefix)
+{
+ struct column_data *c = rtbl_get_column (table, column);
+
+ if (c == NULL)
+ return -1;
+ if (c->prefix)
+ free (c->prefix);
+ c->prefix = strdup (prefix);
+ if (c->prefix == NULL)
+ return ENOMEM;
+ return 0;
+}
+
+int ROKEN_LIB_FUNCTION
+rtbl_set_column_affix_by_id(rtbl_t table, unsigned int id,
+ const char *prefix, const char *suffix)
+{
+ struct column_data *c = rtbl_get_column_by_id (table, id);
+
+ if (c == NULL)
+ return -1;
+ if (c->prefix)
+ free (c->prefix);
+ if(prefix == NULL)
+ c->prefix = NULL;
+ else {
+ c->prefix = strdup (prefix);
+ if (c->prefix == NULL)
+ return ENOMEM;
+ }
+
+ if (c->suffix)
+ free (c->suffix);
+ if(suffix == NULL)
+ c->suffix = NULL;
+ else {
+ c->suffix = strdup (suffix);
+ if (c->suffix == NULL)
+ return ENOMEM;
+ }
+ return 0;
+}
+
+
+static const char *
+get_column_prefix (rtbl_t table, struct column_data *c)
+{
+ if (c == NULL)
+ return "";
+ if (c->prefix)
+ return c->prefix;
+ if (table->column_prefix)
+ return table->column_prefix;
+ return "";
+}
+
+static const char *
+get_column_suffix (rtbl_t table, struct column_data *c)
+{
+ if (c && c->suffix)
+ return c->suffix;
+ return "";
+}
+
+static int
+add_column_entry (struct column_data *c, const char *data)
+{
+ struct column_entry row, *tmp;
+
+ row.data = strdup (data);
+ if (row.data == NULL)
+ return ENOMEM;
+ tmp = realloc (c->rows, (c->num_rows + 1) * sizeof (*tmp));
+ if (tmp == NULL) {
+ free (row.data);
+ return ENOMEM;
+ }
+ c->rows = tmp;
+ c->rows[c->num_rows++] = row;
+ return 0;
+}
+
+int ROKEN_LIB_FUNCTION
+rtbl_add_column_entry_by_id (rtbl_t table, unsigned int id, const char *data)
+{
+ struct column_data *c = rtbl_get_column_by_id (table, id);
+
+ if (c == NULL)
+ return -1;
+
+ return add_column_entry(c, data);
+}
+
+int ROKEN_LIB_FUNCTION
+rtbl_add_column_entryv_by_id (rtbl_t table, unsigned int id,
+ const char *fmt, ...)
+{
+ va_list ap;
+ char *str;
+ int ret;
+
+ va_start(ap, fmt);
+ ret = vasprintf(&str, fmt, ap);
+ va_end(ap);
+ if (ret == -1)
+ return -1;
+ ret = rtbl_add_column_entry_by_id(table, id, str);
+ free(str);
+ return ret;
+}
+
+int ROKEN_LIB_FUNCTION
+rtbl_add_column_entry (rtbl_t table, const char *column, const char *data)
+{
+ struct column_data *c = rtbl_get_column (table, column);
+
+ if (c == NULL)
+ return -1;
+
+ return add_column_entry(c, data);
+}
+
+int ROKEN_LIB_FUNCTION
+rtbl_add_column_entryv (rtbl_t table, const char *column, const char *fmt, ...)
+{
+ va_list ap;
+ char *str;
+ int ret;
+
+ va_start(ap, fmt);
+ ret = vasprintf(&str, fmt, ap);
+ va_end(ap);
+ if (ret == -1)
+ return -1;
+ ret = rtbl_add_column_entry(table, column, str);
+ free(str);
+ return ret;
+}
+
+
+int ROKEN_LIB_FUNCTION
+rtbl_format (rtbl_t table, FILE * f)
+{
+ int i, j;
+
+ for (i = 0; i < table->num_columns; i++)
+ column_compute_width (table, table->columns[i]);
+ if((table->flags & RTBL_HEADER_STYLE_NONE) == 0) {
+ for (i = 0; i < table->num_columns; i++) {
+ struct column_data *c = table->columns[i];
+
+ if(table->column_separator != NULL && i > 0)
+ fprintf (f, "%s", table->column_separator);
+ fprintf (f, "%s", get_column_prefix (table, c));
+ if(i == table->num_columns - 1 && c->suffix == NULL)
+ /* last column, so no need to pad with spaces */
+ fprintf (f, "%-*s", 0, c->header);
+ else
+ fprintf (f, "%-*s", (int)c->width, c->header);
+ fprintf (f, "%s", get_column_suffix (table, c));
+ }
+ fprintf (f, "\n");
+ }
+
+ for (j = 0;; j++) {
+ int flag = 0;
+
+ /* are there any more rows left? */
+ for (i = 0; flag == 0 && i < table->num_columns; ++i) {
+ struct column_data *c = table->columns[i];
+
+ if (c->num_rows > j) {
+ ++flag;
+ break;
+ }
+ }
+ if (flag == 0)
+ break;
+
+ for (i = 0; i < table->num_columns; i++) {
+ int w;
+ struct column_data *c = table->columns[i];
+
+ if(table->column_separator != NULL && i > 0)
+ fprintf (f, "%s", table->column_separator);
+
+ w = c->width;
+
+ if ((c->flags & RTBL_ALIGN_RIGHT) == 0) {
+ if(i == table->num_columns - 1 && c->suffix == NULL)
+ /* last column, so no need to pad with spaces */
+ w = 0;
+ else
+ w = -w;
+ }
+ fprintf (f, "%s", get_column_prefix (table, c));
+ if (c->num_rows <= j)
+ fprintf (f, "%*s", w, "");
+ else
+ fprintf (f, "%*s", w, c->rows[j].data);
+ fprintf (f, "%s", get_column_suffix (table, c));
+ }
+ fprintf (f, "\n");
+ }
+ return 0;
+}
+
+#ifdef TEST
+int
+main (int argc, char **argv)
+{
+ rtbl_t table;
+
+ table = rtbl_create ();
+ rtbl_add_column_by_id (table, 0, "Issued", 0);
+ rtbl_add_column_by_id (table, 1, "Expires", 0);
+ rtbl_add_column_by_id (table, 2, "Foo", RTBL_ALIGN_RIGHT);
+ rtbl_add_column_by_id (table, 3, "Principal", 0);
+
+ rtbl_add_column_entry_by_id (table, 0, "Jul 7 21:19:29");
+ rtbl_add_column_entry_by_id (table, 1, "Jul 8 07:19:29");
+ rtbl_add_column_entry_by_id (table, 2, "73");
+ rtbl_add_column_entry_by_id (table, 2, "0");
+ rtbl_add_column_entry_by_id (table, 2, "-2000");
+ rtbl_add_column_entry_by_id (table, 3, "krbtgt/NADA.KTH.SE@NADA.KTH.SE");
+
+ rtbl_add_column_entry_by_id (table, 0, "Jul 7 21:19:29");
+ rtbl_add_column_entry_by_id (table, 1, "Jul 8 07:19:29");
+ rtbl_add_column_entry_by_id (table, 3, "afs/pdc.kth.se@NADA.KTH.SE");
+
+ rtbl_add_column_entry_by_id (table, 0, "Jul 7 21:19:29");
+ rtbl_add_column_entry_by_id (table, 1, "Jul 8 07:19:29");
+ rtbl_add_column_entry_by_id (table, 3, "afs@NADA.KTH.SE");
+
+ rtbl_set_separator (table, " ");
+
+ rtbl_format (table, stdout);
+
+ rtbl_destroy (table);
+
+ printf("\n");
+
+ table = rtbl_create ();
+ rtbl_add_column_by_id (table, 0, "Column A", 0);
+ rtbl_set_column_affix_by_id (table, 0, "<", ">");
+ rtbl_add_column_by_id (table, 1, "Column B", 0);
+ rtbl_set_column_affix_by_id (table, 1, "[", "]");
+ rtbl_add_column_by_id (table, 2, "Column C", 0);
+ rtbl_set_column_affix_by_id (table, 2, "(", ")");
+
+ rtbl_add_column_entry_by_id (table, 0, "1");
+ rtbl_new_row(table);
+ rtbl_add_column_entry_by_id (table, 1, "2");
+ rtbl_new_row(table);
+ rtbl_add_column_entry_by_id (table, 2, "3");
+ rtbl_new_row(table);
+
+ rtbl_set_separator (table, " ");
+ rtbl_format (table, stdout);
+
+ rtbl_destroy (table);
+
+ return 0;
+}
+
+#endif
diff --git a/source4/heimdal/lib/roken/rtbl.h b/source4/heimdal/lib/roken/rtbl.h
new file mode 100644
index 0000000000..60bbc9f873
--- /dev/null
+++ b/source4/heimdal/lib/roken/rtbl.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2000,2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/* $Id$ */
+
+#ifndef __rtbl_h__
+#define __rtbl_h__
+
+#ifndef ROKEN_LIB_FUNCTION
+#ifdef _WIN32
+#define ROKEN_LIB_FUNCTION _stdcall
+#else
+#define ROKEN_LIB_FUNCTION
+#endif
+#endif
+
+#if !defined(__GNUC__) && !defined(__attribute__)
+#define __attribute__(x)
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct rtbl_data;
+typedef struct rtbl_data *rtbl_t;
+
+#define RTBL_ALIGN_LEFT 0
+#define RTBL_ALIGN_RIGHT 1
+
+/* flags */
+#define RTBL_HEADER_STYLE_NONE 1
+
+int ROKEN_LIB_FUNCTION
+rtbl_add_column (rtbl_t, const char*, unsigned int);
+
+int ROKEN_LIB_FUNCTION
+rtbl_add_column_by_id (rtbl_t, unsigned int, const char*, unsigned int);
+
+int ROKEN_LIB_FUNCTION
+rtbl_add_column_entryv_by_id (rtbl_t table, unsigned int id,
+ const char *fmt, ...)
+ __attribute__ ((format (printf, 3, 0)));
+
+int ROKEN_LIB_FUNCTION
+rtbl_add_column_entry (rtbl_t, const char*, const char*);
+
+int ROKEN_LIB_FUNCTION
+rtbl_add_column_entryv (rtbl_t, const char*, const char*, ...)
+ __attribute__ ((format (printf, 3, 0)));
+
+int ROKEN_LIB_FUNCTION
+rtbl_add_column_entry_by_id (rtbl_t, unsigned int, const char*);
+
+rtbl_t ROKEN_LIB_FUNCTION
+rtbl_create (void);
+
+void ROKEN_LIB_FUNCTION
+rtbl_destroy (rtbl_t);
+
+int ROKEN_LIB_FUNCTION
+rtbl_format (rtbl_t, FILE*);
+
+unsigned int ROKEN_LIB_FUNCTION
+rtbl_get_flags (rtbl_t);
+
+int ROKEN_LIB_FUNCTION
+rtbl_new_row (rtbl_t);
+
+int ROKEN_LIB_FUNCTION
+rtbl_set_column_affix_by_id (rtbl_t, unsigned int, const char*, const char*);
+
+int ROKEN_LIB_FUNCTION
+rtbl_set_column_prefix (rtbl_t, const char*, const char*);
+
+void ROKEN_LIB_FUNCTION
+rtbl_set_flags (rtbl_t, unsigned int);
+
+int ROKEN_LIB_FUNCTION
+rtbl_set_prefix (rtbl_t, const char*);
+
+int ROKEN_LIB_FUNCTION
+rtbl_set_separator (rtbl_t, const char*);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __rtbl_h__ */
diff --git a/source4/heimdal/lib/roken/setprogname.c b/source4/heimdal/lib/roken/setprogname.c
new file mode 100644
index 0000000000..9beb07afe9
--- /dev/null
+++ b/source4/heimdal/lib/roken/setprogname.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 1995-2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+RCSID("$Id$");
+#endif
+
+#include "roken.h"
+
+#ifndef HAVE___PROGNAME
+extern const char *__progname;
+#endif
+
+#ifndef HAVE_SETPROGNAME
+void ROKEN_LIB_FUNCTION
+setprogname(const char *argv0)
+{
+#ifndef HAVE___PROGNAME
+ const char *p;
+ if(argv0 == NULL)
+ return;
+ p = strrchr(argv0, '/');
+ if(p == NULL)
+ p = argv0;
+ else
+ p++;
+ __progname = p;
+#endif
+}
+#endif /* HAVE_SETPROGNAME */
diff --git a/source4/heimdal/lib/roken/signal.c b/source4/heimdal/lib/roken/signal.c
new file mode 100644
index 0000000000..9141ca2bb1
--- /dev/null
+++ b/source4/heimdal/lib/roken/signal.c
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 1995 - 2000 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+RCSID("$Id$");
+#endif
+
+#include <signal.h>
+#include "roken.h"
+
+/*
+ * We would like to always use this signal but there is a link error
+ * on NEXTSTEP
+ */
+#if !defined(NeXT) && !defined(__APPLE__)
+/*
+ * Bugs:
+ *
+ * Do we need any extra hacks for SIGCLD and/or SIGCHLD?
+ */
+
+SigAction ROKEN_LIB_FUNCTION
+signal(int iSig, SigAction pAction)
+{
+ struct sigaction saNew, saOld;
+
+ saNew.sa_handler = pAction;
+ sigemptyset(&saNew.sa_mask);
+ saNew.sa_flags = 0;
+
+ if (iSig == SIGALRM)
+ {
+#ifdef SA_INTERRUPT
+ saNew.sa_flags |= SA_INTERRUPT;
+#endif
+ }
+ else
+ {
+#ifdef SA_RESTART
+ saNew.sa_flags |= SA_RESTART;
+#endif
+ }
+
+ if (sigaction(iSig, &saNew, &saOld) < 0)
+ return(SIG_ERR);
+
+ return(saOld.sa_handler);
+}
+#endif
diff --git a/source4/heimdal/lib/roken/simple_exec.c b/source4/heimdal/lib/roken/simple_exec.c
new file mode 100644
index 0000000000..7060cb8d37
--- /dev/null
+++ b/source4/heimdal/lib/roken/simple_exec.c
@@ -0,0 +1,335 @@
+/*
+ * Copyright (c) 1998 - 2001, 2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+RCSID("$Id$");
+#endif
+
+#include <stdarg.h>
+#include <stdlib.h>
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <errno.h>
+
+#include "roken.h"
+
+#if !HAVE_DECL_ENVIRON
+extern char **environ;
+#endif
+
+#define EX_NOEXEC 126
+#define EX_NOTFOUND 127
+
+/* return values:
+ -1 on `unspecified' system errors
+ -2 on fork failures
+ -3 on waitpid errors
+ -4 exec timeout
+ 0- is return value from subprocess
+ 126 if the program couldn't be executed
+ 127 if the program couldn't be found
+ 128- is 128 + signal that killed subprocess
+
+ possible values `func' can return:
+ ((time_t)-2) exit loop w/o killing child and return
+ `exec timeout'/-4 from simple_exec
+ ((time_t)-1) kill child with SIGTERM and wait for child to exit
+ 0 don't timeout again
+ n seconds to next timeout
+ */
+
+static int sig_alarm;
+
+static RETSIGTYPE
+sigtimeout(int sig)
+{
+ sig_alarm = 1;
+ SIGRETURN(0);
+}
+
+int ROKEN_LIB_FUNCTION
+wait_for_process_timed(pid_t pid, time_t (*func)(void *),
+ void *ptr, time_t timeout)
+{
+ RETSIGTYPE (*old_func)(int sig) = NULL;
+ unsigned int oldtime = 0;
+ int ret;
+
+ sig_alarm = 0;
+
+ if (func) {
+ old_func = signal(SIGALRM, sigtimeout);
+ oldtime = alarm(timeout);
+ }
+
+ while(1) {
+ int status;
+
+ while(waitpid(pid, &status, 0) < 0) {
+ if (errno != EINTR) {
+ ret = -3;
+ goto out;
+ }
+ if (func == NULL)
+ continue;
+ if (sig_alarm == 0)
+ continue;
+ timeout = (*func)(ptr);
+ if (timeout == (time_t)-1) {
+ kill(pid, SIGTERM);
+ continue;
+ } else if (timeout == (time_t)-2) {
+ ret = -4;
+ goto out;
+ }
+ alarm(timeout);
+ }
+ if(WIFSTOPPED(status))
+ continue;
+ if(WIFEXITED(status)) {
+ ret = WEXITSTATUS(status);
+ break;
+ }
+ if(WIFSIGNALED(status)) {
+ ret = WTERMSIG(status) + 128;
+ break;
+ }
+ }
+ out:
+ if (func) {
+ signal(SIGALRM, old_func);
+ alarm(oldtime);
+ }
+ return ret;
+}
+
+int ROKEN_LIB_FUNCTION
+wait_for_process(pid_t pid)
+{
+ return wait_for_process_timed(pid, NULL, NULL, 0);
+}
+
+int ROKEN_LIB_FUNCTION
+pipe_execv(FILE **stdin_fd, FILE **stdout_fd, FILE **stderr_fd,
+ const char *file, ...)
+{
+ int in_fd[2], out_fd[2], err_fd[2];
+ pid_t pid;
+ va_list ap;
+ char **argv;
+
+ if(stdin_fd != NULL)
+ pipe(in_fd);
+ if(stdout_fd != NULL)
+ pipe(out_fd);
+ if(stderr_fd != NULL)
+ pipe(err_fd);
+ pid = fork();
+ switch(pid) {
+ case 0:
+ va_start(ap, file);
+ argv = vstrcollect(&ap);
+ va_end(ap);
+ if(argv == NULL)
+ exit(-1);
+
+ /* close pipes we're not interested in */
+ if(stdin_fd != NULL)
+ close(in_fd[1]);
+ if(stdout_fd != NULL)
+ close(out_fd[0]);
+ if(stderr_fd != NULL)
+ close(err_fd[0]);
+
+ /* pipe everything caller doesn't care about to /dev/null */
+ if(stdin_fd == NULL)
+ in_fd[0] = open(_PATH_DEVNULL, O_RDONLY);
+ if(stdout_fd == NULL)
+ out_fd[1] = open(_PATH_DEVNULL, O_WRONLY);
+ if(stderr_fd == NULL)
+ err_fd[1] = open(_PATH_DEVNULL, O_WRONLY);
+
+ /* move to proper descriptors */
+ if(in_fd[0] != STDIN_FILENO) {
+ dup2(in_fd[0], STDIN_FILENO);
+ close(in_fd[0]);
+ }
+ if(out_fd[1] != STDOUT_FILENO) {
+ dup2(out_fd[1], STDOUT_FILENO);
+ close(out_fd[1]);
+ }
+ if(err_fd[1] != STDERR_FILENO) {
+ dup2(err_fd[1], STDERR_FILENO);
+ close(err_fd[1]);
+ }
+
+ closefrom(3);
+
+ execv(file, argv);
+ exit((errno == ENOENT) ? EX_NOTFOUND : EX_NOEXEC);
+ case -1:
+ if(stdin_fd != NULL) {
+ close(in_fd[0]);
+ close(in_fd[1]);
+ }
+ if(stdout_fd != NULL) {
+ close(out_fd[0]);
+ close(out_fd[1]);
+ }
+ if(stderr_fd != NULL) {
+ close(err_fd[0]);
+ close(err_fd[1]);
+ }
+ return -2;
+ default:
+ if(stdin_fd != NULL) {
+ close(in_fd[0]);
+ *stdin_fd = fdopen(in_fd[1], "w");
+ }
+ if(stdout_fd != NULL) {
+ close(out_fd[1]);
+ *stdout_fd = fdopen(out_fd[0], "r");
+ }
+ if(stderr_fd != NULL) {
+ close(err_fd[1]);
+ *stderr_fd = fdopen(err_fd[0], "r");
+ }
+ }
+ return pid;
+}
+
+int ROKEN_LIB_FUNCTION
+simple_execvp_timed(const char *file, char *const args[],
+ time_t (*func)(void *), void *ptr, time_t timeout)
+{
+ pid_t pid = fork();
+ switch(pid){
+ case -1:
+ return -2;
+ case 0:
+ execvp(file, args);
+ exit((errno == ENOENT) ? EX_NOTFOUND : EX_NOEXEC);
+ default:
+ return wait_for_process_timed(pid, func, ptr, timeout);
+ }
+}
+
+int ROKEN_LIB_FUNCTION
+simple_execvp(const char *file, char *const args[])
+{
+ return simple_execvp_timed(file, args, NULL, NULL, 0);
+}
+
+/* gee, I'd like a execvpe */
+int ROKEN_LIB_FUNCTION
+simple_execve_timed(const char *file, char *const args[], char *const envp[],
+ time_t (*func)(void *), void *ptr, time_t timeout)
+{
+ pid_t pid = fork();
+ switch(pid){
+ case -1:
+ return -2;
+ case 0:
+ execve(file, args, envp);
+ exit((errno == ENOENT) ? EX_NOTFOUND : EX_NOEXEC);
+ default:
+ return wait_for_process_timed(pid, func, ptr, timeout);
+ }
+}
+
+int ROKEN_LIB_FUNCTION
+simple_execve(const char *file, char *const args[], char *const envp[])
+{
+ return simple_execve_timed(file, args, envp, NULL, NULL, 0);
+}
+
+int ROKEN_LIB_FUNCTION
+simple_execlp(const char *file, ...)
+{
+ va_list ap;
+ char **argv;
+ int ret;
+
+ va_start(ap, file);
+ argv = vstrcollect(&ap);
+ va_end(ap);
+ if(argv == NULL)
+ return -1;
+ ret = simple_execvp(file, argv);
+ free(argv);
+ return ret;
+}
+
+int ROKEN_LIB_FUNCTION
+simple_execle(const char *file, ... /* ,char *const envp[] */)
+{
+ va_list ap;
+ char **argv;
+ char *const* envp;
+ int ret;
+
+ va_start(ap, file);
+ argv = vstrcollect(&ap);
+ envp = va_arg(ap, char **);
+ va_end(ap);
+ if(argv == NULL)
+ return -1;
+ ret = simple_execve(file, argv, envp);
+ free(argv);
+ return ret;
+}
+
+int ROKEN_LIB_FUNCTION
+simple_execl(const char *file, ...)
+{
+ va_list ap;
+ char **argv;
+ int ret;
+
+ va_start(ap, file);
+ argv = vstrcollect(&ap);
+ va_end(ap);
+ if(argv == NULL)
+ return -1;
+ ret = simple_execve(file, argv, environ);
+ free(argv);
+ return ret;
+}
diff --git a/source4/heimdal/lib/roken/socket.c b/source4/heimdal/lib/roken/socket.c
new file mode 100644
index 0000000000..a373eb7ed2
--- /dev/null
+++ b/source4/heimdal/lib/roken/socket.c
@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 1999 - 2000 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+RCSID("$Id$");
+#endif
+
+#include "roken.h"
+#include <err.h>
+
+/*
+ * Set `sa' to the unitialized address of address family `af'
+ */
+
+void ROKEN_LIB_FUNCTION
+socket_set_any (struct sockaddr *sa, int af)
+{
+ switch (af) {
+ case AF_INET : {
+ struct sockaddr_in *sin4 = (struct sockaddr_in *)sa;
+
+ memset (sin4, 0, sizeof(*sin4));
+ sin4->sin_family = AF_INET;
+ sin4->sin_port = 0;
+ sin4->sin_addr.s_addr = INADDR_ANY;
+ break;
+ }
+#ifdef HAVE_IPV6
+ case AF_INET6 : {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
+
+ memset (sin6, 0, sizeof(*sin6));
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = 0;
+ sin6->sin6_addr = in6addr_any;
+ break;
+ }
+#endif
+ default :
+ errx (1, "unknown address family %d", sa->sa_family);
+ break;
+ }
+}
+
+/*
+ * set `sa' to (`ptr', `port')
+ */
+
+void ROKEN_LIB_FUNCTION
+socket_set_address_and_port (struct sockaddr *sa, const void *ptr, int port)
+{
+ switch (sa->sa_family) {
+ case AF_INET : {
+ struct sockaddr_in *sin4 = (struct sockaddr_in *)sa;
+
+ memset (sin4, 0, sizeof(*sin4));
+ sin4->sin_family = AF_INET;
+ sin4->sin_port = port;
+ memcpy (&sin4->sin_addr, ptr, sizeof(struct in_addr));
+ break;
+ }
+#ifdef HAVE_IPV6
+ case AF_INET6 : {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
+
+ memset (sin6, 0, sizeof(*sin6));
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = port;
+ memcpy (&sin6->sin6_addr, ptr, sizeof(struct in6_addr));
+ break;
+ }
+#endif
+ default :
+ errx (1, "unknown address family %d", sa->sa_family);
+ break;
+ }
+}
+
+/*
+ * Return the size of an address of the type in `sa'
+ */
+
+size_t ROKEN_LIB_FUNCTION
+socket_addr_size (const struct sockaddr *sa)
+{
+ switch (sa->sa_family) {
+ case AF_INET :
+ return sizeof(struct in_addr);
+#ifdef HAVE_IPV6
+ case AF_INET6 :
+ return sizeof(struct in6_addr);
+#endif
+ default :
+ errx (1, "unknown address family %d", sa->sa_family);
+ break;
+ }
+}
+
+/*
+ * Return the size of a `struct sockaddr' in `sa'.
+ */
+
+size_t ROKEN_LIB_FUNCTION
+socket_sockaddr_size (const struct sockaddr *sa)
+{
+ switch (sa->sa_family) {
+ case AF_INET :
+ return sizeof(struct sockaddr_in);
+#ifdef HAVE_IPV6
+ case AF_INET6 :
+ return sizeof(struct sockaddr_in6);
+#endif
+ default :
+ errx (1, "unknown address family %d", sa->sa_family);
+ break;
+ }
+}
+
+/*
+ * Return the binary address of `sa'.
+ */
+
+void * ROKEN_LIB_FUNCTION
+socket_get_address (struct sockaddr *sa)
+{
+ switch (sa->sa_family) {
+ case AF_INET : {
+ struct sockaddr_in *sin4 = (struct sockaddr_in *)sa;
+ return &sin4->sin_addr;
+ }
+#ifdef HAVE_IPV6
+ case AF_INET6 : {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
+ return &sin6->sin6_addr;
+ }
+#endif
+ default :
+ errx (1, "unknown address family %d", sa->sa_family);
+ break;
+ }
+}
+
+/*
+ * Return the port number from `sa'.
+ */
+
+int ROKEN_LIB_FUNCTION
+socket_get_port (const struct sockaddr *sa)
+{
+ switch (sa->sa_family) {
+ case AF_INET : {
+ const struct sockaddr_in *sin4 = (const struct sockaddr_in *)sa;
+ return sin4->sin_port;
+ }
+#ifdef HAVE_IPV6
+ case AF_INET6 : {
+ const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sa;
+ return sin6->sin6_port;
+ }
+#endif
+ default :
+ errx (1, "unknown address family %d", sa->sa_family);
+ break;
+ }
+}
+
+/*
+ * Set the port in `sa' to `port'.
+ */
+
+void ROKEN_LIB_FUNCTION
+socket_set_port (struct sockaddr *sa, int port)
+{
+ switch (sa->sa_family) {
+ case AF_INET : {
+ struct sockaddr_in *sin4 = (struct sockaddr_in *)sa;
+ sin4->sin_port = port;
+ break;
+ }
+#ifdef HAVE_IPV6
+ case AF_INET6 : {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
+ sin6->sin6_port = port;
+ break;
+ }
+#endif
+ default :
+ errx (1, "unknown address family %d", sa->sa_family);
+ break;
+ }
+}
+
+/*
+ * Set the range of ports to use when binding with port = 0.
+ */
+void ROKEN_LIB_FUNCTION
+socket_set_portrange (int sock, int restr, int af)
+{
+#if defined(IP_PORTRANGE)
+ if (af == AF_INET) {
+ int on = restr ? IP_PORTRANGE_HIGH : IP_PORTRANGE_DEFAULT;
+ if (setsockopt (sock, IPPROTO_IP, IP_PORTRANGE, &on,
+ sizeof(on)) < 0)
+ warn ("setsockopt IP_PORTRANGE (ignored)");
+ }
+#endif
+#if defined(IPV6_PORTRANGE)
+ if (af == AF_INET6) {
+ int on = restr ? IPV6_PORTRANGE_HIGH :
+ IPV6_PORTRANGE_DEFAULT;
+ if (setsockopt (sock, IPPROTO_IPV6, IPV6_PORTRANGE, &on,
+ sizeof(on)) < 0)
+ warn ("setsockopt IPV6_PORTRANGE (ignored)");
+ }
+#endif
+}
+
+/*
+ * Enable debug on `sock'.
+ */
+
+void ROKEN_LIB_FUNCTION
+socket_set_debug (int sock)
+{
+#if defined(SO_DEBUG) && defined(HAVE_SETSOCKOPT)
+ int on = 1;
+
+ if (setsockopt (sock, SOL_SOCKET, SO_DEBUG, (void *) &on, sizeof (on)) < 0)
+ warn ("setsockopt SO_DEBUG (ignored)");
+#endif
+}
+
+/*
+ * Set the type-of-service of `sock' to `tos'.
+ */
+
+void ROKEN_LIB_FUNCTION
+socket_set_tos (int sock, int tos)
+{
+#if defined(IP_TOS) && defined(HAVE_SETSOCKOPT)
+ if (setsockopt (sock, IPPROTO_IP, IP_TOS, (void *) &tos, sizeof (int)) < 0)
+ if (errno != EINVAL)
+ warn ("setsockopt TOS (ignored)");
+#endif
+}
+
+/*
+ * set the reuse of addresses on `sock' to `val'.
+ */
+
+void ROKEN_LIB_FUNCTION
+socket_set_reuseaddr (int sock, int val)
+{
+#if defined(SO_REUSEADDR) && defined(HAVE_SETSOCKOPT)
+ if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&val,
+ sizeof(val)) < 0)
+ err (1, "setsockopt SO_REUSEADDR");
+#endif
+}
+
+/*
+ * Set the that the `sock' should bind to only IPv6 addresses.
+ */
+
+void ROKEN_LIB_FUNCTION
+socket_set_ipv6only (int sock, int val)
+{
+#if defined(IPV6_V6ONLY) && defined(HAVE_SETSOCKOPT)
+ setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&val, sizeof(val));
+#endif
+}
diff --git a/source4/heimdal/lib/roken/strcollect.c b/source4/heimdal/lib/roken/strcollect.c
new file mode 100644
index 0000000000..a33d52134a
--- /dev/null
+++ b/source4/heimdal/lib/roken/strcollect.c
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 1998, 1999 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+RCSID("$Id$");
+#endif
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include "roken.h"
+
+enum { initial = 10, increment = 5 };
+
+static char **
+sub (char **argv, int i, int argc, va_list *ap)
+{
+ do {
+ if(i == argc) {
+ /* realloc argv */
+ char **tmp = realloc(argv, (argc + increment) * sizeof(*argv));
+ if(tmp == NULL) {
+ free(argv);
+ errno = ENOMEM;
+ return NULL;
+ }
+ argv = tmp;
+ argc += increment;
+ }
+ argv[i++] = va_arg(*ap, char*);
+ } while(argv[i - 1] != NULL);
+ return argv;
+}
+
+/*
+ * return a malloced vector of pointers to the strings in `ap'
+ * terminated by NULL.
+ */
+
+char ** ROKEN_LIB_FUNCTION
+vstrcollect(va_list *ap)
+{
+ return sub (NULL, 0, 0, ap);
+}
+
+/*
+ *
+ */
+
+char ** ROKEN_LIB_FUNCTION
+strcollect(char *first, ...)
+{
+ va_list ap;
+ char **ret = malloc (initial * sizeof(char *));
+
+ if (ret == NULL)
+ return ret;
+
+ ret[0] = first;
+ va_start(ap, first);
+ ret = sub (ret, 1, initial, &ap);
+ va_end(ap);
+ return ret;
+}
diff --git a/source4/heimdal/lib/roken/strlwr.c b/source4/heimdal/lib/roken/strlwr.c
new file mode 100644
index 0000000000..ea787d8184
--- /dev/null
+++ b/source4/heimdal/lib/roken/strlwr.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 1995, 1996, 1997 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+RCSID("$Id$");
+#endif
+#include <string.h>
+#include <ctype.h>
+
+#include "roken.h"
+
+#ifndef HAVE_STRLWR
+char * ROKEN_LIB_FUNCTION
+strlwr(char *str)
+{
+ char *s;
+
+ for(s = str; *s; s++)
+ *s = tolower((unsigned char)*s);
+ return str;
+}
+#endif
diff --git a/source4/heimdal/lib/roken/strpool.c b/source4/heimdal/lib/roken/strpool.c
new file mode 100644
index 0000000000..aa80996946
--- /dev/null
+++ b/source4/heimdal/lib/roken/strpool.c
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+RCSID("$Id$");
+#endif
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include "roken.h"
+
+struct rk_strpool {
+ char *str;
+ size_t len;
+};
+
+/*
+ *
+ */
+
+void ROKEN_LIB_FUNCTION
+rk_strpoolfree(struct rk_strpool *p)
+{
+ if (p->str) {
+ free(p->str);
+ p->str = NULL;
+ }
+ free(p);
+}
+
+/*
+ *
+ */
+
+struct rk_strpool * ROKEN_LIB_FUNCTION
+rk_strpoolprintf(struct rk_strpool *p, const char *fmt, ...)
+{
+ va_list ap;
+ char *str, *str2;
+ int len;
+
+ if (p == NULL) {
+ p = malloc(sizeof(*p));
+ if (p == NULL)
+ return NULL;
+ p->str = NULL;
+ p->len = 0;
+ }
+ va_start(ap, fmt);
+ len = vasprintf(&str, fmt, ap);
+ va_end(ap);
+ if (str == NULL) {
+ rk_strpoolfree(p);
+ return NULL;
+ }
+ str2 = realloc(p->str, len + p->len + 1);
+ if (str2 == NULL) {
+ rk_strpoolfree(p);
+ return NULL;
+ }
+ p->str = str2;
+ memcpy(p->str + p->len, str, len + 1);
+ p->len += len;
+ free(str);
+ return p;
+}
+
+/*
+ *
+ */
+
+char * ROKEN_LIB_FUNCTION
+rk_strpoolcollect(struct rk_strpool *p)
+{
+ char *str = p->str;
+ p->str = NULL;
+ free(p);
+ return str;
+}
diff --git a/source4/heimdal/lib/roken/strsep.c b/source4/heimdal/lib/roken/strsep.c
new file mode 100644
index 0000000000..7a93fb17aa
--- /dev/null
+++ b/source4/heimdal/lib/roken/strsep.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 1997 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+RCSID("$Id$");
+#endif
+
+#include <string.h>
+
+#include "roken.h"
+
+#ifndef HAVE_STRSEP
+
+char * ROKEN_LIB_FUNCTION
+strsep(char **str, const char *delim)
+{
+ char *save = *str;
+ if(*str == NULL)
+ return NULL;
+ *str = *str + strcspn(*str, delim);
+ if(**str == 0)
+ *str = NULL;
+ else{
+ **str = 0;
+ (*str)++;
+ }
+ return save;
+}
+
+#endif
diff --git a/source4/heimdal/lib/roken/strsep_copy.c b/source4/heimdal/lib/roken/strsep_copy.c
new file mode 100644
index 0000000000..61b7d39c57
--- /dev/null
+++ b/source4/heimdal/lib/roken/strsep_copy.c
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2000, 2002 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+RCSID("$Id$");
+#endif
+
+#include <string.h>
+
+#include "roken.h"
+
+#ifndef HAVE_STRSEP_COPY
+
+/* strsep, but with const stringp, so return string in buf */
+
+ssize_t ROKEN_LIB_FUNCTION
+strsep_copy(const char **stringp, const char *delim, char *buf, size_t len)
+{
+ const char *save = *stringp;
+ size_t l;
+ if(save == NULL)
+ return -1;
+ *stringp = *stringp + strcspn(*stringp, delim);
+ l = min(len, *stringp - save);
+ if(len > 0) {
+ memcpy(buf, save, l);
+ buf[l] = '\0';
+ }
+
+ l = *stringp - save;
+ if(**stringp == '\0')
+ *stringp = NULL;
+ else
+ (*stringp)++;
+ return l;
+}
+
+#endif
diff --git a/source4/heimdal/lib/roken/strupr.c b/source4/heimdal/lib/roken/strupr.c
new file mode 100644
index 0000000000..d67dd20dd0
--- /dev/null
+++ b/source4/heimdal/lib/roken/strupr.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 1995, 1996, 1997 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+RCSID("$Id$");
+#endif
+#include <string.h>
+#include <ctype.h>
+
+#include "roken.h"
+
+#ifndef HAVE_STRUPR
+char * ROKEN_LIB_FUNCTION
+strupr(char *str)
+{
+ char *s;
+
+ for(s = str; *s; s++)
+ *s = toupper((unsigned char)*s);
+ return str;
+}
+#endif
diff --git a/source4/heimdal/lib/roken/vis.c b/source4/heimdal/lib/roken/vis.c
new file mode 100644
index 0000000000..43705e4d50
--- /dev/null
+++ b/source4/heimdal/lib/roken/vis.c
@@ -0,0 +1,424 @@
+/* $NetBSD: vis.c,v 1.37 2008/07/25 22:29:23 dsl Exp $ */
+
+/*-
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) 1999, 2005 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if 1
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+RCSID("$Id$");
+#endif
+#include "roken.h"
+#ifndef _DIAGASSERT
+#define _DIAGASSERT(X)
+#endif
+#else /* heimdal */
+#include <sys/cdefs.h>
+#if defined(LIBC_SCCS) && !defined(lint)
+__RCSID("$NetBSD: vis.c,v 1.37 2008/07/25 22:29:23 dsl Exp $");
+#endif /* LIBC_SCCS and not lint */
+
+#include "namespace.h"
+#endif /* heimdal */
+
+#include <sys/types.h>
+
+#include <assert.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <vis.h>
+#include <stdlib.h>
+
+#if 0
+#ifdef __weak_alias
+__weak_alias(strsvis,_strsvis)
+__weak_alias(strsvisx,_strsvisx)
+__weak_alias(strvis,_strvis)
+__weak_alias(strvisx,_strvisx)
+__weak_alias(svis,_svis)
+__weak_alias(vis,_vis)
+#endif
+#endif
+
+#if !HAVE_VIS || !HAVE_SVIS
+#include <ctype.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+
+static char *do_svis(char *, int, int, int, const char *);
+
+#undef BELL
+#if defined(__STDC__)
+#define BELL '\a'
+#else
+#define BELL '\007'
+#endif
+
+char * ROKEN_LIB_FUNCTION
+ rk_vis (char *, int, int, int);
+char * ROKEN_LIB_FUNCTION
+ rk_svis (char *, int, int, int, const char *);
+int ROKEN_LIB_FUNCTION
+ rk_strvis (char *, const char *, int);
+int ROKEN_LIB_FUNCTION
+ rk_strsvis (char *, const char *, int, const char *);
+int ROKEN_LIB_FUNCTION
+ rk_strvisx (char *, const char *, size_t, int);
+int ROKEN_LIB_FUNCTION
+ rk_strsvisx (char *, const char *, size_t, int, const char *);
+
+
+#define isoctal(c) (((u_char)(c)) >= '0' && ((u_char)(c)) <= '7')
+#define iswhite(c) (c == ' ' || c == '\t' || c == '\n')
+#define issafe(c) (c == '\b' || c == BELL || c == '\r')
+#define xtoa(c) "0123456789abcdef"[c]
+
+#define MAXEXTRAS 5
+
+#define MAKEEXTRALIST(flag, extra, orig_str) \
+do { \
+ const char *orig = orig_str; \
+ const char *o = orig; \
+ char *e; \
+ while (*o++) \
+ continue; \
+ extra = malloc((size_t)((o - orig) + MAXEXTRAS)); \
+ if (!extra) break; \
+ for (o = orig, e = extra; (*e++ = *o++) != '\0';) \
+ continue; \
+ e--; \
+ if (flag & VIS_SP) *e++ = ' '; \
+ if (flag & VIS_TAB) *e++ = '\t'; \
+ if (flag & VIS_NL) *e++ = '\n'; \
+ if ((flag & VIS_NOSLASH) == 0) *e++ = '\\'; \
+ *e = '\0'; \
+} while (/*CONSTCOND*/0)
+
+/*
+ * This is do_hvis, for HTTP style (RFC 1808)
+ */
+static char *
+do_hvis(char *dst, int c, int flag, int nextc, const char *extra)
+{
+ if (!isascii(c) || !isalnum(c) || strchr("$-_.+!*'(),", c) != NULL) {
+ *dst++ = '%';
+ *dst++ = xtoa(((unsigned int)c >> 4) & 0xf);
+ *dst++ = xtoa((unsigned int)c & 0xf);
+ } else {
+ dst = do_svis(dst, c, flag, nextc, extra);
+ }
+ return dst;
+}
+
+/*
+ * This is do_vis, the central code of vis.
+ * dst: Pointer to the destination buffer
+ * c: Character to encode
+ * flag: Flag word
+ * nextc: The character following 'c'
+ * extra: Pointer to the list of extra characters to be
+ * backslash-protected.
+ */
+static char *
+do_svis(char *dst, int c, int flag, int nextc, const char *extra)
+{
+ int isextra;
+ isextra = strchr(extra, c) != NULL;
+ if (!isextra && isascii(c) && (isgraph(c) || iswhite(c) ||
+ ((flag & VIS_SAFE) && issafe(c)))) {
+ *dst++ = c;
+ return dst;
+ }
+ if (flag & VIS_CSTYLE) {
+ switch (c) {
+ case '\n':
+ *dst++ = '\\'; *dst++ = 'n';
+ return dst;
+ case '\r':
+ *dst++ = '\\'; *dst++ = 'r';
+ return dst;
+ case '\b':
+ *dst++ = '\\'; *dst++ = 'b';
+ return dst;
+ case BELL:
+ *dst++ = '\\'; *dst++ = 'a';
+ return dst;
+ case '\v':
+ *dst++ = '\\'; *dst++ = 'v';
+ return dst;
+ case '\t':
+ *dst++ = '\\'; *dst++ = 't';
+ return dst;
+ case '\f':
+ *dst++ = '\\'; *dst++ = 'f';
+ return dst;
+ case ' ':
+ *dst++ = '\\'; *dst++ = 's';
+ return dst;
+ case '\0':
+ *dst++ = '\\'; *dst++ = '0';
+ if (isoctal(nextc)) {
+ *dst++ = '0';
+ *dst++ = '0';
+ }
+ return dst;
+ default:
+ if (isgraph(c)) {
+ *dst++ = '\\'; *dst++ = c;
+ return dst;
+ }
+ }
+ }
+ if (isextra || ((c & 0177) == ' ') || (flag & VIS_OCTAL)) {
+ *dst++ = '\\';
+ *dst++ = (u_char)(((u_int32_t)(u_char)c >> 6) & 03) + '0';
+ *dst++ = (u_char)(((u_int32_t)(u_char)c >> 3) & 07) + '0';
+ *dst++ = (c & 07) + '0';
+ } else {
+ if ((flag & VIS_NOSLASH) == 0) *dst++ = '\\';
+ if (c & 0200) {
+ c &= 0177; *dst++ = 'M';
+ }
+ if (iscntrl(c)) {
+ *dst++ = '^';
+ if (c == 0177)
+ *dst++ = '?';
+ else
+ *dst++ = c + '@';
+ } else {
+ *dst++ = '-'; *dst++ = c;
+ }
+ }
+ return dst;
+}
+
+
+/*
+ * svis - visually encode characters, also encoding the characters
+ * pointed to by `extra'
+ */
+char * ROKEN_LIB_FUNCTION
+rk_svis(char *dst, int c, int flag, int nextc, const char *extra)
+{
+ char *nextra = NULL;
+
+ _DIAGASSERT(dst != NULL);
+ _DIAGASSERT(extra != NULL);
+ MAKEEXTRALIST(flag, nextra, extra);
+ if (!nextra) {
+ *dst = '\0'; /* can't create nextra, return "" */
+ return dst;
+ }
+ if (flag & VIS_HTTPSTYLE)
+ dst = do_hvis(dst, c, flag, nextc, nextra);
+ else
+ dst = do_svis(dst, c, flag, nextc, nextra);
+ free(nextra);
+ *dst = '\0';
+ return dst;
+}
+
+
+/*
+ * strsvis, strsvisx - visually encode characters from src into dst
+ *
+ * Extra is a pointer to a \0-terminated list of characters to
+ * be encoded, too. These functions are useful e. g. to
+ * encode strings in such a way so that they are not interpreted
+ * by a shell.
+ *
+ * Dst must be 4 times the size of src to account for possible
+ * expansion. The length of dst, not including the trailing NULL,
+ * is returned.
+ *
+ * Strsvisx encodes exactly len bytes from src into dst.
+ * This is useful for encoding a block of data.
+ */
+int ROKEN_LIB_FUNCTION
+rk_strsvis(char *dst, const char *csrc, int flag, const char *extra)
+{
+ int c;
+ char *start;
+ char *nextra = NULL;
+ const unsigned char *src = (const unsigned char *)csrc;
+
+ _DIAGASSERT(dst != NULL);
+ _DIAGASSERT(src != NULL);
+ _DIAGASSERT(extra != NULL);
+ MAKEEXTRALIST(flag, nextra, extra);
+ if (!nextra) {
+ *dst = '\0'; /* can't create nextra, return "" */
+ return 0;
+ }
+ if (flag & VIS_HTTPSTYLE) {
+ for (start = dst; (c = *src++) != '\0'; /* empty */)
+ dst = do_hvis(dst, c, flag, *src, nextra);
+ } else {
+ for (start = dst; (c = *src++) != '\0'; /* empty */)
+ dst = do_svis(dst, c, flag, *src, nextra);
+ }
+ free(nextra);
+ *dst = '\0';
+ return (dst - start);
+}
+
+
+int ROKEN_LIB_FUNCTION
+rk_strsvisx(char *dst, const char *csrc, size_t len, int flag, const char *extra)
+{
+ unsigned char c;
+ char *start;
+ char *nextra = NULL;
+ const unsigned char *src = (const unsigned char *)csrc;
+
+ _DIAGASSERT(dst != NULL);
+ _DIAGASSERT(src != NULL);
+ _DIAGASSERT(extra != NULL);
+ MAKEEXTRALIST(flag, nextra, extra);
+ if (! nextra) {
+ *dst = '\0'; /* can't create nextra, return "" */
+ return 0;
+ }
+
+ if (flag & VIS_HTTPSTYLE) {
+ for (start = dst; len > 0; len--) {
+ c = *src++;
+ dst = do_hvis(dst, c, flag, len ? *src : '\0', nextra);
+ }
+ } else {
+ for (start = dst; len > 0; len--) {
+ c = *src++;
+ dst = do_svis(dst, c, flag, len ? *src : '\0', nextra);
+ }
+ }
+ free(nextra);
+ *dst = '\0';
+ return (dst - start);
+}
+#endif
+
+#if !HAVE_VIS
+/*
+ * vis - visually encode characters
+ */
+char * ROKEN_LIB_FUNCTION
+rk_vis(char *dst, int c, int flag, int nextc)
+{
+ char *extra = NULL;
+ unsigned char uc = (unsigned char)c;
+
+ _DIAGASSERT(dst != NULL);
+
+ MAKEEXTRALIST(flag, extra, "");
+ if (! extra) {
+ *dst = '\0'; /* can't create extra, return "" */
+ return dst;
+ }
+ if (flag & VIS_HTTPSTYLE)
+ dst = do_hvis(dst, uc, flag, nextc, extra);
+ else
+ dst = do_svis(dst, uc, flag, nextc, extra);
+ free(extra);
+ *dst = '\0';
+ return dst;
+}
+
+
+/*
+ * strvis, strvisx - visually encode characters from src into dst
+ *
+ * Dst must be 4 times the size of src to account for possible
+ * expansion. The length of dst, not including the trailing NULL,
+ * is returned.
+ *
+ * Strvisx encodes exactly len bytes from src into dst.
+ * This is useful for encoding a block of data.
+ */
+int ROKEN_LIB_FUNCTION
+rk_strvis(char *dst, const char *src, int flag)
+{
+ char *extra = NULL;
+ int rv;
+
+ MAKEEXTRALIST(flag, extra, "");
+ if (!extra) {
+ *dst = '\0'; /* can't create extra, return "" */
+ return 0;
+ }
+ rv = strsvis(dst, src, flag, extra);
+ free(extra);
+ return rv;
+}
+
+
+int ROKEN_LIB_FUNCTION
+rk_strvisx(char *dst, const char *src, size_t len, int flag)
+{
+ char *extra = NULL;
+ int rv;
+
+ MAKEEXTRALIST(flag, extra, "");
+ if (!extra) {
+ *dst = '\0'; /* can't create extra, return "" */
+ return 0;
+ }
+ rv = strsvisx(dst, src, len, flag, extra);
+ free(extra);
+ return rv;
+}
+#endif
diff --git a/source4/heimdal/lib/roken/vis.hin b/source4/heimdal/lib/roken/vis.hin
new file mode 100644
index 0000000000..06a250c6d8
--- /dev/null
+++ b/source4/heimdal/lib/roken/vis.hin
@@ -0,0 +1,125 @@
+/* $NetBSD: vis.h,v 1.16 2005/09/13 01:44:32 christos Exp $ */
+
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)vis.h 8.1 (Berkeley) 6/2/93
+ */
+
+#ifndef _VIS_H_
+#define _VIS_H_
+
+#ifndef ROKEN_LIB_FUNCTION
+#ifdef _WIN32
+#define ROKEN_LIB_FUNCTION _stdcall
+#else
+#define ROKEN_LIB_FUNCTION
+#endif
+#endif
+
+#include <sys/types.h>
+
+/*
+ * to select alternate encoding format
+ */
+#define VIS_OCTAL 0x01 /* use octal \ddd format */
+#define VIS_CSTYLE 0x02 /* use \[nrft0..] where appropiate */
+
+/*
+ * to alter set of characters encoded (default is to encode all
+ * non-graphic except space, tab, and newline).
+ */
+#define VIS_SP 0x04 /* also encode space */
+#define VIS_TAB 0x08 /* also encode tab */
+#define VIS_NL 0x10 /* also encode newline */
+#define VIS_WHITE (VIS_SP | VIS_TAB | VIS_NL)
+#define VIS_SAFE 0x20 /* only encode "unsafe" characters */
+
+/*
+ * other
+ */
+#define VIS_NOSLASH 0x40 /* inhibit printing '\' */
+#define VIS_HTTPSTYLE 0x80 /* http-style escape % HEX HEX */
+
+/*
+ * unvis return codes
+ */
+#define UNVIS_VALID 1 /* character valid */
+#define UNVIS_VALIDPUSH 2 /* character valid, push back passed char */
+#define UNVIS_NOCHAR 3 /* valid sequence, no character produced */
+#define UNVIS_SYNBAD -1 /* unrecognized escape sequence */
+#define UNVIS_ERROR -2 /* decoder in unknown state (unrecoverable) */
+
+/*
+ * unvis flags
+ */
+#define UNVIS_END 1 /* no more characters */
+
+#ifdef HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+
+__BEGIN_DECLS
+char * ROKEN_LIB_FUNCTION
+ rk_vis(char *, int, int, int);
+char * ROKEN_LIB_FUNCTION
+ rk_svis(char *, int, int, int, const char *);
+int ROKEN_LIB_FUNCTION
+ rk_strvis(char *, const char *, int);
+int ROKEN_LIB_FUNCTION
+ rk_strsvis(char *, const char *, int, const char *);
+int ROKEN_LIB_FUNCTION
+ rk_strvisx(char *, const char *, size_t, int);
+int ROKEN_LIB_FUNCTION
+ rk_strsvisx(char *, const char *, size_t, int, const char *);
+int ROKEN_LIB_FUNCTION
+ rk_strunvis(char *, const char *);
+int ROKEN_LIB_FUNCTION
+ rk_strunvisx(char *, const char *, int);
+int ROKEN_LIB_FUNCTION
+ rk_unvis(char *, int, int *, int);
+__END_DECLS
+
+#undef vis
+#define vis(a,b,c,d) rk_vis(a,b,c,d)
+#undef svis
+#define svis(a,b,c,d,e) rk_svis(a,b,c,d,e)
+#undef strvis
+#define strvis(a,b,c) rk_strvis(a,b,c)
+#undef strsvis
+#define strsvis(a,b,c,d) rk_strsvis(a,b,c,d)
+#undef strvisx
+#define strvisx(a,b,c,d) rk_strvisx(a,b,c,d)
+#undef strsvisx
+#define strsvisx(a,b,c,d,e) rk_strsvisx(a,b,c,d,e)
+#undef strunvis
+#define strunvis(a,b) rk_strunvis(a,b)
+#undef unvis
+#define unvis(a,b,c,d) rk_unvis(a,b,c,d)
+
+#endif /* !_VIS_H_ */
diff --git a/source4/heimdal/lib/roken/xfree.c b/source4/heimdal/lib/roken/xfree.c
new file mode 100644
index 0000000000..5f6d86ee56
--- /dev/null
+++ b/source4/heimdal/lib/roken/xfree.c
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+RCSID("$Id$");
+#endif
+
+#include <unistd.h>
+
+#include "roken.h"
+
+void ROKEN_LIB_FUNCTION
+rk_xfree (void *buf)
+{
+ free(buf);
+}
diff --git a/source4/heimdal/lib/vers/print_version.c b/source4/heimdal/lib/vers/print_version.c
new file mode 100644
index 0000000000..63e016174a
--- /dev/null
+++ b/source4/heimdal/lib/vers/print_version.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 1998 - 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+RCSID("$Id$");
+#endif
+#include "roken.h"
+
+#include "print_version.h"
+
+void
+print_version(const char *progname)
+{
+ const char *package_list = VERSIONLIST;
+
+ if(progname == NULL)
+ progname = getprogname();
+
+ if(*package_list == '\0')
+ package_list = "no version information";
+ fprintf(stderr, "%s (%s)\n", progname, package_list);
+ fprintf(stderr, "Copyright 1995-2008 Kungliga Tekniska Högskolan\n");
+ fprintf(stderr, "Send bug-reports to %s\n", PACKAGE_BUGREPORT);
+}
diff --git a/source4/heimdal/lib/wind/CompositionExclusions-3.2.0.txt b/source4/heimdal/lib/wind/CompositionExclusions-3.2.0.txt
new file mode 100644
index 0000000000..07a60b8b92
--- /dev/null
+++ b/source4/heimdal/lib/wind/CompositionExclusions-3.2.0.txt
@@ -0,0 +1,176 @@
+# CompositionExclusions-3.2.0.txt
+# Date: 2002-03-19,23:30:28 GMT [MD]
+#
+# This file lists the characters from the UAX #15 Composition Exclusion Table.
+#
+# The format of the comments in this file has been updated since the last version,
+# CompositionExclusions-3.txt. The only substantive change to this file between that
+# version and this one is the addition of U+2ADC FORKING.
+#
+# For more information, see
+# http://www.unicode.org/unicode/reports/tr15/#Primary Exclusion List Table
+# ================================================
+
+# (1) Script Specifics
+# This list of characters cannot be derived from the UnicodeData file.
+# ================================================
+
+0958 # DEVANAGARI LETTER QA
+0959 # DEVANAGARI LETTER KHHA
+095A # DEVANAGARI LETTER GHHA
+095B # DEVANAGARI LETTER ZA
+095C # DEVANAGARI LETTER DDDHA
+095D # DEVANAGARI LETTER RHA
+095E # DEVANAGARI LETTER FA
+095F # DEVANAGARI LETTER YYA
+09DC # BENGALI LETTER RRA
+09DD # BENGALI LETTER RHA
+09DF # BENGALI LETTER YYA
+0A33 # GURMUKHI LETTER LLA
+0A36 # GURMUKHI LETTER SHA
+0A59 # GURMUKHI LETTER KHHA
+0A5A # GURMUKHI LETTER GHHA
+0A5B # GURMUKHI LETTER ZA
+0A5E # GURMUKHI LETTER FA
+0B5C # ORIYA LETTER RRA
+0B5D # ORIYA LETTER RHA
+0F43 # TIBETAN LETTER GHA
+0F4D # TIBETAN LETTER DDHA
+0F52 # TIBETAN LETTER DHA
+0F57 # TIBETAN LETTER BHA
+0F5C # TIBETAN LETTER DZHA
+0F69 # TIBETAN LETTER KSSA
+0F76 # TIBETAN VOWEL SIGN VOCALIC R
+0F78 # TIBETAN VOWEL SIGN VOCALIC L
+0F93 # TIBETAN SUBJOINED LETTER GHA
+0F9D # TIBETAN SUBJOINED LETTER DDHA
+0FA2 # TIBETAN SUBJOINED LETTER DHA
+0FA7 # TIBETAN SUBJOINED LETTER BHA
+0FAC # TIBETAN SUBJOINED LETTER DZHA
+0FB9 # TIBETAN SUBJOINED LETTER KSSA
+FB1D # HEBREW LETTER YOD WITH HIRIQ
+FB1F # HEBREW LIGATURE YIDDISH YOD YOD PATAH
+FB2A # HEBREW LETTER SHIN WITH SHIN DOT
+FB2B # HEBREW LETTER SHIN WITH SIN DOT
+FB2C # HEBREW LETTER SHIN WITH DAGESH AND SHIN DOT
+FB2D # HEBREW LETTER SHIN WITH DAGESH AND SIN DOT
+FB2E # HEBREW LETTER ALEF WITH PATAH
+FB2F # HEBREW LETTER ALEF WITH QAMATS
+FB30 # HEBREW LETTER ALEF WITH MAPIQ
+FB31 # HEBREW LETTER BET WITH DAGESH
+FB32 # HEBREW LETTER GIMEL WITH DAGESH
+FB33 # HEBREW LETTER DALET WITH DAGESH
+FB34 # HEBREW LETTER HE WITH MAPIQ
+FB35 # HEBREW LETTER VAV WITH DAGESH
+FB36 # HEBREW LETTER ZAYIN WITH DAGESH
+FB38 # HEBREW LETTER TET WITH DAGESH
+FB39 # HEBREW LETTER YOD WITH DAGESH
+FB3A # HEBREW LETTER FINAL KAF WITH DAGESH
+FB3B # HEBREW LETTER KAF WITH DAGESH
+FB3C # HEBREW LETTER LAMED WITH DAGESH
+FB3E # HEBREW LETTER MEM WITH DAGESH
+FB40 # HEBREW LETTER NUN WITH DAGESH
+FB41 # HEBREW LETTER SAMEKH WITH DAGESH
+FB43 # HEBREW LETTER FINAL PE WITH DAGESH
+FB44 # HEBREW LETTER PE WITH DAGESH
+FB46 # HEBREW LETTER TSADI WITH DAGESH
+FB47 # HEBREW LETTER QOF WITH DAGESH
+FB48 # HEBREW LETTER RESH WITH DAGESH
+FB49 # HEBREW LETTER SHIN WITH DAGESH
+FB4A # HEBREW LETTER TAV WITH DAGESH
+FB4B # HEBREW LETTER VAV WITH HOLAM
+FB4C # HEBREW LETTER BET WITH RAFE
+FB4D # HEBREW LETTER KAF WITH RAFE
+FB4E # HEBREW LETTER PE WITH RAFE
+
+# Total code points: 67
+
+# ================================================
+# (2) Post Composition Version precomposed characters
+# These characters cannot be derived solely from the UnicodeData.txt file
+# in this version of Unicode.
+# ================================================
+
+2ADC # FORKING
+1D15E # MUSICAL SYMBOL HALF NOTE
+1D15F # MUSICAL SYMBOL QUARTER NOTE
+1D160 # MUSICAL SYMBOL EIGHTH NOTE
+1D161 # MUSICAL SYMBOL SIXTEENTH NOTE
+1D162 # MUSICAL SYMBOL THIRTY-SECOND NOTE
+1D163 # MUSICAL SYMBOL SIXTY-FOURTH NOTE
+1D164 # MUSICAL SYMBOL ONE HUNDRED TWENTY-EIGHTH NOTE
+1D1BB # MUSICAL SYMBOL MINIMA
+1D1BC # MUSICAL SYMBOL MINIMA BLACK
+1D1BD # MUSICAL SYMBOL SEMIMINIMA WHITE
+1D1BE # MUSICAL SYMBOL SEMIMINIMA BLACK
+1D1BF # MUSICAL SYMBOL FUSA WHITE
+1D1C0 # MUSICAL SYMBOL FUSA BLACK
+
+# Total code points: 14
+
+# ================================================
+# (3) Singleton Decompositions
+# These characters can be derived from the UnicodeData file
+# by including all characters whose canonical decomposition
+# consists of a single character.
+# These characters are simply quoted here for reference.
+# ================================================
+
+# 0340..0341 [2] COMBINING GRAVE TONE MARK..COMBINING ACUTE TONE MARK
+# 0343 COMBINING GREEK KORONIS
+# 0374 GREEK NUMERAL SIGN
+# 037E GREEK QUESTION MARK
+# 0387 GREEK ANO TELEIA
+# 1F71 GREEK SMALL LETTER ALPHA WITH OXIA
+# 1F73 GREEK SMALL LETTER EPSILON WITH OXIA
+# 1F75 GREEK SMALL LETTER ETA WITH OXIA
+# 1F77 GREEK SMALL LETTER IOTA WITH OXIA
+# 1F79 GREEK SMALL LETTER OMICRON WITH OXIA
+# 1F7B GREEK SMALL LETTER UPSILON WITH OXIA
+# 1F7D GREEK SMALL LETTER OMEGA WITH OXIA
+# 1FBB GREEK CAPITAL LETTER ALPHA WITH OXIA
+# 1FBE GREEK PROSGEGRAMMENI
+# 1FC9 GREEK CAPITAL LETTER EPSILON WITH OXIA
+# 1FCB GREEK CAPITAL LETTER ETA WITH OXIA
+# 1FD3 GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA
+# 1FDB GREEK CAPITAL LETTER IOTA WITH OXIA
+# 1FE3 GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND OXIA
+# 1FEB GREEK CAPITAL LETTER UPSILON WITH OXIA
+# 1FEE..1FEF [2] GREEK DIALYTIKA AND OXIA..GREEK VARIA
+# 1FF9 GREEK CAPITAL LETTER OMICRON WITH OXIA
+# 1FFB GREEK CAPITAL LETTER OMEGA WITH OXIA
+# 1FFD GREEK OXIA
+# 2000..2001 [2] EN QUAD..EM QUAD
+# 2126 OHM SIGN
+# 212A..212B [2] KELVIN SIGN..ANGSTROM SIGN
+# 2329 LEFT-POINTING ANGLE BRACKET
+# 232A RIGHT-POINTING ANGLE BRACKET
+# F900..FA0D [270] CJK COMPATIBILITY IDEOGRAPH-F900..CJK COMPATIBILITY IDEOGRAPH-FA0D
+# FA10 CJK COMPATIBILITY IDEOGRAPH-FA10
+# FA12 CJK COMPATIBILITY IDEOGRAPH-FA12
+# FA15..FA1E [10] CJK COMPATIBILITY IDEOGRAPH-FA15..CJK COMPATIBILITY IDEOGRAPH-FA1E
+# FA20 CJK COMPATIBILITY IDEOGRAPH-FA20
+# FA22 CJK COMPATIBILITY IDEOGRAPH-FA22
+# FA25..FA26 [2] CJK COMPATIBILITY IDEOGRAPH-FA25..CJK COMPATIBILITY IDEOGRAPH-FA26
+# FA2A..FA2D [4] CJK COMPATIBILITY IDEOGRAPH-FA2A..CJK COMPATIBILITY IDEOGRAPH-FA2D
+# FA30..FA6A [59] CJK COMPATIBILITY IDEOGRAPH-FA30..CJK COMPATIBILITY IDEOGRAPH-FA6A
+# 2F800..2FA1D [542] CJK COMPATIBILITY IDEOGRAPH-2F800..CJK COMPATIBILITY IDEOGRAPH-2FA1D
+
+# Total code points: 924
+
+# ================================================
+# (4) Non-Starter Decompositions
+# These characters can be derived from the UnicodeData file
+# by including all characters whose canonical decomposition consists
+# of a sequence of characters, the first of which has a non-zero
+# combining class.
+# These characters are simply quoted here for reference.
+# ================================================
+
+# 0344 COMBINING GREEK DIALYTIKA TONOS
+# 0F73 TIBETAN VOWEL SIGN II
+# 0F75 TIBETAN VOWEL SIGN UU
+# 0F81 TIBETAN VOWEL SIGN REVERSED II
+
+# Total code points: 4
+
diff --git a/source4/heimdal/lib/wind/DerivedNormalizationProps.txt b/source4/heimdal/lib/wind/DerivedNormalizationProps.txt
new file mode 100644
index 0000000000..2d4f0a6e5d
--- /dev/null
+++ b/source4/heimdal/lib/wind/DerivedNormalizationProps.txt
@@ -0,0 +1,2574 @@
+# DerivedNormalizationProps-4.0.1.txt
+# Date: 2004-03-02, 02:42:17 GMT [MD]
+#
+# Unicode Character Database
+# Copyright (c) 1991-2004 Unicode, Inc.
+# For terms of use, see http://www.unicode.org/terms_of_use.html
+# For documentation, see UCD.html
+
+# ================================================
+
+# Derived Property: FC_NFKC_Closure
+# Generated from computing: b = NFKC(Fold(a)); c = NFKC(Fold(b));
+# Then if (c != b) add the mapping from a to c to the set of
+# mappings that constitute the FC_NFKC_Closure list
+# Uses the full case folding from CaseFolding.txt, without the T option.
+
+037A ; FC_NFKC; 0020 03B9 # Lm GREEK YPOGEGRAMMENI
+03D2 ; FC_NFKC; 03C5 # L& GREEK UPSILON WITH HOOK SYMBOL
+03D3 ; FC_NFKC; 03CD # L& GREEK UPSILON WITH ACUTE AND HOOK SYMBOL
+03D4 ; FC_NFKC; 03CB # L& GREEK UPSILON WITH DIAERESIS AND HOOK SYMBOL
+03F2 ; FC_NFKC; 03C3 # L& GREEK LUNATE SIGMA SYMBOL
+03F9 ; FC_NFKC; 03C3 # L& GREEK CAPITAL LUNATE SIGMA SYMBOL
+1D2C ; FC_NFKC; 0061 # Lm MODIFIER LETTER CAPITAL A
+1D2D ; FC_NFKC; 00E6 # Lm MODIFIER LETTER CAPITAL AE
+1D2E ; FC_NFKC; 0062 # Lm MODIFIER LETTER CAPITAL B
+1D30 ; FC_NFKC; 0064 # Lm MODIFIER LETTER CAPITAL D
+1D31 ; FC_NFKC; 0065 # Lm MODIFIER LETTER CAPITAL E
+1D32 ; FC_NFKC; 01DD # Lm MODIFIER LETTER CAPITAL REVERSED E
+1D33 ; FC_NFKC; 0067 # Lm MODIFIER LETTER CAPITAL G
+1D34 ; FC_NFKC; 0068 # Lm MODIFIER LETTER CAPITAL H
+1D35 ; FC_NFKC; 0069 # Lm MODIFIER LETTER CAPITAL I
+1D36 ; FC_NFKC; 006A # Lm MODIFIER LETTER CAPITAL J
+1D37 ; FC_NFKC; 006B # Lm MODIFIER LETTER CAPITAL K
+1D38 ; FC_NFKC; 006C # Lm MODIFIER LETTER CAPITAL L
+1D39 ; FC_NFKC; 006D # Lm MODIFIER LETTER CAPITAL M
+1D3A ; FC_NFKC; 006E # Lm MODIFIER LETTER CAPITAL N
+1D3C ; FC_NFKC; 006F # Lm MODIFIER LETTER CAPITAL O
+1D3D ; FC_NFKC; 0223 # Lm MODIFIER LETTER CAPITAL OU
+1D3E ; FC_NFKC; 0070 # Lm MODIFIER LETTER CAPITAL P
+1D3F ; FC_NFKC; 0072 # Lm MODIFIER LETTER CAPITAL R
+1D40 ; FC_NFKC; 0074 # Lm MODIFIER LETTER CAPITAL T
+1D41 ; FC_NFKC; 0075 # Lm MODIFIER LETTER CAPITAL U
+1D42 ; FC_NFKC; 0077 # Lm MODIFIER LETTER CAPITAL W
+20A8 ; FC_NFKC; 0072 0073 # Sc RUPEE SIGN
+2102 ; FC_NFKC; 0063 # L& DOUBLE-STRUCK CAPITAL C
+2103 ; FC_NFKC; 00B0 0063 # So DEGREE CELSIUS
+2107 ; FC_NFKC; 025B # L& EULER CONSTANT
+2109 ; FC_NFKC; 00B0 0066 # So DEGREE FAHRENHEIT
+210B ; FC_NFKC; 0068 # L& SCRIPT CAPITAL H
+210C ; FC_NFKC; 0068 # L& BLACK-LETTER CAPITAL H
+210D ; FC_NFKC; 0068 # L& DOUBLE-STRUCK CAPITAL H
+2110 ; FC_NFKC; 0069 # L& SCRIPT CAPITAL I
+2111 ; FC_NFKC; 0069 # L& BLACK-LETTER CAPITAL I
+2112 ; FC_NFKC; 006C # L& SCRIPT CAPITAL L
+2115 ; FC_NFKC; 006E # L& DOUBLE-STRUCK CAPITAL N
+2116 ; FC_NFKC; 006E 006F # So NUMERO SIGN
+2119 ; FC_NFKC; 0070 # L& DOUBLE-STRUCK CAPITAL P
+211A ; FC_NFKC; 0071 # L& DOUBLE-STRUCK CAPITAL Q
+211B ; FC_NFKC; 0072 # L& SCRIPT CAPITAL R
+211C ; FC_NFKC; 0072 # L& BLACK-LETTER CAPITAL R
+211D ; FC_NFKC; 0072 # L& DOUBLE-STRUCK CAPITAL R
+2120 ; FC_NFKC; 0073 006D # So SERVICE MARK
+2121 ; FC_NFKC; 0074 0065 006C # So TELEPHONE SIGN
+2122 ; FC_NFKC; 0074 006D # So TRADE MARK SIGN
+2124 ; FC_NFKC; 007A # L& DOUBLE-STRUCK CAPITAL Z
+2128 ; FC_NFKC; 007A # L& BLACK-LETTER CAPITAL Z
+212C ; FC_NFKC; 0062 # L& SCRIPT CAPITAL B
+212D ; FC_NFKC; 0063 # L& BLACK-LETTER CAPITAL C
+2130 ; FC_NFKC; 0065 # L& SCRIPT CAPITAL E
+2131 ; FC_NFKC; 0066 # L& SCRIPT CAPITAL F
+2133 ; FC_NFKC; 006D # L& SCRIPT CAPITAL M
+213B ; FC_NFKC; 0066 0061 0078 # So FACSIMILE SIGN
+213E ; FC_NFKC; 03B3 # L& DOUBLE-STRUCK CAPITAL GAMMA
+213F ; FC_NFKC; 03C0 # L& DOUBLE-STRUCK CAPITAL PI
+2145 ; FC_NFKC; 0064 # L& DOUBLE-STRUCK ITALIC CAPITAL D
+3250 ; FC_NFKC; 0070 0074 0065 # So PARTNERSHIP SIGN
+32CC ; FC_NFKC; 0068 0067 # So SQUARE HG
+32CE ; FC_NFKC; 0065 0076 # So SQUARE EV
+32CF ; FC_NFKC; 006C 0074 0064 # So LIMITED LIABILITY SIGN
+3371 ; FC_NFKC; 0068 0070 0061 # So SQUARE HPA
+3373 ; FC_NFKC; 0061 0075 # So SQUARE AU
+3375 ; FC_NFKC; 006F 0076 # So SQUARE OV
+337A ; FC_NFKC; 0069 0075 # So SQUARE IU
+3380 ; FC_NFKC; 0070 0061 # So SQUARE PA AMPS
+3381 ; FC_NFKC; 006E 0061 # So SQUARE NA
+3382 ; FC_NFKC; 03BC 0061 # So SQUARE MU A
+3383 ; FC_NFKC; 006D 0061 # So SQUARE MA
+3384 ; FC_NFKC; 006B 0061 # So SQUARE KA
+3385 ; FC_NFKC; 006B 0062 # So SQUARE KB
+3386 ; FC_NFKC; 006D 0062 # So SQUARE MB
+3387 ; FC_NFKC; 0067 0062 # So SQUARE GB
+338A ; FC_NFKC; 0070 0066 # So SQUARE PF
+338B ; FC_NFKC; 006E 0066 # So SQUARE NF
+338C ; FC_NFKC; 03BC 0066 # So SQUARE MU F
+3390 ; FC_NFKC; 0068 007A # So SQUARE HZ
+3391 ; FC_NFKC; 006B 0068 007A # So SQUARE KHZ
+3392 ; FC_NFKC; 006D 0068 007A # So SQUARE MHZ
+3393 ; FC_NFKC; 0067 0068 007A # So SQUARE GHZ
+3394 ; FC_NFKC; 0074 0068 007A # So SQUARE THZ
+33A9 ; FC_NFKC; 0070 0061 # So SQUARE PA
+33AA ; FC_NFKC; 006B 0070 0061 # So SQUARE KPA
+33AB ; FC_NFKC; 006D 0070 0061 # So SQUARE MPA
+33AC ; FC_NFKC; 0067 0070 0061 # So SQUARE GPA
+33B4 ; FC_NFKC; 0070 0076 # So SQUARE PV
+33B5 ; FC_NFKC; 006E 0076 # So SQUARE NV
+33B6 ; FC_NFKC; 03BC 0076 # So SQUARE MU V
+33B7 ; FC_NFKC; 006D 0076 # So SQUARE MV
+33B8 ; FC_NFKC; 006B 0076 # So SQUARE KV
+33B9 ; FC_NFKC; 006D 0076 # So SQUARE MV MEGA
+33BA ; FC_NFKC; 0070 0077 # So SQUARE PW
+33BB ; FC_NFKC; 006E 0077 # So SQUARE NW
+33BC ; FC_NFKC; 03BC 0077 # So SQUARE MU W
+33BD ; FC_NFKC; 006D 0077 # So SQUARE MW
+33BE ; FC_NFKC; 006B 0077 # So SQUARE KW
+33BF ; FC_NFKC; 006D 0077 # So SQUARE MW MEGA
+33C0 ; FC_NFKC; 006B 03C9 # So SQUARE K OHM
+33C1 ; FC_NFKC; 006D 03C9 # So SQUARE M OHM
+33C3 ; FC_NFKC; 0062 0071 # So SQUARE BQ
+33C6 ; FC_NFKC; 0063 2215 006B 0067 # So SQUARE C OVER KG
+33C7 ; FC_NFKC; 0063 006F 002E # So SQUARE CO
+33C8 ; FC_NFKC; 0064 0062 # So SQUARE DB
+33C9 ; FC_NFKC; 0067 0079 # So SQUARE GY
+33CB ; FC_NFKC; 0068 0070 # So SQUARE HP
+33CD ; FC_NFKC; 006B 006B # So SQUARE KK
+33CE ; FC_NFKC; 006B 006D # So SQUARE KM CAPITAL
+33D7 ; FC_NFKC; 0070 0068 # So SQUARE PH
+33D9 ; FC_NFKC; 0070 0070 006D # So SQUARE PPM
+33DA ; FC_NFKC; 0070 0072 # So SQUARE PR
+33DC ; FC_NFKC; 0073 0076 # So SQUARE SV
+33DD ; FC_NFKC; 0077 0062 # So SQUARE WB
+33DE ; FC_NFKC; 0076 2215 006D # So SQUARE V OVER M
+33DF ; FC_NFKC; 0061 2215 006D # So SQUARE A OVER M
+1D400 ; FC_NFKC; 0061 # L& MATHEMATICAL BOLD CAPITAL A
+1D401 ; FC_NFKC; 0062 # L& MATHEMATICAL BOLD CAPITAL B
+1D402 ; FC_NFKC; 0063 # L& MATHEMATICAL BOLD CAPITAL C
+1D403 ; FC_NFKC; 0064 # L& MATHEMATICAL BOLD CAPITAL D
+1D404 ; FC_NFKC; 0065 # L& MATHEMATICAL BOLD CAPITAL E
+1D405 ; FC_NFKC; 0066 # L& MATHEMATICAL BOLD CAPITAL F
+1D406 ; FC_NFKC; 0067 # L& MATHEMATICAL BOLD CAPITAL G
+1D407 ; FC_NFKC; 0068 # L& MATHEMATICAL BOLD CAPITAL H
+1D408 ; FC_NFKC; 0069 # L& MATHEMATICAL BOLD CAPITAL I
+1D409 ; FC_NFKC; 006A # L& MATHEMATICAL BOLD CAPITAL J
+1D40A ; FC_NFKC; 006B # L& MATHEMATICAL BOLD CAPITAL K
+1D40B ; FC_NFKC; 006C # L& MATHEMATICAL BOLD CAPITAL L
+1D40C ; FC_NFKC; 006D # L& MATHEMATICAL BOLD CAPITAL M
+1D40D ; FC_NFKC; 006E # L& MATHEMATICAL BOLD CAPITAL N
+1D40E ; FC_NFKC; 006F # L& MATHEMATICAL BOLD CAPITAL O
+1D40F ; FC_NFKC; 0070 # L& MATHEMATICAL BOLD CAPITAL P
+1D410 ; FC_NFKC; 0071 # L& MATHEMATICAL BOLD CAPITAL Q
+1D411 ; FC_NFKC; 0072 # L& MATHEMATICAL BOLD CAPITAL R
+1D412 ; FC_NFKC; 0073 # L& MATHEMATICAL BOLD CAPITAL S
+1D413 ; FC_NFKC; 0074 # L& MATHEMATICAL BOLD CAPITAL T
+1D414 ; FC_NFKC; 0075 # L& MATHEMATICAL BOLD CAPITAL U
+1D415 ; FC_NFKC; 0076 # L& MATHEMATICAL BOLD CAPITAL V
+1D416 ; FC_NFKC; 0077 # L& MATHEMATICAL BOLD CAPITAL W
+1D417 ; FC_NFKC; 0078 # L& MATHEMATICAL BOLD CAPITAL X
+1D418 ; FC_NFKC; 0079 # L& MATHEMATICAL BOLD CAPITAL Y
+1D419 ; FC_NFKC; 007A # L& MATHEMATICAL BOLD CAPITAL Z
+1D434 ; FC_NFKC; 0061 # L& MATHEMATICAL ITALIC CAPITAL A
+1D435 ; FC_NFKC; 0062 # L& MATHEMATICAL ITALIC CAPITAL B
+1D436 ; FC_NFKC; 0063 # L& MATHEMATICAL ITALIC CAPITAL C
+1D437 ; FC_NFKC; 0064 # L& MATHEMATICAL ITALIC CAPITAL D
+1D438 ; FC_NFKC; 0065 # L& MATHEMATICAL ITALIC CAPITAL E
+1D439 ; FC_NFKC; 0066 # L& MATHEMATICAL ITALIC CAPITAL F
+1D43A ; FC_NFKC; 0067 # L& MATHEMATICAL ITALIC CAPITAL G
+1D43B ; FC_NFKC; 0068 # L& MATHEMATICAL ITALIC CAPITAL H
+1D43C ; FC_NFKC; 0069 # L& MATHEMATICAL ITALIC CAPITAL I
+1D43D ; FC_NFKC; 006A # L& MATHEMATICAL ITALIC CAPITAL J
+1D43E ; FC_NFKC; 006B # L& MATHEMATICAL ITALIC CAPITAL K
+1D43F ; FC_NFKC; 006C # L& MATHEMATICAL ITALIC CAPITAL L
+1D440 ; FC_NFKC; 006D # L& MATHEMATICAL ITALIC CAPITAL M
+1D441 ; FC_NFKC; 006E # L& MATHEMATICAL ITALIC CAPITAL N
+1D442 ; FC_NFKC; 006F # L& MATHEMATICAL ITALIC CAPITAL O
+1D443 ; FC_NFKC; 0070 # L& MATHEMATICAL ITALIC CAPITAL P
+1D444 ; FC_NFKC; 0071 # L& MATHEMATICAL ITALIC CAPITAL Q
+1D445 ; FC_NFKC; 0072 # L& MATHEMATICAL ITALIC CAPITAL R
+1D446 ; FC_NFKC; 0073 # L& MATHEMATICAL ITALIC CAPITAL S
+1D447 ; FC_NFKC; 0074 # L& MATHEMATICAL ITALIC CAPITAL T
+1D448 ; FC_NFKC; 0075 # L& MATHEMATICAL ITALIC CAPITAL U
+1D449 ; FC_NFKC; 0076 # L& MATHEMATICAL ITALIC CAPITAL V
+1D44A ; FC_NFKC; 0077 # L& MATHEMATICAL ITALIC CAPITAL W
+1D44B ; FC_NFKC; 0078 # L& MATHEMATICAL ITALIC CAPITAL X
+1D44C ; FC_NFKC; 0079 # L& MATHEMATICAL ITALIC CAPITAL Y
+1D44D ; FC_NFKC; 007A # L& MATHEMATICAL ITALIC CAPITAL Z
+1D468 ; FC_NFKC; 0061 # L& MATHEMATICAL BOLD ITALIC CAPITAL A
+1D469 ; FC_NFKC; 0062 # L& MATHEMATICAL BOLD ITALIC CAPITAL B
+1D46A ; FC_NFKC; 0063 # L& MATHEMATICAL BOLD ITALIC CAPITAL C
+1D46B ; FC_NFKC; 0064 # L& MATHEMATICAL BOLD ITALIC CAPITAL D
+1D46C ; FC_NFKC; 0065 # L& MATHEMATICAL BOLD ITALIC CAPITAL E
+1D46D ; FC_NFKC; 0066 # L& MATHEMATICAL BOLD ITALIC CAPITAL F
+1D46E ; FC_NFKC; 0067 # L& MATHEMATICAL BOLD ITALIC CAPITAL G
+1D46F ; FC_NFKC; 0068 # L& MATHEMATICAL BOLD ITALIC CAPITAL H
+1D470 ; FC_NFKC; 0069 # L& MATHEMATICAL BOLD ITALIC CAPITAL I
+1D471 ; FC_NFKC; 006A # L& MATHEMATICAL BOLD ITALIC CAPITAL J
+1D472 ; FC_NFKC; 006B # L& MATHEMATICAL BOLD ITALIC CAPITAL K
+1D473 ; FC_NFKC; 006C # L& MATHEMATICAL BOLD ITALIC CAPITAL L
+1D474 ; FC_NFKC; 006D # L& MATHEMATICAL BOLD ITALIC CAPITAL M
+1D475 ; FC_NFKC; 006E # L& MATHEMATICAL BOLD ITALIC CAPITAL N
+1D476 ; FC_NFKC; 006F # L& MATHEMATICAL BOLD ITALIC CAPITAL O
+1D477 ; FC_NFKC; 0070 # L& MATHEMATICAL BOLD ITALIC CAPITAL P
+1D478 ; FC_NFKC; 0071 # L& MATHEMATICAL BOLD ITALIC CAPITAL Q
+1D479 ; FC_NFKC; 0072 # L& MATHEMATICAL BOLD ITALIC CAPITAL R
+1D47A ; FC_NFKC; 0073 # L& MATHEMATICAL BOLD ITALIC CAPITAL S
+1D47B ; FC_NFKC; 0074 # L& MATHEMATICAL BOLD ITALIC CAPITAL T
+1D47C ; FC_NFKC; 0075 # L& MATHEMATICAL BOLD ITALIC CAPITAL U
+1D47D ; FC_NFKC; 0076 # L& MATHEMATICAL BOLD ITALIC CAPITAL V
+1D47E ; FC_NFKC; 0077 # L& MATHEMATICAL BOLD ITALIC CAPITAL W
+1D47F ; FC_NFKC; 0078 # L& MATHEMATICAL BOLD ITALIC CAPITAL X
+1D480 ; FC_NFKC; 0079 # L& MATHEMATICAL BOLD ITALIC CAPITAL Y
+1D481 ; FC_NFKC; 007A # L& MATHEMATICAL BOLD ITALIC CAPITAL Z
+1D49C ; FC_NFKC; 0061 # L& MATHEMATICAL SCRIPT CAPITAL A
+1D49E ; FC_NFKC; 0063 # L& MATHEMATICAL SCRIPT CAPITAL C
+1D49F ; FC_NFKC; 0064 # L& MATHEMATICAL SCRIPT CAPITAL D
+1D4A2 ; FC_NFKC; 0067 # L& MATHEMATICAL SCRIPT CAPITAL G
+1D4A5 ; FC_NFKC; 006A # L& MATHEMATICAL SCRIPT CAPITAL J
+1D4A6 ; FC_NFKC; 006B # L& MATHEMATICAL SCRIPT CAPITAL K
+1D4A9 ; FC_NFKC; 006E # L& MATHEMATICAL SCRIPT CAPITAL N
+1D4AA ; FC_NFKC; 006F # L& MATHEMATICAL SCRIPT CAPITAL O
+1D4AB ; FC_NFKC; 0070 # L& MATHEMATICAL SCRIPT CAPITAL P
+1D4AC ; FC_NFKC; 0071 # L& MATHEMATICAL SCRIPT CAPITAL Q
+1D4AE ; FC_NFKC; 0073 # L& MATHEMATICAL SCRIPT CAPITAL S
+1D4AF ; FC_NFKC; 0074 # L& MATHEMATICAL SCRIPT CAPITAL T
+1D4B0 ; FC_NFKC; 0075 # L& MATHEMATICAL SCRIPT CAPITAL U
+1D4B1 ; FC_NFKC; 0076 # L& MATHEMATICAL SCRIPT CAPITAL V
+1D4B2 ; FC_NFKC; 0077 # L& MATHEMATICAL SCRIPT CAPITAL W
+1D4B3 ; FC_NFKC; 0078 # L& MATHEMATICAL SCRIPT CAPITAL X
+1D4B4 ; FC_NFKC; 0079 # L& MATHEMATICAL SCRIPT CAPITAL Y
+1D4B5 ; FC_NFKC; 007A # L& MATHEMATICAL SCRIPT CAPITAL Z
+1D4D0 ; FC_NFKC; 0061 # L& MATHEMATICAL BOLD SCRIPT CAPITAL A
+1D4D1 ; FC_NFKC; 0062 # L& MATHEMATICAL BOLD SCRIPT CAPITAL B
+1D4D2 ; FC_NFKC; 0063 # L& MATHEMATICAL BOLD SCRIPT CAPITAL C
+1D4D3 ; FC_NFKC; 0064 # L& MATHEMATICAL BOLD SCRIPT CAPITAL D
+1D4D4 ; FC_NFKC; 0065 # L& MATHEMATICAL BOLD SCRIPT CAPITAL E
+1D4D5 ; FC_NFKC; 0066 # L& MATHEMATICAL BOLD SCRIPT CAPITAL F
+1D4D6 ; FC_NFKC; 0067 # L& MATHEMATICAL BOLD SCRIPT CAPITAL G
+1D4D7 ; FC_NFKC; 0068 # L& MATHEMATICAL BOLD SCRIPT CAPITAL H
+1D4D8 ; FC_NFKC; 0069 # L& MATHEMATICAL BOLD SCRIPT CAPITAL I
+1D4D9 ; FC_NFKC; 006A # L& MATHEMATICAL BOLD SCRIPT CAPITAL J
+1D4DA ; FC_NFKC; 006B # L& MATHEMATICAL BOLD SCRIPT CAPITAL K
+1D4DB ; FC_NFKC; 006C # L& MATHEMATICAL BOLD SCRIPT CAPITAL L
+1D4DC ; FC_NFKC; 006D # L& MATHEMATICAL BOLD SCRIPT CAPITAL M
+1D4DD ; FC_NFKC; 006E # L& MATHEMATICAL BOLD SCRIPT CAPITAL N
+1D4DE ; FC_NFKC; 006F # L& MATHEMATICAL BOLD SCRIPT CAPITAL O
+1D4DF ; FC_NFKC; 0070 # L& MATHEMATICAL BOLD SCRIPT CAPITAL P
+1D4E0 ; FC_NFKC; 0071 # L& MATHEMATICAL BOLD SCRIPT CAPITAL Q
+1D4E1 ; FC_NFKC; 0072 # L& MATHEMATICAL BOLD SCRIPT CAPITAL R
+1D4E2 ; FC_NFKC; 0073 # L& MATHEMATICAL BOLD SCRIPT CAPITAL S
+1D4E3 ; FC_NFKC; 0074 # L& MATHEMATICAL BOLD SCRIPT CAPITAL T
+1D4E4 ; FC_NFKC; 0075 # L& MATHEMATICAL BOLD SCRIPT CAPITAL U
+1D4E5 ; FC_NFKC; 0076 # L& MATHEMATICAL BOLD SCRIPT CAPITAL V
+1D4E6 ; FC_NFKC; 0077 # L& MATHEMATICAL BOLD SCRIPT CAPITAL W
+1D4E7 ; FC_NFKC; 0078 # L& MATHEMATICAL BOLD SCRIPT CAPITAL X
+1D4E8 ; FC_NFKC; 0079 # L& MATHEMATICAL BOLD SCRIPT CAPITAL Y
+1D4E9 ; FC_NFKC; 007A # L& MATHEMATICAL BOLD SCRIPT CAPITAL Z
+1D504 ; FC_NFKC; 0061 # L& MATHEMATICAL FRAKTUR CAPITAL A
+1D505 ; FC_NFKC; 0062 # L& MATHEMATICAL FRAKTUR CAPITAL B
+1D507 ; FC_NFKC; 0064 # L& MATHEMATICAL FRAKTUR CAPITAL D
+1D508 ; FC_NFKC; 0065 # L& MATHEMATICAL FRAKTUR CAPITAL E
+1D509 ; FC_NFKC; 0066 # L& MATHEMATICAL FRAKTUR CAPITAL F
+1D50A ; FC_NFKC; 0067 # L& MATHEMATICAL FRAKTUR CAPITAL G
+1D50D ; FC_NFKC; 006A # L& MATHEMATICAL FRAKTUR CAPITAL J
+1D50E ; FC_NFKC; 006B # L& MATHEMATICAL FRAKTUR CAPITAL K
+1D50F ; FC_NFKC; 006C # L& MATHEMATICAL FRAKTUR CAPITAL L
+1D510 ; FC_NFKC; 006D # L& MATHEMATICAL FRAKTUR CAPITAL M
+1D511 ; FC_NFKC; 006E # L& MATHEMATICAL FRAKTUR CAPITAL N
+1D512 ; FC_NFKC; 006F # L& MATHEMATICAL FRAKTUR CAPITAL O
+1D513 ; FC_NFKC; 0070 # L& MATHEMATICAL FRAKTUR CAPITAL P
+1D514 ; FC_NFKC; 0071 # L& MATHEMATICAL FRAKTUR CAPITAL Q
+1D516 ; FC_NFKC; 0073 # L& MATHEMATICAL FRAKTUR CAPITAL S
+1D517 ; FC_NFKC; 0074 # L& MATHEMATICAL FRAKTUR CAPITAL T
+1D518 ; FC_NFKC; 0075 # L& MATHEMATICAL FRAKTUR CAPITAL U
+1D519 ; FC_NFKC; 0076 # L& MATHEMATICAL FRAKTUR CAPITAL V
+1D51A ; FC_NFKC; 0077 # L& MATHEMATICAL FRAKTUR CAPITAL W
+1D51B ; FC_NFKC; 0078 # L& MATHEMATICAL FRAKTUR CAPITAL X
+1D51C ; FC_NFKC; 0079 # L& MATHEMATICAL FRAKTUR CAPITAL Y
+1D538 ; FC_NFKC; 0061 # L& MATHEMATICAL DOUBLE-STRUCK CAPITAL A
+1D539 ; FC_NFKC; 0062 # L& MATHEMATICAL DOUBLE-STRUCK CAPITAL B
+1D53B ; FC_NFKC; 0064 # L& MATHEMATICAL DOUBLE-STRUCK CAPITAL D
+1D53C ; FC_NFKC; 0065 # L& MATHEMATICAL DOUBLE-STRUCK CAPITAL E
+1D53D ; FC_NFKC; 0066 # L& MATHEMATICAL DOUBLE-STRUCK CAPITAL F
+1D53E ; FC_NFKC; 0067 # L& MATHEMATICAL DOUBLE-STRUCK CAPITAL G
+1D540 ; FC_NFKC; 0069 # L& MATHEMATICAL DOUBLE-STRUCK CAPITAL I
+1D541 ; FC_NFKC; 006A # L& MATHEMATICAL DOUBLE-STRUCK CAPITAL J
+1D542 ; FC_NFKC; 006B # L& MATHEMATICAL DOUBLE-STRUCK CAPITAL K
+1D543 ; FC_NFKC; 006C # L& MATHEMATICAL DOUBLE-STRUCK CAPITAL L
+1D544 ; FC_NFKC; 006D # L& MATHEMATICAL DOUBLE-STRUCK CAPITAL M
+1D546 ; FC_NFKC; 006F # L& MATHEMATICAL DOUBLE-STRUCK CAPITAL O
+1D54A ; FC_NFKC; 0073 # L& MATHEMATICAL DOUBLE-STRUCK CAPITAL S
+1D54B ; FC_NFKC; 0074 # L& MATHEMATICAL DOUBLE-STRUCK CAPITAL T
+1D54C ; FC_NFKC; 0075 # L& MATHEMATICAL DOUBLE-STRUCK CAPITAL U
+1D54D ; FC_NFKC; 0076 # L& MATHEMATICAL DOUBLE-STRUCK CAPITAL V
+1D54E ; FC_NFKC; 0077 # L& MATHEMATICAL DOUBLE-STRUCK CAPITAL W
+1D54F ; FC_NFKC; 0078 # L& MATHEMATICAL DOUBLE-STRUCK CAPITAL X
+1D550 ; FC_NFKC; 0079 # L& MATHEMATICAL DOUBLE-STRUCK CAPITAL Y
+1D56C ; FC_NFKC; 0061 # L& MATHEMATICAL BOLD FRAKTUR CAPITAL A
+1D56D ; FC_NFKC; 0062 # L& MATHEMATICAL BOLD FRAKTUR CAPITAL B
+1D56E ; FC_NFKC; 0063 # L& MATHEMATICAL BOLD FRAKTUR CAPITAL C
+1D56F ; FC_NFKC; 0064 # L& MATHEMATICAL BOLD FRAKTUR CAPITAL D
+1D570 ; FC_NFKC; 0065 # L& MATHEMATICAL BOLD FRAKTUR CAPITAL E
+1D571 ; FC_NFKC; 0066 # L& MATHEMATICAL BOLD FRAKTUR CAPITAL F
+1D572 ; FC_NFKC; 0067 # L& MATHEMATICAL BOLD FRAKTUR CAPITAL G
+1D573 ; FC_NFKC; 0068 # L& MATHEMATICAL BOLD FRAKTUR CAPITAL H
+1D574 ; FC_NFKC; 0069 # L& MATHEMATICAL BOLD FRAKTUR CAPITAL I
+1D575 ; FC_NFKC; 006A # L& MATHEMATICAL BOLD FRAKTUR CAPITAL J
+1D576 ; FC_NFKC; 006B # L& MATHEMATICAL BOLD FRAKTUR CAPITAL K
+1D577 ; FC_NFKC; 006C # L& MATHEMATICAL BOLD FRAKTUR CAPITAL L
+1D578 ; FC_NFKC; 006D # L& MATHEMATICAL BOLD FRAKTUR CAPITAL M
+1D579 ; FC_NFKC; 006E # L& MATHEMATICAL BOLD FRAKTUR CAPITAL N
+1D57A ; FC_NFKC; 006F # L& MATHEMATICAL BOLD FRAKTUR CAPITAL O
+1D57B ; FC_NFKC; 0070 # L& MATHEMATICAL BOLD FRAKTUR CAPITAL P
+1D57C ; FC_NFKC; 0071 # L& MATHEMATICAL BOLD FRAKTUR CAPITAL Q
+1D57D ; FC_NFKC; 0072 # L& MATHEMATICAL BOLD FRAKTUR CAPITAL R
+1D57E ; FC_NFKC; 0073 # L& MATHEMATICAL BOLD FRAKTUR CAPITAL S
+1D57F ; FC_NFKC; 0074 # L& MATHEMATICAL BOLD FRAKTUR CAPITAL T
+1D580 ; FC_NFKC; 0075 # L& MATHEMATICAL BOLD FRAKTUR CAPITAL U
+1D581 ; FC_NFKC; 0076 # L& MATHEMATICAL BOLD FRAKTUR CAPITAL V
+1D582 ; FC_NFKC; 0077 # L& MATHEMATICAL BOLD FRAKTUR CAPITAL W
+1D583 ; FC_NFKC; 0078 # L& MATHEMATICAL BOLD FRAKTUR CAPITAL X
+1D584 ; FC_NFKC; 0079 # L& MATHEMATICAL BOLD FRAKTUR CAPITAL Y
+1D585 ; FC_NFKC; 007A # L& MATHEMATICAL BOLD FRAKTUR CAPITAL Z
+1D5A0 ; FC_NFKC; 0061 # L& MATHEMATICAL SANS-SERIF CAPITAL A
+1D5A1 ; FC_NFKC; 0062 # L& MATHEMATICAL SANS-SERIF CAPITAL B
+1D5A2 ; FC_NFKC; 0063 # L& MATHEMATICAL SANS-SERIF CAPITAL C
+1D5A3 ; FC_NFKC; 0064 # L& MATHEMATICAL SANS-SERIF CAPITAL D
+1D5A4 ; FC_NFKC; 0065 # L& MATHEMATICAL SANS-SERIF CAPITAL E
+1D5A5 ; FC_NFKC; 0066 # L& MATHEMATICAL SANS-SERIF CAPITAL F
+1D5A6 ; FC_NFKC; 0067 # L& MATHEMATICAL SANS-SERIF CAPITAL G
+1D5A7 ; FC_NFKC; 0068 # L& MATHEMATICAL SANS-SERIF CAPITAL H
+1D5A8 ; FC_NFKC; 0069 # L& MATHEMATICAL SANS-SERIF CAPITAL I
+1D5A9 ; FC_NFKC; 006A # L& MATHEMATICAL SANS-SERIF CAPITAL J
+1D5AA ; FC_NFKC; 006B # L& MATHEMATICAL SANS-SERIF CAPITAL K
+1D5AB ; FC_NFKC; 006C # L& MATHEMATICAL SANS-SERIF CAPITAL L
+1D5AC ; FC_NFKC; 006D # L& MATHEMATICAL SANS-SERIF CAPITAL M
+1D5AD ; FC_NFKC; 006E # L& MATHEMATICAL SANS-SERIF CAPITAL N
+1D5AE ; FC_NFKC; 006F # L& MATHEMATICAL SANS-SERIF CAPITAL O
+1D5AF ; FC_NFKC; 0070 # L& MATHEMATICAL SANS-SERIF CAPITAL P
+1D5B0 ; FC_NFKC; 0071 # L& MATHEMATICAL SANS-SERIF CAPITAL Q
+1D5B1 ; FC_NFKC; 0072 # L& MATHEMATICAL SANS-SERIF CAPITAL R
+1D5B2 ; FC_NFKC; 0073 # L& MATHEMATICAL SANS-SERIF CAPITAL S
+1D5B3 ; FC_NFKC; 0074 # L& MATHEMATICAL SANS-SERIF CAPITAL T
+1D5B4 ; FC_NFKC; 0075 # L& MATHEMATICAL SANS-SERIF CAPITAL U
+1D5B5 ; FC_NFKC; 0076 # L& MATHEMATICAL SANS-SERIF CAPITAL V
+1D5B6 ; FC_NFKC; 0077 # L& MATHEMATICAL SANS-SERIF CAPITAL W
+1D5B7 ; FC_NFKC; 0078 # L& MATHEMATICAL SANS-SERIF CAPITAL X
+1D5B8 ; FC_NFKC; 0079 # L& MATHEMATICAL SANS-SERIF CAPITAL Y
+1D5B9 ; FC_NFKC; 007A # L& MATHEMATICAL SANS-SERIF CAPITAL Z
+1D5D4 ; FC_NFKC; 0061 # L& MATHEMATICAL SANS-SERIF BOLD CAPITAL A
+1D5D5 ; FC_NFKC; 0062 # L& MATHEMATICAL SANS-SERIF BOLD CAPITAL B
+1D5D6 ; FC_NFKC; 0063 # L& MATHEMATICAL SANS-SERIF BOLD CAPITAL C
+1D5D7 ; FC_NFKC; 0064 # L& MATHEMATICAL SANS-SERIF BOLD CAPITAL D
+1D5D8 ; FC_NFKC; 0065 # L& MATHEMATICAL SANS-SERIF BOLD CAPITAL E
+1D5D9 ; FC_NFKC; 0066 # L& MATHEMATICAL SANS-SERIF BOLD CAPITAL F
+1D5DA ; FC_NFKC; 0067 # L& MATHEMATICAL SANS-SERIF BOLD CAPITAL G
+1D5DB ; FC_NFKC; 0068 # L& MATHEMATICAL SANS-SERIF BOLD CAPITAL H
+1D5DC ; FC_NFKC; 0069 # L& MATHEMATICAL SANS-SERIF BOLD CAPITAL I
+1D5DD ; FC_NFKC; 006A # L& MATHEMATICAL SANS-SERIF BOLD CAPITAL J
+1D5DE ; FC_NFKC; 006B # L& MATHEMATICAL SANS-SERIF BOLD CAPITAL K
+1D5DF ; FC_NFKC; 006C # L& MATHEMATICAL SANS-SERIF BOLD CAPITAL L
+1D5E0 ; FC_NFKC; 006D # L& MATHEMATICAL SANS-SERIF BOLD CAPITAL M
+1D5E1 ; FC_NFKC; 006E # L& MATHEMATICAL SANS-SERIF BOLD CAPITAL N
+1D5E2 ; FC_NFKC; 006F # L& MATHEMATICAL SANS-SERIF BOLD CAPITAL O
+1D5E3 ; FC_NFKC; 0070 # L& MATHEMATICAL SANS-SERIF BOLD CAPITAL P
+1D5E4 ; FC_NFKC; 0071 # L& MATHEMATICAL SANS-SERIF BOLD CAPITAL Q
+1D5E5 ; FC_NFKC; 0072 # L& MATHEMATICAL SANS-SERIF BOLD CAPITAL R
+1D5E6 ; FC_NFKC; 0073 # L& MATHEMATICAL SANS-SERIF BOLD CAPITAL S
+1D5E7 ; FC_NFKC; 0074 # L& MATHEMATICAL SANS-SERIF BOLD CAPITAL T
+1D5E8 ; FC_NFKC; 0075 # L& MATHEMATICAL SANS-SERIF BOLD CAPITAL U
+1D5E9 ; FC_NFKC; 0076 # L& MATHEMATICAL SANS-SERIF BOLD CAPITAL V
+1D5EA ; FC_NFKC; 0077 # L& MATHEMATICAL SANS-SERIF BOLD CAPITAL W
+1D5EB ; FC_NFKC; 0078 # L& MATHEMATICAL SANS-SERIF BOLD CAPITAL X
+1D5EC ; FC_NFKC; 0079 # L& MATHEMATICAL SANS-SERIF BOLD CAPITAL Y
+1D5ED ; FC_NFKC; 007A # L& MATHEMATICAL SANS-SERIF BOLD CAPITAL Z
+1D608 ; FC_NFKC; 0061 # L& MATHEMATICAL SANS-SERIF ITALIC CAPITAL A
+1D609 ; FC_NFKC; 0062 # L& MATHEMATICAL SANS-SERIF ITALIC CAPITAL B
+1D60A ; FC_NFKC; 0063 # L& MATHEMATICAL SANS-SERIF ITALIC CAPITAL C
+1D60B ; FC_NFKC; 0064 # L& MATHEMATICAL SANS-SERIF ITALIC CAPITAL D
+1D60C ; FC_NFKC; 0065 # L& MATHEMATICAL SANS-SERIF ITALIC CAPITAL E
+1D60D ; FC_NFKC; 0066 # L& MATHEMATICAL SANS-SERIF ITALIC CAPITAL F
+1D60E ; FC_NFKC; 0067 # L& MATHEMATICAL SANS-SERIF ITALIC CAPITAL G
+1D60F ; FC_NFKC; 0068 # L& MATHEMATICAL SANS-SERIF ITALIC CAPITAL H
+1D610 ; FC_NFKC; 0069 # L& MATHEMATICAL SANS-SERIF ITALIC CAPITAL I
+1D611 ; FC_NFKC; 006A # L& MATHEMATICAL SANS-SERIF ITALIC CAPITAL J
+1D612 ; FC_NFKC; 006B # L& MATHEMATICAL SANS-SERIF ITALIC CAPITAL K
+1D613 ; FC_NFKC; 006C # L& MATHEMATICAL SANS-SERIF ITALIC CAPITAL L
+1D614 ; FC_NFKC; 006D # L& MATHEMATICAL SANS-SERIF ITALIC CAPITAL M
+1D615 ; FC_NFKC; 006E # L& MATHEMATICAL SANS-SERIF ITALIC CAPITAL N
+1D616 ; FC_NFKC; 006F # L& MATHEMATICAL SANS-SERIF ITALIC CAPITAL O
+1D617 ; FC_NFKC; 0070 # L& MATHEMATICAL SANS-SERIF ITALIC CAPITAL P
+1D618 ; FC_NFKC; 0071 # L& MATHEMATICAL SANS-SERIF ITALIC CAPITAL Q
+1D619 ; FC_NFKC; 0072 # L& MATHEMATICAL SANS-SERIF ITALIC CAPITAL R
+1D61A ; FC_NFKC; 0073 # L& MATHEMATICAL SANS-SERIF ITALIC CAPITAL S
+1D61B ; FC_NFKC; 0074 # L& MATHEMATICAL SANS-SERIF ITALIC CAPITAL T
+1D61C ; FC_NFKC; 0075 # L& MATHEMATICAL SANS-SERIF ITALIC CAPITAL U
+1D61D ; FC_NFKC; 0076 # L& MATHEMATICAL SANS-SERIF ITALIC CAPITAL V
+1D61E ; FC_NFKC; 0077 # L& MATHEMATICAL SANS-SERIF ITALIC CAPITAL W
+1D61F ; FC_NFKC; 0078 # L& MATHEMATICAL SANS-SERIF ITALIC CAPITAL X
+1D620 ; FC_NFKC; 0079 # L& MATHEMATICAL SANS-SERIF ITALIC CAPITAL Y
+1D621 ; FC_NFKC; 007A # L& MATHEMATICAL SANS-SERIF ITALIC CAPITAL Z
+1D63C ; FC_NFKC; 0061 # L& MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL A
+1D63D ; FC_NFKC; 0062 # L& MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL B
+1D63E ; FC_NFKC; 0063 # L& MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL C
+1D63F ; FC_NFKC; 0064 # L& MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL D
+1D640 ; FC_NFKC; 0065 # L& MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL E
+1D641 ; FC_NFKC; 0066 # L& MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL F
+1D642 ; FC_NFKC; 0067 # L& MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL G
+1D643 ; FC_NFKC; 0068 # L& MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL H
+1D644 ; FC_NFKC; 0069 # L& MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL I
+1D645 ; FC_NFKC; 006A # L& MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL J
+1D646 ; FC_NFKC; 006B # L& MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL K
+1D647 ; FC_NFKC; 006C # L& MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL L
+1D648 ; FC_NFKC; 006D # L& MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL M
+1D649 ; FC_NFKC; 006E # L& MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL N
+1D64A ; FC_NFKC; 006F # L& MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL O
+1D64B ; FC_NFKC; 0070 # L& MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL P
+1D64C ; FC_NFKC; 0071 # L& MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL Q
+1D64D ; FC_NFKC; 0072 # L& MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL R
+1D64E ; FC_NFKC; 0073 # L& MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL S
+1D64F ; FC_NFKC; 0074 # L& MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL T
+1D650 ; FC_NFKC; 0075 # L& MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL U
+1D651 ; FC_NFKC; 0076 # L& MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL V
+1D652 ; FC_NFKC; 0077 # L& MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL W
+1D653 ; FC_NFKC; 0078 # L& MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL X
+1D654 ; FC_NFKC; 0079 # L& MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL Y
+1D655 ; FC_NFKC; 007A # L& MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL Z
+1D670 ; FC_NFKC; 0061 # L& MATHEMATICAL MONOSPACE CAPITAL A
+1D671 ; FC_NFKC; 0062 # L& MATHEMATICAL MONOSPACE CAPITAL B
+1D672 ; FC_NFKC; 0063 # L& MATHEMATICAL MONOSPACE CAPITAL C
+1D673 ; FC_NFKC; 0064 # L& MATHEMATICAL MONOSPACE CAPITAL D
+1D674 ; FC_NFKC; 0065 # L& MATHEMATICAL MONOSPACE CAPITAL E
+1D675 ; FC_NFKC; 0066 # L& MATHEMATICAL MONOSPACE CAPITAL F
+1D676 ; FC_NFKC; 0067 # L& MATHEMATICAL MONOSPACE CAPITAL G
+1D677 ; FC_NFKC; 0068 # L& MATHEMATICAL MONOSPACE CAPITAL H
+1D678 ; FC_NFKC; 0069 # L& MATHEMATICAL MONOSPACE CAPITAL I
+1D679 ; FC_NFKC; 006A # L& MATHEMATICAL MONOSPACE CAPITAL J
+1D67A ; FC_NFKC; 006B # L& MATHEMATICAL MONOSPACE CAPITAL K
+1D67B ; FC_NFKC; 006C # L& MATHEMATICAL MONOSPACE CAPITAL L
+1D67C ; FC_NFKC; 006D # L& MATHEMATICAL MONOSPACE CAPITAL M
+1D67D ; FC_NFKC; 006E # L& MATHEMATICAL MONOSPACE CAPITAL N
+1D67E ; FC_NFKC; 006F # L& MATHEMATICAL MONOSPACE CAPITAL O
+1D67F ; FC_NFKC; 0070 # L& MATHEMATICAL MONOSPACE CAPITAL P
+1D680 ; FC_NFKC; 0071 # L& MATHEMATICAL MONOSPACE CAPITAL Q
+1D681 ; FC_NFKC; 0072 # L& MATHEMATICAL MONOSPACE CAPITAL R
+1D682 ; FC_NFKC; 0073 # L& MATHEMATICAL MONOSPACE CAPITAL S
+1D683 ; FC_NFKC; 0074 # L& MATHEMATICAL MONOSPACE CAPITAL T
+1D684 ; FC_NFKC; 0075 # L& MATHEMATICAL MONOSPACE CAPITAL U
+1D685 ; FC_NFKC; 0076 # L& MATHEMATICAL MONOSPACE CAPITAL V
+1D686 ; FC_NFKC; 0077 # L& MATHEMATICAL MONOSPACE CAPITAL W
+1D687 ; FC_NFKC; 0078 # L& MATHEMATICAL MONOSPACE CAPITAL X
+1D688 ; FC_NFKC; 0079 # L& MATHEMATICAL MONOSPACE CAPITAL Y
+1D689 ; FC_NFKC; 007A # L& MATHEMATICAL MONOSPACE CAPITAL Z
+1D6A8 ; FC_NFKC; 03B1 # L& MATHEMATICAL BOLD CAPITAL ALPHA
+1D6A9 ; FC_NFKC; 03B2 # L& MATHEMATICAL BOLD CAPITAL BETA
+1D6AA ; FC_NFKC; 03B3 # L& MATHEMATICAL BOLD CAPITAL GAMMA
+1D6AB ; FC_NFKC; 03B4 # L& MATHEMATICAL BOLD CAPITAL DELTA
+1D6AC ; FC_NFKC; 03B5 # L& MATHEMATICAL BOLD CAPITAL EPSILON
+1D6AD ; FC_NFKC; 03B6 # L& MATHEMATICAL BOLD CAPITAL ZETA
+1D6AE ; FC_NFKC; 03B7 # L& MATHEMATICAL BOLD CAPITAL ETA
+1D6AF ; FC_NFKC; 03B8 # L& MATHEMATICAL BOLD CAPITAL THETA
+1D6B0 ; FC_NFKC; 03B9 # L& MATHEMATICAL BOLD CAPITAL IOTA
+1D6B1 ; FC_NFKC; 03BA # L& MATHEMATICAL BOLD CAPITAL KAPPA
+1D6B2 ; FC_NFKC; 03BB # L& MATHEMATICAL BOLD CAPITAL LAMDA
+1D6B3 ; FC_NFKC; 03BC # L& MATHEMATICAL BOLD CAPITAL MU
+1D6B4 ; FC_NFKC; 03BD # L& MATHEMATICAL BOLD CAPITAL NU
+1D6B5 ; FC_NFKC; 03BE # L& MATHEMATICAL BOLD CAPITAL XI
+1D6B6 ; FC_NFKC; 03BF # L& MATHEMATICAL BOLD CAPITAL OMICRON
+1D6B7 ; FC_NFKC; 03C0 # L& MATHEMATICAL BOLD CAPITAL PI
+1D6B8 ; FC_NFKC; 03C1 # L& MATHEMATICAL BOLD CAPITAL RHO
+1D6B9 ; FC_NFKC; 03B8 # L& MATHEMATICAL BOLD CAPITAL THETA SYMBOL
+1D6BA ; FC_NFKC; 03C3 # L& MATHEMATICAL BOLD CAPITAL SIGMA
+1D6BB ; FC_NFKC; 03C4 # L& MATHEMATICAL BOLD CAPITAL TAU
+1D6BC ; FC_NFKC; 03C5 # L& MATHEMATICAL BOLD CAPITAL UPSILON
+1D6BD ; FC_NFKC; 03C6 # L& MATHEMATICAL BOLD CAPITAL PHI
+1D6BE ; FC_NFKC; 03C7 # L& MATHEMATICAL BOLD CAPITAL CHI
+1D6BF ; FC_NFKC; 03C8 # L& MATHEMATICAL BOLD CAPITAL PSI
+1D6C0 ; FC_NFKC; 03C9 # L& MATHEMATICAL BOLD CAPITAL OMEGA
+1D6D3 ; FC_NFKC; 03C3 # L& MATHEMATICAL BOLD SMALL FINAL SIGMA
+1D6E2 ; FC_NFKC; 03B1 # L& MATHEMATICAL ITALIC CAPITAL ALPHA
+1D6E3 ; FC_NFKC; 03B2 # L& MATHEMATICAL ITALIC CAPITAL BETA
+1D6E4 ; FC_NFKC; 03B3 # L& MATHEMATICAL ITALIC CAPITAL GAMMA
+1D6E5 ; FC_NFKC; 03B4 # L& MATHEMATICAL ITALIC CAPITAL DELTA
+1D6E6 ; FC_NFKC; 03B5 # L& MATHEMATICAL ITALIC CAPITAL EPSILON
+1D6E7 ; FC_NFKC; 03B6 # L& MATHEMATICAL ITALIC CAPITAL ZETA
+1D6E8 ; FC_NFKC; 03B7 # L& MATHEMATICAL ITALIC CAPITAL ETA
+1D6E9 ; FC_NFKC; 03B8 # L& MATHEMATICAL ITALIC CAPITAL THETA
+1D6EA ; FC_NFKC; 03B9 # L& MATHEMATICAL ITALIC CAPITAL IOTA
+1D6EB ; FC_NFKC; 03BA # L& MATHEMATICAL ITALIC CAPITAL KAPPA
+1D6EC ; FC_NFKC; 03BB # L& MATHEMATICAL ITALIC CAPITAL LAMDA
+1D6ED ; FC_NFKC; 03BC # L& MATHEMATICAL ITALIC CAPITAL MU
+1D6EE ; FC_NFKC; 03BD # L& MATHEMATICAL ITALIC CAPITAL NU
+1D6EF ; FC_NFKC; 03BE # L& MATHEMATICAL ITALIC CAPITAL XI
+1D6F0 ; FC_NFKC; 03BF # L& MATHEMATICAL ITALIC CAPITAL OMICRON
+1D6F1 ; FC_NFKC; 03C0 # L& MATHEMATICAL ITALIC CAPITAL PI
+1D6F2 ; FC_NFKC; 03C1 # L& MATHEMATICAL ITALIC CAPITAL RHO
+1D6F3 ; FC_NFKC; 03B8 # L& MATHEMATICAL ITALIC CAPITAL THETA SYMBOL
+1D6F4 ; FC_NFKC; 03C3 # L& MATHEMATICAL ITALIC CAPITAL SIGMA
+1D6F5 ; FC_NFKC; 03C4 # L& MATHEMATICAL ITALIC CAPITAL TAU
+1D6F6 ; FC_NFKC; 03C5 # L& MATHEMATICAL ITALIC CAPITAL UPSILON
+1D6F7 ; FC_NFKC; 03C6 # L& MATHEMATICAL ITALIC CAPITAL PHI
+1D6F8 ; FC_NFKC; 03C7 # L& MATHEMATICAL ITALIC CAPITAL CHI
+1D6F9 ; FC_NFKC; 03C8 # L& MATHEMATICAL ITALIC CAPITAL PSI
+1D6FA ; FC_NFKC; 03C9 # L& MATHEMATICAL ITALIC CAPITAL OMEGA
+1D70D ; FC_NFKC; 03C3 # L& MATHEMATICAL ITALIC SMALL FINAL SIGMA
+1D71C ; FC_NFKC; 03B1 # L& MATHEMATICAL BOLD ITALIC CAPITAL ALPHA
+1D71D ; FC_NFKC; 03B2 # L& MATHEMATICAL BOLD ITALIC CAPITAL BETA
+1D71E ; FC_NFKC; 03B3 # L& MATHEMATICAL BOLD ITALIC CAPITAL GAMMA
+1D71F ; FC_NFKC; 03B4 # L& MATHEMATICAL BOLD ITALIC CAPITAL DELTA
+1D720 ; FC_NFKC; 03B5 # L& MATHEMATICAL BOLD ITALIC CAPITAL EPSILON
+1D721 ; FC_NFKC; 03B6 # L& MATHEMATICAL BOLD ITALIC CAPITAL ZETA
+1D722 ; FC_NFKC; 03B7 # L& MATHEMATICAL BOLD ITALIC CAPITAL ETA
+1D723 ; FC_NFKC; 03B8 # L& MATHEMATICAL BOLD ITALIC CAPITAL THETA
+1D724 ; FC_NFKC; 03B9 # L& MATHEMATICAL BOLD ITALIC CAPITAL IOTA
+1D725 ; FC_NFKC; 03BA # L& MATHEMATICAL BOLD ITALIC CAPITAL KAPPA
+1D726 ; FC_NFKC; 03BB # L& MATHEMATICAL BOLD ITALIC CAPITAL LAMDA
+1D727 ; FC_NFKC; 03BC # L& MATHEMATICAL BOLD ITALIC CAPITAL MU
+1D728 ; FC_NFKC; 03BD # L& MATHEMATICAL BOLD ITALIC CAPITAL NU
+1D729 ; FC_NFKC; 03BE # L& MATHEMATICAL BOLD ITALIC CAPITAL XI
+1D72A ; FC_NFKC; 03BF # L& MATHEMATICAL BOLD ITALIC CAPITAL OMICRON
+1D72B ; FC_NFKC; 03C0 # L& MATHEMATICAL BOLD ITALIC CAPITAL PI
+1D72C ; FC_NFKC; 03C1 # L& MATHEMATICAL BOLD ITALIC CAPITAL RHO
+1D72D ; FC_NFKC; 03B8 # L& MATHEMATICAL BOLD ITALIC CAPITAL THETA SYMBOL
+1D72E ; FC_NFKC; 03C3 # L& MATHEMATICAL BOLD ITALIC CAPITAL SIGMA
+1D72F ; FC_NFKC; 03C4 # L& MATHEMATICAL BOLD ITALIC CAPITAL TAU
+1D730 ; FC_NFKC; 03C5 # L& MATHEMATICAL BOLD ITALIC CAPITAL UPSILON
+1D731 ; FC_NFKC; 03C6 # L& MATHEMATICAL BOLD ITALIC CAPITAL PHI
+1D732 ; FC_NFKC; 03C7 # L& MATHEMATICAL BOLD ITALIC CAPITAL CHI
+1D733 ; FC_NFKC; 03C8 # L& MATHEMATICAL BOLD ITALIC CAPITAL PSI
+1D734 ; FC_NFKC; 03C9 # L& MATHEMATICAL BOLD ITALIC CAPITAL OMEGA
+1D747 ; FC_NFKC; 03C3 # L& MATHEMATICAL BOLD ITALIC SMALL FINAL SIGMA
+1D756 ; FC_NFKC; 03B1 # L& MATHEMATICAL SANS-SERIF BOLD CAPITAL ALPHA
+1D757 ; FC_NFKC; 03B2 # L& MATHEMATICAL SANS-SERIF BOLD CAPITAL BETA
+1D758 ; FC_NFKC; 03B3 # L& MATHEMATICAL SANS-SERIF BOLD CAPITAL GAMMA
+1D759 ; FC_NFKC; 03B4 # L& MATHEMATICAL SANS-SERIF BOLD CAPITAL DELTA
+1D75A ; FC_NFKC; 03B5 # L& MATHEMATICAL SANS-SERIF BOLD CAPITAL EPSILON
+1D75B ; FC_NFKC; 03B6 # L& MATHEMATICAL SANS-SERIF BOLD CAPITAL ZETA
+1D75C ; FC_NFKC; 03B7 # L& MATHEMATICAL SANS-SERIF BOLD CAPITAL ETA
+1D75D ; FC_NFKC; 03B8 # L& MATHEMATICAL SANS-SERIF BOLD CAPITAL THETA
+1D75E ; FC_NFKC; 03B9 # L& MATHEMATICAL SANS-SERIF BOLD CAPITAL IOTA
+1D75F ; FC_NFKC; 03BA # L& MATHEMATICAL SANS-SERIF BOLD CAPITAL KAPPA
+1D760 ; FC_NFKC; 03BB # L& MATHEMATICAL SANS-SERIF BOLD CAPITAL LAMDA
+1D761 ; FC_NFKC; 03BC # L& MATHEMATICAL SANS-SERIF BOLD CAPITAL MU
+1D762 ; FC_NFKC; 03BD # L& MATHEMATICAL SANS-SERIF BOLD CAPITAL NU
+1D763 ; FC_NFKC; 03BE # L& MATHEMATICAL SANS-SERIF BOLD CAPITAL XI
+1D764 ; FC_NFKC; 03BF # L& MATHEMATICAL SANS-SERIF BOLD CAPITAL OMICRON
+1D765 ; FC_NFKC; 03C0 # L& MATHEMATICAL SANS-SERIF BOLD CAPITAL PI
+1D766 ; FC_NFKC; 03C1 # L& MATHEMATICAL SANS-SERIF BOLD CAPITAL RHO
+1D767 ; FC_NFKC; 03B8 # L& MATHEMATICAL SANS-SERIF BOLD CAPITAL THETA SYMBOL
+1D768 ; FC_NFKC; 03C3 # L& MATHEMATICAL SANS-SERIF BOLD CAPITAL SIGMA
+1D769 ; FC_NFKC; 03C4 # L& MATHEMATICAL SANS-SERIF BOLD CAPITAL TAU
+1D76A ; FC_NFKC; 03C5 # L& MATHEMATICAL SANS-SERIF BOLD CAPITAL UPSILON
+1D76B ; FC_NFKC; 03C6 # L& MATHEMATICAL SANS-SERIF BOLD CAPITAL PHI
+1D76C ; FC_NFKC; 03C7 # L& MATHEMATICAL SANS-SERIF BOLD CAPITAL CHI
+1D76D ; FC_NFKC; 03C8 # L& MATHEMATICAL SANS-SERIF BOLD CAPITAL PSI
+1D76E ; FC_NFKC; 03C9 # L& MATHEMATICAL SANS-SERIF BOLD CAPITAL OMEGA
+1D781 ; FC_NFKC; 03C3 # L& MATHEMATICAL SANS-SERIF BOLD SMALL FINAL SIGMA
+1D790 ; FC_NFKC; 03B1 # L& MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL ALPHA
+1D791 ; FC_NFKC; 03B2 # L& MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL BETA
+1D792 ; FC_NFKC; 03B3 # L& MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL GAMMA
+1D793 ; FC_NFKC; 03B4 # L& MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL DELTA
+1D794 ; FC_NFKC; 03B5 # L& MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL EPSILON
+1D795 ; FC_NFKC; 03B6 # L& MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL ZETA
+1D796 ; FC_NFKC; 03B7 # L& MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL ETA
+1D797 ; FC_NFKC; 03B8 # L& MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL THETA
+1D798 ; FC_NFKC; 03B9 # L& MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL IOTA
+1D799 ; FC_NFKC; 03BA # L& MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL KAPPA
+1D79A ; FC_NFKC; 03BB # L& MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL LAMDA
+1D79B ; FC_NFKC; 03BC # L& MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL MU
+1D79C ; FC_NFKC; 03BD # L& MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL NU
+1D79D ; FC_NFKC; 03BE # L& MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL XI
+1D79E ; FC_NFKC; 03BF # L& MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL OMICRON
+1D79F ; FC_NFKC; 03C0 # L& MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL PI
+1D7A0 ; FC_NFKC; 03C1 # L& MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL RHO
+1D7A1 ; FC_NFKC; 03B8 # L& MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL THETA SYMBOL
+1D7A2 ; FC_NFKC; 03C3 # L& MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL SIGMA
+1D7A3 ; FC_NFKC; 03C4 # L& MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL TAU
+1D7A4 ; FC_NFKC; 03C5 # L& MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL UPSILON
+1D7A5 ; FC_NFKC; 03C6 # L& MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL PHI
+1D7A6 ; FC_NFKC; 03C7 # L& MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL CHI
+1D7A7 ; FC_NFKC; 03C8 # L& MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL PSI
+1D7A8 ; FC_NFKC; 03C9 # L& MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL OMEGA
+1D7BB ; FC_NFKC; 03C3 # L& MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL FINAL SIGMA
+
+# Total code points: 564
+
+# ================================================
+
+# Derived Property: Full_Composition_Exclusion
+# Generated from: Composition Exclusions + Singletons + Non-Starter Decompositions
+
+0340..0341 ; Full_Composition_Exclusion # Mn [2] COMBINING GRAVE TONE MARK..COMBINING ACUTE TONE MARK
+0343..0344 ; Full_Composition_Exclusion # Mn [2] COMBINING GREEK KORONIS..COMBINING GREEK DIALYTIKA TONOS
+0374 ; Full_Composition_Exclusion # Sk GREEK NUMERAL SIGN
+037E ; Full_Composition_Exclusion # Po GREEK QUESTION MARK
+0387 ; Full_Composition_Exclusion # Po GREEK ANO TELEIA
+0958..095F ; Full_Composition_Exclusion # Lo [8] DEVANAGARI LETTER QA..DEVANAGARI LETTER YYA
+09DC..09DD ; Full_Composition_Exclusion # Lo [2] BENGALI LETTER RRA..BENGALI LETTER RHA
+09DF ; Full_Composition_Exclusion # Lo BENGALI LETTER YYA
+0A33 ; Full_Composition_Exclusion # Lo GURMUKHI LETTER LLA
+0A36 ; Full_Composition_Exclusion # Lo GURMUKHI LETTER SHA
+0A59..0A5B ; Full_Composition_Exclusion # Lo [3] GURMUKHI LETTER KHHA..GURMUKHI LETTER ZA
+0A5E ; Full_Composition_Exclusion # Lo GURMUKHI LETTER FA
+0B5C..0B5D ; Full_Composition_Exclusion # Lo [2] ORIYA LETTER RRA..ORIYA LETTER RHA
+0F43 ; Full_Composition_Exclusion # Lo TIBETAN LETTER GHA
+0F4D ; Full_Composition_Exclusion # Lo TIBETAN LETTER DDHA
+0F52 ; Full_Composition_Exclusion # Lo TIBETAN LETTER DHA
+0F57 ; Full_Composition_Exclusion # Lo TIBETAN LETTER BHA
+0F5C ; Full_Composition_Exclusion # Lo TIBETAN LETTER DZHA
+0F69 ; Full_Composition_Exclusion # Lo TIBETAN LETTER KSSA
+0F73 ; Full_Composition_Exclusion # Mn TIBETAN VOWEL SIGN II
+0F75..0F76 ; Full_Composition_Exclusion # Mn [2] TIBETAN VOWEL SIGN UU..TIBETAN VOWEL SIGN VOCALIC R
+0F78 ; Full_Composition_Exclusion # Mn TIBETAN VOWEL SIGN VOCALIC L
+0F81 ; Full_Composition_Exclusion # Mn TIBETAN VOWEL SIGN REVERSED II
+0F93 ; Full_Composition_Exclusion # Mn TIBETAN SUBJOINED LETTER GHA
+0F9D ; Full_Composition_Exclusion # Mn TIBETAN SUBJOINED LETTER DDHA
+0FA2 ; Full_Composition_Exclusion # Mn TIBETAN SUBJOINED LETTER DHA
+0FA7 ; Full_Composition_Exclusion # Mn TIBETAN SUBJOINED LETTER BHA
+0FAC ; Full_Composition_Exclusion # Mn TIBETAN SUBJOINED LETTER DZHA
+0FB9 ; Full_Composition_Exclusion # Mn TIBETAN SUBJOINED LETTER KSSA
+1F71 ; Full_Composition_Exclusion # L& GREEK SMALL LETTER ALPHA WITH OXIA
+1F73 ; Full_Composition_Exclusion # L& GREEK SMALL LETTER EPSILON WITH OXIA
+1F75 ; Full_Composition_Exclusion # L& GREEK SMALL LETTER ETA WITH OXIA
+1F77 ; Full_Composition_Exclusion # L& GREEK SMALL LETTER IOTA WITH OXIA
+1F79 ; Full_Composition_Exclusion # L& GREEK SMALL LETTER OMICRON WITH OXIA
+1F7B ; Full_Composition_Exclusion # L& GREEK SMALL LETTER UPSILON WITH OXIA
+1F7D ; Full_Composition_Exclusion # L& GREEK SMALL LETTER OMEGA WITH OXIA
+1FBB ; Full_Composition_Exclusion # L& GREEK CAPITAL LETTER ALPHA WITH OXIA
+1FBE ; Full_Composition_Exclusion # L& GREEK PROSGEGRAMMENI
+1FC9 ; Full_Composition_Exclusion # L& GREEK CAPITAL LETTER EPSILON WITH OXIA
+1FCB ; Full_Composition_Exclusion # L& GREEK CAPITAL LETTER ETA WITH OXIA
+1FD3 ; Full_Composition_Exclusion # L& GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA
+1FDB ; Full_Composition_Exclusion # L& GREEK CAPITAL LETTER IOTA WITH OXIA
+1FE3 ; Full_Composition_Exclusion # L& GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND OXIA
+1FEB ; Full_Composition_Exclusion # L& GREEK CAPITAL LETTER UPSILON WITH OXIA
+1FEE..1FEF ; Full_Composition_Exclusion # Sk [2] GREEK DIALYTIKA AND OXIA..GREEK VARIA
+1FF9 ; Full_Composition_Exclusion # L& GREEK CAPITAL LETTER OMICRON WITH OXIA
+1FFB ; Full_Composition_Exclusion # L& GREEK CAPITAL LETTER OMEGA WITH OXIA
+1FFD ; Full_Composition_Exclusion # Sk GREEK OXIA
+2000..2001 ; Full_Composition_Exclusion # Zs [2] EN QUAD..EM QUAD
+2126 ; Full_Composition_Exclusion # L& OHM SIGN
+212A..212B ; Full_Composition_Exclusion # L& [2] KELVIN SIGN..ANGSTROM SIGN
+2329 ; Full_Composition_Exclusion # Ps LEFT-POINTING ANGLE BRACKET
+232A ; Full_Composition_Exclusion # Pe RIGHT-POINTING ANGLE BRACKET
+2ADC ; Full_Composition_Exclusion # Sm FORKING
+F900..FA0D ; Full_Composition_Exclusion # Lo [270] CJK COMPATIBILITY IDEOGRAPH-F900..CJK COMPATIBILITY IDEOGRAPH-FA0D
+FA10 ; Full_Composition_Exclusion # Lo CJK COMPATIBILITY IDEOGRAPH-FA10
+FA12 ; Full_Composition_Exclusion # Lo CJK COMPATIBILITY IDEOGRAPH-FA12
+FA15..FA1E ; Full_Composition_Exclusion # Lo [10] CJK COMPATIBILITY IDEOGRAPH-FA15..CJK COMPATIBILITY IDEOGRAPH-FA1E
+FA20 ; Full_Composition_Exclusion # Lo CJK COMPATIBILITY IDEOGRAPH-FA20
+FA22 ; Full_Composition_Exclusion # Lo CJK COMPATIBILITY IDEOGRAPH-FA22
+FA25..FA26 ; Full_Composition_Exclusion # Lo [2] CJK COMPATIBILITY IDEOGRAPH-FA25..CJK COMPATIBILITY IDEOGRAPH-FA26
+FA2A..FA2D ; Full_Composition_Exclusion # Lo [4] CJK COMPATIBILITY IDEOGRAPH-FA2A..CJK COMPATIBILITY IDEOGRAPH-FA2D
+FA30..FA6A ; Full_Composition_Exclusion # Lo [59] CJK COMPATIBILITY IDEOGRAPH-FA30..CJK COMPATIBILITY IDEOGRAPH-FA6A
+FB1D ; Full_Composition_Exclusion # Lo HEBREW LETTER YOD WITH HIRIQ
+FB1F ; Full_Composition_Exclusion # Lo HEBREW LIGATURE YIDDISH YOD YOD PATAH
+FB2A..FB36 ; Full_Composition_Exclusion # Lo [13] HEBREW LETTER SHIN WITH SHIN DOT..HEBREW LETTER ZAYIN WITH DAGESH
+FB38..FB3C ; Full_Composition_Exclusion # Lo [5] HEBREW LETTER TET WITH DAGESH..HEBREW LETTER LAMED WITH DAGESH
+FB3E ; Full_Composition_Exclusion # Lo HEBREW LETTER MEM WITH DAGESH
+FB40..FB41 ; Full_Composition_Exclusion # Lo [2] HEBREW LETTER NUN WITH DAGESH..HEBREW LETTER SAMEKH WITH DAGESH
+FB43..FB44 ; Full_Composition_Exclusion # Lo [2] HEBREW LETTER FINAL PE WITH DAGESH..HEBREW LETTER PE WITH DAGESH
+FB46..FB4E ; Full_Composition_Exclusion # Lo [9] HEBREW LETTER TSADI WITH DAGESH..HEBREW LETTER PE WITH RAFE
+1D15E..1D164 ; Full_Composition_Exclusion # So [7] MUSICAL SYMBOL HALF NOTE..MUSICAL SYMBOL ONE HUNDRED TWENTY-EIGHTH NOTE
+1D1BB..1D1C0 ; Full_Composition_Exclusion # So [6] MUSICAL SYMBOL MINIMA..MUSICAL SYMBOL FUSA BLACK
+2F800..2FA1D ; Full_Composition_Exclusion # Lo [542] CJK COMPATIBILITY IDEOGRAPH-2F800..CJK COMPATIBILITY IDEOGRAPH-2FA1D
+
+# Total code points: 1009
+
+# ================================================
+
+# Property: NFD_Quick_Check
+
+# All code points not explicitly listed for NFD_Quick_Check
+# have the value Yes (Y).
+
+# ================================================
+
+# NFD_Quick_Check=No
+
+00C0..00C5 ; NFD_QC; N # L& [6] LATIN CAPITAL LETTER A WITH GRAVE..LATIN CAPITAL LETTER A WITH RING ABOVE
+00C7..00CF ; NFD_QC; N # L& [9] LATIN CAPITAL LETTER C WITH CEDILLA..LATIN CAPITAL LETTER I WITH DIAERESIS
+00D1..00D6 ; NFD_QC; N # L& [6] LATIN CAPITAL LETTER N WITH TILDE..LATIN CAPITAL LETTER O WITH DIAERESIS
+00D9..00DD ; NFD_QC; N # L& [5] LATIN CAPITAL LETTER U WITH GRAVE..LATIN CAPITAL LETTER Y WITH ACUTE
+00E0..00E5 ; NFD_QC; N # L& [6] LATIN SMALL LETTER A WITH GRAVE..LATIN SMALL LETTER A WITH RING ABOVE
+00E7..00EF ; NFD_QC; N # L& [9] LATIN SMALL LETTER C WITH CEDILLA..LATIN SMALL LETTER I WITH DIAERESIS
+00F1..00F6 ; NFD_QC; N # L& [6] LATIN SMALL LETTER N WITH TILDE..LATIN SMALL LETTER O WITH DIAERESIS
+00F9..00FD ; NFD_QC; N # L& [5] LATIN SMALL LETTER U WITH GRAVE..LATIN SMALL LETTER Y WITH ACUTE
+00FF..010F ; NFD_QC; N # L& [17] LATIN SMALL LETTER Y WITH DIAERESIS..LATIN SMALL LETTER D WITH CARON
+0112..0125 ; NFD_QC; N # L& [20] LATIN CAPITAL LETTER E WITH MACRON..LATIN SMALL LETTER H WITH CIRCUMFLEX
+0128..0130 ; NFD_QC; N # L& [9] LATIN CAPITAL LETTER I WITH TILDE..LATIN CAPITAL LETTER I WITH DOT ABOVE
+0134..0137 ; NFD_QC; N # L& [4] LATIN CAPITAL LETTER J WITH CIRCUMFLEX..LATIN SMALL LETTER K WITH CEDILLA
+0139..013E ; NFD_QC; N # L& [6] LATIN CAPITAL LETTER L WITH ACUTE..LATIN SMALL LETTER L WITH CARON
+0143..0148 ; NFD_QC; N # L& [6] LATIN CAPITAL LETTER N WITH ACUTE..LATIN SMALL LETTER N WITH CARON
+014C..0151 ; NFD_QC; N # L& [6] LATIN CAPITAL LETTER O WITH MACRON..LATIN SMALL LETTER O WITH DOUBLE ACUTE
+0154..0165 ; NFD_QC; N # L& [18] LATIN CAPITAL LETTER R WITH ACUTE..LATIN SMALL LETTER T WITH CARON
+0168..017E ; NFD_QC; N # L& [23] LATIN CAPITAL LETTER U WITH TILDE..LATIN SMALL LETTER Z WITH CARON
+01A0..01A1 ; NFD_QC; N # L& [2] LATIN CAPITAL LETTER O WITH HORN..LATIN SMALL LETTER O WITH HORN
+01AF..01B0 ; NFD_QC; N # L& [2] LATIN CAPITAL LETTER U WITH HORN..LATIN SMALL LETTER U WITH HORN
+01CD..01DC ; NFD_QC; N # L& [16] LATIN CAPITAL LETTER A WITH CARON..LATIN SMALL LETTER U WITH DIAERESIS AND GRAVE
+01DE..01E3 ; NFD_QC; N # L& [6] LATIN CAPITAL LETTER A WITH DIAERESIS AND MACRON..LATIN SMALL LETTER AE WITH MACRON
+01E6..01F0 ; NFD_QC; N # L& [11] LATIN CAPITAL LETTER G WITH CARON..LATIN SMALL LETTER J WITH CARON
+01F4..01F5 ; NFD_QC; N # L& [2] LATIN CAPITAL LETTER G WITH ACUTE..LATIN SMALL LETTER G WITH ACUTE
+01F8..021B ; NFD_QC; N # L& [36] LATIN CAPITAL LETTER N WITH GRAVE..LATIN SMALL LETTER T WITH COMMA BELOW
+021E..021F ; NFD_QC; N # L& [2] LATIN CAPITAL LETTER H WITH CARON..LATIN SMALL LETTER H WITH CARON
+0226..0233 ; NFD_QC; N # L& [14] LATIN CAPITAL LETTER A WITH DOT ABOVE..LATIN SMALL LETTER Y WITH MACRON
+0340..0341 ; NFD_QC; N # Mn [2] COMBINING GRAVE TONE MARK..COMBINING ACUTE TONE MARK
+0343..0344 ; NFD_QC; N # Mn [2] COMBINING GREEK KORONIS..COMBINING GREEK DIALYTIKA TONOS
+0374 ; NFD_QC; N # Sk GREEK NUMERAL SIGN
+037E ; NFD_QC; N # Po GREEK QUESTION MARK
+0385 ; NFD_QC; N # Sk GREEK DIALYTIKA TONOS
+0386 ; NFD_QC; N # L& GREEK CAPITAL LETTER ALPHA WITH TONOS
+0387 ; NFD_QC; N # Po GREEK ANO TELEIA
+0388..038A ; NFD_QC; N # L& [3] GREEK CAPITAL LETTER EPSILON WITH TONOS..GREEK CAPITAL LETTER IOTA WITH TONOS
+038C ; NFD_QC; N # L& GREEK CAPITAL LETTER OMICRON WITH TONOS
+038E..0390 ; NFD_QC; N # L& [3] GREEK CAPITAL LETTER UPSILON WITH TONOS..GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS
+03AA..03B0 ; NFD_QC; N # L& [7] GREEK CAPITAL LETTER IOTA WITH DIALYTIKA..GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS
+03CA..03CE ; NFD_QC; N # L& [5] GREEK SMALL LETTER IOTA WITH DIALYTIKA..GREEK SMALL LETTER OMEGA WITH TONOS
+03D3..03D4 ; NFD_QC; N # L& [2] GREEK UPSILON WITH ACUTE AND HOOK SYMBOL..GREEK UPSILON WITH DIAERESIS AND HOOK SYMBOL
+0400..0401 ; NFD_QC; N # L& [2] CYRILLIC CAPITAL LETTER IE WITH GRAVE..CYRILLIC CAPITAL LETTER IO
+0403 ; NFD_QC; N # L& CYRILLIC CAPITAL LETTER GJE
+0407 ; NFD_QC; N # L& CYRILLIC CAPITAL LETTER YI
+040C..040E ; NFD_QC; N # L& [3] CYRILLIC CAPITAL LETTER KJE..CYRILLIC CAPITAL LETTER SHORT U
+0419 ; NFD_QC; N # L& CYRILLIC CAPITAL LETTER SHORT I
+0439 ; NFD_QC; N # L& CYRILLIC SMALL LETTER SHORT I
+0450..0451 ; NFD_QC; N # L& [2] CYRILLIC SMALL LETTER IE WITH GRAVE..CYRILLIC SMALL LETTER IO
+0453 ; NFD_QC; N # L& CYRILLIC SMALL LETTER GJE
+0457 ; NFD_QC; N # L& CYRILLIC SMALL LETTER YI
+045C..045E ; NFD_QC; N # L& [3] CYRILLIC SMALL LETTER KJE..CYRILLIC SMALL LETTER SHORT U
+0476..0477 ; NFD_QC; N # L& [2] CYRILLIC CAPITAL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT..CYRILLIC SMALL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT
+04C1..04C2 ; NFD_QC; N # L& [2] CYRILLIC CAPITAL LETTER ZHE WITH BREVE..CYRILLIC SMALL LETTER ZHE WITH BREVE
+04D0..04D3 ; NFD_QC; N # L& [4] CYRILLIC CAPITAL LETTER A WITH BREVE..CYRILLIC SMALL LETTER A WITH DIAERESIS
+04D6..04D7 ; NFD_QC; N # L& [2] CYRILLIC CAPITAL LETTER IE WITH BREVE..CYRILLIC SMALL LETTER IE WITH BREVE
+04DA..04DF ; NFD_QC; N # L& [6] CYRILLIC CAPITAL LETTER SCHWA WITH DIAERESIS..CYRILLIC SMALL LETTER ZE WITH DIAERESIS
+04E2..04E7 ; NFD_QC; N # L& [6] CYRILLIC CAPITAL LETTER I WITH MACRON..CYRILLIC SMALL LETTER O WITH DIAERESIS
+04EA..04F5 ; NFD_QC; N # L& [12] CYRILLIC CAPITAL LETTER BARRED O WITH DIAERESIS..CYRILLIC SMALL LETTER CHE WITH DIAERESIS
+04F8..04F9 ; NFD_QC; N # L& [2] CYRILLIC CAPITAL LETTER YERU WITH DIAERESIS..CYRILLIC SMALL LETTER YERU WITH DIAERESIS
+0622..0626 ; NFD_QC; N # Lo [5] ARABIC LETTER ALEF WITH MADDA ABOVE..ARABIC LETTER YEH WITH HAMZA ABOVE
+06C0 ; NFD_QC; N # Lo ARABIC LETTER HEH WITH YEH ABOVE
+06C2 ; NFD_QC; N # Lo ARABIC LETTER HEH GOAL WITH HAMZA ABOVE
+06D3 ; NFD_QC; N # Lo ARABIC LETTER YEH BARREE WITH HAMZA ABOVE
+0929 ; NFD_QC; N # Lo DEVANAGARI LETTER NNNA
+0931 ; NFD_QC; N # Lo DEVANAGARI LETTER RRA
+0934 ; NFD_QC; N # Lo DEVANAGARI LETTER LLLA
+0958..095F ; NFD_QC; N # Lo [8] DEVANAGARI LETTER QA..DEVANAGARI LETTER YYA
+09CB..09CC ; NFD_QC; N # Mc [2] BENGALI VOWEL SIGN O..BENGALI VOWEL SIGN AU
+09DC..09DD ; NFD_QC; N # Lo [2] BENGALI LETTER RRA..BENGALI LETTER RHA
+09DF ; NFD_QC; N # Lo BENGALI LETTER YYA
+0A33 ; NFD_QC; N # Lo GURMUKHI LETTER LLA
+0A36 ; NFD_QC; N # Lo GURMUKHI LETTER SHA
+0A59..0A5B ; NFD_QC; N # Lo [3] GURMUKHI LETTER KHHA..GURMUKHI LETTER ZA
+0A5E ; NFD_QC; N # Lo GURMUKHI LETTER FA
+0B48 ; NFD_QC; N # Mc ORIYA VOWEL SIGN AI
+0B4B..0B4C ; NFD_QC; N # Mc [2] ORIYA VOWEL SIGN O..ORIYA VOWEL SIGN AU
+0B5C..0B5D ; NFD_QC; N # Lo [2] ORIYA LETTER RRA..ORIYA LETTER RHA
+0B94 ; NFD_QC; N # Lo TAMIL LETTER AU
+0BCA..0BCC ; NFD_QC; N # Mc [3] TAMIL VOWEL SIGN O..TAMIL VOWEL SIGN AU
+0C48 ; NFD_QC; N # Mn TELUGU VOWEL SIGN AI
+0CC0 ; NFD_QC; N # Mc KANNADA VOWEL SIGN II
+0CC7..0CC8 ; NFD_QC; N # Mc [2] KANNADA VOWEL SIGN EE..KANNADA VOWEL SIGN AI
+0CCA..0CCB ; NFD_QC; N # Mc [2] KANNADA VOWEL SIGN O..KANNADA VOWEL SIGN OO
+0D4A..0D4C ; NFD_QC; N # Mc [3] MALAYALAM VOWEL SIGN O..MALAYALAM VOWEL SIGN AU
+0DDA ; NFD_QC; N # Mc SINHALA VOWEL SIGN DIGA KOMBUVA
+0DDC..0DDE ; NFD_QC; N # Mc [3] SINHALA VOWEL SIGN KOMBUVA HAA AELA-PILLA..SINHALA VOWEL SIGN KOMBUVA HAA GAYANUKITTA
+0F43 ; NFD_QC; N # Lo TIBETAN LETTER GHA
+0F4D ; NFD_QC; N # Lo TIBETAN LETTER DDHA
+0F52 ; NFD_QC; N # Lo TIBETAN LETTER DHA
+0F57 ; NFD_QC; N # Lo TIBETAN LETTER BHA
+0F5C ; NFD_QC; N # Lo TIBETAN LETTER DZHA
+0F69 ; NFD_QC; N # Lo TIBETAN LETTER KSSA
+0F73 ; NFD_QC; N # Mn TIBETAN VOWEL SIGN II
+0F75..0F76 ; NFD_QC; N # Mn [2] TIBETAN VOWEL SIGN UU..TIBETAN VOWEL SIGN VOCALIC R
+0F78 ; NFD_QC; N # Mn TIBETAN VOWEL SIGN VOCALIC L
+0F81 ; NFD_QC; N # Mn TIBETAN VOWEL SIGN REVERSED II
+0F93 ; NFD_QC; N # Mn TIBETAN SUBJOINED LETTER GHA
+0F9D ; NFD_QC; N # Mn TIBETAN SUBJOINED LETTER DDHA
+0FA2 ; NFD_QC; N # Mn TIBETAN SUBJOINED LETTER DHA
+0FA7 ; NFD_QC; N # Mn TIBETAN SUBJOINED LETTER BHA
+0FAC ; NFD_QC; N # Mn TIBETAN SUBJOINED LETTER DZHA
+0FB9 ; NFD_QC; N # Mn TIBETAN SUBJOINED LETTER KSSA
+1026 ; NFD_QC; N # Lo MYANMAR LETTER UU
+1E00..1E99 ; NFD_QC; N # L& [154] LATIN CAPITAL LETTER A WITH RING BELOW..LATIN SMALL LETTER Y WITH RING ABOVE
+1E9B ; NFD_QC; N # L& LATIN SMALL LETTER LONG S WITH DOT ABOVE
+1EA0..1EF9 ; NFD_QC; N # L& [90] LATIN CAPITAL LETTER A WITH DOT BELOW..LATIN SMALL LETTER Y WITH TILDE
+1F00..1F15 ; NFD_QC; N # L& [22] GREEK SMALL LETTER ALPHA WITH PSILI..GREEK SMALL LETTER EPSILON WITH DASIA AND OXIA
+1F18..1F1D ; NFD_QC; N # L& [6] GREEK CAPITAL LETTER EPSILON WITH PSILI..GREEK CAPITAL LETTER EPSILON WITH DASIA AND OXIA
+1F20..1F45 ; NFD_QC; N # L& [38] GREEK SMALL LETTER ETA WITH PSILI..GREEK SMALL LETTER OMICRON WITH DASIA AND OXIA
+1F48..1F4D ; NFD_QC; N # L& [6] GREEK CAPITAL LETTER OMICRON WITH PSILI..GREEK CAPITAL LETTER OMICRON WITH DASIA AND OXIA
+1F50..1F57 ; NFD_QC; N # L& [8] GREEK SMALL LETTER UPSILON WITH PSILI..GREEK SMALL LETTER UPSILON WITH DASIA AND PERISPOMENI
+1F59 ; NFD_QC; N # L& GREEK CAPITAL LETTER UPSILON WITH DASIA
+1F5B ; NFD_QC; N # L& GREEK CAPITAL LETTER UPSILON WITH DASIA AND VARIA
+1F5D ; NFD_QC; N # L& GREEK CAPITAL LETTER UPSILON WITH DASIA AND OXIA
+1F5F..1F7D ; NFD_QC; N # L& [31] GREEK CAPITAL LETTER UPSILON WITH DASIA AND PERISPOMENI..GREEK SMALL LETTER OMEGA WITH OXIA
+1F80..1FB4 ; NFD_QC; N # L& [53] GREEK SMALL LETTER ALPHA WITH PSILI AND YPOGEGRAMMENI..GREEK SMALL LETTER ALPHA WITH OXIA AND YPOGEGRAMMENI
+1FB6..1FBC ; NFD_QC; N # L& [7] GREEK SMALL LETTER ALPHA WITH PERISPOMENI..GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI
+1FBE ; NFD_QC; N # L& GREEK PROSGEGRAMMENI
+1FC1 ; NFD_QC; N # Sk GREEK DIALYTIKA AND PERISPOMENI
+1FC2..1FC4 ; NFD_QC; N # L& [3] GREEK SMALL LETTER ETA WITH VARIA AND YPOGEGRAMMENI..GREEK SMALL LETTER ETA WITH OXIA AND YPOGEGRAMMENI
+1FC6..1FCC ; NFD_QC; N # L& [7] GREEK SMALL LETTER ETA WITH PERISPOMENI..GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI
+1FCD..1FCF ; NFD_QC; N # Sk [3] GREEK PSILI AND VARIA..GREEK PSILI AND PERISPOMENI
+1FD0..1FD3 ; NFD_QC; N # L& [4] GREEK SMALL LETTER IOTA WITH VRACHY..GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA
+1FD6..1FDB ; NFD_QC; N # L& [6] GREEK SMALL LETTER IOTA WITH PERISPOMENI..GREEK CAPITAL LETTER IOTA WITH OXIA
+1FDD..1FDF ; NFD_QC; N # Sk [3] GREEK DASIA AND VARIA..GREEK DASIA AND PERISPOMENI
+1FE0..1FEC ; NFD_QC; N # L& [13] GREEK SMALL LETTER UPSILON WITH VRACHY..GREEK CAPITAL LETTER RHO WITH DASIA
+1FED..1FEF ; NFD_QC; N # Sk [3] GREEK DIALYTIKA AND VARIA..GREEK VARIA
+1FF2..1FF4 ; NFD_QC; N # L& [3] GREEK SMALL LETTER OMEGA WITH VARIA AND YPOGEGRAMMENI..GREEK SMALL LETTER OMEGA WITH OXIA AND YPOGEGRAMMENI
+1FF6..1FFC ; NFD_QC; N # L& [7] GREEK SMALL LETTER OMEGA WITH PERISPOMENI..GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI
+1FFD ; NFD_QC; N # Sk GREEK OXIA
+2000..2001 ; NFD_QC; N # Zs [2] EN QUAD..EM QUAD
+2126 ; NFD_QC; N # L& OHM SIGN
+212A..212B ; NFD_QC; N # L& [2] KELVIN SIGN..ANGSTROM SIGN
+219A..219B ; NFD_QC; N # Sm [2] LEFTWARDS ARROW WITH STROKE..RIGHTWARDS ARROW WITH STROKE
+21AE ; NFD_QC; N # Sm LEFT RIGHT ARROW WITH STROKE
+21CD ; NFD_QC; N # So LEFTWARDS DOUBLE ARROW WITH STROKE
+21CE..21CF ; NFD_QC; N # Sm [2] LEFT RIGHT DOUBLE ARROW WITH STROKE..RIGHTWARDS DOUBLE ARROW WITH STROKE
+2204 ; NFD_QC; N # Sm THERE DOES NOT EXIST
+2209 ; NFD_QC; N # Sm NOT AN ELEMENT OF
+220C ; NFD_QC; N # Sm DOES NOT CONTAIN AS MEMBER
+2224 ; NFD_QC; N # Sm DOES NOT DIVIDE
+2226 ; NFD_QC; N # Sm NOT PARALLEL TO
+2241 ; NFD_QC; N # Sm NOT TILDE
+2244 ; NFD_QC; N # Sm NOT ASYMPTOTICALLY EQUAL TO
+2247 ; NFD_QC; N # Sm NEITHER APPROXIMATELY NOR ACTUALLY EQUAL TO
+2249 ; NFD_QC; N # Sm NOT ALMOST EQUAL TO
+2260 ; NFD_QC; N # Sm NOT EQUAL TO
+2262 ; NFD_QC; N # Sm NOT IDENTICAL TO
+226D..2271 ; NFD_QC; N # Sm [5] NOT EQUIVALENT TO..NEITHER GREATER-THAN NOR EQUAL TO
+2274..2275 ; NFD_QC; N # Sm [2] NEITHER LESS-THAN NOR EQUIVALENT TO..NEITHER GREATER-THAN NOR EQUIVALENT TO
+2278..2279 ; NFD_QC; N # Sm [2] NEITHER LESS-THAN NOR GREATER-THAN..NEITHER GREATER-THAN NOR LESS-THAN
+2280..2281 ; NFD_QC; N # Sm [2] DOES NOT PRECEDE..DOES NOT SUCCEED
+2284..2285 ; NFD_QC; N # Sm [2] NOT A SUBSET OF..NOT A SUPERSET OF
+2288..2289 ; NFD_QC; N # Sm [2] NEITHER A SUBSET OF NOR EQUAL TO..NEITHER A SUPERSET OF NOR EQUAL TO
+22AC..22AF ; NFD_QC; N # Sm [4] DOES NOT PROVE..NEGATED DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE
+22E0..22E3 ; NFD_QC; N # Sm [4] DOES NOT PRECEDE OR EQUAL..NOT SQUARE ORIGINAL OF OR EQUAL TO
+22EA..22ED ; NFD_QC; N # Sm [4] NOT NORMAL SUBGROUP OF..DOES NOT CONTAIN AS NORMAL SUBGROUP OR EQUAL
+2329 ; NFD_QC; N # Ps LEFT-POINTING ANGLE BRACKET
+232A ; NFD_QC; N # Pe RIGHT-POINTING ANGLE BRACKET
+2ADC ; NFD_QC; N # Sm FORKING
+304C ; NFD_QC; N # Lo HIRAGANA LETTER GA
+304E ; NFD_QC; N # Lo HIRAGANA LETTER GI
+3050 ; NFD_QC; N # Lo HIRAGANA LETTER GU
+3052 ; NFD_QC; N # Lo HIRAGANA LETTER GE
+3054 ; NFD_QC; N # Lo HIRAGANA LETTER GO
+3056 ; NFD_QC; N # Lo HIRAGANA LETTER ZA
+3058 ; NFD_QC; N # Lo HIRAGANA LETTER ZI
+305A ; NFD_QC; N # Lo HIRAGANA LETTER ZU
+305C ; NFD_QC; N # Lo HIRAGANA LETTER ZE
+305E ; NFD_QC; N # Lo HIRAGANA LETTER ZO
+3060 ; NFD_QC; N # Lo HIRAGANA LETTER DA
+3062 ; NFD_QC; N # Lo HIRAGANA LETTER DI
+3065 ; NFD_QC; N # Lo HIRAGANA LETTER DU
+3067 ; NFD_QC; N # Lo HIRAGANA LETTER DE
+3069 ; NFD_QC; N # Lo HIRAGANA LETTER DO
+3070..3071 ; NFD_QC; N # Lo [2] HIRAGANA LETTER BA..HIRAGANA LETTER PA
+3073..3074 ; NFD_QC; N # Lo [2] HIRAGANA LETTER BI..HIRAGANA LETTER PI
+3076..3077 ; NFD_QC; N # Lo [2] HIRAGANA LETTER BU..HIRAGANA LETTER PU
+3079..307A ; NFD_QC; N # Lo [2] HIRAGANA LETTER BE..HIRAGANA LETTER PE
+307C..307D ; NFD_QC; N # Lo [2] HIRAGANA LETTER BO..HIRAGANA LETTER PO
+3094 ; NFD_QC; N # Lo HIRAGANA LETTER VU
+309E ; NFD_QC; N # Lm HIRAGANA VOICED ITERATION MARK
+30AC ; NFD_QC; N # Lo KATAKANA LETTER GA
+30AE ; NFD_QC; N # Lo KATAKANA LETTER GI
+30B0 ; NFD_QC; N # Lo KATAKANA LETTER GU
+30B2 ; NFD_QC; N # Lo KATAKANA LETTER GE
+30B4 ; NFD_QC; N # Lo KATAKANA LETTER GO
+30B6 ; NFD_QC; N # Lo KATAKANA LETTER ZA
+30B8 ; NFD_QC; N # Lo KATAKANA LETTER ZI
+30BA ; NFD_QC; N # Lo KATAKANA LETTER ZU
+30BC ; NFD_QC; N # Lo KATAKANA LETTER ZE
+30BE ; NFD_QC; N # Lo KATAKANA LETTER ZO
+30C0 ; NFD_QC; N # Lo KATAKANA LETTER DA
+30C2 ; NFD_QC; N # Lo KATAKANA LETTER DI
+30C5 ; NFD_QC; N # Lo KATAKANA LETTER DU
+30C7 ; NFD_QC; N # Lo KATAKANA LETTER DE
+30C9 ; NFD_QC; N # Lo KATAKANA LETTER DO
+30D0..30D1 ; NFD_QC; N # Lo [2] KATAKANA LETTER BA..KATAKANA LETTER PA
+30D3..30D4 ; NFD_QC; N # Lo [2] KATAKANA LETTER BI..KATAKANA LETTER PI
+30D6..30D7 ; NFD_QC; N # Lo [2] KATAKANA LETTER BU..KATAKANA LETTER PU
+30D9..30DA ; NFD_QC; N # Lo [2] KATAKANA LETTER BE..KATAKANA LETTER PE
+30DC..30DD ; NFD_QC; N # Lo [2] KATAKANA LETTER BO..KATAKANA LETTER PO
+30F4 ; NFD_QC; N # Lo KATAKANA LETTER VU
+30F7..30FA ; NFD_QC; N # Lo [4] KATAKANA LETTER VA..KATAKANA LETTER VO
+30FE ; NFD_QC; N # Lm KATAKANA VOICED ITERATION MARK
+AC00..D7A3 ; NFD_QC; N # Lo [11172] HANGUL SYLLABLE GA..HANGUL SYLLABLE HIH
+F900..FA0D ; NFD_QC; N # Lo [270] CJK COMPATIBILITY IDEOGRAPH-F900..CJK COMPATIBILITY IDEOGRAPH-FA0D
+FA10 ; NFD_QC; N # Lo CJK COMPATIBILITY IDEOGRAPH-FA10
+FA12 ; NFD_QC; N # Lo CJK COMPATIBILITY IDEOGRAPH-FA12
+FA15..FA1E ; NFD_QC; N # Lo [10] CJK COMPATIBILITY IDEOGRAPH-FA15..CJK COMPATIBILITY IDEOGRAPH-FA1E
+FA20 ; NFD_QC; N # Lo CJK COMPATIBILITY IDEOGRAPH-FA20
+FA22 ; NFD_QC; N # Lo CJK COMPATIBILITY IDEOGRAPH-FA22
+FA25..FA26 ; NFD_QC; N # Lo [2] CJK COMPATIBILITY IDEOGRAPH-FA25..CJK COMPATIBILITY IDEOGRAPH-FA26
+FA2A..FA2D ; NFD_QC; N # Lo [4] CJK COMPATIBILITY IDEOGRAPH-FA2A..CJK COMPATIBILITY IDEOGRAPH-FA2D
+FA30..FA6A ; NFD_QC; N # Lo [59] CJK COMPATIBILITY IDEOGRAPH-FA30..CJK COMPATIBILITY IDEOGRAPH-FA6A
+FB1D ; NFD_QC; N # Lo HEBREW LETTER YOD WITH HIRIQ
+FB1F ; NFD_QC; N # Lo HEBREW LIGATURE YIDDISH YOD YOD PATAH
+FB2A..FB36 ; NFD_QC; N # Lo [13] HEBREW LETTER SHIN WITH SHIN DOT..HEBREW LETTER ZAYIN WITH DAGESH
+FB38..FB3C ; NFD_QC; N # Lo [5] HEBREW LETTER TET WITH DAGESH..HEBREW LETTER LAMED WITH DAGESH
+FB3E ; NFD_QC; N # Lo HEBREW LETTER MEM WITH DAGESH
+FB40..FB41 ; NFD_QC; N # Lo [2] HEBREW LETTER NUN WITH DAGESH..HEBREW LETTER SAMEKH WITH DAGESH
+FB43..FB44 ; NFD_QC; N # Lo [2] HEBREW LETTER FINAL PE WITH DAGESH..HEBREW LETTER PE WITH DAGESH
+FB46..FB4E ; NFD_QC; N # Lo [9] HEBREW LETTER TSADI WITH DAGESH..HEBREW LETTER PE WITH RAFE
+1D15E..1D164 ; NFD_QC; N # So [7] MUSICAL SYMBOL HALF NOTE..MUSICAL SYMBOL ONE HUNDRED TWENTY-EIGHTH NOTE
+1D1BB..1D1C0 ; NFD_QC; N # So [6] MUSICAL SYMBOL MINIMA..MUSICAL SYMBOL FUSA BLACK
+2F800..2FA1D ; NFD_QC; N # Lo [542] CJK COMPATIBILITY IDEOGRAPH-2F800..CJK COMPATIBILITY IDEOGRAPH-2FA1D
+
+# Total code points: 13098
+
+# ================================================
+
+# Property: NFC_Quick_Check
+
+# All code points not explicitly listed for NFC_Quick_Check
+# have the value Yes (Y).
+
+# ================================================
+
+# NFC_Quick_Check=No
+
+0340..0341 ; NFC_QC; N # Mn [2] COMBINING GRAVE TONE MARK..COMBINING ACUTE TONE MARK
+0343..0344 ; NFC_QC; N # Mn [2] COMBINING GREEK KORONIS..COMBINING GREEK DIALYTIKA TONOS
+0374 ; NFC_QC; N # Sk GREEK NUMERAL SIGN
+037E ; NFC_QC; N # Po GREEK QUESTION MARK
+0387 ; NFC_QC; N # Po GREEK ANO TELEIA
+0958..095F ; NFC_QC; N # Lo [8] DEVANAGARI LETTER QA..DEVANAGARI LETTER YYA
+09DC..09DD ; NFC_QC; N # Lo [2] BENGALI LETTER RRA..BENGALI LETTER RHA
+09DF ; NFC_QC; N # Lo BENGALI LETTER YYA
+0A33 ; NFC_QC; N # Lo GURMUKHI LETTER LLA
+0A36 ; NFC_QC; N # Lo GURMUKHI LETTER SHA
+0A59..0A5B ; NFC_QC; N # Lo [3] GURMUKHI LETTER KHHA..GURMUKHI LETTER ZA
+0A5E ; NFC_QC; N # Lo GURMUKHI LETTER FA
+0B5C..0B5D ; NFC_QC; N # Lo [2] ORIYA LETTER RRA..ORIYA LETTER RHA
+0F43 ; NFC_QC; N # Lo TIBETAN LETTER GHA
+0F4D ; NFC_QC; N # Lo TIBETAN LETTER DDHA
+0F52 ; NFC_QC; N # Lo TIBETAN LETTER DHA
+0F57 ; NFC_QC; N # Lo TIBETAN LETTER BHA
+0F5C ; NFC_QC; N # Lo TIBETAN LETTER DZHA
+0F69 ; NFC_QC; N # Lo TIBETAN LETTER KSSA
+0F73 ; NFC_QC; N # Mn TIBETAN VOWEL SIGN II
+0F75..0F76 ; NFC_QC; N # Mn [2] TIBETAN VOWEL SIGN UU..TIBETAN VOWEL SIGN VOCALIC R
+0F78 ; NFC_QC; N # Mn TIBETAN VOWEL SIGN VOCALIC L
+0F81 ; NFC_QC; N # Mn TIBETAN VOWEL SIGN REVERSED II
+0F93 ; NFC_QC; N # Mn TIBETAN SUBJOINED LETTER GHA
+0F9D ; NFC_QC; N # Mn TIBETAN SUBJOINED LETTER DDHA
+0FA2 ; NFC_QC; N # Mn TIBETAN SUBJOINED LETTER DHA
+0FA7 ; NFC_QC; N # Mn TIBETAN SUBJOINED LETTER BHA
+0FAC ; NFC_QC; N # Mn TIBETAN SUBJOINED LETTER DZHA
+0FB9 ; NFC_QC; N # Mn TIBETAN SUBJOINED LETTER KSSA
+1F71 ; NFC_QC; N # L& GREEK SMALL LETTER ALPHA WITH OXIA
+1F73 ; NFC_QC; N # L& GREEK SMALL LETTER EPSILON WITH OXIA
+1F75 ; NFC_QC; N # L& GREEK SMALL LETTER ETA WITH OXIA
+1F77 ; NFC_QC; N # L& GREEK SMALL LETTER IOTA WITH OXIA
+1F79 ; NFC_QC; N # L& GREEK SMALL LETTER OMICRON WITH OXIA
+1F7B ; NFC_QC; N # L& GREEK SMALL LETTER UPSILON WITH OXIA
+1F7D ; NFC_QC; N # L& GREEK SMALL LETTER OMEGA WITH OXIA
+1FBB ; NFC_QC; N # L& GREEK CAPITAL LETTER ALPHA WITH OXIA
+1FBE ; NFC_QC; N # L& GREEK PROSGEGRAMMENI
+1FC9 ; NFC_QC; N # L& GREEK CAPITAL LETTER EPSILON WITH OXIA
+1FCB ; NFC_QC; N # L& GREEK CAPITAL LETTER ETA WITH OXIA
+1FD3 ; NFC_QC; N # L& GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA
+1FDB ; NFC_QC; N # L& GREEK CAPITAL LETTER IOTA WITH OXIA
+1FE3 ; NFC_QC; N # L& GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND OXIA
+1FEB ; NFC_QC; N # L& GREEK CAPITAL LETTER UPSILON WITH OXIA
+1FEE..1FEF ; NFC_QC; N # Sk [2] GREEK DIALYTIKA AND OXIA..GREEK VARIA
+1FF9 ; NFC_QC; N # L& GREEK CAPITAL LETTER OMICRON WITH OXIA
+1FFB ; NFC_QC; N # L& GREEK CAPITAL LETTER OMEGA WITH OXIA
+1FFD ; NFC_QC; N # Sk GREEK OXIA
+2000..2001 ; NFC_QC; N # Zs [2] EN QUAD..EM QUAD
+2126 ; NFC_QC; N # L& OHM SIGN
+212A..212B ; NFC_QC; N # L& [2] KELVIN SIGN..ANGSTROM SIGN
+2329 ; NFC_QC; N # Ps LEFT-POINTING ANGLE BRACKET
+232A ; NFC_QC; N # Pe RIGHT-POINTING ANGLE BRACKET
+2ADC ; NFC_QC; N # Sm FORKING
+F900..FA0D ; NFC_QC; N # Lo [270] CJK COMPATIBILITY IDEOGRAPH-F900..CJK COMPATIBILITY IDEOGRAPH-FA0D
+FA10 ; NFC_QC; N # Lo CJK COMPATIBILITY IDEOGRAPH-FA10
+FA12 ; NFC_QC; N # Lo CJK COMPATIBILITY IDEOGRAPH-FA12
+FA15..FA1E ; NFC_QC; N # Lo [10] CJK COMPATIBILITY IDEOGRAPH-FA15..CJK COMPATIBILITY IDEOGRAPH-FA1E
+FA20 ; NFC_QC; N # Lo CJK COMPATIBILITY IDEOGRAPH-FA20
+FA22 ; NFC_QC; N # Lo CJK COMPATIBILITY IDEOGRAPH-FA22
+FA25..FA26 ; NFC_QC; N # Lo [2] CJK COMPATIBILITY IDEOGRAPH-FA25..CJK COMPATIBILITY IDEOGRAPH-FA26
+FA2A..FA2D ; NFC_QC; N # Lo [4] CJK COMPATIBILITY IDEOGRAPH-FA2A..CJK COMPATIBILITY IDEOGRAPH-FA2D
+FA30..FA6A ; NFC_QC; N # Lo [59] CJK COMPATIBILITY IDEOGRAPH-FA30..CJK COMPATIBILITY IDEOGRAPH-FA6A
+FB1D ; NFC_QC; N # Lo HEBREW LETTER YOD WITH HIRIQ
+FB1F ; NFC_QC; N # Lo HEBREW LIGATURE YIDDISH YOD YOD PATAH
+FB2A..FB36 ; NFC_QC; N # Lo [13] HEBREW LETTER SHIN WITH SHIN DOT..HEBREW LETTER ZAYIN WITH DAGESH
+FB38..FB3C ; NFC_QC; N # Lo [5] HEBREW LETTER TET WITH DAGESH..HEBREW LETTER LAMED WITH DAGESH
+FB3E ; NFC_QC; N # Lo HEBREW LETTER MEM WITH DAGESH
+FB40..FB41 ; NFC_QC; N # Lo [2] HEBREW LETTER NUN WITH DAGESH..HEBREW LETTER SAMEKH WITH DAGESH
+FB43..FB44 ; NFC_QC; N # Lo [2] HEBREW LETTER FINAL PE WITH DAGESH..HEBREW LETTER PE WITH DAGESH
+FB46..FB4E ; NFC_QC; N # Lo [9] HEBREW LETTER TSADI WITH DAGESH..HEBREW LETTER PE WITH RAFE
+1D15E..1D164 ; NFC_QC; N # So [7] MUSICAL SYMBOL HALF NOTE..MUSICAL SYMBOL ONE HUNDRED TWENTY-EIGHTH NOTE
+1D1BB..1D1C0 ; NFC_QC; N # So [6] MUSICAL SYMBOL MINIMA..MUSICAL SYMBOL FUSA BLACK
+2F800..2FA1D ; NFC_QC; N # Lo [542] CJK COMPATIBILITY IDEOGRAPH-2F800..CJK COMPATIBILITY IDEOGRAPH-2FA1D
+
+# Total code points: 1009
+
+# ================================================
+
+# NFC_Quick_Check=Maybe
+
+0300..0304 ; NFC_QC; M # Mn [5] COMBINING GRAVE ACCENT..COMBINING MACRON
+0306..030C ; NFC_QC; M # Mn [7] COMBINING BREVE..COMBINING CARON
+030F ; NFC_QC; M # Mn COMBINING DOUBLE GRAVE ACCENT
+0311 ; NFC_QC; M # Mn COMBINING INVERTED BREVE
+0313..0314 ; NFC_QC; M # Mn [2] COMBINING COMMA ABOVE..COMBINING REVERSED COMMA ABOVE
+031B ; NFC_QC; M # Mn COMBINING HORN
+0323..0328 ; NFC_QC; M # Mn [6] COMBINING DOT BELOW..COMBINING OGONEK
+032D..032E ; NFC_QC; M # Mn [2] COMBINING CIRCUMFLEX ACCENT BELOW..COMBINING BREVE BELOW
+0330..0331 ; NFC_QC; M # Mn [2] COMBINING TILDE BELOW..COMBINING MACRON BELOW
+0338 ; NFC_QC; M # Mn COMBINING LONG SOLIDUS OVERLAY
+0342 ; NFC_QC; M # Mn COMBINING GREEK PERISPOMENI
+0345 ; NFC_QC; M # Mn COMBINING GREEK YPOGEGRAMMENI
+0653..0655 ; NFC_QC; M # Mn [3] ARABIC MADDAH ABOVE..ARABIC HAMZA BELOW
+093C ; NFC_QC; M # Mn DEVANAGARI SIGN NUKTA
+09BE ; NFC_QC; M # Mc BENGALI VOWEL SIGN AA
+09D7 ; NFC_QC; M # Mc BENGALI AU LENGTH MARK
+0B3E ; NFC_QC; M # Mc ORIYA VOWEL SIGN AA
+0B56 ; NFC_QC; M # Mn ORIYA AI LENGTH MARK
+0B57 ; NFC_QC; M # Mc ORIYA AU LENGTH MARK
+0BBE ; NFC_QC; M # Mc TAMIL VOWEL SIGN AA
+0BD7 ; NFC_QC; M # Mc TAMIL AU LENGTH MARK
+0C56 ; NFC_QC; M # Mn TELUGU AI LENGTH MARK
+0CC2 ; NFC_QC; M # Mc KANNADA VOWEL SIGN UU
+0CD5..0CD6 ; NFC_QC; M # Mc [2] KANNADA LENGTH MARK..KANNADA AI LENGTH MARK
+0D3E ; NFC_QC; M # Mc MALAYALAM VOWEL SIGN AA
+0D57 ; NFC_QC; M # Mc MALAYALAM AU LENGTH MARK
+0DCA ; NFC_QC; M # Mn SINHALA SIGN AL-LAKUNA
+0DCF ; NFC_QC; M # Mc SINHALA VOWEL SIGN AELA-PILLA
+0DDF ; NFC_QC; M # Mc SINHALA VOWEL SIGN GAYANUKITTA
+102E ; NFC_QC; M # Mn MYANMAR VOWEL SIGN II
+1161..1175 ; NFC_QC; M # Lo [21] HANGUL JUNGSEONG A..HANGUL JUNGSEONG I
+11A8..11C2 ; NFC_QC; M # Lo [27] HANGUL JONGSEONG KIYEOK..HANGUL JONGSEONG HIEUH
+3099..309A ; NFC_QC; M # Mn [2] COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK..COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK
+
+# Total code points: 101
+
+# ================================================
+
+# Property: NFKD_Quick_Check
+
+# All code points not explicitly listed for NFKD_Quick_Check
+# have the value Yes (Y).
+
+# ================================================
+
+# NFKD_Quick_Check=No
+
+00A0 ; NFKD_QC; N # Zs NO-BREAK SPACE
+00A8 ; NFKD_QC; N # Sk DIAERESIS
+00AA ; NFKD_QC; N # L& FEMININE ORDINAL INDICATOR
+00AF ; NFKD_QC; N # Sk MACRON
+00B2..00B3 ; NFKD_QC; N # No [2] SUPERSCRIPT TWO..SUPERSCRIPT THREE
+00B4 ; NFKD_QC; N # Sk ACUTE ACCENT
+00B5 ; NFKD_QC; N # L& MICRO SIGN
+00B8 ; NFKD_QC; N # Sk CEDILLA
+00B9 ; NFKD_QC; N # No SUPERSCRIPT ONE
+00BA ; NFKD_QC; N # L& MASCULINE ORDINAL INDICATOR
+00BC..00BE ; NFKD_QC; N # No [3] VULGAR FRACTION ONE QUARTER..VULGAR FRACTION THREE QUARTERS
+00C0..00C5 ; NFKD_QC; N # L& [6] LATIN CAPITAL LETTER A WITH GRAVE..LATIN CAPITAL LETTER A WITH RING ABOVE
+00C7..00CF ; NFKD_QC; N # L& [9] LATIN CAPITAL LETTER C WITH CEDILLA..LATIN CAPITAL LETTER I WITH DIAERESIS
+00D1..00D6 ; NFKD_QC; N # L& [6] LATIN CAPITAL LETTER N WITH TILDE..LATIN CAPITAL LETTER O WITH DIAERESIS
+00D9..00DD ; NFKD_QC; N # L& [5] LATIN CAPITAL LETTER U WITH GRAVE..LATIN CAPITAL LETTER Y WITH ACUTE
+00E0..00E5 ; NFKD_QC; N # L& [6] LATIN SMALL LETTER A WITH GRAVE..LATIN SMALL LETTER A WITH RING ABOVE
+00E7..00EF ; NFKD_QC; N # L& [9] LATIN SMALL LETTER C WITH CEDILLA..LATIN SMALL LETTER I WITH DIAERESIS
+00F1..00F6 ; NFKD_QC; N # L& [6] LATIN SMALL LETTER N WITH TILDE..LATIN SMALL LETTER O WITH DIAERESIS
+00F9..00FD ; NFKD_QC; N # L& [5] LATIN SMALL LETTER U WITH GRAVE..LATIN SMALL LETTER Y WITH ACUTE
+00FF..010F ; NFKD_QC; N # L& [17] LATIN SMALL LETTER Y WITH DIAERESIS..LATIN SMALL LETTER D WITH CARON
+0112..0125 ; NFKD_QC; N # L& [20] LATIN CAPITAL LETTER E WITH MACRON..LATIN SMALL LETTER H WITH CIRCUMFLEX
+0128..0130 ; NFKD_QC; N # L& [9] LATIN CAPITAL LETTER I WITH TILDE..LATIN CAPITAL LETTER I WITH DOT ABOVE
+0132..0137 ; NFKD_QC; N # L& [6] LATIN CAPITAL LIGATURE IJ..LATIN SMALL LETTER K WITH CEDILLA
+0139..0140 ; NFKD_QC; N # L& [8] LATIN CAPITAL LETTER L WITH ACUTE..LATIN SMALL LETTER L WITH MIDDLE DOT
+0143..0149 ; NFKD_QC; N # L& [7] LATIN CAPITAL LETTER N WITH ACUTE..LATIN SMALL LETTER N PRECEDED BY APOSTROPHE
+014C..0151 ; NFKD_QC; N # L& [6] LATIN CAPITAL LETTER O WITH MACRON..LATIN SMALL LETTER O WITH DOUBLE ACUTE
+0154..0165 ; NFKD_QC; N # L& [18] LATIN CAPITAL LETTER R WITH ACUTE..LATIN SMALL LETTER T WITH CARON
+0168..017F ; NFKD_QC; N # L& [24] LATIN CAPITAL LETTER U WITH TILDE..LATIN SMALL LETTER LONG S
+01A0..01A1 ; NFKD_QC; N # L& [2] LATIN CAPITAL LETTER O WITH HORN..LATIN SMALL LETTER O WITH HORN
+01AF..01B0 ; NFKD_QC; N # L& [2] LATIN CAPITAL LETTER U WITH HORN..LATIN SMALL LETTER U WITH HORN
+01C4..01DC ; NFKD_QC; N # L& [25] LATIN CAPITAL LETTER DZ WITH CARON..LATIN SMALL LETTER U WITH DIAERESIS AND GRAVE
+01DE..01E3 ; NFKD_QC; N # L& [6] LATIN CAPITAL LETTER A WITH DIAERESIS AND MACRON..LATIN SMALL LETTER AE WITH MACRON
+01E6..01F5 ; NFKD_QC; N # L& [16] LATIN CAPITAL LETTER G WITH CARON..LATIN SMALL LETTER G WITH ACUTE
+01F8..021B ; NFKD_QC; N # L& [36] LATIN CAPITAL LETTER N WITH GRAVE..LATIN SMALL LETTER T WITH COMMA BELOW
+021E..021F ; NFKD_QC; N # L& [2] LATIN CAPITAL LETTER H WITH CARON..LATIN SMALL LETTER H WITH CARON
+0226..0233 ; NFKD_QC; N # L& [14] LATIN CAPITAL LETTER A WITH DOT ABOVE..LATIN SMALL LETTER Y WITH MACRON
+02B0..02B8 ; NFKD_QC; N # Lm [9] MODIFIER LETTER SMALL H..MODIFIER LETTER SMALL Y
+02D8..02DD ; NFKD_QC; N # Sk [6] BREVE..DOUBLE ACUTE ACCENT
+02E0..02E4 ; NFKD_QC; N # Lm [5] MODIFIER LETTER SMALL GAMMA..MODIFIER LETTER SMALL REVERSED GLOTTAL STOP
+0340..0341 ; NFKD_QC; N # Mn [2] COMBINING GRAVE TONE MARK..COMBINING ACUTE TONE MARK
+0343..0344 ; NFKD_QC; N # Mn [2] COMBINING GREEK KORONIS..COMBINING GREEK DIALYTIKA TONOS
+0374 ; NFKD_QC; N # Sk GREEK NUMERAL SIGN
+037A ; NFKD_QC; N # Lm GREEK YPOGEGRAMMENI
+037E ; NFKD_QC; N # Po GREEK QUESTION MARK
+0384..0385 ; NFKD_QC; N # Sk [2] GREEK TONOS..GREEK DIALYTIKA TONOS
+0386 ; NFKD_QC; N # L& GREEK CAPITAL LETTER ALPHA WITH TONOS
+0387 ; NFKD_QC; N # Po GREEK ANO TELEIA
+0388..038A ; NFKD_QC; N # L& [3] GREEK CAPITAL LETTER EPSILON WITH TONOS..GREEK CAPITAL LETTER IOTA WITH TONOS
+038C ; NFKD_QC; N # L& GREEK CAPITAL LETTER OMICRON WITH TONOS
+038E..0390 ; NFKD_QC; N # L& [3] GREEK CAPITAL LETTER UPSILON WITH TONOS..GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS
+03AA..03B0 ; NFKD_QC; N # L& [7] GREEK CAPITAL LETTER IOTA WITH DIALYTIKA..GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS
+03CA..03CE ; NFKD_QC; N # L& [5] GREEK SMALL LETTER IOTA WITH DIALYTIKA..GREEK SMALL LETTER OMEGA WITH TONOS
+03D0..03D6 ; NFKD_QC; N # L& [7] GREEK BETA SYMBOL..GREEK PI SYMBOL
+03F0..03F2 ; NFKD_QC; N # L& [3] GREEK KAPPA SYMBOL..GREEK LUNATE SIGMA SYMBOL
+03F4..03F5 ; NFKD_QC; N # L& [2] GREEK CAPITAL THETA SYMBOL..GREEK LUNATE EPSILON SYMBOL
+03F9 ; NFKD_QC; N # L& GREEK CAPITAL LUNATE SIGMA SYMBOL
+0400..0401 ; NFKD_QC; N # L& [2] CYRILLIC CAPITAL LETTER IE WITH GRAVE..CYRILLIC CAPITAL LETTER IO
+0403 ; NFKD_QC; N # L& CYRILLIC CAPITAL LETTER GJE
+0407 ; NFKD_QC; N # L& CYRILLIC CAPITAL LETTER YI
+040C..040E ; NFKD_QC; N # L& [3] CYRILLIC CAPITAL LETTER KJE..CYRILLIC CAPITAL LETTER SHORT U
+0419 ; NFKD_QC; N # L& CYRILLIC CAPITAL LETTER SHORT I
+0439 ; NFKD_QC; N # L& CYRILLIC SMALL LETTER SHORT I
+0450..0451 ; NFKD_QC; N # L& [2] CYRILLIC SMALL LETTER IE WITH GRAVE..CYRILLIC SMALL LETTER IO
+0453 ; NFKD_QC; N # L& CYRILLIC SMALL LETTER GJE
+0457 ; NFKD_QC; N # L& CYRILLIC SMALL LETTER YI
+045C..045E ; NFKD_QC; N # L& [3] CYRILLIC SMALL LETTER KJE..CYRILLIC SMALL LETTER SHORT U
+0476..0477 ; NFKD_QC; N # L& [2] CYRILLIC CAPITAL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT..CYRILLIC SMALL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT
+04C1..04C2 ; NFKD_QC; N # L& [2] CYRILLIC CAPITAL LETTER ZHE WITH BREVE..CYRILLIC SMALL LETTER ZHE WITH BREVE
+04D0..04D3 ; NFKD_QC; N # L& [4] CYRILLIC CAPITAL LETTER A WITH BREVE..CYRILLIC SMALL LETTER A WITH DIAERESIS
+04D6..04D7 ; NFKD_QC; N # L& [2] CYRILLIC CAPITAL LETTER IE WITH BREVE..CYRILLIC SMALL LETTER IE WITH BREVE
+04DA..04DF ; NFKD_QC; N # L& [6] CYRILLIC CAPITAL LETTER SCHWA WITH DIAERESIS..CYRILLIC SMALL LETTER ZE WITH DIAERESIS
+04E2..04E7 ; NFKD_QC; N # L& [6] CYRILLIC CAPITAL LETTER I WITH MACRON..CYRILLIC SMALL LETTER O WITH DIAERESIS
+04EA..04F5 ; NFKD_QC; N # L& [12] CYRILLIC CAPITAL LETTER BARRED O WITH DIAERESIS..CYRILLIC SMALL LETTER CHE WITH DIAERESIS
+04F8..04F9 ; NFKD_QC; N # L& [2] CYRILLIC CAPITAL LETTER YERU WITH DIAERESIS..CYRILLIC SMALL LETTER YERU WITH DIAERESIS
+0587 ; NFKD_QC; N # L& ARMENIAN SMALL LIGATURE ECH YIWN
+0622..0626 ; NFKD_QC; N # Lo [5] ARABIC LETTER ALEF WITH MADDA ABOVE..ARABIC LETTER YEH WITH HAMZA ABOVE
+0675..0678 ; NFKD_QC; N # Lo [4] ARABIC LETTER HIGH HAMZA ALEF..ARABIC LETTER HIGH HAMZA YEH
+06C0 ; NFKD_QC; N # Lo ARABIC LETTER HEH WITH YEH ABOVE
+06C2 ; NFKD_QC; N # Lo ARABIC LETTER HEH GOAL WITH HAMZA ABOVE
+06D3 ; NFKD_QC; N # Lo ARABIC LETTER YEH BARREE WITH HAMZA ABOVE
+0929 ; NFKD_QC; N # Lo DEVANAGARI LETTER NNNA
+0931 ; NFKD_QC; N # Lo DEVANAGARI LETTER RRA
+0934 ; NFKD_QC; N # Lo DEVANAGARI LETTER LLLA
+0958..095F ; NFKD_QC; N # Lo [8] DEVANAGARI LETTER QA..DEVANAGARI LETTER YYA
+09CB..09CC ; NFKD_QC; N # Mc [2] BENGALI VOWEL SIGN O..BENGALI VOWEL SIGN AU
+09DC..09DD ; NFKD_QC; N # Lo [2] BENGALI LETTER RRA..BENGALI LETTER RHA
+09DF ; NFKD_QC; N # Lo BENGALI LETTER YYA
+0A33 ; NFKD_QC; N # Lo GURMUKHI LETTER LLA
+0A36 ; NFKD_QC; N # Lo GURMUKHI LETTER SHA
+0A59..0A5B ; NFKD_QC; N # Lo [3] GURMUKHI LETTER KHHA..GURMUKHI LETTER ZA
+0A5E ; NFKD_QC; N # Lo GURMUKHI LETTER FA
+0B48 ; NFKD_QC; N # Mc ORIYA VOWEL SIGN AI
+0B4B..0B4C ; NFKD_QC; N # Mc [2] ORIYA VOWEL SIGN O..ORIYA VOWEL SIGN AU
+0B5C..0B5D ; NFKD_QC; N # Lo [2] ORIYA LETTER RRA..ORIYA LETTER RHA
+0B94 ; NFKD_QC; N # Lo TAMIL LETTER AU
+0BCA..0BCC ; NFKD_QC; N # Mc [3] TAMIL VOWEL SIGN O..TAMIL VOWEL SIGN AU
+0C48 ; NFKD_QC; N # Mn TELUGU VOWEL SIGN AI
+0CC0 ; NFKD_QC; N # Mc KANNADA VOWEL SIGN II
+0CC7..0CC8 ; NFKD_QC; N # Mc [2] KANNADA VOWEL SIGN EE..KANNADA VOWEL SIGN AI
+0CCA..0CCB ; NFKD_QC; N # Mc [2] KANNADA VOWEL SIGN O..KANNADA VOWEL SIGN OO
+0D4A..0D4C ; NFKD_QC; N # Mc [3] MALAYALAM VOWEL SIGN O..MALAYALAM VOWEL SIGN AU
+0DDA ; NFKD_QC; N # Mc SINHALA VOWEL SIGN DIGA KOMBUVA
+0DDC..0DDE ; NFKD_QC; N # Mc [3] SINHALA VOWEL SIGN KOMBUVA HAA AELA-PILLA..SINHALA VOWEL SIGN KOMBUVA HAA GAYANUKITTA
+0E33 ; NFKD_QC; N # Lo THAI CHARACTER SARA AM
+0EB3 ; NFKD_QC; N # Lo LAO VOWEL SIGN AM
+0EDC..0EDD ; NFKD_QC; N # Lo [2] LAO HO NO..LAO HO MO
+0F0C ; NFKD_QC; N # Po TIBETAN MARK DELIMITER TSHEG BSTAR
+0F43 ; NFKD_QC; N # Lo TIBETAN LETTER GHA
+0F4D ; NFKD_QC; N # Lo TIBETAN LETTER DDHA
+0F52 ; NFKD_QC; N # Lo TIBETAN LETTER DHA
+0F57 ; NFKD_QC; N # Lo TIBETAN LETTER BHA
+0F5C ; NFKD_QC; N # Lo TIBETAN LETTER DZHA
+0F69 ; NFKD_QC; N # Lo TIBETAN LETTER KSSA
+0F73 ; NFKD_QC; N # Mn TIBETAN VOWEL SIGN II
+0F75..0F79 ; NFKD_QC; N # Mn [5] TIBETAN VOWEL SIGN UU..TIBETAN VOWEL SIGN VOCALIC LL
+0F81 ; NFKD_QC; N # Mn TIBETAN VOWEL SIGN REVERSED II
+0F93 ; NFKD_QC; N # Mn TIBETAN SUBJOINED LETTER GHA
+0F9D ; NFKD_QC; N # Mn TIBETAN SUBJOINED LETTER DDHA
+0FA2 ; NFKD_QC; N # Mn TIBETAN SUBJOINED LETTER DHA
+0FA7 ; NFKD_QC; N # Mn TIBETAN SUBJOINED LETTER BHA
+0FAC ; NFKD_QC; N # Mn TIBETAN SUBJOINED LETTER DZHA
+0FB9 ; NFKD_QC; N # Mn TIBETAN SUBJOINED LETTER KSSA
+1026 ; NFKD_QC; N # Lo MYANMAR LETTER UU
+1D2C..1D2E ; NFKD_QC; N # Lm [3] MODIFIER LETTER CAPITAL A..MODIFIER LETTER CAPITAL B
+1D30..1D3A ; NFKD_QC; N # Lm [11] MODIFIER LETTER CAPITAL D..MODIFIER LETTER CAPITAL N
+1D3C..1D4D ; NFKD_QC; N # Lm [18] MODIFIER LETTER CAPITAL O..MODIFIER LETTER SMALL G
+1D4F..1D61 ; NFKD_QC; N # Lm [19] MODIFIER LETTER SMALL K..MODIFIER LETTER SMALL CHI
+1D62..1D6A ; NFKD_QC; N # L& [9] LATIN SUBSCRIPT SMALL LETTER I..GREEK SUBSCRIPT SMALL LETTER CHI
+1E00..1E9B ; NFKD_QC; N # L& [156] LATIN CAPITAL LETTER A WITH RING BELOW..LATIN SMALL LETTER LONG S WITH DOT ABOVE
+1EA0..1EF9 ; NFKD_QC; N # L& [90] LATIN CAPITAL LETTER A WITH DOT BELOW..LATIN SMALL LETTER Y WITH TILDE
+1F00..1F15 ; NFKD_QC; N # L& [22] GREEK SMALL LETTER ALPHA WITH PSILI..GREEK SMALL LETTER EPSILON WITH DASIA AND OXIA
+1F18..1F1D ; NFKD_QC; N # L& [6] GREEK CAPITAL LETTER EPSILON WITH PSILI..GREEK CAPITAL LETTER EPSILON WITH DASIA AND OXIA
+1F20..1F45 ; NFKD_QC; N # L& [38] GREEK SMALL LETTER ETA WITH PSILI..GREEK SMALL LETTER OMICRON WITH DASIA AND OXIA
+1F48..1F4D ; NFKD_QC; N # L& [6] GREEK CAPITAL LETTER OMICRON WITH PSILI..GREEK CAPITAL LETTER OMICRON WITH DASIA AND OXIA
+1F50..1F57 ; NFKD_QC; N # L& [8] GREEK SMALL LETTER UPSILON WITH PSILI..GREEK SMALL LETTER UPSILON WITH DASIA AND PERISPOMENI
+1F59 ; NFKD_QC; N # L& GREEK CAPITAL LETTER UPSILON WITH DASIA
+1F5B ; NFKD_QC; N # L& GREEK CAPITAL LETTER UPSILON WITH DASIA AND VARIA
+1F5D ; NFKD_QC; N # L& GREEK CAPITAL LETTER UPSILON WITH DASIA AND OXIA
+1F5F..1F7D ; NFKD_QC; N # L& [31] GREEK CAPITAL LETTER UPSILON WITH DASIA AND PERISPOMENI..GREEK SMALL LETTER OMEGA WITH OXIA
+1F80..1FB4 ; NFKD_QC; N # L& [53] GREEK SMALL LETTER ALPHA WITH PSILI AND YPOGEGRAMMENI..GREEK SMALL LETTER ALPHA WITH OXIA AND YPOGEGRAMMENI
+1FB6..1FBC ; NFKD_QC; N # L& [7] GREEK SMALL LETTER ALPHA WITH PERISPOMENI..GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI
+1FBD ; NFKD_QC; N # Sk GREEK KORONIS
+1FBE ; NFKD_QC; N # L& GREEK PROSGEGRAMMENI
+1FBF..1FC1 ; NFKD_QC; N # Sk [3] GREEK PSILI..GREEK DIALYTIKA AND PERISPOMENI
+1FC2..1FC4 ; NFKD_QC; N # L& [3] GREEK SMALL LETTER ETA WITH VARIA AND YPOGEGRAMMENI..GREEK SMALL LETTER ETA WITH OXIA AND YPOGEGRAMMENI
+1FC6..1FCC ; NFKD_QC; N # L& [7] GREEK SMALL LETTER ETA WITH PERISPOMENI..GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI
+1FCD..1FCF ; NFKD_QC; N # Sk [3] GREEK PSILI AND VARIA..GREEK PSILI AND PERISPOMENI
+1FD0..1FD3 ; NFKD_QC; N # L& [4] GREEK SMALL LETTER IOTA WITH VRACHY..GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA
+1FD6..1FDB ; NFKD_QC; N # L& [6] GREEK SMALL LETTER IOTA WITH PERISPOMENI..GREEK CAPITAL LETTER IOTA WITH OXIA
+1FDD..1FDF ; NFKD_QC; N # Sk [3] GREEK DASIA AND VARIA..GREEK DASIA AND PERISPOMENI
+1FE0..1FEC ; NFKD_QC; N # L& [13] GREEK SMALL LETTER UPSILON WITH VRACHY..GREEK CAPITAL LETTER RHO WITH DASIA
+1FED..1FEF ; NFKD_QC; N # Sk [3] GREEK DIALYTIKA AND VARIA..GREEK VARIA
+1FF2..1FF4 ; NFKD_QC; N # L& [3] GREEK SMALL LETTER OMEGA WITH VARIA AND YPOGEGRAMMENI..GREEK SMALL LETTER OMEGA WITH OXIA AND YPOGEGRAMMENI
+1FF6..1FFC ; NFKD_QC; N # L& [7] GREEK SMALL LETTER OMEGA WITH PERISPOMENI..GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI
+1FFD..1FFE ; NFKD_QC; N # Sk [2] GREEK OXIA..GREEK DASIA
+2000..200A ; NFKD_QC; N # Zs [11] EN QUAD..HAIR SPACE
+2011 ; NFKD_QC; N # Pd NON-BREAKING HYPHEN
+2017 ; NFKD_QC; N # Po DOUBLE LOW LINE
+2024..2026 ; NFKD_QC; N # Po [3] ONE DOT LEADER..HORIZONTAL ELLIPSIS
+202F ; NFKD_QC; N # Zs NARROW NO-BREAK SPACE
+2033..2034 ; NFKD_QC; N # Po [2] DOUBLE PRIME..TRIPLE PRIME
+2036..2037 ; NFKD_QC; N # Po [2] REVERSED DOUBLE PRIME..REVERSED TRIPLE PRIME
+203C ; NFKD_QC; N # Po DOUBLE EXCLAMATION MARK
+203E ; NFKD_QC; N # Po OVERLINE
+2047..2049 ; NFKD_QC; N # Po [3] DOUBLE QUESTION MARK..EXCLAMATION QUESTION MARK
+2057 ; NFKD_QC; N # Po QUADRUPLE PRIME
+205F ; NFKD_QC; N # Zs MEDIUM MATHEMATICAL SPACE
+2070 ; NFKD_QC; N # No SUPERSCRIPT ZERO
+2071 ; NFKD_QC; N # L& SUPERSCRIPT LATIN SMALL LETTER I
+2074..2079 ; NFKD_QC; N # No [6] SUPERSCRIPT FOUR..SUPERSCRIPT NINE
+207A..207C ; NFKD_QC; N # Sm [3] SUPERSCRIPT PLUS SIGN..SUPERSCRIPT EQUALS SIGN
+207D ; NFKD_QC; N # Ps SUPERSCRIPT LEFT PARENTHESIS
+207E ; NFKD_QC; N # Pe SUPERSCRIPT RIGHT PARENTHESIS
+207F ; NFKD_QC; N # L& SUPERSCRIPT LATIN SMALL LETTER N
+2080..2089 ; NFKD_QC; N # No [10] SUBSCRIPT ZERO..SUBSCRIPT NINE
+208A..208C ; NFKD_QC; N # Sm [3] SUBSCRIPT PLUS SIGN..SUBSCRIPT EQUALS SIGN
+208D ; NFKD_QC; N # Ps SUBSCRIPT LEFT PARENTHESIS
+208E ; NFKD_QC; N # Pe SUBSCRIPT RIGHT PARENTHESIS
+20A8 ; NFKD_QC; N # Sc RUPEE SIGN
+2100..2101 ; NFKD_QC; N # So [2] ACCOUNT OF..ADDRESSED TO THE SUBJECT
+2102 ; NFKD_QC; N # L& DOUBLE-STRUCK CAPITAL C
+2103 ; NFKD_QC; N # So DEGREE CELSIUS
+2105..2106 ; NFKD_QC; N # So [2] CARE OF..CADA UNA
+2107 ; NFKD_QC; N # L& EULER CONSTANT
+2109 ; NFKD_QC; N # So DEGREE FAHRENHEIT
+210A..2113 ; NFKD_QC; N # L& [10] SCRIPT SMALL G..SCRIPT SMALL L
+2115 ; NFKD_QC; N # L& DOUBLE-STRUCK CAPITAL N
+2116 ; NFKD_QC; N # So NUMERO SIGN
+2119..211D ; NFKD_QC; N # L& [5] DOUBLE-STRUCK CAPITAL P..DOUBLE-STRUCK CAPITAL R
+2120..2122 ; NFKD_QC; N # So [3] SERVICE MARK..TRADE MARK SIGN
+2124 ; NFKD_QC; N # L& DOUBLE-STRUCK CAPITAL Z
+2126 ; NFKD_QC; N # L& OHM SIGN
+2128 ; NFKD_QC; N # L& BLACK-LETTER CAPITAL Z
+212A..212D ; NFKD_QC; N # L& [4] KELVIN SIGN..BLACK-LETTER CAPITAL C
+212F..2131 ; NFKD_QC; N # L& [3] SCRIPT SMALL E..SCRIPT CAPITAL F
+2133..2134 ; NFKD_QC; N # L& [2] SCRIPT CAPITAL M..SCRIPT SMALL O
+2135..2138 ; NFKD_QC; N # Lo [4] ALEF SYMBOL..DALET SYMBOL
+2139 ; NFKD_QC; N # L& INFORMATION SOURCE
+213B ; NFKD_QC; N # So FACSIMILE SIGN
+213D..213F ; NFKD_QC; N # L& [3] DOUBLE-STRUCK SMALL GAMMA..DOUBLE-STRUCK CAPITAL PI
+2140 ; NFKD_QC; N # Sm DOUBLE-STRUCK N-ARY SUMMATION
+2145..2149 ; NFKD_QC; N # L& [5] DOUBLE-STRUCK ITALIC CAPITAL D..DOUBLE-STRUCK ITALIC SMALL J
+2153..215F ; NFKD_QC; N # No [13] VULGAR FRACTION ONE THIRD..FRACTION NUMERATOR ONE
+2160..217F ; NFKD_QC; N # Nl [32] ROMAN NUMERAL ONE..SMALL ROMAN NUMERAL ONE THOUSAND
+219A..219B ; NFKD_QC; N # Sm [2] LEFTWARDS ARROW WITH STROKE..RIGHTWARDS ARROW WITH STROKE
+21AE ; NFKD_QC; N # Sm LEFT RIGHT ARROW WITH STROKE
+21CD ; NFKD_QC; N # So LEFTWARDS DOUBLE ARROW WITH STROKE
+21CE..21CF ; NFKD_QC; N # Sm [2] LEFT RIGHT DOUBLE ARROW WITH STROKE..RIGHTWARDS DOUBLE ARROW WITH STROKE
+2204 ; NFKD_QC; N # Sm THERE DOES NOT EXIST
+2209 ; NFKD_QC; N # Sm NOT AN ELEMENT OF
+220C ; NFKD_QC; N # Sm DOES NOT CONTAIN AS MEMBER
+2224 ; NFKD_QC; N # Sm DOES NOT DIVIDE
+2226 ; NFKD_QC; N # Sm NOT PARALLEL TO
+222C..222D ; NFKD_QC; N # Sm [2] DOUBLE INTEGRAL..TRIPLE INTEGRAL
+222F..2230 ; NFKD_QC; N # Sm [2] SURFACE INTEGRAL..VOLUME INTEGRAL
+2241 ; NFKD_QC; N # Sm NOT TILDE
+2244 ; NFKD_QC; N # Sm NOT ASYMPTOTICALLY EQUAL TO
+2247 ; NFKD_QC; N # Sm NEITHER APPROXIMATELY NOR ACTUALLY EQUAL TO
+2249 ; NFKD_QC; N # Sm NOT ALMOST EQUAL TO
+2260 ; NFKD_QC; N # Sm NOT EQUAL TO
+2262 ; NFKD_QC; N # Sm NOT IDENTICAL TO
+226D..2271 ; NFKD_QC; N # Sm [5] NOT EQUIVALENT TO..NEITHER GREATER-THAN NOR EQUAL TO
+2274..2275 ; NFKD_QC; N # Sm [2] NEITHER LESS-THAN NOR EQUIVALENT TO..NEITHER GREATER-THAN NOR EQUIVALENT TO
+2278..2279 ; NFKD_QC; N # Sm [2] NEITHER LESS-THAN NOR GREATER-THAN..NEITHER GREATER-THAN NOR LESS-THAN
+2280..2281 ; NFKD_QC; N # Sm [2] DOES NOT PRECEDE..DOES NOT SUCCEED
+2284..2285 ; NFKD_QC; N # Sm [2] NOT A SUBSET OF..NOT A SUPERSET OF
+2288..2289 ; NFKD_QC; N # Sm [2] NEITHER A SUBSET OF NOR EQUAL TO..NEITHER A SUPERSET OF NOR EQUAL TO
+22AC..22AF ; NFKD_QC; N # Sm [4] DOES NOT PROVE..NEGATED DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE
+22E0..22E3 ; NFKD_QC; N # Sm [4] DOES NOT PRECEDE OR EQUAL..NOT SQUARE ORIGINAL OF OR EQUAL TO
+22EA..22ED ; NFKD_QC; N # Sm [4] NOT NORMAL SUBGROUP OF..DOES NOT CONTAIN AS NORMAL SUBGROUP OR EQUAL
+2329 ; NFKD_QC; N # Ps LEFT-POINTING ANGLE BRACKET
+232A ; NFKD_QC; N # Pe RIGHT-POINTING ANGLE BRACKET
+2460..249B ; NFKD_QC; N # No [60] CIRCLED DIGIT ONE..NUMBER TWENTY FULL STOP
+249C..24E9 ; NFKD_QC; N # So [78] PARENTHESIZED LATIN SMALL LETTER A..CIRCLED LATIN SMALL LETTER Z
+24EA ; NFKD_QC; N # No CIRCLED DIGIT ZERO
+2A0C ; NFKD_QC; N # Sm QUADRUPLE INTEGRAL OPERATOR
+2A74..2A76 ; NFKD_QC; N # Sm [3] DOUBLE COLON EQUAL..THREE CONSECUTIVE EQUALS SIGNS
+2ADC ; NFKD_QC; N # Sm FORKING
+2E9F ; NFKD_QC; N # So CJK RADICAL MOTHER
+2EF3 ; NFKD_QC; N # So CJK RADICAL C-SIMPLIFIED TURTLE
+2F00..2FD5 ; NFKD_QC; N # So [214] KANGXI RADICAL ONE..KANGXI RADICAL FLUTE
+3000 ; NFKD_QC; N # Zs IDEOGRAPHIC SPACE
+3036 ; NFKD_QC; N # So CIRCLED POSTAL MARK
+3038..303A ; NFKD_QC; N # Nl [3] HANGZHOU NUMERAL TEN..HANGZHOU NUMERAL THIRTY
+304C ; NFKD_QC; N # Lo HIRAGANA LETTER GA
+304E ; NFKD_QC; N # Lo HIRAGANA LETTER GI
+3050 ; NFKD_QC; N # Lo HIRAGANA LETTER GU
+3052 ; NFKD_QC; N # Lo HIRAGANA LETTER GE
+3054 ; NFKD_QC; N # Lo HIRAGANA LETTER GO
+3056 ; NFKD_QC; N # Lo HIRAGANA LETTER ZA
+3058 ; NFKD_QC; N # Lo HIRAGANA LETTER ZI
+305A ; NFKD_QC; N # Lo HIRAGANA LETTER ZU
+305C ; NFKD_QC; N # Lo HIRAGANA LETTER ZE
+305E ; NFKD_QC; N # Lo HIRAGANA LETTER ZO
+3060 ; NFKD_QC; N # Lo HIRAGANA LETTER DA
+3062 ; NFKD_QC; N # Lo HIRAGANA LETTER DI
+3065 ; NFKD_QC; N # Lo HIRAGANA LETTER DU
+3067 ; NFKD_QC; N # Lo HIRAGANA LETTER DE
+3069 ; NFKD_QC; N # Lo HIRAGANA LETTER DO
+3070..3071 ; NFKD_QC; N # Lo [2] HIRAGANA LETTER BA..HIRAGANA LETTER PA
+3073..3074 ; NFKD_QC; N # Lo [2] HIRAGANA LETTER BI..HIRAGANA LETTER PI
+3076..3077 ; NFKD_QC; N # Lo [2] HIRAGANA LETTER BU..HIRAGANA LETTER PU
+3079..307A ; NFKD_QC; N # Lo [2] HIRAGANA LETTER BE..HIRAGANA LETTER PE
+307C..307D ; NFKD_QC; N # Lo [2] HIRAGANA LETTER BO..HIRAGANA LETTER PO
+3094 ; NFKD_QC; N # Lo HIRAGANA LETTER VU
+309B..309C ; NFKD_QC; N # Sk [2] KATAKANA-HIRAGANA VOICED SOUND MARK..KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK
+309E ; NFKD_QC; N # Lm HIRAGANA VOICED ITERATION MARK
+309F ; NFKD_QC; N # Lo HIRAGANA DIGRAPH YORI
+30AC ; NFKD_QC; N # Lo KATAKANA LETTER GA
+30AE ; NFKD_QC; N # Lo KATAKANA LETTER GI
+30B0 ; NFKD_QC; N # Lo KATAKANA LETTER GU
+30B2 ; NFKD_QC; N # Lo KATAKANA LETTER GE
+30B4 ; NFKD_QC; N # Lo KATAKANA LETTER GO
+30B6 ; NFKD_QC; N # Lo KATAKANA LETTER ZA
+30B8 ; NFKD_QC; N # Lo KATAKANA LETTER ZI
+30BA ; NFKD_QC; N # Lo KATAKANA LETTER ZU
+30BC ; NFKD_QC; N # Lo KATAKANA LETTER ZE
+30BE ; NFKD_QC; N # Lo KATAKANA LETTER ZO
+30C0 ; NFKD_QC; N # Lo KATAKANA LETTER DA
+30C2 ; NFKD_QC; N # Lo KATAKANA LETTER DI
+30C5 ; NFKD_QC; N # Lo KATAKANA LETTER DU
+30C7 ; NFKD_QC; N # Lo KATAKANA LETTER DE
+30C9 ; NFKD_QC; N # Lo KATAKANA LETTER DO
+30D0..30D1 ; NFKD_QC; N # Lo [2] KATAKANA LETTER BA..KATAKANA LETTER PA
+30D3..30D4 ; NFKD_QC; N # Lo [2] KATAKANA LETTER BI..KATAKANA LETTER PI
+30D6..30D7 ; NFKD_QC; N # Lo [2] KATAKANA LETTER BU..KATAKANA LETTER PU
+30D9..30DA ; NFKD_QC; N # Lo [2] KATAKANA LETTER BE..KATAKANA LETTER PE
+30DC..30DD ; NFKD_QC; N # Lo [2] KATAKANA LETTER BO..KATAKANA LETTER PO
+30F4 ; NFKD_QC; N # Lo KATAKANA LETTER VU
+30F7..30FA ; NFKD_QC; N # Lo [4] KATAKANA LETTER VA..KATAKANA LETTER VO
+30FE ; NFKD_QC; N # Lm KATAKANA VOICED ITERATION MARK
+30FF ; NFKD_QC; N # Lo KATAKANA DIGRAPH KOTO
+3131..318E ; NFKD_QC; N # Lo [94] HANGUL LETTER KIYEOK..HANGUL LETTER ARAEAE
+3192..3195 ; NFKD_QC; N # No [4] IDEOGRAPHIC ANNOTATION ONE MARK..IDEOGRAPHIC ANNOTATION FOUR MARK
+3196..319F ; NFKD_QC; N # So [10] IDEOGRAPHIC ANNOTATION TOP MARK..IDEOGRAPHIC ANNOTATION MAN MARK
+3200..321E ; NFKD_QC; N # So [31] PARENTHESIZED HANGUL KIYEOK..PARENTHESIZED KOREAN CHARACTER O HU
+3220..3229 ; NFKD_QC; N # No [10] PARENTHESIZED IDEOGRAPH ONE..PARENTHESIZED IDEOGRAPH TEN
+322A..3243 ; NFKD_QC; N # So [26] PARENTHESIZED IDEOGRAPH MOON..PARENTHESIZED IDEOGRAPH REACH
+3250 ; NFKD_QC; N # So PARTNERSHIP SIGN
+3251..325F ; NFKD_QC; N # No [15] CIRCLED NUMBER TWENTY ONE..CIRCLED NUMBER THIRTY FIVE
+3260..327D ; NFKD_QC; N # So [30] CIRCLED HANGUL KIYEOK..CIRCLED KOREAN CHARACTER JUEUI
+3280..3289 ; NFKD_QC; N # No [10] CIRCLED IDEOGRAPH ONE..CIRCLED IDEOGRAPH TEN
+328A..32B0 ; NFKD_QC; N # So [39] CIRCLED IDEOGRAPH MOON..CIRCLED IDEOGRAPH NIGHT
+32B1..32BF ; NFKD_QC; N # No [15] CIRCLED NUMBER THIRTY SIX..CIRCLED NUMBER FIFTY
+32C0..32FE ; NFKD_QC; N # So [63] IDEOGRAPHIC TELEGRAPH SYMBOL FOR JANUARY..CIRCLED KATAKANA WO
+3300..33FF ; NFKD_QC; N # So [256] SQUARE APAATO..SQUARE GAL
+AC00..D7A3 ; NFKD_QC; N # Lo [11172] HANGUL SYLLABLE GA..HANGUL SYLLABLE HIH
+F900..FA0D ; NFKD_QC; N # Lo [270] CJK COMPATIBILITY IDEOGRAPH-F900..CJK COMPATIBILITY IDEOGRAPH-FA0D
+FA10 ; NFKD_QC; N # Lo CJK COMPATIBILITY IDEOGRAPH-FA10
+FA12 ; NFKD_QC; N # Lo CJK COMPATIBILITY IDEOGRAPH-FA12
+FA15..FA1E ; NFKD_QC; N # Lo [10] CJK COMPATIBILITY IDEOGRAPH-FA15..CJK COMPATIBILITY IDEOGRAPH-FA1E
+FA20 ; NFKD_QC; N # Lo CJK COMPATIBILITY IDEOGRAPH-FA20
+FA22 ; NFKD_QC; N # Lo CJK COMPATIBILITY IDEOGRAPH-FA22
+FA25..FA26 ; NFKD_QC; N # Lo [2] CJK COMPATIBILITY IDEOGRAPH-FA25..CJK COMPATIBILITY IDEOGRAPH-FA26
+FA2A..FA2D ; NFKD_QC; N # Lo [4] CJK COMPATIBILITY IDEOGRAPH-FA2A..CJK COMPATIBILITY IDEOGRAPH-FA2D
+FA30..FA6A ; NFKD_QC; N # Lo [59] CJK COMPATIBILITY IDEOGRAPH-FA30..CJK COMPATIBILITY IDEOGRAPH-FA6A
+FB00..FB06 ; NFKD_QC; N # L& [7] LATIN SMALL LIGATURE FF..LATIN SMALL LIGATURE ST
+FB13..FB17 ; NFKD_QC; N # L& [5] ARMENIAN SMALL LIGATURE MEN NOW..ARMENIAN SMALL LIGATURE MEN XEH
+FB1D ; NFKD_QC; N # Lo HEBREW LETTER YOD WITH HIRIQ
+FB1F..FB28 ; NFKD_QC; N # Lo [10] HEBREW LIGATURE YIDDISH YOD YOD PATAH..HEBREW LETTER WIDE TAV
+FB29 ; NFKD_QC; N # Sm HEBREW LETTER ALTERNATIVE PLUS SIGN
+FB2A..FB36 ; NFKD_QC; N # Lo [13] HEBREW LETTER SHIN WITH SHIN DOT..HEBREW LETTER ZAYIN WITH DAGESH
+FB38..FB3C ; NFKD_QC; N # Lo [5] HEBREW LETTER TET WITH DAGESH..HEBREW LETTER LAMED WITH DAGESH
+FB3E ; NFKD_QC; N # Lo HEBREW LETTER MEM WITH DAGESH
+FB40..FB41 ; NFKD_QC; N # Lo [2] HEBREW LETTER NUN WITH DAGESH..HEBREW LETTER SAMEKH WITH DAGESH
+FB43..FB44 ; NFKD_QC; N # Lo [2] HEBREW LETTER FINAL PE WITH DAGESH..HEBREW LETTER PE WITH DAGESH
+FB46..FBB1 ; NFKD_QC; N # Lo [108] HEBREW LETTER TSADI WITH DAGESH..ARABIC LETTER YEH BARREE WITH HAMZA ABOVE FINAL FORM
+FBD3..FD3D ; NFKD_QC; N # Lo [363] ARABIC LETTER NG ISOLATED FORM..ARABIC LIGATURE ALEF WITH FATHATAN ISOLATED FORM
+FD50..FD8F ; NFKD_QC; N # Lo [64] ARABIC LIGATURE TEH WITH JEEM WITH MEEM INITIAL FORM..ARABIC LIGATURE MEEM WITH KHAH WITH MEEM INITIAL FORM
+FD92..FDC7 ; NFKD_QC; N # Lo [54] ARABIC LIGATURE MEEM WITH JEEM WITH KHAH INITIAL FORM..ARABIC LIGATURE NOON WITH JEEM WITH YEH FINAL FORM
+FDF0..FDFB ; NFKD_QC; N # Lo [12] ARABIC LIGATURE SALLA USED AS KORANIC STOP SIGN ISOLATED FORM..ARABIC LIGATURE JALLAJALALOUHOU
+FDFC ; NFKD_QC; N # Sc RIAL SIGN
+FE30 ; NFKD_QC; N # Po PRESENTATION FORM FOR VERTICAL TWO DOT LEADER
+FE31..FE32 ; NFKD_QC; N # Pd [2] PRESENTATION FORM FOR VERTICAL EM DASH..PRESENTATION FORM FOR VERTICAL EN DASH
+FE33..FE34 ; NFKD_QC; N # Pc [2] PRESENTATION FORM FOR VERTICAL LOW LINE..PRESENTATION FORM FOR VERTICAL WAVY LOW LINE
+FE35 ; NFKD_QC; N # Ps PRESENTATION FORM FOR VERTICAL LEFT PARENTHESIS
+FE36 ; NFKD_QC; N # Pe PRESENTATION FORM FOR VERTICAL RIGHT PARENTHESIS
+FE37 ; NFKD_QC; N # Ps PRESENTATION FORM FOR VERTICAL LEFT CURLY BRACKET
+FE38 ; NFKD_QC; N # Pe PRESENTATION FORM FOR VERTICAL RIGHT CURLY BRACKET
+FE39 ; NFKD_QC; N # Ps PRESENTATION FORM FOR VERTICAL LEFT TORTOISE SHELL BRACKET
+FE3A ; NFKD_QC; N # Pe PRESENTATION FORM FOR VERTICAL RIGHT TORTOISE SHELL BRACKET
+FE3B ; NFKD_QC; N # Ps PRESENTATION FORM FOR VERTICAL LEFT BLACK LENTICULAR BRACKET
+FE3C ; NFKD_QC; N # Pe PRESENTATION FORM FOR VERTICAL RIGHT BLACK LENTICULAR BRACKET
+FE3D ; NFKD_QC; N # Ps PRESENTATION FORM FOR VERTICAL LEFT DOUBLE ANGLE BRACKET
+FE3E ; NFKD_QC; N # Pe PRESENTATION FORM FOR VERTICAL RIGHT DOUBLE ANGLE BRACKET
+FE3F ; NFKD_QC; N # Ps PRESENTATION FORM FOR VERTICAL LEFT ANGLE BRACKET
+FE40 ; NFKD_QC; N # Pe PRESENTATION FORM FOR VERTICAL RIGHT ANGLE BRACKET
+FE41 ; NFKD_QC; N # Ps PRESENTATION FORM FOR VERTICAL LEFT CORNER BRACKET
+FE42 ; NFKD_QC; N # Pe PRESENTATION FORM FOR VERTICAL RIGHT CORNER BRACKET
+FE43 ; NFKD_QC; N # Ps PRESENTATION FORM FOR VERTICAL LEFT WHITE CORNER BRACKET
+FE44 ; NFKD_QC; N # Pe PRESENTATION FORM FOR VERTICAL RIGHT WHITE CORNER BRACKET
+FE47 ; NFKD_QC; N # Ps PRESENTATION FORM FOR VERTICAL LEFT SQUARE BRACKET
+FE48 ; NFKD_QC; N # Pe PRESENTATION FORM FOR VERTICAL RIGHT SQUARE BRACKET
+FE49..FE4C ; NFKD_QC; N # Po [4] DASHED OVERLINE..DOUBLE WAVY OVERLINE
+FE4D..FE4F ; NFKD_QC; N # Pc [3] DASHED LOW LINE..WAVY LOW LINE
+FE50..FE52 ; NFKD_QC; N # Po [3] SMALL COMMA..SMALL FULL STOP
+FE54..FE57 ; NFKD_QC; N # Po [4] SMALL SEMICOLON..SMALL EXCLAMATION MARK
+FE58 ; NFKD_QC; N # Pd SMALL EM DASH
+FE59 ; NFKD_QC; N # Ps SMALL LEFT PARENTHESIS
+FE5A ; NFKD_QC; N # Pe SMALL RIGHT PARENTHESIS
+FE5B ; NFKD_QC; N # Ps SMALL LEFT CURLY BRACKET
+FE5C ; NFKD_QC; N # Pe SMALL RIGHT CURLY BRACKET
+FE5D ; NFKD_QC; N # Ps SMALL LEFT TORTOISE SHELL BRACKET
+FE5E ; NFKD_QC; N # Pe SMALL RIGHT TORTOISE SHELL BRACKET
+FE5F..FE61 ; NFKD_QC; N # Po [3] SMALL NUMBER SIGN..SMALL ASTERISK
+FE62 ; NFKD_QC; N # Sm SMALL PLUS SIGN
+FE63 ; NFKD_QC; N # Pd SMALL HYPHEN-MINUS
+FE64..FE66 ; NFKD_QC; N # Sm [3] SMALL LESS-THAN SIGN..SMALL EQUALS SIGN
+FE68 ; NFKD_QC; N # Po SMALL REVERSE SOLIDUS
+FE69 ; NFKD_QC; N # Sc SMALL DOLLAR SIGN
+FE6A..FE6B ; NFKD_QC; N # Po [2] SMALL PERCENT SIGN..SMALL COMMERCIAL AT
+FE70..FE72 ; NFKD_QC; N # Lo [3] ARABIC FATHATAN ISOLATED FORM..ARABIC DAMMATAN ISOLATED FORM
+FE74 ; NFKD_QC; N # Lo ARABIC KASRATAN ISOLATED FORM
+FE76..FEFC ; NFKD_QC; N # Lo [135] ARABIC FATHA ISOLATED FORM..ARABIC LIGATURE LAM WITH ALEF FINAL FORM
+FF01..FF03 ; NFKD_QC; N # Po [3] FULLWIDTH EXCLAMATION MARK..FULLWIDTH NUMBER SIGN
+FF04 ; NFKD_QC; N # Sc FULLWIDTH DOLLAR SIGN
+FF05..FF07 ; NFKD_QC; N # Po [3] FULLWIDTH PERCENT SIGN..FULLWIDTH APOSTROPHE
+FF08 ; NFKD_QC; N # Ps FULLWIDTH LEFT PARENTHESIS
+FF09 ; NFKD_QC; N # Pe FULLWIDTH RIGHT PARENTHESIS
+FF0A ; NFKD_QC; N # Po FULLWIDTH ASTERISK
+FF0B ; NFKD_QC; N # Sm FULLWIDTH PLUS SIGN
+FF0C ; NFKD_QC; N # Po FULLWIDTH COMMA
+FF0D ; NFKD_QC; N # Pd FULLWIDTH HYPHEN-MINUS
+FF0E..FF0F ; NFKD_QC; N # Po [2] FULLWIDTH FULL STOP..FULLWIDTH SOLIDUS
+FF10..FF19 ; NFKD_QC; N # Nd [10] FULLWIDTH DIGIT ZERO..FULLWIDTH DIGIT NINE
+FF1A..FF1B ; NFKD_QC; N # Po [2] FULLWIDTH COLON..FULLWIDTH SEMICOLON
+FF1C..FF1E ; NFKD_QC; N # Sm [3] FULLWIDTH LESS-THAN SIGN..FULLWIDTH GREATER-THAN SIGN
+FF1F..FF20 ; NFKD_QC; N # Po [2] FULLWIDTH QUESTION MARK..FULLWIDTH COMMERCIAL AT
+FF21..FF3A ; NFKD_QC; N # L& [26] FULLWIDTH LATIN CAPITAL LETTER A..FULLWIDTH LATIN CAPITAL LETTER Z
+FF3B ; NFKD_QC; N # Ps FULLWIDTH LEFT SQUARE BRACKET
+FF3C ; NFKD_QC; N # Po FULLWIDTH REVERSE SOLIDUS
+FF3D ; NFKD_QC; N # Pe FULLWIDTH RIGHT SQUARE BRACKET
+FF3E ; NFKD_QC; N # Sk FULLWIDTH CIRCUMFLEX ACCENT
+FF3F ; NFKD_QC; N # Pc FULLWIDTH LOW LINE
+FF40 ; NFKD_QC; N # Sk FULLWIDTH GRAVE ACCENT
+FF41..FF5A ; NFKD_QC; N # L& [26] FULLWIDTH LATIN SMALL LETTER A..FULLWIDTH LATIN SMALL LETTER Z
+FF5B ; NFKD_QC; N # Ps FULLWIDTH LEFT CURLY BRACKET
+FF5C ; NFKD_QC; N # Sm FULLWIDTH VERTICAL LINE
+FF5D ; NFKD_QC; N # Pe FULLWIDTH RIGHT CURLY BRACKET
+FF5E ; NFKD_QC; N # Sm FULLWIDTH TILDE
+FF5F ; NFKD_QC; N # Ps FULLWIDTH LEFT WHITE PARENTHESIS
+FF60 ; NFKD_QC; N # Pe FULLWIDTH RIGHT WHITE PARENTHESIS
+FF61 ; NFKD_QC; N # Po HALFWIDTH IDEOGRAPHIC FULL STOP
+FF62 ; NFKD_QC; N # Ps HALFWIDTH LEFT CORNER BRACKET
+FF63 ; NFKD_QC; N # Pe HALFWIDTH RIGHT CORNER BRACKET
+FF64 ; NFKD_QC; N # Po HALFWIDTH IDEOGRAPHIC COMMA
+FF65 ; NFKD_QC; N # Pc HALFWIDTH KATAKANA MIDDLE DOT
+FF66..FF6F ; NFKD_QC; N # Lo [10] HALFWIDTH KATAKANA LETTER WO..HALFWIDTH KATAKANA LETTER SMALL TU
+FF70 ; NFKD_QC; N # Lm HALFWIDTH KATAKANA-HIRAGANA PROLONGED SOUND MARK
+FF71..FF9D ; NFKD_QC; N # Lo [45] HALFWIDTH KATAKANA LETTER A..HALFWIDTH KATAKANA LETTER N
+FF9E..FF9F ; NFKD_QC; N # Lm [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK
+FFA0..FFBE ; NFKD_QC; N # Lo [31] HALFWIDTH HANGUL FILLER..HALFWIDTH HANGUL LETTER HIEUH
+FFC2..FFC7 ; NFKD_QC; N # Lo [6] HALFWIDTH HANGUL LETTER A..HALFWIDTH HANGUL LETTER E
+FFCA..FFCF ; NFKD_QC; N # Lo [6] HALFWIDTH HANGUL LETTER YEO..HALFWIDTH HANGUL LETTER OE
+FFD2..FFD7 ; NFKD_QC; N # Lo [6] HALFWIDTH HANGUL LETTER YO..HALFWIDTH HANGUL LETTER YU
+FFDA..FFDC ; NFKD_QC; N # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL LETTER I
+FFE0..FFE1 ; NFKD_QC; N # Sc [2] FULLWIDTH CENT SIGN..FULLWIDTH POUND SIGN
+FFE2 ; NFKD_QC; N # Sm FULLWIDTH NOT SIGN
+FFE3 ; NFKD_QC; N # Sk FULLWIDTH MACRON
+FFE4 ; NFKD_QC; N # So FULLWIDTH BROKEN BAR
+FFE5..FFE6 ; NFKD_QC; N # Sc [2] FULLWIDTH YEN SIGN..FULLWIDTH WON SIGN
+FFE8 ; NFKD_QC; N # So HALFWIDTH FORMS LIGHT VERTICAL
+FFE9..FFEC ; NFKD_QC; N # Sm [4] HALFWIDTH LEFTWARDS ARROW..HALFWIDTH DOWNWARDS ARROW
+FFED..FFEE ; NFKD_QC; N # So [2] HALFWIDTH BLACK SQUARE..HALFWIDTH WHITE CIRCLE
+1D15E..1D164 ; NFKD_QC; N # So [7] MUSICAL SYMBOL HALF NOTE..MUSICAL SYMBOL ONE HUNDRED TWENTY-EIGHTH NOTE
+1D1BB..1D1C0 ; NFKD_QC; N # So [6] MUSICAL SYMBOL MINIMA..MUSICAL SYMBOL FUSA BLACK
+1D400..1D454 ; NFKD_QC; N # L& [85] MATHEMATICAL BOLD CAPITAL A..MATHEMATICAL ITALIC SMALL G
+1D456..1D49C ; NFKD_QC; N # L& [71] MATHEMATICAL ITALIC SMALL I..MATHEMATICAL SCRIPT CAPITAL A
+1D49E..1D49F ; NFKD_QC; N # L& [2] MATHEMATICAL SCRIPT CAPITAL C..MATHEMATICAL SCRIPT CAPITAL D
+1D4A2 ; NFKD_QC; N # L& MATHEMATICAL SCRIPT CAPITAL G
+1D4A5..1D4A6 ; NFKD_QC; N # L& [2] MATHEMATICAL SCRIPT CAPITAL J..MATHEMATICAL SCRIPT CAPITAL K
+1D4A9..1D4AC ; NFKD_QC; N # L& [4] MATHEMATICAL SCRIPT CAPITAL N..MATHEMATICAL SCRIPT CAPITAL Q
+1D4AE..1D4B9 ; NFKD_QC; N # L& [12] MATHEMATICAL SCRIPT CAPITAL S..MATHEMATICAL SCRIPT SMALL D
+1D4BB ; NFKD_QC; N # L& MATHEMATICAL SCRIPT SMALL F
+1D4BD..1D4C3 ; NFKD_QC; N # L& [7] MATHEMATICAL SCRIPT SMALL H..MATHEMATICAL SCRIPT SMALL N
+1D4C5..1D505 ; NFKD_QC; N # L& [65] MATHEMATICAL SCRIPT SMALL P..MATHEMATICAL FRAKTUR CAPITAL B
+1D507..1D50A ; NFKD_QC; N # L& [4] MATHEMATICAL FRAKTUR CAPITAL D..MATHEMATICAL FRAKTUR CAPITAL G
+1D50D..1D514 ; NFKD_QC; N # L& [8] MATHEMATICAL FRAKTUR CAPITAL J..MATHEMATICAL FRAKTUR CAPITAL Q
+1D516..1D51C ; NFKD_QC; N # L& [7] MATHEMATICAL FRAKTUR CAPITAL S..MATHEMATICAL FRAKTUR CAPITAL Y
+1D51E..1D539 ; NFKD_QC; N # L& [28] MATHEMATICAL FRAKTUR SMALL A..MATHEMATICAL DOUBLE-STRUCK CAPITAL B
+1D53B..1D53E ; NFKD_QC; N # L& [4] MATHEMATICAL DOUBLE-STRUCK CAPITAL D..MATHEMATICAL DOUBLE-STRUCK CAPITAL G
+1D540..1D544 ; NFKD_QC; N # L& [5] MATHEMATICAL DOUBLE-STRUCK CAPITAL I..MATHEMATICAL DOUBLE-STRUCK CAPITAL M
+1D546 ; NFKD_QC; N # L& MATHEMATICAL DOUBLE-STRUCK CAPITAL O
+1D54A..1D550 ; NFKD_QC; N # L& [7] MATHEMATICAL DOUBLE-STRUCK CAPITAL S..MATHEMATICAL DOUBLE-STRUCK CAPITAL Y
+1D552..1D6A3 ; NFKD_QC; N # L& [338] MATHEMATICAL DOUBLE-STRUCK SMALL A..MATHEMATICAL MONOSPACE SMALL Z
+1D6A8..1D6C0 ; NFKD_QC; N # L& [25] MATHEMATICAL BOLD CAPITAL ALPHA..MATHEMATICAL BOLD CAPITAL OMEGA
+1D6C1 ; NFKD_QC; N # Sm MATHEMATICAL BOLD NABLA
+1D6C2..1D6DA ; NFKD_QC; N # L& [25] MATHEMATICAL BOLD SMALL ALPHA..MATHEMATICAL BOLD SMALL OMEGA
+1D6DB ; NFKD_QC; N # Sm MATHEMATICAL BOLD PARTIAL DIFFERENTIAL
+1D6DC..1D6FA ; NFKD_QC; N # L& [31] MATHEMATICAL BOLD EPSILON SYMBOL..MATHEMATICAL ITALIC CAPITAL OMEGA
+1D6FB ; NFKD_QC; N # Sm MATHEMATICAL ITALIC NABLA
+1D6FC..1D714 ; NFKD_QC; N # L& [25] MATHEMATICAL ITALIC SMALL ALPHA..MATHEMATICAL ITALIC SMALL OMEGA
+1D715 ; NFKD_QC; N # Sm MATHEMATICAL ITALIC PARTIAL DIFFERENTIAL
+1D716..1D734 ; NFKD_QC; N # L& [31] MATHEMATICAL ITALIC EPSILON SYMBOL..MATHEMATICAL BOLD ITALIC CAPITAL OMEGA
+1D735 ; NFKD_QC; N # Sm MATHEMATICAL BOLD ITALIC NABLA
+1D736..1D74E ; NFKD_QC; N # L& [25] MATHEMATICAL BOLD ITALIC SMALL ALPHA..MATHEMATICAL BOLD ITALIC SMALL OMEGA
+1D74F ; NFKD_QC; N # Sm MATHEMATICAL BOLD ITALIC PARTIAL DIFFERENTIAL
+1D750..1D76E ; NFKD_QC; N # L& [31] MATHEMATICAL BOLD ITALIC EPSILON SYMBOL..MATHEMATICAL SANS-SERIF BOLD CAPITAL OMEGA
+1D76F ; NFKD_QC; N # Sm MATHEMATICAL SANS-SERIF BOLD NABLA
+1D770..1D788 ; NFKD_QC; N # L& [25] MATHEMATICAL SANS-SERIF BOLD SMALL ALPHA..MATHEMATICAL SANS-SERIF BOLD SMALL OMEGA
+1D789 ; NFKD_QC; N # Sm MATHEMATICAL SANS-SERIF BOLD PARTIAL DIFFERENTIAL
+1D78A..1D7A8 ; NFKD_QC; N # L& [31] MATHEMATICAL SANS-SERIF BOLD EPSILON SYMBOL..MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL OMEGA
+1D7A9 ; NFKD_QC; N # Sm MATHEMATICAL SANS-SERIF BOLD ITALIC NABLA
+1D7AA..1D7C2 ; NFKD_QC; N # L& [25] MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL ALPHA..MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL OMEGA
+1D7C3 ; NFKD_QC; N # Sm MATHEMATICAL SANS-SERIF BOLD ITALIC PARTIAL DIFFERENTIAL
+1D7C4..1D7C9 ; NFKD_QC; N # L& [6] MATHEMATICAL SANS-SERIF BOLD ITALIC EPSILON SYMBOL..MATHEMATICAL SANS-SERIF BOLD ITALIC PI SYMBOL
+1D7CE..1D7FF ; NFKD_QC; N # Nd [50] MATHEMATICAL BOLD DIGIT ZERO..MATHEMATICAL MONOSPACE DIGIT NINE
+2F800..2FA1D ; NFKD_QC; N # Lo [542] CJK COMPATIBILITY IDEOGRAPH-2F800..CJK COMPATIBILITY IDEOGRAPH-2FA1D
+
+# Total code points: 16396
+
+# ================================================
+
+# Property: NFKC_Quick_Check
+
+# All code points not explicitly listed for NFKC_Quick_Check
+# have the value Yes (Y).
+
+# ================================================
+
+# NFKC_Quick_Check=No
+
+00A0 ; NFKC_QC; N # Zs NO-BREAK SPACE
+00A8 ; NFKC_QC; N # Sk DIAERESIS
+00AA ; NFKC_QC; N # L& FEMININE ORDINAL INDICATOR
+00AF ; NFKC_QC; N # Sk MACRON
+00B2..00B3 ; NFKC_QC; N # No [2] SUPERSCRIPT TWO..SUPERSCRIPT THREE
+00B4 ; NFKC_QC; N # Sk ACUTE ACCENT
+00B5 ; NFKC_QC; N # L& MICRO SIGN
+00B8 ; NFKC_QC; N # Sk CEDILLA
+00B9 ; NFKC_QC; N # No SUPERSCRIPT ONE
+00BA ; NFKC_QC; N # L& MASCULINE ORDINAL INDICATOR
+00BC..00BE ; NFKC_QC; N # No [3] VULGAR FRACTION ONE QUARTER..VULGAR FRACTION THREE QUARTERS
+0132..0133 ; NFKC_QC; N # L& [2] LATIN CAPITAL LIGATURE IJ..LATIN SMALL LIGATURE IJ
+013F..0140 ; NFKC_QC; N # L& [2] LATIN CAPITAL LETTER L WITH MIDDLE DOT..LATIN SMALL LETTER L WITH MIDDLE DOT
+0149 ; NFKC_QC; N # L& LATIN SMALL LETTER N PRECEDED BY APOSTROPHE
+017F ; NFKC_QC; N # L& LATIN SMALL LETTER LONG S
+01C4..01CC ; NFKC_QC; N # L& [9] LATIN CAPITAL LETTER DZ WITH CARON..LATIN SMALL LETTER NJ
+01F1..01F3 ; NFKC_QC; N # L& [3] LATIN CAPITAL LETTER DZ..LATIN SMALL LETTER DZ
+02B0..02B8 ; NFKC_QC; N # Lm [9] MODIFIER LETTER SMALL H..MODIFIER LETTER SMALL Y
+02D8..02DD ; NFKC_QC; N # Sk [6] BREVE..DOUBLE ACUTE ACCENT
+02E0..02E4 ; NFKC_QC; N # Lm [5] MODIFIER LETTER SMALL GAMMA..MODIFIER LETTER SMALL REVERSED GLOTTAL STOP
+0340..0341 ; NFKC_QC; N # Mn [2] COMBINING GRAVE TONE MARK..COMBINING ACUTE TONE MARK
+0343..0344 ; NFKC_QC; N # Mn [2] COMBINING GREEK KORONIS..COMBINING GREEK DIALYTIKA TONOS
+0374 ; NFKC_QC; N # Sk GREEK NUMERAL SIGN
+037A ; NFKC_QC; N # Lm GREEK YPOGEGRAMMENI
+037E ; NFKC_QC; N # Po GREEK QUESTION MARK
+0384..0385 ; NFKC_QC; N # Sk [2] GREEK TONOS..GREEK DIALYTIKA TONOS
+0387 ; NFKC_QC; N # Po GREEK ANO TELEIA
+03D0..03D6 ; NFKC_QC; N # L& [7] GREEK BETA SYMBOL..GREEK PI SYMBOL
+03F0..03F2 ; NFKC_QC; N # L& [3] GREEK KAPPA SYMBOL..GREEK LUNATE SIGMA SYMBOL
+03F4..03F5 ; NFKC_QC; N # L& [2] GREEK CAPITAL THETA SYMBOL..GREEK LUNATE EPSILON SYMBOL
+03F9 ; NFKC_QC; N # L& GREEK CAPITAL LUNATE SIGMA SYMBOL
+0587 ; NFKC_QC; N # L& ARMENIAN SMALL LIGATURE ECH YIWN
+0675..0678 ; NFKC_QC; N # Lo [4] ARABIC LETTER HIGH HAMZA ALEF..ARABIC LETTER HIGH HAMZA YEH
+0958..095F ; NFKC_QC; N # Lo [8] DEVANAGARI LETTER QA..DEVANAGARI LETTER YYA
+09DC..09DD ; NFKC_QC; N # Lo [2] BENGALI LETTER RRA..BENGALI LETTER RHA
+09DF ; NFKC_QC; N # Lo BENGALI LETTER YYA
+0A33 ; NFKC_QC; N # Lo GURMUKHI LETTER LLA
+0A36 ; NFKC_QC; N # Lo GURMUKHI LETTER SHA
+0A59..0A5B ; NFKC_QC; N # Lo [3] GURMUKHI LETTER KHHA..GURMUKHI LETTER ZA
+0A5E ; NFKC_QC; N # Lo GURMUKHI LETTER FA
+0B5C..0B5D ; NFKC_QC; N # Lo [2] ORIYA LETTER RRA..ORIYA LETTER RHA
+0E33 ; NFKC_QC; N # Lo THAI CHARACTER SARA AM
+0EB3 ; NFKC_QC; N # Lo LAO VOWEL SIGN AM
+0EDC..0EDD ; NFKC_QC; N # Lo [2] LAO HO NO..LAO HO MO
+0F0C ; NFKC_QC; N # Po TIBETAN MARK DELIMITER TSHEG BSTAR
+0F43 ; NFKC_QC; N # Lo TIBETAN LETTER GHA
+0F4D ; NFKC_QC; N # Lo TIBETAN LETTER DDHA
+0F52 ; NFKC_QC; N # Lo TIBETAN LETTER DHA
+0F57 ; NFKC_QC; N # Lo TIBETAN LETTER BHA
+0F5C ; NFKC_QC; N # Lo TIBETAN LETTER DZHA
+0F69 ; NFKC_QC; N # Lo TIBETAN LETTER KSSA
+0F73 ; NFKC_QC; N # Mn TIBETAN VOWEL SIGN II
+0F75..0F79 ; NFKC_QC; N # Mn [5] TIBETAN VOWEL SIGN UU..TIBETAN VOWEL SIGN VOCALIC LL
+0F81 ; NFKC_QC; N # Mn TIBETAN VOWEL SIGN REVERSED II
+0F93 ; NFKC_QC; N # Mn TIBETAN SUBJOINED LETTER GHA
+0F9D ; NFKC_QC; N # Mn TIBETAN SUBJOINED LETTER DDHA
+0FA2 ; NFKC_QC; N # Mn TIBETAN SUBJOINED LETTER DHA
+0FA7 ; NFKC_QC; N # Mn TIBETAN SUBJOINED LETTER BHA
+0FAC ; NFKC_QC; N # Mn TIBETAN SUBJOINED LETTER DZHA
+0FB9 ; NFKC_QC; N # Mn TIBETAN SUBJOINED LETTER KSSA
+1D2C..1D2E ; NFKC_QC; N # Lm [3] MODIFIER LETTER CAPITAL A..MODIFIER LETTER CAPITAL B
+1D30..1D3A ; NFKC_QC; N # Lm [11] MODIFIER LETTER CAPITAL D..MODIFIER LETTER CAPITAL N
+1D3C..1D4D ; NFKC_QC; N # Lm [18] MODIFIER LETTER CAPITAL O..MODIFIER LETTER SMALL G
+1D4F..1D61 ; NFKC_QC; N # Lm [19] MODIFIER LETTER SMALL K..MODIFIER LETTER SMALL CHI
+1D62..1D6A ; NFKC_QC; N # L& [9] LATIN SUBSCRIPT SMALL LETTER I..GREEK SUBSCRIPT SMALL LETTER CHI
+1E9A..1E9B ; NFKC_QC; N # L& [2] LATIN SMALL LETTER A WITH RIGHT HALF RING..LATIN SMALL LETTER LONG S WITH DOT ABOVE
+1F71 ; NFKC_QC; N # L& GREEK SMALL LETTER ALPHA WITH OXIA
+1F73 ; NFKC_QC; N # L& GREEK SMALL LETTER EPSILON WITH OXIA
+1F75 ; NFKC_QC; N # L& GREEK SMALL LETTER ETA WITH OXIA
+1F77 ; NFKC_QC; N # L& GREEK SMALL LETTER IOTA WITH OXIA
+1F79 ; NFKC_QC; N # L& GREEK SMALL LETTER OMICRON WITH OXIA
+1F7B ; NFKC_QC; N # L& GREEK SMALL LETTER UPSILON WITH OXIA
+1F7D ; NFKC_QC; N # L& GREEK SMALL LETTER OMEGA WITH OXIA
+1FBB ; NFKC_QC; N # L& GREEK CAPITAL LETTER ALPHA WITH OXIA
+1FBD ; NFKC_QC; N # Sk GREEK KORONIS
+1FBE ; NFKC_QC; N # L& GREEK PROSGEGRAMMENI
+1FBF..1FC1 ; NFKC_QC; N # Sk [3] GREEK PSILI..GREEK DIALYTIKA AND PERISPOMENI
+1FC9 ; NFKC_QC; N # L& GREEK CAPITAL LETTER EPSILON WITH OXIA
+1FCB ; NFKC_QC; N # L& GREEK CAPITAL LETTER ETA WITH OXIA
+1FCD..1FCF ; NFKC_QC; N # Sk [3] GREEK PSILI AND VARIA..GREEK PSILI AND PERISPOMENI
+1FD3 ; NFKC_QC; N # L& GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA
+1FDB ; NFKC_QC; N # L& GREEK CAPITAL LETTER IOTA WITH OXIA
+1FDD..1FDF ; NFKC_QC; N # Sk [3] GREEK DASIA AND VARIA..GREEK DASIA AND PERISPOMENI
+1FE3 ; NFKC_QC; N # L& GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND OXIA
+1FEB ; NFKC_QC; N # L& GREEK CAPITAL LETTER UPSILON WITH OXIA
+1FED..1FEF ; NFKC_QC; N # Sk [3] GREEK DIALYTIKA AND VARIA..GREEK VARIA
+1FF9 ; NFKC_QC; N # L& GREEK CAPITAL LETTER OMICRON WITH OXIA
+1FFB ; NFKC_QC; N # L& GREEK CAPITAL LETTER OMEGA WITH OXIA
+1FFD..1FFE ; NFKC_QC; N # Sk [2] GREEK OXIA..GREEK DASIA
+2000..200A ; NFKC_QC; N # Zs [11] EN QUAD..HAIR SPACE
+2011 ; NFKC_QC; N # Pd NON-BREAKING HYPHEN
+2017 ; NFKC_QC; N # Po DOUBLE LOW LINE
+2024..2026 ; NFKC_QC; N # Po [3] ONE DOT LEADER..HORIZONTAL ELLIPSIS
+202F ; NFKC_QC; N # Zs NARROW NO-BREAK SPACE
+2033..2034 ; NFKC_QC; N # Po [2] DOUBLE PRIME..TRIPLE PRIME
+2036..2037 ; NFKC_QC; N # Po [2] REVERSED DOUBLE PRIME..REVERSED TRIPLE PRIME
+203C ; NFKC_QC; N # Po DOUBLE EXCLAMATION MARK
+203E ; NFKC_QC; N # Po OVERLINE
+2047..2049 ; NFKC_QC; N # Po [3] DOUBLE QUESTION MARK..EXCLAMATION QUESTION MARK
+2057 ; NFKC_QC; N # Po QUADRUPLE PRIME
+205F ; NFKC_QC; N # Zs MEDIUM MATHEMATICAL SPACE
+2070 ; NFKC_QC; N # No SUPERSCRIPT ZERO
+2071 ; NFKC_QC; N # L& SUPERSCRIPT LATIN SMALL LETTER I
+2074..2079 ; NFKC_QC; N # No [6] SUPERSCRIPT FOUR..SUPERSCRIPT NINE
+207A..207C ; NFKC_QC; N # Sm [3] SUPERSCRIPT PLUS SIGN..SUPERSCRIPT EQUALS SIGN
+207D ; NFKC_QC; N # Ps SUPERSCRIPT LEFT PARENTHESIS
+207E ; NFKC_QC; N # Pe SUPERSCRIPT RIGHT PARENTHESIS
+207F ; NFKC_QC; N # L& SUPERSCRIPT LATIN SMALL LETTER N
+2080..2089 ; NFKC_QC; N # No [10] SUBSCRIPT ZERO..SUBSCRIPT NINE
+208A..208C ; NFKC_QC; N # Sm [3] SUBSCRIPT PLUS SIGN..SUBSCRIPT EQUALS SIGN
+208D ; NFKC_QC; N # Ps SUBSCRIPT LEFT PARENTHESIS
+208E ; NFKC_QC; N # Pe SUBSCRIPT RIGHT PARENTHESIS
+20A8 ; NFKC_QC; N # Sc RUPEE SIGN
+2100..2101 ; NFKC_QC; N # So [2] ACCOUNT OF..ADDRESSED TO THE SUBJECT
+2102 ; NFKC_QC; N # L& DOUBLE-STRUCK CAPITAL C
+2103 ; NFKC_QC; N # So DEGREE CELSIUS
+2105..2106 ; NFKC_QC; N # So [2] CARE OF..CADA UNA
+2107 ; NFKC_QC; N # L& EULER CONSTANT
+2109 ; NFKC_QC; N # So DEGREE FAHRENHEIT
+210A..2113 ; NFKC_QC; N # L& [10] SCRIPT SMALL G..SCRIPT SMALL L
+2115 ; NFKC_QC; N # L& DOUBLE-STRUCK CAPITAL N
+2116 ; NFKC_QC; N # So NUMERO SIGN
+2119..211D ; NFKC_QC; N # L& [5] DOUBLE-STRUCK CAPITAL P..DOUBLE-STRUCK CAPITAL R
+2120..2122 ; NFKC_QC; N # So [3] SERVICE MARK..TRADE MARK SIGN
+2124 ; NFKC_QC; N # L& DOUBLE-STRUCK CAPITAL Z
+2126 ; NFKC_QC; N # L& OHM SIGN
+2128 ; NFKC_QC; N # L& BLACK-LETTER CAPITAL Z
+212A..212D ; NFKC_QC; N # L& [4] KELVIN SIGN..BLACK-LETTER CAPITAL C
+212F..2131 ; NFKC_QC; N # L& [3] SCRIPT SMALL E..SCRIPT CAPITAL F
+2133..2134 ; NFKC_QC; N # L& [2] SCRIPT CAPITAL M..SCRIPT SMALL O
+2135..2138 ; NFKC_QC; N # Lo [4] ALEF SYMBOL..DALET SYMBOL
+2139 ; NFKC_QC; N # L& INFORMATION SOURCE
+213B ; NFKC_QC; N # So FACSIMILE SIGN
+213D..213F ; NFKC_QC; N # L& [3] DOUBLE-STRUCK SMALL GAMMA..DOUBLE-STRUCK CAPITAL PI
+2140 ; NFKC_QC; N # Sm DOUBLE-STRUCK N-ARY SUMMATION
+2145..2149 ; NFKC_QC; N # L& [5] DOUBLE-STRUCK ITALIC CAPITAL D..DOUBLE-STRUCK ITALIC SMALL J
+2153..215F ; NFKC_QC; N # No [13] VULGAR FRACTION ONE THIRD..FRACTION NUMERATOR ONE
+2160..217F ; NFKC_QC; N # Nl [32] ROMAN NUMERAL ONE..SMALL ROMAN NUMERAL ONE THOUSAND
+222C..222D ; NFKC_QC; N # Sm [2] DOUBLE INTEGRAL..TRIPLE INTEGRAL
+222F..2230 ; NFKC_QC; N # Sm [2] SURFACE INTEGRAL..VOLUME INTEGRAL
+2329 ; NFKC_QC; N # Ps LEFT-POINTING ANGLE BRACKET
+232A ; NFKC_QC; N # Pe RIGHT-POINTING ANGLE BRACKET
+2460..249B ; NFKC_QC; N # No [60] CIRCLED DIGIT ONE..NUMBER TWENTY FULL STOP
+249C..24E9 ; NFKC_QC; N # So [78] PARENTHESIZED LATIN SMALL LETTER A..CIRCLED LATIN SMALL LETTER Z
+24EA ; NFKC_QC; N # No CIRCLED DIGIT ZERO
+2A0C ; NFKC_QC; N # Sm QUADRUPLE INTEGRAL OPERATOR
+2A74..2A76 ; NFKC_QC; N # Sm [3] DOUBLE COLON EQUAL..THREE CONSECUTIVE EQUALS SIGNS
+2ADC ; NFKC_QC; N # Sm FORKING
+2E9F ; NFKC_QC; N # So CJK RADICAL MOTHER
+2EF3 ; NFKC_QC; N # So CJK RADICAL C-SIMPLIFIED TURTLE
+2F00..2FD5 ; NFKC_QC; N # So [214] KANGXI RADICAL ONE..KANGXI RADICAL FLUTE
+3000 ; NFKC_QC; N # Zs IDEOGRAPHIC SPACE
+3036 ; NFKC_QC; N # So CIRCLED POSTAL MARK
+3038..303A ; NFKC_QC; N # Nl [3] HANGZHOU NUMERAL TEN..HANGZHOU NUMERAL THIRTY
+309B..309C ; NFKC_QC; N # Sk [2] KATAKANA-HIRAGANA VOICED SOUND MARK..KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK
+309F ; NFKC_QC; N # Lo HIRAGANA DIGRAPH YORI
+30FF ; NFKC_QC; N # Lo KATAKANA DIGRAPH KOTO
+3131..318E ; NFKC_QC; N # Lo [94] HANGUL LETTER KIYEOK..HANGUL LETTER ARAEAE
+3192..3195 ; NFKC_QC; N # No [4] IDEOGRAPHIC ANNOTATION ONE MARK..IDEOGRAPHIC ANNOTATION FOUR MARK
+3196..319F ; NFKC_QC; N # So [10] IDEOGRAPHIC ANNOTATION TOP MARK..IDEOGRAPHIC ANNOTATION MAN MARK
+3200..321E ; NFKC_QC; N # So [31] PARENTHESIZED HANGUL KIYEOK..PARENTHESIZED KOREAN CHARACTER O HU
+3220..3229 ; NFKC_QC; N # No [10] PARENTHESIZED IDEOGRAPH ONE..PARENTHESIZED IDEOGRAPH TEN
+322A..3243 ; NFKC_QC; N # So [26] PARENTHESIZED IDEOGRAPH MOON..PARENTHESIZED IDEOGRAPH REACH
+3250 ; NFKC_QC; N # So PARTNERSHIP SIGN
+3251..325F ; NFKC_QC; N # No [15] CIRCLED NUMBER TWENTY ONE..CIRCLED NUMBER THIRTY FIVE
+3260..327D ; NFKC_QC; N # So [30] CIRCLED HANGUL KIYEOK..CIRCLED KOREAN CHARACTER JUEUI
+3280..3289 ; NFKC_QC; N # No [10] CIRCLED IDEOGRAPH ONE..CIRCLED IDEOGRAPH TEN
+328A..32B0 ; NFKC_QC; N # So [39] CIRCLED IDEOGRAPH MOON..CIRCLED IDEOGRAPH NIGHT
+32B1..32BF ; NFKC_QC; N # No [15] CIRCLED NUMBER THIRTY SIX..CIRCLED NUMBER FIFTY
+32C0..32FE ; NFKC_QC; N # So [63] IDEOGRAPHIC TELEGRAPH SYMBOL FOR JANUARY..CIRCLED KATAKANA WO
+3300..33FF ; NFKC_QC; N # So [256] SQUARE APAATO..SQUARE GAL
+F900..FA0D ; NFKC_QC; N # Lo [270] CJK COMPATIBILITY IDEOGRAPH-F900..CJK COMPATIBILITY IDEOGRAPH-FA0D
+FA10 ; NFKC_QC; N # Lo CJK COMPATIBILITY IDEOGRAPH-FA10
+FA12 ; NFKC_QC; N # Lo CJK COMPATIBILITY IDEOGRAPH-FA12
+FA15..FA1E ; NFKC_QC; N # Lo [10] CJK COMPATIBILITY IDEOGRAPH-FA15..CJK COMPATIBILITY IDEOGRAPH-FA1E
+FA20 ; NFKC_QC; N # Lo CJK COMPATIBILITY IDEOGRAPH-FA20
+FA22 ; NFKC_QC; N # Lo CJK COMPATIBILITY IDEOGRAPH-FA22
+FA25..FA26 ; NFKC_QC; N # Lo [2] CJK COMPATIBILITY IDEOGRAPH-FA25..CJK COMPATIBILITY IDEOGRAPH-FA26
+FA2A..FA2D ; NFKC_QC; N # Lo [4] CJK COMPATIBILITY IDEOGRAPH-FA2A..CJK COMPATIBILITY IDEOGRAPH-FA2D
+FA30..FA6A ; NFKC_QC; N # Lo [59] CJK COMPATIBILITY IDEOGRAPH-FA30..CJK COMPATIBILITY IDEOGRAPH-FA6A
+FB00..FB06 ; NFKC_QC; N # L& [7] LATIN SMALL LIGATURE FF..LATIN SMALL LIGATURE ST
+FB13..FB17 ; NFKC_QC; N # L& [5] ARMENIAN SMALL LIGATURE MEN NOW..ARMENIAN SMALL LIGATURE MEN XEH
+FB1D ; NFKC_QC; N # Lo HEBREW LETTER YOD WITH HIRIQ
+FB1F..FB28 ; NFKC_QC; N # Lo [10] HEBREW LIGATURE YIDDISH YOD YOD PATAH..HEBREW LETTER WIDE TAV
+FB29 ; NFKC_QC; N # Sm HEBREW LETTER ALTERNATIVE PLUS SIGN
+FB2A..FB36 ; NFKC_QC; N # Lo [13] HEBREW LETTER SHIN WITH SHIN DOT..HEBREW LETTER ZAYIN WITH DAGESH
+FB38..FB3C ; NFKC_QC; N # Lo [5] HEBREW LETTER TET WITH DAGESH..HEBREW LETTER LAMED WITH DAGESH
+FB3E ; NFKC_QC; N # Lo HEBREW LETTER MEM WITH DAGESH
+FB40..FB41 ; NFKC_QC; N # Lo [2] HEBREW LETTER NUN WITH DAGESH..HEBREW LETTER SAMEKH WITH DAGESH
+FB43..FB44 ; NFKC_QC; N # Lo [2] HEBREW LETTER FINAL PE WITH DAGESH..HEBREW LETTER PE WITH DAGESH
+FB46..FBB1 ; NFKC_QC; N # Lo [108] HEBREW LETTER TSADI WITH DAGESH..ARABIC LETTER YEH BARREE WITH HAMZA ABOVE FINAL FORM
+FBD3..FD3D ; NFKC_QC; N # Lo [363] ARABIC LETTER NG ISOLATED FORM..ARABIC LIGATURE ALEF WITH FATHATAN ISOLATED FORM
+FD50..FD8F ; NFKC_QC; N # Lo [64] ARABIC LIGATURE TEH WITH JEEM WITH MEEM INITIAL FORM..ARABIC LIGATURE MEEM WITH KHAH WITH MEEM INITIAL FORM
+FD92..FDC7 ; NFKC_QC; N # Lo [54] ARABIC LIGATURE MEEM WITH JEEM WITH KHAH INITIAL FORM..ARABIC LIGATURE NOON WITH JEEM WITH YEH FINAL FORM
+FDF0..FDFB ; NFKC_QC; N # Lo [12] ARABIC LIGATURE SALLA USED AS KORANIC STOP SIGN ISOLATED FORM..ARABIC LIGATURE JALLAJALALOUHOU
+FDFC ; NFKC_QC; N # Sc RIAL SIGN
+FE30 ; NFKC_QC; N # Po PRESENTATION FORM FOR VERTICAL TWO DOT LEADER
+FE31..FE32 ; NFKC_QC; N # Pd [2] PRESENTATION FORM FOR VERTICAL EM DASH..PRESENTATION FORM FOR VERTICAL EN DASH
+FE33..FE34 ; NFKC_QC; N # Pc [2] PRESENTATION FORM FOR VERTICAL LOW LINE..PRESENTATION FORM FOR VERTICAL WAVY LOW LINE
+FE35 ; NFKC_QC; N # Ps PRESENTATION FORM FOR VERTICAL LEFT PARENTHESIS
+FE36 ; NFKC_QC; N # Pe PRESENTATION FORM FOR VERTICAL RIGHT PARENTHESIS
+FE37 ; NFKC_QC; N # Ps PRESENTATION FORM FOR VERTICAL LEFT CURLY BRACKET
+FE38 ; NFKC_QC; N # Pe PRESENTATION FORM FOR VERTICAL RIGHT CURLY BRACKET
+FE39 ; NFKC_QC; N # Ps PRESENTATION FORM FOR VERTICAL LEFT TORTOISE SHELL BRACKET
+FE3A ; NFKC_QC; N # Pe PRESENTATION FORM FOR VERTICAL RIGHT TORTOISE SHELL BRACKET
+FE3B ; NFKC_QC; N # Ps PRESENTATION FORM FOR VERTICAL LEFT BLACK LENTICULAR BRACKET
+FE3C ; NFKC_QC; N # Pe PRESENTATION FORM FOR VERTICAL RIGHT BLACK LENTICULAR BRACKET
+FE3D ; NFKC_QC; N # Ps PRESENTATION FORM FOR VERTICAL LEFT DOUBLE ANGLE BRACKET
+FE3E ; NFKC_QC; N # Pe PRESENTATION FORM FOR VERTICAL RIGHT DOUBLE ANGLE BRACKET
+FE3F ; NFKC_QC; N # Ps PRESENTATION FORM FOR VERTICAL LEFT ANGLE BRACKET
+FE40 ; NFKC_QC; N # Pe PRESENTATION FORM FOR VERTICAL RIGHT ANGLE BRACKET
+FE41 ; NFKC_QC; N # Ps PRESENTATION FORM FOR VERTICAL LEFT CORNER BRACKET
+FE42 ; NFKC_QC; N # Pe PRESENTATION FORM FOR VERTICAL RIGHT CORNER BRACKET
+FE43 ; NFKC_QC; N # Ps PRESENTATION FORM FOR VERTICAL LEFT WHITE CORNER BRACKET
+FE44 ; NFKC_QC; N # Pe PRESENTATION FORM FOR VERTICAL RIGHT WHITE CORNER BRACKET
+FE47 ; NFKC_QC; N # Ps PRESENTATION FORM FOR VERTICAL LEFT SQUARE BRACKET
+FE48 ; NFKC_QC; N # Pe PRESENTATION FORM FOR VERTICAL RIGHT SQUARE BRACKET
+FE49..FE4C ; NFKC_QC; N # Po [4] DASHED OVERLINE..DOUBLE WAVY OVERLINE
+FE4D..FE4F ; NFKC_QC; N # Pc [3] DASHED LOW LINE..WAVY LOW LINE
+FE50..FE52 ; NFKC_QC; N # Po [3] SMALL COMMA..SMALL FULL STOP
+FE54..FE57 ; NFKC_QC; N # Po [4] SMALL SEMICOLON..SMALL EXCLAMATION MARK
+FE58 ; NFKC_QC; N # Pd SMALL EM DASH
+FE59 ; NFKC_QC; N # Ps SMALL LEFT PARENTHESIS
+FE5A ; NFKC_QC; N # Pe SMALL RIGHT PARENTHESIS
+FE5B ; NFKC_QC; N # Ps SMALL LEFT CURLY BRACKET
+FE5C ; NFKC_QC; N # Pe SMALL RIGHT CURLY BRACKET
+FE5D ; NFKC_QC; N # Ps SMALL LEFT TORTOISE SHELL BRACKET
+FE5E ; NFKC_QC; N # Pe SMALL RIGHT TORTOISE SHELL BRACKET
+FE5F..FE61 ; NFKC_QC; N # Po [3] SMALL NUMBER SIGN..SMALL ASTERISK
+FE62 ; NFKC_QC; N # Sm SMALL PLUS SIGN
+FE63 ; NFKC_QC; N # Pd SMALL HYPHEN-MINUS
+FE64..FE66 ; NFKC_QC; N # Sm [3] SMALL LESS-THAN SIGN..SMALL EQUALS SIGN
+FE68 ; NFKC_QC; N # Po SMALL REVERSE SOLIDUS
+FE69 ; NFKC_QC; N # Sc SMALL DOLLAR SIGN
+FE6A..FE6B ; NFKC_QC; N # Po [2] SMALL PERCENT SIGN..SMALL COMMERCIAL AT
+FE70..FE72 ; NFKC_QC; N # Lo [3] ARABIC FATHATAN ISOLATED FORM..ARABIC DAMMATAN ISOLATED FORM
+FE74 ; NFKC_QC; N # Lo ARABIC KASRATAN ISOLATED FORM
+FE76..FEFC ; NFKC_QC; N # Lo [135] ARABIC FATHA ISOLATED FORM..ARABIC LIGATURE LAM WITH ALEF FINAL FORM
+FF01..FF03 ; NFKC_QC; N # Po [3] FULLWIDTH EXCLAMATION MARK..FULLWIDTH NUMBER SIGN
+FF04 ; NFKC_QC; N # Sc FULLWIDTH DOLLAR SIGN
+FF05..FF07 ; NFKC_QC; N # Po [3] FULLWIDTH PERCENT SIGN..FULLWIDTH APOSTROPHE
+FF08 ; NFKC_QC; N # Ps FULLWIDTH LEFT PARENTHESIS
+FF09 ; NFKC_QC; N # Pe FULLWIDTH RIGHT PARENTHESIS
+FF0A ; NFKC_QC; N # Po FULLWIDTH ASTERISK
+FF0B ; NFKC_QC; N # Sm FULLWIDTH PLUS SIGN
+FF0C ; NFKC_QC; N # Po FULLWIDTH COMMA
+FF0D ; NFKC_QC; N # Pd FULLWIDTH HYPHEN-MINUS
+FF0E..FF0F ; NFKC_QC; N # Po [2] FULLWIDTH FULL STOP..FULLWIDTH SOLIDUS
+FF10..FF19 ; NFKC_QC; N # Nd [10] FULLWIDTH DIGIT ZERO..FULLWIDTH DIGIT NINE
+FF1A..FF1B ; NFKC_QC; N # Po [2] FULLWIDTH COLON..FULLWIDTH SEMICOLON
+FF1C..FF1E ; NFKC_QC; N # Sm [3] FULLWIDTH LESS-THAN SIGN..FULLWIDTH GREATER-THAN SIGN
+FF1F..FF20 ; NFKC_QC; N # Po [2] FULLWIDTH QUESTION MARK..FULLWIDTH COMMERCIAL AT
+FF21..FF3A ; NFKC_QC; N # L& [26] FULLWIDTH LATIN CAPITAL LETTER A..FULLWIDTH LATIN CAPITAL LETTER Z
+FF3B ; NFKC_QC; N # Ps FULLWIDTH LEFT SQUARE BRACKET
+FF3C ; NFKC_QC; N # Po FULLWIDTH REVERSE SOLIDUS
+FF3D ; NFKC_QC; N # Pe FULLWIDTH RIGHT SQUARE BRACKET
+FF3E ; NFKC_QC; N # Sk FULLWIDTH CIRCUMFLEX ACCENT
+FF3F ; NFKC_QC; N # Pc FULLWIDTH LOW LINE
+FF40 ; NFKC_QC; N # Sk FULLWIDTH GRAVE ACCENT
+FF41..FF5A ; NFKC_QC; N # L& [26] FULLWIDTH LATIN SMALL LETTER A..FULLWIDTH LATIN SMALL LETTER Z
+FF5B ; NFKC_QC; N # Ps FULLWIDTH LEFT CURLY BRACKET
+FF5C ; NFKC_QC; N # Sm FULLWIDTH VERTICAL LINE
+FF5D ; NFKC_QC; N # Pe FULLWIDTH RIGHT CURLY BRACKET
+FF5E ; NFKC_QC; N # Sm FULLWIDTH TILDE
+FF5F ; NFKC_QC; N # Ps FULLWIDTH LEFT WHITE PARENTHESIS
+FF60 ; NFKC_QC; N # Pe FULLWIDTH RIGHT WHITE PARENTHESIS
+FF61 ; NFKC_QC; N # Po HALFWIDTH IDEOGRAPHIC FULL STOP
+FF62 ; NFKC_QC; N # Ps HALFWIDTH LEFT CORNER BRACKET
+FF63 ; NFKC_QC; N # Pe HALFWIDTH RIGHT CORNER BRACKET
+FF64 ; NFKC_QC; N # Po HALFWIDTH IDEOGRAPHIC COMMA
+FF65 ; NFKC_QC; N # Pc HALFWIDTH KATAKANA MIDDLE DOT
+FF66..FF6F ; NFKC_QC; N # Lo [10] HALFWIDTH KATAKANA LETTER WO..HALFWIDTH KATAKANA LETTER SMALL TU
+FF70 ; NFKC_QC; N # Lm HALFWIDTH KATAKANA-HIRAGANA PROLONGED SOUND MARK
+FF71..FF9D ; NFKC_QC; N # Lo [45] HALFWIDTH KATAKANA LETTER A..HALFWIDTH KATAKANA LETTER N
+FF9E..FF9F ; NFKC_QC; N # Lm [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK
+FFA0..FFBE ; NFKC_QC; N # Lo [31] HALFWIDTH HANGUL FILLER..HALFWIDTH HANGUL LETTER HIEUH
+FFC2..FFC7 ; NFKC_QC; N # Lo [6] HALFWIDTH HANGUL LETTER A..HALFWIDTH HANGUL LETTER E
+FFCA..FFCF ; NFKC_QC; N # Lo [6] HALFWIDTH HANGUL LETTER YEO..HALFWIDTH HANGUL LETTER OE
+FFD2..FFD7 ; NFKC_QC; N # Lo [6] HALFWIDTH HANGUL LETTER YO..HALFWIDTH HANGUL LETTER YU
+FFDA..FFDC ; NFKC_QC; N # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL LETTER I
+FFE0..FFE1 ; NFKC_QC; N # Sc [2] FULLWIDTH CENT SIGN..FULLWIDTH POUND SIGN
+FFE2 ; NFKC_QC; N # Sm FULLWIDTH NOT SIGN
+FFE3 ; NFKC_QC; N # Sk FULLWIDTH MACRON
+FFE4 ; NFKC_QC; N # So FULLWIDTH BROKEN BAR
+FFE5..FFE6 ; NFKC_QC; N # Sc [2] FULLWIDTH YEN SIGN..FULLWIDTH WON SIGN
+FFE8 ; NFKC_QC; N # So HALFWIDTH FORMS LIGHT VERTICAL
+FFE9..FFEC ; NFKC_QC; N # Sm [4] HALFWIDTH LEFTWARDS ARROW..HALFWIDTH DOWNWARDS ARROW
+FFED..FFEE ; NFKC_QC; N # So [2] HALFWIDTH BLACK SQUARE..HALFWIDTH WHITE CIRCLE
+1D15E..1D164 ; NFKC_QC; N # So [7] MUSICAL SYMBOL HALF NOTE..MUSICAL SYMBOL ONE HUNDRED TWENTY-EIGHTH NOTE
+1D1BB..1D1C0 ; NFKC_QC; N # So [6] MUSICAL SYMBOL MINIMA..MUSICAL SYMBOL FUSA BLACK
+1D400..1D454 ; NFKC_QC; N # L& [85] MATHEMATICAL BOLD CAPITAL A..MATHEMATICAL ITALIC SMALL G
+1D456..1D49C ; NFKC_QC; N # L& [71] MATHEMATICAL ITALIC SMALL I..MATHEMATICAL SCRIPT CAPITAL A
+1D49E..1D49F ; NFKC_QC; N # L& [2] MATHEMATICAL SCRIPT CAPITAL C..MATHEMATICAL SCRIPT CAPITAL D
+1D4A2 ; NFKC_QC; N # L& MATHEMATICAL SCRIPT CAPITAL G
+1D4A5..1D4A6 ; NFKC_QC; N # L& [2] MATHEMATICAL SCRIPT CAPITAL J..MATHEMATICAL SCRIPT CAPITAL K
+1D4A9..1D4AC ; NFKC_QC; N # L& [4] MATHEMATICAL SCRIPT CAPITAL N..MATHEMATICAL SCRIPT CAPITAL Q
+1D4AE..1D4B9 ; NFKC_QC; N # L& [12] MATHEMATICAL SCRIPT CAPITAL S..MATHEMATICAL SCRIPT SMALL D
+1D4BB ; NFKC_QC; N # L& MATHEMATICAL SCRIPT SMALL F
+1D4BD..1D4C3 ; NFKC_QC; N # L& [7] MATHEMATICAL SCRIPT SMALL H..MATHEMATICAL SCRIPT SMALL N
+1D4C5..1D505 ; NFKC_QC; N # L& [65] MATHEMATICAL SCRIPT SMALL P..MATHEMATICAL FRAKTUR CAPITAL B
+1D507..1D50A ; NFKC_QC; N # L& [4] MATHEMATICAL FRAKTUR CAPITAL D..MATHEMATICAL FRAKTUR CAPITAL G
+1D50D..1D514 ; NFKC_QC; N # L& [8] MATHEMATICAL FRAKTUR CAPITAL J..MATHEMATICAL FRAKTUR CAPITAL Q
+1D516..1D51C ; NFKC_QC; N # L& [7] MATHEMATICAL FRAKTUR CAPITAL S..MATHEMATICAL FRAKTUR CAPITAL Y
+1D51E..1D539 ; NFKC_QC; N # L& [28] MATHEMATICAL FRAKTUR SMALL A..MATHEMATICAL DOUBLE-STRUCK CAPITAL B
+1D53B..1D53E ; NFKC_QC; N # L& [4] MATHEMATICAL DOUBLE-STRUCK CAPITAL D..MATHEMATICAL DOUBLE-STRUCK CAPITAL G
+1D540..1D544 ; NFKC_QC; N # L& [5] MATHEMATICAL DOUBLE-STRUCK CAPITAL I..MATHEMATICAL DOUBLE-STRUCK CAPITAL M
+1D546 ; NFKC_QC; N # L& MATHEMATICAL DOUBLE-STRUCK CAPITAL O
+1D54A..1D550 ; NFKC_QC; N # L& [7] MATHEMATICAL DOUBLE-STRUCK CAPITAL S..MATHEMATICAL DOUBLE-STRUCK CAPITAL Y
+1D552..1D6A3 ; NFKC_QC; N # L& [338] MATHEMATICAL DOUBLE-STRUCK SMALL A..MATHEMATICAL MONOSPACE SMALL Z
+1D6A8..1D6C0 ; NFKC_QC; N # L& [25] MATHEMATICAL BOLD CAPITAL ALPHA..MATHEMATICAL BOLD CAPITAL OMEGA
+1D6C1 ; NFKC_QC; N # Sm MATHEMATICAL BOLD NABLA
+1D6C2..1D6DA ; NFKC_QC; N # L& [25] MATHEMATICAL BOLD SMALL ALPHA..MATHEMATICAL BOLD SMALL OMEGA
+1D6DB ; NFKC_QC; N # Sm MATHEMATICAL BOLD PARTIAL DIFFERENTIAL
+1D6DC..1D6FA ; NFKC_QC; N # L& [31] MATHEMATICAL BOLD EPSILON SYMBOL..MATHEMATICAL ITALIC CAPITAL OMEGA
+1D6FB ; NFKC_QC; N # Sm MATHEMATICAL ITALIC NABLA
+1D6FC..1D714 ; NFKC_QC; N # L& [25] MATHEMATICAL ITALIC SMALL ALPHA..MATHEMATICAL ITALIC SMALL OMEGA
+1D715 ; NFKC_QC; N # Sm MATHEMATICAL ITALIC PARTIAL DIFFERENTIAL
+1D716..1D734 ; NFKC_QC; N # L& [31] MATHEMATICAL ITALIC EPSILON SYMBOL..MATHEMATICAL BOLD ITALIC CAPITAL OMEGA
+1D735 ; NFKC_QC; N # Sm MATHEMATICAL BOLD ITALIC NABLA
+1D736..1D74E ; NFKC_QC; N # L& [25] MATHEMATICAL BOLD ITALIC SMALL ALPHA..MATHEMATICAL BOLD ITALIC SMALL OMEGA
+1D74F ; NFKC_QC; N # Sm MATHEMATICAL BOLD ITALIC PARTIAL DIFFERENTIAL
+1D750..1D76E ; NFKC_QC; N # L& [31] MATHEMATICAL BOLD ITALIC EPSILON SYMBOL..MATHEMATICAL SANS-SERIF BOLD CAPITAL OMEGA
+1D76F ; NFKC_QC; N # Sm MATHEMATICAL SANS-SERIF BOLD NABLA
+1D770..1D788 ; NFKC_QC; N # L& [25] MATHEMATICAL SANS-SERIF BOLD SMALL ALPHA..MATHEMATICAL SANS-SERIF BOLD SMALL OMEGA
+1D789 ; NFKC_QC; N # Sm MATHEMATICAL SANS-SERIF BOLD PARTIAL DIFFERENTIAL
+1D78A..1D7A8 ; NFKC_QC; N # L& [31] MATHEMATICAL SANS-SERIF BOLD EPSILON SYMBOL..MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL OMEGA
+1D7A9 ; NFKC_QC; N # Sm MATHEMATICAL SANS-SERIF BOLD ITALIC NABLA
+1D7AA..1D7C2 ; NFKC_QC; N # L& [25] MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL ALPHA..MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL OMEGA
+1D7C3 ; NFKC_QC; N # Sm MATHEMATICAL SANS-SERIF BOLD ITALIC PARTIAL DIFFERENTIAL
+1D7C4..1D7C9 ; NFKC_QC; N # L& [6] MATHEMATICAL SANS-SERIF BOLD ITALIC EPSILON SYMBOL..MATHEMATICAL SANS-SERIF BOLD ITALIC PI SYMBOL
+1D7CE..1D7FF ; NFKC_QC; N # Nd [50] MATHEMATICAL BOLD DIGIT ZERO..MATHEMATICAL MONOSPACE DIGIT NINE
+2F800..2FA1D ; NFKC_QC; N # Lo [542] CJK COMPATIBILITY IDEOGRAPH-2F800..CJK COMPATIBILITY IDEOGRAPH-2FA1D
+
+# Total code points: 4319
+
+# ================================================
+
+# NFKC_Quick_Check=Maybe
+
+0300..0304 ; NFKC_QC; M # Mn [5] COMBINING GRAVE ACCENT..COMBINING MACRON
+0306..030C ; NFKC_QC; M # Mn [7] COMBINING BREVE..COMBINING CARON
+030F ; NFKC_QC; M # Mn COMBINING DOUBLE GRAVE ACCENT
+0311 ; NFKC_QC; M # Mn COMBINING INVERTED BREVE
+0313..0314 ; NFKC_QC; M # Mn [2] COMBINING COMMA ABOVE..COMBINING REVERSED COMMA ABOVE
+031B ; NFKC_QC; M # Mn COMBINING HORN
+0323..0328 ; NFKC_QC; M # Mn [6] COMBINING DOT BELOW..COMBINING OGONEK
+032D..032E ; NFKC_QC; M # Mn [2] COMBINING CIRCUMFLEX ACCENT BELOW..COMBINING BREVE BELOW
+0330..0331 ; NFKC_QC; M # Mn [2] COMBINING TILDE BELOW..COMBINING MACRON BELOW
+0338 ; NFKC_QC; M # Mn COMBINING LONG SOLIDUS OVERLAY
+0342 ; NFKC_QC; M # Mn COMBINING GREEK PERISPOMENI
+0345 ; NFKC_QC; M # Mn COMBINING GREEK YPOGEGRAMMENI
+0653..0655 ; NFKC_QC; M # Mn [3] ARABIC MADDAH ABOVE..ARABIC HAMZA BELOW
+093C ; NFKC_QC; M # Mn DEVANAGARI SIGN NUKTA
+09BE ; NFKC_QC; M # Mc BENGALI VOWEL SIGN AA
+09D7 ; NFKC_QC; M # Mc BENGALI AU LENGTH MARK
+0B3E ; NFKC_QC; M # Mc ORIYA VOWEL SIGN AA
+0B56 ; NFKC_QC; M # Mn ORIYA AI LENGTH MARK
+0B57 ; NFKC_QC; M # Mc ORIYA AU LENGTH MARK
+0BBE ; NFKC_QC; M # Mc TAMIL VOWEL SIGN AA
+0BD7 ; NFKC_QC; M # Mc TAMIL AU LENGTH MARK
+0C56 ; NFKC_QC; M # Mn TELUGU AI LENGTH MARK
+0CC2 ; NFKC_QC; M # Mc KANNADA VOWEL SIGN UU
+0CD5..0CD6 ; NFKC_QC; M # Mc [2] KANNADA LENGTH MARK..KANNADA AI LENGTH MARK
+0D3E ; NFKC_QC; M # Mc MALAYALAM VOWEL SIGN AA
+0D57 ; NFKC_QC; M # Mc MALAYALAM AU LENGTH MARK
+0DCA ; NFKC_QC; M # Mn SINHALA SIGN AL-LAKUNA
+0DCF ; NFKC_QC; M # Mc SINHALA VOWEL SIGN AELA-PILLA
+0DDF ; NFKC_QC; M # Mc SINHALA VOWEL SIGN GAYANUKITTA
+102E ; NFKC_QC; M # Mn MYANMAR VOWEL SIGN II
+1161..1175 ; NFKC_QC; M # Lo [21] HANGUL JUNGSEONG A..HANGUL JUNGSEONG I
+11A8..11C2 ; NFKC_QC; M # Lo [27] HANGUL JONGSEONG KIYEOK..HANGUL JONGSEONG HIEUH
+3099..309A ; NFKC_QC; M # Mn [2] COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK..COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK
+
+# Total code points: 101
+
+# ================================================
+
+# Derived Property: Expands_On_NFD
+# Generated according to UAX #15.
+# Characters whose normalized length is not one.
+# WARNING: Normalization of STRINGS must use the algorithm in UAX #15 because characters may interact.
+# The length of a normalized string is not necessarily the sum of the lengths of the normalized characters!
+
+00C0..00C5 ; Expands_On_NFD # L& [6] LATIN CAPITAL LETTER A WITH GRAVE..LATIN CAPITAL LETTER A WITH RING ABOVE
+00C7..00CF ; Expands_On_NFD # L& [9] LATIN CAPITAL LETTER C WITH CEDILLA..LATIN CAPITAL LETTER I WITH DIAERESIS
+00D1..00D6 ; Expands_On_NFD # L& [6] LATIN CAPITAL LETTER N WITH TILDE..LATIN CAPITAL LETTER O WITH DIAERESIS
+00D9..00DD ; Expands_On_NFD # L& [5] LATIN CAPITAL LETTER U WITH GRAVE..LATIN CAPITAL LETTER Y WITH ACUTE
+00E0..00E5 ; Expands_On_NFD # L& [6] LATIN SMALL LETTER A WITH GRAVE..LATIN SMALL LETTER A WITH RING ABOVE
+00E7..00EF ; Expands_On_NFD # L& [9] LATIN SMALL LETTER C WITH CEDILLA..LATIN SMALL LETTER I WITH DIAERESIS
+00F1..00F6 ; Expands_On_NFD # L& [6] LATIN SMALL LETTER N WITH TILDE..LATIN SMALL LETTER O WITH DIAERESIS
+00F9..00FD ; Expands_On_NFD # L& [5] LATIN SMALL LETTER U WITH GRAVE..LATIN SMALL LETTER Y WITH ACUTE
+00FF..010F ; Expands_On_NFD # L& [17] LATIN SMALL LETTER Y WITH DIAERESIS..LATIN SMALL LETTER D WITH CARON
+0112..0125 ; Expands_On_NFD # L& [20] LATIN CAPITAL LETTER E WITH MACRON..LATIN SMALL LETTER H WITH CIRCUMFLEX
+0128..0130 ; Expands_On_NFD # L& [9] LATIN CAPITAL LETTER I WITH TILDE..LATIN CAPITAL LETTER I WITH DOT ABOVE
+0134..0137 ; Expands_On_NFD # L& [4] LATIN CAPITAL LETTER J WITH CIRCUMFLEX..LATIN SMALL LETTER K WITH CEDILLA
+0139..013E ; Expands_On_NFD # L& [6] LATIN CAPITAL LETTER L WITH ACUTE..LATIN SMALL LETTER L WITH CARON
+0143..0148 ; Expands_On_NFD # L& [6] LATIN CAPITAL LETTER N WITH ACUTE..LATIN SMALL LETTER N WITH CARON
+014C..0151 ; Expands_On_NFD # L& [6] LATIN CAPITAL LETTER O WITH MACRON..LATIN SMALL LETTER O WITH DOUBLE ACUTE
+0154..0165 ; Expands_On_NFD # L& [18] LATIN CAPITAL LETTER R WITH ACUTE..LATIN SMALL LETTER T WITH CARON
+0168..017E ; Expands_On_NFD # L& [23] LATIN CAPITAL LETTER U WITH TILDE..LATIN SMALL LETTER Z WITH CARON
+01A0..01A1 ; Expands_On_NFD # L& [2] LATIN CAPITAL LETTER O WITH HORN..LATIN SMALL LETTER O WITH HORN
+01AF..01B0 ; Expands_On_NFD # L& [2] LATIN CAPITAL LETTER U WITH HORN..LATIN SMALL LETTER U WITH HORN
+01CD..01DC ; Expands_On_NFD # L& [16] LATIN CAPITAL LETTER A WITH CARON..LATIN SMALL LETTER U WITH DIAERESIS AND GRAVE
+01DE..01E3 ; Expands_On_NFD # L& [6] LATIN CAPITAL LETTER A WITH DIAERESIS AND MACRON..LATIN SMALL LETTER AE WITH MACRON
+01E6..01F0 ; Expands_On_NFD # L& [11] LATIN CAPITAL LETTER G WITH CARON..LATIN SMALL LETTER J WITH CARON
+01F4..01F5 ; Expands_On_NFD # L& [2] LATIN CAPITAL LETTER G WITH ACUTE..LATIN SMALL LETTER G WITH ACUTE
+01F8..021B ; Expands_On_NFD # L& [36] LATIN CAPITAL LETTER N WITH GRAVE..LATIN SMALL LETTER T WITH COMMA BELOW
+021E..021F ; Expands_On_NFD # L& [2] LATIN CAPITAL LETTER H WITH CARON..LATIN SMALL LETTER H WITH CARON
+0226..0233 ; Expands_On_NFD # L& [14] LATIN CAPITAL LETTER A WITH DOT ABOVE..LATIN SMALL LETTER Y WITH MACRON
+0344 ; Expands_On_NFD # Mn COMBINING GREEK DIALYTIKA TONOS
+0385 ; Expands_On_NFD # Sk GREEK DIALYTIKA TONOS
+0386 ; Expands_On_NFD # L& GREEK CAPITAL LETTER ALPHA WITH TONOS
+0388..038A ; Expands_On_NFD # L& [3] GREEK CAPITAL LETTER EPSILON WITH TONOS..GREEK CAPITAL LETTER IOTA WITH TONOS
+038C ; Expands_On_NFD # L& GREEK CAPITAL LETTER OMICRON WITH TONOS
+038E..0390 ; Expands_On_NFD # L& [3] GREEK CAPITAL LETTER UPSILON WITH TONOS..GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS
+03AA..03B0 ; Expands_On_NFD # L& [7] GREEK CAPITAL LETTER IOTA WITH DIALYTIKA..GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS
+03CA..03CE ; Expands_On_NFD # L& [5] GREEK SMALL LETTER IOTA WITH DIALYTIKA..GREEK SMALL LETTER OMEGA WITH TONOS
+03D3..03D4 ; Expands_On_NFD # L& [2] GREEK UPSILON WITH ACUTE AND HOOK SYMBOL..GREEK UPSILON WITH DIAERESIS AND HOOK SYMBOL
+0400..0401 ; Expands_On_NFD # L& [2] CYRILLIC CAPITAL LETTER IE WITH GRAVE..CYRILLIC CAPITAL LETTER IO
+0403 ; Expands_On_NFD # L& CYRILLIC CAPITAL LETTER GJE
+0407 ; Expands_On_NFD # L& CYRILLIC CAPITAL LETTER YI
+040C..040E ; Expands_On_NFD # L& [3] CYRILLIC CAPITAL LETTER KJE..CYRILLIC CAPITAL LETTER SHORT U
+0419 ; Expands_On_NFD # L& CYRILLIC CAPITAL LETTER SHORT I
+0439 ; Expands_On_NFD # L& CYRILLIC SMALL LETTER SHORT I
+0450..0451 ; Expands_On_NFD # L& [2] CYRILLIC SMALL LETTER IE WITH GRAVE..CYRILLIC SMALL LETTER IO
+0453 ; Expands_On_NFD # L& CYRILLIC SMALL LETTER GJE
+0457 ; Expands_On_NFD # L& CYRILLIC SMALL LETTER YI
+045C..045E ; Expands_On_NFD # L& [3] CYRILLIC SMALL LETTER KJE..CYRILLIC SMALL LETTER SHORT U
+0476..0477 ; Expands_On_NFD # L& [2] CYRILLIC CAPITAL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT..CYRILLIC SMALL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT
+04C1..04C2 ; Expands_On_NFD # L& [2] CYRILLIC CAPITAL LETTER ZHE WITH BREVE..CYRILLIC SMALL LETTER ZHE WITH BREVE
+04D0..04D3 ; Expands_On_NFD # L& [4] CYRILLIC CAPITAL LETTER A WITH BREVE..CYRILLIC SMALL LETTER A WITH DIAERESIS
+04D6..04D7 ; Expands_On_NFD # L& [2] CYRILLIC CAPITAL LETTER IE WITH BREVE..CYRILLIC SMALL LETTER IE WITH BREVE
+04DA..04DF ; Expands_On_NFD # L& [6] CYRILLIC CAPITAL LETTER SCHWA WITH DIAERESIS..CYRILLIC SMALL LETTER ZE WITH DIAERESIS
+04E2..04E7 ; Expands_On_NFD # L& [6] CYRILLIC CAPITAL LETTER I WITH MACRON..CYRILLIC SMALL LETTER O WITH DIAERESIS
+04EA..04F5 ; Expands_On_NFD # L& [12] CYRILLIC CAPITAL LETTER BARRED O WITH DIAERESIS..CYRILLIC SMALL LETTER CHE WITH DIAERESIS
+04F8..04F9 ; Expands_On_NFD # L& [2] CYRILLIC CAPITAL LETTER YERU WITH DIAERESIS..CYRILLIC SMALL LETTER YERU WITH DIAERESIS
+0622..0626 ; Expands_On_NFD # Lo [5] ARABIC LETTER ALEF WITH MADDA ABOVE..ARABIC LETTER YEH WITH HAMZA ABOVE
+06C0 ; Expands_On_NFD # Lo ARABIC LETTER HEH WITH YEH ABOVE
+06C2 ; Expands_On_NFD # Lo ARABIC LETTER HEH GOAL WITH HAMZA ABOVE
+06D3 ; Expands_On_NFD # Lo ARABIC LETTER YEH BARREE WITH HAMZA ABOVE
+0929 ; Expands_On_NFD # Lo DEVANAGARI LETTER NNNA
+0931 ; Expands_On_NFD # Lo DEVANAGARI LETTER RRA
+0934 ; Expands_On_NFD # Lo DEVANAGARI LETTER LLLA
+0958..095F ; Expands_On_NFD # Lo [8] DEVANAGARI LETTER QA..DEVANAGARI LETTER YYA
+09CB..09CC ; Expands_On_NFD # Mc [2] BENGALI VOWEL SIGN O..BENGALI VOWEL SIGN AU
+09DC..09DD ; Expands_On_NFD # Lo [2] BENGALI LETTER RRA..BENGALI LETTER RHA
+09DF ; Expands_On_NFD # Lo BENGALI LETTER YYA
+0A33 ; Expands_On_NFD # Lo GURMUKHI LETTER LLA
+0A36 ; Expands_On_NFD # Lo GURMUKHI LETTER SHA
+0A59..0A5B ; Expands_On_NFD # Lo [3] GURMUKHI LETTER KHHA..GURMUKHI LETTER ZA
+0A5E ; Expands_On_NFD # Lo GURMUKHI LETTER FA
+0B48 ; Expands_On_NFD # Mc ORIYA VOWEL SIGN AI
+0B4B..0B4C ; Expands_On_NFD # Mc [2] ORIYA VOWEL SIGN O..ORIYA VOWEL SIGN AU
+0B5C..0B5D ; Expands_On_NFD # Lo [2] ORIYA LETTER RRA..ORIYA LETTER RHA
+0B94 ; Expands_On_NFD # Lo TAMIL LETTER AU
+0BCA..0BCC ; Expands_On_NFD # Mc [3] TAMIL VOWEL SIGN O..TAMIL VOWEL SIGN AU
+0C48 ; Expands_On_NFD # Mn TELUGU VOWEL SIGN AI
+0CC0 ; Expands_On_NFD # Mc KANNADA VOWEL SIGN II
+0CC7..0CC8 ; Expands_On_NFD # Mc [2] KANNADA VOWEL SIGN EE..KANNADA VOWEL SIGN AI
+0CCA..0CCB ; Expands_On_NFD # Mc [2] KANNADA VOWEL SIGN O..KANNADA VOWEL SIGN OO
+0D4A..0D4C ; Expands_On_NFD # Mc [3] MALAYALAM VOWEL SIGN O..MALAYALAM VOWEL SIGN AU
+0DDA ; Expands_On_NFD # Mc SINHALA VOWEL SIGN DIGA KOMBUVA
+0DDC..0DDE ; Expands_On_NFD # Mc [3] SINHALA VOWEL SIGN KOMBUVA HAA AELA-PILLA..SINHALA VOWEL SIGN KOMBUVA HAA GAYANUKITTA
+0F43 ; Expands_On_NFD # Lo TIBETAN LETTER GHA
+0F4D ; Expands_On_NFD # Lo TIBETAN LETTER DDHA
+0F52 ; Expands_On_NFD # Lo TIBETAN LETTER DHA
+0F57 ; Expands_On_NFD # Lo TIBETAN LETTER BHA
+0F5C ; Expands_On_NFD # Lo TIBETAN LETTER DZHA
+0F69 ; Expands_On_NFD # Lo TIBETAN LETTER KSSA
+0F73 ; Expands_On_NFD # Mn TIBETAN VOWEL SIGN II
+0F75..0F76 ; Expands_On_NFD # Mn [2] TIBETAN VOWEL SIGN UU..TIBETAN VOWEL SIGN VOCALIC R
+0F78 ; Expands_On_NFD # Mn TIBETAN VOWEL SIGN VOCALIC L
+0F81 ; Expands_On_NFD # Mn TIBETAN VOWEL SIGN REVERSED II
+0F93 ; Expands_On_NFD # Mn TIBETAN SUBJOINED LETTER GHA
+0F9D ; Expands_On_NFD # Mn TIBETAN SUBJOINED LETTER DDHA
+0FA2 ; Expands_On_NFD # Mn TIBETAN SUBJOINED LETTER DHA
+0FA7 ; Expands_On_NFD # Mn TIBETAN SUBJOINED LETTER BHA
+0FAC ; Expands_On_NFD # Mn TIBETAN SUBJOINED LETTER DZHA
+0FB9 ; Expands_On_NFD # Mn TIBETAN SUBJOINED LETTER KSSA
+1026 ; Expands_On_NFD # Lo MYANMAR LETTER UU
+1E00..1E99 ; Expands_On_NFD # L& [154] LATIN CAPITAL LETTER A WITH RING BELOW..LATIN SMALL LETTER Y WITH RING ABOVE
+1E9B ; Expands_On_NFD # L& LATIN SMALL LETTER LONG S WITH DOT ABOVE
+1EA0..1EF9 ; Expands_On_NFD # L& [90] LATIN CAPITAL LETTER A WITH DOT BELOW..LATIN SMALL LETTER Y WITH TILDE
+1F00..1F15 ; Expands_On_NFD # L& [22] GREEK SMALL LETTER ALPHA WITH PSILI..GREEK SMALL LETTER EPSILON WITH DASIA AND OXIA
+1F18..1F1D ; Expands_On_NFD # L& [6] GREEK CAPITAL LETTER EPSILON WITH PSILI..GREEK CAPITAL LETTER EPSILON WITH DASIA AND OXIA
+1F20..1F45 ; Expands_On_NFD # L& [38] GREEK SMALL LETTER ETA WITH PSILI..GREEK SMALL LETTER OMICRON WITH DASIA AND OXIA
+1F48..1F4D ; Expands_On_NFD # L& [6] GREEK CAPITAL LETTER OMICRON WITH PSILI..GREEK CAPITAL LETTER OMICRON WITH DASIA AND OXIA
+1F50..1F57 ; Expands_On_NFD # L& [8] GREEK SMALL LETTER UPSILON WITH PSILI..GREEK SMALL LETTER UPSILON WITH DASIA AND PERISPOMENI
+1F59 ; Expands_On_NFD # L& GREEK CAPITAL LETTER UPSILON WITH DASIA
+1F5B ; Expands_On_NFD # L& GREEK CAPITAL LETTER UPSILON WITH DASIA AND VARIA
+1F5D ; Expands_On_NFD # L& GREEK CAPITAL LETTER UPSILON WITH DASIA AND OXIA
+1F5F..1F7D ; Expands_On_NFD # L& [31] GREEK CAPITAL LETTER UPSILON WITH DASIA AND PERISPOMENI..GREEK SMALL LETTER OMEGA WITH OXIA
+1F80..1FB4 ; Expands_On_NFD # L& [53] GREEK SMALL LETTER ALPHA WITH PSILI AND YPOGEGRAMMENI..GREEK SMALL LETTER ALPHA WITH OXIA AND YPOGEGRAMMENI
+1FB6..1FBC ; Expands_On_NFD # L& [7] GREEK SMALL LETTER ALPHA WITH PERISPOMENI..GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI
+1FC1 ; Expands_On_NFD # Sk GREEK DIALYTIKA AND PERISPOMENI
+1FC2..1FC4 ; Expands_On_NFD # L& [3] GREEK SMALL LETTER ETA WITH VARIA AND YPOGEGRAMMENI..GREEK SMALL LETTER ETA WITH OXIA AND YPOGEGRAMMENI
+1FC6..1FCC ; Expands_On_NFD # L& [7] GREEK SMALL LETTER ETA WITH PERISPOMENI..GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI
+1FCD..1FCF ; Expands_On_NFD # Sk [3] GREEK PSILI AND VARIA..GREEK PSILI AND PERISPOMENI
+1FD0..1FD3 ; Expands_On_NFD # L& [4] GREEK SMALL LETTER IOTA WITH VRACHY..GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA
+1FD6..1FDB ; Expands_On_NFD # L& [6] GREEK SMALL LETTER IOTA WITH PERISPOMENI..GREEK CAPITAL LETTER IOTA WITH OXIA
+1FDD..1FDF ; Expands_On_NFD # Sk [3] GREEK DASIA AND VARIA..GREEK DASIA AND PERISPOMENI
+1FE0..1FEC ; Expands_On_NFD # L& [13] GREEK SMALL LETTER UPSILON WITH VRACHY..GREEK CAPITAL LETTER RHO WITH DASIA
+1FED..1FEE ; Expands_On_NFD # Sk [2] GREEK DIALYTIKA AND VARIA..GREEK DIALYTIKA AND OXIA
+1FF2..1FF4 ; Expands_On_NFD # L& [3] GREEK SMALL LETTER OMEGA WITH VARIA AND YPOGEGRAMMENI..GREEK SMALL LETTER OMEGA WITH OXIA AND YPOGEGRAMMENI
+1FF6..1FFC ; Expands_On_NFD # L& [7] GREEK SMALL LETTER OMEGA WITH PERISPOMENI..GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI
+212B ; Expands_On_NFD # L& ANGSTROM SIGN
+219A..219B ; Expands_On_NFD # Sm [2] LEFTWARDS ARROW WITH STROKE..RIGHTWARDS ARROW WITH STROKE
+21AE ; Expands_On_NFD # Sm LEFT RIGHT ARROW WITH STROKE
+21CD ; Expands_On_NFD # So LEFTWARDS DOUBLE ARROW WITH STROKE
+21CE..21CF ; Expands_On_NFD # Sm [2] LEFT RIGHT DOUBLE ARROW WITH STROKE..RIGHTWARDS DOUBLE ARROW WITH STROKE
+2204 ; Expands_On_NFD # Sm THERE DOES NOT EXIST
+2209 ; Expands_On_NFD # Sm NOT AN ELEMENT OF
+220C ; Expands_On_NFD # Sm DOES NOT CONTAIN AS MEMBER
+2224 ; Expands_On_NFD # Sm DOES NOT DIVIDE
+2226 ; Expands_On_NFD # Sm NOT PARALLEL TO
+2241 ; Expands_On_NFD # Sm NOT TILDE
+2244 ; Expands_On_NFD # Sm NOT ASYMPTOTICALLY EQUAL TO
+2247 ; Expands_On_NFD # Sm NEITHER APPROXIMATELY NOR ACTUALLY EQUAL TO
+2249 ; Expands_On_NFD # Sm NOT ALMOST EQUAL TO
+2260 ; Expands_On_NFD # Sm NOT EQUAL TO
+2262 ; Expands_On_NFD # Sm NOT IDENTICAL TO
+226D..2271 ; Expands_On_NFD # Sm [5] NOT EQUIVALENT TO..NEITHER GREATER-THAN NOR EQUAL TO
+2274..2275 ; Expands_On_NFD # Sm [2] NEITHER LESS-THAN NOR EQUIVALENT TO..NEITHER GREATER-THAN NOR EQUIVALENT TO
+2278..2279 ; Expands_On_NFD # Sm [2] NEITHER LESS-THAN NOR GREATER-THAN..NEITHER GREATER-THAN NOR LESS-THAN
+2280..2281 ; Expands_On_NFD # Sm [2] DOES NOT PRECEDE..DOES NOT SUCCEED
+2284..2285 ; Expands_On_NFD # Sm [2] NOT A SUBSET OF..NOT A SUPERSET OF
+2288..2289 ; Expands_On_NFD # Sm [2] NEITHER A SUBSET OF NOR EQUAL TO..NEITHER A SUPERSET OF NOR EQUAL TO
+22AC..22AF ; Expands_On_NFD # Sm [4] DOES NOT PROVE..NEGATED DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE
+22E0..22E3 ; Expands_On_NFD # Sm [4] DOES NOT PRECEDE OR EQUAL..NOT SQUARE ORIGINAL OF OR EQUAL TO
+22EA..22ED ; Expands_On_NFD # Sm [4] NOT NORMAL SUBGROUP OF..DOES NOT CONTAIN AS NORMAL SUBGROUP OR EQUAL
+2ADC ; Expands_On_NFD # Sm FORKING
+304C ; Expands_On_NFD # Lo HIRAGANA LETTER GA
+304E ; Expands_On_NFD # Lo HIRAGANA LETTER GI
+3050 ; Expands_On_NFD # Lo HIRAGANA LETTER GU
+3052 ; Expands_On_NFD # Lo HIRAGANA LETTER GE
+3054 ; Expands_On_NFD # Lo HIRAGANA LETTER GO
+3056 ; Expands_On_NFD # Lo HIRAGANA LETTER ZA
+3058 ; Expands_On_NFD # Lo HIRAGANA LETTER ZI
+305A ; Expands_On_NFD # Lo HIRAGANA LETTER ZU
+305C ; Expands_On_NFD # Lo HIRAGANA LETTER ZE
+305E ; Expands_On_NFD # Lo HIRAGANA LETTER ZO
+3060 ; Expands_On_NFD # Lo HIRAGANA LETTER DA
+3062 ; Expands_On_NFD # Lo HIRAGANA LETTER DI
+3065 ; Expands_On_NFD # Lo HIRAGANA LETTER DU
+3067 ; Expands_On_NFD # Lo HIRAGANA LETTER DE
+3069 ; Expands_On_NFD # Lo HIRAGANA LETTER DO
+3070..3071 ; Expands_On_NFD # Lo [2] HIRAGANA LETTER BA..HIRAGANA LETTER PA
+3073..3074 ; Expands_On_NFD # Lo [2] HIRAGANA LETTER BI..HIRAGANA LETTER PI
+3076..3077 ; Expands_On_NFD # Lo [2] HIRAGANA LETTER BU..HIRAGANA LETTER PU
+3079..307A ; Expands_On_NFD # Lo [2] HIRAGANA LETTER BE..HIRAGANA LETTER PE
+307C..307D ; Expands_On_NFD # Lo [2] HIRAGANA LETTER BO..HIRAGANA LETTER PO
+3094 ; Expands_On_NFD # Lo HIRAGANA LETTER VU
+309E ; Expands_On_NFD # Lm HIRAGANA VOICED ITERATION MARK
+30AC ; Expands_On_NFD # Lo KATAKANA LETTER GA
+30AE ; Expands_On_NFD # Lo KATAKANA LETTER GI
+30B0 ; Expands_On_NFD # Lo KATAKANA LETTER GU
+30B2 ; Expands_On_NFD # Lo KATAKANA LETTER GE
+30B4 ; Expands_On_NFD # Lo KATAKANA LETTER GO
+30B6 ; Expands_On_NFD # Lo KATAKANA LETTER ZA
+30B8 ; Expands_On_NFD # Lo KATAKANA LETTER ZI
+30BA ; Expands_On_NFD # Lo KATAKANA LETTER ZU
+30BC ; Expands_On_NFD # Lo KATAKANA LETTER ZE
+30BE ; Expands_On_NFD # Lo KATAKANA LETTER ZO
+30C0 ; Expands_On_NFD # Lo KATAKANA LETTER DA
+30C2 ; Expands_On_NFD # Lo KATAKANA LETTER DI
+30C5 ; Expands_On_NFD # Lo KATAKANA LETTER DU
+30C7 ; Expands_On_NFD # Lo KATAKANA LETTER DE
+30C9 ; Expands_On_NFD # Lo KATAKANA LETTER DO
+30D0..30D1 ; Expands_On_NFD # Lo [2] KATAKANA LETTER BA..KATAKANA LETTER PA
+30D3..30D4 ; Expands_On_NFD # Lo [2] KATAKANA LETTER BI..KATAKANA LETTER PI
+30D6..30D7 ; Expands_On_NFD # Lo [2] KATAKANA LETTER BU..KATAKANA LETTER PU
+30D9..30DA ; Expands_On_NFD # Lo [2] KATAKANA LETTER BE..KATAKANA LETTER PE
+30DC..30DD ; Expands_On_NFD # Lo [2] KATAKANA LETTER BO..KATAKANA LETTER PO
+30F4 ; Expands_On_NFD # Lo KATAKANA LETTER VU
+30F7..30FA ; Expands_On_NFD # Lo [4] KATAKANA LETTER VA..KATAKANA LETTER VO
+30FE ; Expands_On_NFD # Lm KATAKANA VOICED ITERATION MARK
+AC00..D7A3 ; Expands_On_NFD # Lo [11172] HANGUL SYLLABLE GA..HANGUL SYLLABLE HIH
+FB1D ; Expands_On_NFD # Lo HEBREW LETTER YOD WITH HIRIQ
+FB1F ; Expands_On_NFD # Lo HEBREW LIGATURE YIDDISH YOD YOD PATAH
+FB2A..FB36 ; Expands_On_NFD # Lo [13] HEBREW LETTER SHIN WITH SHIN DOT..HEBREW LETTER ZAYIN WITH DAGESH
+FB38..FB3C ; Expands_On_NFD # Lo [5] HEBREW LETTER TET WITH DAGESH..HEBREW LETTER LAMED WITH DAGESH
+FB3E ; Expands_On_NFD # Lo HEBREW LETTER MEM WITH DAGESH
+FB40..FB41 ; Expands_On_NFD # Lo [2] HEBREW LETTER NUN WITH DAGESH..HEBREW LETTER SAMEKH WITH DAGESH
+FB43..FB44 ; Expands_On_NFD # Lo [2] HEBREW LETTER FINAL PE WITH DAGESH..HEBREW LETTER PE WITH DAGESH
+FB46..FB4E ; Expands_On_NFD # Lo [9] HEBREW LETTER TSADI WITH DAGESH..HEBREW LETTER PE WITH RAFE
+1D15E..1D164 ; Expands_On_NFD # So [7] MUSICAL SYMBOL HALF NOTE..MUSICAL SYMBOL ONE HUNDRED TWENTY-EIGHTH NOTE
+1D1BB..1D1C0 ; Expands_On_NFD # So [6] MUSICAL SYMBOL MINIMA..MUSICAL SYMBOL FUSA BLACK
+
+# Total code points: 12192
+
+# ================================================
+
+# Derived Property: Expands_On_NFC
+# Generated according to UAX #15.
+# Characters whose normalized length is not one.
+# WARNING: Normalization of STRINGS must use the algorithm in UAX #15 because characters may interact.
+# The length of a normalized string is not necessarily the sum of the lengths of the normalized characters!
+
+0344 ; Expands_On_NFC # Mn COMBINING GREEK DIALYTIKA TONOS
+0958..095F ; Expands_On_NFC # Lo [8] DEVANAGARI LETTER QA..DEVANAGARI LETTER YYA
+09DC..09DD ; Expands_On_NFC # Lo [2] BENGALI LETTER RRA..BENGALI LETTER RHA
+09DF ; Expands_On_NFC # Lo BENGALI LETTER YYA
+0A33 ; Expands_On_NFC # Lo GURMUKHI LETTER LLA
+0A36 ; Expands_On_NFC # Lo GURMUKHI LETTER SHA
+0A59..0A5B ; Expands_On_NFC # Lo [3] GURMUKHI LETTER KHHA..GURMUKHI LETTER ZA
+0A5E ; Expands_On_NFC # Lo GURMUKHI LETTER FA
+0B5C..0B5D ; Expands_On_NFC # Lo [2] ORIYA LETTER RRA..ORIYA LETTER RHA
+0F43 ; Expands_On_NFC # Lo TIBETAN LETTER GHA
+0F4D ; Expands_On_NFC # Lo TIBETAN LETTER DDHA
+0F52 ; Expands_On_NFC # Lo TIBETAN LETTER DHA
+0F57 ; Expands_On_NFC # Lo TIBETAN LETTER BHA
+0F5C ; Expands_On_NFC # Lo TIBETAN LETTER DZHA
+0F69 ; Expands_On_NFC # Lo TIBETAN LETTER KSSA
+0F73 ; Expands_On_NFC # Mn TIBETAN VOWEL SIGN II
+0F75..0F76 ; Expands_On_NFC # Mn [2] TIBETAN VOWEL SIGN UU..TIBETAN VOWEL SIGN VOCALIC R
+0F78 ; Expands_On_NFC # Mn TIBETAN VOWEL SIGN VOCALIC L
+0F81 ; Expands_On_NFC # Mn TIBETAN VOWEL SIGN REVERSED II
+0F93 ; Expands_On_NFC # Mn TIBETAN SUBJOINED LETTER GHA
+0F9D ; Expands_On_NFC # Mn TIBETAN SUBJOINED LETTER DDHA
+0FA2 ; Expands_On_NFC # Mn TIBETAN SUBJOINED LETTER DHA
+0FA7 ; Expands_On_NFC # Mn TIBETAN SUBJOINED LETTER BHA
+0FAC ; Expands_On_NFC # Mn TIBETAN SUBJOINED LETTER DZHA
+0FB9 ; Expands_On_NFC # Mn TIBETAN SUBJOINED LETTER KSSA
+2ADC ; Expands_On_NFC # Sm FORKING
+FB1D ; Expands_On_NFC # Lo HEBREW LETTER YOD WITH HIRIQ
+FB1F ; Expands_On_NFC # Lo HEBREW LIGATURE YIDDISH YOD YOD PATAH
+FB2A..FB36 ; Expands_On_NFC # Lo [13] HEBREW LETTER SHIN WITH SHIN DOT..HEBREW LETTER ZAYIN WITH DAGESH
+FB38..FB3C ; Expands_On_NFC # Lo [5] HEBREW LETTER TET WITH DAGESH..HEBREW LETTER LAMED WITH DAGESH
+FB3E ; Expands_On_NFC # Lo HEBREW LETTER MEM WITH DAGESH
+FB40..FB41 ; Expands_On_NFC # Lo [2] HEBREW LETTER NUN WITH DAGESH..HEBREW LETTER SAMEKH WITH DAGESH
+FB43..FB44 ; Expands_On_NFC # Lo [2] HEBREW LETTER FINAL PE WITH DAGESH..HEBREW LETTER PE WITH DAGESH
+FB46..FB4E ; Expands_On_NFC # Lo [9] HEBREW LETTER TSADI WITH DAGESH..HEBREW LETTER PE WITH RAFE
+1D15E..1D164 ; Expands_On_NFC # So [7] MUSICAL SYMBOL HALF NOTE..MUSICAL SYMBOL ONE HUNDRED TWENTY-EIGHTH NOTE
+1D1BB..1D1C0 ; Expands_On_NFC # So [6] MUSICAL SYMBOL MINIMA..MUSICAL SYMBOL FUSA BLACK
+
+# Total code points: 85
+
+# ================================================
+
+# Derived Property: Expands_On_NFKD
+# Generated according to UAX #15.
+# Characters whose normalized length is not one.
+# WARNING: Normalization of STRINGS must use the algorithm in UAX #15 because characters may interact.
+# The length of a normalized string is not necessarily the sum of the lengths of the normalized characters!
+
+00A8 ; Expands_On_NFKD # Sk DIAERESIS
+00AF ; Expands_On_NFKD # Sk MACRON
+00B4 ; Expands_On_NFKD # Sk ACUTE ACCENT
+00B8 ; Expands_On_NFKD # Sk CEDILLA
+00BC..00BE ; Expands_On_NFKD # No [3] VULGAR FRACTION ONE QUARTER..VULGAR FRACTION THREE QUARTERS
+00C0..00C5 ; Expands_On_NFKD # L& [6] LATIN CAPITAL LETTER A WITH GRAVE..LATIN CAPITAL LETTER A WITH RING ABOVE
+00C7..00CF ; Expands_On_NFKD # L& [9] LATIN CAPITAL LETTER C WITH CEDILLA..LATIN CAPITAL LETTER I WITH DIAERESIS
+00D1..00D6 ; Expands_On_NFKD # L& [6] LATIN CAPITAL LETTER N WITH TILDE..LATIN CAPITAL LETTER O WITH DIAERESIS
+00D9..00DD ; Expands_On_NFKD # L& [5] LATIN CAPITAL LETTER U WITH GRAVE..LATIN CAPITAL LETTER Y WITH ACUTE
+00E0..00E5 ; Expands_On_NFKD # L& [6] LATIN SMALL LETTER A WITH GRAVE..LATIN SMALL LETTER A WITH RING ABOVE
+00E7..00EF ; Expands_On_NFKD # L& [9] LATIN SMALL LETTER C WITH CEDILLA..LATIN SMALL LETTER I WITH DIAERESIS
+00F1..00F6 ; Expands_On_NFKD # L& [6] LATIN SMALL LETTER N WITH TILDE..LATIN SMALL LETTER O WITH DIAERESIS
+00F9..00FD ; Expands_On_NFKD # L& [5] LATIN SMALL LETTER U WITH GRAVE..LATIN SMALL LETTER Y WITH ACUTE
+00FF..010F ; Expands_On_NFKD # L& [17] LATIN SMALL LETTER Y WITH DIAERESIS..LATIN SMALL LETTER D WITH CARON
+0112..0125 ; Expands_On_NFKD # L& [20] LATIN CAPITAL LETTER E WITH MACRON..LATIN SMALL LETTER H WITH CIRCUMFLEX
+0128..0130 ; Expands_On_NFKD # L& [9] LATIN CAPITAL LETTER I WITH TILDE..LATIN CAPITAL LETTER I WITH DOT ABOVE
+0132..0137 ; Expands_On_NFKD # L& [6] LATIN CAPITAL LIGATURE IJ..LATIN SMALL LETTER K WITH CEDILLA
+0139..0140 ; Expands_On_NFKD # L& [8] LATIN CAPITAL LETTER L WITH ACUTE..LATIN SMALL LETTER L WITH MIDDLE DOT
+0143..0149 ; Expands_On_NFKD # L& [7] LATIN CAPITAL LETTER N WITH ACUTE..LATIN SMALL LETTER N PRECEDED BY APOSTROPHE
+014C..0151 ; Expands_On_NFKD # L& [6] LATIN CAPITAL LETTER O WITH MACRON..LATIN SMALL LETTER O WITH DOUBLE ACUTE
+0154..0165 ; Expands_On_NFKD # L& [18] LATIN CAPITAL LETTER R WITH ACUTE..LATIN SMALL LETTER T WITH CARON
+0168..017E ; Expands_On_NFKD # L& [23] LATIN CAPITAL LETTER U WITH TILDE..LATIN SMALL LETTER Z WITH CARON
+01A0..01A1 ; Expands_On_NFKD # L& [2] LATIN CAPITAL LETTER O WITH HORN..LATIN SMALL LETTER O WITH HORN
+01AF..01B0 ; Expands_On_NFKD # L& [2] LATIN CAPITAL LETTER U WITH HORN..LATIN SMALL LETTER U WITH HORN
+01C4..01DC ; Expands_On_NFKD # L& [25] LATIN CAPITAL LETTER DZ WITH CARON..LATIN SMALL LETTER U WITH DIAERESIS AND GRAVE
+01DE..01E3 ; Expands_On_NFKD # L& [6] LATIN CAPITAL LETTER A WITH DIAERESIS AND MACRON..LATIN SMALL LETTER AE WITH MACRON
+01E6..01F5 ; Expands_On_NFKD # L& [16] LATIN CAPITAL LETTER G WITH CARON..LATIN SMALL LETTER G WITH ACUTE
+01F8..021B ; Expands_On_NFKD # L& [36] LATIN CAPITAL LETTER N WITH GRAVE..LATIN SMALL LETTER T WITH COMMA BELOW
+021E..021F ; Expands_On_NFKD # L& [2] LATIN CAPITAL LETTER H WITH CARON..LATIN SMALL LETTER H WITH CARON
+0226..0233 ; Expands_On_NFKD # L& [14] LATIN CAPITAL LETTER A WITH DOT ABOVE..LATIN SMALL LETTER Y WITH MACRON
+02D8..02DD ; Expands_On_NFKD # Sk [6] BREVE..DOUBLE ACUTE ACCENT
+0344 ; Expands_On_NFKD # Mn COMBINING GREEK DIALYTIKA TONOS
+037A ; Expands_On_NFKD # Lm GREEK YPOGEGRAMMENI
+0384..0385 ; Expands_On_NFKD # Sk [2] GREEK TONOS..GREEK DIALYTIKA TONOS
+0386 ; Expands_On_NFKD # L& GREEK CAPITAL LETTER ALPHA WITH TONOS
+0388..038A ; Expands_On_NFKD # L& [3] GREEK CAPITAL LETTER EPSILON WITH TONOS..GREEK CAPITAL LETTER IOTA WITH TONOS
+038C ; Expands_On_NFKD # L& GREEK CAPITAL LETTER OMICRON WITH TONOS
+038E..0390 ; Expands_On_NFKD # L& [3] GREEK CAPITAL LETTER UPSILON WITH TONOS..GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS
+03AA..03B0 ; Expands_On_NFKD # L& [7] GREEK CAPITAL LETTER IOTA WITH DIALYTIKA..GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS
+03CA..03CE ; Expands_On_NFKD # L& [5] GREEK SMALL LETTER IOTA WITH DIALYTIKA..GREEK SMALL LETTER OMEGA WITH TONOS
+03D3..03D4 ; Expands_On_NFKD # L& [2] GREEK UPSILON WITH ACUTE AND HOOK SYMBOL..GREEK UPSILON WITH DIAERESIS AND HOOK SYMBOL
+0400..0401 ; Expands_On_NFKD # L& [2] CYRILLIC CAPITAL LETTER IE WITH GRAVE..CYRILLIC CAPITAL LETTER IO
+0403 ; Expands_On_NFKD # L& CYRILLIC CAPITAL LETTER GJE
+0407 ; Expands_On_NFKD # L& CYRILLIC CAPITAL LETTER YI
+040C..040E ; Expands_On_NFKD # L& [3] CYRILLIC CAPITAL LETTER KJE..CYRILLIC CAPITAL LETTER SHORT U
+0419 ; Expands_On_NFKD # L& CYRILLIC CAPITAL LETTER SHORT I
+0439 ; Expands_On_NFKD # L& CYRILLIC SMALL LETTER SHORT I
+0450..0451 ; Expands_On_NFKD # L& [2] CYRILLIC SMALL LETTER IE WITH GRAVE..CYRILLIC SMALL LETTER IO
+0453 ; Expands_On_NFKD # L& CYRILLIC SMALL LETTER GJE
+0457 ; Expands_On_NFKD # L& CYRILLIC SMALL LETTER YI
+045C..045E ; Expands_On_NFKD # L& [3] CYRILLIC SMALL LETTER KJE..CYRILLIC SMALL LETTER SHORT U
+0476..0477 ; Expands_On_NFKD # L& [2] CYRILLIC CAPITAL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT..CYRILLIC SMALL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT
+04C1..04C2 ; Expands_On_NFKD # L& [2] CYRILLIC CAPITAL LETTER ZHE WITH BREVE..CYRILLIC SMALL LETTER ZHE WITH BREVE
+04D0..04D3 ; Expands_On_NFKD # L& [4] CYRILLIC CAPITAL LETTER A WITH BREVE..CYRILLIC SMALL LETTER A WITH DIAERESIS
+04D6..04D7 ; Expands_On_NFKD # L& [2] CYRILLIC CAPITAL LETTER IE WITH BREVE..CYRILLIC SMALL LETTER IE WITH BREVE
+04DA..04DF ; Expands_On_NFKD # L& [6] CYRILLIC CAPITAL LETTER SCHWA WITH DIAERESIS..CYRILLIC SMALL LETTER ZE WITH DIAERESIS
+04E2..04E7 ; Expands_On_NFKD # L& [6] CYRILLIC CAPITAL LETTER I WITH MACRON..CYRILLIC SMALL LETTER O WITH DIAERESIS
+04EA..04F5 ; Expands_On_NFKD # L& [12] CYRILLIC CAPITAL LETTER BARRED O WITH DIAERESIS..CYRILLIC SMALL LETTER CHE WITH DIAERESIS
+04F8..04F9 ; Expands_On_NFKD # L& [2] CYRILLIC CAPITAL LETTER YERU WITH DIAERESIS..CYRILLIC SMALL LETTER YERU WITH DIAERESIS
+0587 ; Expands_On_NFKD # L& ARMENIAN SMALL LIGATURE ECH YIWN
+0622..0626 ; Expands_On_NFKD # Lo [5] ARABIC LETTER ALEF WITH MADDA ABOVE..ARABIC LETTER YEH WITH HAMZA ABOVE
+0675..0678 ; Expands_On_NFKD # Lo [4] ARABIC LETTER HIGH HAMZA ALEF..ARABIC LETTER HIGH HAMZA YEH
+06C0 ; Expands_On_NFKD # Lo ARABIC LETTER HEH WITH YEH ABOVE
+06C2 ; Expands_On_NFKD # Lo ARABIC LETTER HEH GOAL WITH HAMZA ABOVE
+06D3 ; Expands_On_NFKD # Lo ARABIC LETTER YEH BARREE WITH HAMZA ABOVE
+0929 ; Expands_On_NFKD # Lo DEVANAGARI LETTER NNNA
+0931 ; Expands_On_NFKD # Lo DEVANAGARI LETTER RRA
+0934 ; Expands_On_NFKD # Lo DEVANAGARI LETTER LLLA
+0958..095F ; Expands_On_NFKD # Lo [8] DEVANAGARI LETTER QA..DEVANAGARI LETTER YYA
+09CB..09CC ; Expands_On_NFKD # Mc [2] BENGALI VOWEL SIGN O..BENGALI VOWEL SIGN AU
+09DC..09DD ; Expands_On_NFKD # Lo [2] BENGALI LETTER RRA..BENGALI LETTER RHA
+09DF ; Expands_On_NFKD # Lo BENGALI LETTER YYA
+0A33 ; Expands_On_NFKD # Lo GURMUKHI LETTER LLA
+0A36 ; Expands_On_NFKD # Lo GURMUKHI LETTER SHA
+0A59..0A5B ; Expands_On_NFKD # Lo [3] GURMUKHI LETTER KHHA..GURMUKHI LETTER ZA
+0A5E ; Expands_On_NFKD # Lo GURMUKHI LETTER FA
+0B48 ; Expands_On_NFKD # Mc ORIYA VOWEL SIGN AI
+0B4B..0B4C ; Expands_On_NFKD # Mc [2] ORIYA VOWEL SIGN O..ORIYA VOWEL SIGN AU
+0B5C..0B5D ; Expands_On_NFKD # Lo [2] ORIYA LETTER RRA..ORIYA LETTER RHA
+0B94 ; Expands_On_NFKD # Lo TAMIL LETTER AU
+0BCA..0BCC ; Expands_On_NFKD # Mc [3] TAMIL VOWEL SIGN O..TAMIL VOWEL SIGN AU
+0C48 ; Expands_On_NFKD # Mn TELUGU VOWEL SIGN AI
+0CC0 ; Expands_On_NFKD # Mc KANNADA VOWEL SIGN II
+0CC7..0CC8 ; Expands_On_NFKD # Mc [2] KANNADA VOWEL SIGN EE..KANNADA VOWEL SIGN AI
+0CCA..0CCB ; Expands_On_NFKD # Mc [2] KANNADA VOWEL SIGN O..KANNADA VOWEL SIGN OO
+0D4A..0D4C ; Expands_On_NFKD # Mc [3] MALAYALAM VOWEL SIGN O..MALAYALAM VOWEL SIGN AU
+0DDA ; Expands_On_NFKD # Mc SINHALA VOWEL SIGN DIGA KOMBUVA
+0DDC..0DDE ; Expands_On_NFKD # Mc [3] SINHALA VOWEL SIGN KOMBUVA HAA AELA-PILLA..SINHALA VOWEL SIGN KOMBUVA HAA GAYANUKITTA
+0E33 ; Expands_On_NFKD # Lo THAI CHARACTER SARA AM
+0EB3 ; Expands_On_NFKD # Lo LAO VOWEL SIGN AM
+0EDC..0EDD ; Expands_On_NFKD # Lo [2] LAO HO NO..LAO HO MO
+0F43 ; Expands_On_NFKD # Lo TIBETAN LETTER GHA
+0F4D ; Expands_On_NFKD # Lo TIBETAN LETTER DDHA
+0F52 ; Expands_On_NFKD # Lo TIBETAN LETTER DHA
+0F57 ; Expands_On_NFKD # Lo TIBETAN LETTER BHA
+0F5C ; Expands_On_NFKD # Lo TIBETAN LETTER DZHA
+0F69 ; Expands_On_NFKD # Lo TIBETAN LETTER KSSA
+0F73 ; Expands_On_NFKD # Mn TIBETAN VOWEL SIGN II
+0F75..0F79 ; Expands_On_NFKD # Mn [5] TIBETAN VOWEL SIGN UU..TIBETAN VOWEL SIGN VOCALIC LL
+0F81 ; Expands_On_NFKD # Mn TIBETAN VOWEL SIGN REVERSED II
+0F93 ; Expands_On_NFKD # Mn TIBETAN SUBJOINED LETTER GHA
+0F9D ; Expands_On_NFKD # Mn TIBETAN SUBJOINED LETTER DDHA
+0FA2 ; Expands_On_NFKD # Mn TIBETAN SUBJOINED LETTER DHA
+0FA7 ; Expands_On_NFKD # Mn TIBETAN SUBJOINED LETTER BHA
+0FAC ; Expands_On_NFKD # Mn TIBETAN SUBJOINED LETTER DZHA
+0FB9 ; Expands_On_NFKD # Mn TIBETAN SUBJOINED LETTER KSSA
+1026 ; Expands_On_NFKD # Lo MYANMAR LETTER UU
+1E00..1E9B ; Expands_On_NFKD # L& [156] LATIN CAPITAL LETTER A WITH RING BELOW..LATIN SMALL LETTER LONG S WITH DOT ABOVE
+1EA0..1EF9 ; Expands_On_NFKD # L& [90] LATIN CAPITAL LETTER A WITH DOT BELOW..LATIN SMALL LETTER Y WITH TILDE
+1F00..1F15 ; Expands_On_NFKD # L& [22] GREEK SMALL LETTER ALPHA WITH PSILI..GREEK SMALL LETTER EPSILON WITH DASIA AND OXIA
+1F18..1F1D ; Expands_On_NFKD # L& [6] GREEK CAPITAL LETTER EPSILON WITH PSILI..GREEK CAPITAL LETTER EPSILON WITH DASIA AND OXIA
+1F20..1F45 ; Expands_On_NFKD # L& [38] GREEK SMALL LETTER ETA WITH PSILI..GREEK SMALL LETTER OMICRON WITH DASIA AND OXIA
+1F48..1F4D ; Expands_On_NFKD # L& [6] GREEK CAPITAL LETTER OMICRON WITH PSILI..GREEK CAPITAL LETTER OMICRON WITH DASIA AND OXIA
+1F50..1F57 ; Expands_On_NFKD # L& [8] GREEK SMALL LETTER UPSILON WITH PSILI..GREEK SMALL LETTER UPSILON WITH DASIA AND PERISPOMENI
+1F59 ; Expands_On_NFKD # L& GREEK CAPITAL LETTER UPSILON WITH DASIA
+1F5B ; Expands_On_NFKD # L& GREEK CAPITAL LETTER UPSILON WITH DASIA AND VARIA
+1F5D ; Expands_On_NFKD # L& GREEK CAPITAL LETTER UPSILON WITH DASIA AND OXIA
+1F5F..1F7D ; Expands_On_NFKD # L& [31] GREEK CAPITAL LETTER UPSILON WITH DASIA AND PERISPOMENI..GREEK SMALL LETTER OMEGA WITH OXIA
+1F80..1FB4 ; Expands_On_NFKD # L& [53] GREEK SMALL LETTER ALPHA WITH PSILI AND YPOGEGRAMMENI..GREEK SMALL LETTER ALPHA WITH OXIA AND YPOGEGRAMMENI
+1FB6..1FBC ; Expands_On_NFKD # L& [7] GREEK SMALL LETTER ALPHA WITH PERISPOMENI..GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI
+1FBD ; Expands_On_NFKD # Sk GREEK KORONIS
+1FBF..1FC1 ; Expands_On_NFKD # Sk [3] GREEK PSILI..GREEK DIALYTIKA AND PERISPOMENI
+1FC2..1FC4 ; Expands_On_NFKD # L& [3] GREEK SMALL LETTER ETA WITH VARIA AND YPOGEGRAMMENI..GREEK SMALL LETTER ETA WITH OXIA AND YPOGEGRAMMENI
+1FC6..1FCC ; Expands_On_NFKD # L& [7] GREEK SMALL LETTER ETA WITH PERISPOMENI..GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI
+1FCD..1FCF ; Expands_On_NFKD # Sk [3] GREEK PSILI AND VARIA..GREEK PSILI AND PERISPOMENI
+1FD0..1FD3 ; Expands_On_NFKD # L& [4] GREEK SMALL LETTER IOTA WITH VRACHY..GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA
+1FD6..1FDB ; Expands_On_NFKD # L& [6] GREEK SMALL LETTER IOTA WITH PERISPOMENI..GREEK CAPITAL LETTER IOTA WITH OXIA
+1FDD..1FDF ; Expands_On_NFKD # Sk [3] GREEK DASIA AND VARIA..GREEK DASIA AND PERISPOMENI
+1FE0..1FEC ; Expands_On_NFKD # L& [13] GREEK SMALL LETTER UPSILON WITH VRACHY..GREEK CAPITAL LETTER RHO WITH DASIA
+1FED..1FEE ; Expands_On_NFKD # Sk [2] GREEK DIALYTIKA AND VARIA..GREEK DIALYTIKA AND OXIA
+1FF2..1FF4 ; Expands_On_NFKD # L& [3] GREEK SMALL LETTER OMEGA WITH VARIA AND YPOGEGRAMMENI..GREEK SMALL LETTER OMEGA WITH OXIA AND YPOGEGRAMMENI
+1FF6..1FFC ; Expands_On_NFKD # L& [7] GREEK SMALL LETTER OMEGA WITH PERISPOMENI..GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI
+1FFD..1FFE ; Expands_On_NFKD # Sk [2] GREEK OXIA..GREEK DASIA
+2017 ; Expands_On_NFKD # Po DOUBLE LOW LINE
+2025..2026 ; Expands_On_NFKD # Po [2] TWO DOT LEADER..HORIZONTAL ELLIPSIS
+2033..2034 ; Expands_On_NFKD # Po [2] DOUBLE PRIME..TRIPLE PRIME
+2036..2037 ; Expands_On_NFKD # Po [2] REVERSED DOUBLE PRIME..REVERSED TRIPLE PRIME
+203C ; Expands_On_NFKD # Po DOUBLE EXCLAMATION MARK
+203E ; Expands_On_NFKD # Po OVERLINE
+2047..2049 ; Expands_On_NFKD # Po [3] DOUBLE QUESTION MARK..EXCLAMATION QUESTION MARK
+2057 ; Expands_On_NFKD # Po QUADRUPLE PRIME
+20A8 ; Expands_On_NFKD # Sc RUPEE SIGN
+2100..2101 ; Expands_On_NFKD # So [2] ACCOUNT OF..ADDRESSED TO THE SUBJECT
+2103 ; Expands_On_NFKD # So DEGREE CELSIUS
+2105..2106 ; Expands_On_NFKD # So [2] CARE OF..CADA UNA
+2109 ; Expands_On_NFKD # So DEGREE FAHRENHEIT
+2116 ; Expands_On_NFKD # So NUMERO SIGN
+2120..2122 ; Expands_On_NFKD # So [3] SERVICE MARK..TRADE MARK SIGN
+212B ; Expands_On_NFKD # L& ANGSTROM SIGN
+213B ; Expands_On_NFKD # So FACSIMILE SIGN
+2153..215F ; Expands_On_NFKD # No [13] VULGAR FRACTION ONE THIRD..FRACTION NUMERATOR ONE
+2161..2163 ; Expands_On_NFKD # Nl [3] ROMAN NUMERAL TWO..ROMAN NUMERAL FOUR
+2165..2168 ; Expands_On_NFKD # Nl [4] ROMAN NUMERAL SIX..ROMAN NUMERAL NINE
+216A..216B ; Expands_On_NFKD # Nl [2] ROMAN NUMERAL ELEVEN..ROMAN NUMERAL TWELVE
+2171..2173 ; Expands_On_NFKD # Nl [3] SMALL ROMAN NUMERAL TWO..SMALL ROMAN NUMERAL FOUR
+2175..2178 ; Expands_On_NFKD # Nl [4] SMALL ROMAN NUMERAL SIX..SMALL ROMAN NUMERAL NINE
+217A..217B ; Expands_On_NFKD # Nl [2] SMALL ROMAN NUMERAL ELEVEN..SMALL ROMAN NUMERAL TWELVE
+219A..219B ; Expands_On_NFKD # Sm [2] LEFTWARDS ARROW WITH STROKE..RIGHTWARDS ARROW WITH STROKE
+21AE ; Expands_On_NFKD # Sm LEFT RIGHT ARROW WITH STROKE
+21CD ; Expands_On_NFKD # So LEFTWARDS DOUBLE ARROW WITH STROKE
+21CE..21CF ; Expands_On_NFKD # Sm [2] LEFT RIGHT DOUBLE ARROW WITH STROKE..RIGHTWARDS DOUBLE ARROW WITH STROKE
+2204 ; Expands_On_NFKD # Sm THERE DOES NOT EXIST
+2209 ; Expands_On_NFKD # Sm NOT AN ELEMENT OF
+220C ; Expands_On_NFKD # Sm DOES NOT CONTAIN AS MEMBER
+2224 ; Expands_On_NFKD # Sm DOES NOT DIVIDE
+2226 ; Expands_On_NFKD # Sm NOT PARALLEL TO
+222C..222D ; Expands_On_NFKD # Sm [2] DOUBLE INTEGRAL..TRIPLE INTEGRAL
+222F..2230 ; Expands_On_NFKD # Sm [2] SURFACE INTEGRAL..VOLUME INTEGRAL
+2241 ; Expands_On_NFKD # Sm NOT TILDE
+2244 ; Expands_On_NFKD # Sm NOT ASYMPTOTICALLY EQUAL TO
+2247 ; Expands_On_NFKD # Sm NEITHER APPROXIMATELY NOR ACTUALLY EQUAL TO
+2249 ; Expands_On_NFKD # Sm NOT ALMOST EQUAL TO
+2260 ; Expands_On_NFKD # Sm NOT EQUAL TO
+2262 ; Expands_On_NFKD # Sm NOT IDENTICAL TO
+226D..2271 ; Expands_On_NFKD # Sm [5] NOT EQUIVALENT TO..NEITHER GREATER-THAN NOR EQUAL TO
+2274..2275 ; Expands_On_NFKD # Sm [2] NEITHER LESS-THAN NOR EQUIVALENT TO..NEITHER GREATER-THAN NOR EQUIVALENT TO
+2278..2279 ; Expands_On_NFKD # Sm [2] NEITHER LESS-THAN NOR GREATER-THAN..NEITHER GREATER-THAN NOR LESS-THAN
+2280..2281 ; Expands_On_NFKD # Sm [2] DOES NOT PRECEDE..DOES NOT SUCCEED
+2284..2285 ; Expands_On_NFKD # Sm [2] NOT A SUBSET OF..NOT A SUPERSET OF
+2288..2289 ; Expands_On_NFKD # Sm [2] NEITHER A SUBSET OF NOR EQUAL TO..NEITHER A SUPERSET OF NOR EQUAL TO
+22AC..22AF ; Expands_On_NFKD # Sm [4] DOES NOT PROVE..NEGATED DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE
+22E0..22E3 ; Expands_On_NFKD # Sm [4] DOES NOT PRECEDE OR EQUAL..NOT SQUARE ORIGINAL OF OR EQUAL TO
+22EA..22ED ; Expands_On_NFKD # Sm [4] NOT NORMAL SUBGROUP OF..DOES NOT CONTAIN AS NORMAL SUBGROUP OR EQUAL
+2469..249B ; Expands_On_NFKD # No [51] CIRCLED NUMBER TEN..NUMBER TWENTY FULL STOP
+249C..24B5 ; Expands_On_NFKD # So [26] PARENTHESIZED LATIN SMALL LETTER A..PARENTHESIZED LATIN SMALL LETTER Z
+2A0C ; Expands_On_NFKD # Sm QUADRUPLE INTEGRAL OPERATOR
+2A74..2A76 ; Expands_On_NFKD # Sm [3] DOUBLE COLON EQUAL..THREE CONSECUTIVE EQUALS SIGNS
+2ADC ; Expands_On_NFKD # Sm FORKING
+304C ; Expands_On_NFKD # Lo HIRAGANA LETTER GA
+304E ; Expands_On_NFKD # Lo HIRAGANA LETTER GI
+3050 ; Expands_On_NFKD # Lo HIRAGANA LETTER GU
+3052 ; Expands_On_NFKD # Lo HIRAGANA LETTER GE
+3054 ; Expands_On_NFKD # Lo HIRAGANA LETTER GO
+3056 ; Expands_On_NFKD # Lo HIRAGANA LETTER ZA
+3058 ; Expands_On_NFKD # Lo HIRAGANA LETTER ZI
+305A ; Expands_On_NFKD # Lo HIRAGANA LETTER ZU
+305C ; Expands_On_NFKD # Lo HIRAGANA LETTER ZE
+305E ; Expands_On_NFKD # Lo HIRAGANA LETTER ZO
+3060 ; Expands_On_NFKD # Lo HIRAGANA LETTER DA
+3062 ; Expands_On_NFKD # Lo HIRAGANA LETTER DI
+3065 ; Expands_On_NFKD # Lo HIRAGANA LETTER DU
+3067 ; Expands_On_NFKD # Lo HIRAGANA LETTER DE
+3069 ; Expands_On_NFKD # Lo HIRAGANA LETTER DO
+3070..3071 ; Expands_On_NFKD # Lo [2] HIRAGANA LETTER BA..HIRAGANA LETTER PA
+3073..3074 ; Expands_On_NFKD # Lo [2] HIRAGANA LETTER BI..HIRAGANA LETTER PI
+3076..3077 ; Expands_On_NFKD # Lo [2] HIRAGANA LETTER BU..HIRAGANA LETTER PU
+3079..307A ; Expands_On_NFKD # Lo [2] HIRAGANA LETTER BE..HIRAGANA LETTER PE
+307C..307D ; Expands_On_NFKD # Lo [2] HIRAGANA LETTER BO..HIRAGANA LETTER PO
+3094 ; Expands_On_NFKD # Lo HIRAGANA LETTER VU
+309B..309C ; Expands_On_NFKD # Sk [2] KATAKANA-HIRAGANA VOICED SOUND MARK..KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK
+309E ; Expands_On_NFKD # Lm HIRAGANA VOICED ITERATION MARK
+309F ; Expands_On_NFKD # Lo HIRAGANA DIGRAPH YORI
+30AC ; Expands_On_NFKD # Lo KATAKANA LETTER GA
+30AE ; Expands_On_NFKD # Lo KATAKANA LETTER GI
+30B0 ; Expands_On_NFKD # Lo KATAKANA LETTER GU
+30B2 ; Expands_On_NFKD # Lo KATAKANA LETTER GE
+30B4 ; Expands_On_NFKD # Lo KATAKANA LETTER GO
+30B6 ; Expands_On_NFKD # Lo KATAKANA LETTER ZA
+30B8 ; Expands_On_NFKD # Lo KATAKANA LETTER ZI
+30BA ; Expands_On_NFKD # Lo KATAKANA LETTER ZU
+30BC ; Expands_On_NFKD # Lo KATAKANA LETTER ZE
+30BE ; Expands_On_NFKD # Lo KATAKANA LETTER ZO
+30C0 ; Expands_On_NFKD # Lo KATAKANA LETTER DA
+30C2 ; Expands_On_NFKD # Lo KATAKANA LETTER DI
+30C5 ; Expands_On_NFKD # Lo KATAKANA LETTER DU
+30C7 ; Expands_On_NFKD # Lo KATAKANA LETTER DE
+30C9 ; Expands_On_NFKD # Lo KATAKANA LETTER DO
+30D0..30D1 ; Expands_On_NFKD # Lo [2] KATAKANA LETTER BA..KATAKANA LETTER PA
+30D3..30D4 ; Expands_On_NFKD # Lo [2] KATAKANA LETTER BI..KATAKANA LETTER PI
+30D6..30D7 ; Expands_On_NFKD # Lo [2] KATAKANA LETTER BU..KATAKANA LETTER PU
+30D9..30DA ; Expands_On_NFKD # Lo [2] KATAKANA LETTER BE..KATAKANA LETTER PE
+30DC..30DD ; Expands_On_NFKD # Lo [2] KATAKANA LETTER BO..KATAKANA LETTER PO
+30F4 ; Expands_On_NFKD # Lo KATAKANA LETTER VU
+30F7..30FA ; Expands_On_NFKD # Lo [4] KATAKANA LETTER VA..KATAKANA LETTER VO
+30FE ; Expands_On_NFKD # Lm KATAKANA VOICED ITERATION MARK
+30FF ; Expands_On_NFKD # Lo KATAKANA DIGRAPH KOTO
+3200..321E ; Expands_On_NFKD # So [31] PARENTHESIZED HANGUL KIYEOK..PARENTHESIZED KOREAN CHARACTER O HU
+3220..3229 ; Expands_On_NFKD # No [10] PARENTHESIZED IDEOGRAPH ONE..PARENTHESIZED IDEOGRAPH TEN
+322A..3243 ; Expands_On_NFKD # So [26] PARENTHESIZED IDEOGRAPH MOON..PARENTHESIZED IDEOGRAPH REACH
+3250 ; Expands_On_NFKD # So PARTNERSHIP SIGN
+3251..325F ; Expands_On_NFKD # No [15] CIRCLED NUMBER TWENTY ONE..CIRCLED NUMBER THIRTY FIVE
+326E..327D ; Expands_On_NFKD # So [16] CIRCLED HANGUL KIYEOK A..CIRCLED KOREAN CHARACTER JUEUI
+32B1..32BF ; Expands_On_NFKD # No [15] CIRCLED NUMBER THIRTY SIX..CIRCLED NUMBER FIFTY
+32C0..32CF ; Expands_On_NFKD # So [16] IDEOGRAPHIC TELEGRAPH SYMBOL FOR JANUARY..LIMITED LIABILITY SIGN
+3300..33FF ; Expands_On_NFKD # So [256] SQUARE APAATO..SQUARE GAL
+AC00..D7A3 ; Expands_On_NFKD # Lo [11172] HANGUL SYLLABLE GA..HANGUL SYLLABLE HIH
+FB00..FB06 ; Expands_On_NFKD # L& [7] LATIN SMALL LIGATURE FF..LATIN SMALL LIGATURE ST
+FB13..FB17 ; Expands_On_NFKD # L& [5] ARMENIAN SMALL LIGATURE MEN NOW..ARMENIAN SMALL LIGATURE MEN XEH
+FB1D ; Expands_On_NFKD # Lo HEBREW LETTER YOD WITH HIRIQ
+FB1F ; Expands_On_NFKD # Lo HEBREW LIGATURE YIDDISH YOD YOD PATAH
+FB2A..FB36 ; Expands_On_NFKD # Lo [13] HEBREW LETTER SHIN WITH SHIN DOT..HEBREW LETTER ZAYIN WITH DAGESH
+FB38..FB3C ; Expands_On_NFKD # Lo [5] HEBREW LETTER TET WITH DAGESH..HEBREW LETTER LAMED WITH DAGESH
+FB3E ; Expands_On_NFKD # Lo HEBREW LETTER MEM WITH DAGESH
+FB40..FB41 ; Expands_On_NFKD # Lo [2] HEBREW LETTER NUN WITH DAGESH..HEBREW LETTER SAMEKH WITH DAGESH
+FB43..FB44 ; Expands_On_NFKD # Lo [2] HEBREW LETTER FINAL PE WITH DAGESH..HEBREW LETTER PE WITH DAGESH
+FB46..FB4F ; Expands_On_NFKD # Lo [10] HEBREW LETTER TSADI WITH DAGESH..HEBREW LIGATURE ALEF LAMED
+FBA4..FBA5 ; Expands_On_NFKD # Lo [2] ARABIC LETTER HEH WITH YEH ABOVE ISOLATED FORM..ARABIC LETTER HEH WITH YEH ABOVE FINAL FORM
+FBB0..FBB1 ; Expands_On_NFKD # Lo [2] ARABIC LETTER YEH BARREE WITH HAMZA ABOVE ISOLATED FORM..ARABIC LETTER YEH BARREE WITH HAMZA ABOVE FINAL FORM
+FBDD ; Expands_On_NFKD # Lo ARABIC LETTER U WITH HAMZA ABOVE ISOLATED FORM
+FBEA..FBFB ; Expands_On_NFKD # Lo [18] ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF ISOLATED FORM..ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH HAMZA ABOVE WITH ALEF MAKSURA INITIAL FORM
+FC00..FD3D ; Expands_On_NFKD # Lo [318] ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH JEEM ISOLATED FORM..ARABIC LIGATURE ALEF WITH FATHATAN ISOLATED FORM
+FD50..FD8F ; Expands_On_NFKD # Lo [64] ARABIC LIGATURE TEH WITH JEEM WITH MEEM INITIAL FORM..ARABIC LIGATURE MEEM WITH KHAH WITH MEEM INITIAL FORM
+FD92..FDC7 ; Expands_On_NFKD # Lo [54] ARABIC LIGATURE MEEM WITH JEEM WITH KHAH INITIAL FORM..ARABIC LIGATURE NOON WITH JEEM WITH YEH FINAL FORM
+FDF0..FDFB ; Expands_On_NFKD # Lo [12] ARABIC LIGATURE SALLA USED AS KORANIC STOP SIGN ISOLATED FORM..ARABIC LIGATURE JALLAJALALOUHOU
+FDFC ; Expands_On_NFKD # Sc RIAL SIGN
+FE30 ; Expands_On_NFKD # Po PRESENTATION FORM FOR VERTICAL TWO DOT LEADER
+FE49..FE4C ; Expands_On_NFKD # Po [4] DASHED OVERLINE..DOUBLE WAVY OVERLINE
+FE70..FE72 ; Expands_On_NFKD # Lo [3] ARABIC FATHATAN ISOLATED FORM..ARABIC DAMMATAN ISOLATED FORM
+FE74 ; Expands_On_NFKD # Lo ARABIC KASRATAN ISOLATED FORM
+FE76..FE7F ; Expands_On_NFKD # Lo [10] ARABIC FATHA ISOLATED FORM..ARABIC SUKUN MEDIAL FORM
+FE81..FE8C ; Expands_On_NFKD # Lo [12] ARABIC LETTER ALEF WITH MADDA ABOVE ISOLATED FORM..ARABIC LETTER YEH WITH HAMZA ABOVE MEDIAL FORM
+FEF5..FEFC ; Expands_On_NFKD # Lo [8] ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE ISOLATED FORM..ARABIC LIGATURE LAM WITH ALEF FINAL FORM
+FFE3 ; Expands_On_NFKD # Sk FULLWIDTH MACRON
+1D15E..1D164 ; Expands_On_NFKD # So [7] MUSICAL SYMBOL HALF NOTE..MUSICAL SYMBOL ONE HUNDRED TWENTY-EIGHTH NOTE
+1D1BB..1D1C0 ; Expands_On_NFKD # So [6] MUSICAL SYMBOL MINIMA..MUSICAL SYMBOL FUSA BLACK
+
+# Total code points: 13297
+
+# ================================================
+
+# Derived Property: Expands_On_NFKC
+# Generated according to UAX #15.
+# Characters whose normalized length is not one.
+# WARNING: Normalization of STRINGS must use the algorithm in UAX #15 because characters may interact.
+# The length of a normalized string is not necessarily the sum of the lengths of the normalized characters!
+
+00A8 ; Expands_On_NFKC # Sk DIAERESIS
+00AF ; Expands_On_NFKC # Sk MACRON
+00B4 ; Expands_On_NFKC # Sk ACUTE ACCENT
+00B8 ; Expands_On_NFKC # Sk CEDILLA
+00BC..00BE ; Expands_On_NFKC # No [3] VULGAR FRACTION ONE QUARTER..VULGAR FRACTION THREE QUARTERS
+0132..0133 ; Expands_On_NFKC # L& [2] LATIN CAPITAL LIGATURE IJ..LATIN SMALL LIGATURE IJ
+013F..0140 ; Expands_On_NFKC # L& [2] LATIN CAPITAL LETTER L WITH MIDDLE DOT..LATIN SMALL LETTER L WITH MIDDLE DOT
+0149 ; Expands_On_NFKC # L& LATIN SMALL LETTER N PRECEDED BY APOSTROPHE
+01C4..01CC ; Expands_On_NFKC # L& [9] LATIN CAPITAL LETTER DZ WITH CARON..LATIN SMALL LETTER NJ
+01F1..01F3 ; Expands_On_NFKC # L& [3] LATIN CAPITAL LETTER DZ..LATIN SMALL LETTER DZ
+02D8..02DD ; Expands_On_NFKC # Sk [6] BREVE..DOUBLE ACUTE ACCENT
+0344 ; Expands_On_NFKC # Mn COMBINING GREEK DIALYTIKA TONOS
+037A ; Expands_On_NFKC # Lm GREEK YPOGEGRAMMENI
+0384..0385 ; Expands_On_NFKC # Sk [2] GREEK TONOS..GREEK DIALYTIKA TONOS
+0587 ; Expands_On_NFKC # L& ARMENIAN SMALL LIGATURE ECH YIWN
+0675..0678 ; Expands_On_NFKC # Lo [4] ARABIC LETTER HIGH HAMZA ALEF..ARABIC LETTER HIGH HAMZA YEH
+0958..095F ; Expands_On_NFKC # Lo [8] DEVANAGARI LETTER QA..DEVANAGARI LETTER YYA
+09DC..09DD ; Expands_On_NFKC # Lo [2] BENGALI LETTER RRA..BENGALI LETTER RHA
+09DF ; Expands_On_NFKC # Lo BENGALI LETTER YYA
+0A33 ; Expands_On_NFKC # Lo GURMUKHI LETTER LLA
+0A36 ; Expands_On_NFKC # Lo GURMUKHI LETTER SHA
+0A59..0A5B ; Expands_On_NFKC # Lo [3] GURMUKHI LETTER KHHA..GURMUKHI LETTER ZA
+0A5E ; Expands_On_NFKC # Lo GURMUKHI LETTER FA
+0B5C..0B5D ; Expands_On_NFKC # Lo [2] ORIYA LETTER RRA..ORIYA LETTER RHA
+0E33 ; Expands_On_NFKC # Lo THAI CHARACTER SARA AM
+0EB3 ; Expands_On_NFKC # Lo LAO VOWEL SIGN AM
+0EDC..0EDD ; Expands_On_NFKC # Lo [2] LAO HO NO..LAO HO MO
+0F43 ; Expands_On_NFKC # Lo TIBETAN LETTER GHA
+0F4D ; Expands_On_NFKC # Lo TIBETAN LETTER DDHA
+0F52 ; Expands_On_NFKC # Lo TIBETAN LETTER DHA
+0F57 ; Expands_On_NFKC # Lo TIBETAN LETTER BHA
+0F5C ; Expands_On_NFKC # Lo TIBETAN LETTER DZHA
+0F69 ; Expands_On_NFKC # Lo TIBETAN LETTER KSSA
+0F73 ; Expands_On_NFKC # Mn TIBETAN VOWEL SIGN II
+0F75..0F79 ; Expands_On_NFKC # Mn [5] TIBETAN VOWEL SIGN UU..TIBETAN VOWEL SIGN VOCALIC LL
+0F81 ; Expands_On_NFKC # Mn TIBETAN VOWEL SIGN REVERSED II
+0F93 ; Expands_On_NFKC # Mn TIBETAN SUBJOINED LETTER GHA
+0F9D ; Expands_On_NFKC # Mn TIBETAN SUBJOINED LETTER DDHA
+0FA2 ; Expands_On_NFKC # Mn TIBETAN SUBJOINED LETTER DHA
+0FA7 ; Expands_On_NFKC # Mn TIBETAN SUBJOINED LETTER BHA
+0FAC ; Expands_On_NFKC # Mn TIBETAN SUBJOINED LETTER DZHA
+0FB9 ; Expands_On_NFKC # Mn TIBETAN SUBJOINED LETTER KSSA
+1E9A ; Expands_On_NFKC # L& LATIN SMALL LETTER A WITH RIGHT HALF RING
+1FBD ; Expands_On_NFKC # Sk GREEK KORONIS
+1FBF..1FC1 ; Expands_On_NFKC # Sk [3] GREEK PSILI..GREEK DIALYTIKA AND PERISPOMENI
+1FCD..1FCF ; Expands_On_NFKC # Sk [3] GREEK PSILI AND VARIA..GREEK PSILI AND PERISPOMENI
+1FDD..1FDF ; Expands_On_NFKC # Sk [3] GREEK DASIA AND VARIA..GREEK DASIA AND PERISPOMENI
+1FED..1FEE ; Expands_On_NFKC # Sk [2] GREEK DIALYTIKA AND VARIA..GREEK DIALYTIKA AND OXIA
+1FFD..1FFE ; Expands_On_NFKC # Sk [2] GREEK OXIA..GREEK DASIA
+2017 ; Expands_On_NFKC # Po DOUBLE LOW LINE
+2025..2026 ; Expands_On_NFKC # Po [2] TWO DOT LEADER..HORIZONTAL ELLIPSIS
+2033..2034 ; Expands_On_NFKC # Po [2] DOUBLE PRIME..TRIPLE PRIME
+2036..2037 ; Expands_On_NFKC # Po [2] REVERSED DOUBLE PRIME..REVERSED TRIPLE PRIME
+203C ; Expands_On_NFKC # Po DOUBLE EXCLAMATION MARK
+203E ; Expands_On_NFKC # Po OVERLINE
+2047..2049 ; Expands_On_NFKC # Po [3] DOUBLE QUESTION MARK..EXCLAMATION QUESTION MARK
+2057 ; Expands_On_NFKC # Po QUADRUPLE PRIME
+20A8 ; Expands_On_NFKC # Sc RUPEE SIGN
+2100..2101 ; Expands_On_NFKC # So [2] ACCOUNT OF..ADDRESSED TO THE SUBJECT
+2103 ; Expands_On_NFKC # So DEGREE CELSIUS
+2105..2106 ; Expands_On_NFKC # So [2] CARE OF..CADA UNA
+2109 ; Expands_On_NFKC # So DEGREE FAHRENHEIT
+2116 ; Expands_On_NFKC # So NUMERO SIGN
+2120..2122 ; Expands_On_NFKC # So [3] SERVICE MARK..TRADE MARK SIGN
+213B ; Expands_On_NFKC # So FACSIMILE SIGN
+2153..215F ; Expands_On_NFKC # No [13] VULGAR FRACTION ONE THIRD..FRACTION NUMERATOR ONE
+2161..2163 ; Expands_On_NFKC # Nl [3] ROMAN NUMERAL TWO..ROMAN NUMERAL FOUR
+2165..2168 ; Expands_On_NFKC # Nl [4] ROMAN NUMERAL SIX..ROMAN NUMERAL NINE
+216A..216B ; Expands_On_NFKC # Nl [2] ROMAN NUMERAL ELEVEN..ROMAN NUMERAL TWELVE
+2171..2173 ; Expands_On_NFKC # Nl [3] SMALL ROMAN NUMERAL TWO..SMALL ROMAN NUMERAL FOUR
+2175..2178 ; Expands_On_NFKC # Nl [4] SMALL ROMAN NUMERAL SIX..SMALL ROMAN NUMERAL NINE
+217A..217B ; Expands_On_NFKC # Nl [2] SMALL ROMAN NUMERAL ELEVEN..SMALL ROMAN NUMERAL TWELVE
+222C..222D ; Expands_On_NFKC # Sm [2] DOUBLE INTEGRAL..TRIPLE INTEGRAL
+222F..2230 ; Expands_On_NFKC # Sm [2] SURFACE INTEGRAL..VOLUME INTEGRAL
+2469..249B ; Expands_On_NFKC # No [51] CIRCLED NUMBER TEN..NUMBER TWENTY FULL STOP
+249C..24B5 ; Expands_On_NFKC # So [26] PARENTHESIZED LATIN SMALL LETTER A..PARENTHESIZED LATIN SMALL LETTER Z
+2A0C ; Expands_On_NFKC # Sm QUADRUPLE INTEGRAL OPERATOR
+2A74..2A76 ; Expands_On_NFKC # Sm [3] DOUBLE COLON EQUAL..THREE CONSECUTIVE EQUALS SIGNS
+2ADC ; Expands_On_NFKC # Sm FORKING
+309B..309C ; Expands_On_NFKC # Sk [2] KATAKANA-HIRAGANA VOICED SOUND MARK..KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK
+309F ; Expands_On_NFKC # Lo HIRAGANA DIGRAPH YORI
+30FF ; Expands_On_NFKC # Lo KATAKANA DIGRAPH KOTO
+3200..321E ; Expands_On_NFKC # So [31] PARENTHESIZED HANGUL KIYEOK..PARENTHESIZED KOREAN CHARACTER O HU
+3220..3229 ; Expands_On_NFKC # No [10] PARENTHESIZED IDEOGRAPH ONE..PARENTHESIZED IDEOGRAPH TEN
+322A..3243 ; Expands_On_NFKC # So [26] PARENTHESIZED IDEOGRAPH MOON..PARENTHESIZED IDEOGRAPH REACH
+3250 ; Expands_On_NFKC # So PARTNERSHIP SIGN
+3251..325F ; Expands_On_NFKC # No [15] CIRCLED NUMBER TWENTY ONE..CIRCLED NUMBER THIRTY FIVE
+327C..327D ; Expands_On_NFKC # So [2] CIRCLED KOREAN CHARACTER CHAMKO..CIRCLED KOREAN CHARACTER JUEUI
+32B1..32BF ; Expands_On_NFKC # No [15] CIRCLED NUMBER THIRTY SIX..CIRCLED NUMBER FIFTY
+32C0..32CF ; Expands_On_NFKC # So [16] IDEOGRAPHIC TELEGRAPH SYMBOL FOR JANUARY..LIMITED LIABILITY SIGN
+3300..33FF ; Expands_On_NFKC # So [256] SQUARE APAATO..SQUARE GAL
+FB00..FB06 ; Expands_On_NFKC # L& [7] LATIN SMALL LIGATURE FF..LATIN SMALL LIGATURE ST
+FB13..FB17 ; Expands_On_NFKC # L& [5] ARMENIAN SMALL LIGATURE MEN NOW..ARMENIAN SMALL LIGATURE MEN XEH
+FB1D ; Expands_On_NFKC # Lo HEBREW LETTER YOD WITH HIRIQ
+FB1F ; Expands_On_NFKC # Lo HEBREW LIGATURE YIDDISH YOD YOD PATAH
+FB2A..FB36 ; Expands_On_NFKC # Lo [13] HEBREW LETTER SHIN WITH SHIN DOT..HEBREW LETTER ZAYIN WITH DAGESH
+FB38..FB3C ; Expands_On_NFKC # Lo [5] HEBREW LETTER TET WITH DAGESH..HEBREW LETTER LAMED WITH DAGESH
+FB3E ; Expands_On_NFKC # Lo HEBREW LETTER MEM WITH DAGESH
+FB40..FB41 ; Expands_On_NFKC # Lo [2] HEBREW LETTER NUN WITH DAGESH..HEBREW LETTER SAMEKH WITH DAGESH
+FB43..FB44 ; Expands_On_NFKC # Lo [2] HEBREW LETTER FINAL PE WITH DAGESH..HEBREW LETTER PE WITH DAGESH
+FB46..FB4F ; Expands_On_NFKC # Lo [10] HEBREW LETTER TSADI WITH DAGESH..HEBREW LIGATURE ALEF LAMED
+FBDD ; Expands_On_NFKC # Lo ARABIC LETTER U WITH HAMZA ABOVE ISOLATED FORM
+FBEA..FBFB ; Expands_On_NFKC # Lo [18] ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF ISOLATED FORM..ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH HAMZA ABOVE WITH ALEF MAKSURA INITIAL FORM
+FC00..FD3D ; Expands_On_NFKC # Lo [318] ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH JEEM ISOLATED FORM..ARABIC LIGATURE ALEF WITH FATHATAN ISOLATED FORM
+FD50..FD8F ; Expands_On_NFKC # Lo [64] ARABIC LIGATURE TEH WITH JEEM WITH MEEM INITIAL FORM..ARABIC LIGATURE MEEM WITH KHAH WITH MEEM INITIAL FORM
+FD92..FDC7 ; Expands_On_NFKC # Lo [54] ARABIC LIGATURE MEEM WITH JEEM WITH KHAH INITIAL FORM..ARABIC LIGATURE NOON WITH JEEM WITH YEH FINAL FORM
+FDF0..FDFB ; Expands_On_NFKC # Lo [12] ARABIC LIGATURE SALLA USED AS KORANIC STOP SIGN ISOLATED FORM..ARABIC LIGATURE JALLAJALALOUHOU
+FDFC ; Expands_On_NFKC # Sc RIAL SIGN
+FE30 ; Expands_On_NFKC # Po PRESENTATION FORM FOR VERTICAL TWO DOT LEADER
+FE49..FE4C ; Expands_On_NFKC # Po [4] DASHED OVERLINE..DOUBLE WAVY OVERLINE
+FE70..FE72 ; Expands_On_NFKC # Lo [3] ARABIC FATHATAN ISOLATED FORM..ARABIC DAMMATAN ISOLATED FORM
+FE74 ; Expands_On_NFKC # Lo ARABIC KASRATAN ISOLATED FORM
+FE76..FE7F ; Expands_On_NFKC # Lo [10] ARABIC FATHA ISOLATED FORM..ARABIC SUKUN MEDIAL FORM
+FEF5..FEFC ; Expands_On_NFKC # Lo [8] ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE ISOLATED FORM..ARABIC LIGATURE LAM WITH ALEF FINAL FORM
+FFE3 ; Expands_On_NFKC # Sk FULLWIDTH MACRON
+1D15E..1D164 ; Expands_On_NFKC # So [7] MUSICAL SYMBOL HALF NOTE..MUSICAL SYMBOL ONE HUNDRED TWENTY-EIGHTH NOTE
+1D1BB..1D1C0 ; Expands_On_NFKC # So [6] MUSICAL SYMBOL MINIMA..MUSICAL SYMBOL FUSA BLACK
+
+# Total code points: 1170
diff --git a/source4/heimdal/lib/wind/NormalizationCorrections.txt b/source4/heimdal/lib/wind/NormalizationCorrections.txt
new file mode 100644
index 0000000000..d76c150d2e
--- /dev/null
+++ b/source4/heimdal/lib/wind/NormalizationCorrections.txt
@@ -0,0 +1,43 @@
+# NormalizationCorrections-4.0.0.txt
+#
+# This file is a normative contributory data file in the
+# Unicode Character Database.
+#
+# The normalization stabilization policy of the Unicode
+# Consortium ordinarily precludes any change to the decomposition
+# for any character, once established in a relevant version
+# of the UnicodeData.txt data file. However, under certain
+# exceptional (and rare) conditions, an error in a decomposition
+# mapping may be discovered that is truly just an unintended
+# typo in the data, and not a matter of dubious interpretation.
+#
+# Whenever such an error may be found, and if it meets the
+# requirements for possible exceptions to normalization
+# stability, the correction is entered in this data file,
+# so that any implementation depending on absolute stability
+# of normalization, *including* any errors in the data, can
+# safely reconstruct the exact state of the data tables at
+# any given version of Unicode.
+#
+# Currently this list has exactly six entries in it, one for the
+# typo found and corrected in Corrigendum #3, and five for
+# the typos and misidentifications found and corrected in
+# Corrigendum #4. All efforts
+# will be made to keep the entries limited to just those fixes.
+#
+# Interpretation of the fields:
+# Field 1: Unicode code point
+# Field 2: Original (erroneous) decomposition
+# Field 3: Corrected decomposition
+# Field 4: Version of Unicode for which the correction was
+# entered into UnicodeData.txt, in n.n.n format.
+# Comment: Indicates the Unicode Corrigendum which documents
+# the correction
+#
+#
+F951;96FB;964B;3.2.0 # Corrigendum 3
+2F868;2136A;36FC;4.0.0 # Corrigendum 4
+2F874;5F33;5F53;4.0.0 # Corrigendum 4
+2F91F;43AB;243AB;4.0.0 # Corrigendum 4
+2F95F;7AAE;7AEE;4.0.0 # Corrigendum 4
+2F9BF;4D57;45D7;4.0.0 # Corrigendum 4
diff --git a/source4/heimdal/lib/wind/NormalizationTest.txt b/source4/heimdal/lib/wind/NormalizationTest.txt
new file mode 100644
index 0000000000..afbb369df2
--- /dev/null
+++ b/source4/heimdal/lib/wind/NormalizationTest.txt
@@ -0,0 +1,17166 @@
+# NormalizationTest-4.0.0.txt
+# Date: 2003-02-25,23:12:31 GMT [MD]
+#
+# Normalization Test Suite
+# Format:
+#
+# Columns (c1, c2,...) are separated by semicolons
+# Comments are indicated with hash marks
+#
+# CONFORMANCE:
+# 1. The following invariants must be true for all conformant implementations
+#
+# NFC
+# c2 == NFC(c1) == NFC(c2) == NFC(c3)
+# c4 == NFC(c4) == NFC(c5)
+#
+# NFD
+# c3 == NFD(c1) == NFD(c2) == NFD(c3)
+# c5 == NFD(c4) == NFD(c5)
+#
+# NFKC
+# c4 == NFKC(c1) == NFKC(c2) == NFKC(c3) == NFKC(c4) == NFKC(c5)
+#
+# NFKD
+# c5 == NFKD(c1) == NFKD(c2) == NFKD(c3) == NFKD(c4) == NFKD(c5)
+#
+# 2. For every code point X assigned in this version of Unicode that is not specifically
+# listed in Part 1, the following invariants must be true for all conformant
+# implementations:
+#
+# X == NFC(X) == NFD(X) == NFKC(X) == NFKD(X)
+#
+@Part0 # Specific cases
+#
+1E0A;1E0A;0044 0307;1E0A;0044 0307; # (Ḋ; Ḋ; D◌̇; Ḋ; D◌̇; ) LATIN CAPITAL LETTER D WITH DOT ABOVE
+1E0C;1E0C;0044 0323;1E0C;0044 0323; # (Ḍ; Ḍ; D◌̣; Ḍ; D◌̣; ) LATIN CAPITAL LETTER D WITH DOT BELOW
+1E0A 0323;1E0C 0307;0044 0323 0307;1E0C 0307;0044 0323 0307; # (Ḋ◌̣; Ḍ◌̇; D◌̣◌̇; Ḍ◌̇; D◌̣◌̇; ) LATIN CAPITAL LETTER D WITH DOT ABOVE, COMBINING DOT BELOW
+1E0C 0307;1E0C 0307;0044 0323 0307;1E0C 0307;0044 0323 0307; # (Ḍ◌̇; Ḍ◌̇; D◌̣◌̇; Ḍ◌̇; D◌̣◌̇; ) LATIN CAPITAL LETTER D WITH DOT BELOW, COMBINING DOT ABOVE
+0044 0307 0323;1E0C 0307;0044 0323 0307;1E0C 0307;0044 0323 0307; # (D◌̇◌̣; Ḍ◌̇; D◌̣◌̇; Ḍ◌̇; D◌̣◌̇; ) LATIN CAPITAL LETTER D, COMBINING DOT ABOVE, COMBINING DOT BELOW
+0044 0323 0307;1E0C 0307;0044 0323 0307;1E0C 0307;0044 0323 0307; # (D◌̣◌̇; Ḍ◌̇; D◌̣◌̇; Ḍ◌̇; D◌̣◌̇; ) LATIN CAPITAL LETTER D, COMBINING DOT BELOW, COMBINING DOT ABOVE
+1E0A 031B;1E0A 031B;0044 031B 0307;1E0A 031B;0044 031B 0307; # (Ḋ◌̛; Ḋ◌̛; D◌̛◌̇; Ḋ◌̛; D◌̛◌̇; ) LATIN CAPITAL LETTER D WITH DOT ABOVE, COMBINING HORN
+1E0C 031B;1E0C 031B;0044 031B 0323;1E0C 031B;0044 031B 0323; # (Ḍ◌̛; Ḍ◌̛; D◌̛◌̣; Ḍ◌̛; D◌̛◌̣; ) LATIN CAPITAL LETTER D WITH DOT BELOW, COMBINING HORN
+1E0A 031B 0323;1E0C 031B 0307;0044 031B 0323 0307;1E0C 031B 0307;0044 031B 0323 0307; # (Ḋ◌̛◌̣; Ḍ◌̛◌̇; D◌̛◌̣◌̇; Ḍ◌̛◌̇; D◌̛◌̣◌̇; ) LATIN CAPITAL LETTER D WITH DOT ABOVE, COMBINING HORN, COMBINING DOT BELOW
+1E0C 031B 0307;1E0C 031B 0307;0044 031B 0323 0307;1E0C 031B 0307;0044 031B 0323 0307; # (Ḍ◌̛◌̇; Ḍ◌̛◌̇; D◌̛◌̣◌̇; Ḍ◌̛◌̇; D◌̛◌̣◌̇; ) LATIN CAPITAL LETTER D WITH DOT BELOW, COMBINING HORN, COMBINING DOT ABOVE
+0044 031B 0307 0323;1E0C 031B 0307;0044 031B 0323 0307;1E0C 031B 0307;0044 031B 0323 0307; # (D◌̛◌̇◌̣; Ḍ◌̛◌̇; D◌̛◌̣◌̇; Ḍ◌̛◌̇; D◌̛◌̣◌̇; ) LATIN CAPITAL LETTER D, COMBINING HORN, COMBINING DOT ABOVE, COMBINING DOT BELOW
+0044 031B 0323 0307;1E0C 031B 0307;0044 031B 0323 0307;1E0C 031B 0307;0044 031B 0323 0307; # (D◌̛◌̣◌̇; Ḍ◌̛◌̇; D◌̛◌̣◌̇; Ḍ◌̛◌̇; D◌̛◌̣◌̇; ) LATIN CAPITAL LETTER D, COMBINING HORN, COMBINING DOT BELOW, COMBINING DOT ABOVE
+00C8;00C8;0045 0300;00C8;0045 0300; # (È; È; E◌̀; È; E◌̀; ) LATIN CAPITAL LETTER E WITH GRAVE
+0112;0112;0045 0304;0112;0045 0304; # (Ē; Ē; E◌̄; Ē; E◌̄; ) LATIN CAPITAL LETTER E WITH MACRON
+0045 0300;00C8;0045 0300;00C8;0045 0300; # (E◌̀; È; E◌̀; È; E◌̀; ) LATIN CAPITAL LETTER E, COMBINING GRAVE ACCENT
+0045 0304;0112;0045 0304;0112;0045 0304; # (E◌̄; Ē; E◌̄; Ē; E◌̄; ) LATIN CAPITAL LETTER E, COMBINING MACRON
+1E14;1E14;0045 0304 0300;1E14;0045 0304 0300; # (Ḕ; Ḕ; E◌̄◌̀; Ḕ; E◌̄◌̀; ) LATIN CAPITAL LETTER E WITH MACRON AND GRAVE
+0112 0300;1E14;0045 0304 0300;1E14;0045 0304 0300; # (Ē◌̀; Ḕ; E◌̄◌̀; Ḕ; E◌̄◌̀; ) LATIN CAPITAL LETTER E WITH MACRON, COMBINING GRAVE ACCENT
+1E14 0304;1E14 0304;0045 0304 0300 0304;1E14 0304;0045 0304 0300 0304; # (Ḕ◌̄; Ḕ◌̄; E◌̄◌̀◌̄; Ḕ◌̄; E◌̄◌̀◌̄; ) LATIN CAPITAL LETTER E WITH MACRON AND GRAVE, COMBINING MACRON
+0045 0304 0300;1E14;0045 0304 0300;1E14;0045 0304 0300; # (E◌̄◌̀; Ḕ; E◌̄◌̀; Ḕ; E◌̄◌̀; ) LATIN CAPITAL LETTER E, COMBINING MACRON, COMBINING GRAVE ACCENT
+0045 0300 0304;00C8 0304;0045 0300 0304;00C8 0304;0045 0300 0304; # (E◌̀◌̄; È◌̄; E◌̀◌̄; È◌̄; E◌̀◌̄; ) LATIN CAPITAL LETTER E, COMBINING GRAVE ACCENT, COMBINING MACRON
+05B8 05B9 05B1 0591 05C3 05B0 05AC 059F;05B1 05B8 05B9 0591 05C3 05B0 05AC 059F;05B1 05B8 05B9 0591 05C3 05B0 05AC 059F;05B1 05B8 05B9 0591 05C3 05B0 05AC 059F;05B1 05B8 05B9 0591 05C3 05B0 05AC 059F; # (◌ָ◌ֹ◌ֱ◌֑׃◌ְ◌֬◌֟; ◌ֱ◌ָ◌ֹ◌֑׃◌ְ◌֬◌֟; ◌ֱ◌ָ◌ֹ◌֑׃◌ְ◌֬◌֟; ◌ֱ◌ָ◌ֹ◌֑׃◌ְ◌֬◌֟; ◌ֱ◌ָ◌ֹ◌֑׃◌ְ◌֬◌֟; ) HEBREW POINT QAMATS, HEBREW POINT HOLAM, HEBREW POINT HATAF SEGOL, HEBREW ACCENT ETNAHTA, HEBREW PUNCTUATION SOF PASUQ, HEBREW POINT SHEVA, HEBREW ACCENT ILUY, HEBREW ACCENT QARNEY PARA
+0592 05B7 05BC 05A5 05B0 05C0 05C4 05AD;05B0 05B7 05BC 05A5 0592 05C0 05AD 05C4;05B0 05B7 05BC 05A5 0592 05C0 05AD 05C4;05B0 05B7 05BC 05A5 0592 05C0 05AD 05C4;05B0 05B7 05BC 05A5 0592 05C0 05AD 05C4; # (◌֒◌ַ◌ּ◌֥◌ְ׀◌ׄ◌֭; ◌ְ◌ַ◌ּ◌֥◌֒׀◌֭◌ׄ; ◌ְ◌ַ◌ּ◌֥◌֒׀◌֭◌ׄ; ◌ְ◌ַ◌ּ◌֥◌֒׀◌֭◌ׄ; ◌ְ◌ַ◌ּ◌֥◌֒׀◌֭◌ׄ; ) HEBREW ACCENT SEGOL, HEBREW POINT PATAH, HEBREW POINT DAGESH OR MAPIQ, HEBREW ACCENT MERKHA, HEBREW POINT SHEVA, HEBREW PUNCTUATION PASEQ, HEBREW MARK UPPER DOT, HEBREW ACCENT DEHI
+#
+@Part1 # Character by character test
+# All characters not explicitly occurring in c1 of Part 1 have identical NFC, D, KC, KD forms.
+#
+00A0;00A0;00A0;0020;0020; # ( ;  ;  ; ; ; ) NO-BREAK SPACE
+00A8;00A8;00A8;0020 0308;0020 0308; # (¨; ¨; ¨; ◌̈; ◌̈; ) DIAERESIS
+00AA;00AA;00AA;0061;0061; # (ª; ª; ª; a; a; ) FEMININE ORDINAL INDICATOR
+00AF;00AF;00AF;0020 0304;0020 0304; # (¯; ¯; ¯; ◌̄; ◌̄; ) MACRON
+00B2;00B2;00B2;0032;0032; # (²; ²; ²; 2; 2; ) SUPERSCRIPT TWO
+00B3;00B3;00B3;0033;0033; # (³; ³; ³; 3; 3; ) SUPERSCRIPT THREE
+00B4;00B4;00B4;0020 0301;0020 0301; # (´; ´; ´; â—ŒÌ; â—ŒÌ; ) ACUTE ACCENT
+00B5;00B5;00B5;03BC;03BC; # (µ; µ; µ; μ; μ; ) MICRO SIGN
+00B8;00B8;00B8;0020 0327;0020 0327; # (¸; ¸; ¸; ◌̧; ◌̧; ) CEDILLA
+00B9;00B9;00B9;0031;0031; # (¹; ¹; ¹; 1; 1; ) SUPERSCRIPT ONE
+00BA;00BA;00BA;006F;006F; # (º; º; º; o; o; ) MASCULINE ORDINAL INDICATOR
+00BC;00BC;00BC;0031 2044 0034;0031 2044 0034; # (¼; ¼; ¼; 1â„4; 1â„4; ) VULGAR FRACTION ONE QUARTER
+00BD;00BD;00BD;0031 2044 0032;0031 2044 0032; # (½; ½; ½; 1â„2; 1â„2; ) VULGAR FRACTION ONE HALF
+00BE;00BE;00BE;0033 2044 0034;0033 2044 0034; # (¾; ¾; ¾; 3â„4; 3â„4; ) VULGAR FRACTION THREE QUARTERS
+00C0;00C0;0041 0300;00C0;0041 0300; # (À; À; A◌̀; À; A◌̀; ) LATIN CAPITAL LETTER A WITH GRAVE
+00C1;00C1;0041 0301;00C1;0041 0301; # (Ã; Ã; Aâ—ŒÌ; Ã; Aâ—ŒÌ; ) LATIN CAPITAL LETTER A WITH ACUTE
+00C2;00C2;0041 0302;00C2;0041 0302; # (Â; Â; A◌̂; Â; A◌̂; ) LATIN CAPITAL LETTER A WITH CIRCUMFLEX
+00C3;00C3;0041 0303;00C3;0041 0303; # (Ã; Ã; A◌̃; Ã; A◌̃; ) LATIN CAPITAL LETTER A WITH TILDE
+00C4;00C4;0041 0308;00C4;0041 0308; # (Ä; Ä; A◌̈; Ä; A◌̈; ) LATIN CAPITAL LETTER A WITH DIAERESIS
+00C5;00C5;0041 030A;00C5;0041 030A; # (Å; Å; A◌̊; Å; A◌̊; ) LATIN CAPITAL LETTER A WITH RING ABOVE
+00C7;00C7;0043 0327;00C7;0043 0327; # (Ç; Ç; C◌̧; Ç; C◌̧; ) LATIN CAPITAL LETTER C WITH CEDILLA
+00C8;00C8;0045 0300;00C8;0045 0300; # (È; È; E◌̀; È; E◌̀; ) LATIN CAPITAL LETTER E WITH GRAVE
+00C9;00C9;0045 0301;00C9;0045 0301; # (É; É; Eâ—ŒÌ; É; Eâ—ŒÌ; ) LATIN CAPITAL LETTER E WITH ACUTE
+00CA;00CA;0045 0302;00CA;0045 0302; # (Ê; Ê; E◌̂; Ê; E◌̂; ) LATIN CAPITAL LETTER E WITH CIRCUMFLEX
+00CB;00CB;0045 0308;00CB;0045 0308; # (Ë; Ë; E◌̈; Ë; E◌̈; ) LATIN CAPITAL LETTER E WITH DIAERESIS
+00CC;00CC;0049 0300;00CC;0049 0300; # (Ì; Ì; I◌̀; Ì; I◌̀; ) LATIN CAPITAL LETTER I WITH GRAVE
+00CD;00CD;0049 0301;00CD;0049 0301; # (Ã; Ã; Iâ—ŒÌ; Ã; Iâ—ŒÌ; ) LATIN CAPITAL LETTER I WITH ACUTE
+00CE;00CE;0049 0302;00CE;0049 0302; # (Î; Î; I◌̂; Î; I◌̂; ) LATIN CAPITAL LETTER I WITH CIRCUMFLEX
+00CF;00CF;0049 0308;00CF;0049 0308; # (Ã; Ã; I◌̈; Ã; I◌̈; ) LATIN CAPITAL LETTER I WITH DIAERESIS
+00D1;00D1;004E 0303;00D1;004E 0303; # (Ñ; Ñ; N◌̃; Ñ; N◌̃; ) LATIN CAPITAL LETTER N WITH TILDE
+00D2;00D2;004F 0300;00D2;004F 0300; # (Ò; Ò; O◌̀; Ò; O◌̀; ) LATIN CAPITAL LETTER O WITH GRAVE
+00D3;00D3;004F 0301;00D3;004F 0301; # (Ó; Ó; Oâ—ŒÌ; Ó; Oâ—ŒÌ; ) LATIN CAPITAL LETTER O WITH ACUTE
+00D4;00D4;004F 0302;00D4;004F 0302; # (Ô; Ô; O◌̂; Ô; O◌̂; ) LATIN CAPITAL LETTER O WITH CIRCUMFLEX
+00D5;00D5;004F 0303;00D5;004F 0303; # (Õ; Õ; O◌̃; Õ; O◌̃; ) LATIN CAPITAL LETTER O WITH TILDE
+00D6;00D6;004F 0308;00D6;004F 0308; # (Ö; Ö; O◌̈; Ö; O◌̈; ) LATIN CAPITAL LETTER O WITH DIAERESIS
+00D9;00D9;0055 0300;00D9;0055 0300; # (Ù; Ù; U◌̀; Ù; U◌̀; ) LATIN CAPITAL LETTER U WITH GRAVE
+00DA;00DA;0055 0301;00DA;0055 0301; # (Ú; Ú; Uâ—ŒÌ; Ú; Uâ—ŒÌ; ) LATIN CAPITAL LETTER U WITH ACUTE
+00DB;00DB;0055 0302;00DB;0055 0302; # (Û; Û; U◌̂; Û; U◌̂; ) LATIN CAPITAL LETTER U WITH CIRCUMFLEX
+00DC;00DC;0055 0308;00DC;0055 0308; # (Ü; Ü; U◌̈; Ü; U◌̈; ) LATIN CAPITAL LETTER U WITH DIAERESIS
+00DD;00DD;0059 0301;00DD;0059 0301; # (Ã; Ã; Yâ—ŒÌ; Ã; Yâ—ŒÌ; ) LATIN CAPITAL LETTER Y WITH ACUTE
+00E0;00E0;0061 0300;00E0;0061 0300; # (à; à; a◌̀; à; a◌̀; ) LATIN SMALL LETTER A WITH GRAVE
+00E1;00E1;0061 0301;00E1;0061 0301; # (á; á; aâ—ŒÌ; á; aâ—ŒÌ; ) LATIN SMALL LETTER A WITH ACUTE
+00E2;00E2;0061 0302;00E2;0061 0302; # (â; â; a◌̂; â; a◌̂; ) LATIN SMALL LETTER A WITH CIRCUMFLEX
+00E3;00E3;0061 0303;00E3;0061 0303; # (ã; ã; a◌̃; ã; a◌̃; ) LATIN SMALL LETTER A WITH TILDE
+00E4;00E4;0061 0308;00E4;0061 0308; # (ä; ä; a◌̈; ä; a◌̈; ) LATIN SMALL LETTER A WITH DIAERESIS
+00E5;00E5;0061 030A;00E5;0061 030A; # (å; å; a◌̊; å; a◌̊; ) LATIN SMALL LETTER A WITH RING ABOVE
+00E7;00E7;0063 0327;00E7;0063 0327; # (ç; ç; c◌̧; ç; c◌̧; ) LATIN SMALL LETTER C WITH CEDILLA
+00E8;00E8;0065 0300;00E8;0065 0300; # (è; è; e◌̀; è; e◌̀; ) LATIN SMALL LETTER E WITH GRAVE
+00E9;00E9;0065 0301;00E9;0065 0301; # (é; é; eâ—ŒÌ; é; eâ—ŒÌ; ) LATIN SMALL LETTER E WITH ACUTE
+00EA;00EA;0065 0302;00EA;0065 0302; # (ê; ê; e◌̂; ê; e◌̂; ) LATIN SMALL LETTER E WITH CIRCUMFLEX
+00EB;00EB;0065 0308;00EB;0065 0308; # (ë; ë; e◌̈; ë; e◌̈; ) LATIN SMALL LETTER E WITH DIAERESIS
+00EC;00EC;0069 0300;00EC;0069 0300; # (ì; ì; i◌̀; ì; i◌̀; ) LATIN SMALL LETTER I WITH GRAVE
+00ED;00ED;0069 0301;00ED;0069 0301; # (í; í; iâ—ŒÌ; í; iâ—ŒÌ; ) LATIN SMALL LETTER I WITH ACUTE
+00EE;00EE;0069 0302;00EE;0069 0302; # (î; î; i◌̂; î; i◌̂; ) LATIN SMALL LETTER I WITH CIRCUMFLEX
+00EF;00EF;0069 0308;00EF;0069 0308; # (ï; ï; i◌̈; ï; i◌̈; ) LATIN SMALL LETTER I WITH DIAERESIS
+00F1;00F1;006E 0303;00F1;006E 0303; # (ñ; ñ; n◌̃; ñ; n◌̃; ) LATIN SMALL LETTER N WITH TILDE
+00F2;00F2;006F 0300;00F2;006F 0300; # (ò; ò; o◌̀; ò; o◌̀; ) LATIN SMALL LETTER O WITH GRAVE
+00F3;00F3;006F 0301;00F3;006F 0301; # (ó; ó; oâ—ŒÌ; ó; oâ—ŒÌ; ) LATIN SMALL LETTER O WITH ACUTE
+00F4;00F4;006F 0302;00F4;006F 0302; # (ô; ô; o◌̂; ô; o◌̂; ) LATIN SMALL LETTER O WITH CIRCUMFLEX
+00F5;00F5;006F 0303;00F5;006F 0303; # (õ; õ; o◌̃; õ; o◌̃; ) LATIN SMALL LETTER O WITH TILDE
+00F6;00F6;006F 0308;00F6;006F 0308; # (ö; ö; o◌̈; ö; o◌̈; ) LATIN SMALL LETTER O WITH DIAERESIS
+00F9;00F9;0075 0300;00F9;0075 0300; # (ù; ù; u◌̀; ù; u◌̀; ) LATIN SMALL LETTER U WITH GRAVE
+00FA;00FA;0075 0301;00FA;0075 0301; # (ú; ú; uâ—ŒÌ; ú; uâ—ŒÌ; ) LATIN SMALL LETTER U WITH ACUTE
+00FB;00FB;0075 0302;00FB;0075 0302; # (û; û; u◌̂; û; u◌̂; ) LATIN SMALL LETTER U WITH CIRCUMFLEX
+00FC;00FC;0075 0308;00FC;0075 0308; # (ü; ü; u◌̈; ü; u◌̈; ) LATIN SMALL LETTER U WITH DIAERESIS
+00FD;00FD;0079 0301;00FD;0079 0301; # (ý; ý; yâ—ŒÌ; ý; yâ—ŒÌ; ) LATIN SMALL LETTER Y WITH ACUTE
+00FF;00FF;0079 0308;00FF;0079 0308; # (ÿ; ÿ; y◌̈; ÿ; y◌̈; ) LATIN SMALL LETTER Y WITH DIAERESIS
+0100;0100;0041 0304;0100;0041 0304; # (Ā; Ā; A◌̄; Ā; A◌̄; ) LATIN CAPITAL LETTER A WITH MACRON
+0101;0101;0061 0304;0101;0061 0304; # (Ä; Ä; a◌̄; Ä; a◌̄; ) LATIN SMALL LETTER A WITH MACRON
+0102;0102;0041 0306;0102;0041 0306; # (Ă; Ă; A◌̆; Ă; A◌̆; ) LATIN CAPITAL LETTER A WITH BREVE
+0103;0103;0061 0306;0103;0061 0306; # (ă; ă; a◌̆; ă; a◌̆; ) LATIN SMALL LETTER A WITH BREVE
+0104;0104;0041 0328;0104;0041 0328; # (Ą; Ą; A◌̨; Ą; A◌̨; ) LATIN CAPITAL LETTER A WITH OGONEK
+0105;0105;0061 0328;0105;0061 0328; # (ą; ą; a◌̨; ą; a◌̨; ) LATIN SMALL LETTER A WITH OGONEK
+0106;0106;0043 0301;0106;0043 0301; # (Ć; Ć; Câ—ŒÌ; Ć; Câ—ŒÌ; ) LATIN CAPITAL LETTER C WITH ACUTE
+0107;0107;0063 0301;0107;0063 0301; # (ć; ć; câ—ŒÌ; ć; câ—ŒÌ; ) LATIN SMALL LETTER C WITH ACUTE
+0108;0108;0043 0302;0108;0043 0302; # (Ĉ; Ĉ; C◌̂; Ĉ; C◌̂; ) LATIN CAPITAL LETTER C WITH CIRCUMFLEX
+0109;0109;0063 0302;0109;0063 0302; # (ĉ; ĉ; c◌̂; ĉ; c◌̂; ) LATIN SMALL LETTER C WITH CIRCUMFLEX
+010A;010A;0043 0307;010A;0043 0307; # (Ċ; Ċ; C◌̇; Ċ; C◌̇; ) LATIN CAPITAL LETTER C WITH DOT ABOVE
+010B;010B;0063 0307;010B;0063 0307; # (ċ; ċ; c◌̇; ċ; c◌̇; ) LATIN SMALL LETTER C WITH DOT ABOVE
+010C;010C;0043 030C;010C;0043 030C; # (Č; Č; C◌̌; Č; C◌̌; ) LATIN CAPITAL LETTER C WITH CARON
+010D;010D;0063 030C;010D;0063 030C; # (Ä; Ä; c◌̌; Ä; c◌̌; ) LATIN SMALL LETTER C WITH CARON
+010E;010E;0044 030C;010E;0044 030C; # (Ď; Ď; D◌̌; Ď; D◌̌; ) LATIN CAPITAL LETTER D WITH CARON
+010F;010F;0064 030C;010F;0064 030C; # (Ä; Ä; d◌̌; Ä; d◌̌; ) LATIN SMALL LETTER D WITH CARON
+0112;0112;0045 0304;0112;0045 0304; # (Ē; Ē; E◌̄; Ē; E◌̄; ) LATIN CAPITAL LETTER E WITH MACRON
+0113;0113;0065 0304;0113;0065 0304; # (ē; ē; e◌̄; ē; e◌̄; ) LATIN SMALL LETTER E WITH MACRON
+0114;0114;0045 0306;0114;0045 0306; # (Ĕ; Ĕ; E◌̆; Ĕ; E◌̆; ) LATIN CAPITAL LETTER E WITH BREVE
+0115;0115;0065 0306;0115;0065 0306; # (ĕ; ĕ; e◌̆; ĕ; e◌̆; ) LATIN SMALL LETTER E WITH BREVE
+0116;0116;0045 0307;0116;0045 0307; # (Ė; Ė; E◌̇; Ė; E◌̇; ) LATIN CAPITAL LETTER E WITH DOT ABOVE
+0117;0117;0065 0307;0117;0065 0307; # (ė; ė; e◌̇; ė; e◌̇; ) LATIN SMALL LETTER E WITH DOT ABOVE
+0118;0118;0045 0328;0118;0045 0328; # (Ę; Ę; E◌̨; Ę; E◌̨; ) LATIN CAPITAL LETTER E WITH OGONEK
+0119;0119;0065 0328;0119;0065 0328; # (ę; ę; e◌̨; ę; e◌̨; ) LATIN SMALL LETTER E WITH OGONEK
+011A;011A;0045 030C;011A;0045 030C; # (Ě; Ě; E◌̌; Ě; E◌̌; ) LATIN CAPITAL LETTER E WITH CARON
+011B;011B;0065 030C;011B;0065 030C; # (ě; ě; e◌̌; ě; e◌̌; ) LATIN SMALL LETTER E WITH CARON
+011C;011C;0047 0302;011C;0047 0302; # (Ĝ; Ĝ; G◌̂; Ĝ; G◌̂; ) LATIN CAPITAL LETTER G WITH CIRCUMFLEX
+011D;011D;0067 0302;011D;0067 0302; # (Ä; Ä; g◌̂; Ä; g◌̂; ) LATIN SMALL LETTER G WITH CIRCUMFLEX
+011E;011E;0047 0306;011E;0047 0306; # (Ğ; Ğ; G◌̆; Ğ; G◌̆; ) LATIN CAPITAL LETTER G WITH BREVE
+011F;011F;0067 0306;011F;0067 0306; # (ğ; ğ; g◌̆; ğ; g◌̆; ) LATIN SMALL LETTER G WITH BREVE
+0120;0120;0047 0307;0120;0047 0307; # (Ġ; Ġ; G◌̇; Ġ; G◌̇; ) LATIN CAPITAL LETTER G WITH DOT ABOVE
+0121;0121;0067 0307;0121;0067 0307; # (ġ; ġ; g◌̇; ġ; g◌̇; ) LATIN SMALL LETTER G WITH DOT ABOVE
+0122;0122;0047 0327;0122;0047 0327; # (Ģ; Ģ; G◌̧; Ģ; G◌̧; ) LATIN CAPITAL LETTER G WITH CEDILLA
+0123;0123;0067 0327;0123;0067 0327; # (ģ; ģ; g◌̧; ģ; g◌̧; ) LATIN SMALL LETTER G WITH CEDILLA
+0124;0124;0048 0302;0124;0048 0302; # (Ĥ; Ĥ; H◌̂; Ĥ; H◌̂; ) LATIN CAPITAL LETTER H WITH CIRCUMFLEX
+0125;0125;0068 0302;0125;0068 0302; # (ĥ; ĥ; h◌̂; ĥ; h◌̂; ) LATIN SMALL LETTER H WITH CIRCUMFLEX
+0128;0128;0049 0303;0128;0049 0303; # (Ĩ; Ĩ; I◌̃; Ĩ; I◌̃; ) LATIN CAPITAL LETTER I WITH TILDE
+0129;0129;0069 0303;0129;0069 0303; # (ĩ; ĩ; i◌̃; ĩ; i◌̃; ) LATIN SMALL LETTER I WITH TILDE
+012A;012A;0049 0304;012A;0049 0304; # (Ī; Ī; I◌̄; Ī; I◌̄; ) LATIN CAPITAL LETTER I WITH MACRON
+012B;012B;0069 0304;012B;0069 0304; # (ī; ī; i◌̄; ī; i◌̄; ) LATIN SMALL LETTER I WITH MACRON
+012C;012C;0049 0306;012C;0049 0306; # (Ĭ; Ĭ; I◌̆; Ĭ; I◌̆; ) LATIN CAPITAL LETTER I WITH BREVE
+012D;012D;0069 0306;012D;0069 0306; # (ĭ; ĭ; i◌̆; ĭ; i◌̆; ) LATIN SMALL LETTER I WITH BREVE
+012E;012E;0049 0328;012E;0049 0328; # (Į; Į; I◌̨; Į; I◌̨; ) LATIN CAPITAL LETTER I WITH OGONEK
+012F;012F;0069 0328;012F;0069 0328; # (į; į; i◌̨; į; i◌̨; ) LATIN SMALL LETTER I WITH OGONEK
+0130;0130;0049 0307;0130;0049 0307; # (İ; İ; I◌̇; İ; I◌̇; ) LATIN CAPITAL LETTER I WITH DOT ABOVE
+0132;0132;0132;0049 004A;0049 004A; # (IJ; IJ; IJ; IJ; IJ; ) LATIN CAPITAL LIGATURE IJ
+0133;0133;0133;0069 006A;0069 006A; # (ij; ij; ij; ij; ij; ) LATIN SMALL LIGATURE IJ
+0134;0134;004A 0302;0134;004A 0302; # (Ĵ; Ĵ; J◌̂; Ĵ; J◌̂; ) LATIN CAPITAL LETTER J WITH CIRCUMFLEX
+0135;0135;006A 0302;0135;006A 0302; # (ĵ; ĵ; j◌̂; ĵ; j◌̂; ) LATIN SMALL LETTER J WITH CIRCUMFLEX
+0136;0136;004B 0327;0136;004B 0327; # (Ķ; Ķ; K◌̧; Ķ; K◌̧; ) LATIN CAPITAL LETTER K WITH CEDILLA
+0137;0137;006B 0327;0137;006B 0327; # (ķ; ķ; k◌̧; ķ; k◌̧; ) LATIN SMALL LETTER K WITH CEDILLA
+0139;0139;004C 0301;0139;004C 0301; # (Ĺ; Ĺ; Lâ—ŒÌ; Ĺ; Lâ—ŒÌ; ) LATIN CAPITAL LETTER L WITH ACUTE
+013A;013A;006C 0301;013A;006C 0301; # (ĺ; ĺ; lâ—ŒÌ; ĺ; lâ—ŒÌ; ) LATIN SMALL LETTER L WITH ACUTE
+013B;013B;004C 0327;013B;004C 0327; # (Ļ; Ļ; L◌̧; Ļ; L◌̧; ) LATIN CAPITAL LETTER L WITH CEDILLA
+013C;013C;006C 0327;013C;006C 0327; # (ļ; ļ; l◌̧; ļ; l◌̧; ) LATIN SMALL LETTER L WITH CEDILLA
+013D;013D;004C 030C;013D;004C 030C; # (Ľ; Ľ; L◌̌; Ľ; L◌̌; ) LATIN CAPITAL LETTER L WITH CARON
+013E;013E;006C 030C;013E;006C 030C; # (ľ; ľ; l◌̌; ľ; l◌̌; ) LATIN SMALL LETTER L WITH CARON
+013F;013F;013F;004C 00B7;004C 00B7; # (Ŀ; Ŀ; Ŀ; L·; L·; ) LATIN CAPITAL LETTER L WITH MIDDLE DOT
+0140;0140;0140;006C 00B7;006C 00B7; # (ŀ; ŀ; ŀ; l·; l·; ) LATIN SMALL LETTER L WITH MIDDLE DOT
+0143;0143;004E 0301;0143;004E 0301; # (Ń; Ń; Nâ—ŒÌ; Ń; Nâ—ŒÌ; ) LATIN CAPITAL LETTER N WITH ACUTE
+0144;0144;006E 0301;0144;006E 0301; # (Å„; Å„; nâ—ŒÌ; Å„; nâ—ŒÌ; ) LATIN SMALL LETTER N WITH ACUTE
+0145;0145;004E 0327;0145;004E 0327; # (Ņ; Ņ; N◌̧; Ņ; N◌̧; ) LATIN CAPITAL LETTER N WITH CEDILLA
+0146;0146;006E 0327;0146;006E 0327; # (ņ; ņ; n◌̧; ņ; n◌̧; ) LATIN SMALL LETTER N WITH CEDILLA
+0147;0147;004E 030C;0147;004E 030C; # (Ň; Ň; N◌̌; Ň; N◌̌; ) LATIN CAPITAL LETTER N WITH CARON
+0148;0148;006E 030C;0148;006E 030C; # (ň; ň; n◌̌; ň; n◌̌; ) LATIN SMALL LETTER N WITH CARON
+0149;0149;0149;02BC 006E;02BC 006E; # (ʼn; ʼn; ʼn; ʼn; ʼn; ) LATIN SMALL LETTER N PRECEDED BY APOSTROPHE
+014C;014C;004F 0304;014C;004F 0304; # (Ō; Ō; O◌̄; Ō; O◌̄; ) LATIN CAPITAL LETTER O WITH MACRON
+014D;014D;006F 0304;014D;006F 0304; # (Å; Å; o◌̄; Å; o◌̄; ) LATIN SMALL LETTER O WITH MACRON
+014E;014E;004F 0306;014E;004F 0306; # (Ŏ; Ŏ; O◌̆; Ŏ; O◌̆; ) LATIN CAPITAL LETTER O WITH BREVE
+014F;014F;006F 0306;014F;006F 0306; # (Å; Å; o◌̆; Å; o◌̆; ) LATIN SMALL LETTER O WITH BREVE
+0150;0150;004F 030B;0150;004F 030B; # (Å; Å; O◌̋; Å; O◌̋; ) LATIN CAPITAL LETTER O WITH DOUBLE ACUTE
+0151;0151;006F 030B;0151;006F 030B; # (ő; ő; o◌̋; ő; o◌̋; ) LATIN SMALL LETTER O WITH DOUBLE ACUTE
+0154;0154;0052 0301;0154;0052 0301; # (Å”; Å”; Râ—ŒÌ; Å”; Râ—ŒÌ; ) LATIN CAPITAL LETTER R WITH ACUTE
+0155;0155;0072 0301;0155;0072 0301; # (Å•; Å•; râ—ŒÌ; Å•; râ—ŒÌ; ) LATIN SMALL LETTER R WITH ACUTE
+0156;0156;0052 0327;0156;0052 0327; # (Ŗ; Ŗ; R◌̧; Ŗ; R◌̧; ) LATIN CAPITAL LETTER R WITH CEDILLA
+0157;0157;0072 0327;0157;0072 0327; # (ŗ; ŗ; r◌̧; ŗ; r◌̧; ) LATIN SMALL LETTER R WITH CEDILLA
+0158;0158;0052 030C;0158;0052 030C; # (Ř; Ř; R◌̌; Ř; R◌̌; ) LATIN CAPITAL LETTER R WITH CARON
+0159;0159;0072 030C;0159;0072 030C; # (ř; ř; r◌̌; ř; r◌̌; ) LATIN SMALL LETTER R WITH CARON
+015A;015A;0053 0301;015A;0053 0301; # (Åš; Åš; Sâ—ŒÌ; Åš; Sâ—ŒÌ; ) LATIN CAPITAL LETTER S WITH ACUTE
+015B;015B;0073 0301;015B;0073 0301; # (Å›; Å›; sâ—ŒÌ; Å›; sâ—ŒÌ; ) LATIN SMALL LETTER S WITH ACUTE
+015C;015C;0053 0302;015C;0053 0302; # (Ŝ; Ŝ; S◌̂; Ŝ; S◌̂; ) LATIN CAPITAL LETTER S WITH CIRCUMFLEX
+015D;015D;0073 0302;015D;0073 0302; # (Å; Å; s◌̂; Å; s◌̂; ) LATIN SMALL LETTER S WITH CIRCUMFLEX
+015E;015E;0053 0327;015E;0053 0327; # (Ş; Ş; S◌̧; Ş; S◌̧; ) LATIN CAPITAL LETTER S WITH CEDILLA
+015F;015F;0073 0327;015F;0073 0327; # (ş; ş; s◌̧; ş; s◌̧; ) LATIN SMALL LETTER S WITH CEDILLA
+0160;0160;0053 030C;0160;0053 030C; # (Š; Š; S◌̌; Š; S◌̌; ) LATIN CAPITAL LETTER S WITH CARON
+0161;0161;0073 030C;0161;0073 030C; # (š; š; s◌̌; š; s◌̌; ) LATIN SMALL LETTER S WITH CARON
+0162;0162;0054 0327;0162;0054 0327; # (Ţ; Ţ; T◌̧; Ţ; T◌̧; ) LATIN CAPITAL LETTER T WITH CEDILLA
+0163;0163;0074 0327;0163;0074 0327; # (ţ; ţ; t◌̧; ţ; t◌̧; ) LATIN SMALL LETTER T WITH CEDILLA
+0164;0164;0054 030C;0164;0054 030C; # (Ť; Ť; T◌̌; Ť; T◌̌; ) LATIN CAPITAL LETTER T WITH CARON
+0165;0165;0074 030C;0165;0074 030C; # (ť; ť; t◌̌; ť; t◌̌; ) LATIN SMALL LETTER T WITH CARON
+0168;0168;0055 0303;0168;0055 0303; # (Ũ; Ũ; U◌̃; Ũ; U◌̃; ) LATIN CAPITAL LETTER U WITH TILDE
+0169;0169;0075 0303;0169;0075 0303; # (ũ; ũ; u◌̃; ũ; u◌̃; ) LATIN SMALL LETTER U WITH TILDE
+016A;016A;0055 0304;016A;0055 0304; # (Ū; Ū; U◌̄; Ū; U◌̄; ) LATIN CAPITAL LETTER U WITH MACRON
+016B;016B;0075 0304;016B;0075 0304; # (ū; ū; u◌̄; ū; u◌̄; ) LATIN SMALL LETTER U WITH MACRON
+016C;016C;0055 0306;016C;0055 0306; # (Ŭ; Ŭ; U◌̆; Ŭ; U◌̆; ) LATIN CAPITAL LETTER U WITH BREVE
+016D;016D;0075 0306;016D;0075 0306; # (ŭ; ŭ; u◌̆; ŭ; u◌̆; ) LATIN SMALL LETTER U WITH BREVE
+016E;016E;0055 030A;016E;0055 030A; # (Ů; Ů; U◌̊; Ů; U◌̊; ) LATIN CAPITAL LETTER U WITH RING ABOVE
+016F;016F;0075 030A;016F;0075 030A; # (ů; ů; u◌̊; ů; u◌̊; ) LATIN SMALL LETTER U WITH RING ABOVE
+0170;0170;0055 030B;0170;0055 030B; # (Ű; Ű; U◌̋; Ű; U◌̋; ) LATIN CAPITAL LETTER U WITH DOUBLE ACUTE
+0171;0171;0075 030B;0171;0075 030B; # (ű; ű; u◌̋; ű; u◌̋; ) LATIN SMALL LETTER U WITH DOUBLE ACUTE
+0172;0172;0055 0328;0172;0055 0328; # (Ų; Ų; U◌̨; Ų; U◌̨; ) LATIN CAPITAL LETTER U WITH OGONEK
+0173;0173;0075 0328;0173;0075 0328; # (ų; ų; u◌̨; ų; u◌̨; ) LATIN SMALL LETTER U WITH OGONEK
+0174;0174;0057 0302;0174;0057 0302; # (Ŵ; Ŵ; W◌̂; Ŵ; W◌̂; ) LATIN CAPITAL LETTER W WITH CIRCUMFLEX
+0175;0175;0077 0302;0175;0077 0302; # (ŵ; ŵ; w◌̂; ŵ; w◌̂; ) LATIN SMALL LETTER W WITH CIRCUMFLEX
+0176;0176;0059 0302;0176;0059 0302; # (Ŷ; Ŷ; Y◌̂; Ŷ; Y◌̂; ) LATIN CAPITAL LETTER Y WITH CIRCUMFLEX
+0177;0177;0079 0302;0177;0079 0302; # (ŷ; ŷ; y◌̂; ŷ; y◌̂; ) LATIN SMALL LETTER Y WITH CIRCUMFLEX
+0178;0178;0059 0308;0178;0059 0308; # (Ÿ; Ÿ; Y◌̈; Ÿ; Y◌̈; ) LATIN CAPITAL LETTER Y WITH DIAERESIS
+0179;0179;005A 0301;0179;005A 0301; # (Ź; Ź; Zâ—ŒÌ; Ź; Zâ—ŒÌ; ) LATIN CAPITAL LETTER Z WITH ACUTE
+017A;017A;007A 0301;017A;007A 0301; # (ź; ź; zâ—ŒÌ; ź; zâ—ŒÌ; ) LATIN SMALL LETTER Z WITH ACUTE
+017B;017B;005A 0307;017B;005A 0307; # (Ż; Ż; Z◌̇; Ż; Z◌̇; ) LATIN CAPITAL LETTER Z WITH DOT ABOVE
+017C;017C;007A 0307;017C;007A 0307; # (ż; ż; z◌̇; ż; z◌̇; ) LATIN SMALL LETTER Z WITH DOT ABOVE
+017D;017D;005A 030C;017D;005A 030C; # (Ž; Ž; Z◌̌; Ž; Z◌̌; ) LATIN CAPITAL LETTER Z WITH CARON
+017E;017E;007A 030C;017E;007A 030C; # (ž; ž; z◌̌; ž; z◌̌; ) LATIN SMALL LETTER Z WITH CARON
+017F;017F;017F;0073;0073; # (Å¿; Å¿; Å¿; s; s; ) LATIN SMALL LETTER LONG S
+01A0;01A0;004F 031B;01A0;004F 031B; # (Ơ; Ơ; O◌̛; Ơ; O◌̛; ) LATIN CAPITAL LETTER O WITH HORN
+01A1;01A1;006F 031B;01A1;006F 031B; # (ơ; ơ; o◌̛; ơ; o◌̛; ) LATIN SMALL LETTER O WITH HORN
+01AF;01AF;0055 031B;01AF;0055 031B; # (Ư; Ư; U◌̛; Ư; U◌̛; ) LATIN CAPITAL LETTER U WITH HORN
+01B0;01B0;0075 031B;01B0;0075 031B; # (ư; ư; u◌̛; ư; u◌̛; ) LATIN SMALL LETTER U WITH HORN
+01C4;01C4;01C4;0044 017D;0044 005A 030C; # (DŽ; DŽ; DŽ; DŽ; DZ◌̌; ) LATIN CAPITAL LETTER DZ WITH CARON
+01C5;01C5;01C5;0044 017E;0044 007A 030C; # (Dž; Dž; Dž; Dž; Dz◌̌; ) LATIN CAPITAL LETTER D WITH SMALL LETTER Z WITH CARON
+01C6;01C6;01C6;0064 017E;0064 007A 030C; # (dž; dž; dž; dž; dz◌̌; ) LATIN SMALL LETTER DZ WITH CARON
+01C7;01C7;01C7;004C 004A;004C 004A; # (LJ; LJ; LJ; LJ; LJ; ) LATIN CAPITAL LETTER LJ
+01C8;01C8;01C8;004C 006A;004C 006A; # (Lj; Lj; Lj; Lj; Lj; ) LATIN CAPITAL LETTER L WITH SMALL LETTER J
+01C9;01C9;01C9;006C 006A;006C 006A; # (lj; lj; lj; lj; lj; ) LATIN SMALL LETTER LJ
+01CA;01CA;01CA;004E 004A;004E 004A; # (ÇŠ; ÇŠ; ÇŠ; NJ; NJ; ) LATIN CAPITAL LETTER NJ
+01CB;01CB;01CB;004E 006A;004E 006A; # (Ç‹; Ç‹; Ç‹; Nj; Nj; ) LATIN CAPITAL LETTER N WITH SMALL LETTER J
+01CC;01CC;01CC;006E 006A;006E 006A; # (nj; nj; nj; nj; nj; ) LATIN SMALL LETTER NJ
+01CD;01CD;0041 030C;01CD;0041 030C; # (Ç; Ç; A◌̌; Ç; A◌̌; ) LATIN CAPITAL LETTER A WITH CARON
+01CE;01CE;0061 030C;01CE;0061 030C; # (ǎ; ǎ; a◌̌; ǎ; a◌̌; ) LATIN SMALL LETTER A WITH CARON
+01CF;01CF;0049 030C;01CF;0049 030C; # (Ç; Ç; I◌̌; Ç; I◌̌; ) LATIN CAPITAL LETTER I WITH CARON
+01D0;01D0;0069 030C;01D0;0069 030C; # (Ç; Ç; i◌̌; Ç; i◌̌; ) LATIN SMALL LETTER I WITH CARON
+01D1;01D1;004F 030C;01D1;004F 030C; # (Ǒ; Ǒ; O◌̌; Ǒ; O◌̌; ) LATIN CAPITAL LETTER O WITH CARON
+01D2;01D2;006F 030C;01D2;006F 030C; # (ǒ; ǒ; o◌̌; ǒ; o◌̌; ) LATIN SMALL LETTER O WITH CARON
+01D3;01D3;0055 030C;01D3;0055 030C; # (Ǔ; Ǔ; U◌̌; Ǔ; U◌̌; ) LATIN CAPITAL LETTER U WITH CARON
+01D4;01D4;0075 030C;01D4;0075 030C; # (ǔ; ǔ; u◌̌; ǔ; u◌̌; ) LATIN SMALL LETTER U WITH CARON
+01D5;01D5;0055 0308 0304;01D5;0055 0308 0304; # (Ǖ; Ǖ; U◌̈◌̄; Ǖ; U◌̈◌̄; ) LATIN CAPITAL LETTER U WITH DIAERESIS AND MACRON
+01D6;01D6;0075 0308 0304;01D6;0075 0308 0304; # (ǖ; ǖ; u◌̈◌̄; ǖ; u◌̈◌̄; ) LATIN SMALL LETTER U WITH DIAERESIS AND MACRON
+01D7;01D7;0055 0308 0301;01D7;0055 0308 0301; # (Ç—; Ç—; U◌̈◌Ì; Ç—; U◌̈◌Ì; ) LATIN CAPITAL LETTER U WITH DIAERESIS AND ACUTE
+01D8;01D8;0075 0308 0301;01D8;0075 0308 0301; # (ǘ; ǘ; u◌̈◌Ì; ǘ; u◌̈◌Ì; ) LATIN SMALL LETTER U WITH DIAERESIS AND ACUTE
+01D9;01D9;0055 0308 030C;01D9;0055 0308 030C; # (Ǚ; Ǚ; U◌̈◌̌; Ǚ; U◌̈◌̌; ) LATIN CAPITAL LETTER U WITH DIAERESIS AND CARON
+01DA;01DA;0075 0308 030C;01DA;0075 0308 030C; # (ǚ; ǚ; u◌̈◌̌; ǚ; u◌̈◌̌; ) LATIN SMALL LETTER U WITH DIAERESIS AND CARON
+01DB;01DB;0055 0308 0300;01DB;0055 0308 0300; # (Ǜ; Ǜ; U◌̈◌̀; Ǜ; U◌̈◌̀; ) LATIN CAPITAL LETTER U WITH DIAERESIS AND GRAVE
+01DC;01DC;0075 0308 0300;01DC;0075 0308 0300; # (ǜ; ǜ; u◌̈◌̀; ǜ; u◌̈◌̀; ) LATIN SMALL LETTER U WITH DIAERESIS AND GRAVE
+01DE;01DE;0041 0308 0304;01DE;0041 0308 0304; # (Ǟ; Ǟ; A◌̈◌̄; Ǟ; A◌̈◌̄; ) LATIN CAPITAL LETTER A WITH DIAERESIS AND MACRON
+01DF;01DF;0061 0308 0304;01DF;0061 0308 0304; # (ǟ; ǟ; a◌̈◌̄; ǟ; a◌̈◌̄; ) LATIN SMALL LETTER A WITH DIAERESIS AND MACRON
+01E0;01E0;0041 0307 0304;01E0;0041 0307 0304; # (Ǡ; Ǡ; A◌̇◌̄; Ǡ; A◌̇◌̄; ) LATIN CAPITAL LETTER A WITH DOT ABOVE AND MACRON
+01E1;01E1;0061 0307 0304;01E1;0061 0307 0304; # (ǡ; ǡ; a◌̇◌̄; ǡ; a◌̇◌̄; ) LATIN SMALL LETTER A WITH DOT ABOVE AND MACRON
+01E2;01E2;00C6 0304;01E2;00C6 0304; # (Ǣ; Ǣ; Æ◌̄; Ǣ; Æ◌̄; ) LATIN CAPITAL LETTER AE WITH MACRON
+01E3;01E3;00E6 0304;01E3;00E6 0304; # (ǣ; ǣ; æ◌̄; ǣ; æ◌̄; ) LATIN SMALL LETTER AE WITH MACRON
+01E6;01E6;0047 030C;01E6;0047 030C; # (Ǧ; Ǧ; G◌̌; Ǧ; G◌̌; ) LATIN CAPITAL LETTER G WITH CARON
+01E7;01E7;0067 030C;01E7;0067 030C; # (ǧ; ǧ; g◌̌; ǧ; g◌̌; ) LATIN SMALL LETTER G WITH CARON
+01E8;01E8;004B 030C;01E8;004B 030C; # (Ǩ; Ǩ; K◌̌; Ǩ; K◌̌; ) LATIN CAPITAL LETTER K WITH CARON
+01E9;01E9;006B 030C;01E9;006B 030C; # (ǩ; ǩ; k◌̌; ǩ; k◌̌; ) LATIN SMALL LETTER K WITH CARON
+01EA;01EA;004F 0328;01EA;004F 0328; # (Ǫ; Ǫ; O◌̨; Ǫ; O◌̨; ) LATIN CAPITAL LETTER O WITH OGONEK
+01EB;01EB;006F 0328;01EB;006F 0328; # (ǫ; ǫ; o◌̨; ǫ; o◌̨; ) LATIN SMALL LETTER O WITH OGONEK
+01EC;01EC;004F 0328 0304;01EC;004F 0328 0304; # (Ǭ; Ǭ; O◌̨◌̄; Ǭ; O◌̨◌̄; ) LATIN CAPITAL LETTER O WITH OGONEK AND MACRON
+01ED;01ED;006F 0328 0304;01ED;006F 0328 0304; # (ǭ; ǭ; o◌̨◌̄; ǭ; o◌̨◌̄; ) LATIN SMALL LETTER O WITH OGONEK AND MACRON
+01EE;01EE;01B7 030C;01EE;01B7 030C; # (Ǯ; Ǯ; Ʒ◌̌; Ǯ; Ʒ◌̌; ) LATIN CAPITAL LETTER EZH WITH CARON
+01EF;01EF;0292 030C;01EF;0292 030C; # (ǯ; ǯ; ʒ◌̌; ǯ; ʒ◌̌; ) LATIN SMALL LETTER EZH WITH CARON
+01F0;01F0;006A 030C;01F0;006A 030C; # (ǰ; ǰ; j◌̌; ǰ; j◌̌; ) LATIN SMALL LETTER J WITH CARON
+01F1;01F1;01F1;0044 005A;0044 005A; # (DZ; DZ; DZ; DZ; DZ; ) LATIN CAPITAL LETTER DZ
+01F2;01F2;01F2;0044 007A;0044 007A; # (Dz; Dz; Dz; Dz; Dz; ) LATIN CAPITAL LETTER D WITH SMALL LETTER Z
+01F3;01F3;01F3;0064 007A;0064 007A; # (dz; dz; dz; dz; dz; ) LATIN SMALL LETTER DZ
+01F4;01F4;0047 0301;01F4;0047 0301; # (Ç´; Ç´; Gâ—ŒÌ; Ç´; Gâ—ŒÌ; ) LATIN CAPITAL LETTER G WITH ACUTE
+01F5;01F5;0067 0301;01F5;0067 0301; # (ǵ; ǵ; gâ—ŒÌ; ǵ; gâ—ŒÌ; ) LATIN SMALL LETTER G WITH ACUTE
+01F8;01F8;004E 0300;01F8;004E 0300; # (Ǹ; Ǹ; N◌̀; Ǹ; N◌̀; ) LATIN CAPITAL LETTER N WITH GRAVE
+01F9;01F9;006E 0300;01F9;006E 0300; # (ǹ; ǹ; n◌̀; ǹ; n◌̀; ) LATIN SMALL LETTER N WITH GRAVE
+01FA;01FA;0041 030A 0301;01FA;0041 030A 0301; # (Ǻ; Ǻ; A◌̊◌Ì; Ǻ; A◌̊◌Ì; ) LATIN CAPITAL LETTER A WITH RING ABOVE AND ACUTE
+01FB;01FB;0061 030A 0301;01FB;0061 030A 0301; # (Ç»; Ç»; a◌̊◌Ì; Ç»; a◌̊◌Ì; ) LATIN SMALL LETTER A WITH RING ABOVE AND ACUTE
+01FC;01FC;00C6 0301;01FC;00C6 0301; # (Ǽ; Ǽ; Æ◌Ì; Ǽ; Æ◌Ì; ) LATIN CAPITAL LETTER AE WITH ACUTE
+01FD;01FD;00E6 0301;01FD;00E6 0301; # (ǽ; ǽ; æ◌Ì; ǽ; æ◌Ì; ) LATIN SMALL LETTER AE WITH ACUTE
+01FE;01FE;00D8 0301;01FE;00D8 0301; # (Ǿ; Ǿ; Ø◌Ì; Ǿ; Ø◌Ì; ) LATIN CAPITAL LETTER O WITH STROKE AND ACUTE
+01FF;01FF;00F8 0301;01FF;00F8 0301; # (Ç¿; Ç¿; ø◌Ì; Ç¿; ø◌Ì; ) LATIN SMALL LETTER O WITH STROKE AND ACUTE
+0200;0200;0041 030F;0200;0041 030F; # (È€; È€; Aâ—ŒÌ; È€; Aâ—ŒÌ; ) LATIN CAPITAL LETTER A WITH DOUBLE GRAVE
+0201;0201;0061 030F;0201;0061 030F; # (È; È; aâ—ŒÌ; È; aâ—ŒÌ; ) LATIN SMALL LETTER A WITH DOUBLE GRAVE
+0202;0202;0041 0311;0202;0041 0311; # (Ȃ; Ȃ; A◌̑; Ȃ; A◌̑; ) LATIN CAPITAL LETTER A WITH INVERTED BREVE
+0203;0203;0061 0311;0203;0061 0311; # (ȃ; ȃ; a◌̑; ȃ; a◌̑; ) LATIN SMALL LETTER A WITH INVERTED BREVE
+0204;0204;0045 030F;0204;0045 030F; # (È„; È„; Eâ—ŒÌ; È„; Eâ—ŒÌ; ) LATIN CAPITAL LETTER E WITH DOUBLE GRAVE
+0205;0205;0065 030F;0205;0065 030F; # (È…; È…; eâ—ŒÌ; È…; eâ—ŒÌ; ) LATIN SMALL LETTER E WITH DOUBLE GRAVE
+0206;0206;0045 0311;0206;0045 0311; # (Ȇ; Ȇ; E◌̑; Ȇ; E◌̑; ) LATIN CAPITAL LETTER E WITH INVERTED BREVE
+0207;0207;0065 0311;0207;0065 0311; # (ȇ; ȇ; e◌̑; ȇ; e◌̑; ) LATIN SMALL LETTER E WITH INVERTED BREVE
+0208;0208;0049 030F;0208;0049 030F; # (Ȉ; Ȉ; Iâ—ŒÌ; Ȉ; Iâ—ŒÌ; ) LATIN CAPITAL LETTER I WITH DOUBLE GRAVE
+0209;0209;0069 030F;0209;0069 030F; # (ȉ; ȉ; iâ—ŒÌ; ȉ; iâ—ŒÌ; ) LATIN SMALL LETTER I WITH DOUBLE GRAVE
+020A;020A;0049 0311;020A;0049 0311; # (Ȋ; Ȋ; I◌̑; Ȋ; I◌̑; ) LATIN CAPITAL LETTER I WITH INVERTED BREVE
+020B;020B;0069 0311;020B;0069 0311; # (ȋ; ȋ; i◌̑; ȋ; i◌̑; ) LATIN SMALL LETTER I WITH INVERTED BREVE
+020C;020C;004F 030F;020C;004F 030F; # (ÈŒ; ÈŒ; Oâ—ŒÌ; ÈŒ; Oâ—ŒÌ; ) LATIN CAPITAL LETTER O WITH DOUBLE GRAVE
+020D;020D;006F 030F;020D;006F 030F; # (È; È; oâ—ŒÌ; È; oâ—ŒÌ; ) LATIN SMALL LETTER O WITH DOUBLE GRAVE
+020E;020E;004F 0311;020E;004F 0311; # (Ȏ; Ȏ; O◌̑; Ȏ; O◌̑; ) LATIN CAPITAL LETTER O WITH INVERTED BREVE
+020F;020F;006F 0311;020F;006F 0311; # (È; È; o◌̑; È; o◌̑; ) LATIN SMALL LETTER O WITH INVERTED BREVE
+0210;0210;0052 030F;0210;0052 030F; # (È; È; Râ—ŒÌ; È; Râ—ŒÌ; ) LATIN CAPITAL LETTER R WITH DOUBLE GRAVE
+0211;0211;0072 030F;0211;0072 030F; # (È‘; È‘; râ—ŒÌ; È‘; râ—ŒÌ; ) LATIN SMALL LETTER R WITH DOUBLE GRAVE
+0212;0212;0052 0311;0212;0052 0311; # (Ȓ; Ȓ; R◌̑; Ȓ; R◌̑; ) LATIN CAPITAL LETTER R WITH INVERTED BREVE
+0213;0213;0072 0311;0213;0072 0311; # (ȓ; ȓ; r◌̑; ȓ; r◌̑; ) LATIN SMALL LETTER R WITH INVERTED BREVE
+0214;0214;0055 030F;0214;0055 030F; # (È”; È”; Uâ—ŒÌ; È”; Uâ—ŒÌ; ) LATIN CAPITAL LETTER U WITH DOUBLE GRAVE
+0215;0215;0075 030F;0215;0075 030F; # (È•; È•; uâ—ŒÌ; È•; uâ—ŒÌ; ) LATIN SMALL LETTER U WITH DOUBLE GRAVE
+0216;0216;0055 0311;0216;0055 0311; # (Ȗ; Ȗ; U◌̑; Ȗ; U◌̑; ) LATIN CAPITAL LETTER U WITH INVERTED BREVE
+0217;0217;0075 0311;0217;0075 0311; # (ȗ; ȗ; u◌̑; ȗ; u◌̑; ) LATIN SMALL LETTER U WITH INVERTED BREVE
+0218;0218;0053 0326;0218;0053 0326; # (Ș; Ș; S◌̦; Ș; S◌̦; ) LATIN CAPITAL LETTER S WITH COMMA BELOW
+0219;0219;0073 0326;0219;0073 0326; # (ș; ș; s◌̦; ș; s◌̦; ) LATIN SMALL LETTER S WITH COMMA BELOW
+021A;021A;0054 0326;021A;0054 0326; # (Ț; Ț; T◌̦; Ț; T◌̦; ) LATIN CAPITAL LETTER T WITH COMMA BELOW
+021B;021B;0074 0326;021B;0074 0326; # (ț; ț; t◌̦; ț; t◌̦; ) LATIN SMALL LETTER T WITH COMMA BELOW
+021E;021E;0048 030C;021E;0048 030C; # (Ȟ; Ȟ; H◌̌; Ȟ; H◌̌; ) LATIN CAPITAL LETTER H WITH CARON
+021F;021F;0068 030C;021F;0068 030C; # (ȟ; ȟ; h◌̌; ȟ; h◌̌; ) LATIN SMALL LETTER H WITH CARON
+0226;0226;0041 0307;0226;0041 0307; # (Ȧ; Ȧ; A◌̇; Ȧ; A◌̇; ) LATIN CAPITAL LETTER A WITH DOT ABOVE
+0227;0227;0061 0307;0227;0061 0307; # (ȧ; ȧ; a◌̇; ȧ; a◌̇; ) LATIN SMALL LETTER A WITH DOT ABOVE
+0228;0228;0045 0327;0228;0045 0327; # (Ȩ; Ȩ; E◌̧; Ȩ; E◌̧; ) LATIN CAPITAL LETTER E WITH CEDILLA
+0229;0229;0065 0327;0229;0065 0327; # (ȩ; ȩ; e◌̧; ȩ; e◌̧; ) LATIN SMALL LETTER E WITH CEDILLA
+022A;022A;004F 0308 0304;022A;004F 0308 0304; # (Ȫ; Ȫ; O◌̈◌̄; Ȫ; O◌̈◌̄; ) LATIN CAPITAL LETTER O WITH DIAERESIS AND MACRON
+022B;022B;006F 0308 0304;022B;006F 0308 0304; # (ȫ; ȫ; o◌̈◌̄; ȫ; o◌̈◌̄; ) LATIN SMALL LETTER O WITH DIAERESIS AND MACRON
+022C;022C;004F 0303 0304;022C;004F 0303 0304; # (Ȭ; Ȭ; O◌̃◌̄; Ȭ; O◌̃◌̄; ) LATIN CAPITAL LETTER O WITH TILDE AND MACRON
+022D;022D;006F 0303 0304;022D;006F 0303 0304; # (ȭ; ȭ; o◌̃◌̄; ȭ; o◌̃◌̄; ) LATIN SMALL LETTER O WITH TILDE AND MACRON
+022E;022E;004F 0307;022E;004F 0307; # (Ȯ; Ȯ; O◌̇; Ȯ; O◌̇; ) LATIN CAPITAL LETTER O WITH DOT ABOVE
+022F;022F;006F 0307;022F;006F 0307; # (ȯ; ȯ; o◌̇; ȯ; o◌̇; ) LATIN SMALL LETTER O WITH DOT ABOVE
+0230;0230;004F 0307 0304;0230;004F 0307 0304; # (Ȱ; Ȱ; O◌̇◌̄; Ȱ; O◌̇◌̄; ) LATIN CAPITAL LETTER O WITH DOT ABOVE AND MACRON
+0231;0231;006F 0307 0304;0231;006F 0307 0304; # (ȱ; ȱ; o◌̇◌̄; ȱ; o◌̇◌̄; ) LATIN SMALL LETTER O WITH DOT ABOVE AND MACRON
+0232;0232;0059 0304;0232;0059 0304; # (Ȳ; Ȳ; Y◌̄; Ȳ; Y◌̄; ) LATIN CAPITAL LETTER Y WITH MACRON
+0233;0233;0079 0304;0233;0079 0304; # (ȳ; ȳ; y◌̄; ȳ; y◌̄; ) LATIN SMALL LETTER Y WITH MACRON
+02B0;02B0;02B0;0068;0068; # (ʰ; ʰ; ʰ; h; h; ) MODIFIER LETTER SMALL H
+02B1;02B1;02B1;0266;0266; # (ʱ; ʱ; ʱ; ɦ; ɦ; ) MODIFIER LETTER SMALL H WITH HOOK
+02B2;02B2;02B2;006A;006A; # (ʲ; ʲ; ʲ; j; j; ) MODIFIER LETTER SMALL J
+02B3;02B3;02B3;0072;0072; # (ʳ; ʳ; ʳ; r; r; ) MODIFIER LETTER SMALL R
+02B4;02B4;02B4;0279;0279; # (ʴ; ʴ; ʴ; ɹ; ɹ; ) MODIFIER LETTER SMALL TURNED R
+02B5;02B5;02B5;027B;027B; # (ʵ; ʵ; ʵ; ɻ; ɻ; ) MODIFIER LETTER SMALL TURNED R WITH HOOK
+02B6;02B6;02B6;0281;0281; # (ʶ; ʶ; ʶ; Ê; Ê; ) MODIFIER LETTER SMALL CAPITAL INVERTED R
+02B7;02B7;02B7;0077;0077; # (Ê·; Ê·; Ê·; w; w; ) MODIFIER LETTER SMALL W
+02B8;02B8;02B8;0079;0079; # (ʸ; ʸ; ʸ; y; y; ) MODIFIER LETTER SMALL Y
+02D8;02D8;02D8;0020 0306;0020 0306; # (˘; ˘; ˘; ◌̆; ◌̆; ) BREVE
+02D9;02D9;02D9;0020 0307;0020 0307; # (˙; ˙; ˙; ◌̇; ◌̇; ) DOT ABOVE
+02DA;02DA;02DA;0020 030A;0020 030A; # (˚; ˚; ˚; ◌̊; ◌̊; ) RING ABOVE
+02DB;02DB;02DB;0020 0328;0020 0328; # (˛; ˛; ˛; ◌̨; ◌̨; ) OGONEK
+02DC;02DC;02DC;0020 0303;0020 0303; # (˜; ˜; ˜; ◌̃; ◌̃; ) SMALL TILDE
+02DD;02DD;02DD;0020 030B;0020 030B; # (Ë; Ë; Ë; ◌̋; ◌̋; ) DOUBLE ACUTE ACCENT
+02E0;02E0;02E0;0263;0263; # (ˠ; ˠ; ˠ; ɣ; ɣ; ) MODIFIER LETTER SMALL GAMMA
+02E1;02E1;02E1;006C;006C; # (Ë¡; Ë¡; Ë¡; l; l; ) MODIFIER LETTER SMALL L
+02E2;02E2;02E2;0073;0073; # (ˢ; ˢ; ˢ; s; s; ) MODIFIER LETTER SMALL S
+02E3;02E3;02E3;0078;0078; # (ˣ; ˣ; ˣ; x; x; ) MODIFIER LETTER SMALL X
+02E4;02E4;02E4;0295;0295; # (ˤ; ˤ; ˤ; ʕ; ʕ; ) MODIFIER LETTER SMALL REVERSED GLOTTAL STOP
+0340;0300;0300;0300;0300; # (◌̀; ◌̀; ◌̀; ◌̀; ◌̀; ) COMBINING GRAVE TONE MARK
+0341;0301;0301;0301;0301; # (â—ŒÍ; â—ŒÌ; â—ŒÌ; â—ŒÌ; â—ŒÌ; ) COMBINING ACUTE TONE MARK
+0343;0313;0313;0313;0313; # (◌̓; ◌̓; ◌̓; ◌̓; ◌̓; ) COMBINING GREEK KORONIS
+0344;0308 0301;0308 0301;0308 0301;0308 0301; # (◌̈́; ◌̈◌Ì; ◌̈◌Ì; ◌̈◌Ì; ◌̈◌Ì; ) COMBINING GREEK DIALYTIKA TONOS
+0374;02B9;02B9;02B9;02B9; # (ʹ; ʹ; ʹ; ʹ; ʹ; ) GREEK NUMERAL SIGN
+037A;037A;037A;0020 0345;0020 0345; # (ͺ; ͺ; ͺ; ◌ͅ; ◌ͅ; ) GREEK YPOGEGRAMMENI
+037E;003B;003B;003B;003B; # (;; ;; ;; ;; ;; ) GREEK QUESTION MARK
+0384;0384;0384;0020 0301;0020 0301; # (΄; ΄; ΄; â—ŒÌ; â—ŒÌ; ) GREEK TONOS
+0385;0385;00A8 0301;0020 0308 0301;0020 0308 0301; # (Î…; Î…; ¨◌Ì; ◌̈◌Ì; ◌̈◌Ì; ) GREEK DIALYTIKA TONOS
+0386;0386;0391 0301;0386;0391 0301; # (Ά; Ά; Α◌Ì; Ά; Α◌Ì; ) GREEK CAPITAL LETTER ALPHA WITH TONOS
+0387;00B7;00B7;00B7;00B7; # (·; ·; ·; ·; ·; ) GREEK ANO TELEIA
+0388;0388;0395 0301;0388;0395 0301; # (Έ; Έ; Ε◌Ì; Έ; Ε◌Ì; ) GREEK CAPITAL LETTER EPSILON WITH TONOS
+0389;0389;0397 0301;0389;0397 0301; # (Ή; Ή; Η◌Ì; Ή; Η◌Ì; ) GREEK CAPITAL LETTER ETA WITH TONOS
+038A;038A;0399 0301;038A;0399 0301; # (Ί; Ί; Ι◌Ì; Ί; Ι◌Ì; ) GREEK CAPITAL LETTER IOTA WITH TONOS
+038C;038C;039F 0301;038C;039F 0301; # (ÎŒ; ÎŒ; Ο◌Ì; ÎŒ; Ο◌Ì; ) GREEK CAPITAL LETTER OMICRON WITH TONOS
+038E;038E;03A5 0301;038E;03A5 0301; # (ÎŽ; ÎŽ; Υ◌Ì; ÎŽ; Υ◌Ì; ) GREEK CAPITAL LETTER UPSILON WITH TONOS
+038F;038F;03A9 0301;038F;03A9 0301; # (Î; Î; Ω◌Ì; Î; Ω◌Ì; ) GREEK CAPITAL LETTER OMEGA WITH TONOS
+0390;0390;03B9 0308 0301;0390;03B9 0308 0301; # (Î; Î; ι◌̈◌Ì; Î; ι◌̈◌Ì; ) GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS
+03AA;03AA;0399 0308;03AA;0399 0308; # (Ϊ; Ϊ; Ι◌̈; Ϊ; Ι◌̈; ) GREEK CAPITAL LETTER IOTA WITH DIALYTIKA
+03AB;03AB;03A5 0308;03AB;03A5 0308; # (Ϋ; Ϋ; Υ◌̈; Ϋ; Υ◌̈; ) GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA
+03AC;03AC;03B1 0301;03AC;03B1 0301; # (ά; ά; α◌Ì; ά; α◌Ì; ) GREEK SMALL LETTER ALPHA WITH TONOS
+03AD;03AD;03B5 0301;03AD;03B5 0301; # (έ; έ; ε◌Ì; έ; ε◌Ì; ) GREEK SMALL LETTER EPSILON WITH TONOS
+03AE;03AE;03B7 0301;03AE;03B7 0301; # (ή; ή; η◌Ì; ή; η◌Ì; ) GREEK SMALL LETTER ETA WITH TONOS
+03AF;03AF;03B9 0301;03AF;03B9 0301; # (ί; ί; ι◌Ì; ί; ι◌Ì; ) GREEK SMALL LETTER IOTA WITH TONOS
+03B0;03B0;03C5 0308 0301;03B0;03C5 0308 0301; # (ΰ; ΰ; υ◌̈◌Ì; ΰ; υ◌̈◌Ì; ) GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS
+03CA;03CA;03B9 0308;03CA;03B9 0308; # (ϊ; ϊ; ι◌̈; ϊ; ι◌̈; ) GREEK SMALL LETTER IOTA WITH DIALYTIKA
+03CB;03CB;03C5 0308;03CB;03C5 0308; # (ϋ; ϋ; υ◌̈; ϋ; υ◌̈; ) GREEK SMALL LETTER UPSILON WITH DIALYTIKA
+03CC;03CC;03BF 0301;03CC;03BF 0301; # (ÏŒ; ÏŒ; ο◌Ì; ÏŒ; ο◌Ì; ) GREEK SMALL LETTER OMICRON WITH TONOS
+03CD;03CD;03C5 0301;03CD;03C5 0301; # (Ï; Ï; Ï…â—ŒÌ; Ï; Ï…â—ŒÌ; ) GREEK SMALL LETTER UPSILON WITH TONOS
+03CE;03CE;03C9 0301;03CE;03C9 0301; # (ÏŽ; ÏŽ; ω◌Ì; ÏŽ; ω◌Ì; ) GREEK SMALL LETTER OMEGA WITH TONOS
+03D0;03D0;03D0;03B2;03B2; # (Ï; Ï; Ï; β; β; ) GREEK BETA SYMBOL
+03D1;03D1;03D1;03B8;03B8; # (ϑ; ϑ; ϑ; θ; θ; ) GREEK THETA SYMBOL
+03D2;03D2;03D2;03A5;03A5; # (ϒ; ϒ; ϒ; Υ; Υ; ) GREEK UPSILON WITH HOOK SYMBOL
+03D3;03D3;03D2 0301;038E;03A5 0301; # (Ï“; Ï“; Ï’â—ŒÌ; ÎŽ; Υ◌Ì; ) GREEK UPSILON WITH ACUTE AND HOOK SYMBOL
+03D4;03D4;03D2 0308;03AB;03A5 0308; # (ϔ; ϔ; ϒ◌̈; Ϋ; Υ◌̈; ) GREEK UPSILON WITH DIAERESIS AND HOOK SYMBOL
+03D5;03D5;03D5;03C6;03C6; # (ϕ; ϕ; ϕ; φ; φ; ) GREEK PHI SYMBOL
+03D6;03D6;03D6;03C0;03C0; # (ϖ; ϖ; ϖ; π; π; ) GREEK PI SYMBOL
+03F0;03F0;03F0;03BA;03BA; # (ϰ; ϰ; ϰ; κ; κ; ) GREEK KAPPA SYMBOL
+03F1;03F1;03F1;03C1;03C1; # (ϱ; ϱ; ϱ; Ï; Ï; ) GREEK RHO SYMBOL
+03F2;03F2;03F2;03C2;03C2; # (ϲ; ϲ; ϲ; ς; ς; ) GREEK LUNATE SIGMA SYMBOL
+03F4;03F4;03F4;0398;0398; # (ϴ; ϴ; ϴ; Θ; Θ; ) GREEK CAPITAL THETA SYMBOL
+03F5;03F5;03F5;03B5;03B5; # (ϵ; ϵ; ϵ; ε; ε; ) GREEK LUNATE EPSILON SYMBOL
+03F9;03F9;03F9;03A3;03A3; # (Ϲ; Ϲ; Ϲ; Σ; Σ; ) GREEK CAPITAL LUNATE SIGMA SYMBOL
+0400;0400;0415 0300;0400;0415 0300; # (Ѐ; Ѐ; Е◌̀; Ѐ; Е◌̀; ) CYRILLIC CAPITAL LETTER IE WITH GRAVE
+0401;0401;0415 0308;0401;0415 0308; # (Ð; Ð; Е◌̈; Ð; Е◌̈; ) CYRILLIC CAPITAL LETTER IO
+0403;0403;0413 0301;0403;0413 0301; # (Ѓ; Ѓ; Г◌Ì; Ѓ; Г◌Ì; ) CYRILLIC CAPITAL LETTER GJE
+0407;0407;0406 0308;0407;0406 0308; # (Ї; Ї; І◌̈; Ї; І◌̈; ) CYRILLIC CAPITAL LETTER YI
+040C;040C;041A 0301;040C;041A 0301; # (ÐŒ; ÐŒ; К◌Ì; ÐŒ; К◌Ì; ) CYRILLIC CAPITAL LETTER KJE
+040D;040D;0418 0300;040D;0418 0300; # (Ð; Ð; И◌̀; Ð; И◌̀; ) CYRILLIC CAPITAL LETTER I WITH GRAVE
+040E;040E;0423 0306;040E;0423 0306; # (Ў; Ў; У◌̆; Ў; У◌̆; ) CYRILLIC CAPITAL LETTER SHORT U
+0419;0419;0418 0306;0419;0418 0306; # (Й; Й; И◌̆; Й; И◌̆; ) CYRILLIC CAPITAL LETTER SHORT I
+0439;0439;0438 0306;0439;0438 0306; # (й; й; и◌̆; й; и◌̆; ) CYRILLIC SMALL LETTER SHORT I
+0450;0450;0435 0300;0450;0435 0300; # (Ñ; Ñ; е◌̀; Ñ; е◌̀; ) CYRILLIC SMALL LETTER IE WITH GRAVE
+0451;0451;0435 0308;0451;0435 0308; # (ё; ё; е◌̈; ё; е◌̈; ) CYRILLIC SMALL LETTER IO
+0453;0453;0433 0301;0453;0433 0301; # (Ñ“; Ñ“; г◌Ì; Ñ“; г◌Ì; ) CYRILLIC SMALL LETTER GJE
+0457;0457;0456 0308;0457;0456 0308; # (ї; ї; і◌̈; ї; і◌̈; ) CYRILLIC SMALL LETTER YI
+045C;045C;043A 0301;045C;043A 0301; # (Ñœ; Ñœ; к◌Ì; Ñœ; к◌Ì; ) CYRILLIC SMALL LETTER KJE
+045D;045D;0438 0300;045D;0438 0300; # (Ñ; Ñ; и◌̀; Ñ; и◌̀; ) CYRILLIC SMALL LETTER I WITH GRAVE
+045E;045E;0443 0306;045E;0443 0306; # (ў; ў; у◌̆; ў; у◌̆; ) CYRILLIC SMALL LETTER SHORT U
+0476;0476;0474 030F;0476;0474 030F; # (Ѷ; Ѷ; Ñ´â—ŒÌ; Ѷ; Ñ´â—ŒÌ; ) CYRILLIC CAPITAL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT
+0477;0477;0475 030F;0477;0475 030F; # (Ñ·; Ñ·; ѵ◌Ì; Ñ·; ѵ◌Ì; ) CYRILLIC SMALL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT
+04C1;04C1;0416 0306;04C1;0416 0306; # (Ó; Ó; Ж◌̆; Ó; Ж◌̆; ) CYRILLIC CAPITAL LETTER ZHE WITH BREVE
+04C2;04C2;0436 0306;04C2;0436 0306; # (ӂ; ӂ; ж◌̆; ӂ; ж◌̆; ) CYRILLIC SMALL LETTER ZHE WITH BREVE
+04D0;04D0;0410 0306;04D0;0410 0306; # (Ó; Ó; Ð◌̆; Ó; Ð◌̆; ) CYRILLIC CAPITAL LETTER A WITH BREVE
+04D1;04D1;0430 0306;04D1;0430 0306; # (ӑ; ӑ; а◌̆; ӑ; а◌̆; ) CYRILLIC SMALL LETTER A WITH BREVE
+04D2;04D2;0410 0308;04D2;0410 0308; # (Ó’; Ó’; Ð◌̈; Ó’; Ð◌̈; ) CYRILLIC CAPITAL LETTER A WITH DIAERESIS
+04D3;04D3;0430 0308;04D3;0430 0308; # (ӓ; ӓ; а◌̈; ӓ; а◌̈; ) CYRILLIC SMALL LETTER A WITH DIAERESIS
+04D6;04D6;0415 0306;04D6;0415 0306; # (Ӗ; Ӗ; Е◌̆; Ӗ; Е◌̆; ) CYRILLIC CAPITAL LETTER IE WITH BREVE
+04D7;04D7;0435 0306;04D7;0435 0306; # (ӗ; ӗ; е◌̆; ӗ; е◌̆; ) CYRILLIC SMALL LETTER IE WITH BREVE
+04DA;04DA;04D8 0308;04DA;04D8 0308; # (Ӛ; Ӛ; Ә◌̈; Ӛ; Ә◌̈; ) CYRILLIC CAPITAL LETTER SCHWA WITH DIAERESIS
+04DB;04DB;04D9 0308;04DB;04D9 0308; # (ӛ; ӛ; ә◌̈; ӛ; ә◌̈; ) CYRILLIC SMALL LETTER SCHWA WITH DIAERESIS
+04DC;04DC;0416 0308;04DC;0416 0308; # (Ӝ; Ӝ; Ж◌̈; Ӝ; Ж◌̈; ) CYRILLIC CAPITAL LETTER ZHE WITH DIAERESIS
+04DD;04DD;0436 0308;04DD;0436 0308; # (Ó; Ó; ж◌̈; Ó; ж◌̈; ) CYRILLIC SMALL LETTER ZHE WITH DIAERESIS
+04DE;04DE;0417 0308;04DE;0417 0308; # (Ӟ; Ӟ; З◌̈; Ӟ; З◌̈; ) CYRILLIC CAPITAL LETTER ZE WITH DIAERESIS
+04DF;04DF;0437 0308;04DF;0437 0308; # (ӟ; ӟ; з◌̈; ӟ; з◌̈; ) CYRILLIC SMALL LETTER ZE WITH DIAERESIS
+04E2;04E2;0418 0304;04E2;0418 0304; # (Ӣ; Ӣ; И◌̄; Ӣ; И◌̄; ) CYRILLIC CAPITAL LETTER I WITH MACRON
+04E3;04E3;0438 0304;04E3;0438 0304; # (ӣ; ӣ; и◌̄; ӣ; и◌̄; ) CYRILLIC SMALL LETTER I WITH MACRON
+04E4;04E4;0418 0308;04E4;0418 0308; # (Ӥ; Ӥ; И◌̈; Ӥ; И◌̈; ) CYRILLIC CAPITAL LETTER I WITH DIAERESIS
+04E5;04E5;0438 0308;04E5;0438 0308; # (ӥ; ӥ; и◌̈; ӥ; и◌̈; ) CYRILLIC SMALL LETTER I WITH DIAERESIS
+04E6;04E6;041E 0308;04E6;041E 0308; # (Ӧ; Ӧ; О◌̈; Ӧ; О◌̈; ) CYRILLIC CAPITAL LETTER O WITH DIAERESIS
+04E7;04E7;043E 0308;04E7;043E 0308; # (ӧ; ӧ; о◌̈; ӧ; о◌̈; ) CYRILLIC SMALL LETTER O WITH DIAERESIS
+04EA;04EA;04E8 0308;04EA;04E8 0308; # (Ӫ; Ӫ; Ө◌̈; Ӫ; Ө◌̈; ) CYRILLIC CAPITAL LETTER BARRED O WITH DIAERESIS
+04EB;04EB;04E9 0308;04EB;04E9 0308; # (ӫ; ӫ; ө◌̈; ӫ; ө◌̈; ) CYRILLIC SMALL LETTER BARRED O WITH DIAERESIS
+04EC;04EC;042D 0308;04EC;042D 0308; # (Ӭ; Ӭ; Э◌̈; Ӭ; Э◌̈; ) CYRILLIC CAPITAL LETTER E WITH DIAERESIS
+04ED;04ED;044D 0308;04ED;044D 0308; # (Ó­; Ó­; Ñ◌̈; Ó­; Ñ◌̈; ) CYRILLIC SMALL LETTER E WITH DIAERESIS
+04EE;04EE;0423 0304;04EE;0423 0304; # (Ӯ; Ӯ; У◌̄; Ӯ; У◌̄; ) CYRILLIC CAPITAL LETTER U WITH MACRON
+04EF;04EF;0443 0304;04EF;0443 0304; # (ӯ; ӯ; у◌̄; ӯ; у◌̄; ) CYRILLIC SMALL LETTER U WITH MACRON
+04F0;04F0;0423 0308;04F0;0423 0308; # (Ӱ; Ӱ; У◌̈; Ӱ; У◌̈; ) CYRILLIC CAPITAL LETTER U WITH DIAERESIS
+04F1;04F1;0443 0308;04F1;0443 0308; # (ӱ; ӱ; у◌̈; ӱ; у◌̈; ) CYRILLIC SMALL LETTER U WITH DIAERESIS
+04F2;04F2;0423 030B;04F2;0423 030B; # (Ӳ; Ӳ; У◌̋; Ӳ; У◌̋; ) CYRILLIC CAPITAL LETTER U WITH DOUBLE ACUTE
+04F3;04F3;0443 030B;04F3;0443 030B; # (ӳ; ӳ; у◌̋; ӳ; у◌̋; ) CYRILLIC SMALL LETTER U WITH DOUBLE ACUTE
+04F4;04F4;0427 0308;04F4;0427 0308; # (Ӵ; Ӵ; Ч◌̈; Ӵ; Ч◌̈; ) CYRILLIC CAPITAL LETTER CHE WITH DIAERESIS
+04F5;04F5;0447 0308;04F5;0447 0308; # (ӵ; ӵ; ч◌̈; ӵ; ч◌̈; ) CYRILLIC SMALL LETTER CHE WITH DIAERESIS
+04F8;04F8;042B 0308;04F8;042B 0308; # (Ӹ; Ӹ; Ы◌̈; Ӹ; Ы◌̈; ) CYRILLIC CAPITAL LETTER YERU WITH DIAERESIS
+04F9;04F9;044B 0308;04F9;044B 0308; # (ӹ; ӹ; ы◌̈; ӹ; ы◌̈; ) CYRILLIC SMALL LETTER YERU WITH DIAERESIS
+0587;0587;0587;0565 0582;0565 0582; # (Ö‡; Ö‡; Ö‡; Õ¥Ö‚; Õ¥Ö‚; ) ARMENIAN SMALL LIGATURE ECH YIWN
+0622;0622;0627 0653;0622;0627 0653; # (آ; آ; ا◌ٓ; آ; ا◌ٓ; ) ARABIC LETTER ALEF WITH MADDA ABOVE
+0623;0623;0627 0654;0623;0627 0654; # (أ; أ; ا◌ٔ; أ; ا◌ٔ; ) ARABIC LETTER ALEF WITH HAMZA ABOVE
+0624;0624;0648 0654;0624;0648 0654; # (ؤ; ؤ; و◌ٔ; ؤ; و◌ٔ; ) ARABIC LETTER WAW WITH HAMZA ABOVE
+0625;0625;0627 0655;0625;0627 0655; # (إ; إ; ا◌ٕ; إ; ا◌ٕ; ) ARABIC LETTER ALEF WITH HAMZA BELOW
+0626;0626;064A 0654;0626;064A 0654; # (ئ; ئ; ي◌ٔ; ئ; ي◌ٔ; ) ARABIC LETTER YEH WITH HAMZA ABOVE
+0675;0675;0675;0627 0674;0627 0674; # (ٵ; ٵ; ٵ; اٴ; اٴ; ) ARABIC LETTER HIGH HAMZA ALEF
+0676;0676;0676;0648 0674;0648 0674; # (ٶ; ٶ; ٶ; وٴ; وٴ; ) ARABIC LETTER HIGH HAMZA WAW
+0677;0677;0677;06C7 0674;06C7 0674; # (Ù·; Ù·; Ù·; Û‡Ù´; Û‡Ù´; ) ARABIC LETTER U WITH HAMZA ABOVE
+0678;0678;0678;064A 0674;064A 0674; # (ٸ; ٸ; ٸ; يٴ; يٴ; ) ARABIC LETTER HIGH HAMZA YEH
+06C0;06C0;06D5 0654;06C0;06D5 0654; # (ۀ; ۀ; ە◌ٔ; ۀ; ە◌ٔ; ) ARABIC LETTER HEH WITH YEH ABOVE
+06C2;06C2;06C1 0654;06C2;06C1 0654; # (Û‚; Û‚; Û◌ٔ; Û‚; Û◌ٔ; ) ARABIC LETTER HEH GOAL WITH HAMZA ABOVE
+06D3;06D3;06D2 0654;06D3;06D2 0654; # (ۓ; ۓ; ے◌ٔ; ۓ; ے◌ٔ; ) ARABIC LETTER YEH BARREE WITH HAMZA ABOVE
+0929;0929;0928 093C;0929;0928 093C; # (ऩ; ऩ; न◌़; ऩ; न◌़; ) DEVANAGARI LETTER NNNA
+0931;0931;0930 093C;0931;0930 093C; # (ऱ; ऱ; र◌़; ऱ; र◌़; ) DEVANAGARI LETTER RRA
+0934;0934;0933 093C;0934;0933 093C; # (ऴ; ऴ; ळ◌़; ऴ; ळ◌़; ) DEVANAGARI LETTER LLLA
+0958;0915 093C;0915 093C;0915 093C;0915 093C; # (क़; क◌़; क◌़; क◌़; क◌़; ) DEVANAGARI LETTER QA
+0959;0916 093C;0916 093C;0916 093C;0916 093C; # (ख़; ख◌़; ख◌़; ख◌़; ख◌़; ) DEVANAGARI LETTER KHHA
+095A;0917 093C;0917 093C;0917 093C;0917 093C; # (ग़; ग◌़; ग◌़; ग◌़; ग◌़; ) DEVANAGARI LETTER GHHA
+095B;091C 093C;091C 093C;091C 093C;091C 093C; # (ज़; ज◌़; ज◌़; ज◌़; ज◌़; ) DEVANAGARI LETTER ZA
+095C;0921 093C;0921 093C;0921 093C;0921 093C; # (ड़; ड◌़; ड◌़; ड◌़; ड◌़; ) DEVANAGARI LETTER DDDHA
+095D;0922 093C;0922 093C;0922 093C;0922 093C; # (à¥; ढ◌़; ढ◌़; ढ◌़; ढ◌़; ) DEVANAGARI LETTER RHA
+095E;092B 093C;092B 093C;092B 093C;092B 093C; # (फ़; फ◌़; फ◌़; फ◌़; फ◌़; ) DEVANAGARI LETTER FA
+095F;092F 093C;092F 093C;092F 093C;092F 093C; # (य़; य◌़; य◌़; य◌़; य◌़; ) DEVANAGARI LETTER YYA
+09CB;09CB;09C7 09BE;09CB;09C7 09BE; # (ো; ো; ো; ো; ো; ) BENGALI VOWEL SIGN O
+09CC;09CC;09C7 09D7;09CC;09C7 09D7; # (ৌ; ৌ; ৌ; ৌ; ৌ; ) BENGALI VOWEL SIGN AU
+09DC;09A1 09BC;09A1 09BC;09A1 09BC;09A1 09BC; # (ড়; ড◌়; ড◌়; ড◌়; ড◌়; ) BENGALI LETTER RRA
+09DD;09A2 09BC;09A2 09BC;09A2 09BC;09A2 09BC; # (à§; ঢ◌়; ঢ◌়; ঢ◌়; ঢ◌়; ) BENGALI LETTER RHA
+09DF;09AF 09BC;09AF 09BC;09AF 09BC;09AF 09BC; # (য়; য◌়; য◌়; য◌়; য◌়; ) BENGALI LETTER YYA
+0A33;0A32 0A3C;0A32 0A3C;0A32 0A3C;0A32 0A3C; # (ਲ਼; ਲ◌਼; ਲ◌਼; ਲ◌਼; ਲ◌਼; ) GURMUKHI LETTER LLA
+0A36;0A38 0A3C;0A38 0A3C;0A38 0A3C;0A38 0A3C; # (ਸ਼; ਸ◌਼; ਸ◌਼; ਸ◌਼; ਸ◌਼; ) GURMUKHI LETTER SHA
+0A59;0A16 0A3C;0A16 0A3C;0A16 0A3C;0A16 0A3C; # (ਖ਼; ਖ◌਼; ਖ◌਼; ਖ◌਼; ਖ◌਼; ) GURMUKHI LETTER KHHA
+0A5A;0A17 0A3C;0A17 0A3C;0A17 0A3C;0A17 0A3C; # (ਗ਼; ਗ◌਼; ਗ◌਼; ਗ◌਼; ਗ◌਼; ) GURMUKHI LETTER GHHA
+0A5B;0A1C 0A3C;0A1C 0A3C;0A1C 0A3C;0A1C 0A3C; # (ਜ਼; ਜ◌਼; ਜ◌਼; ਜ◌਼; ਜ◌਼; ) GURMUKHI LETTER ZA
+0A5E;0A2B 0A3C;0A2B 0A3C;0A2B 0A3C;0A2B 0A3C; # (ਫ਼; ਫ◌਼; ਫ◌਼; ਫ◌਼; ਫ◌਼; ) GURMUKHI LETTER FA
+0B48;0B48;0B47 0B56;0B48;0B47 0B56; # (ୈ; ୈ; େ◌ୖ; ୈ; େ◌ୖ; ) ORIYA VOWEL SIGN AI
+0B4B;0B4B;0B47 0B3E;0B4B;0B47 0B3E; # (ୋ; ୋ; ୋ; ୋ; ୋ; ) ORIYA VOWEL SIGN O
+0B4C;0B4C;0B47 0B57;0B4C;0B47 0B57; # (ୌ; ୌ; ୌ; ୌ; ୌ; ) ORIYA VOWEL SIGN AU
+0B5C;0B21 0B3C;0B21 0B3C;0B21 0B3C;0B21 0B3C; # (ଡ଼; ଡ◌଼; ଡ◌଼; ଡ◌଼; ଡ◌଼; ) ORIYA LETTER RRA
+0B5D;0B22 0B3C;0B22 0B3C;0B22 0B3C;0B22 0B3C; # (à­; ଢ◌଼; ଢ◌଼; ଢ◌଼; ଢ◌଼; ) ORIYA LETTER RHA
+0B94;0B94;0B92 0BD7;0B94;0B92 0BD7; # (ஔ; ஔ; ஔ; ஔ; ஔ; ) TAMIL LETTER AU
+0BCA;0BCA;0BC6 0BBE;0BCA;0BC6 0BBE; # (ொ; ொ; ொ; ொ; ொ; ) TAMIL VOWEL SIGN O
+0BCB;0BCB;0BC7 0BBE;0BCB;0BC7 0BBE; # (ோ; ோ; ோ; ோ; ோ; ) TAMIL VOWEL SIGN OO
+0BCC;0BCC;0BC6 0BD7;0BCC;0BC6 0BD7; # (ௌ; ௌ; ௌ; ௌ; ௌ; ) TAMIL VOWEL SIGN AU
+0C48;0C48;0C46 0C56;0C48;0C46 0C56; # (◌ై; ◌ై; ◌ె◌ౖ; ◌ై; ◌ె◌ౖ; ) TELUGU VOWEL SIGN AI
+0CC0;0CC0;0CBF 0CD5;0CC0;0CBF 0CD5; # (ೀ; ೀ; ◌ೀ; ೀ; ◌ೀ; ) KANNADA VOWEL SIGN II
+0CC7;0CC7;0CC6 0CD5;0CC7;0CC6 0CD5; # (ೇ; ೇ; ◌ೇ; ೇ; ◌ೇ; ) KANNADA VOWEL SIGN EE
+0CC8;0CC8;0CC6 0CD6;0CC8;0CC6 0CD6; # (ೈ; ೈ; ◌ೈ; ೈ; ◌ೈ; ) KANNADA VOWEL SIGN AI
+0CCA;0CCA;0CC6 0CC2;0CCA;0CC6 0CC2; # (ೊ; ೊ; ◌ೊ; ೊ; ◌ೊ; ) KANNADA VOWEL SIGN O
+0CCB;0CCB;0CC6 0CC2 0CD5;0CCB;0CC6 0CC2 0CD5; # (ೋ; ೋ; ◌ೋ; ೋ; ◌ೋ; ) KANNADA VOWEL SIGN OO
+0D4A;0D4A;0D46 0D3E;0D4A;0D46 0D3E; # (ൊ; ൊ; ൊ; ൊ; ൊ; ) MALAYALAM VOWEL SIGN O
+0D4B;0D4B;0D47 0D3E;0D4B;0D47 0D3E; # (ോ; ോ; ോ; ോ; ോ; ) MALAYALAM VOWEL SIGN OO
+0D4C;0D4C;0D46 0D57;0D4C;0D46 0D57; # (ൌ; ൌ; ൌ; ൌ; ൌ; ) MALAYALAM VOWEL SIGN AU
+0DDA;0DDA;0DD9 0DCA;0DDA;0DD9 0DCA; # (ේ; ේ; ෙ◌්; ේ; ෙ◌්; ) SINHALA VOWEL SIGN DIGA KOMBUVA
+0DDC;0DDC;0DD9 0DCF;0DDC;0DD9 0DCF; # (à·œ; à·œ; à·™à·; à·œ; à·™à·; ) SINHALA VOWEL SIGN KOMBUVA HAA AELA-PILLA
+0DDD;0DDD;0DD9 0DCF 0DCA;0DDD;0DD9 0DCF 0DCA; # (à·; à·; à·™à·â—Œà·Š; à·; à·™à·â—Œà·Š; ) SINHALA VOWEL SIGN KOMBUVA HAA DIGA AELA-PILLA
+0DDE;0DDE;0DD9 0DDF;0DDE;0DD9 0DDF; # (ෞ; ෞ; ෞ; ෞ; ෞ; ) SINHALA VOWEL SIGN KOMBUVA HAA GAYANUKITTA
+0E33;0E33;0E33;0E4D 0E32;0E4D 0E32; # (ำ; ำ; ำ; â—Œà¹à¸²; â—Œà¹à¸²; ) THAI CHARACTER SARA AM
+0EB3;0EB3;0EB3;0ECD 0EB2;0ECD 0EB2; # (ຳ; ຳ; ຳ; â—Œà»àº²; â—Œà»àº²; ) LAO VOWEL SIGN AM
+0EDC;0EDC;0EDC;0EAB 0E99;0EAB 0E99; # (ໜ; ໜ; ໜ; ຫນ; ຫນ; ) LAO HO NO
+0EDD;0EDD;0EDD;0EAB 0EA1;0EAB 0EA1; # (à»; à»; à»; ຫມ; ຫມ; ) LAO HO MO
+0F0C;0F0C;0F0C;0F0B;0F0B; # (༌; ༌; ༌; ་; ་; ) TIBETAN MARK DELIMITER TSHEG BSTAR
+0F43;0F42 0FB7;0F42 0FB7;0F42 0FB7;0F42 0FB7; # (གྷ; ག◌ྷ; ག◌ྷ; ག◌ྷ; ག◌ྷ; ) TIBETAN LETTER GHA
+0F4D;0F4C 0FB7;0F4C 0FB7;0F4C 0FB7;0F4C 0FB7; # (à½; ཌ◌ྷ; ཌ◌ྷ; ཌ◌ྷ; ཌ◌ྷ; ) TIBETAN LETTER DDHA
+0F52;0F51 0FB7;0F51 0FB7;0F51 0FB7;0F51 0FB7; # (དྷ; ད◌ྷ; ད◌ྷ; ད◌ྷ; ད◌ྷ; ) TIBETAN LETTER DHA
+0F57;0F56 0FB7;0F56 0FB7;0F56 0FB7;0F56 0FB7; # (བྷ; བ◌ྷ; བ◌ྷ; བ◌ྷ; བ◌ྷ; ) TIBETAN LETTER BHA
+0F5C;0F5B 0FB7;0F5B 0FB7;0F5B 0FB7;0F5B 0FB7; # (ཛྷ; ཛ◌ྷ; ཛ◌ྷ; ཛ◌ྷ; ཛ◌ྷ; ) TIBETAN LETTER DZHA
+0F69;0F40 0FB5;0F40 0FB5;0F40 0FB5;0F40 0FB5; # (ཀྵ; ཀ◌ྵ; ཀ◌ྵ; ཀ◌ྵ; ཀ◌ྵ; ) TIBETAN LETTER KSSA
+0F73;0F71 0F72;0F71 0F72;0F71 0F72;0F71 0F72; # (◌ཱི; ◌ཱ◌ི; ◌ཱ◌ི; ◌ཱ◌ི; ◌ཱ◌ི; ) TIBETAN VOWEL SIGN II
+0F75;0F71 0F74;0F71 0F74;0F71 0F74;0F71 0F74; # (◌ཱུ; ◌ཱ◌ུ; ◌ཱ◌ུ; ◌ཱ◌ུ; ◌ཱ◌ུ; ) TIBETAN VOWEL SIGN UU
+0F76;0FB2 0F80;0FB2 0F80;0FB2 0F80;0FB2 0F80; # (◌ྲྀ; ◌ྲ◌ྀ; ◌ྲ◌ྀ; ◌ྲ◌ྀ; ◌ྲ◌ྀ; ) TIBETAN VOWEL SIGN VOCALIC R
+0F77;0F77;0F77;0FB2 0F71 0F80;0FB2 0F71 0F80; # (◌ཷ; ◌ཷ; ◌ཷ; ◌ྲ◌ཱ◌ྀ; ◌ྲ◌ཱ◌ྀ; ) TIBETAN VOWEL SIGN VOCALIC RR
+0F78;0FB3 0F80;0FB3 0F80;0FB3 0F80;0FB3 0F80; # (◌ླྀ; ◌ླ◌ྀ; ◌ླ◌ྀ; ◌ླ◌ྀ; ◌ླ◌ྀ; ) TIBETAN VOWEL SIGN VOCALIC L
+0F79;0F79;0F79;0FB3 0F71 0F80;0FB3 0F71 0F80; # (◌ཹ; ◌ཹ; ◌ཹ; ◌ླ◌ཱ◌ྀ; ◌ླ◌ཱ◌ྀ; ) TIBETAN VOWEL SIGN VOCALIC LL
+0F81;0F71 0F80;0F71 0F80;0F71 0F80;0F71 0F80; # (â—Œà¾; ◌ཱ◌ྀ; ◌ཱ◌ྀ; ◌ཱ◌ྀ; ◌ཱ◌ྀ; ) TIBETAN VOWEL SIGN REVERSED II
+0F93;0F92 0FB7;0F92 0FB7;0F92 0FB7;0F92 0FB7; # (◌ྒྷ; ◌ྒ◌ྷ; ◌ྒ◌ྷ; ◌ྒ◌ྷ; ◌ྒ◌ྷ; ) TIBETAN SUBJOINED LETTER GHA
+0F9D;0F9C 0FB7;0F9C 0FB7;0F9C 0FB7;0F9C 0FB7; # (â—Œà¾; ◌ྜ◌ྷ; ◌ྜ◌ྷ; ◌ྜ◌ྷ; ◌ྜ◌ྷ; ) TIBETAN SUBJOINED LETTER DDHA
+0FA2;0FA1 0FB7;0FA1 0FB7;0FA1 0FB7;0FA1 0FB7; # (◌ྡྷ; ◌ྡ◌ྷ; ◌ྡ◌ྷ; ◌ྡ◌ྷ; ◌ྡ◌ྷ; ) TIBETAN SUBJOINED LETTER DHA
+0FA7;0FA6 0FB7;0FA6 0FB7;0FA6 0FB7;0FA6 0FB7; # (◌ྦྷ; ◌ྦ◌ྷ; ◌ྦ◌ྷ; ◌ྦ◌ྷ; ◌ྦ◌ྷ; ) TIBETAN SUBJOINED LETTER BHA
+0FAC;0FAB 0FB7;0FAB 0FB7;0FAB 0FB7;0FAB 0FB7; # (◌ྫྷ; ◌ྫ◌ྷ; ◌ྫ◌ྷ; ◌ྫ◌ྷ; ◌ྫ◌ྷ; ) TIBETAN SUBJOINED LETTER DZHA
+0FB9;0F90 0FB5;0F90 0FB5;0F90 0FB5;0F90 0FB5; # (◌ྐྵ; â—Œà¾â—Œà¾µ; â—Œà¾â—Œà¾µ; â—Œà¾â—Œà¾µ; â—Œà¾â—Œà¾µ; ) TIBETAN SUBJOINED LETTER KSSA
+1026;1026;1025 102E;1026;1025 102E; # (ဦ; ဦ; ဥ◌ီ; ဦ; ဥ◌ီ; ) MYANMAR LETTER UU
+1D2C;1D2C;1D2C;0041;0041; # (á´¬; á´¬; á´¬; A; A; ) MODIFIER LETTER CAPITAL A
+1D2D;1D2D;1D2D;00C6;00C6; # (ᴭ; ᴭ; ᴭ; Æ; Æ; ) MODIFIER LETTER CAPITAL AE
+1D2E;1D2E;1D2E;0042;0042; # (á´®; á´®; á´®; B; B; ) MODIFIER LETTER CAPITAL B
+1D30;1D30;1D30;0044;0044; # (á´°; á´°; á´°; D; D; ) MODIFIER LETTER CAPITAL D
+1D31;1D31;1D31;0045;0045; # (á´±; á´±; á´±; E; E; ) MODIFIER LETTER CAPITAL E
+1D32;1D32;1D32;018E;018E; # (á´²; á´²; á´²; ÆŽ; ÆŽ; ) MODIFIER LETTER CAPITAL REVERSED E
+1D33;1D33;1D33;0047;0047; # (á´³; á´³; á´³; G; G; ) MODIFIER LETTER CAPITAL G
+1D34;1D34;1D34;0048;0048; # (á´´; á´´; á´´; H; H; ) MODIFIER LETTER CAPITAL H
+1D35;1D35;1D35;0049;0049; # (á´µ; á´µ; á´µ; I; I; ) MODIFIER LETTER CAPITAL I
+1D36;1D36;1D36;004A;004A; # (á´¶; á´¶; á´¶; J; J; ) MODIFIER LETTER CAPITAL J
+1D37;1D37;1D37;004B;004B; # (á´·; á´·; á´·; K; K; ) MODIFIER LETTER CAPITAL K
+1D38;1D38;1D38;004C;004C; # (á´¸; á´¸; á´¸; L; L; ) MODIFIER LETTER CAPITAL L
+1D39;1D39;1D39;004D;004D; # (á´¹; á´¹; á´¹; M; M; ) MODIFIER LETTER CAPITAL M
+1D3A;1D3A;1D3A;004E;004E; # (á´º; á´º; á´º; N; N; ) MODIFIER LETTER CAPITAL N
+1D3C;1D3C;1D3C;004F;004F; # (á´¼; á´¼; á´¼; O; O; ) MODIFIER LETTER CAPITAL O
+1D3D;1D3D;1D3D;0222;0222; # (ᴽ; ᴽ; ᴽ; Ȣ; Ȣ; ) MODIFIER LETTER CAPITAL OU
+1D3E;1D3E;1D3E;0050;0050; # (á´¾; á´¾; á´¾; P; P; ) MODIFIER LETTER CAPITAL P
+1D3F;1D3F;1D3F;0052;0052; # (á´¿; á´¿; á´¿; R; R; ) MODIFIER LETTER CAPITAL R
+1D40;1D40;1D40;0054;0054; # (áµ€; áµ€; áµ€; T; T; ) MODIFIER LETTER CAPITAL T
+1D41;1D41;1D41;0055;0055; # (áµ; áµ; áµ; U; U; ) MODIFIER LETTER CAPITAL U
+1D42;1D42;1D42;0057;0057; # (ᵂ; ᵂ; ᵂ; W; W; ) MODIFIER LETTER CAPITAL W
+1D43;1D43;1D43;0061;0061; # (ᵃ; ᵃ; ᵃ; a; a; ) MODIFIER LETTER SMALL A
+1D44;1D44;1D44;0250;0250; # (ᵄ; ᵄ; ᵄ; É; É; ) MODIFIER LETTER SMALL TURNED A
+1D45;1D45;1D45;0251;0251; # (áµ…; áµ…; áµ…; É‘; É‘; ) MODIFIER LETTER SMALL ALPHA
+1D46;1D46;1D46;1D02;1D02; # (ᵆ; ᵆ; ᵆ; ᴂ; ᴂ; ) MODIFIER LETTER SMALL TURNED AE
+1D47;1D47;1D47;0062;0062; # (ᵇ; ᵇ; ᵇ; b; b; ) MODIFIER LETTER SMALL B
+1D48;1D48;1D48;0064;0064; # (ᵈ; ᵈ; ᵈ; d; d; ) MODIFIER LETTER SMALL D
+1D49;1D49;1D49;0065;0065; # (ᵉ; ᵉ; ᵉ; e; e; ) MODIFIER LETTER SMALL E
+1D4A;1D4A;1D4A;0259;0259; # (ᵊ; ᵊ; ᵊ; ə; ə; ) MODIFIER LETTER SMALL SCHWA
+1D4B;1D4B;1D4B;025B;025B; # (ᵋ; ᵋ; ᵋ; ɛ; ɛ; ) MODIFIER LETTER SMALL OPEN E
+1D4C;1D4C;1D4C;025C;025C; # (ᵌ; ᵌ; ᵌ; ɜ; ɜ; ) MODIFIER LETTER SMALL TURNED OPEN E
+1D4D;1D4D;1D4D;0067;0067; # (áµ; áµ; áµ; g; g; ) MODIFIER LETTER SMALL G
+1D4F;1D4F;1D4F;006B;006B; # (áµ; áµ; áµ; k; k; ) MODIFIER LETTER SMALL K
+1D50;1D50;1D50;006D;006D; # (áµ; áµ; áµ; m; m; ) MODIFIER LETTER SMALL M
+1D51;1D51;1D51;014B;014B; # (ᵑ; ᵑ; ᵑ; ŋ; ŋ; ) MODIFIER LETTER SMALL ENG
+1D52;1D52;1D52;006F;006F; # (áµ’; áµ’; áµ’; o; o; ) MODIFIER LETTER SMALL O
+1D53;1D53;1D53;0254;0254; # (ᵓ; ᵓ; ᵓ; ɔ; ɔ; ) MODIFIER LETTER SMALL OPEN O
+1D54;1D54;1D54;1D16;1D16; # (áµ”; áµ”; áµ”; á´–; á´–; ) MODIFIER LETTER SMALL TOP HALF O
+1D55;1D55;1D55;1D17;1D17; # (ᵕ; ᵕ; ᵕ; ᴗ; ᴗ; ) MODIFIER LETTER SMALL BOTTOM HALF O
+1D56;1D56;1D56;0070;0070; # (áµ–; áµ–; áµ–; p; p; ) MODIFIER LETTER SMALL P
+1D57;1D57;1D57;0074;0074; # (áµ—; áµ—; áµ—; t; t; ) MODIFIER LETTER SMALL T
+1D58;1D58;1D58;0075;0075; # (ᵘ; ᵘ; ᵘ; u; u; ) MODIFIER LETTER SMALL U
+1D59;1D59;1D59;1D1D;1D1D; # (áµ™; áµ™; áµ™; á´; á´; ) MODIFIER LETTER SMALL SIDEWAYS U
+1D5A;1D5A;1D5A;026F;026F; # (ᵚ; ᵚ; ᵚ; ɯ; ɯ; ) MODIFIER LETTER SMALL TURNED M
+1D5B;1D5B;1D5B;0076;0076; # (áµ›; áµ›; áµ›; v; v; ) MODIFIER LETTER SMALL V
+1D5C;1D5C;1D5C;1D25;1D25; # (ᵜ; ᵜ; ᵜ; ᴥ; ᴥ; ) MODIFIER LETTER SMALL AIN
+1D5D;1D5D;1D5D;03B2;03B2; # (áµ; áµ; áµ; β; β; ) MODIFIER LETTER SMALL BETA
+1D5E;1D5E;1D5E;03B3;03B3; # (ᵞ; ᵞ; ᵞ; γ; γ; ) MODIFIER LETTER SMALL GREEK GAMMA
+1D5F;1D5F;1D5F;03B4;03B4; # (ᵟ; ᵟ; ᵟ; δ; δ; ) MODIFIER LETTER SMALL DELTA
+1D60;1D60;1D60;03C6;03C6; # (ᵠ; ᵠ; ᵠ; φ; φ; ) MODIFIER LETTER SMALL GREEK PHI
+1D61;1D61;1D61;03C7;03C7; # (ᵡ; ᵡ; ᵡ; χ; χ; ) MODIFIER LETTER SMALL CHI
+1D62;1D62;1D62;0069;0069; # (áµ¢; áµ¢; áµ¢; i; i; ) LATIN SUBSCRIPT SMALL LETTER I
+1D63;1D63;1D63;0072;0072; # (áµ£; áµ£; áµ£; r; r; ) LATIN SUBSCRIPT SMALL LETTER R
+1D64;1D64;1D64;0075;0075; # (ᵤ; ᵤ; ᵤ; u; u; ) LATIN SUBSCRIPT SMALL LETTER U
+1D65;1D65;1D65;0076;0076; # (áµ¥; áµ¥; áµ¥; v; v; ) LATIN SUBSCRIPT SMALL LETTER V
+1D66;1D66;1D66;03B2;03B2; # (ᵦ; ᵦ; ᵦ; β; β; ) GREEK SUBSCRIPT SMALL LETTER BETA
+1D67;1D67;1D67;03B3;03B3; # (ᵧ; ᵧ; ᵧ; γ; γ; ) GREEK SUBSCRIPT SMALL LETTER GAMMA
+1D68;1D68;1D68;03C1;03C1; # (ᵨ; ᵨ; ᵨ; Ï; Ï; ) GREEK SUBSCRIPT SMALL LETTER RHO
+1D69;1D69;1D69;03C6;03C6; # (ᵩ; ᵩ; ᵩ; φ; φ; ) GREEK SUBSCRIPT SMALL LETTER PHI
+1D6A;1D6A;1D6A;03C7;03C7; # (ᵪ; ᵪ; ᵪ; χ; χ; ) GREEK SUBSCRIPT SMALL LETTER CHI
+1E00;1E00;0041 0325;1E00;0041 0325; # (Ḁ; Ḁ; A◌̥; Ḁ; A◌̥; ) LATIN CAPITAL LETTER A WITH RING BELOW
+1E01;1E01;0061 0325;1E01;0061 0325; # (á¸; á¸; a◌̥; á¸; a◌̥; ) LATIN SMALL LETTER A WITH RING BELOW
+1E02;1E02;0042 0307;1E02;0042 0307; # (Ḃ; Ḃ; B◌̇; Ḃ; B◌̇; ) LATIN CAPITAL LETTER B WITH DOT ABOVE
+1E03;1E03;0062 0307;1E03;0062 0307; # (ḃ; ḃ; b◌̇; ḃ; b◌̇; ) LATIN SMALL LETTER B WITH DOT ABOVE
+1E04;1E04;0042 0323;1E04;0042 0323; # (Ḅ; Ḅ; B◌̣; Ḅ; B◌̣; ) LATIN CAPITAL LETTER B WITH DOT BELOW
+1E05;1E05;0062 0323;1E05;0062 0323; # (ḅ; ḅ; b◌̣; ḅ; b◌̣; ) LATIN SMALL LETTER B WITH DOT BELOW
+1E06;1E06;0042 0331;1E06;0042 0331; # (Ḇ; Ḇ; B◌̱; Ḇ; B◌̱; ) LATIN CAPITAL LETTER B WITH LINE BELOW
+1E07;1E07;0062 0331;1E07;0062 0331; # (ḇ; ḇ; b◌̱; ḇ; b◌̱; ) LATIN SMALL LETTER B WITH LINE BELOW
+1E08;1E08;0043 0327 0301;1E08;0043 0327 0301; # (Ḉ; Ḉ; C◌̧◌Ì; Ḉ; C◌̧◌Ì; ) LATIN CAPITAL LETTER C WITH CEDILLA AND ACUTE
+1E09;1E09;0063 0327 0301;1E09;0063 0327 0301; # (ḉ; ḉ; c◌̧◌Ì; ḉ; c◌̧◌Ì; ) LATIN SMALL LETTER C WITH CEDILLA AND ACUTE
+1E0A;1E0A;0044 0307;1E0A;0044 0307; # (Ḋ; Ḋ; D◌̇; Ḋ; D◌̇; ) LATIN CAPITAL LETTER D WITH DOT ABOVE
+1E0B;1E0B;0064 0307;1E0B;0064 0307; # (ḋ; ḋ; d◌̇; ḋ; d◌̇; ) LATIN SMALL LETTER D WITH DOT ABOVE
+1E0C;1E0C;0044 0323;1E0C;0044 0323; # (Ḍ; Ḍ; D◌̣; Ḍ; D◌̣; ) LATIN CAPITAL LETTER D WITH DOT BELOW
+1E0D;1E0D;0064 0323;1E0D;0064 0323; # (á¸; á¸; d◌̣; á¸; d◌̣; ) LATIN SMALL LETTER D WITH DOT BELOW
+1E0E;1E0E;0044 0331;1E0E;0044 0331; # (Ḏ; Ḏ; D◌̱; Ḏ; D◌̱; ) LATIN CAPITAL LETTER D WITH LINE BELOW
+1E0F;1E0F;0064 0331;1E0F;0064 0331; # (á¸; á¸; d◌̱; á¸; d◌̱; ) LATIN SMALL LETTER D WITH LINE BELOW
+1E10;1E10;0044 0327;1E10;0044 0327; # (á¸; á¸; D◌̧; á¸; D◌̧; ) LATIN CAPITAL LETTER D WITH CEDILLA
+1E11;1E11;0064 0327;1E11;0064 0327; # (ḑ; ḑ; d◌̧; ḑ; d◌̧; ) LATIN SMALL LETTER D WITH CEDILLA
+1E12;1E12;0044 032D;1E12;0044 032D; # (Ḓ; Ḓ; D◌̭; Ḓ; D◌̭; ) LATIN CAPITAL LETTER D WITH CIRCUMFLEX BELOW
+1E13;1E13;0064 032D;1E13;0064 032D; # (ḓ; ḓ; d◌̭; ḓ; d◌̭; ) LATIN SMALL LETTER D WITH CIRCUMFLEX BELOW
+1E14;1E14;0045 0304 0300;1E14;0045 0304 0300; # (Ḕ; Ḕ; E◌̄◌̀; Ḕ; E◌̄◌̀; ) LATIN CAPITAL LETTER E WITH MACRON AND GRAVE
+1E15;1E15;0065 0304 0300;1E15;0065 0304 0300; # (ḕ; ḕ; e◌̄◌̀; ḕ; e◌̄◌̀; ) LATIN SMALL LETTER E WITH MACRON AND GRAVE
+1E16;1E16;0045 0304 0301;1E16;0045 0304 0301; # (Ḗ; Ḗ; E◌̄◌Ì; Ḗ; E◌̄◌Ì; ) LATIN CAPITAL LETTER E WITH MACRON AND ACUTE
+1E17;1E17;0065 0304 0301;1E17;0065 0304 0301; # (ḗ; ḗ; e◌̄◌Ì; ḗ; e◌̄◌Ì; ) LATIN SMALL LETTER E WITH MACRON AND ACUTE
+1E18;1E18;0045 032D;1E18;0045 032D; # (Ḙ; Ḙ; E◌̭; Ḙ; E◌̭; ) LATIN CAPITAL LETTER E WITH CIRCUMFLEX BELOW
+1E19;1E19;0065 032D;1E19;0065 032D; # (ḙ; ḙ; e◌̭; ḙ; e◌̭; ) LATIN SMALL LETTER E WITH CIRCUMFLEX BELOW
+1E1A;1E1A;0045 0330;1E1A;0045 0330; # (Ḛ; Ḛ; E◌̰; Ḛ; E◌̰; ) LATIN CAPITAL LETTER E WITH TILDE BELOW
+1E1B;1E1B;0065 0330;1E1B;0065 0330; # (ḛ; ḛ; e◌̰; ḛ; e◌̰; ) LATIN SMALL LETTER E WITH TILDE BELOW
+1E1C;1E1C;0045 0327 0306;1E1C;0045 0327 0306; # (Ḝ; Ḝ; E◌̧◌̆; Ḝ; E◌̧◌̆; ) LATIN CAPITAL LETTER E WITH CEDILLA AND BREVE
+1E1D;1E1D;0065 0327 0306;1E1D;0065 0327 0306; # (á¸; á¸; e◌̧◌̆; á¸; e◌̧◌̆; ) LATIN SMALL LETTER E WITH CEDILLA AND BREVE
+1E1E;1E1E;0046 0307;1E1E;0046 0307; # (Ḟ; Ḟ; F◌̇; Ḟ; F◌̇; ) LATIN CAPITAL LETTER F WITH DOT ABOVE
+1E1F;1E1F;0066 0307;1E1F;0066 0307; # (ḟ; ḟ; f◌̇; ḟ; f◌̇; ) LATIN SMALL LETTER F WITH DOT ABOVE
+1E20;1E20;0047 0304;1E20;0047 0304; # (Ḡ; Ḡ; G◌̄; Ḡ; G◌̄; ) LATIN CAPITAL LETTER G WITH MACRON
+1E21;1E21;0067 0304;1E21;0067 0304; # (ḡ; ḡ; g◌̄; ḡ; g◌̄; ) LATIN SMALL LETTER G WITH MACRON
+1E22;1E22;0048 0307;1E22;0048 0307; # (Ḣ; Ḣ; H◌̇; Ḣ; H◌̇; ) LATIN CAPITAL LETTER H WITH DOT ABOVE
+1E23;1E23;0068 0307;1E23;0068 0307; # (ḣ; ḣ; h◌̇; ḣ; h◌̇; ) LATIN SMALL LETTER H WITH DOT ABOVE
+1E24;1E24;0048 0323;1E24;0048 0323; # (Ḥ; Ḥ; H◌̣; Ḥ; H◌̣; ) LATIN CAPITAL LETTER H WITH DOT BELOW
+1E25;1E25;0068 0323;1E25;0068 0323; # (ḥ; ḥ; h◌̣; ḥ; h◌̣; ) LATIN SMALL LETTER H WITH DOT BELOW
+1E26;1E26;0048 0308;1E26;0048 0308; # (Ḧ; Ḧ; H◌̈; Ḧ; H◌̈; ) LATIN CAPITAL LETTER H WITH DIAERESIS
+1E27;1E27;0068 0308;1E27;0068 0308; # (ḧ; ḧ; h◌̈; ḧ; h◌̈; ) LATIN SMALL LETTER H WITH DIAERESIS
+1E28;1E28;0048 0327;1E28;0048 0327; # (Ḩ; Ḩ; H◌̧; Ḩ; H◌̧; ) LATIN CAPITAL LETTER H WITH CEDILLA
+1E29;1E29;0068 0327;1E29;0068 0327; # (ḩ; ḩ; h◌̧; ḩ; h◌̧; ) LATIN SMALL LETTER H WITH CEDILLA
+1E2A;1E2A;0048 032E;1E2A;0048 032E; # (Ḫ; Ḫ; H◌̮; Ḫ; H◌̮; ) LATIN CAPITAL LETTER H WITH BREVE BELOW
+1E2B;1E2B;0068 032E;1E2B;0068 032E; # (ḫ; ḫ; h◌̮; ḫ; h◌̮; ) LATIN SMALL LETTER H WITH BREVE BELOW
+1E2C;1E2C;0049 0330;1E2C;0049 0330; # (Ḭ; Ḭ; I◌̰; Ḭ; I◌̰; ) LATIN CAPITAL LETTER I WITH TILDE BELOW
+1E2D;1E2D;0069 0330;1E2D;0069 0330; # (ḭ; ḭ; i◌̰; ḭ; i◌̰; ) LATIN SMALL LETTER I WITH TILDE BELOW
+1E2E;1E2E;0049 0308 0301;1E2E;0049 0308 0301; # (Ḯ; Ḯ; I◌̈◌Ì; Ḯ; I◌̈◌Ì; ) LATIN CAPITAL LETTER I WITH DIAERESIS AND ACUTE
+1E2F;1E2F;0069 0308 0301;1E2F;0069 0308 0301; # (ḯ; ḯ; i◌̈◌Ì; ḯ; i◌̈◌Ì; ) LATIN SMALL LETTER I WITH DIAERESIS AND ACUTE
+1E30;1E30;004B 0301;1E30;004B 0301; # (Ḱ; Ḱ; Kâ—ŒÌ; Ḱ; Kâ—ŒÌ; ) LATIN CAPITAL LETTER K WITH ACUTE
+1E31;1E31;006B 0301;1E31;006B 0301; # (ḱ; ḱ; kâ—ŒÌ; ḱ; kâ—ŒÌ; ) LATIN SMALL LETTER K WITH ACUTE
+1E32;1E32;004B 0323;1E32;004B 0323; # (Ḳ; Ḳ; K◌̣; Ḳ; K◌̣; ) LATIN CAPITAL LETTER K WITH DOT BELOW
+1E33;1E33;006B 0323;1E33;006B 0323; # (ḳ; ḳ; k◌̣; ḳ; k◌̣; ) LATIN SMALL LETTER K WITH DOT BELOW
+1E34;1E34;004B 0331;1E34;004B 0331; # (Ḵ; Ḵ; K◌̱; Ḵ; K◌̱; ) LATIN CAPITAL LETTER K WITH LINE BELOW
+1E35;1E35;006B 0331;1E35;006B 0331; # (ḵ; ḵ; k◌̱; ḵ; k◌̱; ) LATIN SMALL LETTER K WITH LINE BELOW
+1E36;1E36;004C 0323;1E36;004C 0323; # (Ḷ; Ḷ; L◌̣; Ḷ; L◌̣; ) LATIN CAPITAL LETTER L WITH DOT BELOW
+1E37;1E37;006C 0323;1E37;006C 0323; # (ḷ; ḷ; l◌̣; ḷ; l◌̣; ) LATIN SMALL LETTER L WITH DOT BELOW
+1E38;1E38;004C 0323 0304;1E38;004C 0323 0304; # (Ḹ; Ḹ; L◌̣◌̄; Ḹ; L◌̣◌̄; ) LATIN CAPITAL LETTER L WITH DOT BELOW AND MACRON
+1E39;1E39;006C 0323 0304;1E39;006C 0323 0304; # (ḹ; ḹ; l◌̣◌̄; ḹ; l◌̣◌̄; ) LATIN SMALL LETTER L WITH DOT BELOW AND MACRON
+1E3A;1E3A;004C 0331;1E3A;004C 0331; # (Ḻ; Ḻ; L◌̱; Ḻ; L◌̱; ) LATIN CAPITAL LETTER L WITH LINE BELOW
+1E3B;1E3B;006C 0331;1E3B;006C 0331; # (ḻ; ḻ; l◌̱; ḻ; l◌̱; ) LATIN SMALL LETTER L WITH LINE BELOW
+1E3C;1E3C;004C 032D;1E3C;004C 032D; # (Ḽ; Ḽ; L◌̭; Ḽ; L◌̭; ) LATIN CAPITAL LETTER L WITH CIRCUMFLEX BELOW
+1E3D;1E3D;006C 032D;1E3D;006C 032D; # (ḽ; ḽ; l◌̭; ḽ; l◌̭; ) LATIN SMALL LETTER L WITH CIRCUMFLEX BELOW
+1E3E;1E3E;004D 0301;1E3E;004D 0301; # (Ḿ; Ḿ; Mâ—ŒÌ; Ḿ; Mâ—ŒÌ; ) LATIN CAPITAL LETTER M WITH ACUTE
+1E3F;1E3F;006D 0301;1E3F;006D 0301; # (ḿ; ḿ; mâ—ŒÌ; ḿ; mâ—ŒÌ; ) LATIN SMALL LETTER M WITH ACUTE
+1E40;1E40;004D 0307;1E40;004D 0307; # (Ṁ; Ṁ; M◌̇; Ṁ; M◌̇; ) LATIN CAPITAL LETTER M WITH DOT ABOVE
+1E41;1E41;006D 0307;1E41;006D 0307; # (á¹; á¹; m◌̇; á¹; m◌̇; ) LATIN SMALL LETTER M WITH DOT ABOVE
+1E42;1E42;004D 0323;1E42;004D 0323; # (Ṃ; Ṃ; M◌̣; Ṃ; M◌̣; ) LATIN CAPITAL LETTER M WITH DOT BELOW
+1E43;1E43;006D 0323;1E43;006D 0323; # (ṃ; ṃ; m◌̣; ṃ; m◌̣; ) LATIN SMALL LETTER M WITH DOT BELOW
+1E44;1E44;004E 0307;1E44;004E 0307; # (Ṅ; Ṅ; N◌̇; Ṅ; N◌̇; ) LATIN CAPITAL LETTER N WITH DOT ABOVE
+1E45;1E45;006E 0307;1E45;006E 0307; # (ṅ; ṅ; n◌̇; ṅ; n◌̇; ) LATIN SMALL LETTER N WITH DOT ABOVE
+1E46;1E46;004E 0323;1E46;004E 0323; # (Ṇ; Ṇ; N◌̣; Ṇ; N◌̣; ) LATIN CAPITAL LETTER N WITH DOT BELOW
+1E47;1E47;006E 0323;1E47;006E 0323; # (ṇ; ṇ; n◌̣; ṇ; n◌̣; ) LATIN SMALL LETTER N WITH DOT BELOW
+1E48;1E48;004E 0331;1E48;004E 0331; # (Ṉ; Ṉ; N◌̱; Ṉ; N◌̱; ) LATIN CAPITAL LETTER N WITH LINE BELOW
+1E49;1E49;006E 0331;1E49;006E 0331; # (ṉ; ṉ; n◌̱; ṉ; n◌̱; ) LATIN SMALL LETTER N WITH LINE BELOW
+1E4A;1E4A;004E 032D;1E4A;004E 032D; # (Ṋ; Ṋ; N◌̭; Ṋ; N◌̭; ) LATIN CAPITAL LETTER N WITH CIRCUMFLEX BELOW
+1E4B;1E4B;006E 032D;1E4B;006E 032D; # (ṋ; ṋ; n◌̭; ṋ; n◌̭; ) LATIN SMALL LETTER N WITH CIRCUMFLEX BELOW
+1E4C;1E4C;004F 0303 0301;1E4C;004F 0303 0301; # (Ṍ; Ṍ; O◌̃◌Ì; Ṍ; O◌̃◌Ì; ) LATIN CAPITAL LETTER O WITH TILDE AND ACUTE
+1E4D;1E4D;006F 0303 0301;1E4D;006F 0303 0301; # (á¹; á¹; o◌̃◌Ì; á¹; o◌̃◌Ì; ) LATIN SMALL LETTER O WITH TILDE AND ACUTE
+1E4E;1E4E;004F 0303 0308;1E4E;004F 0303 0308; # (Ṏ; Ṏ; O◌̃◌̈; Ṏ; O◌̃◌̈; ) LATIN CAPITAL LETTER O WITH TILDE AND DIAERESIS
+1E4F;1E4F;006F 0303 0308;1E4F;006F 0303 0308; # (á¹; á¹; o◌̃◌̈; á¹; o◌̃◌̈; ) LATIN SMALL LETTER O WITH TILDE AND DIAERESIS
+1E50;1E50;004F 0304 0300;1E50;004F 0304 0300; # (á¹; á¹; O◌̄◌̀; á¹; O◌̄◌̀; ) LATIN CAPITAL LETTER O WITH MACRON AND GRAVE
+1E51;1E51;006F 0304 0300;1E51;006F 0304 0300; # (ṑ; ṑ; o◌̄◌̀; ṑ; o◌̄◌̀; ) LATIN SMALL LETTER O WITH MACRON AND GRAVE
+1E52;1E52;004F 0304 0301;1E52;004F 0304 0301; # (á¹’; á¹’; O◌̄◌Ì; á¹’; O◌̄◌Ì; ) LATIN CAPITAL LETTER O WITH MACRON AND ACUTE
+1E53;1E53;006F 0304 0301;1E53;006F 0304 0301; # (ṓ; ṓ; o◌̄◌Ì; ṓ; o◌̄◌Ì; ) LATIN SMALL LETTER O WITH MACRON AND ACUTE
+1E54;1E54;0050 0301;1E54;0050 0301; # (á¹”; á¹”; Pâ—ŒÌ; á¹”; Pâ—ŒÌ; ) LATIN CAPITAL LETTER P WITH ACUTE
+1E55;1E55;0070 0301;1E55;0070 0301; # (ṕ; ṕ; pâ—ŒÌ; ṕ; pâ—ŒÌ; ) LATIN SMALL LETTER P WITH ACUTE
+1E56;1E56;0050 0307;1E56;0050 0307; # (Ṗ; Ṗ; P◌̇; Ṗ; P◌̇; ) LATIN CAPITAL LETTER P WITH DOT ABOVE
+1E57;1E57;0070 0307;1E57;0070 0307; # (ṗ; ṗ; p◌̇; ṗ; p◌̇; ) LATIN SMALL LETTER P WITH DOT ABOVE
+1E58;1E58;0052 0307;1E58;0052 0307; # (Ṙ; Ṙ; R◌̇; Ṙ; R◌̇; ) LATIN CAPITAL LETTER R WITH DOT ABOVE
+1E59;1E59;0072 0307;1E59;0072 0307; # (ṙ; ṙ; r◌̇; ṙ; r◌̇; ) LATIN SMALL LETTER R WITH DOT ABOVE
+1E5A;1E5A;0052 0323;1E5A;0052 0323; # (Ṛ; Ṛ; R◌̣; Ṛ; R◌̣; ) LATIN CAPITAL LETTER R WITH DOT BELOW
+1E5B;1E5B;0072 0323;1E5B;0072 0323; # (ṛ; ṛ; r◌̣; ṛ; r◌̣; ) LATIN SMALL LETTER R WITH DOT BELOW
+1E5C;1E5C;0052 0323 0304;1E5C;0052 0323 0304; # (Ṝ; Ṝ; R◌̣◌̄; Ṝ; R◌̣◌̄; ) LATIN CAPITAL LETTER R WITH DOT BELOW AND MACRON
+1E5D;1E5D;0072 0323 0304;1E5D;0072 0323 0304; # (á¹; á¹; r◌̣◌̄; á¹; r◌̣◌̄; ) LATIN SMALL LETTER R WITH DOT BELOW AND MACRON
+1E5E;1E5E;0052 0331;1E5E;0052 0331; # (Ṟ; Ṟ; R◌̱; Ṟ; R◌̱; ) LATIN CAPITAL LETTER R WITH LINE BELOW
+1E5F;1E5F;0072 0331;1E5F;0072 0331; # (ṟ; ṟ; r◌̱; ṟ; r◌̱; ) LATIN SMALL LETTER R WITH LINE BELOW
+1E60;1E60;0053 0307;1E60;0053 0307; # (Ṡ; Ṡ; S◌̇; Ṡ; S◌̇; ) LATIN CAPITAL LETTER S WITH DOT ABOVE
+1E61;1E61;0073 0307;1E61;0073 0307; # (ṡ; ṡ; s◌̇; ṡ; s◌̇; ) LATIN SMALL LETTER S WITH DOT ABOVE
+1E62;1E62;0053 0323;1E62;0053 0323; # (Ṣ; Ṣ; S◌̣; Ṣ; S◌̣; ) LATIN CAPITAL LETTER S WITH DOT BELOW
+1E63;1E63;0073 0323;1E63;0073 0323; # (ṣ; ṣ; s◌̣; ṣ; s◌̣; ) LATIN SMALL LETTER S WITH DOT BELOW
+1E64;1E64;0053 0301 0307;1E64;0053 0301 0307; # (Ṥ; Ṥ; Sâ—ŒÌ◌̇; Ṥ; Sâ—ŒÌ◌̇; ) LATIN CAPITAL LETTER S WITH ACUTE AND DOT ABOVE
+1E65;1E65;0073 0301 0307;1E65;0073 0301 0307; # (á¹¥; á¹¥; sâ—ŒÌ◌̇; á¹¥; sâ—ŒÌ◌̇; ) LATIN SMALL LETTER S WITH ACUTE AND DOT ABOVE
+1E66;1E66;0053 030C 0307;1E66;0053 030C 0307; # (Ṧ; Ṧ; S◌̌◌̇; Ṧ; S◌̌◌̇; ) LATIN CAPITAL LETTER S WITH CARON AND DOT ABOVE
+1E67;1E67;0073 030C 0307;1E67;0073 030C 0307; # (ṧ; ṧ; s◌̌◌̇; ṧ; s◌̌◌̇; ) LATIN SMALL LETTER S WITH CARON AND DOT ABOVE
+1E68;1E68;0053 0323 0307;1E68;0053 0323 0307; # (Ṩ; Ṩ; S◌̣◌̇; Ṩ; S◌̣◌̇; ) LATIN CAPITAL LETTER S WITH DOT BELOW AND DOT ABOVE
+1E69;1E69;0073 0323 0307;1E69;0073 0323 0307; # (ṩ; ṩ; s◌̣◌̇; ṩ; s◌̣◌̇; ) LATIN SMALL LETTER S WITH DOT BELOW AND DOT ABOVE
+1E6A;1E6A;0054 0307;1E6A;0054 0307; # (Ṫ; Ṫ; T◌̇; Ṫ; T◌̇; ) LATIN CAPITAL LETTER T WITH DOT ABOVE
+1E6B;1E6B;0074 0307;1E6B;0074 0307; # (ṫ; ṫ; t◌̇; ṫ; t◌̇; ) LATIN SMALL LETTER T WITH DOT ABOVE
+1E6C;1E6C;0054 0323;1E6C;0054 0323; # (Ṭ; Ṭ; T◌̣; Ṭ; T◌̣; ) LATIN CAPITAL LETTER T WITH DOT BELOW
+1E6D;1E6D;0074 0323;1E6D;0074 0323; # (ṭ; ṭ; t◌̣; ṭ; t◌̣; ) LATIN SMALL LETTER T WITH DOT BELOW
+1E6E;1E6E;0054 0331;1E6E;0054 0331; # (Ṯ; Ṯ; T◌̱; Ṯ; T◌̱; ) LATIN CAPITAL LETTER T WITH LINE BELOW
+1E6F;1E6F;0074 0331;1E6F;0074 0331; # (ṯ; ṯ; t◌̱; ṯ; t◌̱; ) LATIN SMALL LETTER T WITH LINE BELOW
+1E70;1E70;0054 032D;1E70;0054 032D; # (Ṱ; Ṱ; T◌̭; Ṱ; T◌̭; ) LATIN CAPITAL LETTER T WITH CIRCUMFLEX BELOW
+1E71;1E71;0074 032D;1E71;0074 032D; # (ṱ; ṱ; t◌̭; ṱ; t◌̭; ) LATIN SMALL LETTER T WITH CIRCUMFLEX BELOW
+1E72;1E72;0055 0324;1E72;0055 0324; # (Ṳ; Ṳ; U◌̤; Ṳ; U◌̤; ) LATIN CAPITAL LETTER U WITH DIAERESIS BELOW
+1E73;1E73;0075 0324;1E73;0075 0324; # (ṳ; ṳ; u◌̤; ṳ; u◌̤; ) LATIN SMALL LETTER U WITH DIAERESIS BELOW
+1E74;1E74;0055 0330;1E74;0055 0330; # (Ṵ; Ṵ; U◌̰; Ṵ; U◌̰; ) LATIN CAPITAL LETTER U WITH TILDE BELOW
+1E75;1E75;0075 0330;1E75;0075 0330; # (ṵ; ṵ; u◌̰; ṵ; u◌̰; ) LATIN SMALL LETTER U WITH TILDE BELOW
+1E76;1E76;0055 032D;1E76;0055 032D; # (Ṷ; Ṷ; U◌̭; Ṷ; U◌̭; ) LATIN CAPITAL LETTER U WITH CIRCUMFLEX BELOW
+1E77;1E77;0075 032D;1E77;0075 032D; # (ṷ; ṷ; u◌̭; ṷ; u◌̭; ) LATIN SMALL LETTER U WITH CIRCUMFLEX BELOW
+1E78;1E78;0055 0303 0301;1E78;0055 0303 0301; # (Ṹ; Ṹ; U◌̃◌Ì; Ṹ; U◌̃◌Ì; ) LATIN CAPITAL LETTER U WITH TILDE AND ACUTE
+1E79;1E79;0075 0303 0301;1E79;0075 0303 0301; # (á¹¹; á¹¹; u◌̃◌Ì; á¹¹; u◌̃◌Ì; ) LATIN SMALL LETTER U WITH TILDE AND ACUTE
+1E7A;1E7A;0055 0304 0308;1E7A;0055 0304 0308; # (Ṻ; Ṻ; U◌̄◌̈; Ṻ; U◌̄◌̈; ) LATIN CAPITAL LETTER U WITH MACRON AND DIAERESIS
+1E7B;1E7B;0075 0304 0308;1E7B;0075 0304 0308; # (ṻ; ṻ; u◌̄◌̈; ṻ; u◌̄◌̈; ) LATIN SMALL LETTER U WITH MACRON AND DIAERESIS
+1E7C;1E7C;0056 0303;1E7C;0056 0303; # (Ṽ; Ṽ; V◌̃; Ṽ; V◌̃; ) LATIN CAPITAL LETTER V WITH TILDE
+1E7D;1E7D;0076 0303;1E7D;0076 0303; # (ṽ; ṽ; v◌̃; ṽ; v◌̃; ) LATIN SMALL LETTER V WITH TILDE
+1E7E;1E7E;0056 0323;1E7E;0056 0323; # (Ṿ; Ṿ; V◌̣; Ṿ; V◌̣; ) LATIN CAPITAL LETTER V WITH DOT BELOW
+1E7F;1E7F;0076 0323;1E7F;0076 0323; # (ṿ; ṿ; v◌̣; ṿ; v◌̣; ) LATIN SMALL LETTER V WITH DOT BELOW
+1E80;1E80;0057 0300;1E80;0057 0300; # (Ẁ; Ẁ; W◌̀; Ẁ; W◌̀; ) LATIN CAPITAL LETTER W WITH GRAVE
+1E81;1E81;0077 0300;1E81;0077 0300; # (áº; áº; w◌̀; áº; w◌̀; ) LATIN SMALL LETTER W WITH GRAVE
+1E82;1E82;0057 0301;1E82;0057 0301; # (Ẃ; Ẃ; Wâ—ŒÌ; Ẃ; Wâ—ŒÌ; ) LATIN CAPITAL LETTER W WITH ACUTE
+1E83;1E83;0077 0301;1E83;0077 0301; # (ẃ; ẃ; wâ—ŒÌ; ẃ; wâ—ŒÌ; ) LATIN SMALL LETTER W WITH ACUTE
+1E84;1E84;0057 0308;1E84;0057 0308; # (Ẅ; Ẅ; W◌̈; Ẅ; W◌̈; ) LATIN CAPITAL LETTER W WITH DIAERESIS
+1E85;1E85;0077 0308;1E85;0077 0308; # (ẅ; ẅ; w◌̈; ẅ; w◌̈; ) LATIN SMALL LETTER W WITH DIAERESIS
+1E86;1E86;0057 0307;1E86;0057 0307; # (Ẇ; Ẇ; W◌̇; Ẇ; W◌̇; ) LATIN CAPITAL LETTER W WITH DOT ABOVE
+1E87;1E87;0077 0307;1E87;0077 0307; # (ẇ; ẇ; w◌̇; ẇ; w◌̇; ) LATIN SMALL LETTER W WITH DOT ABOVE
+1E88;1E88;0057 0323;1E88;0057 0323; # (Ẉ; Ẉ; W◌̣; Ẉ; W◌̣; ) LATIN CAPITAL LETTER W WITH DOT BELOW
+1E89;1E89;0077 0323;1E89;0077 0323; # (ẉ; ẉ; w◌̣; ẉ; w◌̣; ) LATIN SMALL LETTER W WITH DOT BELOW
+1E8A;1E8A;0058 0307;1E8A;0058 0307; # (Ẋ; Ẋ; X◌̇; Ẋ; X◌̇; ) LATIN CAPITAL LETTER X WITH DOT ABOVE
+1E8B;1E8B;0078 0307;1E8B;0078 0307; # (ẋ; ẋ; x◌̇; ẋ; x◌̇; ) LATIN SMALL LETTER X WITH DOT ABOVE
+1E8C;1E8C;0058 0308;1E8C;0058 0308; # (Ẍ; Ẍ; X◌̈; Ẍ; X◌̈; ) LATIN CAPITAL LETTER X WITH DIAERESIS
+1E8D;1E8D;0078 0308;1E8D;0078 0308; # (áº; áº; x◌̈; áº; x◌̈; ) LATIN SMALL LETTER X WITH DIAERESIS
+1E8E;1E8E;0059 0307;1E8E;0059 0307; # (Ẏ; Ẏ; Y◌̇; Ẏ; Y◌̇; ) LATIN CAPITAL LETTER Y WITH DOT ABOVE
+1E8F;1E8F;0079 0307;1E8F;0079 0307; # (áº; áº; y◌̇; áº; y◌̇; ) LATIN SMALL LETTER Y WITH DOT ABOVE
+1E90;1E90;005A 0302;1E90;005A 0302; # (áº; áº; Z◌̂; áº; Z◌̂; ) LATIN CAPITAL LETTER Z WITH CIRCUMFLEX
+1E91;1E91;007A 0302;1E91;007A 0302; # (ẑ; ẑ; z◌̂; ẑ; z◌̂; ) LATIN SMALL LETTER Z WITH CIRCUMFLEX
+1E92;1E92;005A 0323;1E92;005A 0323; # (Ẓ; Ẓ; Z◌̣; Ẓ; Z◌̣; ) LATIN CAPITAL LETTER Z WITH DOT BELOW
+1E93;1E93;007A 0323;1E93;007A 0323; # (ẓ; ẓ; z◌̣; ẓ; z◌̣; ) LATIN SMALL LETTER Z WITH DOT BELOW
+1E94;1E94;005A 0331;1E94;005A 0331; # (Ẕ; Ẕ; Z◌̱; Ẕ; Z◌̱; ) LATIN CAPITAL LETTER Z WITH LINE BELOW
+1E95;1E95;007A 0331;1E95;007A 0331; # (ẕ; ẕ; z◌̱; ẕ; z◌̱; ) LATIN SMALL LETTER Z WITH LINE BELOW
+1E96;1E96;0068 0331;1E96;0068 0331; # (ẖ; ẖ; h◌̱; ẖ; h◌̱; ) LATIN SMALL LETTER H WITH LINE BELOW
+1E97;1E97;0074 0308;1E97;0074 0308; # (ẗ; ẗ; t◌̈; ẗ; t◌̈; ) LATIN SMALL LETTER T WITH DIAERESIS
+1E98;1E98;0077 030A;1E98;0077 030A; # (ẘ; ẘ; w◌̊; ẘ; w◌̊; ) LATIN SMALL LETTER W WITH RING ABOVE
+1E99;1E99;0079 030A;1E99;0079 030A; # (ẙ; ẙ; y◌̊; ẙ; y◌̊; ) LATIN SMALL LETTER Y WITH RING ABOVE
+1E9A;1E9A;1E9A;0061 02BE;0061 02BE; # (ẚ; ẚ; ẚ; aʾ; aʾ; ) LATIN SMALL LETTER A WITH RIGHT HALF RING
+1E9B;1E9B;017F 0307;1E61;0073 0307; # (ẛ; ẛ; ſ◌̇; ṡ; s◌̇; ) LATIN SMALL LETTER LONG S WITH DOT ABOVE
+1EA0;1EA0;0041 0323;1EA0;0041 0323; # (Ạ; Ạ; A◌̣; Ạ; A◌̣; ) LATIN CAPITAL LETTER A WITH DOT BELOW
+1EA1;1EA1;0061 0323;1EA1;0061 0323; # (ạ; ạ; a◌̣; ạ; a◌̣; ) LATIN SMALL LETTER A WITH DOT BELOW
+1EA2;1EA2;0041 0309;1EA2;0041 0309; # (Ả; Ả; A◌̉; Ả; A◌̉; ) LATIN CAPITAL LETTER A WITH HOOK ABOVE
+1EA3;1EA3;0061 0309;1EA3;0061 0309; # (ả; ả; a◌̉; ả; a◌̉; ) LATIN SMALL LETTER A WITH HOOK ABOVE
+1EA4;1EA4;0041 0302 0301;1EA4;0041 0302 0301; # (Ấ; Ấ; A◌̂◌Ì; Ấ; A◌̂◌Ì; ) LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND ACUTE
+1EA5;1EA5;0061 0302 0301;1EA5;0061 0302 0301; # (ấ; ấ; a◌̂◌Ì; ấ; a◌̂◌Ì; ) LATIN SMALL LETTER A WITH CIRCUMFLEX AND ACUTE
+1EA6;1EA6;0041 0302 0300;1EA6;0041 0302 0300; # (Ầ; Ầ; A◌̂◌̀; Ầ; A◌̂◌̀; ) LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND GRAVE
+1EA7;1EA7;0061 0302 0300;1EA7;0061 0302 0300; # (ầ; ầ; a◌̂◌̀; ầ; a◌̂◌̀; ) LATIN SMALL LETTER A WITH CIRCUMFLEX AND GRAVE
+1EA8;1EA8;0041 0302 0309;1EA8;0041 0302 0309; # (Ẩ; Ẩ; A◌̂◌̉; Ẩ; A◌̂◌̉; ) LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE
+1EA9;1EA9;0061 0302 0309;1EA9;0061 0302 0309; # (ẩ; ẩ; a◌̂◌̉; ẩ; a◌̂◌̉; ) LATIN SMALL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE
+1EAA;1EAA;0041 0302 0303;1EAA;0041 0302 0303; # (Ẫ; Ẫ; A◌̂◌̃; Ẫ; A◌̂◌̃; ) LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND TILDE
+1EAB;1EAB;0061 0302 0303;1EAB;0061 0302 0303; # (ẫ; ẫ; a◌̂◌̃; ẫ; a◌̂◌̃; ) LATIN SMALL LETTER A WITH CIRCUMFLEX AND TILDE
+1EAC;1EAC;0041 0323 0302;1EAC;0041 0323 0302; # (Ậ; Ậ; A◌̣◌̂; Ậ; A◌̣◌̂; ) LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND DOT BELOW
+1EAD;1EAD;0061 0323 0302;1EAD;0061 0323 0302; # (ậ; ậ; a◌̣◌̂; ậ; a◌̣◌̂; ) LATIN SMALL LETTER A WITH CIRCUMFLEX AND DOT BELOW
+1EAE;1EAE;0041 0306 0301;1EAE;0041 0306 0301; # (Ắ; Ắ; A◌̆◌Ì; Ắ; A◌̆◌Ì; ) LATIN CAPITAL LETTER A WITH BREVE AND ACUTE
+1EAF;1EAF;0061 0306 0301;1EAF;0061 0306 0301; # (ắ; ắ; a◌̆◌Ì; ắ; a◌̆◌Ì; ) LATIN SMALL LETTER A WITH BREVE AND ACUTE
+1EB0;1EB0;0041 0306 0300;1EB0;0041 0306 0300; # (Ằ; Ằ; A◌̆◌̀; Ằ; A◌̆◌̀; ) LATIN CAPITAL LETTER A WITH BREVE AND GRAVE
+1EB1;1EB1;0061 0306 0300;1EB1;0061 0306 0300; # (ằ; ằ; a◌̆◌̀; ằ; a◌̆◌̀; ) LATIN SMALL LETTER A WITH BREVE AND GRAVE
+1EB2;1EB2;0041 0306 0309;1EB2;0041 0306 0309; # (Ẳ; Ẳ; A◌̆◌̉; Ẳ; A◌̆◌̉; ) LATIN CAPITAL LETTER A WITH BREVE AND HOOK ABOVE
+1EB3;1EB3;0061 0306 0309;1EB3;0061 0306 0309; # (ẳ; ẳ; a◌̆◌̉; ẳ; a◌̆◌̉; ) LATIN SMALL LETTER A WITH BREVE AND HOOK ABOVE
+1EB4;1EB4;0041 0306 0303;1EB4;0041 0306 0303; # (Ẵ; Ẵ; A◌̆◌̃; Ẵ; A◌̆◌̃; ) LATIN CAPITAL LETTER A WITH BREVE AND TILDE
+1EB5;1EB5;0061 0306 0303;1EB5;0061 0306 0303; # (ẵ; ẵ; a◌̆◌̃; ẵ; a◌̆◌̃; ) LATIN SMALL LETTER A WITH BREVE AND TILDE
+1EB6;1EB6;0041 0323 0306;1EB6;0041 0323 0306; # (Ặ; Ặ; A◌̣◌̆; Ặ; A◌̣◌̆; ) LATIN CAPITAL LETTER A WITH BREVE AND DOT BELOW
+1EB7;1EB7;0061 0323 0306;1EB7;0061 0323 0306; # (ặ; ặ; a◌̣◌̆; ặ; a◌̣◌̆; ) LATIN SMALL LETTER A WITH BREVE AND DOT BELOW
+1EB8;1EB8;0045 0323;1EB8;0045 0323; # (Ẹ; Ẹ; E◌̣; Ẹ; E◌̣; ) LATIN CAPITAL LETTER E WITH DOT BELOW
+1EB9;1EB9;0065 0323;1EB9;0065 0323; # (ẹ; ẹ; e◌̣; ẹ; e◌̣; ) LATIN SMALL LETTER E WITH DOT BELOW
+1EBA;1EBA;0045 0309;1EBA;0045 0309; # (Ẻ; Ẻ; E◌̉; Ẻ; E◌̉; ) LATIN CAPITAL LETTER E WITH HOOK ABOVE
+1EBB;1EBB;0065 0309;1EBB;0065 0309; # (ẻ; ẻ; e◌̉; ẻ; e◌̉; ) LATIN SMALL LETTER E WITH HOOK ABOVE
+1EBC;1EBC;0045 0303;1EBC;0045 0303; # (Ẽ; Ẽ; E◌̃; Ẽ; E◌̃; ) LATIN CAPITAL LETTER E WITH TILDE
+1EBD;1EBD;0065 0303;1EBD;0065 0303; # (ẽ; ẽ; e◌̃; ẽ; e◌̃; ) LATIN SMALL LETTER E WITH TILDE
+1EBE;1EBE;0045 0302 0301;1EBE;0045 0302 0301; # (Ế; Ế; E◌̂◌Ì; Ế; E◌̂◌Ì; ) LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND ACUTE
+1EBF;1EBF;0065 0302 0301;1EBF;0065 0302 0301; # (ế; ế; e◌̂◌Ì; ế; e◌̂◌Ì; ) LATIN SMALL LETTER E WITH CIRCUMFLEX AND ACUTE
+1EC0;1EC0;0045 0302 0300;1EC0;0045 0302 0300; # (Ề; Ề; E◌̂◌̀; Ề; E◌̂◌̀; ) LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND GRAVE
+1EC1;1EC1;0065 0302 0300;1EC1;0065 0302 0300; # (á»; á»; e◌̂◌̀; á»; e◌̂◌̀; ) LATIN SMALL LETTER E WITH CIRCUMFLEX AND GRAVE
+1EC2;1EC2;0045 0302 0309;1EC2;0045 0302 0309; # (Ể; Ể; E◌̂◌̉; Ể; E◌̂◌̉; ) LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE
+1EC3;1EC3;0065 0302 0309;1EC3;0065 0302 0309; # (ể; ể; e◌̂◌̉; ể; e◌̂◌̉; ) LATIN SMALL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE
+1EC4;1EC4;0045 0302 0303;1EC4;0045 0302 0303; # (Ễ; Ễ; E◌̂◌̃; Ễ; E◌̂◌̃; ) LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND TILDE
+1EC5;1EC5;0065 0302 0303;1EC5;0065 0302 0303; # (ễ; ễ; e◌̂◌̃; ễ; e◌̂◌̃; ) LATIN SMALL LETTER E WITH CIRCUMFLEX AND TILDE
+1EC6;1EC6;0045 0323 0302;1EC6;0045 0323 0302; # (Ệ; Ệ; E◌̣◌̂; Ệ; E◌̣◌̂; ) LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND DOT BELOW
+1EC7;1EC7;0065 0323 0302;1EC7;0065 0323 0302; # (ệ; ệ; e◌̣◌̂; ệ; e◌̣◌̂; ) LATIN SMALL LETTER E WITH CIRCUMFLEX AND DOT BELOW
+1EC8;1EC8;0049 0309;1EC8;0049 0309; # (Ỉ; Ỉ; I◌̉; Ỉ; I◌̉; ) LATIN CAPITAL LETTER I WITH HOOK ABOVE
+1EC9;1EC9;0069 0309;1EC9;0069 0309; # (ỉ; ỉ; i◌̉; ỉ; i◌̉; ) LATIN SMALL LETTER I WITH HOOK ABOVE
+1ECA;1ECA;0049 0323;1ECA;0049 0323; # (Ị; Ị; I◌̣; Ị; I◌̣; ) LATIN CAPITAL LETTER I WITH DOT BELOW
+1ECB;1ECB;0069 0323;1ECB;0069 0323; # (ị; ị; i◌̣; ị; i◌̣; ) LATIN SMALL LETTER I WITH DOT BELOW
+1ECC;1ECC;004F 0323;1ECC;004F 0323; # (Ọ; Ọ; O◌̣; Ọ; O◌̣; ) LATIN CAPITAL LETTER O WITH DOT BELOW
+1ECD;1ECD;006F 0323;1ECD;006F 0323; # (á»; á»; o◌̣; á»; o◌̣; ) LATIN SMALL LETTER O WITH DOT BELOW
+1ECE;1ECE;004F 0309;1ECE;004F 0309; # (Ỏ; Ỏ; O◌̉; Ỏ; O◌̉; ) LATIN CAPITAL LETTER O WITH HOOK ABOVE
+1ECF;1ECF;006F 0309;1ECF;006F 0309; # (á»; á»; o◌̉; á»; o◌̉; ) LATIN SMALL LETTER O WITH HOOK ABOVE
+1ED0;1ED0;004F 0302 0301;1ED0;004F 0302 0301; # (á»; á»; O◌̂◌Ì; á»; O◌̂◌Ì; ) LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND ACUTE
+1ED1;1ED1;006F 0302 0301;1ED1;006F 0302 0301; # (ố; ố; o◌̂◌Ì; ố; o◌̂◌Ì; ) LATIN SMALL LETTER O WITH CIRCUMFLEX AND ACUTE
+1ED2;1ED2;004F 0302 0300;1ED2;004F 0302 0300; # (Ồ; Ồ; O◌̂◌̀; Ồ; O◌̂◌̀; ) LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND GRAVE
+1ED3;1ED3;006F 0302 0300;1ED3;006F 0302 0300; # (ồ; ồ; o◌̂◌̀; ồ; o◌̂◌̀; ) LATIN SMALL LETTER O WITH CIRCUMFLEX AND GRAVE
+1ED4;1ED4;004F 0302 0309;1ED4;004F 0302 0309; # (Ổ; Ổ; O◌̂◌̉; Ổ; O◌̂◌̉; ) LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE
+1ED5;1ED5;006F 0302 0309;1ED5;006F 0302 0309; # (ổ; ổ; o◌̂◌̉; ổ; o◌̂◌̉; ) LATIN SMALL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE
+1ED6;1ED6;004F 0302 0303;1ED6;004F 0302 0303; # (Ỗ; Ỗ; O◌̂◌̃; Ỗ; O◌̂◌̃; ) LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND TILDE
+1ED7;1ED7;006F 0302 0303;1ED7;006F 0302 0303; # (ỗ; ỗ; o◌̂◌̃; ỗ; o◌̂◌̃; ) LATIN SMALL LETTER O WITH CIRCUMFLEX AND TILDE
+1ED8;1ED8;004F 0323 0302;1ED8;004F 0323 0302; # (Ộ; Ộ; O◌̣◌̂; Ộ; O◌̣◌̂; ) LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND DOT BELOW
+1ED9;1ED9;006F 0323 0302;1ED9;006F 0323 0302; # (ộ; ộ; o◌̣◌̂; ộ; o◌̣◌̂; ) LATIN SMALL LETTER O WITH CIRCUMFLEX AND DOT BELOW
+1EDA;1EDA;004F 031B 0301;1EDA;004F 031B 0301; # (Ớ; Ớ; O◌̛◌Ì; Ớ; O◌̛◌Ì; ) LATIN CAPITAL LETTER O WITH HORN AND ACUTE
+1EDB;1EDB;006F 031B 0301;1EDB;006F 031B 0301; # (á»›; á»›; o◌̛◌Ì; á»›; o◌̛◌Ì; ) LATIN SMALL LETTER O WITH HORN AND ACUTE
+1EDC;1EDC;004F 031B 0300;1EDC;004F 031B 0300; # (Ờ; Ờ; O◌̛◌̀; Ờ; O◌̛◌̀; ) LATIN CAPITAL LETTER O WITH HORN AND GRAVE
+1EDD;1EDD;006F 031B 0300;1EDD;006F 031B 0300; # (á»; á»; o◌̛◌̀; á»; o◌̛◌̀; ) LATIN SMALL LETTER O WITH HORN AND GRAVE
+1EDE;1EDE;004F 031B 0309;1EDE;004F 031B 0309; # (Ở; Ở; O◌̛◌̉; Ở; O◌̛◌̉; ) LATIN CAPITAL LETTER O WITH HORN AND HOOK ABOVE
+1EDF;1EDF;006F 031B 0309;1EDF;006F 031B 0309; # (ở; ở; o◌̛◌̉; ở; o◌̛◌̉; ) LATIN SMALL LETTER O WITH HORN AND HOOK ABOVE
+1EE0;1EE0;004F 031B 0303;1EE0;004F 031B 0303; # (Ỡ; Ỡ; O◌̛◌̃; Ỡ; O◌̛◌̃; ) LATIN CAPITAL LETTER O WITH HORN AND TILDE
+1EE1;1EE1;006F 031B 0303;1EE1;006F 031B 0303; # (ỡ; ỡ; o◌̛◌̃; ỡ; o◌̛◌̃; ) LATIN SMALL LETTER O WITH HORN AND TILDE
+1EE2;1EE2;004F 031B 0323;1EE2;004F 031B 0323; # (Ợ; Ợ; O◌̛◌̣; Ợ; O◌̛◌̣; ) LATIN CAPITAL LETTER O WITH HORN AND DOT BELOW
+1EE3;1EE3;006F 031B 0323;1EE3;006F 031B 0323; # (ợ; ợ; o◌̛◌̣; ợ; o◌̛◌̣; ) LATIN SMALL LETTER O WITH HORN AND DOT BELOW
+1EE4;1EE4;0055 0323;1EE4;0055 0323; # (Ụ; Ụ; U◌̣; Ụ; U◌̣; ) LATIN CAPITAL LETTER U WITH DOT BELOW
+1EE5;1EE5;0075 0323;1EE5;0075 0323; # (ụ; ụ; u◌̣; ụ; u◌̣; ) LATIN SMALL LETTER U WITH DOT BELOW
+1EE6;1EE6;0055 0309;1EE6;0055 0309; # (Ủ; Ủ; U◌̉; Ủ; U◌̉; ) LATIN CAPITAL LETTER U WITH HOOK ABOVE
+1EE7;1EE7;0075 0309;1EE7;0075 0309; # (ủ; ủ; u◌̉; ủ; u◌̉; ) LATIN SMALL LETTER U WITH HOOK ABOVE
+1EE8;1EE8;0055 031B 0301;1EE8;0055 031B 0301; # (Ứ; Ứ; U◌̛◌Ì; Ứ; U◌̛◌Ì; ) LATIN CAPITAL LETTER U WITH HORN AND ACUTE
+1EE9;1EE9;0075 031B 0301;1EE9;0075 031B 0301; # (ứ; ứ; u◌̛◌Ì; ứ; u◌̛◌Ì; ) LATIN SMALL LETTER U WITH HORN AND ACUTE
+1EEA;1EEA;0055 031B 0300;1EEA;0055 031B 0300; # (Ừ; Ừ; U◌̛◌̀; Ừ; U◌̛◌̀; ) LATIN CAPITAL LETTER U WITH HORN AND GRAVE
+1EEB;1EEB;0075 031B 0300;1EEB;0075 031B 0300; # (ừ; ừ; u◌̛◌̀; ừ; u◌̛◌̀; ) LATIN SMALL LETTER U WITH HORN AND GRAVE
+1EEC;1EEC;0055 031B 0309;1EEC;0055 031B 0309; # (Ử; Ử; U◌̛◌̉; Ử; U◌̛◌̉; ) LATIN CAPITAL LETTER U WITH HORN AND HOOK ABOVE
+1EED;1EED;0075 031B 0309;1EED;0075 031B 0309; # (ử; ử; u◌̛◌̉; ử; u◌̛◌̉; ) LATIN SMALL LETTER U WITH HORN AND HOOK ABOVE
+1EEE;1EEE;0055 031B 0303;1EEE;0055 031B 0303; # (Ữ; Ữ; U◌̛◌̃; Ữ; U◌̛◌̃; ) LATIN CAPITAL LETTER U WITH HORN AND TILDE
+1EEF;1EEF;0075 031B 0303;1EEF;0075 031B 0303; # (ữ; ữ; u◌̛◌̃; ữ; u◌̛◌̃; ) LATIN SMALL LETTER U WITH HORN AND TILDE
+1EF0;1EF0;0055 031B 0323;1EF0;0055 031B 0323; # (Ự; Ự; U◌̛◌̣; Ự; U◌̛◌̣; ) LATIN CAPITAL LETTER U WITH HORN AND DOT BELOW
+1EF1;1EF1;0075 031B 0323;1EF1;0075 031B 0323; # (ự; ự; u◌̛◌̣; ự; u◌̛◌̣; ) LATIN SMALL LETTER U WITH HORN AND DOT BELOW
+1EF2;1EF2;0059 0300;1EF2;0059 0300; # (Ỳ; Ỳ; Y◌̀; Ỳ; Y◌̀; ) LATIN CAPITAL LETTER Y WITH GRAVE
+1EF3;1EF3;0079 0300;1EF3;0079 0300; # (ỳ; ỳ; y◌̀; ỳ; y◌̀; ) LATIN SMALL LETTER Y WITH GRAVE
+1EF4;1EF4;0059 0323;1EF4;0059 0323; # (Ỵ; Ỵ; Y◌̣; Ỵ; Y◌̣; ) LATIN CAPITAL LETTER Y WITH DOT BELOW
+1EF5;1EF5;0079 0323;1EF5;0079 0323; # (ỵ; ỵ; y◌̣; ỵ; y◌̣; ) LATIN SMALL LETTER Y WITH DOT BELOW
+1EF6;1EF6;0059 0309;1EF6;0059 0309; # (Ỷ; Ỷ; Y◌̉; Ỷ; Y◌̉; ) LATIN CAPITAL LETTER Y WITH HOOK ABOVE
+1EF7;1EF7;0079 0309;1EF7;0079 0309; # (ỷ; ỷ; y◌̉; ỷ; y◌̉; ) LATIN SMALL LETTER Y WITH HOOK ABOVE
+1EF8;1EF8;0059 0303;1EF8;0059 0303; # (Ỹ; Ỹ; Y◌̃; Ỹ; Y◌̃; ) LATIN CAPITAL LETTER Y WITH TILDE
+1EF9;1EF9;0079 0303;1EF9;0079 0303; # (ỹ; ỹ; y◌̃; ỹ; y◌̃; ) LATIN SMALL LETTER Y WITH TILDE
+1F00;1F00;03B1 0313;1F00;03B1 0313; # (ἀ; ἀ; α◌̓; ἀ; α◌̓; ) GREEK SMALL LETTER ALPHA WITH PSILI
+1F01;1F01;03B1 0314;1F01;03B1 0314; # (á¼; á¼; α◌̔; á¼; α◌̔; ) GREEK SMALL LETTER ALPHA WITH DASIA
+1F02;1F02;03B1 0313 0300;1F02;03B1 0313 0300; # (ἂ; ἂ; α◌̓◌̀; ἂ; α◌̓◌̀; ) GREEK SMALL LETTER ALPHA WITH PSILI AND VARIA
+1F03;1F03;03B1 0314 0300;1F03;03B1 0314 0300; # (ἃ; ἃ; α◌̔◌̀; ἃ; α◌̔◌̀; ) GREEK SMALL LETTER ALPHA WITH DASIA AND VARIA
+1F04;1F04;03B1 0313 0301;1F04;03B1 0313 0301; # (ἄ; ἄ; α◌̓◌Ì; ἄ; α◌̓◌Ì; ) GREEK SMALL LETTER ALPHA WITH PSILI AND OXIA
+1F05;1F05;03B1 0314 0301;1F05;03B1 0314 0301; # (á¼…; á¼…; α◌̔◌Ì; á¼…; α◌̔◌Ì; ) GREEK SMALL LETTER ALPHA WITH DASIA AND OXIA
+1F06;1F06;03B1 0313 0342;1F06;03B1 0313 0342; # (ἆ; ἆ; α◌̓◌͂; ἆ; α◌̓◌͂; ) GREEK SMALL LETTER ALPHA WITH PSILI AND PERISPOMENI
+1F07;1F07;03B1 0314 0342;1F07;03B1 0314 0342; # (ἇ; ἇ; α◌̔◌͂; ἇ; α◌̔◌͂; ) GREEK SMALL LETTER ALPHA WITH DASIA AND PERISPOMENI
+1F08;1F08;0391 0313;1F08;0391 0313; # (Ἀ; Ἀ; Α◌̓; Ἀ; Α◌̓; ) GREEK CAPITAL LETTER ALPHA WITH PSILI
+1F09;1F09;0391 0314;1F09;0391 0314; # (Ἁ; Ἁ; Α◌̔; Ἁ; Α◌̔; ) GREEK CAPITAL LETTER ALPHA WITH DASIA
+1F0A;1F0A;0391 0313 0300;1F0A;0391 0313 0300; # (Ἂ; Ἂ; Α◌̓◌̀; Ἂ; Α◌̓◌̀; ) GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA
+1F0B;1F0B;0391 0314 0300;1F0B;0391 0314 0300; # (Ἃ; Ἃ; Α◌̔◌̀; Ἃ; Α◌̔◌̀; ) GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA
+1F0C;1F0C;0391 0313 0301;1F0C;0391 0313 0301; # (Ἄ; Ἄ; Α◌̓◌Ì; Ἄ; Α◌̓◌Ì; ) GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA
+1F0D;1F0D;0391 0314 0301;1F0D;0391 0314 0301; # (á¼; á¼; Α◌̔◌Ì; á¼; Α◌̔◌Ì; ) GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA
+1F0E;1F0E;0391 0313 0342;1F0E;0391 0313 0342; # (Ἆ; Ἆ; Α◌̓◌͂; Ἆ; Α◌̓◌͂; ) GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI
+1F0F;1F0F;0391 0314 0342;1F0F;0391 0314 0342; # (á¼; á¼; Α◌̔◌͂; á¼; Α◌̔◌͂; ) GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI
+1F10;1F10;03B5 0313;1F10;03B5 0313; # (á¼; á¼; ε◌̓; á¼; ε◌̓; ) GREEK SMALL LETTER EPSILON WITH PSILI
+1F11;1F11;03B5 0314;1F11;03B5 0314; # (ἑ; ἑ; ε◌̔; ἑ; ε◌̔; ) GREEK SMALL LETTER EPSILON WITH DASIA
+1F12;1F12;03B5 0313 0300;1F12;03B5 0313 0300; # (ἒ; ἒ; ε◌̓◌̀; ἒ; ε◌̓◌̀; ) GREEK SMALL LETTER EPSILON WITH PSILI AND VARIA
+1F13;1F13;03B5 0314 0300;1F13;03B5 0314 0300; # (ἓ; ἓ; ε◌̔◌̀; ἓ; ε◌̔◌̀; ) GREEK SMALL LETTER EPSILON WITH DASIA AND VARIA
+1F14;1F14;03B5 0313 0301;1F14;03B5 0313 0301; # (á¼”; á¼”; ε◌̓◌Ì; á¼”; ε◌̓◌Ì; ) GREEK SMALL LETTER EPSILON WITH PSILI AND OXIA
+1F15;1F15;03B5 0314 0301;1F15;03B5 0314 0301; # (ἕ; ἕ; ε◌̔◌Ì; ἕ; ε◌̔◌Ì; ) GREEK SMALL LETTER EPSILON WITH DASIA AND OXIA
+1F18;1F18;0395 0313;1F18;0395 0313; # (Ἐ; Ἐ; Ε◌̓; Ἐ; Ε◌̓; ) GREEK CAPITAL LETTER EPSILON WITH PSILI
+1F19;1F19;0395 0314;1F19;0395 0314; # (Ἑ; Ἑ; Ε◌̔; Ἑ; Ε◌̔; ) GREEK CAPITAL LETTER EPSILON WITH DASIA
+1F1A;1F1A;0395 0313 0300;1F1A;0395 0313 0300; # (Ἒ; Ἒ; Ε◌̓◌̀; Ἒ; Ε◌̓◌̀; ) GREEK CAPITAL LETTER EPSILON WITH PSILI AND VARIA
+1F1B;1F1B;0395 0314 0300;1F1B;0395 0314 0300; # (Ἓ; Ἓ; Ε◌̔◌̀; Ἓ; Ε◌̔◌̀; ) GREEK CAPITAL LETTER EPSILON WITH DASIA AND VARIA
+1F1C;1F1C;0395 0313 0301;1F1C;0395 0313 0301; # (Ἔ; Ἔ; Ε◌̓◌Ì; Ἔ; Ε◌̓◌Ì; ) GREEK CAPITAL LETTER EPSILON WITH PSILI AND OXIA
+1F1D;1F1D;0395 0314 0301;1F1D;0395 0314 0301; # (á¼; á¼; Ε◌̔◌Ì; á¼; Ε◌̔◌Ì; ) GREEK CAPITAL LETTER EPSILON WITH DASIA AND OXIA
+1F20;1F20;03B7 0313;1F20;03B7 0313; # (ἠ; ἠ; η◌̓; ἠ; η◌̓; ) GREEK SMALL LETTER ETA WITH PSILI
+1F21;1F21;03B7 0314;1F21;03B7 0314; # (ἡ; ἡ; η◌̔; ἡ; η◌̔; ) GREEK SMALL LETTER ETA WITH DASIA
+1F22;1F22;03B7 0313 0300;1F22;03B7 0313 0300; # (ἢ; ἢ; η◌̓◌̀; ἢ; η◌̓◌̀; ) GREEK SMALL LETTER ETA WITH PSILI AND VARIA
+1F23;1F23;03B7 0314 0300;1F23;03B7 0314 0300; # (ἣ; ἣ; η◌̔◌̀; ἣ; η◌̔◌̀; ) GREEK SMALL LETTER ETA WITH DASIA AND VARIA
+1F24;1F24;03B7 0313 0301;1F24;03B7 0313 0301; # (ἤ; ἤ; η◌̓◌Ì; ἤ; η◌̓◌Ì; ) GREEK SMALL LETTER ETA WITH PSILI AND OXIA
+1F25;1F25;03B7 0314 0301;1F25;03B7 0314 0301; # (á¼¥; á¼¥; η◌̔◌Ì; á¼¥; η◌̔◌Ì; ) GREEK SMALL LETTER ETA WITH DASIA AND OXIA
+1F26;1F26;03B7 0313 0342;1F26;03B7 0313 0342; # (ἦ; ἦ; η◌̓◌͂; ἦ; η◌̓◌͂; ) GREEK SMALL LETTER ETA WITH PSILI AND PERISPOMENI
+1F27;1F27;03B7 0314 0342;1F27;03B7 0314 0342; # (ἧ; ἧ; η◌̔◌͂; ἧ; η◌̔◌͂; ) GREEK SMALL LETTER ETA WITH DASIA AND PERISPOMENI
+1F28;1F28;0397 0313;1F28;0397 0313; # (Ἠ; Ἠ; Η◌̓; Ἠ; Η◌̓; ) GREEK CAPITAL LETTER ETA WITH PSILI
+1F29;1F29;0397 0314;1F29;0397 0314; # (Ἡ; Ἡ; Η◌̔; Ἡ; Η◌̔; ) GREEK CAPITAL LETTER ETA WITH DASIA
+1F2A;1F2A;0397 0313 0300;1F2A;0397 0313 0300; # (Ἢ; Ἢ; Η◌̓◌̀; Ἢ; Η◌̓◌̀; ) GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA
+1F2B;1F2B;0397 0314 0300;1F2B;0397 0314 0300; # (Ἣ; Ἣ; Η◌̔◌̀; Ἣ; Η◌̔◌̀; ) GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA
+1F2C;1F2C;0397 0313 0301;1F2C;0397 0313 0301; # (Ἤ; Ἤ; Η◌̓◌Ì; Ἤ; Η◌̓◌Ì; ) GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA
+1F2D;1F2D;0397 0314 0301;1F2D;0397 0314 0301; # (á¼­; á¼­; Η◌̔◌Ì; á¼­; Η◌̔◌Ì; ) GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA
+1F2E;1F2E;0397 0313 0342;1F2E;0397 0313 0342; # (Ἦ; Ἦ; Η◌̓◌͂; Ἦ; Η◌̓◌͂; ) GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI
+1F2F;1F2F;0397 0314 0342;1F2F;0397 0314 0342; # (Ἧ; Ἧ; Η◌̔◌͂; Ἧ; Η◌̔◌͂; ) GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI
+1F30;1F30;03B9 0313;1F30;03B9 0313; # (ἰ; ἰ; ι◌̓; ἰ; ι◌̓; ) GREEK SMALL LETTER IOTA WITH PSILI
+1F31;1F31;03B9 0314;1F31;03B9 0314; # (ἱ; ἱ; ι◌̔; ἱ; ι◌̔; ) GREEK SMALL LETTER IOTA WITH DASIA
+1F32;1F32;03B9 0313 0300;1F32;03B9 0313 0300; # (ἲ; ἲ; ι◌̓◌̀; ἲ; ι◌̓◌̀; ) GREEK SMALL LETTER IOTA WITH PSILI AND VARIA
+1F33;1F33;03B9 0314 0300;1F33;03B9 0314 0300; # (ἳ; ἳ; ι◌̔◌̀; ἳ; ι◌̔◌̀; ) GREEK SMALL LETTER IOTA WITH DASIA AND VARIA
+1F34;1F34;03B9 0313 0301;1F34;03B9 0313 0301; # (á¼´; á¼´; ι◌̓◌Ì; á¼´; ι◌̓◌Ì; ) GREEK SMALL LETTER IOTA WITH PSILI AND OXIA
+1F35;1F35;03B9 0314 0301;1F35;03B9 0314 0301; # (á¼µ; á¼µ; ι◌̔◌Ì; á¼µ; ι◌̔◌Ì; ) GREEK SMALL LETTER IOTA WITH DASIA AND OXIA
+1F36;1F36;03B9 0313 0342;1F36;03B9 0313 0342; # (ἶ; ἶ; ι◌̓◌͂; ἶ; ι◌̓◌͂; ) GREEK SMALL LETTER IOTA WITH PSILI AND PERISPOMENI
+1F37;1F37;03B9 0314 0342;1F37;03B9 0314 0342; # (ἷ; ἷ; ι◌̔◌͂; ἷ; ι◌̔◌͂; ) GREEK SMALL LETTER IOTA WITH DASIA AND PERISPOMENI
+1F38;1F38;0399 0313;1F38;0399 0313; # (Ἰ; Ἰ; Ι◌̓; Ἰ; Ι◌̓; ) GREEK CAPITAL LETTER IOTA WITH PSILI
+1F39;1F39;0399 0314;1F39;0399 0314; # (Ἱ; Ἱ; Ι◌̔; Ἱ; Ι◌̔; ) GREEK CAPITAL LETTER IOTA WITH DASIA
+1F3A;1F3A;0399 0313 0300;1F3A;0399 0313 0300; # (Ἲ; Ἲ; Ι◌̓◌̀; Ἲ; Ι◌̓◌̀; ) GREEK CAPITAL LETTER IOTA WITH PSILI AND VARIA
+1F3B;1F3B;0399 0314 0300;1F3B;0399 0314 0300; # (Ἳ; Ἳ; Ι◌̔◌̀; Ἳ; Ι◌̔◌̀; ) GREEK CAPITAL LETTER IOTA WITH DASIA AND VARIA
+1F3C;1F3C;0399 0313 0301;1F3C;0399 0313 0301; # (á¼¼; á¼¼; Ι◌̓◌Ì; á¼¼; Ι◌̓◌Ì; ) GREEK CAPITAL LETTER IOTA WITH PSILI AND OXIA
+1F3D;1F3D;0399 0314 0301;1F3D;0399 0314 0301; # (á¼½; á¼½; Ι◌̔◌Ì; á¼½; Ι◌̔◌Ì; ) GREEK CAPITAL LETTER IOTA WITH DASIA AND OXIA
+1F3E;1F3E;0399 0313 0342;1F3E;0399 0313 0342; # (Ἶ; Ἶ; Ι◌̓◌͂; Ἶ; Ι◌̓◌͂; ) GREEK CAPITAL LETTER IOTA WITH PSILI AND PERISPOMENI
+1F3F;1F3F;0399 0314 0342;1F3F;0399 0314 0342; # (Ἷ; Ἷ; Ι◌̔◌͂; Ἷ; Ι◌̔◌͂; ) GREEK CAPITAL LETTER IOTA WITH DASIA AND PERISPOMENI
+1F40;1F40;03BF 0313;1F40;03BF 0313; # (ὀ; ὀ; ο◌̓; ὀ; ο◌̓; ) GREEK SMALL LETTER OMICRON WITH PSILI
+1F41;1F41;03BF 0314;1F41;03BF 0314; # (á½; á½; ο◌̔; á½; ο◌̔; ) GREEK SMALL LETTER OMICRON WITH DASIA
+1F42;1F42;03BF 0313 0300;1F42;03BF 0313 0300; # (ὂ; ὂ; ο◌̓◌̀; ὂ; ο◌̓◌̀; ) GREEK SMALL LETTER OMICRON WITH PSILI AND VARIA
+1F43;1F43;03BF 0314 0300;1F43;03BF 0314 0300; # (ὃ; ὃ; ο◌̔◌̀; ὃ; ο◌̔◌̀; ) GREEK SMALL LETTER OMICRON WITH DASIA AND VARIA
+1F44;1F44;03BF 0313 0301;1F44;03BF 0313 0301; # (ὄ; ὄ; ο◌̓◌Ì; ὄ; ο◌̓◌Ì; ) GREEK SMALL LETTER OMICRON WITH PSILI AND OXIA
+1F45;1F45;03BF 0314 0301;1F45;03BF 0314 0301; # (á½…; á½…; ο◌̔◌Ì; á½…; ο◌̔◌Ì; ) GREEK SMALL LETTER OMICRON WITH DASIA AND OXIA
+1F48;1F48;039F 0313;1F48;039F 0313; # (Ὀ; Ὀ; Ο◌̓; Ὀ; Ο◌̓; ) GREEK CAPITAL LETTER OMICRON WITH PSILI
+1F49;1F49;039F 0314;1F49;039F 0314; # (Ὁ; Ὁ; Ο◌̔; Ὁ; Ο◌̔; ) GREEK CAPITAL LETTER OMICRON WITH DASIA
+1F4A;1F4A;039F 0313 0300;1F4A;039F 0313 0300; # (Ὂ; Ὂ; Ο◌̓◌̀; Ὂ; Ο◌̓◌̀; ) GREEK CAPITAL LETTER OMICRON WITH PSILI AND VARIA
+1F4B;1F4B;039F 0314 0300;1F4B;039F 0314 0300; # (Ὃ; Ὃ; Ο◌̔◌̀; Ὃ; Ο◌̔◌̀; ) GREEK CAPITAL LETTER OMICRON WITH DASIA AND VARIA
+1F4C;1F4C;039F 0313 0301;1F4C;039F 0313 0301; # (Ὄ; Ὄ; Ο◌̓◌Ì; Ὄ; Ο◌̓◌Ì; ) GREEK CAPITAL LETTER OMICRON WITH PSILI AND OXIA
+1F4D;1F4D;039F 0314 0301;1F4D;039F 0314 0301; # (á½; á½; Ο◌̔◌Ì; á½; Ο◌̔◌Ì; ) GREEK CAPITAL LETTER OMICRON WITH DASIA AND OXIA
+1F50;1F50;03C5 0313;1F50;03C5 0313; # (á½; á½; υ◌̓; á½; υ◌̓; ) GREEK SMALL LETTER UPSILON WITH PSILI
+1F51;1F51;03C5 0314;1F51;03C5 0314; # (ὑ; ὑ; υ◌̔; ὑ; υ◌̔; ) GREEK SMALL LETTER UPSILON WITH DASIA
+1F52;1F52;03C5 0313 0300;1F52;03C5 0313 0300; # (ὒ; ὒ; υ◌̓◌̀; ὒ; υ◌̓◌̀; ) GREEK SMALL LETTER UPSILON WITH PSILI AND VARIA
+1F53;1F53;03C5 0314 0300;1F53;03C5 0314 0300; # (ὓ; ὓ; υ◌̔◌̀; ὓ; υ◌̔◌̀; ) GREEK SMALL LETTER UPSILON WITH DASIA AND VARIA
+1F54;1F54;03C5 0313 0301;1F54;03C5 0313 0301; # (á½”; á½”; υ◌̓◌Ì; á½”; υ◌̓◌Ì; ) GREEK SMALL LETTER UPSILON WITH PSILI AND OXIA
+1F55;1F55;03C5 0314 0301;1F55;03C5 0314 0301; # (ὕ; ὕ; υ◌̔◌Ì; ὕ; υ◌̔◌Ì; ) GREEK SMALL LETTER UPSILON WITH DASIA AND OXIA
+1F56;1F56;03C5 0313 0342;1F56;03C5 0313 0342; # (ὖ; ὖ; υ◌̓◌͂; ὖ; υ◌̓◌͂; ) GREEK SMALL LETTER UPSILON WITH PSILI AND PERISPOMENI
+1F57;1F57;03C5 0314 0342;1F57;03C5 0314 0342; # (ὗ; ὗ; υ◌̔◌͂; ὗ; υ◌̔◌͂; ) GREEK SMALL LETTER UPSILON WITH DASIA AND PERISPOMENI
+1F59;1F59;03A5 0314;1F59;03A5 0314; # (Ὑ; Ὑ; Υ◌̔; Ὑ; Υ◌̔; ) GREEK CAPITAL LETTER UPSILON WITH DASIA
+1F5B;1F5B;03A5 0314 0300;1F5B;03A5 0314 0300; # (Ὓ; Ὓ; Υ◌̔◌̀; Ὓ; Υ◌̔◌̀; ) GREEK CAPITAL LETTER UPSILON WITH DASIA AND VARIA
+1F5D;1F5D;03A5 0314 0301;1F5D;03A5 0314 0301; # (á½; á½; Υ◌̔◌Ì; á½; Υ◌̔◌Ì; ) GREEK CAPITAL LETTER UPSILON WITH DASIA AND OXIA
+1F5F;1F5F;03A5 0314 0342;1F5F;03A5 0314 0342; # (Ὗ; Ὗ; Υ◌̔◌͂; Ὗ; Υ◌̔◌͂; ) GREEK CAPITAL LETTER UPSILON WITH DASIA AND PERISPOMENI
+1F60;1F60;03C9 0313;1F60;03C9 0313; # (ὠ; ὠ; ω◌̓; ὠ; ω◌̓; ) GREEK SMALL LETTER OMEGA WITH PSILI
+1F61;1F61;03C9 0314;1F61;03C9 0314; # (ὡ; ὡ; ω◌̔; ὡ; ω◌̔; ) GREEK SMALL LETTER OMEGA WITH DASIA
+1F62;1F62;03C9 0313 0300;1F62;03C9 0313 0300; # (ὢ; ὢ; ω◌̓◌̀; ὢ; ω◌̓◌̀; ) GREEK SMALL LETTER OMEGA WITH PSILI AND VARIA
+1F63;1F63;03C9 0314 0300;1F63;03C9 0314 0300; # (ὣ; ὣ; ω◌̔◌̀; ὣ; ω◌̔◌̀; ) GREEK SMALL LETTER OMEGA WITH DASIA AND VARIA
+1F64;1F64;03C9 0313 0301;1F64;03C9 0313 0301; # (ὤ; ὤ; ω◌̓◌Ì; ὤ; ω◌̓◌Ì; ) GREEK SMALL LETTER OMEGA WITH PSILI AND OXIA
+1F65;1F65;03C9 0314 0301;1F65;03C9 0314 0301; # (á½¥; á½¥; ω◌̔◌Ì; á½¥; ω◌̔◌Ì; ) GREEK SMALL LETTER OMEGA WITH DASIA AND OXIA
+1F66;1F66;03C9 0313 0342;1F66;03C9 0313 0342; # (ὦ; ὦ; ω◌̓◌͂; ὦ; ω◌̓◌͂; ) GREEK SMALL LETTER OMEGA WITH PSILI AND PERISPOMENI
+1F67;1F67;03C9 0314 0342;1F67;03C9 0314 0342; # (ὧ; ὧ; ω◌̔◌͂; ὧ; ω◌̔◌͂; ) GREEK SMALL LETTER OMEGA WITH DASIA AND PERISPOMENI
+1F68;1F68;03A9 0313;1F68;03A9 0313; # (Ὠ; Ὠ; Ω◌̓; Ὠ; Ω◌̓; ) GREEK CAPITAL LETTER OMEGA WITH PSILI
+1F69;1F69;03A9 0314;1F69;03A9 0314; # (Ὡ; Ὡ; Ω◌̔; Ὡ; Ω◌̔; ) GREEK CAPITAL LETTER OMEGA WITH DASIA
+1F6A;1F6A;03A9 0313 0300;1F6A;03A9 0313 0300; # (Ὢ; Ὢ; Ω◌̓◌̀; Ὢ; Ω◌̓◌̀; ) GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA
+1F6B;1F6B;03A9 0314 0300;1F6B;03A9 0314 0300; # (Ὣ; Ὣ; Ω◌̔◌̀; Ὣ; Ω◌̔◌̀; ) GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA
+1F6C;1F6C;03A9 0313 0301;1F6C;03A9 0313 0301; # (Ὤ; Ὤ; Ω◌̓◌Ì; Ὤ; Ω◌̓◌Ì; ) GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA
+1F6D;1F6D;03A9 0314 0301;1F6D;03A9 0314 0301; # (á½­; á½­; Ω◌̔◌Ì; á½­; Ω◌̔◌Ì; ) GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA
+1F6E;1F6E;03A9 0313 0342;1F6E;03A9 0313 0342; # (Ὦ; Ὦ; Ω◌̓◌͂; Ὦ; Ω◌̓◌͂; ) GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI
+1F6F;1F6F;03A9 0314 0342;1F6F;03A9 0314 0342; # (Ὧ; Ὧ; Ω◌̔◌͂; Ὧ; Ω◌̔◌͂; ) GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI
+1F70;1F70;03B1 0300;1F70;03B1 0300; # (ὰ; ὰ; α◌̀; ὰ; α◌̀; ) GREEK SMALL LETTER ALPHA WITH VARIA
+1F71;03AC;03B1 0301;03AC;03B1 0301; # (á½±; ά; α◌Ì; ά; α◌Ì; ) GREEK SMALL LETTER ALPHA WITH OXIA
+1F72;1F72;03B5 0300;1F72;03B5 0300; # (ὲ; ὲ; ε◌̀; ὲ; ε◌̀; ) GREEK SMALL LETTER EPSILON WITH VARIA
+1F73;03AD;03B5 0301;03AD;03B5 0301; # (á½³; έ; ε◌Ì; έ; ε◌Ì; ) GREEK SMALL LETTER EPSILON WITH OXIA
+1F74;1F74;03B7 0300;1F74;03B7 0300; # (ὴ; ὴ; η◌̀; ὴ; η◌̀; ) GREEK SMALL LETTER ETA WITH VARIA
+1F75;03AE;03B7 0301;03AE;03B7 0301; # (á½µ; ή; η◌Ì; ή; η◌Ì; ) GREEK SMALL LETTER ETA WITH OXIA
+1F76;1F76;03B9 0300;1F76;03B9 0300; # (ὶ; ὶ; ι◌̀; ὶ; ι◌̀; ) GREEK SMALL LETTER IOTA WITH VARIA
+1F77;03AF;03B9 0301;03AF;03B9 0301; # (á½·; ί; ι◌Ì; ί; ι◌Ì; ) GREEK SMALL LETTER IOTA WITH OXIA
+1F78;1F78;03BF 0300;1F78;03BF 0300; # (ὸ; ὸ; ο◌̀; ὸ; ο◌̀; ) GREEK SMALL LETTER OMICRON WITH VARIA
+1F79;03CC;03BF 0301;03CC;03BF 0301; # (á½¹; ÏŒ; ο◌Ì; ÏŒ; ο◌Ì; ) GREEK SMALL LETTER OMICRON WITH OXIA
+1F7A;1F7A;03C5 0300;1F7A;03C5 0300; # (ὺ; ὺ; υ◌̀; ὺ; υ◌̀; ) GREEK SMALL LETTER UPSILON WITH VARIA
+1F7B;03CD;03C5 0301;03CD;03C5 0301; # (á½»; Ï; Ï…â—ŒÌ; Ï; Ï…â—ŒÌ; ) GREEK SMALL LETTER UPSILON WITH OXIA
+1F7C;1F7C;03C9 0300;1F7C;03C9 0300; # (ὼ; ὼ; ω◌̀; ὼ; ω◌̀; ) GREEK SMALL LETTER OMEGA WITH VARIA
+1F7D;03CE;03C9 0301;03CE;03C9 0301; # (á½½; ÏŽ; ω◌Ì; ÏŽ; ω◌Ì; ) GREEK SMALL LETTER OMEGA WITH OXIA
+1F80;1F80;03B1 0313 0345;1F80;03B1 0313 0345; # (ᾀ; ᾀ; α◌̓◌ͅ; ᾀ; α◌̓◌ͅ; ) GREEK SMALL LETTER ALPHA WITH PSILI AND YPOGEGRAMMENI
+1F81;1F81;03B1 0314 0345;1F81;03B1 0314 0345; # (á¾; á¾; α◌̔◌ͅ; á¾; α◌̔◌ͅ; ) GREEK SMALL LETTER ALPHA WITH DASIA AND YPOGEGRAMMENI
+1F82;1F82;03B1 0313 0300 0345;1F82;03B1 0313 0300 0345; # (ᾂ; ᾂ; α◌̓◌̀◌ͅ; ᾂ; α◌̓◌̀◌ͅ; ) GREEK SMALL LETTER ALPHA WITH PSILI AND VARIA AND YPOGEGRAMMENI
+1F83;1F83;03B1 0314 0300 0345;1F83;03B1 0314 0300 0345; # (ᾃ; ᾃ; α◌̔◌̀◌ͅ; ᾃ; α◌̔◌̀◌ͅ; ) GREEK SMALL LETTER ALPHA WITH DASIA AND VARIA AND YPOGEGRAMMENI
+1F84;1F84;03B1 0313 0301 0345;1F84;03B1 0313 0301 0345; # (ᾄ; ᾄ; α◌̓◌Ì◌ͅ; ᾄ; α◌̓◌Ì◌ͅ; ) GREEK SMALL LETTER ALPHA WITH PSILI AND OXIA AND YPOGEGRAMMENI
+1F85;1F85;03B1 0314 0301 0345;1F85;03B1 0314 0301 0345; # (á¾…; á¾…; α◌̔◌Ì◌ͅ; á¾…; α◌̔◌Ì◌ͅ; ) GREEK SMALL LETTER ALPHA WITH DASIA AND OXIA AND YPOGEGRAMMENI
+1F86;1F86;03B1 0313 0342 0345;1F86;03B1 0313 0342 0345; # (ᾆ; ᾆ; α◌̓◌͂◌ͅ; ᾆ; α◌̓◌͂◌ͅ; ) GREEK SMALL LETTER ALPHA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI
+1F87;1F87;03B1 0314 0342 0345;1F87;03B1 0314 0342 0345; # (ᾇ; ᾇ; α◌̔◌͂◌ͅ; ᾇ; α◌̔◌͂◌ͅ; ) GREEK SMALL LETTER ALPHA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI
+1F88;1F88;0391 0313 0345;1F88;0391 0313 0345; # (ᾈ; ᾈ; Α◌̓◌ͅ; ᾈ; Α◌̓◌ͅ; ) GREEK CAPITAL LETTER ALPHA WITH PSILI AND PROSGEGRAMMENI
+1F89;1F89;0391 0314 0345;1F89;0391 0314 0345; # (ᾉ; ᾉ; Α◌̔◌ͅ; ᾉ; Α◌̔◌ͅ; ) GREEK CAPITAL LETTER ALPHA WITH DASIA AND PROSGEGRAMMENI
+1F8A;1F8A;0391 0313 0300 0345;1F8A;0391 0313 0300 0345; # (ᾊ; ᾊ; Α◌̓◌̀◌ͅ; ᾊ; Α◌̓◌̀◌ͅ; ) GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA AND PROSGEGRAMMENI
+1F8B;1F8B;0391 0314 0300 0345;1F8B;0391 0314 0300 0345; # (ᾋ; ᾋ; Α◌̔◌̀◌ͅ; ᾋ; Α◌̔◌̀◌ͅ; ) GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA AND PROSGEGRAMMENI
+1F8C;1F8C;0391 0313 0301 0345;1F8C;0391 0313 0301 0345; # (ᾌ; ᾌ; Α◌̓◌Ì◌ͅ; ᾌ; Α◌̓◌Ì◌ͅ; ) GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA AND PROSGEGRAMMENI
+1F8D;1F8D;0391 0314 0301 0345;1F8D;0391 0314 0301 0345; # (á¾; á¾; Α◌̔◌Ì◌ͅ; á¾; Α◌̔◌Ì◌ͅ; ) GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA AND PROSGEGRAMMENI
+1F8E;1F8E;0391 0313 0342 0345;1F8E;0391 0313 0342 0345; # (ᾎ; ᾎ; Α◌̓◌͂◌ͅ; ᾎ; Α◌̓◌͂◌ͅ; ) GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI
+1F8F;1F8F;0391 0314 0342 0345;1F8F;0391 0314 0342 0345; # (á¾; á¾; Α◌̔◌͂◌ͅ; á¾; Α◌̔◌͂◌ͅ; ) GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI
+1F90;1F90;03B7 0313 0345;1F90;03B7 0313 0345; # (á¾; á¾; η◌̓◌ͅ; á¾; η◌̓◌ͅ; ) GREEK SMALL LETTER ETA WITH PSILI AND YPOGEGRAMMENI
+1F91;1F91;03B7 0314 0345;1F91;03B7 0314 0345; # (ᾑ; ᾑ; η◌̔◌ͅ; ᾑ; η◌̔◌ͅ; ) GREEK SMALL LETTER ETA WITH DASIA AND YPOGEGRAMMENI
+1F92;1F92;03B7 0313 0300 0345;1F92;03B7 0313 0300 0345; # (ᾒ; ᾒ; η◌̓◌̀◌ͅ; ᾒ; η◌̓◌̀◌ͅ; ) GREEK SMALL LETTER ETA WITH PSILI AND VARIA AND YPOGEGRAMMENI
+1F93;1F93;03B7 0314 0300 0345;1F93;03B7 0314 0300 0345; # (ᾓ; ᾓ; η◌̔◌̀◌ͅ; ᾓ; η◌̔◌̀◌ͅ; ) GREEK SMALL LETTER ETA WITH DASIA AND VARIA AND YPOGEGRAMMENI
+1F94;1F94;03B7 0313 0301 0345;1F94;03B7 0313 0301 0345; # (á¾”; á¾”; η◌̓◌Ì◌ͅ; á¾”; η◌̓◌Ì◌ͅ; ) GREEK SMALL LETTER ETA WITH PSILI AND OXIA AND YPOGEGRAMMENI
+1F95;1F95;03B7 0314 0301 0345;1F95;03B7 0314 0301 0345; # (ᾕ; ᾕ; η◌̔◌Ì◌ͅ; ᾕ; η◌̔◌Ì◌ͅ; ) GREEK SMALL LETTER ETA WITH DASIA AND OXIA AND YPOGEGRAMMENI
+1F96;1F96;03B7 0313 0342 0345;1F96;03B7 0313 0342 0345; # (ᾖ; ᾖ; η◌̓◌͂◌ͅ; ᾖ; η◌̓◌͂◌ͅ; ) GREEK SMALL LETTER ETA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI
+1F97;1F97;03B7 0314 0342 0345;1F97;03B7 0314 0342 0345; # (ᾗ; ᾗ; η◌̔◌͂◌ͅ; ᾗ; η◌̔◌͂◌ͅ; ) GREEK SMALL LETTER ETA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI
+1F98;1F98;0397 0313 0345;1F98;0397 0313 0345; # (ᾘ; ᾘ; Η◌̓◌ͅ; ᾘ; Η◌̓◌ͅ; ) GREEK CAPITAL LETTER ETA WITH PSILI AND PROSGEGRAMMENI
+1F99;1F99;0397 0314 0345;1F99;0397 0314 0345; # (ᾙ; ᾙ; Η◌̔◌ͅ; ᾙ; Η◌̔◌ͅ; ) GREEK CAPITAL LETTER ETA WITH DASIA AND PROSGEGRAMMENI
+1F9A;1F9A;0397 0313 0300 0345;1F9A;0397 0313 0300 0345; # (ᾚ; ᾚ; Η◌̓◌̀◌ͅ; ᾚ; Η◌̓◌̀◌ͅ; ) GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA AND PROSGEGRAMMENI
+1F9B;1F9B;0397 0314 0300 0345;1F9B;0397 0314 0300 0345; # (ᾛ; ᾛ; Η◌̔◌̀◌ͅ; ᾛ; Η◌̔◌̀◌ͅ; ) GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA AND PROSGEGRAMMENI
+1F9C;1F9C;0397 0313 0301 0345;1F9C;0397 0313 0301 0345; # (ᾜ; ᾜ; Η◌̓◌Ì◌ͅ; ᾜ; Η◌̓◌Ì◌ͅ; ) GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA AND PROSGEGRAMMENI
+1F9D;1F9D;0397 0314 0301 0345;1F9D;0397 0314 0301 0345; # (á¾; á¾; Η◌̔◌Ì◌ͅ; á¾; Η◌̔◌Ì◌ͅ; ) GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA AND PROSGEGRAMMENI
+1F9E;1F9E;0397 0313 0342 0345;1F9E;0397 0313 0342 0345; # (ᾞ; ᾞ; Η◌̓◌͂◌ͅ; ᾞ; Η◌̓◌͂◌ͅ; ) GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI
+1F9F;1F9F;0397 0314 0342 0345;1F9F;0397 0314 0342 0345; # (ᾟ; ᾟ; Η◌̔◌͂◌ͅ; ᾟ; Η◌̔◌͂◌ͅ; ) GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI
+1FA0;1FA0;03C9 0313 0345;1FA0;03C9 0313 0345; # (ᾠ; ᾠ; ω◌̓◌ͅ; ᾠ; ω◌̓◌ͅ; ) GREEK SMALL LETTER OMEGA WITH PSILI AND YPOGEGRAMMENI
+1FA1;1FA1;03C9 0314 0345;1FA1;03C9 0314 0345; # (ᾡ; ᾡ; ω◌̔◌ͅ; ᾡ; ω◌̔◌ͅ; ) GREEK SMALL LETTER OMEGA WITH DASIA AND YPOGEGRAMMENI
+1FA2;1FA2;03C9 0313 0300 0345;1FA2;03C9 0313 0300 0345; # (ᾢ; ᾢ; ω◌̓◌̀◌ͅ; ᾢ; ω◌̓◌̀◌ͅ; ) GREEK SMALL LETTER OMEGA WITH PSILI AND VARIA AND YPOGEGRAMMENI
+1FA3;1FA3;03C9 0314 0300 0345;1FA3;03C9 0314 0300 0345; # (ᾣ; ᾣ; ω◌̔◌̀◌ͅ; ᾣ; ω◌̔◌̀◌ͅ; ) GREEK SMALL LETTER OMEGA WITH DASIA AND VARIA AND YPOGEGRAMMENI
+1FA4;1FA4;03C9 0313 0301 0345;1FA4;03C9 0313 0301 0345; # (ᾤ; ᾤ; ω◌̓◌Ì◌ͅ; ᾤ; ω◌̓◌Ì◌ͅ; ) GREEK SMALL LETTER OMEGA WITH PSILI AND OXIA AND YPOGEGRAMMENI
+1FA5;1FA5;03C9 0314 0301 0345;1FA5;03C9 0314 0301 0345; # (á¾¥; á¾¥; ω◌̔◌Ì◌ͅ; á¾¥; ω◌̔◌Ì◌ͅ; ) GREEK SMALL LETTER OMEGA WITH DASIA AND OXIA AND YPOGEGRAMMENI
+1FA6;1FA6;03C9 0313 0342 0345;1FA6;03C9 0313 0342 0345; # (ᾦ; ᾦ; ω◌̓◌͂◌ͅ; ᾦ; ω◌̓◌͂◌ͅ; ) GREEK SMALL LETTER OMEGA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI
+1FA7;1FA7;03C9 0314 0342 0345;1FA7;03C9 0314 0342 0345; # (ᾧ; ᾧ; ω◌̔◌͂◌ͅ; ᾧ; ω◌̔◌͂◌ͅ; ) GREEK SMALL LETTER OMEGA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI
+1FA8;1FA8;03A9 0313 0345;1FA8;03A9 0313 0345; # (ᾨ; ᾨ; Ω◌̓◌ͅ; ᾨ; Ω◌̓◌ͅ; ) GREEK CAPITAL LETTER OMEGA WITH PSILI AND PROSGEGRAMMENI
+1FA9;1FA9;03A9 0314 0345;1FA9;03A9 0314 0345; # (ᾩ; ᾩ; Ω◌̔◌ͅ; ᾩ; Ω◌̔◌ͅ; ) GREEK CAPITAL LETTER OMEGA WITH DASIA AND PROSGEGRAMMENI
+1FAA;1FAA;03A9 0313 0300 0345;1FAA;03A9 0313 0300 0345; # (ᾪ; ᾪ; Ω◌̓◌̀◌ͅ; ᾪ; Ω◌̓◌̀◌ͅ; ) GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA AND PROSGEGRAMMENI
+1FAB;1FAB;03A9 0314 0300 0345;1FAB;03A9 0314 0300 0345; # (ᾫ; ᾫ; Ω◌̔◌̀◌ͅ; ᾫ; Ω◌̔◌̀◌ͅ; ) GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA AND PROSGEGRAMMENI
+1FAC;1FAC;03A9 0313 0301 0345;1FAC;03A9 0313 0301 0345; # (ᾬ; ᾬ; Ω◌̓◌Ì◌ͅ; ᾬ; Ω◌̓◌Ì◌ͅ; ) GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA AND PROSGEGRAMMENI
+1FAD;1FAD;03A9 0314 0301 0345;1FAD;03A9 0314 0301 0345; # (á¾­; á¾­; Ω◌̔◌Ì◌ͅ; á¾­; Ω◌̔◌Ì◌ͅ; ) GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA AND PROSGEGRAMMENI
+1FAE;1FAE;03A9 0313 0342 0345;1FAE;03A9 0313 0342 0345; # (ᾮ; ᾮ; Ω◌̓◌͂◌ͅ; ᾮ; Ω◌̓◌͂◌ͅ; ) GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI
+1FAF;1FAF;03A9 0314 0342 0345;1FAF;03A9 0314 0342 0345; # (ᾯ; ᾯ; Ω◌̔◌͂◌ͅ; ᾯ; Ω◌̔◌͂◌ͅ; ) GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI
+1FB0;1FB0;03B1 0306;1FB0;03B1 0306; # (ᾰ; ᾰ; α◌̆; ᾰ; α◌̆; ) GREEK SMALL LETTER ALPHA WITH VRACHY
+1FB1;1FB1;03B1 0304;1FB1;03B1 0304; # (ᾱ; ᾱ; α◌̄; ᾱ; α◌̄; ) GREEK SMALL LETTER ALPHA WITH MACRON
+1FB2;1FB2;03B1 0300 0345;1FB2;03B1 0300 0345; # (ᾲ; ᾲ; α◌̀◌ͅ; ᾲ; α◌̀◌ͅ; ) GREEK SMALL LETTER ALPHA WITH VARIA AND YPOGEGRAMMENI
+1FB3;1FB3;03B1 0345;1FB3;03B1 0345; # (ᾳ; ᾳ; α◌ͅ; ᾳ; α◌ͅ; ) GREEK SMALL LETTER ALPHA WITH YPOGEGRAMMENI
+1FB4;1FB4;03B1 0301 0345;1FB4;03B1 0301 0345; # (á¾´; á¾´; α◌Ì◌ͅ; á¾´; α◌Ì◌ͅ; ) GREEK SMALL LETTER ALPHA WITH OXIA AND YPOGEGRAMMENI
+1FB6;1FB6;03B1 0342;1FB6;03B1 0342; # (ᾶ; ᾶ; α◌͂; ᾶ; α◌͂; ) GREEK SMALL LETTER ALPHA WITH PERISPOMENI
+1FB7;1FB7;03B1 0342 0345;1FB7;03B1 0342 0345; # (ᾷ; ᾷ; α◌͂◌ͅ; ᾷ; α◌͂◌ͅ; ) GREEK SMALL LETTER ALPHA WITH PERISPOMENI AND YPOGEGRAMMENI
+1FB8;1FB8;0391 0306;1FB8;0391 0306; # (Ᾰ; Ᾰ; Α◌̆; Ᾰ; Α◌̆; ) GREEK CAPITAL LETTER ALPHA WITH VRACHY
+1FB9;1FB9;0391 0304;1FB9;0391 0304; # (Ᾱ; Ᾱ; Α◌̄; Ᾱ; Α◌̄; ) GREEK CAPITAL LETTER ALPHA WITH MACRON
+1FBA;1FBA;0391 0300;1FBA;0391 0300; # (Ὰ; Ὰ; Α◌̀; Ὰ; Α◌̀; ) GREEK CAPITAL LETTER ALPHA WITH VARIA
+1FBB;0386;0391 0301;0386;0391 0301; # (á¾»; Ά; Α◌Ì; Ά; Α◌Ì; ) GREEK CAPITAL LETTER ALPHA WITH OXIA
+1FBC;1FBC;0391 0345;1FBC;0391 0345; # (ᾼ; ᾼ; Α◌ͅ; ᾼ; Α◌ͅ; ) GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI
+1FBD;1FBD;1FBD;0020 0313;0020 0313; # (᾽; ᾽; ᾽; ◌̓; ◌̓; ) GREEK KORONIS
+1FBE;03B9;03B9;03B9;03B9; # (ι; ι; ι; ι; ι; ) GREEK PROSGEGRAMMENI
+1FBF;1FBF;1FBF;0020 0313;0020 0313; # (᾿; ᾿; ᾿; ◌̓; ◌̓; ) GREEK PSILI
+1FC0;1FC0;1FC0;0020 0342;0020 0342; # (῀; ῀; ῀; ◌͂; ◌͂; ) GREEK PERISPOMENI
+1FC1;1FC1;00A8 0342;0020 0308 0342;0020 0308 0342; # (á¿; á¿; ¨◌͂; ◌̈◌͂; ◌̈◌͂; ) GREEK DIALYTIKA AND PERISPOMENI
+1FC2;1FC2;03B7 0300 0345;1FC2;03B7 0300 0345; # (ῂ; ῂ; η◌̀◌ͅ; ῂ; η◌̀◌ͅ; ) GREEK SMALL LETTER ETA WITH VARIA AND YPOGEGRAMMENI
+1FC3;1FC3;03B7 0345;1FC3;03B7 0345; # (ῃ; ῃ; η◌ͅ; ῃ; η◌ͅ; ) GREEK SMALL LETTER ETA WITH YPOGEGRAMMENI
+1FC4;1FC4;03B7 0301 0345;1FC4;03B7 0301 0345; # (á¿„; á¿„; η◌Ì◌ͅ; á¿„; η◌Ì◌ͅ; ) GREEK SMALL LETTER ETA WITH OXIA AND YPOGEGRAMMENI
+1FC6;1FC6;03B7 0342;1FC6;03B7 0342; # (ῆ; ῆ; η◌͂; ῆ; η◌͂; ) GREEK SMALL LETTER ETA WITH PERISPOMENI
+1FC7;1FC7;03B7 0342 0345;1FC7;03B7 0342 0345; # (ῇ; ῇ; η◌͂◌ͅ; ῇ; η◌͂◌ͅ; ) GREEK SMALL LETTER ETA WITH PERISPOMENI AND YPOGEGRAMMENI
+1FC8;1FC8;0395 0300;1FC8;0395 0300; # (Ὲ; Ὲ; Ε◌̀; Ὲ; Ε◌̀; ) GREEK CAPITAL LETTER EPSILON WITH VARIA
+1FC9;0388;0395 0301;0388;0395 0301; # (Έ; Έ; Ε◌Ì; Έ; Ε◌Ì; ) GREEK CAPITAL LETTER EPSILON WITH OXIA
+1FCA;1FCA;0397 0300;1FCA;0397 0300; # (Ὴ; Ὴ; Η◌̀; Ὴ; Η◌̀; ) GREEK CAPITAL LETTER ETA WITH VARIA
+1FCB;0389;0397 0301;0389;0397 0301; # (á¿‹; Ή; Η◌Ì; Ή; Η◌Ì; ) GREEK CAPITAL LETTER ETA WITH OXIA
+1FCC;1FCC;0397 0345;1FCC;0397 0345; # (ῌ; ῌ; Η◌ͅ; ῌ; Η◌ͅ; ) GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI
+1FCD;1FCD;1FBF 0300;0020 0313 0300;0020 0313 0300; # (á¿; á¿; ᾿◌̀; ◌̓◌̀; ◌̓◌̀; ) GREEK PSILI AND VARIA
+1FCE;1FCE;1FBF 0301;0020 0313 0301;0020 0313 0301; # (῎; ῎; ᾿◌Ì; ◌̓◌Ì; ◌̓◌Ì; ) GREEK PSILI AND OXIA
+1FCF;1FCF;1FBF 0342;0020 0313 0342;0020 0313 0342; # (á¿; á¿; ᾿◌͂; ◌̓◌͂; ◌̓◌͂; ) GREEK PSILI AND PERISPOMENI
+1FD0;1FD0;03B9 0306;1FD0;03B9 0306; # (á¿; á¿; ι◌̆; á¿; ι◌̆; ) GREEK SMALL LETTER IOTA WITH VRACHY
+1FD1;1FD1;03B9 0304;1FD1;03B9 0304; # (ῑ; ῑ; ι◌̄; ῑ; ι◌̄; ) GREEK SMALL LETTER IOTA WITH MACRON
+1FD2;1FD2;03B9 0308 0300;1FD2;03B9 0308 0300; # (ῒ; ῒ; ι◌̈◌̀; ῒ; ι◌̈◌̀; ) GREEK SMALL LETTER IOTA WITH DIALYTIKA AND VARIA
+1FD3;0390;03B9 0308 0301;0390;03B9 0308 0301; # (á¿“; Î; ι◌̈◌Ì; Î; ι◌̈◌Ì; ) GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA
+1FD6;1FD6;03B9 0342;1FD6;03B9 0342; # (ῖ; ῖ; ι◌͂; ῖ; ι◌͂; ) GREEK SMALL LETTER IOTA WITH PERISPOMENI
+1FD7;1FD7;03B9 0308 0342;1FD7;03B9 0308 0342; # (ῗ; ῗ; ι◌̈◌͂; ῗ; ι◌̈◌͂; ) GREEK SMALL LETTER IOTA WITH DIALYTIKA AND PERISPOMENI
+1FD8;1FD8;0399 0306;1FD8;0399 0306; # (Ῐ; Ῐ; Ι◌̆; Ῐ; Ι◌̆; ) GREEK CAPITAL LETTER IOTA WITH VRACHY
+1FD9;1FD9;0399 0304;1FD9;0399 0304; # (Ῑ; Ῑ; Ι◌̄; Ῑ; Ι◌̄; ) GREEK CAPITAL LETTER IOTA WITH MACRON
+1FDA;1FDA;0399 0300;1FDA;0399 0300; # (Ὶ; Ὶ; Ι◌̀; Ὶ; Ι◌̀; ) GREEK CAPITAL LETTER IOTA WITH VARIA
+1FDB;038A;0399 0301;038A;0399 0301; # (á¿›; Ί; Ι◌Ì; Ί; Ι◌Ì; ) GREEK CAPITAL LETTER IOTA WITH OXIA
+1FDD;1FDD;1FFE 0300;0020 0314 0300;0020 0314 0300; # (á¿; á¿; ῾◌̀; ◌̔◌̀; ◌̔◌̀; ) GREEK DASIA AND VARIA
+1FDE;1FDE;1FFE 0301;0020 0314 0301;0020 0314 0301; # (῞; ῞; ῾◌Ì; ◌̔◌Ì; ◌̔◌Ì; ) GREEK DASIA AND OXIA
+1FDF;1FDF;1FFE 0342;0020 0314 0342;0020 0314 0342; # (῟; ῟; ῾◌͂; ◌̔◌͂; ◌̔◌͂; ) GREEK DASIA AND PERISPOMENI
+1FE0;1FE0;03C5 0306;1FE0;03C5 0306; # (ῠ; ῠ; υ◌̆; ῠ; υ◌̆; ) GREEK SMALL LETTER UPSILON WITH VRACHY
+1FE1;1FE1;03C5 0304;1FE1;03C5 0304; # (ῡ; ῡ; υ◌̄; ῡ; υ◌̄; ) GREEK SMALL LETTER UPSILON WITH MACRON
+1FE2;1FE2;03C5 0308 0300;1FE2;03C5 0308 0300; # (ῢ; ῢ; υ◌̈◌̀; ῢ; υ◌̈◌̀; ) GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND VARIA
+1FE3;03B0;03C5 0308 0301;03B0;03C5 0308 0301; # (á¿£; ΰ; υ◌̈◌Ì; ΰ; υ◌̈◌Ì; ) GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND OXIA
+1FE4;1FE4;03C1 0313;1FE4;03C1 0313; # (ῤ; ῤ; Ï◌̓; ῤ; Ï◌̓; ) GREEK SMALL LETTER RHO WITH PSILI
+1FE5;1FE5;03C1 0314;1FE5;03C1 0314; # (á¿¥; á¿¥; Ï◌̔; á¿¥; Ï◌̔; ) GREEK SMALL LETTER RHO WITH DASIA
+1FE6;1FE6;03C5 0342;1FE6;03C5 0342; # (ῦ; ῦ; υ◌͂; ῦ; υ◌͂; ) GREEK SMALL LETTER UPSILON WITH PERISPOMENI
+1FE7;1FE7;03C5 0308 0342;1FE7;03C5 0308 0342; # (ῧ; ῧ; υ◌̈◌͂; ῧ; υ◌̈◌͂; ) GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND PERISPOMENI
+1FE8;1FE8;03A5 0306;1FE8;03A5 0306; # (Ῠ; Ῠ; Υ◌̆; Ῠ; Υ◌̆; ) GREEK CAPITAL LETTER UPSILON WITH VRACHY
+1FE9;1FE9;03A5 0304;1FE9;03A5 0304; # (Ῡ; Ῡ; Υ◌̄; Ῡ; Υ◌̄; ) GREEK CAPITAL LETTER UPSILON WITH MACRON
+1FEA;1FEA;03A5 0300;1FEA;03A5 0300; # (Ὺ; Ὺ; Υ◌̀; Ὺ; Υ◌̀; ) GREEK CAPITAL LETTER UPSILON WITH VARIA
+1FEB;038E;03A5 0301;038E;03A5 0301; # (á¿«; ÎŽ; Υ◌Ì; ÎŽ; Υ◌Ì; ) GREEK CAPITAL LETTER UPSILON WITH OXIA
+1FEC;1FEC;03A1 0314;1FEC;03A1 0314; # (Ῥ; Ῥ; Ρ◌̔; Ῥ; Ρ◌̔; ) GREEK CAPITAL LETTER RHO WITH DASIA
+1FED;1FED;00A8 0300;0020 0308 0300;0020 0308 0300; # (῭; ῭; ¨◌̀; ◌̈◌̀; ◌̈◌̀; ) GREEK DIALYTIKA AND VARIA
+1FEE;0385;00A8 0301;0020 0308 0301;0020 0308 0301; # (á¿®; Î…; ¨◌Ì; ◌̈◌Ì; ◌̈◌Ì; ) GREEK DIALYTIKA AND OXIA
+1FEF;0060;0060;0060;0060; # (`; `; `; `; `; ) GREEK VARIA
+1FF2;1FF2;03C9 0300 0345;1FF2;03C9 0300 0345; # (ῲ; ῲ; ω◌̀◌ͅ; ῲ; ω◌̀◌ͅ; ) GREEK SMALL LETTER OMEGA WITH VARIA AND YPOGEGRAMMENI
+1FF3;1FF3;03C9 0345;1FF3;03C9 0345; # (ῳ; ῳ; ω◌ͅ; ῳ; ω◌ͅ; ) GREEK SMALL LETTER OMEGA WITH YPOGEGRAMMENI
+1FF4;1FF4;03C9 0301 0345;1FF4;03C9 0301 0345; # (á¿´; á¿´; ω◌Ì◌ͅ; á¿´; ω◌Ì◌ͅ; ) GREEK SMALL LETTER OMEGA WITH OXIA AND YPOGEGRAMMENI
+1FF6;1FF6;03C9 0342;1FF6;03C9 0342; # (ῶ; ῶ; ω◌͂; ῶ; ω◌͂; ) GREEK SMALL LETTER OMEGA WITH PERISPOMENI
+1FF7;1FF7;03C9 0342 0345;1FF7;03C9 0342 0345; # (ῷ; ῷ; ω◌͂◌ͅ; ῷ; ω◌͂◌ͅ; ) GREEK SMALL LETTER OMEGA WITH PERISPOMENI AND YPOGEGRAMMENI
+1FF8;1FF8;039F 0300;1FF8;039F 0300; # (Ὸ; Ὸ; Ο◌̀; Ὸ; Ο◌̀; ) GREEK CAPITAL LETTER OMICRON WITH VARIA
+1FF9;038C;039F 0301;038C;039F 0301; # (Ό; ÎŒ; Ο◌Ì; ÎŒ; Ο◌Ì; ) GREEK CAPITAL LETTER OMICRON WITH OXIA
+1FFA;1FFA;03A9 0300;1FFA;03A9 0300; # (Ὼ; Ὼ; Ω◌̀; Ὼ; Ω◌̀; ) GREEK CAPITAL LETTER OMEGA WITH VARIA
+1FFB;038F;03A9 0301;038F;03A9 0301; # (á¿»; Î; Ω◌Ì; Î; Ω◌Ì; ) GREEK CAPITAL LETTER OMEGA WITH OXIA
+1FFC;1FFC;03A9 0345;1FFC;03A9 0345; # (ῼ; ῼ; Ω◌ͅ; ῼ; Ω◌ͅ; ) GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI
+1FFD;00B4;00B4;0020 0301;0020 0301; # (´; ´; ´; â—ŒÌ; â—ŒÌ; ) GREEK OXIA
+1FFE;1FFE;1FFE;0020 0314;0020 0314; # (῾; ῾; ῾; ◌̔; ◌̔; ) GREEK DASIA
+2000;2002;2002;0020;0020; # ( ;  ;  ; ; ; ) EN QUAD
+2001;2003;2003;0020;0020; # (â€;  ;  ; ; ; ) EM QUAD
+2002;2002;2002;0020;0020; # ( ;  ;  ; ; ; ) EN SPACE
+2003;2003;2003;0020;0020; # ( ;  ;  ; ; ; ) EM SPACE
+2004;2004;2004;0020;0020; # ( ;  ;  ; ; ; ) THREE-PER-EM SPACE
+2005;2005;2005;0020;0020; # ( ;  ;  ; ; ; ) FOUR-PER-EM SPACE
+2006;2006;2006;0020;0020; # ( ;  ;  ; ; ; ) SIX-PER-EM SPACE
+2007;2007;2007;0020;0020; # ( ;  ;  ; ; ; ) FIGURE SPACE
+2008;2008;2008;0020;0020; # ( ;  ;  ; ; ; ) PUNCTUATION SPACE
+2009;2009;2009;0020;0020; # ( ;  ;  ; ; ; ) THIN SPACE
+200A;200A;200A;0020;0020; # ( ;  ;  ; ; ; ) HAIR SPACE
+2011;2011;2011;2010;2010; # (‑; ‑; ‑; â€; â€; ) NON-BREAKING HYPHEN
+2017;2017;2017;0020 0333;0020 0333; # (‗; ‗; ‗; ◌̳; ◌̳; ) DOUBLE LOW LINE
+2024;2024;2024;002E;002E; # (․; ․; ․; .; .; ) ONE DOT LEADER
+2025;2025;2025;002E 002E;002E 002E; # (‥; ‥; ‥; ..; ..; ) TWO DOT LEADER
+2026;2026;2026;002E 002E 002E;002E 002E 002E; # (…; …; …; ...; ...; ) HORIZONTAL ELLIPSIS
+202F;202F;202F;0020;0020; # ( ;  ;  ; ; ; ) NARROW NO-BREAK SPACE
+2033;2033;2033;2032 2032;2032 2032; # (″; ″; ″; ′′; ′′; ) DOUBLE PRIME
+2034;2034;2034;2032 2032 2032;2032 2032 2032; # (‴; ‴; ‴; ′′′; ′′′; ) TRIPLE PRIME
+2036;2036;2036;2035 2035;2035 2035; # (‶; ‶; ‶; ‵‵; ‵‵; ) REVERSED DOUBLE PRIME
+2037;2037;2037;2035 2035 2035;2035 2035 2035; # (‷; ‷; ‷; ‵‵‵; ‵‵‵; ) REVERSED TRIPLE PRIME
+203C;203C;203C;0021 0021;0021 0021; # (‼; ‼; ‼; !!; !!; ) DOUBLE EXCLAMATION MARK
+203E;203E;203E;0020 0305;0020 0305; # (‾; ‾; ‾; ◌̅; ◌̅; ) OVERLINE
+2047;2047;2047;003F 003F;003F 003F; # (â‡; â‡; â‡; ??; ??; ) DOUBLE QUESTION MARK
+2048;2048;2048;003F 0021;003F 0021; # (âˆ; âˆ; âˆ; ?!; ?!; ) QUESTION EXCLAMATION MARK
+2049;2049;2049;0021 003F;0021 003F; # (â‰; â‰; â‰; !?; !?; ) EXCLAMATION QUESTION MARK
+2057;2057;2057;2032 2032 2032 2032;2032 2032 2032 2032; # (â—; â—; â—; ′′′′; ′′′′; ) QUADRUPLE PRIME
+205F;205F;205F;0020;0020; # (âŸ; âŸ; âŸ; ; ; ) MEDIUM MATHEMATICAL SPACE
+2070;2070;2070;0030;0030; # (â°; â°; â°; 0; 0; ) SUPERSCRIPT ZERO
+2071;2071;2071;0069;0069; # (â±; â±; â±; i; i; ) SUPERSCRIPT LATIN SMALL LETTER I
+2074;2074;2074;0034;0034; # (â´; â´; â´; 4; 4; ) SUPERSCRIPT FOUR
+2075;2075;2075;0035;0035; # (âµ; âµ; âµ; 5; 5; ) SUPERSCRIPT FIVE
+2076;2076;2076;0036;0036; # (â¶; â¶; â¶; 6; 6; ) SUPERSCRIPT SIX
+2077;2077;2077;0037;0037; # (â·; â·; â·; 7; 7; ) SUPERSCRIPT SEVEN
+2078;2078;2078;0038;0038; # (â¸; â¸; â¸; 8; 8; ) SUPERSCRIPT EIGHT
+2079;2079;2079;0039;0039; # (â¹; â¹; â¹; 9; 9; ) SUPERSCRIPT NINE
+207A;207A;207A;002B;002B; # (âº; âº; âº; +; +; ) SUPERSCRIPT PLUS SIGN
+207B;207B;207B;2212;2212; # (â»; â»; â»; −; −; ) SUPERSCRIPT MINUS
+207C;207C;207C;003D;003D; # (â¼; â¼; â¼; =; =; ) SUPERSCRIPT EQUALS SIGN
+207D;207D;207D;0028;0028; # (â½; â½; â½; (; (; ) SUPERSCRIPT LEFT PARENTHESIS
+207E;207E;207E;0029;0029; # (â¾; â¾; â¾; ); ); ) SUPERSCRIPT RIGHT PARENTHESIS
+207F;207F;207F;006E;006E; # (â¿; â¿; â¿; n; n; ) SUPERSCRIPT LATIN SMALL LETTER N
+2080;2080;2080;0030;0030; # (â‚€; â‚€; â‚€; 0; 0; ) SUBSCRIPT ZERO
+2081;2081;2081;0031;0031; # (â‚; â‚; â‚; 1; 1; ) SUBSCRIPT ONE
+2082;2082;2082;0032;0032; # (â‚‚; â‚‚; â‚‚; 2; 2; ) SUBSCRIPT TWO
+2083;2083;2083;0033;0033; # (₃; ₃; ₃; 3; 3; ) SUBSCRIPT THREE
+2084;2084;2084;0034;0034; # (â‚„; â‚„; â‚„; 4; 4; ) SUBSCRIPT FOUR
+2085;2085;2085;0035;0035; # (â‚…; â‚…; â‚…; 5; 5; ) SUBSCRIPT FIVE
+2086;2086;2086;0036;0036; # (₆; ₆; ₆; 6; 6; ) SUBSCRIPT SIX
+2087;2087;2087;0037;0037; # (₇; ₇; ₇; 7; 7; ) SUBSCRIPT SEVEN
+2088;2088;2088;0038;0038; # (₈; ₈; ₈; 8; 8; ) SUBSCRIPT EIGHT
+2089;2089;2089;0039;0039; # (₉; ₉; ₉; 9; 9; ) SUBSCRIPT NINE
+208A;208A;208A;002B;002B; # (₊; ₊; ₊; +; +; ) SUBSCRIPT PLUS SIGN
+208B;208B;208B;2212;2212; # (₋; ₋; ₋; −; −; ) SUBSCRIPT MINUS
+208C;208C;208C;003D;003D; # (₌; ₌; ₌; =; =; ) SUBSCRIPT EQUALS SIGN
+208D;208D;208D;0028;0028; # (â‚; â‚; â‚; (; (; ) SUBSCRIPT LEFT PARENTHESIS
+208E;208E;208E;0029;0029; # (₎; ₎; ₎; ); ); ) SUBSCRIPT RIGHT PARENTHESIS
+20A8;20A8;20A8;0052 0073;0052 0073; # (₨; ₨; ₨; Rs; Rs; ) RUPEE SIGN
+2100;2100;2100;0061 002F 0063;0061 002F 0063; # (â„€; â„€; â„€; a/c; a/c; ) ACCOUNT OF
+2101;2101;2101;0061 002F 0073;0061 002F 0073; # (â„; â„; â„; a/s; a/s; ) ADDRESSED TO THE SUBJECT
+2102;2102;2102;0043;0043; # (â„‚; â„‚; â„‚; C; C; ) DOUBLE-STRUCK CAPITAL C
+2103;2103;2103;00B0 0043;00B0 0043; # (℃; ℃; ℃; °C; °C; ) DEGREE CELSIUS
+2105;2105;2105;0063 002F 006F;0063 002F 006F; # (â„…; â„…; â„…; c/o; c/o; ) CARE OF
+2106;2106;2106;0063 002F 0075;0063 002F 0075; # (℆; ℆; ℆; c/u; c/u; ) CADA UNA
+2107;2107;2107;0190;0190; # (ℇ; ℇ; ℇ; Æ; Æ; ) EULER CONSTANT
+2109;2109;2109;00B0 0046;00B0 0046; # (℉; ℉; ℉; °F; °F; ) DEGREE FAHRENHEIT
+210A;210A;210A;0067;0067; # (ℊ; ℊ; ℊ; g; g; ) SCRIPT SMALL G
+210B;210B;210B;0048;0048; # (â„‹; â„‹; â„‹; H; H; ) SCRIPT CAPITAL H
+210C;210C;210C;0048;0048; # (ℌ; ℌ; ℌ; H; H; ) BLACK-LETTER CAPITAL H
+210D;210D;210D;0048;0048; # (â„; â„; â„; H; H; ) DOUBLE-STRUCK CAPITAL H
+210E;210E;210E;0068;0068; # (ℎ; ℎ; ℎ; h; h; ) PLANCK CONSTANT
+210F;210F;210F;0127;0127; # (â„; â„; â„; ħ; ħ; ) PLANCK CONSTANT OVER TWO PI
+2110;2110;2110;0049;0049; # (â„; â„; â„; I; I; ) SCRIPT CAPITAL I
+2111;2111;2111;0049;0049; # (â„‘; â„‘; â„‘; I; I; ) BLACK-LETTER CAPITAL I
+2112;2112;2112;004C;004C; # (â„’; â„’; â„’; L; L; ) SCRIPT CAPITAL L
+2113;2113;2113;006C;006C; # (â„“; â„“; â„“; l; l; ) SCRIPT SMALL L
+2115;2115;2115;004E;004E; # (â„•; â„•; â„•; N; N; ) DOUBLE-STRUCK CAPITAL N
+2116;2116;2116;004E 006F;004E 006F; # (â„–; â„–; â„–; No; No; ) NUMERO SIGN
+2119;2119;2119;0050;0050; # (â„™; â„™; â„™; P; P; ) DOUBLE-STRUCK CAPITAL P
+211A;211A;211A;0051;0051; # (ℚ; ℚ; ℚ; Q; Q; ) DOUBLE-STRUCK CAPITAL Q
+211B;211B;211B;0052;0052; # (â„›; â„›; â„›; R; R; ) SCRIPT CAPITAL R
+211C;211C;211C;0052;0052; # (ℜ; ℜ; ℜ; R; R; ) BLACK-LETTER CAPITAL R
+211D;211D;211D;0052;0052; # (â„; â„; â„; R; R; ) DOUBLE-STRUCK CAPITAL R
+2120;2120;2120;0053 004D;0053 004D; # (â„ ; â„ ; â„ ; SM; SM; ) SERVICE MARK
+2121;2121;2121;0054 0045 004C;0054 0045 004C; # (â„¡; â„¡; â„¡; TEL; TEL; ) TELEPHONE SIGN
+2122;2122;2122;0054 004D;0054 004D; # (â„¢; â„¢; â„¢; TM; TM; ) TRADE MARK SIGN
+2124;2124;2124;005A;005A; # (ℤ; ℤ; ℤ; Z; Z; ) DOUBLE-STRUCK CAPITAL Z
+2126;03A9;03A9;03A9;03A9; # (Ω; Ω; Ω; Ω; Ω; ) OHM SIGN
+2128;2128;2128;005A;005A; # (ℨ; ℨ; ℨ; Z; Z; ) BLACK-LETTER CAPITAL Z
+212A;004B;004B;004B;004B; # (K; K; K; K; K; ) KELVIN SIGN
+212B;00C5;0041 030A;00C5;0041 030A; # (Å; Å; A◌̊; Å; A◌̊; ) ANGSTROM SIGN
+212C;212C;212C;0042;0042; # (ℬ; ℬ; ℬ; B; B; ) SCRIPT CAPITAL B
+212D;212D;212D;0043;0043; # (â„­; â„­; â„­; C; C; ) BLACK-LETTER CAPITAL C
+212F;212F;212F;0065;0065; # (ℯ; ℯ; ℯ; e; e; ) SCRIPT SMALL E
+2130;2130;2130;0045;0045; # (â„°; â„°; â„°; E; E; ) SCRIPT CAPITAL E
+2131;2131;2131;0046;0046; # (ℱ; ℱ; ℱ; F; F; ) SCRIPT CAPITAL F
+2133;2133;2133;004D;004D; # (ℳ; ℳ; ℳ; M; M; ) SCRIPT CAPITAL M
+2134;2134;2134;006F;006F; # (â„´; â„´; â„´; o; o; ) SCRIPT SMALL O
+2135;2135;2135;05D0;05D0; # (ℵ; ℵ; ℵ; ×; ×; ) ALEF SYMBOL
+2136;2136;2136;05D1;05D1; # (ℶ; ℶ; ℶ; ב; ב; ) BET SYMBOL
+2137;2137;2137;05D2;05D2; # (â„·; â„·; â„·; ×’; ×’; ) GIMEL SYMBOL
+2138;2138;2138;05D3;05D3; # (ℸ; ℸ; ℸ; ד; ד; ) DALET SYMBOL
+2139;2139;2139;0069;0069; # (ℹ; ℹ; ℹ; i; i; ) INFORMATION SOURCE
+213B;213B;213B;0046 0041 0058;0046 0041 0058; # (â„»; â„»; â„»; FAX; FAX; ) FACSIMILE SIGN
+213D;213D;213D;03B3;03B3; # (ℽ; ℽ; ℽ; γ; γ; ) DOUBLE-STRUCK SMALL GAMMA
+213E;213E;213E;0393;0393; # (ℾ; ℾ; ℾ; Γ; Γ; ) DOUBLE-STRUCK CAPITAL GAMMA
+213F;213F;213F;03A0;03A0; # (ℿ; ℿ; ℿ; Π; Π; ) DOUBLE-STRUCK CAPITAL PI
+2140;2140;2140;2211;2211; # (⅀; ⅀; ⅀; ∑; ∑; ) DOUBLE-STRUCK N-ARY SUMMATION
+2145;2145;2145;0044;0044; # (â……; â……; â……; D; D; ) DOUBLE-STRUCK ITALIC CAPITAL D
+2146;2146;2146;0064;0064; # (â…†; â…†; â…†; d; d; ) DOUBLE-STRUCK ITALIC SMALL D
+2147;2147;2147;0065;0065; # (â…‡; â…‡; â…‡; e; e; ) DOUBLE-STRUCK ITALIC SMALL E
+2148;2148;2148;0069;0069; # (â…ˆ; â…ˆ; â…ˆ; i; i; ) DOUBLE-STRUCK ITALIC SMALL I
+2149;2149;2149;006A;006A; # (â…‰; â…‰; â…‰; j; j; ) DOUBLE-STRUCK ITALIC SMALL J
+2153;2153;2153;0031 2044 0033;0031 2044 0033; # (â…“; â…“; â…“; 1â„3; 1â„3; ) VULGAR FRACTION ONE THIRD
+2154;2154;2154;0032 2044 0033;0032 2044 0033; # (â…”; â…”; â…”; 2â„3; 2â„3; ) VULGAR FRACTION TWO THIRDS
+2155;2155;2155;0031 2044 0035;0031 2044 0035; # (â…•; â…•; â…•; 1â„5; 1â„5; ) VULGAR FRACTION ONE FIFTH
+2156;2156;2156;0032 2044 0035;0032 2044 0035; # (â…–; â…–; â…–; 2â„5; 2â„5; ) VULGAR FRACTION TWO FIFTHS
+2157;2157;2157;0033 2044 0035;0033 2044 0035; # (â…—; â…—; â…—; 3â„5; 3â„5; ) VULGAR FRACTION THREE FIFTHS
+2158;2158;2158;0034 2044 0035;0034 2044 0035; # (â…˜; â…˜; â…˜; 4â„5; 4â„5; ) VULGAR FRACTION FOUR FIFTHS
+2159;2159;2159;0031 2044 0036;0031 2044 0036; # (â…™; â…™; â…™; 1â„6; 1â„6; ) VULGAR FRACTION ONE SIXTH
+215A;215A;215A;0035 2044 0036;0035 2044 0036; # (â…š; â…š; â…š; 5â„6; 5â„6; ) VULGAR FRACTION FIVE SIXTHS
+215B;215B;215B;0031 2044 0038;0031 2044 0038; # (â…›; â…›; â…›; 1â„8; 1â„8; ) VULGAR FRACTION ONE EIGHTH
+215C;215C;215C;0033 2044 0038;0033 2044 0038; # (â…œ; â…œ; â…œ; 3â„8; 3â„8; ) VULGAR FRACTION THREE EIGHTHS
+215D;215D;215D;0035 2044 0038;0035 2044 0038; # (â…; â…; â…; 5â„8; 5â„8; ) VULGAR FRACTION FIVE EIGHTHS
+215E;215E;215E;0037 2044 0038;0037 2044 0038; # (â…ž; â…ž; â…ž; 7â„8; 7â„8; ) VULGAR FRACTION SEVEN EIGHTHS
+215F;215F;215F;0031 2044;0031 2044; # (â…Ÿ; â…Ÿ; â…Ÿ; 1â„; 1â„; ) FRACTION NUMERATOR ONE
+2160;2160;2160;0049;0049; # (â… ; â… ; â… ; I; I; ) ROMAN NUMERAL ONE
+2161;2161;2161;0049 0049;0049 0049; # (â…¡; â…¡; â…¡; II; II; ) ROMAN NUMERAL TWO
+2162;2162;2162;0049 0049 0049;0049 0049 0049; # (â…¢; â…¢; â…¢; III; III; ) ROMAN NUMERAL THREE
+2163;2163;2163;0049 0056;0049 0056; # (â…£; â…£; â…£; IV; IV; ) ROMAN NUMERAL FOUR
+2164;2164;2164;0056;0056; # (â…¤; â…¤; â…¤; V; V; ) ROMAN NUMERAL FIVE
+2165;2165;2165;0056 0049;0056 0049; # (â…¥; â…¥; â…¥; VI; VI; ) ROMAN NUMERAL SIX
+2166;2166;2166;0056 0049 0049;0056 0049 0049; # (â…¦; â…¦; â…¦; VII; VII; ) ROMAN NUMERAL SEVEN
+2167;2167;2167;0056 0049 0049 0049;0056 0049 0049 0049; # (â…§; â…§; â…§; VIII; VIII; ) ROMAN NUMERAL EIGHT
+2168;2168;2168;0049 0058;0049 0058; # (â…¨; â…¨; â…¨; IX; IX; ) ROMAN NUMERAL NINE
+2169;2169;2169;0058;0058; # (â…©; â…©; â…©; X; X; ) ROMAN NUMERAL TEN
+216A;216A;216A;0058 0049;0058 0049; # (â…ª; â…ª; â…ª; XI; XI; ) ROMAN NUMERAL ELEVEN
+216B;216B;216B;0058 0049 0049;0058 0049 0049; # (â…«; â…«; â…«; XII; XII; ) ROMAN NUMERAL TWELVE
+216C;216C;216C;004C;004C; # (â…¬; â…¬; â…¬; L; L; ) ROMAN NUMERAL FIFTY
+216D;216D;216D;0043;0043; # (â…­; â…­; â…­; C; C; ) ROMAN NUMERAL ONE HUNDRED
+216E;216E;216E;0044;0044; # (â…®; â…®; â…®; D; D; ) ROMAN NUMERAL FIVE HUNDRED
+216F;216F;216F;004D;004D; # (â…¯; â…¯; â…¯; M; M; ) ROMAN NUMERAL ONE THOUSAND
+2170;2170;2170;0069;0069; # (â…°; â…°; â…°; i; i; ) SMALL ROMAN NUMERAL ONE
+2171;2171;2171;0069 0069;0069 0069; # (â…±; â…±; â…±; ii; ii; ) SMALL ROMAN NUMERAL TWO
+2172;2172;2172;0069 0069 0069;0069 0069 0069; # (â…²; â…²; â…²; iii; iii; ) SMALL ROMAN NUMERAL THREE
+2173;2173;2173;0069 0076;0069 0076; # (â…³; â…³; â…³; iv; iv; ) SMALL ROMAN NUMERAL FOUR
+2174;2174;2174;0076;0076; # (â…´; â…´; â…´; v; v; ) SMALL ROMAN NUMERAL FIVE
+2175;2175;2175;0076 0069;0076 0069; # (â…µ; â…µ; â…µ; vi; vi; ) SMALL ROMAN NUMERAL SIX
+2176;2176;2176;0076 0069 0069;0076 0069 0069; # (â…¶; â…¶; â…¶; vii; vii; ) SMALL ROMAN NUMERAL SEVEN
+2177;2177;2177;0076 0069 0069 0069;0076 0069 0069 0069; # (â…·; â…·; â…·; viii; viii; ) SMALL ROMAN NUMERAL EIGHT
+2178;2178;2178;0069 0078;0069 0078; # (â…¸; â…¸; â…¸; ix; ix; ) SMALL ROMAN NUMERAL NINE
+2179;2179;2179;0078;0078; # (â…¹; â…¹; â…¹; x; x; ) SMALL ROMAN NUMERAL TEN
+217A;217A;217A;0078 0069;0078 0069; # (â…º; â…º; â…º; xi; xi; ) SMALL ROMAN NUMERAL ELEVEN
+217B;217B;217B;0078 0069 0069;0078 0069 0069; # (â…»; â…»; â…»; xii; xii; ) SMALL ROMAN NUMERAL TWELVE
+217C;217C;217C;006C;006C; # (â…¼; â…¼; â…¼; l; l; ) SMALL ROMAN NUMERAL FIFTY
+217D;217D;217D;0063;0063; # (â…½; â…½; â…½; c; c; ) SMALL ROMAN NUMERAL ONE HUNDRED
+217E;217E;217E;0064;0064; # (â…¾; â…¾; â…¾; d; d; ) SMALL ROMAN NUMERAL FIVE HUNDRED
+217F;217F;217F;006D;006D; # (â…¿; â…¿; â…¿; m; m; ) SMALL ROMAN NUMERAL ONE THOUSAND
+219A;219A;2190 0338;219A;2190 0338; # (↚; ↚; â†â—ŒÌ¸; ↚; â†â—ŒÌ¸; ) LEFTWARDS ARROW WITH STROKE
+219B;219B;2192 0338;219B;2192 0338; # (↛; ↛; →◌̸; ↛; →◌̸; ) RIGHTWARDS ARROW WITH STROKE
+21AE;21AE;2194 0338;21AE;2194 0338; # (↮; ↮; ↔◌̸; ↮; ↔◌̸; ) LEFT RIGHT ARROW WITH STROKE
+21CD;21CD;21D0 0338;21CD;21D0 0338; # (â‡; â‡; â‡â—ŒÌ¸; â‡; â‡â—ŒÌ¸; ) LEFTWARDS DOUBLE ARROW WITH STROKE
+21CE;21CE;21D4 0338;21CE;21D4 0338; # (⇎; ⇎; ⇔◌̸; ⇎; ⇔◌̸; ) LEFT RIGHT DOUBLE ARROW WITH STROKE
+21CF;21CF;21D2 0338;21CF;21D2 0338; # (â‡; â‡; ⇒◌̸; â‡; ⇒◌̸; ) RIGHTWARDS DOUBLE ARROW WITH STROKE
+2204;2204;2203 0338;2204;2203 0338; # (∄; ∄; ∃◌̸; ∄; ∃◌̸; ) THERE DOES NOT EXIST
+2209;2209;2208 0338;2209;2208 0338; # (∉; ∉; ∈◌̸; ∉; ∈◌̸; ) NOT AN ELEMENT OF
+220C;220C;220B 0338;220C;220B 0338; # (∌; ∌; ∋◌̸; ∌; ∋◌̸; ) DOES NOT CONTAIN AS MEMBER
+2224;2224;2223 0338;2224;2223 0338; # (∤; ∤; ∣◌̸; ∤; ∣◌̸; ) DOES NOT DIVIDE
+2226;2226;2225 0338;2226;2225 0338; # (∦; ∦; ∥◌̸; ∦; ∥◌̸; ) NOT PARALLEL TO
+222C;222C;222C;222B 222B;222B 222B; # (∬; ∬; ∬; ∫∫; ∫∫; ) DOUBLE INTEGRAL
+222D;222D;222D;222B 222B 222B;222B 222B 222B; # (∭; ∭; ∭; ∫∫∫; ∫∫∫; ) TRIPLE INTEGRAL
+222F;222F;222F;222E 222E;222E 222E; # (∯; ∯; ∯; ∮∮; ∮∮; ) SURFACE INTEGRAL
+2230;2230;2230;222E 222E 222E;222E 222E 222E; # (∰; ∰; ∰; ∮∮∮; ∮∮∮; ) VOLUME INTEGRAL
+2241;2241;223C 0338;2241;223C 0338; # (â‰; â‰; ∼◌̸; â‰; ∼◌̸; ) NOT TILDE
+2244;2244;2243 0338;2244;2243 0338; # (≄; ≄; ≃◌̸; ≄; ≃◌̸; ) NOT ASYMPTOTICALLY EQUAL TO
+2247;2247;2245 0338;2247;2245 0338; # (≇; ≇; ≅◌̸; ≇; ≅◌̸; ) NEITHER APPROXIMATELY NOR ACTUALLY EQUAL TO
+2249;2249;2248 0338;2249;2248 0338; # (≉; ≉; ≈◌̸; ≉; ≈◌̸; ) NOT ALMOST EQUAL TO
+2260;2260;003D 0338;2260;003D 0338; # (≠; ≠; =◌̸; ≠; =◌̸; ) NOT EQUAL TO
+2262;2262;2261 0338;2262;2261 0338; # (≢; ≢; ≡◌̸; ≢; ≡◌̸; ) NOT IDENTICAL TO
+226D;226D;224D 0338;226D;224D 0338; # (≭; ≭; â‰â—ŒÌ¸; ≭; â‰â—ŒÌ¸; ) NOT EQUIVALENT TO
+226E;226E;003C 0338;226E;003C 0338; # (≮; ≮; <◌̸; ≮; <◌̸; ) NOT LESS-THAN
+226F;226F;003E 0338;226F;003E 0338; # (≯; ≯; >◌̸; ≯; >◌̸; ) NOT GREATER-THAN
+2270;2270;2264 0338;2270;2264 0338; # (≰; ≰; ≤◌̸; ≰; ≤◌̸; ) NEITHER LESS-THAN NOR EQUAL TO
+2271;2271;2265 0338;2271;2265 0338; # (≱; ≱; ≥◌̸; ≱; ≥◌̸; ) NEITHER GREATER-THAN NOR EQUAL TO
+2274;2274;2272 0338;2274;2272 0338; # (≴; ≴; ≲◌̸; ≴; ≲◌̸; ) NEITHER LESS-THAN NOR EQUIVALENT TO
+2275;2275;2273 0338;2275;2273 0338; # (≵; ≵; ≳◌̸; ≵; ≳◌̸; ) NEITHER GREATER-THAN NOR EQUIVALENT TO
+2278;2278;2276 0338;2278;2276 0338; # (≸; ≸; ≶◌̸; ≸; ≶◌̸; ) NEITHER LESS-THAN NOR GREATER-THAN
+2279;2279;2277 0338;2279;2277 0338; # (≹; ≹; ≷◌̸; ≹; ≷◌̸; ) NEITHER GREATER-THAN NOR LESS-THAN
+2280;2280;227A 0338;2280;227A 0338; # (⊀; ⊀; ≺◌̸; ⊀; ≺◌̸; ) DOES NOT PRECEDE
+2281;2281;227B 0338;2281;227B 0338; # (âŠ; âŠ; ≻◌̸; âŠ; ≻◌̸; ) DOES NOT SUCCEED
+2284;2284;2282 0338;2284;2282 0338; # (⊄; ⊄; ⊂◌̸; ⊄; ⊂◌̸; ) NOT A SUBSET OF
+2285;2285;2283 0338;2285;2283 0338; # (⊅; ⊅; ⊃◌̸; ⊅; ⊃◌̸; ) NOT A SUPERSET OF
+2288;2288;2286 0338;2288;2286 0338; # (⊈; ⊈; ⊆◌̸; ⊈; ⊆◌̸; ) NEITHER A SUBSET OF NOR EQUAL TO
+2289;2289;2287 0338;2289;2287 0338; # (⊉; ⊉; ⊇◌̸; ⊉; ⊇◌̸; ) NEITHER A SUPERSET OF NOR EQUAL TO
+22AC;22AC;22A2 0338;22AC;22A2 0338; # (⊬; ⊬; ⊢◌̸; ⊬; ⊢◌̸; ) DOES NOT PROVE
+22AD;22AD;22A8 0338;22AD;22A8 0338; # (⊭; ⊭; ⊨◌̸; ⊭; ⊨◌̸; ) NOT TRUE
+22AE;22AE;22A9 0338;22AE;22A9 0338; # (⊮; ⊮; ⊩◌̸; ⊮; ⊩◌̸; ) DOES NOT FORCE
+22AF;22AF;22AB 0338;22AF;22AB 0338; # (⊯; ⊯; ⊫◌̸; ⊯; ⊫◌̸; ) NEGATED DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE
+22E0;22E0;227C 0338;22E0;227C 0338; # (⋠; ⋠; ≼◌̸; ⋠; ≼◌̸; ) DOES NOT PRECEDE OR EQUAL
+22E1;22E1;227D 0338;22E1;227D 0338; # (⋡; ⋡; ≽◌̸; ⋡; ≽◌̸; ) DOES NOT SUCCEED OR EQUAL
+22E2;22E2;2291 0338;22E2;2291 0338; # (⋢; ⋢; ⊑◌̸; ⋢; ⊑◌̸; ) NOT SQUARE IMAGE OF OR EQUAL TO
+22E3;22E3;2292 0338;22E3;2292 0338; # (⋣; ⋣; ⊒◌̸; ⋣; ⊒◌̸; ) NOT SQUARE ORIGINAL OF OR EQUAL TO
+22EA;22EA;22B2 0338;22EA;22B2 0338; # (⋪; ⋪; ⊲◌̸; ⋪; ⊲◌̸; ) NOT NORMAL SUBGROUP OF
+22EB;22EB;22B3 0338;22EB;22B3 0338; # (⋫; ⋫; ⊳◌̸; ⋫; ⊳◌̸; ) DOES NOT CONTAIN AS NORMAL SUBGROUP
+22EC;22EC;22B4 0338;22EC;22B4 0338; # (⋬; ⋬; ⊴◌̸; ⋬; ⊴◌̸; ) NOT NORMAL SUBGROUP OF OR EQUAL TO
+22ED;22ED;22B5 0338;22ED;22B5 0338; # (⋭; ⋭; ⊵◌̸; ⋭; ⊵◌̸; ) DOES NOT CONTAIN AS NORMAL SUBGROUP OR EQUAL
+2329;3008;3008;3008;3008; # (〈; 〈; 〈; 〈; 〈; ) LEFT-POINTING ANGLE BRACKET
+232A;3009;3009;3009;3009; # (〉; 〉; 〉; 〉; 〉; ) RIGHT-POINTING ANGLE BRACKET
+2460;2460;2460;0031;0031; # (â‘ ; â‘ ; â‘ ; 1; 1; ) CIRCLED DIGIT ONE
+2461;2461;2461;0032;0032; # (â‘¡; â‘¡; â‘¡; 2; 2; ) CIRCLED DIGIT TWO
+2462;2462;2462;0033;0033; # (â‘¢; â‘¢; â‘¢; 3; 3; ) CIRCLED DIGIT THREE
+2463;2463;2463;0034;0034; # (â‘£; â‘£; â‘£; 4; 4; ) CIRCLED DIGIT FOUR
+2464;2464;2464;0035;0035; # (⑤; ⑤; ⑤; 5; 5; ) CIRCLED DIGIT FIVE
+2465;2465;2465;0036;0036; # (â‘¥; â‘¥; â‘¥; 6; 6; ) CIRCLED DIGIT SIX
+2466;2466;2466;0037;0037; # (⑦; ⑦; ⑦; 7; 7; ) CIRCLED DIGIT SEVEN
+2467;2467;2467;0038;0038; # (â‘§; â‘§; â‘§; 8; 8; ) CIRCLED DIGIT EIGHT
+2468;2468;2468;0039;0039; # (⑨; ⑨; ⑨; 9; 9; ) CIRCLED DIGIT NINE
+2469;2469;2469;0031 0030;0031 0030; # (â‘©; â‘©; â‘©; 10; 10; ) CIRCLED NUMBER TEN
+246A;246A;246A;0031 0031;0031 0031; # (⑪; ⑪; ⑪; 11; 11; ) CIRCLED NUMBER ELEVEN
+246B;246B;246B;0031 0032;0031 0032; # (â‘«; â‘«; â‘«; 12; 12; ) CIRCLED NUMBER TWELVE
+246C;246C;246C;0031 0033;0031 0033; # (⑬; ⑬; ⑬; 13; 13; ) CIRCLED NUMBER THIRTEEN
+246D;246D;246D;0031 0034;0031 0034; # (â‘­; â‘­; â‘­; 14; 14; ) CIRCLED NUMBER FOURTEEN
+246E;246E;246E;0031 0035;0031 0035; # (â‘®; â‘®; â‘®; 15; 15; ) CIRCLED NUMBER FIFTEEN
+246F;246F;246F;0031 0036;0031 0036; # (⑯; ⑯; ⑯; 16; 16; ) CIRCLED NUMBER SIXTEEN
+2470;2470;2470;0031 0037;0031 0037; # (â‘°; â‘°; â‘°; 17; 17; ) CIRCLED NUMBER SEVENTEEN
+2471;2471;2471;0031 0038;0031 0038; # (⑱; ⑱; ⑱; 18; 18; ) CIRCLED NUMBER EIGHTEEN
+2472;2472;2472;0031 0039;0031 0039; # (⑲; ⑲; ⑲; 19; 19; ) CIRCLED NUMBER NINETEEN
+2473;2473;2473;0032 0030;0032 0030; # (⑳; ⑳; ⑳; 20; 20; ) CIRCLED NUMBER TWENTY
+2474;2474;2474;0028 0031 0029;0028 0031 0029; # (â‘´; â‘´; â‘´; (1); (1); ) PARENTHESIZED DIGIT ONE
+2475;2475;2475;0028 0032 0029;0028 0032 0029; # (⑵; ⑵; ⑵; (2); (2); ) PARENTHESIZED DIGIT TWO
+2476;2476;2476;0028 0033 0029;0028 0033 0029; # (â‘¶; â‘¶; â‘¶; (3); (3); ) PARENTHESIZED DIGIT THREE
+2477;2477;2477;0028 0034 0029;0028 0034 0029; # (â‘·; â‘·; â‘·; (4); (4); ) PARENTHESIZED DIGIT FOUR
+2478;2478;2478;0028 0035 0029;0028 0035 0029; # (⑸; ⑸; ⑸; (5); (5); ) PARENTHESIZED DIGIT FIVE
+2479;2479;2479;0028 0036 0029;0028 0036 0029; # (⑹; ⑹; ⑹; (6); (6); ) PARENTHESIZED DIGIT SIX
+247A;247A;247A;0028 0037 0029;0028 0037 0029; # (⑺; ⑺; ⑺; (7); (7); ) PARENTHESIZED DIGIT SEVEN
+247B;247B;247B;0028 0038 0029;0028 0038 0029; # (â‘»; â‘»; â‘»; (8); (8); ) PARENTHESIZED DIGIT EIGHT
+247C;247C;247C;0028 0039 0029;0028 0039 0029; # (⑼; ⑼; ⑼; (9); (9); ) PARENTHESIZED DIGIT NINE
+247D;247D;247D;0028 0031 0030 0029;0028 0031 0030 0029; # (⑽; ⑽; ⑽; (10); (10); ) PARENTHESIZED NUMBER TEN
+247E;247E;247E;0028 0031 0031 0029;0028 0031 0031 0029; # (⑾; ⑾; ⑾; (11); (11); ) PARENTHESIZED NUMBER ELEVEN
+247F;247F;247F;0028 0031 0032 0029;0028 0031 0032 0029; # (â‘¿; â‘¿; â‘¿; (12); (12); ) PARENTHESIZED NUMBER TWELVE
+2480;2480;2480;0028 0031 0033 0029;0028 0031 0033 0029; # (â’€; â’€; â’€; (13); (13); ) PARENTHESIZED NUMBER THIRTEEN
+2481;2481;2481;0028 0031 0034 0029;0028 0031 0034 0029; # (â’; â’; â’; (14); (14); ) PARENTHESIZED NUMBER FOURTEEN
+2482;2482;2482;0028 0031 0035 0029;0028 0031 0035 0029; # (â’‚; â’‚; â’‚; (15); (15); ) PARENTHESIZED NUMBER FIFTEEN
+2483;2483;2483;0028 0031 0036 0029;0028 0031 0036 0029; # (â’ƒ; â’ƒ; â’ƒ; (16); (16); ) PARENTHESIZED NUMBER SIXTEEN
+2484;2484;2484;0028 0031 0037 0029;0028 0031 0037 0029; # (â’„; â’„; â’„; (17); (17); ) PARENTHESIZED NUMBER SEVENTEEN
+2485;2485;2485;0028 0031 0038 0029;0028 0031 0038 0029; # (â’…; â’…; â’…; (18); (18); ) PARENTHESIZED NUMBER EIGHTEEN
+2486;2486;2486;0028 0031 0039 0029;0028 0031 0039 0029; # (â’†; â’†; â’†; (19); (19); ) PARENTHESIZED NUMBER NINETEEN
+2487;2487;2487;0028 0032 0030 0029;0028 0032 0030 0029; # (â’‡; â’‡; â’‡; (20); (20); ) PARENTHESIZED NUMBER TWENTY
+2488;2488;2488;0031 002E;0031 002E; # (â’ˆ; â’ˆ; â’ˆ; 1.; 1.; ) DIGIT ONE FULL STOP
+2489;2489;2489;0032 002E;0032 002E; # (â’‰; â’‰; â’‰; 2.; 2.; ) DIGIT TWO FULL STOP
+248A;248A;248A;0033 002E;0033 002E; # (â’Š; â’Š; â’Š; 3.; 3.; ) DIGIT THREE FULL STOP
+248B;248B;248B;0034 002E;0034 002E; # (â’‹; â’‹; â’‹; 4.; 4.; ) DIGIT FOUR FULL STOP
+248C;248C;248C;0035 002E;0035 002E; # (⒌; ⒌; ⒌; 5.; 5.; ) DIGIT FIVE FULL STOP
+248D;248D;248D;0036 002E;0036 002E; # (â’; â’; â’; 6.; 6.; ) DIGIT SIX FULL STOP
+248E;248E;248E;0037 002E;0037 002E; # (â’Ž; â’Ž; â’Ž; 7.; 7.; ) DIGIT SEVEN FULL STOP
+248F;248F;248F;0038 002E;0038 002E; # (â’; â’; â’; 8.; 8.; ) DIGIT EIGHT FULL STOP
+2490;2490;2490;0039 002E;0039 002E; # (â’; â’; â’; 9.; 9.; ) DIGIT NINE FULL STOP
+2491;2491;2491;0031 0030 002E;0031 0030 002E; # (â’‘; â’‘; â’‘; 10.; 10.; ) NUMBER TEN FULL STOP
+2492;2492;2492;0031 0031 002E;0031 0031 002E; # (â’’; â’’; â’’; 11.; 11.; ) NUMBER ELEVEN FULL STOP
+2493;2493;2493;0031 0032 002E;0031 0032 002E; # (â’“; â’“; â’“; 12.; 12.; ) NUMBER TWELVE FULL STOP
+2494;2494;2494;0031 0033 002E;0031 0033 002E; # (â’”; â’”; â’”; 13.; 13.; ) NUMBER THIRTEEN FULL STOP
+2495;2495;2495;0031 0034 002E;0031 0034 002E; # (â’•; â’•; â’•; 14.; 14.; ) NUMBER FOURTEEN FULL STOP
+2496;2496;2496;0031 0035 002E;0031 0035 002E; # (â’–; â’–; â’–; 15.; 15.; ) NUMBER FIFTEEN FULL STOP
+2497;2497;2497;0031 0036 002E;0031 0036 002E; # (â’—; â’—; â’—; 16.; 16.; ) NUMBER SIXTEEN FULL STOP
+2498;2498;2498;0031 0037 002E;0031 0037 002E; # (â’˜; â’˜; â’˜; 17.; 17.; ) NUMBER SEVENTEEN FULL STOP
+2499;2499;2499;0031 0038 002E;0031 0038 002E; # (â’™; â’™; â’™; 18.; 18.; ) NUMBER EIGHTEEN FULL STOP
+249A;249A;249A;0031 0039 002E;0031 0039 002E; # (â’š; â’š; â’š; 19.; 19.; ) NUMBER NINETEEN FULL STOP
+249B;249B;249B;0032 0030 002E;0032 0030 002E; # (â’›; â’›; â’›; 20.; 20.; ) NUMBER TWENTY FULL STOP
+249C;249C;249C;0028 0061 0029;0028 0061 0029; # (⒜; ⒜; ⒜; (a); (a); ) PARENTHESIZED LATIN SMALL LETTER A
+249D;249D;249D;0028 0062 0029;0028 0062 0029; # (â’; â’; â’; (b); (b); ) PARENTHESIZED LATIN SMALL LETTER B
+249E;249E;249E;0028 0063 0029;0028 0063 0029; # (â’ž; â’ž; â’ž; (c); (c); ) PARENTHESIZED LATIN SMALL LETTER C
+249F;249F;249F;0028 0064 0029;0028 0064 0029; # (â’Ÿ; â’Ÿ; â’Ÿ; (d); (d); ) PARENTHESIZED LATIN SMALL LETTER D
+24A0;24A0;24A0;0028 0065 0029;0028 0065 0029; # (â’ ; â’ ; â’ ; (e); (e); ) PARENTHESIZED LATIN SMALL LETTER E
+24A1;24A1;24A1;0028 0066 0029;0028 0066 0029; # (â’¡; â’¡; â’¡; (f); (f); ) PARENTHESIZED LATIN SMALL LETTER F
+24A2;24A2;24A2;0028 0067 0029;0028 0067 0029; # (â’¢; â’¢; â’¢; (g); (g); ) PARENTHESIZED LATIN SMALL LETTER G
+24A3;24A3;24A3;0028 0068 0029;0028 0068 0029; # (â’£; â’£; â’£; (h); (h); ) PARENTHESIZED LATIN SMALL LETTER H
+24A4;24A4;24A4;0028 0069 0029;0028 0069 0029; # (â’¤; â’¤; â’¤; (i); (i); ) PARENTHESIZED LATIN SMALL LETTER I
+24A5;24A5;24A5;0028 006A 0029;0028 006A 0029; # (â’¥; â’¥; â’¥; (j); (j); ) PARENTHESIZED LATIN SMALL LETTER J
+24A6;24A6;24A6;0028 006B 0029;0028 006B 0029; # (â’¦; â’¦; â’¦; (k); (k); ) PARENTHESIZED LATIN SMALL LETTER K
+24A7;24A7;24A7;0028 006C 0029;0028 006C 0029; # (â’§; â’§; â’§; (l); (l); ) PARENTHESIZED LATIN SMALL LETTER L
+24A8;24A8;24A8;0028 006D 0029;0028 006D 0029; # (â’¨; â’¨; â’¨; (m); (m); ) PARENTHESIZED LATIN SMALL LETTER M
+24A9;24A9;24A9;0028 006E 0029;0028 006E 0029; # (â’©; â’©; â’©; (n); (n); ) PARENTHESIZED LATIN SMALL LETTER N
+24AA;24AA;24AA;0028 006F 0029;0028 006F 0029; # (â’ª; â’ª; â’ª; (o); (o); ) PARENTHESIZED LATIN SMALL LETTER O
+24AB;24AB;24AB;0028 0070 0029;0028 0070 0029; # (â’«; â’«; â’«; (p); (p); ) PARENTHESIZED LATIN SMALL LETTER P
+24AC;24AC;24AC;0028 0071 0029;0028 0071 0029; # (â’¬; â’¬; â’¬; (q); (q); ) PARENTHESIZED LATIN SMALL LETTER Q
+24AD;24AD;24AD;0028 0072 0029;0028 0072 0029; # (â’­; â’­; â’­; (r); (r); ) PARENTHESIZED LATIN SMALL LETTER R
+24AE;24AE;24AE;0028 0073 0029;0028 0073 0029; # (â’®; â’®; â’®; (s); (s); ) PARENTHESIZED LATIN SMALL LETTER S
+24AF;24AF;24AF;0028 0074 0029;0028 0074 0029; # (â’¯; â’¯; â’¯; (t); (t); ) PARENTHESIZED LATIN SMALL LETTER T
+24B0;24B0;24B0;0028 0075 0029;0028 0075 0029; # (â’°; â’°; â’°; (u); (u); ) PARENTHESIZED LATIN SMALL LETTER U
+24B1;24B1;24B1;0028 0076 0029;0028 0076 0029; # (â’±; â’±; â’±; (v); (v); ) PARENTHESIZED LATIN SMALL LETTER V
+24B2;24B2;24B2;0028 0077 0029;0028 0077 0029; # (â’²; â’²; â’²; (w); (w); ) PARENTHESIZED LATIN SMALL LETTER W
+24B3;24B3;24B3;0028 0078 0029;0028 0078 0029; # (â’³; â’³; â’³; (x); (x); ) PARENTHESIZED LATIN SMALL LETTER X
+24B4;24B4;24B4;0028 0079 0029;0028 0079 0029; # (â’´; â’´; â’´; (y); (y); ) PARENTHESIZED LATIN SMALL LETTER Y
+24B5;24B5;24B5;0028 007A 0029;0028 007A 0029; # (â’µ; â’µ; â’µ; (z); (z); ) PARENTHESIZED LATIN SMALL LETTER Z
+24B6;24B6;24B6;0041;0041; # (â’¶; â’¶; â’¶; A; A; ) CIRCLED LATIN CAPITAL LETTER A
+24B7;24B7;24B7;0042;0042; # (â’·; â’·; â’·; B; B; ) CIRCLED LATIN CAPITAL LETTER B
+24B8;24B8;24B8;0043;0043; # (â’¸; â’¸; â’¸; C; C; ) CIRCLED LATIN CAPITAL LETTER C
+24B9;24B9;24B9;0044;0044; # (â’¹; â’¹; â’¹; D; D; ) CIRCLED LATIN CAPITAL LETTER D
+24BA;24BA;24BA;0045;0045; # (â’º; â’º; â’º; E; E; ) CIRCLED LATIN CAPITAL LETTER E
+24BB;24BB;24BB;0046;0046; # (â’»; â’»; â’»; F; F; ) CIRCLED LATIN CAPITAL LETTER F
+24BC;24BC;24BC;0047;0047; # (â’¼; â’¼; â’¼; G; G; ) CIRCLED LATIN CAPITAL LETTER G
+24BD;24BD;24BD;0048;0048; # (â’½; â’½; â’½; H; H; ) CIRCLED LATIN CAPITAL LETTER H
+24BE;24BE;24BE;0049;0049; # (â’¾; â’¾; â’¾; I; I; ) CIRCLED LATIN CAPITAL LETTER I
+24BF;24BF;24BF;004A;004A; # (â’¿; â’¿; â’¿; J; J; ) CIRCLED LATIN CAPITAL LETTER J
+24C0;24C0;24C0;004B;004B; # (â“€; â“€; â“€; K; K; ) CIRCLED LATIN CAPITAL LETTER K
+24C1;24C1;24C1;004C;004C; # (â“; â“; â“; L; L; ) CIRCLED LATIN CAPITAL LETTER L
+24C2;24C2;24C2;004D;004D; # (â“‚; â“‚; â“‚; M; M; ) CIRCLED LATIN CAPITAL LETTER M
+24C3;24C3;24C3;004E;004E; # (Ⓝ; Ⓝ; Ⓝ; N; N; ) CIRCLED LATIN CAPITAL LETTER N
+24C4;24C4;24C4;004F;004F; # (â“„; â“„; â“„; O; O; ) CIRCLED LATIN CAPITAL LETTER O
+24C5;24C5;24C5;0050;0050; # (â“…; â“…; â“…; P; P; ) CIRCLED LATIN CAPITAL LETTER P
+24C6;24C6;24C6;0051;0051; # (Ⓠ; Ⓠ; Ⓠ; Q; Q; ) CIRCLED LATIN CAPITAL LETTER Q
+24C7;24C7;24C7;0052;0052; # (Ⓡ; Ⓡ; Ⓡ; R; R; ) CIRCLED LATIN CAPITAL LETTER R
+24C8;24C8;24C8;0053;0053; # (Ⓢ; Ⓢ; Ⓢ; S; S; ) CIRCLED LATIN CAPITAL LETTER S
+24C9;24C9;24C9;0054;0054; # (Ⓣ; Ⓣ; Ⓣ; T; T; ) CIRCLED LATIN CAPITAL LETTER T
+24CA;24CA;24CA;0055;0055; # (Ⓤ; Ⓤ; Ⓤ; U; U; ) CIRCLED LATIN CAPITAL LETTER U
+24CB;24CB;24CB;0056;0056; # (â“‹; â“‹; â“‹; V; V; ) CIRCLED LATIN CAPITAL LETTER V
+24CC;24CC;24CC;0057;0057; # (Ⓦ; Ⓦ; Ⓦ; W; W; ) CIRCLED LATIN CAPITAL LETTER W
+24CD;24CD;24CD;0058;0058; # (â“; â“; â“; X; X; ) CIRCLED LATIN CAPITAL LETTER X
+24CE;24CE;24CE;0059;0059; # (Ⓨ; Ⓨ; Ⓨ; Y; Y; ) CIRCLED LATIN CAPITAL LETTER Y
+24CF;24CF;24CF;005A;005A; # (â“; â“; â“; Z; Z; ) CIRCLED LATIN CAPITAL LETTER Z
+24D0;24D0;24D0;0061;0061; # (â“; â“; â“; a; a; ) CIRCLED LATIN SMALL LETTER A
+24D1;24D1;24D1;0062;0062; # (â“‘; â“‘; â“‘; b; b; ) CIRCLED LATIN SMALL LETTER B
+24D2;24D2;24D2;0063;0063; # (â“’; â“’; â“’; c; c; ) CIRCLED LATIN SMALL LETTER C
+24D3;24D3;24D3;0064;0064; # (â““; â““; â““; d; d; ) CIRCLED LATIN SMALL LETTER D
+24D4;24D4;24D4;0065;0065; # (â“”; â“”; â“”; e; e; ) CIRCLED LATIN SMALL LETTER E
+24D5;24D5;24D5;0066;0066; # (â“•; â“•; â“•; f; f; ) CIRCLED LATIN SMALL LETTER F
+24D6;24D6;24D6;0067;0067; # (â“–; â“–; â“–; g; g; ) CIRCLED LATIN SMALL LETTER G
+24D7;24D7;24D7;0068;0068; # (â“—; â“—; â“—; h; h; ) CIRCLED LATIN SMALL LETTER H
+24D8;24D8;24D8;0069;0069; # (ⓘ; ⓘ; ⓘ; i; i; ) CIRCLED LATIN SMALL LETTER I
+24D9;24D9;24D9;006A;006A; # (â“™; â“™; â“™; j; j; ) CIRCLED LATIN SMALL LETTER J
+24DA;24DA;24DA;006B;006B; # (ⓚ; ⓚ; ⓚ; k; k; ) CIRCLED LATIN SMALL LETTER K
+24DB;24DB;24DB;006C;006C; # (â“›; â“›; â“›; l; l; ) CIRCLED LATIN SMALL LETTER L
+24DC;24DC;24DC;006D;006D; # (ⓜ; ⓜ; ⓜ; m; m; ) CIRCLED LATIN SMALL LETTER M
+24DD;24DD;24DD;006E;006E; # (â“; â“; â“; n; n; ) CIRCLED LATIN SMALL LETTER N
+24DE;24DE;24DE;006F;006F; # (ⓞ; ⓞ; ⓞ; o; o; ) CIRCLED LATIN SMALL LETTER O
+24DF;24DF;24DF;0070;0070; # (ⓟ; ⓟ; ⓟ; p; p; ) CIRCLED LATIN SMALL LETTER P
+24E0;24E0;24E0;0071;0071; # (â“ ; â“ ; â“ ; q; q; ) CIRCLED LATIN SMALL LETTER Q
+24E1;24E1;24E1;0072;0072; # (â“¡; â“¡; â“¡; r; r; ) CIRCLED LATIN SMALL LETTER R
+24E2;24E2;24E2;0073;0073; # (â“¢; â“¢; â“¢; s; s; ) CIRCLED LATIN SMALL LETTER S
+24E3;24E3;24E3;0074;0074; # (â“£; â“£; â“£; t; t; ) CIRCLED LATIN SMALL LETTER T
+24E4;24E4;24E4;0075;0075; # (ⓤ; ⓤ; ⓤ; u; u; ) CIRCLED LATIN SMALL LETTER U
+24E5;24E5;24E5;0076;0076; # (â“¥; â“¥; â“¥; v; v; ) CIRCLED LATIN SMALL LETTER V
+24E6;24E6;24E6;0077;0077; # (ⓦ; ⓦ; ⓦ; w; w; ) CIRCLED LATIN SMALL LETTER W
+24E7;24E7;24E7;0078;0078; # (â“§; â“§; â“§; x; x; ) CIRCLED LATIN SMALL LETTER X
+24E8;24E8;24E8;0079;0079; # (ⓨ; ⓨ; ⓨ; y; y; ) CIRCLED LATIN SMALL LETTER Y
+24E9;24E9;24E9;007A;007A; # (â“©; â“©; â“©; z; z; ) CIRCLED LATIN SMALL LETTER Z
+24EA;24EA;24EA;0030;0030; # (⓪; ⓪; ⓪; 0; 0; ) CIRCLED DIGIT ZERO
+2A0C;2A0C;2A0C;222B 222B 222B 222B;222B 222B 222B 222B; # (⨌; ⨌; ⨌; ∫∫∫∫; ∫∫∫∫; ) QUADRUPLE INTEGRAL OPERATOR
+2A74;2A74;2A74;003A 003A 003D;003A 003A 003D; # (â©´; â©´; â©´; ::=; ::=; ) DOUBLE COLON EQUAL
+2A75;2A75;2A75;003D 003D;003D 003D; # (⩵; ⩵; ⩵; ==; ==; ) TWO CONSECUTIVE EQUALS SIGNS
+2A76;2A76;2A76;003D 003D 003D;003D 003D 003D; # (â©¶; â©¶; â©¶; ===; ===; ) THREE CONSECUTIVE EQUALS SIGNS
+2ADC;2ADD 0338;2ADD 0338;2ADD 0338;2ADD 0338; # (⫝̸; â«â—ŒÌ¸; â«â—ŒÌ¸; â«â—ŒÌ¸; â«â—ŒÌ¸; ) FORKING
+2E9F;2E9F;2E9F;6BCD;6BCD; # (⺟; ⺟; ⺟; æ¯; æ¯; ) CJK RADICAL MOTHER
+2EF3;2EF3;2EF3;9F9F;9F9F; # (⻳; ⻳; ⻳; 龟; 龟; ) CJK RADICAL C-SIMPLIFIED TURTLE
+2F00;2F00;2F00;4E00;4E00; # (⼀; ⼀; ⼀; 一; 一; ) KANGXI RADICAL ONE
+2F01;2F01;2F01;4E28;4E28; # (â¼; â¼; â¼; 丨; 丨; ) KANGXI RADICAL LINE
+2F02;2F02;2F02;4E36;4E36; # (⼂; ⼂; ⼂; 丶; 丶; ) KANGXI RADICAL DOT
+2F03;2F03;2F03;4E3F;4E3F; # (⼃; ⼃; ⼃; 丿; 丿; ) KANGXI RADICAL SLASH
+2F04;2F04;2F04;4E59;4E59; # (⼄; ⼄; ⼄; 乙; 乙; ) KANGXI RADICAL SECOND
+2F05;2F05;2F05;4E85;4E85; # (⼅; ⼅; ⼅; 亅; 亅; ) KANGXI RADICAL HOOK
+2F06;2F06;2F06;4E8C;4E8C; # (⼆; ⼆; ⼆; 二; 二; ) KANGXI RADICAL TWO
+2F07;2F07;2F07;4EA0;4EA0; # (⼇; ⼇; ⼇; 亠; 亠; ) KANGXI RADICAL LID
+2F08;2F08;2F08;4EBA;4EBA; # (⼈; ⼈; ⼈; 人; 人; ) KANGXI RADICAL MAN
+2F09;2F09;2F09;513F;513F; # (⼉; ⼉; ⼉; 儿; 儿; ) KANGXI RADICAL LEGS
+2F0A;2F0A;2F0A;5165;5165; # (⼊; ⼊; ⼊; 入; 入; ) KANGXI RADICAL ENTER
+2F0B;2F0B;2F0B;516B;516B; # (⼋; ⼋; ⼋; 八; 八; ) KANGXI RADICAL EIGHT
+2F0C;2F0C;2F0C;5182;5182; # (⼌; ⼌; ⼌; 冂; 冂; ) KANGXI RADICAL DOWN BOX
+2F0D;2F0D;2F0D;5196;5196; # (â¼; â¼; â¼; 冖; 冖; ) KANGXI RADICAL COVER
+2F0E;2F0E;2F0E;51AB;51AB; # (⼎; ⼎; ⼎; 冫; 冫; ) KANGXI RADICAL ICE
+2F0F;2F0F;2F0F;51E0;51E0; # (â¼; â¼; â¼; 几; 几; ) KANGXI RADICAL TABLE
+2F10;2F10;2F10;51F5;51F5; # (â¼; â¼; â¼; 凵; 凵; ) KANGXI RADICAL OPEN BOX
+2F11;2F11;2F11;5200;5200; # (⼑; ⼑; ⼑; 刀; 刀; ) KANGXI RADICAL KNIFE
+2F12;2F12;2F12;529B;529B; # (⼒; ⼒; ⼒; 力; 力; ) KANGXI RADICAL POWER
+2F13;2F13;2F13;52F9;52F9; # (⼓; ⼓; ⼓; 勹; 勹; ) KANGXI RADICAL WRAP
+2F14;2F14;2F14;5315;5315; # (⼔; ⼔; ⼔; 匕; 匕; ) KANGXI RADICAL SPOON
+2F15;2F15;2F15;531A;531A; # (⼕; ⼕; ⼕; 匚; 匚; ) KANGXI RADICAL RIGHT OPEN BOX
+2F16;2F16;2F16;5338;5338; # (⼖; ⼖; ⼖; 匸; 匸; ) KANGXI RADICAL HIDING ENCLOSURE
+2F17;2F17;2F17;5341;5341; # (â¼—; â¼—; â¼—; å; å; ) KANGXI RADICAL TEN
+2F18;2F18;2F18;535C;535C; # (⼘; ⼘; ⼘; åœ; åœ; ) KANGXI RADICAL DIVINATION
+2F19;2F19;2F19;5369;5369; # (â¼™; â¼™; â¼™; å©; å©; ) KANGXI RADICAL SEAL
+2F1A;2F1A;2F1A;5382;5382; # (⼚; ⼚; ⼚; 厂; 厂; ) KANGXI RADICAL CLIFF
+2F1B;2F1B;2F1B;53B6;53B6; # (⼛; ⼛; ⼛; 厶; 厶; ) KANGXI RADICAL PRIVATE
+2F1C;2F1C;2F1C;53C8;53C8; # (⼜; ⼜; ⼜; åˆ; åˆ; ) KANGXI RADICAL AGAIN
+2F1D;2F1D;2F1D;53E3;53E3; # (â¼; â¼; â¼; å£; å£; ) KANGXI RADICAL MOUTH
+2F1E;2F1E;2F1E;56D7;56D7; # (⼞; ⼞; ⼞; 囗; 囗; ) KANGXI RADICAL ENCLOSURE
+2F1F;2F1F;2F1F;571F;571F; # (⼟; ⼟; ⼟; 土; 土; ) KANGXI RADICAL EARTH
+2F20;2F20;2F20;58EB;58EB; # (⼠; ⼠; ⼠; 士; 士; ) KANGXI RADICAL SCHOLAR
+2F21;2F21;2F21;5902;5902; # (⼡; ⼡; ⼡; 夂; 夂; ) KANGXI RADICAL GO
+2F22;2F22;2F22;590A;590A; # (⼢; ⼢; ⼢; 夊; 夊; ) KANGXI RADICAL GO SLOWLY
+2F23;2F23;2F23;5915;5915; # (⼣; ⼣; ⼣; 夕; 夕; ) KANGXI RADICAL EVENING
+2F24;2F24;2F24;5927;5927; # (⼤; ⼤; ⼤; 大; 大; ) KANGXI RADICAL BIG
+2F25;2F25;2F25;5973;5973; # (⼥; ⼥; ⼥; 女; 女; ) KANGXI RADICAL WOMAN
+2F26;2F26;2F26;5B50;5B50; # (⼦; ⼦; ⼦; å­; å­; ) KANGXI RADICAL CHILD
+2F27;2F27;2F27;5B80;5B80; # (⼧; ⼧; ⼧; 宀; 宀; ) KANGXI RADICAL ROOF
+2F28;2F28;2F28;5BF8;5BF8; # (⼨; ⼨; ⼨; 寸; 寸; ) KANGXI RADICAL INCH
+2F29;2F29;2F29;5C0F;5C0F; # (⼩; ⼩; ⼩; å°; å°; ) KANGXI RADICAL SMALL
+2F2A;2F2A;2F2A;5C22;5C22; # (⼪; ⼪; ⼪; 尢; 尢; ) KANGXI RADICAL LAME
+2F2B;2F2B;2F2B;5C38;5C38; # (⼫; ⼫; ⼫; 尸; 尸; ) KANGXI RADICAL CORPSE
+2F2C;2F2C;2F2C;5C6E;5C6E; # (⼬; ⼬; ⼬; 屮; 屮; ) KANGXI RADICAL SPROUT
+2F2D;2F2D;2F2D;5C71;5C71; # (â¼­; â¼­; â¼­; å±±; å±±; ) KANGXI RADICAL MOUNTAIN
+2F2E;2F2E;2F2E;5DDB;5DDB; # (â¼®; â¼®; â¼®; å·›; å·›; ) KANGXI RADICAL RIVER
+2F2F;2F2F;2F2F;5DE5;5DE5; # (⼯; ⼯; ⼯; 工; 工; ) KANGXI RADICAL WORK
+2F30;2F30;2F30;5DF1;5DF1; # (â¼°; â¼°; â¼°; å·±; å·±; ) KANGXI RADICAL ONESELF
+2F31;2F31;2F31;5DFE;5DFE; # (â¼±; â¼±; â¼±; å·¾; å·¾; ) KANGXI RADICAL TURBAN
+2F32;2F32;2F32;5E72;5E72; # (â¼²; â¼²; â¼²; å¹²; å¹²; ) KANGXI RADICAL DRY
+2F33;2F33;2F33;5E7A;5E7A; # (⼳; ⼳; ⼳; 幺; 幺; ) KANGXI RADICAL SHORT THREAD
+2F34;2F34;2F34;5E7F;5E7F; # (⼴; ⼴; ⼴; 广; 广; ) KANGXI RADICAL DOTTED CLIFF
+2F35;2F35;2F35;5EF4;5EF4; # (â¼µ; â¼µ; â¼µ; å»´; å»´; ) KANGXI RADICAL LONG STRIDE
+2F36;2F36;2F36;5EFE;5EFE; # (⼶; ⼶; ⼶; 廾; 廾; ) KANGXI RADICAL TWO HANDS
+2F37;2F37;2F37;5F0B;5F0B; # (⼷; ⼷; ⼷; 弋; 弋; ) KANGXI RADICAL SHOOT
+2F38;2F38;2F38;5F13;5F13; # (⼸; ⼸; ⼸; 弓; 弓; ) KANGXI RADICAL BOW
+2F39;2F39;2F39;5F50;5F50; # (â¼¹; â¼¹; â¼¹; å½; å½; ) KANGXI RADICAL SNOUT
+2F3A;2F3A;2F3A;5F61;5F61; # (⼺; ⼺; ⼺; 彡; 彡; ) KANGXI RADICAL BRISTLE
+2F3B;2F3B;2F3B;5F73;5F73; # (â¼»; â¼»; â¼»; å½³; å½³; ) KANGXI RADICAL STEP
+2F3C;2F3C;2F3C;5FC3;5FC3; # (⼼; ⼼; ⼼; 心; 心; ) KANGXI RADICAL HEART
+2F3D;2F3D;2F3D;6208;6208; # (⼽; ⼽; ⼽; 戈; 戈; ) KANGXI RADICAL HALBERD
+2F3E;2F3E;2F3E;6236;6236; # (⼾; ⼾; ⼾; 戶; 戶; ) KANGXI RADICAL DOOR
+2F3F;2F3F;2F3F;624B;624B; # (⼿; ⼿; ⼿; 手; 手; ) KANGXI RADICAL HAND
+2F40;2F40;2F40;652F;652F; # (⽀; ⽀; ⽀; 支; 支; ) KANGXI RADICAL BRANCH
+2F41;2F41;2F41;6534;6534; # (â½; â½; â½; æ”´; æ”´; ) KANGXI RADICAL RAP
+2F42;2F42;2F42;6587;6587; # (⽂; ⽂; ⽂; 文; 文; ) KANGXI RADICAL SCRIPT
+2F43;2F43;2F43;6597;6597; # (⽃; ⽃; ⽃; 斗; 斗; ) KANGXI RADICAL DIPPER
+2F44;2F44;2F44;65A4;65A4; # (⽄; ⽄; ⽄; 斤; 斤; ) KANGXI RADICAL AXE
+2F45;2F45;2F45;65B9;65B9; # (â½…; â½…; â½…; æ–¹; æ–¹; ) KANGXI RADICAL SQUARE
+2F46;2F46;2F46;65E0;65E0; # (⽆; ⽆; ⽆; 无; 无; ) KANGXI RADICAL NOT
+2F47;2F47;2F47;65E5;65E5; # (⽇; ⽇; ⽇; 日; 日; ) KANGXI RADICAL SUN
+2F48;2F48;2F48;66F0;66F0; # (⽈; ⽈; ⽈; 曰; 曰; ) KANGXI RADICAL SAY
+2F49;2F49;2F49;6708;6708; # (⽉; ⽉; ⽉; 月; 月; ) KANGXI RADICAL MOON
+2F4A;2F4A;2F4A;6728;6728; # (⽊; ⽊; ⽊; 木; 木; ) KANGXI RADICAL TREE
+2F4B;2F4B;2F4B;6B20;6B20; # (⽋; ⽋; ⽋; 欠; 欠; ) KANGXI RADICAL LACK
+2F4C;2F4C;2F4C;6B62;6B62; # (⽌; ⽌; ⽌; 止; 止; ) KANGXI RADICAL STOP
+2F4D;2F4D;2F4D;6B79;6B79; # (â½; â½; â½; æ­¹; æ­¹; ) KANGXI RADICAL DEATH
+2F4E;2F4E;2F4E;6BB3;6BB3; # (⽎; ⽎; ⽎; 殳; 殳; ) KANGXI RADICAL WEAPON
+2F4F;2F4F;2F4F;6BCB;6BCB; # (â½; â½; â½; 毋; 毋; ) KANGXI RADICAL DO NOT
+2F50;2F50;2F50;6BD4;6BD4; # (â½; â½; â½; 比; 比; ) KANGXI RADICAL COMPARE
+2F51;2F51;2F51;6BDB;6BDB; # (⽑; ⽑; ⽑; 毛; 毛; ) KANGXI RADICAL FUR
+2F52;2F52;2F52;6C0F;6C0F; # (â½’; â½’; â½’; æ°; æ°; ) KANGXI RADICAL CLAN
+2F53;2F53;2F53;6C14;6C14; # (⽓; ⽓; ⽓; 气; 气; ) KANGXI RADICAL STEAM
+2F54;2F54;2F54;6C34;6C34; # (â½”; â½”; â½”; æ°´; æ°´; ) KANGXI RADICAL WATER
+2F55;2F55;2F55;706B;706B; # (⽕; ⽕; ⽕; ç«; ç«; ) KANGXI RADICAL FIRE
+2F56;2F56;2F56;722A;722A; # (⽖; ⽖; ⽖; 爪; 爪; ) KANGXI RADICAL CLAW
+2F57;2F57;2F57;7236;7236; # (⽗; ⽗; ⽗; 父; 父; ) KANGXI RADICAL FATHER
+2F58;2F58;2F58;723B;723B; # (⽘; ⽘; ⽘; 爻; 爻; ) KANGXI RADICAL DOUBLE X
+2F59;2F59;2F59;723F;723F; # (⽙; ⽙; ⽙; 爿; 爿; ) KANGXI RADICAL HALF TREE TRUNK
+2F5A;2F5A;2F5A;7247;7247; # (⽚; ⽚; ⽚; 片; 片; ) KANGXI RADICAL SLICE
+2F5B;2F5B;2F5B;7259;7259; # (⽛; ⽛; ⽛; 牙; 牙; ) KANGXI RADICAL FANG
+2F5C;2F5C;2F5C;725B;725B; # (⽜; ⽜; ⽜; 牛; 牛; ) KANGXI RADICAL COW
+2F5D;2F5D;2F5D;72AC;72AC; # (â½; â½; â½; 犬; 犬; ) KANGXI RADICAL DOG
+2F5E;2F5E;2F5E;7384;7384; # (⽞; ⽞; ⽞; 玄; 玄; ) KANGXI RADICAL PROFOUND
+2F5F;2F5F;2F5F;7389;7389; # (⽟; ⽟; ⽟; 玉; 玉; ) KANGXI RADICAL JADE
+2F60;2F60;2F60;74DC;74DC; # (⽠; ⽠; ⽠; 瓜; 瓜; ) KANGXI RADICAL MELON
+2F61;2F61;2F61;74E6;74E6; # (⽡; ⽡; ⽡; 瓦; 瓦; ) KANGXI RADICAL TILE
+2F62;2F62;2F62;7518;7518; # (⽢; ⽢; ⽢; 甘; 甘; ) KANGXI RADICAL SWEET
+2F63;2F63;2F63;751F;751F; # (⽣; ⽣; ⽣; 生; 生; ) KANGXI RADICAL LIFE
+2F64;2F64;2F64;7528;7528; # (⽤; ⽤; ⽤; 用; 用; ) KANGXI RADICAL USE
+2F65;2F65;2F65;7530;7530; # (â½¥; â½¥; â½¥; ç”°; ç”°; ) KANGXI RADICAL FIELD
+2F66;2F66;2F66;758B;758B; # (⽦; ⽦; ⽦; 疋; 疋; ) KANGXI RADICAL BOLT OF CLOTH
+2F67;2F67;2F67;7592;7592; # (â½§; â½§; â½§; ç–’; ç–’; ) KANGXI RADICAL SICKNESS
+2F68;2F68;2F68;7676;7676; # (⽨; ⽨; ⽨; 癶; 癶; ) KANGXI RADICAL DOTTED TENT
+2F69;2F69;2F69;767D;767D; # (⽩; ⽩; ⽩; 白; 白; ) KANGXI RADICAL WHITE
+2F6A;2F6A;2F6A;76AE;76AE; # (⽪; ⽪; ⽪; 皮; 皮; ) KANGXI RADICAL SKIN
+2F6B;2F6B;2F6B;76BF;76BF; # (⽫; ⽫; ⽫; 皿; 皿; ) KANGXI RADICAL DISH
+2F6C;2F6C;2F6C;76EE;76EE; # (⽬; ⽬; ⽬; 目; 目; ) KANGXI RADICAL EYE
+2F6D;2F6D;2F6D;77DB;77DB; # (⽭; ⽭; ⽭; 矛; 矛; ) KANGXI RADICAL SPEAR
+2F6E;2F6E;2F6E;77E2;77E2; # (⽮; ⽮; ⽮; 矢; 矢; ) KANGXI RADICAL ARROW
+2F6F;2F6F;2F6F;77F3;77F3; # (⽯; ⽯; ⽯; 石; 石; ) KANGXI RADICAL STONE
+2F70;2F70;2F70;793A;793A; # (⽰; ⽰; ⽰; 示; 示; ) KANGXI RADICAL SPIRIT
+2F71;2F71;2F71;79B8;79B8; # (⽱; ⽱; ⽱; 禸; 禸; ) KANGXI RADICAL TRACK
+2F72;2F72;2F72;79BE;79BE; # (⽲; ⽲; ⽲; 禾; 禾; ) KANGXI RADICAL GRAIN
+2F73;2F73;2F73;7A74;7A74; # (â½³; â½³; â½³; ç©´; ç©´; ) KANGXI RADICAL CAVE
+2F74;2F74;2F74;7ACB;7ACB; # (â½´; â½´; â½´; ç«‹; ç«‹; ) KANGXI RADICAL STAND
+2F75;2F75;2F75;7AF9;7AF9; # (⽵; ⽵; ⽵; 竹; 竹; ) KANGXI RADICAL BAMBOO
+2F76;2F76;2F76;7C73;7C73; # (â½¶; â½¶; â½¶; ç±³; ç±³; ) KANGXI RADICAL RICE
+2F77;2F77;2F77;7CF8;7CF8; # (⽷; ⽷; ⽷; 糸; 糸; ) KANGXI RADICAL SILK
+2F78;2F78;2F78;7F36;7F36; # (⽸; ⽸; ⽸; 缶; 缶; ) KANGXI RADICAL JAR
+2F79;2F79;2F79;7F51;7F51; # (⽹; ⽹; ⽹; 网; 网; ) KANGXI RADICAL NET
+2F7A;2F7A;2F7A;7F8A;7F8A; # (⽺; ⽺; ⽺; 羊; 羊; ) KANGXI RADICAL SHEEP
+2F7B;2F7B;2F7B;7FBD;7FBD; # (â½»; â½»; â½»; ç¾½; ç¾½; ) KANGXI RADICAL FEATHER
+2F7C;2F7C;2F7C;8001;8001; # (â½¼; â½¼; â½¼; è€; è€; ) KANGXI RADICAL OLD
+2F7D;2F7D;2F7D;800C;800C; # (⽽; ⽽; ⽽; 而; 而; ) KANGXI RADICAL AND
+2F7E;2F7E;2F7E;8012;8012; # (⽾; ⽾; ⽾; 耒; 耒; ) KANGXI RADICAL PLOW
+2F7F;2F7F;2F7F;8033;8033; # (⽿; ⽿; ⽿; 耳; 耳; ) KANGXI RADICAL EAR
+2F80;2F80;2F80;807F;807F; # (â¾€; â¾€; â¾€; è¿; è¿; ) KANGXI RADICAL BRUSH
+2F81;2F81;2F81;8089;8089; # (â¾; â¾; â¾; 肉; 肉; ) KANGXI RADICAL MEAT
+2F82;2F82;2F82;81E3;81E3; # (⾂; ⾂; ⾂; 臣; 臣; ) KANGXI RADICAL MINISTER
+2F83;2F83;2F83;81EA;81EA; # (⾃; ⾃; ⾃; 自; 自; ) KANGXI RADICAL SELF
+2F84;2F84;2F84;81F3;81F3; # (⾄; ⾄; ⾄; 至; 至; ) KANGXI RADICAL ARRIVE
+2F85;2F85;2F85;81FC;81FC; # (⾅; ⾅; ⾅; 臼; 臼; ) KANGXI RADICAL MORTAR
+2F86;2F86;2F86;820C;820C; # (⾆; ⾆; ⾆; 舌; 舌; ) KANGXI RADICAL TONGUE
+2F87;2F87;2F87;821B;821B; # (⾇; ⾇; ⾇; 舛; 舛; ) KANGXI RADICAL OPPOSE
+2F88;2F88;2F88;821F;821F; # (⾈; ⾈; ⾈; 舟; 舟; ) KANGXI RADICAL BOAT
+2F89;2F89;2F89;826E;826E; # (⾉; ⾉; ⾉; 艮; 艮; ) KANGXI RADICAL STOPPING
+2F8A;2F8A;2F8A;8272;8272; # (⾊; ⾊; ⾊; 色; 色; ) KANGXI RADICAL COLOR
+2F8B;2F8B;2F8B;8278;8278; # (⾋; ⾋; ⾋; 艸; 艸; ) KANGXI RADICAL GRASS
+2F8C;2F8C;2F8C;864D;864D; # (⾌; ⾌; ⾌; è™; è™; ) KANGXI RADICAL TIGER
+2F8D;2F8D;2F8D;866B;866B; # (â¾; â¾; â¾; 虫; 虫; ) KANGXI RADICAL INSECT
+2F8E;2F8E;2F8E;8840;8840; # (⾎; ⾎; ⾎; 血; 血; ) KANGXI RADICAL BLOOD
+2F8F;2F8F;2F8F;884C;884C; # (â¾; â¾; â¾; 行; 行; ) KANGXI RADICAL WALK ENCLOSURE
+2F90;2F90;2F90;8863;8863; # (â¾; â¾; â¾; è¡£; è¡£; ) KANGXI RADICAL CLOTHES
+2F91;2F91;2F91;897E;897E; # (⾑; ⾑; ⾑; 襾; 襾; ) KANGXI RADICAL WEST
+2F92;2F92;2F92;898B;898B; # (⾒; ⾒; ⾒; 見; 見; ) KANGXI RADICAL SEE
+2F93;2F93;2F93;89D2;89D2; # (⾓; ⾓; ⾓; 角; 角; ) KANGXI RADICAL HORN
+2F94;2F94;2F94;8A00;8A00; # (⾔; ⾔; ⾔; 言; 言; ) KANGXI RADICAL SPEECH
+2F95;2F95;2F95;8C37;8C37; # (⾕; ⾕; ⾕; 谷; 谷; ) KANGXI RADICAL VALLEY
+2F96;2F96;2F96;8C46;8C46; # (⾖; ⾖; ⾖; 豆; 豆; ) KANGXI RADICAL BEAN
+2F97;2F97;2F97;8C55;8C55; # (⾗; ⾗; ⾗; 豕; 豕; ) KANGXI RADICAL PIG
+2F98;2F98;2F98;8C78;8C78; # (⾘; ⾘; ⾘; 豸; 豸; ) KANGXI RADICAL BADGER
+2F99;2F99;2F99;8C9D;8C9D; # (â¾™; â¾™; â¾™; è²; è²; ) KANGXI RADICAL SHELL
+2F9A;2F9A;2F9A;8D64;8D64; # (⾚; ⾚; ⾚; 赤; 赤; ) KANGXI RADICAL RED
+2F9B;2F9B;2F9B;8D70;8D70; # (â¾›; â¾›; â¾›; èµ°; èµ°; ) KANGXI RADICAL RUN
+2F9C;2F9C;2F9C;8DB3;8DB3; # (⾜; ⾜; ⾜; 足; 足; ) KANGXI RADICAL FOOT
+2F9D;2F9D;2F9D;8EAB;8EAB; # (â¾; â¾; â¾; 身; 身; ) KANGXI RADICAL BODY
+2F9E;2F9E;2F9E;8ECA;8ECA; # (⾞; ⾞; ⾞; 車; 車; ) KANGXI RADICAL CART
+2F9F;2F9F;2F9F;8F9B;8F9B; # (⾟; ⾟; ⾟; 辛; 辛; ) KANGXI RADICAL BITTER
+2FA0;2FA0;2FA0;8FB0;8FB0; # (â¾ ; â¾ ; â¾ ; è¾°; è¾°; ) KANGXI RADICAL MORNING
+2FA1;2FA1;2FA1;8FB5;8FB5; # (⾡; ⾡; ⾡; 辵; 辵; ) KANGXI RADICAL WALK
+2FA2;2FA2;2FA2;9091;9091; # (â¾¢; â¾¢; â¾¢; é‚‘; é‚‘; ) KANGXI RADICAL CITY
+2FA3;2FA3;2FA3;9149;9149; # (â¾£; â¾£; â¾£; é…‰; é…‰; ) KANGXI RADICAL WINE
+2FA4;2FA4;2FA4;91C6;91C6; # (⾤; ⾤; ⾤; 釆; 釆; ) KANGXI RADICAL DISTINGUISH
+2FA5;2FA5;2FA5;91CC;91CC; # (⾥; ⾥; ⾥; 里; 里; ) KANGXI RADICAL VILLAGE
+2FA6;2FA6;2FA6;91D1;91D1; # (⾦; ⾦; ⾦; 金; 金; ) KANGXI RADICAL GOLD
+2FA7;2FA7;2FA7;9577;9577; # (â¾§; â¾§; â¾§; é•·; é•·; ) KANGXI RADICAL LONG
+2FA8;2FA8;2FA8;9580;9580; # (⾨; ⾨; ⾨; 門; 門; ) KANGXI RADICAL GATE
+2FA9;2FA9;2FA9;961C;961C; # (⾩; ⾩; ⾩; 阜; 阜; ) KANGXI RADICAL MOUND
+2FAA;2FAA;2FAA;96B6;96B6; # (⾪; ⾪; ⾪; 隶; 隶; ) KANGXI RADICAL SLAVE
+2FAB;2FAB;2FAB;96B9;96B9; # (⾫; ⾫; ⾫; 隹; 隹; ) KANGXI RADICAL SHORT TAILED BIRD
+2FAC;2FAC;2FAC;96E8;96E8; # (⾬; ⾬; ⾬; 雨; 雨; ) KANGXI RADICAL RAIN
+2FAD;2FAD;2FAD;9751;9751; # (â¾­; â¾­; â¾­; é‘; é‘; ) KANGXI RADICAL BLUE
+2FAE;2FAE;2FAE;975E;975E; # (â¾®; â¾®; â¾®; éž; éž; ) KANGXI RADICAL WRONG
+2FAF;2FAF;2FAF;9762;9762; # (⾯; ⾯; ⾯; é¢; é¢; ) KANGXI RADICAL FACE
+2FB0;2FB0;2FB0;9769;9769; # (â¾°; â¾°; â¾°; é©; é©; ) KANGXI RADICAL LEATHER
+2FB1;2FB1;2FB1;97CB;97CB; # (⾱; ⾱; ⾱; 韋; 韋; ) KANGXI RADICAL TANNED LEATHER
+2FB2;2FB2;2FB2;97ED;97ED; # (⾲; ⾲; ⾲; 韭; 韭; ) KANGXI RADICAL LEEK
+2FB3;2FB3;2FB3;97F3;97F3; # (⾳; ⾳; ⾳; 音; 音; ) KANGXI RADICAL SOUND
+2FB4;2FB4;2FB4;9801;9801; # (â¾´; â¾´; â¾´; é ; é ; ) KANGXI RADICAL LEAF
+2FB5;2FB5;2FB5;98A8;98A8; # (⾵; ⾵; ⾵; 風; 風; ) KANGXI RADICAL WIND
+2FB6;2FB6;2FB6;98DB;98DB; # (⾶; ⾶; ⾶; 飛; 飛; ) KANGXI RADICAL FLY
+2FB7;2FB7;2FB7;98DF;98DF; # (⾷; ⾷; ⾷; 食; 食; ) KANGXI RADICAL EAT
+2FB8;2FB8;2FB8;9996;9996; # (⾸; ⾸; ⾸; 首; 首; ) KANGXI RADICAL HEAD
+2FB9;2FB9;2FB9;9999;9999; # (⾹; ⾹; ⾹; 香; 香; ) KANGXI RADICAL FRAGRANT
+2FBA;2FBA;2FBA;99AC;99AC; # (⾺; ⾺; ⾺; 馬; 馬; ) KANGXI RADICAL HORSE
+2FBB;2FBB;2FBB;9AA8;9AA8; # (⾻; ⾻; ⾻; 骨; 骨; ) KANGXI RADICAL BONE
+2FBC;2FBC;2FBC;9AD8;9AD8; # (⾼; ⾼; ⾼; 高; 高; ) KANGXI RADICAL TALL
+2FBD;2FBD;2FBD;9ADF;9ADF; # (⾽; ⾽; ⾽; 髟; 髟; ) KANGXI RADICAL HAIR
+2FBE;2FBE;2FBE;9B25;9B25; # (⾾; ⾾; ⾾; 鬥; 鬥; ) KANGXI RADICAL FIGHT
+2FBF;2FBF;2FBF;9B2F;9B2F; # (⾿; ⾿; ⾿; 鬯; 鬯; ) KANGXI RADICAL SACRIFICIAL WINE
+2FC0;2FC0;2FC0;9B32;9B32; # (⿀; ⿀; ⿀; 鬲; 鬲; ) KANGXI RADICAL CAULDRON
+2FC1;2FC1;2FC1;9B3C;9B3C; # (â¿; â¿; â¿; 鬼; 鬼; ) KANGXI RADICAL GHOST
+2FC2;2FC2;2FC2;9B5A;9B5A; # (â¿‚; â¿‚; â¿‚; é­š; é­š; ) KANGXI RADICAL FISH
+2FC3;2FC3;2FC3;9CE5;9CE5; # (⿃; ⿃; ⿃; 鳥; 鳥; ) KANGXI RADICAL BIRD
+2FC4;2FC4;2FC4;9E75;9E75; # (â¿„; â¿„; â¿„; é¹µ; é¹µ; ) KANGXI RADICAL SALT
+2FC5;2FC5;2FC5;9E7F;9E7F; # (⿅; ⿅; ⿅; 鹿; 鹿; ) KANGXI RADICAL DEER
+2FC6;2FC6;2FC6;9EA5;9EA5; # (⿆; ⿆; ⿆; 麥; 麥; ) KANGXI RADICAL WHEAT
+2FC7;2FC7;2FC7;9EBB;9EBB; # (⿇; ⿇; ⿇; 麻; 麻; ) KANGXI RADICAL HEMP
+2FC8;2FC8;2FC8;9EC3;9EC3; # (⿈; ⿈; ⿈; 黃; 黃; ) KANGXI RADICAL YELLOW
+2FC9;2FC9;2FC9;9ECD;9ECD; # (⿉; ⿉; ⿉; é»; é»; ) KANGXI RADICAL MILLET
+2FCA;2FCA;2FCA;9ED1;9ED1; # (⿊; ⿊; ⿊; 黑; 黑; ) KANGXI RADICAL BLACK
+2FCB;2FCB;2FCB;9EF9;9EF9; # (⿋; ⿋; ⿋; 黹; 黹; ) KANGXI RADICAL EMBROIDERY
+2FCC;2FCC;2FCC;9EFD;9EFD; # (⿌; ⿌; ⿌; 黽; 黽; ) KANGXI RADICAL FROG
+2FCD;2FCD;2FCD;9F0E;9F0E; # (â¿; â¿; â¿; 鼎; 鼎; ) KANGXI RADICAL TRIPOD
+2FCE;2FCE;2FCE;9F13;9F13; # (⿎; ⿎; ⿎; 鼓; 鼓; ) KANGXI RADICAL DRUM
+2FCF;2FCF;2FCF;9F20;9F20; # (â¿; â¿; â¿; é¼ ; é¼ ; ) KANGXI RADICAL RAT
+2FD0;2FD0;2FD0;9F3B;9F3B; # (â¿; â¿; â¿; é¼»; é¼»; ) KANGXI RADICAL NOSE
+2FD1;2FD1;2FD1;9F4A;9F4A; # (⿑; ⿑; ⿑; 齊; 齊; ) KANGXI RADICAL EVEN
+2FD2;2FD2;2FD2;9F52;9F52; # (â¿’; â¿’; â¿’; é½’; é½’; ) KANGXI RADICAL TOOTH
+2FD3;2FD3;2FD3;9F8D;9F8D; # (â¿“; â¿“; â¿“; é¾; é¾; ) KANGXI RADICAL DRAGON
+2FD4;2FD4;2FD4;9F9C;9F9C; # (⿔; ⿔; ⿔; 龜; 龜; ) KANGXI RADICAL TURTLE
+2FD5;2FD5;2FD5;9FA0;9FA0; # (â¿•; â¿•; â¿•; é¾ ; é¾ ; ) KANGXI RADICAL FLUTE
+3000;3000;3000;0020;0020; # ( ;  ;  ; ; ; ) IDEOGRAPHIC SPACE
+3036;3036;3036;3012;3012; # (〶; 〶; 〶; 〒; 〒; ) CIRCLED POSTAL MARK
+3038;3038;3038;5341;5341; # (〸; 〸; 〸; å; å; ) HANGZHOU NUMERAL TEN
+3039;3039;3039;5344;5344; # (〹; 〹; 〹; å„; å„; ) HANGZHOU NUMERAL TWENTY
+303A;303A;303A;5345;5345; # (〺; 〺; 〺; å…; å…; ) HANGZHOU NUMERAL THIRTY
+304C;304C;304B 3099;304C;304B 3099; # (ãŒ; ãŒ; ã‹â—Œã‚™; ãŒ; ã‹â—Œã‚™; ) HIRAGANA LETTER GA
+304E;304E;304D 3099;304E;304D 3099; # (ãŽ; ãŽ; ã◌゙; ãŽ; ã◌゙; ) HIRAGANA LETTER GI
+3050;3050;304F 3099;3050;304F 3099; # (ã; ã; ã◌゙; ã; ã◌゙; ) HIRAGANA LETTER GU
+3052;3052;3051 3099;3052;3051 3099; # (ã’; ã’; ã‘◌゙; ã’; ã‘◌゙; ) HIRAGANA LETTER GE
+3054;3054;3053 3099;3054;3053 3099; # (ã”; ã”; ã“◌゙; ã”; ã“◌゙; ) HIRAGANA LETTER GO
+3056;3056;3055 3099;3056;3055 3099; # (ã–; ã–; ã•◌゙; ã–; ã•◌゙; ) HIRAGANA LETTER ZA
+3058;3058;3057 3099;3058;3057 3099; # (ã˜; ã˜; ã—◌゙; ã˜; ã—◌゙; ) HIRAGANA LETTER ZI
+305A;305A;3059 3099;305A;3059 3099; # (ãš; ãš; ã™â—Œã‚™; ãš; ã™â—Œã‚™; ) HIRAGANA LETTER ZU
+305C;305C;305B 3099;305C;305B 3099; # (ãœ; ãœ; ã›â—Œã‚™; ãœ; ã›â—Œã‚™; ) HIRAGANA LETTER ZE
+305E;305E;305D 3099;305E;305D 3099; # (ãž; ãž; ã◌゙; ãž; ã◌゙; ) HIRAGANA LETTER ZO
+3060;3060;305F 3099;3060;305F 3099; # (ã ; ã ; ãŸâ—Œã‚™; ã ; ãŸâ—Œã‚™; ) HIRAGANA LETTER DA
+3062;3062;3061 3099;3062;3061 3099; # (ã¢; ã¢; ã¡â—Œã‚™; ã¢; ã¡â—Œã‚™; ) HIRAGANA LETTER DI
+3065;3065;3064 3099;3065;3064 3099; # (ã¥; ã¥; ã¤â—Œã‚™; ã¥; ã¤â—Œã‚™; ) HIRAGANA LETTER DU
+3067;3067;3066 3099;3067;3066 3099; # (ã§; ã§; ã¦â—Œã‚™; ã§; ã¦â—Œã‚™; ) HIRAGANA LETTER DE
+3069;3069;3068 3099;3069;3068 3099; # (ã©; ã©; ã¨â—Œã‚™; ã©; ã¨â—Œã‚™; ) HIRAGANA LETTER DO
+3070;3070;306F 3099;3070;306F 3099; # (ã°; ã°; ã¯â—Œã‚™; ã°; ã¯â—Œã‚™; ) HIRAGANA LETTER BA
+3071;3071;306F 309A;3071;306F 309A; # (ã±; ã±; ã¯â—Œã‚š; ã±; ã¯â—Œã‚š; ) HIRAGANA LETTER PA
+3073;3073;3072 3099;3073;3072 3099; # (ã³; ã³; ã²â—Œã‚™; ã³; ã²â—Œã‚™; ) HIRAGANA LETTER BI
+3074;3074;3072 309A;3074;3072 309A; # (ã´; ã´; ã²â—Œã‚š; ã´; ã²â—Œã‚š; ) HIRAGANA LETTER PI
+3076;3076;3075 3099;3076;3075 3099; # (ã¶; ã¶; ãµâ—Œã‚™; ã¶; ãµâ—Œã‚™; ) HIRAGANA LETTER BU
+3077;3077;3075 309A;3077;3075 309A; # (ã·; ã·; ãµâ—Œã‚š; ã·; ãµâ—Œã‚š; ) HIRAGANA LETTER PU
+3079;3079;3078 3099;3079;3078 3099; # (ã¹; ã¹; ã¸â—Œã‚™; ã¹; ã¸â—Œã‚™; ) HIRAGANA LETTER BE
+307A;307A;3078 309A;307A;3078 309A; # (ãº; ãº; ã¸â—Œã‚š; ãº; ã¸â—Œã‚š; ) HIRAGANA LETTER PE
+307C;307C;307B 3099;307C;307B 3099; # (ã¼; ã¼; ã»â—Œã‚™; ã¼; ã»â—Œã‚™; ) HIRAGANA LETTER BO
+307D;307D;307B 309A;307D;307B 309A; # (ã½; ã½; ã»â—Œã‚š; ã½; ã»â—Œã‚š; ) HIRAGANA LETTER PO
+3094;3094;3046 3099;3094;3046 3099; # (ã‚”; ã‚”; ã†â—Œã‚™; ã‚”; ã†â—Œã‚™; ) HIRAGANA LETTER VU
+309B;309B;309B;0020 3099;0020 3099; # (゛; ゛; ゛; ◌゙; ◌゙; ) KATAKANA-HIRAGANA VOICED SOUND MARK
+309C;309C;309C;0020 309A;0020 309A; # (゜; ゜; ゜; ◌゚; ◌゚; ) KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK
+309E;309E;309D 3099;309E;309D 3099; # (ゞ; ゞ; ã‚◌゙; ゞ; ã‚◌゙; ) HIRAGANA VOICED ITERATION MARK
+309F;309F;309F;3088 308A;3088 308A; # (ゟ; ゟ; ゟ; より; より; ) HIRAGANA DIGRAPH YORI
+30AC;30AC;30AB 3099;30AC;30AB 3099; # (ガ; ガ; カ◌゙; ガ; カ◌゙; ) KATAKANA LETTER GA
+30AE;30AE;30AD 3099;30AE;30AD 3099; # (ギ; ギ; キ◌゙; ギ; キ◌゙; ) KATAKANA LETTER GI
+30B0;30B0;30AF 3099;30B0;30AF 3099; # (グ; グ; ク◌゙; グ; ク◌゙; ) KATAKANA LETTER GU
+30B2;30B2;30B1 3099;30B2;30B1 3099; # (ゲ; ゲ; ケ◌゙; ゲ; ケ◌゙; ) KATAKANA LETTER GE
+30B4;30B4;30B3 3099;30B4;30B3 3099; # (ゴ; ゴ; コ◌゙; ゴ; コ◌゙; ) KATAKANA LETTER GO
+30B6;30B6;30B5 3099;30B6;30B5 3099; # (ザ; ザ; サ◌゙; ザ; サ◌゙; ) KATAKANA LETTER ZA
+30B8;30B8;30B7 3099;30B8;30B7 3099; # (ジ; ジ; シ◌゙; ジ; シ◌゙; ) KATAKANA LETTER ZI
+30BA;30BA;30B9 3099;30BA;30B9 3099; # (ズ; ズ; ス◌゙; ズ; ス◌゙; ) KATAKANA LETTER ZU
+30BC;30BC;30BB 3099;30BC;30BB 3099; # (ゼ; ゼ; セ◌゙; ゼ; セ◌゙; ) KATAKANA LETTER ZE
+30BE;30BE;30BD 3099;30BE;30BD 3099; # (ゾ; ゾ; ソ◌゙; ゾ; ソ◌゙; ) KATAKANA LETTER ZO
+30C0;30C0;30BF 3099;30C0;30BF 3099; # (ダ; ダ; タ◌゙; ダ; タ◌゙; ) KATAKANA LETTER DA
+30C2;30C2;30C1 3099;30C2;30C1 3099; # (ヂ; ヂ; ãƒâ—Œã‚™; ヂ; ãƒâ—Œã‚™; ) KATAKANA LETTER DI
+30C5;30C5;30C4 3099;30C5;30C4 3099; # (ヅ; ヅ; ツ◌゙; ヅ; ツ◌゙; ) KATAKANA LETTER DU
+30C7;30C7;30C6 3099;30C7;30C6 3099; # (デ; デ; テ◌゙; デ; テ◌゙; ) KATAKANA LETTER DE
+30C9;30C9;30C8 3099;30C9;30C8 3099; # (ド; ド; ト◌゙; ド; ト◌゙; ) KATAKANA LETTER DO
+30D0;30D0;30CF 3099;30D0;30CF 3099; # (ãƒ; ãƒ; ãƒâ—Œã‚™; ãƒ; ãƒâ—Œã‚™; ) KATAKANA LETTER BA
+30D1;30D1;30CF 309A;30D1;30CF 309A; # (パ; パ; ãƒâ—Œã‚š; パ; ãƒâ—Œã‚š; ) KATAKANA LETTER PA
+30D3;30D3;30D2 3099;30D3;30D2 3099; # (ビ; ビ; ヒ◌゙; ビ; ヒ◌゙; ) KATAKANA LETTER BI
+30D4;30D4;30D2 309A;30D4;30D2 309A; # (ピ; ピ; ヒ◌゚; ピ; ヒ◌゚; ) KATAKANA LETTER PI
+30D6;30D6;30D5 3099;30D6;30D5 3099; # (ブ; ブ; フ◌゙; ブ; フ◌゙; ) KATAKANA LETTER BU
+30D7;30D7;30D5 309A;30D7;30D5 309A; # (プ; プ; フ◌゚; プ; フ◌゚; ) KATAKANA LETTER PU
+30D9;30D9;30D8 3099;30D9;30D8 3099; # (ベ; ベ; ヘ◌゙; ベ; ヘ◌゙; ) KATAKANA LETTER BE
+30DA;30DA;30D8 309A;30DA;30D8 309A; # (ペ; ペ; ヘ◌゚; ペ; ヘ◌゚; ) KATAKANA LETTER PE
+30DC;30DC;30DB 3099;30DC;30DB 3099; # (ボ; ボ; ホ◌゙; ボ; ホ◌゙; ) KATAKANA LETTER BO
+30DD;30DD;30DB 309A;30DD;30DB 309A; # (ãƒ; ãƒ; ホ◌゚; ãƒ; ホ◌゚; ) KATAKANA LETTER PO
+30F4;30F4;30A6 3099;30F4;30A6 3099; # (ヴ; ヴ; ウ◌゙; ヴ; ウ◌゙; ) KATAKANA LETTER VU
+30F7;30F7;30EF 3099;30F7;30EF 3099; # (ヷ; ヷ; ワ◌゙; ヷ; ワ◌゙; ) KATAKANA LETTER VA
+30F8;30F8;30F0 3099;30F8;30F0 3099; # (ヸ; ヸ; ヰ◌゙; ヸ; ヰ◌゙; ) KATAKANA LETTER VI
+30F9;30F9;30F1 3099;30F9;30F1 3099; # (ヹ; ヹ; ヱ◌゙; ヹ; ヱ◌゙; ) KATAKANA LETTER VE
+30FA;30FA;30F2 3099;30FA;30F2 3099; # (ヺ; ヺ; ヲ◌゙; ヺ; ヲ◌゙; ) KATAKANA LETTER VO
+30FE;30FE;30FD 3099;30FE;30FD 3099; # (ヾ; ヾ; ヽ◌゙; ヾ; ヽ◌゙; ) KATAKANA VOICED ITERATION MARK
+30FF;30FF;30FF;30B3 30C8;30B3 30C8; # (ヿ; ヿ; ヿ; コト; コト; ) KATAKANA DIGRAPH KOTO
+3131;3131;3131;1100;1100; # (ㄱ; ㄱ; ㄱ; ᄀ; ᄀ; ) HANGUL LETTER KIYEOK
+3132;3132;3132;1101;1101; # (ㄲ; ㄲ; ㄲ; á„; á„; ) HANGUL LETTER SSANGKIYEOK
+3133;3133;3133;11AA;11AA; # (ㄳ; ㄳ; ㄳ; ᆪ; ᆪ; ) HANGUL LETTER KIYEOK-SIOS
+3134;3134;3134;1102;1102; # (ã„´; ã„´; ã„´; á„‚; á„‚; ) HANGUL LETTER NIEUN
+3135;3135;3135;11AC;11AC; # (ㄵ; ㄵ; ㄵ; ᆬ; ᆬ; ) HANGUL LETTER NIEUN-CIEUC
+3136;3136;3136;11AD;11AD; # (ㄶ; ㄶ; ㄶ; ᆭ; ᆭ; ) HANGUL LETTER NIEUN-HIEUH
+3137;3137;3137;1103;1103; # (ㄷ; ㄷ; ㄷ; ᄃ; ᄃ; ) HANGUL LETTER TIKEUT
+3138;3138;3138;1104;1104; # (ㄸ; ㄸ; ㄸ; ᄄ; ᄄ; ) HANGUL LETTER SSANGTIKEUT
+3139;3139;3139;1105;1105; # (ㄹ; ㄹ; ㄹ; ᄅ; ᄅ; ) HANGUL LETTER RIEUL
+313A;313A;313A;11B0;11B0; # (ㄺ; ㄺ; ㄺ; ᆰ; ᆰ; ) HANGUL LETTER RIEUL-KIYEOK
+313B;313B;313B;11B1;11B1; # (ㄻ; ㄻ; ㄻ; ᆱ; ᆱ; ) HANGUL LETTER RIEUL-MIEUM
+313C;313C;313C;11B2;11B2; # (ㄼ; ㄼ; ㄼ; ᆲ; ᆲ; ) HANGUL LETTER RIEUL-PIEUP
+313D;313D;313D;11B3;11B3; # (ㄽ; ㄽ; ㄽ; ᆳ; ᆳ; ) HANGUL LETTER RIEUL-SIOS
+313E;313E;313E;11B4;11B4; # (ㄾ; ㄾ; ㄾ; ᆴ; ᆴ; ) HANGUL LETTER RIEUL-THIEUTH
+313F;313F;313F;11B5;11B5; # (ㄿ; ㄿ; ㄿ; ᆵ; ᆵ; ) HANGUL LETTER RIEUL-PHIEUPH
+3140;3140;3140;111A;111A; # (ㅀ; ㅀ; ㅀ; ᄚ; ᄚ; ) HANGUL LETTER RIEUL-HIEUH
+3141;3141;3141;1106;1106; # (ã…; ã…; ã…; ᄆ; ᄆ; ) HANGUL LETTER MIEUM
+3142;3142;3142;1107;1107; # (ㅂ; ㅂ; ㅂ; ᄇ; ᄇ; ) HANGUL LETTER PIEUP
+3143;3143;3143;1108;1108; # (ㅃ; ㅃ; ㅃ; ᄈ; ᄈ; ) HANGUL LETTER SSANGPIEUP
+3144;3144;3144;1121;1121; # (ã…„; ã…„; ã…„; á„¡; á„¡; ) HANGUL LETTER PIEUP-SIOS
+3145;3145;3145;1109;1109; # (ㅅ; ㅅ; ㅅ; ᄉ; ᄉ; ) HANGUL LETTER SIOS
+3146;3146;3146;110A;110A; # (ㅆ; ㅆ; ㅆ; ᄊ; ᄊ; ) HANGUL LETTER SSANGSIOS
+3147;3147;3147;110B;110B; # (ã…‡; ã…‡; ã…‡; á„‹; á„‹; ) HANGUL LETTER IEUNG
+3148;3148;3148;110C;110C; # (ㅈ; ㅈ; ㅈ; ᄌ; ᄌ; ) HANGUL LETTER CIEUC
+3149;3149;3149;110D;110D; # (ã…‰; ã…‰; ã…‰; á„; á„; ) HANGUL LETTER SSANGCIEUC
+314A;314A;314A;110E;110E; # (ㅊ; ㅊ; ㅊ; ᄎ; ᄎ; ) HANGUL LETTER CHIEUCH
+314B;314B;314B;110F;110F; # (ã…‹; ã…‹; ã…‹; á„; á„; ) HANGUL LETTER KHIEUKH
+314C;314C;314C;1110;1110; # (ã…Œ; ã…Œ; ã…Œ; á„; á„; ) HANGUL LETTER THIEUTH
+314D;314D;314D;1111;1111; # (ã…; ã…; ã…; á„‘; á„‘; ) HANGUL LETTER PHIEUPH
+314E;314E;314E;1112;1112; # (ã…Ž; ã…Ž; ã…Ž; á„’; á„’; ) HANGUL LETTER HIEUH
+314F;314F;314F;1161;1161; # (ã…; ã…; ã…; á…¡; á…¡; ) HANGUL LETTER A
+3150;3150;3150;1162;1162; # (ã…; ã…; ã…; á…¢; á…¢; ) HANGUL LETTER AE
+3151;3151;3151;1163;1163; # (ã…‘; ã…‘; ã…‘; á…£; á…£; ) HANGUL LETTER YA
+3152;3152;3152;1164;1164; # (ã…’; ã…’; ã…’; á…¤; á…¤; ) HANGUL LETTER YAE
+3153;3153;3153;1165;1165; # (ã…“; ã…“; ã…“; á…¥; á…¥; ) HANGUL LETTER EO
+3154;3154;3154;1166;1166; # (ã…”; ã…”; ã…”; á…¦; á…¦; ) HANGUL LETTER E
+3155;3155;3155;1167;1167; # (ã…•; ã…•; ã…•; á…§; á…§; ) HANGUL LETTER YEO
+3156;3156;3156;1168;1168; # (ã…–; ã…–; ã…–; á…¨; á…¨; ) HANGUL LETTER YE
+3157;3157;3157;1169;1169; # (ã…—; ã…—; ã…—; á…©; á…©; ) HANGUL LETTER O
+3158;3158;3158;116A;116A; # (ã…˜; ã…˜; ã…˜; á…ª; á…ª; ) HANGUL LETTER WA
+3159;3159;3159;116B;116B; # (ã…™; ã…™; ã…™; á…«; á…«; ) HANGUL LETTER WAE
+315A;315A;315A;116C;116C; # (ã…š; ã…š; ã…š; á…¬; á…¬; ) HANGUL LETTER OE
+315B;315B;315B;116D;116D; # (ã…›; ã…›; ã…›; á…­; á…­; ) HANGUL LETTER YO
+315C;315C;315C;116E;116E; # (ㅜ; ㅜ; ㅜ; ᅮ; ᅮ; ) HANGUL LETTER U
+315D;315D;315D;116F;116F; # (ã…; ã…; ã…; á…¯; á…¯; ) HANGUL LETTER WEO
+315E;315E;315E;1170;1170; # (ã…ž; ã…ž; ã…ž; á…°; á…°; ) HANGUL LETTER WE
+315F;315F;315F;1171;1171; # (ã…Ÿ; ã…Ÿ; ã…Ÿ; á…±; á…±; ) HANGUL LETTER WI
+3160;3160;3160;1172;1172; # (ã… ; ã… ; ã… ; á…²; á…²; ) HANGUL LETTER YU
+3161;3161;3161;1173;1173; # (ã…¡; ã…¡; ã…¡; á…³; á…³; ) HANGUL LETTER EU
+3162;3162;3162;1174;1174; # (ã…¢; ã…¢; ã…¢; á…´; á…´; ) HANGUL LETTER YI
+3163;3163;3163;1175;1175; # (ã…£; ã…£; ã…£; á…µ; á…µ; ) HANGUL LETTER I
+3164;3164;3164;1160;1160; # (ã…¤; ã…¤; ã…¤; á… ; á… ; ) HANGUL FILLER
+3165;3165;3165;1114;1114; # (ã…¥; ã…¥; ã…¥; á„”; á„”; ) HANGUL LETTER SSANGNIEUN
+3166;3166;3166;1115;1115; # (ã…¦; ã…¦; ã…¦; á„•; á„•; ) HANGUL LETTER NIEUN-TIKEUT
+3167;3167;3167;11C7;11C7; # (ㅧ; ㅧ; ㅧ; ᇇ; ᇇ; ) HANGUL LETTER NIEUN-SIOS
+3168;3168;3168;11C8;11C8; # (ㅨ; ㅨ; ㅨ; ᇈ; ᇈ; ) HANGUL LETTER NIEUN-PANSIOS
+3169;3169;3169;11CC;11CC; # (ㅩ; ㅩ; ㅩ; ᇌ; ᇌ; ) HANGUL LETTER RIEUL-KIYEOK-SIOS
+316A;316A;316A;11CE;11CE; # (ㅪ; ㅪ; ㅪ; ᇎ; ᇎ; ) HANGUL LETTER RIEUL-TIKEUT
+316B;316B;316B;11D3;11D3; # (ㅫ; ㅫ; ㅫ; ᇓ; ᇓ; ) HANGUL LETTER RIEUL-PIEUP-SIOS
+316C;316C;316C;11D7;11D7; # (ㅬ; ㅬ; ㅬ; ᇗ; ᇗ; ) HANGUL LETTER RIEUL-PANSIOS
+316D;316D;316D;11D9;11D9; # (ㅭ; ㅭ; ㅭ; ᇙ; ᇙ; ) HANGUL LETTER RIEUL-YEORINHIEUH
+316E;316E;316E;111C;111C; # (ㅮ; ㅮ; ㅮ; ᄜ; ᄜ; ) HANGUL LETTER MIEUM-PIEUP
+316F;316F;316F;11DD;11DD; # (ã…¯; ã…¯; ã…¯; á‡; á‡; ) HANGUL LETTER MIEUM-SIOS
+3170;3170;3170;11DF;11DF; # (ㅰ; ㅰ; ㅰ; ᇟ; ᇟ; ) HANGUL LETTER MIEUM-PANSIOS
+3171;3171;3171;111D;111D; # (ã…±; ã…±; ã…±; á„; á„; ) HANGUL LETTER KAPYEOUNMIEUM
+3172;3172;3172;111E;111E; # (ㅲ; ㅲ; ㅲ; ᄞ; ᄞ; ) HANGUL LETTER PIEUP-KIYEOK
+3173;3173;3173;1120;1120; # (ã…³; ã…³; ã…³; á„ ; á„ ; ) HANGUL LETTER PIEUP-TIKEUT
+3174;3174;3174;1122;1122; # (ã…´; ã…´; ã…´; á„¢; á„¢; ) HANGUL LETTER PIEUP-SIOS-KIYEOK
+3175;3175;3175;1123;1123; # (ã…µ; ã…µ; ã…µ; á„£; á„£; ) HANGUL LETTER PIEUP-SIOS-TIKEUT
+3176;3176;3176;1127;1127; # (ã…¶; ã…¶; ã…¶; á„§; á„§; ) HANGUL LETTER PIEUP-CIEUC
+3177;3177;3177;1129;1129; # (ã…·; ã…·; ã…·; á„©; á„©; ) HANGUL LETTER PIEUP-THIEUTH
+3178;3178;3178;112B;112B; # (ã…¸; ã…¸; ã…¸; á„«; á„«; ) HANGUL LETTER KAPYEOUNPIEUP
+3179;3179;3179;112C;112C; # (ㅹ; ㅹ; ㅹ; ᄬ; ᄬ; ) HANGUL LETTER KAPYEOUNSSANGPIEUP
+317A;317A;317A;112D;112D; # (ã…º; ã…º; ã…º; á„­; á„­; ) HANGUL LETTER SIOS-KIYEOK
+317B;317B;317B;112E;112E; # (ã…»; ã…»; ã…»; á„®; á„®; ) HANGUL LETTER SIOS-NIEUN
+317C;317C;317C;112F;112F; # (ㅼ; ㅼ; ㅼ; ᄯ; ᄯ; ) HANGUL LETTER SIOS-TIKEUT
+317D;317D;317D;1132;1132; # (ㅽ; ㅽ; ㅽ; ᄲ; ᄲ; ) HANGUL LETTER SIOS-PIEUP
+317E;317E;317E;1136;1136; # (ã…¾; ã…¾; ã…¾; á„¶; á„¶; ) HANGUL LETTER SIOS-CIEUC
+317F;317F;317F;1140;1140; # (ã…¿; ã…¿; ã…¿; á…€; á…€; ) HANGUL LETTER PANSIOS
+3180;3180;3180;1147;1147; # (ㆀ; ㆀ; ㆀ; ᅇ; ᅇ; ) HANGUL LETTER SSANGIEUNG
+3181;3181;3181;114C;114C; # (ã†; ã†; ã†; á…Œ; á…Œ; ) HANGUL LETTER YESIEUNG
+3182;3182;3182;11F1;11F1; # (ㆂ; ㆂ; ㆂ; ᇱ; ᇱ; ) HANGUL LETTER YESIEUNG-SIOS
+3183;3183;3183;11F2;11F2; # (ㆃ; ㆃ; ㆃ; ᇲ; ᇲ; ) HANGUL LETTER YESIEUNG-PANSIOS
+3184;3184;3184;1157;1157; # (ㆄ; ㆄ; ㆄ; ᅗ; ᅗ; ) HANGUL LETTER KAPYEOUNPHIEUPH
+3185;3185;3185;1158;1158; # (ㆅ; ㆅ; ㆅ; ᅘ; ᅘ; ) HANGUL LETTER SSANGHIEUH
+3186;3186;3186;1159;1159; # (ㆆ; ㆆ; ㆆ; ᅙ; ᅙ; ) HANGUL LETTER YEORINHIEUH
+3187;3187;3187;1184;1184; # (ㆇ; ㆇ; ㆇ; ᆄ; ᆄ; ) HANGUL LETTER YO-YA
+3188;3188;3188;1185;1185; # (ㆈ; ㆈ; ㆈ; ᆅ; ᆅ; ) HANGUL LETTER YO-YAE
+3189;3189;3189;1188;1188; # (ㆉ; ㆉ; ㆉ; ᆈ; ᆈ; ) HANGUL LETTER YO-I
+318A;318A;318A;1191;1191; # (ㆊ; ㆊ; ㆊ; ᆑ; ᆑ; ) HANGUL LETTER YU-YEO
+318B;318B;318B;1192;1192; # (ㆋ; ㆋ; ㆋ; ᆒ; ᆒ; ) HANGUL LETTER YU-YE
+318C;318C;318C;1194;1194; # (ㆌ; ㆌ; ㆌ; ᆔ; ᆔ; ) HANGUL LETTER YU-I
+318D;318D;318D;119E;119E; # (ã†; ã†; ã†; ᆞ; ᆞ; ) HANGUL LETTER ARAEA
+318E;318E;318E;11A1;11A1; # (ㆎ; ㆎ; ㆎ; ᆡ; ᆡ; ) HANGUL LETTER ARAEAE
+3192;3192;3192;4E00;4E00; # (㆒; ㆒; ㆒; 一; 一; ) IDEOGRAPHIC ANNOTATION ONE MARK
+3193;3193;3193;4E8C;4E8C; # (㆓; ㆓; ㆓; 二; 二; ) IDEOGRAPHIC ANNOTATION TWO MARK
+3194;3194;3194;4E09;4E09; # (㆔; ㆔; ㆔; 三; 三; ) IDEOGRAPHIC ANNOTATION THREE MARK
+3195;3195;3195;56DB;56DB; # (㆕; ㆕; ㆕; 四; 四; ) IDEOGRAPHIC ANNOTATION FOUR MARK
+3196;3196;3196;4E0A;4E0A; # (㆖; ㆖; ㆖; 上; 上; ) IDEOGRAPHIC ANNOTATION TOP MARK
+3197;3197;3197;4E2D;4E2D; # (㆗; ㆗; ㆗; 中; 中; ) IDEOGRAPHIC ANNOTATION MIDDLE MARK
+3198;3198;3198;4E0B;4E0B; # (㆘; ㆘; ㆘; 下; 下; ) IDEOGRAPHIC ANNOTATION BOTTOM MARK
+3199;3199;3199;7532;7532; # (㆙; ㆙; ㆙; 甲; 甲; ) IDEOGRAPHIC ANNOTATION FIRST MARK
+319A;319A;319A;4E59;4E59; # (㆚; ㆚; ㆚; 乙; 乙; ) IDEOGRAPHIC ANNOTATION SECOND MARK
+319B;319B;319B;4E19;4E19; # (㆛; ㆛; ㆛; 丙; 丙; ) IDEOGRAPHIC ANNOTATION THIRD MARK
+319C;319C;319C;4E01;4E01; # (㆜; ㆜; ㆜; ä¸; ä¸; ) IDEOGRAPHIC ANNOTATION FOURTH MARK
+319D;319D;319D;5929;5929; # (ã†; ã†; ã†; 天; 天; ) IDEOGRAPHIC ANNOTATION HEAVEN MARK
+319E;319E;319E;5730;5730; # (㆞; ㆞; ㆞; 地; 地; ) IDEOGRAPHIC ANNOTATION EARTH MARK
+319F;319F;319F;4EBA;4EBA; # (㆟; ㆟; ㆟; 人; 人; ) IDEOGRAPHIC ANNOTATION MAN MARK
+3200;3200;3200;0028 1100 0029;0028 1100 0029; # (㈀; ㈀; ㈀; (ᄀ); (ᄀ); ) PARENTHESIZED HANGUL KIYEOK
+3201;3201;3201;0028 1102 0029;0028 1102 0029; # (ãˆ; ãˆ; ãˆ; (á„‚); (á„‚); ) PARENTHESIZED HANGUL NIEUN
+3202;3202;3202;0028 1103 0029;0028 1103 0029; # (㈂; ㈂; ㈂; (ᄃ); (ᄃ); ) PARENTHESIZED HANGUL TIKEUT
+3203;3203;3203;0028 1105 0029;0028 1105 0029; # (㈃; ㈃; ㈃; (ᄅ); (ᄅ); ) PARENTHESIZED HANGUL RIEUL
+3204;3204;3204;0028 1106 0029;0028 1106 0029; # (㈄; ㈄; ㈄; (ᄆ); (ᄆ); ) PARENTHESIZED HANGUL MIEUM
+3205;3205;3205;0028 1107 0029;0028 1107 0029; # (㈅; ㈅; ㈅; (ᄇ); (ᄇ); ) PARENTHESIZED HANGUL PIEUP
+3206;3206;3206;0028 1109 0029;0028 1109 0029; # (㈆; ㈆; ㈆; (ᄉ); (ᄉ); ) PARENTHESIZED HANGUL SIOS
+3207;3207;3207;0028 110B 0029;0028 110B 0029; # (㈇; ㈇; ㈇; (ᄋ); (ᄋ); ) PARENTHESIZED HANGUL IEUNG
+3208;3208;3208;0028 110C 0029;0028 110C 0029; # (㈈; ㈈; ㈈; (ᄌ); (ᄌ); ) PARENTHESIZED HANGUL CIEUC
+3209;3209;3209;0028 110E 0029;0028 110E 0029; # (㈉; ㈉; ㈉; (ᄎ); (ᄎ); ) PARENTHESIZED HANGUL CHIEUCH
+320A;320A;320A;0028 110F 0029;0028 110F 0029; # (㈊; ㈊; ㈊; (á„); (á„); ) PARENTHESIZED HANGUL KHIEUKH
+320B;320B;320B;0028 1110 0029;0028 1110 0029; # (㈋; ㈋; ㈋; (á„); (á„); ) PARENTHESIZED HANGUL THIEUTH
+320C;320C;320C;0028 1111 0029;0028 1111 0029; # (㈌; ㈌; ㈌; (ᄑ); (ᄑ); ) PARENTHESIZED HANGUL PHIEUPH
+320D;320D;320D;0028 1112 0029;0028 1112 0029; # (ãˆ; ãˆ; ãˆ; (á„’); (á„’); ) PARENTHESIZED HANGUL HIEUH
+320E;320E;320E;0028 AC00 0029;0028 1100 1161 0029; # (㈎; ㈎; ㈎; (가); (가); ) PARENTHESIZED HANGUL KIYEOK A
+320F;320F;320F;0028 B098 0029;0028 1102 1161 0029; # (ãˆ; ãˆ; ãˆ; (나); (á„‚á…¡); ) PARENTHESIZED HANGUL NIEUN A
+3210;3210;3210;0028 B2E4 0029;0028 1103 1161 0029; # (ãˆ; ãˆ; ãˆ; (다); (다); ) PARENTHESIZED HANGUL TIKEUT A
+3211;3211;3211;0028 B77C 0029;0028 1105 1161 0029; # (㈑; ㈑; ㈑; (ë¼); (á„…á…¡); ) PARENTHESIZED HANGUL RIEUL A
+3212;3212;3212;0028 B9C8 0029;0028 1106 1161 0029; # (㈒; ㈒; ㈒; (마); (마); ) PARENTHESIZED HANGUL MIEUM A
+3213;3213;3213;0028 BC14 0029;0028 1107 1161 0029; # (㈓; ㈓; ㈓; (바); (바); ) PARENTHESIZED HANGUL PIEUP A
+3214;3214;3214;0028 C0AC 0029;0028 1109 1161 0029; # (㈔; ㈔; ㈔; (사); (사); ) PARENTHESIZED HANGUL SIOS A
+3215;3215;3215;0028 C544 0029;0028 110B 1161 0029; # (㈕; ㈕; ㈕; (아); (아); ) PARENTHESIZED HANGUL IEUNG A
+3216;3216;3216;0028 C790 0029;0028 110C 1161 0029; # (㈖; ㈖; ㈖; (ìž); (자); ) PARENTHESIZED HANGUL CIEUC A
+3217;3217;3217;0028 CC28 0029;0028 110E 1161 0029; # (㈗; ㈗; ㈗; (차); (차); ) PARENTHESIZED HANGUL CHIEUCH A
+3218;3218;3218;0028 CE74 0029;0028 110F 1161 0029; # (㈘; ㈘; ㈘; (ì¹´); (á„á…¡); ) PARENTHESIZED HANGUL KHIEUKH A
+3219;3219;3219;0028 D0C0 0029;0028 1110 1161 0029; # (㈙; ㈙; ㈙; (타); (á„á…¡); ) PARENTHESIZED HANGUL THIEUTH A
+321A;321A;321A;0028 D30C 0029;0028 1111 1161 0029; # (㈚; ㈚; ㈚; (파); (파); ) PARENTHESIZED HANGUL PHIEUPH A
+321B;321B;321B;0028 D558 0029;0028 1112 1161 0029; # (㈛; ㈛; ㈛; (하); (하); ) PARENTHESIZED HANGUL HIEUH A
+321C;321C;321C;0028 C8FC 0029;0028 110C 116E 0029; # (㈜; ㈜; ㈜; (주); (주); ) PARENTHESIZED HANGUL CIEUC U
+321D;321D;321D;0028 C624 C804 0029;0028 110B 1169 110C 1165 11AB 0029; # (ãˆ; ãˆ; ãˆ; (오전); (오전); ) PARENTHESIZED KOREAN CHARACTER OJEON
+321E;321E;321E;0028 C624 D6C4 0029;0028 110B 1169 1112 116E 0029; # (㈞; ㈞; ㈞; (오후); (오후); ) PARENTHESIZED KOREAN CHARACTER O HU
+3220;3220;3220;0028 4E00 0029;0028 4E00 0029; # (㈠; ㈠; ㈠; (一); (一); ) PARENTHESIZED IDEOGRAPH ONE
+3221;3221;3221;0028 4E8C 0029;0028 4E8C 0029; # (㈡; ㈡; ㈡; (二); (二); ) PARENTHESIZED IDEOGRAPH TWO
+3222;3222;3222;0028 4E09 0029;0028 4E09 0029; # (㈢; ㈢; ㈢; (三); (三); ) PARENTHESIZED IDEOGRAPH THREE
+3223;3223;3223;0028 56DB 0029;0028 56DB 0029; # (㈣; ㈣; ㈣; (四); (四); ) PARENTHESIZED IDEOGRAPH FOUR
+3224;3224;3224;0028 4E94 0029;0028 4E94 0029; # (㈤; ㈤; ㈤; (五); (五); ) PARENTHESIZED IDEOGRAPH FIVE
+3225;3225;3225;0028 516D 0029;0028 516D 0029; # (㈥; ㈥; ㈥; (六); (六); ) PARENTHESIZED IDEOGRAPH SIX
+3226;3226;3226;0028 4E03 0029;0028 4E03 0029; # (㈦; ㈦; ㈦; (七); (七); ) PARENTHESIZED IDEOGRAPH SEVEN
+3227;3227;3227;0028 516B 0029;0028 516B 0029; # (㈧; ㈧; ㈧; (八); (八); ) PARENTHESIZED IDEOGRAPH EIGHT
+3228;3228;3228;0028 4E5D 0029;0028 4E5D 0029; # (㈨; ㈨; ㈨; (ä¹); (ä¹); ) PARENTHESIZED IDEOGRAPH NINE
+3229;3229;3229;0028 5341 0029;0028 5341 0029; # (㈩; ㈩; ㈩; (å); (å); ) PARENTHESIZED IDEOGRAPH TEN
+322A;322A;322A;0028 6708 0029;0028 6708 0029; # (㈪; ㈪; ㈪; (月); (月); ) PARENTHESIZED IDEOGRAPH MOON
+322B;322B;322B;0028 706B 0029;0028 706B 0029; # (㈫; ㈫; ㈫; (ç«); (ç«); ) PARENTHESIZED IDEOGRAPH FIRE
+322C;322C;322C;0028 6C34 0029;0028 6C34 0029; # (㈬; ㈬; ㈬; (水); (水); ) PARENTHESIZED IDEOGRAPH WATER
+322D;322D;322D;0028 6728 0029;0028 6728 0029; # (㈭; ㈭; ㈭; (木); (木); ) PARENTHESIZED IDEOGRAPH WOOD
+322E;322E;322E;0028 91D1 0029;0028 91D1 0029; # (㈮; ㈮; ㈮; (金); (金); ) PARENTHESIZED IDEOGRAPH METAL
+322F;322F;322F;0028 571F 0029;0028 571F 0029; # (㈯; ㈯; ㈯; (土); (土); ) PARENTHESIZED IDEOGRAPH EARTH
+3230;3230;3230;0028 65E5 0029;0028 65E5 0029; # (㈰; ㈰; ㈰; (日); (日); ) PARENTHESIZED IDEOGRAPH SUN
+3231;3231;3231;0028 682A 0029;0028 682A 0029; # (㈱; ㈱; ㈱; (株); (株); ) PARENTHESIZED IDEOGRAPH STOCK
+3232;3232;3232;0028 6709 0029;0028 6709 0029; # (㈲; ㈲; ㈲; (有); (有); ) PARENTHESIZED IDEOGRAPH HAVE
+3233;3233;3233;0028 793E 0029;0028 793E 0029; # (㈳; ㈳; ㈳; (社); (社); ) PARENTHESIZED IDEOGRAPH SOCIETY
+3234;3234;3234;0028 540D 0029;0028 540D 0029; # (㈴; ㈴; ㈴; (å); (å); ) PARENTHESIZED IDEOGRAPH NAME
+3235;3235;3235;0028 7279 0029;0028 7279 0029; # (㈵; ㈵; ㈵; (特); (特); ) PARENTHESIZED IDEOGRAPH SPECIAL
+3236;3236;3236;0028 8CA1 0029;0028 8CA1 0029; # (㈶; ㈶; ㈶; (財); (財); ) PARENTHESIZED IDEOGRAPH FINANCIAL
+3237;3237;3237;0028 795D 0029;0028 795D 0029; # (㈷; ㈷; ㈷; (ç¥); (ç¥); ) PARENTHESIZED IDEOGRAPH CONGRATULATION
+3238;3238;3238;0028 52B4 0029;0028 52B4 0029; # (㈸; ㈸; ㈸; (労); (労); ) PARENTHESIZED IDEOGRAPH LABOR
+3239;3239;3239;0028 4EE3 0029;0028 4EE3 0029; # (㈹; ㈹; ㈹; (代); (代); ) PARENTHESIZED IDEOGRAPH REPRESENT
+323A;323A;323A;0028 547C 0029;0028 547C 0029; # (㈺; ㈺; ㈺; (呼); (呼); ) PARENTHESIZED IDEOGRAPH CALL
+323B;323B;323B;0028 5B66 0029;0028 5B66 0029; # (㈻; ㈻; ㈻; (学); (学); ) PARENTHESIZED IDEOGRAPH STUDY
+323C;323C;323C;0028 76E3 0029;0028 76E3 0029; # (㈼; ㈼; ㈼; (監); (監); ) PARENTHESIZED IDEOGRAPH SUPERVISE
+323D;323D;323D;0028 4F01 0029;0028 4F01 0029; # (㈽; ㈽; ㈽; (ä¼); (ä¼); ) PARENTHESIZED IDEOGRAPH ENTERPRISE
+323E;323E;323E;0028 8CC7 0029;0028 8CC7 0029; # (㈾; ㈾; ㈾; (資); (資); ) PARENTHESIZED IDEOGRAPH RESOURCE
+323F;323F;323F;0028 5354 0029;0028 5354 0029; # (㈿; ㈿; ㈿; (å”); (å”); ) PARENTHESIZED IDEOGRAPH ALLIANCE
+3240;3240;3240;0028 796D 0029;0028 796D 0029; # (㉀; ㉀; ㉀; (祭); (祭); ) PARENTHESIZED IDEOGRAPH FESTIVAL
+3241;3241;3241;0028 4F11 0029;0028 4F11 0029; # (ã‰; ã‰; ã‰; (休); (休); ) PARENTHESIZED IDEOGRAPH REST
+3242;3242;3242;0028 81EA 0029;0028 81EA 0029; # (㉂; ㉂; ㉂; (自); (自); ) PARENTHESIZED IDEOGRAPH SELF
+3243;3243;3243;0028 81F3 0029;0028 81F3 0029; # (㉃; ㉃; ㉃; (至); (至); ) PARENTHESIZED IDEOGRAPH REACH
+3250;3250;3250;0050 0054 0045;0050 0054 0045; # (ã‰; ã‰; ã‰; PTE; PTE; ) PARTNERSHIP SIGN
+3251;3251;3251;0032 0031;0032 0031; # (㉑; ㉑; ㉑; 21; 21; ) CIRCLED NUMBER TWENTY ONE
+3252;3252;3252;0032 0032;0032 0032; # (㉒; ㉒; ㉒; 22; 22; ) CIRCLED NUMBER TWENTY TWO
+3253;3253;3253;0032 0033;0032 0033; # (㉓; ㉓; ㉓; 23; 23; ) CIRCLED NUMBER TWENTY THREE
+3254;3254;3254;0032 0034;0032 0034; # (㉔; ㉔; ㉔; 24; 24; ) CIRCLED NUMBER TWENTY FOUR
+3255;3255;3255;0032 0035;0032 0035; # (㉕; ㉕; ㉕; 25; 25; ) CIRCLED NUMBER TWENTY FIVE
+3256;3256;3256;0032 0036;0032 0036; # (㉖; ㉖; ㉖; 26; 26; ) CIRCLED NUMBER TWENTY SIX
+3257;3257;3257;0032 0037;0032 0037; # (㉗; ㉗; ㉗; 27; 27; ) CIRCLED NUMBER TWENTY SEVEN
+3258;3258;3258;0032 0038;0032 0038; # (㉘; ㉘; ㉘; 28; 28; ) CIRCLED NUMBER TWENTY EIGHT
+3259;3259;3259;0032 0039;0032 0039; # (㉙; ㉙; ㉙; 29; 29; ) CIRCLED NUMBER TWENTY NINE
+325A;325A;325A;0033 0030;0033 0030; # (㉚; ㉚; ㉚; 30; 30; ) CIRCLED NUMBER THIRTY
+325B;325B;325B;0033 0031;0033 0031; # (㉛; ㉛; ㉛; 31; 31; ) CIRCLED NUMBER THIRTY ONE
+325C;325C;325C;0033 0032;0033 0032; # (㉜; ㉜; ㉜; 32; 32; ) CIRCLED NUMBER THIRTY TWO
+325D;325D;325D;0033 0033;0033 0033; # (ã‰; ã‰; ã‰; 33; 33; ) CIRCLED NUMBER THIRTY THREE
+325E;325E;325E;0033 0034;0033 0034; # (㉞; ㉞; ㉞; 34; 34; ) CIRCLED NUMBER THIRTY FOUR
+325F;325F;325F;0033 0035;0033 0035; # (㉟; ㉟; ㉟; 35; 35; ) CIRCLED NUMBER THIRTY FIVE
+3260;3260;3260;1100;1100; # (㉠; ㉠; ㉠; ᄀ; ᄀ; ) CIRCLED HANGUL KIYEOK
+3261;3261;3261;1102;1102; # (㉡; ㉡; ㉡; ᄂ; ᄂ; ) CIRCLED HANGUL NIEUN
+3262;3262;3262;1103;1103; # (㉢; ㉢; ㉢; ᄃ; ᄃ; ) CIRCLED HANGUL TIKEUT
+3263;3263;3263;1105;1105; # (㉣; ㉣; ㉣; ᄅ; ᄅ; ) CIRCLED HANGUL RIEUL
+3264;3264;3264;1106;1106; # (㉤; ㉤; ㉤; ᄆ; ᄆ; ) CIRCLED HANGUL MIEUM
+3265;3265;3265;1107;1107; # (㉥; ㉥; ㉥; ᄇ; ᄇ; ) CIRCLED HANGUL PIEUP
+3266;3266;3266;1109;1109; # (㉦; ㉦; ㉦; ᄉ; ᄉ; ) CIRCLED HANGUL SIOS
+3267;3267;3267;110B;110B; # (㉧; ㉧; ㉧; ᄋ; ᄋ; ) CIRCLED HANGUL IEUNG
+3268;3268;3268;110C;110C; # (㉨; ㉨; ㉨; ᄌ; ᄌ; ) CIRCLED HANGUL CIEUC
+3269;3269;3269;110E;110E; # (㉩; ㉩; ㉩; ᄎ; ᄎ; ) CIRCLED HANGUL CHIEUCH
+326A;326A;326A;110F;110F; # (㉪; ㉪; ㉪; á„; á„; ) CIRCLED HANGUL KHIEUKH
+326B;326B;326B;1110;1110; # (㉫; ㉫; ㉫; á„; á„; ) CIRCLED HANGUL THIEUTH
+326C;326C;326C;1111;1111; # (㉬; ㉬; ㉬; ᄑ; ᄑ; ) CIRCLED HANGUL PHIEUPH
+326D;326D;326D;1112;1112; # (㉭; ㉭; ㉭; ᄒ; ᄒ; ) CIRCLED HANGUL HIEUH
+326E;326E;326E;AC00;1100 1161; # (㉮; ㉮; ㉮; 가; 가; ) CIRCLED HANGUL KIYEOK A
+326F;326F;326F;B098;1102 1161; # (㉯; ㉯; ㉯; 나; 나; ) CIRCLED HANGUL NIEUN A
+3270;3270;3270;B2E4;1103 1161; # (㉰; ㉰; ㉰; 다; 다; ) CIRCLED HANGUL TIKEUT A
+3271;3271;3271;B77C;1105 1161; # (㉱; ㉱; ㉱; ë¼; á„…á…¡; ) CIRCLED HANGUL RIEUL A
+3272;3272;3272;B9C8;1106 1161; # (㉲; ㉲; ㉲; 마; 마; ) CIRCLED HANGUL MIEUM A
+3273;3273;3273;BC14;1107 1161; # (㉳; ㉳; ㉳; 바; 바; ) CIRCLED HANGUL PIEUP A
+3274;3274;3274;C0AC;1109 1161; # (㉴; ㉴; ㉴; 사; 사; ) CIRCLED HANGUL SIOS A
+3275;3275;3275;C544;110B 1161; # (㉵; ㉵; ㉵; 아; 아; ) CIRCLED HANGUL IEUNG A
+3276;3276;3276;C790;110C 1161; # (㉶; ㉶; ㉶; ìž; 자; ) CIRCLED HANGUL CIEUC A
+3277;3277;3277;CC28;110E 1161; # (㉷; ㉷; ㉷; 차; 차; ) CIRCLED HANGUL CHIEUCH A
+3278;3278;3278;CE74;110F 1161; # (㉸; ㉸; ㉸; ì¹´; á„á…¡; ) CIRCLED HANGUL KHIEUKH A
+3279;3279;3279;D0C0;1110 1161; # (㉹; ㉹; ㉹; 타; á„á…¡; ) CIRCLED HANGUL THIEUTH A
+327A;327A;327A;D30C;1111 1161; # (㉺; ㉺; ㉺; 파; 파; ) CIRCLED HANGUL PHIEUPH A
+327B;327B;327B;D558;1112 1161; # (㉻; ㉻; ㉻; 하; 하; ) CIRCLED HANGUL HIEUH A
+327C;327C;327C;CC38 ACE0;110E 1161 11B7 1100 1169; # (㉼; ㉼; ㉼; 참고; 참고; ) CIRCLED KOREAN CHARACTER CHAMKO
+327D;327D;327D;C8FC C758;110C 116E 110B 1174; # (㉽; ㉽; ㉽; 주ì˜; 주의; ) CIRCLED KOREAN CHARACTER JUEUI
+3280;3280;3280;4E00;4E00; # (㊀; ㊀; ㊀; 一; 一; ) CIRCLED IDEOGRAPH ONE
+3281;3281;3281;4E8C;4E8C; # (ãŠ; ãŠ; ãŠ; 二; 二; ) CIRCLED IDEOGRAPH TWO
+3282;3282;3282;4E09;4E09; # (㊂; ㊂; ㊂; 三; 三; ) CIRCLED IDEOGRAPH THREE
+3283;3283;3283;56DB;56DB; # (㊃; ㊃; ㊃; 四; 四; ) CIRCLED IDEOGRAPH FOUR
+3284;3284;3284;4E94;4E94; # (㊄; ㊄; ㊄; 五; 五; ) CIRCLED IDEOGRAPH FIVE
+3285;3285;3285;516D;516D; # (㊅; ㊅; ㊅; 六; 六; ) CIRCLED IDEOGRAPH SIX
+3286;3286;3286;4E03;4E03; # (㊆; ㊆; ㊆; 七; 七; ) CIRCLED IDEOGRAPH SEVEN
+3287;3287;3287;516B;516B; # (㊇; ㊇; ㊇; 八; 八; ) CIRCLED IDEOGRAPH EIGHT
+3288;3288;3288;4E5D;4E5D; # (㊈; ㊈; ㊈; ä¹; ä¹; ) CIRCLED IDEOGRAPH NINE
+3289;3289;3289;5341;5341; # (㊉; ㊉; ㊉; å; å; ) CIRCLED IDEOGRAPH TEN
+328A;328A;328A;6708;6708; # (㊊; ㊊; ㊊; 月; 月; ) CIRCLED IDEOGRAPH MOON
+328B;328B;328B;706B;706B; # (㊋; ㊋; ㊋; ç«; ç«; ) CIRCLED IDEOGRAPH FIRE
+328C;328C;328C;6C34;6C34; # (㊌; ㊌; ㊌; 水; 水; ) CIRCLED IDEOGRAPH WATER
+328D;328D;328D;6728;6728; # (ãŠ; ãŠ; ãŠ; 木; 木; ) CIRCLED IDEOGRAPH WOOD
+328E;328E;328E;91D1;91D1; # (㊎; ㊎; ㊎; 金; 金; ) CIRCLED IDEOGRAPH METAL
+328F;328F;328F;571F;571F; # (ãŠ; ãŠ; ãŠ; 土; 土; ) CIRCLED IDEOGRAPH EARTH
+3290;3290;3290;65E5;65E5; # (ãŠ; ãŠ; ãŠ; æ—¥; æ—¥; ) CIRCLED IDEOGRAPH SUN
+3291;3291;3291;682A;682A; # (㊑; ㊑; ㊑; 株; 株; ) CIRCLED IDEOGRAPH STOCK
+3292;3292;3292;6709;6709; # (㊒; ㊒; ㊒; 有; 有; ) CIRCLED IDEOGRAPH HAVE
+3293;3293;3293;793E;793E; # (㊓; ㊓; ㊓; 社; 社; ) CIRCLED IDEOGRAPH SOCIETY
+3294;3294;3294;540D;540D; # (㊔; ㊔; ㊔; å; å; ) CIRCLED IDEOGRAPH NAME
+3295;3295;3295;7279;7279; # (㊕; ㊕; ㊕; 特; 特; ) CIRCLED IDEOGRAPH SPECIAL
+3296;3296;3296;8CA1;8CA1; # (㊖; ㊖; ㊖; 財; 財; ) CIRCLED IDEOGRAPH FINANCIAL
+3297;3297;3297;795D;795D; # (㊗; ㊗; ㊗; ç¥; ç¥; ) CIRCLED IDEOGRAPH CONGRATULATION
+3298;3298;3298;52B4;52B4; # (㊘; ㊘; ㊘; 労; 労; ) CIRCLED IDEOGRAPH LABOR
+3299;3299;3299;79D8;79D8; # (㊙; ㊙; ㊙; 秘; 秘; ) CIRCLED IDEOGRAPH SECRET
+329A;329A;329A;7537;7537; # (㊚; ㊚; ㊚; 男; 男; ) CIRCLED IDEOGRAPH MALE
+329B;329B;329B;5973;5973; # (㊛; ㊛; ㊛; 女; 女; ) CIRCLED IDEOGRAPH FEMALE
+329C;329C;329C;9069;9069; # (㊜; ㊜; ㊜; é©; é©; ) CIRCLED IDEOGRAPH SUITABLE
+329D;329D;329D;512A;512A; # (ãŠ; ãŠ; ãŠ; 優; 優; ) CIRCLED IDEOGRAPH EXCELLENT
+329E;329E;329E;5370;5370; # (㊞; ㊞; ㊞; å°; å°; ) CIRCLED IDEOGRAPH PRINT
+329F;329F;329F;6CE8;6CE8; # (㊟; ㊟; ㊟; 注; 注; ) CIRCLED IDEOGRAPH ATTENTION
+32A0;32A0;32A0;9805;9805; # (㊠; ㊠; ㊠; 項; 項; ) CIRCLED IDEOGRAPH ITEM
+32A1;32A1;32A1;4F11;4F11; # (㊡; ㊡; ㊡; 休; 休; ) CIRCLED IDEOGRAPH REST
+32A2;32A2;32A2;5199;5199; # (㊢; ㊢; ㊢; 写; 写; ) CIRCLED IDEOGRAPH COPY
+32A3;32A3;32A3;6B63;6B63; # (㊣; ㊣; ㊣; 正; 正; ) CIRCLED IDEOGRAPH CORRECT
+32A4;32A4;32A4;4E0A;4E0A; # (㊤; ㊤; ㊤; 上; 上; ) CIRCLED IDEOGRAPH HIGH
+32A5;32A5;32A5;4E2D;4E2D; # (㊥; ㊥; ㊥; 中; 中; ) CIRCLED IDEOGRAPH CENTRE
+32A6;32A6;32A6;4E0B;4E0B; # (㊦; ㊦; ㊦; 下; 下; ) CIRCLED IDEOGRAPH LOW
+32A7;32A7;32A7;5DE6;5DE6; # (㊧; ㊧; ㊧; 左; 左; ) CIRCLED IDEOGRAPH LEFT
+32A8;32A8;32A8;53F3;53F3; # (㊨; ㊨; ㊨; å³; å³; ) CIRCLED IDEOGRAPH RIGHT
+32A9;32A9;32A9;533B;533B; # (㊩; ㊩; ㊩; 医; 医; ) CIRCLED IDEOGRAPH MEDICINE
+32AA;32AA;32AA;5B97;5B97; # (㊪; ㊪; ㊪; 宗; 宗; ) CIRCLED IDEOGRAPH RELIGION
+32AB;32AB;32AB;5B66;5B66; # (㊫; ㊫; ㊫; 学; 学; ) CIRCLED IDEOGRAPH STUDY
+32AC;32AC;32AC;76E3;76E3; # (㊬; ㊬; ㊬; 監; 監; ) CIRCLED IDEOGRAPH SUPERVISE
+32AD;32AD;32AD;4F01;4F01; # (㊭; ㊭; ㊭; ä¼; ä¼; ) CIRCLED IDEOGRAPH ENTERPRISE
+32AE;32AE;32AE;8CC7;8CC7; # (㊮; ㊮; ㊮; 資; 資; ) CIRCLED IDEOGRAPH RESOURCE
+32AF;32AF;32AF;5354;5354; # (㊯; ㊯; ㊯; å”; å”; ) CIRCLED IDEOGRAPH ALLIANCE
+32B0;32B0;32B0;591C;591C; # (㊰; ㊰; ㊰; 夜; 夜; ) CIRCLED IDEOGRAPH NIGHT
+32B1;32B1;32B1;0033 0036;0033 0036; # (㊱; ㊱; ㊱; 36; 36; ) CIRCLED NUMBER THIRTY SIX
+32B2;32B2;32B2;0033 0037;0033 0037; # (㊲; ㊲; ㊲; 37; 37; ) CIRCLED NUMBER THIRTY SEVEN
+32B3;32B3;32B3;0033 0038;0033 0038; # (㊳; ㊳; ㊳; 38; 38; ) CIRCLED NUMBER THIRTY EIGHT
+32B4;32B4;32B4;0033 0039;0033 0039; # (㊴; ㊴; ㊴; 39; 39; ) CIRCLED NUMBER THIRTY NINE
+32B5;32B5;32B5;0034 0030;0034 0030; # (㊵; ㊵; ㊵; 40; 40; ) CIRCLED NUMBER FORTY
+32B6;32B6;32B6;0034 0031;0034 0031; # (㊶; ㊶; ㊶; 41; 41; ) CIRCLED NUMBER FORTY ONE
+32B7;32B7;32B7;0034 0032;0034 0032; # (㊷; ㊷; ㊷; 42; 42; ) CIRCLED NUMBER FORTY TWO
+32B8;32B8;32B8;0034 0033;0034 0033; # (㊸; ㊸; ㊸; 43; 43; ) CIRCLED NUMBER FORTY THREE
+32B9;32B9;32B9;0034 0034;0034 0034; # (㊹; ㊹; ㊹; 44; 44; ) CIRCLED NUMBER FORTY FOUR
+32BA;32BA;32BA;0034 0035;0034 0035; # (㊺; ㊺; ㊺; 45; 45; ) CIRCLED NUMBER FORTY FIVE
+32BB;32BB;32BB;0034 0036;0034 0036; # (㊻; ㊻; ㊻; 46; 46; ) CIRCLED NUMBER FORTY SIX
+32BC;32BC;32BC;0034 0037;0034 0037; # (㊼; ㊼; ㊼; 47; 47; ) CIRCLED NUMBER FORTY SEVEN
+32BD;32BD;32BD;0034 0038;0034 0038; # (㊽; ㊽; ㊽; 48; 48; ) CIRCLED NUMBER FORTY EIGHT
+32BE;32BE;32BE;0034 0039;0034 0039; # (㊾; ㊾; ㊾; 49; 49; ) CIRCLED NUMBER FORTY NINE
+32BF;32BF;32BF;0035 0030;0035 0030; # (㊿; ㊿; ㊿; 50; 50; ) CIRCLED NUMBER FIFTY
+32C0;32C0;32C0;0031 6708;0031 6708; # (㋀; ㋀; ㋀; 1月; 1月; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR JANUARY
+32C1;32C1;32C1;0032 6708;0032 6708; # (ã‹; ã‹; ã‹; 2月; 2月; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR FEBRUARY
+32C2;32C2;32C2;0033 6708;0033 6708; # (㋂; ㋂; ㋂; 3月; 3月; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR MARCH
+32C3;32C3;32C3;0034 6708;0034 6708; # (㋃; ㋃; ㋃; 4月; 4月; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR APRIL
+32C4;32C4;32C4;0035 6708;0035 6708; # (㋄; ㋄; ㋄; 5月; 5月; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR MAY
+32C5;32C5;32C5;0036 6708;0036 6708; # (㋅; ㋅; ㋅; 6月; 6月; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR JUNE
+32C6;32C6;32C6;0037 6708;0037 6708; # (㋆; ㋆; ㋆; 7月; 7月; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR JULY
+32C7;32C7;32C7;0038 6708;0038 6708; # (㋇; ㋇; ㋇; 8月; 8月; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR AUGUST
+32C8;32C8;32C8;0039 6708;0039 6708; # (㋈; ㋈; ㋈; 9月; 9月; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR SEPTEMBER
+32C9;32C9;32C9;0031 0030 6708;0031 0030 6708; # (㋉; ㋉; ㋉; 10月; 10月; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR OCTOBER
+32CA;32CA;32CA;0031 0031 6708;0031 0031 6708; # (㋊; ㋊; ㋊; 11月; 11月; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR NOVEMBER
+32CB;32CB;32CB;0031 0032 6708;0031 0032 6708; # (㋋; ㋋; ㋋; 12月; 12月; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DECEMBER
+32CC;32CC;32CC;0048 0067;0048 0067; # (㋌; ㋌; ㋌; Hg; Hg; ) SQUARE HG
+32CD;32CD;32CD;0065 0072 0067;0065 0072 0067; # (ã‹; ã‹; ã‹; erg; erg; ) SQUARE ERG
+32CE;32CE;32CE;0065 0056;0065 0056; # (㋎; ㋎; ㋎; eV; eV; ) SQUARE EV
+32CF;32CF;32CF;004C 0054 0044;004C 0054 0044; # (ã‹; ã‹; ã‹; LTD; LTD; ) LIMITED LIABILITY SIGN
+32D0;32D0;32D0;30A2;30A2; # (ã‹; ã‹; ã‹; ã‚¢; ã‚¢; ) CIRCLED KATAKANA A
+32D1;32D1;32D1;30A4;30A4; # (㋑; ㋑; ㋑; イ; イ; ) CIRCLED KATAKANA I
+32D2;32D2;32D2;30A6;30A6; # (㋒; ㋒; ㋒; ウ; ウ; ) CIRCLED KATAKANA U
+32D3;32D3;32D3;30A8;30A8; # (㋓; ㋓; ㋓; エ; エ; ) CIRCLED KATAKANA E
+32D4;32D4;32D4;30AA;30AA; # (㋔; ㋔; ㋔; オ; オ; ) CIRCLED KATAKANA O
+32D5;32D5;32D5;30AB;30AB; # (ã‹•; ã‹•; ã‹•; ã‚«; ã‚«; ) CIRCLED KATAKANA KA
+32D6;32D6;32D6;30AD;30AD; # (ã‹–; ã‹–; ã‹–; ã‚­; ã‚­; ) CIRCLED KATAKANA KI
+32D7;32D7;32D7;30AF;30AF; # (㋗; ㋗; ㋗; ク; ク; ) CIRCLED KATAKANA KU
+32D8;32D8;32D8;30B1;30B1; # (㋘; ㋘; ㋘; ケ; ケ; ) CIRCLED KATAKANA KE
+32D9;32D9;32D9;30B3;30B3; # (㋙; ㋙; ㋙; コ; コ; ) CIRCLED KATAKANA KO
+32DA;32DA;32DA;30B5;30B5; # (㋚; ㋚; ㋚; サ; サ; ) CIRCLED KATAKANA SA
+32DB;32DB;32DB;30B7;30B7; # (ã‹›; ã‹›; ã‹›; ã‚·; ã‚·; ) CIRCLED KATAKANA SI
+32DC;32DC;32DC;30B9;30B9; # (㋜; ㋜; ㋜; ス; ス; ) CIRCLED KATAKANA SU
+32DD;32DD;32DD;30BB;30BB; # (ã‹; ã‹; ã‹; ã‚»; ã‚»; ) CIRCLED KATAKANA SE
+32DE;32DE;32DE;30BD;30BD; # (㋞; ㋞; ㋞; ソ; ソ; ) CIRCLED KATAKANA SO
+32DF;32DF;32DF;30BF;30BF; # (㋟; ㋟; ㋟; タ; タ; ) CIRCLED KATAKANA TA
+32E0;32E0;32E0;30C1;30C1; # (ã‹ ; ã‹ ; ã‹ ; ãƒ; ãƒ; ) CIRCLED KATAKANA TI
+32E1;32E1;32E1;30C4;30C4; # (㋡; ㋡; ㋡; ツ; ツ; ) CIRCLED KATAKANA TU
+32E2;32E2;32E2;30C6;30C6; # (㋢; ㋢; ㋢; テ; テ; ) CIRCLED KATAKANA TE
+32E3;32E3;32E3;30C8;30C8; # (㋣; ㋣; ㋣; ト; ト; ) CIRCLED KATAKANA TO
+32E4;32E4;32E4;30CA;30CA; # (㋤; ㋤; ㋤; ナ; ナ; ) CIRCLED KATAKANA NA
+32E5;32E5;32E5;30CB;30CB; # (㋥; ㋥; ㋥; ニ; ニ; ) CIRCLED KATAKANA NI
+32E6;32E6;32E6;30CC;30CC; # (㋦; ㋦; ㋦; ヌ; ヌ; ) CIRCLED KATAKANA NU
+32E7;32E7;32E7;30CD;30CD; # (ã‹§; ã‹§; ã‹§; ãƒ; ãƒ; ) CIRCLED KATAKANA NE
+32E8;32E8;32E8;30CE;30CE; # (㋨; ㋨; ㋨; ノ; ノ; ) CIRCLED KATAKANA NO
+32E9;32E9;32E9;30CF;30CF; # (ã‹©; ã‹©; ã‹©; ãƒ; ãƒ; ) CIRCLED KATAKANA HA
+32EA;32EA;32EA;30D2;30D2; # (㋪; ㋪; ㋪; ヒ; ヒ; ) CIRCLED KATAKANA HI
+32EB;32EB;32EB;30D5;30D5; # (㋫; ㋫; ㋫; フ; フ; ) CIRCLED KATAKANA HU
+32EC;32EC;32EC;30D8;30D8; # (㋬; ㋬; ㋬; ヘ; ヘ; ) CIRCLED KATAKANA HE
+32ED;32ED;32ED;30DB;30DB; # (㋭; ㋭; ㋭; ホ; ホ; ) CIRCLED KATAKANA HO
+32EE;32EE;32EE;30DE;30DE; # (㋮; ㋮; ㋮; マ; マ; ) CIRCLED KATAKANA MA
+32EF;32EF;32EF;30DF;30DF; # (㋯; ㋯; ㋯; ミ; ミ; ) CIRCLED KATAKANA MI
+32F0;32F0;32F0;30E0;30E0; # (㋰; ㋰; ㋰; ム; ム; ) CIRCLED KATAKANA MU
+32F1;32F1;32F1;30E1;30E1; # (㋱; ㋱; ㋱; メ; メ; ) CIRCLED KATAKANA ME
+32F2;32F2;32F2;30E2;30E2; # (㋲; ㋲; ㋲; モ; モ; ) CIRCLED KATAKANA MO
+32F3;32F3;32F3;30E4;30E4; # (㋳; ㋳; ㋳; ヤ; ヤ; ) CIRCLED KATAKANA YA
+32F4;32F4;32F4;30E6;30E6; # (㋴; ㋴; ㋴; ユ; ユ; ) CIRCLED KATAKANA YU
+32F5;32F5;32F5;30E8;30E8; # (㋵; ㋵; ㋵; ヨ; ヨ; ) CIRCLED KATAKANA YO
+32F6;32F6;32F6;30E9;30E9; # (㋶; ㋶; ㋶; ラ; ラ; ) CIRCLED KATAKANA RA
+32F7;32F7;32F7;30EA;30EA; # (㋷; ㋷; ㋷; リ; リ; ) CIRCLED KATAKANA RI
+32F8;32F8;32F8;30EB;30EB; # (㋸; ㋸; ㋸; ル; ル; ) CIRCLED KATAKANA RU
+32F9;32F9;32F9;30EC;30EC; # (㋹; ㋹; ㋹; レ; レ; ) CIRCLED KATAKANA RE
+32FA;32FA;32FA;30ED;30ED; # (㋺; ㋺; ㋺; ロ; ロ; ) CIRCLED KATAKANA RO
+32FB;32FB;32FB;30EF;30EF; # (㋻; ㋻; ㋻; ワ; ワ; ) CIRCLED KATAKANA WA
+32FC;32FC;32FC;30F0;30F0; # (㋼; ㋼; ㋼; ヰ; ヰ; ) CIRCLED KATAKANA WI
+32FD;32FD;32FD;30F1;30F1; # (㋽; ㋽; ㋽; ヱ; ヱ; ) CIRCLED KATAKANA WE
+32FE;32FE;32FE;30F2;30F2; # (㋾; ㋾; ㋾; ヲ; ヲ; ) CIRCLED KATAKANA WO
+3300;3300;3300;30A2 30D1 30FC 30C8;30A2 30CF 309A 30FC 30C8; # (㌀; ㌀; ㌀; アパート; ã‚¢ãƒâ—Œã‚šãƒ¼ãƒˆ; ) SQUARE APAATO
+3301;3301;3301;30A2 30EB 30D5 30A1;30A2 30EB 30D5 30A1; # (ãŒ; ãŒ; ãŒ; アルファ; アルファ; ) SQUARE ARUHUA
+3302;3302;3302;30A2 30F3 30DA 30A2;30A2 30F3 30D8 309A 30A2; # (㌂; ㌂; ㌂; アンペア; アンヘ◌゚ア; ) SQUARE ANPEA
+3303;3303;3303;30A2 30FC 30EB;30A2 30FC 30EB; # (㌃; ㌃; ㌃; アール; アール; ) SQUARE AARU
+3304;3304;3304;30A4 30CB 30F3 30B0;30A4 30CB 30F3 30AF 3099; # (㌄; ㌄; ㌄; イニング; イニンク◌゙; ) SQUARE ININGU
+3305;3305;3305;30A4 30F3 30C1;30A4 30F3 30C1; # (㌅; ㌅; ㌅; インãƒ; インãƒ; ) SQUARE INTI
+3306;3306;3306;30A6 30A9 30F3;30A6 30A9 30F3; # (㌆; ㌆; ㌆; ウォン; ウォン; ) SQUARE UON
+3307;3307;3307;30A8 30B9 30AF 30FC 30C9;30A8 30B9 30AF 30FC 30C8 3099; # (㌇; ㌇; ㌇; エスクード; エスクート◌゙; ) SQUARE ESUKUUDO
+3308;3308;3308;30A8 30FC 30AB 30FC;30A8 30FC 30AB 30FC; # (㌈; ㌈; ㌈; エーカー; エーカー; ) SQUARE EEKAA
+3309;3309;3309;30AA 30F3 30B9;30AA 30F3 30B9; # (㌉; ㌉; ㌉; オンス; オンス; ) SQUARE ONSU
+330A;330A;330A;30AA 30FC 30E0;30AA 30FC 30E0; # (㌊; ㌊; ㌊; オーム; オーム; ) SQUARE OOMU
+330B;330B;330B;30AB 30A4 30EA;30AB 30A4 30EA; # (㌋; ㌋; ㌋; カイリ; カイリ; ) SQUARE KAIRI
+330C;330C;330C;30AB 30E9 30C3 30C8;30AB 30E9 30C3 30C8; # (㌌; ㌌; ㌌; カラット; カラット; ) SQUARE KARATTO
+330D;330D;330D;30AB 30ED 30EA 30FC;30AB 30ED 30EA 30FC; # (ãŒ; ãŒ; ãŒ; カロリー; カロリー; ) SQUARE KARORII
+330E;330E;330E;30AC 30ED 30F3;30AB 3099 30ED 30F3; # (㌎; ㌎; ㌎; ガロン; カ◌゙ロン; ) SQUARE GARON
+330F;330F;330F;30AC 30F3 30DE;30AB 3099 30F3 30DE; # (ãŒ; ãŒ; ãŒ; ガンマ; カ◌゙ンマ; ) SQUARE GANMA
+3310;3310;3310;30AE 30AC;30AD 3099 30AB 3099; # (ãŒ; ãŒ; ãŒ; ギガ; キ◌゙カ◌゙; ) SQUARE GIGA
+3311;3311;3311;30AE 30CB 30FC;30AD 3099 30CB 30FC; # (㌑; ㌑; ㌑; ギニー; キ◌゙ニー; ) SQUARE GINII
+3312;3312;3312;30AD 30E5 30EA 30FC;30AD 30E5 30EA 30FC; # (㌒; ㌒; ㌒; キュリー; キュリー; ) SQUARE KYURII
+3313;3313;3313;30AE 30EB 30C0 30FC;30AD 3099 30EB 30BF 3099 30FC; # (㌓; ㌓; ㌓; ギルダー; キ◌゙ルタ◌゙ー; ) SQUARE GIRUDAA
+3314;3314;3314;30AD 30ED;30AD 30ED; # (㌔; ㌔; ㌔; キロ; キロ; ) SQUARE KIRO
+3315;3315;3315;30AD 30ED 30B0 30E9 30E0;30AD 30ED 30AF 3099 30E9 30E0; # (㌕; ㌕; ㌕; キログラム; キロク◌゙ラム; ) SQUARE KIROGURAMU
+3316;3316;3316;30AD 30ED 30E1 30FC 30C8 30EB;30AD 30ED 30E1 30FC 30C8 30EB; # (㌖; ㌖; ㌖; キロメートル; キロメートル; ) SQUARE KIROMEETORU
+3317;3317;3317;30AD 30ED 30EF 30C3 30C8;30AD 30ED 30EF 30C3 30C8; # (㌗; ㌗; ㌗; キロワット; キロワット; ) SQUARE KIROWATTO
+3318;3318;3318;30B0 30E9 30E0;30AF 3099 30E9 30E0; # (㌘; ㌘; ㌘; グラム; ク◌゙ラム; ) SQUARE GURAMU
+3319;3319;3319;30B0 30E9 30E0 30C8 30F3;30AF 3099 30E9 30E0 30C8 30F3; # (㌙; ㌙; ㌙; グラムトン; ク◌゙ラムトン; ) SQUARE GURAMUTON
+331A;331A;331A;30AF 30EB 30BC 30A4 30ED;30AF 30EB 30BB 3099 30A4 30ED; # (㌚; ㌚; ㌚; クルゼイロ; クルセ◌゙イロ; ) SQUARE KURUZEIRO
+331B;331B;331B;30AF 30ED 30FC 30CD;30AF 30ED 30FC 30CD; # (㌛; ㌛; ㌛; クローãƒ; クローãƒ; ) SQUARE KUROONE
+331C;331C;331C;30B1 30FC 30B9;30B1 30FC 30B9; # (㌜; ㌜; ㌜; ケース; ケース; ) SQUARE KEESU
+331D;331D;331D;30B3 30EB 30CA;30B3 30EB 30CA; # (ãŒ; ãŒ; ãŒ; コルナ; コルナ; ) SQUARE KORUNA
+331E;331E;331E;30B3 30FC 30DD;30B3 30FC 30DB 309A; # (㌞; ㌞; ㌞; コーãƒ; コーホ◌゚; ) SQUARE KOOPO
+331F;331F;331F;30B5 30A4 30AF 30EB;30B5 30A4 30AF 30EB; # (㌟; ㌟; ㌟; サイクル; サイクル; ) SQUARE SAIKURU
+3320;3320;3320;30B5 30F3 30C1 30FC 30E0;30B5 30F3 30C1 30FC 30E0; # (㌠; ㌠; ㌠; サンãƒãƒ¼ãƒ ; サンãƒãƒ¼ãƒ ; ) SQUARE SANTIIMU
+3321;3321;3321;30B7 30EA 30F3 30B0;30B7 30EA 30F3 30AF 3099; # (㌡; ㌡; ㌡; シリング; シリンク◌゙; ) SQUARE SIRINGU
+3322;3322;3322;30BB 30F3 30C1;30BB 30F3 30C1; # (㌢; ㌢; ㌢; センãƒ; センãƒ; ) SQUARE SENTI
+3323;3323;3323;30BB 30F3 30C8;30BB 30F3 30C8; # (㌣; ㌣; ㌣; セント; セント; ) SQUARE SENTO
+3324;3324;3324;30C0 30FC 30B9;30BF 3099 30FC 30B9; # (㌤; ㌤; ㌤; ダース; タ◌゙ース; ) SQUARE DAASU
+3325;3325;3325;30C7 30B7;30C6 3099 30B7; # (㌥; ㌥; ㌥; デシ; テ◌゙シ; ) SQUARE DESI
+3326;3326;3326;30C9 30EB;30C8 3099 30EB; # (㌦; ㌦; ㌦; ドル; ト◌゙ル; ) SQUARE DORU
+3327;3327;3327;30C8 30F3;30C8 30F3; # (㌧; ㌧; ㌧; トン; トン; ) SQUARE TON
+3328;3328;3328;30CA 30CE;30CA 30CE; # (㌨; ㌨; ㌨; ナノ; ナノ; ) SQUARE NANO
+3329;3329;3329;30CE 30C3 30C8;30CE 30C3 30C8; # (㌩; ㌩; ㌩; ノット; ノット; ) SQUARE NOTTO
+332A;332A;332A;30CF 30A4 30C4;30CF 30A4 30C4; # (㌪; ㌪; ㌪; ãƒã‚¤ãƒ„; ãƒã‚¤ãƒ„; ) SQUARE HAITU
+332B;332B;332B;30D1 30FC 30BB 30F3 30C8;30CF 309A 30FC 30BB 30F3 30C8; # (㌫; ㌫; ㌫; パーセント; ãƒâ—Œã‚šãƒ¼ã‚»ãƒ³ãƒˆ; ) SQUARE PAASENTO
+332C;332C;332C;30D1 30FC 30C4;30CF 309A 30FC 30C4; # (㌬; ㌬; ㌬; パーツ; ãƒâ—Œã‚šãƒ¼ãƒ„; ) SQUARE PAATU
+332D;332D;332D;30D0 30FC 30EC 30EB;30CF 3099 30FC 30EC 30EB; # (㌭; ㌭; ㌭; ãƒãƒ¼ãƒ¬ãƒ«; ãƒâ—Œã‚™ãƒ¼ãƒ¬ãƒ«; ) SQUARE BAARERU
+332E;332E;332E;30D4 30A2 30B9 30C8 30EB;30D2 309A 30A2 30B9 30C8 30EB; # (㌮; ㌮; ㌮; ピアストル; ヒ◌゚アストル; ) SQUARE PIASUTORU
+332F;332F;332F;30D4 30AF 30EB;30D2 309A 30AF 30EB; # (㌯; ㌯; ㌯; ピクル; ヒ◌゚クル; ) SQUARE PIKURU
+3330;3330;3330;30D4 30B3;30D2 309A 30B3; # (㌰; ㌰; ㌰; ピコ; ヒ◌゚コ; ) SQUARE PIKO
+3331;3331;3331;30D3 30EB;30D2 3099 30EB; # (㌱; ㌱; ㌱; ビル; ヒ◌゙ル; ) SQUARE BIRU
+3332;3332;3332;30D5 30A1 30E9 30C3 30C9;30D5 30A1 30E9 30C3 30C8 3099; # (㌲; ㌲; ㌲; ファラッド; ファラット◌゙; ) SQUARE HUARADDO
+3333;3333;3333;30D5 30A3 30FC 30C8;30D5 30A3 30FC 30C8; # (㌳; ㌳; ㌳; フィート; フィート; ) SQUARE HUIITO
+3334;3334;3334;30D6 30C3 30B7 30A7 30EB;30D5 3099 30C3 30B7 30A7 30EB; # (㌴; ㌴; ㌴; ブッシェル; フ◌゙ッシェル; ) SQUARE BUSSYERU
+3335;3335;3335;30D5 30E9 30F3;30D5 30E9 30F3; # (㌵; ㌵; ㌵; フラン; フラン; ) SQUARE HURAN
+3336;3336;3336;30D8 30AF 30BF 30FC 30EB;30D8 30AF 30BF 30FC 30EB; # (㌶; ㌶; ㌶; ヘクタール; ヘクタール; ) SQUARE HEKUTAARU
+3337;3337;3337;30DA 30BD;30D8 309A 30BD; # (㌷; ㌷; ㌷; ペソ; ヘ◌゚ソ; ) SQUARE PESO
+3338;3338;3338;30DA 30CB 30D2;30D8 309A 30CB 30D2; # (㌸; ㌸; ㌸; ペニヒ; ヘ◌゚ニヒ; ) SQUARE PENIHI
+3339;3339;3339;30D8 30EB 30C4;30D8 30EB 30C4; # (㌹; ㌹; ㌹; ヘルツ; ヘルツ; ) SQUARE HERUTU
+333A;333A;333A;30DA 30F3 30B9;30D8 309A 30F3 30B9; # (㌺; ㌺; ㌺; ペンス; ヘ◌゚ンス; ) SQUARE PENSU
+333B;333B;333B;30DA 30FC 30B8;30D8 309A 30FC 30B7 3099; # (㌻; ㌻; ㌻; ページ; ヘ◌゚ーシ◌゙; ) SQUARE PEEZI
+333C;333C;333C;30D9 30FC 30BF;30D8 3099 30FC 30BF; # (㌼; ㌼; ㌼; ベータ; ヘ◌゙ータ; ) SQUARE BEETA
+333D;333D;333D;30DD 30A4 30F3 30C8;30DB 309A 30A4 30F3 30C8; # (㌽; ㌽; ㌽; ãƒã‚¤ãƒ³ãƒˆ; ホ◌゚イント; ) SQUARE POINTO
+333E;333E;333E;30DC 30EB 30C8;30DB 3099 30EB 30C8; # (㌾; ㌾; ㌾; ボルト; ホ◌゙ルト; ) SQUARE BORUTO
+333F;333F;333F;30DB 30F3;30DB 30F3; # (㌿; ㌿; ㌿; ホン; ホン; ) SQUARE HON
+3340;3340;3340;30DD 30F3 30C9;30DB 309A 30F3 30C8 3099; # (ã€; ã€; ã€; ãƒãƒ³ãƒ‰; ホ◌゚ント◌゙; ) SQUARE PONDO
+3341;3341;3341;30DB 30FC 30EB;30DB 30FC 30EB; # (ã; ã; ã; ホール; ホール; ) SQUARE HOORU
+3342;3342;3342;30DB 30FC 30F3;30DB 30FC 30F3; # (ã‚; ã‚; ã‚; ホーン; ホーン; ) SQUARE HOON
+3343;3343;3343;30DE 30A4 30AF 30ED;30DE 30A4 30AF 30ED; # (ãƒ; ãƒ; ãƒ; マイクロ; マイクロ; ) SQUARE MAIKURO
+3344;3344;3344;30DE 30A4 30EB;30DE 30A4 30EB; # (ã„; ã„; ã„; マイル; マイル; ) SQUARE MAIRU
+3345;3345;3345;30DE 30C3 30CF;30DE 30C3 30CF; # (ã…; ã…; ã…; マッãƒ; マッãƒ; ) SQUARE MAHHA
+3346;3346;3346;30DE 30EB 30AF;30DE 30EB 30AF; # (ã†; ã†; ã†; マルク; マルク; ) SQUARE MARUKU
+3347;3347;3347;30DE 30F3 30B7 30E7 30F3;30DE 30F3 30B7 30E7 30F3; # (ã‡; ã‡; ã‡; マンション; マンション; ) SQUARE MANSYON
+3348;3348;3348;30DF 30AF 30ED 30F3;30DF 30AF 30ED 30F3; # (ãˆ; ãˆ; ãˆ; ミクロン; ミクロン; ) SQUARE MIKURON
+3349;3349;3349;30DF 30EA;30DF 30EA; # (ã‰; ã‰; ã‰; ミリ; ミリ; ) SQUARE MIRI
+334A;334A;334A;30DF 30EA 30D0 30FC 30EB;30DF 30EA 30CF 3099 30FC 30EB; # (ãŠ; ãŠ; ãŠ; ミリãƒãƒ¼ãƒ«; ミリãƒâ—Œã‚™ãƒ¼ãƒ«; ) SQUARE MIRIBAARU
+334B;334B;334B;30E1 30AC;30E1 30AB 3099; # (ã‹; ã‹; ã‹; メガ; メカ◌゙; ) SQUARE MEGA
+334C;334C;334C;30E1 30AC 30C8 30F3;30E1 30AB 3099 30C8 30F3; # (ãŒ; ãŒ; ãŒ; メガトン; メカ◌゙トン; ) SQUARE MEGATON
+334D;334D;334D;30E1 30FC 30C8 30EB;30E1 30FC 30C8 30EB; # (ã; ã; ã; メートル; メートル; ) SQUARE MEETORU
+334E;334E;334E;30E4 30FC 30C9;30E4 30FC 30C8 3099; # (ãŽ; ãŽ; ãŽ; ヤード; ヤート◌゙; ) SQUARE YAADO
+334F;334F;334F;30E4 30FC 30EB;30E4 30FC 30EB; # (ã; ã; ã; ヤール; ヤール; ) SQUARE YAARU
+3350;3350;3350;30E6 30A2 30F3;30E6 30A2 30F3; # (ã; ã; ã; ユアン; ユアン; ) SQUARE YUAN
+3351;3351;3351;30EA 30C3 30C8 30EB;30EA 30C3 30C8 30EB; # (ã‘; ã‘; ã‘; リットル; リットル; ) SQUARE RITTORU
+3352;3352;3352;30EA 30E9;30EA 30E9; # (ã’; ã’; ã’; リラ; リラ; ) SQUARE RIRA
+3353;3353;3353;30EB 30D4 30FC;30EB 30D2 309A 30FC; # (ã“; ã“; ã“; ルピー; ルヒ◌゚ー; ) SQUARE RUPII
+3354;3354;3354;30EB 30FC 30D6 30EB;30EB 30FC 30D5 3099 30EB; # (ã”; ã”; ã”; ルーブル; ルーフ◌゙ル; ) SQUARE RUUBURU
+3355;3355;3355;30EC 30E0;30EC 30E0; # (ã•; ã•; ã•; レム; レム; ) SQUARE REMU
+3356;3356;3356;30EC 30F3 30C8 30B2 30F3;30EC 30F3 30C8 30B1 3099 30F3; # (ã–; ã–; ã–; レントゲン; レントケ◌゙ン; ) SQUARE RENTOGEN
+3357;3357;3357;30EF 30C3 30C8;30EF 30C3 30C8; # (ã—; ã—; ã—; ワット; ワット; ) SQUARE WATTO
+3358;3358;3358;0030 70B9;0030 70B9; # (ã˜; ã˜; ã˜; 0点; 0点; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR ZERO
+3359;3359;3359;0031 70B9;0031 70B9; # (ã™; ã™; ã™; 1点; 1点; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR ONE
+335A;335A;335A;0032 70B9;0032 70B9; # (ãš; ãš; ãš; 2点; 2点; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWO
+335B;335B;335B;0033 70B9;0033 70B9; # (ã›; ã›; ã›; 3点; 3点; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR THREE
+335C;335C;335C;0034 70B9;0034 70B9; # (ãœ; ãœ; ãœ; 4点; 4点; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR FOUR
+335D;335D;335D;0035 70B9;0035 70B9; # (ã; ã; ã; 5点; 5点; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR FIVE
+335E;335E;335E;0036 70B9;0036 70B9; # (ãž; ãž; ãž; 6点; 6点; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR SIX
+335F;335F;335F;0037 70B9;0037 70B9; # (ãŸ; ãŸ; ãŸ; 7点; 7点; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR SEVEN
+3360;3360;3360;0038 70B9;0038 70B9; # (ã ; ã ; ã ; 8点; 8点; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR EIGHT
+3361;3361;3361;0039 70B9;0039 70B9; # (ã¡; ã¡; ã¡; 9点; 9点; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR NINE
+3362;3362;3362;0031 0030 70B9;0031 0030 70B9; # (ã¢; ã¢; ã¢; 10点; 10点; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TEN
+3363;3363;3363;0031 0031 70B9;0031 0031 70B9; # (ã£; ã£; ã£; 11点; 11点; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR ELEVEN
+3364;3364;3364;0031 0032 70B9;0031 0032 70B9; # (ã¤; ã¤; ã¤; 12点; 12点; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWELVE
+3365;3365;3365;0031 0033 70B9;0031 0033 70B9; # (ã¥; ã¥; ã¥; 13点; 13点; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR THIRTEEN
+3366;3366;3366;0031 0034 70B9;0031 0034 70B9; # (ã¦; ã¦; ã¦; 14点; 14点; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR FOURTEEN
+3367;3367;3367;0031 0035 70B9;0031 0035 70B9; # (ã§; ã§; ã§; 15点; 15点; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR FIFTEEN
+3368;3368;3368;0031 0036 70B9;0031 0036 70B9; # (ã¨; ã¨; ã¨; 16点; 16点; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR SIXTEEN
+3369;3369;3369;0031 0037 70B9;0031 0037 70B9; # (ã©; ã©; ã©; 17点; 17点; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR SEVENTEEN
+336A;336A;336A;0031 0038 70B9;0031 0038 70B9; # (ãª; ãª; ãª; 18点; 18点; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR EIGHTEEN
+336B;336B;336B;0031 0039 70B9;0031 0039 70B9; # (ã«; ã«; ã«; 19点; 19点; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR NINETEEN
+336C;336C;336C;0032 0030 70B9;0032 0030 70B9; # (ã¬; ã¬; ã¬; 20点; 20点; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY
+336D;336D;336D;0032 0031 70B9;0032 0031 70B9; # (ã­; ã­; ã­; 21点; 21点; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY-ONE
+336E;336E;336E;0032 0032 70B9;0032 0032 70B9; # (ã®; ã®; ã®; 22点; 22点; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY-TWO
+336F;336F;336F;0032 0033 70B9;0032 0033 70B9; # (ã¯; ã¯; ã¯; 23点; 23点; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY-THREE
+3370;3370;3370;0032 0034 70B9;0032 0034 70B9; # (ã°; ã°; ã°; 24点; 24点; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY-FOUR
+3371;3371;3371;0068 0050 0061;0068 0050 0061; # (ã±; ã±; ã±; hPa; hPa; ) SQUARE HPA
+3372;3372;3372;0064 0061;0064 0061; # (ã²; ã²; ã²; da; da; ) SQUARE DA
+3373;3373;3373;0041 0055;0041 0055; # (ã³; ã³; ã³; AU; AU; ) SQUARE AU
+3374;3374;3374;0062 0061 0072;0062 0061 0072; # (ã´; ã´; ã´; bar; bar; ) SQUARE BAR
+3375;3375;3375;006F 0056;006F 0056; # (ãµ; ãµ; ãµ; oV; oV; ) SQUARE OV
+3376;3376;3376;0070 0063;0070 0063; # (ã¶; ã¶; ã¶; pc; pc; ) SQUARE PC
+3377;3377;3377;0064 006D;0064 006D; # (ã·; ã·; ã·; dm; dm; ) SQUARE DM
+3378;3378;3378;0064 006D 0032;0064 006D 0032; # (ã¸; ã¸; ã¸; dm2; dm2; ) SQUARE DM SQUARED
+3379;3379;3379;0064 006D 0033;0064 006D 0033; # (ã¹; ã¹; ã¹; dm3; dm3; ) SQUARE DM CUBED
+337A;337A;337A;0049 0055;0049 0055; # (ãº; ãº; ãº; IU; IU; ) SQUARE IU
+337B;337B;337B;5E73 6210;5E73 6210; # (ã»; ã»; ã»; å¹³æˆ; å¹³æˆ; ) SQUARE ERA NAME HEISEI
+337C;337C;337C;662D 548C;662D 548C; # (ã¼; ã¼; ã¼; 昭和; 昭和; ) SQUARE ERA NAME SYOUWA
+337D;337D;337D;5927 6B63;5927 6B63; # (ã½; ã½; ã½; 大正; 大正; ) SQUARE ERA NAME TAISYOU
+337E;337E;337E;660E 6CBB;660E 6CBB; # (ã¾; ã¾; ã¾; 明治; 明治; ) SQUARE ERA NAME MEIZI
+337F;337F;337F;682A 5F0F 4F1A 793E;682A 5F0F 4F1A 793E; # (ã¿; ã¿; ã¿; æ ªå¼ä¼šç¤¾; æ ªå¼ä¼šç¤¾; ) SQUARE CORPORATION
+3380;3380;3380;0070 0041;0070 0041; # (㎀; ㎀; ㎀; pA; pA; ) SQUARE PA AMPS
+3381;3381;3381;006E 0041;006E 0041; # (ãŽ; ãŽ; ãŽ; nA; nA; ) SQUARE NA
+3382;3382;3382;03BC 0041;03BC 0041; # (㎂; ㎂; ㎂; μA; μA; ) SQUARE MU A
+3383;3383;3383;006D 0041;006D 0041; # (㎃; ㎃; ㎃; mA; mA; ) SQUARE MA
+3384;3384;3384;006B 0041;006B 0041; # (㎄; ㎄; ㎄; kA; kA; ) SQUARE KA
+3385;3385;3385;004B 0042;004B 0042; # (㎅; ㎅; ㎅; KB; KB; ) SQUARE KB
+3386;3386;3386;004D 0042;004D 0042; # (㎆; ㎆; ㎆; MB; MB; ) SQUARE MB
+3387;3387;3387;0047 0042;0047 0042; # (㎇; ㎇; ㎇; GB; GB; ) SQUARE GB
+3388;3388;3388;0063 0061 006C;0063 0061 006C; # (㎈; ㎈; ㎈; cal; cal; ) SQUARE CAL
+3389;3389;3389;006B 0063 0061 006C;006B 0063 0061 006C; # (㎉; ㎉; ㎉; kcal; kcal; ) SQUARE KCAL
+338A;338A;338A;0070 0046;0070 0046; # (㎊; ㎊; ㎊; pF; pF; ) SQUARE PF
+338B;338B;338B;006E 0046;006E 0046; # (㎋; ㎋; ㎋; nF; nF; ) SQUARE NF
+338C;338C;338C;03BC 0046;03BC 0046; # (㎌; ㎌; ㎌; μF; μF; ) SQUARE MU F
+338D;338D;338D;03BC 0067;03BC 0067; # (ãŽ; ãŽ; ãŽ; μg; μg; ) SQUARE MU G
+338E;338E;338E;006D 0067;006D 0067; # (㎎; ㎎; ㎎; mg; mg; ) SQUARE MG
+338F;338F;338F;006B 0067;006B 0067; # (ãŽ; ãŽ; ãŽ; kg; kg; ) SQUARE KG
+3390;3390;3390;0048 007A;0048 007A; # (ãŽ; ãŽ; ãŽ; Hz; Hz; ) SQUARE HZ
+3391;3391;3391;006B 0048 007A;006B 0048 007A; # (㎑; ㎑; ㎑; kHz; kHz; ) SQUARE KHZ
+3392;3392;3392;004D 0048 007A;004D 0048 007A; # (㎒; ㎒; ㎒; MHz; MHz; ) SQUARE MHZ
+3393;3393;3393;0047 0048 007A;0047 0048 007A; # (㎓; ㎓; ㎓; GHz; GHz; ) SQUARE GHZ
+3394;3394;3394;0054 0048 007A;0054 0048 007A; # (㎔; ㎔; ㎔; THz; THz; ) SQUARE THZ
+3395;3395;3395;03BC 006C;03BC 006C; # (㎕; ㎕; ㎕; μl; μl; ) SQUARE MU L
+3396;3396;3396;006D 006C;006D 006C; # (㎖; ㎖; ㎖; ml; ml; ) SQUARE ML
+3397;3397;3397;0064 006C;0064 006C; # (㎗; ㎗; ㎗; dl; dl; ) SQUARE DL
+3398;3398;3398;006B 006C;006B 006C; # (㎘; ㎘; ㎘; kl; kl; ) SQUARE KL
+3399;3399;3399;0066 006D;0066 006D; # (㎙; ㎙; ㎙; fm; fm; ) SQUARE FM
+339A;339A;339A;006E 006D;006E 006D; # (㎚; ㎚; ㎚; nm; nm; ) SQUARE NM
+339B;339B;339B;03BC 006D;03BC 006D; # (㎛; ㎛; ㎛; μm; μm; ) SQUARE MU M
+339C;339C;339C;006D 006D;006D 006D; # (㎜; ㎜; ㎜; mm; mm; ) SQUARE MM
+339D;339D;339D;0063 006D;0063 006D; # (ãŽ; ãŽ; ãŽ; cm; cm; ) SQUARE CM
+339E;339E;339E;006B 006D;006B 006D; # (㎞; ㎞; ㎞; km; km; ) SQUARE KM
+339F;339F;339F;006D 006D 0032;006D 006D 0032; # (㎟; ㎟; ㎟; mm2; mm2; ) SQUARE MM SQUARED
+33A0;33A0;33A0;0063 006D 0032;0063 006D 0032; # (㎠; ㎠; ㎠; cm2; cm2; ) SQUARE CM SQUARED
+33A1;33A1;33A1;006D 0032;006D 0032; # (㎡; ㎡; ㎡; m2; m2; ) SQUARE M SQUARED
+33A2;33A2;33A2;006B 006D 0032;006B 006D 0032; # (㎢; ㎢; ㎢; km2; km2; ) SQUARE KM SQUARED
+33A3;33A3;33A3;006D 006D 0033;006D 006D 0033; # (㎣; ㎣; ㎣; mm3; mm3; ) SQUARE MM CUBED
+33A4;33A4;33A4;0063 006D 0033;0063 006D 0033; # (㎤; ㎤; ㎤; cm3; cm3; ) SQUARE CM CUBED
+33A5;33A5;33A5;006D 0033;006D 0033; # (㎥; ㎥; ㎥; m3; m3; ) SQUARE M CUBED
+33A6;33A6;33A6;006B 006D 0033;006B 006D 0033; # (㎦; ㎦; ㎦; km3; km3; ) SQUARE KM CUBED
+33A7;33A7;33A7;006D 2215 0073;006D 2215 0073; # (㎧; ㎧; ㎧; m∕s; m∕s; ) SQUARE M OVER S
+33A8;33A8;33A8;006D 2215 0073 0032;006D 2215 0073 0032; # (㎨; ㎨; ㎨; m∕s2; m∕s2; ) SQUARE M OVER S SQUARED
+33A9;33A9;33A9;0050 0061;0050 0061; # (㎩; ㎩; ㎩; Pa; Pa; ) SQUARE PA
+33AA;33AA;33AA;006B 0050 0061;006B 0050 0061; # (㎪; ㎪; ㎪; kPa; kPa; ) SQUARE KPA
+33AB;33AB;33AB;004D 0050 0061;004D 0050 0061; # (㎫; ㎫; ㎫; MPa; MPa; ) SQUARE MPA
+33AC;33AC;33AC;0047 0050 0061;0047 0050 0061; # (㎬; ㎬; ㎬; GPa; GPa; ) SQUARE GPA
+33AD;33AD;33AD;0072 0061 0064;0072 0061 0064; # (㎭; ㎭; ㎭; rad; rad; ) SQUARE RAD
+33AE;33AE;33AE;0072 0061 0064 2215 0073;0072 0061 0064 2215 0073; # (㎮; ㎮; ㎮; rad∕s; rad∕s; ) SQUARE RAD OVER S
+33AF;33AF;33AF;0072 0061 0064 2215 0073 0032;0072 0061 0064 2215 0073 0032; # (㎯; ㎯; ㎯; rad∕s2; rad∕s2; ) SQUARE RAD OVER S SQUARED
+33B0;33B0;33B0;0070 0073;0070 0073; # (㎰; ㎰; ㎰; ps; ps; ) SQUARE PS
+33B1;33B1;33B1;006E 0073;006E 0073; # (㎱; ㎱; ㎱; ns; ns; ) SQUARE NS
+33B2;33B2;33B2;03BC 0073;03BC 0073; # (㎲; ㎲; ㎲; μs; μs; ) SQUARE MU S
+33B3;33B3;33B3;006D 0073;006D 0073; # (㎳; ㎳; ㎳; ms; ms; ) SQUARE MS
+33B4;33B4;33B4;0070 0056;0070 0056; # (㎴; ㎴; ㎴; pV; pV; ) SQUARE PV
+33B5;33B5;33B5;006E 0056;006E 0056; # (㎵; ㎵; ㎵; nV; nV; ) SQUARE NV
+33B6;33B6;33B6;03BC 0056;03BC 0056; # (㎶; ㎶; ㎶; μV; μV; ) SQUARE MU V
+33B7;33B7;33B7;006D 0056;006D 0056; # (㎷; ㎷; ㎷; mV; mV; ) SQUARE MV
+33B8;33B8;33B8;006B 0056;006B 0056; # (㎸; ㎸; ㎸; kV; kV; ) SQUARE KV
+33B9;33B9;33B9;004D 0056;004D 0056; # (㎹; ㎹; ㎹; MV; MV; ) SQUARE MV MEGA
+33BA;33BA;33BA;0070 0057;0070 0057; # (㎺; ㎺; ㎺; pW; pW; ) SQUARE PW
+33BB;33BB;33BB;006E 0057;006E 0057; # (㎻; ㎻; ㎻; nW; nW; ) SQUARE NW
+33BC;33BC;33BC;03BC 0057;03BC 0057; # (㎼; ㎼; ㎼; μW; μW; ) SQUARE MU W
+33BD;33BD;33BD;006D 0057;006D 0057; # (㎽; ㎽; ㎽; mW; mW; ) SQUARE MW
+33BE;33BE;33BE;006B 0057;006B 0057; # (㎾; ㎾; ㎾; kW; kW; ) SQUARE KW
+33BF;33BF;33BF;004D 0057;004D 0057; # (㎿; ㎿; ㎿; MW; MW; ) SQUARE MW MEGA
+33C0;33C0;33C0;006B 03A9;006B 03A9; # (ã€; ã€; ã€; kΩ; kΩ; ) SQUARE K OHM
+33C1;33C1;33C1;004D 03A9;004D 03A9; # (ã; ã; ã; MΩ; MΩ; ) SQUARE M OHM
+33C2;33C2;33C2;0061 002E 006D 002E;0061 002E 006D 002E; # (ã‚; ã‚; ã‚; a.m.; a.m.; ) SQUARE AM
+33C3;33C3;33C3;0042 0071;0042 0071; # (ãƒ; ãƒ; ãƒ; Bq; Bq; ) SQUARE BQ
+33C4;33C4;33C4;0063 0063;0063 0063; # (ã„; ã„; ã„; cc; cc; ) SQUARE CC
+33C5;33C5;33C5;0063 0064;0063 0064; # (ã…; ã…; ã…; cd; cd; ) SQUARE CD
+33C6;33C6;33C6;0043 2215 006B 0067;0043 2215 006B 0067; # (ã†; ã†; ã†; C∕kg; C∕kg; ) SQUARE C OVER KG
+33C7;33C7;33C7;0043 006F 002E;0043 006F 002E; # (ã‡; ã‡; ã‡; Co.; Co.; ) SQUARE CO
+33C8;33C8;33C8;0064 0042;0064 0042; # (ãˆ; ãˆ; ãˆ; dB; dB; ) SQUARE DB
+33C9;33C9;33C9;0047 0079;0047 0079; # (ã‰; ã‰; ã‰; Gy; Gy; ) SQUARE GY
+33CA;33CA;33CA;0068 0061;0068 0061; # (ãŠ; ãŠ; ãŠ; ha; ha; ) SQUARE HA
+33CB;33CB;33CB;0048 0050;0048 0050; # (ã‹; ã‹; ã‹; HP; HP; ) SQUARE HP
+33CC;33CC;33CC;0069 006E;0069 006E; # (ãŒ; ãŒ; ãŒ; in; in; ) SQUARE IN
+33CD;33CD;33CD;004B 004B;004B 004B; # (ã; ã; ã; KK; KK; ) SQUARE KK
+33CE;33CE;33CE;004B 004D;004B 004D; # (ãŽ; ãŽ; ãŽ; KM; KM; ) SQUARE KM CAPITAL
+33CF;33CF;33CF;006B 0074;006B 0074; # (ã; ã; ã; kt; kt; ) SQUARE KT
+33D0;33D0;33D0;006C 006D;006C 006D; # (ã; ã; ã; lm; lm; ) SQUARE LM
+33D1;33D1;33D1;006C 006E;006C 006E; # (ã‘; ã‘; ã‘; ln; ln; ) SQUARE LN
+33D2;33D2;33D2;006C 006F 0067;006C 006F 0067; # (ã’; ã’; ã’; log; log; ) SQUARE LOG
+33D3;33D3;33D3;006C 0078;006C 0078; # (ã“; ã“; ã“; lx; lx; ) SQUARE LX
+33D4;33D4;33D4;006D 0062;006D 0062; # (ã”; ã”; ã”; mb; mb; ) SQUARE MB SMALL
+33D5;33D5;33D5;006D 0069 006C;006D 0069 006C; # (ã•; ã•; ã•; mil; mil; ) SQUARE MIL
+33D6;33D6;33D6;006D 006F 006C;006D 006F 006C; # (ã–; ã–; ã–; mol; mol; ) SQUARE MOL
+33D7;33D7;33D7;0050 0048;0050 0048; # (ã—; ã—; ã—; PH; PH; ) SQUARE PH
+33D8;33D8;33D8;0070 002E 006D 002E;0070 002E 006D 002E; # (ã˜; ã˜; ã˜; p.m.; p.m.; ) SQUARE PM
+33D9;33D9;33D9;0050 0050 004D;0050 0050 004D; # (ã™; ã™; ã™; PPM; PPM; ) SQUARE PPM
+33DA;33DA;33DA;0050 0052;0050 0052; # (ãš; ãš; ãš; PR; PR; ) SQUARE PR
+33DB;33DB;33DB;0073 0072;0073 0072; # (ã›; ã›; ã›; sr; sr; ) SQUARE SR
+33DC;33DC;33DC;0053 0076;0053 0076; # (ãœ; ãœ; ãœ; Sv; Sv; ) SQUARE SV
+33DD;33DD;33DD;0057 0062;0057 0062; # (ã; ã; ã; Wb; Wb; ) SQUARE WB
+33DE;33DE;33DE;0056 2215 006D;0056 2215 006D; # (ãž; ãž; ãž; V∕m; V∕m; ) SQUARE V OVER M
+33DF;33DF;33DF;0041 2215 006D;0041 2215 006D; # (ãŸ; ãŸ; ãŸ; A∕m; A∕m; ) SQUARE A OVER M
+33E0;33E0;33E0;0031 65E5;0031 65E5; # (ã ; ã ; ã ; 1æ—¥; 1æ—¥; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY ONE
+33E1;33E1;33E1;0032 65E5;0032 65E5; # (ã¡; ã¡; ã¡; 2æ—¥; 2æ—¥; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWO
+33E2;33E2;33E2;0033 65E5;0033 65E5; # (ã¢; ã¢; ã¢; 3æ—¥; 3æ—¥; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY THREE
+33E3;33E3;33E3;0034 65E5;0034 65E5; # (ã£; ã£; ã£; 4æ—¥; 4æ—¥; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FOUR
+33E4;33E4;33E4;0035 65E5;0035 65E5; # (ã¤; ã¤; ã¤; 5æ—¥; 5æ—¥; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FIVE
+33E5;33E5;33E5;0036 65E5;0036 65E5; # (ã¥; ã¥; ã¥; 6æ—¥; 6æ—¥; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SIX
+33E6;33E6;33E6;0037 65E5;0037 65E5; # (ã¦; ã¦; ã¦; 7æ—¥; 7æ—¥; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SEVEN
+33E7;33E7;33E7;0038 65E5;0038 65E5; # (ã§; ã§; ã§; 8æ—¥; 8æ—¥; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY EIGHT
+33E8;33E8;33E8;0039 65E5;0039 65E5; # (ã¨; ã¨; ã¨; 9æ—¥; 9æ—¥; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY NINE
+33E9;33E9;33E9;0031 0030 65E5;0031 0030 65E5; # (ã©; ã©; ã©; 10æ—¥; 10æ—¥; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TEN
+33EA;33EA;33EA;0031 0031 65E5;0031 0031 65E5; # (ãª; ãª; ãª; 11æ—¥; 11æ—¥; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY ELEVEN
+33EB;33EB;33EB;0031 0032 65E5;0031 0032 65E5; # (ã«; ã«; ã«; 12æ—¥; 12æ—¥; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWELVE
+33EC;33EC;33EC;0031 0033 65E5;0031 0033 65E5; # (ã¬; ã¬; ã¬; 13æ—¥; 13æ—¥; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY THIRTEEN
+33ED;33ED;33ED;0031 0034 65E5;0031 0034 65E5; # (ã­; ã­; ã­; 14æ—¥; 14æ—¥; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FOURTEEN
+33EE;33EE;33EE;0031 0035 65E5;0031 0035 65E5; # (ã®; ã®; ã®; 15æ—¥; 15æ—¥; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FIFTEEN
+33EF;33EF;33EF;0031 0036 65E5;0031 0036 65E5; # (ã¯; ã¯; ã¯; 16æ—¥; 16æ—¥; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SIXTEEN
+33F0;33F0;33F0;0031 0037 65E5;0031 0037 65E5; # (ã°; ã°; ã°; 17æ—¥; 17æ—¥; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SEVENTEEN
+33F1;33F1;33F1;0031 0038 65E5;0031 0038 65E5; # (ã±; ã±; ã±; 18æ—¥; 18æ—¥; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY EIGHTEEN
+33F2;33F2;33F2;0031 0039 65E5;0031 0039 65E5; # (ã²; ã²; ã²; 19æ—¥; 19æ—¥; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY NINETEEN
+33F3;33F3;33F3;0032 0030 65E5;0032 0030 65E5; # (ã³; ã³; ã³; 20æ—¥; 20æ—¥; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY
+33F4;33F4;33F4;0032 0031 65E5;0032 0031 65E5; # (ã´; ã´; ã´; 21æ—¥; 21æ—¥; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-ONE
+33F5;33F5;33F5;0032 0032 65E5;0032 0032 65E5; # (ãµ; ãµ; ãµ; 22æ—¥; 22æ—¥; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-TWO
+33F6;33F6;33F6;0032 0033 65E5;0032 0033 65E5; # (ã¶; ã¶; ã¶; 23æ—¥; 23æ—¥; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-THREE
+33F7;33F7;33F7;0032 0034 65E5;0032 0034 65E5; # (ã·; ã·; ã·; 24æ—¥; 24æ—¥; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-FOUR
+33F8;33F8;33F8;0032 0035 65E5;0032 0035 65E5; # (ã¸; ã¸; ã¸; 25æ—¥; 25æ—¥; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-FIVE
+33F9;33F9;33F9;0032 0036 65E5;0032 0036 65E5; # (ã¹; ã¹; ã¹; 26æ—¥; 26æ—¥; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-SIX
+33FA;33FA;33FA;0032 0037 65E5;0032 0037 65E5; # (ãº; ãº; ãº; 27æ—¥; 27æ—¥; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-SEVEN
+33FB;33FB;33FB;0032 0038 65E5;0032 0038 65E5; # (ã»; ã»; ã»; 28æ—¥; 28æ—¥; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-EIGHT
+33FC;33FC;33FC;0032 0039 65E5;0032 0039 65E5; # (ã¼; ã¼; ã¼; 29æ—¥; 29æ—¥; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-NINE
+33FD;33FD;33FD;0033 0030 65E5;0033 0030 65E5; # (ã½; ã½; ã½; 30æ—¥; 30æ—¥; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY THIRTY
+33FE;33FE;33FE;0033 0031 65E5;0033 0031 65E5; # (ã¾; ã¾; ã¾; 31æ—¥; 31æ—¥; ) IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY THIRTY-ONE
+33FF;33FF;33FF;0067 0061 006C;0067 0061 006C; # (ã¿; ã¿; ã¿; gal; gal; ) SQUARE GAL
+AC00;AC00;1100 1161;AC00;1100 1161; # (가; 가; 가; 가; 가; ) HANGUL SYLLABLE GA
+AC01;AC01;1100 1161 11A8;AC01;1100 1161 11A8; # (ê°; ê°; 각; ê°; 각; ) HANGUL SYLLABLE GAG
+AC02;AC02;1100 1161 11A9;AC02;1100 1161 11A9; # (갂; 갂; 갂; 갂; 갂; ) HANGUL SYLLABLE GAGG
+AC03;AC03;1100 1161 11AA;AC03;1100 1161 11AA; # (갃; 갃; 갃; 갃; 갃; ) HANGUL SYLLABLE GAGS
+AC04;AC04;1100 1161 11AB;AC04;1100 1161 11AB; # (간; 간; 간; 간; 간; ) HANGUL SYLLABLE GAN
+AC05;AC05;1100 1161 11AC;AC05;1100 1161 11AC; # (갅; 갅; 갅; 갅; 갅; ) HANGUL SYLLABLE GANJ
+AC06;AC06;1100 1161 11AD;AC06;1100 1161 11AD; # (갆; 갆; 갆; 갆; 갆; ) HANGUL SYLLABLE GANH
+AC07;AC07;1100 1161 11AE;AC07;1100 1161 11AE; # (갇; 갇; 갇; 갇; 갇; ) HANGUL SYLLABLE GAD
+AC08;AC08;1100 1161 11AF;AC08;1100 1161 11AF; # (갈; 갈; 갈; 갈; 갈; ) HANGUL SYLLABLE GAL
+AC09;AC09;1100 1161 11B0;AC09;1100 1161 11B0; # (갉; 갉; 갉; 갉; 갉; ) HANGUL SYLLABLE GALG
+AC0A;AC0A;1100 1161 11B1;AC0A;1100 1161 11B1; # (갊; 갊; 갊; 갊; 갊; ) HANGUL SYLLABLE GALM
+AC0B;AC0B;1100 1161 11B2;AC0B;1100 1161 11B2; # (갋; 갋; 갋; 갋; 갋; ) HANGUL SYLLABLE GALB
+AC0C;AC0C;1100 1161 11B3;AC0C;1100 1161 11B3; # (갌; 갌; 갌; 갌; 갌; ) HANGUL SYLLABLE GALS
+AC0D;AC0D;1100 1161 11B4;AC0D;1100 1161 11B4; # (ê°; ê°; 갍; ê°; 갍; ) HANGUL SYLLABLE GALT
+AC0E;AC0E;1100 1161 11B5;AC0E;1100 1161 11B5; # (갎; 갎; 갎; 갎; 갎; ) HANGUL SYLLABLE GALP
+AC0F;AC0F;1100 1161 11B6;AC0F;1100 1161 11B6; # (ê°; ê°; 갏; ê°; 갏; ) HANGUL SYLLABLE GALH
+AC10;AC10;1100 1161 11B7;AC10;1100 1161 11B7; # (ê°; ê°; 감; ê°; 감; ) HANGUL SYLLABLE GAM
+AC11;AC11;1100 1161 11B8;AC11;1100 1161 11B8; # (갑; 갑; 갑; 갑; 갑; ) HANGUL SYLLABLE GAB
+AC12;AC12;1100 1161 11B9;AC12;1100 1161 11B9; # (값; 값; 값; 값; 값; ) HANGUL SYLLABLE GABS
+AC13;AC13;1100 1161 11BA;AC13;1100 1161 11BA; # (갓; 갓; 갓; 갓; 갓; ) HANGUL SYLLABLE GAS
+AC14;AC14;1100 1161 11BB;AC14;1100 1161 11BB; # (갔; 갔; 갔; 갔; 갔; ) HANGUL SYLLABLE GASS
+AC15;AC15;1100 1161 11BC;AC15;1100 1161 11BC; # (강; 강; 강; 강; 강; ) HANGUL SYLLABLE GANG
+AC16;AC16;1100 1161 11BD;AC16;1100 1161 11BD; # (갖; 갖; 갖; 갖; 갖; ) HANGUL SYLLABLE GAJ
+AC17;AC17;1100 1161 11BE;AC17;1100 1161 11BE; # (갗; 갗; 갗; 갗; 갗; ) HANGUL SYLLABLE GAC
+AC18;AC18;1100 1161 11BF;AC18;1100 1161 11BF; # (갘; 갘; 갘; 갘; 갘; ) HANGUL SYLLABLE GAK
+AC19;AC19;1100 1161 11C0;AC19;1100 1161 11C0; # (같; 같; 같; 같; 같; ) HANGUL SYLLABLE GAT
+AC1A;AC1A;1100 1161 11C1;AC1A;1100 1161 11C1; # (ê°š; ê°š; 가á‡; ê°š; 가á‡; ) HANGUL SYLLABLE GAP
+AC1B;AC1B;1100 1161 11C2;AC1B;1100 1161 11C2; # (갛; 갛; 갛; 갛; 갛; ) HANGUL SYLLABLE GAH
+AC1C;AC1C;1100 1162;AC1C;1100 1162; # (개; 개; 개; 개; 개; ) HANGUL SYLLABLE GAE
+AC1D;AC1D;1100 1162 11A8;AC1D;1100 1162 11A8; # (ê°; ê°; 객; ê°; 객; ) HANGUL SYLLABLE GAEG
+AC1E;AC1E;1100 1162 11A9;AC1E;1100 1162 11A9; # (갞; 갞; 갞; 갞; 갞; ) HANGUL SYLLABLE GAEGG
+AC1F;AC1F;1100 1162 11AA;AC1F;1100 1162 11AA; # (갟; 갟; 갟; 갟; 갟; ) HANGUL SYLLABLE GAEGS
+AC20;AC20;1100 1162 11AB;AC20;1100 1162 11AB; # (갠; 갠; 갠; 갠; 갠; ) HANGUL SYLLABLE GAEN
+AC21;AC21;1100 1162 11AC;AC21;1100 1162 11AC; # (갡; 갡; 갡; 갡; 갡; ) HANGUL SYLLABLE GAENJ
+AC22;AC22;1100 1162 11AD;AC22;1100 1162 11AD; # (갢; 갢; 갢; 갢; 갢; ) HANGUL SYLLABLE GAENH
+AC23;AC23;1100 1162 11AE;AC23;1100 1162 11AE; # (갣; 갣; 갣; 갣; 갣; ) HANGUL SYLLABLE GAED
+AC24;AC24;1100 1162 11AF;AC24;1100 1162 11AF; # (갤; 갤; 갤; 갤; 갤; ) HANGUL SYLLABLE GAEL
+AC25;AC25;1100 1162 11B0;AC25;1100 1162 11B0; # (갥; 갥; 갥; 갥; 갥; ) HANGUL SYLLABLE GAELG
+AC26;AC26;1100 1162 11B1;AC26;1100 1162 11B1; # (갦; 갦; 갦; 갦; 갦; ) HANGUL SYLLABLE GAELM
+AC27;AC27;1100 1162 11B2;AC27;1100 1162 11B2; # (갧; 갧; 갧; 갧; 갧; ) HANGUL SYLLABLE GAELB
+AC28;AC28;1100 1162 11B3;AC28;1100 1162 11B3; # (갨; 갨; 갨; 갨; 갨; ) HANGUL SYLLABLE GAELS
+AC29;AC29;1100 1162 11B4;AC29;1100 1162 11B4; # (갩; 갩; 갩; 갩; 갩; ) HANGUL SYLLABLE GAELT
+AC2A;AC2A;1100 1162 11B5;AC2A;1100 1162 11B5; # (갪; 갪; 갪; 갪; 갪; ) HANGUL SYLLABLE GAELP
+AC2B;AC2B;1100 1162 11B6;AC2B;1100 1162 11B6; # (갫; 갫; 갫; 갫; 갫; ) HANGUL SYLLABLE GAELH
+AC2C;AC2C;1100 1162 11B7;AC2C;1100 1162 11B7; # (갬; 갬; 갬; 갬; 갬; ) HANGUL SYLLABLE GAEM
+AC2D;AC2D;1100 1162 11B8;AC2D;1100 1162 11B8; # (갭; 갭; 갭; 갭; 갭; ) HANGUL SYLLABLE GAEB
+AC2E;AC2E;1100 1162 11B9;AC2E;1100 1162 11B9; # (갮; 갮; 갮; 갮; 갮; ) HANGUL SYLLABLE GAEBS
+AC2F;AC2F;1100 1162 11BA;AC2F;1100 1162 11BA; # (갯; 갯; 갯; 갯; 갯; ) HANGUL SYLLABLE GAES
+AC30;AC30;1100 1162 11BB;AC30;1100 1162 11BB; # (갰; 갰; 갰; 갰; 갰; ) HANGUL SYLLABLE GAESS
+AC31;AC31;1100 1162 11BC;AC31;1100 1162 11BC; # (갱; 갱; 갱; 갱; 갱; ) HANGUL SYLLABLE GAENG
+AC32;AC32;1100 1162 11BD;AC32;1100 1162 11BD; # (갲; 갲; 갲; 갲; 갲; ) HANGUL SYLLABLE GAEJ
+AC33;AC33;1100 1162 11BE;AC33;1100 1162 11BE; # (갳; 갳; 갳; 갳; 갳; ) HANGUL SYLLABLE GAEC
+AC34;AC34;1100 1162 11BF;AC34;1100 1162 11BF; # (갴; 갴; 갴; 갴; 갴; ) HANGUL SYLLABLE GAEK
+AC35;AC35;1100 1162 11C0;AC35;1100 1162 11C0; # (갵; 갵; 갵; 갵; 갵; ) HANGUL SYLLABLE GAET
+AC36;AC36;1100 1162 11C1;AC36;1100 1162 11C1; # (ê°¶; ê°¶; 개á‡; ê°¶; 개á‡; ) HANGUL SYLLABLE GAEP
+AC37;AC37;1100 1162 11C2;AC37;1100 1162 11C2; # (갷; 갷; 갷; 갷; 갷; ) HANGUL SYLLABLE GAEH
+AC38;AC38;1100 1163;AC38;1100 1163; # (갸; 갸; 갸; 갸; 갸; ) HANGUL SYLLABLE GYA
+AC39;AC39;1100 1163 11A8;AC39;1100 1163 11A8; # (갹; 갹; 갹; 갹; 갹; ) HANGUL SYLLABLE GYAG
+AC3A;AC3A;1100 1163 11A9;AC3A;1100 1163 11A9; # (갺; 갺; 갺; 갺; 갺; ) HANGUL SYLLABLE GYAGG
+AC3B;AC3B;1100 1163 11AA;AC3B;1100 1163 11AA; # (갻; 갻; 갻; 갻; 갻; ) HANGUL SYLLABLE GYAGS
+AC3C;AC3C;1100 1163 11AB;AC3C;1100 1163 11AB; # (갼; 갼; 갼; 갼; 갼; ) HANGUL SYLLABLE GYAN
+AC3D;AC3D;1100 1163 11AC;AC3D;1100 1163 11AC; # (갽; 갽; 갽; 갽; 갽; ) HANGUL SYLLABLE GYANJ
+AC3E;AC3E;1100 1163 11AD;AC3E;1100 1163 11AD; # (갾; 갾; 갾; 갾; 갾; ) HANGUL SYLLABLE GYANH
+AC3F;AC3F;1100 1163 11AE;AC3F;1100 1163 11AE; # (갿; 갿; 갿; 갿; 갿; ) HANGUL SYLLABLE GYAD
+AC40;AC40;1100 1163 11AF;AC40;1100 1163 11AF; # (걀; 걀; 걀; 걀; 걀; ) HANGUL SYLLABLE GYAL
+AC41;AC41;1100 1163 11B0;AC41;1100 1163 11B0; # (ê±; ê±; 걁; ê±; 걁; ) HANGUL SYLLABLE GYALG
+AC42;AC42;1100 1163 11B1;AC42;1100 1163 11B1; # (걂; 걂; 걂; 걂; 걂; ) HANGUL SYLLABLE GYALM
+AC43;AC43;1100 1163 11B2;AC43;1100 1163 11B2; # (걃; 걃; 걃; 걃; 걃; ) HANGUL SYLLABLE GYALB
+AC44;AC44;1100 1163 11B3;AC44;1100 1163 11B3; # (걄; 걄; 걄; 걄; 걄; ) HANGUL SYLLABLE GYALS
+AC45;AC45;1100 1163 11B4;AC45;1100 1163 11B4; # (걅; 걅; 걅; 걅; 걅; ) HANGUL SYLLABLE GYALT
+AC46;AC46;1100 1163 11B5;AC46;1100 1163 11B5; # (걆; 걆; 걆; 걆; 걆; ) HANGUL SYLLABLE GYALP
+AC47;AC47;1100 1163 11B6;AC47;1100 1163 11B6; # (걇; 걇; 걇; 걇; 걇; ) HANGUL SYLLABLE GYALH
+AC48;AC48;1100 1163 11B7;AC48;1100 1163 11B7; # (걈; 걈; 걈; 걈; 걈; ) HANGUL SYLLABLE GYAM
+AC49;AC49;1100 1163 11B8;AC49;1100 1163 11B8; # (걉; 걉; 걉; 걉; 걉; ) HANGUL SYLLABLE GYAB
+AC4A;AC4A;1100 1163 11B9;AC4A;1100 1163 11B9; # (걊; 걊; 걊; 걊; 걊; ) HANGUL SYLLABLE GYABS
+AC4B;AC4B;1100 1163 11BA;AC4B;1100 1163 11BA; # (걋; 걋; 걋; 걋; 걋; ) HANGUL SYLLABLE GYAS
+AC4C;AC4C;1100 1163 11BB;AC4C;1100 1163 11BB; # (걌; 걌; 걌; 걌; 걌; ) HANGUL SYLLABLE GYASS
+AC4D;AC4D;1100 1163 11BC;AC4D;1100 1163 11BC; # (ê±; ê±; 걍; ê±; 걍; ) HANGUL SYLLABLE GYANG
+AC4E;AC4E;1100 1163 11BD;AC4E;1100 1163 11BD; # (걎; 걎; 걎; 걎; 걎; ) HANGUL SYLLABLE GYAJ
+AC4F;AC4F;1100 1163 11BE;AC4F;1100 1163 11BE; # (ê±; ê±; 걏; ê±; 걏; ) HANGUL SYLLABLE GYAC
+AC50;AC50;1100 1163 11BF;AC50;1100 1163 11BF; # (ê±; ê±; 걐; ê±; 걐; ) HANGUL SYLLABLE GYAK
+AC51;AC51;1100 1163 11C0;AC51;1100 1163 11C0; # (걑; 걑; 걑; 걑; 걑; ) HANGUL SYLLABLE GYAT
+AC52;AC52;1100 1163 11C1;AC52;1100 1163 11C1; # (ê±’; ê±’; 갸á‡; ê±’; 갸á‡; ) HANGUL SYLLABLE GYAP
+AC53;AC53;1100 1163 11C2;AC53;1100 1163 11C2; # (걓; 걓; 걓; 걓; 걓; ) HANGUL SYLLABLE GYAH
+AC54;AC54;1100 1164;AC54;1100 1164; # (걔; 걔; 걔; 걔; 걔; ) HANGUL SYLLABLE GYAE
+AC55;AC55;1100 1164 11A8;AC55;1100 1164 11A8; # (걕; 걕; 걕; 걕; 걕; ) HANGUL SYLLABLE GYAEG
+AC56;AC56;1100 1164 11A9;AC56;1100 1164 11A9; # (걖; 걖; 걖; 걖; 걖; ) HANGUL SYLLABLE GYAEGG
+AC57;AC57;1100 1164 11AA;AC57;1100 1164 11AA; # (걗; 걗; 걗; 걗; 걗; ) HANGUL SYLLABLE GYAEGS
+AC58;AC58;1100 1164 11AB;AC58;1100 1164 11AB; # (걘; 걘; 걘; 걘; 걘; ) HANGUL SYLLABLE GYAEN
+AC59;AC59;1100 1164 11AC;AC59;1100 1164 11AC; # (걙; 걙; 걙; 걙; 걙; ) HANGUL SYLLABLE GYAENJ
+AC5A;AC5A;1100 1164 11AD;AC5A;1100 1164 11AD; # (걚; 걚; 걚; 걚; 걚; ) HANGUL SYLLABLE GYAENH
+AC5B;AC5B;1100 1164 11AE;AC5B;1100 1164 11AE; # (걛; 걛; 걛; 걛; 걛; ) HANGUL SYLLABLE GYAED
+AC5C;AC5C;1100 1164 11AF;AC5C;1100 1164 11AF; # (걜; 걜; 걜; 걜; 걜; ) HANGUL SYLLABLE GYAEL
+AC5D;AC5D;1100 1164 11B0;AC5D;1100 1164 11B0; # (ê±; ê±; 걝; ê±; 걝; ) HANGUL SYLLABLE GYAELG
+AC5E;AC5E;1100 1164 11B1;AC5E;1100 1164 11B1; # (걞; 걞; 걞; 걞; 걞; ) HANGUL SYLLABLE GYAELM
+AC5F;AC5F;1100 1164 11B2;AC5F;1100 1164 11B2; # (걟; 걟; 걟; 걟; 걟; ) HANGUL SYLLABLE GYAELB
+AC60;AC60;1100 1164 11B3;AC60;1100 1164 11B3; # (걠; 걠; 걠; 걠; 걠; ) HANGUL SYLLABLE GYAELS
+AC61;AC61;1100 1164 11B4;AC61;1100 1164 11B4; # (걡; 걡; 걡; 걡; 걡; ) HANGUL SYLLABLE GYAELT
+AC62;AC62;1100 1164 11B5;AC62;1100 1164 11B5; # (걢; 걢; 걢; 걢; 걢; ) HANGUL SYLLABLE GYAELP
+AC63;AC63;1100 1164 11B6;AC63;1100 1164 11B6; # (걣; 걣; 걣; 걣; 걣; ) HANGUL SYLLABLE GYAELH
+AC64;AC64;1100 1164 11B7;AC64;1100 1164 11B7; # (걤; 걤; 걤; 걤; 걤; ) HANGUL SYLLABLE GYAEM
+AC65;AC65;1100 1164 11B8;AC65;1100 1164 11B8; # (걥; 걥; 걥; 걥; 걥; ) HANGUL SYLLABLE GYAEB
+AC66;AC66;1100 1164 11B9;AC66;1100 1164 11B9; # (걦; 걦; 걦; 걦; 걦; ) HANGUL SYLLABLE GYAEBS
+AC67;AC67;1100 1164 11BA;AC67;1100 1164 11BA; # (걧; 걧; 걧; 걧; 걧; ) HANGUL SYLLABLE GYAES
+AC68;AC68;1100 1164 11BB;AC68;1100 1164 11BB; # (걨; 걨; 걨; 걨; 걨; ) HANGUL SYLLABLE GYAESS
+AC69;AC69;1100 1164 11BC;AC69;1100 1164 11BC; # (걩; 걩; 걩; 걩; 걩; ) HANGUL SYLLABLE GYAENG
+AC6A;AC6A;1100 1164 11BD;AC6A;1100 1164 11BD; # (걪; 걪; 걪; 걪; 걪; ) HANGUL SYLLABLE GYAEJ
+AC6B;AC6B;1100 1164 11BE;AC6B;1100 1164 11BE; # (걫; 걫; 걫; 걫; 걫; ) HANGUL SYLLABLE GYAEC
+AC6C;AC6C;1100 1164 11BF;AC6C;1100 1164 11BF; # (걬; 걬; 걬; 걬; 걬; ) HANGUL SYLLABLE GYAEK
+AC6D;AC6D;1100 1164 11C0;AC6D;1100 1164 11C0; # (걭; 걭; 걭; 걭; 걭; ) HANGUL SYLLABLE GYAET
+AC6E;AC6E;1100 1164 11C1;AC6E;1100 1164 11C1; # (ê±®; ê±®; 걔á‡; ê±®; 걔á‡; ) HANGUL SYLLABLE GYAEP
+AC6F;AC6F;1100 1164 11C2;AC6F;1100 1164 11C2; # (걯; 걯; 걯; 걯; 걯; ) HANGUL SYLLABLE GYAEH
+AC70;AC70;1100 1165;AC70;1100 1165; # (거; 거; 거; 거; 거; ) HANGUL SYLLABLE GEO
+AC71;AC71;1100 1165 11A8;AC71;1100 1165 11A8; # (걱; 걱; 걱; 걱; 걱; ) HANGUL SYLLABLE GEOG
+AC72;AC72;1100 1165 11A9;AC72;1100 1165 11A9; # (걲; 걲; 걲; 걲; 걲; ) HANGUL SYLLABLE GEOGG
+AC73;AC73;1100 1165 11AA;AC73;1100 1165 11AA; # (걳; 걳; 걳; 걳; 걳; ) HANGUL SYLLABLE GEOGS
+AC74;AC74;1100 1165 11AB;AC74;1100 1165 11AB; # (건; 건; 건; 건; 건; ) HANGUL SYLLABLE GEON
+AC75;AC75;1100 1165 11AC;AC75;1100 1165 11AC; # (걵; 걵; 걵; 걵; 걵; ) HANGUL SYLLABLE GEONJ
+AC76;AC76;1100 1165 11AD;AC76;1100 1165 11AD; # (걶; 걶; 걶; 걶; 걶; ) HANGUL SYLLABLE GEONH
+AC77;AC77;1100 1165 11AE;AC77;1100 1165 11AE; # (걷; 걷; 걷; 걷; 걷; ) HANGUL SYLLABLE GEOD
+AC78;AC78;1100 1165 11AF;AC78;1100 1165 11AF; # (걸; 걸; 걸; 걸; 걸; ) HANGUL SYLLABLE GEOL
+AC79;AC79;1100 1165 11B0;AC79;1100 1165 11B0; # (걹; 걹; 걹; 걹; 걹; ) HANGUL SYLLABLE GEOLG
+AC7A;AC7A;1100 1165 11B1;AC7A;1100 1165 11B1; # (걺; 걺; 걺; 걺; 걺; ) HANGUL SYLLABLE GEOLM
+AC7B;AC7B;1100 1165 11B2;AC7B;1100 1165 11B2; # (걻; 걻; 걻; 걻; 걻; ) HANGUL SYLLABLE GEOLB
+AC7C;AC7C;1100 1165 11B3;AC7C;1100 1165 11B3; # (걼; 걼; 걼; 걼; 걼; ) HANGUL SYLLABLE GEOLS
+AC7D;AC7D;1100 1165 11B4;AC7D;1100 1165 11B4; # (걽; 걽; 걽; 걽; 걽; ) HANGUL SYLLABLE GEOLT
+AC7E;AC7E;1100 1165 11B5;AC7E;1100 1165 11B5; # (걾; 걾; 걾; 걾; 걾; ) HANGUL SYLLABLE GEOLP
+AC7F;AC7F;1100 1165 11B6;AC7F;1100 1165 11B6; # (걿; 걿; 걿; 걿; 걿; ) HANGUL SYLLABLE GEOLH
+AC80;AC80;1100 1165 11B7;AC80;1100 1165 11B7; # (검; 검; 검; 검; 검; ) HANGUL SYLLABLE GEOM
+AC81;AC81;1100 1165 11B8;AC81;1100 1165 11B8; # (ê²; ê²; 겁; ê²; 겁; ) HANGUL SYLLABLE GEOB
+AC82;AC82;1100 1165 11B9;AC82;1100 1165 11B9; # (겂; 겂; 겂; 겂; 겂; ) HANGUL SYLLABLE GEOBS
+AC83;AC83;1100 1165 11BA;AC83;1100 1165 11BA; # (것; 것; 것; 것; 것; ) HANGUL SYLLABLE GEOS
+AC84;AC84;1100 1165 11BB;AC84;1100 1165 11BB; # (겄; 겄; 겄; 겄; 겄; ) HANGUL SYLLABLE GEOSS
+AC85;AC85;1100 1165 11BC;AC85;1100 1165 11BC; # (겅; 겅; 겅; 겅; 겅; ) HANGUL SYLLABLE GEONG
+AC86;AC86;1100 1165 11BD;AC86;1100 1165 11BD; # (겆; 겆; 겆; 겆; 겆; ) HANGUL SYLLABLE GEOJ
+AC87;AC87;1100 1165 11BE;AC87;1100 1165 11BE; # (겇; 겇; 겇; 겇; 겇; ) HANGUL SYLLABLE GEOC
+AC88;AC88;1100 1165 11BF;AC88;1100 1165 11BF; # (겈; 겈; 겈; 겈; 겈; ) HANGUL SYLLABLE GEOK
+AC89;AC89;1100 1165 11C0;AC89;1100 1165 11C0; # (겉; 겉; 겉; 겉; 겉; ) HANGUL SYLLABLE GEOT
+AC8A;AC8A;1100 1165 11C1;AC8A;1100 1165 11C1; # (겊; 겊; 거á‡; 겊; 거á‡; ) HANGUL SYLLABLE GEOP
+AC8B;AC8B;1100 1165 11C2;AC8B;1100 1165 11C2; # (겋; 겋; 겋; 겋; 겋; ) HANGUL SYLLABLE GEOH
+AC8C;AC8C;1100 1166;AC8C;1100 1166; # (게; 게; 게; 게; 게; ) HANGUL SYLLABLE GE
+AC8D;AC8D;1100 1166 11A8;AC8D;1100 1166 11A8; # (ê²; ê²; 겍; ê²; 겍; ) HANGUL SYLLABLE GEG
+AC8E;AC8E;1100 1166 11A9;AC8E;1100 1166 11A9; # (겎; 겎; 겎; 겎; 겎; ) HANGUL SYLLABLE GEGG
+AC8F;AC8F;1100 1166 11AA;AC8F;1100 1166 11AA; # (ê²; ê²; 겏; ê²; 겏; ) HANGUL SYLLABLE GEGS
+AC90;AC90;1100 1166 11AB;AC90;1100 1166 11AB; # (ê²; ê²; 겐; ê²; 겐; ) HANGUL SYLLABLE GEN
+AC91;AC91;1100 1166 11AC;AC91;1100 1166 11AC; # (겑; 겑; 겑; 겑; 겑; ) HANGUL SYLLABLE GENJ
+AC92;AC92;1100 1166 11AD;AC92;1100 1166 11AD; # (겒; 겒; 겒; 겒; 겒; ) HANGUL SYLLABLE GENH
+AC93;AC93;1100 1166 11AE;AC93;1100 1166 11AE; # (겓; 겓; 겓; 겓; 겓; ) HANGUL SYLLABLE GED
+AC94;AC94;1100 1166 11AF;AC94;1100 1166 11AF; # (겔; 겔; 겔; 겔; 겔; ) HANGUL SYLLABLE GEL
+AC95;AC95;1100 1166 11B0;AC95;1100 1166 11B0; # (겕; 겕; 겕; 겕; 겕; ) HANGUL SYLLABLE GELG
+AC96;AC96;1100 1166 11B1;AC96;1100 1166 11B1; # (겖; 겖; 겖; 겖; 겖; ) HANGUL SYLLABLE GELM
+AC97;AC97;1100 1166 11B2;AC97;1100 1166 11B2; # (겗; 겗; 겗; 겗; 겗; ) HANGUL SYLLABLE GELB
+AC98;AC98;1100 1166 11B3;AC98;1100 1166 11B3; # (겘; 겘; 겘; 겘; 겘; ) HANGUL SYLLABLE GELS
+AC99;AC99;1100 1166 11B4;AC99;1100 1166 11B4; # (겙; 겙; 겙; 겙; 겙; ) HANGUL SYLLABLE GELT
+AC9A;AC9A;1100 1166 11B5;AC9A;1100 1166 11B5; # (겚; 겚; 겚; 겚; 겚; ) HANGUL SYLLABLE GELP
+AC9B;AC9B;1100 1166 11B6;AC9B;1100 1166 11B6; # (겛; 겛; 겛; 겛; 겛; ) HANGUL SYLLABLE GELH
+AC9C;AC9C;1100 1166 11B7;AC9C;1100 1166 11B7; # (겜; 겜; 겜; 겜; 겜; ) HANGUL SYLLABLE GEM
+AC9D;AC9D;1100 1166 11B8;AC9D;1100 1166 11B8; # (ê²; ê²; 겝; ê²; 겝; ) HANGUL SYLLABLE GEB
+AC9E;AC9E;1100 1166 11B9;AC9E;1100 1166 11B9; # (겞; 겞; 겞; 겞; 겞; ) HANGUL SYLLABLE GEBS
+AC9F;AC9F;1100 1166 11BA;AC9F;1100 1166 11BA; # (겟; 겟; 겟; 겟; 겟; ) HANGUL SYLLABLE GES
+ACA0;ACA0;1100 1166 11BB;ACA0;1100 1166 11BB; # (겠; 겠; 겠; 겠; 겠; ) HANGUL SYLLABLE GESS
+ACA1;ACA1;1100 1166 11BC;ACA1;1100 1166 11BC; # (겡; 겡; 겡; 겡; 겡; ) HANGUL SYLLABLE GENG
+ACA2;ACA2;1100 1166 11BD;ACA2;1100 1166 11BD; # (겢; 겢; 겢; 겢; 겢; ) HANGUL SYLLABLE GEJ
+ACA3;ACA3;1100 1166 11BE;ACA3;1100 1166 11BE; # (겣; 겣; 겣; 겣; 겣; ) HANGUL SYLLABLE GEC
+ACA4;ACA4;1100 1166 11BF;ACA4;1100 1166 11BF; # (겤; 겤; 겤; 겤; 겤; ) HANGUL SYLLABLE GEK
+ACA5;ACA5;1100 1166 11C0;ACA5;1100 1166 11C0; # (겥; 겥; 겥; 겥; 겥; ) HANGUL SYLLABLE GET
+ACA6;ACA6;1100 1166 11C1;ACA6;1100 1166 11C1; # (겦; 겦; 게á‡; 겦; 게á‡; ) HANGUL SYLLABLE GEP
+ACA7;ACA7;1100 1166 11C2;ACA7;1100 1166 11C2; # (겧; 겧; 겧; 겧; 겧; ) HANGUL SYLLABLE GEH
+ACA8;ACA8;1100 1167;ACA8;1100 1167; # (겨; 겨; 겨; 겨; 겨; ) HANGUL SYLLABLE GYEO
+ACA9;ACA9;1100 1167 11A8;ACA9;1100 1167 11A8; # (격; 격; 격; 격; 격; ) HANGUL SYLLABLE GYEOG
+ACAA;ACAA;1100 1167 11A9;ACAA;1100 1167 11A9; # (겪; 겪; 겪; 겪; 겪; ) HANGUL SYLLABLE GYEOGG
+ACAB;ACAB;1100 1167 11AA;ACAB;1100 1167 11AA; # (겫; 겫; 겫; 겫; 겫; ) HANGUL SYLLABLE GYEOGS
+ACAC;ACAC;1100 1167 11AB;ACAC;1100 1167 11AB; # (견; 견; 견; 견; 견; ) HANGUL SYLLABLE GYEON
+ACAD;ACAD;1100 1167 11AC;ACAD;1100 1167 11AC; # (겭; 겭; 겭; 겭; 겭; ) HANGUL SYLLABLE GYEONJ
+ACAE;ACAE;1100 1167 11AD;ACAE;1100 1167 11AD; # (겮; 겮; 겮; 겮; 겮; ) HANGUL SYLLABLE GYEONH
+ACAF;ACAF;1100 1167 11AE;ACAF;1100 1167 11AE; # (겯; 겯; 겯; 겯; 겯; ) HANGUL SYLLABLE GYEOD
+ACB0;ACB0;1100 1167 11AF;ACB0;1100 1167 11AF; # (결; 결; 결; 결; 결; ) HANGUL SYLLABLE GYEOL
+ACB1;ACB1;1100 1167 11B0;ACB1;1100 1167 11B0; # (겱; 겱; 겱; 겱; 겱; ) HANGUL SYLLABLE GYEOLG
+ACB2;ACB2;1100 1167 11B1;ACB2;1100 1167 11B1; # (겲; 겲; 겲; 겲; 겲; ) HANGUL SYLLABLE GYEOLM
+ACB3;ACB3;1100 1167 11B2;ACB3;1100 1167 11B2; # (겳; 겳; 겳; 겳; 겳; ) HANGUL SYLLABLE GYEOLB
+ACB4;ACB4;1100 1167 11B3;ACB4;1100 1167 11B3; # (겴; 겴; 겴; 겴; 겴; ) HANGUL SYLLABLE GYEOLS
+ACB5;ACB5;1100 1167 11B4;ACB5;1100 1167 11B4; # (겵; 겵; 겵; 겵; 겵; ) HANGUL SYLLABLE GYEOLT
+ACB6;ACB6;1100 1167 11B5;ACB6;1100 1167 11B5; # (겶; 겶; 겶; 겶; 겶; ) HANGUL SYLLABLE GYEOLP
+ACB7;ACB7;1100 1167 11B6;ACB7;1100 1167 11B6; # (겷; 겷; 겷; 겷; 겷; ) HANGUL SYLLABLE GYEOLH
+ACB8;ACB8;1100 1167 11B7;ACB8;1100 1167 11B7; # (겸; 겸; 겸; 겸; 겸; ) HANGUL SYLLABLE GYEOM
+ACB9;ACB9;1100 1167 11B8;ACB9;1100 1167 11B8; # (겹; 겹; 겹; 겹; 겹; ) HANGUL SYLLABLE GYEOB
+ACBA;ACBA;1100 1167 11B9;ACBA;1100 1167 11B9; # (겺; 겺; 겺; 겺; 겺; ) HANGUL SYLLABLE GYEOBS
+ACBB;ACBB;1100 1167 11BA;ACBB;1100 1167 11BA; # (겻; 겻; 겻; 겻; 겻; ) HANGUL SYLLABLE GYEOS
+ACBC;ACBC;1100 1167 11BB;ACBC;1100 1167 11BB; # (겼; 겼; 겼; 겼; 겼; ) HANGUL SYLLABLE GYEOSS
+ACBD;ACBD;1100 1167 11BC;ACBD;1100 1167 11BC; # (경; 경; 경; 경; 경; ) HANGUL SYLLABLE GYEONG
+ACBE;ACBE;1100 1167 11BD;ACBE;1100 1167 11BD; # (겾; 겾; 겾; 겾; 겾; ) HANGUL SYLLABLE GYEOJ
+ACBF;ACBF;1100 1167 11BE;ACBF;1100 1167 11BE; # (겿; 겿; 겿; 겿; 겿; ) HANGUL SYLLABLE GYEOC
+ACC0;ACC0;1100 1167 11BF;ACC0;1100 1167 11BF; # (곀; 곀; 곀; 곀; 곀; ) HANGUL SYLLABLE GYEOK
+ACC1;ACC1;1100 1167 11C0;ACC1;1100 1167 11C0; # (ê³; ê³; 곁; ê³; 곁; ) HANGUL SYLLABLE GYEOT
+ACC2;ACC2;1100 1167 11C1;ACC2;1100 1167 11C1; # (곂; 곂; 겨á‡; 곂; 겨á‡; ) HANGUL SYLLABLE GYEOP
+ACC3;ACC3;1100 1167 11C2;ACC3;1100 1167 11C2; # (곃; 곃; 곃; 곃; 곃; ) HANGUL SYLLABLE GYEOH
+ACC4;ACC4;1100 1168;ACC4;1100 1168; # (계; 계; 계; 계; 계; ) HANGUL SYLLABLE GYE
+ACC5;ACC5;1100 1168 11A8;ACC5;1100 1168 11A8; # (곅; 곅; 곅; 곅; 곅; ) HANGUL SYLLABLE GYEG
+ACC6;ACC6;1100 1168 11A9;ACC6;1100 1168 11A9; # (곆; 곆; 곆; 곆; 곆; ) HANGUL SYLLABLE GYEGG
+ACC7;ACC7;1100 1168 11AA;ACC7;1100 1168 11AA; # (곇; 곇; 곇; 곇; 곇; ) HANGUL SYLLABLE GYEGS
+ACC8;ACC8;1100 1168 11AB;ACC8;1100 1168 11AB; # (곈; 곈; 곈; 곈; 곈; ) HANGUL SYLLABLE GYEN
+ACC9;ACC9;1100 1168 11AC;ACC9;1100 1168 11AC; # (곉; 곉; 곉; 곉; 곉; ) HANGUL SYLLABLE GYENJ
+ACCA;ACCA;1100 1168 11AD;ACCA;1100 1168 11AD; # (곊; 곊; 곊; 곊; 곊; ) HANGUL SYLLABLE GYENH
+ACCB;ACCB;1100 1168 11AE;ACCB;1100 1168 11AE; # (곋; 곋; 곋; 곋; 곋; ) HANGUL SYLLABLE GYED
+ACCC;ACCC;1100 1168 11AF;ACCC;1100 1168 11AF; # (곌; 곌; 곌; 곌; 곌; ) HANGUL SYLLABLE GYEL
+ACCD;ACCD;1100 1168 11B0;ACCD;1100 1168 11B0; # (ê³; ê³; 곍; ê³; 곍; ) HANGUL SYLLABLE GYELG
+ACCE;ACCE;1100 1168 11B1;ACCE;1100 1168 11B1; # (곎; 곎; 곎; 곎; 곎; ) HANGUL SYLLABLE GYELM
+ACCF;ACCF;1100 1168 11B2;ACCF;1100 1168 11B2; # (ê³; ê³; 곏; ê³; 곏; ) HANGUL SYLLABLE GYELB
+ACD0;ACD0;1100 1168 11B3;ACD0;1100 1168 11B3; # (ê³; ê³; 곐; ê³; 곐; ) HANGUL SYLLABLE GYELS
+ACD1;ACD1;1100 1168 11B4;ACD1;1100 1168 11B4; # (곑; 곑; 곑; 곑; 곑; ) HANGUL SYLLABLE GYELT
+ACD2;ACD2;1100 1168 11B5;ACD2;1100 1168 11B5; # (곒; 곒; 곒; 곒; 곒; ) HANGUL SYLLABLE GYELP
+ACD3;ACD3;1100 1168 11B6;ACD3;1100 1168 11B6; # (곓; 곓; 곓; 곓; 곓; ) HANGUL SYLLABLE GYELH
+ACD4;ACD4;1100 1168 11B7;ACD4;1100 1168 11B7; # (곔; 곔; 곔; 곔; 곔; ) HANGUL SYLLABLE GYEM
+ACD5;ACD5;1100 1168 11B8;ACD5;1100 1168 11B8; # (곕; 곕; 곕; 곕; 곕; ) HANGUL SYLLABLE GYEB
+ACD6;ACD6;1100 1168 11B9;ACD6;1100 1168 11B9; # (곖; 곖; 곖; 곖; 곖; ) HANGUL SYLLABLE GYEBS
+ACD7;ACD7;1100 1168 11BA;ACD7;1100 1168 11BA; # (곗; 곗; 곗; 곗; 곗; ) HANGUL SYLLABLE GYES
+ACD8;ACD8;1100 1168 11BB;ACD8;1100 1168 11BB; # (곘; 곘; 곘; 곘; 곘; ) HANGUL SYLLABLE GYESS
+ACD9;ACD9;1100 1168 11BC;ACD9;1100 1168 11BC; # (곙; 곙; 곙; 곙; 곙; ) HANGUL SYLLABLE GYENG
+ACDA;ACDA;1100 1168 11BD;ACDA;1100 1168 11BD; # (곚; 곚; 곚; 곚; 곚; ) HANGUL SYLLABLE GYEJ
+ACDB;ACDB;1100 1168 11BE;ACDB;1100 1168 11BE; # (곛; 곛; 곛; 곛; 곛; ) HANGUL SYLLABLE GYEC
+ACDC;ACDC;1100 1168 11BF;ACDC;1100 1168 11BF; # (곜; 곜; 곜; 곜; 곜; ) HANGUL SYLLABLE GYEK
+ACDD;ACDD;1100 1168 11C0;ACDD;1100 1168 11C0; # (ê³; ê³; 곝; ê³; 곝; ) HANGUL SYLLABLE GYET
+ACDE;ACDE;1100 1168 11C1;ACDE;1100 1168 11C1; # (곞; 곞; 계á‡; 곞; 계á‡; ) HANGUL SYLLABLE GYEP
+ACDF;ACDF;1100 1168 11C2;ACDF;1100 1168 11C2; # (곟; 곟; 곟; 곟; 곟; ) HANGUL SYLLABLE GYEH
+ACE0;ACE0;1100 1169;ACE0;1100 1169; # (고; 고; 고; 고; 고; ) HANGUL SYLLABLE GO
+ACE1;ACE1;1100 1169 11A8;ACE1;1100 1169 11A8; # (곡; 곡; 곡; 곡; 곡; ) HANGUL SYLLABLE GOG
+ACE2;ACE2;1100 1169 11A9;ACE2;1100 1169 11A9; # (곢; 곢; 곢; 곢; 곢; ) HANGUL SYLLABLE GOGG
+ACE3;ACE3;1100 1169 11AA;ACE3;1100 1169 11AA; # (곣; 곣; 곣; 곣; 곣; ) HANGUL SYLLABLE GOGS
+ACE4;ACE4;1100 1169 11AB;ACE4;1100 1169 11AB; # (곤; 곤; 곤; 곤; 곤; ) HANGUL SYLLABLE GON
+ACE5;ACE5;1100 1169 11AC;ACE5;1100 1169 11AC; # (곥; 곥; 곥; 곥; 곥; ) HANGUL SYLLABLE GONJ
+ACE6;ACE6;1100 1169 11AD;ACE6;1100 1169 11AD; # (곦; 곦; 곦; 곦; 곦; ) HANGUL SYLLABLE GONH
+ACE7;ACE7;1100 1169 11AE;ACE7;1100 1169 11AE; # (곧; 곧; 곧; 곧; 곧; ) HANGUL SYLLABLE GOD
+ACE8;ACE8;1100 1169 11AF;ACE8;1100 1169 11AF; # (골; 골; 골; 골; 골; ) HANGUL SYLLABLE GOL
+ACE9;ACE9;1100 1169 11B0;ACE9;1100 1169 11B0; # (곩; 곩; 곩; 곩; 곩; ) HANGUL SYLLABLE GOLG
+ACEA;ACEA;1100 1169 11B1;ACEA;1100 1169 11B1; # (곪; 곪; 곪; 곪; 곪; ) HANGUL SYLLABLE GOLM
+ACEB;ACEB;1100 1169 11B2;ACEB;1100 1169 11B2; # (곫; 곫; 곫; 곫; 곫; ) HANGUL SYLLABLE GOLB
+ACEC;ACEC;1100 1169 11B3;ACEC;1100 1169 11B3; # (곬; 곬; 곬; 곬; 곬; ) HANGUL SYLLABLE GOLS
+ACED;ACED;1100 1169 11B4;ACED;1100 1169 11B4; # (곭; 곭; 곭; 곭; 곭; ) HANGUL SYLLABLE GOLT
+ACEE;ACEE;1100 1169 11B5;ACEE;1100 1169 11B5; # (곮; 곮; 곮; 곮; 곮; ) HANGUL SYLLABLE GOLP
+ACEF;ACEF;1100 1169 11B6;ACEF;1100 1169 11B6; # (곯; 곯; 곯; 곯; 곯; ) HANGUL SYLLABLE GOLH
+ACF0;ACF0;1100 1169 11B7;ACF0;1100 1169 11B7; # (곰; 곰; 곰; 곰; 곰; ) HANGUL SYLLABLE GOM
+ACF1;ACF1;1100 1169 11B8;ACF1;1100 1169 11B8; # (곱; 곱; 곱; 곱; 곱; ) HANGUL SYLLABLE GOB
+ACF2;ACF2;1100 1169 11B9;ACF2;1100 1169 11B9; # (곲; 곲; 곲; 곲; 곲; ) HANGUL SYLLABLE GOBS
+ACF3;ACF3;1100 1169 11BA;ACF3;1100 1169 11BA; # (곳; 곳; 곳; 곳; 곳; ) HANGUL SYLLABLE GOS
+ACF4;ACF4;1100 1169 11BB;ACF4;1100 1169 11BB; # (곴; 곴; 곴; 곴; 곴; ) HANGUL SYLLABLE GOSS
+ACF5;ACF5;1100 1169 11BC;ACF5;1100 1169 11BC; # (공; 공; 공; 공; 공; ) HANGUL SYLLABLE GONG
+ACF6;ACF6;1100 1169 11BD;ACF6;1100 1169 11BD; # (곶; 곶; 곶; 곶; 곶; ) HANGUL SYLLABLE GOJ
+ACF7;ACF7;1100 1169 11BE;ACF7;1100 1169 11BE; # (곷; 곷; 곷; 곷; 곷; ) HANGUL SYLLABLE GOC
+ACF8;ACF8;1100 1169 11BF;ACF8;1100 1169 11BF; # (곸; 곸; 곸; 곸; 곸; ) HANGUL SYLLABLE GOK
+ACF9;ACF9;1100 1169 11C0;ACF9;1100 1169 11C0; # (곹; 곹; 곹; 곹; 곹; ) HANGUL SYLLABLE GOT
+ACFA;ACFA;1100 1169 11C1;ACFA;1100 1169 11C1; # (곺; 곺; 고á‡; 곺; 고á‡; ) HANGUL SYLLABLE GOP
+ACFB;ACFB;1100 1169 11C2;ACFB;1100 1169 11C2; # (곻; 곻; 곻; 곻; 곻; ) HANGUL SYLLABLE GOH
+ACFC;ACFC;1100 116A;ACFC;1100 116A; # (과; 과; 과; 과; 과; ) HANGUL SYLLABLE GWA
+ACFD;ACFD;1100 116A 11A8;ACFD;1100 116A 11A8; # (곽; 곽; 곽; 곽; 곽; ) HANGUL SYLLABLE GWAG
+ACFE;ACFE;1100 116A 11A9;ACFE;1100 116A 11A9; # (곾; 곾; 곾; 곾; 곾; ) HANGUL SYLLABLE GWAGG
+ACFF;ACFF;1100 116A 11AA;ACFF;1100 116A 11AA; # (곿; 곿; 곿; 곿; 곿; ) HANGUL SYLLABLE GWAGS
+AD00;AD00;1100 116A 11AB;AD00;1100 116A 11AB; # (관; 관; 관; 관; 관; ) HANGUL SYLLABLE GWAN
+AD01;AD01;1100 116A 11AC;AD01;1100 116A 11AC; # (ê´; ê´; 괁; ê´; 괁; ) HANGUL SYLLABLE GWANJ
+AD02;AD02;1100 116A 11AD;AD02;1100 116A 11AD; # (괂; 괂; 괂; 괂; 괂; ) HANGUL SYLLABLE GWANH
+AD03;AD03;1100 116A 11AE;AD03;1100 116A 11AE; # (괃; 괃; 괃; 괃; 괃; ) HANGUL SYLLABLE GWAD
+AD04;AD04;1100 116A 11AF;AD04;1100 116A 11AF; # (괄; 괄; 괄; 괄; 괄; ) HANGUL SYLLABLE GWAL
+AD05;AD05;1100 116A 11B0;AD05;1100 116A 11B0; # (괅; 괅; 괅; 괅; 괅; ) HANGUL SYLLABLE GWALG
+AD06;AD06;1100 116A 11B1;AD06;1100 116A 11B1; # (괆; 괆; 괆; 괆; 괆; ) HANGUL SYLLABLE GWALM
+AD07;AD07;1100 116A 11B2;AD07;1100 116A 11B2; # (괇; 괇; 괇; 괇; 괇; ) HANGUL SYLLABLE GWALB
+AD08;AD08;1100 116A 11B3;AD08;1100 116A 11B3; # (괈; 괈; 괈; 괈; 괈; ) HANGUL SYLLABLE GWALS
+AD09;AD09;1100 116A 11B4;AD09;1100 116A 11B4; # (괉; 괉; 괉; 괉; 괉; ) HANGUL SYLLABLE GWALT
+AD0A;AD0A;1100 116A 11B5;AD0A;1100 116A 11B5; # (괊; 괊; 괊; 괊; 괊; ) HANGUL SYLLABLE GWALP
+AD0B;AD0B;1100 116A 11B6;AD0B;1100 116A 11B6; # (괋; 괋; 괋; 괋; 괋; ) HANGUL SYLLABLE GWALH
+AD0C;AD0C;1100 116A 11B7;AD0C;1100 116A 11B7; # (괌; 괌; 괌; 괌; 괌; ) HANGUL SYLLABLE GWAM
+AD0D;AD0D;1100 116A 11B8;AD0D;1100 116A 11B8; # (ê´; ê´; 괍; ê´; 괍; ) HANGUL SYLLABLE GWAB
+AD0E;AD0E;1100 116A 11B9;AD0E;1100 116A 11B9; # (괎; 괎; 괎; 괎; 괎; ) HANGUL SYLLABLE GWABS
+AD0F;AD0F;1100 116A 11BA;AD0F;1100 116A 11BA; # (ê´; ê´; 괏; ê´; 괏; ) HANGUL SYLLABLE GWAS
+AD10;AD10;1100 116A 11BB;AD10;1100 116A 11BB; # (ê´; ê´; 괐; ê´; 괐; ) HANGUL SYLLABLE GWASS
+AD11;AD11;1100 116A 11BC;AD11;1100 116A 11BC; # (광; 광; 광; 광; 광; ) HANGUL SYLLABLE GWANG
+AD12;AD12;1100 116A 11BD;AD12;1100 116A 11BD; # (괒; 괒; 괒; 괒; 괒; ) HANGUL SYLLABLE GWAJ
+AD13;AD13;1100 116A 11BE;AD13;1100 116A 11BE; # (괓; 괓; 괓; 괓; 괓; ) HANGUL SYLLABLE GWAC
+AD14;AD14;1100 116A 11BF;AD14;1100 116A 11BF; # (괔; 괔; 괔; 괔; 괔; ) HANGUL SYLLABLE GWAK
+AD15;AD15;1100 116A 11C0;AD15;1100 116A 11C0; # (괕; 괕; 괕; 괕; 괕; ) HANGUL SYLLABLE GWAT
+AD16;AD16;1100 116A 11C1;AD16;1100 116A 11C1; # (ê´–; ê´–; 과á‡; ê´–; 과á‡; ) HANGUL SYLLABLE GWAP
+AD17;AD17;1100 116A 11C2;AD17;1100 116A 11C2; # (괗; 괗; 괗; 괗; 괗; ) HANGUL SYLLABLE GWAH
+AD18;AD18;1100 116B;AD18;1100 116B; # (괘; 괘; 괘; 괘; 괘; ) HANGUL SYLLABLE GWAE
+AD19;AD19;1100 116B 11A8;AD19;1100 116B 11A8; # (괙; 괙; 괙; 괙; 괙; ) HANGUL SYLLABLE GWAEG
+AD1A;AD1A;1100 116B 11A9;AD1A;1100 116B 11A9; # (괚; 괚; 괚; 괚; 괚; ) HANGUL SYLLABLE GWAEGG
+AD1B;AD1B;1100 116B 11AA;AD1B;1100 116B 11AA; # (괛; 괛; 괛; 괛; 괛; ) HANGUL SYLLABLE GWAEGS
+AD1C;AD1C;1100 116B 11AB;AD1C;1100 116B 11AB; # (괜; 괜; 괜; 괜; 괜; ) HANGUL SYLLABLE GWAEN
+AD1D;AD1D;1100 116B 11AC;AD1D;1100 116B 11AC; # (ê´; ê´; 괝; ê´; 괝; ) HANGUL SYLLABLE GWAENJ
+AD1E;AD1E;1100 116B 11AD;AD1E;1100 116B 11AD; # (괞; 괞; 괞; 괞; 괞; ) HANGUL SYLLABLE GWAENH
+AD1F;AD1F;1100 116B 11AE;AD1F;1100 116B 11AE; # (괟; 괟; 괟; 괟; 괟; ) HANGUL SYLLABLE GWAED
+AD20;AD20;1100 116B 11AF;AD20;1100 116B 11AF; # (괠; 괠; 괠; 괠; 괠; ) HANGUL SYLLABLE GWAEL
+AD21;AD21;1100 116B 11B0;AD21;1100 116B 11B0; # (괡; 괡; 괡; 괡; 괡; ) HANGUL SYLLABLE GWAELG
+AD22;AD22;1100 116B 11B1;AD22;1100 116B 11B1; # (괢; 괢; 괢; 괢; 괢; ) HANGUL SYLLABLE GWAELM
+AD23;AD23;1100 116B 11B2;AD23;1100 116B 11B2; # (괣; 괣; 괣; 괣; 괣; ) HANGUL SYLLABLE GWAELB
+AD24;AD24;1100 116B 11B3;AD24;1100 116B 11B3; # (괤; 괤; 괤; 괤; 괤; ) HANGUL SYLLABLE GWAELS
+AD25;AD25;1100 116B 11B4;AD25;1100 116B 11B4; # (괥; 괥; 괥; 괥; 괥; ) HANGUL SYLLABLE GWAELT
+AD26;AD26;1100 116B 11B5;AD26;1100 116B 11B5; # (괦; 괦; 괦; 괦; 괦; ) HANGUL SYLLABLE GWAELP
+AD27;AD27;1100 116B 11B6;AD27;1100 116B 11B6; # (괧; 괧; 괧; 괧; 괧; ) HANGUL SYLLABLE GWAELH
+AD28;AD28;1100 116B 11B7;AD28;1100 116B 11B7; # (괨; 괨; 괨; 괨; 괨; ) HANGUL SYLLABLE GWAEM
+AD29;AD29;1100 116B 11B8;AD29;1100 116B 11B8; # (괩; 괩; 괩; 괩; 괩; ) HANGUL SYLLABLE GWAEB
+AD2A;AD2A;1100 116B 11B9;AD2A;1100 116B 11B9; # (괪; 괪; 괪; 괪; 괪; ) HANGUL SYLLABLE GWAEBS
+AD2B;AD2B;1100 116B 11BA;AD2B;1100 116B 11BA; # (괫; 괫; 괫; 괫; 괫; ) HANGUL SYLLABLE GWAES
+AD2C;AD2C;1100 116B 11BB;AD2C;1100 116B 11BB; # (괬; 괬; 괬; 괬; 괬; ) HANGUL SYLLABLE GWAESS
+AD2D;AD2D;1100 116B 11BC;AD2D;1100 116B 11BC; # (괭; 괭; 괭; 괭; 괭; ) HANGUL SYLLABLE GWAENG
+AD2E;AD2E;1100 116B 11BD;AD2E;1100 116B 11BD; # (괮; 괮; 괮; 괮; 괮; ) HANGUL SYLLABLE GWAEJ
+AD2F;AD2F;1100 116B 11BE;AD2F;1100 116B 11BE; # (괯; 괯; 괯; 괯; 괯; ) HANGUL SYLLABLE GWAEC
+AD30;AD30;1100 116B 11BF;AD30;1100 116B 11BF; # (괰; 괰; 괰; 괰; 괰; ) HANGUL SYLLABLE GWAEK
+AD31;AD31;1100 116B 11C0;AD31;1100 116B 11C0; # (괱; 괱; 괱; 괱; 괱; ) HANGUL SYLLABLE GWAET
+AD32;AD32;1100 116B 11C1;AD32;1100 116B 11C1; # (ê´²; ê´²; 괘á‡; ê´²; 괘á‡; ) HANGUL SYLLABLE GWAEP
+AD33;AD33;1100 116B 11C2;AD33;1100 116B 11C2; # (괳; 괳; 괳; 괳; 괳; ) HANGUL SYLLABLE GWAEH
+AD34;AD34;1100 116C;AD34;1100 116C; # (괴; 괴; 괴; 괴; 괴; ) HANGUL SYLLABLE GOE
+AD35;AD35;1100 116C 11A8;AD35;1100 116C 11A8; # (괵; 괵; 괵; 괵; 괵; ) HANGUL SYLLABLE GOEG
+AD36;AD36;1100 116C 11A9;AD36;1100 116C 11A9; # (괶; 괶; 괶; 괶; 괶; ) HANGUL SYLLABLE GOEGG
+AD37;AD37;1100 116C 11AA;AD37;1100 116C 11AA; # (괷; 괷; 괷; 괷; 괷; ) HANGUL SYLLABLE GOEGS
+AD38;AD38;1100 116C 11AB;AD38;1100 116C 11AB; # (괸; 괸; 괸; 괸; 괸; ) HANGUL SYLLABLE GOEN
+AD39;AD39;1100 116C 11AC;AD39;1100 116C 11AC; # (괹; 괹; 괹; 괹; 괹; ) HANGUL SYLLABLE GOENJ
+AD3A;AD3A;1100 116C 11AD;AD3A;1100 116C 11AD; # (괺; 괺; 괺; 괺; 괺; ) HANGUL SYLLABLE GOENH
+AD3B;AD3B;1100 116C 11AE;AD3B;1100 116C 11AE; # (괻; 괻; 괻; 괻; 괻; ) HANGUL SYLLABLE GOED
+AD3C;AD3C;1100 116C 11AF;AD3C;1100 116C 11AF; # (괼; 괼; 괼; 괼; 괼; ) HANGUL SYLLABLE GOEL
+AD3D;AD3D;1100 116C 11B0;AD3D;1100 116C 11B0; # (괽; 괽; 괽; 괽; 괽; ) HANGUL SYLLABLE GOELG
+AD3E;AD3E;1100 116C 11B1;AD3E;1100 116C 11B1; # (괾; 괾; 괾; 괾; 괾; ) HANGUL SYLLABLE GOELM
+AD3F;AD3F;1100 116C 11B2;AD3F;1100 116C 11B2; # (괿; 괿; 괿; 괿; 괿; ) HANGUL SYLLABLE GOELB
+AD40;AD40;1100 116C 11B3;AD40;1100 116C 11B3; # (굀; 굀; 굀; 굀; 굀; ) HANGUL SYLLABLE GOELS
+AD41;AD41;1100 116C 11B4;AD41;1100 116C 11B4; # (êµ; êµ; 굁; êµ; 굁; ) HANGUL SYLLABLE GOELT
+AD42;AD42;1100 116C 11B5;AD42;1100 116C 11B5; # (굂; 굂; 굂; 굂; 굂; ) HANGUL SYLLABLE GOELP
+AD43;AD43;1100 116C 11B6;AD43;1100 116C 11B6; # (굃; 굃; 굃; 굃; 굃; ) HANGUL SYLLABLE GOELH
+AD44;AD44;1100 116C 11B7;AD44;1100 116C 11B7; # (굄; 굄; 굄; 굄; 굄; ) HANGUL SYLLABLE GOEM
+AD45;AD45;1100 116C 11B8;AD45;1100 116C 11B8; # (굅; 굅; 굅; 굅; 굅; ) HANGUL SYLLABLE GOEB
+AD46;AD46;1100 116C 11B9;AD46;1100 116C 11B9; # (굆; 굆; 굆; 굆; 굆; ) HANGUL SYLLABLE GOEBS
+AD47;AD47;1100 116C 11BA;AD47;1100 116C 11BA; # (굇; 굇; 굇; 굇; 굇; ) HANGUL SYLLABLE GOES
+AD48;AD48;1100 116C 11BB;AD48;1100 116C 11BB; # (굈; 굈; 굈; 굈; 굈; ) HANGUL SYLLABLE GOESS
+AD49;AD49;1100 116C 11BC;AD49;1100 116C 11BC; # (굉; 굉; 굉; 굉; 굉; ) HANGUL SYLLABLE GOENG
+AD4A;AD4A;1100 116C 11BD;AD4A;1100 116C 11BD; # (굊; 굊; 굊; 굊; 굊; ) HANGUL SYLLABLE GOEJ
+AD4B;AD4B;1100 116C 11BE;AD4B;1100 116C 11BE; # (굋; 굋; 굋; 굋; 굋; ) HANGUL SYLLABLE GOEC
+AD4C;AD4C;1100 116C 11BF;AD4C;1100 116C 11BF; # (굌; 굌; 굌; 굌; 굌; ) HANGUL SYLLABLE GOEK
+AD4D;AD4D;1100 116C 11C0;AD4D;1100 116C 11C0; # (êµ; êµ; 굍; êµ; 굍; ) HANGUL SYLLABLE GOET
+AD4E;AD4E;1100 116C 11C1;AD4E;1100 116C 11C1; # (굎; 굎; 괴á‡; 굎; 괴á‡; ) HANGUL SYLLABLE GOEP
+AD4F;AD4F;1100 116C 11C2;AD4F;1100 116C 11C2; # (êµ; êµ; 굏; êµ; 굏; ) HANGUL SYLLABLE GOEH
+AD50;AD50;1100 116D;AD50;1100 116D; # (êµ; êµ; 교; êµ; 교; ) HANGUL SYLLABLE GYO
+AD51;AD51;1100 116D 11A8;AD51;1100 116D 11A8; # (굑; 굑; 굑; 굑; 굑; ) HANGUL SYLLABLE GYOG
+AD52;AD52;1100 116D 11A9;AD52;1100 116D 11A9; # (굒; 굒; 굒; 굒; 굒; ) HANGUL SYLLABLE GYOGG
+AD53;AD53;1100 116D 11AA;AD53;1100 116D 11AA; # (굓; 굓; 굓; 굓; 굓; ) HANGUL SYLLABLE GYOGS
+AD54;AD54;1100 116D 11AB;AD54;1100 116D 11AB; # (굔; 굔; 굔; 굔; 굔; ) HANGUL SYLLABLE GYON
+AD55;AD55;1100 116D 11AC;AD55;1100 116D 11AC; # (굕; 굕; 굕; 굕; 굕; ) HANGUL SYLLABLE GYONJ
+AD56;AD56;1100 116D 11AD;AD56;1100 116D 11AD; # (굖; 굖; 굖; 굖; 굖; ) HANGUL SYLLABLE GYONH
+AD57;AD57;1100 116D 11AE;AD57;1100 116D 11AE; # (굗; 굗; 굗; 굗; 굗; ) HANGUL SYLLABLE GYOD
+AD58;AD58;1100 116D 11AF;AD58;1100 116D 11AF; # (굘; 굘; 굘; 굘; 굘; ) HANGUL SYLLABLE GYOL
+AD59;AD59;1100 116D 11B0;AD59;1100 116D 11B0; # (굙; 굙; 굙; 굙; 굙; ) HANGUL SYLLABLE GYOLG
+AD5A;AD5A;1100 116D 11B1;AD5A;1100 116D 11B1; # (굚; 굚; 굚; 굚; 굚; ) HANGUL SYLLABLE GYOLM
+AD5B;AD5B;1100 116D 11B2;AD5B;1100 116D 11B2; # (굛; 굛; 굛; 굛; 굛; ) HANGUL SYLLABLE GYOLB
+AD5C;AD5C;1100 116D 11B3;AD5C;1100 116D 11B3; # (굜; 굜; 굜; 굜; 굜; ) HANGUL SYLLABLE GYOLS
+AD5D;AD5D;1100 116D 11B4;AD5D;1100 116D 11B4; # (êµ; êµ; 굝; êµ; 굝; ) HANGUL SYLLABLE GYOLT
+AD5E;AD5E;1100 116D 11B5;AD5E;1100 116D 11B5; # (굞; 굞; 굞; 굞; 굞; ) HANGUL SYLLABLE GYOLP
+AD5F;AD5F;1100 116D 11B6;AD5F;1100 116D 11B6; # (굟; 굟; 굟; 굟; 굟; ) HANGUL SYLLABLE GYOLH
+AD60;AD60;1100 116D 11B7;AD60;1100 116D 11B7; # (굠; 굠; 굠; 굠; 굠; ) HANGUL SYLLABLE GYOM
+AD61;AD61;1100 116D 11B8;AD61;1100 116D 11B8; # (굡; 굡; 굡; 굡; 굡; ) HANGUL SYLLABLE GYOB
+AD62;AD62;1100 116D 11B9;AD62;1100 116D 11B9; # (굢; 굢; 굢; 굢; 굢; ) HANGUL SYLLABLE GYOBS
+AD63;AD63;1100 116D 11BA;AD63;1100 116D 11BA; # (굣; 굣; 굣; 굣; 굣; ) HANGUL SYLLABLE GYOS
+AD64;AD64;1100 116D 11BB;AD64;1100 116D 11BB; # (굤; 굤; 굤; 굤; 굤; ) HANGUL SYLLABLE GYOSS
+AD65;AD65;1100 116D 11BC;AD65;1100 116D 11BC; # (굥; 굥; 굥; 굥; 굥; ) HANGUL SYLLABLE GYONG
+AD66;AD66;1100 116D 11BD;AD66;1100 116D 11BD; # (굦; 굦; 굦; 굦; 굦; ) HANGUL SYLLABLE GYOJ
+AD67;AD67;1100 116D 11BE;AD67;1100 116D 11BE; # (굧; 굧; 굧; 굧; 굧; ) HANGUL SYLLABLE GYOC
+AD68;AD68;1100 116D 11BF;AD68;1100 116D 11BF; # (굨; 굨; 굨; 굨; 굨; ) HANGUL SYLLABLE GYOK
+AD69;AD69;1100 116D 11C0;AD69;1100 116D 11C0; # (굩; 굩; 굩; 굩; 굩; ) HANGUL SYLLABLE GYOT
+AD6A;AD6A;1100 116D 11C1;AD6A;1100 116D 11C1; # (굪; 굪; 교á‡; 굪; 교á‡; ) HANGUL SYLLABLE GYOP
+AD6B;AD6B;1100 116D 11C2;AD6B;1100 116D 11C2; # (굫; 굫; 굫; 굫; 굫; ) HANGUL SYLLABLE GYOH
+AD6C;AD6C;1100 116E;AD6C;1100 116E; # (구; 구; 구; 구; 구; ) HANGUL SYLLABLE GU
+AD6D;AD6D;1100 116E 11A8;AD6D;1100 116E 11A8; # (국; 국; 국; 국; 국; ) HANGUL SYLLABLE GUG
+AD6E;AD6E;1100 116E 11A9;AD6E;1100 116E 11A9; # (굮; 굮; 굮; 굮; 굮; ) HANGUL SYLLABLE GUGG
+AD6F;AD6F;1100 116E 11AA;AD6F;1100 116E 11AA; # (굯; 굯; 굯; 굯; 굯; ) HANGUL SYLLABLE GUGS
+AD70;AD70;1100 116E 11AB;AD70;1100 116E 11AB; # (군; 군; 군; 군; 군; ) HANGUL SYLLABLE GUN
+AD71;AD71;1100 116E 11AC;AD71;1100 116E 11AC; # (굱; 굱; 굱; 굱; 굱; ) HANGUL SYLLABLE GUNJ
+AD72;AD72;1100 116E 11AD;AD72;1100 116E 11AD; # (굲; 굲; 굲; 굲; 굲; ) HANGUL SYLLABLE GUNH
+AD73;AD73;1100 116E 11AE;AD73;1100 116E 11AE; # (굳; 굳; 굳; 굳; 굳; ) HANGUL SYLLABLE GUD
+AD74;AD74;1100 116E 11AF;AD74;1100 116E 11AF; # (굴; 굴; 굴; 굴; 굴; ) HANGUL SYLLABLE GUL
+AD75;AD75;1100 116E 11B0;AD75;1100 116E 11B0; # (굵; 굵; 굵; 굵; 굵; ) HANGUL SYLLABLE GULG
+AD76;AD76;1100 116E 11B1;AD76;1100 116E 11B1; # (굶; 굶; 굶; 굶; 굶; ) HANGUL SYLLABLE GULM
+AD77;AD77;1100 116E 11B2;AD77;1100 116E 11B2; # (굷; 굷; 굷; 굷; 굷; ) HANGUL SYLLABLE GULB
+AD78;AD78;1100 116E 11B3;AD78;1100 116E 11B3; # (굸; 굸; 굸; 굸; 굸; ) HANGUL SYLLABLE GULS
+AD79;AD79;1100 116E 11B4;AD79;1100 116E 11B4; # (굹; 굹; 굹; 굹; 굹; ) HANGUL SYLLABLE GULT
+AD7A;AD7A;1100 116E 11B5;AD7A;1100 116E 11B5; # (굺; 굺; 굺; 굺; 굺; ) HANGUL SYLLABLE GULP
+AD7B;AD7B;1100 116E 11B6;AD7B;1100 116E 11B6; # (굻; 굻; 굻; 굻; 굻; ) HANGUL SYLLABLE GULH
+AD7C;AD7C;1100 116E 11B7;AD7C;1100 116E 11B7; # (굼; 굼; 굼; 굼; 굼; ) HANGUL SYLLABLE GUM
+AD7D;AD7D;1100 116E 11B8;AD7D;1100 116E 11B8; # (굽; 굽; 굽; 굽; 굽; ) HANGUL SYLLABLE GUB
+AD7E;AD7E;1100 116E 11B9;AD7E;1100 116E 11B9; # (굾; 굾; 굾; 굾; 굾; ) HANGUL SYLLABLE GUBS
+AD7F;AD7F;1100 116E 11BA;AD7F;1100 116E 11BA; # (굿; 굿; 굿; 굿; 굿; ) HANGUL SYLLABLE GUS
+AD80;AD80;1100 116E 11BB;AD80;1100 116E 11BB; # (궀; 궀; 궀; 궀; 궀; ) HANGUL SYLLABLE GUSS
+AD81;AD81;1100 116E 11BC;AD81;1100 116E 11BC; # (ê¶; ê¶; 궁; ê¶; 궁; ) HANGUL SYLLABLE GUNG
+AD82;AD82;1100 116E 11BD;AD82;1100 116E 11BD; # (궂; 궂; 궂; 궂; 궂; ) HANGUL SYLLABLE GUJ
+AD83;AD83;1100 116E 11BE;AD83;1100 116E 11BE; # (궃; 궃; 궃; 궃; 궃; ) HANGUL SYLLABLE GUC
+AD84;AD84;1100 116E 11BF;AD84;1100 116E 11BF; # (궄; 궄; 궄; 궄; 궄; ) HANGUL SYLLABLE GUK
+AD85;AD85;1100 116E 11C0;AD85;1100 116E 11C0; # (궅; 궅; 궅; 궅; 궅; ) HANGUL SYLLABLE GUT
+AD86;AD86;1100 116E 11C1;AD86;1100 116E 11C1; # (궆; 궆; 구á‡; 궆; 구á‡; ) HANGUL SYLLABLE GUP
+AD87;AD87;1100 116E 11C2;AD87;1100 116E 11C2; # (궇; 궇; 궇; 궇; 궇; ) HANGUL SYLLABLE GUH
+AD88;AD88;1100 116F;AD88;1100 116F; # (궈; 궈; 궈; 궈; 궈; ) HANGUL SYLLABLE GWEO
+AD89;AD89;1100 116F 11A8;AD89;1100 116F 11A8; # (궉; 궉; 궉; 궉; 궉; ) HANGUL SYLLABLE GWEOG
+AD8A;AD8A;1100 116F 11A9;AD8A;1100 116F 11A9; # (궊; 궊; 궊; 궊; 궊; ) HANGUL SYLLABLE GWEOGG
+AD8B;AD8B;1100 116F 11AA;AD8B;1100 116F 11AA; # (궋; 궋; 궋; 궋; 궋; ) HANGUL SYLLABLE GWEOGS
+AD8C;AD8C;1100 116F 11AB;AD8C;1100 116F 11AB; # (권; 권; 권; 권; 권; ) HANGUL SYLLABLE GWEON
+AD8D;AD8D;1100 116F 11AC;AD8D;1100 116F 11AC; # (ê¶; ê¶; 궍; ê¶; 궍; ) HANGUL SYLLABLE GWEONJ
+AD8E;AD8E;1100 116F 11AD;AD8E;1100 116F 11AD; # (궎; 궎; 궎; 궎; 궎; ) HANGUL SYLLABLE GWEONH
+AD8F;AD8F;1100 116F 11AE;AD8F;1100 116F 11AE; # (ê¶; ê¶; 궏; ê¶; 궏; ) HANGUL SYLLABLE GWEOD
+AD90;AD90;1100 116F 11AF;AD90;1100 116F 11AF; # (ê¶; ê¶; 궐; ê¶; 궐; ) HANGUL SYLLABLE GWEOL
+AD91;AD91;1100 116F 11B0;AD91;1100 116F 11B0; # (궑; 궑; 궑; 궑; 궑; ) HANGUL SYLLABLE GWEOLG
+AD92;AD92;1100 116F 11B1;AD92;1100 116F 11B1; # (궒; 궒; 궒; 궒; 궒; ) HANGUL SYLLABLE GWEOLM
+AD93;AD93;1100 116F 11B2;AD93;1100 116F 11B2; # (궓; 궓; 궓; 궓; 궓; ) HANGUL SYLLABLE GWEOLB
+AD94;AD94;1100 116F 11B3;AD94;1100 116F 11B3; # (궔; 궔; 궔; 궔; 궔; ) HANGUL SYLLABLE GWEOLS
+AD95;AD95;1100 116F 11B4;AD95;1100 116F 11B4; # (궕; 궕; 궕; 궕; 궕; ) HANGUL SYLLABLE GWEOLT
+AD96;AD96;1100 116F 11B5;AD96;1100 116F 11B5; # (궖; 궖; 궖; 궖; 궖; ) HANGUL SYLLABLE GWEOLP
+AD97;AD97;1100 116F 11B6;AD97;1100 116F 11B6; # (궗; 궗; 궗; 궗; 궗; ) HANGUL SYLLABLE GWEOLH
+AD98;AD98;1100 116F 11B7;AD98;1100 116F 11B7; # (궘; 궘; 궘; 궘; 궘; ) HANGUL SYLLABLE GWEOM
+AD99;AD99;1100 116F 11B8;AD99;1100 116F 11B8; # (궙; 궙; 궙; 궙; 궙; ) HANGUL SYLLABLE GWEOB
+AD9A;AD9A;1100 116F 11B9;AD9A;1100 116F 11B9; # (궚; 궚; 궚; 궚; 궚; ) HANGUL SYLLABLE GWEOBS
+AD9B;AD9B;1100 116F 11BA;AD9B;1100 116F 11BA; # (궛; 궛; 궛; 궛; 궛; ) HANGUL SYLLABLE GWEOS
+AD9C;AD9C;1100 116F 11BB;AD9C;1100 116F 11BB; # (궜; 궜; 궜; 궜; 궜; ) HANGUL SYLLABLE GWEOSS
+AD9D;AD9D;1100 116F 11BC;AD9D;1100 116F 11BC; # (ê¶; ê¶; 궝; ê¶; 궝; ) HANGUL SYLLABLE GWEONG
+AD9E;AD9E;1100 116F 11BD;AD9E;1100 116F 11BD; # (궞; 궞; 궞; 궞; 궞; ) HANGUL SYLLABLE GWEOJ
+AD9F;AD9F;1100 116F 11BE;AD9F;1100 116F 11BE; # (궟; 궟; 궟; 궟; 궟; ) HANGUL SYLLABLE GWEOC
+ADA0;ADA0;1100 116F 11BF;ADA0;1100 116F 11BF; # (궠; 궠; 궠; 궠; 궠; ) HANGUL SYLLABLE GWEOK
+ADA1;ADA1;1100 116F 11C0;ADA1;1100 116F 11C0; # (궡; 궡; 궡; 궡; 궡; ) HANGUL SYLLABLE GWEOT
+ADA2;ADA2;1100 116F 11C1;ADA2;1100 116F 11C1; # (ê¶¢; ê¶¢; 궈á‡; ê¶¢; 궈á‡; ) HANGUL SYLLABLE GWEOP
+ADA3;ADA3;1100 116F 11C2;ADA3;1100 116F 11C2; # (궣; 궣; 궣; 궣; 궣; ) HANGUL SYLLABLE GWEOH
+ADA4;ADA4;1100 1170;ADA4;1100 1170; # (궤; 궤; 궤; 궤; 궤; ) HANGUL SYLLABLE GWE
+ADA5;ADA5;1100 1170 11A8;ADA5;1100 1170 11A8; # (궥; 궥; 궥; 궥; 궥; ) HANGUL SYLLABLE GWEG
+ADA6;ADA6;1100 1170 11A9;ADA6;1100 1170 11A9; # (궦; 궦; 궦; 궦; 궦; ) HANGUL SYLLABLE GWEGG
+ADA7;ADA7;1100 1170 11AA;ADA7;1100 1170 11AA; # (궧; 궧; 궧; 궧; 궧; ) HANGUL SYLLABLE GWEGS
+ADA8;ADA8;1100 1170 11AB;ADA8;1100 1170 11AB; # (궨; 궨; 궨; 궨; 궨; ) HANGUL SYLLABLE GWEN
+ADA9;ADA9;1100 1170 11AC;ADA9;1100 1170 11AC; # (궩; 궩; 궩; 궩; 궩; ) HANGUL SYLLABLE GWENJ
+ADAA;ADAA;1100 1170 11AD;ADAA;1100 1170 11AD; # (궪; 궪; 궪; 궪; 궪; ) HANGUL SYLLABLE GWENH
+ADAB;ADAB;1100 1170 11AE;ADAB;1100 1170 11AE; # (궫; 궫; 궫; 궫; 궫; ) HANGUL SYLLABLE GWED
+ADAC;ADAC;1100 1170 11AF;ADAC;1100 1170 11AF; # (궬; 궬; 궬; 궬; 궬; ) HANGUL SYLLABLE GWEL
+ADAD;ADAD;1100 1170 11B0;ADAD;1100 1170 11B0; # (궭; 궭; 궭; 궭; 궭; ) HANGUL SYLLABLE GWELG
+ADAE;ADAE;1100 1170 11B1;ADAE;1100 1170 11B1; # (궮; 궮; 궮; 궮; 궮; ) HANGUL SYLLABLE GWELM
+ADAF;ADAF;1100 1170 11B2;ADAF;1100 1170 11B2; # (궯; 궯; 궯; 궯; 궯; ) HANGUL SYLLABLE GWELB
+ADB0;ADB0;1100 1170 11B3;ADB0;1100 1170 11B3; # (궰; 궰; 궰; 궰; 궰; ) HANGUL SYLLABLE GWELS
+ADB1;ADB1;1100 1170 11B4;ADB1;1100 1170 11B4; # (궱; 궱; 궱; 궱; 궱; ) HANGUL SYLLABLE GWELT
+ADB2;ADB2;1100 1170 11B5;ADB2;1100 1170 11B5; # (궲; 궲; 궲; 궲; 궲; ) HANGUL SYLLABLE GWELP
+ADB3;ADB3;1100 1170 11B6;ADB3;1100 1170 11B6; # (궳; 궳; 궳; 궳; 궳; ) HANGUL SYLLABLE GWELH
+ADB4;ADB4;1100 1170 11B7;ADB4;1100 1170 11B7; # (궴; 궴; 궴; 궴; 궴; ) HANGUL SYLLABLE GWEM
+ADB5;ADB5;1100 1170 11B8;ADB5;1100 1170 11B8; # (궵; 궵; 궵; 궵; 궵; ) HANGUL SYLLABLE GWEB
+ADB6;ADB6;1100 1170 11B9;ADB6;1100 1170 11B9; # (궶; 궶; 궶; 궶; 궶; ) HANGUL SYLLABLE GWEBS
+ADB7;ADB7;1100 1170 11BA;ADB7;1100 1170 11BA; # (궷; 궷; 궷; 궷; 궷; ) HANGUL SYLLABLE GWES
+ADB8;ADB8;1100 1170 11BB;ADB8;1100 1170 11BB; # (궸; 궸; 궸; 궸; 궸; ) HANGUL SYLLABLE GWESS
+ADB9;ADB9;1100 1170 11BC;ADB9;1100 1170 11BC; # (궹; 궹; 궹; 궹; 궹; ) HANGUL SYLLABLE GWENG
+ADBA;ADBA;1100 1170 11BD;ADBA;1100 1170 11BD; # (궺; 궺; 궺; 궺; 궺; ) HANGUL SYLLABLE GWEJ
+ADBB;ADBB;1100 1170 11BE;ADBB;1100 1170 11BE; # (궻; 궻; 궻; 궻; 궻; ) HANGUL SYLLABLE GWEC
+ADBC;ADBC;1100 1170 11BF;ADBC;1100 1170 11BF; # (궼; 궼; 궼; 궼; 궼; ) HANGUL SYLLABLE GWEK
+ADBD;ADBD;1100 1170 11C0;ADBD;1100 1170 11C0; # (궽; 궽; 궽; 궽; 궽; ) HANGUL SYLLABLE GWET
+ADBE;ADBE;1100 1170 11C1;ADBE;1100 1170 11C1; # (ê¶¾; ê¶¾; 궤á‡; ê¶¾; 궤á‡; ) HANGUL SYLLABLE GWEP
+ADBF;ADBF;1100 1170 11C2;ADBF;1100 1170 11C2; # (궿; 궿; 궿; 궿; 궿; ) HANGUL SYLLABLE GWEH
+ADC0;ADC0;1100 1171;ADC0;1100 1171; # (귀; 귀; 귀; 귀; 귀; ) HANGUL SYLLABLE GWI
+ADC1;ADC1;1100 1171 11A8;ADC1;1100 1171 11A8; # (ê·; ê·; 귁; ê·; 귁; ) HANGUL SYLLABLE GWIG
+ADC2;ADC2;1100 1171 11A9;ADC2;1100 1171 11A9; # (귂; 귂; 귂; 귂; 귂; ) HANGUL SYLLABLE GWIGG
+ADC3;ADC3;1100 1171 11AA;ADC3;1100 1171 11AA; # (귃; 귃; 귃; 귃; 귃; ) HANGUL SYLLABLE GWIGS
+ADC4;ADC4;1100 1171 11AB;ADC4;1100 1171 11AB; # (귄; 귄; 귄; 귄; 귄; ) HANGUL SYLLABLE GWIN
+ADC5;ADC5;1100 1171 11AC;ADC5;1100 1171 11AC; # (귅; 귅; 귅; 귅; 귅; ) HANGUL SYLLABLE GWINJ
+ADC6;ADC6;1100 1171 11AD;ADC6;1100 1171 11AD; # (귆; 귆; 귆; 귆; 귆; ) HANGUL SYLLABLE GWINH
+ADC7;ADC7;1100 1171 11AE;ADC7;1100 1171 11AE; # (귇; 귇; 귇; 귇; 귇; ) HANGUL SYLLABLE GWID
+ADC8;ADC8;1100 1171 11AF;ADC8;1100 1171 11AF; # (귈; 귈; 귈; 귈; 귈; ) HANGUL SYLLABLE GWIL
+ADC9;ADC9;1100 1171 11B0;ADC9;1100 1171 11B0; # (귉; 귉; 귉; 귉; 귉; ) HANGUL SYLLABLE GWILG
+ADCA;ADCA;1100 1171 11B1;ADCA;1100 1171 11B1; # (귊; 귊; 귊; 귊; 귊; ) HANGUL SYLLABLE GWILM
+ADCB;ADCB;1100 1171 11B2;ADCB;1100 1171 11B2; # (귋; 귋; 귋; 귋; 귋; ) HANGUL SYLLABLE GWILB
+ADCC;ADCC;1100 1171 11B3;ADCC;1100 1171 11B3; # (귌; 귌; 귌; 귌; 귌; ) HANGUL SYLLABLE GWILS
+ADCD;ADCD;1100 1171 11B4;ADCD;1100 1171 11B4; # (ê·; ê·; 귍; ê·; 귍; ) HANGUL SYLLABLE GWILT
+ADCE;ADCE;1100 1171 11B5;ADCE;1100 1171 11B5; # (귎; 귎; 귎; 귎; 귎; ) HANGUL SYLLABLE GWILP
+ADCF;ADCF;1100 1171 11B6;ADCF;1100 1171 11B6; # (ê·; ê·; 귏; ê·; 귏; ) HANGUL SYLLABLE GWILH
+ADD0;ADD0;1100 1171 11B7;ADD0;1100 1171 11B7; # (ê·; ê·; 귐; ê·; 귐; ) HANGUL SYLLABLE GWIM
+ADD1;ADD1;1100 1171 11B8;ADD1;1100 1171 11B8; # (귑; 귑; 귑; 귑; 귑; ) HANGUL SYLLABLE GWIB
+ADD2;ADD2;1100 1171 11B9;ADD2;1100 1171 11B9; # (귒; 귒; 귒; 귒; 귒; ) HANGUL SYLLABLE GWIBS
+ADD3;ADD3;1100 1171 11BA;ADD3;1100 1171 11BA; # (귓; 귓; 귓; 귓; 귓; ) HANGUL SYLLABLE GWIS
+ADD4;ADD4;1100 1171 11BB;ADD4;1100 1171 11BB; # (귔; 귔; 귔; 귔; 귔; ) HANGUL SYLLABLE GWISS
+ADD5;ADD5;1100 1171 11BC;ADD5;1100 1171 11BC; # (귕; 귕; 귕; 귕; 귕; ) HANGUL SYLLABLE GWING
+ADD6;ADD6;1100 1171 11BD;ADD6;1100 1171 11BD; # (귖; 귖; 귖; 귖; 귖; ) HANGUL SYLLABLE GWIJ
+ADD7;ADD7;1100 1171 11BE;ADD7;1100 1171 11BE; # (귗; 귗; 귗; 귗; 귗; ) HANGUL SYLLABLE GWIC
+ADD8;ADD8;1100 1171 11BF;ADD8;1100 1171 11BF; # (귘; 귘; 귘; 귘; 귘; ) HANGUL SYLLABLE GWIK
+ADD9;ADD9;1100 1171 11C0;ADD9;1100 1171 11C0; # (귙; 귙; 귙; 귙; 귙; ) HANGUL SYLLABLE GWIT
+ADDA;ADDA;1100 1171 11C1;ADDA;1100 1171 11C1; # (ê·š; ê·š; 귀á‡; ê·š; 귀á‡; ) HANGUL SYLLABLE GWIP
+ADDB;ADDB;1100 1171 11C2;ADDB;1100 1171 11C2; # (귛; 귛; 귛; 귛; 귛; ) HANGUL SYLLABLE GWIH
+ADDC;ADDC;1100 1172;ADDC;1100 1172; # (규; 규; 규; 규; 규; ) HANGUL SYLLABLE GYU
+ADDD;ADDD;1100 1172 11A8;ADDD;1100 1172 11A8; # (ê·; ê·; 귝; ê·; 귝; ) HANGUL SYLLABLE GYUG
+ADDE;ADDE;1100 1172 11A9;ADDE;1100 1172 11A9; # (귞; 귞; 귞; 귞; 귞; ) HANGUL SYLLABLE GYUGG
+ADDF;ADDF;1100 1172 11AA;ADDF;1100 1172 11AA; # (귟; 귟; 귟; 귟; 귟; ) HANGUL SYLLABLE GYUGS
+ADE0;ADE0;1100 1172 11AB;ADE0;1100 1172 11AB; # (균; 균; 균; 균; 균; ) HANGUL SYLLABLE GYUN
+ADE1;ADE1;1100 1172 11AC;ADE1;1100 1172 11AC; # (귡; 귡; 귡; 귡; 귡; ) HANGUL SYLLABLE GYUNJ
+ADE2;ADE2;1100 1172 11AD;ADE2;1100 1172 11AD; # (귢; 귢; 귢; 귢; 귢; ) HANGUL SYLLABLE GYUNH
+ADE3;ADE3;1100 1172 11AE;ADE3;1100 1172 11AE; # (귣; 귣; 귣; 귣; 귣; ) HANGUL SYLLABLE GYUD
+ADE4;ADE4;1100 1172 11AF;ADE4;1100 1172 11AF; # (귤; 귤; 귤; 귤; 귤; ) HANGUL SYLLABLE GYUL
+ADE5;ADE5;1100 1172 11B0;ADE5;1100 1172 11B0; # (귥; 귥; 귥; 귥; 귥; ) HANGUL SYLLABLE GYULG
+ADE6;ADE6;1100 1172 11B1;ADE6;1100 1172 11B1; # (귦; 귦; 귦; 귦; 귦; ) HANGUL SYLLABLE GYULM
+ADE7;ADE7;1100 1172 11B2;ADE7;1100 1172 11B2; # (귧; 귧; 귧; 귧; 귧; ) HANGUL SYLLABLE GYULB
+ADE8;ADE8;1100 1172 11B3;ADE8;1100 1172 11B3; # (귨; 귨; 귨; 귨; 귨; ) HANGUL SYLLABLE GYULS
+ADE9;ADE9;1100 1172 11B4;ADE9;1100 1172 11B4; # (귩; 귩; 귩; 귩; 귩; ) HANGUL SYLLABLE GYULT
+ADEA;ADEA;1100 1172 11B5;ADEA;1100 1172 11B5; # (귪; 귪; 귪; 귪; 귪; ) HANGUL SYLLABLE GYULP
+ADEB;ADEB;1100 1172 11B6;ADEB;1100 1172 11B6; # (귫; 귫; 귫; 귫; 귫; ) HANGUL SYLLABLE GYULH
+ADEC;ADEC;1100 1172 11B7;ADEC;1100 1172 11B7; # (귬; 귬; 귬; 귬; 귬; ) HANGUL SYLLABLE GYUM
+ADED;ADED;1100 1172 11B8;ADED;1100 1172 11B8; # (귭; 귭; 귭; 귭; 귭; ) HANGUL SYLLABLE GYUB
+ADEE;ADEE;1100 1172 11B9;ADEE;1100 1172 11B9; # (귮; 귮; 귮; 귮; 귮; ) HANGUL SYLLABLE GYUBS
+ADEF;ADEF;1100 1172 11BA;ADEF;1100 1172 11BA; # (귯; 귯; 귯; 귯; 귯; ) HANGUL SYLLABLE GYUS
+ADF0;ADF0;1100 1172 11BB;ADF0;1100 1172 11BB; # (귰; 귰; 귰; 귰; 귰; ) HANGUL SYLLABLE GYUSS
+ADF1;ADF1;1100 1172 11BC;ADF1;1100 1172 11BC; # (귱; 귱; 귱; 귱; 귱; ) HANGUL SYLLABLE GYUNG
+ADF2;ADF2;1100 1172 11BD;ADF2;1100 1172 11BD; # (귲; 귲; 귲; 귲; 귲; ) HANGUL SYLLABLE GYUJ
+ADF3;ADF3;1100 1172 11BE;ADF3;1100 1172 11BE; # (귳; 귳; 귳; 귳; 귳; ) HANGUL SYLLABLE GYUC
+ADF4;ADF4;1100 1172 11BF;ADF4;1100 1172 11BF; # (귴; 귴; 귴; 귴; 귴; ) HANGUL SYLLABLE GYUK
+ADF5;ADF5;1100 1172 11C0;ADF5;1100 1172 11C0; # (귵; 귵; 귵; 귵; 귵; ) HANGUL SYLLABLE GYUT
+ADF6;ADF6;1100 1172 11C1;ADF6;1100 1172 11C1; # (ê·¶; ê·¶; 규á‡; ê·¶; 규á‡; ) HANGUL SYLLABLE GYUP
+ADF7;ADF7;1100 1172 11C2;ADF7;1100 1172 11C2; # (귷; 귷; 귷; 귷; 귷; ) HANGUL SYLLABLE GYUH
+ADF8;ADF8;1100 1173;ADF8;1100 1173; # (그; 그; 그; 그; 그; ) HANGUL SYLLABLE GEU
+ADF9;ADF9;1100 1173 11A8;ADF9;1100 1173 11A8; # (극; 극; 극; 극; 극; ) HANGUL SYLLABLE GEUG
+ADFA;ADFA;1100 1173 11A9;ADFA;1100 1173 11A9; # (귺; 귺; 귺; 귺; 귺; ) HANGUL SYLLABLE GEUGG
+ADFB;ADFB;1100 1173 11AA;ADFB;1100 1173 11AA; # (귻; 귻; 귻; 귻; 귻; ) HANGUL SYLLABLE GEUGS
+ADFC;ADFC;1100 1173 11AB;ADFC;1100 1173 11AB; # (근; 근; 근; 근; 근; ) HANGUL SYLLABLE GEUN
+ADFD;ADFD;1100 1173 11AC;ADFD;1100 1173 11AC; # (귽; 귽; 귽; 귽; 귽; ) HANGUL SYLLABLE GEUNJ
+ADFE;ADFE;1100 1173 11AD;ADFE;1100 1173 11AD; # (귾; 귾; 귾; 귾; 귾; ) HANGUL SYLLABLE GEUNH
+ADFF;ADFF;1100 1173 11AE;ADFF;1100 1173 11AE; # (귿; 귿; 귿; 귿; 귿; ) HANGUL SYLLABLE GEUD
+AE00;AE00;1100 1173 11AF;AE00;1100 1173 11AF; # (글; 글; 글; 글; 글; ) HANGUL SYLLABLE GEUL
+AE01;AE01;1100 1173 11B0;AE01;1100 1173 11B0; # (ê¸; ê¸; 긁; ê¸; 긁; ) HANGUL SYLLABLE GEULG
+AE02;AE02;1100 1173 11B1;AE02;1100 1173 11B1; # (긂; 긂; 긂; 긂; 긂; ) HANGUL SYLLABLE GEULM
+AE03;AE03;1100 1173 11B2;AE03;1100 1173 11B2; # (긃; 긃; 긃; 긃; 긃; ) HANGUL SYLLABLE GEULB
+AE04;AE04;1100 1173 11B3;AE04;1100 1173 11B3; # (긄; 긄; 긄; 긄; 긄; ) HANGUL SYLLABLE GEULS
+AE05;AE05;1100 1173 11B4;AE05;1100 1173 11B4; # (긅; 긅; 긅; 긅; 긅; ) HANGUL SYLLABLE GEULT
+AE06;AE06;1100 1173 11B5;AE06;1100 1173 11B5; # (긆; 긆; 긆; 긆; 긆; ) HANGUL SYLLABLE GEULP
+AE07;AE07;1100 1173 11B6;AE07;1100 1173 11B6; # (긇; 긇; 긇; 긇; 긇; ) HANGUL SYLLABLE GEULH
+AE08;AE08;1100 1173 11B7;AE08;1100 1173 11B7; # (금; 금; 금; 금; 금; ) HANGUL SYLLABLE GEUM
+AE09;AE09;1100 1173 11B8;AE09;1100 1173 11B8; # (급; 급; 급; 급; 급; ) HANGUL SYLLABLE GEUB
+AE0A;AE0A;1100 1173 11B9;AE0A;1100 1173 11B9; # (긊; 긊; 긊; 긊; 긊; ) HANGUL SYLLABLE GEUBS
+AE0B;AE0B;1100 1173 11BA;AE0B;1100 1173 11BA; # (긋; 긋; 긋; 긋; 긋; ) HANGUL SYLLABLE GEUS
+AE0C;AE0C;1100 1173 11BB;AE0C;1100 1173 11BB; # (긌; 긌; 긌; 긌; 긌; ) HANGUL SYLLABLE GEUSS
+AE0D;AE0D;1100 1173 11BC;AE0D;1100 1173 11BC; # (ê¸; ê¸; 긍; ê¸; 긍; ) HANGUL SYLLABLE GEUNG
+AE0E;AE0E;1100 1173 11BD;AE0E;1100 1173 11BD; # (긎; 긎; 긎; 긎; 긎; ) HANGUL SYLLABLE GEUJ
+AE0F;AE0F;1100 1173 11BE;AE0F;1100 1173 11BE; # (ê¸; ê¸; 긏; ê¸; 긏; ) HANGUL SYLLABLE GEUC
+AE10;AE10;1100 1173 11BF;AE10;1100 1173 11BF; # (ê¸; ê¸; 긐; ê¸; 긐; ) HANGUL SYLLABLE GEUK
+AE11;AE11;1100 1173 11C0;AE11;1100 1173 11C0; # (긑; 긑; 긑; 긑; 긑; ) HANGUL SYLLABLE GEUT
+AE12;AE12;1100 1173 11C1;AE12;1100 1173 11C1; # (긒; 긒; 그á‡; 긒; 그á‡; ) HANGUL SYLLABLE GEUP
+AE13;AE13;1100 1173 11C2;AE13;1100 1173 11C2; # (긓; 긓; 긓; 긓; 긓; ) HANGUL SYLLABLE GEUH
+AE14;AE14;1100 1174;AE14;1100 1174; # (긔; 긔; 긔; 긔; 긔; ) HANGUL SYLLABLE GYI
+AE15;AE15;1100 1174 11A8;AE15;1100 1174 11A8; # (긕; 긕; 긕; 긕; 긕; ) HANGUL SYLLABLE GYIG
+AE16;AE16;1100 1174 11A9;AE16;1100 1174 11A9; # (긖; 긖; 긖; 긖; 긖; ) HANGUL SYLLABLE GYIGG
+AE17;AE17;1100 1174 11AA;AE17;1100 1174 11AA; # (긗; 긗; 긗; 긗; 긗; ) HANGUL SYLLABLE GYIGS
+AE18;AE18;1100 1174 11AB;AE18;1100 1174 11AB; # (긘; 긘; 긘; 긘; 긘; ) HANGUL SYLLABLE GYIN
+AE19;AE19;1100 1174 11AC;AE19;1100 1174 11AC; # (긙; 긙; 긙; 긙; 긙; ) HANGUL SYLLABLE GYINJ
+AE1A;AE1A;1100 1174 11AD;AE1A;1100 1174 11AD; # (긚; 긚; 긚; 긚; 긚; ) HANGUL SYLLABLE GYINH
+AE1B;AE1B;1100 1174 11AE;AE1B;1100 1174 11AE; # (긛; 긛; 긛; 긛; 긛; ) HANGUL SYLLABLE GYID
+AE1C;AE1C;1100 1174 11AF;AE1C;1100 1174 11AF; # (긜; 긜; 긜; 긜; 긜; ) HANGUL SYLLABLE GYIL
+AE1D;AE1D;1100 1174 11B0;AE1D;1100 1174 11B0; # (ê¸; ê¸; 긝; ê¸; 긝; ) HANGUL SYLLABLE GYILG
+AE1E;AE1E;1100 1174 11B1;AE1E;1100 1174 11B1; # (긞; 긞; 긞; 긞; 긞; ) HANGUL SYLLABLE GYILM
+AE1F;AE1F;1100 1174 11B2;AE1F;1100 1174 11B2; # (긟; 긟; 긟; 긟; 긟; ) HANGUL SYLLABLE GYILB
+AE20;AE20;1100 1174 11B3;AE20;1100 1174 11B3; # (긠; 긠; 긠; 긠; 긠; ) HANGUL SYLLABLE GYILS
+AE21;AE21;1100 1174 11B4;AE21;1100 1174 11B4; # (긡; 긡; 긡; 긡; 긡; ) HANGUL SYLLABLE GYILT
+AE22;AE22;1100 1174 11B5;AE22;1100 1174 11B5; # (긢; 긢; 긢; 긢; 긢; ) HANGUL SYLLABLE GYILP
+AE23;AE23;1100 1174 11B6;AE23;1100 1174 11B6; # (긣; 긣; 긣; 긣; 긣; ) HANGUL SYLLABLE GYILH
+AE24;AE24;1100 1174 11B7;AE24;1100 1174 11B7; # (긤; 긤; 긤; 긤; 긤; ) HANGUL SYLLABLE GYIM
+AE25;AE25;1100 1174 11B8;AE25;1100 1174 11B8; # (긥; 긥; 긥; 긥; 긥; ) HANGUL SYLLABLE GYIB
+AE26;AE26;1100 1174 11B9;AE26;1100 1174 11B9; # (긦; 긦; 긦; 긦; 긦; ) HANGUL SYLLABLE GYIBS
+AE27;AE27;1100 1174 11BA;AE27;1100 1174 11BA; # (긧; 긧; 긧; 긧; 긧; ) HANGUL SYLLABLE GYIS
+AE28;AE28;1100 1174 11BB;AE28;1100 1174 11BB; # (긨; 긨; 긨; 긨; 긨; ) HANGUL SYLLABLE GYISS
+AE29;AE29;1100 1174 11BC;AE29;1100 1174 11BC; # (긩; 긩; 긩; 긩; 긩; ) HANGUL SYLLABLE GYING
+AE2A;AE2A;1100 1174 11BD;AE2A;1100 1174 11BD; # (긪; 긪; 긪; 긪; 긪; ) HANGUL SYLLABLE GYIJ
+AE2B;AE2B;1100 1174 11BE;AE2B;1100 1174 11BE; # (긫; 긫; 긫; 긫; 긫; ) HANGUL SYLLABLE GYIC
+AE2C;AE2C;1100 1174 11BF;AE2C;1100 1174 11BF; # (긬; 긬; 긬; 긬; 긬; ) HANGUL SYLLABLE GYIK
+AE2D;AE2D;1100 1174 11C0;AE2D;1100 1174 11C0; # (긭; 긭; 긭; 긭; 긭; ) HANGUL SYLLABLE GYIT
+AE2E;AE2E;1100 1174 11C1;AE2E;1100 1174 11C1; # (긮; 긮; 긔á‡; 긮; 긔á‡; ) HANGUL SYLLABLE GYIP
+AE2F;AE2F;1100 1174 11C2;AE2F;1100 1174 11C2; # (긯; 긯; 긯; 긯; 긯; ) HANGUL SYLLABLE GYIH
+AE30;AE30;1100 1175;AE30;1100 1175; # (기; 기; 기; 기; 기; ) HANGUL SYLLABLE GI
+AE31;AE31;1100 1175 11A8;AE31;1100 1175 11A8; # (긱; 긱; 긱; 긱; 긱; ) HANGUL SYLLABLE GIG
+AE32;AE32;1100 1175 11A9;AE32;1100 1175 11A9; # (긲; 긲; 긲; 긲; 긲; ) HANGUL SYLLABLE GIGG
+AE33;AE33;1100 1175 11AA;AE33;1100 1175 11AA; # (긳; 긳; 긳; 긳; 긳; ) HANGUL SYLLABLE GIGS
+AE34;AE34;1100 1175 11AB;AE34;1100 1175 11AB; # (긴; 긴; 긴; 긴; 긴; ) HANGUL SYLLABLE GIN
+AE35;AE35;1100 1175 11AC;AE35;1100 1175 11AC; # (긵; 긵; 긵; 긵; 긵; ) HANGUL SYLLABLE GINJ
+AE36;AE36;1100 1175 11AD;AE36;1100 1175 11AD; # (긶; 긶; 긶; 긶; 긶; ) HANGUL SYLLABLE GINH
+AE37;AE37;1100 1175 11AE;AE37;1100 1175 11AE; # (긷; 긷; 긷; 긷; 긷; ) HANGUL SYLLABLE GID
+AE38;AE38;1100 1175 11AF;AE38;1100 1175 11AF; # (길; 길; 길; 길; 길; ) HANGUL SYLLABLE GIL
+AE39;AE39;1100 1175 11B0;AE39;1100 1175 11B0; # (긹; 긹; 긹; 긹; 긹; ) HANGUL SYLLABLE GILG
+AE3A;AE3A;1100 1175 11B1;AE3A;1100 1175 11B1; # (긺; 긺; 긺; 긺; 긺; ) HANGUL SYLLABLE GILM
+AE3B;AE3B;1100 1175 11B2;AE3B;1100 1175 11B2; # (긻; 긻; 긻; 긻; 긻; ) HANGUL SYLLABLE GILB
+AE3C;AE3C;1100 1175 11B3;AE3C;1100 1175 11B3; # (긼; 긼; 긼; 긼; 긼; ) HANGUL SYLLABLE GILS
+AE3D;AE3D;1100 1175 11B4;AE3D;1100 1175 11B4; # (긽; 긽; 긽; 긽; 긽; ) HANGUL SYLLABLE GILT
+AE3E;AE3E;1100 1175 11B5;AE3E;1100 1175 11B5; # (긾; 긾; 긾; 긾; 긾; ) HANGUL SYLLABLE GILP
+AE3F;AE3F;1100 1175 11B6;AE3F;1100 1175 11B6; # (긿; 긿; 긿; 긿; 긿; ) HANGUL SYLLABLE GILH
+AE40;AE40;1100 1175 11B7;AE40;1100 1175 11B7; # (김; 김; 김; 김; 김; ) HANGUL SYLLABLE GIM
+AE41;AE41;1100 1175 11B8;AE41;1100 1175 11B8; # (ê¹; ê¹; 깁; ê¹; 깁; ) HANGUL SYLLABLE GIB
+AE42;AE42;1100 1175 11B9;AE42;1100 1175 11B9; # (깂; 깂; 깂; 깂; 깂; ) HANGUL SYLLABLE GIBS
+AE43;AE43;1100 1175 11BA;AE43;1100 1175 11BA; # (깃; 깃; 깃; 깃; 깃; ) HANGUL SYLLABLE GIS
+AE44;AE44;1100 1175 11BB;AE44;1100 1175 11BB; # (깄; 깄; 깄; 깄; 깄; ) HANGUL SYLLABLE GISS
+AE45;AE45;1100 1175 11BC;AE45;1100 1175 11BC; # (깅; 깅; 깅; 깅; 깅; ) HANGUL SYLLABLE GING
+AE46;AE46;1100 1175 11BD;AE46;1100 1175 11BD; # (깆; 깆; 깆; 깆; 깆; ) HANGUL SYLLABLE GIJ
+AE47;AE47;1100 1175 11BE;AE47;1100 1175 11BE; # (깇; 깇; 깇; 깇; 깇; ) HANGUL SYLLABLE GIC
+AE48;AE48;1100 1175 11BF;AE48;1100 1175 11BF; # (깈; 깈; 깈; 깈; 깈; ) HANGUL SYLLABLE GIK
+AE49;AE49;1100 1175 11C0;AE49;1100 1175 11C0; # (깉; 깉; 깉; 깉; 깉; ) HANGUL SYLLABLE GIT
+AE4A;AE4A;1100 1175 11C1;AE4A;1100 1175 11C1; # (깊; 깊; 기á‡; 깊; 기á‡; ) HANGUL SYLLABLE GIP
+AE4B;AE4B;1100 1175 11C2;AE4B;1100 1175 11C2; # (깋; 깋; 깋; 깋; 깋; ) HANGUL SYLLABLE GIH
+AE4C;AE4C;1101 1161;AE4C;1101 1161; # (까; 까; á„á…¡; 까; á„á…¡; ) HANGUL SYLLABLE GGA
+AE4D;AE4D;1101 1161 11A8;AE4D;1101 1161 11A8; # (ê¹; ê¹; á„ᅡᆨ; ê¹; á„ᅡᆨ; ) HANGUL SYLLABLE GGAG
+AE4E;AE4E;1101 1161 11A9;AE4E;1101 1161 11A9; # (깎; 깎; á„ᅡᆩ; 깎; á„ᅡᆩ; ) HANGUL SYLLABLE GGAGG
+AE4F;AE4F;1101 1161 11AA;AE4F;1101 1161 11AA; # (ê¹; ê¹; á„ᅡᆪ; ê¹; á„ᅡᆪ; ) HANGUL SYLLABLE GGAGS
+AE50;AE50;1101 1161 11AB;AE50;1101 1161 11AB; # (ê¹; ê¹; á„ᅡᆫ; ê¹; á„ᅡᆫ; ) HANGUL SYLLABLE GGAN
+AE51;AE51;1101 1161 11AC;AE51;1101 1161 11AC; # (깑; 깑; á„ᅡᆬ; 깑; á„ᅡᆬ; ) HANGUL SYLLABLE GGANJ
+AE52;AE52;1101 1161 11AD;AE52;1101 1161 11AD; # (ê¹’; ê¹’; á„ᅡᆭ; ê¹’; á„ᅡᆭ; ) HANGUL SYLLABLE GGANH
+AE53;AE53;1101 1161 11AE;AE53;1101 1161 11AE; # (깓; 깓; á„ᅡᆮ; 깓; á„ᅡᆮ; ) HANGUL SYLLABLE GGAD
+AE54;AE54;1101 1161 11AF;AE54;1101 1161 11AF; # (ê¹”; ê¹”; á„ᅡᆯ; ê¹”; á„ᅡᆯ; ) HANGUL SYLLABLE GGAL
+AE55;AE55;1101 1161 11B0;AE55;1101 1161 11B0; # (깕; 깕; á„ᅡᆰ; 깕; á„ᅡᆰ; ) HANGUL SYLLABLE GGALG
+AE56;AE56;1101 1161 11B1;AE56;1101 1161 11B1; # (ê¹–; ê¹–; á„ᅡᆱ; ê¹–; á„ᅡᆱ; ) HANGUL SYLLABLE GGALM
+AE57;AE57;1101 1161 11B2;AE57;1101 1161 11B2; # (ê¹—; ê¹—; á„ᅡᆲ; ê¹—; á„ᅡᆲ; ) HANGUL SYLLABLE GGALB
+AE58;AE58;1101 1161 11B3;AE58;1101 1161 11B3; # (깘; 깘; á„ᅡᆳ; 깘; á„ᅡᆳ; ) HANGUL SYLLABLE GGALS
+AE59;AE59;1101 1161 11B4;AE59;1101 1161 11B4; # (ê¹™; ê¹™; á„ᅡᆴ; ê¹™; á„ᅡᆴ; ) HANGUL SYLLABLE GGALT
+AE5A;AE5A;1101 1161 11B5;AE5A;1101 1161 11B5; # (깚; 깚; á„ᅡᆵ; 깚; á„ᅡᆵ; ) HANGUL SYLLABLE GGALP
+AE5B;AE5B;1101 1161 11B6;AE5B;1101 1161 11B6; # (ê¹›; ê¹›; á„ᅡᆶ; ê¹›; á„ᅡᆶ; ) HANGUL SYLLABLE GGALH
+AE5C;AE5C;1101 1161 11B7;AE5C;1101 1161 11B7; # (깜; 깜; á„ᅡᆷ; 깜; á„ᅡᆷ; ) HANGUL SYLLABLE GGAM
+AE5D;AE5D;1101 1161 11B8;AE5D;1101 1161 11B8; # (ê¹; ê¹; á„ᅡᆸ; ê¹; á„ᅡᆸ; ) HANGUL SYLLABLE GGAB
+AE5E;AE5E;1101 1161 11B9;AE5E;1101 1161 11B9; # (깞; 깞; á„ᅡᆹ; 깞; á„ᅡᆹ; ) HANGUL SYLLABLE GGABS
+AE5F;AE5F;1101 1161 11BA;AE5F;1101 1161 11BA; # (깟; 깟; á„ᅡᆺ; 깟; á„ᅡᆺ; ) HANGUL SYLLABLE GGAS
+AE60;AE60;1101 1161 11BB;AE60;1101 1161 11BB; # (ê¹ ; ê¹ ; á„ᅡᆻ; ê¹ ; á„ᅡᆻ; ) HANGUL SYLLABLE GGASS
+AE61;AE61;1101 1161 11BC;AE61;1101 1161 11BC; # (깡; 깡; á„ᅡᆼ; 깡; á„ᅡᆼ; ) HANGUL SYLLABLE GGANG
+AE62;AE62;1101 1161 11BD;AE62;1101 1161 11BD; # (ê¹¢; ê¹¢; á„ᅡᆽ; ê¹¢; á„ᅡᆽ; ) HANGUL SYLLABLE GGAJ
+AE63;AE63;1101 1161 11BE;AE63;1101 1161 11BE; # (ê¹£; ê¹£; á„ᅡᆾ; ê¹£; á„ᅡᆾ; ) HANGUL SYLLABLE GGAC
+AE64;AE64;1101 1161 11BF;AE64;1101 1161 11BF; # (깤; 깤; á„ᅡᆿ; 깤; á„ᅡᆿ; ) HANGUL SYLLABLE GGAK
+AE65;AE65;1101 1161 11C0;AE65;1101 1161 11C0; # (ê¹¥; ê¹¥; á„ᅡᇀ; ê¹¥; á„ᅡᇀ; ) HANGUL SYLLABLE GGAT
+AE66;AE66;1101 1161 11C1;AE66;1101 1161 11C1; # (깦; 깦; á„á…¡á‡; 깦; á„á…¡á‡; ) HANGUL SYLLABLE GGAP
+AE67;AE67;1101 1161 11C2;AE67;1101 1161 11C2; # (ê¹§; ê¹§; á„ᅡᇂ; ê¹§; á„ᅡᇂ; ) HANGUL SYLLABLE GGAH
+AE68;AE68;1101 1162;AE68;1101 1162; # (깨; 깨; á„á…¢; 깨; á„á…¢; ) HANGUL SYLLABLE GGAE
+AE69;AE69;1101 1162 11A8;AE69;1101 1162 11A8; # (깩; 깩; á„ᅢᆨ; 깩; á„ᅢᆨ; ) HANGUL SYLLABLE GGAEG
+AE6A;AE6A;1101 1162 11A9;AE6A;1101 1162 11A9; # (깪; 깪; á„ᅢᆩ; 깪; á„ᅢᆩ; ) HANGUL SYLLABLE GGAEGG
+AE6B;AE6B;1101 1162 11AA;AE6B;1101 1162 11AA; # (깫; 깫; á„ᅢᆪ; 깫; á„ᅢᆪ; ) HANGUL SYLLABLE GGAEGS
+AE6C;AE6C;1101 1162 11AB;AE6C;1101 1162 11AB; # (깬; 깬; á„ᅢᆫ; 깬; á„ᅢᆫ; ) HANGUL SYLLABLE GGAEN
+AE6D;AE6D;1101 1162 11AC;AE6D;1101 1162 11AC; # (ê¹­; ê¹­; á„ᅢᆬ; ê¹­; á„ᅢᆬ; ) HANGUL SYLLABLE GGAENJ
+AE6E;AE6E;1101 1162 11AD;AE6E;1101 1162 11AD; # (ê¹®; ê¹®; á„ᅢᆭ; ê¹®; á„ᅢᆭ; ) HANGUL SYLLABLE GGAENH
+AE6F;AE6F;1101 1162 11AE;AE6F;1101 1162 11AE; # (깯; 깯; á„ᅢᆮ; 깯; á„ᅢᆮ; ) HANGUL SYLLABLE GGAED
+AE70;AE70;1101 1162 11AF;AE70;1101 1162 11AF; # (ê¹°; ê¹°; á„ᅢᆯ; ê¹°; á„ᅢᆯ; ) HANGUL SYLLABLE GGAEL
+AE71;AE71;1101 1162 11B0;AE71;1101 1162 11B0; # (ê¹±; ê¹±; á„ᅢᆰ; ê¹±; á„ᅢᆰ; ) HANGUL SYLLABLE GGAELG
+AE72;AE72;1101 1162 11B1;AE72;1101 1162 11B1; # (ê¹²; ê¹²; á„ᅢᆱ; ê¹²; á„ᅢᆱ; ) HANGUL SYLLABLE GGAELM
+AE73;AE73;1101 1162 11B2;AE73;1101 1162 11B2; # (ê¹³; ê¹³; á„ᅢᆲ; ê¹³; á„ᅢᆲ; ) HANGUL SYLLABLE GGAELB
+AE74;AE74;1101 1162 11B3;AE74;1101 1162 11B3; # (ê¹´; ê¹´; á„ᅢᆳ; ê¹´; á„ᅢᆳ; ) HANGUL SYLLABLE GGAELS
+AE75;AE75;1101 1162 11B4;AE75;1101 1162 11B4; # (ê¹µ; ê¹µ; á„ᅢᆴ; ê¹µ; á„ᅢᆴ; ) HANGUL SYLLABLE GGAELT
+AE76;AE76;1101 1162 11B5;AE76;1101 1162 11B5; # (ê¹¶; ê¹¶; á„ᅢᆵ; ê¹¶; á„ᅢᆵ; ) HANGUL SYLLABLE GGAELP
+AE77;AE77;1101 1162 11B6;AE77;1101 1162 11B6; # (ê¹·; ê¹·; á„ᅢᆶ; ê¹·; á„ᅢᆶ; ) HANGUL SYLLABLE GGAELH
+AE78;AE78;1101 1162 11B7;AE78;1101 1162 11B7; # (깸; 깸; á„ᅢᆷ; 깸; á„ᅢᆷ; ) HANGUL SYLLABLE GGAEM
+AE79;AE79;1101 1162 11B8;AE79;1101 1162 11B8; # (ê¹¹; ê¹¹; á„ᅢᆸ; ê¹¹; á„ᅢᆸ; ) HANGUL SYLLABLE GGAEB
+AE7A;AE7A;1101 1162 11B9;AE7A;1101 1162 11B9; # (깺; 깺; á„ᅢᆹ; 깺; á„ᅢᆹ; ) HANGUL SYLLABLE GGAEBS
+AE7B;AE7B;1101 1162 11BA;AE7B;1101 1162 11BA; # (ê¹»; ê¹»; á„ᅢᆺ; ê¹»; á„ᅢᆺ; ) HANGUL SYLLABLE GGAES
+AE7C;AE7C;1101 1162 11BB;AE7C;1101 1162 11BB; # (ê¹¼; ê¹¼; á„ᅢᆻ; ê¹¼; á„ᅢᆻ; ) HANGUL SYLLABLE GGAESS
+AE7D;AE7D;1101 1162 11BC;AE7D;1101 1162 11BC; # (ê¹½; ê¹½; á„ᅢᆼ; ê¹½; á„ᅢᆼ; ) HANGUL SYLLABLE GGAENG
+AE7E;AE7E;1101 1162 11BD;AE7E;1101 1162 11BD; # (ê¹¾; ê¹¾; á„ᅢᆽ; ê¹¾; á„ᅢᆽ; ) HANGUL SYLLABLE GGAEJ
+AE7F;AE7F;1101 1162 11BE;AE7F;1101 1162 11BE; # (깿; 깿; á„ᅢᆾ; 깿; á„ᅢᆾ; ) HANGUL SYLLABLE GGAEC
+AE80;AE80;1101 1162 11BF;AE80;1101 1162 11BF; # (꺀; 꺀; á„ᅢᆿ; 꺀; á„ᅢᆿ; ) HANGUL SYLLABLE GGAEK
+AE81;AE81;1101 1162 11C0;AE81;1101 1162 11C0; # (êº; êº; á„ᅢᇀ; êº; á„ᅢᇀ; ) HANGUL SYLLABLE GGAET
+AE82;AE82;1101 1162 11C1;AE82;1101 1162 11C1; # (꺂; 꺂; á„á…¢á‡; 꺂; á„á…¢á‡; ) HANGUL SYLLABLE GGAEP
+AE83;AE83;1101 1162 11C2;AE83;1101 1162 11C2; # (꺃; 꺃; á„ᅢᇂ; 꺃; á„ᅢᇂ; ) HANGUL SYLLABLE GGAEH
+AE84;AE84;1101 1163;AE84;1101 1163; # (꺄; 꺄; á„á…£; 꺄; á„á…£; ) HANGUL SYLLABLE GGYA
+AE85;AE85;1101 1163 11A8;AE85;1101 1163 11A8; # (꺅; 꺅; á„ᅣᆨ; 꺅; á„ᅣᆨ; ) HANGUL SYLLABLE GGYAG
+AE86;AE86;1101 1163 11A9;AE86;1101 1163 11A9; # (꺆; 꺆; á„ᅣᆩ; 꺆; á„ᅣᆩ; ) HANGUL SYLLABLE GGYAGG
+AE87;AE87;1101 1163 11AA;AE87;1101 1163 11AA; # (꺇; 꺇; á„ᅣᆪ; 꺇; á„ᅣᆪ; ) HANGUL SYLLABLE GGYAGS
+AE88;AE88;1101 1163 11AB;AE88;1101 1163 11AB; # (꺈; 꺈; á„ᅣᆫ; 꺈; á„ᅣᆫ; ) HANGUL SYLLABLE GGYAN
+AE89;AE89;1101 1163 11AC;AE89;1101 1163 11AC; # (꺉; 꺉; á„ᅣᆬ; 꺉; á„ᅣᆬ; ) HANGUL SYLLABLE GGYANJ
+AE8A;AE8A;1101 1163 11AD;AE8A;1101 1163 11AD; # (꺊; 꺊; á„ᅣᆭ; 꺊; á„ᅣᆭ; ) HANGUL SYLLABLE GGYANH
+AE8B;AE8B;1101 1163 11AE;AE8B;1101 1163 11AE; # (꺋; 꺋; á„ᅣᆮ; 꺋; á„ᅣᆮ; ) HANGUL SYLLABLE GGYAD
+AE8C;AE8C;1101 1163 11AF;AE8C;1101 1163 11AF; # (꺌; 꺌; á„ᅣᆯ; 꺌; á„ᅣᆯ; ) HANGUL SYLLABLE GGYAL
+AE8D;AE8D;1101 1163 11B0;AE8D;1101 1163 11B0; # (êº; êº; á„ᅣᆰ; êº; á„ᅣᆰ; ) HANGUL SYLLABLE GGYALG
+AE8E;AE8E;1101 1163 11B1;AE8E;1101 1163 11B1; # (꺎; 꺎; á„ᅣᆱ; 꺎; á„ᅣᆱ; ) HANGUL SYLLABLE GGYALM
+AE8F;AE8F;1101 1163 11B2;AE8F;1101 1163 11B2; # (êº; êº; á„ᅣᆲ; êº; á„ᅣᆲ; ) HANGUL SYLLABLE GGYALB
+AE90;AE90;1101 1163 11B3;AE90;1101 1163 11B3; # (êº; êº; á„ᅣᆳ; êº; á„ᅣᆳ; ) HANGUL SYLLABLE GGYALS
+AE91;AE91;1101 1163 11B4;AE91;1101 1163 11B4; # (꺑; 꺑; á„ᅣᆴ; 꺑; á„ᅣᆴ; ) HANGUL SYLLABLE GGYALT
+AE92;AE92;1101 1163 11B5;AE92;1101 1163 11B5; # (꺒; 꺒; á„ᅣᆵ; 꺒; á„ᅣᆵ; ) HANGUL SYLLABLE GGYALP
+AE93;AE93;1101 1163 11B6;AE93;1101 1163 11B6; # (꺓; 꺓; á„ᅣᆶ; 꺓; á„ᅣᆶ; ) HANGUL SYLLABLE GGYALH
+AE94;AE94;1101 1163 11B7;AE94;1101 1163 11B7; # (꺔; 꺔; á„ᅣᆷ; 꺔; á„ᅣᆷ; ) HANGUL SYLLABLE GGYAM
+AE95;AE95;1101 1163 11B8;AE95;1101 1163 11B8; # (꺕; 꺕; á„ᅣᆸ; 꺕; á„ᅣᆸ; ) HANGUL SYLLABLE GGYAB
+AE96;AE96;1101 1163 11B9;AE96;1101 1163 11B9; # (꺖; 꺖; á„ᅣᆹ; 꺖; á„ᅣᆹ; ) HANGUL SYLLABLE GGYABS
+AE97;AE97;1101 1163 11BA;AE97;1101 1163 11BA; # (꺗; 꺗; á„ᅣᆺ; 꺗; á„ᅣᆺ; ) HANGUL SYLLABLE GGYAS
+AE98;AE98;1101 1163 11BB;AE98;1101 1163 11BB; # (꺘; 꺘; á„ᅣᆻ; 꺘; á„ᅣᆻ; ) HANGUL SYLLABLE GGYASS
+AE99;AE99;1101 1163 11BC;AE99;1101 1163 11BC; # (꺙; 꺙; á„ᅣᆼ; 꺙; á„ᅣᆼ; ) HANGUL SYLLABLE GGYANG
+AE9A;AE9A;1101 1163 11BD;AE9A;1101 1163 11BD; # (꺚; 꺚; á„ᅣᆽ; 꺚; á„ᅣᆽ; ) HANGUL SYLLABLE GGYAJ
+AE9B;AE9B;1101 1163 11BE;AE9B;1101 1163 11BE; # (꺛; 꺛; á„ᅣᆾ; 꺛; á„ᅣᆾ; ) HANGUL SYLLABLE GGYAC
+AE9C;AE9C;1101 1163 11BF;AE9C;1101 1163 11BF; # (꺜; 꺜; á„ᅣᆿ; 꺜; á„ᅣᆿ; ) HANGUL SYLLABLE GGYAK
+AE9D;AE9D;1101 1163 11C0;AE9D;1101 1163 11C0; # (êº; êº; á„ᅣᇀ; êº; á„ᅣᇀ; ) HANGUL SYLLABLE GGYAT
+AE9E;AE9E;1101 1163 11C1;AE9E;1101 1163 11C1; # (꺞; 꺞; á„á…£á‡; 꺞; á„á…£á‡; ) HANGUL SYLLABLE GGYAP
+AE9F;AE9F;1101 1163 11C2;AE9F;1101 1163 11C2; # (꺟; 꺟; á„ᅣᇂ; 꺟; á„ᅣᇂ; ) HANGUL SYLLABLE GGYAH
+AEA0;AEA0;1101 1164;AEA0;1101 1164; # (꺠; 꺠; á„á…¤; 꺠; á„á…¤; ) HANGUL SYLLABLE GGYAE
+AEA1;AEA1;1101 1164 11A8;AEA1;1101 1164 11A8; # (꺡; 꺡; á„ᅤᆨ; 꺡; á„ᅤᆨ; ) HANGUL SYLLABLE GGYAEG
+AEA2;AEA2;1101 1164 11A9;AEA2;1101 1164 11A9; # (꺢; 꺢; á„ᅤᆩ; 꺢; á„ᅤᆩ; ) HANGUL SYLLABLE GGYAEGG
+AEA3;AEA3;1101 1164 11AA;AEA3;1101 1164 11AA; # (꺣; 꺣; á„ᅤᆪ; 꺣; á„ᅤᆪ; ) HANGUL SYLLABLE GGYAEGS
+AEA4;AEA4;1101 1164 11AB;AEA4;1101 1164 11AB; # (꺤; 꺤; á„ᅤᆫ; 꺤; á„ᅤᆫ; ) HANGUL SYLLABLE GGYAEN
+AEA5;AEA5;1101 1164 11AC;AEA5;1101 1164 11AC; # (꺥; 꺥; á„ᅤᆬ; 꺥; á„ᅤᆬ; ) HANGUL SYLLABLE GGYAENJ
+AEA6;AEA6;1101 1164 11AD;AEA6;1101 1164 11AD; # (꺦; 꺦; á„ᅤᆭ; 꺦; á„ᅤᆭ; ) HANGUL SYLLABLE GGYAENH
+AEA7;AEA7;1101 1164 11AE;AEA7;1101 1164 11AE; # (꺧; 꺧; á„ᅤᆮ; 꺧; á„ᅤᆮ; ) HANGUL SYLLABLE GGYAED
+AEA8;AEA8;1101 1164 11AF;AEA8;1101 1164 11AF; # (꺨; 꺨; á„ᅤᆯ; 꺨; á„ᅤᆯ; ) HANGUL SYLLABLE GGYAEL
+AEA9;AEA9;1101 1164 11B0;AEA9;1101 1164 11B0; # (꺩; 꺩; á„ᅤᆰ; 꺩; á„ᅤᆰ; ) HANGUL SYLLABLE GGYAELG
+AEAA;AEAA;1101 1164 11B1;AEAA;1101 1164 11B1; # (꺪; 꺪; á„ᅤᆱ; 꺪; á„ᅤᆱ; ) HANGUL SYLLABLE GGYAELM
+AEAB;AEAB;1101 1164 11B2;AEAB;1101 1164 11B2; # (꺫; 꺫; á„ᅤᆲ; 꺫; á„ᅤᆲ; ) HANGUL SYLLABLE GGYAELB
+AEAC;AEAC;1101 1164 11B3;AEAC;1101 1164 11B3; # (꺬; 꺬; á„ᅤᆳ; 꺬; á„ᅤᆳ; ) HANGUL SYLLABLE GGYAELS
+AEAD;AEAD;1101 1164 11B4;AEAD;1101 1164 11B4; # (꺭; 꺭; á„ᅤᆴ; 꺭; á„ᅤᆴ; ) HANGUL SYLLABLE GGYAELT
+AEAE;AEAE;1101 1164 11B5;AEAE;1101 1164 11B5; # (꺮; 꺮; á„ᅤᆵ; 꺮; á„ᅤᆵ; ) HANGUL SYLLABLE GGYAELP
+AEAF;AEAF;1101 1164 11B6;AEAF;1101 1164 11B6; # (꺯; 꺯; á„ᅤᆶ; 꺯; á„ᅤᆶ; ) HANGUL SYLLABLE GGYAELH
+AEB0;AEB0;1101 1164 11B7;AEB0;1101 1164 11B7; # (꺰; 꺰; á„ᅤᆷ; 꺰; á„ᅤᆷ; ) HANGUL SYLLABLE GGYAEM
+AEB1;AEB1;1101 1164 11B8;AEB1;1101 1164 11B8; # (꺱; 꺱; á„ᅤᆸ; 꺱; á„ᅤᆸ; ) HANGUL SYLLABLE GGYAEB
+AEB2;AEB2;1101 1164 11B9;AEB2;1101 1164 11B9; # (꺲; 꺲; á„ᅤᆹ; 꺲; á„ᅤᆹ; ) HANGUL SYLLABLE GGYAEBS
+AEB3;AEB3;1101 1164 11BA;AEB3;1101 1164 11BA; # (꺳; 꺳; á„ᅤᆺ; 꺳; á„ᅤᆺ; ) HANGUL SYLLABLE GGYAES
+AEB4;AEB4;1101 1164 11BB;AEB4;1101 1164 11BB; # (꺴; 꺴; á„ᅤᆻ; 꺴; á„ᅤᆻ; ) HANGUL SYLLABLE GGYAESS
+AEB5;AEB5;1101 1164 11BC;AEB5;1101 1164 11BC; # (꺵; 꺵; á„ᅤᆼ; 꺵; á„ᅤᆼ; ) HANGUL SYLLABLE GGYAENG
+AEB6;AEB6;1101 1164 11BD;AEB6;1101 1164 11BD; # (꺶; 꺶; á„ᅤᆽ; 꺶; á„ᅤᆽ; ) HANGUL SYLLABLE GGYAEJ
+AEB7;AEB7;1101 1164 11BE;AEB7;1101 1164 11BE; # (꺷; 꺷; á„ᅤᆾ; 꺷; á„ᅤᆾ; ) HANGUL SYLLABLE GGYAEC
+AEB8;AEB8;1101 1164 11BF;AEB8;1101 1164 11BF; # (꺸; 꺸; á„ᅤᆿ; 꺸; á„ᅤᆿ; ) HANGUL SYLLABLE GGYAEK
+AEB9;AEB9;1101 1164 11C0;AEB9;1101 1164 11C0; # (꺹; 꺹; á„ᅤᇀ; 꺹; á„ᅤᇀ; ) HANGUL SYLLABLE GGYAET
+AEBA;AEBA;1101 1164 11C1;AEBA;1101 1164 11C1; # (꺺; 꺺; á„á…¤á‡; 꺺; á„á…¤á‡; ) HANGUL SYLLABLE GGYAEP
+AEBB;AEBB;1101 1164 11C2;AEBB;1101 1164 11C2; # (꺻; 꺻; á„ᅤᇂ; 꺻; á„ᅤᇂ; ) HANGUL SYLLABLE GGYAEH
+AEBC;AEBC;1101 1165;AEBC;1101 1165; # (꺼; 꺼; á„á…¥; 꺼; á„á…¥; ) HANGUL SYLLABLE GGEO
+AEBD;AEBD;1101 1165 11A8;AEBD;1101 1165 11A8; # (꺽; 꺽; á„ᅥᆨ; 꺽; á„ᅥᆨ; ) HANGUL SYLLABLE GGEOG
+AEBE;AEBE;1101 1165 11A9;AEBE;1101 1165 11A9; # (꺾; 꺾; á„ᅥᆩ; 꺾; á„ᅥᆩ; ) HANGUL SYLLABLE GGEOGG
+AEBF;AEBF;1101 1165 11AA;AEBF;1101 1165 11AA; # (꺿; 꺿; á„ᅥᆪ; 꺿; á„ᅥᆪ; ) HANGUL SYLLABLE GGEOGS
+AEC0;AEC0;1101 1165 11AB;AEC0;1101 1165 11AB; # (껀; 껀; á„ᅥᆫ; 껀; á„ᅥᆫ; ) HANGUL SYLLABLE GGEON
+AEC1;AEC1;1101 1165 11AC;AEC1;1101 1165 11AC; # (ê»; ê»; á„ᅥᆬ; ê»; á„ᅥᆬ; ) HANGUL SYLLABLE GGEONJ
+AEC2;AEC2;1101 1165 11AD;AEC2;1101 1165 11AD; # (껂; 껂; á„ᅥᆭ; 껂; á„ᅥᆭ; ) HANGUL SYLLABLE GGEONH
+AEC3;AEC3;1101 1165 11AE;AEC3;1101 1165 11AE; # (껃; 껃; á„ᅥᆮ; 껃; á„ᅥᆮ; ) HANGUL SYLLABLE GGEOD
+AEC4;AEC4;1101 1165 11AF;AEC4;1101 1165 11AF; # (껄; 껄; á„ᅥᆯ; 껄; á„ᅥᆯ; ) HANGUL SYLLABLE GGEOL
+AEC5;AEC5;1101 1165 11B0;AEC5;1101 1165 11B0; # (ê»…; ê»…; á„ᅥᆰ; ê»…; á„ᅥᆰ; ) HANGUL SYLLABLE GGEOLG
+AEC6;AEC6;1101 1165 11B1;AEC6;1101 1165 11B1; # (껆; 껆; á„ᅥᆱ; 껆; á„ᅥᆱ; ) HANGUL SYLLABLE GGEOLM
+AEC7;AEC7;1101 1165 11B2;AEC7;1101 1165 11B2; # (껇; 껇; á„ᅥᆲ; 껇; á„ᅥᆲ; ) HANGUL SYLLABLE GGEOLB
+AEC8;AEC8;1101 1165 11B3;AEC8;1101 1165 11B3; # (껈; 껈; á„ᅥᆳ; 껈; á„ᅥᆳ; ) HANGUL SYLLABLE GGEOLS
+AEC9;AEC9;1101 1165 11B4;AEC9;1101 1165 11B4; # (껉; 껉; á„ᅥᆴ; 껉; á„ᅥᆴ; ) HANGUL SYLLABLE GGEOLT
+AECA;AECA;1101 1165 11B5;AECA;1101 1165 11B5; # (껊; 껊; á„ᅥᆵ; 껊; á„ᅥᆵ; ) HANGUL SYLLABLE GGEOLP
+AECB;AECB;1101 1165 11B6;AECB;1101 1165 11B6; # (껋; 껋; á„ᅥᆶ; 껋; á„ᅥᆶ; ) HANGUL SYLLABLE GGEOLH
+AECC;AECC;1101 1165 11B7;AECC;1101 1165 11B7; # (껌; 껌; á„ᅥᆷ; 껌; á„ᅥᆷ; ) HANGUL SYLLABLE GGEOM
+AECD;AECD;1101 1165 11B8;AECD;1101 1165 11B8; # (ê»; ê»; á„ᅥᆸ; ê»; á„ᅥᆸ; ) HANGUL SYLLABLE GGEOB
+AECE;AECE;1101 1165 11B9;AECE;1101 1165 11B9; # (껎; 껎; á„ᅥᆹ; 껎; á„ᅥᆹ; ) HANGUL SYLLABLE GGEOBS
+AECF;AECF;1101 1165 11BA;AECF;1101 1165 11BA; # (ê»; ê»; á„ᅥᆺ; ê»; á„ᅥᆺ; ) HANGUL SYLLABLE GGEOS
+AED0;AED0;1101 1165 11BB;AED0;1101 1165 11BB; # (ê»; ê»; á„ᅥᆻ; ê»; á„ᅥᆻ; ) HANGUL SYLLABLE GGEOSS
+AED1;AED1;1101 1165 11BC;AED1;1101 1165 11BC; # (껑; 껑; á„ᅥᆼ; 껑; á„ᅥᆼ; ) HANGUL SYLLABLE GGEONG
+AED2;AED2;1101 1165 11BD;AED2;1101 1165 11BD; # (ê»’; ê»’; á„ᅥᆽ; ê»’; á„ᅥᆽ; ) HANGUL SYLLABLE GGEOJ
+AED3;AED3;1101 1165 11BE;AED3;1101 1165 11BE; # (껓; 껓; á„ᅥᆾ; 껓; á„ᅥᆾ; ) HANGUL SYLLABLE GGEOC
+AED4;AED4;1101 1165 11BF;AED4;1101 1165 11BF; # (ê»”; ê»”; á„ᅥᆿ; ê»”; á„ᅥᆿ; ) HANGUL SYLLABLE GGEOK
+AED5;AED5;1101 1165 11C0;AED5;1101 1165 11C0; # (껕; 껕; á„ᅥᇀ; 껕; á„ᅥᇀ; ) HANGUL SYLLABLE GGEOT
+AED6;AED6;1101 1165 11C1;AED6;1101 1165 11C1; # (ê»–; ê»–; á„á…¥á‡; ê»–; á„á…¥á‡; ) HANGUL SYLLABLE GGEOP
+AED7;AED7;1101 1165 11C2;AED7;1101 1165 11C2; # (ê»—; ê»—; á„ᅥᇂ; ê»—; á„ᅥᇂ; ) HANGUL SYLLABLE GGEOH
+AED8;AED8;1101 1166;AED8;1101 1166; # (께; 께; á„á…¦; 께; á„á…¦; ) HANGUL SYLLABLE GGE
+AED9;AED9;1101 1166 11A8;AED9;1101 1166 11A8; # (ê»™; ê»™; á„ᅦᆨ; ê»™; á„ᅦᆨ; ) HANGUL SYLLABLE GGEG
+AEDA;AEDA;1101 1166 11A9;AEDA;1101 1166 11A9; # (껚; 껚; á„ᅦᆩ; 껚; á„ᅦᆩ; ) HANGUL SYLLABLE GGEGG
+AEDB;AEDB;1101 1166 11AA;AEDB;1101 1166 11AA; # (ê»›; ê»›; á„ᅦᆪ; ê»›; á„ᅦᆪ; ) HANGUL SYLLABLE GGEGS
+AEDC;AEDC;1101 1166 11AB;AEDC;1101 1166 11AB; # (껜; 껜; á„ᅦᆫ; 껜; á„ᅦᆫ; ) HANGUL SYLLABLE GGEN
+AEDD;AEDD;1101 1166 11AC;AEDD;1101 1166 11AC; # (ê»; ê»; á„ᅦᆬ; ê»; á„ᅦᆬ; ) HANGUL SYLLABLE GGENJ
+AEDE;AEDE;1101 1166 11AD;AEDE;1101 1166 11AD; # (껞; 껞; á„ᅦᆭ; 껞; á„ᅦᆭ; ) HANGUL SYLLABLE GGENH
+AEDF;AEDF;1101 1166 11AE;AEDF;1101 1166 11AE; # (껟; 껟; á„ᅦᆮ; 껟; á„ᅦᆮ; ) HANGUL SYLLABLE GGED
+AEE0;AEE0;1101 1166 11AF;AEE0;1101 1166 11AF; # (ê» ; ê» ; á„ᅦᆯ; ê» ; á„ᅦᆯ; ) HANGUL SYLLABLE GGEL
+AEE1;AEE1;1101 1166 11B0;AEE1;1101 1166 11B0; # (껡; 껡; á„ᅦᆰ; 껡; á„ᅦᆰ; ) HANGUL SYLLABLE GGELG
+AEE2;AEE2;1101 1166 11B1;AEE2;1101 1166 11B1; # (껢; 껢; á„ᅦᆱ; 껢; á„ᅦᆱ; ) HANGUL SYLLABLE GGELM
+AEE3;AEE3;1101 1166 11B2;AEE3;1101 1166 11B2; # (껣; 껣; á„ᅦᆲ; 껣; á„ᅦᆲ; ) HANGUL SYLLABLE GGELB
+AEE4;AEE4;1101 1166 11B3;AEE4;1101 1166 11B3; # (껤; 껤; á„ᅦᆳ; 껤; á„ᅦᆳ; ) HANGUL SYLLABLE GGELS
+AEE5;AEE5;1101 1166 11B4;AEE5;1101 1166 11B4; # (껥; 껥; á„ᅦᆴ; 껥; á„ᅦᆴ; ) HANGUL SYLLABLE GGELT
+AEE6;AEE6;1101 1166 11B5;AEE6;1101 1166 11B5; # (껦; 껦; á„ᅦᆵ; 껦; á„ᅦᆵ; ) HANGUL SYLLABLE GGELP
+AEE7;AEE7;1101 1166 11B6;AEE7;1101 1166 11B6; # (ê»§; ê»§; á„ᅦᆶ; ê»§; á„ᅦᆶ; ) HANGUL SYLLABLE GGELH
+AEE8;AEE8;1101 1166 11B7;AEE8;1101 1166 11B7; # (껨; 껨; á„ᅦᆷ; 껨; á„ᅦᆷ; ) HANGUL SYLLABLE GGEM
+AEE9;AEE9;1101 1166 11B8;AEE9;1101 1166 11B8; # (껩; 껩; á„ᅦᆸ; 껩; á„ᅦᆸ; ) HANGUL SYLLABLE GGEB
+AEEA;AEEA;1101 1166 11B9;AEEA;1101 1166 11B9; # (껪; 껪; á„ᅦᆹ; 껪; á„ᅦᆹ; ) HANGUL SYLLABLE GGEBS
+AEEB;AEEB;1101 1166 11BA;AEEB;1101 1166 11BA; # (껫; 껫; á„ᅦᆺ; 껫; á„ᅦᆺ; ) HANGUL SYLLABLE GGES
+AEEC;AEEC;1101 1166 11BB;AEEC;1101 1166 11BB; # (껬; 껬; á„ᅦᆻ; 껬; á„ᅦᆻ; ) HANGUL SYLLABLE GGESS
+AEED;AEED;1101 1166 11BC;AEED;1101 1166 11BC; # (ê»­; ê»­; á„ᅦᆼ; ê»­; á„ᅦᆼ; ) HANGUL SYLLABLE GGENG
+AEEE;AEEE;1101 1166 11BD;AEEE;1101 1166 11BD; # (ê»®; ê»®; á„ᅦᆽ; ê»®; á„ᅦᆽ; ) HANGUL SYLLABLE GGEJ
+AEEF;AEEF;1101 1166 11BE;AEEF;1101 1166 11BE; # (껯; 껯; á„ᅦᆾ; 껯; á„ᅦᆾ; ) HANGUL SYLLABLE GGEC
+AEF0;AEF0;1101 1166 11BF;AEF0;1101 1166 11BF; # (ê»°; ê»°; á„ᅦᆿ; ê»°; á„ᅦᆿ; ) HANGUL SYLLABLE GGEK
+AEF1;AEF1;1101 1166 11C0;AEF1;1101 1166 11C0; # (ê»±; ê»±; á„ᅦᇀ; ê»±; á„ᅦᇀ; ) HANGUL SYLLABLE GGET
+AEF2;AEF2;1101 1166 11C1;AEF2;1101 1166 11C1; # (껲; 껲; á„á…¦á‡; 껲; á„á…¦á‡; ) HANGUL SYLLABLE GGEP
+AEF3;AEF3;1101 1166 11C2;AEF3;1101 1166 11C2; # (껳; 껳; á„ᅦᇂ; 껳; á„ᅦᇂ; ) HANGUL SYLLABLE GGEH
+AEF4;AEF4;1101 1167;AEF4;1101 1167; # (ê»´; ê»´; á„á…§; ê»´; á„á…§; ) HANGUL SYLLABLE GGYEO
+AEF5;AEF5;1101 1167 11A8;AEF5;1101 1167 11A8; # (껵; 껵; á„ᅧᆨ; 껵; á„ᅧᆨ; ) HANGUL SYLLABLE GGYEOG
+AEF6;AEF6;1101 1167 11A9;AEF6;1101 1167 11A9; # (ê»¶; ê»¶; á„ᅧᆩ; ê»¶; á„ᅧᆩ; ) HANGUL SYLLABLE GGYEOGG
+AEF7;AEF7;1101 1167 11AA;AEF7;1101 1167 11AA; # (ê»·; ê»·; á„ᅧᆪ; ê»·; á„ᅧᆪ; ) HANGUL SYLLABLE GGYEOGS
+AEF8;AEF8;1101 1167 11AB;AEF8;1101 1167 11AB; # (껸; 껸; á„ᅧᆫ; 껸; á„ᅧᆫ; ) HANGUL SYLLABLE GGYEON
+AEF9;AEF9;1101 1167 11AC;AEF9;1101 1167 11AC; # (껹; 껹; á„ᅧᆬ; 껹; á„ᅧᆬ; ) HANGUL SYLLABLE GGYEONJ
+AEFA;AEFA;1101 1167 11AD;AEFA;1101 1167 11AD; # (껺; 껺; á„ᅧᆭ; 껺; á„ᅧᆭ; ) HANGUL SYLLABLE GGYEONH
+AEFB;AEFB;1101 1167 11AE;AEFB;1101 1167 11AE; # (ê»»; ê»»; á„ᅧᆮ; ê»»; á„ᅧᆮ; ) HANGUL SYLLABLE GGYEOD
+AEFC;AEFC;1101 1167 11AF;AEFC;1101 1167 11AF; # (껼; 껼; á„ᅧᆯ; 껼; á„ᅧᆯ; ) HANGUL SYLLABLE GGYEOL
+AEFD;AEFD;1101 1167 11B0;AEFD;1101 1167 11B0; # (껽; 껽; á„ᅧᆰ; 껽; á„ᅧᆰ; ) HANGUL SYLLABLE GGYEOLG
+AEFE;AEFE;1101 1167 11B1;AEFE;1101 1167 11B1; # (껾; 껾; á„ᅧᆱ; 껾; á„ᅧᆱ; ) HANGUL SYLLABLE GGYEOLM
+AEFF;AEFF;1101 1167 11B2;AEFF;1101 1167 11B2; # (껿; 껿; á„ᅧᆲ; 껿; á„ᅧᆲ; ) HANGUL SYLLABLE GGYEOLB
+AF00;AF00;1101 1167 11B3;AF00;1101 1167 11B3; # (ê¼€; ê¼€; á„ᅧᆳ; ê¼€; á„ᅧᆳ; ) HANGUL SYLLABLE GGYEOLS
+AF01;AF01;1101 1167 11B4;AF01;1101 1167 11B4; # (ê¼; ê¼; á„ᅧᆴ; ê¼; á„ᅧᆴ; ) HANGUL SYLLABLE GGYEOLT
+AF02;AF02;1101 1167 11B5;AF02;1101 1167 11B5; # (꼂; 꼂; á„ᅧᆵ; 꼂; á„ᅧᆵ; ) HANGUL SYLLABLE GGYEOLP
+AF03;AF03;1101 1167 11B6;AF03;1101 1167 11B6; # (꼃; 꼃; á„ᅧᆶ; 꼃; á„ᅧᆶ; ) HANGUL SYLLABLE GGYEOLH
+AF04;AF04;1101 1167 11B7;AF04;1101 1167 11B7; # (꼄; 꼄; á„ᅧᆷ; 꼄; á„ᅧᆷ; ) HANGUL SYLLABLE GGYEOM
+AF05;AF05;1101 1167 11B8;AF05;1101 1167 11B8; # (ê¼…; ê¼…; á„ᅧᆸ; ê¼…; á„ᅧᆸ; ) HANGUL SYLLABLE GGYEOB
+AF06;AF06;1101 1167 11B9;AF06;1101 1167 11B9; # (꼆; 꼆; á„ᅧᆹ; 꼆; á„ᅧᆹ; ) HANGUL SYLLABLE GGYEOBS
+AF07;AF07;1101 1167 11BA;AF07;1101 1167 11BA; # (꼇; 꼇; á„ᅧᆺ; 꼇; á„ᅧᆺ; ) HANGUL SYLLABLE GGYEOS
+AF08;AF08;1101 1167 11BB;AF08;1101 1167 11BB; # (꼈; 꼈; á„ᅧᆻ; 꼈; á„ᅧᆻ; ) HANGUL SYLLABLE GGYEOSS
+AF09;AF09;1101 1167 11BC;AF09;1101 1167 11BC; # (꼉; 꼉; á„ᅧᆼ; 꼉; á„ᅧᆼ; ) HANGUL SYLLABLE GGYEONG
+AF0A;AF0A;1101 1167 11BD;AF0A;1101 1167 11BD; # (꼊; 꼊; á„ᅧᆽ; 꼊; á„ᅧᆽ; ) HANGUL SYLLABLE GGYEOJ
+AF0B;AF0B;1101 1167 11BE;AF0B;1101 1167 11BE; # (꼋; 꼋; á„ᅧᆾ; 꼋; á„ᅧᆾ; ) HANGUL SYLLABLE GGYEOC
+AF0C;AF0C;1101 1167 11BF;AF0C;1101 1167 11BF; # (꼌; 꼌; á„ᅧᆿ; 꼌; á„ᅧᆿ; ) HANGUL SYLLABLE GGYEOK
+AF0D;AF0D;1101 1167 11C0;AF0D;1101 1167 11C0; # (ê¼; ê¼; á„ᅧᇀ; ê¼; á„ᅧᇀ; ) HANGUL SYLLABLE GGYEOT
+AF0E;AF0E;1101 1167 11C1;AF0E;1101 1167 11C1; # (꼎; 꼎; á„á…§á‡; 꼎; á„á…§á‡; ) HANGUL SYLLABLE GGYEOP
+AF0F;AF0F;1101 1167 11C2;AF0F;1101 1167 11C2; # (ê¼; ê¼; á„ᅧᇂ; ê¼; á„ᅧᇂ; ) HANGUL SYLLABLE GGYEOH
+AF10;AF10;1101 1168;AF10;1101 1168; # (ê¼; ê¼; á„á…¨; ê¼; á„á…¨; ) HANGUL SYLLABLE GGYE
+AF11;AF11;1101 1168 11A8;AF11;1101 1168 11A8; # (꼑; 꼑; á„ᅨᆨ; 꼑; á„ᅨᆨ; ) HANGUL SYLLABLE GGYEG
+AF12;AF12;1101 1168 11A9;AF12;1101 1168 11A9; # (ê¼’; ê¼’; á„ᅨᆩ; ê¼’; á„ᅨᆩ; ) HANGUL SYLLABLE GGYEGG
+AF13;AF13;1101 1168 11AA;AF13;1101 1168 11AA; # (꼓; 꼓; á„ᅨᆪ; 꼓; á„ᅨᆪ; ) HANGUL SYLLABLE GGYEGS
+AF14;AF14;1101 1168 11AB;AF14;1101 1168 11AB; # (ê¼”; ê¼”; á„ᅨᆫ; ê¼”; á„ᅨᆫ; ) HANGUL SYLLABLE GGYEN
+AF15;AF15;1101 1168 11AC;AF15;1101 1168 11AC; # (꼕; 꼕; á„ᅨᆬ; 꼕; á„ᅨᆬ; ) HANGUL SYLLABLE GGYENJ
+AF16;AF16;1101 1168 11AD;AF16;1101 1168 11AD; # (ê¼–; ê¼–; á„ᅨᆭ; ê¼–; á„ᅨᆭ; ) HANGUL SYLLABLE GGYENH
+AF17;AF17;1101 1168 11AE;AF17;1101 1168 11AE; # (ê¼—; ê¼—; á„ᅨᆮ; ê¼—; á„ᅨᆮ; ) HANGUL SYLLABLE GGYED
+AF18;AF18;1101 1168 11AF;AF18;1101 1168 11AF; # (꼘; 꼘; á„ᅨᆯ; 꼘; á„ᅨᆯ; ) HANGUL SYLLABLE GGYEL
+AF19;AF19;1101 1168 11B0;AF19;1101 1168 11B0; # (ê¼™; ê¼™; á„ᅨᆰ; ê¼™; á„ᅨᆰ; ) HANGUL SYLLABLE GGYELG
+AF1A;AF1A;1101 1168 11B1;AF1A;1101 1168 11B1; # (꼚; 꼚; á„ᅨᆱ; 꼚; á„ᅨᆱ; ) HANGUL SYLLABLE GGYELM
+AF1B;AF1B;1101 1168 11B2;AF1B;1101 1168 11B2; # (ê¼›; ê¼›; á„ᅨᆲ; ê¼›; á„ᅨᆲ; ) HANGUL SYLLABLE GGYELB
+AF1C;AF1C;1101 1168 11B3;AF1C;1101 1168 11B3; # (꼜; 꼜; á„ᅨᆳ; 꼜; á„ᅨᆳ; ) HANGUL SYLLABLE GGYELS
+AF1D;AF1D;1101 1168 11B4;AF1D;1101 1168 11B4; # (ê¼; ê¼; á„ᅨᆴ; ê¼; á„ᅨᆴ; ) HANGUL SYLLABLE GGYELT
+AF1E;AF1E;1101 1168 11B5;AF1E;1101 1168 11B5; # (꼞; 꼞; á„ᅨᆵ; 꼞; á„ᅨᆵ; ) HANGUL SYLLABLE GGYELP
+AF1F;AF1F;1101 1168 11B6;AF1F;1101 1168 11B6; # (꼟; 꼟; á„ᅨᆶ; 꼟; á„ᅨᆶ; ) HANGUL SYLLABLE GGYELH
+AF20;AF20;1101 1168 11B7;AF20;1101 1168 11B7; # (ê¼ ; ê¼ ; á„ᅨᆷ; ê¼ ; á„ᅨᆷ; ) HANGUL SYLLABLE GGYEM
+AF21;AF21;1101 1168 11B8;AF21;1101 1168 11B8; # (꼡; 꼡; á„ᅨᆸ; 꼡; á„ᅨᆸ; ) HANGUL SYLLABLE GGYEB
+AF22;AF22;1101 1168 11B9;AF22;1101 1168 11B9; # (ê¼¢; ê¼¢; á„ᅨᆹ; ê¼¢; á„ᅨᆹ; ) HANGUL SYLLABLE GGYEBS
+AF23;AF23;1101 1168 11BA;AF23;1101 1168 11BA; # (ê¼£; ê¼£; á„ᅨᆺ; ê¼£; á„ᅨᆺ; ) HANGUL SYLLABLE GGYES
+AF24;AF24;1101 1168 11BB;AF24;1101 1168 11BB; # (꼤; 꼤; á„ᅨᆻ; 꼤; á„ᅨᆻ; ) HANGUL SYLLABLE GGYESS
+AF25;AF25;1101 1168 11BC;AF25;1101 1168 11BC; # (ê¼¥; ê¼¥; á„ᅨᆼ; ê¼¥; á„ᅨᆼ; ) HANGUL SYLLABLE GGYENG
+AF26;AF26;1101 1168 11BD;AF26;1101 1168 11BD; # (꼦; 꼦; á„ᅨᆽ; 꼦; á„ᅨᆽ; ) HANGUL SYLLABLE GGYEJ
+AF27;AF27;1101 1168 11BE;AF27;1101 1168 11BE; # (ê¼§; ê¼§; á„ᅨᆾ; ê¼§; á„ᅨᆾ; ) HANGUL SYLLABLE GGYEC
+AF28;AF28;1101 1168 11BF;AF28;1101 1168 11BF; # (꼨; 꼨; á„ᅨᆿ; 꼨; á„ᅨᆿ; ) HANGUL SYLLABLE GGYEK
+AF29;AF29;1101 1168 11C0;AF29;1101 1168 11C0; # (꼩; 꼩; á„ᅨᇀ; 꼩; á„ᅨᇀ; ) HANGUL SYLLABLE GGYET
+AF2A;AF2A;1101 1168 11C1;AF2A;1101 1168 11C1; # (꼪; 꼪; á„á…¨á‡; 꼪; á„á…¨á‡; ) HANGUL SYLLABLE GGYEP
+AF2B;AF2B;1101 1168 11C2;AF2B;1101 1168 11C2; # (꼫; 꼫; á„ᅨᇂ; 꼫; á„ᅨᇂ; ) HANGUL SYLLABLE GGYEH
+AF2C;AF2C;1101 1169;AF2C;1101 1169; # (꼬; 꼬; á„á…©; 꼬; á„á…©; ) HANGUL SYLLABLE GGO
+AF2D;AF2D;1101 1169 11A8;AF2D;1101 1169 11A8; # (ê¼­; ê¼­; á„ᅩᆨ; ê¼­; á„ᅩᆨ; ) HANGUL SYLLABLE GGOG
+AF2E;AF2E;1101 1169 11A9;AF2E;1101 1169 11A9; # (ê¼®; ê¼®; á„ᅩᆩ; ê¼®; á„ᅩᆩ; ) HANGUL SYLLABLE GGOGG
+AF2F;AF2F;1101 1169 11AA;AF2F;1101 1169 11AA; # (꼯; 꼯; á„ᅩᆪ; 꼯; á„ᅩᆪ; ) HANGUL SYLLABLE GGOGS
+AF30;AF30;1101 1169 11AB;AF30;1101 1169 11AB; # (ê¼°; ê¼°; á„ᅩᆫ; ê¼°; á„ᅩᆫ; ) HANGUL SYLLABLE GGON
+AF31;AF31;1101 1169 11AC;AF31;1101 1169 11AC; # (ê¼±; ê¼±; á„ᅩᆬ; ê¼±; á„ᅩᆬ; ) HANGUL SYLLABLE GGONJ
+AF32;AF32;1101 1169 11AD;AF32;1101 1169 11AD; # (ê¼²; ê¼²; á„ᅩᆭ; ê¼²; á„ᅩᆭ; ) HANGUL SYLLABLE GGONH
+AF33;AF33;1101 1169 11AE;AF33;1101 1169 11AE; # (ê¼³; ê¼³; á„ᅩᆮ; ê¼³; á„ᅩᆮ; ) HANGUL SYLLABLE GGOD
+AF34;AF34;1101 1169 11AF;AF34;1101 1169 11AF; # (ê¼´; ê¼´; á„ᅩᆯ; ê¼´; á„ᅩᆯ; ) HANGUL SYLLABLE GGOL
+AF35;AF35;1101 1169 11B0;AF35;1101 1169 11B0; # (ê¼µ; ê¼µ; á„ᅩᆰ; ê¼µ; á„ᅩᆰ; ) HANGUL SYLLABLE GGOLG
+AF36;AF36;1101 1169 11B1;AF36;1101 1169 11B1; # (ê¼¶; ê¼¶; á„ᅩᆱ; ê¼¶; á„ᅩᆱ; ) HANGUL SYLLABLE GGOLM
+AF37;AF37;1101 1169 11B2;AF37;1101 1169 11B2; # (ê¼·; ê¼·; á„ᅩᆲ; ê¼·; á„ᅩᆲ; ) HANGUL SYLLABLE GGOLB
+AF38;AF38;1101 1169 11B3;AF38;1101 1169 11B3; # (꼸; 꼸; á„ᅩᆳ; 꼸; á„ᅩᆳ; ) HANGUL SYLLABLE GGOLS
+AF39;AF39;1101 1169 11B4;AF39;1101 1169 11B4; # (ê¼¹; ê¼¹; á„ᅩᆴ; ê¼¹; á„ᅩᆴ; ) HANGUL SYLLABLE GGOLT
+AF3A;AF3A;1101 1169 11B5;AF3A;1101 1169 11B5; # (꼺; 꼺; á„ᅩᆵ; 꼺; á„ᅩᆵ; ) HANGUL SYLLABLE GGOLP
+AF3B;AF3B;1101 1169 11B6;AF3B;1101 1169 11B6; # (ê¼»; ê¼»; á„ᅩᆶ; ê¼»; á„ᅩᆶ; ) HANGUL SYLLABLE GGOLH
+AF3C;AF3C;1101 1169 11B7;AF3C;1101 1169 11B7; # (ê¼¼; ê¼¼; á„ᅩᆷ; ê¼¼; á„ᅩᆷ; ) HANGUL SYLLABLE GGOM
+AF3D;AF3D;1101 1169 11B8;AF3D;1101 1169 11B8; # (ê¼½; ê¼½; á„ᅩᆸ; ê¼½; á„ᅩᆸ; ) HANGUL SYLLABLE GGOB
+AF3E;AF3E;1101 1169 11B9;AF3E;1101 1169 11B9; # (ê¼¾; ê¼¾; á„ᅩᆹ; ê¼¾; á„ᅩᆹ; ) HANGUL SYLLABLE GGOBS
+AF3F;AF3F;1101 1169 11BA;AF3F;1101 1169 11BA; # (꼿; 꼿; á„ᅩᆺ; 꼿; á„ᅩᆺ; ) HANGUL SYLLABLE GGOS
+AF40;AF40;1101 1169 11BB;AF40;1101 1169 11BB; # (ê½€; ê½€; á„ᅩᆻ; ê½€; á„ᅩᆻ; ) HANGUL SYLLABLE GGOSS
+AF41;AF41;1101 1169 11BC;AF41;1101 1169 11BC; # (ê½; ê½; á„ᅩᆼ; ê½; á„ᅩᆼ; ) HANGUL SYLLABLE GGONG
+AF42;AF42;1101 1169 11BD;AF42;1101 1169 11BD; # (꽂; 꽂; á„ᅩᆽ; 꽂; á„ᅩᆽ; ) HANGUL SYLLABLE GGOJ
+AF43;AF43;1101 1169 11BE;AF43;1101 1169 11BE; # (꽃; 꽃; á„ᅩᆾ; 꽃; á„ᅩᆾ; ) HANGUL SYLLABLE GGOC
+AF44;AF44;1101 1169 11BF;AF44;1101 1169 11BF; # (꽄; 꽄; á„ᅩᆿ; 꽄; á„ᅩᆿ; ) HANGUL SYLLABLE GGOK
+AF45;AF45;1101 1169 11C0;AF45;1101 1169 11C0; # (ê½…; ê½…; á„ᅩᇀ; ê½…; á„ᅩᇀ; ) HANGUL SYLLABLE GGOT
+AF46;AF46;1101 1169 11C1;AF46;1101 1169 11C1; # (꽆; 꽆; á„á…©á‡; 꽆; á„á…©á‡; ) HANGUL SYLLABLE GGOP
+AF47;AF47;1101 1169 11C2;AF47;1101 1169 11C2; # (꽇; 꽇; á„ᅩᇂ; 꽇; á„ᅩᇂ; ) HANGUL SYLLABLE GGOH
+AF48;AF48;1101 116A;AF48;1101 116A; # (꽈; 꽈; á„á…ª; 꽈; á„á…ª; ) HANGUL SYLLABLE GGWA
+AF49;AF49;1101 116A 11A8;AF49;1101 116A 11A8; # (꽉; 꽉; á„ᅪᆨ; 꽉; á„ᅪᆨ; ) HANGUL SYLLABLE GGWAG
+AF4A;AF4A;1101 116A 11A9;AF4A;1101 116A 11A9; # (꽊; 꽊; á„ᅪᆩ; 꽊; á„ᅪᆩ; ) HANGUL SYLLABLE GGWAGG
+AF4B;AF4B;1101 116A 11AA;AF4B;1101 116A 11AA; # (꽋; 꽋; á„ᅪᆪ; 꽋; á„ᅪᆪ; ) HANGUL SYLLABLE GGWAGS
+AF4C;AF4C;1101 116A 11AB;AF4C;1101 116A 11AB; # (꽌; 꽌; á„ᅪᆫ; 꽌; á„ᅪᆫ; ) HANGUL SYLLABLE GGWAN
+AF4D;AF4D;1101 116A 11AC;AF4D;1101 116A 11AC; # (ê½; ê½; á„ᅪᆬ; ê½; á„ᅪᆬ; ) HANGUL SYLLABLE GGWANJ
+AF4E;AF4E;1101 116A 11AD;AF4E;1101 116A 11AD; # (꽎; 꽎; á„ᅪᆭ; 꽎; á„ᅪᆭ; ) HANGUL SYLLABLE GGWANH
+AF4F;AF4F;1101 116A 11AE;AF4F;1101 116A 11AE; # (ê½; ê½; á„ᅪᆮ; ê½; á„ᅪᆮ; ) HANGUL SYLLABLE GGWAD
+AF50;AF50;1101 116A 11AF;AF50;1101 116A 11AF; # (ê½; ê½; á„ᅪᆯ; ê½; á„ᅪᆯ; ) HANGUL SYLLABLE GGWAL
+AF51;AF51;1101 116A 11B0;AF51;1101 116A 11B0; # (꽑; 꽑; á„ᅪᆰ; 꽑; á„ᅪᆰ; ) HANGUL SYLLABLE GGWALG
+AF52;AF52;1101 116A 11B1;AF52;1101 116A 11B1; # (ê½’; ê½’; á„ᅪᆱ; ê½’; á„ᅪᆱ; ) HANGUL SYLLABLE GGWALM
+AF53;AF53;1101 116A 11B2;AF53;1101 116A 11B2; # (꽓; 꽓; á„ᅪᆲ; 꽓; á„ᅪᆲ; ) HANGUL SYLLABLE GGWALB
+AF54;AF54;1101 116A 11B3;AF54;1101 116A 11B3; # (ê½”; ê½”; á„ᅪᆳ; ê½”; á„ᅪᆳ; ) HANGUL SYLLABLE GGWALS
+AF55;AF55;1101 116A 11B4;AF55;1101 116A 11B4; # (꽕; 꽕; á„ᅪᆴ; 꽕; á„ᅪᆴ; ) HANGUL SYLLABLE GGWALT
+AF56;AF56;1101 116A 11B5;AF56;1101 116A 11B5; # (ê½–; ê½–; á„ᅪᆵ; ê½–; á„ᅪᆵ; ) HANGUL SYLLABLE GGWALP
+AF57;AF57;1101 116A 11B6;AF57;1101 116A 11B6; # (ê½—; ê½—; á„ᅪᆶ; ê½—; á„ᅪᆶ; ) HANGUL SYLLABLE GGWALH
+AF58;AF58;1101 116A 11B7;AF58;1101 116A 11B7; # (꽘; 꽘; á„ᅪᆷ; 꽘; á„ᅪᆷ; ) HANGUL SYLLABLE GGWAM
+AF59;AF59;1101 116A 11B8;AF59;1101 116A 11B8; # (ê½™; ê½™; á„ᅪᆸ; ê½™; á„ᅪᆸ; ) HANGUL SYLLABLE GGWAB
+AF5A;AF5A;1101 116A 11B9;AF5A;1101 116A 11B9; # (꽚; 꽚; á„ᅪᆹ; 꽚; á„ᅪᆹ; ) HANGUL SYLLABLE GGWABS
+AF5B;AF5B;1101 116A 11BA;AF5B;1101 116A 11BA; # (ê½›; ê½›; á„ᅪᆺ; ê½›; á„ᅪᆺ; ) HANGUL SYLLABLE GGWAS
+AF5C;AF5C;1101 116A 11BB;AF5C;1101 116A 11BB; # (꽜; 꽜; á„ᅪᆻ; 꽜; á„ᅪᆻ; ) HANGUL SYLLABLE GGWASS
+AF5D;AF5D;1101 116A 11BC;AF5D;1101 116A 11BC; # (ê½; ê½; á„ᅪᆼ; ê½; á„ᅪᆼ; ) HANGUL SYLLABLE GGWANG
+AF5E;AF5E;1101 116A 11BD;AF5E;1101 116A 11BD; # (꽞; 꽞; á„ᅪᆽ; 꽞; á„ᅪᆽ; ) HANGUL SYLLABLE GGWAJ
+AF5F;AF5F;1101 116A 11BE;AF5F;1101 116A 11BE; # (꽟; 꽟; á„ᅪᆾ; 꽟; á„ᅪᆾ; ) HANGUL SYLLABLE GGWAC
+AF60;AF60;1101 116A 11BF;AF60;1101 116A 11BF; # (ê½ ; ê½ ; á„ᅪᆿ; ê½ ; á„ᅪᆿ; ) HANGUL SYLLABLE GGWAK
+AF61;AF61;1101 116A 11C0;AF61;1101 116A 11C0; # (꽡; 꽡; á„ᅪᇀ; 꽡; á„ᅪᇀ; ) HANGUL SYLLABLE GGWAT
+AF62;AF62;1101 116A 11C1;AF62;1101 116A 11C1; # (ê½¢; ê½¢; á„á…ªá‡; ê½¢; á„á…ªá‡; ) HANGUL SYLLABLE GGWAP
+AF63;AF63;1101 116A 11C2;AF63;1101 116A 11C2; # (ê½£; ê½£; á„ᅪᇂ; ê½£; á„ᅪᇂ; ) HANGUL SYLLABLE GGWAH
+AF64;AF64;1101 116B;AF64;1101 116B; # (꽤; 꽤; á„á…«; 꽤; á„á…«; ) HANGUL SYLLABLE GGWAE
+AF65;AF65;1101 116B 11A8;AF65;1101 116B 11A8; # (ê½¥; ê½¥; á„ᅫᆨ; ê½¥; á„ᅫᆨ; ) HANGUL SYLLABLE GGWAEG
+AF66;AF66;1101 116B 11A9;AF66;1101 116B 11A9; # (꽦; 꽦; á„ᅫᆩ; 꽦; á„ᅫᆩ; ) HANGUL SYLLABLE GGWAEGG
+AF67;AF67;1101 116B 11AA;AF67;1101 116B 11AA; # (ê½§; ê½§; á„ᅫᆪ; ê½§; á„ᅫᆪ; ) HANGUL SYLLABLE GGWAEGS
+AF68;AF68;1101 116B 11AB;AF68;1101 116B 11AB; # (꽨; 꽨; á„ᅫᆫ; 꽨; á„ᅫᆫ; ) HANGUL SYLLABLE GGWAEN
+AF69;AF69;1101 116B 11AC;AF69;1101 116B 11AC; # (꽩; 꽩; á„ᅫᆬ; 꽩; á„ᅫᆬ; ) HANGUL SYLLABLE GGWAENJ
+AF6A;AF6A;1101 116B 11AD;AF6A;1101 116B 11AD; # (꽪; 꽪; á„ᅫᆭ; 꽪; á„ᅫᆭ; ) HANGUL SYLLABLE GGWAENH
+AF6B;AF6B;1101 116B 11AE;AF6B;1101 116B 11AE; # (꽫; 꽫; á„ᅫᆮ; 꽫; á„ᅫᆮ; ) HANGUL SYLLABLE GGWAED
+AF6C;AF6C;1101 116B 11AF;AF6C;1101 116B 11AF; # (꽬; 꽬; á„ᅫᆯ; 꽬; á„ᅫᆯ; ) HANGUL SYLLABLE GGWAEL
+AF6D;AF6D;1101 116B 11B0;AF6D;1101 116B 11B0; # (ê½­; ê½­; á„ᅫᆰ; ê½­; á„ᅫᆰ; ) HANGUL SYLLABLE GGWAELG
+AF6E;AF6E;1101 116B 11B1;AF6E;1101 116B 11B1; # (ê½®; ê½®; á„ᅫᆱ; ê½®; á„ᅫᆱ; ) HANGUL SYLLABLE GGWAELM
+AF6F;AF6F;1101 116B 11B2;AF6F;1101 116B 11B2; # (꽯; 꽯; á„ᅫᆲ; 꽯; á„ᅫᆲ; ) HANGUL SYLLABLE GGWAELB
+AF70;AF70;1101 116B 11B3;AF70;1101 116B 11B3; # (ê½°; ê½°; á„ᅫᆳ; ê½°; á„ᅫᆳ; ) HANGUL SYLLABLE GGWAELS
+AF71;AF71;1101 116B 11B4;AF71;1101 116B 11B4; # (ê½±; ê½±; á„ᅫᆴ; ê½±; á„ᅫᆴ; ) HANGUL SYLLABLE GGWAELT
+AF72;AF72;1101 116B 11B5;AF72;1101 116B 11B5; # (ê½²; ê½²; á„ᅫᆵ; ê½²; á„ᅫᆵ; ) HANGUL SYLLABLE GGWAELP
+AF73;AF73;1101 116B 11B6;AF73;1101 116B 11B6; # (ê½³; ê½³; á„ᅫᆶ; ê½³; á„ᅫᆶ; ) HANGUL SYLLABLE GGWAELH
+AF74;AF74;1101 116B 11B7;AF74;1101 116B 11B7; # (ê½´; ê½´; á„ᅫᆷ; ê½´; á„ᅫᆷ; ) HANGUL SYLLABLE GGWAEM
+AF75;AF75;1101 116B 11B8;AF75;1101 116B 11B8; # (ê½µ; ê½µ; á„ᅫᆸ; ê½µ; á„ᅫᆸ; ) HANGUL SYLLABLE GGWAEB
+AF76;AF76;1101 116B 11B9;AF76;1101 116B 11B9; # (ê½¶; ê½¶; á„ᅫᆹ; ê½¶; á„ᅫᆹ; ) HANGUL SYLLABLE GGWAEBS
+AF77;AF77;1101 116B 11BA;AF77;1101 116B 11BA; # (ê½·; ê½·; á„ᅫᆺ; ê½·; á„ᅫᆺ; ) HANGUL SYLLABLE GGWAES
+AF78;AF78;1101 116B 11BB;AF78;1101 116B 11BB; # (꽸; 꽸; á„ᅫᆻ; 꽸; á„ᅫᆻ; ) HANGUL SYLLABLE GGWAESS
+AF79;AF79;1101 116B 11BC;AF79;1101 116B 11BC; # (ê½¹; ê½¹; á„ᅫᆼ; ê½¹; á„ᅫᆼ; ) HANGUL SYLLABLE GGWAENG
+AF7A;AF7A;1101 116B 11BD;AF7A;1101 116B 11BD; # (꽺; 꽺; á„ᅫᆽ; 꽺; á„ᅫᆽ; ) HANGUL SYLLABLE GGWAEJ
+AF7B;AF7B;1101 116B 11BE;AF7B;1101 116B 11BE; # (ê½»; ê½»; á„ᅫᆾ; ê½»; á„ᅫᆾ; ) HANGUL SYLLABLE GGWAEC
+AF7C;AF7C;1101 116B 11BF;AF7C;1101 116B 11BF; # (ê½¼; ê½¼; á„ᅫᆿ; ê½¼; á„ᅫᆿ; ) HANGUL SYLLABLE GGWAEK
+AF7D;AF7D;1101 116B 11C0;AF7D;1101 116B 11C0; # (ê½½; ê½½; á„ᅫᇀ; ê½½; á„ᅫᇀ; ) HANGUL SYLLABLE GGWAET
+AF7E;AF7E;1101 116B 11C1;AF7E;1101 116B 11C1; # (ê½¾; ê½¾; á„á…«á‡; ê½¾; á„á…«á‡; ) HANGUL SYLLABLE GGWAEP
+AF7F;AF7F;1101 116B 11C2;AF7F;1101 116B 11C2; # (꽿; 꽿; á„ᅫᇂ; 꽿; á„ᅫᇂ; ) HANGUL SYLLABLE GGWAEH
+AF80;AF80;1101 116C;AF80;1101 116C; # (ê¾€; ê¾€; á„á…¬; ê¾€; á„á…¬; ) HANGUL SYLLABLE GGOE
+AF81;AF81;1101 116C 11A8;AF81;1101 116C 11A8; # (ê¾; ê¾; á„ᅬᆨ; ê¾; á„ᅬᆨ; ) HANGUL SYLLABLE GGOEG
+AF82;AF82;1101 116C 11A9;AF82;1101 116C 11A9; # (꾂; 꾂; á„ᅬᆩ; 꾂; á„ᅬᆩ; ) HANGUL SYLLABLE GGOEGG
+AF83;AF83;1101 116C 11AA;AF83;1101 116C 11AA; # (꾃; 꾃; á„ᅬᆪ; 꾃; á„ᅬᆪ; ) HANGUL SYLLABLE GGOEGS
+AF84;AF84;1101 116C 11AB;AF84;1101 116C 11AB; # (꾄; 꾄; á„ᅬᆫ; 꾄; á„ᅬᆫ; ) HANGUL SYLLABLE GGOEN
+AF85;AF85;1101 116C 11AC;AF85;1101 116C 11AC; # (ê¾…; ê¾…; á„ᅬᆬ; ê¾…; á„ᅬᆬ; ) HANGUL SYLLABLE GGOENJ
+AF86;AF86;1101 116C 11AD;AF86;1101 116C 11AD; # (꾆; 꾆; á„ᅬᆭ; 꾆; á„ᅬᆭ; ) HANGUL SYLLABLE GGOENH
+AF87;AF87;1101 116C 11AE;AF87;1101 116C 11AE; # (꾇; 꾇; á„ᅬᆮ; 꾇; á„ᅬᆮ; ) HANGUL SYLLABLE GGOED
+AF88;AF88;1101 116C 11AF;AF88;1101 116C 11AF; # (꾈; 꾈; á„ᅬᆯ; 꾈; á„ᅬᆯ; ) HANGUL SYLLABLE GGOEL
+AF89;AF89;1101 116C 11B0;AF89;1101 116C 11B0; # (꾉; 꾉; á„ᅬᆰ; 꾉; á„ᅬᆰ; ) HANGUL SYLLABLE GGOELG
+AF8A;AF8A;1101 116C 11B1;AF8A;1101 116C 11B1; # (꾊; 꾊; á„ᅬᆱ; 꾊; á„ᅬᆱ; ) HANGUL SYLLABLE GGOELM
+AF8B;AF8B;1101 116C 11B2;AF8B;1101 116C 11B2; # (꾋; 꾋; á„ᅬᆲ; 꾋; á„ᅬᆲ; ) HANGUL SYLLABLE GGOELB
+AF8C;AF8C;1101 116C 11B3;AF8C;1101 116C 11B3; # (꾌; 꾌; á„ᅬᆳ; 꾌; á„ᅬᆳ; ) HANGUL SYLLABLE GGOELS
+AF8D;AF8D;1101 116C 11B4;AF8D;1101 116C 11B4; # (ê¾; ê¾; á„ᅬᆴ; ê¾; á„ᅬᆴ; ) HANGUL SYLLABLE GGOELT
+AF8E;AF8E;1101 116C 11B5;AF8E;1101 116C 11B5; # (꾎; 꾎; á„ᅬᆵ; 꾎; á„ᅬᆵ; ) HANGUL SYLLABLE GGOELP
+AF8F;AF8F;1101 116C 11B6;AF8F;1101 116C 11B6; # (ê¾; ê¾; á„ᅬᆶ; ê¾; á„ᅬᆶ; ) HANGUL SYLLABLE GGOELH
+AF90;AF90;1101 116C 11B7;AF90;1101 116C 11B7; # (ê¾; ê¾; á„ᅬᆷ; ê¾; á„ᅬᆷ; ) HANGUL SYLLABLE GGOEM
+AF91;AF91;1101 116C 11B8;AF91;1101 116C 11B8; # (꾑; 꾑; á„ᅬᆸ; 꾑; á„ᅬᆸ; ) HANGUL SYLLABLE GGOEB
+AF92;AF92;1101 116C 11B9;AF92;1101 116C 11B9; # (ê¾’; ê¾’; á„ᅬᆹ; ê¾’; á„ᅬᆹ; ) HANGUL SYLLABLE GGOEBS
+AF93;AF93;1101 116C 11BA;AF93;1101 116C 11BA; # (꾓; 꾓; á„ᅬᆺ; 꾓; á„ᅬᆺ; ) HANGUL SYLLABLE GGOES
+AF94;AF94;1101 116C 11BB;AF94;1101 116C 11BB; # (ê¾”; ê¾”; á„ᅬᆻ; ê¾”; á„ᅬᆻ; ) HANGUL SYLLABLE GGOESS
+AF95;AF95;1101 116C 11BC;AF95;1101 116C 11BC; # (꾕; 꾕; á„ᅬᆼ; 꾕; á„ᅬᆼ; ) HANGUL SYLLABLE GGOENG
+AF96;AF96;1101 116C 11BD;AF96;1101 116C 11BD; # (ê¾–; ê¾–; á„ᅬᆽ; ê¾–; á„ᅬᆽ; ) HANGUL SYLLABLE GGOEJ
+AF97;AF97;1101 116C 11BE;AF97;1101 116C 11BE; # (ê¾—; ê¾—; á„ᅬᆾ; ê¾—; á„ᅬᆾ; ) HANGUL SYLLABLE GGOEC
+AF98;AF98;1101 116C 11BF;AF98;1101 116C 11BF; # (꾘; 꾘; á„ᅬᆿ; 꾘; á„ᅬᆿ; ) HANGUL SYLLABLE GGOEK
+AF99;AF99;1101 116C 11C0;AF99;1101 116C 11C0; # (ê¾™; ê¾™; á„ᅬᇀ; ê¾™; á„ᅬᇀ; ) HANGUL SYLLABLE GGOET
+AF9A;AF9A;1101 116C 11C1;AF9A;1101 116C 11C1; # (꾚; 꾚; á„á…¬á‡; 꾚; á„á…¬á‡; ) HANGUL SYLLABLE GGOEP
+AF9B;AF9B;1101 116C 11C2;AF9B;1101 116C 11C2; # (ê¾›; ê¾›; á„ᅬᇂ; ê¾›; á„ᅬᇂ; ) HANGUL SYLLABLE GGOEH
+AF9C;AF9C;1101 116D;AF9C;1101 116D; # (꾜; 꾜; á„á…­; 꾜; á„á…­; ) HANGUL SYLLABLE GGYO
+AF9D;AF9D;1101 116D 11A8;AF9D;1101 116D 11A8; # (ê¾; ê¾; á„ᅭᆨ; ê¾; á„ᅭᆨ; ) HANGUL SYLLABLE GGYOG
+AF9E;AF9E;1101 116D 11A9;AF9E;1101 116D 11A9; # (꾞; 꾞; á„ᅭᆩ; 꾞; á„ᅭᆩ; ) HANGUL SYLLABLE GGYOGG
+AF9F;AF9F;1101 116D 11AA;AF9F;1101 116D 11AA; # (꾟; 꾟; á„ᅭᆪ; 꾟; á„ᅭᆪ; ) HANGUL SYLLABLE GGYOGS
+AFA0;AFA0;1101 116D 11AB;AFA0;1101 116D 11AB; # (ê¾ ; ê¾ ; á„ᅭᆫ; ê¾ ; á„ᅭᆫ; ) HANGUL SYLLABLE GGYON
+AFA1;AFA1;1101 116D 11AC;AFA1;1101 116D 11AC; # (꾡; 꾡; á„ᅭᆬ; 꾡; á„ᅭᆬ; ) HANGUL SYLLABLE GGYONJ
+AFA2;AFA2;1101 116D 11AD;AFA2;1101 116D 11AD; # (ê¾¢; ê¾¢; á„ᅭᆭ; ê¾¢; á„ᅭᆭ; ) HANGUL SYLLABLE GGYONH
+AFA3;AFA3;1101 116D 11AE;AFA3;1101 116D 11AE; # (ê¾£; ê¾£; á„ᅭᆮ; ê¾£; á„ᅭᆮ; ) HANGUL SYLLABLE GGYOD
+AFA4;AFA4;1101 116D 11AF;AFA4;1101 116D 11AF; # (꾤; 꾤; á„ᅭᆯ; 꾤; á„ᅭᆯ; ) HANGUL SYLLABLE GGYOL
+AFA5;AFA5;1101 116D 11B0;AFA5;1101 116D 11B0; # (ê¾¥; ê¾¥; á„ᅭᆰ; ê¾¥; á„ᅭᆰ; ) HANGUL SYLLABLE GGYOLG
+AFA6;AFA6;1101 116D 11B1;AFA6;1101 116D 11B1; # (꾦; 꾦; á„ᅭᆱ; 꾦; á„ᅭᆱ; ) HANGUL SYLLABLE GGYOLM
+AFA7;AFA7;1101 116D 11B2;AFA7;1101 116D 11B2; # (ê¾§; ê¾§; á„ᅭᆲ; ê¾§; á„ᅭᆲ; ) HANGUL SYLLABLE GGYOLB
+AFA8;AFA8;1101 116D 11B3;AFA8;1101 116D 11B3; # (꾨; 꾨; á„ᅭᆳ; 꾨; á„ᅭᆳ; ) HANGUL SYLLABLE GGYOLS
+AFA9;AFA9;1101 116D 11B4;AFA9;1101 116D 11B4; # (꾩; 꾩; á„ᅭᆴ; 꾩; á„ᅭᆴ; ) HANGUL SYLLABLE GGYOLT
+AFAA;AFAA;1101 116D 11B5;AFAA;1101 116D 11B5; # (꾪; 꾪; á„ᅭᆵ; 꾪; á„ᅭᆵ; ) HANGUL SYLLABLE GGYOLP
+AFAB;AFAB;1101 116D 11B6;AFAB;1101 116D 11B6; # (꾫; 꾫; á„ᅭᆶ; 꾫; á„ᅭᆶ; ) HANGUL SYLLABLE GGYOLH
+AFAC;AFAC;1101 116D 11B7;AFAC;1101 116D 11B7; # (꾬; 꾬; á„ᅭᆷ; 꾬; á„ᅭᆷ; ) HANGUL SYLLABLE GGYOM
+AFAD;AFAD;1101 116D 11B8;AFAD;1101 116D 11B8; # (ê¾­; ê¾­; á„ᅭᆸ; ê¾­; á„ᅭᆸ; ) HANGUL SYLLABLE GGYOB
+AFAE;AFAE;1101 116D 11B9;AFAE;1101 116D 11B9; # (ê¾®; ê¾®; á„ᅭᆹ; ê¾®; á„ᅭᆹ; ) HANGUL SYLLABLE GGYOBS
+AFAF;AFAF;1101 116D 11BA;AFAF;1101 116D 11BA; # (꾯; 꾯; á„ᅭᆺ; 꾯; á„ᅭᆺ; ) HANGUL SYLLABLE GGYOS
+AFB0;AFB0;1101 116D 11BB;AFB0;1101 116D 11BB; # (ê¾°; ê¾°; á„ᅭᆻ; ê¾°; á„ᅭᆻ; ) HANGUL SYLLABLE GGYOSS
+AFB1;AFB1;1101 116D 11BC;AFB1;1101 116D 11BC; # (ê¾±; ê¾±; á„ᅭᆼ; ê¾±; á„ᅭᆼ; ) HANGUL SYLLABLE GGYONG
+AFB2;AFB2;1101 116D 11BD;AFB2;1101 116D 11BD; # (ê¾²; ê¾²; á„ᅭᆽ; ê¾²; á„ᅭᆽ; ) HANGUL SYLLABLE GGYOJ
+AFB3;AFB3;1101 116D 11BE;AFB3;1101 116D 11BE; # (ê¾³; ê¾³; á„ᅭᆾ; ê¾³; á„ᅭᆾ; ) HANGUL SYLLABLE GGYOC
+AFB4;AFB4;1101 116D 11BF;AFB4;1101 116D 11BF; # (ê¾´; ê¾´; á„ᅭᆿ; ê¾´; á„ᅭᆿ; ) HANGUL SYLLABLE GGYOK
+AFB5;AFB5;1101 116D 11C0;AFB5;1101 116D 11C0; # (ê¾µ; ê¾µ; á„ᅭᇀ; ê¾µ; á„ᅭᇀ; ) HANGUL SYLLABLE GGYOT
+AFB6;AFB6;1101 116D 11C1;AFB6;1101 116D 11C1; # (ê¾¶; ê¾¶; á„á…­á‡; ê¾¶; á„á…­á‡; ) HANGUL SYLLABLE GGYOP
+AFB7;AFB7;1101 116D 11C2;AFB7;1101 116D 11C2; # (ê¾·; ê¾·; á„ᅭᇂ; ê¾·; á„ᅭᇂ; ) HANGUL SYLLABLE GGYOH
+AFB8;AFB8;1101 116E;AFB8;1101 116E; # (꾸; 꾸; á„á…®; 꾸; á„á…®; ) HANGUL SYLLABLE GGU
+AFB9;AFB9;1101 116E 11A8;AFB9;1101 116E 11A8; # (ê¾¹; ê¾¹; á„ᅮᆨ; ê¾¹; á„ᅮᆨ; ) HANGUL SYLLABLE GGUG
+AFBA;AFBA;1101 116E 11A9;AFBA;1101 116E 11A9; # (꾺; 꾺; á„ᅮᆩ; 꾺; á„ᅮᆩ; ) HANGUL SYLLABLE GGUGG
+AFBB;AFBB;1101 116E 11AA;AFBB;1101 116E 11AA; # (ê¾»; ê¾»; á„ᅮᆪ; ê¾»; á„ᅮᆪ; ) HANGUL SYLLABLE GGUGS
+AFBC;AFBC;1101 116E 11AB;AFBC;1101 116E 11AB; # (ê¾¼; ê¾¼; á„ᅮᆫ; ê¾¼; á„ᅮᆫ; ) HANGUL SYLLABLE GGUN
+AFBD;AFBD;1101 116E 11AC;AFBD;1101 116E 11AC; # (ê¾½; ê¾½; á„ᅮᆬ; ê¾½; á„ᅮᆬ; ) HANGUL SYLLABLE GGUNJ
+AFBE;AFBE;1101 116E 11AD;AFBE;1101 116E 11AD; # (ê¾¾; ê¾¾; á„ᅮᆭ; ê¾¾; á„ᅮᆭ; ) HANGUL SYLLABLE GGUNH
+AFBF;AFBF;1101 116E 11AE;AFBF;1101 116E 11AE; # (꾿; 꾿; á„ᅮᆮ; 꾿; á„ᅮᆮ; ) HANGUL SYLLABLE GGUD
+AFC0;AFC0;1101 116E 11AF;AFC0;1101 116E 11AF; # (ê¿€; ê¿€; á„ᅮᆯ; ê¿€; á„ᅮᆯ; ) HANGUL SYLLABLE GGUL
+AFC1;AFC1;1101 116E 11B0;AFC1;1101 116E 11B0; # (ê¿; ê¿; á„ᅮᆰ; ê¿; á„ᅮᆰ; ) HANGUL SYLLABLE GGULG
+AFC2;AFC2;1101 116E 11B1;AFC2;1101 116E 11B1; # (ê¿‚; ê¿‚; á„ᅮᆱ; ê¿‚; á„ᅮᆱ; ) HANGUL SYLLABLE GGULM
+AFC3;AFC3;1101 116E 11B2;AFC3;1101 116E 11B2; # (꿃; 꿃; á„ᅮᆲ; 꿃; á„ᅮᆲ; ) HANGUL SYLLABLE GGULB
+AFC4;AFC4;1101 116E 11B3;AFC4;1101 116E 11B3; # (ê¿„; ê¿„; á„ᅮᆳ; ê¿„; á„ᅮᆳ; ) HANGUL SYLLABLE GGULS
+AFC5;AFC5;1101 116E 11B4;AFC5;1101 116E 11B4; # (ê¿…; ê¿…; á„ᅮᆴ; ê¿…; á„ᅮᆴ; ) HANGUL SYLLABLE GGULT
+AFC6;AFC6;1101 116E 11B5;AFC6;1101 116E 11B5; # (꿆; 꿆; á„ᅮᆵ; 꿆; á„ᅮᆵ; ) HANGUL SYLLABLE GGULP
+AFC7;AFC7;1101 116E 11B6;AFC7;1101 116E 11B6; # (꿇; 꿇; á„ᅮᆶ; 꿇; á„ᅮᆶ; ) HANGUL SYLLABLE GGULH
+AFC8;AFC8;1101 116E 11B7;AFC8;1101 116E 11B7; # (꿈; 꿈; á„ᅮᆷ; 꿈; á„ᅮᆷ; ) HANGUL SYLLABLE GGUM
+AFC9;AFC9;1101 116E 11B8;AFC9;1101 116E 11B8; # (꿉; 꿉; á„ᅮᆸ; 꿉; á„ᅮᆸ; ) HANGUL SYLLABLE GGUB
+AFCA;AFCA;1101 116E 11B9;AFCA;1101 116E 11B9; # (꿊; 꿊; á„ᅮᆹ; 꿊; á„ᅮᆹ; ) HANGUL SYLLABLE GGUBS
+AFCB;AFCB;1101 116E 11BA;AFCB;1101 116E 11BA; # (ê¿‹; ê¿‹; á„ᅮᆺ; ê¿‹; á„ᅮᆺ; ) HANGUL SYLLABLE GGUS
+AFCC;AFCC;1101 116E 11BB;AFCC;1101 116E 11BB; # (꿌; 꿌; á„ᅮᆻ; 꿌; á„ᅮᆻ; ) HANGUL SYLLABLE GGUSS
+AFCD;AFCD;1101 116E 11BC;AFCD;1101 116E 11BC; # (ê¿; ê¿; á„ᅮᆼ; ê¿; á„ᅮᆼ; ) HANGUL SYLLABLE GGUNG
+AFCE;AFCE;1101 116E 11BD;AFCE;1101 116E 11BD; # (꿎; 꿎; á„ᅮᆽ; 꿎; á„ᅮᆽ; ) HANGUL SYLLABLE GGUJ
+AFCF;AFCF;1101 116E 11BE;AFCF;1101 116E 11BE; # (ê¿; ê¿; á„ᅮᆾ; ê¿; á„ᅮᆾ; ) HANGUL SYLLABLE GGUC
+AFD0;AFD0;1101 116E 11BF;AFD0;1101 116E 11BF; # (ê¿; ê¿; á„ᅮᆿ; ê¿; á„ᅮᆿ; ) HANGUL SYLLABLE GGUK
+AFD1;AFD1;1101 116E 11C0;AFD1;1101 116E 11C0; # (ê¿‘; ê¿‘; á„ᅮᇀ; ê¿‘; á„ᅮᇀ; ) HANGUL SYLLABLE GGUT
+AFD2;AFD2;1101 116E 11C1;AFD2;1101 116E 11C1; # (ê¿’; ê¿’; á„á…®á‡; ê¿’; á„á…®á‡; ) HANGUL SYLLABLE GGUP
+AFD3;AFD3;1101 116E 11C2;AFD3;1101 116E 11C2; # (ê¿“; ê¿“; á„ᅮᇂ; ê¿“; á„ᅮᇂ; ) HANGUL SYLLABLE GGUH
+AFD4;AFD4;1101 116F;AFD4;1101 116F; # (ê¿”; ê¿”; á„á…¯; ê¿”; á„á…¯; ) HANGUL SYLLABLE GGWEO
+AFD5;AFD5;1101 116F 11A8;AFD5;1101 116F 11A8; # (ê¿•; ê¿•; á„ᅯᆨ; ê¿•; á„ᅯᆨ; ) HANGUL SYLLABLE GGWEOG
+AFD6;AFD6;1101 116F 11A9;AFD6;1101 116F 11A9; # (ê¿–; ê¿–; á„ᅯᆩ; ê¿–; á„ᅯᆩ; ) HANGUL SYLLABLE GGWEOGG
+AFD7;AFD7;1101 116F 11AA;AFD7;1101 116F 11AA; # (ê¿—; ê¿—; á„ᅯᆪ; ê¿—; á„ᅯᆪ; ) HANGUL SYLLABLE GGWEOGS
+AFD8;AFD8;1101 116F 11AB;AFD8;1101 116F 11AB; # (꿘; 꿘; á„ᅯᆫ; 꿘; á„ᅯᆫ; ) HANGUL SYLLABLE GGWEON
+AFD9;AFD9;1101 116F 11AC;AFD9;1101 116F 11AC; # (ê¿™; ê¿™; á„ᅯᆬ; ê¿™; á„ᅯᆬ; ) HANGUL SYLLABLE GGWEONJ
+AFDA;AFDA;1101 116F 11AD;AFDA;1101 116F 11AD; # (꿚; 꿚; á„ᅯᆭ; 꿚; á„ᅯᆭ; ) HANGUL SYLLABLE GGWEONH
+AFDB;AFDB;1101 116F 11AE;AFDB;1101 116F 11AE; # (ê¿›; ê¿›; á„ᅯᆮ; ê¿›; á„ᅯᆮ; ) HANGUL SYLLABLE GGWEOD
+AFDC;AFDC;1101 116F 11AF;AFDC;1101 116F 11AF; # (꿜; 꿜; á„ᅯᆯ; 꿜; á„ᅯᆯ; ) HANGUL SYLLABLE GGWEOL
+AFDD;AFDD;1101 116F 11B0;AFDD;1101 116F 11B0; # (ê¿; ê¿; á„ᅯᆰ; ê¿; á„ᅯᆰ; ) HANGUL SYLLABLE GGWEOLG
+AFDE;AFDE;1101 116F 11B1;AFDE;1101 116F 11B1; # (꿞; 꿞; á„ᅯᆱ; 꿞; á„ᅯᆱ; ) HANGUL SYLLABLE GGWEOLM
+AFDF;AFDF;1101 116F 11B2;AFDF;1101 116F 11B2; # (꿟; 꿟; á„ᅯᆲ; 꿟; á„ᅯᆲ; ) HANGUL SYLLABLE GGWEOLB
+AFE0;AFE0;1101 116F 11B3;AFE0;1101 116F 11B3; # (ê¿ ; ê¿ ; á„ᅯᆳ; ê¿ ; á„ᅯᆳ; ) HANGUL SYLLABLE GGWEOLS
+AFE1;AFE1;1101 116F 11B4;AFE1;1101 116F 11B4; # (ê¿¡; ê¿¡; á„ᅯᆴ; ê¿¡; á„ᅯᆴ; ) HANGUL SYLLABLE GGWEOLT
+AFE2;AFE2;1101 116F 11B5;AFE2;1101 116F 11B5; # (ê¿¢; ê¿¢; á„ᅯᆵ; ê¿¢; á„ᅯᆵ; ) HANGUL SYLLABLE GGWEOLP
+AFE3;AFE3;1101 116F 11B6;AFE3;1101 116F 11B6; # (ê¿£; ê¿£; á„ᅯᆶ; ê¿£; á„ᅯᆶ; ) HANGUL SYLLABLE GGWEOLH
+AFE4;AFE4;1101 116F 11B7;AFE4;1101 116F 11B7; # (꿤; 꿤; á„ᅯᆷ; 꿤; á„ᅯᆷ; ) HANGUL SYLLABLE GGWEOM
+AFE5;AFE5;1101 116F 11B8;AFE5;1101 116F 11B8; # (ê¿¥; ê¿¥; á„ᅯᆸ; ê¿¥; á„ᅯᆸ; ) HANGUL SYLLABLE GGWEOB
+AFE6;AFE6;1101 116F 11B9;AFE6;1101 116F 11B9; # (꿦; 꿦; á„ᅯᆹ; 꿦; á„ᅯᆹ; ) HANGUL SYLLABLE GGWEOBS
+AFE7;AFE7;1101 116F 11BA;AFE7;1101 116F 11BA; # (ê¿§; ê¿§; á„ᅯᆺ; ê¿§; á„ᅯᆺ; ) HANGUL SYLLABLE GGWEOS
+AFE8;AFE8;1101 116F 11BB;AFE8;1101 116F 11BB; # (꿨; 꿨; á„ᅯᆻ; 꿨; á„ᅯᆻ; ) HANGUL SYLLABLE GGWEOSS
+AFE9;AFE9;1101 116F 11BC;AFE9;1101 116F 11BC; # (ê¿©; ê¿©; á„ᅯᆼ; ê¿©; á„ᅯᆼ; ) HANGUL SYLLABLE GGWEONG
+AFEA;AFEA;1101 116F 11BD;AFEA;1101 116F 11BD; # (꿪; 꿪; á„ᅯᆽ; 꿪; á„ᅯᆽ; ) HANGUL SYLLABLE GGWEOJ
+AFEB;AFEB;1101 116F 11BE;AFEB;1101 116F 11BE; # (ê¿«; ê¿«; á„ᅯᆾ; ê¿«; á„ᅯᆾ; ) HANGUL SYLLABLE GGWEOC
+AFEC;AFEC;1101 116F 11BF;AFEC;1101 116F 11BF; # (꿬; 꿬; á„ᅯᆿ; 꿬; á„ᅯᆿ; ) HANGUL SYLLABLE GGWEOK
+AFED;AFED;1101 116F 11C0;AFED;1101 116F 11C0; # (ê¿­; ê¿­; á„ᅯᇀ; ê¿­; á„ᅯᇀ; ) HANGUL SYLLABLE GGWEOT
+AFEE;AFEE;1101 116F 11C1;AFEE;1101 116F 11C1; # (ê¿®; ê¿®; á„á…¯á‡; ê¿®; á„á…¯á‡; ) HANGUL SYLLABLE GGWEOP
+AFEF;AFEF;1101 116F 11C2;AFEF;1101 116F 11C2; # (꿯; 꿯; á„ᅯᇂ; 꿯; á„ᅯᇂ; ) HANGUL SYLLABLE GGWEOH
+AFF0;AFF0;1101 1170;AFF0;1101 1170; # (ê¿°; ê¿°; á„á…°; ê¿°; á„á…°; ) HANGUL SYLLABLE GGWE
+AFF1;AFF1;1101 1170 11A8;AFF1;1101 1170 11A8; # (꿱; 꿱; á„ᅰᆨ; 꿱; á„ᅰᆨ; ) HANGUL SYLLABLE GGWEG
+AFF2;AFF2;1101 1170 11A9;AFF2;1101 1170 11A9; # (꿲; 꿲; á„ᅰᆩ; 꿲; á„ᅰᆩ; ) HANGUL SYLLABLE GGWEGG
+AFF3;AFF3;1101 1170 11AA;AFF3;1101 1170 11AA; # (꿳; 꿳; á„ᅰᆪ; 꿳; á„ᅰᆪ; ) HANGUL SYLLABLE GGWEGS
+AFF4;AFF4;1101 1170 11AB;AFF4;1101 1170 11AB; # (ê¿´; ê¿´; á„ᅰᆫ; ê¿´; á„ᅰᆫ; ) HANGUL SYLLABLE GGWEN
+AFF5;AFF5;1101 1170 11AC;AFF5;1101 1170 11AC; # (꿵; 꿵; á„ᅰᆬ; 꿵; á„ᅰᆬ; ) HANGUL SYLLABLE GGWENJ
+AFF6;AFF6;1101 1170 11AD;AFF6;1101 1170 11AD; # (ê¿¶; ê¿¶; á„ᅰᆭ; ê¿¶; á„ᅰᆭ; ) HANGUL SYLLABLE GGWENH
+AFF7;AFF7;1101 1170 11AE;AFF7;1101 1170 11AE; # (ê¿·; ê¿·; á„ᅰᆮ; ê¿·; á„ᅰᆮ; ) HANGUL SYLLABLE GGWED
+AFF8;AFF8;1101 1170 11AF;AFF8;1101 1170 11AF; # (꿸; 꿸; á„ᅰᆯ; 꿸; á„ᅰᆯ; ) HANGUL SYLLABLE GGWEL
+AFF9;AFF9;1101 1170 11B0;AFF9;1101 1170 11B0; # (꿹; 꿹; á„ᅰᆰ; 꿹; á„ᅰᆰ; ) HANGUL SYLLABLE GGWELG
+AFFA;AFFA;1101 1170 11B1;AFFA;1101 1170 11B1; # (꿺; 꿺; á„ᅰᆱ; 꿺; á„ᅰᆱ; ) HANGUL SYLLABLE GGWELM
+AFFB;AFFB;1101 1170 11B2;AFFB;1101 1170 11B2; # (ê¿»; ê¿»; á„ᅰᆲ; ê¿»; á„ᅰᆲ; ) HANGUL SYLLABLE GGWELB
+AFFC;AFFC;1101 1170 11B3;AFFC;1101 1170 11B3; # (꿼; 꿼; á„ᅰᆳ; 꿼; á„ᅰᆳ; ) HANGUL SYLLABLE GGWELS
+AFFD;AFFD;1101 1170 11B4;AFFD;1101 1170 11B4; # (꿽; 꿽; á„ᅰᆴ; 꿽; á„ᅰᆴ; ) HANGUL SYLLABLE GGWELT
+AFFE;AFFE;1101 1170 11B5;AFFE;1101 1170 11B5; # (꿾; 꿾; á„ᅰᆵ; 꿾; á„ᅰᆵ; ) HANGUL SYLLABLE GGWELP
+AFFF;AFFF;1101 1170 11B6;AFFF;1101 1170 11B6; # (ê¿¿; ê¿¿; á„ᅰᆶ; ê¿¿; á„ᅰᆶ; ) HANGUL SYLLABLE GGWELH
+B000;B000;1101 1170 11B7;B000;1101 1170 11B7; # (뀀; 뀀; á„ᅰᆷ; 뀀; á„ᅰᆷ; ) HANGUL SYLLABLE GGWEM
+B001;B001;1101 1170 11B8;B001;1101 1170 11B8; # (ë€; ë€; á„ᅰᆸ; ë€; á„ᅰᆸ; ) HANGUL SYLLABLE GGWEB
+B002;B002;1101 1170 11B9;B002;1101 1170 11B9; # (뀂; 뀂; á„ᅰᆹ; 뀂; á„ᅰᆹ; ) HANGUL SYLLABLE GGWEBS
+B003;B003;1101 1170 11BA;B003;1101 1170 11BA; # (뀃; 뀃; á„ᅰᆺ; 뀃; á„ᅰᆺ; ) HANGUL SYLLABLE GGWES
+B004;B004;1101 1170 11BB;B004;1101 1170 11BB; # (뀄; 뀄; á„ᅰᆻ; 뀄; á„ᅰᆻ; ) HANGUL SYLLABLE GGWESS
+B005;B005;1101 1170 11BC;B005;1101 1170 11BC; # (뀅; 뀅; á„ᅰᆼ; 뀅; á„ᅰᆼ; ) HANGUL SYLLABLE GGWENG
+B006;B006;1101 1170 11BD;B006;1101 1170 11BD; # (뀆; 뀆; á„ᅰᆽ; 뀆; á„ᅰᆽ; ) HANGUL SYLLABLE GGWEJ
+B007;B007;1101 1170 11BE;B007;1101 1170 11BE; # (뀇; 뀇; á„ᅰᆾ; 뀇; á„ᅰᆾ; ) HANGUL SYLLABLE GGWEC
+B008;B008;1101 1170 11BF;B008;1101 1170 11BF; # (뀈; 뀈; á„ᅰᆿ; 뀈; á„ᅰᆿ; ) HANGUL SYLLABLE GGWEK
+B009;B009;1101 1170 11C0;B009;1101 1170 11C0; # (뀉; 뀉; á„ᅰᇀ; 뀉; á„ᅰᇀ; ) HANGUL SYLLABLE GGWET
+B00A;B00A;1101 1170 11C1;B00A;1101 1170 11C1; # (뀊; 뀊; á„á…°á‡; 뀊; á„á…°á‡; ) HANGUL SYLLABLE GGWEP
+B00B;B00B;1101 1170 11C2;B00B;1101 1170 11C2; # (뀋; 뀋; á„ᅰᇂ; 뀋; á„ᅰᇂ; ) HANGUL SYLLABLE GGWEH
+B00C;B00C;1101 1171;B00C;1101 1171; # (뀌; 뀌; á„á…±; 뀌; á„á…±; ) HANGUL SYLLABLE GGWI
+B00D;B00D;1101 1171 11A8;B00D;1101 1171 11A8; # (ë€; ë€; á„ᅱᆨ; ë€; á„ᅱᆨ; ) HANGUL SYLLABLE GGWIG
+B00E;B00E;1101 1171 11A9;B00E;1101 1171 11A9; # (뀎; 뀎; á„ᅱᆩ; 뀎; á„ᅱᆩ; ) HANGUL SYLLABLE GGWIGG
+B00F;B00F;1101 1171 11AA;B00F;1101 1171 11AA; # (ë€; ë€; á„ᅱᆪ; ë€; á„ᅱᆪ; ) HANGUL SYLLABLE GGWIGS
+B010;B010;1101 1171 11AB;B010;1101 1171 11AB; # (ë€; ë€; á„ᅱᆫ; ë€; á„ᅱᆫ; ) HANGUL SYLLABLE GGWIN
+B011;B011;1101 1171 11AC;B011;1101 1171 11AC; # (뀑; 뀑; á„ᅱᆬ; 뀑; á„ᅱᆬ; ) HANGUL SYLLABLE GGWINJ
+B012;B012;1101 1171 11AD;B012;1101 1171 11AD; # (뀒; 뀒; á„ᅱᆭ; 뀒; á„ᅱᆭ; ) HANGUL SYLLABLE GGWINH
+B013;B013;1101 1171 11AE;B013;1101 1171 11AE; # (뀓; 뀓; á„ᅱᆮ; 뀓; á„ᅱᆮ; ) HANGUL SYLLABLE GGWID
+B014;B014;1101 1171 11AF;B014;1101 1171 11AF; # (뀔; 뀔; á„ᅱᆯ; 뀔; á„ᅱᆯ; ) HANGUL SYLLABLE GGWIL
+B015;B015;1101 1171 11B0;B015;1101 1171 11B0; # (뀕; 뀕; á„ᅱᆰ; 뀕; á„ᅱᆰ; ) HANGUL SYLLABLE GGWILG
+B016;B016;1101 1171 11B1;B016;1101 1171 11B1; # (뀖; 뀖; á„ᅱᆱ; 뀖; á„ᅱᆱ; ) HANGUL SYLLABLE GGWILM
+B017;B017;1101 1171 11B2;B017;1101 1171 11B2; # (뀗; 뀗; á„ᅱᆲ; 뀗; á„ᅱᆲ; ) HANGUL SYLLABLE GGWILB
+B018;B018;1101 1171 11B3;B018;1101 1171 11B3; # (뀘; 뀘; á„ᅱᆳ; 뀘; á„ᅱᆳ; ) HANGUL SYLLABLE GGWILS
+B019;B019;1101 1171 11B4;B019;1101 1171 11B4; # (뀙; 뀙; á„ᅱᆴ; 뀙; á„ᅱᆴ; ) HANGUL SYLLABLE GGWILT
+B01A;B01A;1101 1171 11B5;B01A;1101 1171 11B5; # (뀚; 뀚; á„ᅱᆵ; 뀚; á„ᅱᆵ; ) HANGUL SYLLABLE GGWILP
+B01B;B01B;1101 1171 11B6;B01B;1101 1171 11B6; # (뀛; 뀛; á„ᅱᆶ; 뀛; á„ᅱᆶ; ) HANGUL SYLLABLE GGWILH
+B01C;B01C;1101 1171 11B7;B01C;1101 1171 11B7; # (뀜; 뀜; á„ᅱᆷ; 뀜; á„ᅱᆷ; ) HANGUL SYLLABLE GGWIM
+B01D;B01D;1101 1171 11B8;B01D;1101 1171 11B8; # (ë€; ë€; á„ᅱᆸ; ë€; á„ᅱᆸ; ) HANGUL SYLLABLE GGWIB
+B01E;B01E;1101 1171 11B9;B01E;1101 1171 11B9; # (뀞; 뀞; á„ᅱᆹ; 뀞; á„ᅱᆹ; ) HANGUL SYLLABLE GGWIBS
+B01F;B01F;1101 1171 11BA;B01F;1101 1171 11BA; # (뀟; 뀟; á„ᅱᆺ; 뀟; á„ᅱᆺ; ) HANGUL SYLLABLE GGWIS
+B020;B020;1101 1171 11BB;B020;1101 1171 11BB; # (뀠; 뀠; á„ᅱᆻ; 뀠; á„ᅱᆻ; ) HANGUL SYLLABLE GGWISS
+B021;B021;1101 1171 11BC;B021;1101 1171 11BC; # (뀡; 뀡; á„ᅱᆼ; 뀡; á„ᅱᆼ; ) HANGUL SYLLABLE GGWING
+B022;B022;1101 1171 11BD;B022;1101 1171 11BD; # (뀢; 뀢; á„ᅱᆽ; 뀢; á„ᅱᆽ; ) HANGUL SYLLABLE GGWIJ
+B023;B023;1101 1171 11BE;B023;1101 1171 11BE; # (뀣; 뀣; á„ᅱᆾ; 뀣; á„ᅱᆾ; ) HANGUL SYLLABLE GGWIC
+B024;B024;1101 1171 11BF;B024;1101 1171 11BF; # (뀤; 뀤; á„ᅱᆿ; 뀤; á„ᅱᆿ; ) HANGUL SYLLABLE GGWIK
+B025;B025;1101 1171 11C0;B025;1101 1171 11C0; # (뀥; 뀥; á„ᅱᇀ; 뀥; á„ᅱᇀ; ) HANGUL SYLLABLE GGWIT
+B026;B026;1101 1171 11C1;B026;1101 1171 11C1; # (뀦; 뀦; á„á…±á‡; 뀦; á„á…±á‡; ) HANGUL SYLLABLE GGWIP
+B027;B027;1101 1171 11C2;B027;1101 1171 11C2; # (뀧; 뀧; á„ᅱᇂ; 뀧; á„ᅱᇂ; ) HANGUL SYLLABLE GGWIH
+B028;B028;1101 1172;B028;1101 1172; # (뀨; 뀨; á„á…²; 뀨; á„á…²; ) HANGUL SYLLABLE GGYU
+B029;B029;1101 1172 11A8;B029;1101 1172 11A8; # (뀩; 뀩; á„ᅲᆨ; 뀩; á„ᅲᆨ; ) HANGUL SYLLABLE GGYUG
+B02A;B02A;1101 1172 11A9;B02A;1101 1172 11A9; # (뀪; 뀪; á„ᅲᆩ; 뀪; á„ᅲᆩ; ) HANGUL SYLLABLE GGYUGG
+B02B;B02B;1101 1172 11AA;B02B;1101 1172 11AA; # (뀫; 뀫; á„ᅲᆪ; 뀫; á„ᅲᆪ; ) HANGUL SYLLABLE GGYUGS
+B02C;B02C;1101 1172 11AB;B02C;1101 1172 11AB; # (뀬; 뀬; á„ᅲᆫ; 뀬; á„ᅲᆫ; ) HANGUL SYLLABLE GGYUN
+B02D;B02D;1101 1172 11AC;B02D;1101 1172 11AC; # (뀭; 뀭; á„ᅲᆬ; 뀭; á„ᅲᆬ; ) HANGUL SYLLABLE GGYUNJ
+B02E;B02E;1101 1172 11AD;B02E;1101 1172 11AD; # (뀮; 뀮; á„ᅲᆭ; 뀮; á„ᅲᆭ; ) HANGUL SYLLABLE GGYUNH
+B02F;B02F;1101 1172 11AE;B02F;1101 1172 11AE; # (뀯; 뀯; á„ᅲᆮ; 뀯; á„ᅲᆮ; ) HANGUL SYLLABLE GGYUD
+B030;B030;1101 1172 11AF;B030;1101 1172 11AF; # (뀰; 뀰; á„ᅲᆯ; 뀰; á„ᅲᆯ; ) HANGUL SYLLABLE GGYUL
+B031;B031;1101 1172 11B0;B031;1101 1172 11B0; # (뀱; 뀱; á„ᅲᆰ; 뀱; á„ᅲᆰ; ) HANGUL SYLLABLE GGYULG
+B032;B032;1101 1172 11B1;B032;1101 1172 11B1; # (뀲; 뀲; á„ᅲᆱ; 뀲; á„ᅲᆱ; ) HANGUL SYLLABLE GGYULM
+B033;B033;1101 1172 11B2;B033;1101 1172 11B2; # (뀳; 뀳; á„ᅲᆲ; 뀳; á„ᅲᆲ; ) HANGUL SYLLABLE GGYULB
+B034;B034;1101 1172 11B3;B034;1101 1172 11B3; # (뀴; 뀴; á„ᅲᆳ; 뀴; á„ᅲᆳ; ) HANGUL SYLLABLE GGYULS
+B035;B035;1101 1172 11B4;B035;1101 1172 11B4; # (뀵; 뀵; á„ᅲᆴ; 뀵; á„ᅲᆴ; ) HANGUL SYLLABLE GGYULT
+B036;B036;1101 1172 11B5;B036;1101 1172 11B5; # (뀶; 뀶; á„ᅲᆵ; 뀶; á„ᅲᆵ; ) HANGUL SYLLABLE GGYULP
+B037;B037;1101 1172 11B6;B037;1101 1172 11B6; # (뀷; 뀷; á„ᅲᆶ; 뀷; á„ᅲᆶ; ) HANGUL SYLLABLE GGYULH
+B038;B038;1101 1172 11B7;B038;1101 1172 11B7; # (뀸; 뀸; á„ᅲᆷ; 뀸; á„ᅲᆷ; ) HANGUL SYLLABLE GGYUM
+B039;B039;1101 1172 11B8;B039;1101 1172 11B8; # (뀹; 뀹; á„ᅲᆸ; 뀹; á„ᅲᆸ; ) HANGUL SYLLABLE GGYUB
+B03A;B03A;1101 1172 11B9;B03A;1101 1172 11B9; # (뀺; 뀺; á„ᅲᆹ; 뀺; á„ᅲᆹ; ) HANGUL SYLLABLE GGYUBS
+B03B;B03B;1101 1172 11BA;B03B;1101 1172 11BA; # (뀻; 뀻; á„ᅲᆺ; 뀻; á„ᅲᆺ; ) HANGUL SYLLABLE GGYUS
+B03C;B03C;1101 1172 11BB;B03C;1101 1172 11BB; # (뀼; 뀼; á„ᅲᆻ; 뀼; á„ᅲᆻ; ) HANGUL SYLLABLE GGYUSS
+B03D;B03D;1101 1172 11BC;B03D;1101 1172 11BC; # (뀽; 뀽; á„ᅲᆼ; 뀽; á„ᅲᆼ; ) HANGUL SYLLABLE GGYUNG
+B03E;B03E;1101 1172 11BD;B03E;1101 1172 11BD; # (뀾; 뀾; á„ᅲᆽ; 뀾; á„ᅲᆽ; ) HANGUL SYLLABLE GGYUJ
+B03F;B03F;1101 1172 11BE;B03F;1101 1172 11BE; # (뀿; 뀿; á„ᅲᆾ; 뀿; á„ᅲᆾ; ) HANGUL SYLLABLE GGYUC
+B040;B040;1101 1172 11BF;B040;1101 1172 11BF; # (ë€; ë€; á„ᅲᆿ; ë€; á„ᅲᆿ; ) HANGUL SYLLABLE GGYUK
+B041;B041;1101 1172 11C0;B041;1101 1172 11C0; # (ë; ë; á„ᅲᇀ; ë; á„ᅲᇀ; ) HANGUL SYLLABLE GGYUT
+B042;B042;1101 1172 11C1;B042;1101 1172 11C1; # (ë‚; ë‚; á„á…²á‡; ë‚; á„á…²á‡; ) HANGUL SYLLABLE GGYUP
+B043;B043;1101 1172 11C2;B043;1101 1172 11C2; # (ëƒ; ëƒ; á„ᅲᇂ; ëƒ; á„ᅲᇂ; ) HANGUL SYLLABLE GGYUH
+B044;B044;1101 1173;B044;1101 1173; # (ë„; ë„; á„á…³; ë„; á„á…³; ) HANGUL SYLLABLE GGEU
+B045;B045;1101 1173 11A8;B045;1101 1173 11A8; # (ë…; ë…; á„ᅳᆨ; ë…; á„ᅳᆨ; ) HANGUL SYLLABLE GGEUG
+B046;B046;1101 1173 11A9;B046;1101 1173 11A9; # (ë†; ë†; á„ᅳᆩ; ë†; á„ᅳᆩ; ) HANGUL SYLLABLE GGEUGG
+B047;B047;1101 1173 11AA;B047;1101 1173 11AA; # (ë‡; ë‡; á„ᅳᆪ; ë‡; á„ᅳᆪ; ) HANGUL SYLLABLE GGEUGS
+B048;B048;1101 1173 11AB;B048;1101 1173 11AB; # (ëˆ; ëˆ; á„ᅳᆫ; ëˆ; á„ᅳᆫ; ) HANGUL SYLLABLE GGEUN
+B049;B049;1101 1173 11AC;B049;1101 1173 11AC; # (ë‰; ë‰; á„ᅳᆬ; ë‰; á„ᅳᆬ; ) HANGUL SYLLABLE GGEUNJ
+B04A;B04A;1101 1173 11AD;B04A;1101 1173 11AD; # (ëŠ; ëŠ; á„ᅳᆭ; ëŠ; á„ᅳᆭ; ) HANGUL SYLLABLE GGEUNH
+B04B;B04B;1101 1173 11AE;B04B;1101 1173 11AE; # (ë‹; ë‹; á„ᅳᆮ; ë‹; á„ᅳᆮ; ) HANGUL SYLLABLE GGEUD
+B04C;B04C;1101 1173 11AF;B04C;1101 1173 11AF; # (ëŒ; ëŒ; á„ᅳᆯ; ëŒ; á„ᅳᆯ; ) HANGUL SYLLABLE GGEUL
+B04D;B04D;1101 1173 11B0;B04D;1101 1173 11B0; # (ë; ë; á„ᅳᆰ; ë; á„ᅳᆰ; ) HANGUL SYLLABLE GGEULG
+B04E;B04E;1101 1173 11B1;B04E;1101 1173 11B1; # (ëŽ; ëŽ; á„ᅳᆱ; ëŽ; á„ᅳᆱ; ) HANGUL SYLLABLE GGEULM
+B04F;B04F;1101 1173 11B2;B04F;1101 1173 11B2; # (ë; ë; á„ᅳᆲ; ë; á„ᅳᆲ; ) HANGUL SYLLABLE GGEULB
+B050;B050;1101 1173 11B3;B050;1101 1173 11B3; # (ë; ë; á„ᅳᆳ; ë; á„ᅳᆳ; ) HANGUL SYLLABLE GGEULS
+B051;B051;1101 1173 11B4;B051;1101 1173 11B4; # (ë‘; ë‘; á„ᅳᆴ; ë‘; á„ᅳᆴ; ) HANGUL SYLLABLE GGEULT
+B052;B052;1101 1173 11B5;B052;1101 1173 11B5; # (ë’; ë’; á„ᅳᆵ; ë’; á„ᅳᆵ; ) HANGUL SYLLABLE GGEULP
+B053;B053;1101 1173 11B6;B053;1101 1173 11B6; # (ë“; ë“; á„ᅳᆶ; ë“; á„ᅳᆶ; ) HANGUL SYLLABLE GGEULH
+B054;B054;1101 1173 11B7;B054;1101 1173 11B7; # (ë”; ë”; á„ᅳᆷ; ë”; á„ᅳᆷ; ) HANGUL SYLLABLE GGEUM
+B055;B055;1101 1173 11B8;B055;1101 1173 11B8; # (ë•; ë•; á„ᅳᆸ; ë•; á„ᅳᆸ; ) HANGUL SYLLABLE GGEUB
+B056;B056;1101 1173 11B9;B056;1101 1173 11B9; # (ë–; ë–; á„ᅳᆹ; ë–; á„ᅳᆹ; ) HANGUL SYLLABLE GGEUBS
+B057;B057;1101 1173 11BA;B057;1101 1173 11BA; # (ë—; ë—; á„ᅳᆺ; ë—; á„ᅳᆺ; ) HANGUL SYLLABLE GGEUS
+B058;B058;1101 1173 11BB;B058;1101 1173 11BB; # (ë˜; ë˜; á„ᅳᆻ; ë˜; á„ᅳᆻ; ) HANGUL SYLLABLE GGEUSS
+B059;B059;1101 1173 11BC;B059;1101 1173 11BC; # (ë™; ë™; á„ᅳᆼ; ë™; á„ᅳᆼ; ) HANGUL SYLLABLE GGEUNG
+B05A;B05A;1101 1173 11BD;B05A;1101 1173 11BD; # (ëš; ëš; á„ᅳᆽ; ëš; á„ᅳᆽ; ) HANGUL SYLLABLE GGEUJ
+B05B;B05B;1101 1173 11BE;B05B;1101 1173 11BE; # (ë›; ë›; á„ᅳᆾ; ë›; á„ᅳᆾ; ) HANGUL SYLLABLE GGEUC
+B05C;B05C;1101 1173 11BF;B05C;1101 1173 11BF; # (ëœ; ëœ; á„ᅳᆿ; ëœ; á„ᅳᆿ; ) HANGUL SYLLABLE GGEUK
+B05D;B05D;1101 1173 11C0;B05D;1101 1173 11C0; # (ë; ë; á„ᅳᇀ; ë; á„ᅳᇀ; ) HANGUL SYLLABLE GGEUT
+B05E;B05E;1101 1173 11C1;B05E;1101 1173 11C1; # (ëž; ëž; á„á…³á‡; ëž; á„á…³á‡; ) HANGUL SYLLABLE GGEUP
+B05F;B05F;1101 1173 11C2;B05F;1101 1173 11C2; # (ëŸ; ëŸ; á„ᅳᇂ; ëŸ; á„ᅳᇂ; ) HANGUL SYLLABLE GGEUH
+B060;B060;1101 1174;B060;1101 1174; # (ë ; ë ; á„á…´; ë ; á„á…´; ) HANGUL SYLLABLE GGYI
+B061;B061;1101 1174 11A8;B061;1101 1174 11A8; # (ë¡; ë¡; á„ᅴᆨ; ë¡; á„ᅴᆨ; ) HANGUL SYLLABLE GGYIG
+B062;B062;1101 1174 11A9;B062;1101 1174 11A9; # (ë¢; ë¢; á„ᅴᆩ; ë¢; á„ᅴᆩ; ) HANGUL SYLLABLE GGYIGG
+B063;B063;1101 1174 11AA;B063;1101 1174 11AA; # (ë£; ë£; á„ᅴᆪ; ë£; á„ᅴᆪ; ) HANGUL SYLLABLE GGYIGS
+B064;B064;1101 1174 11AB;B064;1101 1174 11AB; # (ë¤; ë¤; á„ᅴᆫ; ë¤; á„ᅴᆫ; ) HANGUL SYLLABLE GGYIN
+B065;B065;1101 1174 11AC;B065;1101 1174 11AC; # (ë¥; ë¥; á„ᅴᆬ; ë¥; á„ᅴᆬ; ) HANGUL SYLLABLE GGYINJ
+B066;B066;1101 1174 11AD;B066;1101 1174 11AD; # (ë¦; ë¦; á„ᅴᆭ; ë¦; á„ᅴᆭ; ) HANGUL SYLLABLE GGYINH
+B067;B067;1101 1174 11AE;B067;1101 1174 11AE; # (ë§; ë§; á„ᅴᆮ; ë§; á„ᅴᆮ; ) HANGUL SYLLABLE GGYID
+B068;B068;1101 1174 11AF;B068;1101 1174 11AF; # (ë¨; ë¨; á„ᅴᆯ; ë¨; á„ᅴᆯ; ) HANGUL SYLLABLE GGYIL
+B069;B069;1101 1174 11B0;B069;1101 1174 11B0; # (ë©; ë©; á„ᅴᆰ; ë©; á„ᅴᆰ; ) HANGUL SYLLABLE GGYILG
+B06A;B06A;1101 1174 11B1;B06A;1101 1174 11B1; # (ëª; ëª; á„ᅴᆱ; ëª; á„ᅴᆱ; ) HANGUL SYLLABLE GGYILM
+B06B;B06B;1101 1174 11B2;B06B;1101 1174 11B2; # (ë«; ë«; á„ᅴᆲ; ë«; á„ᅴᆲ; ) HANGUL SYLLABLE GGYILB
+B06C;B06C;1101 1174 11B3;B06C;1101 1174 11B3; # (ë¬; ë¬; á„ᅴᆳ; ë¬; á„ᅴᆳ; ) HANGUL SYLLABLE GGYILS
+B06D;B06D;1101 1174 11B4;B06D;1101 1174 11B4; # (ë­; ë­; á„ᅴᆴ; ë­; á„ᅴᆴ; ) HANGUL SYLLABLE GGYILT
+B06E;B06E;1101 1174 11B5;B06E;1101 1174 11B5; # (ë®; ë®; á„ᅴᆵ; ë®; á„ᅴᆵ; ) HANGUL SYLLABLE GGYILP
+B06F;B06F;1101 1174 11B6;B06F;1101 1174 11B6; # (ë¯; ë¯; á„ᅴᆶ; ë¯; á„ᅴᆶ; ) HANGUL SYLLABLE GGYILH
+B070;B070;1101 1174 11B7;B070;1101 1174 11B7; # (ë°; ë°; á„ᅴᆷ; ë°; á„ᅴᆷ; ) HANGUL SYLLABLE GGYIM
+B071;B071;1101 1174 11B8;B071;1101 1174 11B8; # (ë±; ë±; á„ᅴᆸ; ë±; á„ᅴᆸ; ) HANGUL SYLLABLE GGYIB
+B072;B072;1101 1174 11B9;B072;1101 1174 11B9; # (ë²; ë²; á„ᅴᆹ; ë²; á„ᅴᆹ; ) HANGUL SYLLABLE GGYIBS
+B073;B073;1101 1174 11BA;B073;1101 1174 11BA; # (ë³; ë³; á„ᅴᆺ; ë³; á„ᅴᆺ; ) HANGUL SYLLABLE GGYIS
+B074;B074;1101 1174 11BB;B074;1101 1174 11BB; # (ë´; ë´; á„ᅴᆻ; ë´; á„ᅴᆻ; ) HANGUL SYLLABLE GGYISS
+B075;B075;1101 1174 11BC;B075;1101 1174 11BC; # (ëµ; ëµ; á„ᅴᆼ; ëµ; á„ᅴᆼ; ) HANGUL SYLLABLE GGYING
+B076;B076;1101 1174 11BD;B076;1101 1174 11BD; # (ë¶; ë¶; á„ᅴᆽ; ë¶; á„ᅴᆽ; ) HANGUL SYLLABLE GGYIJ
+B077;B077;1101 1174 11BE;B077;1101 1174 11BE; # (ë·; ë·; á„ᅴᆾ; ë·; á„ᅴᆾ; ) HANGUL SYLLABLE GGYIC
+B078;B078;1101 1174 11BF;B078;1101 1174 11BF; # (ë¸; ë¸; á„ᅴᆿ; ë¸; á„ᅴᆿ; ) HANGUL SYLLABLE GGYIK
+B079;B079;1101 1174 11C0;B079;1101 1174 11C0; # (ë¹; ë¹; á„ᅴᇀ; ë¹; á„ᅴᇀ; ) HANGUL SYLLABLE GGYIT
+B07A;B07A;1101 1174 11C1;B07A;1101 1174 11C1; # (ëº; ëº; á„á…´á‡; ëº; á„á…´á‡; ) HANGUL SYLLABLE GGYIP
+B07B;B07B;1101 1174 11C2;B07B;1101 1174 11C2; # (ë»; ë»; á„ᅴᇂ; ë»; á„ᅴᇂ; ) HANGUL SYLLABLE GGYIH
+B07C;B07C;1101 1175;B07C;1101 1175; # (ë¼; ë¼; á„á…µ; ë¼; á„á…µ; ) HANGUL SYLLABLE GGI
+B07D;B07D;1101 1175 11A8;B07D;1101 1175 11A8; # (ë½; ë½; á„ᅵᆨ; ë½; á„ᅵᆨ; ) HANGUL SYLLABLE GGIG
+B07E;B07E;1101 1175 11A9;B07E;1101 1175 11A9; # (ë¾; ë¾; á„ᅵᆩ; ë¾; á„ᅵᆩ; ) HANGUL SYLLABLE GGIGG
+B07F;B07F;1101 1175 11AA;B07F;1101 1175 11AA; # (ë¿; ë¿; á„ᅵᆪ; ë¿; á„ᅵᆪ; ) HANGUL SYLLABLE GGIGS
+B080;B080;1101 1175 11AB;B080;1101 1175 11AB; # (ë‚€; ë‚€; á„ᅵᆫ; ë‚€; á„ᅵᆫ; ) HANGUL SYLLABLE GGIN
+B081;B081;1101 1175 11AC;B081;1101 1175 11AC; # (ë‚; ë‚; á„ᅵᆬ; ë‚; á„ᅵᆬ; ) HANGUL SYLLABLE GGINJ
+B082;B082;1101 1175 11AD;B082;1101 1175 11AD; # (ë‚‚; ë‚‚; á„ᅵᆭ; ë‚‚; á„ᅵᆭ; ) HANGUL SYLLABLE GGINH
+B083;B083;1101 1175 11AE;B083;1101 1175 11AE; # (낃; 낃; á„ᅵᆮ; 낃; á„ᅵᆮ; ) HANGUL SYLLABLE GGID
+B084;B084;1101 1175 11AF;B084;1101 1175 11AF; # (ë‚„; ë‚„; á„ᅵᆯ; ë‚„; á„ᅵᆯ; ) HANGUL SYLLABLE GGIL
+B085;B085;1101 1175 11B0;B085;1101 1175 11B0; # (ë‚…; ë‚…; á„ᅵᆰ; ë‚…; á„ᅵᆰ; ) HANGUL SYLLABLE GGILG
+B086;B086;1101 1175 11B1;B086;1101 1175 11B1; # (낆; 낆; á„ᅵᆱ; 낆; á„ᅵᆱ; ) HANGUL SYLLABLE GGILM
+B087;B087;1101 1175 11B2;B087;1101 1175 11B2; # (낇; 낇; á„ᅵᆲ; 낇; á„ᅵᆲ; ) HANGUL SYLLABLE GGILB
+B088;B088;1101 1175 11B3;B088;1101 1175 11B3; # (낈; 낈; á„ᅵᆳ; 낈; á„ᅵᆳ; ) HANGUL SYLLABLE GGILS
+B089;B089;1101 1175 11B4;B089;1101 1175 11B4; # (낉; 낉; á„ᅵᆴ; 낉; á„ᅵᆴ; ) HANGUL SYLLABLE GGILT
+B08A;B08A;1101 1175 11B5;B08A;1101 1175 11B5; # (낊; 낊; á„ᅵᆵ; 낊; á„ᅵᆵ; ) HANGUL SYLLABLE GGILP
+B08B;B08B;1101 1175 11B6;B08B;1101 1175 11B6; # (ë‚‹; ë‚‹; á„ᅵᆶ; ë‚‹; á„ᅵᆶ; ) HANGUL SYLLABLE GGILH
+B08C;B08C;1101 1175 11B7;B08C;1101 1175 11B7; # (낌; 낌; á„ᅵᆷ; 낌; á„ᅵᆷ; ) HANGUL SYLLABLE GGIM
+B08D;B08D;1101 1175 11B8;B08D;1101 1175 11B8; # (ë‚; ë‚; á„ᅵᆸ; ë‚; á„ᅵᆸ; ) HANGUL SYLLABLE GGIB
+B08E;B08E;1101 1175 11B9;B08E;1101 1175 11B9; # (낎; 낎; á„ᅵᆹ; 낎; á„ᅵᆹ; ) HANGUL SYLLABLE GGIBS
+B08F;B08F;1101 1175 11BA;B08F;1101 1175 11BA; # (ë‚; ë‚; á„ᅵᆺ; ë‚; á„ᅵᆺ; ) HANGUL SYLLABLE GGIS
+B090;B090;1101 1175 11BB;B090;1101 1175 11BB; # (ë‚; ë‚; á„ᅵᆻ; ë‚; á„ᅵᆻ; ) HANGUL SYLLABLE GGISS
+B091;B091;1101 1175 11BC;B091;1101 1175 11BC; # (ë‚‘; ë‚‘; á„ᅵᆼ; ë‚‘; á„ᅵᆼ; ) HANGUL SYLLABLE GGING
+B092;B092;1101 1175 11BD;B092;1101 1175 11BD; # (ë‚’; ë‚’; á„ᅵᆽ; ë‚’; á„ᅵᆽ; ) HANGUL SYLLABLE GGIJ
+B093;B093;1101 1175 11BE;B093;1101 1175 11BE; # (ë‚“; ë‚“; á„ᅵᆾ; ë‚“; á„ᅵᆾ; ) HANGUL SYLLABLE GGIC
+B094;B094;1101 1175 11BF;B094;1101 1175 11BF; # (ë‚”; ë‚”; á„ᅵᆿ; ë‚”; á„ᅵᆿ; ) HANGUL SYLLABLE GGIK
+B095;B095;1101 1175 11C0;B095;1101 1175 11C0; # (ë‚•; ë‚•; á„ᅵᇀ; ë‚•; á„ᅵᇀ; ) HANGUL SYLLABLE GGIT
+B096;B096;1101 1175 11C1;B096;1101 1175 11C1; # (ë‚–; ë‚–; á„á…µá‡; ë‚–; á„á…µá‡; ) HANGUL SYLLABLE GGIP
+B097;B097;1101 1175 11C2;B097;1101 1175 11C2; # (ë‚—; ë‚—; á„ᅵᇂ; ë‚—; á„ᅵᇂ; ) HANGUL SYLLABLE GGIH
+B098;B098;1102 1161;B098;1102 1161; # (나; 나; 나; 나; 나; ) HANGUL SYLLABLE NA
+B099;B099;1102 1161 11A8;B099;1102 1161 11A8; # (낙; 낙; 낙; 낙; 낙; ) HANGUL SYLLABLE NAG
+B09A;B09A;1102 1161 11A9;B09A;1102 1161 11A9; # (낚; 낚; 낚; 낚; 낚; ) HANGUL SYLLABLE NAGG
+B09B;B09B;1102 1161 11AA;B09B;1102 1161 11AA; # (낛; 낛; 낛; 낛; 낛; ) HANGUL SYLLABLE NAGS
+B09C;B09C;1102 1161 11AB;B09C;1102 1161 11AB; # (난; 난; 난; 난; 난; ) HANGUL SYLLABLE NAN
+B09D;B09D;1102 1161 11AC;B09D;1102 1161 11AC; # (ë‚; ë‚; 낝; ë‚; 낝; ) HANGUL SYLLABLE NANJ
+B09E;B09E;1102 1161 11AD;B09E;1102 1161 11AD; # (낞; 낞; 낞; 낞; 낞; ) HANGUL SYLLABLE NANH
+B09F;B09F;1102 1161 11AE;B09F;1102 1161 11AE; # (낟; 낟; 낟; 낟; 낟; ) HANGUL SYLLABLE NAD
+B0A0;B0A0;1102 1161 11AF;B0A0;1102 1161 11AF; # (날; 날; 날; 날; 날; ) HANGUL SYLLABLE NAL
+B0A1;B0A1;1102 1161 11B0;B0A1;1102 1161 11B0; # (낡; 낡; 낡; 낡; 낡; ) HANGUL SYLLABLE NALG
+B0A2;B0A2;1102 1161 11B1;B0A2;1102 1161 11B1; # (낢; 낢; 낢; 낢; 낢; ) HANGUL SYLLABLE NALM
+B0A3;B0A3;1102 1161 11B2;B0A3;1102 1161 11B2; # (낣; 낣; 낣; 낣; 낣; ) HANGUL SYLLABLE NALB
+B0A4;B0A4;1102 1161 11B3;B0A4;1102 1161 11B3; # (낤; 낤; 낤; 낤; 낤; ) HANGUL SYLLABLE NALS
+B0A5;B0A5;1102 1161 11B4;B0A5;1102 1161 11B4; # (낥; 낥; 낥; 낥; 낥; ) HANGUL SYLLABLE NALT
+B0A6;B0A6;1102 1161 11B5;B0A6;1102 1161 11B5; # (낦; 낦; 낦; 낦; 낦; ) HANGUL SYLLABLE NALP
+B0A7;B0A7;1102 1161 11B6;B0A7;1102 1161 11B6; # (낧; 낧; 낧; 낧; 낧; ) HANGUL SYLLABLE NALH
+B0A8;B0A8;1102 1161 11B7;B0A8;1102 1161 11B7; # (남; 남; 남; 남; 남; ) HANGUL SYLLABLE NAM
+B0A9;B0A9;1102 1161 11B8;B0A9;1102 1161 11B8; # (납; 납; 납; 납; 납; ) HANGUL SYLLABLE NAB
+B0AA;B0AA;1102 1161 11B9;B0AA;1102 1161 11B9; # (낪; 낪; 낪; 낪; 낪; ) HANGUL SYLLABLE NABS
+B0AB;B0AB;1102 1161 11BA;B0AB;1102 1161 11BA; # (낫; 낫; 낫; 낫; 낫; ) HANGUL SYLLABLE NAS
+B0AC;B0AC;1102 1161 11BB;B0AC;1102 1161 11BB; # (났; 났; 났; 났; 났; ) HANGUL SYLLABLE NASS
+B0AD;B0AD;1102 1161 11BC;B0AD;1102 1161 11BC; # (낭; 낭; 낭; 낭; 낭; ) HANGUL SYLLABLE NANG
+B0AE;B0AE;1102 1161 11BD;B0AE;1102 1161 11BD; # (낮; 낮; 낮; 낮; 낮; ) HANGUL SYLLABLE NAJ
+B0AF;B0AF;1102 1161 11BE;B0AF;1102 1161 11BE; # (낯; 낯; 낯; 낯; 낯; ) HANGUL SYLLABLE NAC
+B0B0;B0B0;1102 1161 11BF;B0B0;1102 1161 11BF; # (낰; 낰; 낰; 낰; 낰; ) HANGUL SYLLABLE NAK
+B0B1;B0B1;1102 1161 11C0;B0B1;1102 1161 11C0; # (낱; 낱; 낱; 낱; 낱; ) HANGUL SYLLABLE NAT
+B0B2;B0B2;1102 1161 11C1;B0B2;1102 1161 11C1; # (낲; 낲; á„‚á…¡á‡; 낲; á„‚á…¡á‡; ) HANGUL SYLLABLE NAP
+B0B3;B0B3;1102 1161 11C2;B0B3;1102 1161 11C2; # (낳; 낳; 낳; 낳; 낳; ) HANGUL SYLLABLE NAH
+B0B4;B0B4;1102 1162;B0B4;1102 1162; # (ë‚´; ë‚´; á„‚á…¢; ë‚´; á„‚á…¢; ) HANGUL SYLLABLE NAE
+B0B5;B0B5;1102 1162 11A8;B0B5;1102 1162 11A8; # (낵; 낵; 낵; 낵; 낵; ) HANGUL SYLLABLE NAEG
+B0B6;B0B6;1102 1162 11A9;B0B6;1102 1162 11A9; # (낶; 낶; 낶; 낶; 낶; ) HANGUL SYLLABLE NAEGG
+B0B7;B0B7;1102 1162 11AA;B0B7;1102 1162 11AA; # (낷; 낷; 낷; 낷; 낷; ) HANGUL SYLLABLE NAEGS
+B0B8;B0B8;1102 1162 11AB;B0B8;1102 1162 11AB; # (낸; 낸; 낸; 낸; 낸; ) HANGUL SYLLABLE NAEN
+B0B9;B0B9;1102 1162 11AC;B0B9;1102 1162 11AC; # (낹; 낹; 낹; 낹; 낹; ) HANGUL SYLLABLE NAENJ
+B0BA;B0BA;1102 1162 11AD;B0BA;1102 1162 11AD; # (낺; 낺; 낺; 낺; 낺; ) HANGUL SYLLABLE NAENH
+B0BB;B0BB;1102 1162 11AE;B0BB;1102 1162 11AE; # (낻; 낻; 낻; 낻; 낻; ) HANGUL SYLLABLE NAED
+B0BC;B0BC;1102 1162 11AF;B0BC;1102 1162 11AF; # (낼; 낼; 낼; 낼; 낼; ) HANGUL SYLLABLE NAEL
+B0BD;B0BD;1102 1162 11B0;B0BD;1102 1162 11B0; # (낽; 낽; 낽; 낽; 낽; ) HANGUL SYLLABLE NAELG
+B0BE;B0BE;1102 1162 11B1;B0BE;1102 1162 11B1; # (낾; 낾; 낾; 낾; 낾; ) HANGUL SYLLABLE NAELM
+B0BF;B0BF;1102 1162 11B2;B0BF;1102 1162 11B2; # (낿; 낿; 낿; 낿; 낿; ) HANGUL SYLLABLE NAELB
+B0C0;B0C0;1102 1162 11B3;B0C0;1102 1162 11B3; # (냀; 냀; 냀; 냀; 냀; ) HANGUL SYLLABLE NAELS
+B0C1;B0C1;1102 1162 11B4;B0C1;1102 1162 11B4; # (ëƒ; ëƒ; 냁; ëƒ; 냁; ) HANGUL SYLLABLE NAELT
+B0C2;B0C2;1102 1162 11B5;B0C2;1102 1162 11B5; # (냂; 냂; 냂; 냂; 냂; ) HANGUL SYLLABLE NAELP
+B0C3;B0C3;1102 1162 11B6;B0C3;1102 1162 11B6; # (냃; 냃; 냃; 냃; 냃; ) HANGUL SYLLABLE NAELH
+B0C4;B0C4;1102 1162 11B7;B0C4;1102 1162 11B7; # (냄; 냄; 냄; 냄; 냄; ) HANGUL SYLLABLE NAEM
+B0C5;B0C5;1102 1162 11B8;B0C5;1102 1162 11B8; # (냅; 냅; 냅; 냅; 냅; ) HANGUL SYLLABLE NAEB
+B0C6;B0C6;1102 1162 11B9;B0C6;1102 1162 11B9; # (냆; 냆; 냆; 냆; 냆; ) HANGUL SYLLABLE NAEBS
+B0C7;B0C7;1102 1162 11BA;B0C7;1102 1162 11BA; # (냇; 냇; 냇; 냇; 냇; ) HANGUL SYLLABLE NAES
+B0C8;B0C8;1102 1162 11BB;B0C8;1102 1162 11BB; # (냈; 냈; 냈; 냈; 냈; ) HANGUL SYLLABLE NAESS
+B0C9;B0C9;1102 1162 11BC;B0C9;1102 1162 11BC; # (냉; 냉; 냉; 냉; 냉; ) HANGUL SYLLABLE NAENG
+B0CA;B0CA;1102 1162 11BD;B0CA;1102 1162 11BD; # (냊; 냊; 냊; 냊; 냊; ) HANGUL SYLLABLE NAEJ
+B0CB;B0CB;1102 1162 11BE;B0CB;1102 1162 11BE; # (냋; 냋; 냋; 냋; 냋; ) HANGUL SYLLABLE NAEC
+B0CC;B0CC;1102 1162 11BF;B0CC;1102 1162 11BF; # (냌; 냌; 냌; 냌; 냌; ) HANGUL SYLLABLE NAEK
+B0CD;B0CD;1102 1162 11C0;B0CD;1102 1162 11C0; # (ëƒ; ëƒ; 냍; ëƒ; 냍; ) HANGUL SYLLABLE NAET
+B0CE;B0CE;1102 1162 11C1;B0CE;1102 1162 11C1; # (냎; 냎; á„‚á…¢á‡; 냎; á„‚á…¢á‡; ) HANGUL SYLLABLE NAEP
+B0CF;B0CF;1102 1162 11C2;B0CF;1102 1162 11C2; # (ëƒ; ëƒ; 냏; ëƒ; 냏; ) HANGUL SYLLABLE NAEH
+B0D0;B0D0;1102 1163;B0D0;1102 1163; # (ëƒ; ëƒ; á„‚á…£; ëƒ; á„‚á…£; ) HANGUL SYLLABLE NYA
+B0D1;B0D1;1102 1163 11A8;B0D1;1102 1163 11A8; # (냑; 냑; 냑; 냑; 냑; ) HANGUL SYLLABLE NYAG
+B0D2;B0D2;1102 1163 11A9;B0D2;1102 1163 11A9; # (냒; 냒; 냒; 냒; 냒; ) HANGUL SYLLABLE NYAGG
+B0D3;B0D3;1102 1163 11AA;B0D3;1102 1163 11AA; # (냓; 냓; 냓; 냓; 냓; ) HANGUL SYLLABLE NYAGS
+B0D4;B0D4;1102 1163 11AB;B0D4;1102 1163 11AB; # (냔; 냔; 냔; 냔; 냔; ) HANGUL SYLLABLE NYAN
+B0D5;B0D5;1102 1163 11AC;B0D5;1102 1163 11AC; # (냕; 냕; 냕; 냕; 냕; ) HANGUL SYLLABLE NYANJ
+B0D6;B0D6;1102 1163 11AD;B0D6;1102 1163 11AD; # (냖; 냖; 냖; 냖; 냖; ) HANGUL SYLLABLE NYANH
+B0D7;B0D7;1102 1163 11AE;B0D7;1102 1163 11AE; # (냗; 냗; 냗; 냗; 냗; ) HANGUL SYLLABLE NYAD
+B0D8;B0D8;1102 1163 11AF;B0D8;1102 1163 11AF; # (냘; 냘; 냘; 냘; 냘; ) HANGUL SYLLABLE NYAL
+B0D9;B0D9;1102 1163 11B0;B0D9;1102 1163 11B0; # (냙; 냙; 냙; 냙; 냙; ) HANGUL SYLLABLE NYALG
+B0DA;B0DA;1102 1163 11B1;B0DA;1102 1163 11B1; # (냚; 냚; 냚; 냚; 냚; ) HANGUL SYLLABLE NYALM
+B0DB;B0DB;1102 1163 11B2;B0DB;1102 1163 11B2; # (냛; 냛; 냛; 냛; 냛; ) HANGUL SYLLABLE NYALB
+B0DC;B0DC;1102 1163 11B3;B0DC;1102 1163 11B3; # (냜; 냜; 냜; 냜; 냜; ) HANGUL SYLLABLE NYALS
+B0DD;B0DD;1102 1163 11B4;B0DD;1102 1163 11B4; # (ëƒ; ëƒ; 냝; ëƒ; 냝; ) HANGUL SYLLABLE NYALT
+B0DE;B0DE;1102 1163 11B5;B0DE;1102 1163 11B5; # (냞; 냞; 냞; 냞; 냞; ) HANGUL SYLLABLE NYALP
+B0DF;B0DF;1102 1163 11B6;B0DF;1102 1163 11B6; # (냟; 냟; 냟; 냟; 냟; ) HANGUL SYLLABLE NYALH
+B0E0;B0E0;1102 1163 11B7;B0E0;1102 1163 11B7; # (냠; 냠; 냠; 냠; 냠; ) HANGUL SYLLABLE NYAM
+B0E1;B0E1;1102 1163 11B8;B0E1;1102 1163 11B8; # (냡; 냡; 냡; 냡; 냡; ) HANGUL SYLLABLE NYAB
+B0E2;B0E2;1102 1163 11B9;B0E2;1102 1163 11B9; # (냢; 냢; 냢; 냢; 냢; ) HANGUL SYLLABLE NYABS
+B0E3;B0E3;1102 1163 11BA;B0E3;1102 1163 11BA; # (냣; 냣; 냣; 냣; 냣; ) HANGUL SYLLABLE NYAS
+B0E4;B0E4;1102 1163 11BB;B0E4;1102 1163 11BB; # (냤; 냤; 냤; 냤; 냤; ) HANGUL SYLLABLE NYASS
+B0E5;B0E5;1102 1163 11BC;B0E5;1102 1163 11BC; # (냥; 냥; 냥; 냥; 냥; ) HANGUL SYLLABLE NYANG
+B0E6;B0E6;1102 1163 11BD;B0E6;1102 1163 11BD; # (냦; 냦; 냦; 냦; 냦; ) HANGUL SYLLABLE NYAJ
+B0E7;B0E7;1102 1163 11BE;B0E7;1102 1163 11BE; # (냧; 냧; 냧; 냧; 냧; ) HANGUL SYLLABLE NYAC
+B0E8;B0E8;1102 1163 11BF;B0E8;1102 1163 11BF; # (냨; 냨; 냨; 냨; 냨; ) HANGUL SYLLABLE NYAK
+B0E9;B0E9;1102 1163 11C0;B0E9;1102 1163 11C0; # (냩; 냩; 냩; 냩; 냩; ) HANGUL SYLLABLE NYAT
+B0EA;B0EA;1102 1163 11C1;B0EA;1102 1163 11C1; # (냪; 냪; á„‚á…£á‡; 냪; á„‚á…£á‡; ) HANGUL SYLLABLE NYAP
+B0EB;B0EB;1102 1163 11C2;B0EB;1102 1163 11C2; # (냫; 냫; 냫; 냫; 냫; ) HANGUL SYLLABLE NYAH
+B0EC;B0EC;1102 1164;B0EC;1102 1164; # (냬; 냬; 냬; 냬; 냬; ) HANGUL SYLLABLE NYAE
+B0ED;B0ED;1102 1164 11A8;B0ED;1102 1164 11A8; # (냭; 냭; 냭; 냭; 냭; ) HANGUL SYLLABLE NYAEG
+B0EE;B0EE;1102 1164 11A9;B0EE;1102 1164 11A9; # (냮; 냮; 냮; 냮; 냮; ) HANGUL SYLLABLE NYAEGG
+B0EF;B0EF;1102 1164 11AA;B0EF;1102 1164 11AA; # (냯; 냯; 냯; 냯; 냯; ) HANGUL SYLLABLE NYAEGS
+B0F0;B0F0;1102 1164 11AB;B0F0;1102 1164 11AB; # (냰; 냰; 냰; 냰; 냰; ) HANGUL SYLLABLE NYAEN
+B0F1;B0F1;1102 1164 11AC;B0F1;1102 1164 11AC; # (냱; 냱; 냱; 냱; 냱; ) HANGUL SYLLABLE NYAENJ
+B0F2;B0F2;1102 1164 11AD;B0F2;1102 1164 11AD; # (냲; 냲; 냲; 냲; 냲; ) HANGUL SYLLABLE NYAENH
+B0F3;B0F3;1102 1164 11AE;B0F3;1102 1164 11AE; # (냳; 냳; 냳; 냳; 냳; ) HANGUL SYLLABLE NYAED
+B0F4;B0F4;1102 1164 11AF;B0F4;1102 1164 11AF; # (냴; 냴; 냴; 냴; 냴; ) HANGUL SYLLABLE NYAEL
+B0F5;B0F5;1102 1164 11B0;B0F5;1102 1164 11B0; # (냵; 냵; 냵; 냵; 냵; ) HANGUL SYLLABLE NYAELG
+B0F6;B0F6;1102 1164 11B1;B0F6;1102 1164 11B1; # (냶; 냶; 냶; 냶; 냶; ) HANGUL SYLLABLE NYAELM
+B0F7;B0F7;1102 1164 11B2;B0F7;1102 1164 11B2; # (냷; 냷; 냷; 냷; 냷; ) HANGUL SYLLABLE NYAELB
+B0F8;B0F8;1102 1164 11B3;B0F8;1102 1164 11B3; # (냸; 냸; 냸; 냸; 냸; ) HANGUL SYLLABLE NYAELS
+B0F9;B0F9;1102 1164 11B4;B0F9;1102 1164 11B4; # (냹; 냹; 냹; 냹; 냹; ) HANGUL SYLLABLE NYAELT
+B0FA;B0FA;1102 1164 11B5;B0FA;1102 1164 11B5; # (냺; 냺; 냺; 냺; 냺; ) HANGUL SYLLABLE NYAELP
+B0FB;B0FB;1102 1164 11B6;B0FB;1102 1164 11B6; # (냻; 냻; 냻; 냻; 냻; ) HANGUL SYLLABLE NYAELH
+B0FC;B0FC;1102 1164 11B7;B0FC;1102 1164 11B7; # (냼; 냼; 냼; 냼; 냼; ) HANGUL SYLLABLE NYAEM
+B0FD;B0FD;1102 1164 11B8;B0FD;1102 1164 11B8; # (냽; 냽; 냽; 냽; 냽; ) HANGUL SYLLABLE NYAEB
+B0FE;B0FE;1102 1164 11B9;B0FE;1102 1164 11B9; # (냾; 냾; 냾; 냾; 냾; ) HANGUL SYLLABLE NYAEBS
+B0FF;B0FF;1102 1164 11BA;B0FF;1102 1164 11BA; # (냿; 냿; 냿; 냿; 냿; ) HANGUL SYLLABLE NYAES
+B100;B100;1102 1164 11BB;B100;1102 1164 11BB; # (넀; 넀; 넀; 넀; 넀; ) HANGUL SYLLABLE NYAESS
+B101;B101;1102 1164 11BC;B101;1102 1164 11BC; # (ë„; ë„; 넁; ë„; 넁; ) HANGUL SYLLABLE NYAENG
+B102;B102;1102 1164 11BD;B102;1102 1164 11BD; # (넂; 넂; 넂; 넂; 넂; ) HANGUL SYLLABLE NYAEJ
+B103;B103;1102 1164 11BE;B103;1102 1164 11BE; # (넃; 넃; 넃; 넃; 넃; ) HANGUL SYLLABLE NYAEC
+B104;B104;1102 1164 11BF;B104;1102 1164 11BF; # (넄; 넄; 넄; 넄; 넄; ) HANGUL SYLLABLE NYAEK
+B105;B105;1102 1164 11C0;B105;1102 1164 11C0; # (넅; 넅; 넅; 넅; 넅; ) HANGUL SYLLABLE NYAET
+B106;B106;1102 1164 11C1;B106;1102 1164 11C1; # (넆; 넆; á„‚á…¤á‡; 넆; á„‚á…¤á‡; ) HANGUL SYLLABLE NYAEP
+B107;B107;1102 1164 11C2;B107;1102 1164 11C2; # (넇; 넇; 넇; 넇; 넇; ) HANGUL SYLLABLE NYAEH
+B108;B108;1102 1165;B108;1102 1165; # (너; 너; 너; 너; 너; ) HANGUL SYLLABLE NEO
+B109;B109;1102 1165 11A8;B109;1102 1165 11A8; # (넉; 넉; 넉; 넉; 넉; ) HANGUL SYLLABLE NEOG
+B10A;B10A;1102 1165 11A9;B10A;1102 1165 11A9; # (넊; 넊; 넊; 넊; 넊; ) HANGUL SYLLABLE NEOGG
+B10B;B10B;1102 1165 11AA;B10B;1102 1165 11AA; # (넋; 넋; 넋; 넋; 넋; ) HANGUL SYLLABLE NEOGS
+B10C;B10C;1102 1165 11AB;B10C;1102 1165 11AB; # (넌; 넌; 넌; 넌; 넌; ) HANGUL SYLLABLE NEON
+B10D;B10D;1102 1165 11AC;B10D;1102 1165 11AC; # (ë„; ë„; 넍; ë„; 넍; ) HANGUL SYLLABLE NEONJ
+B10E;B10E;1102 1165 11AD;B10E;1102 1165 11AD; # (넎; 넎; 넎; 넎; 넎; ) HANGUL SYLLABLE NEONH
+B10F;B10F;1102 1165 11AE;B10F;1102 1165 11AE; # (ë„; ë„; 넏; ë„; 넏; ) HANGUL SYLLABLE NEOD
+B110;B110;1102 1165 11AF;B110;1102 1165 11AF; # (ë„; ë„; 널; ë„; 널; ) HANGUL SYLLABLE NEOL
+B111;B111;1102 1165 11B0;B111;1102 1165 11B0; # (넑; 넑; 넑; 넑; 넑; ) HANGUL SYLLABLE NEOLG
+B112;B112;1102 1165 11B1;B112;1102 1165 11B1; # (넒; 넒; 넒; 넒; 넒; ) HANGUL SYLLABLE NEOLM
+B113;B113;1102 1165 11B2;B113;1102 1165 11B2; # (넓; 넓; 넓; 넓; 넓; ) HANGUL SYLLABLE NEOLB
+B114;B114;1102 1165 11B3;B114;1102 1165 11B3; # (넔; 넔; 넔; 넔; 넔; ) HANGUL SYLLABLE NEOLS
+B115;B115;1102 1165 11B4;B115;1102 1165 11B4; # (넕; 넕; 넕; 넕; 넕; ) HANGUL SYLLABLE NEOLT
+B116;B116;1102 1165 11B5;B116;1102 1165 11B5; # (넖; 넖; 넖; 넖; 넖; ) HANGUL SYLLABLE NEOLP
+B117;B117;1102 1165 11B6;B117;1102 1165 11B6; # (넗; 넗; 넗; 넗; 넗; ) HANGUL SYLLABLE NEOLH
+B118;B118;1102 1165 11B7;B118;1102 1165 11B7; # (넘; 넘; 넘; 넘; 넘; ) HANGUL SYLLABLE NEOM
+B119;B119;1102 1165 11B8;B119;1102 1165 11B8; # (넙; 넙; 넙; 넙; 넙; ) HANGUL SYLLABLE NEOB
+B11A;B11A;1102 1165 11B9;B11A;1102 1165 11B9; # (넚; 넚; 넚; 넚; 넚; ) HANGUL SYLLABLE NEOBS
+B11B;B11B;1102 1165 11BA;B11B;1102 1165 11BA; # (넛; 넛; 넛; 넛; 넛; ) HANGUL SYLLABLE NEOS
+B11C;B11C;1102 1165 11BB;B11C;1102 1165 11BB; # (넜; 넜; 넜; 넜; 넜; ) HANGUL SYLLABLE NEOSS
+B11D;B11D;1102 1165 11BC;B11D;1102 1165 11BC; # (ë„; ë„; 넝; ë„; 넝; ) HANGUL SYLLABLE NEONG
+B11E;B11E;1102 1165 11BD;B11E;1102 1165 11BD; # (넞; 넞; 넞; 넞; 넞; ) HANGUL SYLLABLE NEOJ
+B11F;B11F;1102 1165 11BE;B11F;1102 1165 11BE; # (넟; 넟; 넟; 넟; 넟; ) HANGUL SYLLABLE NEOC
+B120;B120;1102 1165 11BF;B120;1102 1165 11BF; # (넠; 넠; 넠; 넠; 넠; ) HANGUL SYLLABLE NEOK
+B121;B121;1102 1165 11C0;B121;1102 1165 11C0; # (넡; 넡; 넡; 넡; 넡; ) HANGUL SYLLABLE NEOT
+B122;B122;1102 1165 11C1;B122;1102 1165 11C1; # (ë„¢; ë„¢; á„‚á…¥á‡; ë„¢; á„‚á…¥á‡; ) HANGUL SYLLABLE NEOP
+B123;B123;1102 1165 11C2;B123;1102 1165 11C2; # (넣; 넣; 넣; 넣; 넣; ) HANGUL SYLLABLE NEOH
+B124;B124;1102 1166;B124;1102 1166; # (네; 네; 네; 네; 네; ) HANGUL SYLLABLE NE
+B125;B125;1102 1166 11A8;B125;1102 1166 11A8; # (넥; 넥; 넥; 넥; 넥; ) HANGUL SYLLABLE NEG
+B126;B126;1102 1166 11A9;B126;1102 1166 11A9; # (넦; 넦; 넦; 넦; 넦; ) HANGUL SYLLABLE NEGG
+B127;B127;1102 1166 11AA;B127;1102 1166 11AA; # (넧; 넧; 넧; 넧; 넧; ) HANGUL SYLLABLE NEGS
+B128;B128;1102 1166 11AB;B128;1102 1166 11AB; # (넨; 넨; 넨; 넨; 넨; ) HANGUL SYLLABLE NEN
+B129;B129;1102 1166 11AC;B129;1102 1166 11AC; # (넩; 넩; 넩; 넩; 넩; ) HANGUL SYLLABLE NENJ
+B12A;B12A;1102 1166 11AD;B12A;1102 1166 11AD; # (넪; 넪; 넪; 넪; 넪; ) HANGUL SYLLABLE NENH
+B12B;B12B;1102 1166 11AE;B12B;1102 1166 11AE; # (넫; 넫; 넫; 넫; 넫; ) HANGUL SYLLABLE NED
+B12C;B12C;1102 1166 11AF;B12C;1102 1166 11AF; # (넬; 넬; 넬; 넬; 넬; ) HANGUL SYLLABLE NEL
+B12D;B12D;1102 1166 11B0;B12D;1102 1166 11B0; # (넭; 넭; 넭; 넭; 넭; ) HANGUL SYLLABLE NELG
+B12E;B12E;1102 1166 11B1;B12E;1102 1166 11B1; # (넮; 넮; 넮; 넮; 넮; ) HANGUL SYLLABLE NELM
+B12F;B12F;1102 1166 11B2;B12F;1102 1166 11B2; # (넯; 넯; 넯; 넯; 넯; ) HANGUL SYLLABLE NELB
+B130;B130;1102 1166 11B3;B130;1102 1166 11B3; # (넰; 넰; 넰; 넰; 넰; ) HANGUL SYLLABLE NELS
+B131;B131;1102 1166 11B4;B131;1102 1166 11B4; # (넱; 넱; 넱; 넱; 넱; ) HANGUL SYLLABLE NELT
+B132;B132;1102 1166 11B5;B132;1102 1166 11B5; # (넲; 넲; 넲; 넲; 넲; ) HANGUL SYLLABLE NELP
+B133;B133;1102 1166 11B6;B133;1102 1166 11B6; # (넳; 넳; 넳; 넳; 넳; ) HANGUL SYLLABLE NELH
+B134;B134;1102 1166 11B7;B134;1102 1166 11B7; # (넴; 넴; 넴; 넴; 넴; ) HANGUL SYLLABLE NEM
+B135;B135;1102 1166 11B8;B135;1102 1166 11B8; # (넵; 넵; 넵; 넵; 넵; ) HANGUL SYLLABLE NEB
+B136;B136;1102 1166 11B9;B136;1102 1166 11B9; # (넶; 넶; 넶; 넶; 넶; ) HANGUL SYLLABLE NEBS
+B137;B137;1102 1166 11BA;B137;1102 1166 11BA; # (넷; 넷; 넷; 넷; 넷; ) HANGUL SYLLABLE NES
+B138;B138;1102 1166 11BB;B138;1102 1166 11BB; # (넸; 넸; 넸; 넸; 넸; ) HANGUL SYLLABLE NESS
+B139;B139;1102 1166 11BC;B139;1102 1166 11BC; # (넹; 넹; 넹; 넹; 넹; ) HANGUL SYLLABLE NENG
+B13A;B13A;1102 1166 11BD;B13A;1102 1166 11BD; # (넺; 넺; 넺; 넺; 넺; ) HANGUL SYLLABLE NEJ
+B13B;B13B;1102 1166 11BE;B13B;1102 1166 11BE; # (넻; 넻; 넻; 넻; 넻; ) HANGUL SYLLABLE NEC
+B13C;B13C;1102 1166 11BF;B13C;1102 1166 11BF; # (넼; 넼; 넼; 넼; 넼; ) HANGUL SYLLABLE NEK
+B13D;B13D;1102 1166 11C0;B13D;1102 1166 11C0; # (넽; 넽; 넽; 넽; 넽; ) HANGUL SYLLABLE NET
+B13E;B13E;1102 1166 11C1;B13E;1102 1166 11C1; # (넾; 넾; á„‚á…¦á‡; 넾; á„‚á…¦á‡; ) HANGUL SYLLABLE NEP
+B13F;B13F;1102 1166 11C2;B13F;1102 1166 11C2; # (넿; 넿; 넿; 넿; 넿; ) HANGUL SYLLABLE NEH
+B140;B140;1102 1167;B140;1102 1167; # (ë…€; ë…€; á„‚á…§; ë…€; á„‚á…§; ) HANGUL SYLLABLE NYEO
+B141;B141;1102 1167 11A8;B141;1102 1167 11A8; # (ë…; ë…; 녁; ë…; 녁; ) HANGUL SYLLABLE NYEOG
+B142;B142;1102 1167 11A9;B142;1102 1167 11A9; # (녂; 녂; 녂; 녂; 녂; ) HANGUL SYLLABLE NYEOGG
+B143;B143;1102 1167 11AA;B143;1102 1167 11AA; # (녃; 녃; 녃; 녃; 녃; ) HANGUL SYLLABLE NYEOGS
+B144;B144;1102 1167 11AB;B144;1102 1167 11AB; # (년; 년; 년; 년; 년; ) HANGUL SYLLABLE NYEON
+B145;B145;1102 1167 11AC;B145;1102 1167 11AC; # (녅; 녅; 녅; 녅; 녅; ) HANGUL SYLLABLE NYEONJ
+B146;B146;1102 1167 11AD;B146;1102 1167 11AD; # (녆; 녆; 녆; 녆; 녆; ) HANGUL SYLLABLE NYEONH
+B147;B147;1102 1167 11AE;B147;1102 1167 11AE; # (녇; 녇; 녇; 녇; 녇; ) HANGUL SYLLABLE NYEOD
+B148;B148;1102 1167 11AF;B148;1102 1167 11AF; # (녈; 녈; 녈; 녈; 녈; ) HANGUL SYLLABLE NYEOL
+B149;B149;1102 1167 11B0;B149;1102 1167 11B0; # (녉; 녉; 녉; 녉; 녉; ) HANGUL SYLLABLE NYEOLG
+B14A;B14A;1102 1167 11B1;B14A;1102 1167 11B1; # (녊; 녊; 녊; 녊; 녊; ) HANGUL SYLLABLE NYEOLM
+B14B;B14B;1102 1167 11B2;B14B;1102 1167 11B2; # (녋; 녋; 녋; 녋; 녋; ) HANGUL SYLLABLE NYEOLB
+B14C;B14C;1102 1167 11B3;B14C;1102 1167 11B3; # (녌; 녌; 녌; 녌; 녌; ) HANGUL SYLLABLE NYEOLS
+B14D;B14D;1102 1167 11B4;B14D;1102 1167 11B4; # (ë…; ë…; 녍; ë…; 녍; ) HANGUL SYLLABLE NYEOLT
+B14E;B14E;1102 1167 11B5;B14E;1102 1167 11B5; # (녎; 녎; 녎; 녎; 녎; ) HANGUL SYLLABLE NYEOLP
+B14F;B14F;1102 1167 11B6;B14F;1102 1167 11B6; # (ë…; ë…; 녏; ë…; 녏; ) HANGUL SYLLABLE NYEOLH
+B150;B150;1102 1167 11B7;B150;1102 1167 11B7; # (ë…; ë…; 념; ë…; 념; ) HANGUL SYLLABLE NYEOM
+B151;B151;1102 1167 11B8;B151;1102 1167 11B8; # (녑; 녑; 녑; 녑; 녑; ) HANGUL SYLLABLE NYEOB
+B152;B152;1102 1167 11B9;B152;1102 1167 11B9; # (녒; 녒; 녒; 녒; 녒; ) HANGUL SYLLABLE NYEOBS
+B153;B153;1102 1167 11BA;B153;1102 1167 11BA; # (녓; 녓; 녓; 녓; 녓; ) HANGUL SYLLABLE NYEOS
+B154;B154;1102 1167 11BB;B154;1102 1167 11BB; # (녔; 녔; 녔; 녔; 녔; ) HANGUL SYLLABLE NYEOSS
+B155;B155;1102 1167 11BC;B155;1102 1167 11BC; # (녕; 녕; 녕; 녕; 녕; ) HANGUL SYLLABLE NYEONG
+B156;B156;1102 1167 11BD;B156;1102 1167 11BD; # (녖; 녖; 녖; 녖; 녖; ) HANGUL SYLLABLE NYEOJ
+B157;B157;1102 1167 11BE;B157;1102 1167 11BE; # (녗; 녗; 녗; 녗; 녗; ) HANGUL SYLLABLE NYEOC
+B158;B158;1102 1167 11BF;B158;1102 1167 11BF; # (녘; 녘; 녘; 녘; 녘; ) HANGUL SYLLABLE NYEOK
+B159;B159;1102 1167 11C0;B159;1102 1167 11C0; # (녙; 녙; 녙; 녙; 녙; ) HANGUL SYLLABLE NYEOT
+B15A;B15A;1102 1167 11C1;B15A;1102 1167 11C1; # (ë…š; ë…š; á„‚á…§á‡; ë…š; á„‚á…§á‡; ) HANGUL SYLLABLE NYEOP
+B15B;B15B;1102 1167 11C2;B15B;1102 1167 11C2; # (녛; 녛; 녛; 녛; 녛; ) HANGUL SYLLABLE NYEOH
+B15C;B15C;1102 1168;B15C;1102 1168; # (녜; 녜; 녜; 녜; 녜; ) HANGUL SYLLABLE NYE
+B15D;B15D;1102 1168 11A8;B15D;1102 1168 11A8; # (ë…; ë…; 녝; ë…; 녝; ) HANGUL SYLLABLE NYEG
+B15E;B15E;1102 1168 11A9;B15E;1102 1168 11A9; # (녞; 녞; 녞; 녞; 녞; ) HANGUL SYLLABLE NYEGG
+B15F;B15F;1102 1168 11AA;B15F;1102 1168 11AA; # (녟; 녟; 녟; 녟; 녟; ) HANGUL SYLLABLE NYEGS
+B160;B160;1102 1168 11AB;B160;1102 1168 11AB; # (녠; 녠; 녠; 녠; 녠; ) HANGUL SYLLABLE NYEN
+B161;B161;1102 1168 11AC;B161;1102 1168 11AC; # (녡; 녡; 녡; 녡; 녡; ) HANGUL SYLLABLE NYENJ
+B162;B162;1102 1168 11AD;B162;1102 1168 11AD; # (녢; 녢; 녢; 녢; 녢; ) HANGUL SYLLABLE NYENH
+B163;B163;1102 1168 11AE;B163;1102 1168 11AE; # (녣; 녣; 녣; 녣; 녣; ) HANGUL SYLLABLE NYED
+B164;B164;1102 1168 11AF;B164;1102 1168 11AF; # (녤; 녤; 녤; 녤; 녤; ) HANGUL SYLLABLE NYEL
+B165;B165;1102 1168 11B0;B165;1102 1168 11B0; # (녥; 녥; 녥; 녥; 녥; ) HANGUL SYLLABLE NYELG
+B166;B166;1102 1168 11B1;B166;1102 1168 11B1; # (녦; 녦; 녦; 녦; 녦; ) HANGUL SYLLABLE NYELM
+B167;B167;1102 1168 11B2;B167;1102 1168 11B2; # (녧; 녧; 녧; 녧; 녧; ) HANGUL SYLLABLE NYELB
+B168;B168;1102 1168 11B3;B168;1102 1168 11B3; # (녨; 녨; 녨; 녨; 녨; ) HANGUL SYLLABLE NYELS
+B169;B169;1102 1168 11B4;B169;1102 1168 11B4; # (녩; 녩; 녩; 녩; 녩; ) HANGUL SYLLABLE NYELT
+B16A;B16A;1102 1168 11B5;B16A;1102 1168 11B5; # (녪; 녪; 녪; 녪; 녪; ) HANGUL SYLLABLE NYELP
+B16B;B16B;1102 1168 11B6;B16B;1102 1168 11B6; # (녫; 녫; 녫; 녫; 녫; ) HANGUL SYLLABLE NYELH
+B16C;B16C;1102 1168 11B7;B16C;1102 1168 11B7; # (녬; 녬; 녬; 녬; 녬; ) HANGUL SYLLABLE NYEM
+B16D;B16D;1102 1168 11B8;B16D;1102 1168 11B8; # (녭; 녭; 녭; 녭; 녭; ) HANGUL SYLLABLE NYEB
+B16E;B16E;1102 1168 11B9;B16E;1102 1168 11B9; # (녮; 녮; 녮; 녮; 녮; ) HANGUL SYLLABLE NYEBS
+B16F;B16F;1102 1168 11BA;B16F;1102 1168 11BA; # (녯; 녯; 녯; 녯; 녯; ) HANGUL SYLLABLE NYES
+B170;B170;1102 1168 11BB;B170;1102 1168 11BB; # (녰; 녰; 녰; 녰; 녰; ) HANGUL SYLLABLE NYESS
+B171;B171;1102 1168 11BC;B171;1102 1168 11BC; # (녱; 녱; 녱; 녱; 녱; ) HANGUL SYLLABLE NYENG
+B172;B172;1102 1168 11BD;B172;1102 1168 11BD; # (녲; 녲; 녲; 녲; 녲; ) HANGUL SYLLABLE NYEJ
+B173;B173;1102 1168 11BE;B173;1102 1168 11BE; # (녳; 녳; 녳; 녳; 녳; ) HANGUL SYLLABLE NYEC
+B174;B174;1102 1168 11BF;B174;1102 1168 11BF; # (녴; 녴; 녴; 녴; 녴; ) HANGUL SYLLABLE NYEK
+B175;B175;1102 1168 11C0;B175;1102 1168 11C0; # (녵; 녵; 녵; 녵; 녵; ) HANGUL SYLLABLE NYET
+B176;B176;1102 1168 11C1;B176;1102 1168 11C1; # (ë…¶; ë…¶; á„‚á…¨á‡; ë…¶; á„‚á…¨á‡; ) HANGUL SYLLABLE NYEP
+B177;B177;1102 1168 11C2;B177;1102 1168 11C2; # (녷; 녷; 녷; 녷; 녷; ) HANGUL SYLLABLE NYEH
+B178;B178;1102 1169;B178;1102 1169; # (ë…¸; ë…¸; á„‚á…©; ë…¸; á„‚á…©; ) HANGUL SYLLABLE NO
+B179;B179;1102 1169 11A8;B179;1102 1169 11A8; # (녹; 녹; 녹; 녹; 녹; ) HANGUL SYLLABLE NOG
+B17A;B17A;1102 1169 11A9;B17A;1102 1169 11A9; # (녺; 녺; 녺; 녺; 녺; ) HANGUL SYLLABLE NOGG
+B17B;B17B;1102 1169 11AA;B17B;1102 1169 11AA; # (녻; 녻; 녻; 녻; 녻; ) HANGUL SYLLABLE NOGS
+B17C;B17C;1102 1169 11AB;B17C;1102 1169 11AB; # (논; 논; 논; 논; 논; ) HANGUL SYLLABLE NON
+B17D;B17D;1102 1169 11AC;B17D;1102 1169 11AC; # (녽; 녽; 녽; 녽; 녽; ) HANGUL SYLLABLE NONJ
+B17E;B17E;1102 1169 11AD;B17E;1102 1169 11AD; # (녾; 녾; 녾; 녾; 녾; ) HANGUL SYLLABLE NONH
+B17F;B17F;1102 1169 11AE;B17F;1102 1169 11AE; # (녿; 녿; 녿; 녿; 녿; ) HANGUL SYLLABLE NOD
+B180;B180;1102 1169 11AF;B180;1102 1169 11AF; # (놀; 놀; 놀; 놀; 놀; ) HANGUL SYLLABLE NOL
+B181;B181;1102 1169 11B0;B181;1102 1169 11B0; # (ë†; ë†; 놁; ë†; 놁; ) HANGUL SYLLABLE NOLG
+B182;B182;1102 1169 11B1;B182;1102 1169 11B1; # (놂; 놂; 놂; 놂; 놂; ) HANGUL SYLLABLE NOLM
+B183;B183;1102 1169 11B2;B183;1102 1169 11B2; # (놃; 놃; 놃; 놃; 놃; ) HANGUL SYLLABLE NOLB
+B184;B184;1102 1169 11B3;B184;1102 1169 11B3; # (놄; 놄; 놄; 놄; 놄; ) HANGUL SYLLABLE NOLS
+B185;B185;1102 1169 11B4;B185;1102 1169 11B4; # (놅; 놅; 놅; 놅; 놅; ) HANGUL SYLLABLE NOLT
+B186;B186;1102 1169 11B5;B186;1102 1169 11B5; # (놆; 놆; 놆; 놆; 놆; ) HANGUL SYLLABLE NOLP
+B187;B187;1102 1169 11B6;B187;1102 1169 11B6; # (놇; 놇; 놇; 놇; 놇; ) HANGUL SYLLABLE NOLH
+B188;B188;1102 1169 11B7;B188;1102 1169 11B7; # (놈; 놈; 놈; 놈; 놈; ) HANGUL SYLLABLE NOM
+B189;B189;1102 1169 11B8;B189;1102 1169 11B8; # (놉; 놉; 놉; 놉; 놉; ) HANGUL SYLLABLE NOB
+B18A;B18A;1102 1169 11B9;B18A;1102 1169 11B9; # (놊; 놊; 놊; 놊; 놊; ) HANGUL SYLLABLE NOBS
+B18B;B18B;1102 1169 11BA;B18B;1102 1169 11BA; # (놋; 놋; 놋; 놋; 놋; ) HANGUL SYLLABLE NOS
+B18C;B18C;1102 1169 11BB;B18C;1102 1169 11BB; # (놌; 놌; 놌; 놌; 놌; ) HANGUL SYLLABLE NOSS
+B18D;B18D;1102 1169 11BC;B18D;1102 1169 11BC; # (ë†; ë†; 농; ë†; 농; ) HANGUL SYLLABLE NONG
+B18E;B18E;1102 1169 11BD;B18E;1102 1169 11BD; # (놎; 놎; 놎; 놎; 놎; ) HANGUL SYLLABLE NOJ
+B18F;B18F;1102 1169 11BE;B18F;1102 1169 11BE; # (ë†; ë†; 놏; ë†; 놏; ) HANGUL SYLLABLE NOC
+B190;B190;1102 1169 11BF;B190;1102 1169 11BF; # (ë†; ë†; 놐; ë†; 놐; ) HANGUL SYLLABLE NOK
+B191;B191;1102 1169 11C0;B191;1102 1169 11C0; # (놑; 놑; 놑; 놑; 놑; ) HANGUL SYLLABLE NOT
+B192;B192;1102 1169 11C1;B192;1102 1169 11C1; # (높; 높; á„‚á…©á‡; 높; á„‚á…©á‡; ) HANGUL SYLLABLE NOP
+B193;B193;1102 1169 11C2;B193;1102 1169 11C2; # (놓; 놓; 놓; 놓; 놓; ) HANGUL SYLLABLE NOH
+B194;B194;1102 116A;B194;1102 116A; # (놔; 놔; 놔; 놔; 놔; ) HANGUL SYLLABLE NWA
+B195;B195;1102 116A 11A8;B195;1102 116A 11A8; # (놕; 놕; 놕; 놕; 놕; ) HANGUL SYLLABLE NWAG
+B196;B196;1102 116A 11A9;B196;1102 116A 11A9; # (놖; 놖; 놖; 놖; 놖; ) HANGUL SYLLABLE NWAGG
+B197;B197;1102 116A 11AA;B197;1102 116A 11AA; # (놗; 놗; 놗; 놗; 놗; ) HANGUL SYLLABLE NWAGS
+B198;B198;1102 116A 11AB;B198;1102 116A 11AB; # (놘; 놘; 놘; 놘; 놘; ) HANGUL SYLLABLE NWAN
+B199;B199;1102 116A 11AC;B199;1102 116A 11AC; # (놙; 놙; 놙; 놙; 놙; ) HANGUL SYLLABLE NWANJ
+B19A;B19A;1102 116A 11AD;B19A;1102 116A 11AD; # (놚; 놚; 놚; 놚; 놚; ) HANGUL SYLLABLE NWANH
+B19B;B19B;1102 116A 11AE;B19B;1102 116A 11AE; # (놛; 놛; 놛; 놛; 놛; ) HANGUL SYLLABLE NWAD
+B19C;B19C;1102 116A 11AF;B19C;1102 116A 11AF; # (놜; 놜; 놜; 놜; 놜; ) HANGUL SYLLABLE NWAL
+B19D;B19D;1102 116A 11B0;B19D;1102 116A 11B0; # (ë†; ë†; 놝; ë†; 놝; ) HANGUL SYLLABLE NWALG
+B19E;B19E;1102 116A 11B1;B19E;1102 116A 11B1; # (놞; 놞; 놞; 놞; 놞; ) HANGUL SYLLABLE NWALM
+B19F;B19F;1102 116A 11B2;B19F;1102 116A 11B2; # (놟; 놟; 놟; 놟; 놟; ) HANGUL SYLLABLE NWALB
+B1A0;B1A0;1102 116A 11B3;B1A0;1102 116A 11B3; # (놠; 놠; 놠; 놠; 놠; ) HANGUL SYLLABLE NWALS
+B1A1;B1A1;1102 116A 11B4;B1A1;1102 116A 11B4; # (놡; 놡; 놡; 놡; 놡; ) HANGUL SYLLABLE NWALT
+B1A2;B1A2;1102 116A 11B5;B1A2;1102 116A 11B5; # (놢; 놢; 놢; 놢; 놢; ) HANGUL SYLLABLE NWALP
+B1A3;B1A3;1102 116A 11B6;B1A3;1102 116A 11B6; # (놣; 놣; 놣; 놣; 놣; ) HANGUL SYLLABLE NWALH
+B1A4;B1A4;1102 116A 11B7;B1A4;1102 116A 11B7; # (놤; 놤; 놤; 놤; 놤; ) HANGUL SYLLABLE NWAM
+B1A5;B1A5;1102 116A 11B8;B1A5;1102 116A 11B8; # (놥; 놥; 놥; 놥; 놥; ) HANGUL SYLLABLE NWAB
+B1A6;B1A6;1102 116A 11B9;B1A6;1102 116A 11B9; # (놦; 놦; 놦; 놦; 놦; ) HANGUL SYLLABLE NWABS
+B1A7;B1A7;1102 116A 11BA;B1A7;1102 116A 11BA; # (놧; 놧; 놧; 놧; 놧; ) HANGUL SYLLABLE NWAS
+B1A8;B1A8;1102 116A 11BB;B1A8;1102 116A 11BB; # (놨; 놨; 놨; 놨; 놨; ) HANGUL SYLLABLE NWASS
+B1A9;B1A9;1102 116A 11BC;B1A9;1102 116A 11BC; # (놩; 놩; 놩; 놩; 놩; ) HANGUL SYLLABLE NWANG
+B1AA;B1AA;1102 116A 11BD;B1AA;1102 116A 11BD; # (놪; 놪; 놪; 놪; 놪; ) HANGUL SYLLABLE NWAJ
+B1AB;B1AB;1102 116A 11BE;B1AB;1102 116A 11BE; # (놫; 놫; 놫; 놫; 놫; ) HANGUL SYLLABLE NWAC
+B1AC;B1AC;1102 116A 11BF;B1AC;1102 116A 11BF; # (놬; 놬; 놬; 놬; 놬; ) HANGUL SYLLABLE NWAK
+B1AD;B1AD;1102 116A 11C0;B1AD;1102 116A 11C0; # (놭; 놭; 놭; 놭; 놭; ) HANGUL SYLLABLE NWAT
+B1AE;B1AE;1102 116A 11C1;B1AE;1102 116A 11C1; # (놮; 놮; á„‚á…ªá‡; 놮; á„‚á…ªá‡; ) HANGUL SYLLABLE NWAP
+B1AF;B1AF;1102 116A 11C2;B1AF;1102 116A 11C2; # (놯; 놯; 놯; 놯; 놯; ) HANGUL SYLLABLE NWAH
+B1B0;B1B0;1102 116B;B1B0;1102 116B; # (놰; 놰; 놰; 놰; 놰; ) HANGUL SYLLABLE NWAE
+B1B1;B1B1;1102 116B 11A8;B1B1;1102 116B 11A8; # (놱; 놱; 놱; 놱; 놱; ) HANGUL SYLLABLE NWAEG
+B1B2;B1B2;1102 116B 11A9;B1B2;1102 116B 11A9; # (놲; 놲; 놲; 놲; 놲; ) HANGUL SYLLABLE NWAEGG
+B1B3;B1B3;1102 116B 11AA;B1B3;1102 116B 11AA; # (놳; 놳; 놳; 놳; 놳; ) HANGUL SYLLABLE NWAEGS
+B1B4;B1B4;1102 116B 11AB;B1B4;1102 116B 11AB; # (놴; 놴; 놴; 놴; 놴; ) HANGUL SYLLABLE NWAEN
+B1B5;B1B5;1102 116B 11AC;B1B5;1102 116B 11AC; # (놵; 놵; 놵; 놵; 놵; ) HANGUL SYLLABLE NWAENJ
+B1B6;B1B6;1102 116B 11AD;B1B6;1102 116B 11AD; # (놶; 놶; 놶; 놶; 놶; ) HANGUL SYLLABLE NWAENH
+B1B7;B1B7;1102 116B 11AE;B1B7;1102 116B 11AE; # (놷; 놷; 놷; 놷; 놷; ) HANGUL SYLLABLE NWAED
+B1B8;B1B8;1102 116B 11AF;B1B8;1102 116B 11AF; # (놸; 놸; 놸; 놸; 놸; ) HANGUL SYLLABLE NWAEL
+B1B9;B1B9;1102 116B 11B0;B1B9;1102 116B 11B0; # (놹; 놹; 놹; 놹; 놹; ) HANGUL SYLLABLE NWAELG
+B1BA;B1BA;1102 116B 11B1;B1BA;1102 116B 11B1; # (놺; 놺; 놺; 놺; 놺; ) HANGUL SYLLABLE NWAELM
+B1BB;B1BB;1102 116B 11B2;B1BB;1102 116B 11B2; # (놻; 놻; 놻; 놻; 놻; ) HANGUL SYLLABLE NWAELB
+B1BC;B1BC;1102 116B 11B3;B1BC;1102 116B 11B3; # (놼; 놼; 놼; 놼; 놼; ) HANGUL SYLLABLE NWAELS
+B1BD;B1BD;1102 116B 11B4;B1BD;1102 116B 11B4; # (놽; 놽; 놽; 놽; 놽; ) HANGUL SYLLABLE NWAELT
+B1BE;B1BE;1102 116B 11B5;B1BE;1102 116B 11B5; # (놾; 놾; 놾; 놾; 놾; ) HANGUL SYLLABLE NWAELP
+B1BF;B1BF;1102 116B 11B6;B1BF;1102 116B 11B6; # (놿; 놿; 놿; 놿; 놿; ) HANGUL SYLLABLE NWAELH
+B1C0;B1C0;1102 116B 11B7;B1C0;1102 116B 11B7; # (뇀; 뇀; 뇀; 뇀; 뇀; ) HANGUL SYLLABLE NWAEM
+B1C1;B1C1;1102 116B 11B8;B1C1;1102 116B 11B8; # (ë‡; ë‡; 뇁; ë‡; 뇁; ) HANGUL SYLLABLE NWAEB
+B1C2;B1C2;1102 116B 11B9;B1C2;1102 116B 11B9; # (뇂; 뇂; 뇂; 뇂; 뇂; ) HANGUL SYLLABLE NWAEBS
+B1C3;B1C3;1102 116B 11BA;B1C3;1102 116B 11BA; # (뇃; 뇃; 뇃; 뇃; 뇃; ) HANGUL SYLLABLE NWAES
+B1C4;B1C4;1102 116B 11BB;B1C4;1102 116B 11BB; # (뇄; 뇄; 뇄; 뇄; 뇄; ) HANGUL SYLLABLE NWAESS
+B1C5;B1C5;1102 116B 11BC;B1C5;1102 116B 11BC; # (뇅; 뇅; 뇅; 뇅; 뇅; ) HANGUL SYLLABLE NWAENG
+B1C6;B1C6;1102 116B 11BD;B1C6;1102 116B 11BD; # (뇆; 뇆; 뇆; 뇆; 뇆; ) HANGUL SYLLABLE NWAEJ
+B1C7;B1C7;1102 116B 11BE;B1C7;1102 116B 11BE; # (뇇; 뇇; 뇇; 뇇; 뇇; ) HANGUL SYLLABLE NWAEC
+B1C8;B1C8;1102 116B 11BF;B1C8;1102 116B 11BF; # (뇈; 뇈; 뇈; 뇈; 뇈; ) HANGUL SYLLABLE NWAEK
+B1C9;B1C9;1102 116B 11C0;B1C9;1102 116B 11C0; # (뇉; 뇉; 뇉; 뇉; 뇉; ) HANGUL SYLLABLE NWAET
+B1CA;B1CA;1102 116B 11C1;B1CA;1102 116B 11C1; # (뇊; 뇊; á„‚á…«á‡; 뇊; á„‚á…«á‡; ) HANGUL SYLLABLE NWAEP
+B1CB;B1CB;1102 116B 11C2;B1CB;1102 116B 11C2; # (뇋; 뇋; 뇋; 뇋; 뇋; ) HANGUL SYLLABLE NWAEH
+B1CC;B1CC;1102 116C;B1CC;1102 116C; # (뇌; 뇌; 뇌; 뇌; 뇌; ) HANGUL SYLLABLE NOE
+B1CD;B1CD;1102 116C 11A8;B1CD;1102 116C 11A8; # (ë‡; ë‡; 뇍; ë‡; 뇍; ) HANGUL SYLLABLE NOEG
+B1CE;B1CE;1102 116C 11A9;B1CE;1102 116C 11A9; # (뇎; 뇎; 뇎; 뇎; 뇎; ) HANGUL SYLLABLE NOEGG
+B1CF;B1CF;1102 116C 11AA;B1CF;1102 116C 11AA; # (ë‡; ë‡; 뇏; ë‡; 뇏; ) HANGUL SYLLABLE NOEGS
+B1D0;B1D0;1102 116C 11AB;B1D0;1102 116C 11AB; # (ë‡; ë‡; 뇐; ë‡; 뇐; ) HANGUL SYLLABLE NOEN
+B1D1;B1D1;1102 116C 11AC;B1D1;1102 116C 11AC; # (뇑; 뇑; 뇑; 뇑; 뇑; ) HANGUL SYLLABLE NOENJ
+B1D2;B1D2;1102 116C 11AD;B1D2;1102 116C 11AD; # (뇒; 뇒; 뇒; 뇒; 뇒; ) HANGUL SYLLABLE NOENH
+B1D3;B1D3;1102 116C 11AE;B1D3;1102 116C 11AE; # (뇓; 뇓; 뇓; 뇓; 뇓; ) HANGUL SYLLABLE NOED
+B1D4;B1D4;1102 116C 11AF;B1D4;1102 116C 11AF; # (뇔; 뇔; 뇔; 뇔; 뇔; ) HANGUL SYLLABLE NOEL
+B1D5;B1D5;1102 116C 11B0;B1D5;1102 116C 11B0; # (뇕; 뇕; 뇕; 뇕; 뇕; ) HANGUL SYLLABLE NOELG
+B1D6;B1D6;1102 116C 11B1;B1D6;1102 116C 11B1; # (뇖; 뇖; 뇖; 뇖; 뇖; ) HANGUL SYLLABLE NOELM
+B1D7;B1D7;1102 116C 11B2;B1D7;1102 116C 11B2; # (뇗; 뇗; 뇗; 뇗; 뇗; ) HANGUL SYLLABLE NOELB
+B1D8;B1D8;1102 116C 11B3;B1D8;1102 116C 11B3; # (뇘; 뇘; 뇘; 뇘; 뇘; ) HANGUL SYLLABLE NOELS
+B1D9;B1D9;1102 116C 11B4;B1D9;1102 116C 11B4; # (뇙; 뇙; 뇙; 뇙; 뇙; ) HANGUL SYLLABLE NOELT
+B1DA;B1DA;1102 116C 11B5;B1DA;1102 116C 11B5; # (뇚; 뇚; 뇚; 뇚; 뇚; ) HANGUL SYLLABLE NOELP
+B1DB;B1DB;1102 116C 11B6;B1DB;1102 116C 11B6; # (뇛; 뇛; 뇛; 뇛; 뇛; ) HANGUL SYLLABLE NOELH
+B1DC;B1DC;1102 116C 11B7;B1DC;1102 116C 11B7; # (뇜; 뇜; 뇜; 뇜; 뇜; ) HANGUL SYLLABLE NOEM
+B1DD;B1DD;1102 116C 11B8;B1DD;1102 116C 11B8; # (ë‡; ë‡; 뇝; ë‡; 뇝; ) HANGUL SYLLABLE NOEB
+B1DE;B1DE;1102 116C 11B9;B1DE;1102 116C 11B9; # (뇞; 뇞; 뇞; 뇞; 뇞; ) HANGUL SYLLABLE NOEBS
+B1DF;B1DF;1102 116C 11BA;B1DF;1102 116C 11BA; # (뇟; 뇟; 뇟; 뇟; 뇟; ) HANGUL SYLLABLE NOES
+B1E0;B1E0;1102 116C 11BB;B1E0;1102 116C 11BB; # (뇠; 뇠; 뇠; 뇠; 뇠; ) HANGUL SYLLABLE NOESS
+B1E1;B1E1;1102 116C 11BC;B1E1;1102 116C 11BC; # (뇡; 뇡; 뇡; 뇡; 뇡; ) HANGUL SYLLABLE NOENG
+B1E2;B1E2;1102 116C 11BD;B1E2;1102 116C 11BD; # (뇢; 뇢; 뇢; 뇢; 뇢; ) HANGUL SYLLABLE NOEJ
+B1E3;B1E3;1102 116C 11BE;B1E3;1102 116C 11BE; # (뇣; 뇣; 뇣; 뇣; 뇣; ) HANGUL SYLLABLE NOEC
+B1E4;B1E4;1102 116C 11BF;B1E4;1102 116C 11BF; # (뇤; 뇤; 뇤; 뇤; 뇤; ) HANGUL SYLLABLE NOEK
+B1E5;B1E5;1102 116C 11C0;B1E5;1102 116C 11C0; # (뇥; 뇥; 뇥; 뇥; 뇥; ) HANGUL SYLLABLE NOET
+B1E6;B1E6;1102 116C 11C1;B1E6;1102 116C 11C1; # (뇦; 뇦; á„‚á…¬á‡; 뇦; á„‚á…¬á‡; ) HANGUL SYLLABLE NOEP
+B1E7;B1E7;1102 116C 11C2;B1E7;1102 116C 11C2; # (뇧; 뇧; 뇧; 뇧; 뇧; ) HANGUL SYLLABLE NOEH
+B1E8;B1E8;1102 116D;B1E8;1102 116D; # (뇨; 뇨; 뇨; 뇨; 뇨; ) HANGUL SYLLABLE NYO
+B1E9;B1E9;1102 116D 11A8;B1E9;1102 116D 11A8; # (뇩; 뇩; 뇩; 뇩; 뇩; ) HANGUL SYLLABLE NYOG
+B1EA;B1EA;1102 116D 11A9;B1EA;1102 116D 11A9; # (뇪; 뇪; 뇪; 뇪; 뇪; ) HANGUL SYLLABLE NYOGG
+B1EB;B1EB;1102 116D 11AA;B1EB;1102 116D 11AA; # (뇫; 뇫; 뇫; 뇫; 뇫; ) HANGUL SYLLABLE NYOGS
+B1EC;B1EC;1102 116D 11AB;B1EC;1102 116D 11AB; # (뇬; 뇬; 뇬; 뇬; 뇬; ) HANGUL SYLLABLE NYON
+B1ED;B1ED;1102 116D 11AC;B1ED;1102 116D 11AC; # (뇭; 뇭; 뇭; 뇭; 뇭; ) HANGUL SYLLABLE NYONJ
+B1EE;B1EE;1102 116D 11AD;B1EE;1102 116D 11AD; # (뇮; 뇮; 뇮; 뇮; 뇮; ) HANGUL SYLLABLE NYONH
+B1EF;B1EF;1102 116D 11AE;B1EF;1102 116D 11AE; # (뇯; 뇯; 뇯; 뇯; 뇯; ) HANGUL SYLLABLE NYOD
+B1F0;B1F0;1102 116D 11AF;B1F0;1102 116D 11AF; # (뇰; 뇰; 뇰; 뇰; 뇰; ) HANGUL SYLLABLE NYOL
+B1F1;B1F1;1102 116D 11B0;B1F1;1102 116D 11B0; # (뇱; 뇱; 뇱; 뇱; 뇱; ) HANGUL SYLLABLE NYOLG
+B1F2;B1F2;1102 116D 11B1;B1F2;1102 116D 11B1; # (뇲; 뇲; 뇲; 뇲; 뇲; ) HANGUL SYLLABLE NYOLM
+B1F3;B1F3;1102 116D 11B2;B1F3;1102 116D 11B2; # (뇳; 뇳; 뇳; 뇳; 뇳; ) HANGUL SYLLABLE NYOLB
+B1F4;B1F4;1102 116D 11B3;B1F4;1102 116D 11B3; # (뇴; 뇴; 뇴; 뇴; 뇴; ) HANGUL SYLLABLE NYOLS
+B1F5;B1F5;1102 116D 11B4;B1F5;1102 116D 11B4; # (뇵; 뇵; 뇵; 뇵; 뇵; ) HANGUL SYLLABLE NYOLT
+B1F6;B1F6;1102 116D 11B5;B1F6;1102 116D 11B5; # (뇶; 뇶; 뇶; 뇶; 뇶; ) HANGUL SYLLABLE NYOLP
+B1F7;B1F7;1102 116D 11B6;B1F7;1102 116D 11B6; # (뇷; 뇷; 뇷; 뇷; 뇷; ) HANGUL SYLLABLE NYOLH
+B1F8;B1F8;1102 116D 11B7;B1F8;1102 116D 11B7; # (뇸; 뇸; 뇸; 뇸; 뇸; ) HANGUL SYLLABLE NYOM
+B1F9;B1F9;1102 116D 11B8;B1F9;1102 116D 11B8; # (뇹; 뇹; 뇹; 뇹; 뇹; ) HANGUL SYLLABLE NYOB
+B1FA;B1FA;1102 116D 11B9;B1FA;1102 116D 11B9; # (뇺; 뇺; 뇺; 뇺; 뇺; ) HANGUL SYLLABLE NYOBS
+B1FB;B1FB;1102 116D 11BA;B1FB;1102 116D 11BA; # (뇻; 뇻; 뇻; 뇻; 뇻; ) HANGUL SYLLABLE NYOS
+B1FC;B1FC;1102 116D 11BB;B1FC;1102 116D 11BB; # (뇼; 뇼; 뇼; 뇼; 뇼; ) HANGUL SYLLABLE NYOSS
+B1FD;B1FD;1102 116D 11BC;B1FD;1102 116D 11BC; # (뇽; 뇽; 뇽; 뇽; 뇽; ) HANGUL SYLLABLE NYONG
+B1FE;B1FE;1102 116D 11BD;B1FE;1102 116D 11BD; # (뇾; 뇾; 뇾; 뇾; 뇾; ) HANGUL SYLLABLE NYOJ
+B1FF;B1FF;1102 116D 11BE;B1FF;1102 116D 11BE; # (뇿; 뇿; 뇿; 뇿; 뇿; ) HANGUL SYLLABLE NYOC
+B200;B200;1102 116D 11BF;B200;1102 116D 11BF; # (눀; 눀; 눀; 눀; 눀; ) HANGUL SYLLABLE NYOK
+B201;B201;1102 116D 11C0;B201;1102 116D 11C0; # (ëˆ; ëˆ; 눁; ëˆ; 눁; ) HANGUL SYLLABLE NYOT
+B202;B202;1102 116D 11C1;B202;1102 116D 11C1; # (눂; 눂; á„‚á…­á‡; 눂; á„‚á…­á‡; ) HANGUL SYLLABLE NYOP
+B203;B203;1102 116D 11C2;B203;1102 116D 11C2; # (눃; 눃; 눃; 눃; 눃; ) HANGUL SYLLABLE NYOH
+B204;B204;1102 116E;B204;1102 116E; # (누; 누; 누; 누; 누; ) HANGUL SYLLABLE NU
+B205;B205;1102 116E 11A8;B205;1102 116E 11A8; # (눅; 눅; 눅; 눅; 눅; ) HANGUL SYLLABLE NUG
+B206;B206;1102 116E 11A9;B206;1102 116E 11A9; # (눆; 눆; 눆; 눆; 눆; ) HANGUL SYLLABLE NUGG
+B207;B207;1102 116E 11AA;B207;1102 116E 11AA; # (눇; 눇; 눇; 눇; 눇; ) HANGUL SYLLABLE NUGS
+B208;B208;1102 116E 11AB;B208;1102 116E 11AB; # (눈; 눈; 눈; 눈; 눈; ) HANGUL SYLLABLE NUN
+B209;B209;1102 116E 11AC;B209;1102 116E 11AC; # (눉; 눉; 눉; 눉; 눉; ) HANGUL SYLLABLE NUNJ
+B20A;B20A;1102 116E 11AD;B20A;1102 116E 11AD; # (눊; 눊; 눊; 눊; 눊; ) HANGUL SYLLABLE NUNH
+B20B;B20B;1102 116E 11AE;B20B;1102 116E 11AE; # (눋; 눋; 눋; 눋; 눋; ) HANGUL SYLLABLE NUD
+B20C;B20C;1102 116E 11AF;B20C;1102 116E 11AF; # (눌; 눌; 눌; 눌; 눌; ) HANGUL SYLLABLE NUL
+B20D;B20D;1102 116E 11B0;B20D;1102 116E 11B0; # (ëˆ; ëˆ; 눍; ëˆ; 눍; ) HANGUL SYLLABLE NULG
+B20E;B20E;1102 116E 11B1;B20E;1102 116E 11B1; # (눎; 눎; 눎; 눎; 눎; ) HANGUL SYLLABLE NULM
+B20F;B20F;1102 116E 11B2;B20F;1102 116E 11B2; # (ëˆ; ëˆ; 눏; ëˆ; 눏; ) HANGUL SYLLABLE NULB
+B210;B210;1102 116E 11B3;B210;1102 116E 11B3; # (ëˆ; ëˆ; 눐; ëˆ; 눐; ) HANGUL SYLLABLE NULS
+B211;B211;1102 116E 11B4;B211;1102 116E 11B4; # (눑; 눑; 눑; 눑; 눑; ) HANGUL SYLLABLE NULT
+B212;B212;1102 116E 11B5;B212;1102 116E 11B5; # (눒; 눒; 눒; 눒; 눒; ) HANGUL SYLLABLE NULP
+B213;B213;1102 116E 11B6;B213;1102 116E 11B6; # (눓; 눓; 눓; 눓; 눓; ) HANGUL SYLLABLE NULH
+B214;B214;1102 116E 11B7;B214;1102 116E 11B7; # (눔; 눔; 눔; 눔; 눔; ) HANGUL SYLLABLE NUM
+B215;B215;1102 116E 11B8;B215;1102 116E 11B8; # (눕; 눕; 눕; 눕; 눕; ) HANGUL SYLLABLE NUB
+B216;B216;1102 116E 11B9;B216;1102 116E 11B9; # (눖; 눖; 눖; 눖; 눖; ) HANGUL SYLLABLE NUBS
+B217;B217;1102 116E 11BA;B217;1102 116E 11BA; # (눗; 눗; 눗; 눗; 눗; ) HANGUL SYLLABLE NUS
+B218;B218;1102 116E 11BB;B218;1102 116E 11BB; # (눘; 눘; 눘; 눘; 눘; ) HANGUL SYLLABLE NUSS
+B219;B219;1102 116E 11BC;B219;1102 116E 11BC; # (눙; 눙; 눙; 눙; 눙; ) HANGUL SYLLABLE NUNG
+B21A;B21A;1102 116E 11BD;B21A;1102 116E 11BD; # (눚; 눚; 눚; 눚; 눚; ) HANGUL SYLLABLE NUJ
+B21B;B21B;1102 116E 11BE;B21B;1102 116E 11BE; # (눛; 눛; 눛; 눛; 눛; ) HANGUL SYLLABLE NUC
+B21C;B21C;1102 116E 11BF;B21C;1102 116E 11BF; # (눜; 눜; 눜; 눜; 눜; ) HANGUL SYLLABLE NUK
+B21D;B21D;1102 116E 11C0;B21D;1102 116E 11C0; # (ëˆ; ëˆ; 눝; ëˆ; 눝; ) HANGUL SYLLABLE NUT
+B21E;B21E;1102 116E 11C1;B21E;1102 116E 11C1; # (눞; 눞; á„‚á…®á‡; 눞; á„‚á…®á‡; ) HANGUL SYLLABLE NUP
+B21F;B21F;1102 116E 11C2;B21F;1102 116E 11C2; # (눟; 눟; 눟; 눟; 눟; ) HANGUL SYLLABLE NUH
+B220;B220;1102 116F;B220;1102 116F; # (눠; 눠; 눠; 눠; 눠; ) HANGUL SYLLABLE NWEO
+B221;B221;1102 116F 11A8;B221;1102 116F 11A8; # (눡; 눡; 눡; 눡; 눡; ) HANGUL SYLLABLE NWEOG
+B222;B222;1102 116F 11A9;B222;1102 116F 11A9; # (눢; 눢; 눢; 눢; 눢; ) HANGUL SYLLABLE NWEOGG
+B223;B223;1102 116F 11AA;B223;1102 116F 11AA; # (눣; 눣; 눣; 눣; 눣; ) HANGUL SYLLABLE NWEOGS
+B224;B224;1102 116F 11AB;B224;1102 116F 11AB; # (눤; 눤; 눤; 눤; 눤; ) HANGUL SYLLABLE NWEON
+B225;B225;1102 116F 11AC;B225;1102 116F 11AC; # (눥; 눥; 눥; 눥; 눥; ) HANGUL SYLLABLE NWEONJ
+B226;B226;1102 116F 11AD;B226;1102 116F 11AD; # (눦; 눦; 눦; 눦; 눦; ) HANGUL SYLLABLE NWEONH
+B227;B227;1102 116F 11AE;B227;1102 116F 11AE; # (눧; 눧; 눧; 눧; 눧; ) HANGUL SYLLABLE NWEOD
+B228;B228;1102 116F 11AF;B228;1102 116F 11AF; # (눨; 눨; 눨; 눨; 눨; ) HANGUL SYLLABLE NWEOL
+B229;B229;1102 116F 11B0;B229;1102 116F 11B0; # (눩; 눩; 눩; 눩; 눩; ) HANGUL SYLLABLE NWEOLG
+B22A;B22A;1102 116F 11B1;B22A;1102 116F 11B1; # (눪; 눪; 눪; 눪; 눪; ) HANGUL SYLLABLE NWEOLM
+B22B;B22B;1102 116F 11B2;B22B;1102 116F 11B2; # (눫; 눫; 눫; 눫; 눫; ) HANGUL SYLLABLE NWEOLB
+B22C;B22C;1102 116F 11B3;B22C;1102 116F 11B3; # (눬; 눬; 눬; 눬; 눬; ) HANGUL SYLLABLE NWEOLS
+B22D;B22D;1102 116F 11B4;B22D;1102 116F 11B4; # (눭; 눭; 눭; 눭; 눭; ) HANGUL SYLLABLE NWEOLT
+B22E;B22E;1102 116F 11B5;B22E;1102 116F 11B5; # (눮; 눮; 눮; 눮; 눮; ) HANGUL SYLLABLE NWEOLP
+B22F;B22F;1102 116F 11B6;B22F;1102 116F 11B6; # (눯; 눯; 눯; 눯; 눯; ) HANGUL SYLLABLE NWEOLH
+B230;B230;1102 116F 11B7;B230;1102 116F 11B7; # (눰; 눰; 눰; 눰; 눰; ) HANGUL SYLLABLE NWEOM
+B231;B231;1102 116F 11B8;B231;1102 116F 11B8; # (눱; 눱; 눱; 눱; 눱; ) HANGUL SYLLABLE NWEOB
+B232;B232;1102 116F 11B9;B232;1102 116F 11B9; # (눲; 눲; 눲; 눲; 눲; ) HANGUL SYLLABLE NWEOBS
+B233;B233;1102 116F 11BA;B233;1102 116F 11BA; # (눳; 눳; 눳; 눳; 눳; ) HANGUL SYLLABLE NWEOS
+B234;B234;1102 116F 11BB;B234;1102 116F 11BB; # (눴; 눴; 눴; 눴; 눴; ) HANGUL SYLLABLE NWEOSS
+B235;B235;1102 116F 11BC;B235;1102 116F 11BC; # (눵; 눵; 눵; 눵; 눵; ) HANGUL SYLLABLE NWEONG
+B236;B236;1102 116F 11BD;B236;1102 116F 11BD; # (눶; 눶; 눶; 눶; 눶; ) HANGUL SYLLABLE NWEOJ
+B237;B237;1102 116F 11BE;B237;1102 116F 11BE; # (눷; 눷; 눷; 눷; 눷; ) HANGUL SYLLABLE NWEOC
+B238;B238;1102 116F 11BF;B238;1102 116F 11BF; # (눸; 눸; 눸; 눸; 눸; ) HANGUL SYLLABLE NWEOK
+B239;B239;1102 116F 11C0;B239;1102 116F 11C0; # (눹; 눹; 눹; 눹; 눹; ) HANGUL SYLLABLE NWEOT
+B23A;B23A;1102 116F 11C1;B23A;1102 116F 11C1; # (눺; 눺; á„‚á…¯á‡; 눺; á„‚á…¯á‡; ) HANGUL SYLLABLE NWEOP
+B23B;B23B;1102 116F 11C2;B23B;1102 116F 11C2; # (눻; 눻; 눻; 눻; 눻; ) HANGUL SYLLABLE NWEOH
+B23C;B23C;1102 1170;B23C;1102 1170; # (눼; 눼; 눼; 눼; 눼; ) HANGUL SYLLABLE NWE
+B23D;B23D;1102 1170 11A8;B23D;1102 1170 11A8; # (눽; 눽; 눽; 눽; 눽; ) HANGUL SYLLABLE NWEG
+B23E;B23E;1102 1170 11A9;B23E;1102 1170 11A9; # (눾; 눾; 눾; 눾; 눾; ) HANGUL SYLLABLE NWEGG
+B23F;B23F;1102 1170 11AA;B23F;1102 1170 11AA; # (눿; 눿; 눿; 눿; 눿; ) HANGUL SYLLABLE NWEGS
+B240;B240;1102 1170 11AB;B240;1102 1170 11AB; # (뉀; 뉀; 뉀; 뉀; 뉀; ) HANGUL SYLLABLE NWEN
+B241;B241;1102 1170 11AC;B241;1102 1170 11AC; # (ë‰; ë‰; 뉁; ë‰; 뉁; ) HANGUL SYLLABLE NWENJ
+B242;B242;1102 1170 11AD;B242;1102 1170 11AD; # (뉂; 뉂; 뉂; 뉂; 뉂; ) HANGUL SYLLABLE NWENH
+B243;B243;1102 1170 11AE;B243;1102 1170 11AE; # (뉃; 뉃; 뉃; 뉃; 뉃; ) HANGUL SYLLABLE NWED
+B244;B244;1102 1170 11AF;B244;1102 1170 11AF; # (뉄; 뉄; 뉄; 뉄; 뉄; ) HANGUL SYLLABLE NWEL
+B245;B245;1102 1170 11B0;B245;1102 1170 11B0; # (뉅; 뉅; 뉅; 뉅; 뉅; ) HANGUL SYLLABLE NWELG
+B246;B246;1102 1170 11B1;B246;1102 1170 11B1; # (뉆; 뉆; 뉆; 뉆; 뉆; ) HANGUL SYLLABLE NWELM
+B247;B247;1102 1170 11B2;B247;1102 1170 11B2; # (뉇; 뉇; 뉇; 뉇; 뉇; ) HANGUL SYLLABLE NWELB
+B248;B248;1102 1170 11B3;B248;1102 1170 11B3; # (뉈; 뉈; 뉈; 뉈; 뉈; ) HANGUL SYLLABLE NWELS
+B249;B249;1102 1170 11B4;B249;1102 1170 11B4; # (뉉; 뉉; 뉉; 뉉; 뉉; ) HANGUL SYLLABLE NWELT
+B24A;B24A;1102 1170 11B5;B24A;1102 1170 11B5; # (뉊; 뉊; 뉊; 뉊; 뉊; ) HANGUL SYLLABLE NWELP
+B24B;B24B;1102 1170 11B6;B24B;1102 1170 11B6; # (뉋; 뉋; 뉋; 뉋; 뉋; ) HANGUL SYLLABLE NWELH
+B24C;B24C;1102 1170 11B7;B24C;1102 1170 11B7; # (뉌; 뉌; 뉌; 뉌; 뉌; ) HANGUL SYLLABLE NWEM
+B24D;B24D;1102 1170 11B8;B24D;1102 1170 11B8; # (ë‰; ë‰; 뉍; ë‰; 뉍; ) HANGUL SYLLABLE NWEB
+B24E;B24E;1102 1170 11B9;B24E;1102 1170 11B9; # (뉎; 뉎; 뉎; 뉎; 뉎; ) HANGUL SYLLABLE NWEBS
+B24F;B24F;1102 1170 11BA;B24F;1102 1170 11BA; # (ë‰; ë‰; 뉏; ë‰; 뉏; ) HANGUL SYLLABLE NWES
+B250;B250;1102 1170 11BB;B250;1102 1170 11BB; # (ë‰; ë‰; 뉐; ë‰; 뉐; ) HANGUL SYLLABLE NWESS
+B251;B251;1102 1170 11BC;B251;1102 1170 11BC; # (뉑; 뉑; 뉑; 뉑; 뉑; ) HANGUL SYLLABLE NWENG
+B252;B252;1102 1170 11BD;B252;1102 1170 11BD; # (뉒; 뉒; 뉒; 뉒; 뉒; ) HANGUL SYLLABLE NWEJ
+B253;B253;1102 1170 11BE;B253;1102 1170 11BE; # (뉓; 뉓; 뉓; 뉓; 뉓; ) HANGUL SYLLABLE NWEC
+B254;B254;1102 1170 11BF;B254;1102 1170 11BF; # (뉔; 뉔; 뉔; 뉔; 뉔; ) HANGUL SYLLABLE NWEK
+B255;B255;1102 1170 11C0;B255;1102 1170 11C0; # (뉕; 뉕; 뉕; 뉕; 뉕; ) HANGUL SYLLABLE NWET
+B256;B256;1102 1170 11C1;B256;1102 1170 11C1; # (뉖; 뉖; á„‚á…°á‡; 뉖; á„‚á…°á‡; ) HANGUL SYLLABLE NWEP
+B257;B257;1102 1170 11C2;B257;1102 1170 11C2; # (뉗; 뉗; 뉗; 뉗; 뉗; ) HANGUL SYLLABLE NWEH
+B258;B258;1102 1171;B258;1102 1171; # (뉘; 뉘; 뉘; 뉘; 뉘; ) HANGUL SYLLABLE NWI
+B259;B259;1102 1171 11A8;B259;1102 1171 11A8; # (뉙; 뉙; 뉙; 뉙; 뉙; ) HANGUL SYLLABLE NWIG
+B25A;B25A;1102 1171 11A9;B25A;1102 1171 11A9; # (뉚; 뉚; 뉚; 뉚; 뉚; ) HANGUL SYLLABLE NWIGG
+B25B;B25B;1102 1171 11AA;B25B;1102 1171 11AA; # (뉛; 뉛; 뉛; 뉛; 뉛; ) HANGUL SYLLABLE NWIGS
+B25C;B25C;1102 1171 11AB;B25C;1102 1171 11AB; # (뉜; 뉜; 뉜; 뉜; 뉜; ) HANGUL SYLLABLE NWIN
+B25D;B25D;1102 1171 11AC;B25D;1102 1171 11AC; # (ë‰; ë‰; 뉝; ë‰; 뉝; ) HANGUL SYLLABLE NWINJ
+B25E;B25E;1102 1171 11AD;B25E;1102 1171 11AD; # (뉞; 뉞; 뉞; 뉞; 뉞; ) HANGUL SYLLABLE NWINH
+B25F;B25F;1102 1171 11AE;B25F;1102 1171 11AE; # (뉟; 뉟; 뉟; 뉟; 뉟; ) HANGUL SYLLABLE NWID
+B260;B260;1102 1171 11AF;B260;1102 1171 11AF; # (뉠; 뉠; 뉠; 뉠; 뉠; ) HANGUL SYLLABLE NWIL
+B261;B261;1102 1171 11B0;B261;1102 1171 11B0; # (뉡; 뉡; 뉡; 뉡; 뉡; ) HANGUL SYLLABLE NWILG
+B262;B262;1102 1171 11B1;B262;1102 1171 11B1; # (뉢; 뉢; 뉢; 뉢; 뉢; ) HANGUL SYLLABLE NWILM
+B263;B263;1102 1171 11B2;B263;1102 1171 11B2; # (뉣; 뉣; 뉣; 뉣; 뉣; ) HANGUL SYLLABLE NWILB
+B264;B264;1102 1171 11B3;B264;1102 1171 11B3; # (뉤; 뉤; 뉤; 뉤; 뉤; ) HANGUL SYLLABLE NWILS
+B265;B265;1102 1171 11B4;B265;1102 1171 11B4; # (뉥; 뉥; 뉥; 뉥; 뉥; ) HANGUL SYLLABLE NWILT
+B266;B266;1102 1171 11B5;B266;1102 1171 11B5; # (뉦; 뉦; 뉦; 뉦; 뉦; ) HANGUL SYLLABLE NWILP
+B267;B267;1102 1171 11B6;B267;1102 1171 11B6; # (뉧; 뉧; 뉧; 뉧; 뉧; ) HANGUL SYLLABLE NWILH
+B268;B268;1102 1171 11B7;B268;1102 1171 11B7; # (뉨; 뉨; 뉨; 뉨; 뉨; ) HANGUL SYLLABLE NWIM
+B269;B269;1102 1171 11B8;B269;1102 1171 11B8; # (뉩; 뉩; 뉩; 뉩; 뉩; ) HANGUL SYLLABLE NWIB
+B26A;B26A;1102 1171 11B9;B26A;1102 1171 11B9; # (뉪; 뉪; 뉪; 뉪; 뉪; ) HANGUL SYLLABLE NWIBS
+B26B;B26B;1102 1171 11BA;B26B;1102 1171 11BA; # (뉫; 뉫; 뉫; 뉫; 뉫; ) HANGUL SYLLABLE NWIS
+B26C;B26C;1102 1171 11BB;B26C;1102 1171 11BB; # (뉬; 뉬; 뉬; 뉬; 뉬; ) HANGUL SYLLABLE NWISS
+B26D;B26D;1102 1171 11BC;B26D;1102 1171 11BC; # (뉭; 뉭; 뉭; 뉭; 뉭; ) HANGUL SYLLABLE NWING
+B26E;B26E;1102 1171 11BD;B26E;1102 1171 11BD; # (뉮; 뉮; 뉮; 뉮; 뉮; ) HANGUL SYLLABLE NWIJ
+B26F;B26F;1102 1171 11BE;B26F;1102 1171 11BE; # (뉯; 뉯; 뉯; 뉯; 뉯; ) HANGUL SYLLABLE NWIC
+B270;B270;1102 1171 11BF;B270;1102 1171 11BF; # (뉰; 뉰; 뉰; 뉰; 뉰; ) HANGUL SYLLABLE NWIK
+B271;B271;1102 1171 11C0;B271;1102 1171 11C0; # (뉱; 뉱; 뉱; 뉱; 뉱; ) HANGUL SYLLABLE NWIT
+B272;B272;1102 1171 11C1;B272;1102 1171 11C1; # (뉲; 뉲; á„‚á…±á‡; 뉲; á„‚á…±á‡; ) HANGUL SYLLABLE NWIP
+B273;B273;1102 1171 11C2;B273;1102 1171 11C2; # (뉳; 뉳; 뉳; 뉳; 뉳; ) HANGUL SYLLABLE NWIH
+B274;B274;1102 1172;B274;1102 1172; # (뉴; 뉴; 뉴; 뉴; 뉴; ) HANGUL SYLLABLE NYU
+B275;B275;1102 1172 11A8;B275;1102 1172 11A8; # (뉵; 뉵; 뉵; 뉵; 뉵; ) HANGUL SYLLABLE NYUG
+B276;B276;1102 1172 11A9;B276;1102 1172 11A9; # (뉶; 뉶; 뉶; 뉶; 뉶; ) HANGUL SYLLABLE NYUGG
+B277;B277;1102 1172 11AA;B277;1102 1172 11AA; # (뉷; 뉷; 뉷; 뉷; 뉷; ) HANGUL SYLLABLE NYUGS
+B278;B278;1102 1172 11AB;B278;1102 1172 11AB; # (뉸; 뉸; 뉸; 뉸; 뉸; ) HANGUL SYLLABLE NYUN
+B279;B279;1102 1172 11AC;B279;1102 1172 11AC; # (뉹; 뉹; 뉹; 뉹; 뉹; ) HANGUL SYLLABLE NYUNJ
+B27A;B27A;1102 1172 11AD;B27A;1102 1172 11AD; # (뉺; 뉺; 뉺; 뉺; 뉺; ) HANGUL SYLLABLE NYUNH
+B27B;B27B;1102 1172 11AE;B27B;1102 1172 11AE; # (뉻; 뉻; 뉻; 뉻; 뉻; ) HANGUL SYLLABLE NYUD
+B27C;B27C;1102 1172 11AF;B27C;1102 1172 11AF; # (뉼; 뉼; 뉼; 뉼; 뉼; ) HANGUL SYLLABLE NYUL
+B27D;B27D;1102 1172 11B0;B27D;1102 1172 11B0; # (뉽; 뉽; 뉽; 뉽; 뉽; ) HANGUL SYLLABLE NYULG
+B27E;B27E;1102 1172 11B1;B27E;1102 1172 11B1; # (뉾; 뉾; 뉾; 뉾; 뉾; ) HANGUL SYLLABLE NYULM
+B27F;B27F;1102 1172 11B2;B27F;1102 1172 11B2; # (뉿; 뉿; 뉿; 뉿; 뉿; ) HANGUL SYLLABLE NYULB
+B280;B280;1102 1172 11B3;B280;1102 1172 11B3; # (늀; 늀; 늀; 늀; 늀; ) HANGUL SYLLABLE NYULS
+B281;B281;1102 1172 11B4;B281;1102 1172 11B4; # (ëŠ; ëŠ; 늁; ëŠ; 늁; ) HANGUL SYLLABLE NYULT
+B282;B282;1102 1172 11B5;B282;1102 1172 11B5; # (늂; 늂; 늂; 늂; 늂; ) HANGUL SYLLABLE NYULP
+B283;B283;1102 1172 11B6;B283;1102 1172 11B6; # (늃; 늃; 늃; 늃; 늃; ) HANGUL SYLLABLE NYULH
+B284;B284;1102 1172 11B7;B284;1102 1172 11B7; # (늄; 늄; 늄; 늄; 늄; ) HANGUL SYLLABLE NYUM
+B285;B285;1102 1172 11B8;B285;1102 1172 11B8; # (늅; 늅; 늅; 늅; 늅; ) HANGUL SYLLABLE NYUB
+B286;B286;1102 1172 11B9;B286;1102 1172 11B9; # (늆; 늆; 늆; 늆; 늆; ) HANGUL SYLLABLE NYUBS
+B287;B287;1102 1172 11BA;B287;1102 1172 11BA; # (늇; 늇; 늇; 늇; 늇; ) HANGUL SYLLABLE NYUS
+B288;B288;1102 1172 11BB;B288;1102 1172 11BB; # (늈; 늈; 늈; 늈; 늈; ) HANGUL SYLLABLE NYUSS
+B289;B289;1102 1172 11BC;B289;1102 1172 11BC; # (늉; 늉; 늉; 늉; 늉; ) HANGUL SYLLABLE NYUNG
+B28A;B28A;1102 1172 11BD;B28A;1102 1172 11BD; # (늊; 늊; 늊; 늊; 늊; ) HANGUL SYLLABLE NYUJ
+B28B;B28B;1102 1172 11BE;B28B;1102 1172 11BE; # (늋; 늋; 늋; 늋; 늋; ) HANGUL SYLLABLE NYUC
+B28C;B28C;1102 1172 11BF;B28C;1102 1172 11BF; # (늌; 늌; 늌; 늌; 늌; ) HANGUL SYLLABLE NYUK
+B28D;B28D;1102 1172 11C0;B28D;1102 1172 11C0; # (ëŠ; ëŠ; 늍; ëŠ; 늍; ) HANGUL SYLLABLE NYUT
+B28E;B28E;1102 1172 11C1;B28E;1102 1172 11C1; # (늎; 늎; á„‚á…²á‡; 늎; á„‚á…²á‡; ) HANGUL SYLLABLE NYUP
+B28F;B28F;1102 1172 11C2;B28F;1102 1172 11C2; # (ëŠ; ëŠ; 늏; ëŠ; 늏; ) HANGUL SYLLABLE NYUH
+B290;B290;1102 1173;B290;1102 1173; # (ëŠ; ëŠ; á„‚á…³; ëŠ; á„‚á…³; ) HANGUL SYLLABLE NEU
+B291;B291;1102 1173 11A8;B291;1102 1173 11A8; # (늑; 늑; 늑; 늑; 늑; ) HANGUL SYLLABLE NEUG
+B292;B292;1102 1173 11A9;B292;1102 1173 11A9; # (늒; 늒; 늒; 늒; 늒; ) HANGUL SYLLABLE NEUGG
+B293;B293;1102 1173 11AA;B293;1102 1173 11AA; # (늓; 늓; 늓; 늓; 늓; ) HANGUL SYLLABLE NEUGS
+B294;B294;1102 1173 11AB;B294;1102 1173 11AB; # (는; 는; 는; 는; 는; ) HANGUL SYLLABLE NEUN
+B295;B295;1102 1173 11AC;B295;1102 1173 11AC; # (늕; 늕; 늕; 늕; 늕; ) HANGUL SYLLABLE NEUNJ
+B296;B296;1102 1173 11AD;B296;1102 1173 11AD; # (늖; 늖; 늖; 늖; 늖; ) HANGUL SYLLABLE NEUNH
+B297;B297;1102 1173 11AE;B297;1102 1173 11AE; # (늗; 늗; 늗; 늗; 늗; ) HANGUL SYLLABLE NEUD
+B298;B298;1102 1173 11AF;B298;1102 1173 11AF; # (늘; 늘; 늘; 늘; 늘; ) HANGUL SYLLABLE NEUL
+B299;B299;1102 1173 11B0;B299;1102 1173 11B0; # (늙; 늙; 늙; 늙; 늙; ) HANGUL SYLLABLE NEULG
+B29A;B29A;1102 1173 11B1;B29A;1102 1173 11B1; # (늚; 늚; 늚; 늚; 늚; ) HANGUL SYLLABLE NEULM
+B29B;B29B;1102 1173 11B2;B29B;1102 1173 11B2; # (늛; 늛; 늛; 늛; 늛; ) HANGUL SYLLABLE NEULB
+B29C;B29C;1102 1173 11B3;B29C;1102 1173 11B3; # (늜; 늜; 늜; 늜; 늜; ) HANGUL SYLLABLE NEULS
+B29D;B29D;1102 1173 11B4;B29D;1102 1173 11B4; # (ëŠ; ëŠ; 늝; ëŠ; 늝; ) HANGUL SYLLABLE NEULT
+B29E;B29E;1102 1173 11B5;B29E;1102 1173 11B5; # (늞; 늞; 늞; 늞; 늞; ) HANGUL SYLLABLE NEULP
+B29F;B29F;1102 1173 11B6;B29F;1102 1173 11B6; # (늟; 늟; 늟; 늟; 늟; ) HANGUL SYLLABLE NEULH
+B2A0;B2A0;1102 1173 11B7;B2A0;1102 1173 11B7; # (늠; 늠; 늠; 늠; 늠; ) HANGUL SYLLABLE NEUM
+B2A1;B2A1;1102 1173 11B8;B2A1;1102 1173 11B8; # (늡; 늡; 늡; 늡; 늡; ) HANGUL SYLLABLE NEUB
+B2A2;B2A2;1102 1173 11B9;B2A2;1102 1173 11B9; # (늢; 늢; 늢; 늢; 늢; ) HANGUL SYLLABLE NEUBS
+B2A3;B2A3;1102 1173 11BA;B2A3;1102 1173 11BA; # (늣; 늣; 늣; 늣; 늣; ) HANGUL SYLLABLE NEUS
+B2A4;B2A4;1102 1173 11BB;B2A4;1102 1173 11BB; # (늤; 늤; 늤; 늤; 늤; ) HANGUL SYLLABLE NEUSS
+B2A5;B2A5;1102 1173 11BC;B2A5;1102 1173 11BC; # (능; 능; 능; 능; 능; ) HANGUL SYLLABLE NEUNG
+B2A6;B2A6;1102 1173 11BD;B2A6;1102 1173 11BD; # (늦; 늦; 늦; 늦; 늦; ) HANGUL SYLLABLE NEUJ
+B2A7;B2A7;1102 1173 11BE;B2A7;1102 1173 11BE; # (늧; 늧; 늧; 늧; 늧; ) HANGUL SYLLABLE NEUC
+B2A8;B2A8;1102 1173 11BF;B2A8;1102 1173 11BF; # (늨; 늨; 늨; 늨; 늨; ) HANGUL SYLLABLE NEUK
+B2A9;B2A9;1102 1173 11C0;B2A9;1102 1173 11C0; # (늩; 늩; 늩; 늩; 늩; ) HANGUL SYLLABLE NEUT
+B2AA;B2AA;1102 1173 11C1;B2AA;1102 1173 11C1; # (늪; 늪; á„‚á…³á‡; 늪; á„‚á…³á‡; ) HANGUL SYLLABLE NEUP
+B2AB;B2AB;1102 1173 11C2;B2AB;1102 1173 11C2; # (늫; 늫; 늫; 늫; 늫; ) HANGUL SYLLABLE NEUH
+B2AC;B2AC;1102 1174;B2AC;1102 1174; # (늬; 늬; 늬; 늬; 늬; ) HANGUL SYLLABLE NYI
+B2AD;B2AD;1102 1174 11A8;B2AD;1102 1174 11A8; # (늭; 늭; 늭; 늭; 늭; ) HANGUL SYLLABLE NYIG
+B2AE;B2AE;1102 1174 11A9;B2AE;1102 1174 11A9; # (늮; 늮; 늮; 늮; 늮; ) HANGUL SYLLABLE NYIGG
+B2AF;B2AF;1102 1174 11AA;B2AF;1102 1174 11AA; # (늯; 늯; 늯; 늯; 늯; ) HANGUL SYLLABLE NYIGS
+B2B0;B2B0;1102 1174 11AB;B2B0;1102 1174 11AB; # (늰; 늰; 늰; 늰; 늰; ) HANGUL SYLLABLE NYIN
+B2B1;B2B1;1102 1174 11AC;B2B1;1102 1174 11AC; # (늱; 늱; 늱; 늱; 늱; ) HANGUL SYLLABLE NYINJ
+B2B2;B2B2;1102 1174 11AD;B2B2;1102 1174 11AD; # (늲; 늲; 늲; 늲; 늲; ) HANGUL SYLLABLE NYINH
+B2B3;B2B3;1102 1174 11AE;B2B3;1102 1174 11AE; # (늳; 늳; 늳; 늳; 늳; ) HANGUL SYLLABLE NYID
+B2B4;B2B4;1102 1174 11AF;B2B4;1102 1174 11AF; # (늴; 늴; 늴; 늴; 늴; ) HANGUL SYLLABLE NYIL
+B2B5;B2B5;1102 1174 11B0;B2B5;1102 1174 11B0; # (늵; 늵; 늵; 늵; 늵; ) HANGUL SYLLABLE NYILG
+B2B6;B2B6;1102 1174 11B1;B2B6;1102 1174 11B1; # (늶; 늶; 늶; 늶; 늶; ) HANGUL SYLLABLE NYILM
+B2B7;B2B7;1102 1174 11B2;B2B7;1102 1174 11B2; # (늷; 늷; 늷; 늷; 늷; ) HANGUL SYLLABLE NYILB
+B2B8;B2B8;1102 1174 11B3;B2B8;1102 1174 11B3; # (늸; 늸; 늸; 늸; 늸; ) HANGUL SYLLABLE NYILS
+B2B9;B2B9;1102 1174 11B4;B2B9;1102 1174 11B4; # (늹; 늹; 늹; 늹; 늹; ) HANGUL SYLLABLE NYILT
+B2BA;B2BA;1102 1174 11B5;B2BA;1102 1174 11B5; # (늺; 늺; 늺; 늺; 늺; ) HANGUL SYLLABLE NYILP
+B2BB;B2BB;1102 1174 11B6;B2BB;1102 1174 11B6; # (늻; 늻; 늻; 늻; 늻; ) HANGUL SYLLABLE NYILH
+B2BC;B2BC;1102 1174 11B7;B2BC;1102 1174 11B7; # (늼; 늼; 늼; 늼; 늼; ) HANGUL SYLLABLE NYIM
+B2BD;B2BD;1102 1174 11B8;B2BD;1102 1174 11B8; # (늽; 늽; 늽; 늽; 늽; ) HANGUL SYLLABLE NYIB
+B2BE;B2BE;1102 1174 11B9;B2BE;1102 1174 11B9; # (늾; 늾; 늾; 늾; 늾; ) HANGUL SYLLABLE NYIBS
+B2BF;B2BF;1102 1174 11BA;B2BF;1102 1174 11BA; # (늿; 늿; 늿; 늿; 늿; ) HANGUL SYLLABLE NYIS
+B2C0;B2C0;1102 1174 11BB;B2C0;1102 1174 11BB; # (닀; 닀; 닀; 닀; 닀; ) HANGUL SYLLABLE NYISS
+B2C1;B2C1;1102 1174 11BC;B2C1;1102 1174 11BC; # (ë‹; ë‹; 닁; ë‹; 닁; ) HANGUL SYLLABLE NYING
+B2C2;B2C2;1102 1174 11BD;B2C2;1102 1174 11BD; # (닂; 닂; 닂; 닂; 닂; ) HANGUL SYLLABLE NYIJ
+B2C3;B2C3;1102 1174 11BE;B2C3;1102 1174 11BE; # (닃; 닃; 닃; 닃; 닃; ) HANGUL SYLLABLE NYIC
+B2C4;B2C4;1102 1174 11BF;B2C4;1102 1174 11BF; # (닄; 닄; 닄; 닄; 닄; ) HANGUL SYLLABLE NYIK
+B2C5;B2C5;1102 1174 11C0;B2C5;1102 1174 11C0; # (닅; 닅; 닅; 닅; 닅; ) HANGUL SYLLABLE NYIT
+B2C6;B2C6;1102 1174 11C1;B2C6;1102 1174 11C1; # (닆; 닆; á„‚á…´á‡; 닆; á„‚á…´á‡; ) HANGUL SYLLABLE NYIP
+B2C7;B2C7;1102 1174 11C2;B2C7;1102 1174 11C2; # (닇; 닇; 닇; 닇; 닇; ) HANGUL SYLLABLE NYIH
+B2C8;B2C8;1102 1175;B2C8;1102 1175; # (니; 니; 니; 니; 니; ) HANGUL SYLLABLE NI
+B2C9;B2C9;1102 1175 11A8;B2C9;1102 1175 11A8; # (닉; 닉; 닉; 닉; 닉; ) HANGUL SYLLABLE NIG
+B2CA;B2CA;1102 1175 11A9;B2CA;1102 1175 11A9; # (닊; 닊; 닊; 닊; 닊; ) HANGUL SYLLABLE NIGG
+B2CB;B2CB;1102 1175 11AA;B2CB;1102 1175 11AA; # (닋; 닋; 닋; 닋; 닋; ) HANGUL SYLLABLE NIGS
+B2CC;B2CC;1102 1175 11AB;B2CC;1102 1175 11AB; # (닌; 닌; 닌; 닌; 닌; ) HANGUL SYLLABLE NIN
+B2CD;B2CD;1102 1175 11AC;B2CD;1102 1175 11AC; # (ë‹; ë‹; 닍; ë‹; 닍; ) HANGUL SYLLABLE NINJ
+B2CE;B2CE;1102 1175 11AD;B2CE;1102 1175 11AD; # (닎; 닎; 닎; 닎; 닎; ) HANGUL SYLLABLE NINH
+B2CF;B2CF;1102 1175 11AE;B2CF;1102 1175 11AE; # (ë‹; ë‹; 닏; ë‹; 닏; ) HANGUL SYLLABLE NID
+B2D0;B2D0;1102 1175 11AF;B2D0;1102 1175 11AF; # (ë‹; ë‹; 닐; ë‹; 닐; ) HANGUL SYLLABLE NIL
+B2D1;B2D1;1102 1175 11B0;B2D1;1102 1175 11B0; # (닑; 닑; 닑; 닑; 닑; ) HANGUL SYLLABLE NILG
+B2D2;B2D2;1102 1175 11B1;B2D2;1102 1175 11B1; # (닒; 닒; 닒; 닒; 닒; ) HANGUL SYLLABLE NILM
+B2D3;B2D3;1102 1175 11B2;B2D3;1102 1175 11B2; # (닓; 닓; 닓; 닓; 닓; ) HANGUL SYLLABLE NILB
+B2D4;B2D4;1102 1175 11B3;B2D4;1102 1175 11B3; # (닔; 닔; 닔; 닔; 닔; ) HANGUL SYLLABLE NILS
+B2D5;B2D5;1102 1175 11B4;B2D5;1102 1175 11B4; # (닕; 닕; 닕; 닕; 닕; ) HANGUL SYLLABLE NILT
+B2D6;B2D6;1102 1175 11B5;B2D6;1102 1175 11B5; # (닖; 닖; 닖; 닖; 닖; ) HANGUL SYLLABLE NILP
+B2D7;B2D7;1102 1175 11B6;B2D7;1102 1175 11B6; # (닗; 닗; 닗; 닗; 닗; ) HANGUL SYLLABLE NILH
+B2D8;B2D8;1102 1175 11B7;B2D8;1102 1175 11B7; # (님; 님; 님; 님; 님; ) HANGUL SYLLABLE NIM
+B2D9;B2D9;1102 1175 11B8;B2D9;1102 1175 11B8; # (닙; 닙; 닙; 닙; 닙; ) HANGUL SYLLABLE NIB
+B2DA;B2DA;1102 1175 11B9;B2DA;1102 1175 11B9; # (닚; 닚; 닚; 닚; 닚; ) HANGUL SYLLABLE NIBS
+B2DB;B2DB;1102 1175 11BA;B2DB;1102 1175 11BA; # (닛; 닛; 닛; 닛; 닛; ) HANGUL SYLLABLE NIS
+B2DC;B2DC;1102 1175 11BB;B2DC;1102 1175 11BB; # (닜; 닜; 닜; 닜; 닜; ) HANGUL SYLLABLE NISS
+B2DD;B2DD;1102 1175 11BC;B2DD;1102 1175 11BC; # (ë‹; ë‹; 닝; ë‹; 닝; ) HANGUL SYLLABLE NING
+B2DE;B2DE;1102 1175 11BD;B2DE;1102 1175 11BD; # (닞; 닞; 닞; 닞; 닞; ) HANGUL SYLLABLE NIJ
+B2DF;B2DF;1102 1175 11BE;B2DF;1102 1175 11BE; # (닟; 닟; 닟; 닟; 닟; ) HANGUL SYLLABLE NIC
+B2E0;B2E0;1102 1175 11BF;B2E0;1102 1175 11BF; # (닠; 닠; 닠; 닠; 닠; ) HANGUL SYLLABLE NIK
+B2E1;B2E1;1102 1175 11C0;B2E1;1102 1175 11C0; # (닡; 닡; 닡; 닡; 닡; ) HANGUL SYLLABLE NIT
+B2E2;B2E2;1102 1175 11C1;B2E2;1102 1175 11C1; # (ë‹¢; ë‹¢; á„‚á…µá‡; ë‹¢; á„‚á…µá‡; ) HANGUL SYLLABLE NIP
+B2E3;B2E3;1102 1175 11C2;B2E3;1102 1175 11C2; # (닣; 닣; 닣; 닣; 닣; ) HANGUL SYLLABLE NIH
+B2E4;B2E4;1103 1161;B2E4;1103 1161; # (다; 다; 다; 다; 다; ) HANGUL SYLLABLE DA
+B2E5;B2E5;1103 1161 11A8;B2E5;1103 1161 11A8; # (닥; 닥; 닥; 닥; 닥; ) HANGUL SYLLABLE DAG
+B2E6;B2E6;1103 1161 11A9;B2E6;1103 1161 11A9; # (닦; 닦; 닦; 닦; 닦; ) HANGUL SYLLABLE DAGG
+B2E7;B2E7;1103 1161 11AA;B2E7;1103 1161 11AA; # (닧; 닧; 닧; 닧; 닧; ) HANGUL SYLLABLE DAGS
+B2E8;B2E8;1103 1161 11AB;B2E8;1103 1161 11AB; # (단; 단; 단; 단; 단; ) HANGUL SYLLABLE DAN
+B2E9;B2E9;1103 1161 11AC;B2E9;1103 1161 11AC; # (닩; 닩; 닩; 닩; 닩; ) HANGUL SYLLABLE DANJ
+B2EA;B2EA;1103 1161 11AD;B2EA;1103 1161 11AD; # (닪; 닪; 닪; 닪; 닪; ) HANGUL SYLLABLE DANH
+B2EB;B2EB;1103 1161 11AE;B2EB;1103 1161 11AE; # (닫; 닫; 닫; 닫; 닫; ) HANGUL SYLLABLE DAD
+B2EC;B2EC;1103 1161 11AF;B2EC;1103 1161 11AF; # (달; 달; 달; 달; 달; ) HANGUL SYLLABLE DAL
+B2ED;B2ED;1103 1161 11B0;B2ED;1103 1161 11B0; # (닭; 닭; 닭; 닭; 닭; ) HANGUL SYLLABLE DALG
+B2EE;B2EE;1103 1161 11B1;B2EE;1103 1161 11B1; # (닮; 닮; 닮; 닮; 닮; ) HANGUL SYLLABLE DALM
+B2EF;B2EF;1103 1161 11B2;B2EF;1103 1161 11B2; # (닯; 닯; 닯; 닯; 닯; ) HANGUL SYLLABLE DALB
+B2F0;B2F0;1103 1161 11B3;B2F0;1103 1161 11B3; # (닰; 닰; 닰; 닰; 닰; ) HANGUL SYLLABLE DALS
+B2F1;B2F1;1103 1161 11B4;B2F1;1103 1161 11B4; # (닱; 닱; 닱; 닱; 닱; ) HANGUL SYLLABLE DALT
+B2F2;B2F2;1103 1161 11B5;B2F2;1103 1161 11B5; # (닲; 닲; 닲; 닲; 닲; ) HANGUL SYLLABLE DALP
+B2F3;B2F3;1103 1161 11B6;B2F3;1103 1161 11B6; # (닳; 닳; 닳; 닳; 닳; ) HANGUL SYLLABLE DALH
+B2F4;B2F4;1103 1161 11B7;B2F4;1103 1161 11B7; # (담; 담; 담; 담; 담; ) HANGUL SYLLABLE DAM
+B2F5;B2F5;1103 1161 11B8;B2F5;1103 1161 11B8; # (답; 답; 답; 답; 답; ) HANGUL SYLLABLE DAB
+B2F6;B2F6;1103 1161 11B9;B2F6;1103 1161 11B9; # (닶; 닶; 닶; 닶; 닶; ) HANGUL SYLLABLE DABS
+B2F7;B2F7;1103 1161 11BA;B2F7;1103 1161 11BA; # (닷; 닷; 닷; 닷; 닷; ) HANGUL SYLLABLE DAS
+B2F8;B2F8;1103 1161 11BB;B2F8;1103 1161 11BB; # (닸; 닸; 닸; 닸; 닸; ) HANGUL SYLLABLE DASS
+B2F9;B2F9;1103 1161 11BC;B2F9;1103 1161 11BC; # (당; 당; 당; 당; 당; ) HANGUL SYLLABLE DANG
+B2FA;B2FA;1103 1161 11BD;B2FA;1103 1161 11BD; # (닺; 닺; 닺; 닺; 닺; ) HANGUL SYLLABLE DAJ
+B2FB;B2FB;1103 1161 11BE;B2FB;1103 1161 11BE; # (닻; 닻; 닻; 닻; 닻; ) HANGUL SYLLABLE DAC
+B2FC;B2FC;1103 1161 11BF;B2FC;1103 1161 11BF; # (닼; 닼; 닼; 닼; 닼; ) HANGUL SYLLABLE DAK
+B2FD;B2FD;1103 1161 11C0;B2FD;1103 1161 11C0; # (닽; 닽; 닽; 닽; 닽; ) HANGUL SYLLABLE DAT
+B2FE;B2FE;1103 1161 11C1;B2FE;1103 1161 11C1; # (닾; 닾; 다á‡; 닾; 다á‡; ) HANGUL SYLLABLE DAP
+B2FF;B2FF;1103 1161 11C2;B2FF;1103 1161 11C2; # (닿; 닿; 닿; 닿; 닿; ) HANGUL SYLLABLE DAH
+B300;B300;1103 1162;B300;1103 1162; # (대; 대; 대; 대; 대; ) HANGUL SYLLABLE DAE
+B301;B301;1103 1162 11A8;B301;1103 1162 11A8; # (ëŒ; ëŒ; 댁; ëŒ; 댁; ) HANGUL SYLLABLE DAEG
+B302;B302;1103 1162 11A9;B302;1103 1162 11A9; # (댂; 댂; 댂; 댂; 댂; ) HANGUL SYLLABLE DAEGG
+B303;B303;1103 1162 11AA;B303;1103 1162 11AA; # (댃; 댃; 댃; 댃; 댃; ) HANGUL SYLLABLE DAEGS
+B304;B304;1103 1162 11AB;B304;1103 1162 11AB; # (댄; 댄; 댄; 댄; 댄; ) HANGUL SYLLABLE DAEN
+B305;B305;1103 1162 11AC;B305;1103 1162 11AC; # (댅; 댅; 댅; 댅; 댅; ) HANGUL SYLLABLE DAENJ
+B306;B306;1103 1162 11AD;B306;1103 1162 11AD; # (댆; 댆; 댆; 댆; 댆; ) HANGUL SYLLABLE DAENH
+B307;B307;1103 1162 11AE;B307;1103 1162 11AE; # (댇; 댇; 댇; 댇; 댇; ) HANGUL SYLLABLE DAED
+B308;B308;1103 1162 11AF;B308;1103 1162 11AF; # (댈; 댈; 댈; 댈; 댈; ) HANGUL SYLLABLE DAEL
+B309;B309;1103 1162 11B0;B309;1103 1162 11B0; # (댉; 댉; 댉; 댉; 댉; ) HANGUL SYLLABLE DAELG
+B30A;B30A;1103 1162 11B1;B30A;1103 1162 11B1; # (댊; 댊; 댊; 댊; 댊; ) HANGUL SYLLABLE DAELM
+B30B;B30B;1103 1162 11B2;B30B;1103 1162 11B2; # (댋; 댋; 댋; 댋; 댋; ) HANGUL SYLLABLE DAELB
+B30C;B30C;1103 1162 11B3;B30C;1103 1162 11B3; # (댌; 댌; 댌; 댌; 댌; ) HANGUL SYLLABLE DAELS
+B30D;B30D;1103 1162 11B4;B30D;1103 1162 11B4; # (ëŒ; ëŒ; 댍; ëŒ; 댍; ) HANGUL SYLLABLE DAELT
+B30E;B30E;1103 1162 11B5;B30E;1103 1162 11B5; # (댎; 댎; 댎; 댎; 댎; ) HANGUL SYLLABLE DAELP
+B30F;B30F;1103 1162 11B6;B30F;1103 1162 11B6; # (ëŒ; ëŒ; 댏; ëŒ; 댏; ) HANGUL SYLLABLE DAELH
+B310;B310;1103 1162 11B7;B310;1103 1162 11B7; # (ëŒ; ëŒ; 댐; ëŒ; 댐; ) HANGUL SYLLABLE DAEM
+B311;B311;1103 1162 11B8;B311;1103 1162 11B8; # (댑; 댑; 댑; 댑; 댑; ) HANGUL SYLLABLE DAEB
+B312;B312;1103 1162 11B9;B312;1103 1162 11B9; # (댒; 댒; 댒; 댒; 댒; ) HANGUL SYLLABLE DAEBS
+B313;B313;1103 1162 11BA;B313;1103 1162 11BA; # (댓; 댓; 댓; 댓; 댓; ) HANGUL SYLLABLE DAES
+B314;B314;1103 1162 11BB;B314;1103 1162 11BB; # (댔; 댔; 댔; 댔; 댔; ) HANGUL SYLLABLE DAESS
+B315;B315;1103 1162 11BC;B315;1103 1162 11BC; # (댕; 댕; 댕; 댕; 댕; ) HANGUL SYLLABLE DAENG
+B316;B316;1103 1162 11BD;B316;1103 1162 11BD; # (댖; 댖; 댖; 댖; 댖; ) HANGUL SYLLABLE DAEJ
+B317;B317;1103 1162 11BE;B317;1103 1162 11BE; # (댗; 댗; 댗; 댗; 댗; ) HANGUL SYLLABLE DAEC
+B318;B318;1103 1162 11BF;B318;1103 1162 11BF; # (댘; 댘; 댘; 댘; 댘; ) HANGUL SYLLABLE DAEK
+B319;B319;1103 1162 11C0;B319;1103 1162 11C0; # (댙; 댙; 댙; 댙; 댙; ) HANGUL SYLLABLE DAET
+B31A;B31A;1103 1162 11C1;B31A;1103 1162 11C1; # (댚; 댚; 대á‡; 댚; 대á‡; ) HANGUL SYLLABLE DAEP
+B31B;B31B;1103 1162 11C2;B31B;1103 1162 11C2; # (댛; 댛; 댛; 댛; 댛; ) HANGUL SYLLABLE DAEH
+B31C;B31C;1103 1163;B31C;1103 1163; # (댜; 댜; 댜; 댜; 댜; ) HANGUL SYLLABLE DYA
+B31D;B31D;1103 1163 11A8;B31D;1103 1163 11A8; # (ëŒ; ëŒ; 댝; ëŒ; 댝; ) HANGUL SYLLABLE DYAG
+B31E;B31E;1103 1163 11A9;B31E;1103 1163 11A9; # (댞; 댞; 댞; 댞; 댞; ) HANGUL SYLLABLE DYAGG
+B31F;B31F;1103 1163 11AA;B31F;1103 1163 11AA; # (댟; 댟; 댟; 댟; 댟; ) HANGUL SYLLABLE DYAGS
+B320;B320;1103 1163 11AB;B320;1103 1163 11AB; # (댠; 댠; 댠; 댠; 댠; ) HANGUL SYLLABLE DYAN
+B321;B321;1103 1163 11AC;B321;1103 1163 11AC; # (댡; 댡; 댡; 댡; 댡; ) HANGUL SYLLABLE DYANJ
+B322;B322;1103 1163 11AD;B322;1103 1163 11AD; # (댢; 댢; 댢; 댢; 댢; ) HANGUL SYLLABLE DYANH
+B323;B323;1103 1163 11AE;B323;1103 1163 11AE; # (댣; 댣; 댣; 댣; 댣; ) HANGUL SYLLABLE DYAD
+B324;B324;1103 1163 11AF;B324;1103 1163 11AF; # (댤; 댤; 댤; 댤; 댤; ) HANGUL SYLLABLE DYAL
+B325;B325;1103 1163 11B0;B325;1103 1163 11B0; # (댥; 댥; 댥; 댥; 댥; ) HANGUL SYLLABLE DYALG
+B326;B326;1103 1163 11B1;B326;1103 1163 11B1; # (댦; 댦; 댦; 댦; 댦; ) HANGUL SYLLABLE DYALM
+B327;B327;1103 1163 11B2;B327;1103 1163 11B2; # (댧; 댧; 댧; 댧; 댧; ) HANGUL SYLLABLE DYALB
+B328;B328;1103 1163 11B3;B328;1103 1163 11B3; # (댨; 댨; 댨; 댨; 댨; ) HANGUL SYLLABLE DYALS
+B329;B329;1103 1163 11B4;B329;1103 1163 11B4; # (댩; 댩; 댩; 댩; 댩; ) HANGUL SYLLABLE DYALT
+B32A;B32A;1103 1163 11B5;B32A;1103 1163 11B5; # (댪; 댪; 댪; 댪; 댪; ) HANGUL SYLLABLE DYALP
+B32B;B32B;1103 1163 11B6;B32B;1103 1163 11B6; # (댫; 댫; 댫; 댫; 댫; ) HANGUL SYLLABLE DYALH
+B32C;B32C;1103 1163 11B7;B32C;1103 1163 11B7; # (댬; 댬; 댬; 댬; 댬; ) HANGUL SYLLABLE DYAM
+B32D;B32D;1103 1163 11B8;B32D;1103 1163 11B8; # (댭; 댭; 댭; 댭; 댭; ) HANGUL SYLLABLE DYAB
+B32E;B32E;1103 1163 11B9;B32E;1103 1163 11B9; # (댮; 댮; 댮; 댮; 댮; ) HANGUL SYLLABLE DYABS
+B32F;B32F;1103 1163 11BA;B32F;1103 1163 11BA; # (댯; 댯; 댯; 댯; 댯; ) HANGUL SYLLABLE DYAS
+B330;B330;1103 1163 11BB;B330;1103 1163 11BB; # (댰; 댰; 댰; 댰; 댰; ) HANGUL SYLLABLE DYASS
+B331;B331;1103 1163 11BC;B331;1103 1163 11BC; # (댱; 댱; 댱; 댱; 댱; ) HANGUL SYLLABLE DYANG
+B332;B332;1103 1163 11BD;B332;1103 1163 11BD; # (댲; 댲; 댲; 댲; 댲; ) HANGUL SYLLABLE DYAJ
+B333;B333;1103 1163 11BE;B333;1103 1163 11BE; # (댳; 댳; 댳; 댳; 댳; ) HANGUL SYLLABLE DYAC
+B334;B334;1103 1163 11BF;B334;1103 1163 11BF; # (댴; 댴; 댴; 댴; 댴; ) HANGUL SYLLABLE DYAK
+B335;B335;1103 1163 11C0;B335;1103 1163 11C0; # (댵; 댵; 댵; 댵; 댵; ) HANGUL SYLLABLE DYAT
+B336;B336;1103 1163 11C1;B336;1103 1163 11C1; # (댶; 댶; 댜á‡; 댶; 댜á‡; ) HANGUL SYLLABLE DYAP
+B337;B337;1103 1163 11C2;B337;1103 1163 11C2; # (댷; 댷; 댷; 댷; 댷; ) HANGUL SYLLABLE DYAH
+B338;B338;1103 1164;B338;1103 1164; # (댸; 댸; 댸; 댸; 댸; ) HANGUL SYLLABLE DYAE
+B339;B339;1103 1164 11A8;B339;1103 1164 11A8; # (댹; 댹; 댹; 댹; 댹; ) HANGUL SYLLABLE DYAEG
+B33A;B33A;1103 1164 11A9;B33A;1103 1164 11A9; # (댺; 댺; 댺; 댺; 댺; ) HANGUL SYLLABLE DYAEGG
+B33B;B33B;1103 1164 11AA;B33B;1103 1164 11AA; # (댻; 댻; 댻; 댻; 댻; ) HANGUL SYLLABLE DYAEGS
+B33C;B33C;1103 1164 11AB;B33C;1103 1164 11AB; # (댼; 댼; 댼; 댼; 댼; ) HANGUL SYLLABLE DYAEN
+B33D;B33D;1103 1164 11AC;B33D;1103 1164 11AC; # (댽; 댽; 댽; 댽; 댽; ) HANGUL SYLLABLE DYAENJ
+B33E;B33E;1103 1164 11AD;B33E;1103 1164 11AD; # (댾; 댾; 댾; 댾; 댾; ) HANGUL SYLLABLE DYAENH
+B33F;B33F;1103 1164 11AE;B33F;1103 1164 11AE; # (댿; 댿; 댿; 댿; 댿; ) HANGUL SYLLABLE DYAED
+B340;B340;1103 1164 11AF;B340;1103 1164 11AF; # (ë€; ë€; 덀; ë€; 덀; ) HANGUL SYLLABLE DYAEL
+B341;B341;1103 1164 11B0;B341;1103 1164 11B0; # (ë; ë; 덁; ë; 덁; ) HANGUL SYLLABLE DYAELG
+B342;B342;1103 1164 11B1;B342;1103 1164 11B1; # (ë‚; ë‚; 덂; ë‚; 덂; ) HANGUL SYLLABLE DYAELM
+B343;B343;1103 1164 11B2;B343;1103 1164 11B2; # (ëƒ; ëƒ; 덃; ëƒ; 덃; ) HANGUL SYLLABLE DYAELB
+B344;B344;1103 1164 11B3;B344;1103 1164 11B3; # (ë„; ë„; 덄; ë„; 덄; ) HANGUL SYLLABLE DYAELS
+B345;B345;1103 1164 11B4;B345;1103 1164 11B4; # (ë…; ë…; 덅; ë…; 덅; ) HANGUL SYLLABLE DYAELT
+B346;B346;1103 1164 11B5;B346;1103 1164 11B5; # (ë†; ë†; 덆; ë†; 덆; ) HANGUL SYLLABLE DYAELP
+B347;B347;1103 1164 11B6;B347;1103 1164 11B6; # (ë‡; ë‡; 덇; ë‡; 덇; ) HANGUL SYLLABLE DYAELH
+B348;B348;1103 1164 11B7;B348;1103 1164 11B7; # (ëˆ; ëˆ; 덈; ëˆ; 덈; ) HANGUL SYLLABLE DYAEM
+B349;B349;1103 1164 11B8;B349;1103 1164 11B8; # (ë‰; ë‰; 덉; ë‰; 덉; ) HANGUL SYLLABLE DYAEB
+B34A;B34A;1103 1164 11B9;B34A;1103 1164 11B9; # (ëŠ; ëŠ; 덊; ëŠ; 덊; ) HANGUL SYLLABLE DYAEBS
+B34B;B34B;1103 1164 11BA;B34B;1103 1164 11BA; # (ë‹; ë‹; 덋; ë‹; 덋; ) HANGUL SYLLABLE DYAES
+B34C;B34C;1103 1164 11BB;B34C;1103 1164 11BB; # (ëŒ; ëŒ; 덌; ëŒ; 덌; ) HANGUL SYLLABLE DYAESS
+B34D;B34D;1103 1164 11BC;B34D;1103 1164 11BC; # (ë; ë; 덍; ë; 덍; ) HANGUL SYLLABLE DYAENG
+B34E;B34E;1103 1164 11BD;B34E;1103 1164 11BD; # (ëŽ; ëŽ; 덎; ëŽ; 덎; ) HANGUL SYLLABLE DYAEJ
+B34F;B34F;1103 1164 11BE;B34F;1103 1164 11BE; # (ë; ë; 덏; ë; 덏; ) HANGUL SYLLABLE DYAEC
+B350;B350;1103 1164 11BF;B350;1103 1164 11BF; # (ë; ë; 덐; ë; 덐; ) HANGUL SYLLABLE DYAEK
+B351;B351;1103 1164 11C0;B351;1103 1164 11C0; # (ë‘; ë‘; 덑; ë‘; 덑; ) HANGUL SYLLABLE DYAET
+B352;B352;1103 1164 11C1;B352;1103 1164 11C1; # (ë’; ë’; 댸á‡; ë’; 댸á‡; ) HANGUL SYLLABLE DYAEP
+B353;B353;1103 1164 11C2;B353;1103 1164 11C2; # (ë“; ë“; 덓; ë“; 덓; ) HANGUL SYLLABLE DYAEH
+B354;B354;1103 1165;B354;1103 1165; # (ë”; ë”; 더; ë”; 더; ) HANGUL SYLLABLE DEO
+B355;B355;1103 1165 11A8;B355;1103 1165 11A8; # (ë•; ë•; 덕; ë•; 덕; ) HANGUL SYLLABLE DEOG
+B356;B356;1103 1165 11A9;B356;1103 1165 11A9; # (ë–; ë–; 덖; ë–; 덖; ) HANGUL SYLLABLE DEOGG
+B357;B357;1103 1165 11AA;B357;1103 1165 11AA; # (ë—; ë—; 덗; ë—; 덗; ) HANGUL SYLLABLE DEOGS
+B358;B358;1103 1165 11AB;B358;1103 1165 11AB; # (ë˜; ë˜; 던; ë˜; 던; ) HANGUL SYLLABLE DEON
+B359;B359;1103 1165 11AC;B359;1103 1165 11AC; # (ë™; ë™; 덙; ë™; 덙; ) HANGUL SYLLABLE DEONJ
+B35A;B35A;1103 1165 11AD;B35A;1103 1165 11AD; # (ëš; ëš; 덚; ëš; 덚; ) HANGUL SYLLABLE DEONH
+B35B;B35B;1103 1165 11AE;B35B;1103 1165 11AE; # (ë›; ë›; 덛; ë›; 덛; ) HANGUL SYLLABLE DEOD
+B35C;B35C;1103 1165 11AF;B35C;1103 1165 11AF; # (ëœ; ëœ; 덜; ëœ; 덜; ) HANGUL SYLLABLE DEOL
+B35D;B35D;1103 1165 11B0;B35D;1103 1165 11B0; # (ë; ë; 덝; ë; 덝; ) HANGUL SYLLABLE DEOLG
+B35E;B35E;1103 1165 11B1;B35E;1103 1165 11B1; # (ëž; ëž; 덞; ëž; 덞; ) HANGUL SYLLABLE DEOLM
+B35F;B35F;1103 1165 11B2;B35F;1103 1165 11B2; # (ëŸ; ëŸ; 덟; ëŸ; 덟; ) HANGUL SYLLABLE DEOLB
+B360;B360;1103 1165 11B3;B360;1103 1165 11B3; # (ë ; ë ; 덠; ë ; 덠; ) HANGUL SYLLABLE DEOLS
+B361;B361;1103 1165 11B4;B361;1103 1165 11B4; # (ë¡; ë¡; 덡; ë¡; 덡; ) HANGUL SYLLABLE DEOLT
+B362;B362;1103 1165 11B5;B362;1103 1165 11B5; # (ë¢; ë¢; 덢; ë¢; 덢; ) HANGUL SYLLABLE DEOLP
+B363;B363;1103 1165 11B6;B363;1103 1165 11B6; # (ë£; ë£; 덣; ë£; 덣; ) HANGUL SYLLABLE DEOLH
+B364;B364;1103 1165 11B7;B364;1103 1165 11B7; # (ë¤; ë¤; 덤; ë¤; 덤; ) HANGUL SYLLABLE DEOM
+B365;B365;1103 1165 11B8;B365;1103 1165 11B8; # (ë¥; ë¥; 덥; ë¥; 덥; ) HANGUL SYLLABLE DEOB
+B366;B366;1103 1165 11B9;B366;1103 1165 11B9; # (ë¦; ë¦; 덦; ë¦; 덦; ) HANGUL SYLLABLE DEOBS
+B367;B367;1103 1165 11BA;B367;1103 1165 11BA; # (ë§; ë§; 덧; ë§; 덧; ) HANGUL SYLLABLE DEOS
+B368;B368;1103 1165 11BB;B368;1103 1165 11BB; # (ë¨; ë¨; 덨; ë¨; 덨; ) HANGUL SYLLABLE DEOSS
+B369;B369;1103 1165 11BC;B369;1103 1165 11BC; # (ë©; ë©; 덩; ë©; 덩; ) HANGUL SYLLABLE DEONG
+B36A;B36A;1103 1165 11BD;B36A;1103 1165 11BD; # (ëª; ëª; 덪; ëª; 덪; ) HANGUL SYLLABLE DEOJ
+B36B;B36B;1103 1165 11BE;B36B;1103 1165 11BE; # (ë«; ë«; 덫; ë«; 덫; ) HANGUL SYLLABLE DEOC
+B36C;B36C;1103 1165 11BF;B36C;1103 1165 11BF; # (ë¬; ë¬; 덬; ë¬; 덬; ) HANGUL SYLLABLE DEOK
+B36D;B36D;1103 1165 11C0;B36D;1103 1165 11C0; # (ë­; ë­; 덭; ë­; 덭; ) HANGUL SYLLABLE DEOT
+B36E;B36E;1103 1165 11C1;B36E;1103 1165 11C1; # (ë®; ë®; 더á‡; ë®; 더á‡; ) HANGUL SYLLABLE DEOP
+B36F;B36F;1103 1165 11C2;B36F;1103 1165 11C2; # (ë¯; ë¯; 덯; ë¯; 덯; ) HANGUL SYLLABLE DEOH
+B370;B370;1103 1166;B370;1103 1166; # (ë°; ë°; 데; ë°; 데; ) HANGUL SYLLABLE DE
+B371;B371;1103 1166 11A8;B371;1103 1166 11A8; # (ë±; ë±; 덱; ë±; 덱; ) HANGUL SYLLABLE DEG
+B372;B372;1103 1166 11A9;B372;1103 1166 11A9; # (ë²; ë²; 덲; ë²; 덲; ) HANGUL SYLLABLE DEGG
+B373;B373;1103 1166 11AA;B373;1103 1166 11AA; # (ë³; ë³; 덳; ë³; 덳; ) HANGUL SYLLABLE DEGS
+B374;B374;1103 1166 11AB;B374;1103 1166 11AB; # (ë´; ë´; 덴; ë´; 덴; ) HANGUL SYLLABLE DEN
+B375;B375;1103 1166 11AC;B375;1103 1166 11AC; # (ëµ; ëµ; 덵; ëµ; 덵; ) HANGUL SYLLABLE DENJ
+B376;B376;1103 1166 11AD;B376;1103 1166 11AD; # (ë¶; ë¶; 덶; ë¶; 덶; ) HANGUL SYLLABLE DENH
+B377;B377;1103 1166 11AE;B377;1103 1166 11AE; # (ë·; ë·; 덷; ë·; 덷; ) HANGUL SYLLABLE DED
+B378;B378;1103 1166 11AF;B378;1103 1166 11AF; # (ë¸; ë¸; 델; ë¸; 델; ) HANGUL SYLLABLE DEL
+B379;B379;1103 1166 11B0;B379;1103 1166 11B0; # (ë¹; ë¹; 덹; ë¹; 덹; ) HANGUL SYLLABLE DELG
+B37A;B37A;1103 1166 11B1;B37A;1103 1166 11B1; # (ëº; ëº; 덺; ëº; 덺; ) HANGUL SYLLABLE DELM
+B37B;B37B;1103 1166 11B2;B37B;1103 1166 11B2; # (ë»; ë»; 덻; ë»; 덻; ) HANGUL SYLLABLE DELB
+B37C;B37C;1103 1166 11B3;B37C;1103 1166 11B3; # (ë¼; ë¼; 덼; ë¼; 덼; ) HANGUL SYLLABLE DELS
+B37D;B37D;1103 1166 11B4;B37D;1103 1166 11B4; # (ë½; ë½; 덽; ë½; 덽; ) HANGUL SYLLABLE DELT
+B37E;B37E;1103 1166 11B5;B37E;1103 1166 11B5; # (ë¾; ë¾; 덾; ë¾; 덾; ) HANGUL SYLLABLE DELP
+B37F;B37F;1103 1166 11B6;B37F;1103 1166 11B6; # (ë¿; ë¿; 덿; ë¿; 덿; ) HANGUL SYLLABLE DELH
+B380;B380;1103 1166 11B7;B380;1103 1166 11B7; # (뎀; 뎀; 뎀; 뎀; 뎀; ) HANGUL SYLLABLE DEM
+B381;B381;1103 1166 11B8;B381;1103 1166 11B8; # (ëŽ; ëŽ; 뎁; ëŽ; 뎁; ) HANGUL SYLLABLE DEB
+B382;B382;1103 1166 11B9;B382;1103 1166 11B9; # (뎂; 뎂; 뎂; 뎂; 뎂; ) HANGUL SYLLABLE DEBS
+B383;B383;1103 1166 11BA;B383;1103 1166 11BA; # (뎃; 뎃; 뎃; 뎃; 뎃; ) HANGUL SYLLABLE DES
+B384;B384;1103 1166 11BB;B384;1103 1166 11BB; # (뎄; 뎄; 뎄; 뎄; 뎄; ) HANGUL SYLLABLE DESS
+B385;B385;1103 1166 11BC;B385;1103 1166 11BC; # (뎅; 뎅; 뎅; 뎅; 뎅; ) HANGUL SYLLABLE DENG
+B386;B386;1103 1166 11BD;B386;1103 1166 11BD; # (뎆; 뎆; 뎆; 뎆; 뎆; ) HANGUL SYLLABLE DEJ
+B387;B387;1103 1166 11BE;B387;1103 1166 11BE; # (뎇; 뎇; 뎇; 뎇; 뎇; ) HANGUL SYLLABLE DEC
+B388;B388;1103 1166 11BF;B388;1103 1166 11BF; # (뎈; 뎈; 뎈; 뎈; 뎈; ) HANGUL SYLLABLE DEK
+B389;B389;1103 1166 11C0;B389;1103 1166 11C0; # (뎉; 뎉; 뎉; 뎉; 뎉; ) HANGUL SYLLABLE DET
+B38A;B38A;1103 1166 11C1;B38A;1103 1166 11C1; # (뎊; 뎊; 데á‡; 뎊; 데á‡; ) HANGUL SYLLABLE DEP
+B38B;B38B;1103 1166 11C2;B38B;1103 1166 11C2; # (뎋; 뎋; 뎋; 뎋; 뎋; ) HANGUL SYLLABLE DEH
+B38C;B38C;1103 1167;B38C;1103 1167; # (뎌; 뎌; 뎌; 뎌; 뎌; ) HANGUL SYLLABLE DYEO
+B38D;B38D;1103 1167 11A8;B38D;1103 1167 11A8; # (ëŽ; ëŽ; 뎍; ëŽ; 뎍; ) HANGUL SYLLABLE DYEOG
+B38E;B38E;1103 1167 11A9;B38E;1103 1167 11A9; # (뎎; 뎎; 뎎; 뎎; 뎎; ) HANGUL SYLLABLE DYEOGG
+B38F;B38F;1103 1167 11AA;B38F;1103 1167 11AA; # (ëŽ; ëŽ; 뎏; ëŽ; 뎏; ) HANGUL SYLLABLE DYEOGS
+B390;B390;1103 1167 11AB;B390;1103 1167 11AB; # (ëŽ; ëŽ; 뎐; ëŽ; 뎐; ) HANGUL SYLLABLE DYEON
+B391;B391;1103 1167 11AC;B391;1103 1167 11AC; # (뎑; 뎑; 뎑; 뎑; 뎑; ) HANGUL SYLLABLE DYEONJ
+B392;B392;1103 1167 11AD;B392;1103 1167 11AD; # (뎒; 뎒; 뎒; 뎒; 뎒; ) HANGUL SYLLABLE DYEONH
+B393;B393;1103 1167 11AE;B393;1103 1167 11AE; # (뎓; 뎓; 뎓; 뎓; 뎓; ) HANGUL SYLLABLE DYEOD
+B394;B394;1103 1167 11AF;B394;1103 1167 11AF; # (뎔; 뎔; 뎔; 뎔; 뎔; ) HANGUL SYLLABLE DYEOL
+B395;B395;1103 1167 11B0;B395;1103 1167 11B0; # (뎕; 뎕; 뎕; 뎕; 뎕; ) HANGUL SYLLABLE DYEOLG
+B396;B396;1103 1167 11B1;B396;1103 1167 11B1; # (뎖; 뎖; 뎖; 뎖; 뎖; ) HANGUL SYLLABLE DYEOLM
+B397;B397;1103 1167 11B2;B397;1103 1167 11B2; # (뎗; 뎗; 뎗; 뎗; 뎗; ) HANGUL SYLLABLE DYEOLB
+B398;B398;1103 1167 11B3;B398;1103 1167 11B3; # (뎘; 뎘; 뎘; 뎘; 뎘; ) HANGUL SYLLABLE DYEOLS
+B399;B399;1103 1167 11B4;B399;1103 1167 11B4; # (뎙; 뎙; 뎙; 뎙; 뎙; ) HANGUL SYLLABLE DYEOLT
+B39A;B39A;1103 1167 11B5;B39A;1103 1167 11B5; # (뎚; 뎚; 뎚; 뎚; 뎚; ) HANGUL SYLLABLE DYEOLP
+B39B;B39B;1103 1167 11B6;B39B;1103 1167 11B6; # (뎛; 뎛; 뎛; 뎛; 뎛; ) HANGUL SYLLABLE DYEOLH
+B39C;B39C;1103 1167 11B7;B39C;1103 1167 11B7; # (뎜; 뎜; 뎜; 뎜; 뎜; ) HANGUL SYLLABLE DYEOM
+B39D;B39D;1103 1167 11B8;B39D;1103 1167 11B8; # (ëŽ; ëŽ; 뎝; ëŽ; 뎝; ) HANGUL SYLLABLE DYEOB
+B39E;B39E;1103 1167 11B9;B39E;1103 1167 11B9; # (뎞; 뎞; 뎞; 뎞; 뎞; ) HANGUL SYLLABLE DYEOBS
+B39F;B39F;1103 1167 11BA;B39F;1103 1167 11BA; # (뎟; 뎟; 뎟; 뎟; 뎟; ) HANGUL SYLLABLE DYEOS
+B3A0;B3A0;1103 1167 11BB;B3A0;1103 1167 11BB; # (뎠; 뎠; 뎠; 뎠; 뎠; ) HANGUL SYLLABLE DYEOSS
+B3A1;B3A1;1103 1167 11BC;B3A1;1103 1167 11BC; # (뎡; 뎡; 뎡; 뎡; 뎡; ) HANGUL SYLLABLE DYEONG
+B3A2;B3A2;1103 1167 11BD;B3A2;1103 1167 11BD; # (뎢; 뎢; 뎢; 뎢; 뎢; ) HANGUL SYLLABLE DYEOJ
+B3A3;B3A3;1103 1167 11BE;B3A3;1103 1167 11BE; # (뎣; 뎣; 뎣; 뎣; 뎣; ) HANGUL SYLLABLE DYEOC
+B3A4;B3A4;1103 1167 11BF;B3A4;1103 1167 11BF; # (뎤; 뎤; 뎤; 뎤; 뎤; ) HANGUL SYLLABLE DYEOK
+B3A5;B3A5;1103 1167 11C0;B3A5;1103 1167 11C0; # (뎥; 뎥; 뎥; 뎥; 뎥; ) HANGUL SYLLABLE DYEOT
+B3A6;B3A6;1103 1167 11C1;B3A6;1103 1167 11C1; # (뎦; 뎦; 뎌á‡; 뎦; 뎌á‡; ) HANGUL SYLLABLE DYEOP
+B3A7;B3A7;1103 1167 11C2;B3A7;1103 1167 11C2; # (뎧; 뎧; 뎧; 뎧; 뎧; ) HANGUL SYLLABLE DYEOH
+B3A8;B3A8;1103 1168;B3A8;1103 1168; # (뎨; 뎨; 뎨; 뎨; 뎨; ) HANGUL SYLLABLE DYE
+B3A9;B3A9;1103 1168 11A8;B3A9;1103 1168 11A8; # (뎩; 뎩; 뎩; 뎩; 뎩; ) HANGUL SYLLABLE DYEG
+B3AA;B3AA;1103 1168 11A9;B3AA;1103 1168 11A9; # (뎪; 뎪; 뎪; 뎪; 뎪; ) HANGUL SYLLABLE DYEGG
+B3AB;B3AB;1103 1168 11AA;B3AB;1103 1168 11AA; # (뎫; 뎫; 뎫; 뎫; 뎫; ) HANGUL SYLLABLE DYEGS
+B3AC;B3AC;1103 1168 11AB;B3AC;1103 1168 11AB; # (뎬; 뎬; 뎬; 뎬; 뎬; ) HANGUL SYLLABLE DYEN
+B3AD;B3AD;1103 1168 11AC;B3AD;1103 1168 11AC; # (뎭; 뎭; 뎭; 뎭; 뎭; ) HANGUL SYLLABLE DYENJ
+B3AE;B3AE;1103 1168 11AD;B3AE;1103 1168 11AD; # (뎮; 뎮; 뎮; 뎮; 뎮; ) HANGUL SYLLABLE DYENH
+B3AF;B3AF;1103 1168 11AE;B3AF;1103 1168 11AE; # (뎯; 뎯; 뎯; 뎯; 뎯; ) HANGUL SYLLABLE DYED
+B3B0;B3B0;1103 1168 11AF;B3B0;1103 1168 11AF; # (뎰; 뎰; 뎰; 뎰; 뎰; ) HANGUL SYLLABLE DYEL
+B3B1;B3B1;1103 1168 11B0;B3B1;1103 1168 11B0; # (뎱; 뎱; 뎱; 뎱; 뎱; ) HANGUL SYLLABLE DYELG
+B3B2;B3B2;1103 1168 11B1;B3B2;1103 1168 11B1; # (뎲; 뎲; 뎲; 뎲; 뎲; ) HANGUL SYLLABLE DYELM
+B3B3;B3B3;1103 1168 11B2;B3B3;1103 1168 11B2; # (뎳; 뎳; 뎳; 뎳; 뎳; ) HANGUL SYLLABLE DYELB
+B3B4;B3B4;1103 1168 11B3;B3B4;1103 1168 11B3; # (뎴; 뎴; 뎴; 뎴; 뎴; ) HANGUL SYLLABLE DYELS
+B3B5;B3B5;1103 1168 11B4;B3B5;1103 1168 11B4; # (뎵; 뎵; 뎵; 뎵; 뎵; ) HANGUL SYLLABLE DYELT
+B3B6;B3B6;1103 1168 11B5;B3B6;1103 1168 11B5; # (뎶; 뎶; 뎶; 뎶; 뎶; ) HANGUL SYLLABLE DYELP
+B3B7;B3B7;1103 1168 11B6;B3B7;1103 1168 11B6; # (뎷; 뎷; 뎷; 뎷; 뎷; ) HANGUL SYLLABLE DYELH
+B3B8;B3B8;1103 1168 11B7;B3B8;1103 1168 11B7; # (뎸; 뎸; 뎸; 뎸; 뎸; ) HANGUL SYLLABLE DYEM
+B3B9;B3B9;1103 1168 11B8;B3B9;1103 1168 11B8; # (뎹; 뎹; 뎹; 뎹; 뎹; ) HANGUL SYLLABLE DYEB
+B3BA;B3BA;1103 1168 11B9;B3BA;1103 1168 11B9; # (뎺; 뎺; 뎺; 뎺; 뎺; ) HANGUL SYLLABLE DYEBS
+B3BB;B3BB;1103 1168 11BA;B3BB;1103 1168 11BA; # (뎻; 뎻; 뎻; 뎻; 뎻; ) HANGUL SYLLABLE DYES
+B3BC;B3BC;1103 1168 11BB;B3BC;1103 1168 11BB; # (뎼; 뎼; 뎼; 뎼; 뎼; ) HANGUL SYLLABLE DYESS
+B3BD;B3BD;1103 1168 11BC;B3BD;1103 1168 11BC; # (뎽; 뎽; 뎽; 뎽; 뎽; ) HANGUL SYLLABLE DYENG
+B3BE;B3BE;1103 1168 11BD;B3BE;1103 1168 11BD; # (뎾; 뎾; 뎾; 뎾; 뎾; ) HANGUL SYLLABLE DYEJ
+B3BF;B3BF;1103 1168 11BE;B3BF;1103 1168 11BE; # (뎿; 뎿; 뎿; 뎿; 뎿; ) HANGUL SYLLABLE DYEC
+B3C0;B3C0;1103 1168 11BF;B3C0;1103 1168 11BF; # (ë€; ë€; 돀; ë€; 돀; ) HANGUL SYLLABLE DYEK
+B3C1;B3C1;1103 1168 11C0;B3C1;1103 1168 11C0; # (ë; ë; 돁; ë; 돁; ) HANGUL SYLLABLE DYET
+B3C2;B3C2;1103 1168 11C1;B3C2;1103 1168 11C1; # (ë‚; ë‚; 뎨á‡; ë‚; 뎨á‡; ) HANGUL SYLLABLE DYEP
+B3C3;B3C3;1103 1168 11C2;B3C3;1103 1168 11C2; # (ëƒ; ëƒ; 돃; ëƒ; 돃; ) HANGUL SYLLABLE DYEH
+B3C4;B3C4;1103 1169;B3C4;1103 1169; # (ë„; ë„; 도; ë„; 도; ) HANGUL SYLLABLE DO
+B3C5;B3C5;1103 1169 11A8;B3C5;1103 1169 11A8; # (ë…; ë…; 독; ë…; 독; ) HANGUL SYLLABLE DOG
+B3C6;B3C6;1103 1169 11A9;B3C6;1103 1169 11A9; # (ë†; ë†; 돆; ë†; 돆; ) HANGUL SYLLABLE DOGG
+B3C7;B3C7;1103 1169 11AA;B3C7;1103 1169 11AA; # (ë‡; ë‡; 돇; ë‡; 돇; ) HANGUL SYLLABLE DOGS
+B3C8;B3C8;1103 1169 11AB;B3C8;1103 1169 11AB; # (ëˆ; ëˆ; 돈; ëˆ; 돈; ) HANGUL SYLLABLE DON
+B3C9;B3C9;1103 1169 11AC;B3C9;1103 1169 11AC; # (ë‰; ë‰; 돉; ë‰; 돉; ) HANGUL SYLLABLE DONJ
+B3CA;B3CA;1103 1169 11AD;B3CA;1103 1169 11AD; # (ëŠ; ëŠ; 돊; ëŠ; 돊; ) HANGUL SYLLABLE DONH
+B3CB;B3CB;1103 1169 11AE;B3CB;1103 1169 11AE; # (ë‹; ë‹; 돋; ë‹; 돋; ) HANGUL SYLLABLE DOD
+B3CC;B3CC;1103 1169 11AF;B3CC;1103 1169 11AF; # (ëŒ; ëŒ; 돌; ëŒ; 돌; ) HANGUL SYLLABLE DOL
+B3CD;B3CD;1103 1169 11B0;B3CD;1103 1169 11B0; # (ë; ë; 돍; ë; 돍; ) HANGUL SYLLABLE DOLG
+B3CE;B3CE;1103 1169 11B1;B3CE;1103 1169 11B1; # (ëŽ; ëŽ; 돎; ëŽ; 돎; ) HANGUL SYLLABLE DOLM
+B3CF;B3CF;1103 1169 11B2;B3CF;1103 1169 11B2; # (ë; ë; 돏; ë; 돏; ) HANGUL SYLLABLE DOLB
+B3D0;B3D0;1103 1169 11B3;B3D0;1103 1169 11B3; # (ë; ë; 돐; ë; 돐; ) HANGUL SYLLABLE DOLS
+B3D1;B3D1;1103 1169 11B4;B3D1;1103 1169 11B4; # (ë‘; ë‘; 돑; ë‘; 돑; ) HANGUL SYLLABLE DOLT
+B3D2;B3D2;1103 1169 11B5;B3D2;1103 1169 11B5; # (ë’; ë’; 돒; ë’; 돒; ) HANGUL SYLLABLE DOLP
+B3D3;B3D3;1103 1169 11B6;B3D3;1103 1169 11B6; # (ë“; ë“; 돓; ë“; 돓; ) HANGUL SYLLABLE DOLH
+B3D4;B3D4;1103 1169 11B7;B3D4;1103 1169 11B7; # (ë”; ë”; 돔; ë”; 돔; ) HANGUL SYLLABLE DOM
+B3D5;B3D5;1103 1169 11B8;B3D5;1103 1169 11B8; # (ë•; ë•; 돕; ë•; 돕; ) HANGUL SYLLABLE DOB
+B3D6;B3D6;1103 1169 11B9;B3D6;1103 1169 11B9; # (ë–; ë–; 돖; ë–; 돖; ) HANGUL SYLLABLE DOBS
+B3D7;B3D7;1103 1169 11BA;B3D7;1103 1169 11BA; # (ë—; ë—; 돗; ë—; 돗; ) HANGUL SYLLABLE DOS
+B3D8;B3D8;1103 1169 11BB;B3D8;1103 1169 11BB; # (ë˜; ë˜; 돘; ë˜; 돘; ) HANGUL SYLLABLE DOSS
+B3D9;B3D9;1103 1169 11BC;B3D9;1103 1169 11BC; # (ë™; ë™; 동; ë™; 동; ) HANGUL SYLLABLE DONG
+B3DA;B3DA;1103 1169 11BD;B3DA;1103 1169 11BD; # (ëš; ëš; 돚; ëš; 돚; ) HANGUL SYLLABLE DOJ
+B3DB;B3DB;1103 1169 11BE;B3DB;1103 1169 11BE; # (ë›; ë›; 돛; ë›; 돛; ) HANGUL SYLLABLE DOC
+B3DC;B3DC;1103 1169 11BF;B3DC;1103 1169 11BF; # (ëœ; ëœ; 돜; ëœ; 돜; ) HANGUL SYLLABLE DOK
+B3DD;B3DD;1103 1169 11C0;B3DD;1103 1169 11C0; # (ë; ë; 돝; ë; 돝; ) HANGUL SYLLABLE DOT
+B3DE;B3DE;1103 1169 11C1;B3DE;1103 1169 11C1; # (ëž; ëž; 도á‡; ëž; 도á‡; ) HANGUL SYLLABLE DOP
+B3DF;B3DF;1103 1169 11C2;B3DF;1103 1169 11C2; # (ëŸ; ëŸ; 돟; ëŸ; 돟; ) HANGUL SYLLABLE DOH
+B3E0;B3E0;1103 116A;B3E0;1103 116A; # (ë ; ë ; 돠; ë ; 돠; ) HANGUL SYLLABLE DWA
+B3E1;B3E1;1103 116A 11A8;B3E1;1103 116A 11A8; # (ë¡; ë¡; 돡; ë¡; 돡; ) HANGUL SYLLABLE DWAG
+B3E2;B3E2;1103 116A 11A9;B3E2;1103 116A 11A9; # (ë¢; ë¢; 돢; ë¢; 돢; ) HANGUL SYLLABLE DWAGG
+B3E3;B3E3;1103 116A 11AA;B3E3;1103 116A 11AA; # (ë£; ë£; 돣; ë£; 돣; ) HANGUL SYLLABLE DWAGS
+B3E4;B3E4;1103 116A 11AB;B3E4;1103 116A 11AB; # (ë¤; ë¤; 돤; ë¤; 돤; ) HANGUL SYLLABLE DWAN
+B3E5;B3E5;1103 116A 11AC;B3E5;1103 116A 11AC; # (ë¥; ë¥; 돥; ë¥; 돥; ) HANGUL SYLLABLE DWANJ
+B3E6;B3E6;1103 116A 11AD;B3E6;1103 116A 11AD; # (ë¦; ë¦; 돦; ë¦; 돦; ) HANGUL SYLLABLE DWANH
+B3E7;B3E7;1103 116A 11AE;B3E7;1103 116A 11AE; # (ë§; ë§; 돧; ë§; 돧; ) HANGUL SYLLABLE DWAD
+B3E8;B3E8;1103 116A 11AF;B3E8;1103 116A 11AF; # (ë¨; ë¨; 돨; ë¨; 돨; ) HANGUL SYLLABLE DWAL
+B3E9;B3E9;1103 116A 11B0;B3E9;1103 116A 11B0; # (ë©; ë©; 돩; ë©; 돩; ) HANGUL SYLLABLE DWALG
+B3EA;B3EA;1103 116A 11B1;B3EA;1103 116A 11B1; # (ëª; ëª; 돪; ëª; 돪; ) HANGUL SYLLABLE DWALM
+B3EB;B3EB;1103 116A 11B2;B3EB;1103 116A 11B2; # (ë«; ë«; 돫; ë«; 돫; ) HANGUL SYLLABLE DWALB
+B3EC;B3EC;1103 116A 11B3;B3EC;1103 116A 11B3; # (ë¬; ë¬; 돬; ë¬; 돬; ) HANGUL SYLLABLE DWALS
+B3ED;B3ED;1103 116A 11B4;B3ED;1103 116A 11B4; # (ë­; ë­; 돭; ë­; 돭; ) HANGUL SYLLABLE DWALT
+B3EE;B3EE;1103 116A 11B5;B3EE;1103 116A 11B5; # (ë®; ë®; 돮; ë®; 돮; ) HANGUL SYLLABLE DWALP
+B3EF;B3EF;1103 116A 11B6;B3EF;1103 116A 11B6; # (ë¯; ë¯; 돯; ë¯; 돯; ) HANGUL SYLLABLE DWALH
+B3F0;B3F0;1103 116A 11B7;B3F0;1103 116A 11B7; # (ë°; ë°; 돰; ë°; 돰; ) HANGUL SYLLABLE DWAM
+B3F1;B3F1;1103 116A 11B8;B3F1;1103 116A 11B8; # (ë±; ë±; 돱; ë±; 돱; ) HANGUL SYLLABLE DWAB
+B3F2;B3F2;1103 116A 11B9;B3F2;1103 116A 11B9; # (ë²; ë²; 돲; ë²; 돲; ) HANGUL SYLLABLE DWABS
+B3F3;B3F3;1103 116A 11BA;B3F3;1103 116A 11BA; # (ë³; ë³; 돳; ë³; 돳; ) HANGUL SYLLABLE DWAS
+B3F4;B3F4;1103 116A 11BB;B3F4;1103 116A 11BB; # (ë´; ë´; 돴; ë´; 돴; ) HANGUL SYLLABLE DWASS
+B3F5;B3F5;1103 116A 11BC;B3F5;1103 116A 11BC; # (ëµ; ëµ; 돵; ëµ; 돵; ) HANGUL SYLLABLE DWANG
+B3F6;B3F6;1103 116A 11BD;B3F6;1103 116A 11BD; # (ë¶; ë¶; 돶; ë¶; 돶; ) HANGUL SYLLABLE DWAJ
+B3F7;B3F7;1103 116A 11BE;B3F7;1103 116A 11BE; # (ë·; ë·; 돷; ë·; 돷; ) HANGUL SYLLABLE DWAC
+B3F8;B3F8;1103 116A 11BF;B3F8;1103 116A 11BF; # (ë¸; ë¸; 돸; ë¸; 돸; ) HANGUL SYLLABLE DWAK
+B3F9;B3F9;1103 116A 11C0;B3F9;1103 116A 11C0; # (ë¹; ë¹; 돹; ë¹; 돹; ) HANGUL SYLLABLE DWAT
+B3FA;B3FA;1103 116A 11C1;B3FA;1103 116A 11C1; # (ëº; ëº; 돠á‡; ëº; 돠á‡; ) HANGUL SYLLABLE DWAP
+B3FB;B3FB;1103 116A 11C2;B3FB;1103 116A 11C2; # (ë»; ë»; 돻; ë»; 돻; ) HANGUL SYLLABLE DWAH
+B3FC;B3FC;1103 116B;B3FC;1103 116B; # (ë¼; ë¼; 돼; ë¼; 돼; ) HANGUL SYLLABLE DWAE
+B3FD;B3FD;1103 116B 11A8;B3FD;1103 116B 11A8; # (ë½; ë½; 돽; ë½; 돽; ) HANGUL SYLLABLE DWAEG
+B3FE;B3FE;1103 116B 11A9;B3FE;1103 116B 11A9; # (ë¾; ë¾; 돾; ë¾; 돾; ) HANGUL SYLLABLE DWAEGG
+B3FF;B3FF;1103 116B 11AA;B3FF;1103 116B 11AA; # (ë¿; ë¿; 돿; ë¿; 돿; ) HANGUL SYLLABLE DWAEGS
+B400;B400;1103 116B 11AB;B400;1103 116B 11AB; # (ë€; ë€; 됀; ë€; 됀; ) HANGUL SYLLABLE DWAEN
+B401;B401;1103 116B 11AC;B401;1103 116B 11AC; # (ë; ë; 됁; ë; 됁; ) HANGUL SYLLABLE DWAENJ
+B402;B402;1103 116B 11AD;B402;1103 116B 11AD; # (ë‚; ë‚; 됂; ë‚; 됂; ) HANGUL SYLLABLE DWAENH
+B403;B403;1103 116B 11AE;B403;1103 116B 11AE; # (ëƒ; ëƒ; 됃; ëƒ; 됃; ) HANGUL SYLLABLE DWAED
+B404;B404;1103 116B 11AF;B404;1103 116B 11AF; # (ë„; ë„; 됄; ë„; 됄; ) HANGUL SYLLABLE DWAEL
+B405;B405;1103 116B 11B0;B405;1103 116B 11B0; # (ë…; ë…; 됅; ë…; 됅; ) HANGUL SYLLABLE DWAELG
+B406;B406;1103 116B 11B1;B406;1103 116B 11B1; # (ë†; ë†; 됆; ë†; 됆; ) HANGUL SYLLABLE DWAELM
+B407;B407;1103 116B 11B2;B407;1103 116B 11B2; # (ë‡; ë‡; 됇; ë‡; 됇; ) HANGUL SYLLABLE DWAELB
+B408;B408;1103 116B 11B3;B408;1103 116B 11B3; # (ëˆ; ëˆ; 됈; ëˆ; 됈; ) HANGUL SYLLABLE DWAELS
+B409;B409;1103 116B 11B4;B409;1103 116B 11B4; # (ë‰; ë‰; 됉; ë‰; 됉; ) HANGUL SYLLABLE DWAELT
+B40A;B40A;1103 116B 11B5;B40A;1103 116B 11B5; # (ëŠ; ëŠ; 됊; ëŠ; 됊; ) HANGUL SYLLABLE DWAELP
+B40B;B40B;1103 116B 11B6;B40B;1103 116B 11B6; # (ë‹; ë‹; 됋; ë‹; 됋; ) HANGUL SYLLABLE DWAELH
+B40C;B40C;1103 116B 11B7;B40C;1103 116B 11B7; # (ëŒ; ëŒ; 됌; ëŒ; 됌; ) HANGUL SYLLABLE DWAEM
+B40D;B40D;1103 116B 11B8;B40D;1103 116B 11B8; # (ë; ë; 됍; ë; 됍; ) HANGUL SYLLABLE DWAEB
+B40E;B40E;1103 116B 11B9;B40E;1103 116B 11B9; # (ëŽ; ëŽ; 됎; ëŽ; 됎; ) HANGUL SYLLABLE DWAEBS
+B40F;B40F;1103 116B 11BA;B40F;1103 116B 11BA; # (ë; ë; 됏; ë; 됏; ) HANGUL SYLLABLE DWAES
+B410;B410;1103 116B 11BB;B410;1103 116B 11BB; # (ë; ë; 됐; ë; 됐; ) HANGUL SYLLABLE DWAESS
+B411;B411;1103 116B 11BC;B411;1103 116B 11BC; # (ë‘; ë‘; 됑; ë‘; 됑; ) HANGUL SYLLABLE DWAENG
+B412;B412;1103 116B 11BD;B412;1103 116B 11BD; # (ë’; ë’; 됒; ë’; 됒; ) HANGUL SYLLABLE DWAEJ
+B413;B413;1103 116B 11BE;B413;1103 116B 11BE; # (ë“; ë“; 됓; ë“; 됓; ) HANGUL SYLLABLE DWAEC
+B414;B414;1103 116B 11BF;B414;1103 116B 11BF; # (ë”; ë”; 됔; ë”; 됔; ) HANGUL SYLLABLE DWAEK
+B415;B415;1103 116B 11C0;B415;1103 116B 11C0; # (ë•; ë•; 됕; ë•; 됕; ) HANGUL SYLLABLE DWAET
+B416;B416;1103 116B 11C1;B416;1103 116B 11C1; # (ë–; ë–; 돼á‡; ë–; 돼á‡; ) HANGUL SYLLABLE DWAEP
+B417;B417;1103 116B 11C2;B417;1103 116B 11C2; # (ë—; ë—; 됗; ë—; 됗; ) HANGUL SYLLABLE DWAEH
+B418;B418;1103 116C;B418;1103 116C; # (ë˜; ë˜; 되; ë˜; 되; ) HANGUL SYLLABLE DOE
+B419;B419;1103 116C 11A8;B419;1103 116C 11A8; # (ë™; ë™; 됙; ë™; 됙; ) HANGUL SYLLABLE DOEG
+B41A;B41A;1103 116C 11A9;B41A;1103 116C 11A9; # (ëš; ëš; 됚; ëš; 됚; ) HANGUL SYLLABLE DOEGG
+B41B;B41B;1103 116C 11AA;B41B;1103 116C 11AA; # (ë›; ë›; 됛; ë›; 됛; ) HANGUL SYLLABLE DOEGS
+B41C;B41C;1103 116C 11AB;B41C;1103 116C 11AB; # (ëœ; ëœ; 된; ëœ; 된; ) HANGUL SYLLABLE DOEN
+B41D;B41D;1103 116C 11AC;B41D;1103 116C 11AC; # (ë; ë; 됝; ë; 됝; ) HANGUL SYLLABLE DOENJ
+B41E;B41E;1103 116C 11AD;B41E;1103 116C 11AD; # (ëž; ëž; 됞; ëž; 됞; ) HANGUL SYLLABLE DOENH
+B41F;B41F;1103 116C 11AE;B41F;1103 116C 11AE; # (ëŸ; ëŸ; 됟; ëŸ; 됟; ) HANGUL SYLLABLE DOED
+B420;B420;1103 116C 11AF;B420;1103 116C 11AF; # (ë ; ë ; 될; ë ; 될; ) HANGUL SYLLABLE DOEL
+B421;B421;1103 116C 11B0;B421;1103 116C 11B0; # (ë¡; ë¡; 됡; ë¡; 됡; ) HANGUL SYLLABLE DOELG
+B422;B422;1103 116C 11B1;B422;1103 116C 11B1; # (ë¢; ë¢; 됢; ë¢; 됢; ) HANGUL SYLLABLE DOELM
+B423;B423;1103 116C 11B2;B423;1103 116C 11B2; # (ë£; ë£; 됣; ë£; 됣; ) HANGUL SYLLABLE DOELB
+B424;B424;1103 116C 11B3;B424;1103 116C 11B3; # (ë¤; ë¤; 됤; ë¤; 됤; ) HANGUL SYLLABLE DOELS
+B425;B425;1103 116C 11B4;B425;1103 116C 11B4; # (ë¥; ë¥; 됥; ë¥; 됥; ) HANGUL SYLLABLE DOELT
+B426;B426;1103 116C 11B5;B426;1103 116C 11B5; # (ë¦; ë¦; 됦; ë¦; 됦; ) HANGUL SYLLABLE DOELP
+B427;B427;1103 116C 11B6;B427;1103 116C 11B6; # (ë§; ë§; 됧; ë§; 됧; ) HANGUL SYLLABLE DOELH
+B428;B428;1103 116C 11B7;B428;1103 116C 11B7; # (ë¨; ë¨; 됨; ë¨; 됨; ) HANGUL SYLLABLE DOEM
+B429;B429;1103 116C 11B8;B429;1103 116C 11B8; # (ë©; ë©; 됩; ë©; 됩; ) HANGUL SYLLABLE DOEB
+B42A;B42A;1103 116C 11B9;B42A;1103 116C 11B9; # (ëª; ëª; 됪; ëª; 됪; ) HANGUL SYLLABLE DOEBS
+B42B;B42B;1103 116C 11BA;B42B;1103 116C 11BA; # (ë«; ë«; 됫; ë«; 됫; ) HANGUL SYLLABLE DOES
+B42C;B42C;1103 116C 11BB;B42C;1103 116C 11BB; # (ë¬; ë¬; 됬; ë¬; 됬; ) HANGUL SYLLABLE DOESS
+B42D;B42D;1103 116C 11BC;B42D;1103 116C 11BC; # (ë­; ë­; 됭; ë­; 됭; ) HANGUL SYLLABLE DOENG
+B42E;B42E;1103 116C 11BD;B42E;1103 116C 11BD; # (ë®; ë®; 됮; ë®; 됮; ) HANGUL SYLLABLE DOEJ
+B42F;B42F;1103 116C 11BE;B42F;1103 116C 11BE; # (ë¯; ë¯; 됯; ë¯; 됯; ) HANGUL SYLLABLE DOEC
+B430;B430;1103 116C 11BF;B430;1103 116C 11BF; # (ë°; ë°; 됰; ë°; 됰; ) HANGUL SYLLABLE DOEK
+B431;B431;1103 116C 11C0;B431;1103 116C 11C0; # (ë±; ë±; 됱; ë±; 됱; ) HANGUL SYLLABLE DOET
+B432;B432;1103 116C 11C1;B432;1103 116C 11C1; # (ë²; ë²; 되á‡; ë²; 되á‡; ) HANGUL SYLLABLE DOEP
+B433;B433;1103 116C 11C2;B433;1103 116C 11C2; # (ë³; ë³; 됳; ë³; 됳; ) HANGUL SYLLABLE DOEH
+B434;B434;1103 116D;B434;1103 116D; # (ë´; ë´; 됴; ë´; 됴; ) HANGUL SYLLABLE DYO
+B435;B435;1103 116D 11A8;B435;1103 116D 11A8; # (ëµ; ëµ; 됵; ëµ; 됵; ) HANGUL SYLLABLE DYOG
+B436;B436;1103 116D 11A9;B436;1103 116D 11A9; # (ë¶; ë¶; 됶; ë¶; 됶; ) HANGUL SYLLABLE DYOGG
+B437;B437;1103 116D 11AA;B437;1103 116D 11AA; # (ë·; ë·; 됷; ë·; 됷; ) HANGUL SYLLABLE DYOGS
+B438;B438;1103 116D 11AB;B438;1103 116D 11AB; # (ë¸; ë¸; 됸; ë¸; 됸; ) HANGUL SYLLABLE DYON
+B439;B439;1103 116D 11AC;B439;1103 116D 11AC; # (ë¹; ë¹; 됹; ë¹; 됹; ) HANGUL SYLLABLE DYONJ
+B43A;B43A;1103 116D 11AD;B43A;1103 116D 11AD; # (ëº; ëº; 됺; ëº; 됺; ) HANGUL SYLLABLE DYONH
+B43B;B43B;1103 116D 11AE;B43B;1103 116D 11AE; # (ë»; ë»; 됻; ë»; 됻; ) HANGUL SYLLABLE DYOD
+B43C;B43C;1103 116D 11AF;B43C;1103 116D 11AF; # (ë¼; ë¼; 됼; ë¼; 됼; ) HANGUL SYLLABLE DYOL
+B43D;B43D;1103 116D 11B0;B43D;1103 116D 11B0; # (ë½; ë½; 됽; ë½; 됽; ) HANGUL SYLLABLE DYOLG
+B43E;B43E;1103 116D 11B1;B43E;1103 116D 11B1; # (ë¾; ë¾; 됾; ë¾; 됾; ) HANGUL SYLLABLE DYOLM
+B43F;B43F;1103 116D 11B2;B43F;1103 116D 11B2; # (ë¿; ë¿; 됿; ë¿; 됿; ) HANGUL SYLLABLE DYOLB
+B440;B440;1103 116D 11B3;B440;1103 116D 11B3; # (둀; 둀; 둀; 둀; 둀; ) HANGUL SYLLABLE DYOLS
+B441;B441;1103 116D 11B4;B441;1103 116D 11B4; # (ë‘; ë‘; 둁; ë‘; 둁; ) HANGUL SYLLABLE DYOLT
+B442;B442;1103 116D 11B5;B442;1103 116D 11B5; # (둂; 둂; 둂; 둂; 둂; ) HANGUL SYLLABLE DYOLP
+B443;B443;1103 116D 11B6;B443;1103 116D 11B6; # (둃; 둃; 둃; 둃; 둃; ) HANGUL SYLLABLE DYOLH
+B444;B444;1103 116D 11B7;B444;1103 116D 11B7; # (둄; 둄; 둄; 둄; 둄; ) HANGUL SYLLABLE DYOM
+B445;B445;1103 116D 11B8;B445;1103 116D 11B8; # (둅; 둅; 둅; 둅; 둅; ) HANGUL SYLLABLE DYOB
+B446;B446;1103 116D 11B9;B446;1103 116D 11B9; # (둆; 둆; 둆; 둆; 둆; ) HANGUL SYLLABLE DYOBS
+B447;B447;1103 116D 11BA;B447;1103 116D 11BA; # (둇; 둇; 둇; 둇; 둇; ) HANGUL SYLLABLE DYOS
+B448;B448;1103 116D 11BB;B448;1103 116D 11BB; # (둈; 둈; 둈; 둈; 둈; ) HANGUL SYLLABLE DYOSS
+B449;B449;1103 116D 11BC;B449;1103 116D 11BC; # (둉; 둉; 둉; 둉; 둉; ) HANGUL SYLLABLE DYONG
+B44A;B44A;1103 116D 11BD;B44A;1103 116D 11BD; # (둊; 둊; 둊; 둊; 둊; ) HANGUL SYLLABLE DYOJ
+B44B;B44B;1103 116D 11BE;B44B;1103 116D 11BE; # (둋; 둋; 둋; 둋; 둋; ) HANGUL SYLLABLE DYOC
+B44C;B44C;1103 116D 11BF;B44C;1103 116D 11BF; # (둌; 둌; 둌; 둌; 둌; ) HANGUL SYLLABLE DYOK
+B44D;B44D;1103 116D 11C0;B44D;1103 116D 11C0; # (ë‘; ë‘; 둍; ë‘; 둍; ) HANGUL SYLLABLE DYOT
+B44E;B44E;1103 116D 11C1;B44E;1103 116D 11C1; # (둎; 둎; 됴á‡; 둎; 됴á‡; ) HANGUL SYLLABLE DYOP
+B44F;B44F;1103 116D 11C2;B44F;1103 116D 11C2; # (ë‘; ë‘; 둏; ë‘; 둏; ) HANGUL SYLLABLE DYOH
+B450;B450;1103 116E;B450;1103 116E; # (ë‘; ë‘; 두; ë‘; 두; ) HANGUL SYLLABLE DU
+B451;B451;1103 116E 11A8;B451;1103 116E 11A8; # (둑; 둑; 둑; 둑; 둑; ) HANGUL SYLLABLE DUG
+B452;B452;1103 116E 11A9;B452;1103 116E 11A9; # (둒; 둒; 둒; 둒; 둒; ) HANGUL SYLLABLE DUGG
+B453;B453;1103 116E 11AA;B453;1103 116E 11AA; # (둓; 둓; 둓; 둓; 둓; ) HANGUL SYLLABLE DUGS
+B454;B454;1103 116E 11AB;B454;1103 116E 11AB; # (둔; 둔; 둔; 둔; 둔; ) HANGUL SYLLABLE DUN
+B455;B455;1103 116E 11AC;B455;1103 116E 11AC; # (둕; 둕; 둕; 둕; 둕; ) HANGUL SYLLABLE DUNJ
+B456;B456;1103 116E 11AD;B456;1103 116E 11AD; # (둖; 둖; 둖; 둖; 둖; ) HANGUL SYLLABLE DUNH
+B457;B457;1103 116E 11AE;B457;1103 116E 11AE; # (둗; 둗; 둗; 둗; 둗; ) HANGUL SYLLABLE DUD
+B458;B458;1103 116E 11AF;B458;1103 116E 11AF; # (둘; 둘; 둘; 둘; 둘; ) HANGUL SYLLABLE DUL
+B459;B459;1103 116E 11B0;B459;1103 116E 11B0; # (둙; 둙; 둙; 둙; 둙; ) HANGUL SYLLABLE DULG
+B45A;B45A;1103 116E 11B1;B45A;1103 116E 11B1; # (둚; 둚; 둚; 둚; 둚; ) HANGUL SYLLABLE DULM
+B45B;B45B;1103 116E 11B2;B45B;1103 116E 11B2; # (둛; 둛; 둛; 둛; 둛; ) HANGUL SYLLABLE DULB
+B45C;B45C;1103 116E 11B3;B45C;1103 116E 11B3; # (둜; 둜; 둜; 둜; 둜; ) HANGUL SYLLABLE DULS
+B45D;B45D;1103 116E 11B4;B45D;1103 116E 11B4; # (ë‘; ë‘; 둝; ë‘; 둝; ) HANGUL SYLLABLE DULT
+B45E;B45E;1103 116E 11B5;B45E;1103 116E 11B5; # (둞; 둞; 둞; 둞; 둞; ) HANGUL SYLLABLE DULP
+B45F;B45F;1103 116E 11B6;B45F;1103 116E 11B6; # (둟; 둟; 둟; 둟; 둟; ) HANGUL SYLLABLE DULH
+B460;B460;1103 116E 11B7;B460;1103 116E 11B7; # (둠; 둠; 둠; 둠; 둠; ) HANGUL SYLLABLE DUM
+B461;B461;1103 116E 11B8;B461;1103 116E 11B8; # (둡; 둡; 둡; 둡; 둡; ) HANGUL SYLLABLE DUB
+B462;B462;1103 116E 11B9;B462;1103 116E 11B9; # (둢; 둢; 둢; 둢; 둢; ) HANGUL SYLLABLE DUBS
+B463;B463;1103 116E 11BA;B463;1103 116E 11BA; # (둣; 둣; 둣; 둣; 둣; ) HANGUL SYLLABLE DUS
+B464;B464;1103 116E 11BB;B464;1103 116E 11BB; # (둤; 둤; 둤; 둤; 둤; ) HANGUL SYLLABLE DUSS
+B465;B465;1103 116E 11BC;B465;1103 116E 11BC; # (둥; 둥; 둥; 둥; 둥; ) HANGUL SYLLABLE DUNG
+B466;B466;1103 116E 11BD;B466;1103 116E 11BD; # (둦; 둦; 둦; 둦; 둦; ) HANGUL SYLLABLE DUJ
+B467;B467;1103 116E 11BE;B467;1103 116E 11BE; # (둧; 둧; 둧; 둧; 둧; ) HANGUL SYLLABLE DUC
+B468;B468;1103 116E 11BF;B468;1103 116E 11BF; # (둨; 둨; 둨; 둨; 둨; ) HANGUL SYLLABLE DUK
+B469;B469;1103 116E 11C0;B469;1103 116E 11C0; # (둩; 둩; 둩; 둩; 둩; ) HANGUL SYLLABLE DUT
+B46A;B46A;1103 116E 11C1;B46A;1103 116E 11C1; # (둪; 둪; 두á‡; 둪; 두á‡; ) HANGUL SYLLABLE DUP
+B46B;B46B;1103 116E 11C2;B46B;1103 116E 11C2; # (둫; 둫; 둫; 둫; 둫; ) HANGUL SYLLABLE DUH
+B46C;B46C;1103 116F;B46C;1103 116F; # (둬; 둬; 둬; 둬; 둬; ) HANGUL SYLLABLE DWEO
+B46D;B46D;1103 116F 11A8;B46D;1103 116F 11A8; # (둭; 둭; 둭; 둭; 둭; ) HANGUL SYLLABLE DWEOG
+B46E;B46E;1103 116F 11A9;B46E;1103 116F 11A9; # (둮; 둮; 둮; 둮; 둮; ) HANGUL SYLLABLE DWEOGG
+B46F;B46F;1103 116F 11AA;B46F;1103 116F 11AA; # (둯; 둯; 둯; 둯; 둯; ) HANGUL SYLLABLE DWEOGS
+B470;B470;1103 116F 11AB;B470;1103 116F 11AB; # (둰; 둰; 둰; 둰; 둰; ) HANGUL SYLLABLE DWEON
+B471;B471;1103 116F 11AC;B471;1103 116F 11AC; # (둱; 둱; 둱; 둱; 둱; ) HANGUL SYLLABLE DWEONJ
+B472;B472;1103 116F 11AD;B472;1103 116F 11AD; # (둲; 둲; 둲; 둲; 둲; ) HANGUL SYLLABLE DWEONH
+B473;B473;1103 116F 11AE;B473;1103 116F 11AE; # (둳; 둳; 둳; 둳; 둳; ) HANGUL SYLLABLE DWEOD
+B474;B474;1103 116F 11AF;B474;1103 116F 11AF; # (둴; 둴; 둴; 둴; 둴; ) HANGUL SYLLABLE DWEOL
+B475;B475;1103 116F 11B0;B475;1103 116F 11B0; # (둵; 둵; 둵; 둵; 둵; ) HANGUL SYLLABLE DWEOLG
+B476;B476;1103 116F 11B1;B476;1103 116F 11B1; # (둶; 둶; 둶; 둶; 둶; ) HANGUL SYLLABLE DWEOLM
+B477;B477;1103 116F 11B2;B477;1103 116F 11B2; # (둷; 둷; 둷; 둷; 둷; ) HANGUL SYLLABLE DWEOLB
+B478;B478;1103 116F 11B3;B478;1103 116F 11B3; # (둸; 둸; 둸; 둸; 둸; ) HANGUL SYLLABLE DWEOLS
+B479;B479;1103 116F 11B4;B479;1103 116F 11B4; # (둹; 둹; 둹; 둹; 둹; ) HANGUL SYLLABLE DWEOLT
+B47A;B47A;1103 116F 11B5;B47A;1103 116F 11B5; # (둺; 둺; 둺; 둺; 둺; ) HANGUL SYLLABLE DWEOLP
+B47B;B47B;1103 116F 11B6;B47B;1103 116F 11B6; # (둻; 둻; 둻; 둻; 둻; ) HANGUL SYLLABLE DWEOLH
+B47C;B47C;1103 116F 11B7;B47C;1103 116F 11B7; # (둼; 둼; 둼; 둼; 둼; ) HANGUL SYLLABLE DWEOM
+B47D;B47D;1103 116F 11B8;B47D;1103 116F 11B8; # (둽; 둽; 둽; 둽; 둽; ) HANGUL SYLLABLE DWEOB
+B47E;B47E;1103 116F 11B9;B47E;1103 116F 11B9; # (둾; 둾; 둾; 둾; 둾; ) HANGUL SYLLABLE DWEOBS
+B47F;B47F;1103 116F 11BA;B47F;1103 116F 11BA; # (둿; 둿; 둿; 둿; 둿; ) HANGUL SYLLABLE DWEOS
+B480;B480;1103 116F 11BB;B480;1103 116F 11BB; # (뒀; 뒀; 뒀; 뒀; 뒀; ) HANGUL SYLLABLE DWEOSS
+B481;B481;1103 116F 11BC;B481;1103 116F 11BC; # (ë’; ë’; 뒁; ë’; 뒁; ) HANGUL SYLLABLE DWEONG
+B482;B482;1103 116F 11BD;B482;1103 116F 11BD; # (뒂; 뒂; 뒂; 뒂; 뒂; ) HANGUL SYLLABLE DWEOJ
+B483;B483;1103 116F 11BE;B483;1103 116F 11BE; # (뒃; 뒃; 뒃; 뒃; 뒃; ) HANGUL SYLLABLE DWEOC
+B484;B484;1103 116F 11BF;B484;1103 116F 11BF; # (뒄; 뒄; 뒄; 뒄; 뒄; ) HANGUL SYLLABLE DWEOK
+B485;B485;1103 116F 11C0;B485;1103 116F 11C0; # (뒅; 뒅; 뒅; 뒅; 뒅; ) HANGUL SYLLABLE DWEOT
+B486;B486;1103 116F 11C1;B486;1103 116F 11C1; # (ë’†; ë’†; 둬á‡; ë’†; 둬á‡; ) HANGUL SYLLABLE DWEOP
+B487;B487;1103 116F 11C2;B487;1103 116F 11C2; # (뒇; 뒇; 뒇; 뒇; 뒇; ) HANGUL SYLLABLE DWEOH
+B488;B488;1103 1170;B488;1103 1170; # (뒈; 뒈; 뒈; 뒈; 뒈; ) HANGUL SYLLABLE DWE
+B489;B489;1103 1170 11A8;B489;1103 1170 11A8; # (뒉; 뒉; 뒉; 뒉; 뒉; ) HANGUL SYLLABLE DWEG
+B48A;B48A;1103 1170 11A9;B48A;1103 1170 11A9; # (뒊; 뒊; 뒊; 뒊; 뒊; ) HANGUL SYLLABLE DWEGG
+B48B;B48B;1103 1170 11AA;B48B;1103 1170 11AA; # (뒋; 뒋; 뒋; 뒋; 뒋; ) HANGUL SYLLABLE DWEGS
+B48C;B48C;1103 1170 11AB;B48C;1103 1170 11AB; # (뒌; 뒌; 뒌; 뒌; 뒌; ) HANGUL SYLLABLE DWEN
+B48D;B48D;1103 1170 11AC;B48D;1103 1170 11AC; # (ë’; ë’; 뒍; ë’; 뒍; ) HANGUL SYLLABLE DWENJ
+B48E;B48E;1103 1170 11AD;B48E;1103 1170 11AD; # (뒎; 뒎; 뒎; 뒎; 뒎; ) HANGUL SYLLABLE DWENH
+B48F;B48F;1103 1170 11AE;B48F;1103 1170 11AE; # (ë’; ë’; 뒏; ë’; 뒏; ) HANGUL SYLLABLE DWED
+B490;B490;1103 1170 11AF;B490;1103 1170 11AF; # (ë’; ë’; 뒐; ë’; 뒐; ) HANGUL SYLLABLE DWEL
+B491;B491;1103 1170 11B0;B491;1103 1170 11B0; # (뒑; 뒑; 뒑; 뒑; 뒑; ) HANGUL SYLLABLE DWELG
+B492;B492;1103 1170 11B1;B492;1103 1170 11B1; # (뒒; 뒒; 뒒; 뒒; 뒒; ) HANGUL SYLLABLE DWELM
+B493;B493;1103 1170 11B2;B493;1103 1170 11B2; # (뒓; 뒓; 뒓; 뒓; 뒓; ) HANGUL SYLLABLE DWELB
+B494;B494;1103 1170 11B3;B494;1103 1170 11B3; # (뒔; 뒔; 뒔; 뒔; 뒔; ) HANGUL SYLLABLE DWELS
+B495;B495;1103 1170 11B4;B495;1103 1170 11B4; # (뒕; 뒕; 뒕; 뒕; 뒕; ) HANGUL SYLLABLE DWELT
+B496;B496;1103 1170 11B5;B496;1103 1170 11B5; # (뒖; 뒖; 뒖; 뒖; 뒖; ) HANGUL SYLLABLE DWELP
+B497;B497;1103 1170 11B6;B497;1103 1170 11B6; # (뒗; 뒗; 뒗; 뒗; 뒗; ) HANGUL SYLLABLE DWELH
+B498;B498;1103 1170 11B7;B498;1103 1170 11B7; # (뒘; 뒘; 뒘; 뒘; 뒘; ) HANGUL SYLLABLE DWEM
+B499;B499;1103 1170 11B8;B499;1103 1170 11B8; # (뒙; 뒙; 뒙; 뒙; 뒙; ) HANGUL SYLLABLE DWEB
+B49A;B49A;1103 1170 11B9;B49A;1103 1170 11B9; # (뒚; 뒚; 뒚; 뒚; 뒚; ) HANGUL SYLLABLE DWEBS
+B49B;B49B;1103 1170 11BA;B49B;1103 1170 11BA; # (뒛; 뒛; 뒛; 뒛; 뒛; ) HANGUL SYLLABLE DWES
+B49C;B49C;1103 1170 11BB;B49C;1103 1170 11BB; # (뒜; 뒜; 뒜; 뒜; 뒜; ) HANGUL SYLLABLE DWESS
+B49D;B49D;1103 1170 11BC;B49D;1103 1170 11BC; # (ë’; ë’; 뒝; ë’; 뒝; ) HANGUL SYLLABLE DWENG
+B49E;B49E;1103 1170 11BD;B49E;1103 1170 11BD; # (뒞; 뒞; 뒞; 뒞; 뒞; ) HANGUL SYLLABLE DWEJ
+B49F;B49F;1103 1170 11BE;B49F;1103 1170 11BE; # (뒟; 뒟; 뒟; 뒟; 뒟; ) HANGUL SYLLABLE DWEC
+B4A0;B4A0;1103 1170 11BF;B4A0;1103 1170 11BF; # (뒠; 뒠; 뒠; 뒠; 뒠; ) HANGUL SYLLABLE DWEK
+B4A1;B4A1;1103 1170 11C0;B4A1;1103 1170 11C0; # (뒡; 뒡; 뒡; 뒡; 뒡; ) HANGUL SYLLABLE DWET
+B4A2;B4A2;1103 1170 11C1;B4A2;1103 1170 11C1; # (ë’¢; ë’¢; 뒈á‡; ë’¢; 뒈á‡; ) HANGUL SYLLABLE DWEP
+B4A3;B4A3;1103 1170 11C2;B4A3;1103 1170 11C2; # (뒣; 뒣; 뒣; 뒣; 뒣; ) HANGUL SYLLABLE DWEH
+B4A4;B4A4;1103 1171;B4A4;1103 1171; # (뒤; 뒤; 뒤; 뒤; 뒤; ) HANGUL SYLLABLE DWI
+B4A5;B4A5;1103 1171 11A8;B4A5;1103 1171 11A8; # (뒥; 뒥; 뒥; 뒥; 뒥; ) HANGUL SYLLABLE DWIG
+B4A6;B4A6;1103 1171 11A9;B4A6;1103 1171 11A9; # (뒦; 뒦; 뒦; 뒦; 뒦; ) HANGUL SYLLABLE DWIGG
+B4A7;B4A7;1103 1171 11AA;B4A7;1103 1171 11AA; # (뒧; 뒧; 뒧; 뒧; 뒧; ) HANGUL SYLLABLE DWIGS
+B4A8;B4A8;1103 1171 11AB;B4A8;1103 1171 11AB; # (뒨; 뒨; 뒨; 뒨; 뒨; ) HANGUL SYLLABLE DWIN
+B4A9;B4A9;1103 1171 11AC;B4A9;1103 1171 11AC; # (뒩; 뒩; 뒩; 뒩; 뒩; ) HANGUL SYLLABLE DWINJ
+B4AA;B4AA;1103 1171 11AD;B4AA;1103 1171 11AD; # (뒪; 뒪; 뒪; 뒪; 뒪; ) HANGUL SYLLABLE DWINH
+B4AB;B4AB;1103 1171 11AE;B4AB;1103 1171 11AE; # (뒫; 뒫; 뒫; 뒫; 뒫; ) HANGUL SYLLABLE DWID
+B4AC;B4AC;1103 1171 11AF;B4AC;1103 1171 11AF; # (뒬; 뒬; 뒬; 뒬; 뒬; ) HANGUL SYLLABLE DWIL
+B4AD;B4AD;1103 1171 11B0;B4AD;1103 1171 11B0; # (뒭; 뒭; 뒭; 뒭; 뒭; ) HANGUL SYLLABLE DWILG
+B4AE;B4AE;1103 1171 11B1;B4AE;1103 1171 11B1; # (뒮; 뒮; 뒮; 뒮; 뒮; ) HANGUL SYLLABLE DWILM
+B4AF;B4AF;1103 1171 11B2;B4AF;1103 1171 11B2; # (뒯; 뒯; 뒯; 뒯; 뒯; ) HANGUL SYLLABLE DWILB
+B4B0;B4B0;1103 1171 11B3;B4B0;1103 1171 11B3; # (뒰; 뒰; 뒰; 뒰; 뒰; ) HANGUL SYLLABLE DWILS
+B4B1;B4B1;1103 1171 11B4;B4B1;1103 1171 11B4; # (뒱; 뒱; 뒱; 뒱; 뒱; ) HANGUL SYLLABLE DWILT
+B4B2;B4B2;1103 1171 11B5;B4B2;1103 1171 11B5; # (뒲; 뒲; 뒲; 뒲; 뒲; ) HANGUL SYLLABLE DWILP
+B4B3;B4B3;1103 1171 11B6;B4B3;1103 1171 11B6; # (뒳; 뒳; 뒳; 뒳; 뒳; ) HANGUL SYLLABLE DWILH
+B4B4;B4B4;1103 1171 11B7;B4B4;1103 1171 11B7; # (뒴; 뒴; 뒴; 뒴; 뒴; ) HANGUL SYLLABLE DWIM
+B4B5;B4B5;1103 1171 11B8;B4B5;1103 1171 11B8; # (뒵; 뒵; 뒵; 뒵; 뒵; ) HANGUL SYLLABLE DWIB
+B4B6;B4B6;1103 1171 11B9;B4B6;1103 1171 11B9; # (뒶; 뒶; 뒶; 뒶; 뒶; ) HANGUL SYLLABLE DWIBS
+B4B7;B4B7;1103 1171 11BA;B4B7;1103 1171 11BA; # (뒷; 뒷; 뒷; 뒷; 뒷; ) HANGUL SYLLABLE DWIS
+B4B8;B4B8;1103 1171 11BB;B4B8;1103 1171 11BB; # (뒸; 뒸; 뒸; 뒸; 뒸; ) HANGUL SYLLABLE DWISS
+B4B9;B4B9;1103 1171 11BC;B4B9;1103 1171 11BC; # (뒹; 뒹; 뒹; 뒹; 뒹; ) HANGUL SYLLABLE DWING
+B4BA;B4BA;1103 1171 11BD;B4BA;1103 1171 11BD; # (뒺; 뒺; 뒺; 뒺; 뒺; ) HANGUL SYLLABLE DWIJ
+B4BB;B4BB;1103 1171 11BE;B4BB;1103 1171 11BE; # (뒻; 뒻; 뒻; 뒻; 뒻; ) HANGUL SYLLABLE DWIC
+B4BC;B4BC;1103 1171 11BF;B4BC;1103 1171 11BF; # (뒼; 뒼; 뒼; 뒼; 뒼; ) HANGUL SYLLABLE DWIK
+B4BD;B4BD;1103 1171 11C0;B4BD;1103 1171 11C0; # (뒽; 뒽; 뒽; 뒽; 뒽; ) HANGUL SYLLABLE DWIT
+B4BE;B4BE;1103 1171 11C1;B4BE;1103 1171 11C1; # (ë’¾; ë’¾; 뒤á‡; ë’¾; 뒤á‡; ) HANGUL SYLLABLE DWIP
+B4BF;B4BF;1103 1171 11C2;B4BF;1103 1171 11C2; # (뒿; 뒿; 뒿; 뒿; 뒿; ) HANGUL SYLLABLE DWIH
+B4C0;B4C0;1103 1172;B4C0;1103 1172; # (듀; 듀; 듀; 듀; 듀; ) HANGUL SYLLABLE DYU
+B4C1;B4C1;1103 1172 11A8;B4C1;1103 1172 11A8; # (ë“; ë“; 듁; ë“; 듁; ) HANGUL SYLLABLE DYUG
+B4C2;B4C2;1103 1172 11A9;B4C2;1103 1172 11A9; # (듂; 듂; 듂; 듂; 듂; ) HANGUL SYLLABLE DYUGG
+B4C3;B4C3;1103 1172 11AA;B4C3;1103 1172 11AA; # (듃; 듃; 듃; 듃; 듃; ) HANGUL SYLLABLE DYUGS
+B4C4;B4C4;1103 1172 11AB;B4C4;1103 1172 11AB; # (듄; 듄; 듄; 듄; 듄; ) HANGUL SYLLABLE DYUN
+B4C5;B4C5;1103 1172 11AC;B4C5;1103 1172 11AC; # (듅; 듅; 듅; 듅; 듅; ) HANGUL SYLLABLE DYUNJ
+B4C6;B4C6;1103 1172 11AD;B4C6;1103 1172 11AD; # (듆; 듆; 듆; 듆; 듆; ) HANGUL SYLLABLE DYUNH
+B4C7;B4C7;1103 1172 11AE;B4C7;1103 1172 11AE; # (듇; 듇; 듇; 듇; 듇; ) HANGUL SYLLABLE DYUD
+B4C8;B4C8;1103 1172 11AF;B4C8;1103 1172 11AF; # (듈; 듈; 듈; 듈; 듈; ) HANGUL SYLLABLE DYUL
+B4C9;B4C9;1103 1172 11B0;B4C9;1103 1172 11B0; # (듉; 듉; 듉; 듉; 듉; ) HANGUL SYLLABLE DYULG
+B4CA;B4CA;1103 1172 11B1;B4CA;1103 1172 11B1; # (듊; 듊; 듊; 듊; 듊; ) HANGUL SYLLABLE DYULM
+B4CB;B4CB;1103 1172 11B2;B4CB;1103 1172 11B2; # (듋; 듋; 듋; 듋; 듋; ) HANGUL SYLLABLE DYULB
+B4CC;B4CC;1103 1172 11B3;B4CC;1103 1172 11B3; # (듌; 듌; 듌; 듌; 듌; ) HANGUL SYLLABLE DYULS
+B4CD;B4CD;1103 1172 11B4;B4CD;1103 1172 11B4; # (ë“; ë“; 듍; ë“; 듍; ) HANGUL SYLLABLE DYULT
+B4CE;B4CE;1103 1172 11B5;B4CE;1103 1172 11B5; # (듎; 듎; 듎; 듎; 듎; ) HANGUL SYLLABLE DYULP
+B4CF;B4CF;1103 1172 11B6;B4CF;1103 1172 11B6; # (ë“; ë“; 듏; ë“; 듏; ) HANGUL SYLLABLE DYULH
+B4D0;B4D0;1103 1172 11B7;B4D0;1103 1172 11B7; # (ë“; ë“; 듐; ë“; 듐; ) HANGUL SYLLABLE DYUM
+B4D1;B4D1;1103 1172 11B8;B4D1;1103 1172 11B8; # (듑; 듑; 듑; 듑; 듑; ) HANGUL SYLLABLE DYUB
+B4D2;B4D2;1103 1172 11B9;B4D2;1103 1172 11B9; # (듒; 듒; 듒; 듒; 듒; ) HANGUL SYLLABLE DYUBS
+B4D3;B4D3;1103 1172 11BA;B4D3;1103 1172 11BA; # (듓; 듓; 듓; 듓; 듓; ) HANGUL SYLLABLE DYUS
+B4D4;B4D4;1103 1172 11BB;B4D4;1103 1172 11BB; # (듔; 듔; 듔; 듔; 듔; ) HANGUL SYLLABLE DYUSS
+B4D5;B4D5;1103 1172 11BC;B4D5;1103 1172 11BC; # (듕; 듕; 듕; 듕; 듕; ) HANGUL SYLLABLE DYUNG
+B4D6;B4D6;1103 1172 11BD;B4D6;1103 1172 11BD; # (듖; 듖; 듖; 듖; 듖; ) HANGUL SYLLABLE DYUJ
+B4D7;B4D7;1103 1172 11BE;B4D7;1103 1172 11BE; # (듗; 듗; 듗; 듗; 듗; ) HANGUL SYLLABLE DYUC
+B4D8;B4D8;1103 1172 11BF;B4D8;1103 1172 11BF; # (듘; 듘; 듘; 듘; 듘; ) HANGUL SYLLABLE DYUK
+B4D9;B4D9;1103 1172 11C0;B4D9;1103 1172 11C0; # (듙; 듙; 듙; 듙; 듙; ) HANGUL SYLLABLE DYUT
+B4DA;B4DA;1103 1172 11C1;B4DA;1103 1172 11C1; # (듚; 듚; 듀á‡; 듚; 듀á‡; ) HANGUL SYLLABLE DYUP
+B4DB;B4DB;1103 1172 11C2;B4DB;1103 1172 11C2; # (듛; 듛; 듛; 듛; 듛; ) HANGUL SYLLABLE DYUH
+B4DC;B4DC;1103 1173;B4DC;1103 1173; # (드; 드; 드; 드; 드; ) HANGUL SYLLABLE DEU
+B4DD;B4DD;1103 1173 11A8;B4DD;1103 1173 11A8; # (ë“; ë“; 득; ë“; 득; ) HANGUL SYLLABLE DEUG
+B4DE;B4DE;1103 1173 11A9;B4DE;1103 1173 11A9; # (듞; 듞; 듞; 듞; 듞; ) HANGUL SYLLABLE DEUGG
+B4DF;B4DF;1103 1173 11AA;B4DF;1103 1173 11AA; # (듟; 듟; 듟; 듟; 듟; ) HANGUL SYLLABLE DEUGS
+B4E0;B4E0;1103 1173 11AB;B4E0;1103 1173 11AB; # (든; 든; 든; 든; 든; ) HANGUL SYLLABLE DEUN
+B4E1;B4E1;1103 1173 11AC;B4E1;1103 1173 11AC; # (듡; 듡; 듡; 듡; 듡; ) HANGUL SYLLABLE DEUNJ
+B4E2;B4E2;1103 1173 11AD;B4E2;1103 1173 11AD; # (듢; 듢; 듢; 듢; 듢; ) HANGUL SYLLABLE DEUNH
+B4E3;B4E3;1103 1173 11AE;B4E3;1103 1173 11AE; # (듣; 듣; 듣; 듣; 듣; ) HANGUL SYLLABLE DEUD
+B4E4;B4E4;1103 1173 11AF;B4E4;1103 1173 11AF; # (들; 들; 들; 들; 들; ) HANGUL SYLLABLE DEUL
+B4E5;B4E5;1103 1173 11B0;B4E5;1103 1173 11B0; # (듥; 듥; 듥; 듥; 듥; ) HANGUL SYLLABLE DEULG
+B4E6;B4E6;1103 1173 11B1;B4E6;1103 1173 11B1; # (듦; 듦; 듦; 듦; 듦; ) HANGUL SYLLABLE DEULM
+B4E7;B4E7;1103 1173 11B2;B4E7;1103 1173 11B2; # (듧; 듧; 듧; 듧; 듧; ) HANGUL SYLLABLE DEULB
+B4E8;B4E8;1103 1173 11B3;B4E8;1103 1173 11B3; # (듨; 듨; 듨; 듨; 듨; ) HANGUL SYLLABLE DEULS
+B4E9;B4E9;1103 1173 11B4;B4E9;1103 1173 11B4; # (듩; 듩; 듩; 듩; 듩; ) HANGUL SYLLABLE DEULT
+B4EA;B4EA;1103 1173 11B5;B4EA;1103 1173 11B5; # (듪; 듪; 듪; 듪; 듪; ) HANGUL SYLLABLE DEULP
+B4EB;B4EB;1103 1173 11B6;B4EB;1103 1173 11B6; # (듫; 듫; 듫; 듫; 듫; ) HANGUL SYLLABLE DEULH
+B4EC;B4EC;1103 1173 11B7;B4EC;1103 1173 11B7; # (듬; 듬; 듬; 듬; 듬; ) HANGUL SYLLABLE DEUM
+B4ED;B4ED;1103 1173 11B8;B4ED;1103 1173 11B8; # (듭; 듭; 듭; 듭; 듭; ) HANGUL SYLLABLE DEUB
+B4EE;B4EE;1103 1173 11B9;B4EE;1103 1173 11B9; # (듮; 듮; 듮; 듮; 듮; ) HANGUL SYLLABLE DEUBS
+B4EF;B4EF;1103 1173 11BA;B4EF;1103 1173 11BA; # (듯; 듯; 듯; 듯; 듯; ) HANGUL SYLLABLE DEUS
+B4F0;B4F0;1103 1173 11BB;B4F0;1103 1173 11BB; # (듰; 듰; 듰; 듰; 듰; ) HANGUL SYLLABLE DEUSS
+B4F1;B4F1;1103 1173 11BC;B4F1;1103 1173 11BC; # (등; 등; 등; 등; 등; ) HANGUL SYLLABLE DEUNG
+B4F2;B4F2;1103 1173 11BD;B4F2;1103 1173 11BD; # (듲; 듲; 듲; 듲; 듲; ) HANGUL SYLLABLE DEUJ
+B4F3;B4F3;1103 1173 11BE;B4F3;1103 1173 11BE; # (듳; 듳; 듳; 듳; 듳; ) HANGUL SYLLABLE DEUC
+B4F4;B4F4;1103 1173 11BF;B4F4;1103 1173 11BF; # (듴; 듴; 듴; 듴; 듴; ) HANGUL SYLLABLE DEUK
+B4F5;B4F5;1103 1173 11C0;B4F5;1103 1173 11C0; # (듵; 듵; 듵; 듵; 듵; ) HANGUL SYLLABLE DEUT
+B4F6;B4F6;1103 1173 11C1;B4F6;1103 1173 11C1; # (ë“¶; ë“¶; 드á‡; ë“¶; 드á‡; ) HANGUL SYLLABLE DEUP
+B4F7;B4F7;1103 1173 11C2;B4F7;1103 1173 11C2; # (듷; 듷; 듷; 듷; 듷; ) HANGUL SYLLABLE DEUH
+B4F8;B4F8;1103 1174;B4F8;1103 1174; # (듸; 듸; 듸; 듸; 듸; ) HANGUL SYLLABLE DYI
+B4F9;B4F9;1103 1174 11A8;B4F9;1103 1174 11A8; # (듹; 듹; 듹; 듹; 듹; ) HANGUL SYLLABLE DYIG
+B4FA;B4FA;1103 1174 11A9;B4FA;1103 1174 11A9; # (듺; 듺; 듺; 듺; 듺; ) HANGUL SYLLABLE DYIGG
+B4FB;B4FB;1103 1174 11AA;B4FB;1103 1174 11AA; # (듻; 듻; 듻; 듻; 듻; ) HANGUL SYLLABLE DYIGS
+B4FC;B4FC;1103 1174 11AB;B4FC;1103 1174 11AB; # (듼; 듼; 듼; 듼; 듼; ) HANGUL SYLLABLE DYIN
+B4FD;B4FD;1103 1174 11AC;B4FD;1103 1174 11AC; # (듽; 듽; 듽; 듽; 듽; ) HANGUL SYLLABLE DYINJ
+B4FE;B4FE;1103 1174 11AD;B4FE;1103 1174 11AD; # (듾; 듾; 듾; 듾; 듾; ) HANGUL SYLLABLE DYINH
+B4FF;B4FF;1103 1174 11AE;B4FF;1103 1174 11AE; # (듿; 듿; 듿; 듿; 듿; ) HANGUL SYLLABLE DYID
+B500;B500;1103 1174 11AF;B500;1103 1174 11AF; # (딀; 딀; 딀; 딀; 딀; ) HANGUL SYLLABLE DYIL
+B501;B501;1103 1174 11B0;B501;1103 1174 11B0; # (ë”; ë”; 딁; ë”; 딁; ) HANGUL SYLLABLE DYILG
+B502;B502;1103 1174 11B1;B502;1103 1174 11B1; # (딂; 딂; 딂; 딂; 딂; ) HANGUL SYLLABLE DYILM
+B503;B503;1103 1174 11B2;B503;1103 1174 11B2; # (딃; 딃; 딃; 딃; 딃; ) HANGUL SYLLABLE DYILB
+B504;B504;1103 1174 11B3;B504;1103 1174 11B3; # (딄; 딄; 딄; 딄; 딄; ) HANGUL SYLLABLE DYILS
+B505;B505;1103 1174 11B4;B505;1103 1174 11B4; # (딅; 딅; 딅; 딅; 딅; ) HANGUL SYLLABLE DYILT
+B506;B506;1103 1174 11B5;B506;1103 1174 11B5; # (딆; 딆; 딆; 딆; 딆; ) HANGUL SYLLABLE DYILP
+B507;B507;1103 1174 11B6;B507;1103 1174 11B6; # (딇; 딇; 딇; 딇; 딇; ) HANGUL SYLLABLE DYILH
+B508;B508;1103 1174 11B7;B508;1103 1174 11B7; # (딈; 딈; 딈; 딈; 딈; ) HANGUL SYLLABLE DYIM
+B509;B509;1103 1174 11B8;B509;1103 1174 11B8; # (딉; 딉; 딉; 딉; 딉; ) HANGUL SYLLABLE DYIB
+B50A;B50A;1103 1174 11B9;B50A;1103 1174 11B9; # (딊; 딊; 딊; 딊; 딊; ) HANGUL SYLLABLE DYIBS
+B50B;B50B;1103 1174 11BA;B50B;1103 1174 11BA; # (딋; 딋; 딋; 딋; 딋; ) HANGUL SYLLABLE DYIS
+B50C;B50C;1103 1174 11BB;B50C;1103 1174 11BB; # (딌; 딌; 딌; 딌; 딌; ) HANGUL SYLLABLE DYISS
+B50D;B50D;1103 1174 11BC;B50D;1103 1174 11BC; # (ë”; ë”; 딍; ë”; 딍; ) HANGUL SYLLABLE DYING
+B50E;B50E;1103 1174 11BD;B50E;1103 1174 11BD; # (딎; 딎; 딎; 딎; 딎; ) HANGUL SYLLABLE DYIJ
+B50F;B50F;1103 1174 11BE;B50F;1103 1174 11BE; # (ë”; ë”; 딏; ë”; 딏; ) HANGUL SYLLABLE DYIC
+B510;B510;1103 1174 11BF;B510;1103 1174 11BF; # (ë”; ë”; 딐; ë”; 딐; ) HANGUL SYLLABLE DYIK
+B511;B511;1103 1174 11C0;B511;1103 1174 11C0; # (딑; 딑; 딑; 딑; 딑; ) HANGUL SYLLABLE DYIT
+B512;B512;1103 1174 11C1;B512;1103 1174 11C1; # (ë”’; ë”’; 듸á‡; ë”’; 듸á‡; ) HANGUL SYLLABLE DYIP
+B513;B513;1103 1174 11C2;B513;1103 1174 11C2; # (딓; 딓; 딓; 딓; 딓; ) HANGUL SYLLABLE DYIH
+B514;B514;1103 1175;B514;1103 1175; # (디; 디; 디; 디; 디; ) HANGUL SYLLABLE DI
+B515;B515;1103 1175 11A8;B515;1103 1175 11A8; # (딕; 딕; 딕; 딕; 딕; ) HANGUL SYLLABLE DIG
+B516;B516;1103 1175 11A9;B516;1103 1175 11A9; # (딖; 딖; 딖; 딖; 딖; ) HANGUL SYLLABLE DIGG
+B517;B517;1103 1175 11AA;B517;1103 1175 11AA; # (딗; 딗; 딗; 딗; 딗; ) HANGUL SYLLABLE DIGS
+B518;B518;1103 1175 11AB;B518;1103 1175 11AB; # (딘; 딘; 딘; 딘; 딘; ) HANGUL SYLLABLE DIN
+B519;B519;1103 1175 11AC;B519;1103 1175 11AC; # (딙; 딙; 딙; 딙; 딙; ) HANGUL SYLLABLE DINJ
+B51A;B51A;1103 1175 11AD;B51A;1103 1175 11AD; # (딚; 딚; 딚; 딚; 딚; ) HANGUL SYLLABLE DINH
+B51B;B51B;1103 1175 11AE;B51B;1103 1175 11AE; # (딛; 딛; 딛; 딛; 딛; ) HANGUL SYLLABLE DID
+B51C;B51C;1103 1175 11AF;B51C;1103 1175 11AF; # (딜; 딜; 딜; 딜; 딜; ) HANGUL SYLLABLE DIL
+B51D;B51D;1103 1175 11B0;B51D;1103 1175 11B0; # (ë”; ë”; 딝; ë”; 딝; ) HANGUL SYLLABLE DILG
+B51E;B51E;1103 1175 11B1;B51E;1103 1175 11B1; # (딞; 딞; 딞; 딞; 딞; ) HANGUL SYLLABLE DILM
+B51F;B51F;1103 1175 11B2;B51F;1103 1175 11B2; # (딟; 딟; 딟; 딟; 딟; ) HANGUL SYLLABLE DILB
+B520;B520;1103 1175 11B3;B520;1103 1175 11B3; # (딠; 딠; 딠; 딠; 딠; ) HANGUL SYLLABLE DILS
+B521;B521;1103 1175 11B4;B521;1103 1175 11B4; # (딡; 딡; 딡; 딡; 딡; ) HANGUL SYLLABLE DILT
+B522;B522;1103 1175 11B5;B522;1103 1175 11B5; # (딢; 딢; 딢; 딢; 딢; ) HANGUL SYLLABLE DILP
+B523;B523;1103 1175 11B6;B523;1103 1175 11B6; # (딣; 딣; 딣; 딣; 딣; ) HANGUL SYLLABLE DILH
+B524;B524;1103 1175 11B7;B524;1103 1175 11B7; # (딤; 딤; 딤; 딤; 딤; ) HANGUL SYLLABLE DIM
+B525;B525;1103 1175 11B8;B525;1103 1175 11B8; # (딥; 딥; 딥; 딥; 딥; ) HANGUL SYLLABLE DIB
+B526;B526;1103 1175 11B9;B526;1103 1175 11B9; # (딦; 딦; 딦; 딦; 딦; ) HANGUL SYLLABLE DIBS
+B527;B527;1103 1175 11BA;B527;1103 1175 11BA; # (딧; 딧; 딧; 딧; 딧; ) HANGUL SYLLABLE DIS
+B528;B528;1103 1175 11BB;B528;1103 1175 11BB; # (딨; 딨; 딨; 딨; 딨; ) HANGUL SYLLABLE DISS
+B529;B529;1103 1175 11BC;B529;1103 1175 11BC; # (딩; 딩; 딩; 딩; 딩; ) HANGUL SYLLABLE DING
+B52A;B52A;1103 1175 11BD;B52A;1103 1175 11BD; # (딪; 딪; 딪; 딪; 딪; ) HANGUL SYLLABLE DIJ
+B52B;B52B;1103 1175 11BE;B52B;1103 1175 11BE; # (딫; 딫; 딫; 딫; 딫; ) HANGUL SYLLABLE DIC
+B52C;B52C;1103 1175 11BF;B52C;1103 1175 11BF; # (딬; 딬; 딬; 딬; 딬; ) HANGUL SYLLABLE DIK
+B52D;B52D;1103 1175 11C0;B52D;1103 1175 11C0; # (딭; 딭; 딭; 딭; 딭; ) HANGUL SYLLABLE DIT
+B52E;B52E;1103 1175 11C1;B52E;1103 1175 11C1; # (ë”®; ë”®; 디á‡; ë”®; 디á‡; ) HANGUL SYLLABLE DIP
+B52F;B52F;1103 1175 11C2;B52F;1103 1175 11C2; # (딯; 딯; 딯; 딯; 딯; ) HANGUL SYLLABLE DIH
+B530;B530;1104 1161;B530;1104 1161; # (ë”°; ë”°; á„„á…¡; ë”°; á„„á…¡; ) HANGUL SYLLABLE DDA
+B531;B531;1104 1161 11A8;B531;1104 1161 11A8; # (딱; 딱; 딱; 딱; 딱; ) HANGUL SYLLABLE DDAG
+B532;B532;1104 1161 11A9;B532;1104 1161 11A9; # (딲; 딲; 딲; 딲; 딲; ) HANGUL SYLLABLE DDAGG
+B533;B533;1104 1161 11AA;B533;1104 1161 11AA; # (딳; 딳; 딳; 딳; 딳; ) HANGUL SYLLABLE DDAGS
+B534;B534;1104 1161 11AB;B534;1104 1161 11AB; # (딴; 딴; 딴; 딴; 딴; ) HANGUL SYLLABLE DDAN
+B535;B535;1104 1161 11AC;B535;1104 1161 11AC; # (딵; 딵; 딵; 딵; 딵; ) HANGUL SYLLABLE DDANJ
+B536;B536;1104 1161 11AD;B536;1104 1161 11AD; # (딶; 딶; 딶; 딶; 딶; ) HANGUL SYLLABLE DDANH
+B537;B537;1104 1161 11AE;B537;1104 1161 11AE; # (딷; 딷; 딷; 딷; 딷; ) HANGUL SYLLABLE DDAD
+B538;B538;1104 1161 11AF;B538;1104 1161 11AF; # (딸; 딸; 딸; 딸; 딸; ) HANGUL SYLLABLE DDAL
+B539;B539;1104 1161 11B0;B539;1104 1161 11B0; # (딹; 딹; 딹; 딹; 딹; ) HANGUL SYLLABLE DDALG
+B53A;B53A;1104 1161 11B1;B53A;1104 1161 11B1; # (딺; 딺; 딺; 딺; 딺; ) HANGUL SYLLABLE DDALM
+B53B;B53B;1104 1161 11B2;B53B;1104 1161 11B2; # (딻; 딻; 딻; 딻; 딻; ) HANGUL SYLLABLE DDALB
+B53C;B53C;1104 1161 11B3;B53C;1104 1161 11B3; # (딼; 딼; 딼; 딼; 딼; ) HANGUL SYLLABLE DDALS
+B53D;B53D;1104 1161 11B4;B53D;1104 1161 11B4; # (딽; 딽; 딽; 딽; 딽; ) HANGUL SYLLABLE DDALT
+B53E;B53E;1104 1161 11B5;B53E;1104 1161 11B5; # (딾; 딾; 딾; 딾; 딾; ) HANGUL SYLLABLE DDALP
+B53F;B53F;1104 1161 11B6;B53F;1104 1161 11B6; # (딿; 딿; 딿; 딿; 딿; ) HANGUL SYLLABLE DDALH
+B540;B540;1104 1161 11B7;B540;1104 1161 11B7; # (땀; 땀; 땀; 땀; 땀; ) HANGUL SYLLABLE DDAM
+B541;B541;1104 1161 11B8;B541;1104 1161 11B8; # (ë•; ë•; 땁; ë•; 땁; ) HANGUL SYLLABLE DDAB
+B542;B542;1104 1161 11B9;B542;1104 1161 11B9; # (땂; 땂; 땂; 땂; 땂; ) HANGUL SYLLABLE DDABS
+B543;B543;1104 1161 11BA;B543;1104 1161 11BA; # (땃; 땃; 땃; 땃; 땃; ) HANGUL SYLLABLE DDAS
+B544;B544;1104 1161 11BB;B544;1104 1161 11BB; # (땄; 땄; 땄; 땄; 땄; ) HANGUL SYLLABLE DDASS
+B545;B545;1104 1161 11BC;B545;1104 1161 11BC; # (땅; 땅; 땅; 땅; 땅; ) HANGUL SYLLABLE DDANG
+B546;B546;1104 1161 11BD;B546;1104 1161 11BD; # (땆; 땆; 땆; 땆; 땆; ) HANGUL SYLLABLE DDAJ
+B547;B547;1104 1161 11BE;B547;1104 1161 11BE; # (땇; 땇; 땇; 땇; 땇; ) HANGUL SYLLABLE DDAC
+B548;B548;1104 1161 11BF;B548;1104 1161 11BF; # (땈; 땈; 땈; 땈; 땈; ) HANGUL SYLLABLE DDAK
+B549;B549;1104 1161 11C0;B549;1104 1161 11C0; # (땉; 땉; 땉; 땉; 땉; ) HANGUL SYLLABLE DDAT
+B54A;B54A;1104 1161 11C1;B54A;1104 1161 11C1; # (땊; 땊; á„„á…¡á‡; 땊; á„„á…¡á‡; ) HANGUL SYLLABLE DDAP
+B54B;B54B;1104 1161 11C2;B54B;1104 1161 11C2; # (땋; 땋; 땋; 땋; 땋; ) HANGUL SYLLABLE DDAH
+B54C;B54C;1104 1162;B54C;1104 1162; # (때; 때; 때; 때; 때; ) HANGUL SYLLABLE DDAE
+B54D;B54D;1104 1162 11A8;B54D;1104 1162 11A8; # (ë•; ë•; 땍; ë•; 땍; ) HANGUL SYLLABLE DDAEG
+B54E;B54E;1104 1162 11A9;B54E;1104 1162 11A9; # (땎; 땎; 땎; 땎; 땎; ) HANGUL SYLLABLE DDAEGG
+B54F;B54F;1104 1162 11AA;B54F;1104 1162 11AA; # (ë•; ë•; 땏; ë•; 땏; ) HANGUL SYLLABLE DDAEGS
+B550;B550;1104 1162 11AB;B550;1104 1162 11AB; # (ë•; ë•; 땐; ë•; 땐; ) HANGUL SYLLABLE DDAEN
+B551;B551;1104 1162 11AC;B551;1104 1162 11AC; # (땑; 땑; 땑; 땑; 땑; ) HANGUL SYLLABLE DDAENJ
+B552;B552;1104 1162 11AD;B552;1104 1162 11AD; # (땒; 땒; 땒; 땒; 땒; ) HANGUL SYLLABLE DDAENH
+B553;B553;1104 1162 11AE;B553;1104 1162 11AE; # (땓; 땓; 땓; 땓; 땓; ) HANGUL SYLLABLE DDAED
+B554;B554;1104 1162 11AF;B554;1104 1162 11AF; # (땔; 땔; 땔; 땔; 땔; ) HANGUL SYLLABLE DDAEL
+B555;B555;1104 1162 11B0;B555;1104 1162 11B0; # (땕; 땕; 땕; 땕; 땕; ) HANGUL SYLLABLE DDAELG
+B556;B556;1104 1162 11B1;B556;1104 1162 11B1; # (땖; 땖; 땖; 땖; 땖; ) HANGUL SYLLABLE DDAELM
+B557;B557;1104 1162 11B2;B557;1104 1162 11B2; # (땗; 땗; 땗; 땗; 땗; ) HANGUL SYLLABLE DDAELB
+B558;B558;1104 1162 11B3;B558;1104 1162 11B3; # (땘; 땘; 땘; 땘; 땘; ) HANGUL SYLLABLE DDAELS
+B559;B559;1104 1162 11B4;B559;1104 1162 11B4; # (땙; 땙; 땙; 땙; 땙; ) HANGUL SYLLABLE DDAELT
+B55A;B55A;1104 1162 11B5;B55A;1104 1162 11B5; # (땚; 땚; 땚; 땚; 땚; ) HANGUL SYLLABLE DDAELP
+B55B;B55B;1104 1162 11B6;B55B;1104 1162 11B6; # (땛; 땛; 땛; 땛; 땛; ) HANGUL SYLLABLE DDAELH
+B55C;B55C;1104 1162 11B7;B55C;1104 1162 11B7; # (땜; 땜; 땜; 땜; 땜; ) HANGUL SYLLABLE DDAEM
+B55D;B55D;1104 1162 11B8;B55D;1104 1162 11B8; # (ë•; ë•; 땝; ë•; 땝; ) HANGUL SYLLABLE DDAEB
+B55E;B55E;1104 1162 11B9;B55E;1104 1162 11B9; # (땞; 땞; 땞; 땞; 땞; ) HANGUL SYLLABLE DDAEBS
+B55F;B55F;1104 1162 11BA;B55F;1104 1162 11BA; # (땟; 땟; 땟; 땟; 땟; ) HANGUL SYLLABLE DDAES
+B560;B560;1104 1162 11BB;B560;1104 1162 11BB; # (땠; 땠; 땠; 땠; 땠; ) HANGUL SYLLABLE DDAESS
+B561;B561;1104 1162 11BC;B561;1104 1162 11BC; # (땡; 땡; 땡; 땡; 땡; ) HANGUL SYLLABLE DDAENG
+B562;B562;1104 1162 11BD;B562;1104 1162 11BD; # (땢; 땢; 땢; 땢; 땢; ) HANGUL SYLLABLE DDAEJ
+B563;B563;1104 1162 11BE;B563;1104 1162 11BE; # (땣; 땣; 땣; 땣; 땣; ) HANGUL SYLLABLE DDAEC
+B564;B564;1104 1162 11BF;B564;1104 1162 11BF; # (땤; 땤; 땤; 땤; 땤; ) HANGUL SYLLABLE DDAEK
+B565;B565;1104 1162 11C0;B565;1104 1162 11C0; # (땥; 땥; 땥; 땥; 땥; ) HANGUL SYLLABLE DDAET
+B566;B566;1104 1162 11C1;B566;1104 1162 11C1; # (땦; 땦; á„„á…¢á‡; 땦; á„„á…¢á‡; ) HANGUL SYLLABLE DDAEP
+B567;B567;1104 1162 11C2;B567;1104 1162 11C2; # (땧; 땧; 땧; 땧; 땧; ) HANGUL SYLLABLE DDAEH
+B568;B568;1104 1163;B568;1104 1163; # (땨; 땨; 땨; 땨; 땨; ) HANGUL SYLLABLE DDYA
+B569;B569;1104 1163 11A8;B569;1104 1163 11A8; # (땩; 땩; 땩; 땩; 땩; ) HANGUL SYLLABLE DDYAG
+B56A;B56A;1104 1163 11A9;B56A;1104 1163 11A9; # (땪; 땪; 땪; 땪; 땪; ) HANGUL SYLLABLE DDYAGG
+B56B;B56B;1104 1163 11AA;B56B;1104 1163 11AA; # (땫; 땫; 땫; 땫; 땫; ) HANGUL SYLLABLE DDYAGS
+B56C;B56C;1104 1163 11AB;B56C;1104 1163 11AB; # (땬; 땬; 땬; 땬; 땬; ) HANGUL SYLLABLE DDYAN
+B56D;B56D;1104 1163 11AC;B56D;1104 1163 11AC; # (땭; 땭; 땭; 땭; 땭; ) HANGUL SYLLABLE DDYANJ
+B56E;B56E;1104 1163 11AD;B56E;1104 1163 11AD; # (땮; 땮; 땮; 땮; 땮; ) HANGUL SYLLABLE DDYANH
+B56F;B56F;1104 1163 11AE;B56F;1104 1163 11AE; # (땯; 땯; 땯; 땯; 땯; ) HANGUL SYLLABLE DDYAD
+B570;B570;1104 1163 11AF;B570;1104 1163 11AF; # (땰; 땰; 땰; 땰; 땰; ) HANGUL SYLLABLE DDYAL
+B571;B571;1104 1163 11B0;B571;1104 1163 11B0; # (땱; 땱; 땱; 땱; 땱; ) HANGUL SYLLABLE DDYALG
+B572;B572;1104 1163 11B1;B572;1104 1163 11B1; # (땲; 땲; 땲; 땲; 땲; ) HANGUL SYLLABLE DDYALM
+B573;B573;1104 1163 11B2;B573;1104 1163 11B2; # (땳; 땳; 땳; 땳; 땳; ) HANGUL SYLLABLE DDYALB
+B574;B574;1104 1163 11B3;B574;1104 1163 11B3; # (땴; 땴; 땴; 땴; 땴; ) HANGUL SYLLABLE DDYALS
+B575;B575;1104 1163 11B4;B575;1104 1163 11B4; # (땵; 땵; 땵; 땵; 땵; ) HANGUL SYLLABLE DDYALT
+B576;B576;1104 1163 11B5;B576;1104 1163 11B5; # (땶; 땶; 땶; 땶; 땶; ) HANGUL SYLLABLE DDYALP
+B577;B577;1104 1163 11B6;B577;1104 1163 11B6; # (땷; 땷; 땷; 땷; 땷; ) HANGUL SYLLABLE DDYALH
+B578;B578;1104 1163 11B7;B578;1104 1163 11B7; # (땸; 땸; 땸; 땸; 땸; ) HANGUL SYLLABLE DDYAM
+B579;B579;1104 1163 11B8;B579;1104 1163 11B8; # (땹; 땹; 땹; 땹; 땹; ) HANGUL SYLLABLE DDYAB
+B57A;B57A;1104 1163 11B9;B57A;1104 1163 11B9; # (땺; 땺; 땺; 땺; 땺; ) HANGUL SYLLABLE DDYABS
+B57B;B57B;1104 1163 11BA;B57B;1104 1163 11BA; # (땻; 땻; 땻; 땻; 땻; ) HANGUL SYLLABLE DDYAS
+B57C;B57C;1104 1163 11BB;B57C;1104 1163 11BB; # (땼; 땼; 땼; 땼; 땼; ) HANGUL SYLLABLE DDYASS
+B57D;B57D;1104 1163 11BC;B57D;1104 1163 11BC; # (땽; 땽; 땽; 땽; 땽; ) HANGUL SYLLABLE DDYANG
+B57E;B57E;1104 1163 11BD;B57E;1104 1163 11BD; # (땾; 땾; 땾; 땾; 땾; ) HANGUL SYLLABLE DDYAJ
+B57F;B57F;1104 1163 11BE;B57F;1104 1163 11BE; # (땿; 땿; 땿; 땿; 땿; ) HANGUL SYLLABLE DDYAC
+B580;B580;1104 1163 11BF;B580;1104 1163 11BF; # (떀; 떀; 떀; 떀; 떀; ) HANGUL SYLLABLE DDYAK
+B581;B581;1104 1163 11C0;B581;1104 1163 11C0; # (ë–; ë–; 떁; ë–; 떁; ) HANGUL SYLLABLE DDYAT
+B582;B582;1104 1163 11C1;B582;1104 1163 11C1; # (ë–‚; ë–‚; á„„á…£á‡; ë–‚; á„„á…£á‡; ) HANGUL SYLLABLE DDYAP
+B583;B583;1104 1163 11C2;B583;1104 1163 11C2; # (떃; 떃; 떃; 떃; 떃; ) HANGUL SYLLABLE DDYAH
+B584;B584;1104 1164;B584;1104 1164; # (ë–„; ë–„; á„„á…¤; ë–„; á„„á…¤; ) HANGUL SYLLABLE DDYAE
+B585;B585;1104 1164 11A8;B585;1104 1164 11A8; # (떅; 떅; 떅; 떅; 떅; ) HANGUL SYLLABLE DDYAEG
+B586;B586;1104 1164 11A9;B586;1104 1164 11A9; # (떆; 떆; 떆; 떆; 떆; ) HANGUL SYLLABLE DDYAEGG
+B587;B587;1104 1164 11AA;B587;1104 1164 11AA; # (떇; 떇; 떇; 떇; 떇; ) HANGUL SYLLABLE DDYAEGS
+B588;B588;1104 1164 11AB;B588;1104 1164 11AB; # (떈; 떈; 떈; 떈; 떈; ) HANGUL SYLLABLE DDYAEN
+B589;B589;1104 1164 11AC;B589;1104 1164 11AC; # (떉; 떉; 떉; 떉; 떉; ) HANGUL SYLLABLE DDYAENJ
+B58A;B58A;1104 1164 11AD;B58A;1104 1164 11AD; # (떊; 떊; 떊; 떊; 떊; ) HANGUL SYLLABLE DDYAENH
+B58B;B58B;1104 1164 11AE;B58B;1104 1164 11AE; # (떋; 떋; 떋; 떋; 떋; ) HANGUL SYLLABLE DDYAED
+B58C;B58C;1104 1164 11AF;B58C;1104 1164 11AF; # (떌; 떌; 떌; 떌; 떌; ) HANGUL SYLLABLE DDYAEL
+B58D;B58D;1104 1164 11B0;B58D;1104 1164 11B0; # (ë–; ë–; 떍; ë–; 떍; ) HANGUL SYLLABLE DDYAELG
+B58E;B58E;1104 1164 11B1;B58E;1104 1164 11B1; # (떎; 떎; 떎; 떎; 떎; ) HANGUL SYLLABLE DDYAELM
+B58F;B58F;1104 1164 11B2;B58F;1104 1164 11B2; # (ë–; ë–; 떏; ë–; 떏; ) HANGUL SYLLABLE DDYAELB
+B590;B590;1104 1164 11B3;B590;1104 1164 11B3; # (ë–; ë–; 떐; ë–; 떐; ) HANGUL SYLLABLE DDYAELS
+B591;B591;1104 1164 11B4;B591;1104 1164 11B4; # (떑; 떑; 떑; 떑; 떑; ) HANGUL SYLLABLE DDYAELT
+B592;B592;1104 1164 11B5;B592;1104 1164 11B5; # (떒; 떒; 떒; 떒; 떒; ) HANGUL SYLLABLE DDYAELP
+B593;B593;1104 1164 11B6;B593;1104 1164 11B6; # (떓; 떓; 떓; 떓; 떓; ) HANGUL SYLLABLE DDYAELH
+B594;B594;1104 1164 11B7;B594;1104 1164 11B7; # (떔; 떔; 떔; 떔; 떔; ) HANGUL SYLLABLE DDYAEM
+B595;B595;1104 1164 11B8;B595;1104 1164 11B8; # (떕; 떕; 떕; 떕; 떕; ) HANGUL SYLLABLE DDYAEB
+B596;B596;1104 1164 11B9;B596;1104 1164 11B9; # (떖; 떖; 떖; 떖; 떖; ) HANGUL SYLLABLE DDYAEBS
+B597;B597;1104 1164 11BA;B597;1104 1164 11BA; # (떗; 떗; 떗; 떗; 떗; ) HANGUL SYLLABLE DDYAES
+B598;B598;1104 1164 11BB;B598;1104 1164 11BB; # (떘; 떘; 떘; 떘; 떘; ) HANGUL SYLLABLE DDYAESS
+B599;B599;1104 1164 11BC;B599;1104 1164 11BC; # (떙; 떙; 떙; 떙; 떙; ) HANGUL SYLLABLE DDYAENG
+B59A;B59A;1104 1164 11BD;B59A;1104 1164 11BD; # (떚; 떚; 떚; 떚; 떚; ) HANGUL SYLLABLE DDYAEJ
+B59B;B59B;1104 1164 11BE;B59B;1104 1164 11BE; # (떛; 떛; 떛; 떛; 떛; ) HANGUL SYLLABLE DDYAEC
+B59C;B59C;1104 1164 11BF;B59C;1104 1164 11BF; # (떜; 떜; 떜; 떜; 떜; ) HANGUL SYLLABLE DDYAEK
+B59D;B59D;1104 1164 11C0;B59D;1104 1164 11C0; # (ë–; ë–; 떝; ë–; 떝; ) HANGUL SYLLABLE DDYAET
+B59E;B59E;1104 1164 11C1;B59E;1104 1164 11C1; # (ë–ž; ë–ž; á„„á…¤á‡; ë–ž; á„„á…¤á‡; ) HANGUL SYLLABLE DDYAEP
+B59F;B59F;1104 1164 11C2;B59F;1104 1164 11C2; # (떟; 떟; 떟; 떟; 떟; ) HANGUL SYLLABLE DDYAEH
+B5A0;B5A0;1104 1165;B5A0;1104 1165; # (ë– ; ë– ; á„„á…¥; ë– ; á„„á…¥; ) HANGUL SYLLABLE DDEO
+B5A1;B5A1;1104 1165 11A8;B5A1;1104 1165 11A8; # (떡; 떡; 떡; 떡; 떡; ) HANGUL SYLLABLE DDEOG
+B5A2;B5A2;1104 1165 11A9;B5A2;1104 1165 11A9; # (떢; 떢; 떢; 떢; 떢; ) HANGUL SYLLABLE DDEOGG
+B5A3;B5A3;1104 1165 11AA;B5A3;1104 1165 11AA; # (떣; 떣; 떣; 떣; 떣; ) HANGUL SYLLABLE DDEOGS
+B5A4;B5A4;1104 1165 11AB;B5A4;1104 1165 11AB; # (떤; 떤; 떤; 떤; 떤; ) HANGUL SYLLABLE DDEON
+B5A5;B5A5;1104 1165 11AC;B5A5;1104 1165 11AC; # (떥; 떥; 떥; 떥; 떥; ) HANGUL SYLLABLE DDEONJ
+B5A6;B5A6;1104 1165 11AD;B5A6;1104 1165 11AD; # (떦; 떦; 떦; 떦; 떦; ) HANGUL SYLLABLE DDEONH
+B5A7;B5A7;1104 1165 11AE;B5A7;1104 1165 11AE; # (떧; 떧; 떧; 떧; 떧; ) HANGUL SYLLABLE DDEOD
+B5A8;B5A8;1104 1165 11AF;B5A8;1104 1165 11AF; # (떨; 떨; 떨; 떨; 떨; ) HANGUL SYLLABLE DDEOL
+B5A9;B5A9;1104 1165 11B0;B5A9;1104 1165 11B0; # (떩; 떩; 떩; 떩; 떩; ) HANGUL SYLLABLE DDEOLG
+B5AA;B5AA;1104 1165 11B1;B5AA;1104 1165 11B1; # (떪; 떪; 떪; 떪; 떪; ) HANGUL SYLLABLE DDEOLM
+B5AB;B5AB;1104 1165 11B2;B5AB;1104 1165 11B2; # (떫; 떫; 떫; 떫; 떫; ) HANGUL SYLLABLE DDEOLB
+B5AC;B5AC;1104 1165 11B3;B5AC;1104 1165 11B3; # (떬; 떬; 떬; 떬; 떬; ) HANGUL SYLLABLE DDEOLS
+B5AD;B5AD;1104 1165 11B4;B5AD;1104 1165 11B4; # (떭; 떭; 떭; 떭; 떭; ) HANGUL SYLLABLE DDEOLT
+B5AE;B5AE;1104 1165 11B5;B5AE;1104 1165 11B5; # (떮; 떮; 떮; 떮; 떮; ) HANGUL SYLLABLE DDEOLP
+B5AF;B5AF;1104 1165 11B6;B5AF;1104 1165 11B6; # (떯; 떯; 떯; 떯; 떯; ) HANGUL SYLLABLE DDEOLH
+B5B0;B5B0;1104 1165 11B7;B5B0;1104 1165 11B7; # (떰; 떰; 떰; 떰; 떰; ) HANGUL SYLLABLE DDEOM
+B5B1;B5B1;1104 1165 11B8;B5B1;1104 1165 11B8; # (떱; 떱; 떱; 떱; 떱; ) HANGUL SYLLABLE DDEOB
+B5B2;B5B2;1104 1165 11B9;B5B2;1104 1165 11B9; # (떲; 떲; 떲; 떲; 떲; ) HANGUL SYLLABLE DDEOBS
+B5B3;B5B3;1104 1165 11BA;B5B3;1104 1165 11BA; # (떳; 떳; 떳; 떳; 떳; ) HANGUL SYLLABLE DDEOS
+B5B4;B5B4;1104 1165 11BB;B5B4;1104 1165 11BB; # (떴; 떴; 떴; 떴; 떴; ) HANGUL SYLLABLE DDEOSS
+B5B5;B5B5;1104 1165 11BC;B5B5;1104 1165 11BC; # (떵; 떵; 떵; 떵; 떵; ) HANGUL SYLLABLE DDEONG
+B5B6;B5B6;1104 1165 11BD;B5B6;1104 1165 11BD; # (떶; 떶; 떶; 떶; 떶; ) HANGUL SYLLABLE DDEOJ
+B5B7;B5B7;1104 1165 11BE;B5B7;1104 1165 11BE; # (떷; 떷; 떷; 떷; 떷; ) HANGUL SYLLABLE DDEOC
+B5B8;B5B8;1104 1165 11BF;B5B8;1104 1165 11BF; # (떸; 떸; 떸; 떸; 떸; ) HANGUL SYLLABLE DDEOK
+B5B9;B5B9;1104 1165 11C0;B5B9;1104 1165 11C0; # (떹; 떹; 떹; 떹; 떹; ) HANGUL SYLLABLE DDEOT
+B5BA;B5BA;1104 1165 11C1;B5BA;1104 1165 11C1; # (ë–º; ë–º; á„„á…¥á‡; ë–º; á„„á…¥á‡; ) HANGUL SYLLABLE DDEOP
+B5BB;B5BB;1104 1165 11C2;B5BB;1104 1165 11C2; # (떻; 떻; 떻; 떻; 떻; ) HANGUL SYLLABLE DDEOH
+B5BC;B5BC;1104 1166;B5BC;1104 1166; # (ë–¼; ë–¼; á„„á…¦; ë–¼; á„„á…¦; ) HANGUL SYLLABLE DDE
+B5BD;B5BD;1104 1166 11A8;B5BD;1104 1166 11A8; # (떽; 떽; 떽; 떽; 떽; ) HANGUL SYLLABLE DDEG
+B5BE;B5BE;1104 1166 11A9;B5BE;1104 1166 11A9; # (떾; 떾; 떾; 떾; 떾; ) HANGUL SYLLABLE DDEGG
+B5BF;B5BF;1104 1166 11AA;B5BF;1104 1166 11AA; # (떿; 떿; 떿; 떿; 떿; ) HANGUL SYLLABLE DDEGS
+B5C0;B5C0;1104 1166 11AB;B5C0;1104 1166 11AB; # (뗀; 뗀; 뗀; 뗀; 뗀; ) HANGUL SYLLABLE DDEN
+B5C1;B5C1;1104 1166 11AC;B5C1;1104 1166 11AC; # (ë—; ë—; 뗁; ë—; 뗁; ) HANGUL SYLLABLE DDENJ
+B5C2;B5C2;1104 1166 11AD;B5C2;1104 1166 11AD; # (뗂; 뗂; 뗂; 뗂; 뗂; ) HANGUL SYLLABLE DDENH
+B5C3;B5C3;1104 1166 11AE;B5C3;1104 1166 11AE; # (뗃; 뗃; 뗃; 뗃; 뗃; ) HANGUL SYLLABLE DDED
+B5C4;B5C4;1104 1166 11AF;B5C4;1104 1166 11AF; # (뗄; 뗄; 뗄; 뗄; 뗄; ) HANGUL SYLLABLE DDEL
+B5C5;B5C5;1104 1166 11B0;B5C5;1104 1166 11B0; # (뗅; 뗅; 뗅; 뗅; 뗅; ) HANGUL SYLLABLE DDELG
+B5C6;B5C6;1104 1166 11B1;B5C6;1104 1166 11B1; # (뗆; 뗆; 뗆; 뗆; 뗆; ) HANGUL SYLLABLE DDELM
+B5C7;B5C7;1104 1166 11B2;B5C7;1104 1166 11B2; # (뗇; 뗇; 뗇; 뗇; 뗇; ) HANGUL SYLLABLE DDELB
+B5C8;B5C8;1104 1166 11B3;B5C8;1104 1166 11B3; # (뗈; 뗈; 뗈; 뗈; 뗈; ) HANGUL SYLLABLE DDELS
+B5C9;B5C9;1104 1166 11B4;B5C9;1104 1166 11B4; # (뗉; 뗉; 뗉; 뗉; 뗉; ) HANGUL SYLLABLE DDELT
+B5CA;B5CA;1104 1166 11B5;B5CA;1104 1166 11B5; # (뗊; 뗊; 뗊; 뗊; 뗊; ) HANGUL SYLLABLE DDELP
+B5CB;B5CB;1104 1166 11B6;B5CB;1104 1166 11B6; # (뗋; 뗋; 뗋; 뗋; 뗋; ) HANGUL SYLLABLE DDELH
+B5CC;B5CC;1104 1166 11B7;B5CC;1104 1166 11B7; # (뗌; 뗌; 뗌; 뗌; 뗌; ) HANGUL SYLLABLE DDEM
+B5CD;B5CD;1104 1166 11B8;B5CD;1104 1166 11B8; # (ë—; ë—; 뗍; ë—; 뗍; ) HANGUL SYLLABLE DDEB
+B5CE;B5CE;1104 1166 11B9;B5CE;1104 1166 11B9; # (뗎; 뗎; 뗎; 뗎; 뗎; ) HANGUL SYLLABLE DDEBS
+B5CF;B5CF;1104 1166 11BA;B5CF;1104 1166 11BA; # (ë—; ë—; 뗏; ë—; 뗏; ) HANGUL SYLLABLE DDES
+B5D0;B5D0;1104 1166 11BB;B5D0;1104 1166 11BB; # (ë—; ë—; 뗐; ë—; 뗐; ) HANGUL SYLLABLE DDESS
+B5D1;B5D1;1104 1166 11BC;B5D1;1104 1166 11BC; # (뗑; 뗑; 뗑; 뗑; 뗑; ) HANGUL SYLLABLE DDENG
+B5D2;B5D2;1104 1166 11BD;B5D2;1104 1166 11BD; # (뗒; 뗒; 뗒; 뗒; 뗒; ) HANGUL SYLLABLE DDEJ
+B5D3;B5D3;1104 1166 11BE;B5D3;1104 1166 11BE; # (뗓; 뗓; 뗓; 뗓; 뗓; ) HANGUL SYLLABLE DDEC
+B5D4;B5D4;1104 1166 11BF;B5D4;1104 1166 11BF; # (뗔; 뗔; 뗔; 뗔; 뗔; ) HANGUL SYLLABLE DDEK
+B5D5;B5D5;1104 1166 11C0;B5D5;1104 1166 11C0; # (뗕; 뗕; 뗕; 뗕; 뗕; ) HANGUL SYLLABLE DDET
+B5D6;B5D6;1104 1166 11C1;B5D6;1104 1166 11C1; # (ë—–; ë—–; á„„á…¦á‡; ë—–; á„„á…¦á‡; ) HANGUL SYLLABLE DDEP
+B5D7;B5D7;1104 1166 11C2;B5D7;1104 1166 11C2; # (뗗; 뗗; 뗗; 뗗; 뗗; ) HANGUL SYLLABLE DDEH
+B5D8;B5D8;1104 1167;B5D8;1104 1167; # (ë—˜; ë—˜; á„„á…§; ë—˜; á„„á…§; ) HANGUL SYLLABLE DDYEO
+B5D9;B5D9;1104 1167 11A8;B5D9;1104 1167 11A8; # (뗙; 뗙; 뗙; 뗙; 뗙; ) HANGUL SYLLABLE DDYEOG
+B5DA;B5DA;1104 1167 11A9;B5DA;1104 1167 11A9; # (뗚; 뗚; 뗚; 뗚; 뗚; ) HANGUL SYLLABLE DDYEOGG
+B5DB;B5DB;1104 1167 11AA;B5DB;1104 1167 11AA; # (뗛; 뗛; 뗛; 뗛; 뗛; ) HANGUL SYLLABLE DDYEOGS
+B5DC;B5DC;1104 1167 11AB;B5DC;1104 1167 11AB; # (뗜; 뗜; 뗜; 뗜; 뗜; ) HANGUL SYLLABLE DDYEON
+B5DD;B5DD;1104 1167 11AC;B5DD;1104 1167 11AC; # (ë—; ë—; 뗝; ë—; 뗝; ) HANGUL SYLLABLE DDYEONJ
+B5DE;B5DE;1104 1167 11AD;B5DE;1104 1167 11AD; # (뗞; 뗞; 뗞; 뗞; 뗞; ) HANGUL SYLLABLE DDYEONH
+B5DF;B5DF;1104 1167 11AE;B5DF;1104 1167 11AE; # (뗟; 뗟; 뗟; 뗟; 뗟; ) HANGUL SYLLABLE DDYEOD
+B5E0;B5E0;1104 1167 11AF;B5E0;1104 1167 11AF; # (뗠; 뗠; 뗠; 뗠; 뗠; ) HANGUL SYLLABLE DDYEOL
+B5E1;B5E1;1104 1167 11B0;B5E1;1104 1167 11B0; # (뗡; 뗡; 뗡; 뗡; 뗡; ) HANGUL SYLLABLE DDYEOLG
+B5E2;B5E2;1104 1167 11B1;B5E2;1104 1167 11B1; # (뗢; 뗢; 뗢; 뗢; 뗢; ) HANGUL SYLLABLE DDYEOLM
+B5E3;B5E3;1104 1167 11B2;B5E3;1104 1167 11B2; # (뗣; 뗣; 뗣; 뗣; 뗣; ) HANGUL SYLLABLE DDYEOLB
+B5E4;B5E4;1104 1167 11B3;B5E4;1104 1167 11B3; # (뗤; 뗤; 뗤; 뗤; 뗤; ) HANGUL SYLLABLE DDYEOLS
+B5E5;B5E5;1104 1167 11B4;B5E5;1104 1167 11B4; # (뗥; 뗥; 뗥; 뗥; 뗥; ) HANGUL SYLLABLE DDYEOLT
+B5E6;B5E6;1104 1167 11B5;B5E6;1104 1167 11B5; # (뗦; 뗦; 뗦; 뗦; 뗦; ) HANGUL SYLLABLE DDYEOLP
+B5E7;B5E7;1104 1167 11B6;B5E7;1104 1167 11B6; # (뗧; 뗧; 뗧; 뗧; 뗧; ) HANGUL SYLLABLE DDYEOLH
+B5E8;B5E8;1104 1167 11B7;B5E8;1104 1167 11B7; # (뗨; 뗨; 뗨; 뗨; 뗨; ) HANGUL SYLLABLE DDYEOM
+B5E9;B5E9;1104 1167 11B8;B5E9;1104 1167 11B8; # (뗩; 뗩; 뗩; 뗩; 뗩; ) HANGUL SYLLABLE DDYEOB
+B5EA;B5EA;1104 1167 11B9;B5EA;1104 1167 11B9; # (뗪; 뗪; 뗪; 뗪; 뗪; ) HANGUL SYLLABLE DDYEOBS
+B5EB;B5EB;1104 1167 11BA;B5EB;1104 1167 11BA; # (뗫; 뗫; 뗫; 뗫; 뗫; ) HANGUL SYLLABLE DDYEOS
+B5EC;B5EC;1104 1167 11BB;B5EC;1104 1167 11BB; # (뗬; 뗬; 뗬; 뗬; 뗬; ) HANGUL SYLLABLE DDYEOSS
+B5ED;B5ED;1104 1167 11BC;B5ED;1104 1167 11BC; # (뗭; 뗭; 뗭; 뗭; 뗭; ) HANGUL SYLLABLE DDYEONG
+B5EE;B5EE;1104 1167 11BD;B5EE;1104 1167 11BD; # (뗮; 뗮; 뗮; 뗮; 뗮; ) HANGUL SYLLABLE DDYEOJ
+B5EF;B5EF;1104 1167 11BE;B5EF;1104 1167 11BE; # (뗯; 뗯; 뗯; 뗯; 뗯; ) HANGUL SYLLABLE DDYEOC
+B5F0;B5F0;1104 1167 11BF;B5F0;1104 1167 11BF; # (뗰; 뗰; 뗰; 뗰; 뗰; ) HANGUL SYLLABLE DDYEOK
+B5F1;B5F1;1104 1167 11C0;B5F1;1104 1167 11C0; # (뗱; 뗱; 뗱; 뗱; 뗱; ) HANGUL SYLLABLE DDYEOT
+B5F2;B5F2;1104 1167 11C1;B5F2;1104 1167 11C1; # (ë—²; ë—²; á„„á…§á‡; ë—²; á„„á…§á‡; ) HANGUL SYLLABLE DDYEOP
+B5F3;B5F3;1104 1167 11C2;B5F3;1104 1167 11C2; # (뗳; 뗳; 뗳; 뗳; 뗳; ) HANGUL SYLLABLE DDYEOH
+B5F4;B5F4;1104 1168;B5F4;1104 1168; # (ë—´; ë—´; á„„á…¨; ë—´; á„„á…¨; ) HANGUL SYLLABLE DDYE
+B5F5;B5F5;1104 1168 11A8;B5F5;1104 1168 11A8; # (뗵; 뗵; 뗵; 뗵; 뗵; ) HANGUL SYLLABLE DDYEG
+B5F6;B5F6;1104 1168 11A9;B5F6;1104 1168 11A9; # (뗶; 뗶; 뗶; 뗶; 뗶; ) HANGUL SYLLABLE DDYEGG
+B5F7;B5F7;1104 1168 11AA;B5F7;1104 1168 11AA; # (뗷; 뗷; 뗷; 뗷; 뗷; ) HANGUL SYLLABLE DDYEGS
+B5F8;B5F8;1104 1168 11AB;B5F8;1104 1168 11AB; # (뗸; 뗸; 뗸; 뗸; 뗸; ) HANGUL SYLLABLE DDYEN
+B5F9;B5F9;1104 1168 11AC;B5F9;1104 1168 11AC; # (뗹; 뗹; 뗹; 뗹; 뗹; ) HANGUL SYLLABLE DDYENJ
+B5FA;B5FA;1104 1168 11AD;B5FA;1104 1168 11AD; # (뗺; 뗺; 뗺; 뗺; 뗺; ) HANGUL SYLLABLE DDYENH
+B5FB;B5FB;1104 1168 11AE;B5FB;1104 1168 11AE; # (뗻; 뗻; 뗻; 뗻; 뗻; ) HANGUL SYLLABLE DDYED
+B5FC;B5FC;1104 1168 11AF;B5FC;1104 1168 11AF; # (뗼; 뗼; 뗼; 뗼; 뗼; ) HANGUL SYLLABLE DDYEL
+B5FD;B5FD;1104 1168 11B0;B5FD;1104 1168 11B0; # (뗽; 뗽; 뗽; 뗽; 뗽; ) HANGUL SYLLABLE DDYELG
+B5FE;B5FE;1104 1168 11B1;B5FE;1104 1168 11B1; # (뗾; 뗾; 뗾; 뗾; 뗾; ) HANGUL SYLLABLE DDYELM
+B5FF;B5FF;1104 1168 11B2;B5FF;1104 1168 11B2; # (뗿; 뗿; 뗿; 뗿; 뗿; ) HANGUL SYLLABLE DDYELB
+B600;B600;1104 1168 11B3;B600;1104 1168 11B3; # (똀; 똀; 똀; 똀; 똀; ) HANGUL SYLLABLE DDYELS
+B601;B601;1104 1168 11B4;B601;1104 1168 11B4; # (ë˜; ë˜; 똁; ë˜; 똁; ) HANGUL SYLLABLE DDYELT
+B602;B602;1104 1168 11B5;B602;1104 1168 11B5; # (똂; 똂; 똂; 똂; 똂; ) HANGUL SYLLABLE DDYELP
+B603;B603;1104 1168 11B6;B603;1104 1168 11B6; # (똃; 똃; 똃; 똃; 똃; ) HANGUL SYLLABLE DDYELH
+B604;B604;1104 1168 11B7;B604;1104 1168 11B7; # (똄; 똄; 똄; 똄; 똄; ) HANGUL SYLLABLE DDYEM
+B605;B605;1104 1168 11B8;B605;1104 1168 11B8; # (똅; 똅; 똅; 똅; 똅; ) HANGUL SYLLABLE DDYEB
+B606;B606;1104 1168 11B9;B606;1104 1168 11B9; # (똆; 똆; 똆; 똆; 똆; ) HANGUL SYLLABLE DDYEBS
+B607;B607;1104 1168 11BA;B607;1104 1168 11BA; # (똇; 똇; 똇; 똇; 똇; ) HANGUL SYLLABLE DDYES
+B608;B608;1104 1168 11BB;B608;1104 1168 11BB; # (똈; 똈; 똈; 똈; 똈; ) HANGUL SYLLABLE DDYESS
+B609;B609;1104 1168 11BC;B609;1104 1168 11BC; # (똉; 똉; 똉; 똉; 똉; ) HANGUL SYLLABLE DDYENG
+B60A;B60A;1104 1168 11BD;B60A;1104 1168 11BD; # (똊; 똊; 똊; 똊; 똊; ) HANGUL SYLLABLE DDYEJ
+B60B;B60B;1104 1168 11BE;B60B;1104 1168 11BE; # (똋; 똋; 똋; 똋; 똋; ) HANGUL SYLLABLE DDYEC
+B60C;B60C;1104 1168 11BF;B60C;1104 1168 11BF; # (똌; 똌; 똌; 똌; 똌; ) HANGUL SYLLABLE DDYEK
+B60D;B60D;1104 1168 11C0;B60D;1104 1168 11C0; # (ë˜; ë˜; 똍; ë˜; 똍; ) HANGUL SYLLABLE DDYET
+B60E;B60E;1104 1168 11C1;B60E;1104 1168 11C1; # (똎; 똎; á„„á…¨á‡; 똎; á„„á…¨á‡; ) HANGUL SYLLABLE DDYEP
+B60F;B60F;1104 1168 11C2;B60F;1104 1168 11C2; # (ë˜; ë˜; 똏; ë˜; 똏; ) HANGUL SYLLABLE DDYEH
+B610;B610;1104 1169;B610;1104 1169; # (ë˜; ë˜; á„„á…©; ë˜; á„„á…©; ) HANGUL SYLLABLE DDO
+B611;B611;1104 1169 11A8;B611;1104 1169 11A8; # (똑; 똑; 똑; 똑; 똑; ) HANGUL SYLLABLE DDOG
+B612;B612;1104 1169 11A9;B612;1104 1169 11A9; # (똒; 똒; 똒; 똒; 똒; ) HANGUL SYLLABLE DDOGG
+B613;B613;1104 1169 11AA;B613;1104 1169 11AA; # (똓; 똓; 똓; 똓; 똓; ) HANGUL SYLLABLE DDOGS
+B614;B614;1104 1169 11AB;B614;1104 1169 11AB; # (똔; 똔; 똔; 똔; 똔; ) HANGUL SYLLABLE DDON
+B615;B615;1104 1169 11AC;B615;1104 1169 11AC; # (똕; 똕; 똕; 똕; 똕; ) HANGUL SYLLABLE DDONJ
+B616;B616;1104 1169 11AD;B616;1104 1169 11AD; # (똖; 똖; 똖; 똖; 똖; ) HANGUL SYLLABLE DDONH
+B617;B617;1104 1169 11AE;B617;1104 1169 11AE; # (똗; 똗; 똗; 똗; 똗; ) HANGUL SYLLABLE DDOD
+B618;B618;1104 1169 11AF;B618;1104 1169 11AF; # (똘; 똘; 똘; 똘; 똘; ) HANGUL SYLLABLE DDOL
+B619;B619;1104 1169 11B0;B619;1104 1169 11B0; # (똙; 똙; 똙; 똙; 똙; ) HANGUL SYLLABLE DDOLG
+B61A;B61A;1104 1169 11B1;B61A;1104 1169 11B1; # (똚; 똚; 똚; 똚; 똚; ) HANGUL SYLLABLE DDOLM
+B61B;B61B;1104 1169 11B2;B61B;1104 1169 11B2; # (똛; 똛; 똛; 똛; 똛; ) HANGUL SYLLABLE DDOLB
+B61C;B61C;1104 1169 11B3;B61C;1104 1169 11B3; # (똜; 똜; 똜; 똜; 똜; ) HANGUL SYLLABLE DDOLS
+B61D;B61D;1104 1169 11B4;B61D;1104 1169 11B4; # (ë˜; ë˜; 똝; ë˜; 똝; ) HANGUL SYLLABLE DDOLT
+B61E;B61E;1104 1169 11B5;B61E;1104 1169 11B5; # (똞; 똞; 똞; 똞; 똞; ) HANGUL SYLLABLE DDOLP
+B61F;B61F;1104 1169 11B6;B61F;1104 1169 11B6; # (똟; 똟; 똟; 똟; 똟; ) HANGUL SYLLABLE DDOLH
+B620;B620;1104 1169 11B7;B620;1104 1169 11B7; # (똠; 똠; 똠; 똠; 똠; ) HANGUL SYLLABLE DDOM
+B621;B621;1104 1169 11B8;B621;1104 1169 11B8; # (똡; 똡; 똡; 똡; 똡; ) HANGUL SYLLABLE DDOB
+B622;B622;1104 1169 11B9;B622;1104 1169 11B9; # (똢; 똢; 똢; 똢; 똢; ) HANGUL SYLLABLE DDOBS
+B623;B623;1104 1169 11BA;B623;1104 1169 11BA; # (똣; 똣; 똣; 똣; 똣; ) HANGUL SYLLABLE DDOS
+B624;B624;1104 1169 11BB;B624;1104 1169 11BB; # (똤; 똤; 똤; 똤; 똤; ) HANGUL SYLLABLE DDOSS
+B625;B625;1104 1169 11BC;B625;1104 1169 11BC; # (똥; 똥; 똥; 똥; 똥; ) HANGUL SYLLABLE DDONG
+B626;B626;1104 1169 11BD;B626;1104 1169 11BD; # (똦; 똦; 똦; 똦; 똦; ) HANGUL SYLLABLE DDOJ
+B627;B627;1104 1169 11BE;B627;1104 1169 11BE; # (똧; 똧; 똧; 똧; 똧; ) HANGUL SYLLABLE DDOC
+B628;B628;1104 1169 11BF;B628;1104 1169 11BF; # (똨; 똨; 똨; 똨; 똨; ) HANGUL SYLLABLE DDOK
+B629;B629;1104 1169 11C0;B629;1104 1169 11C0; # (똩; 똩; 똩; 똩; 똩; ) HANGUL SYLLABLE DDOT
+B62A;B62A;1104 1169 11C1;B62A;1104 1169 11C1; # (똪; 똪; á„„á…©á‡; 똪; á„„á…©á‡; ) HANGUL SYLLABLE DDOP
+B62B;B62B;1104 1169 11C2;B62B;1104 1169 11C2; # (똫; 똫; 똫; 똫; 똫; ) HANGUL SYLLABLE DDOH
+B62C;B62C;1104 116A;B62C;1104 116A; # (똬; 똬; 똬; 똬; 똬; ) HANGUL SYLLABLE DDWA
+B62D;B62D;1104 116A 11A8;B62D;1104 116A 11A8; # (똭; 똭; 똭; 똭; 똭; ) HANGUL SYLLABLE DDWAG
+B62E;B62E;1104 116A 11A9;B62E;1104 116A 11A9; # (똮; 똮; 똮; 똮; 똮; ) HANGUL SYLLABLE DDWAGG
+B62F;B62F;1104 116A 11AA;B62F;1104 116A 11AA; # (똯; 똯; 똯; 똯; 똯; ) HANGUL SYLLABLE DDWAGS
+B630;B630;1104 116A 11AB;B630;1104 116A 11AB; # (똰; 똰; 똰; 똰; 똰; ) HANGUL SYLLABLE DDWAN
+B631;B631;1104 116A 11AC;B631;1104 116A 11AC; # (똱; 똱; 똱; 똱; 똱; ) HANGUL SYLLABLE DDWANJ
+B632;B632;1104 116A 11AD;B632;1104 116A 11AD; # (똲; 똲; 똲; 똲; 똲; ) HANGUL SYLLABLE DDWANH
+B633;B633;1104 116A 11AE;B633;1104 116A 11AE; # (똳; 똳; 똳; 똳; 똳; ) HANGUL SYLLABLE DDWAD
+B634;B634;1104 116A 11AF;B634;1104 116A 11AF; # (똴; 똴; 똴; 똴; 똴; ) HANGUL SYLLABLE DDWAL
+B635;B635;1104 116A 11B0;B635;1104 116A 11B0; # (똵; 똵; 똵; 똵; 똵; ) HANGUL SYLLABLE DDWALG
+B636;B636;1104 116A 11B1;B636;1104 116A 11B1; # (똶; 똶; 똶; 똶; 똶; ) HANGUL SYLLABLE DDWALM
+B637;B637;1104 116A 11B2;B637;1104 116A 11B2; # (똷; 똷; 똷; 똷; 똷; ) HANGUL SYLLABLE DDWALB
+B638;B638;1104 116A 11B3;B638;1104 116A 11B3; # (똸; 똸; 똸; 똸; 똸; ) HANGUL SYLLABLE DDWALS
+B639;B639;1104 116A 11B4;B639;1104 116A 11B4; # (똹; 똹; 똹; 똹; 똹; ) HANGUL SYLLABLE DDWALT
+B63A;B63A;1104 116A 11B5;B63A;1104 116A 11B5; # (똺; 똺; 똺; 똺; 똺; ) HANGUL SYLLABLE DDWALP
+B63B;B63B;1104 116A 11B6;B63B;1104 116A 11B6; # (똻; 똻; 똻; 똻; 똻; ) HANGUL SYLLABLE DDWALH
+B63C;B63C;1104 116A 11B7;B63C;1104 116A 11B7; # (똼; 똼; 똼; 똼; 똼; ) HANGUL SYLLABLE DDWAM
+B63D;B63D;1104 116A 11B8;B63D;1104 116A 11B8; # (똽; 똽; 똽; 똽; 똽; ) HANGUL SYLLABLE DDWAB
+B63E;B63E;1104 116A 11B9;B63E;1104 116A 11B9; # (똾; 똾; 똾; 똾; 똾; ) HANGUL SYLLABLE DDWABS
+B63F;B63F;1104 116A 11BA;B63F;1104 116A 11BA; # (똿; 똿; 똿; 똿; 똿; ) HANGUL SYLLABLE DDWAS
+B640;B640;1104 116A 11BB;B640;1104 116A 11BB; # (뙀; 뙀; 뙀; 뙀; 뙀; ) HANGUL SYLLABLE DDWASS
+B641;B641;1104 116A 11BC;B641;1104 116A 11BC; # (ë™; ë™; 뙁; ë™; 뙁; ) HANGUL SYLLABLE DDWANG
+B642;B642;1104 116A 11BD;B642;1104 116A 11BD; # (뙂; 뙂; 뙂; 뙂; 뙂; ) HANGUL SYLLABLE DDWAJ
+B643;B643;1104 116A 11BE;B643;1104 116A 11BE; # (뙃; 뙃; 뙃; 뙃; 뙃; ) HANGUL SYLLABLE DDWAC
+B644;B644;1104 116A 11BF;B644;1104 116A 11BF; # (뙄; 뙄; 뙄; 뙄; 뙄; ) HANGUL SYLLABLE DDWAK
+B645;B645;1104 116A 11C0;B645;1104 116A 11C0; # (뙅; 뙅; 뙅; 뙅; 뙅; ) HANGUL SYLLABLE DDWAT
+B646;B646;1104 116A 11C1;B646;1104 116A 11C1; # (뙆; 뙆; á„„á…ªá‡; 뙆; á„„á…ªá‡; ) HANGUL SYLLABLE DDWAP
+B647;B647;1104 116A 11C2;B647;1104 116A 11C2; # (뙇; 뙇; 뙇; 뙇; 뙇; ) HANGUL SYLLABLE DDWAH
+B648;B648;1104 116B;B648;1104 116B; # (뙈; 뙈; 뙈; 뙈; 뙈; ) HANGUL SYLLABLE DDWAE
+B649;B649;1104 116B 11A8;B649;1104 116B 11A8; # (뙉; 뙉; 뙉; 뙉; 뙉; ) HANGUL SYLLABLE DDWAEG
+B64A;B64A;1104 116B 11A9;B64A;1104 116B 11A9; # (뙊; 뙊; 뙊; 뙊; 뙊; ) HANGUL SYLLABLE DDWAEGG
+B64B;B64B;1104 116B 11AA;B64B;1104 116B 11AA; # (뙋; 뙋; 뙋; 뙋; 뙋; ) HANGUL SYLLABLE DDWAEGS
+B64C;B64C;1104 116B 11AB;B64C;1104 116B 11AB; # (뙌; 뙌; 뙌; 뙌; 뙌; ) HANGUL SYLLABLE DDWAEN
+B64D;B64D;1104 116B 11AC;B64D;1104 116B 11AC; # (ë™; ë™; 뙍; ë™; 뙍; ) HANGUL SYLLABLE DDWAENJ
+B64E;B64E;1104 116B 11AD;B64E;1104 116B 11AD; # (뙎; 뙎; 뙎; 뙎; 뙎; ) HANGUL SYLLABLE DDWAENH
+B64F;B64F;1104 116B 11AE;B64F;1104 116B 11AE; # (ë™; ë™; 뙏; ë™; 뙏; ) HANGUL SYLLABLE DDWAED
+B650;B650;1104 116B 11AF;B650;1104 116B 11AF; # (ë™; ë™; 뙐; ë™; 뙐; ) HANGUL SYLLABLE DDWAEL
+B651;B651;1104 116B 11B0;B651;1104 116B 11B0; # (뙑; 뙑; 뙑; 뙑; 뙑; ) HANGUL SYLLABLE DDWAELG
+B652;B652;1104 116B 11B1;B652;1104 116B 11B1; # (뙒; 뙒; 뙒; 뙒; 뙒; ) HANGUL SYLLABLE DDWAELM
+B653;B653;1104 116B 11B2;B653;1104 116B 11B2; # (뙓; 뙓; 뙓; 뙓; 뙓; ) HANGUL SYLLABLE DDWAELB
+B654;B654;1104 116B 11B3;B654;1104 116B 11B3; # (뙔; 뙔; 뙔; 뙔; 뙔; ) HANGUL SYLLABLE DDWAELS
+B655;B655;1104 116B 11B4;B655;1104 116B 11B4; # (뙕; 뙕; 뙕; 뙕; 뙕; ) HANGUL SYLLABLE DDWAELT
+B656;B656;1104 116B 11B5;B656;1104 116B 11B5; # (뙖; 뙖; 뙖; 뙖; 뙖; ) HANGUL SYLLABLE DDWAELP
+B657;B657;1104 116B 11B6;B657;1104 116B 11B6; # (뙗; 뙗; 뙗; 뙗; 뙗; ) HANGUL SYLLABLE DDWAELH
+B658;B658;1104 116B 11B7;B658;1104 116B 11B7; # (뙘; 뙘; 뙘; 뙘; 뙘; ) HANGUL SYLLABLE DDWAEM
+B659;B659;1104 116B 11B8;B659;1104 116B 11B8; # (뙙; 뙙; 뙙; 뙙; 뙙; ) HANGUL SYLLABLE DDWAEB
+B65A;B65A;1104 116B 11B9;B65A;1104 116B 11B9; # (뙚; 뙚; 뙚; 뙚; 뙚; ) HANGUL SYLLABLE DDWAEBS
+B65B;B65B;1104 116B 11BA;B65B;1104 116B 11BA; # (뙛; 뙛; 뙛; 뙛; 뙛; ) HANGUL SYLLABLE DDWAES
+B65C;B65C;1104 116B 11BB;B65C;1104 116B 11BB; # (뙜; 뙜; 뙜; 뙜; 뙜; ) HANGUL SYLLABLE DDWAESS
+B65D;B65D;1104 116B 11BC;B65D;1104 116B 11BC; # (ë™; ë™; 뙝; ë™; 뙝; ) HANGUL SYLLABLE DDWAENG
+B65E;B65E;1104 116B 11BD;B65E;1104 116B 11BD; # (뙞; 뙞; 뙞; 뙞; 뙞; ) HANGUL SYLLABLE DDWAEJ
+B65F;B65F;1104 116B 11BE;B65F;1104 116B 11BE; # (뙟; 뙟; 뙟; 뙟; 뙟; ) HANGUL SYLLABLE DDWAEC
+B660;B660;1104 116B 11BF;B660;1104 116B 11BF; # (뙠; 뙠; 뙠; 뙠; 뙠; ) HANGUL SYLLABLE DDWAEK
+B661;B661;1104 116B 11C0;B661;1104 116B 11C0; # (뙡; 뙡; 뙡; 뙡; 뙡; ) HANGUL SYLLABLE DDWAET
+B662;B662;1104 116B 11C1;B662;1104 116B 11C1; # (뙢; 뙢; á„„á…«á‡; 뙢; á„„á…«á‡; ) HANGUL SYLLABLE DDWAEP
+B663;B663;1104 116B 11C2;B663;1104 116B 11C2; # (뙣; 뙣; 뙣; 뙣; 뙣; ) HANGUL SYLLABLE DDWAEH
+B664;B664;1104 116C;B664;1104 116C; # (뙤; 뙤; 뙤; 뙤; 뙤; ) HANGUL SYLLABLE DDOE
+B665;B665;1104 116C 11A8;B665;1104 116C 11A8; # (뙥; 뙥; 뙥; 뙥; 뙥; ) HANGUL SYLLABLE DDOEG
+B666;B666;1104 116C 11A9;B666;1104 116C 11A9; # (뙦; 뙦; 뙦; 뙦; 뙦; ) HANGUL SYLLABLE DDOEGG
+B667;B667;1104 116C 11AA;B667;1104 116C 11AA; # (뙧; 뙧; 뙧; 뙧; 뙧; ) HANGUL SYLLABLE DDOEGS
+B668;B668;1104 116C 11AB;B668;1104 116C 11AB; # (뙨; 뙨; 뙨; 뙨; 뙨; ) HANGUL SYLLABLE DDOEN
+B669;B669;1104 116C 11AC;B669;1104 116C 11AC; # (뙩; 뙩; 뙩; 뙩; 뙩; ) HANGUL SYLLABLE DDOENJ
+B66A;B66A;1104 116C 11AD;B66A;1104 116C 11AD; # (뙪; 뙪; 뙪; 뙪; 뙪; ) HANGUL SYLLABLE DDOENH
+B66B;B66B;1104 116C 11AE;B66B;1104 116C 11AE; # (뙫; 뙫; 뙫; 뙫; 뙫; ) HANGUL SYLLABLE DDOED
+B66C;B66C;1104 116C 11AF;B66C;1104 116C 11AF; # (뙬; 뙬; 뙬; 뙬; 뙬; ) HANGUL SYLLABLE DDOEL
+B66D;B66D;1104 116C 11B0;B66D;1104 116C 11B0; # (뙭; 뙭; 뙭; 뙭; 뙭; ) HANGUL SYLLABLE DDOELG
+B66E;B66E;1104 116C 11B1;B66E;1104 116C 11B1; # (뙮; 뙮; 뙮; 뙮; 뙮; ) HANGUL SYLLABLE DDOELM
+B66F;B66F;1104 116C 11B2;B66F;1104 116C 11B2; # (뙯; 뙯; 뙯; 뙯; 뙯; ) HANGUL SYLLABLE DDOELB
+B670;B670;1104 116C 11B3;B670;1104 116C 11B3; # (뙰; 뙰; 뙰; 뙰; 뙰; ) HANGUL SYLLABLE DDOELS
+B671;B671;1104 116C 11B4;B671;1104 116C 11B4; # (뙱; 뙱; 뙱; 뙱; 뙱; ) HANGUL SYLLABLE DDOELT
+B672;B672;1104 116C 11B5;B672;1104 116C 11B5; # (뙲; 뙲; 뙲; 뙲; 뙲; ) HANGUL SYLLABLE DDOELP
+B673;B673;1104 116C 11B6;B673;1104 116C 11B6; # (뙳; 뙳; 뙳; 뙳; 뙳; ) HANGUL SYLLABLE DDOELH
+B674;B674;1104 116C 11B7;B674;1104 116C 11B7; # (뙴; 뙴; 뙴; 뙴; 뙴; ) HANGUL SYLLABLE DDOEM
+B675;B675;1104 116C 11B8;B675;1104 116C 11B8; # (뙵; 뙵; 뙵; 뙵; 뙵; ) HANGUL SYLLABLE DDOEB
+B676;B676;1104 116C 11B9;B676;1104 116C 11B9; # (뙶; 뙶; 뙶; 뙶; 뙶; ) HANGUL SYLLABLE DDOEBS
+B677;B677;1104 116C 11BA;B677;1104 116C 11BA; # (뙷; 뙷; 뙷; 뙷; 뙷; ) HANGUL SYLLABLE DDOES
+B678;B678;1104 116C 11BB;B678;1104 116C 11BB; # (뙸; 뙸; 뙸; 뙸; 뙸; ) HANGUL SYLLABLE DDOESS
+B679;B679;1104 116C 11BC;B679;1104 116C 11BC; # (뙹; 뙹; 뙹; 뙹; 뙹; ) HANGUL SYLLABLE DDOENG
+B67A;B67A;1104 116C 11BD;B67A;1104 116C 11BD; # (뙺; 뙺; 뙺; 뙺; 뙺; ) HANGUL SYLLABLE DDOEJ
+B67B;B67B;1104 116C 11BE;B67B;1104 116C 11BE; # (뙻; 뙻; 뙻; 뙻; 뙻; ) HANGUL SYLLABLE DDOEC
+B67C;B67C;1104 116C 11BF;B67C;1104 116C 11BF; # (뙼; 뙼; 뙼; 뙼; 뙼; ) HANGUL SYLLABLE DDOEK
+B67D;B67D;1104 116C 11C0;B67D;1104 116C 11C0; # (뙽; 뙽; 뙽; 뙽; 뙽; ) HANGUL SYLLABLE DDOET
+B67E;B67E;1104 116C 11C1;B67E;1104 116C 11C1; # (뙾; 뙾; á„„á…¬á‡; 뙾; á„„á…¬á‡; ) HANGUL SYLLABLE DDOEP
+B67F;B67F;1104 116C 11C2;B67F;1104 116C 11C2; # (뙿; 뙿; 뙿; 뙿; 뙿; ) HANGUL SYLLABLE DDOEH
+B680;B680;1104 116D;B680;1104 116D; # (뚀; 뚀; 뚀; 뚀; 뚀; ) HANGUL SYLLABLE DDYO
+B681;B681;1104 116D 11A8;B681;1104 116D 11A8; # (ëš; ëš; 뚁; ëš; 뚁; ) HANGUL SYLLABLE DDYOG
+B682;B682;1104 116D 11A9;B682;1104 116D 11A9; # (뚂; 뚂; 뚂; 뚂; 뚂; ) HANGUL SYLLABLE DDYOGG
+B683;B683;1104 116D 11AA;B683;1104 116D 11AA; # (뚃; 뚃; 뚃; 뚃; 뚃; ) HANGUL SYLLABLE DDYOGS
+B684;B684;1104 116D 11AB;B684;1104 116D 11AB; # (뚄; 뚄; 뚄; 뚄; 뚄; ) HANGUL SYLLABLE DDYON
+B685;B685;1104 116D 11AC;B685;1104 116D 11AC; # (뚅; 뚅; 뚅; 뚅; 뚅; ) HANGUL SYLLABLE DDYONJ
+B686;B686;1104 116D 11AD;B686;1104 116D 11AD; # (뚆; 뚆; 뚆; 뚆; 뚆; ) HANGUL SYLLABLE DDYONH
+B687;B687;1104 116D 11AE;B687;1104 116D 11AE; # (뚇; 뚇; 뚇; 뚇; 뚇; ) HANGUL SYLLABLE DDYOD
+B688;B688;1104 116D 11AF;B688;1104 116D 11AF; # (뚈; 뚈; 뚈; 뚈; 뚈; ) HANGUL SYLLABLE DDYOL
+B689;B689;1104 116D 11B0;B689;1104 116D 11B0; # (뚉; 뚉; 뚉; 뚉; 뚉; ) HANGUL SYLLABLE DDYOLG
+B68A;B68A;1104 116D 11B1;B68A;1104 116D 11B1; # (뚊; 뚊; 뚊; 뚊; 뚊; ) HANGUL SYLLABLE DDYOLM
+B68B;B68B;1104 116D 11B2;B68B;1104 116D 11B2; # (뚋; 뚋; 뚋; 뚋; 뚋; ) HANGUL SYLLABLE DDYOLB
+B68C;B68C;1104 116D 11B3;B68C;1104 116D 11B3; # (뚌; 뚌; 뚌; 뚌; 뚌; ) HANGUL SYLLABLE DDYOLS
+B68D;B68D;1104 116D 11B4;B68D;1104 116D 11B4; # (ëš; ëš; 뚍; ëš; 뚍; ) HANGUL SYLLABLE DDYOLT
+B68E;B68E;1104 116D 11B5;B68E;1104 116D 11B5; # (뚎; 뚎; 뚎; 뚎; 뚎; ) HANGUL SYLLABLE DDYOLP
+B68F;B68F;1104 116D 11B6;B68F;1104 116D 11B6; # (ëš; ëš; 뚏; ëš; 뚏; ) HANGUL SYLLABLE DDYOLH
+B690;B690;1104 116D 11B7;B690;1104 116D 11B7; # (ëš; ëš; 뚐; ëš; 뚐; ) HANGUL SYLLABLE DDYOM
+B691;B691;1104 116D 11B8;B691;1104 116D 11B8; # (뚑; 뚑; 뚑; 뚑; 뚑; ) HANGUL SYLLABLE DDYOB
+B692;B692;1104 116D 11B9;B692;1104 116D 11B9; # (뚒; 뚒; 뚒; 뚒; 뚒; ) HANGUL SYLLABLE DDYOBS
+B693;B693;1104 116D 11BA;B693;1104 116D 11BA; # (뚓; 뚓; 뚓; 뚓; 뚓; ) HANGUL SYLLABLE DDYOS
+B694;B694;1104 116D 11BB;B694;1104 116D 11BB; # (뚔; 뚔; 뚔; 뚔; 뚔; ) HANGUL SYLLABLE DDYOSS
+B695;B695;1104 116D 11BC;B695;1104 116D 11BC; # (뚕; 뚕; 뚕; 뚕; 뚕; ) HANGUL SYLLABLE DDYONG
+B696;B696;1104 116D 11BD;B696;1104 116D 11BD; # (뚖; 뚖; 뚖; 뚖; 뚖; ) HANGUL SYLLABLE DDYOJ
+B697;B697;1104 116D 11BE;B697;1104 116D 11BE; # (뚗; 뚗; 뚗; 뚗; 뚗; ) HANGUL SYLLABLE DDYOC
+B698;B698;1104 116D 11BF;B698;1104 116D 11BF; # (뚘; 뚘; 뚘; 뚘; 뚘; ) HANGUL SYLLABLE DDYOK
+B699;B699;1104 116D 11C0;B699;1104 116D 11C0; # (뚙; 뚙; 뚙; 뚙; 뚙; ) HANGUL SYLLABLE DDYOT
+B69A;B69A;1104 116D 11C1;B69A;1104 116D 11C1; # (ëšš; ëšš; á„„á…­á‡; ëšš; á„„á…­á‡; ) HANGUL SYLLABLE DDYOP
+B69B;B69B;1104 116D 11C2;B69B;1104 116D 11C2; # (뚛; 뚛; 뚛; 뚛; 뚛; ) HANGUL SYLLABLE DDYOH
+B69C;B69C;1104 116E;B69C;1104 116E; # (뚜; 뚜; 뚜; 뚜; 뚜; ) HANGUL SYLLABLE DDU
+B69D;B69D;1104 116E 11A8;B69D;1104 116E 11A8; # (ëš; ëš; 뚝; ëš; 뚝; ) HANGUL SYLLABLE DDUG
+B69E;B69E;1104 116E 11A9;B69E;1104 116E 11A9; # (뚞; 뚞; 뚞; 뚞; 뚞; ) HANGUL SYLLABLE DDUGG
+B69F;B69F;1104 116E 11AA;B69F;1104 116E 11AA; # (뚟; 뚟; 뚟; 뚟; 뚟; ) HANGUL SYLLABLE DDUGS
+B6A0;B6A0;1104 116E 11AB;B6A0;1104 116E 11AB; # (뚠; 뚠; 뚠; 뚠; 뚠; ) HANGUL SYLLABLE DDUN
+B6A1;B6A1;1104 116E 11AC;B6A1;1104 116E 11AC; # (뚡; 뚡; 뚡; 뚡; 뚡; ) HANGUL SYLLABLE DDUNJ
+B6A2;B6A2;1104 116E 11AD;B6A2;1104 116E 11AD; # (뚢; 뚢; 뚢; 뚢; 뚢; ) HANGUL SYLLABLE DDUNH
+B6A3;B6A3;1104 116E 11AE;B6A3;1104 116E 11AE; # (뚣; 뚣; 뚣; 뚣; 뚣; ) HANGUL SYLLABLE DDUD
+B6A4;B6A4;1104 116E 11AF;B6A4;1104 116E 11AF; # (뚤; 뚤; 뚤; 뚤; 뚤; ) HANGUL SYLLABLE DDUL
+B6A5;B6A5;1104 116E 11B0;B6A5;1104 116E 11B0; # (뚥; 뚥; 뚥; 뚥; 뚥; ) HANGUL SYLLABLE DDULG
+B6A6;B6A6;1104 116E 11B1;B6A6;1104 116E 11B1; # (뚦; 뚦; 뚦; 뚦; 뚦; ) HANGUL SYLLABLE DDULM
+B6A7;B6A7;1104 116E 11B2;B6A7;1104 116E 11B2; # (뚧; 뚧; 뚧; 뚧; 뚧; ) HANGUL SYLLABLE DDULB
+B6A8;B6A8;1104 116E 11B3;B6A8;1104 116E 11B3; # (뚨; 뚨; 뚨; 뚨; 뚨; ) HANGUL SYLLABLE DDULS
+B6A9;B6A9;1104 116E 11B4;B6A9;1104 116E 11B4; # (뚩; 뚩; 뚩; 뚩; 뚩; ) HANGUL SYLLABLE DDULT
+B6AA;B6AA;1104 116E 11B5;B6AA;1104 116E 11B5; # (뚪; 뚪; 뚪; 뚪; 뚪; ) HANGUL SYLLABLE DDULP
+B6AB;B6AB;1104 116E 11B6;B6AB;1104 116E 11B6; # (뚫; 뚫; 뚫; 뚫; 뚫; ) HANGUL SYLLABLE DDULH
+B6AC;B6AC;1104 116E 11B7;B6AC;1104 116E 11B7; # (뚬; 뚬; 뚬; 뚬; 뚬; ) HANGUL SYLLABLE DDUM
+B6AD;B6AD;1104 116E 11B8;B6AD;1104 116E 11B8; # (뚭; 뚭; 뚭; 뚭; 뚭; ) HANGUL SYLLABLE DDUB
+B6AE;B6AE;1104 116E 11B9;B6AE;1104 116E 11B9; # (뚮; 뚮; 뚮; 뚮; 뚮; ) HANGUL SYLLABLE DDUBS
+B6AF;B6AF;1104 116E 11BA;B6AF;1104 116E 11BA; # (뚯; 뚯; 뚯; 뚯; 뚯; ) HANGUL SYLLABLE DDUS
+B6B0;B6B0;1104 116E 11BB;B6B0;1104 116E 11BB; # (뚰; 뚰; 뚰; 뚰; 뚰; ) HANGUL SYLLABLE DDUSS
+B6B1;B6B1;1104 116E 11BC;B6B1;1104 116E 11BC; # (뚱; 뚱; 뚱; 뚱; 뚱; ) HANGUL SYLLABLE DDUNG
+B6B2;B6B2;1104 116E 11BD;B6B2;1104 116E 11BD; # (뚲; 뚲; 뚲; 뚲; 뚲; ) HANGUL SYLLABLE DDUJ
+B6B3;B6B3;1104 116E 11BE;B6B3;1104 116E 11BE; # (뚳; 뚳; 뚳; 뚳; 뚳; ) HANGUL SYLLABLE DDUC
+B6B4;B6B4;1104 116E 11BF;B6B4;1104 116E 11BF; # (뚴; 뚴; 뚴; 뚴; 뚴; ) HANGUL SYLLABLE DDUK
+B6B5;B6B5;1104 116E 11C0;B6B5;1104 116E 11C0; # (뚵; 뚵; 뚵; 뚵; 뚵; ) HANGUL SYLLABLE DDUT
+B6B6;B6B6;1104 116E 11C1;B6B6;1104 116E 11C1; # (ëš¶; ëš¶; á„„á…®á‡; ëš¶; á„„á…®á‡; ) HANGUL SYLLABLE DDUP
+B6B7;B6B7;1104 116E 11C2;B6B7;1104 116E 11C2; # (뚷; 뚷; 뚷; 뚷; 뚷; ) HANGUL SYLLABLE DDUH
+B6B8;B6B8;1104 116F;B6B8;1104 116F; # (뚸; 뚸; 뚸; 뚸; 뚸; ) HANGUL SYLLABLE DDWEO
+B6B9;B6B9;1104 116F 11A8;B6B9;1104 116F 11A8; # (뚹; 뚹; 뚹; 뚹; 뚹; ) HANGUL SYLLABLE DDWEOG
+B6BA;B6BA;1104 116F 11A9;B6BA;1104 116F 11A9; # (뚺; 뚺; 뚺; 뚺; 뚺; ) HANGUL SYLLABLE DDWEOGG
+B6BB;B6BB;1104 116F 11AA;B6BB;1104 116F 11AA; # (뚻; 뚻; 뚻; 뚻; 뚻; ) HANGUL SYLLABLE DDWEOGS
+B6BC;B6BC;1104 116F 11AB;B6BC;1104 116F 11AB; # (뚼; 뚼; 뚼; 뚼; 뚼; ) HANGUL SYLLABLE DDWEON
+B6BD;B6BD;1104 116F 11AC;B6BD;1104 116F 11AC; # (뚽; 뚽; 뚽; 뚽; 뚽; ) HANGUL SYLLABLE DDWEONJ
+B6BE;B6BE;1104 116F 11AD;B6BE;1104 116F 11AD; # (뚾; 뚾; 뚾; 뚾; 뚾; ) HANGUL SYLLABLE DDWEONH
+B6BF;B6BF;1104 116F 11AE;B6BF;1104 116F 11AE; # (뚿; 뚿; 뚿; 뚿; 뚿; ) HANGUL SYLLABLE DDWEOD
+B6C0;B6C0;1104 116F 11AF;B6C0;1104 116F 11AF; # (뛀; 뛀; 뛀; 뛀; 뛀; ) HANGUL SYLLABLE DDWEOL
+B6C1;B6C1;1104 116F 11B0;B6C1;1104 116F 11B0; # (ë›; ë›; 뛁; ë›; 뛁; ) HANGUL SYLLABLE DDWEOLG
+B6C2;B6C2;1104 116F 11B1;B6C2;1104 116F 11B1; # (뛂; 뛂; 뛂; 뛂; 뛂; ) HANGUL SYLLABLE DDWEOLM
+B6C3;B6C3;1104 116F 11B2;B6C3;1104 116F 11B2; # (뛃; 뛃; 뛃; 뛃; 뛃; ) HANGUL SYLLABLE DDWEOLB
+B6C4;B6C4;1104 116F 11B3;B6C4;1104 116F 11B3; # (뛄; 뛄; 뛄; 뛄; 뛄; ) HANGUL SYLLABLE DDWEOLS
+B6C5;B6C5;1104 116F 11B4;B6C5;1104 116F 11B4; # (뛅; 뛅; 뛅; 뛅; 뛅; ) HANGUL SYLLABLE DDWEOLT
+B6C6;B6C6;1104 116F 11B5;B6C6;1104 116F 11B5; # (뛆; 뛆; 뛆; 뛆; 뛆; ) HANGUL SYLLABLE DDWEOLP
+B6C7;B6C7;1104 116F 11B6;B6C7;1104 116F 11B6; # (뛇; 뛇; 뛇; 뛇; 뛇; ) HANGUL SYLLABLE DDWEOLH
+B6C8;B6C8;1104 116F 11B7;B6C8;1104 116F 11B7; # (뛈; 뛈; 뛈; 뛈; 뛈; ) HANGUL SYLLABLE DDWEOM
+B6C9;B6C9;1104 116F 11B8;B6C9;1104 116F 11B8; # (뛉; 뛉; 뛉; 뛉; 뛉; ) HANGUL SYLLABLE DDWEOB
+B6CA;B6CA;1104 116F 11B9;B6CA;1104 116F 11B9; # (뛊; 뛊; 뛊; 뛊; 뛊; ) HANGUL SYLLABLE DDWEOBS
+B6CB;B6CB;1104 116F 11BA;B6CB;1104 116F 11BA; # (뛋; 뛋; 뛋; 뛋; 뛋; ) HANGUL SYLLABLE DDWEOS
+B6CC;B6CC;1104 116F 11BB;B6CC;1104 116F 11BB; # (뛌; 뛌; 뛌; 뛌; 뛌; ) HANGUL SYLLABLE DDWEOSS
+B6CD;B6CD;1104 116F 11BC;B6CD;1104 116F 11BC; # (ë›; ë›; 뛍; ë›; 뛍; ) HANGUL SYLLABLE DDWEONG
+B6CE;B6CE;1104 116F 11BD;B6CE;1104 116F 11BD; # (뛎; 뛎; 뛎; 뛎; 뛎; ) HANGUL SYLLABLE DDWEOJ
+B6CF;B6CF;1104 116F 11BE;B6CF;1104 116F 11BE; # (ë›; ë›; 뛏; ë›; 뛏; ) HANGUL SYLLABLE DDWEOC
+B6D0;B6D0;1104 116F 11BF;B6D0;1104 116F 11BF; # (ë›; ë›; 뛐; ë›; 뛐; ) HANGUL SYLLABLE DDWEOK
+B6D1;B6D1;1104 116F 11C0;B6D1;1104 116F 11C0; # (뛑; 뛑; 뛑; 뛑; 뛑; ) HANGUL SYLLABLE DDWEOT
+B6D2;B6D2;1104 116F 11C1;B6D2;1104 116F 11C1; # (ë›’; ë›’; á„„á…¯á‡; ë›’; á„„á…¯á‡; ) HANGUL SYLLABLE DDWEOP
+B6D3;B6D3;1104 116F 11C2;B6D3;1104 116F 11C2; # (뛓; 뛓; 뛓; 뛓; 뛓; ) HANGUL SYLLABLE DDWEOH
+B6D4;B6D4;1104 1170;B6D4;1104 1170; # (ë›”; ë›”; á„„á…°; ë›”; á„„á…°; ) HANGUL SYLLABLE DDWE
+B6D5;B6D5;1104 1170 11A8;B6D5;1104 1170 11A8; # (뛕; 뛕; 뛕; 뛕; 뛕; ) HANGUL SYLLABLE DDWEG
+B6D6;B6D6;1104 1170 11A9;B6D6;1104 1170 11A9; # (뛖; 뛖; 뛖; 뛖; 뛖; ) HANGUL SYLLABLE DDWEGG
+B6D7;B6D7;1104 1170 11AA;B6D7;1104 1170 11AA; # (뛗; 뛗; 뛗; 뛗; 뛗; ) HANGUL SYLLABLE DDWEGS
+B6D8;B6D8;1104 1170 11AB;B6D8;1104 1170 11AB; # (뛘; 뛘; 뛘; 뛘; 뛘; ) HANGUL SYLLABLE DDWEN
+B6D9;B6D9;1104 1170 11AC;B6D9;1104 1170 11AC; # (뛙; 뛙; 뛙; 뛙; 뛙; ) HANGUL SYLLABLE DDWENJ
+B6DA;B6DA;1104 1170 11AD;B6DA;1104 1170 11AD; # (뛚; 뛚; 뛚; 뛚; 뛚; ) HANGUL SYLLABLE DDWENH
+B6DB;B6DB;1104 1170 11AE;B6DB;1104 1170 11AE; # (뛛; 뛛; 뛛; 뛛; 뛛; ) HANGUL SYLLABLE DDWED
+B6DC;B6DC;1104 1170 11AF;B6DC;1104 1170 11AF; # (뛜; 뛜; 뛜; 뛜; 뛜; ) HANGUL SYLLABLE DDWEL
+B6DD;B6DD;1104 1170 11B0;B6DD;1104 1170 11B0; # (ë›; ë›; 뛝; ë›; 뛝; ) HANGUL SYLLABLE DDWELG
+B6DE;B6DE;1104 1170 11B1;B6DE;1104 1170 11B1; # (뛞; 뛞; 뛞; 뛞; 뛞; ) HANGUL SYLLABLE DDWELM
+B6DF;B6DF;1104 1170 11B2;B6DF;1104 1170 11B2; # (뛟; 뛟; 뛟; 뛟; 뛟; ) HANGUL SYLLABLE DDWELB
+B6E0;B6E0;1104 1170 11B3;B6E0;1104 1170 11B3; # (뛠; 뛠; 뛠; 뛠; 뛠; ) HANGUL SYLLABLE DDWELS
+B6E1;B6E1;1104 1170 11B4;B6E1;1104 1170 11B4; # (뛡; 뛡; 뛡; 뛡; 뛡; ) HANGUL SYLLABLE DDWELT
+B6E2;B6E2;1104 1170 11B5;B6E2;1104 1170 11B5; # (뛢; 뛢; 뛢; 뛢; 뛢; ) HANGUL SYLLABLE DDWELP
+B6E3;B6E3;1104 1170 11B6;B6E3;1104 1170 11B6; # (뛣; 뛣; 뛣; 뛣; 뛣; ) HANGUL SYLLABLE DDWELH
+B6E4;B6E4;1104 1170 11B7;B6E4;1104 1170 11B7; # (뛤; 뛤; 뛤; 뛤; 뛤; ) HANGUL SYLLABLE DDWEM
+B6E5;B6E5;1104 1170 11B8;B6E5;1104 1170 11B8; # (뛥; 뛥; 뛥; 뛥; 뛥; ) HANGUL SYLLABLE DDWEB
+B6E6;B6E6;1104 1170 11B9;B6E6;1104 1170 11B9; # (뛦; 뛦; 뛦; 뛦; 뛦; ) HANGUL SYLLABLE DDWEBS
+B6E7;B6E7;1104 1170 11BA;B6E7;1104 1170 11BA; # (뛧; 뛧; 뛧; 뛧; 뛧; ) HANGUL SYLLABLE DDWES
+B6E8;B6E8;1104 1170 11BB;B6E8;1104 1170 11BB; # (뛨; 뛨; 뛨; 뛨; 뛨; ) HANGUL SYLLABLE DDWESS
+B6E9;B6E9;1104 1170 11BC;B6E9;1104 1170 11BC; # (뛩; 뛩; 뛩; 뛩; 뛩; ) HANGUL SYLLABLE DDWENG
+B6EA;B6EA;1104 1170 11BD;B6EA;1104 1170 11BD; # (뛪; 뛪; 뛪; 뛪; 뛪; ) HANGUL SYLLABLE DDWEJ
+B6EB;B6EB;1104 1170 11BE;B6EB;1104 1170 11BE; # (뛫; 뛫; 뛫; 뛫; 뛫; ) HANGUL SYLLABLE DDWEC
+B6EC;B6EC;1104 1170 11BF;B6EC;1104 1170 11BF; # (뛬; 뛬; 뛬; 뛬; 뛬; ) HANGUL SYLLABLE DDWEK
+B6ED;B6ED;1104 1170 11C0;B6ED;1104 1170 11C0; # (뛭; 뛭; 뛭; 뛭; 뛭; ) HANGUL SYLLABLE DDWET
+B6EE;B6EE;1104 1170 11C1;B6EE;1104 1170 11C1; # (ë›®; ë›®; á„„á…°á‡; ë›®; á„„á…°á‡; ) HANGUL SYLLABLE DDWEP
+B6EF;B6EF;1104 1170 11C2;B6EF;1104 1170 11C2; # (뛯; 뛯; 뛯; 뛯; 뛯; ) HANGUL SYLLABLE DDWEH
+B6F0;B6F0;1104 1171;B6F0;1104 1171; # (ë›°; ë›°; á„„á…±; ë›°; á„„á…±; ) HANGUL SYLLABLE DDWI
+B6F1;B6F1;1104 1171 11A8;B6F1;1104 1171 11A8; # (뛱; 뛱; 뛱; 뛱; 뛱; ) HANGUL SYLLABLE DDWIG
+B6F2;B6F2;1104 1171 11A9;B6F2;1104 1171 11A9; # (뛲; 뛲; 뛲; 뛲; 뛲; ) HANGUL SYLLABLE DDWIGG
+B6F3;B6F3;1104 1171 11AA;B6F3;1104 1171 11AA; # (뛳; 뛳; 뛳; 뛳; 뛳; ) HANGUL SYLLABLE DDWIGS
+B6F4;B6F4;1104 1171 11AB;B6F4;1104 1171 11AB; # (뛴; 뛴; 뛴; 뛴; 뛴; ) HANGUL SYLLABLE DDWIN
+B6F5;B6F5;1104 1171 11AC;B6F5;1104 1171 11AC; # (뛵; 뛵; 뛵; 뛵; 뛵; ) HANGUL SYLLABLE DDWINJ
+B6F6;B6F6;1104 1171 11AD;B6F6;1104 1171 11AD; # (뛶; 뛶; 뛶; 뛶; 뛶; ) HANGUL SYLLABLE DDWINH
+B6F7;B6F7;1104 1171 11AE;B6F7;1104 1171 11AE; # (뛷; 뛷; 뛷; 뛷; 뛷; ) HANGUL SYLLABLE DDWID
+B6F8;B6F8;1104 1171 11AF;B6F8;1104 1171 11AF; # (뛸; 뛸; 뛸; 뛸; 뛸; ) HANGUL SYLLABLE DDWIL
+B6F9;B6F9;1104 1171 11B0;B6F9;1104 1171 11B0; # (뛹; 뛹; 뛹; 뛹; 뛹; ) HANGUL SYLLABLE DDWILG
+B6FA;B6FA;1104 1171 11B1;B6FA;1104 1171 11B1; # (뛺; 뛺; 뛺; 뛺; 뛺; ) HANGUL SYLLABLE DDWILM
+B6FB;B6FB;1104 1171 11B2;B6FB;1104 1171 11B2; # (뛻; 뛻; 뛻; 뛻; 뛻; ) HANGUL SYLLABLE DDWILB
+B6FC;B6FC;1104 1171 11B3;B6FC;1104 1171 11B3; # (뛼; 뛼; 뛼; 뛼; 뛼; ) HANGUL SYLLABLE DDWILS
+B6FD;B6FD;1104 1171 11B4;B6FD;1104 1171 11B4; # (뛽; 뛽; 뛽; 뛽; 뛽; ) HANGUL SYLLABLE DDWILT
+B6FE;B6FE;1104 1171 11B5;B6FE;1104 1171 11B5; # (뛾; 뛾; 뛾; 뛾; 뛾; ) HANGUL SYLLABLE DDWILP
+B6FF;B6FF;1104 1171 11B6;B6FF;1104 1171 11B6; # (뛿; 뛿; 뛿; 뛿; 뛿; ) HANGUL SYLLABLE DDWILH
+B700;B700;1104 1171 11B7;B700;1104 1171 11B7; # (뜀; 뜀; 뜀; 뜀; 뜀; ) HANGUL SYLLABLE DDWIM
+B701;B701;1104 1171 11B8;B701;1104 1171 11B8; # (ëœ; ëœ; 뜁; ëœ; 뜁; ) HANGUL SYLLABLE DDWIB
+B702;B702;1104 1171 11B9;B702;1104 1171 11B9; # (뜂; 뜂; 뜂; 뜂; 뜂; ) HANGUL SYLLABLE DDWIBS
+B703;B703;1104 1171 11BA;B703;1104 1171 11BA; # (뜃; 뜃; 뜃; 뜃; 뜃; ) HANGUL SYLLABLE DDWIS
+B704;B704;1104 1171 11BB;B704;1104 1171 11BB; # (뜄; 뜄; 뜄; 뜄; 뜄; ) HANGUL SYLLABLE DDWISS
+B705;B705;1104 1171 11BC;B705;1104 1171 11BC; # (뜅; 뜅; 뜅; 뜅; 뜅; ) HANGUL SYLLABLE DDWING
+B706;B706;1104 1171 11BD;B706;1104 1171 11BD; # (뜆; 뜆; 뜆; 뜆; 뜆; ) HANGUL SYLLABLE DDWIJ
+B707;B707;1104 1171 11BE;B707;1104 1171 11BE; # (뜇; 뜇; 뜇; 뜇; 뜇; ) HANGUL SYLLABLE DDWIC
+B708;B708;1104 1171 11BF;B708;1104 1171 11BF; # (뜈; 뜈; 뜈; 뜈; 뜈; ) HANGUL SYLLABLE DDWIK
+B709;B709;1104 1171 11C0;B709;1104 1171 11C0; # (뜉; 뜉; 뜉; 뜉; 뜉; ) HANGUL SYLLABLE DDWIT
+B70A;B70A;1104 1171 11C1;B70A;1104 1171 11C1; # (뜊; 뜊; á„„á…±á‡; 뜊; á„„á…±á‡; ) HANGUL SYLLABLE DDWIP
+B70B;B70B;1104 1171 11C2;B70B;1104 1171 11C2; # (뜋; 뜋; 뜋; 뜋; 뜋; ) HANGUL SYLLABLE DDWIH
+B70C;B70C;1104 1172;B70C;1104 1172; # (뜌; 뜌; 뜌; 뜌; 뜌; ) HANGUL SYLLABLE DDYU
+B70D;B70D;1104 1172 11A8;B70D;1104 1172 11A8; # (ëœ; ëœ; 뜍; ëœ; 뜍; ) HANGUL SYLLABLE DDYUG
+B70E;B70E;1104 1172 11A9;B70E;1104 1172 11A9; # (뜎; 뜎; 뜎; 뜎; 뜎; ) HANGUL SYLLABLE DDYUGG
+B70F;B70F;1104 1172 11AA;B70F;1104 1172 11AA; # (ëœ; ëœ; 뜏; ëœ; 뜏; ) HANGUL SYLLABLE DDYUGS
+B710;B710;1104 1172 11AB;B710;1104 1172 11AB; # (ëœ; ëœ; 뜐; ëœ; 뜐; ) HANGUL SYLLABLE DDYUN
+B711;B711;1104 1172 11AC;B711;1104 1172 11AC; # (뜑; 뜑; 뜑; 뜑; 뜑; ) HANGUL SYLLABLE DDYUNJ
+B712;B712;1104 1172 11AD;B712;1104 1172 11AD; # (뜒; 뜒; 뜒; 뜒; 뜒; ) HANGUL SYLLABLE DDYUNH
+B713;B713;1104 1172 11AE;B713;1104 1172 11AE; # (뜓; 뜓; 뜓; 뜓; 뜓; ) HANGUL SYLLABLE DDYUD
+B714;B714;1104 1172 11AF;B714;1104 1172 11AF; # (뜔; 뜔; 뜔; 뜔; 뜔; ) HANGUL SYLLABLE DDYUL
+B715;B715;1104 1172 11B0;B715;1104 1172 11B0; # (뜕; 뜕; 뜕; 뜕; 뜕; ) HANGUL SYLLABLE DDYULG
+B716;B716;1104 1172 11B1;B716;1104 1172 11B1; # (뜖; 뜖; 뜖; 뜖; 뜖; ) HANGUL SYLLABLE DDYULM
+B717;B717;1104 1172 11B2;B717;1104 1172 11B2; # (뜗; 뜗; 뜗; 뜗; 뜗; ) HANGUL SYLLABLE DDYULB
+B718;B718;1104 1172 11B3;B718;1104 1172 11B3; # (뜘; 뜘; 뜘; 뜘; 뜘; ) HANGUL SYLLABLE DDYULS
+B719;B719;1104 1172 11B4;B719;1104 1172 11B4; # (뜙; 뜙; 뜙; 뜙; 뜙; ) HANGUL SYLLABLE DDYULT
+B71A;B71A;1104 1172 11B5;B71A;1104 1172 11B5; # (뜚; 뜚; 뜚; 뜚; 뜚; ) HANGUL SYLLABLE DDYULP
+B71B;B71B;1104 1172 11B6;B71B;1104 1172 11B6; # (뜛; 뜛; 뜛; 뜛; 뜛; ) HANGUL SYLLABLE DDYULH
+B71C;B71C;1104 1172 11B7;B71C;1104 1172 11B7; # (뜜; 뜜; 뜜; 뜜; 뜜; ) HANGUL SYLLABLE DDYUM
+B71D;B71D;1104 1172 11B8;B71D;1104 1172 11B8; # (ëœ; ëœ; 뜝; ëœ; 뜝; ) HANGUL SYLLABLE DDYUB
+B71E;B71E;1104 1172 11B9;B71E;1104 1172 11B9; # (뜞; 뜞; 뜞; 뜞; 뜞; ) HANGUL SYLLABLE DDYUBS
+B71F;B71F;1104 1172 11BA;B71F;1104 1172 11BA; # (뜟; 뜟; 뜟; 뜟; 뜟; ) HANGUL SYLLABLE DDYUS
+B720;B720;1104 1172 11BB;B720;1104 1172 11BB; # (뜠; 뜠; 뜠; 뜠; 뜠; ) HANGUL SYLLABLE DDYUSS
+B721;B721;1104 1172 11BC;B721;1104 1172 11BC; # (뜡; 뜡; 뜡; 뜡; 뜡; ) HANGUL SYLLABLE DDYUNG
+B722;B722;1104 1172 11BD;B722;1104 1172 11BD; # (뜢; 뜢; 뜢; 뜢; 뜢; ) HANGUL SYLLABLE DDYUJ
+B723;B723;1104 1172 11BE;B723;1104 1172 11BE; # (뜣; 뜣; 뜣; 뜣; 뜣; ) HANGUL SYLLABLE DDYUC
+B724;B724;1104 1172 11BF;B724;1104 1172 11BF; # (뜤; 뜤; 뜤; 뜤; 뜤; ) HANGUL SYLLABLE DDYUK
+B725;B725;1104 1172 11C0;B725;1104 1172 11C0; # (뜥; 뜥; 뜥; 뜥; 뜥; ) HANGUL SYLLABLE DDYUT
+B726;B726;1104 1172 11C1;B726;1104 1172 11C1; # (뜦; 뜦; á„„á…²á‡; 뜦; á„„á…²á‡; ) HANGUL SYLLABLE DDYUP
+B727;B727;1104 1172 11C2;B727;1104 1172 11C2; # (뜧; 뜧; 뜧; 뜧; 뜧; ) HANGUL SYLLABLE DDYUH
+B728;B728;1104 1173;B728;1104 1173; # (뜨; 뜨; 뜨; 뜨; 뜨; ) HANGUL SYLLABLE DDEU
+B729;B729;1104 1173 11A8;B729;1104 1173 11A8; # (뜩; 뜩; 뜩; 뜩; 뜩; ) HANGUL SYLLABLE DDEUG
+B72A;B72A;1104 1173 11A9;B72A;1104 1173 11A9; # (뜪; 뜪; 뜪; 뜪; 뜪; ) HANGUL SYLLABLE DDEUGG
+B72B;B72B;1104 1173 11AA;B72B;1104 1173 11AA; # (뜫; 뜫; 뜫; 뜫; 뜫; ) HANGUL SYLLABLE DDEUGS
+B72C;B72C;1104 1173 11AB;B72C;1104 1173 11AB; # (뜬; 뜬; 뜬; 뜬; 뜬; ) HANGUL SYLLABLE DDEUN
+B72D;B72D;1104 1173 11AC;B72D;1104 1173 11AC; # (뜭; 뜭; 뜭; 뜭; 뜭; ) HANGUL SYLLABLE DDEUNJ
+B72E;B72E;1104 1173 11AD;B72E;1104 1173 11AD; # (뜮; 뜮; 뜮; 뜮; 뜮; ) HANGUL SYLLABLE DDEUNH
+B72F;B72F;1104 1173 11AE;B72F;1104 1173 11AE; # (뜯; 뜯; 뜯; 뜯; 뜯; ) HANGUL SYLLABLE DDEUD
+B730;B730;1104 1173 11AF;B730;1104 1173 11AF; # (뜰; 뜰; 뜰; 뜰; 뜰; ) HANGUL SYLLABLE DDEUL
+B731;B731;1104 1173 11B0;B731;1104 1173 11B0; # (뜱; 뜱; 뜱; 뜱; 뜱; ) HANGUL SYLLABLE DDEULG
+B732;B732;1104 1173 11B1;B732;1104 1173 11B1; # (뜲; 뜲; 뜲; 뜲; 뜲; ) HANGUL SYLLABLE DDEULM
+B733;B733;1104 1173 11B2;B733;1104 1173 11B2; # (뜳; 뜳; 뜳; 뜳; 뜳; ) HANGUL SYLLABLE DDEULB
+B734;B734;1104 1173 11B3;B734;1104 1173 11B3; # (뜴; 뜴; 뜴; 뜴; 뜴; ) HANGUL SYLLABLE DDEULS
+B735;B735;1104 1173 11B4;B735;1104 1173 11B4; # (뜵; 뜵; 뜵; 뜵; 뜵; ) HANGUL SYLLABLE DDEULT
+B736;B736;1104 1173 11B5;B736;1104 1173 11B5; # (뜶; 뜶; 뜶; 뜶; 뜶; ) HANGUL SYLLABLE DDEULP
+B737;B737;1104 1173 11B6;B737;1104 1173 11B6; # (뜷; 뜷; 뜷; 뜷; 뜷; ) HANGUL SYLLABLE DDEULH
+B738;B738;1104 1173 11B7;B738;1104 1173 11B7; # (뜸; 뜸; 뜸; 뜸; 뜸; ) HANGUL SYLLABLE DDEUM
+B739;B739;1104 1173 11B8;B739;1104 1173 11B8; # (뜹; 뜹; 뜹; 뜹; 뜹; ) HANGUL SYLLABLE DDEUB
+B73A;B73A;1104 1173 11B9;B73A;1104 1173 11B9; # (뜺; 뜺; 뜺; 뜺; 뜺; ) HANGUL SYLLABLE DDEUBS
+B73B;B73B;1104 1173 11BA;B73B;1104 1173 11BA; # (뜻; 뜻; 뜻; 뜻; 뜻; ) HANGUL SYLLABLE DDEUS
+B73C;B73C;1104 1173 11BB;B73C;1104 1173 11BB; # (뜼; 뜼; 뜼; 뜼; 뜼; ) HANGUL SYLLABLE DDEUSS
+B73D;B73D;1104 1173 11BC;B73D;1104 1173 11BC; # (뜽; 뜽; 뜽; 뜽; 뜽; ) HANGUL SYLLABLE DDEUNG
+B73E;B73E;1104 1173 11BD;B73E;1104 1173 11BD; # (뜾; 뜾; 뜾; 뜾; 뜾; ) HANGUL SYLLABLE DDEUJ
+B73F;B73F;1104 1173 11BE;B73F;1104 1173 11BE; # (뜿; 뜿; 뜿; 뜿; 뜿; ) HANGUL SYLLABLE DDEUC
+B740;B740;1104 1173 11BF;B740;1104 1173 11BF; # (ë€; ë€; 띀; ë€; 띀; ) HANGUL SYLLABLE DDEUK
+B741;B741;1104 1173 11C0;B741;1104 1173 11C0; # (ë; ë; 띁; ë; 띁; ) HANGUL SYLLABLE DDEUT
+B742;B742;1104 1173 11C1;B742;1104 1173 11C1; # (ë‚; ë‚; á„„á…³á‡; ë‚; á„„á…³á‡; ) HANGUL SYLLABLE DDEUP
+B743;B743;1104 1173 11C2;B743;1104 1173 11C2; # (ëƒ; ëƒ; 띃; ëƒ; 띃; ) HANGUL SYLLABLE DDEUH
+B744;B744;1104 1174;B744;1104 1174; # (ë„; ë„; á„„á…´; ë„; á„„á…´; ) HANGUL SYLLABLE DDYI
+B745;B745;1104 1174 11A8;B745;1104 1174 11A8; # (ë…; ë…; 띅; ë…; 띅; ) HANGUL SYLLABLE DDYIG
+B746;B746;1104 1174 11A9;B746;1104 1174 11A9; # (ë†; ë†; 띆; ë†; 띆; ) HANGUL SYLLABLE DDYIGG
+B747;B747;1104 1174 11AA;B747;1104 1174 11AA; # (ë‡; ë‡; 띇; ë‡; 띇; ) HANGUL SYLLABLE DDYIGS
+B748;B748;1104 1174 11AB;B748;1104 1174 11AB; # (ëˆ; ëˆ; 띈; ëˆ; 띈; ) HANGUL SYLLABLE DDYIN
+B749;B749;1104 1174 11AC;B749;1104 1174 11AC; # (ë‰; ë‰; 띉; ë‰; 띉; ) HANGUL SYLLABLE DDYINJ
+B74A;B74A;1104 1174 11AD;B74A;1104 1174 11AD; # (ëŠ; ëŠ; 띊; ëŠ; 띊; ) HANGUL SYLLABLE DDYINH
+B74B;B74B;1104 1174 11AE;B74B;1104 1174 11AE; # (ë‹; ë‹; 띋; ë‹; 띋; ) HANGUL SYLLABLE DDYID
+B74C;B74C;1104 1174 11AF;B74C;1104 1174 11AF; # (ëŒ; ëŒ; 띌; ëŒ; 띌; ) HANGUL SYLLABLE DDYIL
+B74D;B74D;1104 1174 11B0;B74D;1104 1174 11B0; # (ë; ë; 띍; ë; 띍; ) HANGUL SYLLABLE DDYILG
+B74E;B74E;1104 1174 11B1;B74E;1104 1174 11B1; # (ëŽ; ëŽ; 띎; ëŽ; 띎; ) HANGUL SYLLABLE DDYILM
+B74F;B74F;1104 1174 11B2;B74F;1104 1174 11B2; # (ë; ë; 띏; ë; 띏; ) HANGUL SYLLABLE DDYILB
+B750;B750;1104 1174 11B3;B750;1104 1174 11B3; # (ë; ë; 띐; ë; 띐; ) HANGUL SYLLABLE DDYILS
+B751;B751;1104 1174 11B4;B751;1104 1174 11B4; # (ë‘; ë‘; 띑; ë‘; 띑; ) HANGUL SYLLABLE DDYILT
+B752;B752;1104 1174 11B5;B752;1104 1174 11B5; # (ë’; ë’; 띒; ë’; 띒; ) HANGUL SYLLABLE DDYILP
+B753;B753;1104 1174 11B6;B753;1104 1174 11B6; # (ë“; ë“; 띓; ë“; 띓; ) HANGUL SYLLABLE DDYILH
+B754;B754;1104 1174 11B7;B754;1104 1174 11B7; # (ë”; ë”; 띔; ë”; 띔; ) HANGUL SYLLABLE DDYIM
+B755;B755;1104 1174 11B8;B755;1104 1174 11B8; # (ë•; ë•; 띕; ë•; 띕; ) HANGUL SYLLABLE DDYIB
+B756;B756;1104 1174 11B9;B756;1104 1174 11B9; # (ë–; ë–; 띖; ë–; 띖; ) HANGUL SYLLABLE DDYIBS
+B757;B757;1104 1174 11BA;B757;1104 1174 11BA; # (ë—; ë—; 띗; ë—; 띗; ) HANGUL SYLLABLE DDYIS
+B758;B758;1104 1174 11BB;B758;1104 1174 11BB; # (ë˜; ë˜; 띘; ë˜; 띘; ) HANGUL SYLLABLE DDYISS
+B759;B759;1104 1174 11BC;B759;1104 1174 11BC; # (ë™; ë™; 띙; ë™; 띙; ) HANGUL SYLLABLE DDYING
+B75A;B75A;1104 1174 11BD;B75A;1104 1174 11BD; # (ëš; ëš; 띚; ëš; 띚; ) HANGUL SYLLABLE DDYIJ
+B75B;B75B;1104 1174 11BE;B75B;1104 1174 11BE; # (ë›; ë›; 띛; ë›; 띛; ) HANGUL SYLLABLE DDYIC
+B75C;B75C;1104 1174 11BF;B75C;1104 1174 11BF; # (ëœ; ëœ; 띜; ëœ; 띜; ) HANGUL SYLLABLE DDYIK
+B75D;B75D;1104 1174 11C0;B75D;1104 1174 11C0; # (ë; ë; 띝; ë; 띝; ) HANGUL SYLLABLE DDYIT
+B75E;B75E;1104 1174 11C1;B75E;1104 1174 11C1; # (ëž; ëž; á„„á…´á‡; ëž; á„„á…´á‡; ) HANGUL SYLLABLE DDYIP
+B75F;B75F;1104 1174 11C2;B75F;1104 1174 11C2; # (ëŸ; ëŸ; 띟; ëŸ; 띟; ) HANGUL SYLLABLE DDYIH
+B760;B760;1104 1175;B760;1104 1175; # (ë ; ë ; á„„á…µ; ë ; á„„á…µ; ) HANGUL SYLLABLE DDI
+B761;B761;1104 1175 11A8;B761;1104 1175 11A8; # (ë¡; ë¡; 띡; ë¡; 띡; ) HANGUL SYLLABLE DDIG
+B762;B762;1104 1175 11A9;B762;1104 1175 11A9; # (ë¢; ë¢; 띢; ë¢; 띢; ) HANGUL SYLLABLE DDIGG
+B763;B763;1104 1175 11AA;B763;1104 1175 11AA; # (ë£; ë£; 띣; ë£; 띣; ) HANGUL SYLLABLE DDIGS
+B764;B764;1104 1175 11AB;B764;1104 1175 11AB; # (ë¤; ë¤; 띤; ë¤; 띤; ) HANGUL SYLLABLE DDIN
+B765;B765;1104 1175 11AC;B765;1104 1175 11AC; # (ë¥; ë¥; 띥; ë¥; 띥; ) HANGUL SYLLABLE DDINJ
+B766;B766;1104 1175 11AD;B766;1104 1175 11AD; # (ë¦; ë¦; 띦; ë¦; 띦; ) HANGUL SYLLABLE DDINH
+B767;B767;1104 1175 11AE;B767;1104 1175 11AE; # (ë§; ë§; 띧; ë§; 띧; ) HANGUL SYLLABLE DDID
+B768;B768;1104 1175 11AF;B768;1104 1175 11AF; # (ë¨; ë¨; 띨; ë¨; 띨; ) HANGUL SYLLABLE DDIL
+B769;B769;1104 1175 11B0;B769;1104 1175 11B0; # (ë©; ë©; 띩; ë©; 띩; ) HANGUL SYLLABLE DDILG
+B76A;B76A;1104 1175 11B1;B76A;1104 1175 11B1; # (ëª; ëª; 띪; ëª; 띪; ) HANGUL SYLLABLE DDILM
+B76B;B76B;1104 1175 11B2;B76B;1104 1175 11B2; # (ë«; ë«; 띫; ë«; 띫; ) HANGUL SYLLABLE DDILB
+B76C;B76C;1104 1175 11B3;B76C;1104 1175 11B3; # (ë¬; ë¬; 띬; ë¬; 띬; ) HANGUL SYLLABLE DDILS
+B76D;B76D;1104 1175 11B4;B76D;1104 1175 11B4; # (ë­; ë­; 띭; ë­; 띭; ) HANGUL SYLLABLE DDILT
+B76E;B76E;1104 1175 11B5;B76E;1104 1175 11B5; # (ë®; ë®; 띮; ë®; 띮; ) HANGUL SYLLABLE DDILP
+B76F;B76F;1104 1175 11B6;B76F;1104 1175 11B6; # (ë¯; ë¯; 띯; ë¯; 띯; ) HANGUL SYLLABLE DDILH
+B770;B770;1104 1175 11B7;B770;1104 1175 11B7; # (ë°; ë°; 띰; ë°; 띰; ) HANGUL SYLLABLE DDIM
+B771;B771;1104 1175 11B8;B771;1104 1175 11B8; # (ë±; ë±; 띱; ë±; 띱; ) HANGUL SYLLABLE DDIB
+B772;B772;1104 1175 11B9;B772;1104 1175 11B9; # (ë²; ë²; 띲; ë²; 띲; ) HANGUL SYLLABLE DDIBS
+B773;B773;1104 1175 11BA;B773;1104 1175 11BA; # (ë³; ë³; 띳; ë³; 띳; ) HANGUL SYLLABLE DDIS
+B774;B774;1104 1175 11BB;B774;1104 1175 11BB; # (ë´; ë´; 띴; ë´; 띴; ) HANGUL SYLLABLE DDISS
+B775;B775;1104 1175 11BC;B775;1104 1175 11BC; # (ëµ; ëµ; 띵; ëµ; 띵; ) HANGUL SYLLABLE DDING
+B776;B776;1104 1175 11BD;B776;1104 1175 11BD; # (ë¶; ë¶; 띶; ë¶; 띶; ) HANGUL SYLLABLE DDIJ
+B777;B777;1104 1175 11BE;B777;1104 1175 11BE; # (ë·; ë·; 띷; ë·; 띷; ) HANGUL SYLLABLE DDIC
+B778;B778;1104 1175 11BF;B778;1104 1175 11BF; # (ë¸; ë¸; 띸; ë¸; 띸; ) HANGUL SYLLABLE DDIK
+B779;B779;1104 1175 11C0;B779;1104 1175 11C0; # (ë¹; ë¹; 띹; ë¹; 띹; ) HANGUL SYLLABLE DDIT
+B77A;B77A;1104 1175 11C1;B77A;1104 1175 11C1; # (ëº; ëº; á„„á…µá‡; ëº; á„„á…µá‡; ) HANGUL SYLLABLE DDIP
+B77B;B77B;1104 1175 11C2;B77B;1104 1175 11C2; # (ë»; ë»; 띻; ë»; 띻; ) HANGUL SYLLABLE DDIH
+B77C;B77C;1105 1161;B77C;1105 1161; # (ë¼; ë¼; á„…á…¡; ë¼; á„…á…¡; ) HANGUL SYLLABLE RA
+B77D;B77D;1105 1161 11A8;B77D;1105 1161 11A8; # (ë½; ë½; 락; ë½; 락; ) HANGUL SYLLABLE RAG
+B77E;B77E;1105 1161 11A9;B77E;1105 1161 11A9; # (ë¾; ë¾; 띾; ë¾; 띾; ) HANGUL SYLLABLE RAGG
+B77F;B77F;1105 1161 11AA;B77F;1105 1161 11AA; # (ë¿; ë¿; 띿; ë¿; 띿; ) HANGUL SYLLABLE RAGS
+B780;B780;1105 1161 11AB;B780;1105 1161 11AB; # (란; 란; 란; 란; 란; ) HANGUL SYLLABLE RAN
+B781;B781;1105 1161 11AC;B781;1105 1161 11AC; # (ëž; ëž; 랁; ëž; 랁; ) HANGUL SYLLABLE RANJ
+B782;B782;1105 1161 11AD;B782;1105 1161 11AD; # (랂; 랂; 랂; 랂; 랂; ) HANGUL SYLLABLE RANH
+B783;B783;1105 1161 11AE;B783;1105 1161 11AE; # (랃; 랃; 랃; 랃; 랃; ) HANGUL SYLLABLE RAD
+B784;B784;1105 1161 11AF;B784;1105 1161 11AF; # (랄; 랄; 랄; 랄; 랄; ) HANGUL SYLLABLE RAL
+B785;B785;1105 1161 11B0;B785;1105 1161 11B0; # (랅; 랅; 랅; 랅; 랅; ) HANGUL SYLLABLE RALG
+B786;B786;1105 1161 11B1;B786;1105 1161 11B1; # (랆; 랆; 랆; 랆; 랆; ) HANGUL SYLLABLE RALM
+B787;B787;1105 1161 11B2;B787;1105 1161 11B2; # (랇; 랇; 랇; 랇; 랇; ) HANGUL SYLLABLE RALB
+B788;B788;1105 1161 11B3;B788;1105 1161 11B3; # (랈; 랈; 랈; 랈; 랈; ) HANGUL SYLLABLE RALS
+B789;B789;1105 1161 11B4;B789;1105 1161 11B4; # (랉; 랉; 랉; 랉; 랉; ) HANGUL SYLLABLE RALT
+B78A;B78A;1105 1161 11B5;B78A;1105 1161 11B5; # (랊; 랊; 랊; 랊; 랊; ) HANGUL SYLLABLE RALP
+B78B;B78B;1105 1161 11B6;B78B;1105 1161 11B6; # (랋; 랋; 랋; 랋; 랋; ) HANGUL SYLLABLE RALH
+B78C;B78C;1105 1161 11B7;B78C;1105 1161 11B7; # (람; 람; 람; 람; 람; ) HANGUL SYLLABLE RAM
+B78D;B78D;1105 1161 11B8;B78D;1105 1161 11B8; # (ëž; ëž; 랍; ëž; 랍; ) HANGUL SYLLABLE RAB
+B78E;B78E;1105 1161 11B9;B78E;1105 1161 11B9; # (랎; 랎; 랎; 랎; 랎; ) HANGUL SYLLABLE RABS
+B78F;B78F;1105 1161 11BA;B78F;1105 1161 11BA; # (ëž; ëž; 랏; ëž; 랏; ) HANGUL SYLLABLE RAS
+B790;B790;1105 1161 11BB;B790;1105 1161 11BB; # (ëž; ëž; 랐; ëž; 랐; ) HANGUL SYLLABLE RASS
+B791;B791;1105 1161 11BC;B791;1105 1161 11BC; # (랑; 랑; 랑; 랑; 랑; ) HANGUL SYLLABLE RANG
+B792;B792;1105 1161 11BD;B792;1105 1161 11BD; # (랒; 랒; 랒; 랒; 랒; ) HANGUL SYLLABLE RAJ
+B793;B793;1105 1161 11BE;B793;1105 1161 11BE; # (랓; 랓; 랓; 랓; 랓; ) HANGUL SYLLABLE RAC
+B794;B794;1105 1161 11BF;B794;1105 1161 11BF; # (랔; 랔; 랔; 랔; 랔; ) HANGUL SYLLABLE RAK
+B795;B795;1105 1161 11C0;B795;1105 1161 11C0; # (랕; 랕; 랕; 랕; 랕; ) HANGUL SYLLABLE RAT
+B796;B796;1105 1161 11C1;B796;1105 1161 11C1; # (ëž–; ëž–; á„…á…¡á‡; ëž–; á„…á…¡á‡; ) HANGUL SYLLABLE RAP
+B797;B797;1105 1161 11C2;B797;1105 1161 11C2; # (랗; 랗; 랗; 랗; 랗; ) HANGUL SYLLABLE RAH
+B798;B798;1105 1162;B798;1105 1162; # (래; 래; 래; 래; 래; ) HANGUL SYLLABLE RAE
+B799;B799;1105 1162 11A8;B799;1105 1162 11A8; # (랙; 랙; 랙; 랙; 랙; ) HANGUL SYLLABLE RAEG
+B79A;B79A;1105 1162 11A9;B79A;1105 1162 11A9; # (랚; 랚; 랚; 랚; 랚; ) HANGUL SYLLABLE RAEGG
+B79B;B79B;1105 1162 11AA;B79B;1105 1162 11AA; # (랛; 랛; 랛; 랛; 랛; ) HANGUL SYLLABLE RAEGS
+B79C;B79C;1105 1162 11AB;B79C;1105 1162 11AB; # (랜; 랜; 랜; 랜; 랜; ) HANGUL SYLLABLE RAEN
+B79D;B79D;1105 1162 11AC;B79D;1105 1162 11AC; # (ëž; ëž; 랝; ëž; 랝; ) HANGUL SYLLABLE RAENJ
+B79E;B79E;1105 1162 11AD;B79E;1105 1162 11AD; # (랞; 랞; 랞; 랞; 랞; ) HANGUL SYLLABLE RAENH
+B79F;B79F;1105 1162 11AE;B79F;1105 1162 11AE; # (랟; 랟; 랟; 랟; 랟; ) HANGUL SYLLABLE RAED
+B7A0;B7A0;1105 1162 11AF;B7A0;1105 1162 11AF; # (랠; 랠; 랠; 랠; 랠; ) HANGUL SYLLABLE RAEL
+B7A1;B7A1;1105 1162 11B0;B7A1;1105 1162 11B0; # (랡; 랡; 랡; 랡; 랡; ) HANGUL SYLLABLE RAELG
+B7A2;B7A2;1105 1162 11B1;B7A2;1105 1162 11B1; # (랢; 랢; 랢; 랢; 랢; ) HANGUL SYLLABLE RAELM
+B7A3;B7A3;1105 1162 11B2;B7A3;1105 1162 11B2; # (랣; 랣; 랣; 랣; 랣; ) HANGUL SYLLABLE RAELB
+B7A4;B7A4;1105 1162 11B3;B7A4;1105 1162 11B3; # (랤; 랤; 랤; 랤; 랤; ) HANGUL SYLLABLE RAELS
+B7A5;B7A5;1105 1162 11B4;B7A5;1105 1162 11B4; # (랥; 랥; 랥; 랥; 랥; ) HANGUL SYLLABLE RAELT
+B7A6;B7A6;1105 1162 11B5;B7A6;1105 1162 11B5; # (랦; 랦; 랦; 랦; 랦; ) HANGUL SYLLABLE RAELP
+B7A7;B7A7;1105 1162 11B6;B7A7;1105 1162 11B6; # (랧; 랧; 랧; 랧; 랧; ) HANGUL SYLLABLE RAELH
+B7A8;B7A8;1105 1162 11B7;B7A8;1105 1162 11B7; # (램; 램; 램; 램; 램; ) HANGUL SYLLABLE RAEM
+B7A9;B7A9;1105 1162 11B8;B7A9;1105 1162 11B8; # (랩; 랩; 랩; 랩; 랩; ) HANGUL SYLLABLE RAEB
+B7AA;B7AA;1105 1162 11B9;B7AA;1105 1162 11B9; # (랪; 랪; 랪; 랪; 랪; ) HANGUL SYLLABLE RAEBS
+B7AB;B7AB;1105 1162 11BA;B7AB;1105 1162 11BA; # (랫; 랫; 랫; 랫; 랫; ) HANGUL SYLLABLE RAES
+B7AC;B7AC;1105 1162 11BB;B7AC;1105 1162 11BB; # (랬; 랬; 랬; 랬; 랬; ) HANGUL SYLLABLE RAESS
+B7AD;B7AD;1105 1162 11BC;B7AD;1105 1162 11BC; # (랭; 랭; 랭; 랭; 랭; ) HANGUL SYLLABLE RAENG
+B7AE;B7AE;1105 1162 11BD;B7AE;1105 1162 11BD; # (랮; 랮; 랮; 랮; 랮; ) HANGUL SYLLABLE RAEJ
+B7AF;B7AF;1105 1162 11BE;B7AF;1105 1162 11BE; # (랯; 랯; 랯; 랯; 랯; ) HANGUL SYLLABLE RAEC
+B7B0;B7B0;1105 1162 11BF;B7B0;1105 1162 11BF; # (랰; 랰; 랰; 랰; 랰; ) HANGUL SYLLABLE RAEK
+B7B1;B7B1;1105 1162 11C0;B7B1;1105 1162 11C0; # (랱; 랱; 랱; 랱; 랱; ) HANGUL SYLLABLE RAET
+B7B2;B7B2;1105 1162 11C1;B7B2;1105 1162 11C1; # (ëž²; ëž²; á„…á…¢á‡; ëž²; á„…á…¢á‡; ) HANGUL SYLLABLE RAEP
+B7B3;B7B3;1105 1162 11C2;B7B3;1105 1162 11C2; # (랳; 랳; 랳; 랳; 랳; ) HANGUL SYLLABLE RAEH
+B7B4;B7B4;1105 1163;B7B4;1105 1163; # (ëž´; ëž´; á„…á…£; ëž´; á„…á…£; ) HANGUL SYLLABLE RYA
+B7B5;B7B5;1105 1163 11A8;B7B5;1105 1163 11A8; # (략; 략; 략; 략; 략; ) HANGUL SYLLABLE RYAG
+B7B6;B7B6;1105 1163 11A9;B7B6;1105 1163 11A9; # (랶; 랶; 랶; 랶; 랶; ) HANGUL SYLLABLE RYAGG
+B7B7;B7B7;1105 1163 11AA;B7B7;1105 1163 11AA; # (랷; 랷; 랷; 랷; 랷; ) HANGUL SYLLABLE RYAGS
+B7B8;B7B8;1105 1163 11AB;B7B8;1105 1163 11AB; # (랸; 랸; 랸; 랸; 랸; ) HANGUL SYLLABLE RYAN
+B7B9;B7B9;1105 1163 11AC;B7B9;1105 1163 11AC; # (랹; 랹; 랹; 랹; 랹; ) HANGUL SYLLABLE RYANJ
+B7BA;B7BA;1105 1163 11AD;B7BA;1105 1163 11AD; # (랺; 랺; 랺; 랺; 랺; ) HANGUL SYLLABLE RYANH
+B7BB;B7BB;1105 1163 11AE;B7BB;1105 1163 11AE; # (랻; 랻; 랻; 랻; 랻; ) HANGUL SYLLABLE RYAD
+B7BC;B7BC;1105 1163 11AF;B7BC;1105 1163 11AF; # (랼; 랼; 랼; 랼; 랼; ) HANGUL SYLLABLE RYAL
+B7BD;B7BD;1105 1163 11B0;B7BD;1105 1163 11B0; # (랽; 랽; 랽; 랽; 랽; ) HANGUL SYLLABLE RYALG
+B7BE;B7BE;1105 1163 11B1;B7BE;1105 1163 11B1; # (랾; 랾; 랾; 랾; 랾; ) HANGUL SYLLABLE RYALM
+B7BF;B7BF;1105 1163 11B2;B7BF;1105 1163 11B2; # (랿; 랿; 랿; 랿; 랿; ) HANGUL SYLLABLE RYALB
+B7C0;B7C0;1105 1163 11B3;B7C0;1105 1163 11B3; # (럀; 럀; 럀; 럀; 럀; ) HANGUL SYLLABLE RYALS
+B7C1;B7C1;1105 1163 11B4;B7C1;1105 1163 11B4; # (ëŸ; ëŸ; 럁; ëŸ; 럁; ) HANGUL SYLLABLE RYALT
+B7C2;B7C2;1105 1163 11B5;B7C2;1105 1163 11B5; # (럂; 럂; 럂; 럂; 럂; ) HANGUL SYLLABLE RYALP
+B7C3;B7C3;1105 1163 11B6;B7C3;1105 1163 11B6; # (럃; 럃; 럃; 럃; 럃; ) HANGUL SYLLABLE RYALH
+B7C4;B7C4;1105 1163 11B7;B7C4;1105 1163 11B7; # (럄; 럄; 럄; 럄; 럄; ) HANGUL SYLLABLE RYAM
+B7C5;B7C5;1105 1163 11B8;B7C5;1105 1163 11B8; # (럅; 럅; 럅; 럅; 럅; ) HANGUL SYLLABLE RYAB
+B7C6;B7C6;1105 1163 11B9;B7C6;1105 1163 11B9; # (럆; 럆; 럆; 럆; 럆; ) HANGUL SYLLABLE RYABS
+B7C7;B7C7;1105 1163 11BA;B7C7;1105 1163 11BA; # (럇; 럇; 럇; 럇; 럇; ) HANGUL SYLLABLE RYAS
+B7C8;B7C8;1105 1163 11BB;B7C8;1105 1163 11BB; # (럈; 럈; 럈; 럈; 럈; ) HANGUL SYLLABLE RYASS
+B7C9;B7C9;1105 1163 11BC;B7C9;1105 1163 11BC; # (량; 량; 량; 량; 량; ) HANGUL SYLLABLE RYANG
+B7CA;B7CA;1105 1163 11BD;B7CA;1105 1163 11BD; # (럊; 럊; 럊; 럊; 럊; ) HANGUL SYLLABLE RYAJ
+B7CB;B7CB;1105 1163 11BE;B7CB;1105 1163 11BE; # (럋; 럋; 럋; 럋; 럋; ) HANGUL SYLLABLE RYAC
+B7CC;B7CC;1105 1163 11BF;B7CC;1105 1163 11BF; # (럌; 럌; 럌; 럌; 럌; ) HANGUL SYLLABLE RYAK
+B7CD;B7CD;1105 1163 11C0;B7CD;1105 1163 11C0; # (ëŸ; ëŸ; 럍; ëŸ; 럍; ) HANGUL SYLLABLE RYAT
+B7CE;B7CE;1105 1163 11C1;B7CE;1105 1163 11C1; # (럎; 럎; á„…á…£á‡; 럎; á„…á…£á‡; ) HANGUL SYLLABLE RYAP
+B7CF;B7CF;1105 1163 11C2;B7CF;1105 1163 11C2; # (ëŸ; ëŸ; 럏; ëŸ; 럏; ) HANGUL SYLLABLE RYAH
+B7D0;B7D0;1105 1164;B7D0;1105 1164; # (ëŸ; ëŸ; á„…á…¤; ëŸ; á„…á…¤; ) HANGUL SYLLABLE RYAE
+B7D1;B7D1;1105 1164 11A8;B7D1;1105 1164 11A8; # (럑; 럑; 럑; 럑; 럑; ) HANGUL SYLLABLE RYAEG
+B7D2;B7D2;1105 1164 11A9;B7D2;1105 1164 11A9; # (럒; 럒; 럒; 럒; 럒; ) HANGUL SYLLABLE RYAEGG
+B7D3;B7D3;1105 1164 11AA;B7D3;1105 1164 11AA; # (럓; 럓; 럓; 럓; 럓; ) HANGUL SYLLABLE RYAEGS
+B7D4;B7D4;1105 1164 11AB;B7D4;1105 1164 11AB; # (럔; 럔; 럔; 럔; 럔; ) HANGUL SYLLABLE RYAEN
+B7D5;B7D5;1105 1164 11AC;B7D5;1105 1164 11AC; # (럕; 럕; 럕; 럕; 럕; ) HANGUL SYLLABLE RYAENJ
+B7D6;B7D6;1105 1164 11AD;B7D6;1105 1164 11AD; # (럖; 럖; 럖; 럖; 럖; ) HANGUL SYLLABLE RYAENH
+B7D7;B7D7;1105 1164 11AE;B7D7;1105 1164 11AE; # (럗; 럗; 럗; 럗; 럗; ) HANGUL SYLLABLE RYAED
+B7D8;B7D8;1105 1164 11AF;B7D8;1105 1164 11AF; # (럘; 럘; 럘; 럘; 럘; ) HANGUL SYLLABLE RYAEL
+B7D9;B7D9;1105 1164 11B0;B7D9;1105 1164 11B0; # (럙; 럙; 럙; 럙; 럙; ) HANGUL SYLLABLE RYAELG
+B7DA;B7DA;1105 1164 11B1;B7DA;1105 1164 11B1; # (럚; 럚; 럚; 럚; 럚; ) HANGUL SYLLABLE RYAELM
+B7DB;B7DB;1105 1164 11B2;B7DB;1105 1164 11B2; # (럛; 럛; 럛; 럛; 럛; ) HANGUL SYLLABLE RYAELB
+B7DC;B7DC;1105 1164 11B3;B7DC;1105 1164 11B3; # (럜; 럜; 럜; 럜; 럜; ) HANGUL SYLLABLE RYAELS
+B7DD;B7DD;1105 1164 11B4;B7DD;1105 1164 11B4; # (ëŸ; ëŸ; 럝; ëŸ; 럝; ) HANGUL SYLLABLE RYAELT
+B7DE;B7DE;1105 1164 11B5;B7DE;1105 1164 11B5; # (럞; 럞; 럞; 럞; 럞; ) HANGUL SYLLABLE RYAELP
+B7DF;B7DF;1105 1164 11B6;B7DF;1105 1164 11B6; # (럟; 럟; 럟; 럟; 럟; ) HANGUL SYLLABLE RYAELH
+B7E0;B7E0;1105 1164 11B7;B7E0;1105 1164 11B7; # (럠; 럠; 럠; 럠; 럠; ) HANGUL SYLLABLE RYAEM
+B7E1;B7E1;1105 1164 11B8;B7E1;1105 1164 11B8; # (럡; 럡; 럡; 럡; 럡; ) HANGUL SYLLABLE RYAEB
+B7E2;B7E2;1105 1164 11B9;B7E2;1105 1164 11B9; # (럢; 럢; 럢; 럢; 럢; ) HANGUL SYLLABLE RYAEBS
+B7E3;B7E3;1105 1164 11BA;B7E3;1105 1164 11BA; # (럣; 럣; 럣; 럣; 럣; ) HANGUL SYLLABLE RYAES
+B7E4;B7E4;1105 1164 11BB;B7E4;1105 1164 11BB; # (럤; 럤; 럤; 럤; 럤; ) HANGUL SYLLABLE RYAESS
+B7E5;B7E5;1105 1164 11BC;B7E5;1105 1164 11BC; # (럥; 럥; 럥; 럥; 럥; ) HANGUL SYLLABLE RYAENG
+B7E6;B7E6;1105 1164 11BD;B7E6;1105 1164 11BD; # (럦; 럦; 럦; 럦; 럦; ) HANGUL SYLLABLE RYAEJ
+B7E7;B7E7;1105 1164 11BE;B7E7;1105 1164 11BE; # (럧; 럧; 럧; 럧; 럧; ) HANGUL SYLLABLE RYAEC
+B7E8;B7E8;1105 1164 11BF;B7E8;1105 1164 11BF; # (럨; 럨; 럨; 럨; 럨; ) HANGUL SYLLABLE RYAEK
+B7E9;B7E9;1105 1164 11C0;B7E9;1105 1164 11C0; # (럩; 럩; 럩; 럩; 럩; ) HANGUL SYLLABLE RYAET
+B7EA;B7EA;1105 1164 11C1;B7EA;1105 1164 11C1; # (럪; 럪; á„…á…¤á‡; 럪; á„…á…¤á‡; ) HANGUL SYLLABLE RYAEP
+B7EB;B7EB;1105 1164 11C2;B7EB;1105 1164 11C2; # (럫; 럫; 럫; 럫; 럫; ) HANGUL SYLLABLE RYAEH
+B7EC;B7EC;1105 1165;B7EC;1105 1165; # (러; 러; 러; 러; 러; ) HANGUL SYLLABLE REO
+B7ED;B7ED;1105 1165 11A8;B7ED;1105 1165 11A8; # (럭; 럭; 럭; 럭; 럭; ) HANGUL SYLLABLE REOG
+B7EE;B7EE;1105 1165 11A9;B7EE;1105 1165 11A9; # (럮; 럮; 럮; 럮; 럮; ) HANGUL SYLLABLE REOGG
+B7EF;B7EF;1105 1165 11AA;B7EF;1105 1165 11AA; # (럯; 럯; 럯; 럯; 럯; ) HANGUL SYLLABLE REOGS
+B7F0;B7F0;1105 1165 11AB;B7F0;1105 1165 11AB; # (런; 런; 런; 런; 런; ) HANGUL SYLLABLE REON
+B7F1;B7F1;1105 1165 11AC;B7F1;1105 1165 11AC; # (럱; 럱; 럱; 럱; 럱; ) HANGUL SYLLABLE REONJ
+B7F2;B7F2;1105 1165 11AD;B7F2;1105 1165 11AD; # (럲; 럲; 럲; 럲; 럲; ) HANGUL SYLLABLE REONH
+B7F3;B7F3;1105 1165 11AE;B7F3;1105 1165 11AE; # (럳; 럳; 럳; 럳; 럳; ) HANGUL SYLLABLE REOD
+B7F4;B7F4;1105 1165 11AF;B7F4;1105 1165 11AF; # (럴; 럴; 럴; 럴; 럴; ) HANGUL SYLLABLE REOL
+B7F5;B7F5;1105 1165 11B0;B7F5;1105 1165 11B0; # (럵; 럵; 럵; 럵; 럵; ) HANGUL SYLLABLE REOLG
+B7F6;B7F6;1105 1165 11B1;B7F6;1105 1165 11B1; # (럶; 럶; 럶; 럶; 럶; ) HANGUL SYLLABLE REOLM
+B7F7;B7F7;1105 1165 11B2;B7F7;1105 1165 11B2; # (럷; 럷; 럷; 럷; 럷; ) HANGUL SYLLABLE REOLB
+B7F8;B7F8;1105 1165 11B3;B7F8;1105 1165 11B3; # (럸; 럸; 럸; 럸; 럸; ) HANGUL SYLLABLE REOLS
+B7F9;B7F9;1105 1165 11B4;B7F9;1105 1165 11B4; # (럹; 럹; 럹; 럹; 럹; ) HANGUL SYLLABLE REOLT
+B7FA;B7FA;1105 1165 11B5;B7FA;1105 1165 11B5; # (럺; 럺; 럺; 럺; 럺; ) HANGUL SYLLABLE REOLP
+B7FB;B7FB;1105 1165 11B6;B7FB;1105 1165 11B6; # (럻; 럻; 럻; 럻; 럻; ) HANGUL SYLLABLE REOLH
+B7FC;B7FC;1105 1165 11B7;B7FC;1105 1165 11B7; # (럼; 럼; 럼; 럼; 럼; ) HANGUL SYLLABLE REOM
+B7FD;B7FD;1105 1165 11B8;B7FD;1105 1165 11B8; # (럽; 럽; 럽; 럽; 럽; ) HANGUL SYLLABLE REOB
+B7FE;B7FE;1105 1165 11B9;B7FE;1105 1165 11B9; # (럾; 럾; 럾; 럾; 럾; ) HANGUL SYLLABLE REOBS
+B7FF;B7FF;1105 1165 11BA;B7FF;1105 1165 11BA; # (럿; 럿; 럿; 럿; 럿; ) HANGUL SYLLABLE REOS
+B800;B800;1105 1165 11BB;B800;1105 1165 11BB; # (렀; 렀; 렀; 렀; 렀; ) HANGUL SYLLABLE REOSS
+B801;B801;1105 1165 11BC;B801;1105 1165 11BC; # (ë ; ë ; 렁; ë ; 렁; ) HANGUL SYLLABLE REONG
+B802;B802;1105 1165 11BD;B802;1105 1165 11BD; # (렂; 렂; 렂; 렂; 렂; ) HANGUL SYLLABLE REOJ
+B803;B803;1105 1165 11BE;B803;1105 1165 11BE; # (렃; 렃; 렃; 렃; 렃; ) HANGUL SYLLABLE REOC
+B804;B804;1105 1165 11BF;B804;1105 1165 11BF; # (렄; 렄; 렄; 렄; 렄; ) HANGUL SYLLABLE REOK
+B805;B805;1105 1165 11C0;B805;1105 1165 11C0; # (렅; 렅; 렅; 렅; 렅; ) HANGUL SYLLABLE REOT
+B806;B806;1105 1165 11C1;B806;1105 1165 11C1; # (ë †; ë †; á„…á…¥á‡; ë †; á„…á…¥á‡; ) HANGUL SYLLABLE REOP
+B807;B807;1105 1165 11C2;B807;1105 1165 11C2; # (렇; 렇; 렇; 렇; 렇; ) HANGUL SYLLABLE REOH
+B808;B808;1105 1166;B808;1105 1166; # (ë ˆ; ë ˆ; á„…á…¦; ë ˆ; á„…á…¦; ) HANGUL SYLLABLE RE
+B809;B809;1105 1166 11A8;B809;1105 1166 11A8; # (렉; 렉; 렉; 렉; 렉; ) HANGUL SYLLABLE REG
+B80A;B80A;1105 1166 11A9;B80A;1105 1166 11A9; # (렊; 렊; 렊; 렊; 렊; ) HANGUL SYLLABLE REGG
+B80B;B80B;1105 1166 11AA;B80B;1105 1166 11AA; # (렋; 렋; 렋; 렋; 렋; ) HANGUL SYLLABLE REGS
+B80C;B80C;1105 1166 11AB;B80C;1105 1166 11AB; # (렌; 렌; 렌; 렌; 렌; ) HANGUL SYLLABLE REN
+B80D;B80D;1105 1166 11AC;B80D;1105 1166 11AC; # (ë ; ë ; 렍; ë ; 렍; ) HANGUL SYLLABLE RENJ
+B80E;B80E;1105 1166 11AD;B80E;1105 1166 11AD; # (렎; 렎; 렎; 렎; 렎; ) HANGUL SYLLABLE RENH
+B80F;B80F;1105 1166 11AE;B80F;1105 1166 11AE; # (ë ; ë ; 렏; ë ; 렏; ) HANGUL SYLLABLE RED
+B810;B810;1105 1166 11AF;B810;1105 1166 11AF; # (ë ; ë ; 렐; ë ; 렐; ) HANGUL SYLLABLE REL
+B811;B811;1105 1166 11B0;B811;1105 1166 11B0; # (렑; 렑; 렑; 렑; 렑; ) HANGUL SYLLABLE RELG
+B812;B812;1105 1166 11B1;B812;1105 1166 11B1; # (렒; 렒; 렒; 렒; 렒; ) HANGUL SYLLABLE RELM
+B813;B813;1105 1166 11B2;B813;1105 1166 11B2; # (렓; 렓; 렓; 렓; 렓; ) HANGUL SYLLABLE RELB
+B814;B814;1105 1166 11B3;B814;1105 1166 11B3; # (렔; 렔; 렔; 렔; 렔; ) HANGUL SYLLABLE RELS
+B815;B815;1105 1166 11B4;B815;1105 1166 11B4; # (렕; 렕; 렕; 렕; 렕; ) HANGUL SYLLABLE RELT
+B816;B816;1105 1166 11B5;B816;1105 1166 11B5; # (렖; 렖; 렖; 렖; 렖; ) HANGUL SYLLABLE RELP
+B817;B817;1105 1166 11B6;B817;1105 1166 11B6; # (렗; 렗; 렗; 렗; 렗; ) HANGUL SYLLABLE RELH
+B818;B818;1105 1166 11B7;B818;1105 1166 11B7; # (렘; 렘; 렘; 렘; 렘; ) HANGUL SYLLABLE REM
+B819;B819;1105 1166 11B8;B819;1105 1166 11B8; # (렙; 렙; 렙; 렙; 렙; ) HANGUL SYLLABLE REB
+B81A;B81A;1105 1166 11B9;B81A;1105 1166 11B9; # (렚; 렚; 렚; 렚; 렚; ) HANGUL SYLLABLE REBS
+B81B;B81B;1105 1166 11BA;B81B;1105 1166 11BA; # (렛; 렛; 렛; 렛; 렛; ) HANGUL SYLLABLE RES
+B81C;B81C;1105 1166 11BB;B81C;1105 1166 11BB; # (렜; 렜; 렜; 렜; 렜; ) HANGUL SYLLABLE RESS
+B81D;B81D;1105 1166 11BC;B81D;1105 1166 11BC; # (ë ; ë ; 렝; ë ; 렝; ) HANGUL SYLLABLE RENG
+B81E;B81E;1105 1166 11BD;B81E;1105 1166 11BD; # (렞; 렞; 렞; 렞; 렞; ) HANGUL SYLLABLE REJ
+B81F;B81F;1105 1166 11BE;B81F;1105 1166 11BE; # (렟; 렟; 렟; 렟; 렟; ) HANGUL SYLLABLE REC
+B820;B820;1105 1166 11BF;B820;1105 1166 11BF; # (렠; 렠; 렠; 렠; 렠; ) HANGUL SYLLABLE REK
+B821;B821;1105 1166 11C0;B821;1105 1166 11C0; # (렡; 렡; 렡; 렡; 렡; ) HANGUL SYLLABLE RET
+B822;B822;1105 1166 11C1;B822;1105 1166 11C1; # (ë ¢; ë ¢; á„…á…¦á‡; ë ¢; á„…á…¦á‡; ) HANGUL SYLLABLE REP
+B823;B823;1105 1166 11C2;B823;1105 1166 11C2; # (렣; 렣; 렣; 렣; 렣; ) HANGUL SYLLABLE REH
+B824;B824;1105 1167;B824;1105 1167; # (ë ¤; ë ¤; á„…á…§; ë ¤; á„…á…§; ) HANGUL SYLLABLE RYEO
+B825;B825;1105 1167 11A8;B825;1105 1167 11A8; # (력; 력; 력; 력; 력; ) HANGUL SYLLABLE RYEOG
+B826;B826;1105 1167 11A9;B826;1105 1167 11A9; # (렦; 렦; 렦; 렦; 렦; ) HANGUL SYLLABLE RYEOGG
+B827;B827;1105 1167 11AA;B827;1105 1167 11AA; # (렧; 렧; 렧; 렧; 렧; ) HANGUL SYLLABLE RYEOGS
+B828;B828;1105 1167 11AB;B828;1105 1167 11AB; # (련; 련; 련; 련; 련; ) HANGUL SYLLABLE RYEON
+B829;B829;1105 1167 11AC;B829;1105 1167 11AC; # (렩; 렩; 렩; 렩; 렩; ) HANGUL SYLLABLE RYEONJ
+B82A;B82A;1105 1167 11AD;B82A;1105 1167 11AD; # (렪; 렪; 렪; 렪; 렪; ) HANGUL SYLLABLE RYEONH
+B82B;B82B;1105 1167 11AE;B82B;1105 1167 11AE; # (렫; 렫; 렫; 렫; 렫; ) HANGUL SYLLABLE RYEOD
+B82C;B82C;1105 1167 11AF;B82C;1105 1167 11AF; # (렬; 렬; 렬; 렬; 렬; ) HANGUL SYLLABLE RYEOL
+B82D;B82D;1105 1167 11B0;B82D;1105 1167 11B0; # (렭; 렭; 렭; 렭; 렭; ) HANGUL SYLLABLE RYEOLG
+B82E;B82E;1105 1167 11B1;B82E;1105 1167 11B1; # (렮; 렮; 렮; 렮; 렮; ) HANGUL SYLLABLE RYEOLM
+B82F;B82F;1105 1167 11B2;B82F;1105 1167 11B2; # (렯; 렯; 렯; 렯; 렯; ) HANGUL SYLLABLE RYEOLB
+B830;B830;1105 1167 11B3;B830;1105 1167 11B3; # (렰; 렰; 렰; 렰; 렰; ) HANGUL SYLLABLE RYEOLS
+B831;B831;1105 1167 11B4;B831;1105 1167 11B4; # (렱; 렱; 렱; 렱; 렱; ) HANGUL SYLLABLE RYEOLT
+B832;B832;1105 1167 11B5;B832;1105 1167 11B5; # (렲; 렲; 렲; 렲; 렲; ) HANGUL SYLLABLE RYEOLP
+B833;B833;1105 1167 11B6;B833;1105 1167 11B6; # (렳; 렳; 렳; 렳; 렳; ) HANGUL SYLLABLE RYEOLH
+B834;B834;1105 1167 11B7;B834;1105 1167 11B7; # (렴; 렴; 렴; 렴; 렴; ) HANGUL SYLLABLE RYEOM
+B835;B835;1105 1167 11B8;B835;1105 1167 11B8; # (렵; 렵; 렵; 렵; 렵; ) HANGUL SYLLABLE RYEOB
+B836;B836;1105 1167 11B9;B836;1105 1167 11B9; # (렶; 렶; 렶; 렶; 렶; ) HANGUL SYLLABLE RYEOBS
+B837;B837;1105 1167 11BA;B837;1105 1167 11BA; # (렷; 렷; 렷; 렷; 렷; ) HANGUL SYLLABLE RYEOS
+B838;B838;1105 1167 11BB;B838;1105 1167 11BB; # (렸; 렸; 렸; 렸; 렸; ) HANGUL SYLLABLE RYEOSS
+B839;B839;1105 1167 11BC;B839;1105 1167 11BC; # (령; 령; 령; 령; 령; ) HANGUL SYLLABLE RYEONG
+B83A;B83A;1105 1167 11BD;B83A;1105 1167 11BD; # (렺; 렺; 렺; 렺; 렺; ) HANGUL SYLLABLE RYEOJ
+B83B;B83B;1105 1167 11BE;B83B;1105 1167 11BE; # (렻; 렻; 렻; 렻; 렻; ) HANGUL SYLLABLE RYEOC
+B83C;B83C;1105 1167 11BF;B83C;1105 1167 11BF; # (렼; 렼; 렼; 렼; 렼; ) HANGUL SYLLABLE RYEOK
+B83D;B83D;1105 1167 11C0;B83D;1105 1167 11C0; # (렽; 렽; 렽; 렽; 렽; ) HANGUL SYLLABLE RYEOT
+B83E;B83E;1105 1167 11C1;B83E;1105 1167 11C1; # (ë ¾; ë ¾; á„…á…§á‡; ë ¾; á„…á…§á‡; ) HANGUL SYLLABLE RYEOP
+B83F;B83F;1105 1167 11C2;B83F;1105 1167 11C2; # (렿; 렿; 렿; 렿; 렿; ) HANGUL SYLLABLE RYEOH
+B840;B840;1105 1168;B840;1105 1168; # (ë¡€; ë¡€; á„…á…¨; ë¡€; á„…á…¨; ) HANGUL SYLLABLE RYE
+B841;B841;1105 1168 11A8;B841;1105 1168 11A8; # (ë¡; ë¡; 롁; ë¡; 롁; ) HANGUL SYLLABLE RYEG
+B842;B842;1105 1168 11A9;B842;1105 1168 11A9; # (롂; 롂; 롂; 롂; 롂; ) HANGUL SYLLABLE RYEGG
+B843;B843;1105 1168 11AA;B843;1105 1168 11AA; # (롃; 롃; 롃; 롃; 롃; ) HANGUL SYLLABLE RYEGS
+B844;B844;1105 1168 11AB;B844;1105 1168 11AB; # (롄; 롄; 롄; 롄; 롄; ) HANGUL SYLLABLE RYEN
+B845;B845;1105 1168 11AC;B845;1105 1168 11AC; # (롅; 롅; 롅; 롅; 롅; ) HANGUL SYLLABLE RYENJ
+B846;B846;1105 1168 11AD;B846;1105 1168 11AD; # (롆; 롆; 롆; 롆; 롆; ) HANGUL SYLLABLE RYENH
+B847;B847;1105 1168 11AE;B847;1105 1168 11AE; # (롇; 롇; 롇; 롇; 롇; ) HANGUL SYLLABLE RYED
+B848;B848;1105 1168 11AF;B848;1105 1168 11AF; # (롈; 롈; 롈; 롈; 롈; ) HANGUL SYLLABLE RYEL
+B849;B849;1105 1168 11B0;B849;1105 1168 11B0; # (롉; 롉; 롉; 롉; 롉; ) HANGUL SYLLABLE RYELG
+B84A;B84A;1105 1168 11B1;B84A;1105 1168 11B1; # (롊; 롊; 롊; 롊; 롊; ) HANGUL SYLLABLE RYELM
+B84B;B84B;1105 1168 11B2;B84B;1105 1168 11B2; # (롋; 롋; 롋; 롋; 롋; ) HANGUL SYLLABLE RYELB
+B84C;B84C;1105 1168 11B3;B84C;1105 1168 11B3; # (롌; 롌; 롌; 롌; 롌; ) HANGUL SYLLABLE RYELS
+B84D;B84D;1105 1168 11B4;B84D;1105 1168 11B4; # (ë¡; ë¡; 롍; ë¡; 롍; ) HANGUL SYLLABLE RYELT
+B84E;B84E;1105 1168 11B5;B84E;1105 1168 11B5; # (롎; 롎; 롎; 롎; 롎; ) HANGUL SYLLABLE RYELP
+B84F;B84F;1105 1168 11B6;B84F;1105 1168 11B6; # (ë¡; ë¡; 롏; ë¡; 롏; ) HANGUL SYLLABLE RYELH
+B850;B850;1105 1168 11B7;B850;1105 1168 11B7; # (ë¡; ë¡; 롐; ë¡; 롐; ) HANGUL SYLLABLE RYEM
+B851;B851;1105 1168 11B8;B851;1105 1168 11B8; # (롑; 롑; 롑; 롑; 롑; ) HANGUL SYLLABLE RYEB
+B852;B852;1105 1168 11B9;B852;1105 1168 11B9; # (롒; 롒; 롒; 롒; 롒; ) HANGUL SYLLABLE RYEBS
+B853;B853;1105 1168 11BA;B853;1105 1168 11BA; # (롓; 롓; 롓; 롓; 롓; ) HANGUL SYLLABLE RYES
+B854;B854;1105 1168 11BB;B854;1105 1168 11BB; # (롔; 롔; 롔; 롔; 롔; ) HANGUL SYLLABLE RYESS
+B855;B855;1105 1168 11BC;B855;1105 1168 11BC; # (롕; 롕; 롕; 롕; 롕; ) HANGUL SYLLABLE RYENG
+B856;B856;1105 1168 11BD;B856;1105 1168 11BD; # (롖; 롖; 롖; 롖; 롖; ) HANGUL SYLLABLE RYEJ
+B857;B857;1105 1168 11BE;B857;1105 1168 11BE; # (롗; 롗; 롗; 롗; 롗; ) HANGUL SYLLABLE RYEC
+B858;B858;1105 1168 11BF;B858;1105 1168 11BF; # (롘; 롘; 롘; 롘; 롘; ) HANGUL SYLLABLE RYEK
+B859;B859;1105 1168 11C0;B859;1105 1168 11C0; # (롙; 롙; 롙; 롙; 롙; ) HANGUL SYLLABLE RYET
+B85A;B85A;1105 1168 11C1;B85A;1105 1168 11C1; # (롚; 롚; á„…á…¨á‡; 롚; á„…á…¨á‡; ) HANGUL SYLLABLE RYEP
+B85B;B85B;1105 1168 11C2;B85B;1105 1168 11C2; # (롛; 롛; 롛; 롛; 롛; ) HANGUL SYLLABLE RYEH
+B85C;B85C;1105 1169;B85C;1105 1169; # (로; 로; 로; 로; 로; ) HANGUL SYLLABLE RO
+B85D;B85D;1105 1169 11A8;B85D;1105 1169 11A8; # (ë¡; ë¡; 록; ë¡; 록; ) HANGUL SYLLABLE ROG
+B85E;B85E;1105 1169 11A9;B85E;1105 1169 11A9; # (롞; 롞; 롞; 롞; 롞; ) HANGUL SYLLABLE ROGG
+B85F;B85F;1105 1169 11AA;B85F;1105 1169 11AA; # (롟; 롟; 롟; 롟; 롟; ) HANGUL SYLLABLE ROGS
+B860;B860;1105 1169 11AB;B860;1105 1169 11AB; # (론; 론; 론; 론; 론; ) HANGUL SYLLABLE RON
+B861;B861;1105 1169 11AC;B861;1105 1169 11AC; # (롡; 롡; 롡; 롡; 롡; ) HANGUL SYLLABLE RONJ
+B862;B862;1105 1169 11AD;B862;1105 1169 11AD; # (롢; 롢; 롢; 롢; 롢; ) HANGUL SYLLABLE RONH
+B863;B863;1105 1169 11AE;B863;1105 1169 11AE; # (롣; 롣; 롣; 롣; 롣; ) HANGUL SYLLABLE ROD
+B864;B864;1105 1169 11AF;B864;1105 1169 11AF; # (롤; 롤; 롤; 롤; 롤; ) HANGUL SYLLABLE ROL
+B865;B865;1105 1169 11B0;B865;1105 1169 11B0; # (롥; 롥; 롥; 롥; 롥; ) HANGUL SYLLABLE ROLG
+B866;B866;1105 1169 11B1;B866;1105 1169 11B1; # (롦; 롦; 롦; 롦; 롦; ) HANGUL SYLLABLE ROLM
+B867;B867;1105 1169 11B2;B867;1105 1169 11B2; # (롧; 롧; 롧; 롧; 롧; ) HANGUL SYLLABLE ROLB
+B868;B868;1105 1169 11B3;B868;1105 1169 11B3; # (롨; 롨; 롨; 롨; 롨; ) HANGUL SYLLABLE ROLS
+B869;B869;1105 1169 11B4;B869;1105 1169 11B4; # (롩; 롩; 롩; 롩; 롩; ) HANGUL SYLLABLE ROLT
+B86A;B86A;1105 1169 11B5;B86A;1105 1169 11B5; # (롪; 롪; 롪; 롪; 롪; ) HANGUL SYLLABLE ROLP
+B86B;B86B;1105 1169 11B6;B86B;1105 1169 11B6; # (롫; 롫; 롫; 롫; 롫; ) HANGUL SYLLABLE ROLH
+B86C;B86C;1105 1169 11B7;B86C;1105 1169 11B7; # (롬; 롬; 롬; 롬; 롬; ) HANGUL SYLLABLE ROM
+B86D;B86D;1105 1169 11B8;B86D;1105 1169 11B8; # (롭; 롭; 롭; 롭; 롭; ) HANGUL SYLLABLE ROB
+B86E;B86E;1105 1169 11B9;B86E;1105 1169 11B9; # (롮; 롮; 롮; 롮; 롮; ) HANGUL SYLLABLE ROBS
+B86F;B86F;1105 1169 11BA;B86F;1105 1169 11BA; # (롯; 롯; 롯; 롯; 롯; ) HANGUL SYLLABLE ROS
+B870;B870;1105 1169 11BB;B870;1105 1169 11BB; # (롰; 롰; 롰; 롰; 롰; ) HANGUL SYLLABLE ROSS
+B871;B871;1105 1169 11BC;B871;1105 1169 11BC; # (롱; 롱; 롱; 롱; 롱; ) HANGUL SYLLABLE RONG
+B872;B872;1105 1169 11BD;B872;1105 1169 11BD; # (롲; 롲; 롲; 롲; 롲; ) HANGUL SYLLABLE ROJ
+B873;B873;1105 1169 11BE;B873;1105 1169 11BE; # (롳; 롳; 롳; 롳; 롳; ) HANGUL SYLLABLE ROC
+B874;B874;1105 1169 11BF;B874;1105 1169 11BF; # (롴; 롴; 롴; 롴; 롴; ) HANGUL SYLLABLE ROK
+B875;B875;1105 1169 11C0;B875;1105 1169 11C0; # (롵; 롵; 롵; 롵; 롵; ) HANGUL SYLLABLE ROT
+B876;B876;1105 1169 11C1;B876;1105 1169 11C1; # (ë¡¶; ë¡¶; á„…á…©á‡; ë¡¶; á„…á…©á‡; ) HANGUL SYLLABLE ROP
+B877;B877;1105 1169 11C2;B877;1105 1169 11C2; # (롷; 롷; 롷; 롷; 롷; ) HANGUL SYLLABLE ROH
+B878;B878;1105 116A;B878;1105 116A; # (롸; 롸; 롸; 롸; 롸; ) HANGUL SYLLABLE RWA
+B879;B879;1105 116A 11A8;B879;1105 116A 11A8; # (롹; 롹; 롹; 롹; 롹; ) HANGUL SYLLABLE RWAG
+B87A;B87A;1105 116A 11A9;B87A;1105 116A 11A9; # (롺; 롺; 롺; 롺; 롺; ) HANGUL SYLLABLE RWAGG
+B87B;B87B;1105 116A 11AA;B87B;1105 116A 11AA; # (롻; 롻; 롻; 롻; 롻; ) HANGUL SYLLABLE RWAGS
+B87C;B87C;1105 116A 11AB;B87C;1105 116A 11AB; # (롼; 롼; 롼; 롼; 롼; ) HANGUL SYLLABLE RWAN
+B87D;B87D;1105 116A 11AC;B87D;1105 116A 11AC; # (롽; 롽; 롽; 롽; 롽; ) HANGUL SYLLABLE RWANJ
+B87E;B87E;1105 116A 11AD;B87E;1105 116A 11AD; # (롾; 롾; 롾; 롾; 롾; ) HANGUL SYLLABLE RWANH
+B87F;B87F;1105 116A 11AE;B87F;1105 116A 11AE; # (롿; 롿; 롿; 롿; 롿; ) HANGUL SYLLABLE RWAD
+B880;B880;1105 116A 11AF;B880;1105 116A 11AF; # (뢀; 뢀; 뢀; 뢀; 뢀; ) HANGUL SYLLABLE RWAL
+B881;B881;1105 116A 11B0;B881;1105 116A 11B0; # (ë¢; ë¢; 뢁; ë¢; 뢁; ) HANGUL SYLLABLE RWALG
+B882;B882;1105 116A 11B1;B882;1105 116A 11B1; # (뢂; 뢂; 뢂; 뢂; 뢂; ) HANGUL SYLLABLE RWALM
+B883;B883;1105 116A 11B2;B883;1105 116A 11B2; # (뢃; 뢃; 뢃; 뢃; 뢃; ) HANGUL SYLLABLE RWALB
+B884;B884;1105 116A 11B3;B884;1105 116A 11B3; # (뢄; 뢄; 뢄; 뢄; 뢄; ) HANGUL SYLLABLE RWALS
+B885;B885;1105 116A 11B4;B885;1105 116A 11B4; # (뢅; 뢅; 뢅; 뢅; 뢅; ) HANGUL SYLLABLE RWALT
+B886;B886;1105 116A 11B5;B886;1105 116A 11B5; # (뢆; 뢆; 뢆; 뢆; 뢆; ) HANGUL SYLLABLE RWALP
+B887;B887;1105 116A 11B6;B887;1105 116A 11B6; # (뢇; 뢇; 뢇; 뢇; 뢇; ) HANGUL SYLLABLE RWALH
+B888;B888;1105 116A 11B7;B888;1105 116A 11B7; # (뢈; 뢈; 뢈; 뢈; 뢈; ) HANGUL SYLLABLE RWAM
+B889;B889;1105 116A 11B8;B889;1105 116A 11B8; # (뢉; 뢉; 뢉; 뢉; 뢉; ) HANGUL SYLLABLE RWAB
+B88A;B88A;1105 116A 11B9;B88A;1105 116A 11B9; # (뢊; 뢊; 뢊; 뢊; 뢊; ) HANGUL SYLLABLE RWABS
+B88B;B88B;1105 116A 11BA;B88B;1105 116A 11BA; # (뢋; 뢋; 뢋; 뢋; 뢋; ) HANGUL SYLLABLE RWAS
+B88C;B88C;1105 116A 11BB;B88C;1105 116A 11BB; # (뢌; 뢌; 뢌; 뢌; 뢌; ) HANGUL SYLLABLE RWASS
+B88D;B88D;1105 116A 11BC;B88D;1105 116A 11BC; # (ë¢; ë¢; 뢍; ë¢; 뢍; ) HANGUL SYLLABLE RWANG
+B88E;B88E;1105 116A 11BD;B88E;1105 116A 11BD; # (뢎; 뢎; 뢎; 뢎; 뢎; ) HANGUL SYLLABLE RWAJ
+B88F;B88F;1105 116A 11BE;B88F;1105 116A 11BE; # (ë¢; ë¢; 뢏; ë¢; 뢏; ) HANGUL SYLLABLE RWAC
+B890;B890;1105 116A 11BF;B890;1105 116A 11BF; # (ë¢; ë¢; 뢐; ë¢; 뢐; ) HANGUL SYLLABLE RWAK
+B891;B891;1105 116A 11C0;B891;1105 116A 11C0; # (뢑; 뢑; 뢑; 뢑; 뢑; ) HANGUL SYLLABLE RWAT
+B892;B892;1105 116A 11C1;B892;1105 116A 11C1; # (뢒; 뢒; á„…á…ªá‡; 뢒; á„…á…ªá‡; ) HANGUL SYLLABLE RWAP
+B893;B893;1105 116A 11C2;B893;1105 116A 11C2; # (뢓; 뢓; 뢓; 뢓; 뢓; ) HANGUL SYLLABLE RWAH
+B894;B894;1105 116B;B894;1105 116B; # (뢔; 뢔; 뢔; 뢔; 뢔; ) HANGUL SYLLABLE RWAE
+B895;B895;1105 116B 11A8;B895;1105 116B 11A8; # (뢕; 뢕; 뢕; 뢕; 뢕; ) HANGUL SYLLABLE RWAEG
+B896;B896;1105 116B 11A9;B896;1105 116B 11A9; # (뢖; 뢖; 뢖; 뢖; 뢖; ) HANGUL SYLLABLE RWAEGG
+B897;B897;1105 116B 11AA;B897;1105 116B 11AA; # (뢗; 뢗; 뢗; 뢗; 뢗; ) HANGUL SYLLABLE RWAEGS
+B898;B898;1105 116B 11AB;B898;1105 116B 11AB; # (뢘; 뢘; 뢘; 뢘; 뢘; ) HANGUL SYLLABLE RWAEN
+B899;B899;1105 116B 11AC;B899;1105 116B 11AC; # (뢙; 뢙; 뢙; 뢙; 뢙; ) HANGUL SYLLABLE RWAENJ
+B89A;B89A;1105 116B 11AD;B89A;1105 116B 11AD; # (뢚; 뢚; 뢚; 뢚; 뢚; ) HANGUL SYLLABLE RWAENH
+B89B;B89B;1105 116B 11AE;B89B;1105 116B 11AE; # (뢛; 뢛; 뢛; 뢛; 뢛; ) HANGUL SYLLABLE RWAED
+B89C;B89C;1105 116B 11AF;B89C;1105 116B 11AF; # (뢜; 뢜; 뢜; 뢜; 뢜; ) HANGUL SYLLABLE RWAEL
+B89D;B89D;1105 116B 11B0;B89D;1105 116B 11B0; # (ë¢; ë¢; 뢝; ë¢; 뢝; ) HANGUL SYLLABLE RWAELG
+B89E;B89E;1105 116B 11B1;B89E;1105 116B 11B1; # (뢞; 뢞; 뢞; 뢞; 뢞; ) HANGUL SYLLABLE RWAELM
+B89F;B89F;1105 116B 11B2;B89F;1105 116B 11B2; # (뢟; 뢟; 뢟; 뢟; 뢟; ) HANGUL SYLLABLE RWAELB
+B8A0;B8A0;1105 116B 11B3;B8A0;1105 116B 11B3; # (뢠; 뢠; 뢠; 뢠; 뢠; ) HANGUL SYLLABLE RWAELS
+B8A1;B8A1;1105 116B 11B4;B8A1;1105 116B 11B4; # (뢡; 뢡; 뢡; 뢡; 뢡; ) HANGUL SYLLABLE RWAELT
+B8A2;B8A2;1105 116B 11B5;B8A2;1105 116B 11B5; # (뢢; 뢢; 뢢; 뢢; 뢢; ) HANGUL SYLLABLE RWAELP
+B8A3;B8A3;1105 116B 11B6;B8A3;1105 116B 11B6; # (뢣; 뢣; 뢣; 뢣; 뢣; ) HANGUL SYLLABLE RWAELH
+B8A4;B8A4;1105 116B 11B7;B8A4;1105 116B 11B7; # (뢤; 뢤; 뢤; 뢤; 뢤; ) HANGUL SYLLABLE RWAEM
+B8A5;B8A5;1105 116B 11B8;B8A5;1105 116B 11B8; # (뢥; 뢥; 뢥; 뢥; 뢥; ) HANGUL SYLLABLE RWAEB
+B8A6;B8A6;1105 116B 11B9;B8A6;1105 116B 11B9; # (뢦; 뢦; 뢦; 뢦; 뢦; ) HANGUL SYLLABLE RWAEBS
+B8A7;B8A7;1105 116B 11BA;B8A7;1105 116B 11BA; # (뢧; 뢧; 뢧; 뢧; 뢧; ) HANGUL SYLLABLE RWAES
+B8A8;B8A8;1105 116B 11BB;B8A8;1105 116B 11BB; # (뢨; 뢨; 뢨; 뢨; 뢨; ) HANGUL SYLLABLE RWAESS
+B8A9;B8A9;1105 116B 11BC;B8A9;1105 116B 11BC; # (뢩; 뢩; 뢩; 뢩; 뢩; ) HANGUL SYLLABLE RWAENG
+B8AA;B8AA;1105 116B 11BD;B8AA;1105 116B 11BD; # (뢪; 뢪; 뢪; 뢪; 뢪; ) HANGUL SYLLABLE RWAEJ
+B8AB;B8AB;1105 116B 11BE;B8AB;1105 116B 11BE; # (뢫; 뢫; 뢫; 뢫; 뢫; ) HANGUL SYLLABLE RWAEC
+B8AC;B8AC;1105 116B 11BF;B8AC;1105 116B 11BF; # (뢬; 뢬; 뢬; 뢬; 뢬; ) HANGUL SYLLABLE RWAEK
+B8AD;B8AD;1105 116B 11C0;B8AD;1105 116B 11C0; # (뢭; 뢭; 뢭; 뢭; 뢭; ) HANGUL SYLLABLE RWAET
+B8AE;B8AE;1105 116B 11C1;B8AE;1105 116B 11C1; # (뢮; 뢮; á„…á…«á‡; 뢮; á„…á…«á‡; ) HANGUL SYLLABLE RWAEP
+B8AF;B8AF;1105 116B 11C2;B8AF;1105 116B 11C2; # (뢯; 뢯; 뢯; 뢯; 뢯; ) HANGUL SYLLABLE RWAEH
+B8B0;B8B0;1105 116C;B8B0;1105 116C; # (뢰; 뢰; 뢰; 뢰; 뢰; ) HANGUL SYLLABLE ROE
+B8B1;B8B1;1105 116C 11A8;B8B1;1105 116C 11A8; # (뢱; 뢱; 뢱; 뢱; 뢱; ) HANGUL SYLLABLE ROEG
+B8B2;B8B2;1105 116C 11A9;B8B2;1105 116C 11A9; # (뢲; 뢲; 뢲; 뢲; 뢲; ) HANGUL SYLLABLE ROEGG
+B8B3;B8B3;1105 116C 11AA;B8B3;1105 116C 11AA; # (뢳; 뢳; 뢳; 뢳; 뢳; ) HANGUL SYLLABLE ROEGS
+B8B4;B8B4;1105 116C 11AB;B8B4;1105 116C 11AB; # (뢴; 뢴; 뢴; 뢴; 뢴; ) HANGUL SYLLABLE ROEN
+B8B5;B8B5;1105 116C 11AC;B8B5;1105 116C 11AC; # (뢵; 뢵; 뢵; 뢵; 뢵; ) HANGUL SYLLABLE ROENJ
+B8B6;B8B6;1105 116C 11AD;B8B6;1105 116C 11AD; # (뢶; 뢶; 뢶; 뢶; 뢶; ) HANGUL SYLLABLE ROENH
+B8B7;B8B7;1105 116C 11AE;B8B7;1105 116C 11AE; # (뢷; 뢷; 뢷; 뢷; 뢷; ) HANGUL SYLLABLE ROED
+B8B8;B8B8;1105 116C 11AF;B8B8;1105 116C 11AF; # (뢸; 뢸; 뢸; 뢸; 뢸; ) HANGUL SYLLABLE ROEL
+B8B9;B8B9;1105 116C 11B0;B8B9;1105 116C 11B0; # (뢹; 뢹; 뢹; 뢹; 뢹; ) HANGUL SYLLABLE ROELG
+B8BA;B8BA;1105 116C 11B1;B8BA;1105 116C 11B1; # (뢺; 뢺; 뢺; 뢺; 뢺; ) HANGUL SYLLABLE ROELM
+B8BB;B8BB;1105 116C 11B2;B8BB;1105 116C 11B2; # (뢻; 뢻; 뢻; 뢻; 뢻; ) HANGUL SYLLABLE ROELB
+B8BC;B8BC;1105 116C 11B3;B8BC;1105 116C 11B3; # (뢼; 뢼; 뢼; 뢼; 뢼; ) HANGUL SYLLABLE ROELS
+B8BD;B8BD;1105 116C 11B4;B8BD;1105 116C 11B4; # (뢽; 뢽; 뢽; 뢽; 뢽; ) HANGUL SYLLABLE ROELT
+B8BE;B8BE;1105 116C 11B5;B8BE;1105 116C 11B5; # (뢾; 뢾; 뢾; 뢾; 뢾; ) HANGUL SYLLABLE ROELP
+B8BF;B8BF;1105 116C 11B6;B8BF;1105 116C 11B6; # (뢿; 뢿; 뢿; 뢿; 뢿; ) HANGUL SYLLABLE ROELH
+B8C0;B8C0;1105 116C 11B7;B8C0;1105 116C 11B7; # (룀; 룀; 룀; 룀; 룀; ) HANGUL SYLLABLE ROEM
+B8C1;B8C1;1105 116C 11B8;B8C1;1105 116C 11B8; # (ë£; ë£; 룁; ë£; 룁; ) HANGUL SYLLABLE ROEB
+B8C2;B8C2;1105 116C 11B9;B8C2;1105 116C 11B9; # (룂; 룂; 룂; 룂; 룂; ) HANGUL SYLLABLE ROEBS
+B8C3;B8C3;1105 116C 11BA;B8C3;1105 116C 11BA; # (룃; 룃; 룃; 룃; 룃; ) HANGUL SYLLABLE ROES
+B8C4;B8C4;1105 116C 11BB;B8C4;1105 116C 11BB; # (룄; 룄; 룄; 룄; 룄; ) HANGUL SYLLABLE ROESS
+B8C5;B8C5;1105 116C 11BC;B8C5;1105 116C 11BC; # (룅; 룅; 룅; 룅; 룅; ) HANGUL SYLLABLE ROENG
+B8C6;B8C6;1105 116C 11BD;B8C6;1105 116C 11BD; # (룆; 룆; 룆; 룆; 룆; ) HANGUL SYLLABLE ROEJ
+B8C7;B8C7;1105 116C 11BE;B8C7;1105 116C 11BE; # (룇; 룇; 룇; 룇; 룇; ) HANGUL SYLLABLE ROEC
+B8C8;B8C8;1105 116C 11BF;B8C8;1105 116C 11BF; # (룈; 룈; 룈; 룈; 룈; ) HANGUL SYLLABLE ROEK
+B8C9;B8C9;1105 116C 11C0;B8C9;1105 116C 11C0; # (룉; 룉; 룉; 룉; 룉; ) HANGUL SYLLABLE ROET
+B8CA;B8CA;1105 116C 11C1;B8CA;1105 116C 11C1; # (룊; 룊; á„…á…¬á‡; 룊; á„…á…¬á‡; ) HANGUL SYLLABLE ROEP
+B8CB;B8CB;1105 116C 11C2;B8CB;1105 116C 11C2; # (룋; 룋; 룋; 룋; 룋; ) HANGUL SYLLABLE ROEH
+B8CC;B8CC;1105 116D;B8CC;1105 116D; # (료; 료; 료; 료; 료; ) HANGUL SYLLABLE RYO
+B8CD;B8CD;1105 116D 11A8;B8CD;1105 116D 11A8; # (ë£; ë£; 룍; ë£; 룍; ) HANGUL SYLLABLE RYOG
+B8CE;B8CE;1105 116D 11A9;B8CE;1105 116D 11A9; # (룎; 룎; 룎; 룎; 룎; ) HANGUL SYLLABLE RYOGG
+B8CF;B8CF;1105 116D 11AA;B8CF;1105 116D 11AA; # (ë£; ë£; 룏; ë£; 룏; ) HANGUL SYLLABLE RYOGS
+B8D0;B8D0;1105 116D 11AB;B8D0;1105 116D 11AB; # (ë£; ë£; 룐; ë£; 룐; ) HANGUL SYLLABLE RYON
+B8D1;B8D1;1105 116D 11AC;B8D1;1105 116D 11AC; # (룑; 룑; 룑; 룑; 룑; ) HANGUL SYLLABLE RYONJ
+B8D2;B8D2;1105 116D 11AD;B8D2;1105 116D 11AD; # (룒; 룒; 룒; 룒; 룒; ) HANGUL SYLLABLE RYONH
+B8D3;B8D3;1105 116D 11AE;B8D3;1105 116D 11AE; # (룓; 룓; 룓; 룓; 룓; ) HANGUL SYLLABLE RYOD
+B8D4;B8D4;1105 116D 11AF;B8D4;1105 116D 11AF; # (룔; 룔; 룔; 룔; 룔; ) HANGUL SYLLABLE RYOL
+B8D5;B8D5;1105 116D 11B0;B8D5;1105 116D 11B0; # (룕; 룕; 룕; 룕; 룕; ) HANGUL SYLLABLE RYOLG
+B8D6;B8D6;1105 116D 11B1;B8D6;1105 116D 11B1; # (룖; 룖; 룖; 룖; 룖; ) HANGUL SYLLABLE RYOLM
+B8D7;B8D7;1105 116D 11B2;B8D7;1105 116D 11B2; # (룗; 룗; 룗; 룗; 룗; ) HANGUL SYLLABLE RYOLB
+B8D8;B8D8;1105 116D 11B3;B8D8;1105 116D 11B3; # (룘; 룘; 룘; 룘; 룘; ) HANGUL SYLLABLE RYOLS
+B8D9;B8D9;1105 116D 11B4;B8D9;1105 116D 11B4; # (룙; 룙; 룙; 룙; 룙; ) HANGUL SYLLABLE RYOLT
+B8DA;B8DA;1105 116D 11B5;B8DA;1105 116D 11B5; # (룚; 룚; 룚; 룚; 룚; ) HANGUL SYLLABLE RYOLP
+B8DB;B8DB;1105 116D 11B6;B8DB;1105 116D 11B6; # (룛; 룛; 룛; 룛; 룛; ) HANGUL SYLLABLE RYOLH
+B8DC;B8DC;1105 116D 11B7;B8DC;1105 116D 11B7; # (룜; 룜; 룜; 룜; 룜; ) HANGUL SYLLABLE RYOM
+B8DD;B8DD;1105 116D 11B8;B8DD;1105 116D 11B8; # (ë£; ë£; 룝; ë£; 룝; ) HANGUL SYLLABLE RYOB
+B8DE;B8DE;1105 116D 11B9;B8DE;1105 116D 11B9; # (룞; 룞; 룞; 룞; 룞; ) HANGUL SYLLABLE RYOBS
+B8DF;B8DF;1105 116D 11BA;B8DF;1105 116D 11BA; # (룟; 룟; 룟; 룟; 룟; ) HANGUL SYLLABLE RYOS
+B8E0;B8E0;1105 116D 11BB;B8E0;1105 116D 11BB; # (룠; 룠; 룠; 룠; 룠; ) HANGUL SYLLABLE RYOSS
+B8E1;B8E1;1105 116D 11BC;B8E1;1105 116D 11BC; # (룡; 룡; 룡; 룡; 룡; ) HANGUL SYLLABLE RYONG
+B8E2;B8E2;1105 116D 11BD;B8E2;1105 116D 11BD; # (룢; 룢; 룢; 룢; 룢; ) HANGUL SYLLABLE RYOJ
+B8E3;B8E3;1105 116D 11BE;B8E3;1105 116D 11BE; # (룣; 룣; 룣; 룣; 룣; ) HANGUL SYLLABLE RYOC
+B8E4;B8E4;1105 116D 11BF;B8E4;1105 116D 11BF; # (룤; 룤; 룤; 룤; 룤; ) HANGUL SYLLABLE RYOK
+B8E5;B8E5;1105 116D 11C0;B8E5;1105 116D 11C0; # (룥; 룥; 룥; 룥; 룥; ) HANGUL SYLLABLE RYOT
+B8E6;B8E6;1105 116D 11C1;B8E6;1105 116D 11C1; # (룦; 룦; á„…á…­á‡; 룦; á„…á…­á‡; ) HANGUL SYLLABLE RYOP
+B8E7;B8E7;1105 116D 11C2;B8E7;1105 116D 11C2; # (룧; 룧; 룧; 룧; 룧; ) HANGUL SYLLABLE RYOH
+B8E8;B8E8;1105 116E;B8E8;1105 116E; # (루; 루; 루; 루; 루; ) HANGUL SYLLABLE RU
+B8E9;B8E9;1105 116E 11A8;B8E9;1105 116E 11A8; # (룩; 룩; 룩; 룩; 룩; ) HANGUL SYLLABLE RUG
+B8EA;B8EA;1105 116E 11A9;B8EA;1105 116E 11A9; # (룪; 룪; 룪; 룪; 룪; ) HANGUL SYLLABLE RUGG
+B8EB;B8EB;1105 116E 11AA;B8EB;1105 116E 11AA; # (룫; 룫; 룫; 룫; 룫; ) HANGUL SYLLABLE RUGS
+B8EC;B8EC;1105 116E 11AB;B8EC;1105 116E 11AB; # (룬; 룬; 룬; 룬; 룬; ) HANGUL SYLLABLE RUN
+B8ED;B8ED;1105 116E 11AC;B8ED;1105 116E 11AC; # (룭; 룭; 룭; 룭; 룭; ) HANGUL SYLLABLE RUNJ
+B8EE;B8EE;1105 116E 11AD;B8EE;1105 116E 11AD; # (룮; 룮; 룮; 룮; 룮; ) HANGUL SYLLABLE RUNH
+B8EF;B8EF;1105 116E 11AE;B8EF;1105 116E 11AE; # (룯; 룯; 룯; 룯; 룯; ) HANGUL SYLLABLE RUD
+B8F0;B8F0;1105 116E 11AF;B8F0;1105 116E 11AF; # (룰; 룰; 룰; 룰; 룰; ) HANGUL SYLLABLE RUL
+B8F1;B8F1;1105 116E 11B0;B8F1;1105 116E 11B0; # (룱; 룱; 룱; 룱; 룱; ) HANGUL SYLLABLE RULG
+B8F2;B8F2;1105 116E 11B1;B8F2;1105 116E 11B1; # (룲; 룲; 룲; 룲; 룲; ) HANGUL SYLLABLE RULM
+B8F3;B8F3;1105 116E 11B2;B8F3;1105 116E 11B2; # (룳; 룳; 룳; 룳; 룳; ) HANGUL SYLLABLE RULB
+B8F4;B8F4;1105 116E 11B3;B8F4;1105 116E 11B3; # (룴; 룴; 룴; 룴; 룴; ) HANGUL SYLLABLE RULS
+B8F5;B8F5;1105 116E 11B4;B8F5;1105 116E 11B4; # (룵; 룵; 룵; 룵; 룵; ) HANGUL SYLLABLE RULT
+B8F6;B8F6;1105 116E 11B5;B8F6;1105 116E 11B5; # (룶; 룶; 룶; 룶; 룶; ) HANGUL SYLLABLE RULP
+B8F7;B8F7;1105 116E 11B6;B8F7;1105 116E 11B6; # (룷; 룷; 룷; 룷; 룷; ) HANGUL SYLLABLE RULH
+B8F8;B8F8;1105 116E 11B7;B8F8;1105 116E 11B7; # (룸; 룸; 룸; 룸; 룸; ) HANGUL SYLLABLE RUM
+B8F9;B8F9;1105 116E 11B8;B8F9;1105 116E 11B8; # (룹; 룹; 룹; 룹; 룹; ) HANGUL SYLLABLE RUB
+B8FA;B8FA;1105 116E 11B9;B8FA;1105 116E 11B9; # (룺; 룺; 룺; 룺; 룺; ) HANGUL SYLLABLE RUBS
+B8FB;B8FB;1105 116E 11BA;B8FB;1105 116E 11BA; # (룻; 룻; 룻; 룻; 룻; ) HANGUL SYLLABLE RUS
+B8FC;B8FC;1105 116E 11BB;B8FC;1105 116E 11BB; # (룼; 룼; 룼; 룼; 룼; ) HANGUL SYLLABLE RUSS
+B8FD;B8FD;1105 116E 11BC;B8FD;1105 116E 11BC; # (룽; 룽; 룽; 룽; 룽; ) HANGUL SYLLABLE RUNG
+B8FE;B8FE;1105 116E 11BD;B8FE;1105 116E 11BD; # (룾; 룾; 룾; 룾; 룾; ) HANGUL SYLLABLE RUJ
+B8FF;B8FF;1105 116E 11BE;B8FF;1105 116E 11BE; # (룿; 룿; 룿; 룿; 룿; ) HANGUL SYLLABLE RUC
+B900;B900;1105 116E 11BF;B900;1105 116E 11BF; # (뤀; 뤀; 뤀; 뤀; 뤀; ) HANGUL SYLLABLE RUK
+B901;B901;1105 116E 11C0;B901;1105 116E 11C0; # (ë¤; ë¤; 뤁; ë¤; 뤁; ) HANGUL SYLLABLE RUT
+B902;B902;1105 116E 11C1;B902;1105 116E 11C1; # (뤂; 뤂; á„…á…®á‡; 뤂; á„…á…®á‡; ) HANGUL SYLLABLE RUP
+B903;B903;1105 116E 11C2;B903;1105 116E 11C2; # (뤃; 뤃; 뤃; 뤃; 뤃; ) HANGUL SYLLABLE RUH
+B904;B904;1105 116F;B904;1105 116F; # (뤄; 뤄; 뤄; 뤄; 뤄; ) HANGUL SYLLABLE RWEO
+B905;B905;1105 116F 11A8;B905;1105 116F 11A8; # (뤅; 뤅; 뤅; 뤅; 뤅; ) HANGUL SYLLABLE RWEOG
+B906;B906;1105 116F 11A9;B906;1105 116F 11A9; # (뤆; 뤆; 뤆; 뤆; 뤆; ) HANGUL SYLLABLE RWEOGG
+B907;B907;1105 116F 11AA;B907;1105 116F 11AA; # (뤇; 뤇; 뤇; 뤇; 뤇; ) HANGUL SYLLABLE RWEOGS
+B908;B908;1105 116F 11AB;B908;1105 116F 11AB; # (뤈; 뤈; 뤈; 뤈; 뤈; ) HANGUL SYLLABLE RWEON
+B909;B909;1105 116F 11AC;B909;1105 116F 11AC; # (뤉; 뤉; 뤉; 뤉; 뤉; ) HANGUL SYLLABLE RWEONJ
+B90A;B90A;1105 116F 11AD;B90A;1105 116F 11AD; # (뤊; 뤊; 뤊; 뤊; 뤊; ) HANGUL SYLLABLE RWEONH
+B90B;B90B;1105 116F 11AE;B90B;1105 116F 11AE; # (뤋; 뤋; 뤋; 뤋; 뤋; ) HANGUL SYLLABLE RWEOD
+B90C;B90C;1105 116F 11AF;B90C;1105 116F 11AF; # (뤌; 뤌; 뤌; 뤌; 뤌; ) HANGUL SYLLABLE RWEOL
+B90D;B90D;1105 116F 11B0;B90D;1105 116F 11B0; # (ë¤; ë¤; 뤍; ë¤; 뤍; ) HANGUL SYLLABLE RWEOLG
+B90E;B90E;1105 116F 11B1;B90E;1105 116F 11B1; # (뤎; 뤎; 뤎; 뤎; 뤎; ) HANGUL SYLLABLE RWEOLM
+B90F;B90F;1105 116F 11B2;B90F;1105 116F 11B2; # (ë¤; ë¤; 뤏; ë¤; 뤏; ) HANGUL SYLLABLE RWEOLB
+B910;B910;1105 116F 11B3;B910;1105 116F 11B3; # (ë¤; ë¤; 뤐; ë¤; 뤐; ) HANGUL SYLLABLE RWEOLS
+B911;B911;1105 116F 11B4;B911;1105 116F 11B4; # (뤑; 뤑; 뤑; 뤑; 뤑; ) HANGUL SYLLABLE RWEOLT
+B912;B912;1105 116F 11B5;B912;1105 116F 11B5; # (뤒; 뤒; 뤒; 뤒; 뤒; ) HANGUL SYLLABLE RWEOLP
+B913;B913;1105 116F 11B6;B913;1105 116F 11B6; # (뤓; 뤓; 뤓; 뤓; 뤓; ) HANGUL SYLLABLE RWEOLH
+B914;B914;1105 116F 11B7;B914;1105 116F 11B7; # (뤔; 뤔; 뤔; 뤔; 뤔; ) HANGUL SYLLABLE RWEOM
+B915;B915;1105 116F 11B8;B915;1105 116F 11B8; # (뤕; 뤕; 뤕; 뤕; 뤕; ) HANGUL SYLLABLE RWEOB
+B916;B916;1105 116F 11B9;B916;1105 116F 11B9; # (뤖; 뤖; 뤖; 뤖; 뤖; ) HANGUL SYLLABLE RWEOBS
+B917;B917;1105 116F 11BA;B917;1105 116F 11BA; # (뤗; 뤗; 뤗; 뤗; 뤗; ) HANGUL SYLLABLE RWEOS
+B918;B918;1105 116F 11BB;B918;1105 116F 11BB; # (뤘; 뤘; 뤘; 뤘; 뤘; ) HANGUL SYLLABLE RWEOSS
+B919;B919;1105 116F 11BC;B919;1105 116F 11BC; # (뤙; 뤙; 뤙; 뤙; 뤙; ) HANGUL SYLLABLE RWEONG
+B91A;B91A;1105 116F 11BD;B91A;1105 116F 11BD; # (뤚; 뤚; 뤚; 뤚; 뤚; ) HANGUL SYLLABLE RWEOJ
+B91B;B91B;1105 116F 11BE;B91B;1105 116F 11BE; # (뤛; 뤛; 뤛; 뤛; 뤛; ) HANGUL SYLLABLE RWEOC
+B91C;B91C;1105 116F 11BF;B91C;1105 116F 11BF; # (뤜; 뤜; 뤜; 뤜; 뤜; ) HANGUL SYLLABLE RWEOK
+B91D;B91D;1105 116F 11C0;B91D;1105 116F 11C0; # (ë¤; ë¤; 뤝; ë¤; 뤝; ) HANGUL SYLLABLE RWEOT
+B91E;B91E;1105 116F 11C1;B91E;1105 116F 11C1; # (뤞; 뤞; á„…á…¯á‡; 뤞; á„…á…¯á‡; ) HANGUL SYLLABLE RWEOP
+B91F;B91F;1105 116F 11C2;B91F;1105 116F 11C2; # (뤟; 뤟; 뤟; 뤟; 뤟; ) HANGUL SYLLABLE RWEOH
+B920;B920;1105 1170;B920;1105 1170; # (뤠; 뤠; 뤠; 뤠; 뤠; ) HANGUL SYLLABLE RWE
+B921;B921;1105 1170 11A8;B921;1105 1170 11A8; # (뤡; 뤡; 뤡; 뤡; 뤡; ) HANGUL SYLLABLE RWEG
+B922;B922;1105 1170 11A9;B922;1105 1170 11A9; # (뤢; 뤢; 뤢; 뤢; 뤢; ) HANGUL SYLLABLE RWEGG
+B923;B923;1105 1170 11AA;B923;1105 1170 11AA; # (뤣; 뤣; 뤣; 뤣; 뤣; ) HANGUL SYLLABLE RWEGS
+B924;B924;1105 1170 11AB;B924;1105 1170 11AB; # (뤤; 뤤; 뤤; 뤤; 뤤; ) HANGUL SYLLABLE RWEN
+B925;B925;1105 1170 11AC;B925;1105 1170 11AC; # (뤥; 뤥; 뤥; 뤥; 뤥; ) HANGUL SYLLABLE RWENJ
+B926;B926;1105 1170 11AD;B926;1105 1170 11AD; # (뤦; 뤦; 뤦; 뤦; 뤦; ) HANGUL SYLLABLE RWENH
+B927;B927;1105 1170 11AE;B927;1105 1170 11AE; # (뤧; 뤧; 뤧; 뤧; 뤧; ) HANGUL SYLLABLE RWED
+B928;B928;1105 1170 11AF;B928;1105 1170 11AF; # (뤨; 뤨; 뤨; 뤨; 뤨; ) HANGUL SYLLABLE RWEL
+B929;B929;1105 1170 11B0;B929;1105 1170 11B0; # (뤩; 뤩; 뤩; 뤩; 뤩; ) HANGUL SYLLABLE RWELG
+B92A;B92A;1105 1170 11B1;B92A;1105 1170 11B1; # (뤪; 뤪; 뤪; 뤪; 뤪; ) HANGUL SYLLABLE RWELM
+B92B;B92B;1105 1170 11B2;B92B;1105 1170 11B2; # (뤫; 뤫; 뤫; 뤫; 뤫; ) HANGUL SYLLABLE RWELB
+B92C;B92C;1105 1170 11B3;B92C;1105 1170 11B3; # (뤬; 뤬; 뤬; 뤬; 뤬; ) HANGUL SYLLABLE RWELS
+B92D;B92D;1105 1170 11B4;B92D;1105 1170 11B4; # (뤭; 뤭; 뤭; 뤭; 뤭; ) HANGUL SYLLABLE RWELT
+B92E;B92E;1105 1170 11B5;B92E;1105 1170 11B5; # (뤮; 뤮; 뤮; 뤮; 뤮; ) HANGUL SYLLABLE RWELP
+B92F;B92F;1105 1170 11B6;B92F;1105 1170 11B6; # (뤯; 뤯; 뤯; 뤯; 뤯; ) HANGUL SYLLABLE RWELH
+B930;B930;1105 1170 11B7;B930;1105 1170 11B7; # (뤰; 뤰; 뤰; 뤰; 뤰; ) HANGUL SYLLABLE RWEM
+B931;B931;1105 1170 11B8;B931;1105 1170 11B8; # (뤱; 뤱; 뤱; 뤱; 뤱; ) HANGUL SYLLABLE RWEB
+B932;B932;1105 1170 11B9;B932;1105 1170 11B9; # (뤲; 뤲; 뤲; 뤲; 뤲; ) HANGUL SYLLABLE RWEBS
+B933;B933;1105 1170 11BA;B933;1105 1170 11BA; # (뤳; 뤳; 뤳; 뤳; 뤳; ) HANGUL SYLLABLE RWES
+B934;B934;1105 1170 11BB;B934;1105 1170 11BB; # (뤴; 뤴; 뤴; 뤴; 뤴; ) HANGUL SYLLABLE RWESS
+B935;B935;1105 1170 11BC;B935;1105 1170 11BC; # (뤵; 뤵; 뤵; 뤵; 뤵; ) HANGUL SYLLABLE RWENG
+B936;B936;1105 1170 11BD;B936;1105 1170 11BD; # (뤶; 뤶; 뤶; 뤶; 뤶; ) HANGUL SYLLABLE RWEJ
+B937;B937;1105 1170 11BE;B937;1105 1170 11BE; # (뤷; 뤷; 뤷; 뤷; 뤷; ) HANGUL SYLLABLE RWEC
+B938;B938;1105 1170 11BF;B938;1105 1170 11BF; # (뤸; 뤸; 뤸; 뤸; 뤸; ) HANGUL SYLLABLE RWEK
+B939;B939;1105 1170 11C0;B939;1105 1170 11C0; # (뤹; 뤹; 뤹; 뤹; 뤹; ) HANGUL SYLLABLE RWET
+B93A;B93A;1105 1170 11C1;B93A;1105 1170 11C1; # (뤺; 뤺; á„…á…°á‡; 뤺; á„…á…°á‡; ) HANGUL SYLLABLE RWEP
+B93B;B93B;1105 1170 11C2;B93B;1105 1170 11C2; # (뤻; 뤻; 뤻; 뤻; 뤻; ) HANGUL SYLLABLE RWEH
+B93C;B93C;1105 1171;B93C;1105 1171; # (뤼; 뤼; 뤼; 뤼; 뤼; ) HANGUL SYLLABLE RWI
+B93D;B93D;1105 1171 11A8;B93D;1105 1171 11A8; # (뤽; 뤽; 뤽; 뤽; 뤽; ) HANGUL SYLLABLE RWIG
+B93E;B93E;1105 1171 11A9;B93E;1105 1171 11A9; # (뤾; 뤾; 뤾; 뤾; 뤾; ) HANGUL SYLLABLE RWIGG
+B93F;B93F;1105 1171 11AA;B93F;1105 1171 11AA; # (뤿; 뤿; 뤿; 뤿; 뤿; ) HANGUL SYLLABLE RWIGS
+B940;B940;1105 1171 11AB;B940;1105 1171 11AB; # (륀; 륀; 륀; 륀; 륀; ) HANGUL SYLLABLE RWIN
+B941;B941;1105 1171 11AC;B941;1105 1171 11AC; # (ë¥; ë¥; 륁; ë¥; 륁; ) HANGUL SYLLABLE RWINJ
+B942;B942;1105 1171 11AD;B942;1105 1171 11AD; # (륂; 륂; 륂; 륂; 륂; ) HANGUL SYLLABLE RWINH
+B943;B943;1105 1171 11AE;B943;1105 1171 11AE; # (륃; 륃; 륃; 륃; 륃; ) HANGUL SYLLABLE RWID
+B944;B944;1105 1171 11AF;B944;1105 1171 11AF; # (륄; 륄; 륄; 륄; 륄; ) HANGUL SYLLABLE RWIL
+B945;B945;1105 1171 11B0;B945;1105 1171 11B0; # (륅; 륅; 륅; 륅; 륅; ) HANGUL SYLLABLE RWILG
+B946;B946;1105 1171 11B1;B946;1105 1171 11B1; # (륆; 륆; 륆; 륆; 륆; ) HANGUL SYLLABLE RWILM
+B947;B947;1105 1171 11B2;B947;1105 1171 11B2; # (륇; 륇; 륇; 륇; 륇; ) HANGUL SYLLABLE RWILB
+B948;B948;1105 1171 11B3;B948;1105 1171 11B3; # (륈; 륈; 륈; 륈; 륈; ) HANGUL SYLLABLE RWILS
+B949;B949;1105 1171 11B4;B949;1105 1171 11B4; # (륉; 륉; 륉; 륉; 륉; ) HANGUL SYLLABLE RWILT
+B94A;B94A;1105 1171 11B5;B94A;1105 1171 11B5; # (륊; 륊; 륊; 륊; 륊; ) HANGUL SYLLABLE RWILP
+B94B;B94B;1105 1171 11B6;B94B;1105 1171 11B6; # (륋; 륋; 륋; 륋; 륋; ) HANGUL SYLLABLE RWILH
+B94C;B94C;1105 1171 11B7;B94C;1105 1171 11B7; # (륌; 륌; 륌; 륌; 륌; ) HANGUL SYLLABLE RWIM
+B94D;B94D;1105 1171 11B8;B94D;1105 1171 11B8; # (ë¥; ë¥; 륍; ë¥; 륍; ) HANGUL SYLLABLE RWIB
+B94E;B94E;1105 1171 11B9;B94E;1105 1171 11B9; # (륎; 륎; 륎; 륎; 륎; ) HANGUL SYLLABLE RWIBS
+B94F;B94F;1105 1171 11BA;B94F;1105 1171 11BA; # (ë¥; ë¥; 륏; ë¥; 륏; ) HANGUL SYLLABLE RWIS
+B950;B950;1105 1171 11BB;B950;1105 1171 11BB; # (ë¥; ë¥; 륐; ë¥; 륐; ) HANGUL SYLLABLE RWISS
+B951;B951;1105 1171 11BC;B951;1105 1171 11BC; # (륑; 륑; 륑; 륑; 륑; ) HANGUL SYLLABLE RWING
+B952;B952;1105 1171 11BD;B952;1105 1171 11BD; # (륒; 륒; 륒; 륒; 륒; ) HANGUL SYLLABLE RWIJ
+B953;B953;1105 1171 11BE;B953;1105 1171 11BE; # (륓; 륓; 륓; 륓; 륓; ) HANGUL SYLLABLE RWIC
+B954;B954;1105 1171 11BF;B954;1105 1171 11BF; # (륔; 륔; 륔; 륔; 륔; ) HANGUL SYLLABLE RWIK
+B955;B955;1105 1171 11C0;B955;1105 1171 11C0; # (륕; 륕; 륕; 륕; 륕; ) HANGUL SYLLABLE RWIT
+B956;B956;1105 1171 11C1;B956;1105 1171 11C1; # (륖; 륖; á„…á…±á‡; 륖; á„…á…±á‡; ) HANGUL SYLLABLE RWIP
+B957;B957;1105 1171 11C2;B957;1105 1171 11C2; # (륗; 륗; 륗; 륗; 륗; ) HANGUL SYLLABLE RWIH
+B958;B958;1105 1172;B958;1105 1172; # (류; 류; 류; 류; 류; ) HANGUL SYLLABLE RYU
+B959;B959;1105 1172 11A8;B959;1105 1172 11A8; # (륙; 륙; 륙; 륙; 륙; ) HANGUL SYLLABLE RYUG
+B95A;B95A;1105 1172 11A9;B95A;1105 1172 11A9; # (륚; 륚; 륚; 륚; 륚; ) HANGUL SYLLABLE RYUGG
+B95B;B95B;1105 1172 11AA;B95B;1105 1172 11AA; # (륛; 륛; 륛; 륛; 륛; ) HANGUL SYLLABLE RYUGS
+B95C;B95C;1105 1172 11AB;B95C;1105 1172 11AB; # (륜; 륜; 륜; 륜; 륜; ) HANGUL SYLLABLE RYUN
+B95D;B95D;1105 1172 11AC;B95D;1105 1172 11AC; # (ë¥; ë¥; 륝; ë¥; 륝; ) HANGUL SYLLABLE RYUNJ
+B95E;B95E;1105 1172 11AD;B95E;1105 1172 11AD; # (륞; 륞; 륞; 륞; 륞; ) HANGUL SYLLABLE RYUNH
+B95F;B95F;1105 1172 11AE;B95F;1105 1172 11AE; # (륟; 륟; 륟; 륟; 륟; ) HANGUL SYLLABLE RYUD
+B960;B960;1105 1172 11AF;B960;1105 1172 11AF; # (률; 률; 률; 률; 률; ) HANGUL SYLLABLE RYUL
+B961;B961;1105 1172 11B0;B961;1105 1172 11B0; # (륡; 륡; 륡; 륡; 륡; ) HANGUL SYLLABLE RYULG
+B962;B962;1105 1172 11B1;B962;1105 1172 11B1; # (륢; 륢; 륢; 륢; 륢; ) HANGUL SYLLABLE RYULM
+B963;B963;1105 1172 11B2;B963;1105 1172 11B2; # (륣; 륣; 륣; 륣; 륣; ) HANGUL SYLLABLE RYULB
+B964;B964;1105 1172 11B3;B964;1105 1172 11B3; # (륤; 륤; 륤; 륤; 륤; ) HANGUL SYLLABLE RYULS
+B965;B965;1105 1172 11B4;B965;1105 1172 11B4; # (륥; 륥; 륥; 륥; 륥; ) HANGUL SYLLABLE RYULT
+B966;B966;1105 1172 11B5;B966;1105 1172 11B5; # (륦; 륦; 륦; 륦; 륦; ) HANGUL SYLLABLE RYULP
+B967;B967;1105 1172 11B6;B967;1105 1172 11B6; # (륧; 륧; 륧; 륧; 륧; ) HANGUL SYLLABLE RYULH
+B968;B968;1105 1172 11B7;B968;1105 1172 11B7; # (륨; 륨; 륨; 륨; 륨; ) HANGUL SYLLABLE RYUM
+B969;B969;1105 1172 11B8;B969;1105 1172 11B8; # (륩; 륩; 륩; 륩; 륩; ) HANGUL SYLLABLE RYUB
+B96A;B96A;1105 1172 11B9;B96A;1105 1172 11B9; # (륪; 륪; 륪; 륪; 륪; ) HANGUL SYLLABLE RYUBS
+B96B;B96B;1105 1172 11BA;B96B;1105 1172 11BA; # (륫; 륫; 륫; 륫; 륫; ) HANGUL SYLLABLE RYUS
+B96C;B96C;1105 1172 11BB;B96C;1105 1172 11BB; # (륬; 륬; 륬; 륬; 륬; ) HANGUL SYLLABLE RYUSS
+B96D;B96D;1105 1172 11BC;B96D;1105 1172 11BC; # (륭; 륭; 륭; 륭; 륭; ) HANGUL SYLLABLE RYUNG
+B96E;B96E;1105 1172 11BD;B96E;1105 1172 11BD; # (륮; 륮; 륮; 륮; 륮; ) HANGUL SYLLABLE RYUJ
+B96F;B96F;1105 1172 11BE;B96F;1105 1172 11BE; # (륯; 륯; 륯; 륯; 륯; ) HANGUL SYLLABLE RYUC
+B970;B970;1105 1172 11BF;B970;1105 1172 11BF; # (륰; 륰; 륰; 륰; 륰; ) HANGUL SYLLABLE RYUK
+B971;B971;1105 1172 11C0;B971;1105 1172 11C0; # (륱; 륱; 륱; 륱; 륱; ) HANGUL SYLLABLE RYUT
+B972;B972;1105 1172 11C1;B972;1105 1172 11C1; # (륲; 륲; á„…á…²á‡; 륲; á„…á…²á‡; ) HANGUL SYLLABLE RYUP
+B973;B973;1105 1172 11C2;B973;1105 1172 11C2; # (륳; 륳; 륳; 륳; 륳; ) HANGUL SYLLABLE RYUH
+B974;B974;1105 1173;B974;1105 1173; # (르; 르; 르; 르; 르; ) HANGUL SYLLABLE REU
+B975;B975;1105 1173 11A8;B975;1105 1173 11A8; # (륵; 륵; 륵; 륵; 륵; ) HANGUL SYLLABLE REUG
+B976;B976;1105 1173 11A9;B976;1105 1173 11A9; # (륶; 륶; 륶; 륶; 륶; ) HANGUL SYLLABLE REUGG
+B977;B977;1105 1173 11AA;B977;1105 1173 11AA; # (륷; 륷; 륷; 륷; 륷; ) HANGUL SYLLABLE REUGS
+B978;B978;1105 1173 11AB;B978;1105 1173 11AB; # (른; 른; 른; 른; 른; ) HANGUL SYLLABLE REUN
+B979;B979;1105 1173 11AC;B979;1105 1173 11AC; # (륹; 륹; 륹; 륹; 륹; ) HANGUL SYLLABLE REUNJ
+B97A;B97A;1105 1173 11AD;B97A;1105 1173 11AD; # (륺; 륺; 륺; 륺; 륺; ) HANGUL SYLLABLE REUNH
+B97B;B97B;1105 1173 11AE;B97B;1105 1173 11AE; # (륻; 륻; 륻; 륻; 륻; ) HANGUL SYLLABLE REUD
+B97C;B97C;1105 1173 11AF;B97C;1105 1173 11AF; # (를; 를; 를; 를; 를; ) HANGUL SYLLABLE REUL
+B97D;B97D;1105 1173 11B0;B97D;1105 1173 11B0; # (륽; 륽; 륽; 륽; 륽; ) HANGUL SYLLABLE REULG
+B97E;B97E;1105 1173 11B1;B97E;1105 1173 11B1; # (륾; 륾; 륾; 륾; 륾; ) HANGUL SYLLABLE REULM
+B97F;B97F;1105 1173 11B2;B97F;1105 1173 11B2; # (륿; 륿; 륿; 륿; 륿; ) HANGUL SYLLABLE REULB
+B980;B980;1105 1173 11B3;B980;1105 1173 11B3; # (릀; 릀; 릀; 릀; 릀; ) HANGUL SYLLABLE REULS
+B981;B981;1105 1173 11B4;B981;1105 1173 11B4; # (ë¦; ë¦; 릁; ë¦; 릁; ) HANGUL SYLLABLE REULT
+B982;B982;1105 1173 11B5;B982;1105 1173 11B5; # (릂; 릂; 릂; 릂; 릂; ) HANGUL SYLLABLE REULP
+B983;B983;1105 1173 11B6;B983;1105 1173 11B6; # (릃; 릃; 릃; 릃; 릃; ) HANGUL SYLLABLE REULH
+B984;B984;1105 1173 11B7;B984;1105 1173 11B7; # (름; 름; 름; 름; 름; ) HANGUL SYLLABLE REUM
+B985;B985;1105 1173 11B8;B985;1105 1173 11B8; # (릅; 릅; 릅; 릅; 릅; ) HANGUL SYLLABLE REUB
+B986;B986;1105 1173 11B9;B986;1105 1173 11B9; # (릆; 릆; 릆; 릆; 릆; ) HANGUL SYLLABLE REUBS
+B987;B987;1105 1173 11BA;B987;1105 1173 11BA; # (릇; 릇; 릇; 릇; 릇; ) HANGUL SYLLABLE REUS
+B988;B988;1105 1173 11BB;B988;1105 1173 11BB; # (릈; 릈; 릈; 릈; 릈; ) HANGUL SYLLABLE REUSS
+B989;B989;1105 1173 11BC;B989;1105 1173 11BC; # (릉; 릉; 릉; 릉; 릉; ) HANGUL SYLLABLE REUNG
+B98A;B98A;1105 1173 11BD;B98A;1105 1173 11BD; # (릊; 릊; 릊; 릊; 릊; ) HANGUL SYLLABLE REUJ
+B98B;B98B;1105 1173 11BE;B98B;1105 1173 11BE; # (릋; 릋; 릋; 릋; 릋; ) HANGUL SYLLABLE REUC
+B98C;B98C;1105 1173 11BF;B98C;1105 1173 11BF; # (릌; 릌; 릌; 릌; 릌; ) HANGUL SYLLABLE REUK
+B98D;B98D;1105 1173 11C0;B98D;1105 1173 11C0; # (ë¦; ë¦; 릍; ë¦; 릍; ) HANGUL SYLLABLE REUT
+B98E;B98E;1105 1173 11C1;B98E;1105 1173 11C1; # (릎; 릎; á„…á…³á‡; 릎; á„…á…³á‡; ) HANGUL SYLLABLE REUP
+B98F;B98F;1105 1173 11C2;B98F;1105 1173 11C2; # (ë¦; ë¦; 릏; ë¦; 릏; ) HANGUL SYLLABLE REUH
+B990;B990;1105 1174;B990;1105 1174; # (ë¦; ë¦; á„…á…´; ë¦; á„…á…´; ) HANGUL SYLLABLE RYI
+B991;B991;1105 1174 11A8;B991;1105 1174 11A8; # (릑; 릑; 릑; 릑; 릑; ) HANGUL SYLLABLE RYIG
+B992;B992;1105 1174 11A9;B992;1105 1174 11A9; # (릒; 릒; 릒; 릒; 릒; ) HANGUL SYLLABLE RYIGG
+B993;B993;1105 1174 11AA;B993;1105 1174 11AA; # (릓; 릓; 릓; 릓; 릓; ) HANGUL SYLLABLE RYIGS
+B994;B994;1105 1174 11AB;B994;1105 1174 11AB; # (릔; 릔; 릔; 릔; 릔; ) HANGUL SYLLABLE RYIN
+B995;B995;1105 1174 11AC;B995;1105 1174 11AC; # (릕; 릕; 릕; 릕; 릕; ) HANGUL SYLLABLE RYINJ
+B996;B996;1105 1174 11AD;B996;1105 1174 11AD; # (릖; 릖; 릖; 릖; 릖; ) HANGUL SYLLABLE RYINH
+B997;B997;1105 1174 11AE;B997;1105 1174 11AE; # (릗; 릗; 릗; 릗; 릗; ) HANGUL SYLLABLE RYID
+B998;B998;1105 1174 11AF;B998;1105 1174 11AF; # (릘; 릘; 릘; 릘; 릘; ) HANGUL SYLLABLE RYIL
+B999;B999;1105 1174 11B0;B999;1105 1174 11B0; # (릙; 릙; 릙; 릙; 릙; ) HANGUL SYLLABLE RYILG
+B99A;B99A;1105 1174 11B1;B99A;1105 1174 11B1; # (릚; 릚; 릚; 릚; 릚; ) HANGUL SYLLABLE RYILM
+B99B;B99B;1105 1174 11B2;B99B;1105 1174 11B2; # (릛; 릛; 릛; 릛; 릛; ) HANGUL SYLLABLE RYILB
+B99C;B99C;1105 1174 11B3;B99C;1105 1174 11B3; # (릜; 릜; 릜; 릜; 릜; ) HANGUL SYLLABLE RYILS
+B99D;B99D;1105 1174 11B4;B99D;1105 1174 11B4; # (ë¦; ë¦; 릝; ë¦; 릝; ) HANGUL SYLLABLE RYILT
+B99E;B99E;1105 1174 11B5;B99E;1105 1174 11B5; # (릞; 릞; 릞; 릞; 릞; ) HANGUL SYLLABLE RYILP
+B99F;B99F;1105 1174 11B6;B99F;1105 1174 11B6; # (릟; 릟; 릟; 릟; 릟; ) HANGUL SYLLABLE RYILH
+B9A0;B9A0;1105 1174 11B7;B9A0;1105 1174 11B7; # (릠; 릠; 릠; 릠; 릠; ) HANGUL SYLLABLE RYIM
+B9A1;B9A1;1105 1174 11B8;B9A1;1105 1174 11B8; # (릡; 릡; 릡; 릡; 릡; ) HANGUL SYLLABLE RYIB
+B9A2;B9A2;1105 1174 11B9;B9A2;1105 1174 11B9; # (릢; 릢; 릢; 릢; 릢; ) HANGUL SYLLABLE RYIBS
+B9A3;B9A3;1105 1174 11BA;B9A3;1105 1174 11BA; # (릣; 릣; 릣; 릣; 릣; ) HANGUL SYLLABLE RYIS
+B9A4;B9A4;1105 1174 11BB;B9A4;1105 1174 11BB; # (릤; 릤; 릤; 릤; 릤; ) HANGUL SYLLABLE RYISS
+B9A5;B9A5;1105 1174 11BC;B9A5;1105 1174 11BC; # (릥; 릥; 릥; 릥; 릥; ) HANGUL SYLLABLE RYING
+B9A6;B9A6;1105 1174 11BD;B9A6;1105 1174 11BD; # (릦; 릦; 릦; 릦; 릦; ) HANGUL SYLLABLE RYIJ
+B9A7;B9A7;1105 1174 11BE;B9A7;1105 1174 11BE; # (릧; 릧; 릧; 릧; 릧; ) HANGUL SYLLABLE RYIC
+B9A8;B9A8;1105 1174 11BF;B9A8;1105 1174 11BF; # (릨; 릨; 릨; 릨; 릨; ) HANGUL SYLLABLE RYIK
+B9A9;B9A9;1105 1174 11C0;B9A9;1105 1174 11C0; # (릩; 릩; 릩; 릩; 릩; ) HANGUL SYLLABLE RYIT
+B9AA;B9AA;1105 1174 11C1;B9AA;1105 1174 11C1; # (릪; 릪; á„…á…´á‡; 릪; á„…á…´á‡; ) HANGUL SYLLABLE RYIP
+B9AB;B9AB;1105 1174 11C2;B9AB;1105 1174 11C2; # (릫; 릫; 릫; 릫; 릫; ) HANGUL SYLLABLE RYIH
+B9AC;B9AC;1105 1175;B9AC;1105 1175; # (리; 리; 리; 리; 리; ) HANGUL SYLLABLE RI
+B9AD;B9AD;1105 1175 11A8;B9AD;1105 1175 11A8; # (릭; 릭; 릭; 릭; 릭; ) HANGUL SYLLABLE RIG
+B9AE;B9AE;1105 1175 11A9;B9AE;1105 1175 11A9; # (릮; 릮; 릮; 릮; 릮; ) HANGUL SYLLABLE RIGG
+B9AF;B9AF;1105 1175 11AA;B9AF;1105 1175 11AA; # (릯; 릯; 릯; 릯; 릯; ) HANGUL SYLLABLE RIGS
+B9B0;B9B0;1105 1175 11AB;B9B0;1105 1175 11AB; # (린; 린; 린; 린; 린; ) HANGUL SYLLABLE RIN
+B9B1;B9B1;1105 1175 11AC;B9B1;1105 1175 11AC; # (릱; 릱; 릱; 릱; 릱; ) HANGUL SYLLABLE RINJ
+B9B2;B9B2;1105 1175 11AD;B9B2;1105 1175 11AD; # (릲; 릲; 릲; 릲; 릲; ) HANGUL SYLLABLE RINH
+B9B3;B9B3;1105 1175 11AE;B9B3;1105 1175 11AE; # (릳; 릳; 릳; 릳; 릳; ) HANGUL SYLLABLE RID
+B9B4;B9B4;1105 1175 11AF;B9B4;1105 1175 11AF; # (릴; 릴; 릴; 릴; 릴; ) HANGUL SYLLABLE RIL
+B9B5;B9B5;1105 1175 11B0;B9B5;1105 1175 11B0; # (릵; 릵; 릵; 릵; 릵; ) HANGUL SYLLABLE RILG
+B9B6;B9B6;1105 1175 11B1;B9B6;1105 1175 11B1; # (릶; 릶; 릶; 릶; 릶; ) HANGUL SYLLABLE RILM
+B9B7;B9B7;1105 1175 11B2;B9B7;1105 1175 11B2; # (릷; 릷; 릷; 릷; 릷; ) HANGUL SYLLABLE RILB
+B9B8;B9B8;1105 1175 11B3;B9B8;1105 1175 11B3; # (릸; 릸; 릸; 릸; 릸; ) HANGUL SYLLABLE RILS
+B9B9;B9B9;1105 1175 11B4;B9B9;1105 1175 11B4; # (릹; 릹; 릹; 릹; 릹; ) HANGUL SYLLABLE RILT
+B9BA;B9BA;1105 1175 11B5;B9BA;1105 1175 11B5; # (릺; 릺; 릺; 릺; 릺; ) HANGUL SYLLABLE RILP
+B9BB;B9BB;1105 1175 11B6;B9BB;1105 1175 11B6; # (릻; 릻; 릻; 릻; 릻; ) HANGUL SYLLABLE RILH
+B9BC;B9BC;1105 1175 11B7;B9BC;1105 1175 11B7; # (림; 림; 림; 림; 림; ) HANGUL SYLLABLE RIM
+B9BD;B9BD;1105 1175 11B8;B9BD;1105 1175 11B8; # (립; 립; 립; 립; 립; ) HANGUL SYLLABLE RIB
+B9BE;B9BE;1105 1175 11B9;B9BE;1105 1175 11B9; # (릾; 릾; 릾; 릾; 릾; ) HANGUL SYLLABLE RIBS
+B9BF;B9BF;1105 1175 11BA;B9BF;1105 1175 11BA; # (릿; 릿; 릿; 릿; 릿; ) HANGUL SYLLABLE RIS
+B9C0;B9C0;1105 1175 11BB;B9C0;1105 1175 11BB; # (맀; 맀; 맀; 맀; 맀; ) HANGUL SYLLABLE RISS
+B9C1;B9C1;1105 1175 11BC;B9C1;1105 1175 11BC; # (ë§; ë§; 링; ë§; 링; ) HANGUL SYLLABLE RING
+B9C2;B9C2;1105 1175 11BD;B9C2;1105 1175 11BD; # (맂; 맂; 맂; 맂; 맂; ) HANGUL SYLLABLE RIJ
+B9C3;B9C3;1105 1175 11BE;B9C3;1105 1175 11BE; # (맃; 맃; 맃; 맃; 맃; ) HANGUL SYLLABLE RIC
+B9C4;B9C4;1105 1175 11BF;B9C4;1105 1175 11BF; # (맄; 맄; 맄; 맄; 맄; ) HANGUL SYLLABLE RIK
+B9C5;B9C5;1105 1175 11C0;B9C5;1105 1175 11C0; # (맅; 맅; 맅; 맅; 맅; ) HANGUL SYLLABLE RIT
+B9C6;B9C6;1105 1175 11C1;B9C6;1105 1175 11C1; # (맆; 맆; á„…á…µá‡; 맆; á„…á…µá‡; ) HANGUL SYLLABLE RIP
+B9C7;B9C7;1105 1175 11C2;B9C7;1105 1175 11C2; # (맇; 맇; 맇; 맇; 맇; ) HANGUL SYLLABLE RIH
+B9C8;B9C8;1106 1161;B9C8;1106 1161; # (마; 마; 마; 마; 마; ) HANGUL SYLLABLE MA
+B9C9;B9C9;1106 1161 11A8;B9C9;1106 1161 11A8; # (막; 막; 막; 막; 막; ) HANGUL SYLLABLE MAG
+B9CA;B9CA;1106 1161 11A9;B9CA;1106 1161 11A9; # (맊; 맊; 맊; 맊; 맊; ) HANGUL SYLLABLE MAGG
+B9CB;B9CB;1106 1161 11AA;B9CB;1106 1161 11AA; # (맋; 맋; 맋; 맋; 맋; ) HANGUL SYLLABLE MAGS
+B9CC;B9CC;1106 1161 11AB;B9CC;1106 1161 11AB; # (만; 만; 만; 만; 만; ) HANGUL SYLLABLE MAN
+B9CD;B9CD;1106 1161 11AC;B9CD;1106 1161 11AC; # (ë§; ë§; 맍; ë§; 맍; ) HANGUL SYLLABLE MANJ
+B9CE;B9CE;1106 1161 11AD;B9CE;1106 1161 11AD; # (많; 많; 많; 많; 많; ) HANGUL SYLLABLE MANH
+B9CF;B9CF;1106 1161 11AE;B9CF;1106 1161 11AE; # (ë§; ë§; 맏; ë§; 맏; ) HANGUL SYLLABLE MAD
+B9D0;B9D0;1106 1161 11AF;B9D0;1106 1161 11AF; # (ë§; ë§; 말; ë§; 말; ) HANGUL SYLLABLE MAL
+B9D1;B9D1;1106 1161 11B0;B9D1;1106 1161 11B0; # (맑; 맑; 맑; 맑; 맑; ) HANGUL SYLLABLE MALG
+B9D2;B9D2;1106 1161 11B1;B9D2;1106 1161 11B1; # (맒; 맒; 맒; 맒; 맒; ) HANGUL SYLLABLE MALM
+B9D3;B9D3;1106 1161 11B2;B9D3;1106 1161 11B2; # (맓; 맓; 맓; 맓; 맓; ) HANGUL SYLLABLE MALB
+B9D4;B9D4;1106 1161 11B3;B9D4;1106 1161 11B3; # (맔; 맔; 맔; 맔; 맔; ) HANGUL SYLLABLE MALS
+B9D5;B9D5;1106 1161 11B4;B9D5;1106 1161 11B4; # (맕; 맕; 맕; 맕; 맕; ) HANGUL SYLLABLE MALT
+B9D6;B9D6;1106 1161 11B5;B9D6;1106 1161 11B5; # (맖; 맖; 맖; 맖; 맖; ) HANGUL SYLLABLE MALP
+B9D7;B9D7;1106 1161 11B6;B9D7;1106 1161 11B6; # (맗; 맗; 맗; 맗; 맗; ) HANGUL SYLLABLE MALH
+B9D8;B9D8;1106 1161 11B7;B9D8;1106 1161 11B7; # (맘; 맘; 맘; 맘; 맘; ) HANGUL SYLLABLE MAM
+B9D9;B9D9;1106 1161 11B8;B9D9;1106 1161 11B8; # (맙; 맙; 맙; 맙; 맙; ) HANGUL SYLLABLE MAB
+B9DA;B9DA;1106 1161 11B9;B9DA;1106 1161 11B9; # (맚; 맚; 맚; 맚; 맚; ) HANGUL SYLLABLE MABS
+B9DB;B9DB;1106 1161 11BA;B9DB;1106 1161 11BA; # (맛; 맛; 맛; 맛; 맛; ) HANGUL SYLLABLE MAS
+B9DC;B9DC;1106 1161 11BB;B9DC;1106 1161 11BB; # (맜; 맜; 맜; 맜; 맜; ) HANGUL SYLLABLE MASS
+B9DD;B9DD;1106 1161 11BC;B9DD;1106 1161 11BC; # (ë§; ë§; 망; ë§; 망; ) HANGUL SYLLABLE MANG
+B9DE;B9DE;1106 1161 11BD;B9DE;1106 1161 11BD; # (맞; 맞; 맞; 맞; 맞; ) HANGUL SYLLABLE MAJ
+B9DF;B9DF;1106 1161 11BE;B9DF;1106 1161 11BE; # (맟; 맟; 맟; 맟; 맟; ) HANGUL SYLLABLE MAC
+B9E0;B9E0;1106 1161 11BF;B9E0;1106 1161 11BF; # (맠; 맠; 맠; 맠; 맠; ) HANGUL SYLLABLE MAK
+B9E1;B9E1;1106 1161 11C0;B9E1;1106 1161 11C0; # (맡; 맡; 맡; 맡; 맡; ) HANGUL SYLLABLE MAT
+B9E2;B9E2;1106 1161 11C1;B9E2;1106 1161 11C1; # (ë§¢; ë§¢; 마á‡; ë§¢; 마á‡; ) HANGUL SYLLABLE MAP
+B9E3;B9E3;1106 1161 11C2;B9E3;1106 1161 11C2; # (맣; 맣; 맣; 맣; 맣; ) HANGUL SYLLABLE MAH
+B9E4;B9E4;1106 1162;B9E4;1106 1162; # (매; 매; 매; 매; 매; ) HANGUL SYLLABLE MAE
+B9E5;B9E5;1106 1162 11A8;B9E5;1106 1162 11A8; # (맥; 맥; 맥; 맥; 맥; ) HANGUL SYLLABLE MAEG
+B9E6;B9E6;1106 1162 11A9;B9E6;1106 1162 11A9; # (맦; 맦; 맦; 맦; 맦; ) HANGUL SYLLABLE MAEGG
+B9E7;B9E7;1106 1162 11AA;B9E7;1106 1162 11AA; # (맧; 맧; 맧; 맧; 맧; ) HANGUL SYLLABLE MAEGS
+B9E8;B9E8;1106 1162 11AB;B9E8;1106 1162 11AB; # (맨; 맨; 맨; 맨; 맨; ) HANGUL SYLLABLE MAEN
+B9E9;B9E9;1106 1162 11AC;B9E9;1106 1162 11AC; # (맩; 맩; 맩; 맩; 맩; ) HANGUL SYLLABLE MAENJ
+B9EA;B9EA;1106 1162 11AD;B9EA;1106 1162 11AD; # (맪; 맪; 맪; 맪; 맪; ) HANGUL SYLLABLE MAENH
+B9EB;B9EB;1106 1162 11AE;B9EB;1106 1162 11AE; # (맫; 맫; 맫; 맫; 맫; ) HANGUL SYLLABLE MAED
+B9EC;B9EC;1106 1162 11AF;B9EC;1106 1162 11AF; # (맬; 맬; 맬; 맬; 맬; ) HANGUL SYLLABLE MAEL
+B9ED;B9ED;1106 1162 11B0;B9ED;1106 1162 11B0; # (맭; 맭; 맭; 맭; 맭; ) HANGUL SYLLABLE MAELG
+B9EE;B9EE;1106 1162 11B1;B9EE;1106 1162 11B1; # (맮; 맮; 맮; 맮; 맮; ) HANGUL SYLLABLE MAELM
+B9EF;B9EF;1106 1162 11B2;B9EF;1106 1162 11B2; # (맯; 맯; 맯; 맯; 맯; ) HANGUL SYLLABLE MAELB
+B9F0;B9F0;1106 1162 11B3;B9F0;1106 1162 11B3; # (맰; 맰; 맰; 맰; 맰; ) HANGUL SYLLABLE MAELS
+B9F1;B9F1;1106 1162 11B4;B9F1;1106 1162 11B4; # (맱; 맱; 맱; 맱; 맱; ) HANGUL SYLLABLE MAELT
+B9F2;B9F2;1106 1162 11B5;B9F2;1106 1162 11B5; # (맲; 맲; 맲; 맲; 맲; ) HANGUL SYLLABLE MAELP
+B9F3;B9F3;1106 1162 11B6;B9F3;1106 1162 11B6; # (맳; 맳; 맳; 맳; 맳; ) HANGUL SYLLABLE MAELH
+B9F4;B9F4;1106 1162 11B7;B9F4;1106 1162 11B7; # (맴; 맴; 맴; 맴; 맴; ) HANGUL SYLLABLE MAEM
+B9F5;B9F5;1106 1162 11B8;B9F5;1106 1162 11B8; # (맵; 맵; 맵; 맵; 맵; ) HANGUL SYLLABLE MAEB
+B9F6;B9F6;1106 1162 11B9;B9F6;1106 1162 11B9; # (맶; 맶; 맶; 맶; 맶; ) HANGUL SYLLABLE MAEBS
+B9F7;B9F7;1106 1162 11BA;B9F7;1106 1162 11BA; # (맷; 맷; 맷; 맷; 맷; ) HANGUL SYLLABLE MAES
+B9F8;B9F8;1106 1162 11BB;B9F8;1106 1162 11BB; # (맸; 맸; 맸; 맸; 맸; ) HANGUL SYLLABLE MAESS
+B9F9;B9F9;1106 1162 11BC;B9F9;1106 1162 11BC; # (맹; 맹; 맹; 맹; 맹; ) HANGUL SYLLABLE MAENG
+B9FA;B9FA;1106 1162 11BD;B9FA;1106 1162 11BD; # (맺; 맺; 맺; 맺; 맺; ) HANGUL SYLLABLE MAEJ
+B9FB;B9FB;1106 1162 11BE;B9FB;1106 1162 11BE; # (맻; 맻; 맻; 맻; 맻; ) HANGUL SYLLABLE MAEC
+B9FC;B9FC;1106 1162 11BF;B9FC;1106 1162 11BF; # (맼; 맼; 맼; 맼; 맼; ) HANGUL SYLLABLE MAEK
+B9FD;B9FD;1106 1162 11C0;B9FD;1106 1162 11C0; # (맽; 맽; 맽; 맽; 맽; ) HANGUL SYLLABLE MAET
+B9FE;B9FE;1106 1162 11C1;B9FE;1106 1162 11C1; # (ë§¾; ë§¾; 매á‡; ë§¾; 매á‡; ) HANGUL SYLLABLE MAEP
+B9FF;B9FF;1106 1162 11C2;B9FF;1106 1162 11C2; # (맿; 맿; 맿; 맿; 맿; ) HANGUL SYLLABLE MAEH
+BA00;BA00;1106 1163;BA00;1106 1163; # (먀; 먀; 먀; 먀; 먀; ) HANGUL SYLLABLE MYA
+BA01;BA01;1106 1163 11A8;BA01;1106 1163 11A8; # (ë¨; ë¨; 먁; ë¨; 먁; ) HANGUL SYLLABLE MYAG
+BA02;BA02;1106 1163 11A9;BA02;1106 1163 11A9; # (먂; 먂; 먂; 먂; 먂; ) HANGUL SYLLABLE MYAGG
+BA03;BA03;1106 1163 11AA;BA03;1106 1163 11AA; # (먃; 먃; 먃; 먃; 먃; ) HANGUL SYLLABLE MYAGS
+BA04;BA04;1106 1163 11AB;BA04;1106 1163 11AB; # (먄; 먄; 먄; 먄; 먄; ) HANGUL SYLLABLE MYAN
+BA05;BA05;1106 1163 11AC;BA05;1106 1163 11AC; # (먅; 먅; 먅; 먅; 먅; ) HANGUL SYLLABLE MYANJ
+BA06;BA06;1106 1163 11AD;BA06;1106 1163 11AD; # (먆; 먆; 먆; 먆; 먆; ) HANGUL SYLLABLE MYANH
+BA07;BA07;1106 1163 11AE;BA07;1106 1163 11AE; # (먇; 먇; 먇; 먇; 먇; ) HANGUL SYLLABLE MYAD
+BA08;BA08;1106 1163 11AF;BA08;1106 1163 11AF; # (먈; 먈; 먈; 먈; 먈; ) HANGUL SYLLABLE MYAL
+BA09;BA09;1106 1163 11B0;BA09;1106 1163 11B0; # (먉; 먉; 먉; 먉; 먉; ) HANGUL SYLLABLE MYALG
+BA0A;BA0A;1106 1163 11B1;BA0A;1106 1163 11B1; # (먊; 먊; 먊; 먊; 먊; ) HANGUL SYLLABLE MYALM
+BA0B;BA0B;1106 1163 11B2;BA0B;1106 1163 11B2; # (먋; 먋; 먋; 먋; 먋; ) HANGUL SYLLABLE MYALB
+BA0C;BA0C;1106 1163 11B3;BA0C;1106 1163 11B3; # (먌; 먌; 먌; 먌; 먌; ) HANGUL SYLLABLE MYALS
+BA0D;BA0D;1106 1163 11B4;BA0D;1106 1163 11B4; # (ë¨; ë¨; 먍; ë¨; 먍; ) HANGUL SYLLABLE MYALT
+BA0E;BA0E;1106 1163 11B5;BA0E;1106 1163 11B5; # (먎; 먎; 먎; 먎; 먎; ) HANGUL SYLLABLE MYALP
+BA0F;BA0F;1106 1163 11B6;BA0F;1106 1163 11B6; # (ë¨; ë¨; 먏; ë¨; 먏; ) HANGUL SYLLABLE MYALH
+BA10;BA10;1106 1163 11B7;BA10;1106 1163 11B7; # (ë¨; ë¨; 먐; ë¨; 먐; ) HANGUL SYLLABLE MYAM
+BA11;BA11;1106 1163 11B8;BA11;1106 1163 11B8; # (먑; 먑; 먑; 먑; 먑; ) HANGUL SYLLABLE MYAB
+BA12;BA12;1106 1163 11B9;BA12;1106 1163 11B9; # (먒; 먒; 먒; 먒; 먒; ) HANGUL SYLLABLE MYABS
+BA13;BA13;1106 1163 11BA;BA13;1106 1163 11BA; # (먓; 먓; 먓; 먓; 먓; ) HANGUL SYLLABLE MYAS
+BA14;BA14;1106 1163 11BB;BA14;1106 1163 11BB; # (먔; 먔; 먔; 먔; 먔; ) HANGUL SYLLABLE MYASS
+BA15;BA15;1106 1163 11BC;BA15;1106 1163 11BC; # (먕; 먕; 먕; 먕; 먕; ) HANGUL SYLLABLE MYANG
+BA16;BA16;1106 1163 11BD;BA16;1106 1163 11BD; # (먖; 먖; 먖; 먖; 먖; ) HANGUL SYLLABLE MYAJ
+BA17;BA17;1106 1163 11BE;BA17;1106 1163 11BE; # (먗; 먗; 먗; 먗; 먗; ) HANGUL SYLLABLE MYAC
+BA18;BA18;1106 1163 11BF;BA18;1106 1163 11BF; # (먘; 먘; 먘; 먘; 먘; ) HANGUL SYLLABLE MYAK
+BA19;BA19;1106 1163 11C0;BA19;1106 1163 11C0; # (먙; 먙; 먙; 먙; 먙; ) HANGUL SYLLABLE MYAT
+BA1A;BA1A;1106 1163 11C1;BA1A;1106 1163 11C1; # (먚; 먚; 먀á‡; 먚; 먀á‡; ) HANGUL SYLLABLE MYAP
+BA1B;BA1B;1106 1163 11C2;BA1B;1106 1163 11C2; # (먛; 먛; 먛; 먛; 먛; ) HANGUL SYLLABLE MYAH
+BA1C;BA1C;1106 1164;BA1C;1106 1164; # (먜; 먜; 먜; 먜; 먜; ) HANGUL SYLLABLE MYAE
+BA1D;BA1D;1106 1164 11A8;BA1D;1106 1164 11A8; # (ë¨; ë¨; 먝; ë¨; 먝; ) HANGUL SYLLABLE MYAEG
+BA1E;BA1E;1106 1164 11A9;BA1E;1106 1164 11A9; # (먞; 먞; 먞; 먞; 먞; ) HANGUL SYLLABLE MYAEGG
+BA1F;BA1F;1106 1164 11AA;BA1F;1106 1164 11AA; # (먟; 먟; 먟; 먟; 먟; ) HANGUL SYLLABLE MYAEGS
+BA20;BA20;1106 1164 11AB;BA20;1106 1164 11AB; # (먠; 먠; 먠; 먠; 먠; ) HANGUL SYLLABLE MYAEN
+BA21;BA21;1106 1164 11AC;BA21;1106 1164 11AC; # (먡; 먡; 먡; 먡; 먡; ) HANGUL SYLLABLE MYAENJ
+BA22;BA22;1106 1164 11AD;BA22;1106 1164 11AD; # (먢; 먢; 먢; 먢; 먢; ) HANGUL SYLLABLE MYAENH
+BA23;BA23;1106 1164 11AE;BA23;1106 1164 11AE; # (먣; 먣; 먣; 먣; 먣; ) HANGUL SYLLABLE MYAED
+BA24;BA24;1106 1164 11AF;BA24;1106 1164 11AF; # (먤; 먤; 먤; 먤; 먤; ) HANGUL SYLLABLE MYAEL
+BA25;BA25;1106 1164 11B0;BA25;1106 1164 11B0; # (먥; 먥; 먥; 먥; 먥; ) HANGUL SYLLABLE MYAELG
+BA26;BA26;1106 1164 11B1;BA26;1106 1164 11B1; # (먦; 먦; 먦; 먦; 먦; ) HANGUL SYLLABLE MYAELM
+BA27;BA27;1106 1164 11B2;BA27;1106 1164 11B2; # (먧; 먧; 먧; 먧; 먧; ) HANGUL SYLLABLE MYAELB
+BA28;BA28;1106 1164 11B3;BA28;1106 1164 11B3; # (먨; 먨; 먨; 먨; 먨; ) HANGUL SYLLABLE MYAELS
+BA29;BA29;1106 1164 11B4;BA29;1106 1164 11B4; # (먩; 먩; 먩; 먩; 먩; ) HANGUL SYLLABLE MYAELT
+BA2A;BA2A;1106 1164 11B5;BA2A;1106 1164 11B5; # (먪; 먪; 먪; 먪; 먪; ) HANGUL SYLLABLE MYAELP
+BA2B;BA2B;1106 1164 11B6;BA2B;1106 1164 11B6; # (먫; 먫; 먫; 먫; 먫; ) HANGUL SYLLABLE MYAELH
+BA2C;BA2C;1106 1164 11B7;BA2C;1106 1164 11B7; # (먬; 먬; 먬; 먬; 먬; ) HANGUL SYLLABLE MYAEM
+BA2D;BA2D;1106 1164 11B8;BA2D;1106 1164 11B8; # (먭; 먭; 먭; 먭; 먭; ) HANGUL SYLLABLE MYAEB
+BA2E;BA2E;1106 1164 11B9;BA2E;1106 1164 11B9; # (먮; 먮; 먮; 먮; 먮; ) HANGUL SYLLABLE MYAEBS
+BA2F;BA2F;1106 1164 11BA;BA2F;1106 1164 11BA; # (먯; 먯; 먯; 먯; 먯; ) HANGUL SYLLABLE MYAES
+BA30;BA30;1106 1164 11BB;BA30;1106 1164 11BB; # (먰; 먰; 먰; 먰; 먰; ) HANGUL SYLLABLE MYAESS
+BA31;BA31;1106 1164 11BC;BA31;1106 1164 11BC; # (먱; 먱; 먱; 먱; 먱; ) HANGUL SYLLABLE MYAENG
+BA32;BA32;1106 1164 11BD;BA32;1106 1164 11BD; # (먲; 먲; 먲; 먲; 먲; ) HANGUL SYLLABLE MYAEJ
+BA33;BA33;1106 1164 11BE;BA33;1106 1164 11BE; # (먳; 먳; 먳; 먳; 먳; ) HANGUL SYLLABLE MYAEC
+BA34;BA34;1106 1164 11BF;BA34;1106 1164 11BF; # (먴; 먴; 먴; 먴; 먴; ) HANGUL SYLLABLE MYAEK
+BA35;BA35;1106 1164 11C0;BA35;1106 1164 11C0; # (먵; 먵; 먵; 먵; 먵; ) HANGUL SYLLABLE MYAET
+BA36;BA36;1106 1164 11C1;BA36;1106 1164 11C1; # (먶; 먶; 먜á‡; 먶; 먜á‡; ) HANGUL SYLLABLE MYAEP
+BA37;BA37;1106 1164 11C2;BA37;1106 1164 11C2; # (먷; 먷; 먷; 먷; 먷; ) HANGUL SYLLABLE MYAEH
+BA38;BA38;1106 1165;BA38;1106 1165; # (머; 머; 머; 머; 머; ) HANGUL SYLLABLE MEO
+BA39;BA39;1106 1165 11A8;BA39;1106 1165 11A8; # (먹; 먹; 먹; 먹; 먹; ) HANGUL SYLLABLE MEOG
+BA3A;BA3A;1106 1165 11A9;BA3A;1106 1165 11A9; # (먺; 먺; 먺; 먺; 먺; ) HANGUL SYLLABLE MEOGG
+BA3B;BA3B;1106 1165 11AA;BA3B;1106 1165 11AA; # (먻; 먻; 먻; 먻; 먻; ) HANGUL SYLLABLE MEOGS
+BA3C;BA3C;1106 1165 11AB;BA3C;1106 1165 11AB; # (먼; 먼; 먼; 먼; 먼; ) HANGUL SYLLABLE MEON
+BA3D;BA3D;1106 1165 11AC;BA3D;1106 1165 11AC; # (먽; 먽; 먽; 먽; 먽; ) HANGUL SYLLABLE MEONJ
+BA3E;BA3E;1106 1165 11AD;BA3E;1106 1165 11AD; # (먾; 먾; 먾; 먾; 먾; ) HANGUL SYLLABLE MEONH
+BA3F;BA3F;1106 1165 11AE;BA3F;1106 1165 11AE; # (먿; 먿; 먿; 먿; 먿; ) HANGUL SYLLABLE MEOD
+BA40;BA40;1106 1165 11AF;BA40;1106 1165 11AF; # (멀; 멀; 멀; 멀; 멀; ) HANGUL SYLLABLE MEOL
+BA41;BA41;1106 1165 11B0;BA41;1106 1165 11B0; # (ë©; ë©; 멁; ë©; 멁; ) HANGUL SYLLABLE MEOLG
+BA42;BA42;1106 1165 11B1;BA42;1106 1165 11B1; # (멂; 멂; 멂; 멂; 멂; ) HANGUL SYLLABLE MEOLM
+BA43;BA43;1106 1165 11B2;BA43;1106 1165 11B2; # (멃; 멃; 멃; 멃; 멃; ) HANGUL SYLLABLE MEOLB
+BA44;BA44;1106 1165 11B3;BA44;1106 1165 11B3; # (멄; 멄; 멄; 멄; 멄; ) HANGUL SYLLABLE MEOLS
+BA45;BA45;1106 1165 11B4;BA45;1106 1165 11B4; # (멅; 멅; 멅; 멅; 멅; ) HANGUL SYLLABLE MEOLT
+BA46;BA46;1106 1165 11B5;BA46;1106 1165 11B5; # (멆; 멆; 멆; 멆; 멆; ) HANGUL SYLLABLE MEOLP
+BA47;BA47;1106 1165 11B6;BA47;1106 1165 11B6; # (멇; 멇; 멇; 멇; 멇; ) HANGUL SYLLABLE MEOLH
+BA48;BA48;1106 1165 11B7;BA48;1106 1165 11B7; # (멈; 멈; 멈; 멈; 멈; ) HANGUL SYLLABLE MEOM
+BA49;BA49;1106 1165 11B8;BA49;1106 1165 11B8; # (멉; 멉; 멉; 멉; 멉; ) HANGUL SYLLABLE MEOB
+BA4A;BA4A;1106 1165 11B9;BA4A;1106 1165 11B9; # (멊; 멊; 멊; 멊; 멊; ) HANGUL SYLLABLE MEOBS
+BA4B;BA4B;1106 1165 11BA;BA4B;1106 1165 11BA; # (멋; 멋; 멋; 멋; 멋; ) HANGUL SYLLABLE MEOS
+BA4C;BA4C;1106 1165 11BB;BA4C;1106 1165 11BB; # (멌; 멌; 멌; 멌; 멌; ) HANGUL SYLLABLE MEOSS
+BA4D;BA4D;1106 1165 11BC;BA4D;1106 1165 11BC; # (ë©; ë©; 멍; ë©; 멍; ) HANGUL SYLLABLE MEONG
+BA4E;BA4E;1106 1165 11BD;BA4E;1106 1165 11BD; # (멎; 멎; 멎; 멎; 멎; ) HANGUL SYLLABLE MEOJ
+BA4F;BA4F;1106 1165 11BE;BA4F;1106 1165 11BE; # (ë©; ë©; 멏; ë©; 멏; ) HANGUL SYLLABLE MEOC
+BA50;BA50;1106 1165 11BF;BA50;1106 1165 11BF; # (ë©; ë©; 멐; ë©; 멐; ) HANGUL SYLLABLE MEOK
+BA51;BA51;1106 1165 11C0;BA51;1106 1165 11C0; # (멑; 멑; 멑; 멑; 멑; ) HANGUL SYLLABLE MEOT
+BA52;BA52;1106 1165 11C1;BA52;1106 1165 11C1; # (ë©’; ë©’; 머á‡; ë©’; 머á‡; ) HANGUL SYLLABLE MEOP
+BA53;BA53;1106 1165 11C2;BA53;1106 1165 11C2; # (멓; 멓; 멓; 멓; 멓; ) HANGUL SYLLABLE MEOH
+BA54;BA54;1106 1166;BA54;1106 1166; # (메; 메; 메; 메; 메; ) HANGUL SYLLABLE ME
+BA55;BA55;1106 1166 11A8;BA55;1106 1166 11A8; # (멕; 멕; 멕; 멕; 멕; ) HANGUL SYLLABLE MEG
+BA56;BA56;1106 1166 11A9;BA56;1106 1166 11A9; # (멖; 멖; 멖; 멖; 멖; ) HANGUL SYLLABLE MEGG
+BA57;BA57;1106 1166 11AA;BA57;1106 1166 11AA; # (멗; 멗; 멗; 멗; 멗; ) HANGUL SYLLABLE MEGS
+BA58;BA58;1106 1166 11AB;BA58;1106 1166 11AB; # (멘; 멘; 멘; 멘; 멘; ) HANGUL SYLLABLE MEN
+BA59;BA59;1106 1166 11AC;BA59;1106 1166 11AC; # (멙; 멙; 멙; 멙; 멙; ) HANGUL SYLLABLE MENJ
+BA5A;BA5A;1106 1166 11AD;BA5A;1106 1166 11AD; # (멚; 멚; 멚; 멚; 멚; ) HANGUL SYLLABLE MENH
+BA5B;BA5B;1106 1166 11AE;BA5B;1106 1166 11AE; # (멛; 멛; 멛; 멛; 멛; ) HANGUL SYLLABLE MED
+BA5C;BA5C;1106 1166 11AF;BA5C;1106 1166 11AF; # (멜; 멜; 멜; 멜; 멜; ) HANGUL SYLLABLE MEL
+BA5D;BA5D;1106 1166 11B0;BA5D;1106 1166 11B0; # (ë©; ë©; 멝; ë©; 멝; ) HANGUL SYLLABLE MELG
+BA5E;BA5E;1106 1166 11B1;BA5E;1106 1166 11B1; # (멞; 멞; 멞; 멞; 멞; ) HANGUL SYLLABLE MELM
+BA5F;BA5F;1106 1166 11B2;BA5F;1106 1166 11B2; # (멟; 멟; 멟; 멟; 멟; ) HANGUL SYLLABLE MELB
+BA60;BA60;1106 1166 11B3;BA60;1106 1166 11B3; # (멠; 멠; 멠; 멠; 멠; ) HANGUL SYLLABLE MELS
+BA61;BA61;1106 1166 11B4;BA61;1106 1166 11B4; # (멡; 멡; 멡; 멡; 멡; ) HANGUL SYLLABLE MELT
+BA62;BA62;1106 1166 11B5;BA62;1106 1166 11B5; # (멢; 멢; 멢; 멢; 멢; ) HANGUL SYLLABLE MELP
+BA63;BA63;1106 1166 11B6;BA63;1106 1166 11B6; # (멣; 멣; 멣; 멣; 멣; ) HANGUL SYLLABLE MELH
+BA64;BA64;1106 1166 11B7;BA64;1106 1166 11B7; # (멤; 멤; 멤; 멤; 멤; ) HANGUL SYLLABLE MEM
+BA65;BA65;1106 1166 11B8;BA65;1106 1166 11B8; # (멥; 멥; 멥; 멥; 멥; ) HANGUL SYLLABLE MEB
+BA66;BA66;1106 1166 11B9;BA66;1106 1166 11B9; # (멦; 멦; 멦; 멦; 멦; ) HANGUL SYLLABLE MEBS
+BA67;BA67;1106 1166 11BA;BA67;1106 1166 11BA; # (멧; 멧; 멧; 멧; 멧; ) HANGUL SYLLABLE MES
+BA68;BA68;1106 1166 11BB;BA68;1106 1166 11BB; # (멨; 멨; 멨; 멨; 멨; ) HANGUL SYLLABLE MESS
+BA69;BA69;1106 1166 11BC;BA69;1106 1166 11BC; # (멩; 멩; 멩; 멩; 멩; ) HANGUL SYLLABLE MENG
+BA6A;BA6A;1106 1166 11BD;BA6A;1106 1166 11BD; # (멪; 멪; 멪; 멪; 멪; ) HANGUL SYLLABLE MEJ
+BA6B;BA6B;1106 1166 11BE;BA6B;1106 1166 11BE; # (멫; 멫; 멫; 멫; 멫; ) HANGUL SYLLABLE MEC
+BA6C;BA6C;1106 1166 11BF;BA6C;1106 1166 11BF; # (멬; 멬; 멬; 멬; 멬; ) HANGUL SYLLABLE MEK
+BA6D;BA6D;1106 1166 11C0;BA6D;1106 1166 11C0; # (멭; 멭; 멭; 멭; 멭; ) HANGUL SYLLABLE MET
+BA6E;BA6E;1106 1166 11C1;BA6E;1106 1166 11C1; # (ë©®; ë©®; 메á‡; ë©®; 메á‡; ) HANGUL SYLLABLE MEP
+BA6F;BA6F;1106 1166 11C2;BA6F;1106 1166 11C2; # (멯; 멯; 멯; 멯; 멯; ) HANGUL SYLLABLE MEH
+BA70;BA70;1106 1167;BA70;1106 1167; # (며; 며; 며; 며; 며; ) HANGUL SYLLABLE MYEO
+BA71;BA71;1106 1167 11A8;BA71;1106 1167 11A8; # (멱; 멱; 멱; 멱; 멱; ) HANGUL SYLLABLE MYEOG
+BA72;BA72;1106 1167 11A9;BA72;1106 1167 11A9; # (멲; 멲; 멲; 멲; 멲; ) HANGUL SYLLABLE MYEOGG
+BA73;BA73;1106 1167 11AA;BA73;1106 1167 11AA; # (멳; 멳; 멳; 멳; 멳; ) HANGUL SYLLABLE MYEOGS
+BA74;BA74;1106 1167 11AB;BA74;1106 1167 11AB; # (면; 면; 면; 면; 면; ) HANGUL SYLLABLE MYEON
+BA75;BA75;1106 1167 11AC;BA75;1106 1167 11AC; # (멵; 멵; 멵; 멵; 멵; ) HANGUL SYLLABLE MYEONJ
+BA76;BA76;1106 1167 11AD;BA76;1106 1167 11AD; # (멶; 멶; 멶; 멶; 멶; ) HANGUL SYLLABLE MYEONH
+BA77;BA77;1106 1167 11AE;BA77;1106 1167 11AE; # (멷; 멷; 멷; 멷; 멷; ) HANGUL SYLLABLE MYEOD
+BA78;BA78;1106 1167 11AF;BA78;1106 1167 11AF; # (멸; 멸; 멸; 멸; 멸; ) HANGUL SYLLABLE MYEOL
+BA79;BA79;1106 1167 11B0;BA79;1106 1167 11B0; # (멹; 멹; 멹; 멹; 멹; ) HANGUL SYLLABLE MYEOLG
+BA7A;BA7A;1106 1167 11B1;BA7A;1106 1167 11B1; # (멺; 멺; 멺; 멺; 멺; ) HANGUL SYLLABLE MYEOLM
+BA7B;BA7B;1106 1167 11B2;BA7B;1106 1167 11B2; # (멻; 멻; 멻; 멻; 멻; ) HANGUL SYLLABLE MYEOLB
+BA7C;BA7C;1106 1167 11B3;BA7C;1106 1167 11B3; # (멼; 멼; 멼; 멼; 멼; ) HANGUL SYLLABLE MYEOLS
+BA7D;BA7D;1106 1167 11B4;BA7D;1106 1167 11B4; # (멽; 멽; 멽; 멽; 멽; ) HANGUL SYLLABLE MYEOLT
+BA7E;BA7E;1106 1167 11B5;BA7E;1106 1167 11B5; # (멾; 멾; 멾; 멾; 멾; ) HANGUL SYLLABLE MYEOLP
+BA7F;BA7F;1106 1167 11B6;BA7F;1106 1167 11B6; # (멿; 멿; 멿; 멿; 멿; ) HANGUL SYLLABLE MYEOLH
+BA80;BA80;1106 1167 11B7;BA80;1106 1167 11B7; # (몀; 몀; 몀; 몀; 몀; ) HANGUL SYLLABLE MYEOM
+BA81;BA81;1106 1167 11B8;BA81;1106 1167 11B8; # (ëª; ëª; 몁; ëª; 몁; ) HANGUL SYLLABLE MYEOB
+BA82;BA82;1106 1167 11B9;BA82;1106 1167 11B9; # (몂; 몂; 몂; 몂; 몂; ) HANGUL SYLLABLE MYEOBS
+BA83;BA83;1106 1167 11BA;BA83;1106 1167 11BA; # (몃; 몃; 몃; 몃; 몃; ) HANGUL SYLLABLE MYEOS
+BA84;BA84;1106 1167 11BB;BA84;1106 1167 11BB; # (몄; 몄; 몄; 몄; 몄; ) HANGUL SYLLABLE MYEOSS
+BA85;BA85;1106 1167 11BC;BA85;1106 1167 11BC; # (명; 명; 명; 명; 명; ) HANGUL SYLLABLE MYEONG
+BA86;BA86;1106 1167 11BD;BA86;1106 1167 11BD; # (몆; 몆; 몆; 몆; 몆; ) HANGUL SYLLABLE MYEOJ
+BA87;BA87;1106 1167 11BE;BA87;1106 1167 11BE; # (몇; 몇; 몇; 몇; 몇; ) HANGUL SYLLABLE MYEOC
+BA88;BA88;1106 1167 11BF;BA88;1106 1167 11BF; # (몈; 몈; 몈; 몈; 몈; ) HANGUL SYLLABLE MYEOK
+BA89;BA89;1106 1167 11C0;BA89;1106 1167 11C0; # (몉; 몉; 몉; 몉; 몉; ) HANGUL SYLLABLE MYEOT
+BA8A;BA8A;1106 1167 11C1;BA8A;1106 1167 11C1; # (몊; 몊; 며á‡; 몊; 며á‡; ) HANGUL SYLLABLE MYEOP
+BA8B;BA8B;1106 1167 11C2;BA8B;1106 1167 11C2; # (몋; 몋; 몋; 몋; 몋; ) HANGUL SYLLABLE MYEOH
+BA8C;BA8C;1106 1168;BA8C;1106 1168; # (몌; 몌; 몌; 몌; 몌; ) HANGUL SYLLABLE MYE
+BA8D;BA8D;1106 1168 11A8;BA8D;1106 1168 11A8; # (ëª; ëª; 몍; ëª; 몍; ) HANGUL SYLLABLE MYEG
+BA8E;BA8E;1106 1168 11A9;BA8E;1106 1168 11A9; # (몎; 몎; 몎; 몎; 몎; ) HANGUL SYLLABLE MYEGG
+BA8F;BA8F;1106 1168 11AA;BA8F;1106 1168 11AA; # (ëª; ëª; 몏; ëª; 몏; ) HANGUL SYLLABLE MYEGS
+BA90;BA90;1106 1168 11AB;BA90;1106 1168 11AB; # (ëª; ëª; 몐; ëª; 몐; ) HANGUL SYLLABLE MYEN
+BA91;BA91;1106 1168 11AC;BA91;1106 1168 11AC; # (몑; 몑; 몑; 몑; 몑; ) HANGUL SYLLABLE MYENJ
+BA92;BA92;1106 1168 11AD;BA92;1106 1168 11AD; # (몒; 몒; 몒; 몒; 몒; ) HANGUL SYLLABLE MYENH
+BA93;BA93;1106 1168 11AE;BA93;1106 1168 11AE; # (몓; 몓; 몓; 몓; 몓; ) HANGUL SYLLABLE MYED
+BA94;BA94;1106 1168 11AF;BA94;1106 1168 11AF; # (몔; 몔; 몔; 몔; 몔; ) HANGUL SYLLABLE MYEL
+BA95;BA95;1106 1168 11B0;BA95;1106 1168 11B0; # (몕; 몕; 몕; 몕; 몕; ) HANGUL SYLLABLE MYELG
+BA96;BA96;1106 1168 11B1;BA96;1106 1168 11B1; # (몖; 몖; 몖; 몖; 몖; ) HANGUL SYLLABLE MYELM
+BA97;BA97;1106 1168 11B2;BA97;1106 1168 11B2; # (몗; 몗; 몗; 몗; 몗; ) HANGUL SYLLABLE MYELB
+BA98;BA98;1106 1168 11B3;BA98;1106 1168 11B3; # (몘; 몘; 몘; 몘; 몘; ) HANGUL SYLLABLE MYELS
+BA99;BA99;1106 1168 11B4;BA99;1106 1168 11B4; # (몙; 몙; 몙; 몙; 몙; ) HANGUL SYLLABLE MYELT
+BA9A;BA9A;1106 1168 11B5;BA9A;1106 1168 11B5; # (몚; 몚; 몚; 몚; 몚; ) HANGUL SYLLABLE MYELP
+BA9B;BA9B;1106 1168 11B6;BA9B;1106 1168 11B6; # (몛; 몛; 몛; 몛; 몛; ) HANGUL SYLLABLE MYELH
+BA9C;BA9C;1106 1168 11B7;BA9C;1106 1168 11B7; # (몜; 몜; 몜; 몜; 몜; ) HANGUL SYLLABLE MYEM
+BA9D;BA9D;1106 1168 11B8;BA9D;1106 1168 11B8; # (ëª; ëª; 몝; ëª; 몝; ) HANGUL SYLLABLE MYEB
+BA9E;BA9E;1106 1168 11B9;BA9E;1106 1168 11B9; # (몞; 몞; 몞; 몞; 몞; ) HANGUL SYLLABLE MYEBS
+BA9F;BA9F;1106 1168 11BA;BA9F;1106 1168 11BA; # (몟; 몟; 몟; 몟; 몟; ) HANGUL SYLLABLE MYES
+BAA0;BAA0;1106 1168 11BB;BAA0;1106 1168 11BB; # (몠; 몠; 몠; 몠; 몠; ) HANGUL SYLLABLE MYESS
+BAA1;BAA1;1106 1168 11BC;BAA1;1106 1168 11BC; # (몡; 몡; 몡; 몡; 몡; ) HANGUL SYLLABLE MYENG
+BAA2;BAA2;1106 1168 11BD;BAA2;1106 1168 11BD; # (몢; 몢; 몢; 몢; 몢; ) HANGUL SYLLABLE MYEJ
+BAA3;BAA3;1106 1168 11BE;BAA3;1106 1168 11BE; # (몣; 몣; 몣; 몣; 몣; ) HANGUL SYLLABLE MYEC
+BAA4;BAA4;1106 1168 11BF;BAA4;1106 1168 11BF; # (몤; 몤; 몤; 몤; 몤; ) HANGUL SYLLABLE MYEK
+BAA5;BAA5;1106 1168 11C0;BAA5;1106 1168 11C0; # (몥; 몥; 몥; 몥; 몥; ) HANGUL SYLLABLE MYET
+BAA6;BAA6;1106 1168 11C1;BAA6;1106 1168 11C1; # (몦; 몦; 몌á‡; 몦; 몌á‡; ) HANGUL SYLLABLE MYEP
+BAA7;BAA7;1106 1168 11C2;BAA7;1106 1168 11C2; # (몧; 몧; 몧; 몧; 몧; ) HANGUL SYLLABLE MYEH
+BAA8;BAA8;1106 1169;BAA8;1106 1169; # (모; 모; 모; 모; 모; ) HANGUL SYLLABLE MO
+BAA9;BAA9;1106 1169 11A8;BAA9;1106 1169 11A8; # (목; 목; 목; 목; 목; ) HANGUL SYLLABLE MOG
+BAAA;BAAA;1106 1169 11A9;BAAA;1106 1169 11A9; # (몪; 몪; 몪; 몪; 몪; ) HANGUL SYLLABLE MOGG
+BAAB;BAAB;1106 1169 11AA;BAAB;1106 1169 11AA; # (몫; 몫; 몫; 몫; 몫; ) HANGUL SYLLABLE MOGS
+BAAC;BAAC;1106 1169 11AB;BAAC;1106 1169 11AB; # (몬; 몬; 몬; 몬; 몬; ) HANGUL SYLLABLE MON
+BAAD;BAAD;1106 1169 11AC;BAAD;1106 1169 11AC; # (몭; 몭; 몭; 몭; 몭; ) HANGUL SYLLABLE MONJ
+BAAE;BAAE;1106 1169 11AD;BAAE;1106 1169 11AD; # (몮; 몮; 몮; 몮; 몮; ) HANGUL SYLLABLE MONH
+BAAF;BAAF;1106 1169 11AE;BAAF;1106 1169 11AE; # (몯; 몯; 몯; 몯; 몯; ) HANGUL SYLLABLE MOD
+BAB0;BAB0;1106 1169 11AF;BAB0;1106 1169 11AF; # (몰; 몰; 몰; 몰; 몰; ) HANGUL SYLLABLE MOL
+BAB1;BAB1;1106 1169 11B0;BAB1;1106 1169 11B0; # (몱; 몱; 몱; 몱; 몱; ) HANGUL SYLLABLE MOLG
+BAB2;BAB2;1106 1169 11B1;BAB2;1106 1169 11B1; # (몲; 몲; 몲; 몲; 몲; ) HANGUL SYLLABLE MOLM
+BAB3;BAB3;1106 1169 11B2;BAB3;1106 1169 11B2; # (몳; 몳; 몳; 몳; 몳; ) HANGUL SYLLABLE MOLB
+BAB4;BAB4;1106 1169 11B3;BAB4;1106 1169 11B3; # (몴; 몴; 몴; 몴; 몴; ) HANGUL SYLLABLE MOLS
+BAB5;BAB5;1106 1169 11B4;BAB5;1106 1169 11B4; # (몵; 몵; 몵; 몵; 몵; ) HANGUL SYLLABLE MOLT
+BAB6;BAB6;1106 1169 11B5;BAB6;1106 1169 11B5; # (몶; 몶; 몶; 몶; 몶; ) HANGUL SYLLABLE MOLP
+BAB7;BAB7;1106 1169 11B6;BAB7;1106 1169 11B6; # (몷; 몷; 몷; 몷; 몷; ) HANGUL SYLLABLE MOLH
+BAB8;BAB8;1106 1169 11B7;BAB8;1106 1169 11B7; # (몸; 몸; 몸; 몸; 몸; ) HANGUL SYLLABLE MOM
+BAB9;BAB9;1106 1169 11B8;BAB9;1106 1169 11B8; # (몹; 몹; 몹; 몹; 몹; ) HANGUL SYLLABLE MOB
+BABA;BABA;1106 1169 11B9;BABA;1106 1169 11B9; # (몺; 몺; 몺; 몺; 몺; ) HANGUL SYLLABLE MOBS
+BABB;BABB;1106 1169 11BA;BABB;1106 1169 11BA; # (못; 못; 못; 못; 못; ) HANGUL SYLLABLE MOS
+BABC;BABC;1106 1169 11BB;BABC;1106 1169 11BB; # (몼; 몼; 몼; 몼; 몼; ) HANGUL SYLLABLE MOSS
+BABD;BABD;1106 1169 11BC;BABD;1106 1169 11BC; # (몽; 몽; 몽; 몽; 몽; ) HANGUL SYLLABLE MONG
+BABE;BABE;1106 1169 11BD;BABE;1106 1169 11BD; # (몾; 몾; 몾; 몾; 몾; ) HANGUL SYLLABLE MOJ
+BABF;BABF;1106 1169 11BE;BABF;1106 1169 11BE; # (몿; 몿; 몿; 몿; 몿; ) HANGUL SYLLABLE MOC
+BAC0;BAC0;1106 1169 11BF;BAC0;1106 1169 11BF; # (뫀; 뫀; 뫀; 뫀; 뫀; ) HANGUL SYLLABLE MOK
+BAC1;BAC1;1106 1169 11C0;BAC1;1106 1169 11C0; # (ë«; ë«; 뫁; ë«; 뫁; ) HANGUL SYLLABLE MOT
+BAC2;BAC2;1106 1169 11C1;BAC2;1106 1169 11C1; # (ë«‚; ë«‚; 모á‡; ë«‚; 모á‡; ) HANGUL SYLLABLE MOP
+BAC3;BAC3;1106 1169 11C2;BAC3;1106 1169 11C2; # (뫃; 뫃; 뫃; 뫃; 뫃; ) HANGUL SYLLABLE MOH
+BAC4;BAC4;1106 116A;BAC4;1106 116A; # (뫄; 뫄; 뫄; 뫄; 뫄; ) HANGUL SYLLABLE MWA
+BAC5;BAC5;1106 116A 11A8;BAC5;1106 116A 11A8; # (뫅; 뫅; 뫅; 뫅; 뫅; ) HANGUL SYLLABLE MWAG
+BAC6;BAC6;1106 116A 11A9;BAC6;1106 116A 11A9; # (뫆; 뫆; 뫆; 뫆; 뫆; ) HANGUL SYLLABLE MWAGG
+BAC7;BAC7;1106 116A 11AA;BAC7;1106 116A 11AA; # (뫇; 뫇; 뫇; 뫇; 뫇; ) HANGUL SYLLABLE MWAGS
+BAC8;BAC8;1106 116A 11AB;BAC8;1106 116A 11AB; # (뫈; 뫈; 뫈; 뫈; 뫈; ) HANGUL SYLLABLE MWAN
+BAC9;BAC9;1106 116A 11AC;BAC9;1106 116A 11AC; # (뫉; 뫉; 뫉; 뫉; 뫉; ) HANGUL SYLLABLE MWANJ
+BACA;BACA;1106 116A 11AD;BACA;1106 116A 11AD; # (뫊; 뫊; 뫊; 뫊; 뫊; ) HANGUL SYLLABLE MWANH
+BACB;BACB;1106 116A 11AE;BACB;1106 116A 11AE; # (뫋; 뫋; 뫋; 뫋; 뫋; ) HANGUL SYLLABLE MWAD
+BACC;BACC;1106 116A 11AF;BACC;1106 116A 11AF; # (뫌; 뫌; 뫌; 뫌; 뫌; ) HANGUL SYLLABLE MWAL
+BACD;BACD;1106 116A 11B0;BACD;1106 116A 11B0; # (ë«; ë«; 뫍; ë«; 뫍; ) HANGUL SYLLABLE MWALG
+BACE;BACE;1106 116A 11B1;BACE;1106 116A 11B1; # (뫎; 뫎; 뫎; 뫎; 뫎; ) HANGUL SYLLABLE MWALM
+BACF;BACF;1106 116A 11B2;BACF;1106 116A 11B2; # (ë«; ë«; 뫏; ë«; 뫏; ) HANGUL SYLLABLE MWALB
+BAD0;BAD0;1106 116A 11B3;BAD0;1106 116A 11B3; # (ë«; ë«; 뫐; ë«; 뫐; ) HANGUL SYLLABLE MWALS
+BAD1;BAD1;1106 116A 11B4;BAD1;1106 116A 11B4; # (뫑; 뫑; 뫑; 뫑; 뫑; ) HANGUL SYLLABLE MWALT
+BAD2;BAD2;1106 116A 11B5;BAD2;1106 116A 11B5; # (뫒; 뫒; 뫒; 뫒; 뫒; ) HANGUL SYLLABLE MWALP
+BAD3;BAD3;1106 116A 11B6;BAD3;1106 116A 11B6; # (뫓; 뫓; 뫓; 뫓; 뫓; ) HANGUL SYLLABLE MWALH
+BAD4;BAD4;1106 116A 11B7;BAD4;1106 116A 11B7; # (뫔; 뫔; 뫔; 뫔; 뫔; ) HANGUL SYLLABLE MWAM
+BAD5;BAD5;1106 116A 11B8;BAD5;1106 116A 11B8; # (뫕; 뫕; 뫕; 뫕; 뫕; ) HANGUL SYLLABLE MWAB
+BAD6;BAD6;1106 116A 11B9;BAD6;1106 116A 11B9; # (뫖; 뫖; 뫖; 뫖; 뫖; ) HANGUL SYLLABLE MWABS
+BAD7;BAD7;1106 116A 11BA;BAD7;1106 116A 11BA; # (뫗; 뫗; 뫗; 뫗; 뫗; ) HANGUL SYLLABLE MWAS
+BAD8;BAD8;1106 116A 11BB;BAD8;1106 116A 11BB; # (뫘; 뫘; 뫘; 뫘; 뫘; ) HANGUL SYLLABLE MWASS
+BAD9;BAD9;1106 116A 11BC;BAD9;1106 116A 11BC; # (뫙; 뫙; 뫙; 뫙; 뫙; ) HANGUL SYLLABLE MWANG
+BADA;BADA;1106 116A 11BD;BADA;1106 116A 11BD; # (뫚; 뫚; 뫚; 뫚; 뫚; ) HANGUL SYLLABLE MWAJ
+BADB;BADB;1106 116A 11BE;BADB;1106 116A 11BE; # (뫛; 뫛; 뫛; 뫛; 뫛; ) HANGUL SYLLABLE MWAC
+BADC;BADC;1106 116A 11BF;BADC;1106 116A 11BF; # (뫜; 뫜; 뫜; 뫜; 뫜; ) HANGUL SYLLABLE MWAK
+BADD;BADD;1106 116A 11C0;BADD;1106 116A 11C0; # (ë«; ë«; 뫝; ë«; 뫝; ) HANGUL SYLLABLE MWAT
+BADE;BADE;1106 116A 11C1;BADE;1106 116A 11C1; # (뫞; 뫞; 뫄á‡; 뫞; 뫄á‡; ) HANGUL SYLLABLE MWAP
+BADF;BADF;1106 116A 11C2;BADF;1106 116A 11C2; # (뫟; 뫟; 뫟; 뫟; 뫟; ) HANGUL SYLLABLE MWAH
+BAE0;BAE0;1106 116B;BAE0;1106 116B; # (뫠; 뫠; 뫠; 뫠; 뫠; ) HANGUL SYLLABLE MWAE
+BAE1;BAE1;1106 116B 11A8;BAE1;1106 116B 11A8; # (뫡; 뫡; 뫡; 뫡; 뫡; ) HANGUL SYLLABLE MWAEG
+BAE2;BAE2;1106 116B 11A9;BAE2;1106 116B 11A9; # (뫢; 뫢; 뫢; 뫢; 뫢; ) HANGUL SYLLABLE MWAEGG
+BAE3;BAE3;1106 116B 11AA;BAE3;1106 116B 11AA; # (뫣; 뫣; 뫣; 뫣; 뫣; ) HANGUL SYLLABLE MWAEGS
+BAE4;BAE4;1106 116B 11AB;BAE4;1106 116B 11AB; # (뫤; 뫤; 뫤; 뫤; 뫤; ) HANGUL SYLLABLE MWAEN
+BAE5;BAE5;1106 116B 11AC;BAE5;1106 116B 11AC; # (뫥; 뫥; 뫥; 뫥; 뫥; ) HANGUL SYLLABLE MWAENJ
+BAE6;BAE6;1106 116B 11AD;BAE6;1106 116B 11AD; # (뫦; 뫦; 뫦; 뫦; 뫦; ) HANGUL SYLLABLE MWAENH
+BAE7;BAE7;1106 116B 11AE;BAE7;1106 116B 11AE; # (뫧; 뫧; 뫧; 뫧; 뫧; ) HANGUL SYLLABLE MWAED
+BAE8;BAE8;1106 116B 11AF;BAE8;1106 116B 11AF; # (뫨; 뫨; 뫨; 뫨; 뫨; ) HANGUL SYLLABLE MWAEL
+BAE9;BAE9;1106 116B 11B0;BAE9;1106 116B 11B0; # (뫩; 뫩; 뫩; 뫩; 뫩; ) HANGUL SYLLABLE MWAELG
+BAEA;BAEA;1106 116B 11B1;BAEA;1106 116B 11B1; # (뫪; 뫪; 뫪; 뫪; 뫪; ) HANGUL SYLLABLE MWAELM
+BAEB;BAEB;1106 116B 11B2;BAEB;1106 116B 11B2; # (뫫; 뫫; 뫫; 뫫; 뫫; ) HANGUL SYLLABLE MWAELB
+BAEC;BAEC;1106 116B 11B3;BAEC;1106 116B 11B3; # (뫬; 뫬; 뫬; 뫬; 뫬; ) HANGUL SYLLABLE MWAELS
+BAED;BAED;1106 116B 11B4;BAED;1106 116B 11B4; # (뫭; 뫭; 뫭; 뫭; 뫭; ) HANGUL SYLLABLE MWAELT
+BAEE;BAEE;1106 116B 11B5;BAEE;1106 116B 11B5; # (뫮; 뫮; 뫮; 뫮; 뫮; ) HANGUL SYLLABLE MWAELP
+BAEF;BAEF;1106 116B 11B6;BAEF;1106 116B 11B6; # (뫯; 뫯; 뫯; 뫯; 뫯; ) HANGUL SYLLABLE MWAELH
+BAF0;BAF0;1106 116B 11B7;BAF0;1106 116B 11B7; # (뫰; 뫰; 뫰; 뫰; 뫰; ) HANGUL SYLLABLE MWAEM
+BAF1;BAF1;1106 116B 11B8;BAF1;1106 116B 11B8; # (뫱; 뫱; 뫱; 뫱; 뫱; ) HANGUL SYLLABLE MWAEB
+BAF2;BAF2;1106 116B 11B9;BAF2;1106 116B 11B9; # (뫲; 뫲; 뫲; 뫲; 뫲; ) HANGUL SYLLABLE MWAEBS
+BAF3;BAF3;1106 116B 11BA;BAF3;1106 116B 11BA; # (뫳; 뫳; 뫳; 뫳; 뫳; ) HANGUL SYLLABLE MWAES
+BAF4;BAF4;1106 116B 11BB;BAF4;1106 116B 11BB; # (뫴; 뫴; 뫴; 뫴; 뫴; ) HANGUL SYLLABLE MWAESS
+BAF5;BAF5;1106 116B 11BC;BAF5;1106 116B 11BC; # (뫵; 뫵; 뫵; 뫵; 뫵; ) HANGUL SYLLABLE MWAENG
+BAF6;BAF6;1106 116B 11BD;BAF6;1106 116B 11BD; # (뫶; 뫶; 뫶; 뫶; 뫶; ) HANGUL SYLLABLE MWAEJ
+BAF7;BAF7;1106 116B 11BE;BAF7;1106 116B 11BE; # (뫷; 뫷; 뫷; 뫷; 뫷; ) HANGUL SYLLABLE MWAEC
+BAF8;BAF8;1106 116B 11BF;BAF8;1106 116B 11BF; # (뫸; 뫸; 뫸; 뫸; 뫸; ) HANGUL SYLLABLE MWAEK
+BAF9;BAF9;1106 116B 11C0;BAF9;1106 116B 11C0; # (뫹; 뫹; 뫹; 뫹; 뫹; ) HANGUL SYLLABLE MWAET
+BAFA;BAFA;1106 116B 11C1;BAFA;1106 116B 11C1; # (뫺; 뫺; 뫠á‡; 뫺; 뫠á‡; ) HANGUL SYLLABLE MWAEP
+BAFB;BAFB;1106 116B 11C2;BAFB;1106 116B 11C2; # (뫻; 뫻; 뫻; 뫻; 뫻; ) HANGUL SYLLABLE MWAEH
+BAFC;BAFC;1106 116C;BAFC;1106 116C; # (뫼; 뫼; 뫼; 뫼; 뫼; ) HANGUL SYLLABLE MOE
+BAFD;BAFD;1106 116C 11A8;BAFD;1106 116C 11A8; # (뫽; 뫽; 뫽; 뫽; 뫽; ) HANGUL SYLLABLE MOEG
+BAFE;BAFE;1106 116C 11A9;BAFE;1106 116C 11A9; # (뫾; 뫾; 뫾; 뫾; 뫾; ) HANGUL SYLLABLE MOEGG
+BAFF;BAFF;1106 116C 11AA;BAFF;1106 116C 11AA; # (뫿; 뫿; 뫿; 뫿; 뫿; ) HANGUL SYLLABLE MOEGS
+BB00;BB00;1106 116C 11AB;BB00;1106 116C 11AB; # (묀; 묀; 묀; 묀; 묀; ) HANGUL SYLLABLE MOEN
+BB01;BB01;1106 116C 11AC;BB01;1106 116C 11AC; # (ë¬; ë¬; 묁; ë¬; 묁; ) HANGUL SYLLABLE MOENJ
+BB02;BB02;1106 116C 11AD;BB02;1106 116C 11AD; # (묂; 묂; 묂; 묂; 묂; ) HANGUL SYLLABLE MOENH
+BB03;BB03;1106 116C 11AE;BB03;1106 116C 11AE; # (묃; 묃; 묃; 묃; 묃; ) HANGUL SYLLABLE MOED
+BB04;BB04;1106 116C 11AF;BB04;1106 116C 11AF; # (묄; 묄; 묄; 묄; 묄; ) HANGUL SYLLABLE MOEL
+BB05;BB05;1106 116C 11B0;BB05;1106 116C 11B0; # (묅; 묅; 묅; 묅; 묅; ) HANGUL SYLLABLE MOELG
+BB06;BB06;1106 116C 11B1;BB06;1106 116C 11B1; # (묆; 묆; 묆; 묆; 묆; ) HANGUL SYLLABLE MOELM
+BB07;BB07;1106 116C 11B2;BB07;1106 116C 11B2; # (묇; 묇; 묇; 묇; 묇; ) HANGUL SYLLABLE MOELB
+BB08;BB08;1106 116C 11B3;BB08;1106 116C 11B3; # (묈; 묈; 묈; 묈; 묈; ) HANGUL SYLLABLE MOELS
+BB09;BB09;1106 116C 11B4;BB09;1106 116C 11B4; # (묉; 묉; 묉; 묉; 묉; ) HANGUL SYLLABLE MOELT
+BB0A;BB0A;1106 116C 11B5;BB0A;1106 116C 11B5; # (묊; 묊; 묊; 묊; 묊; ) HANGUL SYLLABLE MOELP
+BB0B;BB0B;1106 116C 11B6;BB0B;1106 116C 11B6; # (묋; 묋; 묋; 묋; 묋; ) HANGUL SYLLABLE MOELH
+BB0C;BB0C;1106 116C 11B7;BB0C;1106 116C 11B7; # (묌; 묌; 묌; 묌; 묌; ) HANGUL SYLLABLE MOEM
+BB0D;BB0D;1106 116C 11B8;BB0D;1106 116C 11B8; # (ë¬; ë¬; 묍; ë¬; 묍; ) HANGUL SYLLABLE MOEB
+BB0E;BB0E;1106 116C 11B9;BB0E;1106 116C 11B9; # (묎; 묎; 묎; 묎; 묎; ) HANGUL SYLLABLE MOEBS
+BB0F;BB0F;1106 116C 11BA;BB0F;1106 116C 11BA; # (ë¬; ë¬; 묏; ë¬; 묏; ) HANGUL SYLLABLE MOES
+BB10;BB10;1106 116C 11BB;BB10;1106 116C 11BB; # (ë¬; ë¬; 묐; ë¬; 묐; ) HANGUL SYLLABLE MOESS
+BB11;BB11;1106 116C 11BC;BB11;1106 116C 11BC; # (묑; 묑; 묑; 묑; 묑; ) HANGUL SYLLABLE MOENG
+BB12;BB12;1106 116C 11BD;BB12;1106 116C 11BD; # (묒; 묒; 묒; 묒; 묒; ) HANGUL SYLLABLE MOEJ
+BB13;BB13;1106 116C 11BE;BB13;1106 116C 11BE; # (묓; 묓; 묓; 묓; 묓; ) HANGUL SYLLABLE MOEC
+BB14;BB14;1106 116C 11BF;BB14;1106 116C 11BF; # (묔; 묔; 묔; 묔; 묔; ) HANGUL SYLLABLE MOEK
+BB15;BB15;1106 116C 11C0;BB15;1106 116C 11C0; # (묕; 묕; 묕; 묕; 묕; ) HANGUL SYLLABLE MOET
+BB16;BB16;1106 116C 11C1;BB16;1106 116C 11C1; # (묖; 묖; 뫼á‡; 묖; 뫼á‡; ) HANGUL SYLLABLE MOEP
+BB17;BB17;1106 116C 11C2;BB17;1106 116C 11C2; # (묗; 묗; 묗; 묗; 묗; ) HANGUL SYLLABLE MOEH
+BB18;BB18;1106 116D;BB18;1106 116D; # (묘; 묘; 묘; 묘; 묘; ) HANGUL SYLLABLE MYO
+BB19;BB19;1106 116D 11A8;BB19;1106 116D 11A8; # (묙; 묙; 묙; 묙; 묙; ) HANGUL SYLLABLE MYOG
+BB1A;BB1A;1106 116D 11A9;BB1A;1106 116D 11A9; # (묚; 묚; 묚; 묚; 묚; ) HANGUL SYLLABLE MYOGG
+BB1B;BB1B;1106 116D 11AA;BB1B;1106 116D 11AA; # (묛; 묛; 묛; 묛; 묛; ) HANGUL SYLLABLE MYOGS
+BB1C;BB1C;1106 116D 11AB;BB1C;1106 116D 11AB; # (묜; 묜; 묜; 묜; 묜; ) HANGUL SYLLABLE MYON
+BB1D;BB1D;1106 116D 11AC;BB1D;1106 116D 11AC; # (ë¬; ë¬; 묝; ë¬; 묝; ) HANGUL SYLLABLE MYONJ
+BB1E;BB1E;1106 116D 11AD;BB1E;1106 116D 11AD; # (묞; 묞; 묞; 묞; 묞; ) HANGUL SYLLABLE MYONH
+BB1F;BB1F;1106 116D 11AE;BB1F;1106 116D 11AE; # (묟; 묟; 묟; 묟; 묟; ) HANGUL SYLLABLE MYOD
+BB20;BB20;1106 116D 11AF;BB20;1106 116D 11AF; # (묠; 묠; 묠; 묠; 묠; ) HANGUL SYLLABLE MYOL
+BB21;BB21;1106 116D 11B0;BB21;1106 116D 11B0; # (묡; 묡; 묡; 묡; 묡; ) HANGUL SYLLABLE MYOLG
+BB22;BB22;1106 116D 11B1;BB22;1106 116D 11B1; # (묢; 묢; 묢; 묢; 묢; ) HANGUL SYLLABLE MYOLM
+BB23;BB23;1106 116D 11B2;BB23;1106 116D 11B2; # (묣; 묣; 묣; 묣; 묣; ) HANGUL SYLLABLE MYOLB
+BB24;BB24;1106 116D 11B3;BB24;1106 116D 11B3; # (묤; 묤; 묤; 묤; 묤; ) HANGUL SYLLABLE MYOLS
+BB25;BB25;1106 116D 11B4;BB25;1106 116D 11B4; # (묥; 묥; 묥; 묥; 묥; ) HANGUL SYLLABLE MYOLT
+BB26;BB26;1106 116D 11B5;BB26;1106 116D 11B5; # (묦; 묦; 묦; 묦; 묦; ) HANGUL SYLLABLE MYOLP
+BB27;BB27;1106 116D 11B6;BB27;1106 116D 11B6; # (묧; 묧; 묧; 묧; 묧; ) HANGUL SYLLABLE MYOLH
+BB28;BB28;1106 116D 11B7;BB28;1106 116D 11B7; # (묨; 묨; 묨; 묨; 묨; ) HANGUL SYLLABLE MYOM
+BB29;BB29;1106 116D 11B8;BB29;1106 116D 11B8; # (묩; 묩; 묩; 묩; 묩; ) HANGUL SYLLABLE MYOB
+BB2A;BB2A;1106 116D 11B9;BB2A;1106 116D 11B9; # (묪; 묪; 묪; 묪; 묪; ) HANGUL SYLLABLE MYOBS
+BB2B;BB2B;1106 116D 11BA;BB2B;1106 116D 11BA; # (묫; 묫; 묫; 묫; 묫; ) HANGUL SYLLABLE MYOS
+BB2C;BB2C;1106 116D 11BB;BB2C;1106 116D 11BB; # (묬; 묬; 묬; 묬; 묬; ) HANGUL SYLLABLE MYOSS
+BB2D;BB2D;1106 116D 11BC;BB2D;1106 116D 11BC; # (묭; 묭; 묭; 묭; 묭; ) HANGUL SYLLABLE MYONG
+BB2E;BB2E;1106 116D 11BD;BB2E;1106 116D 11BD; # (묮; 묮; 묮; 묮; 묮; ) HANGUL SYLLABLE MYOJ
+BB2F;BB2F;1106 116D 11BE;BB2F;1106 116D 11BE; # (묯; 묯; 묯; 묯; 묯; ) HANGUL SYLLABLE MYOC
+BB30;BB30;1106 116D 11BF;BB30;1106 116D 11BF; # (묰; 묰; 묰; 묰; 묰; ) HANGUL SYLLABLE MYOK
+BB31;BB31;1106 116D 11C0;BB31;1106 116D 11C0; # (묱; 묱; 묱; 묱; 묱; ) HANGUL SYLLABLE MYOT
+BB32;BB32;1106 116D 11C1;BB32;1106 116D 11C1; # (묲; 묲; 묘á‡; 묲; 묘á‡; ) HANGUL SYLLABLE MYOP
+BB33;BB33;1106 116D 11C2;BB33;1106 116D 11C2; # (묳; 묳; 묳; 묳; 묳; ) HANGUL SYLLABLE MYOH
+BB34;BB34;1106 116E;BB34;1106 116E; # (무; 무; 무; 무; 무; ) HANGUL SYLLABLE MU
+BB35;BB35;1106 116E 11A8;BB35;1106 116E 11A8; # (묵; 묵; 묵; 묵; 묵; ) HANGUL SYLLABLE MUG
+BB36;BB36;1106 116E 11A9;BB36;1106 116E 11A9; # (묶; 묶; 묶; 묶; 묶; ) HANGUL SYLLABLE MUGG
+BB37;BB37;1106 116E 11AA;BB37;1106 116E 11AA; # (묷; 묷; 묷; 묷; 묷; ) HANGUL SYLLABLE MUGS
+BB38;BB38;1106 116E 11AB;BB38;1106 116E 11AB; # (문; 문; 문; 문; 문; ) HANGUL SYLLABLE MUN
+BB39;BB39;1106 116E 11AC;BB39;1106 116E 11AC; # (묹; 묹; 묹; 묹; 묹; ) HANGUL SYLLABLE MUNJ
+BB3A;BB3A;1106 116E 11AD;BB3A;1106 116E 11AD; # (묺; 묺; 묺; 묺; 묺; ) HANGUL SYLLABLE MUNH
+BB3B;BB3B;1106 116E 11AE;BB3B;1106 116E 11AE; # (묻; 묻; 묻; 묻; 묻; ) HANGUL SYLLABLE MUD
+BB3C;BB3C;1106 116E 11AF;BB3C;1106 116E 11AF; # (물; 물; 물; 물; 물; ) HANGUL SYLLABLE MUL
+BB3D;BB3D;1106 116E 11B0;BB3D;1106 116E 11B0; # (묽; 묽; 묽; 묽; 묽; ) HANGUL SYLLABLE MULG
+BB3E;BB3E;1106 116E 11B1;BB3E;1106 116E 11B1; # (묾; 묾; 묾; 묾; 묾; ) HANGUL SYLLABLE MULM
+BB3F;BB3F;1106 116E 11B2;BB3F;1106 116E 11B2; # (묿; 묿; 묿; 묿; 묿; ) HANGUL SYLLABLE MULB
+BB40;BB40;1106 116E 11B3;BB40;1106 116E 11B3; # (뭀; 뭀; 뭀; 뭀; 뭀; ) HANGUL SYLLABLE MULS
+BB41;BB41;1106 116E 11B4;BB41;1106 116E 11B4; # (ë­; ë­; 뭁; ë­; 뭁; ) HANGUL SYLLABLE MULT
+BB42;BB42;1106 116E 11B5;BB42;1106 116E 11B5; # (뭂; 뭂; 뭂; 뭂; 뭂; ) HANGUL SYLLABLE MULP
+BB43;BB43;1106 116E 11B6;BB43;1106 116E 11B6; # (뭃; 뭃; 뭃; 뭃; 뭃; ) HANGUL SYLLABLE MULH
+BB44;BB44;1106 116E 11B7;BB44;1106 116E 11B7; # (뭄; 뭄; 뭄; 뭄; 뭄; ) HANGUL SYLLABLE MUM
+BB45;BB45;1106 116E 11B8;BB45;1106 116E 11B8; # (뭅; 뭅; 뭅; 뭅; 뭅; ) HANGUL SYLLABLE MUB
+BB46;BB46;1106 116E 11B9;BB46;1106 116E 11B9; # (뭆; 뭆; 뭆; 뭆; 뭆; ) HANGUL SYLLABLE MUBS
+BB47;BB47;1106 116E 11BA;BB47;1106 116E 11BA; # (뭇; 뭇; 뭇; 뭇; 뭇; ) HANGUL SYLLABLE MUS
+BB48;BB48;1106 116E 11BB;BB48;1106 116E 11BB; # (뭈; 뭈; 뭈; 뭈; 뭈; ) HANGUL SYLLABLE MUSS
+BB49;BB49;1106 116E 11BC;BB49;1106 116E 11BC; # (뭉; 뭉; 뭉; 뭉; 뭉; ) HANGUL SYLLABLE MUNG
+BB4A;BB4A;1106 116E 11BD;BB4A;1106 116E 11BD; # (뭊; 뭊; 뭊; 뭊; 뭊; ) HANGUL SYLLABLE MUJ
+BB4B;BB4B;1106 116E 11BE;BB4B;1106 116E 11BE; # (뭋; 뭋; 뭋; 뭋; 뭋; ) HANGUL SYLLABLE MUC
+BB4C;BB4C;1106 116E 11BF;BB4C;1106 116E 11BF; # (뭌; 뭌; 뭌; 뭌; 뭌; ) HANGUL SYLLABLE MUK
+BB4D;BB4D;1106 116E 11C0;BB4D;1106 116E 11C0; # (ë­; ë­; 뭍; ë­; 뭍; ) HANGUL SYLLABLE MUT
+BB4E;BB4E;1106 116E 11C1;BB4E;1106 116E 11C1; # (ë­Ž; ë­Ž; 무á‡; ë­Ž; 무á‡; ) HANGUL SYLLABLE MUP
+BB4F;BB4F;1106 116E 11C2;BB4F;1106 116E 11C2; # (ë­; ë­; 뭏; ë­; 뭏; ) HANGUL SYLLABLE MUH
+BB50;BB50;1106 116F;BB50;1106 116F; # (ë­; ë­; 뭐; ë­; 뭐; ) HANGUL SYLLABLE MWEO
+BB51;BB51;1106 116F 11A8;BB51;1106 116F 11A8; # (뭑; 뭑; 뭑; 뭑; 뭑; ) HANGUL SYLLABLE MWEOG
+BB52;BB52;1106 116F 11A9;BB52;1106 116F 11A9; # (뭒; 뭒; 뭒; 뭒; 뭒; ) HANGUL SYLLABLE MWEOGG
+BB53;BB53;1106 116F 11AA;BB53;1106 116F 11AA; # (뭓; 뭓; 뭓; 뭓; 뭓; ) HANGUL SYLLABLE MWEOGS
+BB54;BB54;1106 116F 11AB;BB54;1106 116F 11AB; # (뭔; 뭔; 뭔; 뭔; 뭔; ) HANGUL SYLLABLE MWEON
+BB55;BB55;1106 116F 11AC;BB55;1106 116F 11AC; # (뭕; 뭕; 뭕; 뭕; 뭕; ) HANGUL SYLLABLE MWEONJ
+BB56;BB56;1106 116F 11AD;BB56;1106 116F 11AD; # (뭖; 뭖; 뭖; 뭖; 뭖; ) HANGUL SYLLABLE MWEONH
+BB57;BB57;1106 116F 11AE;BB57;1106 116F 11AE; # (뭗; 뭗; 뭗; 뭗; 뭗; ) HANGUL SYLLABLE MWEOD
+BB58;BB58;1106 116F 11AF;BB58;1106 116F 11AF; # (뭘; 뭘; 뭘; 뭘; 뭘; ) HANGUL SYLLABLE MWEOL
+BB59;BB59;1106 116F 11B0;BB59;1106 116F 11B0; # (뭙; 뭙; 뭙; 뭙; 뭙; ) HANGUL SYLLABLE MWEOLG
+BB5A;BB5A;1106 116F 11B1;BB5A;1106 116F 11B1; # (뭚; 뭚; 뭚; 뭚; 뭚; ) HANGUL SYLLABLE MWEOLM
+BB5B;BB5B;1106 116F 11B2;BB5B;1106 116F 11B2; # (뭛; 뭛; 뭛; 뭛; 뭛; ) HANGUL SYLLABLE MWEOLB
+BB5C;BB5C;1106 116F 11B3;BB5C;1106 116F 11B3; # (뭜; 뭜; 뭜; 뭜; 뭜; ) HANGUL SYLLABLE MWEOLS
+BB5D;BB5D;1106 116F 11B4;BB5D;1106 116F 11B4; # (ë­; ë­; 뭝; ë­; 뭝; ) HANGUL SYLLABLE MWEOLT
+BB5E;BB5E;1106 116F 11B5;BB5E;1106 116F 11B5; # (뭞; 뭞; 뭞; 뭞; 뭞; ) HANGUL SYLLABLE MWEOLP
+BB5F;BB5F;1106 116F 11B6;BB5F;1106 116F 11B6; # (뭟; 뭟; 뭟; 뭟; 뭟; ) HANGUL SYLLABLE MWEOLH
+BB60;BB60;1106 116F 11B7;BB60;1106 116F 11B7; # (뭠; 뭠; 뭠; 뭠; 뭠; ) HANGUL SYLLABLE MWEOM
+BB61;BB61;1106 116F 11B8;BB61;1106 116F 11B8; # (뭡; 뭡; 뭡; 뭡; 뭡; ) HANGUL SYLLABLE MWEOB
+BB62;BB62;1106 116F 11B9;BB62;1106 116F 11B9; # (뭢; 뭢; 뭢; 뭢; 뭢; ) HANGUL SYLLABLE MWEOBS
+BB63;BB63;1106 116F 11BA;BB63;1106 116F 11BA; # (뭣; 뭣; 뭣; 뭣; 뭣; ) HANGUL SYLLABLE MWEOS
+BB64;BB64;1106 116F 11BB;BB64;1106 116F 11BB; # (뭤; 뭤; 뭤; 뭤; 뭤; ) HANGUL SYLLABLE MWEOSS
+BB65;BB65;1106 116F 11BC;BB65;1106 116F 11BC; # (뭥; 뭥; 뭥; 뭥; 뭥; ) HANGUL SYLLABLE MWEONG
+BB66;BB66;1106 116F 11BD;BB66;1106 116F 11BD; # (뭦; 뭦; 뭦; 뭦; 뭦; ) HANGUL SYLLABLE MWEOJ
+BB67;BB67;1106 116F 11BE;BB67;1106 116F 11BE; # (뭧; 뭧; 뭧; 뭧; 뭧; ) HANGUL SYLLABLE MWEOC
+BB68;BB68;1106 116F 11BF;BB68;1106 116F 11BF; # (뭨; 뭨; 뭨; 뭨; 뭨; ) HANGUL SYLLABLE MWEOK
+BB69;BB69;1106 116F 11C0;BB69;1106 116F 11C0; # (뭩; 뭩; 뭩; 뭩; 뭩; ) HANGUL SYLLABLE MWEOT
+BB6A;BB6A;1106 116F 11C1;BB6A;1106 116F 11C1; # (ë­ª; ë­ª; 뭐á‡; ë­ª; 뭐á‡; ) HANGUL SYLLABLE MWEOP
+BB6B;BB6B;1106 116F 11C2;BB6B;1106 116F 11C2; # (뭫; 뭫; 뭫; 뭫; 뭫; ) HANGUL SYLLABLE MWEOH
+BB6C;BB6C;1106 1170;BB6C;1106 1170; # (뭬; 뭬; 뭬; 뭬; 뭬; ) HANGUL SYLLABLE MWE
+BB6D;BB6D;1106 1170 11A8;BB6D;1106 1170 11A8; # (뭭; 뭭; 뭭; 뭭; 뭭; ) HANGUL SYLLABLE MWEG
+BB6E;BB6E;1106 1170 11A9;BB6E;1106 1170 11A9; # (뭮; 뭮; 뭮; 뭮; 뭮; ) HANGUL SYLLABLE MWEGG
+BB6F;BB6F;1106 1170 11AA;BB6F;1106 1170 11AA; # (뭯; 뭯; 뭯; 뭯; 뭯; ) HANGUL SYLLABLE MWEGS
+BB70;BB70;1106 1170 11AB;BB70;1106 1170 11AB; # (뭰; 뭰; 뭰; 뭰; 뭰; ) HANGUL SYLLABLE MWEN
+BB71;BB71;1106 1170 11AC;BB71;1106 1170 11AC; # (뭱; 뭱; 뭱; 뭱; 뭱; ) HANGUL SYLLABLE MWENJ
+BB72;BB72;1106 1170 11AD;BB72;1106 1170 11AD; # (뭲; 뭲; 뭲; 뭲; 뭲; ) HANGUL SYLLABLE MWENH
+BB73;BB73;1106 1170 11AE;BB73;1106 1170 11AE; # (뭳; 뭳; 뭳; 뭳; 뭳; ) HANGUL SYLLABLE MWED
+BB74;BB74;1106 1170 11AF;BB74;1106 1170 11AF; # (뭴; 뭴; 뭴; 뭴; 뭴; ) HANGUL SYLLABLE MWEL
+BB75;BB75;1106 1170 11B0;BB75;1106 1170 11B0; # (뭵; 뭵; 뭵; 뭵; 뭵; ) HANGUL SYLLABLE MWELG
+BB76;BB76;1106 1170 11B1;BB76;1106 1170 11B1; # (뭶; 뭶; 뭶; 뭶; 뭶; ) HANGUL SYLLABLE MWELM
+BB77;BB77;1106 1170 11B2;BB77;1106 1170 11B2; # (뭷; 뭷; 뭷; 뭷; 뭷; ) HANGUL SYLLABLE MWELB
+BB78;BB78;1106 1170 11B3;BB78;1106 1170 11B3; # (뭸; 뭸; 뭸; 뭸; 뭸; ) HANGUL SYLLABLE MWELS
+BB79;BB79;1106 1170 11B4;BB79;1106 1170 11B4; # (뭹; 뭹; 뭹; 뭹; 뭹; ) HANGUL SYLLABLE MWELT
+BB7A;BB7A;1106 1170 11B5;BB7A;1106 1170 11B5; # (뭺; 뭺; 뭺; 뭺; 뭺; ) HANGUL SYLLABLE MWELP
+BB7B;BB7B;1106 1170 11B6;BB7B;1106 1170 11B6; # (뭻; 뭻; 뭻; 뭻; 뭻; ) HANGUL SYLLABLE MWELH
+BB7C;BB7C;1106 1170 11B7;BB7C;1106 1170 11B7; # (뭼; 뭼; 뭼; 뭼; 뭼; ) HANGUL SYLLABLE MWEM
+BB7D;BB7D;1106 1170 11B8;BB7D;1106 1170 11B8; # (뭽; 뭽; 뭽; 뭽; 뭽; ) HANGUL SYLLABLE MWEB
+BB7E;BB7E;1106 1170 11B9;BB7E;1106 1170 11B9; # (뭾; 뭾; 뭾; 뭾; 뭾; ) HANGUL SYLLABLE MWEBS
+BB7F;BB7F;1106 1170 11BA;BB7F;1106 1170 11BA; # (뭿; 뭿; 뭿; 뭿; 뭿; ) HANGUL SYLLABLE MWES
+BB80;BB80;1106 1170 11BB;BB80;1106 1170 11BB; # (뮀; 뮀; 뮀; 뮀; 뮀; ) HANGUL SYLLABLE MWESS
+BB81;BB81;1106 1170 11BC;BB81;1106 1170 11BC; # (ë®; ë®; 뮁; ë®; 뮁; ) HANGUL SYLLABLE MWENG
+BB82;BB82;1106 1170 11BD;BB82;1106 1170 11BD; # (뮂; 뮂; 뮂; 뮂; 뮂; ) HANGUL SYLLABLE MWEJ
+BB83;BB83;1106 1170 11BE;BB83;1106 1170 11BE; # (뮃; 뮃; 뮃; 뮃; 뮃; ) HANGUL SYLLABLE MWEC
+BB84;BB84;1106 1170 11BF;BB84;1106 1170 11BF; # (뮄; 뮄; 뮄; 뮄; 뮄; ) HANGUL SYLLABLE MWEK
+BB85;BB85;1106 1170 11C0;BB85;1106 1170 11C0; # (뮅; 뮅; 뮅; 뮅; 뮅; ) HANGUL SYLLABLE MWET
+BB86;BB86;1106 1170 11C1;BB86;1106 1170 11C1; # (뮆; 뮆; 뭬á‡; 뮆; 뭬á‡; ) HANGUL SYLLABLE MWEP
+BB87;BB87;1106 1170 11C2;BB87;1106 1170 11C2; # (뮇; 뮇; 뮇; 뮇; 뮇; ) HANGUL SYLLABLE MWEH
+BB88;BB88;1106 1171;BB88;1106 1171; # (뮈; 뮈; 뮈; 뮈; 뮈; ) HANGUL SYLLABLE MWI
+BB89;BB89;1106 1171 11A8;BB89;1106 1171 11A8; # (뮉; 뮉; 뮉; 뮉; 뮉; ) HANGUL SYLLABLE MWIG
+BB8A;BB8A;1106 1171 11A9;BB8A;1106 1171 11A9; # (뮊; 뮊; 뮊; 뮊; 뮊; ) HANGUL SYLLABLE MWIGG
+BB8B;BB8B;1106 1171 11AA;BB8B;1106 1171 11AA; # (뮋; 뮋; 뮋; 뮋; 뮋; ) HANGUL SYLLABLE MWIGS
+BB8C;BB8C;1106 1171 11AB;BB8C;1106 1171 11AB; # (뮌; 뮌; 뮌; 뮌; 뮌; ) HANGUL SYLLABLE MWIN
+BB8D;BB8D;1106 1171 11AC;BB8D;1106 1171 11AC; # (ë®; ë®; 뮍; ë®; 뮍; ) HANGUL SYLLABLE MWINJ
+BB8E;BB8E;1106 1171 11AD;BB8E;1106 1171 11AD; # (뮎; 뮎; 뮎; 뮎; 뮎; ) HANGUL SYLLABLE MWINH
+BB8F;BB8F;1106 1171 11AE;BB8F;1106 1171 11AE; # (ë®; ë®; 뮏; ë®; 뮏; ) HANGUL SYLLABLE MWID
+BB90;BB90;1106 1171 11AF;BB90;1106 1171 11AF; # (ë®; ë®; 뮐; ë®; 뮐; ) HANGUL SYLLABLE MWIL
+BB91;BB91;1106 1171 11B0;BB91;1106 1171 11B0; # (뮑; 뮑; 뮑; 뮑; 뮑; ) HANGUL SYLLABLE MWILG
+BB92;BB92;1106 1171 11B1;BB92;1106 1171 11B1; # (뮒; 뮒; 뮒; 뮒; 뮒; ) HANGUL SYLLABLE MWILM
+BB93;BB93;1106 1171 11B2;BB93;1106 1171 11B2; # (뮓; 뮓; 뮓; 뮓; 뮓; ) HANGUL SYLLABLE MWILB
+BB94;BB94;1106 1171 11B3;BB94;1106 1171 11B3; # (뮔; 뮔; 뮔; 뮔; 뮔; ) HANGUL SYLLABLE MWILS
+BB95;BB95;1106 1171 11B4;BB95;1106 1171 11B4; # (뮕; 뮕; 뮕; 뮕; 뮕; ) HANGUL SYLLABLE MWILT
+BB96;BB96;1106 1171 11B5;BB96;1106 1171 11B5; # (뮖; 뮖; 뮖; 뮖; 뮖; ) HANGUL SYLLABLE MWILP
+BB97;BB97;1106 1171 11B6;BB97;1106 1171 11B6; # (뮗; 뮗; 뮗; 뮗; 뮗; ) HANGUL SYLLABLE MWILH
+BB98;BB98;1106 1171 11B7;BB98;1106 1171 11B7; # (뮘; 뮘; 뮘; 뮘; 뮘; ) HANGUL SYLLABLE MWIM
+BB99;BB99;1106 1171 11B8;BB99;1106 1171 11B8; # (뮙; 뮙; 뮙; 뮙; 뮙; ) HANGUL SYLLABLE MWIB
+BB9A;BB9A;1106 1171 11B9;BB9A;1106 1171 11B9; # (뮚; 뮚; 뮚; 뮚; 뮚; ) HANGUL SYLLABLE MWIBS
+BB9B;BB9B;1106 1171 11BA;BB9B;1106 1171 11BA; # (뮛; 뮛; 뮛; 뮛; 뮛; ) HANGUL SYLLABLE MWIS
+BB9C;BB9C;1106 1171 11BB;BB9C;1106 1171 11BB; # (뮜; 뮜; 뮜; 뮜; 뮜; ) HANGUL SYLLABLE MWISS
+BB9D;BB9D;1106 1171 11BC;BB9D;1106 1171 11BC; # (ë®; ë®; 뮝; ë®; 뮝; ) HANGUL SYLLABLE MWING
+BB9E;BB9E;1106 1171 11BD;BB9E;1106 1171 11BD; # (뮞; 뮞; 뮞; 뮞; 뮞; ) HANGUL SYLLABLE MWIJ
+BB9F;BB9F;1106 1171 11BE;BB9F;1106 1171 11BE; # (뮟; 뮟; 뮟; 뮟; 뮟; ) HANGUL SYLLABLE MWIC
+BBA0;BBA0;1106 1171 11BF;BBA0;1106 1171 11BF; # (뮠; 뮠; 뮠; 뮠; 뮠; ) HANGUL SYLLABLE MWIK
+BBA1;BBA1;1106 1171 11C0;BBA1;1106 1171 11C0; # (뮡; 뮡; 뮡; 뮡; 뮡; ) HANGUL SYLLABLE MWIT
+BBA2;BBA2;1106 1171 11C1;BBA2;1106 1171 11C1; # (뮢; 뮢; 뮈á‡; 뮢; 뮈á‡; ) HANGUL SYLLABLE MWIP
+BBA3;BBA3;1106 1171 11C2;BBA3;1106 1171 11C2; # (뮣; 뮣; 뮣; 뮣; 뮣; ) HANGUL SYLLABLE MWIH
+BBA4;BBA4;1106 1172;BBA4;1106 1172; # (뮤; 뮤; 뮤; 뮤; 뮤; ) HANGUL SYLLABLE MYU
+BBA5;BBA5;1106 1172 11A8;BBA5;1106 1172 11A8; # (뮥; 뮥; 뮥; 뮥; 뮥; ) HANGUL SYLLABLE MYUG
+BBA6;BBA6;1106 1172 11A9;BBA6;1106 1172 11A9; # (뮦; 뮦; 뮦; 뮦; 뮦; ) HANGUL SYLLABLE MYUGG
+BBA7;BBA7;1106 1172 11AA;BBA7;1106 1172 11AA; # (뮧; 뮧; 뮧; 뮧; 뮧; ) HANGUL SYLLABLE MYUGS
+BBA8;BBA8;1106 1172 11AB;BBA8;1106 1172 11AB; # (뮨; 뮨; 뮨; 뮨; 뮨; ) HANGUL SYLLABLE MYUN
+BBA9;BBA9;1106 1172 11AC;BBA9;1106 1172 11AC; # (뮩; 뮩; 뮩; 뮩; 뮩; ) HANGUL SYLLABLE MYUNJ
+BBAA;BBAA;1106 1172 11AD;BBAA;1106 1172 11AD; # (뮪; 뮪; 뮪; 뮪; 뮪; ) HANGUL SYLLABLE MYUNH
+BBAB;BBAB;1106 1172 11AE;BBAB;1106 1172 11AE; # (뮫; 뮫; 뮫; 뮫; 뮫; ) HANGUL SYLLABLE MYUD
+BBAC;BBAC;1106 1172 11AF;BBAC;1106 1172 11AF; # (뮬; 뮬; 뮬; 뮬; 뮬; ) HANGUL SYLLABLE MYUL
+BBAD;BBAD;1106 1172 11B0;BBAD;1106 1172 11B0; # (뮭; 뮭; 뮭; 뮭; 뮭; ) HANGUL SYLLABLE MYULG
+BBAE;BBAE;1106 1172 11B1;BBAE;1106 1172 11B1; # (뮮; 뮮; 뮮; 뮮; 뮮; ) HANGUL SYLLABLE MYULM
+BBAF;BBAF;1106 1172 11B2;BBAF;1106 1172 11B2; # (뮯; 뮯; 뮯; 뮯; 뮯; ) HANGUL SYLLABLE MYULB
+BBB0;BBB0;1106 1172 11B3;BBB0;1106 1172 11B3; # (뮰; 뮰; 뮰; 뮰; 뮰; ) HANGUL SYLLABLE MYULS
+BBB1;BBB1;1106 1172 11B4;BBB1;1106 1172 11B4; # (뮱; 뮱; 뮱; 뮱; 뮱; ) HANGUL SYLLABLE MYULT
+BBB2;BBB2;1106 1172 11B5;BBB2;1106 1172 11B5; # (뮲; 뮲; 뮲; 뮲; 뮲; ) HANGUL SYLLABLE MYULP
+BBB3;BBB3;1106 1172 11B6;BBB3;1106 1172 11B6; # (뮳; 뮳; 뮳; 뮳; 뮳; ) HANGUL SYLLABLE MYULH
+BBB4;BBB4;1106 1172 11B7;BBB4;1106 1172 11B7; # (뮴; 뮴; 뮴; 뮴; 뮴; ) HANGUL SYLLABLE MYUM
+BBB5;BBB5;1106 1172 11B8;BBB5;1106 1172 11B8; # (뮵; 뮵; 뮵; 뮵; 뮵; ) HANGUL SYLLABLE MYUB
+BBB6;BBB6;1106 1172 11B9;BBB6;1106 1172 11B9; # (뮶; 뮶; 뮶; 뮶; 뮶; ) HANGUL SYLLABLE MYUBS
+BBB7;BBB7;1106 1172 11BA;BBB7;1106 1172 11BA; # (뮷; 뮷; 뮷; 뮷; 뮷; ) HANGUL SYLLABLE MYUS
+BBB8;BBB8;1106 1172 11BB;BBB8;1106 1172 11BB; # (뮸; 뮸; 뮸; 뮸; 뮸; ) HANGUL SYLLABLE MYUSS
+BBB9;BBB9;1106 1172 11BC;BBB9;1106 1172 11BC; # (뮹; 뮹; 뮹; 뮹; 뮹; ) HANGUL SYLLABLE MYUNG
+BBBA;BBBA;1106 1172 11BD;BBBA;1106 1172 11BD; # (뮺; 뮺; 뮺; 뮺; 뮺; ) HANGUL SYLLABLE MYUJ
+BBBB;BBBB;1106 1172 11BE;BBBB;1106 1172 11BE; # (뮻; 뮻; 뮻; 뮻; 뮻; ) HANGUL SYLLABLE MYUC
+BBBC;BBBC;1106 1172 11BF;BBBC;1106 1172 11BF; # (뮼; 뮼; 뮼; 뮼; 뮼; ) HANGUL SYLLABLE MYUK
+BBBD;BBBD;1106 1172 11C0;BBBD;1106 1172 11C0; # (뮽; 뮽; 뮽; 뮽; 뮽; ) HANGUL SYLLABLE MYUT
+BBBE;BBBE;1106 1172 11C1;BBBE;1106 1172 11C1; # (뮾; 뮾; 뮤á‡; 뮾; 뮤á‡; ) HANGUL SYLLABLE MYUP
+BBBF;BBBF;1106 1172 11C2;BBBF;1106 1172 11C2; # (뮿; 뮿; 뮿; 뮿; 뮿; ) HANGUL SYLLABLE MYUH
+BBC0;BBC0;1106 1173;BBC0;1106 1173; # (므; 므; 므; 므; 므; ) HANGUL SYLLABLE MEU
+BBC1;BBC1;1106 1173 11A8;BBC1;1106 1173 11A8; # (ë¯; ë¯; 믁; ë¯; 믁; ) HANGUL SYLLABLE MEUG
+BBC2;BBC2;1106 1173 11A9;BBC2;1106 1173 11A9; # (믂; 믂; 믂; 믂; 믂; ) HANGUL SYLLABLE MEUGG
+BBC3;BBC3;1106 1173 11AA;BBC3;1106 1173 11AA; # (믃; 믃; 믃; 믃; 믃; ) HANGUL SYLLABLE MEUGS
+BBC4;BBC4;1106 1173 11AB;BBC4;1106 1173 11AB; # (믄; 믄; 믄; 믄; 믄; ) HANGUL SYLLABLE MEUN
+BBC5;BBC5;1106 1173 11AC;BBC5;1106 1173 11AC; # (믅; 믅; 믅; 믅; 믅; ) HANGUL SYLLABLE MEUNJ
+BBC6;BBC6;1106 1173 11AD;BBC6;1106 1173 11AD; # (믆; 믆; 믆; 믆; 믆; ) HANGUL SYLLABLE MEUNH
+BBC7;BBC7;1106 1173 11AE;BBC7;1106 1173 11AE; # (믇; 믇; 믇; 믇; 믇; ) HANGUL SYLLABLE MEUD
+BBC8;BBC8;1106 1173 11AF;BBC8;1106 1173 11AF; # (믈; 믈; 믈; 믈; 믈; ) HANGUL SYLLABLE MEUL
+BBC9;BBC9;1106 1173 11B0;BBC9;1106 1173 11B0; # (믉; 믉; 믉; 믉; 믉; ) HANGUL SYLLABLE MEULG
+BBCA;BBCA;1106 1173 11B1;BBCA;1106 1173 11B1; # (믊; 믊; 믊; 믊; 믊; ) HANGUL SYLLABLE MEULM
+BBCB;BBCB;1106 1173 11B2;BBCB;1106 1173 11B2; # (믋; 믋; 믋; 믋; 믋; ) HANGUL SYLLABLE MEULB
+BBCC;BBCC;1106 1173 11B3;BBCC;1106 1173 11B3; # (믌; 믌; 믌; 믌; 믌; ) HANGUL SYLLABLE MEULS
+BBCD;BBCD;1106 1173 11B4;BBCD;1106 1173 11B4; # (ë¯; ë¯; 믍; ë¯; 믍; ) HANGUL SYLLABLE MEULT
+BBCE;BBCE;1106 1173 11B5;BBCE;1106 1173 11B5; # (믎; 믎; 믎; 믎; 믎; ) HANGUL SYLLABLE MEULP
+BBCF;BBCF;1106 1173 11B6;BBCF;1106 1173 11B6; # (ë¯; ë¯; 믏; ë¯; 믏; ) HANGUL SYLLABLE MEULH
+BBD0;BBD0;1106 1173 11B7;BBD0;1106 1173 11B7; # (ë¯; ë¯; 믐; ë¯; 믐; ) HANGUL SYLLABLE MEUM
+BBD1;BBD1;1106 1173 11B8;BBD1;1106 1173 11B8; # (믑; 믑; 믑; 믑; 믑; ) HANGUL SYLLABLE MEUB
+BBD2;BBD2;1106 1173 11B9;BBD2;1106 1173 11B9; # (믒; 믒; 믒; 믒; 믒; ) HANGUL SYLLABLE MEUBS
+BBD3;BBD3;1106 1173 11BA;BBD3;1106 1173 11BA; # (믓; 믓; 믓; 믓; 믓; ) HANGUL SYLLABLE MEUS
+BBD4;BBD4;1106 1173 11BB;BBD4;1106 1173 11BB; # (믔; 믔; 믔; 믔; 믔; ) HANGUL SYLLABLE MEUSS
+BBD5;BBD5;1106 1173 11BC;BBD5;1106 1173 11BC; # (믕; 믕; 믕; 믕; 믕; ) HANGUL SYLLABLE MEUNG
+BBD6;BBD6;1106 1173 11BD;BBD6;1106 1173 11BD; # (믖; 믖; 믖; 믖; 믖; ) HANGUL SYLLABLE MEUJ
+BBD7;BBD7;1106 1173 11BE;BBD7;1106 1173 11BE; # (믗; 믗; 믗; 믗; 믗; ) HANGUL SYLLABLE MEUC
+BBD8;BBD8;1106 1173 11BF;BBD8;1106 1173 11BF; # (믘; 믘; 믘; 믘; 믘; ) HANGUL SYLLABLE MEUK
+BBD9;BBD9;1106 1173 11C0;BBD9;1106 1173 11C0; # (믙; 믙; 믙; 믙; 믙; ) HANGUL SYLLABLE MEUT
+BBDA;BBDA;1106 1173 11C1;BBDA;1106 1173 11C1; # (믚; 믚; 므á‡; 믚; 므á‡; ) HANGUL SYLLABLE MEUP
+BBDB;BBDB;1106 1173 11C2;BBDB;1106 1173 11C2; # (믛; 믛; 믛; 믛; 믛; ) HANGUL SYLLABLE MEUH
+BBDC;BBDC;1106 1174;BBDC;1106 1174; # (믜; 믜; 믜; 믜; 믜; ) HANGUL SYLLABLE MYI
+BBDD;BBDD;1106 1174 11A8;BBDD;1106 1174 11A8; # (ë¯; ë¯; 믝; ë¯; 믝; ) HANGUL SYLLABLE MYIG
+BBDE;BBDE;1106 1174 11A9;BBDE;1106 1174 11A9; # (믞; 믞; 믞; 믞; 믞; ) HANGUL SYLLABLE MYIGG
+BBDF;BBDF;1106 1174 11AA;BBDF;1106 1174 11AA; # (믟; 믟; 믟; 믟; 믟; ) HANGUL SYLLABLE MYIGS
+BBE0;BBE0;1106 1174 11AB;BBE0;1106 1174 11AB; # (믠; 믠; 믠; 믠; 믠; ) HANGUL SYLLABLE MYIN
+BBE1;BBE1;1106 1174 11AC;BBE1;1106 1174 11AC; # (믡; 믡; 믡; 믡; 믡; ) HANGUL SYLLABLE MYINJ
+BBE2;BBE2;1106 1174 11AD;BBE2;1106 1174 11AD; # (믢; 믢; 믢; 믢; 믢; ) HANGUL SYLLABLE MYINH
+BBE3;BBE3;1106 1174 11AE;BBE3;1106 1174 11AE; # (믣; 믣; 믣; 믣; 믣; ) HANGUL SYLLABLE MYID
+BBE4;BBE4;1106 1174 11AF;BBE4;1106 1174 11AF; # (믤; 믤; 믤; 믤; 믤; ) HANGUL SYLLABLE MYIL
+BBE5;BBE5;1106 1174 11B0;BBE5;1106 1174 11B0; # (믥; 믥; 믥; 믥; 믥; ) HANGUL SYLLABLE MYILG
+BBE6;BBE6;1106 1174 11B1;BBE6;1106 1174 11B1; # (믦; 믦; 믦; 믦; 믦; ) HANGUL SYLLABLE MYILM
+BBE7;BBE7;1106 1174 11B2;BBE7;1106 1174 11B2; # (믧; 믧; 믧; 믧; 믧; ) HANGUL SYLLABLE MYILB
+BBE8;BBE8;1106 1174 11B3;BBE8;1106 1174 11B3; # (믨; 믨; 믨; 믨; 믨; ) HANGUL SYLLABLE MYILS
+BBE9;BBE9;1106 1174 11B4;BBE9;1106 1174 11B4; # (믩; 믩; 믩; 믩; 믩; ) HANGUL SYLLABLE MYILT
+BBEA;BBEA;1106 1174 11B5;BBEA;1106 1174 11B5; # (믪; 믪; 믪; 믪; 믪; ) HANGUL SYLLABLE MYILP
+BBEB;BBEB;1106 1174 11B6;BBEB;1106 1174 11B6; # (믫; 믫; 믫; 믫; 믫; ) HANGUL SYLLABLE MYILH
+BBEC;BBEC;1106 1174 11B7;BBEC;1106 1174 11B7; # (믬; 믬; 믬; 믬; 믬; ) HANGUL SYLLABLE MYIM
+BBED;BBED;1106 1174 11B8;BBED;1106 1174 11B8; # (믭; 믭; 믭; 믭; 믭; ) HANGUL SYLLABLE MYIB
+BBEE;BBEE;1106 1174 11B9;BBEE;1106 1174 11B9; # (믮; 믮; 믮; 믮; 믮; ) HANGUL SYLLABLE MYIBS
+BBEF;BBEF;1106 1174 11BA;BBEF;1106 1174 11BA; # (믯; 믯; 믯; 믯; 믯; ) HANGUL SYLLABLE MYIS
+BBF0;BBF0;1106 1174 11BB;BBF0;1106 1174 11BB; # (믰; 믰; 믰; 믰; 믰; ) HANGUL SYLLABLE MYISS
+BBF1;BBF1;1106 1174 11BC;BBF1;1106 1174 11BC; # (믱; 믱; 믱; 믱; 믱; ) HANGUL SYLLABLE MYING
+BBF2;BBF2;1106 1174 11BD;BBF2;1106 1174 11BD; # (믲; 믲; 믲; 믲; 믲; ) HANGUL SYLLABLE MYIJ
+BBF3;BBF3;1106 1174 11BE;BBF3;1106 1174 11BE; # (믳; 믳; 믳; 믳; 믳; ) HANGUL SYLLABLE MYIC
+BBF4;BBF4;1106 1174 11BF;BBF4;1106 1174 11BF; # (믴; 믴; 믴; 믴; 믴; ) HANGUL SYLLABLE MYIK
+BBF5;BBF5;1106 1174 11C0;BBF5;1106 1174 11C0; # (믵; 믵; 믵; 믵; 믵; ) HANGUL SYLLABLE MYIT
+BBF6;BBF6;1106 1174 11C1;BBF6;1106 1174 11C1; # (믶; 믶; 믜á‡; 믶; 믜á‡; ) HANGUL SYLLABLE MYIP
+BBF7;BBF7;1106 1174 11C2;BBF7;1106 1174 11C2; # (믷; 믷; 믷; 믷; 믷; ) HANGUL SYLLABLE MYIH
+BBF8;BBF8;1106 1175;BBF8;1106 1175; # (미; 미; 미; 미; 미; ) HANGUL SYLLABLE MI
+BBF9;BBF9;1106 1175 11A8;BBF9;1106 1175 11A8; # (믹; 믹; 믹; 믹; 믹; ) HANGUL SYLLABLE MIG
+BBFA;BBFA;1106 1175 11A9;BBFA;1106 1175 11A9; # (믺; 믺; 믺; 믺; 믺; ) HANGUL SYLLABLE MIGG
+BBFB;BBFB;1106 1175 11AA;BBFB;1106 1175 11AA; # (믻; 믻; 믻; 믻; 믻; ) HANGUL SYLLABLE MIGS
+BBFC;BBFC;1106 1175 11AB;BBFC;1106 1175 11AB; # (민; 민; 민; 민; 민; ) HANGUL SYLLABLE MIN
+BBFD;BBFD;1106 1175 11AC;BBFD;1106 1175 11AC; # (믽; 믽; 믽; 믽; 믽; ) HANGUL SYLLABLE MINJ
+BBFE;BBFE;1106 1175 11AD;BBFE;1106 1175 11AD; # (믾; 믾; 믾; 믾; 믾; ) HANGUL SYLLABLE MINH
+BBFF;BBFF;1106 1175 11AE;BBFF;1106 1175 11AE; # (믿; 믿; 믿; 믿; 믿; ) HANGUL SYLLABLE MID
+BC00;BC00;1106 1175 11AF;BC00;1106 1175 11AF; # (밀; 밀; 밀; 밀; 밀; ) HANGUL SYLLABLE MIL
+BC01;BC01;1106 1175 11B0;BC01;1106 1175 11B0; # (ë°; ë°; 밁; ë°; 밁; ) HANGUL SYLLABLE MILG
+BC02;BC02;1106 1175 11B1;BC02;1106 1175 11B1; # (밂; 밂; 밂; 밂; 밂; ) HANGUL SYLLABLE MILM
+BC03;BC03;1106 1175 11B2;BC03;1106 1175 11B2; # (밃; 밃; 밃; 밃; 밃; ) HANGUL SYLLABLE MILB
+BC04;BC04;1106 1175 11B3;BC04;1106 1175 11B3; # (밄; 밄; 밄; 밄; 밄; ) HANGUL SYLLABLE MILS
+BC05;BC05;1106 1175 11B4;BC05;1106 1175 11B4; # (밅; 밅; 밅; 밅; 밅; ) HANGUL SYLLABLE MILT
+BC06;BC06;1106 1175 11B5;BC06;1106 1175 11B5; # (밆; 밆; 밆; 밆; 밆; ) HANGUL SYLLABLE MILP
+BC07;BC07;1106 1175 11B6;BC07;1106 1175 11B6; # (밇; 밇; 밇; 밇; 밇; ) HANGUL SYLLABLE MILH
+BC08;BC08;1106 1175 11B7;BC08;1106 1175 11B7; # (밈; 밈; 밈; 밈; 밈; ) HANGUL SYLLABLE MIM
+BC09;BC09;1106 1175 11B8;BC09;1106 1175 11B8; # (밉; 밉; 밉; 밉; 밉; ) HANGUL SYLLABLE MIB
+BC0A;BC0A;1106 1175 11B9;BC0A;1106 1175 11B9; # (밊; 밊; 밊; 밊; 밊; ) HANGUL SYLLABLE MIBS
+BC0B;BC0B;1106 1175 11BA;BC0B;1106 1175 11BA; # (밋; 밋; 밋; 밋; 밋; ) HANGUL SYLLABLE MIS
+BC0C;BC0C;1106 1175 11BB;BC0C;1106 1175 11BB; # (밌; 밌; 밌; 밌; 밌; ) HANGUL SYLLABLE MISS
+BC0D;BC0D;1106 1175 11BC;BC0D;1106 1175 11BC; # (ë°; ë°; 밍; ë°; 밍; ) HANGUL SYLLABLE MING
+BC0E;BC0E;1106 1175 11BD;BC0E;1106 1175 11BD; # (밎; 밎; 밎; 밎; 밎; ) HANGUL SYLLABLE MIJ
+BC0F;BC0F;1106 1175 11BE;BC0F;1106 1175 11BE; # (ë°; ë°; 및; ë°; 및; ) HANGUL SYLLABLE MIC
+BC10;BC10;1106 1175 11BF;BC10;1106 1175 11BF; # (ë°; ë°; 밐; ë°; 밐; ) HANGUL SYLLABLE MIK
+BC11;BC11;1106 1175 11C0;BC11;1106 1175 11C0; # (밑; 밑; 밑; 밑; 밑; ) HANGUL SYLLABLE MIT
+BC12;BC12;1106 1175 11C1;BC12;1106 1175 11C1; # (ë°’; ë°’; 미á‡; ë°’; 미á‡; ) HANGUL SYLLABLE MIP
+BC13;BC13;1106 1175 11C2;BC13;1106 1175 11C2; # (밓; 밓; 밓; 밓; 밓; ) HANGUL SYLLABLE MIH
+BC14;BC14;1107 1161;BC14;1107 1161; # (바; 바; 바; 바; 바; ) HANGUL SYLLABLE BA
+BC15;BC15;1107 1161 11A8;BC15;1107 1161 11A8; # (박; 박; 박; 박; 박; ) HANGUL SYLLABLE BAG
+BC16;BC16;1107 1161 11A9;BC16;1107 1161 11A9; # (밖; 밖; 밖; 밖; 밖; ) HANGUL SYLLABLE BAGG
+BC17;BC17;1107 1161 11AA;BC17;1107 1161 11AA; # (밗; 밗; 밗; 밗; 밗; ) HANGUL SYLLABLE BAGS
+BC18;BC18;1107 1161 11AB;BC18;1107 1161 11AB; # (반; 반; 반; 반; 반; ) HANGUL SYLLABLE BAN
+BC19;BC19;1107 1161 11AC;BC19;1107 1161 11AC; # (밙; 밙; 밙; 밙; 밙; ) HANGUL SYLLABLE BANJ
+BC1A;BC1A;1107 1161 11AD;BC1A;1107 1161 11AD; # (밚; 밚; 밚; 밚; 밚; ) HANGUL SYLLABLE BANH
+BC1B;BC1B;1107 1161 11AE;BC1B;1107 1161 11AE; # (받; 받; 받; 받; 받; ) HANGUL SYLLABLE BAD
+BC1C;BC1C;1107 1161 11AF;BC1C;1107 1161 11AF; # (발; 발; 발; 발; 발; ) HANGUL SYLLABLE BAL
+BC1D;BC1D;1107 1161 11B0;BC1D;1107 1161 11B0; # (ë°; ë°; 밝; ë°; 밝; ) HANGUL SYLLABLE BALG
+BC1E;BC1E;1107 1161 11B1;BC1E;1107 1161 11B1; # (밞; 밞; 밞; 밞; 밞; ) HANGUL SYLLABLE BALM
+BC1F;BC1F;1107 1161 11B2;BC1F;1107 1161 11B2; # (밟; 밟; 밟; 밟; 밟; ) HANGUL SYLLABLE BALB
+BC20;BC20;1107 1161 11B3;BC20;1107 1161 11B3; # (밠; 밠; 밠; 밠; 밠; ) HANGUL SYLLABLE BALS
+BC21;BC21;1107 1161 11B4;BC21;1107 1161 11B4; # (밡; 밡; 밡; 밡; 밡; ) HANGUL SYLLABLE BALT
+BC22;BC22;1107 1161 11B5;BC22;1107 1161 11B5; # (밢; 밢; 밢; 밢; 밢; ) HANGUL SYLLABLE BALP
+BC23;BC23;1107 1161 11B6;BC23;1107 1161 11B6; # (밣; 밣; 밣; 밣; 밣; ) HANGUL SYLLABLE BALH
+BC24;BC24;1107 1161 11B7;BC24;1107 1161 11B7; # (밤; 밤; 밤; 밤; 밤; ) HANGUL SYLLABLE BAM
+BC25;BC25;1107 1161 11B8;BC25;1107 1161 11B8; # (밥; 밥; 밥; 밥; 밥; ) HANGUL SYLLABLE BAB
+BC26;BC26;1107 1161 11B9;BC26;1107 1161 11B9; # (밦; 밦; 밦; 밦; 밦; ) HANGUL SYLLABLE BABS
+BC27;BC27;1107 1161 11BA;BC27;1107 1161 11BA; # (밧; 밧; 밧; 밧; 밧; ) HANGUL SYLLABLE BAS
+BC28;BC28;1107 1161 11BB;BC28;1107 1161 11BB; # (밨; 밨; 밨; 밨; 밨; ) HANGUL SYLLABLE BASS
+BC29;BC29;1107 1161 11BC;BC29;1107 1161 11BC; # (방; 방; 방; 방; 방; ) HANGUL SYLLABLE BANG
+BC2A;BC2A;1107 1161 11BD;BC2A;1107 1161 11BD; # (밪; 밪; 밪; 밪; 밪; ) HANGUL SYLLABLE BAJ
+BC2B;BC2B;1107 1161 11BE;BC2B;1107 1161 11BE; # (밫; 밫; 밫; 밫; 밫; ) HANGUL SYLLABLE BAC
+BC2C;BC2C;1107 1161 11BF;BC2C;1107 1161 11BF; # (밬; 밬; 밬; 밬; 밬; ) HANGUL SYLLABLE BAK
+BC2D;BC2D;1107 1161 11C0;BC2D;1107 1161 11C0; # (밭; 밭; 밭; 밭; 밭; ) HANGUL SYLLABLE BAT
+BC2E;BC2E;1107 1161 11C1;BC2E;1107 1161 11C1; # (ë°®; ë°®; 바á‡; ë°®; 바á‡; ) HANGUL SYLLABLE BAP
+BC2F;BC2F;1107 1161 11C2;BC2F;1107 1161 11C2; # (밯; 밯; 밯; 밯; 밯; ) HANGUL SYLLABLE BAH
+BC30;BC30;1107 1162;BC30;1107 1162; # (배; 배; 배; 배; 배; ) HANGUL SYLLABLE BAE
+BC31;BC31;1107 1162 11A8;BC31;1107 1162 11A8; # (백; 백; 백; 백; 백; ) HANGUL SYLLABLE BAEG
+BC32;BC32;1107 1162 11A9;BC32;1107 1162 11A9; # (밲; 밲; 밲; 밲; 밲; ) HANGUL SYLLABLE BAEGG
+BC33;BC33;1107 1162 11AA;BC33;1107 1162 11AA; # (밳; 밳; 밳; 밳; 밳; ) HANGUL SYLLABLE BAEGS
+BC34;BC34;1107 1162 11AB;BC34;1107 1162 11AB; # (밴; 밴; 밴; 밴; 밴; ) HANGUL SYLLABLE BAEN
+BC35;BC35;1107 1162 11AC;BC35;1107 1162 11AC; # (밵; 밵; 밵; 밵; 밵; ) HANGUL SYLLABLE BAENJ
+BC36;BC36;1107 1162 11AD;BC36;1107 1162 11AD; # (밶; 밶; 밶; 밶; 밶; ) HANGUL SYLLABLE BAENH
+BC37;BC37;1107 1162 11AE;BC37;1107 1162 11AE; # (밷; 밷; 밷; 밷; 밷; ) HANGUL SYLLABLE BAED
+BC38;BC38;1107 1162 11AF;BC38;1107 1162 11AF; # (밸; 밸; 밸; 밸; 밸; ) HANGUL SYLLABLE BAEL
+BC39;BC39;1107 1162 11B0;BC39;1107 1162 11B0; # (밹; 밹; 밹; 밹; 밹; ) HANGUL SYLLABLE BAELG
+BC3A;BC3A;1107 1162 11B1;BC3A;1107 1162 11B1; # (밺; 밺; 밺; 밺; 밺; ) HANGUL SYLLABLE BAELM
+BC3B;BC3B;1107 1162 11B2;BC3B;1107 1162 11B2; # (밻; 밻; 밻; 밻; 밻; ) HANGUL SYLLABLE BAELB
+BC3C;BC3C;1107 1162 11B3;BC3C;1107 1162 11B3; # (밼; 밼; 밼; 밼; 밼; ) HANGUL SYLLABLE BAELS
+BC3D;BC3D;1107 1162 11B4;BC3D;1107 1162 11B4; # (밽; 밽; 밽; 밽; 밽; ) HANGUL SYLLABLE BAELT
+BC3E;BC3E;1107 1162 11B5;BC3E;1107 1162 11B5; # (밾; 밾; 밾; 밾; 밾; ) HANGUL SYLLABLE BAELP
+BC3F;BC3F;1107 1162 11B6;BC3F;1107 1162 11B6; # (밿; 밿; 밿; 밿; 밿; ) HANGUL SYLLABLE BAELH
+BC40;BC40;1107 1162 11B7;BC40;1107 1162 11B7; # (뱀; 뱀; 뱀; 뱀; 뱀; ) HANGUL SYLLABLE BAEM
+BC41;BC41;1107 1162 11B8;BC41;1107 1162 11B8; # (ë±; ë±; 뱁; ë±; 뱁; ) HANGUL SYLLABLE BAEB
+BC42;BC42;1107 1162 11B9;BC42;1107 1162 11B9; # (뱂; 뱂; 뱂; 뱂; 뱂; ) HANGUL SYLLABLE BAEBS
+BC43;BC43;1107 1162 11BA;BC43;1107 1162 11BA; # (뱃; 뱃; 뱃; 뱃; 뱃; ) HANGUL SYLLABLE BAES
+BC44;BC44;1107 1162 11BB;BC44;1107 1162 11BB; # (뱄; 뱄; 뱄; 뱄; 뱄; ) HANGUL SYLLABLE BAESS
+BC45;BC45;1107 1162 11BC;BC45;1107 1162 11BC; # (뱅; 뱅; 뱅; 뱅; 뱅; ) HANGUL SYLLABLE BAENG
+BC46;BC46;1107 1162 11BD;BC46;1107 1162 11BD; # (뱆; 뱆; 뱆; 뱆; 뱆; ) HANGUL SYLLABLE BAEJ
+BC47;BC47;1107 1162 11BE;BC47;1107 1162 11BE; # (뱇; 뱇; 뱇; 뱇; 뱇; ) HANGUL SYLLABLE BAEC
+BC48;BC48;1107 1162 11BF;BC48;1107 1162 11BF; # (뱈; 뱈; 뱈; 뱈; 뱈; ) HANGUL SYLLABLE BAEK
+BC49;BC49;1107 1162 11C0;BC49;1107 1162 11C0; # (뱉; 뱉; 뱉; 뱉; 뱉; ) HANGUL SYLLABLE BAET
+BC4A;BC4A;1107 1162 11C1;BC4A;1107 1162 11C1; # (뱊; 뱊; 배á‡; 뱊; 배á‡; ) HANGUL SYLLABLE BAEP
+BC4B;BC4B;1107 1162 11C2;BC4B;1107 1162 11C2; # (뱋; 뱋; 뱋; 뱋; 뱋; ) HANGUL SYLLABLE BAEH
+BC4C;BC4C;1107 1163;BC4C;1107 1163; # (뱌; 뱌; 뱌; 뱌; 뱌; ) HANGUL SYLLABLE BYA
+BC4D;BC4D;1107 1163 11A8;BC4D;1107 1163 11A8; # (ë±; ë±; 뱍; ë±; 뱍; ) HANGUL SYLLABLE BYAG
+BC4E;BC4E;1107 1163 11A9;BC4E;1107 1163 11A9; # (뱎; 뱎; 뱎; 뱎; 뱎; ) HANGUL SYLLABLE BYAGG
+BC4F;BC4F;1107 1163 11AA;BC4F;1107 1163 11AA; # (ë±; ë±; 뱏; ë±; 뱏; ) HANGUL SYLLABLE BYAGS
+BC50;BC50;1107 1163 11AB;BC50;1107 1163 11AB; # (ë±; ë±; 뱐; ë±; 뱐; ) HANGUL SYLLABLE BYAN
+BC51;BC51;1107 1163 11AC;BC51;1107 1163 11AC; # (뱑; 뱑; 뱑; 뱑; 뱑; ) HANGUL SYLLABLE BYANJ
+BC52;BC52;1107 1163 11AD;BC52;1107 1163 11AD; # (뱒; 뱒; 뱒; 뱒; 뱒; ) HANGUL SYLLABLE BYANH
+BC53;BC53;1107 1163 11AE;BC53;1107 1163 11AE; # (뱓; 뱓; 뱓; 뱓; 뱓; ) HANGUL SYLLABLE BYAD
+BC54;BC54;1107 1163 11AF;BC54;1107 1163 11AF; # (뱔; 뱔; 뱔; 뱔; 뱔; ) HANGUL SYLLABLE BYAL
+BC55;BC55;1107 1163 11B0;BC55;1107 1163 11B0; # (뱕; 뱕; 뱕; 뱕; 뱕; ) HANGUL SYLLABLE BYALG
+BC56;BC56;1107 1163 11B1;BC56;1107 1163 11B1; # (뱖; 뱖; 뱖; 뱖; 뱖; ) HANGUL SYLLABLE BYALM
+BC57;BC57;1107 1163 11B2;BC57;1107 1163 11B2; # (뱗; 뱗; 뱗; 뱗; 뱗; ) HANGUL SYLLABLE BYALB
+BC58;BC58;1107 1163 11B3;BC58;1107 1163 11B3; # (뱘; 뱘; 뱘; 뱘; 뱘; ) HANGUL SYLLABLE BYALS
+BC59;BC59;1107 1163 11B4;BC59;1107 1163 11B4; # (뱙; 뱙; 뱙; 뱙; 뱙; ) HANGUL SYLLABLE BYALT
+BC5A;BC5A;1107 1163 11B5;BC5A;1107 1163 11B5; # (뱚; 뱚; 뱚; 뱚; 뱚; ) HANGUL SYLLABLE BYALP
+BC5B;BC5B;1107 1163 11B6;BC5B;1107 1163 11B6; # (뱛; 뱛; 뱛; 뱛; 뱛; ) HANGUL SYLLABLE BYALH
+BC5C;BC5C;1107 1163 11B7;BC5C;1107 1163 11B7; # (뱜; 뱜; 뱜; 뱜; 뱜; ) HANGUL SYLLABLE BYAM
+BC5D;BC5D;1107 1163 11B8;BC5D;1107 1163 11B8; # (ë±; ë±; 뱝; ë±; 뱝; ) HANGUL SYLLABLE BYAB
+BC5E;BC5E;1107 1163 11B9;BC5E;1107 1163 11B9; # (뱞; 뱞; 뱞; 뱞; 뱞; ) HANGUL SYLLABLE BYABS
+BC5F;BC5F;1107 1163 11BA;BC5F;1107 1163 11BA; # (뱟; 뱟; 뱟; 뱟; 뱟; ) HANGUL SYLLABLE BYAS
+BC60;BC60;1107 1163 11BB;BC60;1107 1163 11BB; # (뱠; 뱠; 뱠; 뱠; 뱠; ) HANGUL SYLLABLE BYASS
+BC61;BC61;1107 1163 11BC;BC61;1107 1163 11BC; # (뱡; 뱡; 뱡; 뱡; 뱡; ) HANGUL SYLLABLE BYANG
+BC62;BC62;1107 1163 11BD;BC62;1107 1163 11BD; # (뱢; 뱢; 뱢; 뱢; 뱢; ) HANGUL SYLLABLE BYAJ
+BC63;BC63;1107 1163 11BE;BC63;1107 1163 11BE; # (뱣; 뱣; 뱣; 뱣; 뱣; ) HANGUL SYLLABLE BYAC
+BC64;BC64;1107 1163 11BF;BC64;1107 1163 11BF; # (뱤; 뱤; 뱤; 뱤; 뱤; ) HANGUL SYLLABLE BYAK
+BC65;BC65;1107 1163 11C0;BC65;1107 1163 11C0; # (뱥; 뱥; 뱥; 뱥; 뱥; ) HANGUL SYLLABLE BYAT
+BC66;BC66;1107 1163 11C1;BC66;1107 1163 11C1; # (뱦; 뱦; 뱌á‡; 뱦; 뱌á‡; ) HANGUL SYLLABLE BYAP
+BC67;BC67;1107 1163 11C2;BC67;1107 1163 11C2; # (뱧; 뱧; 뱧; 뱧; 뱧; ) HANGUL SYLLABLE BYAH
+BC68;BC68;1107 1164;BC68;1107 1164; # (뱨; 뱨; 뱨; 뱨; 뱨; ) HANGUL SYLLABLE BYAE
+BC69;BC69;1107 1164 11A8;BC69;1107 1164 11A8; # (뱩; 뱩; 뱩; 뱩; 뱩; ) HANGUL SYLLABLE BYAEG
+BC6A;BC6A;1107 1164 11A9;BC6A;1107 1164 11A9; # (뱪; 뱪; 뱪; 뱪; 뱪; ) HANGUL SYLLABLE BYAEGG
+BC6B;BC6B;1107 1164 11AA;BC6B;1107 1164 11AA; # (뱫; 뱫; 뱫; 뱫; 뱫; ) HANGUL SYLLABLE BYAEGS
+BC6C;BC6C;1107 1164 11AB;BC6C;1107 1164 11AB; # (뱬; 뱬; 뱬; 뱬; 뱬; ) HANGUL SYLLABLE BYAEN
+BC6D;BC6D;1107 1164 11AC;BC6D;1107 1164 11AC; # (뱭; 뱭; 뱭; 뱭; 뱭; ) HANGUL SYLLABLE BYAENJ
+BC6E;BC6E;1107 1164 11AD;BC6E;1107 1164 11AD; # (뱮; 뱮; 뱮; 뱮; 뱮; ) HANGUL SYLLABLE BYAENH
+BC6F;BC6F;1107 1164 11AE;BC6F;1107 1164 11AE; # (뱯; 뱯; 뱯; 뱯; 뱯; ) HANGUL SYLLABLE BYAED
+BC70;BC70;1107 1164 11AF;BC70;1107 1164 11AF; # (뱰; 뱰; 뱰; 뱰; 뱰; ) HANGUL SYLLABLE BYAEL
+BC71;BC71;1107 1164 11B0;BC71;1107 1164 11B0; # (뱱; 뱱; 뱱; 뱱; 뱱; ) HANGUL SYLLABLE BYAELG
+BC72;BC72;1107 1164 11B1;BC72;1107 1164 11B1; # (뱲; 뱲; 뱲; 뱲; 뱲; ) HANGUL SYLLABLE BYAELM
+BC73;BC73;1107 1164 11B2;BC73;1107 1164 11B2; # (뱳; 뱳; 뱳; 뱳; 뱳; ) HANGUL SYLLABLE BYAELB
+BC74;BC74;1107 1164 11B3;BC74;1107 1164 11B3; # (뱴; 뱴; 뱴; 뱴; 뱴; ) HANGUL SYLLABLE BYAELS
+BC75;BC75;1107 1164 11B4;BC75;1107 1164 11B4; # (뱵; 뱵; 뱵; 뱵; 뱵; ) HANGUL SYLLABLE BYAELT
+BC76;BC76;1107 1164 11B5;BC76;1107 1164 11B5; # (뱶; 뱶; 뱶; 뱶; 뱶; ) HANGUL SYLLABLE BYAELP
+BC77;BC77;1107 1164 11B6;BC77;1107 1164 11B6; # (뱷; 뱷; 뱷; 뱷; 뱷; ) HANGUL SYLLABLE BYAELH
+BC78;BC78;1107 1164 11B7;BC78;1107 1164 11B7; # (뱸; 뱸; 뱸; 뱸; 뱸; ) HANGUL SYLLABLE BYAEM
+BC79;BC79;1107 1164 11B8;BC79;1107 1164 11B8; # (뱹; 뱹; 뱹; 뱹; 뱹; ) HANGUL SYLLABLE BYAEB
+BC7A;BC7A;1107 1164 11B9;BC7A;1107 1164 11B9; # (뱺; 뱺; 뱺; 뱺; 뱺; ) HANGUL SYLLABLE BYAEBS
+BC7B;BC7B;1107 1164 11BA;BC7B;1107 1164 11BA; # (뱻; 뱻; 뱻; 뱻; 뱻; ) HANGUL SYLLABLE BYAES
+BC7C;BC7C;1107 1164 11BB;BC7C;1107 1164 11BB; # (뱼; 뱼; 뱼; 뱼; 뱼; ) HANGUL SYLLABLE BYAESS
+BC7D;BC7D;1107 1164 11BC;BC7D;1107 1164 11BC; # (뱽; 뱽; 뱽; 뱽; 뱽; ) HANGUL SYLLABLE BYAENG
+BC7E;BC7E;1107 1164 11BD;BC7E;1107 1164 11BD; # (뱾; 뱾; 뱾; 뱾; 뱾; ) HANGUL SYLLABLE BYAEJ
+BC7F;BC7F;1107 1164 11BE;BC7F;1107 1164 11BE; # (뱿; 뱿; 뱿; 뱿; 뱿; ) HANGUL SYLLABLE BYAEC
+BC80;BC80;1107 1164 11BF;BC80;1107 1164 11BF; # (벀; 벀; 벀; 벀; 벀; ) HANGUL SYLLABLE BYAEK
+BC81;BC81;1107 1164 11C0;BC81;1107 1164 11C0; # (ë²; ë²; 벁; ë²; 벁; ) HANGUL SYLLABLE BYAET
+BC82;BC82;1107 1164 11C1;BC82;1107 1164 11C1; # (벂; 벂; 뱨á‡; 벂; 뱨á‡; ) HANGUL SYLLABLE BYAEP
+BC83;BC83;1107 1164 11C2;BC83;1107 1164 11C2; # (벃; 벃; 벃; 벃; 벃; ) HANGUL SYLLABLE BYAEH
+BC84;BC84;1107 1165;BC84;1107 1165; # (버; 버; 버; 버; 버; ) HANGUL SYLLABLE BEO
+BC85;BC85;1107 1165 11A8;BC85;1107 1165 11A8; # (벅; 벅; 벅; 벅; 벅; ) HANGUL SYLLABLE BEOG
+BC86;BC86;1107 1165 11A9;BC86;1107 1165 11A9; # (벆; 벆; 벆; 벆; 벆; ) HANGUL SYLLABLE BEOGG
+BC87;BC87;1107 1165 11AA;BC87;1107 1165 11AA; # (벇; 벇; 벇; 벇; 벇; ) HANGUL SYLLABLE BEOGS
+BC88;BC88;1107 1165 11AB;BC88;1107 1165 11AB; # (번; 번; 번; 번; 번; ) HANGUL SYLLABLE BEON
+BC89;BC89;1107 1165 11AC;BC89;1107 1165 11AC; # (벉; 벉; 벉; 벉; 벉; ) HANGUL SYLLABLE BEONJ
+BC8A;BC8A;1107 1165 11AD;BC8A;1107 1165 11AD; # (벊; 벊; 벊; 벊; 벊; ) HANGUL SYLLABLE BEONH
+BC8B;BC8B;1107 1165 11AE;BC8B;1107 1165 11AE; # (벋; 벋; 벋; 벋; 벋; ) HANGUL SYLLABLE BEOD
+BC8C;BC8C;1107 1165 11AF;BC8C;1107 1165 11AF; # (벌; 벌; 벌; 벌; 벌; ) HANGUL SYLLABLE BEOL
+BC8D;BC8D;1107 1165 11B0;BC8D;1107 1165 11B0; # (ë²; ë²; 벍; ë²; 벍; ) HANGUL SYLLABLE BEOLG
+BC8E;BC8E;1107 1165 11B1;BC8E;1107 1165 11B1; # (벎; 벎; 벎; 벎; 벎; ) HANGUL SYLLABLE BEOLM
+BC8F;BC8F;1107 1165 11B2;BC8F;1107 1165 11B2; # (ë²; ë²; 벏; ë²; 벏; ) HANGUL SYLLABLE BEOLB
+BC90;BC90;1107 1165 11B3;BC90;1107 1165 11B3; # (ë²; ë²; 벐; ë²; 벐; ) HANGUL SYLLABLE BEOLS
+BC91;BC91;1107 1165 11B4;BC91;1107 1165 11B4; # (벑; 벑; 벑; 벑; 벑; ) HANGUL SYLLABLE BEOLT
+BC92;BC92;1107 1165 11B5;BC92;1107 1165 11B5; # (벒; 벒; 벒; 벒; 벒; ) HANGUL SYLLABLE BEOLP
+BC93;BC93;1107 1165 11B6;BC93;1107 1165 11B6; # (벓; 벓; 벓; 벓; 벓; ) HANGUL SYLLABLE BEOLH
+BC94;BC94;1107 1165 11B7;BC94;1107 1165 11B7; # (범; 범; 범; 범; 범; ) HANGUL SYLLABLE BEOM
+BC95;BC95;1107 1165 11B8;BC95;1107 1165 11B8; # (법; 법; 법; 법; 법; ) HANGUL SYLLABLE BEOB
+BC96;BC96;1107 1165 11B9;BC96;1107 1165 11B9; # (벖; 벖; 벖; 벖; 벖; ) HANGUL SYLLABLE BEOBS
+BC97;BC97;1107 1165 11BA;BC97;1107 1165 11BA; # (벗; 벗; 벗; 벗; 벗; ) HANGUL SYLLABLE BEOS
+BC98;BC98;1107 1165 11BB;BC98;1107 1165 11BB; # (벘; 벘; 벘; 벘; 벘; ) HANGUL SYLLABLE BEOSS
+BC99;BC99;1107 1165 11BC;BC99;1107 1165 11BC; # (벙; 벙; 벙; 벙; 벙; ) HANGUL SYLLABLE BEONG
+BC9A;BC9A;1107 1165 11BD;BC9A;1107 1165 11BD; # (벚; 벚; 벚; 벚; 벚; ) HANGUL SYLLABLE BEOJ
+BC9B;BC9B;1107 1165 11BE;BC9B;1107 1165 11BE; # (벛; 벛; 벛; 벛; 벛; ) HANGUL SYLLABLE BEOC
+BC9C;BC9C;1107 1165 11BF;BC9C;1107 1165 11BF; # (벜; 벜; 벜; 벜; 벜; ) HANGUL SYLLABLE BEOK
+BC9D;BC9D;1107 1165 11C0;BC9D;1107 1165 11C0; # (ë²; ë²; 벝; ë²; 벝; ) HANGUL SYLLABLE BEOT
+BC9E;BC9E;1107 1165 11C1;BC9E;1107 1165 11C1; # (벞; 벞; 버á‡; 벞; 버á‡; ) HANGUL SYLLABLE BEOP
+BC9F;BC9F;1107 1165 11C2;BC9F;1107 1165 11C2; # (벟; 벟; 벟; 벟; 벟; ) HANGUL SYLLABLE BEOH
+BCA0;BCA0;1107 1166;BCA0;1107 1166; # (베; 베; 베; 베; 베; ) HANGUL SYLLABLE BE
+BCA1;BCA1;1107 1166 11A8;BCA1;1107 1166 11A8; # (벡; 벡; 벡; 벡; 벡; ) HANGUL SYLLABLE BEG
+BCA2;BCA2;1107 1166 11A9;BCA2;1107 1166 11A9; # (벢; 벢; 벢; 벢; 벢; ) HANGUL SYLLABLE BEGG
+BCA3;BCA3;1107 1166 11AA;BCA3;1107 1166 11AA; # (벣; 벣; 벣; 벣; 벣; ) HANGUL SYLLABLE BEGS
+BCA4;BCA4;1107 1166 11AB;BCA4;1107 1166 11AB; # (벤; 벤; 벤; 벤; 벤; ) HANGUL SYLLABLE BEN
+BCA5;BCA5;1107 1166 11AC;BCA5;1107 1166 11AC; # (벥; 벥; 벥; 벥; 벥; ) HANGUL SYLLABLE BENJ
+BCA6;BCA6;1107 1166 11AD;BCA6;1107 1166 11AD; # (벦; 벦; 벦; 벦; 벦; ) HANGUL SYLLABLE BENH
+BCA7;BCA7;1107 1166 11AE;BCA7;1107 1166 11AE; # (벧; 벧; 벧; 벧; 벧; ) HANGUL SYLLABLE BED
+BCA8;BCA8;1107 1166 11AF;BCA8;1107 1166 11AF; # (벨; 벨; 벨; 벨; 벨; ) HANGUL SYLLABLE BEL
+BCA9;BCA9;1107 1166 11B0;BCA9;1107 1166 11B0; # (벩; 벩; 벩; 벩; 벩; ) HANGUL SYLLABLE BELG
+BCAA;BCAA;1107 1166 11B1;BCAA;1107 1166 11B1; # (벪; 벪; 벪; 벪; 벪; ) HANGUL SYLLABLE BELM
+BCAB;BCAB;1107 1166 11B2;BCAB;1107 1166 11B2; # (벫; 벫; 벫; 벫; 벫; ) HANGUL SYLLABLE BELB
+BCAC;BCAC;1107 1166 11B3;BCAC;1107 1166 11B3; # (벬; 벬; 벬; 벬; 벬; ) HANGUL SYLLABLE BELS
+BCAD;BCAD;1107 1166 11B4;BCAD;1107 1166 11B4; # (벭; 벭; 벭; 벭; 벭; ) HANGUL SYLLABLE BELT
+BCAE;BCAE;1107 1166 11B5;BCAE;1107 1166 11B5; # (벮; 벮; 벮; 벮; 벮; ) HANGUL SYLLABLE BELP
+BCAF;BCAF;1107 1166 11B6;BCAF;1107 1166 11B6; # (벯; 벯; 벯; 벯; 벯; ) HANGUL SYLLABLE BELH
+BCB0;BCB0;1107 1166 11B7;BCB0;1107 1166 11B7; # (벰; 벰; 벰; 벰; 벰; ) HANGUL SYLLABLE BEM
+BCB1;BCB1;1107 1166 11B8;BCB1;1107 1166 11B8; # (벱; 벱; 벱; 벱; 벱; ) HANGUL SYLLABLE BEB
+BCB2;BCB2;1107 1166 11B9;BCB2;1107 1166 11B9; # (벲; 벲; 벲; 벲; 벲; ) HANGUL SYLLABLE BEBS
+BCB3;BCB3;1107 1166 11BA;BCB3;1107 1166 11BA; # (벳; 벳; 벳; 벳; 벳; ) HANGUL SYLLABLE BES
+BCB4;BCB4;1107 1166 11BB;BCB4;1107 1166 11BB; # (벴; 벴; 벴; 벴; 벴; ) HANGUL SYLLABLE BESS
+BCB5;BCB5;1107 1166 11BC;BCB5;1107 1166 11BC; # (벵; 벵; 벵; 벵; 벵; ) HANGUL SYLLABLE BENG
+BCB6;BCB6;1107 1166 11BD;BCB6;1107 1166 11BD; # (벶; 벶; 벶; 벶; 벶; ) HANGUL SYLLABLE BEJ
+BCB7;BCB7;1107 1166 11BE;BCB7;1107 1166 11BE; # (벷; 벷; 벷; 벷; 벷; ) HANGUL SYLLABLE BEC
+BCB8;BCB8;1107 1166 11BF;BCB8;1107 1166 11BF; # (벸; 벸; 벸; 벸; 벸; ) HANGUL SYLLABLE BEK
+BCB9;BCB9;1107 1166 11C0;BCB9;1107 1166 11C0; # (벹; 벹; 벹; 벹; 벹; ) HANGUL SYLLABLE BET
+BCBA;BCBA;1107 1166 11C1;BCBA;1107 1166 11C1; # (벺; 벺; 베á‡; 벺; 베á‡; ) HANGUL SYLLABLE BEP
+BCBB;BCBB;1107 1166 11C2;BCBB;1107 1166 11C2; # (벻; 벻; 벻; 벻; 벻; ) HANGUL SYLLABLE BEH
+BCBC;BCBC;1107 1167;BCBC;1107 1167; # (벼; 벼; 벼; 벼; 벼; ) HANGUL SYLLABLE BYEO
+BCBD;BCBD;1107 1167 11A8;BCBD;1107 1167 11A8; # (벽; 벽; 벽; 벽; 벽; ) HANGUL SYLLABLE BYEOG
+BCBE;BCBE;1107 1167 11A9;BCBE;1107 1167 11A9; # (벾; 벾; 벾; 벾; 벾; ) HANGUL SYLLABLE BYEOGG
+BCBF;BCBF;1107 1167 11AA;BCBF;1107 1167 11AA; # (벿; 벿; 벿; 벿; 벿; ) HANGUL SYLLABLE BYEOGS
+BCC0;BCC0;1107 1167 11AB;BCC0;1107 1167 11AB; # (변; 변; 변; 변; 변; ) HANGUL SYLLABLE BYEON
+BCC1;BCC1;1107 1167 11AC;BCC1;1107 1167 11AC; # (ë³; ë³; 볁; ë³; 볁; ) HANGUL SYLLABLE BYEONJ
+BCC2;BCC2;1107 1167 11AD;BCC2;1107 1167 11AD; # (볂; 볂; 볂; 볂; 볂; ) HANGUL SYLLABLE BYEONH
+BCC3;BCC3;1107 1167 11AE;BCC3;1107 1167 11AE; # (볃; 볃; 볃; 볃; 볃; ) HANGUL SYLLABLE BYEOD
+BCC4;BCC4;1107 1167 11AF;BCC4;1107 1167 11AF; # (별; 별; 별; 별; 별; ) HANGUL SYLLABLE BYEOL
+BCC5;BCC5;1107 1167 11B0;BCC5;1107 1167 11B0; # (볅; 볅; 볅; 볅; 볅; ) HANGUL SYLLABLE BYEOLG
+BCC6;BCC6;1107 1167 11B1;BCC6;1107 1167 11B1; # (볆; 볆; 볆; 볆; 볆; ) HANGUL SYLLABLE BYEOLM
+BCC7;BCC7;1107 1167 11B2;BCC7;1107 1167 11B2; # (볇; 볇; 볇; 볇; 볇; ) HANGUL SYLLABLE BYEOLB
+BCC8;BCC8;1107 1167 11B3;BCC8;1107 1167 11B3; # (볈; 볈; 볈; 볈; 볈; ) HANGUL SYLLABLE BYEOLS
+BCC9;BCC9;1107 1167 11B4;BCC9;1107 1167 11B4; # (볉; 볉; 볉; 볉; 볉; ) HANGUL SYLLABLE BYEOLT
+BCCA;BCCA;1107 1167 11B5;BCCA;1107 1167 11B5; # (볊; 볊; 볊; 볊; 볊; ) HANGUL SYLLABLE BYEOLP
+BCCB;BCCB;1107 1167 11B6;BCCB;1107 1167 11B6; # (볋; 볋; 볋; 볋; 볋; ) HANGUL SYLLABLE BYEOLH
+BCCC;BCCC;1107 1167 11B7;BCCC;1107 1167 11B7; # (볌; 볌; 볌; 볌; 볌; ) HANGUL SYLLABLE BYEOM
+BCCD;BCCD;1107 1167 11B8;BCCD;1107 1167 11B8; # (ë³; ë³; 볍; ë³; 볍; ) HANGUL SYLLABLE BYEOB
+BCCE;BCCE;1107 1167 11B9;BCCE;1107 1167 11B9; # (볎; 볎; 볎; 볎; 볎; ) HANGUL SYLLABLE BYEOBS
+BCCF;BCCF;1107 1167 11BA;BCCF;1107 1167 11BA; # (ë³; ë³; 볏; ë³; 볏; ) HANGUL SYLLABLE BYEOS
+BCD0;BCD0;1107 1167 11BB;BCD0;1107 1167 11BB; # (ë³; ë³; 볐; ë³; 볐; ) HANGUL SYLLABLE BYEOSS
+BCD1;BCD1;1107 1167 11BC;BCD1;1107 1167 11BC; # (병; 병; 병; 병; 병; ) HANGUL SYLLABLE BYEONG
+BCD2;BCD2;1107 1167 11BD;BCD2;1107 1167 11BD; # (볒; 볒; 볒; 볒; 볒; ) HANGUL SYLLABLE BYEOJ
+BCD3;BCD3;1107 1167 11BE;BCD3;1107 1167 11BE; # (볓; 볓; 볓; 볓; 볓; ) HANGUL SYLLABLE BYEOC
+BCD4;BCD4;1107 1167 11BF;BCD4;1107 1167 11BF; # (볔; 볔; 볔; 볔; 볔; ) HANGUL SYLLABLE BYEOK
+BCD5;BCD5;1107 1167 11C0;BCD5;1107 1167 11C0; # (볕; 볕; 볕; 볕; 볕; ) HANGUL SYLLABLE BYEOT
+BCD6;BCD6;1107 1167 11C1;BCD6;1107 1167 11C1; # (ë³–; ë³–; 벼á‡; ë³–; 벼á‡; ) HANGUL SYLLABLE BYEOP
+BCD7;BCD7;1107 1167 11C2;BCD7;1107 1167 11C2; # (볗; 볗; 볗; 볗; 볗; ) HANGUL SYLLABLE BYEOH
+BCD8;BCD8;1107 1168;BCD8;1107 1168; # (볘; 볘; 볘; 볘; 볘; ) HANGUL SYLLABLE BYE
+BCD9;BCD9;1107 1168 11A8;BCD9;1107 1168 11A8; # (볙; 볙; 볙; 볙; 볙; ) HANGUL SYLLABLE BYEG
+BCDA;BCDA;1107 1168 11A9;BCDA;1107 1168 11A9; # (볚; 볚; 볚; 볚; 볚; ) HANGUL SYLLABLE BYEGG
+BCDB;BCDB;1107 1168 11AA;BCDB;1107 1168 11AA; # (볛; 볛; 볛; 볛; 볛; ) HANGUL SYLLABLE BYEGS
+BCDC;BCDC;1107 1168 11AB;BCDC;1107 1168 11AB; # (볜; 볜; 볜; 볜; 볜; ) HANGUL SYLLABLE BYEN
+BCDD;BCDD;1107 1168 11AC;BCDD;1107 1168 11AC; # (ë³; ë³; 볝; ë³; 볝; ) HANGUL SYLLABLE BYENJ
+BCDE;BCDE;1107 1168 11AD;BCDE;1107 1168 11AD; # (볞; 볞; 볞; 볞; 볞; ) HANGUL SYLLABLE BYENH
+BCDF;BCDF;1107 1168 11AE;BCDF;1107 1168 11AE; # (볟; 볟; 볟; 볟; 볟; ) HANGUL SYLLABLE BYED
+BCE0;BCE0;1107 1168 11AF;BCE0;1107 1168 11AF; # (볠; 볠; 볠; 볠; 볠; ) HANGUL SYLLABLE BYEL
+BCE1;BCE1;1107 1168 11B0;BCE1;1107 1168 11B0; # (볡; 볡; 볡; 볡; 볡; ) HANGUL SYLLABLE BYELG
+BCE2;BCE2;1107 1168 11B1;BCE2;1107 1168 11B1; # (볢; 볢; 볢; 볢; 볢; ) HANGUL SYLLABLE BYELM
+BCE3;BCE3;1107 1168 11B2;BCE3;1107 1168 11B2; # (볣; 볣; 볣; 볣; 볣; ) HANGUL SYLLABLE BYELB
+BCE4;BCE4;1107 1168 11B3;BCE4;1107 1168 11B3; # (볤; 볤; 볤; 볤; 볤; ) HANGUL SYLLABLE BYELS
+BCE5;BCE5;1107 1168 11B4;BCE5;1107 1168 11B4; # (볥; 볥; 볥; 볥; 볥; ) HANGUL SYLLABLE BYELT
+BCE6;BCE6;1107 1168 11B5;BCE6;1107 1168 11B5; # (볦; 볦; 볦; 볦; 볦; ) HANGUL SYLLABLE BYELP
+BCE7;BCE7;1107 1168 11B6;BCE7;1107 1168 11B6; # (볧; 볧; 볧; 볧; 볧; ) HANGUL SYLLABLE BYELH
+BCE8;BCE8;1107 1168 11B7;BCE8;1107 1168 11B7; # (볨; 볨; 볨; 볨; 볨; ) HANGUL SYLLABLE BYEM
+BCE9;BCE9;1107 1168 11B8;BCE9;1107 1168 11B8; # (볩; 볩; 볩; 볩; 볩; ) HANGUL SYLLABLE BYEB
+BCEA;BCEA;1107 1168 11B9;BCEA;1107 1168 11B9; # (볪; 볪; 볪; 볪; 볪; ) HANGUL SYLLABLE BYEBS
+BCEB;BCEB;1107 1168 11BA;BCEB;1107 1168 11BA; # (볫; 볫; 볫; 볫; 볫; ) HANGUL SYLLABLE BYES
+BCEC;BCEC;1107 1168 11BB;BCEC;1107 1168 11BB; # (볬; 볬; 볬; 볬; 볬; ) HANGUL SYLLABLE BYESS
+BCED;BCED;1107 1168 11BC;BCED;1107 1168 11BC; # (볭; 볭; 볭; 볭; 볭; ) HANGUL SYLLABLE BYENG
+BCEE;BCEE;1107 1168 11BD;BCEE;1107 1168 11BD; # (볮; 볮; 볮; 볮; 볮; ) HANGUL SYLLABLE BYEJ
+BCEF;BCEF;1107 1168 11BE;BCEF;1107 1168 11BE; # (볯; 볯; 볯; 볯; 볯; ) HANGUL SYLLABLE BYEC
+BCF0;BCF0;1107 1168 11BF;BCF0;1107 1168 11BF; # (볰; 볰; 볰; 볰; 볰; ) HANGUL SYLLABLE BYEK
+BCF1;BCF1;1107 1168 11C0;BCF1;1107 1168 11C0; # (볱; 볱; 볱; 볱; 볱; ) HANGUL SYLLABLE BYET
+BCF2;BCF2;1107 1168 11C1;BCF2;1107 1168 11C1; # (ë³²; ë³²; 볘á‡; ë³²; 볘á‡; ) HANGUL SYLLABLE BYEP
+BCF3;BCF3;1107 1168 11C2;BCF3;1107 1168 11C2; # (볳; 볳; 볳; 볳; 볳; ) HANGUL SYLLABLE BYEH
+BCF4;BCF4;1107 1169;BCF4;1107 1169; # (보; 보; 보; 보; 보; ) HANGUL SYLLABLE BO
+BCF5;BCF5;1107 1169 11A8;BCF5;1107 1169 11A8; # (복; 복; 복; 복; 복; ) HANGUL SYLLABLE BOG
+BCF6;BCF6;1107 1169 11A9;BCF6;1107 1169 11A9; # (볶; 볶; 볶; 볶; 볶; ) HANGUL SYLLABLE BOGG
+BCF7;BCF7;1107 1169 11AA;BCF7;1107 1169 11AA; # (볷; 볷; 볷; 볷; 볷; ) HANGUL SYLLABLE BOGS
+BCF8;BCF8;1107 1169 11AB;BCF8;1107 1169 11AB; # (본; 본; 본; 본; 본; ) HANGUL SYLLABLE BON
+BCF9;BCF9;1107 1169 11AC;BCF9;1107 1169 11AC; # (볹; 볹; 볹; 볹; 볹; ) HANGUL SYLLABLE BONJ
+BCFA;BCFA;1107 1169 11AD;BCFA;1107 1169 11AD; # (볺; 볺; 볺; 볺; 볺; ) HANGUL SYLLABLE BONH
+BCFB;BCFB;1107 1169 11AE;BCFB;1107 1169 11AE; # (볻; 볻; 볻; 볻; 볻; ) HANGUL SYLLABLE BOD
+BCFC;BCFC;1107 1169 11AF;BCFC;1107 1169 11AF; # (볼; 볼; 볼; 볼; 볼; ) HANGUL SYLLABLE BOL
+BCFD;BCFD;1107 1169 11B0;BCFD;1107 1169 11B0; # (볽; 볽; 볽; 볽; 볽; ) HANGUL SYLLABLE BOLG
+BCFE;BCFE;1107 1169 11B1;BCFE;1107 1169 11B1; # (볾; 볾; 볾; 볾; 볾; ) HANGUL SYLLABLE BOLM
+BCFF;BCFF;1107 1169 11B2;BCFF;1107 1169 11B2; # (볿; 볿; 볿; 볿; 볿; ) HANGUL SYLLABLE BOLB
+BD00;BD00;1107 1169 11B3;BD00;1107 1169 11B3; # (봀; 봀; 봀; 봀; 봀; ) HANGUL SYLLABLE BOLS
+BD01;BD01;1107 1169 11B4;BD01;1107 1169 11B4; # (ë´; ë´; 봁; ë´; 봁; ) HANGUL SYLLABLE BOLT
+BD02;BD02;1107 1169 11B5;BD02;1107 1169 11B5; # (봂; 봂; 봂; 봂; 봂; ) HANGUL SYLLABLE BOLP
+BD03;BD03;1107 1169 11B6;BD03;1107 1169 11B6; # (봃; 봃; 봃; 봃; 봃; ) HANGUL SYLLABLE BOLH
+BD04;BD04;1107 1169 11B7;BD04;1107 1169 11B7; # (봄; 봄; 봄; 봄; 봄; ) HANGUL SYLLABLE BOM
+BD05;BD05;1107 1169 11B8;BD05;1107 1169 11B8; # (봅; 봅; 봅; 봅; 봅; ) HANGUL SYLLABLE BOB
+BD06;BD06;1107 1169 11B9;BD06;1107 1169 11B9; # (봆; 봆; 봆; 봆; 봆; ) HANGUL SYLLABLE BOBS
+BD07;BD07;1107 1169 11BA;BD07;1107 1169 11BA; # (봇; 봇; 봇; 봇; 봇; ) HANGUL SYLLABLE BOS
+BD08;BD08;1107 1169 11BB;BD08;1107 1169 11BB; # (봈; 봈; 봈; 봈; 봈; ) HANGUL SYLLABLE BOSS
+BD09;BD09;1107 1169 11BC;BD09;1107 1169 11BC; # (봉; 봉; 봉; 봉; 봉; ) HANGUL SYLLABLE BONG
+BD0A;BD0A;1107 1169 11BD;BD0A;1107 1169 11BD; # (봊; 봊; 봊; 봊; 봊; ) HANGUL SYLLABLE BOJ
+BD0B;BD0B;1107 1169 11BE;BD0B;1107 1169 11BE; # (봋; 봋; 봋; 봋; 봋; ) HANGUL SYLLABLE BOC
+BD0C;BD0C;1107 1169 11BF;BD0C;1107 1169 11BF; # (봌; 봌; 봌; 봌; 봌; ) HANGUL SYLLABLE BOK
+BD0D;BD0D;1107 1169 11C0;BD0D;1107 1169 11C0; # (ë´; ë´; 봍; ë´; 봍; ) HANGUL SYLLABLE BOT
+BD0E;BD0E;1107 1169 11C1;BD0E;1107 1169 11C1; # (ë´Ž; ë´Ž; 보á‡; ë´Ž; 보á‡; ) HANGUL SYLLABLE BOP
+BD0F;BD0F;1107 1169 11C2;BD0F;1107 1169 11C2; # (ë´; ë´; 봏; ë´; 봏; ) HANGUL SYLLABLE BOH
+BD10;BD10;1107 116A;BD10;1107 116A; # (ë´; ë´; 봐; ë´; 봐; ) HANGUL SYLLABLE BWA
+BD11;BD11;1107 116A 11A8;BD11;1107 116A 11A8; # (봑; 봑; 봑; 봑; 봑; ) HANGUL SYLLABLE BWAG
+BD12;BD12;1107 116A 11A9;BD12;1107 116A 11A9; # (봒; 봒; 봒; 봒; 봒; ) HANGUL SYLLABLE BWAGG
+BD13;BD13;1107 116A 11AA;BD13;1107 116A 11AA; # (봓; 봓; 봓; 봓; 봓; ) HANGUL SYLLABLE BWAGS
+BD14;BD14;1107 116A 11AB;BD14;1107 116A 11AB; # (봔; 봔; 봔; 봔; 봔; ) HANGUL SYLLABLE BWAN
+BD15;BD15;1107 116A 11AC;BD15;1107 116A 11AC; # (봕; 봕; 봕; 봕; 봕; ) HANGUL SYLLABLE BWANJ
+BD16;BD16;1107 116A 11AD;BD16;1107 116A 11AD; # (봖; 봖; 봖; 봖; 봖; ) HANGUL SYLLABLE BWANH
+BD17;BD17;1107 116A 11AE;BD17;1107 116A 11AE; # (봗; 봗; 봗; 봗; 봗; ) HANGUL SYLLABLE BWAD
+BD18;BD18;1107 116A 11AF;BD18;1107 116A 11AF; # (봘; 봘; 봘; 봘; 봘; ) HANGUL SYLLABLE BWAL
+BD19;BD19;1107 116A 11B0;BD19;1107 116A 11B0; # (봙; 봙; 봙; 봙; 봙; ) HANGUL SYLLABLE BWALG
+BD1A;BD1A;1107 116A 11B1;BD1A;1107 116A 11B1; # (봚; 봚; 봚; 봚; 봚; ) HANGUL SYLLABLE BWALM
+BD1B;BD1B;1107 116A 11B2;BD1B;1107 116A 11B2; # (봛; 봛; 봛; 봛; 봛; ) HANGUL SYLLABLE BWALB
+BD1C;BD1C;1107 116A 11B3;BD1C;1107 116A 11B3; # (봜; 봜; 봜; 봜; 봜; ) HANGUL SYLLABLE BWALS
+BD1D;BD1D;1107 116A 11B4;BD1D;1107 116A 11B4; # (ë´; ë´; 봝; ë´; 봝; ) HANGUL SYLLABLE BWALT
+BD1E;BD1E;1107 116A 11B5;BD1E;1107 116A 11B5; # (봞; 봞; 봞; 봞; 봞; ) HANGUL SYLLABLE BWALP
+BD1F;BD1F;1107 116A 11B6;BD1F;1107 116A 11B6; # (봟; 봟; 봟; 봟; 봟; ) HANGUL SYLLABLE BWALH
+BD20;BD20;1107 116A 11B7;BD20;1107 116A 11B7; # (봠; 봠; 봠; 봠; 봠; ) HANGUL SYLLABLE BWAM
+BD21;BD21;1107 116A 11B8;BD21;1107 116A 11B8; # (봡; 봡; 봡; 봡; 봡; ) HANGUL SYLLABLE BWAB
+BD22;BD22;1107 116A 11B9;BD22;1107 116A 11B9; # (봢; 봢; 봢; 봢; 봢; ) HANGUL SYLLABLE BWABS
+BD23;BD23;1107 116A 11BA;BD23;1107 116A 11BA; # (봣; 봣; 봣; 봣; 봣; ) HANGUL SYLLABLE BWAS
+BD24;BD24;1107 116A 11BB;BD24;1107 116A 11BB; # (봤; 봤; 봤; 봤; 봤; ) HANGUL SYLLABLE BWASS
+BD25;BD25;1107 116A 11BC;BD25;1107 116A 11BC; # (봥; 봥; 봥; 봥; 봥; ) HANGUL SYLLABLE BWANG
+BD26;BD26;1107 116A 11BD;BD26;1107 116A 11BD; # (봦; 봦; 봦; 봦; 봦; ) HANGUL SYLLABLE BWAJ
+BD27;BD27;1107 116A 11BE;BD27;1107 116A 11BE; # (봧; 봧; 봧; 봧; 봧; ) HANGUL SYLLABLE BWAC
+BD28;BD28;1107 116A 11BF;BD28;1107 116A 11BF; # (봨; 봨; 봨; 봨; 봨; ) HANGUL SYLLABLE BWAK
+BD29;BD29;1107 116A 11C0;BD29;1107 116A 11C0; # (봩; 봩; 봩; 봩; 봩; ) HANGUL SYLLABLE BWAT
+BD2A;BD2A;1107 116A 11C1;BD2A;1107 116A 11C1; # (ë´ª; ë´ª; 봐á‡; ë´ª; 봐á‡; ) HANGUL SYLLABLE BWAP
+BD2B;BD2B;1107 116A 11C2;BD2B;1107 116A 11C2; # (봫; 봫; 봫; 봫; 봫; ) HANGUL SYLLABLE BWAH
+BD2C;BD2C;1107 116B;BD2C;1107 116B; # (봬; 봬; 봬; 봬; 봬; ) HANGUL SYLLABLE BWAE
+BD2D;BD2D;1107 116B 11A8;BD2D;1107 116B 11A8; # (봭; 봭; 봭; 봭; 봭; ) HANGUL SYLLABLE BWAEG
+BD2E;BD2E;1107 116B 11A9;BD2E;1107 116B 11A9; # (봮; 봮; 봮; 봮; 봮; ) HANGUL SYLLABLE BWAEGG
+BD2F;BD2F;1107 116B 11AA;BD2F;1107 116B 11AA; # (봯; 봯; 봯; 봯; 봯; ) HANGUL SYLLABLE BWAEGS
+BD30;BD30;1107 116B 11AB;BD30;1107 116B 11AB; # (봰; 봰; 봰; 봰; 봰; ) HANGUL SYLLABLE BWAEN
+BD31;BD31;1107 116B 11AC;BD31;1107 116B 11AC; # (봱; 봱; 봱; 봱; 봱; ) HANGUL SYLLABLE BWAENJ
+BD32;BD32;1107 116B 11AD;BD32;1107 116B 11AD; # (봲; 봲; 봲; 봲; 봲; ) HANGUL SYLLABLE BWAENH
+BD33;BD33;1107 116B 11AE;BD33;1107 116B 11AE; # (봳; 봳; 봳; 봳; 봳; ) HANGUL SYLLABLE BWAED
+BD34;BD34;1107 116B 11AF;BD34;1107 116B 11AF; # (봴; 봴; 봴; 봴; 봴; ) HANGUL SYLLABLE BWAEL
+BD35;BD35;1107 116B 11B0;BD35;1107 116B 11B0; # (봵; 봵; 봵; 봵; 봵; ) HANGUL SYLLABLE BWAELG
+BD36;BD36;1107 116B 11B1;BD36;1107 116B 11B1; # (봶; 봶; 봶; 봶; 봶; ) HANGUL SYLLABLE BWAELM
+BD37;BD37;1107 116B 11B2;BD37;1107 116B 11B2; # (봷; 봷; 봷; 봷; 봷; ) HANGUL SYLLABLE BWAELB
+BD38;BD38;1107 116B 11B3;BD38;1107 116B 11B3; # (봸; 봸; 봸; 봸; 봸; ) HANGUL SYLLABLE BWAELS
+BD39;BD39;1107 116B 11B4;BD39;1107 116B 11B4; # (봹; 봹; 봹; 봹; 봹; ) HANGUL SYLLABLE BWAELT
+BD3A;BD3A;1107 116B 11B5;BD3A;1107 116B 11B5; # (봺; 봺; 봺; 봺; 봺; ) HANGUL SYLLABLE BWAELP
+BD3B;BD3B;1107 116B 11B6;BD3B;1107 116B 11B6; # (봻; 봻; 봻; 봻; 봻; ) HANGUL SYLLABLE BWAELH
+BD3C;BD3C;1107 116B 11B7;BD3C;1107 116B 11B7; # (봼; 봼; 봼; 봼; 봼; ) HANGUL SYLLABLE BWAEM
+BD3D;BD3D;1107 116B 11B8;BD3D;1107 116B 11B8; # (봽; 봽; 봽; 봽; 봽; ) HANGUL SYLLABLE BWAEB
+BD3E;BD3E;1107 116B 11B9;BD3E;1107 116B 11B9; # (봾; 봾; 봾; 봾; 봾; ) HANGUL SYLLABLE BWAEBS
+BD3F;BD3F;1107 116B 11BA;BD3F;1107 116B 11BA; # (봿; 봿; 봿; 봿; 봿; ) HANGUL SYLLABLE BWAES
+BD40;BD40;1107 116B 11BB;BD40;1107 116B 11BB; # (뵀; 뵀; 뵀; 뵀; 뵀; ) HANGUL SYLLABLE BWAESS
+BD41;BD41;1107 116B 11BC;BD41;1107 116B 11BC; # (ëµ; ëµ; 뵁; ëµ; 뵁; ) HANGUL SYLLABLE BWAENG
+BD42;BD42;1107 116B 11BD;BD42;1107 116B 11BD; # (뵂; 뵂; 뵂; 뵂; 뵂; ) HANGUL SYLLABLE BWAEJ
+BD43;BD43;1107 116B 11BE;BD43;1107 116B 11BE; # (뵃; 뵃; 뵃; 뵃; 뵃; ) HANGUL SYLLABLE BWAEC
+BD44;BD44;1107 116B 11BF;BD44;1107 116B 11BF; # (뵄; 뵄; 뵄; 뵄; 뵄; ) HANGUL SYLLABLE BWAEK
+BD45;BD45;1107 116B 11C0;BD45;1107 116B 11C0; # (뵅; 뵅; 뵅; 뵅; 뵅; ) HANGUL SYLLABLE BWAET
+BD46;BD46;1107 116B 11C1;BD46;1107 116B 11C1; # (뵆; 뵆; 봬á‡; 뵆; 봬á‡; ) HANGUL SYLLABLE BWAEP
+BD47;BD47;1107 116B 11C2;BD47;1107 116B 11C2; # (뵇; 뵇; 뵇; 뵇; 뵇; ) HANGUL SYLLABLE BWAEH
+BD48;BD48;1107 116C;BD48;1107 116C; # (뵈; 뵈; 뵈; 뵈; 뵈; ) HANGUL SYLLABLE BOE
+BD49;BD49;1107 116C 11A8;BD49;1107 116C 11A8; # (뵉; 뵉; 뵉; 뵉; 뵉; ) HANGUL SYLLABLE BOEG
+BD4A;BD4A;1107 116C 11A9;BD4A;1107 116C 11A9; # (뵊; 뵊; 뵊; 뵊; 뵊; ) HANGUL SYLLABLE BOEGG
+BD4B;BD4B;1107 116C 11AA;BD4B;1107 116C 11AA; # (뵋; 뵋; 뵋; 뵋; 뵋; ) HANGUL SYLLABLE BOEGS
+BD4C;BD4C;1107 116C 11AB;BD4C;1107 116C 11AB; # (뵌; 뵌; 뵌; 뵌; 뵌; ) HANGUL SYLLABLE BOEN
+BD4D;BD4D;1107 116C 11AC;BD4D;1107 116C 11AC; # (ëµ; ëµ; 뵍; ëµ; 뵍; ) HANGUL SYLLABLE BOENJ
+BD4E;BD4E;1107 116C 11AD;BD4E;1107 116C 11AD; # (뵎; 뵎; 뵎; 뵎; 뵎; ) HANGUL SYLLABLE BOENH
+BD4F;BD4F;1107 116C 11AE;BD4F;1107 116C 11AE; # (ëµ; ëµ; 뵏; ëµ; 뵏; ) HANGUL SYLLABLE BOED
+BD50;BD50;1107 116C 11AF;BD50;1107 116C 11AF; # (ëµ; ëµ; 뵐; ëµ; 뵐; ) HANGUL SYLLABLE BOEL
+BD51;BD51;1107 116C 11B0;BD51;1107 116C 11B0; # (뵑; 뵑; 뵑; 뵑; 뵑; ) HANGUL SYLLABLE BOELG
+BD52;BD52;1107 116C 11B1;BD52;1107 116C 11B1; # (뵒; 뵒; 뵒; 뵒; 뵒; ) HANGUL SYLLABLE BOELM
+BD53;BD53;1107 116C 11B2;BD53;1107 116C 11B2; # (뵓; 뵓; 뵓; 뵓; 뵓; ) HANGUL SYLLABLE BOELB
+BD54;BD54;1107 116C 11B3;BD54;1107 116C 11B3; # (뵔; 뵔; 뵔; 뵔; 뵔; ) HANGUL SYLLABLE BOELS
+BD55;BD55;1107 116C 11B4;BD55;1107 116C 11B4; # (뵕; 뵕; 뵕; 뵕; 뵕; ) HANGUL SYLLABLE BOELT
+BD56;BD56;1107 116C 11B5;BD56;1107 116C 11B5; # (뵖; 뵖; 뵖; 뵖; 뵖; ) HANGUL SYLLABLE BOELP
+BD57;BD57;1107 116C 11B6;BD57;1107 116C 11B6; # (뵗; 뵗; 뵗; 뵗; 뵗; ) HANGUL SYLLABLE BOELH
+BD58;BD58;1107 116C 11B7;BD58;1107 116C 11B7; # (뵘; 뵘; 뵘; 뵘; 뵘; ) HANGUL SYLLABLE BOEM
+BD59;BD59;1107 116C 11B8;BD59;1107 116C 11B8; # (뵙; 뵙; 뵙; 뵙; 뵙; ) HANGUL SYLLABLE BOEB
+BD5A;BD5A;1107 116C 11B9;BD5A;1107 116C 11B9; # (뵚; 뵚; 뵚; 뵚; 뵚; ) HANGUL SYLLABLE BOEBS
+BD5B;BD5B;1107 116C 11BA;BD5B;1107 116C 11BA; # (뵛; 뵛; 뵛; 뵛; 뵛; ) HANGUL SYLLABLE BOES
+BD5C;BD5C;1107 116C 11BB;BD5C;1107 116C 11BB; # (뵜; 뵜; 뵜; 뵜; 뵜; ) HANGUL SYLLABLE BOESS
+BD5D;BD5D;1107 116C 11BC;BD5D;1107 116C 11BC; # (ëµ; ëµ; 뵝; ëµ; 뵝; ) HANGUL SYLLABLE BOENG
+BD5E;BD5E;1107 116C 11BD;BD5E;1107 116C 11BD; # (뵞; 뵞; 뵞; 뵞; 뵞; ) HANGUL SYLLABLE BOEJ
+BD5F;BD5F;1107 116C 11BE;BD5F;1107 116C 11BE; # (뵟; 뵟; 뵟; 뵟; 뵟; ) HANGUL SYLLABLE BOEC
+BD60;BD60;1107 116C 11BF;BD60;1107 116C 11BF; # (뵠; 뵠; 뵠; 뵠; 뵠; ) HANGUL SYLLABLE BOEK
+BD61;BD61;1107 116C 11C0;BD61;1107 116C 11C0; # (뵡; 뵡; 뵡; 뵡; 뵡; ) HANGUL SYLLABLE BOET
+BD62;BD62;1107 116C 11C1;BD62;1107 116C 11C1; # (ëµ¢; ëµ¢; 뵈á‡; ëµ¢; 뵈á‡; ) HANGUL SYLLABLE BOEP
+BD63;BD63;1107 116C 11C2;BD63;1107 116C 11C2; # (뵣; 뵣; 뵣; 뵣; 뵣; ) HANGUL SYLLABLE BOEH
+BD64;BD64;1107 116D;BD64;1107 116D; # (뵤; 뵤; 뵤; 뵤; 뵤; ) HANGUL SYLLABLE BYO
+BD65;BD65;1107 116D 11A8;BD65;1107 116D 11A8; # (뵥; 뵥; 뵥; 뵥; 뵥; ) HANGUL SYLLABLE BYOG
+BD66;BD66;1107 116D 11A9;BD66;1107 116D 11A9; # (뵦; 뵦; 뵦; 뵦; 뵦; ) HANGUL SYLLABLE BYOGG
+BD67;BD67;1107 116D 11AA;BD67;1107 116D 11AA; # (뵧; 뵧; 뵧; 뵧; 뵧; ) HANGUL SYLLABLE BYOGS
+BD68;BD68;1107 116D 11AB;BD68;1107 116D 11AB; # (뵨; 뵨; 뵨; 뵨; 뵨; ) HANGUL SYLLABLE BYON
+BD69;BD69;1107 116D 11AC;BD69;1107 116D 11AC; # (뵩; 뵩; 뵩; 뵩; 뵩; ) HANGUL SYLLABLE BYONJ
+BD6A;BD6A;1107 116D 11AD;BD6A;1107 116D 11AD; # (뵪; 뵪; 뵪; 뵪; 뵪; ) HANGUL SYLLABLE BYONH
+BD6B;BD6B;1107 116D 11AE;BD6B;1107 116D 11AE; # (뵫; 뵫; 뵫; 뵫; 뵫; ) HANGUL SYLLABLE BYOD
+BD6C;BD6C;1107 116D 11AF;BD6C;1107 116D 11AF; # (뵬; 뵬; 뵬; 뵬; 뵬; ) HANGUL SYLLABLE BYOL
+BD6D;BD6D;1107 116D 11B0;BD6D;1107 116D 11B0; # (뵭; 뵭; 뵭; 뵭; 뵭; ) HANGUL SYLLABLE BYOLG
+BD6E;BD6E;1107 116D 11B1;BD6E;1107 116D 11B1; # (뵮; 뵮; 뵮; 뵮; 뵮; ) HANGUL SYLLABLE BYOLM
+BD6F;BD6F;1107 116D 11B2;BD6F;1107 116D 11B2; # (뵯; 뵯; 뵯; 뵯; 뵯; ) HANGUL SYLLABLE BYOLB
+BD70;BD70;1107 116D 11B3;BD70;1107 116D 11B3; # (뵰; 뵰; 뵰; 뵰; 뵰; ) HANGUL SYLLABLE BYOLS
+BD71;BD71;1107 116D 11B4;BD71;1107 116D 11B4; # (뵱; 뵱; 뵱; 뵱; 뵱; ) HANGUL SYLLABLE BYOLT
+BD72;BD72;1107 116D 11B5;BD72;1107 116D 11B5; # (뵲; 뵲; 뵲; 뵲; 뵲; ) HANGUL SYLLABLE BYOLP
+BD73;BD73;1107 116D 11B6;BD73;1107 116D 11B6; # (뵳; 뵳; 뵳; 뵳; 뵳; ) HANGUL SYLLABLE BYOLH
+BD74;BD74;1107 116D 11B7;BD74;1107 116D 11B7; # (뵴; 뵴; 뵴; 뵴; 뵴; ) HANGUL SYLLABLE BYOM
+BD75;BD75;1107 116D 11B8;BD75;1107 116D 11B8; # (뵵; 뵵; 뵵; 뵵; 뵵; ) HANGUL SYLLABLE BYOB
+BD76;BD76;1107 116D 11B9;BD76;1107 116D 11B9; # (뵶; 뵶; 뵶; 뵶; 뵶; ) HANGUL SYLLABLE BYOBS
+BD77;BD77;1107 116D 11BA;BD77;1107 116D 11BA; # (뵷; 뵷; 뵷; 뵷; 뵷; ) HANGUL SYLLABLE BYOS
+BD78;BD78;1107 116D 11BB;BD78;1107 116D 11BB; # (뵸; 뵸; 뵸; 뵸; 뵸; ) HANGUL SYLLABLE BYOSS
+BD79;BD79;1107 116D 11BC;BD79;1107 116D 11BC; # (뵹; 뵹; 뵹; 뵹; 뵹; ) HANGUL SYLLABLE BYONG
+BD7A;BD7A;1107 116D 11BD;BD7A;1107 116D 11BD; # (뵺; 뵺; 뵺; 뵺; 뵺; ) HANGUL SYLLABLE BYOJ
+BD7B;BD7B;1107 116D 11BE;BD7B;1107 116D 11BE; # (뵻; 뵻; 뵻; 뵻; 뵻; ) HANGUL SYLLABLE BYOC
+BD7C;BD7C;1107 116D 11BF;BD7C;1107 116D 11BF; # (뵼; 뵼; 뵼; 뵼; 뵼; ) HANGUL SYLLABLE BYOK
+BD7D;BD7D;1107 116D 11C0;BD7D;1107 116D 11C0; # (뵽; 뵽; 뵽; 뵽; 뵽; ) HANGUL SYLLABLE BYOT
+BD7E;BD7E;1107 116D 11C1;BD7E;1107 116D 11C1; # (ëµ¾; ëµ¾; 뵤á‡; ëµ¾; 뵤á‡; ) HANGUL SYLLABLE BYOP
+BD7F;BD7F;1107 116D 11C2;BD7F;1107 116D 11C2; # (뵿; 뵿; 뵿; 뵿; 뵿; ) HANGUL SYLLABLE BYOH
+BD80;BD80;1107 116E;BD80;1107 116E; # (부; 부; 부; 부; 부; ) HANGUL SYLLABLE BU
+BD81;BD81;1107 116E 11A8;BD81;1107 116E 11A8; # (ë¶; ë¶; 북; ë¶; 북; ) HANGUL SYLLABLE BUG
+BD82;BD82;1107 116E 11A9;BD82;1107 116E 11A9; # (붂; 붂; 붂; 붂; 붂; ) HANGUL SYLLABLE BUGG
+BD83;BD83;1107 116E 11AA;BD83;1107 116E 11AA; # (붃; 붃; 붃; 붃; 붃; ) HANGUL SYLLABLE BUGS
+BD84;BD84;1107 116E 11AB;BD84;1107 116E 11AB; # (분; 분; 분; 분; 분; ) HANGUL SYLLABLE BUN
+BD85;BD85;1107 116E 11AC;BD85;1107 116E 11AC; # (붅; 붅; 붅; 붅; 붅; ) HANGUL SYLLABLE BUNJ
+BD86;BD86;1107 116E 11AD;BD86;1107 116E 11AD; # (붆; 붆; 붆; 붆; 붆; ) HANGUL SYLLABLE BUNH
+BD87;BD87;1107 116E 11AE;BD87;1107 116E 11AE; # (붇; 붇; 붇; 붇; 붇; ) HANGUL SYLLABLE BUD
+BD88;BD88;1107 116E 11AF;BD88;1107 116E 11AF; # (불; 불; 불; 불; 불; ) HANGUL SYLLABLE BUL
+BD89;BD89;1107 116E 11B0;BD89;1107 116E 11B0; # (붉; 붉; 붉; 붉; 붉; ) HANGUL SYLLABLE BULG
+BD8A;BD8A;1107 116E 11B1;BD8A;1107 116E 11B1; # (붊; 붊; 붊; 붊; 붊; ) HANGUL SYLLABLE BULM
+BD8B;BD8B;1107 116E 11B2;BD8B;1107 116E 11B2; # (붋; 붋; 붋; 붋; 붋; ) HANGUL SYLLABLE BULB
+BD8C;BD8C;1107 116E 11B3;BD8C;1107 116E 11B3; # (붌; 붌; 붌; 붌; 붌; ) HANGUL SYLLABLE BULS
+BD8D;BD8D;1107 116E 11B4;BD8D;1107 116E 11B4; # (ë¶; ë¶; 붍; ë¶; 붍; ) HANGUL SYLLABLE BULT
+BD8E;BD8E;1107 116E 11B5;BD8E;1107 116E 11B5; # (붎; 붎; 붎; 붎; 붎; ) HANGUL SYLLABLE BULP
+BD8F;BD8F;1107 116E 11B6;BD8F;1107 116E 11B6; # (ë¶; ë¶; 붏; ë¶; 붏; ) HANGUL SYLLABLE BULH
+BD90;BD90;1107 116E 11B7;BD90;1107 116E 11B7; # (ë¶; ë¶; 붐; ë¶; 붐; ) HANGUL SYLLABLE BUM
+BD91;BD91;1107 116E 11B8;BD91;1107 116E 11B8; # (붑; 붑; 붑; 붑; 붑; ) HANGUL SYLLABLE BUB
+BD92;BD92;1107 116E 11B9;BD92;1107 116E 11B9; # (붒; 붒; 붒; 붒; 붒; ) HANGUL SYLLABLE BUBS
+BD93;BD93;1107 116E 11BA;BD93;1107 116E 11BA; # (붓; 붓; 붓; 붓; 붓; ) HANGUL SYLLABLE BUS
+BD94;BD94;1107 116E 11BB;BD94;1107 116E 11BB; # (붔; 붔; 붔; 붔; 붔; ) HANGUL SYLLABLE BUSS
+BD95;BD95;1107 116E 11BC;BD95;1107 116E 11BC; # (붕; 붕; 붕; 붕; 붕; ) HANGUL SYLLABLE BUNG
+BD96;BD96;1107 116E 11BD;BD96;1107 116E 11BD; # (붖; 붖; 붖; 붖; 붖; ) HANGUL SYLLABLE BUJ
+BD97;BD97;1107 116E 11BE;BD97;1107 116E 11BE; # (붗; 붗; 붗; 붗; 붗; ) HANGUL SYLLABLE BUC
+BD98;BD98;1107 116E 11BF;BD98;1107 116E 11BF; # (붘; 붘; 붘; 붘; 붘; ) HANGUL SYLLABLE BUK
+BD99;BD99;1107 116E 11C0;BD99;1107 116E 11C0; # (붙; 붙; 붙; 붙; 붙; ) HANGUL SYLLABLE BUT
+BD9A;BD9A;1107 116E 11C1;BD9A;1107 116E 11C1; # (ë¶š; ë¶š; 부á‡; ë¶š; 부á‡; ) HANGUL SYLLABLE BUP
+BD9B;BD9B;1107 116E 11C2;BD9B;1107 116E 11C2; # (붛; 붛; 붛; 붛; 붛; ) HANGUL SYLLABLE BUH
+BD9C;BD9C;1107 116F;BD9C;1107 116F; # (붜; 붜; 붜; 붜; 붜; ) HANGUL SYLLABLE BWEO
+BD9D;BD9D;1107 116F 11A8;BD9D;1107 116F 11A8; # (ë¶; ë¶; 붝; ë¶; 붝; ) HANGUL SYLLABLE BWEOG
+BD9E;BD9E;1107 116F 11A9;BD9E;1107 116F 11A9; # (붞; 붞; 붞; 붞; 붞; ) HANGUL SYLLABLE BWEOGG
+BD9F;BD9F;1107 116F 11AA;BD9F;1107 116F 11AA; # (붟; 붟; 붟; 붟; 붟; ) HANGUL SYLLABLE BWEOGS
+BDA0;BDA0;1107 116F 11AB;BDA0;1107 116F 11AB; # (붠; 붠; 붠; 붠; 붠; ) HANGUL SYLLABLE BWEON
+BDA1;BDA1;1107 116F 11AC;BDA1;1107 116F 11AC; # (붡; 붡; 붡; 붡; 붡; ) HANGUL SYLLABLE BWEONJ
+BDA2;BDA2;1107 116F 11AD;BDA2;1107 116F 11AD; # (붢; 붢; 붢; 붢; 붢; ) HANGUL SYLLABLE BWEONH
+BDA3;BDA3;1107 116F 11AE;BDA3;1107 116F 11AE; # (붣; 붣; 붣; 붣; 붣; ) HANGUL SYLLABLE BWEOD
+BDA4;BDA4;1107 116F 11AF;BDA4;1107 116F 11AF; # (붤; 붤; 붤; 붤; 붤; ) HANGUL SYLLABLE BWEOL
+BDA5;BDA5;1107 116F 11B0;BDA5;1107 116F 11B0; # (붥; 붥; 붥; 붥; 붥; ) HANGUL SYLLABLE BWEOLG
+BDA6;BDA6;1107 116F 11B1;BDA6;1107 116F 11B1; # (붦; 붦; 붦; 붦; 붦; ) HANGUL SYLLABLE BWEOLM
+BDA7;BDA7;1107 116F 11B2;BDA7;1107 116F 11B2; # (붧; 붧; 붧; 붧; 붧; ) HANGUL SYLLABLE BWEOLB
+BDA8;BDA8;1107 116F 11B3;BDA8;1107 116F 11B3; # (붨; 붨; 붨; 붨; 붨; ) HANGUL SYLLABLE BWEOLS
+BDA9;BDA9;1107 116F 11B4;BDA9;1107 116F 11B4; # (붩; 붩; 붩; 붩; 붩; ) HANGUL SYLLABLE BWEOLT
+BDAA;BDAA;1107 116F 11B5;BDAA;1107 116F 11B5; # (붪; 붪; 붪; 붪; 붪; ) HANGUL SYLLABLE BWEOLP
+BDAB;BDAB;1107 116F 11B6;BDAB;1107 116F 11B6; # (붫; 붫; 붫; 붫; 붫; ) HANGUL SYLLABLE BWEOLH
+BDAC;BDAC;1107 116F 11B7;BDAC;1107 116F 11B7; # (붬; 붬; 붬; 붬; 붬; ) HANGUL SYLLABLE BWEOM
+BDAD;BDAD;1107 116F 11B8;BDAD;1107 116F 11B8; # (붭; 붭; 붭; 붭; 붭; ) HANGUL SYLLABLE BWEOB
+BDAE;BDAE;1107 116F 11B9;BDAE;1107 116F 11B9; # (붮; 붮; 붮; 붮; 붮; ) HANGUL SYLLABLE BWEOBS
+BDAF;BDAF;1107 116F 11BA;BDAF;1107 116F 11BA; # (붯; 붯; 붯; 붯; 붯; ) HANGUL SYLLABLE BWEOS
+BDB0;BDB0;1107 116F 11BB;BDB0;1107 116F 11BB; # (붰; 붰; 붰; 붰; 붰; ) HANGUL SYLLABLE BWEOSS
+BDB1;BDB1;1107 116F 11BC;BDB1;1107 116F 11BC; # (붱; 붱; 붱; 붱; 붱; ) HANGUL SYLLABLE BWEONG
+BDB2;BDB2;1107 116F 11BD;BDB2;1107 116F 11BD; # (붲; 붲; 붲; 붲; 붲; ) HANGUL SYLLABLE BWEOJ
+BDB3;BDB3;1107 116F 11BE;BDB3;1107 116F 11BE; # (붳; 붳; 붳; 붳; 붳; ) HANGUL SYLLABLE BWEOC
+BDB4;BDB4;1107 116F 11BF;BDB4;1107 116F 11BF; # (붴; 붴; 붴; 붴; 붴; ) HANGUL SYLLABLE BWEOK
+BDB5;BDB5;1107 116F 11C0;BDB5;1107 116F 11C0; # (붵; 붵; 붵; 붵; 붵; ) HANGUL SYLLABLE BWEOT
+BDB6;BDB6;1107 116F 11C1;BDB6;1107 116F 11C1; # (ë¶¶; ë¶¶; 붜á‡; ë¶¶; 붜á‡; ) HANGUL SYLLABLE BWEOP
+BDB7;BDB7;1107 116F 11C2;BDB7;1107 116F 11C2; # (붷; 붷; 붷; 붷; 붷; ) HANGUL SYLLABLE BWEOH
+BDB8;BDB8;1107 1170;BDB8;1107 1170; # (붸; 붸; 붸; 붸; 붸; ) HANGUL SYLLABLE BWE
+BDB9;BDB9;1107 1170 11A8;BDB9;1107 1170 11A8; # (붹; 붹; 붹; 붹; 붹; ) HANGUL SYLLABLE BWEG
+BDBA;BDBA;1107 1170 11A9;BDBA;1107 1170 11A9; # (붺; 붺; 붺; 붺; 붺; ) HANGUL SYLLABLE BWEGG
+BDBB;BDBB;1107 1170 11AA;BDBB;1107 1170 11AA; # (붻; 붻; 붻; 붻; 붻; ) HANGUL SYLLABLE BWEGS
+BDBC;BDBC;1107 1170 11AB;BDBC;1107 1170 11AB; # (붼; 붼; 붼; 붼; 붼; ) HANGUL SYLLABLE BWEN
+BDBD;BDBD;1107 1170 11AC;BDBD;1107 1170 11AC; # (붽; 붽; 붽; 붽; 붽; ) HANGUL SYLLABLE BWENJ
+BDBE;BDBE;1107 1170 11AD;BDBE;1107 1170 11AD; # (붾; 붾; 붾; 붾; 붾; ) HANGUL SYLLABLE BWENH
+BDBF;BDBF;1107 1170 11AE;BDBF;1107 1170 11AE; # (붿; 붿; 붿; 붿; 붿; ) HANGUL SYLLABLE BWED
+BDC0;BDC0;1107 1170 11AF;BDC0;1107 1170 11AF; # (뷀; 뷀; 뷀; 뷀; 뷀; ) HANGUL SYLLABLE BWEL
+BDC1;BDC1;1107 1170 11B0;BDC1;1107 1170 11B0; # (ë·; ë·; 뷁; ë·; 뷁; ) HANGUL SYLLABLE BWELG
+BDC2;BDC2;1107 1170 11B1;BDC2;1107 1170 11B1; # (뷂; 뷂; 뷂; 뷂; 뷂; ) HANGUL SYLLABLE BWELM
+BDC3;BDC3;1107 1170 11B2;BDC3;1107 1170 11B2; # (뷃; 뷃; 뷃; 뷃; 뷃; ) HANGUL SYLLABLE BWELB
+BDC4;BDC4;1107 1170 11B3;BDC4;1107 1170 11B3; # (뷄; 뷄; 뷄; 뷄; 뷄; ) HANGUL SYLLABLE BWELS
+BDC5;BDC5;1107 1170 11B4;BDC5;1107 1170 11B4; # (뷅; 뷅; 뷅; 뷅; 뷅; ) HANGUL SYLLABLE BWELT
+BDC6;BDC6;1107 1170 11B5;BDC6;1107 1170 11B5; # (뷆; 뷆; 뷆; 뷆; 뷆; ) HANGUL SYLLABLE BWELP
+BDC7;BDC7;1107 1170 11B6;BDC7;1107 1170 11B6; # (뷇; 뷇; 뷇; 뷇; 뷇; ) HANGUL SYLLABLE BWELH
+BDC8;BDC8;1107 1170 11B7;BDC8;1107 1170 11B7; # (뷈; 뷈; 뷈; 뷈; 뷈; ) HANGUL SYLLABLE BWEM
+BDC9;BDC9;1107 1170 11B8;BDC9;1107 1170 11B8; # (뷉; 뷉; 뷉; 뷉; 뷉; ) HANGUL SYLLABLE BWEB
+BDCA;BDCA;1107 1170 11B9;BDCA;1107 1170 11B9; # (뷊; 뷊; 뷊; 뷊; 뷊; ) HANGUL SYLLABLE BWEBS
+BDCB;BDCB;1107 1170 11BA;BDCB;1107 1170 11BA; # (뷋; 뷋; 뷋; 뷋; 뷋; ) HANGUL SYLLABLE BWES
+BDCC;BDCC;1107 1170 11BB;BDCC;1107 1170 11BB; # (뷌; 뷌; 뷌; 뷌; 뷌; ) HANGUL SYLLABLE BWESS
+BDCD;BDCD;1107 1170 11BC;BDCD;1107 1170 11BC; # (ë·; ë·; 뷍; ë·; 뷍; ) HANGUL SYLLABLE BWENG
+BDCE;BDCE;1107 1170 11BD;BDCE;1107 1170 11BD; # (뷎; 뷎; 뷎; 뷎; 뷎; ) HANGUL SYLLABLE BWEJ
+BDCF;BDCF;1107 1170 11BE;BDCF;1107 1170 11BE; # (ë·; ë·; 뷏; ë·; 뷏; ) HANGUL SYLLABLE BWEC
+BDD0;BDD0;1107 1170 11BF;BDD0;1107 1170 11BF; # (ë·; ë·; 뷐; ë·; 뷐; ) HANGUL SYLLABLE BWEK
+BDD1;BDD1;1107 1170 11C0;BDD1;1107 1170 11C0; # (뷑; 뷑; 뷑; 뷑; 뷑; ) HANGUL SYLLABLE BWET
+BDD2;BDD2;1107 1170 11C1;BDD2;1107 1170 11C1; # (ë·’; ë·’; 붸á‡; ë·’; 붸á‡; ) HANGUL SYLLABLE BWEP
+BDD3;BDD3;1107 1170 11C2;BDD3;1107 1170 11C2; # (뷓; 뷓; 뷓; 뷓; 뷓; ) HANGUL SYLLABLE BWEH
+BDD4;BDD4;1107 1171;BDD4;1107 1171; # (뷔; 뷔; 뷔; 뷔; 뷔; ) HANGUL SYLLABLE BWI
+BDD5;BDD5;1107 1171 11A8;BDD5;1107 1171 11A8; # (뷕; 뷕; 뷕; 뷕; 뷕; ) HANGUL SYLLABLE BWIG
+BDD6;BDD6;1107 1171 11A9;BDD6;1107 1171 11A9; # (뷖; 뷖; 뷖; 뷖; 뷖; ) HANGUL SYLLABLE BWIGG
+BDD7;BDD7;1107 1171 11AA;BDD7;1107 1171 11AA; # (뷗; 뷗; 뷗; 뷗; 뷗; ) HANGUL SYLLABLE BWIGS
+BDD8;BDD8;1107 1171 11AB;BDD8;1107 1171 11AB; # (뷘; 뷘; 뷘; 뷘; 뷘; ) HANGUL SYLLABLE BWIN
+BDD9;BDD9;1107 1171 11AC;BDD9;1107 1171 11AC; # (뷙; 뷙; 뷙; 뷙; 뷙; ) HANGUL SYLLABLE BWINJ
+BDDA;BDDA;1107 1171 11AD;BDDA;1107 1171 11AD; # (뷚; 뷚; 뷚; 뷚; 뷚; ) HANGUL SYLLABLE BWINH
+BDDB;BDDB;1107 1171 11AE;BDDB;1107 1171 11AE; # (뷛; 뷛; 뷛; 뷛; 뷛; ) HANGUL SYLLABLE BWID
+BDDC;BDDC;1107 1171 11AF;BDDC;1107 1171 11AF; # (뷜; 뷜; 뷜; 뷜; 뷜; ) HANGUL SYLLABLE BWIL
+BDDD;BDDD;1107 1171 11B0;BDDD;1107 1171 11B0; # (ë·; ë·; 뷝; ë·; 뷝; ) HANGUL SYLLABLE BWILG
+BDDE;BDDE;1107 1171 11B1;BDDE;1107 1171 11B1; # (뷞; 뷞; 뷞; 뷞; 뷞; ) HANGUL SYLLABLE BWILM
+BDDF;BDDF;1107 1171 11B2;BDDF;1107 1171 11B2; # (뷟; 뷟; 뷟; 뷟; 뷟; ) HANGUL SYLLABLE BWILB
+BDE0;BDE0;1107 1171 11B3;BDE0;1107 1171 11B3; # (뷠; 뷠; 뷠; 뷠; 뷠; ) HANGUL SYLLABLE BWILS
+BDE1;BDE1;1107 1171 11B4;BDE1;1107 1171 11B4; # (뷡; 뷡; 뷡; 뷡; 뷡; ) HANGUL SYLLABLE BWILT
+BDE2;BDE2;1107 1171 11B5;BDE2;1107 1171 11B5; # (뷢; 뷢; 뷢; 뷢; 뷢; ) HANGUL SYLLABLE BWILP
+BDE3;BDE3;1107 1171 11B6;BDE3;1107 1171 11B6; # (뷣; 뷣; 뷣; 뷣; 뷣; ) HANGUL SYLLABLE BWILH
+BDE4;BDE4;1107 1171 11B7;BDE4;1107 1171 11B7; # (뷤; 뷤; 뷤; 뷤; 뷤; ) HANGUL SYLLABLE BWIM
+BDE5;BDE5;1107 1171 11B8;BDE5;1107 1171 11B8; # (뷥; 뷥; 뷥; 뷥; 뷥; ) HANGUL SYLLABLE BWIB
+BDE6;BDE6;1107 1171 11B9;BDE6;1107 1171 11B9; # (뷦; 뷦; 뷦; 뷦; 뷦; ) HANGUL SYLLABLE BWIBS
+BDE7;BDE7;1107 1171 11BA;BDE7;1107 1171 11BA; # (뷧; 뷧; 뷧; 뷧; 뷧; ) HANGUL SYLLABLE BWIS
+BDE8;BDE8;1107 1171 11BB;BDE8;1107 1171 11BB; # (뷨; 뷨; 뷨; 뷨; 뷨; ) HANGUL SYLLABLE BWISS
+BDE9;BDE9;1107 1171 11BC;BDE9;1107 1171 11BC; # (뷩; 뷩; 뷩; 뷩; 뷩; ) HANGUL SYLLABLE BWING
+BDEA;BDEA;1107 1171 11BD;BDEA;1107 1171 11BD; # (뷪; 뷪; 뷪; 뷪; 뷪; ) HANGUL SYLLABLE BWIJ
+BDEB;BDEB;1107 1171 11BE;BDEB;1107 1171 11BE; # (뷫; 뷫; 뷫; 뷫; 뷫; ) HANGUL SYLLABLE BWIC
+BDEC;BDEC;1107 1171 11BF;BDEC;1107 1171 11BF; # (뷬; 뷬; 뷬; 뷬; 뷬; ) HANGUL SYLLABLE BWIK
+BDED;BDED;1107 1171 11C0;BDED;1107 1171 11C0; # (뷭; 뷭; 뷭; 뷭; 뷭; ) HANGUL SYLLABLE BWIT
+BDEE;BDEE;1107 1171 11C1;BDEE;1107 1171 11C1; # (ë·®; ë·®; 뷔á‡; ë·®; 뷔á‡; ) HANGUL SYLLABLE BWIP
+BDEF;BDEF;1107 1171 11C2;BDEF;1107 1171 11C2; # (뷯; 뷯; 뷯; 뷯; 뷯; ) HANGUL SYLLABLE BWIH
+BDF0;BDF0;1107 1172;BDF0;1107 1172; # (뷰; 뷰; 뷰; 뷰; 뷰; ) HANGUL SYLLABLE BYU
+BDF1;BDF1;1107 1172 11A8;BDF1;1107 1172 11A8; # (뷱; 뷱; 뷱; 뷱; 뷱; ) HANGUL SYLLABLE BYUG
+BDF2;BDF2;1107 1172 11A9;BDF2;1107 1172 11A9; # (뷲; 뷲; 뷲; 뷲; 뷲; ) HANGUL SYLLABLE BYUGG
+BDF3;BDF3;1107 1172 11AA;BDF3;1107 1172 11AA; # (뷳; 뷳; 뷳; 뷳; 뷳; ) HANGUL SYLLABLE BYUGS
+BDF4;BDF4;1107 1172 11AB;BDF4;1107 1172 11AB; # (뷴; 뷴; 뷴; 뷴; 뷴; ) HANGUL SYLLABLE BYUN
+BDF5;BDF5;1107 1172 11AC;BDF5;1107 1172 11AC; # (뷵; 뷵; 뷵; 뷵; 뷵; ) HANGUL SYLLABLE BYUNJ
+BDF6;BDF6;1107 1172 11AD;BDF6;1107 1172 11AD; # (뷶; 뷶; 뷶; 뷶; 뷶; ) HANGUL SYLLABLE BYUNH
+BDF7;BDF7;1107 1172 11AE;BDF7;1107 1172 11AE; # (뷷; 뷷; 뷷; 뷷; 뷷; ) HANGUL SYLLABLE BYUD
+BDF8;BDF8;1107 1172 11AF;BDF8;1107 1172 11AF; # (뷸; 뷸; 뷸; 뷸; 뷸; ) HANGUL SYLLABLE BYUL
+BDF9;BDF9;1107 1172 11B0;BDF9;1107 1172 11B0; # (뷹; 뷹; 뷹; 뷹; 뷹; ) HANGUL SYLLABLE BYULG
+BDFA;BDFA;1107 1172 11B1;BDFA;1107 1172 11B1; # (뷺; 뷺; 뷺; 뷺; 뷺; ) HANGUL SYLLABLE BYULM
+BDFB;BDFB;1107 1172 11B2;BDFB;1107 1172 11B2; # (뷻; 뷻; 뷻; 뷻; 뷻; ) HANGUL SYLLABLE BYULB
+BDFC;BDFC;1107 1172 11B3;BDFC;1107 1172 11B3; # (뷼; 뷼; 뷼; 뷼; 뷼; ) HANGUL SYLLABLE BYULS
+BDFD;BDFD;1107 1172 11B4;BDFD;1107 1172 11B4; # (뷽; 뷽; 뷽; 뷽; 뷽; ) HANGUL SYLLABLE BYULT
+BDFE;BDFE;1107 1172 11B5;BDFE;1107 1172 11B5; # (뷾; 뷾; 뷾; 뷾; 뷾; ) HANGUL SYLLABLE BYULP
+BDFF;BDFF;1107 1172 11B6;BDFF;1107 1172 11B6; # (뷿; 뷿; 뷿; 뷿; 뷿; ) HANGUL SYLLABLE BYULH
+BE00;BE00;1107 1172 11B7;BE00;1107 1172 11B7; # (븀; 븀; 븀; 븀; 븀; ) HANGUL SYLLABLE BYUM
+BE01;BE01;1107 1172 11B8;BE01;1107 1172 11B8; # (ë¸; ë¸; 븁; ë¸; 븁; ) HANGUL SYLLABLE BYUB
+BE02;BE02;1107 1172 11B9;BE02;1107 1172 11B9; # (븂; 븂; 븂; 븂; 븂; ) HANGUL SYLLABLE BYUBS
+BE03;BE03;1107 1172 11BA;BE03;1107 1172 11BA; # (븃; 븃; 븃; 븃; 븃; ) HANGUL SYLLABLE BYUS
+BE04;BE04;1107 1172 11BB;BE04;1107 1172 11BB; # (븄; 븄; 븄; 븄; 븄; ) HANGUL SYLLABLE BYUSS
+BE05;BE05;1107 1172 11BC;BE05;1107 1172 11BC; # (븅; 븅; 븅; 븅; 븅; ) HANGUL SYLLABLE BYUNG
+BE06;BE06;1107 1172 11BD;BE06;1107 1172 11BD; # (븆; 븆; 븆; 븆; 븆; ) HANGUL SYLLABLE BYUJ
+BE07;BE07;1107 1172 11BE;BE07;1107 1172 11BE; # (븇; 븇; 븇; 븇; 븇; ) HANGUL SYLLABLE BYUC
+BE08;BE08;1107 1172 11BF;BE08;1107 1172 11BF; # (븈; 븈; 븈; 븈; 븈; ) HANGUL SYLLABLE BYUK
+BE09;BE09;1107 1172 11C0;BE09;1107 1172 11C0; # (븉; 븉; 븉; 븉; 븉; ) HANGUL SYLLABLE BYUT
+BE0A;BE0A;1107 1172 11C1;BE0A;1107 1172 11C1; # (븊; 븊; 뷰á‡; 븊; 뷰á‡; ) HANGUL SYLLABLE BYUP
+BE0B;BE0B;1107 1172 11C2;BE0B;1107 1172 11C2; # (븋; 븋; 븋; 븋; 븋; ) HANGUL SYLLABLE BYUH
+BE0C;BE0C;1107 1173;BE0C;1107 1173; # (브; 브; 브; 브; 브; ) HANGUL SYLLABLE BEU
+BE0D;BE0D;1107 1173 11A8;BE0D;1107 1173 11A8; # (ë¸; ë¸; 븍; ë¸; 븍; ) HANGUL SYLLABLE BEUG
+BE0E;BE0E;1107 1173 11A9;BE0E;1107 1173 11A9; # (븎; 븎; 븎; 븎; 븎; ) HANGUL SYLLABLE BEUGG
+BE0F;BE0F;1107 1173 11AA;BE0F;1107 1173 11AA; # (ë¸; ë¸; 븏; ë¸; 븏; ) HANGUL SYLLABLE BEUGS
+BE10;BE10;1107 1173 11AB;BE10;1107 1173 11AB; # (ë¸; ë¸; 븐; ë¸; 븐; ) HANGUL SYLLABLE BEUN
+BE11;BE11;1107 1173 11AC;BE11;1107 1173 11AC; # (븑; 븑; 븑; 븑; 븑; ) HANGUL SYLLABLE BEUNJ
+BE12;BE12;1107 1173 11AD;BE12;1107 1173 11AD; # (븒; 븒; 븒; 븒; 븒; ) HANGUL SYLLABLE BEUNH
+BE13;BE13;1107 1173 11AE;BE13;1107 1173 11AE; # (븓; 븓; 븓; 븓; 븓; ) HANGUL SYLLABLE BEUD
+BE14;BE14;1107 1173 11AF;BE14;1107 1173 11AF; # (블; 블; 블; 블; 블; ) HANGUL SYLLABLE BEUL
+BE15;BE15;1107 1173 11B0;BE15;1107 1173 11B0; # (븕; 븕; 븕; 븕; 븕; ) HANGUL SYLLABLE BEULG
+BE16;BE16;1107 1173 11B1;BE16;1107 1173 11B1; # (븖; 븖; 븖; 븖; 븖; ) HANGUL SYLLABLE BEULM
+BE17;BE17;1107 1173 11B2;BE17;1107 1173 11B2; # (븗; 븗; 븗; 븗; 븗; ) HANGUL SYLLABLE BEULB
+BE18;BE18;1107 1173 11B3;BE18;1107 1173 11B3; # (븘; 븘; 븘; 븘; 븘; ) HANGUL SYLLABLE BEULS
+BE19;BE19;1107 1173 11B4;BE19;1107 1173 11B4; # (븙; 븙; 븙; 븙; 븙; ) HANGUL SYLLABLE BEULT
+BE1A;BE1A;1107 1173 11B5;BE1A;1107 1173 11B5; # (븚; 븚; 븚; 븚; 븚; ) HANGUL SYLLABLE BEULP
+BE1B;BE1B;1107 1173 11B6;BE1B;1107 1173 11B6; # (븛; 븛; 븛; 븛; 븛; ) HANGUL SYLLABLE BEULH
+BE1C;BE1C;1107 1173 11B7;BE1C;1107 1173 11B7; # (븜; 븜; 븜; 븜; 븜; ) HANGUL SYLLABLE BEUM
+BE1D;BE1D;1107 1173 11B8;BE1D;1107 1173 11B8; # (ë¸; ë¸; 븝; ë¸; 븝; ) HANGUL SYLLABLE BEUB
+BE1E;BE1E;1107 1173 11B9;BE1E;1107 1173 11B9; # (븞; 븞; 븞; 븞; 븞; ) HANGUL SYLLABLE BEUBS
+BE1F;BE1F;1107 1173 11BA;BE1F;1107 1173 11BA; # (븟; 븟; 븟; 븟; 븟; ) HANGUL SYLLABLE BEUS
+BE20;BE20;1107 1173 11BB;BE20;1107 1173 11BB; # (븠; 븠; 븠; 븠; 븠; ) HANGUL SYLLABLE BEUSS
+BE21;BE21;1107 1173 11BC;BE21;1107 1173 11BC; # (븡; 븡; 븡; 븡; 븡; ) HANGUL SYLLABLE BEUNG
+BE22;BE22;1107 1173 11BD;BE22;1107 1173 11BD; # (븢; 븢; 븢; 븢; 븢; ) HANGUL SYLLABLE BEUJ
+BE23;BE23;1107 1173 11BE;BE23;1107 1173 11BE; # (븣; 븣; 븣; 븣; 븣; ) HANGUL SYLLABLE BEUC
+BE24;BE24;1107 1173 11BF;BE24;1107 1173 11BF; # (븤; 븤; 븤; 븤; 븤; ) HANGUL SYLLABLE BEUK
+BE25;BE25;1107 1173 11C0;BE25;1107 1173 11C0; # (븥; 븥; 븥; 븥; 븥; ) HANGUL SYLLABLE BEUT
+BE26;BE26;1107 1173 11C1;BE26;1107 1173 11C1; # (븦; 븦; 브á‡; 븦; 브á‡; ) HANGUL SYLLABLE BEUP
+BE27;BE27;1107 1173 11C2;BE27;1107 1173 11C2; # (븧; 븧; 븧; 븧; 븧; ) HANGUL SYLLABLE BEUH
+BE28;BE28;1107 1174;BE28;1107 1174; # (븨; 븨; 븨; 븨; 븨; ) HANGUL SYLLABLE BYI
+BE29;BE29;1107 1174 11A8;BE29;1107 1174 11A8; # (븩; 븩; 븩; 븩; 븩; ) HANGUL SYLLABLE BYIG
+BE2A;BE2A;1107 1174 11A9;BE2A;1107 1174 11A9; # (븪; 븪; 븪; 븪; 븪; ) HANGUL SYLLABLE BYIGG
+BE2B;BE2B;1107 1174 11AA;BE2B;1107 1174 11AA; # (븫; 븫; 븫; 븫; 븫; ) HANGUL SYLLABLE BYIGS
+BE2C;BE2C;1107 1174 11AB;BE2C;1107 1174 11AB; # (븬; 븬; 븬; 븬; 븬; ) HANGUL SYLLABLE BYIN
+BE2D;BE2D;1107 1174 11AC;BE2D;1107 1174 11AC; # (븭; 븭; 븭; 븭; 븭; ) HANGUL SYLLABLE BYINJ
+BE2E;BE2E;1107 1174 11AD;BE2E;1107 1174 11AD; # (븮; 븮; 븮; 븮; 븮; ) HANGUL SYLLABLE BYINH
+BE2F;BE2F;1107 1174 11AE;BE2F;1107 1174 11AE; # (븯; 븯; 븯; 븯; 븯; ) HANGUL SYLLABLE BYID
+BE30;BE30;1107 1174 11AF;BE30;1107 1174 11AF; # (븰; 븰; 븰; 븰; 븰; ) HANGUL SYLLABLE BYIL
+BE31;BE31;1107 1174 11B0;BE31;1107 1174 11B0; # (븱; 븱; 븱; 븱; 븱; ) HANGUL SYLLABLE BYILG
+BE32;BE32;1107 1174 11B1;BE32;1107 1174 11B1; # (븲; 븲; 븲; 븲; 븲; ) HANGUL SYLLABLE BYILM
+BE33;BE33;1107 1174 11B2;BE33;1107 1174 11B2; # (븳; 븳; 븳; 븳; 븳; ) HANGUL SYLLABLE BYILB
+BE34;BE34;1107 1174 11B3;BE34;1107 1174 11B3; # (븴; 븴; 븴; 븴; 븴; ) HANGUL SYLLABLE BYILS
+BE35;BE35;1107 1174 11B4;BE35;1107 1174 11B4; # (븵; 븵; 븵; 븵; 븵; ) HANGUL SYLLABLE BYILT
+BE36;BE36;1107 1174 11B5;BE36;1107 1174 11B5; # (븶; 븶; 븶; 븶; 븶; ) HANGUL SYLLABLE BYILP
+BE37;BE37;1107 1174 11B6;BE37;1107 1174 11B6; # (븷; 븷; 븷; 븷; 븷; ) HANGUL SYLLABLE BYILH
+BE38;BE38;1107 1174 11B7;BE38;1107 1174 11B7; # (븸; 븸; 븸; 븸; 븸; ) HANGUL SYLLABLE BYIM
+BE39;BE39;1107 1174 11B8;BE39;1107 1174 11B8; # (븹; 븹; 븹; 븹; 븹; ) HANGUL SYLLABLE BYIB
+BE3A;BE3A;1107 1174 11B9;BE3A;1107 1174 11B9; # (븺; 븺; 븺; 븺; 븺; ) HANGUL SYLLABLE BYIBS
+BE3B;BE3B;1107 1174 11BA;BE3B;1107 1174 11BA; # (븻; 븻; 븻; 븻; 븻; ) HANGUL SYLLABLE BYIS
+BE3C;BE3C;1107 1174 11BB;BE3C;1107 1174 11BB; # (븼; 븼; 븼; 븼; 븼; ) HANGUL SYLLABLE BYISS
+BE3D;BE3D;1107 1174 11BC;BE3D;1107 1174 11BC; # (븽; 븽; 븽; 븽; 븽; ) HANGUL SYLLABLE BYING
+BE3E;BE3E;1107 1174 11BD;BE3E;1107 1174 11BD; # (븾; 븾; 븾; 븾; 븾; ) HANGUL SYLLABLE BYIJ
+BE3F;BE3F;1107 1174 11BE;BE3F;1107 1174 11BE; # (븿; 븿; 븿; 븿; 븿; ) HANGUL SYLLABLE BYIC
+BE40;BE40;1107 1174 11BF;BE40;1107 1174 11BF; # (빀; 빀; 빀; 빀; 빀; ) HANGUL SYLLABLE BYIK
+BE41;BE41;1107 1174 11C0;BE41;1107 1174 11C0; # (ë¹; ë¹; 빁; ë¹; 빁; ) HANGUL SYLLABLE BYIT
+BE42;BE42;1107 1174 11C1;BE42;1107 1174 11C1; # (빂; 빂; 븨á‡; 빂; 븨á‡; ) HANGUL SYLLABLE BYIP
+BE43;BE43;1107 1174 11C2;BE43;1107 1174 11C2; # (빃; 빃; 빃; 빃; 빃; ) HANGUL SYLLABLE BYIH
+BE44;BE44;1107 1175;BE44;1107 1175; # (비; 비; 비; 비; 비; ) HANGUL SYLLABLE BI
+BE45;BE45;1107 1175 11A8;BE45;1107 1175 11A8; # (빅; 빅; 빅; 빅; 빅; ) HANGUL SYLLABLE BIG
+BE46;BE46;1107 1175 11A9;BE46;1107 1175 11A9; # (빆; 빆; 빆; 빆; 빆; ) HANGUL SYLLABLE BIGG
+BE47;BE47;1107 1175 11AA;BE47;1107 1175 11AA; # (빇; 빇; 빇; 빇; 빇; ) HANGUL SYLLABLE BIGS
+BE48;BE48;1107 1175 11AB;BE48;1107 1175 11AB; # (빈; 빈; 빈; 빈; 빈; ) HANGUL SYLLABLE BIN
+BE49;BE49;1107 1175 11AC;BE49;1107 1175 11AC; # (빉; 빉; 빉; 빉; 빉; ) HANGUL SYLLABLE BINJ
+BE4A;BE4A;1107 1175 11AD;BE4A;1107 1175 11AD; # (빊; 빊; 빊; 빊; 빊; ) HANGUL SYLLABLE BINH
+BE4B;BE4B;1107 1175 11AE;BE4B;1107 1175 11AE; # (빋; 빋; 빋; 빋; 빋; ) HANGUL SYLLABLE BID
+BE4C;BE4C;1107 1175 11AF;BE4C;1107 1175 11AF; # (빌; 빌; 빌; 빌; 빌; ) HANGUL SYLLABLE BIL
+BE4D;BE4D;1107 1175 11B0;BE4D;1107 1175 11B0; # (ë¹; ë¹; 빍; ë¹; 빍; ) HANGUL SYLLABLE BILG
+BE4E;BE4E;1107 1175 11B1;BE4E;1107 1175 11B1; # (빎; 빎; 빎; 빎; 빎; ) HANGUL SYLLABLE BILM
+BE4F;BE4F;1107 1175 11B2;BE4F;1107 1175 11B2; # (ë¹; ë¹; 빏; ë¹; 빏; ) HANGUL SYLLABLE BILB
+BE50;BE50;1107 1175 11B3;BE50;1107 1175 11B3; # (ë¹; ë¹; 빐; ë¹; 빐; ) HANGUL SYLLABLE BILS
+BE51;BE51;1107 1175 11B4;BE51;1107 1175 11B4; # (빑; 빑; 빑; 빑; 빑; ) HANGUL SYLLABLE BILT
+BE52;BE52;1107 1175 11B5;BE52;1107 1175 11B5; # (빒; 빒; 빒; 빒; 빒; ) HANGUL SYLLABLE BILP
+BE53;BE53;1107 1175 11B6;BE53;1107 1175 11B6; # (빓; 빓; 빓; 빓; 빓; ) HANGUL SYLLABLE BILH
+BE54;BE54;1107 1175 11B7;BE54;1107 1175 11B7; # (빔; 빔; 빔; 빔; 빔; ) HANGUL SYLLABLE BIM
+BE55;BE55;1107 1175 11B8;BE55;1107 1175 11B8; # (빕; 빕; 빕; 빕; 빕; ) HANGUL SYLLABLE BIB
+BE56;BE56;1107 1175 11B9;BE56;1107 1175 11B9; # (빖; 빖; 빖; 빖; 빖; ) HANGUL SYLLABLE BIBS
+BE57;BE57;1107 1175 11BA;BE57;1107 1175 11BA; # (빗; 빗; 빗; 빗; 빗; ) HANGUL SYLLABLE BIS
+BE58;BE58;1107 1175 11BB;BE58;1107 1175 11BB; # (빘; 빘; 빘; 빘; 빘; ) HANGUL SYLLABLE BISS
+BE59;BE59;1107 1175 11BC;BE59;1107 1175 11BC; # (빙; 빙; 빙; 빙; 빙; ) HANGUL SYLLABLE BING
+BE5A;BE5A;1107 1175 11BD;BE5A;1107 1175 11BD; # (빚; 빚; 빚; 빚; 빚; ) HANGUL SYLLABLE BIJ
+BE5B;BE5B;1107 1175 11BE;BE5B;1107 1175 11BE; # (빛; 빛; 빛; 빛; 빛; ) HANGUL SYLLABLE BIC
+BE5C;BE5C;1107 1175 11BF;BE5C;1107 1175 11BF; # (빜; 빜; 빜; 빜; 빜; ) HANGUL SYLLABLE BIK
+BE5D;BE5D;1107 1175 11C0;BE5D;1107 1175 11C0; # (ë¹; ë¹; 빝; ë¹; 빝; ) HANGUL SYLLABLE BIT
+BE5E;BE5E;1107 1175 11C1;BE5E;1107 1175 11C1; # (빞; 빞; 비á‡; 빞; 비á‡; ) HANGUL SYLLABLE BIP
+BE5F;BE5F;1107 1175 11C2;BE5F;1107 1175 11C2; # (빟; 빟; 빟; 빟; 빟; ) HANGUL SYLLABLE BIH
+BE60;BE60;1108 1161;BE60;1108 1161; # (빠; 빠; 빠; 빠; 빠; ) HANGUL SYLLABLE BBA
+BE61;BE61;1108 1161 11A8;BE61;1108 1161 11A8; # (빡; 빡; 빡; 빡; 빡; ) HANGUL SYLLABLE BBAG
+BE62;BE62;1108 1161 11A9;BE62;1108 1161 11A9; # (빢; 빢; 빢; 빢; 빢; ) HANGUL SYLLABLE BBAGG
+BE63;BE63;1108 1161 11AA;BE63;1108 1161 11AA; # (빣; 빣; 빣; 빣; 빣; ) HANGUL SYLLABLE BBAGS
+BE64;BE64;1108 1161 11AB;BE64;1108 1161 11AB; # (빤; 빤; 빤; 빤; 빤; ) HANGUL SYLLABLE BBAN
+BE65;BE65;1108 1161 11AC;BE65;1108 1161 11AC; # (빥; 빥; 빥; 빥; 빥; ) HANGUL SYLLABLE BBANJ
+BE66;BE66;1108 1161 11AD;BE66;1108 1161 11AD; # (빦; 빦; 빦; 빦; 빦; ) HANGUL SYLLABLE BBANH
+BE67;BE67;1108 1161 11AE;BE67;1108 1161 11AE; # (빧; 빧; 빧; 빧; 빧; ) HANGUL SYLLABLE BBAD
+BE68;BE68;1108 1161 11AF;BE68;1108 1161 11AF; # (빨; 빨; 빨; 빨; 빨; ) HANGUL SYLLABLE BBAL
+BE69;BE69;1108 1161 11B0;BE69;1108 1161 11B0; # (빩; 빩; 빩; 빩; 빩; ) HANGUL SYLLABLE BBALG
+BE6A;BE6A;1108 1161 11B1;BE6A;1108 1161 11B1; # (빪; 빪; 빪; 빪; 빪; ) HANGUL SYLLABLE BBALM
+BE6B;BE6B;1108 1161 11B2;BE6B;1108 1161 11B2; # (빫; 빫; 빫; 빫; 빫; ) HANGUL SYLLABLE BBALB
+BE6C;BE6C;1108 1161 11B3;BE6C;1108 1161 11B3; # (빬; 빬; 빬; 빬; 빬; ) HANGUL SYLLABLE BBALS
+BE6D;BE6D;1108 1161 11B4;BE6D;1108 1161 11B4; # (빭; 빭; 빭; 빭; 빭; ) HANGUL SYLLABLE BBALT
+BE6E;BE6E;1108 1161 11B5;BE6E;1108 1161 11B5; # (빮; 빮; 빮; 빮; 빮; ) HANGUL SYLLABLE BBALP
+BE6F;BE6F;1108 1161 11B6;BE6F;1108 1161 11B6; # (빯; 빯; 빯; 빯; 빯; ) HANGUL SYLLABLE BBALH
+BE70;BE70;1108 1161 11B7;BE70;1108 1161 11B7; # (빰; 빰; 빰; 빰; 빰; ) HANGUL SYLLABLE BBAM
+BE71;BE71;1108 1161 11B8;BE71;1108 1161 11B8; # (빱; 빱; 빱; 빱; 빱; ) HANGUL SYLLABLE BBAB
+BE72;BE72;1108 1161 11B9;BE72;1108 1161 11B9; # (빲; 빲; 빲; 빲; 빲; ) HANGUL SYLLABLE BBABS
+BE73;BE73;1108 1161 11BA;BE73;1108 1161 11BA; # (빳; 빳; 빳; 빳; 빳; ) HANGUL SYLLABLE BBAS
+BE74;BE74;1108 1161 11BB;BE74;1108 1161 11BB; # (빴; 빴; 빴; 빴; 빴; ) HANGUL SYLLABLE BBASS
+BE75;BE75;1108 1161 11BC;BE75;1108 1161 11BC; # (빵; 빵; 빵; 빵; 빵; ) HANGUL SYLLABLE BBANG
+BE76;BE76;1108 1161 11BD;BE76;1108 1161 11BD; # (빶; 빶; 빶; 빶; 빶; ) HANGUL SYLLABLE BBAJ
+BE77;BE77;1108 1161 11BE;BE77;1108 1161 11BE; # (빷; 빷; 빷; 빷; 빷; ) HANGUL SYLLABLE BBAC
+BE78;BE78;1108 1161 11BF;BE78;1108 1161 11BF; # (빸; 빸; 빸; 빸; 빸; ) HANGUL SYLLABLE BBAK
+BE79;BE79;1108 1161 11C0;BE79;1108 1161 11C0; # (빹; 빹; 빹; 빹; 빹; ) HANGUL SYLLABLE BBAT
+BE7A;BE7A;1108 1161 11C1;BE7A;1108 1161 11C1; # (빺; 빺; 빠á‡; 빺; 빠á‡; ) HANGUL SYLLABLE BBAP
+BE7B;BE7B;1108 1161 11C2;BE7B;1108 1161 11C2; # (빻; 빻; 빻; 빻; 빻; ) HANGUL SYLLABLE BBAH
+BE7C;BE7C;1108 1162;BE7C;1108 1162; # (빼; 빼; 빼; 빼; 빼; ) HANGUL SYLLABLE BBAE
+BE7D;BE7D;1108 1162 11A8;BE7D;1108 1162 11A8; # (빽; 빽; 빽; 빽; 빽; ) HANGUL SYLLABLE BBAEG
+BE7E;BE7E;1108 1162 11A9;BE7E;1108 1162 11A9; # (빾; 빾; 빾; 빾; 빾; ) HANGUL SYLLABLE BBAEGG
+BE7F;BE7F;1108 1162 11AA;BE7F;1108 1162 11AA; # (빿; 빿; 빿; 빿; 빿; ) HANGUL SYLLABLE BBAEGS
+BE80;BE80;1108 1162 11AB;BE80;1108 1162 11AB; # (뺀; 뺀; 뺀; 뺀; 뺀; ) HANGUL SYLLABLE BBAEN
+BE81;BE81;1108 1162 11AC;BE81;1108 1162 11AC; # (ëº; ëº; 뺁; ëº; 뺁; ) HANGUL SYLLABLE BBAENJ
+BE82;BE82;1108 1162 11AD;BE82;1108 1162 11AD; # (뺂; 뺂; 뺂; 뺂; 뺂; ) HANGUL SYLLABLE BBAENH
+BE83;BE83;1108 1162 11AE;BE83;1108 1162 11AE; # (뺃; 뺃; 뺃; 뺃; 뺃; ) HANGUL SYLLABLE BBAED
+BE84;BE84;1108 1162 11AF;BE84;1108 1162 11AF; # (뺄; 뺄; 뺄; 뺄; 뺄; ) HANGUL SYLLABLE BBAEL
+BE85;BE85;1108 1162 11B0;BE85;1108 1162 11B0; # (뺅; 뺅; 뺅; 뺅; 뺅; ) HANGUL SYLLABLE BBAELG
+BE86;BE86;1108 1162 11B1;BE86;1108 1162 11B1; # (뺆; 뺆; 뺆; 뺆; 뺆; ) HANGUL SYLLABLE BBAELM
+BE87;BE87;1108 1162 11B2;BE87;1108 1162 11B2; # (뺇; 뺇; 뺇; 뺇; 뺇; ) HANGUL SYLLABLE BBAELB
+BE88;BE88;1108 1162 11B3;BE88;1108 1162 11B3; # (뺈; 뺈; 뺈; 뺈; 뺈; ) HANGUL SYLLABLE BBAELS
+BE89;BE89;1108 1162 11B4;BE89;1108 1162 11B4; # (뺉; 뺉; 뺉; 뺉; 뺉; ) HANGUL SYLLABLE BBAELT
+BE8A;BE8A;1108 1162 11B5;BE8A;1108 1162 11B5; # (뺊; 뺊; 뺊; 뺊; 뺊; ) HANGUL SYLLABLE BBAELP
+BE8B;BE8B;1108 1162 11B6;BE8B;1108 1162 11B6; # (뺋; 뺋; 뺋; 뺋; 뺋; ) HANGUL SYLLABLE BBAELH
+BE8C;BE8C;1108 1162 11B7;BE8C;1108 1162 11B7; # (뺌; 뺌; 뺌; 뺌; 뺌; ) HANGUL SYLLABLE BBAEM
+BE8D;BE8D;1108 1162 11B8;BE8D;1108 1162 11B8; # (ëº; ëº; 뺍; ëº; 뺍; ) HANGUL SYLLABLE BBAEB
+BE8E;BE8E;1108 1162 11B9;BE8E;1108 1162 11B9; # (뺎; 뺎; 뺎; 뺎; 뺎; ) HANGUL SYLLABLE BBAEBS
+BE8F;BE8F;1108 1162 11BA;BE8F;1108 1162 11BA; # (ëº; ëº; 뺏; ëº; 뺏; ) HANGUL SYLLABLE BBAES
+BE90;BE90;1108 1162 11BB;BE90;1108 1162 11BB; # (ëº; ëº; 뺐; ëº; 뺐; ) HANGUL SYLLABLE BBAESS
+BE91;BE91;1108 1162 11BC;BE91;1108 1162 11BC; # (뺑; 뺑; 뺑; 뺑; 뺑; ) HANGUL SYLLABLE BBAENG
+BE92;BE92;1108 1162 11BD;BE92;1108 1162 11BD; # (뺒; 뺒; 뺒; 뺒; 뺒; ) HANGUL SYLLABLE BBAEJ
+BE93;BE93;1108 1162 11BE;BE93;1108 1162 11BE; # (뺓; 뺓; 뺓; 뺓; 뺓; ) HANGUL SYLLABLE BBAEC
+BE94;BE94;1108 1162 11BF;BE94;1108 1162 11BF; # (뺔; 뺔; 뺔; 뺔; 뺔; ) HANGUL SYLLABLE BBAEK
+BE95;BE95;1108 1162 11C0;BE95;1108 1162 11C0; # (뺕; 뺕; 뺕; 뺕; 뺕; ) HANGUL SYLLABLE BBAET
+BE96;BE96;1108 1162 11C1;BE96;1108 1162 11C1; # (뺖; 뺖; 빼á‡; 뺖; 빼á‡; ) HANGUL SYLLABLE BBAEP
+BE97;BE97;1108 1162 11C2;BE97;1108 1162 11C2; # (뺗; 뺗; 뺗; 뺗; 뺗; ) HANGUL SYLLABLE BBAEH
+BE98;BE98;1108 1163;BE98;1108 1163; # (뺘; 뺘; 뺘; 뺘; 뺘; ) HANGUL SYLLABLE BBYA
+BE99;BE99;1108 1163 11A8;BE99;1108 1163 11A8; # (뺙; 뺙; 뺙; 뺙; 뺙; ) HANGUL SYLLABLE BBYAG
+BE9A;BE9A;1108 1163 11A9;BE9A;1108 1163 11A9; # (뺚; 뺚; 뺚; 뺚; 뺚; ) HANGUL SYLLABLE BBYAGG
+BE9B;BE9B;1108 1163 11AA;BE9B;1108 1163 11AA; # (뺛; 뺛; 뺛; 뺛; 뺛; ) HANGUL SYLLABLE BBYAGS
+BE9C;BE9C;1108 1163 11AB;BE9C;1108 1163 11AB; # (뺜; 뺜; 뺜; 뺜; 뺜; ) HANGUL SYLLABLE BBYAN
+BE9D;BE9D;1108 1163 11AC;BE9D;1108 1163 11AC; # (ëº; ëº; 뺝; ëº; 뺝; ) HANGUL SYLLABLE BBYANJ
+BE9E;BE9E;1108 1163 11AD;BE9E;1108 1163 11AD; # (뺞; 뺞; 뺞; 뺞; 뺞; ) HANGUL SYLLABLE BBYANH
+BE9F;BE9F;1108 1163 11AE;BE9F;1108 1163 11AE; # (뺟; 뺟; 뺟; 뺟; 뺟; ) HANGUL SYLLABLE BBYAD
+BEA0;BEA0;1108 1163 11AF;BEA0;1108 1163 11AF; # (뺠; 뺠; 뺠; 뺠; 뺠; ) HANGUL SYLLABLE BBYAL
+BEA1;BEA1;1108 1163 11B0;BEA1;1108 1163 11B0; # (뺡; 뺡; 뺡; 뺡; 뺡; ) HANGUL SYLLABLE BBYALG
+BEA2;BEA2;1108 1163 11B1;BEA2;1108 1163 11B1; # (뺢; 뺢; 뺢; 뺢; 뺢; ) HANGUL SYLLABLE BBYALM
+BEA3;BEA3;1108 1163 11B2;BEA3;1108 1163 11B2; # (뺣; 뺣; 뺣; 뺣; 뺣; ) HANGUL SYLLABLE BBYALB
+BEA4;BEA4;1108 1163 11B3;BEA4;1108 1163 11B3; # (뺤; 뺤; 뺤; 뺤; 뺤; ) HANGUL SYLLABLE BBYALS
+BEA5;BEA5;1108 1163 11B4;BEA5;1108 1163 11B4; # (뺥; 뺥; 뺥; 뺥; 뺥; ) HANGUL SYLLABLE BBYALT
+BEA6;BEA6;1108 1163 11B5;BEA6;1108 1163 11B5; # (뺦; 뺦; 뺦; 뺦; 뺦; ) HANGUL SYLLABLE BBYALP
+BEA7;BEA7;1108 1163 11B6;BEA7;1108 1163 11B6; # (뺧; 뺧; 뺧; 뺧; 뺧; ) HANGUL SYLLABLE BBYALH
+BEA8;BEA8;1108 1163 11B7;BEA8;1108 1163 11B7; # (뺨; 뺨; 뺨; 뺨; 뺨; ) HANGUL SYLLABLE BBYAM
+BEA9;BEA9;1108 1163 11B8;BEA9;1108 1163 11B8; # (뺩; 뺩; 뺩; 뺩; 뺩; ) HANGUL SYLLABLE BBYAB
+BEAA;BEAA;1108 1163 11B9;BEAA;1108 1163 11B9; # (뺪; 뺪; 뺪; 뺪; 뺪; ) HANGUL SYLLABLE BBYABS
+BEAB;BEAB;1108 1163 11BA;BEAB;1108 1163 11BA; # (뺫; 뺫; 뺫; 뺫; 뺫; ) HANGUL SYLLABLE BBYAS
+BEAC;BEAC;1108 1163 11BB;BEAC;1108 1163 11BB; # (뺬; 뺬; 뺬; 뺬; 뺬; ) HANGUL SYLLABLE BBYASS
+BEAD;BEAD;1108 1163 11BC;BEAD;1108 1163 11BC; # (뺭; 뺭; 뺭; 뺭; 뺭; ) HANGUL SYLLABLE BBYANG
+BEAE;BEAE;1108 1163 11BD;BEAE;1108 1163 11BD; # (뺮; 뺮; 뺮; 뺮; 뺮; ) HANGUL SYLLABLE BBYAJ
+BEAF;BEAF;1108 1163 11BE;BEAF;1108 1163 11BE; # (뺯; 뺯; 뺯; 뺯; 뺯; ) HANGUL SYLLABLE BBYAC
+BEB0;BEB0;1108 1163 11BF;BEB0;1108 1163 11BF; # (뺰; 뺰; 뺰; 뺰; 뺰; ) HANGUL SYLLABLE BBYAK
+BEB1;BEB1;1108 1163 11C0;BEB1;1108 1163 11C0; # (뺱; 뺱; 뺱; 뺱; 뺱; ) HANGUL SYLLABLE BBYAT
+BEB2;BEB2;1108 1163 11C1;BEB2;1108 1163 11C1; # (뺲; 뺲; 뺘á‡; 뺲; 뺘á‡; ) HANGUL SYLLABLE BBYAP
+BEB3;BEB3;1108 1163 11C2;BEB3;1108 1163 11C2; # (뺳; 뺳; 뺳; 뺳; 뺳; ) HANGUL SYLLABLE BBYAH
+BEB4;BEB4;1108 1164;BEB4;1108 1164; # (뺴; 뺴; 뺴; 뺴; 뺴; ) HANGUL SYLLABLE BBYAE
+BEB5;BEB5;1108 1164 11A8;BEB5;1108 1164 11A8; # (뺵; 뺵; 뺵; 뺵; 뺵; ) HANGUL SYLLABLE BBYAEG
+BEB6;BEB6;1108 1164 11A9;BEB6;1108 1164 11A9; # (뺶; 뺶; 뺶; 뺶; 뺶; ) HANGUL SYLLABLE BBYAEGG
+BEB7;BEB7;1108 1164 11AA;BEB7;1108 1164 11AA; # (뺷; 뺷; 뺷; 뺷; 뺷; ) HANGUL SYLLABLE BBYAEGS
+BEB8;BEB8;1108 1164 11AB;BEB8;1108 1164 11AB; # (뺸; 뺸; 뺸; 뺸; 뺸; ) HANGUL SYLLABLE BBYAEN
+BEB9;BEB9;1108 1164 11AC;BEB9;1108 1164 11AC; # (뺹; 뺹; 뺹; 뺹; 뺹; ) HANGUL SYLLABLE BBYAENJ
+BEBA;BEBA;1108 1164 11AD;BEBA;1108 1164 11AD; # (뺺; 뺺; 뺺; 뺺; 뺺; ) HANGUL SYLLABLE BBYAENH
+BEBB;BEBB;1108 1164 11AE;BEBB;1108 1164 11AE; # (뺻; 뺻; 뺻; 뺻; 뺻; ) HANGUL SYLLABLE BBYAED
+BEBC;BEBC;1108 1164 11AF;BEBC;1108 1164 11AF; # (뺼; 뺼; 뺼; 뺼; 뺼; ) HANGUL SYLLABLE BBYAEL
+BEBD;BEBD;1108 1164 11B0;BEBD;1108 1164 11B0; # (뺽; 뺽; 뺽; 뺽; 뺽; ) HANGUL SYLLABLE BBYAELG
+BEBE;BEBE;1108 1164 11B1;BEBE;1108 1164 11B1; # (뺾; 뺾; 뺾; 뺾; 뺾; ) HANGUL SYLLABLE BBYAELM
+BEBF;BEBF;1108 1164 11B2;BEBF;1108 1164 11B2; # (뺿; 뺿; 뺿; 뺿; 뺿; ) HANGUL SYLLABLE BBYAELB
+BEC0;BEC0;1108 1164 11B3;BEC0;1108 1164 11B3; # (뻀; 뻀; 뻀; 뻀; 뻀; ) HANGUL SYLLABLE BBYAELS
+BEC1;BEC1;1108 1164 11B4;BEC1;1108 1164 11B4; # (ë»; ë»; 뻁; ë»; 뻁; ) HANGUL SYLLABLE BBYAELT
+BEC2;BEC2;1108 1164 11B5;BEC2;1108 1164 11B5; # (뻂; 뻂; 뻂; 뻂; 뻂; ) HANGUL SYLLABLE BBYAELP
+BEC3;BEC3;1108 1164 11B6;BEC3;1108 1164 11B6; # (뻃; 뻃; 뻃; 뻃; 뻃; ) HANGUL SYLLABLE BBYAELH
+BEC4;BEC4;1108 1164 11B7;BEC4;1108 1164 11B7; # (뻄; 뻄; 뻄; 뻄; 뻄; ) HANGUL SYLLABLE BBYAEM
+BEC5;BEC5;1108 1164 11B8;BEC5;1108 1164 11B8; # (뻅; 뻅; 뻅; 뻅; 뻅; ) HANGUL SYLLABLE BBYAEB
+BEC6;BEC6;1108 1164 11B9;BEC6;1108 1164 11B9; # (뻆; 뻆; 뻆; 뻆; 뻆; ) HANGUL SYLLABLE BBYAEBS
+BEC7;BEC7;1108 1164 11BA;BEC7;1108 1164 11BA; # (뻇; 뻇; 뻇; 뻇; 뻇; ) HANGUL SYLLABLE BBYAES
+BEC8;BEC8;1108 1164 11BB;BEC8;1108 1164 11BB; # (뻈; 뻈; 뻈; 뻈; 뻈; ) HANGUL SYLLABLE BBYAESS
+BEC9;BEC9;1108 1164 11BC;BEC9;1108 1164 11BC; # (뻉; 뻉; 뻉; 뻉; 뻉; ) HANGUL SYLLABLE BBYAENG
+BECA;BECA;1108 1164 11BD;BECA;1108 1164 11BD; # (뻊; 뻊; 뻊; 뻊; 뻊; ) HANGUL SYLLABLE BBYAEJ
+BECB;BECB;1108 1164 11BE;BECB;1108 1164 11BE; # (뻋; 뻋; 뻋; 뻋; 뻋; ) HANGUL SYLLABLE BBYAEC
+BECC;BECC;1108 1164 11BF;BECC;1108 1164 11BF; # (뻌; 뻌; 뻌; 뻌; 뻌; ) HANGUL SYLLABLE BBYAEK
+BECD;BECD;1108 1164 11C0;BECD;1108 1164 11C0; # (ë»; ë»; 뻍; ë»; 뻍; ) HANGUL SYLLABLE BBYAET
+BECE;BECE;1108 1164 11C1;BECE;1108 1164 11C1; # (뻎; 뻎; 뺴á‡; 뻎; 뺴á‡; ) HANGUL SYLLABLE BBYAEP
+BECF;BECF;1108 1164 11C2;BECF;1108 1164 11C2; # (ë»; ë»; 뻏; ë»; 뻏; ) HANGUL SYLLABLE BBYAEH
+BED0;BED0;1108 1165;BED0;1108 1165; # (ë»; ë»; 뻐; ë»; 뻐; ) HANGUL SYLLABLE BBEO
+BED1;BED1;1108 1165 11A8;BED1;1108 1165 11A8; # (뻑; 뻑; 뻑; 뻑; 뻑; ) HANGUL SYLLABLE BBEOG
+BED2;BED2;1108 1165 11A9;BED2;1108 1165 11A9; # (뻒; 뻒; 뻒; 뻒; 뻒; ) HANGUL SYLLABLE BBEOGG
+BED3;BED3;1108 1165 11AA;BED3;1108 1165 11AA; # (뻓; 뻓; 뻓; 뻓; 뻓; ) HANGUL SYLLABLE BBEOGS
+BED4;BED4;1108 1165 11AB;BED4;1108 1165 11AB; # (뻔; 뻔; 뻔; 뻔; 뻔; ) HANGUL SYLLABLE BBEON
+BED5;BED5;1108 1165 11AC;BED5;1108 1165 11AC; # (뻕; 뻕; 뻕; 뻕; 뻕; ) HANGUL SYLLABLE BBEONJ
+BED6;BED6;1108 1165 11AD;BED6;1108 1165 11AD; # (뻖; 뻖; 뻖; 뻖; 뻖; ) HANGUL SYLLABLE BBEONH
+BED7;BED7;1108 1165 11AE;BED7;1108 1165 11AE; # (뻗; 뻗; 뻗; 뻗; 뻗; ) HANGUL SYLLABLE BBEOD
+BED8;BED8;1108 1165 11AF;BED8;1108 1165 11AF; # (뻘; 뻘; 뻘; 뻘; 뻘; ) HANGUL SYLLABLE BBEOL
+BED9;BED9;1108 1165 11B0;BED9;1108 1165 11B0; # (뻙; 뻙; 뻙; 뻙; 뻙; ) HANGUL SYLLABLE BBEOLG
+BEDA;BEDA;1108 1165 11B1;BEDA;1108 1165 11B1; # (뻚; 뻚; 뻚; 뻚; 뻚; ) HANGUL SYLLABLE BBEOLM
+BEDB;BEDB;1108 1165 11B2;BEDB;1108 1165 11B2; # (뻛; 뻛; 뻛; 뻛; 뻛; ) HANGUL SYLLABLE BBEOLB
+BEDC;BEDC;1108 1165 11B3;BEDC;1108 1165 11B3; # (뻜; 뻜; 뻜; 뻜; 뻜; ) HANGUL SYLLABLE BBEOLS
+BEDD;BEDD;1108 1165 11B4;BEDD;1108 1165 11B4; # (ë»; ë»; 뻝; ë»; 뻝; ) HANGUL SYLLABLE BBEOLT
+BEDE;BEDE;1108 1165 11B5;BEDE;1108 1165 11B5; # (뻞; 뻞; 뻞; 뻞; 뻞; ) HANGUL SYLLABLE BBEOLP
+BEDF;BEDF;1108 1165 11B6;BEDF;1108 1165 11B6; # (뻟; 뻟; 뻟; 뻟; 뻟; ) HANGUL SYLLABLE BBEOLH
+BEE0;BEE0;1108 1165 11B7;BEE0;1108 1165 11B7; # (뻠; 뻠; 뻠; 뻠; 뻠; ) HANGUL SYLLABLE BBEOM
+BEE1;BEE1;1108 1165 11B8;BEE1;1108 1165 11B8; # (뻡; 뻡; 뻡; 뻡; 뻡; ) HANGUL SYLLABLE BBEOB
+BEE2;BEE2;1108 1165 11B9;BEE2;1108 1165 11B9; # (뻢; 뻢; 뻢; 뻢; 뻢; ) HANGUL SYLLABLE BBEOBS
+BEE3;BEE3;1108 1165 11BA;BEE3;1108 1165 11BA; # (뻣; 뻣; 뻣; 뻣; 뻣; ) HANGUL SYLLABLE BBEOS
+BEE4;BEE4;1108 1165 11BB;BEE4;1108 1165 11BB; # (뻤; 뻤; 뻤; 뻤; 뻤; ) HANGUL SYLLABLE BBEOSS
+BEE5;BEE5;1108 1165 11BC;BEE5;1108 1165 11BC; # (뻥; 뻥; 뻥; 뻥; 뻥; ) HANGUL SYLLABLE BBEONG
+BEE6;BEE6;1108 1165 11BD;BEE6;1108 1165 11BD; # (뻦; 뻦; 뻦; 뻦; 뻦; ) HANGUL SYLLABLE BBEOJ
+BEE7;BEE7;1108 1165 11BE;BEE7;1108 1165 11BE; # (뻧; 뻧; 뻧; 뻧; 뻧; ) HANGUL SYLLABLE BBEOC
+BEE8;BEE8;1108 1165 11BF;BEE8;1108 1165 11BF; # (뻨; 뻨; 뻨; 뻨; 뻨; ) HANGUL SYLLABLE BBEOK
+BEE9;BEE9;1108 1165 11C0;BEE9;1108 1165 11C0; # (뻩; 뻩; 뻩; 뻩; 뻩; ) HANGUL SYLLABLE BBEOT
+BEEA;BEEA;1108 1165 11C1;BEEA;1108 1165 11C1; # (뻪; 뻪; 뻐á‡; 뻪; 뻐á‡; ) HANGUL SYLLABLE BBEOP
+BEEB;BEEB;1108 1165 11C2;BEEB;1108 1165 11C2; # (뻫; 뻫; 뻫; 뻫; 뻫; ) HANGUL SYLLABLE BBEOH
+BEEC;BEEC;1108 1166;BEEC;1108 1166; # (뻬; 뻬; 뻬; 뻬; 뻬; ) HANGUL SYLLABLE BBE
+BEED;BEED;1108 1166 11A8;BEED;1108 1166 11A8; # (뻭; 뻭; 뻭; 뻭; 뻭; ) HANGUL SYLLABLE BBEG
+BEEE;BEEE;1108 1166 11A9;BEEE;1108 1166 11A9; # (뻮; 뻮; 뻮; 뻮; 뻮; ) HANGUL SYLLABLE BBEGG
+BEEF;BEEF;1108 1166 11AA;BEEF;1108 1166 11AA; # (뻯; 뻯; 뻯; 뻯; 뻯; ) HANGUL SYLLABLE BBEGS
+BEF0;BEF0;1108 1166 11AB;BEF0;1108 1166 11AB; # (뻰; 뻰; 뻰; 뻰; 뻰; ) HANGUL SYLLABLE BBEN
+BEF1;BEF1;1108 1166 11AC;BEF1;1108 1166 11AC; # (뻱; 뻱; 뻱; 뻱; 뻱; ) HANGUL SYLLABLE BBENJ
+BEF2;BEF2;1108 1166 11AD;BEF2;1108 1166 11AD; # (뻲; 뻲; 뻲; 뻲; 뻲; ) HANGUL SYLLABLE BBENH
+BEF3;BEF3;1108 1166 11AE;BEF3;1108 1166 11AE; # (뻳; 뻳; 뻳; 뻳; 뻳; ) HANGUL SYLLABLE BBED
+BEF4;BEF4;1108 1166 11AF;BEF4;1108 1166 11AF; # (뻴; 뻴; 뻴; 뻴; 뻴; ) HANGUL SYLLABLE BBEL
+BEF5;BEF5;1108 1166 11B0;BEF5;1108 1166 11B0; # (뻵; 뻵; 뻵; 뻵; 뻵; ) HANGUL SYLLABLE BBELG
+BEF6;BEF6;1108 1166 11B1;BEF6;1108 1166 11B1; # (뻶; 뻶; 뻶; 뻶; 뻶; ) HANGUL SYLLABLE BBELM
+BEF7;BEF7;1108 1166 11B2;BEF7;1108 1166 11B2; # (뻷; 뻷; 뻷; 뻷; 뻷; ) HANGUL SYLLABLE BBELB
+BEF8;BEF8;1108 1166 11B3;BEF8;1108 1166 11B3; # (뻸; 뻸; 뻸; 뻸; 뻸; ) HANGUL SYLLABLE BBELS
+BEF9;BEF9;1108 1166 11B4;BEF9;1108 1166 11B4; # (뻹; 뻹; 뻹; 뻹; 뻹; ) HANGUL SYLLABLE BBELT
+BEFA;BEFA;1108 1166 11B5;BEFA;1108 1166 11B5; # (뻺; 뻺; 뻺; 뻺; 뻺; ) HANGUL SYLLABLE BBELP
+BEFB;BEFB;1108 1166 11B6;BEFB;1108 1166 11B6; # (뻻; 뻻; 뻻; 뻻; 뻻; ) HANGUL SYLLABLE BBELH
+BEFC;BEFC;1108 1166 11B7;BEFC;1108 1166 11B7; # (뻼; 뻼; 뻼; 뻼; 뻼; ) HANGUL SYLLABLE BBEM
+BEFD;BEFD;1108 1166 11B8;BEFD;1108 1166 11B8; # (뻽; 뻽; 뻽; 뻽; 뻽; ) HANGUL SYLLABLE BBEB
+BEFE;BEFE;1108 1166 11B9;BEFE;1108 1166 11B9; # (뻾; 뻾; 뻾; 뻾; 뻾; ) HANGUL SYLLABLE BBEBS
+BEFF;BEFF;1108 1166 11BA;BEFF;1108 1166 11BA; # (뻿; 뻿; 뻿; 뻿; 뻿; ) HANGUL SYLLABLE BBES
+BF00;BF00;1108 1166 11BB;BF00;1108 1166 11BB; # (뼀; 뼀; 뼀; 뼀; 뼀; ) HANGUL SYLLABLE BBESS
+BF01;BF01;1108 1166 11BC;BF01;1108 1166 11BC; # (ë¼; ë¼; 뼁; ë¼; 뼁; ) HANGUL SYLLABLE BBENG
+BF02;BF02;1108 1166 11BD;BF02;1108 1166 11BD; # (뼂; 뼂; 뼂; 뼂; 뼂; ) HANGUL SYLLABLE BBEJ
+BF03;BF03;1108 1166 11BE;BF03;1108 1166 11BE; # (뼃; 뼃; 뼃; 뼃; 뼃; ) HANGUL SYLLABLE BBEC
+BF04;BF04;1108 1166 11BF;BF04;1108 1166 11BF; # (뼄; 뼄; 뼄; 뼄; 뼄; ) HANGUL SYLLABLE BBEK
+BF05;BF05;1108 1166 11C0;BF05;1108 1166 11C0; # (뼅; 뼅; 뼅; 뼅; 뼅; ) HANGUL SYLLABLE BBET
+BF06;BF06;1108 1166 11C1;BF06;1108 1166 11C1; # (뼆; 뼆; 뻬á‡; 뼆; 뻬á‡; ) HANGUL SYLLABLE BBEP
+BF07;BF07;1108 1166 11C2;BF07;1108 1166 11C2; # (뼇; 뼇; 뼇; 뼇; 뼇; ) HANGUL SYLLABLE BBEH
+BF08;BF08;1108 1167;BF08;1108 1167; # (뼈; 뼈; 뼈; 뼈; 뼈; ) HANGUL SYLLABLE BBYEO
+BF09;BF09;1108 1167 11A8;BF09;1108 1167 11A8; # (뼉; 뼉; 뼉; 뼉; 뼉; ) HANGUL SYLLABLE BBYEOG
+BF0A;BF0A;1108 1167 11A9;BF0A;1108 1167 11A9; # (뼊; 뼊; 뼊; 뼊; 뼊; ) HANGUL SYLLABLE BBYEOGG
+BF0B;BF0B;1108 1167 11AA;BF0B;1108 1167 11AA; # (뼋; 뼋; 뼋; 뼋; 뼋; ) HANGUL SYLLABLE BBYEOGS
+BF0C;BF0C;1108 1167 11AB;BF0C;1108 1167 11AB; # (뼌; 뼌; 뼌; 뼌; 뼌; ) HANGUL SYLLABLE BBYEON
+BF0D;BF0D;1108 1167 11AC;BF0D;1108 1167 11AC; # (ë¼; ë¼; 뼍; ë¼; 뼍; ) HANGUL SYLLABLE BBYEONJ
+BF0E;BF0E;1108 1167 11AD;BF0E;1108 1167 11AD; # (뼎; 뼎; 뼎; 뼎; 뼎; ) HANGUL SYLLABLE BBYEONH
+BF0F;BF0F;1108 1167 11AE;BF0F;1108 1167 11AE; # (ë¼; ë¼; 뼏; ë¼; 뼏; ) HANGUL SYLLABLE BBYEOD
+BF10;BF10;1108 1167 11AF;BF10;1108 1167 11AF; # (ë¼; ë¼; 뼐; ë¼; 뼐; ) HANGUL SYLLABLE BBYEOL
+BF11;BF11;1108 1167 11B0;BF11;1108 1167 11B0; # (뼑; 뼑; 뼑; 뼑; 뼑; ) HANGUL SYLLABLE BBYEOLG
+BF12;BF12;1108 1167 11B1;BF12;1108 1167 11B1; # (뼒; 뼒; 뼒; 뼒; 뼒; ) HANGUL SYLLABLE BBYEOLM
+BF13;BF13;1108 1167 11B2;BF13;1108 1167 11B2; # (뼓; 뼓; 뼓; 뼓; 뼓; ) HANGUL SYLLABLE BBYEOLB
+BF14;BF14;1108 1167 11B3;BF14;1108 1167 11B3; # (뼔; 뼔; 뼔; 뼔; 뼔; ) HANGUL SYLLABLE BBYEOLS
+BF15;BF15;1108 1167 11B4;BF15;1108 1167 11B4; # (뼕; 뼕; 뼕; 뼕; 뼕; ) HANGUL SYLLABLE BBYEOLT
+BF16;BF16;1108 1167 11B5;BF16;1108 1167 11B5; # (뼖; 뼖; 뼖; 뼖; 뼖; ) HANGUL SYLLABLE BBYEOLP
+BF17;BF17;1108 1167 11B6;BF17;1108 1167 11B6; # (뼗; 뼗; 뼗; 뼗; 뼗; ) HANGUL SYLLABLE BBYEOLH
+BF18;BF18;1108 1167 11B7;BF18;1108 1167 11B7; # (뼘; 뼘; 뼘; 뼘; 뼘; ) HANGUL SYLLABLE BBYEOM
+BF19;BF19;1108 1167 11B8;BF19;1108 1167 11B8; # (뼙; 뼙; 뼙; 뼙; 뼙; ) HANGUL SYLLABLE BBYEOB
+BF1A;BF1A;1108 1167 11B9;BF1A;1108 1167 11B9; # (뼚; 뼚; 뼚; 뼚; 뼚; ) HANGUL SYLLABLE BBYEOBS
+BF1B;BF1B;1108 1167 11BA;BF1B;1108 1167 11BA; # (뼛; 뼛; 뼛; 뼛; 뼛; ) HANGUL SYLLABLE BBYEOS
+BF1C;BF1C;1108 1167 11BB;BF1C;1108 1167 11BB; # (뼜; 뼜; 뼜; 뼜; 뼜; ) HANGUL SYLLABLE BBYEOSS
+BF1D;BF1D;1108 1167 11BC;BF1D;1108 1167 11BC; # (ë¼; ë¼; 뼝; ë¼; 뼝; ) HANGUL SYLLABLE BBYEONG
+BF1E;BF1E;1108 1167 11BD;BF1E;1108 1167 11BD; # (뼞; 뼞; 뼞; 뼞; 뼞; ) HANGUL SYLLABLE BBYEOJ
+BF1F;BF1F;1108 1167 11BE;BF1F;1108 1167 11BE; # (뼟; 뼟; 뼟; 뼟; 뼟; ) HANGUL SYLLABLE BBYEOC
+BF20;BF20;1108 1167 11BF;BF20;1108 1167 11BF; # (뼠; 뼠; 뼠; 뼠; 뼠; ) HANGUL SYLLABLE BBYEOK
+BF21;BF21;1108 1167 11C0;BF21;1108 1167 11C0; # (뼡; 뼡; 뼡; 뼡; 뼡; ) HANGUL SYLLABLE BBYEOT
+BF22;BF22;1108 1167 11C1;BF22;1108 1167 11C1; # (ë¼¢; ë¼¢; 뼈á‡; ë¼¢; 뼈á‡; ) HANGUL SYLLABLE BBYEOP
+BF23;BF23;1108 1167 11C2;BF23;1108 1167 11C2; # (뼣; 뼣; 뼣; 뼣; 뼣; ) HANGUL SYLLABLE BBYEOH
+BF24;BF24;1108 1168;BF24;1108 1168; # (뼤; 뼤; 뼤; 뼤; 뼤; ) HANGUL SYLLABLE BBYE
+BF25;BF25;1108 1168 11A8;BF25;1108 1168 11A8; # (뼥; 뼥; 뼥; 뼥; 뼥; ) HANGUL SYLLABLE BBYEG
+BF26;BF26;1108 1168 11A9;BF26;1108 1168 11A9; # (뼦; 뼦; 뼦; 뼦; 뼦; ) HANGUL SYLLABLE BBYEGG
+BF27;BF27;1108 1168 11AA;BF27;1108 1168 11AA; # (뼧; 뼧; 뼧; 뼧; 뼧; ) HANGUL SYLLABLE BBYEGS
+BF28;BF28;1108 1168 11AB;BF28;1108 1168 11AB; # (뼨; 뼨; 뼨; 뼨; 뼨; ) HANGUL SYLLABLE BBYEN
+BF29;BF29;1108 1168 11AC;BF29;1108 1168 11AC; # (뼩; 뼩; 뼩; 뼩; 뼩; ) HANGUL SYLLABLE BBYENJ
+BF2A;BF2A;1108 1168 11AD;BF2A;1108 1168 11AD; # (뼪; 뼪; 뼪; 뼪; 뼪; ) HANGUL SYLLABLE BBYENH
+BF2B;BF2B;1108 1168 11AE;BF2B;1108 1168 11AE; # (뼫; 뼫; 뼫; 뼫; 뼫; ) HANGUL SYLLABLE BBYED
+BF2C;BF2C;1108 1168 11AF;BF2C;1108 1168 11AF; # (뼬; 뼬; 뼬; 뼬; 뼬; ) HANGUL SYLLABLE BBYEL
+BF2D;BF2D;1108 1168 11B0;BF2D;1108 1168 11B0; # (뼭; 뼭; 뼭; 뼭; 뼭; ) HANGUL SYLLABLE BBYELG
+BF2E;BF2E;1108 1168 11B1;BF2E;1108 1168 11B1; # (뼮; 뼮; 뼮; 뼮; 뼮; ) HANGUL SYLLABLE BBYELM
+BF2F;BF2F;1108 1168 11B2;BF2F;1108 1168 11B2; # (뼯; 뼯; 뼯; 뼯; 뼯; ) HANGUL SYLLABLE BBYELB
+BF30;BF30;1108 1168 11B3;BF30;1108 1168 11B3; # (뼰; 뼰; 뼰; 뼰; 뼰; ) HANGUL SYLLABLE BBYELS
+BF31;BF31;1108 1168 11B4;BF31;1108 1168 11B4; # (뼱; 뼱; 뼱; 뼱; 뼱; ) HANGUL SYLLABLE BBYELT
+BF32;BF32;1108 1168 11B5;BF32;1108 1168 11B5; # (뼲; 뼲; 뼲; 뼲; 뼲; ) HANGUL SYLLABLE BBYELP
+BF33;BF33;1108 1168 11B6;BF33;1108 1168 11B6; # (뼳; 뼳; 뼳; 뼳; 뼳; ) HANGUL SYLLABLE BBYELH
+BF34;BF34;1108 1168 11B7;BF34;1108 1168 11B7; # (뼴; 뼴; 뼴; 뼴; 뼴; ) HANGUL SYLLABLE BBYEM
+BF35;BF35;1108 1168 11B8;BF35;1108 1168 11B8; # (뼵; 뼵; 뼵; 뼵; 뼵; ) HANGUL SYLLABLE BBYEB
+BF36;BF36;1108 1168 11B9;BF36;1108 1168 11B9; # (뼶; 뼶; 뼶; 뼶; 뼶; ) HANGUL SYLLABLE BBYEBS
+BF37;BF37;1108 1168 11BA;BF37;1108 1168 11BA; # (뼷; 뼷; 뼷; 뼷; 뼷; ) HANGUL SYLLABLE BBYES
+BF38;BF38;1108 1168 11BB;BF38;1108 1168 11BB; # (뼸; 뼸; 뼸; 뼸; 뼸; ) HANGUL SYLLABLE BBYESS
+BF39;BF39;1108 1168 11BC;BF39;1108 1168 11BC; # (뼹; 뼹; 뼹; 뼹; 뼹; ) HANGUL SYLLABLE BBYENG
+BF3A;BF3A;1108 1168 11BD;BF3A;1108 1168 11BD; # (뼺; 뼺; 뼺; 뼺; 뼺; ) HANGUL SYLLABLE BBYEJ
+BF3B;BF3B;1108 1168 11BE;BF3B;1108 1168 11BE; # (뼻; 뼻; 뼻; 뼻; 뼻; ) HANGUL SYLLABLE BBYEC
+BF3C;BF3C;1108 1168 11BF;BF3C;1108 1168 11BF; # (뼼; 뼼; 뼼; 뼼; 뼼; ) HANGUL SYLLABLE BBYEK
+BF3D;BF3D;1108 1168 11C0;BF3D;1108 1168 11C0; # (뼽; 뼽; 뼽; 뼽; 뼽; ) HANGUL SYLLABLE BBYET
+BF3E;BF3E;1108 1168 11C1;BF3E;1108 1168 11C1; # (ë¼¾; ë¼¾; 뼤á‡; ë¼¾; 뼤á‡; ) HANGUL SYLLABLE BBYEP
+BF3F;BF3F;1108 1168 11C2;BF3F;1108 1168 11C2; # (뼿; 뼿; 뼿; 뼿; 뼿; ) HANGUL SYLLABLE BBYEH
+BF40;BF40;1108 1169;BF40;1108 1169; # (뽀; 뽀; 뽀; 뽀; 뽀; ) HANGUL SYLLABLE BBO
+BF41;BF41;1108 1169 11A8;BF41;1108 1169 11A8; # (ë½; ë½; 뽁; ë½; 뽁; ) HANGUL SYLLABLE BBOG
+BF42;BF42;1108 1169 11A9;BF42;1108 1169 11A9; # (뽂; 뽂; 뽂; 뽂; 뽂; ) HANGUL SYLLABLE BBOGG
+BF43;BF43;1108 1169 11AA;BF43;1108 1169 11AA; # (뽃; 뽃; 뽃; 뽃; 뽃; ) HANGUL SYLLABLE BBOGS
+BF44;BF44;1108 1169 11AB;BF44;1108 1169 11AB; # (뽄; 뽄; 뽄; 뽄; 뽄; ) HANGUL SYLLABLE BBON
+BF45;BF45;1108 1169 11AC;BF45;1108 1169 11AC; # (뽅; 뽅; 뽅; 뽅; 뽅; ) HANGUL SYLLABLE BBONJ
+BF46;BF46;1108 1169 11AD;BF46;1108 1169 11AD; # (뽆; 뽆; 뽆; 뽆; 뽆; ) HANGUL SYLLABLE BBONH
+BF47;BF47;1108 1169 11AE;BF47;1108 1169 11AE; # (뽇; 뽇; 뽇; 뽇; 뽇; ) HANGUL SYLLABLE BBOD
+BF48;BF48;1108 1169 11AF;BF48;1108 1169 11AF; # (뽈; 뽈; 뽈; 뽈; 뽈; ) HANGUL SYLLABLE BBOL
+BF49;BF49;1108 1169 11B0;BF49;1108 1169 11B0; # (뽉; 뽉; 뽉; 뽉; 뽉; ) HANGUL SYLLABLE BBOLG
+BF4A;BF4A;1108 1169 11B1;BF4A;1108 1169 11B1; # (뽊; 뽊; 뽊; 뽊; 뽊; ) HANGUL SYLLABLE BBOLM
+BF4B;BF4B;1108 1169 11B2;BF4B;1108 1169 11B2; # (뽋; 뽋; 뽋; 뽋; 뽋; ) HANGUL SYLLABLE BBOLB
+BF4C;BF4C;1108 1169 11B3;BF4C;1108 1169 11B3; # (뽌; 뽌; 뽌; 뽌; 뽌; ) HANGUL SYLLABLE BBOLS
+BF4D;BF4D;1108 1169 11B4;BF4D;1108 1169 11B4; # (ë½; ë½; 뽍; ë½; 뽍; ) HANGUL SYLLABLE BBOLT
+BF4E;BF4E;1108 1169 11B5;BF4E;1108 1169 11B5; # (뽎; 뽎; 뽎; 뽎; 뽎; ) HANGUL SYLLABLE BBOLP
+BF4F;BF4F;1108 1169 11B6;BF4F;1108 1169 11B6; # (ë½; ë½; 뽏; ë½; 뽏; ) HANGUL SYLLABLE BBOLH
+BF50;BF50;1108 1169 11B7;BF50;1108 1169 11B7; # (ë½; ë½; 뽐; ë½; 뽐; ) HANGUL SYLLABLE BBOM
+BF51;BF51;1108 1169 11B8;BF51;1108 1169 11B8; # (뽑; 뽑; 뽑; 뽑; 뽑; ) HANGUL SYLLABLE BBOB
+BF52;BF52;1108 1169 11B9;BF52;1108 1169 11B9; # (뽒; 뽒; 뽒; 뽒; 뽒; ) HANGUL SYLLABLE BBOBS
+BF53;BF53;1108 1169 11BA;BF53;1108 1169 11BA; # (뽓; 뽓; 뽓; 뽓; 뽓; ) HANGUL SYLLABLE BBOS
+BF54;BF54;1108 1169 11BB;BF54;1108 1169 11BB; # (뽔; 뽔; 뽔; 뽔; 뽔; ) HANGUL SYLLABLE BBOSS
+BF55;BF55;1108 1169 11BC;BF55;1108 1169 11BC; # (뽕; 뽕; 뽕; 뽕; 뽕; ) HANGUL SYLLABLE BBONG
+BF56;BF56;1108 1169 11BD;BF56;1108 1169 11BD; # (뽖; 뽖; 뽖; 뽖; 뽖; ) HANGUL SYLLABLE BBOJ
+BF57;BF57;1108 1169 11BE;BF57;1108 1169 11BE; # (뽗; 뽗; 뽗; 뽗; 뽗; ) HANGUL SYLLABLE BBOC
+BF58;BF58;1108 1169 11BF;BF58;1108 1169 11BF; # (뽘; 뽘; 뽘; 뽘; 뽘; ) HANGUL SYLLABLE BBOK
+BF59;BF59;1108 1169 11C0;BF59;1108 1169 11C0; # (뽙; 뽙; 뽙; 뽙; 뽙; ) HANGUL SYLLABLE BBOT
+BF5A;BF5A;1108 1169 11C1;BF5A;1108 1169 11C1; # (뽚; 뽚; 뽀á‡; 뽚; 뽀á‡; ) HANGUL SYLLABLE BBOP
+BF5B;BF5B;1108 1169 11C2;BF5B;1108 1169 11C2; # (뽛; 뽛; 뽛; 뽛; 뽛; ) HANGUL SYLLABLE BBOH
+BF5C;BF5C;1108 116A;BF5C;1108 116A; # (뽜; 뽜; 뽜; 뽜; 뽜; ) HANGUL SYLLABLE BBWA
+BF5D;BF5D;1108 116A 11A8;BF5D;1108 116A 11A8; # (ë½; ë½; 뽝; ë½; 뽝; ) HANGUL SYLLABLE BBWAG
+BF5E;BF5E;1108 116A 11A9;BF5E;1108 116A 11A9; # (뽞; 뽞; 뽞; 뽞; 뽞; ) HANGUL SYLLABLE BBWAGG
+BF5F;BF5F;1108 116A 11AA;BF5F;1108 116A 11AA; # (뽟; 뽟; 뽟; 뽟; 뽟; ) HANGUL SYLLABLE BBWAGS
+BF60;BF60;1108 116A 11AB;BF60;1108 116A 11AB; # (뽠; 뽠; 뽠; 뽠; 뽠; ) HANGUL SYLLABLE BBWAN
+BF61;BF61;1108 116A 11AC;BF61;1108 116A 11AC; # (뽡; 뽡; 뽡; 뽡; 뽡; ) HANGUL SYLLABLE BBWANJ
+BF62;BF62;1108 116A 11AD;BF62;1108 116A 11AD; # (뽢; 뽢; 뽢; 뽢; 뽢; ) HANGUL SYLLABLE BBWANH
+BF63;BF63;1108 116A 11AE;BF63;1108 116A 11AE; # (뽣; 뽣; 뽣; 뽣; 뽣; ) HANGUL SYLLABLE BBWAD
+BF64;BF64;1108 116A 11AF;BF64;1108 116A 11AF; # (뽤; 뽤; 뽤; 뽤; 뽤; ) HANGUL SYLLABLE BBWAL
+BF65;BF65;1108 116A 11B0;BF65;1108 116A 11B0; # (뽥; 뽥; 뽥; 뽥; 뽥; ) HANGUL SYLLABLE BBWALG
+BF66;BF66;1108 116A 11B1;BF66;1108 116A 11B1; # (뽦; 뽦; 뽦; 뽦; 뽦; ) HANGUL SYLLABLE BBWALM
+BF67;BF67;1108 116A 11B2;BF67;1108 116A 11B2; # (뽧; 뽧; 뽧; 뽧; 뽧; ) HANGUL SYLLABLE BBWALB
+BF68;BF68;1108 116A 11B3;BF68;1108 116A 11B3; # (뽨; 뽨; 뽨; 뽨; 뽨; ) HANGUL SYLLABLE BBWALS
+BF69;BF69;1108 116A 11B4;BF69;1108 116A 11B4; # (뽩; 뽩; 뽩; 뽩; 뽩; ) HANGUL SYLLABLE BBWALT
+BF6A;BF6A;1108 116A 11B5;BF6A;1108 116A 11B5; # (뽪; 뽪; 뽪; 뽪; 뽪; ) HANGUL SYLLABLE BBWALP
+BF6B;BF6B;1108 116A 11B6;BF6B;1108 116A 11B6; # (뽫; 뽫; 뽫; 뽫; 뽫; ) HANGUL SYLLABLE BBWALH
+BF6C;BF6C;1108 116A 11B7;BF6C;1108 116A 11B7; # (뽬; 뽬; 뽬; 뽬; 뽬; ) HANGUL SYLLABLE BBWAM
+BF6D;BF6D;1108 116A 11B8;BF6D;1108 116A 11B8; # (뽭; 뽭; 뽭; 뽭; 뽭; ) HANGUL SYLLABLE BBWAB
+BF6E;BF6E;1108 116A 11B9;BF6E;1108 116A 11B9; # (뽮; 뽮; 뽮; 뽮; 뽮; ) HANGUL SYLLABLE BBWABS
+BF6F;BF6F;1108 116A 11BA;BF6F;1108 116A 11BA; # (뽯; 뽯; 뽯; 뽯; 뽯; ) HANGUL SYLLABLE BBWAS
+BF70;BF70;1108 116A 11BB;BF70;1108 116A 11BB; # (뽰; 뽰; 뽰; 뽰; 뽰; ) HANGUL SYLLABLE BBWASS
+BF71;BF71;1108 116A 11BC;BF71;1108 116A 11BC; # (뽱; 뽱; 뽱; 뽱; 뽱; ) HANGUL SYLLABLE BBWANG
+BF72;BF72;1108 116A 11BD;BF72;1108 116A 11BD; # (뽲; 뽲; 뽲; 뽲; 뽲; ) HANGUL SYLLABLE BBWAJ
+BF73;BF73;1108 116A 11BE;BF73;1108 116A 11BE; # (뽳; 뽳; 뽳; 뽳; 뽳; ) HANGUL SYLLABLE BBWAC
+BF74;BF74;1108 116A 11BF;BF74;1108 116A 11BF; # (뽴; 뽴; 뽴; 뽴; 뽴; ) HANGUL SYLLABLE BBWAK
+BF75;BF75;1108 116A 11C0;BF75;1108 116A 11C0; # (뽵; 뽵; 뽵; 뽵; 뽵; ) HANGUL SYLLABLE BBWAT
+BF76;BF76;1108 116A 11C1;BF76;1108 116A 11C1; # (ë½¶; ë½¶; 뽜á‡; ë½¶; 뽜á‡; ) HANGUL SYLLABLE BBWAP
+BF77;BF77;1108 116A 11C2;BF77;1108 116A 11C2; # (뽷; 뽷; 뽷; 뽷; 뽷; ) HANGUL SYLLABLE BBWAH
+BF78;BF78;1108 116B;BF78;1108 116B; # (뽸; 뽸; 뽸; 뽸; 뽸; ) HANGUL SYLLABLE BBWAE
+BF79;BF79;1108 116B 11A8;BF79;1108 116B 11A8; # (뽹; 뽹; 뽹; 뽹; 뽹; ) HANGUL SYLLABLE BBWAEG
+BF7A;BF7A;1108 116B 11A9;BF7A;1108 116B 11A9; # (뽺; 뽺; 뽺; 뽺; 뽺; ) HANGUL SYLLABLE BBWAEGG
+BF7B;BF7B;1108 116B 11AA;BF7B;1108 116B 11AA; # (뽻; 뽻; 뽻; 뽻; 뽻; ) HANGUL SYLLABLE BBWAEGS
+BF7C;BF7C;1108 116B 11AB;BF7C;1108 116B 11AB; # (뽼; 뽼; 뽼; 뽼; 뽼; ) HANGUL SYLLABLE BBWAEN
+BF7D;BF7D;1108 116B 11AC;BF7D;1108 116B 11AC; # (뽽; 뽽; 뽽; 뽽; 뽽; ) HANGUL SYLLABLE BBWAENJ
+BF7E;BF7E;1108 116B 11AD;BF7E;1108 116B 11AD; # (뽾; 뽾; 뽾; 뽾; 뽾; ) HANGUL SYLLABLE BBWAENH
+BF7F;BF7F;1108 116B 11AE;BF7F;1108 116B 11AE; # (뽿; 뽿; 뽿; 뽿; 뽿; ) HANGUL SYLLABLE BBWAED
+BF80;BF80;1108 116B 11AF;BF80;1108 116B 11AF; # (뾀; 뾀; 뾀; 뾀; 뾀; ) HANGUL SYLLABLE BBWAEL
+BF81;BF81;1108 116B 11B0;BF81;1108 116B 11B0; # (ë¾; ë¾; 뾁; ë¾; 뾁; ) HANGUL SYLLABLE BBWAELG
+BF82;BF82;1108 116B 11B1;BF82;1108 116B 11B1; # (뾂; 뾂; 뾂; 뾂; 뾂; ) HANGUL SYLLABLE BBWAELM
+BF83;BF83;1108 116B 11B2;BF83;1108 116B 11B2; # (뾃; 뾃; 뾃; 뾃; 뾃; ) HANGUL SYLLABLE BBWAELB
+BF84;BF84;1108 116B 11B3;BF84;1108 116B 11B3; # (뾄; 뾄; 뾄; 뾄; 뾄; ) HANGUL SYLLABLE BBWAELS
+BF85;BF85;1108 116B 11B4;BF85;1108 116B 11B4; # (뾅; 뾅; 뾅; 뾅; 뾅; ) HANGUL SYLLABLE BBWAELT
+BF86;BF86;1108 116B 11B5;BF86;1108 116B 11B5; # (뾆; 뾆; 뾆; 뾆; 뾆; ) HANGUL SYLLABLE BBWAELP
+BF87;BF87;1108 116B 11B6;BF87;1108 116B 11B6; # (뾇; 뾇; 뾇; 뾇; 뾇; ) HANGUL SYLLABLE BBWAELH
+BF88;BF88;1108 116B 11B7;BF88;1108 116B 11B7; # (뾈; 뾈; 뾈; 뾈; 뾈; ) HANGUL SYLLABLE BBWAEM
+BF89;BF89;1108 116B 11B8;BF89;1108 116B 11B8; # (뾉; 뾉; 뾉; 뾉; 뾉; ) HANGUL SYLLABLE BBWAEB
+BF8A;BF8A;1108 116B 11B9;BF8A;1108 116B 11B9; # (뾊; 뾊; 뾊; 뾊; 뾊; ) HANGUL SYLLABLE BBWAEBS
+BF8B;BF8B;1108 116B 11BA;BF8B;1108 116B 11BA; # (뾋; 뾋; 뾋; 뾋; 뾋; ) HANGUL SYLLABLE BBWAES
+BF8C;BF8C;1108 116B 11BB;BF8C;1108 116B 11BB; # (뾌; 뾌; 뾌; 뾌; 뾌; ) HANGUL SYLLABLE BBWAESS
+BF8D;BF8D;1108 116B 11BC;BF8D;1108 116B 11BC; # (ë¾; ë¾; 뾍; ë¾; 뾍; ) HANGUL SYLLABLE BBWAENG
+BF8E;BF8E;1108 116B 11BD;BF8E;1108 116B 11BD; # (뾎; 뾎; 뾎; 뾎; 뾎; ) HANGUL SYLLABLE BBWAEJ
+BF8F;BF8F;1108 116B 11BE;BF8F;1108 116B 11BE; # (ë¾; ë¾; 뾏; ë¾; 뾏; ) HANGUL SYLLABLE BBWAEC
+BF90;BF90;1108 116B 11BF;BF90;1108 116B 11BF; # (ë¾; ë¾; 뾐; ë¾; 뾐; ) HANGUL SYLLABLE BBWAEK
+BF91;BF91;1108 116B 11C0;BF91;1108 116B 11C0; # (뾑; 뾑; 뾑; 뾑; 뾑; ) HANGUL SYLLABLE BBWAET
+BF92;BF92;1108 116B 11C1;BF92;1108 116B 11C1; # (ë¾’; ë¾’; 뽸á‡; ë¾’; 뽸á‡; ) HANGUL SYLLABLE BBWAEP
+BF93;BF93;1108 116B 11C2;BF93;1108 116B 11C2; # (뾓; 뾓; 뾓; 뾓; 뾓; ) HANGUL SYLLABLE BBWAEH
+BF94;BF94;1108 116C;BF94;1108 116C; # (뾔; 뾔; 뾔; 뾔; 뾔; ) HANGUL SYLLABLE BBOE
+BF95;BF95;1108 116C 11A8;BF95;1108 116C 11A8; # (뾕; 뾕; 뾕; 뾕; 뾕; ) HANGUL SYLLABLE BBOEG
+BF96;BF96;1108 116C 11A9;BF96;1108 116C 11A9; # (뾖; 뾖; 뾖; 뾖; 뾖; ) HANGUL SYLLABLE BBOEGG
+BF97;BF97;1108 116C 11AA;BF97;1108 116C 11AA; # (뾗; 뾗; 뾗; 뾗; 뾗; ) HANGUL SYLLABLE BBOEGS
+BF98;BF98;1108 116C 11AB;BF98;1108 116C 11AB; # (뾘; 뾘; 뾘; 뾘; 뾘; ) HANGUL SYLLABLE BBOEN
+BF99;BF99;1108 116C 11AC;BF99;1108 116C 11AC; # (뾙; 뾙; 뾙; 뾙; 뾙; ) HANGUL SYLLABLE BBOENJ
+BF9A;BF9A;1108 116C 11AD;BF9A;1108 116C 11AD; # (뾚; 뾚; 뾚; 뾚; 뾚; ) HANGUL SYLLABLE BBOENH
+BF9B;BF9B;1108 116C 11AE;BF9B;1108 116C 11AE; # (뾛; 뾛; 뾛; 뾛; 뾛; ) HANGUL SYLLABLE BBOED
+BF9C;BF9C;1108 116C 11AF;BF9C;1108 116C 11AF; # (뾜; 뾜; 뾜; 뾜; 뾜; ) HANGUL SYLLABLE BBOEL
+BF9D;BF9D;1108 116C 11B0;BF9D;1108 116C 11B0; # (ë¾; ë¾; 뾝; ë¾; 뾝; ) HANGUL SYLLABLE BBOELG
+BF9E;BF9E;1108 116C 11B1;BF9E;1108 116C 11B1; # (뾞; 뾞; 뾞; 뾞; 뾞; ) HANGUL SYLLABLE BBOELM
+BF9F;BF9F;1108 116C 11B2;BF9F;1108 116C 11B2; # (뾟; 뾟; 뾟; 뾟; 뾟; ) HANGUL SYLLABLE BBOELB
+BFA0;BFA0;1108 116C 11B3;BFA0;1108 116C 11B3; # (뾠; 뾠; 뾠; 뾠; 뾠; ) HANGUL SYLLABLE BBOELS
+BFA1;BFA1;1108 116C 11B4;BFA1;1108 116C 11B4; # (뾡; 뾡; 뾡; 뾡; 뾡; ) HANGUL SYLLABLE BBOELT
+BFA2;BFA2;1108 116C 11B5;BFA2;1108 116C 11B5; # (뾢; 뾢; 뾢; 뾢; 뾢; ) HANGUL SYLLABLE BBOELP
+BFA3;BFA3;1108 116C 11B6;BFA3;1108 116C 11B6; # (뾣; 뾣; 뾣; 뾣; 뾣; ) HANGUL SYLLABLE BBOELH
+BFA4;BFA4;1108 116C 11B7;BFA4;1108 116C 11B7; # (뾤; 뾤; 뾤; 뾤; 뾤; ) HANGUL SYLLABLE BBOEM
+BFA5;BFA5;1108 116C 11B8;BFA5;1108 116C 11B8; # (뾥; 뾥; 뾥; 뾥; 뾥; ) HANGUL SYLLABLE BBOEB
+BFA6;BFA6;1108 116C 11B9;BFA6;1108 116C 11B9; # (뾦; 뾦; 뾦; 뾦; 뾦; ) HANGUL SYLLABLE BBOEBS
+BFA7;BFA7;1108 116C 11BA;BFA7;1108 116C 11BA; # (뾧; 뾧; 뾧; 뾧; 뾧; ) HANGUL SYLLABLE BBOES
+BFA8;BFA8;1108 116C 11BB;BFA8;1108 116C 11BB; # (뾨; 뾨; 뾨; 뾨; 뾨; ) HANGUL SYLLABLE BBOESS
+BFA9;BFA9;1108 116C 11BC;BFA9;1108 116C 11BC; # (뾩; 뾩; 뾩; 뾩; 뾩; ) HANGUL SYLLABLE BBOENG
+BFAA;BFAA;1108 116C 11BD;BFAA;1108 116C 11BD; # (뾪; 뾪; 뾪; 뾪; 뾪; ) HANGUL SYLLABLE BBOEJ
+BFAB;BFAB;1108 116C 11BE;BFAB;1108 116C 11BE; # (뾫; 뾫; 뾫; 뾫; 뾫; ) HANGUL SYLLABLE BBOEC
+BFAC;BFAC;1108 116C 11BF;BFAC;1108 116C 11BF; # (뾬; 뾬; 뾬; 뾬; 뾬; ) HANGUL SYLLABLE BBOEK
+BFAD;BFAD;1108 116C 11C0;BFAD;1108 116C 11C0; # (뾭; 뾭; 뾭; 뾭; 뾭; ) HANGUL SYLLABLE BBOET
+BFAE;BFAE;1108 116C 11C1;BFAE;1108 116C 11C1; # (ë¾®; ë¾®; 뾔á‡; ë¾®; 뾔á‡; ) HANGUL SYLLABLE BBOEP
+BFAF;BFAF;1108 116C 11C2;BFAF;1108 116C 11C2; # (뾯; 뾯; 뾯; 뾯; 뾯; ) HANGUL SYLLABLE BBOEH
+BFB0;BFB0;1108 116D;BFB0;1108 116D; # (뾰; 뾰; 뾰; 뾰; 뾰; ) HANGUL SYLLABLE BBYO
+BFB1;BFB1;1108 116D 11A8;BFB1;1108 116D 11A8; # (뾱; 뾱; 뾱; 뾱; 뾱; ) HANGUL SYLLABLE BBYOG
+BFB2;BFB2;1108 116D 11A9;BFB2;1108 116D 11A9; # (뾲; 뾲; 뾲; 뾲; 뾲; ) HANGUL SYLLABLE BBYOGG
+BFB3;BFB3;1108 116D 11AA;BFB3;1108 116D 11AA; # (뾳; 뾳; 뾳; 뾳; 뾳; ) HANGUL SYLLABLE BBYOGS
+BFB4;BFB4;1108 116D 11AB;BFB4;1108 116D 11AB; # (뾴; 뾴; 뾴; 뾴; 뾴; ) HANGUL SYLLABLE BBYON
+BFB5;BFB5;1108 116D 11AC;BFB5;1108 116D 11AC; # (뾵; 뾵; 뾵; 뾵; 뾵; ) HANGUL SYLLABLE BBYONJ
+BFB6;BFB6;1108 116D 11AD;BFB6;1108 116D 11AD; # (뾶; 뾶; 뾶; 뾶; 뾶; ) HANGUL SYLLABLE BBYONH
+BFB7;BFB7;1108 116D 11AE;BFB7;1108 116D 11AE; # (뾷; 뾷; 뾷; 뾷; 뾷; ) HANGUL SYLLABLE BBYOD
+BFB8;BFB8;1108 116D 11AF;BFB8;1108 116D 11AF; # (뾸; 뾸; 뾸; 뾸; 뾸; ) HANGUL SYLLABLE BBYOL
+BFB9;BFB9;1108 116D 11B0;BFB9;1108 116D 11B0; # (뾹; 뾹; 뾹; 뾹; 뾹; ) HANGUL SYLLABLE BBYOLG
+BFBA;BFBA;1108 116D 11B1;BFBA;1108 116D 11B1; # (뾺; 뾺; 뾺; 뾺; 뾺; ) HANGUL SYLLABLE BBYOLM
+BFBB;BFBB;1108 116D 11B2;BFBB;1108 116D 11B2; # (뾻; 뾻; 뾻; 뾻; 뾻; ) HANGUL SYLLABLE BBYOLB
+BFBC;BFBC;1108 116D 11B3;BFBC;1108 116D 11B3; # (뾼; 뾼; 뾼; 뾼; 뾼; ) HANGUL SYLLABLE BBYOLS
+BFBD;BFBD;1108 116D 11B4;BFBD;1108 116D 11B4; # (뾽; 뾽; 뾽; 뾽; 뾽; ) HANGUL SYLLABLE BBYOLT
+BFBE;BFBE;1108 116D 11B5;BFBE;1108 116D 11B5; # (뾾; 뾾; 뾾; 뾾; 뾾; ) HANGUL SYLLABLE BBYOLP
+BFBF;BFBF;1108 116D 11B6;BFBF;1108 116D 11B6; # (뾿; 뾿; 뾿; 뾿; 뾿; ) HANGUL SYLLABLE BBYOLH
+BFC0;BFC0;1108 116D 11B7;BFC0;1108 116D 11B7; # (뿀; 뿀; 뿀; 뿀; 뿀; ) HANGUL SYLLABLE BBYOM
+BFC1;BFC1;1108 116D 11B8;BFC1;1108 116D 11B8; # (ë¿; ë¿; 뿁; ë¿; 뿁; ) HANGUL SYLLABLE BBYOB
+BFC2;BFC2;1108 116D 11B9;BFC2;1108 116D 11B9; # (뿂; 뿂; 뿂; 뿂; 뿂; ) HANGUL SYLLABLE BBYOBS
+BFC3;BFC3;1108 116D 11BA;BFC3;1108 116D 11BA; # (뿃; 뿃; 뿃; 뿃; 뿃; ) HANGUL SYLLABLE BBYOS
+BFC4;BFC4;1108 116D 11BB;BFC4;1108 116D 11BB; # (뿄; 뿄; 뿄; 뿄; 뿄; ) HANGUL SYLLABLE BBYOSS
+BFC5;BFC5;1108 116D 11BC;BFC5;1108 116D 11BC; # (뿅; 뿅; 뿅; 뿅; 뿅; ) HANGUL SYLLABLE BBYONG
+BFC6;BFC6;1108 116D 11BD;BFC6;1108 116D 11BD; # (뿆; 뿆; 뿆; 뿆; 뿆; ) HANGUL SYLLABLE BBYOJ
+BFC7;BFC7;1108 116D 11BE;BFC7;1108 116D 11BE; # (뿇; 뿇; 뿇; 뿇; 뿇; ) HANGUL SYLLABLE BBYOC
+BFC8;BFC8;1108 116D 11BF;BFC8;1108 116D 11BF; # (뿈; 뿈; 뿈; 뿈; 뿈; ) HANGUL SYLLABLE BBYOK
+BFC9;BFC9;1108 116D 11C0;BFC9;1108 116D 11C0; # (뿉; 뿉; 뿉; 뿉; 뿉; ) HANGUL SYLLABLE BBYOT
+BFCA;BFCA;1108 116D 11C1;BFCA;1108 116D 11C1; # (뿊; 뿊; 뾰á‡; 뿊; 뾰á‡; ) HANGUL SYLLABLE BBYOP
+BFCB;BFCB;1108 116D 11C2;BFCB;1108 116D 11C2; # (뿋; 뿋; 뿋; 뿋; 뿋; ) HANGUL SYLLABLE BBYOH
+BFCC;BFCC;1108 116E;BFCC;1108 116E; # (뿌; 뿌; 뿌; 뿌; 뿌; ) HANGUL SYLLABLE BBU
+BFCD;BFCD;1108 116E 11A8;BFCD;1108 116E 11A8; # (ë¿; ë¿; 뿍; ë¿; 뿍; ) HANGUL SYLLABLE BBUG
+BFCE;BFCE;1108 116E 11A9;BFCE;1108 116E 11A9; # (뿎; 뿎; 뿎; 뿎; 뿎; ) HANGUL SYLLABLE BBUGG
+BFCF;BFCF;1108 116E 11AA;BFCF;1108 116E 11AA; # (ë¿; ë¿; 뿏; ë¿; 뿏; ) HANGUL SYLLABLE BBUGS
+BFD0;BFD0;1108 116E 11AB;BFD0;1108 116E 11AB; # (ë¿; ë¿; 뿐; ë¿; 뿐; ) HANGUL SYLLABLE BBUN
+BFD1;BFD1;1108 116E 11AC;BFD1;1108 116E 11AC; # (뿑; 뿑; 뿑; 뿑; 뿑; ) HANGUL SYLLABLE BBUNJ
+BFD2;BFD2;1108 116E 11AD;BFD2;1108 116E 11AD; # (뿒; 뿒; 뿒; 뿒; 뿒; ) HANGUL SYLLABLE BBUNH
+BFD3;BFD3;1108 116E 11AE;BFD3;1108 116E 11AE; # (뿓; 뿓; 뿓; 뿓; 뿓; ) HANGUL SYLLABLE BBUD
+BFD4;BFD4;1108 116E 11AF;BFD4;1108 116E 11AF; # (뿔; 뿔; 뿔; 뿔; 뿔; ) HANGUL SYLLABLE BBUL
+BFD5;BFD5;1108 116E 11B0;BFD5;1108 116E 11B0; # (뿕; 뿕; 뿕; 뿕; 뿕; ) HANGUL SYLLABLE BBULG
+BFD6;BFD6;1108 116E 11B1;BFD6;1108 116E 11B1; # (뿖; 뿖; 뿖; 뿖; 뿖; ) HANGUL SYLLABLE BBULM
+BFD7;BFD7;1108 116E 11B2;BFD7;1108 116E 11B2; # (뿗; 뿗; 뿗; 뿗; 뿗; ) HANGUL SYLLABLE BBULB
+BFD8;BFD8;1108 116E 11B3;BFD8;1108 116E 11B3; # (뿘; 뿘; 뿘; 뿘; 뿘; ) HANGUL SYLLABLE BBULS
+BFD9;BFD9;1108 116E 11B4;BFD9;1108 116E 11B4; # (뿙; 뿙; 뿙; 뿙; 뿙; ) HANGUL SYLLABLE BBULT
+BFDA;BFDA;1108 116E 11B5;BFDA;1108 116E 11B5; # (뿚; 뿚; 뿚; 뿚; 뿚; ) HANGUL SYLLABLE BBULP
+BFDB;BFDB;1108 116E 11B6;BFDB;1108 116E 11B6; # (뿛; 뿛; 뿛; 뿛; 뿛; ) HANGUL SYLLABLE BBULH
+BFDC;BFDC;1108 116E 11B7;BFDC;1108 116E 11B7; # (뿜; 뿜; 뿜; 뿜; 뿜; ) HANGUL SYLLABLE BBUM
+BFDD;BFDD;1108 116E 11B8;BFDD;1108 116E 11B8; # (ë¿; ë¿; 뿝; ë¿; 뿝; ) HANGUL SYLLABLE BBUB
+BFDE;BFDE;1108 116E 11B9;BFDE;1108 116E 11B9; # (뿞; 뿞; 뿞; 뿞; 뿞; ) HANGUL SYLLABLE BBUBS
+BFDF;BFDF;1108 116E 11BA;BFDF;1108 116E 11BA; # (뿟; 뿟; 뿟; 뿟; 뿟; ) HANGUL SYLLABLE BBUS
+BFE0;BFE0;1108 116E 11BB;BFE0;1108 116E 11BB; # (뿠; 뿠; 뿠; 뿠; 뿠; ) HANGUL SYLLABLE BBUSS
+BFE1;BFE1;1108 116E 11BC;BFE1;1108 116E 11BC; # (뿡; 뿡; 뿡; 뿡; 뿡; ) HANGUL SYLLABLE BBUNG
+BFE2;BFE2;1108 116E 11BD;BFE2;1108 116E 11BD; # (뿢; 뿢; 뿢; 뿢; 뿢; ) HANGUL SYLLABLE BBUJ
+BFE3;BFE3;1108 116E 11BE;BFE3;1108 116E 11BE; # (뿣; 뿣; 뿣; 뿣; 뿣; ) HANGUL SYLLABLE BBUC
+BFE4;BFE4;1108 116E 11BF;BFE4;1108 116E 11BF; # (뿤; 뿤; 뿤; 뿤; 뿤; ) HANGUL SYLLABLE BBUK
+BFE5;BFE5;1108 116E 11C0;BFE5;1108 116E 11C0; # (뿥; 뿥; 뿥; 뿥; 뿥; ) HANGUL SYLLABLE BBUT
+BFE6;BFE6;1108 116E 11C1;BFE6;1108 116E 11C1; # (뿦; 뿦; 뿌á‡; 뿦; 뿌á‡; ) HANGUL SYLLABLE BBUP
+BFE7;BFE7;1108 116E 11C2;BFE7;1108 116E 11C2; # (뿧; 뿧; 뿧; 뿧; 뿧; ) HANGUL SYLLABLE BBUH
+BFE8;BFE8;1108 116F;BFE8;1108 116F; # (뿨; 뿨; 뿨; 뿨; 뿨; ) HANGUL SYLLABLE BBWEO
+BFE9;BFE9;1108 116F 11A8;BFE9;1108 116F 11A8; # (뿩; 뿩; 뿩; 뿩; 뿩; ) HANGUL SYLLABLE BBWEOG
+BFEA;BFEA;1108 116F 11A9;BFEA;1108 116F 11A9; # (뿪; 뿪; 뿪; 뿪; 뿪; ) HANGUL SYLLABLE BBWEOGG
+BFEB;BFEB;1108 116F 11AA;BFEB;1108 116F 11AA; # (뿫; 뿫; 뿫; 뿫; 뿫; ) HANGUL SYLLABLE BBWEOGS
+BFEC;BFEC;1108 116F 11AB;BFEC;1108 116F 11AB; # (뿬; 뿬; 뿬; 뿬; 뿬; ) HANGUL SYLLABLE BBWEON
+BFED;BFED;1108 116F 11AC;BFED;1108 116F 11AC; # (뿭; 뿭; 뿭; 뿭; 뿭; ) HANGUL SYLLABLE BBWEONJ
+BFEE;BFEE;1108 116F 11AD;BFEE;1108 116F 11AD; # (뿮; 뿮; 뿮; 뿮; 뿮; ) HANGUL SYLLABLE BBWEONH
+BFEF;BFEF;1108 116F 11AE;BFEF;1108 116F 11AE; # (뿯; 뿯; 뿯; 뿯; 뿯; ) HANGUL SYLLABLE BBWEOD
+BFF0;BFF0;1108 116F 11AF;BFF0;1108 116F 11AF; # (뿰; 뿰; 뿰; 뿰; 뿰; ) HANGUL SYLLABLE BBWEOL
+BFF1;BFF1;1108 116F 11B0;BFF1;1108 116F 11B0; # (뿱; 뿱; 뿱; 뿱; 뿱; ) HANGUL SYLLABLE BBWEOLG
+BFF2;BFF2;1108 116F 11B1;BFF2;1108 116F 11B1; # (뿲; 뿲; 뿲; 뿲; 뿲; ) HANGUL SYLLABLE BBWEOLM
+BFF3;BFF3;1108 116F 11B2;BFF3;1108 116F 11B2; # (뿳; 뿳; 뿳; 뿳; 뿳; ) HANGUL SYLLABLE BBWEOLB
+BFF4;BFF4;1108 116F 11B3;BFF4;1108 116F 11B3; # (뿴; 뿴; 뿴; 뿴; 뿴; ) HANGUL SYLLABLE BBWEOLS
+BFF5;BFF5;1108 116F 11B4;BFF5;1108 116F 11B4; # (뿵; 뿵; 뿵; 뿵; 뿵; ) HANGUL SYLLABLE BBWEOLT
+BFF6;BFF6;1108 116F 11B5;BFF6;1108 116F 11B5; # (뿶; 뿶; 뿶; 뿶; 뿶; ) HANGUL SYLLABLE BBWEOLP
+BFF7;BFF7;1108 116F 11B6;BFF7;1108 116F 11B6; # (뿷; 뿷; 뿷; 뿷; 뿷; ) HANGUL SYLLABLE BBWEOLH
+BFF8;BFF8;1108 116F 11B7;BFF8;1108 116F 11B7; # (뿸; 뿸; 뿸; 뿸; 뿸; ) HANGUL SYLLABLE BBWEOM
+BFF9;BFF9;1108 116F 11B8;BFF9;1108 116F 11B8; # (뿹; 뿹; 뿹; 뿹; 뿹; ) HANGUL SYLLABLE BBWEOB
+BFFA;BFFA;1108 116F 11B9;BFFA;1108 116F 11B9; # (뿺; 뿺; 뿺; 뿺; 뿺; ) HANGUL SYLLABLE BBWEOBS
+BFFB;BFFB;1108 116F 11BA;BFFB;1108 116F 11BA; # (뿻; 뿻; 뿻; 뿻; 뿻; ) HANGUL SYLLABLE BBWEOS
+BFFC;BFFC;1108 116F 11BB;BFFC;1108 116F 11BB; # (뿼; 뿼; 뿼; 뿼; 뿼; ) HANGUL SYLLABLE BBWEOSS
+BFFD;BFFD;1108 116F 11BC;BFFD;1108 116F 11BC; # (뿽; 뿽; 뿽; 뿽; 뿽; ) HANGUL SYLLABLE BBWEONG
+BFFE;BFFE;1108 116F 11BD;BFFE;1108 116F 11BD; # (뿾; 뿾; 뿾; 뿾; 뿾; ) HANGUL SYLLABLE BBWEOJ
+BFFF;BFFF;1108 116F 11BE;BFFF;1108 116F 11BE; # (뿿; 뿿; 뿿; 뿿; 뿿; ) HANGUL SYLLABLE BBWEOC
+C000;C000;1108 116F 11BF;C000;1108 116F 11BF; # (쀀; 쀀; 쀀; 쀀; 쀀; ) HANGUL SYLLABLE BBWEOK
+C001;C001;1108 116F 11C0;C001;1108 116F 11C0; # (ì€; ì€; 쀁; ì€; 쀁; ) HANGUL SYLLABLE BBWEOT
+C002;C002;1108 116F 11C1;C002;1108 116F 11C1; # (쀂; 쀂; 뿨á‡; 쀂; 뿨á‡; ) HANGUL SYLLABLE BBWEOP
+C003;C003;1108 116F 11C2;C003;1108 116F 11C2; # (쀃; 쀃; 쀃; 쀃; 쀃; ) HANGUL SYLLABLE BBWEOH
+C004;C004;1108 1170;C004;1108 1170; # (쀄; 쀄; 쀄; 쀄; 쀄; ) HANGUL SYLLABLE BBWE
+C005;C005;1108 1170 11A8;C005;1108 1170 11A8; # (쀅; 쀅; 쀅; 쀅; 쀅; ) HANGUL SYLLABLE BBWEG
+C006;C006;1108 1170 11A9;C006;1108 1170 11A9; # (쀆; 쀆; 쀆; 쀆; 쀆; ) HANGUL SYLLABLE BBWEGG
+C007;C007;1108 1170 11AA;C007;1108 1170 11AA; # (쀇; 쀇; 쀇; 쀇; 쀇; ) HANGUL SYLLABLE BBWEGS
+C008;C008;1108 1170 11AB;C008;1108 1170 11AB; # (쀈; 쀈; 쀈; 쀈; 쀈; ) HANGUL SYLLABLE BBWEN
+C009;C009;1108 1170 11AC;C009;1108 1170 11AC; # (쀉; 쀉; 쀉; 쀉; 쀉; ) HANGUL SYLLABLE BBWENJ
+C00A;C00A;1108 1170 11AD;C00A;1108 1170 11AD; # (쀊; 쀊; 쀊; 쀊; 쀊; ) HANGUL SYLLABLE BBWENH
+C00B;C00B;1108 1170 11AE;C00B;1108 1170 11AE; # (쀋; 쀋; 쀋; 쀋; 쀋; ) HANGUL SYLLABLE BBWED
+C00C;C00C;1108 1170 11AF;C00C;1108 1170 11AF; # (쀌; 쀌; 쀌; 쀌; 쀌; ) HANGUL SYLLABLE BBWEL
+C00D;C00D;1108 1170 11B0;C00D;1108 1170 11B0; # (ì€; ì€; 쀍; ì€; 쀍; ) HANGUL SYLLABLE BBWELG
+C00E;C00E;1108 1170 11B1;C00E;1108 1170 11B1; # (쀎; 쀎; 쀎; 쀎; 쀎; ) HANGUL SYLLABLE BBWELM
+C00F;C00F;1108 1170 11B2;C00F;1108 1170 11B2; # (ì€; ì€; 쀏; ì€; 쀏; ) HANGUL SYLLABLE BBWELB
+C010;C010;1108 1170 11B3;C010;1108 1170 11B3; # (ì€; ì€; 쀐; ì€; 쀐; ) HANGUL SYLLABLE BBWELS
+C011;C011;1108 1170 11B4;C011;1108 1170 11B4; # (쀑; 쀑; 쀑; 쀑; 쀑; ) HANGUL SYLLABLE BBWELT
+C012;C012;1108 1170 11B5;C012;1108 1170 11B5; # (쀒; 쀒; 쀒; 쀒; 쀒; ) HANGUL SYLLABLE BBWELP
+C013;C013;1108 1170 11B6;C013;1108 1170 11B6; # (쀓; 쀓; 쀓; 쀓; 쀓; ) HANGUL SYLLABLE BBWELH
+C014;C014;1108 1170 11B7;C014;1108 1170 11B7; # (쀔; 쀔; 쀔; 쀔; 쀔; ) HANGUL SYLLABLE BBWEM
+C015;C015;1108 1170 11B8;C015;1108 1170 11B8; # (쀕; 쀕; 쀕; 쀕; 쀕; ) HANGUL SYLLABLE BBWEB
+C016;C016;1108 1170 11B9;C016;1108 1170 11B9; # (쀖; 쀖; 쀖; 쀖; 쀖; ) HANGUL SYLLABLE BBWEBS
+C017;C017;1108 1170 11BA;C017;1108 1170 11BA; # (쀗; 쀗; 쀗; 쀗; 쀗; ) HANGUL SYLLABLE BBWES
+C018;C018;1108 1170 11BB;C018;1108 1170 11BB; # (쀘; 쀘; 쀘; 쀘; 쀘; ) HANGUL SYLLABLE BBWESS
+C019;C019;1108 1170 11BC;C019;1108 1170 11BC; # (쀙; 쀙; 쀙; 쀙; 쀙; ) HANGUL SYLLABLE BBWENG
+C01A;C01A;1108 1170 11BD;C01A;1108 1170 11BD; # (쀚; 쀚; 쀚; 쀚; 쀚; ) HANGUL SYLLABLE BBWEJ
+C01B;C01B;1108 1170 11BE;C01B;1108 1170 11BE; # (쀛; 쀛; 쀛; 쀛; 쀛; ) HANGUL SYLLABLE BBWEC
+C01C;C01C;1108 1170 11BF;C01C;1108 1170 11BF; # (쀜; 쀜; 쀜; 쀜; 쀜; ) HANGUL SYLLABLE BBWEK
+C01D;C01D;1108 1170 11C0;C01D;1108 1170 11C0; # (ì€; ì€; 쀝; ì€; 쀝; ) HANGUL SYLLABLE BBWET
+C01E;C01E;1108 1170 11C1;C01E;1108 1170 11C1; # (쀞; 쀞; 쀄á‡; 쀞; 쀄á‡; ) HANGUL SYLLABLE BBWEP
+C01F;C01F;1108 1170 11C2;C01F;1108 1170 11C2; # (쀟; 쀟; 쀟; 쀟; 쀟; ) HANGUL SYLLABLE BBWEH
+C020;C020;1108 1171;C020;1108 1171; # (쀠; 쀠; 쀠; 쀠; 쀠; ) HANGUL SYLLABLE BBWI
+C021;C021;1108 1171 11A8;C021;1108 1171 11A8; # (쀡; 쀡; 쀡; 쀡; 쀡; ) HANGUL SYLLABLE BBWIG
+C022;C022;1108 1171 11A9;C022;1108 1171 11A9; # (쀢; 쀢; 쀢; 쀢; 쀢; ) HANGUL SYLLABLE BBWIGG
+C023;C023;1108 1171 11AA;C023;1108 1171 11AA; # (쀣; 쀣; 쀣; 쀣; 쀣; ) HANGUL SYLLABLE BBWIGS
+C024;C024;1108 1171 11AB;C024;1108 1171 11AB; # (쀤; 쀤; 쀤; 쀤; 쀤; ) HANGUL SYLLABLE BBWIN
+C025;C025;1108 1171 11AC;C025;1108 1171 11AC; # (쀥; 쀥; 쀥; 쀥; 쀥; ) HANGUL SYLLABLE BBWINJ
+C026;C026;1108 1171 11AD;C026;1108 1171 11AD; # (쀦; 쀦; 쀦; 쀦; 쀦; ) HANGUL SYLLABLE BBWINH
+C027;C027;1108 1171 11AE;C027;1108 1171 11AE; # (쀧; 쀧; 쀧; 쀧; 쀧; ) HANGUL SYLLABLE BBWID
+C028;C028;1108 1171 11AF;C028;1108 1171 11AF; # (쀨; 쀨; 쀨; 쀨; 쀨; ) HANGUL SYLLABLE BBWIL
+C029;C029;1108 1171 11B0;C029;1108 1171 11B0; # (쀩; 쀩; 쀩; 쀩; 쀩; ) HANGUL SYLLABLE BBWILG
+C02A;C02A;1108 1171 11B1;C02A;1108 1171 11B1; # (쀪; 쀪; 쀪; 쀪; 쀪; ) HANGUL SYLLABLE BBWILM
+C02B;C02B;1108 1171 11B2;C02B;1108 1171 11B2; # (쀫; 쀫; 쀫; 쀫; 쀫; ) HANGUL SYLLABLE BBWILB
+C02C;C02C;1108 1171 11B3;C02C;1108 1171 11B3; # (쀬; 쀬; 쀬; 쀬; 쀬; ) HANGUL SYLLABLE BBWILS
+C02D;C02D;1108 1171 11B4;C02D;1108 1171 11B4; # (쀭; 쀭; 쀭; 쀭; 쀭; ) HANGUL SYLLABLE BBWILT
+C02E;C02E;1108 1171 11B5;C02E;1108 1171 11B5; # (쀮; 쀮; 쀮; 쀮; 쀮; ) HANGUL SYLLABLE BBWILP
+C02F;C02F;1108 1171 11B6;C02F;1108 1171 11B6; # (쀯; 쀯; 쀯; 쀯; 쀯; ) HANGUL SYLLABLE BBWILH
+C030;C030;1108 1171 11B7;C030;1108 1171 11B7; # (쀰; 쀰; 쀰; 쀰; 쀰; ) HANGUL SYLLABLE BBWIM
+C031;C031;1108 1171 11B8;C031;1108 1171 11B8; # (쀱; 쀱; 쀱; 쀱; 쀱; ) HANGUL SYLLABLE BBWIB
+C032;C032;1108 1171 11B9;C032;1108 1171 11B9; # (쀲; 쀲; 쀲; 쀲; 쀲; ) HANGUL SYLLABLE BBWIBS
+C033;C033;1108 1171 11BA;C033;1108 1171 11BA; # (쀳; 쀳; 쀳; 쀳; 쀳; ) HANGUL SYLLABLE BBWIS
+C034;C034;1108 1171 11BB;C034;1108 1171 11BB; # (쀴; 쀴; 쀴; 쀴; 쀴; ) HANGUL SYLLABLE BBWISS
+C035;C035;1108 1171 11BC;C035;1108 1171 11BC; # (쀵; 쀵; 쀵; 쀵; 쀵; ) HANGUL SYLLABLE BBWING
+C036;C036;1108 1171 11BD;C036;1108 1171 11BD; # (쀶; 쀶; 쀶; 쀶; 쀶; ) HANGUL SYLLABLE BBWIJ
+C037;C037;1108 1171 11BE;C037;1108 1171 11BE; # (쀷; 쀷; 쀷; 쀷; 쀷; ) HANGUL SYLLABLE BBWIC
+C038;C038;1108 1171 11BF;C038;1108 1171 11BF; # (쀸; 쀸; 쀸; 쀸; 쀸; ) HANGUL SYLLABLE BBWIK
+C039;C039;1108 1171 11C0;C039;1108 1171 11C0; # (쀹; 쀹; 쀹; 쀹; 쀹; ) HANGUL SYLLABLE BBWIT
+C03A;C03A;1108 1171 11C1;C03A;1108 1171 11C1; # (쀺; 쀺; 쀠á‡; 쀺; 쀠á‡; ) HANGUL SYLLABLE BBWIP
+C03B;C03B;1108 1171 11C2;C03B;1108 1171 11C2; # (쀻; 쀻; 쀻; 쀻; 쀻; ) HANGUL SYLLABLE BBWIH
+C03C;C03C;1108 1172;C03C;1108 1172; # (쀼; 쀼; 쀼; 쀼; 쀼; ) HANGUL SYLLABLE BBYU
+C03D;C03D;1108 1172 11A8;C03D;1108 1172 11A8; # (쀽; 쀽; 쀽; 쀽; 쀽; ) HANGUL SYLLABLE BBYUG
+C03E;C03E;1108 1172 11A9;C03E;1108 1172 11A9; # (쀾; 쀾; 쀾; 쀾; 쀾; ) HANGUL SYLLABLE BBYUGG
+C03F;C03F;1108 1172 11AA;C03F;1108 1172 11AA; # (쀿; 쀿; 쀿; 쀿; 쀿; ) HANGUL SYLLABLE BBYUGS
+C040;C040;1108 1172 11AB;C040;1108 1172 11AB; # (ì€; ì€; 쁀; ì€; 쁀; ) HANGUL SYLLABLE BBYUN
+C041;C041;1108 1172 11AC;C041;1108 1172 11AC; # (ì; ì; 쁁; ì; 쁁; ) HANGUL SYLLABLE BBYUNJ
+C042;C042;1108 1172 11AD;C042;1108 1172 11AD; # (ì‚; ì‚; 쁂; ì‚; 쁂; ) HANGUL SYLLABLE BBYUNH
+C043;C043;1108 1172 11AE;C043;1108 1172 11AE; # (ìƒ; ìƒ; 쁃; ìƒ; 쁃; ) HANGUL SYLLABLE BBYUD
+C044;C044;1108 1172 11AF;C044;1108 1172 11AF; # (ì„; ì„; 쁄; ì„; 쁄; ) HANGUL SYLLABLE BBYUL
+C045;C045;1108 1172 11B0;C045;1108 1172 11B0; # (ì…; ì…; 쁅; ì…; 쁅; ) HANGUL SYLLABLE BBYULG
+C046;C046;1108 1172 11B1;C046;1108 1172 11B1; # (ì†; ì†; 쁆; ì†; 쁆; ) HANGUL SYLLABLE BBYULM
+C047;C047;1108 1172 11B2;C047;1108 1172 11B2; # (ì‡; ì‡; 쁇; ì‡; 쁇; ) HANGUL SYLLABLE BBYULB
+C048;C048;1108 1172 11B3;C048;1108 1172 11B3; # (ìˆ; ìˆ; 쁈; ìˆ; 쁈; ) HANGUL SYLLABLE BBYULS
+C049;C049;1108 1172 11B4;C049;1108 1172 11B4; # (ì‰; ì‰; 쁉; ì‰; 쁉; ) HANGUL SYLLABLE BBYULT
+C04A;C04A;1108 1172 11B5;C04A;1108 1172 11B5; # (ìŠ; ìŠ; 쁊; ìŠ; 쁊; ) HANGUL SYLLABLE BBYULP
+C04B;C04B;1108 1172 11B6;C04B;1108 1172 11B6; # (ì‹; ì‹; 쁋; ì‹; 쁋; ) HANGUL SYLLABLE BBYULH
+C04C;C04C;1108 1172 11B7;C04C;1108 1172 11B7; # (ìŒ; ìŒ; 쁌; ìŒ; 쁌; ) HANGUL SYLLABLE BBYUM
+C04D;C04D;1108 1172 11B8;C04D;1108 1172 11B8; # (ì; ì; 쁍; ì; 쁍; ) HANGUL SYLLABLE BBYUB
+C04E;C04E;1108 1172 11B9;C04E;1108 1172 11B9; # (ìŽ; ìŽ; 쁎; ìŽ; 쁎; ) HANGUL SYLLABLE BBYUBS
+C04F;C04F;1108 1172 11BA;C04F;1108 1172 11BA; # (ì; ì; 쁏; ì; 쁏; ) HANGUL SYLLABLE BBYUS
+C050;C050;1108 1172 11BB;C050;1108 1172 11BB; # (ì; ì; 쁐; ì; 쁐; ) HANGUL SYLLABLE BBYUSS
+C051;C051;1108 1172 11BC;C051;1108 1172 11BC; # (ì‘; ì‘; 쁑; ì‘; 쁑; ) HANGUL SYLLABLE BBYUNG
+C052;C052;1108 1172 11BD;C052;1108 1172 11BD; # (ì’; ì’; 쁒; ì’; 쁒; ) HANGUL SYLLABLE BBYUJ
+C053;C053;1108 1172 11BE;C053;1108 1172 11BE; # (ì“; ì“; 쁓; ì“; 쁓; ) HANGUL SYLLABLE BBYUC
+C054;C054;1108 1172 11BF;C054;1108 1172 11BF; # (ì”; ì”; 쁔; ì”; 쁔; ) HANGUL SYLLABLE BBYUK
+C055;C055;1108 1172 11C0;C055;1108 1172 11C0; # (ì•; ì•; 쁕; ì•; 쁕; ) HANGUL SYLLABLE BBYUT
+C056;C056;1108 1172 11C1;C056;1108 1172 11C1; # (ì–; ì–; 쀼á‡; ì–; 쀼á‡; ) HANGUL SYLLABLE BBYUP
+C057;C057;1108 1172 11C2;C057;1108 1172 11C2; # (ì—; ì—; 쁗; ì—; 쁗; ) HANGUL SYLLABLE BBYUH
+C058;C058;1108 1173;C058;1108 1173; # (ì˜; ì˜; 쁘; ì˜; 쁘; ) HANGUL SYLLABLE BBEU
+C059;C059;1108 1173 11A8;C059;1108 1173 11A8; # (ì™; ì™; 쁙; ì™; 쁙; ) HANGUL SYLLABLE BBEUG
+C05A;C05A;1108 1173 11A9;C05A;1108 1173 11A9; # (ìš; ìš; 쁚; ìš; 쁚; ) HANGUL SYLLABLE BBEUGG
+C05B;C05B;1108 1173 11AA;C05B;1108 1173 11AA; # (ì›; ì›; 쁛; ì›; 쁛; ) HANGUL SYLLABLE BBEUGS
+C05C;C05C;1108 1173 11AB;C05C;1108 1173 11AB; # (ìœ; ìœ; 쁜; ìœ; 쁜; ) HANGUL SYLLABLE BBEUN
+C05D;C05D;1108 1173 11AC;C05D;1108 1173 11AC; # (ì; ì; 쁝; ì; 쁝; ) HANGUL SYLLABLE BBEUNJ
+C05E;C05E;1108 1173 11AD;C05E;1108 1173 11AD; # (ìž; ìž; 쁞; ìž; 쁞; ) HANGUL SYLLABLE BBEUNH
+C05F;C05F;1108 1173 11AE;C05F;1108 1173 11AE; # (ìŸ; ìŸ; 쁟; ìŸ; 쁟; ) HANGUL SYLLABLE BBEUD
+C060;C060;1108 1173 11AF;C060;1108 1173 11AF; # (ì ; ì ; 쁠; ì ; 쁠; ) HANGUL SYLLABLE BBEUL
+C061;C061;1108 1173 11B0;C061;1108 1173 11B0; # (ì¡; ì¡; 쁡; ì¡; 쁡; ) HANGUL SYLLABLE BBEULG
+C062;C062;1108 1173 11B1;C062;1108 1173 11B1; # (ì¢; ì¢; 쁢; ì¢; 쁢; ) HANGUL SYLLABLE BBEULM
+C063;C063;1108 1173 11B2;C063;1108 1173 11B2; # (ì£; ì£; 쁣; ì£; 쁣; ) HANGUL SYLLABLE BBEULB
+C064;C064;1108 1173 11B3;C064;1108 1173 11B3; # (ì¤; ì¤; 쁤; ì¤; 쁤; ) HANGUL SYLLABLE BBEULS
+C065;C065;1108 1173 11B4;C065;1108 1173 11B4; # (ì¥; ì¥; 쁥; ì¥; 쁥; ) HANGUL SYLLABLE BBEULT
+C066;C066;1108 1173 11B5;C066;1108 1173 11B5; # (ì¦; ì¦; 쁦; ì¦; 쁦; ) HANGUL SYLLABLE BBEULP
+C067;C067;1108 1173 11B6;C067;1108 1173 11B6; # (ì§; ì§; 쁧; ì§; 쁧; ) HANGUL SYLLABLE BBEULH
+C068;C068;1108 1173 11B7;C068;1108 1173 11B7; # (ì¨; ì¨; 쁨; ì¨; 쁨; ) HANGUL SYLLABLE BBEUM
+C069;C069;1108 1173 11B8;C069;1108 1173 11B8; # (ì©; ì©; 쁩; ì©; 쁩; ) HANGUL SYLLABLE BBEUB
+C06A;C06A;1108 1173 11B9;C06A;1108 1173 11B9; # (ìª; ìª; 쁪; ìª; 쁪; ) HANGUL SYLLABLE BBEUBS
+C06B;C06B;1108 1173 11BA;C06B;1108 1173 11BA; # (ì«; ì«; 쁫; ì«; 쁫; ) HANGUL SYLLABLE BBEUS
+C06C;C06C;1108 1173 11BB;C06C;1108 1173 11BB; # (ì¬; ì¬; 쁬; ì¬; 쁬; ) HANGUL SYLLABLE BBEUSS
+C06D;C06D;1108 1173 11BC;C06D;1108 1173 11BC; # (ì­; ì­; 쁭; ì­; 쁭; ) HANGUL SYLLABLE BBEUNG
+C06E;C06E;1108 1173 11BD;C06E;1108 1173 11BD; # (ì®; ì®; 쁮; ì®; 쁮; ) HANGUL SYLLABLE BBEUJ
+C06F;C06F;1108 1173 11BE;C06F;1108 1173 11BE; # (ì¯; ì¯; 쁯; ì¯; 쁯; ) HANGUL SYLLABLE BBEUC
+C070;C070;1108 1173 11BF;C070;1108 1173 11BF; # (ì°; ì°; 쁰; ì°; 쁰; ) HANGUL SYLLABLE BBEUK
+C071;C071;1108 1173 11C0;C071;1108 1173 11C0; # (ì±; ì±; 쁱; ì±; 쁱; ) HANGUL SYLLABLE BBEUT
+C072;C072;1108 1173 11C1;C072;1108 1173 11C1; # (ì²; ì²; 쁘á‡; ì²; 쁘á‡; ) HANGUL SYLLABLE BBEUP
+C073;C073;1108 1173 11C2;C073;1108 1173 11C2; # (ì³; ì³; 쁳; ì³; 쁳; ) HANGUL SYLLABLE BBEUH
+C074;C074;1108 1174;C074;1108 1174; # (ì´; ì´; 쁴; ì´; 쁴; ) HANGUL SYLLABLE BBYI
+C075;C075;1108 1174 11A8;C075;1108 1174 11A8; # (ìµ; ìµ; 쁵; ìµ; 쁵; ) HANGUL SYLLABLE BBYIG
+C076;C076;1108 1174 11A9;C076;1108 1174 11A9; # (ì¶; ì¶; 쁶; ì¶; 쁶; ) HANGUL SYLLABLE BBYIGG
+C077;C077;1108 1174 11AA;C077;1108 1174 11AA; # (ì·; ì·; 쁷; ì·; 쁷; ) HANGUL SYLLABLE BBYIGS
+C078;C078;1108 1174 11AB;C078;1108 1174 11AB; # (ì¸; ì¸; 쁸; ì¸; 쁸; ) HANGUL SYLLABLE BBYIN
+C079;C079;1108 1174 11AC;C079;1108 1174 11AC; # (ì¹; ì¹; 쁹; ì¹; 쁹; ) HANGUL SYLLABLE BBYINJ
+C07A;C07A;1108 1174 11AD;C07A;1108 1174 11AD; # (ìº; ìº; 쁺; ìº; 쁺; ) HANGUL SYLLABLE BBYINH
+C07B;C07B;1108 1174 11AE;C07B;1108 1174 11AE; # (ì»; ì»; 쁻; ì»; 쁻; ) HANGUL SYLLABLE BBYID
+C07C;C07C;1108 1174 11AF;C07C;1108 1174 11AF; # (ì¼; ì¼; 쁼; ì¼; 쁼; ) HANGUL SYLLABLE BBYIL
+C07D;C07D;1108 1174 11B0;C07D;1108 1174 11B0; # (ì½; ì½; 쁽; ì½; 쁽; ) HANGUL SYLLABLE BBYILG
+C07E;C07E;1108 1174 11B1;C07E;1108 1174 11B1; # (ì¾; ì¾; 쁾; ì¾; 쁾; ) HANGUL SYLLABLE BBYILM
+C07F;C07F;1108 1174 11B2;C07F;1108 1174 11B2; # (ì¿; ì¿; 쁿; ì¿; 쁿; ) HANGUL SYLLABLE BBYILB
+C080;C080;1108 1174 11B3;C080;1108 1174 11B3; # (삀; 삀; 삀; 삀; 삀; ) HANGUL SYLLABLE BBYILS
+C081;C081;1108 1174 11B4;C081;1108 1174 11B4; # (ì‚; ì‚; 삁; ì‚; 삁; ) HANGUL SYLLABLE BBYILT
+C082;C082;1108 1174 11B5;C082;1108 1174 11B5; # (삂; 삂; 삂; 삂; 삂; ) HANGUL SYLLABLE BBYILP
+C083;C083;1108 1174 11B6;C083;1108 1174 11B6; # (삃; 삃; 삃; 삃; 삃; ) HANGUL SYLLABLE BBYILH
+C084;C084;1108 1174 11B7;C084;1108 1174 11B7; # (삄; 삄; 삄; 삄; 삄; ) HANGUL SYLLABLE BBYIM
+C085;C085;1108 1174 11B8;C085;1108 1174 11B8; # (삅; 삅; 삅; 삅; 삅; ) HANGUL SYLLABLE BBYIB
+C086;C086;1108 1174 11B9;C086;1108 1174 11B9; # (삆; 삆; 삆; 삆; 삆; ) HANGUL SYLLABLE BBYIBS
+C087;C087;1108 1174 11BA;C087;1108 1174 11BA; # (삇; 삇; 삇; 삇; 삇; ) HANGUL SYLLABLE BBYIS
+C088;C088;1108 1174 11BB;C088;1108 1174 11BB; # (삈; 삈; 삈; 삈; 삈; ) HANGUL SYLLABLE BBYISS
+C089;C089;1108 1174 11BC;C089;1108 1174 11BC; # (삉; 삉; 삉; 삉; 삉; ) HANGUL SYLLABLE BBYING
+C08A;C08A;1108 1174 11BD;C08A;1108 1174 11BD; # (삊; 삊; 삊; 삊; 삊; ) HANGUL SYLLABLE BBYIJ
+C08B;C08B;1108 1174 11BE;C08B;1108 1174 11BE; # (삋; 삋; 삋; 삋; 삋; ) HANGUL SYLLABLE BBYIC
+C08C;C08C;1108 1174 11BF;C08C;1108 1174 11BF; # (삌; 삌; 삌; 삌; 삌; ) HANGUL SYLLABLE BBYIK
+C08D;C08D;1108 1174 11C0;C08D;1108 1174 11C0; # (ì‚; ì‚; 삍; ì‚; 삍; ) HANGUL SYLLABLE BBYIT
+C08E;C08E;1108 1174 11C1;C08E;1108 1174 11C1; # (삎; 삎; 쁴á‡; 삎; 쁴á‡; ) HANGUL SYLLABLE BBYIP
+C08F;C08F;1108 1174 11C2;C08F;1108 1174 11C2; # (ì‚; ì‚; 삏; ì‚; 삏; ) HANGUL SYLLABLE BBYIH
+C090;C090;1108 1175;C090;1108 1175; # (ì‚; ì‚; 삐; ì‚; 삐; ) HANGUL SYLLABLE BBI
+C091;C091;1108 1175 11A8;C091;1108 1175 11A8; # (삑; 삑; 삑; 삑; 삑; ) HANGUL SYLLABLE BBIG
+C092;C092;1108 1175 11A9;C092;1108 1175 11A9; # (삒; 삒; 삒; 삒; 삒; ) HANGUL SYLLABLE BBIGG
+C093;C093;1108 1175 11AA;C093;1108 1175 11AA; # (삓; 삓; 삓; 삓; 삓; ) HANGUL SYLLABLE BBIGS
+C094;C094;1108 1175 11AB;C094;1108 1175 11AB; # (삔; 삔; 삔; 삔; 삔; ) HANGUL SYLLABLE BBIN
+C095;C095;1108 1175 11AC;C095;1108 1175 11AC; # (삕; 삕; 삕; 삕; 삕; ) HANGUL SYLLABLE BBINJ
+C096;C096;1108 1175 11AD;C096;1108 1175 11AD; # (삖; 삖; 삖; 삖; 삖; ) HANGUL SYLLABLE BBINH
+C097;C097;1108 1175 11AE;C097;1108 1175 11AE; # (삗; 삗; 삗; 삗; 삗; ) HANGUL SYLLABLE BBID
+C098;C098;1108 1175 11AF;C098;1108 1175 11AF; # (삘; 삘; 삘; 삘; 삘; ) HANGUL SYLLABLE BBIL
+C099;C099;1108 1175 11B0;C099;1108 1175 11B0; # (삙; 삙; 삙; 삙; 삙; ) HANGUL SYLLABLE BBILG
+C09A;C09A;1108 1175 11B1;C09A;1108 1175 11B1; # (삚; 삚; 삚; 삚; 삚; ) HANGUL SYLLABLE BBILM
+C09B;C09B;1108 1175 11B2;C09B;1108 1175 11B2; # (삛; 삛; 삛; 삛; 삛; ) HANGUL SYLLABLE BBILB
+C09C;C09C;1108 1175 11B3;C09C;1108 1175 11B3; # (삜; 삜; 삜; 삜; 삜; ) HANGUL SYLLABLE BBILS
+C09D;C09D;1108 1175 11B4;C09D;1108 1175 11B4; # (ì‚; ì‚; 삝; ì‚; 삝; ) HANGUL SYLLABLE BBILT
+C09E;C09E;1108 1175 11B5;C09E;1108 1175 11B5; # (삞; 삞; 삞; 삞; 삞; ) HANGUL SYLLABLE BBILP
+C09F;C09F;1108 1175 11B6;C09F;1108 1175 11B6; # (삟; 삟; 삟; 삟; 삟; ) HANGUL SYLLABLE BBILH
+C0A0;C0A0;1108 1175 11B7;C0A0;1108 1175 11B7; # (삠; 삠; 삠; 삠; 삠; ) HANGUL SYLLABLE BBIM
+C0A1;C0A1;1108 1175 11B8;C0A1;1108 1175 11B8; # (삡; 삡; 삡; 삡; 삡; ) HANGUL SYLLABLE BBIB
+C0A2;C0A2;1108 1175 11B9;C0A2;1108 1175 11B9; # (삢; 삢; 삢; 삢; 삢; ) HANGUL SYLLABLE BBIBS
+C0A3;C0A3;1108 1175 11BA;C0A3;1108 1175 11BA; # (삣; 삣; 삣; 삣; 삣; ) HANGUL SYLLABLE BBIS
+C0A4;C0A4;1108 1175 11BB;C0A4;1108 1175 11BB; # (삤; 삤; 삤; 삤; 삤; ) HANGUL SYLLABLE BBISS
+C0A5;C0A5;1108 1175 11BC;C0A5;1108 1175 11BC; # (삥; 삥; 삥; 삥; 삥; ) HANGUL SYLLABLE BBING
+C0A6;C0A6;1108 1175 11BD;C0A6;1108 1175 11BD; # (삦; 삦; 삦; 삦; 삦; ) HANGUL SYLLABLE BBIJ
+C0A7;C0A7;1108 1175 11BE;C0A7;1108 1175 11BE; # (삧; 삧; 삧; 삧; 삧; ) HANGUL SYLLABLE BBIC
+C0A8;C0A8;1108 1175 11BF;C0A8;1108 1175 11BF; # (삨; 삨; 삨; 삨; 삨; ) HANGUL SYLLABLE BBIK
+C0A9;C0A9;1108 1175 11C0;C0A9;1108 1175 11C0; # (삩; 삩; 삩; 삩; 삩; ) HANGUL SYLLABLE BBIT
+C0AA;C0AA;1108 1175 11C1;C0AA;1108 1175 11C1; # (삪; 삪; 삐á‡; 삪; 삐á‡; ) HANGUL SYLLABLE BBIP
+C0AB;C0AB;1108 1175 11C2;C0AB;1108 1175 11C2; # (삫; 삫; 삫; 삫; 삫; ) HANGUL SYLLABLE BBIH
+C0AC;C0AC;1109 1161;C0AC;1109 1161; # (사; 사; 사; 사; 사; ) HANGUL SYLLABLE SA
+C0AD;C0AD;1109 1161 11A8;C0AD;1109 1161 11A8; # (삭; 삭; 삭; 삭; 삭; ) HANGUL SYLLABLE SAG
+C0AE;C0AE;1109 1161 11A9;C0AE;1109 1161 11A9; # (삮; 삮; 삮; 삮; 삮; ) HANGUL SYLLABLE SAGG
+C0AF;C0AF;1109 1161 11AA;C0AF;1109 1161 11AA; # (삯; 삯; 삯; 삯; 삯; ) HANGUL SYLLABLE SAGS
+C0B0;C0B0;1109 1161 11AB;C0B0;1109 1161 11AB; # (산; 산; 산; 산; 산; ) HANGUL SYLLABLE SAN
+C0B1;C0B1;1109 1161 11AC;C0B1;1109 1161 11AC; # (삱; 삱; 삱; 삱; 삱; ) HANGUL SYLLABLE SANJ
+C0B2;C0B2;1109 1161 11AD;C0B2;1109 1161 11AD; # (삲; 삲; 삲; 삲; 삲; ) HANGUL SYLLABLE SANH
+C0B3;C0B3;1109 1161 11AE;C0B3;1109 1161 11AE; # (삳; 삳; 삳; 삳; 삳; ) HANGUL SYLLABLE SAD
+C0B4;C0B4;1109 1161 11AF;C0B4;1109 1161 11AF; # (살; 살; 살; 살; 살; ) HANGUL SYLLABLE SAL
+C0B5;C0B5;1109 1161 11B0;C0B5;1109 1161 11B0; # (삵; 삵; 삵; 삵; 삵; ) HANGUL SYLLABLE SALG
+C0B6;C0B6;1109 1161 11B1;C0B6;1109 1161 11B1; # (삶; 삶; 삶; 삶; 삶; ) HANGUL SYLLABLE SALM
+C0B7;C0B7;1109 1161 11B2;C0B7;1109 1161 11B2; # (삷; 삷; 삷; 삷; 삷; ) HANGUL SYLLABLE SALB
+C0B8;C0B8;1109 1161 11B3;C0B8;1109 1161 11B3; # (삸; 삸; 삸; 삸; 삸; ) HANGUL SYLLABLE SALS
+C0B9;C0B9;1109 1161 11B4;C0B9;1109 1161 11B4; # (삹; 삹; 삹; 삹; 삹; ) HANGUL SYLLABLE SALT
+C0BA;C0BA;1109 1161 11B5;C0BA;1109 1161 11B5; # (삺; 삺; 삺; 삺; 삺; ) HANGUL SYLLABLE SALP
+C0BB;C0BB;1109 1161 11B6;C0BB;1109 1161 11B6; # (삻; 삻; 삻; 삻; 삻; ) HANGUL SYLLABLE SALH
+C0BC;C0BC;1109 1161 11B7;C0BC;1109 1161 11B7; # (삼; 삼; 삼; 삼; 삼; ) HANGUL SYLLABLE SAM
+C0BD;C0BD;1109 1161 11B8;C0BD;1109 1161 11B8; # (삽; 삽; 삽; 삽; 삽; ) HANGUL SYLLABLE SAB
+C0BE;C0BE;1109 1161 11B9;C0BE;1109 1161 11B9; # (삾; 삾; 삾; 삾; 삾; ) HANGUL SYLLABLE SABS
+C0BF;C0BF;1109 1161 11BA;C0BF;1109 1161 11BA; # (삿; 삿; 삿; 삿; 삿; ) HANGUL SYLLABLE SAS
+C0C0;C0C0;1109 1161 11BB;C0C0;1109 1161 11BB; # (샀; 샀; 샀; 샀; 샀; ) HANGUL SYLLABLE SASS
+C0C1;C0C1;1109 1161 11BC;C0C1;1109 1161 11BC; # (ìƒ; ìƒ; 상; ìƒ; 상; ) HANGUL SYLLABLE SANG
+C0C2;C0C2;1109 1161 11BD;C0C2;1109 1161 11BD; # (샂; 샂; 샂; 샂; 샂; ) HANGUL SYLLABLE SAJ
+C0C3;C0C3;1109 1161 11BE;C0C3;1109 1161 11BE; # (샃; 샃; 샃; 샃; 샃; ) HANGUL SYLLABLE SAC
+C0C4;C0C4;1109 1161 11BF;C0C4;1109 1161 11BF; # (샄; 샄; 샄; 샄; 샄; ) HANGUL SYLLABLE SAK
+C0C5;C0C5;1109 1161 11C0;C0C5;1109 1161 11C0; # (샅; 샅; 샅; 샅; 샅; ) HANGUL SYLLABLE SAT
+C0C6;C0C6;1109 1161 11C1;C0C6;1109 1161 11C1; # (샆; 샆; 사á‡; 샆; 사á‡; ) HANGUL SYLLABLE SAP
+C0C7;C0C7;1109 1161 11C2;C0C7;1109 1161 11C2; # (샇; 샇; 샇; 샇; 샇; ) HANGUL SYLLABLE SAH
+C0C8;C0C8;1109 1162;C0C8;1109 1162; # (새; 새; 새; 새; 새; ) HANGUL SYLLABLE SAE
+C0C9;C0C9;1109 1162 11A8;C0C9;1109 1162 11A8; # (색; 색; 색; 색; 색; ) HANGUL SYLLABLE SAEG
+C0CA;C0CA;1109 1162 11A9;C0CA;1109 1162 11A9; # (샊; 샊; 샊; 샊; 샊; ) HANGUL SYLLABLE SAEGG
+C0CB;C0CB;1109 1162 11AA;C0CB;1109 1162 11AA; # (샋; 샋; 샋; 샋; 샋; ) HANGUL SYLLABLE SAEGS
+C0CC;C0CC;1109 1162 11AB;C0CC;1109 1162 11AB; # (샌; 샌; 샌; 샌; 샌; ) HANGUL SYLLABLE SAEN
+C0CD;C0CD;1109 1162 11AC;C0CD;1109 1162 11AC; # (ìƒ; ìƒ; 샍; ìƒ; 샍; ) HANGUL SYLLABLE SAENJ
+C0CE;C0CE;1109 1162 11AD;C0CE;1109 1162 11AD; # (샎; 샎; 샎; 샎; 샎; ) HANGUL SYLLABLE SAENH
+C0CF;C0CF;1109 1162 11AE;C0CF;1109 1162 11AE; # (ìƒ; ìƒ; 샏; ìƒ; 샏; ) HANGUL SYLLABLE SAED
+C0D0;C0D0;1109 1162 11AF;C0D0;1109 1162 11AF; # (ìƒ; ìƒ; 샐; ìƒ; 샐; ) HANGUL SYLLABLE SAEL
+C0D1;C0D1;1109 1162 11B0;C0D1;1109 1162 11B0; # (샑; 샑; 샑; 샑; 샑; ) HANGUL SYLLABLE SAELG
+C0D2;C0D2;1109 1162 11B1;C0D2;1109 1162 11B1; # (샒; 샒; 샒; 샒; 샒; ) HANGUL SYLLABLE SAELM
+C0D3;C0D3;1109 1162 11B2;C0D3;1109 1162 11B2; # (샓; 샓; 샓; 샓; 샓; ) HANGUL SYLLABLE SAELB
+C0D4;C0D4;1109 1162 11B3;C0D4;1109 1162 11B3; # (샔; 샔; 샔; 샔; 샔; ) HANGUL SYLLABLE SAELS
+C0D5;C0D5;1109 1162 11B4;C0D5;1109 1162 11B4; # (샕; 샕; 샕; 샕; 샕; ) HANGUL SYLLABLE SAELT
+C0D6;C0D6;1109 1162 11B5;C0D6;1109 1162 11B5; # (샖; 샖; 샖; 샖; 샖; ) HANGUL SYLLABLE SAELP
+C0D7;C0D7;1109 1162 11B6;C0D7;1109 1162 11B6; # (샗; 샗; 샗; 샗; 샗; ) HANGUL SYLLABLE SAELH
+C0D8;C0D8;1109 1162 11B7;C0D8;1109 1162 11B7; # (샘; 샘; 샘; 샘; 샘; ) HANGUL SYLLABLE SAEM
+C0D9;C0D9;1109 1162 11B8;C0D9;1109 1162 11B8; # (샙; 샙; 샙; 샙; 샙; ) HANGUL SYLLABLE SAEB
+C0DA;C0DA;1109 1162 11B9;C0DA;1109 1162 11B9; # (샚; 샚; 샚; 샚; 샚; ) HANGUL SYLLABLE SAEBS
+C0DB;C0DB;1109 1162 11BA;C0DB;1109 1162 11BA; # (샛; 샛; 샛; 샛; 샛; ) HANGUL SYLLABLE SAES
+C0DC;C0DC;1109 1162 11BB;C0DC;1109 1162 11BB; # (샜; 샜; 샜; 샜; 샜; ) HANGUL SYLLABLE SAESS
+C0DD;C0DD;1109 1162 11BC;C0DD;1109 1162 11BC; # (ìƒ; ìƒ; 생; ìƒ; 생; ) HANGUL SYLLABLE SAENG
+C0DE;C0DE;1109 1162 11BD;C0DE;1109 1162 11BD; # (샞; 샞; 샞; 샞; 샞; ) HANGUL SYLLABLE SAEJ
+C0DF;C0DF;1109 1162 11BE;C0DF;1109 1162 11BE; # (샟; 샟; 샟; 샟; 샟; ) HANGUL SYLLABLE SAEC
+C0E0;C0E0;1109 1162 11BF;C0E0;1109 1162 11BF; # (샠; 샠; 샠; 샠; 샠; ) HANGUL SYLLABLE SAEK
+C0E1;C0E1;1109 1162 11C0;C0E1;1109 1162 11C0; # (샡; 샡; 샡; 샡; 샡; ) HANGUL SYLLABLE SAET
+C0E2;C0E2;1109 1162 11C1;C0E2;1109 1162 11C1; # (샢; 샢; 새á‡; 샢; 새á‡; ) HANGUL SYLLABLE SAEP
+C0E3;C0E3;1109 1162 11C2;C0E3;1109 1162 11C2; # (샣; 샣; 샣; 샣; 샣; ) HANGUL SYLLABLE SAEH
+C0E4;C0E4;1109 1163;C0E4;1109 1163; # (샤; 샤; 샤; 샤; 샤; ) HANGUL SYLLABLE SYA
+C0E5;C0E5;1109 1163 11A8;C0E5;1109 1163 11A8; # (샥; 샥; 샥; 샥; 샥; ) HANGUL SYLLABLE SYAG
+C0E6;C0E6;1109 1163 11A9;C0E6;1109 1163 11A9; # (샦; 샦; 샦; 샦; 샦; ) HANGUL SYLLABLE SYAGG
+C0E7;C0E7;1109 1163 11AA;C0E7;1109 1163 11AA; # (샧; 샧; 샧; 샧; 샧; ) HANGUL SYLLABLE SYAGS
+C0E8;C0E8;1109 1163 11AB;C0E8;1109 1163 11AB; # (샨; 샨; 샨; 샨; 샨; ) HANGUL SYLLABLE SYAN
+C0E9;C0E9;1109 1163 11AC;C0E9;1109 1163 11AC; # (샩; 샩; 샩; 샩; 샩; ) HANGUL SYLLABLE SYANJ
+C0EA;C0EA;1109 1163 11AD;C0EA;1109 1163 11AD; # (샪; 샪; 샪; 샪; 샪; ) HANGUL SYLLABLE SYANH
+C0EB;C0EB;1109 1163 11AE;C0EB;1109 1163 11AE; # (샫; 샫; 샫; 샫; 샫; ) HANGUL SYLLABLE SYAD
+C0EC;C0EC;1109 1163 11AF;C0EC;1109 1163 11AF; # (샬; 샬; 샬; 샬; 샬; ) HANGUL SYLLABLE SYAL
+C0ED;C0ED;1109 1163 11B0;C0ED;1109 1163 11B0; # (샭; 샭; 샭; 샭; 샭; ) HANGUL SYLLABLE SYALG
+C0EE;C0EE;1109 1163 11B1;C0EE;1109 1163 11B1; # (샮; 샮; 샮; 샮; 샮; ) HANGUL SYLLABLE SYALM
+C0EF;C0EF;1109 1163 11B2;C0EF;1109 1163 11B2; # (샯; 샯; 샯; 샯; 샯; ) HANGUL SYLLABLE SYALB
+C0F0;C0F0;1109 1163 11B3;C0F0;1109 1163 11B3; # (샰; 샰; 샰; 샰; 샰; ) HANGUL SYLLABLE SYALS
+C0F1;C0F1;1109 1163 11B4;C0F1;1109 1163 11B4; # (샱; 샱; 샱; 샱; 샱; ) HANGUL SYLLABLE SYALT
+C0F2;C0F2;1109 1163 11B5;C0F2;1109 1163 11B5; # (샲; 샲; 샲; 샲; 샲; ) HANGUL SYLLABLE SYALP
+C0F3;C0F3;1109 1163 11B6;C0F3;1109 1163 11B6; # (샳; 샳; 샳; 샳; 샳; ) HANGUL SYLLABLE SYALH
+C0F4;C0F4;1109 1163 11B7;C0F4;1109 1163 11B7; # (샴; 샴; 샴; 샴; 샴; ) HANGUL SYLLABLE SYAM
+C0F5;C0F5;1109 1163 11B8;C0F5;1109 1163 11B8; # (샵; 샵; 샵; 샵; 샵; ) HANGUL SYLLABLE SYAB
+C0F6;C0F6;1109 1163 11B9;C0F6;1109 1163 11B9; # (샶; 샶; 샶; 샶; 샶; ) HANGUL SYLLABLE SYABS
+C0F7;C0F7;1109 1163 11BA;C0F7;1109 1163 11BA; # (샷; 샷; 샷; 샷; 샷; ) HANGUL SYLLABLE SYAS
+C0F8;C0F8;1109 1163 11BB;C0F8;1109 1163 11BB; # (샸; 샸; 샸; 샸; 샸; ) HANGUL SYLLABLE SYASS
+C0F9;C0F9;1109 1163 11BC;C0F9;1109 1163 11BC; # (샹; 샹; 샹; 샹; 샹; ) HANGUL SYLLABLE SYANG
+C0FA;C0FA;1109 1163 11BD;C0FA;1109 1163 11BD; # (샺; 샺; 샺; 샺; 샺; ) HANGUL SYLLABLE SYAJ
+C0FB;C0FB;1109 1163 11BE;C0FB;1109 1163 11BE; # (샻; 샻; 샻; 샻; 샻; ) HANGUL SYLLABLE SYAC
+C0FC;C0FC;1109 1163 11BF;C0FC;1109 1163 11BF; # (샼; 샼; 샼; 샼; 샼; ) HANGUL SYLLABLE SYAK
+C0FD;C0FD;1109 1163 11C0;C0FD;1109 1163 11C0; # (샽; 샽; 샽; 샽; 샽; ) HANGUL SYLLABLE SYAT
+C0FE;C0FE;1109 1163 11C1;C0FE;1109 1163 11C1; # (샾; 샾; 샤á‡; 샾; 샤á‡; ) HANGUL SYLLABLE SYAP
+C0FF;C0FF;1109 1163 11C2;C0FF;1109 1163 11C2; # (샿; 샿; 샿; 샿; 샿; ) HANGUL SYLLABLE SYAH
+C100;C100;1109 1164;C100;1109 1164; # (섀; 섀; 섀; 섀; 섀; ) HANGUL SYLLABLE SYAE
+C101;C101;1109 1164 11A8;C101;1109 1164 11A8; # (ì„; ì„; 섁; ì„; 섁; ) HANGUL SYLLABLE SYAEG
+C102;C102;1109 1164 11A9;C102;1109 1164 11A9; # (섂; 섂; 섂; 섂; 섂; ) HANGUL SYLLABLE SYAEGG
+C103;C103;1109 1164 11AA;C103;1109 1164 11AA; # (섃; 섃; 섃; 섃; 섃; ) HANGUL SYLLABLE SYAEGS
+C104;C104;1109 1164 11AB;C104;1109 1164 11AB; # (섄; 섄; 섄; 섄; 섄; ) HANGUL SYLLABLE SYAEN
+C105;C105;1109 1164 11AC;C105;1109 1164 11AC; # (섅; 섅; 섅; 섅; 섅; ) HANGUL SYLLABLE SYAENJ
+C106;C106;1109 1164 11AD;C106;1109 1164 11AD; # (섆; 섆; 섆; 섆; 섆; ) HANGUL SYLLABLE SYAENH
+C107;C107;1109 1164 11AE;C107;1109 1164 11AE; # (섇; 섇; 섇; 섇; 섇; ) HANGUL SYLLABLE SYAED
+C108;C108;1109 1164 11AF;C108;1109 1164 11AF; # (섈; 섈; 섈; 섈; 섈; ) HANGUL SYLLABLE SYAEL
+C109;C109;1109 1164 11B0;C109;1109 1164 11B0; # (섉; 섉; 섉; 섉; 섉; ) HANGUL SYLLABLE SYAELG
+C10A;C10A;1109 1164 11B1;C10A;1109 1164 11B1; # (섊; 섊; 섊; 섊; 섊; ) HANGUL SYLLABLE SYAELM
+C10B;C10B;1109 1164 11B2;C10B;1109 1164 11B2; # (섋; 섋; 섋; 섋; 섋; ) HANGUL SYLLABLE SYAELB
+C10C;C10C;1109 1164 11B3;C10C;1109 1164 11B3; # (섌; 섌; 섌; 섌; 섌; ) HANGUL SYLLABLE SYAELS
+C10D;C10D;1109 1164 11B4;C10D;1109 1164 11B4; # (ì„; ì„; 섍; ì„; 섍; ) HANGUL SYLLABLE SYAELT
+C10E;C10E;1109 1164 11B5;C10E;1109 1164 11B5; # (섎; 섎; 섎; 섎; 섎; ) HANGUL SYLLABLE SYAELP
+C10F;C10F;1109 1164 11B6;C10F;1109 1164 11B6; # (ì„; ì„; 섏; ì„; 섏; ) HANGUL SYLLABLE SYAELH
+C110;C110;1109 1164 11B7;C110;1109 1164 11B7; # (ì„; ì„; 섐; ì„; 섐; ) HANGUL SYLLABLE SYAEM
+C111;C111;1109 1164 11B8;C111;1109 1164 11B8; # (섑; 섑; 섑; 섑; 섑; ) HANGUL SYLLABLE SYAEB
+C112;C112;1109 1164 11B9;C112;1109 1164 11B9; # (섒; 섒; 섒; 섒; 섒; ) HANGUL SYLLABLE SYAEBS
+C113;C113;1109 1164 11BA;C113;1109 1164 11BA; # (섓; 섓; 섓; 섓; 섓; ) HANGUL SYLLABLE SYAES
+C114;C114;1109 1164 11BB;C114;1109 1164 11BB; # (섔; 섔; 섔; 섔; 섔; ) HANGUL SYLLABLE SYAESS
+C115;C115;1109 1164 11BC;C115;1109 1164 11BC; # (섕; 섕; 섕; 섕; 섕; ) HANGUL SYLLABLE SYAENG
+C116;C116;1109 1164 11BD;C116;1109 1164 11BD; # (섖; 섖; 섖; 섖; 섖; ) HANGUL SYLLABLE SYAEJ
+C117;C117;1109 1164 11BE;C117;1109 1164 11BE; # (섗; 섗; 섗; 섗; 섗; ) HANGUL SYLLABLE SYAEC
+C118;C118;1109 1164 11BF;C118;1109 1164 11BF; # (섘; 섘; 섘; 섘; 섘; ) HANGUL SYLLABLE SYAEK
+C119;C119;1109 1164 11C0;C119;1109 1164 11C0; # (섙; 섙; 섙; 섙; 섙; ) HANGUL SYLLABLE SYAET
+C11A;C11A;1109 1164 11C1;C11A;1109 1164 11C1; # (섚; 섚; 섀á‡; 섚; 섀á‡; ) HANGUL SYLLABLE SYAEP
+C11B;C11B;1109 1164 11C2;C11B;1109 1164 11C2; # (섛; 섛; 섛; 섛; 섛; ) HANGUL SYLLABLE SYAEH
+C11C;C11C;1109 1165;C11C;1109 1165; # (서; 서; 서; 서; 서; ) HANGUL SYLLABLE SEO
+C11D;C11D;1109 1165 11A8;C11D;1109 1165 11A8; # (ì„; ì„; 석; ì„; 석; ) HANGUL SYLLABLE SEOG
+C11E;C11E;1109 1165 11A9;C11E;1109 1165 11A9; # (섞; 섞; 섞; 섞; 섞; ) HANGUL SYLLABLE SEOGG
+C11F;C11F;1109 1165 11AA;C11F;1109 1165 11AA; # (섟; 섟; 섟; 섟; 섟; ) HANGUL SYLLABLE SEOGS
+C120;C120;1109 1165 11AB;C120;1109 1165 11AB; # (선; 선; 선; 선; 선; ) HANGUL SYLLABLE SEON
+C121;C121;1109 1165 11AC;C121;1109 1165 11AC; # (섡; 섡; 섡; 섡; 섡; ) HANGUL SYLLABLE SEONJ
+C122;C122;1109 1165 11AD;C122;1109 1165 11AD; # (섢; 섢; 섢; 섢; 섢; ) HANGUL SYLLABLE SEONH
+C123;C123;1109 1165 11AE;C123;1109 1165 11AE; # (섣; 섣; 섣; 섣; 섣; ) HANGUL SYLLABLE SEOD
+C124;C124;1109 1165 11AF;C124;1109 1165 11AF; # (설; 설; 설; 설; 설; ) HANGUL SYLLABLE SEOL
+C125;C125;1109 1165 11B0;C125;1109 1165 11B0; # (섥; 섥; 섥; 섥; 섥; ) HANGUL SYLLABLE SEOLG
+C126;C126;1109 1165 11B1;C126;1109 1165 11B1; # (섦; 섦; 섦; 섦; 섦; ) HANGUL SYLLABLE SEOLM
+C127;C127;1109 1165 11B2;C127;1109 1165 11B2; # (섧; 섧; 섧; 섧; 섧; ) HANGUL SYLLABLE SEOLB
+C128;C128;1109 1165 11B3;C128;1109 1165 11B3; # (섨; 섨; 섨; 섨; 섨; ) HANGUL SYLLABLE SEOLS
+C129;C129;1109 1165 11B4;C129;1109 1165 11B4; # (섩; 섩; 섩; 섩; 섩; ) HANGUL SYLLABLE SEOLT
+C12A;C12A;1109 1165 11B5;C12A;1109 1165 11B5; # (섪; 섪; 섪; 섪; 섪; ) HANGUL SYLLABLE SEOLP
+C12B;C12B;1109 1165 11B6;C12B;1109 1165 11B6; # (섫; 섫; 섫; 섫; 섫; ) HANGUL SYLLABLE SEOLH
+C12C;C12C;1109 1165 11B7;C12C;1109 1165 11B7; # (섬; 섬; 섬; 섬; 섬; ) HANGUL SYLLABLE SEOM
+C12D;C12D;1109 1165 11B8;C12D;1109 1165 11B8; # (섭; 섭; 섭; 섭; 섭; ) HANGUL SYLLABLE SEOB
+C12E;C12E;1109 1165 11B9;C12E;1109 1165 11B9; # (섮; 섮; 섮; 섮; 섮; ) HANGUL SYLLABLE SEOBS
+C12F;C12F;1109 1165 11BA;C12F;1109 1165 11BA; # (섯; 섯; 섯; 섯; 섯; ) HANGUL SYLLABLE SEOS
+C130;C130;1109 1165 11BB;C130;1109 1165 11BB; # (섰; 섰; 섰; 섰; 섰; ) HANGUL SYLLABLE SEOSS
+C131;C131;1109 1165 11BC;C131;1109 1165 11BC; # (성; 성; 성; 성; 성; ) HANGUL SYLLABLE SEONG
+C132;C132;1109 1165 11BD;C132;1109 1165 11BD; # (섲; 섲; 섲; 섲; 섲; ) HANGUL SYLLABLE SEOJ
+C133;C133;1109 1165 11BE;C133;1109 1165 11BE; # (섳; 섳; 섳; 섳; 섳; ) HANGUL SYLLABLE SEOC
+C134;C134;1109 1165 11BF;C134;1109 1165 11BF; # (섴; 섴; 섴; 섴; 섴; ) HANGUL SYLLABLE SEOK
+C135;C135;1109 1165 11C0;C135;1109 1165 11C0; # (섵; 섵; 섵; 섵; 섵; ) HANGUL SYLLABLE SEOT
+C136;C136;1109 1165 11C1;C136;1109 1165 11C1; # (ì„¶; ì„¶; 서á‡; ì„¶; 서á‡; ) HANGUL SYLLABLE SEOP
+C137;C137;1109 1165 11C2;C137;1109 1165 11C2; # (섷; 섷; 섷; 섷; 섷; ) HANGUL SYLLABLE SEOH
+C138;C138;1109 1166;C138;1109 1166; # (세; 세; 세; 세; 세; ) HANGUL SYLLABLE SE
+C139;C139;1109 1166 11A8;C139;1109 1166 11A8; # (섹; 섹; 섹; 섹; 섹; ) HANGUL SYLLABLE SEG
+C13A;C13A;1109 1166 11A9;C13A;1109 1166 11A9; # (섺; 섺; 섺; 섺; 섺; ) HANGUL SYLLABLE SEGG
+C13B;C13B;1109 1166 11AA;C13B;1109 1166 11AA; # (섻; 섻; 섻; 섻; 섻; ) HANGUL SYLLABLE SEGS
+C13C;C13C;1109 1166 11AB;C13C;1109 1166 11AB; # (센; 센; 센; 센; 센; ) HANGUL SYLLABLE SEN
+C13D;C13D;1109 1166 11AC;C13D;1109 1166 11AC; # (섽; 섽; 섽; 섽; 섽; ) HANGUL SYLLABLE SENJ
+C13E;C13E;1109 1166 11AD;C13E;1109 1166 11AD; # (섾; 섾; 섾; 섾; 섾; ) HANGUL SYLLABLE SENH
+C13F;C13F;1109 1166 11AE;C13F;1109 1166 11AE; # (섿; 섿; 섿; 섿; 섿; ) HANGUL SYLLABLE SED
+C140;C140;1109 1166 11AF;C140;1109 1166 11AF; # (셀; 셀; 셀; 셀; 셀; ) HANGUL SYLLABLE SEL
+C141;C141;1109 1166 11B0;C141;1109 1166 11B0; # (ì…; ì…; 셁; ì…; 셁; ) HANGUL SYLLABLE SELG
+C142;C142;1109 1166 11B1;C142;1109 1166 11B1; # (셂; 셂; 셂; 셂; 셂; ) HANGUL SYLLABLE SELM
+C143;C143;1109 1166 11B2;C143;1109 1166 11B2; # (셃; 셃; 셃; 셃; 셃; ) HANGUL SYLLABLE SELB
+C144;C144;1109 1166 11B3;C144;1109 1166 11B3; # (셄; 셄; 셄; 셄; 셄; ) HANGUL SYLLABLE SELS
+C145;C145;1109 1166 11B4;C145;1109 1166 11B4; # (셅; 셅; 셅; 셅; 셅; ) HANGUL SYLLABLE SELT
+C146;C146;1109 1166 11B5;C146;1109 1166 11B5; # (셆; 셆; 셆; 셆; 셆; ) HANGUL SYLLABLE SELP
+C147;C147;1109 1166 11B6;C147;1109 1166 11B6; # (셇; 셇; 셇; 셇; 셇; ) HANGUL SYLLABLE SELH
+C148;C148;1109 1166 11B7;C148;1109 1166 11B7; # (셈; 셈; 셈; 셈; 셈; ) HANGUL SYLLABLE SEM
+C149;C149;1109 1166 11B8;C149;1109 1166 11B8; # (셉; 셉; 셉; 셉; 셉; ) HANGUL SYLLABLE SEB
+C14A;C14A;1109 1166 11B9;C14A;1109 1166 11B9; # (셊; 셊; 셊; 셊; 셊; ) HANGUL SYLLABLE SEBS
+C14B;C14B;1109 1166 11BA;C14B;1109 1166 11BA; # (셋; 셋; 셋; 셋; 셋; ) HANGUL SYLLABLE SES
+C14C;C14C;1109 1166 11BB;C14C;1109 1166 11BB; # (셌; 셌; 셌; 셌; 셌; ) HANGUL SYLLABLE SESS
+C14D;C14D;1109 1166 11BC;C14D;1109 1166 11BC; # (ì…; ì…; 셍; ì…; 셍; ) HANGUL SYLLABLE SENG
+C14E;C14E;1109 1166 11BD;C14E;1109 1166 11BD; # (셎; 셎; 셎; 셎; 셎; ) HANGUL SYLLABLE SEJ
+C14F;C14F;1109 1166 11BE;C14F;1109 1166 11BE; # (ì…; ì…; 셏; ì…; 셏; ) HANGUL SYLLABLE SEC
+C150;C150;1109 1166 11BF;C150;1109 1166 11BF; # (ì…; ì…; 셐; ì…; 셐; ) HANGUL SYLLABLE SEK
+C151;C151;1109 1166 11C0;C151;1109 1166 11C0; # (셑; 셑; 셑; 셑; 셑; ) HANGUL SYLLABLE SET
+C152;C152;1109 1166 11C1;C152;1109 1166 11C1; # (ì…’; ì…’; 세á‡; ì…’; 세á‡; ) HANGUL SYLLABLE SEP
+C153;C153;1109 1166 11C2;C153;1109 1166 11C2; # (셓; 셓; 셓; 셓; 셓; ) HANGUL SYLLABLE SEH
+C154;C154;1109 1167;C154;1109 1167; # (셔; 셔; 셔; 셔; 셔; ) HANGUL SYLLABLE SYEO
+C155;C155;1109 1167 11A8;C155;1109 1167 11A8; # (셕; 셕; 셕; 셕; 셕; ) HANGUL SYLLABLE SYEOG
+C156;C156;1109 1167 11A9;C156;1109 1167 11A9; # (셖; 셖; 셖; 셖; 셖; ) HANGUL SYLLABLE SYEOGG
+C157;C157;1109 1167 11AA;C157;1109 1167 11AA; # (셗; 셗; 셗; 셗; 셗; ) HANGUL SYLLABLE SYEOGS
+C158;C158;1109 1167 11AB;C158;1109 1167 11AB; # (션; 션; 션; 션; 션; ) HANGUL SYLLABLE SYEON
+C159;C159;1109 1167 11AC;C159;1109 1167 11AC; # (셙; 셙; 셙; 셙; 셙; ) HANGUL SYLLABLE SYEONJ
+C15A;C15A;1109 1167 11AD;C15A;1109 1167 11AD; # (셚; 셚; 셚; 셚; 셚; ) HANGUL SYLLABLE SYEONH
+C15B;C15B;1109 1167 11AE;C15B;1109 1167 11AE; # (셛; 셛; 셛; 셛; 셛; ) HANGUL SYLLABLE SYEOD
+C15C;C15C;1109 1167 11AF;C15C;1109 1167 11AF; # (셜; 셜; 셜; 셜; 셜; ) HANGUL SYLLABLE SYEOL
+C15D;C15D;1109 1167 11B0;C15D;1109 1167 11B0; # (ì…; ì…; 셝; ì…; 셝; ) HANGUL SYLLABLE SYEOLG
+C15E;C15E;1109 1167 11B1;C15E;1109 1167 11B1; # (셞; 셞; 셞; 셞; 셞; ) HANGUL SYLLABLE SYEOLM
+C15F;C15F;1109 1167 11B2;C15F;1109 1167 11B2; # (셟; 셟; 셟; 셟; 셟; ) HANGUL SYLLABLE SYEOLB
+C160;C160;1109 1167 11B3;C160;1109 1167 11B3; # (셠; 셠; 셠; 셠; 셠; ) HANGUL SYLLABLE SYEOLS
+C161;C161;1109 1167 11B4;C161;1109 1167 11B4; # (셡; 셡; 셡; 셡; 셡; ) HANGUL SYLLABLE SYEOLT
+C162;C162;1109 1167 11B5;C162;1109 1167 11B5; # (셢; 셢; 셢; 셢; 셢; ) HANGUL SYLLABLE SYEOLP
+C163;C163;1109 1167 11B6;C163;1109 1167 11B6; # (셣; 셣; 셣; 셣; 셣; ) HANGUL SYLLABLE SYEOLH
+C164;C164;1109 1167 11B7;C164;1109 1167 11B7; # (셤; 셤; 셤; 셤; 셤; ) HANGUL SYLLABLE SYEOM
+C165;C165;1109 1167 11B8;C165;1109 1167 11B8; # (셥; 셥; 셥; 셥; 셥; ) HANGUL SYLLABLE SYEOB
+C166;C166;1109 1167 11B9;C166;1109 1167 11B9; # (셦; 셦; 셦; 셦; 셦; ) HANGUL SYLLABLE SYEOBS
+C167;C167;1109 1167 11BA;C167;1109 1167 11BA; # (셧; 셧; 셧; 셧; 셧; ) HANGUL SYLLABLE SYEOS
+C168;C168;1109 1167 11BB;C168;1109 1167 11BB; # (셨; 셨; 셨; 셨; 셨; ) HANGUL SYLLABLE SYEOSS
+C169;C169;1109 1167 11BC;C169;1109 1167 11BC; # (셩; 셩; 셩; 셩; 셩; ) HANGUL SYLLABLE SYEONG
+C16A;C16A;1109 1167 11BD;C16A;1109 1167 11BD; # (셪; 셪; 셪; 셪; 셪; ) HANGUL SYLLABLE SYEOJ
+C16B;C16B;1109 1167 11BE;C16B;1109 1167 11BE; # (셫; 셫; 셫; 셫; 셫; ) HANGUL SYLLABLE SYEOC
+C16C;C16C;1109 1167 11BF;C16C;1109 1167 11BF; # (셬; 셬; 셬; 셬; 셬; ) HANGUL SYLLABLE SYEOK
+C16D;C16D;1109 1167 11C0;C16D;1109 1167 11C0; # (셭; 셭; 셭; 셭; 셭; ) HANGUL SYLLABLE SYEOT
+C16E;C16E;1109 1167 11C1;C16E;1109 1167 11C1; # (ì…®; ì…®; 셔á‡; ì…®; 셔á‡; ) HANGUL SYLLABLE SYEOP
+C16F;C16F;1109 1167 11C2;C16F;1109 1167 11C2; # (셯; 셯; 셯; 셯; 셯; ) HANGUL SYLLABLE SYEOH
+C170;C170;1109 1168;C170;1109 1168; # (셰; 셰; 셰; 셰; 셰; ) HANGUL SYLLABLE SYE
+C171;C171;1109 1168 11A8;C171;1109 1168 11A8; # (셱; 셱; 셱; 셱; 셱; ) HANGUL SYLLABLE SYEG
+C172;C172;1109 1168 11A9;C172;1109 1168 11A9; # (셲; 셲; 셲; 셲; 셲; ) HANGUL SYLLABLE SYEGG
+C173;C173;1109 1168 11AA;C173;1109 1168 11AA; # (셳; 셳; 셳; 셳; 셳; ) HANGUL SYLLABLE SYEGS
+C174;C174;1109 1168 11AB;C174;1109 1168 11AB; # (셴; 셴; 셴; 셴; 셴; ) HANGUL SYLLABLE SYEN
+C175;C175;1109 1168 11AC;C175;1109 1168 11AC; # (셵; 셵; 셵; 셵; 셵; ) HANGUL SYLLABLE SYENJ
+C176;C176;1109 1168 11AD;C176;1109 1168 11AD; # (셶; 셶; 셶; 셶; 셶; ) HANGUL SYLLABLE SYENH
+C177;C177;1109 1168 11AE;C177;1109 1168 11AE; # (셷; 셷; 셷; 셷; 셷; ) HANGUL SYLLABLE SYED
+C178;C178;1109 1168 11AF;C178;1109 1168 11AF; # (셸; 셸; 셸; 셸; 셸; ) HANGUL SYLLABLE SYEL
+C179;C179;1109 1168 11B0;C179;1109 1168 11B0; # (셹; 셹; 셹; 셹; 셹; ) HANGUL SYLLABLE SYELG
+C17A;C17A;1109 1168 11B1;C17A;1109 1168 11B1; # (셺; 셺; 셺; 셺; 셺; ) HANGUL SYLLABLE SYELM
+C17B;C17B;1109 1168 11B2;C17B;1109 1168 11B2; # (셻; 셻; 셻; 셻; 셻; ) HANGUL SYLLABLE SYELB
+C17C;C17C;1109 1168 11B3;C17C;1109 1168 11B3; # (셼; 셼; 셼; 셼; 셼; ) HANGUL SYLLABLE SYELS
+C17D;C17D;1109 1168 11B4;C17D;1109 1168 11B4; # (셽; 셽; 셽; 셽; 셽; ) HANGUL SYLLABLE SYELT
+C17E;C17E;1109 1168 11B5;C17E;1109 1168 11B5; # (셾; 셾; 셾; 셾; 셾; ) HANGUL SYLLABLE SYELP
+C17F;C17F;1109 1168 11B6;C17F;1109 1168 11B6; # (셿; 셿; 셿; 셿; 셿; ) HANGUL SYLLABLE SYELH
+C180;C180;1109 1168 11B7;C180;1109 1168 11B7; # (솀; 솀; 솀; 솀; 솀; ) HANGUL SYLLABLE SYEM
+C181;C181;1109 1168 11B8;C181;1109 1168 11B8; # (ì†; ì†; 솁; ì†; 솁; ) HANGUL SYLLABLE SYEB
+C182;C182;1109 1168 11B9;C182;1109 1168 11B9; # (솂; 솂; 솂; 솂; 솂; ) HANGUL SYLLABLE SYEBS
+C183;C183;1109 1168 11BA;C183;1109 1168 11BA; # (솃; 솃; 솃; 솃; 솃; ) HANGUL SYLLABLE SYES
+C184;C184;1109 1168 11BB;C184;1109 1168 11BB; # (솄; 솄; 솄; 솄; 솄; ) HANGUL SYLLABLE SYESS
+C185;C185;1109 1168 11BC;C185;1109 1168 11BC; # (솅; 솅; 솅; 솅; 솅; ) HANGUL SYLLABLE SYENG
+C186;C186;1109 1168 11BD;C186;1109 1168 11BD; # (솆; 솆; 솆; 솆; 솆; ) HANGUL SYLLABLE SYEJ
+C187;C187;1109 1168 11BE;C187;1109 1168 11BE; # (솇; 솇; 솇; 솇; 솇; ) HANGUL SYLLABLE SYEC
+C188;C188;1109 1168 11BF;C188;1109 1168 11BF; # (솈; 솈; 솈; 솈; 솈; ) HANGUL SYLLABLE SYEK
+C189;C189;1109 1168 11C0;C189;1109 1168 11C0; # (솉; 솉; 솉; 솉; 솉; ) HANGUL SYLLABLE SYET
+C18A;C18A;1109 1168 11C1;C18A;1109 1168 11C1; # (솊; 솊; 셰á‡; 솊; 셰á‡; ) HANGUL SYLLABLE SYEP
+C18B;C18B;1109 1168 11C2;C18B;1109 1168 11C2; # (솋; 솋; 솋; 솋; 솋; ) HANGUL SYLLABLE SYEH
+C18C;C18C;1109 1169;C18C;1109 1169; # (소; 소; 소; 소; 소; ) HANGUL SYLLABLE SO
+C18D;C18D;1109 1169 11A8;C18D;1109 1169 11A8; # (ì†; ì†; 속; ì†; 속; ) HANGUL SYLLABLE SOG
+C18E;C18E;1109 1169 11A9;C18E;1109 1169 11A9; # (솎; 솎; 솎; 솎; 솎; ) HANGUL SYLLABLE SOGG
+C18F;C18F;1109 1169 11AA;C18F;1109 1169 11AA; # (ì†; ì†; 솏; ì†; 솏; ) HANGUL SYLLABLE SOGS
+C190;C190;1109 1169 11AB;C190;1109 1169 11AB; # (ì†; ì†; 손; ì†; 손; ) HANGUL SYLLABLE SON
+C191;C191;1109 1169 11AC;C191;1109 1169 11AC; # (솑; 솑; 솑; 솑; 솑; ) HANGUL SYLLABLE SONJ
+C192;C192;1109 1169 11AD;C192;1109 1169 11AD; # (솒; 솒; 솒; 솒; 솒; ) HANGUL SYLLABLE SONH
+C193;C193;1109 1169 11AE;C193;1109 1169 11AE; # (솓; 솓; 솓; 솓; 솓; ) HANGUL SYLLABLE SOD
+C194;C194;1109 1169 11AF;C194;1109 1169 11AF; # (솔; 솔; 솔; 솔; 솔; ) HANGUL SYLLABLE SOL
+C195;C195;1109 1169 11B0;C195;1109 1169 11B0; # (솕; 솕; 솕; 솕; 솕; ) HANGUL SYLLABLE SOLG
+C196;C196;1109 1169 11B1;C196;1109 1169 11B1; # (솖; 솖; 솖; 솖; 솖; ) HANGUL SYLLABLE SOLM
+C197;C197;1109 1169 11B2;C197;1109 1169 11B2; # (솗; 솗; 솗; 솗; 솗; ) HANGUL SYLLABLE SOLB
+C198;C198;1109 1169 11B3;C198;1109 1169 11B3; # (솘; 솘; 솘; 솘; 솘; ) HANGUL SYLLABLE SOLS
+C199;C199;1109 1169 11B4;C199;1109 1169 11B4; # (솙; 솙; 솙; 솙; 솙; ) HANGUL SYLLABLE SOLT
+C19A;C19A;1109 1169 11B5;C19A;1109 1169 11B5; # (솚; 솚; 솚; 솚; 솚; ) HANGUL SYLLABLE SOLP
+C19B;C19B;1109 1169 11B6;C19B;1109 1169 11B6; # (솛; 솛; 솛; 솛; 솛; ) HANGUL SYLLABLE SOLH
+C19C;C19C;1109 1169 11B7;C19C;1109 1169 11B7; # (솜; 솜; 솜; 솜; 솜; ) HANGUL SYLLABLE SOM
+C19D;C19D;1109 1169 11B8;C19D;1109 1169 11B8; # (ì†; ì†; 솝; ì†; 솝; ) HANGUL SYLLABLE SOB
+C19E;C19E;1109 1169 11B9;C19E;1109 1169 11B9; # (솞; 솞; 솞; 솞; 솞; ) HANGUL SYLLABLE SOBS
+C19F;C19F;1109 1169 11BA;C19F;1109 1169 11BA; # (솟; 솟; 솟; 솟; 솟; ) HANGUL SYLLABLE SOS
+C1A0;C1A0;1109 1169 11BB;C1A0;1109 1169 11BB; # (솠; 솠; 솠; 솠; 솠; ) HANGUL SYLLABLE SOSS
+C1A1;C1A1;1109 1169 11BC;C1A1;1109 1169 11BC; # (송; 송; 송; 송; 송; ) HANGUL SYLLABLE SONG
+C1A2;C1A2;1109 1169 11BD;C1A2;1109 1169 11BD; # (솢; 솢; 솢; 솢; 솢; ) HANGUL SYLLABLE SOJ
+C1A3;C1A3;1109 1169 11BE;C1A3;1109 1169 11BE; # (솣; 솣; 솣; 솣; 솣; ) HANGUL SYLLABLE SOC
+C1A4;C1A4;1109 1169 11BF;C1A4;1109 1169 11BF; # (솤; 솤; 솤; 솤; 솤; ) HANGUL SYLLABLE SOK
+C1A5;C1A5;1109 1169 11C0;C1A5;1109 1169 11C0; # (솥; 솥; 솥; 솥; 솥; ) HANGUL SYLLABLE SOT
+C1A6;C1A6;1109 1169 11C1;C1A6;1109 1169 11C1; # (솦; 솦; 소á‡; 솦; 소á‡; ) HANGUL SYLLABLE SOP
+C1A7;C1A7;1109 1169 11C2;C1A7;1109 1169 11C2; # (솧; 솧; 솧; 솧; 솧; ) HANGUL SYLLABLE SOH
+C1A8;C1A8;1109 116A;C1A8;1109 116A; # (솨; 솨; 솨; 솨; 솨; ) HANGUL SYLLABLE SWA
+C1A9;C1A9;1109 116A 11A8;C1A9;1109 116A 11A8; # (솩; 솩; 솩; 솩; 솩; ) HANGUL SYLLABLE SWAG
+C1AA;C1AA;1109 116A 11A9;C1AA;1109 116A 11A9; # (솪; 솪; 솪; 솪; 솪; ) HANGUL SYLLABLE SWAGG
+C1AB;C1AB;1109 116A 11AA;C1AB;1109 116A 11AA; # (솫; 솫; 솫; 솫; 솫; ) HANGUL SYLLABLE SWAGS
+C1AC;C1AC;1109 116A 11AB;C1AC;1109 116A 11AB; # (솬; 솬; 솬; 솬; 솬; ) HANGUL SYLLABLE SWAN
+C1AD;C1AD;1109 116A 11AC;C1AD;1109 116A 11AC; # (솭; 솭; 솭; 솭; 솭; ) HANGUL SYLLABLE SWANJ
+C1AE;C1AE;1109 116A 11AD;C1AE;1109 116A 11AD; # (솮; 솮; 솮; 솮; 솮; ) HANGUL SYLLABLE SWANH
+C1AF;C1AF;1109 116A 11AE;C1AF;1109 116A 11AE; # (솯; 솯; 솯; 솯; 솯; ) HANGUL SYLLABLE SWAD
+C1B0;C1B0;1109 116A 11AF;C1B0;1109 116A 11AF; # (솰; 솰; 솰; 솰; 솰; ) HANGUL SYLLABLE SWAL
+C1B1;C1B1;1109 116A 11B0;C1B1;1109 116A 11B0; # (솱; 솱; 솱; 솱; 솱; ) HANGUL SYLLABLE SWALG
+C1B2;C1B2;1109 116A 11B1;C1B2;1109 116A 11B1; # (솲; 솲; 솲; 솲; 솲; ) HANGUL SYLLABLE SWALM
+C1B3;C1B3;1109 116A 11B2;C1B3;1109 116A 11B2; # (솳; 솳; 솳; 솳; 솳; ) HANGUL SYLLABLE SWALB
+C1B4;C1B4;1109 116A 11B3;C1B4;1109 116A 11B3; # (솴; 솴; 솴; 솴; 솴; ) HANGUL SYLLABLE SWALS
+C1B5;C1B5;1109 116A 11B4;C1B5;1109 116A 11B4; # (솵; 솵; 솵; 솵; 솵; ) HANGUL SYLLABLE SWALT
+C1B6;C1B6;1109 116A 11B5;C1B6;1109 116A 11B5; # (솶; 솶; 솶; 솶; 솶; ) HANGUL SYLLABLE SWALP
+C1B7;C1B7;1109 116A 11B6;C1B7;1109 116A 11B6; # (솷; 솷; 솷; 솷; 솷; ) HANGUL SYLLABLE SWALH
+C1B8;C1B8;1109 116A 11B7;C1B8;1109 116A 11B7; # (솸; 솸; 솸; 솸; 솸; ) HANGUL SYLLABLE SWAM
+C1B9;C1B9;1109 116A 11B8;C1B9;1109 116A 11B8; # (솹; 솹; 솹; 솹; 솹; ) HANGUL SYLLABLE SWAB
+C1BA;C1BA;1109 116A 11B9;C1BA;1109 116A 11B9; # (솺; 솺; 솺; 솺; 솺; ) HANGUL SYLLABLE SWABS
+C1BB;C1BB;1109 116A 11BA;C1BB;1109 116A 11BA; # (솻; 솻; 솻; 솻; 솻; ) HANGUL SYLLABLE SWAS
+C1BC;C1BC;1109 116A 11BB;C1BC;1109 116A 11BB; # (솼; 솼; 솼; 솼; 솼; ) HANGUL SYLLABLE SWASS
+C1BD;C1BD;1109 116A 11BC;C1BD;1109 116A 11BC; # (솽; 솽; 솽; 솽; 솽; ) HANGUL SYLLABLE SWANG
+C1BE;C1BE;1109 116A 11BD;C1BE;1109 116A 11BD; # (솾; 솾; 솾; 솾; 솾; ) HANGUL SYLLABLE SWAJ
+C1BF;C1BF;1109 116A 11BE;C1BF;1109 116A 11BE; # (솿; 솿; 솿; 솿; 솿; ) HANGUL SYLLABLE SWAC
+C1C0;C1C0;1109 116A 11BF;C1C0;1109 116A 11BF; # (쇀; 쇀; 쇀; 쇀; 쇀; ) HANGUL SYLLABLE SWAK
+C1C1;C1C1;1109 116A 11C0;C1C1;1109 116A 11C0; # (ì‡; ì‡; 쇁; ì‡; 쇁; ) HANGUL SYLLABLE SWAT
+C1C2;C1C2;1109 116A 11C1;C1C2;1109 116A 11C1; # (쇂; 쇂; 솨á‡; 쇂; 솨á‡; ) HANGUL SYLLABLE SWAP
+C1C3;C1C3;1109 116A 11C2;C1C3;1109 116A 11C2; # (쇃; 쇃; 쇃; 쇃; 쇃; ) HANGUL SYLLABLE SWAH
+C1C4;C1C4;1109 116B;C1C4;1109 116B; # (쇄; 쇄; 쇄; 쇄; 쇄; ) HANGUL SYLLABLE SWAE
+C1C5;C1C5;1109 116B 11A8;C1C5;1109 116B 11A8; # (쇅; 쇅; 쇅; 쇅; 쇅; ) HANGUL SYLLABLE SWAEG
+C1C6;C1C6;1109 116B 11A9;C1C6;1109 116B 11A9; # (쇆; 쇆; 쇆; 쇆; 쇆; ) HANGUL SYLLABLE SWAEGG
+C1C7;C1C7;1109 116B 11AA;C1C7;1109 116B 11AA; # (쇇; 쇇; 쇇; 쇇; 쇇; ) HANGUL SYLLABLE SWAEGS
+C1C8;C1C8;1109 116B 11AB;C1C8;1109 116B 11AB; # (쇈; 쇈; 쇈; 쇈; 쇈; ) HANGUL SYLLABLE SWAEN
+C1C9;C1C9;1109 116B 11AC;C1C9;1109 116B 11AC; # (쇉; 쇉; 쇉; 쇉; 쇉; ) HANGUL SYLLABLE SWAENJ
+C1CA;C1CA;1109 116B 11AD;C1CA;1109 116B 11AD; # (쇊; 쇊; 쇊; 쇊; 쇊; ) HANGUL SYLLABLE SWAENH
+C1CB;C1CB;1109 116B 11AE;C1CB;1109 116B 11AE; # (쇋; 쇋; 쇋; 쇋; 쇋; ) HANGUL SYLLABLE SWAED
+C1CC;C1CC;1109 116B 11AF;C1CC;1109 116B 11AF; # (쇌; 쇌; 쇌; 쇌; 쇌; ) HANGUL SYLLABLE SWAEL
+C1CD;C1CD;1109 116B 11B0;C1CD;1109 116B 11B0; # (ì‡; ì‡; 쇍; ì‡; 쇍; ) HANGUL SYLLABLE SWAELG
+C1CE;C1CE;1109 116B 11B1;C1CE;1109 116B 11B1; # (쇎; 쇎; 쇎; 쇎; 쇎; ) HANGUL SYLLABLE SWAELM
+C1CF;C1CF;1109 116B 11B2;C1CF;1109 116B 11B2; # (ì‡; ì‡; 쇏; ì‡; 쇏; ) HANGUL SYLLABLE SWAELB
+C1D0;C1D0;1109 116B 11B3;C1D0;1109 116B 11B3; # (ì‡; ì‡; 쇐; ì‡; 쇐; ) HANGUL SYLLABLE SWAELS
+C1D1;C1D1;1109 116B 11B4;C1D1;1109 116B 11B4; # (쇑; 쇑; 쇑; 쇑; 쇑; ) HANGUL SYLLABLE SWAELT
+C1D2;C1D2;1109 116B 11B5;C1D2;1109 116B 11B5; # (쇒; 쇒; 쇒; 쇒; 쇒; ) HANGUL SYLLABLE SWAELP
+C1D3;C1D3;1109 116B 11B6;C1D3;1109 116B 11B6; # (쇓; 쇓; 쇓; 쇓; 쇓; ) HANGUL SYLLABLE SWAELH
+C1D4;C1D4;1109 116B 11B7;C1D4;1109 116B 11B7; # (쇔; 쇔; 쇔; 쇔; 쇔; ) HANGUL SYLLABLE SWAEM
+C1D5;C1D5;1109 116B 11B8;C1D5;1109 116B 11B8; # (쇕; 쇕; 쇕; 쇕; 쇕; ) HANGUL SYLLABLE SWAEB
+C1D6;C1D6;1109 116B 11B9;C1D6;1109 116B 11B9; # (쇖; 쇖; 쇖; 쇖; 쇖; ) HANGUL SYLLABLE SWAEBS
+C1D7;C1D7;1109 116B 11BA;C1D7;1109 116B 11BA; # (쇗; 쇗; 쇗; 쇗; 쇗; ) HANGUL SYLLABLE SWAES
+C1D8;C1D8;1109 116B 11BB;C1D8;1109 116B 11BB; # (쇘; 쇘; 쇘; 쇘; 쇘; ) HANGUL SYLLABLE SWAESS
+C1D9;C1D9;1109 116B 11BC;C1D9;1109 116B 11BC; # (쇙; 쇙; 쇙; 쇙; 쇙; ) HANGUL SYLLABLE SWAENG
+C1DA;C1DA;1109 116B 11BD;C1DA;1109 116B 11BD; # (쇚; 쇚; 쇚; 쇚; 쇚; ) HANGUL SYLLABLE SWAEJ
+C1DB;C1DB;1109 116B 11BE;C1DB;1109 116B 11BE; # (쇛; 쇛; 쇛; 쇛; 쇛; ) HANGUL SYLLABLE SWAEC
+C1DC;C1DC;1109 116B 11BF;C1DC;1109 116B 11BF; # (쇜; 쇜; 쇜; 쇜; 쇜; ) HANGUL SYLLABLE SWAEK
+C1DD;C1DD;1109 116B 11C0;C1DD;1109 116B 11C0; # (ì‡; ì‡; 쇝; ì‡; 쇝; ) HANGUL SYLLABLE SWAET
+C1DE;C1DE;1109 116B 11C1;C1DE;1109 116B 11C1; # (쇞; 쇞; 쇄á‡; 쇞; 쇄á‡; ) HANGUL SYLLABLE SWAEP
+C1DF;C1DF;1109 116B 11C2;C1DF;1109 116B 11C2; # (쇟; 쇟; 쇟; 쇟; 쇟; ) HANGUL SYLLABLE SWAEH
+C1E0;C1E0;1109 116C;C1E0;1109 116C; # (쇠; 쇠; 쇠; 쇠; 쇠; ) HANGUL SYLLABLE SOE
+C1E1;C1E1;1109 116C 11A8;C1E1;1109 116C 11A8; # (쇡; 쇡; 쇡; 쇡; 쇡; ) HANGUL SYLLABLE SOEG
+C1E2;C1E2;1109 116C 11A9;C1E2;1109 116C 11A9; # (쇢; 쇢; 쇢; 쇢; 쇢; ) HANGUL SYLLABLE SOEGG
+C1E3;C1E3;1109 116C 11AA;C1E3;1109 116C 11AA; # (쇣; 쇣; 쇣; 쇣; 쇣; ) HANGUL SYLLABLE SOEGS
+C1E4;C1E4;1109 116C 11AB;C1E4;1109 116C 11AB; # (쇤; 쇤; 쇤; 쇤; 쇤; ) HANGUL SYLLABLE SOEN
+C1E5;C1E5;1109 116C 11AC;C1E5;1109 116C 11AC; # (쇥; 쇥; 쇥; 쇥; 쇥; ) HANGUL SYLLABLE SOENJ
+C1E6;C1E6;1109 116C 11AD;C1E6;1109 116C 11AD; # (쇦; 쇦; 쇦; 쇦; 쇦; ) HANGUL SYLLABLE SOENH
+C1E7;C1E7;1109 116C 11AE;C1E7;1109 116C 11AE; # (쇧; 쇧; 쇧; 쇧; 쇧; ) HANGUL SYLLABLE SOED
+C1E8;C1E8;1109 116C 11AF;C1E8;1109 116C 11AF; # (쇨; 쇨; 쇨; 쇨; 쇨; ) HANGUL SYLLABLE SOEL
+C1E9;C1E9;1109 116C 11B0;C1E9;1109 116C 11B0; # (쇩; 쇩; 쇩; 쇩; 쇩; ) HANGUL SYLLABLE SOELG
+C1EA;C1EA;1109 116C 11B1;C1EA;1109 116C 11B1; # (쇪; 쇪; 쇪; 쇪; 쇪; ) HANGUL SYLLABLE SOELM
+C1EB;C1EB;1109 116C 11B2;C1EB;1109 116C 11B2; # (쇫; 쇫; 쇫; 쇫; 쇫; ) HANGUL SYLLABLE SOELB
+C1EC;C1EC;1109 116C 11B3;C1EC;1109 116C 11B3; # (쇬; 쇬; 쇬; 쇬; 쇬; ) HANGUL SYLLABLE SOELS
+C1ED;C1ED;1109 116C 11B4;C1ED;1109 116C 11B4; # (쇭; 쇭; 쇭; 쇭; 쇭; ) HANGUL SYLLABLE SOELT
+C1EE;C1EE;1109 116C 11B5;C1EE;1109 116C 11B5; # (쇮; 쇮; 쇮; 쇮; 쇮; ) HANGUL SYLLABLE SOELP
+C1EF;C1EF;1109 116C 11B6;C1EF;1109 116C 11B6; # (쇯; 쇯; 쇯; 쇯; 쇯; ) HANGUL SYLLABLE SOELH
+C1F0;C1F0;1109 116C 11B7;C1F0;1109 116C 11B7; # (쇰; 쇰; 쇰; 쇰; 쇰; ) HANGUL SYLLABLE SOEM
+C1F1;C1F1;1109 116C 11B8;C1F1;1109 116C 11B8; # (쇱; 쇱; 쇱; 쇱; 쇱; ) HANGUL SYLLABLE SOEB
+C1F2;C1F2;1109 116C 11B9;C1F2;1109 116C 11B9; # (쇲; 쇲; 쇲; 쇲; 쇲; ) HANGUL SYLLABLE SOEBS
+C1F3;C1F3;1109 116C 11BA;C1F3;1109 116C 11BA; # (쇳; 쇳; 쇳; 쇳; 쇳; ) HANGUL SYLLABLE SOES
+C1F4;C1F4;1109 116C 11BB;C1F4;1109 116C 11BB; # (쇴; 쇴; 쇴; 쇴; 쇴; ) HANGUL SYLLABLE SOESS
+C1F5;C1F5;1109 116C 11BC;C1F5;1109 116C 11BC; # (쇵; 쇵; 쇵; 쇵; 쇵; ) HANGUL SYLLABLE SOENG
+C1F6;C1F6;1109 116C 11BD;C1F6;1109 116C 11BD; # (쇶; 쇶; 쇶; 쇶; 쇶; ) HANGUL SYLLABLE SOEJ
+C1F7;C1F7;1109 116C 11BE;C1F7;1109 116C 11BE; # (쇷; 쇷; 쇷; 쇷; 쇷; ) HANGUL SYLLABLE SOEC
+C1F8;C1F8;1109 116C 11BF;C1F8;1109 116C 11BF; # (쇸; 쇸; 쇸; 쇸; 쇸; ) HANGUL SYLLABLE SOEK
+C1F9;C1F9;1109 116C 11C0;C1F9;1109 116C 11C0; # (쇹; 쇹; 쇹; 쇹; 쇹; ) HANGUL SYLLABLE SOET
+C1FA;C1FA;1109 116C 11C1;C1FA;1109 116C 11C1; # (쇺; 쇺; 쇠á‡; 쇺; 쇠á‡; ) HANGUL SYLLABLE SOEP
+C1FB;C1FB;1109 116C 11C2;C1FB;1109 116C 11C2; # (쇻; 쇻; 쇻; 쇻; 쇻; ) HANGUL SYLLABLE SOEH
+C1FC;C1FC;1109 116D;C1FC;1109 116D; # (쇼; 쇼; 쇼; 쇼; 쇼; ) HANGUL SYLLABLE SYO
+C1FD;C1FD;1109 116D 11A8;C1FD;1109 116D 11A8; # (쇽; 쇽; 쇽; 쇽; 쇽; ) HANGUL SYLLABLE SYOG
+C1FE;C1FE;1109 116D 11A9;C1FE;1109 116D 11A9; # (쇾; 쇾; 쇾; 쇾; 쇾; ) HANGUL SYLLABLE SYOGG
+C1FF;C1FF;1109 116D 11AA;C1FF;1109 116D 11AA; # (쇿; 쇿; 쇿; 쇿; 쇿; ) HANGUL SYLLABLE SYOGS
+C200;C200;1109 116D 11AB;C200;1109 116D 11AB; # (숀; 숀; 숀; 숀; 숀; ) HANGUL SYLLABLE SYON
+C201;C201;1109 116D 11AC;C201;1109 116D 11AC; # (ìˆ; ìˆ; 숁; ìˆ; 숁; ) HANGUL SYLLABLE SYONJ
+C202;C202;1109 116D 11AD;C202;1109 116D 11AD; # (숂; 숂; 숂; 숂; 숂; ) HANGUL SYLLABLE SYONH
+C203;C203;1109 116D 11AE;C203;1109 116D 11AE; # (숃; 숃; 숃; 숃; 숃; ) HANGUL SYLLABLE SYOD
+C204;C204;1109 116D 11AF;C204;1109 116D 11AF; # (숄; 숄; 숄; 숄; 숄; ) HANGUL SYLLABLE SYOL
+C205;C205;1109 116D 11B0;C205;1109 116D 11B0; # (숅; 숅; 숅; 숅; 숅; ) HANGUL SYLLABLE SYOLG
+C206;C206;1109 116D 11B1;C206;1109 116D 11B1; # (숆; 숆; 숆; 숆; 숆; ) HANGUL SYLLABLE SYOLM
+C207;C207;1109 116D 11B2;C207;1109 116D 11B2; # (숇; 숇; 숇; 숇; 숇; ) HANGUL SYLLABLE SYOLB
+C208;C208;1109 116D 11B3;C208;1109 116D 11B3; # (숈; 숈; 숈; 숈; 숈; ) HANGUL SYLLABLE SYOLS
+C209;C209;1109 116D 11B4;C209;1109 116D 11B4; # (숉; 숉; 숉; 숉; 숉; ) HANGUL SYLLABLE SYOLT
+C20A;C20A;1109 116D 11B5;C20A;1109 116D 11B5; # (숊; 숊; 숊; 숊; 숊; ) HANGUL SYLLABLE SYOLP
+C20B;C20B;1109 116D 11B6;C20B;1109 116D 11B6; # (숋; 숋; 숋; 숋; 숋; ) HANGUL SYLLABLE SYOLH
+C20C;C20C;1109 116D 11B7;C20C;1109 116D 11B7; # (숌; 숌; 숌; 숌; 숌; ) HANGUL SYLLABLE SYOM
+C20D;C20D;1109 116D 11B8;C20D;1109 116D 11B8; # (ìˆ; ìˆ; 숍; ìˆ; 숍; ) HANGUL SYLLABLE SYOB
+C20E;C20E;1109 116D 11B9;C20E;1109 116D 11B9; # (숎; 숎; 숎; 숎; 숎; ) HANGUL SYLLABLE SYOBS
+C20F;C20F;1109 116D 11BA;C20F;1109 116D 11BA; # (ìˆ; ìˆ; 숏; ìˆ; 숏; ) HANGUL SYLLABLE SYOS
+C210;C210;1109 116D 11BB;C210;1109 116D 11BB; # (ìˆ; ìˆ; 숐; ìˆ; 숐; ) HANGUL SYLLABLE SYOSS
+C211;C211;1109 116D 11BC;C211;1109 116D 11BC; # (숑; 숑; 숑; 숑; 숑; ) HANGUL SYLLABLE SYONG
+C212;C212;1109 116D 11BD;C212;1109 116D 11BD; # (숒; 숒; 숒; 숒; 숒; ) HANGUL SYLLABLE SYOJ
+C213;C213;1109 116D 11BE;C213;1109 116D 11BE; # (숓; 숓; 숓; 숓; 숓; ) HANGUL SYLLABLE SYOC
+C214;C214;1109 116D 11BF;C214;1109 116D 11BF; # (숔; 숔; 숔; 숔; 숔; ) HANGUL SYLLABLE SYOK
+C215;C215;1109 116D 11C0;C215;1109 116D 11C0; # (숕; 숕; 숕; 숕; 숕; ) HANGUL SYLLABLE SYOT
+C216;C216;1109 116D 11C1;C216;1109 116D 11C1; # (숖; 숖; 쇼á‡; 숖; 쇼á‡; ) HANGUL SYLLABLE SYOP
+C217;C217;1109 116D 11C2;C217;1109 116D 11C2; # (숗; 숗; 숗; 숗; 숗; ) HANGUL SYLLABLE SYOH
+C218;C218;1109 116E;C218;1109 116E; # (수; 수; 수; 수; 수; ) HANGUL SYLLABLE SU
+C219;C219;1109 116E 11A8;C219;1109 116E 11A8; # (숙; 숙; 숙; 숙; 숙; ) HANGUL SYLLABLE SUG
+C21A;C21A;1109 116E 11A9;C21A;1109 116E 11A9; # (숚; 숚; 숚; 숚; 숚; ) HANGUL SYLLABLE SUGG
+C21B;C21B;1109 116E 11AA;C21B;1109 116E 11AA; # (숛; 숛; 숛; 숛; 숛; ) HANGUL SYLLABLE SUGS
+C21C;C21C;1109 116E 11AB;C21C;1109 116E 11AB; # (순; 순; 순; 순; 순; ) HANGUL SYLLABLE SUN
+C21D;C21D;1109 116E 11AC;C21D;1109 116E 11AC; # (ìˆ; ìˆ; 숝; ìˆ; 숝; ) HANGUL SYLLABLE SUNJ
+C21E;C21E;1109 116E 11AD;C21E;1109 116E 11AD; # (숞; 숞; 숞; 숞; 숞; ) HANGUL SYLLABLE SUNH
+C21F;C21F;1109 116E 11AE;C21F;1109 116E 11AE; # (숟; 숟; 숟; 숟; 숟; ) HANGUL SYLLABLE SUD
+C220;C220;1109 116E 11AF;C220;1109 116E 11AF; # (술; 술; 술; 술; 술; ) HANGUL SYLLABLE SUL
+C221;C221;1109 116E 11B0;C221;1109 116E 11B0; # (숡; 숡; 숡; 숡; 숡; ) HANGUL SYLLABLE SULG
+C222;C222;1109 116E 11B1;C222;1109 116E 11B1; # (숢; 숢; 숢; 숢; 숢; ) HANGUL SYLLABLE SULM
+C223;C223;1109 116E 11B2;C223;1109 116E 11B2; # (숣; 숣; 숣; 숣; 숣; ) HANGUL SYLLABLE SULB
+C224;C224;1109 116E 11B3;C224;1109 116E 11B3; # (숤; 숤; 숤; 숤; 숤; ) HANGUL SYLLABLE SULS
+C225;C225;1109 116E 11B4;C225;1109 116E 11B4; # (숥; 숥; 숥; 숥; 숥; ) HANGUL SYLLABLE SULT
+C226;C226;1109 116E 11B5;C226;1109 116E 11B5; # (숦; 숦; 숦; 숦; 숦; ) HANGUL SYLLABLE SULP
+C227;C227;1109 116E 11B6;C227;1109 116E 11B6; # (숧; 숧; 숧; 숧; 숧; ) HANGUL SYLLABLE SULH
+C228;C228;1109 116E 11B7;C228;1109 116E 11B7; # (숨; 숨; 숨; 숨; 숨; ) HANGUL SYLLABLE SUM
+C229;C229;1109 116E 11B8;C229;1109 116E 11B8; # (숩; 숩; 숩; 숩; 숩; ) HANGUL SYLLABLE SUB
+C22A;C22A;1109 116E 11B9;C22A;1109 116E 11B9; # (숪; 숪; 숪; 숪; 숪; ) HANGUL SYLLABLE SUBS
+C22B;C22B;1109 116E 11BA;C22B;1109 116E 11BA; # (숫; 숫; 숫; 숫; 숫; ) HANGUL SYLLABLE SUS
+C22C;C22C;1109 116E 11BB;C22C;1109 116E 11BB; # (숬; 숬; 숬; 숬; 숬; ) HANGUL SYLLABLE SUSS
+C22D;C22D;1109 116E 11BC;C22D;1109 116E 11BC; # (숭; 숭; 숭; 숭; 숭; ) HANGUL SYLLABLE SUNG
+C22E;C22E;1109 116E 11BD;C22E;1109 116E 11BD; # (숮; 숮; 숮; 숮; 숮; ) HANGUL SYLLABLE SUJ
+C22F;C22F;1109 116E 11BE;C22F;1109 116E 11BE; # (숯; 숯; 숯; 숯; 숯; ) HANGUL SYLLABLE SUC
+C230;C230;1109 116E 11BF;C230;1109 116E 11BF; # (숰; 숰; 숰; 숰; 숰; ) HANGUL SYLLABLE SUK
+C231;C231;1109 116E 11C0;C231;1109 116E 11C0; # (숱; 숱; 숱; 숱; 숱; ) HANGUL SYLLABLE SUT
+C232;C232;1109 116E 11C1;C232;1109 116E 11C1; # (숲; 숲; 수á‡; 숲; 수á‡; ) HANGUL SYLLABLE SUP
+C233;C233;1109 116E 11C2;C233;1109 116E 11C2; # (숳; 숳; 숳; 숳; 숳; ) HANGUL SYLLABLE SUH
+C234;C234;1109 116F;C234;1109 116F; # (숴; 숴; 숴; 숴; 숴; ) HANGUL SYLLABLE SWEO
+C235;C235;1109 116F 11A8;C235;1109 116F 11A8; # (숵; 숵; 숵; 숵; 숵; ) HANGUL SYLLABLE SWEOG
+C236;C236;1109 116F 11A9;C236;1109 116F 11A9; # (숶; 숶; 숶; 숶; 숶; ) HANGUL SYLLABLE SWEOGG
+C237;C237;1109 116F 11AA;C237;1109 116F 11AA; # (숷; 숷; 숷; 숷; 숷; ) HANGUL SYLLABLE SWEOGS
+C238;C238;1109 116F 11AB;C238;1109 116F 11AB; # (숸; 숸; 숸; 숸; 숸; ) HANGUL SYLLABLE SWEON
+C239;C239;1109 116F 11AC;C239;1109 116F 11AC; # (숹; 숹; 숹; 숹; 숹; ) HANGUL SYLLABLE SWEONJ
+C23A;C23A;1109 116F 11AD;C23A;1109 116F 11AD; # (숺; 숺; 숺; 숺; 숺; ) HANGUL SYLLABLE SWEONH
+C23B;C23B;1109 116F 11AE;C23B;1109 116F 11AE; # (숻; 숻; 숻; 숻; 숻; ) HANGUL SYLLABLE SWEOD
+C23C;C23C;1109 116F 11AF;C23C;1109 116F 11AF; # (숼; 숼; 숼; 숼; 숼; ) HANGUL SYLLABLE SWEOL
+C23D;C23D;1109 116F 11B0;C23D;1109 116F 11B0; # (숽; 숽; 숽; 숽; 숽; ) HANGUL SYLLABLE SWEOLG
+C23E;C23E;1109 116F 11B1;C23E;1109 116F 11B1; # (숾; 숾; 숾; 숾; 숾; ) HANGUL SYLLABLE SWEOLM
+C23F;C23F;1109 116F 11B2;C23F;1109 116F 11B2; # (숿; 숿; 숿; 숿; 숿; ) HANGUL SYLLABLE SWEOLB
+C240;C240;1109 116F 11B3;C240;1109 116F 11B3; # (쉀; 쉀; 쉀; 쉀; 쉀; ) HANGUL SYLLABLE SWEOLS
+C241;C241;1109 116F 11B4;C241;1109 116F 11B4; # (ì‰; ì‰; 쉁; ì‰; 쉁; ) HANGUL SYLLABLE SWEOLT
+C242;C242;1109 116F 11B5;C242;1109 116F 11B5; # (쉂; 쉂; 쉂; 쉂; 쉂; ) HANGUL SYLLABLE SWEOLP
+C243;C243;1109 116F 11B6;C243;1109 116F 11B6; # (쉃; 쉃; 쉃; 쉃; 쉃; ) HANGUL SYLLABLE SWEOLH
+C244;C244;1109 116F 11B7;C244;1109 116F 11B7; # (쉄; 쉄; 쉄; 쉄; 쉄; ) HANGUL SYLLABLE SWEOM
+C245;C245;1109 116F 11B8;C245;1109 116F 11B8; # (쉅; 쉅; 쉅; 쉅; 쉅; ) HANGUL SYLLABLE SWEOB
+C246;C246;1109 116F 11B9;C246;1109 116F 11B9; # (쉆; 쉆; 쉆; 쉆; 쉆; ) HANGUL SYLLABLE SWEOBS
+C247;C247;1109 116F 11BA;C247;1109 116F 11BA; # (쉇; 쉇; 쉇; 쉇; 쉇; ) HANGUL SYLLABLE SWEOS
+C248;C248;1109 116F 11BB;C248;1109 116F 11BB; # (쉈; 쉈; 쉈; 쉈; 쉈; ) HANGUL SYLLABLE SWEOSS
+C249;C249;1109 116F 11BC;C249;1109 116F 11BC; # (쉉; 쉉; 쉉; 쉉; 쉉; ) HANGUL SYLLABLE SWEONG
+C24A;C24A;1109 116F 11BD;C24A;1109 116F 11BD; # (쉊; 쉊; 쉊; 쉊; 쉊; ) HANGUL SYLLABLE SWEOJ
+C24B;C24B;1109 116F 11BE;C24B;1109 116F 11BE; # (쉋; 쉋; 쉋; 쉋; 쉋; ) HANGUL SYLLABLE SWEOC
+C24C;C24C;1109 116F 11BF;C24C;1109 116F 11BF; # (쉌; 쉌; 쉌; 쉌; 쉌; ) HANGUL SYLLABLE SWEOK
+C24D;C24D;1109 116F 11C0;C24D;1109 116F 11C0; # (ì‰; ì‰; 쉍; ì‰; 쉍; ) HANGUL SYLLABLE SWEOT
+C24E;C24E;1109 116F 11C1;C24E;1109 116F 11C1; # (쉎; 쉎; 숴á‡; 쉎; 숴á‡; ) HANGUL SYLLABLE SWEOP
+C24F;C24F;1109 116F 11C2;C24F;1109 116F 11C2; # (ì‰; ì‰; 쉏; ì‰; 쉏; ) HANGUL SYLLABLE SWEOH
+C250;C250;1109 1170;C250;1109 1170; # (ì‰; ì‰; 쉐; ì‰; 쉐; ) HANGUL SYLLABLE SWE
+C251;C251;1109 1170 11A8;C251;1109 1170 11A8; # (쉑; 쉑; 쉑; 쉑; 쉑; ) HANGUL SYLLABLE SWEG
+C252;C252;1109 1170 11A9;C252;1109 1170 11A9; # (쉒; 쉒; 쉒; 쉒; 쉒; ) HANGUL SYLLABLE SWEGG
+C253;C253;1109 1170 11AA;C253;1109 1170 11AA; # (쉓; 쉓; 쉓; 쉓; 쉓; ) HANGUL SYLLABLE SWEGS
+C254;C254;1109 1170 11AB;C254;1109 1170 11AB; # (쉔; 쉔; 쉔; 쉔; 쉔; ) HANGUL SYLLABLE SWEN
+C255;C255;1109 1170 11AC;C255;1109 1170 11AC; # (쉕; 쉕; 쉕; 쉕; 쉕; ) HANGUL SYLLABLE SWENJ
+C256;C256;1109 1170 11AD;C256;1109 1170 11AD; # (쉖; 쉖; 쉖; 쉖; 쉖; ) HANGUL SYLLABLE SWENH
+C257;C257;1109 1170 11AE;C257;1109 1170 11AE; # (쉗; 쉗; 쉗; 쉗; 쉗; ) HANGUL SYLLABLE SWED
+C258;C258;1109 1170 11AF;C258;1109 1170 11AF; # (쉘; 쉘; 쉘; 쉘; 쉘; ) HANGUL SYLLABLE SWEL
+C259;C259;1109 1170 11B0;C259;1109 1170 11B0; # (쉙; 쉙; 쉙; 쉙; 쉙; ) HANGUL SYLLABLE SWELG
+C25A;C25A;1109 1170 11B1;C25A;1109 1170 11B1; # (쉚; 쉚; 쉚; 쉚; 쉚; ) HANGUL SYLLABLE SWELM
+C25B;C25B;1109 1170 11B2;C25B;1109 1170 11B2; # (쉛; 쉛; 쉛; 쉛; 쉛; ) HANGUL SYLLABLE SWELB
+C25C;C25C;1109 1170 11B3;C25C;1109 1170 11B3; # (쉜; 쉜; 쉜; 쉜; 쉜; ) HANGUL SYLLABLE SWELS
+C25D;C25D;1109 1170 11B4;C25D;1109 1170 11B4; # (ì‰; ì‰; 쉝; ì‰; 쉝; ) HANGUL SYLLABLE SWELT
+C25E;C25E;1109 1170 11B5;C25E;1109 1170 11B5; # (쉞; 쉞; 쉞; 쉞; 쉞; ) HANGUL SYLLABLE SWELP
+C25F;C25F;1109 1170 11B6;C25F;1109 1170 11B6; # (쉟; 쉟; 쉟; 쉟; 쉟; ) HANGUL SYLLABLE SWELH
+C260;C260;1109 1170 11B7;C260;1109 1170 11B7; # (쉠; 쉠; 쉠; 쉠; 쉠; ) HANGUL SYLLABLE SWEM
+C261;C261;1109 1170 11B8;C261;1109 1170 11B8; # (쉡; 쉡; 쉡; 쉡; 쉡; ) HANGUL SYLLABLE SWEB
+C262;C262;1109 1170 11B9;C262;1109 1170 11B9; # (쉢; 쉢; 쉢; 쉢; 쉢; ) HANGUL SYLLABLE SWEBS
+C263;C263;1109 1170 11BA;C263;1109 1170 11BA; # (쉣; 쉣; 쉣; 쉣; 쉣; ) HANGUL SYLLABLE SWES
+C264;C264;1109 1170 11BB;C264;1109 1170 11BB; # (쉤; 쉤; 쉤; 쉤; 쉤; ) HANGUL SYLLABLE SWESS
+C265;C265;1109 1170 11BC;C265;1109 1170 11BC; # (쉥; 쉥; 쉥; 쉥; 쉥; ) HANGUL SYLLABLE SWENG
+C266;C266;1109 1170 11BD;C266;1109 1170 11BD; # (쉦; 쉦; 쉦; 쉦; 쉦; ) HANGUL SYLLABLE SWEJ
+C267;C267;1109 1170 11BE;C267;1109 1170 11BE; # (쉧; 쉧; 쉧; 쉧; 쉧; ) HANGUL SYLLABLE SWEC
+C268;C268;1109 1170 11BF;C268;1109 1170 11BF; # (쉨; 쉨; 쉨; 쉨; 쉨; ) HANGUL SYLLABLE SWEK
+C269;C269;1109 1170 11C0;C269;1109 1170 11C0; # (쉩; 쉩; 쉩; 쉩; 쉩; ) HANGUL SYLLABLE SWET
+C26A;C26A;1109 1170 11C1;C26A;1109 1170 11C1; # (쉪; 쉪; 쉐á‡; 쉪; 쉐á‡; ) HANGUL SYLLABLE SWEP
+C26B;C26B;1109 1170 11C2;C26B;1109 1170 11C2; # (쉫; 쉫; 쉫; 쉫; 쉫; ) HANGUL SYLLABLE SWEH
+C26C;C26C;1109 1171;C26C;1109 1171; # (쉬; 쉬; 쉬; 쉬; 쉬; ) HANGUL SYLLABLE SWI
+C26D;C26D;1109 1171 11A8;C26D;1109 1171 11A8; # (쉭; 쉭; 쉭; 쉭; 쉭; ) HANGUL SYLLABLE SWIG
+C26E;C26E;1109 1171 11A9;C26E;1109 1171 11A9; # (쉮; 쉮; 쉮; 쉮; 쉮; ) HANGUL SYLLABLE SWIGG
+C26F;C26F;1109 1171 11AA;C26F;1109 1171 11AA; # (쉯; 쉯; 쉯; 쉯; 쉯; ) HANGUL SYLLABLE SWIGS
+C270;C270;1109 1171 11AB;C270;1109 1171 11AB; # (쉰; 쉰; 쉰; 쉰; 쉰; ) HANGUL SYLLABLE SWIN
+C271;C271;1109 1171 11AC;C271;1109 1171 11AC; # (쉱; 쉱; 쉱; 쉱; 쉱; ) HANGUL SYLLABLE SWINJ
+C272;C272;1109 1171 11AD;C272;1109 1171 11AD; # (쉲; 쉲; 쉲; 쉲; 쉲; ) HANGUL SYLLABLE SWINH
+C273;C273;1109 1171 11AE;C273;1109 1171 11AE; # (쉳; 쉳; 쉳; 쉳; 쉳; ) HANGUL SYLLABLE SWID
+C274;C274;1109 1171 11AF;C274;1109 1171 11AF; # (쉴; 쉴; 쉴; 쉴; 쉴; ) HANGUL SYLLABLE SWIL
+C275;C275;1109 1171 11B0;C275;1109 1171 11B0; # (쉵; 쉵; 쉵; 쉵; 쉵; ) HANGUL SYLLABLE SWILG
+C276;C276;1109 1171 11B1;C276;1109 1171 11B1; # (쉶; 쉶; 쉶; 쉶; 쉶; ) HANGUL SYLLABLE SWILM
+C277;C277;1109 1171 11B2;C277;1109 1171 11B2; # (쉷; 쉷; 쉷; 쉷; 쉷; ) HANGUL SYLLABLE SWILB
+C278;C278;1109 1171 11B3;C278;1109 1171 11B3; # (쉸; 쉸; 쉸; 쉸; 쉸; ) HANGUL SYLLABLE SWILS
+C279;C279;1109 1171 11B4;C279;1109 1171 11B4; # (쉹; 쉹; 쉹; 쉹; 쉹; ) HANGUL SYLLABLE SWILT
+C27A;C27A;1109 1171 11B5;C27A;1109 1171 11B5; # (쉺; 쉺; 쉺; 쉺; 쉺; ) HANGUL SYLLABLE SWILP
+C27B;C27B;1109 1171 11B6;C27B;1109 1171 11B6; # (쉻; 쉻; 쉻; 쉻; 쉻; ) HANGUL SYLLABLE SWILH
+C27C;C27C;1109 1171 11B7;C27C;1109 1171 11B7; # (쉼; 쉼; 쉼; 쉼; 쉼; ) HANGUL SYLLABLE SWIM
+C27D;C27D;1109 1171 11B8;C27D;1109 1171 11B8; # (쉽; 쉽; 쉽; 쉽; 쉽; ) HANGUL SYLLABLE SWIB
+C27E;C27E;1109 1171 11B9;C27E;1109 1171 11B9; # (쉾; 쉾; 쉾; 쉾; 쉾; ) HANGUL SYLLABLE SWIBS
+C27F;C27F;1109 1171 11BA;C27F;1109 1171 11BA; # (쉿; 쉿; 쉿; 쉿; 쉿; ) HANGUL SYLLABLE SWIS
+C280;C280;1109 1171 11BB;C280;1109 1171 11BB; # (슀; 슀; 슀; 슀; 슀; ) HANGUL SYLLABLE SWISS
+C281;C281;1109 1171 11BC;C281;1109 1171 11BC; # (ìŠ; ìŠ; 슁; ìŠ; 슁; ) HANGUL SYLLABLE SWING
+C282;C282;1109 1171 11BD;C282;1109 1171 11BD; # (슂; 슂; 슂; 슂; 슂; ) HANGUL SYLLABLE SWIJ
+C283;C283;1109 1171 11BE;C283;1109 1171 11BE; # (슃; 슃; 슃; 슃; 슃; ) HANGUL SYLLABLE SWIC
+C284;C284;1109 1171 11BF;C284;1109 1171 11BF; # (슄; 슄; 슄; 슄; 슄; ) HANGUL SYLLABLE SWIK
+C285;C285;1109 1171 11C0;C285;1109 1171 11C0; # (슅; 슅; 슅; 슅; 슅; ) HANGUL SYLLABLE SWIT
+C286;C286;1109 1171 11C1;C286;1109 1171 11C1; # (슆; 슆; 쉬á‡; 슆; 쉬á‡; ) HANGUL SYLLABLE SWIP
+C287;C287;1109 1171 11C2;C287;1109 1171 11C2; # (슇; 슇; 슇; 슇; 슇; ) HANGUL SYLLABLE SWIH
+C288;C288;1109 1172;C288;1109 1172; # (슈; 슈; 슈; 슈; 슈; ) HANGUL SYLLABLE SYU
+C289;C289;1109 1172 11A8;C289;1109 1172 11A8; # (슉; 슉; 슉; 슉; 슉; ) HANGUL SYLLABLE SYUG
+C28A;C28A;1109 1172 11A9;C28A;1109 1172 11A9; # (슊; 슊; 슊; 슊; 슊; ) HANGUL SYLLABLE SYUGG
+C28B;C28B;1109 1172 11AA;C28B;1109 1172 11AA; # (슋; 슋; 슋; 슋; 슋; ) HANGUL SYLLABLE SYUGS
+C28C;C28C;1109 1172 11AB;C28C;1109 1172 11AB; # (슌; 슌; 슌; 슌; 슌; ) HANGUL SYLLABLE SYUN
+C28D;C28D;1109 1172 11AC;C28D;1109 1172 11AC; # (ìŠ; ìŠ; 슍; ìŠ; 슍; ) HANGUL SYLLABLE SYUNJ
+C28E;C28E;1109 1172 11AD;C28E;1109 1172 11AD; # (슎; 슎; 슎; 슎; 슎; ) HANGUL SYLLABLE SYUNH
+C28F;C28F;1109 1172 11AE;C28F;1109 1172 11AE; # (ìŠ; ìŠ; 슏; ìŠ; 슏; ) HANGUL SYLLABLE SYUD
+C290;C290;1109 1172 11AF;C290;1109 1172 11AF; # (ìŠ; ìŠ; 슐; ìŠ; 슐; ) HANGUL SYLLABLE SYUL
+C291;C291;1109 1172 11B0;C291;1109 1172 11B0; # (슑; 슑; 슑; 슑; 슑; ) HANGUL SYLLABLE SYULG
+C292;C292;1109 1172 11B1;C292;1109 1172 11B1; # (슒; 슒; 슒; 슒; 슒; ) HANGUL SYLLABLE SYULM
+C293;C293;1109 1172 11B2;C293;1109 1172 11B2; # (슓; 슓; 슓; 슓; 슓; ) HANGUL SYLLABLE SYULB
+C294;C294;1109 1172 11B3;C294;1109 1172 11B3; # (슔; 슔; 슔; 슔; 슔; ) HANGUL SYLLABLE SYULS
+C295;C295;1109 1172 11B4;C295;1109 1172 11B4; # (슕; 슕; 슕; 슕; 슕; ) HANGUL SYLLABLE SYULT
+C296;C296;1109 1172 11B5;C296;1109 1172 11B5; # (슖; 슖; 슖; 슖; 슖; ) HANGUL SYLLABLE SYULP
+C297;C297;1109 1172 11B6;C297;1109 1172 11B6; # (슗; 슗; 슗; 슗; 슗; ) HANGUL SYLLABLE SYULH
+C298;C298;1109 1172 11B7;C298;1109 1172 11B7; # (슘; 슘; 슘; 슘; 슘; ) HANGUL SYLLABLE SYUM
+C299;C299;1109 1172 11B8;C299;1109 1172 11B8; # (슙; 슙; 슙; 슙; 슙; ) HANGUL SYLLABLE SYUB
+C29A;C29A;1109 1172 11B9;C29A;1109 1172 11B9; # (슚; 슚; 슚; 슚; 슚; ) HANGUL SYLLABLE SYUBS
+C29B;C29B;1109 1172 11BA;C29B;1109 1172 11BA; # (슛; 슛; 슛; 슛; 슛; ) HANGUL SYLLABLE SYUS
+C29C;C29C;1109 1172 11BB;C29C;1109 1172 11BB; # (슜; 슜; 슜; 슜; 슜; ) HANGUL SYLLABLE SYUSS
+C29D;C29D;1109 1172 11BC;C29D;1109 1172 11BC; # (ìŠ; ìŠ; 슝; ìŠ; 슝; ) HANGUL SYLLABLE SYUNG
+C29E;C29E;1109 1172 11BD;C29E;1109 1172 11BD; # (슞; 슞; 슞; 슞; 슞; ) HANGUL SYLLABLE SYUJ
+C29F;C29F;1109 1172 11BE;C29F;1109 1172 11BE; # (슟; 슟; 슟; 슟; 슟; ) HANGUL SYLLABLE SYUC
+C2A0;C2A0;1109 1172 11BF;C2A0;1109 1172 11BF; # (슠; 슠; 슠; 슠; 슠; ) HANGUL SYLLABLE SYUK
+C2A1;C2A1;1109 1172 11C0;C2A1;1109 1172 11C0; # (슡; 슡; 슡; 슡; 슡; ) HANGUL SYLLABLE SYUT
+C2A2;C2A2;1109 1172 11C1;C2A2;1109 1172 11C1; # (슢; 슢; 슈á‡; 슢; 슈á‡; ) HANGUL SYLLABLE SYUP
+C2A3;C2A3;1109 1172 11C2;C2A3;1109 1172 11C2; # (슣; 슣; 슣; 슣; 슣; ) HANGUL SYLLABLE SYUH
+C2A4;C2A4;1109 1173;C2A4;1109 1173; # (스; 스; 스; 스; 스; ) HANGUL SYLLABLE SEU
+C2A5;C2A5;1109 1173 11A8;C2A5;1109 1173 11A8; # (슥; 슥; 슥; 슥; 슥; ) HANGUL SYLLABLE SEUG
+C2A6;C2A6;1109 1173 11A9;C2A6;1109 1173 11A9; # (슦; 슦; 슦; 슦; 슦; ) HANGUL SYLLABLE SEUGG
+C2A7;C2A7;1109 1173 11AA;C2A7;1109 1173 11AA; # (슧; 슧; 슧; 슧; 슧; ) HANGUL SYLLABLE SEUGS
+C2A8;C2A8;1109 1173 11AB;C2A8;1109 1173 11AB; # (슨; 슨; 슨; 슨; 슨; ) HANGUL SYLLABLE SEUN
+C2A9;C2A9;1109 1173 11AC;C2A9;1109 1173 11AC; # (슩; 슩; 슩; 슩; 슩; ) HANGUL SYLLABLE SEUNJ
+C2AA;C2AA;1109 1173 11AD;C2AA;1109 1173 11AD; # (슪; 슪; 슪; 슪; 슪; ) HANGUL SYLLABLE SEUNH
+C2AB;C2AB;1109 1173 11AE;C2AB;1109 1173 11AE; # (슫; 슫; 슫; 슫; 슫; ) HANGUL SYLLABLE SEUD
+C2AC;C2AC;1109 1173 11AF;C2AC;1109 1173 11AF; # (슬; 슬; 슬; 슬; 슬; ) HANGUL SYLLABLE SEUL
+C2AD;C2AD;1109 1173 11B0;C2AD;1109 1173 11B0; # (슭; 슭; 슭; 슭; 슭; ) HANGUL SYLLABLE SEULG
+C2AE;C2AE;1109 1173 11B1;C2AE;1109 1173 11B1; # (슮; 슮; 슮; 슮; 슮; ) HANGUL SYLLABLE SEULM
+C2AF;C2AF;1109 1173 11B2;C2AF;1109 1173 11B2; # (슯; 슯; 슯; 슯; 슯; ) HANGUL SYLLABLE SEULB
+C2B0;C2B0;1109 1173 11B3;C2B0;1109 1173 11B3; # (슰; 슰; 슰; 슰; 슰; ) HANGUL SYLLABLE SEULS
+C2B1;C2B1;1109 1173 11B4;C2B1;1109 1173 11B4; # (슱; 슱; 슱; 슱; 슱; ) HANGUL SYLLABLE SEULT
+C2B2;C2B2;1109 1173 11B5;C2B2;1109 1173 11B5; # (슲; 슲; 슲; 슲; 슲; ) HANGUL SYLLABLE SEULP
+C2B3;C2B3;1109 1173 11B6;C2B3;1109 1173 11B6; # (슳; 슳; 슳; 슳; 슳; ) HANGUL SYLLABLE SEULH
+C2B4;C2B4;1109 1173 11B7;C2B4;1109 1173 11B7; # (슴; 슴; 슴; 슴; 슴; ) HANGUL SYLLABLE SEUM
+C2B5;C2B5;1109 1173 11B8;C2B5;1109 1173 11B8; # (습; 습; 습; 습; 습; ) HANGUL SYLLABLE SEUB
+C2B6;C2B6;1109 1173 11B9;C2B6;1109 1173 11B9; # (슶; 슶; 슶; 슶; 슶; ) HANGUL SYLLABLE SEUBS
+C2B7;C2B7;1109 1173 11BA;C2B7;1109 1173 11BA; # (슷; 슷; 슷; 슷; 슷; ) HANGUL SYLLABLE SEUS
+C2B8;C2B8;1109 1173 11BB;C2B8;1109 1173 11BB; # (슸; 슸; 슸; 슸; 슸; ) HANGUL SYLLABLE SEUSS
+C2B9;C2B9;1109 1173 11BC;C2B9;1109 1173 11BC; # (승; 승; 승; 승; 승; ) HANGUL SYLLABLE SEUNG
+C2BA;C2BA;1109 1173 11BD;C2BA;1109 1173 11BD; # (슺; 슺; 슺; 슺; 슺; ) HANGUL SYLLABLE SEUJ
+C2BB;C2BB;1109 1173 11BE;C2BB;1109 1173 11BE; # (슻; 슻; 슻; 슻; 슻; ) HANGUL SYLLABLE SEUC
+C2BC;C2BC;1109 1173 11BF;C2BC;1109 1173 11BF; # (슼; 슼; 슼; 슼; 슼; ) HANGUL SYLLABLE SEUK
+C2BD;C2BD;1109 1173 11C0;C2BD;1109 1173 11C0; # (슽; 슽; 슽; 슽; 슽; ) HANGUL SYLLABLE SEUT
+C2BE;C2BE;1109 1173 11C1;C2BE;1109 1173 11C1; # (슾; 슾; 스á‡; 슾; 스á‡; ) HANGUL SYLLABLE SEUP
+C2BF;C2BF;1109 1173 11C2;C2BF;1109 1173 11C2; # (슿; 슿; 슿; 슿; 슿; ) HANGUL SYLLABLE SEUH
+C2C0;C2C0;1109 1174;C2C0;1109 1174; # (싀; 싀; 싀; 싀; 싀; ) HANGUL SYLLABLE SYI
+C2C1;C2C1;1109 1174 11A8;C2C1;1109 1174 11A8; # (ì‹; ì‹; 싁; ì‹; 싁; ) HANGUL SYLLABLE SYIG
+C2C2;C2C2;1109 1174 11A9;C2C2;1109 1174 11A9; # (싂; 싂; 싂; 싂; 싂; ) HANGUL SYLLABLE SYIGG
+C2C3;C2C3;1109 1174 11AA;C2C3;1109 1174 11AA; # (싃; 싃; 싃; 싃; 싃; ) HANGUL SYLLABLE SYIGS
+C2C4;C2C4;1109 1174 11AB;C2C4;1109 1174 11AB; # (싄; 싄; 싄; 싄; 싄; ) HANGUL SYLLABLE SYIN
+C2C5;C2C5;1109 1174 11AC;C2C5;1109 1174 11AC; # (싅; 싅; 싅; 싅; 싅; ) HANGUL SYLLABLE SYINJ
+C2C6;C2C6;1109 1174 11AD;C2C6;1109 1174 11AD; # (싆; 싆; 싆; 싆; 싆; ) HANGUL SYLLABLE SYINH
+C2C7;C2C7;1109 1174 11AE;C2C7;1109 1174 11AE; # (싇; 싇; 싇; 싇; 싇; ) HANGUL SYLLABLE SYID
+C2C8;C2C8;1109 1174 11AF;C2C8;1109 1174 11AF; # (싈; 싈; 싈; 싈; 싈; ) HANGUL SYLLABLE SYIL
+C2C9;C2C9;1109 1174 11B0;C2C9;1109 1174 11B0; # (싉; 싉; 싉; 싉; 싉; ) HANGUL SYLLABLE SYILG
+C2CA;C2CA;1109 1174 11B1;C2CA;1109 1174 11B1; # (싊; 싊; 싊; 싊; 싊; ) HANGUL SYLLABLE SYILM
+C2CB;C2CB;1109 1174 11B2;C2CB;1109 1174 11B2; # (싋; 싋; 싋; 싋; 싋; ) HANGUL SYLLABLE SYILB
+C2CC;C2CC;1109 1174 11B3;C2CC;1109 1174 11B3; # (싌; 싌; 싌; 싌; 싌; ) HANGUL SYLLABLE SYILS
+C2CD;C2CD;1109 1174 11B4;C2CD;1109 1174 11B4; # (ì‹; ì‹; 싍; ì‹; 싍; ) HANGUL SYLLABLE SYILT
+C2CE;C2CE;1109 1174 11B5;C2CE;1109 1174 11B5; # (싎; 싎; 싎; 싎; 싎; ) HANGUL SYLLABLE SYILP
+C2CF;C2CF;1109 1174 11B6;C2CF;1109 1174 11B6; # (ì‹; ì‹; 싏; ì‹; 싏; ) HANGUL SYLLABLE SYILH
+C2D0;C2D0;1109 1174 11B7;C2D0;1109 1174 11B7; # (ì‹; ì‹; 싐; ì‹; 싐; ) HANGUL SYLLABLE SYIM
+C2D1;C2D1;1109 1174 11B8;C2D1;1109 1174 11B8; # (싑; 싑; 싑; 싑; 싑; ) HANGUL SYLLABLE SYIB
+C2D2;C2D2;1109 1174 11B9;C2D2;1109 1174 11B9; # (싒; 싒; 싒; 싒; 싒; ) HANGUL SYLLABLE SYIBS
+C2D3;C2D3;1109 1174 11BA;C2D3;1109 1174 11BA; # (싓; 싓; 싓; 싓; 싓; ) HANGUL SYLLABLE SYIS
+C2D4;C2D4;1109 1174 11BB;C2D4;1109 1174 11BB; # (싔; 싔; 싔; 싔; 싔; ) HANGUL SYLLABLE SYISS
+C2D5;C2D5;1109 1174 11BC;C2D5;1109 1174 11BC; # (싕; 싕; 싕; 싕; 싕; ) HANGUL SYLLABLE SYING
+C2D6;C2D6;1109 1174 11BD;C2D6;1109 1174 11BD; # (싖; 싖; 싖; 싖; 싖; ) HANGUL SYLLABLE SYIJ
+C2D7;C2D7;1109 1174 11BE;C2D7;1109 1174 11BE; # (싗; 싗; 싗; 싗; 싗; ) HANGUL SYLLABLE SYIC
+C2D8;C2D8;1109 1174 11BF;C2D8;1109 1174 11BF; # (싘; 싘; 싘; 싘; 싘; ) HANGUL SYLLABLE SYIK
+C2D9;C2D9;1109 1174 11C0;C2D9;1109 1174 11C0; # (싙; 싙; 싙; 싙; 싙; ) HANGUL SYLLABLE SYIT
+C2DA;C2DA;1109 1174 11C1;C2DA;1109 1174 11C1; # (싚; 싚; 싀á‡; 싚; 싀á‡; ) HANGUL SYLLABLE SYIP
+C2DB;C2DB;1109 1174 11C2;C2DB;1109 1174 11C2; # (싛; 싛; 싛; 싛; 싛; ) HANGUL SYLLABLE SYIH
+C2DC;C2DC;1109 1175;C2DC;1109 1175; # (시; 시; 시; 시; 시; ) HANGUL SYLLABLE SI
+C2DD;C2DD;1109 1175 11A8;C2DD;1109 1175 11A8; # (ì‹; ì‹; 식; ì‹; 식; ) HANGUL SYLLABLE SIG
+C2DE;C2DE;1109 1175 11A9;C2DE;1109 1175 11A9; # (싞; 싞; 싞; 싞; 싞; ) HANGUL SYLLABLE SIGG
+C2DF;C2DF;1109 1175 11AA;C2DF;1109 1175 11AA; # (싟; 싟; 싟; 싟; 싟; ) HANGUL SYLLABLE SIGS
+C2E0;C2E0;1109 1175 11AB;C2E0;1109 1175 11AB; # (신; 신; 신; 신; 신; ) HANGUL SYLLABLE SIN
+C2E1;C2E1;1109 1175 11AC;C2E1;1109 1175 11AC; # (싡; 싡; 싡; 싡; 싡; ) HANGUL SYLLABLE SINJ
+C2E2;C2E2;1109 1175 11AD;C2E2;1109 1175 11AD; # (싢; 싢; 싢; 싢; 싢; ) HANGUL SYLLABLE SINH
+C2E3;C2E3;1109 1175 11AE;C2E3;1109 1175 11AE; # (싣; 싣; 싣; 싣; 싣; ) HANGUL SYLLABLE SID
+C2E4;C2E4;1109 1175 11AF;C2E4;1109 1175 11AF; # (실; 실; 실; 실; 실; ) HANGUL SYLLABLE SIL
+C2E5;C2E5;1109 1175 11B0;C2E5;1109 1175 11B0; # (싥; 싥; 싥; 싥; 싥; ) HANGUL SYLLABLE SILG
+C2E6;C2E6;1109 1175 11B1;C2E6;1109 1175 11B1; # (싦; 싦; 싦; 싦; 싦; ) HANGUL SYLLABLE SILM
+C2E7;C2E7;1109 1175 11B2;C2E7;1109 1175 11B2; # (싧; 싧; 싧; 싧; 싧; ) HANGUL SYLLABLE SILB
+C2E8;C2E8;1109 1175 11B3;C2E8;1109 1175 11B3; # (싨; 싨; 싨; 싨; 싨; ) HANGUL SYLLABLE SILS
+C2E9;C2E9;1109 1175 11B4;C2E9;1109 1175 11B4; # (싩; 싩; 싩; 싩; 싩; ) HANGUL SYLLABLE SILT
+C2EA;C2EA;1109 1175 11B5;C2EA;1109 1175 11B5; # (싪; 싪; 싪; 싪; 싪; ) HANGUL SYLLABLE SILP
+C2EB;C2EB;1109 1175 11B6;C2EB;1109 1175 11B6; # (싫; 싫; 싫; 싫; 싫; ) HANGUL SYLLABLE SILH
+C2EC;C2EC;1109 1175 11B7;C2EC;1109 1175 11B7; # (심; 심; 심; 심; 심; ) HANGUL SYLLABLE SIM
+C2ED;C2ED;1109 1175 11B8;C2ED;1109 1175 11B8; # (십; 십; 십; 십; 십; ) HANGUL SYLLABLE SIB
+C2EE;C2EE;1109 1175 11B9;C2EE;1109 1175 11B9; # (싮; 싮; 싮; 싮; 싮; ) HANGUL SYLLABLE SIBS
+C2EF;C2EF;1109 1175 11BA;C2EF;1109 1175 11BA; # (싯; 싯; 싯; 싯; 싯; ) HANGUL SYLLABLE SIS
+C2F0;C2F0;1109 1175 11BB;C2F0;1109 1175 11BB; # (싰; 싰; 싰; 싰; 싰; ) HANGUL SYLLABLE SISS
+C2F1;C2F1;1109 1175 11BC;C2F1;1109 1175 11BC; # (싱; 싱; 싱; 싱; 싱; ) HANGUL SYLLABLE SING
+C2F2;C2F2;1109 1175 11BD;C2F2;1109 1175 11BD; # (싲; 싲; 싲; 싲; 싲; ) HANGUL SYLLABLE SIJ
+C2F3;C2F3;1109 1175 11BE;C2F3;1109 1175 11BE; # (싳; 싳; 싳; 싳; 싳; ) HANGUL SYLLABLE SIC
+C2F4;C2F4;1109 1175 11BF;C2F4;1109 1175 11BF; # (싴; 싴; 싴; 싴; 싴; ) HANGUL SYLLABLE SIK
+C2F5;C2F5;1109 1175 11C0;C2F5;1109 1175 11C0; # (싵; 싵; 싵; 싵; 싵; ) HANGUL SYLLABLE SIT
+C2F6;C2F6;1109 1175 11C1;C2F6;1109 1175 11C1; # (ì‹¶; ì‹¶; 시á‡; ì‹¶; 시á‡; ) HANGUL SYLLABLE SIP
+C2F7;C2F7;1109 1175 11C2;C2F7;1109 1175 11C2; # (싷; 싷; 싷; 싷; 싷; ) HANGUL SYLLABLE SIH
+C2F8;C2F8;110A 1161;C2F8;110A 1161; # (싸; 싸; 싸; 싸; 싸; ) HANGUL SYLLABLE SSA
+C2F9;C2F9;110A 1161 11A8;C2F9;110A 1161 11A8; # (싹; 싹; 싹; 싹; 싹; ) HANGUL SYLLABLE SSAG
+C2FA;C2FA;110A 1161 11A9;C2FA;110A 1161 11A9; # (싺; 싺; 싺; 싺; 싺; ) HANGUL SYLLABLE SSAGG
+C2FB;C2FB;110A 1161 11AA;C2FB;110A 1161 11AA; # (싻; 싻; 싻; 싻; 싻; ) HANGUL SYLLABLE SSAGS
+C2FC;C2FC;110A 1161 11AB;C2FC;110A 1161 11AB; # (싼; 싼; 싼; 싼; 싼; ) HANGUL SYLLABLE SSAN
+C2FD;C2FD;110A 1161 11AC;C2FD;110A 1161 11AC; # (싽; 싽; 싽; 싽; 싽; ) HANGUL SYLLABLE SSANJ
+C2FE;C2FE;110A 1161 11AD;C2FE;110A 1161 11AD; # (싾; 싾; 싾; 싾; 싾; ) HANGUL SYLLABLE SSANH
+C2FF;C2FF;110A 1161 11AE;C2FF;110A 1161 11AE; # (싿; 싿; 싿; 싿; 싿; ) HANGUL SYLLABLE SSAD
+C300;C300;110A 1161 11AF;C300;110A 1161 11AF; # (쌀; 쌀; 쌀; 쌀; 쌀; ) HANGUL SYLLABLE SSAL
+C301;C301;110A 1161 11B0;C301;110A 1161 11B0; # (ìŒ; ìŒ; 쌁; ìŒ; 쌁; ) HANGUL SYLLABLE SSALG
+C302;C302;110A 1161 11B1;C302;110A 1161 11B1; # (쌂; 쌂; 쌂; 쌂; 쌂; ) HANGUL SYLLABLE SSALM
+C303;C303;110A 1161 11B2;C303;110A 1161 11B2; # (쌃; 쌃; 쌃; 쌃; 쌃; ) HANGUL SYLLABLE SSALB
+C304;C304;110A 1161 11B3;C304;110A 1161 11B3; # (쌄; 쌄; 쌄; 쌄; 쌄; ) HANGUL SYLLABLE SSALS
+C305;C305;110A 1161 11B4;C305;110A 1161 11B4; # (쌅; 쌅; 쌅; 쌅; 쌅; ) HANGUL SYLLABLE SSALT
+C306;C306;110A 1161 11B5;C306;110A 1161 11B5; # (쌆; 쌆; 쌆; 쌆; 쌆; ) HANGUL SYLLABLE SSALP
+C307;C307;110A 1161 11B6;C307;110A 1161 11B6; # (쌇; 쌇; 쌇; 쌇; 쌇; ) HANGUL SYLLABLE SSALH
+C308;C308;110A 1161 11B7;C308;110A 1161 11B7; # (쌈; 쌈; 쌈; 쌈; 쌈; ) HANGUL SYLLABLE SSAM
+C309;C309;110A 1161 11B8;C309;110A 1161 11B8; # (쌉; 쌉; 쌉; 쌉; 쌉; ) HANGUL SYLLABLE SSAB
+C30A;C30A;110A 1161 11B9;C30A;110A 1161 11B9; # (쌊; 쌊; 쌊; 쌊; 쌊; ) HANGUL SYLLABLE SSABS
+C30B;C30B;110A 1161 11BA;C30B;110A 1161 11BA; # (쌋; 쌋; 쌋; 쌋; 쌋; ) HANGUL SYLLABLE SSAS
+C30C;C30C;110A 1161 11BB;C30C;110A 1161 11BB; # (쌌; 쌌; 쌌; 쌌; 쌌; ) HANGUL SYLLABLE SSASS
+C30D;C30D;110A 1161 11BC;C30D;110A 1161 11BC; # (ìŒ; ìŒ; 쌍; ìŒ; 쌍; ) HANGUL SYLLABLE SSANG
+C30E;C30E;110A 1161 11BD;C30E;110A 1161 11BD; # (쌎; 쌎; 쌎; 쌎; 쌎; ) HANGUL SYLLABLE SSAJ
+C30F;C30F;110A 1161 11BE;C30F;110A 1161 11BE; # (ìŒ; ìŒ; 쌏; ìŒ; 쌏; ) HANGUL SYLLABLE SSAC
+C310;C310;110A 1161 11BF;C310;110A 1161 11BF; # (ìŒ; ìŒ; 쌐; ìŒ; 쌐; ) HANGUL SYLLABLE SSAK
+C311;C311;110A 1161 11C0;C311;110A 1161 11C0; # (쌑; 쌑; 쌑; 쌑; 쌑; ) HANGUL SYLLABLE SSAT
+C312;C312;110A 1161 11C1;C312;110A 1161 11C1; # (쌒; 쌒; 싸á‡; 쌒; 싸á‡; ) HANGUL SYLLABLE SSAP
+C313;C313;110A 1161 11C2;C313;110A 1161 11C2; # (쌓; 쌓; 쌓; 쌓; 쌓; ) HANGUL SYLLABLE SSAH
+C314;C314;110A 1162;C314;110A 1162; # (쌔; 쌔; 쌔; 쌔; 쌔; ) HANGUL SYLLABLE SSAE
+C315;C315;110A 1162 11A8;C315;110A 1162 11A8; # (쌕; 쌕; 쌕; 쌕; 쌕; ) HANGUL SYLLABLE SSAEG
+C316;C316;110A 1162 11A9;C316;110A 1162 11A9; # (쌖; 쌖; 쌖; 쌖; 쌖; ) HANGUL SYLLABLE SSAEGG
+C317;C317;110A 1162 11AA;C317;110A 1162 11AA; # (쌗; 쌗; 쌗; 쌗; 쌗; ) HANGUL SYLLABLE SSAEGS
+C318;C318;110A 1162 11AB;C318;110A 1162 11AB; # (쌘; 쌘; 쌘; 쌘; 쌘; ) HANGUL SYLLABLE SSAEN
+C319;C319;110A 1162 11AC;C319;110A 1162 11AC; # (쌙; 쌙; 쌙; 쌙; 쌙; ) HANGUL SYLLABLE SSAENJ
+C31A;C31A;110A 1162 11AD;C31A;110A 1162 11AD; # (쌚; 쌚; 쌚; 쌚; 쌚; ) HANGUL SYLLABLE SSAENH
+C31B;C31B;110A 1162 11AE;C31B;110A 1162 11AE; # (쌛; 쌛; 쌛; 쌛; 쌛; ) HANGUL SYLLABLE SSAED
+C31C;C31C;110A 1162 11AF;C31C;110A 1162 11AF; # (쌜; 쌜; 쌜; 쌜; 쌜; ) HANGUL SYLLABLE SSAEL
+C31D;C31D;110A 1162 11B0;C31D;110A 1162 11B0; # (ìŒ; ìŒ; 쌝; ìŒ; 쌝; ) HANGUL SYLLABLE SSAELG
+C31E;C31E;110A 1162 11B1;C31E;110A 1162 11B1; # (쌞; 쌞; 쌞; 쌞; 쌞; ) HANGUL SYLLABLE SSAELM
+C31F;C31F;110A 1162 11B2;C31F;110A 1162 11B2; # (쌟; 쌟; 쌟; 쌟; 쌟; ) HANGUL SYLLABLE SSAELB
+C320;C320;110A 1162 11B3;C320;110A 1162 11B3; # (쌠; 쌠; 쌠; 쌠; 쌠; ) HANGUL SYLLABLE SSAELS
+C321;C321;110A 1162 11B4;C321;110A 1162 11B4; # (쌡; 쌡; 쌡; 쌡; 쌡; ) HANGUL SYLLABLE SSAELT
+C322;C322;110A 1162 11B5;C322;110A 1162 11B5; # (쌢; 쌢; 쌢; 쌢; 쌢; ) HANGUL SYLLABLE SSAELP
+C323;C323;110A 1162 11B6;C323;110A 1162 11B6; # (쌣; 쌣; 쌣; 쌣; 쌣; ) HANGUL SYLLABLE SSAELH
+C324;C324;110A 1162 11B7;C324;110A 1162 11B7; # (쌤; 쌤; 쌤; 쌤; 쌤; ) HANGUL SYLLABLE SSAEM
+C325;C325;110A 1162 11B8;C325;110A 1162 11B8; # (쌥; 쌥; 쌥; 쌥; 쌥; ) HANGUL SYLLABLE SSAEB
+C326;C326;110A 1162 11B9;C326;110A 1162 11B9; # (쌦; 쌦; 쌦; 쌦; 쌦; ) HANGUL SYLLABLE SSAEBS
+C327;C327;110A 1162 11BA;C327;110A 1162 11BA; # (쌧; 쌧; 쌧; 쌧; 쌧; ) HANGUL SYLLABLE SSAES
+C328;C328;110A 1162 11BB;C328;110A 1162 11BB; # (쌨; 쌨; 쌨; 쌨; 쌨; ) HANGUL SYLLABLE SSAESS
+C329;C329;110A 1162 11BC;C329;110A 1162 11BC; # (쌩; 쌩; 쌩; 쌩; 쌩; ) HANGUL SYLLABLE SSAENG
+C32A;C32A;110A 1162 11BD;C32A;110A 1162 11BD; # (쌪; 쌪; 쌪; 쌪; 쌪; ) HANGUL SYLLABLE SSAEJ
+C32B;C32B;110A 1162 11BE;C32B;110A 1162 11BE; # (쌫; 쌫; 쌫; 쌫; 쌫; ) HANGUL SYLLABLE SSAEC
+C32C;C32C;110A 1162 11BF;C32C;110A 1162 11BF; # (쌬; 쌬; 쌬; 쌬; 쌬; ) HANGUL SYLLABLE SSAEK
+C32D;C32D;110A 1162 11C0;C32D;110A 1162 11C0; # (쌭; 쌭; 쌭; 쌭; 쌭; ) HANGUL SYLLABLE SSAET
+C32E;C32E;110A 1162 11C1;C32E;110A 1162 11C1; # (쌮; 쌮; 쌔á‡; 쌮; 쌔á‡; ) HANGUL SYLLABLE SSAEP
+C32F;C32F;110A 1162 11C2;C32F;110A 1162 11C2; # (쌯; 쌯; 쌯; 쌯; 쌯; ) HANGUL SYLLABLE SSAEH
+C330;C330;110A 1163;C330;110A 1163; # (쌰; 쌰; 쌰; 쌰; 쌰; ) HANGUL SYLLABLE SSYA
+C331;C331;110A 1163 11A8;C331;110A 1163 11A8; # (쌱; 쌱; 쌱; 쌱; 쌱; ) HANGUL SYLLABLE SSYAG
+C332;C332;110A 1163 11A9;C332;110A 1163 11A9; # (쌲; 쌲; 쌲; 쌲; 쌲; ) HANGUL SYLLABLE SSYAGG
+C333;C333;110A 1163 11AA;C333;110A 1163 11AA; # (쌳; 쌳; 쌳; 쌳; 쌳; ) HANGUL SYLLABLE SSYAGS
+C334;C334;110A 1163 11AB;C334;110A 1163 11AB; # (쌴; 쌴; 쌴; 쌴; 쌴; ) HANGUL SYLLABLE SSYAN
+C335;C335;110A 1163 11AC;C335;110A 1163 11AC; # (쌵; 쌵; 쌵; 쌵; 쌵; ) HANGUL SYLLABLE SSYANJ
+C336;C336;110A 1163 11AD;C336;110A 1163 11AD; # (쌶; 쌶; 쌶; 쌶; 쌶; ) HANGUL SYLLABLE SSYANH
+C337;C337;110A 1163 11AE;C337;110A 1163 11AE; # (쌷; 쌷; 쌷; 쌷; 쌷; ) HANGUL SYLLABLE SSYAD
+C338;C338;110A 1163 11AF;C338;110A 1163 11AF; # (쌸; 쌸; 쌸; 쌸; 쌸; ) HANGUL SYLLABLE SSYAL
+C339;C339;110A 1163 11B0;C339;110A 1163 11B0; # (쌹; 쌹; 쌹; 쌹; 쌹; ) HANGUL SYLLABLE SSYALG
+C33A;C33A;110A 1163 11B1;C33A;110A 1163 11B1; # (쌺; 쌺; 쌺; 쌺; 쌺; ) HANGUL SYLLABLE SSYALM
+C33B;C33B;110A 1163 11B2;C33B;110A 1163 11B2; # (쌻; 쌻; 쌻; 쌻; 쌻; ) HANGUL SYLLABLE SSYALB
+C33C;C33C;110A 1163 11B3;C33C;110A 1163 11B3; # (쌼; 쌼; 쌼; 쌼; 쌼; ) HANGUL SYLLABLE SSYALS
+C33D;C33D;110A 1163 11B4;C33D;110A 1163 11B4; # (쌽; 쌽; 쌽; 쌽; 쌽; ) HANGUL SYLLABLE SSYALT
+C33E;C33E;110A 1163 11B5;C33E;110A 1163 11B5; # (쌾; 쌾; 쌾; 쌾; 쌾; ) HANGUL SYLLABLE SSYALP
+C33F;C33F;110A 1163 11B6;C33F;110A 1163 11B6; # (쌿; 쌿; 쌿; 쌿; 쌿; ) HANGUL SYLLABLE SSYALH
+C340;C340;110A 1163 11B7;C340;110A 1163 11B7; # (ì€; ì€; 썀; ì€; 썀; ) HANGUL SYLLABLE SSYAM
+C341;C341;110A 1163 11B8;C341;110A 1163 11B8; # (ì; ì; 썁; ì; 썁; ) HANGUL SYLLABLE SSYAB
+C342;C342;110A 1163 11B9;C342;110A 1163 11B9; # (ì‚; ì‚; 썂; ì‚; 썂; ) HANGUL SYLLABLE SSYABS
+C343;C343;110A 1163 11BA;C343;110A 1163 11BA; # (ìƒ; ìƒ; 썃; ìƒ; 썃; ) HANGUL SYLLABLE SSYAS
+C344;C344;110A 1163 11BB;C344;110A 1163 11BB; # (ì„; ì„; 썄; ì„; 썄; ) HANGUL SYLLABLE SSYASS
+C345;C345;110A 1163 11BC;C345;110A 1163 11BC; # (ì…; ì…; 썅; ì…; 썅; ) HANGUL SYLLABLE SSYANG
+C346;C346;110A 1163 11BD;C346;110A 1163 11BD; # (ì†; ì†; 썆; ì†; 썆; ) HANGUL SYLLABLE SSYAJ
+C347;C347;110A 1163 11BE;C347;110A 1163 11BE; # (ì‡; ì‡; 썇; ì‡; 썇; ) HANGUL SYLLABLE SSYAC
+C348;C348;110A 1163 11BF;C348;110A 1163 11BF; # (ìˆ; ìˆ; 썈; ìˆ; 썈; ) HANGUL SYLLABLE SSYAK
+C349;C349;110A 1163 11C0;C349;110A 1163 11C0; # (ì‰; ì‰; 썉; ì‰; 썉; ) HANGUL SYLLABLE SSYAT
+C34A;C34A;110A 1163 11C1;C34A;110A 1163 11C1; # (ìŠ; ìŠ; 쌰á‡; ìŠ; 쌰á‡; ) HANGUL SYLLABLE SSYAP
+C34B;C34B;110A 1163 11C2;C34B;110A 1163 11C2; # (ì‹; ì‹; 썋; ì‹; 썋; ) HANGUL SYLLABLE SSYAH
+C34C;C34C;110A 1164;C34C;110A 1164; # (ìŒ; ìŒ; 썌; ìŒ; 썌; ) HANGUL SYLLABLE SSYAE
+C34D;C34D;110A 1164 11A8;C34D;110A 1164 11A8; # (ì; ì; 썍; ì; 썍; ) HANGUL SYLLABLE SSYAEG
+C34E;C34E;110A 1164 11A9;C34E;110A 1164 11A9; # (ìŽ; ìŽ; 썎; ìŽ; 썎; ) HANGUL SYLLABLE SSYAEGG
+C34F;C34F;110A 1164 11AA;C34F;110A 1164 11AA; # (ì; ì; 썏; ì; 썏; ) HANGUL SYLLABLE SSYAEGS
+C350;C350;110A 1164 11AB;C350;110A 1164 11AB; # (ì; ì; 썐; ì; 썐; ) HANGUL SYLLABLE SSYAEN
+C351;C351;110A 1164 11AC;C351;110A 1164 11AC; # (ì‘; ì‘; 썑; ì‘; 썑; ) HANGUL SYLLABLE SSYAENJ
+C352;C352;110A 1164 11AD;C352;110A 1164 11AD; # (ì’; ì’; 썒; ì’; 썒; ) HANGUL SYLLABLE SSYAENH
+C353;C353;110A 1164 11AE;C353;110A 1164 11AE; # (ì“; ì“; 썓; ì“; 썓; ) HANGUL SYLLABLE SSYAED
+C354;C354;110A 1164 11AF;C354;110A 1164 11AF; # (ì”; ì”; 썔; ì”; 썔; ) HANGUL SYLLABLE SSYAEL
+C355;C355;110A 1164 11B0;C355;110A 1164 11B0; # (ì•; ì•; 썕; ì•; 썕; ) HANGUL SYLLABLE SSYAELG
+C356;C356;110A 1164 11B1;C356;110A 1164 11B1; # (ì–; ì–; 썖; ì–; 썖; ) HANGUL SYLLABLE SSYAELM
+C357;C357;110A 1164 11B2;C357;110A 1164 11B2; # (ì—; ì—; 썗; ì—; 썗; ) HANGUL SYLLABLE SSYAELB
+C358;C358;110A 1164 11B3;C358;110A 1164 11B3; # (ì˜; ì˜; 썘; ì˜; 썘; ) HANGUL SYLLABLE SSYAELS
+C359;C359;110A 1164 11B4;C359;110A 1164 11B4; # (ì™; ì™; 썙; ì™; 썙; ) HANGUL SYLLABLE SSYAELT
+C35A;C35A;110A 1164 11B5;C35A;110A 1164 11B5; # (ìš; ìš; 썚; ìš; 썚; ) HANGUL SYLLABLE SSYAELP
+C35B;C35B;110A 1164 11B6;C35B;110A 1164 11B6; # (ì›; ì›; 썛; ì›; 썛; ) HANGUL SYLLABLE SSYAELH
+C35C;C35C;110A 1164 11B7;C35C;110A 1164 11B7; # (ìœ; ìœ; 썜; ìœ; 썜; ) HANGUL SYLLABLE SSYAEM
+C35D;C35D;110A 1164 11B8;C35D;110A 1164 11B8; # (ì; ì; 썝; ì; 썝; ) HANGUL SYLLABLE SSYAEB
+C35E;C35E;110A 1164 11B9;C35E;110A 1164 11B9; # (ìž; ìž; 썞; ìž; 썞; ) HANGUL SYLLABLE SSYAEBS
+C35F;C35F;110A 1164 11BA;C35F;110A 1164 11BA; # (ìŸ; ìŸ; 썟; ìŸ; 썟; ) HANGUL SYLLABLE SSYAES
+C360;C360;110A 1164 11BB;C360;110A 1164 11BB; # (ì ; ì ; 썠; ì ; 썠; ) HANGUL SYLLABLE SSYAESS
+C361;C361;110A 1164 11BC;C361;110A 1164 11BC; # (ì¡; ì¡; 썡; ì¡; 썡; ) HANGUL SYLLABLE SSYAENG
+C362;C362;110A 1164 11BD;C362;110A 1164 11BD; # (ì¢; ì¢; 썢; ì¢; 썢; ) HANGUL SYLLABLE SSYAEJ
+C363;C363;110A 1164 11BE;C363;110A 1164 11BE; # (ì£; ì£; 썣; ì£; 썣; ) HANGUL SYLLABLE SSYAEC
+C364;C364;110A 1164 11BF;C364;110A 1164 11BF; # (ì¤; ì¤; 썤; ì¤; 썤; ) HANGUL SYLLABLE SSYAEK
+C365;C365;110A 1164 11C0;C365;110A 1164 11C0; # (ì¥; ì¥; 썥; ì¥; 썥; ) HANGUL SYLLABLE SSYAET
+C366;C366;110A 1164 11C1;C366;110A 1164 11C1; # (ì¦; ì¦; 썌á‡; ì¦; 썌á‡; ) HANGUL SYLLABLE SSYAEP
+C367;C367;110A 1164 11C2;C367;110A 1164 11C2; # (ì§; ì§; 썧; ì§; 썧; ) HANGUL SYLLABLE SSYAEH
+C368;C368;110A 1165;C368;110A 1165; # (ì¨; ì¨; 써; ì¨; 써; ) HANGUL SYLLABLE SSEO
+C369;C369;110A 1165 11A8;C369;110A 1165 11A8; # (ì©; ì©; 썩; ì©; 썩; ) HANGUL SYLLABLE SSEOG
+C36A;C36A;110A 1165 11A9;C36A;110A 1165 11A9; # (ìª; ìª; 썪; ìª; 썪; ) HANGUL SYLLABLE SSEOGG
+C36B;C36B;110A 1165 11AA;C36B;110A 1165 11AA; # (ì«; ì«; 썫; ì«; 썫; ) HANGUL SYLLABLE SSEOGS
+C36C;C36C;110A 1165 11AB;C36C;110A 1165 11AB; # (ì¬; ì¬; 썬; ì¬; 썬; ) HANGUL SYLLABLE SSEON
+C36D;C36D;110A 1165 11AC;C36D;110A 1165 11AC; # (ì­; ì­; 썭; ì­; 썭; ) HANGUL SYLLABLE SSEONJ
+C36E;C36E;110A 1165 11AD;C36E;110A 1165 11AD; # (ì®; ì®; 썮; ì®; 썮; ) HANGUL SYLLABLE SSEONH
+C36F;C36F;110A 1165 11AE;C36F;110A 1165 11AE; # (ì¯; ì¯; 썯; ì¯; 썯; ) HANGUL SYLLABLE SSEOD
+C370;C370;110A 1165 11AF;C370;110A 1165 11AF; # (ì°; ì°; 썰; ì°; 썰; ) HANGUL SYLLABLE SSEOL
+C371;C371;110A 1165 11B0;C371;110A 1165 11B0; # (ì±; ì±; 썱; ì±; 썱; ) HANGUL SYLLABLE SSEOLG
+C372;C372;110A 1165 11B1;C372;110A 1165 11B1; # (ì²; ì²; 썲; ì²; 썲; ) HANGUL SYLLABLE SSEOLM
+C373;C373;110A 1165 11B2;C373;110A 1165 11B2; # (ì³; ì³; 썳; ì³; 썳; ) HANGUL SYLLABLE SSEOLB
+C374;C374;110A 1165 11B3;C374;110A 1165 11B3; # (ì´; ì´; 썴; ì´; 썴; ) HANGUL SYLLABLE SSEOLS
+C375;C375;110A 1165 11B4;C375;110A 1165 11B4; # (ìµ; ìµ; 썵; ìµ; 썵; ) HANGUL SYLLABLE SSEOLT
+C376;C376;110A 1165 11B5;C376;110A 1165 11B5; # (ì¶; ì¶; 썶; ì¶; 썶; ) HANGUL SYLLABLE SSEOLP
+C377;C377;110A 1165 11B6;C377;110A 1165 11B6; # (ì·; ì·; 썷; ì·; 썷; ) HANGUL SYLLABLE SSEOLH
+C378;C378;110A 1165 11B7;C378;110A 1165 11B7; # (ì¸; ì¸; 썸; ì¸; 썸; ) HANGUL SYLLABLE SSEOM
+C379;C379;110A 1165 11B8;C379;110A 1165 11B8; # (ì¹; ì¹; 썹; ì¹; 썹; ) HANGUL SYLLABLE SSEOB
+C37A;C37A;110A 1165 11B9;C37A;110A 1165 11B9; # (ìº; ìº; 썺; ìº; 썺; ) HANGUL SYLLABLE SSEOBS
+C37B;C37B;110A 1165 11BA;C37B;110A 1165 11BA; # (ì»; ì»; 썻; ì»; 썻; ) HANGUL SYLLABLE SSEOS
+C37C;C37C;110A 1165 11BB;C37C;110A 1165 11BB; # (ì¼; ì¼; 썼; ì¼; 썼; ) HANGUL SYLLABLE SSEOSS
+C37D;C37D;110A 1165 11BC;C37D;110A 1165 11BC; # (ì½; ì½; 썽; ì½; 썽; ) HANGUL SYLLABLE SSEONG
+C37E;C37E;110A 1165 11BD;C37E;110A 1165 11BD; # (ì¾; ì¾; 썾; ì¾; 썾; ) HANGUL SYLLABLE SSEOJ
+C37F;C37F;110A 1165 11BE;C37F;110A 1165 11BE; # (ì¿; ì¿; 썿; ì¿; 썿; ) HANGUL SYLLABLE SSEOC
+C380;C380;110A 1165 11BF;C380;110A 1165 11BF; # (쎀; 쎀; 쎀; 쎀; 쎀; ) HANGUL SYLLABLE SSEOK
+C381;C381;110A 1165 11C0;C381;110A 1165 11C0; # (ìŽ; ìŽ; 쎁; ìŽ; 쎁; ) HANGUL SYLLABLE SSEOT
+C382;C382;110A 1165 11C1;C382;110A 1165 11C1; # (쎂; 쎂; 써á‡; 쎂; 써á‡; ) HANGUL SYLLABLE SSEOP
+C383;C383;110A 1165 11C2;C383;110A 1165 11C2; # (쎃; 쎃; 쎃; 쎃; 쎃; ) HANGUL SYLLABLE SSEOH
+C384;C384;110A 1166;C384;110A 1166; # (쎄; 쎄; 쎄; 쎄; 쎄; ) HANGUL SYLLABLE SSE
+C385;C385;110A 1166 11A8;C385;110A 1166 11A8; # (쎅; 쎅; 쎅; 쎅; 쎅; ) HANGUL SYLLABLE SSEG
+C386;C386;110A 1166 11A9;C386;110A 1166 11A9; # (쎆; 쎆; 쎆; 쎆; 쎆; ) HANGUL SYLLABLE SSEGG
+C387;C387;110A 1166 11AA;C387;110A 1166 11AA; # (쎇; 쎇; 쎇; 쎇; 쎇; ) HANGUL SYLLABLE SSEGS
+C388;C388;110A 1166 11AB;C388;110A 1166 11AB; # (쎈; 쎈; 쎈; 쎈; 쎈; ) HANGUL SYLLABLE SSEN
+C389;C389;110A 1166 11AC;C389;110A 1166 11AC; # (쎉; 쎉; 쎉; 쎉; 쎉; ) HANGUL SYLLABLE SSENJ
+C38A;C38A;110A 1166 11AD;C38A;110A 1166 11AD; # (쎊; 쎊; 쎊; 쎊; 쎊; ) HANGUL SYLLABLE SSENH
+C38B;C38B;110A 1166 11AE;C38B;110A 1166 11AE; # (쎋; 쎋; 쎋; 쎋; 쎋; ) HANGUL SYLLABLE SSED
+C38C;C38C;110A 1166 11AF;C38C;110A 1166 11AF; # (쎌; 쎌; 쎌; 쎌; 쎌; ) HANGUL SYLLABLE SSEL
+C38D;C38D;110A 1166 11B0;C38D;110A 1166 11B0; # (ìŽ; ìŽ; 쎍; ìŽ; 쎍; ) HANGUL SYLLABLE SSELG
+C38E;C38E;110A 1166 11B1;C38E;110A 1166 11B1; # (쎎; 쎎; 쎎; 쎎; 쎎; ) HANGUL SYLLABLE SSELM
+C38F;C38F;110A 1166 11B2;C38F;110A 1166 11B2; # (ìŽ; ìŽ; 쎏; ìŽ; 쎏; ) HANGUL SYLLABLE SSELB
+C390;C390;110A 1166 11B3;C390;110A 1166 11B3; # (ìŽ; ìŽ; 쎐; ìŽ; 쎐; ) HANGUL SYLLABLE SSELS
+C391;C391;110A 1166 11B4;C391;110A 1166 11B4; # (쎑; 쎑; 쎑; 쎑; 쎑; ) HANGUL SYLLABLE SSELT
+C392;C392;110A 1166 11B5;C392;110A 1166 11B5; # (쎒; 쎒; 쎒; 쎒; 쎒; ) HANGUL SYLLABLE SSELP
+C393;C393;110A 1166 11B6;C393;110A 1166 11B6; # (쎓; 쎓; 쎓; 쎓; 쎓; ) HANGUL SYLLABLE SSELH
+C394;C394;110A 1166 11B7;C394;110A 1166 11B7; # (쎔; 쎔; 쎔; 쎔; 쎔; ) HANGUL SYLLABLE SSEM
+C395;C395;110A 1166 11B8;C395;110A 1166 11B8; # (쎕; 쎕; 쎕; 쎕; 쎕; ) HANGUL SYLLABLE SSEB
+C396;C396;110A 1166 11B9;C396;110A 1166 11B9; # (쎖; 쎖; 쎖; 쎖; 쎖; ) HANGUL SYLLABLE SSEBS
+C397;C397;110A 1166 11BA;C397;110A 1166 11BA; # (쎗; 쎗; 쎗; 쎗; 쎗; ) HANGUL SYLLABLE SSES
+C398;C398;110A 1166 11BB;C398;110A 1166 11BB; # (쎘; 쎘; 쎘; 쎘; 쎘; ) HANGUL SYLLABLE SSESS
+C399;C399;110A 1166 11BC;C399;110A 1166 11BC; # (쎙; 쎙; 쎙; 쎙; 쎙; ) HANGUL SYLLABLE SSENG
+C39A;C39A;110A 1166 11BD;C39A;110A 1166 11BD; # (쎚; 쎚; 쎚; 쎚; 쎚; ) HANGUL SYLLABLE SSEJ
+C39B;C39B;110A 1166 11BE;C39B;110A 1166 11BE; # (쎛; 쎛; 쎛; 쎛; 쎛; ) HANGUL SYLLABLE SSEC
+C39C;C39C;110A 1166 11BF;C39C;110A 1166 11BF; # (쎜; 쎜; 쎜; 쎜; 쎜; ) HANGUL SYLLABLE SSEK
+C39D;C39D;110A 1166 11C0;C39D;110A 1166 11C0; # (ìŽ; ìŽ; 쎝; ìŽ; 쎝; ) HANGUL SYLLABLE SSET
+C39E;C39E;110A 1166 11C1;C39E;110A 1166 11C1; # (쎞; 쎞; 쎄á‡; 쎞; 쎄á‡; ) HANGUL SYLLABLE SSEP
+C39F;C39F;110A 1166 11C2;C39F;110A 1166 11C2; # (쎟; 쎟; 쎟; 쎟; 쎟; ) HANGUL SYLLABLE SSEH
+C3A0;C3A0;110A 1167;C3A0;110A 1167; # (쎠; 쎠; 쎠; 쎠; 쎠; ) HANGUL SYLLABLE SSYEO
+C3A1;C3A1;110A 1167 11A8;C3A1;110A 1167 11A8; # (쎡; 쎡; 쎡; 쎡; 쎡; ) HANGUL SYLLABLE SSYEOG
+C3A2;C3A2;110A 1167 11A9;C3A2;110A 1167 11A9; # (쎢; 쎢; 쎢; 쎢; 쎢; ) HANGUL SYLLABLE SSYEOGG
+C3A3;C3A3;110A 1167 11AA;C3A3;110A 1167 11AA; # (쎣; 쎣; 쎣; 쎣; 쎣; ) HANGUL SYLLABLE SSYEOGS
+C3A4;C3A4;110A 1167 11AB;C3A4;110A 1167 11AB; # (쎤; 쎤; 쎤; 쎤; 쎤; ) HANGUL SYLLABLE SSYEON
+C3A5;C3A5;110A 1167 11AC;C3A5;110A 1167 11AC; # (쎥; 쎥; 쎥; 쎥; 쎥; ) HANGUL SYLLABLE SSYEONJ
+C3A6;C3A6;110A 1167 11AD;C3A6;110A 1167 11AD; # (쎦; 쎦; 쎦; 쎦; 쎦; ) HANGUL SYLLABLE SSYEONH
+C3A7;C3A7;110A 1167 11AE;C3A7;110A 1167 11AE; # (쎧; 쎧; 쎧; 쎧; 쎧; ) HANGUL SYLLABLE SSYEOD
+C3A8;C3A8;110A 1167 11AF;C3A8;110A 1167 11AF; # (쎨; 쎨; 쎨; 쎨; 쎨; ) HANGUL SYLLABLE SSYEOL
+C3A9;C3A9;110A 1167 11B0;C3A9;110A 1167 11B0; # (쎩; 쎩; 쎩; 쎩; 쎩; ) HANGUL SYLLABLE SSYEOLG
+C3AA;C3AA;110A 1167 11B1;C3AA;110A 1167 11B1; # (쎪; 쎪; 쎪; 쎪; 쎪; ) HANGUL SYLLABLE SSYEOLM
+C3AB;C3AB;110A 1167 11B2;C3AB;110A 1167 11B2; # (쎫; 쎫; 쎫; 쎫; 쎫; ) HANGUL SYLLABLE SSYEOLB
+C3AC;C3AC;110A 1167 11B3;C3AC;110A 1167 11B3; # (쎬; 쎬; 쎬; 쎬; 쎬; ) HANGUL SYLLABLE SSYEOLS
+C3AD;C3AD;110A 1167 11B4;C3AD;110A 1167 11B4; # (쎭; 쎭; 쎭; 쎭; 쎭; ) HANGUL SYLLABLE SSYEOLT
+C3AE;C3AE;110A 1167 11B5;C3AE;110A 1167 11B5; # (쎮; 쎮; 쎮; 쎮; 쎮; ) HANGUL SYLLABLE SSYEOLP
+C3AF;C3AF;110A 1167 11B6;C3AF;110A 1167 11B6; # (쎯; 쎯; 쎯; 쎯; 쎯; ) HANGUL SYLLABLE SSYEOLH
+C3B0;C3B0;110A 1167 11B7;C3B0;110A 1167 11B7; # (쎰; 쎰; 쎰; 쎰; 쎰; ) HANGUL SYLLABLE SSYEOM
+C3B1;C3B1;110A 1167 11B8;C3B1;110A 1167 11B8; # (쎱; 쎱; 쎱; 쎱; 쎱; ) HANGUL SYLLABLE SSYEOB
+C3B2;C3B2;110A 1167 11B9;C3B2;110A 1167 11B9; # (쎲; 쎲; 쎲; 쎲; 쎲; ) HANGUL SYLLABLE SSYEOBS
+C3B3;C3B3;110A 1167 11BA;C3B3;110A 1167 11BA; # (쎳; 쎳; 쎳; 쎳; 쎳; ) HANGUL SYLLABLE SSYEOS
+C3B4;C3B4;110A 1167 11BB;C3B4;110A 1167 11BB; # (쎴; 쎴; 쎴; 쎴; 쎴; ) HANGUL SYLLABLE SSYEOSS
+C3B5;C3B5;110A 1167 11BC;C3B5;110A 1167 11BC; # (쎵; 쎵; 쎵; 쎵; 쎵; ) HANGUL SYLLABLE SSYEONG
+C3B6;C3B6;110A 1167 11BD;C3B6;110A 1167 11BD; # (쎶; 쎶; 쎶; 쎶; 쎶; ) HANGUL SYLLABLE SSYEOJ
+C3B7;C3B7;110A 1167 11BE;C3B7;110A 1167 11BE; # (쎷; 쎷; 쎷; 쎷; 쎷; ) HANGUL SYLLABLE SSYEOC
+C3B8;C3B8;110A 1167 11BF;C3B8;110A 1167 11BF; # (쎸; 쎸; 쎸; 쎸; 쎸; ) HANGUL SYLLABLE SSYEOK
+C3B9;C3B9;110A 1167 11C0;C3B9;110A 1167 11C0; # (쎹; 쎹; 쎹; 쎹; 쎹; ) HANGUL SYLLABLE SSYEOT
+C3BA;C3BA;110A 1167 11C1;C3BA;110A 1167 11C1; # (쎺; 쎺; 쎠á‡; 쎺; 쎠á‡; ) HANGUL SYLLABLE SSYEOP
+C3BB;C3BB;110A 1167 11C2;C3BB;110A 1167 11C2; # (쎻; 쎻; 쎻; 쎻; 쎻; ) HANGUL SYLLABLE SSYEOH
+C3BC;C3BC;110A 1168;C3BC;110A 1168; # (쎼; 쎼; 쎼; 쎼; 쎼; ) HANGUL SYLLABLE SSYE
+C3BD;C3BD;110A 1168 11A8;C3BD;110A 1168 11A8; # (쎽; 쎽; 쎽; 쎽; 쎽; ) HANGUL SYLLABLE SSYEG
+C3BE;C3BE;110A 1168 11A9;C3BE;110A 1168 11A9; # (쎾; 쎾; 쎾; 쎾; 쎾; ) HANGUL SYLLABLE SSYEGG
+C3BF;C3BF;110A 1168 11AA;C3BF;110A 1168 11AA; # (쎿; 쎿; 쎿; 쎿; 쎿; ) HANGUL SYLLABLE SSYEGS
+C3C0;C3C0;110A 1168 11AB;C3C0;110A 1168 11AB; # (ì€; ì€; 쏀; ì€; 쏀; ) HANGUL SYLLABLE SSYEN
+C3C1;C3C1;110A 1168 11AC;C3C1;110A 1168 11AC; # (ì; ì; 쏁; ì; 쏁; ) HANGUL SYLLABLE SSYENJ
+C3C2;C3C2;110A 1168 11AD;C3C2;110A 1168 11AD; # (ì‚; ì‚; 쏂; ì‚; 쏂; ) HANGUL SYLLABLE SSYENH
+C3C3;C3C3;110A 1168 11AE;C3C3;110A 1168 11AE; # (ìƒ; ìƒ; 쏃; ìƒ; 쏃; ) HANGUL SYLLABLE SSYED
+C3C4;C3C4;110A 1168 11AF;C3C4;110A 1168 11AF; # (ì„; ì„; 쏄; ì„; 쏄; ) HANGUL SYLLABLE SSYEL
+C3C5;C3C5;110A 1168 11B0;C3C5;110A 1168 11B0; # (ì…; ì…; 쏅; ì…; 쏅; ) HANGUL SYLLABLE SSYELG
+C3C6;C3C6;110A 1168 11B1;C3C6;110A 1168 11B1; # (ì†; ì†; 쏆; ì†; 쏆; ) HANGUL SYLLABLE SSYELM
+C3C7;C3C7;110A 1168 11B2;C3C7;110A 1168 11B2; # (ì‡; ì‡; 쏇; ì‡; 쏇; ) HANGUL SYLLABLE SSYELB
+C3C8;C3C8;110A 1168 11B3;C3C8;110A 1168 11B3; # (ìˆ; ìˆ; 쏈; ìˆ; 쏈; ) HANGUL SYLLABLE SSYELS
+C3C9;C3C9;110A 1168 11B4;C3C9;110A 1168 11B4; # (ì‰; ì‰; 쏉; ì‰; 쏉; ) HANGUL SYLLABLE SSYELT
+C3CA;C3CA;110A 1168 11B5;C3CA;110A 1168 11B5; # (ìŠ; ìŠ; 쏊; ìŠ; 쏊; ) HANGUL SYLLABLE SSYELP
+C3CB;C3CB;110A 1168 11B6;C3CB;110A 1168 11B6; # (ì‹; ì‹; 쏋; ì‹; 쏋; ) HANGUL SYLLABLE SSYELH
+C3CC;C3CC;110A 1168 11B7;C3CC;110A 1168 11B7; # (ìŒ; ìŒ; 쏌; ìŒ; 쏌; ) HANGUL SYLLABLE SSYEM
+C3CD;C3CD;110A 1168 11B8;C3CD;110A 1168 11B8; # (ì; ì; 쏍; ì; 쏍; ) HANGUL SYLLABLE SSYEB
+C3CE;C3CE;110A 1168 11B9;C3CE;110A 1168 11B9; # (ìŽ; ìŽ; 쏎; ìŽ; 쏎; ) HANGUL SYLLABLE SSYEBS
+C3CF;C3CF;110A 1168 11BA;C3CF;110A 1168 11BA; # (ì; ì; 쏏; ì; 쏏; ) HANGUL SYLLABLE SSYES
+C3D0;C3D0;110A 1168 11BB;C3D0;110A 1168 11BB; # (ì; ì; 쏐; ì; 쏐; ) HANGUL SYLLABLE SSYESS
+C3D1;C3D1;110A 1168 11BC;C3D1;110A 1168 11BC; # (ì‘; ì‘; 쏑; ì‘; 쏑; ) HANGUL SYLLABLE SSYENG
+C3D2;C3D2;110A 1168 11BD;C3D2;110A 1168 11BD; # (ì’; ì’; 쏒; ì’; 쏒; ) HANGUL SYLLABLE SSYEJ
+C3D3;C3D3;110A 1168 11BE;C3D3;110A 1168 11BE; # (ì“; ì“; 쏓; ì“; 쏓; ) HANGUL SYLLABLE SSYEC
+C3D4;C3D4;110A 1168 11BF;C3D4;110A 1168 11BF; # (ì”; ì”; 쏔; ì”; 쏔; ) HANGUL SYLLABLE SSYEK
+C3D5;C3D5;110A 1168 11C0;C3D5;110A 1168 11C0; # (ì•; ì•; 쏕; ì•; 쏕; ) HANGUL SYLLABLE SSYET
+C3D6;C3D6;110A 1168 11C1;C3D6;110A 1168 11C1; # (ì–; ì–; 쎼á‡; ì–; 쎼á‡; ) HANGUL SYLLABLE SSYEP
+C3D7;C3D7;110A 1168 11C2;C3D7;110A 1168 11C2; # (ì—; ì—; 쏗; ì—; 쏗; ) HANGUL SYLLABLE SSYEH
+C3D8;C3D8;110A 1169;C3D8;110A 1169; # (ì˜; ì˜; 쏘; ì˜; 쏘; ) HANGUL SYLLABLE SSO
+C3D9;C3D9;110A 1169 11A8;C3D9;110A 1169 11A8; # (ì™; ì™; 쏙; ì™; 쏙; ) HANGUL SYLLABLE SSOG
+C3DA;C3DA;110A 1169 11A9;C3DA;110A 1169 11A9; # (ìš; ìš; 쏚; ìš; 쏚; ) HANGUL SYLLABLE SSOGG
+C3DB;C3DB;110A 1169 11AA;C3DB;110A 1169 11AA; # (ì›; ì›; 쏛; ì›; 쏛; ) HANGUL SYLLABLE SSOGS
+C3DC;C3DC;110A 1169 11AB;C3DC;110A 1169 11AB; # (ìœ; ìœ; 쏜; ìœ; 쏜; ) HANGUL SYLLABLE SSON
+C3DD;C3DD;110A 1169 11AC;C3DD;110A 1169 11AC; # (ì; ì; 쏝; ì; 쏝; ) HANGUL SYLLABLE SSONJ
+C3DE;C3DE;110A 1169 11AD;C3DE;110A 1169 11AD; # (ìž; ìž; 쏞; ìž; 쏞; ) HANGUL SYLLABLE SSONH
+C3DF;C3DF;110A 1169 11AE;C3DF;110A 1169 11AE; # (ìŸ; ìŸ; 쏟; ìŸ; 쏟; ) HANGUL SYLLABLE SSOD
+C3E0;C3E0;110A 1169 11AF;C3E0;110A 1169 11AF; # (ì ; ì ; 쏠; ì ; 쏠; ) HANGUL SYLLABLE SSOL
+C3E1;C3E1;110A 1169 11B0;C3E1;110A 1169 11B0; # (ì¡; ì¡; 쏡; ì¡; 쏡; ) HANGUL SYLLABLE SSOLG
+C3E2;C3E2;110A 1169 11B1;C3E2;110A 1169 11B1; # (ì¢; ì¢; 쏢; ì¢; 쏢; ) HANGUL SYLLABLE SSOLM
+C3E3;C3E3;110A 1169 11B2;C3E3;110A 1169 11B2; # (ì£; ì£; 쏣; ì£; 쏣; ) HANGUL SYLLABLE SSOLB
+C3E4;C3E4;110A 1169 11B3;C3E4;110A 1169 11B3; # (ì¤; ì¤; 쏤; ì¤; 쏤; ) HANGUL SYLLABLE SSOLS
+C3E5;C3E5;110A 1169 11B4;C3E5;110A 1169 11B4; # (ì¥; ì¥; 쏥; ì¥; 쏥; ) HANGUL SYLLABLE SSOLT
+C3E6;C3E6;110A 1169 11B5;C3E6;110A 1169 11B5; # (ì¦; ì¦; 쏦; ì¦; 쏦; ) HANGUL SYLLABLE SSOLP
+C3E7;C3E7;110A 1169 11B6;C3E7;110A 1169 11B6; # (ì§; ì§; 쏧; ì§; 쏧; ) HANGUL SYLLABLE SSOLH
+C3E8;C3E8;110A 1169 11B7;C3E8;110A 1169 11B7; # (ì¨; ì¨; 쏨; ì¨; 쏨; ) HANGUL SYLLABLE SSOM
+C3E9;C3E9;110A 1169 11B8;C3E9;110A 1169 11B8; # (ì©; ì©; 쏩; ì©; 쏩; ) HANGUL SYLLABLE SSOB
+C3EA;C3EA;110A 1169 11B9;C3EA;110A 1169 11B9; # (ìª; ìª; 쏪; ìª; 쏪; ) HANGUL SYLLABLE SSOBS
+C3EB;C3EB;110A 1169 11BA;C3EB;110A 1169 11BA; # (ì«; ì«; 쏫; ì«; 쏫; ) HANGUL SYLLABLE SSOS
+C3EC;C3EC;110A 1169 11BB;C3EC;110A 1169 11BB; # (ì¬; ì¬; 쏬; ì¬; 쏬; ) HANGUL SYLLABLE SSOSS
+C3ED;C3ED;110A 1169 11BC;C3ED;110A 1169 11BC; # (ì­; ì­; 쏭; ì­; 쏭; ) HANGUL SYLLABLE SSONG
+C3EE;C3EE;110A 1169 11BD;C3EE;110A 1169 11BD; # (ì®; ì®; 쏮; ì®; 쏮; ) HANGUL SYLLABLE SSOJ
+C3EF;C3EF;110A 1169 11BE;C3EF;110A 1169 11BE; # (ì¯; ì¯; 쏯; ì¯; 쏯; ) HANGUL SYLLABLE SSOC
+C3F0;C3F0;110A 1169 11BF;C3F0;110A 1169 11BF; # (ì°; ì°; 쏰; ì°; 쏰; ) HANGUL SYLLABLE SSOK
+C3F1;C3F1;110A 1169 11C0;C3F1;110A 1169 11C0; # (ì±; ì±; 쏱; ì±; 쏱; ) HANGUL SYLLABLE SSOT
+C3F2;C3F2;110A 1169 11C1;C3F2;110A 1169 11C1; # (ì²; ì²; 쏘á‡; ì²; 쏘á‡; ) HANGUL SYLLABLE SSOP
+C3F3;C3F3;110A 1169 11C2;C3F3;110A 1169 11C2; # (ì³; ì³; 쏳; ì³; 쏳; ) HANGUL SYLLABLE SSOH
+C3F4;C3F4;110A 116A;C3F4;110A 116A; # (ì´; ì´; 쏴; ì´; 쏴; ) HANGUL SYLLABLE SSWA
+C3F5;C3F5;110A 116A 11A8;C3F5;110A 116A 11A8; # (ìµ; ìµ; 쏵; ìµ; 쏵; ) HANGUL SYLLABLE SSWAG
+C3F6;C3F6;110A 116A 11A9;C3F6;110A 116A 11A9; # (ì¶; ì¶; 쏶; ì¶; 쏶; ) HANGUL SYLLABLE SSWAGG
+C3F7;C3F7;110A 116A 11AA;C3F7;110A 116A 11AA; # (ì·; ì·; 쏷; ì·; 쏷; ) HANGUL SYLLABLE SSWAGS
+C3F8;C3F8;110A 116A 11AB;C3F8;110A 116A 11AB; # (ì¸; ì¸; 쏸; ì¸; 쏸; ) HANGUL SYLLABLE SSWAN
+C3F9;C3F9;110A 116A 11AC;C3F9;110A 116A 11AC; # (ì¹; ì¹; 쏹; ì¹; 쏹; ) HANGUL SYLLABLE SSWANJ
+C3FA;C3FA;110A 116A 11AD;C3FA;110A 116A 11AD; # (ìº; ìº; 쏺; ìº; 쏺; ) HANGUL SYLLABLE SSWANH
+C3FB;C3FB;110A 116A 11AE;C3FB;110A 116A 11AE; # (ì»; ì»; 쏻; ì»; 쏻; ) HANGUL SYLLABLE SSWAD
+C3FC;C3FC;110A 116A 11AF;C3FC;110A 116A 11AF; # (ì¼; ì¼; 쏼; ì¼; 쏼; ) HANGUL SYLLABLE SSWAL
+C3FD;C3FD;110A 116A 11B0;C3FD;110A 116A 11B0; # (ì½; ì½; 쏽; ì½; 쏽; ) HANGUL SYLLABLE SSWALG
+C3FE;C3FE;110A 116A 11B1;C3FE;110A 116A 11B1; # (ì¾; ì¾; 쏾; ì¾; 쏾; ) HANGUL SYLLABLE SSWALM
+C3FF;C3FF;110A 116A 11B2;C3FF;110A 116A 11B2; # (ì¿; ì¿; 쏿; ì¿; 쏿; ) HANGUL SYLLABLE SSWALB
+C400;C400;110A 116A 11B3;C400;110A 116A 11B3; # (ì€; ì€; 쐀; ì€; 쐀; ) HANGUL SYLLABLE SSWALS
+C401;C401;110A 116A 11B4;C401;110A 116A 11B4; # (ì; ì; 쐁; ì; 쐁; ) HANGUL SYLLABLE SSWALT
+C402;C402;110A 116A 11B5;C402;110A 116A 11B5; # (ì‚; ì‚; 쐂; ì‚; 쐂; ) HANGUL SYLLABLE SSWALP
+C403;C403;110A 116A 11B6;C403;110A 116A 11B6; # (ìƒ; ìƒ; 쐃; ìƒ; 쐃; ) HANGUL SYLLABLE SSWALH
+C404;C404;110A 116A 11B7;C404;110A 116A 11B7; # (ì„; ì„; 쐄; ì„; 쐄; ) HANGUL SYLLABLE SSWAM
+C405;C405;110A 116A 11B8;C405;110A 116A 11B8; # (ì…; ì…; 쐅; ì…; 쐅; ) HANGUL SYLLABLE SSWAB
+C406;C406;110A 116A 11B9;C406;110A 116A 11B9; # (ì†; ì†; 쐆; ì†; 쐆; ) HANGUL SYLLABLE SSWABS
+C407;C407;110A 116A 11BA;C407;110A 116A 11BA; # (ì‡; ì‡; 쐇; ì‡; 쐇; ) HANGUL SYLLABLE SSWAS
+C408;C408;110A 116A 11BB;C408;110A 116A 11BB; # (ìˆ; ìˆ; 쐈; ìˆ; 쐈; ) HANGUL SYLLABLE SSWASS
+C409;C409;110A 116A 11BC;C409;110A 116A 11BC; # (ì‰; ì‰; 쐉; ì‰; 쐉; ) HANGUL SYLLABLE SSWANG
+C40A;C40A;110A 116A 11BD;C40A;110A 116A 11BD; # (ìŠ; ìŠ; 쐊; ìŠ; 쐊; ) HANGUL SYLLABLE SSWAJ
+C40B;C40B;110A 116A 11BE;C40B;110A 116A 11BE; # (ì‹; ì‹; 쐋; ì‹; 쐋; ) HANGUL SYLLABLE SSWAC
+C40C;C40C;110A 116A 11BF;C40C;110A 116A 11BF; # (ìŒ; ìŒ; 쐌; ìŒ; 쐌; ) HANGUL SYLLABLE SSWAK
+C40D;C40D;110A 116A 11C0;C40D;110A 116A 11C0; # (ì; ì; 쐍; ì; 쐍; ) HANGUL SYLLABLE SSWAT
+C40E;C40E;110A 116A 11C1;C40E;110A 116A 11C1; # (ìŽ; ìŽ; 쏴á‡; ìŽ; 쏴á‡; ) HANGUL SYLLABLE SSWAP
+C40F;C40F;110A 116A 11C2;C40F;110A 116A 11C2; # (ì; ì; 쐏; ì; 쐏; ) HANGUL SYLLABLE SSWAH
+C410;C410;110A 116B;C410;110A 116B; # (ì; ì; 쐐; ì; 쐐; ) HANGUL SYLLABLE SSWAE
+C411;C411;110A 116B 11A8;C411;110A 116B 11A8; # (ì‘; ì‘; 쐑; ì‘; 쐑; ) HANGUL SYLLABLE SSWAEG
+C412;C412;110A 116B 11A9;C412;110A 116B 11A9; # (ì’; ì’; 쐒; ì’; 쐒; ) HANGUL SYLLABLE SSWAEGG
+C413;C413;110A 116B 11AA;C413;110A 116B 11AA; # (ì“; ì“; 쐓; ì“; 쐓; ) HANGUL SYLLABLE SSWAEGS
+C414;C414;110A 116B 11AB;C414;110A 116B 11AB; # (ì”; ì”; 쐔; ì”; 쐔; ) HANGUL SYLLABLE SSWAEN
+C415;C415;110A 116B 11AC;C415;110A 116B 11AC; # (ì•; ì•; 쐕; ì•; 쐕; ) HANGUL SYLLABLE SSWAENJ
+C416;C416;110A 116B 11AD;C416;110A 116B 11AD; # (ì–; ì–; 쐖; ì–; 쐖; ) HANGUL SYLLABLE SSWAENH
+C417;C417;110A 116B 11AE;C417;110A 116B 11AE; # (ì—; ì—; 쐗; ì—; 쐗; ) HANGUL SYLLABLE SSWAED
+C418;C418;110A 116B 11AF;C418;110A 116B 11AF; # (ì˜; ì˜; 쐘; ì˜; 쐘; ) HANGUL SYLLABLE SSWAEL
+C419;C419;110A 116B 11B0;C419;110A 116B 11B0; # (ì™; ì™; 쐙; ì™; 쐙; ) HANGUL SYLLABLE SSWAELG
+C41A;C41A;110A 116B 11B1;C41A;110A 116B 11B1; # (ìš; ìš; 쐚; ìš; 쐚; ) HANGUL SYLLABLE SSWAELM
+C41B;C41B;110A 116B 11B2;C41B;110A 116B 11B2; # (ì›; ì›; 쐛; ì›; 쐛; ) HANGUL SYLLABLE SSWAELB
+C41C;C41C;110A 116B 11B3;C41C;110A 116B 11B3; # (ìœ; ìœ; 쐜; ìœ; 쐜; ) HANGUL SYLLABLE SSWAELS
+C41D;C41D;110A 116B 11B4;C41D;110A 116B 11B4; # (ì; ì; 쐝; ì; 쐝; ) HANGUL SYLLABLE SSWAELT
+C41E;C41E;110A 116B 11B5;C41E;110A 116B 11B5; # (ìž; ìž; 쐞; ìž; 쐞; ) HANGUL SYLLABLE SSWAELP
+C41F;C41F;110A 116B 11B6;C41F;110A 116B 11B6; # (ìŸ; ìŸ; 쐟; ìŸ; 쐟; ) HANGUL SYLLABLE SSWAELH
+C420;C420;110A 116B 11B7;C420;110A 116B 11B7; # (ì ; ì ; 쐠; ì ; 쐠; ) HANGUL SYLLABLE SSWAEM
+C421;C421;110A 116B 11B8;C421;110A 116B 11B8; # (ì¡; ì¡; 쐡; ì¡; 쐡; ) HANGUL SYLLABLE SSWAEB
+C422;C422;110A 116B 11B9;C422;110A 116B 11B9; # (ì¢; ì¢; 쐢; ì¢; 쐢; ) HANGUL SYLLABLE SSWAEBS
+C423;C423;110A 116B 11BA;C423;110A 116B 11BA; # (ì£; ì£; 쐣; ì£; 쐣; ) HANGUL SYLLABLE SSWAES
+C424;C424;110A 116B 11BB;C424;110A 116B 11BB; # (ì¤; ì¤; 쐤; ì¤; 쐤; ) HANGUL SYLLABLE SSWAESS
+C425;C425;110A 116B 11BC;C425;110A 116B 11BC; # (ì¥; ì¥; 쐥; ì¥; 쐥; ) HANGUL SYLLABLE SSWAENG
+C426;C426;110A 116B 11BD;C426;110A 116B 11BD; # (ì¦; ì¦; 쐦; ì¦; 쐦; ) HANGUL SYLLABLE SSWAEJ
+C427;C427;110A 116B 11BE;C427;110A 116B 11BE; # (ì§; ì§; 쐧; ì§; 쐧; ) HANGUL SYLLABLE SSWAEC
+C428;C428;110A 116B 11BF;C428;110A 116B 11BF; # (ì¨; ì¨; 쐨; ì¨; 쐨; ) HANGUL SYLLABLE SSWAEK
+C429;C429;110A 116B 11C0;C429;110A 116B 11C0; # (ì©; ì©; 쐩; ì©; 쐩; ) HANGUL SYLLABLE SSWAET
+C42A;C42A;110A 116B 11C1;C42A;110A 116B 11C1; # (ìª; ìª; 쐐á‡; ìª; 쐐á‡; ) HANGUL SYLLABLE SSWAEP
+C42B;C42B;110A 116B 11C2;C42B;110A 116B 11C2; # (ì«; ì«; 쐫; ì«; 쐫; ) HANGUL SYLLABLE SSWAEH
+C42C;C42C;110A 116C;C42C;110A 116C; # (ì¬; ì¬; 쐬; ì¬; 쐬; ) HANGUL SYLLABLE SSOE
+C42D;C42D;110A 116C 11A8;C42D;110A 116C 11A8; # (ì­; ì­; 쐭; ì­; 쐭; ) HANGUL SYLLABLE SSOEG
+C42E;C42E;110A 116C 11A9;C42E;110A 116C 11A9; # (ì®; ì®; 쐮; ì®; 쐮; ) HANGUL SYLLABLE SSOEGG
+C42F;C42F;110A 116C 11AA;C42F;110A 116C 11AA; # (ì¯; ì¯; 쐯; ì¯; 쐯; ) HANGUL SYLLABLE SSOEGS
+C430;C430;110A 116C 11AB;C430;110A 116C 11AB; # (ì°; ì°; 쐰; ì°; 쐰; ) HANGUL SYLLABLE SSOEN
+C431;C431;110A 116C 11AC;C431;110A 116C 11AC; # (ì±; ì±; 쐱; ì±; 쐱; ) HANGUL SYLLABLE SSOENJ
+C432;C432;110A 116C 11AD;C432;110A 116C 11AD; # (ì²; ì²; 쐲; ì²; 쐲; ) HANGUL SYLLABLE SSOENH
+C433;C433;110A 116C 11AE;C433;110A 116C 11AE; # (ì³; ì³; 쐳; ì³; 쐳; ) HANGUL SYLLABLE SSOED
+C434;C434;110A 116C 11AF;C434;110A 116C 11AF; # (ì´; ì´; 쐴; ì´; 쐴; ) HANGUL SYLLABLE SSOEL
+C435;C435;110A 116C 11B0;C435;110A 116C 11B0; # (ìµ; ìµ; 쐵; ìµ; 쐵; ) HANGUL SYLLABLE SSOELG
+C436;C436;110A 116C 11B1;C436;110A 116C 11B1; # (ì¶; ì¶; 쐶; ì¶; 쐶; ) HANGUL SYLLABLE SSOELM
+C437;C437;110A 116C 11B2;C437;110A 116C 11B2; # (ì·; ì·; 쐷; ì·; 쐷; ) HANGUL SYLLABLE SSOELB
+C438;C438;110A 116C 11B3;C438;110A 116C 11B3; # (ì¸; ì¸; 쐸; ì¸; 쐸; ) HANGUL SYLLABLE SSOELS
+C439;C439;110A 116C 11B4;C439;110A 116C 11B4; # (ì¹; ì¹; 쐹; ì¹; 쐹; ) HANGUL SYLLABLE SSOELT
+C43A;C43A;110A 116C 11B5;C43A;110A 116C 11B5; # (ìº; ìº; 쐺; ìº; 쐺; ) HANGUL SYLLABLE SSOELP
+C43B;C43B;110A 116C 11B6;C43B;110A 116C 11B6; # (ì»; ì»; 쐻; ì»; 쐻; ) HANGUL SYLLABLE SSOELH
+C43C;C43C;110A 116C 11B7;C43C;110A 116C 11B7; # (ì¼; ì¼; 쐼; ì¼; 쐼; ) HANGUL SYLLABLE SSOEM
+C43D;C43D;110A 116C 11B8;C43D;110A 116C 11B8; # (ì½; ì½; 쐽; ì½; 쐽; ) HANGUL SYLLABLE SSOEB
+C43E;C43E;110A 116C 11B9;C43E;110A 116C 11B9; # (ì¾; ì¾; 쐾; ì¾; 쐾; ) HANGUL SYLLABLE SSOEBS
+C43F;C43F;110A 116C 11BA;C43F;110A 116C 11BA; # (ì¿; ì¿; 쐿; ì¿; 쐿; ) HANGUL SYLLABLE SSOES
+C440;C440;110A 116C 11BB;C440;110A 116C 11BB; # (쑀; 쑀; 쑀; 쑀; 쑀; ) HANGUL SYLLABLE SSOESS
+C441;C441;110A 116C 11BC;C441;110A 116C 11BC; # (ì‘; ì‘; 쑁; ì‘; 쑁; ) HANGUL SYLLABLE SSOENG
+C442;C442;110A 116C 11BD;C442;110A 116C 11BD; # (쑂; 쑂; 쑂; 쑂; 쑂; ) HANGUL SYLLABLE SSOEJ
+C443;C443;110A 116C 11BE;C443;110A 116C 11BE; # (쑃; 쑃; 쑃; 쑃; 쑃; ) HANGUL SYLLABLE SSOEC
+C444;C444;110A 116C 11BF;C444;110A 116C 11BF; # (쑄; 쑄; 쑄; 쑄; 쑄; ) HANGUL SYLLABLE SSOEK
+C445;C445;110A 116C 11C0;C445;110A 116C 11C0; # (쑅; 쑅; 쑅; 쑅; 쑅; ) HANGUL SYLLABLE SSOET
+C446;C446;110A 116C 11C1;C446;110A 116C 11C1; # (쑆; 쑆; 쐬á‡; 쑆; 쐬á‡; ) HANGUL SYLLABLE SSOEP
+C447;C447;110A 116C 11C2;C447;110A 116C 11C2; # (쑇; 쑇; 쑇; 쑇; 쑇; ) HANGUL SYLLABLE SSOEH
+C448;C448;110A 116D;C448;110A 116D; # (쑈; 쑈; 쑈; 쑈; 쑈; ) HANGUL SYLLABLE SSYO
+C449;C449;110A 116D 11A8;C449;110A 116D 11A8; # (쑉; 쑉; 쑉; 쑉; 쑉; ) HANGUL SYLLABLE SSYOG
+C44A;C44A;110A 116D 11A9;C44A;110A 116D 11A9; # (쑊; 쑊; 쑊; 쑊; 쑊; ) HANGUL SYLLABLE SSYOGG
+C44B;C44B;110A 116D 11AA;C44B;110A 116D 11AA; # (쑋; 쑋; 쑋; 쑋; 쑋; ) HANGUL SYLLABLE SSYOGS
+C44C;C44C;110A 116D 11AB;C44C;110A 116D 11AB; # (쑌; 쑌; 쑌; 쑌; 쑌; ) HANGUL SYLLABLE SSYON
+C44D;C44D;110A 116D 11AC;C44D;110A 116D 11AC; # (ì‘; ì‘; 쑍; ì‘; 쑍; ) HANGUL SYLLABLE SSYONJ
+C44E;C44E;110A 116D 11AD;C44E;110A 116D 11AD; # (쑎; 쑎; 쑎; 쑎; 쑎; ) HANGUL SYLLABLE SSYONH
+C44F;C44F;110A 116D 11AE;C44F;110A 116D 11AE; # (ì‘; ì‘; 쑏; ì‘; 쑏; ) HANGUL SYLLABLE SSYOD
+C450;C450;110A 116D 11AF;C450;110A 116D 11AF; # (ì‘; ì‘; 쑐; ì‘; 쑐; ) HANGUL SYLLABLE SSYOL
+C451;C451;110A 116D 11B0;C451;110A 116D 11B0; # (쑑; 쑑; 쑑; 쑑; 쑑; ) HANGUL SYLLABLE SSYOLG
+C452;C452;110A 116D 11B1;C452;110A 116D 11B1; # (쑒; 쑒; 쑒; 쑒; 쑒; ) HANGUL SYLLABLE SSYOLM
+C453;C453;110A 116D 11B2;C453;110A 116D 11B2; # (쑓; 쑓; 쑓; 쑓; 쑓; ) HANGUL SYLLABLE SSYOLB
+C454;C454;110A 116D 11B3;C454;110A 116D 11B3; # (쑔; 쑔; 쑔; 쑔; 쑔; ) HANGUL SYLLABLE SSYOLS
+C455;C455;110A 116D 11B4;C455;110A 116D 11B4; # (쑕; 쑕; 쑕; 쑕; 쑕; ) HANGUL SYLLABLE SSYOLT
+C456;C456;110A 116D 11B5;C456;110A 116D 11B5; # (쑖; 쑖; 쑖; 쑖; 쑖; ) HANGUL SYLLABLE SSYOLP
+C457;C457;110A 116D 11B6;C457;110A 116D 11B6; # (쑗; 쑗; 쑗; 쑗; 쑗; ) HANGUL SYLLABLE SSYOLH
+C458;C458;110A 116D 11B7;C458;110A 116D 11B7; # (쑘; 쑘; 쑘; 쑘; 쑘; ) HANGUL SYLLABLE SSYOM
+C459;C459;110A 116D 11B8;C459;110A 116D 11B8; # (쑙; 쑙; 쑙; 쑙; 쑙; ) HANGUL SYLLABLE SSYOB
+C45A;C45A;110A 116D 11B9;C45A;110A 116D 11B9; # (쑚; 쑚; 쑚; 쑚; 쑚; ) HANGUL SYLLABLE SSYOBS
+C45B;C45B;110A 116D 11BA;C45B;110A 116D 11BA; # (쑛; 쑛; 쑛; 쑛; 쑛; ) HANGUL SYLLABLE SSYOS
+C45C;C45C;110A 116D 11BB;C45C;110A 116D 11BB; # (쑜; 쑜; 쑜; 쑜; 쑜; ) HANGUL SYLLABLE SSYOSS
+C45D;C45D;110A 116D 11BC;C45D;110A 116D 11BC; # (ì‘; ì‘; 쑝; ì‘; 쑝; ) HANGUL SYLLABLE SSYONG
+C45E;C45E;110A 116D 11BD;C45E;110A 116D 11BD; # (쑞; 쑞; 쑞; 쑞; 쑞; ) HANGUL SYLLABLE SSYOJ
+C45F;C45F;110A 116D 11BE;C45F;110A 116D 11BE; # (쑟; 쑟; 쑟; 쑟; 쑟; ) HANGUL SYLLABLE SSYOC
+C460;C460;110A 116D 11BF;C460;110A 116D 11BF; # (쑠; 쑠; 쑠; 쑠; 쑠; ) HANGUL SYLLABLE SSYOK
+C461;C461;110A 116D 11C0;C461;110A 116D 11C0; # (쑡; 쑡; 쑡; 쑡; 쑡; ) HANGUL SYLLABLE SSYOT
+C462;C462;110A 116D 11C1;C462;110A 116D 11C1; # (ì‘¢; ì‘¢; 쑈á‡; ì‘¢; 쑈á‡; ) HANGUL SYLLABLE SSYOP
+C463;C463;110A 116D 11C2;C463;110A 116D 11C2; # (쑣; 쑣; 쑣; 쑣; 쑣; ) HANGUL SYLLABLE SSYOH
+C464;C464;110A 116E;C464;110A 116E; # (쑤; 쑤; 쑤; 쑤; 쑤; ) HANGUL SYLLABLE SSU
+C465;C465;110A 116E 11A8;C465;110A 116E 11A8; # (쑥; 쑥; 쑥; 쑥; 쑥; ) HANGUL SYLLABLE SSUG
+C466;C466;110A 116E 11A9;C466;110A 116E 11A9; # (쑦; 쑦; 쑦; 쑦; 쑦; ) HANGUL SYLLABLE SSUGG
+C467;C467;110A 116E 11AA;C467;110A 116E 11AA; # (쑧; 쑧; 쑧; 쑧; 쑧; ) HANGUL SYLLABLE SSUGS
+C468;C468;110A 116E 11AB;C468;110A 116E 11AB; # (쑨; 쑨; 쑨; 쑨; 쑨; ) HANGUL SYLLABLE SSUN
+C469;C469;110A 116E 11AC;C469;110A 116E 11AC; # (쑩; 쑩; 쑩; 쑩; 쑩; ) HANGUL SYLLABLE SSUNJ
+C46A;C46A;110A 116E 11AD;C46A;110A 116E 11AD; # (쑪; 쑪; 쑪; 쑪; 쑪; ) HANGUL SYLLABLE SSUNH
+C46B;C46B;110A 116E 11AE;C46B;110A 116E 11AE; # (쑫; 쑫; 쑫; 쑫; 쑫; ) HANGUL SYLLABLE SSUD
+C46C;C46C;110A 116E 11AF;C46C;110A 116E 11AF; # (쑬; 쑬; 쑬; 쑬; 쑬; ) HANGUL SYLLABLE SSUL
+C46D;C46D;110A 116E 11B0;C46D;110A 116E 11B0; # (쑭; 쑭; 쑭; 쑭; 쑭; ) HANGUL SYLLABLE SSULG
+C46E;C46E;110A 116E 11B1;C46E;110A 116E 11B1; # (쑮; 쑮; 쑮; 쑮; 쑮; ) HANGUL SYLLABLE SSULM
+C46F;C46F;110A 116E 11B2;C46F;110A 116E 11B2; # (쑯; 쑯; 쑯; 쑯; 쑯; ) HANGUL SYLLABLE SSULB
+C470;C470;110A 116E 11B3;C470;110A 116E 11B3; # (쑰; 쑰; 쑰; 쑰; 쑰; ) HANGUL SYLLABLE SSULS
+C471;C471;110A 116E 11B4;C471;110A 116E 11B4; # (쑱; 쑱; 쑱; 쑱; 쑱; ) HANGUL SYLLABLE SSULT
+C472;C472;110A 116E 11B5;C472;110A 116E 11B5; # (쑲; 쑲; 쑲; 쑲; 쑲; ) HANGUL SYLLABLE SSULP
+C473;C473;110A 116E 11B6;C473;110A 116E 11B6; # (쑳; 쑳; 쑳; 쑳; 쑳; ) HANGUL SYLLABLE SSULH
+C474;C474;110A 116E 11B7;C474;110A 116E 11B7; # (쑴; 쑴; 쑴; 쑴; 쑴; ) HANGUL SYLLABLE SSUM
+C475;C475;110A 116E 11B8;C475;110A 116E 11B8; # (쑵; 쑵; 쑵; 쑵; 쑵; ) HANGUL SYLLABLE SSUB
+C476;C476;110A 116E 11B9;C476;110A 116E 11B9; # (쑶; 쑶; 쑶; 쑶; 쑶; ) HANGUL SYLLABLE SSUBS
+C477;C477;110A 116E 11BA;C477;110A 116E 11BA; # (쑷; 쑷; 쑷; 쑷; 쑷; ) HANGUL SYLLABLE SSUS
+C478;C478;110A 116E 11BB;C478;110A 116E 11BB; # (쑸; 쑸; 쑸; 쑸; 쑸; ) HANGUL SYLLABLE SSUSS
+C479;C479;110A 116E 11BC;C479;110A 116E 11BC; # (쑹; 쑹; 쑹; 쑹; 쑹; ) HANGUL SYLLABLE SSUNG
+C47A;C47A;110A 116E 11BD;C47A;110A 116E 11BD; # (쑺; 쑺; 쑺; 쑺; 쑺; ) HANGUL SYLLABLE SSUJ
+C47B;C47B;110A 116E 11BE;C47B;110A 116E 11BE; # (쑻; 쑻; 쑻; 쑻; 쑻; ) HANGUL SYLLABLE SSUC
+C47C;C47C;110A 116E 11BF;C47C;110A 116E 11BF; # (쑼; 쑼; 쑼; 쑼; 쑼; ) HANGUL SYLLABLE SSUK
+C47D;C47D;110A 116E 11C0;C47D;110A 116E 11C0; # (쑽; 쑽; 쑽; 쑽; 쑽; ) HANGUL SYLLABLE SSUT
+C47E;C47E;110A 116E 11C1;C47E;110A 116E 11C1; # (쑾; 쑾; 쑤á‡; 쑾; 쑤á‡; ) HANGUL SYLLABLE SSUP
+C47F;C47F;110A 116E 11C2;C47F;110A 116E 11C2; # (쑿; 쑿; 쑿; 쑿; 쑿; ) HANGUL SYLLABLE SSUH
+C480;C480;110A 116F;C480;110A 116F; # (쒀; 쒀; 쒀; 쒀; 쒀; ) HANGUL SYLLABLE SSWEO
+C481;C481;110A 116F 11A8;C481;110A 116F 11A8; # (ì’; ì’; 쒁; ì’; 쒁; ) HANGUL SYLLABLE SSWEOG
+C482;C482;110A 116F 11A9;C482;110A 116F 11A9; # (쒂; 쒂; 쒂; 쒂; 쒂; ) HANGUL SYLLABLE SSWEOGG
+C483;C483;110A 116F 11AA;C483;110A 116F 11AA; # (쒃; 쒃; 쒃; 쒃; 쒃; ) HANGUL SYLLABLE SSWEOGS
+C484;C484;110A 116F 11AB;C484;110A 116F 11AB; # (쒄; 쒄; 쒄; 쒄; 쒄; ) HANGUL SYLLABLE SSWEON
+C485;C485;110A 116F 11AC;C485;110A 116F 11AC; # (쒅; 쒅; 쒅; 쒅; 쒅; ) HANGUL SYLLABLE SSWEONJ
+C486;C486;110A 116F 11AD;C486;110A 116F 11AD; # (쒆; 쒆; 쒆; 쒆; 쒆; ) HANGUL SYLLABLE SSWEONH
+C487;C487;110A 116F 11AE;C487;110A 116F 11AE; # (쒇; 쒇; 쒇; 쒇; 쒇; ) HANGUL SYLLABLE SSWEOD
+C488;C488;110A 116F 11AF;C488;110A 116F 11AF; # (쒈; 쒈; 쒈; 쒈; 쒈; ) HANGUL SYLLABLE SSWEOL
+C489;C489;110A 116F 11B0;C489;110A 116F 11B0; # (쒉; 쒉; 쒉; 쒉; 쒉; ) HANGUL SYLLABLE SSWEOLG
+C48A;C48A;110A 116F 11B1;C48A;110A 116F 11B1; # (쒊; 쒊; 쒊; 쒊; 쒊; ) HANGUL SYLLABLE SSWEOLM
+C48B;C48B;110A 116F 11B2;C48B;110A 116F 11B2; # (쒋; 쒋; 쒋; 쒋; 쒋; ) HANGUL SYLLABLE SSWEOLB
+C48C;C48C;110A 116F 11B3;C48C;110A 116F 11B3; # (쒌; 쒌; 쒌; 쒌; 쒌; ) HANGUL SYLLABLE SSWEOLS
+C48D;C48D;110A 116F 11B4;C48D;110A 116F 11B4; # (ì’; ì’; 쒍; ì’; 쒍; ) HANGUL SYLLABLE SSWEOLT
+C48E;C48E;110A 116F 11B5;C48E;110A 116F 11B5; # (쒎; 쒎; 쒎; 쒎; 쒎; ) HANGUL SYLLABLE SSWEOLP
+C48F;C48F;110A 116F 11B6;C48F;110A 116F 11B6; # (ì’; ì’; 쒏; ì’; 쒏; ) HANGUL SYLLABLE SSWEOLH
+C490;C490;110A 116F 11B7;C490;110A 116F 11B7; # (ì’; ì’; 쒐; ì’; 쒐; ) HANGUL SYLLABLE SSWEOM
+C491;C491;110A 116F 11B8;C491;110A 116F 11B8; # (쒑; 쒑; 쒑; 쒑; 쒑; ) HANGUL SYLLABLE SSWEOB
+C492;C492;110A 116F 11B9;C492;110A 116F 11B9; # (쒒; 쒒; 쒒; 쒒; 쒒; ) HANGUL SYLLABLE SSWEOBS
+C493;C493;110A 116F 11BA;C493;110A 116F 11BA; # (쒓; 쒓; 쒓; 쒓; 쒓; ) HANGUL SYLLABLE SSWEOS
+C494;C494;110A 116F 11BB;C494;110A 116F 11BB; # (쒔; 쒔; 쒔; 쒔; 쒔; ) HANGUL SYLLABLE SSWEOSS
+C495;C495;110A 116F 11BC;C495;110A 116F 11BC; # (쒕; 쒕; 쒕; 쒕; 쒕; ) HANGUL SYLLABLE SSWEONG
+C496;C496;110A 116F 11BD;C496;110A 116F 11BD; # (쒖; 쒖; 쒖; 쒖; 쒖; ) HANGUL SYLLABLE SSWEOJ
+C497;C497;110A 116F 11BE;C497;110A 116F 11BE; # (쒗; 쒗; 쒗; 쒗; 쒗; ) HANGUL SYLLABLE SSWEOC
+C498;C498;110A 116F 11BF;C498;110A 116F 11BF; # (쒘; 쒘; 쒘; 쒘; 쒘; ) HANGUL SYLLABLE SSWEOK
+C499;C499;110A 116F 11C0;C499;110A 116F 11C0; # (쒙; 쒙; 쒙; 쒙; 쒙; ) HANGUL SYLLABLE SSWEOT
+C49A;C49A;110A 116F 11C1;C49A;110A 116F 11C1; # (ì’š; ì’š; 쒀á‡; ì’š; 쒀á‡; ) HANGUL SYLLABLE SSWEOP
+C49B;C49B;110A 116F 11C2;C49B;110A 116F 11C2; # (쒛; 쒛; 쒛; 쒛; 쒛; ) HANGUL SYLLABLE SSWEOH
+C49C;C49C;110A 1170;C49C;110A 1170; # (쒜; 쒜; 쒜; 쒜; 쒜; ) HANGUL SYLLABLE SSWE
+C49D;C49D;110A 1170 11A8;C49D;110A 1170 11A8; # (ì’; ì’; 쒝; ì’; 쒝; ) HANGUL SYLLABLE SSWEG
+C49E;C49E;110A 1170 11A9;C49E;110A 1170 11A9; # (쒞; 쒞; 쒞; 쒞; 쒞; ) HANGUL SYLLABLE SSWEGG
+C49F;C49F;110A 1170 11AA;C49F;110A 1170 11AA; # (쒟; 쒟; 쒟; 쒟; 쒟; ) HANGUL SYLLABLE SSWEGS
+C4A0;C4A0;110A 1170 11AB;C4A0;110A 1170 11AB; # (쒠; 쒠; 쒠; 쒠; 쒠; ) HANGUL SYLLABLE SSWEN
+C4A1;C4A1;110A 1170 11AC;C4A1;110A 1170 11AC; # (쒡; 쒡; 쒡; 쒡; 쒡; ) HANGUL SYLLABLE SSWENJ
+C4A2;C4A2;110A 1170 11AD;C4A2;110A 1170 11AD; # (쒢; 쒢; 쒢; 쒢; 쒢; ) HANGUL SYLLABLE SSWENH
+C4A3;C4A3;110A 1170 11AE;C4A3;110A 1170 11AE; # (쒣; 쒣; 쒣; 쒣; 쒣; ) HANGUL SYLLABLE SSWED
+C4A4;C4A4;110A 1170 11AF;C4A4;110A 1170 11AF; # (쒤; 쒤; 쒤; 쒤; 쒤; ) HANGUL SYLLABLE SSWEL
+C4A5;C4A5;110A 1170 11B0;C4A5;110A 1170 11B0; # (쒥; 쒥; 쒥; 쒥; 쒥; ) HANGUL SYLLABLE SSWELG
+C4A6;C4A6;110A 1170 11B1;C4A6;110A 1170 11B1; # (쒦; 쒦; 쒦; 쒦; 쒦; ) HANGUL SYLLABLE SSWELM
+C4A7;C4A7;110A 1170 11B2;C4A7;110A 1170 11B2; # (쒧; 쒧; 쒧; 쒧; 쒧; ) HANGUL SYLLABLE SSWELB
+C4A8;C4A8;110A 1170 11B3;C4A8;110A 1170 11B3; # (쒨; 쒨; 쒨; 쒨; 쒨; ) HANGUL SYLLABLE SSWELS
+C4A9;C4A9;110A 1170 11B4;C4A9;110A 1170 11B4; # (쒩; 쒩; 쒩; 쒩; 쒩; ) HANGUL SYLLABLE SSWELT
+C4AA;C4AA;110A 1170 11B5;C4AA;110A 1170 11B5; # (쒪; 쒪; 쒪; 쒪; 쒪; ) HANGUL SYLLABLE SSWELP
+C4AB;C4AB;110A 1170 11B6;C4AB;110A 1170 11B6; # (쒫; 쒫; 쒫; 쒫; 쒫; ) HANGUL SYLLABLE SSWELH
+C4AC;C4AC;110A 1170 11B7;C4AC;110A 1170 11B7; # (쒬; 쒬; 쒬; 쒬; 쒬; ) HANGUL SYLLABLE SSWEM
+C4AD;C4AD;110A 1170 11B8;C4AD;110A 1170 11B8; # (쒭; 쒭; 쒭; 쒭; 쒭; ) HANGUL SYLLABLE SSWEB
+C4AE;C4AE;110A 1170 11B9;C4AE;110A 1170 11B9; # (쒮; 쒮; 쒮; 쒮; 쒮; ) HANGUL SYLLABLE SSWEBS
+C4AF;C4AF;110A 1170 11BA;C4AF;110A 1170 11BA; # (쒯; 쒯; 쒯; 쒯; 쒯; ) HANGUL SYLLABLE SSWES
+C4B0;C4B0;110A 1170 11BB;C4B0;110A 1170 11BB; # (쒰; 쒰; 쒰; 쒰; 쒰; ) HANGUL SYLLABLE SSWESS
+C4B1;C4B1;110A 1170 11BC;C4B1;110A 1170 11BC; # (쒱; 쒱; 쒱; 쒱; 쒱; ) HANGUL SYLLABLE SSWENG
+C4B2;C4B2;110A 1170 11BD;C4B2;110A 1170 11BD; # (쒲; 쒲; 쒲; 쒲; 쒲; ) HANGUL SYLLABLE SSWEJ
+C4B3;C4B3;110A 1170 11BE;C4B3;110A 1170 11BE; # (쒳; 쒳; 쒳; 쒳; 쒳; ) HANGUL SYLLABLE SSWEC
+C4B4;C4B4;110A 1170 11BF;C4B4;110A 1170 11BF; # (쒴; 쒴; 쒴; 쒴; 쒴; ) HANGUL SYLLABLE SSWEK
+C4B5;C4B5;110A 1170 11C0;C4B5;110A 1170 11C0; # (쒵; 쒵; 쒵; 쒵; 쒵; ) HANGUL SYLLABLE SSWET
+C4B6;C4B6;110A 1170 11C1;C4B6;110A 1170 11C1; # (ì’¶; ì’¶; 쒜á‡; ì’¶; 쒜á‡; ) HANGUL SYLLABLE SSWEP
+C4B7;C4B7;110A 1170 11C2;C4B7;110A 1170 11C2; # (쒷; 쒷; 쒷; 쒷; 쒷; ) HANGUL SYLLABLE SSWEH
+C4B8;C4B8;110A 1171;C4B8;110A 1171; # (쒸; 쒸; 쒸; 쒸; 쒸; ) HANGUL SYLLABLE SSWI
+C4B9;C4B9;110A 1171 11A8;C4B9;110A 1171 11A8; # (쒹; 쒹; 쒹; 쒹; 쒹; ) HANGUL SYLLABLE SSWIG
+C4BA;C4BA;110A 1171 11A9;C4BA;110A 1171 11A9; # (쒺; 쒺; 쒺; 쒺; 쒺; ) HANGUL SYLLABLE SSWIGG
+C4BB;C4BB;110A 1171 11AA;C4BB;110A 1171 11AA; # (쒻; 쒻; 쒻; 쒻; 쒻; ) HANGUL SYLLABLE SSWIGS
+C4BC;C4BC;110A 1171 11AB;C4BC;110A 1171 11AB; # (쒼; 쒼; 쒼; 쒼; 쒼; ) HANGUL SYLLABLE SSWIN
+C4BD;C4BD;110A 1171 11AC;C4BD;110A 1171 11AC; # (쒽; 쒽; 쒽; 쒽; 쒽; ) HANGUL SYLLABLE SSWINJ
+C4BE;C4BE;110A 1171 11AD;C4BE;110A 1171 11AD; # (쒾; 쒾; 쒾; 쒾; 쒾; ) HANGUL SYLLABLE SSWINH
+C4BF;C4BF;110A 1171 11AE;C4BF;110A 1171 11AE; # (쒿; 쒿; 쒿; 쒿; 쒿; ) HANGUL SYLLABLE SSWID
+C4C0;C4C0;110A 1171 11AF;C4C0;110A 1171 11AF; # (쓀; 쓀; 쓀; 쓀; 쓀; ) HANGUL SYLLABLE SSWIL
+C4C1;C4C1;110A 1171 11B0;C4C1;110A 1171 11B0; # (ì“; ì“; 쓁; ì“; 쓁; ) HANGUL SYLLABLE SSWILG
+C4C2;C4C2;110A 1171 11B1;C4C2;110A 1171 11B1; # (쓂; 쓂; 쓂; 쓂; 쓂; ) HANGUL SYLLABLE SSWILM
+C4C3;C4C3;110A 1171 11B2;C4C3;110A 1171 11B2; # (쓃; 쓃; 쓃; 쓃; 쓃; ) HANGUL SYLLABLE SSWILB
+C4C4;C4C4;110A 1171 11B3;C4C4;110A 1171 11B3; # (쓄; 쓄; 쓄; 쓄; 쓄; ) HANGUL SYLLABLE SSWILS
+C4C5;C4C5;110A 1171 11B4;C4C5;110A 1171 11B4; # (쓅; 쓅; 쓅; 쓅; 쓅; ) HANGUL SYLLABLE SSWILT
+C4C6;C4C6;110A 1171 11B5;C4C6;110A 1171 11B5; # (쓆; 쓆; 쓆; 쓆; 쓆; ) HANGUL SYLLABLE SSWILP
+C4C7;C4C7;110A 1171 11B6;C4C7;110A 1171 11B6; # (쓇; 쓇; 쓇; 쓇; 쓇; ) HANGUL SYLLABLE SSWILH
+C4C8;C4C8;110A 1171 11B7;C4C8;110A 1171 11B7; # (쓈; 쓈; 쓈; 쓈; 쓈; ) HANGUL SYLLABLE SSWIM
+C4C9;C4C9;110A 1171 11B8;C4C9;110A 1171 11B8; # (쓉; 쓉; 쓉; 쓉; 쓉; ) HANGUL SYLLABLE SSWIB
+C4CA;C4CA;110A 1171 11B9;C4CA;110A 1171 11B9; # (쓊; 쓊; 쓊; 쓊; 쓊; ) HANGUL SYLLABLE SSWIBS
+C4CB;C4CB;110A 1171 11BA;C4CB;110A 1171 11BA; # (쓋; 쓋; 쓋; 쓋; 쓋; ) HANGUL SYLLABLE SSWIS
+C4CC;C4CC;110A 1171 11BB;C4CC;110A 1171 11BB; # (쓌; 쓌; 쓌; 쓌; 쓌; ) HANGUL SYLLABLE SSWISS
+C4CD;C4CD;110A 1171 11BC;C4CD;110A 1171 11BC; # (ì“; ì“; 쓍; ì“; 쓍; ) HANGUL SYLLABLE SSWING
+C4CE;C4CE;110A 1171 11BD;C4CE;110A 1171 11BD; # (쓎; 쓎; 쓎; 쓎; 쓎; ) HANGUL SYLLABLE SSWIJ
+C4CF;C4CF;110A 1171 11BE;C4CF;110A 1171 11BE; # (ì“; ì“; 쓏; ì“; 쓏; ) HANGUL SYLLABLE SSWIC
+C4D0;C4D0;110A 1171 11BF;C4D0;110A 1171 11BF; # (ì“; ì“; 쓐; ì“; 쓐; ) HANGUL SYLLABLE SSWIK
+C4D1;C4D1;110A 1171 11C0;C4D1;110A 1171 11C0; # (쓑; 쓑; 쓑; 쓑; 쓑; ) HANGUL SYLLABLE SSWIT
+C4D2;C4D2;110A 1171 11C1;C4D2;110A 1171 11C1; # (ì“’; ì“’; 쒸á‡; ì“’; 쒸á‡; ) HANGUL SYLLABLE SSWIP
+C4D3;C4D3;110A 1171 11C2;C4D3;110A 1171 11C2; # (쓓; 쓓; 쓓; 쓓; 쓓; ) HANGUL SYLLABLE SSWIH
+C4D4;C4D4;110A 1172;C4D4;110A 1172; # (쓔; 쓔; 쓔; 쓔; 쓔; ) HANGUL SYLLABLE SSYU
+C4D5;C4D5;110A 1172 11A8;C4D5;110A 1172 11A8; # (쓕; 쓕; 쓕; 쓕; 쓕; ) HANGUL SYLLABLE SSYUG
+C4D6;C4D6;110A 1172 11A9;C4D6;110A 1172 11A9; # (쓖; 쓖; 쓖; 쓖; 쓖; ) HANGUL SYLLABLE SSYUGG
+C4D7;C4D7;110A 1172 11AA;C4D7;110A 1172 11AA; # (쓗; 쓗; 쓗; 쓗; 쓗; ) HANGUL SYLLABLE SSYUGS
+C4D8;C4D8;110A 1172 11AB;C4D8;110A 1172 11AB; # (쓘; 쓘; 쓘; 쓘; 쓘; ) HANGUL SYLLABLE SSYUN
+C4D9;C4D9;110A 1172 11AC;C4D9;110A 1172 11AC; # (쓙; 쓙; 쓙; 쓙; 쓙; ) HANGUL SYLLABLE SSYUNJ
+C4DA;C4DA;110A 1172 11AD;C4DA;110A 1172 11AD; # (쓚; 쓚; 쓚; 쓚; 쓚; ) HANGUL SYLLABLE SSYUNH
+C4DB;C4DB;110A 1172 11AE;C4DB;110A 1172 11AE; # (쓛; 쓛; 쓛; 쓛; 쓛; ) HANGUL SYLLABLE SSYUD
+C4DC;C4DC;110A 1172 11AF;C4DC;110A 1172 11AF; # (쓜; 쓜; 쓜; 쓜; 쓜; ) HANGUL SYLLABLE SSYUL
+C4DD;C4DD;110A 1172 11B0;C4DD;110A 1172 11B0; # (ì“; ì“; 쓝; ì“; 쓝; ) HANGUL SYLLABLE SSYULG
+C4DE;C4DE;110A 1172 11B1;C4DE;110A 1172 11B1; # (쓞; 쓞; 쓞; 쓞; 쓞; ) HANGUL SYLLABLE SSYULM
+C4DF;C4DF;110A 1172 11B2;C4DF;110A 1172 11B2; # (쓟; 쓟; 쓟; 쓟; 쓟; ) HANGUL SYLLABLE SSYULB
+C4E0;C4E0;110A 1172 11B3;C4E0;110A 1172 11B3; # (쓠; 쓠; 쓠; 쓠; 쓠; ) HANGUL SYLLABLE SSYULS
+C4E1;C4E1;110A 1172 11B4;C4E1;110A 1172 11B4; # (쓡; 쓡; 쓡; 쓡; 쓡; ) HANGUL SYLLABLE SSYULT
+C4E2;C4E2;110A 1172 11B5;C4E2;110A 1172 11B5; # (쓢; 쓢; 쓢; 쓢; 쓢; ) HANGUL SYLLABLE SSYULP
+C4E3;C4E3;110A 1172 11B6;C4E3;110A 1172 11B6; # (쓣; 쓣; 쓣; 쓣; 쓣; ) HANGUL SYLLABLE SSYULH
+C4E4;C4E4;110A 1172 11B7;C4E4;110A 1172 11B7; # (쓤; 쓤; 쓤; 쓤; 쓤; ) HANGUL SYLLABLE SSYUM
+C4E5;C4E5;110A 1172 11B8;C4E5;110A 1172 11B8; # (쓥; 쓥; 쓥; 쓥; 쓥; ) HANGUL SYLLABLE SSYUB
+C4E6;C4E6;110A 1172 11B9;C4E6;110A 1172 11B9; # (쓦; 쓦; 쓦; 쓦; 쓦; ) HANGUL SYLLABLE SSYUBS
+C4E7;C4E7;110A 1172 11BA;C4E7;110A 1172 11BA; # (쓧; 쓧; 쓧; 쓧; 쓧; ) HANGUL SYLLABLE SSYUS
+C4E8;C4E8;110A 1172 11BB;C4E8;110A 1172 11BB; # (쓨; 쓨; 쓨; 쓨; 쓨; ) HANGUL SYLLABLE SSYUSS
+C4E9;C4E9;110A 1172 11BC;C4E9;110A 1172 11BC; # (쓩; 쓩; 쓩; 쓩; 쓩; ) HANGUL SYLLABLE SSYUNG
+C4EA;C4EA;110A 1172 11BD;C4EA;110A 1172 11BD; # (쓪; 쓪; 쓪; 쓪; 쓪; ) HANGUL SYLLABLE SSYUJ
+C4EB;C4EB;110A 1172 11BE;C4EB;110A 1172 11BE; # (쓫; 쓫; 쓫; 쓫; 쓫; ) HANGUL SYLLABLE SSYUC
+C4EC;C4EC;110A 1172 11BF;C4EC;110A 1172 11BF; # (쓬; 쓬; 쓬; 쓬; 쓬; ) HANGUL SYLLABLE SSYUK
+C4ED;C4ED;110A 1172 11C0;C4ED;110A 1172 11C0; # (쓭; 쓭; 쓭; 쓭; 쓭; ) HANGUL SYLLABLE SSYUT
+C4EE;C4EE;110A 1172 11C1;C4EE;110A 1172 11C1; # (ì“®; ì“®; 쓔á‡; ì“®; 쓔á‡; ) HANGUL SYLLABLE SSYUP
+C4EF;C4EF;110A 1172 11C2;C4EF;110A 1172 11C2; # (쓯; 쓯; 쓯; 쓯; 쓯; ) HANGUL SYLLABLE SSYUH
+C4F0;C4F0;110A 1173;C4F0;110A 1173; # (쓰; 쓰; 쓰; 쓰; 쓰; ) HANGUL SYLLABLE SSEU
+C4F1;C4F1;110A 1173 11A8;C4F1;110A 1173 11A8; # (쓱; 쓱; 쓱; 쓱; 쓱; ) HANGUL SYLLABLE SSEUG
+C4F2;C4F2;110A 1173 11A9;C4F2;110A 1173 11A9; # (쓲; 쓲; 쓲; 쓲; 쓲; ) HANGUL SYLLABLE SSEUGG
+C4F3;C4F3;110A 1173 11AA;C4F3;110A 1173 11AA; # (쓳; 쓳; 쓳; 쓳; 쓳; ) HANGUL SYLLABLE SSEUGS
+C4F4;C4F4;110A 1173 11AB;C4F4;110A 1173 11AB; # (쓴; 쓴; 쓴; 쓴; 쓴; ) HANGUL SYLLABLE SSEUN
+C4F5;C4F5;110A 1173 11AC;C4F5;110A 1173 11AC; # (쓵; 쓵; 쓵; 쓵; 쓵; ) HANGUL SYLLABLE SSEUNJ
+C4F6;C4F6;110A 1173 11AD;C4F6;110A 1173 11AD; # (쓶; 쓶; 쓶; 쓶; 쓶; ) HANGUL SYLLABLE SSEUNH
+C4F7;C4F7;110A 1173 11AE;C4F7;110A 1173 11AE; # (쓷; 쓷; 쓷; 쓷; 쓷; ) HANGUL SYLLABLE SSEUD
+C4F8;C4F8;110A 1173 11AF;C4F8;110A 1173 11AF; # (쓸; 쓸; 쓸; 쓸; 쓸; ) HANGUL SYLLABLE SSEUL
+C4F9;C4F9;110A 1173 11B0;C4F9;110A 1173 11B0; # (쓹; 쓹; 쓹; 쓹; 쓹; ) HANGUL SYLLABLE SSEULG
+C4FA;C4FA;110A 1173 11B1;C4FA;110A 1173 11B1; # (쓺; 쓺; 쓺; 쓺; 쓺; ) HANGUL SYLLABLE SSEULM
+C4FB;C4FB;110A 1173 11B2;C4FB;110A 1173 11B2; # (쓻; 쓻; 쓻; 쓻; 쓻; ) HANGUL SYLLABLE SSEULB
+C4FC;C4FC;110A 1173 11B3;C4FC;110A 1173 11B3; # (쓼; 쓼; 쓼; 쓼; 쓼; ) HANGUL SYLLABLE SSEULS
+C4FD;C4FD;110A 1173 11B4;C4FD;110A 1173 11B4; # (쓽; 쓽; 쓽; 쓽; 쓽; ) HANGUL SYLLABLE SSEULT
+C4FE;C4FE;110A 1173 11B5;C4FE;110A 1173 11B5; # (쓾; 쓾; 쓾; 쓾; 쓾; ) HANGUL SYLLABLE SSEULP
+C4FF;C4FF;110A 1173 11B6;C4FF;110A 1173 11B6; # (쓿; 쓿; 쓿; 쓿; 쓿; ) HANGUL SYLLABLE SSEULH
+C500;C500;110A 1173 11B7;C500;110A 1173 11B7; # (씀; 씀; 씀; 씀; 씀; ) HANGUL SYLLABLE SSEUM
+C501;C501;110A 1173 11B8;C501;110A 1173 11B8; # (ì”; ì”; 씁; ì”; 씁; ) HANGUL SYLLABLE SSEUB
+C502;C502;110A 1173 11B9;C502;110A 1173 11B9; # (씂; 씂; 씂; 씂; 씂; ) HANGUL SYLLABLE SSEUBS
+C503;C503;110A 1173 11BA;C503;110A 1173 11BA; # (씃; 씃; 씃; 씃; 씃; ) HANGUL SYLLABLE SSEUS
+C504;C504;110A 1173 11BB;C504;110A 1173 11BB; # (씄; 씄; 씄; 씄; 씄; ) HANGUL SYLLABLE SSEUSS
+C505;C505;110A 1173 11BC;C505;110A 1173 11BC; # (씅; 씅; 씅; 씅; 씅; ) HANGUL SYLLABLE SSEUNG
+C506;C506;110A 1173 11BD;C506;110A 1173 11BD; # (씆; 씆; 씆; 씆; 씆; ) HANGUL SYLLABLE SSEUJ
+C507;C507;110A 1173 11BE;C507;110A 1173 11BE; # (씇; 씇; 씇; 씇; 씇; ) HANGUL SYLLABLE SSEUC
+C508;C508;110A 1173 11BF;C508;110A 1173 11BF; # (씈; 씈; 씈; 씈; 씈; ) HANGUL SYLLABLE SSEUK
+C509;C509;110A 1173 11C0;C509;110A 1173 11C0; # (씉; 씉; 씉; 씉; 씉; ) HANGUL SYLLABLE SSEUT
+C50A;C50A;110A 1173 11C1;C50A;110A 1173 11C1; # (씊; 씊; 쓰á‡; 씊; 쓰á‡; ) HANGUL SYLLABLE SSEUP
+C50B;C50B;110A 1173 11C2;C50B;110A 1173 11C2; # (씋; 씋; 씋; 씋; 씋; ) HANGUL SYLLABLE SSEUH
+C50C;C50C;110A 1174;C50C;110A 1174; # (씌; 씌; 씌; 씌; 씌; ) HANGUL SYLLABLE SSYI
+C50D;C50D;110A 1174 11A8;C50D;110A 1174 11A8; # (ì”; ì”; 씍; ì”; 씍; ) HANGUL SYLLABLE SSYIG
+C50E;C50E;110A 1174 11A9;C50E;110A 1174 11A9; # (씎; 씎; 씎; 씎; 씎; ) HANGUL SYLLABLE SSYIGG
+C50F;C50F;110A 1174 11AA;C50F;110A 1174 11AA; # (ì”; ì”; 씏; ì”; 씏; ) HANGUL SYLLABLE SSYIGS
+C510;C510;110A 1174 11AB;C510;110A 1174 11AB; # (ì”; ì”; 씐; ì”; 씐; ) HANGUL SYLLABLE SSYIN
+C511;C511;110A 1174 11AC;C511;110A 1174 11AC; # (씑; 씑; 씑; 씑; 씑; ) HANGUL SYLLABLE SSYINJ
+C512;C512;110A 1174 11AD;C512;110A 1174 11AD; # (씒; 씒; 씒; 씒; 씒; ) HANGUL SYLLABLE SSYINH
+C513;C513;110A 1174 11AE;C513;110A 1174 11AE; # (씓; 씓; 씓; 씓; 씓; ) HANGUL SYLLABLE SSYID
+C514;C514;110A 1174 11AF;C514;110A 1174 11AF; # (씔; 씔; 씔; 씔; 씔; ) HANGUL SYLLABLE SSYIL
+C515;C515;110A 1174 11B0;C515;110A 1174 11B0; # (씕; 씕; 씕; 씕; 씕; ) HANGUL SYLLABLE SSYILG
+C516;C516;110A 1174 11B1;C516;110A 1174 11B1; # (씖; 씖; 씖; 씖; 씖; ) HANGUL SYLLABLE SSYILM
+C517;C517;110A 1174 11B2;C517;110A 1174 11B2; # (씗; 씗; 씗; 씗; 씗; ) HANGUL SYLLABLE SSYILB
+C518;C518;110A 1174 11B3;C518;110A 1174 11B3; # (씘; 씘; 씘; 씘; 씘; ) HANGUL SYLLABLE SSYILS
+C519;C519;110A 1174 11B4;C519;110A 1174 11B4; # (씙; 씙; 씙; 씙; 씙; ) HANGUL SYLLABLE SSYILT
+C51A;C51A;110A 1174 11B5;C51A;110A 1174 11B5; # (씚; 씚; 씚; 씚; 씚; ) HANGUL SYLLABLE SSYILP
+C51B;C51B;110A 1174 11B6;C51B;110A 1174 11B6; # (씛; 씛; 씛; 씛; 씛; ) HANGUL SYLLABLE SSYILH
+C51C;C51C;110A 1174 11B7;C51C;110A 1174 11B7; # (씜; 씜; 씜; 씜; 씜; ) HANGUL SYLLABLE SSYIM
+C51D;C51D;110A 1174 11B8;C51D;110A 1174 11B8; # (ì”; ì”; 씝; ì”; 씝; ) HANGUL SYLLABLE SSYIB
+C51E;C51E;110A 1174 11B9;C51E;110A 1174 11B9; # (씞; 씞; 씞; 씞; 씞; ) HANGUL SYLLABLE SSYIBS
+C51F;C51F;110A 1174 11BA;C51F;110A 1174 11BA; # (씟; 씟; 씟; 씟; 씟; ) HANGUL SYLLABLE SSYIS
+C520;C520;110A 1174 11BB;C520;110A 1174 11BB; # (씠; 씠; 씠; 씠; 씠; ) HANGUL SYLLABLE SSYISS
+C521;C521;110A 1174 11BC;C521;110A 1174 11BC; # (씡; 씡; 씡; 씡; 씡; ) HANGUL SYLLABLE SSYING
+C522;C522;110A 1174 11BD;C522;110A 1174 11BD; # (씢; 씢; 씢; 씢; 씢; ) HANGUL SYLLABLE SSYIJ
+C523;C523;110A 1174 11BE;C523;110A 1174 11BE; # (씣; 씣; 씣; 씣; 씣; ) HANGUL SYLLABLE SSYIC
+C524;C524;110A 1174 11BF;C524;110A 1174 11BF; # (씤; 씤; 씤; 씤; 씤; ) HANGUL SYLLABLE SSYIK
+C525;C525;110A 1174 11C0;C525;110A 1174 11C0; # (씥; 씥; 씥; 씥; 씥; ) HANGUL SYLLABLE SSYIT
+C526;C526;110A 1174 11C1;C526;110A 1174 11C1; # (씦; 씦; 씌á‡; 씦; 씌á‡; ) HANGUL SYLLABLE SSYIP
+C527;C527;110A 1174 11C2;C527;110A 1174 11C2; # (씧; 씧; 씧; 씧; 씧; ) HANGUL SYLLABLE SSYIH
+C528;C528;110A 1175;C528;110A 1175; # (씨; 씨; 씨; 씨; 씨; ) HANGUL SYLLABLE SSI
+C529;C529;110A 1175 11A8;C529;110A 1175 11A8; # (씩; 씩; 씩; 씩; 씩; ) HANGUL SYLLABLE SSIG
+C52A;C52A;110A 1175 11A9;C52A;110A 1175 11A9; # (씪; 씪; 씪; 씪; 씪; ) HANGUL SYLLABLE SSIGG
+C52B;C52B;110A 1175 11AA;C52B;110A 1175 11AA; # (씫; 씫; 씫; 씫; 씫; ) HANGUL SYLLABLE SSIGS
+C52C;C52C;110A 1175 11AB;C52C;110A 1175 11AB; # (씬; 씬; 씬; 씬; 씬; ) HANGUL SYLLABLE SSIN
+C52D;C52D;110A 1175 11AC;C52D;110A 1175 11AC; # (씭; 씭; 씭; 씭; 씭; ) HANGUL SYLLABLE SSINJ
+C52E;C52E;110A 1175 11AD;C52E;110A 1175 11AD; # (씮; 씮; 씮; 씮; 씮; ) HANGUL SYLLABLE SSINH
+C52F;C52F;110A 1175 11AE;C52F;110A 1175 11AE; # (씯; 씯; 씯; 씯; 씯; ) HANGUL SYLLABLE SSID
+C530;C530;110A 1175 11AF;C530;110A 1175 11AF; # (씰; 씰; 씰; 씰; 씰; ) HANGUL SYLLABLE SSIL
+C531;C531;110A 1175 11B0;C531;110A 1175 11B0; # (씱; 씱; 씱; 씱; 씱; ) HANGUL SYLLABLE SSILG
+C532;C532;110A 1175 11B1;C532;110A 1175 11B1; # (씲; 씲; 씲; 씲; 씲; ) HANGUL SYLLABLE SSILM
+C533;C533;110A 1175 11B2;C533;110A 1175 11B2; # (씳; 씳; 씳; 씳; 씳; ) HANGUL SYLLABLE SSILB
+C534;C534;110A 1175 11B3;C534;110A 1175 11B3; # (씴; 씴; 씴; 씴; 씴; ) HANGUL SYLLABLE SSILS
+C535;C535;110A 1175 11B4;C535;110A 1175 11B4; # (씵; 씵; 씵; 씵; 씵; ) HANGUL SYLLABLE SSILT
+C536;C536;110A 1175 11B5;C536;110A 1175 11B5; # (씶; 씶; 씶; 씶; 씶; ) HANGUL SYLLABLE SSILP
+C537;C537;110A 1175 11B6;C537;110A 1175 11B6; # (씷; 씷; 씷; 씷; 씷; ) HANGUL SYLLABLE SSILH
+C538;C538;110A 1175 11B7;C538;110A 1175 11B7; # (씸; 씸; 씸; 씸; 씸; ) HANGUL SYLLABLE SSIM
+C539;C539;110A 1175 11B8;C539;110A 1175 11B8; # (씹; 씹; 씹; 씹; 씹; ) HANGUL SYLLABLE SSIB
+C53A;C53A;110A 1175 11B9;C53A;110A 1175 11B9; # (씺; 씺; 씺; 씺; 씺; ) HANGUL SYLLABLE SSIBS
+C53B;C53B;110A 1175 11BA;C53B;110A 1175 11BA; # (씻; 씻; 씻; 씻; 씻; ) HANGUL SYLLABLE SSIS
+C53C;C53C;110A 1175 11BB;C53C;110A 1175 11BB; # (씼; 씼; 씼; 씼; 씼; ) HANGUL SYLLABLE SSISS
+C53D;C53D;110A 1175 11BC;C53D;110A 1175 11BC; # (씽; 씽; 씽; 씽; 씽; ) HANGUL SYLLABLE SSING
+C53E;C53E;110A 1175 11BD;C53E;110A 1175 11BD; # (씾; 씾; 씾; 씾; 씾; ) HANGUL SYLLABLE SSIJ
+C53F;C53F;110A 1175 11BE;C53F;110A 1175 11BE; # (씿; 씿; 씿; 씿; 씿; ) HANGUL SYLLABLE SSIC
+C540;C540;110A 1175 11BF;C540;110A 1175 11BF; # (앀; 앀; 앀; 앀; 앀; ) HANGUL SYLLABLE SSIK
+C541;C541;110A 1175 11C0;C541;110A 1175 11C0; # (ì•; ì•; 앁; ì•; 앁; ) HANGUL SYLLABLE SSIT
+C542;C542;110A 1175 11C1;C542;110A 1175 11C1; # (ì•‚; ì•‚; 씨á‡; ì•‚; 씨á‡; ) HANGUL SYLLABLE SSIP
+C543;C543;110A 1175 11C2;C543;110A 1175 11C2; # (앃; 앃; 앃; 앃; 앃; ) HANGUL SYLLABLE SSIH
+C544;C544;110B 1161;C544;110B 1161; # (ì•„; ì•„; á„‹á…¡; ì•„; á„‹á…¡; ) HANGUL SYLLABLE A
+C545;C545;110B 1161 11A8;C545;110B 1161 11A8; # (악; 악; 악; 악; 악; ) HANGUL SYLLABLE AG
+C546;C546;110B 1161 11A9;C546;110B 1161 11A9; # (앆; 앆; 앆; 앆; 앆; ) HANGUL SYLLABLE AGG
+C547;C547;110B 1161 11AA;C547;110B 1161 11AA; # (앇; 앇; 앇; 앇; 앇; ) HANGUL SYLLABLE AGS
+C548;C548;110B 1161 11AB;C548;110B 1161 11AB; # (안; 안; 안; 안; 안; ) HANGUL SYLLABLE AN
+C549;C549;110B 1161 11AC;C549;110B 1161 11AC; # (앉; 앉; 앉; 앉; 앉; ) HANGUL SYLLABLE ANJ
+C54A;C54A;110B 1161 11AD;C54A;110B 1161 11AD; # (않; 않; 않; 않; 않; ) HANGUL SYLLABLE ANH
+C54B;C54B;110B 1161 11AE;C54B;110B 1161 11AE; # (앋; 앋; 앋; 앋; 앋; ) HANGUL SYLLABLE AD
+C54C;C54C;110B 1161 11AF;C54C;110B 1161 11AF; # (알; 알; 알; 알; 알; ) HANGUL SYLLABLE AL
+C54D;C54D;110B 1161 11B0;C54D;110B 1161 11B0; # (ì•; ì•; 앍; ì•; 앍; ) HANGUL SYLLABLE ALG
+C54E;C54E;110B 1161 11B1;C54E;110B 1161 11B1; # (앎; 앎; 앎; 앎; 앎; ) HANGUL SYLLABLE ALM
+C54F;C54F;110B 1161 11B2;C54F;110B 1161 11B2; # (ì•; ì•; 앏; ì•; 앏; ) HANGUL SYLLABLE ALB
+C550;C550;110B 1161 11B3;C550;110B 1161 11B3; # (ì•; ì•; 앐; ì•; 앐; ) HANGUL SYLLABLE ALS
+C551;C551;110B 1161 11B4;C551;110B 1161 11B4; # (앑; 앑; 앑; 앑; 앑; ) HANGUL SYLLABLE ALT
+C552;C552;110B 1161 11B5;C552;110B 1161 11B5; # (앒; 앒; 앒; 앒; 앒; ) HANGUL SYLLABLE ALP
+C553;C553;110B 1161 11B6;C553;110B 1161 11B6; # (앓; 앓; 앓; 앓; 앓; ) HANGUL SYLLABLE ALH
+C554;C554;110B 1161 11B7;C554;110B 1161 11B7; # (암; 암; 암; 암; 암; ) HANGUL SYLLABLE AM
+C555;C555;110B 1161 11B8;C555;110B 1161 11B8; # (압; 압; 압; 압; 압; ) HANGUL SYLLABLE AB
+C556;C556;110B 1161 11B9;C556;110B 1161 11B9; # (앖; 앖; 앖; 앖; 앖; ) HANGUL SYLLABLE ABS
+C557;C557;110B 1161 11BA;C557;110B 1161 11BA; # (앗; 앗; 앗; 앗; 앗; ) HANGUL SYLLABLE AS
+C558;C558;110B 1161 11BB;C558;110B 1161 11BB; # (았; 았; 았; 았; 았; ) HANGUL SYLLABLE ASS
+C559;C559;110B 1161 11BC;C559;110B 1161 11BC; # (앙; 앙; 앙; 앙; 앙; ) HANGUL SYLLABLE ANG
+C55A;C55A;110B 1161 11BD;C55A;110B 1161 11BD; # (앚; 앚; 앚; 앚; 앚; ) HANGUL SYLLABLE AJ
+C55B;C55B;110B 1161 11BE;C55B;110B 1161 11BE; # (앛; 앛; 앛; 앛; 앛; ) HANGUL SYLLABLE AC
+C55C;C55C;110B 1161 11BF;C55C;110B 1161 11BF; # (앜; 앜; 앜; 앜; 앜; ) HANGUL SYLLABLE AK
+C55D;C55D;110B 1161 11C0;C55D;110B 1161 11C0; # (ì•; ì•; 앝; ì•; 앝; ) HANGUL SYLLABLE AT
+C55E;C55E;110B 1161 11C1;C55E;110B 1161 11C1; # (앞; 앞; á„‹á…¡á‡; 앞; á„‹á…¡á‡; ) HANGUL SYLLABLE AP
+C55F;C55F;110B 1161 11C2;C55F;110B 1161 11C2; # (앟; 앟; 앟; 앟; 앟; ) HANGUL SYLLABLE AH
+C560;C560;110B 1162;C560;110B 1162; # (ì• ; ì• ; á„‹á…¢; ì• ; á„‹á…¢; ) HANGUL SYLLABLE AE
+C561;C561;110B 1162 11A8;C561;110B 1162 11A8; # (액; 액; 액; 액; 액; ) HANGUL SYLLABLE AEG
+C562;C562;110B 1162 11A9;C562;110B 1162 11A9; # (앢; 앢; 앢; 앢; 앢; ) HANGUL SYLLABLE AEGG
+C563;C563;110B 1162 11AA;C563;110B 1162 11AA; # (앣; 앣; 앣; 앣; 앣; ) HANGUL SYLLABLE AEGS
+C564;C564;110B 1162 11AB;C564;110B 1162 11AB; # (앤; 앤; 앤; 앤; 앤; ) HANGUL SYLLABLE AEN
+C565;C565;110B 1162 11AC;C565;110B 1162 11AC; # (앥; 앥; 앥; 앥; 앥; ) HANGUL SYLLABLE AENJ
+C566;C566;110B 1162 11AD;C566;110B 1162 11AD; # (앦; 앦; 앦; 앦; 앦; ) HANGUL SYLLABLE AENH
+C567;C567;110B 1162 11AE;C567;110B 1162 11AE; # (앧; 앧; 앧; 앧; 앧; ) HANGUL SYLLABLE AED
+C568;C568;110B 1162 11AF;C568;110B 1162 11AF; # (앨; 앨; 앨; 앨; 앨; ) HANGUL SYLLABLE AEL
+C569;C569;110B 1162 11B0;C569;110B 1162 11B0; # (앩; 앩; 앩; 앩; 앩; ) HANGUL SYLLABLE AELG
+C56A;C56A;110B 1162 11B1;C56A;110B 1162 11B1; # (앪; 앪; 앪; 앪; 앪; ) HANGUL SYLLABLE AELM
+C56B;C56B;110B 1162 11B2;C56B;110B 1162 11B2; # (앫; 앫; 앫; 앫; 앫; ) HANGUL SYLLABLE AELB
+C56C;C56C;110B 1162 11B3;C56C;110B 1162 11B3; # (앬; 앬; 앬; 앬; 앬; ) HANGUL SYLLABLE AELS
+C56D;C56D;110B 1162 11B4;C56D;110B 1162 11B4; # (앭; 앭; 앭; 앭; 앭; ) HANGUL SYLLABLE AELT
+C56E;C56E;110B 1162 11B5;C56E;110B 1162 11B5; # (앮; 앮; 앮; 앮; 앮; ) HANGUL SYLLABLE AELP
+C56F;C56F;110B 1162 11B6;C56F;110B 1162 11B6; # (앯; 앯; 앯; 앯; 앯; ) HANGUL SYLLABLE AELH
+C570;C570;110B 1162 11B7;C570;110B 1162 11B7; # (앰; 앰; 앰; 앰; 앰; ) HANGUL SYLLABLE AEM
+C571;C571;110B 1162 11B8;C571;110B 1162 11B8; # (앱; 앱; 앱; 앱; 앱; ) HANGUL SYLLABLE AEB
+C572;C572;110B 1162 11B9;C572;110B 1162 11B9; # (앲; 앲; 앲; 앲; 앲; ) HANGUL SYLLABLE AEBS
+C573;C573;110B 1162 11BA;C573;110B 1162 11BA; # (앳; 앳; 앳; 앳; 앳; ) HANGUL SYLLABLE AES
+C574;C574;110B 1162 11BB;C574;110B 1162 11BB; # (앴; 앴; 앴; 앴; 앴; ) HANGUL SYLLABLE AESS
+C575;C575;110B 1162 11BC;C575;110B 1162 11BC; # (앵; 앵; 앵; 앵; 앵; ) HANGUL SYLLABLE AENG
+C576;C576;110B 1162 11BD;C576;110B 1162 11BD; # (앶; 앶; 앶; 앶; 앶; ) HANGUL SYLLABLE AEJ
+C577;C577;110B 1162 11BE;C577;110B 1162 11BE; # (앷; 앷; 앷; 앷; 앷; ) HANGUL SYLLABLE AEC
+C578;C578;110B 1162 11BF;C578;110B 1162 11BF; # (앸; 앸; 앸; 앸; 앸; ) HANGUL SYLLABLE AEK
+C579;C579;110B 1162 11C0;C579;110B 1162 11C0; # (앹; 앹; 앹; 앹; 앹; ) HANGUL SYLLABLE AET
+C57A;C57A;110B 1162 11C1;C57A;110B 1162 11C1; # (앺; 앺; á„‹á…¢á‡; 앺; á„‹á…¢á‡; ) HANGUL SYLLABLE AEP
+C57B;C57B;110B 1162 11C2;C57B;110B 1162 11C2; # (앻; 앻; 앻; 앻; 앻; ) HANGUL SYLLABLE AEH
+C57C;C57C;110B 1163;C57C;110B 1163; # (야; 야; 야; 야; 야; ) HANGUL SYLLABLE YA
+C57D;C57D;110B 1163 11A8;C57D;110B 1163 11A8; # (약; 약; 약; 약; 약; ) HANGUL SYLLABLE YAG
+C57E;C57E;110B 1163 11A9;C57E;110B 1163 11A9; # (앾; 앾; 앾; 앾; 앾; ) HANGUL SYLLABLE YAGG
+C57F;C57F;110B 1163 11AA;C57F;110B 1163 11AA; # (앿; 앿; 앿; 앿; 앿; ) HANGUL SYLLABLE YAGS
+C580;C580;110B 1163 11AB;C580;110B 1163 11AB; # (얀; 얀; 얀; 얀; 얀; ) HANGUL SYLLABLE YAN
+C581;C581;110B 1163 11AC;C581;110B 1163 11AC; # (ì–; ì–; 얁; ì–; 얁; ) HANGUL SYLLABLE YANJ
+C582;C582;110B 1163 11AD;C582;110B 1163 11AD; # (얂; 얂; 얂; 얂; 얂; ) HANGUL SYLLABLE YANH
+C583;C583;110B 1163 11AE;C583;110B 1163 11AE; # (얃; 얃; 얃; 얃; 얃; ) HANGUL SYLLABLE YAD
+C584;C584;110B 1163 11AF;C584;110B 1163 11AF; # (얄; 얄; 얄; 얄; 얄; ) HANGUL SYLLABLE YAL
+C585;C585;110B 1163 11B0;C585;110B 1163 11B0; # (얅; 얅; 얅; 얅; 얅; ) HANGUL SYLLABLE YALG
+C586;C586;110B 1163 11B1;C586;110B 1163 11B1; # (얆; 얆; 얆; 얆; 얆; ) HANGUL SYLLABLE YALM
+C587;C587;110B 1163 11B2;C587;110B 1163 11B2; # (얇; 얇; 얇; 얇; 얇; ) HANGUL SYLLABLE YALB
+C588;C588;110B 1163 11B3;C588;110B 1163 11B3; # (얈; 얈; 얈; 얈; 얈; ) HANGUL SYLLABLE YALS
+C589;C589;110B 1163 11B4;C589;110B 1163 11B4; # (얉; 얉; 얉; 얉; 얉; ) HANGUL SYLLABLE YALT
+C58A;C58A;110B 1163 11B5;C58A;110B 1163 11B5; # (얊; 얊; 얊; 얊; 얊; ) HANGUL SYLLABLE YALP
+C58B;C58B;110B 1163 11B6;C58B;110B 1163 11B6; # (얋; 얋; 얋; 얋; 얋; ) HANGUL SYLLABLE YALH
+C58C;C58C;110B 1163 11B7;C58C;110B 1163 11B7; # (얌; 얌; 얌; 얌; 얌; ) HANGUL SYLLABLE YAM
+C58D;C58D;110B 1163 11B8;C58D;110B 1163 11B8; # (ì–; ì–; 얍; ì–; 얍; ) HANGUL SYLLABLE YAB
+C58E;C58E;110B 1163 11B9;C58E;110B 1163 11B9; # (얎; 얎; 얎; 얎; 얎; ) HANGUL SYLLABLE YABS
+C58F;C58F;110B 1163 11BA;C58F;110B 1163 11BA; # (ì–; ì–; 얏; ì–; 얏; ) HANGUL SYLLABLE YAS
+C590;C590;110B 1163 11BB;C590;110B 1163 11BB; # (ì–; ì–; 얐; ì–; 얐; ) HANGUL SYLLABLE YASS
+C591;C591;110B 1163 11BC;C591;110B 1163 11BC; # (양; 양; 양; 양; 양; ) HANGUL SYLLABLE YANG
+C592;C592;110B 1163 11BD;C592;110B 1163 11BD; # (얒; 얒; 얒; 얒; 얒; ) HANGUL SYLLABLE YAJ
+C593;C593;110B 1163 11BE;C593;110B 1163 11BE; # (얓; 얓; 얓; 얓; 얓; ) HANGUL SYLLABLE YAC
+C594;C594;110B 1163 11BF;C594;110B 1163 11BF; # (얔; 얔; 얔; 얔; 얔; ) HANGUL SYLLABLE YAK
+C595;C595;110B 1163 11C0;C595;110B 1163 11C0; # (얕; 얕; 얕; 얕; 얕; ) HANGUL SYLLABLE YAT
+C596;C596;110B 1163 11C1;C596;110B 1163 11C1; # (ì––; ì––; á„‹á…£á‡; ì––; á„‹á…£á‡; ) HANGUL SYLLABLE YAP
+C597;C597;110B 1163 11C2;C597;110B 1163 11C2; # (얗; 얗; 얗; 얗; 얗; ) HANGUL SYLLABLE YAH
+C598;C598;110B 1164;C598;110B 1164; # (ì–˜; ì–˜; á„‹á…¤; ì–˜; á„‹á…¤; ) HANGUL SYLLABLE YAE
+C599;C599;110B 1164 11A8;C599;110B 1164 11A8; # (얙; 얙; 얙; 얙; 얙; ) HANGUL SYLLABLE YAEG
+C59A;C59A;110B 1164 11A9;C59A;110B 1164 11A9; # (얚; 얚; 얚; 얚; 얚; ) HANGUL SYLLABLE YAEGG
+C59B;C59B;110B 1164 11AA;C59B;110B 1164 11AA; # (얛; 얛; 얛; 얛; 얛; ) HANGUL SYLLABLE YAEGS
+C59C;C59C;110B 1164 11AB;C59C;110B 1164 11AB; # (얜; 얜; 얜; 얜; 얜; ) HANGUL SYLLABLE YAEN
+C59D;C59D;110B 1164 11AC;C59D;110B 1164 11AC; # (ì–; ì–; 얝; ì–; 얝; ) HANGUL SYLLABLE YAENJ
+C59E;C59E;110B 1164 11AD;C59E;110B 1164 11AD; # (얞; 얞; 얞; 얞; 얞; ) HANGUL SYLLABLE YAENH
+C59F;C59F;110B 1164 11AE;C59F;110B 1164 11AE; # (얟; 얟; 얟; 얟; 얟; ) HANGUL SYLLABLE YAED
+C5A0;C5A0;110B 1164 11AF;C5A0;110B 1164 11AF; # (얠; 얠; 얠; 얠; 얠; ) HANGUL SYLLABLE YAEL
+C5A1;C5A1;110B 1164 11B0;C5A1;110B 1164 11B0; # (얡; 얡; 얡; 얡; 얡; ) HANGUL SYLLABLE YAELG
+C5A2;C5A2;110B 1164 11B1;C5A2;110B 1164 11B1; # (얢; 얢; 얢; 얢; 얢; ) HANGUL SYLLABLE YAELM
+C5A3;C5A3;110B 1164 11B2;C5A3;110B 1164 11B2; # (얣; 얣; 얣; 얣; 얣; ) HANGUL SYLLABLE YAELB
+C5A4;C5A4;110B 1164 11B3;C5A4;110B 1164 11B3; # (얤; 얤; 얤; 얤; 얤; ) HANGUL SYLLABLE YAELS
+C5A5;C5A5;110B 1164 11B4;C5A5;110B 1164 11B4; # (얥; 얥; 얥; 얥; 얥; ) HANGUL SYLLABLE YAELT
+C5A6;C5A6;110B 1164 11B5;C5A6;110B 1164 11B5; # (얦; 얦; 얦; 얦; 얦; ) HANGUL SYLLABLE YAELP
+C5A7;C5A7;110B 1164 11B6;C5A7;110B 1164 11B6; # (얧; 얧; 얧; 얧; 얧; ) HANGUL SYLLABLE YAELH
+C5A8;C5A8;110B 1164 11B7;C5A8;110B 1164 11B7; # (얨; 얨; 얨; 얨; 얨; ) HANGUL SYLLABLE YAEM
+C5A9;C5A9;110B 1164 11B8;C5A9;110B 1164 11B8; # (얩; 얩; 얩; 얩; 얩; ) HANGUL SYLLABLE YAEB
+C5AA;C5AA;110B 1164 11B9;C5AA;110B 1164 11B9; # (얪; 얪; 얪; 얪; 얪; ) HANGUL SYLLABLE YAEBS
+C5AB;C5AB;110B 1164 11BA;C5AB;110B 1164 11BA; # (얫; 얫; 얫; 얫; 얫; ) HANGUL SYLLABLE YAES
+C5AC;C5AC;110B 1164 11BB;C5AC;110B 1164 11BB; # (얬; 얬; 얬; 얬; 얬; ) HANGUL SYLLABLE YAESS
+C5AD;C5AD;110B 1164 11BC;C5AD;110B 1164 11BC; # (얭; 얭; 얭; 얭; 얭; ) HANGUL SYLLABLE YAENG
+C5AE;C5AE;110B 1164 11BD;C5AE;110B 1164 11BD; # (얮; 얮; 얮; 얮; 얮; ) HANGUL SYLLABLE YAEJ
+C5AF;C5AF;110B 1164 11BE;C5AF;110B 1164 11BE; # (얯; 얯; 얯; 얯; 얯; ) HANGUL SYLLABLE YAEC
+C5B0;C5B0;110B 1164 11BF;C5B0;110B 1164 11BF; # (얰; 얰; 얰; 얰; 얰; ) HANGUL SYLLABLE YAEK
+C5B1;C5B1;110B 1164 11C0;C5B1;110B 1164 11C0; # (얱; 얱; 얱; 얱; 얱; ) HANGUL SYLLABLE YAET
+C5B2;C5B2;110B 1164 11C1;C5B2;110B 1164 11C1; # (ì–²; ì–²; á„‹á…¤á‡; ì–²; á„‹á…¤á‡; ) HANGUL SYLLABLE YAEP
+C5B3;C5B3;110B 1164 11C2;C5B3;110B 1164 11C2; # (얳; 얳; 얳; 얳; 얳; ) HANGUL SYLLABLE YAEH
+C5B4;C5B4;110B 1165;C5B4;110B 1165; # (ì–´; ì–´; á„‹á…¥; ì–´; á„‹á…¥; ) HANGUL SYLLABLE EO
+C5B5;C5B5;110B 1165 11A8;C5B5;110B 1165 11A8; # (억; 억; 억; 억; 억; ) HANGUL SYLLABLE EOG
+C5B6;C5B6;110B 1165 11A9;C5B6;110B 1165 11A9; # (얶; 얶; 얶; 얶; 얶; ) HANGUL SYLLABLE EOGG
+C5B7;C5B7;110B 1165 11AA;C5B7;110B 1165 11AA; # (얷; 얷; 얷; 얷; 얷; ) HANGUL SYLLABLE EOGS
+C5B8;C5B8;110B 1165 11AB;C5B8;110B 1165 11AB; # (언; 언; 언; 언; 언; ) HANGUL SYLLABLE EON
+C5B9;C5B9;110B 1165 11AC;C5B9;110B 1165 11AC; # (얹; 얹; 얹; 얹; 얹; ) HANGUL SYLLABLE EONJ
+C5BA;C5BA;110B 1165 11AD;C5BA;110B 1165 11AD; # (얺; 얺; 얺; 얺; 얺; ) HANGUL SYLLABLE EONH
+C5BB;C5BB;110B 1165 11AE;C5BB;110B 1165 11AE; # (얻; 얻; 얻; 얻; 얻; ) HANGUL SYLLABLE EOD
+C5BC;C5BC;110B 1165 11AF;C5BC;110B 1165 11AF; # (얼; 얼; 얼; 얼; 얼; ) HANGUL SYLLABLE EOL
+C5BD;C5BD;110B 1165 11B0;C5BD;110B 1165 11B0; # (얽; 얽; 얽; 얽; 얽; ) HANGUL SYLLABLE EOLG
+C5BE;C5BE;110B 1165 11B1;C5BE;110B 1165 11B1; # (얾; 얾; 얾; 얾; 얾; ) HANGUL SYLLABLE EOLM
+C5BF;C5BF;110B 1165 11B2;C5BF;110B 1165 11B2; # (얿; 얿; 얿; 얿; 얿; ) HANGUL SYLLABLE EOLB
+C5C0;C5C0;110B 1165 11B3;C5C0;110B 1165 11B3; # (엀; 엀; 엀; 엀; 엀; ) HANGUL SYLLABLE EOLS
+C5C1;C5C1;110B 1165 11B4;C5C1;110B 1165 11B4; # (ì—; ì—; 엁; ì—; 엁; ) HANGUL SYLLABLE EOLT
+C5C2;C5C2;110B 1165 11B5;C5C2;110B 1165 11B5; # (엂; 엂; 엂; 엂; 엂; ) HANGUL SYLLABLE EOLP
+C5C3;C5C3;110B 1165 11B6;C5C3;110B 1165 11B6; # (엃; 엃; 엃; 엃; 엃; ) HANGUL SYLLABLE EOLH
+C5C4;C5C4;110B 1165 11B7;C5C4;110B 1165 11B7; # (엄; 엄; 엄; 엄; 엄; ) HANGUL SYLLABLE EOM
+C5C5;C5C5;110B 1165 11B8;C5C5;110B 1165 11B8; # (업; 업; 업; 업; 업; ) HANGUL SYLLABLE EOB
+C5C6;C5C6;110B 1165 11B9;C5C6;110B 1165 11B9; # (없; 없; 없; 없; 없; ) HANGUL SYLLABLE EOBS
+C5C7;C5C7;110B 1165 11BA;C5C7;110B 1165 11BA; # (엇; 엇; 엇; 엇; 엇; ) HANGUL SYLLABLE EOS
+C5C8;C5C8;110B 1165 11BB;C5C8;110B 1165 11BB; # (었; 었; 었; 었; 었; ) HANGUL SYLLABLE EOSS
+C5C9;C5C9;110B 1165 11BC;C5C9;110B 1165 11BC; # (엉; 엉; 엉; 엉; 엉; ) HANGUL SYLLABLE EONG
+C5CA;C5CA;110B 1165 11BD;C5CA;110B 1165 11BD; # (엊; 엊; 엊; 엊; 엊; ) HANGUL SYLLABLE EOJ
+C5CB;C5CB;110B 1165 11BE;C5CB;110B 1165 11BE; # (엋; 엋; 엋; 엋; 엋; ) HANGUL SYLLABLE EOC
+C5CC;C5CC;110B 1165 11BF;C5CC;110B 1165 11BF; # (엌; 엌; 엌; 엌; 엌; ) HANGUL SYLLABLE EOK
+C5CD;C5CD;110B 1165 11C0;C5CD;110B 1165 11C0; # (ì—; ì—; 엍; ì—; 엍; ) HANGUL SYLLABLE EOT
+C5CE;C5CE;110B 1165 11C1;C5CE;110B 1165 11C1; # (ì—Ž; ì—Ž; á„‹á…¥á‡; ì—Ž; á„‹á…¥á‡; ) HANGUL SYLLABLE EOP
+C5CF;C5CF;110B 1165 11C2;C5CF;110B 1165 11C2; # (ì—; ì—; 엏; ì—; 엏; ) HANGUL SYLLABLE EOH
+C5D0;C5D0;110B 1166;C5D0;110B 1166; # (ì—; ì—; á„‹á…¦; ì—; á„‹á…¦; ) HANGUL SYLLABLE E
+C5D1;C5D1;110B 1166 11A8;C5D1;110B 1166 11A8; # (엑; 엑; 엑; 엑; 엑; ) HANGUL SYLLABLE EG
+C5D2;C5D2;110B 1166 11A9;C5D2;110B 1166 11A9; # (엒; 엒; 엒; 엒; 엒; ) HANGUL SYLLABLE EGG
+C5D3;C5D3;110B 1166 11AA;C5D3;110B 1166 11AA; # (엓; 엓; 엓; 엓; 엓; ) HANGUL SYLLABLE EGS
+C5D4;C5D4;110B 1166 11AB;C5D4;110B 1166 11AB; # (엔; 엔; 엔; 엔; 엔; ) HANGUL SYLLABLE EN
+C5D5;C5D5;110B 1166 11AC;C5D5;110B 1166 11AC; # (엕; 엕; 엕; 엕; 엕; ) HANGUL SYLLABLE ENJ
+C5D6;C5D6;110B 1166 11AD;C5D6;110B 1166 11AD; # (엖; 엖; 엖; 엖; 엖; ) HANGUL SYLLABLE ENH
+C5D7;C5D7;110B 1166 11AE;C5D7;110B 1166 11AE; # (엗; 엗; 엗; 엗; 엗; ) HANGUL SYLLABLE ED
+C5D8;C5D8;110B 1166 11AF;C5D8;110B 1166 11AF; # (엘; 엘; 엘; 엘; 엘; ) HANGUL SYLLABLE EL
+C5D9;C5D9;110B 1166 11B0;C5D9;110B 1166 11B0; # (엙; 엙; 엙; 엙; 엙; ) HANGUL SYLLABLE ELG
+C5DA;C5DA;110B 1166 11B1;C5DA;110B 1166 11B1; # (엚; 엚; 엚; 엚; 엚; ) HANGUL SYLLABLE ELM
+C5DB;C5DB;110B 1166 11B2;C5DB;110B 1166 11B2; # (엛; 엛; 엛; 엛; 엛; ) HANGUL SYLLABLE ELB
+C5DC;C5DC;110B 1166 11B3;C5DC;110B 1166 11B3; # (엜; 엜; 엜; 엜; 엜; ) HANGUL SYLLABLE ELS
+C5DD;C5DD;110B 1166 11B4;C5DD;110B 1166 11B4; # (ì—; ì—; 엝; ì—; 엝; ) HANGUL SYLLABLE ELT
+C5DE;C5DE;110B 1166 11B5;C5DE;110B 1166 11B5; # (엞; 엞; 엞; 엞; 엞; ) HANGUL SYLLABLE ELP
+C5DF;C5DF;110B 1166 11B6;C5DF;110B 1166 11B6; # (엟; 엟; 엟; 엟; 엟; ) HANGUL SYLLABLE ELH
+C5E0;C5E0;110B 1166 11B7;C5E0;110B 1166 11B7; # (엠; 엠; 엠; 엠; 엠; ) HANGUL SYLLABLE EM
+C5E1;C5E1;110B 1166 11B8;C5E1;110B 1166 11B8; # (엡; 엡; 엡; 엡; 엡; ) HANGUL SYLLABLE EB
+C5E2;C5E2;110B 1166 11B9;C5E2;110B 1166 11B9; # (엢; 엢; 엢; 엢; 엢; ) HANGUL SYLLABLE EBS
+C5E3;C5E3;110B 1166 11BA;C5E3;110B 1166 11BA; # (엣; 엣; 엣; 엣; 엣; ) HANGUL SYLLABLE ES
+C5E4;C5E4;110B 1166 11BB;C5E4;110B 1166 11BB; # (엤; 엤; 엤; 엤; 엤; ) HANGUL SYLLABLE ESS
+C5E5;C5E5;110B 1166 11BC;C5E5;110B 1166 11BC; # (엥; 엥; 엥; 엥; 엥; ) HANGUL SYLLABLE ENG
+C5E6;C5E6;110B 1166 11BD;C5E6;110B 1166 11BD; # (엦; 엦; 엦; 엦; 엦; ) HANGUL SYLLABLE EJ
+C5E7;C5E7;110B 1166 11BE;C5E7;110B 1166 11BE; # (엧; 엧; 엧; 엧; 엧; ) HANGUL SYLLABLE EC
+C5E8;C5E8;110B 1166 11BF;C5E8;110B 1166 11BF; # (엨; 엨; 엨; 엨; 엨; ) HANGUL SYLLABLE EK
+C5E9;C5E9;110B 1166 11C0;C5E9;110B 1166 11C0; # (엩; 엩; 엩; 엩; 엩; ) HANGUL SYLLABLE ET
+C5EA;C5EA;110B 1166 11C1;C5EA;110B 1166 11C1; # (ì—ª; ì—ª; á„‹á…¦á‡; ì—ª; á„‹á…¦á‡; ) HANGUL SYLLABLE EP
+C5EB;C5EB;110B 1166 11C2;C5EB;110B 1166 11C2; # (엫; 엫; 엫; 엫; 엫; ) HANGUL SYLLABLE EH
+C5EC;C5EC;110B 1167;C5EC;110B 1167; # (ì—¬; ì—¬; á„‹á…§; ì—¬; á„‹á…§; ) HANGUL SYLLABLE YEO
+C5ED;C5ED;110B 1167 11A8;C5ED;110B 1167 11A8; # (역; 역; 역; 역; 역; ) HANGUL SYLLABLE YEOG
+C5EE;C5EE;110B 1167 11A9;C5EE;110B 1167 11A9; # (엮; 엮; 엮; 엮; 엮; ) HANGUL SYLLABLE YEOGG
+C5EF;C5EF;110B 1167 11AA;C5EF;110B 1167 11AA; # (엯; 엯; 엯; 엯; 엯; ) HANGUL SYLLABLE YEOGS
+C5F0;C5F0;110B 1167 11AB;C5F0;110B 1167 11AB; # (연; 연; 연; 연; 연; ) HANGUL SYLLABLE YEON
+C5F1;C5F1;110B 1167 11AC;C5F1;110B 1167 11AC; # (엱; 엱; 엱; 엱; 엱; ) HANGUL SYLLABLE YEONJ
+C5F2;C5F2;110B 1167 11AD;C5F2;110B 1167 11AD; # (엲; 엲; 엲; 엲; 엲; ) HANGUL SYLLABLE YEONH
+C5F3;C5F3;110B 1167 11AE;C5F3;110B 1167 11AE; # (엳; 엳; 엳; 엳; 엳; ) HANGUL SYLLABLE YEOD
+C5F4;C5F4;110B 1167 11AF;C5F4;110B 1167 11AF; # (열; 열; 열; 열; 열; ) HANGUL SYLLABLE YEOL
+C5F5;C5F5;110B 1167 11B0;C5F5;110B 1167 11B0; # (엵; 엵; 엵; 엵; 엵; ) HANGUL SYLLABLE YEOLG
+C5F6;C5F6;110B 1167 11B1;C5F6;110B 1167 11B1; # (엶; 엶; 엶; 엶; 엶; ) HANGUL SYLLABLE YEOLM
+C5F7;C5F7;110B 1167 11B2;C5F7;110B 1167 11B2; # (엷; 엷; 엷; 엷; 엷; ) HANGUL SYLLABLE YEOLB
+C5F8;C5F8;110B 1167 11B3;C5F8;110B 1167 11B3; # (엸; 엸; 엸; 엸; 엸; ) HANGUL SYLLABLE YEOLS
+C5F9;C5F9;110B 1167 11B4;C5F9;110B 1167 11B4; # (엹; 엹; 엹; 엹; 엹; ) HANGUL SYLLABLE YEOLT
+C5FA;C5FA;110B 1167 11B5;C5FA;110B 1167 11B5; # (엺; 엺; 엺; 엺; 엺; ) HANGUL SYLLABLE YEOLP
+C5FB;C5FB;110B 1167 11B6;C5FB;110B 1167 11B6; # (엻; 엻; 엻; 엻; 엻; ) HANGUL SYLLABLE YEOLH
+C5FC;C5FC;110B 1167 11B7;C5FC;110B 1167 11B7; # (염; 염; 염; 염; 염; ) HANGUL SYLLABLE YEOM
+C5FD;C5FD;110B 1167 11B8;C5FD;110B 1167 11B8; # (엽; 엽; 엽; 엽; 엽; ) HANGUL SYLLABLE YEOB
+C5FE;C5FE;110B 1167 11B9;C5FE;110B 1167 11B9; # (엾; 엾; 엾; 엾; 엾; ) HANGUL SYLLABLE YEOBS
+C5FF;C5FF;110B 1167 11BA;C5FF;110B 1167 11BA; # (엿; 엿; 엿; 엿; 엿; ) HANGUL SYLLABLE YEOS
+C600;C600;110B 1167 11BB;C600;110B 1167 11BB; # (였; 였; 였; 였; 였; ) HANGUL SYLLABLE YEOSS
+C601;C601;110B 1167 11BC;C601;110B 1167 11BC; # (ì˜; ì˜; 영; ì˜; 영; ) HANGUL SYLLABLE YEONG
+C602;C602;110B 1167 11BD;C602;110B 1167 11BD; # (옂; 옂; 옂; 옂; 옂; ) HANGUL SYLLABLE YEOJ
+C603;C603;110B 1167 11BE;C603;110B 1167 11BE; # (옃; 옃; 옃; 옃; 옃; ) HANGUL SYLLABLE YEOC
+C604;C604;110B 1167 11BF;C604;110B 1167 11BF; # (옄; 옄; 옄; 옄; 옄; ) HANGUL SYLLABLE YEOK
+C605;C605;110B 1167 11C0;C605;110B 1167 11C0; # (옅; 옅; 옅; 옅; 옅; ) HANGUL SYLLABLE YEOT
+C606;C606;110B 1167 11C1;C606;110B 1167 11C1; # (옆; 옆; á„‹á…§á‡; 옆; á„‹á…§á‡; ) HANGUL SYLLABLE YEOP
+C607;C607;110B 1167 11C2;C607;110B 1167 11C2; # (옇; 옇; 옇; 옇; 옇; ) HANGUL SYLLABLE YEOH
+C608;C608;110B 1168;C608;110B 1168; # (예; 예; 예; 예; 예; ) HANGUL SYLLABLE YE
+C609;C609;110B 1168 11A8;C609;110B 1168 11A8; # (옉; 옉; 옉; 옉; 옉; ) HANGUL SYLLABLE YEG
+C60A;C60A;110B 1168 11A9;C60A;110B 1168 11A9; # (옊; 옊; 옊; 옊; 옊; ) HANGUL SYLLABLE YEGG
+C60B;C60B;110B 1168 11AA;C60B;110B 1168 11AA; # (옋; 옋; 옋; 옋; 옋; ) HANGUL SYLLABLE YEGS
+C60C;C60C;110B 1168 11AB;C60C;110B 1168 11AB; # (옌; 옌; 옌; 옌; 옌; ) HANGUL SYLLABLE YEN
+C60D;C60D;110B 1168 11AC;C60D;110B 1168 11AC; # (ì˜; ì˜; 옍; ì˜; 옍; ) HANGUL SYLLABLE YENJ
+C60E;C60E;110B 1168 11AD;C60E;110B 1168 11AD; # (옎; 옎; 옎; 옎; 옎; ) HANGUL SYLLABLE YENH
+C60F;C60F;110B 1168 11AE;C60F;110B 1168 11AE; # (ì˜; ì˜; 옏; ì˜; 옏; ) HANGUL SYLLABLE YED
+C610;C610;110B 1168 11AF;C610;110B 1168 11AF; # (ì˜; ì˜; 옐; ì˜; 옐; ) HANGUL SYLLABLE YEL
+C611;C611;110B 1168 11B0;C611;110B 1168 11B0; # (옑; 옑; 옑; 옑; 옑; ) HANGUL SYLLABLE YELG
+C612;C612;110B 1168 11B1;C612;110B 1168 11B1; # (옒; 옒; 옒; 옒; 옒; ) HANGUL SYLLABLE YELM
+C613;C613;110B 1168 11B2;C613;110B 1168 11B2; # (옓; 옓; 옓; 옓; 옓; ) HANGUL SYLLABLE YELB
+C614;C614;110B 1168 11B3;C614;110B 1168 11B3; # (옔; 옔; 옔; 옔; 옔; ) HANGUL SYLLABLE YELS
+C615;C615;110B 1168 11B4;C615;110B 1168 11B4; # (옕; 옕; 옕; 옕; 옕; ) HANGUL SYLLABLE YELT
+C616;C616;110B 1168 11B5;C616;110B 1168 11B5; # (옖; 옖; 옖; 옖; 옖; ) HANGUL SYLLABLE YELP
+C617;C617;110B 1168 11B6;C617;110B 1168 11B6; # (옗; 옗; 옗; 옗; 옗; ) HANGUL SYLLABLE YELH
+C618;C618;110B 1168 11B7;C618;110B 1168 11B7; # (옘; 옘; 옘; 옘; 옘; ) HANGUL SYLLABLE YEM
+C619;C619;110B 1168 11B8;C619;110B 1168 11B8; # (옙; 옙; 옙; 옙; 옙; ) HANGUL SYLLABLE YEB
+C61A;C61A;110B 1168 11B9;C61A;110B 1168 11B9; # (옚; 옚; 옚; 옚; 옚; ) HANGUL SYLLABLE YEBS
+C61B;C61B;110B 1168 11BA;C61B;110B 1168 11BA; # (옛; 옛; 옛; 옛; 옛; ) HANGUL SYLLABLE YES
+C61C;C61C;110B 1168 11BB;C61C;110B 1168 11BB; # (옜; 옜; 옜; 옜; 옜; ) HANGUL SYLLABLE YESS
+C61D;C61D;110B 1168 11BC;C61D;110B 1168 11BC; # (ì˜; ì˜; 옝; ì˜; 옝; ) HANGUL SYLLABLE YENG
+C61E;C61E;110B 1168 11BD;C61E;110B 1168 11BD; # (옞; 옞; 옞; 옞; 옞; ) HANGUL SYLLABLE YEJ
+C61F;C61F;110B 1168 11BE;C61F;110B 1168 11BE; # (옟; 옟; 옟; 옟; 옟; ) HANGUL SYLLABLE YEC
+C620;C620;110B 1168 11BF;C620;110B 1168 11BF; # (옠; 옠; 옠; 옠; 옠; ) HANGUL SYLLABLE YEK
+C621;C621;110B 1168 11C0;C621;110B 1168 11C0; # (옡; 옡; 옡; 옡; 옡; ) HANGUL SYLLABLE YET
+C622;C622;110B 1168 11C1;C622;110B 1168 11C1; # (옢; 옢; á„‹á…¨á‡; 옢; á„‹á…¨á‡; ) HANGUL SYLLABLE YEP
+C623;C623;110B 1168 11C2;C623;110B 1168 11C2; # (옣; 옣; 옣; 옣; 옣; ) HANGUL SYLLABLE YEH
+C624;C624;110B 1169;C624;110B 1169; # (오; 오; 오; 오; 오; ) HANGUL SYLLABLE O
+C625;C625;110B 1169 11A8;C625;110B 1169 11A8; # (옥; 옥; 옥; 옥; 옥; ) HANGUL SYLLABLE OG
+C626;C626;110B 1169 11A9;C626;110B 1169 11A9; # (옦; 옦; 옦; 옦; 옦; ) HANGUL SYLLABLE OGG
+C627;C627;110B 1169 11AA;C627;110B 1169 11AA; # (옧; 옧; 옧; 옧; 옧; ) HANGUL SYLLABLE OGS
+C628;C628;110B 1169 11AB;C628;110B 1169 11AB; # (온; 온; 온; 온; 온; ) HANGUL SYLLABLE ON
+C629;C629;110B 1169 11AC;C629;110B 1169 11AC; # (옩; 옩; 옩; 옩; 옩; ) HANGUL SYLLABLE ONJ
+C62A;C62A;110B 1169 11AD;C62A;110B 1169 11AD; # (옪; 옪; 옪; 옪; 옪; ) HANGUL SYLLABLE ONH
+C62B;C62B;110B 1169 11AE;C62B;110B 1169 11AE; # (옫; 옫; 옫; 옫; 옫; ) HANGUL SYLLABLE OD
+C62C;C62C;110B 1169 11AF;C62C;110B 1169 11AF; # (올; 올; 올; 올; 올; ) HANGUL SYLLABLE OL
+C62D;C62D;110B 1169 11B0;C62D;110B 1169 11B0; # (옭; 옭; 옭; 옭; 옭; ) HANGUL SYLLABLE OLG
+C62E;C62E;110B 1169 11B1;C62E;110B 1169 11B1; # (옮; 옮; 옮; 옮; 옮; ) HANGUL SYLLABLE OLM
+C62F;C62F;110B 1169 11B2;C62F;110B 1169 11B2; # (옯; 옯; 옯; 옯; 옯; ) HANGUL SYLLABLE OLB
+C630;C630;110B 1169 11B3;C630;110B 1169 11B3; # (옰; 옰; 옰; 옰; 옰; ) HANGUL SYLLABLE OLS
+C631;C631;110B 1169 11B4;C631;110B 1169 11B4; # (옱; 옱; 옱; 옱; 옱; ) HANGUL SYLLABLE OLT
+C632;C632;110B 1169 11B5;C632;110B 1169 11B5; # (옲; 옲; 옲; 옲; 옲; ) HANGUL SYLLABLE OLP
+C633;C633;110B 1169 11B6;C633;110B 1169 11B6; # (옳; 옳; 옳; 옳; 옳; ) HANGUL SYLLABLE OLH
+C634;C634;110B 1169 11B7;C634;110B 1169 11B7; # (옴; 옴; 옴; 옴; 옴; ) HANGUL SYLLABLE OM
+C635;C635;110B 1169 11B8;C635;110B 1169 11B8; # (옵; 옵; 옵; 옵; 옵; ) HANGUL SYLLABLE OB
+C636;C636;110B 1169 11B9;C636;110B 1169 11B9; # (옶; 옶; 옶; 옶; 옶; ) HANGUL SYLLABLE OBS
+C637;C637;110B 1169 11BA;C637;110B 1169 11BA; # (옷; 옷; 옷; 옷; 옷; ) HANGUL SYLLABLE OS
+C638;C638;110B 1169 11BB;C638;110B 1169 11BB; # (옸; 옸; 옸; 옸; 옸; ) HANGUL SYLLABLE OSS
+C639;C639;110B 1169 11BC;C639;110B 1169 11BC; # (옹; 옹; 옹; 옹; 옹; ) HANGUL SYLLABLE ONG
+C63A;C63A;110B 1169 11BD;C63A;110B 1169 11BD; # (옺; 옺; 옺; 옺; 옺; ) HANGUL SYLLABLE OJ
+C63B;C63B;110B 1169 11BE;C63B;110B 1169 11BE; # (옻; 옻; 옻; 옻; 옻; ) HANGUL SYLLABLE OC
+C63C;C63C;110B 1169 11BF;C63C;110B 1169 11BF; # (옼; 옼; 옼; 옼; 옼; ) HANGUL SYLLABLE OK
+C63D;C63D;110B 1169 11C0;C63D;110B 1169 11C0; # (옽; 옽; 옽; 옽; 옽; ) HANGUL SYLLABLE OT
+C63E;C63E;110B 1169 11C1;C63E;110B 1169 11C1; # (옾; 옾; á„‹á…©á‡; 옾; á„‹á…©á‡; ) HANGUL SYLLABLE OP
+C63F;C63F;110B 1169 11C2;C63F;110B 1169 11C2; # (옿; 옿; 옿; 옿; 옿; ) HANGUL SYLLABLE OH
+C640;C640;110B 116A;C640;110B 116A; # (와; 와; 와; 와; 와; ) HANGUL SYLLABLE WA
+C641;C641;110B 116A 11A8;C641;110B 116A 11A8; # (ì™; ì™; 왁; ì™; 왁; ) HANGUL SYLLABLE WAG
+C642;C642;110B 116A 11A9;C642;110B 116A 11A9; # (왂; 왂; 왂; 왂; 왂; ) HANGUL SYLLABLE WAGG
+C643;C643;110B 116A 11AA;C643;110B 116A 11AA; # (왃; 왃; 왃; 왃; 왃; ) HANGUL SYLLABLE WAGS
+C644;C644;110B 116A 11AB;C644;110B 116A 11AB; # (완; 완; 완; 완; 완; ) HANGUL SYLLABLE WAN
+C645;C645;110B 116A 11AC;C645;110B 116A 11AC; # (왅; 왅; 왅; 왅; 왅; ) HANGUL SYLLABLE WANJ
+C646;C646;110B 116A 11AD;C646;110B 116A 11AD; # (왆; 왆; 왆; 왆; 왆; ) HANGUL SYLLABLE WANH
+C647;C647;110B 116A 11AE;C647;110B 116A 11AE; # (왇; 왇; 왇; 왇; 왇; ) HANGUL SYLLABLE WAD
+C648;C648;110B 116A 11AF;C648;110B 116A 11AF; # (왈; 왈; 왈; 왈; 왈; ) HANGUL SYLLABLE WAL
+C649;C649;110B 116A 11B0;C649;110B 116A 11B0; # (왉; 왉; 왉; 왉; 왉; ) HANGUL SYLLABLE WALG
+C64A;C64A;110B 116A 11B1;C64A;110B 116A 11B1; # (왊; 왊; 왊; 왊; 왊; ) HANGUL SYLLABLE WALM
+C64B;C64B;110B 116A 11B2;C64B;110B 116A 11B2; # (왋; 왋; 왋; 왋; 왋; ) HANGUL SYLLABLE WALB
+C64C;C64C;110B 116A 11B3;C64C;110B 116A 11B3; # (왌; 왌; 왌; 왌; 왌; ) HANGUL SYLLABLE WALS
+C64D;C64D;110B 116A 11B4;C64D;110B 116A 11B4; # (ì™; ì™; 왍; ì™; 왍; ) HANGUL SYLLABLE WALT
+C64E;C64E;110B 116A 11B5;C64E;110B 116A 11B5; # (왎; 왎; 왎; 왎; 왎; ) HANGUL SYLLABLE WALP
+C64F;C64F;110B 116A 11B6;C64F;110B 116A 11B6; # (ì™; ì™; 왏; ì™; 왏; ) HANGUL SYLLABLE WALH
+C650;C650;110B 116A 11B7;C650;110B 116A 11B7; # (ì™; ì™; 왐; ì™; 왐; ) HANGUL SYLLABLE WAM
+C651;C651;110B 116A 11B8;C651;110B 116A 11B8; # (왑; 왑; 왑; 왑; 왑; ) HANGUL SYLLABLE WAB
+C652;C652;110B 116A 11B9;C652;110B 116A 11B9; # (왒; 왒; 왒; 왒; 왒; ) HANGUL SYLLABLE WABS
+C653;C653;110B 116A 11BA;C653;110B 116A 11BA; # (왓; 왓; 왓; 왓; 왓; ) HANGUL SYLLABLE WAS
+C654;C654;110B 116A 11BB;C654;110B 116A 11BB; # (왔; 왔; 왔; 왔; 왔; ) HANGUL SYLLABLE WASS
+C655;C655;110B 116A 11BC;C655;110B 116A 11BC; # (왕; 왕; 왕; 왕; 왕; ) HANGUL SYLLABLE WANG
+C656;C656;110B 116A 11BD;C656;110B 116A 11BD; # (왖; 왖; 왖; 왖; 왖; ) HANGUL SYLLABLE WAJ
+C657;C657;110B 116A 11BE;C657;110B 116A 11BE; # (왗; 왗; 왗; 왗; 왗; ) HANGUL SYLLABLE WAC
+C658;C658;110B 116A 11BF;C658;110B 116A 11BF; # (왘; 왘; 왘; 왘; 왘; ) HANGUL SYLLABLE WAK
+C659;C659;110B 116A 11C0;C659;110B 116A 11C0; # (왙; 왙; 왙; 왙; 왙; ) HANGUL SYLLABLE WAT
+C65A;C65A;110B 116A 11C1;C65A;110B 116A 11C1; # (왚; 왚; á„‹á…ªá‡; 왚; á„‹á…ªá‡; ) HANGUL SYLLABLE WAP
+C65B;C65B;110B 116A 11C2;C65B;110B 116A 11C2; # (왛; 왛; 왛; 왛; 왛; ) HANGUL SYLLABLE WAH
+C65C;C65C;110B 116B;C65C;110B 116B; # (왜; 왜; 왜; 왜; 왜; ) HANGUL SYLLABLE WAE
+C65D;C65D;110B 116B 11A8;C65D;110B 116B 11A8; # (ì™; ì™; 왝; ì™; 왝; ) HANGUL SYLLABLE WAEG
+C65E;C65E;110B 116B 11A9;C65E;110B 116B 11A9; # (왞; 왞; 왞; 왞; 왞; ) HANGUL SYLLABLE WAEGG
+C65F;C65F;110B 116B 11AA;C65F;110B 116B 11AA; # (왟; 왟; 왟; 왟; 왟; ) HANGUL SYLLABLE WAEGS
+C660;C660;110B 116B 11AB;C660;110B 116B 11AB; # (왠; 왠; 왠; 왠; 왠; ) HANGUL SYLLABLE WAEN
+C661;C661;110B 116B 11AC;C661;110B 116B 11AC; # (왡; 왡; 왡; 왡; 왡; ) HANGUL SYLLABLE WAENJ
+C662;C662;110B 116B 11AD;C662;110B 116B 11AD; # (왢; 왢; 왢; 왢; 왢; ) HANGUL SYLLABLE WAENH
+C663;C663;110B 116B 11AE;C663;110B 116B 11AE; # (왣; 왣; 왣; 왣; 왣; ) HANGUL SYLLABLE WAED
+C664;C664;110B 116B 11AF;C664;110B 116B 11AF; # (왤; 왤; 왤; 왤; 왤; ) HANGUL SYLLABLE WAEL
+C665;C665;110B 116B 11B0;C665;110B 116B 11B0; # (왥; 왥; 왥; 왥; 왥; ) HANGUL SYLLABLE WAELG
+C666;C666;110B 116B 11B1;C666;110B 116B 11B1; # (왦; 왦; 왦; 왦; 왦; ) HANGUL SYLLABLE WAELM
+C667;C667;110B 116B 11B2;C667;110B 116B 11B2; # (왧; 왧; 왧; 왧; 왧; ) HANGUL SYLLABLE WAELB
+C668;C668;110B 116B 11B3;C668;110B 116B 11B3; # (왨; 왨; 왨; 왨; 왨; ) HANGUL SYLLABLE WAELS
+C669;C669;110B 116B 11B4;C669;110B 116B 11B4; # (왩; 왩; 왩; 왩; 왩; ) HANGUL SYLLABLE WAELT
+C66A;C66A;110B 116B 11B5;C66A;110B 116B 11B5; # (왪; 왪; 왪; 왪; 왪; ) HANGUL SYLLABLE WAELP
+C66B;C66B;110B 116B 11B6;C66B;110B 116B 11B6; # (왫; 왫; 왫; 왫; 왫; ) HANGUL SYLLABLE WAELH
+C66C;C66C;110B 116B 11B7;C66C;110B 116B 11B7; # (왬; 왬; 왬; 왬; 왬; ) HANGUL SYLLABLE WAEM
+C66D;C66D;110B 116B 11B8;C66D;110B 116B 11B8; # (왭; 왭; 왭; 왭; 왭; ) HANGUL SYLLABLE WAEB
+C66E;C66E;110B 116B 11B9;C66E;110B 116B 11B9; # (왮; 왮; 왮; 왮; 왮; ) HANGUL SYLLABLE WAEBS
+C66F;C66F;110B 116B 11BA;C66F;110B 116B 11BA; # (왯; 왯; 왯; 왯; 왯; ) HANGUL SYLLABLE WAES
+C670;C670;110B 116B 11BB;C670;110B 116B 11BB; # (왰; 왰; 왰; 왰; 왰; ) HANGUL SYLLABLE WAESS
+C671;C671;110B 116B 11BC;C671;110B 116B 11BC; # (왱; 왱; 왱; 왱; 왱; ) HANGUL SYLLABLE WAENG
+C672;C672;110B 116B 11BD;C672;110B 116B 11BD; # (왲; 왲; 왲; 왲; 왲; ) HANGUL SYLLABLE WAEJ
+C673;C673;110B 116B 11BE;C673;110B 116B 11BE; # (왳; 왳; 왳; 왳; 왳; ) HANGUL SYLLABLE WAEC
+C674;C674;110B 116B 11BF;C674;110B 116B 11BF; # (왴; 왴; 왴; 왴; 왴; ) HANGUL SYLLABLE WAEK
+C675;C675;110B 116B 11C0;C675;110B 116B 11C0; # (왵; 왵; 왵; 왵; 왵; ) HANGUL SYLLABLE WAET
+C676;C676;110B 116B 11C1;C676;110B 116B 11C1; # (ì™¶; ì™¶; á„‹á…«á‡; ì™¶; á„‹á…«á‡; ) HANGUL SYLLABLE WAEP
+C677;C677;110B 116B 11C2;C677;110B 116B 11C2; # (왷; 왷; 왷; 왷; 왷; ) HANGUL SYLLABLE WAEH
+C678;C678;110B 116C;C678;110B 116C; # (외; 외; 외; 외; 외; ) HANGUL SYLLABLE OE
+C679;C679;110B 116C 11A8;C679;110B 116C 11A8; # (왹; 왹; 왹; 왹; 왹; ) HANGUL SYLLABLE OEG
+C67A;C67A;110B 116C 11A9;C67A;110B 116C 11A9; # (왺; 왺; 왺; 왺; 왺; ) HANGUL SYLLABLE OEGG
+C67B;C67B;110B 116C 11AA;C67B;110B 116C 11AA; # (왻; 왻; 왻; 왻; 왻; ) HANGUL SYLLABLE OEGS
+C67C;C67C;110B 116C 11AB;C67C;110B 116C 11AB; # (왼; 왼; 왼; 왼; 왼; ) HANGUL SYLLABLE OEN
+C67D;C67D;110B 116C 11AC;C67D;110B 116C 11AC; # (왽; 왽; 왽; 왽; 왽; ) HANGUL SYLLABLE OENJ
+C67E;C67E;110B 116C 11AD;C67E;110B 116C 11AD; # (왾; 왾; 왾; 왾; 왾; ) HANGUL SYLLABLE OENH
+C67F;C67F;110B 116C 11AE;C67F;110B 116C 11AE; # (왿; 왿; 왿; 왿; 왿; ) HANGUL SYLLABLE OED
+C680;C680;110B 116C 11AF;C680;110B 116C 11AF; # (욀; 욀; 욀; 욀; 욀; ) HANGUL SYLLABLE OEL
+C681;C681;110B 116C 11B0;C681;110B 116C 11B0; # (ìš; ìš; 욁; ìš; 욁; ) HANGUL SYLLABLE OELG
+C682;C682;110B 116C 11B1;C682;110B 116C 11B1; # (욂; 욂; 욂; 욂; 욂; ) HANGUL SYLLABLE OELM
+C683;C683;110B 116C 11B2;C683;110B 116C 11B2; # (욃; 욃; 욃; 욃; 욃; ) HANGUL SYLLABLE OELB
+C684;C684;110B 116C 11B3;C684;110B 116C 11B3; # (욄; 욄; 욄; 욄; 욄; ) HANGUL SYLLABLE OELS
+C685;C685;110B 116C 11B4;C685;110B 116C 11B4; # (욅; 욅; 욅; 욅; 욅; ) HANGUL SYLLABLE OELT
+C686;C686;110B 116C 11B5;C686;110B 116C 11B5; # (욆; 욆; 욆; 욆; 욆; ) HANGUL SYLLABLE OELP
+C687;C687;110B 116C 11B6;C687;110B 116C 11B6; # (욇; 욇; 욇; 욇; 욇; ) HANGUL SYLLABLE OELH
+C688;C688;110B 116C 11B7;C688;110B 116C 11B7; # (욈; 욈; 욈; 욈; 욈; ) HANGUL SYLLABLE OEM
+C689;C689;110B 116C 11B8;C689;110B 116C 11B8; # (욉; 욉; 욉; 욉; 욉; ) HANGUL SYLLABLE OEB
+C68A;C68A;110B 116C 11B9;C68A;110B 116C 11B9; # (욊; 욊; 욊; 욊; 욊; ) HANGUL SYLLABLE OEBS
+C68B;C68B;110B 116C 11BA;C68B;110B 116C 11BA; # (욋; 욋; 욋; 욋; 욋; ) HANGUL SYLLABLE OES
+C68C;C68C;110B 116C 11BB;C68C;110B 116C 11BB; # (욌; 욌; 욌; 욌; 욌; ) HANGUL SYLLABLE OESS
+C68D;C68D;110B 116C 11BC;C68D;110B 116C 11BC; # (ìš; ìš; 욍; ìš; 욍; ) HANGUL SYLLABLE OENG
+C68E;C68E;110B 116C 11BD;C68E;110B 116C 11BD; # (욎; 욎; 욎; 욎; 욎; ) HANGUL SYLLABLE OEJ
+C68F;C68F;110B 116C 11BE;C68F;110B 116C 11BE; # (ìš; ìš; 욏; ìš; 욏; ) HANGUL SYLLABLE OEC
+C690;C690;110B 116C 11BF;C690;110B 116C 11BF; # (ìš; ìš; 욐; ìš; 욐; ) HANGUL SYLLABLE OEK
+C691;C691;110B 116C 11C0;C691;110B 116C 11C0; # (욑; 욑; 욑; 욑; 욑; ) HANGUL SYLLABLE OET
+C692;C692;110B 116C 11C1;C692;110B 116C 11C1; # (ìš’; ìš’; á„‹á…¬á‡; ìš’; á„‹á…¬á‡; ) HANGUL SYLLABLE OEP
+C693;C693;110B 116C 11C2;C693;110B 116C 11C2; # (욓; 욓; 욓; 욓; 욓; ) HANGUL SYLLABLE OEH
+C694;C694;110B 116D;C694;110B 116D; # (ìš”; ìš”; á„‹á…­; ìš”; á„‹á…­; ) HANGUL SYLLABLE YO
+C695;C695;110B 116D 11A8;C695;110B 116D 11A8; # (욕; 욕; 욕; 욕; 욕; ) HANGUL SYLLABLE YOG
+C696;C696;110B 116D 11A9;C696;110B 116D 11A9; # (욖; 욖; 욖; 욖; 욖; ) HANGUL SYLLABLE YOGG
+C697;C697;110B 116D 11AA;C697;110B 116D 11AA; # (욗; 욗; 욗; 욗; 욗; ) HANGUL SYLLABLE YOGS
+C698;C698;110B 116D 11AB;C698;110B 116D 11AB; # (욘; 욘; 욘; 욘; 욘; ) HANGUL SYLLABLE YON
+C699;C699;110B 116D 11AC;C699;110B 116D 11AC; # (욙; 욙; 욙; 욙; 욙; ) HANGUL SYLLABLE YONJ
+C69A;C69A;110B 116D 11AD;C69A;110B 116D 11AD; # (욚; 욚; 욚; 욚; 욚; ) HANGUL SYLLABLE YONH
+C69B;C69B;110B 116D 11AE;C69B;110B 116D 11AE; # (욛; 욛; 욛; 욛; 욛; ) HANGUL SYLLABLE YOD
+C69C;C69C;110B 116D 11AF;C69C;110B 116D 11AF; # (욜; 욜; 욜; 욜; 욜; ) HANGUL SYLLABLE YOL
+C69D;C69D;110B 116D 11B0;C69D;110B 116D 11B0; # (ìš; ìš; 욝; ìš; 욝; ) HANGUL SYLLABLE YOLG
+C69E;C69E;110B 116D 11B1;C69E;110B 116D 11B1; # (욞; 욞; 욞; 욞; 욞; ) HANGUL SYLLABLE YOLM
+C69F;C69F;110B 116D 11B2;C69F;110B 116D 11B2; # (욟; 욟; 욟; 욟; 욟; ) HANGUL SYLLABLE YOLB
+C6A0;C6A0;110B 116D 11B3;C6A0;110B 116D 11B3; # (욠; 욠; 욠; 욠; 욠; ) HANGUL SYLLABLE YOLS
+C6A1;C6A1;110B 116D 11B4;C6A1;110B 116D 11B4; # (욡; 욡; 욡; 욡; 욡; ) HANGUL SYLLABLE YOLT
+C6A2;C6A2;110B 116D 11B5;C6A2;110B 116D 11B5; # (욢; 욢; 욢; 욢; 욢; ) HANGUL SYLLABLE YOLP
+C6A3;C6A3;110B 116D 11B6;C6A3;110B 116D 11B6; # (욣; 욣; 욣; 욣; 욣; ) HANGUL SYLLABLE YOLH
+C6A4;C6A4;110B 116D 11B7;C6A4;110B 116D 11B7; # (욤; 욤; 욤; 욤; 욤; ) HANGUL SYLLABLE YOM
+C6A5;C6A5;110B 116D 11B8;C6A5;110B 116D 11B8; # (욥; 욥; 욥; 욥; 욥; ) HANGUL SYLLABLE YOB
+C6A6;C6A6;110B 116D 11B9;C6A6;110B 116D 11B9; # (욦; 욦; 욦; 욦; 욦; ) HANGUL SYLLABLE YOBS
+C6A7;C6A7;110B 116D 11BA;C6A7;110B 116D 11BA; # (욧; 욧; 욧; 욧; 욧; ) HANGUL SYLLABLE YOS
+C6A8;C6A8;110B 116D 11BB;C6A8;110B 116D 11BB; # (욨; 욨; 욨; 욨; 욨; ) HANGUL SYLLABLE YOSS
+C6A9;C6A9;110B 116D 11BC;C6A9;110B 116D 11BC; # (용; 용; 용; 용; 용; ) HANGUL SYLLABLE YONG
+C6AA;C6AA;110B 116D 11BD;C6AA;110B 116D 11BD; # (욪; 욪; 욪; 욪; 욪; ) HANGUL SYLLABLE YOJ
+C6AB;C6AB;110B 116D 11BE;C6AB;110B 116D 11BE; # (욫; 욫; 욫; 욫; 욫; ) HANGUL SYLLABLE YOC
+C6AC;C6AC;110B 116D 11BF;C6AC;110B 116D 11BF; # (욬; 욬; 욬; 욬; 욬; ) HANGUL SYLLABLE YOK
+C6AD;C6AD;110B 116D 11C0;C6AD;110B 116D 11C0; # (욭; 욭; 욭; 욭; 욭; ) HANGUL SYLLABLE YOT
+C6AE;C6AE;110B 116D 11C1;C6AE;110B 116D 11C1; # (ìš®; ìš®; á„‹á…­á‡; ìš®; á„‹á…­á‡; ) HANGUL SYLLABLE YOP
+C6AF;C6AF;110B 116D 11C2;C6AF;110B 116D 11C2; # (욯; 욯; 욯; 욯; 욯; ) HANGUL SYLLABLE YOH
+C6B0;C6B0;110B 116E;C6B0;110B 116E; # (ìš°; ìš°; á„‹á…®; ìš°; á„‹á…®; ) HANGUL SYLLABLE U
+C6B1;C6B1;110B 116E 11A8;C6B1;110B 116E 11A8; # (욱; 욱; 욱; 욱; 욱; ) HANGUL SYLLABLE UG
+C6B2;C6B2;110B 116E 11A9;C6B2;110B 116E 11A9; # (욲; 욲; 욲; 욲; 욲; ) HANGUL SYLLABLE UGG
+C6B3;C6B3;110B 116E 11AA;C6B3;110B 116E 11AA; # (욳; 욳; 욳; 욳; 욳; ) HANGUL SYLLABLE UGS
+C6B4;C6B4;110B 116E 11AB;C6B4;110B 116E 11AB; # (운; 운; 운; 운; 운; ) HANGUL SYLLABLE UN
+C6B5;C6B5;110B 116E 11AC;C6B5;110B 116E 11AC; # (욵; 욵; 욵; 욵; 욵; ) HANGUL SYLLABLE UNJ
+C6B6;C6B6;110B 116E 11AD;C6B6;110B 116E 11AD; # (욶; 욶; 욶; 욶; 욶; ) HANGUL SYLLABLE UNH
+C6B7;C6B7;110B 116E 11AE;C6B7;110B 116E 11AE; # (욷; 욷; 욷; 욷; 욷; ) HANGUL SYLLABLE UD
+C6B8;C6B8;110B 116E 11AF;C6B8;110B 116E 11AF; # (울; 울; 울; 울; 울; ) HANGUL SYLLABLE UL
+C6B9;C6B9;110B 116E 11B0;C6B9;110B 116E 11B0; # (욹; 욹; 욹; 욹; 욹; ) HANGUL SYLLABLE ULG
+C6BA;C6BA;110B 116E 11B1;C6BA;110B 116E 11B1; # (욺; 욺; 욺; 욺; 욺; ) HANGUL SYLLABLE ULM
+C6BB;C6BB;110B 116E 11B2;C6BB;110B 116E 11B2; # (욻; 욻; 욻; 욻; 욻; ) HANGUL SYLLABLE ULB
+C6BC;C6BC;110B 116E 11B3;C6BC;110B 116E 11B3; # (욼; 욼; 욼; 욼; 욼; ) HANGUL SYLLABLE ULS
+C6BD;C6BD;110B 116E 11B4;C6BD;110B 116E 11B4; # (욽; 욽; 욽; 욽; 욽; ) HANGUL SYLLABLE ULT
+C6BE;C6BE;110B 116E 11B5;C6BE;110B 116E 11B5; # (욾; 욾; 욾; 욾; 욾; ) HANGUL SYLLABLE ULP
+C6BF;C6BF;110B 116E 11B6;C6BF;110B 116E 11B6; # (욿; 욿; 욿; 욿; 욿; ) HANGUL SYLLABLE ULH
+C6C0;C6C0;110B 116E 11B7;C6C0;110B 116E 11B7; # (움; 움; 움; 움; 움; ) HANGUL SYLLABLE UM
+C6C1;C6C1;110B 116E 11B8;C6C1;110B 116E 11B8; # (ì›; ì›; 웁; ì›; 웁; ) HANGUL SYLLABLE UB
+C6C2;C6C2;110B 116E 11B9;C6C2;110B 116E 11B9; # (웂; 웂; 웂; 웂; 웂; ) HANGUL SYLLABLE UBS
+C6C3;C6C3;110B 116E 11BA;C6C3;110B 116E 11BA; # (웃; 웃; 웃; 웃; 웃; ) HANGUL SYLLABLE US
+C6C4;C6C4;110B 116E 11BB;C6C4;110B 116E 11BB; # (웄; 웄; 웄; 웄; 웄; ) HANGUL SYLLABLE USS
+C6C5;C6C5;110B 116E 11BC;C6C5;110B 116E 11BC; # (웅; 웅; 웅; 웅; 웅; ) HANGUL SYLLABLE UNG
+C6C6;C6C6;110B 116E 11BD;C6C6;110B 116E 11BD; # (웆; 웆; 웆; 웆; 웆; ) HANGUL SYLLABLE UJ
+C6C7;C6C7;110B 116E 11BE;C6C7;110B 116E 11BE; # (웇; 웇; 웇; 웇; 웇; ) HANGUL SYLLABLE UC
+C6C8;C6C8;110B 116E 11BF;C6C8;110B 116E 11BF; # (웈; 웈; 웈; 웈; 웈; ) HANGUL SYLLABLE UK
+C6C9;C6C9;110B 116E 11C0;C6C9;110B 116E 11C0; # (웉; 웉; 웉; 웉; 웉; ) HANGUL SYLLABLE UT
+C6CA;C6CA;110B 116E 11C1;C6CA;110B 116E 11C1; # (웊; 웊; á„‹á…®á‡; 웊; á„‹á…®á‡; ) HANGUL SYLLABLE UP
+C6CB;C6CB;110B 116E 11C2;C6CB;110B 116E 11C2; # (웋; 웋; 웋; 웋; 웋; ) HANGUL SYLLABLE UH
+C6CC;C6CC;110B 116F;C6CC;110B 116F; # (워; 워; 워; 워; 워; ) HANGUL SYLLABLE WEO
+C6CD;C6CD;110B 116F 11A8;C6CD;110B 116F 11A8; # (ì›; ì›; 웍; ì›; 웍; ) HANGUL SYLLABLE WEOG
+C6CE;C6CE;110B 116F 11A9;C6CE;110B 116F 11A9; # (웎; 웎; 웎; 웎; 웎; ) HANGUL SYLLABLE WEOGG
+C6CF;C6CF;110B 116F 11AA;C6CF;110B 116F 11AA; # (ì›; ì›; 웏; ì›; 웏; ) HANGUL SYLLABLE WEOGS
+C6D0;C6D0;110B 116F 11AB;C6D0;110B 116F 11AB; # (ì›; ì›; 원; ì›; 원; ) HANGUL SYLLABLE WEON
+C6D1;C6D1;110B 116F 11AC;C6D1;110B 116F 11AC; # (웑; 웑; 웑; 웑; 웑; ) HANGUL SYLLABLE WEONJ
+C6D2;C6D2;110B 116F 11AD;C6D2;110B 116F 11AD; # (웒; 웒; 웒; 웒; 웒; ) HANGUL SYLLABLE WEONH
+C6D3;C6D3;110B 116F 11AE;C6D3;110B 116F 11AE; # (웓; 웓; 웓; 웓; 웓; ) HANGUL SYLLABLE WEOD
+C6D4;C6D4;110B 116F 11AF;C6D4;110B 116F 11AF; # (월; 월; 월; 월; 월; ) HANGUL SYLLABLE WEOL
+C6D5;C6D5;110B 116F 11B0;C6D5;110B 116F 11B0; # (웕; 웕; 웕; 웕; 웕; ) HANGUL SYLLABLE WEOLG
+C6D6;C6D6;110B 116F 11B1;C6D6;110B 116F 11B1; # (웖; 웖; 웖; 웖; 웖; ) HANGUL SYLLABLE WEOLM
+C6D7;C6D7;110B 116F 11B2;C6D7;110B 116F 11B2; # (웗; 웗; 웗; 웗; 웗; ) HANGUL SYLLABLE WEOLB
+C6D8;C6D8;110B 116F 11B3;C6D8;110B 116F 11B3; # (웘; 웘; 웘; 웘; 웘; ) HANGUL SYLLABLE WEOLS
+C6D9;C6D9;110B 116F 11B4;C6D9;110B 116F 11B4; # (웙; 웙; 웙; 웙; 웙; ) HANGUL SYLLABLE WEOLT
+C6DA;C6DA;110B 116F 11B5;C6DA;110B 116F 11B5; # (웚; 웚; 웚; 웚; 웚; ) HANGUL SYLLABLE WEOLP
+C6DB;C6DB;110B 116F 11B6;C6DB;110B 116F 11B6; # (웛; 웛; 웛; 웛; 웛; ) HANGUL SYLLABLE WEOLH
+C6DC;C6DC;110B 116F 11B7;C6DC;110B 116F 11B7; # (웜; 웜; 웜; 웜; 웜; ) HANGUL SYLLABLE WEOM
+C6DD;C6DD;110B 116F 11B8;C6DD;110B 116F 11B8; # (ì›; ì›; 웝; ì›; 웝; ) HANGUL SYLLABLE WEOB
+C6DE;C6DE;110B 116F 11B9;C6DE;110B 116F 11B9; # (웞; 웞; 웞; 웞; 웞; ) HANGUL SYLLABLE WEOBS
+C6DF;C6DF;110B 116F 11BA;C6DF;110B 116F 11BA; # (웟; 웟; 웟; 웟; 웟; ) HANGUL SYLLABLE WEOS
+C6E0;C6E0;110B 116F 11BB;C6E0;110B 116F 11BB; # (웠; 웠; 웠; 웠; 웠; ) HANGUL SYLLABLE WEOSS
+C6E1;C6E1;110B 116F 11BC;C6E1;110B 116F 11BC; # (웡; 웡; 웡; 웡; 웡; ) HANGUL SYLLABLE WEONG
+C6E2;C6E2;110B 116F 11BD;C6E2;110B 116F 11BD; # (웢; 웢; 웢; 웢; 웢; ) HANGUL SYLLABLE WEOJ
+C6E3;C6E3;110B 116F 11BE;C6E3;110B 116F 11BE; # (웣; 웣; 웣; 웣; 웣; ) HANGUL SYLLABLE WEOC
+C6E4;C6E4;110B 116F 11BF;C6E4;110B 116F 11BF; # (웤; 웤; 웤; 웤; 웤; ) HANGUL SYLLABLE WEOK
+C6E5;C6E5;110B 116F 11C0;C6E5;110B 116F 11C0; # (웥; 웥; 웥; 웥; 웥; ) HANGUL SYLLABLE WEOT
+C6E6;C6E6;110B 116F 11C1;C6E6;110B 116F 11C1; # (웦; 웦; á„‹á…¯á‡; 웦; á„‹á…¯á‡; ) HANGUL SYLLABLE WEOP
+C6E7;C6E7;110B 116F 11C2;C6E7;110B 116F 11C2; # (웧; 웧; 웧; 웧; 웧; ) HANGUL SYLLABLE WEOH
+C6E8;C6E8;110B 1170;C6E8;110B 1170; # (웨; 웨; 웨; 웨; 웨; ) HANGUL SYLLABLE WE
+C6E9;C6E9;110B 1170 11A8;C6E9;110B 1170 11A8; # (웩; 웩; 웩; 웩; 웩; ) HANGUL SYLLABLE WEG
+C6EA;C6EA;110B 1170 11A9;C6EA;110B 1170 11A9; # (웪; 웪; 웪; 웪; 웪; ) HANGUL SYLLABLE WEGG
+C6EB;C6EB;110B 1170 11AA;C6EB;110B 1170 11AA; # (웫; 웫; 웫; 웫; 웫; ) HANGUL SYLLABLE WEGS
+C6EC;C6EC;110B 1170 11AB;C6EC;110B 1170 11AB; # (웬; 웬; 웬; 웬; 웬; ) HANGUL SYLLABLE WEN
+C6ED;C6ED;110B 1170 11AC;C6ED;110B 1170 11AC; # (웭; 웭; 웭; 웭; 웭; ) HANGUL SYLLABLE WENJ
+C6EE;C6EE;110B 1170 11AD;C6EE;110B 1170 11AD; # (웮; 웮; 웮; 웮; 웮; ) HANGUL SYLLABLE WENH
+C6EF;C6EF;110B 1170 11AE;C6EF;110B 1170 11AE; # (웯; 웯; 웯; 웯; 웯; ) HANGUL SYLLABLE WED
+C6F0;C6F0;110B 1170 11AF;C6F0;110B 1170 11AF; # (웰; 웰; 웰; 웰; 웰; ) HANGUL SYLLABLE WEL
+C6F1;C6F1;110B 1170 11B0;C6F1;110B 1170 11B0; # (웱; 웱; 웱; 웱; 웱; ) HANGUL SYLLABLE WELG
+C6F2;C6F2;110B 1170 11B1;C6F2;110B 1170 11B1; # (웲; 웲; 웲; 웲; 웲; ) HANGUL SYLLABLE WELM
+C6F3;C6F3;110B 1170 11B2;C6F3;110B 1170 11B2; # (웳; 웳; 웳; 웳; 웳; ) HANGUL SYLLABLE WELB
+C6F4;C6F4;110B 1170 11B3;C6F4;110B 1170 11B3; # (웴; 웴; 웴; 웴; 웴; ) HANGUL SYLLABLE WELS
+C6F5;C6F5;110B 1170 11B4;C6F5;110B 1170 11B4; # (웵; 웵; 웵; 웵; 웵; ) HANGUL SYLLABLE WELT
+C6F6;C6F6;110B 1170 11B5;C6F6;110B 1170 11B5; # (웶; 웶; 웶; 웶; 웶; ) HANGUL SYLLABLE WELP
+C6F7;C6F7;110B 1170 11B6;C6F7;110B 1170 11B6; # (웷; 웷; 웷; 웷; 웷; ) HANGUL SYLLABLE WELH
+C6F8;C6F8;110B 1170 11B7;C6F8;110B 1170 11B7; # (웸; 웸; 웸; 웸; 웸; ) HANGUL SYLLABLE WEM
+C6F9;C6F9;110B 1170 11B8;C6F9;110B 1170 11B8; # (웹; 웹; 웹; 웹; 웹; ) HANGUL SYLLABLE WEB
+C6FA;C6FA;110B 1170 11B9;C6FA;110B 1170 11B9; # (웺; 웺; 웺; 웺; 웺; ) HANGUL SYLLABLE WEBS
+C6FB;C6FB;110B 1170 11BA;C6FB;110B 1170 11BA; # (웻; 웻; 웻; 웻; 웻; ) HANGUL SYLLABLE WES
+C6FC;C6FC;110B 1170 11BB;C6FC;110B 1170 11BB; # (웼; 웼; 웼; 웼; 웼; ) HANGUL SYLLABLE WESS
+C6FD;C6FD;110B 1170 11BC;C6FD;110B 1170 11BC; # (웽; 웽; 웽; 웽; 웽; ) HANGUL SYLLABLE WENG
+C6FE;C6FE;110B 1170 11BD;C6FE;110B 1170 11BD; # (웾; 웾; 웾; 웾; 웾; ) HANGUL SYLLABLE WEJ
+C6FF;C6FF;110B 1170 11BE;C6FF;110B 1170 11BE; # (웿; 웿; 웿; 웿; 웿; ) HANGUL SYLLABLE WEC
+C700;C700;110B 1170 11BF;C700;110B 1170 11BF; # (윀; 윀; 윀; 윀; 윀; ) HANGUL SYLLABLE WEK
+C701;C701;110B 1170 11C0;C701;110B 1170 11C0; # (ìœ; ìœ; 윁; ìœ; 윁; ) HANGUL SYLLABLE WET
+C702;C702;110B 1170 11C1;C702;110B 1170 11C1; # (윂; 윂; á„‹á…°á‡; 윂; á„‹á…°á‡; ) HANGUL SYLLABLE WEP
+C703;C703;110B 1170 11C2;C703;110B 1170 11C2; # (윃; 윃; 윃; 윃; 윃; ) HANGUL SYLLABLE WEH
+C704;C704;110B 1171;C704;110B 1171; # (위; 위; 위; 위; 위; ) HANGUL SYLLABLE WI
+C705;C705;110B 1171 11A8;C705;110B 1171 11A8; # (윅; 윅; 윅; 윅; 윅; ) HANGUL SYLLABLE WIG
+C706;C706;110B 1171 11A9;C706;110B 1171 11A9; # (윆; 윆; 윆; 윆; 윆; ) HANGUL SYLLABLE WIGG
+C707;C707;110B 1171 11AA;C707;110B 1171 11AA; # (윇; 윇; 윇; 윇; 윇; ) HANGUL SYLLABLE WIGS
+C708;C708;110B 1171 11AB;C708;110B 1171 11AB; # (윈; 윈; 윈; 윈; 윈; ) HANGUL SYLLABLE WIN
+C709;C709;110B 1171 11AC;C709;110B 1171 11AC; # (윉; 윉; 윉; 윉; 윉; ) HANGUL SYLLABLE WINJ
+C70A;C70A;110B 1171 11AD;C70A;110B 1171 11AD; # (윊; 윊; 윊; 윊; 윊; ) HANGUL SYLLABLE WINH
+C70B;C70B;110B 1171 11AE;C70B;110B 1171 11AE; # (윋; 윋; 윋; 윋; 윋; ) HANGUL SYLLABLE WID
+C70C;C70C;110B 1171 11AF;C70C;110B 1171 11AF; # (윌; 윌; 윌; 윌; 윌; ) HANGUL SYLLABLE WIL
+C70D;C70D;110B 1171 11B0;C70D;110B 1171 11B0; # (ìœ; ìœ; 윍; ìœ; 윍; ) HANGUL SYLLABLE WILG
+C70E;C70E;110B 1171 11B1;C70E;110B 1171 11B1; # (윎; 윎; 윎; 윎; 윎; ) HANGUL SYLLABLE WILM
+C70F;C70F;110B 1171 11B2;C70F;110B 1171 11B2; # (ìœ; ìœ; 윏; ìœ; 윏; ) HANGUL SYLLABLE WILB
+C710;C710;110B 1171 11B3;C710;110B 1171 11B3; # (ìœ; ìœ; 윐; ìœ; 윐; ) HANGUL SYLLABLE WILS
+C711;C711;110B 1171 11B4;C711;110B 1171 11B4; # (윑; 윑; 윑; 윑; 윑; ) HANGUL SYLLABLE WILT
+C712;C712;110B 1171 11B5;C712;110B 1171 11B5; # (윒; 윒; 윒; 윒; 윒; ) HANGUL SYLLABLE WILP
+C713;C713;110B 1171 11B6;C713;110B 1171 11B6; # (윓; 윓; 윓; 윓; 윓; ) HANGUL SYLLABLE WILH
+C714;C714;110B 1171 11B7;C714;110B 1171 11B7; # (윔; 윔; 윔; 윔; 윔; ) HANGUL SYLLABLE WIM
+C715;C715;110B 1171 11B8;C715;110B 1171 11B8; # (윕; 윕; 윕; 윕; 윕; ) HANGUL SYLLABLE WIB
+C716;C716;110B 1171 11B9;C716;110B 1171 11B9; # (윖; 윖; 윖; 윖; 윖; ) HANGUL SYLLABLE WIBS
+C717;C717;110B 1171 11BA;C717;110B 1171 11BA; # (윗; 윗; 윗; 윗; 윗; ) HANGUL SYLLABLE WIS
+C718;C718;110B 1171 11BB;C718;110B 1171 11BB; # (윘; 윘; 윘; 윘; 윘; ) HANGUL SYLLABLE WISS
+C719;C719;110B 1171 11BC;C719;110B 1171 11BC; # (윙; 윙; 윙; 윙; 윙; ) HANGUL SYLLABLE WING
+C71A;C71A;110B 1171 11BD;C71A;110B 1171 11BD; # (윚; 윚; 윚; 윚; 윚; ) HANGUL SYLLABLE WIJ
+C71B;C71B;110B 1171 11BE;C71B;110B 1171 11BE; # (윛; 윛; 윛; 윛; 윛; ) HANGUL SYLLABLE WIC
+C71C;C71C;110B 1171 11BF;C71C;110B 1171 11BF; # (윜; 윜; 윜; 윜; 윜; ) HANGUL SYLLABLE WIK
+C71D;C71D;110B 1171 11C0;C71D;110B 1171 11C0; # (ìœ; ìœ; 윝; ìœ; 윝; ) HANGUL SYLLABLE WIT
+C71E;C71E;110B 1171 11C1;C71E;110B 1171 11C1; # (윞; 윞; á„‹á…±á‡; 윞; á„‹á…±á‡; ) HANGUL SYLLABLE WIP
+C71F;C71F;110B 1171 11C2;C71F;110B 1171 11C2; # (윟; 윟; 윟; 윟; 윟; ) HANGUL SYLLABLE WIH
+C720;C720;110B 1172;C720;110B 1172; # (유; 유; 유; 유; 유; ) HANGUL SYLLABLE YU
+C721;C721;110B 1172 11A8;C721;110B 1172 11A8; # (육; 육; 육; 육; 육; ) HANGUL SYLLABLE YUG
+C722;C722;110B 1172 11A9;C722;110B 1172 11A9; # (윢; 윢; 윢; 윢; 윢; ) HANGUL SYLLABLE YUGG
+C723;C723;110B 1172 11AA;C723;110B 1172 11AA; # (윣; 윣; 윣; 윣; 윣; ) HANGUL SYLLABLE YUGS
+C724;C724;110B 1172 11AB;C724;110B 1172 11AB; # (윤; 윤; 윤; 윤; 윤; ) HANGUL SYLLABLE YUN
+C725;C725;110B 1172 11AC;C725;110B 1172 11AC; # (윥; 윥; 윥; 윥; 윥; ) HANGUL SYLLABLE YUNJ
+C726;C726;110B 1172 11AD;C726;110B 1172 11AD; # (윦; 윦; 윦; 윦; 윦; ) HANGUL SYLLABLE YUNH
+C727;C727;110B 1172 11AE;C727;110B 1172 11AE; # (윧; 윧; 윧; 윧; 윧; ) HANGUL SYLLABLE YUD
+C728;C728;110B 1172 11AF;C728;110B 1172 11AF; # (율; 율; 율; 율; 율; ) HANGUL SYLLABLE YUL
+C729;C729;110B 1172 11B0;C729;110B 1172 11B0; # (윩; 윩; 윩; 윩; 윩; ) HANGUL SYLLABLE YULG
+C72A;C72A;110B 1172 11B1;C72A;110B 1172 11B1; # (윪; 윪; 윪; 윪; 윪; ) HANGUL SYLLABLE YULM
+C72B;C72B;110B 1172 11B2;C72B;110B 1172 11B2; # (윫; 윫; 윫; 윫; 윫; ) HANGUL SYLLABLE YULB
+C72C;C72C;110B 1172 11B3;C72C;110B 1172 11B3; # (윬; 윬; 윬; 윬; 윬; ) HANGUL SYLLABLE YULS
+C72D;C72D;110B 1172 11B4;C72D;110B 1172 11B4; # (윭; 윭; 윭; 윭; 윭; ) HANGUL SYLLABLE YULT
+C72E;C72E;110B 1172 11B5;C72E;110B 1172 11B5; # (윮; 윮; 윮; 윮; 윮; ) HANGUL SYLLABLE YULP
+C72F;C72F;110B 1172 11B6;C72F;110B 1172 11B6; # (윯; 윯; 윯; 윯; 윯; ) HANGUL SYLLABLE YULH
+C730;C730;110B 1172 11B7;C730;110B 1172 11B7; # (윰; 윰; 윰; 윰; 윰; ) HANGUL SYLLABLE YUM
+C731;C731;110B 1172 11B8;C731;110B 1172 11B8; # (윱; 윱; 윱; 윱; 윱; ) HANGUL SYLLABLE YUB
+C732;C732;110B 1172 11B9;C732;110B 1172 11B9; # (윲; 윲; 윲; 윲; 윲; ) HANGUL SYLLABLE YUBS
+C733;C733;110B 1172 11BA;C733;110B 1172 11BA; # (윳; 윳; 윳; 윳; 윳; ) HANGUL SYLLABLE YUS
+C734;C734;110B 1172 11BB;C734;110B 1172 11BB; # (윴; 윴; 윴; 윴; 윴; ) HANGUL SYLLABLE YUSS
+C735;C735;110B 1172 11BC;C735;110B 1172 11BC; # (융; 융; 융; 융; 융; ) HANGUL SYLLABLE YUNG
+C736;C736;110B 1172 11BD;C736;110B 1172 11BD; # (윶; 윶; 윶; 윶; 윶; ) HANGUL SYLLABLE YUJ
+C737;C737;110B 1172 11BE;C737;110B 1172 11BE; # (윷; 윷; 윷; 윷; 윷; ) HANGUL SYLLABLE YUC
+C738;C738;110B 1172 11BF;C738;110B 1172 11BF; # (윸; 윸; 윸; 윸; 윸; ) HANGUL SYLLABLE YUK
+C739;C739;110B 1172 11C0;C739;110B 1172 11C0; # (윹; 윹; 윹; 윹; 윹; ) HANGUL SYLLABLE YUT
+C73A;C73A;110B 1172 11C1;C73A;110B 1172 11C1; # (윺; 윺; á„‹á…²á‡; 윺; á„‹á…²á‡; ) HANGUL SYLLABLE YUP
+C73B;C73B;110B 1172 11C2;C73B;110B 1172 11C2; # (윻; 윻; 윻; 윻; 윻; ) HANGUL SYLLABLE YUH
+C73C;C73C;110B 1173;C73C;110B 1173; # (으; 으; 으; 으; 으; ) HANGUL SYLLABLE EU
+C73D;C73D;110B 1173 11A8;C73D;110B 1173 11A8; # (윽; 윽; 윽; 윽; 윽; ) HANGUL SYLLABLE EUG
+C73E;C73E;110B 1173 11A9;C73E;110B 1173 11A9; # (윾; 윾; 윾; 윾; 윾; ) HANGUL SYLLABLE EUGG
+C73F;C73F;110B 1173 11AA;C73F;110B 1173 11AA; # (윿; 윿; 윿; 윿; 윿; ) HANGUL SYLLABLE EUGS
+C740;C740;110B 1173 11AB;C740;110B 1173 11AB; # (ì€; ì€; 은; ì€; 은; ) HANGUL SYLLABLE EUN
+C741;C741;110B 1173 11AC;C741;110B 1173 11AC; # (ì; ì; 읁; ì; 읁; ) HANGUL SYLLABLE EUNJ
+C742;C742;110B 1173 11AD;C742;110B 1173 11AD; # (ì‚; ì‚; 읂; ì‚; 읂; ) HANGUL SYLLABLE EUNH
+C743;C743;110B 1173 11AE;C743;110B 1173 11AE; # (ìƒ; ìƒ; 읃; ìƒ; 읃; ) HANGUL SYLLABLE EUD
+C744;C744;110B 1173 11AF;C744;110B 1173 11AF; # (ì„; ì„; 을; ì„; 을; ) HANGUL SYLLABLE EUL
+C745;C745;110B 1173 11B0;C745;110B 1173 11B0; # (ì…; ì…; 읅; ì…; 읅; ) HANGUL SYLLABLE EULG
+C746;C746;110B 1173 11B1;C746;110B 1173 11B1; # (ì†; ì†; 읆; ì†; 읆; ) HANGUL SYLLABLE EULM
+C747;C747;110B 1173 11B2;C747;110B 1173 11B2; # (ì‡; ì‡; 읇; ì‡; 읇; ) HANGUL SYLLABLE EULB
+C748;C748;110B 1173 11B3;C748;110B 1173 11B3; # (ìˆ; ìˆ; 읈; ìˆ; 읈; ) HANGUL SYLLABLE EULS
+C749;C749;110B 1173 11B4;C749;110B 1173 11B4; # (ì‰; ì‰; 읉; ì‰; 읉; ) HANGUL SYLLABLE EULT
+C74A;C74A;110B 1173 11B5;C74A;110B 1173 11B5; # (ìŠ; ìŠ; 읊; ìŠ; 읊; ) HANGUL SYLLABLE EULP
+C74B;C74B;110B 1173 11B6;C74B;110B 1173 11B6; # (ì‹; ì‹; 읋; ì‹; 읋; ) HANGUL SYLLABLE EULH
+C74C;C74C;110B 1173 11B7;C74C;110B 1173 11B7; # (ìŒ; ìŒ; 음; ìŒ; 음; ) HANGUL SYLLABLE EUM
+C74D;C74D;110B 1173 11B8;C74D;110B 1173 11B8; # (ì; ì; 읍; ì; 읍; ) HANGUL SYLLABLE EUB
+C74E;C74E;110B 1173 11B9;C74E;110B 1173 11B9; # (ìŽ; ìŽ; 읎; ìŽ; 읎; ) HANGUL SYLLABLE EUBS
+C74F;C74F;110B 1173 11BA;C74F;110B 1173 11BA; # (ì; ì; 읏; ì; 읏; ) HANGUL SYLLABLE EUS
+C750;C750;110B 1173 11BB;C750;110B 1173 11BB; # (ì; ì; 읐; ì; 읐; ) HANGUL SYLLABLE EUSS
+C751;C751;110B 1173 11BC;C751;110B 1173 11BC; # (ì‘; ì‘; 응; ì‘; 응; ) HANGUL SYLLABLE EUNG
+C752;C752;110B 1173 11BD;C752;110B 1173 11BD; # (ì’; ì’; 읒; ì’; 읒; ) HANGUL SYLLABLE EUJ
+C753;C753;110B 1173 11BE;C753;110B 1173 11BE; # (ì“; ì“; 읓; ì“; 읓; ) HANGUL SYLLABLE EUC
+C754;C754;110B 1173 11BF;C754;110B 1173 11BF; # (ì”; ì”; 읔; ì”; 읔; ) HANGUL SYLLABLE EUK
+C755;C755;110B 1173 11C0;C755;110B 1173 11C0; # (ì•; ì•; 읕; ì•; 읕; ) HANGUL SYLLABLE EUT
+C756;C756;110B 1173 11C1;C756;110B 1173 11C1; # (ì–; ì–; á„‹á…³á‡; ì–; á„‹á…³á‡; ) HANGUL SYLLABLE EUP
+C757;C757;110B 1173 11C2;C757;110B 1173 11C2; # (ì—; ì—; 읗; ì—; 읗; ) HANGUL SYLLABLE EUH
+C758;C758;110B 1174;C758;110B 1174; # (ì˜; ì˜; á„‹á…´; ì˜; á„‹á…´; ) HANGUL SYLLABLE YI
+C759;C759;110B 1174 11A8;C759;110B 1174 11A8; # (ì™; ì™; 읙; ì™; 읙; ) HANGUL SYLLABLE YIG
+C75A;C75A;110B 1174 11A9;C75A;110B 1174 11A9; # (ìš; ìš; 읚; ìš; 읚; ) HANGUL SYLLABLE YIGG
+C75B;C75B;110B 1174 11AA;C75B;110B 1174 11AA; # (ì›; ì›; 읛; ì›; 읛; ) HANGUL SYLLABLE YIGS
+C75C;C75C;110B 1174 11AB;C75C;110B 1174 11AB; # (ìœ; ìœ; 읜; ìœ; 읜; ) HANGUL SYLLABLE YIN
+C75D;C75D;110B 1174 11AC;C75D;110B 1174 11AC; # (ì; ì; 읝; ì; 읝; ) HANGUL SYLLABLE YINJ
+C75E;C75E;110B 1174 11AD;C75E;110B 1174 11AD; # (ìž; ìž; 읞; ìž; 읞; ) HANGUL SYLLABLE YINH
+C75F;C75F;110B 1174 11AE;C75F;110B 1174 11AE; # (ìŸ; ìŸ; 읟; ìŸ; 읟; ) HANGUL SYLLABLE YID
+C760;C760;110B 1174 11AF;C760;110B 1174 11AF; # (ì ; ì ; 읠; ì ; 읠; ) HANGUL SYLLABLE YIL
+C761;C761;110B 1174 11B0;C761;110B 1174 11B0; # (ì¡; ì¡; 읡; ì¡; 읡; ) HANGUL SYLLABLE YILG
+C762;C762;110B 1174 11B1;C762;110B 1174 11B1; # (ì¢; ì¢; 읢; ì¢; 읢; ) HANGUL SYLLABLE YILM
+C763;C763;110B 1174 11B2;C763;110B 1174 11B2; # (ì£; ì£; 읣; ì£; 읣; ) HANGUL SYLLABLE YILB
+C764;C764;110B 1174 11B3;C764;110B 1174 11B3; # (ì¤; ì¤; 읤; ì¤; 읤; ) HANGUL SYLLABLE YILS
+C765;C765;110B 1174 11B4;C765;110B 1174 11B4; # (ì¥; ì¥; 읥; ì¥; 읥; ) HANGUL SYLLABLE YILT
+C766;C766;110B 1174 11B5;C766;110B 1174 11B5; # (ì¦; ì¦; 읦; ì¦; 읦; ) HANGUL SYLLABLE YILP
+C767;C767;110B 1174 11B6;C767;110B 1174 11B6; # (ì§; ì§; 읧; ì§; 읧; ) HANGUL SYLLABLE YILH
+C768;C768;110B 1174 11B7;C768;110B 1174 11B7; # (ì¨; ì¨; 읨; ì¨; 읨; ) HANGUL SYLLABLE YIM
+C769;C769;110B 1174 11B8;C769;110B 1174 11B8; # (ì©; ì©; 읩; ì©; 읩; ) HANGUL SYLLABLE YIB
+C76A;C76A;110B 1174 11B9;C76A;110B 1174 11B9; # (ìª; ìª; 읪; ìª; 읪; ) HANGUL SYLLABLE YIBS
+C76B;C76B;110B 1174 11BA;C76B;110B 1174 11BA; # (ì«; ì«; 읫; ì«; 읫; ) HANGUL SYLLABLE YIS
+C76C;C76C;110B 1174 11BB;C76C;110B 1174 11BB; # (ì¬; ì¬; 읬; ì¬; 읬; ) HANGUL SYLLABLE YISS
+C76D;C76D;110B 1174 11BC;C76D;110B 1174 11BC; # (ì­; ì­; 읭; ì­; 읭; ) HANGUL SYLLABLE YING
+C76E;C76E;110B 1174 11BD;C76E;110B 1174 11BD; # (ì®; ì®; 읮; ì®; 읮; ) HANGUL SYLLABLE YIJ
+C76F;C76F;110B 1174 11BE;C76F;110B 1174 11BE; # (ì¯; ì¯; 읯; ì¯; 읯; ) HANGUL SYLLABLE YIC
+C770;C770;110B 1174 11BF;C770;110B 1174 11BF; # (ì°; ì°; 읰; ì°; 읰; ) HANGUL SYLLABLE YIK
+C771;C771;110B 1174 11C0;C771;110B 1174 11C0; # (ì±; ì±; 읱; ì±; 읱; ) HANGUL SYLLABLE YIT
+C772;C772;110B 1174 11C1;C772;110B 1174 11C1; # (ì²; ì²; á„‹á…´á‡; ì²; á„‹á…´á‡; ) HANGUL SYLLABLE YIP
+C773;C773;110B 1174 11C2;C773;110B 1174 11C2; # (ì³; ì³; 읳; ì³; 읳; ) HANGUL SYLLABLE YIH
+C774;C774;110B 1175;C774;110B 1175; # (ì´; ì´; á„‹á…µ; ì´; á„‹á…µ; ) HANGUL SYLLABLE I
+C775;C775;110B 1175 11A8;C775;110B 1175 11A8; # (ìµ; ìµ; 익; ìµ; 익; ) HANGUL SYLLABLE IG
+C776;C776;110B 1175 11A9;C776;110B 1175 11A9; # (ì¶; ì¶; 읶; ì¶; 읶; ) HANGUL SYLLABLE IGG
+C777;C777;110B 1175 11AA;C777;110B 1175 11AA; # (ì·; ì·; 읷; ì·; 읷; ) HANGUL SYLLABLE IGS
+C778;C778;110B 1175 11AB;C778;110B 1175 11AB; # (ì¸; ì¸; 인; ì¸; 인; ) HANGUL SYLLABLE IN
+C779;C779;110B 1175 11AC;C779;110B 1175 11AC; # (ì¹; ì¹; 읹; ì¹; 읹; ) HANGUL SYLLABLE INJ
+C77A;C77A;110B 1175 11AD;C77A;110B 1175 11AD; # (ìº; ìº; 읺; ìº; 읺; ) HANGUL SYLLABLE INH
+C77B;C77B;110B 1175 11AE;C77B;110B 1175 11AE; # (ì»; ì»; 읻; ì»; 읻; ) HANGUL SYLLABLE ID
+C77C;C77C;110B 1175 11AF;C77C;110B 1175 11AF; # (ì¼; ì¼; 일; ì¼; 일; ) HANGUL SYLLABLE IL
+C77D;C77D;110B 1175 11B0;C77D;110B 1175 11B0; # (ì½; ì½; 읽; ì½; 읽; ) HANGUL SYLLABLE ILG
+C77E;C77E;110B 1175 11B1;C77E;110B 1175 11B1; # (ì¾; ì¾; 읾; ì¾; 읾; ) HANGUL SYLLABLE ILM
+C77F;C77F;110B 1175 11B2;C77F;110B 1175 11B2; # (ì¿; ì¿; 읿; ì¿; 읿; ) HANGUL SYLLABLE ILB
+C780;C780;110B 1175 11B3;C780;110B 1175 11B3; # (잀; 잀; 잀; 잀; 잀; ) HANGUL SYLLABLE ILS
+C781;C781;110B 1175 11B4;C781;110B 1175 11B4; # (ìž; ìž; 잁; ìž; 잁; ) HANGUL SYLLABLE ILT
+C782;C782;110B 1175 11B5;C782;110B 1175 11B5; # (잂; 잂; 잂; 잂; 잂; ) HANGUL SYLLABLE ILP
+C783;C783;110B 1175 11B6;C783;110B 1175 11B6; # (잃; 잃; 잃; 잃; 잃; ) HANGUL SYLLABLE ILH
+C784;C784;110B 1175 11B7;C784;110B 1175 11B7; # (임; 임; 임; 임; 임; ) HANGUL SYLLABLE IM
+C785;C785;110B 1175 11B8;C785;110B 1175 11B8; # (입; 입; 입; 입; 입; ) HANGUL SYLLABLE IB
+C786;C786;110B 1175 11B9;C786;110B 1175 11B9; # (잆; 잆; 잆; 잆; 잆; ) HANGUL SYLLABLE IBS
+C787;C787;110B 1175 11BA;C787;110B 1175 11BA; # (잇; 잇; 잇; 잇; 잇; ) HANGUL SYLLABLE IS
+C788;C788;110B 1175 11BB;C788;110B 1175 11BB; # (있; 있; 있; 있; 있; ) HANGUL SYLLABLE ISS
+C789;C789;110B 1175 11BC;C789;110B 1175 11BC; # (잉; 잉; 잉; 잉; 잉; ) HANGUL SYLLABLE ING
+C78A;C78A;110B 1175 11BD;C78A;110B 1175 11BD; # (잊; 잊; 잊; 잊; 잊; ) HANGUL SYLLABLE IJ
+C78B;C78B;110B 1175 11BE;C78B;110B 1175 11BE; # (잋; 잋; 잋; 잋; 잋; ) HANGUL SYLLABLE IC
+C78C;C78C;110B 1175 11BF;C78C;110B 1175 11BF; # (잌; 잌; 잌; 잌; 잌; ) HANGUL SYLLABLE IK
+C78D;C78D;110B 1175 11C0;C78D;110B 1175 11C0; # (ìž; ìž; 잍; ìž; 잍; ) HANGUL SYLLABLE IT
+C78E;C78E;110B 1175 11C1;C78E;110B 1175 11C1; # (잎; 잎; á„‹á…µá‡; 잎; á„‹á…µá‡; ) HANGUL SYLLABLE IP
+C78F;C78F;110B 1175 11C2;C78F;110B 1175 11C2; # (ìž; ìž; 잏; ìž; 잏; ) HANGUL SYLLABLE IH
+C790;C790;110C 1161;C790;110C 1161; # (ìž; ìž; 자; ìž; 자; ) HANGUL SYLLABLE JA
+C791;C791;110C 1161 11A8;C791;110C 1161 11A8; # (작; 작; 작; 작; 작; ) HANGUL SYLLABLE JAG
+C792;C792;110C 1161 11A9;C792;110C 1161 11A9; # (잒; 잒; 잒; 잒; 잒; ) HANGUL SYLLABLE JAGG
+C793;C793;110C 1161 11AA;C793;110C 1161 11AA; # (잓; 잓; 잓; 잓; 잓; ) HANGUL SYLLABLE JAGS
+C794;C794;110C 1161 11AB;C794;110C 1161 11AB; # (잔; 잔; 잔; 잔; 잔; ) HANGUL SYLLABLE JAN
+C795;C795;110C 1161 11AC;C795;110C 1161 11AC; # (잕; 잕; 잕; 잕; 잕; ) HANGUL SYLLABLE JANJ
+C796;C796;110C 1161 11AD;C796;110C 1161 11AD; # (잖; 잖; 잖; 잖; 잖; ) HANGUL SYLLABLE JANH
+C797;C797;110C 1161 11AE;C797;110C 1161 11AE; # (잗; 잗; 잗; 잗; 잗; ) HANGUL SYLLABLE JAD
+C798;C798;110C 1161 11AF;C798;110C 1161 11AF; # (잘; 잘; 잘; 잘; 잘; ) HANGUL SYLLABLE JAL
+C799;C799;110C 1161 11B0;C799;110C 1161 11B0; # (잙; 잙; 잙; 잙; 잙; ) HANGUL SYLLABLE JALG
+C79A;C79A;110C 1161 11B1;C79A;110C 1161 11B1; # (잚; 잚; 잚; 잚; 잚; ) HANGUL SYLLABLE JALM
+C79B;C79B;110C 1161 11B2;C79B;110C 1161 11B2; # (잛; 잛; 잛; 잛; 잛; ) HANGUL SYLLABLE JALB
+C79C;C79C;110C 1161 11B3;C79C;110C 1161 11B3; # (잜; 잜; 잜; 잜; 잜; ) HANGUL SYLLABLE JALS
+C79D;C79D;110C 1161 11B4;C79D;110C 1161 11B4; # (ìž; ìž; 잝; ìž; 잝; ) HANGUL SYLLABLE JALT
+C79E;C79E;110C 1161 11B5;C79E;110C 1161 11B5; # (잞; 잞; 잞; 잞; 잞; ) HANGUL SYLLABLE JALP
+C79F;C79F;110C 1161 11B6;C79F;110C 1161 11B6; # (잟; 잟; 잟; 잟; 잟; ) HANGUL SYLLABLE JALH
+C7A0;C7A0;110C 1161 11B7;C7A0;110C 1161 11B7; # (잠; 잠; 잠; 잠; 잠; ) HANGUL SYLLABLE JAM
+C7A1;C7A1;110C 1161 11B8;C7A1;110C 1161 11B8; # (잡; 잡; 잡; 잡; 잡; ) HANGUL SYLLABLE JAB
+C7A2;C7A2;110C 1161 11B9;C7A2;110C 1161 11B9; # (잢; 잢; 잢; 잢; 잢; ) HANGUL SYLLABLE JABS
+C7A3;C7A3;110C 1161 11BA;C7A3;110C 1161 11BA; # (잣; 잣; 잣; 잣; 잣; ) HANGUL SYLLABLE JAS
+C7A4;C7A4;110C 1161 11BB;C7A4;110C 1161 11BB; # (잤; 잤; 잤; 잤; 잤; ) HANGUL SYLLABLE JASS
+C7A5;C7A5;110C 1161 11BC;C7A5;110C 1161 11BC; # (장; 장; 장; 장; 장; ) HANGUL SYLLABLE JANG
+C7A6;C7A6;110C 1161 11BD;C7A6;110C 1161 11BD; # (잦; 잦; 잦; 잦; 잦; ) HANGUL SYLLABLE JAJ
+C7A7;C7A7;110C 1161 11BE;C7A7;110C 1161 11BE; # (잧; 잧; 잧; 잧; 잧; ) HANGUL SYLLABLE JAC
+C7A8;C7A8;110C 1161 11BF;C7A8;110C 1161 11BF; # (잨; 잨; 잨; 잨; 잨; ) HANGUL SYLLABLE JAK
+C7A9;C7A9;110C 1161 11C0;C7A9;110C 1161 11C0; # (잩; 잩; 잩; 잩; 잩; ) HANGUL SYLLABLE JAT
+C7AA;C7AA;110C 1161 11C1;C7AA;110C 1161 11C1; # (잪; 잪; 자á‡; 잪; 자á‡; ) HANGUL SYLLABLE JAP
+C7AB;C7AB;110C 1161 11C2;C7AB;110C 1161 11C2; # (잫; 잫; 잫; 잫; 잫; ) HANGUL SYLLABLE JAH
+C7AC;C7AC;110C 1162;C7AC;110C 1162; # (재; 재; 재; 재; 재; ) HANGUL SYLLABLE JAE
+C7AD;C7AD;110C 1162 11A8;C7AD;110C 1162 11A8; # (잭; 잭; 잭; 잭; 잭; ) HANGUL SYLLABLE JAEG
+C7AE;C7AE;110C 1162 11A9;C7AE;110C 1162 11A9; # (잮; 잮; 잮; 잮; 잮; ) HANGUL SYLLABLE JAEGG
+C7AF;C7AF;110C 1162 11AA;C7AF;110C 1162 11AA; # (잯; 잯; 잯; 잯; 잯; ) HANGUL SYLLABLE JAEGS
+C7B0;C7B0;110C 1162 11AB;C7B0;110C 1162 11AB; # (잰; 잰; 잰; 잰; 잰; ) HANGUL SYLLABLE JAEN
+C7B1;C7B1;110C 1162 11AC;C7B1;110C 1162 11AC; # (잱; 잱; 잱; 잱; 잱; ) HANGUL SYLLABLE JAENJ
+C7B2;C7B2;110C 1162 11AD;C7B2;110C 1162 11AD; # (잲; 잲; 잲; 잲; 잲; ) HANGUL SYLLABLE JAENH
+C7B3;C7B3;110C 1162 11AE;C7B3;110C 1162 11AE; # (잳; 잳; 잳; 잳; 잳; ) HANGUL SYLLABLE JAED
+C7B4;C7B4;110C 1162 11AF;C7B4;110C 1162 11AF; # (잴; 잴; 잴; 잴; 잴; ) HANGUL SYLLABLE JAEL
+C7B5;C7B5;110C 1162 11B0;C7B5;110C 1162 11B0; # (잵; 잵; 잵; 잵; 잵; ) HANGUL SYLLABLE JAELG
+C7B6;C7B6;110C 1162 11B1;C7B6;110C 1162 11B1; # (잶; 잶; 잶; 잶; 잶; ) HANGUL SYLLABLE JAELM
+C7B7;C7B7;110C 1162 11B2;C7B7;110C 1162 11B2; # (잷; 잷; 잷; 잷; 잷; ) HANGUL SYLLABLE JAELB
+C7B8;C7B8;110C 1162 11B3;C7B8;110C 1162 11B3; # (잸; 잸; 잸; 잸; 잸; ) HANGUL SYLLABLE JAELS
+C7B9;C7B9;110C 1162 11B4;C7B9;110C 1162 11B4; # (잹; 잹; 잹; 잹; 잹; ) HANGUL SYLLABLE JAELT
+C7BA;C7BA;110C 1162 11B5;C7BA;110C 1162 11B5; # (잺; 잺; 잺; 잺; 잺; ) HANGUL SYLLABLE JAELP
+C7BB;C7BB;110C 1162 11B6;C7BB;110C 1162 11B6; # (잻; 잻; 잻; 잻; 잻; ) HANGUL SYLLABLE JAELH
+C7BC;C7BC;110C 1162 11B7;C7BC;110C 1162 11B7; # (잼; 잼; 잼; 잼; 잼; ) HANGUL SYLLABLE JAEM
+C7BD;C7BD;110C 1162 11B8;C7BD;110C 1162 11B8; # (잽; 잽; 잽; 잽; 잽; ) HANGUL SYLLABLE JAEB
+C7BE;C7BE;110C 1162 11B9;C7BE;110C 1162 11B9; # (잾; 잾; 잾; 잾; 잾; ) HANGUL SYLLABLE JAEBS
+C7BF;C7BF;110C 1162 11BA;C7BF;110C 1162 11BA; # (잿; 잿; 잿; 잿; 잿; ) HANGUL SYLLABLE JAES
+C7C0;C7C0;110C 1162 11BB;C7C0;110C 1162 11BB; # (쟀; 쟀; 쟀; 쟀; 쟀; ) HANGUL SYLLABLE JAESS
+C7C1;C7C1;110C 1162 11BC;C7C1;110C 1162 11BC; # (ìŸ; ìŸ; 쟁; ìŸ; 쟁; ) HANGUL SYLLABLE JAENG
+C7C2;C7C2;110C 1162 11BD;C7C2;110C 1162 11BD; # (쟂; 쟂; 쟂; 쟂; 쟂; ) HANGUL SYLLABLE JAEJ
+C7C3;C7C3;110C 1162 11BE;C7C3;110C 1162 11BE; # (쟃; 쟃; 쟃; 쟃; 쟃; ) HANGUL SYLLABLE JAEC
+C7C4;C7C4;110C 1162 11BF;C7C4;110C 1162 11BF; # (쟄; 쟄; 쟄; 쟄; 쟄; ) HANGUL SYLLABLE JAEK
+C7C5;C7C5;110C 1162 11C0;C7C5;110C 1162 11C0; # (쟅; 쟅; 쟅; 쟅; 쟅; ) HANGUL SYLLABLE JAET
+C7C6;C7C6;110C 1162 11C1;C7C6;110C 1162 11C1; # (쟆; 쟆; 재á‡; 쟆; 재á‡; ) HANGUL SYLLABLE JAEP
+C7C7;C7C7;110C 1162 11C2;C7C7;110C 1162 11C2; # (쟇; 쟇; 쟇; 쟇; 쟇; ) HANGUL SYLLABLE JAEH
+C7C8;C7C8;110C 1163;C7C8;110C 1163; # (쟈; 쟈; 쟈; 쟈; 쟈; ) HANGUL SYLLABLE JYA
+C7C9;C7C9;110C 1163 11A8;C7C9;110C 1163 11A8; # (쟉; 쟉; 쟉; 쟉; 쟉; ) HANGUL SYLLABLE JYAG
+C7CA;C7CA;110C 1163 11A9;C7CA;110C 1163 11A9; # (쟊; 쟊; 쟊; 쟊; 쟊; ) HANGUL SYLLABLE JYAGG
+C7CB;C7CB;110C 1163 11AA;C7CB;110C 1163 11AA; # (쟋; 쟋; 쟋; 쟋; 쟋; ) HANGUL SYLLABLE JYAGS
+C7CC;C7CC;110C 1163 11AB;C7CC;110C 1163 11AB; # (쟌; 쟌; 쟌; 쟌; 쟌; ) HANGUL SYLLABLE JYAN
+C7CD;C7CD;110C 1163 11AC;C7CD;110C 1163 11AC; # (ìŸ; ìŸ; 쟍; ìŸ; 쟍; ) HANGUL SYLLABLE JYANJ
+C7CE;C7CE;110C 1163 11AD;C7CE;110C 1163 11AD; # (쟎; 쟎; 쟎; 쟎; 쟎; ) HANGUL SYLLABLE JYANH
+C7CF;C7CF;110C 1163 11AE;C7CF;110C 1163 11AE; # (ìŸ; ìŸ; 쟏; ìŸ; 쟏; ) HANGUL SYLLABLE JYAD
+C7D0;C7D0;110C 1163 11AF;C7D0;110C 1163 11AF; # (ìŸ; ìŸ; 쟐; ìŸ; 쟐; ) HANGUL SYLLABLE JYAL
+C7D1;C7D1;110C 1163 11B0;C7D1;110C 1163 11B0; # (쟑; 쟑; 쟑; 쟑; 쟑; ) HANGUL SYLLABLE JYALG
+C7D2;C7D2;110C 1163 11B1;C7D2;110C 1163 11B1; # (쟒; 쟒; 쟒; 쟒; 쟒; ) HANGUL SYLLABLE JYALM
+C7D3;C7D3;110C 1163 11B2;C7D3;110C 1163 11B2; # (쟓; 쟓; 쟓; 쟓; 쟓; ) HANGUL SYLLABLE JYALB
+C7D4;C7D4;110C 1163 11B3;C7D4;110C 1163 11B3; # (쟔; 쟔; 쟔; 쟔; 쟔; ) HANGUL SYLLABLE JYALS
+C7D5;C7D5;110C 1163 11B4;C7D5;110C 1163 11B4; # (쟕; 쟕; 쟕; 쟕; 쟕; ) HANGUL SYLLABLE JYALT
+C7D6;C7D6;110C 1163 11B5;C7D6;110C 1163 11B5; # (쟖; 쟖; 쟖; 쟖; 쟖; ) HANGUL SYLLABLE JYALP
+C7D7;C7D7;110C 1163 11B6;C7D7;110C 1163 11B6; # (쟗; 쟗; 쟗; 쟗; 쟗; ) HANGUL SYLLABLE JYALH
+C7D8;C7D8;110C 1163 11B7;C7D8;110C 1163 11B7; # (쟘; 쟘; 쟘; 쟘; 쟘; ) HANGUL SYLLABLE JYAM
+C7D9;C7D9;110C 1163 11B8;C7D9;110C 1163 11B8; # (쟙; 쟙; 쟙; 쟙; 쟙; ) HANGUL SYLLABLE JYAB
+C7DA;C7DA;110C 1163 11B9;C7DA;110C 1163 11B9; # (쟚; 쟚; 쟚; 쟚; 쟚; ) HANGUL SYLLABLE JYABS
+C7DB;C7DB;110C 1163 11BA;C7DB;110C 1163 11BA; # (쟛; 쟛; 쟛; 쟛; 쟛; ) HANGUL SYLLABLE JYAS
+C7DC;C7DC;110C 1163 11BB;C7DC;110C 1163 11BB; # (쟜; 쟜; 쟜; 쟜; 쟜; ) HANGUL SYLLABLE JYASS
+C7DD;C7DD;110C 1163 11BC;C7DD;110C 1163 11BC; # (ìŸ; ìŸ; 쟝; ìŸ; 쟝; ) HANGUL SYLLABLE JYANG
+C7DE;C7DE;110C 1163 11BD;C7DE;110C 1163 11BD; # (쟞; 쟞; 쟞; 쟞; 쟞; ) HANGUL SYLLABLE JYAJ
+C7DF;C7DF;110C 1163 11BE;C7DF;110C 1163 11BE; # (쟟; 쟟; 쟟; 쟟; 쟟; ) HANGUL SYLLABLE JYAC
+C7E0;C7E0;110C 1163 11BF;C7E0;110C 1163 11BF; # (쟠; 쟠; 쟠; 쟠; 쟠; ) HANGUL SYLLABLE JYAK
+C7E1;C7E1;110C 1163 11C0;C7E1;110C 1163 11C0; # (쟡; 쟡; 쟡; 쟡; 쟡; ) HANGUL SYLLABLE JYAT
+C7E2;C7E2;110C 1163 11C1;C7E2;110C 1163 11C1; # (쟢; 쟢; 쟈á‡; 쟢; 쟈á‡; ) HANGUL SYLLABLE JYAP
+C7E3;C7E3;110C 1163 11C2;C7E3;110C 1163 11C2; # (쟣; 쟣; 쟣; 쟣; 쟣; ) HANGUL SYLLABLE JYAH
+C7E4;C7E4;110C 1164;C7E4;110C 1164; # (쟤; 쟤; 쟤; 쟤; 쟤; ) HANGUL SYLLABLE JYAE
+C7E5;C7E5;110C 1164 11A8;C7E5;110C 1164 11A8; # (쟥; 쟥; 쟥; 쟥; 쟥; ) HANGUL SYLLABLE JYAEG
+C7E6;C7E6;110C 1164 11A9;C7E6;110C 1164 11A9; # (쟦; 쟦; 쟦; 쟦; 쟦; ) HANGUL SYLLABLE JYAEGG
+C7E7;C7E7;110C 1164 11AA;C7E7;110C 1164 11AA; # (쟧; 쟧; 쟧; 쟧; 쟧; ) HANGUL SYLLABLE JYAEGS
+C7E8;C7E8;110C 1164 11AB;C7E8;110C 1164 11AB; # (쟨; 쟨; 쟨; 쟨; 쟨; ) HANGUL SYLLABLE JYAEN
+C7E9;C7E9;110C 1164 11AC;C7E9;110C 1164 11AC; # (쟩; 쟩; 쟩; 쟩; 쟩; ) HANGUL SYLLABLE JYAENJ
+C7EA;C7EA;110C 1164 11AD;C7EA;110C 1164 11AD; # (쟪; 쟪; 쟪; 쟪; 쟪; ) HANGUL SYLLABLE JYAENH
+C7EB;C7EB;110C 1164 11AE;C7EB;110C 1164 11AE; # (쟫; 쟫; 쟫; 쟫; 쟫; ) HANGUL SYLLABLE JYAED
+C7EC;C7EC;110C 1164 11AF;C7EC;110C 1164 11AF; # (쟬; 쟬; 쟬; 쟬; 쟬; ) HANGUL SYLLABLE JYAEL
+C7ED;C7ED;110C 1164 11B0;C7ED;110C 1164 11B0; # (쟭; 쟭; 쟭; 쟭; 쟭; ) HANGUL SYLLABLE JYAELG
+C7EE;C7EE;110C 1164 11B1;C7EE;110C 1164 11B1; # (쟮; 쟮; 쟮; 쟮; 쟮; ) HANGUL SYLLABLE JYAELM
+C7EF;C7EF;110C 1164 11B2;C7EF;110C 1164 11B2; # (쟯; 쟯; 쟯; 쟯; 쟯; ) HANGUL SYLLABLE JYAELB
+C7F0;C7F0;110C 1164 11B3;C7F0;110C 1164 11B3; # (쟰; 쟰; 쟰; 쟰; 쟰; ) HANGUL SYLLABLE JYAELS
+C7F1;C7F1;110C 1164 11B4;C7F1;110C 1164 11B4; # (쟱; 쟱; 쟱; 쟱; 쟱; ) HANGUL SYLLABLE JYAELT
+C7F2;C7F2;110C 1164 11B5;C7F2;110C 1164 11B5; # (쟲; 쟲; 쟲; 쟲; 쟲; ) HANGUL SYLLABLE JYAELP
+C7F3;C7F3;110C 1164 11B6;C7F3;110C 1164 11B6; # (쟳; 쟳; 쟳; 쟳; 쟳; ) HANGUL SYLLABLE JYAELH
+C7F4;C7F4;110C 1164 11B7;C7F4;110C 1164 11B7; # (쟴; 쟴; 쟴; 쟴; 쟴; ) HANGUL SYLLABLE JYAEM
+C7F5;C7F5;110C 1164 11B8;C7F5;110C 1164 11B8; # (쟵; 쟵; 쟵; 쟵; 쟵; ) HANGUL SYLLABLE JYAEB
+C7F6;C7F6;110C 1164 11B9;C7F6;110C 1164 11B9; # (쟶; 쟶; 쟶; 쟶; 쟶; ) HANGUL SYLLABLE JYAEBS
+C7F7;C7F7;110C 1164 11BA;C7F7;110C 1164 11BA; # (쟷; 쟷; 쟷; 쟷; 쟷; ) HANGUL SYLLABLE JYAES
+C7F8;C7F8;110C 1164 11BB;C7F8;110C 1164 11BB; # (쟸; 쟸; 쟸; 쟸; 쟸; ) HANGUL SYLLABLE JYAESS
+C7F9;C7F9;110C 1164 11BC;C7F9;110C 1164 11BC; # (쟹; 쟹; 쟹; 쟹; 쟹; ) HANGUL SYLLABLE JYAENG
+C7FA;C7FA;110C 1164 11BD;C7FA;110C 1164 11BD; # (쟺; 쟺; 쟺; 쟺; 쟺; ) HANGUL SYLLABLE JYAEJ
+C7FB;C7FB;110C 1164 11BE;C7FB;110C 1164 11BE; # (쟻; 쟻; 쟻; 쟻; 쟻; ) HANGUL SYLLABLE JYAEC
+C7FC;C7FC;110C 1164 11BF;C7FC;110C 1164 11BF; # (쟼; 쟼; 쟼; 쟼; 쟼; ) HANGUL SYLLABLE JYAEK
+C7FD;C7FD;110C 1164 11C0;C7FD;110C 1164 11C0; # (쟽; 쟽; 쟽; 쟽; 쟽; ) HANGUL SYLLABLE JYAET
+C7FE;C7FE;110C 1164 11C1;C7FE;110C 1164 11C1; # (쟾; 쟾; 쟤á‡; 쟾; 쟤á‡; ) HANGUL SYLLABLE JYAEP
+C7FF;C7FF;110C 1164 11C2;C7FF;110C 1164 11C2; # (쟿; 쟿; 쟿; 쟿; 쟿; ) HANGUL SYLLABLE JYAEH
+C800;C800;110C 1165;C800;110C 1165; # (저; 저; 저; 저; 저; ) HANGUL SYLLABLE JEO
+C801;C801;110C 1165 11A8;C801;110C 1165 11A8; # (ì ; ì ; 적; ì ; 적; ) HANGUL SYLLABLE JEOG
+C802;C802;110C 1165 11A9;C802;110C 1165 11A9; # (젂; 젂; 젂; 젂; 젂; ) HANGUL SYLLABLE JEOGG
+C803;C803;110C 1165 11AA;C803;110C 1165 11AA; # (젃; 젃; 젃; 젃; 젃; ) HANGUL SYLLABLE JEOGS
+C804;C804;110C 1165 11AB;C804;110C 1165 11AB; # (전; 전; 전; 전; 전; ) HANGUL SYLLABLE JEON
+C805;C805;110C 1165 11AC;C805;110C 1165 11AC; # (젅; 젅; 젅; 젅; 젅; ) HANGUL SYLLABLE JEONJ
+C806;C806;110C 1165 11AD;C806;110C 1165 11AD; # (젆; 젆; 젆; 젆; 젆; ) HANGUL SYLLABLE JEONH
+C807;C807;110C 1165 11AE;C807;110C 1165 11AE; # (젇; 젇; 젇; 젇; 젇; ) HANGUL SYLLABLE JEOD
+C808;C808;110C 1165 11AF;C808;110C 1165 11AF; # (절; 절; 절; 절; 절; ) HANGUL SYLLABLE JEOL
+C809;C809;110C 1165 11B0;C809;110C 1165 11B0; # (젉; 젉; 젉; 젉; 젉; ) HANGUL SYLLABLE JEOLG
+C80A;C80A;110C 1165 11B1;C80A;110C 1165 11B1; # (젊; 젊; 젊; 젊; 젊; ) HANGUL SYLLABLE JEOLM
+C80B;C80B;110C 1165 11B2;C80B;110C 1165 11B2; # (젋; 젋; 젋; 젋; 젋; ) HANGUL SYLLABLE JEOLB
+C80C;C80C;110C 1165 11B3;C80C;110C 1165 11B3; # (젌; 젌; 젌; 젌; 젌; ) HANGUL SYLLABLE JEOLS
+C80D;C80D;110C 1165 11B4;C80D;110C 1165 11B4; # (ì ; ì ; 젍; ì ; 젍; ) HANGUL SYLLABLE JEOLT
+C80E;C80E;110C 1165 11B5;C80E;110C 1165 11B5; # (젎; 젎; 젎; 젎; 젎; ) HANGUL SYLLABLE JEOLP
+C80F;C80F;110C 1165 11B6;C80F;110C 1165 11B6; # (ì ; ì ; 젏; ì ; 젏; ) HANGUL SYLLABLE JEOLH
+C810;C810;110C 1165 11B7;C810;110C 1165 11B7; # (ì ; ì ; 점; ì ; 점; ) HANGUL SYLLABLE JEOM
+C811;C811;110C 1165 11B8;C811;110C 1165 11B8; # (접; 접; 접; 접; 접; ) HANGUL SYLLABLE JEOB
+C812;C812;110C 1165 11B9;C812;110C 1165 11B9; # (젒; 젒; 젒; 젒; 젒; ) HANGUL SYLLABLE JEOBS
+C813;C813;110C 1165 11BA;C813;110C 1165 11BA; # (젓; 젓; 젓; 젓; 젓; ) HANGUL SYLLABLE JEOS
+C814;C814;110C 1165 11BB;C814;110C 1165 11BB; # (젔; 젔; 젔; 젔; 젔; ) HANGUL SYLLABLE JEOSS
+C815;C815;110C 1165 11BC;C815;110C 1165 11BC; # (정; 정; 정; 정; 정; ) HANGUL SYLLABLE JEONG
+C816;C816;110C 1165 11BD;C816;110C 1165 11BD; # (젖; 젖; 젖; 젖; 젖; ) HANGUL SYLLABLE JEOJ
+C817;C817;110C 1165 11BE;C817;110C 1165 11BE; # (젗; 젗; 젗; 젗; 젗; ) HANGUL SYLLABLE JEOC
+C818;C818;110C 1165 11BF;C818;110C 1165 11BF; # (젘; 젘; 젘; 젘; 젘; ) HANGUL SYLLABLE JEOK
+C819;C819;110C 1165 11C0;C819;110C 1165 11C0; # (젙; 젙; 젙; 젙; 젙; ) HANGUL SYLLABLE JEOT
+C81A;C81A;110C 1165 11C1;C81A;110C 1165 11C1; # (ì š; ì š; 저á‡; ì š; 저á‡; ) HANGUL SYLLABLE JEOP
+C81B;C81B;110C 1165 11C2;C81B;110C 1165 11C2; # (젛; 젛; 젛; 젛; 젛; ) HANGUL SYLLABLE JEOH
+C81C;C81C;110C 1166;C81C;110C 1166; # (제; 제; 제; 제; 제; ) HANGUL SYLLABLE JE
+C81D;C81D;110C 1166 11A8;C81D;110C 1166 11A8; # (ì ; ì ; 젝; ì ; 젝; ) HANGUL SYLLABLE JEG
+C81E;C81E;110C 1166 11A9;C81E;110C 1166 11A9; # (젞; 젞; 젞; 젞; 젞; ) HANGUL SYLLABLE JEGG
+C81F;C81F;110C 1166 11AA;C81F;110C 1166 11AA; # (젟; 젟; 젟; 젟; 젟; ) HANGUL SYLLABLE JEGS
+C820;C820;110C 1166 11AB;C820;110C 1166 11AB; # (젠; 젠; 젠; 젠; 젠; ) HANGUL SYLLABLE JEN
+C821;C821;110C 1166 11AC;C821;110C 1166 11AC; # (젡; 젡; 젡; 젡; 젡; ) HANGUL SYLLABLE JENJ
+C822;C822;110C 1166 11AD;C822;110C 1166 11AD; # (젢; 젢; 젢; 젢; 젢; ) HANGUL SYLLABLE JENH
+C823;C823;110C 1166 11AE;C823;110C 1166 11AE; # (젣; 젣; 젣; 젣; 젣; ) HANGUL SYLLABLE JED
+C824;C824;110C 1166 11AF;C824;110C 1166 11AF; # (젤; 젤; 젤; 젤; 젤; ) HANGUL SYLLABLE JEL
+C825;C825;110C 1166 11B0;C825;110C 1166 11B0; # (젥; 젥; 젥; 젥; 젥; ) HANGUL SYLLABLE JELG
+C826;C826;110C 1166 11B1;C826;110C 1166 11B1; # (젦; 젦; 젦; 젦; 젦; ) HANGUL SYLLABLE JELM
+C827;C827;110C 1166 11B2;C827;110C 1166 11B2; # (젧; 젧; 젧; 젧; 젧; ) HANGUL SYLLABLE JELB
+C828;C828;110C 1166 11B3;C828;110C 1166 11B3; # (젨; 젨; 젨; 젨; 젨; ) HANGUL SYLLABLE JELS
+C829;C829;110C 1166 11B4;C829;110C 1166 11B4; # (젩; 젩; 젩; 젩; 젩; ) HANGUL SYLLABLE JELT
+C82A;C82A;110C 1166 11B5;C82A;110C 1166 11B5; # (젪; 젪; 젪; 젪; 젪; ) HANGUL SYLLABLE JELP
+C82B;C82B;110C 1166 11B6;C82B;110C 1166 11B6; # (젫; 젫; 젫; 젫; 젫; ) HANGUL SYLLABLE JELH
+C82C;C82C;110C 1166 11B7;C82C;110C 1166 11B7; # (젬; 젬; 젬; 젬; 젬; ) HANGUL SYLLABLE JEM
+C82D;C82D;110C 1166 11B8;C82D;110C 1166 11B8; # (젭; 젭; 젭; 젭; 젭; ) HANGUL SYLLABLE JEB
+C82E;C82E;110C 1166 11B9;C82E;110C 1166 11B9; # (젮; 젮; 젮; 젮; 젮; ) HANGUL SYLLABLE JEBS
+C82F;C82F;110C 1166 11BA;C82F;110C 1166 11BA; # (젯; 젯; 젯; 젯; 젯; ) HANGUL SYLLABLE JES
+C830;C830;110C 1166 11BB;C830;110C 1166 11BB; # (젰; 젰; 젰; 젰; 젰; ) HANGUL SYLLABLE JESS
+C831;C831;110C 1166 11BC;C831;110C 1166 11BC; # (젱; 젱; 젱; 젱; 젱; ) HANGUL SYLLABLE JENG
+C832;C832;110C 1166 11BD;C832;110C 1166 11BD; # (젲; 젲; 젲; 젲; 젲; ) HANGUL SYLLABLE JEJ
+C833;C833;110C 1166 11BE;C833;110C 1166 11BE; # (젳; 젳; 젳; 젳; 젳; ) HANGUL SYLLABLE JEC
+C834;C834;110C 1166 11BF;C834;110C 1166 11BF; # (젴; 젴; 젴; 젴; 젴; ) HANGUL SYLLABLE JEK
+C835;C835;110C 1166 11C0;C835;110C 1166 11C0; # (젵; 젵; 젵; 젵; 젵; ) HANGUL SYLLABLE JET
+C836;C836;110C 1166 11C1;C836;110C 1166 11C1; # (ì ¶; ì ¶; 제á‡; ì ¶; 제á‡; ) HANGUL SYLLABLE JEP
+C837;C837;110C 1166 11C2;C837;110C 1166 11C2; # (젷; 젷; 젷; 젷; 젷; ) HANGUL SYLLABLE JEH
+C838;C838;110C 1167;C838;110C 1167; # (져; 져; 져; 져; 져; ) HANGUL SYLLABLE JYEO
+C839;C839;110C 1167 11A8;C839;110C 1167 11A8; # (젹; 젹; 젹; 젹; 젹; ) HANGUL SYLLABLE JYEOG
+C83A;C83A;110C 1167 11A9;C83A;110C 1167 11A9; # (젺; 젺; 젺; 젺; 젺; ) HANGUL SYLLABLE JYEOGG
+C83B;C83B;110C 1167 11AA;C83B;110C 1167 11AA; # (젻; 젻; 젻; 젻; 젻; ) HANGUL SYLLABLE JYEOGS
+C83C;C83C;110C 1167 11AB;C83C;110C 1167 11AB; # (젼; 젼; 젼; 젼; 젼; ) HANGUL SYLLABLE JYEON
+C83D;C83D;110C 1167 11AC;C83D;110C 1167 11AC; # (젽; 젽; 젽; 젽; 젽; ) HANGUL SYLLABLE JYEONJ
+C83E;C83E;110C 1167 11AD;C83E;110C 1167 11AD; # (젾; 젾; 젾; 젾; 젾; ) HANGUL SYLLABLE JYEONH
+C83F;C83F;110C 1167 11AE;C83F;110C 1167 11AE; # (젿; 젿; 젿; 젿; 젿; ) HANGUL SYLLABLE JYEOD
+C840;C840;110C 1167 11AF;C840;110C 1167 11AF; # (졀; 졀; 졀; 졀; 졀; ) HANGUL SYLLABLE JYEOL
+C841;C841;110C 1167 11B0;C841;110C 1167 11B0; # (ì¡; ì¡; 졁; ì¡; 졁; ) HANGUL SYLLABLE JYEOLG
+C842;C842;110C 1167 11B1;C842;110C 1167 11B1; # (졂; 졂; 졂; 졂; 졂; ) HANGUL SYLLABLE JYEOLM
+C843;C843;110C 1167 11B2;C843;110C 1167 11B2; # (졃; 졃; 졃; 졃; 졃; ) HANGUL SYLLABLE JYEOLB
+C844;C844;110C 1167 11B3;C844;110C 1167 11B3; # (졄; 졄; 졄; 졄; 졄; ) HANGUL SYLLABLE JYEOLS
+C845;C845;110C 1167 11B4;C845;110C 1167 11B4; # (졅; 졅; 졅; 졅; 졅; ) HANGUL SYLLABLE JYEOLT
+C846;C846;110C 1167 11B5;C846;110C 1167 11B5; # (졆; 졆; 졆; 졆; 졆; ) HANGUL SYLLABLE JYEOLP
+C847;C847;110C 1167 11B6;C847;110C 1167 11B6; # (졇; 졇; 졇; 졇; 졇; ) HANGUL SYLLABLE JYEOLH
+C848;C848;110C 1167 11B7;C848;110C 1167 11B7; # (졈; 졈; 졈; 졈; 졈; ) HANGUL SYLLABLE JYEOM
+C849;C849;110C 1167 11B8;C849;110C 1167 11B8; # (졉; 졉; 졉; 졉; 졉; ) HANGUL SYLLABLE JYEOB
+C84A;C84A;110C 1167 11B9;C84A;110C 1167 11B9; # (졊; 졊; 졊; 졊; 졊; ) HANGUL SYLLABLE JYEOBS
+C84B;C84B;110C 1167 11BA;C84B;110C 1167 11BA; # (졋; 졋; 졋; 졋; 졋; ) HANGUL SYLLABLE JYEOS
+C84C;C84C;110C 1167 11BB;C84C;110C 1167 11BB; # (졌; 졌; 졌; 졌; 졌; ) HANGUL SYLLABLE JYEOSS
+C84D;C84D;110C 1167 11BC;C84D;110C 1167 11BC; # (ì¡; ì¡; 졍; ì¡; 졍; ) HANGUL SYLLABLE JYEONG
+C84E;C84E;110C 1167 11BD;C84E;110C 1167 11BD; # (졎; 졎; 졎; 졎; 졎; ) HANGUL SYLLABLE JYEOJ
+C84F;C84F;110C 1167 11BE;C84F;110C 1167 11BE; # (ì¡; ì¡; 졏; ì¡; 졏; ) HANGUL SYLLABLE JYEOC
+C850;C850;110C 1167 11BF;C850;110C 1167 11BF; # (ì¡; ì¡; 졐; ì¡; 졐; ) HANGUL SYLLABLE JYEOK
+C851;C851;110C 1167 11C0;C851;110C 1167 11C0; # (졑; 졑; 졑; 졑; 졑; ) HANGUL SYLLABLE JYEOT
+C852;C852;110C 1167 11C1;C852;110C 1167 11C1; # (ì¡’; ì¡’; 져á‡; ì¡’; 져á‡; ) HANGUL SYLLABLE JYEOP
+C853;C853;110C 1167 11C2;C853;110C 1167 11C2; # (졓; 졓; 졓; 졓; 졓; ) HANGUL SYLLABLE JYEOH
+C854;C854;110C 1168;C854;110C 1168; # (졔; 졔; 졔; 졔; 졔; ) HANGUL SYLLABLE JYE
+C855;C855;110C 1168 11A8;C855;110C 1168 11A8; # (졕; 졕; 졕; 졕; 졕; ) HANGUL SYLLABLE JYEG
+C856;C856;110C 1168 11A9;C856;110C 1168 11A9; # (졖; 졖; 졖; 졖; 졖; ) HANGUL SYLLABLE JYEGG
+C857;C857;110C 1168 11AA;C857;110C 1168 11AA; # (졗; 졗; 졗; 졗; 졗; ) HANGUL SYLLABLE JYEGS
+C858;C858;110C 1168 11AB;C858;110C 1168 11AB; # (졘; 졘; 졘; 졘; 졘; ) HANGUL SYLLABLE JYEN
+C859;C859;110C 1168 11AC;C859;110C 1168 11AC; # (졙; 졙; 졙; 졙; 졙; ) HANGUL SYLLABLE JYENJ
+C85A;C85A;110C 1168 11AD;C85A;110C 1168 11AD; # (졚; 졚; 졚; 졚; 졚; ) HANGUL SYLLABLE JYENH
+C85B;C85B;110C 1168 11AE;C85B;110C 1168 11AE; # (졛; 졛; 졛; 졛; 졛; ) HANGUL SYLLABLE JYED
+C85C;C85C;110C 1168 11AF;C85C;110C 1168 11AF; # (졜; 졜; 졜; 졜; 졜; ) HANGUL SYLLABLE JYEL
+C85D;C85D;110C 1168 11B0;C85D;110C 1168 11B0; # (ì¡; ì¡; 졝; ì¡; 졝; ) HANGUL SYLLABLE JYELG
+C85E;C85E;110C 1168 11B1;C85E;110C 1168 11B1; # (졞; 졞; 졞; 졞; 졞; ) HANGUL SYLLABLE JYELM
+C85F;C85F;110C 1168 11B2;C85F;110C 1168 11B2; # (졟; 졟; 졟; 졟; 졟; ) HANGUL SYLLABLE JYELB
+C860;C860;110C 1168 11B3;C860;110C 1168 11B3; # (졠; 졠; 졠; 졠; 졠; ) HANGUL SYLLABLE JYELS
+C861;C861;110C 1168 11B4;C861;110C 1168 11B4; # (졡; 졡; 졡; 졡; 졡; ) HANGUL SYLLABLE JYELT
+C862;C862;110C 1168 11B5;C862;110C 1168 11B5; # (졢; 졢; 졢; 졢; 졢; ) HANGUL SYLLABLE JYELP
+C863;C863;110C 1168 11B6;C863;110C 1168 11B6; # (졣; 졣; 졣; 졣; 졣; ) HANGUL SYLLABLE JYELH
+C864;C864;110C 1168 11B7;C864;110C 1168 11B7; # (졤; 졤; 졤; 졤; 졤; ) HANGUL SYLLABLE JYEM
+C865;C865;110C 1168 11B8;C865;110C 1168 11B8; # (졥; 졥; 졥; 졥; 졥; ) HANGUL SYLLABLE JYEB
+C866;C866;110C 1168 11B9;C866;110C 1168 11B9; # (졦; 졦; 졦; 졦; 졦; ) HANGUL SYLLABLE JYEBS
+C867;C867;110C 1168 11BA;C867;110C 1168 11BA; # (졧; 졧; 졧; 졧; 졧; ) HANGUL SYLLABLE JYES
+C868;C868;110C 1168 11BB;C868;110C 1168 11BB; # (졨; 졨; 졨; 졨; 졨; ) HANGUL SYLLABLE JYESS
+C869;C869;110C 1168 11BC;C869;110C 1168 11BC; # (졩; 졩; 졩; 졩; 졩; ) HANGUL SYLLABLE JYENG
+C86A;C86A;110C 1168 11BD;C86A;110C 1168 11BD; # (졪; 졪; 졪; 졪; 졪; ) HANGUL SYLLABLE JYEJ
+C86B;C86B;110C 1168 11BE;C86B;110C 1168 11BE; # (졫; 졫; 졫; 졫; 졫; ) HANGUL SYLLABLE JYEC
+C86C;C86C;110C 1168 11BF;C86C;110C 1168 11BF; # (졬; 졬; 졬; 졬; 졬; ) HANGUL SYLLABLE JYEK
+C86D;C86D;110C 1168 11C0;C86D;110C 1168 11C0; # (졭; 졭; 졭; 졭; 졭; ) HANGUL SYLLABLE JYET
+C86E;C86E;110C 1168 11C1;C86E;110C 1168 11C1; # (ì¡®; ì¡®; 졔á‡; ì¡®; 졔á‡; ) HANGUL SYLLABLE JYEP
+C86F;C86F;110C 1168 11C2;C86F;110C 1168 11C2; # (졯; 졯; 졯; 졯; 졯; ) HANGUL SYLLABLE JYEH
+C870;C870;110C 1169;C870;110C 1169; # (조; 조; 조; 조; 조; ) HANGUL SYLLABLE JO
+C871;C871;110C 1169 11A8;C871;110C 1169 11A8; # (족; 족; 족; 족; 족; ) HANGUL SYLLABLE JOG
+C872;C872;110C 1169 11A9;C872;110C 1169 11A9; # (졲; 졲; 졲; 졲; 졲; ) HANGUL SYLLABLE JOGG
+C873;C873;110C 1169 11AA;C873;110C 1169 11AA; # (졳; 졳; 졳; 졳; 졳; ) HANGUL SYLLABLE JOGS
+C874;C874;110C 1169 11AB;C874;110C 1169 11AB; # (존; 존; 존; 존; 존; ) HANGUL SYLLABLE JON
+C875;C875;110C 1169 11AC;C875;110C 1169 11AC; # (졵; 졵; 졵; 졵; 졵; ) HANGUL SYLLABLE JONJ
+C876;C876;110C 1169 11AD;C876;110C 1169 11AD; # (졶; 졶; 졶; 졶; 졶; ) HANGUL SYLLABLE JONH
+C877;C877;110C 1169 11AE;C877;110C 1169 11AE; # (졷; 졷; 졷; 졷; 졷; ) HANGUL SYLLABLE JOD
+C878;C878;110C 1169 11AF;C878;110C 1169 11AF; # (졸; 졸; 졸; 졸; 졸; ) HANGUL SYLLABLE JOL
+C879;C879;110C 1169 11B0;C879;110C 1169 11B0; # (졹; 졹; 졹; 졹; 졹; ) HANGUL SYLLABLE JOLG
+C87A;C87A;110C 1169 11B1;C87A;110C 1169 11B1; # (졺; 졺; 졺; 졺; 졺; ) HANGUL SYLLABLE JOLM
+C87B;C87B;110C 1169 11B2;C87B;110C 1169 11B2; # (졻; 졻; 졻; 졻; 졻; ) HANGUL SYLLABLE JOLB
+C87C;C87C;110C 1169 11B3;C87C;110C 1169 11B3; # (졼; 졼; 졼; 졼; 졼; ) HANGUL SYLLABLE JOLS
+C87D;C87D;110C 1169 11B4;C87D;110C 1169 11B4; # (졽; 졽; 졽; 졽; 졽; ) HANGUL SYLLABLE JOLT
+C87E;C87E;110C 1169 11B5;C87E;110C 1169 11B5; # (졾; 졾; 졾; 졾; 졾; ) HANGUL SYLLABLE JOLP
+C87F;C87F;110C 1169 11B6;C87F;110C 1169 11B6; # (졿; 졿; 졿; 졿; 졿; ) HANGUL SYLLABLE JOLH
+C880;C880;110C 1169 11B7;C880;110C 1169 11B7; # (좀; 좀; 좀; 좀; 좀; ) HANGUL SYLLABLE JOM
+C881;C881;110C 1169 11B8;C881;110C 1169 11B8; # (ì¢; ì¢; 좁; ì¢; 좁; ) HANGUL SYLLABLE JOB
+C882;C882;110C 1169 11B9;C882;110C 1169 11B9; # (좂; 좂; 좂; 좂; 좂; ) HANGUL SYLLABLE JOBS
+C883;C883;110C 1169 11BA;C883;110C 1169 11BA; # (좃; 좃; 좃; 좃; 좃; ) HANGUL SYLLABLE JOS
+C884;C884;110C 1169 11BB;C884;110C 1169 11BB; # (좄; 좄; 좄; 좄; 좄; ) HANGUL SYLLABLE JOSS
+C885;C885;110C 1169 11BC;C885;110C 1169 11BC; # (종; 종; 종; 종; 종; ) HANGUL SYLLABLE JONG
+C886;C886;110C 1169 11BD;C886;110C 1169 11BD; # (좆; 좆; 좆; 좆; 좆; ) HANGUL SYLLABLE JOJ
+C887;C887;110C 1169 11BE;C887;110C 1169 11BE; # (좇; 좇; 좇; 좇; 좇; ) HANGUL SYLLABLE JOC
+C888;C888;110C 1169 11BF;C888;110C 1169 11BF; # (좈; 좈; 좈; 좈; 좈; ) HANGUL SYLLABLE JOK
+C889;C889;110C 1169 11C0;C889;110C 1169 11C0; # (좉; 좉; 좉; 좉; 좉; ) HANGUL SYLLABLE JOT
+C88A;C88A;110C 1169 11C1;C88A;110C 1169 11C1; # (좊; 좊; 조á‡; 좊; 조á‡; ) HANGUL SYLLABLE JOP
+C88B;C88B;110C 1169 11C2;C88B;110C 1169 11C2; # (좋; 좋; 좋; 좋; 좋; ) HANGUL SYLLABLE JOH
+C88C;C88C;110C 116A;C88C;110C 116A; # (좌; 좌; 좌; 좌; 좌; ) HANGUL SYLLABLE JWA
+C88D;C88D;110C 116A 11A8;C88D;110C 116A 11A8; # (ì¢; ì¢; 좍; ì¢; 좍; ) HANGUL SYLLABLE JWAG
+C88E;C88E;110C 116A 11A9;C88E;110C 116A 11A9; # (좎; 좎; 좎; 좎; 좎; ) HANGUL SYLLABLE JWAGG
+C88F;C88F;110C 116A 11AA;C88F;110C 116A 11AA; # (ì¢; ì¢; 좏; ì¢; 좏; ) HANGUL SYLLABLE JWAGS
+C890;C890;110C 116A 11AB;C890;110C 116A 11AB; # (ì¢; ì¢; 좐; ì¢; 좐; ) HANGUL SYLLABLE JWAN
+C891;C891;110C 116A 11AC;C891;110C 116A 11AC; # (좑; 좑; 좑; 좑; 좑; ) HANGUL SYLLABLE JWANJ
+C892;C892;110C 116A 11AD;C892;110C 116A 11AD; # (좒; 좒; 좒; 좒; 좒; ) HANGUL SYLLABLE JWANH
+C893;C893;110C 116A 11AE;C893;110C 116A 11AE; # (좓; 좓; 좓; 좓; 좓; ) HANGUL SYLLABLE JWAD
+C894;C894;110C 116A 11AF;C894;110C 116A 11AF; # (좔; 좔; 좔; 좔; 좔; ) HANGUL SYLLABLE JWAL
+C895;C895;110C 116A 11B0;C895;110C 116A 11B0; # (좕; 좕; 좕; 좕; 좕; ) HANGUL SYLLABLE JWALG
+C896;C896;110C 116A 11B1;C896;110C 116A 11B1; # (좖; 좖; 좖; 좖; 좖; ) HANGUL SYLLABLE JWALM
+C897;C897;110C 116A 11B2;C897;110C 116A 11B2; # (좗; 좗; 좗; 좗; 좗; ) HANGUL SYLLABLE JWALB
+C898;C898;110C 116A 11B3;C898;110C 116A 11B3; # (좘; 좘; 좘; 좘; 좘; ) HANGUL SYLLABLE JWALS
+C899;C899;110C 116A 11B4;C899;110C 116A 11B4; # (좙; 좙; 좙; 좙; 좙; ) HANGUL SYLLABLE JWALT
+C89A;C89A;110C 116A 11B5;C89A;110C 116A 11B5; # (좚; 좚; 좚; 좚; 좚; ) HANGUL SYLLABLE JWALP
+C89B;C89B;110C 116A 11B6;C89B;110C 116A 11B6; # (좛; 좛; 좛; 좛; 좛; ) HANGUL SYLLABLE JWALH
+C89C;C89C;110C 116A 11B7;C89C;110C 116A 11B7; # (좜; 좜; 좜; 좜; 좜; ) HANGUL SYLLABLE JWAM
+C89D;C89D;110C 116A 11B8;C89D;110C 116A 11B8; # (ì¢; ì¢; 좝; ì¢; 좝; ) HANGUL SYLLABLE JWAB
+C89E;C89E;110C 116A 11B9;C89E;110C 116A 11B9; # (좞; 좞; 좞; 좞; 좞; ) HANGUL SYLLABLE JWABS
+C89F;C89F;110C 116A 11BA;C89F;110C 116A 11BA; # (좟; 좟; 좟; 좟; 좟; ) HANGUL SYLLABLE JWAS
+C8A0;C8A0;110C 116A 11BB;C8A0;110C 116A 11BB; # (좠; 좠; 좠; 좠; 좠; ) HANGUL SYLLABLE JWASS
+C8A1;C8A1;110C 116A 11BC;C8A1;110C 116A 11BC; # (좡; 좡; 좡; 좡; 좡; ) HANGUL SYLLABLE JWANG
+C8A2;C8A2;110C 116A 11BD;C8A2;110C 116A 11BD; # (좢; 좢; 좢; 좢; 좢; ) HANGUL SYLLABLE JWAJ
+C8A3;C8A3;110C 116A 11BE;C8A3;110C 116A 11BE; # (좣; 좣; 좣; 좣; 좣; ) HANGUL SYLLABLE JWAC
+C8A4;C8A4;110C 116A 11BF;C8A4;110C 116A 11BF; # (좤; 좤; 좤; 좤; 좤; ) HANGUL SYLLABLE JWAK
+C8A5;C8A5;110C 116A 11C0;C8A5;110C 116A 11C0; # (좥; 좥; 좥; 좥; 좥; ) HANGUL SYLLABLE JWAT
+C8A6;C8A6;110C 116A 11C1;C8A6;110C 116A 11C1; # (좦; 좦; 좌á‡; 좦; 좌á‡; ) HANGUL SYLLABLE JWAP
+C8A7;C8A7;110C 116A 11C2;C8A7;110C 116A 11C2; # (좧; 좧; 좧; 좧; 좧; ) HANGUL SYLLABLE JWAH
+C8A8;C8A8;110C 116B;C8A8;110C 116B; # (좨; 좨; 좨; 좨; 좨; ) HANGUL SYLLABLE JWAE
+C8A9;C8A9;110C 116B 11A8;C8A9;110C 116B 11A8; # (좩; 좩; 좩; 좩; 좩; ) HANGUL SYLLABLE JWAEG
+C8AA;C8AA;110C 116B 11A9;C8AA;110C 116B 11A9; # (좪; 좪; 좪; 좪; 좪; ) HANGUL SYLLABLE JWAEGG
+C8AB;C8AB;110C 116B 11AA;C8AB;110C 116B 11AA; # (좫; 좫; 좫; 좫; 좫; ) HANGUL SYLLABLE JWAEGS
+C8AC;C8AC;110C 116B 11AB;C8AC;110C 116B 11AB; # (좬; 좬; 좬; 좬; 좬; ) HANGUL SYLLABLE JWAEN
+C8AD;C8AD;110C 116B 11AC;C8AD;110C 116B 11AC; # (좭; 좭; 좭; 좭; 좭; ) HANGUL SYLLABLE JWAENJ
+C8AE;C8AE;110C 116B 11AD;C8AE;110C 116B 11AD; # (좮; 좮; 좮; 좮; 좮; ) HANGUL SYLLABLE JWAENH
+C8AF;C8AF;110C 116B 11AE;C8AF;110C 116B 11AE; # (좯; 좯; 좯; 좯; 좯; ) HANGUL SYLLABLE JWAED
+C8B0;C8B0;110C 116B 11AF;C8B0;110C 116B 11AF; # (좰; 좰; 좰; 좰; 좰; ) HANGUL SYLLABLE JWAEL
+C8B1;C8B1;110C 116B 11B0;C8B1;110C 116B 11B0; # (좱; 좱; 좱; 좱; 좱; ) HANGUL SYLLABLE JWAELG
+C8B2;C8B2;110C 116B 11B1;C8B2;110C 116B 11B1; # (좲; 좲; 좲; 좲; 좲; ) HANGUL SYLLABLE JWAELM
+C8B3;C8B3;110C 116B 11B2;C8B3;110C 116B 11B2; # (좳; 좳; 좳; 좳; 좳; ) HANGUL SYLLABLE JWAELB
+C8B4;C8B4;110C 116B 11B3;C8B4;110C 116B 11B3; # (좴; 좴; 좴; 좴; 좴; ) HANGUL SYLLABLE JWAELS
+C8B5;C8B5;110C 116B 11B4;C8B5;110C 116B 11B4; # (좵; 좵; 좵; 좵; 좵; ) HANGUL SYLLABLE JWAELT
+C8B6;C8B6;110C 116B 11B5;C8B6;110C 116B 11B5; # (좶; 좶; 좶; 좶; 좶; ) HANGUL SYLLABLE JWAELP
+C8B7;C8B7;110C 116B 11B6;C8B7;110C 116B 11B6; # (좷; 좷; 좷; 좷; 좷; ) HANGUL SYLLABLE JWAELH
+C8B8;C8B8;110C 116B 11B7;C8B8;110C 116B 11B7; # (좸; 좸; 좸; 좸; 좸; ) HANGUL SYLLABLE JWAEM
+C8B9;C8B9;110C 116B 11B8;C8B9;110C 116B 11B8; # (좹; 좹; 좹; 좹; 좹; ) HANGUL SYLLABLE JWAEB
+C8BA;C8BA;110C 116B 11B9;C8BA;110C 116B 11B9; # (좺; 좺; 좺; 좺; 좺; ) HANGUL SYLLABLE JWAEBS
+C8BB;C8BB;110C 116B 11BA;C8BB;110C 116B 11BA; # (좻; 좻; 좻; 좻; 좻; ) HANGUL SYLLABLE JWAES
+C8BC;C8BC;110C 116B 11BB;C8BC;110C 116B 11BB; # (좼; 좼; 좼; 좼; 좼; ) HANGUL SYLLABLE JWAESS
+C8BD;C8BD;110C 116B 11BC;C8BD;110C 116B 11BC; # (좽; 좽; 좽; 좽; 좽; ) HANGUL SYLLABLE JWAENG
+C8BE;C8BE;110C 116B 11BD;C8BE;110C 116B 11BD; # (좾; 좾; 좾; 좾; 좾; ) HANGUL SYLLABLE JWAEJ
+C8BF;C8BF;110C 116B 11BE;C8BF;110C 116B 11BE; # (좿; 좿; 좿; 좿; 좿; ) HANGUL SYLLABLE JWAEC
+C8C0;C8C0;110C 116B 11BF;C8C0;110C 116B 11BF; # (죀; 죀; 죀; 죀; 죀; ) HANGUL SYLLABLE JWAEK
+C8C1;C8C1;110C 116B 11C0;C8C1;110C 116B 11C0; # (ì£; ì£; 죁; ì£; 죁; ) HANGUL SYLLABLE JWAET
+C8C2;C8C2;110C 116B 11C1;C8C2;110C 116B 11C1; # (죂; 죂; 좨á‡; 죂; 좨á‡; ) HANGUL SYLLABLE JWAEP
+C8C3;C8C3;110C 116B 11C2;C8C3;110C 116B 11C2; # (죃; 죃; 죃; 죃; 죃; ) HANGUL SYLLABLE JWAEH
+C8C4;C8C4;110C 116C;C8C4;110C 116C; # (죄; 죄; 죄; 죄; 죄; ) HANGUL SYLLABLE JOE
+C8C5;C8C5;110C 116C 11A8;C8C5;110C 116C 11A8; # (죅; 죅; 죅; 죅; 죅; ) HANGUL SYLLABLE JOEG
+C8C6;C8C6;110C 116C 11A9;C8C6;110C 116C 11A9; # (죆; 죆; 죆; 죆; 죆; ) HANGUL SYLLABLE JOEGG
+C8C7;C8C7;110C 116C 11AA;C8C7;110C 116C 11AA; # (죇; 죇; 죇; 죇; 죇; ) HANGUL SYLLABLE JOEGS
+C8C8;C8C8;110C 116C 11AB;C8C8;110C 116C 11AB; # (죈; 죈; 죈; 죈; 죈; ) HANGUL SYLLABLE JOEN
+C8C9;C8C9;110C 116C 11AC;C8C9;110C 116C 11AC; # (죉; 죉; 죉; 죉; 죉; ) HANGUL SYLLABLE JOENJ
+C8CA;C8CA;110C 116C 11AD;C8CA;110C 116C 11AD; # (죊; 죊; 죊; 죊; 죊; ) HANGUL SYLLABLE JOENH
+C8CB;C8CB;110C 116C 11AE;C8CB;110C 116C 11AE; # (죋; 죋; 죋; 죋; 죋; ) HANGUL SYLLABLE JOED
+C8CC;C8CC;110C 116C 11AF;C8CC;110C 116C 11AF; # (죌; 죌; 죌; 죌; 죌; ) HANGUL SYLLABLE JOEL
+C8CD;C8CD;110C 116C 11B0;C8CD;110C 116C 11B0; # (ì£; ì£; 죍; ì£; 죍; ) HANGUL SYLLABLE JOELG
+C8CE;C8CE;110C 116C 11B1;C8CE;110C 116C 11B1; # (죎; 죎; 죎; 죎; 죎; ) HANGUL SYLLABLE JOELM
+C8CF;C8CF;110C 116C 11B2;C8CF;110C 116C 11B2; # (ì£; ì£; 죏; ì£; 죏; ) HANGUL SYLLABLE JOELB
+C8D0;C8D0;110C 116C 11B3;C8D0;110C 116C 11B3; # (ì£; ì£; 죐; ì£; 죐; ) HANGUL SYLLABLE JOELS
+C8D1;C8D1;110C 116C 11B4;C8D1;110C 116C 11B4; # (죑; 죑; 죑; 죑; 죑; ) HANGUL SYLLABLE JOELT
+C8D2;C8D2;110C 116C 11B5;C8D2;110C 116C 11B5; # (죒; 죒; 죒; 죒; 죒; ) HANGUL SYLLABLE JOELP
+C8D3;C8D3;110C 116C 11B6;C8D3;110C 116C 11B6; # (죓; 죓; 죓; 죓; 죓; ) HANGUL SYLLABLE JOELH
+C8D4;C8D4;110C 116C 11B7;C8D4;110C 116C 11B7; # (죔; 죔; 죔; 죔; 죔; ) HANGUL SYLLABLE JOEM
+C8D5;C8D5;110C 116C 11B8;C8D5;110C 116C 11B8; # (죕; 죕; 죕; 죕; 죕; ) HANGUL SYLLABLE JOEB
+C8D6;C8D6;110C 116C 11B9;C8D6;110C 116C 11B9; # (죖; 죖; 죖; 죖; 죖; ) HANGUL SYLLABLE JOEBS
+C8D7;C8D7;110C 116C 11BA;C8D7;110C 116C 11BA; # (죗; 죗; 죗; 죗; 죗; ) HANGUL SYLLABLE JOES
+C8D8;C8D8;110C 116C 11BB;C8D8;110C 116C 11BB; # (죘; 죘; 죘; 죘; 죘; ) HANGUL SYLLABLE JOESS
+C8D9;C8D9;110C 116C 11BC;C8D9;110C 116C 11BC; # (죙; 죙; 죙; 죙; 죙; ) HANGUL SYLLABLE JOENG
+C8DA;C8DA;110C 116C 11BD;C8DA;110C 116C 11BD; # (죚; 죚; 죚; 죚; 죚; ) HANGUL SYLLABLE JOEJ
+C8DB;C8DB;110C 116C 11BE;C8DB;110C 116C 11BE; # (죛; 죛; 죛; 죛; 죛; ) HANGUL SYLLABLE JOEC
+C8DC;C8DC;110C 116C 11BF;C8DC;110C 116C 11BF; # (죜; 죜; 죜; 죜; 죜; ) HANGUL SYLLABLE JOEK
+C8DD;C8DD;110C 116C 11C0;C8DD;110C 116C 11C0; # (ì£; ì£; 죝; ì£; 죝; ) HANGUL SYLLABLE JOET
+C8DE;C8DE;110C 116C 11C1;C8DE;110C 116C 11C1; # (죞; 죞; 죄á‡; 죞; 죄á‡; ) HANGUL SYLLABLE JOEP
+C8DF;C8DF;110C 116C 11C2;C8DF;110C 116C 11C2; # (죟; 죟; 죟; 죟; 죟; ) HANGUL SYLLABLE JOEH
+C8E0;C8E0;110C 116D;C8E0;110C 116D; # (죠; 죠; 죠; 죠; 죠; ) HANGUL SYLLABLE JYO
+C8E1;C8E1;110C 116D 11A8;C8E1;110C 116D 11A8; # (죡; 죡; 죡; 죡; 죡; ) HANGUL SYLLABLE JYOG
+C8E2;C8E2;110C 116D 11A9;C8E2;110C 116D 11A9; # (죢; 죢; 죢; 죢; 죢; ) HANGUL SYLLABLE JYOGG
+C8E3;C8E3;110C 116D 11AA;C8E3;110C 116D 11AA; # (죣; 죣; 죣; 죣; 죣; ) HANGUL SYLLABLE JYOGS
+C8E4;C8E4;110C 116D 11AB;C8E4;110C 116D 11AB; # (죤; 죤; 죤; 죤; 죤; ) HANGUL SYLLABLE JYON
+C8E5;C8E5;110C 116D 11AC;C8E5;110C 116D 11AC; # (죥; 죥; 죥; 죥; 죥; ) HANGUL SYLLABLE JYONJ
+C8E6;C8E6;110C 116D 11AD;C8E6;110C 116D 11AD; # (죦; 죦; 죦; 죦; 죦; ) HANGUL SYLLABLE JYONH
+C8E7;C8E7;110C 116D 11AE;C8E7;110C 116D 11AE; # (죧; 죧; 죧; 죧; 죧; ) HANGUL SYLLABLE JYOD
+C8E8;C8E8;110C 116D 11AF;C8E8;110C 116D 11AF; # (죨; 죨; 죨; 죨; 죨; ) HANGUL SYLLABLE JYOL
+C8E9;C8E9;110C 116D 11B0;C8E9;110C 116D 11B0; # (죩; 죩; 죩; 죩; 죩; ) HANGUL SYLLABLE JYOLG
+C8EA;C8EA;110C 116D 11B1;C8EA;110C 116D 11B1; # (죪; 죪; 죪; 죪; 죪; ) HANGUL SYLLABLE JYOLM
+C8EB;C8EB;110C 116D 11B2;C8EB;110C 116D 11B2; # (죫; 죫; 죫; 죫; 죫; ) HANGUL SYLLABLE JYOLB
+C8EC;C8EC;110C 116D 11B3;C8EC;110C 116D 11B3; # (죬; 죬; 죬; 죬; 죬; ) HANGUL SYLLABLE JYOLS
+C8ED;C8ED;110C 116D 11B4;C8ED;110C 116D 11B4; # (죭; 죭; 죭; 죭; 죭; ) HANGUL SYLLABLE JYOLT
+C8EE;C8EE;110C 116D 11B5;C8EE;110C 116D 11B5; # (죮; 죮; 죮; 죮; 죮; ) HANGUL SYLLABLE JYOLP
+C8EF;C8EF;110C 116D 11B6;C8EF;110C 116D 11B6; # (죯; 죯; 죯; 죯; 죯; ) HANGUL SYLLABLE JYOLH
+C8F0;C8F0;110C 116D 11B7;C8F0;110C 116D 11B7; # (죰; 죰; 죰; 죰; 죰; ) HANGUL SYLLABLE JYOM
+C8F1;C8F1;110C 116D 11B8;C8F1;110C 116D 11B8; # (죱; 죱; 죱; 죱; 죱; ) HANGUL SYLLABLE JYOB
+C8F2;C8F2;110C 116D 11B9;C8F2;110C 116D 11B9; # (죲; 죲; 죲; 죲; 죲; ) HANGUL SYLLABLE JYOBS
+C8F3;C8F3;110C 116D 11BA;C8F3;110C 116D 11BA; # (죳; 죳; 죳; 죳; 죳; ) HANGUL SYLLABLE JYOS
+C8F4;C8F4;110C 116D 11BB;C8F4;110C 116D 11BB; # (죴; 죴; 죴; 죴; 죴; ) HANGUL SYLLABLE JYOSS
+C8F5;C8F5;110C 116D 11BC;C8F5;110C 116D 11BC; # (죵; 죵; 죵; 죵; 죵; ) HANGUL SYLLABLE JYONG
+C8F6;C8F6;110C 116D 11BD;C8F6;110C 116D 11BD; # (죶; 죶; 죶; 죶; 죶; ) HANGUL SYLLABLE JYOJ
+C8F7;C8F7;110C 116D 11BE;C8F7;110C 116D 11BE; # (죷; 죷; 죷; 죷; 죷; ) HANGUL SYLLABLE JYOC
+C8F8;C8F8;110C 116D 11BF;C8F8;110C 116D 11BF; # (죸; 죸; 죸; 죸; 죸; ) HANGUL SYLLABLE JYOK
+C8F9;C8F9;110C 116D 11C0;C8F9;110C 116D 11C0; # (죹; 죹; 죹; 죹; 죹; ) HANGUL SYLLABLE JYOT
+C8FA;C8FA;110C 116D 11C1;C8FA;110C 116D 11C1; # (죺; 죺; 죠á‡; 죺; 죠á‡; ) HANGUL SYLLABLE JYOP
+C8FB;C8FB;110C 116D 11C2;C8FB;110C 116D 11C2; # (죻; 죻; 죻; 죻; 죻; ) HANGUL SYLLABLE JYOH
+C8FC;C8FC;110C 116E;C8FC;110C 116E; # (주; 주; 주; 주; 주; ) HANGUL SYLLABLE JU
+C8FD;C8FD;110C 116E 11A8;C8FD;110C 116E 11A8; # (죽; 죽; 죽; 죽; 죽; ) HANGUL SYLLABLE JUG
+C8FE;C8FE;110C 116E 11A9;C8FE;110C 116E 11A9; # (죾; 죾; 죾; 죾; 죾; ) HANGUL SYLLABLE JUGG
+C8FF;C8FF;110C 116E 11AA;C8FF;110C 116E 11AA; # (죿; 죿; 죿; 죿; 죿; ) HANGUL SYLLABLE JUGS
+C900;C900;110C 116E 11AB;C900;110C 116E 11AB; # (준; 준; 준; 준; 준; ) HANGUL SYLLABLE JUN
+C901;C901;110C 116E 11AC;C901;110C 116E 11AC; # (ì¤; ì¤; 줁; ì¤; 줁; ) HANGUL SYLLABLE JUNJ
+C902;C902;110C 116E 11AD;C902;110C 116E 11AD; # (줂; 줂; 줂; 줂; 줂; ) HANGUL SYLLABLE JUNH
+C903;C903;110C 116E 11AE;C903;110C 116E 11AE; # (줃; 줃; 줃; 줃; 줃; ) HANGUL SYLLABLE JUD
+C904;C904;110C 116E 11AF;C904;110C 116E 11AF; # (줄; 줄; 줄; 줄; 줄; ) HANGUL SYLLABLE JUL
+C905;C905;110C 116E 11B0;C905;110C 116E 11B0; # (줅; 줅; 줅; 줅; 줅; ) HANGUL SYLLABLE JULG
+C906;C906;110C 116E 11B1;C906;110C 116E 11B1; # (줆; 줆; 줆; 줆; 줆; ) HANGUL SYLLABLE JULM
+C907;C907;110C 116E 11B2;C907;110C 116E 11B2; # (줇; 줇; 줇; 줇; 줇; ) HANGUL SYLLABLE JULB
+C908;C908;110C 116E 11B3;C908;110C 116E 11B3; # (줈; 줈; 줈; 줈; 줈; ) HANGUL SYLLABLE JULS
+C909;C909;110C 116E 11B4;C909;110C 116E 11B4; # (줉; 줉; 줉; 줉; 줉; ) HANGUL SYLLABLE JULT
+C90A;C90A;110C 116E 11B5;C90A;110C 116E 11B5; # (줊; 줊; 줊; 줊; 줊; ) HANGUL SYLLABLE JULP
+C90B;C90B;110C 116E 11B6;C90B;110C 116E 11B6; # (줋; 줋; 줋; 줋; 줋; ) HANGUL SYLLABLE JULH
+C90C;C90C;110C 116E 11B7;C90C;110C 116E 11B7; # (줌; 줌; 줌; 줌; 줌; ) HANGUL SYLLABLE JUM
+C90D;C90D;110C 116E 11B8;C90D;110C 116E 11B8; # (ì¤; ì¤; 줍; ì¤; 줍; ) HANGUL SYLLABLE JUB
+C90E;C90E;110C 116E 11B9;C90E;110C 116E 11B9; # (줎; 줎; 줎; 줎; 줎; ) HANGUL SYLLABLE JUBS
+C90F;C90F;110C 116E 11BA;C90F;110C 116E 11BA; # (ì¤; ì¤; 줏; ì¤; 줏; ) HANGUL SYLLABLE JUS
+C910;C910;110C 116E 11BB;C910;110C 116E 11BB; # (ì¤; ì¤; 줐; ì¤; 줐; ) HANGUL SYLLABLE JUSS
+C911;C911;110C 116E 11BC;C911;110C 116E 11BC; # (중; 중; 중; 중; 중; ) HANGUL SYLLABLE JUNG
+C912;C912;110C 116E 11BD;C912;110C 116E 11BD; # (줒; 줒; 줒; 줒; 줒; ) HANGUL SYLLABLE JUJ
+C913;C913;110C 116E 11BE;C913;110C 116E 11BE; # (줓; 줓; 줓; 줓; 줓; ) HANGUL SYLLABLE JUC
+C914;C914;110C 116E 11BF;C914;110C 116E 11BF; # (줔; 줔; 줔; 줔; 줔; ) HANGUL SYLLABLE JUK
+C915;C915;110C 116E 11C0;C915;110C 116E 11C0; # (줕; 줕; 줕; 줕; 줕; ) HANGUL SYLLABLE JUT
+C916;C916;110C 116E 11C1;C916;110C 116E 11C1; # (줖; 줖; 주á‡; 줖; 주á‡; ) HANGUL SYLLABLE JUP
+C917;C917;110C 116E 11C2;C917;110C 116E 11C2; # (줗; 줗; 줗; 줗; 줗; ) HANGUL SYLLABLE JUH
+C918;C918;110C 116F;C918;110C 116F; # (줘; 줘; 줘; 줘; 줘; ) HANGUL SYLLABLE JWEO
+C919;C919;110C 116F 11A8;C919;110C 116F 11A8; # (줙; 줙; 줙; 줙; 줙; ) HANGUL SYLLABLE JWEOG
+C91A;C91A;110C 116F 11A9;C91A;110C 116F 11A9; # (줚; 줚; 줚; 줚; 줚; ) HANGUL SYLLABLE JWEOGG
+C91B;C91B;110C 116F 11AA;C91B;110C 116F 11AA; # (줛; 줛; 줛; 줛; 줛; ) HANGUL SYLLABLE JWEOGS
+C91C;C91C;110C 116F 11AB;C91C;110C 116F 11AB; # (줜; 줜; 줜; 줜; 줜; ) HANGUL SYLLABLE JWEON
+C91D;C91D;110C 116F 11AC;C91D;110C 116F 11AC; # (ì¤; ì¤; 줝; ì¤; 줝; ) HANGUL SYLLABLE JWEONJ
+C91E;C91E;110C 116F 11AD;C91E;110C 116F 11AD; # (줞; 줞; 줞; 줞; 줞; ) HANGUL SYLLABLE JWEONH
+C91F;C91F;110C 116F 11AE;C91F;110C 116F 11AE; # (줟; 줟; 줟; 줟; 줟; ) HANGUL SYLLABLE JWEOD
+C920;C920;110C 116F 11AF;C920;110C 116F 11AF; # (줠; 줠; 줠; 줠; 줠; ) HANGUL SYLLABLE JWEOL
+C921;C921;110C 116F 11B0;C921;110C 116F 11B0; # (줡; 줡; 줡; 줡; 줡; ) HANGUL SYLLABLE JWEOLG
+C922;C922;110C 116F 11B1;C922;110C 116F 11B1; # (줢; 줢; 줢; 줢; 줢; ) HANGUL SYLLABLE JWEOLM
+C923;C923;110C 116F 11B2;C923;110C 116F 11B2; # (줣; 줣; 줣; 줣; 줣; ) HANGUL SYLLABLE JWEOLB
+C924;C924;110C 116F 11B3;C924;110C 116F 11B3; # (줤; 줤; 줤; 줤; 줤; ) HANGUL SYLLABLE JWEOLS
+C925;C925;110C 116F 11B4;C925;110C 116F 11B4; # (줥; 줥; 줥; 줥; 줥; ) HANGUL SYLLABLE JWEOLT
+C926;C926;110C 116F 11B5;C926;110C 116F 11B5; # (줦; 줦; 줦; 줦; 줦; ) HANGUL SYLLABLE JWEOLP
+C927;C927;110C 116F 11B6;C927;110C 116F 11B6; # (줧; 줧; 줧; 줧; 줧; ) HANGUL SYLLABLE JWEOLH
+C928;C928;110C 116F 11B7;C928;110C 116F 11B7; # (줨; 줨; 줨; 줨; 줨; ) HANGUL SYLLABLE JWEOM
+C929;C929;110C 116F 11B8;C929;110C 116F 11B8; # (줩; 줩; 줩; 줩; 줩; ) HANGUL SYLLABLE JWEOB
+C92A;C92A;110C 116F 11B9;C92A;110C 116F 11B9; # (줪; 줪; 줪; 줪; 줪; ) HANGUL SYLLABLE JWEOBS
+C92B;C92B;110C 116F 11BA;C92B;110C 116F 11BA; # (줫; 줫; 줫; 줫; 줫; ) HANGUL SYLLABLE JWEOS
+C92C;C92C;110C 116F 11BB;C92C;110C 116F 11BB; # (줬; 줬; 줬; 줬; 줬; ) HANGUL SYLLABLE JWEOSS
+C92D;C92D;110C 116F 11BC;C92D;110C 116F 11BC; # (줭; 줭; 줭; 줭; 줭; ) HANGUL SYLLABLE JWEONG
+C92E;C92E;110C 116F 11BD;C92E;110C 116F 11BD; # (줮; 줮; 줮; 줮; 줮; ) HANGUL SYLLABLE JWEOJ
+C92F;C92F;110C 116F 11BE;C92F;110C 116F 11BE; # (줯; 줯; 줯; 줯; 줯; ) HANGUL SYLLABLE JWEOC
+C930;C930;110C 116F 11BF;C930;110C 116F 11BF; # (줰; 줰; 줰; 줰; 줰; ) HANGUL SYLLABLE JWEOK
+C931;C931;110C 116F 11C0;C931;110C 116F 11C0; # (줱; 줱; 줱; 줱; 줱; ) HANGUL SYLLABLE JWEOT
+C932;C932;110C 116F 11C1;C932;110C 116F 11C1; # (줲; 줲; 줘á‡; 줲; 줘á‡; ) HANGUL SYLLABLE JWEOP
+C933;C933;110C 116F 11C2;C933;110C 116F 11C2; # (줳; 줳; 줳; 줳; 줳; ) HANGUL SYLLABLE JWEOH
+C934;C934;110C 1170;C934;110C 1170; # (줴; 줴; 줴; 줴; 줴; ) HANGUL SYLLABLE JWE
+C935;C935;110C 1170 11A8;C935;110C 1170 11A8; # (줵; 줵; 줵; 줵; 줵; ) HANGUL SYLLABLE JWEG
+C936;C936;110C 1170 11A9;C936;110C 1170 11A9; # (줶; 줶; 줶; 줶; 줶; ) HANGUL SYLLABLE JWEGG
+C937;C937;110C 1170 11AA;C937;110C 1170 11AA; # (줷; 줷; 줷; 줷; 줷; ) HANGUL SYLLABLE JWEGS
+C938;C938;110C 1170 11AB;C938;110C 1170 11AB; # (줸; 줸; 줸; 줸; 줸; ) HANGUL SYLLABLE JWEN
+C939;C939;110C 1170 11AC;C939;110C 1170 11AC; # (줹; 줹; 줹; 줹; 줹; ) HANGUL SYLLABLE JWENJ
+C93A;C93A;110C 1170 11AD;C93A;110C 1170 11AD; # (줺; 줺; 줺; 줺; 줺; ) HANGUL SYLLABLE JWENH
+C93B;C93B;110C 1170 11AE;C93B;110C 1170 11AE; # (줻; 줻; 줻; 줻; 줻; ) HANGUL SYLLABLE JWED
+C93C;C93C;110C 1170 11AF;C93C;110C 1170 11AF; # (줼; 줼; 줼; 줼; 줼; ) HANGUL SYLLABLE JWEL
+C93D;C93D;110C 1170 11B0;C93D;110C 1170 11B0; # (줽; 줽; 줽; 줽; 줽; ) HANGUL SYLLABLE JWELG
+C93E;C93E;110C 1170 11B1;C93E;110C 1170 11B1; # (줾; 줾; 줾; 줾; 줾; ) HANGUL SYLLABLE JWELM
+C93F;C93F;110C 1170 11B2;C93F;110C 1170 11B2; # (줿; 줿; 줿; 줿; 줿; ) HANGUL SYLLABLE JWELB
+C940;C940;110C 1170 11B3;C940;110C 1170 11B3; # (쥀; 쥀; 쥀; 쥀; 쥀; ) HANGUL SYLLABLE JWELS
+C941;C941;110C 1170 11B4;C941;110C 1170 11B4; # (ì¥; ì¥; 쥁; ì¥; 쥁; ) HANGUL SYLLABLE JWELT
+C942;C942;110C 1170 11B5;C942;110C 1170 11B5; # (쥂; 쥂; 쥂; 쥂; 쥂; ) HANGUL SYLLABLE JWELP
+C943;C943;110C 1170 11B6;C943;110C 1170 11B6; # (쥃; 쥃; 쥃; 쥃; 쥃; ) HANGUL SYLLABLE JWELH
+C944;C944;110C 1170 11B7;C944;110C 1170 11B7; # (쥄; 쥄; 쥄; 쥄; 쥄; ) HANGUL SYLLABLE JWEM
+C945;C945;110C 1170 11B8;C945;110C 1170 11B8; # (쥅; 쥅; 쥅; 쥅; 쥅; ) HANGUL SYLLABLE JWEB
+C946;C946;110C 1170 11B9;C946;110C 1170 11B9; # (쥆; 쥆; 쥆; 쥆; 쥆; ) HANGUL SYLLABLE JWEBS
+C947;C947;110C 1170 11BA;C947;110C 1170 11BA; # (쥇; 쥇; 쥇; 쥇; 쥇; ) HANGUL SYLLABLE JWES
+C948;C948;110C 1170 11BB;C948;110C 1170 11BB; # (쥈; 쥈; 쥈; 쥈; 쥈; ) HANGUL SYLLABLE JWESS
+C949;C949;110C 1170 11BC;C949;110C 1170 11BC; # (쥉; 쥉; 쥉; 쥉; 쥉; ) HANGUL SYLLABLE JWENG
+C94A;C94A;110C 1170 11BD;C94A;110C 1170 11BD; # (쥊; 쥊; 쥊; 쥊; 쥊; ) HANGUL SYLLABLE JWEJ
+C94B;C94B;110C 1170 11BE;C94B;110C 1170 11BE; # (쥋; 쥋; 쥋; 쥋; 쥋; ) HANGUL SYLLABLE JWEC
+C94C;C94C;110C 1170 11BF;C94C;110C 1170 11BF; # (쥌; 쥌; 쥌; 쥌; 쥌; ) HANGUL SYLLABLE JWEK
+C94D;C94D;110C 1170 11C0;C94D;110C 1170 11C0; # (ì¥; ì¥; 쥍; ì¥; 쥍; ) HANGUL SYLLABLE JWET
+C94E;C94E;110C 1170 11C1;C94E;110C 1170 11C1; # (쥎; 쥎; 줴á‡; 쥎; 줴á‡; ) HANGUL SYLLABLE JWEP
+C94F;C94F;110C 1170 11C2;C94F;110C 1170 11C2; # (ì¥; ì¥; 쥏; ì¥; 쥏; ) HANGUL SYLLABLE JWEH
+C950;C950;110C 1171;C950;110C 1171; # (ì¥; ì¥; 쥐; ì¥; 쥐; ) HANGUL SYLLABLE JWI
+C951;C951;110C 1171 11A8;C951;110C 1171 11A8; # (쥑; 쥑; 쥑; 쥑; 쥑; ) HANGUL SYLLABLE JWIG
+C952;C952;110C 1171 11A9;C952;110C 1171 11A9; # (쥒; 쥒; 쥒; 쥒; 쥒; ) HANGUL SYLLABLE JWIGG
+C953;C953;110C 1171 11AA;C953;110C 1171 11AA; # (쥓; 쥓; 쥓; 쥓; 쥓; ) HANGUL SYLLABLE JWIGS
+C954;C954;110C 1171 11AB;C954;110C 1171 11AB; # (쥔; 쥔; 쥔; 쥔; 쥔; ) HANGUL SYLLABLE JWIN
+C955;C955;110C 1171 11AC;C955;110C 1171 11AC; # (쥕; 쥕; 쥕; 쥕; 쥕; ) HANGUL SYLLABLE JWINJ
+C956;C956;110C 1171 11AD;C956;110C 1171 11AD; # (쥖; 쥖; 쥖; 쥖; 쥖; ) HANGUL SYLLABLE JWINH
+C957;C957;110C 1171 11AE;C957;110C 1171 11AE; # (쥗; 쥗; 쥗; 쥗; 쥗; ) HANGUL SYLLABLE JWID
+C958;C958;110C 1171 11AF;C958;110C 1171 11AF; # (쥘; 쥘; 쥘; 쥘; 쥘; ) HANGUL SYLLABLE JWIL
+C959;C959;110C 1171 11B0;C959;110C 1171 11B0; # (쥙; 쥙; 쥙; 쥙; 쥙; ) HANGUL SYLLABLE JWILG
+C95A;C95A;110C 1171 11B1;C95A;110C 1171 11B1; # (쥚; 쥚; 쥚; 쥚; 쥚; ) HANGUL SYLLABLE JWILM
+C95B;C95B;110C 1171 11B2;C95B;110C 1171 11B2; # (쥛; 쥛; 쥛; 쥛; 쥛; ) HANGUL SYLLABLE JWILB
+C95C;C95C;110C 1171 11B3;C95C;110C 1171 11B3; # (쥜; 쥜; 쥜; 쥜; 쥜; ) HANGUL SYLLABLE JWILS
+C95D;C95D;110C 1171 11B4;C95D;110C 1171 11B4; # (ì¥; ì¥; 쥝; ì¥; 쥝; ) HANGUL SYLLABLE JWILT
+C95E;C95E;110C 1171 11B5;C95E;110C 1171 11B5; # (쥞; 쥞; 쥞; 쥞; 쥞; ) HANGUL SYLLABLE JWILP
+C95F;C95F;110C 1171 11B6;C95F;110C 1171 11B6; # (쥟; 쥟; 쥟; 쥟; 쥟; ) HANGUL SYLLABLE JWILH
+C960;C960;110C 1171 11B7;C960;110C 1171 11B7; # (쥠; 쥠; 쥠; 쥠; 쥠; ) HANGUL SYLLABLE JWIM
+C961;C961;110C 1171 11B8;C961;110C 1171 11B8; # (쥡; 쥡; 쥡; 쥡; 쥡; ) HANGUL SYLLABLE JWIB
+C962;C962;110C 1171 11B9;C962;110C 1171 11B9; # (쥢; 쥢; 쥢; 쥢; 쥢; ) HANGUL SYLLABLE JWIBS
+C963;C963;110C 1171 11BA;C963;110C 1171 11BA; # (쥣; 쥣; 쥣; 쥣; 쥣; ) HANGUL SYLLABLE JWIS
+C964;C964;110C 1171 11BB;C964;110C 1171 11BB; # (쥤; 쥤; 쥤; 쥤; 쥤; ) HANGUL SYLLABLE JWISS
+C965;C965;110C 1171 11BC;C965;110C 1171 11BC; # (쥥; 쥥; 쥥; 쥥; 쥥; ) HANGUL SYLLABLE JWING
+C966;C966;110C 1171 11BD;C966;110C 1171 11BD; # (쥦; 쥦; 쥦; 쥦; 쥦; ) HANGUL SYLLABLE JWIJ
+C967;C967;110C 1171 11BE;C967;110C 1171 11BE; # (쥧; 쥧; 쥧; 쥧; 쥧; ) HANGUL SYLLABLE JWIC
+C968;C968;110C 1171 11BF;C968;110C 1171 11BF; # (쥨; 쥨; 쥨; 쥨; 쥨; ) HANGUL SYLLABLE JWIK
+C969;C969;110C 1171 11C0;C969;110C 1171 11C0; # (쥩; 쥩; 쥩; 쥩; 쥩; ) HANGUL SYLLABLE JWIT
+C96A;C96A;110C 1171 11C1;C96A;110C 1171 11C1; # (쥪; 쥪; 쥐á‡; 쥪; 쥐á‡; ) HANGUL SYLLABLE JWIP
+C96B;C96B;110C 1171 11C2;C96B;110C 1171 11C2; # (쥫; 쥫; 쥫; 쥫; 쥫; ) HANGUL SYLLABLE JWIH
+C96C;C96C;110C 1172;C96C;110C 1172; # (쥬; 쥬; 쥬; 쥬; 쥬; ) HANGUL SYLLABLE JYU
+C96D;C96D;110C 1172 11A8;C96D;110C 1172 11A8; # (쥭; 쥭; 쥭; 쥭; 쥭; ) HANGUL SYLLABLE JYUG
+C96E;C96E;110C 1172 11A9;C96E;110C 1172 11A9; # (쥮; 쥮; 쥮; 쥮; 쥮; ) HANGUL SYLLABLE JYUGG
+C96F;C96F;110C 1172 11AA;C96F;110C 1172 11AA; # (쥯; 쥯; 쥯; 쥯; 쥯; ) HANGUL SYLLABLE JYUGS
+C970;C970;110C 1172 11AB;C970;110C 1172 11AB; # (쥰; 쥰; 쥰; 쥰; 쥰; ) HANGUL SYLLABLE JYUN
+C971;C971;110C 1172 11AC;C971;110C 1172 11AC; # (쥱; 쥱; 쥱; 쥱; 쥱; ) HANGUL SYLLABLE JYUNJ
+C972;C972;110C 1172 11AD;C972;110C 1172 11AD; # (쥲; 쥲; 쥲; 쥲; 쥲; ) HANGUL SYLLABLE JYUNH
+C973;C973;110C 1172 11AE;C973;110C 1172 11AE; # (쥳; 쥳; 쥳; 쥳; 쥳; ) HANGUL SYLLABLE JYUD
+C974;C974;110C 1172 11AF;C974;110C 1172 11AF; # (쥴; 쥴; 쥴; 쥴; 쥴; ) HANGUL SYLLABLE JYUL
+C975;C975;110C 1172 11B0;C975;110C 1172 11B0; # (쥵; 쥵; 쥵; 쥵; 쥵; ) HANGUL SYLLABLE JYULG
+C976;C976;110C 1172 11B1;C976;110C 1172 11B1; # (쥶; 쥶; 쥶; 쥶; 쥶; ) HANGUL SYLLABLE JYULM
+C977;C977;110C 1172 11B2;C977;110C 1172 11B2; # (쥷; 쥷; 쥷; 쥷; 쥷; ) HANGUL SYLLABLE JYULB
+C978;C978;110C 1172 11B3;C978;110C 1172 11B3; # (쥸; 쥸; 쥸; 쥸; 쥸; ) HANGUL SYLLABLE JYULS
+C979;C979;110C 1172 11B4;C979;110C 1172 11B4; # (쥹; 쥹; 쥹; 쥹; 쥹; ) HANGUL SYLLABLE JYULT
+C97A;C97A;110C 1172 11B5;C97A;110C 1172 11B5; # (쥺; 쥺; 쥺; 쥺; 쥺; ) HANGUL SYLLABLE JYULP
+C97B;C97B;110C 1172 11B6;C97B;110C 1172 11B6; # (쥻; 쥻; 쥻; 쥻; 쥻; ) HANGUL SYLLABLE JYULH
+C97C;C97C;110C 1172 11B7;C97C;110C 1172 11B7; # (쥼; 쥼; 쥼; 쥼; 쥼; ) HANGUL SYLLABLE JYUM
+C97D;C97D;110C 1172 11B8;C97D;110C 1172 11B8; # (쥽; 쥽; 쥽; 쥽; 쥽; ) HANGUL SYLLABLE JYUB
+C97E;C97E;110C 1172 11B9;C97E;110C 1172 11B9; # (쥾; 쥾; 쥾; 쥾; 쥾; ) HANGUL SYLLABLE JYUBS
+C97F;C97F;110C 1172 11BA;C97F;110C 1172 11BA; # (쥿; 쥿; 쥿; 쥿; 쥿; ) HANGUL SYLLABLE JYUS
+C980;C980;110C 1172 11BB;C980;110C 1172 11BB; # (즀; 즀; 즀; 즀; 즀; ) HANGUL SYLLABLE JYUSS
+C981;C981;110C 1172 11BC;C981;110C 1172 11BC; # (ì¦; ì¦; 즁; ì¦; 즁; ) HANGUL SYLLABLE JYUNG
+C982;C982;110C 1172 11BD;C982;110C 1172 11BD; # (즂; 즂; 즂; 즂; 즂; ) HANGUL SYLLABLE JYUJ
+C983;C983;110C 1172 11BE;C983;110C 1172 11BE; # (즃; 즃; 즃; 즃; 즃; ) HANGUL SYLLABLE JYUC
+C984;C984;110C 1172 11BF;C984;110C 1172 11BF; # (즄; 즄; 즄; 즄; 즄; ) HANGUL SYLLABLE JYUK
+C985;C985;110C 1172 11C0;C985;110C 1172 11C0; # (즅; 즅; 즅; 즅; 즅; ) HANGUL SYLLABLE JYUT
+C986;C986;110C 1172 11C1;C986;110C 1172 11C1; # (즆; 즆; 쥬á‡; 즆; 쥬á‡; ) HANGUL SYLLABLE JYUP
+C987;C987;110C 1172 11C2;C987;110C 1172 11C2; # (즇; 즇; 즇; 즇; 즇; ) HANGUL SYLLABLE JYUH
+C988;C988;110C 1173;C988;110C 1173; # (즈; 즈; 즈; 즈; 즈; ) HANGUL SYLLABLE JEU
+C989;C989;110C 1173 11A8;C989;110C 1173 11A8; # (즉; 즉; 즉; 즉; 즉; ) HANGUL SYLLABLE JEUG
+C98A;C98A;110C 1173 11A9;C98A;110C 1173 11A9; # (즊; 즊; 즊; 즊; 즊; ) HANGUL SYLLABLE JEUGG
+C98B;C98B;110C 1173 11AA;C98B;110C 1173 11AA; # (즋; 즋; 즋; 즋; 즋; ) HANGUL SYLLABLE JEUGS
+C98C;C98C;110C 1173 11AB;C98C;110C 1173 11AB; # (즌; 즌; 즌; 즌; 즌; ) HANGUL SYLLABLE JEUN
+C98D;C98D;110C 1173 11AC;C98D;110C 1173 11AC; # (ì¦; ì¦; 즍; ì¦; 즍; ) HANGUL SYLLABLE JEUNJ
+C98E;C98E;110C 1173 11AD;C98E;110C 1173 11AD; # (즎; 즎; 즎; 즎; 즎; ) HANGUL SYLLABLE JEUNH
+C98F;C98F;110C 1173 11AE;C98F;110C 1173 11AE; # (ì¦; ì¦; 즏; ì¦; 즏; ) HANGUL SYLLABLE JEUD
+C990;C990;110C 1173 11AF;C990;110C 1173 11AF; # (ì¦; ì¦; 즐; ì¦; 즐; ) HANGUL SYLLABLE JEUL
+C991;C991;110C 1173 11B0;C991;110C 1173 11B0; # (즑; 즑; 즑; 즑; 즑; ) HANGUL SYLLABLE JEULG
+C992;C992;110C 1173 11B1;C992;110C 1173 11B1; # (즒; 즒; 즒; 즒; 즒; ) HANGUL SYLLABLE JEULM
+C993;C993;110C 1173 11B2;C993;110C 1173 11B2; # (즓; 즓; 즓; 즓; 즓; ) HANGUL SYLLABLE JEULB
+C994;C994;110C 1173 11B3;C994;110C 1173 11B3; # (즔; 즔; 즔; 즔; 즔; ) HANGUL SYLLABLE JEULS
+C995;C995;110C 1173 11B4;C995;110C 1173 11B4; # (즕; 즕; 즕; 즕; 즕; ) HANGUL SYLLABLE JEULT
+C996;C996;110C 1173 11B5;C996;110C 1173 11B5; # (즖; 즖; 즖; 즖; 즖; ) HANGUL SYLLABLE JEULP
+C997;C997;110C 1173 11B6;C997;110C 1173 11B6; # (즗; 즗; 즗; 즗; 즗; ) HANGUL SYLLABLE JEULH
+C998;C998;110C 1173 11B7;C998;110C 1173 11B7; # (즘; 즘; 즘; 즘; 즘; ) HANGUL SYLLABLE JEUM
+C999;C999;110C 1173 11B8;C999;110C 1173 11B8; # (즙; 즙; 즙; 즙; 즙; ) HANGUL SYLLABLE JEUB
+C99A;C99A;110C 1173 11B9;C99A;110C 1173 11B9; # (즚; 즚; 즚; 즚; 즚; ) HANGUL SYLLABLE JEUBS
+C99B;C99B;110C 1173 11BA;C99B;110C 1173 11BA; # (즛; 즛; 즛; 즛; 즛; ) HANGUL SYLLABLE JEUS
+C99C;C99C;110C 1173 11BB;C99C;110C 1173 11BB; # (즜; 즜; 즜; 즜; 즜; ) HANGUL SYLLABLE JEUSS
+C99D;C99D;110C 1173 11BC;C99D;110C 1173 11BC; # (ì¦; ì¦; 증; ì¦; 증; ) HANGUL SYLLABLE JEUNG
+C99E;C99E;110C 1173 11BD;C99E;110C 1173 11BD; # (즞; 즞; 즞; 즞; 즞; ) HANGUL SYLLABLE JEUJ
+C99F;C99F;110C 1173 11BE;C99F;110C 1173 11BE; # (즟; 즟; 즟; 즟; 즟; ) HANGUL SYLLABLE JEUC
+C9A0;C9A0;110C 1173 11BF;C9A0;110C 1173 11BF; # (즠; 즠; 즠; 즠; 즠; ) HANGUL SYLLABLE JEUK
+C9A1;C9A1;110C 1173 11C0;C9A1;110C 1173 11C0; # (즡; 즡; 즡; 즡; 즡; ) HANGUL SYLLABLE JEUT
+C9A2;C9A2;110C 1173 11C1;C9A2;110C 1173 11C1; # (즢; 즢; 즈á‡; 즢; 즈á‡; ) HANGUL SYLLABLE JEUP
+C9A3;C9A3;110C 1173 11C2;C9A3;110C 1173 11C2; # (즣; 즣; 즣; 즣; 즣; ) HANGUL SYLLABLE JEUH
+C9A4;C9A4;110C 1174;C9A4;110C 1174; # (즤; 즤; 즤; 즤; 즤; ) HANGUL SYLLABLE JYI
+C9A5;C9A5;110C 1174 11A8;C9A5;110C 1174 11A8; # (즥; 즥; 즥; 즥; 즥; ) HANGUL SYLLABLE JYIG
+C9A6;C9A6;110C 1174 11A9;C9A6;110C 1174 11A9; # (즦; 즦; 즦; 즦; 즦; ) HANGUL SYLLABLE JYIGG
+C9A7;C9A7;110C 1174 11AA;C9A7;110C 1174 11AA; # (즧; 즧; 즧; 즧; 즧; ) HANGUL SYLLABLE JYIGS
+C9A8;C9A8;110C 1174 11AB;C9A8;110C 1174 11AB; # (즨; 즨; 즨; 즨; 즨; ) HANGUL SYLLABLE JYIN
+C9A9;C9A9;110C 1174 11AC;C9A9;110C 1174 11AC; # (즩; 즩; 즩; 즩; 즩; ) HANGUL SYLLABLE JYINJ
+C9AA;C9AA;110C 1174 11AD;C9AA;110C 1174 11AD; # (즪; 즪; 즪; 즪; 즪; ) HANGUL SYLLABLE JYINH
+C9AB;C9AB;110C 1174 11AE;C9AB;110C 1174 11AE; # (즫; 즫; 즫; 즫; 즫; ) HANGUL SYLLABLE JYID
+C9AC;C9AC;110C 1174 11AF;C9AC;110C 1174 11AF; # (즬; 즬; 즬; 즬; 즬; ) HANGUL SYLLABLE JYIL
+C9AD;C9AD;110C 1174 11B0;C9AD;110C 1174 11B0; # (즭; 즭; 즭; 즭; 즭; ) HANGUL SYLLABLE JYILG
+C9AE;C9AE;110C 1174 11B1;C9AE;110C 1174 11B1; # (즮; 즮; 즮; 즮; 즮; ) HANGUL SYLLABLE JYILM
+C9AF;C9AF;110C 1174 11B2;C9AF;110C 1174 11B2; # (즯; 즯; 즯; 즯; 즯; ) HANGUL SYLLABLE JYILB
+C9B0;C9B0;110C 1174 11B3;C9B0;110C 1174 11B3; # (즰; 즰; 즰; 즰; 즰; ) HANGUL SYLLABLE JYILS
+C9B1;C9B1;110C 1174 11B4;C9B1;110C 1174 11B4; # (즱; 즱; 즱; 즱; 즱; ) HANGUL SYLLABLE JYILT
+C9B2;C9B2;110C 1174 11B5;C9B2;110C 1174 11B5; # (즲; 즲; 즲; 즲; 즲; ) HANGUL SYLLABLE JYILP
+C9B3;C9B3;110C 1174 11B6;C9B3;110C 1174 11B6; # (즳; 즳; 즳; 즳; 즳; ) HANGUL SYLLABLE JYILH
+C9B4;C9B4;110C 1174 11B7;C9B4;110C 1174 11B7; # (즴; 즴; 즴; 즴; 즴; ) HANGUL SYLLABLE JYIM
+C9B5;C9B5;110C 1174 11B8;C9B5;110C 1174 11B8; # (즵; 즵; 즵; 즵; 즵; ) HANGUL SYLLABLE JYIB
+C9B6;C9B6;110C 1174 11B9;C9B6;110C 1174 11B9; # (즶; 즶; 즶; 즶; 즶; ) HANGUL SYLLABLE JYIBS
+C9B7;C9B7;110C 1174 11BA;C9B7;110C 1174 11BA; # (즷; 즷; 즷; 즷; 즷; ) HANGUL SYLLABLE JYIS
+C9B8;C9B8;110C 1174 11BB;C9B8;110C 1174 11BB; # (즸; 즸; 즸; 즸; 즸; ) HANGUL SYLLABLE JYISS
+C9B9;C9B9;110C 1174 11BC;C9B9;110C 1174 11BC; # (즹; 즹; 즹; 즹; 즹; ) HANGUL SYLLABLE JYING
+C9BA;C9BA;110C 1174 11BD;C9BA;110C 1174 11BD; # (즺; 즺; 즺; 즺; 즺; ) HANGUL SYLLABLE JYIJ
+C9BB;C9BB;110C 1174 11BE;C9BB;110C 1174 11BE; # (즻; 즻; 즻; 즻; 즻; ) HANGUL SYLLABLE JYIC
+C9BC;C9BC;110C 1174 11BF;C9BC;110C 1174 11BF; # (즼; 즼; 즼; 즼; 즼; ) HANGUL SYLLABLE JYIK
+C9BD;C9BD;110C 1174 11C0;C9BD;110C 1174 11C0; # (즽; 즽; 즽; 즽; 즽; ) HANGUL SYLLABLE JYIT
+C9BE;C9BE;110C 1174 11C1;C9BE;110C 1174 11C1; # (즾; 즾; 즤á‡; 즾; 즤á‡; ) HANGUL SYLLABLE JYIP
+C9BF;C9BF;110C 1174 11C2;C9BF;110C 1174 11C2; # (즿; 즿; 즿; 즿; 즿; ) HANGUL SYLLABLE JYIH
+C9C0;C9C0;110C 1175;C9C0;110C 1175; # (지; 지; 지; 지; 지; ) HANGUL SYLLABLE JI
+C9C1;C9C1;110C 1175 11A8;C9C1;110C 1175 11A8; # (ì§; ì§; 직; ì§; 직; ) HANGUL SYLLABLE JIG
+C9C2;C9C2;110C 1175 11A9;C9C2;110C 1175 11A9; # (짂; 짂; 짂; 짂; 짂; ) HANGUL SYLLABLE JIGG
+C9C3;C9C3;110C 1175 11AA;C9C3;110C 1175 11AA; # (짃; 짃; 짃; 짃; 짃; ) HANGUL SYLLABLE JIGS
+C9C4;C9C4;110C 1175 11AB;C9C4;110C 1175 11AB; # (진; 진; 진; 진; 진; ) HANGUL SYLLABLE JIN
+C9C5;C9C5;110C 1175 11AC;C9C5;110C 1175 11AC; # (짅; 짅; 짅; 짅; 짅; ) HANGUL SYLLABLE JINJ
+C9C6;C9C6;110C 1175 11AD;C9C6;110C 1175 11AD; # (짆; 짆; 짆; 짆; 짆; ) HANGUL SYLLABLE JINH
+C9C7;C9C7;110C 1175 11AE;C9C7;110C 1175 11AE; # (짇; 짇; 짇; 짇; 짇; ) HANGUL SYLLABLE JID
+C9C8;C9C8;110C 1175 11AF;C9C8;110C 1175 11AF; # (질; 질; 질; 질; 질; ) HANGUL SYLLABLE JIL
+C9C9;C9C9;110C 1175 11B0;C9C9;110C 1175 11B0; # (짉; 짉; 짉; 짉; 짉; ) HANGUL SYLLABLE JILG
+C9CA;C9CA;110C 1175 11B1;C9CA;110C 1175 11B1; # (짊; 짊; 짊; 짊; 짊; ) HANGUL SYLLABLE JILM
+C9CB;C9CB;110C 1175 11B2;C9CB;110C 1175 11B2; # (짋; 짋; 짋; 짋; 짋; ) HANGUL SYLLABLE JILB
+C9CC;C9CC;110C 1175 11B3;C9CC;110C 1175 11B3; # (짌; 짌; 짌; 짌; 짌; ) HANGUL SYLLABLE JILS
+C9CD;C9CD;110C 1175 11B4;C9CD;110C 1175 11B4; # (ì§; ì§; 짍; ì§; 짍; ) HANGUL SYLLABLE JILT
+C9CE;C9CE;110C 1175 11B5;C9CE;110C 1175 11B5; # (짎; 짎; 짎; 짎; 짎; ) HANGUL SYLLABLE JILP
+C9CF;C9CF;110C 1175 11B6;C9CF;110C 1175 11B6; # (ì§; ì§; 짏; ì§; 짏; ) HANGUL SYLLABLE JILH
+C9D0;C9D0;110C 1175 11B7;C9D0;110C 1175 11B7; # (ì§; ì§; 짐; ì§; 짐; ) HANGUL SYLLABLE JIM
+C9D1;C9D1;110C 1175 11B8;C9D1;110C 1175 11B8; # (집; 집; 집; 집; 집; ) HANGUL SYLLABLE JIB
+C9D2;C9D2;110C 1175 11B9;C9D2;110C 1175 11B9; # (짒; 짒; 짒; 짒; 짒; ) HANGUL SYLLABLE JIBS
+C9D3;C9D3;110C 1175 11BA;C9D3;110C 1175 11BA; # (짓; 짓; 짓; 짓; 짓; ) HANGUL SYLLABLE JIS
+C9D4;C9D4;110C 1175 11BB;C9D4;110C 1175 11BB; # (짔; 짔; 짔; 짔; 짔; ) HANGUL SYLLABLE JISS
+C9D5;C9D5;110C 1175 11BC;C9D5;110C 1175 11BC; # (징; 징; 징; 징; 징; ) HANGUL SYLLABLE JING
+C9D6;C9D6;110C 1175 11BD;C9D6;110C 1175 11BD; # (짖; 짖; 짖; 짖; 짖; ) HANGUL SYLLABLE JIJ
+C9D7;C9D7;110C 1175 11BE;C9D7;110C 1175 11BE; # (짗; 짗; 짗; 짗; 짗; ) HANGUL SYLLABLE JIC
+C9D8;C9D8;110C 1175 11BF;C9D8;110C 1175 11BF; # (짘; 짘; 짘; 짘; 짘; ) HANGUL SYLLABLE JIK
+C9D9;C9D9;110C 1175 11C0;C9D9;110C 1175 11C0; # (짙; 짙; 짙; 짙; 짙; ) HANGUL SYLLABLE JIT
+C9DA;C9DA;110C 1175 11C1;C9DA;110C 1175 11C1; # (ì§š; ì§š; 지á‡; ì§š; 지á‡; ) HANGUL SYLLABLE JIP
+C9DB;C9DB;110C 1175 11C2;C9DB;110C 1175 11C2; # (짛; 짛; 짛; 짛; 짛; ) HANGUL SYLLABLE JIH
+C9DC;C9DC;110D 1161;C9DC;110D 1161; # (ì§œ; ì§œ; á„á…¡; ì§œ; á„á…¡; ) HANGUL SYLLABLE JJA
+C9DD;C9DD;110D 1161 11A8;C9DD;110D 1161 11A8; # (ì§; ì§; á„ᅡᆨ; ì§; á„ᅡᆨ; ) HANGUL SYLLABLE JJAG
+C9DE;C9DE;110D 1161 11A9;C9DE;110D 1161 11A9; # (ì§ž; ì§ž; á„ᅡᆩ; ì§ž; á„ᅡᆩ; ) HANGUL SYLLABLE JJAGG
+C9DF;C9DF;110D 1161 11AA;C9DF;110D 1161 11AA; # (ì§Ÿ; ì§Ÿ; á„ᅡᆪ; ì§Ÿ; á„ᅡᆪ; ) HANGUL SYLLABLE JJAGS
+C9E0;C9E0;110D 1161 11AB;C9E0;110D 1161 11AB; # (ì§ ; ì§ ; á„ᅡᆫ; ì§ ; á„ᅡᆫ; ) HANGUL SYLLABLE JJAN
+C9E1;C9E1;110D 1161 11AC;C9E1;110D 1161 11AC; # (ì§¡; ì§¡; á„ᅡᆬ; ì§¡; á„ᅡᆬ; ) HANGUL SYLLABLE JJANJ
+C9E2;C9E2;110D 1161 11AD;C9E2;110D 1161 11AD; # (ì§¢; ì§¢; á„ᅡᆭ; ì§¢; á„ᅡᆭ; ) HANGUL SYLLABLE JJANH
+C9E3;C9E3;110D 1161 11AE;C9E3;110D 1161 11AE; # (ì§£; ì§£; á„ᅡᆮ; ì§£; á„ᅡᆮ; ) HANGUL SYLLABLE JJAD
+C9E4;C9E4;110D 1161 11AF;C9E4;110D 1161 11AF; # (짤; 짤; á„ᅡᆯ; 짤; á„ᅡᆯ; ) HANGUL SYLLABLE JJAL
+C9E5;C9E5;110D 1161 11B0;C9E5;110D 1161 11B0; # (ì§¥; ì§¥; á„ᅡᆰ; ì§¥; á„ᅡᆰ; ) HANGUL SYLLABLE JJALG
+C9E6;C9E6;110D 1161 11B1;C9E6;110D 1161 11B1; # (짦; 짦; á„ᅡᆱ; 짦; á„ᅡᆱ; ) HANGUL SYLLABLE JJALM
+C9E7;C9E7;110D 1161 11B2;C9E7;110D 1161 11B2; # (ì§§; ì§§; á„ᅡᆲ; ì§§; á„ᅡᆲ; ) HANGUL SYLLABLE JJALB
+C9E8;C9E8;110D 1161 11B3;C9E8;110D 1161 11B3; # (짨; 짨; á„ᅡᆳ; 짨; á„ᅡᆳ; ) HANGUL SYLLABLE JJALS
+C9E9;C9E9;110D 1161 11B4;C9E9;110D 1161 11B4; # (ì§©; ì§©; á„ᅡᆴ; ì§©; á„ᅡᆴ; ) HANGUL SYLLABLE JJALT
+C9EA;C9EA;110D 1161 11B5;C9EA;110D 1161 11B5; # (짪; 짪; á„ᅡᆵ; 짪; á„ᅡᆵ; ) HANGUL SYLLABLE JJALP
+C9EB;C9EB;110D 1161 11B6;C9EB;110D 1161 11B6; # (ì§«; ì§«; á„ᅡᆶ; ì§«; á„ᅡᆶ; ) HANGUL SYLLABLE JJALH
+C9EC;C9EC;110D 1161 11B7;C9EC;110D 1161 11B7; # (짬; 짬; á„ᅡᆷ; 짬; á„ᅡᆷ; ) HANGUL SYLLABLE JJAM
+C9ED;C9ED;110D 1161 11B8;C9ED;110D 1161 11B8; # (ì§­; ì§­; á„ᅡᆸ; ì§­; á„ᅡᆸ; ) HANGUL SYLLABLE JJAB
+C9EE;C9EE;110D 1161 11B9;C9EE;110D 1161 11B9; # (ì§®; ì§®; á„ᅡᆹ; ì§®; á„ᅡᆹ; ) HANGUL SYLLABLE JJABS
+C9EF;C9EF;110D 1161 11BA;C9EF;110D 1161 11BA; # (짯; 짯; á„ᅡᆺ; 짯; á„ᅡᆺ; ) HANGUL SYLLABLE JJAS
+C9F0;C9F0;110D 1161 11BB;C9F0;110D 1161 11BB; # (ì§°; ì§°; á„ᅡᆻ; ì§°; á„ᅡᆻ; ) HANGUL SYLLABLE JJASS
+C9F1;C9F1;110D 1161 11BC;C9F1;110D 1161 11BC; # (ì§±; ì§±; á„ᅡᆼ; ì§±; á„ᅡᆼ; ) HANGUL SYLLABLE JJANG
+C9F2;C9F2;110D 1161 11BD;C9F2;110D 1161 11BD; # (ì§²; ì§²; á„ᅡᆽ; ì§²; á„ᅡᆽ; ) HANGUL SYLLABLE JJAJ
+C9F3;C9F3;110D 1161 11BE;C9F3;110D 1161 11BE; # (ì§³; ì§³; á„ᅡᆾ; ì§³; á„ᅡᆾ; ) HANGUL SYLLABLE JJAC
+C9F4;C9F4;110D 1161 11BF;C9F4;110D 1161 11BF; # (ì§´; ì§´; á„ᅡᆿ; ì§´; á„ᅡᆿ; ) HANGUL SYLLABLE JJAK
+C9F5;C9F5;110D 1161 11C0;C9F5;110D 1161 11C0; # (ì§µ; ì§µ; á„ᅡᇀ; ì§µ; á„ᅡᇀ; ) HANGUL SYLLABLE JJAT
+C9F6;C9F6;110D 1161 11C1;C9F6;110D 1161 11C1; # (ì§¶; ì§¶; á„á…¡á‡; ì§¶; á„á…¡á‡; ) HANGUL SYLLABLE JJAP
+C9F7;C9F7;110D 1161 11C2;C9F7;110D 1161 11C2; # (ì§·; ì§·; á„ᅡᇂ; ì§·; á„ᅡᇂ; ) HANGUL SYLLABLE JJAH
+C9F8;C9F8;110D 1162;C9F8;110D 1162; # (째; 째; á„á…¢; 째; á„á…¢; ) HANGUL SYLLABLE JJAE
+C9F9;C9F9;110D 1162 11A8;C9F9;110D 1162 11A8; # (ì§¹; ì§¹; á„ᅢᆨ; ì§¹; á„ᅢᆨ; ) HANGUL SYLLABLE JJAEG
+C9FA;C9FA;110D 1162 11A9;C9FA;110D 1162 11A9; # (짺; 짺; á„ᅢᆩ; 짺; á„ᅢᆩ; ) HANGUL SYLLABLE JJAEGG
+C9FB;C9FB;110D 1162 11AA;C9FB;110D 1162 11AA; # (ì§»; ì§»; á„ᅢᆪ; ì§»; á„ᅢᆪ; ) HANGUL SYLLABLE JJAEGS
+C9FC;C9FC;110D 1162 11AB;C9FC;110D 1162 11AB; # (ì§¼; ì§¼; á„ᅢᆫ; ì§¼; á„ᅢᆫ; ) HANGUL SYLLABLE JJAEN
+C9FD;C9FD;110D 1162 11AC;C9FD;110D 1162 11AC; # (ì§½; ì§½; á„ᅢᆬ; ì§½; á„ᅢᆬ; ) HANGUL SYLLABLE JJAENJ
+C9FE;C9FE;110D 1162 11AD;C9FE;110D 1162 11AD; # (ì§¾; ì§¾; á„ᅢᆭ; ì§¾; á„ᅢᆭ; ) HANGUL SYLLABLE JJAENH
+C9FF;C9FF;110D 1162 11AE;C9FF;110D 1162 11AE; # (ì§¿; ì§¿; á„ᅢᆮ; ì§¿; á„ᅢᆮ; ) HANGUL SYLLABLE JJAED
+CA00;CA00;110D 1162 11AF;CA00;110D 1162 11AF; # (쨀; 쨀; á„ᅢᆯ; 쨀; á„ᅢᆯ; ) HANGUL SYLLABLE JJAEL
+CA01;CA01;110D 1162 11B0;CA01;110D 1162 11B0; # (ì¨; ì¨; á„ᅢᆰ; ì¨; á„ᅢᆰ; ) HANGUL SYLLABLE JJAELG
+CA02;CA02;110D 1162 11B1;CA02;110D 1162 11B1; # (쨂; 쨂; á„ᅢᆱ; 쨂; á„ᅢᆱ; ) HANGUL SYLLABLE JJAELM
+CA03;CA03;110D 1162 11B2;CA03;110D 1162 11B2; # (쨃; 쨃; á„ᅢᆲ; 쨃; á„ᅢᆲ; ) HANGUL SYLLABLE JJAELB
+CA04;CA04;110D 1162 11B3;CA04;110D 1162 11B3; # (쨄; 쨄; á„ᅢᆳ; 쨄; á„ᅢᆳ; ) HANGUL SYLLABLE JJAELS
+CA05;CA05;110D 1162 11B4;CA05;110D 1162 11B4; # (쨅; 쨅; á„ᅢᆴ; 쨅; á„ᅢᆴ; ) HANGUL SYLLABLE JJAELT
+CA06;CA06;110D 1162 11B5;CA06;110D 1162 11B5; # (쨆; 쨆; á„ᅢᆵ; 쨆; á„ᅢᆵ; ) HANGUL SYLLABLE JJAELP
+CA07;CA07;110D 1162 11B6;CA07;110D 1162 11B6; # (쨇; 쨇; á„ᅢᆶ; 쨇; á„ᅢᆶ; ) HANGUL SYLLABLE JJAELH
+CA08;CA08;110D 1162 11B7;CA08;110D 1162 11B7; # (쨈; 쨈; á„ᅢᆷ; 쨈; á„ᅢᆷ; ) HANGUL SYLLABLE JJAEM
+CA09;CA09;110D 1162 11B8;CA09;110D 1162 11B8; # (쨉; 쨉; á„ᅢᆸ; 쨉; á„ᅢᆸ; ) HANGUL SYLLABLE JJAEB
+CA0A;CA0A;110D 1162 11B9;CA0A;110D 1162 11B9; # (쨊; 쨊; á„ᅢᆹ; 쨊; á„ᅢᆹ; ) HANGUL SYLLABLE JJAEBS
+CA0B;CA0B;110D 1162 11BA;CA0B;110D 1162 11BA; # (쨋; 쨋; á„ᅢᆺ; 쨋; á„ᅢᆺ; ) HANGUL SYLLABLE JJAES
+CA0C;CA0C;110D 1162 11BB;CA0C;110D 1162 11BB; # (쨌; 쨌; á„ᅢᆻ; 쨌; á„ᅢᆻ; ) HANGUL SYLLABLE JJAESS
+CA0D;CA0D;110D 1162 11BC;CA0D;110D 1162 11BC; # (ì¨; ì¨; á„ᅢᆼ; ì¨; á„ᅢᆼ; ) HANGUL SYLLABLE JJAENG
+CA0E;CA0E;110D 1162 11BD;CA0E;110D 1162 11BD; # (쨎; 쨎; á„ᅢᆽ; 쨎; á„ᅢᆽ; ) HANGUL SYLLABLE JJAEJ
+CA0F;CA0F;110D 1162 11BE;CA0F;110D 1162 11BE; # (ì¨; ì¨; á„ᅢᆾ; ì¨; á„ᅢᆾ; ) HANGUL SYLLABLE JJAEC
+CA10;CA10;110D 1162 11BF;CA10;110D 1162 11BF; # (ì¨; ì¨; á„ᅢᆿ; ì¨; á„ᅢᆿ; ) HANGUL SYLLABLE JJAEK
+CA11;CA11;110D 1162 11C0;CA11;110D 1162 11C0; # (쨑; 쨑; á„ᅢᇀ; 쨑; á„ᅢᇀ; ) HANGUL SYLLABLE JJAET
+CA12;CA12;110D 1162 11C1;CA12;110D 1162 11C1; # (쨒; 쨒; á„á…¢á‡; 쨒; á„á…¢á‡; ) HANGUL SYLLABLE JJAEP
+CA13;CA13;110D 1162 11C2;CA13;110D 1162 11C2; # (쨓; 쨓; á„ᅢᇂ; 쨓; á„ᅢᇂ; ) HANGUL SYLLABLE JJAEH
+CA14;CA14;110D 1163;CA14;110D 1163; # (쨔; 쨔; á„á…£; 쨔; á„á…£; ) HANGUL SYLLABLE JJYA
+CA15;CA15;110D 1163 11A8;CA15;110D 1163 11A8; # (쨕; 쨕; á„ᅣᆨ; 쨕; á„ᅣᆨ; ) HANGUL SYLLABLE JJYAG
+CA16;CA16;110D 1163 11A9;CA16;110D 1163 11A9; # (쨖; 쨖; á„ᅣᆩ; 쨖; á„ᅣᆩ; ) HANGUL SYLLABLE JJYAGG
+CA17;CA17;110D 1163 11AA;CA17;110D 1163 11AA; # (쨗; 쨗; á„ᅣᆪ; 쨗; á„ᅣᆪ; ) HANGUL SYLLABLE JJYAGS
+CA18;CA18;110D 1163 11AB;CA18;110D 1163 11AB; # (쨘; 쨘; á„ᅣᆫ; 쨘; á„ᅣᆫ; ) HANGUL SYLLABLE JJYAN
+CA19;CA19;110D 1163 11AC;CA19;110D 1163 11AC; # (쨙; 쨙; á„ᅣᆬ; 쨙; á„ᅣᆬ; ) HANGUL SYLLABLE JJYANJ
+CA1A;CA1A;110D 1163 11AD;CA1A;110D 1163 11AD; # (쨚; 쨚; á„ᅣᆭ; 쨚; á„ᅣᆭ; ) HANGUL SYLLABLE JJYANH
+CA1B;CA1B;110D 1163 11AE;CA1B;110D 1163 11AE; # (쨛; 쨛; á„ᅣᆮ; 쨛; á„ᅣᆮ; ) HANGUL SYLLABLE JJYAD
+CA1C;CA1C;110D 1163 11AF;CA1C;110D 1163 11AF; # (쨜; 쨜; á„ᅣᆯ; 쨜; á„ᅣᆯ; ) HANGUL SYLLABLE JJYAL
+CA1D;CA1D;110D 1163 11B0;CA1D;110D 1163 11B0; # (ì¨; ì¨; á„ᅣᆰ; ì¨; á„ᅣᆰ; ) HANGUL SYLLABLE JJYALG
+CA1E;CA1E;110D 1163 11B1;CA1E;110D 1163 11B1; # (쨞; 쨞; á„ᅣᆱ; 쨞; á„ᅣᆱ; ) HANGUL SYLLABLE JJYALM
+CA1F;CA1F;110D 1163 11B2;CA1F;110D 1163 11B2; # (쨟; 쨟; á„ᅣᆲ; 쨟; á„ᅣᆲ; ) HANGUL SYLLABLE JJYALB
+CA20;CA20;110D 1163 11B3;CA20;110D 1163 11B3; # (쨠; 쨠; á„ᅣᆳ; 쨠; á„ᅣᆳ; ) HANGUL SYLLABLE JJYALS
+CA21;CA21;110D 1163 11B4;CA21;110D 1163 11B4; # (쨡; 쨡; á„ᅣᆴ; 쨡; á„ᅣᆴ; ) HANGUL SYLLABLE JJYALT
+CA22;CA22;110D 1163 11B5;CA22;110D 1163 11B5; # (쨢; 쨢; á„ᅣᆵ; 쨢; á„ᅣᆵ; ) HANGUL SYLLABLE JJYALP
+CA23;CA23;110D 1163 11B6;CA23;110D 1163 11B6; # (쨣; 쨣; á„ᅣᆶ; 쨣; á„ᅣᆶ; ) HANGUL SYLLABLE JJYALH
+CA24;CA24;110D 1163 11B7;CA24;110D 1163 11B7; # (쨤; 쨤; á„ᅣᆷ; 쨤; á„ᅣᆷ; ) HANGUL SYLLABLE JJYAM
+CA25;CA25;110D 1163 11B8;CA25;110D 1163 11B8; # (쨥; 쨥; á„ᅣᆸ; 쨥; á„ᅣᆸ; ) HANGUL SYLLABLE JJYAB
+CA26;CA26;110D 1163 11B9;CA26;110D 1163 11B9; # (쨦; 쨦; á„ᅣᆹ; 쨦; á„ᅣᆹ; ) HANGUL SYLLABLE JJYABS
+CA27;CA27;110D 1163 11BA;CA27;110D 1163 11BA; # (쨧; 쨧; á„ᅣᆺ; 쨧; á„ᅣᆺ; ) HANGUL SYLLABLE JJYAS
+CA28;CA28;110D 1163 11BB;CA28;110D 1163 11BB; # (쨨; 쨨; á„ᅣᆻ; 쨨; á„ᅣᆻ; ) HANGUL SYLLABLE JJYASS
+CA29;CA29;110D 1163 11BC;CA29;110D 1163 11BC; # (쨩; 쨩; á„ᅣᆼ; 쨩; á„ᅣᆼ; ) HANGUL SYLLABLE JJYANG
+CA2A;CA2A;110D 1163 11BD;CA2A;110D 1163 11BD; # (쨪; 쨪; á„ᅣᆽ; 쨪; á„ᅣᆽ; ) HANGUL SYLLABLE JJYAJ
+CA2B;CA2B;110D 1163 11BE;CA2B;110D 1163 11BE; # (쨫; 쨫; á„ᅣᆾ; 쨫; á„ᅣᆾ; ) HANGUL SYLLABLE JJYAC
+CA2C;CA2C;110D 1163 11BF;CA2C;110D 1163 11BF; # (쨬; 쨬; á„ᅣᆿ; 쨬; á„ᅣᆿ; ) HANGUL SYLLABLE JJYAK
+CA2D;CA2D;110D 1163 11C0;CA2D;110D 1163 11C0; # (쨭; 쨭; á„ᅣᇀ; 쨭; á„ᅣᇀ; ) HANGUL SYLLABLE JJYAT
+CA2E;CA2E;110D 1163 11C1;CA2E;110D 1163 11C1; # (쨮; 쨮; á„á…£á‡; 쨮; á„á…£á‡; ) HANGUL SYLLABLE JJYAP
+CA2F;CA2F;110D 1163 11C2;CA2F;110D 1163 11C2; # (쨯; 쨯; á„ᅣᇂ; 쨯; á„ᅣᇂ; ) HANGUL SYLLABLE JJYAH
+CA30;CA30;110D 1164;CA30;110D 1164; # (쨰; 쨰; á„á…¤; 쨰; á„á…¤; ) HANGUL SYLLABLE JJYAE
+CA31;CA31;110D 1164 11A8;CA31;110D 1164 11A8; # (쨱; 쨱; á„ᅤᆨ; 쨱; á„ᅤᆨ; ) HANGUL SYLLABLE JJYAEG
+CA32;CA32;110D 1164 11A9;CA32;110D 1164 11A9; # (쨲; 쨲; á„ᅤᆩ; 쨲; á„ᅤᆩ; ) HANGUL SYLLABLE JJYAEGG
+CA33;CA33;110D 1164 11AA;CA33;110D 1164 11AA; # (쨳; 쨳; á„ᅤᆪ; 쨳; á„ᅤᆪ; ) HANGUL SYLLABLE JJYAEGS
+CA34;CA34;110D 1164 11AB;CA34;110D 1164 11AB; # (쨴; 쨴; á„ᅤᆫ; 쨴; á„ᅤᆫ; ) HANGUL SYLLABLE JJYAEN
+CA35;CA35;110D 1164 11AC;CA35;110D 1164 11AC; # (쨵; 쨵; á„ᅤᆬ; 쨵; á„ᅤᆬ; ) HANGUL SYLLABLE JJYAENJ
+CA36;CA36;110D 1164 11AD;CA36;110D 1164 11AD; # (쨶; 쨶; á„ᅤᆭ; 쨶; á„ᅤᆭ; ) HANGUL SYLLABLE JJYAENH
+CA37;CA37;110D 1164 11AE;CA37;110D 1164 11AE; # (쨷; 쨷; á„ᅤᆮ; 쨷; á„ᅤᆮ; ) HANGUL SYLLABLE JJYAED
+CA38;CA38;110D 1164 11AF;CA38;110D 1164 11AF; # (쨸; 쨸; á„ᅤᆯ; 쨸; á„ᅤᆯ; ) HANGUL SYLLABLE JJYAEL
+CA39;CA39;110D 1164 11B0;CA39;110D 1164 11B0; # (쨹; 쨹; á„ᅤᆰ; 쨹; á„ᅤᆰ; ) HANGUL SYLLABLE JJYAELG
+CA3A;CA3A;110D 1164 11B1;CA3A;110D 1164 11B1; # (쨺; 쨺; á„ᅤᆱ; 쨺; á„ᅤᆱ; ) HANGUL SYLLABLE JJYAELM
+CA3B;CA3B;110D 1164 11B2;CA3B;110D 1164 11B2; # (쨻; 쨻; á„ᅤᆲ; 쨻; á„ᅤᆲ; ) HANGUL SYLLABLE JJYAELB
+CA3C;CA3C;110D 1164 11B3;CA3C;110D 1164 11B3; # (쨼; 쨼; á„ᅤᆳ; 쨼; á„ᅤᆳ; ) HANGUL SYLLABLE JJYAELS
+CA3D;CA3D;110D 1164 11B4;CA3D;110D 1164 11B4; # (쨽; 쨽; á„ᅤᆴ; 쨽; á„ᅤᆴ; ) HANGUL SYLLABLE JJYAELT
+CA3E;CA3E;110D 1164 11B5;CA3E;110D 1164 11B5; # (쨾; 쨾; á„ᅤᆵ; 쨾; á„ᅤᆵ; ) HANGUL SYLLABLE JJYAELP
+CA3F;CA3F;110D 1164 11B6;CA3F;110D 1164 11B6; # (쨿; 쨿; á„ᅤᆶ; 쨿; á„ᅤᆶ; ) HANGUL SYLLABLE JJYAELH
+CA40;CA40;110D 1164 11B7;CA40;110D 1164 11B7; # (ì©€; ì©€; á„ᅤᆷ; ì©€; á„ᅤᆷ; ) HANGUL SYLLABLE JJYAEM
+CA41;CA41;110D 1164 11B8;CA41;110D 1164 11B8; # (ì©; ì©; á„ᅤᆸ; ì©; á„ᅤᆸ; ) HANGUL SYLLABLE JJYAEB
+CA42;CA42;110D 1164 11B9;CA42;110D 1164 11B9; # (ì©‚; ì©‚; á„ᅤᆹ; ì©‚; á„ᅤᆹ; ) HANGUL SYLLABLE JJYAEBS
+CA43;CA43;110D 1164 11BA;CA43;110D 1164 11BA; # (쩃; 쩃; á„ᅤᆺ; 쩃; á„ᅤᆺ; ) HANGUL SYLLABLE JJYAES
+CA44;CA44;110D 1164 11BB;CA44;110D 1164 11BB; # (ì©„; ì©„; á„ᅤᆻ; ì©„; á„ᅤᆻ; ) HANGUL SYLLABLE JJYAESS
+CA45;CA45;110D 1164 11BC;CA45;110D 1164 11BC; # (ì©…; ì©…; á„ᅤᆼ; ì©…; á„ᅤᆼ; ) HANGUL SYLLABLE JJYAENG
+CA46;CA46;110D 1164 11BD;CA46;110D 1164 11BD; # (쩆; 쩆; á„ᅤᆽ; 쩆; á„ᅤᆽ; ) HANGUL SYLLABLE JJYAEJ
+CA47;CA47;110D 1164 11BE;CA47;110D 1164 11BE; # (쩇; 쩇; á„ᅤᆾ; 쩇; á„ᅤᆾ; ) HANGUL SYLLABLE JJYAEC
+CA48;CA48;110D 1164 11BF;CA48;110D 1164 11BF; # (쩈; 쩈; á„ᅤᆿ; 쩈; á„ᅤᆿ; ) HANGUL SYLLABLE JJYAEK
+CA49;CA49;110D 1164 11C0;CA49;110D 1164 11C0; # (쩉; 쩉; á„ᅤᇀ; 쩉; á„ᅤᇀ; ) HANGUL SYLLABLE JJYAET
+CA4A;CA4A;110D 1164 11C1;CA4A;110D 1164 11C1; # (쩊; 쩊; á„á…¤á‡; 쩊; á„á…¤á‡; ) HANGUL SYLLABLE JJYAEP
+CA4B;CA4B;110D 1164 11C2;CA4B;110D 1164 11C2; # (ì©‹; ì©‹; á„ᅤᇂ; ì©‹; á„ᅤᇂ; ) HANGUL SYLLABLE JJYAEH
+CA4C;CA4C;110D 1165;CA4C;110D 1165; # (쩌; 쩌; á„á…¥; 쩌; á„á…¥; ) HANGUL SYLLABLE JJEO
+CA4D;CA4D;110D 1165 11A8;CA4D;110D 1165 11A8; # (ì©; ì©; á„ᅥᆨ; ì©; á„ᅥᆨ; ) HANGUL SYLLABLE JJEOG
+CA4E;CA4E;110D 1165 11A9;CA4E;110D 1165 11A9; # (쩎; 쩎; á„ᅥᆩ; 쩎; á„ᅥᆩ; ) HANGUL SYLLABLE JJEOGG
+CA4F;CA4F;110D 1165 11AA;CA4F;110D 1165 11AA; # (ì©; ì©; á„ᅥᆪ; ì©; á„ᅥᆪ; ) HANGUL SYLLABLE JJEOGS
+CA50;CA50;110D 1165 11AB;CA50;110D 1165 11AB; # (ì©; ì©; á„ᅥᆫ; ì©; á„ᅥᆫ; ) HANGUL SYLLABLE JJEON
+CA51;CA51;110D 1165 11AC;CA51;110D 1165 11AC; # (ì©‘; ì©‘; á„ᅥᆬ; ì©‘; á„ᅥᆬ; ) HANGUL SYLLABLE JJEONJ
+CA52;CA52;110D 1165 11AD;CA52;110D 1165 11AD; # (ì©’; ì©’; á„ᅥᆭ; ì©’; á„ᅥᆭ; ) HANGUL SYLLABLE JJEONH
+CA53;CA53;110D 1165 11AE;CA53;110D 1165 11AE; # (ì©“; ì©“; á„ᅥᆮ; ì©“; á„ᅥᆮ; ) HANGUL SYLLABLE JJEOD
+CA54;CA54;110D 1165 11AF;CA54;110D 1165 11AF; # (ì©”; ì©”; á„ᅥᆯ; ì©”; á„ᅥᆯ; ) HANGUL SYLLABLE JJEOL
+CA55;CA55;110D 1165 11B0;CA55;110D 1165 11B0; # (ì©•; ì©•; á„ᅥᆰ; ì©•; á„ᅥᆰ; ) HANGUL SYLLABLE JJEOLG
+CA56;CA56;110D 1165 11B1;CA56;110D 1165 11B1; # (ì©–; ì©–; á„ᅥᆱ; ì©–; á„ᅥᆱ; ) HANGUL SYLLABLE JJEOLM
+CA57;CA57;110D 1165 11B2;CA57;110D 1165 11B2; # (ì©—; ì©—; á„ᅥᆲ; ì©—; á„ᅥᆲ; ) HANGUL SYLLABLE JJEOLB
+CA58;CA58;110D 1165 11B3;CA58;110D 1165 11B3; # (쩘; 쩘; á„ᅥᆳ; 쩘; á„ᅥᆳ; ) HANGUL SYLLABLE JJEOLS
+CA59;CA59;110D 1165 11B4;CA59;110D 1165 11B4; # (ì©™; ì©™; á„ᅥᆴ; ì©™; á„ᅥᆴ; ) HANGUL SYLLABLE JJEOLT
+CA5A;CA5A;110D 1165 11B5;CA5A;110D 1165 11B5; # (쩚; 쩚; á„ᅥᆵ; 쩚; á„ᅥᆵ; ) HANGUL SYLLABLE JJEOLP
+CA5B;CA5B;110D 1165 11B6;CA5B;110D 1165 11B6; # (ì©›; ì©›; á„ᅥᆶ; ì©›; á„ᅥᆶ; ) HANGUL SYLLABLE JJEOLH
+CA5C;CA5C;110D 1165 11B7;CA5C;110D 1165 11B7; # (쩜; 쩜; á„ᅥᆷ; 쩜; á„ᅥᆷ; ) HANGUL SYLLABLE JJEOM
+CA5D;CA5D;110D 1165 11B8;CA5D;110D 1165 11B8; # (ì©; ì©; á„ᅥᆸ; ì©; á„ᅥᆸ; ) HANGUL SYLLABLE JJEOB
+CA5E;CA5E;110D 1165 11B9;CA5E;110D 1165 11B9; # (쩞; 쩞; á„ᅥᆹ; 쩞; á„ᅥᆹ; ) HANGUL SYLLABLE JJEOBS
+CA5F;CA5F;110D 1165 11BA;CA5F;110D 1165 11BA; # (쩟; 쩟; á„ᅥᆺ; 쩟; á„ᅥᆺ; ) HANGUL SYLLABLE JJEOS
+CA60;CA60;110D 1165 11BB;CA60;110D 1165 11BB; # (ì© ; ì© ; á„ᅥᆻ; ì© ; á„ᅥᆻ; ) HANGUL SYLLABLE JJEOSS
+CA61;CA61;110D 1165 11BC;CA61;110D 1165 11BC; # (ì©¡; ì©¡; á„ᅥᆼ; ì©¡; á„ᅥᆼ; ) HANGUL SYLLABLE JJEONG
+CA62;CA62;110D 1165 11BD;CA62;110D 1165 11BD; # (ì©¢; ì©¢; á„ᅥᆽ; ì©¢; á„ᅥᆽ; ) HANGUL SYLLABLE JJEOJ
+CA63;CA63;110D 1165 11BE;CA63;110D 1165 11BE; # (ì©£; ì©£; á„ᅥᆾ; ì©£; á„ᅥᆾ; ) HANGUL SYLLABLE JJEOC
+CA64;CA64;110D 1165 11BF;CA64;110D 1165 11BF; # (쩤; 쩤; á„ᅥᆿ; 쩤; á„ᅥᆿ; ) HANGUL SYLLABLE JJEOK
+CA65;CA65;110D 1165 11C0;CA65;110D 1165 11C0; # (ì©¥; ì©¥; á„ᅥᇀ; ì©¥; á„ᅥᇀ; ) HANGUL SYLLABLE JJEOT
+CA66;CA66;110D 1165 11C1;CA66;110D 1165 11C1; # (쩦; 쩦; á„á…¥á‡; 쩦; á„á…¥á‡; ) HANGUL SYLLABLE JJEOP
+CA67;CA67;110D 1165 11C2;CA67;110D 1165 11C2; # (ì©§; ì©§; á„ᅥᇂ; ì©§; á„ᅥᇂ; ) HANGUL SYLLABLE JJEOH
+CA68;CA68;110D 1166;CA68;110D 1166; # (쩨; 쩨; á„á…¦; 쩨; á„á…¦; ) HANGUL SYLLABLE JJE
+CA69;CA69;110D 1166 11A8;CA69;110D 1166 11A8; # (ì©©; ì©©; á„ᅦᆨ; ì©©; á„ᅦᆨ; ) HANGUL SYLLABLE JJEG
+CA6A;CA6A;110D 1166 11A9;CA6A;110D 1166 11A9; # (쩪; 쩪; á„ᅦᆩ; 쩪; á„ᅦᆩ; ) HANGUL SYLLABLE JJEGG
+CA6B;CA6B;110D 1166 11AA;CA6B;110D 1166 11AA; # (ì©«; ì©«; á„ᅦᆪ; ì©«; á„ᅦᆪ; ) HANGUL SYLLABLE JJEGS
+CA6C;CA6C;110D 1166 11AB;CA6C;110D 1166 11AB; # (쩬; 쩬; á„ᅦᆫ; 쩬; á„ᅦᆫ; ) HANGUL SYLLABLE JJEN
+CA6D;CA6D;110D 1166 11AC;CA6D;110D 1166 11AC; # (ì©­; ì©­; á„ᅦᆬ; ì©­; á„ᅦᆬ; ) HANGUL SYLLABLE JJENJ
+CA6E;CA6E;110D 1166 11AD;CA6E;110D 1166 11AD; # (ì©®; ì©®; á„ᅦᆭ; ì©®; á„ᅦᆭ; ) HANGUL SYLLABLE JJENH
+CA6F;CA6F;110D 1166 11AE;CA6F;110D 1166 11AE; # (쩯; 쩯; á„ᅦᆮ; 쩯; á„ᅦᆮ; ) HANGUL SYLLABLE JJED
+CA70;CA70;110D 1166 11AF;CA70;110D 1166 11AF; # (ì©°; ì©°; á„ᅦᆯ; ì©°; á„ᅦᆯ; ) HANGUL SYLLABLE JJEL
+CA71;CA71;110D 1166 11B0;CA71;110D 1166 11B0; # (쩱; 쩱; á„ᅦᆰ; 쩱; á„ᅦᆰ; ) HANGUL SYLLABLE JJELG
+CA72;CA72;110D 1166 11B1;CA72;110D 1166 11B1; # (쩲; 쩲; á„ᅦᆱ; 쩲; á„ᅦᆱ; ) HANGUL SYLLABLE JJELM
+CA73;CA73;110D 1166 11B2;CA73;110D 1166 11B2; # (쩳; 쩳; á„ᅦᆲ; 쩳; á„ᅦᆲ; ) HANGUL SYLLABLE JJELB
+CA74;CA74;110D 1166 11B3;CA74;110D 1166 11B3; # (ì©´; ì©´; á„ᅦᆳ; ì©´; á„ᅦᆳ; ) HANGUL SYLLABLE JJELS
+CA75;CA75;110D 1166 11B4;CA75;110D 1166 11B4; # (쩵; 쩵; á„ᅦᆴ; 쩵; á„ᅦᆴ; ) HANGUL SYLLABLE JJELT
+CA76;CA76;110D 1166 11B5;CA76;110D 1166 11B5; # (ì©¶; ì©¶; á„ᅦᆵ; ì©¶; á„ᅦᆵ; ) HANGUL SYLLABLE JJELP
+CA77;CA77;110D 1166 11B6;CA77;110D 1166 11B6; # (ì©·; ì©·; á„ᅦᆶ; ì©·; á„ᅦᆶ; ) HANGUL SYLLABLE JJELH
+CA78;CA78;110D 1166 11B7;CA78;110D 1166 11B7; # (쩸; 쩸; á„ᅦᆷ; 쩸; á„ᅦᆷ; ) HANGUL SYLLABLE JJEM
+CA79;CA79;110D 1166 11B8;CA79;110D 1166 11B8; # (쩹; 쩹; á„ᅦᆸ; 쩹; á„ᅦᆸ; ) HANGUL SYLLABLE JJEB
+CA7A;CA7A;110D 1166 11B9;CA7A;110D 1166 11B9; # (쩺; 쩺; á„ᅦᆹ; 쩺; á„ᅦᆹ; ) HANGUL SYLLABLE JJEBS
+CA7B;CA7B;110D 1166 11BA;CA7B;110D 1166 11BA; # (ì©»; ì©»; á„ᅦᆺ; ì©»; á„ᅦᆺ; ) HANGUL SYLLABLE JJES
+CA7C;CA7C;110D 1166 11BB;CA7C;110D 1166 11BB; # (쩼; 쩼; á„ᅦᆻ; 쩼; á„ᅦᆻ; ) HANGUL SYLLABLE JJESS
+CA7D;CA7D;110D 1166 11BC;CA7D;110D 1166 11BC; # (쩽; 쩽; á„ᅦᆼ; 쩽; á„ᅦᆼ; ) HANGUL SYLLABLE JJENG
+CA7E;CA7E;110D 1166 11BD;CA7E;110D 1166 11BD; # (쩾; 쩾; á„ᅦᆽ; 쩾; á„ᅦᆽ; ) HANGUL SYLLABLE JJEJ
+CA7F;CA7F;110D 1166 11BE;CA7F;110D 1166 11BE; # (ì©¿; ì©¿; á„ᅦᆾ; ì©¿; á„ᅦᆾ; ) HANGUL SYLLABLE JJEC
+CA80;CA80;110D 1166 11BF;CA80;110D 1166 11BF; # (쪀; 쪀; á„ᅦᆿ; 쪀; á„ᅦᆿ; ) HANGUL SYLLABLE JJEK
+CA81;CA81;110D 1166 11C0;CA81;110D 1166 11C0; # (ìª; ìª; á„ᅦᇀ; ìª; á„ᅦᇀ; ) HANGUL SYLLABLE JJET
+CA82;CA82;110D 1166 11C1;CA82;110D 1166 11C1; # (쪂; 쪂; á„á…¦á‡; 쪂; á„á…¦á‡; ) HANGUL SYLLABLE JJEP
+CA83;CA83;110D 1166 11C2;CA83;110D 1166 11C2; # (쪃; 쪃; á„ᅦᇂ; 쪃; á„ᅦᇂ; ) HANGUL SYLLABLE JJEH
+CA84;CA84;110D 1167;CA84;110D 1167; # (쪄; 쪄; á„á…§; 쪄; á„á…§; ) HANGUL SYLLABLE JJYEO
+CA85;CA85;110D 1167 11A8;CA85;110D 1167 11A8; # (쪅; 쪅; á„ᅧᆨ; 쪅; á„ᅧᆨ; ) HANGUL SYLLABLE JJYEOG
+CA86;CA86;110D 1167 11A9;CA86;110D 1167 11A9; # (쪆; 쪆; á„ᅧᆩ; 쪆; á„ᅧᆩ; ) HANGUL SYLLABLE JJYEOGG
+CA87;CA87;110D 1167 11AA;CA87;110D 1167 11AA; # (쪇; 쪇; á„ᅧᆪ; 쪇; á„ᅧᆪ; ) HANGUL SYLLABLE JJYEOGS
+CA88;CA88;110D 1167 11AB;CA88;110D 1167 11AB; # (쪈; 쪈; á„ᅧᆫ; 쪈; á„ᅧᆫ; ) HANGUL SYLLABLE JJYEON
+CA89;CA89;110D 1167 11AC;CA89;110D 1167 11AC; # (쪉; 쪉; á„ᅧᆬ; 쪉; á„ᅧᆬ; ) HANGUL SYLLABLE JJYEONJ
+CA8A;CA8A;110D 1167 11AD;CA8A;110D 1167 11AD; # (쪊; 쪊; á„ᅧᆭ; 쪊; á„ᅧᆭ; ) HANGUL SYLLABLE JJYEONH
+CA8B;CA8B;110D 1167 11AE;CA8B;110D 1167 11AE; # (쪋; 쪋; á„ᅧᆮ; 쪋; á„ᅧᆮ; ) HANGUL SYLLABLE JJYEOD
+CA8C;CA8C;110D 1167 11AF;CA8C;110D 1167 11AF; # (쪌; 쪌; á„ᅧᆯ; 쪌; á„ᅧᆯ; ) HANGUL SYLLABLE JJYEOL
+CA8D;CA8D;110D 1167 11B0;CA8D;110D 1167 11B0; # (ìª; ìª; á„ᅧᆰ; ìª; á„ᅧᆰ; ) HANGUL SYLLABLE JJYEOLG
+CA8E;CA8E;110D 1167 11B1;CA8E;110D 1167 11B1; # (쪎; 쪎; á„ᅧᆱ; 쪎; á„ᅧᆱ; ) HANGUL SYLLABLE JJYEOLM
+CA8F;CA8F;110D 1167 11B2;CA8F;110D 1167 11B2; # (ìª; ìª; á„ᅧᆲ; ìª; á„ᅧᆲ; ) HANGUL SYLLABLE JJYEOLB
+CA90;CA90;110D 1167 11B3;CA90;110D 1167 11B3; # (ìª; ìª; á„ᅧᆳ; ìª; á„ᅧᆳ; ) HANGUL SYLLABLE JJYEOLS
+CA91;CA91;110D 1167 11B4;CA91;110D 1167 11B4; # (쪑; 쪑; á„ᅧᆴ; 쪑; á„ᅧᆴ; ) HANGUL SYLLABLE JJYEOLT
+CA92;CA92;110D 1167 11B5;CA92;110D 1167 11B5; # (쪒; 쪒; á„ᅧᆵ; 쪒; á„ᅧᆵ; ) HANGUL SYLLABLE JJYEOLP
+CA93;CA93;110D 1167 11B6;CA93;110D 1167 11B6; # (쪓; 쪓; á„ᅧᆶ; 쪓; á„ᅧᆶ; ) HANGUL SYLLABLE JJYEOLH
+CA94;CA94;110D 1167 11B7;CA94;110D 1167 11B7; # (쪔; 쪔; á„ᅧᆷ; 쪔; á„ᅧᆷ; ) HANGUL SYLLABLE JJYEOM
+CA95;CA95;110D 1167 11B8;CA95;110D 1167 11B8; # (쪕; 쪕; á„ᅧᆸ; 쪕; á„ᅧᆸ; ) HANGUL SYLLABLE JJYEOB
+CA96;CA96;110D 1167 11B9;CA96;110D 1167 11B9; # (쪖; 쪖; á„ᅧᆹ; 쪖; á„ᅧᆹ; ) HANGUL SYLLABLE JJYEOBS
+CA97;CA97;110D 1167 11BA;CA97;110D 1167 11BA; # (쪗; 쪗; á„ᅧᆺ; 쪗; á„ᅧᆺ; ) HANGUL SYLLABLE JJYEOS
+CA98;CA98;110D 1167 11BB;CA98;110D 1167 11BB; # (쪘; 쪘; á„ᅧᆻ; 쪘; á„ᅧᆻ; ) HANGUL SYLLABLE JJYEOSS
+CA99;CA99;110D 1167 11BC;CA99;110D 1167 11BC; # (쪙; 쪙; á„ᅧᆼ; 쪙; á„ᅧᆼ; ) HANGUL SYLLABLE JJYEONG
+CA9A;CA9A;110D 1167 11BD;CA9A;110D 1167 11BD; # (쪚; 쪚; á„ᅧᆽ; 쪚; á„ᅧᆽ; ) HANGUL SYLLABLE JJYEOJ
+CA9B;CA9B;110D 1167 11BE;CA9B;110D 1167 11BE; # (쪛; 쪛; á„ᅧᆾ; 쪛; á„ᅧᆾ; ) HANGUL SYLLABLE JJYEOC
+CA9C;CA9C;110D 1167 11BF;CA9C;110D 1167 11BF; # (쪜; 쪜; á„ᅧᆿ; 쪜; á„ᅧᆿ; ) HANGUL SYLLABLE JJYEOK
+CA9D;CA9D;110D 1167 11C0;CA9D;110D 1167 11C0; # (ìª; ìª; á„ᅧᇀ; ìª; á„ᅧᇀ; ) HANGUL SYLLABLE JJYEOT
+CA9E;CA9E;110D 1167 11C1;CA9E;110D 1167 11C1; # (쪞; 쪞; á„á…§á‡; 쪞; á„á…§á‡; ) HANGUL SYLLABLE JJYEOP
+CA9F;CA9F;110D 1167 11C2;CA9F;110D 1167 11C2; # (쪟; 쪟; á„ᅧᇂ; 쪟; á„ᅧᇂ; ) HANGUL SYLLABLE JJYEOH
+CAA0;CAA0;110D 1168;CAA0;110D 1168; # (쪠; 쪠; á„á…¨; 쪠; á„á…¨; ) HANGUL SYLLABLE JJYE
+CAA1;CAA1;110D 1168 11A8;CAA1;110D 1168 11A8; # (쪡; 쪡; á„ᅨᆨ; 쪡; á„ᅨᆨ; ) HANGUL SYLLABLE JJYEG
+CAA2;CAA2;110D 1168 11A9;CAA2;110D 1168 11A9; # (쪢; 쪢; á„ᅨᆩ; 쪢; á„ᅨᆩ; ) HANGUL SYLLABLE JJYEGG
+CAA3;CAA3;110D 1168 11AA;CAA3;110D 1168 11AA; # (쪣; 쪣; á„ᅨᆪ; 쪣; á„ᅨᆪ; ) HANGUL SYLLABLE JJYEGS
+CAA4;CAA4;110D 1168 11AB;CAA4;110D 1168 11AB; # (쪤; 쪤; á„ᅨᆫ; 쪤; á„ᅨᆫ; ) HANGUL SYLLABLE JJYEN
+CAA5;CAA5;110D 1168 11AC;CAA5;110D 1168 11AC; # (쪥; 쪥; á„ᅨᆬ; 쪥; á„ᅨᆬ; ) HANGUL SYLLABLE JJYENJ
+CAA6;CAA6;110D 1168 11AD;CAA6;110D 1168 11AD; # (쪦; 쪦; á„ᅨᆭ; 쪦; á„ᅨᆭ; ) HANGUL SYLLABLE JJYENH
+CAA7;CAA7;110D 1168 11AE;CAA7;110D 1168 11AE; # (쪧; 쪧; á„ᅨᆮ; 쪧; á„ᅨᆮ; ) HANGUL SYLLABLE JJYED
+CAA8;CAA8;110D 1168 11AF;CAA8;110D 1168 11AF; # (쪨; 쪨; á„ᅨᆯ; 쪨; á„ᅨᆯ; ) HANGUL SYLLABLE JJYEL
+CAA9;CAA9;110D 1168 11B0;CAA9;110D 1168 11B0; # (쪩; 쪩; á„ᅨᆰ; 쪩; á„ᅨᆰ; ) HANGUL SYLLABLE JJYELG
+CAAA;CAAA;110D 1168 11B1;CAAA;110D 1168 11B1; # (쪪; 쪪; á„ᅨᆱ; 쪪; á„ᅨᆱ; ) HANGUL SYLLABLE JJYELM
+CAAB;CAAB;110D 1168 11B2;CAAB;110D 1168 11B2; # (쪫; 쪫; á„ᅨᆲ; 쪫; á„ᅨᆲ; ) HANGUL SYLLABLE JJYELB
+CAAC;CAAC;110D 1168 11B3;CAAC;110D 1168 11B3; # (쪬; 쪬; á„ᅨᆳ; 쪬; á„ᅨᆳ; ) HANGUL SYLLABLE JJYELS
+CAAD;CAAD;110D 1168 11B4;CAAD;110D 1168 11B4; # (쪭; 쪭; á„ᅨᆴ; 쪭; á„ᅨᆴ; ) HANGUL SYLLABLE JJYELT
+CAAE;CAAE;110D 1168 11B5;CAAE;110D 1168 11B5; # (쪮; 쪮; á„ᅨᆵ; 쪮; á„ᅨᆵ; ) HANGUL SYLLABLE JJYELP
+CAAF;CAAF;110D 1168 11B6;CAAF;110D 1168 11B6; # (쪯; 쪯; á„ᅨᆶ; 쪯; á„ᅨᆶ; ) HANGUL SYLLABLE JJYELH
+CAB0;CAB0;110D 1168 11B7;CAB0;110D 1168 11B7; # (쪰; 쪰; á„ᅨᆷ; 쪰; á„ᅨᆷ; ) HANGUL SYLLABLE JJYEM
+CAB1;CAB1;110D 1168 11B8;CAB1;110D 1168 11B8; # (쪱; 쪱; á„ᅨᆸ; 쪱; á„ᅨᆸ; ) HANGUL SYLLABLE JJYEB
+CAB2;CAB2;110D 1168 11B9;CAB2;110D 1168 11B9; # (쪲; 쪲; á„ᅨᆹ; 쪲; á„ᅨᆹ; ) HANGUL SYLLABLE JJYEBS
+CAB3;CAB3;110D 1168 11BA;CAB3;110D 1168 11BA; # (쪳; 쪳; á„ᅨᆺ; 쪳; á„ᅨᆺ; ) HANGUL SYLLABLE JJYES
+CAB4;CAB4;110D 1168 11BB;CAB4;110D 1168 11BB; # (쪴; 쪴; á„ᅨᆻ; 쪴; á„ᅨᆻ; ) HANGUL SYLLABLE JJYESS
+CAB5;CAB5;110D 1168 11BC;CAB5;110D 1168 11BC; # (쪵; 쪵; á„ᅨᆼ; 쪵; á„ᅨᆼ; ) HANGUL SYLLABLE JJYENG
+CAB6;CAB6;110D 1168 11BD;CAB6;110D 1168 11BD; # (쪶; 쪶; á„ᅨᆽ; 쪶; á„ᅨᆽ; ) HANGUL SYLLABLE JJYEJ
+CAB7;CAB7;110D 1168 11BE;CAB7;110D 1168 11BE; # (쪷; 쪷; á„ᅨᆾ; 쪷; á„ᅨᆾ; ) HANGUL SYLLABLE JJYEC
+CAB8;CAB8;110D 1168 11BF;CAB8;110D 1168 11BF; # (쪸; 쪸; á„ᅨᆿ; 쪸; á„ᅨᆿ; ) HANGUL SYLLABLE JJYEK
+CAB9;CAB9;110D 1168 11C0;CAB9;110D 1168 11C0; # (쪹; 쪹; á„ᅨᇀ; 쪹; á„ᅨᇀ; ) HANGUL SYLLABLE JJYET
+CABA;CABA;110D 1168 11C1;CABA;110D 1168 11C1; # (쪺; 쪺; á„á…¨á‡; 쪺; á„á…¨á‡; ) HANGUL SYLLABLE JJYEP
+CABB;CABB;110D 1168 11C2;CABB;110D 1168 11C2; # (쪻; 쪻; á„ᅨᇂ; 쪻; á„ᅨᇂ; ) HANGUL SYLLABLE JJYEH
+CABC;CABC;110D 1169;CABC;110D 1169; # (쪼; 쪼; á„á…©; 쪼; á„á…©; ) HANGUL SYLLABLE JJO
+CABD;CABD;110D 1169 11A8;CABD;110D 1169 11A8; # (쪽; 쪽; á„ᅩᆨ; 쪽; á„ᅩᆨ; ) HANGUL SYLLABLE JJOG
+CABE;CABE;110D 1169 11A9;CABE;110D 1169 11A9; # (쪾; 쪾; á„ᅩᆩ; 쪾; á„ᅩᆩ; ) HANGUL SYLLABLE JJOGG
+CABF;CABF;110D 1169 11AA;CABF;110D 1169 11AA; # (쪿; 쪿; á„ᅩᆪ; 쪿; á„ᅩᆪ; ) HANGUL SYLLABLE JJOGS
+CAC0;CAC0;110D 1169 11AB;CAC0;110D 1169 11AB; # (ì«€; ì«€; á„ᅩᆫ; ì«€; á„ᅩᆫ; ) HANGUL SYLLABLE JJON
+CAC1;CAC1;110D 1169 11AC;CAC1;110D 1169 11AC; # (ì«; ì«; á„ᅩᆬ; ì«; á„ᅩᆬ; ) HANGUL SYLLABLE JJONJ
+CAC2;CAC2;110D 1169 11AD;CAC2;110D 1169 11AD; # (ì«‚; ì«‚; á„ᅩᆭ; ì«‚; á„ᅩᆭ; ) HANGUL SYLLABLE JJONH
+CAC3;CAC3;110D 1169 11AE;CAC3;110D 1169 11AE; # (쫃; 쫃; á„ᅩᆮ; 쫃; á„ᅩᆮ; ) HANGUL SYLLABLE JJOD
+CAC4;CAC4;110D 1169 11AF;CAC4;110D 1169 11AF; # (ì«„; ì«„; á„ᅩᆯ; ì«„; á„ᅩᆯ; ) HANGUL SYLLABLE JJOL
+CAC5;CAC5;110D 1169 11B0;CAC5;110D 1169 11B0; # (ì«…; ì«…; á„ᅩᆰ; ì«…; á„ᅩᆰ; ) HANGUL SYLLABLE JJOLG
+CAC6;CAC6;110D 1169 11B1;CAC6;110D 1169 11B1; # (쫆; 쫆; á„ᅩᆱ; 쫆; á„ᅩᆱ; ) HANGUL SYLLABLE JJOLM
+CAC7;CAC7;110D 1169 11B2;CAC7;110D 1169 11B2; # (쫇; 쫇; á„ᅩᆲ; 쫇; á„ᅩᆲ; ) HANGUL SYLLABLE JJOLB
+CAC8;CAC8;110D 1169 11B3;CAC8;110D 1169 11B3; # (쫈; 쫈; á„ᅩᆳ; 쫈; á„ᅩᆳ; ) HANGUL SYLLABLE JJOLS
+CAC9;CAC9;110D 1169 11B4;CAC9;110D 1169 11B4; # (쫉; 쫉; á„ᅩᆴ; 쫉; á„ᅩᆴ; ) HANGUL SYLLABLE JJOLT
+CACA;CACA;110D 1169 11B5;CACA;110D 1169 11B5; # (쫊; 쫊; á„ᅩᆵ; 쫊; á„ᅩᆵ; ) HANGUL SYLLABLE JJOLP
+CACB;CACB;110D 1169 11B6;CACB;110D 1169 11B6; # (ì«‹; ì«‹; á„ᅩᆶ; ì«‹; á„ᅩᆶ; ) HANGUL SYLLABLE JJOLH
+CACC;CACC;110D 1169 11B7;CACC;110D 1169 11B7; # (쫌; 쫌; á„ᅩᆷ; 쫌; á„ᅩᆷ; ) HANGUL SYLLABLE JJOM
+CACD;CACD;110D 1169 11B8;CACD;110D 1169 11B8; # (ì«; ì«; á„ᅩᆸ; ì«; á„ᅩᆸ; ) HANGUL SYLLABLE JJOB
+CACE;CACE;110D 1169 11B9;CACE;110D 1169 11B9; # (쫎; 쫎; á„ᅩᆹ; 쫎; á„ᅩᆹ; ) HANGUL SYLLABLE JJOBS
+CACF;CACF;110D 1169 11BA;CACF;110D 1169 11BA; # (ì«; ì«; á„ᅩᆺ; ì«; á„ᅩᆺ; ) HANGUL SYLLABLE JJOS
+CAD0;CAD0;110D 1169 11BB;CAD0;110D 1169 11BB; # (ì«; ì«; á„ᅩᆻ; ì«; á„ᅩᆻ; ) HANGUL SYLLABLE JJOSS
+CAD1;CAD1;110D 1169 11BC;CAD1;110D 1169 11BC; # (ì«‘; ì«‘; á„ᅩᆼ; ì«‘; á„ᅩᆼ; ) HANGUL SYLLABLE JJONG
+CAD2;CAD2;110D 1169 11BD;CAD2;110D 1169 11BD; # (ì«’; ì«’; á„ᅩᆽ; ì«’; á„ᅩᆽ; ) HANGUL SYLLABLE JJOJ
+CAD3;CAD3;110D 1169 11BE;CAD3;110D 1169 11BE; # (ì«“; ì«“; á„ᅩᆾ; ì«“; á„ᅩᆾ; ) HANGUL SYLLABLE JJOC
+CAD4;CAD4;110D 1169 11BF;CAD4;110D 1169 11BF; # (ì«”; ì«”; á„ᅩᆿ; ì«”; á„ᅩᆿ; ) HANGUL SYLLABLE JJOK
+CAD5;CAD5;110D 1169 11C0;CAD5;110D 1169 11C0; # (ì«•; ì«•; á„ᅩᇀ; ì«•; á„ᅩᇀ; ) HANGUL SYLLABLE JJOT
+CAD6;CAD6;110D 1169 11C1;CAD6;110D 1169 11C1; # (ì«–; ì«–; á„á…©á‡; ì«–; á„á…©á‡; ) HANGUL SYLLABLE JJOP
+CAD7;CAD7;110D 1169 11C2;CAD7;110D 1169 11C2; # (ì«—; ì«—; á„ᅩᇂ; ì«—; á„ᅩᇂ; ) HANGUL SYLLABLE JJOH
+CAD8;CAD8;110D 116A;CAD8;110D 116A; # (쫘; 쫘; á„á…ª; 쫘; á„á…ª; ) HANGUL SYLLABLE JJWA
+CAD9;CAD9;110D 116A 11A8;CAD9;110D 116A 11A8; # (ì«™; ì«™; á„ᅪᆨ; ì«™; á„ᅪᆨ; ) HANGUL SYLLABLE JJWAG
+CADA;CADA;110D 116A 11A9;CADA;110D 116A 11A9; # (쫚; 쫚; á„ᅪᆩ; 쫚; á„ᅪᆩ; ) HANGUL SYLLABLE JJWAGG
+CADB;CADB;110D 116A 11AA;CADB;110D 116A 11AA; # (ì«›; ì«›; á„ᅪᆪ; ì«›; á„ᅪᆪ; ) HANGUL SYLLABLE JJWAGS
+CADC;CADC;110D 116A 11AB;CADC;110D 116A 11AB; # (쫜; 쫜; á„ᅪᆫ; 쫜; á„ᅪᆫ; ) HANGUL SYLLABLE JJWAN
+CADD;CADD;110D 116A 11AC;CADD;110D 116A 11AC; # (ì«; ì«; á„ᅪᆬ; ì«; á„ᅪᆬ; ) HANGUL SYLLABLE JJWANJ
+CADE;CADE;110D 116A 11AD;CADE;110D 116A 11AD; # (쫞; 쫞; á„ᅪᆭ; 쫞; á„ᅪᆭ; ) HANGUL SYLLABLE JJWANH
+CADF;CADF;110D 116A 11AE;CADF;110D 116A 11AE; # (쫟; 쫟; á„ᅪᆮ; 쫟; á„ᅪᆮ; ) HANGUL SYLLABLE JJWAD
+CAE0;CAE0;110D 116A 11AF;CAE0;110D 116A 11AF; # (ì« ; ì« ; á„ᅪᆯ; ì« ; á„ᅪᆯ; ) HANGUL SYLLABLE JJWAL
+CAE1;CAE1;110D 116A 11B0;CAE1;110D 116A 11B0; # (ì«¡; ì«¡; á„ᅪᆰ; ì«¡; á„ᅪᆰ; ) HANGUL SYLLABLE JJWALG
+CAE2;CAE2;110D 116A 11B1;CAE2;110D 116A 11B1; # (ì«¢; ì«¢; á„ᅪᆱ; ì«¢; á„ᅪᆱ; ) HANGUL SYLLABLE JJWALM
+CAE3;CAE3;110D 116A 11B2;CAE3;110D 116A 11B2; # (ì«£; ì«£; á„ᅪᆲ; ì«£; á„ᅪᆲ; ) HANGUL SYLLABLE JJWALB
+CAE4;CAE4;110D 116A 11B3;CAE4;110D 116A 11B3; # (쫤; 쫤; á„ᅪᆳ; 쫤; á„ᅪᆳ; ) HANGUL SYLLABLE JJWALS
+CAE5;CAE5;110D 116A 11B4;CAE5;110D 116A 11B4; # (ì«¥; ì«¥; á„ᅪᆴ; ì«¥; á„ᅪᆴ; ) HANGUL SYLLABLE JJWALT
+CAE6;CAE6;110D 116A 11B5;CAE6;110D 116A 11B5; # (쫦; 쫦; á„ᅪᆵ; 쫦; á„ᅪᆵ; ) HANGUL SYLLABLE JJWALP
+CAE7;CAE7;110D 116A 11B6;CAE7;110D 116A 11B6; # (ì«§; ì«§; á„ᅪᆶ; ì«§; á„ᅪᆶ; ) HANGUL SYLLABLE JJWALH
+CAE8;CAE8;110D 116A 11B7;CAE8;110D 116A 11B7; # (쫨; 쫨; á„ᅪᆷ; 쫨; á„ᅪᆷ; ) HANGUL SYLLABLE JJWAM
+CAE9;CAE9;110D 116A 11B8;CAE9;110D 116A 11B8; # (ì«©; ì«©; á„ᅪᆸ; ì«©; á„ᅪᆸ; ) HANGUL SYLLABLE JJWAB
+CAEA;CAEA;110D 116A 11B9;CAEA;110D 116A 11B9; # (쫪; 쫪; á„ᅪᆹ; 쫪; á„ᅪᆹ; ) HANGUL SYLLABLE JJWABS
+CAEB;CAEB;110D 116A 11BA;CAEB;110D 116A 11BA; # (ì««; ì««; á„ᅪᆺ; ì««; á„ᅪᆺ; ) HANGUL SYLLABLE JJWAS
+CAEC;CAEC;110D 116A 11BB;CAEC;110D 116A 11BB; # (쫬; 쫬; á„ᅪᆻ; 쫬; á„ᅪᆻ; ) HANGUL SYLLABLE JJWASS
+CAED;CAED;110D 116A 11BC;CAED;110D 116A 11BC; # (ì«­; ì«­; á„ᅪᆼ; ì«­; á„ᅪᆼ; ) HANGUL SYLLABLE JJWANG
+CAEE;CAEE;110D 116A 11BD;CAEE;110D 116A 11BD; # (ì«®; ì«®; á„ᅪᆽ; ì«®; á„ᅪᆽ; ) HANGUL SYLLABLE JJWAJ
+CAEF;CAEF;110D 116A 11BE;CAEF;110D 116A 11BE; # (쫯; 쫯; á„ᅪᆾ; 쫯; á„ᅪᆾ; ) HANGUL SYLLABLE JJWAC
+CAF0;CAF0;110D 116A 11BF;CAF0;110D 116A 11BF; # (ì«°; ì«°; á„ᅪᆿ; ì«°; á„ᅪᆿ; ) HANGUL SYLLABLE JJWAK
+CAF1;CAF1;110D 116A 11C0;CAF1;110D 116A 11C0; # (쫱; 쫱; á„ᅪᇀ; 쫱; á„ᅪᇀ; ) HANGUL SYLLABLE JJWAT
+CAF2;CAF2;110D 116A 11C1;CAF2;110D 116A 11C1; # (쫲; 쫲; á„á…ªá‡; 쫲; á„á…ªá‡; ) HANGUL SYLLABLE JJWAP
+CAF3;CAF3;110D 116A 11C2;CAF3;110D 116A 11C2; # (쫳; 쫳; á„ᅪᇂ; 쫳; á„ᅪᇂ; ) HANGUL SYLLABLE JJWAH
+CAF4;CAF4;110D 116B;CAF4;110D 116B; # (ì«´; ì«´; á„á…«; ì«´; á„á…«; ) HANGUL SYLLABLE JJWAE
+CAF5;CAF5;110D 116B 11A8;CAF5;110D 116B 11A8; # (쫵; 쫵; á„ᅫᆨ; 쫵; á„ᅫᆨ; ) HANGUL SYLLABLE JJWAEG
+CAF6;CAF6;110D 116B 11A9;CAF6;110D 116B 11A9; # (ì«¶; ì«¶; á„ᅫᆩ; ì«¶; á„ᅫᆩ; ) HANGUL SYLLABLE JJWAEGG
+CAF7;CAF7;110D 116B 11AA;CAF7;110D 116B 11AA; # (ì«·; ì«·; á„ᅫᆪ; ì«·; á„ᅫᆪ; ) HANGUL SYLLABLE JJWAEGS
+CAF8;CAF8;110D 116B 11AB;CAF8;110D 116B 11AB; # (쫸; 쫸; á„ᅫᆫ; 쫸; á„ᅫᆫ; ) HANGUL SYLLABLE JJWAEN
+CAF9;CAF9;110D 116B 11AC;CAF9;110D 116B 11AC; # (쫹; 쫹; á„ᅫᆬ; 쫹; á„ᅫᆬ; ) HANGUL SYLLABLE JJWAENJ
+CAFA;CAFA;110D 116B 11AD;CAFA;110D 116B 11AD; # (쫺; 쫺; á„ᅫᆭ; 쫺; á„ᅫᆭ; ) HANGUL SYLLABLE JJWAENH
+CAFB;CAFB;110D 116B 11AE;CAFB;110D 116B 11AE; # (ì«»; ì«»; á„ᅫᆮ; ì«»; á„ᅫᆮ; ) HANGUL SYLLABLE JJWAED
+CAFC;CAFC;110D 116B 11AF;CAFC;110D 116B 11AF; # (쫼; 쫼; á„ᅫᆯ; 쫼; á„ᅫᆯ; ) HANGUL SYLLABLE JJWAEL
+CAFD;CAFD;110D 116B 11B0;CAFD;110D 116B 11B0; # (쫽; 쫽; á„ᅫᆰ; 쫽; á„ᅫᆰ; ) HANGUL SYLLABLE JJWAELG
+CAFE;CAFE;110D 116B 11B1;CAFE;110D 116B 11B1; # (쫾; 쫾; á„ᅫᆱ; 쫾; á„ᅫᆱ; ) HANGUL SYLLABLE JJWAELM
+CAFF;CAFF;110D 116B 11B2;CAFF;110D 116B 11B2; # (ì«¿; ì«¿; á„ᅫᆲ; ì«¿; á„ᅫᆲ; ) HANGUL SYLLABLE JJWAELB
+CB00;CB00;110D 116B 11B3;CB00;110D 116B 11B3; # (쬀; 쬀; á„ᅫᆳ; 쬀; á„ᅫᆳ; ) HANGUL SYLLABLE JJWAELS
+CB01;CB01;110D 116B 11B4;CB01;110D 116B 11B4; # (ì¬; ì¬; á„ᅫᆴ; ì¬; á„ᅫᆴ; ) HANGUL SYLLABLE JJWAELT
+CB02;CB02;110D 116B 11B5;CB02;110D 116B 11B5; # (쬂; 쬂; á„ᅫᆵ; 쬂; á„ᅫᆵ; ) HANGUL SYLLABLE JJWAELP
+CB03;CB03;110D 116B 11B6;CB03;110D 116B 11B6; # (쬃; 쬃; á„ᅫᆶ; 쬃; á„ᅫᆶ; ) HANGUL SYLLABLE JJWAELH
+CB04;CB04;110D 116B 11B7;CB04;110D 116B 11B7; # (쬄; 쬄; á„ᅫᆷ; 쬄; á„ᅫᆷ; ) HANGUL SYLLABLE JJWAEM
+CB05;CB05;110D 116B 11B8;CB05;110D 116B 11B8; # (쬅; 쬅; á„ᅫᆸ; 쬅; á„ᅫᆸ; ) HANGUL SYLLABLE JJWAEB
+CB06;CB06;110D 116B 11B9;CB06;110D 116B 11B9; # (쬆; 쬆; á„ᅫᆹ; 쬆; á„ᅫᆹ; ) HANGUL SYLLABLE JJWAEBS
+CB07;CB07;110D 116B 11BA;CB07;110D 116B 11BA; # (쬇; 쬇; á„ᅫᆺ; 쬇; á„ᅫᆺ; ) HANGUL SYLLABLE JJWAES
+CB08;CB08;110D 116B 11BB;CB08;110D 116B 11BB; # (쬈; 쬈; á„ᅫᆻ; 쬈; á„ᅫᆻ; ) HANGUL SYLLABLE JJWAESS
+CB09;CB09;110D 116B 11BC;CB09;110D 116B 11BC; # (쬉; 쬉; á„ᅫᆼ; 쬉; á„ᅫᆼ; ) HANGUL SYLLABLE JJWAENG
+CB0A;CB0A;110D 116B 11BD;CB0A;110D 116B 11BD; # (쬊; 쬊; á„ᅫᆽ; 쬊; á„ᅫᆽ; ) HANGUL SYLLABLE JJWAEJ
+CB0B;CB0B;110D 116B 11BE;CB0B;110D 116B 11BE; # (쬋; 쬋; á„ᅫᆾ; 쬋; á„ᅫᆾ; ) HANGUL SYLLABLE JJWAEC
+CB0C;CB0C;110D 116B 11BF;CB0C;110D 116B 11BF; # (쬌; 쬌; á„ᅫᆿ; 쬌; á„ᅫᆿ; ) HANGUL SYLLABLE JJWAEK
+CB0D;CB0D;110D 116B 11C0;CB0D;110D 116B 11C0; # (ì¬; ì¬; á„ᅫᇀ; ì¬; á„ᅫᇀ; ) HANGUL SYLLABLE JJWAET
+CB0E;CB0E;110D 116B 11C1;CB0E;110D 116B 11C1; # (쬎; 쬎; á„á…«á‡; 쬎; á„á…«á‡; ) HANGUL SYLLABLE JJWAEP
+CB0F;CB0F;110D 116B 11C2;CB0F;110D 116B 11C2; # (ì¬; ì¬; á„ᅫᇂ; ì¬; á„ᅫᇂ; ) HANGUL SYLLABLE JJWAEH
+CB10;CB10;110D 116C;CB10;110D 116C; # (ì¬; ì¬; á„á…¬; ì¬; á„á…¬; ) HANGUL SYLLABLE JJOE
+CB11;CB11;110D 116C 11A8;CB11;110D 116C 11A8; # (쬑; 쬑; á„ᅬᆨ; 쬑; á„ᅬᆨ; ) HANGUL SYLLABLE JJOEG
+CB12;CB12;110D 116C 11A9;CB12;110D 116C 11A9; # (쬒; 쬒; á„ᅬᆩ; 쬒; á„ᅬᆩ; ) HANGUL SYLLABLE JJOEGG
+CB13;CB13;110D 116C 11AA;CB13;110D 116C 11AA; # (쬓; 쬓; á„ᅬᆪ; 쬓; á„ᅬᆪ; ) HANGUL SYLLABLE JJOEGS
+CB14;CB14;110D 116C 11AB;CB14;110D 116C 11AB; # (쬔; 쬔; á„ᅬᆫ; 쬔; á„ᅬᆫ; ) HANGUL SYLLABLE JJOEN
+CB15;CB15;110D 116C 11AC;CB15;110D 116C 11AC; # (쬕; 쬕; á„ᅬᆬ; 쬕; á„ᅬᆬ; ) HANGUL SYLLABLE JJOENJ
+CB16;CB16;110D 116C 11AD;CB16;110D 116C 11AD; # (쬖; 쬖; á„ᅬᆭ; 쬖; á„ᅬᆭ; ) HANGUL SYLLABLE JJOENH
+CB17;CB17;110D 116C 11AE;CB17;110D 116C 11AE; # (쬗; 쬗; á„ᅬᆮ; 쬗; á„ᅬᆮ; ) HANGUL SYLLABLE JJOED
+CB18;CB18;110D 116C 11AF;CB18;110D 116C 11AF; # (쬘; 쬘; á„ᅬᆯ; 쬘; á„ᅬᆯ; ) HANGUL SYLLABLE JJOEL
+CB19;CB19;110D 116C 11B0;CB19;110D 116C 11B0; # (쬙; 쬙; á„ᅬᆰ; 쬙; á„ᅬᆰ; ) HANGUL SYLLABLE JJOELG
+CB1A;CB1A;110D 116C 11B1;CB1A;110D 116C 11B1; # (쬚; 쬚; á„ᅬᆱ; 쬚; á„ᅬᆱ; ) HANGUL SYLLABLE JJOELM
+CB1B;CB1B;110D 116C 11B2;CB1B;110D 116C 11B2; # (쬛; 쬛; á„ᅬᆲ; 쬛; á„ᅬᆲ; ) HANGUL SYLLABLE JJOELB
+CB1C;CB1C;110D 116C 11B3;CB1C;110D 116C 11B3; # (쬜; 쬜; á„ᅬᆳ; 쬜; á„ᅬᆳ; ) HANGUL SYLLABLE JJOELS
+CB1D;CB1D;110D 116C 11B4;CB1D;110D 116C 11B4; # (ì¬; ì¬; á„ᅬᆴ; ì¬; á„ᅬᆴ; ) HANGUL SYLLABLE JJOELT
+CB1E;CB1E;110D 116C 11B5;CB1E;110D 116C 11B5; # (쬞; 쬞; á„ᅬᆵ; 쬞; á„ᅬᆵ; ) HANGUL SYLLABLE JJOELP
+CB1F;CB1F;110D 116C 11B6;CB1F;110D 116C 11B6; # (쬟; 쬟; á„ᅬᆶ; 쬟; á„ᅬᆶ; ) HANGUL SYLLABLE JJOELH
+CB20;CB20;110D 116C 11B7;CB20;110D 116C 11B7; # (쬠; 쬠; á„ᅬᆷ; 쬠; á„ᅬᆷ; ) HANGUL SYLLABLE JJOEM
+CB21;CB21;110D 116C 11B8;CB21;110D 116C 11B8; # (쬡; 쬡; á„ᅬᆸ; 쬡; á„ᅬᆸ; ) HANGUL SYLLABLE JJOEB
+CB22;CB22;110D 116C 11B9;CB22;110D 116C 11B9; # (쬢; 쬢; á„ᅬᆹ; 쬢; á„ᅬᆹ; ) HANGUL SYLLABLE JJOEBS
+CB23;CB23;110D 116C 11BA;CB23;110D 116C 11BA; # (쬣; 쬣; á„ᅬᆺ; 쬣; á„ᅬᆺ; ) HANGUL SYLLABLE JJOES
+CB24;CB24;110D 116C 11BB;CB24;110D 116C 11BB; # (쬤; 쬤; á„ᅬᆻ; 쬤; á„ᅬᆻ; ) HANGUL SYLLABLE JJOESS
+CB25;CB25;110D 116C 11BC;CB25;110D 116C 11BC; # (쬥; 쬥; á„ᅬᆼ; 쬥; á„ᅬᆼ; ) HANGUL SYLLABLE JJOENG
+CB26;CB26;110D 116C 11BD;CB26;110D 116C 11BD; # (쬦; 쬦; á„ᅬᆽ; 쬦; á„ᅬᆽ; ) HANGUL SYLLABLE JJOEJ
+CB27;CB27;110D 116C 11BE;CB27;110D 116C 11BE; # (쬧; 쬧; á„ᅬᆾ; 쬧; á„ᅬᆾ; ) HANGUL SYLLABLE JJOEC
+CB28;CB28;110D 116C 11BF;CB28;110D 116C 11BF; # (쬨; 쬨; á„ᅬᆿ; 쬨; á„ᅬᆿ; ) HANGUL SYLLABLE JJOEK
+CB29;CB29;110D 116C 11C0;CB29;110D 116C 11C0; # (쬩; 쬩; á„ᅬᇀ; 쬩; á„ᅬᇀ; ) HANGUL SYLLABLE JJOET
+CB2A;CB2A;110D 116C 11C1;CB2A;110D 116C 11C1; # (쬪; 쬪; á„á…¬á‡; 쬪; á„á…¬á‡; ) HANGUL SYLLABLE JJOEP
+CB2B;CB2B;110D 116C 11C2;CB2B;110D 116C 11C2; # (쬫; 쬫; á„ᅬᇂ; 쬫; á„ᅬᇂ; ) HANGUL SYLLABLE JJOEH
+CB2C;CB2C;110D 116D;CB2C;110D 116D; # (쬬; 쬬; á„á…­; 쬬; á„á…­; ) HANGUL SYLLABLE JJYO
+CB2D;CB2D;110D 116D 11A8;CB2D;110D 116D 11A8; # (쬭; 쬭; á„ᅭᆨ; 쬭; á„ᅭᆨ; ) HANGUL SYLLABLE JJYOG
+CB2E;CB2E;110D 116D 11A9;CB2E;110D 116D 11A9; # (쬮; 쬮; á„ᅭᆩ; 쬮; á„ᅭᆩ; ) HANGUL SYLLABLE JJYOGG
+CB2F;CB2F;110D 116D 11AA;CB2F;110D 116D 11AA; # (쬯; 쬯; á„ᅭᆪ; 쬯; á„ᅭᆪ; ) HANGUL SYLLABLE JJYOGS
+CB30;CB30;110D 116D 11AB;CB30;110D 116D 11AB; # (쬰; 쬰; á„ᅭᆫ; 쬰; á„ᅭᆫ; ) HANGUL SYLLABLE JJYON
+CB31;CB31;110D 116D 11AC;CB31;110D 116D 11AC; # (쬱; 쬱; á„ᅭᆬ; 쬱; á„ᅭᆬ; ) HANGUL SYLLABLE JJYONJ
+CB32;CB32;110D 116D 11AD;CB32;110D 116D 11AD; # (쬲; 쬲; á„ᅭᆭ; 쬲; á„ᅭᆭ; ) HANGUL SYLLABLE JJYONH
+CB33;CB33;110D 116D 11AE;CB33;110D 116D 11AE; # (쬳; 쬳; á„ᅭᆮ; 쬳; á„ᅭᆮ; ) HANGUL SYLLABLE JJYOD
+CB34;CB34;110D 116D 11AF;CB34;110D 116D 11AF; # (쬴; 쬴; á„ᅭᆯ; 쬴; á„ᅭᆯ; ) HANGUL SYLLABLE JJYOL
+CB35;CB35;110D 116D 11B0;CB35;110D 116D 11B0; # (쬵; 쬵; á„ᅭᆰ; 쬵; á„ᅭᆰ; ) HANGUL SYLLABLE JJYOLG
+CB36;CB36;110D 116D 11B1;CB36;110D 116D 11B1; # (쬶; 쬶; á„ᅭᆱ; 쬶; á„ᅭᆱ; ) HANGUL SYLLABLE JJYOLM
+CB37;CB37;110D 116D 11B2;CB37;110D 116D 11B2; # (쬷; 쬷; á„ᅭᆲ; 쬷; á„ᅭᆲ; ) HANGUL SYLLABLE JJYOLB
+CB38;CB38;110D 116D 11B3;CB38;110D 116D 11B3; # (쬸; 쬸; á„ᅭᆳ; 쬸; á„ᅭᆳ; ) HANGUL SYLLABLE JJYOLS
+CB39;CB39;110D 116D 11B4;CB39;110D 116D 11B4; # (쬹; 쬹; á„ᅭᆴ; 쬹; á„ᅭᆴ; ) HANGUL SYLLABLE JJYOLT
+CB3A;CB3A;110D 116D 11B5;CB3A;110D 116D 11B5; # (쬺; 쬺; á„ᅭᆵ; 쬺; á„ᅭᆵ; ) HANGUL SYLLABLE JJYOLP
+CB3B;CB3B;110D 116D 11B6;CB3B;110D 116D 11B6; # (쬻; 쬻; á„ᅭᆶ; 쬻; á„ᅭᆶ; ) HANGUL SYLLABLE JJYOLH
+CB3C;CB3C;110D 116D 11B7;CB3C;110D 116D 11B7; # (쬼; 쬼; á„ᅭᆷ; 쬼; á„ᅭᆷ; ) HANGUL SYLLABLE JJYOM
+CB3D;CB3D;110D 116D 11B8;CB3D;110D 116D 11B8; # (쬽; 쬽; á„ᅭᆸ; 쬽; á„ᅭᆸ; ) HANGUL SYLLABLE JJYOB
+CB3E;CB3E;110D 116D 11B9;CB3E;110D 116D 11B9; # (쬾; 쬾; á„ᅭᆹ; 쬾; á„ᅭᆹ; ) HANGUL SYLLABLE JJYOBS
+CB3F;CB3F;110D 116D 11BA;CB3F;110D 116D 11BA; # (쬿; 쬿; á„ᅭᆺ; 쬿; á„ᅭᆺ; ) HANGUL SYLLABLE JJYOS
+CB40;CB40;110D 116D 11BB;CB40;110D 116D 11BB; # (ì­€; ì­€; á„ᅭᆻ; ì­€; á„ᅭᆻ; ) HANGUL SYLLABLE JJYOSS
+CB41;CB41;110D 116D 11BC;CB41;110D 116D 11BC; # (ì­; ì­; á„ᅭᆼ; ì­; á„ᅭᆼ; ) HANGUL SYLLABLE JJYONG
+CB42;CB42;110D 116D 11BD;CB42;110D 116D 11BD; # (ì­‚; ì­‚; á„ᅭᆽ; ì­‚; á„ᅭᆽ; ) HANGUL SYLLABLE JJYOJ
+CB43;CB43;110D 116D 11BE;CB43;110D 116D 11BE; # (ì­ƒ; ì­ƒ; á„ᅭᆾ; ì­ƒ; á„ᅭᆾ; ) HANGUL SYLLABLE JJYOC
+CB44;CB44;110D 116D 11BF;CB44;110D 116D 11BF; # (ì­„; ì­„; á„ᅭᆿ; ì­„; á„ᅭᆿ; ) HANGUL SYLLABLE JJYOK
+CB45;CB45;110D 116D 11C0;CB45;110D 116D 11C0; # (ì­…; ì­…; á„ᅭᇀ; ì­…; á„ᅭᇀ; ) HANGUL SYLLABLE JJYOT
+CB46;CB46;110D 116D 11C1;CB46;110D 116D 11C1; # (ì­†; ì­†; á„á…­á‡; ì­†; á„á…­á‡; ) HANGUL SYLLABLE JJYOP
+CB47;CB47;110D 116D 11C2;CB47;110D 116D 11C2; # (ì­‡; ì­‡; á„ᅭᇂ; ì­‡; á„ᅭᇂ; ) HANGUL SYLLABLE JJYOH
+CB48;CB48;110D 116E;CB48;110D 116E; # (ì­ˆ; ì­ˆ; á„á…®; ì­ˆ; á„á…®; ) HANGUL SYLLABLE JJU
+CB49;CB49;110D 116E 11A8;CB49;110D 116E 11A8; # (ì­‰; ì­‰; á„ᅮᆨ; ì­‰; á„ᅮᆨ; ) HANGUL SYLLABLE JJUG
+CB4A;CB4A;110D 116E 11A9;CB4A;110D 116E 11A9; # (ì­Š; ì­Š; á„ᅮᆩ; ì­Š; á„ᅮᆩ; ) HANGUL SYLLABLE JJUGG
+CB4B;CB4B;110D 116E 11AA;CB4B;110D 116E 11AA; # (ì­‹; ì­‹; á„ᅮᆪ; ì­‹; á„ᅮᆪ; ) HANGUL SYLLABLE JJUGS
+CB4C;CB4C;110D 116E 11AB;CB4C;110D 116E 11AB; # (ì­Œ; ì­Œ; á„ᅮᆫ; ì­Œ; á„ᅮᆫ; ) HANGUL SYLLABLE JJUN
+CB4D;CB4D;110D 116E 11AC;CB4D;110D 116E 11AC; # (ì­; ì­; á„ᅮᆬ; ì­; á„ᅮᆬ; ) HANGUL SYLLABLE JJUNJ
+CB4E;CB4E;110D 116E 11AD;CB4E;110D 116E 11AD; # (ì­Ž; ì­Ž; á„ᅮᆭ; ì­Ž; á„ᅮᆭ; ) HANGUL SYLLABLE JJUNH
+CB4F;CB4F;110D 116E 11AE;CB4F;110D 116E 11AE; # (ì­; ì­; á„ᅮᆮ; ì­; á„ᅮᆮ; ) HANGUL SYLLABLE JJUD
+CB50;CB50;110D 116E 11AF;CB50;110D 116E 11AF; # (ì­; ì­; á„ᅮᆯ; ì­; á„ᅮᆯ; ) HANGUL SYLLABLE JJUL
+CB51;CB51;110D 116E 11B0;CB51;110D 116E 11B0; # (ì­‘; ì­‘; á„ᅮᆰ; ì­‘; á„ᅮᆰ; ) HANGUL SYLLABLE JJULG
+CB52;CB52;110D 116E 11B1;CB52;110D 116E 11B1; # (ì­’; ì­’; á„ᅮᆱ; ì­’; á„ᅮᆱ; ) HANGUL SYLLABLE JJULM
+CB53;CB53;110D 116E 11B2;CB53;110D 116E 11B2; # (ì­“; ì­“; á„ᅮᆲ; ì­“; á„ᅮᆲ; ) HANGUL SYLLABLE JJULB
+CB54;CB54;110D 116E 11B3;CB54;110D 116E 11B3; # (ì­”; ì­”; á„ᅮᆳ; ì­”; á„ᅮᆳ; ) HANGUL SYLLABLE JJULS
+CB55;CB55;110D 116E 11B4;CB55;110D 116E 11B4; # (ì­•; ì­•; á„ᅮᆴ; ì­•; á„ᅮᆴ; ) HANGUL SYLLABLE JJULT
+CB56;CB56;110D 116E 11B5;CB56;110D 116E 11B5; # (ì­–; ì­–; á„ᅮᆵ; ì­–; á„ᅮᆵ; ) HANGUL SYLLABLE JJULP
+CB57;CB57;110D 116E 11B6;CB57;110D 116E 11B6; # (ì­—; ì­—; á„ᅮᆶ; ì­—; á„ᅮᆶ; ) HANGUL SYLLABLE JJULH
+CB58;CB58;110D 116E 11B7;CB58;110D 116E 11B7; # (ì­˜; ì­˜; á„ᅮᆷ; ì­˜; á„ᅮᆷ; ) HANGUL SYLLABLE JJUM
+CB59;CB59;110D 116E 11B8;CB59;110D 116E 11B8; # (ì­™; ì­™; á„ᅮᆸ; ì­™; á„ᅮᆸ; ) HANGUL SYLLABLE JJUB
+CB5A;CB5A;110D 116E 11B9;CB5A;110D 116E 11B9; # (ì­š; ì­š; á„ᅮᆹ; ì­š; á„ᅮᆹ; ) HANGUL SYLLABLE JJUBS
+CB5B;CB5B;110D 116E 11BA;CB5B;110D 116E 11BA; # (ì­›; ì­›; á„ᅮᆺ; ì­›; á„ᅮᆺ; ) HANGUL SYLLABLE JJUS
+CB5C;CB5C;110D 116E 11BB;CB5C;110D 116E 11BB; # (ì­œ; ì­œ; á„ᅮᆻ; ì­œ; á„ᅮᆻ; ) HANGUL SYLLABLE JJUSS
+CB5D;CB5D;110D 116E 11BC;CB5D;110D 116E 11BC; # (ì­; ì­; á„ᅮᆼ; ì­; á„ᅮᆼ; ) HANGUL SYLLABLE JJUNG
+CB5E;CB5E;110D 116E 11BD;CB5E;110D 116E 11BD; # (ì­ž; ì­ž; á„ᅮᆽ; ì­ž; á„ᅮᆽ; ) HANGUL SYLLABLE JJUJ
+CB5F;CB5F;110D 116E 11BE;CB5F;110D 116E 11BE; # (ì­Ÿ; ì­Ÿ; á„ᅮᆾ; ì­Ÿ; á„ᅮᆾ; ) HANGUL SYLLABLE JJUC
+CB60;CB60;110D 116E 11BF;CB60;110D 116E 11BF; # (ì­ ; ì­ ; á„ᅮᆿ; ì­ ; á„ᅮᆿ; ) HANGUL SYLLABLE JJUK
+CB61;CB61;110D 116E 11C0;CB61;110D 116E 11C0; # (ì­¡; ì­¡; á„ᅮᇀ; ì­¡; á„ᅮᇀ; ) HANGUL SYLLABLE JJUT
+CB62;CB62;110D 116E 11C1;CB62;110D 116E 11C1; # (ì­¢; ì­¢; á„á…®á‡; ì­¢; á„á…®á‡; ) HANGUL SYLLABLE JJUP
+CB63;CB63;110D 116E 11C2;CB63;110D 116E 11C2; # (ì­£; ì­£; á„ᅮᇂ; ì­£; á„ᅮᇂ; ) HANGUL SYLLABLE JJUH
+CB64;CB64;110D 116F;CB64;110D 116F; # (ì­¤; ì­¤; á„á…¯; ì­¤; á„á…¯; ) HANGUL SYLLABLE JJWEO
+CB65;CB65;110D 116F 11A8;CB65;110D 116F 11A8; # (ì­¥; ì­¥; á„ᅯᆨ; ì­¥; á„ᅯᆨ; ) HANGUL SYLLABLE JJWEOG
+CB66;CB66;110D 116F 11A9;CB66;110D 116F 11A9; # (ì­¦; ì­¦; á„ᅯᆩ; ì­¦; á„ᅯᆩ; ) HANGUL SYLLABLE JJWEOGG
+CB67;CB67;110D 116F 11AA;CB67;110D 116F 11AA; # (ì­§; ì­§; á„ᅯᆪ; ì­§; á„ᅯᆪ; ) HANGUL SYLLABLE JJWEOGS
+CB68;CB68;110D 116F 11AB;CB68;110D 116F 11AB; # (ì­¨; ì­¨; á„ᅯᆫ; ì­¨; á„ᅯᆫ; ) HANGUL SYLLABLE JJWEON
+CB69;CB69;110D 116F 11AC;CB69;110D 116F 11AC; # (ì­©; ì­©; á„ᅯᆬ; ì­©; á„ᅯᆬ; ) HANGUL SYLLABLE JJWEONJ
+CB6A;CB6A;110D 116F 11AD;CB6A;110D 116F 11AD; # (ì­ª; ì­ª; á„ᅯᆭ; ì­ª; á„ᅯᆭ; ) HANGUL SYLLABLE JJWEONH
+CB6B;CB6B;110D 116F 11AE;CB6B;110D 116F 11AE; # (ì­«; ì­«; á„ᅯᆮ; ì­«; á„ᅯᆮ; ) HANGUL SYLLABLE JJWEOD
+CB6C;CB6C;110D 116F 11AF;CB6C;110D 116F 11AF; # (ì­¬; ì­¬; á„ᅯᆯ; ì­¬; á„ᅯᆯ; ) HANGUL SYLLABLE JJWEOL
+CB6D;CB6D;110D 116F 11B0;CB6D;110D 116F 11B0; # (ì­­; ì­­; á„ᅯᆰ; ì­­; á„ᅯᆰ; ) HANGUL SYLLABLE JJWEOLG
+CB6E;CB6E;110D 116F 11B1;CB6E;110D 116F 11B1; # (ì­®; ì­®; á„ᅯᆱ; ì­®; á„ᅯᆱ; ) HANGUL SYLLABLE JJWEOLM
+CB6F;CB6F;110D 116F 11B2;CB6F;110D 116F 11B2; # (ì­¯; ì­¯; á„ᅯᆲ; ì­¯; á„ᅯᆲ; ) HANGUL SYLLABLE JJWEOLB
+CB70;CB70;110D 116F 11B3;CB70;110D 116F 11B3; # (ì­°; ì­°; á„ᅯᆳ; ì­°; á„ᅯᆳ; ) HANGUL SYLLABLE JJWEOLS
+CB71;CB71;110D 116F 11B4;CB71;110D 116F 11B4; # (ì­±; ì­±; á„ᅯᆴ; ì­±; á„ᅯᆴ; ) HANGUL SYLLABLE JJWEOLT
+CB72;CB72;110D 116F 11B5;CB72;110D 116F 11B5; # (ì­²; ì­²; á„ᅯᆵ; ì­²; á„ᅯᆵ; ) HANGUL SYLLABLE JJWEOLP
+CB73;CB73;110D 116F 11B6;CB73;110D 116F 11B6; # (ì­³; ì­³; á„ᅯᆶ; ì­³; á„ᅯᆶ; ) HANGUL SYLLABLE JJWEOLH
+CB74;CB74;110D 116F 11B7;CB74;110D 116F 11B7; # (ì­´; ì­´; á„ᅯᆷ; ì­´; á„ᅯᆷ; ) HANGUL SYLLABLE JJWEOM
+CB75;CB75;110D 116F 11B8;CB75;110D 116F 11B8; # (ì­µ; ì­µ; á„ᅯᆸ; ì­µ; á„ᅯᆸ; ) HANGUL SYLLABLE JJWEOB
+CB76;CB76;110D 116F 11B9;CB76;110D 116F 11B9; # (ì­¶; ì­¶; á„ᅯᆹ; ì­¶; á„ᅯᆹ; ) HANGUL SYLLABLE JJWEOBS
+CB77;CB77;110D 116F 11BA;CB77;110D 116F 11BA; # (ì­·; ì­·; á„ᅯᆺ; ì­·; á„ᅯᆺ; ) HANGUL SYLLABLE JJWEOS
+CB78;CB78;110D 116F 11BB;CB78;110D 116F 11BB; # (ì­¸; ì­¸; á„ᅯᆻ; ì­¸; á„ᅯᆻ; ) HANGUL SYLLABLE JJWEOSS
+CB79;CB79;110D 116F 11BC;CB79;110D 116F 11BC; # (ì­¹; ì­¹; á„ᅯᆼ; ì­¹; á„ᅯᆼ; ) HANGUL SYLLABLE JJWEONG
+CB7A;CB7A;110D 116F 11BD;CB7A;110D 116F 11BD; # (ì­º; ì­º; á„ᅯᆽ; ì­º; á„ᅯᆽ; ) HANGUL SYLLABLE JJWEOJ
+CB7B;CB7B;110D 116F 11BE;CB7B;110D 116F 11BE; # (ì­»; ì­»; á„ᅯᆾ; ì­»; á„ᅯᆾ; ) HANGUL SYLLABLE JJWEOC
+CB7C;CB7C;110D 116F 11BF;CB7C;110D 116F 11BF; # (ì­¼; ì­¼; á„ᅯᆿ; ì­¼; á„ᅯᆿ; ) HANGUL SYLLABLE JJWEOK
+CB7D;CB7D;110D 116F 11C0;CB7D;110D 116F 11C0; # (ì­½; ì­½; á„ᅯᇀ; ì­½; á„ᅯᇀ; ) HANGUL SYLLABLE JJWEOT
+CB7E;CB7E;110D 116F 11C1;CB7E;110D 116F 11C1; # (ì­¾; ì­¾; á„á…¯á‡; ì­¾; á„á…¯á‡; ) HANGUL SYLLABLE JJWEOP
+CB7F;CB7F;110D 116F 11C2;CB7F;110D 116F 11C2; # (ì­¿; ì­¿; á„ᅯᇂ; ì­¿; á„ᅯᇂ; ) HANGUL SYLLABLE JJWEOH
+CB80;CB80;110D 1170;CB80;110D 1170; # (쮀; 쮀; á„á…°; 쮀; á„á…°; ) HANGUL SYLLABLE JJWE
+CB81;CB81;110D 1170 11A8;CB81;110D 1170 11A8; # (ì®; ì®; á„ᅰᆨ; ì®; á„ᅰᆨ; ) HANGUL SYLLABLE JJWEG
+CB82;CB82;110D 1170 11A9;CB82;110D 1170 11A9; # (쮂; 쮂; á„ᅰᆩ; 쮂; á„ᅰᆩ; ) HANGUL SYLLABLE JJWEGG
+CB83;CB83;110D 1170 11AA;CB83;110D 1170 11AA; # (쮃; 쮃; á„ᅰᆪ; 쮃; á„ᅰᆪ; ) HANGUL SYLLABLE JJWEGS
+CB84;CB84;110D 1170 11AB;CB84;110D 1170 11AB; # (쮄; 쮄; á„ᅰᆫ; 쮄; á„ᅰᆫ; ) HANGUL SYLLABLE JJWEN
+CB85;CB85;110D 1170 11AC;CB85;110D 1170 11AC; # (ì®…; ì®…; á„ᅰᆬ; ì®…; á„ᅰᆬ; ) HANGUL SYLLABLE JJWENJ
+CB86;CB86;110D 1170 11AD;CB86;110D 1170 11AD; # (쮆; 쮆; á„ᅰᆭ; 쮆; á„ᅰᆭ; ) HANGUL SYLLABLE JJWENH
+CB87;CB87;110D 1170 11AE;CB87;110D 1170 11AE; # (쮇; 쮇; á„ᅰᆮ; 쮇; á„ᅰᆮ; ) HANGUL SYLLABLE JJWED
+CB88;CB88;110D 1170 11AF;CB88;110D 1170 11AF; # (쮈; 쮈; á„ᅰᆯ; 쮈; á„ᅰᆯ; ) HANGUL SYLLABLE JJWEL
+CB89;CB89;110D 1170 11B0;CB89;110D 1170 11B0; # (쮉; 쮉; á„ᅰᆰ; 쮉; á„ᅰᆰ; ) HANGUL SYLLABLE JJWELG
+CB8A;CB8A;110D 1170 11B1;CB8A;110D 1170 11B1; # (쮊; 쮊; á„ᅰᆱ; 쮊; á„ᅰᆱ; ) HANGUL SYLLABLE JJWELM
+CB8B;CB8B;110D 1170 11B2;CB8B;110D 1170 11B2; # (쮋; 쮋; á„ᅰᆲ; 쮋; á„ᅰᆲ; ) HANGUL SYLLABLE JJWELB
+CB8C;CB8C;110D 1170 11B3;CB8C;110D 1170 11B3; # (쮌; 쮌; á„ᅰᆳ; 쮌; á„ᅰᆳ; ) HANGUL SYLLABLE JJWELS
+CB8D;CB8D;110D 1170 11B4;CB8D;110D 1170 11B4; # (ì®; ì®; á„ᅰᆴ; ì®; á„ᅰᆴ; ) HANGUL SYLLABLE JJWELT
+CB8E;CB8E;110D 1170 11B5;CB8E;110D 1170 11B5; # (쮎; 쮎; á„ᅰᆵ; 쮎; á„ᅰᆵ; ) HANGUL SYLLABLE JJWELP
+CB8F;CB8F;110D 1170 11B6;CB8F;110D 1170 11B6; # (ì®; ì®; á„ᅰᆶ; ì®; á„ᅰᆶ; ) HANGUL SYLLABLE JJWELH
+CB90;CB90;110D 1170 11B7;CB90;110D 1170 11B7; # (ì®; ì®; á„ᅰᆷ; ì®; á„ᅰᆷ; ) HANGUL SYLLABLE JJWEM
+CB91;CB91;110D 1170 11B8;CB91;110D 1170 11B8; # (쮑; 쮑; á„ᅰᆸ; 쮑; á„ᅰᆸ; ) HANGUL SYLLABLE JJWEB
+CB92;CB92;110D 1170 11B9;CB92;110D 1170 11B9; # (ì®’; ì®’; á„ᅰᆹ; ì®’; á„ᅰᆹ; ) HANGUL SYLLABLE JJWEBS
+CB93;CB93;110D 1170 11BA;CB93;110D 1170 11BA; # (쮓; 쮓; á„ᅰᆺ; 쮓; á„ᅰᆺ; ) HANGUL SYLLABLE JJWES
+CB94;CB94;110D 1170 11BB;CB94;110D 1170 11BB; # (ì®”; ì®”; á„ᅰᆻ; ì®”; á„ᅰᆻ; ) HANGUL SYLLABLE JJWESS
+CB95;CB95;110D 1170 11BC;CB95;110D 1170 11BC; # (쮕; 쮕; á„ᅰᆼ; 쮕; á„ᅰᆼ; ) HANGUL SYLLABLE JJWENG
+CB96;CB96;110D 1170 11BD;CB96;110D 1170 11BD; # (ì®–; ì®–; á„ᅰᆽ; ì®–; á„ᅰᆽ; ) HANGUL SYLLABLE JJWEJ
+CB97;CB97;110D 1170 11BE;CB97;110D 1170 11BE; # (ì®—; ì®—; á„ᅰᆾ; ì®—; á„ᅰᆾ; ) HANGUL SYLLABLE JJWEC
+CB98;CB98;110D 1170 11BF;CB98;110D 1170 11BF; # (쮘; 쮘; á„ᅰᆿ; 쮘; á„ᅰᆿ; ) HANGUL SYLLABLE JJWEK
+CB99;CB99;110D 1170 11C0;CB99;110D 1170 11C0; # (ì®™; ì®™; á„ᅰᇀ; ì®™; á„ᅰᇀ; ) HANGUL SYLLABLE JJWET
+CB9A;CB9A;110D 1170 11C1;CB9A;110D 1170 11C1; # (쮚; 쮚; á„á…°á‡; 쮚; á„á…°á‡; ) HANGUL SYLLABLE JJWEP
+CB9B;CB9B;110D 1170 11C2;CB9B;110D 1170 11C2; # (ì®›; ì®›; á„ᅰᇂ; ì®›; á„ᅰᇂ; ) HANGUL SYLLABLE JJWEH
+CB9C;CB9C;110D 1171;CB9C;110D 1171; # (쮜; 쮜; á„á…±; 쮜; á„á…±; ) HANGUL SYLLABLE JJWI
+CB9D;CB9D;110D 1171 11A8;CB9D;110D 1171 11A8; # (ì®; ì®; á„ᅱᆨ; ì®; á„ᅱᆨ; ) HANGUL SYLLABLE JJWIG
+CB9E;CB9E;110D 1171 11A9;CB9E;110D 1171 11A9; # (쮞; 쮞; á„ᅱᆩ; 쮞; á„ᅱᆩ; ) HANGUL SYLLABLE JJWIGG
+CB9F;CB9F;110D 1171 11AA;CB9F;110D 1171 11AA; # (쮟; 쮟; á„ᅱᆪ; 쮟; á„ᅱᆪ; ) HANGUL SYLLABLE JJWIGS
+CBA0;CBA0;110D 1171 11AB;CBA0;110D 1171 11AB; # (ì® ; ì® ; á„ᅱᆫ; ì® ; á„ᅱᆫ; ) HANGUL SYLLABLE JJWIN
+CBA1;CBA1;110D 1171 11AC;CBA1;110D 1171 11AC; # (쮡; 쮡; á„ᅱᆬ; 쮡; á„ᅱᆬ; ) HANGUL SYLLABLE JJWINJ
+CBA2;CBA2;110D 1171 11AD;CBA2;110D 1171 11AD; # (쮢; 쮢; á„ᅱᆭ; 쮢; á„ᅱᆭ; ) HANGUL SYLLABLE JJWINH
+CBA3;CBA3;110D 1171 11AE;CBA3;110D 1171 11AE; # (쮣; 쮣; á„ᅱᆮ; 쮣; á„ᅱᆮ; ) HANGUL SYLLABLE JJWID
+CBA4;CBA4;110D 1171 11AF;CBA4;110D 1171 11AF; # (쮤; 쮤; á„ᅱᆯ; 쮤; á„ᅱᆯ; ) HANGUL SYLLABLE JJWIL
+CBA5;CBA5;110D 1171 11B0;CBA5;110D 1171 11B0; # (쮥; 쮥; á„ᅱᆰ; 쮥; á„ᅱᆰ; ) HANGUL SYLLABLE JJWILG
+CBA6;CBA6;110D 1171 11B1;CBA6;110D 1171 11B1; # (쮦; 쮦; á„ᅱᆱ; 쮦; á„ᅱᆱ; ) HANGUL SYLLABLE JJWILM
+CBA7;CBA7;110D 1171 11B2;CBA7;110D 1171 11B2; # (ì®§; ì®§; á„ᅱᆲ; ì®§; á„ᅱᆲ; ) HANGUL SYLLABLE JJWILB
+CBA8;CBA8;110D 1171 11B3;CBA8;110D 1171 11B3; # (쮨; 쮨; á„ᅱᆳ; 쮨; á„ᅱᆳ; ) HANGUL SYLLABLE JJWILS
+CBA9;CBA9;110D 1171 11B4;CBA9;110D 1171 11B4; # (쮩; 쮩; á„ᅱᆴ; 쮩; á„ᅱᆴ; ) HANGUL SYLLABLE JJWILT
+CBAA;CBAA;110D 1171 11B5;CBAA;110D 1171 11B5; # (쮪; 쮪; á„ᅱᆵ; 쮪; á„ᅱᆵ; ) HANGUL SYLLABLE JJWILP
+CBAB;CBAB;110D 1171 11B6;CBAB;110D 1171 11B6; # (쮫; 쮫; á„ᅱᆶ; 쮫; á„ᅱᆶ; ) HANGUL SYLLABLE JJWILH
+CBAC;CBAC;110D 1171 11B7;CBAC;110D 1171 11B7; # (쮬; 쮬; á„ᅱᆷ; 쮬; á„ᅱᆷ; ) HANGUL SYLLABLE JJWIM
+CBAD;CBAD;110D 1171 11B8;CBAD;110D 1171 11B8; # (ì®­; ì®­; á„ᅱᆸ; ì®­; á„ᅱᆸ; ) HANGUL SYLLABLE JJWIB
+CBAE;CBAE;110D 1171 11B9;CBAE;110D 1171 11B9; # (ì®®; ì®®; á„ᅱᆹ; ì®®; á„ᅱᆹ; ) HANGUL SYLLABLE JJWIBS
+CBAF;CBAF;110D 1171 11BA;CBAF;110D 1171 11BA; # (쮯; 쮯; á„ᅱᆺ; 쮯; á„ᅱᆺ; ) HANGUL SYLLABLE JJWIS
+CBB0;CBB0;110D 1171 11BB;CBB0;110D 1171 11BB; # (ì®°; ì®°; á„ᅱᆻ; ì®°; á„ᅱᆻ; ) HANGUL SYLLABLE JJWISS
+CBB1;CBB1;110D 1171 11BC;CBB1;110D 1171 11BC; # (ì®±; ì®±; á„ᅱᆼ; ì®±; á„ᅱᆼ; ) HANGUL SYLLABLE JJWING
+CBB2;CBB2;110D 1171 11BD;CBB2;110D 1171 11BD; # (쮲; 쮲; á„ᅱᆽ; 쮲; á„ᅱᆽ; ) HANGUL SYLLABLE JJWIJ
+CBB3;CBB3;110D 1171 11BE;CBB3;110D 1171 11BE; # (쮳; 쮳; á„ᅱᆾ; 쮳; á„ᅱᆾ; ) HANGUL SYLLABLE JJWIC
+CBB4;CBB4;110D 1171 11BF;CBB4;110D 1171 11BF; # (ì®´; ì®´; á„ᅱᆿ; ì®´; á„ᅱᆿ; ) HANGUL SYLLABLE JJWIK
+CBB5;CBB5;110D 1171 11C0;CBB5;110D 1171 11C0; # (쮵; 쮵; á„ᅱᇀ; 쮵; á„ᅱᇀ; ) HANGUL SYLLABLE JJWIT
+CBB6;CBB6;110D 1171 11C1;CBB6;110D 1171 11C1; # (ì®¶; ì®¶; á„á…±á‡; ì®¶; á„á…±á‡; ) HANGUL SYLLABLE JJWIP
+CBB7;CBB7;110D 1171 11C2;CBB7;110D 1171 11C2; # (ì®·; ì®·; á„ᅱᇂ; ì®·; á„ᅱᇂ; ) HANGUL SYLLABLE JJWIH
+CBB8;CBB8;110D 1172;CBB8;110D 1172; # (쮸; 쮸; á„á…²; 쮸; á„á…²; ) HANGUL SYLLABLE JJYU
+CBB9;CBB9;110D 1172 11A8;CBB9;110D 1172 11A8; # (쮹; 쮹; á„ᅲᆨ; 쮹; á„ᅲᆨ; ) HANGUL SYLLABLE JJYUG
+CBBA;CBBA;110D 1172 11A9;CBBA;110D 1172 11A9; # (쮺; 쮺; á„ᅲᆩ; 쮺; á„ᅲᆩ; ) HANGUL SYLLABLE JJYUGG
+CBBB;CBBB;110D 1172 11AA;CBBB;110D 1172 11AA; # (ì®»; ì®»; á„ᅲᆪ; ì®»; á„ᅲᆪ; ) HANGUL SYLLABLE JJYUGS
+CBBC;CBBC;110D 1172 11AB;CBBC;110D 1172 11AB; # (쮼; 쮼; á„ᅲᆫ; 쮼; á„ᅲᆫ; ) HANGUL SYLLABLE JJYUN
+CBBD;CBBD;110D 1172 11AC;CBBD;110D 1172 11AC; # (쮽; 쮽; á„ᅲᆬ; 쮽; á„ᅲᆬ; ) HANGUL SYLLABLE JJYUNJ
+CBBE;CBBE;110D 1172 11AD;CBBE;110D 1172 11AD; # (쮾; 쮾; á„ᅲᆭ; 쮾; á„ᅲᆭ; ) HANGUL SYLLABLE JJYUNH
+CBBF;CBBF;110D 1172 11AE;CBBF;110D 1172 11AE; # (쮿; 쮿; á„ᅲᆮ; 쮿; á„ᅲᆮ; ) HANGUL SYLLABLE JJYUD
+CBC0;CBC0;110D 1172 11AF;CBC0;110D 1172 11AF; # (쯀; 쯀; á„ᅲᆯ; 쯀; á„ᅲᆯ; ) HANGUL SYLLABLE JJYUL
+CBC1;CBC1;110D 1172 11B0;CBC1;110D 1172 11B0; # (ì¯; ì¯; á„ᅲᆰ; ì¯; á„ᅲᆰ; ) HANGUL SYLLABLE JJYULG
+CBC2;CBC2;110D 1172 11B1;CBC2;110D 1172 11B1; # (쯂; 쯂; á„ᅲᆱ; 쯂; á„ᅲᆱ; ) HANGUL SYLLABLE JJYULM
+CBC3;CBC3;110D 1172 11B2;CBC3;110D 1172 11B2; # (쯃; 쯃; á„ᅲᆲ; 쯃; á„ᅲᆲ; ) HANGUL SYLLABLE JJYULB
+CBC4;CBC4;110D 1172 11B3;CBC4;110D 1172 11B3; # (쯄; 쯄; á„ᅲᆳ; 쯄; á„ᅲᆳ; ) HANGUL SYLLABLE JJYULS
+CBC5;CBC5;110D 1172 11B4;CBC5;110D 1172 11B4; # (쯅; 쯅; á„ᅲᆴ; 쯅; á„ᅲᆴ; ) HANGUL SYLLABLE JJYULT
+CBC6;CBC6;110D 1172 11B5;CBC6;110D 1172 11B5; # (쯆; 쯆; á„ᅲᆵ; 쯆; á„ᅲᆵ; ) HANGUL SYLLABLE JJYULP
+CBC7;CBC7;110D 1172 11B6;CBC7;110D 1172 11B6; # (쯇; 쯇; á„ᅲᆶ; 쯇; á„ᅲᆶ; ) HANGUL SYLLABLE JJYULH
+CBC8;CBC8;110D 1172 11B7;CBC8;110D 1172 11B7; # (쯈; 쯈; á„ᅲᆷ; 쯈; á„ᅲᆷ; ) HANGUL SYLLABLE JJYUM
+CBC9;CBC9;110D 1172 11B8;CBC9;110D 1172 11B8; # (쯉; 쯉; á„ᅲᆸ; 쯉; á„ᅲᆸ; ) HANGUL SYLLABLE JJYUB
+CBCA;CBCA;110D 1172 11B9;CBCA;110D 1172 11B9; # (쯊; 쯊; á„ᅲᆹ; 쯊; á„ᅲᆹ; ) HANGUL SYLLABLE JJYUBS
+CBCB;CBCB;110D 1172 11BA;CBCB;110D 1172 11BA; # (쯋; 쯋; á„ᅲᆺ; 쯋; á„ᅲᆺ; ) HANGUL SYLLABLE JJYUS
+CBCC;CBCC;110D 1172 11BB;CBCC;110D 1172 11BB; # (쯌; 쯌; á„ᅲᆻ; 쯌; á„ᅲᆻ; ) HANGUL SYLLABLE JJYUSS
+CBCD;CBCD;110D 1172 11BC;CBCD;110D 1172 11BC; # (ì¯; ì¯; á„ᅲᆼ; ì¯; á„ᅲᆼ; ) HANGUL SYLLABLE JJYUNG
+CBCE;CBCE;110D 1172 11BD;CBCE;110D 1172 11BD; # (쯎; 쯎; á„ᅲᆽ; 쯎; á„ᅲᆽ; ) HANGUL SYLLABLE JJYUJ
+CBCF;CBCF;110D 1172 11BE;CBCF;110D 1172 11BE; # (ì¯; ì¯; á„ᅲᆾ; ì¯; á„ᅲᆾ; ) HANGUL SYLLABLE JJYUC
+CBD0;CBD0;110D 1172 11BF;CBD0;110D 1172 11BF; # (ì¯; ì¯; á„ᅲᆿ; ì¯; á„ᅲᆿ; ) HANGUL SYLLABLE JJYUK
+CBD1;CBD1;110D 1172 11C0;CBD1;110D 1172 11C0; # (쯑; 쯑; á„ᅲᇀ; 쯑; á„ᅲᇀ; ) HANGUL SYLLABLE JJYUT
+CBD2;CBD2;110D 1172 11C1;CBD2;110D 1172 11C1; # (쯒; 쯒; á„á…²á‡; 쯒; á„á…²á‡; ) HANGUL SYLLABLE JJYUP
+CBD3;CBD3;110D 1172 11C2;CBD3;110D 1172 11C2; # (쯓; 쯓; á„ᅲᇂ; 쯓; á„ᅲᇂ; ) HANGUL SYLLABLE JJYUH
+CBD4;CBD4;110D 1173;CBD4;110D 1173; # (쯔; 쯔; á„á…³; 쯔; á„á…³; ) HANGUL SYLLABLE JJEU
+CBD5;CBD5;110D 1173 11A8;CBD5;110D 1173 11A8; # (쯕; 쯕; á„ᅳᆨ; 쯕; á„ᅳᆨ; ) HANGUL SYLLABLE JJEUG
+CBD6;CBD6;110D 1173 11A9;CBD6;110D 1173 11A9; # (쯖; 쯖; á„ᅳᆩ; 쯖; á„ᅳᆩ; ) HANGUL SYLLABLE JJEUGG
+CBD7;CBD7;110D 1173 11AA;CBD7;110D 1173 11AA; # (쯗; 쯗; á„ᅳᆪ; 쯗; á„ᅳᆪ; ) HANGUL SYLLABLE JJEUGS
+CBD8;CBD8;110D 1173 11AB;CBD8;110D 1173 11AB; # (쯘; 쯘; á„ᅳᆫ; 쯘; á„ᅳᆫ; ) HANGUL SYLLABLE JJEUN
+CBD9;CBD9;110D 1173 11AC;CBD9;110D 1173 11AC; # (쯙; 쯙; á„ᅳᆬ; 쯙; á„ᅳᆬ; ) HANGUL SYLLABLE JJEUNJ
+CBDA;CBDA;110D 1173 11AD;CBDA;110D 1173 11AD; # (쯚; 쯚; á„ᅳᆭ; 쯚; á„ᅳᆭ; ) HANGUL SYLLABLE JJEUNH
+CBDB;CBDB;110D 1173 11AE;CBDB;110D 1173 11AE; # (쯛; 쯛; á„ᅳᆮ; 쯛; á„ᅳᆮ; ) HANGUL SYLLABLE JJEUD
+CBDC;CBDC;110D 1173 11AF;CBDC;110D 1173 11AF; # (쯜; 쯜; á„ᅳᆯ; 쯜; á„ᅳᆯ; ) HANGUL SYLLABLE JJEUL
+CBDD;CBDD;110D 1173 11B0;CBDD;110D 1173 11B0; # (ì¯; ì¯; á„ᅳᆰ; ì¯; á„ᅳᆰ; ) HANGUL SYLLABLE JJEULG
+CBDE;CBDE;110D 1173 11B1;CBDE;110D 1173 11B1; # (쯞; 쯞; á„ᅳᆱ; 쯞; á„ᅳᆱ; ) HANGUL SYLLABLE JJEULM
+CBDF;CBDF;110D 1173 11B2;CBDF;110D 1173 11B2; # (쯟; 쯟; á„ᅳᆲ; 쯟; á„ᅳᆲ; ) HANGUL SYLLABLE JJEULB
+CBE0;CBE0;110D 1173 11B3;CBE0;110D 1173 11B3; # (쯠; 쯠; á„ᅳᆳ; 쯠; á„ᅳᆳ; ) HANGUL SYLLABLE JJEULS
+CBE1;CBE1;110D 1173 11B4;CBE1;110D 1173 11B4; # (쯡; 쯡; á„ᅳᆴ; 쯡; á„ᅳᆴ; ) HANGUL SYLLABLE JJEULT
+CBE2;CBE2;110D 1173 11B5;CBE2;110D 1173 11B5; # (쯢; 쯢; á„ᅳᆵ; 쯢; á„ᅳᆵ; ) HANGUL SYLLABLE JJEULP
+CBE3;CBE3;110D 1173 11B6;CBE3;110D 1173 11B6; # (쯣; 쯣; á„ᅳᆶ; 쯣; á„ᅳᆶ; ) HANGUL SYLLABLE JJEULH
+CBE4;CBE4;110D 1173 11B7;CBE4;110D 1173 11B7; # (쯤; 쯤; á„ᅳᆷ; 쯤; á„ᅳᆷ; ) HANGUL SYLLABLE JJEUM
+CBE5;CBE5;110D 1173 11B8;CBE5;110D 1173 11B8; # (쯥; 쯥; á„ᅳᆸ; 쯥; á„ᅳᆸ; ) HANGUL SYLLABLE JJEUB
+CBE6;CBE6;110D 1173 11B9;CBE6;110D 1173 11B9; # (쯦; 쯦; á„ᅳᆹ; 쯦; á„ᅳᆹ; ) HANGUL SYLLABLE JJEUBS
+CBE7;CBE7;110D 1173 11BA;CBE7;110D 1173 11BA; # (쯧; 쯧; á„ᅳᆺ; 쯧; á„ᅳᆺ; ) HANGUL SYLLABLE JJEUS
+CBE8;CBE8;110D 1173 11BB;CBE8;110D 1173 11BB; # (쯨; 쯨; á„ᅳᆻ; 쯨; á„ᅳᆻ; ) HANGUL SYLLABLE JJEUSS
+CBE9;CBE9;110D 1173 11BC;CBE9;110D 1173 11BC; # (쯩; 쯩; á„ᅳᆼ; 쯩; á„ᅳᆼ; ) HANGUL SYLLABLE JJEUNG
+CBEA;CBEA;110D 1173 11BD;CBEA;110D 1173 11BD; # (쯪; 쯪; á„ᅳᆽ; 쯪; á„ᅳᆽ; ) HANGUL SYLLABLE JJEUJ
+CBEB;CBEB;110D 1173 11BE;CBEB;110D 1173 11BE; # (쯫; 쯫; á„ᅳᆾ; 쯫; á„ᅳᆾ; ) HANGUL SYLLABLE JJEUC
+CBEC;CBEC;110D 1173 11BF;CBEC;110D 1173 11BF; # (쯬; 쯬; á„ᅳᆿ; 쯬; á„ᅳᆿ; ) HANGUL SYLLABLE JJEUK
+CBED;CBED;110D 1173 11C0;CBED;110D 1173 11C0; # (쯭; 쯭; á„ᅳᇀ; 쯭; á„ᅳᇀ; ) HANGUL SYLLABLE JJEUT
+CBEE;CBEE;110D 1173 11C1;CBEE;110D 1173 11C1; # (쯮; 쯮; á„á…³á‡; 쯮; á„á…³á‡; ) HANGUL SYLLABLE JJEUP
+CBEF;CBEF;110D 1173 11C2;CBEF;110D 1173 11C2; # (쯯; 쯯; á„ᅳᇂ; 쯯; á„ᅳᇂ; ) HANGUL SYLLABLE JJEUH
+CBF0;CBF0;110D 1174;CBF0;110D 1174; # (쯰; 쯰; á„á…´; 쯰; á„á…´; ) HANGUL SYLLABLE JJYI
+CBF1;CBF1;110D 1174 11A8;CBF1;110D 1174 11A8; # (쯱; 쯱; á„ᅴᆨ; 쯱; á„ᅴᆨ; ) HANGUL SYLLABLE JJYIG
+CBF2;CBF2;110D 1174 11A9;CBF2;110D 1174 11A9; # (쯲; 쯲; á„ᅴᆩ; 쯲; á„ᅴᆩ; ) HANGUL SYLLABLE JJYIGG
+CBF3;CBF3;110D 1174 11AA;CBF3;110D 1174 11AA; # (쯳; 쯳; á„ᅴᆪ; 쯳; á„ᅴᆪ; ) HANGUL SYLLABLE JJYIGS
+CBF4;CBF4;110D 1174 11AB;CBF4;110D 1174 11AB; # (쯴; 쯴; á„ᅴᆫ; 쯴; á„ᅴᆫ; ) HANGUL SYLLABLE JJYIN
+CBF5;CBF5;110D 1174 11AC;CBF5;110D 1174 11AC; # (쯵; 쯵; á„ᅴᆬ; 쯵; á„ᅴᆬ; ) HANGUL SYLLABLE JJYINJ
+CBF6;CBF6;110D 1174 11AD;CBF6;110D 1174 11AD; # (쯶; 쯶; á„ᅴᆭ; 쯶; á„ᅴᆭ; ) HANGUL SYLLABLE JJYINH
+CBF7;CBF7;110D 1174 11AE;CBF7;110D 1174 11AE; # (쯷; 쯷; á„ᅴᆮ; 쯷; á„ᅴᆮ; ) HANGUL SYLLABLE JJYID
+CBF8;CBF8;110D 1174 11AF;CBF8;110D 1174 11AF; # (쯸; 쯸; á„ᅴᆯ; 쯸; á„ᅴᆯ; ) HANGUL SYLLABLE JJYIL
+CBF9;CBF9;110D 1174 11B0;CBF9;110D 1174 11B0; # (쯹; 쯹; á„ᅴᆰ; 쯹; á„ᅴᆰ; ) HANGUL SYLLABLE JJYILG
+CBFA;CBFA;110D 1174 11B1;CBFA;110D 1174 11B1; # (쯺; 쯺; á„ᅴᆱ; 쯺; á„ᅴᆱ; ) HANGUL SYLLABLE JJYILM
+CBFB;CBFB;110D 1174 11B2;CBFB;110D 1174 11B2; # (쯻; 쯻; á„ᅴᆲ; 쯻; á„ᅴᆲ; ) HANGUL SYLLABLE JJYILB
+CBFC;CBFC;110D 1174 11B3;CBFC;110D 1174 11B3; # (쯼; 쯼; á„ᅴᆳ; 쯼; á„ᅴᆳ; ) HANGUL SYLLABLE JJYILS
+CBFD;CBFD;110D 1174 11B4;CBFD;110D 1174 11B4; # (쯽; 쯽; á„ᅴᆴ; 쯽; á„ᅴᆴ; ) HANGUL SYLLABLE JJYILT
+CBFE;CBFE;110D 1174 11B5;CBFE;110D 1174 11B5; # (쯾; 쯾; á„ᅴᆵ; 쯾; á„ᅴᆵ; ) HANGUL SYLLABLE JJYILP
+CBFF;CBFF;110D 1174 11B6;CBFF;110D 1174 11B6; # (쯿; 쯿; á„ᅴᆶ; 쯿; á„ᅴᆶ; ) HANGUL SYLLABLE JJYILH
+CC00;CC00;110D 1174 11B7;CC00;110D 1174 11B7; # (ì°€; ì°€; á„ᅴᆷ; ì°€; á„ᅴᆷ; ) HANGUL SYLLABLE JJYIM
+CC01;CC01;110D 1174 11B8;CC01;110D 1174 11B8; # (ì°; ì°; á„ᅴᆸ; ì°; á„ᅴᆸ; ) HANGUL SYLLABLE JJYIB
+CC02;CC02;110D 1174 11B9;CC02;110D 1174 11B9; # (ì°‚; ì°‚; á„ᅴᆹ; ì°‚; á„ᅴᆹ; ) HANGUL SYLLABLE JJYIBS
+CC03;CC03;110D 1174 11BA;CC03;110D 1174 11BA; # (ì°ƒ; ì°ƒ; á„ᅴᆺ; ì°ƒ; á„ᅴᆺ; ) HANGUL SYLLABLE JJYIS
+CC04;CC04;110D 1174 11BB;CC04;110D 1174 11BB; # (ì°„; ì°„; á„ᅴᆻ; ì°„; á„ᅴᆻ; ) HANGUL SYLLABLE JJYISS
+CC05;CC05;110D 1174 11BC;CC05;110D 1174 11BC; # (ì°…; ì°…; á„ᅴᆼ; ì°…; á„ᅴᆼ; ) HANGUL SYLLABLE JJYING
+CC06;CC06;110D 1174 11BD;CC06;110D 1174 11BD; # (ì°†; ì°†; á„ᅴᆽ; ì°†; á„ᅴᆽ; ) HANGUL SYLLABLE JJYIJ
+CC07;CC07;110D 1174 11BE;CC07;110D 1174 11BE; # (ì°‡; ì°‡; á„ᅴᆾ; ì°‡; á„ᅴᆾ; ) HANGUL SYLLABLE JJYIC
+CC08;CC08;110D 1174 11BF;CC08;110D 1174 11BF; # (ì°ˆ; ì°ˆ; á„ᅴᆿ; ì°ˆ; á„ᅴᆿ; ) HANGUL SYLLABLE JJYIK
+CC09;CC09;110D 1174 11C0;CC09;110D 1174 11C0; # (ì°‰; ì°‰; á„ᅴᇀ; ì°‰; á„ᅴᇀ; ) HANGUL SYLLABLE JJYIT
+CC0A;CC0A;110D 1174 11C1;CC0A;110D 1174 11C1; # (ì°Š; ì°Š; á„á…´á‡; ì°Š; á„á…´á‡; ) HANGUL SYLLABLE JJYIP
+CC0B;CC0B;110D 1174 11C2;CC0B;110D 1174 11C2; # (ì°‹; ì°‹; á„ᅴᇂ; ì°‹; á„ᅴᇂ; ) HANGUL SYLLABLE JJYIH
+CC0C;CC0C;110D 1175;CC0C;110D 1175; # (ì°Œ; ì°Œ; á„á…µ; ì°Œ; á„á…µ; ) HANGUL SYLLABLE JJI
+CC0D;CC0D;110D 1175 11A8;CC0D;110D 1175 11A8; # (ì°; ì°; á„ᅵᆨ; ì°; á„ᅵᆨ; ) HANGUL SYLLABLE JJIG
+CC0E;CC0E;110D 1175 11A9;CC0E;110D 1175 11A9; # (ì°Ž; ì°Ž; á„ᅵᆩ; ì°Ž; á„ᅵᆩ; ) HANGUL SYLLABLE JJIGG
+CC0F;CC0F;110D 1175 11AA;CC0F;110D 1175 11AA; # (ì°; ì°; á„ᅵᆪ; ì°; á„ᅵᆪ; ) HANGUL SYLLABLE JJIGS
+CC10;CC10;110D 1175 11AB;CC10;110D 1175 11AB; # (ì°; ì°; á„ᅵᆫ; ì°; á„ᅵᆫ; ) HANGUL SYLLABLE JJIN
+CC11;CC11;110D 1175 11AC;CC11;110D 1175 11AC; # (ì°‘; ì°‘; á„ᅵᆬ; ì°‘; á„ᅵᆬ; ) HANGUL SYLLABLE JJINJ
+CC12;CC12;110D 1175 11AD;CC12;110D 1175 11AD; # (ì°’; ì°’; á„ᅵᆭ; ì°’; á„ᅵᆭ; ) HANGUL SYLLABLE JJINH
+CC13;CC13;110D 1175 11AE;CC13;110D 1175 11AE; # (ì°“; ì°“; á„ᅵᆮ; ì°“; á„ᅵᆮ; ) HANGUL SYLLABLE JJID
+CC14;CC14;110D 1175 11AF;CC14;110D 1175 11AF; # (ì°”; ì°”; á„ᅵᆯ; ì°”; á„ᅵᆯ; ) HANGUL SYLLABLE JJIL
+CC15;CC15;110D 1175 11B0;CC15;110D 1175 11B0; # (ì°•; ì°•; á„ᅵᆰ; ì°•; á„ᅵᆰ; ) HANGUL SYLLABLE JJILG
+CC16;CC16;110D 1175 11B1;CC16;110D 1175 11B1; # (ì°–; ì°–; á„ᅵᆱ; ì°–; á„ᅵᆱ; ) HANGUL SYLLABLE JJILM
+CC17;CC17;110D 1175 11B2;CC17;110D 1175 11B2; # (ì°—; ì°—; á„ᅵᆲ; ì°—; á„ᅵᆲ; ) HANGUL SYLLABLE JJILB
+CC18;CC18;110D 1175 11B3;CC18;110D 1175 11B3; # (ì°˜; ì°˜; á„ᅵᆳ; ì°˜; á„ᅵᆳ; ) HANGUL SYLLABLE JJILS
+CC19;CC19;110D 1175 11B4;CC19;110D 1175 11B4; # (ì°™; ì°™; á„ᅵᆴ; ì°™; á„ᅵᆴ; ) HANGUL SYLLABLE JJILT
+CC1A;CC1A;110D 1175 11B5;CC1A;110D 1175 11B5; # (ì°š; ì°š; á„ᅵᆵ; ì°š; á„ᅵᆵ; ) HANGUL SYLLABLE JJILP
+CC1B;CC1B;110D 1175 11B6;CC1B;110D 1175 11B6; # (ì°›; ì°›; á„ᅵᆶ; ì°›; á„ᅵᆶ; ) HANGUL SYLLABLE JJILH
+CC1C;CC1C;110D 1175 11B7;CC1C;110D 1175 11B7; # (ì°œ; ì°œ; á„ᅵᆷ; ì°œ; á„ᅵᆷ; ) HANGUL SYLLABLE JJIM
+CC1D;CC1D;110D 1175 11B8;CC1D;110D 1175 11B8; # (ì°; ì°; á„ᅵᆸ; ì°; á„ᅵᆸ; ) HANGUL SYLLABLE JJIB
+CC1E;CC1E;110D 1175 11B9;CC1E;110D 1175 11B9; # (ì°ž; ì°ž; á„ᅵᆹ; ì°ž; á„ᅵᆹ; ) HANGUL SYLLABLE JJIBS
+CC1F;CC1F;110D 1175 11BA;CC1F;110D 1175 11BA; # (ì°Ÿ; ì°Ÿ; á„ᅵᆺ; ì°Ÿ; á„ᅵᆺ; ) HANGUL SYLLABLE JJIS
+CC20;CC20;110D 1175 11BB;CC20;110D 1175 11BB; # (ì° ; ì° ; á„ᅵᆻ; ì° ; á„ᅵᆻ; ) HANGUL SYLLABLE JJISS
+CC21;CC21;110D 1175 11BC;CC21;110D 1175 11BC; # (ì°¡; ì°¡; á„ᅵᆼ; ì°¡; á„ᅵᆼ; ) HANGUL SYLLABLE JJING
+CC22;CC22;110D 1175 11BD;CC22;110D 1175 11BD; # (ì°¢; ì°¢; á„ᅵᆽ; ì°¢; á„ᅵᆽ; ) HANGUL SYLLABLE JJIJ
+CC23;CC23;110D 1175 11BE;CC23;110D 1175 11BE; # (ì°£; ì°£; á„ᅵᆾ; ì°£; á„ᅵᆾ; ) HANGUL SYLLABLE JJIC
+CC24;CC24;110D 1175 11BF;CC24;110D 1175 11BF; # (ì°¤; ì°¤; á„ᅵᆿ; ì°¤; á„ᅵᆿ; ) HANGUL SYLLABLE JJIK
+CC25;CC25;110D 1175 11C0;CC25;110D 1175 11C0; # (ì°¥; ì°¥; á„ᅵᇀ; ì°¥; á„ᅵᇀ; ) HANGUL SYLLABLE JJIT
+CC26;CC26;110D 1175 11C1;CC26;110D 1175 11C1; # (ì°¦; ì°¦; á„á…µá‡; ì°¦; á„á…µá‡; ) HANGUL SYLLABLE JJIP
+CC27;CC27;110D 1175 11C2;CC27;110D 1175 11C2; # (ì°§; ì°§; á„ᅵᇂ; ì°§; á„ᅵᇂ; ) HANGUL SYLLABLE JJIH
+CC28;CC28;110E 1161;CC28;110E 1161; # (차; 차; 차; 차; 차; ) HANGUL SYLLABLE CA
+CC29;CC29;110E 1161 11A8;CC29;110E 1161 11A8; # (착; 착; 착; 착; 착; ) HANGUL SYLLABLE CAG
+CC2A;CC2A;110E 1161 11A9;CC2A;110E 1161 11A9; # (찪; 찪; 찪; 찪; 찪; ) HANGUL SYLLABLE CAGG
+CC2B;CC2B;110E 1161 11AA;CC2B;110E 1161 11AA; # (찫; 찫; 찫; 찫; 찫; ) HANGUL SYLLABLE CAGS
+CC2C;CC2C;110E 1161 11AB;CC2C;110E 1161 11AB; # (찬; 찬; 찬; 찬; 찬; ) HANGUL SYLLABLE CAN
+CC2D;CC2D;110E 1161 11AC;CC2D;110E 1161 11AC; # (찭; 찭; 찭; 찭; 찭; ) HANGUL SYLLABLE CANJ
+CC2E;CC2E;110E 1161 11AD;CC2E;110E 1161 11AD; # (찮; 찮; 찮; 찮; 찮; ) HANGUL SYLLABLE CANH
+CC2F;CC2F;110E 1161 11AE;CC2F;110E 1161 11AE; # (찯; 찯; 찯; 찯; 찯; ) HANGUL SYLLABLE CAD
+CC30;CC30;110E 1161 11AF;CC30;110E 1161 11AF; # (찰; 찰; 찰; 찰; 찰; ) HANGUL SYLLABLE CAL
+CC31;CC31;110E 1161 11B0;CC31;110E 1161 11B0; # (찱; 찱; 찱; 찱; 찱; ) HANGUL SYLLABLE CALG
+CC32;CC32;110E 1161 11B1;CC32;110E 1161 11B1; # (찲; 찲; 찲; 찲; 찲; ) HANGUL SYLLABLE CALM
+CC33;CC33;110E 1161 11B2;CC33;110E 1161 11B2; # (찳; 찳; 찳; 찳; 찳; ) HANGUL SYLLABLE CALB
+CC34;CC34;110E 1161 11B3;CC34;110E 1161 11B3; # (찴; 찴; 찴; 찴; 찴; ) HANGUL SYLLABLE CALS
+CC35;CC35;110E 1161 11B4;CC35;110E 1161 11B4; # (찵; 찵; 찵; 찵; 찵; ) HANGUL SYLLABLE CALT
+CC36;CC36;110E 1161 11B5;CC36;110E 1161 11B5; # (찶; 찶; 찶; 찶; 찶; ) HANGUL SYLLABLE CALP
+CC37;CC37;110E 1161 11B6;CC37;110E 1161 11B6; # (찷; 찷; 찷; 찷; 찷; ) HANGUL SYLLABLE CALH
+CC38;CC38;110E 1161 11B7;CC38;110E 1161 11B7; # (참; 참; 참; 참; 참; ) HANGUL SYLLABLE CAM
+CC39;CC39;110E 1161 11B8;CC39;110E 1161 11B8; # (찹; 찹; 찹; 찹; 찹; ) HANGUL SYLLABLE CAB
+CC3A;CC3A;110E 1161 11B9;CC3A;110E 1161 11B9; # (찺; 찺; 찺; 찺; 찺; ) HANGUL SYLLABLE CABS
+CC3B;CC3B;110E 1161 11BA;CC3B;110E 1161 11BA; # (찻; 찻; 찻; 찻; 찻; ) HANGUL SYLLABLE CAS
+CC3C;CC3C;110E 1161 11BB;CC3C;110E 1161 11BB; # (찼; 찼; 찼; 찼; 찼; ) HANGUL SYLLABLE CASS
+CC3D;CC3D;110E 1161 11BC;CC3D;110E 1161 11BC; # (창; 창; 창; 창; 창; ) HANGUL SYLLABLE CANG
+CC3E;CC3E;110E 1161 11BD;CC3E;110E 1161 11BD; # (찾; 찾; 찾; 찾; 찾; ) HANGUL SYLLABLE CAJ
+CC3F;CC3F;110E 1161 11BE;CC3F;110E 1161 11BE; # (찿; 찿; 찿; 찿; 찿; ) HANGUL SYLLABLE CAC
+CC40;CC40;110E 1161 11BF;CC40;110E 1161 11BF; # (챀; 챀; 챀; 챀; 챀; ) HANGUL SYLLABLE CAK
+CC41;CC41;110E 1161 11C0;CC41;110E 1161 11C0; # (ì±; ì±; 챁; ì±; 챁; ) HANGUL SYLLABLE CAT
+CC42;CC42;110E 1161 11C1;CC42;110E 1161 11C1; # (챂; 챂; 차á‡; 챂; 차á‡; ) HANGUL SYLLABLE CAP
+CC43;CC43;110E 1161 11C2;CC43;110E 1161 11C2; # (챃; 챃; 챃; 챃; 챃; ) HANGUL SYLLABLE CAH
+CC44;CC44;110E 1162;CC44;110E 1162; # (채; 채; 채; 채; 채; ) HANGUL SYLLABLE CAE
+CC45;CC45;110E 1162 11A8;CC45;110E 1162 11A8; # (책; 책; 책; 책; 책; ) HANGUL SYLLABLE CAEG
+CC46;CC46;110E 1162 11A9;CC46;110E 1162 11A9; # (챆; 챆; 챆; 챆; 챆; ) HANGUL SYLLABLE CAEGG
+CC47;CC47;110E 1162 11AA;CC47;110E 1162 11AA; # (챇; 챇; 챇; 챇; 챇; ) HANGUL SYLLABLE CAEGS
+CC48;CC48;110E 1162 11AB;CC48;110E 1162 11AB; # (챈; 챈; 챈; 챈; 챈; ) HANGUL SYLLABLE CAEN
+CC49;CC49;110E 1162 11AC;CC49;110E 1162 11AC; # (챉; 챉; 챉; 챉; 챉; ) HANGUL SYLLABLE CAENJ
+CC4A;CC4A;110E 1162 11AD;CC4A;110E 1162 11AD; # (챊; 챊; 챊; 챊; 챊; ) HANGUL SYLLABLE CAENH
+CC4B;CC4B;110E 1162 11AE;CC4B;110E 1162 11AE; # (챋; 챋; 챋; 챋; 챋; ) HANGUL SYLLABLE CAED
+CC4C;CC4C;110E 1162 11AF;CC4C;110E 1162 11AF; # (챌; 챌; 챌; 챌; 챌; ) HANGUL SYLLABLE CAEL
+CC4D;CC4D;110E 1162 11B0;CC4D;110E 1162 11B0; # (ì±; ì±; 챍; ì±; 챍; ) HANGUL SYLLABLE CAELG
+CC4E;CC4E;110E 1162 11B1;CC4E;110E 1162 11B1; # (챎; 챎; 챎; 챎; 챎; ) HANGUL SYLLABLE CAELM
+CC4F;CC4F;110E 1162 11B2;CC4F;110E 1162 11B2; # (ì±; ì±; 챏; ì±; 챏; ) HANGUL SYLLABLE CAELB
+CC50;CC50;110E 1162 11B3;CC50;110E 1162 11B3; # (ì±; ì±; 챐; ì±; 챐; ) HANGUL SYLLABLE CAELS
+CC51;CC51;110E 1162 11B4;CC51;110E 1162 11B4; # (챑; 챑; 챑; 챑; 챑; ) HANGUL SYLLABLE CAELT
+CC52;CC52;110E 1162 11B5;CC52;110E 1162 11B5; # (챒; 챒; 챒; 챒; 챒; ) HANGUL SYLLABLE CAELP
+CC53;CC53;110E 1162 11B6;CC53;110E 1162 11B6; # (챓; 챓; 챓; 챓; 챓; ) HANGUL SYLLABLE CAELH
+CC54;CC54;110E 1162 11B7;CC54;110E 1162 11B7; # (챔; 챔; 챔; 챔; 챔; ) HANGUL SYLLABLE CAEM
+CC55;CC55;110E 1162 11B8;CC55;110E 1162 11B8; # (챕; 챕; 챕; 챕; 챕; ) HANGUL SYLLABLE CAEB
+CC56;CC56;110E 1162 11B9;CC56;110E 1162 11B9; # (챖; 챖; 챖; 챖; 챖; ) HANGUL SYLLABLE CAEBS
+CC57;CC57;110E 1162 11BA;CC57;110E 1162 11BA; # (챗; 챗; 챗; 챗; 챗; ) HANGUL SYLLABLE CAES
+CC58;CC58;110E 1162 11BB;CC58;110E 1162 11BB; # (챘; 챘; 챘; 챘; 챘; ) HANGUL SYLLABLE CAESS
+CC59;CC59;110E 1162 11BC;CC59;110E 1162 11BC; # (챙; 챙; 챙; 챙; 챙; ) HANGUL SYLLABLE CAENG
+CC5A;CC5A;110E 1162 11BD;CC5A;110E 1162 11BD; # (챚; 챚; 챚; 챚; 챚; ) HANGUL SYLLABLE CAEJ
+CC5B;CC5B;110E 1162 11BE;CC5B;110E 1162 11BE; # (챛; 챛; 챛; 챛; 챛; ) HANGUL SYLLABLE CAEC
+CC5C;CC5C;110E 1162 11BF;CC5C;110E 1162 11BF; # (챜; 챜; 챜; 챜; 챜; ) HANGUL SYLLABLE CAEK
+CC5D;CC5D;110E 1162 11C0;CC5D;110E 1162 11C0; # (ì±; ì±; 챝; ì±; 챝; ) HANGUL SYLLABLE CAET
+CC5E;CC5E;110E 1162 11C1;CC5E;110E 1162 11C1; # (챞; 챞; 채á‡; 챞; 채á‡; ) HANGUL SYLLABLE CAEP
+CC5F;CC5F;110E 1162 11C2;CC5F;110E 1162 11C2; # (챟; 챟; 챟; 챟; 챟; ) HANGUL SYLLABLE CAEH
+CC60;CC60;110E 1163;CC60;110E 1163; # (챠; 챠; 챠; 챠; 챠; ) HANGUL SYLLABLE CYA
+CC61;CC61;110E 1163 11A8;CC61;110E 1163 11A8; # (챡; 챡; 챡; 챡; 챡; ) HANGUL SYLLABLE CYAG
+CC62;CC62;110E 1163 11A9;CC62;110E 1163 11A9; # (챢; 챢; 챢; 챢; 챢; ) HANGUL SYLLABLE CYAGG
+CC63;CC63;110E 1163 11AA;CC63;110E 1163 11AA; # (챣; 챣; 챣; 챣; 챣; ) HANGUL SYLLABLE CYAGS
+CC64;CC64;110E 1163 11AB;CC64;110E 1163 11AB; # (챤; 챤; 챤; 챤; 챤; ) HANGUL SYLLABLE CYAN
+CC65;CC65;110E 1163 11AC;CC65;110E 1163 11AC; # (챥; 챥; 챥; 챥; 챥; ) HANGUL SYLLABLE CYANJ
+CC66;CC66;110E 1163 11AD;CC66;110E 1163 11AD; # (챦; 챦; 챦; 챦; 챦; ) HANGUL SYLLABLE CYANH
+CC67;CC67;110E 1163 11AE;CC67;110E 1163 11AE; # (챧; 챧; 챧; 챧; 챧; ) HANGUL SYLLABLE CYAD
+CC68;CC68;110E 1163 11AF;CC68;110E 1163 11AF; # (챨; 챨; 챨; 챨; 챨; ) HANGUL SYLLABLE CYAL
+CC69;CC69;110E 1163 11B0;CC69;110E 1163 11B0; # (챩; 챩; 챩; 챩; 챩; ) HANGUL SYLLABLE CYALG
+CC6A;CC6A;110E 1163 11B1;CC6A;110E 1163 11B1; # (챪; 챪; 챪; 챪; 챪; ) HANGUL SYLLABLE CYALM
+CC6B;CC6B;110E 1163 11B2;CC6B;110E 1163 11B2; # (챫; 챫; 챫; 챫; 챫; ) HANGUL SYLLABLE CYALB
+CC6C;CC6C;110E 1163 11B3;CC6C;110E 1163 11B3; # (챬; 챬; 챬; 챬; 챬; ) HANGUL SYLLABLE CYALS
+CC6D;CC6D;110E 1163 11B4;CC6D;110E 1163 11B4; # (챭; 챭; 챭; 챭; 챭; ) HANGUL SYLLABLE CYALT
+CC6E;CC6E;110E 1163 11B5;CC6E;110E 1163 11B5; # (챮; 챮; 챮; 챮; 챮; ) HANGUL SYLLABLE CYALP
+CC6F;CC6F;110E 1163 11B6;CC6F;110E 1163 11B6; # (챯; 챯; 챯; 챯; 챯; ) HANGUL SYLLABLE CYALH
+CC70;CC70;110E 1163 11B7;CC70;110E 1163 11B7; # (챰; 챰; 챰; 챰; 챰; ) HANGUL SYLLABLE CYAM
+CC71;CC71;110E 1163 11B8;CC71;110E 1163 11B8; # (챱; 챱; 챱; 챱; 챱; ) HANGUL SYLLABLE CYAB
+CC72;CC72;110E 1163 11B9;CC72;110E 1163 11B9; # (챲; 챲; 챲; 챲; 챲; ) HANGUL SYLLABLE CYABS
+CC73;CC73;110E 1163 11BA;CC73;110E 1163 11BA; # (챳; 챳; 챳; 챳; 챳; ) HANGUL SYLLABLE CYAS
+CC74;CC74;110E 1163 11BB;CC74;110E 1163 11BB; # (챴; 챴; 챴; 챴; 챴; ) HANGUL SYLLABLE CYASS
+CC75;CC75;110E 1163 11BC;CC75;110E 1163 11BC; # (챵; 챵; 챵; 챵; 챵; ) HANGUL SYLLABLE CYANG
+CC76;CC76;110E 1163 11BD;CC76;110E 1163 11BD; # (챶; 챶; 챶; 챶; 챶; ) HANGUL SYLLABLE CYAJ
+CC77;CC77;110E 1163 11BE;CC77;110E 1163 11BE; # (챷; 챷; 챷; 챷; 챷; ) HANGUL SYLLABLE CYAC
+CC78;CC78;110E 1163 11BF;CC78;110E 1163 11BF; # (챸; 챸; 챸; 챸; 챸; ) HANGUL SYLLABLE CYAK
+CC79;CC79;110E 1163 11C0;CC79;110E 1163 11C0; # (챹; 챹; 챹; 챹; 챹; ) HANGUL SYLLABLE CYAT
+CC7A;CC7A;110E 1163 11C1;CC7A;110E 1163 11C1; # (챺; 챺; 챠á‡; 챺; 챠á‡; ) HANGUL SYLLABLE CYAP
+CC7B;CC7B;110E 1163 11C2;CC7B;110E 1163 11C2; # (챻; 챻; 챻; 챻; 챻; ) HANGUL SYLLABLE CYAH
+CC7C;CC7C;110E 1164;CC7C;110E 1164; # (챼; 챼; 챼; 챼; 챼; ) HANGUL SYLLABLE CYAE
+CC7D;CC7D;110E 1164 11A8;CC7D;110E 1164 11A8; # (챽; 챽; 챽; 챽; 챽; ) HANGUL SYLLABLE CYAEG
+CC7E;CC7E;110E 1164 11A9;CC7E;110E 1164 11A9; # (챾; 챾; 챾; 챾; 챾; ) HANGUL SYLLABLE CYAEGG
+CC7F;CC7F;110E 1164 11AA;CC7F;110E 1164 11AA; # (챿; 챿; 챿; 챿; 챿; ) HANGUL SYLLABLE CYAEGS
+CC80;CC80;110E 1164 11AB;CC80;110E 1164 11AB; # (첀; 첀; 첀; 첀; 첀; ) HANGUL SYLLABLE CYAEN
+CC81;CC81;110E 1164 11AC;CC81;110E 1164 11AC; # (ì²; ì²; 첁; ì²; 첁; ) HANGUL SYLLABLE CYAENJ
+CC82;CC82;110E 1164 11AD;CC82;110E 1164 11AD; # (첂; 첂; 첂; 첂; 첂; ) HANGUL SYLLABLE CYAENH
+CC83;CC83;110E 1164 11AE;CC83;110E 1164 11AE; # (첃; 첃; 첃; 첃; 첃; ) HANGUL SYLLABLE CYAED
+CC84;CC84;110E 1164 11AF;CC84;110E 1164 11AF; # (첄; 첄; 첄; 첄; 첄; ) HANGUL SYLLABLE CYAEL
+CC85;CC85;110E 1164 11B0;CC85;110E 1164 11B0; # (첅; 첅; 첅; 첅; 첅; ) HANGUL SYLLABLE CYAELG
+CC86;CC86;110E 1164 11B1;CC86;110E 1164 11B1; # (첆; 첆; 첆; 첆; 첆; ) HANGUL SYLLABLE CYAELM
+CC87;CC87;110E 1164 11B2;CC87;110E 1164 11B2; # (첇; 첇; 첇; 첇; 첇; ) HANGUL SYLLABLE CYAELB
+CC88;CC88;110E 1164 11B3;CC88;110E 1164 11B3; # (첈; 첈; 첈; 첈; 첈; ) HANGUL SYLLABLE CYAELS
+CC89;CC89;110E 1164 11B4;CC89;110E 1164 11B4; # (첉; 첉; 첉; 첉; 첉; ) HANGUL SYLLABLE CYAELT
+CC8A;CC8A;110E 1164 11B5;CC8A;110E 1164 11B5; # (첊; 첊; 첊; 첊; 첊; ) HANGUL SYLLABLE CYAELP
+CC8B;CC8B;110E 1164 11B6;CC8B;110E 1164 11B6; # (첋; 첋; 첋; 첋; 첋; ) HANGUL SYLLABLE CYAELH
+CC8C;CC8C;110E 1164 11B7;CC8C;110E 1164 11B7; # (첌; 첌; 첌; 첌; 첌; ) HANGUL SYLLABLE CYAEM
+CC8D;CC8D;110E 1164 11B8;CC8D;110E 1164 11B8; # (ì²; ì²; 첍; ì²; 첍; ) HANGUL SYLLABLE CYAEB
+CC8E;CC8E;110E 1164 11B9;CC8E;110E 1164 11B9; # (첎; 첎; 첎; 첎; 첎; ) HANGUL SYLLABLE CYAEBS
+CC8F;CC8F;110E 1164 11BA;CC8F;110E 1164 11BA; # (ì²; ì²; 첏; ì²; 첏; ) HANGUL SYLLABLE CYAES
+CC90;CC90;110E 1164 11BB;CC90;110E 1164 11BB; # (ì²; ì²; 첐; ì²; 첐; ) HANGUL SYLLABLE CYAESS
+CC91;CC91;110E 1164 11BC;CC91;110E 1164 11BC; # (첑; 첑; 첑; 첑; 첑; ) HANGUL SYLLABLE CYAENG
+CC92;CC92;110E 1164 11BD;CC92;110E 1164 11BD; # (첒; 첒; 첒; 첒; 첒; ) HANGUL SYLLABLE CYAEJ
+CC93;CC93;110E 1164 11BE;CC93;110E 1164 11BE; # (첓; 첓; 첓; 첓; 첓; ) HANGUL SYLLABLE CYAEC
+CC94;CC94;110E 1164 11BF;CC94;110E 1164 11BF; # (첔; 첔; 첔; 첔; 첔; ) HANGUL SYLLABLE CYAEK
+CC95;CC95;110E 1164 11C0;CC95;110E 1164 11C0; # (첕; 첕; 첕; 첕; 첕; ) HANGUL SYLLABLE CYAET
+CC96;CC96;110E 1164 11C1;CC96;110E 1164 11C1; # (ì²–; ì²–; 챼á‡; ì²–; 챼á‡; ) HANGUL SYLLABLE CYAEP
+CC97;CC97;110E 1164 11C2;CC97;110E 1164 11C2; # (첗; 첗; 첗; 첗; 첗; ) HANGUL SYLLABLE CYAEH
+CC98;CC98;110E 1165;CC98;110E 1165; # (처; 처; 처; 처; 처; ) HANGUL SYLLABLE CEO
+CC99;CC99;110E 1165 11A8;CC99;110E 1165 11A8; # (척; 척; 척; 척; 척; ) HANGUL SYLLABLE CEOG
+CC9A;CC9A;110E 1165 11A9;CC9A;110E 1165 11A9; # (첚; 첚; 첚; 첚; 첚; ) HANGUL SYLLABLE CEOGG
+CC9B;CC9B;110E 1165 11AA;CC9B;110E 1165 11AA; # (첛; 첛; 첛; 첛; 첛; ) HANGUL SYLLABLE CEOGS
+CC9C;CC9C;110E 1165 11AB;CC9C;110E 1165 11AB; # (천; 천; 천; 천; 천; ) HANGUL SYLLABLE CEON
+CC9D;CC9D;110E 1165 11AC;CC9D;110E 1165 11AC; # (ì²; ì²; 첝; ì²; 첝; ) HANGUL SYLLABLE CEONJ
+CC9E;CC9E;110E 1165 11AD;CC9E;110E 1165 11AD; # (첞; 첞; 첞; 첞; 첞; ) HANGUL SYLLABLE CEONH
+CC9F;CC9F;110E 1165 11AE;CC9F;110E 1165 11AE; # (첟; 첟; 첟; 첟; 첟; ) HANGUL SYLLABLE CEOD
+CCA0;CCA0;110E 1165 11AF;CCA0;110E 1165 11AF; # (철; 철; 철; 철; 철; ) HANGUL SYLLABLE CEOL
+CCA1;CCA1;110E 1165 11B0;CCA1;110E 1165 11B0; # (첡; 첡; 첡; 첡; 첡; ) HANGUL SYLLABLE CEOLG
+CCA2;CCA2;110E 1165 11B1;CCA2;110E 1165 11B1; # (첢; 첢; 첢; 첢; 첢; ) HANGUL SYLLABLE CEOLM
+CCA3;CCA3;110E 1165 11B2;CCA3;110E 1165 11B2; # (첣; 첣; 첣; 첣; 첣; ) HANGUL SYLLABLE CEOLB
+CCA4;CCA4;110E 1165 11B3;CCA4;110E 1165 11B3; # (첤; 첤; 첤; 첤; 첤; ) HANGUL SYLLABLE CEOLS
+CCA5;CCA5;110E 1165 11B4;CCA5;110E 1165 11B4; # (첥; 첥; 첥; 첥; 첥; ) HANGUL SYLLABLE CEOLT
+CCA6;CCA6;110E 1165 11B5;CCA6;110E 1165 11B5; # (첦; 첦; 첦; 첦; 첦; ) HANGUL SYLLABLE CEOLP
+CCA7;CCA7;110E 1165 11B6;CCA7;110E 1165 11B6; # (첧; 첧; 첧; 첧; 첧; ) HANGUL SYLLABLE CEOLH
+CCA8;CCA8;110E 1165 11B7;CCA8;110E 1165 11B7; # (첨; 첨; 첨; 첨; 첨; ) HANGUL SYLLABLE CEOM
+CCA9;CCA9;110E 1165 11B8;CCA9;110E 1165 11B8; # (첩; 첩; 첩; 첩; 첩; ) HANGUL SYLLABLE CEOB
+CCAA;CCAA;110E 1165 11B9;CCAA;110E 1165 11B9; # (첪; 첪; 첪; 첪; 첪; ) HANGUL SYLLABLE CEOBS
+CCAB;CCAB;110E 1165 11BA;CCAB;110E 1165 11BA; # (첫; 첫; 첫; 첫; 첫; ) HANGUL SYLLABLE CEOS
+CCAC;CCAC;110E 1165 11BB;CCAC;110E 1165 11BB; # (첬; 첬; 첬; 첬; 첬; ) HANGUL SYLLABLE CEOSS
+CCAD;CCAD;110E 1165 11BC;CCAD;110E 1165 11BC; # (청; 청; 청; 청; 청; ) HANGUL SYLLABLE CEONG
+CCAE;CCAE;110E 1165 11BD;CCAE;110E 1165 11BD; # (첮; 첮; 첮; 첮; 첮; ) HANGUL SYLLABLE CEOJ
+CCAF;CCAF;110E 1165 11BE;CCAF;110E 1165 11BE; # (첯; 첯; 첯; 첯; 첯; ) HANGUL SYLLABLE CEOC
+CCB0;CCB0;110E 1165 11BF;CCB0;110E 1165 11BF; # (첰; 첰; 첰; 첰; 첰; ) HANGUL SYLLABLE CEOK
+CCB1;CCB1;110E 1165 11C0;CCB1;110E 1165 11C0; # (첱; 첱; 첱; 첱; 첱; ) HANGUL SYLLABLE CEOT
+CCB2;CCB2;110E 1165 11C1;CCB2;110E 1165 11C1; # (ì²²; ì²²; 처á‡; ì²²; 처á‡; ) HANGUL SYLLABLE CEOP
+CCB3;CCB3;110E 1165 11C2;CCB3;110E 1165 11C2; # (첳; 첳; 첳; 첳; 첳; ) HANGUL SYLLABLE CEOH
+CCB4;CCB4;110E 1166;CCB4;110E 1166; # (체; 체; 체; 체; 체; ) HANGUL SYLLABLE CE
+CCB5;CCB5;110E 1166 11A8;CCB5;110E 1166 11A8; # (첵; 첵; 첵; 첵; 첵; ) HANGUL SYLLABLE CEG
+CCB6;CCB6;110E 1166 11A9;CCB6;110E 1166 11A9; # (첶; 첶; 첶; 첶; 첶; ) HANGUL SYLLABLE CEGG
+CCB7;CCB7;110E 1166 11AA;CCB7;110E 1166 11AA; # (첷; 첷; 첷; 첷; 첷; ) HANGUL SYLLABLE CEGS
+CCB8;CCB8;110E 1166 11AB;CCB8;110E 1166 11AB; # (첸; 첸; 첸; 첸; 첸; ) HANGUL SYLLABLE CEN
+CCB9;CCB9;110E 1166 11AC;CCB9;110E 1166 11AC; # (첹; 첹; 첹; 첹; 첹; ) HANGUL SYLLABLE CENJ
+CCBA;CCBA;110E 1166 11AD;CCBA;110E 1166 11AD; # (첺; 첺; 첺; 첺; 첺; ) HANGUL SYLLABLE CENH
+CCBB;CCBB;110E 1166 11AE;CCBB;110E 1166 11AE; # (첻; 첻; 첻; 첻; 첻; ) HANGUL SYLLABLE CED
+CCBC;CCBC;110E 1166 11AF;CCBC;110E 1166 11AF; # (첼; 첼; 첼; 첼; 첼; ) HANGUL SYLLABLE CEL
+CCBD;CCBD;110E 1166 11B0;CCBD;110E 1166 11B0; # (첽; 첽; 첽; 첽; 첽; ) HANGUL SYLLABLE CELG
+CCBE;CCBE;110E 1166 11B1;CCBE;110E 1166 11B1; # (첾; 첾; 첾; 첾; 첾; ) HANGUL SYLLABLE CELM
+CCBF;CCBF;110E 1166 11B2;CCBF;110E 1166 11B2; # (첿; 첿; 첿; 첿; 첿; ) HANGUL SYLLABLE CELB
+CCC0;CCC0;110E 1166 11B3;CCC0;110E 1166 11B3; # (쳀; 쳀; 쳀; 쳀; 쳀; ) HANGUL SYLLABLE CELS
+CCC1;CCC1;110E 1166 11B4;CCC1;110E 1166 11B4; # (ì³; ì³; 쳁; ì³; 쳁; ) HANGUL SYLLABLE CELT
+CCC2;CCC2;110E 1166 11B5;CCC2;110E 1166 11B5; # (쳂; 쳂; 쳂; 쳂; 쳂; ) HANGUL SYLLABLE CELP
+CCC3;CCC3;110E 1166 11B6;CCC3;110E 1166 11B6; # (쳃; 쳃; 쳃; 쳃; 쳃; ) HANGUL SYLLABLE CELH
+CCC4;CCC4;110E 1166 11B7;CCC4;110E 1166 11B7; # (쳄; 쳄; 쳄; 쳄; 쳄; ) HANGUL SYLLABLE CEM
+CCC5;CCC5;110E 1166 11B8;CCC5;110E 1166 11B8; # (쳅; 쳅; 쳅; 쳅; 쳅; ) HANGUL SYLLABLE CEB
+CCC6;CCC6;110E 1166 11B9;CCC6;110E 1166 11B9; # (쳆; 쳆; 쳆; 쳆; 쳆; ) HANGUL SYLLABLE CEBS
+CCC7;CCC7;110E 1166 11BA;CCC7;110E 1166 11BA; # (쳇; 쳇; 쳇; 쳇; 쳇; ) HANGUL SYLLABLE CES
+CCC8;CCC8;110E 1166 11BB;CCC8;110E 1166 11BB; # (쳈; 쳈; 쳈; 쳈; 쳈; ) HANGUL SYLLABLE CESS
+CCC9;CCC9;110E 1166 11BC;CCC9;110E 1166 11BC; # (쳉; 쳉; 쳉; 쳉; 쳉; ) HANGUL SYLLABLE CENG
+CCCA;CCCA;110E 1166 11BD;CCCA;110E 1166 11BD; # (쳊; 쳊; 쳊; 쳊; 쳊; ) HANGUL SYLLABLE CEJ
+CCCB;CCCB;110E 1166 11BE;CCCB;110E 1166 11BE; # (쳋; 쳋; 쳋; 쳋; 쳋; ) HANGUL SYLLABLE CEC
+CCCC;CCCC;110E 1166 11BF;CCCC;110E 1166 11BF; # (쳌; 쳌; 쳌; 쳌; 쳌; ) HANGUL SYLLABLE CEK
+CCCD;CCCD;110E 1166 11C0;CCCD;110E 1166 11C0; # (ì³; ì³; 쳍; ì³; 쳍; ) HANGUL SYLLABLE CET
+CCCE;CCCE;110E 1166 11C1;CCCE;110E 1166 11C1; # (쳎; 쳎; 체á‡; 쳎; 체á‡; ) HANGUL SYLLABLE CEP
+CCCF;CCCF;110E 1166 11C2;CCCF;110E 1166 11C2; # (ì³; ì³; 쳏; ì³; 쳏; ) HANGUL SYLLABLE CEH
+CCD0;CCD0;110E 1167;CCD0;110E 1167; # (ì³; ì³; 쳐; ì³; 쳐; ) HANGUL SYLLABLE CYEO
+CCD1;CCD1;110E 1167 11A8;CCD1;110E 1167 11A8; # (쳑; 쳑; 쳑; 쳑; 쳑; ) HANGUL SYLLABLE CYEOG
+CCD2;CCD2;110E 1167 11A9;CCD2;110E 1167 11A9; # (쳒; 쳒; 쳒; 쳒; 쳒; ) HANGUL SYLLABLE CYEOGG
+CCD3;CCD3;110E 1167 11AA;CCD3;110E 1167 11AA; # (쳓; 쳓; 쳓; 쳓; 쳓; ) HANGUL SYLLABLE CYEOGS
+CCD4;CCD4;110E 1167 11AB;CCD4;110E 1167 11AB; # (쳔; 쳔; 쳔; 쳔; 쳔; ) HANGUL SYLLABLE CYEON
+CCD5;CCD5;110E 1167 11AC;CCD5;110E 1167 11AC; # (쳕; 쳕; 쳕; 쳕; 쳕; ) HANGUL SYLLABLE CYEONJ
+CCD6;CCD6;110E 1167 11AD;CCD6;110E 1167 11AD; # (쳖; 쳖; 쳖; 쳖; 쳖; ) HANGUL SYLLABLE CYEONH
+CCD7;CCD7;110E 1167 11AE;CCD7;110E 1167 11AE; # (쳗; 쳗; 쳗; 쳗; 쳗; ) HANGUL SYLLABLE CYEOD
+CCD8;CCD8;110E 1167 11AF;CCD8;110E 1167 11AF; # (쳘; 쳘; 쳘; 쳘; 쳘; ) HANGUL SYLLABLE CYEOL
+CCD9;CCD9;110E 1167 11B0;CCD9;110E 1167 11B0; # (쳙; 쳙; 쳙; 쳙; 쳙; ) HANGUL SYLLABLE CYEOLG
+CCDA;CCDA;110E 1167 11B1;CCDA;110E 1167 11B1; # (쳚; 쳚; 쳚; 쳚; 쳚; ) HANGUL SYLLABLE CYEOLM
+CCDB;CCDB;110E 1167 11B2;CCDB;110E 1167 11B2; # (쳛; 쳛; 쳛; 쳛; 쳛; ) HANGUL SYLLABLE CYEOLB
+CCDC;CCDC;110E 1167 11B3;CCDC;110E 1167 11B3; # (쳜; 쳜; 쳜; 쳜; 쳜; ) HANGUL SYLLABLE CYEOLS
+CCDD;CCDD;110E 1167 11B4;CCDD;110E 1167 11B4; # (ì³; ì³; 쳝; ì³; 쳝; ) HANGUL SYLLABLE CYEOLT
+CCDE;CCDE;110E 1167 11B5;CCDE;110E 1167 11B5; # (쳞; 쳞; 쳞; 쳞; 쳞; ) HANGUL SYLLABLE CYEOLP
+CCDF;CCDF;110E 1167 11B6;CCDF;110E 1167 11B6; # (쳟; 쳟; 쳟; 쳟; 쳟; ) HANGUL SYLLABLE CYEOLH
+CCE0;CCE0;110E 1167 11B7;CCE0;110E 1167 11B7; # (쳠; 쳠; 쳠; 쳠; 쳠; ) HANGUL SYLLABLE CYEOM
+CCE1;CCE1;110E 1167 11B8;CCE1;110E 1167 11B8; # (쳡; 쳡; 쳡; 쳡; 쳡; ) HANGUL SYLLABLE CYEOB
+CCE2;CCE2;110E 1167 11B9;CCE2;110E 1167 11B9; # (쳢; 쳢; 쳢; 쳢; 쳢; ) HANGUL SYLLABLE CYEOBS
+CCE3;CCE3;110E 1167 11BA;CCE3;110E 1167 11BA; # (쳣; 쳣; 쳣; 쳣; 쳣; ) HANGUL SYLLABLE CYEOS
+CCE4;CCE4;110E 1167 11BB;CCE4;110E 1167 11BB; # (쳤; 쳤; 쳤; 쳤; 쳤; ) HANGUL SYLLABLE CYEOSS
+CCE5;CCE5;110E 1167 11BC;CCE5;110E 1167 11BC; # (쳥; 쳥; 쳥; 쳥; 쳥; ) HANGUL SYLLABLE CYEONG
+CCE6;CCE6;110E 1167 11BD;CCE6;110E 1167 11BD; # (쳦; 쳦; 쳦; 쳦; 쳦; ) HANGUL SYLLABLE CYEOJ
+CCE7;CCE7;110E 1167 11BE;CCE7;110E 1167 11BE; # (쳧; 쳧; 쳧; 쳧; 쳧; ) HANGUL SYLLABLE CYEOC
+CCE8;CCE8;110E 1167 11BF;CCE8;110E 1167 11BF; # (쳨; 쳨; 쳨; 쳨; 쳨; ) HANGUL SYLLABLE CYEOK
+CCE9;CCE9;110E 1167 11C0;CCE9;110E 1167 11C0; # (쳩; 쳩; 쳩; 쳩; 쳩; ) HANGUL SYLLABLE CYEOT
+CCEA;CCEA;110E 1167 11C1;CCEA;110E 1167 11C1; # (쳪; 쳪; 쳐á‡; 쳪; 쳐á‡; ) HANGUL SYLLABLE CYEOP
+CCEB;CCEB;110E 1167 11C2;CCEB;110E 1167 11C2; # (쳫; 쳫; 쳫; 쳫; 쳫; ) HANGUL SYLLABLE CYEOH
+CCEC;CCEC;110E 1168;CCEC;110E 1168; # (쳬; 쳬; 쳬; 쳬; 쳬; ) HANGUL SYLLABLE CYE
+CCED;CCED;110E 1168 11A8;CCED;110E 1168 11A8; # (쳭; 쳭; 쳭; 쳭; 쳭; ) HANGUL SYLLABLE CYEG
+CCEE;CCEE;110E 1168 11A9;CCEE;110E 1168 11A9; # (쳮; 쳮; 쳮; 쳮; 쳮; ) HANGUL SYLLABLE CYEGG
+CCEF;CCEF;110E 1168 11AA;CCEF;110E 1168 11AA; # (쳯; 쳯; 쳯; 쳯; 쳯; ) HANGUL SYLLABLE CYEGS
+CCF0;CCF0;110E 1168 11AB;CCF0;110E 1168 11AB; # (쳰; 쳰; 쳰; 쳰; 쳰; ) HANGUL SYLLABLE CYEN
+CCF1;CCF1;110E 1168 11AC;CCF1;110E 1168 11AC; # (쳱; 쳱; 쳱; 쳱; 쳱; ) HANGUL SYLLABLE CYENJ
+CCF2;CCF2;110E 1168 11AD;CCF2;110E 1168 11AD; # (쳲; 쳲; 쳲; 쳲; 쳲; ) HANGUL SYLLABLE CYENH
+CCF3;CCF3;110E 1168 11AE;CCF3;110E 1168 11AE; # (쳳; 쳳; 쳳; 쳳; 쳳; ) HANGUL SYLLABLE CYED
+CCF4;CCF4;110E 1168 11AF;CCF4;110E 1168 11AF; # (쳴; 쳴; 쳴; 쳴; 쳴; ) HANGUL SYLLABLE CYEL
+CCF5;CCF5;110E 1168 11B0;CCF5;110E 1168 11B0; # (쳵; 쳵; 쳵; 쳵; 쳵; ) HANGUL SYLLABLE CYELG
+CCF6;CCF6;110E 1168 11B1;CCF6;110E 1168 11B1; # (쳶; 쳶; 쳶; 쳶; 쳶; ) HANGUL SYLLABLE CYELM
+CCF7;CCF7;110E 1168 11B2;CCF7;110E 1168 11B2; # (쳷; 쳷; 쳷; 쳷; 쳷; ) HANGUL SYLLABLE CYELB
+CCF8;CCF8;110E 1168 11B3;CCF8;110E 1168 11B3; # (쳸; 쳸; 쳸; 쳸; 쳸; ) HANGUL SYLLABLE CYELS
+CCF9;CCF9;110E 1168 11B4;CCF9;110E 1168 11B4; # (쳹; 쳹; 쳹; 쳹; 쳹; ) HANGUL SYLLABLE CYELT
+CCFA;CCFA;110E 1168 11B5;CCFA;110E 1168 11B5; # (쳺; 쳺; 쳺; 쳺; 쳺; ) HANGUL SYLLABLE CYELP
+CCFB;CCFB;110E 1168 11B6;CCFB;110E 1168 11B6; # (쳻; 쳻; 쳻; 쳻; 쳻; ) HANGUL SYLLABLE CYELH
+CCFC;CCFC;110E 1168 11B7;CCFC;110E 1168 11B7; # (쳼; 쳼; 쳼; 쳼; 쳼; ) HANGUL SYLLABLE CYEM
+CCFD;CCFD;110E 1168 11B8;CCFD;110E 1168 11B8; # (쳽; 쳽; 쳽; 쳽; 쳽; ) HANGUL SYLLABLE CYEB
+CCFE;CCFE;110E 1168 11B9;CCFE;110E 1168 11B9; # (쳾; 쳾; 쳾; 쳾; 쳾; ) HANGUL SYLLABLE CYEBS
+CCFF;CCFF;110E 1168 11BA;CCFF;110E 1168 11BA; # (쳿; 쳿; 쳿; 쳿; 쳿; ) HANGUL SYLLABLE CYES
+CD00;CD00;110E 1168 11BB;CD00;110E 1168 11BB; # (촀; 촀; 촀; 촀; 촀; ) HANGUL SYLLABLE CYESS
+CD01;CD01;110E 1168 11BC;CD01;110E 1168 11BC; # (ì´; ì´; 촁; ì´; 촁; ) HANGUL SYLLABLE CYENG
+CD02;CD02;110E 1168 11BD;CD02;110E 1168 11BD; # (촂; 촂; 촂; 촂; 촂; ) HANGUL SYLLABLE CYEJ
+CD03;CD03;110E 1168 11BE;CD03;110E 1168 11BE; # (촃; 촃; 촃; 촃; 촃; ) HANGUL SYLLABLE CYEC
+CD04;CD04;110E 1168 11BF;CD04;110E 1168 11BF; # (촄; 촄; 촄; 촄; 촄; ) HANGUL SYLLABLE CYEK
+CD05;CD05;110E 1168 11C0;CD05;110E 1168 11C0; # (촅; 촅; 촅; 촅; 촅; ) HANGUL SYLLABLE CYET
+CD06;CD06;110E 1168 11C1;CD06;110E 1168 11C1; # (ì´†; ì´†; 쳬á‡; ì´†; 쳬á‡; ) HANGUL SYLLABLE CYEP
+CD07;CD07;110E 1168 11C2;CD07;110E 1168 11C2; # (촇; 촇; 촇; 촇; 촇; ) HANGUL SYLLABLE CYEH
+CD08;CD08;110E 1169;CD08;110E 1169; # (초; 초; 초; 초; 초; ) HANGUL SYLLABLE CO
+CD09;CD09;110E 1169 11A8;CD09;110E 1169 11A8; # (촉; 촉; 촉; 촉; 촉; ) HANGUL SYLLABLE COG
+CD0A;CD0A;110E 1169 11A9;CD0A;110E 1169 11A9; # (촊; 촊; 촊; 촊; 촊; ) HANGUL SYLLABLE COGG
+CD0B;CD0B;110E 1169 11AA;CD0B;110E 1169 11AA; # (촋; 촋; 촋; 촋; 촋; ) HANGUL SYLLABLE COGS
+CD0C;CD0C;110E 1169 11AB;CD0C;110E 1169 11AB; # (촌; 촌; 촌; 촌; 촌; ) HANGUL SYLLABLE CON
+CD0D;CD0D;110E 1169 11AC;CD0D;110E 1169 11AC; # (ì´; ì´; 촍; ì´; 촍; ) HANGUL SYLLABLE CONJ
+CD0E;CD0E;110E 1169 11AD;CD0E;110E 1169 11AD; # (촎; 촎; 촎; 촎; 촎; ) HANGUL SYLLABLE CONH
+CD0F;CD0F;110E 1169 11AE;CD0F;110E 1169 11AE; # (ì´; ì´; 촏; ì´; 촏; ) HANGUL SYLLABLE COD
+CD10;CD10;110E 1169 11AF;CD10;110E 1169 11AF; # (ì´; ì´; 촐; ì´; 촐; ) HANGUL SYLLABLE COL
+CD11;CD11;110E 1169 11B0;CD11;110E 1169 11B0; # (촑; 촑; 촑; 촑; 촑; ) HANGUL SYLLABLE COLG
+CD12;CD12;110E 1169 11B1;CD12;110E 1169 11B1; # (촒; 촒; 촒; 촒; 촒; ) HANGUL SYLLABLE COLM
+CD13;CD13;110E 1169 11B2;CD13;110E 1169 11B2; # (촓; 촓; 촓; 촓; 촓; ) HANGUL SYLLABLE COLB
+CD14;CD14;110E 1169 11B3;CD14;110E 1169 11B3; # (촔; 촔; 촔; 촔; 촔; ) HANGUL SYLLABLE COLS
+CD15;CD15;110E 1169 11B4;CD15;110E 1169 11B4; # (촕; 촕; 촕; 촕; 촕; ) HANGUL SYLLABLE COLT
+CD16;CD16;110E 1169 11B5;CD16;110E 1169 11B5; # (촖; 촖; 촖; 촖; 촖; ) HANGUL SYLLABLE COLP
+CD17;CD17;110E 1169 11B6;CD17;110E 1169 11B6; # (촗; 촗; 촗; 촗; 촗; ) HANGUL SYLLABLE COLH
+CD18;CD18;110E 1169 11B7;CD18;110E 1169 11B7; # (촘; 촘; 촘; 촘; 촘; ) HANGUL SYLLABLE COM
+CD19;CD19;110E 1169 11B8;CD19;110E 1169 11B8; # (촙; 촙; 촙; 촙; 촙; ) HANGUL SYLLABLE COB
+CD1A;CD1A;110E 1169 11B9;CD1A;110E 1169 11B9; # (촚; 촚; 촚; 촚; 촚; ) HANGUL SYLLABLE COBS
+CD1B;CD1B;110E 1169 11BA;CD1B;110E 1169 11BA; # (촛; 촛; 촛; 촛; 촛; ) HANGUL SYLLABLE COS
+CD1C;CD1C;110E 1169 11BB;CD1C;110E 1169 11BB; # (촜; 촜; 촜; 촜; 촜; ) HANGUL SYLLABLE COSS
+CD1D;CD1D;110E 1169 11BC;CD1D;110E 1169 11BC; # (ì´; ì´; 총; ì´; 총; ) HANGUL SYLLABLE CONG
+CD1E;CD1E;110E 1169 11BD;CD1E;110E 1169 11BD; # (촞; 촞; 촞; 촞; 촞; ) HANGUL SYLLABLE COJ
+CD1F;CD1F;110E 1169 11BE;CD1F;110E 1169 11BE; # (촟; 촟; 촟; 촟; 촟; ) HANGUL SYLLABLE COC
+CD20;CD20;110E 1169 11BF;CD20;110E 1169 11BF; # (촠; 촠; 촠; 촠; 촠; ) HANGUL SYLLABLE COK
+CD21;CD21;110E 1169 11C0;CD21;110E 1169 11C0; # (촡; 촡; 촡; 촡; 촡; ) HANGUL SYLLABLE COT
+CD22;CD22;110E 1169 11C1;CD22;110E 1169 11C1; # (ì´¢; ì´¢; 초á‡; ì´¢; 초á‡; ) HANGUL SYLLABLE COP
+CD23;CD23;110E 1169 11C2;CD23;110E 1169 11C2; # (촣; 촣; 촣; 촣; 촣; ) HANGUL SYLLABLE COH
+CD24;CD24;110E 116A;CD24;110E 116A; # (촤; 촤; 촤; 촤; 촤; ) HANGUL SYLLABLE CWA
+CD25;CD25;110E 116A 11A8;CD25;110E 116A 11A8; # (촥; 촥; 촥; 촥; 촥; ) HANGUL SYLLABLE CWAG
+CD26;CD26;110E 116A 11A9;CD26;110E 116A 11A9; # (촦; 촦; 촦; 촦; 촦; ) HANGUL SYLLABLE CWAGG
+CD27;CD27;110E 116A 11AA;CD27;110E 116A 11AA; # (촧; 촧; 촧; 촧; 촧; ) HANGUL SYLLABLE CWAGS
+CD28;CD28;110E 116A 11AB;CD28;110E 116A 11AB; # (촨; 촨; 촨; 촨; 촨; ) HANGUL SYLLABLE CWAN
+CD29;CD29;110E 116A 11AC;CD29;110E 116A 11AC; # (촩; 촩; 촩; 촩; 촩; ) HANGUL SYLLABLE CWANJ
+CD2A;CD2A;110E 116A 11AD;CD2A;110E 116A 11AD; # (촪; 촪; 촪; 촪; 촪; ) HANGUL SYLLABLE CWANH
+CD2B;CD2B;110E 116A 11AE;CD2B;110E 116A 11AE; # (촫; 촫; 촫; 촫; 촫; ) HANGUL SYLLABLE CWAD
+CD2C;CD2C;110E 116A 11AF;CD2C;110E 116A 11AF; # (촬; 촬; 촬; 촬; 촬; ) HANGUL SYLLABLE CWAL
+CD2D;CD2D;110E 116A 11B0;CD2D;110E 116A 11B0; # (촭; 촭; 촭; 촭; 촭; ) HANGUL SYLLABLE CWALG
+CD2E;CD2E;110E 116A 11B1;CD2E;110E 116A 11B1; # (촮; 촮; 촮; 촮; 촮; ) HANGUL SYLLABLE CWALM
+CD2F;CD2F;110E 116A 11B2;CD2F;110E 116A 11B2; # (촯; 촯; 촯; 촯; 촯; ) HANGUL SYLLABLE CWALB
+CD30;CD30;110E 116A 11B3;CD30;110E 116A 11B3; # (촰; 촰; 촰; 촰; 촰; ) HANGUL SYLLABLE CWALS
+CD31;CD31;110E 116A 11B4;CD31;110E 116A 11B4; # (촱; 촱; 촱; 촱; 촱; ) HANGUL SYLLABLE CWALT
+CD32;CD32;110E 116A 11B5;CD32;110E 116A 11B5; # (촲; 촲; 촲; 촲; 촲; ) HANGUL SYLLABLE CWALP
+CD33;CD33;110E 116A 11B6;CD33;110E 116A 11B6; # (촳; 촳; 촳; 촳; 촳; ) HANGUL SYLLABLE CWALH
+CD34;CD34;110E 116A 11B7;CD34;110E 116A 11B7; # (촴; 촴; 촴; 촴; 촴; ) HANGUL SYLLABLE CWAM
+CD35;CD35;110E 116A 11B8;CD35;110E 116A 11B8; # (촵; 촵; 촵; 촵; 촵; ) HANGUL SYLLABLE CWAB
+CD36;CD36;110E 116A 11B9;CD36;110E 116A 11B9; # (촶; 촶; 촶; 촶; 촶; ) HANGUL SYLLABLE CWABS
+CD37;CD37;110E 116A 11BA;CD37;110E 116A 11BA; # (촷; 촷; 촷; 촷; 촷; ) HANGUL SYLLABLE CWAS
+CD38;CD38;110E 116A 11BB;CD38;110E 116A 11BB; # (촸; 촸; 촸; 촸; 촸; ) HANGUL SYLLABLE CWASS
+CD39;CD39;110E 116A 11BC;CD39;110E 116A 11BC; # (촹; 촹; 촹; 촹; 촹; ) HANGUL SYLLABLE CWANG
+CD3A;CD3A;110E 116A 11BD;CD3A;110E 116A 11BD; # (촺; 촺; 촺; 촺; 촺; ) HANGUL SYLLABLE CWAJ
+CD3B;CD3B;110E 116A 11BE;CD3B;110E 116A 11BE; # (촻; 촻; 촻; 촻; 촻; ) HANGUL SYLLABLE CWAC
+CD3C;CD3C;110E 116A 11BF;CD3C;110E 116A 11BF; # (촼; 촼; 촼; 촼; 촼; ) HANGUL SYLLABLE CWAK
+CD3D;CD3D;110E 116A 11C0;CD3D;110E 116A 11C0; # (촽; 촽; 촽; 촽; 촽; ) HANGUL SYLLABLE CWAT
+CD3E;CD3E;110E 116A 11C1;CD3E;110E 116A 11C1; # (ì´¾; ì´¾; 촤á‡; ì´¾; 촤á‡; ) HANGUL SYLLABLE CWAP
+CD3F;CD3F;110E 116A 11C2;CD3F;110E 116A 11C2; # (촿; 촿; 촿; 촿; 촿; ) HANGUL SYLLABLE CWAH
+CD40;CD40;110E 116B;CD40;110E 116B; # (쵀; 쵀; 쵀; 쵀; 쵀; ) HANGUL SYLLABLE CWAE
+CD41;CD41;110E 116B 11A8;CD41;110E 116B 11A8; # (ìµ; ìµ; 쵁; ìµ; 쵁; ) HANGUL SYLLABLE CWAEG
+CD42;CD42;110E 116B 11A9;CD42;110E 116B 11A9; # (쵂; 쵂; 쵂; 쵂; 쵂; ) HANGUL SYLLABLE CWAEGG
+CD43;CD43;110E 116B 11AA;CD43;110E 116B 11AA; # (쵃; 쵃; 쵃; 쵃; 쵃; ) HANGUL SYLLABLE CWAEGS
+CD44;CD44;110E 116B 11AB;CD44;110E 116B 11AB; # (쵄; 쵄; 쵄; 쵄; 쵄; ) HANGUL SYLLABLE CWAEN
+CD45;CD45;110E 116B 11AC;CD45;110E 116B 11AC; # (쵅; 쵅; 쵅; 쵅; 쵅; ) HANGUL SYLLABLE CWAENJ
+CD46;CD46;110E 116B 11AD;CD46;110E 116B 11AD; # (쵆; 쵆; 쵆; 쵆; 쵆; ) HANGUL SYLLABLE CWAENH
+CD47;CD47;110E 116B 11AE;CD47;110E 116B 11AE; # (쵇; 쵇; 쵇; 쵇; 쵇; ) HANGUL SYLLABLE CWAED
+CD48;CD48;110E 116B 11AF;CD48;110E 116B 11AF; # (쵈; 쵈; 쵈; 쵈; 쵈; ) HANGUL SYLLABLE CWAEL
+CD49;CD49;110E 116B 11B0;CD49;110E 116B 11B0; # (쵉; 쵉; 쵉; 쵉; 쵉; ) HANGUL SYLLABLE CWAELG
+CD4A;CD4A;110E 116B 11B1;CD4A;110E 116B 11B1; # (쵊; 쵊; 쵊; 쵊; 쵊; ) HANGUL SYLLABLE CWAELM
+CD4B;CD4B;110E 116B 11B2;CD4B;110E 116B 11B2; # (쵋; 쵋; 쵋; 쵋; 쵋; ) HANGUL SYLLABLE CWAELB
+CD4C;CD4C;110E 116B 11B3;CD4C;110E 116B 11B3; # (쵌; 쵌; 쵌; 쵌; 쵌; ) HANGUL SYLLABLE CWAELS
+CD4D;CD4D;110E 116B 11B4;CD4D;110E 116B 11B4; # (ìµ; ìµ; 쵍; ìµ; 쵍; ) HANGUL SYLLABLE CWAELT
+CD4E;CD4E;110E 116B 11B5;CD4E;110E 116B 11B5; # (쵎; 쵎; 쵎; 쵎; 쵎; ) HANGUL SYLLABLE CWAELP
+CD4F;CD4F;110E 116B 11B6;CD4F;110E 116B 11B6; # (ìµ; ìµ; 쵏; ìµ; 쵏; ) HANGUL SYLLABLE CWAELH
+CD50;CD50;110E 116B 11B7;CD50;110E 116B 11B7; # (ìµ; ìµ; 쵐; ìµ; 쵐; ) HANGUL SYLLABLE CWAEM
+CD51;CD51;110E 116B 11B8;CD51;110E 116B 11B8; # (쵑; 쵑; 쵑; 쵑; 쵑; ) HANGUL SYLLABLE CWAEB
+CD52;CD52;110E 116B 11B9;CD52;110E 116B 11B9; # (쵒; 쵒; 쵒; 쵒; 쵒; ) HANGUL SYLLABLE CWAEBS
+CD53;CD53;110E 116B 11BA;CD53;110E 116B 11BA; # (쵓; 쵓; 쵓; 쵓; 쵓; ) HANGUL SYLLABLE CWAES
+CD54;CD54;110E 116B 11BB;CD54;110E 116B 11BB; # (쵔; 쵔; 쵔; 쵔; 쵔; ) HANGUL SYLLABLE CWAESS
+CD55;CD55;110E 116B 11BC;CD55;110E 116B 11BC; # (쵕; 쵕; 쵕; 쵕; 쵕; ) HANGUL SYLLABLE CWAENG
+CD56;CD56;110E 116B 11BD;CD56;110E 116B 11BD; # (쵖; 쵖; 쵖; 쵖; 쵖; ) HANGUL SYLLABLE CWAEJ
+CD57;CD57;110E 116B 11BE;CD57;110E 116B 11BE; # (쵗; 쵗; 쵗; 쵗; 쵗; ) HANGUL SYLLABLE CWAEC
+CD58;CD58;110E 116B 11BF;CD58;110E 116B 11BF; # (쵘; 쵘; 쵘; 쵘; 쵘; ) HANGUL SYLLABLE CWAEK
+CD59;CD59;110E 116B 11C0;CD59;110E 116B 11C0; # (쵙; 쵙; 쵙; 쵙; 쵙; ) HANGUL SYLLABLE CWAET
+CD5A;CD5A;110E 116B 11C1;CD5A;110E 116B 11C1; # (쵚; 쵚; 쵀á‡; 쵚; 쵀á‡; ) HANGUL SYLLABLE CWAEP
+CD5B;CD5B;110E 116B 11C2;CD5B;110E 116B 11C2; # (쵛; 쵛; 쵛; 쵛; 쵛; ) HANGUL SYLLABLE CWAEH
+CD5C;CD5C;110E 116C;CD5C;110E 116C; # (최; 최; 최; 최; 최; ) HANGUL SYLLABLE COE
+CD5D;CD5D;110E 116C 11A8;CD5D;110E 116C 11A8; # (ìµ; ìµ; 쵝; ìµ; 쵝; ) HANGUL SYLLABLE COEG
+CD5E;CD5E;110E 116C 11A9;CD5E;110E 116C 11A9; # (쵞; 쵞; 쵞; 쵞; 쵞; ) HANGUL SYLLABLE COEGG
+CD5F;CD5F;110E 116C 11AA;CD5F;110E 116C 11AA; # (쵟; 쵟; 쵟; 쵟; 쵟; ) HANGUL SYLLABLE COEGS
+CD60;CD60;110E 116C 11AB;CD60;110E 116C 11AB; # (쵠; 쵠; 쵠; 쵠; 쵠; ) HANGUL SYLLABLE COEN
+CD61;CD61;110E 116C 11AC;CD61;110E 116C 11AC; # (쵡; 쵡; 쵡; 쵡; 쵡; ) HANGUL SYLLABLE COENJ
+CD62;CD62;110E 116C 11AD;CD62;110E 116C 11AD; # (쵢; 쵢; 쵢; 쵢; 쵢; ) HANGUL SYLLABLE COENH
+CD63;CD63;110E 116C 11AE;CD63;110E 116C 11AE; # (쵣; 쵣; 쵣; 쵣; 쵣; ) HANGUL SYLLABLE COED
+CD64;CD64;110E 116C 11AF;CD64;110E 116C 11AF; # (쵤; 쵤; 쵤; 쵤; 쵤; ) HANGUL SYLLABLE COEL
+CD65;CD65;110E 116C 11B0;CD65;110E 116C 11B0; # (쵥; 쵥; 쵥; 쵥; 쵥; ) HANGUL SYLLABLE COELG
+CD66;CD66;110E 116C 11B1;CD66;110E 116C 11B1; # (쵦; 쵦; 쵦; 쵦; 쵦; ) HANGUL SYLLABLE COELM
+CD67;CD67;110E 116C 11B2;CD67;110E 116C 11B2; # (쵧; 쵧; 쵧; 쵧; 쵧; ) HANGUL SYLLABLE COELB
+CD68;CD68;110E 116C 11B3;CD68;110E 116C 11B3; # (쵨; 쵨; 쵨; 쵨; 쵨; ) HANGUL SYLLABLE COELS
+CD69;CD69;110E 116C 11B4;CD69;110E 116C 11B4; # (쵩; 쵩; 쵩; 쵩; 쵩; ) HANGUL SYLLABLE COELT
+CD6A;CD6A;110E 116C 11B5;CD6A;110E 116C 11B5; # (쵪; 쵪; 쵪; 쵪; 쵪; ) HANGUL SYLLABLE COELP
+CD6B;CD6B;110E 116C 11B6;CD6B;110E 116C 11B6; # (쵫; 쵫; 쵫; 쵫; 쵫; ) HANGUL SYLLABLE COELH
+CD6C;CD6C;110E 116C 11B7;CD6C;110E 116C 11B7; # (쵬; 쵬; 쵬; 쵬; 쵬; ) HANGUL SYLLABLE COEM
+CD6D;CD6D;110E 116C 11B8;CD6D;110E 116C 11B8; # (쵭; 쵭; 쵭; 쵭; 쵭; ) HANGUL SYLLABLE COEB
+CD6E;CD6E;110E 116C 11B9;CD6E;110E 116C 11B9; # (쵮; 쵮; 쵮; 쵮; 쵮; ) HANGUL SYLLABLE COEBS
+CD6F;CD6F;110E 116C 11BA;CD6F;110E 116C 11BA; # (쵯; 쵯; 쵯; 쵯; 쵯; ) HANGUL SYLLABLE COES
+CD70;CD70;110E 116C 11BB;CD70;110E 116C 11BB; # (쵰; 쵰; 쵰; 쵰; 쵰; ) HANGUL SYLLABLE COESS
+CD71;CD71;110E 116C 11BC;CD71;110E 116C 11BC; # (쵱; 쵱; 쵱; 쵱; 쵱; ) HANGUL SYLLABLE COENG
+CD72;CD72;110E 116C 11BD;CD72;110E 116C 11BD; # (쵲; 쵲; 쵲; 쵲; 쵲; ) HANGUL SYLLABLE COEJ
+CD73;CD73;110E 116C 11BE;CD73;110E 116C 11BE; # (쵳; 쵳; 쵳; 쵳; 쵳; ) HANGUL SYLLABLE COEC
+CD74;CD74;110E 116C 11BF;CD74;110E 116C 11BF; # (쵴; 쵴; 쵴; 쵴; 쵴; ) HANGUL SYLLABLE COEK
+CD75;CD75;110E 116C 11C0;CD75;110E 116C 11C0; # (쵵; 쵵; 쵵; 쵵; 쵵; ) HANGUL SYLLABLE COET
+CD76;CD76;110E 116C 11C1;CD76;110E 116C 11C1; # (ìµ¶; ìµ¶; 최á‡; ìµ¶; 최á‡; ) HANGUL SYLLABLE COEP
+CD77;CD77;110E 116C 11C2;CD77;110E 116C 11C2; # (쵷; 쵷; 쵷; 쵷; 쵷; ) HANGUL SYLLABLE COEH
+CD78;CD78;110E 116D;CD78;110E 116D; # (쵸; 쵸; 쵸; 쵸; 쵸; ) HANGUL SYLLABLE CYO
+CD79;CD79;110E 116D 11A8;CD79;110E 116D 11A8; # (쵹; 쵹; 쵹; 쵹; 쵹; ) HANGUL SYLLABLE CYOG
+CD7A;CD7A;110E 116D 11A9;CD7A;110E 116D 11A9; # (쵺; 쵺; 쵺; 쵺; 쵺; ) HANGUL SYLLABLE CYOGG
+CD7B;CD7B;110E 116D 11AA;CD7B;110E 116D 11AA; # (쵻; 쵻; 쵻; 쵻; 쵻; ) HANGUL SYLLABLE CYOGS
+CD7C;CD7C;110E 116D 11AB;CD7C;110E 116D 11AB; # (쵼; 쵼; 쵼; 쵼; 쵼; ) HANGUL SYLLABLE CYON
+CD7D;CD7D;110E 116D 11AC;CD7D;110E 116D 11AC; # (쵽; 쵽; 쵽; 쵽; 쵽; ) HANGUL SYLLABLE CYONJ
+CD7E;CD7E;110E 116D 11AD;CD7E;110E 116D 11AD; # (쵾; 쵾; 쵾; 쵾; 쵾; ) HANGUL SYLLABLE CYONH
+CD7F;CD7F;110E 116D 11AE;CD7F;110E 116D 11AE; # (쵿; 쵿; 쵿; 쵿; 쵿; ) HANGUL SYLLABLE CYOD
+CD80;CD80;110E 116D 11AF;CD80;110E 116D 11AF; # (춀; 춀; 춀; 춀; 춀; ) HANGUL SYLLABLE CYOL
+CD81;CD81;110E 116D 11B0;CD81;110E 116D 11B0; # (ì¶; ì¶; 춁; ì¶; 춁; ) HANGUL SYLLABLE CYOLG
+CD82;CD82;110E 116D 11B1;CD82;110E 116D 11B1; # (춂; 춂; 춂; 춂; 춂; ) HANGUL SYLLABLE CYOLM
+CD83;CD83;110E 116D 11B2;CD83;110E 116D 11B2; # (춃; 춃; 춃; 춃; 춃; ) HANGUL SYLLABLE CYOLB
+CD84;CD84;110E 116D 11B3;CD84;110E 116D 11B3; # (춄; 춄; 춄; 춄; 춄; ) HANGUL SYLLABLE CYOLS
+CD85;CD85;110E 116D 11B4;CD85;110E 116D 11B4; # (춅; 춅; 춅; 춅; 춅; ) HANGUL SYLLABLE CYOLT
+CD86;CD86;110E 116D 11B5;CD86;110E 116D 11B5; # (춆; 춆; 춆; 춆; 춆; ) HANGUL SYLLABLE CYOLP
+CD87;CD87;110E 116D 11B6;CD87;110E 116D 11B6; # (춇; 춇; 춇; 춇; 춇; ) HANGUL SYLLABLE CYOLH
+CD88;CD88;110E 116D 11B7;CD88;110E 116D 11B7; # (춈; 춈; 춈; 춈; 춈; ) HANGUL SYLLABLE CYOM
+CD89;CD89;110E 116D 11B8;CD89;110E 116D 11B8; # (춉; 춉; 춉; 춉; 춉; ) HANGUL SYLLABLE CYOB
+CD8A;CD8A;110E 116D 11B9;CD8A;110E 116D 11B9; # (춊; 춊; 춊; 춊; 춊; ) HANGUL SYLLABLE CYOBS
+CD8B;CD8B;110E 116D 11BA;CD8B;110E 116D 11BA; # (춋; 춋; 춋; 춋; 춋; ) HANGUL SYLLABLE CYOS
+CD8C;CD8C;110E 116D 11BB;CD8C;110E 116D 11BB; # (춌; 춌; 춌; 춌; 춌; ) HANGUL SYLLABLE CYOSS
+CD8D;CD8D;110E 116D 11BC;CD8D;110E 116D 11BC; # (ì¶; ì¶; 춍; ì¶; 춍; ) HANGUL SYLLABLE CYONG
+CD8E;CD8E;110E 116D 11BD;CD8E;110E 116D 11BD; # (춎; 춎; 춎; 춎; 춎; ) HANGUL SYLLABLE CYOJ
+CD8F;CD8F;110E 116D 11BE;CD8F;110E 116D 11BE; # (ì¶; ì¶; 춏; ì¶; 춏; ) HANGUL SYLLABLE CYOC
+CD90;CD90;110E 116D 11BF;CD90;110E 116D 11BF; # (ì¶; ì¶; 춐; ì¶; 춐; ) HANGUL SYLLABLE CYOK
+CD91;CD91;110E 116D 11C0;CD91;110E 116D 11C0; # (춑; 춑; 춑; 춑; 춑; ) HANGUL SYLLABLE CYOT
+CD92;CD92;110E 116D 11C1;CD92;110E 116D 11C1; # (ì¶’; ì¶’; 쵸á‡; ì¶’; 쵸á‡; ) HANGUL SYLLABLE CYOP
+CD93;CD93;110E 116D 11C2;CD93;110E 116D 11C2; # (춓; 춓; 춓; 춓; 춓; ) HANGUL SYLLABLE CYOH
+CD94;CD94;110E 116E;CD94;110E 116E; # (추; 추; 추; 추; 추; ) HANGUL SYLLABLE CU
+CD95;CD95;110E 116E 11A8;CD95;110E 116E 11A8; # (축; 축; 축; 축; 축; ) HANGUL SYLLABLE CUG
+CD96;CD96;110E 116E 11A9;CD96;110E 116E 11A9; # (춖; 춖; 춖; 춖; 춖; ) HANGUL SYLLABLE CUGG
+CD97;CD97;110E 116E 11AA;CD97;110E 116E 11AA; # (춗; 춗; 춗; 춗; 춗; ) HANGUL SYLLABLE CUGS
+CD98;CD98;110E 116E 11AB;CD98;110E 116E 11AB; # (춘; 춘; 춘; 춘; 춘; ) HANGUL SYLLABLE CUN
+CD99;CD99;110E 116E 11AC;CD99;110E 116E 11AC; # (춙; 춙; 춙; 춙; 춙; ) HANGUL SYLLABLE CUNJ
+CD9A;CD9A;110E 116E 11AD;CD9A;110E 116E 11AD; # (춚; 춚; 춚; 춚; 춚; ) HANGUL SYLLABLE CUNH
+CD9B;CD9B;110E 116E 11AE;CD9B;110E 116E 11AE; # (춛; 춛; 춛; 춛; 춛; ) HANGUL SYLLABLE CUD
+CD9C;CD9C;110E 116E 11AF;CD9C;110E 116E 11AF; # (출; 출; 출; 출; 출; ) HANGUL SYLLABLE CUL
+CD9D;CD9D;110E 116E 11B0;CD9D;110E 116E 11B0; # (ì¶; ì¶; 춝; ì¶; 춝; ) HANGUL SYLLABLE CULG
+CD9E;CD9E;110E 116E 11B1;CD9E;110E 116E 11B1; # (춞; 춞; 춞; 춞; 춞; ) HANGUL SYLLABLE CULM
+CD9F;CD9F;110E 116E 11B2;CD9F;110E 116E 11B2; # (춟; 춟; 춟; 춟; 춟; ) HANGUL SYLLABLE CULB
+CDA0;CDA0;110E 116E 11B3;CDA0;110E 116E 11B3; # (춠; 춠; 춠; 춠; 춠; ) HANGUL SYLLABLE CULS
+CDA1;CDA1;110E 116E 11B4;CDA1;110E 116E 11B4; # (춡; 춡; 춡; 춡; 춡; ) HANGUL SYLLABLE CULT
+CDA2;CDA2;110E 116E 11B5;CDA2;110E 116E 11B5; # (춢; 춢; 춢; 춢; 춢; ) HANGUL SYLLABLE CULP
+CDA3;CDA3;110E 116E 11B6;CDA3;110E 116E 11B6; # (춣; 춣; 춣; 춣; 춣; ) HANGUL SYLLABLE CULH
+CDA4;CDA4;110E 116E 11B7;CDA4;110E 116E 11B7; # (춤; 춤; 춤; 춤; 춤; ) HANGUL SYLLABLE CUM
+CDA5;CDA5;110E 116E 11B8;CDA5;110E 116E 11B8; # (춥; 춥; 춥; 춥; 춥; ) HANGUL SYLLABLE CUB
+CDA6;CDA6;110E 116E 11B9;CDA6;110E 116E 11B9; # (춦; 춦; 춦; 춦; 춦; ) HANGUL SYLLABLE CUBS
+CDA7;CDA7;110E 116E 11BA;CDA7;110E 116E 11BA; # (춧; 춧; 춧; 춧; 춧; ) HANGUL SYLLABLE CUS
+CDA8;CDA8;110E 116E 11BB;CDA8;110E 116E 11BB; # (춨; 춨; 춨; 춨; 춨; ) HANGUL SYLLABLE CUSS
+CDA9;CDA9;110E 116E 11BC;CDA9;110E 116E 11BC; # (충; 충; 충; 충; 충; ) HANGUL SYLLABLE CUNG
+CDAA;CDAA;110E 116E 11BD;CDAA;110E 116E 11BD; # (춪; 춪; 춪; 춪; 춪; ) HANGUL SYLLABLE CUJ
+CDAB;CDAB;110E 116E 11BE;CDAB;110E 116E 11BE; # (춫; 춫; 춫; 춫; 춫; ) HANGUL SYLLABLE CUC
+CDAC;CDAC;110E 116E 11BF;CDAC;110E 116E 11BF; # (춬; 춬; 춬; 춬; 춬; ) HANGUL SYLLABLE CUK
+CDAD;CDAD;110E 116E 11C0;CDAD;110E 116E 11C0; # (춭; 춭; 춭; 춭; 춭; ) HANGUL SYLLABLE CUT
+CDAE;CDAE;110E 116E 11C1;CDAE;110E 116E 11C1; # (ì¶®; ì¶®; 추á‡; ì¶®; 추á‡; ) HANGUL SYLLABLE CUP
+CDAF;CDAF;110E 116E 11C2;CDAF;110E 116E 11C2; # (춯; 춯; 춯; 춯; 춯; ) HANGUL SYLLABLE CUH
+CDB0;CDB0;110E 116F;CDB0;110E 116F; # (춰; 춰; 춰; 춰; 춰; ) HANGUL SYLLABLE CWEO
+CDB1;CDB1;110E 116F 11A8;CDB1;110E 116F 11A8; # (춱; 춱; 춱; 춱; 춱; ) HANGUL SYLLABLE CWEOG
+CDB2;CDB2;110E 116F 11A9;CDB2;110E 116F 11A9; # (춲; 춲; 춲; 춲; 춲; ) HANGUL SYLLABLE CWEOGG
+CDB3;CDB3;110E 116F 11AA;CDB3;110E 116F 11AA; # (춳; 춳; 춳; 춳; 춳; ) HANGUL SYLLABLE CWEOGS
+CDB4;CDB4;110E 116F 11AB;CDB4;110E 116F 11AB; # (춴; 춴; 춴; 춴; 춴; ) HANGUL SYLLABLE CWEON
+CDB5;CDB5;110E 116F 11AC;CDB5;110E 116F 11AC; # (춵; 춵; 춵; 춵; 춵; ) HANGUL SYLLABLE CWEONJ
+CDB6;CDB6;110E 116F 11AD;CDB6;110E 116F 11AD; # (춶; 춶; 춶; 춶; 춶; ) HANGUL SYLLABLE CWEONH
+CDB7;CDB7;110E 116F 11AE;CDB7;110E 116F 11AE; # (춷; 춷; 춷; 춷; 춷; ) HANGUL SYLLABLE CWEOD
+CDB8;CDB8;110E 116F 11AF;CDB8;110E 116F 11AF; # (춸; 춸; 춸; 춸; 춸; ) HANGUL SYLLABLE CWEOL
+CDB9;CDB9;110E 116F 11B0;CDB9;110E 116F 11B0; # (춹; 춹; 춹; 춹; 춹; ) HANGUL SYLLABLE CWEOLG
+CDBA;CDBA;110E 116F 11B1;CDBA;110E 116F 11B1; # (춺; 춺; 춺; 춺; 춺; ) HANGUL SYLLABLE CWEOLM
+CDBB;CDBB;110E 116F 11B2;CDBB;110E 116F 11B2; # (춻; 춻; 춻; 춻; 춻; ) HANGUL SYLLABLE CWEOLB
+CDBC;CDBC;110E 116F 11B3;CDBC;110E 116F 11B3; # (춼; 춼; 춼; 춼; 춼; ) HANGUL SYLLABLE CWEOLS
+CDBD;CDBD;110E 116F 11B4;CDBD;110E 116F 11B4; # (춽; 춽; 춽; 춽; 춽; ) HANGUL SYLLABLE CWEOLT
+CDBE;CDBE;110E 116F 11B5;CDBE;110E 116F 11B5; # (춾; 춾; 춾; 춾; 춾; ) HANGUL SYLLABLE CWEOLP
+CDBF;CDBF;110E 116F 11B6;CDBF;110E 116F 11B6; # (춿; 춿; 춿; 춿; 춿; ) HANGUL SYLLABLE CWEOLH
+CDC0;CDC0;110E 116F 11B7;CDC0;110E 116F 11B7; # (췀; 췀; 췀; 췀; 췀; ) HANGUL SYLLABLE CWEOM
+CDC1;CDC1;110E 116F 11B8;CDC1;110E 116F 11B8; # (ì·; ì·; 췁; ì·; 췁; ) HANGUL SYLLABLE CWEOB
+CDC2;CDC2;110E 116F 11B9;CDC2;110E 116F 11B9; # (췂; 췂; 췂; 췂; 췂; ) HANGUL SYLLABLE CWEOBS
+CDC3;CDC3;110E 116F 11BA;CDC3;110E 116F 11BA; # (췃; 췃; 췃; 췃; 췃; ) HANGUL SYLLABLE CWEOS
+CDC4;CDC4;110E 116F 11BB;CDC4;110E 116F 11BB; # (췄; 췄; 췄; 췄; 췄; ) HANGUL SYLLABLE CWEOSS
+CDC5;CDC5;110E 116F 11BC;CDC5;110E 116F 11BC; # (췅; 췅; 췅; 췅; 췅; ) HANGUL SYLLABLE CWEONG
+CDC6;CDC6;110E 116F 11BD;CDC6;110E 116F 11BD; # (췆; 췆; 췆; 췆; 췆; ) HANGUL SYLLABLE CWEOJ
+CDC7;CDC7;110E 116F 11BE;CDC7;110E 116F 11BE; # (췇; 췇; 췇; 췇; 췇; ) HANGUL SYLLABLE CWEOC
+CDC8;CDC8;110E 116F 11BF;CDC8;110E 116F 11BF; # (췈; 췈; 췈; 췈; 췈; ) HANGUL SYLLABLE CWEOK
+CDC9;CDC9;110E 116F 11C0;CDC9;110E 116F 11C0; # (췉; 췉; 췉; 췉; 췉; ) HANGUL SYLLABLE CWEOT
+CDCA;CDCA;110E 116F 11C1;CDCA;110E 116F 11C1; # (ì·Š; ì·Š; 춰á‡; ì·Š; 춰á‡; ) HANGUL SYLLABLE CWEOP
+CDCB;CDCB;110E 116F 11C2;CDCB;110E 116F 11C2; # (췋; 췋; 췋; 췋; 췋; ) HANGUL SYLLABLE CWEOH
+CDCC;CDCC;110E 1170;CDCC;110E 1170; # (췌; 췌; 췌; 췌; 췌; ) HANGUL SYLLABLE CWE
+CDCD;CDCD;110E 1170 11A8;CDCD;110E 1170 11A8; # (ì·; ì·; 췍; ì·; 췍; ) HANGUL SYLLABLE CWEG
+CDCE;CDCE;110E 1170 11A9;CDCE;110E 1170 11A9; # (췎; 췎; 췎; 췎; 췎; ) HANGUL SYLLABLE CWEGG
+CDCF;CDCF;110E 1170 11AA;CDCF;110E 1170 11AA; # (ì·; ì·; 췏; ì·; 췏; ) HANGUL SYLLABLE CWEGS
+CDD0;CDD0;110E 1170 11AB;CDD0;110E 1170 11AB; # (ì·; ì·; 췐; ì·; 췐; ) HANGUL SYLLABLE CWEN
+CDD1;CDD1;110E 1170 11AC;CDD1;110E 1170 11AC; # (췑; 췑; 췑; 췑; 췑; ) HANGUL SYLLABLE CWENJ
+CDD2;CDD2;110E 1170 11AD;CDD2;110E 1170 11AD; # (췒; 췒; 췒; 췒; 췒; ) HANGUL SYLLABLE CWENH
+CDD3;CDD3;110E 1170 11AE;CDD3;110E 1170 11AE; # (췓; 췓; 췓; 췓; 췓; ) HANGUL SYLLABLE CWED
+CDD4;CDD4;110E 1170 11AF;CDD4;110E 1170 11AF; # (췔; 췔; 췔; 췔; 췔; ) HANGUL SYLLABLE CWEL
+CDD5;CDD5;110E 1170 11B0;CDD5;110E 1170 11B0; # (췕; 췕; 췕; 췕; 췕; ) HANGUL SYLLABLE CWELG
+CDD6;CDD6;110E 1170 11B1;CDD6;110E 1170 11B1; # (췖; 췖; 췖; 췖; 췖; ) HANGUL SYLLABLE CWELM
+CDD7;CDD7;110E 1170 11B2;CDD7;110E 1170 11B2; # (췗; 췗; 췗; 췗; 췗; ) HANGUL SYLLABLE CWELB
+CDD8;CDD8;110E 1170 11B3;CDD8;110E 1170 11B3; # (췘; 췘; 췘; 췘; 췘; ) HANGUL SYLLABLE CWELS
+CDD9;CDD9;110E 1170 11B4;CDD9;110E 1170 11B4; # (췙; 췙; 췙; 췙; 췙; ) HANGUL SYLLABLE CWELT
+CDDA;CDDA;110E 1170 11B5;CDDA;110E 1170 11B5; # (췚; 췚; 췚; 췚; 췚; ) HANGUL SYLLABLE CWELP
+CDDB;CDDB;110E 1170 11B6;CDDB;110E 1170 11B6; # (췛; 췛; 췛; 췛; 췛; ) HANGUL SYLLABLE CWELH
+CDDC;CDDC;110E 1170 11B7;CDDC;110E 1170 11B7; # (췜; 췜; 췜; 췜; 췜; ) HANGUL SYLLABLE CWEM
+CDDD;CDDD;110E 1170 11B8;CDDD;110E 1170 11B8; # (ì·; ì·; 췝; ì·; 췝; ) HANGUL SYLLABLE CWEB
+CDDE;CDDE;110E 1170 11B9;CDDE;110E 1170 11B9; # (췞; 췞; 췞; 췞; 췞; ) HANGUL SYLLABLE CWEBS
+CDDF;CDDF;110E 1170 11BA;CDDF;110E 1170 11BA; # (췟; 췟; 췟; 췟; 췟; ) HANGUL SYLLABLE CWES
+CDE0;CDE0;110E 1170 11BB;CDE0;110E 1170 11BB; # (췠; 췠; 췠; 췠; 췠; ) HANGUL SYLLABLE CWESS
+CDE1;CDE1;110E 1170 11BC;CDE1;110E 1170 11BC; # (췡; 췡; 췡; 췡; 췡; ) HANGUL SYLLABLE CWENG
+CDE2;CDE2;110E 1170 11BD;CDE2;110E 1170 11BD; # (췢; 췢; 췢; 췢; 췢; ) HANGUL SYLLABLE CWEJ
+CDE3;CDE3;110E 1170 11BE;CDE3;110E 1170 11BE; # (췣; 췣; 췣; 췣; 췣; ) HANGUL SYLLABLE CWEC
+CDE4;CDE4;110E 1170 11BF;CDE4;110E 1170 11BF; # (췤; 췤; 췤; 췤; 췤; ) HANGUL SYLLABLE CWEK
+CDE5;CDE5;110E 1170 11C0;CDE5;110E 1170 11C0; # (췥; 췥; 췥; 췥; 췥; ) HANGUL SYLLABLE CWET
+CDE6;CDE6;110E 1170 11C1;CDE6;110E 1170 11C1; # (ì·¦; ì·¦; 췌á‡; ì·¦; 췌á‡; ) HANGUL SYLLABLE CWEP
+CDE7;CDE7;110E 1170 11C2;CDE7;110E 1170 11C2; # (췧; 췧; 췧; 췧; 췧; ) HANGUL SYLLABLE CWEH
+CDE8;CDE8;110E 1171;CDE8;110E 1171; # (취; 취; 취; 취; 취; ) HANGUL SYLLABLE CWI
+CDE9;CDE9;110E 1171 11A8;CDE9;110E 1171 11A8; # (췩; 췩; 췩; 췩; 췩; ) HANGUL SYLLABLE CWIG
+CDEA;CDEA;110E 1171 11A9;CDEA;110E 1171 11A9; # (췪; 췪; 췪; 췪; 췪; ) HANGUL SYLLABLE CWIGG
+CDEB;CDEB;110E 1171 11AA;CDEB;110E 1171 11AA; # (췫; 췫; 췫; 췫; 췫; ) HANGUL SYLLABLE CWIGS
+CDEC;CDEC;110E 1171 11AB;CDEC;110E 1171 11AB; # (췬; 췬; 췬; 췬; 췬; ) HANGUL SYLLABLE CWIN
+CDED;CDED;110E 1171 11AC;CDED;110E 1171 11AC; # (췭; 췭; 췭; 췭; 췭; ) HANGUL SYLLABLE CWINJ
+CDEE;CDEE;110E 1171 11AD;CDEE;110E 1171 11AD; # (췮; 췮; 췮; 췮; 췮; ) HANGUL SYLLABLE CWINH
+CDEF;CDEF;110E 1171 11AE;CDEF;110E 1171 11AE; # (췯; 췯; 췯; 췯; 췯; ) HANGUL SYLLABLE CWID
+CDF0;CDF0;110E 1171 11AF;CDF0;110E 1171 11AF; # (췰; 췰; 췰; 췰; 췰; ) HANGUL SYLLABLE CWIL
+CDF1;CDF1;110E 1171 11B0;CDF1;110E 1171 11B0; # (췱; 췱; 췱; 췱; 췱; ) HANGUL SYLLABLE CWILG
+CDF2;CDF2;110E 1171 11B1;CDF2;110E 1171 11B1; # (췲; 췲; 췲; 췲; 췲; ) HANGUL SYLLABLE CWILM
+CDF3;CDF3;110E 1171 11B2;CDF3;110E 1171 11B2; # (췳; 췳; 췳; 췳; 췳; ) HANGUL SYLLABLE CWILB
+CDF4;CDF4;110E 1171 11B3;CDF4;110E 1171 11B3; # (췴; 췴; 췴; 췴; 췴; ) HANGUL SYLLABLE CWILS
+CDF5;CDF5;110E 1171 11B4;CDF5;110E 1171 11B4; # (췵; 췵; 췵; 췵; 췵; ) HANGUL SYLLABLE CWILT
+CDF6;CDF6;110E 1171 11B5;CDF6;110E 1171 11B5; # (췶; 췶; 췶; 췶; 췶; ) HANGUL SYLLABLE CWILP
+CDF7;CDF7;110E 1171 11B6;CDF7;110E 1171 11B6; # (췷; 췷; 췷; 췷; 췷; ) HANGUL SYLLABLE CWILH
+CDF8;CDF8;110E 1171 11B7;CDF8;110E 1171 11B7; # (췸; 췸; 췸; 췸; 췸; ) HANGUL SYLLABLE CWIM
+CDF9;CDF9;110E 1171 11B8;CDF9;110E 1171 11B8; # (췹; 췹; 췹; 췹; 췹; ) HANGUL SYLLABLE CWIB
+CDFA;CDFA;110E 1171 11B9;CDFA;110E 1171 11B9; # (췺; 췺; 췺; 췺; 췺; ) HANGUL SYLLABLE CWIBS
+CDFB;CDFB;110E 1171 11BA;CDFB;110E 1171 11BA; # (췻; 췻; 췻; 췻; 췻; ) HANGUL SYLLABLE CWIS
+CDFC;CDFC;110E 1171 11BB;CDFC;110E 1171 11BB; # (췼; 췼; 췼; 췼; 췼; ) HANGUL SYLLABLE CWISS
+CDFD;CDFD;110E 1171 11BC;CDFD;110E 1171 11BC; # (췽; 췽; 췽; 췽; 췽; ) HANGUL SYLLABLE CWING
+CDFE;CDFE;110E 1171 11BD;CDFE;110E 1171 11BD; # (췾; 췾; 췾; 췾; 췾; ) HANGUL SYLLABLE CWIJ
+CDFF;CDFF;110E 1171 11BE;CDFF;110E 1171 11BE; # (췿; 췿; 췿; 췿; 췿; ) HANGUL SYLLABLE CWIC
+CE00;CE00;110E 1171 11BF;CE00;110E 1171 11BF; # (츀; 츀; 츀; 츀; 츀; ) HANGUL SYLLABLE CWIK
+CE01;CE01;110E 1171 11C0;CE01;110E 1171 11C0; # (ì¸; ì¸; 츁; ì¸; 츁; ) HANGUL SYLLABLE CWIT
+CE02;CE02;110E 1171 11C1;CE02;110E 1171 11C1; # (츂; 츂; 취á‡; 츂; 취á‡; ) HANGUL SYLLABLE CWIP
+CE03;CE03;110E 1171 11C2;CE03;110E 1171 11C2; # (츃; 츃; 츃; 츃; 츃; ) HANGUL SYLLABLE CWIH
+CE04;CE04;110E 1172;CE04;110E 1172; # (츄; 츄; 츄; 츄; 츄; ) HANGUL SYLLABLE CYU
+CE05;CE05;110E 1172 11A8;CE05;110E 1172 11A8; # (츅; 츅; 츅; 츅; 츅; ) HANGUL SYLLABLE CYUG
+CE06;CE06;110E 1172 11A9;CE06;110E 1172 11A9; # (츆; 츆; 츆; 츆; 츆; ) HANGUL SYLLABLE CYUGG
+CE07;CE07;110E 1172 11AA;CE07;110E 1172 11AA; # (츇; 츇; 츇; 츇; 츇; ) HANGUL SYLLABLE CYUGS
+CE08;CE08;110E 1172 11AB;CE08;110E 1172 11AB; # (츈; 츈; 츈; 츈; 츈; ) HANGUL SYLLABLE CYUN
+CE09;CE09;110E 1172 11AC;CE09;110E 1172 11AC; # (츉; 츉; 츉; 츉; 츉; ) HANGUL SYLLABLE CYUNJ
+CE0A;CE0A;110E 1172 11AD;CE0A;110E 1172 11AD; # (츊; 츊; 츊; 츊; 츊; ) HANGUL SYLLABLE CYUNH
+CE0B;CE0B;110E 1172 11AE;CE0B;110E 1172 11AE; # (츋; 츋; 츋; 츋; 츋; ) HANGUL SYLLABLE CYUD
+CE0C;CE0C;110E 1172 11AF;CE0C;110E 1172 11AF; # (츌; 츌; 츌; 츌; 츌; ) HANGUL SYLLABLE CYUL
+CE0D;CE0D;110E 1172 11B0;CE0D;110E 1172 11B0; # (ì¸; ì¸; 츍; ì¸; 츍; ) HANGUL SYLLABLE CYULG
+CE0E;CE0E;110E 1172 11B1;CE0E;110E 1172 11B1; # (츎; 츎; 츎; 츎; 츎; ) HANGUL SYLLABLE CYULM
+CE0F;CE0F;110E 1172 11B2;CE0F;110E 1172 11B2; # (ì¸; ì¸; 츏; ì¸; 츏; ) HANGUL SYLLABLE CYULB
+CE10;CE10;110E 1172 11B3;CE10;110E 1172 11B3; # (ì¸; ì¸; 츐; ì¸; 츐; ) HANGUL SYLLABLE CYULS
+CE11;CE11;110E 1172 11B4;CE11;110E 1172 11B4; # (츑; 츑; 츑; 츑; 츑; ) HANGUL SYLLABLE CYULT
+CE12;CE12;110E 1172 11B5;CE12;110E 1172 11B5; # (츒; 츒; 츒; 츒; 츒; ) HANGUL SYLLABLE CYULP
+CE13;CE13;110E 1172 11B6;CE13;110E 1172 11B6; # (츓; 츓; 츓; 츓; 츓; ) HANGUL SYLLABLE CYULH
+CE14;CE14;110E 1172 11B7;CE14;110E 1172 11B7; # (츔; 츔; 츔; 츔; 츔; ) HANGUL SYLLABLE CYUM
+CE15;CE15;110E 1172 11B8;CE15;110E 1172 11B8; # (츕; 츕; 츕; 츕; 츕; ) HANGUL SYLLABLE CYUB
+CE16;CE16;110E 1172 11B9;CE16;110E 1172 11B9; # (츖; 츖; 츖; 츖; 츖; ) HANGUL SYLLABLE CYUBS
+CE17;CE17;110E 1172 11BA;CE17;110E 1172 11BA; # (츗; 츗; 츗; 츗; 츗; ) HANGUL SYLLABLE CYUS
+CE18;CE18;110E 1172 11BB;CE18;110E 1172 11BB; # (츘; 츘; 츘; 츘; 츘; ) HANGUL SYLLABLE CYUSS
+CE19;CE19;110E 1172 11BC;CE19;110E 1172 11BC; # (츙; 츙; 츙; 츙; 츙; ) HANGUL SYLLABLE CYUNG
+CE1A;CE1A;110E 1172 11BD;CE1A;110E 1172 11BD; # (츚; 츚; 츚; 츚; 츚; ) HANGUL SYLLABLE CYUJ
+CE1B;CE1B;110E 1172 11BE;CE1B;110E 1172 11BE; # (츛; 츛; 츛; 츛; 츛; ) HANGUL SYLLABLE CYUC
+CE1C;CE1C;110E 1172 11BF;CE1C;110E 1172 11BF; # (츜; 츜; 츜; 츜; 츜; ) HANGUL SYLLABLE CYUK
+CE1D;CE1D;110E 1172 11C0;CE1D;110E 1172 11C0; # (ì¸; ì¸; 츝; ì¸; 츝; ) HANGUL SYLLABLE CYUT
+CE1E;CE1E;110E 1172 11C1;CE1E;110E 1172 11C1; # (츞; 츞; 츄á‡; 츞; 츄á‡; ) HANGUL SYLLABLE CYUP
+CE1F;CE1F;110E 1172 11C2;CE1F;110E 1172 11C2; # (츟; 츟; 츟; 츟; 츟; ) HANGUL SYLLABLE CYUH
+CE20;CE20;110E 1173;CE20;110E 1173; # (츠; 츠; 츠; 츠; 츠; ) HANGUL SYLLABLE CEU
+CE21;CE21;110E 1173 11A8;CE21;110E 1173 11A8; # (측; 측; 측; 측; 측; ) HANGUL SYLLABLE CEUG
+CE22;CE22;110E 1173 11A9;CE22;110E 1173 11A9; # (츢; 츢; 츢; 츢; 츢; ) HANGUL SYLLABLE CEUGG
+CE23;CE23;110E 1173 11AA;CE23;110E 1173 11AA; # (츣; 츣; 츣; 츣; 츣; ) HANGUL SYLLABLE CEUGS
+CE24;CE24;110E 1173 11AB;CE24;110E 1173 11AB; # (츤; 츤; 츤; 츤; 츤; ) HANGUL SYLLABLE CEUN
+CE25;CE25;110E 1173 11AC;CE25;110E 1173 11AC; # (츥; 츥; 츥; 츥; 츥; ) HANGUL SYLLABLE CEUNJ
+CE26;CE26;110E 1173 11AD;CE26;110E 1173 11AD; # (츦; 츦; 츦; 츦; 츦; ) HANGUL SYLLABLE CEUNH
+CE27;CE27;110E 1173 11AE;CE27;110E 1173 11AE; # (츧; 츧; 츧; 츧; 츧; ) HANGUL SYLLABLE CEUD
+CE28;CE28;110E 1173 11AF;CE28;110E 1173 11AF; # (츨; 츨; 츨; 츨; 츨; ) HANGUL SYLLABLE CEUL
+CE29;CE29;110E 1173 11B0;CE29;110E 1173 11B0; # (츩; 츩; 츩; 츩; 츩; ) HANGUL SYLLABLE CEULG
+CE2A;CE2A;110E 1173 11B1;CE2A;110E 1173 11B1; # (츪; 츪; 츪; 츪; 츪; ) HANGUL SYLLABLE CEULM
+CE2B;CE2B;110E 1173 11B2;CE2B;110E 1173 11B2; # (츫; 츫; 츫; 츫; 츫; ) HANGUL SYLLABLE CEULB
+CE2C;CE2C;110E 1173 11B3;CE2C;110E 1173 11B3; # (츬; 츬; 츬; 츬; 츬; ) HANGUL SYLLABLE CEULS
+CE2D;CE2D;110E 1173 11B4;CE2D;110E 1173 11B4; # (츭; 츭; 츭; 츭; 츭; ) HANGUL SYLLABLE CEULT
+CE2E;CE2E;110E 1173 11B5;CE2E;110E 1173 11B5; # (츮; 츮; 츮; 츮; 츮; ) HANGUL SYLLABLE CEULP
+CE2F;CE2F;110E 1173 11B6;CE2F;110E 1173 11B6; # (츯; 츯; 츯; 츯; 츯; ) HANGUL SYLLABLE CEULH
+CE30;CE30;110E 1173 11B7;CE30;110E 1173 11B7; # (츰; 츰; 츰; 츰; 츰; ) HANGUL SYLLABLE CEUM
+CE31;CE31;110E 1173 11B8;CE31;110E 1173 11B8; # (츱; 츱; 츱; 츱; 츱; ) HANGUL SYLLABLE CEUB
+CE32;CE32;110E 1173 11B9;CE32;110E 1173 11B9; # (츲; 츲; 츲; 츲; 츲; ) HANGUL SYLLABLE CEUBS
+CE33;CE33;110E 1173 11BA;CE33;110E 1173 11BA; # (츳; 츳; 츳; 츳; 츳; ) HANGUL SYLLABLE CEUS
+CE34;CE34;110E 1173 11BB;CE34;110E 1173 11BB; # (츴; 츴; 츴; 츴; 츴; ) HANGUL SYLLABLE CEUSS
+CE35;CE35;110E 1173 11BC;CE35;110E 1173 11BC; # (층; 층; 층; 층; 층; ) HANGUL SYLLABLE CEUNG
+CE36;CE36;110E 1173 11BD;CE36;110E 1173 11BD; # (츶; 츶; 츶; 츶; 츶; ) HANGUL SYLLABLE CEUJ
+CE37;CE37;110E 1173 11BE;CE37;110E 1173 11BE; # (츷; 츷; 츷; 츷; 츷; ) HANGUL SYLLABLE CEUC
+CE38;CE38;110E 1173 11BF;CE38;110E 1173 11BF; # (츸; 츸; 츸; 츸; 츸; ) HANGUL SYLLABLE CEUK
+CE39;CE39;110E 1173 11C0;CE39;110E 1173 11C0; # (츹; 츹; 츹; 츹; 츹; ) HANGUL SYLLABLE CEUT
+CE3A;CE3A;110E 1173 11C1;CE3A;110E 1173 11C1; # (츺; 츺; 츠á‡; 츺; 츠á‡; ) HANGUL SYLLABLE CEUP
+CE3B;CE3B;110E 1173 11C2;CE3B;110E 1173 11C2; # (츻; 츻; 츻; 츻; 츻; ) HANGUL SYLLABLE CEUH
+CE3C;CE3C;110E 1174;CE3C;110E 1174; # (츼; 츼; 츼; 츼; 츼; ) HANGUL SYLLABLE CYI
+CE3D;CE3D;110E 1174 11A8;CE3D;110E 1174 11A8; # (츽; 츽; 츽; 츽; 츽; ) HANGUL SYLLABLE CYIG
+CE3E;CE3E;110E 1174 11A9;CE3E;110E 1174 11A9; # (츾; 츾; 츾; 츾; 츾; ) HANGUL SYLLABLE CYIGG
+CE3F;CE3F;110E 1174 11AA;CE3F;110E 1174 11AA; # (츿; 츿; 츿; 츿; 츿; ) HANGUL SYLLABLE CYIGS
+CE40;CE40;110E 1174 11AB;CE40;110E 1174 11AB; # (칀; 칀; 칀; 칀; 칀; ) HANGUL SYLLABLE CYIN
+CE41;CE41;110E 1174 11AC;CE41;110E 1174 11AC; # (ì¹; ì¹; 칁; ì¹; 칁; ) HANGUL SYLLABLE CYINJ
+CE42;CE42;110E 1174 11AD;CE42;110E 1174 11AD; # (칂; 칂; 칂; 칂; 칂; ) HANGUL SYLLABLE CYINH
+CE43;CE43;110E 1174 11AE;CE43;110E 1174 11AE; # (칃; 칃; 칃; 칃; 칃; ) HANGUL SYLLABLE CYID
+CE44;CE44;110E 1174 11AF;CE44;110E 1174 11AF; # (칄; 칄; 칄; 칄; 칄; ) HANGUL SYLLABLE CYIL
+CE45;CE45;110E 1174 11B0;CE45;110E 1174 11B0; # (칅; 칅; 칅; 칅; 칅; ) HANGUL SYLLABLE CYILG
+CE46;CE46;110E 1174 11B1;CE46;110E 1174 11B1; # (칆; 칆; 칆; 칆; 칆; ) HANGUL SYLLABLE CYILM
+CE47;CE47;110E 1174 11B2;CE47;110E 1174 11B2; # (칇; 칇; 칇; 칇; 칇; ) HANGUL SYLLABLE CYILB
+CE48;CE48;110E 1174 11B3;CE48;110E 1174 11B3; # (칈; 칈; 칈; 칈; 칈; ) HANGUL SYLLABLE CYILS
+CE49;CE49;110E 1174 11B4;CE49;110E 1174 11B4; # (칉; 칉; 칉; 칉; 칉; ) HANGUL SYLLABLE CYILT
+CE4A;CE4A;110E 1174 11B5;CE4A;110E 1174 11B5; # (칊; 칊; 칊; 칊; 칊; ) HANGUL SYLLABLE CYILP
+CE4B;CE4B;110E 1174 11B6;CE4B;110E 1174 11B6; # (칋; 칋; 칋; 칋; 칋; ) HANGUL SYLLABLE CYILH
+CE4C;CE4C;110E 1174 11B7;CE4C;110E 1174 11B7; # (칌; 칌; 칌; 칌; 칌; ) HANGUL SYLLABLE CYIM
+CE4D;CE4D;110E 1174 11B8;CE4D;110E 1174 11B8; # (ì¹; ì¹; 칍; ì¹; 칍; ) HANGUL SYLLABLE CYIB
+CE4E;CE4E;110E 1174 11B9;CE4E;110E 1174 11B9; # (칎; 칎; 칎; 칎; 칎; ) HANGUL SYLLABLE CYIBS
+CE4F;CE4F;110E 1174 11BA;CE4F;110E 1174 11BA; # (ì¹; ì¹; 칏; ì¹; 칏; ) HANGUL SYLLABLE CYIS
+CE50;CE50;110E 1174 11BB;CE50;110E 1174 11BB; # (ì¹; ì¹; 칐; ì¹; 칐; ) HANGUL SYLLABLE CYISS
+CE51;CE51;110E 1174 11BC;CE51;110E 1174 11BC; # (칑; 칑; 칑; 칑; 칑; ) HANGUL SYLLABLE CYING
+CE52;CE52;110E 1174 11BD;CE52;110E 1174 11BD; # (칒; 칒; 칒; 칒; 칒; ) HANGUL SYLLABLE CYIJ
+CE53;CE53;110E 1174 11BE;CE53;110E 1174 11BE; # (칓; 칓; 칓; 칓; 칓; ) HANGUL SYLLABLE CYIC
+CE54;CE54;110E 1174 11BF;CE54;110E 1174 11BF; # (칔; 칔; 칔; 칔; 칔; ) HANGUL SYLLABLE CYIK
+CE55;CE55;110E 1174 11C0;CE55;110E 1174 11C0; # (칕; 칕; 칕; 칕; 칕; ) HANGUL SYLLABLE CYIT
+CE56;CE56;110E 1174 11C1;CE56;110E 1174 11C1; # (ì¹–; ì¹–; 츼á‡; ì¹–; 츼á‡; ) HANGUL SYLLABLE CYIP
+CE57;CE57;110E 1174 11C2;CE57;110E 1174 11C2; # (칗; 칗; 칗; 칗; 칗; ) HANGUL SYLLABLE CYIH
+CE58;CE58;110E 1175;CE58;110E 1175; # (치; 치; 치; 치; 치; ) HANGUL SYLLABLE CI
+CE59;CE59;110E 1175 11A8;CE59;110E 1175 11A8; # (칙; 칙; 칙; 칙; 칙; ) HANGUL SYLLABLE CIG
+CE5A;CE5A;110E 1175 11A9;CE5A;110E 1175 11A9; # (칚; 칚; 칚; 칚; 칚; ) HANGUL SYLLABLE CIGG
+CE5B;CE5B;110E 1175 11AA;CE5B;110E 1175 11AA; # (칛; 칛; 칛; 칛; 칛; ) HANGUL SYLLABLE CIGS
+CE5C;CE5C;110E 1175 11AB;CE5C;110E 1175 11AB; # (친; 친; 친; 친; 친; ) HANGUL SYLLABLE CIN
+CE5D;CE5D;110E 1175 11AC;CE5D;110E 1175 11AC; # (ì¹; ì¹; 칝; ì¹; 칝; ) HANGUL SYLLABLE CINJ
+CE5E;CE5E;110E 1175 11AD;CE5E;110E 1175 11AD; # (칞; 칞; 칞; 칞; 칞; ) HANGUL SYLLABLE CINH
+CE5F;CE5F;110E 1175 11AE;CE5F;110E 1175 11AE; # (칟; 칟; 칟; 칟; 칟; ) HANGUL SYLLABLE CID
+CE60;CE60;110E 1175 11AF;CE60;110E 1175 11AF; # (칠; 칠; 칠; 칠; 칠; ) HANGUL SYLLABLE CIL
+CE61;CE61;110E 1175 11B0;CE61;110E 1175 11B0; # (칡; 칡; 칡; 칡; 칡; ) HANGUL SYLLABLE CILG
+CE62;CE62;110E 1175 11B1;CE62;110E 1175 11B1; # (칢; 칢; 칢; 칢; 칢; ) HANGUL SYLLABLE CILM
+CE63;CE63;110E 1175 11B2;CE63;110E 1175 11B2; # (칣; 칣; 칣; 칣; 칣; ) HANGUL SYLLABLE CILB
+CE64;CE64;110E 1175 11B3;CE64;110E 1175 11B3; # (칤; 칤; 칤; 칤; 칤; ) HANGUL SYLLABLE CILS
+CE65;CE65;110E 1175 11B4;CE65;110E 1175 11B4; # (칥; 칥; 칥; 칥; 칥; ) HANGUL SYLLABLE CILT
+CE66;CE66;110E 1175 11B5;CE66;110E 1175 11B5; # (칦; 칦; 칦; 칦; 칦; ) HANGUL SYLLABLE CILP
+CE67;CE67;110E 1175 11B6;CE67;110E 1175 11B6; # (칧; 칧; 칧; 칧; 칧; ) HANGUL SYLLABLE CILH
+CE68;CE68;110E 1175 11B7;CE68;110E 1175 11B7; # (침; 침; 침; 침; 침; ) HANGUL SYLLABLE CIM
+CE69;CE69;110E 1175 11B8;CE69;110E 1175 11B8; # (칩; 칩; 칩; 칩; 칩; ) HANGUL SYLLABLE CIB
+CE6A;CE6A;110E 1175 11B9;CE6A;110E 1175 11B9; # (칪; 칪; 칪; 칪; 칪; ) HANGUL SYLLABLE CIBS
+CE6B;CE6B;110E 1175 11BA;CE6B;110E 1175 11BA; # (칫; 칫; 칫; 칫; 칫; ) HANGUL SYLLABLE CIS
+CE6C;CE6C;110E 1175 11BB;CE6C;110E 1175 11BB; # (칬; 칬; 칬; 칬; 칬; ) HANGUL SYLLABLE CISS
+CE6D;CE6D;110E 1175 11BC;CE6D;110E 1175 11BC; # (칭; 칭; 칭; 칭; 칭; ) HANGUL SYLLABLE CING
+CE6E;CE6E;110E 1175 11BD;CE6E;110E 1175 11BD; # (칮; 칮; 칮; 칮; 칮; ) HANGUL SYLLABLE CIJ
+CE6F;CE6F;110E 1175 11BE;CE6F;110E 1175 11BE; # (칯; 칯; 칯; 칯; 칯; ) HANGUL SYLLABLE CIC
+CE70;CE70;110E 1175 11BF;CE70;110E 1175 11BF; # (칰; 칰; 칰; 칰; 칰; ) HANGUL SYLLABLE CIK
+CE71;CE71;110E 1175 11C0;CE71;110E 1175 11C0; # (칱; 칱; 칱; 칱; 칱; ) HANGUL SYLLABLE CIT
+CE72;CE72;110E 1175 11C1;CE72;110E 1175 11C1; # (ì¹²; ì¹²; 치á‡; ì¹²; 치á‡; ) HANGUL SYLLABLE CIP
+CE73;CE73;110E 1175 11C2;CE73;110E 1175 11C2; # (칳; 칳; 칳; 칳; 칳; ) HANGUL SYLLABLE CIH
+CE74;CE74;110F 1161;CE74;110F 1161; # (ì¹´; ì¹´; á„á…¡; ì¹´; á„á…¡; ) HANGUL SYLLABLE KA
+CE75;CE75;110F 1161 11A8;CE75;110F 1161 11A8; # (ì¹µ; ì¹µ; á„ᅡᆨ; ì¹µ; á„ᅡᆨ; ) HANGUL SYLLABLE KAG
+CE76;CE76;110F 1161 11A9;CE76;110F 1161 11A9; # (ì¹¶; ì¹¶; á„ᅡᆩ; ì¹¶; á„ᅡᆩ; ) HANGUL SYLLABLE KAGG
+CE77;CE77;110F 1161 11AA;CE77;110F 1161 11AA; # (ì¹·; ì¹·; á„ᅡᆪ; ì¹·; á„ᅡᆪ; ) HANGUL SYLLABLE KAGS
+CE78;CE78;110F 1161 11AB;CE78;110F 1161 11AB; # (칸; 칸; á„ᅡᆫ; 칸; á„ᅡᆫ; ) HANGUL SYLLABLE KAN
+CE79;CE79;110F 1161 11AC;CE79;110F 1161 11AC; # (ì¹¹; ì¹¹; á„ᅡᆬ; ì¹¹; á„ᅡᆬ; ) HANGUL SYLLABLE KANJ
+CE7A;CE7A;110F 1161 11AD;CE7A;110F 1161 11AD; # (칺; 칺; á„ᅡᆭ; 칺; á„ᅡᆭ; ) HANGUL SYLLABLE KANH
+CE7B;CE7B;110F 1161 11AE;CE7B;110F 1161 11AE; # (ì¹»; ì¹»; á„ᅡᆮ; ì¹»; á„ᅡᆮ; ) HANGUL SYLLABLE KAD
+CE7C;CE7C;110F 1161 11AF;CE7C;110F 1161 11AF; # (ì¹¼; ì¹¼; á„ᅡᆯ; ì¹¼; á„ᅡᆯ; ) HANGUL SYLLABLE KAL
+CE7D;CE7D;110F 1161 11B0;CE7D;110F 1161 11B0; # (ì¹½; ì¹½; á„ᅡᆰ; ì¹½; á„ᅡᆰ; ) HANGUL SYLLABLE KALG
+CE7E;CE7E;110F 1161 11B1;CE7E;110F 1161 11B1; # (ì¹¾; ì¹¾; á„ᅡᆱ; ì¹¾; á„ᅡᆱ; ) HANGUL SYLLABLE KALM
+CE7F;CE7F;110F 1161 11B2;CE7F;110F 1161 11B2; # (칿; 칿; á„ᅡᆲ; 칿; á„ᅡᆲ; ) HANGUL SYLLABLE KALB
+CE80;CE80;110F 1161 11B3;CE80;110F 1161 11B3; # (캀; 캀; á„ᅡᆳ; 캀; á„ᅡᆳ; ) HANGUL SYLLABLE KALS
+CE81;CE81;110F 1161 11B4;CE81;110F 1161 11B4; # (ìº; ìº; á„ᅡᆴ; ìº; á„ᅡᆴ; ) HANGUL SYLLABLE KALT
+CE82;CE82;110F 1161 11B5;CE82;110F 1161 11B5; # (캂; 캂; á„ᅡᆵ; 캂; á„ᅡᆵ; ) HANGUL SYLLABLE KALP
+CE83;CE83;110F 1161 11B6;CE83;110F 1161 11B6; # (캃; 캃; á„ᅡᆶ; 캃; á„ᅡᆶ; ) HANGUL SYLLABLE KALH
+CE84;CE84;110F 1161 11B7;CE84;110F 1161 11B7; # (캄; 캄; á„ᅡᆷ; 캄; á„ᅡᆷ; ) HANGUL SYLLABLE KAM
+CE85;CE85;110F 1161 11B8;CE85;110F 1161 11B8; # (캅; 캅; á„ᅡᆸ; 캅; á„ᅡᆸ; ) HANGUL SYLLABLE KAB
+CE86;CE86;110F 1161 11B9;CE86;110F 1161 11B9; # (캆; 캆; á„ᅡᆹ; 캆; á„ᅡᆹ; ) HANGUL SYLLABLE KABS
+CE87;CE87;110F 1161 11BA;CE87;110F 1161 11BA; # (캇; 캇; á„ᅡᆺ; 캇; á„ᅡᆺ; ) HANGUL SYLLABLE KAS
+CE88;CE88;110F 1161 11BB;CE88;110F 1161 11BB; # (캈; 캈; á„ᅡᆻ; 캈; á„ᅡᆻ; ) HANGUL SYLLABLE KASS
+CE89;CE89;110F 1161 11BC;CE89;110F 1161 11BC; # (캉; 캉; á„ᅡᆼ; 캉; á„ᅡᆼ; ) HANGUL SYLLABLE KANG
+CE8A;CE8A;110F 1161 11BD;CE8A;110F 1161 11BD; # (캊; 캊; á„ᅡᆽ; 캊; á„ᅡᆽ; ) HANGUL SYLLABLE KAJ
+CE8B;CE8B;110F 1161 11BE;CE8B;110F 1161 11BE; # (캋; 캋; á„ᅡᆾ; 캋; á„ᅡᆾ; ) HANGUL SYLLABLE KAC
+CE8C;CE8C;110F 1161 11BF;CE8C;110F 1161 11BF; # (캌; 캌; á„ᅡᆿ; 캌; á„ᅡᆿ; ) HANGUL SYLLABLE KAK
+CE8D;CE8D;110F 1161 11C0;CE8D;110F 1161 11C0; # (ìº; ìº; á„ᅡᇀ; ìº; á„ᅡᇀ; ) HANGUL SYLLABLE KAT
+CE8E;CE8E;110F 1161 11C1;CE8E;110F 1161 11C1; # (캎; 캎; á„á…¡á‡; 캎; á„á…¡á‡; ) HANGUL SYLLABLE KAP
+CE8F;CE8F;110F 1161 11C2;CE8F;110F 1161 11C2; # (ìº; ìº; á„ᅡᇂ; ìº; á„ᅡᇂ; ) HANGUL SYLLABLE KAH
+CE90;CE90;110F 1162;CE90;110F 1162; # (ìº; ìº; á„á…¢; ìº; á„á…¢; ) HANGUL SYLLABLE KAE
+CE91;CE91;110F 1162 11A8;CE91;110F 1162 11A8; # (캑; 캑; á„ᅢᆨ; 캑; á„ᅢᆨ; ) HANGUL SYLLABLE KAEG
+CE92;CE92;110F 1162 11A9;CE92;110F 1162 11A9; # (캒; 캒; á„ᅢᆩ; 캒; á„ᅢᆩ; ) HANGUL SYLLABLE KAEGG
+CE93;CE93;110F 1162 11AA;CE93;110F 1162 11AA; # (캓; 캓; á„ᅢᆪ; 캓; á„ᅢᆪ; ) HANGUL SYLLABLE KAEGS
+CE94;CE94;110F 1162 11AB;CE94;110F 1162 11AB; # (캔; 캔; á„ᅢᆫ; 캔; á„ᅢᆫ; ) HANGUL SYLLABLE KAEN
+CE95;CE95;110F 1162 11AC;CE95;110F 1162 11AC; # (캕; 캕; á„ᅢᆬ; 캕; á„ᅢᆬ; ) HANGUL SYLLABLE KAENJ
+CE96;CE96;110F 1162 11AD;CE96;110F 1162 11AD; # (캖; 캖; á„ᅢᆭ; 캖; á„ᅢᆭ; ) HANGUL SYLLABLE KAENH
+CE97;CE97;110F 1162 11AE;CE97;110F 1162 11AE; # (캗; 캗; á„ᅢᆮ; 캗; á„ᅢᆮ; ) HANGUL SYLLABLE KAED
+CE98;CE98;110F 1162 11AF;CE98;110F 1162 11AF; # (캘; 캘; á„ᅢᆯ; 캘; á„ᅢᆯ; ) HANGUL SYLLABLE KAEL
+CE99;CE99;110F 1162 11B0;CE99;110F 1162 11B0; # (캙; 캙; á„ᅢᆰ; 캙; á„ᅢᆰ; ) HANGUL SYLLABLE KAELG
+CE9A;CE9A;110F 1162 11B1;CE9A;110F 1162 11B1; # (캚; 캚; á„ᅢᆱ; 캚; á„ᅢᆱ; ) HANGUL SYLLABLE KAELM
+CE9B;CE9B;110F 1162 11B2;CE9B;110F 1162 11B2; # (캛; 캛; á„ᅢᆲ; 캛; á„ᅢᆲ; ) HANGUL SYLLABLE KAELB
+CE9C;CE9C;110F 1162 11B3;CE9C;110F 1162 11B3; # (캜; 캜; á„ᅢᆳ; 캜; á„ᅢᆳ; ) HANGUL SYLLABLE KAELS
+CE9D;CE9D;110F 1162 11B4;CE9D;110F 1162 11B4; # (ìº; ìº; á„ᅢᆴ; ìº; á„ᅢᆴ; ) HANGUL SYLLABLE KAELT
+CE9E;CE9E;110F 1162 11B5;CE9E;110F 1162 11B5; # (캞; 캞; á„ᅢᆵ; 캞; á„ᅢᆵ; ) HANGUL SYLLABLE KAELP
+CE9F;CE9F;110F 1162 11B6;CE9F;110F 1162 11B6; # (캟; 캟; á„ᅢᆶ; 캟; á„ᅢᆶ; ) HANGUL SYLLABLE KAELH
+CEA0;CEA0;110F 1162 11B7;CEA0;110F 1162 11B7; # (캠; 캠; á„ᅢᆷ; 캠; á„ᅢᆷ; ) HANGUL SYLLABLE KAEM
+CEA1;CEA1;110F 1162 11B8;CEA1;110F 1162 11B8; # (캡; 캡; á„ᅢᆸ; 캡; á„ᅢᆸ; ) HANGUL SYLLABLE KAEB
+CEA2;CEA2;110F 1162 11B9;CEA2;110F 1162 11B9; # (캢; 캢; á„ᅢᆹ; 캢; á„ᅢᆹ; ) HANGUL SYLLABLE KAEBS
+CEA3;CEA3;110F 1162 11BA;CEA3;110F 1162 11BA; # (캣; 캣; á„ᅢᆺ; 캣; á„ᅢᆺ; ) HANGUL SYLLABLE KAES
+CEA4;CEA4;110F 1162 11BB;CEA4;110F 1162 11BB; # (캤; 캤; á„ᅢᆻ; 캤; á„ᅢᆻ; ) HANGUL SYLLABLE KAESS
+CEA5;CEA5;110F 1162 11BC;CEA5;110F 1162 11BC; # (캥; 캥; á„ᅢᆼ; 캥; á„ᅢᆼ; ) HANGUL SYLLABLE KAENG
+CEA6;CEA6;110F 1162 11BD;CEA6;110F 1162 11BD; # (캦; 캦; á„ᅢᆽ; 캦; á„ᅢᆽ; ) HANGUL SYLLABLE KAEJ
+CEA7;CEA7;110F 1162 11BE;CEA7;110F 1162 11BE; # (캧; 캧; á„ᅢᆾ; 캧; á„ᅢᆾ; ) HANGUL SYLLABLE KAEC
+CEA8;CEA8;110F 1162 11BF;CEA8;110F 1162 11BF; # (캨; 캨; á„ᅢᆿ; 캨; á„ᅢᆿ; ) HANGUL SYLLABLE KAEK
+CEA9;CEA9;110F 1162 11C0;CEA9;110F 1162 11C0; # (캩; 캩; á„ᅢᇀ; 캩; á„ᅢᇀ; ) HANGUL SYLLABLE KAET
+CEAA;CEAA;110F 1162 11C1;CEAA;110F 1162 11C1; # (캪; 캪; á„á…¢á‡; 캪; á„á…¢á‡; ) HANGUL SYLLABLE KAEP
+CEAB;CEAB;110F 1162 11C2;CEAB;110F 1162 11C2; # (캫; 캫; á„ᅢᇂ; 캫; á„ᅢᇂ; ) HANGUL SYLLABLE KAEH
+CEAC;CEAC;110F 1163;CEAC;110F 1163; # (캬; 캬; á„á…£; 캬; á„á…£; ) HANGUL SYLLABLE KYA
+CEAD;CEAD;110F 1163 11A8;CEAD;110F 1163 11A8; # (캭; 캭; á„ᅣᆨ; 캭; á„ᅣᆨ; ) HANGUL SYLLABLE KYAG
+CEAE;CEAE;110F 1163 11A9;CEAE;110F 1163 11A9; # (캮; 캮; á„ᅣᆩ; 캮; á„ᅣᆩ; ) HANGUL SYLLABLE KYAGG
+CEAF;CEAF;110F 1163 11AA;CEAF;110F 1163 11AA; # (캯; 캯; á„ᅣᆪ; 캯; á„ᅣᆪ; ) HANGUL SYLLABLE KYAGS
+CEB0;CEB0;110F 1163 11AB;CEB0;110F 1163 11AB; # (캰; 캰; á„ᅣᆫ; 캰; á„ᅣᆫ; ) HANGUL SYLLABLE KYAN
+CEB1;CEB1;110F 1163 11AC;CEB1;110F 1163 11AC; # (캱; 캱; á„ᅣᆬ; 캱; á„ᅣᆬ; ) HANGUL SYLLABLE KYANJ
+CEB2;CEB2;110F 1163 11AD;CEB2;110F 1163 11AD; # (캲; 캲; á„ᅣᆭ; 캲; á„ᅣᆭ; ) HANGUL SYLLABLE KYANH
+CEB3;CEB3;110F 1163 11AE;CEB3;110F 1163 11AE; # (캳; 캳; á„ᅣᆮ; 캳; á„ᅣᆮ; ) HANGUL SYLLABLE KYAD
+CEB4;CEB4;110F 1163 11AF;CEB4;110F 1163 11AF; # (캴; 캴; á„ᅣᆯ; 캴; á„ᅣᆯ; ) HANGUL SYLLABLE KYAL
+CEB5;CEB5;110F 1163 11B0;CEB5;110F 1163 11B0; # (캵; 캵; á„ᅣᆰ; 캵; á„ᅣᆰ; ) HANGUL SYLLABLE KYALG
+CEB6;CEB6;110F 1163 11B1;CEB6;110F 1163 11B1; # (캶; 캶; á„ᅣᆱ; 캶; á„ᅣᆱ; ) HANGUL SYLLABLE KYALM
+CEB7;CEB7;110F 1163 11B2;CEB7;110F 1163 11B2; # (캷; 캷; á„ᅣᆲ; 캷; á„ᅣᆲ; ) HANGUL SYLLABLE KYALB
+CEB8;CEB8;110F 1163 11B3;CEB8;110F 1163 11B3; # (캸; 캸; á„ᅣᆳ; 캸; á„ᅣᆳ; ) HANGUL SYLLABLE KYALS
+CEB9;CEB9;110F 1163 11B4;CEB9;110F 1163 11B4; # (캹; 캹; á„ᅣᆴ; 캹; á„ᅣᆴ; ) HANGUL SYLLABLE KYALT
+CEBA;CEBA;110F 1163 11B5;CEBA;110F 1163 11B5; # (캺; 캺; á„ᅣᆵ; 캺; á„ᅣᆵ; ) HANGUL SYLLABLE KYALP
+CEBB;CEBB;110F 1163 11B6;CEBB;110F 1163 11B6; # (캻; 캻; á„ᅣᆶ; 캻; á„ᅣᆶ; ) HANGUL SYLLABLE KYALH
+CEBC;CEBC;110F 1163 11B7;CEBC;110F 1163 11B7; # (캼; 캼; á„ᅣᆷ; 캼; á„ᅣᆷ; ) HANGUL SYLLABLE KYAM
+CEBD;CEBD;110F 1163 11B8;CEBD;110F 1163 11B8; # (캽; 캽; á„ᅣᆸ; 캽; á„ᅣᆸ; ) HANGUL SYLLABLE KYAB
+CEBE;CEBE;110F 1163 11B9;CEBE;110F 1163 11B9; # (캾; 캾; á„ᅣᆹ; 캾; á„ᅣᆹ; ) HANGUL SYLLABLE KYABS
+CEBF;CEBF;110F 1163 11BA;CEBF;110F 1163 11BA; # (캿; 캿; á„ᅣᆺ; 캿; á„ᅣᆺ; ) HANGUL SYLLABLE KYAS
+CEC0;CEC0;110F 1163 11BB;CEC0;110F 1163 11BB; # (컀; 컀; á„ᅣᆻ; 컀; á„ᅣᆻ; ) HANGUL SYLLABLE KYASS
+CEC1;CEC1;110F 1163 11BC;CEC1;110F 1163 11BC; # (ì»; ì»; á„ᅣᆼ; ì»; á„ᅣᆼ; ) HANGUL SYLLABLE KYANG
+CEC2;CEC2;110F 1163 11BD;CEC2;110F 1163 11BD; # (컂; 컂; á„ᅣᆽ; 컂; á„ᅣᆽ; ) HANGUL SYLLABLE KYAJ
+CEC3;CEC3;110F 1163 11BE;CEC3;110F 1163 11BE; # (컃; 컃; á„ᅣᆾ; 컃; á„ᅣᆾ; ) HANGUL SYLLABLE KYAC
+CEC4;CEC4;110F 1163 11BF;CEC4;110F 1163 11BF; # (컄; 컄; á„ᅣᆿ; 컄; á„ᅣᆿ; ) HANGUL SYLLABLE KYAK
+CEC5;CEC5;110F 1163 11C0;CEC5;110F 1163 11C0; # (ì»…; ì»…; á„ᅣᇀ; ì»…; á„ᅣᇀ; ) HANGUL SYLLABLE KYAT
+CEC6;CEC6;110F 1163 11C1;CEC6;110F 1163 11C1; # (컆; 컆; á„á…£á‡; 컆; á„á…£á‡; ) HANGUL SYLLABLE KYAP
+CEC7;CEC7;110F 1163 11C2;CEC7;110F 1163 11C2; # (컇; 컇; á„ᅣᇂ; 컇; á„ᅣᇂ; ) HANGUL SYLLABLE KYAH
+CEC8;CEC8;110F 1164;CEC8;110F 1164; # (컈; 컈; á„á…¤; 컈; á„á…¤; ) HANGUL SYLLABLE KYAE
+CEC9;CEC9;110F 1164 11A8;CEC9;110F 1164 11A8; # (컉; 컉; á„ᅤᆨ; 컉; á„ᅤᆨ; ) HANGUL SYLLABLE KYAEG
+CECA;CECA;110F 1164 11A9;CECA;110F 1164 11A9; # (컊; 컊; á„ᅤᆩ; 컊; á„ᅤᆩ; ) HANGUL SYLLABLE KYAEGG
+CECB;CECB;110F 1164 11AA;CECB;110F 1164 11AA; # (컋; 컋; á„ᅤᆪ; 컋; á„ᅤᆪ; ) HANGUL SYLLABLE KYAEGS
+CECC;CECC;110F 1164 11AB;CECC;110F 1164 11AB; # (컌; 컌; á„ᅤᆫ; 컌; á„ᅤᆫ; ) HANGUL SYLLABLE KYAEN
+CECD;CECD;110F 1164 11AC;CECD;110F 1164 11AC; # (ì»; ì»; á„ᅤᆬ; ì»; á„ᅤᆬ; ) HANGUL SYLLABLE KYAENJ
+CECE;CECE;110F 1164 11AD;CECE;110F 1164 11AD; # (컎; 컎; á„ᅤᆭ; 컎; á„ᅤᆭ; ) HANGUL SYLLABLE KYAENH
+CECF;CECF;110F 1164 11AE;CECF;110F 1164 11AE; # (ì»; ì»; á„ᅤᆮ; ì»; á„ᅤᆮ; ) HANGUL SYLLABLE KYAED
+CED0;CED0;110F 1164 11AF;CED0;110F 1164 11AF; # (ì»; ì»; á„ᅤᆯ; ì»; á„ᅤᆯ; ) HANGUL SYLLABLE KYAEL
+CED1;CED1;110F 1164 11B0;CED1;110F 1164 11B0; # (컑; 컑; á„ᅤᆰ; 컑; á„ᅤᆰ; ) HANGUL SYLLABLE KYAELG
+CED2;CED2;110F 1164 11B1;CED2;110F 1164 11B1; # (ì»’; ì»’; á„ᅤᆱ; ì»’; á„ᅤᆱ; ) HANGUL SYLLABLE KYAELM
+CED3;CED3;110F 1164 11B2;CED3;110F 1164 11B2; # (컓; 컓; á„ᅤᆲ; 컓; á„ᅤᆲ; ) HANGUL SYLLABLE KYAELB
+CED4;CED4;110F 1164 11B3;CED4;110F 1164 11B3; # (ì»”; ì»”; á„ᅤᆳ; ì»”; á„ᅤᆳ; ) HANGUL SYLLABLE KYAELS
+CED5;CED5;110F 1164 11B4;CED5;110F 1164 11B4; # (컕; 컕; á„ᅤᆴ; 컕; á„ᅤᆴ; ) HANGUL SYLLABLE KYAELT
+CED6;CED6;110F 1164 11B5;CED6;110F 1164 11B5; # (ì»–; ì»–; á„ᅤᆵ; ì»–; á„ᅤᆵ; ) HANGUL SYLLABLE KYAELP
+CED7;CED7;110F 1164 11B6;CED7;110F 1164 11B6; # (ì»—; ì»—; á„ᅤᆶ; ì»—; á„ᅤᆶ; ) HANGUL SYLLABLE KYAELH
+CED8;CED8;110F 1164 11B7;CED8;110F 1164 11B7; # (컘; 컘; á„ᅤᆷ; 컘; á„ᅤᆷ; ) HANGUL SYLLABLE KYAEM
+CED9;CED9;110F 1164 11B8;CED9;110F 1164 11B8; # (ì»™; ì»™; á„ᅤᆸ; ì»™; á„ᅤᆸ; ) HANGUL SYLLABLE KYAEB
+CEDA;CEDA;110F 1164 11B9;CEDA;110F 1164 11B9; # (컚; 컚; á„ᅤᆹ; 컚; á„ᅤᆹ; ) HANGUL SYLLABLE KYAEBS
+CEDB;CEDB;110F 1164 11BA;CEDB;110F 1164 11BA; # (ì»›; ì»›; á„ᅤᆺ; ì»›; á„ᅤᆺ; ) HANGUL SYLLABLE KYAES
+CEDC;CEDC;110F 1164 11BB;CEDC;110F 1164 11BB; # (컜; 컜; á„ᅤᆻ; 컜; á„ᅤᆻ; ) HANGUL SYLLABLE KYAESS
+CEDD;CEDD;110F 1164 11BC;CEDD;110F 1164 11BC; # (ì»; ì»; á„ᅤᆼ; ì»; á„ᅤᆼ; ) HANGUL SYLLABLE KYAENG
+CEDE;CEDE;110F 1164 11BD;CEDE;110F 1164 11BD; # (컞; 컞; á„ᅤᆽ; 컞; á„ᅤᆽ; ) HANGUL SYLLABLE KYAEJ
+CEDF;CEDF;110F 1164 11BE;CEDF;110F 1164 11BE; # (컟; 컟; á„ᅤᆾ; 컟; á„ᅤᆾ; ) HANGUL SYLLABLE KYAEC
+CEE0;CEE0;110F 1164 11BF;CEE0;110F 1164 11BF; # (ì» ; ì» ; á„ᅤᆿ; ì» ; á„ᅤᆿ; ) HANGUL SYLLABLE KYAEK
+CEE1;CEE1;110F 1164 11C0;CEE1;110F 1164 11C0; # (컡; 컡; á„ᅤᇀ; 컡; á„ᅤᇀ; ) HANGUL SYLLABLE KYAET
+CEE2;CEE2;110F 1164 11C1;CEE2;110F 1164 11C1; # (컢; 컢; á„á…¤á‡; 컢; á„á…¤á‡; ) HANGUL SYLLABLE KYAEP
+CEE3;CEE3;110F 1164 11C2;CEE3;110F 1164 11C2; # (컣; 컣; á„ᅤᇂ; 컣; á„ᅤᇂ; ) HANGUL SYLLABLE KYAEH
+CEE4;CEE4;110F 1165;CEE4;110F 1165; # (커; 커; á„á…¥; 커; á„á…¥; ) HANGUL SYLLABLE KEO
+CEE5;CEE5;110F 1165 11A8;CEE5;110F 1165 11A8; # (컥; 컥; á„ᅥᆨ; 컥; á„ᅥᆨ; ) HANGUL SYLLABLE KEOG
+CEE6;CEE6;110F 1165 11A9;CEE6;110F 1165 11A9; # (컦; 컦; á„ᅥᆩ; 컦; á„ᅥᆩ; ) HANGUL SYLLABLE KEOGG
+CEE7;CEE7;110F 1165 11AA;CEE7;110F 1165 11AA; # (ì»§; ì»§; á„ᅥᆪ; ì»§; á„ᅥᆪ; ) HANGUL SYLLABLE KEOGS
+CEE8;CEE8;110F 1165 11AB;CEE8;110F 1165 11AB; # (컨; 컨; á„ᅥᆫ; 컨; á„ᅥᆫ; ) HANGUL SYLLABLE KEON
+CEE9;CEE9;110F 1165 11AC;CEE9;110F 1165 11AC; # (컩; 컩; á„ᅥᆬ; 컩; á„ᅥᆬ; ) HANGUL SYLLABLE KEONJ
+CEEA;CEEA;110F 1165 11AD;CEEA;110F 1165 11AD; # (컪; 컪; á„ᅥᆭ; 컪; á„ᅥᆭ; ) HANGUL SYLLABLE KEONH
+CEEB;CEEB;110F 1165 11AE;CEEB;110F 1165 11AE; # (컫; 컫; á„ᅥᆮ; 컫; á„ᅥᆮ; ) HANGUL SYLLABLE KEOD
+CEEC;CEEC;110F 1165 11AF;CEEC;110F 1165 11AF; # (컬; 컬; á„ᅥᆯ; 컬; á„ᅥᆯ; ) HANGUL SYLLABLE KEOL
+CEED;CEED;110F 1165 11B0;CEED;110F 1165 11B0; # (ì»­; ì»­; á„ᅥᆰ; ì»­; á„ᅥᆰ; ) HANGUL SYLLABLE KEOLG
+CEEE;CEEE;110F 1165 11B1;CEEE;110F 1165 11B1; # (ì»®; ì»®; á„ᅥᆱ; ì»®; á„ᅥᆱ; ) HANGUL SYLLABLE KEOLM
+CEEF;CEEF;110F 1165 11B2;CEEF;110F 1165 11B2; # (컯; 컯; á„ᅥᆲ; 컯; á„ᅥᆲ; ) HANGUL SYLLABLE KEOLB
+CEF0;CEF0;110F 1165 11B3;CEF0;110F 1165 11B3; # (ì»°; ì»°; á„ᅥᆳ; ì»°; á„ᅥᆳ; ) HANGUL SYLLABLE KEOLS
+CEF1;CEF1;110F 1165 11B4;CEF1;110F 1165 11B4; # (ì»±; ì»±; á„ᅥᆴ; ì»±; á„ᅥᆴ; ) HANGUL SYLLABLE KEOLT
+CEF2;CEF2;110F 1165 11B5;CEF2;110F 1165 11B5; # (컲; 컲; á„ᅥᆵ; 컲; á„ᅥᆵ; ) HANGUL SYLLABLE KEOLP
+CEF3;CEF3;110F 1165 11B6;CEF3;110F 1165 11B6; # (컳; 컳; á„ᅥᆶ; 컳; á„ᅥᆶ; ) HANGUL SYLLABLE KEOLH
+CEF4;CEF4;110F 1165 11B7;CEF4;110F 1165 11B7; # (ì»´; ì»´; á„ᅥᆷ; ì»´; á„ᅥᆷ; ) HANGUL SYLLABLE KEOM
+CEF5;CEF5;110F 1165 11B8;CEF5;110F 1165 11B8; # (컵; 컵; á„ᅥᆸ; 컵; á„ᅥᆸ; ) HANGUL SYLLABLE KEOB
+CEF6;CEF6;110F 1165 11B9;CEF6;110F 1165 11B9; # (ì»¶; ì»¶; á„ᅥᆹ; ì»¶; á„ᅥᆹ; ) HANGUL SYLLABLE KEOBS
+CEF7;CEF7;110F 1165 11BA;CEF7;110F 1165 11BA; # (ì»·; ì»·; á„ᅥᆺ; ì»·; á„ᅥᆺ; ) HANGUL SYLLABLE KEOS
+CEF8;CEF8;110F 1165 11BB;CEF8;110F 1165 11BB; # (컸; 컸; á„ᅥᆻ; 컸; á„ᅥᆻ; ) HANGUL SYLLABLE KEOSS
+CEF9;CEF9;110F 1165 11BC;CEF9;110F 1165 11BC; # (컹; 컹; á„ᅥᆼ; 컹; á„ᅥᆼ; ) HANGUL SYLLABLE KEONG
+CEFA;CEFA;110F 1165 11BD;CEFA;110F 1165 11BD; # (컺; 컺; á„ᅥᆽ; 컺; á„ᅥᆽ; ) HANGUL SYLLABLE KEOJ
+CEFB;CEFB;110F 1165 11BE;CEFB;110F 1165 11BE; # (ì»»; ì»»; á„ᅥᆾ; ì»»; á„ᅥᆾ; ) HANGUL SYLLABLE KEOC
+CEFC;CEFC;110F 1165 11BF;CEFC;110F 1165 11BF; # (컼; 컼; á„ᅥᆿ; 컼; á„ᅥᆿ; ) HANGUL SYLLABLE KEOK
+CEFD;CEFD;110F 1165 11C0;CEFD;110F 1165 11C0; # (컽; 컽; á„ᅥᇀ; 컽; á„ᅥᇀ; ) HANGUL SYLLABLE KEOT
+CEFE;CEFE;110F 1165 11C1;CEFE;110F 1165 11C1; # (컾; 컾; á„á…¥á‡; 컾; á„á…¥á‡; ) HANGUL SYLLABLE KEOP
+CEFF;CEFF;110F 1165 11C2;CEFF;110F 1165 11C2; # (컿; 컿; á„ᅥᇂ; 컿; á„ᅥᇂ; ) HANGUL SYLLABLE KEOH
+CF00;CF00;110F 1166;CF00;110F 1166; # (ì¼€; ì¼€; á„á…¦; ì¼€; á„á…¦; ) HANGUL SYLLABLE KE
+CF01;CF01;110F 1166 11A8;CF01;110F 1166 11A8; # (ì¼; ì¼; á„ᅦᆨ; ì¼; á„ᅦᆨ; ) HANGUL SYLLABLE KEG
+CF02;CF02;110F 1166 11A9;CF02;110F 1166 11A9; # (켂; 켂; á„ᅦᆩ; 켂; á„ᅦᆩ; ) HANGUL SYLLABLE KEGG
+CF03;CF03;110F 1166 11AA;CF03;110F 1166 11AA; # (켃; 켃; á„ᅦᆪ; 켃; á„ᅦᆪ; ) HANGUL SYLLABLE KEGS
+CF04;CF04;110F 1166 11AB;CF04;110F 1166 11AB; # (켄; 켄; á„ᅦᆫ; 켄; á„ᅦᆫ; ) HANGUL SYLLABLE KEN
+CF05;CF05;110F 1166 11AC;CF05;110F 1166 11AC; # (ì¼…; ì¼…; á„ᅦᆬ; ì¼…; á„ᅦᆬ; ) HANGUL SYLLABLE KENJ
+CF06;CF06;110F 1166 11AD;CF06;110F 1166 11AD; # (켆; 켆; á„ᅦᆭ; 켆; á„ᅦᆭ; ) HANGUL SYLLABLE KENH
+CF07;CF07;110F 1166 11AE;CF07;110F 1166 11AE; # (켇; 켇; á„ᅦᆮ; 켇; á„ᅦᆮ; ) HANGUL SYLLABLE KED
+CF08;CF08;110F 1166 11AF;CF08;110F 1166 11AF; # (켈; 켈; á„ᅦᆯ; 켈; á„ᅦᆯ; ) HANGUL SYLLABLE KEL
+CF09;CF09;110F 1166 11B0;CF09;110F 1166 11B0; # (켉; 켉; á„ᅦᆰ; 켉; á„ᅦᆰ; ) HANGUL SYLLABLE KELG
+CF0A;CF0A;110F 1166 11B1;CF0A;110F 1166 11B1; # (켊; 켊; á„ᅦᆱ; 켊; á„ᅦᆱ; ) HANGUL SYLLABLE KELM
+CF0B;CF0B;110F 1166 11B2;CF0B;110F 1166 11B2; # (켋; 켋; á„ᅦᆲ; 켋; á„ᅦᆲ; ) HANGUL SYLLABLE KELB
+CF0C;CF0C;110F 1166 11B3;CF0C;110F 1166 11B3; # (켌; 켌; á„ᅦᆳ; 켌; á„ᅦᆳ; ) HANGUL SYLLABLE KELS
+CF0D;CF0D;110F 1166 11B4;CF0D;110F 1166 11B4; # (ì¼; ì¼; á„ᅦᆴ; ì¼; á„ᅦᆴ; ) HANGUL SYLLABLE KELT
+CF0E;CF0E;110F 1166 11B5;CF0E;110F 1166 11B5; # (켎; 켎; á„ᅦᆵ; 켎; á„ᅦᆵ; ) HANGUL SYLLABLE KELP
+CF0F;CF0F;110F 1166 11B6;CF0F;110F 1166 11B6; # (ì¼; ì¼; á„ᅦᆶ; ì¼; á„ᅦᆶ; ) HANGUL SYLLABLE KELH
+CF10;CF10;110F 1166 11B7;CF10;110F 1166 11B7; # (ì¼; ì¼; á„ᅦᆷ; ì¼; á„ᅦᆷ; ) HANGUL SYLLABLE KEM
+CF11;CF11;110F 1166 11B8;CF11;110F 1166 11B8; # (켑; 켑; á„ᅦᆸ; 켑; á„ᅦᆸ; ) HANGUL SYLLABLE KEB
+CF12;CF12;110F 1166 11B9;CF12;110F 1166 11B9; # (ì¼’; ì¼’; á„ᅦᆹ; ì¼’; á„ᅦᆹ; ) HANGUL SYLLABLE KEBS
+CF13;CF13;110F 1166 11BA;CF13;110F 1166 11BA; # (켓; 켓; á„ᅦᆺ; 켓; á„ᅦᆺ; ) HANGUL SYLLABLE KES
+CF14;CF14;110F 1166 11BB;CF14;110F 1166 11BB; # (ì¼”; ì¼”; á„ᅦᆻ; ì¼”; á„ᅦᆻ; ) HANGUL SYLLABLE KESS
+CF15;CF15;110F 1166 11BC;CF15;110F 1166 11BC; # (켕; 켕; á„ᅦᆼ; 켕; á„ᅦᆼ; ) HANGUL SYLLABLE KENG
+CF16;CF16;110F 1166 11BD;CF16;110F 1166 11BD; # (ì¼–; ì¼–; á„ᅦᆽ; ì¼–; á„ᅦᆽ; ) HANGUL SYLLABLE KEJ
+CF17;CF17;110F 1166 11BE;CF17;110F 1166 11BE; # (ì¼—; ì¼—; á„ᅦᆾ; ì¼—; á„ᅦᆾ; ) HANGUL SYLLABLE KEC
+CF18;CF18;110F 1166 11BF;CF18;110F 1166 11BF; # (켘; 켘; á„ᅦᆿ; 켘; á„ᅦᆿ; ) HANGUL SYLLABLE KEK
+CF19;CF19;110F 1166 11C0;CF19;110F 1166 11C0; # (ì¼™; ì¼™; á„ᅦᇀ; ì¼™; á„ᅦᇀ; ) HANGUL SYLLABLE KET
+CF1A;CF1A;110F 1166 11C1;CF1A;110F 1166 11C1; # (켚; 켚; á„á…¦á‡; 켚; á„á…¦á‡; ) HANGUL SYLLABLE KEP
+CF1B;CF1B;110F 1166 11C2;CF1B;110F 1166 11C2; # (ì¼›; ì¼›; á„ᅦᇂ; ì¼›; á„ᅦᇂ; ) HANGUL SYLLABLE KEH
+CF1C;CF1C;110F 1167;CF1C;110F 1167; # (켜; 켜; á„á…§; 켜; á„á…§; ) HANGUL SYLLABLE KYEO
+CF1D;CF1D;110F 1167 11A8;CF1D;110F 1167 11A8; # (ì¼; ì¼; á„ᅧᆨ; ì¼; á„ᅧᆨ; ) HANGUL SYLLABLE KYEOG
+CF1E;CF1E;110F 1167 11A9;CF1E;110F 1167 11A9; # (켞; 켞; á„ᅧᆩ; 켞; á„ᅧᆩ; ) HANGUL SYLLABLE KYEOGG
+CF1F;CF1F;110F 1167 11AA;CF1F;110F 1167 11AA; # (켟; 켟; á„ᅧᆪ; 켟; á„ᅧᆪ; ) HANGUL SYLLABLE KYEOGS
+CF20;CF20;110F 1167 11AB;CF20;110F 1167 11AB; # (ì¼ ; ì¼ ; á„ᅧᆫ; ì¼ ; á„ᅧᆫ; ) HANGUL SYLLABLE KYEON
+CF21;CF21;110F 1167 11AC;CF21;110F 1167 11AC; # (켡; 켡; á„ᅧᆬ; 켡; á„ᅧᆬ; ) HANGUL SYLLABLE KYEONJ
+CF22;CF22;110F 1167 11AD;CF22;110F 1167 11AD; # (ì¼¢; ì¼¢; á„ᅧᆭ; ì¼¢; á„ᅧᆭ; ) HANGUL SYLLABLE KYEONH
+CF23;CF23;110F 1167 11AE;CF23;110F 1167 11AE; # (ì¼£; ì¼£; á„ᅧᆮ; ì¼£; á„ᅧᆮ; ) HANGUL SYLLABLE KYEOD
+CF24;CF24;110F 1167 11AF;CF24;110F 1167 11AF; # (켤; 켤; á„ᅧᆯ; 켤; á„ᅧᆯ; ) HANGUL SYLLABLE KYEOL
+CF25;CF25;110F 1167 11B0;CF25;110F 1167 11B0; # (ì¼¥; ì¼¥; á„ᅧᆰ; ì¼¥; á„ᅧᆰ; ) HANGUL SYLLABLE KYEOLG
+CF26;CF26;110F 1167 11B1;CF26;110F 1167 11B1; # (켦; 켦; á„ᅧᆱ; 켦; á„ᅧᆱ; ) HANGUL SYLLABLE KYEOLM
+CF27;CF27;110F 1167 11B2;CF27;110F 1167 11B2; # (ì¼§; ì¼§; á„ᅧᆲ; ì¼§; á„ᅧᆲ; ) HANGUL SYLLABLE KYEOLB
+CF28;CF28;110F 1167 11B3;CF28;110F 1167 11B3; # (켨; 켨; á„ᅧᆳ; 켨; á„ᅧᆳ; ) HANGUL SYLLABLE KYEOLS
+CF29;CF29;110F 1167 11B4;CF29;110F 1167 11B4; # (켩; 켩; á„ᅧᆴ; 켩; á„ᅧᆴ; ) HANGUL SYLLABLE KYEOLT
+CF2A;CF2A;110F 1167 11B5;CF2A;110F 1167 11B5; # (켪; 켪; á„ᅧᆵ; 켪; á„ᅧᆵ; ) HANGUL SYLLABLE KYEOLP
+CF2B;CF2B;110F 1167 11B6;CF2B;110F 1167 11B6; # (켫; 켫; á„ᅧᆶ; 켫; á„ᅧᆶ; ) HANGUL SYLLABLE KYEOLH
+CF2C;CF2C;110F 1167 11B7;CF2C;110F 1167 11B7; # (켬; 켬; á„ᅧᆷ; 켬; á„ᅧᆷ; ) HANGUL SYLLABLE KYEOM
+CF2D;CF2D;110F 1167 11B8;CF2D;110F 1167 11B8; # (ì¼­; ì¼­; á„ᅧᆸ; ì¼­; á„ᅧᆸ; ) HANGUL SYLLABLE KYEOB
+CF2E;CF2E;110F 1167 11B9;CF2E;110F 1167 11B9; # (ì¼®; ì¼®; á„ᅧᆹ; ì¼®; á„ᅧᆹ; ) HANGUL SYLLABLE KYEOBS
+CF2F;CF2F;110F 1167 11BA;CF2F;110F 1167 11BA; # (켯; 켯; á„ᅧᆺ; 켯; á„ᅧᆺ; ) HANGUL SYLLABLE KYEOS
+CF30;CF30;110F 1167 11BB;CF30;110F 1167 11BB; # (ì¼°; ì¼°; á„ᅧᆻ; ì¼°; á„ᅧᆻ; ) HANGUL SYLLABLE KYEOSS
+CF31;CF31;110F 1167 11BC;CF31;110F 1167 11BC; # (ì¼±; ì¼±; á„ᅧᆼ; ì¼±; á„ᅧᆼ; ) HANGUL SYLLABLE KYEONG
+CF32;CF32;110F 1167 11BD;CF32;110F 1167 11BD; # (ì¼²; ì¼²; á„ᅧᆽ; ì¼²; á„ᅧᆽ; ) HANGUL SYLLABLE KYEOJ
+CF33;CF33;110F 1167 11BE;CF33;110F 1167 11BE; # (ì¼³; ì¼³; á„ᅧᆾ; ì¼³; á„ᅧᆾ; ) HANGUL SYLLABLE KYEOC
+CF34;CF34;110F 1167 11BF;CF34;110F 1167 11BF; # (ì¼´; ì¼´; á„ᅧᆿ; ì¼´; á„ᅧᆿ; ) HANGUL SYLLABLE KYEOK
+CF35;CF35;110F 1167 11C0;CF35;110F 1167 11C0; # (ì¼µ; ì¼µ; á„ᅧᇀ; ì¼µ; á„ᅧᇀ; ) HANGUL SYLLABLE KYEOT
+CF36;CF36;110F 1167 11C1;CF36;110F 1167 11C1; # (ì¼¶; ì¼¶; á„á…§á‡; ì¼¶; á„á…§á‡; ) HANGUL SYLLABLE KYEOP
+CF37;CF37;110F 1167 11C2;CF37;110F 1167 11C2; # (ì¼·; ì¼·; á„ᅧᇂ; ì¼·; á„ᅧᇂ; ) HANGUL SYLLABLE KYEOH
+CF38;CF38;110F 1168;CF38;110F 1168; # (켸; 켸; á„á…¨; 켸; á„á…¨; ) HANGUL SYLLABLE KYE
+CF39;CF39;110F 1168 11A8;CF39;110F 1168 11A8; # (ì¼¹; ì¼¹; á„ᅨᆨ; ì¼¹; á„ᅨᆨ; ) HANGUL SYLLABLE KYEG
+CF3A;CF3A;110F 1168 11A9;CF3A;110F 1168 11A9; # (켺; 켺; á„ᅨᆩ; 켺; á„ᅨᆩ; ) HANGUL SYLLABLE KYEGG
+CF3B;CF3B;110F 1168 11AA;CF3B;110F 1168 11AA; # (ì¼»; ì¼»; á„ᅨᆪ; ì¼»; á„ᅨᆪ; ) HANGUL SYLLABLE KYEGS
+CF3C;CF3C;110F 1168 11AB;CF3C;110F 1168 11AB; # (ì¼¼; ì¼¼; á„ᅨᆫ; ì¼¼; á„ᅨᆫ; ) HANGUL SYLLABLE KYEN
+CF3D;CF3D;110F 1168 11AC;CF3D;110F 1168 11AC; # (ì¼½; ì¼½; á„ᅨᆬ; ì¼½; á„ᅨᆬ; ) HANGUL SYLLABLE KYENJ
+CF3E;CF3E;110F 1168 11AD;CF3E;110F 1168 11AD; # (ì¼¾; ì¼¾; á„ᅨᆭ; ì¼¾; á„ᅨᆭ; ) HANGUL SYLLABLE KYENH
+CF3F;CF3F;110F 1168 11AE;CF3F;110F 1168 11AE; # (켿; 켿; á„ᅨᆮ; 켿; á„ᅨᆮ; ) HANGUL SYLLABLE KYED
+CF40;CF40;110F 1168 11AF;CF40;110F 1168 11AF; # (ì½€; ì½€; á„ᅨᆯ; ì½€; á„ᅨᆯ; ) HANGUL SYLLABLE KYEL
+CF41;CF41;110F 1168 11B0;CF41;110F 1168 11B0; # (ì½; ì½; á„ᅨᆰ; ì½; á„ᅨᆰ; ) HANGUL SYLLABLE KYELG
+CF42;CF42;110F 1168 11B1;CF42;110F 1168 11B1; # (콂; 콂; á„ᅨᆱ; 콂; á„ᅨᆱ; ) HANGUL SYLLABLE KYELM
+CF43;CF43;110F 1168 11B2;CF43;110F 1168 11B2; # (콃; 콃; á„ᅨᆲ; 콃; á„ᅨᆲ; ) HANGUL SYLLABLE KYELB
+CF44;CF44;110F 1168 11B3;CF44;110F 1168 11B3; # (콄; 콄; á„ᅨᆳ; 콄; á„ᅨᆳ; ) HANGUL SYLLABLE KYELS
+CF45;CF45;110F 1168 11B4;CF45;110F 1168 11B4; # (ì½…; ì½…; á„ᅨᆴ; ì½…; á„ᅨᆴ; ) HANGUL SYLLABLE KYELT
+CF46;CF46;110F 1168 11B5;CF46;110F 1168 11B5; # (콆; 콆; á„ᅨᆵ; 콆; á„ᅨᆵ; ) HANGUL SYLLABLE KYELP
+CF47;CF47;110F 1168 11B6;CF47;110F 1168 11B6; # (콇; 콇; á„ᅨᆶ; 콇; á„ᅨᆶ; ) HANGUL SYLLABLE KYELH
+CF48;CF48;110F 1168 11B7;CF48;110F 1168 11B7; # (콈; 콈; á„ᅨᆷ; 콈; á„ᅨᆷ; ) HANGUL SYLLABLE KYEM
+CF49;CF49;110F 1168 11B8;CF49;110F 1168 11B8; # (콉; 콉; á„ᅨᆸ; 콉; á„ᅨᆸ; ) HANGUL SYLLABLE KYEB
+CF4A;CF4A;110F 1168 11B9;CF4A;110F 1168 11B9; # (콊; 콊; á„ᅨᆹ; 콊; á„ᅨᆹ; ) HANGUL SYLLABLE KYEBS
+CF4B;CF4B;110F 1168 11BA;CF4B;110F 1168 11BA; # (콋; 콋; á„ᅨᆺ; 콋; á„ᅨᆺ; ) HANGUL SYLLABLE KYES
+CF4C;CF4C;110F 1168 11BB;CF4C;110F 1168 11BB; # (콌; 콌; á„ᅨᆻ; 콌; á„ᅨᆻ; ) HANGUL SYLLABLE KYESS
+CF4D;CF4D;110F 1168 11BC;CF4D;110F 1168 11BC; # (ì½; ì½; á„ᅨᆼ; ì½; á„ᅨᆼ; ) HANGUL SYLLABLE KYENG
+CF4E;CF4E;110F 1168 11BD;CF4E;110F 1168 11BD; # (콎; 콎; á„ᅨᆽ; 콎; á„ᅨᆽ; ) HANGUL SYLLABLE KYEJ
+CF4F;CF4F;110F 1168 11BE;CF4F;110F 1168 11BE; # (ì½; ì½; á„ᅨᆾ; ì½; á„ᅨᆾ; ) HANGUL SYLLABLE KYEC
+CF50;CF50;110F 1168 11BF;CF50;110F 1168 11BF; # (ì½; ì½; á„ᅨᆿ; ì½; á„ᅨᆿ; ) HANGUL SYLLABLE KYEK
+CF51;CF51;110F 1168 11C0;CF51;110F 1168 11C0; # (콑; 콑; á„ᅨᇀ; 콑; á„ᅨᇀ; ) HANGUL SYLLABLE KYET
+CF52;CF52;110F 1168 11C1;CF52;110F 1168 11C1; # (ì½’; ì½’; á„á…¨á‡; ì½’; á„á…¨á‡; ) HANGUL SYLLABLE KYEP
+CF53;CF53;110F 1168 11C2;CF53;110F 1168 11C2; # (콓; 콓; á„ᅨᇂ; 콓; á„ᅨᇂ; ) HANGUL SYLLABLE KYEH
+CF54;CF54;110F 1169;CF54;110F 1169; # (ì½”; ì½”; á„á…©; ì½”; á„á…©; ) HANGUL SYLLABLE KO
+CF55;CF55;110F 1169 11A8;CF55;110F 1169 11A8; # (콕; 콕; á„ᅩᆨ; 콕; á„ᅩᆨ; ) HANGUL SYLLABLE KOG
+CF56;CF56;110F 1169 11A9;CF56;110F 1169 11A9; # (ì½–; ì½–; á„ᅩᆩ; ì½–; á„ᅩᆩ; ) HANGUL SYLLABLE KOGG
+CF57;CF57;110F 1169 11AA;CF57;110F 1169 11AA; # (ì½—; ì½—; á„ᅩᆪ; ì½—; á„ᅩᆪ; ) HANGUL SYLLABLE KOGS
+CF58;CF58;110F 1169 11AB;CF58;110F 1169 11AB; # (콘; 콘; á„ᅩᆫ; 콘; á„ᅩᆫ; ) HANGUL SYLLABLE KON
+CF59;CF59;110F 1169 11AC;CF59;110F 1169 11AC; # (ì½™; ì½™; á„ᅩᆬ; ì½™; á„ᅩᆬ; ) HANGUL SYLLABLE KONJ
+CF5A;CF5A;110F 1169 11AD;CF5A;110F 1169 11AD; # (콚; 콚; á„ᅩᆭ; 콚; á„ᅩᆭ; ) HANGUL SYLLABLE KONH
+CF5B;CF5B;110F 1169 11AE;CF5B;110F 1169 11AE; # (ì½›; ì½›; á„ᅩᆮ; ì½›; á„ᅩᆮ; ) HANGUL SYLLABLE KOD
+CF5C;CF5C;110F 1169 11AF;CF5C;110F 1169 11AF; # (콜; 콜; á„ᅩᆯ; 콜; á„ᅩᆯ; ) HANGUL SYLLABLE KOL
+CF5D;CF5D;110F 1169 11B0;CF5D;110F 1169 11B0; # (ì½; ì½; á„ᅩᆰ; ì½; á„ᅩᆰ; ) HANGUL SYLLABLE KOLG
+CF5E;CF5E;110F 1169 11B1;CF5E;110F 1169 11B1; # (콞; 콞; á„ᅩᆱ; 콞; á„ᅩᆱ; ) HANGUL SYLLABLE KOLM
+CF5F;CF5F;110F 1169 11B2;CF5F;110F 1169 11B2; # (콟; 콟; á„ᅩᆲ; 콟; á„ᅩᆲ; ) HANGUL SYLLABLE KOLB
+CF60;CF60;110F 1169 11B3;CF60;110F 1169 11B3; # (ì½ ; ì½ ; á„ᅩᆳ; ì½ ; á„ᅩᆳ; ) HANGUL SYLLABLE KOLS
+CF61;CF61;110F 1169 11B4;CF61;110F 1169 11B4; # (콡; 콡; á„ᅩᆴ; 콡; á„ᅩᆴ; ) HANGUL SYLLABLE KOLT
+CF62;CF62;110F 1169 11B5;CF62;110F 1169 11B5; # (ì½¢; ì½¢; á„ᅩᆵ; ì½¢; á„ᅩᆵ; ) HANGUL SYLLABLE KOLP
+CF63;CF63;110F 1169 11B6;CF63;110F 1169 11B6; # (ì½£; ì½£; á„ᅩᆶ; ì½£; á„ᅩᆶ; ) HANGUL SYLLABLE KOLH
+CF64;CF64;110F 1169 11B7;CF64;110F 1169 11B7; # (콤; 콤; á„ᅩᆷ; 콤; á„ᅩᆷ; ) HANGUL SYLLABLE KOM
+CF65;CF65;110F 1169 11B8;CF65;110F 1169 11B8; # (ì½¥; ì½¥; á„ᅩᆸ; ì½¥; á„ᅩᆸ; ) HANGUL SYLLABLE KOB
+CF66;CF66;110F 1169 11B9;CF66;110F 1169 11B9; # (콦; 콦; á„ᅩᆹ; 콦; á„ᅩᆹ; ) HANGUL SYLLABLE KOBS
+CF67;CF67;110F 1169 11BA;CF67;110F 1169 11BA; # (ì½§; ì½§; á„ᅩᆺ; ì½§; á„ᅩᆺ; ) HANGUL SYLLABLE KOS
+CF68;CF68;110F 1169 11BB;CF68;110F 1169 11BB; # (콨; 콨; á„ᅩᆻ; 콨; á„ᅩᆻ; ) HANGUL SYLLABLE KOSS
+CF69;CF69;110F 1169 11BC;CF69;110F 1169 11BC; # (콩; 콩; á„ᅩᆼ; 콩; á„ᅩᆼ; ) HANGUL SYLLABLE KONG
+CF6A;CF6A;110F 1169 11BD;CF6A;110F 1169 11BD; # (콪; 콪; á„ᅩᆽ; 콪; á„ᅩᆽ; ) HANGUL SYLLABLE KOJ
+CF6B;CF6B;110F 1169 11BE;CF6B;110F 1169 11BE; # (콫; 콫; á„ᅩᆾ; 콫; á„ᅩᆾ; ) HANGUL SYLLABLE KOC
+CF6C;CF6C;110F 1169 11BF;CF6C;110F 1169 11BF; # (콬; 콬; á„ᅩᆿ; 콬; á„ᅩᆿ; ) HANGUL SYLLABLE KOK
+CF6D;CF6D;110F 1169 11C0;CF6D;110F 1169 11C0; # (ì½­; ì½­; á„ᅩᇀ; ì½­; á„ᅩᇀ; ) HANGUL SYLLABLE KOT
+CF6E;CF6E;110F 1169 11C1;CF6E;110F 1169 11C1; # (ì½®; ì½®; á„á…©á‡; ì½®; á„á…©á‡; ) HANGUL SYLLABLE KOP
+CF6F;CF6F;110F 1169 11C2;CF6F;110F 1169 11C2; # (콯; 콯; á„ᅩᇂ; 콯; á„ᅩᇂ; ) HANGUL SYLLABLE KOH
+CF70;CF70;110F 116A;CF70;110F 116A; # (ì½°; ì½°; á„á…ª; ì½°; á„á…ª; ) HANGUL SYLLABLE KWA
+CF71;CF71;110F 116A 11A8;CF71;110F 116A 11A8; # (ì½±; ì½±; á„ᅪᆨ; ì½±; á„ᅪᆨ; ) HANGUL SYLLABLE KWAG
+CF72;CF72;110F 116A 11A9;CF72;110F 116A 11A9; # (ì½²; ì½²; á„ᅪᆩ; ì½²; á„ᅪᆩ; ) HANGUL SYLLABLE KWAGG
+CF73;CF73;110F 116A 11AA;CF73;110F 116A 11AA; # (ì½³; ì½³; á„ᅪᆪ; ì½³; á„ᅪᆪ; ) HANGUL SYLLABLE KWAGS
+CF74;CF74;110F 116A 11AB;CF74;110F 116A 11AB; # (ì½´; ì½´; á„ᅪᆫ; ì½´; á„ᅪᆫ; ) HANGUL SYLLABLE KWAN
+CF75;CF75;110F 116A 11AC;CF75;110F 116A 11AC; # (ì½µ; ì½µ; á„ᅪᆬ; ì½µ; á„ᅪᆬ; ) HANGUL SYLLABLE KWANJ
+CF76;CF76;110F 116A 11AD;CF76;110F 116A 11AD; # (ì½¶; ì½¶; á„ᅪᆭ; ì½¶; á„ᅪᆭ; ) HANGUL SYLLABLE KWANH
+CF77;CF77;110F 116A 11AE;CF77;110F 116A 11AE; # (ì½·; ì½·; á„ᅪᆮ; ì½·; á„ᅪᆮ; ) HANGUL SYLLABLE KWAD
+CF78;CF78;110F 116A 11AF;CF78;110F 116A 11AF; # (콸; 콸; á„ᅪᆯ; 콸; á„ᅪᆯ; ) HANGUL SYLLABLE KWAL
+CF79;CF79;110F 116A 11B0;CF79;110F 116A 11B0; # (ì½¹; ì½¹; á„ᅪᆰ; ì½¹; á„ᅪᆰ; ) HANGUL SYLLABLE KWALG
+CF7A;CF7A;110F 116A 11B1;CF7A;110F 116A 11B1; # (콺; 콺; á„ᅪᆱ; 콺; á„ᅪᆱ; ) HANGUL SYLLABLE KWALM
+CF7B;CF7B;110F 116A 11B2;CF7B;110F 116A 11B2; # (ì½»; ì½»; á„ᅪᆲ; ì½»; á„ᅪᆲ; ) HANGUL SYLLABLE KWALB
+CF7C;CF7C;110F 116A 11B3;CF7C;110F 116A 11B3; # (ì½¼; ì½¼; á„ᅪᆳ; ì½¼; á„ᅪᆳ; ) HANGUL SYLLABLE KWALS
+CF7D;CF7D;110F 116A 11B4;CF7D;110F 116A 11B4; # (ì½½; ì½½; á„ᅪᆴ; ì½½; á„ᅪᆴ; ) HANGUL SYLLABLE KWALT
+CF7E;CF7E;110F 116A 11B5;CF7E;110F 116A 11B5; # (ì½¾; ì½¾; á„ᅪᆵ; ì½¾; á„ᅪᆵ; ) HANGUL SYLLABLE KWALP
+CF7F;CF7F;110F 116A 11B6;CF7F;110F 116A 11B6; # (콿; 콿; á„ᅪᆶ; 콿; á„ᅪᆶ; ) HANGUL SYLLABLE KWALH
+CF80;CF80;110F 116A 11B7;CF80;110F 116A 11B7; # (ì¾€; ì¾€; á„ᅪᆷ; ì¾€; á„ᅪᆷ; ) HANGUL SYLLABLE KWAM
+CF81;CF81;110F 116A 11B8;CF81;110F 116A 11B8; # (ì¾; ì¾; á„ᅪᆸ; ì¾; á„ᅪᆸ; ) HANGUL SYLLABLE KWAB
+CF82;CF82;110F 116A 11B9;CF82;110F 116A 11B9; # (쾂; 쾂; á„ᅪᆹ; 쾂; á„ᅪᆹ; ) HANGUL SYLLABLE KWABS
+CF83;CF83;110F 116A 11BA;CF83;110F 116A 11BA; # (쾃; 쾃; á„ᅪᆺ; 쾃; á„ᅪᆺ; ) HANGUL SYLLABLE KWAS
+CF84;CF84;110F 116A 11BB;CF84;110F 116A 11BB; # (쾄; 쾄; á„ᅪᆻ; 쾄; á„ᅪᆻ; ) HANGUL SYLLABLE KWASS
+CF85;CF85;110F 116A 11BC;CF85;110F 116A 11BC; # (ì¾…; ì¾…; á„ᅪᆼ; ì¾…; á„ᅪᆼ; ) HANGUL SYLLABLE KWANG
+CF86;CF86;110F 116A 11BD;CF86;110F 116A 11BD; # (쾆; 쾆; á„ᅪᆽ; 쾆; á„ᅪᆽ; ) HANGUL SYLLABLE KWAJ
+CF87;CF87;110F 116A 11BE;CF87;110F 116A 11BE; # (쾇; 쾇; á„ᅪᆾ; 쾇; á„ᅪᆾ; ) HANGUL SYLLABLE KWAC
+CF88;CF88;110F 116A 11BF;CF88;110F 116A 11BF; # (쾈; 쾈; á„ᅪᆿ; 쾈; á„ᅪᆿ; ) HANGUL SYLLABLE KWAK
+CF89;CF89;110F 116A 11C0;CF89;110F 116A 11C0; # (쾉; 쾉; á„ᅪᇀ; 쾉; á„ᅪᇀ; ) HANGUL SYLLABLE KWAT
+CF8A;CF8A;110F 116A 11C1;CF8A;110F 116A 11C1; # (쾊; 쾊; á„á…ªá‡; 쾊; á„á…ªá‡; ) HANGUL SYLLABLE KWAP
+CF8B;CF8B;110F 116A 11C2;CF8B;110F 116A 11C2; # (쾋; 쾋; á„ᅪᇂ; 쾋; á„ᅪᇂ; ) HANGUL SYLLABLE KWAH
+CF8C;CF8C;110F 116B;CF8C;110F 116B; # (쾌; 쾌; á„á…«; 쾌; á„á…«; ) HANGUL SYLLABLE KWAE
+CF8D;CF8D;110F 116B 11A8;CF8D;110F 116B 11A8; # (ì¾; ì¾; á„ᅫᆨ; ì¾; á„ᅫᆨ; ) HANGUL SYLLABLE KWAEG
+CF8E;CF8E;110F 116B 11A9;CF8E;110F 116B 11A9; # (쾎; 쾎; á„ᅫᆩ; 쾎; á„ᅫᆩ; ) HANGUL SYLLABLE KWAEGG
+CF8F;CF8F;110F 116B 11AA;CF8F;110F 116B 11AA; # (ì¾; ì¾; á„ᅫᆪ; ì¾; á„ᅫᆪ; ) HANGUL SYLLABLE KWAEGS
+CF90;CF90;110F 116B 11AB;CF90;110F 116B 11AB; # (ì¾; ì¾; á„ᅫᆫ; ì¾; á„ᅫᆫ; ) HANGUL SYLLABLE KWAEN
+CF91;CF91;110F 116B 11AC;CF91;110F 116B 11AC; # (쾑; 쾑; á„ᅫᆬ; 쾑; á„ᅫᆬ; ) HANGUL SYLLABLE KWAENJ
+CF92;CF92;110F 116B 11AD;CF92;110F 116B 11AD; # (ì¾’; ì¾’; á„ᅫᆭ; ì¾’; á„ᅫᆭ; ) HANGUL SYLLABLE KWAENH
+CF93;CF93;110F 116B 11AE;CF93;110F 116B 11AE; # (쾓; 쾓; á„ᅫᆮ; 쾓; á„ᅫᆮ; ) HANGUL SYLLABLE KWAED
+CF94;CF94;110F 116B 11AF;CF94;110F 116B 11AF; # (ì¾”; ì¾”; á„ᅫᆯ; ì¾”; á„ᅫᆯ; ) HANGUL SYLLABLE KWAEL
+CF95;CF95;110F 116B 11B0;CF95;110F 116B 11B0; # (쾕; 쾕; á„ᅫᆰ; 쾕; á„ᅫᆰ; ) HANGUL SYLLABLE KWAELG
+CF96;CF96;110F 116B 11B1;CF96;110F 116B 11B1; # (ì¾–; ì¾–; á„ᅫᆱ; ì¾–; á„ᅫᆱ; ) HANGUL SYLLABLE KWAELM
+CF97;CF97;110F 116B 11B2;CF97;110F 116B 11B2; # (ì¾—; ì¾—; á„ᅫᆲ; ì¾—; á„ᅫᆲ; ) HANGUL SYLLABLE KWAELB
+CF98;CF98;110F 116B 11B3;CF98;110F 116B 11B3; # (쾘; 쾘; á„ᅫᆳ; 쾘; á„ᅫᆳ; ) HANGUL SYLLABLE KWAELS
+CF99;CF99;110F 116B 11B4;CF99;110F 116B 11B4; # (ì¾™; ì¾™; á„ᅫᆴ; ì¾™; á„ᅫᆴ; ) HANGUL SYLLABLE KWAELT
+CF9A;CF9A;110F 116B 11B5;CF9A;110F 116B 11B5; # (쾚; 쾚; á„ᅫᆵ; 쾚; á„ᅫᆵ; ) HANGUL SYLLABLE KWAELP
+CF9B;CF9B;110F 116B 11B6;CF9B;110F 116B 11B6; # (ì¾›; ì¾›; á„ᅫᆶ; ì¾›; á„ᅫᆶ; ) HANGUL SYLLABLE KWAELH
+CF9C;CF9C;110F 116B 11B7;CF9C;110F 116B 11B7; # (쾜; 쾜; á„ᅫᆷ; 쾜; á„ᅫᆷ; ) HANGUL SYLLABLE KWAEM
+CF9D;CF9D;110F 116B 11B8;CF9D;110F 116B 11B8; # (ì¾; ì¾; á„ᅫᆸ; ì¾; á„ᅫᆸ; ) HANGUL SYLLABLE KWAEB
+CF9E;CF9E;110F 116B 11B9;CF9E;110F 116B 11B9; # (쾞; 쾞; á„ᅫᆹ; 쾞; á„ᅫᆹ; ) HANGUL SYLLABLE KWAEBS
+CF9F;CF9F;110F 116B 11BA;CF9F;110F 116B 11BA; # (쾟; 쾟; á„ᅫᆺ; 쾟; á„ᅫᆺ; ) HANGUL SYLLABLE KWAES
+CFA0;CFA0;110F 116B 11BB;CFA0;110F 116B 11BB; # (ì¾ ; ì¾ ; á„ᅫᆻ; ì¾ ; á„ᅫᆻ; ) HANGUL SYLLABLE KWAESS
+CFA1;CFA1;110F 116B 11BC;CFA1;110F 116B 11BC; # (쾡; 쾡; á„ᅫᆼ; 쾡; á„ᅫᆼ; ) HANGUL SYLLABLE KWAENG
+CFA2;CFA2;110F 116B 11BD;CFA2;110F 116B 11BD; # (ì¾¢; ì¾¢; á„ᅫᆽ; ì¾¢; á„ᅫᆽ; ) HANGUL SYLLABLE KWAEJ
+CFA3;CFA3;110F 116B 11BE;CFA3;110F 116B 11BE; # (ì¾£; ì¾£; á„ᅫᆾ; ì¾£; á„ᅫᆾ; ) HANGUL SYLLABLE KWAEC
+CFA4;CFA4;110F 116B 11BF;CFA4;110F 116B 11BF; # (쾤; 쾤; á„ᅫᆿ; 쾤; á„ᅫᆿ; ) HANGUL SYLLABLE KWAEK
+CFA5;CFA5;110F 116B 11C0;CFA5;110F 116B 11C0; # (ì¾¥; ì¾¥; á„ᅫᇀ; ì¾¥; á„ᅫᇀ; ) HANGUL SYLLABLE KWAET
+CFA6;CFA6;110F 116B 11C1;CFA6;110F 116B 11C1; # (쾦; 쾦; á„á…«á‡; 쾦; á„á…«á‡; ) HANGUL SYLLABLE KWAEP
+CFA7;CFA7;110F 116B 11C2;CFA7;110F 116B 11C2; # (ì¾§; ì¾§; á„ᅫᇂ; ì¾§; á„ᅫᇂ; ) HANGUL SYLLABLE KWAEH
+CFA8;CFA8;110F 116C;CFA8;110F 116C; # (쾨; 쾨; á„á…¬; 쾨; á„á…¬; ) HANGUL SYLLABLE KOE
+CFA9;CFA9;110F 116C 11A8;CFA9;110F 116C 11A8; # (쾩; 쾩; á„ᅬᆨ; 쾩; á„ᅬᆨ; ) HANGUL SYLLABLE KOEG
+CFAA;CFAA;110F 116C 11A9;CFAA;110F 116C 11A9; # (쾪; 쾪; á„ᅬᆩ; 쾪; á„ᅬᆩ; ) HANGUL SYLLABLE KOEGG
+CFAB;CFAB;110F 116C 11AA;CFAB;110F 116C 11AA; # (쾫; 쾫; á„ᅬᆪ; 쾫; á„ᅬᆪ; ) HANGUL SYLLABLE KOEGS
+CFAC;CFAC;110F 116C 11AB;CFAC;110F 116C 11AB; # (쾬; 쾬; á„ᅬᆫ; 쾬; á„ᅬᆫ; ) HANGUL SYLLABLE KOEN
+CFAD;CFAD;110F 116C 11AC;CFAD;110F 116C 11AC; # (ì¾­; ì¾­; á„ᅬᆬ; ì¾­; á„ᅬᆬ; ) HANGUL SYLLABLE KOENJ
+CFAE;CFAE;110F 116C 11AD;CFAE;110F 116C 11AD; # (ì¾®; ì¾®; á„ᅬᆭ; ì¾®; á„ᅬᆭ; ) HANGUL SYLLABLE KOENH
+CFAF;CFAF;110F 116C 11AE;CFAF;110F 116C 11AE; # (쾯; 쾯; á„ᅬᆮ; 쾯; á„ᅬᆮ; ) HANGUL SYLLABLE KOED
+CFB0;CFB0;110F 116C 11AF;CFB0;110F 116C 11AF; # (ì¾°; ì¾°; á„ᅬᆯ; ì¾°; á„ᅬᆯ; ) HANGUL SYLLABLE KOEL
+CFB1;CFB1;110F 116C 11B0;CFB1;110F 116C 11B0; # (ì¾±; ì¾±; á„ᅬᆰ; ì¾±; á„ᅬᆰ; ) HANGUL SYLLABLE KOELG
+CFB2;CFB2;110F 116C 11B1;CFB2;110F 116C 11B1; # (ì¾²; ì¾²; á„ᅬᆱ; ì¾²; á„ᅬᆱ; ) HANGUL SYLLABLE KOELM
+CFB3;CFB3;110F 116C 11B2;CFB3;110F 116C 11B2; # (ì¾³; ì¾³; á„ᅬᆲ; ì¾³; á„ᅬᆲ; ) HANGUL SYLLABLE KOELB
+CFB4;CFB4;110F 116C 11B3;CFB4;110F 116C 11B3; # (ì¾´; ì¾´; á„ᅬᆳ; ì¾´; á„ᅬᆳ; ) HANGUL SYLLABLE KOELS
+CFB5;CFB5;110F 116C 11B4;CFB5;110F 116C 11B4; # (ì¾µ; ì¾µ; á„ᅬᆴ; ì¾µ; á„ᅬᆴ; ) HANGUL SYLLABLE KOELT
+CFB6;CFB6;110F 116C 11B5;CFB6;110F 116C 11B5; # (ì¾¶; ì¾¶; á„ᅬᆵ; ì¾¶; á„ᅬᆵ; ) HANGUL SYLLABLE KOELP
+CFB7;CFB7;110F 116C 11B6;CFB7;110F 116C 11B6; # (ì¾·; ì¾·; á„ᅬᆶ; ì¾·; á„ᅬᆶ; ) HANGUL SYLLABLE KOELH
+CFB8;CFB8;110F 116C 11B7;CFB8;110F 116C 11B7; # (쾸; 쾸; á„ᅬᆷ; 쾸; á„ᅬᆷ; ) HANGUL SYLLABLE KOEM
+CFB9;CFB9;110F 116C 11B8;CFB9;110F 116C 11B8; # (ì¾¹; ì¾¹; á„ᅬᆸ; ì¾¹; á„ᅬᆸ; ) HANGUL SYLLABLE KOEB
+CFBA;CFBA;110F 116C 11B9;CFBA;110F 116C 11B9; # (쾺; 쾺; á„ᅬᆹ; 쾺; á„ᅬᆹ; ) HANGUL SYLLABLE KOEBS
+CFBB;CFBB;110F 116C 11BA;CFBB;110F 116C 11BA; # (ì¾»; ì¾»; á„ᅬᆺ; ì¾»; á„ᅬᆺ; ) HANGUL SYLLABLE KOES
+CFBC;CFBC;110F 116C 11BB;CFBC;110F 116C 11BB; # (ì¾¼; ì¾¼; á„ᅬᆻ; ì¾¼; á„ᅬᆻ; ) HANGUL SYLLABLE KOESS
+CFBD;CFBD;110F 116C 11BC;CFBD;110F 116C 11BC; # (ì¾½; ì¾½; á„ᅬᆼ; ì¾½; á„ᅬᆼ; ) HANGUL SYLLABLE KOENG
+CFBE;CFBE;110F 116C 11BD;CFBE;110F 116C 11BD; # (ì¾¾; ì¾¾; á„ᅬᆽ; ì¾¾; á„ᅬᆽ; ) HANGUL SYLLABLE KOEJ
+CFBF;CFBF;110F 116C 11BE;CFBF;110F 116C 11BE; # (쾿; 쾿; á„ᅬᆾ; 쾿; á„ᅬᆾ; ) HANGUL SYLLABLE KOEC
+CFC0;CFC0;110F 116C 11BF;CFC0;110F 116C 11BF; # (ì¿€; ì¿€; á„ᅬᆿ; ì¿€; á„ᅬᆿ; ) HANGUL SYLLABLE KOEK
+CFC1;CFC1;110F 116C 11C0;CFC1;110F 116C 11C0; # (ì¿; ì¿; á„ᅬᇀ; ì¿; á„ᅬᇀ; ) HANGUL SYLLABLE KOET
+CFC2;CFC2;110F 116C 11C1;CFC2;110F 116C 11C1; # (ì¿‚; ì¿‚; á„á…¬á‡; ì¿‚; á„á…¬á‡; ) HANGUL SYLLABLE KOEP
+CFC3;CFC3;110F 116C 11C2;CFC3;110F 116C 11C2; # (쿃; 쿃; á„ᅬᇂ; 쿃; á„ᅬᇂ; ) HANGUL SYLLABLE KOEH
+CFC4;CFC4;110F 116D;CFC4;110F 116D; # (ì¿„; ì¿„; á„á…­; ì¿„; á„á…­; ) HANGUL SYLLABLE KYO
+CFC5;CFC5;110F 116D 11A8;CFC5;110F 116D 11A8; # (ì¿…; ì¿…; á„ᅭᆨ; ì¿…; á„ᅭᆨ; ) HANGUL SYLLABLE KYOG
+CFC6;CFC6;110F 116D 11A9;CFC6;110F 116D 11A9; # (쿆; 쿆; á„ᅭᆩ; 쿆; á„ᅭᆩ; ) HANGUL SYLLABLE KYOGG
+CFC7;CFC7;110F 116D 11AA;CFC7;110F 116D 11AA; # (쿇; 쿇; á„ᅭᆪ; 쿇; á„ᅭᆪ; ) HANGUL SYLLABLE KYOGS
+CFC8;CFC8;110F 116D 11AB;CFC8;110F 116D 11AB; # (쿈; 쿈; á„ᅭᆫ; 쿈; á„ᅭᆫ; ) HANGUL SYLLABLE KYON
+CFC9;CFC9;110F 116D 11AC;CFC9;110F 116D 11AC; # (쿉; 쿉; á„ᅭᆬ; 쿉; á„ᅭᆬ; ) HANGUL SYLLABLE KYONJ
+CFCA;CFCA;110F 116D 11AD;CFCA;110F 116D 11AD; # (쿊; 쿊; á„ᅭᆭ; 쿊; á„ᅭᆭ; ) HANGUL SYLLABLE KYONH
+CFCB;CFCB;110F 116D 11AE;CFCB;110F 116D 11AE; # (ì¿‹; ì¿‹; á„ᅭᆮ; ì¿‹; á„ᅭᆮ; ) HANGUL SYLLABLE KYOD
+CFCC;CFCC;110F 116D 11AF;CFCC;110F 116D 11AF; # (쿌; 쿌; á„ᅭᆯ; 쿌; á„ᅭᆯ; ) HANGUL SYLLABLE KYOL
+CFCD;CFCD;110F 116D 11B0;CFCD;110F 116D 11B0; # (ì¿; ì¿; á„ᅭᆰ; ì¿; á„ᅭᆰ; ) HANGUL SYLLABLE KYOLG
+CFCE;CFCE;110F 116D 11B1;CFCE;110F 116D 11B1; # (쿎; 쿎; á„ᅭᆱ; 쿎; á„ᅭᆱ; ) HANGUL SYLLABLE KYOLM
+CFCF;CFCF;110F 116D 11B2;CFCF;110F 116D 11B2; # (ì¿; ì¿; á„ᅭᆲ; ì¿; á„ᅭᆲ; ) HANGUL SYLLABLE KYOLB
+CFD0;CFD0;110F 116D 11B3;CFD0;110F 116D 11B3; # (ì¿; ì¿; á„ᅭᆳ; ì¿; á„ᅭᆳ; ) HANGUL SYLLABLE KYOLS
+CFD1;CFD1;110F 116D 11B4;CFD1;110F 116D 11B4; # (ì¿‘; ì¿‘; á„ᅭᆴ; ì¿‘; á„ᅭᆴ; ) HANGUL SYLLABLE KYOLT
+CFD2;CFD2;110F 116D 11B5;CFD2;110F 116D 11B5; # (ì¿’; ì¿’; á„ᅭᆵ; ì¿’; á„ᅭᆵ; ) HANGUL SYLLABLE KYOLP
+CFD3;CFD3;110F 116D 11B6;CFD3;110F 116D 11B6; # (ì¿“; ì¿“; á„ᅭᆶ; ì¿“; á„ᅭᆶ; ) HANGUL SYLLABLE KYOLH
+CFD4;CFD4;110F 116D 11B7;CFD4;110F 116D 11B7; # (ì¿”; ì¿”; á„ᅭᆷ; ì¿”; á„ᅭᆷ; ) HANGUL SYLLABLE KYOM
+CFD5;CFD5;110F 116D 11B8;CFD5;110F 116D 11B8; # (ì¿•; ì¿•; á„ᅭᆸ; ì¿•; á„ᅭᆸ; ) HANGUL SYLLABLE KYOB
+CFD6;CFD6;110F 116D 11B9;CFD6;110F 116D 11B9; # (ì¿–; ì¿–; á„ᅭᆹ; ì¿–; á„ᅭᆹ; ) HANGUL SYLLABLE KYOBS
+CFD7;CFD7;110F 116D 11BA;CFD7;110F 116D 11BA; # (ì¿—; ì¿—; á„ᅭᆺ; ì¿—; á„ᅭᆺ; ) HANGUL SYLLABLE KYOS
+CFD8;CFD8;110F 116D 11BB;CFD8;110F 116D 11BB; # (쿘; 쿘; á„ᅭᆻ; 쿘; á„ᅭᆻ; ) HANGUL SYLLABLE KYOSS
+CFD9;CFD9;110F 116D 11BC;CFD9;110F 116D 11BC; # (ì¿™; ì¿™; á„ᅭᆼ; ì¿™; á„ᅭᆼ; ) HANGUL SYLLABLE KYONG
+CFDA;CFDA;110F 116D 11BD;CFDA;110F 116D 11BD; # (쿚; 쿚; á„ᅭᆽ; 쿚; á„ᅭᆽ; ) HANGUL SYLLABLE KYOJ
+CFDB;CFDB;110F 116D 11BE;CFDB;110F 116D 11BE; # (ì¿›; ì¿›; á„ᅭᆾ; ì¿›; á„ᅭᆾ; ) HANGUL SYLLABLE KYOC
+CFDC;CFDC;110F 116D 11BF;CFDC;110F 116D 11BF; # (쿜; 쿜; á„ᅭᆿ; 쿜; á„ᅭᆿ; ) HANGUL SYLLABLE KYOK
+CFDD;CFDD;110F 116D 11C0;CFDD;110F 116D 11C0; # (ì¿; ì¿; á„ᅭᇀ; ì¿; á„ᅭᇀ; ) HANGUL SYLLABLE KYOT
+CFDE;CFDE;110F 116D 11C1;CFDE;110F 116D 11C1; # (쿞; 쿞; á„á…­á‡; 쿞; á„á…­á‡; ) HANGUL SYLLABLE KYOP
+CFDF;CFDF;110F 116D 11C2;CFDF;110F 116D 11C2; # (쿟; 쿟; á„ᅭᇂ; 쿟; á„ᅭᇂ; ) HANGUL SYLLABLE KYOH
+CFE0;CFE0;110F 116E;CFE0;110F 116E; # (ì¿ ; ì¿ ; á„á…®; ì¿ ; á„á…®; ) HANGUL SYLLABLE KU
+CFE1;CFE1;110F 116E 11A8;CFE1;110F 116E 11A8; # (ì¿¡; ì¿¡; á„ᅮᆨ; ì¿¡; á„ᅮᆨ; ) HANGUL SYLLABLE KUG
+CFE2;CFE2;110F 116E 11A9;CFE2;110F 116E 11A9; # (ì¿¢; ì¿¢; á„ᅮᆩ; ì¿¢; á„ᅮᆩ; ) HANGUL SYLLABLE KUGG
+CFE3;CFE3;110F 116E 11AA;CFE3;110F 116E 11AA; # (ì¿£; ì¿£; á„ᅮᆪ; ì¿£; á„ᅮᆪ; ) HANGUL SYLLABLE KUGS
+CFE4;CFE4;110F 116E 11AB;CFE4;110F 116E 11AB; # (쿤; 쿤; á„ᅮᆫ; 쿤; á„ᅮᆫ; ) HANGUL SYLLABLE KUN
+CFE5;CFE5;110F 116E 11AC;CFE5;110F 116E 11AC; # (ì¿¥; ì¿¥; á„ᅮᆬ; ì¿¥; á„ᅮᆬ; ) HANGUL SYLLABLE KUNJ
+CFE6;CFE6;110F 116E 11AD;CFE6;110F 116E 11AD; # (쿦; 쿦; á„ᅮᆭ; 쿦; á„ᅮᆭ; ) HANGUL SYLLABLE KUNH
+CFE7;CFE7;110F 116E 11AE;CFE7;110F 116E 11AE; # (ì¿§; ì¿§; á„ᅮᆮ; ì¿§; á„ᅮᆮ; ) HANGUL SYLLABLE KUD
+CFE8;CFE8;110F 116E 11AF;CFE8;110F 116E 11AF; # (쿨; 쿨; á„ᅮᆯ; 쿨; á„ᅮᆯ; ) HANGUL SYLLABLE KUL
+CFE9;CFE9;110F 116E 11B0;CFE9;110F 116E 11B0; # (ì¿©; ì¿©; á„ᅮᆰ; ì¿©; á„ᅮᆰ; ) HANGUL SYLLABLE KULG
+CFEA;CFEA;110F 116E 11B1;CFEA;110F 116E 11B1; # (쿪; 쿪; á„ᅮᆱ; 쿪; á„ᅮᆱ; ) HANGUL SYLLABLE KULM
+CFEB;CFEB;110F 116E 11B2;CFEB;110F 116E 11B2; # (ì¿«; ì¿«; á„ᅮᆲ; ì¿«; á„ᅮᆲ; ) HANGUL SYLLABLE KULB
+CFEC;CFEC;110F 116E 11B3;CFEC;110F 116E 11B3; # (쿬; 쿬; á„ᅮᆳ; 쿬; á„ᅮᆳ; ) HANGUL SYLLABLE KULS
+CFED;CFED;110F 116E 11B4;CFED;110F 116E 11B4; # (ì¿­; ì¿­; á„ᅮᆴ; ì¿­; á„ᅮᆴ; ) HANGUL SYLLABLE KULT
+CFEE;CFEE;110F 116E 11B5;CFEE;110F 116E 11B5; # (ì¿®; ì¿®; á„ᅮᆵ; ì¿®; á„ᅮᆵ; ) HANGUL SYLLABLE KULP
+CFEF;CFEF;110F 116E 11B6;CFEF;110F 116E 11B6; # (쿯; 쿯; á„ᅮᆶ; 쿯; á„ᅮᆶ; ) HANGUL SYLLABLE KULH
+CFF0;CFF0;110F 116E 11B7;CFF0;110F 116E 11B7; # (ì¿°; ì¿°; á„ᅮᆷ; ì¿°; á„ᅮᆷ; ) HANGUL SYLLABLE KUM
+CFF1;CFF1;110F 116E 11B8;CFF1;110F 116E 11B8; # (쿱; 쿱; á„ᅮᆸ; 쿱; á„ᅮᆸ; ) HANGUL SYLLABLE KUB
+CFF2;CFF2;110F 116E 11B9;CFF2;110F 116E 11B9; # (쿲; 쿲; á„ᅮᆹ; 쿲; á„ᅮᆹ; ) HANGUL SYLLABLE KUBS
+CFF3;CFF3;110F 116E 11BA;CFF3;110F 116E 11BA; # (쿳; 쿳; á„ᅮᆺ; 쿳; á„ᅮᆺ; ) HANGUL SYLLABLE KUS
+CFF4;CFF4;110F 116E 11BB;CFF4;110F 116E 11BB; # (ì¿´; ì¿´; á„ᅮᆻ; ì¿´; á„ᅮᆻ; ) HANGUL SYLLABLE KUSS
+CFF5;CFF5;110F 116E 11BC;CFF5;110F 116E 11BC; # (쿵; 쿵; á„ᅮᆼ; 쿵; á„ᅮᆼ; ) HANGUL SYLLABLE KUNG
+CFF6;CFF6;110F 116E 11BD;CFF6;110F 116E 11BD; # (ì¿¶; ì¿¶; á„ᅮᆽ; ì¿¶; á„ᅮᆽ; ) HANGUL SYLLABLE KUJ
+CFF7;CFF7;110F 116E 11BE;CFF7;110F 116E 11BE; # (ì¿·; ì¿·; á„ᅮᆾ; ì¿·; á„ᅮᆾ; ) HANGUL SYLLABLE KUC
+CFF8;CFF8;110F 116E 11BF;CFF8;110F 116E 11BF; # (쿸; 쿸; á„ᅮᆿ; 쿸; á„ᅮᆿ; ) HANGUL SYLLABLE KUK
+CFF9;CFF9;110F 116E 11C0;CFF9;110F 116E 11C0; # (쿹; 쿹; á„ᅮᇀ; 쿹; á„ᅮᇀ; ) HANGUL SYLLABLE KUT
+CFFA;CFFA;110F 116E 11C1;CFFA;110F 116E 11C1; # (쿺; 쿺; á„á…®á‡; 쿺; á„á…®á‡; ) HANGUL SYLLABLE KUP
+CFFB;CFFB;110F 116E 11C2;CFFB;110F 116E 11C2; # (ì¿»; ì¿»; á„ᅮᇂ; ì¿»; á„ᅮᇂ; ) HANGUL SYLLABLE KUH
+CFFC;CFFC;110F 116F;CFFC;110F 116F; # (쿼; 쿼; á„á…¯; 쿼; á„á…¯; ) HANGUL SYLLABLE KWEO
+CFFD;CFFD;110F 116F 11A8;CFFD;110F 116F 11A8; # (쿽; 쿽; á„ᅯᆨ; 쿽; á„ᅯᆨ; ) HANGUL SYLLABLE KWEOG
+CFFE;CFFE;110F 116F 11A9;CFFE;110F 116F 11A9; # (쿾; 쿾; á„ᅯᆩ; 쿾; á„ᅯᆩ; ) HANGUL SYLLABLE KWEOGG
+CFFF;CFFF;110F 116F 11AA;CFFF;110F 116F 11AA; # (ì¿¿; ì¿¿; á„ᅯᆪ; ì¿¿; á„ᅯᆪ; ) HANGUL SYLLABLE KWEOGS
+D000;D000;110F 116F 11AB;D000;110F 116F 11AB; # (퀀; 퀀; á„ᅯᆫ; 퀀; á„ᅯᆫ; ) HANGUL SYLLABLE KWEON
+D001;D001;110F 116F 11AC;D001;110F 116F 11AC; # (í€; í€; á„ᅯᆬ; í€; á„ᅯᆬ; ) HANGUL SYLLABLE KWEONJ
+D002;D002;110F 116F 11AD;D002;110F 116F 11AD; # (퀂; 퀂; á„ᅯᆭ; 퀂; á„ᅯᆭ; ) HANGUL SYLLABLE KWEONH
+D003;D003;110F 116F 11AE;D003;110F 116F 11AE; # (퀃; 퀃; á„ᅯᆮ; 퀃; á„ᅯᆮ; ) HANGUL SYLLABLE KWEOD
+D004;D004;110F 116F 11AF;D004;110F 116F 11AF; # (퀄; 퀄; á„ᅯᆯ; 퀄; á„ᅯᆯ; ) HANGUL SYLLABLE KWEOL
+D005;D005;110F 116F 11B0;D005;110F 116F 11B0; # (퀅; 퀅; á„ᅯᆰ; 퀅; á„ᅯᆰ; ) HANGUL SYLLABLE KWEOLG
+D006;D006;110F 116F 11B1;D006;110F 116F 11B1; # (퀆; 퀆; á„ᅯᆱ; 퀆; á„ᅯᆱ; ) HANGUL SYLLABLE KWEOLM
+D007;D007;110F 116F 11B2;D007;110F 116F 11B2; # (퀇; 퀇; á„ᅯᆲ; 퀇; á„ᅯᆲ; ) HANGUL SYLLABLE KWEOLB
+D008;D008;110F 116F 11B3;D008;110F 116F 11B3; # (퀈; 퀈; á„ᅯᆳ; 퀈; á„ᅯᆳ; ) HANGUL SYLLABLE KWEOLS
+D009;D009;110F 116F 11B4;D009;110F 116F 11B4; # (퀉; 퀉; á„ᅯᆴ; 퀉; á„ᅯᆴ; ) HANGUL SYLLABLE KWEOLT
+D00A;D00A;110F 116F 11B5;D00A;110F 116F 11B5; # (퀊; 퀊; á„ᅯᆵ; 퀊; á„ᅯᆵ; ) HANGUL SYLLABLE KWEOLP
+D00B;D00B;110F 116F 11B6;D00B;110F 116F 11B6; # (퀋; 퀋; á„ᅯᆶ; 퀋; á„ᅯᆶ; ) HANGUL SYLLABLE KWEOLH
+D00C;D00C;110F 116F 11B7;D00C;110F 116F 11B7; # (퀌; 퀌; á„ᅯᆷ; 퀌; á„ᅯᆷ; ) HANGUL SYLLABLE KWEOM
+D00D;D00D;110F 116F 11B8;D00D;110F 116F 11B8; # (í€; í€; á„ᅯᆸ; í€; á„ᅯᆸ; ) HANGUL SYLLABLE KWEOB
+D00E;D00E;110F 116F 11B9;D00E;110F 116F 11B9; # (퀎; 퀎; á„ᅯᆹ; 퀎; á„ᅯᆹ; ) HANGUL SYLLABLE KWEOBS
+D00F;D00F;110F 116F 11BA;D00F;110F 116F 11BA; # (í€; í€; á„ᅯᆺ; í€; á„ᅯᆺ; ) HANGUL SYLLABLE KWEOS
+D010;D010;110F 116F 11BB;D010;110F 116F 11BB; # (í€; í€; á„ᅯᆻ; í€; á„ᅯᆻ; ) HANGUL SYLLABLE KWEOSS
+D011;D011;110F 116F 11BC;D011;110F 116F 11BC; # (퀑; 퀑; á„ᅯᆼ; 퀑; á„ᅯᆼ; ) HANGUL SYLLABLE KWEONG
+D012;D012;110F 116F 11BD;D012;110F 116F 11BD; # (퀒; 퀒; á„ᅯᆽ; 퀒; á„ᅯᆽ; ) HANGUL SYLLABLE KWEOJ
+D013;D013;110F 116F 11BE;D013;110F 116F 11BE; # (퀓; 퀓; á„ᅯᆾ; 퀓; á„ᅯᆾ; ) HANGUL SYLLABLE KWEOC
+D014;D014;110F 116F 11BF;D014;110F 116F 11BF; # (퀔; 퀔; á„ᅯᆿ; 퀔; á„ᅯᆿ; ) HANGUL SYLLABLE KWEOK
+D015;D015;110F 116F 11C0;D015;110F 116F 11C0; # (퀕; 퀕; á„ᅯᇀ; 퀕; á„ᅯᇀ; ) HANGUL SYLLABLE KWEOT
+D016;D016;110F 116F 11C1;D016;110F 116F 11C1; # (퀖; 퀖; á„á…¯á‡; 퀖; á„á…¯á‡; ) HANGUL SYLLABLE KWEOP
+D017;D017;110F 116F 11C2;D017;110F 116F 11C2; # (퀗; 퀗; á„ᅯᇂ; 퀗; á„ᅯᇂ; ) HANGUL SYLLABLE KWEOH
+D018;D018;110F 1170;D018;110F 1170; # (퀘; 퀘; á„á…°; 퀘; á„á…°; ) HANGUL SYLLABLE KWE
+D019;D019;110F 1170 11A8;D019;110F 1170 11A8; # (퀙; 퀙; á„ᅰᆨ; 퀙; á„ᅰᆨ; ) HANGUL SYLLABLE KWEG
+D01A;D01A;110F 1170 11A9;D01A;110F 1170 11A9; # (퀚; 퀚; á„ᅰᆩ; 퀚; á„ᅰᆩ; ) HANGUL SYLLABLE KWEGG
+D01B;D01B;110F 1170 11AA;D01B;110F 1170 11AA; # (퀛; 퀛; á„ᅰᆪ; 퀛; á„ᅰᆪ; ) HANGUL SYLLABLE KWEGS
+D01C;D01C;110F 1170 11AB;D01C;110F 1170 11AB; # (퀜; 퀜; á„ᅰᆫ; 퀜; á„ᅰᆫ; ) HANGUL SYLLABLE KWEN
+D01D;D01D;110F 1170 11AC;D01D;110F 1170 11AC; # (í€; í€; á„ᅰᆬ; í€; á„ᅰᆬ; ) HANGUL SYLLABLE KWENJ
+D01E;D01E;110F 1170 11AD;D01E;110F 1170 11AD; # (퀞; 퀞; á„ᅰᆭ; 퀞; á„ᅰᆭ; ) HANGUL SYLLABLE KWENH
+D01F;D01F;110F 1170 11AE;D01F;110F 1170 11AE; # (퀟; 퀟; á„ᅰᆮ; 퀟; á„ᅰᆮ; ) HANGUL SYLLABLE KWED
+D020;D020;110F 1170 11AF;D020;110F 1170 11AF; # (퀠; 퀠; á„ᅰᆯ; 퀠; á„ᅰᆯ; ) HANGUL SYLLABLE KWEL
+D021;D021;110F 1170 11B0;D021;110F 1170 11B0; # (퀡; 퀡; á„ᅰᆰ; 퀡; á„ᅰᆰ; ) HANGUL SYLLABLE KWELG
+D022;D022;110F 1170 11B1;D022;110F 1170 11B1; # (퀢; 퀢; á„ᅰᆱ; 퀢; á„ᅰᆱ; ) HANGUL SYLLABLE KWELM
+D023;D023;110F 1170 11B2;D023;110F 1170 11B2; # (퀣; 퀣; á„ᅰᆲ; 퀣; á„ᅰᆲ; ) HANGUL SYLLABLE KWELB
+D024;D024;110F 1170 11B3;D024;110F 1170 11B3; # (퀤; 퀤; á„ᅰᆳ; 퀤; á„ᅰᆳ; ) HANGUL SYLLABLE KWELS
+D025;D025;110F 1170 11B4;D025;110F 1170 11B4; # (퀥; 퀥; á„ᅰᆴ; 퀥; á„ᅰᆴ; ) HANGUL SYLLABLE KWELT
+D026;D026;110F 1170 11B5;D026;110F 1170 11B5; # (퀦; 퀦; á„ᅰᆵ; 퀦; á„ᅰᆵ; ) HANGUL SYLLABLE KWELP
+D027;D027;110F 1170 11B6;D027;110F 1170 11B6; # (퀧; 퀧; á„ᅰᆶ; 퀧; á„ᅰᆶ; ) HANGUL SYLLABLE KWELH
+D028;D028;110F 1170 11B7;D028;110F 1170 11B7; # (퀨; 퀨; á„ᅰᆷ; 퀨; á„ᅰᆷ; ) HANGUL SYLLABLE KWEM
+D029;D029;110F 1170 11B8;D029;110F 1170 11B8; # (퀩; 퀩; á„ᅰᆸ; 퀩; á„ᅰᆸ; ) HANGUL SYLLABLE KWEB
+D02A;D02A;110F 1170 11B9;D02A;110F 1170 11B9; # (퀪; 퀪; á„ᅰᆹ; 퀪; á„ᅰᆹ; ) HANGUL SYLLABLE KWEBS
+D02B;D02B;110F 1170 11BA;D02B;110F 1170 11BA; # (퀫; 퀫; á„ᅰᆺ; 퀫; á„ᅰᆺ; ) HANGUL SYLLABLE KWES
+D02C;D02C;110F 1170 11BB;D02C;110F 1170 11BB; # (퀬; 퀬; á„ᅰᆻ; 퀬; á„ᅰᆻ; ) HANGUL SYLLABLE KWESS
+D02D;D02D;110F 1170 11BC;D02D;110F 1170 11BC; # (퀭; 퀭; á„ᅰᆼ; 퀭; á„ᅰᆼ; ) HANGUL SYLLABLE KWENG
+D02E;D02E;110F 1170 11BD;D02E;110F 1170 11BD; # (퀮; 퀮; á„ᅰᆽ; 퀮; á„ᅰᆽ; ) HANGUL SYLLABLE KWEJ
+D02F;D02F;110F 1170 11BE;D02F;110F 1170 11BE; # (퀯; 퀯; á„ᅰᆾ; 퀯; á„ᅰᆾ; ) HANGUL SYLLABLE KWEC
+D030;D030;110F 1170 11BF;D030;110F 1170 11BF; # (퀰; 퀰; á„ᅰᆿ; 퀰; á„ᅰᆿ; ) HANGUL SYLLABLE KWEK
+D031;D031;110F 1170 11C0;D031;110F 1170 11C0; # (퀱; 퀱; á„ᅰᇀ; 퀱; á„ᅰᇀ; ) HANGUL SYLLABLE KWET
+D032;D032;110F 1170 11C1;D032;110F 1170 11C1; # (퀲; 퀲; á„á…°á‡; 퀲; á„á…°á‡; ) HANGUL SYLLABLE KWEP
+D033;D033;110F 1170 11C2;D033;110F 1170 11C2; # (퀳; 퀳; á„ᅰᇂ; 퀳; á„ᅰᇂ; ) HANGUL SYLLABLE KWEH
+D034;D034;110F 1171;D034;110F 1171; # (퀴; 퀴; á„á…±; 퀴; á„á…±; ) HANGUL SYLLABLE KWI
+D035;D035;110F 1171 11A8;D035;110F 1171 11A8; # (퀵; 퀵; á„ᅱᆨ; 퀵; á„ᅱᆨ; ) HANGUL SYLLABLE KWIG
+D036;D036;110F 1171 11A9;D036;110F 1171 11A9; # (퀶; 퀶; á„ᅱᆩ; 퀶; á„ᅱᆩ; ) HANGUL SYLLABLE KWIGG
+D037;D037;110F 1171 11AA;D037;110F 1171 11AA; # (퀷; 퀷; á„ᅱᆪ; 퀷; á„ᅱᆪ; ) HANGUL SYLLABLE KWIGS
+D038;D038;110F 1171 11AB;D038;110F 1171 11AB; # (퀸; 퀸; á„ᅱᆫ; 퀸; á„ᅱᆫ; ) HANGUL SYLLABLE KWIN
+D039;D039;110F 1171 11AC;D039;110F 1171 11AC; # (퀹; 퀹; á„ᅱᆬ; 퀹; á„ᅱᆬ; ) HANGUL SYLLABLE KWINJ
+D03A;D03A;110F 1171 11AD;D03A;110F 1171 11AD; # (퀺; 퀺; á„ᅱᆭ; 퀺; á„ᅱᆭ; ) HANGUL SYLLABLE KWINH
+D03B;D03B;110F 1171 11AE;D03B;110F 1171 11AE; # (퀻; 퀻; á„ᅱᆮ; 퀻; á„ᅱᆮ; ) HANGUL SYLLABLE KWID
+D03C;D03C;110F 1171 11AF;D03C;110F 1171 11AF; # (퀼; 퀼; á„ᅱᆯ; 퀼; á„ᅱᆯ; ) HANGUL SYLLABLE KWIL
+D03D;D03D;110F 1171 11B0;D03D;110F 1171 11B0; # (퀽; 퀽; á„ᅱᆰ; 퀽; á„ᅱᆰ; ) HANGUL SYLLABLE KWILG
+D03E;D03E;110F 1171 11B1;D03E;110F 1171 11B1; # (퀾; 퀾; á„ᅱᆱ; 퀾; á„ᅱᆱ; ) HANGUL SYLLABLE KWILM
+D03F;D03F;110F 1171 11B2;D03F;110F 1171 11B2; # (퀿; 퀿; á„ᅱᆲ; 퀿; á„ᅱᆲ; ) HANGUL SYLLABLE KWILB
+D040;D040;110F 1171 11B3;D040;110F 1171 11B3; # (í€; í€; á„ᅱᆳ; í€; á„ᅱᆳ; ) HANGUL SYLLABLE KWILS
+D041;D041;110F 1171 11B4;D041;110F 1171 11B4; # (í; í; á„ᅱᆴ; í; á„ᅱᆴ; ) HANGUL SYLLABLE KWILT
+D042;D042;110F 1171 11B5;D042;110F 1171 11B5; # (í‚; í‚; á„ᅱᆵ; í‚; á„ᅱᆵ; ) HANGUL SYLLABLE KWILP
+D043;D043;110F 1171 11B6;D043;110F 1171 11B6; # (íƒ; íƒ; á„ᅱᆶ; íƒ; á„ᅱᆶ; ) HANGUL SYLLABLE KWILH
+D044;D044;110F 1171 11B7;D044;110F 1171 11B7; # (í„; í„; á„ᅱᆷ; í„; á„ᅱᆷ; ) HANGUL SYLLABLE KWIM
+D045;D045;110F 1171 11B8;D045;110F 1171 11B8; # (í…; í…; á„ᅱᆸ; í…; á„ᅱᆸ; ) HANGUL SYLLABLE KWIB
+D046;D046;110F 1171 11B9;D046;110F 1171 11B9; # (í†; í†; á„ᅱᆹ; í†; á„ᅱᆹ; ) HANGUL SYLLABLE KWIBS
+D047;D047;110F 1171 11BA;D047;110F 1171 11BA; # (í‡; í‡; á„ᅱᆺ; í‡; á„ᅱᆺ; ) HANGUL SYLLABLE KWIS
+D048;D048;110F 1171 11BB;D048;110F 1171 11BB; # (íˆ; íˆ; á„ᅱᆻ; íˆ; á„ᅱᆻ; ) HANGUL SYLLABLE KWISS
+D049;D049;110F 1171 11BC;D049;110F 1171 11BC; # (í‰; í‰; á„ᅱᆼ; í‰; á„ᅱᆼ; ) HANGUL SYLLABLE KWING
+D04A;D04A;110F 1171 11BD;D04A;110F 1171 11BD; # (íŠ; íŠ; á„ᅱᆽ; íŠ; á„ᅱᆽ; ) HANGUL SYLLABLE KWIJ
+D04B;D04B;110F 1171 11BE;D04B;110F 1171 11BE; # (í‹; í‹; á„ᅱᆾ; í‹; á„ᅱᆾ; ) HANGUL SYLLABLE KWIC
+D04C;D04C;110F 1171 11BF;D04C;110F 1171 11BF; # (íŒ; íŒ; á„ᅱᆿ; íŒ; á„ᅱᆿ; ) HANGUL SYLLABLE KWIK
+D04D;D04D;110F 1171 11C0;D04D;110F 1171 11C0; # (í; í; á„ᅱᇀ; í; á„ᅱᇀ; ) HANGUL SYLLABLE KWIT
+D04E;D04E;110F 1171 11C1;D04E;110F 1171 11C1; # (íŽ; íŽ; á„á…±á‡; íŽ; á„á…±á‡; ) HANGUL SYLLABLE KWIP
+D04F;D04F;110F 1171 11C2;D04F;110F 1171 11C2; # (í; í; á„ᅱᇂ; í; á„ᅱᇂ; ) HANGUL SYLLABLE KWIH
+D050;D050;110F 1172;D050;110F 1172; # (í; í; á„á…²; í; á„á…²; ) HANGUL SYLLABLE KYU
+D051;D051;110F 1172 11A8;D051;110F 1172 11A8; # (í‘; í‘; á„ᅲᆨ; í‘; á„ᅲᆨ; ) HANGUL SYLLABLE KYUG
+D052;D052;110F 1172 11A9;D052;110F 1172 11A9; # (í’; í’; á„ᅲᆩ; í’; á„ᅲᆩ; ) HANGUL SYLLABLE KYUGG
+D053;D053;110F 1172 11AA;D053;110F 1172 11AA; # (í“; í“; á„ᅲᆪ; í“; á„ᅲᆪ; ) HANGUL SYLLABLE KYUGS
+D054;D054;110F 1172 11AB;D054;110F 1172 11AB; # (í”; í”; á„ᅲᆫ; í”; á„ᅲᆫ; ) HANGUL SYLLABLE KYUN
+D055;D055;110F 1172 11AC;D055;110F 1172 11AC; # (í•; í•; á„ᅲᆬ; í•; á„ᅲᆬ; ) HANGUL SYLLABLE KYUNJ
+D056;D056;110F 1172 11AD;D056;110F 1172 11AD; # (í–; í–; á„ᅲᆭ; í–; á„ᅲᆭ; ) HANGUL SYLLABLE KYUNH
+D057;D057;110F 1172 11AE;D057;110F 1172 11AE; # (í—; í—; á„ᅲᆮ; í—; á„ᅲᆮ; ) HANGUL SYLLABLE KYUD
+D058;D058;110F 1172 11AF;D058;110F 1172 11AF; # (í˜; í˜; á„ᅲᆯ; í˜; á„ᅲᆯ; ) HANGUL SYLLABLE KYUL
+D059;D059;110F 1172 11B0;D059;110F 1172 11B0; # (í™; í™; á„ᅲᆰ; í™; á„ᅲᆰ; ) HANGUL SYLLABLE KYULG
+D05A;D05A;110F 1172 11B1;D05A;110F 1172 11B1; # (íš; íš; á„ᅲᆱ; íš; á„ᅲᆱ; ) HANGUL SYLLABLE KYULM
+D05B;D05B;110F 1172 11B2;D05B;110F 1172 11B2; # (í›; í›; á„ᅲᆲ; í›; á„ᅲᆲ; ) HANGUL SYLLABLE KYULB
+D05C;D05C;110F 1172 11B3;D05C;110F 1172 11B3; # (íœ; íœ; á„ᅲᆳ; íœ; á„ᅲᆳ; ) HANGUL SYLLABLE KYULS
+D05D;D05D;110F 1172 11B4;D05D;110F 1172 11B4; # (í; í; á„ᅲᆴ; í; á„ᅲᆴ; ) HANGUL SYLLABLE KYULT
+D05E;D05E;110F 1172 11B5;D05E;110F 1172 11B5; # (íž; íž; á„ᅲᆵ; íž; á„ᅲᆵ; ) HANGUL SYLLABLE KYULP
+D05F;D05F;110F 1172 11B6;D05F;110F 1172 11B6; # (íŸ; íŸ; á„ᅲᆶ; íŸ; á„ᅲᆶ; ) HANGUL SYLLABLE KYULH
+D060;D060;110F 1172 11B7;D060;110F 1172 11B7; # (í ; í ; á„ᅲᆷ; í ; á„ᅲᆷ; ) HANGUL SYLLABLE KYUM
+D061;D061;110F 1172 11B8;D061;110F 1172 11B8; # (í¡; í¡; á„ᅲᆸ; í¡; á„ᅲᆸ; ) HANGUL SYLLABLE KYUB
+D062;D062;110F 1172 11B9;D062;110F 1172 11B9; # (í¢; í¢; á„ᅲᆹ; í¢; á„ᅲᆹ; ) HANGUL SYLLABLE KYUBS
+D063;D063;110F 1172 11BA;D063;110F 1172 11BA; # (í£; í£; á„ᅲᆺ; í£; á„ᅲᆺ; ) HANGUL SYLLABLE KYUS
+D064;D064;110F 1172 11BB;D064;110F 1172 11BB; # (í¤; í¤; á„ᅲᆻ; í¤; á„ᅲᆻ; ) HANGUL SYLLABLE KYUSS
+D065;D065;110F 1172 11BC;D065;110F 1172 11BC; # (í¥; í¥; á„ᅲᆼ; í¥; á„ᅲᆼ; ) HANGUL SYLLABLE KYUNG
+D066;D066;110F 1172 11BD;D066;110F 1172 11BD; # (í¦; í¦; á„ᅲᆽ; í¦; á„ᅲᆽ; ) HANGUL SYLLABLE KYUJ
+D067;D067;110F 1172 11BE;D067;110F 1172 11BE; # (í§; í§; á„ᅲᆾ; í§; á„ᅲᆾ; ) HANGUL SYLLABLE KYUC
+D068;D068;110F 1172 11BF;D068;110F 1172 11BF; # (í¨; í¨; á„ᅲᆿ; í¨; á„ᅲᆿ; ) HANGUL SYLLABLE KYUK
+D069;D069;110F 1172 11C0;D069;110F 1172 11C0; # (í©; í©; á„ᅲᇀ; í©; á„ᅲᇀ; ) HANGUL SYLLABLE KYUT
+D06A;D06A;110F 1172 11C1;D06A;110F 1172 11C1; # (íª; íª; á„á…²á‡; íª; á„á…²á‡; ) HANGUL SYLLABLE KYUP
+D06B;D06B;110F 1172 11C2;D06B;110F 1172 11C2; # (í«; í«; á„ᅲᇂ; í«; á„ᅲᇂ; ) HANGUL SYLLABLE KYUH
+D06C;D06C;110F 1173;D06C;110F 1173; # (í¬; í¬; á„á…³; í¬; á„á…³; ) HANGUL SYLLABLE KEU
+D06D;D06D;110F 1173 11A8;D06D;110F 1173 11A8; # (í­; í­; á„ᅳᆨ; í­; á„ᅳᆨ; ) HANGUL SYLLABLE KEUG
+D06E;D06E;110F 1173 11A9;D06E;110F 1173 11A9; # (í®; í®; á„ᅳᆩ; í®; á„ᅳᆩ; ) HANGUL SYLLABLE KEUGG
+D06F;D06F;110F 1173 11AA;D06F;110F 1173 11AA; # (í¯; í¯; á„ᅳᆪ; í¯; á„ᅳᆪ; ) HANGUL SYLLABLE KEUGS
+D070;D070;110F 1173 11AB;D070;110F 1173 11AB; # (í°; í°; á„ᅳᆫ; í°; á„ᅳᆫ; ) HANGUL SYLLABLE KEUN
+D071;D071;110F 1173 11AC;D071;110F 1173 11AC; # (í±; í±; á„ᅳᆬ; í±; á„ᅳᆬ; ) HANGUL SYLLABLE KEUNJ
+D072;D072;110F 1173 11AD;D072;110F 1173 11AD; # (í²; í²; á„ᅳᆭ; í²; á„ᅳᆭ; ) HANGUL SYLLABLE KEUNH
+D073;D073;110F 1173 11AE;D073;110F 1173 11AE; # (í³; í³; á„ᅳᆮ; í³; á„ᅳᆮ; ) HANGUL SYLLABLE KEUD
+D074;D074;110F 1173 11AF;D074;110F 1173 11AF; # (í´; í´; á„ᅳᆯ; í´; á„ᅳᆯ; ) HANGUL SYLLABLE KEUL
+D075;D075;110F 1173 11B0;D075;110F 1173 11B0; # (íµ; íµ; á„ᅳᆰ; íµ; á„ᅳᆰ; ) HANGUL SYLLABLE KEULG
+D076;D076;110F 1173 11B1;D076;110F 1173 11B1; # (í¶; í¶; á„ᅳᆱ; í¶; á„ᅳᆱ; ) HANGUL SYLLABLE KEULM
+D077;D077;110F 1173 11B2;D077;110F 1173 11B2; # (í·; í·; á„ᅳᆲ; í·; á„ᅳᆲ; ) HANGUL SYLLABLE KEULB
+D078;D078;110F 1173 11B3;D078;110F 1173 11B3; # (í¸; í¸; á„ᅳᆳ; í¸; á„ᅳᆳ; ) HANGUL SYLLABLE KEULS
+D079;D079;110F 1173 11B4;D079;110F 1173 11B4; # (í¹; í¹; á„ᅳᆴ; í¹; á„ᅳᆴ; ) HANGUL SYLLABLE KEULT
+D07A;D07A;110F 1173 11B5;D07A;110F 1173 11B5; # (íº; íº; á„ᅳᆵ; íº; á„ᅳᆵ; ) HANGUL SYLLABLE KEULP
+D07B;D07B;110F 1173 11B6;D07B;110F 1173 11B6; # (í»; í»; á„ᅳᆶ; í»; á„ᅳᆶ; ) HANGUL SYLLABLE KEULH
+D07C;D07C;110F 1173 11B7;D07C;110F 1173 11B7; # (í¼; í¼; á„ᅳᆷ; í¼; á„ᅳᆷ; ) HANGUL SYLLABLE KEUM
+D07D;D07D;110F 1173 11B8;D07D;110F 1173 11B8; # (í½; í½; á„ᅳᆸ; í½; á„ᅳᆸ; ) HANGUL SYLLABLE KEUB
+D07E;D07E;110F 1173 11B9;D07E;110F 1173 11B9; # (í¾; í¾; á„ᅳᆹ; í¾; á„ᅳᆹ; ) HANGUL SYLLABLE KEUBS
+D07F;D07F;110F 1173 11BA;D07F;110F 1173 11BA; # (í¿; í¿; á„ᅳᆺ; í¿; á„ᅳᆺ; ) HANGUL SYLLABLE KEUS
+D080;D080;110F 1173 11BB;D080;110F 1173 11BB; # (í‚€; í‚€; á„ᅳᆻ; í‚€; á„ᅳᆻ; ) HANGUL SYLLABLE KEUSS
+D081;D081;110F 1173 11BC;D081;110F 1173 11BC; # (í‚; í‚; á„ᅳᆼ; í‚; á„ᅳᆼ; ) HANGUL SYLLABLE KEUNG
+D082;D082;110F 1173 11BD;D082;110F 1173 11BD; # (í‚‚; í‚‚; á„ᅳᆽ; í‚‚; á„ᅳᆽ; ) HANGUL SYLLABLE KEUJ
+D083;D083;110F 1173 11BE;D083;110F 1173 11BE; # (킃; 킃; á„ᅳᆾ; 킃; á„ᅳᆾ; ) HANGUL SYLLABLE KEUC
+D084;D084;110F 1173 11BF;D084;110F 1173 11BF; # (í‚„; í‚„; á„ᅳᆿ; í‚„; á„ᅳᆿ; ) HANGUL SYLLABLE KEUK
+D085;D085;110F 1173 11C0;D085;110F 1173 11C0; # (í‚…; í‚…; á„ᅳᇀ; í‚…; á„ᅳᇀ; ) HANGUL SYLLABLE KEUT
+D086;D086;110F 1173 11C1;D086;110F 1173 11C1; # (킆; 킆; á„á…³á‡; 킆; á„á…³á‡; ) HANGUL SYLLABLE KEUP
+D087;D087;110F 1173 11C2;D087;110F 1173 11C2; # (킇; 킇; á„ᅳᇂ; 킇; á„ᅳᇂ; ) HANGUL SYLLABLE KEUH
+D088;D088;110F 1174;D088;110F 1174; # (킈; 킈; á„á…´; 킈; á„á…´; ) HANGUL SYLLABLE KYI
+D089;D089;110F 1174 11A8;D089;110F 1174 11A8; # (킉; 킉; á„ᅴᆨ; 킉; á„ᅴᆨ; ) HANGUL SYLLABLE KYIG
+D08A;D08A;110F 1174 11A9;D08A;110F 1174 11A9; # (킊; 킊; á„ᅴᆩ; 킊; á„ᅴᆩ; ) HANGUL SYLLABLE KYIGG
+D08B;D08B;110F 1174 11AA;D08B;110F 1174 11AA; # (í‚‹; í‚‹; á„ᅴᆪ; í‚‹; á„ᅴᆪ; ) HANGUL SYLLABLE KYIGS
+D08C;D08C;110F 1174 11AB;D08C;110F 1174 11AB; # (킌; 킌; á„ᅴᆫ; 킌; á„ᅴᆫ; ) HANGUL SYLLABLE KYIN
+D08D;D08D;110F 1174 11AC;D08D;110F 1174 11AC; # (í‚; í‚; á„ᅴᆬ; í‚; á„ᅴᆬ; ) HANGUL SYLLABLE KYINJ
+D08E;D08E;110F 1174 11AD;D08E;110F 1174 11AD; # (킎; 킎; á„ᅴᆭ; 킎; á„ᅴᆭ; ) HANGUL SYLLABLE KYINH
+D08F;D08F;110F 1174 11AE;D08F;110F 1174 11AE; # (í‚; í‚; á„ᅴᆮ; í‚; á„ᅴᆮ; ) HANGUL SYLLABLE KYID
+D090;D090;110F 1174 11AF;D090;110F 1174 11AF; # (í‚; í‚; á„ᅴᆯ; í‚; á„ᅴᆯ; ) HANGUL SYLLABLE KYIL
+D091;D091;110F 1174 11B0;D091;110F 1174 11B0; # (í‚‘; í‚‘; á„ᅴᆰ; í‚‘; á„ᅴᆰ; ) HANGUL SYLLABLE KYILG
+D092;D092;110F 1174 11B1;D092;110F 1174 11B1; # (í‚’; í‚’; á„ᅴᆱ; í‚’; á„ᅴᆱ; ) HANGUL SYLLABLE KYILM
+D093;D093;110F 1174 11B2;D093;110F 1174 11B2; # (í‚“; í‚“; á„ᅴᆲ; í‚“; á„ᅴᆲ; ) HANGUL SYLLABLE KYILB
+D094;D094;110F 1174 11B3;D094;110F 1174 11B3; # (í‚”; í‚”; á„ᅴᆳ; í‚”; á„ᅴᆳ; ) HANGUL SYLLABLE KYILS
+D095;D095;110F 1174 11B4;D095;110F 1174 11B4; # (í‚•; í‚•; á„ᅴᆴ; í‚•; á„ᅴᆴ; ) HANGUL SYLLABLE KYILT
+D096;D096;110F 1174 11B5;D096;110F 1174 11B5; # (í‚–; í‚–; á„ᅴᆵ; í‚–; á„ᅴᆵ; ) HANGUL SYLLABLE KYILP
+D097;D097;110F 1174 11B6;D097;110F 1174 11B6; # (í‚—; í‚—; á„ᅴᆶ; í‚—; á„ᅴᆶ; ) HANGUL SYLLABLE KYILH
+D098;D098;110F 1174 11B7;D098;110F 1174 11B7; # (킘; 킘; á„ᅴᆷ; 킘; á„ᅴᆷ; ) HANGUL SYLLABLE KYIM
+D099;D099;110F 1174 11B8;D099;110F 1174 11B8; # (í‚™; í‚™; á„ᅴᆸ; í‚™; á„ᅴᆸ; ) HANGUL SYLLABLE KYIB
+D09A;D09A;110F 1174 11B9;D09A;110F 1174 11B9; # (킚; 킚; á„ᅴᆹ; 킚; á„ᅴᆹ; ) HANGUL SYLLABLE KYIBS
+D09B;D09B;110F 1174 11BA;D09B;110F 1174 11BA; # (í‚›; í‚›; á„ᅴᆺ; í‚›; á„ᅴᆺ; ) HANGUL SYLLABLE KYIS
+D09C;D09C;110F 1174 11BB;D09C;110F 1174 11BB; # (킜; 킜; á„ᅴᆻ; 킜; á„ᅴᆻ; ) HANGUL SYLLABLE KYISS
+D09D;D09D;110F 1174 11BC;D09D;110F 1174 11BC; # (í‚; í‚; á„ᅴᆼ; í‚; á„ᅴᆼ; ) HANGUL SYLLABLE KYING
+D09E;D09E;110F 1174 11BD;D09E;110F 1174 11BD; # (킞; 킞; á„ᅴᆽ; 킞; á„ᅴᆽ; ) HANGUL SYLLABLE KYIJ
+D09F;D09F;110F 1174 11BE;D09F;110F 1174 11BE; # (킟; 킟; á„ᅴᆾ; 킟; á„ᅴᆾ; ) HANGUL SYLLABLE KYIC
+D0A0;D0A0;110F 1174 11BF;D0A0;110F 1174 11BF; # (í‚ ; í‚ ; á„ᅴᆿ; í‚ ; á„ᅴᆿ; ) HANGUL SYLLABLE KYIK
+D0A1;D0A1;110F 1174 11C0;D0A1;110F 1174 11C0; # (í‚¡; í‚¡; á„ᅴᇀ; í‚¡; á„ᅴᇀ; ) HANGUL SYLLABLE KYIT
+D0A2;D0A2;110F 1174 11C1;D0A2;110F 1174 11C1; # (í‚¢; í‚¢; á„á…´á‡; í‚¢; á„á…´á‡; ) HANGUL SYLLABLE KYIP
+D0A3;D0A3;110F 1174 11C2;D0A3;110F 1174 11C2; # (í‚£; í‚£; á„ᅴᇂ; í‚£; á„ᅴᇂ; ) HANGUL SYLLABLE KYIH
+D0A4;D0A4;110F 1175;D0A4;110F 1175; # (키; 키; á„á…µ; 키; á„á…µ; ) HANGUL SYLLABLE KI
+D0A5;D0A5;110F 1175 11A8;D0A5;110F 1175 11A8; # (í‚¥; í‚¥; á„ᅵᆨ; í‚¥; á„ᅵᆨ; ) HANGUL SYLLABLE KIG
+D0A6;D0A6;110F 1175 11A9;D0A6;110F 1175 11A9; # (킦; 킦; á„ᅵᆩ; 킦; á„ᅵᆩ; ) HANGUL SYLLABLE KIGG
+D0A7;D0A7;110F 1175 11AA;D0A7;110F 1175 11AA; # (í‚§; í‚§; á„ᅵᆪ; í‚§; á„ᅵᆪ; ) HANGUL SYLLABLE KIGS
+D0A8;D0A8;110F 1175 11AB;D0A8;110F 1175 11AB; # (킨; 킨; á„ᅵᆫ; 킨; á„ᅵᆫ; ) HANGUL SYLLABLE KIN
+D0A9;D0A9;110F 1175 11AC;D0A9;110F 1175 11AC; # (í‚©; í‚©; á„ᅵᆬ; í‚©; á„ᅵᆬ; ) HANGUL SYLLABLE KINJ
+D0AA;D0AA;110F 1175 11AD;D0AA;110F 1175 11AD; # (킪; 킪; á„ᅵᆭ; 킪; á„ᅵᆭ; ) HANGUL SYLLABLE KINH
+D0AB;D0AB;110F 1175 11AE;D0AB;110F 1175 11AE; # (í‚«; í‚«; á„ᅵᆮ; í‚«; á„ᅵᆮ; ) HANGUL SYLLABLE KID
+D0AC;D0AC;110F 1175 11AF;D0AC;110F 1175 11AF; # (킬; 킬; á„ᅵᆯ; 킬; á„ᅵᆯ; ) HANGUL SYLLABLE KIL
+D0AD;D0AD;110F 1175 11B0;D0AD;110F 1175 11B0; # (í‚­; í‚­; á„ᅵᆰ; í‚­; á„ᅵᆰ; ) HANGUL SYLLABLE KILG
+D0AE;D0AE;110F 1175 11B1;D0AE;110F 1175 11B1; # (í‚®; í‚®; á„ᅵᆱ; í‚®; á„ᅵᆱ; ) HANGUL SYLLABLE KILM
+D0AF;D0AF;110F 1175 11B2;D0AF;110F 1175 11B2; # (킯; 킯; á„ᅵᆲ; 킯; á„ᅵᆲ; ) HANGUL SYLLABLE KILB
+D0B0;D0B0;110F 1175 11B3;D0B0;110F 1175 11B3; # (í‚°; í‚°; á„ᅵᆳ; í‚°; á„ᅵᆳ; ) HANGUL SYLLABLE KILS
+D0B1;D0B1;110F 1175 11B4;D0B1;110F 1175 11B4; # (킱; 킱; á„ᅵᆴ; 킱; á„ᅵᆴ; ) HANGUL SYLLABLE KILT
+D0B2;D0B2;110F 1175 11B5;D0B2;110F 1175 11B5; # (킲; 킲; á„ᅵᆵ; 킲; á„ᅵᆵ; ) HANGUL SYLLABLE KILP
+D0B3;D0B3;110F 1175 11B6;D0B3;110F 1175 11B6; # (킳; 킳; á„ᅵᆶ; 킳; á„ᅵᆶ; ) HANGUL SYLLABLE KILH
+D0B4;D0B4;110F 1175 11B7;D0B4;110F 1175 11B7; # (í‚´; í‚´; á„ᅵᆷ; í‚´; á„ᅵᆷ; ) HANGUL SYLLABLE KIM
+D0B5;D0B5;110F 1175 11B8;D0B5;110F 1175 11B8; # (킵; 킵; á„ᅵᆸ; 킵; á„ᅵᆸ; ) HANGUL SYLLABLE KIB
+D0B6;D0B6;110F 1175 11B9;D0B6;110F 1175 11B9; # (í‚¶; í‚¶; á„ᅵᆹ; í‚¶; á„ᅵᆹ; ) HANGUL SYLLABLE KIBS
+D0B7;D0B7;110F 1175 11BA;D0B7;110F 1175 11BA; # (í‚·; í‚·; á„ᅵᆺ; í‚·; á„ᅵᆺ; ) HANGUL SYLLABLE KIS
+D0B8;D0B8;110F 1175 11BB;D0B8;110F 1175 11BB; # (킸; 킸; á„ᅵᆻ; 킸; á„ᅵᆻ; ) HANGUL SYLLABLE KISS
+D0B9;D0B9;110F 1175 11BC;D0B9;110F 1175 11BC; # (킹; 킹; á„ᅵᆼ; 킹; á„ᅵᆼ; ) HANGUL SYLLABLE KING
+D0BA;D0BA;110F 1175 11BD;D0BA;110F 1175 11BD; # (킺; 킺; á„ᅵᆽ; 킺; á„ᅵᆽ; ) HANGUL SYLLABLE KIJ
+D0BB;D0BB;110F 1175 11BE;D0BB;110F 1175 11BE; # (í‚»; í‚»; á„ᅵᆾ; í‚»; á„ᅵᆾ; ) HANGUL SYLLABLE KIC
+D0BC;D0BC;110F 1175 11BF;D0BC;110F 1175 11BF; # (킼; 킼; á„ᅵᆿ; 킼; á„ᅵᆿ; ) HANGUL SYLLABLE KIK
+D0BD;D0BD;110F 1175 11C0;D0BD;110F 1175 11C0; # (킽; 킽; á„ᅵᇀ; 킽; á„ᅵᇀ; ) HANGUL SYLLABLE KIT
+D0BE;D0BE;110F 1175 11C1;D0BE;110F 1175 11C1; # (킾; 킾; á„á…µá‡; 킾; á„á…µá‡; ) HANGUL SYLLABLE KIP
+D0BF;D0BF;110F 1175 11C2;D0BF;110F 1175 11C2; # (í‚¿; í‚¿; á„ᅵᇂ; í‚¿; á„ᅵᇂ; ) HANGUL SYLLABLE KIH
+D0C0;D0C0;1110 1161;D0C0;1110 1161; # (타; 타; á„á…¡; 타; á„á…¡; ) HANGUL SYLLABLE TA
+D0C1;D0C1;1110 1161 11A8;D0C1;1110 1161 11A8; # (íƒ; íƒ; á„ᅡᆨ; íƒ; á„ᅡᆨ; ) HANGUL SYLLABLE TAG
+D0C2;D0C2;1110 1161 11A9;D0C2;1110 1161 11A9; # (탂; 탂; á„ᅡᆩ; 탂; á„ᅡᆩ; ) HANGUL SYLLABLE TAGG
+D0C3;D0C3;1110 1161 11AA;D0C3;1110 1161 11AA; # (탃; 탃; á„ᅡᆪ; 탃; á„ᅡᆪ; ) HANGUL SYLLABLE TAGS
+D0C4;D0C4;1110 1161 11AB;D0C4;1110 1161 11AB; # (탄; 탄; á„ᅡᆫ; 탄; á„ᅡᆫ; ) HANGUL SYLLABLE TAN
+D0C5;D0C5;1110 1161 11AC;D0C5;1110 1161 11AC; # (탅; 탅; á„ᅡᆬ; 탅; á„ᅡᆬ; ) HANGUL SYLLABLE TANJ
+D0C6;D0C6;1110 1161 11AD;D0C6;1110 1161 11AD; # (탆; 탆; á„ᅡᆭ; 탆; á„ᅡᆭ; ) HANGUL SYLLABLE TANH
+D0C7;D0C7;1110 1161 11AE;D0C7;1110 1161 11AE; # (탇; 탇; á„ᅡᆮ; 탇; á„ᅡᆮ; ) HANGUL SYLLABLE TAD
+D0C8;D0C8;1110 1161 11AF;D0C8;1110 1161 11AF; # (탈; 탈; á„ᅡᆯ; 탈; á„ᅡᆯ; ) HANGUL SYLLABLE TAL
+D0C9;D0C9;1110 1161 11B0;D0C9;1110 1161 11B0; # (탉; 탉; á„ᅡᆰ; 탉; á„ᅡᆰ; ) HANGUL SYLLABLE TALG
+D0CA;D0CA;1110 1161 11B1;D0CA;1110 1161 11B1; # (탊; 탊; á„ᅡᆱ; 탊; á„ᅡᆱ; ) HANGUL SYLLABLE TALM
+D0CB;D0CB;1110 1161 11B2;D0CB;1110 1161 11B2; # (탋; 탋; á„ᅡᆲ; 탋; á„ᅡᆲ; ) HANGUL SYLLABLE TALB
+D0CC;D0CC;1110 1161 11B3;D0CC;1110 1161 11B3; # (탌; 탌; á„ᅡᆳ; 탌; á„ᅡᆳ; ) HANGUL SYLLABLE TALS
+D0CD;D0CD;1110 1161 11B4;D0CD;1110 1161 11B4; # (íƒ; íƒ; á„ᅡᆴ; íƒ; á„ᅡᆴ; ) HANGUL SYLLABLE TALT
+D0CE;D0CE;1110 1161 11B5;D0CE;1110 1161 11B5; # (탎; 탎; á„ᅡᆵ; 탎; á„ᅡᆵ; ) HANGUL SYLLABLE TALP
+D0CF;D0CF;1110 1161 11B6;D0CF;1110 1161 11B6; # (íƒ; íƒ; á„ᅡᆶ; íƒ; á„ᅡᆶ; ) HANGUL SYLLABLE TALH
+D0D0;D0D0;1110 1161 11B7;D0D0;1110 1161 11B7; # (íƒ; íƒ; á„ᅡᆷ; íƒ; á„ᅡᆷ; ) HANGUL SYLLABLE TAM
+D0D1;D0D1;1110 1161 11B8;D0D1;1110 1161 11B8; # (탑; 탑; á„ᅡᆸ; 탑; á„ᅡᆸ; ) HANGUL SYLLABLE TAB
+D0D2;D0D2;1110 1161 11B9;D0D2;1110 1161 11B9; # (탒; 탒; á„ᅡᆹ; 탒; á„ᅡᆹ; ) HANGUL SYLLABLE TABS
+D0D3;D0D3;1110 1161 11BA;D0D3;1110 1161 11BA; # (탓; 탓; á„ᅡᆺ; 탓; á„ᅡᆺ; ) HANGUL SYLLABLE TAS
+D0D4;D0D4;1110 1161 11BB;D0D4;1110 1161 11BB; # (탔; 탔; á„ᅡᆻ; 탔; á„ᅡᆻ; ) HANGUL SYLLABLE TASS
+D0D5;D0D5;1110 1161 11BC;D0D5;1110 1161 11BC; # (탕; 탕; á„ᅡᆼ; 탕; á„ᅡᆼ; ) HANGUL SYLLABLE TANG
+D0D6;D0D6;1110 1161 11BD;D0D6;1110 1161 11BD; # (탖; 탖; á„ᅡᆽ; 탖; á„ᅡᆽ; ) HANGUL SYLLABLE TAJ
+D0D7;D0D7;1110 1161 11BE;D0D7;1110 1161 11BE; # (탗; 탗; á„ᅡᆾ; 탗; á„ᅡᆾ; ) HANGUL SYLLABLE TAC
+D0D8;D0D8;1110 1161 11BF;D0D8;1110 1161 11BF; # (탘; 탘; á„ᅡᆿ; 탘; á„ᅡᆿ; ) HANGUL SYLLABLE TAK
+D0D9;D0D9;1110 1161 11C0;D0D9;1110 1161 11C0; # (탙; 탙; á„ᅡᇀ; 탙; á„ᅡᇀ; ) HANGUL SYLLABLE TAT
+D0DA;D0DA;1110 1161 11C1;D0DA;1110 1161 11C1; # (탚; 탚; á„á…¡á‡; 탚; á„á…¡á‡; ) HANGUL SYLLABLE TAP
+D0DB;D0DB;1110 1161 11C2;D0DB;1110 1161 11C2; # (탛; 탛; á„ᅡᇂ; 탛; á„ᅡᇂ; ) HANGUL SYLLABLE TAH
+D0DC;D0DC;1110 1162;D0DC;1110 1162; # (태; 태; á„á…¢; 태; á„á…¢; ) HANGUL SYLLABLE TAE
+D0DD;D0DD;1110 1162 11A8;D0DD;1110 1162 11A8; # (íƒ; íƒ; á„ᅢᆨ; íƒ; á„ᅢᆨ; ) HANGUL SYLLABLE TAEG
+D0DE;D0DE;1110 1162 11A9;D0DE;1110 1162 11A9; # (탞; 탞; á„ᅢᆩ; 탞; á„ᅢᆩ; ) HANGUL SYLLABLE TAEGG
+D0DF;D0DF;1110 1162 11AA;D0DF;1110 1162 11AA; # (탟; 탟; á„ᅢᆪ; 탟; á„ᅢᆪ; ) HANGUL SYLLABLE TAEGS
+D0E0;D0E0;1110 1162 11AB;D0E0;1110 1162 11AB; # (탠; 탠; á„ᅢᆫ; 탠; á„ᅢᆫ; ) HANGUL SYLLABLE TAEN
+D0E1;D0E1;1110 1162 11AC;D0E1;1110 1162 11AC; # (탡; 탡; á„ᅢᆬ; 탡; á„ᅢᆬ; ) HANGUL SYLLABLE TAENJ
+D0E2;D0E2;1110 1162 11AD;D0E2;1110 1162 11AD; # (탢; 탢; á„ᅢᆭ; 탢; á„ᅢᆭ; ) HANGUL SYLLABLE TAENH
+D0E3;D0E3;1110 1162 11AE;D0E3;1110 1162 11AE; # (탣; 탣; á„ᅢᆮ; 탣; á„ᅢᆮ; ) HANGUL SYLLABLE TAED
+D0E4;D0E4;1110 1162 11AF;D0E4;1110 1162 11AF; # (탤; 탤; á„ᅢᆯ; 탤; á„ᅢᆯ; ) HANGUL SYLLABLE TAEL
+D0E5;D0E5;1110 1162 11B0;D0E5;1110 1162 11B0; # (탥; 탥; á„ᅢᆰ; 탥; á„ᅢᆰ; ) HANGUL SYLLABLE TAELG
+D0E6;D0E6;1110 1162 11B1;D0E6;1110 1162 11B1; # (탦; 탦; á„ᅢᆱ; 탦; á„ᅢᆱ; ) HANGUL SYLLABLE TAELM
+D0E7;D0E7;1110 1162 11B2;D0E7;1110 1162 11B2; # (탧; 탧; á„ᅢᆲ; 탧; á„ᅢᆲ; ) HANGUL SYLLABLE TAELB
+D0E8;D0E8;1110 1162 11B3;D0E8;1110 1162 11B3; # (탨; 탨; á„ᅢᆳ; 탨; á„ᅢᆳ; ) HANGUL SYLLABLE TAELS
+D0E9;D0E9;1110 1162 11B4;D0E9;1110 1162 11B4; # (탩; 탩; á„ᅢᆴ; 탩; á„ᅢᆴ; ) HANGUL SYLLABLE TAELT
+D0EA;D0EA;1110 1162 11B5;D0EA;1110 1162 11B5; # (탪; 탪; á„ᅢᆵ; 탪; á„ᅢᆵ; ) HANGUL SYLLABLE TAELP
+D0EB;D0EB;1110 1162 11B6;D0EB;1110 1162 11B6; # (탫; 탫; á„ᅢᆶ; 탫; á„ᅢᆶ; ) HANGUL SYLLABLE TAELH
+D0EC;D0EC;1110 1162 11B7;D0EC;1110 1162 11B7; # (탬; 탬; á„ᅢᆷ; 탬; á„ᅢᆷ; ) HANGUL SYLLABLE TAEM
+D0ED;D0ED;1110 1162 11B8;D0ED;1110 1162 11B8; # (탭; 탭; á„ᅢᆸ; 탭; á„ᅢᆸ; ) HANGUL SYLLABLE TAEB
+D0EE;D0EE;1110 1162 11B9;D0EE;1110 1162 11B9; # (탮; 탮; á„ᅢᆹ; 탮; á„ᅢᆹ; ) HANGUL SYLLABLE TAEBS
+D0EF;D0EF;1110 1162 11BA;D0EF;1110 1162 11BA; # (탯; 탯; á„ᅢᆺ; 탯; á„ᅢᆺ; ) HANGUL SYLLABLE TAES
+D0F0;D0F0;1110 1162 11BB;D0F0;1110 1162 11BB; # (탰; 탰; á„ᅢᆻ; 탰; á„ᅢᆻ; ) HANGUL SYLLABLE TAESS
+D0F1;D0F1;1110 1162 11BC;D0F1;1110 1162 11BC; # (탱; 탱; á„ᅢᆼ; 탱; á„ᅢᆼ; ) HANGUL SYLLABLE TAENG
+D0F2;D0F2;1110 1162 11BD;D0F2;1110 1162 11BD; # (탲; 탲; á„ᅢᆽ; 탲; á„ᅢᆽ; ) HANGUL SYLLABLE TAEJ
+D0F3;D0F3;1110 1162 11BE;D0F3;1110 1162 11BE; # (탳; 탳; á„ᅢᆾ; 탳; á„ᅢᆾ; ) HANGUL SYLLABLE TAEC
+D0F4;D0F4;1110 1162 11BF;D0F4;1110 1162 11BF; # (탴; 탴; á„ᅢᆿ; 탴; á„ᅢᆿ; ) HANGUL SYLLABLE TAEK
+D0F5;D0F5;1110 1162 11C0;D0F5;1110 1162 11C0; # (탵; 탵; á„ᅢᇀ; 탵; á„ᅢᇀ; ) HANGUL SYLLABLE TAET
+D0F6;D0F6;1110 1162 11C1;D0F6;1110 1162 11C1; # (탶; 탶; á„á…¢á‡; 탶; á„á…¢á‡; ) HANGUL SYLLABLE TAEP
+D0F7;D0F7;1110 1162 11C2;D0F7;1110 1162 11C2; # (탷; 탷; á„ᅢᇂ; 탷; á„ᅢᇂ; ) HANGUL SYLLABLE TAEH
+D0F8;D0F8;1110 1163;D0F8;1110 1163; # (탸; 탸; á„á…£; 탸; á„á…£; ) HANGUL SYLLABLE TYA
+D0F9;D0F9;1110 1163 11A8;D0F9;1110 1163 11A8; # (탹; 탹; á„ᅣᆨ; 탹; á„ᅣᆨ; ) HANGUL SYLLABLE TYAG
+D0FA;D0FA;1110 1163 11A9;D0FA;1110 1163 11A9; # (탺; 탺; á„ᅣᆩ; 탺; á„ᅣᆩ; ) HANGUL SYLLABLE TYAGG
+D0FB;D0FB;1110 1163 11AA;D0FB;1110 1163 11AA; # (탻; 탻; á„ᅣᆪ; 탻; á„ᅣᆪ; ) HANGUL SYLLABLE TYAGS
+D0FC;D0FC;1110 1163 11AB;D0FC;1110 1163 11AB; # (탼; 탼; á„ᅣᆫ; 탼; á„ᅣᆫ; ) HANGUL SYLLABLE TYAN
+D0FD;D0FD;1110 1163 11AC;D0FD;1110 1163 11AC; # (탽; 탽; á„ᅣᆬ; 탽; á„ᅣᆬ; ) HANGUL SYLLABLE TYANJ
+D0FE;D0FE;1110 1163 11AD;D0FE;1110 1163 11AD; # (탾; 탾; á„ᅣᆭ; 탾; á„ᅣᆭ; ) HANGUL SYLLABLE TYANH
+D0FF;D0FF;1110 1163 11AE;D0FF;1110 1163 11AE; # (탿; 탿; á„ᅣᆮ; 탿; á„ᅣᆮ; ) HANGUL SYLLABLE TYAD
+D100;D100;1110 1163 11AF;D100;1110 1163 11AF; # (í„€; í„€; á„ᅣᆯ; í„€; á„ᅣᆯ; ) HANGUL SYLLABLE TYAL
+D101;D101;1110 1163 11B0;D101;1110 1163 11B0; # (í„; í„; á„ᅣᆰ; í„; á„ᅣᆰ; ) HANGUL SYLLABLE TYALG
+D102;D102;1110 1163 11B1;D102;1110 1163 11B1; # (í„‚; í„‚; á„ᅣᆱ; í„‚; á„ᅣᆱ; ) HANGUL SYLLABLE TYALM
+D103;D103;1110 1163 11B2;D103;1110 1163 11B2; # (턃; 턃; á„ᅣᆲ; 턃; á„ᅣᆲ; ) HANGUL SYLLABLE TYALB
+D104;D104;1110 1163 11B3;D104;1110 1163 11B3; # (í„„; í„„; á„ᅣᆳ; í„„; á„ᅣᆳ; ) HANGUL SYLLABLE TYALS
+D105;D105;1110 1163 11B4;D105;1110 1163 11B4; # (í„…; í„…; á„ᅣᆴ; í„…; á„ᅣᆴ; ) HANGUL SYLLABLE TYALT
+D106;D106;1110 1163 11B5;D106;1110 1163 11B5; # (턆; 턆; á„ᅣᆵ; 턆; á„ᅣᆵ; ) HANGUL SYLLABLE TYALP
+D107;D107;1110 1163 11B6;D107;1110 1163 11B6; # (턇; 턇; á„ᅣᆶ; 턇; á„ᅣᆶ; ) HANGUL SYLLABLE TYALH
+D108;D108;1110 1163 11B7;D108;1110 1163 11B7; # (턈; 턈; á„ᅣᆷ; 턈; á„ᅣᆷ; ) HANGUL SYLLABLE TYAM
+D109;D109;1110 1163 11B8;D109;1110 1163 11B8; # (턉; 턉; á„ᅣᆸ; 턉; á„ᅣᆸ; ) HANGUL SYLLABLE TYAB
+D10A;D10A;1110 1163 11B9;D10A;1110 1163 11B9; # (턊; 턊; á„ᅣᆹ; 턊; á„ᅣᆹ; ) HANGUL SYLLABLE TYABS
+D10B;D10B;1110 1163 11BA;D10B;1110 1163 11BA; # (í„‹; í„‹; á„ᅣᆺ; í„‹; á„ᅣᆺ; ) HANGUL SYLLABLE TYAS
+D10C;D10C;1110 1163 11BB;D10C;1110 1163 11BB; # (턌; 턌; á„ᅣᆻ; 턌; á„ᅣᆻ; ) HANGUL SYLLABLE TYASS
+D10D;D10D;1110 1163 11BC;D10D;1110 1163 11BC; # (í„; í„; á„ᅣᆼ; í„; á„ᅣᆼ; ) HANGUL SYLLABLE TYANG
+D10E;D10E;1110 1163 11BD;D10E;1110 1163 11BD; # (턎; 턎; á„ᅣᆽ; 턎; á„ᅣᆽ; ) HANGUL SYLLABLE TYAJ
+D10F;D10F;1110 1163 11BE;D10F;1110 1163 11BE; # (í„; í„; á„ᅣᆾ; í„; á„ᅣᆾ; ) HANGUL SYLLABLE TYAC
+D110;D110;1110 1163 11BF;D110;1110 1163 11BF; # (í„; í„; á„ᅣᆿ; í„; á„ᅣᆿ; ) HANGUL SYLLABLE TYAK
+D111;D111;1110 1163 11C0;D111;1110 1163 11C0; # (í„‘; í„‘; á„ᅣᇀ; í„‘; á„ᅣᇀ; ) HANGUL SYLLABLE TYAT
+D112;D112;1110 1163 11C1;D112;1110 1163 11C1; # (í„’; í„’; á„á…£á‡; í„’; á„á…£á‡; ) HANGUL SYLLABLE TYAP
+D113;D113;1110 1163 11C2;D113;1110 1163 11C2; # (í„“; í„“; á„ᅣᇂ; í„“; á„ᅣᇂ; ) HANGUL SYLLABLE TYAH
+D114;D114;1110 1164;D114;1110 1164; # (í„”; í„”; á„á…¤; í„”; á„á…¤; ) HANGUL SYLLABLE TYAE
+D115;D115;1110 1164 11A8;D115;1110 1164 11A8; # (í„•; í„•; á„ᅤᆨ; í„•; á„ᅤᆨ; ) HANGUL SYLLABLE TYAEG
+D116;D116;1110 1164 11A9;D116;1110 1164 11A9; # (í„–; í„–; á„ᅤᆩ; í„–; á„ᅤᆩ; ) HANGUL SYLLABLE TYAEGG
+D117;D117;1110 1164 11AA;D117;1110 1164 11AA; # (í„—; í„—; á„ᅤᆪ; í„—; á„ᅤᆪ; ) HANGUL SYLLABLE TYAEGS
+D118;D118;1110 1164 11AB;D118;1110 1164 11AB; # (턘; 턘; á„ᅤᆫ; 턘; á„ᅤᆫ; ) HANGUL SYLLABLE TYAEN
+D119;D119;1110 1164 11AC;D119;1110 1164 11AC; # (í„™; í„™; á„ᅤᆬ; í„™; á„ᅤᆬ; ) HANGUL SYLLABLE TYAENJ
+D11A;D11A;1110 1164 11AD;D11A;1110 1164 11AD; # (턚; 턚; á„ᅤᆭ; 턚; á„ᅤᆭ; ) HANGUL SYLLABLE TYAENH
+D11B;D11B;1110 1164 11AE;D11B;1110 1164 11AE; # (í„›; í„›; á„ᅤᆮ; í„›; á„ᅤᆮ; ) HANGUL SYLLABLE TYAED
+D11C;D11C;1110 1164 11AF;D11C;1110 1164 11AF; # (턜; 턜; á„ᅤᆯ; 턜; á„ᅤᆯ; ) HANGUL SYLLABLE TYAEL
+D11D;D11D;1110 1164 11B0;D11D;1110 1164 11B0; # (í„; í„; á„ᅤᆰ; í„; á„ᅤᆰ; ) HANGUL SYLLABLE TYAELG
+D11E;D11E;1110 1164 11B1;D11E;1110 1164 11B1; # (턞; 턞; á„ᅤᆱ; 턞; á„ᅤᆱ; ) HANGUL SYLLABLE TYAELM
+D11F;D11F;1110 1164 11B2;D11F;1110 1164 11B2; # (턟; 턟; á„ᅤᆲ; 턟; á„ᅤᆲ; ) HANGUL SYLLABLE TYAELB
+D120;D120;1110 1164 11B3;D120;1110 1164 11B3; # (í„ ; í„ ; á„ᅤᆳ; í„ ; á„ᅤᆳ; ) HANGUL SYLLABLE TYAELS
+D121;D121;1110 1164 11B4;D121;1110 1164 11B4; # (í„¡; í„¡; á„ᅤᆴ; í„¡; á„ᅤᆴ; ) HANGUL SYLLABLE TYAELT
+D122;D122;1110 1164 11B5;D122;1110 1164 11B5; # (í„¢; í„¢; á„ᅤᆵ; í„¢; á„ᅤᆵ; ) HANGUL SYLLABLE TYAELP
+D123;D123;1110 1164 11B6;D123;1110 1164 11B6; # (í„£; í„£; á„ᅤᆶ; í„£; á„ᅤᆶ; ) HANGUL SYLLABLE TYAELH
+D124;D124;1110 1164 11B7;D124;1110 1164 11B7; # (턤; 턤; á„ᅤᆷ; 턤; á„ᅤᆷ; ) HANGUL SYLLABLE TYAEM
+D125;D125;1110 1164 11B8;D125;1110 1164 11B8; # (í„¥; í„¥; á„ᅤᆸ; í„¥; á„ᅤᆸ; ) HANGUL SYLLABLE TYAEB
+D126;D126;1110 1164 11B9;D126;1110 1164 11B9; # (턦; 턦; á„ᅤᆹ; 턦; á„ᅤᆹ; ) HANGUL SYLLABLE TYAEBS
+D127;D127;1110 1164 11BA;D127;1110 1164 11BA; # (í„§; í„§; á„ᅤᆺ; í„§; á„ᅤᆺ; ) HANGUL SYLLABLE TYAES
+D128;D128;1110 1164 11BB;D128;1110 1164 11BB; # (턨; 턨; á„ᅤᆻ; 턨; á„ᅤᆻ; ) HANGUL SYLLABLE TYAESS
+D129;D129;1110 1164 11BC;D129;1110 1164 11BC; # (í„©; í„©; á„ᅤᆼ; í„©; á„ᅤᆼ; ) HANGUL SYLLABLE TYAENG
+D12A;D12A;1110 1164 11BD;D12A;1110 1164 11BD; # (턪; 턪; á„ᅤᆽ; 턪; á„ᅤᆽ; ) HANGUL SYLLABLE TYAEJ
+D12B;D12B;1110 1164 11BE;D12B;1110 1164 11BE; # (í„«; í„«; á„ᅤᆾ; í„«; á„ᅤᆾ; ) HANGUL SYLLABLE TYAEC
+D12C;D12C;1110 1164 11BF;D12C;1110 1164 11BF; # (턬; 턬; á„ᅤᆿ; 턬; á„ᅤᆿ; ) HANGUL SYLLABLE TYAEK
+D12D;D12D;1110 1164 11C0;D12D;1110 1164 11C0; # (í„­; í„­; á„ᅤᇀ; í„­; á„ᅤᇀ; ) HANGUL SYLLABLE TYAET
+D12E;D12E;1110 1164 11C1;D12E;1110 1164 11C1; # (í„®; í„®; á„á…¤á‡; í„®; á„á…¤á‡; ) HANGUL SYLLABLE TYAEP
+D12F;D12F;1110 1164 11C2;D12F;1110 1164 11C2; # (턯; 턯; á„ᅤᇂ; 턯; á„ᅤᇂ; ) HANGUL SYLLABLE TYAEH
+D130;D130;1110 1165;D130;1110 1165; # (í„°; í„°; á„á…¥; í„°; á„á…¥; ) HANGUL SYLLABLE TEO
+D131;D131;1110 1165 11A8;D131;1110 1165 11A8; # (턱; 턱; á„ᅥᆨ; 턱; á„ᅥᆨ; ) HANGUL SYLLABLE TEOG
+D132;D132;1110 1165 11A9;D132;1110 1165 11A9; # (턲; 턲; á„ᅥᆩ; 턲; á„ᅥᆩ; ) HANGUL SYLLABLE TEOGG
+D133;D133;1110 1165 11AA;D133;1110 1165 11AA; # (턳; 턳; á„ᅥᆪ; 턳; á„ᅥᆪ; ) HANGUL SYLLABLE TEOGS
+D134;D134;1110 1165 11AB;D134;1110 1165 11AB; # (í„´; í„´; á„ᅥᆫ; í„´; á„ᅥᆫ; ) HANGUL SYLLABLE TEON
+D135;D135;1110 1165 11AC;D135;1110 1165 11AC; # (턵; 턵; á„ᅥᆬ; 턵; á„ᅥᆬ; ) HANGUL SYLLABLE TEONJ
+D136;D136;1110 1165 11AD;D136;1110 1165 11AD; # (í„¶; í„¶; á„ᅥᆭ; í„¶; á„ᅥᆭ; ) HANGUL SYLLABLE TEONH
+D137;D137;1110 1165 11AE;D137;1110 1165 11AE; # (í„·; í„·; á„ᅥᆮ; í„·; á„ᅥᆮ; ) HANGUL SYLLABLE TEOD
+D138;D138;1110 1165 11AF;D138;1110 1165 11AF; # (털; 털; á„ᅥᆯ; 털; á„ᅥᆯ; ) HANGUL SYLLABLE TEOL
+D139;D139;1110 1165 11B0;D139;1110 1165 11B0; # (턹; 턹; á„ᅥᆰ; 턹; á„ᅥᆰ; ) HANGUL SYLLABLE TEOLG
+D13A;D13A;1110 1165 11B1;D13A;1110 1165 11B1; # (턺; 턺; á„ᅥᆱ; 턺; á„ᅥᆱ; ) HANGUL SYLLABLE TEOLM
+D13B;D13B;1110 1165 11B2;D13B;1110 1165 11B2; # (í„»; í„»; á„ᅥᆲ; í„»; á„ᅥᆲ; ) HANGUL SYLLABLE TEOLB
+D13C;D13C;1110 1165 11B3;D13C;1110 1165 11B3; # (턼; 턼; á„ᅥᆳ; 턼; á„ᅥᆳ; ) HANGUL SYLLABLE TEOLS
+D13D;D13D;1110 1165 11B4;D13D;1110 1165 11B4; # (턽; 턽; á„ᅥᆴ; 턽; á„ᅥᆴ; ) HANGUL SYLLABLE TEOLT
+D13E;D13E;1110 1165 11B5;D13E;1110 1165 11B5; # (턾; 턾; á„ᅥᆵ; 턾; á„ᅥᆵ; ) HANGUL SYLLABLE TEOLP
+D13F;D13F;1110 1165 11B6;D13F;1110 1165 11B6; # (í„¿; í„¿; á„ᅥᆶ; í„¿; á„ᅥᆶ; ) HANGUL SYLLABLE TEOLH
+D140;D140;1110 1165 11B7;D140;1110 1165 11B7; # (í…€; í…€; á„ᅥᆷ; í…€; á„ᅥᆷ; ) HANGUL SYLLABLE TEOM
+D141;D141;1110 1165 11B8;D141;1110 1165 11B8; # (í…; í…; á„ᅥᆸ; í…; á„ᅥᆸ; ) HANGUL SYLLABLE TEOB
+D142;D142;1110 1165 11B9;D142;1110 1165 11B9; # (í…‚; í…‚; á„ᅥᆹ; í…‚; á„ᅥᆹ; ) HANGUL SYLLABLE TEOBS
+D143;D143;1110 1165 11BA;D143;1110 1165 11BA; # (í…ƒ; í…ƒ; á„ᅥᆺ; í…ƒ; á„ᅥᆺ; ) HANGUL SYLLABLE TEOS
+D144;D144;1110 1165 11BB;D144;1110 1165 11BB; # (í…„; í…„; á„ᅥᆻ; í…„; á„ᅥᆻ; ) HANGUL SYLLABLE TEOSS
+D145;D145;1110 1165 11BC;D145;1110 1165 11BC; # (í……; í……; á„ᅥᆼ; í……; á„ᅥᆼ; ) HANGUL SYLLABLE TEONG
+D146;D146;1110 1165 11BD;D146;1110 1165 11BD; # (í…†; í…†; á„ᅥᆽ; í…†; á„ᅥᆽ; ) HANGUL SYLLABLE TEOJ
+D147;D147;1110 1165 11BE;D147;1110 1165 11BE; # (í…‡; í…‡; á„ᅥᆾ; í…‡; á„ᅥᆾ; ) HANGUL SYLLABLE TEOC
+D148;D148;1110 1165 11BF;D148;1110 1165 11BF; # (í…ˆ; í…ˆ; á„ᅥᆿ; í…ˆ; á„ᅥᆿ; ) HANGUL SYLLABLE TEOK
+D149;D149;1110 1165 11C0;D149;1110 1165 11C0; # (í…‰; í…‰; á„ᅥᇀ; í…‰; á„ᅥᇀ; ) HANGUL SYLLABLE TEOT
+D14A;D14A;1110 1165 11C1;D14A;1110 1165 11C1; # (í…Š; í…Š; á„á…¥á‡; í…Š; á„á…¥á‡; ) HANGUL SYLLABLE TEOP
+D14B;D14B;1110 1165 11C2;D14B;1110 1165 11C2; # (í…‹; í…‹; á„ᅥᇂ; í…‹; á„ᅥᇂ; ) HANGUL SYLLABLE TEOH
+D14C;D14C;1110 1166;D14C;1110 1166; # (í…Œ; í…Œ; á„á…¦; í…Œ; á„á…¦; ) HANGUL SYLLABLE TE
+D14D;D14D;1110 1166 11A8;D14D;1110 1166 11A8; # (í…; í…; á„ᅦᆨ; í…; á„ᅦᆨ; ) HANGUL SYLLABLE TEG
+D14E;D14E;1110 1166 11A9;D14E;1110 1166 11A9; # (í…Ž; í…Ž; á„ᅦᆩ; í…Ž; á„ᅦᆩ; ) HANGUL SYLLABLE TEGG
+D14F;D14F;1110 1166 11AA;D14F;1110 1166 11AA; # (í…; í…; á„ᅦᆪ; í…; á„ᅦᆪ; ) HANGUL SYLLABLE TEGS
+D150;D150;1110 1166 11AB;D150;1110 1166 11AB; # (í…; í…; á„ᅦᆫ; í…; á„ᅦᆫ; ) HANGUL SYLLABLE TEN
+D151;D151;1110 1166 11AC;D151;1110 1166 11AC; # (í…‘; í…‘; á„ᅦᆬ; í…‘; á„ᅦᆬ; ) HANGUL SYLLABLE TENJ
+D152;D152;1110 1166 11AD;D152;1110 1166 11AD; # (í…’; í…’; á„ᅦᆭ; í…’; á„ᅦᆭ; ) HANGUL SYLLABLE TENH
+D153;D153;1110 1166 11AE;D153;1110 1166 11AE; # (í…“; í…“; á„ᅦᆮ; í…“; á„ᅦᆮ; ) HANGUL SYLLABLE TED
+D154;D154;1110 1166 11AF;D154;1110 1166 11AF; # (í…”; í…”; á„ᅦᆯ; í…”; á„ᅦᆯ; ) HANGUL SYLLABLE TEL
+D155;D155;1110 1166 11B0;D155;1110 1166 11B0; # (í…•; í…•; á„ᅦᆰ; í…•; á„ᅦᆰ; ) HANGUL SYLLABLE TELG
+D156;D156;1110 1166 11B1;D156;1110 1166 11B1; # (í…–; í…–; á„ᅦᆱ; í…–; á„ᅦᆱ; ) HANGUL SYLLABLE TELM
+D157;D157;1110 1166 11B2;D157;1110 1166 11B2; # (í…—; í…—; á„ᅦᆲ; í…—; á„ᅦᆲ; ) HANGUL SYLLABLE TELB
+D158;D158;1110 1166 11B3;D158;1110 1166 11B3; # (í…˜; í…˜; á„ᅦᆳ; í…˜; á„ᅦᆳ; ) HANGUL SYLLABLE TELS
+D159;D159;1110 1166 11B4;D159;1110 1166 11B4; # (í…™; í…™; á„ᅦᆴ; í…™; á„ᅦᆴ; ) HANGUL SYLLABLE TELT
+D15A;D15A;1110 1166 11B5;D15A;1110 1166 11B5; # (í…š; í…š; á„ᅦᆵ; í…š; á„ᅦᆵ; ) HANGUL SYLLABLE TELP
+D15B;D15B;1110 1166 11B6;D15B;1110 1166 11B6; # (í…›; í…›; á„ᅦᆶ; í…›; á„ᅦᆶ; ) HANGUL SYLLABLE TELH
+D15C;D15C;1110 1166 11B7;D15C;1110 1166 11B7; # (í…œ; í…œ; á„ᅦᆷ; í…œ; á„ᅦᆷ; ) HANGUL SYLLABLE TEM
+D15D;D15D;1110 1166 11B8;D15D;1110 1166 11B8; # (í…; í…; á„ᅦᆸ; í…; á„ᅦᆸ; ) HANGUL SYLLABLE TEB
+D15E;D15E;1110 1166 11B9;D15E;1110 1166 11B9; # (í…ž; í…ž; á„ᅦᆹ; í…ž; á„ᅦᆹ; ) HANGUL SYLLABLE TEBS
+D15F;D15F;1110 1166 11BA;D15F;1110 1166 11BA; # (í…Ÿ; í…Ÿ; á„ᅦᆺ; í…Ÿ; á„ᅦᆺ; ) HANGUL SYLLABLE TES
+D160;D160;1110 1166 11BB;D160;1110 1166 11BB; # (í… ; í… ; á„ᅦᆻ; í… ; á„ᅦᆻ; ) HANGUL SYLLABLE TESS
+D161;D161;1110 1166 11BC;D161;1110 1166 11BC; # (í…¡; í…¡; á„ᅦᆼ; í…¡; á„ᅦᆼ; ) HANGUL SYLLABLE TENG
+D162;D162;1110 1166 11BD;D162;1110 1166 11BD; # (í…¢; í…¢; á„ᅦᆽ; í…¢; á„ᅦᆽ; ) HANGUL SYLLABLE TEJ
+D163;D163;1110 1166 11BE;D163;1110 1166 11BE; # (í…£; í…£; á„ᅦᆾ; í…£; á„ᅦᆾ; ) HANGUL SYLLABLE TEC
+D164;D164;1110 1166 11BF;D164;1110 1166 11BF; # (í…¤; í…¤; á„ᅦᆿ; í…¤; á„ᅦᆿ; ) HANGUL SYLLABLE TEK
+D165;D165;1110 1166 11C0;D165;1110 1166 11C0; # (í…¥; í…¥; á„ᅦᇀ; í…¥; á„ᅦᇀ; ) HANGUL SYLLABLE TET
+D166;D166;1110 1166 11C1;D166;1110 1166 11C1; # (í…¦; í…¦; á„á…¦á‡; í…¦; á„á…¦á‡; ) HANGUL SYLLABLE TEP
+D167;D167;1110 1166 11C2;D167;1110 1166 11C2; # (í…§; í…§; á„ᅦᇂ; í…§; á„ᅦᇂ; ) HANGUL SYLLABLE TEH
+D168;D168;1110 1167;D168;1110 1167; # (í…¨; í…¨; á„á…§; í…¨; á„á…§; ) HANGUL SYLLABLE TYEO
+D169;D169;1110 1167 11A8;D169;1110 1167 11A8; # (í…©; í…©; á„ᅧᆨ; í…©; á„ᅧᆨ; ) HANGUL SYLLABLE TYEOG
+D16A;D16A;1110 1167 11A9;D16A;1110 1167 11A9; # (í…ª; í…ª; á„ᅧᆩ; í…ª; á„ᅧᆩ; ) HANGUL SYLLABLE TYEOGG
+D16B;D16B;1110 1167 11AA;D16B;1110 1167 11AA; # (í…«; í…«; á„ᅧᆪ; í…«; á„ᅧᆪ; ) HANGUL SYLLABLE TYEOGS
+D16C;D16C;1110 1167 11AB;D16C;1110 1167 11AB; # (í…¬; í…¬; á„ᅧᆫ; í…¬; á„ᅧᆫ; ) HANGUL SYLLABLE TYEON
+D16D;D16D;1110 1167 11AC;D16D;1110 1167 11AC; # (í…­; í…­; á„ᅧᆬ; í…­; á„ᅧᆬ; ) HANGUL SYLLABLE TYEONJ
+D16E;D16E;1110 1167 11AD;D16E;1110 1167 11AD; # (í…®; í…®; á„ᅧᆭ; í…®; á„ᅧᆭ; ) HANGUL SYLLABLE TYEONH
+D16F;D16F;1110 1167 11AE;D16F;1110 1167 11AE; # (í…¯; í…¯; á„ᅧᆮ; í…¯; á„ᅧᆮ; ) HANGUL SYLLABLE TYEOD
+D170;D170;1110 1167 11AF;D170;1110 1167 11AF; # (í…°; í…°; á„ᅧᆯ; í…°; á„ᅧᆯ; ) HANGUL SYLLABLE TYEOL
+D171;D171;1110 1167 11B0;D171;1110 1167 11B0; # (í…±; í…±; á„ᅧᆰ; í…±; á„ᅧᆰ; ) HANGUL SYLLABLE TYEOLG
+D172;D172;1110 1167 11B1;D172;1110 1167 11B1; # (í…²; í…²; á„ᅧᆱ; í…²; á„ᅧᆱ; ) HANGUL SYLLABLE TYEOLM
+D173;D173;1110 1167 11B2;D173;1110 1167 11B2; # (í…³; í…³; á„ᅧᆲ; í…³; á„ᅧᆲ; ) HANGUL SYLLABLE TYEOLB
+D174;D174;1110 1167 11B3;D174;1110 1167 11B3; # (í…´; í…´; á„ᅧᆳ; í…´; á„ᅧᆳ; ) HANGUL SYLLABLE TYEOLS
+D175;D175;1110 1167 11B4;D175;1110 1167 11B4; # (í…µ; í…µ; á„ᅧᆴ; í…µ; á„ᅧᆴ; ) HANGUL SYLLABLE TYEOLT
+D176;D176;1110 1167 11B5;D176;1110 1167 11B5; # (í…¶; í…¶; á„ᅧᆵ; í…¶; á„ᅧᆵ; ) HANGUL SYLLABLE TYEOLP
+D177;D177;1110 1167 11B6;D177;1110 1167 11B6; # (í…·; í…·; á„ᅧᆶ; í…·; á„ᅧᆶ; ) HANGUL SYLLABLE TYEOLH
+D178;D178;1110 1167 11B7;D178;1110 1167 11B7; # (í…¸; í…¸; á„ᅧᆷ; í…¸; á„ᅧᆷ; ) HANGUL SYLLABLE TYEOM
+D179;D179;1110 1167 11B8;D179;1110 1167 11B8; # (í…¹; í…¹; á„ᅧᆸ; í…¹; á„ᅧᆸ; ) HANGUL SYLLABLE TYEOB
+D17A;D17A;1110 1167 11B9;D17A;1110 1167 11B9; # (í…º; í…º; á„ᅧᆹ; í…º; á„ᅧᆹ; ) HANGUL SYLLABLE TYEOBS
+D17B;D17B;1110 1167 11BA;D17B;1110 1167 11BA; # (í…»; í…»; á„ᅧᆺ; í…»; á„ᅧᆺ; ) HANGUL SYLLABLE TYEOS
+D17C;D17C;1110 1167 11BB;D17C;1110 1167 11BB; # (í…¼; í…¼; á„ᅧᆻ; í…¼; á„ᅧᆻ; ) HANGUL SYLLABLE TYEOSS
+D17D;D17D;1110 1167 11BC;D17D;1110 1167 11BC; # (í…½; í…½; á„ᅧᆼ; í…½; á„ᅧᆼ; ) HANGUL SYLLABLE TYEONG
+D17E;D17E;1110 1167 11BD;D17E;1110 1167 11BD; # (í…¾; í…¾; á„ᅧᆽ; í…¾; á„ᅧᆽ; ) HANGUL SYLLABLE TYEOJ
+D17F;D17F;1110 1167 11BE;D17F;1110 1167 11BE; # (í…¿; í…¿; á„ᅧᆾ; í…¿; á„ᅧᆾ; ) HANGUL SYLLABLE TYEOC
+D180;D180;1110 1167 11BF;D180;1110 1167 11BF; # (톀; 톀; á„ᅧᆿ; 톀; á„ᅧᆿ; ) HANGUL SYLLABLE TYEOK
+D181;D181;1110 1167 11C0;D181;1110 1167 11C0; # (í†; í†; á„ᅧᇀ; í†; á„ᅧᇀ; ) HANGUL SYLLABLE TYEOT
+D182;D182;1110 1167 11C1;D182;1110 1167 11C1; # (톂; 톂; á„á…§á‡; 톂; á„á…§á‡; ) HANGUL SYLLABLE TYEOP
+D183;D183;1110 1167 11C2;D183;1110 1167 11C2; # (톃; 톃; á„ᅧᇂ; 톃; á„ᅧᇂ; ) HANGUL SYLLABLE TYEOH
+D184;D184;1110 1168;D184;1110 1168; # (톄; 톄; á„á…¨; 톄; á„á…¨; ) HANGUL SYLLABLE TYE
+D185;D185;1110 1168 11A8;D185;1110 1168 11A8; # (톅; 톅; á„ᅨᆨ; 톅; á„ᅨᆨ; ) HANGUL SYLLABLE TYEG
+D186;D186;1110 1168 11A9;D186;1110 1168 11A9; # (톆; 톆; á„ᅨᆩ; 톆; á„ᅨᆩ; ) HANGUL SYLLABLE TYEGG
+D187;D187;1110 1168 11AA;D187;1110 1168 11AA; # (톇; 톇; á„ᅨᆪ; 톇; á„ᅨᆪ; ) HANGUL SYLLABLE TYEGS
+D188;D188;1110 1168 11AB;D188;1110 1168 11AB; # (톈; 톈; á„ᅨᆫ; 톈; á„ᅨᆫ; ) HANGUL SYLLABLE TYEN
+D189;D189;1110 1168 11AC;D189;1110 1168 11AC; # (톉; 톉; á„ᅨᆬ; 톉; á„ᅨᆬ; ) HANGUL SYLLABLE TYENJ
+D18A;D18A;1110 1168 11AD;D18A;1110 1168 11AD; # (톊; 톊; á„ᅨᆭ; 톊; á„ᅨᆭ; ) HANGUL SYLLABLE TYENH
+D18B;D18B;1110 1168 11AE;D18B;1110 1168 11AE; # (톋; 톋; á„ᅨᆮ; 톋; á„ᅨᆮ; ) HANGUL SYLLABLE TYED
+D18C;D18C;1110 1168 11AF;D18C;1110 1168 11AF; # (톌; 톌; á„ᅨᆯ; 톌; á„ᅨᆯ; ) HANGUL SYLLABLE TYEL
+D18D;D18D;1110 1168 11B0;D18D;1110 1168 11B0; # (í†; í†; á„ᅨᆰ; í†; á„ᅨᆰ; ) HANGUL SYLLABLE TYELG
+D18E;D18E;1110 1168 11B1;D18E;1110 1168 11B1; # (톎; 톎; á„ᅨᆱ; 톎; á„ᅨᆱ; ) HANGUL SYLLABLE TYELM
+D18F;D18F;1110 1168 11B2;D18F;1110 1168 11B2; # (í†; í†; á„ᅨᆲ; í†; á„ᅨᆲ; ) HANGUL SYLLABLE TYELB
+D190;D190;1110 1168 11B3;D190;1110 1168 11B3; # (í†; í†; á„ᅨᆳ; í†; á„ᅨᆳ; ) HANGUL SYLLABLE TYELS
+D191;D191;1110 1168 11B4;D191;1110 1168 11B4; # (톑; 톑; á„ᅨᆴ; 톑; á„ᅨᆴ; ) HANGUL SYLLABLE TYELT
+D192;D192;1110 1168 11B5;D192;1110 1168 11B5; # (톒; 톒; á„ᅨᆵ; 톒; á„ᅨᆵ; ) HANGUL SYLLABLE TYELP
+D193;D193;1110 1168 11B6;D193;1110 1168 11B6; # (톓; 톓; á„ᅨᆶ; 톓; á„ᅨᆶ; ) HANGUL SYLLABLE TYELH
+D194;D194;1110 1168 11B7;D194;1110 1168 11B7; # (톔; 톔; á„ᅨᆷ; 톔; á„ᅨᆷ; ) HANGUL SYLLABLE TYEM
+D195;D195;1110 1168 11B8;D195;1110 1168 11B8; # (톕; 톕; á„ᅨᆸ; 톕; á„ᅨᆸ; ) HANGUL SYLLABLE TYEB
+D196;D196;1110 1168 11B9;D196;1110 1168 11B9; # (톖; 톖; á„ᅨᆹ; 톖; á„ᅨᆹ; ) HANGUL SYLLABLE TYEBS
+D197;D197;1110 1168 11BA;D197;1110 1168 11BA; # (톗; 톗; á„ᅨᆺ; 톗; á„ᅨᆺ; ) HANGUL SYLLABLE TYES
+D198;D198;1110 1168 11BB;D198;1110 1168 11BB; # (톘; 톘; á„ᅨᆻ; 톘; á„ᅨᆻ; ) HANGUL SYLLABLE TYESS
+D199;D199;1110 1168 11BC;D199;1110 1168 11BC; # (톙; 톙; á„ᅨᆼ; 톙; á„ᅨᆼ; ) HANGUL SYLLABLE TYENG
+D19A;D19A;1110 1168 11BD;D19A;1110 1168 11BD; # (톚; 톚; á„ᅨᆽ; 톚; á„ᅨᆽ; ) HANGUL SYLLABLE TYEJ
+D19B;D19B;1110 1168 11BE;D19B;1110 1168 11BE; # (톛; 톛; á„ᅨᆾ; 톛; á„ᅨᆾ; ) HANGUL SYLLABLE TYEC
+D19C;D19C;1110 1168 11BF;D19C;1110 1168 11BF; # (톜; 톜; á„ᅨᆿ; 톜; á„ᅨᆿ; ) HANGUL SYLLABLE TYEK
+D19D;D19D;1110 1168 11C0;D19D;1110 1168 11C0; # (í†; í†; á„ᅨᇀ; í†; á„ᅨᇀ; ) HANGUL SYLLABLE TYET
+D19E;D19E;1110 1168 11C1;D19E;1110 1168 11C1; # (톞; 톞; á„á…¨á‡; 톞; á„á…¨á‡; ) HANGUL SYLLABLE TYEP
+D19F;D19F;1110 1168 11C2;D19F;1110 1168 11C2; # (톟; 톟; á„ᅨᇂ; 톟; á„ᅨᇂ; ) HANGUL SYLLABLE TYEH
+D1A0;D1A0;1110 1169;D1A0;1110 1169; # (토; 토; á„á…©; 토; á„á…©; ) HANGUL SYLLABLE TO
+D1A1;D1A1;1110 1169 11A8;D1A1;1110 1169 11A8; # (톡; 톡; á„ᅩᆨ; 톡; á„ᅩᆨ; ) HANGUL SYLLABLE TOG
+D1A2;D1A2;1110 1169 11A9;D1A2;1110 1169 11A9; # (톢; 톢; á„ᅩᆩ; 톢; á„ᅩᆩ; ) HANGUL SYLLABLE TOGG
+D1A3;D1A3;1110 1169 11AA;D1A3;1110 1169 11AA; # (톣; 톣; á„ᅩᆪ; 톣; á„ᅩᆪ; ) HANGUL SYLLABLE TOGS
+D1A4;D1A4;1110 1169 11AB;D1A4;1110 1169 11AB; # (톤; 톤; á„ᅩᆫ; 톤; á„ᅩᆫ; ) HANGUL SYLLABLE TON
+D1A5;D1A5;1110 1169 11AC;D1A5;1110 1169 11AC; # (톥; 톥; á„ᅩᆬ; 톥; á„ᅩᆬ; ) HANGUL SYLLABLE TONJ
+D1A6;D1A6;1110 1169 11AD;D1A6;1110 1169 11AD; # (톦; 톦; á„ᅩᆭ; 톦; á„ᅩᆭ; ) HANGUL SYLLABLE TONH
+D1A7;D1A7;1110 1169 11AE;D1A7;1110 1169 11AE; # (톧; 톧; á„ᅩᆮ; 톧; á„ᅩᆮ; ) HANGUL SYLLABLE TOD
+D1A8;D1A8;1110 1169 11AF;D1A8;1110 1169 11AF; # (톨; 톨; á„ᅩᆯ; 톨; á„ᅩᆯ; ) HANGUL SYLLABLE TOL
+D1A9;D1A9;1110 1169 11B0;D1A9;1110 1169 11B0; # (톩; 톩; á„ᅩᆰ; 톩; á„ᅩᆰ; ) HANGUL SYLLABLE TOLG
+D1AA;D1AA;1110 1169 11B1;D1AA;1110 1169 11B1; # (톪; 톪; á„ᅩᆱ; 톪; á„ᅩᆱ; ) HANGUL SYLLABLE TOLM
+D1AB;D1AB;1110 1169 11B2;D1AB;1110 1169 11B2; # (톫; 톫; á„ᅩᆲ; 톫; á„ᅩᆲ; ) HANGUL SYLLABLE TOLB
+D1AC;D1AC;1110 1169 11B3;D1AC;1110 1169 11B3; # (톬; 톬; á„ᅩᆳ; 톬; á„ᅩᆳ; ) HANGUL SYLLABLE TOLS
+D1AD;D1AD;1110 1169 11B4;D1AD;1110 1169 11B4; # (톭; 톭; á„ᅩᆴ; 톭; á„ᅩᆴ; ) HANGUL SYLLABLE TOLT
+D1AE;D1AE;1110 1169 11B5;D1AE;1110 1169 11B5; # (톮; 톮; á„ᅩᆵ; 톮; á„ᅩᆵ; ) HANGUL SYLLABLE TOLP
+D1AF;D1AF;1110 1169 11B6;D1AF;1110 1169 11B6; # (톯; 톯; á„ᅩᆶ; 톯; á„ᅩᆶ; ) HANGUL SYLLABLE TOLH
+D1B0;D1B0;1110 1169 11B7;D1B0;1110 1169 11B7; # (톰; 톰; á„ᅩᆷ; 톰; á„ᅩᆷ; ) HANGUL SYLLABLE TOM
+D1B1;D1B1;1110 1169 11B8;D1B1;1110 1169 11B8; # (톱; 톱; á„ᅩᆸ; 톱; á„ᅩᆸ; ) HANGUL SYLLABLE TOB
+D1B2;D1B2;1110 1169 11B9;D1B2;1110 1169 11B9; # (톲; 톲; á„ᅩᆹ; 톲; á„ᅩᆹ; ) HANGUL SYLLABLE TOBS
+D1B3;D1B3;1110 1169 11BA;D1B3;1110 1169 11BA; # (톳; 톳; á„ᅩᆺ; 톳; á„ᅩᆺ; ) HANGUL SYLLABLE TOS
+D1B4;D1B4;1110 1169 11BB;D1B4;1110 1169 11BB; # (톴; 톴; á„ᅩᆻ; 톴; á„ᅩᆻ; ) HANGUL SYLLABLE TOSS
+D1B5;D1B5;1110 1169 11BC;D1B5;1110 1169 11BC; # (통; 통; á„ᅩᆼ; 통; á„ᅩᆼ; ) HANGUL SYLLABLE TONG
+D1B6;D1B6;1110 1169 11BD;D1B6;1110 1169 11BD; # (톶; 톶; á„ᅩᆽ; 톶; á„ᅩᆽ; ) HANGUL SYLLABLE TOJ
+D1B7;D1B7;1110 1169 11BE;D1B7;1110 1169 11BE; # (톷; 톷; á„ᅩᆾ; 톷; á„ᅩᆾ; ) HANGUL SYLLABLE TOC
+D1B8;D1B8;1110 1169 11BF;D1B8;1110 1169 11BF; # (톸; 톸; á„ᅩᆿ; 톸; á„ᅩᆿ; ) HANGUL SYLLABLE TOK
+D1B9;D1B9;1110 1169 11C0;D1B9;1110 1169 11C0; # (톹; 톹; á„ᅩᇀ; 톹; á„ᅩᇀ; ) HANGUL SYLLABLE TOT
+D1BA;D1BA;1110 1169 11C1;D1BA;1110 1169 11C1; # (톺; 톺; á„á…©á‡; 톺; á„á…©á‡; ) HANGUL SYLLABLE TOP
+D1BB;D1BB;1110 1169 11C2;D1BB;1110 1169 11C2; # (톻; 톻; á„ᅩᇂ; 톻; á„ᅩᇂ; ) HANGUL SYLLABLE TOH
+D1BC;D1BC;1110 116A;D1BC;1110 116A; # (톼; 톼; á„á…ª; 톼; á„á…ª; ) HANGUL SYLLABLE TWA
+D1BD;D1BD;1110 116A 11A8;D1BD;1110 116A 11A8; # (톽; 톽; á„ᅪᆨ; 톽; á„ᅪᆨ; ) HANGUL SYLLABLE TWAG
+D1BE;D1BE;1110 116A 11A9;D1BE;1110 116A 11A9; # (톾; 톾; á„ᅪᆩ; 톾; á„ᅪᆩ; ) HANGUL SYLLABLE TWAGG
+D1BF;D1BF;1110 116A 11AA;D1BF;1110 116A 11AA; # (톿; 톿; á„ᅪᆪ; 톿; á„ᅪᆪ; ) HANGUL SYLLABLE TWAGS
+D1C0;D1C0;1110 116A 11AB;D1C0;1110 116A 11AB; # (퇀; 퇀; á„ᅪᆫ; 퇀; á„ᅪᆫ; ) HANGUL SYLLABLE TWAN
+D1C1;D1C1;1110 116A 11AC;D1C1;1110 116A 11AC; # (í‡; í‡; á„ᅪᆬ; í‡; á„ᅪᆬ; ) HANGUL SYLLABLE TWANJ
+D1C2;D1C2;1110 116A 11AD;D1C2;1110 116A 11AD; # (퇂; 퇂; á„ᅪᆭ; 퇂; á„ᅪᆭ; ) HANGUL SYLLABLE TWANH
+D1C3;D1C3;1110 116A 11AE;D1C3;1110 116A 11AE; # (퇃; 퇃; á„ᅪᆮ; 퇃; á„ᅪᆮ; ) HANGUL SYLLABLE TWAD
+D1C4;D1C4;1110 116A 11AF;D1C4;1110 116A 11AF; # (퇄; 퇄; á„ᅪᆯ; 퇄; á„ᅪᆯ; ) HANGUL SYLLABLE TWAL
+D1C5;D1C5;1110 116A 11B0;D1C5;1110 116A 11B0; # (퇅; 퇅; á„ᅪᆰ; 퇅; á„ᅪᆰ; ) HANGUL SYLLABLE TWALG
+D1C6;D1C6;1110 116A 11B1;D1C6;1110 116A 11B1; # (퇆; 퇆; á„ᅪᆱ; 퇆; á„ᅪᆱ; ) HANGUL SYLLABLE TWALM
+D1C7;D1C7;1110 116A 11B2;D1C7;1110 116A 11B2; # (퇇; 퇇; á„ᅪᆲ; 퇇; á„ᅪᆲ; ) HANGUL SYLLABLE TWALB
+D1C8;D1C8;1110 116A 11B3;D1C8;1110 116A 11B3; # (퇈; 퇈; á„ᅪᆳ; 퇈; á„ᅪᆳ; ) HANGUL SYLLABLE TWALS
+D1C9;D1C9;1110 116A 11B4;D1C9;1110 116A 11B4; # (퇉; 퇉; á„ᅪᆴ; 퇉; á„ᅪᆴ; ) HANGUL SYLLABLE TWALT
+D1CA;D1CA;1110 116A 11B5;D1CA;1110 116A 11B5; # (퇊; 퇊; á„ᅪᆵ; 퇊; á„ᅪᆵ; ) HANGUL SYLLABLE TWALP
+D1CB;D1CB;1110 116A 11B6;D1CB;1110 116A 11B6; # (퇋; 퇋; á„ᅪᆶ; 퇋; á„ᅪᆶ; ) HANGUL SYLLABLE TWALH
+D1CC;D1CC;1110 116A 11B7;D1CC;1110 116A 11B7; # (퇌; 퇌; á„ᅪᆷ; 퇌; á„ᅪᆷ; ) HANGUL SYLLABLE TWAM
+D1CD;D1CD;1110 116A 11B8;D1CD;1110 116A 11B8; # (í‡; í‡; á„ᅪᆸ; í‡; á„ᅪᆸ; ) HANGUL SYLLABLE TWAB
+D1CE;D1CE;1110 116A 11B9;D1CE;1110 116A 11B9; # (퇎; 퇎; á„ᅪᆹ; 퇎; á„ᅪᆹ; ) HANGUL SYLLABLE TWABS
+D1CF;D1CF;1110 116A 11BA;D1CF;1110 116A 11BA; # (í‡; í‡; á„ᅪᆺ; í‡; á„ᅪᆺ; ) HANGUL SYLLABLE TWAS
+D1D0;D1D0;1110 116A 11BB;D1D0;1110 116A 11BB; # (í‡; í‡; á„ᅪᆻ; í‡; á„ᅪᆻ; ) HANGUL SYLLABLE TWASS
+D1D1;D1D1;1110 116A 11BC;D1D1;1110 116A 11BC; # (퇑; 퇑; á„ᅪᆼ; 퇑; á„ᅪᆼ; ) HANGUL SYLLABLE TWANG
+D1D2;D1D2;1110 116A 11BD;D1D2;1110 116A 11BD; # (퇒; 퇒; á„ᅪᆽ; 퇒; á„ᅪᆽ; ) HANGUL SYLLABLE TWAJ
+D1D3;D1D3;1110 116A 11BE;D1D3;1110 116A 11BE; # (퇓; 퇓; á„ᅪᆾ; 퇓; á„ᅪᆾ; ) HANGUL SYLLABLE TWAC
+D1D4;D1D4;1110 116A 11BF;D1D4;1110 116A 11BF; # (퇔; 퇔; á„ᅪᆿ; 퇔; á„ᅪᆿ; ) HANGUL SYLLABLE TWAK
+D1D5;D1D5;1110 116A 11C0;D1D5;1110 116A 11C0; # (퇕; 퇕; á„ᅪᇀ; 퇕; á„ᅪᇀ; ) HANGUL SYLLABLE TWAT
+D1D6;D1D6;1110 116A 11C1;D1D6;1110 116A 11C1; # (퇖; 퇖; á„á…ªá‡; 퇖; á„á…ªá‡; ) HANGUL SYLLABLE TWAP
+D1D7;D1D7;1110 116A 11C2;D1D7;1110 116A 11C2; # (퇗; 퇗; á„ᅪᇂ; 퇗; á„ᅪᇂ; ) HANGUL SYLLABLE TWAH
+D1D8;D1D8;1110 116B;D1D8;1110 116B; # (퇘; 퇘; á„á…«; 퇘; á„á…«; ) HANGUL SYLLABLE TWAE
+D1D9;D1D9;1110 116B 11A8;D1D9;1110 116B 11A8; # (퇙; 퇙; á„ᅫᆨ; 퇙; á„ᅫᆨ; ) HANGUL SYLLABLE TWAEG
+D1DA;D1DA;1110 116B 11A9;D1DA;1110 116B 11A9; # (퇚; 퇚; á„ᅫᆩ; 퇚; á„ᅫᆩ; ) HANGUL SYLLABLE TWAEGG
+D1DB;D1DB;1110 116B 11AA;D1DB;1110 116B 11AA; # (퇛; 퇛; á„ᅫᆪ; 퇛; á„ᅫᆪ; ) HANGUL SYLLABLE TWAEGS
+D1DC;D1DC;1110 116B 11AB;D1DC;1110 116B 11AB; # (퇜; 퇜; á„ᅫᆫ; 퇜; á„ᅫᆫ; ) HANGUL SYLLABLE TWAEN
+D1DD;D1DD;1110 116B 11AC;D1DD;1110 116B 11AC; # (í‡; í‡; á„ᅫᆬ; í‡; á„ᅫᆬ; ) HANGUL SYLLABLE TWAENJ
+D1DE;D1DE;1110 116B 11AD;D1DE;1110 116B 11AD; # (퇞; 퇞; á„ᅫᆭ; 퇞; á„ᅫᆭ; ) HANGUL SYLLABLE TWAENH
+D1DF;D1DF;1110 116B 11AE;D1DF;1110 116B 11AE; # (퇟; 퇟; á„ᅫᆮ; 퇟; á„ᅫᆮ; ) HANGUL SYLLABLE TWAED
+D1E0;D1E0;1110 116B 11AF;D1E0;1110 116B 11AF; # (퇠; 퇠; á„ᅫᆯ; 퇠; á„ᅫᆯ; ) HANGUL SYLLABLE TWAEL
+D1E1;D1E1;1110 116B 11B0;D1E1;1110 116B 11B0; # (퇡; 퇡; á„ᅫᆰ; 퇡; á„ᅫᆰ; ) HANGUL SYLLABLE TWAELG
+D1E2;D1E2;1110 116B 11B1;D1E2;1110 116B 11B1; # (퇢; 퇢; á„ᅫᆱ; 퇢; á„ᅫᆱ; ) HANGUL SYLLABLE TWAELM
+D1E3;D1E3;1110 116B 11B2;D1E3;1110 116B 11B2; # (퇣; 퇣; á„ᅫᆲ; 퇣; á„ᅫᆲ; ) HANGUL SYLLABLE TWAELB
+D1E4;D1E4;1110 116B 11B3;D1E4;1110 116B 11B3; # (퇤; 퇤; á„ᅫᆳ; 퇤; á„ᅫᆳ; ) HANGUL SYLLABLE TWAELS
+D1E5;D1E5;1110 116B 11B4;D1E5;1110 116B 11B4; # (퇥; 퇥; á„ᅫᆴ; 퇥; á„ᅫᆴ; ) HANGUL SYLLABLE TWAELT
+D1E6;D1E6;1110 116B 11B5;D1E6;1110 116B 11B5; # (퇦; 퇦; á„ᅫᆵ; 퇦; á„ᅫᆵ; ) HANGUL SYLLABLE TWAELP
+D1E7;D1E7;1110 116B 11B6;D1E7;1110 116B 11B6; # (퇧; 퇧; á„ᅫᆶ; 퇧; á„ᅫᆶ; ) HANGUL SYLLABLE TWAELH
+D1E8;D1E8;1110 116B 11B7;D1E8;1110 116B 11B7; # (퇨; 퇨; á„ᅫᆷ; 퇨; á„ᅫᆷ; ) HANGUL SYLLABLE TWAEM
+D1E9;D1E9;1110 116B 11B8;D1E9;1110 116B 11B8; # (퇩; 퇩; á„ᅫᆸ; 퇩; á„ᅫᆸ; ) HANGUL SYLLABLE TWAEB
+D1EA;D1EA;1110 116B 11B9;D1EA;1110 116B 11B9; # (퇪; 퇪; á„ᅫᆹ; 퇪; á„ᅫᆹ; ) HANGUL SYLLABLE TWAEBS
+D1EB;D1EB;1110 116B 11BA;D1EB;1110 116B 11BA; # (퇫; 퇫; á„ᅫᆺ; 퇫; á„ᅫᆺ; ) HANGUL SYLLABLE TWAES
+D1EC;D1EC;1110 116B 11BB;D1EC;1110 116B 11BB; # (퇬; 퇬; á„ᅫᆻ; 퇬; á„ᅫᆻ; ) HANGUL SYLLABLE TWAESS
+D1ED;D1ED;1110 116B 11BC;D1ED;1110 116B 11BC; # (퇭; 퇭; á„ᅫᆼ; 퇭; á„ᅫᆼ; ) HANGUL SYLLABLE TWAENG
+D1EE;D1EE;1110 116B 11BD;D1EE;1110 116B 11BD; # (퇮; 퇮; á„ᅫᆽ; 퇮; á„ᅫᆽ; ) HANGUL SYLLABLE TWAEJ
+D1EF;D1EF;1110 116B 11BE;D1EF;1110 116B 11BE; # (퇯; 퇯; á„ᅫᆾ; 퇯; á„ᅫᆾ; ) HANGUL SYLLABLE TWAEC
+D1F0;D1F0;1110 116B 11BF;D1F0;1110 116B 11BF; # (퇰; 퇰; á„ᅫᆿ; 퇰; á„ᅫᆿ; ) HANGUL SYLLABLE TWAEK
+D1F1;D1F1;1110 116B 11C0;D1F1;1110 116B 11C0; # (퇱; 퇱; á„ᅫᇀ; 퇱; á„ᅫᇀ; ) HANGUL SYLLABLE TWAET
+D1F2;D1F2;1110 116B 11C1;D1F2;1110 116B 11C1; # (퇲; 퇲; á„á…«á‡; 퇲; á„á…«á‡; ) HANGUL SYLLABLE TWAEP
+D1F3;D1F3;1110 116B 11C2;D1F3;1110 116B 11C2; # (퇳; 퇳; á„ᅫᇂ; 퇳; á„ᅫᇂ; ) HANGUL SYLLABLE TWAEH
+D1F4;D1F4;1110 116C;D1F4;1110 116C; # (퇴; 퇴; á„á…¬; 퇴; á„á…¬; ) HANGUL SYLLABLE TOE
+D1F5;D1F5;1110 116C 11A8;D1F5;1110 116C 11A8; # (퇵; 퇵; á„ᅬᆨ; 퇵; á„ᅬᆨ; ) HANGUL SYLLABLE TOEG
+D1F6;D1F6;1110 116C 11A9;D1F6;1110 116C 11A9; # (퇶; 퇶; á„ᅬᆩ; 퇶; á„ᅬᆩ; ) HANGUL SYLLABLE TOEGG
+D1F7;D1F7;1110 116C 11AA;D1F7;1110 116C 11AA; # (퇷; 퇷; á„ᅬᆪ; 퇷; á„ᅬᆪ; ) HANGUL SYLLABLE TOEGS
+D1F8;D1F8;1110 116C 11AB;D1F8;1110 116C 11AB; # (퇸; 퇸; á„ᅬᆫ; 퇸; á„ᅬᆫ; ) HANGUL SYLLABLE TOEN
+D1F9;D1F9;1110 116C 11AC;D1F9;1110 116C 11AC; # (퇹; 퇹; á„ᅬᆬ; 퇹; á„ᅬᆬ; ) HANGUL SYLLABLE TOENJ
+D1FA;D1FA;1110 116C 11AD;D1FA;1110 116C 11AD; # (퇺; 퇺; á„ᅬᆭ; 퇺; á„ᅬᆭ; ) HANGUL SYLLABLE TOENH
+D1FB;D1FB;1110 116C 11AE;D1FB;1110 116C 11AE; # (퇻; 퇻; á„ᅬᆮ; 퇻; á„ᅬᆮ; ) HANGUL SYLLABLE TOED
+D1FC;D1FC;1110 116C 11AF;D1FC;1110 116C 11AF; # (퇼; 퇼; á„ᅬᆯ; 퇼; á„ᅬᆯ; ) HANGUL SYLLABLE TOEL
+D1FD;D1FD;1110 116C 11B0;D1FD;1110 116C 11B0; # (퇽; 퇽; á„ᅬᆰ; 퇽; á„ᅬᆰ; ) HANGUL SYLLABLE TOELG
+D1FE;D1FE;1110 116C 11B1;D1FE;1110 116C 11B1; # (퇾; 퇾; á„ᅬᆱ; 퇾; á„ᅬᆱ; ) HANGUL SYLLABLE TOELM
+D1FF;D1FF;1110 116C 11B2;D1FF;1110 116C 11B2; # (퇿; 퇿; á„ᅬᆲ; 퇿; á„ᅬᆲ; ) HANGUL SYLLABLE TOELB
+D200;D200;1110 116C 11B3;D200;1110 116C 11B3; # (툀; 툀; á„ᅬᆳ; 툀; á„ᅬᆳ; ) HANGUL SYLLABLE TOELS
+D201;D201;1110 116C 11B4;D201;1110 116C 11B4; # (íˆ; íˆ; á„ᅬᆴ; íˆ; á„ᅬᆴ; ) HANGUL SYLLABLE TOELT
+D202;D202;1110 116C 11B5;D202;1110 116C 11B5; # (툂; 툂; á„ᅬᆵ; 툂; á„ᅬᆵ; ) HANGUL SYLLABLE TOELP
+D203;D203;1110 116C 11B6;D203;1110 116C 11B6; # (툃; 툃; á„ᅬᆶ; 툃; á„ᅬᆶ; ) HANGUL SYLLABLE TOELH
+D204;D204;1110 116C 11B7;D204;1110 116C 11B7; # (툄; 툄; á„ᅬᆷ; 툄; á„ᅬᆷ; ) HANGUL SYLLABLE TOEM
+D205;D205;1110 116C 11B8;D205;1110 116C 11B8; # (툅; 툅; á„ᅬᆸ; 툅; á„ᅬᆸ; ) HANGUL SYLLABLE TOEB
+D206;D206;1110 116C 11B9;D206;1110 116C 11B9; # (툆; 툆; á„ᅬᆹ; 툆; á„ᅬᆹ; ) HANGUL SYLLABLE TOEBS
+D207;D207;1110 116C 11BA;D207;1110 116C 11BA; # (툇; 툇; á„ᅬᆺ; 툇; á„ᅬᆺ; ) HANGUL SYLLABLE TOES
+D208;D208;1110 116C 11BB;D208;1110 116C 11BB; # (툈; 툈; á„ᅬᆻ; 툈; á„ᅬᆻ; ) HANGUL SYLLABLE TOESS
+D209;D209;1110 116C 11BC;D209;1110 116C 11BC; # (툉; 툉; á„ᅬᆼ; 툉; á„ᅬᆼ; ) HANGUL SYLLABLE TOENG
+D20A;D20A;1110 116C 11BD;D20A;1110 116C 11BD; # (툊; 툊; á„ᅬᆽ; 툊; á„ᅬᆽ; ) HANGUL SYLLABLE TOEJ
+D20B;D20B;1110 116C 11BE;D20B;1110 116C 11BE; # (툋; 툋; á„ᅬᆾ; 툋; á„ᅬᆾ; ) HANGUL SYLLABLE TOEC
+D20C;D20C;1110 116C 11BF;D20C;1110 116C 11BF; # (툌; 툌; á„ᅬᆿ; 툌; á„ᅬᆿ; ) HANGUL SYLLABLE TOEK
+D20D;D20D;1110 116C 11C0;D20D;1110 116C 11C0; # (íˆ; íˆ; á„ᅬᇀ; íˆ; á„ᅬᇀ; ) HANGUL SYLLABLE TOET
+D20E;D20E;1110 116C 11C1;D20E;1110 116C 11C1; # (툎; 툎; á„á…¬á‡; 툎; á„á…¬á‡; ) HANGUL SYLLABLE TOEP
+D20F;D20F;1110 116C 11C2;D20F;1110 116C 11C2; # (íˆ; íˆ; á„ᅬᇂ; íˆ; á„ᅬᇂ; ) HANGUL SYLLABLE TOEH
+D210;D210;1110 116D;D210;1110 116D; # (íˆ; íˆ; á„á…­; íˆ; á„á…­; ) HANGUL SYLLABLE TYO
+D211;D211;1110 116D 11A8;D211;1110 116D 11A8; # (툑; 툑; á„ᅭᆨ; 툑; á„ᅭᆨ; ) HANGUL SYLLABLE TYOG
+D212;D212;1110 116D 11A9;D212;1110 116D 11A9; # (툒; 툒; á„ᅭᆩ; 툒; á„ᅭᆩ; ) HANGUL SYLLABLE TYOGG
+D213;D213;1110 116D 11AA;D213;1110 116D 11AA; # (툓; 툓; á„ᅭᆪ; 툓; á„ᅭᆪ; ) HANGUL SYLLABLE TYOGS
+D214;D214;1110 116D 11AB;D214;1110 116D 11AB; # (툔; 툔; á„ᅭᆫ; 툔; á„ᅭᆫ; ) HANGUL SYLLABLE TYON
+D215;D215;1110 116D 11AC;D215;1110 116D 11AC; # (툕; 툕; á„ᅭᆬ; 툕; á„ᅭᆬ; ) HANGUL SYLLABLE TYONJ
+D216;D216;1110 116D 11AD;D216;1110 116D 11AD; # (툖; 툖; á„ᅭᆭ; 툖; á„ᅭᆭ; ) HANGUL SYLLABLE TYONH
+D217;D217;1110 116D 11AE;D217;1110 116D 11AE; # (툗; 툗; á„ᅭᆮ; 툗; á„ᅭᆮ; ) HANGUL SYLLABLE TYOD
+D218;D218;1110 116D 11AF;D218;1110 116D 11AF; # (툘; 툘; á„ᅭᆯ; 툘; á„ᅭᆯ; ) HANGUL SYLLABLE TYOL
+D219;D219;1110 116D 11B0;D219;1110 116D 11B0; # (툙; 툙; á„ᅭᆰ; 툙; á„ᅭᆰ; ) HANGUL SYLLABLE TYOLG
+D21A;D21A;1110 116D 11B1;D21A;1110 116D 11B1; # (툚; 툚; á„ᅭᆱ; 툚; á„ᅭᆱ; ) HANGUL SYLLABLE TYOLM
+D21B;D21B;1110 116D 11B2;D21B;1110 116D 11B2; # (툛; 툛; á„ᅭᆲ; 툛; á„ᅭᆲ; ) HANGUL SYLLABLE TYOLB
+D21C;D21C;1110 116D 11B3;D21C;1110 116D 11B3; # (툜; 툜; á„ᅭᆳ; 툜; á„ᅭᆳ; ) HANGUL SYLLABLE TYOLS
+D21D;D21D;1110 116D 11B4;D21D;1110 116D 11B4; # (íˆ; íˆ; á„ᅭᆴ; íˆ; á„ᅭᆴ; ) HANGUL SYLLABLE TYOLT
+D21E;D21E;1110 116D 11B5;D21E;1110 116D 11B5; # (툞; 툞; á„ᅭᆵ; 툞; á„ᅭᆵ; ) HANGUL SYLLABLE TYOLP
+D21F;D21F;1110 116D 11B6;D21F;1110 116D 11B6; # (툟; 툟; á„ᅭᆶ; 툟; á„ᅭᆶ; ) HANGUL SYLLABLE TYOLH
+D220;D220;1110 116D 11B7;D220;1110 116D 11B7; # (툠; 툠; á„ᅭᆷ; 툠; á„ᅭᆷ; ) HANGUL SYLLABLE TYOM
+D221;D221;1110 116D 11B8;D221;1110 116D 11B8; # (툡; 툡; á„ᅭᆸ; 툡; á„ᅭᆸ; ) HANGUL SYLLABLE TYOB
+D222;D222;1110 116D 11B9;D222;1110 116D 11B9; # (툢; 툢; á„ᅭᆹ; 툢; á„ᅭᆹ; ) HANGUL SYLLABLE TYOBS
+D223;D223;1110 116D 11BA;D223;1110 116D 11BA; # (툣; 툣; á„ᅭᆺ; 툣; á„ᅭᆺ; ) HANGUL SYLLABLE TYOS
+D224;D224;1110 116D 11BB;D224;1110 116D 11BB; # (툤; 툤; á„ᅭᆻ; 툤; á„ᅭᆻ; ) HANGUL SYLLABLE TYOSS
+D225;D225;1110 116D 11BC;D225;1110 116D 11BC; # (툥; 툥; á„ᅭᆼ; 툥; á„ᅭᆼ; ) HANGUL SYLLABLE TYONG
+D226;D226;1110 116D 11BD;D226;1110 116D 11BD; # (툦; 툦; á„ᅭᆽ; 툦; á„ᅭᆽ; ) HANGUL SYLLABLE TYOJ
+D227;D227;1110 116D 11BE;D227;1110 116D 11BE; # (툧; 툧; á„ᅭᆾ; 툧; á„ᅭᆾ; ) HANGUL SYLLABLE TYOC
+D228;D228;1110 116D 11BF;D228;1110 116D 11BF; # (툨; 툨; á„ᅭᆿ; 툨; á„ᅭᆿ; ) HANGUL SYLLABLE TYOK
+D229;D229;1110 116D 11C0;D229;1110 116D 11C0; # (툩; 툩; á„ᅭᇀ; 툩; á„ᅭᇀ; ) HANGUL SYLLABLE TYOT
+D22A;D22A;1110 116D 11C1;D22A;1110 116D 11C1; # (툪; 툪; á„á…­á‡; 툪; á„á…­á‡; ) HANGUL SYLLABLE TYOP
+D22B;D22B;1110 116D 11C2;D22B;1110 116D 11C2; # (툫; 툫; á„ᅭᇂ; 툫; á„ᅭᇂ; ) HANGUL SYLLABLE TYOH
+D22C;D22C;1110 116E;D22C;1110 116E; # (투; 투; á„á…®; 투; á„á…®; ) HANGUL SYLLABLE TU
+D22D;D22D;1110 116E 11A8;D22D;1110 116E 11A8; # (툭; 툭; á„ᅮᆨ; 툭; á„ᅮᆨ; ) HANGUL SYLLABLE TUG
+D22E;D22E;1110 116E 11A9;D22E;1110 116E 11A9; # (툮; 툮; á„ᅮᆩ; 툮; á„ᅮᆩ; ) HANGUL SYLLABLE TUGG
+D22F;D22F;1110 116E 11AA;D22F;1110 116E 11AA; # (툯; 툯; á„ᅮᆪ; 툯; á„ᅮᆪ; ) HANGUL SYLLABLE TUGS
+D230;D230;1110 116E 11AB;D230;1110 116E 11AB; # (툰; 툰; á„ᅮᆫ; 툰; á„ᅮᆫ; ) HANGUL SYLLABLE TUN
+D231;D231;1110 116E 11AC;D231;1110 116E 11AC; # (툱; 툱; á„ᅮᆬ; 툱; á„ᅮᆬ; ) HANGUL SYLLABLE TUNJ
+D232;D232;1110 116E 11AD;D232;1110 116E 11AD; # (툲; 툲; á„ᅮᆭ; 툲; á„ᅮᆭ; ) HANGUL SYLLABLE TUNH
+D233;D233;1110 116E 11AE;D233;1110 116E 11AE; # (툳; 툳; á„ᅮᆮ; 툳; á„ᅮᆮ; ) HANGUL SYLLABLE TUD
+D234;D234;1110 116E 11AF;D234;1110 116E 11AF; # (툴; 툴; á„ᅮᆯ; 툴; á„ᅮᆯ; ) HANGUL SYLLABLE TUL
+D235;D235;1110 116E 11B0;D235;1110 116E 11B0; # (툵; 툵; á„ᅮᆰ; 툵; á„ᅮᆰ; ) HANGUL SYLLABLE TULG
+D236;D236;1110 116E 11B1;D236;1110 116E 11B1; # (툶; 툶; á„ᅮᆱ; 툶; á„ᅮᆱ; ) HANGUL SYLLABLE TULM
+D237;D237;1110 116E 11B2;D237;1110 116E 11B2; # (툷; 툷; á„ᅮᆲ; 툷; á„ᅮᆲ; ) HANGUL SYLLABLE TULB
+D238;D238;1110 116E 11B3;D238;1110 116E 11B3; # (툸; 툸; á„ᅮᆳ; 툸; á„ᅮᆳ; ) HANGUL SYLLABLE TULS
+D239;D239;1110 116E 11B4;D239;1110 116E 11B4; # (툹; 툹; á„ᅮᆴ; 툹; á„ᅮᆴ; ) HANGUL SYLLABLE TULT
+D23A;D23A;1110 116E 11B5;D23A;1110 116E 11B5; # (툺; 툺; á„ᅮᆵ; 툺; á„ᅮᆵ; ) HANGUL SYLLABLE TULP
+D23B;D23B;1110 116E 11B6;D23B;1110 116E 11B6; # (툻; 툻; á„ᅮᆶ; 툻; á„ᅮᆶ; ) HANGUL SYLLABLE TULH
+D23C;D23C;1110 116E 11B7;D23C;1110 116E 11B7; # (툼; 툼; á„ᅮᆷ; 툼; á„ᅮᆷ; ) HANGUL SYLLABLE TUM
+D23D;D23D;1110 116E 11B8;D23D;1110 116E 11B8; # (툽; 툽; á„ᅮᆸ; 툽; á„ᅮᆸ; ) HANGUL SYLLABLE TUB
+D23E;D23E;1110 116E 11B9;D23E;1110 116E 11B9; # (툾; 툾; á„ᅮᆹ; 툾; á„ᅮᆹ; ) HANGUL SYLLABLE TUBS
+D23F;D23F;1110 116E 11BA;D23F;1110 116E 11BA; # (툿; 툿; á„ᅮᆺ; 툿; á„ᅮᆺ; ) HANGUL SYLLABLE TUS
+D240;D240;1110 116E 11BB;D240;1110 116E 11BB; # (퉀; 퉀; á„ᅮᆻ; 퉀; á„ᅮᆻ; ) HANGUL SYLLABLE TUSS
+D241;D241;1110 116E 11BC;D241;1110 116E 11BC; # (í‰; í‰; á„ᅮᆼ; í‰; á„ᅮᆼ; ) HANGUL SYLLABLE TUNG
+D242;D242;1110 116E 11BD;D242;1110 116E 11BD; # (퉂; 퉂; á„ᅮᆽ; 퉂; á„ᅮᆽ; ) HANGUL SYLLABLE TUJ
+D243;D243;1110 116E 11BE;D243;1110 116E 11BE; # (퉃; 퉃; á„ᅮᆾ; 퉃; á„ᅮᆾ; ) HANGUL SYLLABLE TUC
+D244;D244;1110 116E 11BF;D244;1110 116E 11BF; # (퉄; 퉄; á„ᅮᆿ; 퉄; á„ᅮᆿ; ) HANGUL SYLLABLE TUK
+D245;D245;1110 116E 11C0;D245;1110 116E 11C0; # (퉅; 퉅; á„ᅮᇀ; 퉅; á„ᅮᇀ; ) HANGUL SYLLABLE TUT
+D246;D246;1110 116E 11C1;D246;1110 116E 11C1; # (퉆; 퉆; á„á…®á‡; 퉆; á„á…®á‡; ) HANGUL SYLLABLE TUP
+D247;D247;1110 116E 11C2;D247;1110 116E 11C2; # (퉇; 퉇; á„ᅮᇂ; 퉇; á„ᅮᇂ; ) HANGUL SYLLABLE TUH
+D248;D248;1110 116F;D248;1110 116F; # (퉈; 퉈; á„á…¯; 퉈; á„á…¯; ) HANGUL SYLLABLE TWEO
+D249;D249;1110 116F 11A8;D249;1110 116F 11A8; # (퉉; 퉉; á„ᅯᆨ; 퉉; á„ᅯᆨ; ) HANGUL SYLLABLE TWEOG
+D24A;D24A;1110 116F 11A9;D24A;1110 116F 11A9; # (퉊; 퉊; á„ᅯᆩ; 퉊; á„ᅯᆩ; ) HANGUL SYLLABLE TWEOGG
+D24B;D24B;1110 116F 11AA;D24B;1110 116F 11AA; # (퉋; 퉋; á„ᅯᆪ; 퉋; á„ᅯᆪ; ) HANGUL SYLLABLE TWEOGS
+D24C;D24C;1110 116F 11AB;D24C;1110 116F 11AB; # (퉌; 퉌; á„ᅯᆫ; 퉌; á„ᅯᆫ; ) HANGUL SYLLABLE TWEON
+D24D;D24D;1110 116F 11AC;D24D;1110 116F 11AC; # (í‰; í‰; á„ᅯᆬ; í‰; á„ᅯᆬ; ) HANGUL SYLLABLE TWEONJ
+D24E;D24E;1110 116F 11AD;D24E;1110 116F 11AD; # (퉎; 퉎; á„ᅯᆭ; 퉎; á„ᅯᆭ; ) HANGUL SYLLABLE TWEONH
+D24F;D24F;1110 116F 11AE;D24F;1110 116F 11AE; # (í‰; í‰; á„ᅯᆮ; í‰; á„ᅯᆮ; ) HANGUL SYLLABLE TWEOD
+D250;D250;1110 116F 11AF;D250;1110 116F 11AF; # (í‰; í‰; á„ᅯᆯ; í‰; á„ᅯᆯ; ) HANGUL SYLLABLE TWEOL
+D251;D251;1110 116F 11B0;D251;1110 116F 11B0; # (퉑; 퉑; á„ᅯᆰ; 퉑; á„ᅯᆰ; ) HANGUL SYLLABLE TWEOLG
+D252;D252;1110 116F 11B1;D252;1110 116F 11B1; # (퉒; 퉒; á„ᅯᆱ; 퉒; á„ᅯᆱ; ) HANGUL SYLLABLE TWEOLM
+D253;D253;1110 116F 11B2;D253;1110 116F 11B2; # (퉓; 퉓; á„ᅯᆲ; 퉓; á„ᅯᆲ; ) HANGUL SYLLABLE TWEOLB
+D254;D254;1110 116F 11B3;D254;1110 116F 11B3; # (퉔; 퉔; á„ᅯᆳ; 퉔; á„ᅯᆳ; ) HANGUL SYLLABLE TWEOLS
+D255;D255;1110 116F 11B4;D255;1110 116F 11B4; # (퉕; 퉕; á„ᅯᆴ; 퉕; á„ᅯᆴ; ) HANGUL SYLLABLE TWEOLT
+D256;D256;1110 116F 11B5;D256;1110 116F 11B5; # (퉖; 퉖; á„ᅯᆵ; 퉖; á„ᅯᆵ; ) HANGUL SYLLABLE TWEOLP
+D257;D257;1110 116F 11B6;D257;1110 116F 11B6; # (퉗; 퉗; á„ᅯᆶ; 퉗; á„ᅯᆶ; ) HANGUL SYLLABLE TWEOLH
+D258;D258;1110 116F 11B7;D258;1110 116F 11B7; # (퉘; 퉘; á„ᅯᆷ; 퉘; á„ᅯᆷ; ) HANGUL SYLLABLE TWEOM
+D259;D259;1110 116F 11B8;D259;1110 116F 11B8; # (퉙; 퉙; á„ᅯᆸ; 퉙; á„ᅯᆸ; ) HANGUL SYLLABLE TWEOB
+D25A;D25A;1110 116F 11B9;D25A;1110 116F 11B9; # (퉚; 퉚; á„ᅯᆹ; 퉚; á„ᅯᆹ; ) HANGUL SYLLABLE TWEOBS
+D25B;D25B;1110 116F 11BA;D25B;1110 116F 11BA; # (퉛; 퉛; á„ᅯᆺ; 퉛; á„ᅯᆺ; ) HANGUL SYLLABLE TWEOS
+D25C;D25C;1110 116F 11BB;D25C;1110 116F 11BB; # (퉜; 퉜; á„ᅯᆻ; 퉜; á„ᅯᆻ; ) HANGUL SYLLABLE TWEOSS
+D25D;D25D;1110 116F 11BC;D25D;1110 116F 11BC; # (í‰; í‰; á„ᅯᆼ; í‰; á„ᅯᆼ; ) HANGUL SYLLABLE TWEONG
+D25E;D25E;1110 116F 11BD;D25E;1110 116F 11BD; # (퉞; 퉞; á„ᅯᆽ; 퉞; á„ᅯᆽ; ) HANGUL SYLLABLE TWEOJ
+D25F;D25F;1110 116F 11BE;D25F;1110 116F 11BE; # (퉟; 퉟; á„ᅯᆾ; 퉟; á„ᅯᆾ; ) HANGUL SYLLABLE TWEOC
+D260;D260;1110 116F 11BF;D260;1110 116F 11BF; # (퉠; 퉠; á„ᅯᆿ; 퉠; á„ᅯᆿ; ) HANGUL SYLLABLE TWEOK
+D261;D261;1110 116F 11C0;D261;1110 116F 11C0; # (퉡; 퉡; á„ᅯᇀ; 퉡; á„ᅯᇀ; ) HANGUL SYLLABLE TWEOT
+D262;D262;1110 116F 11C1;D262;1110 116F 11C1; # (퉢; 퉢; á„á…¯á‡; 퉢; á„á…¯á‡; ) HANGUL SYLLABLE TWEOP
+D263;D263;1110 116F 11C2;D263;1110 116F 11C2; # (퉣; 퉣; á„ᅯᇂ; 퉣; á„ᅯᇂ; ) HANGUL SYLLABLE TWEOH
+D264;D264;1110 1170;D264;1110 1170; # (퉤; 퉤; á„á…°; 퉤; á„á…°; ) HANGUL SYLLABLE TWE
+D265;D265;1110 1170 11A8;D265;1110 1170 11A8; # (퉥; 퉥; á„ᅰᆨ; 퉥; á„ᅰᆨ; ) HANGUL SYLLABLE TWEG
+D266;D266;1110 1170 11A9;D266;1110 1170 11A9; # (퉦; 퉦; á„ᅰᆩ; 퉦; á„ᅰᆩ; ) HANGUL SYLLABLE TWEGG
+D267;D267;1110 1170 11AA;D267;1110 1170 11AA; # (퉧; 퉧; á„ᅰᆪ; 퉧; á„ᅰᆪ; ) HANGUL SYLLABLE TWEGS
+D268;D268;1110 1170 11AB;D268;1110 1170 11AB; # (퉨; 퉨; á„ᅰᆫ; 퉨; á„ᅰᆫ; ) HANGUL SYLLABLE TWEN
+D269;D269;1110 1170 11AC;D269;1110 1170 11AC; # (퉩; 퉩; á„ᅰᆬ; 퉩; á„ᅰᆬ; ) HANGUL SYLLABLE TWENJ
+D26A;D26A;1110 1170 11AD;D26A;1110 1170 11AD; # (퉪; 퉪; á„ᅰᆭ; 퉪; á„ᅰᆭ; ) HANGUL SYLLABLE TWENH
+D26B;D26B;1110 1170 11AE;D26B;1110 1170 11AE; # (퉫; 퉫; á„ᅰᆮ; 퉫; á„ᅰᆮ; ) HANGUL SYLLABLE TWED
+D26C;D26C;1110 1170 11AF;D26C;1110 1170 11AF; # (퉬; 퉬; á„ᅰᆯ; 퉬; á„ᅰᆯ; ) HANGUL SYLLABLE TWEL
+D26D;D26D;1110 1170 11B0;D26D;1110 1170 11B0; # (퉭; 퉭; á„ᅰᆰ; 퉭; á„ᅰᆰ; ) HANGUL SYLLABLE TWELG
+D26E;D26E;1110 1170 11B1;D26E;1110 1170 11B1; # (퉮; 퉮; á„ᅰᆱ; 퉮; á„ᅰᆱ; ) HANGUL SYLLABLE TWELM
+D26F;D26F;1110 1170 11B2;D26F;1110 1170 11B2; # (퉯; 퉯; á„ᅰᆲ; 퉯; á„ᅰᆲ; ) HANGUL SYLLABLE TWELB
+D270;D270;1110 1170 11B3;D270;1110 1170 11B3; # (퉰; 퉰; á„ᅰᆳ; 퉰; á„ᅰᆳ; ) HANGUL SYLLABLE TWELS
+D271;D271;1110 1170 11B4;D271;1110 1170 11B4; # (퉱; 퉱; á„ᅰᆴ; 퉱; á„ᅰᆴ; ) HANGUL SYLLABLE TWELT
+D272;D272;1110 1170 11B5;D272;1110 1170 11B5; # (퉲; 퉲; á„ᅰᆵ; 퉲; á„ᅰᆵ; ) HANGUL SYLLABLE TWELP
+D273;D273;1110 1170 11B6;D273;1110 1170 11B6; # (퉳; 퉳; á„ᅰᆶ; 퉳; á„ᅰᆶ; ) HANGUL SYLLABLE TWELH
+D274;D274;1110 1170 11B7;D274;1110 1170 11B7; # (퉴; 퉴; á„ᅰᆷ; 퉴; á„ᅰᆷ; ) HANGUL SYLLABLE TWEM
+D275;D275;1110 1170 11B8;D275;1110 1170 11B8; # (퉵; 퉵; á„ᅰᆸ; 퉵; á„ᅰᆸ; ) HANGUL SYLLABLE TWEB
+D276;D276;1110 1170 11B9;D276;1110 1170 11B9; # (퉶; 퉶; á„ᅰᆹ; 퉶; á„ᅰᆹ; ) HANGUL SYLLABLE TWEBS
+D277;D277;1110 1170 11BA;D277;1110 1170 11BA; # (퉷; 퉷; á„ᅰᆺ; 퉷; á„ᅰᆺ; ) HANGUL SYLLABLE TWES
+D278;D278;1110 1170 11BB;D278;1110 1170 11BB; # (퉸; 퉸; á„ᅰᆻ; 퉸; á„ᅰᆻ; ) HANGUL SYLLABLE TWESS
+D279;D279;1110 1170 11BC;D279;1110 1170 11BC; # (퉹; 퉹; á„ᅰᆼ; 퉹; á„ᅰᆼ; ) HANGUL SYLLABLE TWENG
+D27A;D27A;1110 1170 11BD;D27A;1110 1170 11BD; # (퉺; 퉺; á„ᅰᆽ; 퉺; á„ᅰᆽ; ) HANGUL SYLLABLE TWEJ
+D27B;D27B;1110 1170 11BE;D27B;1110 1170 11BE; # (퉻; 퉻; á„ᅰᆾ; 퉻; á„ᅰᆾ; ) HANGUL SYLLABLE TWEC
+D27C;D27C;1110 1170 11BF;D27C;1110 1170 11BF; # (퉼; 퉼; á„ᅰᆿ; 퉼; á„ᅰᆿ; ) HANGUL SYLLABLE TWEK
+D27D;D27D;1110 1170 11C0;D27D;1110 1170 11C0; # (퉽; 퉽; á„ᅰᇀ; 퉽; á„ᅰᇀ; ) HANGUL SYLLABLE TWET
+D27E;D27E;1110 1170 11C1;D27E;1110 1170 11C1; # (퉾; 퉾; á„á…°á‡; 퉾; á„á…°á‡; ) HANGUL SYLLABLE TWEP
+D27F;D27F;1110 1170 11C2;D27F;1110 1170 11C2; # (퉿; 퉿; á„ᅰᇂ; 퉿; á„ᅰᇂ; ) HANGUL SYLLABLE TWEH
+D280;D280;1110 1171;D280;1110 1171; # (튀; 튀; á„á…±; 튀; á„á…±; ) HANGUL SYLLABLE TWI
+D281;D281;1110 1171 11A8;D281;1110 1171 11A8; # (íŠ; íŠ; á„ᅱᆨ; íŠ; á„ᅱᆨ; ) HANGUL SYLLABLE TWIG
+D282;D282;1110 1171 11A9;D282;1110 1171 11A9; # (튂; 튂; á„ᅱᆩ; 튂; á„ᅱᆩ; ) HANGUL SYLLABLE TWIGG
+D283;D283;1110 1171 11AA;D283;1110 1171 11AA; # (튃; 튃; á„ᅱᆪ; 튃; á„ᅱᆪ; ) HANGUL SYLLABLE TWIGS
+D284;D284;1110 1171 11AB;D284;1110 1171 11AB; # (튄; 튄; á„ᅱᆫ; 튄; á„ᅱᆫ; ) HANGUL SYLLABLE TWIN
+D285;D285;1110 1171 11AC;D285;1110 1171 11AC; # (튅; 튅; á„ᅱᆬ; 튅; á„ᅱᆬ; ) HANGUL SYLLABLE TWINJ
+D286;D286;1110 1171 11AD;D286;1110 1171 11AD; # (튆; 튆; á„ᅱᆭ; 튆; á„ᅱᆭ; ) HANGUL SYLLABLE TWINH
+D287;D287;1110 1171 11AE;D287;1110 1171 11AE; # (튇; 튇; á„ᅱᆮ; 튇; á„ᅱᆮ; ) HANGUL SYLLABLE TWID
+D288;D288;1110 1171 11AF;D288;1110 1171 11AF; # (튈; 튈; á„ᅱᆯ; 튈; á„ᅱᆯ; ) HANGUL SYLLABLE TWIL
+D289;D289;1110 1171 11B0;D289;1110 1171 11B0; # (튉; 튉; á„ᅱᆰ; 튉; á„ᅱᆰ; ) HANGUL SYLLABLE TWILG
+D28A;D28A;1110 1171 11B1;D28A;1110 1171 11B1; # (튊; 튊; á„ᅱᆱ; 튊; á„ᅱᆱ; ) HANGUL SYLLABLE TWILM
+D28B;D28B;1110 1171 11B2;D28B;1110 1171 11B2; # (튋; 튋; á„ᅱᆲ; 튋; á„ᅱᆲ; ) HANGUL SYLLABLE TWILB
+D28C;D28C;1110 1171 11B3;D28C;1110 1171 11B3; # (튌; 튌; á„ᅱᆳ; 튌; á„ᅱᆳ; ) HANGUL SYLLABLE TWILS
+D28D;D28D;1110 1171 11B4;D28D;1110 1171 11B4; # (íŠ; íŠ; á„ᅱᆴ; íŠ; á„ᅱᆴ; ) HANGUL SYLLABLE TWILT
+D28E;D28E;1110 1171 11B5;D28E;1110 1171 11B5; # (튎; 튎; á„ᅱᆵ; 튎; á„ᅱᆵ; ) HANGUL SYLLABLE TWILP
+D28F;D28F;1110 1171 11B6;D28F;1110 1171 11B6; # (íŠ; íŠ; á„ᅱᆶ; íŠ; á„ᅱᆶ; ) HANGUL SYLLABLE TWILH
+D290;D290;1110 1171 11B7;D290;1110 1171 11B7; # (íŠ; íŠ; á„ᅱᆷ; íŠ; á„ᅱᆷ; ) HANGUL SYLLABLE TWIM
+D291;D291;1110 1171 11B8;D291;1110 1171 11B8; # (튑; 튑; á„ᅱᆸ; 튑; á„ᅱᆸ; ) HANGUL SYLLABLE TWIB
+D292;D292;1110 1171 11B9;D292;1110 1171 11B9; # (튒; 튒; á„ᅱᆹ; 튒; á„ᅱᆹ; ) HANGUL SYLLABLE TWIBS
+D293;D293;1110 1171 11BA;D293;1110 1171 11BA; # (튓; 튓; á„ᅱᆺ; 튓; á„ᅱᆺ; ) HANGUL SYLLABLE TWIS
+D294;D294;1110 1171 11BB;D294;1110 1171 11BB; # (튔; 튔; á„ᅱᆻ; 튔; á„ᅱᆻ; ) HANGUL SYLLABLE TWISS
+D295;D295;1110 1171 11BC;D295;1110 1171 11BC; # (튕; 튕; á„ᅱᆼ; 튕; á„ᅱᆼ; ) HANGUL SYLLABLE TWING
+D296;D296;1110 1171 11BD;D296;1110 1171 11BD; # (튖; 튖; á„ᅱᆽ; 튖; á„ᅱᆽ; ) HANGUL SYLLABLE TWIJ
+D297;D297;1110 1171 11BE;D297;1110 1171 11BE; # (튗; 튗; á„ᅱᆾ; 튗; á„ᅱᆾ; ) HANGUL SYLLABLE TWIC
+D298;D298;1110 1171 11BF;D298;1110 1171 11BF; # (튘; 튘; á„ᅱᆿ; 튘; á„ᅱᆿ; ) HANGUL SYLLABLE TWIK
+D299;D299;1110 1171 11C0;D299;1110 1171 11C0; # (튙; 튙; á„ᅱᇀ; 튙; á„ᅱᇀ; ) HANGUL SYLLABLE TWIT
+D29A;D29A;1110 1171 11C1;D29A;1110 1171 11C1; # (튚; 튚; á„á…±á‡; 튚; á„á…±á‡; ) HANGUL SYLLABLE TWIP
+D29B;D29B;1110 1171 11C2;D29B;1110 1171 11C2; # (튛; 튛; á„ᅱᇂ; 튛; á„ᅱᇂ; ) HANGUL SYLLABLE TWIH
+D29C;D29C;1110 1172;D29C;1110 1172; # (튜; 튜; á„á…²; 튜; á„á…²; ) HANGUL SYLLABLE TYU
+D29D;D29D;1110 1172 11A8;D29D;1110 1172 11A8; # (íŠ; íŠ; á„ᅲᆨ; íŠ; á„ᅲᆨ; ) HANGUL SYLLABLE TYUG
+D29E;D29E;1110 1172 11A9;D29E;1110 1172 11A9; # (튞; 튞; á„ᅲᆩ; 튞; á„ᅲᆩ; ) HANGUL SYLLABLE TYUGG
+D29F;D29F;1110 1172 11AA;D29F;1110 1172 11AA; # (튟; 튟; á„ᅲᆪ; 튟; á„ᅲᆪ; ) HANGUL SYLLABLE TYUGS
+D2A0;D2A0;1110 1172 11AB;D2A0;1110 1172 11AB; # (튠; 튠; á„ᅲᆫ; 튠; á„ᅲᆫ; ) HANGUL SYLLABLE TYUN
+D2A1;D2A1;1110 1172 11AC;D2A1;1110 1172 11AC; # (튡; 튡; á„ᅲᆬ; 튡; á„ᅲᆬ; ) HANGUL SYLLABLE TYUNJ
+D2A2;D2A2;1110 1172 11AD;D2A2;1110 1172 11AD; # (튢; 튢; á„ᅲᆭ; 튢; á„ᅲᆭ; ) HANGUL SYLLABLE TYUNH
+D2A3;D2A3;1110 1172 11AE;D2A3;1110 1172 11AE; # (튣; 튣; á„ᅲᆮ; 튣; á„ᅲᆮ; ) HANGUL SYLLABLE TYUD
+D2A4;D2A4;1110 1172 11AF;D2A4;1110 1172 11AF; # (튤; 튤; á„ᅲᆯ; 튤; á„ᅲᆯ; ) HANGUL SYLLABLE TYUL
+D2A5;D2A5;1110 1172 11B0;D2A5;1110 1172 11B0; # (튥; 튥; á„ᅲᆰ; 튥; á„ᅲᆰ; ) HANGUL SYLLABLE TYULG
+D2A6;D2A6;1110 1172 11B1;D2A6;1110 1172 11B1; # (튦; 튦; á„ᅲᆱ; 튦; á„ᅲᆱ; ) HANGUL SYLLABLE TYULM
+D2A7;D2A7;1110 1172 11B2;D2A7;1110 1172 11B2; # (튧; 튧; á„ᅲᆲ; 튧; á„ᅲᆲ; ) HANGUL SYLLABLE TYULB
+D2A8;D2A8;1110 1172 11B3;D2A8;1110 1172 11B3; # (튨; 튨; á„ᅲᆳ; 튨; á„ᅲᆳ; ) HANGUL SYLLABLE TYULS
+D2A9;D2A9;1110 1172 11B4;D2A9;1110 1172 11B4; # (튩; 튩; á„ᅲᆴ; 튩; á„ᅲᆴ; ) HANGUL SYLLABLE TYULT
+D2AA;D2AA;1110 1172 11B5;D2AA;1110 1172 11B5; # (튪; 튪; á„ᅲᆵ; 튪; á„ᅲᆵ; ) HANGUL SYLLABLE TYULP
+D2AB;D2AB;1110 1172 11B6;D2AB;1110 1172 11B6; # (튫; 튫; á„ᅲᆶ; 튫; á„ᅲᆶ; ) HANGUL SYLLABLE TYULH
+D2AC;D2AC;1110 1172 11B7;D2AC;1110 1172 11B7; # (튬; 튬; á„ᅲᆷ; 튬; á„ᅲᆷ; ) HANGUL SYLLABLE TYUM
+D2AD;D2AD;1110 1172 11B8;D2AD;1110 1172 11B8; # (튭; 튭; á„ᅲᆸ; 튭; á„ᅲᆸ; ) HANGUL SYLLABLE TYUB
+D2AE;D2AE;1110 1172 11B9;D2AE;1110 1172 11B9; # (튮; 튮; á„ᅲᆹ; 튮; á„ᅲᆹ; ) HANGUL SYLLABLE TYUBS
+D2AF;D2AF;1110 1172 11BA;D2AF;1110 1172 11BA; # (튯; 튯; á„ᅲᆺ; 튯; á„ᅲᆺ; ) HANGUL SYLLABLE TYUS
+D2B0;D2B0;1110 1172 11BB;D2B0;1110 1172 11BB; # (튰; 튰; á„ᅲᆻ; 튰; á„ᅲᆻ; ) HANGUL SYLLABLE TYUSS
+D2B1;D2B1;1110 1172 11BC;D2B1;1110 1172 11BC; # (튱; 튱; á„ᅲᆼ; 튱; á„ᅲᆼ; ) HANGUL SYLLABLE TYUNG
+D2B2;D2B2;1110 1172 11BD;D2B2;1110 1172 11BD; # (튲; 튲; á„ᅲᆽ; 튲; á„ᅲᆽ; ) HANGUL SYLLABLE TYUJ
+D2B3;D2B3;1110 1172 11BE;D2B3;1110 1172 11BE; # (튳; 튳; á„ᅲᆾ; 튳; á„ᅲᆾ; ) HANGUL SYLLABLE TYUC
+D2B4;D2B4;1110 1172 11BF;D2B4;1110 1172 11BF; # (튴; 튴; á„ᅲᆿ; 튴; á„ᅲᆿ; ) HANGUL SYLLABLE TYUK
+D2B5;D2B5;1110 1172 11C0;D2B5;1110 1172 11C0; # (튵; 튵; á„ᅲᇀ; 튵; á„ᅲᇀ; ) HANGUL SYLLABLE TYUT
+D2B6;D2B6;1110 1172 11C1;D2B6;1110 1172 11C1; # (튶; 튶; á„á…²á‡; 튶; á„á…²á‡; ) HANGUL SYLLABLE TYUP
+D2B7;D2B7;1110 1172 11C2;D2B7;1110 1172 11C2; # (튷; 튷; á„ᅲᇂ; 튷; á„ᅲᇂ; ) HANGUL SYLLABLE TYUH
+D2B8;D2B8;1110 1173;D2B8;1110 1173; # (트; 트; á„á…³; 트; á„á…³; ) HANGUL SYLLABLE TEU
+D2B9;D2B9;1110 1173 11A8;D2B9;1110 1173 11A8; # (특; 특; á„ᅳᆨ; 특; á„ᅳᆨ; ) HANGUL SYLLABLE TEUG
+D2BA;D2BA;1110 1173 11A9;D2BA;1110 1173 11A9; # (튺; 튺; á„ᅳᆩ; 튺; á„ᅳᆩ; ) HANGUL SYLLABLE TEUGG
+D2BB;D2BB;1110 1173 11AA;D2BB;1110 1173 11AA; # (튻; 튻; á„ᅳᆪ; 튻; á„ᅳᆪ; ) HANGUL SYLLABLE TEUGS
+D2BC;D2BC;1110 1173 11AB;D2BC;1110 1173 11AB; # (튼; 튼; á„ᅳᆫ; 튼; á„ᅳᆫ; ) HANGUL SYLLABLE TEUN
+D2BD;D2BD;1110 1173 11AC;D2BD;1110 1173 11AC; # (튽; 튽; á„ᅳᆬ; 튽; á„ᅳᆬ; ) HANGUL SYLLABLE TEUNJ
+D2BE;D2BE;1110 1173 11AD;D2BE;1110 1173 11AD; # (튾; 튾; á„ᅳᆭ; 튾; á„ᅳᆭ; ) HANGUL SYLLABLE TEUNH
+D2BF;D2BF;1110 1173 11AE;D2BF;1110 1173 11AE; # (튿; 튿; á„ᅳᆮ; 튿; á„ᅳᆮ; ) HANGUL SYLLABLE TEUD
+D2C0;D2C0;1110 1173 11AF;D2C0;1110 1173 11AF; # (í‹€; í‹€; á„ᅳᆯ; í‹€; á„ᅳᆯ; ) HANGUL SYLLABLE TEUL
+D2C1;D2C1;1110 1173 11B0;D2C1;1110 1173 11B0; # (í‹; í‹; á„ᅳᆰ; í‹; á„ᅳᆰ; ) HANGUL SYLLABLE TEULG
+D2C2;D2C2;1110 1173 11B1;D2C2;1110 1173 11B1; # (í‹‚; í‹‚; á„ᅳᆱ; í‹‚; á„ᅳᆱ; ) HANGUL SYLLABLE TEULM
+D2C3;D2C3;1110 1173 11B2;D2C3;1110 1173 11B2; # (틃; 틃; á„ᅳᆲ; 틃; á„ᅳᆲ; ) HANGUL SYLLABLE TEULB
+D2C4;D2C4;1110 1173 11B3;D2C4;1110 1173 11B3; # (í‹„; í‹„; á„ᅳᆳ; í‹„; á„ᅳᆳ; ) HANGUL SYLLABLE TEULS
+D2C5;D2C5;1110 1173 11B4;D2C5;1110 1173 11B4; # (í‹…; í‹…; á„ᅳᆴ; í‹…; á„ᅳᆴ; ) HANGUL SYLLABLE TEULT
+D2C6;D2C6;1110 1173 11B5;D2C6;1110 1173 11B5; # (틆; 틆; á„ᅳᆵ; 틆; á„ᅳᆵ; ) HANGUL SYLLABLE TEULP
+D2C7;D2C7;1110 1173 11B6;D2C7;1110 1173 11B6; # (틇; 틇; á„ᅳᆶ; 틇; á„ᅳᆶ; ) HANGUL SYLLABLE TEULH
+D2C8;D2C8;1110 1173 11B7;D2C8;1110 1173 11B7; # (틈; 틈; á„ᅳᆷ; 틈; á„ᅳᆷ; ) HANGUL SYLLABLE TEUM
+D2C9;D2C9;1110 1173 11B8;D2C9;1110 1173 11B8; # (틉; 틉; á„ᅳᆸ; 틉; á„ᅳᆸ; ) HANGUL SYLLABLE TEUB
+D2CA;D2CA;1110 1173 11B9;D2CA;1110 1173 11B9; # (틊; 틊; á„ᅳᆹ; 틊; á„ᅳᆹ; ) HANGUL SYLLABLE TEUBS
+D2CB;D2CB;1110 1173 11BA;D2CB;1110 1173 11BA; # (í‹‹; í‹‹; á„ᅳᆺ; í‹‹; á„ᅳᆺ; ) HANGUL SYLLABLE TEUS
+D2CC;D2CC;1110 1173 11BB;D2CC;1110 1173 11BB; # (틌; 틌; á„ᅳᆻ; 틌; á„ᅳᆻ; ) HANGUL SYLLABLE TEUSS
+D2CD;D2CD;1110 1173 11BC;D2CD;1110 1173 11BC; # (í‹; í‹; á„ᅳᆼ; í‹; á„ᅳᆼ; ) HANGUL SYLLABLE TEUNG
+D2CE;D2CE;1110 1173 11BD;D2CE;1110 1173 11BD; # (틎; 틎; á„ᅳᆽ; 틎; á„ᅳᆽ; ) HANGUL SYLLABLE TEUJ
+D2CF;D2CF;1110 1173 11BE;D2CF;1110 1173 11BE; # (í‹; í‹; á„ᅳᆾ; í‹; á„ᅳᆾ; ) HANGUL SYLLABLE TEUC
+D2D0;D2D0;1110 1173 11BF;D2D0;1110 1173 11BF; # (í‹; í‹; á„ᅳᆿ; í‹; á„ᅳᆿ; ) HANGUL SYLLABLE TEUK
+D2D1;D2D1;1110 1173 11C0;D2D1;1110 1173 11C0; # (í‹‘; í‹‘; á„ᅳᇀ; í‹‘; á„ᅳᇀ; ) HANGUL SYLLABLE TEUT
+D2D2;D2D2;1110 1173 11C1;D2D2;1110 1173 11C1; # (í‹’; í‹’; á„á…³á‡; í‹’; á„á…³á‡; ) HANGUL SYLLABLE TEUP
+D2D3;D2D3;1110 1173 11C2;D2D3;1110 1173 11C2; # (í‹“; í‹“; á„ᅳᇂ; í‹“; á„ᅳᇂ; ) HANGUL SYLLABLE TEUH
+D2D4;D2D4;1110 1174;D2D4;1110 1174; # (í‹”; í‹”; á„á…´; í‹”; á„á…´; ) HANGUL SYLLABLE TYI
+D2D5;D2D5;1110 1174 11A8;D2D5;1110 1174 11A8; # (í‹•; í‹•; á„ᅴᆨ; í‹•; á„ᅴᆨ; ) HANGUL SYLLABLE TYIG
+D2D6;D2D6;1110 1174 11A9;D2D6;1110 1174 11A9; # (í‹–; í‹–; á„ᅴᆩ; í‹–; á„ᅴᆩ; ) HANGUL SYLLABLE TYIGG
+D2D7;D2D7;1110 1174 11AA;D2D7;1110 1174 11AA; # (í‹—; í‹—; á„ᅴᆪ; í‹—; á„ᅴᆪ; ) HANGUL SYLLABLE TYIGS
+D2D8;D2D8;1110 1174 11AB;D2D8;1110 1174 11AB; # (틘; 틘; á„ᅴᆫ; 틘; á„ᅴᆫ; ) HANGUL SYLLABLE TYIN
+D2D9;D2D9;1110 1174 11AC;D2D9;1110 1174 11AC; # (í‹™; í‹™; á„ᅴᆬ; í‹™; á„ᅴᆬ; ) HANGUL SYLLABLE TYINJ
+D2DA;D2DA;1110 1174 11AD;D2DA;1110 1174 11AD; # (틚; 틚; á„ᅴᆭ; 틚; á„ᅴᆭ; ) HANGUL SYLLABLE TYINH
+D2DB;D2DB;1110 1174 11AE;D2DB;1110 1174 11AE; # (í‹›; í‹›; á„ᅴᆮ; í‹›; á„ᅴᆮ; ) HANGUL SYLLABLE TYID
+D2DC;D2DC;1110 1174 11AF;D2DC;1110 1174 11AF; # (틜; 틜; á„ᅴᆯ; 틜; á„ᅴᆯ; ) HANGUL SYLLABLE TYIL
+D2DD;D2DD;1110 1174 11B0;D2DD;1110 1174 11B0; # (í‹; í‹; á„ᅴᆰ; í‹; á„ᅴᆰ; ) HANGUL SYLLABLE TYILG
+D2DE;D2DE;1110 1174 11B1;D2DE;1110 1174 11B1; # (틞; 틞; á„ᅴᆱ; 틞; á„ᅴᆱ; ) HANGUL SYLLABLE TYILM
+D2DF;D2DF;1110 1174 11B2;D2DF;1110 1174 11B2; # (틟; 틟; á„ᅴᆲ; 틟; á„ᅴᆲ; ) HANGUL SYLLABLE TYILB
+D2E0;D2E0;1110 1174 11B3;D2E0;1110 1174 11B3; # (í‹ ; í‹ ; á„ᅴᆳ; í‹ ; á„ᅴᆳ; ) HANGUL SYLLABLE TYILS
+D2E1;D2E1;1110 1174 11B4;D2E1;1110 1174 11B4; # (í‹¡; í‹¡; á„ᅴᆴ; í‹¡; á„ᅴᆴ; ) HANGUL SYLLABLE TYILT
+D2E2;D2E2;1110 1174 11B5;D2E2;1110 1174 11B5; # (í‹¢; í‹¢; á„ᅴᆵ; í‹¢; á„ᅴᆵ; ) HANGUL SYLLABLE TYILP
+D2E3;D2E3;1110 1174 11B6;D2E3;1110 1174 11B6; # (í‹£; í‹£; á„ᅴᆶ; í‹£; á„ᅴᆶ; ) HANGUL SYLLABLE TYILH
+D2E4;D2E4;1110 1174 11B7;D2E4;1110 1174 11B7; # (틤; 틤; á„ᅴᆷ; 틤; á„ᅴᆷ; ) HANGUL SYLLABLE TYIM
+D2E5;D2E5;1110 1174 11B8;D2E5;1110 1174 11B8; # (í‹¥; í‹¥; á„ᅴᆸ; í‹¥; á„ᅴᆸ; ) HANGUL SYLLABLE TYIB
+D2E6;D2E6;1110 1174 11B9;D2E6;1110 1174 11B9; # (틦; 틦; á„ᅴᆹ; 틦; á„ᅴᆹ; ) HANGUL SYLLABLE TYIBS
+D2E7;D2E7;1110 1174 11BA;D2E7;1110 1174 11BA; # (í‹§; í‹§; á„ᅴᆺ; í‹§; á„ᅴᆺ; ) HANGUL SYLLABLE TYIS
+D2E8;D2E8;1110 1174 11BB;D2E8;1110 1174 11BB; # (틨; 틨; á„ᅴᆻ; 틨; á„ᅴᆻ; ) HANGUL SYLLABLE TYISS
+D2E9;D2E9;1110 1174 11BC;D2E9;1110 1174 11BC; # (í‹©; í‹©; á„ᅴᆼ; í‹©; á„ᅴᆼ; ) HANGUL SYLLABLE TYING
+D2EA;D2EA;1110 1174 11BD;D2EA;1110 1174 11BD; # (틪; 틪; á„ᅴᆽ; 틪; á„ᅴᆽ; ) HANGUL SYLLABLE TYIJ
+D2EB;D2EB;1110 1174 11BE;D2EB;1110 1174 11BE; # (í‹«; í‹«; á„ᅴᆾ; í‹«; á„ᅴᆾ; ) HANGUL SYLLABLE TYIC
+D2EC;D2EC;1110 1174 11BF;D2EC;1110 1174 11BF; # (틬; 틬; á„ᅴᆿ; 틬; á„ᅴᆿ; ) HANGUL SYLLABLE TYIK
+D2ED;D2ED;1110 1174 11C0;D2ED;1110 1174 11C0; # (í‹­; í‹­; á„ᅴᇀ; í‹­; á„ᅴᇀ; ) HANGUL SYLLABLE TYIT
+D2EE;D2EE;1110 1174 11C1;D2EE;1110 1174 11C1; # (í‹®; í‹®; á„á…´á‡; í‹®; á„á…´á‡; ) HANGUL SYLLABLE TYIP
+D2EF;D2EF;1110 1174 11C2;D2EF;1110 1174 11C2; # (틯; 틯; á„ᅴᇂ; 틯; á„ᅴᇂ; ) HANGUL SYLLABLE TYIH
+D2F0;D2F0;1110 1175;D2F0;1110 1175; # (í‹°; í‹°; á„á…µ; í‹°; á„á…µ; ) HANGUL SYLLABLE TI
+D2F1;D2F1;1110 1175 11A8;D2F1;1110 1175 11A8; # (틱; 틱; á„ᅵᆨ; 틱; á„ᅵᆨ; ) HANGUL SYLLABLE TIG
+D2F2;D2F2;1110 1175 11A9;D2F2;1110 1175 11A9; # (틲; 틲; á„ᅵᆩ; 틲; á„ᅵᆩ; ) HANGUL SYLLABLE TIGG
+D2F3;D2F3;1110 1175 11AA;D2F3;1110 1175 11AA; # (틳; 틳; á„ᅵᆪ; 틳; á„ᅵᆪ; ) HANGUL SYLLABLE TIGS
+D2F4;D2F4;1110 1175 11AB;D2F4;1110 1175 11AB; # (í‹´; í‹´; á„ᅵᆫ; í‹´; á„ᅵᆫ; ) HANGUL SYLLABLE TIN
+D2F5;D2F5;1110 1175 11AC;D2F5;1110 1175 11AC; # (틵; 틵; á„ᅵᆬ; 틵; á„ᅵᆬ; ) HANGUL SYLLABLE TINJ
+D2F6;D2F6;1110 1175 11AD;D2F6;1110 1175 11AD; # (í‹¶; í‹¶; á„ᅵᆭ; í‹¶; á„ᅵᆭ; ) HANGUL SYLLABLE TINH
+D2F7;D2F7;1110 1175 11AE;D2F7;1110 1175 11AE; # (í‹·; í‹·; á„ᅵᆮ; í‹·; á„ᅵᆮ; ) HANGUL SYLLABLE TID
+D2F8;D2F8;1110 1175 11AF;D2F8;1110 1175 11AF; # (틸; 틸; á„ᅵᆯ; 틸; á„ᅵᆯ; ) HANGUL SYLLABLE TIL
+D2F9;D2F9;1110 1175 11B0;D2F9;1110 1175 11B0; # (틹; 틹; á„ᅵᆰ; 틹; á„ᅵᆰ; ) HANGUL SYLLABLE TILG
+D2FA;D2FA;1110 1175 11B1;D2FA;1110 1175 11B1; # (틺; 틺; á„ᅵᆱ; 틺; á„ᅵᆱ; ) HANGUL SYLLABLE TILM
+D2FB;D2FB;1110 1175 11B2;D2FB;1110 1175 11B2; # (í‹»; í‹»; á„ᅵᆲ; í‹»; á„ᅵᆲ; ) HANGUL SYLLABLE TILB
+D2FC;D2FC;1110 1175 11B3;D2FC;1110 1175 11B3; # (틼; 틼; á„ᅵᆳ; 틼; á„ᅵᆳ; ) HANGUL SYLLABLE TILS
+D2FD;D2FD;1110 1175 11B4;D2FD;1110 1175 11B4; # (틽; 틽; á„ᅵᆴ; 틽; á„ᅵᆴ; ) HANGUL SYLLABLE TILT
+D2FE;D2FE;1110 1175 11B5;D2FE;1110 1175 11B5; # (틾; 틾; á„ᅵᆵ; 틾; á„ᅵᆵ; ) HANGUL SYLLABLE TILP
+D2FF;D2FF;1110 1175 11B6;D2FF;1110 1175 11B6; # (í‹¿; í‹¿; á„ᅵᆶ; í‹¿; á„ᅵᆶ; ) HANGUL SYLLABLE TILH
+D300;D300;1110 1175 11B7;D300;1110 1175 11B7; # (팀; 팀; á„ᅵᆷ; 팀; á„ᅵᆷ; ) HANGUL SYLLABLE TIM
+D301;D301;1110 1175 11B8;D301;1110 1175 11B8; # (íŒ; íŒ; á„ᅵᆸ; íŒ; á„ᅵᆸ; ) HANGUL SYLLABLE TIB
+D302;D302;1110 1175 11B9;D302;1110 1175 11B9; # (팂; 팂; á„ᅵᆹ; 팂; á„ᅵᆹ; ) HANGUL SYLLABLE TIBS
+D303;D303;1110 1175 11BA;D303;1110 1175 11BA; # (팃; 팃; á„ᅵᆺ; 팃; á„ᅵᆺ; ) HANGUL SYLLABLE TIS
+D304;D304;1110 1175 11BB;D304;1110 1175 11BB; # (팄; 팄; á„ᅵᆻ; 팄; á„ᅵᆻ; ) HANGUL SYLLABLE TISS
+D305;D305;1110 1175 11BC;D305;1110 1175 11BC; # (팅; 팅; á„ᅵᆼ; 팅; á„ᅵᆼ; ) HANGUL SYLLABLE TING
+D306;D306;1110 1175 11BD;D306;1110 1175 11BD; # (팆; 팆; á„ᅵᆽ; 팆; á„ᅵᆽ; ) HANGUL SYLLABLE TIJ
+D307;D307;1110 1175 11BE;D307;1110 1175 11BE; # (팇; 팇; á„ᅵᆾ; 팇; á„ᅵᆾ; ) HANGUL SYLLABLE TIC
+D308;D308;1110 1175 11BF;D308;1110 1175 11BF; # (팈; 팈; á„ᅵᆿ; 팈; á„ᅵᆿ; ) HANGUL SYLLABLE TIK
+D309;D309;1110 1175 11C0;D309;1110 1175 11C0; # (팉; 팉; á„ᅵᇀ; 팉; á„ᅵᇀ; ) HANGUL SYLLABLE TIT
+D30A;D30A;1110 1175 11C1;D30A;1110 1175 11C1; # (팊; 팊; á„á…µá‡; 팊; á„á…µá‡; ) HANGUL SYLLABLE TIP
+D30B;D30B;1110 1175 11C2;D30B;1110 1175 11C2; # (팋; 팋; á„ᅵᇂ; 팋; á„ᅵᇂ; ) HANGUL SYLLABLE TIH
+D30C;D30C;1111 1161;D30C;1111 1161; # (파; 파; 파; 파; 파; ) HANGUL SYLLABLE PA
+D30D;D30D;1111 1161 11A8;D30D;1111 1161 11A8; # (íŒ; íŒ; 팍; íŒ; 팍; ) HANGUL SYLLABLE PAG
+D30E;D30E;1111 1161 11A9;D30E;1111 1161 11A9; # (팎; 팎; 팎; 팎; 팎; ) HANGUL SYLLABLE PAGG
+D30F;D30F;1111 1161 11AA;D30F;1111 1161 11AA; # (íŒ; íŒ; 팏; íŒ; 팏; ) HANGUL SYLLABLE PAGS
+D310;D310;1111 1161 11AB;D310;1111 1161 11AB; # (íŒ; íŒ; 판; íŒ; 판; ) HANGUL SYLLABLE PAN
+D311;D311;1111 1161 11AC;D311;1111 1161 11AC; # (팑; 팑; 팑; 팑; 팑; ) HANGUL SYLLABLE PANJ
+D312;D312;1111 1161 11AD;D312;1111 1161 11AD; # (팒; 팒; 팒; 팒; 팒; ) HANGUL SYLLABLE PANH
+D313;D313;1111 1161 11AE;D313;1111 1161 11AE; # (팓; 팓; 팓; 팓; 팓; ) HANGUL SYLLABLE PAD
+D314;D314;1111 1161 11AF;D314;1111 1161 11AF; # (팔; 팔; 팔; 팔; 팔; ) HANGUL SYLLABLE PAL
+D315;D315;1111 1161 11B0;D315;1111 1161 11B0; # (팕; 팕; 팕; 팕; 팕; ) HANGUL SYLLABLE PALG
+D316;D316;1111 1161 11B1;D316;1111 1161 11B1; # (팖; 팖; 팖; 팖; 팖; ) HANGUL SYLLABLE PALM
+D317;D317;1111 1161 11B2;D317;1111 1161 11B2; # (팗; 팗; 팗; 팗; 팗; ) HANGUL SYLLABLE PALB
+D318;D318;1111 1161 11B3;D318;1111 1161 11B3; # (팘; 팘; 팘; 팘; 팘; ) HANGUL SYLLABLE PALS
+D319;D319;1111 1161 11B4;D319;1111 1161 11B4; # (팙; 팙; 팙; 팙; 팙; ) HANGUL SYLLABLE PALT
+D31A;D31A;1111 1161 11B5;D31A;1111 1161 11B5; # (팚; 팚; 팚; 팚; 팚; ) HANGUL SYLLABLE PALP
+D31B;D31B;1111 1161 11B6;D31B;1111 1161 11B6; # (팛; 팛; 팛; 팛; 팛; ) HANGUL SYLLABLE PALH
+D31C;D31C;1111 1161 11B7;D31C;1111 1161 11B7; # (팜; 팜; 팜; 팜; 팜; ) HANGUL SYLLABLE PAM
+D31D;D31D;1111 1161 11B8;D31D;1111 1161 11B8; # (íŒ; íŒ; 팝; íŒ; 팝; ) HANGUL SYLLABLE PAB
+D31E;D31E;1111 1161 11B9;D31E;1111 1161 11B9; # (팞; 팞; 팞; 팞; 팞; ) HANGUL SYLLABLE PABS
+D31F;D31F;1111 1161 11BA;D31F;1111 1161 11BA; # (팟; 팟; 팟; 팟; 팟; ) HANGUL SYLLABLE PAS
+D320;D320;1111 1161 11BB;D320;1111 1161 11BB; # (팠; 팠; 팠; 팠; 팠; ) HANGUL SYLLABLE PASS
+D321;D321;1111 1161 11BC;D321;1111 1161 11BC; # (팡; 팡; 팡; 팡; 팡; ) HANGUL SYLLABLE PANG
+D322;D322;1111 1161 11BD;D322;1111 1161 11BD; # (팢; 팢; 팢; 팢; 팢; ) HANGUL SYLLABLE PAJ
+D323;D323;1111 1161 11BE;D323;1111 1161 11BE; # (팣; 팣; 팣; 팣; 팣; ) HANGUL SYLLABLE PAC
+D324;D324;1111 1161 11BF;D324;1111 1161 11BF; # (팤; 팤; 팤; 팤; 팤; ) HANGUL SYLLABLE PAK
+D325;D325;1111 1161 11C0;D325;1111 1161 11C0; # (팥; 팥; 팥; 팥; 팥; ) HANGUL SYLLABLE PAT
+D326;D326;1111 1161 11C1;D326;1111 1161 11C1; # (팦; 팦; á„‘á…¡á‡; 팦; á„‘á…¡á‡; ) HANGUL SYLLABLE PAP
+D327;D327;1111 1161 11C2;D327;1111 1161 11C2; # (팧; 팧; 팧; 팧; 팧; ) HANGUL SYLLABLE PAH
+D328;D328;1111 1162;D328;1111 1162; # (패; 패; 패; 패; 패; ) HANGUL SYLLABLE PAE
+D329;D329;1111 1162 11A8;D329;1111 1162 11A8; # (팩; 팩; 팩; 팩; 팩; ) HANGUL SYLLABLE PAEG
+D32A;D32A;1111 1162 11A9;D32A;1111 1162 11A9; # (팪; 팪; 팪; 팪; 팪; ) HANGUL SYLLABLE PAEGG
+D32B;D32B;1111 1162 11AA;D32B;1111 1162 11AA; # (팫; 팫; 팫; 팫; 팫; ) HANGUL SYLLABLE PAEGS
+D32C;D32C;1111 1162 11AB;D32C;1111 1162 11AB; # (팬; 팬; 팬; 팬; 팬; ) HANGUL SYLLABLE PAEN
+D32D;D32D;1111 1162 11AC;D32D;1111 1162 11AC; # (팭; 팭; 팭; 팭; 팭; ) HANGUL SYLLABLE PAENJ
+D32E;D32E;1111 1162 11AD;D32E;1111 1162 11AD; # (팮; 팮; 팮; 팮; 팮; ) HANGUL SYLLABLE PAENH
+D32F;D32F;1111 1162 11AE;D32F;1111 1162 11AE; # (팯; 팯; 팯; 팯; 팯; ) HANGUL SYLLABLE PAED
+D330;D330;1111 1162 11AF;D330;1111 1162 11AF; # (팰; 팰; 팰; 팰; 팰; ) HANGUL SYLLABLE PAEL
+D331;D331;1111 1162 11B0;D331;1111 1162 11B0; # (팱; 팱; 팱; 팱; 팱; ) HANGUL SYLLABLE PAELG
+D332;D332;1111 1162 11B1;D332;1111 1162 11B1; # (팲; 팲; 팲; 팲; 팲; ) HANGUL SYLLABLE PAELM
+D333;D333;1111 1162 11B2;D333;1111 1162 11B2; # (팳; 팳; 팳; 팳; 팳; ) HANGUL SYLLABLE PAELB
+D334;D334;1111 1162 11B3;D334;1111 1162 11B3; # (팴; 팴; 팴; 팴; 팴; ) HANGUL SYLLABLE PAELS
+D335;D335;1111 1162 11B4;D335;1111 1162 11B4; # (팵; 팵; 팵; 팵; 팵; ) HANGUL SYLLABLE PAELT
+D336;D336;1111 1162 11B5;D336;1111 1162 11B5; # (팶; 팶; 팶; 팶; 팶; ) HANGUL SYLLABLE PAELP
+D337;D337;1111 1162 11B6;D337;1111 1162 11B6; # (팷; 팷; 팷; 팷; 팷; ) HANGUL SYLLABLE PAELH
+D338;D338;1111 1162 11B7;D338;1111 1162 11B7; # (팸; 팸; 팸; 팸; 팸; ) HANGUL SYLLABLE PAEM
+D339;D339;1111 1162 11B8;D339;1111 1162 11B8; # (팹; 팹; 팹; 팹; 팹; ) HANGUL SYLLABLE PAEB
+D33A;D33A;1111 1162 11B9;D33A;1111 1162 11B9; # (팺; 팺; 팺; 팺; 팺; ) HANGUL SYLLABLE PAEBS
+D33B;D33B;1111 1162 11BA;D33B;1111 1162 11BA; # (팻; 팻; 팻; 팻; 팻; ) HANGUL SYLLABLE PAES
+D33C;D33C;1111 1162 11BB;D33C;1111 1162 11BB; # (팼; 팼; 팼; 팼; 팼; ) HANGUL SYLLABLE PAESS
+D33D;D33D;1111 1162 11BC;D33D;1111 1162 11BC; # (팽; 팽; 팽; 팽; 팽; ) HANGUL SYLLABLE PAENG
+D33E;D33E;1111 1162 11BD;D33E;1111 1162 11BD; # (팾; 팾; 팾; 팾; 팾; ) HANGUL SYLLABLE PAEJ
+D33F;D33F;1111 1162 11BE;D33F;1111 1162 11BE; # (팿; 팿; 팿; 팿; 팿; ) HANGUL SYLLABLE PAEC
+D340;D340;1111 1162 11BF;D340;1111 1162 11BF; # (í€; í€; 퍀; í€; 퍀; ) HANGUL SYLLABLE PAEK
+D341;D341;1111 1162 11C0;D341;1111 1162 11C0; # (í; í; 퍁; í; 퍁; ) HANGUL SYLLABLE PAET
+D342;D342;1111 1162 11C1;D342;1111 1162 11C1; # (í‚; í‚; á„‘á…¢á‡; í‚; á„‘á…¢á‡; ) HANGUL SYLLABLE PAEP
+D343;D343;1111 1162 11C2;D343;1111 1162 11C2; # (íƒ; íƒ; 퍃; íƒ; 퍃; ) HANGUL SYLLABLE PAEH
+D344;D344;1111 1163;D344;1111 1163; # (í„; í„; á„‘á…£; í„; á„‘á…£; ) HANGUL SYLLABLE PYA
+D345;D345;1111 1163 11A8;D345;1111 1163 11A8; # (í…; í…; 퍅; í…; 퍅; ) HANGUL SYLLABLE PYAG
+D346;D346;1111 1163 11A9;D346;1111 1163 11A9; # (í†; í†; 퍆; í†; 퍆; ) HANGUL SYLLABLE PYAGG
+D347;D347;1111 1163 11AA;D347;1111 1163 11AA; # (í‡; í‡; 퍇; í‡; 퍇; ) HANGUL SYLLABLE PYAGS
+D348;D348;1111 1163 11AB;D348;1111 1163 11AB; # (íˆ; íˆ; 퍈; íˆ; 퍈; ) HANGUL SYLLABLE PYAN
+D349;D349;1111 1163 11AC;D349;1111 1163 11AC; # (í‰; í‰; 퍉; í‰; 퍉; ) HANGUL SYLLABLE PYANJ
+D34A;D34A;1111 1163 11AD;D34A;1111 1163 11AD; # (íŠ; íŠ; 퍊; íŠ; 퍊; ) HANGUL SYLLABLE PYANH
+D34B;D34B;1111 1163 11AE;D34B;1111 1163 11AE; # (í‹; í‹; 퍋; í‹; 퍋; ) HANGUL SYLLABLE PYAD
+D34C;D34C;1111 1163 11AF;D34C;1111 1163 11AF; # (íŒ; íŒ; 퍌; íŒ; 퍌; ) HANGUL SYLLABLE PYAL
+D34D;D34D;1111 1163 11B0;D34D;1111 1163 11B0; # (í; í; 퍍; í; 퍍; ) HANGUL SYLLABLE PYALG
+D34E;D34E;1111 1163 11B1;D34E;1111 1163 11B1; # (íŽ; íŽ; 퍎; íŽ; 퍎; ) HANGUL SYLLABLE PYALM
+D34F;D34F;1111 1163 11B2;D34F;1111 1163 11B2; # (í; í; 퍏; í; 퍏; ) HANGUL SYLLABLE PYALB
+D350;D350;1111 1163 11B3;D350;1111 1163 11B3; # (í; í; 퍐; í; 퍐; ) HANGUL SYLLABLE PYALS
+D351;D351;1111 1163 11B4;D351;1111 1163 11B4; # (í‘; í‘; 퍑; í‘; 퍑; ) HANGUL SYLLABLE PYALT
+D352;D352;1111 1163 11B5;D352;1111 1163 11B5; # (í’; í’; 퍒; í’; 퍒; ) HANGUL SYLLABLE PYALP
+D353;D353;1111 1163 11B6;D353;1111 1163 11B6; # (í“; í“; 퍓; í“; 퍓; ) HANGUL SYLLABLE PYALH
+D354;D354;1111 1163 11B7;D354;1111 1163 11B7; # (í”; í”; 퍔; í”; 퍔; ) HANGUL SYLLABLE PYAM
+D355;D355;1111 1163 11B8;D355;1111 1163 11B8; # (í•; í•; 퍕; í•; 퍕; ) HANGUL SYLLABLE PYAB
+D356;D356;1111 1163 11B9;D356;1111 1163 11B9; # (í–; í–; 퍖; í–; 퍖; ) HANGUL SYLLABLE PYABS
+D357;D357;1111 1163 11BA;D357;1111 1163 11BA; # (í—; í—; 퍗; í—; 퍗; ) HANGUL SYLLABLE PYAS
+D358;D358;1111 1163 11BB;D358;1111 1163 11BB; # (í˜; í˜; 퍘; í˜; 퍘; ) HANGUL SYLLABLE PYASS
+D359;D359;1111 1163 11BC;D359;1111 1163 11BC; # (í™; í™; 퍙; í™; 퍙; ) HANGUL SYLLABLE PYANG
+D35A;D35A;1111 1163 11BD;D35A;1111 1163 11BD; # (íš; íš; 퍚; íš; 퍚; ) HANGUL SYLLABLE PYAJ
+D35B;D35B;1111 1163 11BE;D35B;1111 1163 11BE; # (í›; í›; 퍛; í›; 퍛; ) HANGUL SYLLABLE PYAC
+D35C;D35C;1111 1163 11BF;D35C;1111 1163 11BF; # (íœ; íœ; 퍜; íœ; 퍜; ) HANGUL SYLLABLE PYAK
+D35D;D35D;1111 1163 11C0;D35D;1111 1163 11C0; # (í; í; 퍝; í; 퍝; ) HANGUL SYLLABLE PYAT
+D35E;D35E;1111 1163 11C1;D35E;1111 1163 11C1; # (íž; íž; á„‘á…£á‡; íž; á„‘á…£á‡; ) HANGUL SYLLABLE PYAP
+D35F;D35F;1111 1163 11C2;D35F;1111 1163 11C2; # (íŸ; íŸ; 퍟; íŸ; 퍟; ) HANGUL SYLLABLE PYAH
+D360;D360;1111 1164;D360;1111 1164; # (í ; í ; á„‘á…¤; í ; á„‘á…¤; ) HANGUL SYLLABLE PYAE
+D361;D361;1111 1164 11A8;D361;1111 1164 11A8; # (í¡; í¡; 퍡; í¡; 퍡; ) HANGUL SYLLABLE PYAEG
+D362;D362;1111 1164 11A9;D362;1111 1164 11A9; # (í¢; í¢; 퍢; í¢; 퍢; ) HANGUL SYLLABLE PYAEGG
+D363;D363;1111 1164 11AA;D363;1111 1164 11AA; # (í£; í£; 퍣; í£; 퍣; ) HANGUL SYLLABLE PYAEGS
+D364;D364;1111 1164 11AB;D364;1111 1164 11AB; # (í¤; í¤; 퍤; í¤; 퍤; ) HANGUL SYLLABLE PYAEN
+D365;D365;1111 1164 11AC;D365;1111 1164 11AC; # (í¥; í¥; 퍥; í¥; 퍥; ) HANGUL SYLLABLE PYAENJ
+D366;D366;1111 1164 11AD;D366;1111 1164 11AD; # (í¦; í¦; 퍦; í¦; 퍦; ) HANGUL SYLLABLE PYAENH
+D367;D367;1111 1164 11AE;D367;1111 1164 11AE; # (í§; í§; 퍧; í§; 퍧; ) HANGUL SYLLABLE PYAED
+D368;D368;1111 1164 11AF;D368;1111 1164 11AF; # (í¨; í¨; 퍨; í¨; 퍨; ) HANGUL SYLLABLE PYAEL
+D369;D369;1111 1164 11B0;D369;1111 1164 11B0; # (í©; í©; 퍩; í©; 퍩; ) HANGUL SYLLABLE PYAELG
+D36A;D36A;1111 1164 11B1;D36A;1111 1164 11B1; # (íª; íª; 퍪; íª; 퍪; ) HANGUL SYLLABLE PYAELM
+D36B;D36B;1111 1164 11B2;D36B;1111 1164 11B2; # (í«; í«; 퍫; í«; 퍫; ) HANGUL SYLLABLE PYAELB
+D36C;D36C;1111 1164 11B3;D36C;1111 1164 11B3; # (í¬; í¬; 퍬; í¬; 퍬; ) HANGUL SYLLABLE PYAELS
+D36D;D36D;1111 1164 11B4;D36D;1111 1164 11B4; # (í­; í­; 퍭; í­; 퍭; ) HANGUL SYLLABLE PYAELT
+D36E;D36E;1111 1164 11B5;D36E;1111 1164 11B5; # (í®; í®; 퍮; í®; 퍮; ) HANGUL SYLLABLE PYAELP
+D36F;D36F;1111 1164 11B6;D36F;1111 1164 11B6; # (í¯; í¯; 퍯; í¯; 퍯; ) HANGUL SYLLABLE PYAELH
+D370;D370;1111 1164 11B7;D370;1111 1164 11B7; # (í°; í°; 퍰; í°; 퍰; ) HANGUL SYLLABLE PYAEM
+D371;D371;1111 1164 11B8;D371;1111 1164 11B8; # (í±; í±; 퍱; í±; 퍱; ) HANGUL SYLLABLE PYAEB
+D372;D372;1111 1164 11B9;D372;1111 1164 11B9; # (í²; í²; 퍲; í²; 퍲; ) HANGUL SYLLABLE PYAEBS
+D373;D373;1111 1164 11BA;D373;1111 1164 11BA; # (í³; í³; 퍳; í³; 퍳; ) HANGUL SYLLABLE PYAES
+D374;D374;1111 1164 11BB;D374;1111 1164 11BB; # (í´; í´; 퍴; í´; 퍴; ) HANGUL SYLLABLE PYAESS
+D375;D375;1111 1164 11BC;D375;1111 1164 11BC; # (íµ; íµ; 퍵; íµ; 퍵; ) HANGUL SYLLABLE PYAENG
+D376;D376;1111 1164 11BD;D376;1111 1164 11BD; # (í¶; í¶; 퍶; í¶; 퍶; ) HANGUL SYLLABLE PYAEJ
+D377;D377;1111 1164 11BE;D377;1111 1164 11BE; # (í·; í·; 퍷; í·; 퍷; ) HANGUL SYLLABLE PYAEC
+D378;D378;1111 1164 11BF;D378;1111 1164 11BF; # (í¸; í¸; 퍸; í¸; 퍸; ) HANGUL SYLLABLE PYAEK
+D379;D379;1111 1164 11C0;D379;1111 1164 11C0; # (í¹; í¹; 퍹; í¹; 퍹; ) HANGUL SYLLABLE PYAET
+D37A;D37A;1111 1164 11C1;D37A;1111 1164 11C1; # (íº; íº; á„‘á…¤á‡; íº; á„‘á…¤á‡; ) HANGUL SYLLABLE PYAEP
+D37B;D37B;1111 1164 11C2;D37B;1111 1164 11C2; # (í»; í»; 퍻; í»; 퍻; ) HANGUL SYLLABLE PYAEH
+D37C;D37C;1111 1165;D37C;1111 1165; # (í¼; í¼; á„‘á…¥; í¼; á„‘á…¥; ) HANGUL SYLLABLE PEO
+D37D;D37D;1111 1165 11A8;D37D;1111 1165 11A8; # (í½; í½; 퍽; í½; 퍽; ) HANGUL SYLLABLE PEOG
+D37E;D37E;1111 1165 11A9;D37E;1111 1165 11A9; # (í¾; í¾; 퍾; í¾; 퍾; ) HANGUL SYLLABLE PEOGG
+D37F;D37F;1111 1165 11AA;D37F;1111 1165 11AA; # (í¿; í¿; 퍿; í¿; 퍿; ) HANGUL SYLLABLE PEOGS
+D380;D380;1111 1165 11AB;D380;1111 1165 11AB; # (펀; 펀; 펀; 펀; 펀; ) HANGUL SYLLABLE PEON
+D381;D381;1111 1165 11AC;D381;1111 1165 11AC; # (íŽ; íŽ; 펁; íŽ; 펁; ) HANGUL SYLLABLE PEONJ
+D382;D382;1111 1165 11AD;D382;1111 1165 11AD; # (펂; 펂; 펂; 펂; 펂; ) HANGUL SYLLABLE PEONH
+D383;D383;1111 1165 11AE;D383;1111 1165 11AE; # (펃; 펃; 펃; 펃; 펃; ) HANGUL SYLLABLE PEOD
+D384;D384;1111 1165 11AF;D384;1111 1165 11AF; # (펄; 펄; 펄; 펄; 펄; ) HANGUL SYLLABLE PEOL
+D385;D385;1111 1165 11B0;D385;1111 1165 11B0; # (펅; 펅; 펅; 펅; 펅; ) HANGUL SYLLABLE PEOLG
+D386;D386;1111 1165 11B1;D386;1111 1165 11B1; # (펆; 펆; 펆; 펆; 펆; ) HANGUL SYLLABLE PEOLM
+D387;D387;1111 1165 11B2;D387;1111 1165 11B2; # (펇; 펇; 펇; 펇; 펇; ) HANGUL SYLLABLE PEOLB
+D388;D388;1111 1165 11B3;D388;1111 1165 11B3; # (펈; 펈; 펈; 펈; 펈; ) HANGUL SYLLABLE PEOLS
+D389;D389;1111 1165 11B4;D389;1111 1165 11B4; # (펉; 펉; 펉; 펉; 펉; ) HANGUL SYLLABLE PEOLT
+D38A;D38A;1111 1165 11B5;D38A;1111 1165 11B5; # (펊; 펊; 펊; 펊; 펊; ) HANGUL SYLLABLE PEOLP
+D38B;D38B;1111 1165 11B6;D38B;1111 1165 11B6; # (펋; 펋; 펋; 펋; 펋; ) HANGUL SYLLABLE PEOLH
+D38C;D38C;1111 1165 11B7;D38C;1111 1165 11B7; # (펌; 펌; 펌; 펌; 펌; ) HANGUL SYLLABLE PEOM
+D38D;D38D;1111 1165 11B8;D38D;1111 1165 11B8; # (íŽ; íŽ; 펍; íŽ; 펍; ) HANGUL SYLLABLE PEOB
+D38E;D38E;1111 1165 11B9;D38E;1111 1165 11B9; # (펎; 펎; 펎; 펎; 펎; ) HANGUL SYLLABLE PEOBS
+D38F;D38F;1111 1165 11BA;D38F;1111 1165 11BA; # (íŽ; íŽ; 펏; íŽ; 펏; ) HANGUL SYLLABLE PEOS
+D390;D390;1111 1165 11BB;D390;1111 1165 11BB; # (íŽ; íŽ; 펐; íŽ; 펐; ) HANGUL SYLLABLE PEOSS
+D391;D391;1111 1165 11BC;D391;1111 1165 11BC; # (펑; 펑; 펑; 펑; 펑; ) HANGUL SYLLABLE PEONG
+D392;D392;1111 1165 11BD;D392;1111 1165 11BD; # (펒; 펒; 펒; 펒; 펒; ) HANGUL SYLLABLE PEOJ
+D393;D393;1111 1165 11BE;D393;1111 1165 11BE; # (펓; 펓; 펓; 펓; 펓; ) HANGUL SYLLABLE PEOC
+D394;D394;1111 1165 11BF;D394;1111 1165 11BF; # (펔; 펔; 펔; 펔; 펔; ) HANGUL SYLLABLE PEOK
+D395;D395;1111 1165 11C0;D395;1111 1165 11C0; # (펕; 펕; 펕; 펕; 펕; ) HANGUL SYLLABLE PEOT
+D396;D396;1111 1165 11C1;D396;1111 1165 11C1; # (펖; 펖; á„‘á…¥á‡; 펖; á„‘á…¥á‡; ) HANGUL SYLLABLE PEOP
+D397;D397;1111 1165 11C2;D397;1111 1165 11C2; # (펗; 펗; 펗; 펗; 펗; ) HANGUL SYLLABLE PEOH
+D398;D398;1111 1166;D398;1111 1166; # (페; 페; 페; 페; 페; ) HANGUL SYLLABLE PE
+D399;D399;1111 1166 11A8;D399;1111 1166 11A8; # (펙; 펙; 펙; 펙; 펙; ) HANGUL SYLLABLE PEG
+D39A;D39A;1111 1166 11A9;D39A;1111 1166 11A9; # (펚; 펚; 펚; 펚; 펚; ) HANGUL SYLLABLE PEGG
+D39B;D39B;1111 1166 11AA;D39B;1111 1166 11AA; # (펛; 펛; 펛; 펛; 펛; ) HANGUL SYLLABLE PEGS
+D39C;D39C;1111 1166 11AB;D39C;1111 1166 11AB; # (펜; 펜; 펜; 펜; 펜; ) HANGUL SYLLABLE PEN
+D39D;D39D;1111 1166 11AC;D39D;1111 1166 11AC; # (íŽ; íŽ; 펝; íŽ; 펝; ) HANGUL SYLLABLE PENJ
+D39E;D39E;1111 1166 11AD;D39E;1111 1166 11AD; # (펞; 펞; 펞; 펞; 펞; ) HANGUL SYLLABLE PENH
+D39F;D39F;1111 1166 11AE;D39F;1111 1166 11AE; # (펟; 펟; 펟; 펟; 펟; ) HANGUL SYLLABLE PED
+D3A0;D3A0;1111 1166 11AF;D3A0;1111 1166 11AF; # (펠; 펠; 펠; 펠; 펠; ) HANGUL SYLLABLE PEL
+D3A1;D3A1;1111 1166 11B0;D3A1;1111 1166 11B0; # (펡; 펡; 펡; 펡; 펡; ) HANGUL SYLLABLE PELG
+D3A2;D3A2;1111 1166 11B1;D3A2;1111 1166 11B1; # (펢; 펢; 펢; 펢; 펢; ) HANGUL SYLLABLE PELM
+D3A3;D3A3;1111 1166 11B2;D3A3;1111 1166 11B2; # (펣; 펣; 펣; 펣; 펣; ) HANGUL SYLLABLE PELB
+D3A4;D3A4;1111 1166 11B3;D3A4;1111 1166 11B3; # (펤; 펤; 펤; 펤; 펤; ) HANGUL SYLLABLE PELS
+D3A5;D3A5;1111 1166 11B4;D3A5;1111 1166 11B4; # (펥; 펥; 펥; 펥; 펥; ) HANGUL SYLLABLE PELT
+D3A6;D3A6;1111 1166 11B5;D3A6;1111 1166 11B5; # (펦; 펦; 펦; 펦; 펦; ) HANGUL SYLLABLE PELP
+D3A7;D3A7;1111 1166 11B6;D3A7;1111 1166 11B6; # (펧; 펧; 펧; 펧; 펧; ) HANGUL SYLLABLE PELH
+D3A8;D3A8;1111 1166 11B7;D3A8;1111 1166 11B7; # (펨; 펨; 펨; 펨; 펨; ) HANGUL SYLLABLE PEM
+D3A9;D3A9;1111 1166 11B8;D3A9;1111 1166 11B8; # (펩; 펩; 펩; 펩; 펩; ) HANGUL SYLLABLE PEB
+D3AA;D3AA;1111 1166 11B9;D3AA;1111 1166 11B9; # (펪; 펪; 펪; 펪; 펪; ) HANGUL SYLLABLE PEBS
+D3AB;D3AB;1111 1166 11BA;D3AB;1111 1166 11BA; # (펫; 펫; 펫; 펫; 펫; ) HANGUL SYLLABLE PES
+D3AC;D3AC;1111 1166 11BB;D3AC;1111 1166 11BB; # (펬; 펬; 펬; 펬; 펬; ) HANGUL SYLLABLE PESS
+D3AD;D3AD;1111 1166 11BC;D3AD;1111 1166 11BC; # (펭; 펭; 펭; 펭; 펭; ) HANGUL SYLLABLE PENG
+D3AE;D3AE;1111 1166 11BD;D3AE;1111 1166 11BD; # (펮; 펮; 펮; 펮; 펮; ) HANGUL SYLLABLE PEJ
+D3AF;D3AF;1111 1166 11BE;D3AF;1111 1166 11BE; # (펯; 펯; 펯; 펯; 펯; ) HANGUL SYLLABLE PEC
+D3B0;D3B0;1111 1166 11BF;D3B0;1111 1166 11BF; # (펰; 펰; 펰; 펰; 펰; ) HANGUL SYLLABLE PEK
+D3B1;D3B1;1111 1166 11C0;D3B1;1111 1166 11C0; # (펱; 펱; 펱; 펱; 펱; ) HANGUL SYLLABLE PET
+D3B2;D3B2;1111 1166 11C1;D3B2;1111 1166 11C1; # (펲; 펲; á„‘á…¦á‡; 펲; á„‘á…¦á‡; ) HANGUL SYLLABLE PEP
+D3B3;D3B3;1111 1166 11C2;D3B3;1111 1166 11C2; # (펳; 펳; 펳; 펳; 펳; ) HANGUL SYLLABLE PEH
+D3B4;D3B4;1111 1167;D3B4;1111 1167; # (펴; 펴; 펴; 펴; 펴; ) HANGUL SYLLABLE PYEO
+D3B5;D3B5;1111 1167 11A8;D3B5;1111 1167 11A8; # (펵; 펵; 펵; 펵; 펵; ) HANGUL SYLLABLE PYEOG
+D3B6;D3B6;1111 1167 11A9;D3B6;1111 1167 11A9; # (펶; 펶; 펶; 펶; 펶; ) HANGUL SYLLABLE PYEOGG
+D3B7;D3B7;1111 1167 11AA;D3B7;1111 1167 11AA; # (펷; 펷; 펷; 펷; 펷; ) HANGUL SYLLABLE PYEOGS
+D3B8;D3B8;1111 1167 11AB;D3B8;1111 1167 11AB; # (편; 편; 편; 편; 편; ) HANGUL SYLLABLE PYEON
+D3B9;D3B9;1111 1167 11AC;D3B9;1111 1167 11AC; # (펹; 펹; 펹; 펹; 펹; ) HANGUL SYLLABLE PYEONJ
+D3BA;D3BA;1111 1167 11AD;D3BA;1111 1167 11AD; # (펺; 펺; 펺; 펺; 펺; ) HANGUL SYLLABLE PYEONH
+D3BB;D3BB;1111 1167 11AE;D3BB;1111 1167 11AE; # (펻; 펻; 펻; 펻; 펻; ) HANGUL SYLLABLE PYEOD
+D3BC;D3BC;1111 1167 11AF;D3BC;1111 1167 11AF; # (펼; 펼; 펼; 펼; 펼; ) HANGUL SYLLABLE PYEOL
+D3BD;D3BD;1111 1167 11B0;D3BD;1111 1167 11B0; # (펽; 펽; 펽; 펽; 펽; ) HANGUL SYLLABLE PYEOLG
+D3BE;D3BE;1111 1167 11B1;D3BE;1111 1167 11B1; # (펾; 펾; 펾; 펾; 펾; ) HANGUL SYLLABLE PYEOLM
+D3BF;D3BF;1111 1167 11B2;D3BF;1111 1167 11B2; # (펿; 펿; 펿; 펿; 펿; ) HANGUL SYLLABLE PYEOLB
+D3C0;D3C0;1111 1167 11B3;D3C0;1111 1167 11B3; # (í€; í€; 폀; í€; 폀; ) HANGUL SYLLABLE PYEOLS
+D3C1;D3C1;1111 1167 11B4;D3C1;1111 1167 11B4; # (í; í; 폁; í; 폁; ) HANGUL SYLLABLE PYEOLT
+D3C2;D3C2;1111 1167 11B5;D3C2;1111 1167 11B5; # (í‚; í‚; 폂; í‚; 폂; ) HANGUL SYLLABLE PYEOLP
+D3C3;D3C3;1111 1167 11B6;D3C3;1111 1167 11B6; # (íƒ; íƒ; 폃; íƒ; 폃; ) HANGUL SYLLABLE PYEOLH
+D3C4;D3C4;1111 1167 11B7;D3C4;1111 1167 11B7; # (í„; í„; 폄; í„; 폄; ) HANGUL SYLLABLE PYEOM
+D3C5;D3C5;1111 1167 11B8;D3C5;1111 1167 11B8; # (í…; í…; 폅; í…; 폅; ) HANGUL SYLLABLE PYEOB
+D3C6;D3C6;1111 1167 11B9;D3C6;1111 1167 11B9; # (í†; í†; 폆; í†; 폆; ) HANGUL SYLLABLE PYEOBS
+D3C7;D3C7;1111 1167 11BA;D3C7;1111 1167 11BA; # (í‡; í‡; 폇; í‡; 폇; ) HANGUL SYLLABLE PYEOS
+D3C8;D3C8;1111 1167 11BB;D3C8;1111 1167 11BB; # (íˆ; íˆ; 폈; íˆ; 폈; ) HANGUL SYLLABLE PYEOSS
+D3C9;D3C9;1111 1167 11BC;D3C9;1111 1167 11BC; # (í‰; í‰; 평; í‰; 평; ) HANGUL SYLLABLE PYEONG
+D3CA;D3CA;1111 1167 11BD;D3CA;1111 1167 11BD; # (íŠ; íŠ; 폊; íŠ; 폊; ) HANGUL SYLLABLE PYEOJ
+D3CB;D3CB;1111 1167 11BE;D3CB;1111 1167 11BE; # (í‹; í‹; 폋; í‹; 폋; ) HANGUL SYLLABLE PYEOC
+D3CC;D3CC;1111 1167 11BF;D3CC;1111 1167 11BF; # (íŒ; íŒ; 폌; íŒ; 폌; ) HANGUL SYLLABLE PYEOK
+D3CD;D3CD;1111 1167 11C0;D3CD;1111 1167 11C0; # (í; í; 폍; í; 폍; ) HANGUL SYLLABLE PYEOT
+D3CE;D3CE;1111 1167 11C1;D3CE;1111 1167 11C1; # (íŽ; íŽ; á„‘á…§á‡; íŽ; á„‘á…§á‡; ) HANGUL SYLLABLE PYEOP
+D3CF;D3CF;1111 1167 11C2;D3CF;1111 1167 11C2; # (í; í; 폏; í; 폏; ) HANGUL SYLLABLE PYEOH
+D3D0;D3D0;1111 1168;D3D0;1111 1168; # (í; í; á„‘á…¨; í; á„‘á…¨; ) HANGUL SYLLABLE PYE
+D3D1;D3D1;1111 1168 11A8;D3D1;1111 1168 11A8; # (í‘; í‘; 폑; í‘; 폑; ) HANGUL SYLLABLE PYEG
+D3D2;D3D2;1111 1168 11A9;D3D2;1111 1168 11A9; # (í’; í’; 폒; í’; 폒; ) HANGUL SYLLABLE PYEGG
+D3D3;D3D3;1111 1168 11AA;D3D3;1111 1168 11AA; # (í“; í“; 폓; í“; 폓; ) HANGUL SYLLABLE PYEGS
+D3D4;D3D4;1111 1168 11AB;D3D4;1111 1168 11AB; # (í”; í”; 폔; í”; 폔; ) HANGUL SYLLABLE PYEN
+D3D5;D3D5;1111 1168 11AC;D3D5;1111 1168 11AC; # (í•; í•; 폕; í•; 폕; ) HANGUL SYLLABLE PYENJ
+D3D6;D3D6;1111 1168 11AD;D3D6;1111 1168 11AD; # (í–; í–; 폖; í–; 폖; ) HANGUL SYLLABLE PYENH
+D3D7;D3D7;1111 1168 11AE;D3D7;1111 1168 11AE; # (í—; í—; 폗; í—; 폗; ) HANGUL SYLLABLE PYED
+D3D8;D3D8;1111 1168 11AF;D3D8;1111 1168 11AF; # (í˜; í˜; 폘; í˜; 폘; ) HANGUL SYLLABLE PYEL
+D3D9;D3D9;1111 1168 11B0;D3D9;1111 1168 11B0; # (í™; í™; 폙; í™; 폙; ) HANGUL SYLLABLE PYELG
+D3DA;D3DA;1111 1168 11B1;D3DA;1111 1168 11B1; # (íš; íš; 폚; íš; 폚; ) HANGUL SYLLABLE PYELM
+D3DB;D3DB;1111 1168 11B2;D3DB;1111 1168 11B2; # (í›; í›; 폛; í›; 폛; ) HANGUL SYLLABLE PYELB
+D3DC;D3DC;1111 1168 11B3;D3DC;1111 1168 11B3; # (íœ; íœ; 폜; íœ; 폜; ) HANGUL SYLLABLE PYELS
+D3DD;D3DD;1111 1168 11B4;D3DD;1111 1168 11B4; # (í; í; 폝; í; 폝; ) HANGUL SYLLABLE PYELT
+D3DE;D3DE;1111 1168 11B5;D3DE;1111 1168 11B5; # (íž; íž; 폞; íž; 폞; ) HANGUL SYLLABLE PYELP
+D3DF;D3DF;1111 1168 11B6;D3DF;1111 1168 11B6; # (íŸ; íŸ; 폟; íŸ; 폟; ) HANGUL SYLLABLE PYELH
+D3E0;D3E0;1111 1168 11B7;D3E0;1111 1168 11B7; # (í ; í ; 폠; í ; 폠; ) HANGUL SYLLABLE PYEM
+D3E1;D3E1;1111 1168 11B8;D3E1;1111 1168 11B8; # (í¡; í¡; 폡; í¡; 폡; ) HANGUL SYLLABLE PYEB
+D3E2;D3E2;1111 1168 11B9;D3E2;1111 1168 11B9; # (í¢; í¢; 폢; í¢; 폢; ) HANGUL SYLLABLE PYEBS
+D3E3;D3E3;1111 1168 11BA;D3E3;1111 1168 11BA; # (í£; í£; 폣; í£; 폣; ) HANGUL SYLLABLE PYES
+D3E4;D3E4;1111 1168 11BB;D3E4;1111 1168 11BB; # (í¤; í¤; 폤; í¤; 폤; ) HANGUL SYLLABLE PYESS
+D3E5;D3E5;1111 1168 11BC;D3E5;1111 1168 11BC; # (í¥; í¥; 폥; í¥; 폥; ) HANGUL SYLLABLE PYENG
+D3E6;D3E6;1111 1168 11BD;D3E6;1111 1168 11BD; # (í¦; í¦; 폦; í¦; 폦; ) HANGUL SYLLABLE PYEJ
+D3E7;D3E7;1111 1168 11BE;D3E7;1111 1168 11BE; # (í§; í§; 폧; í§; 폧; ) HANGUL SYLLABLE PYEC
+D3E8;D3E8;1111 1168 11BF;D3E8;1111 1168 11BF; # (í¨; í¨; 폨; í¨; 폨; ) HANGUL SYLLABLE PYEK
+D3E9;D3E9;1111 1168 11C0;D3E9;1111 1168 11C0; # (í©; í©; 폩; í©; 폩; ) HANGUL SYLLABLE PYET
+D3EA;D3EA;1111 1168 11C1;D3EA;1111 1168 11C1; # (íª; íª; á„‘á…¨á‡; íª; á„‘á…¨á‡; ) HANGUL SYLLABLE PYEP
+D3EB;D3EB;1111 1168 11C2;D3EB;1111 1168 11C2; # (í«; í«; 폫; í«; 폫; ) HANGUL SYLLABLE PYEH
+D3EC;D3EC;1111 1169;D3EC;1111 1169; # (í¬; í¬; á„‘á…©; í¬; á„‘á…©; ) HANGUL SYLLABLE PO
+D3ED;D3ED;1111 1169 11A8;D3ED;1111 1169 11A8; # (í­; í­; 폭; í­; 폭; ) HANGUL SYLLABLE POG
+D3EE;D3EE;1111 1169 11A9;D3EE;1111 1169 11A9; # (í®; í®; 폮; í®; 폮; ) HANGUL SYLLABLE POGG
+D3EF;D3EF;1111 1169 11AA;D3EF;1111 1169 11AA; # (í¯; í¯; 폯; í¯; 폯; ) HANGUL SYLLABLE POGS
+D3F0;D3F0;1111 1169 11AB;D3F0;1111 1169 11AB; # (í°; í°; 폰; í°; 폰; ) HANGUL SYLLABLE PON
+D3F1;D3F1;1111 1169 11AC;D3F1;1111 1169 11AC; # (í±; í±; 폱; í±; 폱; ) HANGUL SYLLABLE PONJ
+D3F2;D3F2;1111 1169 11AD;D3F2;1111 1169 11AD; # (í²; í²; 폲; í²; 폲; ) HANGUL SYLLABLE PONH
+D3F3;D3F3;1111 1169 11AE;D3F3;1111 1169 11AE; # (í³; í³; 폳; í³; 폳; ) HANGUL SYLLABLE POD
+D3F4;D3F4;1111 1169 11AF;D3F4;1111 1169 11AF; # (í´; í´; 폴; í´; 폴; ) HANGUL SYLLABLE POL
+D3F5;D3F5;1111 1169 11B0;D3F5;1111 1169 11B0; # (íµ; íµ; 폵; íµ; 폵; ) HANGUL SYLLABLE POLG
+D3F6;D3F6;1111 1169 11B1;D3F6;1111 1169 11B1; # (í¶; í¶; 폶; í¶; 폶; ) HANGUL SYLLABLE POLM
+D3F7;D3F7;1111 1169 11B2;D3F7;1111 1169 11B2; # (í·; í·; 폷; í·; 폷; ) HANGUL SYLLABLE POLB
+D3F8;D3F8;1111 1169 11B3;D3F8;1111 1169 11B3; # (í¸; í¸; 폸; í¸; 폸; ) HANGUL SYLLABLE POLS
+D3F9;D3F9;1111 1169 11B4;D3F9;1111 1169 11B4; # (í¹; í¹; 폹; í¹; 폹; ) HANGUL SYLLABLE POLT
+D3FA;D3FA;1111 1169 11B5;D3FA;1111 1169 11B5; # (íº; íº; 폺; íº; 폺; ) HANGUL SYLLABLE POLP
+D3FB;D3FB;1111 1169 11B6;D3FB;1111 1169 11B6; # (í»; í»; 폻; í»; 폻; ) HANGUL SYLLABLE POLH
+D3FC;D3FC;1111 1169 11B7;D3FC;1111 1169 11B7; # (í¼; í¼; 폼; í¼; 폼; ) HANGUL SYLLABLE POM
+D3FD;D3FD;1111 1169 11B8;D3FD;1111 1169 11B8; # (í½; í½; 폽; í½; 폽; ) HANGUL SYLLABLE POB
+D3FE;D3FE;1111 1169 11B9;D3FE;1111 1169 11B9; # (í¾; í¾; 폾; í¾; 폾; ) HANGUL SYLLABLE POBS
+D3FF;D3FF;1111 1169 11BA;D3FF;1111 1169 11BA; # (í¿; í¿; 폿; í¿; 폿; ) HANGUL SYLLABLE POS
+D400;D400;1111 1169 11BB;D400;1111 1169 11BB; # (í€; í€; 퐀; í€; 퐀; ) HANGUL SYLLABLE POSS
+D401;D401;1111 1169 11BC;D401;1111 1169 11BC; # (í; í; 퐁; í; 퐁; ) HANGUL SYLLABLE PONG
+D402;D402;1111 1169 11BD;D402;1111 1169 11BD; # (í‚; í‚; 퐂; í‚; 퐂; ) HANGUL SYLLABLE POJ
+D403;D403;1111 1169 11BE;D403;1111 1169 11BE; # (íƒ; íƒ; 퐃; íƒ; 퐃; ) HANGUL SYLLABLE POC
+D404;D404;1111 1169 11BF;D404;1111 1169 11BF; # (í„; í„; 퐄; í„; 퐄; ) HANGUL SYLLABLE POK
+D405;D405;1111 1169 11C0;D405;1111 1169 11C0; # (í…; í…; 퐅; í…; 퐅; ) HANGUL SYLLABLE POT
+D406;D406;1111 1169 11C1;D406;1111 1169 11C1; # (í†; í†; á„‘á…©á‡; í†; á„‘á…©á‡; ) HANGUL SYLLABLE POP
+D407;D407;1111 1169 11C2;D407;1111 1169 11C2; # (í‡; í‡; 퐇; í‡; 퐇; ) HANGUL SYLLABLE POH
+D408;D408;1111 116A;D408;1111 116A; # (íˆ; íˆ; á„‘á…ª; íˆ; á„‘á…ª; ) HANGUL SYLLABLE PWA
+D409;D409;1111 116A 11A8;D409;1111 116A 11A8; # (í‰; í‰; 퐉; í‰; 퐉; ) HANGUL SYLLABLE PWAG
+D40A;D40A;1111 116A 11A9;D40A;1111 116A 11A9; # (íŠ; íŠ; 퐊; íŠ; 퐊; ) HANGUL SYLLABLE PWAGG
+D40B;D40B;1111 116A 11AA;D40B;1111 116A 11AA; # (í‹; í‹; 퐋; í‹; 퐋; ) HANGUL SYLLABLE PWAGS
+D40C;D40C;1111 116A 11AB;D40C;1111 116A 11AB; # (íŒ; íŒ; 퐌; íŒ; 퐌; ) HANGUL SYLLABLE PWAN
+D40D;D40D;1111 116A 11AC;D40D;1111 116A 11AC; # (í; í; 퐍; í; 퐍; ) HANGUL SYLLABLE PWANJ
+D40E;D40E;1111 116A 11AD;D40E;1111 116A 11AD; # (íŽ; íŽ; 퐎; íŽ; 퐎; ) HANGUL SYLLABLE PWANH
+D40F;D40F;1111 116A 11AE;D40F;1111 116A 11AE; # (í; í; 퐏; í; 퐏; ) HANGUL SYLLABLE PWAD
+D410;D410;1111 116A 11AF;D410;1111 116A 11AF; # (í; í; 퐐; í; 퐐; ) HANGUL SYLLABLE PWAL
+D411;D411;1111 116A 11B0;D411;1111 116A 11B0; # (í‘; í‘; 퐑; í‘; 퐑; ) HANGUL SYLLABLE PWALG
+D412;D412;1111 116A 11B1;D412;1111 116A 11B1; # (í’; í’; 퐒; í’; 퐒; ) HANGUL SYLLABLE PWALM
+D413;D413;1111 116A 11B2;D413;1111 116A 11B2; # (í“; í“; 퐓; í“; 퐓; ) HANGUL SYLLABLE PWALB
+D414;D414;1111 116A 11B3;D414;1111 116A 11B3; # (í”; í”; 퐔; í”; 퐔; ) HANGUL SYLLABLE PWALS
+D415;D415;1111 116A 11B4;D415;1111 116A 11B4; # (í•; í•; 퐕; í•; 퐕; ) HANGUL SYLLABLE PWALT
+D416;D416;1111 116A 11B5;D416;1111 116A 11B5; # (í–; í–; 퐖; í–; 퐖; ) HANGUL SYLLABLE PWALP
+D417;D417;1111 116A 11B6;D417;1111 116A 11B6; # (í—; í—; 퐗; í—; 퐗; ) HANGUL SYLLABLE PWALH
+D418;D418;1111 116A 11B7;D418;1111 116A 11B7; # (í˜; í˜; 퐘; í˜; 퐘; ) HANGUL SYLLABLE PWAM
+D419;D419;1111 116A 11B8;D419;1111 116A 11B8; # (í™; í™; 퐙; í™; 퐙; ) HANGUL SYLLABLE PWAB
+D41A;D41A;1111 116A 11B9;D41A;1111 116A 11B9; # (íš; íš; 퐚; íš; 퐚; ) HANGUL SYLLABLE PWABS
+D41B;D41B;1111 116A 11BA;D41B;1111 116A 11BA; # (í›; í›; 퐛; í›; 퐛; ) HANGUL SYLLABLE PWAS
+D41C;D41C;1111 116A 11BB;D41C;1111 116A 11BB; # (íœ; íœ; 퐜; íœ; 퐜; ) HANGUL SYLLABLE PWASS
+D41D;D41D;1111 116A 11BC;D41D;1111 116A 11BC; # (í; í; 퐝; í; 퐝; ) HANGUL SYLLABLE PWANG
+D41E;D41E;1111 116A 11BD;D41E;1111 116A 11BD; # (íž; íž; 퐞; íž; 퐞; ) HANGUL SYLLABLE PWAJ
+D41F;D41F;1111 116A 11BE;D41F;1111 116A 11BE; # (íŸ; íŸ; 퐟; íŸ; 퐟; ) HANGUL SYLLABLE PWAC
+D420;D420;1111 116A 11BF;D420;1111 116A 11BF; # (í ; í ; 퐠; í ; 퐠; ) HANGUL SYLLABLE PWAK
+D421;D421;1111 116A 11C0;D421;1111 116A 11C0; # (í¡; í¡; 퐡; í¡; 퐡; ) HANGUL SYLLABLE PWAT
+D422;D422;1111 116A 11C1;D422;1111 116A 11C1; # (í¢; í¢; á„‘á…ªá‡; í¢; á„‘á…ªá‡; ) HANGUL SYLLABLE PWAP
+D423;D423;1111 116A 11C2;D423;1111 116A 11C2; # (í£; í£; 퐣; í£; 퐣; ) HANGUL SYLLABLE PWAH
+D424;D424;1111 116B;D424;1111 116B; # (í¤; í¤; á„‘á…«; í¤; á„‘á…«; ) HANGUL SYLLABLE PWAE
+D425;D425;1111 116B 11A8;D425;1111 116B 11A8; # (í¥; í¥; 퐥; í¥; 퐥; ) HANGUL SYLLABLE PWAEG
+D426;D426;1111 116B 11A9;D426;1111 116B 11A9; # (í¦; í¦; 퐦; í¦; 퐦; ) HANGUL SYLLABLE PWAEGG
+D427;D427;1111 116B 11AA;D427;1111 116B 11AA; # (í§; í§; 퐧; í§; 퐧; ) HANGUL SYLLABLE PWAEGS
+D428;D428;1111 116B 11AB;D428;1111 116B 11AB; # (í¨; í¨; 퐨; í¨; 퐨; ) HANGUL SYLLABLE PWAEN
+D429;D429;1111 116B 11AC;D429;1111 116B 11AC; # (í©; í©; 퐩; í©; 퐩; ) HANGUL SYLLABLE PWAENJ
+D42A;D42A;1111 116B 11AD;D42A;1111 116B 11AD; # (íª; íª; 퐪; íª; 퐪; ) HANGUL SYLLABLE PWAENH
+D42B;D42B;1111 116B 11AE;D42B;1111 116B 11AE; # (í«; í«; 퐫; í«; 퐫; ) HANGUL SYLLABLE PWAED
+D42C;D42C;1111 116B 11AF;D42C;1111 116B 11AF; # (í¬; í¬; 퐬; í¬; 퐬; ) HANGUL SYLLABLE PWAEL
+D42D;D42D;1111 116B 11B0;D42D;1111 116B 11B0; # (í­; í­; 퐭; í­; 퐭; ) HANGUL SYLLABLE PWAELG
+D42E;D42E;1111 116B 11B1;D42E;1111 116B 11B1; # (í®; í®; 퐮; í®; 퐮; ) HANGUL SYLLABLE PWAELM
+D42F;D42F;1111 116B 11B2;D42F;1111 116B 11B2; # (í¯; í¯; 퐯; í¯; 퐯; ) HANGUL SYLLABLE PWAELB
+D430;D430;1111 116B 11B3;D430;1111 116B 11B3; # (í°; í°; 퐰; í°; 퐰; ) HANGUL SYLLABLE PWAELS
+D431;D431;1111 116B 11B4;D431;1111 116B 11B4; # (í±; í±; 퐱; í±; 퐱; ) HANGUL SYLLABLE PWAELT
+D432;D432;1111 116B 11B5;D432;1111 116B 11B5; # (í²; í²; 퐲; í²; 퐲; ) HANGUL SYLLABLE PWAELP
+D433;D433;1111 116B 11B6;D433;1111 116B 11B6; # (í³; í³; 퐳; í³; 퐳; ) HANGUL SYLLABLE PWAELH
+D434;D434;1111 116B 11B7;D434;1111 116B 11B7; # (í´; í´; 퐴; í´; 퐴; ) HANGUL SYLLABLE PWAEM
+D435;D435;1111 116B 11B8;D435;1111 116B 11B8; # (íµ; íµ; 퐵; íµ; 퐵; ) HANGUL SYLLABLE PWAEB
+D436;D436;1111 116B 11B9;D436;1111 116B 11B9; # (í¶; í¶; 퐶; í¶; 퐶; ) HANGUL SYLLABLE PWAEBS
+D437;D437;1111 116B 11BA;D437;1111 116B 11BA; # (í·; í·; 퐷; í·; 퐷; ) HANGUL SYLLABLE PWAES
+D438;D438;1111 116B 11BB;D438;1111 116B 11BB; # (í¸; í¸; 퐸; í¸; 퐸; ) HANGUL SYLLABLE PWAESS
+D439;D439;1111 116B 11BC;D439;1111 116B 11BC; # (í¹; í¹; 퐹; í¹; 퐹; ) HANGUL SYLLABLE PWAENG
+D43A;D43A;1111 116B 11BD;D43A;1111 116B 11BD; # (íº; íº; 퐺; íº; 퐺; ) HANGUL SYLLABLE PWAEJ
+D43B;D43B;1111 116B 11BE;D43B;1111 116B 11BE; # (í»; í»; 퐻; í»; 퐻; ) HANGUL SYLLABLE PWAEC
+D43C;D43C;1111 116B 11BF;D43C;1111 116B 11BF; # (í¼; í¼; 퐼; í¼; 퐼; ) HANGUL SYLLABLE PWAEK
+D43D;D43D;1111 116B 11C0;D43D;1111 116B 11C0; # (í½; í½; 퐽; í½; 퐽; ) HANGUL SYLLABLE PWAET
+D43E;D43E;1111 116B 11C1;D43E;1111 116B 11C1; # (í¾; í¾; á„‘á…«á‡; í¾; á„‘á…«á‡; ) HANGUL SYLLABLE PWAEP
+D43F;D43F;1111 116B 11C2;D43F;1111 116B 11C2; # (í¿; í¿; 퐿; í¿; 퐿; ) HANGUL SYLLABLE PWAEH
+D440;D440;1111 116C;D440;1111 116C; # (í‘€; í‘€; á„‘á…¬; í‘€; á„‘á…¬; ) HANGUL SYLLABLE POE
+D441;D441;1111 116C 11A8;D441;1111 116C 11A8; # (í‘; í‘; 푁; í‘; 푁; ) HANGUL SYLLABLE POEG
+D442;D442;1111 116C 11A9;D442;1111 116C 11A9; # (푂; 푂; 푂; 푂; 푂; ) HANGUL SYLLABLE POEGG
+D443;D443;1111 116C 11AA;D443;1111 116C 11AA; # (푃; 푃; 푃; 푃; 푃; ) HANGUL SYLLABLE POEGS
+D444;D444;1111 116C 11AB;D444;1111 116C 11AB; # (푄; 푄; 푄; 푄; 푄; ) HANGUL SYLLABLE POEN
+D445;D445;1111 116C 11AC;D445;1111 116C 11AC; # (푅; 푅; 푅; 푅; 푅; ) HANGUL SYLLABLE POENJ
+D446;D446;1111 116C 11AD;D446;1111 116C 11AD; # (푆; 푆; 푆; 푆; 푆; ) HANGUL SYLLABLE POENH
+D447;D447;1111 116C 11AE;D447;1111 116C 11AE; # (푇; 푇; 푇; 푇; 푇; ) HANGUL SYLLABLE POED
+D448;D448;1111 116C 11AF;D448;1111 116C 11AF; # (푈; 푈; 푈; 푈; 푈; ) HANGUL SYLLABLE POEL
+D449;D449;1111 116C 11B0;D449;1111 116C 11B0; # (푉; 푉; 푉; 푉; 푉; ) HANGUL SYLLABLE POELG
+D44A;D44A;1111 116C 11B1;D44A;1111 116C 11B1; # (푊; 푊; 푊; 푊; 푊; ) HANGUL SYLLABLE POELM
+D44B;D44B;1111 116C 11B2;D44B;1111 116C 11B2; # (푋; 푋; 푋; 푋; 푋; ) HANGUL SYLLABLE POELB
+D44C;D44C;1111 116C 11B3;D44C;1111 116C 11B3; # (푌; 푌; 푌; 푌; 푌; ) HANGUL SYLLABLE POELS
+D44D;D44D;1111 116C 11B4;D44D;1111 116C 11B4; # (í‘; í‘; 푍; í‘; 푍; ) HANGUL SYLLABLE POELT
+D44E;D44E;1111 116C 11B5;D44E;1111 116C 11B5; # (푎; 푎; 푎; 푎; 푎; ) HANGUL SYLLABLE POELP
+D44F;D44F;1111 116C 11B6;D44F;1111 116C 11B6; # (í‘; í‘; 푏; í‘; 푏; ) HANGUL SYLLABLE POELH
+D450;D450;1111 116C 11B7;D450;1111 116C 11B7; # (í‘; í‘; 푐; í‘; 푐; ) HANGUL SYLLABLE POEM
+D451;D451;1111 116C 11B8;D451;1111 116C 11B8; # (푑; 푑; 푑; 푑; 푑; ) HANGUL SYLLABLE POEB
+D452;D452;1111 116C 11B9;D452;1111 116C 11B9; # (푒; 푒; 푒; 푒; 푒; ) HANGUL SYLLABLE POEBS
+D453;D453;1111 116C 11BA;D453;1111 116C 11BA; # (푓; 푓; 푓; 푓; 푓; ) HANGUL SYLLABLE POES
+D454;D454;1111 116C 11BB;D454;1111 116C 11BB; # (푔; 푔; 푔; 푔; 푔; ) HANGUL SYLLABLE POESS
+D455;D455;1111 116C 11BC;D455;1111 116C 11BC; # (푕; 푕; 푕; 푕; 푕; ) HANGUL SYLLABLE POENG
+D456;D456;1111 116C 11BD;D456;1111 116C 11BD; # (푖; 푖; 푖; 푖; 푖; ) HANGUL SYLLABLE POEJ
+D457;D457;1111 116C 11BE;D457;1111 116C 11BE; # (푗; 푗; 푗; 푗; 푗; ) HANGUL SYLLABLE POEC
+D458;D458;1111 116C 11BF;D458;1111 116C 11BF; # (푘; 푘; 푘; 푘; 푘; ) HANGUL SYLLABLE POEK
+D459;D459;1111 116C 11C0;D459;1111 116C 11C0; # (푙; 푙; 푙; 푙; 푙; ) HANGUL SYLLABLE POET
+D45A;D45A;1111 116C 11C1;D45A;1111 116C 11C1; # (푚; 푚; á„‘á…¬á‡; 푚; á„‘á…¬á‡; ) HANGUL SYLLABLE POEP
+D45B;D45B;1111 116C 11C2;D45B;1111 116C 11C2; # (푛; 푛; 푛; 푛; 푛; ) HANGUL SYLLABLE POEH
+D45C;D45C;1111 116D;D45C;1111 116D; # (표; 표; 표; 표; 표; ) HANGUL SYLLABLE PYO
+D45D;D45D;1111 116D 11A8;D45D;1111 116D 11A8; # (í‘; í‘; 푝; í‘; 푝; ) HANGUL SYLLABLE PYOG
+D45E;D45E;1111 116D 11A9;D45E;1111 116D 11A9; # (푞; 푞; 푞; 푞; 푞; ) HANGUL SYLLABLE PYOGG
+D45F;D45F;1111 116D 11AA;D45F;1111 116D 11AA; # (푟; 푟; 푟; 푟; 푟; ) HANGUL SYLLABLE PYOGS
+D460;D460;1111 116D 11AB;D460;1111 116D 11AB; # (푠; 푠; 푠; 푠; 푠; ) HANGUL SYLLABLE PYON
+D461;D461;1111 116D 11AC;D461;1111 116D 11AC; # (푡; 푡; 푡; 푡; 푡; ) HANGUL SYLLABLE PYONJ
+D462;D462;1111 116D 11AD;D462;1111 116D 11AD; # (푢; 푢; 푢; 푢; 푢; ) HANGUL SYLLABLE PYONH
+D463;D463;1111 116D 11AE;D463;1111 116D 11AE; # (푣; 푣; 푣; 푣; 푣; ) HANGUL SYLLABLE PYOD
+D464;D464;1111 116D 11AF;D464;1111 116D 11AF; # (푤; 푤; 푤; 푤; 푤; ) HANGUL SYLLABLE PYOL
+D465;D465;1111 116D 11B0;D465;1111 116D 11B0; # (푥; 푥; 푥; 푥; 푥; ) HANGUL SYLLABLE PYOLG
+D466;D466;1111 116D 11B1;D466;1111 116D 11B1; # (푦; 푦; 푦; 푦; 푦; ) HANGUL SYLLABLE PYOLM
+D467;D467;1111 116D 11B2;D467;1111 116D 11B2; # (푧; 푧; 푧; 푧; 푧; ) HANGUL SYLLABLE PYOLB
+D468;D468;1111 116D 11B3;D468;1111 116D 11B3; # (푨; 푨; 푨; 푨; 푨; ) HANGUL SYLLABLE PYOLS
+D469;D469;1111 116D 11B4;D469;1111 116D 11B4; # (푩; 푩; 푩; 푩; 푩; ) HANGUL SYLLABLE PYOLT
+D46A;D46A;1111 116D 11B5;D46A;1111 116D 11B5; # (푪; 푪; 푪; 푪; 푪; ) HANGUL SYLLABLE PYOLP
+D46B;D46B;1111 116D 11B6;D46B;1111 116D 11B6; # (푫; 푫; 푫; 푫; 푫; ) HANGUL SYLLABLE PYOLH
+D46C;D46C;1111 116D 11B7;D46C;1111 116D 11B7; # (푬; 푬; 푬; 푬; 푬; ) HANGUL SYLLABLE PYOM
+D46D;D46D;1111 116D 11B8;D46D;1111 116D 11B8; # (푭; 푭; 푭; 푭; 푭; ) HANGUL SYLLABLE PYOB
+D46E;D46E;1111 116D 11B9;D46E;1111 116D 11B9; # (푮; 푮; 푮; 푮; 푮; ) HANGUL SYLLABLE PYOBS
+D46F;D46F;1111 116D 11BA;D46F;1111 116D 11BA; # (푯; 푯; 푯; 푯; 푯; ) HANGUL SYLLABLE PYOS
+D470;D470;1111 116D 11BB;D470;1111 116D 11BB; # (푰; 푰; 푰; 푰; 푰; ) HANGUL SYLLABLE PYOSS
+D471;D471;1111 116D 11BC;D471;1111 116D 11BC; # (푱; 푱; 푱; 푱; 푱; ) HANGUL SYLLABLE PYONG
+D472;D472;1111 116D 11BD;D472;1111 116D 11BD; # (푲; 푲; 푲; 푲; 푲; ) HANGUL SYLLABLE PYOJ
+D473;D473;1111 116D 11BE;D473;1111 116D 11BE; # (푳; 푳; 푳; 푳; 푳; ) HANGUL SYLLABLE PYOC
+D474;D474;1111 116D 11BF;D474;1111 116D 11BF; # (푴; 푴; 푴; 푴; 푴; ) HANGUL SYLLABLE PYOK
+D475;D475;1111 116D 11C0;D475;1111 116D 11C0; # (푵; 푵; 푵; 푵; 푵; ) HANGUL SYLLABLE PYOT
+D476;D476;1111 116D 11C1;D476;1111 116D 11C1; # (í‘¶; í‘¶; á„‘á…­á‡; í‘¶; á„‘á…­á‡; ) HANGUL SYLLABLE PYOP
+D477;D477;1111 116D 11C2;D477;1111 116D 11C2; # (푷; 푷; 푷; 푷; 푷; ) HANGUL SYLLABLE PYOH
+D478;D478;1111 116E;D478;1111 116E; # (푸; 푸; 푸; 푸; 푸; ) HANGUL SYLLABLE PU
+D479;D479;1111 116E 11A8;D479;1111 116E 11A8; # (푹; 푹; 푹; 푹; 푹; ) HANGUL SYLLABLE PUG
+D47A;D47A;1111 116E 11A9;D47A;1111 116E 11A9; # (푺; 푺; 푺; 푺; 푺; ) HANGUL SYLLABLE PUGG
+D47B;D47B;1111 116E 11AA;D47B;1111 116E 11AA; # (푻; 푻; 푻; 푻; 푻; ) HANGUL SYLLABLE PUGS
+D47C;D47C;1111 116E 11AB;D47C;1111 116E 11AB; # (푼; 푼; 푼; 푼; 푼; ) HANGUL SYLLABLE PUN
+D47D;D47D;1111 116E 11AC;D47D;1111 116E 11AC; # (푽; 푽; 푽; 푽; 푽; ) HANGUL SYLLABLE PUNJ
+D47E;D47E;1111 116E 11AD;D47E;1111 116E 11AD; # (푾; 푾; 푾; 푾; 푾; ) HANGUL SYLLABLE PUNH
+D47F;D47F;1111 116E 11AE;D47F;1111 116E 11AE; # (푿; 푿; 푿; 푿; 푿; ) HANGUL SYLLABLE PUD
+D480;D480;1111 116E 11AF;D480;1111 116E 11AF; # (풀; 풀; 풀; 풀; 풀; ) HANGUL SYLLABLE PUL
+D481;D481;1111 116E 11B0;D481;1111 116E 11B0; # (í’; í’; 풁; í’; 풁; ) HANGUL SYLLABLE PULG
+D482;D482;1111 116E 11B1;D482;1111 116E 11B1; # (풂; 풂; 풂; 풂; 풂; ) HANGUL SYLLABLE PULM
+D483;D483;1111 116E 11B2;D483;1111 116E 11B2; # (풃; 풃; 풃; 풃; 풃; ) HANGUL SYLLABLE PULB
+D484;D484;1111 116E 11B3;D484;1111 116E 11B3; # (풄; 풄; 풄; 풄; 풄; ) HANGUL SYLLABLE PULS
+D485;D485;1111 116E 11B4;D485;1111 116E 11B4; # (풅; 풅; 풅; 풅; 풅; ) HANGUL SYLLABLE PULT
+D486;D486;1111 116E 11B5;D486;1111 116E 11B5; # (풆; 풆; 풆; 풆; 풆; ) HANGUL SYLLABLE PULP
+D487;D487;1111 116E 11B6;D487;1111 116E 11B6; # (풇; 풇; 풇; 풇; 풇; ) HANGUL SYLLABLE PULH
+D488;D488;1111 116E 11B7;D488;1111 116E 11B7; # (품; 품; 품; 품; 품; ) HANGUL SYLLABLE PUM
+D489;D489;1111 116E 11B8;D489;1111 116E 11B8; # (풉; 풉; 풉; 풉; 풉; ) HANGUL SYLLABLE PUB
+D48A;D48A;1111 116E 11B9;D48A;1111 116E 11B9; # (풊; 풊; 풊; 풊; 풊; ) HANGUL SYLLABLE PUBS
+D48B;D48B;1111 116E 11BA;D48B;1111 116E 11BA; # (풋; 풋; 풋; 풋; 풋; ) HANGUL SYLLABLE PUS
+D48C;D48C;1111 116E 11BB;D48C;1111 116E 11BB; # (풌; 풌; 풌; 풌; 풌; ) HANGUL SYLLABLE PUSS
+D48D;D48D;1111 116E 11BC;D48D;1111 116E 11BC; # (í’; í’; 풍; í’; 풍; ) HANGUL SYLLABLE PUNG
+D48E;D48E;1111 116E 11BD;D48E;1111 116E 11BD; # (풎; 풎; 풎; 풎; 풎; ) HANGUL SYLLABLE PUJ
+D48F;D48F;1111 116E 11BE;D48F;1111 116E 11BE; # (í’; í’; 풏; í’; 풏; ) HANGUL SYLLABLE PUC
+D490;D490;1111 116E 11BF;D490;1111 116E 11BF; # (í’; í’; 풐; í’; 풐; ) HANGUL SYLLABLE PUK
+D491;D491;1111 116E 11C0;D491;1111 116E 11C0; # (풑; 풑; 풑; 풑; 풑; ) HANGUL SYLLABLE PUT
+D492;D492;1111 116E 11C1;D492;1111 116E 11C1; # (í’’; í’’; á„‘á…®á‡; í’’; á„‘á…®á‡; ) HANGUL SYLLABLE PUP
+D493;D493;1111 116E 11C2;D493;1111 116E 11C2; # (풓; 풓; 풓; 풓; 풓; ) HANGUL SYLLABLE PUH
+D494;D494;1111 116F;D494;1111 116F; # (í’”; í’”; á„‘á…¯; í’”; á„‘á…¯; ) HANGUL SYLLABLE PWEO
+D495;D495;1111 116F 11A8;D495;1111 116F 11A8; # (풕; 풕; 풕; 풕; 풕; ) HANGUL SYLLABLE PWEOG
+D496;D496;1111 116F 11A9;D496;1111 116F 11A9; # (풖; 풖; 풖; 풖; 풖; ) HANGUL SYLLABLE PWEOGG
+D497;D497;1111 116F 11AA;D497;1111 116F 11AA; # (풗; 풗; 풗; 풗; 풗; ) HANGUL SYLLABLE PWEOGS
+D498;D498;1111 116F 11AB;D498;1111 116F 11AB; # (풘; 풘; 풘; 풘; 풘; ) HANGUL SYLLABLE PWEON
+D499;D499;1111 116F 11AC;D499;1111 116F 11AC; # (풙; 풙; 풙; 풙; 풙; ) HANGUL SYLLABLE PWEONJ
+D49A;D49A;1111 116F 11AD;D49A;1111 116F 11AD; # (풚; 풚; 풚; 풚; 풚; ) HANGUL SYLLABLE PWEONH
+D49B;D49B;1111 116F 11AE;D49B;1111 116F 11AE; # (풛; 풛; 풛; 풛; 풛; ) HANGUL SYLLABLE PWEOD
+D49C;D49C;1111 116F 11AF;D49C;1111 116F 11AF; # (풜; 풜; 풜; 풜; 풜; ) HANGUL SYLLABLE PWEOL
+D49D;D49D;1111 116F 11B0;D49D;1111 116F 11B0; # (í’; í’; 풝; í’; 풝; ) HANGUL SYLLABLE PWEOLG
+D49E;D49E;1111 116F 11B1;D49E;1111 116F 11B1; # (풞; 풞; 풞; 풞; 풞; ) HANGUL SYLLABLE PWEOLM
+D49F;D49F;1111 116F 11B2;D49F;1111 116F 11B2; # (풟; 풟; 풟; 풟; 풟; ) HANGUL SYLLABLE PWEOLB
+D4A0;D4A0;1111 116F 11B3;D4A0;1111 116F 11B3; # (풠; 풠; 풠; 풠; 풠; ) HANGUL SYLLABLE PWEOLS
+D4A1;D4A1;1111 116F 11B4;D4A1;1111 116F 11B4; # (풡; 풡; 풡; 풡; 풡; ) HANGUL SYLLABLE PWEOLT
+D4A2;D4A2;1111 116F 11B5;D4A2;1111 116F 11B5; # (풢; 풢; 풢; 풢; 풢; ) HANGUL SYLLABLE PWEOLP
+D4A3;D4A3;1111 116F 11B6;D4A3;1111 116F 11B6; # (풣; 풣; 풣; 풣; 풣; ) HANGUL SYLLABLE PWEOLH
+D4A4;D4A4;1111 116F 11B7;D4A4;1111 116F 11B7; # (풤; 풤; 풤; 풤; 풤; ) HANGUL SYLLABLE PWEOM
+D4A5;D4A5;1111 116F 11B8;D4A5;1111 116F 11B8; # (풥; 풥; 풥; 풥; 풥; ) HANGUL SYLLABLE PWEOB
+D4A6;D4A6;1111 116F 11B9;D4A6;1111 116F 11B9; # (풦; 풦; 풦; 풦; 풦; ) HANGUL SYLLABLE PWEOBS
+D4A7;D4A7;1111 116F 11BA;D4A7;1111 116F 11BA; # (풧; 풧; 풧; 풧; 풧; ) HANGUL SYLLABLE PWEOS
+D4A8;D4A8;1111 116F 11BB;D4A8;1111 116F 11BB; # (풨; 풨; 풨; 풨; 풨; ) HANGUL SYLLABLE PWEOSS
+D4A9;D4A9;1111 116F 11BC;D4A9;1111 116F 11BC; # (풩; 풩; 풩; 풩; 풩; ) HANGUL SYLLABLE PWEONG
+D4AA;D4AA;1111 116F 11BD;D4AA;1111 116F 11BD; # (풪; 풪; 풪; 풪; 풪; ) HANGUL SYLLABLE PWEOJ
+D4AB;D4AB;1111 116F 11BE;D4AB;1111 116F 11BE; # (풫; 풫; 풫; 풫; 풫; ) HANGUL SYLLABLE PWEOC
+D4AC;D4AC;1111 116F 11BF;D4AC;1111 116F 11BF; # (풬; 풬; 풬; 풬; 풬; ) HANGUL SYLLABLE PWEOK
+D4AD;D4AD;1111 116F 11C0;D4AD;1111 116F 11C0; # (풭; 풭; 풭; 풭; 풭; ) HANGUL SYLLABLE PWEOT
+D4AE;D4AE;1111 116F 11C1;D4AE;1111 116F 11C1; # (í’®; í’®; á„‘á…¯á‡; í’®; á„‘á…¯á‡; ) HANGUL SYLLABLE PWEOP
+D4AF;D4AF;1111 116F 11C2;D4AF;1111 116F 11C2; # (풯; 풯; 풯; 풯; 풯; ) HANGUL SYLLABLE PWEOH
+D4B0;D4B0;1111 1170;D4B0;1111 1170; # (í’°; í’°; á„‘á…°; í’°; á„‘á…°; ) HANGUL SYLLABLE PWE
+D4B1;D4B1;1111 1170 11A8;D4B1;1111 1170 11A8; # (풱; 풱; 풱; 풱; 풱; ) HANGUL SYLLABLE PWEG
+D4B2;D4B2;1111 1170 11A9;D4B2;1111 1170 11A9; # (풲; 풲; 풲; 풲; 풲; ) HANGUL SYLLABLE PWEGG
+D4B3;D4B3;1111 1170 11AA;D4B3;1111 1170 11AA; # (풳; 풳; 풳; 풳; 풳; ) HANGUL SYLLABLE PWEGS
+D4B4;D4B4;1111 1170 11AB;D4B4;1111 1170 11AB; # (풴; 풴; 풴; 풴; 풴; ) HANGUL SYLLABLE PWEN
+D4B5;D4B5;1111 1170 11AC;D4B5;1111 1170 11AC; # (풵; 풵; 풵; 풵; 풵; ) HANGUL SYLLABLE PWENJ
+D4B6;D4B6;1111 1170 11AD;D4B6;1111 1170 11AD; # (풶; 풶; 풶; 풶; 풶; ) HANGUL SYLLABLE PWENH
+D4B7;D4B7;1111 1170 11AE;D4B7;1111 1170 11AE; # (풷; 풷; 풷; 풷; 풷; ) HANGUL SYLLABLE PWED
+D4B8;D4B8;1111 1170 11AF;D4B8;1111 1170 11AF; # (풸; 풸; 풸; 풸; 풸; ) HANGUL SYLLABLE PWEL
+D4B9;D4B9;1111 1170 11B0;D4B9;1111 1170 11B0; # (풹; 풹; 풹; 풹; 풹; ) HANGUL SYLLABLE PWELG
+D4BA;D4BA;1111 1170 11B1;D4BA;1111 1170 11B1; # (풺; 풺; 풺; 풺; 풺; ) HANGUL SYLLABLE PWELM
+D4BB;D4BB;1111 1170 11B2;D4BB;1111 1170 11B2; # (풻; 풻; 풻; 풻; 풻; ) HANGUL SYLLABLE PWELB
+D4BC;D4BC;1111 1170 11B3;D4BC;1111 1170 11B3; # (풼; 풼; 풼; 풼; 풼; ) HANGUL SYLLABLE PWELS
+D4BD;D4BD;1111 1170 11B4;D4BD;1111 1170 11B4; # (풽; 풽; 풽; 풽; 풽; ) HANGUL SYLLABLE PWELT
+D4BE;D4BE;1111 1170 11B5;D4BE;1111 1170 11B5; # (풾; 풾; 풾; 풾; 풾; ) HANGUL SYLLABLE PWELP
+D4BF;D4BF;1111 1170 11B6;D4BF;1111 1170 11B6; # (풿; 풿; 풿; 풿; 풿; ) HANGUL SYLLABLE PWELH
+D4C0;D4C0;1111 1170 11B7;D4C0;1111 1170 11B7; # (퓀; 퓀; 퓀; 퓀; 퓀; ) HANGUL SYLLABLE PWEM
+D4C1;D4C1;1111 1170 11B8;D4C1;1111 1170 11B8; # (í“; í“; 퓁; í“; 퓁; ) HANGUL SYLLABLE PWEB
+D4C2;D4C2;1111 1170 11B9;D4C2;1111 1170 11B9; # (퓂; 퓂; 퓂; 퓂; 퓂; ) HANGUL SYLLABLE PWEBS
+D4C3;D4C3;1111 1170 11BA;D4C3;1111 1170 11BA; # (퓃; 퓃; 퓃; 퓃; 퓃; ) HANGUL SYLLABLE PWES
+D4C4;D4C4;1111 1170 11BB;D4C4;1111 1170 11BB; # (퓄; 퓄; 퓄; 퓄; 퓄; ) HANGUL SYLLABLE PWESS
+D4C5;D4C5;1111 1170 11BC;D4C5;1111 1170 11BC; # (퓅; 퓅; 퓅; 퓅; 퓅; ) HANGUL SYLLABLE PWENG
+D4C6;D4C6;1111 1170 11BD;D4C6;1111 1170 11BD; # (퓆; 퓆; 퓆; 퓆; 퓆; ) HANGUL SYLLABLE PWEJ
+D4C7;D4C7;1111 1170 11BE;D4C7;1111 1170 11BE; # (퓇; 퓇; 퓇; 퓇; 퓇; ) HANGUL SYLLABLE PWEC
+D4C8;D4C8;1111 1170 11BF;D4C8;1111 1170 11BF; # (퓈; 퓈; 퓈; 퓈; 퓈; ) HANGUL SYLLABLE PWEK
+D4C9;D4C9;1111 1170 11C0;D4C9;1111 1170 11C0; # (퓉; 퓉; 퓉; 퓉; 퓉; ) HANGUL SYLLABLE PWET
+D4CA;D4CA;1111 1170 11C1;D4CA;1111 1170 11C1; # (퓊; 퓊; á„‘á…°á‡; 퓊; á„‘á…°á‡; ) HANGUL SYLLABLE PWEP
+D4CB;D4CB;1111 1170 11C2;D4CB;1111 1170 11C2; # (퓋; 퓋; 퓋; 퓋; 퓋; ) HANGUL SYLLABLE PWEH
+D4CC;D4CC;1111 1171;D4CC;1111 1171; # (퓌; 퓌; 퓌; 퓌; 퓌; ) HANGUL SYLLABLE PWI
+D4CD;D4CD;1111 1171 11A8;D4CD;1111 1171 11A8; # (í“; í“; 퓍; í“; 퓍; ) HANGUL SYLLABLE PWIG
+D4CE;D4CE;1111 1171 11A9;D4CE;1111 1171 11A9; # (퓎; 퓎; 퓎; 퓎; 퓎; ) HANGUL SYLLABLE PWIGG
+D4CF;D4CF;1111 1171 11AA;D4CF;1111 1171 11AA; # (í“; í“; 퓏; í“; 퓏; ) HANGUL SYLLABLE PWIGS
+D4D0;D4D0;1111 1171 11AB;D4D0;1111 1171 11AB; # (í“; í“; 퓐; í“; 퓐; ) HANGUL SYLLABLE PWIN
+D4D1;D4D1;1111 1171 11AC;D4D1;1111 1171 11AC; # (퓑; 퓑; 퓑; 퓑; 퓑; ) HANGUL SYLLABLE PWINJ
+D4D2;D4D2;1111 1171 11AD;D4D2;1111 1171 11AD; # (퓒; 퓒; 퓒; 퓒; 퓒; ) HANGUL SYLLABLE PWINH
+D4D3;D4D3;1111 1171 11AE;D4D3;1111 1171 11AE; # (퓓; 퓓; 퓓; 퓓; 퓓; ) HANGUL SYLLABLE PWID
+D4D4;D4D4;1111 1171 11AF;D4D4;1111 1171 11AF; # (퓔; 퓔; 퓔; 퓔; 퓔; ) HANGUL SYLLABLE PWIL
+D4D5;D4D5;1111 1171 11B0;D4D5;1111 1171 11B0; # (퓕; 퓕; 퓕; 퓕; 퓕; ) HANGUL SYLLABLE PWILG
+D4D6;D4D6;1111 1171 11B1;D4D6;1111 1171 11B1; # (퓖; 퓖; 퓖; 퓖; 퓖; ) HANGUL SYLLABLE PWILM
+D4D7;D4D7;1111 1171 11B2;D4D7;1111 1171 11B2; # (퓗; 퓗; 퓗; 퓗; 퓗; ) HANGUL SYLLABLE PWILB
+D4D8;D4D8;1111 1171 11B3;D4D8;1111 1171 11B3; # (퓘; 퓘; 퓘; 퓘; 퓘; ) HANGUL SYLLABLE PWILS
+D4D9;D4D9;1111 1171 11B4;D4D9;1111 1171 11B4; # (퓙; 퓙; 퓙; 퓙; 퓙; ) HANGUL SYLLABLE PWILT
+D4DA;D4DA;1111 1171 11B5;D4DA;1111 1171 11B5; # (퓚; 퓚; 퓚; 퓚; 퓚; ) HANGUL SYLLABLE PWILP
+D4DB;D4DB;1111 1171 11B6;D4DB;1111 1171 11B6; # (퓛; 퓛; 퓛; 퓛; 퓛; ) HANGUL SYLLABLE PWILH
+D4DC;D4DC;1111 1171 11B7;D4DC;1111 1171 11B7; # (퓜; 퓜; 퓜; 퓜; 퓜; ) HANGUL SYLLABLE PWIM
+D4DD;D4DD;1111 1171 11B8;D4DD;1111 1171 11B8; # (í“; í“; 퓝; í“; 퓝; ) HANGUL SYLLABLE PWIB
+D4DE;D4DE;1111 1171 11B9;D4DE;1111 1171 11B9; # (퓞; 퓞; 퓞; 퓞; 퓞; ) HANGUL SYLLABLE PWIBS
+D4DF;D4DF;1111 1171 11BA;D4DF;1111 1171 11BA; # (퓟; 퓟; 퓟; 퓟; 퓟; ) HANGUL SYLLABLE PWIS
+D4E0;D4E0;1111 1171 11BB;D4E0;1111 1171 11BB; # (퓠; 퓠; 퓠; 퓠; 퓠; ) HANGUL SYLLABLE PWISS
+D4E1;D4E1;1111 1171 11BC;D4E1;1111 1171 11BC; # (퓡; 퓡; 퓡; 퓡; 퓡; ) HANGUL SYLLABLE PWING
+D4E2;D4E2;1111 1171 11BD;D4E2;1111 1171 11BD; # (퓢; 퓢; 퓢; 퓢; 퓢; ) HANGUL SYLLABLE PWIJ
+D4E3;D4E3;1111 1171 11BE;D4E3;1111 1171 11BE; # (퓣; 퓣; 퓣; 퓣; 퓣; ) HANGUL SYLLABLE PWIC
+D4E4;D4E4;1111 1171 11BF;D4E4;1111 1171 11BF; # (퓤; 퓤; 퓤; 퓤; 퓤; ) HANGUL SYLLABLE PWIK
+D4E5;D4E5;1111 1171 11C0;D4E5;1111 1171 11C0; # (퓥; 퓥; 퓥; 퓥; 퓥; ) HANGUL SYLLABLE PWIT
+D4E6;D4E6;1111 1171 11C1;D4E6;1111 1171 11C1; # (퓦; 퓦; á„‘á…±á‡; 퓦; á„‘á…±á‡; ) HANGUL SYLLABLE PWIP
+D4E7;D4E7;1111 1171 11C2;D4E7;1111 1171 11C2; # (퓧; 퓧; 퓧; 퓧; 퓧; ) HANGUL SYLLABLE PWIH
+D4E8;D4E8;1111 1172;D4E8;1111 1172; # (퓨; 퓨; 퓨; 퓨; 퓨; ) HANGUL SYLLABLE PYU
+D4E9;D4E9;1111 1172 11A8;D4E9;1111 1172 11A8; # (퓩; 퓩; 퓩; 퓩; 퓩; ) HANGUL SYLLABLE PYUG
+D4EA;D4EA;1111 1172 11A9;D4EA;1111 1172 11A9; # (퓪; 퓪; 퓪; 퓪; 퓪; ) HANGUL SYLLABLE PYUGG
+D4EB;D4EB;1111 1172 11AA;D4EB;1111 1172 11AA; # (퓫; 퓫; 퓫; 퓫; 퓫; ) HANGUL SYLLABLE PYUGS
+D4EC;D4EC;1111 1172 11AB;D4EC;1111 1172 11AB; # (퓬; 퓬; 퓬; 퓬; 퓬; ) HANGUL SYLLABLE PYUN
+D4ED;D4ED;1111 1172 11AC;D4ED;1111 1172 11AC; # (퓭; 퓭; 퓭; 퓭; 퓭; ) HANGUL SYLLABLE PYUNJ
+D4EE;D4EE;1111 1172 11AD;D4EE;1111 1172 11AD; # (퓮; 퓮; 퓮; 퓮; 퓮; ) HANGUL SYLLABLE PYUNH
+D4EF;D4EF;1111 1172 11AE;D4EF;1111 1172 11AE; # (퓯; 퓯; 퓯; 퓯; 퓯; ) HANGUL SYLLABLE PYUD
+D4F0;D4F0;1111 1172 11AF;D4F0;1111 1172 11AF; # (퓰; 퓰; 퓰; 퓰; 퓰; ) HANGUL SYLLABLE PYUL
+D4F1;D4F1;1111 1172 11B0;D4F1;1111 1172 11B0; # (퓱; 퓱; 퓱; 퓱; 퓱; ) HANGUL SYLLABLE PYULG
+D4F2;D4F2;1111 1172 11B1;D4F2;1111 1172 11B1; # (퓲; 퓲; 퓲; 퓲; 퓲; ) HANGUL SYLLABLE PYULM
+D4F3;D4F3;1111 1172 11B2;D4F3;1111 1172 11B2; # (퓳; 퓳; 퓳; 퓳; 퓳; ) HANGUL SYLLABLE PYULB
+D4F4;D4F4;1111 1172 11B3;D4F4;1111 1172 11B3; # (퓴; 퓴; 퓴; 퓴; 퓴; ) HANGUL SYLLABLE PYULS
+D4F5;D4F5;1111 1172 11B4;D4F5;1111 1172 11B4; # (퓵; 퓵; 퓵; 퓵; 퓵; ) HANGUL SYLLABLE PYULT
+D4F6;D4F6;1111 1172 11B5;D4F6;1111 1172 11B5; # (퓶; 퓶; 퓶; 퓶; 퓶; ) HANGUL SYLLABLE PYULP
+D4F7;D4F7;1111 1172 11B6;D4F7;1111 1172 11B6; # (퓷; 퓷; 퓷; 퓷; 퓷; ) HANGUL SYLLABLE PYULH
+D4F8;D4F8;1111 1172 11B7;D4F8;1111 1172 11B7; # (퓸; 퓸; 퓸; 퓸; 퓸; ) HANGUL SYLLABLE PYUM
+D4F9;D4F9;1111 1172 11B8;D4F9;1111 1172 11B8; # (퓹; 퓹; 퓹; 퓹; 퓹; ) HANGUL SYLLABLE PYUB
+D4FA;D4FA;1111 1172 11B9;D4FA;1111 1172 11B9; # (퓺; 퓺; 퓺; 퓺; 퓺; ) HANGUL SYLLABLE PYUBS
+D4FB;D4FB;1111 1172 11BA;D4FB;1111 1172 11BA; # (퓻; 퓻; 퓻; 퓻; 퓻; ) HANGUL SYLLABLE PYUS
+D4FC;D4FC;1111 1172 11BB;D4FC;1111 1172 11BB; # (퓼; 퓼; 퓼; 퓼; 퓼; ) HANGUL SYLLABLE PYUSS
+D4FD;D4FD;1111 1172 11BC;D4FD;1111 1172 11BC; # (퓽; 퓽; 퓽; 퓽; 퓽; ) HANGUL SYLLABLE PYUNG
+D4FE;D4FE;1111 1172 11BD;D4FE;1111 1172 11BD; # (퓾; 퓾; 퓾; 퓾; 퓾; ) HANGUL SYLLABLE PYUJ
+D4FF;D4FF;1111 1172 11BE;D4FF;1111 1172 11BE; # (퓿; 퓿; 퓿; 퓿; 퓿; ) HANGUL SYLLABLE PYUC
+D500;D500;1111 1172 11BF;D500;1111 1172 11BF; # (픀; 픀; 픀; 픀; 픀; ) HANGUL SYLLABLE PYUK
+D501;D501;1111 1172 11C0;D501;1111 1172 11C0; # (í”; í”; 픁; í”; 픁; ) HANGUL SYLLABLE PYUT
+D502;D502;1111 1172 11C1;D502;1111 1172 11C1; # (픂; 픂; á„‘á…²á‡; 픂; á„‘á…²á‡; ) HANGUL SYLLABLE PYUP
+D503;D503;1111 1172 11C2;D503;1111 1172 11C2; # (픃; 픃; 픃; 픃; 픃; ) HANGUL SYLLABLE PYUH
+D504;D504;1111 1173;D504;1111 1173; # (프; 프; 프; 프; 프; ) HANGUL SYLLABLE PEU
+D505;D505;1111 1173 11A8;D505;1111 1173 11A8; # (픅; 픅; 픅; 픅; 픅; ) HANGUL SYLLABLE PEUG
+D506;D506;1111 1173 11A9;D506;1111 1173 11A9; # (픆; 픆; 픆; 픆; 픆; ) HANGUL SYLLABLE PEUGG
+D507;D507;1111 1173 11AA;D507;1111 1173 11AA; # (픇; 픇; 픇; 픇; 픇; ) HANGUL SYLLABLE PEUGS
+D508;D508;1111 1173 11AB;D508;1111 1173 11AB; # (픈; 픈; 픈; 픈; 픈; ) HANGUL SYLLABLE PEUN
+D509;D509;1111 1173 11AC;D509;1111 1173 11AC; # (픉; 픉; 픉; 픉; 픉; ) HANGUL SYLLABLE PEUNJ
+D50A;D50A;1111 1173 11AD;D50A;1111 1173 11AD; # (픊; 픊; 픊; 픊; 픊; ) HANGUL SYLLABLE PEUNH
+D50B;D50B;1111 1173 11AE;D50B;1111 1173 11AE; # (픋; 픋; 픋; 픋; 픋; ) HANGUL SYLLABLE PEUD
+D50C;D50C;1111 1173 11AF;D50C;1111 1173 11AF; # (플; 플; 플; 플; 플; ) HANGUL SYLLABLE PEUL
+D50D;D50D;1111 1173 11B0;D50D;1111 1173 11B0; # (í”; í”; 픍; í”; 픍; ) HANGUL SYLLABLE PEULG
+D50E;D50E;1111 1173 11B1;D50E;1111 1173 11B1; # (픎; 픎; 픎; 픎; 픎; ) HANGUL SYLLABLE PEULM
+D50F;D50F;1111 1173 11B2;D50F;1111 1173 11B2; # (í”; í”; 픏; í”; 픏; ) HANGUL SYLLABLE PEULB
+D510;D510;1111 1173 11B3;D510;1111 1173 11B3; # (í”; í”; 픐; í”; 픐; ) HANGUL SYLLABLE PEULS
+D511;D511;1111 1173 11B4;D511;1111 1173 11B4; # (픑; 픑; 픑; 픑; 픑; ) HANGUL SYLLABLE PEULT
+D512;D512;1111 1173 11B5;D512;1111 1173 11B5; # (픒; 픒; 픒; 픒; 픒; ) HANGUL SYLLABLE PEULP
+D513;D513;1111 1173 11B6;D513;1111 1173 11B6; # (픓; 픓; 픓; 픓; 픓; ) HANGUL SYLLABLE PEULH
+D514;D514;1111 1173 11B7;D514;1111 1173 11B7; # (픔; 픔; 픔; 픔; 픔; ) HANGUL SYLLABLE PEUM
+D515;D515;1111 1173 11B8;D515;1111 1173 11B8; # (픕; 픕; 픕; 픕; 픕; ) HANGUL SYLLABLE PEUB
+D516;D516;1111 1173 11B9;D516;1111 1173 11B9; # (픖; 픖; 픖; 픖; 픖; ) HANGUL SYLLABLE PEUBS
+D517;D517;1111 1173 11BA;D517;1111 1173 11BA; # (픗; 픗; 픗; 픗; 픗; ) HANGUL SYLLABLE PEUS
+D518;D518;1111 1173 11BB;D518;1111 1173 11BB; # (픘; 픘; 픘; 픘; 픘; ) HANGUL SYLLABLE PEUSS
+D519;D519;1111 1173 11BC;D519;1111 1173 11BC; # (픙; 픙; 픙; 픙; 픙; ) HANGUL SYLLABLE PEUNG
+D51A;D51A;1111 1173 11BD;D51A;1111 1173 11BD; # (픚; 픚; 픚; 픚; 픚; ) HANGUL SYLLABLE PEUJ
+D51B;D51B;1111 1173 11BE;D51B;1111 1173 11BE; # (픛; 픛; 픛; 픛; 픛; ) HANGUL SYLLABLE PEUC
+D51C;D51C;1111 1173 11BF;D51C;1111 1173 11BF; # (픜; 픜; 픜; 픜; 픜; ) HANGUL SYLLABLE PEUK
+D51D;D51D;1111 1173 11C0;D51D;1111 1173 11C0; # (í”; í”; 픝; í”; 픝; ) HANGUL SYLLABLE PEUT
+D51E;D51E;1111 1173 11C1;D51E;1111 1173 11C1; # (픞; 픞; á„‘á…³á‡; 픞; á„‘á…³á‡; ) HANGUL SYLLABLE PEUP
+D51F;D51F;1111 1173 11C2;D51F;1111 1173 11C2; # (픟; 픟; 픟; 픟; 픟; ) HANGUL SYLLABLE PEUH
+D520;D520;1111 1174;D520;1111 1174; # (í” ; í” ; á„‘á…´; í” ; á„‘á…´; ) HANGUL SYLLABLE PYI
+D521;D521;1111 1174 11A8;D521;1111 1174 11A8; # (픡; 픡; 픡; 픡; 픡; ) HANGUL SYLLABLE PYIG
+D522;D522;1111 1174 11A9;D522;1111 1174 11A9; # (픢; 픢; 픢; 픢; 픢; ) HANGUL SYLLABLE PYIGG
+D523;D523;1111 1174 11AA;D523;1111 1174 11AA; # (픣; 픣; 픣; 픣; 픣; ) HANGUL SYLLABLE PYIGS
+D524;D524;1111 1174 11AB;D524;1111 1174 11AB; # (픤; 픤; 픤; 픤; 픤; ) HANGUL SYLLABLE PYIN
+D525;D525;1111 1174 11AC;D525;1111 1174 11AC; # (픥; 픥; 픥; 픥; 픥; ) HANGUL SYLLABLE PYINJ
+D526;D526;1111 1174 11AD;D526;1111 1174 11AD; # (픦; 픦; 픦; 픦; 픦; ) HANGUL SYLLABLE PYINH
+D527;D527;1111 1174 11AE;D527;1111 1174 11AE; # (픧; 픧; 픧; 픧; 픧; ) HANGUL SYLLABLE PYID
+D528;D528;1111 1174 11AF;D528;1111 1174 11AF; # (픨; 픨; 픨; 픨; 픨; ) HANGUL SYLLABLE PYIL
+D529;D529;1111 1174 11B0;D529;1111 1174 11B0; # (픩; 픩; 픩; 픩; 픩; ) HANGUL SYLLABLE PYILG
+D52A;D52A;1111 1174 11B1;D52A;1111 1174 11B1; # (픪; 픪; 픪; 픪; 픪; ) HANGUL SYLLABLE PYILM
+D52B;D52B;1111 1174 11B2;D52B;1111 1174 11B2; # (픫; 픫; 픫; 픫; 픫; ) HANGUL SYLLABLE PYILB
+D52C;D52C;1111 1174 11B3;D52C;1111 1174 11B3; # (픬; 픬; 픬; 픬; 픬; ) HANGUL SYLLABLE PYILS
+D52D;D52D;1111 1174 11B4;D52D;1111 1174 11B4; # (픭; 픭; 픭; 픭; 픭; ) HANGUL SYLLABLE PYILT
+D52E;D52E;1111 1174 11B5;D52E;1111 1174 11B5; # (픮; 픮; 픮; 픮; 픮; ) HANGUL SYLLABLE PYILP
+D52F;D52F;1111 1174 11B6;D52F;1111 1174 11B6; # (픯; 픯; 픯; 픯; 픯; ) HANGUL SYLLABLE PYILH
+D530;D530;1111 1174 11B7;D530;1111 1174 11B7; # (픰; 픰; 픰; 픰; 픰; ) HANGUL SYLLABLE PYIM
+D531;D531;1111 1174 11B8;D531;1111 1174 11B8; # (픱; 픱; 픱; 픱; 픱; ) HANGUL SYLLABLE PYIB
+D532;D532;1111 1174 11B9;D532;1111 1174 11B9; # (픲; 픲; 픲; 픲; 픲; ) HANGUL SYLLABLE PYIBS
+D533;D533;1111 1174 11BA;D533;1111 1174 11BA; # (픳; 픳; 픳; 픳; 픳; ) HANGUL SYLLABLE PYIS
+D534;D534;1111 1174 11BB;D534;1111 1174 11BB; # (픴; 픴; 픴; 픴; 픴; ) HANGUL SYLLABLE PYISS
+D535;D535;1111 1174 11BC;D535;1111 1174 11BC; # (픵; 픵; 픵; 픵; 픵; ) HANGUL SYLLABLE PYING
+D536;D536;1111 1174 11BD;D536;1111 1174 11BD; # (픶; 픶; 픶; 픶; 픶; ) HANGUL SYLLABLE PYIJ
+D537;D537;1111 1174 11BE;D537;1111 1174 11BE; # (픷; 픷; 픷; 픷; 픷; ) HANGUL SYLLABLE PYIC
+D538;D538;1111 1174 11BF;D538;1111 1174 11BF; # (픸; 픸; 픸; 픸; 픸; ) HANGUL SYLLABLE PYIK
+D539;D539;1111 1174 11C0;D539;1111 1174 11C0; # (픹; 픹; 픹; 픹; 픹; ) HANGUL SYLLABLE PYIT
+D53A;D53A;1111 1174 11C1;D53A;1111 1174 11C1; # (픺; 픺; á„‘á…´á‡; 픺; á„‘á…´á‡; ) HANGUL SYLLABLE PYIP
+D53B;D53B;1111 1174 11C2;D53B;1111 1174 11C2; # (픻; 픻; 픻; 픻; 픻; ) HANGUL SYLLABLE PYIH
+D53C;D53C;1111 1175;D53C;1111 1175; # (피; 피; 피; 피; 피; ) HANGUL SYLLABLE PI
+D53D;D53D;1111 1175 11A8;D53D;1111 1175 11A8; # (픽; 픽; 픽; 픽; 픽; ) HANGUL SYLLABLE PIG
+D53E;D53E;1111 1175 11A9;D53E;1111 1175 11A9; # (픾; 픾; 픾; 픾; 픾; ) HANGUL SYLLABLE PIGG
+D53F;D53F;1111 1175 11AA;D53F;1111 1175 11AA; # (픿; 픿; 픿; 픿; 픿; ) HANGUL SYLLABLE PIGS
+D540;D540;1111 1175 11AB;D540;1111 1175 11AB; # (핀; 핀; 핀; 핀; 핀; ) HANGUL SYLLABLE PIN
+D541;D541;1111 1175 11AC;D541;1111 1175 11AC; # (í•; í•; 핁; í•; 핁; ) HANGUL SYLLABLE PINJ
+D542;D542;1111 1175 11AD;D542;1111 1175 11AD; # (핂; 핂; 핂; 핂; 핂; ) HANGUL SYLLABLE PINH
+D543;D543;1111 1175 11AE;D543;1111 1175 11AE; # (핃; 핃; 핃; 핃; 핃; ) HANGUL SYLLABLE PID
+D544;D544;1111 1175 11AF;D544;1111 1175 11AF; # (필; 필; 필; 필; 필; ) HANGUL SYLLABLE PIL
+D545;D545;1111 1175 11B0;D545;1111 1175 11B0; # (핅; 핅; 핅; 핅; 핅; ) HANGUL SYLLABLE PILG
+D546;D546;1111 1175 11B1;D546;1111 1175 11B1; # (핆; 핆; 핆; 핆; 핆; ) HANGUL SYLLABLE PILM
+D547;D547;1111 1175 11B2;D547;1111 1175 11B2; # (핇; 핇; 핇; 핇; 핇; ) HANGUL SYLLABLE PILB
+D548;D548;1111 1175 11B3;D548;1111 1175 11B3; # (핈; 핈; 핈; 핈; 핈; ) HANGUL SYLLABLE PILS
+D549;D549;1111 1175 11B4;D549;1111 1175 11B4; # (핉; 핉; 핉; 핉; 핉; ) HANGUL SYLLABLE PILT
+D54A;D54A;1111 1175 11B5;D54A;1111 1175 11B5; # (핊; 핊; 핊; 핊; 핊; ) HANGUL SYLLABLE PILP
+D54B;D54B;1111 1175 11B6;D54B;1111 1175 11B6; # (핋; 핋; 핋; 핋; 핋; ) HANGUL SYLLABLE PILH
+D54C;D54C;1111 1175 11B7;D54C;1111 1175 11B7; # (핌; 핌; 핌; 핌; 핌; ) HANGUL SYLLABLE PIM
+D54D;D54D;1111 1175 11B8;D54D;1111 1175 11B8; # (í•; í•; 핍; í•; 핍; ) HANGUL SYLLABLE PIB
+D54E;D54E;1111 1175 11B9;D54E;1111 1175 11B9; # (핎; 핎; 핎; 핎; 핎; ) HANGUL SYLLABLE PIBS
+D54F;D54F;1111 1175 11BA;D54F;1111 1175 11BA; # (í•; í•; 핏; í•; 핏; ) HANGUL SYLLABLE PIS
+D550;D550;1111 1175 11BB;D550;1111 1175 11BB; # (í•; í•; 핐; í•; 핐; ) HANGUL SYLLABLE PISS
+D551;D551;1111 1175 11BC;D551;1111 1175 11BC; # (핑; 핑; 핑; 핑; 핑; ) HANGUL SYLLABLE PING
+D552;D552;1111 1175 11BD;D552;1111 1175 11BD; # (핒; 핒; 핒; 핒; 핒; ) HANGUL SYLLABLE PIJ
+D553;D553;1111 1175 11BE;D553;1111 1175 11BE; # (핓; 핓; 핓; 핓; 핓; ) HANGUL SYLLABLE PIC
+D554;D554;1111 1175 11BF;D554;1111 1175 11BF; # (핔; 핔; 핔; 핔; 핔; ) HANGUL SYLLABLE PIK
+D555;D555;1111 1175 11C0;D555;1111 1175 11C0; # (핕; 핕; 핕; 핕; 핕; ) HANGUL SYLLABLE PIT
+D556;D556;1111 1175 11C1;D556;1111 1175 11C1; # (í•–; í•–; á„‘á…µá‡; í•–; á„‘á…µá‡; ) HANGUL SYLLABLE PIP
+D557;D557;1111 1175 11C2;D557;1111 1175 11C2; # (핗; 핗; 핗; 핗; 핗; ) HANGUL SYLLABLE PIH
+D558;D558;1112 1161;D558;1112 1161; # (하; 하; 하; 하; 하; ) HANGUL SYLLABLE HA
+D559;D559;1112 1161 11A8;D559;1112 1161 11A8; # (학; 학; 학; 학; 학; ) HANGUL SYLLABLE HAG
+D55A;D55A;1112 1161 11A9;D55A;1112 1161 11A9; # (핚; 핚; 핚; 핚; 핚; ) HANGUL SYLLABLE HAGG
+D55B;D55B;1112 1161 11AA;D55B;1112 1161 11AA; # (핛; 핛; 핛; 핛; 핛; ) HANGUL SYLLABLE HAGS
+D55C;D55C;1112 1161 11AB;D55C;1112 1161 11AB; # (한; 한; 한; 한; 한; ) HANGUL SYLLABLE HAN
+D55D;D55D;1112 1161 11AC;D55D;1112 1161 11AC; # (í•; í•; 핝; í•; 핝; ) HANGUL SYLLABLE HANJ
+D55E;D55E;1112 1161 11AD;D55E;1112 1161 11AD; # (핞; 핞; 핞; 핞; 핞; ) HANGUL SYLLABLE HANH
+D55F;D55F;1112 1161 11AE;D55F;1112 1161 11AE; # (핟; 핟; 핟; 핟; 핟; ) HANGUL SYLLABLE HAD
+D560;D560;1112 1161 11AF;D560;1112 1161 11AF; # (할; 할; 할; 할; 할; ) HANGUL SYLLABLE HAL
+D561;D561;1112 1161 11B0;D561;1112 1161 11B0; # (핡; 핡; 핡; 핡; 핡; ) HANGUL SYLLABLE HALG
+D562;D562;1112 1161 11B1;D562;1112 1161 11B1; # (핢; 핢; 핢; 핢; 핢; ) HANGUL SYLLABLE HALM
+D563;D563;1112 1161 11B2;D563;1112 1161 11B2; # (핣; 핣; 핣; 핣; 핣; ) HANGUL SYLLABLE HALB
+D564;D564;1112 1161 11B3;D564;1112 1161 11B3; # (핤; 핤; 핤; 핤; 핤; ) HANGUL SYLLABLE HALS
+D565;D565;1112 1161 11B4;D565;1112 1161 11B4; # (핥; 핥; 핥; 핥; 핥; ) HANGUL SYLLABLE HALT
+D566;D566;1112 1161 11B5;D566;1112 1161 11B5; # (핦; 핦; 핦; 핦; 핦; ) HANGUL SYLLABLE HALP
+D567;D567;1112 1161 11B6;D567;1112 1161 11B6; # (핧; 핧; 핧; 핧; 핧; ) HANGUL SYLLABLE HALH
+D568;D568;1112 1161 11B7;D568;1112 1161 11B7; # (함; 함; 함; 함; 함; ) HANGUL SYLLABLE HAM
+D569;D569;1112 1161 11B8;D569;1112 1161 11B8; # (합; 합; 합; 합; 합; ) HANGUL SYLLABLE HAB
+D56A;D56A;1112 1161 11B9;D56A;1112 1161 11B9; # (핪; 핪; 핪; 핪; 핪; ) HANGUL SYLLABLE HABS
+D56B;D56B;1112 1161 11BA;D56B;1112 1161 11BA; # (핫; 핫; 핫; 핫; 핫; ) HANGUL SYLLABLE HAS
+D56C;D56C;1112 1161 11BB;D56C;1112 1161 11BB; # (핬; 핬; 핬; 핬; 핬; ) HANGUL SYLLABLE HASS
+D56D;D56D;1112 1161 11BC;D56D;1112 1161 11BC; # (항; 항; 항; 항; 항; ) HANGUL SYLLABLE HANG
+D56E;D56E;1112 1161 11BD;D56E;1112 1161 11BD; # (핮; 핮; 핮; 핮; 핮; ) HANGUL SYLLABLE HAJ
+D56F;D56F;1112 1161 11BE;D56F;1112 1161 11BE; # (핯; 핯; 핯; 핯; 핯; ) HANGUL SYLLABLE HAC
+D570;D570;1112 1161 11BF;D570;1112 1161 11BF; # (핰; 핰; 핰; 핰; 핰; ) HANGUL SYLLABLE HAK
+D571;D571;1112 1161 11C0;D571;1112 1161 11C0; # (핱; 핱; 핱; 핱; 핱; ) HANGUL SYLLABLE HAT
+D572;D572;1112 1161 11C1;D572;1112 1161 11C1; # (핲; 핲; á„’á…¡á‡; 핲; á„’á…¡á‡; ) HANGUL SYLLABLE HAP
+D573;D573;1112 1161 11C2;D573;1112 1161 11C2; # (핳; 핳; 핳; 핳; 핳; ) HANGUL SYLLABLE HAH
+D574;D574;1112 1162;D574;1112 1162; # (í•´; í•´; á„’á…¢; í•´; á„’á…¢; ) HANGUL SYLLABLE HAE
+D575;D575;1112 1162 11A8;D575;1112 1162 11A8; # (핵; 핵; 핵; 핵; 핵; ) HANGUL SYLLABLE HAEG
+D576;D576;1112 1162 11A9;D576;1112 1162 11A9; # (핶; 핶; 핶; 핶; 핶; ) HANGUL SYLLABLE HAEGG
+D577;D577;1112 1162 11AA;D577;1112 1162 11AA; # (핷; 핷; 핷; 핷; 핷; ) HANGUL SYLLABLE HAEGS
+D578;D578;1112 1162 11AB;D578;1112 1162 11AB; # (핸; 핸; 핸; 핸; 핸; ) HANGUL SYLLABLE HAEN
+D579;D579;1112 1162 11AC;D579;1112 1162 11AC; # (핹; 핹; 핹; 핹; 핹; ) HANGUL SYLLABLE HAENJ
+D57A;D57A;1112 1162 11AD;D57A;1112 1162 11AD; # (핺; 핺; 핺; 핺; 핺; ) HANGUL SYLLABLE HAENH
+D57B;D57B;1112 1162 11AE;D57B;1112 1162 11AE; # (핻; 핻; 핻; 핻; 핻; ) HANGUL SYLLABLE HAED
+D57C;D57C;1112 1162 11AF;D57C;1112 1162 11AF; # (핼; 핼; 핼; 핼; 핼; ) HANGUL SYLLABLE HAEL
+D57D;D57D;1112 1162 11B0;D57D;1112 1162 11B0; # (핽; 핽; 핽; 핽; 핽; ) HANGUL SYLLABLE HAELG
+D57E;D57E;1112 1162 11B1;D57E;1112 1162 11B1; # (핾; 핾; 핾; 핾; 핾; ) HANGUL SYLLABLE HAELM
+D57F;D57F;1112 1162 11B2;D57F;1112 1162 11B2; # (핿; 핿; 핿; 핿; 핿; ) HANGUL SYLLABLE HAELB
+D580;D580;1112 1162 11B3;D580;1112 1162 11B3; # (햀; 햀; 햀; 햀; 햀; ) HANGUL SYLLABLE HAELS
+D581;D581;1112 1162 11B4;D581;1112 1162 11B4; # (í–; í–; 햁; í–; 햁; ) HANGUL SYLLABLE HAELT
+D582;D582;1112 1162 11B5;D582;1112 1162 11B5; # (햂; 햂; 햂; 햂; 햂; ) HANGUL SYLLABLE HAELP
+D583;D583;1112 1162 11B6;D583;1112 1162 11B6; # (햃; 햃; 햃; 햃; 햃; ) HANGUL SYLLABLE HAELH
+D584;D584;1112 1162 11B7;D584;1112 1162 11B7; # (햄; 햄; 햄; 햄; 햄; ) HANGUL SYLLABLE HAEM
+D585;D585;1112 1162 11B8;D585;1112 1162 11B8; # (햅; 햅; 햅; 햅; 햅; ) HANGUL SYLLABLE HAEB
+D586;D586;1112 1162 11B9;D586;1112 1162 11B9; # (햆; 햆; 햆; 햆; 햆; ) HANGUL SYLLABLE HAEBS
+D587;D587;1112 1162 11BA;D587;1112 1162 11BA; # (햇; 햇; 햇; 햇; 햇; ) HANGUL SYLLABLE HAES
+D588;D588;1112 1162 11BB;D588;1112 1162 11BB; # (했; 했; 했; 했; 했; ) HANGUL SYLLABLE HAESS
+D589;D589;1112 1162 11BC;D589;1112 1162 11BC; # (행; 행; 행; 행; 행; ) HANGUL SYLLABLE HAENG
+D58A;D58A;1112 1162 11BD;D58A;1112 1162 11BD; # (햊; 햊; 햊; 햊; 햊; ) HANGUL SYLLABLE HAEJ
+D58B;D58B;1112 1162 11BE;D58B;1112 1162 11BE; # (햋; 햋; 햋; 햋; 햋; ) HANGUL SYLLABLE HAEC
+D58C;D58C;1112 1162 11BF;D58C;1112 1162 11BF; # (햌; 햌; 햌; 햌; 햌; ) HANGUL SYLLABLE HAEK
+D58D;D58D;1112 1162 11C0;D58D;1112 1162 11C0; # (í–; í–; 햍; í–; 햍; ) HANGUL SYLLABLE HAET
+D58E;D58E;1112 1162 11C1;D58E;1112 1162 11C1; # (í–Ž; í–Ž; á„’á…¢á‡; í–Ž; á„’á…¢á‡; ) HANGUL SYLLABLE HAEP
+D58F;D58F;1112 1162 11C2;D58F;1112 1162 11C2; # (í–; í–; 햏; í–; 햏; ) HANGUL SYLLABLE HAEH
+D590;D590;1112 1163;D590;1112 1163; # (í–; í–; á„’á…£; í–; á„’á…£; ) HANGUL SYLLABLE HYA
+D591;D591;1112 1163 11A8;D591;1112 1163 11A8; # (햑; 햑; 햑; 햑; 햑; ) HANGUL SYLLABLE HYAG
+D592;D592;1112 1163 11A9;D592;1112 1163 11A9; # (햒; 햒; 햒; 햒; 햒; ) HANGUL SYLLABLE HYAGG
+D593;D593;1112 1163 11AA;D593;1112 1163 11AA; # (햓; 햓; 햓; 햓; 햓; ) HANGUL SYLLABLE HYAGS
+D594;D594;1112 1163 11AB;D594;1112 1163 11AB; # (햔; 햔; 햔; 햔; 햔; ) HANGUL SYLLABLE HYAN
+D595;D595;1112 1163 11AC;D595;1112 1163 11AC; # (햕; 햕; 햕; 햕; 햕; ) HANGUL SYLLABLE HYANJ
+D596;D596;1112 1163 11AD;D596;1112 1163 11AD; # (햖; 햖; 햖; 햖; 햖; ) HANGUL SYLLABLE HYANH
+D597;D597;1112 1163 11AE;D597;1112 1163 11AE; # (햗; 햗; 햗; 햗; 햗; ) HANGUL SYLLABLE HYAD
+D598;D598;1112 1163 11AF;D598;1112 1163 11AF; # (햘; 햘; 햘; 햘; 햘; ) HANGUL SYLLABLE HYAL
+D599;D599;1112 1163 11B0;D599;1112 1163 11B0; # (햙; 햙; 햙; 햙; 햙; ) HANGUL SYLLABLE HYALG
+D59A;D59A;1112 1163 11B1;D59A;1112 1163 11B1; # (햚; 햚; 햚; 햚; 햚; ) HANGUL SYLLABLE HYALM
+D59B;D59B;1112 1163 11B2;D59B;1112 1163 11B2; # (햛; 햛; 햛; 햛; 햛; ) HANGUL SYLLABLE HYALB
+D59C;D59C;1112 1163 11B3;D59C;1112 1163 11B3; # (햜; 햜; 햜; 햜; 햜; ) HANGUL SYLLABLE HYALS
+D59D;D59D;1112 1163 11B4;D59D;1112 1163 11B4; # (í–; í–; 햝; í–; 햝; ) HANGUL SYLLABLE HYALT
+D59E;D59E;1112 1163 11B5;D59E;1112 1163 11B5; # (햞; 햞; 햞; 햞; 햞; ) HANGUL SYLLABLE HYALP
+D59F;D59F;1112 1163 11B6;D59F;1112 1163 11B6; # (햟; 햟; 햟; 햟; 햟; ) HANGUL SYLLABLE HYALH
+D5A0;D5A0;1112 1163 11B7;D5A0;1112 1163 11B7; # (햠; 햠; 햠; 햠; 햠; ) HANGUL SYLLABLE HYAM
+D5A1;D5A1;1112 1163 11B8;D5A1;1112 1163 11B8; # (햡; 햡; 햡; 햡; 햡; ) HANGUL SYLLABLE HYAB
+D5A2;D5A2;1112 1163 11B9;D5A2;1112 1163 11B9; # (햢; 햢; 햢; 햢; 햢; ) HANGUL SYLLABLE HYABS
+D5A3;D5A3;1112 1163 11BA;D5A3;1112 1163 11BA; # (햣; 햣; 햣; 햣; 햣; ) HANGUL SYLLABLE HYAS
+D5A4;D5A4;1112 1163 11BB;D5A4;1112 1163 11BB; # (햤; 햤; 햤; 햤; 햤; ) HANGUL SYLLABLE HYASS
+D5A5;D5A5;1112 1163 11BC;D5A5;1112 1163 11BC; # (향; 향; 향; 향; 향; ) HANGUL SYLLABLE HYANG
+D5A6;D5A6;1112 1163 11BD;D5A6;1112 1163 11BD; # (햦; 햦; 햦; 햦; 햦; ) HANGUL SYLLABLE HYAJ
+D5A7;D5A7;1112 1163 11BE;D5A7;1112 1163 11BE; # (햧; 햧; 햧; 햧; 햧; ) HANGUL SYLLABLE HYAC
+D5A8;D5A8;1112 1163 11BF;D5A8;1112 1163 11BF; # (햨; 햨; 햨; 햨; 햨; ) HANGUL SYLLABLE HYAK
+D5A9;D5A9;1112 1163 11C0;D5A9;1112 1163 11C0; # (햩; 햩; 햩; 햩; 햩; ) HANGUL SYLLABLE HYAT
+D5AA;D5AA;1112 1163 11C1;D5AA;1112 1163 11C1; # (í–ª; í–ª; á„’á…£á‡; í–ª; á„’á…£á‡; ) HANGUL SYLLABLE HYAP
+D5AB;D5AB;1112 1163 11C2;D5AB;1112 1163 11C2; # (햫; 햫; 햫; 햫; 햫; ) HANGUL SYLLABLE HYAH
+D5AC;D5AC;1112 1164;D5AC;1112 1164; # (í–¬; í–¬; á„’á…¤; í–¬; á„’á…¤; ) HANGUL SYLLABLE HYAE
+D5AD;D5AD;1112 1164 11A8;D5AD;1112 1164 11A8; # (햭; 햭; 햭; 햭; 햭; ) HANGUL SYLLABLE HYAEG
+D5AE;D5AE;1112 1164 11A9;D5AE;1112 1164 11A9; # (햮; 햮; 햮; 햮; 햮; ) HANGUL SYLLABLE HYAEGG
+D5AF;D5AF;1112 1164 11AA;D5AF;1112 1164 11AA; # (햯; 햯; 햯; 햯; 햯; ) HANGUL SYLLABLE HYAEGS
+D5B0;D5B0;1112 1164 11AB;D5B0;1112 1164 11AB; # (햰; 햰; 햰; 햰; 햰; ) HANGUL SYLLABLE HYAEN
+D5B1;D5B1;1112 1164 11AC;D5B1;1112 1164 11AC; # (햱; 햱; 햱; 햱; 햱; ) HANGUL SYLLABLE HYAENJ
+D5B2;D5B2;1112 1164 11AD;D5B2;1112 1164 11AD; # (햲; 햲; 햲; 햲; 햲; ) HANGUL SYLLABLE HYAENH
+D5B3;D5B3;1112 1164 11AE;D5B3;1112 1164 11AE; # (햳; 햳; 햳; 햳; 햳; ) HANGUL SYLLABLE HYAED
+D5B4;D5B4;1112 1164 11AF;D5B4;1112 1164 11AF; # (햴; 햴; 햴; 햴; 햴; ) HANGUL SYLLABLE HYAEL
+D5B5;D5B5;1112 1164 11B0;D5B5;1112 1164 11B0; # (햵; 햵; 햵; 햵; 햵; ) HANGUL SYLLABLE HYAELG
+D5B6;D5B6;1112 1164 11B1;D5B6;1112 1164 11B1; # (햶; 햶; 햶; 햶; 햶; ) HANGUL SYLLABLE HYAELM
+D5B7;D5B7;1112 1164 11B2;D5B7;1112 1164 11B2; # (햷; 햷; 햷; 햷; 햷; ) HANGUL SYLLABLE HYAELB
+D5B8;D5B8;1112 1164 11B3;D5B8;1112 1164 11B3; # (햸; 햸; 햸; 햸; 햸; ) HANGUL SYLLABLE HYAELS
+D5B9;D5B9;1112 1164 11B4;D5B9;1112 1164 11B4; # (햹; 햹; 햹; 햹; 햹; ) HANGUL SYLLABLE HYAELT
+D5BA;D5BA;1112 1164 11B5;D5BA;1112 1164 11B5; # (햺; 햺; 햺; 햺; 햺; ) HANGUL SYLLABLE HYAELP
+D5BB;D5BB;1112 1164 11B6;D5BB;1112 1164 11B6; # (햻; 햻; 햻; 햻; 햻; ) HANGUL SYLLABLE HYAELH
+D5BC;D5BC;1112 1164 11B7;D5BC;1112 1164 11B7; # (햼; 햼; 햼; 햼; 햼; ) HANGUL SYLLABLE HYAEM
+D5BD;D5BD;1112 1164 11B8;D5BD;1112 1164 11B8; # (햽; 햽; 햽; 햽; 햽; ) HANGUL SYLLABLE HYAEB
+D5BE;D5BE;1112 1164 11B9;D5BE;1112 1164 11B9; # (햾; 햾; 햾; 햾; 햾; ) HANGUL SYLLABLE HYAEBS
+D5BF;D5BF;1112 1164 11BA;D5BF;1112 1164 11BA; # (햿; 햿; 햿; 햿; 햿; ) HANGUL SYLLABLE HYAES
+D5C0;D5C0;1112 1164 11BB;D5C0;1112 1164 11BB; # (헀; 헀; 헀; 헀; 헀; ) HANGUL SYLLABLE HYAESS
+D5C1;D5C1;1112 1164 11BC;D5C1;1112 1164 11BC; # (í—; í—; 헁; í—; 헁; ) HANGUL SYLLABLE HYAENG
+D5C2;D5C2;1112 1164 11BD;D5C2;1112 1164 11BD; # (헂; 헂; 헂; 헂; 헂; ) HANGUL SYLLABLE HYAEJ
+D5C3;D5C3;1112 1164 11BE;D5C3;1112 1164 11BE; # (헃; 헃; 헃; 헃; 헃; ) HANGUL SYLLABLE HYAEC
+D5C4;D5C4;1112 1164 11BF;D5C4;1112 1164 11BF; # (헄; 헄; 헄; 헄; 헄; ) HANGUL SYLLABLE HYAEK
+D5C5;D5C5;1112 1164 11C0;D5C5;1112 1164 11C0; # (헅; 헅; 헅; 헅; 헅; ) HANGUL SYLLABLE HYAET
+D5C6;D5C6;1112 1164 11C1;D5C6;1112 1164 11C1; # (í—†; í—†; á„’á…¤á‡; í—†; á„’á…¤á‡; ) HANGUL SYLLABLE HYAEP
+D5C7;D5C7;1112 1164 11C2;D5C7;1112 1164 11C2; # (헇; 헇; 헇; 헇; 헇; ) HANGUL SYLLABLE HYAEH
+D5C8;D5C8;1112 1165;D5C8;1112 1165; # (í—ˆ; í—ˆ; á„’á…¥; í—ˆ; á„’á…¥; ) HANGUL SYLLABLE HEO
+D5C9;D5C9;1112 1165 11A8;D5C9;1112 1165 11A8; # (헉; 헉; 헉; 헉; 헉; ) HANGUL SYLLABLE HEOG
+D5CA;D5CA;1112 1165 11A9;D5CA;1112 1165 11A9; # (헊; 헊; 헊; 헊; 헊; ) HANGUL SYLLABLE HEOGG
+D5CB;D5CB;1112 1165 11AA;D5CB;1112 1165 11AA; # (헋; 헋; 헋; 헋; 헋; ) HANGUL SYLLABLE HEOGS
+D5CC;D5CC;1112 1165 11AB;D5CC;1112 1165 11AB; # (헌; 헌; 헌; 헌; 헌; ) HANGUL SYLLABLE HEON
+D5CD;D5CD;1112 1165 11AC;D5CD;1112 1165 11AC; # (í—; í—; 헍; í—; 헍; ) HANGUL SYLLABLE HEONJ
+D5CE;D5CE;1112 1165 11AD;D5CE;1112 1165 11AD; # (헎; 헎; 헎; 헎; 헎; ) HANGUL SYLLABLE HEONH
+D5CF;D5CF;1112 1165 11AE;D5CF;1112 1165 11AE; # (í—; í—; 헏; í—; 헏; ) HANGUL SYLLABLE HEOD
+D5D0;D5D0;1112 1165 11AF;D5D0;1112 1165 11AF; # (í—; í—; 헐; í—; 헐; ) HANGUL SYLLABLE HEOL
+D5D1;D5D1;1112 1165 11B0;D5D1;1112 1165 11B0; # (헑; 헑; 헑; 헑; 헑; ) HANGUL SYLLABLE HEOLG
+D5D2;D5D2;1112 1165 11B1;D5D2;1112 1165 11B1; # (헒; 헒; 헒; 헒; 헒; ) HANGUL SYLLABLE HEOLM
+D5D3;D5D3;1112 1165 11B2;D5D3;1112 1165 11B2; # (헓; 헓; 헓; 헓; 헓; ) HANGUL SYLLABLE HEOLB
+D5D4;D5D4;1112 1165 11B3;D5D4;1112 1165 11B3; # (헔; 헔; 헔; 헔; 헔; ) HANGUL SYLLABLE HEOLS
+D5D5;D5D5;1112 1165 11B4;D5D5;1112 1165 11B4; # (헕; 헕; 헕; 헕; 헕; ) HANGUL SYLLABLE HEOLT
+D5D6;D5D6;1112 1165 11B5;D5D6;1112 1165 11B5; # (헖; 헖; 헖; 헖; 헖; ) HANGUL SYLLABLE HEOLP
+D5D7;D5D7;1112 1165 11B6;D5D7;1112 1165 11B6; # (헗; 헗; 헗; 헗; 헗; ) HANGUL SYLLABLE HEOLH
+D5D8;D5D8;1112 1165 11B7;D5D8;1112 1165 11B7; # (험; 험; 험; 험; 험; ) HANGUL SYLLABLE HEOM
+D5D9;D5D9;1112 1165 11B8;D5D9;1112 1165 11B8; # (헙; 헙; 헙; 헙; 헙; ) HANGUL SYLLABLE HEOB
+D5DA;D5DA;1112 1165 11B9;D5DA;1112 1165 11B9; # (헚; 헚; 헚; 헚; 헚; ) HANGUL SYLLABLE HEOBS
+D5DB;D5DB;1112 1165 11BA;D5DB;1112 1165 11BA; # (헛; 헛; 헛; 헛; 헛; ) HANGUL SYLLABLE HEOS
+D5DC;D5DC;1112 1165 11BB;D5DC;1112 1165 11BB; # (헜; 헜; 헜; 헜; 헜; ) HANGUL SYLLABLE HEOSS
+D5DD;D5DD;1112 1165 11BC;D5DD;1112 1165 11BC; # (í—; í—; 헝; í—; 헝; ) HANGUL SYLLABLE HEONG
+D5DE;D5DE;1112 1165 11BD;D5DE;1112 1165 11BD; # (헞; 헞; 헞; 헞; 헞; ) HANGUL SYLLABLE HEOJ
+D5DF;D5DF;1112 1165 11BE;D5DF;1112 1165 11BE; # (헟; 헟; 헟; 헟; 헟; ) HANGUL SYLLABLE HEOC
+D5E0;D5E0;1112 1165 11BF;D5E0;1112 1165 11BF; # (헠; 헠; 헠; 헠; 헠; ) HANGUL SYLLABLE HEOK
+D5E1;D5E1;1112 1165 11C0;D5E1;1112 1165 11C0; # (헡; 헡; 헡; 헡; 헡; ) HANGUL SYLLABLE HEOT
+D5E2;D5E2;1112 1165 11C1;D5E2;1112 1165 11C1; # (í—¢; í—¢; á„’á…¥á‡; í—¢; á„’á…¥á‡; ) HANGUL SYLLABLE HEOP
+D5E3;D5E3;1112 1165 11C2;D5E3;1112 1165 11C2; # (헣; 헣; 헣; 헣; 헣; ) HANGUL SYLLABLE HEOH
+D5E4;D5E4;1112 1166;D5E4;1112 1166; # (í—¤; í—¤; á„’á…¦; í—¤; á„’á…¦; ) HANGUL SYLLABLE HE
+D5E5;D5E5;1112 1166 11A8;D5E5;1112 1166 11A8; # (헥; 헥; 헥; 헥; 헥; ) HANGUL SYLLABLE HEG
+D5E6;D5E6;1112 1166 11A9;D5E6;1112 1166 11A9; # (헦; 헦; 헦; 헦; 헦; ) HANGUL SYLLABLE HEGG
+D5E7;D5E7;1112 1166 11AA;D5E7;1112 1166 11AA; # (헧; 헧; 헧; 헧; 헧; ) HANGUL SYLLABLE HEGS
+D5E8;D5E8;1112 1166 11AB;D5E8;1112 1166 11AB; # (헨; 헨; 헨; 헨; 헨; ) HANGUL SYLLABLE HEN
+D5E9;D5E9;1112 1166 11AC;D5E9;1112 1166 11AC; # (헩; 헩; 헩; 헩; 헩; ) HANGUL SYLLABLE HENJ
+D5EA;D5EA;1112 1166 11AD;D5EA;1112 1166 11AD; # (헪; 헪; 헪; 헪; 헪; ) HANGUL SYLLABLE HENH
+D5EB;D5EB;1112 1166 11AE;D5EB;1112 1166 11AE; # (헫; 헫; 헫; 헫; 헫; ) HANGUL SYLLABLE HED
+D5EC;D5EC;1112 1166 11AF;D5EC;1112 1166 11AF; # (헬; 헬; 헬; 헬; 헬; ) HANGUL SYLLABLE HEL
+D5ED;D5ED;1112 1166 11B0;D5ED;1112 1166 11B0; # (헭; 헭; 헭; 헭; 헭; ) HANGUL SYLLABLE HELG
+D5EE;D5EE;1112 1166 11B1;D5EE;1112 1166 11B1; # (헮; 헮; 헮; 헮; 헮; ) HANGUL SYLLABLE HELM
+D5EF;D5EF;1112 1166 11B2;D5EF;1112 1166 11B2; # (헯; 헯; 헯; 헯; 헯; ) HANGUL SYLLABLE HELB
+D5F0;D5F0;1112 1166 11B3;D5F0;1112 1166 11B3; # (헰; 헰; 헰; 헰; 헰; ) HANGUL SYLLABLE HELS
+D5F1;D5F1;1112 1166 11B4;D5F1;1112 1166 11B4; # (헱; 헱; 헱; 헱; 헱; ) HANGUL SYLLABLE HELT
+D5F2;D5F2;1112 1166 11B5;D5F2;1112 1166 11B5; # (헲; 헲; 헲; 헲; 헲; ) HANGUL SYLLABLE HELP
+D5F3;D5F3;1112 1166 11B6;D5F3;1112 1166 11B6; # (헳; 헳; 헳; 헳; 헳; ) HANGUL SYLLABLE HELH
+D5F4;D5F4;1112 1166 11B7;D5F4;1112 1166 11B7; # (헴; 헴; 헴; 헴; 헴; ) HANGUL SYLLABLE HEM
+D5F5;D5F5;1112 1166 11B8;D5F5;1112 1166 11B8; # (헵; 헵; 헵; 헵; 헵; ) HANGUL SYLLABLE HEB
+D5F6;D5F6;1112 1166 11B9;D5F6;1112 1166 11B9; # (헶; 헶; 헶; 헶; 헶; ) HANGUL SYLLABLE HEBS
+D5F7;D5F7;1112 1166 11BA;D5F7;1112 1166 11BA; # (헷; 헷; 헷; 헷; 헷; ) HANGUL SYLLABLE HES
+D5F8;D5F8;1112 1166 11BB;D5F8;1112 1166 11BB; # (헸; 헸; 헸; 헸; 헸; ) HANGUL SYLLABLE HESS
+D5F9;D5F9;1112 1166 11BC;D5F9;1112 1166 11BC; # (헹; 헹; 헹; 헹; 헹; ) HANGUL SYLLABLE HENG
+D5FA;D5FA;1112 1166 11BD;D5FA;1112 1166 11BD; # (헺; 헺; 헺; 헺; 헺; ) HANGUL SYLLABLE HEJ
+D5FB;D5FB;1112 1166 11BE;D5FB;1112 1166 11BE; # (헻; 헻; 헻; 헻; 헻; ) HANGUL SYLLABLE HEC
+D5FC;D5FC;1112 1166 11BF;D5FC;1112 1166 11BF; # (헼; 헼; 헼; 헼; 헼; ) HANGUL SYLLABLE HEK
+D5FD;D5FD;1112 1166 11C0;D5FD;1112 1166 11C0; # (헽; 헽; 헽; 헽; 헽; ) HANGUL SYLLABLE HET
+D5FE;D5FE;1112 1166 11C1;D5FE;1112 1166 11C1; # (í—¾; í—¾; á„’á…¦á‡; í—¾; á„’á…¦á‡; ) HANGUL SYLLABLE HEP
+D5FF;D5FF;1112 1166 11C2;D5FF;1112 1166 11C2; # (헿; 헿; 헿; 헿; 헿; ) HANGUL SYLLABLE HEH
+D600;D600;1112 1167;D600;1112 1167; # (혀; 혀; 혀; 혀; 혀; ) HANGUL SYLLABLE HYEO
+D601;D601;1112 1167 11A8;D601;1112 1167 11A8; # (í˜; í˜; 혁; í˜; 혁; ) HANGUL SYLLABLE HYEOG
+D602;D602;1112 1167 11A9;D602;1112 1167 11A9; # (혂; 혂; 혂; 혂; 혂; ) HANGUL SYLLABLE HYEOGG
+D603;D603;1112 1167 11AA;D603;1112 1167 11AA; # (혃; 혃; 혃; 혃; 혃; ) HANGUL SYLLABLE HYEOGS
+D604;D604;1112 1167 11AB;D604;1112 1167 11AB; # (현; 현; 현; 현; 현; ) HANGUL SYLLABLE HYEON
+D605;D605;1112 1167 11AC;D605;1112 1167 11AC; # (혅; 혅; 혅; 혅; 혅; ) HANGUL SYLLABLE HYEONJ
+D606;D606;1112 1167 11AD;D606;1112 1167 11AD; # (혆; 혆; 혆; 혆; 혆; ) HANGUL SYLLABLE HYEONH
+D607;D607;1112 1167 11AE;D607;1112 1167 11AE; # (혇; 혇; 혇; 혇; 혇; ) HANGUL SYLLABLE HYEOD
+D608;D608;1112 1167 11AF;D608;1112 1167 11AF; # (혈; 혈; 혈; 혈; 혈; ) HANGUL SYLLABLE HYEOL
+D609;D609;1112 1167 11B0;D609;1112 1167 11B0; # (혉; 혉; 혉; 혉; 혉; ) HANGUL SYLLABLE HYEOLG
+D60A;D60A;1112 1167 11B1;D60A;1112 1167 11B1; # (혊; 혊; 혊; 혊; 혊; ) HANGUL SYLLABLE HYEOLM
+D60B;D60B;1112 1167 11B2;D60B;1112 1167 11B2; # (혋; 혋; 혋; 혋; 혋; ) HANGUL SYLLABLE HYEOLB
+D60C;D60C;1112 1167 11B3;D60C;1112 1167 11B3; # (혌; 혌; 혌; 혌; 혌; ) HANGUL SYLLABLE HYEOLS
+D60D;D60D;1112 1167 11B4;D60D;1112 1167 11B4; # (í˜; í˜; 혍; í˜; 혍; ) HANGUL SYLLABLE HYEOLT
+D60E;D60E;1112 1167 11B5;D60E;1112 1167 11B5; # (혎; 혎; 혎; 혎; 혎; ) HANGUL SYLLABLE HYEOLP
+D60F;D60F;1112 1167 11B6;D60F;1112 1167 11B6; # (í˜; í˜; 혏; í˜; 혏; ) HANGUL SYLLABLE HYEOLH
+D610;D610;1112 1167 11B7;D610;1112 1167 11B7; # (í˜; í˜; 혐; í˜; 혐; ) HANGUL SYLLABLE HYEOM
+D611;D611;1112 1167 11B8;D611;1112 1167 11B8; # (협; 협; 협; 협; 협; ) HANGUL SYLLABLE HYEOB
+D612;D612;1112 1167 11B9;D612;1112 1167 11B9; # (혒; 혒; 혒; 혒; 혒; ) HANGUL SYLLABLE HYEOBS
+D613;D613;1112 1167 11BA;D613;1112 1167 11BA; # (혓; 혓; 혓; 혓; 혓; ) HANGUL SYLLABLE HYEOS
+D614;D614;1112 1167 11BB;D614;1112 1167 11BB; # (혔; 혔; 혔; 혔; 혔; ) HANGUL SYLLABLE HYEOSS
+D615;D615;1112 1167 11BC;D615;1112 1167 11BC; # (형; 형; 형; 형; 형; ) HANGUL SYLLABLE HYEONG
+D616;D616;1112 1167 11BD;D616;1112 1167 11BD; # (혖; 혖; 혖; 혖; 혖; ) HANGUL SYLLABLE HYEOJ
+D617;D617;1112 1167 11BE;D617;1112 1167 11BE; # (혗; 혗; 혗; 혗; 혗; ) HANGUL SYLLABLE HYEOC
+D618;D618;1112 1167 11BF;D618;1112 1167 11BF; # (혘; 혘; 혘; 혘; 혘; ) HANGUL SYLLABLE HYEOK
+D619;D619;1112 1167 11C0;D619;1112 1167 11C0; # (혙; 혙; 혙; 혙; 혙; ) HANGUL SYLLABLE HYEOT
+D61A;D61A;1112 1167 11C1;D61A;1112 1167 11C1; # (혚; 혚; á„’á…§á‡; 혚; á„’á…§á‡; ) HANGUL SYLLABLE HYEOP
+D61B;D61B;1112 1167 11C2;D61B;1112 1167 11C2; # (혛; 혛; 혛; 혛; 혛; ) HANGUL SYLLABLE HYEOH
+D61C;D61C;1112 1168;D61C;1112 1168; # (혜; 혜; 혜; 혜; 혜; ) HANGUL SYLLABLE HYE
+D61D;D61D;1112 1168 11A8;D61D;1112 1168 11A8; # (í˜; í˜; 혝; í˜; 혝; ) HANGUL SYLLABLE HYEG
+D61E;D61E;1112 1168 11A9;D61E;1112 1168 11A9; # (혞; 혞; 혞; 혞; 혞; ) HANGUL SYLLABLE HYEGG
+D61F;D61F;1112 1168 11AA;D61F;1112 1168 11AA; # (혟; 혟; 혟; 혟; 혟; ) HANGUL SYLLABLE HYEGS
+D620;D620;1112 1168 11AB;D620;1112 1168 11AB; # (혠; 혠; 혠; 혠; 혠; ) HANGUL SYLLABLE HYEN
+D621;D621;1112 1168 11AC;D621;1112 1168 11AC; # (혡; 혡; 혡; 혡; 혡; ) HANGUL SYLLABLE HYENJ
+D622;D622;1112 1168 11AD;D622;1112 1168 11AD; # (혢; 혢; 혢; 혢; 혢; ) HANGUL SYLLABLE HYENH
+D623;D623;1112 1168 11AE;D623;1112 1168 11AE; # (혣; 혣; 혣; 혣; 혣; ) HANGUL SYLLABLE HYED
+D624;D624;1112 1168 11AF;D624;1112 1168 11AF; # (혤; 혤; 혤; 혤; 혤; ) HANGUL SYLLABLE HYEL
+D625;D625;1112 1168 11B0;D625;1112 1168 11B0; # (혥; 혥; 혥; 혥; 혥; ) HANGUL SYLLABLE HYELG
+D626;D626;1112 1168 11B1;D626;1112 1168 11B1; # (혦; 혦; 혦; 혦; 혦; ) HANGUL SYLLABLE HYELM
+D627;D627;1112 1168 11B2;D627;1112 1168 11B2; # (혧; 혧; 혧; 혧; 혧; ) HANGUL SYLLABLE HYELB
+D628;D628;1112 1168 11B3;D628;1112 1168 11B3; # (혨; 혨; 혨; 혨; 혨; ) HANGUL SYLLABLE HYELS
+D629;D629;1112 1168 11B4;D629;1112 1168 11B4; # (혩; 혩; 혩; 혩; 혩; ) HANGUL SYLLABLE HYELT
+D62A;D62A;1112 1168 11B5;D62A;1112 1168 11B5; # (혪; 혪; 혪; 혪; 혪; ) HANGUL SYLLABLE HYELP
+D62B;D62B;1112 1168 11B6;D62B;1112 1168 11B6; # (혫; 혫; 혫; 혫; 혫; ) HANGUL SYLLABLE HYELH
+D62C;D62C;1112 1168 11B7;D62C;1112 1168 11B7; # (혬; 혬; 혬; 혬; 혬; ) HANGUL SYLLABLE HYEM
+D62D;D62D;1112 1168 11B8;D62D;1112 1168 11B8; # (혭; 혭; 혭; 혭; 혭; ) HANGUL SYLLABLE HYEB
+D62E;D62E;1112 1168 11B9;D62E;1112 1168 11B9; # (혮; 혮; 혮; 혮; 혮; ) HANGUL SYLLABLE HYEBS
+D62F;D62F;1112 1168 11BA;D62F;1112 1168 11BA; # (혯; 혯; 혯; 혯; 혯; ) HANGUL SYLLABLE HYES
+D630;D630;1112 1168 11BB;D630;1112 1168 11BB; # (혰; 혰; 혰; 혰; 혰; ) HANGUL SYLLABLE HYESS
+D631;D631;1112 1168 11BC;D631;1112 1168 11BC; # (혱; 혱; 혱; 혱; 혱; ) HANGUL SYLLABLE HYENG
+D632;D632;1112 1168 11BD;D632;1112 1168 11BD; # (혲; 혲; 혲; 혲; 혲; ) HANGUL SYLLABLE HYEJ
+D633;D633;1112 1168 11BE;D633;1112 1168 11BE; # (혳; 혳; 혳; 혳; 혳; ) HANGUL SYLLABLE HYEC
+D634;D634;1112 1168 11BF;D634;1112 1168 11BF; # (혴; 혴; 혴; 혴; 혴; ) HANGUL SYLLABLE HYEK
+D635;D635;1112 1168 11C0;D635;1112 1168 11C0; # (혵; 혵; 혵; 혵; 혵; ) HANGUL SYLLABLE HYET
+D636;D636;1112 1168 11C1;D636;1112 1168 11C1; # (혶; 혶; á„’á…¨á‡; 혶; á„’á…¨á‡; ) HANGUL SYLLABLE HYEP
+D637;D637;1112 1168 11C2;D637;1112 1168 11C2; # (혷; 혷; 혷; 혷; 혷; ) HANGUL SYLLABLE HYEH
+D638;D638;1112 1169;D638;1112 1169; # (호; 호; 호; 호; 호; ) HANGUL SYLLABLE HO
+D639;D639;1112 1169 11A8;D639;1112 1169 11A8; # (혹; 혹; 혹; 혹; 혹; ) HANGUL SYLLABLE HOG
+D63A;D63A;1112 1169 11A9;D63A;1112 1169 11A9; # (혺; 혺; 혺; 혺; 혺; ) HANGUL SYLLABLE HOGG
+D63B;D63B;1112 1169 11AA;D63B;1112 1169 11AA; # (혻; 혻; 혻; 혻; 혻; ) HANGUL SYLLABLE HOGS
+D63C;D63C;1112 1169 11AB;D63C;1112 1169 11AB; # (혼; 혼; 혼; 혼; 혼; ) HANGUL SYLLABLE HON
+D63D;D63D;1112 1169 11AC;D63D;1112 1169 11AC; # (혽; 혽; 혽; 혽; 혽; ) HANGUL SYLLABLE HONJ
+D63E;D63E;1112 1169 11AD;D63E;1112 1169 11AD; # (혾; 혾; 혾; 혾; 혾; ) HANGUL SYLLABLE HONH
+D63F;D63F;1112 1169 11AE;D63F;1112 1169 11AE; # (혿; 혿; 혿; 혿; 혿; ) HANGUL SYLLABLE HOD
+D640;D640;1112 1169 11AF;D640;1112 1169 11AF; # (홀; 홀; 홀; 홀; 홀; ) HANGUL SYLLABLE HOL
+D641;D641;1112 1169 11B0;D641;1112 1169 11B0; # (í™; í™; 홁; í™; 홁; ) HANGUL SYLLABLE HOLG
+D642;D642;1112 1169 11B1;D642;1112 1169 11B1; # (홂; 홂; 홂; 홂; 홂; ) HANGUL SYLLABLE HOLM
+D643;D643;1112 1169 11B2;D643;1112 1169 11B2; # (홃; 홃; 홃; 홃; 홃; ) HANGUL SYLLABLE HOLB
+D644;D644;1112 1169 11B3;D644;1112 1169 11B3; # (홄; 홄; 홄; 홄; 홄; ) HANGUL SYLLABLE HOLS
+D645;D645;1112 1169 11B4;D645;1112 1169 11B4; # (홅; 홅; 홅; 홅; 홅; ) HANGUL SYLLABLE HOLT
+D646;D646;1112 1169 11B5;D646;1112 1169 11B5; # (홆; 홆; 홆; 홆; 홆; ) HANGUL SYLLABLE HOLP
+D647;D647;1112 1169 11B6;D647;1112 1169 11B6; # (홇; 홇; 홇; 홇; 홇; ) HANGUL SYLLABLE HOLH
+D648;D648;1112 1169 11B7;D648;1112 1169 11B7; # (홈; 홈; 홈; 홈; 홈; ) HANGUL SYLLABLE HOM
+D649;D649;1112 1169 11B8;D649;1112 1169 11B8; # (홉; 홉; 홉; 홉; 홉; ) HANGUL SYLLABLE HOB
+D64A;D64A;1112 1169 11B9;D64A;1112 1169 11B9; # (홊; 홊; 홊; 홊; 홊; ) HANGUL SYLLABLE HOBS
+D64B;D64B;1112 1169 11BA;D64B;1112 1169 11BA; # (홋; 홋; 홋; 홋; 홋; ) HANGUL SYLLABLE HOS
+D64C;D64C;1112 1169 11BB;D64C;1112 1169 11BB; # (홌; 홌; 홌; 홌; 홌; ) HANGUL SYLLABLE HOSS
+D64D;D64D;1112 1169 11BC;D64D;1112 1169 11BC; # (í™; í™; 홍; í™; 홍; ) HANGUL SYLLABLE HONG
+D64E;D64E;1112 1169 11BD;D64E;1112 1169 11BD; # (홎; 홎; 홎; 홎; 홎; ) HANGUL SYLLABLE HOJ
+D64F;D64F;1112 1169 11BE;D64F;1112 1169 11BE; # (í™; í™; 홏; í™; 홏; ) HANGUL SYLLABLE HOC
+D650;D650;1112 1169 11BF;D650;1112 1169 11BF; # (í™; í™; 홐; í™; 홐; ) HANGUL SYLLABLE HOK
+D651;D651;1112 1169 11C0;D651;1112 1169 11C0; # (홑; 홑; 홑; 홑; 홑; ) HANGUL SYLLABLE HOT
+D652;D652;1112 1169 11C1;D652;1112 1169 11C1; # (í™’; í™’; á„’á…©á‡; í™’; á„’á…©á‡; ) HANGUL SYLLABLE HOP
+D653;D653;1112 1169 11C2;D653;1112 1169 11C2; # (홓; 홓; 홓; 홓; 홓; ) HANGUL SYLLABLE HOH
+D654;D654;1112 116A;D654;1112 116A; # (í™”; í™”; á„’á…ª; í™”; á„’á…ª; ) HANGUL SYLLABLE HWA
+D655;D655;1112 116A 11A8;D655;1112 116A 11A8; # (확; 확; 확; 확; 확; ) HANGUL SYLLABLE HWAG
+D656;D656;1112 116A 11A9;D656;1112 116A 11A9; # (홖; 홖; 홖; 홖; 홖; ) HANGUL SYLLABLE HWAGG
+D657;D657;1112 116A 11AA;D657;1112 116A 11AA; # (홗; 홗; 홗; 홗; 홗; ) HANGUL SYLLABLE HWAGS
+D658;D658;1112 116A 11AB;D658;1112 116A 11AB; # (환; 환; 환; 환; 환; ) HANGUL SYLLABLE HWAN
+D659;D659;1112 116A 11AC;D659;1112 116A 11AC; # (홙; 홙; 홙; 홙; 홙; ) HANGUL SYLLABLE HWANJ
+D65A;D65A;1112 116A 11AD;D65A;1112 116A 11AD; # (홚; 홚; 홚; 홚; 홚; ) HANGUL SYLLABLE HWANH
+D65B;D65B;1112 116A 11AE;D65B;1112 116A 11AE; # (홛; 홛; 홛; 홛; 홛; ) HANGUL SYLLABLE HWAD
+D65C;D65C;1112 116A 11AF;D65C;1112 116A 11AF; # (활; 활; 활; 활; 활; ) HANGUL SYLLABLE HWAL
+D65D;D65D;1112 116A 11B0;D65D;1112 116A 11B0; # (í™; í™; 홝; í™; 홝; ) HANGUL SYLLABLE HWALG
+D65E;D65E;1112 116A 11B1;D65E;1112 116A 11B1; # (홞; 홞; 홞; 홞; 홞; ) HANGUL SYLLABLE HWALM
+D65F;D65F;1112 116A 11B2;D65F;1112 116A 11B2; # (홟; 홟; 홟; 홟; 홟; ) HANGUL SYLLABLE HWALB
+D660;D660;1112 116A 11B3;D660;1112 116A 11B3; # (홠; 홠; 홠; 홠; 홠; ) HANGUL SYLLABLE HWALS
+D661;D661;1112 116A 11B4;D661;1112 116A 11B4; # (홡; 홡; 홡; 홡; 홡; ) HANGUL SYLLABLE HWALT
+D662;D662;1112 116A 11B5;D662;1112 116A 11B5; # (홢; 홢; 홢; 홢; 홢; ) HANGUL SYLLABLE HWALP
+D663;D663;1112 116A 11B6;D663;1112 116A 11B6; # (홣; 홣; 홣; 홣; 홣; ) HANGUL SYLLABLE HWALH
+D664;D664;1112 116A 11B7;D664;1112 116A 11B7; # (홤; 홤; 홤; 홤; 홤; ) HANGUL SYLLABLE HWAM
+D665;D665;1112 116A 11B8;D665;1112 116A 11B8; # (홥; 홥; 홥; 홥; 홥; ) HANGUL SYLLABLE HWAB
+D666;D666;1112 116A 11B9;D666;1112 116A 11B9; # (홦; 홦; 홦; 홦; 홦; ) HANGUL SYLLABLE HWABS
+D667;D667;1112 116A 11BA;D667;1112 116A 11BA; # (홧; 홧; 홧; 홧; 홧; ) HANGUL SYLLABLE HWAS
+D668;D668;1112 116A 11BB;D668;1112 116A 11BB; # (홨; 홨; 홨; 홨; 홨; ) HANGUL SYLLABLE HWASS
+D669;D669;1112 116A 11BC;D669;1112 116A 11BC; # (황; 황; 황; 황; 황; ) HANGUL SYLLABLE HWANG
+D66A;D66A;1112 116A 11BD;D66A;1112 116A 11BD; # (홪; 홪; 홪; 홪; 홪; ) HANGUL SYLLABLE HWAJ
+D66B;D66B;1112 116A 11BE;D66B;1112 116A 11BE; # (홫; 홫; 홫; 홫; 홫; ) HANGUL SYLLABLE HWAC
+D66C;D66C;1112 116A 11BF;D66C;1112 116A 11BF; # (홬; 홬; 홬; 홬; 홬; ) HANGUL SYLLABLE HWAK
+D66D;D66D;1112 116A 11C0;D66D;1112 116A 11C0; # (홭; 홭; 홭; 홭; 홭; ) HANGUL SYLLABLE HWAT
+D66E;D66E;1112 116A 11C1;D66E;1112 116A 11C1; # (í™®; í™®; á„’á…ªá‡; í™®; á„’á…ªá‡; ) HANGUL SYLLABLE HWAP
+D66F;D66F;1112 116A 11C2;D66F;1112 116A 11C2; # (홯; 홯; 홯; 홯; 홯; ) HANGUL SYLLABLE HWAH
+D670;D670;1112 116B;D670;1112 116B; # (í™°; í™°; á„’á…«; í™°; á„’á…«; ) HANGUL SYLLABLE HWAE
+D671;D671;1112 116B 11A8;D671;1112 116B 11A8; # (홱; 홱; 홱; 홱; 홱; ) HANGUL SYLLABLE HWAEG
+D672;D672;1112 116B 11A9;D672;1112 116B 11A9; # (홲; 홲; 홲; 홲; 홲; ) HANGUL SYLLABLE HWAEGG
+D673;D673;1112 116B 11AA;D673;1112 116B 11AA; # (홳; 홳; 홳; 홳; 홳; ) HANGUL SYLLABLE HWAEGS
+D674;D674;1112 116B 11AB;D674;1112 116B 11AB; # (홴; 홴; 홴; 홴; 홴; ) HANGUL SYLLABLE HWAEN
+D675;D675;1112 116B 11AC;D675;1112 116B 11AC; # (홵; 홵; 홵; 홵; 홵; ) HANGUL SYLLABLE HWAENJ
+D676;D676;1112 116B 11AD;D676;1112 116B 11AD; # (홶; 홶; 홶; 홶; 홶; ) HANGUL SYLLABLE HWAENH
+D677;D677;1112 116B 11AE;D677;1112 116B 11AE; # (홷; 홷; 홷; 홷; 홷; ) HANGUL SYLLABLE HWAED
+D678;D678;1112 116B 11AF;D678;1112 116B 11AF; # (홸; 홸; 홸; 홸; 홸; ) HANGUL SYLLABLE HWAEL
+D679;D679;1112 116B 11B0;D679;1112 116B 11B0; # (홹; 홹; 홹; 홹; 홹; ) HANGUL SYLLABLE HWAELG
+D67A;D67A;1112 116B 11B1;D67A;1112 116B 11B1; # (홺; 홺; 홺; 홺; 홺; ) HANGUL SYLLABLE HWAELM
+D67B;D67B;1112 116B 11B2;D67B;1112 116B 11B2; # (홻; 홻; 홻; 홻; 홻; ) HANGUL SYLLABLE HWAELB
+D67C;D67C;1112 116B 11B3;D67C;1112 116B 11B3; # (홼; 홼; 홼; 홼; 홼; ) HANGUL SYLLABLE HWAELS
+D67D;D67D;1112 116B 11B4;D67D;1112 116B 11B4; # (홽; 홽; 홽; 홽; 홽; ) HANGUL SYLLABLE HWAELT
+D67E;D67E;1112 116B 11B5;D67E;1112 116B 11B5; # (홾; 홾; 홾; 홾; 홾; ) HANGUL SYLLABLE HWAELP
+D67F;D67F;1112 116B 11B6;D67F;1112 116B 11B6; # (홿; 홿; 홿; 홿; 홿; ) HANGUL SYLLABLE HWAELH
+D680;D680;1112 116B 11B7;D680;1112 116B 11B7; # (횀; 횀; 횀; 횀; 횀; ) HANGUL SYLLABLE HWAEM
+D681;D681;1112 116B 11B8;D681;1112 116B 11B8; # (íš; íš; 횁; íš; 횁; ) HANGUL SYLLABLE HWAEB
+D682;D682;1112 116B 11B9;D682;1112 116B 11B9; # (횂; 횂; 횂; 횂; 횂; ) HANGUL SYLLABLE HWAEBS
+D683;D683;1112 116B 11BA;D683;1112 116B 11BA; # (횃; 횃; 횃; 횃; 횃; ) HANGUL SYLLABLE HWAES
+D684;D684;1112 116B 11BB;D684;1112 116B 11BB; # (횄; 횄; 횄; 횄; 횄; ) HANGUL SYLLABLE HWAESS
+D685;D685;1112 116B 11BC;D685;1112 116B 11BC; # (횅; 횅; 횅; 횅; 횅; ) HANGUL SYLLABLE HWAENG
+D686;D686;1112 116B 11BD;D686;1112 116B 11BD; # (횆; 횆; 횆; 횆; 횆; ) HANGUL SYLLABLE HWAEJ
+D687;D687;1112 116B 11BE;D687;1112 116B 11BE; # (횇; 횇; 횇; 횇; 횇; ) HANGUL SYLLABLE HWAEC
+D688;D688;1112 116B 11BF;D688;1112 116B 11BF; # (횈; 횈; 횈; 횈; 횈; ) HANGUL SYLLABLE HWAEK
+D689;D689;1112 116B 11C0;D689;1112 116B 11C0; # (횉; 횉; 횉; 횉; 횉; ) HANGUL SYLLABLE HWAET
+D68A;D68A;1112 116B 11C1;D68A;1112 116B 11C1; # (횊; 횊; á„’á…«á‡; 횊; á„’á…«á‡; ) HANGUL SYLLABLE HWAEP
+D68B;D68B;1112 116B 11C2;D68B;1112 116B 11C2; # (횋; 횋; 횋; 횋; 횋; ) HANGUL SYLLABLE HWAEH
+D68C;D68C;1112 116C;D68C;1112 116C; # (회; 회; 회; 회; 회; ) HANGUL SYLLABLE HOE
+D68D;D68D;1112 116C 11A8;D68D;1112 116C 11A8; # (íš; íš; 획; íš; 획; ) HANGUL SYLLABLE HOEG
+D68E;D68E;1112 116C 11A9;D68E;1112 116C 11A9; # (횎; 횎; 횎; 횎; 횎; ) HANGUL SYLLABLE HOEGG
+D68F;D68F;1112 116C 11AA;D68F;1112 116C 11AA; # (íš; íš; 횏; íš; 횏; ) HANGUL SYLLABLE HOEGS
+D690;D690;1112 116C 11AB;D690;1112 116C 11AB; # (íš; íš; 횐; íš; 횐; ) HANGUL SYLLABLE HOEN
+D691;D691;1112 116C 11AC;D691;1112 116C 11AC; # (횑; 횑; 횑; 횑; 횑; ) HANGUL SYLLABLE HOENJ
+D692;D692;1112 116C 11AD;D692;1112 116C 11AD; # (횒; 횒; 횒; 횒; 횒; ) HANGUL SYLLABLE HOENH
+D693;D693;1112 116C 11AE;D693;1112 116C 11AE; # (횓; 횓; 횓; 횓; 횓; ) HANGUL SYLLABLE HOED
+D694;D694;1112 116C 11AF;D694;1112 116C 11AF; # (횔; 횔; 횔; 횔; 횔; ) HANGUL SYLLABLE HOEL
+D695;D695;1112 116C 11B0;D695;1112 116C 11B0; # (횕; 횕; 횕; 횕; 횕; ) HANGUL SYLLABLE HOELG
+D696;D696;1112 116C 11B1;D696;1112 116C 11B1; # (횖; 횖; 횖; 횖; 횖; ) HANGUL SYLLABLE HOELM
+D697;D697;1112 116C 11B2;D697;1112 116C 11B2; # (횗; 횗; 횗; 횗; 횗; ) HANGUL SYLLABLE HOELB
+D698;D698;1112 116C 11B3;D698;1112 116C 11B3; # (횘; 횘; 횘; 횘; 횘; ) HANGUL SYLLABLE HOELS
+D699;D699;1112 116C 11B4;D699;1112 116C 11B4; # (횙; 횙; 횙; 횙; 횙; ) HANGUL SYLLABLE HOELT
+D69A;D69A;1112 116C 11B5;D69A;1112 116C 11B5; # (횚; 횚; 횚; 횚; 횚; ) HANGUL SYLLABLE HOELP
+D69B;D69B;1112 116C 11B6;D69B;1112 116C 11B6; # (횛; 횛; 횛; 횛; 횛; ) HANGUL SYLLABLE HOELH
+D69C;D69C;1112 116C 11B7;D69C;1112 116C 11B7; # (횜; 횜; 횜; 횜; 횜; ) HANGUL SYLLABLE HOEM
+D69D;D69D;1112 116C 11B8;D69D;1112 116C 11B8; # (íš; íš; 횝; íš; 횝; ) HANGUL SYLLABLE HOEB
+D69E;D69E;1112 116C 11B9;D69E;1112 116C 11B9; # (횞; 횞; 횞; 횞; 횞; ) HANGUL SYLLABLE HOEBS
+D69F;D69F;1112 116C 11BA;D69F;1112 116C 11BA; # (횟; 횟; 횟; 횟; 횟; ) HANGUL SYLLABLE HOES
+D6A0;D6A0;1112 116C 11BB;D6A0;1112 116C 11BB; # (횠; 횠; 횠; 횠; 횠; ) HANGUL SYLLABLE HOESS
+D6A1;D6A1;1112 116C 11BC;D6A1;1112 116C 11BC; # (횡; 횡; 횡; 횡; 횡; ) HANGUL SYLLABLE HOENG
+D6A2;D6A2;1112 116C 11BD;D6A2;1112 116C 11BD; # (횢; 횢; 횢; 횢; 횢; ) HANGUL SYLLABLE HOEJ
+D6A3;D6A3;1112 116C 11BE;D6A3;1112 116C 11BE; # (횣; 횣; 횣; 횣; 횣; ) HANGUL SYLLABLE HOEC
+D6A4;D6A4;1112 116C 11BF;D6A4;1112 116C 11BF; # (횤; 횤; 횤; 횤; 횤; ) HANGUL SYLLABLE HOEK
+D6A5;D6A5;1112 116C 11C0;D6A5;1112 116C 11C0; # (횥; 횥; 횥; 횥; 횥; ) HANGUL SYLLABLE HOET
+D6A6;D6A6;1112 116C 11C1;D6A6;1112 116C 11C1; # (횦; 횦; á„’á…¬á‡; 횦; á„’á…¬á‡; ) HANGUL SYLLABLE HOEP
+D6A7;D6A7;1112 116C 11C2;D6A7;1112 116C 11C2; # (횧; 횧; 횧; 횧; 횧; ) HANGUL SYLLABLE HOEH
+D6A8;D6A8;1112 116D;D6A8;1112 116D; # (효; 효; 효; 효; 효; ) HANGUL SYLLABLE HYO
+D6A9;D6A9;1112 116D 11A8;D6A9;1112 116D 11A8; # (횩; 횩; 횩; 횩; 횩; ) HANGUL SYLLABLE HYOG
+D6AA;D6AA;1112 116D 11A9;D6AA;1112 116D 11A9; # (횪; 횪; 횪; 횪; 횪; ) HANGUL SYLLABLE HYOGG
+D6AB;D6AB;1112 116D 11AA;D6AB;1112 116D 11AA; # (횫; 횫; 횫; 횫; 횫; ) HANGUL SYLLABLE HYOGS
+D6AC;D6AC;1112 116D 11AB;D6AC;1112 116D 11AB; # (횬; 횬; 횬; 횬; 횬; ) HANGUL SYLLABLE HYON
+D6AD;D6AD;1112 116D 11AC;D6AD;1112 116D 11AC; # (횭; 횭; 횭; 횭; 횭; ) HANGUL SYLLABLE HYONJ
+D6AE;D6AE;1112 116D 11AD;D6AE;1112 116D 11AD; # (횮; 횮; 횮; 횮; 횮; ) HANGUL SYLLABLE HYONH
+D6AF;D6AF;1112 116D 11AE;D6AF;1112 116D 11AE; # (횯; 횯; 횯; 횯; 횯; ) HANGUL SYLLABLE HYOD
+D6B0;D6B0;1112 116D 11AF;D6B0;1112 116D 11AF; # (횰; 횰; 횰; 횰; 횰; ) HANGUL SYLLABLE HYOL
+D6B1;D6B1;1112 116D 11B0;D6B1;1112 116D 11B0; # (횱; 횱; 횱; 횱; 횱; ) HANGUL SYLLABLE HYOLG
+D6B2;D6B2;1112 116D 11B1;D6B2;1112 116D 11B1; # (횲; 횲; 횲; 횲; 횲; ) HANGUL SYLLABLE HYOLM
+D6B3;D6B3;1112 116D 11B2;D6B3;1112 116D 11B2; # (횳; 횳; 횳; 횳; 횳; ) HANGUL SYLLABLE HYOLB
+D6B4;D6B4;1112 116D 11B3;D6B4;1112 116D 11B3; # (횴; 횴; 횴; 횴; 횴; ) HANGUL SYLLABLE HYOLS
+D6B5;D6B5;1112 116D 11B4;D6B5;1112 116D 11B4; # (횵; 횵; 횵; 횵; 횵; ) HANGUL SYLLABLE HYOLT
+D6B6;D6B6;1112 116D 11B5;D6B6;1112 116D 11B5; # (횶; 횶; 횶; 횶; 횶; ) HANGUL SYLLABLE HYOLP
+D6B7;D6B7;1112 116D 11B6;D6B7;1112 116D 11B6; # (횷; 횷; 횷; 횷; 횷; ) HANGUL SYLLABLE HYOLH
+D6B8;D6B8;1112 116D 11B7;D6B8;1112 116D 11B7; # (횸; 횸; 횸; 횸; 횸; ) HANGUL SYLLABLE HYOM
+D6B9;D6B9;1112 116D 11B8;D6B9;1112 116D 11B8; # (횹; 횹; 횹; 횹; 횹; ) HANGUL SYLLABLE HYOB
+D6BA;D6BA;1112 116D 11B9;D6BA;1112 116D 11B9; # (횺; 횺; 횺; 횺; 횺; ) HANGUL SYLLABLE HYOBS
+D6BB;D6BB;1112 116D 11BA;D6BB;1112 116D 11BA; # (횻; 횻; 횻; 횻; 횻; ) HANGUL SYLLABLE HYOS
+D6BC;D6BC;1112 116D 11BB;D6BC;1112 116D 11BB; # (횼; 횼; 횼; 횼; 횼; ) HANGUL SYLLABLE HYOSS
+D6BD;D6BD;1112 116D 11BC;D6BD;1112 116D 11BC; # (횽; 횽; 횽; 횽; 횽; ) HANGUL SYLLABLE HYONG
+D6BE;D6BE;1112 116D 11BD;D6BE;1112 116D 11BD; # (횾; 횾; 횾; 횾; 횾; ) HANGUL SYLLABLE HYOJ
+D6BF;D6BF;1112 116D 11BE;D6BF;1112 116D 11BE; # (횿; 횿; 횿; 횿; 횿; ) HANGUL SYLLABLE HYOC
+D6C0;D6C0;1112 116D 11BF;D6C0;1112 116D 11BF; # (훀; 훀; 훀; 훀; 훀; ) HANGUL SYLLABLE HYOK
+D6C1;D6C1;1112 116D 11C0;D6C1;1112 116D 11C0; # (í›; í›; 훁; í›; 훁; ) HANGUL SYLLABLE HYOT
+D6C2;D6C2;1112 116D 11C1;D6C2;1112 116D 11C1; # (훂; 훂; á„’á…­á‡; 훂; á„’á…­á‡; ) HANGUL SYLLABLE HYOP
+D6C3;D6C3;1112 116D 11C2;D6C3;1112 116D 11C2; # (훃; 훃; 훃; 훃; 훃; ) HANGUL SYLLABLE HYOH
+D6C4;D6C4;1112 116E;D6C4;1112 116E; # (후; 후; 후; 후; 후; ) HANGUL SYLLABLE HU
+D6C5;D6C5;1112 116E 11A8;D6C5;1112 116E 11A8; # (훅; 훅; 훅; 훅; 훅; ) HANGUL SYLLABLE HUG
+D6C6;D6C6;1112 116E 11A9;D6C6;1112 116E 11A9; # (훆; 훆; 훆; 훆; 훆; ) HANGUL SYLLABLE HUGG
+D6C7;D6C7;1112 116E 11AA;D6C7;1112 116E 11AA; # (훇; 훇; 훇; 훇; 훇; ) HANGUL SYLLABLE HUGS
+D6C8;D6C8;1112 116E 11AB;D6C8;1112 116E 11AB; # (훈; 훈; 훈; 훈; 훈; ) HANGUL SYLLABLE HUN
+D6C9;D6C9;1112 116E 11AC;D6C9;1112 116E 11AC; # (훉; 훉; 훉; 훉; 훉; ) HANGUL SYLLABLE HUNJ
+D6CA;D6CA;1112 116E 11AD;D6CA;1112 116E 11AD; # (훊; 훊; 훊; 훊; 훊; ) HANGUL SYLLABLE HUNH
+D6CB;D6CB;1112 116E 11AE;D6CB;1112 116E 11AE; # (훋; 훋; 훋; 훋; 훋; ) HANGUL SYLLABLE HUD
+D6CC;D6CC;1112 116E 11AF;D6CC;1112 116E 11AF; # (훌; 훌; 훌; 훌; 훌; ) HANGUL SYLLABLE HUL
+D6CD;D6CD;1112 116E 11B0;D6CD;1112 116E 11B0; # (í›; í›; 훍; í›; 훍; ) HANGUL SYLLABLE HULG
+D6CE;D6CE;1112 116E 11B1;D6CE;1112 116E 11B1; # (훎; 훎; 훎; 훎; 훎; ) HANGUL SYLLABLE HULM
+D6CF;D6CF;1112 116E 11B2;D6CF;1112 116E 11B2; # (í›; í›; 훏; í›; 훏; ) HANGUL SYLLABLE HULB
+D6D0;D6D0;1112 116E 11B3;D6D0;1112 116E 11B3; # (í›; í›; 훐; í›; 훐; ) HANGUL SYLLABLE HULS
+D6D1;D6D1;1112 116E 11B4;D6D1;1112 116E 11B4; # (훑; 훑; 훑; 훑; 훑; ) HANGUL SYLLABLE HULT
+D6D2;D6D2;1112 116E 11B5;D6D2;1112 116E 11B5; # (훒; 훒; 훒; 훒; 훒; ) HANGUL SYLLABLE HULP
+D6D3;D6D3;1112 116E 11B6;D6D3;1112 116E 11B6; # (훓; 훓; 훓; 훓; 훓; ) HANGUL SYLLABLE HULH
+D6D4;D6D4;1112 116E 11B7;D6D4;1112 116E 11B7; # (훔; 훔; 훔; 훔; 훔; ) HANGUL SYLLABLE HUM
+D6D5;D6D5;1112 116E 11B8;D6D5;1112 116E 11B8; # (훕; 훕; 훕; 훕; 훕; ) HANGUL SYLLABLE HUB
+D6D6;D6D6;1112 116E 11B9;D6D6;1112 116E 11B9; # (훖; 훖; 훖; 훖; 훖; ) HANGUL SYLLABLE HUBS
+D6D7;D6D7;1112 116E 11BA;D6D7;1112 116E 11BA; # (훗; 훗; 훗; 훗; 훗; ) HANGUL SYLLABLE HUS
+D6D8;D6D8;1112 116E 11BB;D6D8;1112 116E 11BB; # (훘; 훘; 훘; 훘; 훘; ) HANGUL SYLLABLE HUSS
+D6D9;D6D9;1112 116E 11BC;D6D9;1112 116E 11BC; # (훙; 훙; 훙; 훙; 훙; ) HANGUL SYLLABLE HUNG
+D6DA;D6DA;1112 116E 11BD;D6DA;1112 116E 11BD; # (훚; 훚; 훚; 훚; 훚; ) HANGUL SYLLABLE HUJ
+D6DB;D6DB;1112 116E 11BE;D6DB;1112 116E 11BE; # (훛; 훛; 훛; 훛; 훛; ) HANGUL SYLLABLE HUC
+D6DC;D6DC;1112 116E 11BF;D6DC;1112 116E 11BF; # (훜; 훜; 훜; 훜; 훜; ) HANGUL SYLLABLE HUK
+D6DD;D6DD;1112 116E 11C0;D6DD;1112 116E 11C0; # (í›; í›; 훝; í›; 훝; ) HANGUL SYLLABLE HUT
+D6DE;D6DE;1112 116E 11C1;D6DE;1112 116E 11C1; # (훞; 훞; á„’á…®á‡; 훞; á„’á…®á‡; ) HANGUL SYLLABLE HUP
+D6DF;D6DF;1112 116E 11C2;D6DF;1112 116E 11C2; # (훟; 훟; 훟; 훟; 훟; ) HANGUL SYLLABLE HUH
+D6E0;D6E0;1112 116F;D6E0;1112 116F; # (í› ; í› ; á„’á…¯; í› ; á„’á…¯; ) HANGUL SYLLABLE HWEO
+D6E1;D6E1;1112 116F 11A8;D6E1;1112 116F 11A8; # (훡; 훡; 훡; 훡; 훡; ) HANGUL SYLLABLE HWEOG
+D6E2;D6E2;1112 116F 11A9;D6E2;1112 116F 11A9; # (훢; 훢; 훢; 훢; 훢; ) HANGUL SYLLABLE HWEOGG
+D6E3;D6E3;1112 116F 11AA;D6E3;1112 116F 11AA; # (훣; 훣; 훣; 훣; 훣; ) HANGUL SYLLABLE HWEOGS
+D6E4;D6E4;1112 116F 11AB;D6E4;1112 116F 11AB; # (훤; 훤; 훤; 훤; 훤; ) HANGUL SYLLABLE HWEON
+D6E5;D6E5;1112 116F 11AC;D6E5;1112 116F 11AC; # (훥; 훥; 훥; 훥; 훥; ) HANGUL SYLLABLE HWEONJ
+D6E6;D6E6;1112 116F 11AD;D6E6;1112 116F 11AD; # (훦; 훦; 훦; 훦; 훦; ) HANGUL SYLLABLE HWEONH
+D6E7;D6E7;1112 116F 11AE;D6E7;1112 116F 11AE; # (훧; 훧; 훧; 훧; 훧; ) HANGUL SYLLABLE HWEOD
+D6E8;D6E8;1112 116F 11AF;D6E8;1112 116F 11AF; # (훨; 훨; 훨; 훨; 훨; ) HANGUL SYLLABLE HWEOL
+D6E9;D6E9;1112 116F 11B0;D6E9;1112 116F 11B0; # (훩; 훩; 훩; 훩; 훩; ) HANGUL SYLLABLE HWEOLG
+D6EA;D6EA;1112 116F 11B1;D6EA;1112 116F 11B1; # (훪; 훪; 훪; 훪; 훪; ) HANGUL SYLLABLE HWEOLM
+D6EB;D6EB;1112 116F 11B2;D6EB;1112 116F 11B2; # (훫; 훫; 훫; 훫; 훫; ) HANGUL SYLLABLE HWEOLB
+D6EC;D6EC;1112 116F 11B3;D6EC;1112 116F 11B3; # (훬; 훬; 훬; 훬; 훬; ) HANGUL SYLLABLE HWEOLS
+D6ED;D6ED;1112 116F 11B4;D6ED;1112 116F 11B4; # (훭; 훭; 훭; 훭; 훭; ) HANGUL SYLLABLE HWEOLT
+D6EE;D6EE;1112 116F 11B5;D6EE;1112 116F 11B5; # (훮; 훮; 훮; 훮; 훮; ) HANGUL SYLLABLE HWEOLP
+D6EF;D6EF;1112 116F 11B6;D6EF;1112 116F 11B6; # (훯; 훯; 훯; 훯; 훯; ) HANGUL SYLLABLE HWEOLH
+D6F0;D6F0;1112 116F 11B7;D6F0;1112 116F 11B7; # (훰; 훰; 훰; 훰; 훰; ) HANGUL SYLLABLE HWEOM
+D6F1;D6F1;1112 116F 11B8;D6F1;1112 116F 11B8; # (훱; 훱; 훱; 훱; 훱; ) HANGUL SYLLABLE HWEOB
+D6F2;D6F2;1112 116F 11B9;D6F2;1112 116F 11B9; # (훲; 훲; 훲; 훲; 훲; ) HANGUL SYLLABLE HWEOBS
+D6F3;D6F3;1112 116F 11BA;D6F3;1112 116F 11BA; # (훳; 훳; 훳; 훳; 훳; ) HANGUL SYLLABLE HWEOS
+D6F4;D6F4;1112 116F 11BB;D6F4;1112 116F 11BB; # (훴; 훴; 훴; 훴; 훴; ) HANGUL SYLLABLE HWEOSS
+D6F5;D6F5;1112 116F 11BC;D6F5;1112 116F 11BC; # (훵; 훵; 훵; 훵; 훵; ) HANGUL SYLLABLE HWEONG
+D6F6;D6F6;1112 116F 11BD;D6F6;1112 116F 11BD; # (훶; 훶; 훶; 훶; 훶; ) HANGUL SYLLABLE HWEOJ
+D6F7;D6F7;1112 116F 11BE;D6F7;1112 116F 11BE; # (훷; 훷; 훷; 훷; 훷; ) HANGUL SYLLABLE HWEOC
+D6F8;D6F8;1112 116F 11BF;D6F8;1112 116F 11BF; # (훸; 훸; 훸; 훸; 훸; ) HANGUL SYLLABLE HWEOK
+D6F9;D6F9;1112 116F 11C0;D6F9;1112 116F 11C0; # (훹; 훹; 훹; 훹; 훹; ) HANGUL SYLLABLE HWEOT
+D6FA;D6FA;1112 116F 11C1;D6FA;1112 116F 11C1; # (훺; 훺; á„’á…¯á‡; 훺; á„’á…¯á‡; ) HANGUL SYLLABLE HWEOP
+D6FB;D6FB;1112 116F 11C2;D6FB;1112 116F 11C2; # (훻; 훻; 훻; 훻; 훻; ) HANGUL SYLLABLE HWEOH
+D6FC;D6FC;1112 1170;D6FC;1112 1170; # (훼; 훼; 훼; 훼; 훼; ) HANGUL SYLLABLE HWE
+D6FD;D6FD;1112 1170 11A8;D6FD;1112 1170 11A8; # (훽; 훽; 훽; 훽; 훽; ) HANGUL SYLLABLE HWEG
+D6FE;D6FE;1112 1170 11A9;D6FE;1112 1170 11A9; # (훾; 훾; 훾; 훾; 훾; ) HANGUL SYLLABLE HWEGG
+D6FF;D6FF;1112 1170 11AA;D6FF;1112 1170 11AA; # (훿; 훿; 훿; 훿; 훿; ) HANGUL SYLLABLE HWEGS
+D700;D700;1112 1170 11AB;D700;1112 1170 11AB; # (휀; 휀; 휀; 휀; 휀; ) HANGUL SYLLABLE HWEN
+D701;D701;1112 1170 11AC;D701;1112 1170 11AC; # (íœ; íœ; 휁; íœ; 휁; ) HANGUL SYLLABLE HWENJ
+D702;D702;1112 1170 11AD;D702;1112 1170 11AD; # (휂; 휂; 휂; 휂; 휂; ) HANGUL SYLLABLE HWENH
+D703;D703;1112 1170 11AE;D703;1112 1170 11AE; # (휃; 휃; 휃; 휃; 휃; ) HANGUL SYLLABLE HWED
+D704;D704;1112 1170 11AF;D704;1112 1170 11AF; # (휄; 휄; 휄; 휄; 휄; ) HANGUL SYLLABLE HWEL
+D705;D705;1112 1170 11B0;D705;1112 1170 11B0; # (휅; 휅; 휅; 휅; 휅; ) HANGUL SYLLABLE HWELG
+D706;D706;1112 1170 11B1;D706;1112 1170 11B1; # (휆; 휆; 휆; 휆; 휆; ) HANGUL SYLLABLE HWELM
+D707;D707;1112 1170 11B2;D707;1112 1170 11B2; # (휇; 휇; 휇; 휇; 휇; ) HANGUL SYLLABLE HWELB
+D708;D708;1112 1170 11B3;D708;1112 1170 11B3; # (휈; 휈; 휈; 휈; 휈; ) HANGUL SYLLABLE HWELS
+D709;D709;1112 1170 11B4;D709;1112 1170 11B4; # (휉; 휉; 휉; 휉; 휉; ) HANGUL SYLLABLE HWELT
+D70A;D70A;1112 1170 11B5;D70A;1112 1170 11B5; # (휊; 휊; 휊; 휊; 휊; ) HANGUL SYLLABLE HWELP
+D70B;D70B;1112 1170 11B6;D70B;1112 1170 11B6; # (휋; 휋; 휋; 휋; 휋; ) HANGUL SYLLABLE HWELH
+D70C;D70C;1112 1170 11B7;D70C;1112 1170 11B7; # (휌; 휌; 휌; 휌; 휌; ) HANGUL SYLLABLE HWEM
+D70D;D70D;1112 1170 11B8;D70D;1112 1170 11B8; # (íœ; íœ; 휍; íœ; 휍; ) HANGUL SYLLABLE HWEB
+D70E;D70E;1112 1170 11B9;D70E;1112 1170 11B9; # (휎; 휎; 휎; 휎; 휎; ) HANGUL SYLLABLE HWEBS
+D70F;D70F;1112 1170 11BA;D70F;1112 1170 11BA; # (íœ; íœ; 휏; íœ; 휏; ) HANGUL SYLLABLE HWES
+D710;D710;1112 1170 11BB;D710;1112 1170 11BB; # (íœ; íœ; 휐; íœ; 휐; ) HANGUL SYLLABLE HWESS
+D711;D711;1112 1170 11BC;D711;1112 1170 11BC; # (휑; 휑; 휑; 휑; 휑; ) HANGUL SYLLABLE HWENG
+D712;D712;1112 1170 11BD;D712;1112 1170 11BD; # (휒; 휒; 휒; 휒; 휒; ) HANGUL SYLLABLE HWEJ
+D713;D713;1112 1170 11BE;D713;1112 1170 11BE; # (휓; 휓; 휓; 휓; 휓; ) HANGUL SYLLABLE HWEC
+D714;D714;1112 1170 11BF;D714;1112 1170 11BF; # (휔; 휔; 휔; 휔; 휔; ) HANGUL SYLLABLE HWEK
+D715;D715;1112 1170 11C0;D715;1112 1170 11C0; # (휕; 휕; 휕; 휕; 휕; ) HANGUL SYLLABLE HWET
+D716;D716;1112 1170 11C1;D716;1112 1170 11C1; # (휖; 휖; á„’á…°á‡; 휖; á„’á…°á‡; ) HANGUL SYLLABLE HWEP
+D717;D717;1112 1170 11C2;D717;1112 1170 11C2; # (휗; 휗; 휗; 휗; 휗; ) HANGUL SYLLABLE HWEH
+D718;D718;1112 1171;D718;1112 1171; # (휘; 휘; 휘; 휘; 휘; ) HANGUL SYLLABLE HWI
+D719;D719;1112 1171 11A8;D719;1112 1171 11A8; # (휙; 휙; 휙; 휙; 휙; ) HANGUL SYLLABLE HWIG
+D71A;D71A;1112 1171 11A9;D71A;1112 1171 11A9; # (휚; 휚; 휚; 휚; 휚; ) HANGUL SYLLABLE HWIGG
+D71B;D71B;1112 1171 11AA;D71B;1112 1171 11AA; # (휛; 휛; 휛; 휛; 휛; ) HANGUL SYLLABLE HWIGS
+D71C;D71C;1112 1171 11AB;D71C;1112 1171 11AB; # (휜; 휜; 휜; 휜; 휜; ) HANGUL SYLLABLE HWIN
+D71D;D71D;1112 1171 11AC;D71D;1112 1171 11AC; # (íœ; íœ; 휝; íœ; 휝; ) HANGUL SYLLABLE HWINJ
+D71E;D71E;1112 1171 11AD;D71E;1112 1171 11AD; # (휞; 휞; 휞; 휞; 휞; ) HANGUL SYLLABLE HWINH
+D71F;D71F;1112 1171 11AE;D71F;1112 1171 11AE; # (휟; 휟; 휟; 휟; 휟; ) HANGUL SYLLABLE HWID
+D720;D720;1112 1171 11AF;D720;1112 1171 11AF; # (휠; 휠; 휠; 휠; 휠; ) HANGUL SYLLABLE HWIL
+D721;D721;1112 1171 11B0;D721;1112 1171 11B0; # (휡; 휡; 휡; 휡; 휡; ) HANGUL SYLLABLE HWILG
+D722;D722;1112 1171 11B1;D722;1112 1171 11B1; # (휢; 휢; 휢; 휢; 휢; ) HANGUL SYLLABLE HWILM
+D723;D723;1112 1171 11B2;D723;1112 1171 11B2; # (휣; 휣; 휣; 휣; 휣; ) HANGUL SYLLABLE HWILB
+D724;D724;1112 1171 11B3;D724;1112 1171 11B3; # (휤; 휤; 휤; 휤; 휤; ) HANGUL SYLLABLE HWILS
+D725;D725;1112 1171 11B4;D725;1112 1171 11B4; # (휥; 휥; 휥; 휥; 휥; ) HANGUL SYLLABLE HWILT
+D726;D726;1112 1171 11B5;D726;1112 1171 11B5; # (휦; 휦; 휦; 휦; 휦; ) HANGUL SYLLABLE HWILP
+D727;D727;1112 1171 11B6;D727;1112 1171 11B6; # (휧; 휧; 휧; 휧; 휧; ) HANGUL SYLLABLE HWILH
+D728;D728;1112 1171 11B7;D728;1112 1171 11B7; # (휨; 휨; 휨; 휨; 휨; ) HANGUL SYLLABLE HWIM
+D729;D729;1112 1171 11B8;D729;1112 1171 11B8; # (휩; 휩; 휩; 휩; 휩; ) HANGUL SYLLABLE HWIB
+D72A;D72A;1112 1171 11B9;D72A;1112 1171 11B9; # (휪; 휪; 휪; 휪; 휪; ) HANGUL SYLLABLE HWIBS
+D72B;D72B;1112 1171 11BA;D72B;1112 1171 11BA; # (휫; 휫; 휫; 휫; 휫; ) HANGUL SYLLABLE HWIS
+D72C;D72C;1112 1171 11BB;D72C;1112 1171 11BB; # (휬; 휬; 휬; 휬; 휬; ) HANGUL SYLLABLE HWISS
+D72D;D72D;1112 1171 11BC;D72D;1112 1171 11BC; # (휭; 휭; 휭; 휭; 휭; ) HANGUL SYLLABLE HWING
+D72E;D72E;1112 1171 11BD;D72E;1112 1171 11BD; # (휮; 휮; 휮; 휮; 휮; ) HANGUL SYLLABLE HWIJ
+D72F;D72F;1112 1171 11BE;D72F;1112 1171 11BE; # (휯; 휯; 휯; 휯; 휯; ) HANGUL SYLLABLE HWIC
+D730;D730;1112 1171 11BF;D730;1112 1171 11BF; # (휰; 휰; 휰; 휰; 휰; ) HANGUL SYLLABLE HWIK
+D731;D731;1112 1171 11C0;D731;1112 1171 11C0; # (휱; 휱; 휱; 휱; 휱; ) HANGUL SYLLABLE HWIT
+D732;D732;1112 1171 11C1;D732;1112 1171 11C1; # (휲; 휲; á„’á…±á‡; 휲; á„’á…±á‡; ) HANGUL SYLLABLE HWIP
+D733;D733;1112 1171 11C2;D733;1112 1171 11C2; # (휳; 휳; 휳; 휳; 휳; ) HANGUL SYLLABLE HWIH
+D734;D734;1112 1172;D734;1112 1172; # (휴; 휴; 휴; 휴; 휴; ) HANGUL SYLLABLE HYU
+D735;D735;1112 1172 11A8;D735;1112 1172 11A8; # (휵; 휵; 휵; 휵; 휵; ) HANGUL SYLLABLE HYUG
+D736;D736;1112 1172 11A9;D736;1112 1172 11A9; # (휶; 휶; 휶; 휶; 휶; ) HANGUL SYLLABLE HYUGG
+D737;D737;1112 1172 11AA;D737;1112 1172 11AA; # (휷; 휷; 휷; 휷; 휷; ) HANGUL SYLLABLE HYUGS
+D738;D738;1112 1172 11AB;D738;1112 1172 11AB; # (휸; 휸; 휸; 휸; 휸; ) HANGUL SYLLABLE HYUN
+D739;D739;1112 1172 11AC;D739;1112 1172 11AC; # (휹; 휹; 휹; 휹; 휹; ) HANGUL SYLLABLE HYUNJ
+D73A;D73A;1112 1172 11AD;D73A;1112 1172 11AD; # (휺; 휺; 휺; 휺; 휺; ) HANGUL SYLLABLE HYUNH
+D73B;D73B;1112 1172 11AE;D73B;1112 1172 11AE; # (휻; 휻; 휻; 휻; 휻; ) HANGUL SYLLABLE HYUD
+D73C;D73C;1112 1172 11AF;D73C;1112 1172 11AF; # (휼; 휼; 휼; 휼; 휼; ) HANGUL SYLLABLE HYUL
+D73D;D73D;1112 1172 11B0;D73D;1112 1172 11B0; # (휽; 휽; 휽; 휽; 휽; ) HANGUL SYLLABLE HYULG
+D73E;D73E;1112 1172 11B1;D73E;1112 1172 11B1; # (휾; 휾; 휾; 휾; 휾; ) HANGUL SYLLABLE HYULM
+D73F;D73F;1112 1172 11B2;D73F;1112 1172 11B2; # (휿; 휿; 휿; 휿; 휿; ) HANGUL SYLLABLE HYULB
+D740;D740;1112 1172 11B3;D740;1112 1172 11B3; # (í€; í€; 흀; í€; 흀; ) HANGUL SYLLABLE HYULS
+D741;D741;1112 1172 11B4;D741;1112 1172 11B4; # (í; í; 흁; í; 흁; ) HANGUL SYLLABLE HYULT
+D742;D742;1112 1172 11B5;D742;1112 1172 11B5; # (í‚; í‚; 흂; í‚; 흂; ) HANGUL SYLLABLE HYULP
+D743;D743;1112 1172 11B6;D743;1112 1172 11B6; # (íƒ; íƒ; 흃; íƒ; 흃; ) HANGUL SYLLABLE HYULH
+D744;D744;1112 1172 11B7;D744;1112 1172 11B7; # (í„; í„; 흄; í„; 흄; ) HANGUL SYLLABLE HYUM
+D745;D745;1112 1172 11B8;D745;1112 1172 11B8; # (í…; í…; 흅; í…; 흅; ) HANGUL SYLLABLE HYUB
+D746;D746;1112 1172 11B9;D746;1112 1172 11B9; # (í†; í†; 흆; í†; 흆; ) HANGUL SYLLABLE HYUBS
+D747;D747;1112 1172 11BA;D747;1112 1172 11BA; # (í‡; í‡; 흇; í‡; 흇; ) HANGUL SYLLABLE HYUS
+D748;D748;1112 1172 11BB;D748;1112 1172 11BB; # (íˆ; íˆ; 흈; íˆ; 흈; ) HANGUL SYLLABLE HYUSS
+D749;D749;1112 1172 11BC;D749;1112 1172 11BC; # (í‰; í‰; 흉; í‰; 흉; ) HANGUL SYLLABLE HYUNG
+D74A;D74A;1112 1172 11BD;D74A;1112 1172 11BD; # (íŠ; íŠ; 흊; íŠ; 흊; ) HANGUL SYLLABLE HYUJ
+D74B;D74B;1112 1172 11BE;D74B;1112 1172 11BE; # (í‹; í‹; 흋; í‹; 흋; ) HANGUL SYLLABLE HYUC
+D74C;D74C;1112 1172 11BF;D74C;1112 1172 11BF; # (íŒ; íŒ; 흌; íŒ; 흌; ) HANGUL SYLLABLE HYUK
+D74D;D74D;1112 1172 11C0;D74D;1112 1172 11C0; # (í; í; 흍; í; 흍; ) HANGUL SYLLABLE HYUT
+D74E;D74E;1112 1172 11C1;D74E;1112 1172 11C1; # (íŽ; íŽ; á„’á…²á‡; íŽ; á„’á…²á‡; ) HANGUL SYLLABLE HYUP
+D74F;D74F;1112 1172 11C2;D74F;1112 1172 11C2; # (í; í; 흏; í; 흏; ) HANGUL SYLLABLE HYUH
+D750;D750;1112 1173;D750;1112 1173; # (í; í; á„’á…³; í; á„’á…³; ) HANGUL SYLLABLE HEU
+D751;D751;1112 1173 11A8;D751;1112 1173 11A8; # (í‘; í‘; 흑; í‘; 흑; ) HANGUL SYLLABLE HEUG
+D752;D752;1112 1173 11A9;D752;1112 1173 11A9; # (í’; í’; 흒; í’; 흒; ) HANGUL SYLLABLE HEUGG
+D753;D753;1112 1173 11AA;D753;1112 1173 11AA; # (í“; í“; 흓; í“; 흓; ) HANGUL SYLLABLE HEUGS
+D754;D754;1112 1173 11AB;D754;1112 1173 11AB; # (í”; í”; 흔; í”; 흔; ) HANGUL SYLLABLE HEUN
+D755;D755;1112 1173 11AC;D755;1112 1173 11AC; # (í•; í•; 흕; í•; 흕; ) HANGUL SYLLABLE HEUNJ
+D756;D756;1112 1173 11AD;D756;1112 1173 11AD; # (í–; í–; 흖; í–; 흖; ) HANGUL SYLLABLE HEUNH
+D757;D757;1112 1173 11AE;D757;1112 1173 11AE; # (í—; í—; 흗; í—; 흗; ) HANGUL SYLLABLE HEUD
+D758;D758;1112 1173 11AF;D758;1112 1173 11AF; # (í˜; í˜; 흘; í˜; 흘; ) HANGUL SYLLABLE HEUL
+D759;D759;1112 1173 11B0;D759;1112 1173 11B0; # (í™; í™; 흙; í™; 흙; ) HANGUL SYLLABLE HEULG
+D75A;D75A;1112 1173 11B1;D75A;1112 1173 11B1; # (íš; íš; 흚; íš; 흚; ) HANGUL SYLLABLE HEULM
+D75B;D75B;1112 1173 11B2;D75B;1112 1173 11B2; # (í›; í›; 흛; í›; 흛; ) HANGUL SYLLABLE HEULB
+D75C;D75C;1112 1173 11B3;D75C;1112 1173 11B3; # (íœ; íœ; 흜; íœ; 흜; ) HANGUL SYLLABLE HEULS
+D75D;D75D;1112 1173 11B4;D75D;1112 1173 11B4; # (í; í; 흝; í; 흝; ) HANGUL SYLLABLE HEULT
+D75E;D75E;1112 1173 11B5;D75E;1112 1173 11B5; # (íž; íž; 흞; íž; 흞; ) HANGUL SYLLABLE HEULP
+D75F;D75F;1112 1173 11B6;D75F;1112 1173 11B6; # (íŸ; íŸ; 흟; íŸ; 흟; ) HANGUL SYLLABLE HEULH
+D760;D760;1112 1173 11B7;D760;1112 1173 11B7; # (í ; í ; 흠; í ; 흠; ) HANGUL SYLLABLE HEUM
+D761;D761;1112 1173 11B8;D761;1112 1173 11B8; # (í¡; í¡; 흡; í¡; 흡; ) HANGUL SYLLABLE HEUB
+D762;D762;1112 1173 11B9;D762;1112 1173 11B9; # (í¢; í¢; 흢; í¢; 흢; ) HANGUL SYLLABLE HEUBS
+D763;D763;1112 1173 11BA;D763;1112 1173 11BA; # (í£; í£; 흣; í£; 흣; ) HANGUL SYLLABLE HEUS
+D764;D764;1112 1173 11BB;D764;1112 1173 11BB; # (í¤; í¤; 흤; í¤; 흤; ) HANGUL SYLLABLE HEUSS
+D765;D765;1112 1173 11BC;D765;1112 1173 11BC; # (í¥; í¥; 흥; í¥; 흥; ) HANGUL SYLLABLE HEUNG
+D766;D766;1112 1173 11BD;D766;1112 1173 11BD; # (í¦; í¦; 흦; í¦; 흦; ) HANGUL SYLLABLE HEUJ
+D767;D767;1112 1173 11BE;D767;1112 1173 11BE; # (í§; í§; 흧; í§; 흧; ) HANGUL SYLLABLE HEUC
+D768;D768;1112 1173 11BF;D768;1112 1173 11BF; # (í¨; í¨; 흨; í¨; 흨; ) HANGUL SYLLABLE HEUK
+D769;D769;1112 1173 11C0;D769;1112 1173 11C0; # (í©; í©; 흩; í©; 흩; ) HANGUL SYLLABLE HEUT
+D76A;D76A;1112 1173 11C1;D76A;1112 1173 11C1; # (íª; íª; á„’á…³á‡; íª; á„’á…³á‡; ) HANGUL SYLLABLE HEUP
+D76B;D76B;1112 1173 11C2;D76B;1112 1173 11C2; # (í«; í«; 흫; í«; 흫; ) HANGUL SYLLABLE HEUH
+D76C;D76C;1112 1174;D76C;1112 1174; # (í¬; í¬; á„’á…´; í¬; á„’á…´; ) HANGUL SYLLABLE HYI
+D76D;D76D;1112 1174 11A8;D76D;1112 1174 11A8; # (í­; í­; 흭; í­; 흭; ) HANGUL SYLLABLE HYIG
+D76E;D76E;1112 1174 11A9;D76E;1112 1174 11A9; # (í®; í®; 흮; í®; 흮; ) HANGUL SYLLABLE HYIGG
+D76F;D76F;1112 1174 11AA;D76F;1112 1174 11AA; # (í¯; í¯; 흯; í¯; 흯; ) HANGUL SYLLABLE HYIGS
+D770;D770;1112 1174 11AB;D770;1112 1174 11AB; # (í°; í°; 흰; í°; 흰; ) HANGUL SYLLABLE HYIN
+D771;D771;1112 1174 11AC;D771;1112 1174 11AC; # (í±; í±; 흱; í±; 흱; ) HANGUL SYLLABLE HYINJ
+D772;D772;1112 1174 11AD;D772;1112 1174 11AD; # (í²; í²; 흲; í²; 흲; ) HANGUL SYLLABLE HYINH
+D773;D773;1112 1174 11AE;D773;1112 1174 11AE; # (í³; í³; 흳; í³; 흳; ) HANGUL SYLLABLE HYID
+D774;D774;1112 1174 11AF;D774;1112 1174 11AF; # (í´; í´; 흴; í´; 흴; ) HANGUL SYLLABLE HYIL
+D775;D775;1112 1174 11B0;D775;1112 1174 11B0; # (íµ; íµ; 흵; íµ; 흵; ) HANGUL SYLLABLE HYILG
+D776;D776;1112 1174 11B1;D776;1112 1174 11B1; # (í¶; í¶; 흶; í¶; 흶; ) HANGUL SYLLABLE HYILM
+D777;D777;1112 1174 11B2;D777;1112 1174 11B2; # (í·; í·; 흷; í·; 흷; ) HANGUL SYLLABLE HYILB
+D778;D778;1112 1174 11B3;D778;1112 1174 11B3; # (í¸; í¸; 흸; í¸; 흸; ) HANGUL SYLLABLE HYILS
+D779;D779;1112 1174 11B4;D779;1112 1174 11B4; # (í¹; í¹; 흹; í¹; 흹; ) HANGUL SYLLABLE HYILT
+D77A;D77A;1112 1174 11B5;D77A;1112 1174 11B5; # (íº; íº; 흺; íº; 흺; ) HANGUL SYLLABLE HYILP
+D77B;D77B;1112 1174 11B6;D77B;1112 1174 11B6; # (í»; í»; 흻; í»; 흻; ) HANGUL SYLLABLE HYILH
+D77C;D77C;1112 1174 11B7;D77C;1112 1174 11B7; # (í¼; í¼; 흼; í¼; 흼; ) HANGUL SYLLABLE HYIM
+D77D;D77D;1112 1174 11B8;D77D;1112 1174 11B8; # (í½; í½; 흽; í½; 흽; ) HANGUL SYLLABLE HYIB
+D77E;D77E;1112 1174 11B9;D77E;1112 1174 11B9; # (í¾; í¾; 흾; í¾; 흾; ) HANGUL SYLLABLE HYIBS
+D77F;D77F;1112 1174 11BA;D77F;1112 1174 11BA; # (í¿; í¿; 흿; í¿; 흿; ) HANGUL SYLLABLE HYIS
+D780;D780;1112 1174 11BB;D780;1112 1174 11BB; # (힀; 힀; 힀; 힀; 힀; ) HANGUL SYLLABLE HYISS
+D781;D781;1112 1174 11BC;D781;1112 1174 11BC; # (íž; íž; 힁; íž; 힁; ) HANGUL SYLLABLE HYING
+D782;D782;1112 1174 11BD;D782;1112 1174 11BD; # (힂; 힂; 힂; 힂; 힂; ) HANGUL SYLLABLE HYIJ
+D783;D783;1112 1174 11BE;D783;1112 1174 11BE; # (힃; 힃; 힃; 힃; 힃; ) HANGUL SYLLABLE HYIC
+D784;D784;1112 1174 11BF;D784;1112 1174 11BF; # (힄; 힄; 힄; 힄; 힄; ) HANGUL SYLLABLE HYIK
+D785;D785;1112 1174 11C0;D785;1112 1174 11C0; # (힅; 힅; 힅; 힅; 힅; ) HANGUL SYLLABLE HYIT
+D786;D786;1112 1174 11C1;D786;1112 1174 11C1; # (힆; 힆; á„’á…´á‡; 힆; á„’á…´á‡; ) HANGUL SYLLABLE HYIP
+D787;D787;1112 1174 11C2;D787;1112 1174 11C2; # (힇; 힇; 힇; 힇; 힇; ) HANGUL SYLLABLE HYIH
+D788;D788;1112 1175;D788;1112 1175; # (히; 히; 히; 히; 히; ) HANGUL SYLLABLE HI
+D789;D789;1112 1175 11A8;D789;1112 1175 11A8; # (힉; 힉; 힉; 힉; 힉; ) HANGUL SYLLABLE HIG
+D78A;D78A;1112 1175 11A9;D78A;1112 1175 11A9; # (힊; 힊; 힊; 힊; 힊; ) HANGUL SYLLABLE HIGG
+D78B;D78B;1112 1175 11AA;D78B;1112 1175 11AA; # (힋; 힋; 힋; 힋; 힋; ) HANGUL SYLLABLE HIGS
+D78C;D78C;1112 1175 11AB;D78C;1112 1175 11AB; # (힌; 힌; 힌; 힌; 힌; ) HANGUL SYLLABLE HIN
+D78D;D78D;1112 1175 11AC;D78D;1112 1175 11AC; # (íž; íž; 힍; íž; 힍; ) HANGUL SYLLABLE HINJ
+D78E;D78E;1112 1175 11AD;D78E;1112 1175 11AD; # (힎; 힎; 힎; 힎; 힎; ) HANGUL SYLLABLE HINH
+D78F;D78F;1112 1175 11AE;D78F;1112 1175 11AE; # (íž; íž; 힏; íž; 힏; ) HANGUL SYLLABLE HID
+D790;D790;1112 1175 11AF;D790;1112 1175 11AF; # (íž; íž; 힐; íž; 힐; ) HANGUL SYLLABLE HIL
+D791;D791;1112 1175 11B0;D791;1112 1175 11B0; # (힑; 힑; 힑; 힑; 힑; ) HANGUL SYLLABLE HILG
+D792;D792;1112 1175 11B1;D792;1112 1175 11B1; # (힒; 힒; 힒; 힒; 힒; ) HANGUL SYLLABLE HILM
+D793;D793;1112 1175 11B2;D793;1112 1175 11B2; # (힓; 힓; 힓; 힓; 힓; ) HANGUL SYLLABLE HILB
+D794;D794;1112 1175 11B3;D794;1112 1175 11B3; # (힔; 힔; 힔; 힔; 힔; ) HANGUL SYLLABLE HILS
+D795;D795;1112 1175 11B4;D795;1112 1175 11B4; # (힕; 힕; 힕; 힕; 힕; ) HANGUL SYLLABLE HILT
+D796;D796;1112 1175 11B5;D796;1112 1175 11B5; # (힖; 힖; 힖; 힖; 힖; ) HANGUL SYLLABLE HILP
+D797;D797;1112 1175 11B6;D797;1112 1175 11B6; # (힗; 힗; 힗; 힗; 힗; ) HANGUL SYLLABLE HILH
+D798;D798;1112 1175 11B7;D798;1112 1175 11B7; # (힘; 힘; 힘; 힘; 힘; ) HANGUL SYLLABLE HIM
+D799;D799;1112 1175 11B8;D799;1112 1175 11B8; # (힙; 힙; 힙; 힙; 힙; ) HANGUL SYLLABLE HIB
+D79A;D79A;1112 1175 11B9;D79A;1112 1175 11B9; # (힚; 힚; 힚; 힚; 힚; ) HANGUL SYLLABLE HIBS
+D79B;D79B;1112 1175 11BA;D79B;1112 1175 11BA; # (힛; 힛; 힛; 힛; 힛; ) HANGUL SYLLABLE HIS
+D79C;D79C;1112 1175 11BB;D79C;1112 1175 11BB; # (힜; 힜; 힜; 힜; 힜; ) HANGUL SYLLABLE HISS
+D79D;D79D;1112 1175 11BC;D79D;1112 1175 11BC; # (íž; íž; 힝; íž; 힝; ) HANGUL SYLLABLE HING
+D79E;D79E;1112 1175 11BD;D79E;1112 1175 11BD; # (힞; 힞; 힞; 힞; 힞; ) HANGUL SYLLABLE HIJ
+D79F;D79F;1112 1175 11BE;D79F;1112 1175 11BE; # (힟; 힟; 힟; 힟; 힟; ) HANGUL SYLLABLE HIC
+D7A0;D7A0;1112 1175 11BF;D7A0;1112 1175 11BF; # (힠; 힠; 힠; 힠; 힠; ) HANGUL SYLLABLE HIK
+D7A1;D7A1;1112 1175 11C0;D7A1;1112 1175 11C0; # (힡; 힡; 힡; 힡; 힡; ) HANGUL SYLLABLE HIT
+D7A2;D7A2;1112 1175 11C1;D7A2;1112 1175 11C1; # (힢; 힢; á„’á…µá‡; 힢; á„’á…µá‡; ) HANGUL SYLLABLE HIP
+D7A3;D7A3;1112 1175 11C2;D7A3;1112 1175 11C2; # (힣; 힣; 힣; 힣; 힣; ) HANGUL SYLLABLE HIH
+F900;8C48;8C48;8C48;8C48; # (豈; 豈; 豈; 豈; 豈; ) CJK COMPATIBILITY IDEOGRAPH-F900
+F901;66F4;66F4;66F4;66F4; # (ï¤; æ›´; æ›´; æ›´; æ›´; ) CJK COMPATIBILITY IDEOGRAPH-F901
+F902;8ECA;8ECA;8ECA;8ECA; # (車; 車; 車; 車; 車; ) CJK COMPATIBILITY IDEOGRAPH-F902
+F903;8CC8;8CC8;8CC8;8CC8; # (賈; 賈; 賈; 賈; 賈; ) CJK COMPATIBILITY IDEOGRAPH-F903
+F904;6ED1;6ED1;6ED1;6ED1; # (滑; 滑; 滑; 滑; 滑; ) CJK COMPATIBILITY IDEOGRAPH-F904
+F905;4E32;4E32;4E32;4E32; # (串; 串; 串; 串; 串; ) CJK COMPATIBILITY IDEOGRAPH-F905
+F906;53E5;53E5;53E5;53E5; # (句; å¥; å¥; å¥; å¥; ) CJK COMPATIBILITY IDEOGRAPH-F906
+F907;9F9C;9F9C;9F9C;9F9C; # (龜; 龜; 龜; 龜; 龜; ) CJK COMPATIBILITY IDEOGRAPH-F907
+F908;9F9C;9F9C;9F9C;9F9C; # (龜; 龜; 龜; 龜; 龜; ) CJK COMPATIBILITY IDEOGRAPH-F908
+F909;5951;5951;5951;5951; # (契; 契; 契; 契; 契; ) CJK COMPATIBILITY IDEOGRAPH-F909
+F90A;91D1;91D1;91D1;91D1; # (金; 金; 金; 金; 金; ) CJK COMPATIBILITY IDEOGRAPH-F90A
+F90B;5587;5587;5587;5587; # (喇; 喇; 喇; 喇; 喇; ) CJK COMPATIBILITY IDEOGRAPH-F90B
+F90C;5948;5948;5948;5948; # (奈; 奈; 奈; 奈; 奈; ) CJK COMPATIBILITY IDEOGRAPH-F90C
+F90D;61F6;61F6;61F6;61F6; # (ï¤; 懶; 懶; 懶; 懶; ) CJK COMPATIBILITY IDEOGRAPH-F90D
+F90E;7669;7669;7669;7669; # (癩; 癩; 癩; 癩; 癩; ) CJK COMPATIBILITY IDEOGRAPH-F90E
+F90F;7F85;7F85;7F85;7F85; # (ï¤; ç¾…; ç¾…; ç¾…; ç¾…; ) CJK COMPATIBILITY IDEOGRAPH-F90F
+F910;863F;863F;863F;863F; # (ï¤; 蘿; 蘿; 蘿; 蘿; ) CJK COMPATIBILITY IDEOGRAPH-F910
+F911;87BA;87BA;87BA;87BA; # (螺; 螺; 螺; 螺; 螺; ) CJK COMPATIBILITY IDEOGRAPH-F911
+F912;88F8;88F8;88F8;88F8; # (裸; 裸; 裸; 裸; 裸; ) CJK COMPATIBILITY IDEOGRAPH-F912
+F913;908F;908F;908F;908F; # (邏; é‚; é‚; é‚; é‚; ) CJK COMPATIBILITY IDEOGRAPH-F913
+F914;6A02;6A02;6A02;6A02; # (樂; 樂; 樂; 樂; 樂; ) CJK COMPATIBILITY IDEOGRAPH-F914
+F915;6D1B;6D1B;6D1B;6D1B; # (洛; 洛; 洛; 洛; 洛; ) CJK COMPATIBILITY IDEOGRAPH-F915
+F916;70D9;70D9;70D9;70D9; # (烙; 烙; 烙; 烙; 烙; ) CJK COMPATIBILITY IDEOGRAPH-F916
+F917;73DE;73DE;73DE;73DE; # (珞; çž; çž; çž; çž; ) CJK COMPATIBILITY IDEOGRAPH-F917
+F918;843D;843D;843D;843D; # (落; è½; è½; è½; è½; ) CJK COMPATIBILITY IDEOGRAPH-F918
+F919;916A;916A;916A;916A; # (酪; 酪; 酪; 酪; 酪; ) CJK COMPATIBILITY IDEOGRAPH-F919
+F91A;99F1;99F1;99F1;99F1; # (駱; 駱; 駱; 駱; 駱; ) CJK COMPATIBILITY IDEOGRAPH-F91A
+F91B;4E82;4E82;4E82;4E82; # (亂; 亂; 亂; 亂; 亂; ) CJK COMPATIBILITY IDEOGRAPH-F91B
+F91C;5375;5375;5375;5375; # (卵; åµ; åµ; åµ; åµ; ) CJK COMPATIBILITY IDEOGRAPH-F91C
+F91D;6B04;6B04;6B04;6B04; # (ï¤; 欄; 欄; 欄; 欄; ) CJK COMPATIBILITY IDEOGRAPH-F91D
+F91E;721B;721B;721B;721B; # (爛; 爛; 爛; 爛; 爛; ) CJK COMPATIBILITY IDEOGRAPH-F91E
+F91F;862D;862D;862D;862D; # (蘭; 蘭; 蘭; 蘭; 蘭; ) CJK COMPATIBILITY IDEOGRAPH-F91F
+F920;9E1E;9E1E;9E1E;9E1E; # (鸞; 鸞; 鸞; 鸞; 鸞; ) CJK COMPATIBILITY IDEOGRAPH-F920
+F921;5D50;5D50;5D50;5D50; # (嵐; åµ; åµ; åµ; åµ; ) CJK COMPATIBILITY IDEOGRAPH-F921
+F922;6FEB;6FEB;6FEB;6FEB; # (濫; 濫; 濫; 濫; 濫; ) CJK COMPATIBILITY IDEOGRAPH-F922
+F923;85CD;85CD;85CD;85CD; # (藍; è—; è—; è—; è—; ) CJK COMPATIBILITY IDEOGRAPH-F923
+F924;8964;8964;8964;8964; # (襤; 襤; 襤; 襤; 襤; ) CJK COMPATIBILITY IDEOGRAPH-F924
+F925;62C9;62C9;62C9;62C9; # (拉; 拉; 拉; 拉; 拉; ) CJK COMPATIBILITY IDEOGRAPH-F925
+F926;81D8;81D8;81D8;81D8; # (臘; 臘; 臘; 臘; 臘; ) CJK COMPATIBILITY IDEOGRAPH-F926
+F927;881F;881F;881F;881F; # (蠟; 蠟; 蠟; 蠟; 蠟; ) CJK COMPATIBILITY IDEOGRAPH-F927
+F928;5ECA;5ECA;5ECA;5ECA; # (廊; 廊; 廊; 廊; 廊; ) CJK COMPATIBILITY IDEOGRAPH-F928
+F929;6717;6717;6717;6717; # (朗; 朗; 朗; 朗; 朗; ) CJK COMPATIBILITY IDEOGRAPH-F929
+F92A;6D6A;6D6A;6D6A;6D6A; # (浪; 浪; 浪; 浪; 浪; ) CJK COMPATIBILITY IDEOGRAPH-F92A
+F92B;72FC;72FC;72FC;72FC; # (狼; 狼; 狼; 狼; 狼; ) CJK COMPATIBILITY IDEOGRAPH-F92B
+F92C;90CE;90CE;90CE;90CE; # (郎; 郎; 郎; 郎; 郎; ) CJK COMPATIBILITY IDEOGRAPH-F92C
+F92D;4F86;4F86;4F86;4F86; # (來; 來; 來; 來; 來; ) CJK COMPATIBILITY IDEOGRAPH-F92D
+F92E;51B7;51B7;51B7;51B7; # (冷; 冷; 冷; 冷; 冷; ) CJK COMPATIBILITY IDEOGRAPH-F92E
+F92F;52DE;52DE;52DE;52DE; # (勞; 勞; 勞; 勞; 勞; ) CJK COMPATIBILITY IDEOGRAPH-F92F
+F930;64C4;64C4;64C4;64C4; # (擄; 擄; 擄; 擄; 擄; ) CJK COMPATIBILITY IDEOGRAPH-F930
+F931;6AD3;6AD3;6AD3;6AD3; # (櫓; 櫓; 櫓; 櫓; 櫓; ) CJK COMPATIBILITY IDEOGRAPH-F931
+F932;7210;7210;7210;7210; # (爐; çˆ; çˆ; çˆ; çˆ; ) CJK COMPATIBILITY IDEOGRAPH-F932
+F933;76E7;76E7;76E7;76E7; # (盧; 盧; 盧; 盧; 盧; ) CJK COMPATIBILITY IDEOGRAPH-F933
+F934;8001;8001;8001;8001; # (老; è€; è€; è€; è€; ) CJK COMPATIBILITY IDEOGRAPH-F934
+F935;8606;8606;8606;8606; # (蘆; 蘆; 蘆; 蘆; 蘆; ) CJK COMPATIBILITY IDEOGRAPH-F935
+F936;865C;865C;865C;865C; # (虜; 虜; 虜; 虜; 虜; ) CJK COMPATIBILITY IDEOGRAPH-F936
+F937;8DEF;8DEF;8DEF;8DEF; # (路; 路; 路; 路; 路; ) CJK COMPATIBILITY IDEOGRAPH-F937
+F938;9732;9732;9732;9732; # (露; 露; 露; 露; 露; ) CJK COMPATIBILITY IDEOGRAPH-F938
+F939;9B6F;9B6F;9B6F;9B6F; # (魯; 魯; 魯; 魯; 魯; ) CJK COMPATIBILITY IDEOGRAPH-F939
+F93A;9DFA;9DFA;9DFA;9DFA; # (鷺; 鷺; 鷺; 鷺; 鷺; ) CJK COMPATIBILITY IDEOGRAPH-F93A
+F93B;788C;788C;788C;788C; # (碌; 碌; 碌; 碌; 碌; ) CJK COMPATIBILITY IDEOGRAPH-F93B
+F93C;797F;797F;797F;797F; # (祿; 祿; 祿; 祿; 祿; ) CJK COMPATIBILITY IDEOGRAPH-F93C
+F93D;7DA0;7DA0;7DA0;7DA0; # (綠; 綠; 綠; 綠; 綠; ) CJK COMPATIBILITY IDEOGRAPH-F93D
+F93E;83C9;83C9;83C9;83C9; # (菉; è‰; è‰; è‰; è‰; ) CJK COMPATIBILITY IDEOGRAPH-F93E
+F93F;9304;9304;9304;9304; # (錄; 錄; 錄; 錄; 錄; ) CJK COMPATIBILITY IDEOGRAPH-F93F
+F940;9E7F;9E7F;9E7F;9E7F; # (鹿; 鹿; 鹿; 鹿; 鹿; ) CJK COMPATIBILITY IDEOGRAPH-F940
+F941;8AD6;8AD6;8AD6;8AD6; # (ï¥; è«–; è«–; è«–; è«–; ) CJK COMPATIBILITY IDEOGRAPH-F941
+F942;58DF;58DF;58DF;58DF; # (壟; 壟; 壟; 壟; 壟; ) CJK COMPATIBILITY IDEOGRAPH-F942
+F943;5F04;5F04;5F04;5F04; # (弄; 弄; 弄; 弄; 弄; ) CJK COMPATIBILITY IDEOGRAPH-F943
+F944;7C60;7C60;7C60;7C60; # (籠; 籠; 籠; 籠; 籠; ) CJK COMPATIBILITY IDEOGRAPH-F944
+F945;807E;807E;807E;807E; # (聾; è¾; è¾; è¾; è¾; ) CJK COMPATIBILITY IDEOGRAPH-F945
+F946;7262;7262;7262;7262; # (牢; 牢; 牢; 牢; 牢; ) CJK COMPATIBILITY IDEOGRAPH-F946
+F947;78CA;78CA;78CA;78CA; # (磊; 磊; 磊; 磊; 磊; ) CJK COMPATIBILITY IDEOGRAPH-F947
+F948;8CC2;8CC2;8CC2;8CC2; # (賂; 賂; 賂; 賂; 賂; ) CJK COMPATIBILITY IDEOGRAPH-F948
+F949;96F7;96F7;96F7;96F7; # (雷; 雷; 雷; 雷; 雷; ) CJK COMPATIBILITY IDEOGRAPH-F949
+F94A;58D8;58D8;58D8;58D8; # (壘; 壘; 壘; 壘; 壘; ) CJK COMPATIBILITY IDEOGRAPH-F94A
+F94B;5C62;5C62;5C62;5C62; # (屢; 屢; 屢; 屢; 屢; ) CJK COMPATIBILITY IDEOGRAPH-F94B
+F94C;6A13;6A13;6A13;6A13; # (樓; 樓; 樓; 樓; 樓; ) CJK COMPATIBILITY IDEOGRAPH-F94C
+F94D;6DDA;6DDA;6DDA;6DDA; # (ï¥; æ·š; æ·š; æ·š; æ·š; ) CJK COMPATIBILITY IDEOGRAPH-F94D
+F94E;6F0F;6F0F;6F0F;6F0F; # (漏; æ¼; æ¼; æ¼; æ¼; ) CJK COMPATIBILITY IDEOGRAPH-F94E
+F94F;7D2F;7D2F;7D2F;7D2F; # (ï¥; ç´¯; ç´¯; ç´¯; ç´¯; ) CJK COMPATIBILITY IDEOGRAPH-F94F
+F950;7E37;7E37;7E37;7E37; # (ï¥; 縷; 縷; 縷; 縷; ) CJK COMPATIBILITY IDEOGRAPH-F950
+F951;964B;964B;964B;964B; # (陋; 陋; 陋; 陋; 陋; ) CJK COMPATIBILITY IDEOGRAPH-F951
+F952;52D2;52D2;52D2;52D2; # (勒; 勒; 勒; 勒; 勒; ) CJK COMPATIBILITY IDEOGRAPH-F952
+F953;808B;808B;808B;808B; # (肋; 肋; 肋; 肋; 肋; ) CJK COMPATIBILITY IDEOGRAPH-F953
+F954;51DC;51DC;51DC;51DC; # (凜; 凜; 凜; 凜; 凜; ) CJK COMPATIBILITY IDEOGRAPH-F954
+F955;51CC;51CC;51CC;51CC; # (凌; 凌; 凌; 凌; 凌; ) CJK COMPATIBILITY IDEOGRAPH-F955
+F956;7A1C;7A1C;7A1C;7A1C; # (稜; 稜; 稜; 稜; 稜; ) CJK COMPATIBILITY IDEOGRAPH-F956
+F957;7DBE;7DBE;7DBE;7DBE; # (綾; 綾; 綾; 綾; 綾; ) CJK COMPATIBILITY IDEOGRAPH-F957
+F958;83F1;83F1;83F1;83F1; # (菱; è±; è±; è±; è±; ) CJK COMPATIBILITY IDEOGRAPH-F958
+F959;9675;9675;9675;9675; # (陵; 陵; 陵; 陵; 陵; ) CJK COMPATIBILITY IDEOGRAPH-F959
+F95A;8B80;8B80;8B80;8B80; # (讀; 讀; 讀; 讀; 讀; ) CJK COMPATIBILITY IDEOGRAPH-F95A
+F95B;62CF;62CF;62CF;62CF; # (拏; æ‹; æ‹; æ‹; æ‹; ) CJK COMPATIBILITY IDEOGRAPH-F95B
+F95C;6A02;6A02;6A02;6A02; # (樂; 樂; 樂; 樂; 樂; ) CJK COMPATIBILITY IDEOGRAPH-F95C
+F95D;8AFE;8AFE;8AFE;8AFE; # (ï¥; 諾; 諾; 諾; 諾; ) CJK COMPATIBILITY IDEOGRAPH-F95D
+F95E;4E39;4E39;4E39;4E39; # (丹; 丹; 丹; 丹; 丹; ) CJK COMPATIBILITY IDEOGRAPH-F95E
+F95F;5BE7;5BE7;5BE7;5BE7; # (寧; 寧; 寧; 寧; 寧; ) CJK COMPATIBILITY IDEOGRAPH-F95F
+F960;6012;6012;6012;6012; # (怒; 怒; 怒; 怒; 怒; ) CJK COMPATIBILITY IDEOGRAPH-F960
+F961;7387;7387;7387;7387; # (率; 率; 率; 率; 率; ) CJK COMPATIBILITY IDEOGRAPH-F961
+F962;7570;7570;7570;7570; # (異; 異; 異; 異; 異; ) CJK COMPATIBILITY IDEOGRAPH-F962
+F963;5317;5317;5317;5317; # (北; 北; 北; 北; 北; ) CJK COMPATIBILITY IDEOGRAPH-F963
+F964;78FB;78FB;78FB;78FB; # (磻; 磻; 磻; 磻; 磻; ) CJK COMPATIBILITY IDEOGRAPH-F964
+F965;4FBF;4FBF;4FBF;4FBF; # (便; 便; 便; 便; 便; ) CJK COMPATIBILITY IDEOGRAPH-F965
+F966;5FA9;5FA9;5FA9;5FA9; # (復; 復; 復; 復; 復; ) CJK COMPATIBILITY IDEOGRAPH-F966
+F967;4E0D;4E0D;4E0D;4E0D; # (不; ä¸; ä¸; ä¸; ä¸; ) CJK COMPATIBILITY IDEOGRAPH-F967
+F968;6CCC;6CCC;6CCC;6CCC; # (泌; 泌; 泌; 泌; 泌; ) CJK COMPATIBILITY IDEOGRAPH-F968
+F969;6578;6578;6578;6578; # (數; 數; 數; 數; 數; ) CJK COMPATIBILITY IDEOGRAPH-F969
+F96A;7D22;7D22;7D22;7D22; # (索; 索; 索; 索; 索; ) CJK COMPATIBILITY IDEOGRAPH-F96A
+F96B;53C3;53C3;53C3;53C3; # (參; åƒ; åƒ; åƒ; åƒ; ) CJK COMPATIBILITY IDEOGRAPH-F96B
+F96C;585E;585E;585E;585E; # (塞; 塞; 塞; 塞; 塞; ) CJK COMPATIBILITY IDEOGRAPH-F96C
+F96D;7701;7701;7701;7701; # (省; çœ; çœ; çœ; çœ; ) CJK COMPATIBILITY IDEOGRAPH-F96D
+F96E;8449;8449;8449;8449; # (葉; 葉; 葉; 葉; 葉; ) CJK COMPATIBILITY IDEOGRAPH-F96E
+F96F;8AAA;8AAA;8AAA;8AAA; # (說; 說; 說; 說; 說; ) CJK COMPATIBILITY IDEOGRAPH-F96F
+F970;6BBA;6BBA;6BBA;6BBA; # (殺; 殺; 殺; 殺; 殺; ) CJK COMPATIBILITY IDEOGRAPH-F970
+F971;8FB0;8FB0;8FB0;8FB0; # (辰; 辰; 辰; 辰; 辰; ) CJK COMPATIBILITY IDEOGRAPH-F971
+F972;6C88;6C88;6C88;6C88; # (沈; 沈; 沈; 沈; 沈; ) CJK COMPATIBILITY IDEOGRAPH-F972
+F973;62FE;62FE;62FE;62FE; # (拾; 拾; 拾; 拾; 拾; ) CJK COMPATIBILITY IDEOGRAPH-F973
+F974;82E5;82E5;82E5;82E5; # (若; 若; 若; 若; 若; ) CJK COMPATIBILITY IDEOGRAPH-F974
+F975;63A0;63A0;63A0;63A0; # (掠; 掠; 掠; 掠; 掠; ) CJK COMPATIBILITY IDEOGRAPH-F975
+F976;7565;7565;7565;7565; # (略; 略; 略; 略; 略; ) CJK COMPATIBILITY IDEOGRAPH-F976
+F977;4EAE;4EAE;4EAE;4EAE; # (亮; 亮; 亮; 亮; 亮; ) CJK COMPATIBILITY IDEOGRAPH-F977
+F978;5169;5169;5169;5169; # (兩; 兩; 兩; 兩; 兩; ) CJK COMPATIBILITY IDEOGRAPH-F978
+F979;51C9;51C9;51C9;51C9; # (凉; 凉; 凉; 凉; 凉; ) CJK COMPATIBILITY IDEOGRAPH-F979
+F97A;6881;6881;6881;6881; # (梁; æ¢; æ¢; æ¢; æ¢; ) CJK COMPATIBILITY IDEOGRAPH-F97A
+F97B;7CE7;7CE7;7CE7;7CE7; # (糧; 糧; 糧; 糧; 糧; ) CJK COMPATIBILITY IDEOGRAPH-F97B
+F97C;826F;826F;826F;826F; # (良; 良; 良; 良; 良; ) CJK COMPATIBILITY IDEOGRAPH-F97C
+F97D;8AD2;8AD2;8AD2;8AD2; # (諒; 諒; 諒; 諒; 諒; ) CJK COMPATIBILITY IDEOGRAPH-F97D
+F97E;91CF;91CF;91CF;91CF; # (量; é‡; é‡; é‡; é‡; ) CJK COMPATIBILITY IDEOGRAPH-F97E
+F97F;52F5;52F5;52F5;52F5; # (勵; 勵; 勵; 勵; 勵; ) CJK COMPATIBILITY IDEOGRAPH-F97F
+F980;5442;5442;5442;5442; # (呂; 呂; 呂; 呂; 呂; ) CJK COMPATIBILITY IDEOGRAPH-F980
+F981;5973;5973;5973;5973; # (ï¦; 女; 女; 女; 女; ) CJK COMPATIBILITY IDEOGRAPH-F981
+F982;5EEC;5EEC;5EEC;5EEC; # (廬; 廬; 廬; 廬; 廬; ) CJK COMPATIBILITY IDEOGRAPH-F982
+F983;65C5;65C5;65C5;65C5; # (旅; 旅; 旅; 旅; 旅; ) CJK COMPATIBILITY IDEOGRAPH-F983
+F984;6FFE;6FFE;6FFE;6FFE; # (濾; 濾; 濾; 濾; 濾; ) CJK COMPATIBILITY IDEOGRAPH-F984
+F985;792A;792A;792A;792A; # (礪; 礪; 礪; 礪; 礪; ) CJK COMPATIBILITY IDEOGRAPH-F985
+F986;95AD;95AD;95AD;95AD; # (閭; 閭; 閭; 閭; 閭; ) CJK COMPATIBILITY IDEOGRAPH-F986
+F987;9A6A;9A6A;9A6A;9A6A; # (驪; 驪; 驪; 驪; 驪; ) CJK COMPATIBILITY IDEOGRAPH-F987
+F988;9E97;9E97;9E97;9E97; # (麗; 麗; 麗; 麗; 麗; ) CJK COMPATIBILITY IDEOGRAPH-F988
+F989;9ECE;9ECE;9ECE;9ECE; # (黎; 黎; 黎; 黎; 黎; ) CJK COMPATIBILITY IDEOGRAPH-F989
+F98A;529B;529B;529B;529B; # (力; 力; 力; 力; 力; ) CJK COMPATIBILITY IDEOGRAPH-F98A
+F98B;66C6;66C6;66C6;66C6; # (曆; 曆; 曆; 曆; 曆; ) CJK COMPATIBILITY IDEOGRAPH-F98B
+F98C;6B77;6B77;6B77;6B77; # (歷; 歷; 歷; 歷; 歷; ) CJK COMPATIBILITY IDEOGRAPH-F98C
+F98D;8F62;8F62;8F62;8F62; # (ï¦; è½¢; è½¢; è½¢; è½¢; ) CJK COMPATIBILITY IDEOGRAPH-F98D
+F98E;5E74;5E74;5E74;5E74; # (年; 年; 年; 年; 年; ) CJK COMPATIBILITY IDEOGRAPH-F98E
+F98F;6190;6190;6190;6190; # (ï¦; æ†; æ†; æ†; æ†; ) CJK COMPATIBILITY IDEOGRAPH-F98F
+F990;6200;6200;6200;6200; # (ï¦; 戀; 戀; 戀; 戀; ) CJK COMPATIBILITY IDEOGRAPH-F990
+F991;649A;649A;649A;649A; # (撚; 撚; 撚; 撚; 撚; ) CJK COMPATIBILITY IDEOGRAPH-F991
+F992;6F23;6F23;6F23;6F23; # (漣; 漣; 漣; 漣; 漣; ) CJK COMPATIBILITY IDEOGRAPH-F992
+F993;7149;7149;7149;7149; # (煉; 煉; 煉; 煉; 煉; ) CJK COMPATIBILITY IDEOGRAPH-F993
+F994;7489;7489;7489;7489; # (璉; 璉; 璉; 璉; 璉; ) CJK COMPATIBILITY IDEOGRAPH-F994
+F995;79CA;79CA;79CA;79CA; # (秊; 秊; 秊; 秊; 秊; ) CJK COMPATIBILITY IDEOGRAPH-F995
+F996;7DF4;7DF4;7DF4;7DF4; # (練; 練; 練; 練; 練; ) CJK COMPATIBILITY IDEOGRAPH-F996
+F997;806F;806F;806F;806F; # (聯; è¯; è¯; è¯; è¯; ) CJK COMPATIBILITY IDEOGRAPH-F997
+F998;8F26;8F26;8F26;8F26; # (輦; 輦; 輦; 輦; 輦; ) CJK COMPATIBILITY IDEOGRAPH-F998
+F999;84EE;84EE;84EE;84EE; # (蓮; 蓮; 蓮; 蓮; 蓮; ) CJK COMPATIBILITY IDEOGRAPH-F999
+F99A;9023;9023;9023;9023; # (連; 連; 連; 連; 連; ) CJK COMPATIBILITY IDEOGRAPH-F99A
+F99B;934A;934A;934A;934A; # (鍊; éŠ; éŠ; éŠ; éŠ; ) CJK COMPATIBILITY IDEOGRAPH-F99B
+F99C;5217;5217;5217;5217; # (列; 列; 列; 列; 列; ) CJK COMPATIBILITY IDEOGRAPH-F99C
+F99D;52A3;52A3;52A3;52A3; # (ï¦; 劣; 劣; 劣; 劣; ) CJK COMPATIBILITY IDEOGRAPH-F99D
+F99E;54BD;54BD;54BD;54BD; # (咽; 咽; 咽; 咽; 咽; ) CJK COMPATIBILITY IDEOGRAPH-F99E
+F99F;70C8;70C8;70C8;70C8; # (烈; 烈; 烈; 烈; 烈; ) CJK COMPATIBILITY IDEOGRAPH-F99F
+F9A0;88C2;88C2;88C2;88C2; # (裂; 裂; 裂; 裂; 裂; ) CJK COMPATIBILITY IDEOGRAPH-F9A0
+F9A1;8AAA;8AAA;8AAA;8AAA; # (說; 說; 說; 說; 說; ) CJK COMPATIBILITY IDEOGRAPH-F9A1
+F9A2;5EC9;5EC9;5EC9;5EC9; # (廉; 廉; 廉; 廉; 廉; ) CJK COMPATIBILITY IDEOGRAPH-F9A2
+F9A3;5FF5;5FF5;5FF5;5FF5; # (念; 念; 念; 念; 念; ) CJK COMPATIBILITY IDEOGRAPH-F9A3
+F9A4;637B;637B;637B;637B; # (捻; æ»; æ»; æ»; æ»; ) CJK COMPATIBILITY IDEOGRAPH-F9A4
+F9A5;6BAE;6BAE;6BAE;6BAE; # (殮; 殮; 殮; 殮; 殮; ) CJK COMPATIBILITY IDEOGRAPH-F9A5
+F9A6;7C3E;7C3E;7C3E;7C3E; # (簾; 簾; 簾; 簾; 簾; ) CJK COMPATIBILITY IDEOGRAPH-F9A6
+F9A7;7375;7375;7375;7375; # (獵; çµ; çµ; çµ; çµ; ) CJK COMPATIBILITY IDEOGRAPH-F9A7
+F9A8;4EE4;4EE4;4EE4;4EE4; # (令; 令; 令; 令; 令; ) CJK COMPATIBILITY IDEOGRAPH-F9A8
+F9A9;56F9;56F9;56F9;56F9; # (囹; 囹; 囹; 囹; 囹; ) CJK COMPATIBILITY IDEOGRAPH-F9A9
+F9AA;5BE7;5BE7;5BE7;5BE7; # (寧; 寧; 寧; 寧; 寧; ) CJK COMPATIBILITY IDEOGRAPH-F9AA
+F9AB;5DBA;5DBA;5DBA;5DBA; # (嶺; 嶺; 嶺; 嶺; 嶺; ) CJK COMPATIBILITY IDEOGRAPH-F9AB
+F9AC;601C;601C;601C;601C; # (怜; 怜; 怜; 怜; 怜; ) CJK COMPATIBILITY IDEOGRAPH-F9AC
+F9AD;73B2;73B2;73B2;73B2; # (玲; 玲; 玲; 玲; 玲; ) CJK COMPATIBILITY IDEOGRAPH-F9AD
+F9AE;7469;7469;7469;7469; # (瑩; 瑩; 瑩; 瑩; 瑩; ) CJK COMPATIBILITY IDEOGRAPH-F9AE
+F9AF;7F9A;7F9A;7F9A;7F9A; # (羚; 羚; 羚; 羚; 羚; ) CJK COMPATIBILITY IDEOGRAPH-F9AF
+F9B0;8046;8046;8046;8046; # (聆; è†; è†; è†; è†; ) CJK COMPATIBILITY IDEOGRAPH-F9B0
+F9B1;9234;9234;9234;9234; # (鈴; 鈴; 鈴; 鈴; 鈴; ) CJK COMPATIBILITY IDEOGRAPH-F9B1
+F9B2;96F6;96F6;96F6;96F6; # (零; 零; 零; 零; 零; ) CJK COMPATIBILITY IDEOGRAPH-F9B2
+F9B3;9748;9748;9748;9748; # (靈; éˆ; éˆ; éˆ; éˆ; ) CJK COMPATIBILITY IDEOGRAPH-F9B3
+F9B4;9818;9818;9818;9818; # (領; 領; 領; 領; 領; ) CJK COMPATIBILITY IDEOGRAPH-F9B4
+F9B5;4F8B;4F8B;4F8B;4F8B; # (例; 例; 例; 例; 例; ) CJK COMPATIBILITY IDEOGRAPH-F9B5
+F9B6;79AE;79AE;79AE;79AE; # (禮; 禮; 禮; 禮; 禮; ) CJK COMPATIBILITY IDEOGRAPH-F9B6
+F9B7;91B4;91B4;91B4;91B4; # (醴; 醴; 醴; 醴; 醴; ) CJK COMPATIBILITY IDEOGRAPH-F9B7
+F9B8;96B8;96B8;96B8;96B8; # (隸; 隸; 隸; 隸; 隸; ) CJK COMPATIBILITY IDEOGRAPH-F9B8
+F9B9;60E1;60E1;60E1;60E1; # (惡; 惡; 惡; 惡; 惡; ) CJK COMPATIBILITY IDEOGRAPH-F9B9
+F9BA;4E86;4E86;4E86;4E86; # (了; 了; 了; 了; 了; ) CJK COMPATIBILITY IDEOGRAPH-F9BA
+F9BB;50DA;50DA;50DA;50DA; # (僚; 僚; 僚; 僚; 僚; ) CJK COMPATIBILITY IDEOGRAPH-F9BB
+F9BC;5BEE;5BEE;5BEE;5BEE; # (寮; 寮; 寮; 寮; 寮; ) CJK COMPATIBILITY IDEOGRAPH-F9BC
+F9BD;5C3F;5C3F;5C3F;5C3F; # (尿; 尿; 尿; 尿; 尿; ) CJK COMPATIBILITY IDEOGRAPH-F9BD
+F9BE;6599;6599;6599;6599; # (料; 料; 料; 料; 料; ) CJK COMPATIBILITY IDEOGRAPH-F9BE
+F9BF;6A02;6A02;6A02;6A02; # (樂; 樂; 樂; 樂; 樂; ) CJK COMPATIBILITY IDEOGRAPH-F9BF
+F9C0;71CE;71CE;71CE;71CE; # (燎; 燎; 燎; 燎; 燎; ) CJK COMPATIBILITY IDEOGRAPH-F9C0
+F9C1;7642;7642;7642;7642; # (ï§; 療; 療; 療; 療; ) CJK COMPATIBILITY IDEOGRAPH-F9C1
+F9C2;84FC;84FC;84FC;84FC; # (蓼; 蓼; 蓼; 蓼; 蓼; ) CJK COMPATIBILITY IDEOGRAPH-F9C2
+F9C3;907C;907C;907C;907C; # (遼; é¼; é¼; é¼; é¼; ) CJK COMPATIBILITY IDEOGRAPH-F9C3
+F9C4;9F8D;9F8D;9F8D;9F8D; # (ï§„; é¾; é¾; é¾; é¾; ) CJK COMPATIBILITY IDEOGRAPH-F9C4
+F9C5;6688;6688;6688;6688; # (暈; 暈; 暈; 暈; 暈; ) CJK COMPATIBILITY IDEOGRAPH-F9C5
+F9C6;962E;962E;962E;962E; # (阮; 阮; 阮; 阮; 阮; ) CJK COMPATIBILITY IDEOGRAPH-F9C6
+F9C7;5289;5289;5289;5289; # (劉; 劉; 劉; 劉; 劉; ) CJK COMPATIBILITY IDEOGRAPH-F9C7
+F9C8;677B;677B;677B;677B; # (杻; æ»; æ»; æ»; æ»; ) CJK COMPATIBILITY IDEOGRAPH-F9C8
+F9C9;67F3;67F3;67F3;67F3; # (柳; 柳; 柳; 柳; 柳; ) CJK COMPATIBILITY IDEOGRAPH-F9C9
+F9CA;6D41;6D41;6D41;6D41; # (ï§Š; æµ; æµ; æµ; æµ; ) CJK COMPATIBILITY IDEOGRAPH-F9CA
+F9CB;6E9C;6E9C;6E9C;6E9C; # (溜; 溜; 溜; 溜; 溜; ) CJK COMPATIBILITY IDEOGRAPH-F9CB
+F9CC;7409;7409;7409;7409; # (ï§Œ; ç‰; ç‰; ç‰; ç‰; ) CJK COMPATIBILITY IDEOGRAPH-F9CC
+F9CD;7559;7559;7559;7559; # (ï§; ç•™; ç•™; ç•™; ç•™; ) CJK COMPATIBILITY IDEOGRAPH-F9CD
+F9CE;786B;786B;786B;786B; # (ï§Ž; ç¡«; ç¡«; ç¡«; ç¡«; ) CJK COMPATIBILITY IDEOGRAPH-F9CE
+F9CF;7D10;7D10;7D10;7D10; # (ï§; ç´; ç´; ç´; ç´; ) CJK COMPATIBILITY IDEOGRAPH-F9CF
+F9D0;985E;985E;985E;985E; # (ï§; 類; 類; 類; 類; ) CJK COMPATIBILITY IDEOGRAPH-F9D0
+F9D1;516D;516D;516D;516D; # (ï§‘; å…­; å…­; å…­; å…­; ) CJK COMPATIBILITY IDEOGRAPH-F9D1
+F9D2;622E;622E;622E;622E; # (戮; 戮; 戮; 戮; 戮; ) CJK COMPATIBILITY IDEOGRAPH-F9D2
+F9D3;9678;9678;9678;9678; # (陸; 陸; 陸; 陸; 陸; ) CJK COMPATIBILITY IDEOGRAPH-F9D3
+F9D4;502B;502B;502B;502B; # (倫; 倫; 倫; 倫; 倫; ) CJK COMPATIBILITY IDEOGRAPH-F9D4
+F9D5;5D19;5D19;5D19;5D19; # (ï§•; å´™; å´™; å´™; å´™; ) CJK COMPATIBILITY IDEOGRAPH-F9D5
+F9D6;6DEA;6DEA;6DEA;6DEA; # (ï§–; æ·ª; æ·ª; æ·ª; æ·ª; ) CJK COMPATIBILITY IDEOGRAPH-F9D6
+F9D7;8F2A;8F2A;8F2A;8F2A; # (輪; 輪; 輪; 輪; 輪; ) CJK COMPATIBILITY IDEOGRAPH-F9D7
+F9D8;5F8B;5F8B;5F8B;5F8B; # (律; 律; 律; 律; 律; ) CJK COMPATIBILITY IDEOGRAPH-F9D8
+F9D9;6144;6144;6144;6144; # (ï§™; æ…„; æ…„; æ…„; æ…„; ) CJK COMPATIBILITY IDEOGRAPH-F9D9
+F9DA;6817;6817;6817;6817; # (ï§š; æ —; æ —; æ —; æ —; ) CJK COMPATIBILITY IDEOGRAPH-F9DA
+F9DB;7387;7387;7387;7387; # (率; 率; 率; 率; 率; ) CJK COMPATIBILITY IDEOGRAPH-F9DB
+F9DC;9686;9686;9686;9686; # (隆; 隆; 隆; 隆; 隆; ) CJK COMPATIBILITY IDEOGRAPH-F9DC
+F9DD;5229;5229;5229;5229; # (ï§; 利; 利; 利; 利; ) CJK COMPATIBILITY IDEOGRAPH-F9DD
+F9DE;540F;540F;540F;540F; # (ï§ž; å; å; å; å; ) CJK COMPATIBILITY IDEOGRAPH-F9DE
+F9DF;5C65;5C65;5C65;5C65; # (ï§Ÿ; å±¥; å±¥; å±¥; å±¥; ) CJK COMPATIBILITY IDEOGRAPH-F9DF
+F9E0;6613;6613;6613;6613; # (易; 易; 易; 易; 易; ) CJK COMPATIBILITY IDEOGRAPH-F9E0
+F9E1;674E;674E;674E;674E; # (ï§¡; æŽ; æŽ; æŽ; æŽ; ) CJK COMPATIBILITY IDEOGRAPH-F9E1
+F9E2;68A8;68A8;68A8;68A8; # (梨; 梨; 梨; 梨; 梨; ) CJK COMPATIBILITY IDEOGRAPH-F9E2
+F9E3;6CE5;6CE5;6CE5;6CE5; # (ï§£; æ³¥; æ³¥; æ³¥; æ³¥; ) CJK COMPATIBILITY IDEOGRAPH-F9E3
+F9E4;7406;7406;7406;7406; # (理; ç†; ç†; ç†; ç†; ) CJK COMPATIBILITY IDEOGRAPH-F9E4
+F9E5;75E2;75E2;75E2;75E2; # (ï§¥; ç—¢; ç—¢; ç—¢; ç—¢; ) CJK COMPATIBILITY IDEOGRAPH-F9E5
+F9E6;7F79;7F79;7F79;7F79; # (罹; 罹; 罹; 罹; 罹; ) CJK COMPATIBILITY IDEOGRAPH-F9E6
+F9E7;88CF;88CF;88CF;88CF; # (ï§§; è£; è£; è£; è£; ) CJK COMPATIBILITY IDEOGRAPH-F9E7
+F9E8;88E1;88E1;88E1;88E1; # (裡; 裡; 裡; 裡; 裡; ) CJK COMPATIBILITY IDEOGRAPH-F9E8
+F9E9;91CC;91CC;91CC;91CC; # (里; 里; 里; 里; 里; ) CJK COMPATIBILITY IDEOGRAPH-F9E9
+F9EA;96E2;96E2;96E2;96E2; # (離; 離; 離; 離; 離; ) CJK COMPATIBILITY IDEOGRAPH-F9EA
+F9EB;533F;533F;533F;533F; # (匿; 匿; 匿; 匿; 匿; ) CJK COMPATIBILITY IDEOGRAPH-F9EB
+F9EC;6EBA;6EBA;6EBA;6EBA; # (溺; 溺; 溺; 溺; 溺; ) CJK COMPATIBILITY IDEOGRAPH-F9EC
+F9ED;541D;541D;541D;541D; # (ï§­; å; å; å; å; ) CJK COMPATIBILITY IDEOGRAPH-F9ED
+F9EE;71D0;71D0;71D0;71D0; # (ï§®; ç‡; ç‡; ç‡; ç‡; ) CJK COMPATIBILITY IDEOGRAPH-F9EE
+F9EF;7498;7498;7498;7498; # (璘; 璘; 璘; 璘; 璘; ) CJK COMPATIBILITY IDEOGRAPH-F9EF
+F9F0;85FA;85FA;85FA;85FA; # (ï§°; è—º; è—º; è—º; è—º; ) CJK COMPATIBILITY IDEOGRAPH-F9F0
+F9F1;96A3;96A3;96A3;96A3; # (隣; 隣; 隣; 隣; 隣; ) CJK COMPATIBILITY IDEOGRAPH-F9F1
+F9F2;9C57;9C57;9C57;9C57; # (ï§²; é±—; é±—; é±—; é±—; ) CJK COMPATIBILITY IDEOGRAPH-F9F2
+F9F3;9E9F;9E9F;9E9F;9E9F; # (麟; 麟; 麟; 麟; 麟; ) CJK COMPATIBILITY IDEOGRAPH-F9F3
+F9F4;6797;6797;6797;6797; # (ï§´; æž—; æž—; æž—; æž—; ) CJK COMPATIBILITY IDEOGRAPH-F9F4
+F9F5;6DCB;6DCB;6DCB;6DCB; # (ï§µ; æ·‹; æ·‹; æ·‹; æ·‹; ) CJK COMPATIBILITY IDEOGRAPH-F9F5
+F9F6;81E8;81E8;81E8;81E8; # (臨; 臨; 臨; 臨; 臨; ) CJK COMPATIBILITY IDEOGRAPH-F9F6
+F9F7;7ACB;7ACB;7ACB;7ACB; # (ï§·; ç«‹; ç«‹; ç«‹; ç«‹; ) CJK COMPATIBILITY IDEOGRAPH-F9F7
+F9F8;7B20;7B20;7B20;7B20; # (笠; 笠; 笠; 笠; 笠; ) CJK COMPATIBILITY IDEOGRAPH-F9F8
+F9F9;7C92;7C92;7C92;7C92; # (ï§¹; ç²’; ç²’; ç²’; ç²’; ) CJK COMPATIBILITY IDEOGRAPH-F9F9
+F9FA;72C0;72C0;72C0;72C0; # (狀; 狀; 狀; 狀; 狀; ) CJK COMPATIBILITY IDEOGRAPH-F9FA
+F9FB;7099;7099;7099;7099; # (ï§»; ç‚™; ç‚™; ç‚™; ç‚™; ) CJK COMPATIBILITY IDEOGRAPH-F9FB
+F9FC;8B58;8B58;8B58;8B58; # (ï§¼; è­˜; è­˜; è­˜; è­˜; ) CJK COMPATIBILITY IDEOGRAPH-F9FC
+F9FD;4EC0;4EC0;4EC0;4EC0; # (什; 什; 什; 什; 什; ) CJK COMPATIBILITY IDEOGRAPH-F9FD
+F9FE;8336;8336;8336;8336; # (茶; 茶; 茶; 茶; 茶; ) CJK COMPATIBILITY IDEOGRAPH-F9FE
+F9FF;523A;523A;523A;523A; # (刺; 刺; 刺; 刺; 刺; ) CJK COMPATIBILITY IDEOGRAPH-F9FF
+FA00;5207;5207;5207;5207; # (切; 切; 切; 切; 切; ) CJK COMPATIBILITY IDEOGRAPH-FA00
+FA01;5EA6;5EA6;5EA6;5EA6; # (ï¨; 度; 度; 度; 度; ) CJK COMPATIBILITY IDEOGRAPH-FA01
+FA02;62D3;62D3;62D3;62D3; # (拓; 拓; 拓; 拓; 拓; ) CJK COMPATIBILITY IDEOGRAPH-FA02
+FA03;7CD6;7CD6;7CD6;7CD6; # (糖; 糖; 糖; 糖; 糖; ) CJK COMPATIBILITY IDEOGRAPH-FA03
+FA04;5B85;5B85;5B85;5B85; # (宅; 宅; 宅; 宅; 宅; ) CJK COMPATIBILITY IDEOGRAPH-FA04
+FA05;6D1E;6D1E;6D1E;6D1E; # (洞; 洞; 洞; 洞; 洞; ) CJK COMPATIBILITY IDEOGRAPH-FA05
+FA06;66B4;66B4;66B4;66B4; # (暴; 暴; 暴; 暴; 暴; ) CJK COMPATIBILITY IDEOGRAPH-FA06
+FA07;8F3B;8F3B;8F3B;8F3B; # (輻; 輻; 輻; 輻; 輻; ) CJK COMPATIBILITY IDEOGRAPH-FA07
+FA08;884C;884C;884C;884C; # (行; 行; 行; 行; 行; ) CJK COMPATIBILITY IDEOGRAPH-FA08
+FA09;964D;964D;964D;964D; # (降; é™; é™; é™; é™; ) CJK COMPATIBILITY IDEOGRAPH-FA09
+FA0A;898B;898B;898B;898B; # (見; 見; 見; 見; 見; ) CJK COMPATIBILITY IDEOGRAPH-FA0A
+FA0B;5ED3;5ED3;5ED3;5ED3; # (廓; 廓; 廓; 廓; 廓; ) CJK COMPATIBILITY IDEOGRAPH-FA0B
+FA0C;5140;5140;5140;5140; # (兀; 兀; 兀; 兀; 兀; ) CJK COMPATIBILITY IDEOGRAPH-FA0C
+FA0D;55C0;55C0;55C0;55C0; # (ï¨; å—€; å—€; å—€; å—€; ) CJK COMPATIBILITY IDEOGRAPH-FA0D
+FA10;585A;585A;585A;585A; # (ï¨; 塚; 塚; 塚; 塚; ) CJK COMPATIBILITY IDEOGRAPH-FA10
+FA12;6674;6674;6674;6674; # (晴; 晴; 晴; 晴; 晴; ) CJK COMPATIBILITY IDEOGRAPH-FA12
+FA15;51DE;51DE;51DE;51DE; # (凞; 凞; 凞; 凞; 凞; ) CJK COMPATIBILITY IDEOGRAPH-FA15
+FA16;732A;732A;732A;732A; # (猪; 猪; 猪; 猪; 猪; ) CJK COMPATIBILITY IDEOGRAPH-FA16
+FA17;76CA;76CA;76CA;76CA; # (益; 益; 益; 益; 益; ) CJK COMPATIBILITY IDEOGRAPH-FA17
+FA18;793C;793C;793C;793C; # (礼; 礼; 礼; 礼; 礼; ) CJK COMPATIBILITY IDEOGRAPH-FA18
+FA19;795E;795E;795E;795E; # (神; 神; 神; 神; 神; ) CJK COMPATIBILITY IDEOGRAPH-FA19
+FA1A;7965;7965;7965;7965; # (祥; 祥; 祥; 祥; 祥; ) CJK COMPATIBILITY IDEOGRAPH-FA1A
+FA1B;798F;798F;798F;798F; # (福; ç¦; ç¦; ç¦; ç¦; ) CJK COMPATIBILITY IDEOGRAPH-FA1B
+FA1C;9756;9756;9756;9756; # (靖; é–; é–; é–; é–; ) CJK COMPATIBILITY IDEOGRAPH-FA1C
+FA1D;7CBE;7CBE;7CBE;7CBE; # (ï¨; ç²¾; ç²¾; ç²¾; ç²¾; ) CJK COMPATIBILITY IDEOGRAPH-FA1D
+FA1E;7FBD;7FBD;7FBD;7FBD; # (羽; 羽; 羽; 羽; 羽; ) CJK COMPATIBILITY IDEOGRAPH-FA1E
+FA20;8612;8612;8612;8612; # (蘒; 蘒; 蘒; 蘒; 蘒; ) CJK COMPATIBILITY IDEOGRAPH-FA20
+FA22;8AF8;8AF8;8AF8;8AF8; # (諸; 諸; 諸; 諸; 諸; ) CJK COMPATIBILITY IDEOGRAPH-FA22
+FA25;9038;9038;9038;9038; # (逸; 逸; 逸; 逸; 逸; ) CJK COMPATIBILITY IDEOGRAPH-FA25
+FA26;90FD;90FD;90FD;90FD; # (都; 都; 都; 都; 都; ) CJK COMPATIBILITY IDEOGRAPH-FA26
+FA2A;98EF;98EF;98EF;98EF; # (飯; 飯; 飯; 飯; 飯; ) CJK COMPATIBILITY IDEOGRAPH-FA2A
+FA2B;98FC;98FC;98FC;98FC; # (飼; 飼; 飼; 飼; 飼; ) CJK COMPATIBILITY IDEOGRAPH-FA2B
+FA2C;9928;9928;9928;9928; # (館; 館; 館; 館; 館; ) CJK COMPATIBILITY IDEOGRAPH-FA2C
+FA2D;9DB4;9DB4;9DB4;9DB4; # (鶴; 鶴; 鶴; 鶴; 鶴; ) CJK COMPATIBILITY IDEOGRAPH-FA2D
+FA30;4FAE;4FAE;4FAE;4FAE; # (侮; 侮; 侮; 侮; 侮; ) CJK COMPATIBILITY IDEOGRAPH-FA30
+FA31;50E7;50E7;50E7;50E7; # (僧; 僧; 僧; 僧; 僧; ) CJK COMPATIBILITY IDEOGRAPH-FA31
+FA32;514D;514D;514D;514D; # (免; å…; å…; å…; å…; ) CJK COMPATIBILITY IDEOGRAPH-FA32
+FA33;52C9;52C9;52C9;52C9; # (勉; 勉; 勉; 勉; 勉; ) CJK COMPATIBILITY IDEOGRAPH-FA33
+FA34;52E4;52E4;52E4;52E4; # (勤; 勤; 勤; 勤; 勤; ) CJK COMPATIBILITY IDEOGRAPH-FA34
+FA35;5351;5351;5351;5351; # (卑; å‘; å‘; å‘; å‘; ) CJK COMPATIBILITY IDEOGRAPH-FA35
+FA36;559D;559D;559D;559D; # (喝; å–; å–; å–; å–; ) CJK COMPATIBILITY IDEOGRAPH-FA36
+FA37;5606;5606;5606;5606; # (嘆; 嘆; 嘆; 嘆; 嘆; ) CJK COMPATIBILITY IDEOGRAPH-FA37
+FA38;5668;5668;5668;5668; # (器; 器; 器; 器; 器; ) CJK COMPATIBILITY IDEOGRAPH-FA38
+FA39;5840;5840;5840;5840; # (塀; 塀; 塀; 塀; 塀; ) CJK COMPATIBILITY IDEOGRAPH-FA39
+FA3A;58A8;58A8;58A8;58A8; # (墨; 墨; 墨; 墨; 墨; ) CJK COMPATIBILITY IDEOGRAPH-FA3A
+FA3B;5C64;5C64;5C64;5C64; # (層; 層; 層; 層; 層; ) CJK COMPATIBILITY IDEOGRAPH-FA3B
+FA3C;5C6E;5C6E;5C6E;5C6E; # (屮; 屮; 屮; 屮; 屮; ) CJK COMPATIBILITY IDEOGRAPH-FA3C
+FA3D;6094;6094;6094;6094; # (悔; 悔; 悔; 悔; 悔; ) CJK COMPATIBILITY IDEOGRAPH-FA3D
+FA3E;6168;6168;6168;6168; # (慨; 慨; 慨; 慨; 慨; ) CJK COMPATIBILITY IDEOGRAPH-FA3E
+FA3F;618E;618E;618E;618E; # (憎; 憎; 憎; 憎; 憎; ) CJK COMPATIBILITY IDEOGRAPH-FA3F
+FA40;61F2;61F2;61F2;61F2; # (懲; 懲; 懲; 懲; 懲; ) CJK COMPATIBILITY IDEOGRAPH-FA40
+FA41;654F;654F;654F;654F; # (ï©; æ•; æ•; æ•; æ•; ) CJK COMPATIBILITY IDEOGRAPH-FA41
+FA42;65E2;65E2;65E2;65E2; # (ï©‚; æ—¢; æ—¢; æ—¢; æ—¢; ) CJK COMPATIBILITY IDEOGRAPH-FA42
+FA43;6691;6691;6691;6691; # (暑; 暑; 暑; 暑; 暑; ) CJK COMPATIBILITY IDEOGRAPH-FA43
+FA44;6885;6885;6885;6885; # (梅; 梅; 梅; 梅; 梅; ) CJK COMPATIBILITY IDEOGRAPH-FA44
+FA45;6D77;6D77;6D77;6D77; # (ï©…; æµ·; æµ·; æµ·; æµ·; ) CJK COMPATIBILITY IDEOGRAPH-FA45
+FA46;6E1A;6E1A;6E1A;6E1A; # (渚; 渚; 渚; 渚; 渚; ) CJK COMPATIBILITY IDEOGRAPH-FA46
+FA47;6F22;6F22;6F22;6F22; # (漢; 漢; 漢; 漢; 漢; ) CJK COMPATIBILITY IDEOGRAPH-FA47
+FA48;716E;716E;716E;716E; # (煮; 煮; 煮; 煮; 煮; ) CJK COMPATIBILITY IDEOGRAPH-FA48
+FA49;722B;722B;722B;722B; # (爫; 爫; 爫; 爫; 爫; ) CJK COMPATIBILITY IDEOGRAPH-FA49
+FA4A;7422;7422;7422;7422; # (琢; ç¢; ç¢; ç¢; ç¢; ) CJK COMPATIBILITY IDEOGRAPH-FA4A
+FA4B;7891;7891;7891;7891; # (碑; 碑; 碑; 碑; 碑; ) CJK COMPATIBILITY IDEOGRAPH-FA4B
+FA4C;793E;793E;793E;793E; # (社; 社; 社; 社; 社; ) CJK COMPATIBILITY IDEOGRAPH-FA4C
+FA4D;7949;7949;7949;7949; # (ï©; 祉; 祉; 祉; 祉; ) CJK COMPATIBILITY IDEOGRAPH-FA4D
+FA4E;7948;7948;7948;7948; # (祈; 祈; 祈; 祈; 祈; ) CJK COMPATIBILITY IDEOGRAPH-FA4E
+FA4F;7950;7950;7950;7950; # (ï©; ç¥; ç¥; ç¥; ç¥; ) CJK COMPATIBILITY IDEOGRAPH-FA4F
+FA50;7956;7956;7956;7956; # (ï©; 祖; 祖; 祖; 祖; ) CJK COMPATIBILITY IDEOGRAPH-FA50
+FA51;795D;795D;795D;795D; # (ï©‘; ç¥; ç¥; ç¥; ç¥; ) CJK COMPATIBILITY IDEOGRAPH-FA51
+FA52;798D;798D;798D;798D; # (ï©’; ç¦; ç¦; ç¦; ç¦; ) CJK COMPATIBILITY IDEOGRAPH-FA52
+FA53;798E;798E;798E;798E; # (禎; 禎; 禎; 禎; 禎; ) CJK COMPATIBILITY IDEOGRAPH-FA53
+FA54;7A40;7A40;7A40;7A40; # (ï©”; ç©€; ç©€; ç©€; ç©€; ) CJK COMPATIBILITY IDEOGRAPH-FA54
+FA55;7A81;7A81;7A81;7A81; # (ï©•; çª; çª; çª; çª; ) CJK COMPATIBILITY IDEOGRAPH-FA55
+FA56;7BC0;7BC0;7BC0;7BC0; # (節; 節; 節; 節; 節; ) CJK COMPATIBILITY IDEOGRAPH-FA56
+FA57;7DF4;7DF4;7DF4;7DF4; # (ï©—; ç·´; ç·´; ç·´; ç·´; ) CJK COMPATIBILITY IDEOGRAPH-FA57
+FA58;7E09;7E09;7E09;7E09; # (縉; 縉; 縉; 縉; 縉; ) CJK COMPATIBILITY IDEOGRAPH-FA58
+FA59;7E41;7E41;7E41;7E41; # (ï©™; ç¹; ç¹; ç¹; ç¹; ) CJK COMPATIBILITY IDEOGRAPH-FA59
+FA5A;7F72;7F72;7F72;7F72; # (署; 署; 署; 署; 署; ) CJK COMPATIBILITY IDEOGRAPH-FA5A
+FA5B;8005;8005;8005;8005; # (者; 者; 者; 者; 者; ) CJK COMPATIBILITY IDEOGRAPH-FA5B
+FA5C;81ED;81ED;81ED;81ED; # (臭; 臭; 臭; 臭; 臭; ) CJK COMPATIBILITY IDEOGRAPH-FA5C
+FA5D;8279;8279;8279;8279; # (ï©; 艹; 艹; 艹; 艹; ) CJK COMPATIBILITY IDEOGRAPH-FA5D
+FA5E;8279;8279;8279;8279; # (艹; 艹; 艹; 艹; 艹; ) CJK COMPATIBILITY IDEOGRAPH-FA5E
+FA5F;8457;8457;8457;8457; # (著; 著; 著; 著; 著; ) CJK COMPATIBILITY IDEOGRAPH-FA5F
+FA60;8910;8910;8910;8910; # (ï© ; è¤; è¤; è¤; è¤; ) CJK COMPATIBILITY IDEOGRAPH-FA60
+FA61;8996;8996;8996;8996; # (視; 視; 視; 視; 視; ) CJK COMPATIBILITY IDEOGRAPH-FA61
+FA62;8B01;8B01;8B01;8B01; # (ï©¢; è¬; è¬; è¬; è¬; ) CJK COMPATIBILITY IDEOGRAPH-FA62
+FA63;8B39;8B39;8B39;8B39; # (謹; 謹; 謹; 謹; 謹; ) CJK COMPATIBILITY IDEOGRAPH-FA63
+FA64;8CD3;8CD3;8CD3;8CD3; # (賓; 賓; 賓; 賓; 賓; ) CJK COMPATIBILITY IDEOGRAPH-FA64
+FA65;8D08;8D08;8D08;8D08; # (ï©¥; è´ˆ; è´ˆ; è´ˆ; è´ˆ; ) CJK COMPATIBILITY IDEOGRAPH-FA65
+FA66;8FB6;8FB6;8FB6;8FB6; # (辶; 辶; 辶; 辶; 辶; ) CJK COMPATIBILITY IDEOGRAPH-FA66
+FA67;9038;9038;9038;9038; # (逸; 逸; 逸; 逸; 逸; ) CJK COMPATIBILITY IDEOGRAPH-FA67
+FA68;96E3;96E3;96E3;96E3; # (難; 難; 難; 難; 難; ) CJK COMPATIBILITY IDEOGRAPH-FA68
+FA69;97FF;97FF;97FF;97FF; # (響; 響; 響; 響; 響; ) CJK COMPATIBILITY IDEOGRAPH-FA69
+FA6A;983B;983B;983B;983B; # (頻; 頻; 頻; 頻; 頻; ) CJK COMPATIBILITY IDEOGRAPH-FA6A
+FB00;FB00;FB00;0066 0066;0066 0066; # (ff; ff; ff; ff; ff; ) LATIN SMALL LIGATURE FF
+FB01;FB01;FB01;0066 0069;0066 0069; # (ï¬; ï¬; ï¬; fi; fi; ) LATIN SMALL LIGATURE FI
+FB02;FB02;FB02;0066 006C;0066 006C; # (fl; fl; fl; fl; fl; ) LATIN SMALL LIGATURE FL
+FB03;FB03;FB03;0066 0066 0069;0066 0066 0069; # (ffi; ffi; ffi; ffi; ffi; ) LATIN SMALL LIGATURE FFI
+FB04;FB04;FB04;0066 0066 006C;0066 0066 006C; # (ffl; ffl; ffl; ffl; ffl; ) LATIN SMALL LIGATURE FFL
+FB05;FB05;FB05;0073 0074;0073 0074; # (ſt; ſt; ſt; st; st; ) LATIN SMALL LIGATURE LONG S T
+FB06;FB06;FB06;0073 0074;0073 0074; # (st; st; st; st; st; ) LATIN SMALL LIGATURE ST
+FB13;FB13;FB13;0574 0576;0574 0576; # (ﬓ; ﬓ; ﬓ; մն; մն; ) ARMENIAN SMALL LIGATURE MEN NOW
+FB14;FB14;FB14;0574 0565;0574 0565; # (ﬔ; ﬔ; ﬔ; մե; մե; ) ARMENIAN SMALL LIGATURE MEN ECH
+FB15;FB15;FB15;0574 056B;0574 056B; # (ﬕ; ﬕ; ﬕ; մի; մի; ) ARMENIAN SMALL LIGATURE MEN INI
+FB16;FB16;FB16;057E 0576;057E 0576; # (ﬖ; ﬖ; ﬖ; վն; վն; ) ARMENIAN SMALL LIGATURE VEW NOW
+FB17;FB17;FB17;0574 056D;0574 056D; # (ﬗ; ﬗ; ﬗ; մխ; մխ; ) ARMENIAN SMALL LIGATURE MEN XEH
+FB1D;05D9 05B4;05D9 05B4;05D9 05B4;05D9 05B4; # (ï¬; י◌ִ; י◌ִ; י◌ִ; י◌ִ; ) HEBREW LETTER YOD WITH HIRIQ
+FB1F;05F2 05B7;05F2 05B7;05F2 05B7;05F2 05B7; # (ײַ; ײ◌ַ; ײ◌ַ; ײ◌ַ; ײ◌ַ; ) HEBREW LIGATURE YIDDISH YOD YOD PATAH
+FB20;FB20;FB20;05E2;05E2; # (ﬠ; ﬠ; ﬠ; ע; ע; ) HEBREW LETTER ALTERNATIVE AYIN
+FB21;FB21;FB21;05D0;05D0; # (ﬡ; ﬡ; ﬡ; ×; ×; ) HEBREW LETTER WIDE ALEF
+FB22;FB22;FB22;05D3;05D3; # (ﬢ; ﬢ; ﬢ; ד; ד; ) HEBREW LETTER WIDE DALET
+FB23;FB23;FB23;05D4;05D4; # (ﬣ; ﬣ; ﬣ; ה; ה; ) HEBREW LETTER WIDE HE
+FB24;FB24;FB24;05DB;05DB; # (ﬤ; ﬤ; ﬤ; כ; כ; ) HEBREW LETTER WIDE KAF
+FB25;FB25;FB25;05DC;05DC; # (ﬥ; ﬥ; ﬥ; ל; ל; ) HEBREW LETTER WIDE LAMED
+FB26;FB26;FB26;05DD;05DD; # (ﬦ; ﬦ; ﬦ; ×; ×; ) HEBREW LETTER WIDE FINAL MEM
+FB27;FB27;FB27;05E8;05E8; # (ﬧ; ﬧ; ﬧ; ר; ר; ) HEBREW LETTER WIDE RESH
+FB28;FB28;FB28;05EA;05EA; # (ﬨ; ﬨ; ﬨ; ת; ת; ) HEBREW LETTER WIDE TAV
+FB29;FB29;FB29;002B;002B; # (﬩; ﬩; ﬩; +; +; ) HEBREW LETTER ALTERNATIVE PLUS SIGN
+FB2A;05E9 05C1;05E9 05C1;05E9 05C1;05E9 05C1; # (שׁ; ש◌×; ש◌×; ש◌×; ש◌×; ) HEBREW LETTER SHIN WITH SHIN DOT
+FB2B;05E9 05C2;05E9 05C2;05E9 05C2;05E9 05C2; # (שׂ; ש◌ׂ; ש◌ׂ; ש◌ׂ; ש◌ׂ; ) HEBREW LETTER SHIN WITH SIN DOT
+FB2C;05E9 05BC 05C1;05E9 05BC 05C1;05E9 05BC 05C1;05E9 05BC 05C1; # (שּׁ; ש◌ּ◌×; ש◌ּ◌×; ש◌ּ◌×; ש◌ּ◌×; ) HEBREW LETTER SHIN WITH DAGESH AND SHIN DOT
+FB2D;05E9 05BC 05C2;05E9 05BC 05C2;05E9 05BC 05C2;05E9 05BC 05C2; # (שּׂ; ש◌ּ◌ׂ; ש◌ּ◌ׂ; ש◌ּ◌ׂ; ש◌ּ◌ׂ; ) HEBREW LETTER SHIN WITH DAGESH AND SIN DOT
+FB2E;05D0 05B7;05D0 05B7;05D0 05B7;05D0 05B7; # (אַ; ×◌ַ; ×◌ַ; ×◌ַ; ×◌ַ; ) HEBREW LETTER ALEF WITH PATAH
+FB2F;05D0 05B8;05D0 05B8;05D0 05B8;05D0 05B8; # (אָ; ×◌ָ; ×◌ָ; ×◌ָ; ×◌ָ; ) HEBREW LETTER ALEF WITH QAMATS
+FB30;05D0 05BC;05D0 05BC;05D0 05BC;05D0 05BC; # (אּ; ×◌ּ; ×◌ּ; ×◌ּ; ×◌ּ; ) HEBREW LETTER ALEF WITH MAPIQ
+FB31;05D1 05BC;05D1 05BC;05D1 05BC;05D1 05BC; # (בּ; ב◌ּ; ב◌ּ; ב◌ּ; ב◌ּ; ) HEBREW LETTER BET WITH DAGESH
+FB32;05D2 05BC;05D2 05BC;05D2 05BC;05D2 05BC; # (גּ; ג◌ּ; ג◌ּ; ג◌ּ; ג◌ּ; ) HEBREW LETTER GIMEL WITH DAGESH
+FB33;05D3 05BC;05D3 05BC;05D3 05BC;05D3 05BC; # (דּ; ד◌ּ; ד◌ּ; ד◌ּ; ד◌ּ; ) HEBREW LETTER DALET WITH DAGESH
+FB34;05D4 05BC;05D4 05BC;05D4 05BC;05D4 05BC; # (הּ; ה◌ּ; ה◌ּ; ה◌ּ; ה◌ּ; ) HEBREW LETTER HE WITH MAPIQ
+FB35;05D5 05BC;05D5 05BC;05D5 05BC;05D5 05BC; # (וּ; ו◌ּ; ו◌ּ; ו◌ּ; ו◌ּ; ) HEBREW LETTER VAV WITH DAGESH
+FB36;05D6 05BC;05D6 05BC;05D6 05BC;05D6 05BC; # (זּ; ז◌ּ; ז◌ּ; ז◌ּ; ז◌ּ; ) HEBREW LETTER ZAYIN WITH DAGESH
+FB38;05D8 05BC;05D8 05BC;05D8 05BC;05D8 05BC; # (טּ; ט◌ּ; ט◌ּ; ט◌ּ; ט◌ּ; ) HEBREW LETTER TET WITH DAGESH
+FB39;05D9 05BC;05D9 05BC;05D9 05BC;05D9 05BC; # (יּ; י◌ּ; י◌ּ; י◌ּ; י◌ּ; ) HEBREW LETTER YOD WITH DAGESH
+FB3A;05DA 05BC;05DA 05BC;05DA 05BC;05DA 05BC; # (ךּ; ך◌ּ; ך◌ּ; ך◌ּ; ך◌ּ; ) HEBREW LETTER FINAL KAF WITH DAGESH
+FB3B;05DB 05BC;05DB 05BC;05DB 05BC;05DB 05BC; # (כּ; כ◌ּ; כ◌ּ; כ◌ּ; כ◌ּ; ) HEBREW LETTER KAF WITH DAGESH
+FB3C;05DC 05BC;05DC 05BC;05DC 05BC;05DC 05BC; # (לּ; ל◌ּ; ל◌ּ; ל◌ּ; ל◌ּ; ) HEBREW LETTER LAMED WITH DAGESH
+FB3E;05DE 05BC;05DE 05BC;05DE 05BC;05DE 05BC; # (מּ; מ◌ּ; מ◌ּ; מ◌ּ; מ◌ּ; ) HEBREW LETTER MEM WITH DAGESH
+FB40;05E0 05BC;05E0 05BC;05E0 05BC;05E0 05BC; # (נּ; נ◌ּ; נ◌ּ; נ◌ּ; נ◌ּ; ) HEBREW LETTER NUN WITH DAGESH
+FB41;05E1 05BC;05E1 05BC;05E1 05BC;05E1 05BC; # (ï­; ס◌ּ; ס◌ּ; ס◌ּ; ס◌ּ; ) HEBREW LETTER SAMEKH WITH DAGESH
+FB43;05E3 05BC;05E3 05BC;05E3 05BC;05E3 05BC; # (ףּ; ף◌ּ; ף◌ּ; ף◌ּ; ף◌ּ; ) HEBREW LETTER FINAL PE WITH DAGESH
+FB44;05E4 05BC;05E4 05BC;05E4 05BC;05E4 05BC; # (פּ; פ◌ּ; פ◌ּ; פ◌ּ; פ◌ּ; ) HEBREW LETTER PE WITH DAGESH
+FB46;05E6 05BC;05E6 05BC;05E6 05BC;05E6 05BC; # (צּ; צ◌ּ; צ◌ּ; צ◌ּ; צ◌ּ; ) HEBREW LETTER TSADI WITH DAGESH
+FB47;05E7 05BC;05E7 05BC;05E7 05BC;05E7 05BC; # (קּ; ק◌ּ; ק◌ּ; ק◌ּ; ק◌ּ; ) HEBREW LETTER QOF WITH DAGESH
+FB48;05E8 05BC;05E8 05BC;05E8 05BC;05E8 05BC; # (רּ; ר◌ּ; ר◌ּ; ר◌ּ; ר◌ּ; ) HEBREW LETTER RESH WITH DAGESH
+FB49;05E9 05BC;05E9 05BC;05E9 05BC;05E9 05BC; # (שּ; ש◌ּ; ש◌ּ; ש◌ּ; ש◌ּ; ) HEBREW LETTER SHIN WITH DAGESH
+FB4A;05EA 05BC;05EA 05BC;05EA 05BC;05EA 05BC; # (תּ; ת◌ּ; ת◌ּ; ת◌ּ; ת◌ּ; ) HEBREW LETTER TAV WITH DAGESH
+FB4B;05D5 05B9;05D5 05B9;05D5 05B9;05D5 05B9; # (וֹ; ו◌ֹ; ו◌ֹ; ו◌ֹ; ו◌ֹ; ) HEBREW LETTER VAV WITH HOLAM
+FB4C;05D1 05BF;05D1 05BF;05D1 05BF;05D1 05BF; # (בֿ; ב◌ֿ; ב◌ֿ; ב◌ֿ; ב◌ֿ; ) HEBREW LETTER BET WITH RAFE
+FB4D;05DB 05BF;05DB 05BF;05DB 05BF;05DB 05BF; # (ï­; כ◌ֿ; כ◌ֿ; כ◌ֿ; כ◌ֿ; ) HEBREW LETTER KAF WITH RAFE
+FB4E;05E4 05BF;05E4 05BF;05E4 05BF;05E4 05BF; # (פֿ; פ◌ֿ; פ◌ֿ; פ◌ֿ; פ◌ֿ; ) HEBREW LETTER PE WITH RAFE
+FB4F;FB4F;FB4F;05D0 05DC;05D0 05DC; # (ï­; ï­; ï­; ×ל; ×ל; ) HEBREW LIGATURE ALEF LAMED
+FB50;FB50;FB50;0671;0671; # (ï­; ï­; ï­; Ù±; Ù±; ) ARABIC LETTER ALEF WASLA ISOLATED FORM
+FB51;FB51;FB51;0671;0671; # (ï­‘; ï­‘; ï­‘; Ù±; Ù±; ) ARABIC LETTER ALEF WASLA FINAL FORM
+FB52;FB52;FB52;067B;067B; # (ï­’; ï­’; ï­’; Ù»; Ù»; ) ARABIC LETTER BEEH ISOLATED FORM
+FB53;FB53;FB53;067B;067B; # (ï­“; ï­“; ï­“; Ù»; Ù»; ) ARABIC LETTER BEEH FINAL FORM
+FB54;FB54;FB54;067B;067B; # (ï­”; ï­”; ï­”; Ù»; Ù»; ) ARABIC LETTER BEEH INITIAL FORM
+FB55;FB55;FB55;067B;067B; # (ï­•; ï­•; ï­•; Ù»; Ù»; ) ARABIC LETTER BEEH MEDIAL FORM
+FB56;FB56;FB56;067E;067E; # (ï­–; ï­–; ï­–; Ù¾; Ù¾; ) ARABIC LETTER PEH ISOLATED FORM
+FB57;FB57;FB57;067E;067E; # (ï­—; ï­—; ï­—; Ù¾; Ù¾; ) ARABIC LETTER PEH FINAL FORM
+FB58;FB58;FB58;067E;067E; # (ï­˜; ï­˜; ï­˜; Ù¾; Ù¾; ) ARABIC LETTER PEH INITIAL FORM
+FB59;FB59;FB59;067E;067E; # (ï­™; ï­™; ï­™; Ù¾; Ù¾; ) ARABIC LETTER PEH MEDIAL FORM
+FB5A;FB5A;FB5A;0680;0680; # (ï­š; ï­š; ï­š; Ú€; Ú€; ) ARABIC LETTER BEHEH ISOLATED FORM
+FB5B;FB5B;FB5B;0680;0680; # (ï­›; ï­›; ï­›; Ú€; Ú€; ) ARABIC LETTER BEHEH FINAL FORM
+FB5C;FB5C;FB5C;0680;0680; # (ﭜ; ﭜ; ﭜ; ڀ; ڀ; ) ARABIC LETTER BEHEH INITIAL FORM
+FB5D;FB5D;FB5D;0680;0680; # (ï­; ï­; ï­; Ú€; Ú€; ) ARABIC LETTER BEHEH MEDIAL FORM
+FB5E;FB5E;FB5E;067A;067A; # (ï­ž; ï­ž; ï­ž; Ùº; Ùº; ) ARABIC LETTER TTEHEH ISOLATED FORM
+FB5F;FB5F;FB5F;067A;067A; # (ï­Ÿ; ï­Ÿ; ï­Ÿ; Ùº; Ùº; ) ARABIC LETTER TTEHEH FINAL FORM
+FB60;FB60;FB60;067A;067A; # (ï­ ; ï­ ; ï­ ; Ùº; Ùº; ) ARABIC LETTER TTEHEH INITIAL FORM
+FB61;FB61;FB61;067A;067A; # (ï­¡; ï­¡; ï­¡; Ùº; Ùº; ) ARABIC LETTER TTEHEH MEDIAL FORM
+FB62;FB62;FB62;067F;067F; # (ï­¢; ï­¢; ï­¢; Ù¿; Ù¿; ) ARABIC LETTER TEHEH ISOLATED FORM
+FB63;FB63;FB63;067F;067F; # (ï­£; ï­£; ï­£; Ù¿; Ù¿; ) ARABIC LETTER TEHEH FINAL FORM
+FB64;FB64;FB64;067F;067F; # (ï­¤; ï­¤; ï­¤; Ù¿; Ù¿; ) ARABIC LETTER TEHEH INITIAL FORM
+FB65;FB65;FB65;067F;067F; # (ï­¥; ï­¥; ï­¥; Ù¿; Ù¿; ) ARABIC LETTER TEHEH MEDIAL FORM
+FB66;FB66;FB66;0679;0679; # (ï­¦; ï­¦; ï­¦; Ù¹; Ù¹; ) ARABIC LETTER TTEH ISOLATED FORM
+FB67;FB67;FB67;0679;0679; # (ï­§; ï­§; ï­§; Ù¹; Ù¹; ) ARABIC LETTER TTEH FINAL FORM
+FB68;FB68;FB68;0679;0679; # (ï­¨; ï­¨; ï­¨; Ù¹; Ù¹; ) ARABIC LETTER TTEH INITIAL FORM
+FB69;FB69;FB69;0679;0679; # (ï­©; ï­©; ï­©; Ù¹; Ù¹; ) ARABIC LETTER TTEH MEDIAL FORM
+FB6A;FB6A;FB6A;06A4;06A4; # (ï­ª; ï­ª; ï­ª; Ú¤; Ú¤; ) ARABIC LETTER VEH ISOLATED FORM
+FB6B;FB6B;FB6B;06A4;06A4; # (ï­«; ï­«; ï­«; Ú¤; Ú¤; ) ARABIC LETTER VEH FINAL FORM
+FB6C;FB6C;FB6C;06A4;06A4; # (ï­¬; ï­¬; ï­¬; Ú¤; Ú¤; ) ARABIC LETTER VEH INITIAL FORM
+FB6D;FB6D;FB6D;06A4;06A4; # (ï­­; ï­­; ï­­; Ú¤; Ú¤; ) ARABIC LETTER VEH MEDIAL FORM
+FB6E;FB6E;FB6E;06A6;06A6; # (ï­®; ï­®; ï­®; Ú¦; Ú¦; ) ARABIC LETTER PEHEH ISOLATED FORM
+FB6F;FB6F;FB6F;06A6;06A6; # (ï­¯; ï­¯; ï­¯; Ú¦; Ú¦; ) ARABIC LETTER PEHEH FINAL FORM
+FB70;FB70;FB70;06A6;06A6; # (ï­°; ï­°; ï­°; Ú¦; Ú¦; ) ARABIC LETTER PEHEH INITIAL FORM
+FB71;FB71;FB71;06A6;06A6; # (ï­±; ï­±; ï­±; Ú¦; Ú¦; ) ARABIC LETTER PEHEH MEDIAL FORM
+FB72;FB72;FB72;0684;0684; # (ï­²; ï­²; ï­²; Ú„; Ú„; ) ARABIC LETTER DYEH ISOLATED FORM
+FB73;FB73;FB73;0684;0684; # (ï­³; ï­³; ï­³; Ú„; Ú„; ) ARABIC LETTER DYEH FINAL FORM
+FB74;FB74;FB74;0684;0684; # (ï­´; ï­´; ï­´; Ú„; Ú„; ) ARABIC LETTER DYEH INITIAL FORM
+FB75;FB75;FB75;0684;0684; # (ï­µ; ï­µ; ï­µ; Ú„; Ú„; ) ARABIC LETTER DYEH MEDIAL FORM
+FB76;FB76;FB76;0683;0683; # (ï­¶; ï­¶; ï­¶; Úƒ; Úƒ; ) ARABIC LETTER NYEH ISOLATED FORM
+FB77;FB77;FB77;0683;0683; # (ï­·; ï­·; ï­·; Úƒ; Úƒ; ) ARABIC LETTER NYEH FINAL FORM
+FB78;FB78;FB78;0683;0683; # (ï­¸; ï­¸; ï­¸; Úƒ; Úƒ; ) ARABIC LETTER NYEH INITIAL FORM
+FB79;FB79;FB79;0683;0683; # (ï­¹; ï­¹; ï­¹; Úƒ; Úƒ; ) ARABIC LETTER NYEH MEDIAL FORM
+FB7A;FB7A;FB7A;0686;0686; # (ï­º; ï­º; ï­º; Ú†; Ú†; ) ARABIC LETTER TCHEH ISOLATED FORM
+FB7B;FB7B;FB7B;0686;0686; # (ï­»; ï­»; ï­»; Ú†; Ú†; ) ARABIC LETTER TCHEH FINAL FORM
+FB7C;FB7C;FB7C;0686;0686; # (ï­¼; ï­¼; ï­¼; Ú†; Ú†; ) ARABIC LETTER TCHEH INITIAL FORM
+FB7D;FB7D;FB7D;0686;0686; # (ï­½; ï­½; ï­½; Ú†; Ú†; ) ARABIC LETTER TCHEH MEDIAL FORM
+FB7E;FB7E;FB7E;0687;0687; # (ï­¾; ï­¾; ï­¾; Ú‡; Ú‡; ) ARABIC LETTER TCHEHEH ISOLATED FORM
+FB7F;FB7F;FB7F;0687;0687; # (ï­¿; ï­¿; ï­¿; Ú‡; Ú‡; ) ARABIC LETTER TCHEHEH FINAL FORM
+FB80;FB80;FB80;0687;0687; # (ﮀ; ﮀ; ﮀ; ڇ; ڇ; ) ARABIC LETTER TCHEHEH INITIAL FORM
+FB81;FB81;FB81;0687;0687; # (ï®; ï®; ï®; Ú‡; Ú‡; ) ARABIC LETTER TCHEHEH MEDIAL FORM
+FB82;FB82;FB82;068D;068D; # (ﮂ; ﮂ; ﮂ; Ú; Ú; ) ARABIC LETTER DDAHAL ISOLATED FORM
+FB83;FB83;FB83;068D;068D; # (ﮃ; ﮃ; ﮃ; Ú; Ú; ) ARABIC LETTER DDAHAL FINAL FORM
+FB84;FB84;FB84;068C;068C; # (ﮄ; ﮄ; ﮄ; ڌ; ڌ; ) ARABIC LETTER DAHAL ISOLATED FORM
+FB85;FB85;FB85;068C;068C; # (ﮅ; ﮅ; ﮅ; ڌ; ڌ; ) ARABIC LETTER DAHAL FINAL FORM
+FB86;FB86;FB86;068E;068E; # (ﮆ; ﮆ; ﮆ; ڎ; ڎ; ) ARABIC LETTER DUL ISOLATED FORM
+FB87;FB87;FB87;068E;068E; # (ﮇ; ﮇ; ﮇ; ڎ; ڎ; ) ARABIC LETTER DUL FINAL FORM
+FB88;FB88;FB88;0688;0688; # (ﮈ; ﮈ; ﮈ; ڈ; ڈ; ) ARABIC LETTER DDAL ISOLATED FORM
+FB89;FB89;FB89;0688;0688; # (ﮉ; ﮉ; ﮉ; ڈ; ڈ; ) ARABIC LETTER DDAL FINAL FORM
+FB8A;FB8A;FB8A;0698;0698; # (ﮊ; ﮊ; ﮊ; ژ; ژ; ) ARABIC LETTER JEH ISOLATED FORM
+FB8B;FB8B;FB8B;0698;0698; # (ﮋ; ﮋ; ﮋ; ژ; ژ; ) ARABIC LETTER JEH FINAL FORM
+FB8C;FB8C;FB8C;0691;0691; # (ﮌ; ﮌ; ﮌ; ڑ; ڑ; ) ARABIC LETTER RREH ISOLATED FORM
+FB8D;FB8D;FB8D;0691;0691; # (ï®; ï®; ï®; Ú‘; Ú‘; ) ARABIC LETTER RREH FINAL FORM
+FB8E;FB8E;FB8E;06A9;06A9; # (ﮎ; ﮎ; ﮎ; ک; ک; ) ARABIC LETTER KEHEH ISOLATED FORM
+FB8F;FB8F;FB8F;06A9;06A9; # (ï®; ï®; ï®; Ú©; Ú©; ) ARABIC LETTER KEHEH FINAL FORM
+FB90;FB90;FB90;06A9;06A9; # (ï®; ï®; ï®; Ú©; Ú©; ) ARABIC LETTER KEHEH INITIAL FORM
+FB91;FB91;FB91;06A9;06A9; # (ﮑ; ﮑ; ﮑ; ک; ک; ) ARABIC LETTER KEHEH MEDIAL FORM
+FB92;FB92;FB92;06AF;06AF; # (ï®’; ï®’; ï®’; Ú¯; Ú¯; ) ARABIC LETTER GAF ISOLATED FORM
+FB93;FB93;FB93;06AF;06AF; # (ﮓ; ﮓ; ﮓ; گ; گ; ) ARABIC LETTER GAF FINAL FORM
+FB94;FB94;FB94;06AF;06AF; # (ï®”; ï®”; ï®”; Ú¯; Ú¯; ) ARABIC LETTER GAF INITIAL FORM
+FB95;FB95;FB95;06AF;06AF; # (ﮕ; ﮕ; ﮕ; گ; گ; ) ARABIC LETTER GAF MEDIAL FORM
+FB96;FB96;FB96;06B3;06B3; # (ï®–; ï®–; ï®–; Ú³; Ú³; ) ARABIC LETTER GUEH ISOLATED FORM
+FB97;FB97;FB97;06B3;06B3; # (ï®—; ï®—; ï®—; Ú³; Ú³; ) ARABIC LETTER GUEH FINAL FORM
+FB98;FB98;FB98;06B3;06B3; # (ﮘ; ﮘ; ﮘ; ڳ; ڳ; ) ARABIC LETTER GUEH INITIAL FORM
+FB99;FB99;FB99;06B3;06B3; # (ï®™; ï®™; ï®™; Ú³; Ú³; ) ARABIC LETTER GUEH MEDIAL FORM
+FB9A;FB9A;FB9A;06B1;06B1; # (ﮚ; ﮚ; ﮚ; ڱ; ڱ; ) ARABIC LETTER NGOEH ISOLATED FORM
+FB9B;FB9B;FB9B;06B1;06B1; # (ï®›; ï®›; ï®›; Ú±; Ú±; ) ARABIC LETTER NGOEH FINAL FORM
+FB9C;FB9C;FB9C;06B1;06B1; # (ﮜ; ﮜ; ﮜ; ڱ; ڱ; ) ARABIC LETTER NGOEH INITIAL FORM
+FB9D;FB9D;FB9D;06B1;06B1; # (ï®; ï®; ï®; Ú±; Ú±; ) ARABIC LETTER NGOEH MEDIAL FORM
+FB9E;FB9E;FB9E;06BA;06BA; # (ﮞ; ﮞ; ﮞ; ں; ں; ) ARABIC LETTER NOON GHUNNA ISOLATED FORM
+FB9F;FB9F;FB9F;06BA;06BA; # (ﮟ; ﮟ; ﮟ; ں; ں; ) ARABIC LETTER NOON GHUNNA FINAL FORM
+FBA0;FBA0;FBA0;06BB;06BB; # (ï® ; ï® ; ï® ; Ú»; Ú»; ) ARABIC LETTER RNOON ISOLATED FORM
+FBA1;FBA1;FBA1;06BB;06BB; # (ﮡ; ﮡ; ﮡ; ڻ; ڻ; ) ARABIC LETTER RNOON FINAL FORM
+FBA2;FBA2;FBA2;06BB;06BB; # (ﮢ; ﮢ; ﮢ; ڻ; ڻ; ) ARABIC LETTER RNOON INITIAL FORM
+FBA3;FBA3;FBA3;06BB;06BB; # (ﮣ; ﮣ; ﮣ; ڻ; ڻ; ) ARABIC LETTER RNOON MEDIAL FORM
+FBA4;FBA4;FBA4;06C0;06D5 0654; # (ﮤ; ﮤ; ﮤ; ۀ; ە◌ٔ; ) ARABIC LETTER HEH WITH YEH ABOVE ISOLATED FORM
+FBA5;FBA5;FBA5;06C0;06D5 0654; # (ﮥ; ﮥ; ﮥ; ۀ; ە◌ٔ; ) ARABIC LETTER HEH WITH YEH ABOVE FINAL FORM
+FBA6;FBA6;FBA6;06C1;06C1; # (ﮦ; ﮦ; ﮦ; Û; Û; ) ARABIC LETTER HEH GOAL ISOLATED FORM
+FBA7;FBA7;FBA7;06C1;06C1; # (ï®§; ï®§; ï®§; Û; Û; ) ARABIC LETTER HEH GOAL FINAL FORM
+FBA8;FBA8;FBA8;06C1;06C1; # (ﮨ; ﮨ; ﮨ; Û; Û; ) ARABIC LETTER HEH GOAL INITIAL FORM
+FBA9;FBA9;FBA9;06C1;06C1; # (ﮩ; ﮩ; ﮩ; Û; Û; ) ARABIC LETTER HEH GOAL MEDIAL FORM
+FBAA;FBAA;FBAA;06BE;06BE; # (ﮪ; ﮪ; ﮪ; ھ; ھ; ) ARABIC LETTER HEH DOACHASHMEE ISOLATED FORM
+FBAB;FBAB;FBAB;06BE;06BE; # (ﮫ; ﮫ; ﮫ; ھ; ھ; ) ARABIC LETTER HEH DOACHASHMEE FINAL FORM
+FBAC;FBAC;FBAC;06BE;06BE; # (ﮬ; ﮬ; ﮬ; ھ; ھ; ) ARABIC LETTER HEH DOACHASHMEE INITIAL FORM
+FBAD;FBAD;FBAD;06BE;06BE; # (ï®­; ï®­; ï®­; Ú¾; Ú¾; ) ARABIC LETTER HEH DOACHASHMEE MEDIAL FORM
+FBAE;FBAE;FBAE;06D2;06D2; # (ï®®; ï®®; ï®®; Û’; Û’; ) ARABIC LETTER YEH BARREE ISOLATED FORM
+FBAF;FBAF;FBAF;06D2;06D2; # (ﮯ; ﮯ; ﮯ; ے; ے; ) ARABIC LETTER YEH BARREE FINAL FORM
+FBB0;FBB0;FBB0;06D3;06D2 0654; # (ﮰ; ﮰ; ﮰ; ۓ; ے◌ٔ; ) ARABIC LETTER YEH BARREE WITH HAMZA ABOVE ISOLATED FORM
+FBB1;FBB1;FBB1;06D3;06D2 0654; # (ﮱ; ﮱ; ﮱ; ۓ; ے◌ٔ; ) ARABIC LETTER YEH BARREE WITH HAMZA ABOVE FINAL FORM
+FBD3;FBD3;FBD3;06AD;06AD; # (ﯓ; ﯓ; ﯓ; ڭ; ڭ; ) ARABIC LETTER NG ISOLATED FORM
+FBD4;FBD4;FBD4;06AD;06AD; # (ﯔ; ﯔ; ﯔ; ڭ; ڭ; ) ARABIC LETTER NG FINAL FORM
+FBD5;FBD5;FBD5;06AD;06AD; # (ﯕ; ﯕ; ﯕ; ڭ; ڭ; ) ARABIC LETTER NG INITIAL FORM
+FBD6;FBD6;FBD6;06AD;06AD; # (ﯖ; ﯖ; ﯖ; ڭ; ڭ; ) ARABIC LETTER NG MEDIAL FORM
+FBD7;FBD7;FBD7;06C7;06C7; # (ﯗ; ﯗ; ﯗ; ۇ; ۇ; ) ARABIC LETTER U ISOLATED FORM
+FBD8;FBD8;FBD8;06C7;06C7; # (ﯘ; ﯘ; ﯘ; ۇ; ۇ; ) ARABIC LETTER U FINAL FORM
+FBD9;FBD9;FBD9;06C6;06C6; # (ﯙ; ﯙ; ﯙ; ۆ; ۆ; ) ARABIC LETTER OE ISOLATED FORM
+FBDA;FBDA;FBDA;06C6;06C6; # (ﯚ; ﯚ; ﯚ; ۆ; ۆ; ) ARABIC LETTER OE FINAL FORM
+FBDB;FBDB;FBDB;06C8;06C8; # (ﯛ; ﯛ; ﯛ; ۈ; ۈ; ) ARABIC LETTER YU ISOLATED FORM
+FBDC;FBDC;FBDC;06C8;06C8; # (ﯜ; ﯜ; ﯜ; ۈ; ۈ; ) ARABIC LETTER YU FINAL FORM
+FBDD;FBDD;FBDD;06C7 0674;06C7 0674; # (ï¯; ï¯; ï¯; Û‡Ù´; Û‡Ù´; ) ARABIC LETTER U WITH HAMZA ABOVE ISOLATED FORM
+FBDE;FBDE;FBDE;06CB;06CB; # (ﯞ; ﯞ; ﯞ; ۋ; ۋ; ) ARABIC LETTER VE ISOLATED FORM
+FBDF;FBDF;FBDF;06CB;06CB; # (ﯟ; ﯟ; ﯟ; ۋ; ۋ; ) ARABIC LETTER VE FINAL FORM
+FBE0;FBE0;FBE0;06C5;06C5; # (ﯠ; ﯠ; ﯠ; ۅ; ۅ; ) ARABIC LETTER KIRGHIZ OE ISOLATED FORM
+FBE1;FBE1;FBE1;06C5;06C5; # (ﯡ; ﯡ; ﯡ; ۅ; ۅ; ) ARABIC LETTER KIRGHIZ OE FINAL FORM
+FBE2;FBE2;FBE2;06C9;06C9; # (ﯢ; ﯢ; ﯢ; ۉ; ۉ; ) ARABIC LETTER KIRGHIZ YU ISOLATED FORM
+FBE3;FBE3;FBE3;06C9;06C9; # (ﯣ; ﯣ; ﯣ; ۉ; ۉ; ) ARABIC LETTER KIRGHIZ YU FINAL FORM
+FBE4;FBE4;FBE4;06D0;06D0; # (ﯤ; ﯤ; ﯤ; Û; Û; ) ARABIC LETTER E ISOLATED FORM
+FBE5;FBE5;FBE5;06D0;06D0; # (ﯥ; ﯥ; ﯥ; Û; Û; ) ARABIC LETTER E FINAL FORM
+FBE6;FBE6;FBE6;06D0;06D0; # (ﯦ; ﯦ; ﯦ; Û; Û; ) ARABIC LETTER E INITIAL FORM
+FBE7;FBE7;FBE7;06D0;06D0; # (ﯧ; ﯧ; ﯧ; Û; Û; ) ARABIC LETTER E MEDIAL FORM
+FBE8;FBE8;FBE8;0649;0649; # (ﯨ; ﯨ; ﯨ; ى; ى; ) ARABIC LETTER UIGHUR KAZAKH KIRGHIZ ALEF MAKSURA INITIAL FORM
+FBE9;FBE9;FBE9;0649;0649; # (ﯩ; ﯩ; ﯩ; ى; ى; ) ARABIC LETTER UIGHUR KAZAKH KIRGHIZ ALEF MAKSURA MEDIAL FORM
+FBEA;FBEA;FBEA;0626 0627;064A 0654 0627; # (ﯪ; ﯪ; ﯪ; ئا; ي◌ٔا; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF ISOLATED FORM
+FBEB;FBEB;FBEB;0626 0627;064A 0654 0627; # (ﯫ; ﯫ; ﯫ; ئا; ي◌ٔا; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF FINAL FORM
+FBEC;FBEC;FBEC;0626 06D5;064A 0654 06D5; # (ﯬ; ﯬ; ﯬ; ئە; ي◌ٔە; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH AE ISOLATED FORM
+FBED;FBED;FBED;0626 06D5;064A 0654 06D5; # (ﯭ; ﯭ; ﯭ; ئە; ي◌ٔە; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH AE FINAL FORM
+FBEE;FBEE;FBEE;0626 0648;064A 0654 0648; # (ﯮ; ﯮ; ﯮ; ئو; ي◌ٔو; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH WAW ISOLATED FORM
+FBEF;FBEF;FBEF;0626 0648;064A 0654 0648; # (ﯯ; ﯯ; ﯯ; ئو; ي◌ٔو; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH WAW FINAL FORM
+FBF0;FBF0;FBF0;0626 06C7;064A 0654 06C7; # (ﯰ; ﯰ; ﯰ; ئۇ; ي◌ٔۇ; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH U ISOLATED FORM
+FBF1;FBF1;FBF1;0626 06C7;064A 0654 06C7; # (ﯱ; ﯱ; ﯱ; ئۇ; ي◌ٔۇ; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH U FINAL FORM
+FBF2;FBF2;FBF2;0626 06C6;064A 0654 06C6; # (ﯲ; ﯲ; ﯲ; ئۆ; ي◌ٔۆ; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH OE ISOLATED FORM
+FBF3;FBF3;FBF3;0626 06C6;064A 0654 06C6; # (ﯳ; ﯳ; ﯳ; ئۆ; ي◌ٔۆ; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH OE FINAL FORM
+FBF4;FBF4;FBF4;0626 06C8;064A 0654 06C8; # (ﯴ; ﯴ; ﯴ; ئۈ; ي◌ٔۈ; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH YU ISOLATED FORM
+FBF5;FBF5;FBF5;0626 06C8;064A 0654 06C8; # (ﯵ; ﯵ; ﯵ; ئۈ; ي◌ٔۈ; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH YU FINAL FORM
+FBF6;FBF6;FBF6;0626 06D0;064A 0654 06D0; # (ﯶ; ﯶ; ﯶ; ئÛ; ي◌ٔÛ; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH E ISOLATED FORM
+FBF7;FBF7;FBF7;0626 06D0;064A 0654 06D0; # (ﯷ; ﯷ; ﯷ; ئÛ; ي◌ٔÛ; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH E FINAL FORM
+FBF8;FBF8;FBF8;0626 06D0;064A 0654 06D0; # (ﯸ; ﯸ; ﯸ; ئÛ; ي◌ٔÛ; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH E INITIAL FORM
+FBF9;FBF9;FBF9;0626 0649;064A 0654 0649; # (ﯹ; ﯹ; ﯹ; ئى; ي◌ٔى; ) ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH HAMZA ABOVE WITH ALEF MAKSURA ISOLATED FORM
+FBFA;FBFA;FBFA;0626 0649;064A 0654 0649; # (ﯺ; ﯺ; ﯺ; ئى; ي◌ٔى; ) ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH HAMZA ABOVE WITH ALEF MAKSURA FINAL FORM
+FBFB;FBFB;FBFB;0626 0649;064A 0654 0649; # (ﯻ; ﯻ; ﯻ; ئى; ي◌ٔى; ) ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH HAMZA ABOVE WITH ALEF MAKSURA INITIAL FORM
+FBFC;FBFC;FBFC;06CC;06CC; # (ﯼ; ﯼ; ﯼ; ی; ی; ) ARABIC LETTER FARSI YEH ISOLATED FORM
+FBFD;FBFD;FBFD;06CC;06CC; # (ﯽ; ﯽ; ﯽ; ی; ی; ) ARABIC LETTER FARSI YEH FINAL FORM
+FBFE;FBFE;FBFE;06CC;06CC; # (ﯾ; ﯾ; ﯾ; ی; ی; ) ARABIC LETTER FARSI YEH INITIAL FORM
+FBFF;FBFF;FBFF;06CC;06CC; # (ﯿ; ﯿ; ﯿ; ی; ی; ) ARABIC LETTER FARSI YEH MEDIAL FORM
+FC00;FC00;FC00;0626 062C;064A 0654 062C; # (ﰀ; ﰀ; ﰀ; ئج; ي◌ٔج; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH JEEM ISOLATED FORM
+FC01;FC01;FC01;0626 062D;064A 0654 062D; # (ï°; ï°; ï°; ئح; ي◌ٔح; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH HAH ISOLATED FORM
+FC02;FC02;FC02;0626 0645;064A 0654 0645; # (ﰂ; ﰂ; ﰂ; ئم; ي◌ٔم; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH MEEM ISOLATED FORM
+FC03;FC03;FC03;0626 0649;064A 0654 0649; # (ﰃ; ﰃ; ﰃ; ئى; ي◌ٔى; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF MAKSURA ISOLATED FORM
+FC04;FC04;FC04;0626 064A;064A 0654 064A; # (ﰄ; ﰄ; ﰄ; ئي; ي◌ٔي; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH YEH ISOLATED FORM
+FC05;FC05;FC05;0628 062C;0628 062C; # (ﰅ; ﰅ; ﰅ; بج; بج; ) ARABIC LIGATURE BEH WITH JEEM ISOLATED FORM
+FC06;FC06;FC06;0628 062D;0628 062D; # (ﰆ; ﰆ; ﰆ; بح; بح; ) ARABIC LIGATURE BEH WITH HAH ISOLATED FORM
+FC07;FC07;FC07;0628 062E;0628 062E; # (ﰇ; ﰇ; ﰇ; بخ; بخ; ) ARABIC LIGATURE BEH WITH KHAH ISOLATED FORM
+FC08;FC08;FC08;0628 0645;0628 0645; # (ﰈ; ﰈ; ﰈ; بم; بم; ) ARABIC LIGATURE BEH WITH MEEM ISOLATED FORM
+FC09;FC09;FC09;0628 0649;0628 0649; # (ﰉ; ﰉ; ﰉ; بى; بى; ) ARABIC LIGATURE BEH WITH ALEF MAKSURA ISOLATED FORM
+FC0A;FC0A;FC0A;0628 064A;0628 064A; # (ﰊ; ﰊ; ﰊ; بي; بي; ) ARABIC LIGATURE BEH WITH YEH ISOLATED FORM
+FC0B;FC0B;FC0B;062A 062C;062A 062C; # (ﰋ; ﰋ; ﰋ; تج; تج; ) ARABIC LIGATURE TEH WITH JEEM ISOLATED FORM
+FC0C;FC0C;FC0C;062A 062D;062A 062D; # (ﰌ; ﰌ; ﰌ; تح; تح; ) ARABIC LIGATURE TEH WITH HAH ISOLATED FORM
+FC0D;FC0D;FC0D;062A 062E;062A 062E; # (ï°; ï°; ï°; تخ; تخ; ) ARABIC LIGATURE TEH WITH KHAH ISOLATED FORM
+FC0E;FC0E;FC0E;062A 0645;062A 0645; # (ﰎ; ﰎ; ﰎ; تم; تم; ) ARABIC LIGATURE TEH WITH MEEM ISOLATED FORM
+FC0F;FC0F;FC0F;062A 0649;062A 0649; # (ï°; ï°; ï°; تى; تى; ) ARABIC LIGATURE TEH WITH ALEF MAKSURA ISOLATED FORM
+FC10;FC10;FC10;062A 064A;062A 064A; # (ï°; ï°; ï°; تي; تي; ) ARABIC LIGATURE TEH WITH YEH ISOLATED FORM
+FC11;FC11;FC11;062B 062C;062B 062C; # (ﰑ; ﰑ; ﰑ; ثج; ثج; ) ARABIC LIGATURE THEH WITH JEEM ISOLATED FORM
+FC12;FC12;FC12;062B 0645;062B 0645; # (ﰒ; ﰒ; ﰒ; ثم; ثم; ) ARABIC LIGATURE THEH WITH MEEM ISOLATED FORM
+FC13;FC13;FC13;062B 0649;062B 0649; # (ﰓ; ﰓ; ﰓ; ثى; ثى; ) ARABIC LIGATURE THEH WITH ALEF MAKSURA ISOLATED FORM
+FC14;FC14;FC14;062B 064A;062B 064A; # (ﰔ; ﰔ; ﰔ; ثي; ثي; ) ARABIC LIGATURE THEH WITH YEH ISOLATED FORM
+FC15;FC15;FC15;062C 062D;062C 062D; # (ﰕ; ﰕ; ﰕ; جح; جح; ) ARABIC LIGATURE JEEM WITH HAH ISOLATED FORM
+FC16;FC16;FC16;062C 0645;062C 0645; # (ﰖ; ﰖ; ﰖ; جم; جم; ) ARABIC LIGATURE JEEM WITH MEEM ISOLATED FORM
+FC17;FC17;FC17;062D 062C;062D 062C; # (ﰗ; ﰗ; ﰗ; حج; حج; ) ARABIC LIGATURE HAH WITH JEEM ISOLATED FORM
+FC18;FC18;FC18;062D 0645;062D 0645; # (ﰘ; ﰘ; ﰘ; حم; حم; ) ARABIC LIGATURE HAH WITH MEEM ISOLATED FORM
+FC19;FC19;FC19;062E 062C;062E 062C; # (ﰙ; ﰙ; ﰙ; خج; خج; ) ARABIC LIGATURE KHAH WITH JEEM ISOLATED FORM
+FC1A;FC1A;FC1A;062E 062D;062E 062D; # (ﰚ; ﰚ; ﰚ; خح; خح; ) ARABIC LIGATURE KHAH WITH HAH ISOLATED FORM
+FC1B;FC1B;FC1B;062E 0645;062E 0645; # (ﰛ; ﰛ; ﰛ; خم; خم; ) ARABIC LIGATURE KHAH WITH MEEM ISOLATED FORM
+FC1C;FC1C;FC1C;0633 062C;0633 062C; # (ﰜ; ﰜ; ﰜ; سج; سج; ) ARABIC LIGATURE SEEN WITH JEEM ISOLATED FORM
+FC1D;FC1D;FC1D;0633 062D;0633 062D; # (ï°; ï°; ï°; سح; سح; ) ARABIC LIGATURE SEEN WITH HAH ISOLATED FORM
+FC1E;FC1E;FC1E;0633 062E;0633 062E; # (ﰞ; ﰞ; ﰞ; سخ; سخ; ) ARABIC LIGATURE SEEN WITH KHAH ISOLATED FORM
+FC1F;FC1F;FC1F;0633 0645;0633 0645; # (ﰟ; ﰟ; ﰟ; سم; سم; ) ARABIC LIGATURE SEEN WITH MEEM ISOLATED FORM
+FC20;FC20;FC20;0635 062D;0635 062D; # (ﰠ; ﰠ; ﰠ; صح; صح; ) ARABIC LIGATURE SAD WITH HAH ISOLATED FORM
+FC21;FC21;FC21;0635 0645;0635 0645; # (ﰡ; ﰡ; ﰡ; صم; صم; ) ARABIC LIGATURE SAD WITH MEEM ISOLATED FORM
+FC22;FC22;FC22;0636 062C;0636 062C; # (ﰢ; ﰢ; ﰢ; ضج; ضج; ) ARABIC LIGATURE DAD WITH JEEM ISOLATED FORM
+FC23;FC23;FC23;0636 062D;0636 062D; # (ﰣ; ﰣ; ﰣ; ضح; ضح; ) ARABIC LIGATURE DAD WITH HAH ISOLATED FORM
+FC24;FC24;FC24;0636 062E;0636 062E; # (ﰤ; ﰤ; ﰤ; ضخ; ضخ; ) ARABIC LIGATURE DAD WITH KHAH ISOLATED FORM
+FC25;FC25;FC25;0636 0645;0636 0645; # (ﰥ; ﰥ; ﰥ; ضم; ضم; ) ARABIC LIGATURE DAD WITH MEEM ISOLATED FORM
+FC26;FC26;FC26;0637 062D;0637 062D; # (ﰦ; ﰦ; ﰦ; طح; طح; ) ARABIC LIGATURE TAH WITH HAH ISOLATED FORM
+FC27;FC27;FC27;0637 0645;0637 0645; # (ﰧ; ﰧ; ﰧ; طم; طم; ) ARABIC LIGATURE TAH WITH MEEM ISOLATED FORM
+FC28;FC28;FC28;0638 0645;0638 0645; # (ﰨ; ﰨ; ﰨ; ظم; ظم; ) ARABIC LIGATURE ZAH WITH MEEM ISOLATED FORM
+FC29;FC29;FC29;0639 062C;0639 062C; # (ﰩ; ﰩ; ﰩ; عج; عج; ) ARABIC LIGATURE AIN WITH JEEM ISOLATED FORM
+FC2A;FC2A;FC2A;0639 0645;0639 0645; # (ﰪ; ﰪ; ﰪ; عم; عم; ) ARABIC LIGATURE AIN WITH MEEM ISOLATED FORM
+FC2B;FC2B;FC2B;063A 062C;063A 062C; # (ﰫ; ﰫ; ﰫ; غج; غج; ) ARABIC LIGATURE GHAIN WITH JEEM ISOLATED FORM
+FC2C;FC2C;FC2C;063A 0645;063A 0645; # (ﰬ; ﰬ; ﰬ; غم; غم; ) ARABIC LIGATURE GHAIN WITH MEEM ISOLATED FORM
+FC2D;FC2D;FC2D;0641 062C;0641 062C; # (ï°­; ï°­; ï°­; ÙØ¬; ÙØ¬; ) ARABIC LIGATURE FEH WITH JEEM ISOLATED FORM
+FC2E;FC2E;FC2E;0641 062D;0641 062D; # (ï°®; ï°®; ï°®; ÙØ­; ÙØ­; ) ARABIC LIGATURE FEH WITH HAH ISOLATED FORM
+FC2F;FC2F;FC2F;0641 062E;0641 062E; # (ï°¯; ï°¯; ï°¯; ÙØ®; ÙØ®; ) ARABIC LIGATURE FEH WITH KHAH ISOLATED FORM
+FC30;FC30;FC30;0641 0645;0641 0645; # (ï°°; ï°°; ï°°; ÙÙ…; ÙÙ…; ) ARABIC LIGATURE FEH WITH MEEM ISOLATED FORM
+FC31;FC31;FC31;0641 0649;0641 0649; # (ï°±; ï°±; ï°±; ÙÙ‰; ÙÙ‰; ) ARABIC LIGATURE FEH WITH ALEF MAKSURA ISOLATED FORM
+FC32;FC32;FC32;0641 064A;0641 064A; # (ï°²; ï°²; ï°²; ÙÙŠ; ÙÙŠ; ) ARABIC LIGATURE FEH WITH YEH ISOLATED FORM
+FC33;FC33;FC33;0642 062D;0642 062D; # (ﰳ; ﰳ; ﰳ; قح; قح; ) ARABIC LIGATURE QAF WITH HAH ISOLATED FORM
+FC34;FC34;FC34;0642 0645;0642 0645; # (ﰴ; ﰴ; ﰴ; قم; قم; ) ARABIC LIGATURE QAF WITH MEEM ISOLATED FORM
+FC35;FC35;FC35;0642 0649;0642 0649; # (ﰵ; ﰵ; ﰵ; قى; قى; ) ARABIC LIGATURE QAF WITH ALEF MAKSURA ISOLATED FORM
+FC36;FC36;FC36;0642 064A;0642 064A; # (ﰶ; ﰶ; ﰶ; قي; قي; ) ARABIC LIGATURE QAF WITH YEH ISOLATED FORM
+FC37;FC37;FC37;0643 0627;0643 0627; # (ﰷ; ﰷ; ﰷ; كا; كا; ) ARABIC LIGATURE KAF WITH ALEF ISOLATED FORM
+FC38;FC38;FC38;0643 062C;0643 062C; # (ﰸ; ﰸ; ﰸ; كج; كج; ) ARABIC LIGATURE KAF WITH JEEM ISOLATED FORM
+FC39;FC39;FC39;0643 062D;0643 062D; # (ﰹ; ﰹ; ﰹ; كح; كح; ) ARABIC LIGATURE KAF WITH HAH ISOLATED FORM
+FC3A;FC3A;FC3A;0643 062E;0643 062E; # (ﰺ; ﰺ; ﰺ; كخ; كخ; ) ARABIC LIGATURE KAF WITH KHAH ISOLATED FORM
+FC3B;FC3B;FC3B;0643 0644;0643 0644; # (ﰻ; ﰻ; ﰻ; كل; كل; ) ARABIC LIGATURE KAF WITH LAM ISOLATED FORM
+FC3C;FC3C;FC3C;0643 0645;0643 0645; # (ﰼ; ﰼ; ﰼ; كم; كم; ) ARABIC LIGATURE KAF WITH MEEM ISOLATED FORM
+FC3D;FC3D;FC3D;0643 0649;0643 0649; # (ﰽ; ﰽ; ﰽ; كى; كى; ) ARABIC LIGATURE KAF WITH ALEF MAKSURA ISOLATED FORM
+FC3E;FC3E;FC3E;0643 064A;0643 064A; # (ﰾ; ﰾ; ﰾ; كي; كي; ) ARABIC LIGATURE KAF WITH YEH ISOLATED FORM
+FC3F;FC3F;FC3F;0644 062C;0644 062C; # (ﰿ; ﰿ; ﰿ; لج; لج; ) ARABIC LIGATURE LAM WITH JEEM ISOLATED FORM
+FC40;FC40;FC40;0644 062D;0644 062D; # (ﱀ; ﱀ; ﱀ; لح; لح; ) ARABIC LIGATURE LAM WITH HAH ISOLATED FORM
+FC41;FC41;FC41;0644 062E;0644 062E; # (ï±; ï±; ï±; لخ; لخ; ) ARABIC LIGATURE LAM WITH KHAH ISOLATED FORM
+FC42;FC42;FC42;0644 0645;0644 0645; # (ﱂ; ﱂ; ﱂ; لم; لم; ) ARABIC LIGATURE LAM WITH MEEM ISOLATED FORM
+FC43;FC43;FC43;0644 0649;0644 0649; # (ﱃ; ﱃ; ﱃ; لى; لى; ) ARABIC LIGATURE LAM WITH ALEF MAKSURA ISOLATED FORM
+FC44;FC44;FC44;0644 064A;0644 064A; # (ﱄ; ﱄ; ﱄ; لي; لي; ) ARABIC LIGATURE LAM WITH YEH ISOLATED FORM
+FC45;FC45;FC45;0645 062C;0645 062C; # (ﱅ; ﱅ; ﱅ; مج; مج; ) ARABIC LIGATURE MEEM WITH JEEM ISOLATED FORM
+FC46;FC46;FC46;0645 062D;0645 062D; # (ﱆ; ﱆ; ﱆ; مح; مح; ) ARABIC LIGATURE MEEM WITH HAH ISOLATED FORM
+FC47;FC47;FC47;0645 062E;0645 062E; # (ﱇ; ﱇ; ﱇ; مخ; مخ; ) ARABIC LIGATURE MEEM WITH KHAH ISOLATED FORM
+FC48;FC48;FC48;0645 0645;0645 0645; # (ﱈ; ﱈ; ﱈ; مم; مم; ) ARABIC LIGATURE MEEM WITH MEEM ISOLATED FORM
+FC49;FC49;FC49;0645 0649;0645 0649; # (ﱉ; ﱉ; ﱉ; مى; مى; ) ARABIC LIGATURE MEEM WITH ALEF MAKSURA ISOLATED FORM
+FC4A;FC4A;FC4A;0645 064A;0645 064A; # (ﱊ; ﱊ; ﱊ; مي; مي; ) ARABIC LIGATURE MEEM WITH YEH ISOLATED FORM
+FC4B;FC4B;FC4B;0646 062C;0646 062C; # (ﱋ; ﱋ; ﱋ; نج; نج; ) ARABIC LIGATURE NOON WITH JEEM ISOLATED FORM
+FC4C;FC4C;FC4C;0646 062D;0646 062D; # (ﱌ; ﱌ; ﱌ; نح; نح; ) ARABIC LIGATURE NOON WITH HAH ISOLATED FORM
+FC4D;FC4D;FC4D;0646 062E;0646 062E; # (ï±; ï±; ï±; نخ; نخ; ) ARABIC LIGATURE NOON WITH KHAH ISOLATED FORM
+FC4E;FC4E;FC4E;0646 0645;0646 0645; # (ﱎ; ﱎ; ﱎ; نم; نم; ) ARABIC LIGATURE NOON WITH MEEM ISOLATED FORM
+FC4F;FC4F;FC4F;0646 0649;0646 0649; # (ï±; ï±; ï±; نى; نى; ) ARABIC LIGATURE NOON WITH ALEF MAKSURA ISOLATED FORM
+FC50;FC50;FC50;0646 064A;0646 064A; # (ï±; ï±; ï±; ني; ني; ) ARABIC LIGATURE NOON WITH YEH ISOLATED FORM
+FC51;FC51;FC51;0647 062C;0647 062C; # (ﱑ; ﱑ; ﱑ; هج; هج; ) ARABIC LIGATURE HEH WITH JEEM ISOLATED FORM
+FC52;FC52;FC52;0647 0645;0647 0645; # (ﱒ; ﱒ; ﱒ; هم; هم; ) ARABIC LIGATURE HEH WITH MEEM ISOLATED FORM
+FC53;FC53;FC53;0647 0649;0647 0649; # (ﱓ; ﱓ; ﱓ; هى; هى; ) ARABIC LIGATURE HEH WITH ALEF MAKSURA ISOLATED FORM
+FC54;FC54;FC54;0647 064A;0647 064A; # (ﱔ; ﱔ; ﱔ; هي; هي; ) ARABIC LIGATURE HEH WITH YEH ISOLATED FORM
+FC55;FC55;FC55;064A 062C;064A 062C; # (ﱕ; ﱕ; ﱕ; يج; يج; ) ARABIC LIGATURE YEH WITH JEEM ISOLATED FORM
+FC56;FC56;FC56;064A 062D;064A 062D; # (ﱖ; ﱖ; ﱖ; يح; يح; ) ARABIC LIGATURE YEH WITH HAH ISOLATED FORM
+FC57;FC57;FC57;064A 062E;064A 062E; # (ﱗ; ﱗ; ﱗ; يخ; يخ; ) ARABIC LIGATURE YEH WITH KHAH ISOLATED FORM
+FC58;FC58;FC58;064A 0645;064A 0645; # (ﱘ; ﱘ; ﱘ; يم; يم; ) ARABIC LIGATURE YEH WITH MEEM ISOLATED FORM
+FC59;FC59;FC59;064A 0649;064A 0649; # (ﱙ; ﱙ; ﱙ; يى; يى; ) ARABIC LIGATURE YEH WITH ALEF MAKSURA ISOLATED FORM
+FC5A;FC5A;FC5A;064A 064A;064A 064A; # (ﱚ; ﱚ; ﱚ; يي; يي; ) ARABIC LIGATURE YEH WITH YEH ISOLATED FORM
+FC5B;FC5B;FC5B;0630 0670;0630 0670; # (ﱛ; ﱛ; ﱛ; ذ◌ٰ; ذ◌ٰ; ) ARABIC LIGATURE THAL WITH SUPERSCRIPT ALEF ISOLATED FORM
+FC5C;FC5C;FC5C;0631 0670;0631 0670; # (ﱜ; ﱜ; ﱜ; ر◌ٰ; ر◌ٰ; ) ARABIC LIGATURE REH WITH SUPERSCRIPT ALEF ISOLATED FORM
+FC5D;FC5D;FC5D;0649 0670;0649 0670; # (ï±; ï±; ï±; ى◌ٰ; ى◌ٰ; ) ARABIC LIGATURE ALEF MAKSURA WITH SUPERSCRIPT ALEF ISOLATED FORM
+FC5E;FC5E;FC5E;0020 064C 0651;0020 064C 0651; # (ﱞ; ﱞ; ﱞ; ◌ٌ◌ّ; ◌ٌ◌ّ; ) ARABIC LIGATURE SHADDA WITH DAMMATAN ISOLATED FORM
+FC5F;FC5F;FC5F;0020 064D 0651;0020 064D 0651; # (ﱟ; ﱟ; ﱟ; â—ŒÙ◌ّ; â—ŒÙ◌ّ; ) ARABIC LIGATURE SHADDA WITH KASRATAN ISOLATED FORM
+FC60;FC60;FC60;0020 064E 0651;0020 064E 0651; # (ﱠ; ﱠ; ﱠ; ◌َ◌ّ; ◌َ◌ّ; ) ARABIC LIGATURE SHADDA WITH FATHA ISOLATED FORM
+FC61;FC61;FC61;0020 064F 0651;0020 064F 0651; # (ﱡ; ﱡ; ﱡ; â—ŒÙ◌ّ; â—ŒÙ◌ّ; ) ARABIC LIGATURE SHADDA WITH DAMMA ISOLATED FORM
+FC62;FC62;FC62;0020 0650 0651;0020 0650 0651; # (ï±¢; ï±¢; ï±¢; â—ŒÙ◌ّ; â—ŒÙ◌ّ; ) ARABIC LIGATURE SHADDA WITH KASRA ISOLATED FORM
+FC63;FC63;FC63;0020 0651 0670;0020 0651 0670; # (ﱣ; ﱣ; ﱣ; ◌ّ◌ٰ; ◌ّ◌ٰ; ) ARABIC LIGATURE SHADDA WITH SUPERSCRIPT ALEF ISOLATED FORM
+FC64;FC64;FC64;0626 0631;064A 0654 0631; # (ﱤ; ﱤ; ﱤ; ئر; ي◌ٔر; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH REH FINAL FORM
+FC65;FC65;FC65;0626 0632;064A 0654 0632; # (ﱥ; ﱥ; ﱥ; ئز; ي◌ٔز; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ZAIN FINAL FORM
+FC66;FC66;FC66;0626 0645;064A 0654 0645; # (ﱦ; ﱦ; ﱦ; ئم; ي◌ٔم; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH MEEM FINAL FORM
+FC67;FC67;FC67;0626 0646;064A 0654 0646; # (ﱧ; ﱧ; ﱧ; ئن; ي◌ٔن; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH NOON FINAL FORM
+FC68;FC68;FC68;0626 0649;064A 0654 0649; # (ﱨ; ﱨ; ﱨ; ئى; ي◌ٔى; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF MAKSURA FINAL FORM
+FC69;FC69;FC69;0626 064A;064A 0654 064A; # (ﱩ; ﱩ; ﱩ; ئي; ي◌ٔي; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH YEH FINAL FORM
+FC6A;FC6A;FC6A;0628 0631;0628 0631; # (ﱪ; ﱪ; ﱪ; بر; بر; ) ARABIC LIGATURE BEH WITH REH FINAL FORM
+FC6B;FC6B;FC6B;0628 0632;0628 0632; # (ﱫ; ﱫ; ﱫ; بز; بز; ) ARABIC LIGATURE BEH WITH ZAIN FINAL FORM
+FC6C;FC6C;FC6C;0628 0645;0628 0645; # (ﱬ; ﱬ; ﱬ; بم; بم; ) ARABIC LIGATURE BEH WITH MEEM FINAL FORM
+FC6D;FC6D;FC6D;0628 0646;0628 0646; # (ﱭ; ﱭ; ﱭ; بن; بن; ) ARABIC LIGATURE BEH WITH NOON FINAL FORM
+FC6E;FC6E;FC6E;0628 0649;0628 0649; # (ﱮ; ﱮ; ﱮ; بى; بى; ) ARABIC LIGATURE BEH WITH ALEF MAKSURA FINAL FORM
+FC6F;FC6F;FC6F;0628 064A;0628 064A; # (ﱯ; ﱯ; ﱯ; بي; بي; ) ARABIC LIGATURE BEH WITH YEH FINAL FORM
+FC70;FC70;FC70;062A 0631;062A 0631; # (ﱰ; ﱰ; ﱰ; تر; تر; ) ARABIC LIGATURE TEH WITH REH FINAL FORM
+FC71;FC71;FC71;062A 0632;062A 0632; # (ﱱ; ﱱ; ﱱ; تز; تز; ) ARABIC LIGATURE TEH WITH ZAIN FINAL FORM
+FC72;FC72;FC72;062A 0645;062A 0645; # (ﱲ; ﱲ; ﱲ; تم; تم; ) ARABIC LIGATURE TEH WITH MEEM FINAL FORM
+FC73;FC73;FC73;062A 0646;062A 0646; # (ﱳ; ﱳ; ﱳ; تن; تن; ) ARABIC LIGATURE TEH WITH NOON FINAL FORM
+FC74;FC74;FC74;062A 0649;062A 0649; # (ﱴ; ﱴ; ﱴ; تى; تى; ) ARABIC LIGATURE TEH WITH ALEF MAKSURA FINAL FORM
+FC75;FC75;FC75;062A 064A;062A 064A; # (ﱵ; ﱵ; ﱵ; تي; تي; ) ARABIC LIGATURE TEH WITH YEH FINAL FORM
+FC76;FC76;FC76;062B 0631;062B 0631; # (ﱶ; ﱶ; ﱶ; ثر; ثر; ) ARABIC LIGATURE THEH WITH REH FINAL FORM
+FC77;FC77;FC77;062B 0632;062B 0632; # (ﱷ; ﱷ; ﱷ; ثز; ثز; ) ARABIC LIGATURE THEH WITH ZAIN FINAL FORM
+FC78;FC78;FC78;062B 0645;062B 0645; # (ﱸ; ﱸ; ﱸ; ثم; ثم; ) ARABIC LIGATURE THEH WITH MEEM FINAL FORM
+FC79;FC79;FC79;062B 0646;062B 0646; # (ﱹ; ﱹ; ﱹ; ثن; ثن; ) ARABIC LIGATURE THEH WITH NOON FINAL FORM
+FC7A;FC7A;FC7A;062B 0649;062B 0649; # (ﱺ; ﱺ; ﱺ; ثى; ثى; ) ARABIC LIGATURE THEH WITH ALEF MAKSURA FINAL FORM
+FC7B;FC7B;FC7B;062B 064A;062B 064A; # (ﱻ; ﱻ; ﱻ; ثي; ثي; ) ARABIC LIGATURE THEH WITH YEH FINAL FORM
+FC7C;FC7C;FC7C;0641 0649;0641 0649; # (ï±¼; ï±¼; ï±¼; ÙÙ‰; ÙÙ‰; ) ARABIC LIGATURE FEH WITH ALEF MAKSURA FINAL FORM
+FC7D;FC7D;FC7D;0641 064A;0641 064A; # (ï±½; ï±½; ï±½; ÙÙŠ; ÙÙŠ; ) ARABIC LIGATURE FEH WITH YEH FINAL FORM
+FC7E;FC7E;FC7E;0642 0649;0642 0649; # (ﱾ; ﱾ; ﱾ; قى; قى; ) ARABIC LIGATURE QAF WITH ALEF MAKSURA FINAL FORM
+FC7F;FC7F;FC7F;0642 064A;0642 064A; # (ﱿ; ﱿ; ﱿ; قي; قي; ) ARABIC LIGATURE QAF WITH YEH FINAL FORM
+FC80;FC80;FC80;0643 0627;0643 0627; # (ﲀ; ﲀ; ﲀ; كا; كا; ) ARABIC LIGATURE KAF WITH ALEF FINAL FORM
+FC81;FC81;FC81;0643 0644;0643 0644; # (ï²; ï²; ï²; كل; كل; ) ARABIC LIGATURE KAF WITH LAM FINAL FORM
+FC82;FC82;FC82;0643 0645;0643 0645; # (ﲂ; ﲂ; ﲂ; كم; كم; ) ARABIC LIGATURE KAF WITH MEEM FINAL FORM
+FC83;FC83;FC83;0643 0649;0643 0649; # (ﲃ; ﲃ; ﲃ; كى; كى; ) ARABIC LIGATURE KAF WITH ALEF MAKSURA FINAL FORM
+FC84;FC84;FC84;0643 064A;0643 064A; # (ﲄ; ﲄ; ﲄ; كي; كي; ) ARABIC LIGATURE KAF WITH YEH FINAL FORM
+FC85;FC85;FC85;0644 0645;0644 0645; # (ﲅ; ﲅ; ﲅ; لم; لم; ) ARABIC LIGATURE LAM WITH MEEM FINAL FORM
+FC86;FC86;FC86;0644 0649;0644 0649; # (ﲆ; ﲆ; ﲆ; لى; لى; ) ARABIC LIGATURE LAM WITH ALEF MAKSURA FINAL FORM
+FC87;FC87;FC87;0644 064A;0644 064A; # (ﲇ; ﲇ; ﲇ; لي; لي; ) ARABIC LIGATURE LAM WITH YEH FINAL FORM
+FC88;FC88;FC88;0645 0627;0645 0627; # (ﲈ; ﲈ; ﲈ; ما; ما; ) ARABIC LIGATURE MEEM WITH ALEF FINAL FORM
+FC89;FC89;FC89;0645 0645;0645 0645; # (ﲉ; ﲉ; ﲉ; مم; مم; ) ARABIC LIGATURE MEEM WITH MEEM FINAL FORM
+FC8A;FC8A;FC8A;0646 0631;0646 0631; # (ﲊ; ﲊ; ﲊ; نر; نر; ) ARABIC LIGATURE NOON WITH REH FINAL FORM
+FC8B;FC8B;FC8B;0646 0632;0646 0632; # (ﲋ; ﲋ; ﲋ; نز; نز; ) ARABIC LIGATURE NOON WITH ZAIN FINAL FORM
+FC8C;FC8C;FC8C;0646 0645;0646 0645; # (ﲌ; ﲌ; ﲌ; نم; نم; ) ARABIC LIGATURE NOON WITH MEEM FINAL FORM
+FC8D;FC8D;FC8D;0646 0646;0646 0646; # (ï²; ï²; ï²; نن; نن; ) ARABIC LIGATURE NOON WITH NOON FINAL FORM
+FC8E;FC8E;FC8E;0646 0649;0646 0649; # (ﲎ; ﲎ; ﲎ; نى; نى; ) ARABIC LIGATURE NOON WITH ALEF MAKSURA FINAL FORM
+FC8F;FC8F;FC8F;0646 064A;0646 064A; # (ï²; ï²; ï²; ني; ني; ) ARABIC LIGATURE NOON WITH YEH FINAL FORM
+FC90;FC90;FC90;0649 0670;0649 0670; # (ï²; ï²; ï²; ى◌ٰ; ى◌ٰ; ) ARABIC LIGATURE ALEF MAKSURA WITH SUPERSCRIPT ALEF FINAL FORM
+FC91;FC91;FC91;064A 0631;064A 0631; # (ﲑ; ﲑ; ﲑ; ير; ير; ) ARABIC LIGATURE YEH WITH REH FINAL FORM
+FC92;FC92;FC92;064A 0632;064A 0632; # (ﲒ; ﲒ; ﲒ; يز; يز; ) ARABIC LIGATURE YEH WITH ZAIN FINAL FORM
+FC93;FC93;FC93;064A 0645;064A 0645; # (ﲓ; ﲓ; ﲓ; يم; يم; ) ARABIC LIGATURE YEH WITH MEEM FINAL FORM
+FC94;FC94;FC94;064A 0646;064A 0646; # (ﲔ; ﲔ; ﲔ; ين; ين; ) ARABIC LIGATURE YEH WITH NOON FINAL FORM
+FC95;FC95;FC95;064A 0649;064A 0649; # (ﲕ; ﲕ; ﲕ; يى; يى; ) ARABIC LIGATURE YEH WITH ALEF MAKSURA FINAL FORM
+FC96;FC96;FC96;064A 064A;064A 064A; # (ﲖ; ﲖ; ﲖ; يي; يي; ) ARABIC LIGATURE YEH WITH YEH FINAL FORM
+FC97;FC97;FC97;0626 062C;064A 0654 062C; # (ﲗ; ﲗ; ﲗ; ئج; ي◌ٔج; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH JEEM INITIAL FORM
+FC98;FC98;FC98;0626 062D;064A 0654 062D; # (ﲘ; ﲘ; ﲘ; ئح; ي◌ٔح; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH HAH INITIAL FORM
+FC99;FC99;FC99;0626 062E;064A 0654 062E; # (ﲙ; ﲙ; ﲙ; ئخ; ي◌ٔخ; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH KHAH INITIAL FORM
+FC9A;FC9A;FC9A;0626 0645;064A 0654 0645; # (ﲚ; ﲚ; ﲚ; ئم; ي◌ٔم; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH MEEM INITIAL FORM
+FC9B;FC9B;FC9B;0626 0647;064A 0654 0647; # (ﲛ; ﲛ; ﲛ; ئه; ي◌ٔه; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH HEH INITIAL FORM
+FC9C;FC9C;FC9C;0628 062C;0628 062C; # (ﲜ; ﲜ; ﲜ; بج; بج; ) ARABIC LIGATURE BEH WITH JEEM INITIAL FORM
+FC9D;FC9D;FC9D;0628 062D;0628 062D; # (ï²; ï²; ï²; بح; بح; ) ARABIC LIGATURE BEH WITH HAH INITIAL FORM
+FC9E;FC9E;FC9E;0628 062E;0628 062E; # (ﲞ; ﲞ; ﲞ; بخ; بخ; ) ARABIC LIGATURE BEH WITH KHAH INITIAL FORM
+FC9F;FC9F;FC9F;0628 0645;0628 0645; # (ﲟ; ﲟ; ﲟ; بم; بم; ) ARABIC LIGATURE BEH WITH MEEM INITIAL FORM
+FCA0;FCA0;FCA0;0628 0647;0628 0647; # (ﲠ; ﲠ; ﲠ; به; به; ) ARABIC LIGATURE BEH WITH HEH INITIAL FORM
+FCA1;FCA1;FCA1;062A 062C;062A 062C; # (ﲡ; ﲡ; ﲡ; تج; تج; ) ARABIC LIGATURE TEH WITH JEEM INITIAL FORM
+FCA2;FCA2;FCA2;062A 062D;062A 062D; # (ﲢ; ﲢ; ﲢ; تح; تح; ) ARABIC LIGATURE TEH WITH HAH INITIAL FORM
+FCA3;FCA3;FCA3;062A 062E;062A 062E; # (ﲣ; ﲣ; ﲣ; تخ; تخ; ) ARABIC LIGATURE TEH WITH KHAH INITIAL FORM
+FCA4;FCA4;FCA4;062A 0645;062A 0645; # (ﲤ; ﲤ; ﲤ; تم; تم; ) ARABIC LIGATURE TEH WITH MEEM INITIAL FORM
+FCA5;FCA5;FCA5;062A 0647;062A 0647; # (ﲥ; ﲥ; ﲥ; ته; ته; ) ARABIC LIGATURE TEH WITH HEH INITIAL FORM
+FCA6;FCA6;FCA6;062B 0645;062B 0645; # (ﲦ; ﲦ; ﲦ; ثم; ثم; ) ARABIC LIGATURE THEH WITH MEEM INITIAL FORM
+FCA7;FCA7;FCA7;062C 062D;062C 062D; # (ﲧ; ﲧ; ﲧ; جح; جح; ) ARABIC LIGATURE JEEM WITH HAH INITIAL FORM
+FCA8;FCA8;FCA8;062C 0645;062C 0645; # (ﲨ; ﲨ; ﲨ; جم; جم; ) ARABIC LIGATURE JEEM WITH MEEM INITIAL FORM
+FCA9;FCA9;FCA9;062D 062C;062D 062C; # (ﲩ; ﲩ; ﲩ; حج; حج; ) ARABIC LIGATURE HAH WITH JEEM INITIAL FORM
+FCAA;FCAA;FCAA;062D 0645;062D 0645; # (ﲪ; ﲪ; ﲪ; حم; حم; ) ARABIC LIGATURE HAH WITH MEEM INITIAL FORM
+FCAB;FCAB;FCAB;062E 062C;062E 062C; # (ﲫ; ﲫ; ﲫ; خج; خج; ) ARABIC LIGATURE KHAH WITH JEEM INITIAL FORM
+FCAC;FCAC;FCAC;062E 0645;062E 0645; # (ﲬ; ﲬ; ﲬ; خم; خم; ) ARABIC LIGATURE KHAH WITH MEEM INITIAL FORM
+FCAD;FCAD;FCAD;0633 062C;0633 062C; # (ﲭ; ﲭ; ﲭ; سج; سج; ) ARABIC LIGATURE SEEN WITH JEEM INITIAL FORM
+FCAE;FCAE;FCAE;0633 062D;0633 062D; # (ﲮ; ﲮ; ﲮ; سح; سح; ) ARABIC LIGATURE SEEN WITH HAH INITIAL FORM
+FCAF;FCAF;FCAF;0633 062E;0633 062E; # (ﲯ; ﲯ; ﲯ; سخ; سخ; ) ARABIC LIGATURE SEEN WITH KHAH INITIAL FORM
+FCB0;FCB0;FCB0;0633 0645;0633 0645; # (ﲰ; ﲰ; ﲰ; سم; سم; ) ARABIC LIGATURE SEEN WITH MEEM INITIAL FORM
+FCB1;FCB1;FCB1;0635 062D;0635 062D; # (ﲱ; ﲱ; ﲱ; صح; صح; ) ARABIC LIGATURE SAD WITH HAH INITIAL FORM
+FCB2;FCB2;FCB2;0635 062E;0635 062E; # (ﲲ; ﲲ; ﲲ; صخ; صخ; ) ARABIC LIGATURE SAD WITH KHAH INITIAL FORM
+FCB3;FCB3;FCB3;0635 0645;0635 0645; # (ﲳ; ﲳ; ﲳ; صم; صم; ) ARABIC LIGATURE SAD WITH MEEM INITIAL FORM
+FCB4;FCB4;FCB4;0636 062C;0636 062C; # (ﲴ; ﲴ; ﲴ; ضج; ضج; ) ARABIC LIGATURE DAD WITH JEEM INITIAL FORM
+FCB5;FCB5;FCB5;0636 062D;0636 062D; # (ﲵ; ﲵ; ﲵ; ضح; ضح; ) ARABIC LIGATURE DAD WITH HAH INITIAL FORM
+FCB6;FCB6;FCB6;0636 062E;0636 062E; # (ﲶ; ﲶ; ﲶ; ضخ; ضخ; ) ARABIC LIGATURE DAD WITH KHAH INITIAL FORM
+FCB7;FCB7;FCB7;0636 0645;0636 0645; # (ﲷ; ﲷ; ﲷ; ضم; ضم; ) ARABIC LIGATURE DAD WITH MEEM INITIAL FORM
+FCB8;FCB8;FCB8;0637 062D;0637 062D; # (ﲸ; ﲸ; ﲸ; طح; طح; ) ARABIC LIGATURE TAH WITH HAH INITIAL FORM
+FCB9;FCB9;FCB9;0638 0645;0638 0645; # (ﲹ; ﲹ; ﲹ; ظم; ظم; ) ARABIC LIGATURE ZAH WITH MEEM INITIAL FORM
+FCBA;FCBA;FCBA;0639 062C;0639 062C; # (ﲺ; ﲺ; ﲺ; عج; عج; ) ARABIC LIGATURE AIN WITH JEEM INITIAL FORM
+FCBB;FCBB;FCBB;0639 0645;0639 0645; # (ﲻ; ﲻ; ﲻ; عم; عم; ) ARABIC LIGATURE AIN WITH MEEM INITIAL FORM
+FCBC;FCBC;FCBC;063A 062C;063A 062C; # (ﲼ; ﲼ; ﲼ; غج; غج; ) ARABIC LIGATURE GHAIN WITH JEEM INITIAL FORM
+FCBD;FCBD;FCBD;063A 0645;063A 0645; # (ﲽ; ﲽ; ﲽ; غم; غم; ) ARABIC LIGATURE GHAIN WITH MEEM INITIAL FORM
+FCBE;FCBE;FCBE;0641 062C;0641 062C; # (ï²¾; ï²¾; ï²¾; ÙØ¬; ÙØ¬; ) ARABIC LIGATURE FEH WITH JEEM INITIAL FORM
+FCBF;FCBF;FCBF;0641 062D;0641 062D; # (ﲿ; ﲿ; ﲿ; ÙØ­; ÙØ­; ) ARABIC LIGATURE FEH WITH HAH INITIAL FORM
+FCC0;FCC0;FCC0;0641 062E;0641 062E; # (ï³€; ï³€; ï³€; ÙØ®; ÙØ®; ) ARABIC LIGATURE FEH WITH KHAH INITIAL FORM
+FCC1;FCC1;FCC1;0641 0645;0641 0645; # (ï³; ï³; ï³; ÙÙ…; ÙÙ…; ) ARABIC LIGATURE FEH WITH MEEM INITIAL FORM
+FCC2;FCC2;FCC2;0642 062D;0642 062D; # (ﳂ; ﳂ; ﳂ; قح; قح; ) ARABIC LIGATURE QAF WITH HAH INITIAL FORM
+FCC3;FCC3;FCC3;0642 0645;0642 0645; # (ﳃ; ﳃ; ﳃ; قم; قم; ) ARABIC LIGATURE QAF WITH MEEM INITIAL FORM
+FCC4;FCC4;FCC4;0643 062C;0643 062C; # (ﳄ; ﳄ; ﳄ; كج; كج; ) ARABIC LIGATURE KAF WITH JEEM INITIAL FORM
+FCC5;FCC5;FCC5;0643 062D;0643 062D; # (ﳅ; ﳅ; ﳅ; كح; كح; ) ARABIC LIGATURE KAF WITH HAH INITIAL FORM
+FCC6;FCC6;FCC6;0643 062E;0643 062E; # (ﳆ; ﳆ; ﳆ; كخ; كخ; ) ARABIC LIGATURE KAF WITH KHAH INITIAL FORM
+FCC7;FCC7;FCC7;0643 0644;0643 0644; # (ﳇ; ﳇ; ﳇ; كل; كل; ) ARABIC LIGATURE KAF WITH LAM INITIAL FORM
+FCC8;FCC8;FCC8;0643 0645;0643 0645; # (ﳈ; ﳈ; ﳈ; كم; كم; ) ARABIC LIGATURE KAF WITH MEEM INITIAL FORM
+FCC9;FCC9;FCC9;0644 062C;0644 062C; # (ﳉ; ﳉ; ﳉ; لج; لج; ) ARABIC LIGATURE LAM WITH JEEM INITIAL FORM
+FCCA;FCCA;FCCA;0644 062D;0644 062D; # (ﳊ; ﳊ; ﳊ; لح; لح; ) ARABIC LIGATURE LAM WITH HAH INITIAL FORM
+FCCB;FCCB;FCCB;0644 062E;0644 062E; # (ﳋ; ﳋ; ﳋ; لخ; لخ; ) ARABIC LIGATURE LAM WITH KHAH INITIAL FORM
+FCCC;FCCC;FCCC;0644 0645;0644 0645; # (ﳌ; ﳌ; ﳌ; لم; لم; ) ARABIC LIGATURE LAM WITH MEEM INITIAL FORM
+FCCD;FCCD;FCCD;0644 0647;0644 0647; # (ï³; ï³; ï³; له; له; ) ARABIC LIGATURE LAM WITH HEH INITIAL FORM
+FCCE;FCCE;FCCE;0645 062C;0645 062C; # (ﳎ; ﳎ; ﳎ; مج; مج; ) ARABIC LIGATURE MEEM WITH JEEM INITIAL FORM
+FCCF;FCCF;FCCF;0645 062D;0645 062D; # (ï³; ï³; ï³; مح; مح; ) ARABIC LIGATURE MEEM WITH HAH INITIAL FORM
+FCD0;FCD0;FCD0;0645 062E;0645 062E; # (ï³; ï³; ï³; مخ; مخ; ) ARABIC LIGATURE MEEM WITH KHAH INITIAL FORM
+FCD1;FCD1;FCD1;0645 0645;0645 0645; # (ﳑ; ﳑ; ﳑ; مم; مم; ) ARABIC LIGATURE MEEM WITH MEEM INITIAL FORM
+FCD2;FCD2;FCD2;0646 062C;0646 062C; # (ﳒ; ﳒ; ﳒ; نج; نج; ) ARABIC LIGATURE NOON WITH JEEM INITIAL FORM
+FCD3;FCD3;FCD3;0646 062D;0646 062D; # (ﳓ; ﳓ; ﳓ; نح; نح; ) ARABIC LIGATURE NOON WITH HAH INITIAL FORM
+FCD4;FCD4;FCD4;0646 062E;0646 062E; # (ﳔ; ﳔ; ﳔ; نخ; نخ; ) ARABIC LIGATURE NOON WITH KHAH INITIAL FORM
+FCD5;FCD5;FCD5;0646 0645;0646 0645; # (ﳕ; ﳕ; ﳕ; نم; نم; ) ARABIC LIGATURE NOON WITH MEEM INITIAL FORM
+FCD6;FCD6;FCD6;0646 0647;0646 0647; # (ﳖ; ﳖ; ﳖ; نه; نه; ) ARABIC LIGATURE NOON WITH HEH INITIAL FORM
+FCD7;FCD7;FCD7;0647 062C;0647 062C; # (ﳗ; ﳗ; ﳗ; هج; هج; ) ARABIC LIGATURE HEH WITH JEEM INITIAL FORM
+FCD8;FCD8;FCD8;0647 0645;0647 0645; # (ﳘ; ﳘ; ﳘ; هم; هم; ) ARABIC LIGATURE HEH WITH MEEM INITIAL FORM
+FCD9;FCD9;FCD9;0647 0670;0647 0670; # (ﳙ; ﳙ; ﳙ; ه◌ٰ; ه◌ٰ; ) ARABIC LIGATURE HEH WITH SUPERSCRIPT ALEF INITIAL FORM
+FCDA;FCDA;FCDA;064A 062C;064A 062C; # (ﳚ; ﳚ; ﳚ; يج; يج; ) ARABIC LIGATURE YEH WITH JEEM INITIAL FORM
+FCDB;FCDB;FCDB;064A 062D;064A 062D; # (ﳛ; ﳛ; ﳛ; يح; يح; ) ARABIC LIGATURE YEH WITH HAH INITIAL FORM
+FCDC;FCDC;FCDC;064A 062E;064A 062E; # (ﳜ; ﳜ; ﳜ; يخ; يخ; ) ARABIC LIGATURE YEH WITH KHAH INITIAL FORM
+FCDD;FCDD;FCDD;064A 0645;064A 0645; # (ï³; ï³; ï³; يم; يم; ) ARABIC LIGATURE YEH WITH MEEM INITIAL FORM
+FCDE;FCDE;FCDE;064A 0647;064A 0647; # (ﳞ; ﳞ; ﳞ; يه; يه; ) ARABIC LIGATURE YEH WITH HEH INITIAL FORM
+FCDF;FCDF;FCDF;0626 0645;064A 0654 0645; # (ﳟ; ﳟ; ﳟ; ئم; ي◌ٔم; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH MEEM MEDIAL FORM
+FCE0;FCE0;FCE0;0626 0647;064A 0654 0647; # (ﳠ; ﳠ; ﳠ; ئه; ي◌ٔه; ) ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH HEH MEDIAL FORM
+FCE1;FCE1;FCE1;0628 0645;0628 0645; # (ﳡ; ﳡ; ﳡ; بم; بم; ) ARABIC LIGATURE BEH WITH MEEM MEDIAL FORM
+FCE2;FCE2;FCE2;0628 0647;0628 0647; # (ﳢ; ﳢ; ﳢ; به; به; ) ARABIC LIGATURE BEH WITH HEH MEDIAL FORM
+FCE3;FCE3;FCE3;062A 0645;062A 0645; # (ﳣ; ﳣ; ﳣ; تم; تم; ) ARABIC LIGATURE TEH WITH MEEM MEDIAL FORM
+FCE4;FCE4;FCE4;062A 0647;062A 0647; # (ﳤ; ﳤ; ﳤ; ته; ته; ) ARABIC LIGATURE TEH WITH HEH MEDIAL FORM
+FCE5;FCE5;FCE5;062B 0645;062B 0645; # (ﳥ; ﳥ; ﳥ; ثم; ثم; ) ARABIC LIGATURE THEH WITH MEEM MEDIAL FORM
+FCE6;FCE6;FCE6;062B 0647;062B 0647; # (ﳦ; ﳦ; ﳦ; ثه; ثه; ) ARABIC LIGATURE THEH WITH HEH MEDIAL FORM
+FCE7;FCE7;FCE7;0633 0645;0633 0645; # (ﳧ; ﳧ; ﳧ; سم; سم; ) ARABIC LIGATURE SEEN WITH MEEM MEDIAL FORM
+FCE8;FCE8;FCE8;0633 0647;0633 0647; # (ﳨ; ﳨ; ﳨ; سه; سه; ) ARABIC LIGATURE SEEN WITH HEH MEDIAL FORM
+FCE9;FCE9;FCE9;0634 0645;0634 0645; # (ﳩ; ﳩ; ﳩ; شم; شم; ) ARABIC LIGATURE SHEEN WITH MEEM MEDIAL FORM
+FCEA;FCEA;FCEA;0634 0647;0634 0647; # (ﳪ; ﳪ; ﳪ; شه; شه; ) ARABIC LIGATURE SHEEN WITH HEH MEDIAL FORM
+FCEB;FCEB;FCEB;0643 0644;0643 0644; # (ﳫ; ﳫ; ﳫ; كل; كل; ) ARABIC LIGATURE KAF WITH LAM MEDIAL FORM
+FCEC;FCEC;FCEC;0643 0645;0643 0645; # (ﳬ; ﳬ; ﳬ; كم; كم; ) ARABIC LIGATURE KAF WITH MEEM MEDIAL FORM
+FCED;FCED;FCED;0644 0645;0644 0645; # (ﳭ; ﳭ; ﳭ; لم; لم; ) ARABIC LIGATURE LAM WITH MEEM MEDIAL FORM
+FCEE;FCEE;FCEE;0646 0645;0646 0645; # (ﳮ; ﳮ; ﳮ; نم; نم; ) ARABIC LIGATURE NOON WITH MEEM MEDIAL FORM
+FCEF;FCEF;FCEF;0646 0647;0646 0647; # (ﳯ; ﳯ; ﳯ; نه; نه; ) ARABIC LIGATURE NOON WITH HEH MEDIAL FORM
+FCF0;FCF0;FCF0;064A 0645;064A 0645; # (ﳰ; ﳰ; ﳰ; يم; يم; ) ARABIC LIGATURE YEH WITH MEEM MEDIAL FORM
+FCF1;FCF1;FCF1;064A 0647;064A 0647; # (ﳱ; ﳱ; ﳱ; يه; يه; ) ARABIC LIGATURE YEH WITH HEH MEDIAL FORM
+FCF2;FCF2;FCF2;0640 064E 0651;0640 064E 0651; # (ﳲ; ﳲ; ﳲ; ـ◌َ◌ّ; ـ◌َ◌ّ; ) ARABIC LIGATURE SHADDA WITH FATHA MEDIAL FORM
+FCF3;FCF3;FCF3;0640 064F 0651;0640 064F 0651; # (ï³³; ï³³; ï³³; ـ◌Ù◌ّ; ـ◌Ù◌ّ; ) ARABIC LIGATURE SHADDA WITH DAMMA MEDIAL FORM
+FCF4;FCF4;FCF4;0640 0650 0651;0640 0650 0651; # (ï³´; ï³´; ï³´; ـ◌Ù◌ّ; ـ◌Ù◌ّ; ) ARABIC LIGATURE SHADDA WITH KASRA MEDIAL FORM
+FCF5;FCF5;FCF5;0637 0649;0637 0649; # (ﳵ; ﳵ; ﳵ; طى; طى; ) ARABIC LIGATURE TAH WITH ALEF MAKSURA ISOLATED FORM
+FCF6;FCF6;FCF6;0637 064A;0637 064A; # (ﳶ; ﳶ; ﳶ; طي; طي; ) ARABIC LIGATURE TAH WITH YEH ISOLATED FORM
+FCF7;FCF7;FCF7;0639 0649;0639 0649; # (ﳷ; ﳷ; ﳷ; عى; عى; ) ARABIC LIGATURE AIN WITH ALEF MAKSURA ISOLATED FORM
+FCF8;FCF8;FCF8;0639 064A;0639 064A; # (ﳸ; ﳸ; ﳸ; عي; عي; ) ARABIC LIGATURE AIN WITH YEH ISOLATED FORM
+FCF9;FCF9;FCF9;063A 0649;063A 0649; # (ﳹ; ﳹ; ﳹ; غى; غى; ) ARABIC LIGATURE GHAIN WITH ALEF MAKSURA ISOLATED FORM
+FCFA;FCFA;FCFA;063A 064A;063A 064A; # (ﳺ; ﳺ; ﳺ; غي; غي; ) ARABIC LIGATURE GHAIN WITH YEH ISOLATED FORM
+FCFB;FCFB;FCFB;0633 0649;0633 0649; # (ﳻ; ﳻ; ﳻ; سى; سى; ) ARABIC LIGATURE SEEN WITH ALEF MAKSURA ISOLATED FORM
+FCFC;FCFC;FCFC;0633 064A;0633 064A; # (ﳼ; ﳼ; ﳼ; سي; سي; ) ARABIC LIGATURE SEEN WITH YEH ISOLATED FORM
+FCFD;FCFD;FCFD;0634 0649;0634 0649; # (ﳽ; ﳽ; ﳽ; شى; شى; ) ARABIC LIGATURE SHEEN WITH ALEF MAKSURA ISOLATED FORM
+FCFE;FCFE;FCFE;0634 064A;0634 064A; # (ﳾ; ﳾ; ﳾ; شي; شي; ) ARABIC LIGATURE SHEEN WITH YEH ISOLATED FORM
+FCFF;FCFF;FCFF;062D 0649;062D 0649; # (ﳿ; ﳿ; ﳿ; حى; حى; ) ARABIC LIGATURE HAH WITH ALEF MAKSURA ISOLATED FORM
+FD00;FD00;FD00;062D 064A;062D 064A; # (ﴀ; ﴀ; ﴀ; حي; حي; ) ARABIC LIGATURE HAH WITH YEH ISOLATED FORM
+FD01;FD01;FD01;062C 0649;062C 0649; # (ï´; ï´; ï´; جى; جى; ) ARABIC LIGATURE JEEM WITH ALEF MAKSURA ISOLATED FORM
+FD02;FD02;FD02;062C 064A;062C 064A; # (ﴂ; ﴂ; ﴂ; جي; جي; ) ARABIC LIGATURE JEEM WITH YEH ISOLATED FORM
+FD03;FD03;FD03;062E 0649;062E 0649; # (ﴃ; ﴃ; ﴃ; خى; خى; ) ARABIC LIGATURE KHAH WITH ALEF MAKSURA ISOLATED FORM
+FD04;FD04;FD04;062E 064A;062E 064A; # (ﴄ; ﴄ; ﴄ; خي; خي; ) ARABIC LIGATURE KHAH WITH YEH ISOLATED FORM
+FD05;FD05;FD05;0635 0649;0635 0649; # (ﴅ; ﴅ; ﴅ; صى; صى; ) ARABIC LIGATURE SAD WITH ALEF MAKSURA ISOLATED FORM
+FD06;FD06;FD06;0635 064A;0635 064A; # (ﴆ; ﴆ; ﴆ; صي; صي; ) ARABIC LIGATURE SAD WITH YEH ISOLATED FORM
+FD07;FD07;FD07;0636 0649;0636 0649; # (ﴇ; ﴇ; ﴇ; ضى; ضى; ) ARABIC LIGATURE DAD WITH ALEF MAKSURA ISOLATED FORM
+FD08;FD08;FD08;0636 064A;0636 064A; # (ﴈ; ﴈ; ﴈ; ضي; ضي; ) ARABIC LIGATURE DAD WITH YEH ISOLATED FORM
+FD09;FD09;FD09;0634 062C;0634 062C; # (ﴉ; ﴉ; ﴉ; شج; شج; ) ARABIC LIGATURE SHEEN WITH JEEM ISOLATED FORM
+FD0A;FD0A;FD0A;0634 062D;0634 062D; # (ﴊ; ﴊ; ﴊ; شح; شح; ) ARABIC LIGATURE SHEEN WITH HAH ISOLATED FORM
+FD0B;FD0B;FD0B;0634 062E;0634 062E; # (ﴋ; ﴋ; ﴋ; شخ; شخ; ) ARABIC LIGATURE SHEEN WITH KHAH ISOLATED FORM
+FD0C;FD0C;FD0C;0634 0645;0634 0645; # (ﴌ; ﴌ; ﴌ; شم; شم; ) ARABIC LIGATURE SHEEN WITH MEEM ISOLATED FORM
+FD0D;FD0D;FD0D;0634 0631;0634 0631; # (ï´; ï´; ï´; شر; شر; ) ARABIC LIGATURE SHEEN WITH REH ISOLATED FORM
+FD0E;FD0E;FD0E;0633 0631;0633 0631; # (ﴎ; ﴎ; ﴎ; سر; سر; ) ARABIC LIGATURE SEEN WITH REH ISOLATED FORM
+FD0F;FD0F;FD0F;0635 0631;0635 0631; # (ï´; ï´; ï´; صر; صر; ) ARABIC LIGATURE SAD WITH REH ISOLATED FORM
+FD10;FD10;FD10;0636 0631;0636 0631; # (ï´; ï´; ï´; ضر; ضر; ) ARABIC LIGATURE DAD WITH REH ISOLATED FORM
+FD11;FD11;FD11;0637 0649;0637 0649; # (ﴑ; ﴑ; ﴑ; طى; طى; ) ARABIC LIGATURE TAH WITH ALEF MAKSURA FINAL FORM
+FD12;FD12;FD12;0637 064A;0637 064A; # (ﴒ; ﴒ; ﴒ; طي; طي; ) ARABIC LIGATURE TAH WITH YEH FINAL FORM
+FD13;FD13;FD13;0639 0649;0639 0649; # (ﴓ; ﴓ; ﴓ; عى; عى; ) ARABIC LIGATURE AIN WITH ALEF MAKSURA FINAL FORM
+FD14;FD14;FD14;0639 064A;0639 064A; # (ﴔ; ﴔ; ﴔ; عي; عي; ) ARABIC LIGATURE AIN WITH YEH FINAL FORM
+FD15;FD15;FD15;063A 0649;063A 0649; # (ﴕ; ﴕ; ﴕ; غى; غى; ) ARABIC LIGATURE GHAIN WITH ALEF MAKSURA FINAL FORM
+FD16;FD16;FD16;063A 064A;063A 064A; # (ﴖ; ﴖ; ﴖ; غي; غي; ) ARABIC LIGATURE GHAIN WITH YEH FINAL FORM
+FD17;FD17;FD17;0633 0649;0633 0649; # (ﴗ; ﴗ; ﴗ; سى; سى; ) ARABIC LIGATURE SEEN WITH ALEF MAKSURA FINAL FORM
+FD18;FD18;FD18;0633 064A;0633 064A; # (ﴘ; ﴘ; ﴘ; سي; سي; ) ARABIC LIGATURE SEEN WITH YEH FINAL FORM
+FD19;FD19;FD19;0634 0649;0634 0649; # (ﴙ; ﴙ; ﴙ; شى; شى; ) ARABIC LIGATURE SHEEN WITH ALEF MAKSURA FINAL FORM
+FD1A;FD1A;FD1A;0634 064A;0634 064A; # (ﴚ; ﴚ; ﴚ; شي; شي; ) ARABIC LIGATURE SHEEN WITH YEH FINAL FORM
+FD1B;FD1B;FD1B;062D 0649;062D 0649; # (ﴛ; ﴛ; ﴛ; حى; حى; ) ARABIC LIGATURE HAH WITH ALEF MAKSURA FINAL FORM
+FD1C;FD1C;FD1C;062D 064A;062D 064A; # (ﴜ; ﴜ; ﴜ; حي; حي; ) ARABIC LIGATURE HAH WITH YEH FINAL FORM
+FD1D;FD1D;FD1D;062C 0649;062C 0649; # (ï´; ï´; ï´; جى; جى; ) ARABIC LIGATURE JEEM WITH ALEF MAKSURA FINAL FORM
+FD1E;FD1E;FD1E;062C 064A;062C 064A; # (ﴞ; ﴞ; ﴞ; جي; جي; ) ARABIC LIGATURE JEEM WITH YEH FINAL FORM
+FD1F;FD1F;FD1F;062E 0649;062E 0649; # (ﴟ; ﴟ; ﴟ; خى; خى; ) ARABIC LIGATURE KHAH WITH ALEF MAKSURA FINAL FORM
+FD20;FD20;FD20;062E 064A;062E 064A; # (ﴠ; ﴠ; ﴠ; خي; خي; ) ARABIC LIGATURE KHAH WITH YEH FINAL FORM
+FD21;FD21;FD21;0635 0649;0635 0649; # (ﴡ; ﴡ; ﴡ; صى; صى; ) ARABIC LIGATURE SAD WITH ALEF MAKSURA FINAL FORM
+FD22;FD22;FD22;0635 064A;0635 064A; # (ﴢ; ﴢ; ﴢ; صي; صي; ) ARABIC LIGATURE SAD WITH YEH FINAL FORM
+FD23;FD23;FD23;0636 0649;0636 0649; # (ﴣ; ﴣ; ﴣ; ضى; ضى; ) ARABIC LIGATURE DAD WITH ALEF MAKSURA FINAL FORM
+FD24;FD24;FD24;0636 064A;0636 064A; # (ﴤ; ﴤ; ﴤ; ضي; ضي; ) ARABIC LIGATURE DAD WITH YEH FINAL FORM
+FD25;FD25;FD25;0634 062C;0634 062C; # (ﴥ; ﴥ; ﴥ; شج; شج; ) ARABIC LIGATURE SHEEN WITH JEEM FINAL FORM
+FD26;FD26;FD26;0634 062D;0634 062D; # (ﴦ; ﴦ; ﴦ; شح; شح; ) ARABIC LIGATURE SHEEN WITH HAH FINAL FORM
+FD27;FD27;FD27;0634 062E;0634 062E; # (ﴧ; ﴧ; ﴧ; شخ; شخ; ) ARABIC LIGATURE SHEEN WITH KHAH FINAL FORM
+FD28;FD28;FD28;0634 0645;0634 0645; # (ﴨ; ﴨ; ﴨ; شم; شم; ) ARABIC LIGATURE SHEEN WITH MEEM FINAL FORM
+FD29;FD29;FD29;0634 0631;0634 0631; # (ﴩ; ﴩ; ﴩ; شر; شر; ) ARABIC LIGATURE SHEEN WITH REH FINAL FORM
+FD2A;FD2A;FD2A;0633 0631;0633 0631; # (ﴪ; ﴪ; ﴪ; سر; سر; ) ARABIC LIGATURE SEEN WITH REH FINAL FORM
+FD2B;FD2B;FD2B;0635 0631;0635 0631; # (ﴫ; ﴫ; ﴫ; صر; صر; ) ARABIC LIGATURE SAD WITH REH FINAL FORM
+FD2C;FD2C;FD2C;0636 0631;0636 0631; # (ﴬ; ﴬ; ﴬ; ضر; ضر; ) ARABIC LIGATURE DAD WITH REH FINAL FORM
+FD2D;FD2D;FD2D;0634 062C;0634 062C; # (ﴭ; ﴭ; ﴭ; شج; شج; ) ARABIC LIGATURE SHEEN WITH JEEM INITIAL FORM
+FD2E;FD2E;FD2E;0634 062D;0634 062D; # (ﴮ; ﴮ; ﴮ; شح; شح; ) ARABIC LIGATURE SHEEN WITH HAH INITIAL FORM
+FD2F;FD2F;FD2F;0634 062E;0634 062E; # (ﴯ; ﴯ; ﴯ; شخ; شخ; ) ARABIC LIGATURE SHEEN WITH KHAH INITIAL FORM
+FD30;FD30;FD30;0634 0645;0634 0645; # (ﴰ; ﴰ; ﴰ; شم; شم; ) ARABIC LIGATURE SHEEN WITH MEEM INITIAL FORM
+FD31;FD31;FD31;0633 0647;0633 0647; # (ﴱ; ﴱ; ﴱ; سه; سه; ) ARABIC LIGATURE SEEN WITH HEH INITIAL FORM
+FD32;FD32;FD32;0634 0647;0634 0647; # (ﴲ; ﴲ; ﴲ; شه; شه; ) ARABIC LIGATURE SHEEN WITH HEH INITIAL FORM
+FD33;FD33;FD33;0637 0645;0637 0645; # (ﴳ; ﴳ; ﴳ; طم; طم; ) ARABIC LIGATURE TAH WITH MEEM INITIAL FORM
+FD34;FD34;FD34;0633 062C;0633 062C; # (ﴴ; ﴴ; ﴴ; سج; سج; ) ARABIC LIGATURE SEEN WITH JEEM MEDIAL FORM
+FD35;FD35;FD35;0633 062D;0633 062D; # (ﴵ; ﴵ; ﴵ; سح; سح; ) ARABIC LIGATURE SEEN WITH HAH MEDIAL FORM
+FD36;FD36;FD36;0633 062E;0633 062E; # (ﴶ; ﴶ; ﴶ; سخ; سخ; ) ARABIC LIGATURE SEEN WITH KHAH MEDIAL FORM
+FD37;FD37;FD37;0634 062C;0634 062C; # (ﴷ; ﴷ; ﴷ; شج; شج; ) ARABIC LIGATURE SHEEN WITH JEEM MEDIAL FORM
+FD38;FD38;FD38;0634 062D;0634 062D; # (ﴸ; ﴸ; ﴸ; شح; شح; ) ARABIC LIGATURE SHEEN WITH HAH MEDIAL FORM
+FD39;FD39;FD39;0634 062E;0634 062E; # (ﴹ; ﴹ; ﴹ; شخ; شخ; ) ARABIC LIGATURE SHEEN WITH KHAH MEDIAL FORM
+FD3A;FD3A;FD3A;0637 0645;0637 0645; # (ﴺ; ﴺ; ﴺ; طم; طم; ) ARABIC LIGATURE TAH WITH MEEM MEDIAL FORM
+FD3B;FD3B;FD3B;0638 0645;0638 0645; # (ﴻ; ﴻ; ﴻ; ظم; ظم; ) ARABIC LIGATURE ZAH WITH MEEM MEDIAL FORM
+FD3C;FD3C;FD3C;0627 064B;0627 064B; # (ﴼ; ﴼ; ﴼ; ا◌ً; ا◌ً; ) ARABIC LIGATURE ALEF WITH FATHATAN FINAL FORM
+FD3D;FD3D;FD3D;0627 064B;0627 064B; # (ﴽ; ﴽ; ﴽ; ا◌ً; ا◌ً; ) ARABIC LIGATURE ALEF WITH FATHATAN ISOLATED FORM
+FD50;FD50;FD50;062A 062C 0645;062A 062C 0645; # (ïµ; ïµ; ïµ; تجم; تجم; ) ARABIC LIGATURE TEH WITH JEEM WITH MEEM INITIAL FORM
+FD51;FD51;FD51;062A 062D 062C;062A 062D 062C; # (ﵑ; ﵑ; ﵑ; تحج; تحج; ) ARABIC LIGATURE TEH WITH HAH WITH JEEM FINAL FORM
+FD52;FD52;FD52;062A 062D 062C;062A 062D 062C; # (ﵒ; ﵒ; ﵒ; تحج; تحج; ) ARABIC LIGATURE TEH WITH HAH WITH JEEM INITIAL FORM
+FD53;FD53;FD53;062A 062D 0645;062A 062D 0645; # (ﵓ; ﵓ; ﵓ; تحم; تحم; ) ARABIC LIGATURE TEH WITH HAH WITH MEEM INITIAL FORM
+FD54;FD54;FD54;062A 062E 0645;062A 062E 0645; # (ﵔ; ﵔ; ﵔ; تخم; تخم; ) ARABIC LIGATURE TEH WITH KHAH WITH MEEM INITIAL FORM
+FD55;FD55;FD55;062A 0645 062C;062A 0645 062C; # (ﵕ; ﵕ; ﵕ; تمج; تمج; ) ARABIC LIGATURE TEH WITH MEEM WITH JEEM INITIAL FORM
+FD56;FD56;FD56;062A 0645 062D;062A 0645 062D; # (ﵖ; ﵖ; ﵖ; تمح; تمح; ) ARABIC LIGATURE TEH WITH MEEM WITH HAH INITIAL FORM
+FD57;FD57;FD57;062A 0645 062E;062A 0645 062E; # (ﵗ; ﵗ; ﵗ; تمخ; تمخ; ) ARABIC LIGATURE TEH WITH MEEM WITH KHAH INITIAL FORM
+FD58;FD58;FD58;062C 0645 062D;062C 0645 062D; # (ﵘ; ﵘ; ﵘ; جمح; جمح; ) ARABIC LIGATURE JEEM WITH MEEM WITH HAH FINAL FORM
+FD59;FD59;FD59;062C 0645 062D;062C 0645 062D; # (ﵙ; ﵙ; ﵙ; جمح; جمح; ) ARABIC LIGATURE JEEM WITH MEEM WITH HAH INITIAL FORM
+FD5A;FD5A;FD5A;062D 0645 064A;062D 0645 064A; # (ﵚ; ﵚ; ﵚ; حمي; حمي; ) ARABIC LIGATURE HAH WITH MEEM WITH YEH FINAL FORM
+FD5B;FD5B;FD5B;062D 0645 0649;062D 0645 0649; # (ﵛ; ﵛ; ﵛ; حمى; حمى; ) ARABIC LIGATURE HAH WITH MEEM WITH ALEF MAKSURA FINAL FORM
+FD5C;FD5C;FD5C;0633 062D 062C;0633 062D 062C; # (ﵜ; ﵜ; ﵜ; سحج; سحج; ) ARABIC LIGATURE SEEN WITH HAH WITH JEEM INITIAL FORM
+FD5D;FD5D;FD5D;0633 062C 062D;0633 062C 062D; # (ïµ; ïµ; ïµ; سجح; سجح; ) ARABIC LIGATURE SEEN WITH JEEM WITH HAH INITIAL FORM
+FD5E;FD5E;FD5E;0633 062C 0649;0633 062C 0649; # (ﵞ; ﵞ; ﵞ; سجى; سجى; ) ARABIC LIGATURE SEEN WITH JEEM WITH ALEF MAKSURA FINAL FORM
+FD5F;FD5F;FD5F;0633 0645 062D;0633 0645 062D; # (ﵟ; ﵟ; ﵟ; سمح; سمح; ) ARABIC LIGATURE SEEN WITH MEEM WITH HAH FINAL FORM
+FD60;FD60;FD60;0633 0645 062D;0633 0645 062D; # (ﵠ; ﵠ; ﵠ; سمح; سمح; ) ARABIC LIGATURE SEEN WITH MEEM WITH HAH INITIAL FORM
+FD61;FD61;FD61;0633 0645 062C;0633 0645 062C; # (ﵡ; ﵡ; ﵡ; سمج; سمج; ) ARABIC LIGATURE SEEN WITH MEEM WITH JEEM INITIAL FORM
+FD62;FD62;FD62;0633 0645 0645;0633 0645 0645; # (ﵢ; ﵢ; ﵢ; سمم; سمم; ) ARABIC LIGATURE SEEN WITH MEEM WITH MEEM FINAL FORM
+FD63;FD63;FD63;0633 0645 0645;0633 0645 0645; # (ﵣ; ﵣ; ﵣ; سمم; سمم; ) ARABIC LIGATURE SEEN WITH MEEM WITH MEEM INITIAL FORM
+FD64;FD64;FD64;0635 062D 062D;0635 062D 062D; # (ﵤ; ﵤ; ﵤ; صحح; صحح; ) ARABIC LIGATURE SAD WITH HAH WITH HAH FINAL FORM
+FD65;FD65;FD65;0635 062D 062D;0635 062D 062D; # (ﵥ; ﵥ; ﵥ; صحح; صحح; ) ARABIC LIGATURE SAD WITH HAH WITH HAH INITIAL FORM
+FD66;FD66;FD66;0635 0645 0645;0635 0645 0645; # (ﵦ; ﵦ; ﵦ; صمم; صمم; ) ARABIC LIGATURE SAD WITH MEEM WITH MEEM FINAL FORM
+FD67;FD67;FD67;0634 062D 0645;0634 062D 0645; # (ﵧ; ﵧ; ﵧ; شحم; شحم; ) ARABIC LIGATURE SHEEN WITH HAH WITH MEEM FINAL FORM
+FD68;FD68;FD68;0634 062D 0645;0634 062D 0645; # (ﵨ; ﵨ; ﵨ; شحم; شحم; ) ARABIC LIGATURE SHEEN WITH HAH WITH MEEM INITIAL FORM
+FD69;FD69;FD69;0634 062C 064A;0634 062C 064A; # (ﵩ; ﵩ; ﵩ; شجي; شجي; ) ARABIC LIGATURE SHEEN WITH JEEM WITH YEH FINAL FORM
+FD6A;FD6A;FD6A;0634 0645 062E;0634 0645 062E; # (ﵪ; ﵪ; ﵪ; شمخ; شمخ; ) ARABIC LIGATURE SHEEN WITH MEEM WITH KHAH FINAL FORM
+FD6B;FD6B;FD6B;0634 0645 062E;0634 0645 062E; # (ﵫ; ﵫ; ﵫ; شمخ; شمخ; ) ARABIC LIGATURE SHEEN WITH MEEM WITH KHAH INITIAL FORM
+FD6C;FD6C;FD6C;0634 0645 0645;0634 0645 0645; # (ﵬ; ﵬ; ﵬ; شمم; شمم; ) ARABIC LIGATURE SHEEN WITH MEEM WITH MEEM FINAL FORM
+FD6D;FD6D;FD6D;0634 0645 0645;0634 0645 0645; # (ﵭ; ﵭ; ﵭ; شمم; شمم; ) ARABIC LIGATURE SHEEN WITH MEEM WITH MEEM INITIAL FORM
+FD6E;FD6E;FD6E;0636 062D 0649;0636 062D 0649; # (ﵮ; ﵮ; ﵮ; ضحى; ضحى; ) ARABIC LIGATURE DAD WITH HAH WITH ALEF MAKSURA FINAL FORM
+FD6F;FD6F;FD6F;0636 062E 0645;0636 062E 0645; # (ﵯ; ﵯ; ﵯ; ضخم; ضخم; ) ARABIC LIGATURE DAD WITH KHAH WITH MEEM FINAL FORM
+FD70;FD70;FD70;0636 062E 0645;0636 062E 0645; # (ﵰ; ﵰ; ﵰ; ضخم; ضخم; ) ARABIC LIGATURE DAD WITH KHAH WITH MEEM INITIAL FORM
+FD71;FD71;FD71;0637 0645 062D;0637 0645 062D; # (ﵱ; ﵱ; ﵱ; طمح; طمح; ) ARABIC LIGATURE TAH WITH MEEM WITH HAH FINAL FORM
+FD72;FD72;FD72;0637 0645 062D;0637 0645 062D; # (ﵲ; ﵲ; ﵲ; طمح; طمح; ) ARABIC LIGATURE TAH WITH MEEM WITH HAH INITIAL FORM
+FD73;FD73;FD73;0637 0645 0645;0637 0645 0645; # (ﵳ; ﵳ; ﵳ; طمم; طمم; ) ARABIC LIGATURE TAH WITH MEEM WITH MEEM INITIAL FORM
+FD74;FD74;FD74;0637 0645 064A;0637 0645 064A; # (ﵴ; ﵴ; ﵴ; طمي; طمي; ) ARABIC LIGATURE TAH WITH MEEM WITH YEH FINAL FORM
+FD75;FD75;FD75;0639 062C 0645;0639 062C 0645; # (ﵵ; ﵵ; ﵵ; عجم; عجم; ) ARABIC LIGATURE AIN WITH JEEM WITH MEEM FINAL FORM
+FD76;FD76;FD76;0639 0645 0645;0639 0645 0645; # (ﵶ; ﵶ; ﵶ; عمم; عمم; ) ARABIC LIGATURE AIN WITH MEEM WITH MEEM FINAL FORM
+FD77;FD77;FD77;0639 0645 0645;0639 0645 0645; # (ﵷ; ﵷ; ﵷ; عمم; عمم; ) ARABIC LIGATURE AIN WITH MEEM WITH MEEM INITIAL FORM
+FD78;FD78;FD78;0639 0645 0649;0639 0645 0649; # (ﵸ; ﵸ; ﵸ; عمى; عمى; ) ARABIC LIGATURE AIN WITH MEEM WITH ALEF MAKSURA FINAL FORM
+FD79;FD79;FD79;063A 0645 0645;063A 0645 0645; # (ﵹ; ﵹ; ﵹ; غمم; غمم; ) ARABIC LIGATURE GHAIN WITH MEEM WITH MEEM FINAL FORM
+FD7A;FD7A;FD7A;063A 0645 064A;063A 0645 064A; # (ﵺ; ﵺ; ﵺ; غمي; غمي; ) ARABIC LIGATURE GHAIN WITH MEEM WITH YEH FINAL FORM
+FD7B;FD7B;FD7B;063A 0645 0649;063A 0645 0649; # (ﵻ; ﵻ; ﵻ; غمى; غمى; ) ARABIC LIGATURE GHAIN WITH MEEM WITH ALEF MAKSURA FINAL FORM
+FD7C;FD7C;FD7C;0641 062E 0645;0641 062E 0645; # (ïµ¼; ïµ¼; ïµ¼; ÙØ®Ù…; ÙØ®Ù…; ) ARABIC LIGATURE FEH WITH KHAH WITH MEEM FINAL FORM
+FD7D;FD7D;FD7D;0641 062E 0645;0641 062E 0645; # (ïµ½; ïµ½; ïµ½; ÙØ®Ù…; ÙØ®Ù…; ) ARABIC LIGATURE FEH WITH KHAH WITH MEEM INITIAL FORM
+FD7E;FD7E;FD7E;0642 0645 062D;0642 0645 062D; # (ﵾ; ﵾ; ﵾ; قمح; قمح; ) ARABIC LIGATURE QAF WITH MEEM WITH HAH FINAL FORM
+FD7F;FD7F;FD7F;0642 0645 0645;0642 0645 0645; # (ﵿ; ﵿ; ﵿ; قمم; قمم; ) ARABIC LIGATURE QAF WITH MEEM WITH MEEM FINAL FORM
+FD80;FD80;FD80;0644 062D 0645;0644 062D 0645; # (ﶀ; ﶀ; ﶀ; لحم; لحم; ) ARABIC LIGATURE LAM WITH HAH WITH MEEM FINAL FORM
+FD81;FD81;FD81;0644 062D 064A;0644 062D 064A; # (ï¶; ï¶; ï¶; لحي; لحي; ) ARABIC LIGATURE LAM WITH HAH WITH YEH FINAL FORM
+FD82;FD82;FD82;0644 062D 0649;0644 062D 0649; # (ﶂ; ﶂ; ﶂ; لحى; لحى; ) ARABIC LIGATURE LAM WITH HAH WITH ALEF MAKSURA FINAL FORM
+FD83;FD83;FD83;0644 062C 062C;0644 062C 062C; # (ﶃ; ﶃ; ﶃ; لجج; لجج; ) ARABIC LIGATURE LAM WITH JEEM WITH JEEM INITIAL FORM
+FD84;FD84;FD84;0644 062C 062C;0644 062C 062C; # (ﶄ; ﶄ; ﶄ; لجج; لجج; ) ARABIC LIGATURE LAM WITH JEEM WITH JEEM FINAL FORM
+FD85;FD85;FD85;0644 062E 0645;0644 062E 0645; # (ﶅ; ﶅ; ﶅ; لخم; لخم; ) ARABIC LIGATURE LAM WITH KHAH WITH MEEM FINAL FORM
+FD86;FD86;FD86;0644 062E 0645;0644 062E 0645; # (ﶆ; ﶆ; ﶆ; لخم; لخم; ) ARABIC LIGATURE LAM WITH KHAH WITH MEEM INITIAL FORM
+FD87;FD87;FD87;0644 0645 062D;0644 0645 062D; # (ﶇ; ﶇ; ﶇ; لمح; لمح; ) ARABIC LIGATURE LAM WITH MEEM WITH HAH FINAL FORM
+FD88;FD88;FD88;0644 0645 062D;0644 0645 062D; # (ﶈ; ﶈ; ﶈ; لمح; لمح; ) ARABIC LIGATURE LAM WITH MEEM WITH HAH INITIAL FORM
+FD89;FD89;FD89;0645 062D 062C;0645 062D 062C; # (ﶉ; ﶉ; ﶉ; محج; محج; ) ARABIC LIGATURE MEEM WITH HAH WITH JEEM INITIAL FORM
+FD8A;FD8A;FD8A;0645 062D 0645;0645 062D 0645; # (ﶊ; ﶊ; ﶊ; محم; محم; ) ARABIC LIGATURE MEEM WITH HAH WITH MEEM INITIAL FORM
+FD8B;FD8B;FD8B;0645 062D 064A;0645 062D 064A; # (ﶋ; ﶋ; ﶋ; محي; محي; ) ARABIC LIGATURE MEEM WITH HAH WITH YEH FINAL FORM
+FD8C;FD8C;FD8C;0645 062C 062D;0645 062C 062D; # (ﶌ; ﶌ; ﶌ; مجح; مجح; ) ARABIC LIGATURE MEEM WITH JEEM WITH HAH INITIAL FORM
+FD8D;FD8D;FD8D;0645 062C 0645;0645 062C 0645; # (ï¶; ï¶; ï¶; مجم; مجم; ) ARABIC LIGATURE MEEM WITH JEEM WITH MEEM INITIAL FORM
+FD8E;FD8E;FD8E;0645 062E 062C;0645 062E 062C; # (ﶎ; ﶎ; ﶎ; مخج; مخج; ) ARABIC LIGATURE MEEM WITH KHAH WITH JEEM INITIAL FORM
+FD8F;FD8F;FD8F;0645 062E 0645;0645 062E 0645; # (ï¶; ï¶; ï¶; مخم; مخم; ) ARABIC LIGATURE MEEM WITH KHAH WITH MEEM INITIAL FORM
+FD92;FD92;FD92;0645 062C 062E;0645 062C 062E; # (ﶒ; ﶒ; ﶒ; مجخ; مجخ; ) ARABIC LIGATURE MEEM WITH JEEM WITH KHAH INITIAL FORM
+FD93;FD93;FD93;0647 0645 062C;0647 0645 062C; # (ﶓ; ﶓ; ﶓ; همج; همج; ) ARABIC LIGATURE HEH WITH MEEM WITH JEEM INITIAL FORM
+FD94;FD94;FD94;0647 0645 0645;0647 0645 0645; # (ﶔ; ﶔ; ﶔ; همم; همم; ) ARABIC LIGATURE HEH WITH MEEM WITH MEEM INITIAL FORM
+FD95;FD95;FD95;0646 062D 0645;0646 062D 0645; # (ﶕ; ﶕ; ﶕ; نحم; نحم; ) ARABIC LIGATURE NOON WITH HAH WITH MEEM INITIAL FORM
+FD96;FD96;FD96;0646 062D 0649;0646 062D 0649; # (ﶖ; ﶖ; ﶖ; نحى; نحى; ) ARABIC LIGATURE NOON WITH HAH WITH ALEF MAKSURA FINAL FORM
+FD97;FD97;FD97;0646 062C 0645;0646 062C 0645; # (ﶗ; ﶗ; ﶗ; نجم; نجم; ) ARABIC LIGATURE NOON WITH JEEM WITH MEEM FINAL FORM
+FD98;FD98;FD98;0646 062C 0645;0646 062C 0645; # (ﶘ; ﶘ; ﶘ; نجم; نجم; ) ARABIC LIGATURE NOON WITH JEEM WITH MEEM INITIAL FORM
+FD99;FD99;FD99;0646 062C 0649;0646 062C 0649; # (ﶙ; ﶙ; ﶙ; نجى; نجى; ) ARABIC LIGATURE NOON WITH JEEM WITH ALEF MAKSURA FINAL FORM
+FD9A;FD9A;FD9A;0646 0645 064A;0646 0645 064A; # (ﶚ; ﶚ; ﶚ; نمي; نمي; ) ARABIC LIGATURE NOON WITH MEEM WITH YEH FINAL FORM
+FD9B;FD9B;FD9B;0646 0645 0649;0646 0645 0649; # (ﶛ; ﶛ; ﶛ; نمى; نمى; ) ARABIC LIGATURE NOON WITH MEEM WITH ALEF MAKSURA FINAL FORM
+FD9C;FD9C;FD9C;064A 0645 0645;064A 0645 0645; # (ﶜ; ﶜ; ﶜ; يمم; يمم; ) ARABIC LIGATURE YEH WITH MEEM WITH MEEM FINAL FORM
+FD9D;FD9D;FD9D;064A 0645 0645;064A 0645 0645; # (ï¶; ï¶; ï¶; يمم; يمم; ) ARABIC LIGATURE YEH WITH MEEM WITH MEEM INITIAL FORM
+FD9E;FD9E;FD9E;0628 062E 064A;0628 062E 064A; # (ﶞ; ﶞ; ﶞ; بخي; بخي; ) ARABIC LIGATURE BEH WITH KHAH WITH YEH FINAL FORM
+FD9F;FD9F;FD9F;062A 062C 064A;062A 062C 064A; # (ﶟ; ﶟ; ﶟ; تجي; تجي; ) ARABIC LIGATURE TEH WITH JEEM WITH YEH FINAL FORM
+FDA0;FDA0;FDA0;062A 062C 0649;062A 062C 0649; # (ﶠ; ﶠ; ﶠ; تجى; تجى; ) ARABIC LIGATURE TEH WITH JEEM WITH ALEF MAKSURA FINAL FORM
+FDA1;FDA1;FDA1;062A 062E 064A;062A 062E 064A; # (ﶡ; ﶡ; ﶡ; تخي; تخي; ) ARABIC LIGATURE TEH WITH KHAH WITH YEH FINAL FORM
+FDA2;FDA2;FDA2;062A 062E 0649;062A 062E 0649; # (ﶢ; ﶢ; ﶢ; تخى; تخى; ) ARABIC LIGATURE TEH WITH KHAH WITH ALEF MAKSURA FINAL FORM
+FDA3;FDA3;FDA3;062A 0645 064A;062A 0645 064A; # (ﶣ; ﶣ; ﶣ; تمي; تمي; ) ARABIC LIGATURE TEH WITH MEEM WITH YEH FINAL FORM
+FDA4;FDA4;FDA4;062A 0645 0649;062A 0645 0649; # (ﶤ; ﶤ; ﶤ; تمى; تمى; ) ARABIC LIGATURE TEH WITH MEEM WITH ALEF MAKSURA FINAL FORM
+FDA5;FDA5;FDA5;062C 0645 064A;062C 0645 064A; # (ﶥ; ﶥ; ﶥ; جمي; جمي; ) ARABIC LIGATURE JEEM WITH MEEM WITH YEH FINAL FORM
+FDA6;FDA6;FDA6;062C 062D 0649;062C 062D 0649; # (ﶦ; ﶦ; ﶦ; جحى; جحى; ) ARABIC LIGATURE JEEM WITH HAH WITH ALEF MAKSURA FINAL FORM
+FDA7;FDA7;FDA7;062C 0645 0649;062C 0645 0649; # (ﶧ; ﶧ; ﶧ; جمى; جمى; ) ARABIC LIGATURE JEEM WITH MEEM WITH ALEF MAKSURA FINAL FORM
+FDA8;FDA8;FDA8;0633 062E 0649;0633 062E 0649; # (ﶨ; ﶨ; ﶨ; سخى; سخى; ) ARABIC LIGATURE SEEN WITH KHAH WITH ALEF MAKSURA FINAL FORM
+FDA9;FDA9;FDA9;0635 062D 064A;0635 062D 064A; # (ﶩ; ﶩ; ﶩ; صحي; صحي; ) ARABIC LIGATURE SAD WITH HAH WITH YEH FINAL FORM
+FDAA;FDAA;FDAA;0634 062D 064A;0634 062D 064A; # (ﶪ; ﶪ; ﶪ; شحي; شحي; ) ARABIC LIGATURE SHEEN WITH HAH WITH YEH FINAL FORM
+FDAB;FDAB;FDAB;0636 062D 064A;0636 062D 064A; # (ﶫ; ﶫ; ﶫ; ضحي; ضحي; ) ARABIC LIGATURE DAD WITH HAH WITH YEH FINAL FORM
+FDAC;FDAC;FDAC;0644 062C 064A;0644 062C 064A; # (ﶬ; ﶬ; ﶬ; لجي; لجي; ) ARABIC LIGATURE LAM WITH JEEM WITH YEH FINAL FORM
+FDAD;FDAD;FDAD;0644 0645 064A;0644 0645 064A; # (ﶭ; ﶭ; ﶭ; لمي; لمي; ) ARABIC LIGATURE LAM WITH MEEM WITH YEH FINAL FORM
+FDAE;FDAE;FDAE;064A 062D 064A;064A 062D 064A; # (ﶮ; ﶮ; ﶮ; يحي; يحي; ) ARABIC LIGATURE YEH WITH HAH WITH YEH FINAL FORM
+FDAF;FDAF;FDAF;064A 062C 064A;064A 062C 064A; # (ﶯ; ﶯ; ﶯ; يجي; يجي; ) ARABIC LIGATURE YEH WITH JEEM WITH YEH FINAL FORM
+FDB0;FDB0;FDB0;064A 0645 064A;064A 0645 064A; # (ﶰ; ﶰ; ﶰ; يمي; يمي; ) ARABIC LIGATURE YEH WITH MEEM WITH YEH FINAL FORM
+FDB1;FDB1;FDB1;0645 0645 064A;0645 0645 064A; # (ﶱ; ﶱ; ﶱ; ممي; ممي; ) ARABIC LIGATURE MEEM WITH MEEM WITH YEH FINAL FORM
+FDB2;FDB2;FDB2;0642 0645 064A;0642 0645 064A; # (ﶲ; ﶲ; ﶲ; قمي; قمي; ) ARABIC LIGATURE QAF WITH MEEM WITH YEH FINAL FORM
+FDB3;FDB3;FDB3;0646 062D 064A;0646 062D 064A; # (ﶳ; ﶳ; ﶳ; نحي; نحي; ) ARABIC LIGATURE NOON WITH HAH WITH YEH FINAL FORM
+FDB4;FDB4;FDB4;0642 0645 062D;0642 0645 062D; # (ﶴ; ﶴ; ﶴ; قمح; قمح; ) ARABIC LIGATURE QAF WITH MEEM WITH HAH INITIAL FORM
+FDB5;FDB5;FDB5;0644 062D 0645;0644 062D 0645; # (ﶵ; ﶵ; ﶵ; لحم; لحم; ) ARABIC LIGATURE LAM WITH HAH WITH MEEM INITIAL FORM
+FDB6;FDB6;FDB6;0639 0645 064A;0639 0645 064A; # (ﶶ; ﶶ; ﶶ; عمي; عمي; ) ARABIC LIGATURE AIN WITH MEEM WITH YEH FINAL FORM
+FDB7;FDB7;FDB7;0643 0645 064A;0643 0645 064A; # (ﶷ; ﶷ; ﶷ; كمي; كمي; ) ARABIC LIGATURE KAF WITH MEEM WITH YEH FINAL FORM
+FDB8;FDB8;FDB8;0646 062C 062D;0646 062C 062D; # (ﶸ; ﶸ; ﶸ; نجح; نجح; ) ARABIC LIGATURE NOON WITH JEEM WITH HAH INITIAL FORM
+FDB9;FDB9;FDB9;0645 062E 064A;0645 062E 064A; # (ﶹ; ﶹ; ﶹ; مخي; مخي; ) ARABIC LIGATURE MEEM WITH KHAH WITH YEH FINAL FORM
+FDBA;FDBA;FDBA;0644 062C 0645;0644 062C 0645; # (ﶺ; ﶺ; ﶺ; لجم; لجم; ) ARABIC LIGATURE LAM WITH JEEM WITH MEEM INITIAL FORM
+FDBB;FDBB;FDBB;0643 0645 0645;0643 0645 0645; # (ﶻ; ﶻ; ﶻ; كمم; كمم; ) ARABIC LIGATURE KAF WITH MEEM WITH MEEM FINAL FORM
+FDBC;FDBC;FDBC;0644 062C 0645;0644 062C 0645; # (ﶼ; ﶼ; ﶼ; لجم; لجم; ) ARABIC LIGATURE LAM WITH JEEM WITH MEEM FINAL FORM
+FDBD;FDBD;FDBD;0646 062C 062D;0646 062C 062D; # (ﶽ; ﶽ; ﶽ; نجح; نجح; ) ARABIC LIGATURE NOON WITH JEEM WITH HAH FINAL FORM
+FDBE;FDBE;FDBE;062C 062D 064A;062C 062D 064A; # (ﶾ; ﶾ; ﶾ; جحي; جحي; ) ARABIC LIGATURE JEEM WITH HAH WITH YEH FINAL FORM
+FDBF;FDBF;FDBF;062D 062C 064A;062D 062C 064A; # (ﶿ; ﶿ; ﶿ; حجي; حجي; ) ARABIC LIGATURE HAH WITH JEEM WITH YEH FINAL FORM
+FDC0;FDC0;FDC0;0645 062C 064A;0645 062C 064A; # (ﷀ; ﷀ; ﷀ; مجي; مجي; ) ARABIC LIGATURE MEEM WITH JEEM WITH YEH FINAL FORM
+FDC1;FDC1;FDC1;0641 0645 064A;0641 0645 064A; # (ï·; ï·; ï·; Ùمي; Ùمي; ) ARABIC LIGATURE FEH WITH MEEM WITH YEH FINAL FORM
+FDC2;FDC2;FDC2;0628 062D 064A;0628 062D 064A; # (ﷂ; ﷂ; ﷂ; بحي; بحي; ) ARABIC LIGATURE BEH WITH HAH WITH YEH FINAL FORM
+FDC3;FDC3;FDC3;0643 0645 0645;0643 0645 0645; # (ﷃ; ﷃ; ﷃ; كمم; كمم; ) ARABIC LIGATURE KAF WITH MEEM WITH MEEM INITIAL FORM
+FDC4;FDC4;FDC4;0639 062C 0645;0639 062C 0645; # (ﷄ; ﷄ; ﷄ; عجم; عجم; ) ARABIC LIGATURE AIN WITH JEEM WITH MEEM INITIAL FORM
+FDC5;FDC5;FDC5;0635 0645 0645;0635 0645 0645; # (ﷅ; ﷅ; ﷅ; صمم; صمم; ) ARABIC LIGATURE SAD WITH MEEM WITH MEEM INITIAL FORM
+FDC6;FDC6;FDC6;0633 062E 064A;0633 062E 064A; # (ﷆ; ﷆ; ﷆ; سخي; سخي; ) ARABIC LIGATURE SEEN WITH KHAH WITH YEH FINAL FORM
+FDC7;FDC7;FDC7;0646 062C 064A;0646 062C 064A; # (ﷇ; ﷇ; ﷇ; نجي; نجي; ) ARABIC LIGATURE NOON WITH JEEM WITH YEH FINAL FORM
+FDF0;FDF0;FDF0;0635 0644 06D2;0635 0644 06D2; # (ﷰ; ﷰ; ﷰ; صلے; صلے; ) ARABIC LIGATURE SALLA USED AS KORANIC STOP SIGN ISOLATED FORM
+FDF1;FDF1;FDF1;0642 0644 06D2;0642 0644 06D2; # (ﷱ; ﷱ; ﷱ; قلے; قلے; ) ARABIC LIGATURE QALA USED AS KORANIC STOP SIGN ISOLATED FORM
+FDF2;FDF2;FDF2;0627 0644 0644 0647;0627 0644 0644 0647; # (ﷲ; ﷲ; ﷲ; الله; الله; ) ARABIC LIGATURE ALLAH ISOLATED FORM
+FDF3;FDF3;FDF3;0627 0643 0628 0631;0627 0643 0628 0631; # (ﷳ; ﷳ; ﷳ; اكبر; اكبر; ) ARABIC LIGATURE AKBAR ISOLATED FORM
+FDF4;FDF4;FDF4;0645 062D 0645 062F;0645 062D 0645 062F; # (ﷴ; ﷴ; ﷴ; محمد; محمد; ) ARABIC LIGATURE MOHAMMAD ISOLATED FORM
+FDF5;FDF5;FDF5;0635 0644 0639 0645;0635 0644 0639 0645; # (ﷵ; ﷵ; ﷵ; صلعم; صلعم; ) ARABIC LIGATURE SALAM ISOLATED FORM
+FDF6;FDF6;FDF6;0631 0633 0648 0644;0631 0633 0648 0644; # (ﷶ; ﷶ; ﷶ; رسول; رسول; ) ARABIC LIGATURE RASOUL ISOLATED FORM
+FDF7;FDF7;FDF7;0639 0644 064A 0647;0639 0644 064A 0647; # (ﷷ; ﷷ; ﷷ; عليه; عليه; ) ARABIC LIGATURE ALAYHE ISOLATED FORM
+FDF8;FDF8;FDF8;0648 0633 0644 0645;0648 0633 0644 0645; # (ﷸ; ﷸ; ﷸ; وسلم; وسلم; ) ARABIC LIGATURE WASALLAM ISOLATED FORM
+FDF9;FDF9;FDF9;0635 0644 0649;0635 0644 0649; # (ﷹ; ﷹ; ﷹ; صلى; صلى; ) ARABIC LIGATURE SALLA ISOLATED FORM
+FDFA;FDFA;FDFA;0635 0644 0649 0020 0627 0644 0644 0647 0020 0639 0644 064A 0647 0020 0648 0633 0644 0645;0635 0644 0649 0020 0627 0644 0644 0647 0020 0639 0644 064A 0647 0020 0648 0633 0644 0645; # (ﷺ; ﷺ; ﷺ; صلى الله عليه وسلم; صلى الله عليه وسلم; ) ARABIC LIGATURE SALLALLAHOU ALAYHE WASALLAM
+FDFB;FDFB;FDFB;062C 0644 0020 062C 0644 0627 0644 0647;062C 0644 0020 062C 0644 0627 0644 0647; # (ﷻ; ﷻ; ﷻ; جل جلاله; جل جلاله; ) ARABIC LIGATURE JALLAJALALOUHOU
+FDFC;FDFC;FDFC;0631 06CC 0627 0644;0631 06CC 0627 0644; # (﷼; ﷼; ﷼; ریال; ریال; ) RIAL SIGN
+FE30;FE30;FE30;002E 002E;002E 002E; # (︰; ︰; ︰; ..; ..; ) PRESENTATION FORM FOR VERTICAL TWO DOT LEADER
+FE31;FE31;FE31;2014;2014; # (︱; ︱; ︱; —; —; ) PRESENTATION FORM FOR VERTICAL EM DASH
+FE32;FE32;FE32;2013;2013; # (︲; ︲; ︲; –; –; ) PRESENTATION FORM FOR VERTICAL EN DASH
+FE33;FE33;FE33;005F;005F; # (︳; ︳; ︳; _; _; ) PRESENTATION FORM FOR VERTICAL LOW LINE
+FE34;FE34;FE34;005F;005F; # (︴; ︴; ︴; _; _; ) PRESENTATION FORM FOR VERTICAL WAVY LOW LINE
+FE35;FE35;FE35;0028;0028; # (︵; ︵; ︵; (; (; ) PRESENTATION FORM FOR VERTICAL LEFT PARENTHESIS
+FE36;FE36;FE36;0029;0029; # (︶; ︶; ︶; ); ); ) PRESENTATION FORM FOR VERTICAL RIGHT PARENTHESIS
+FE37;FE37;FE37;007B;007B; # (︷; ︷; ︷; {; {; ) PRESENTATION FORM FOR VERTICAL LEFT CURLY BRACKET
+FE38;FE38;FE38;007D;007D; # (︸; ︸; ︸; }; }; ) PRESENTATION FORM FOR VERTICAL RIGHT CURLY BRACKET
+FE39;FE39;FE39;3014;3014; # (︹; ︹; ︹; 〔; 〔; ) PRESENTATION FORM FOR VERTICAL LEFT TORTOISE SHELL BRACKET
+FE3A;FE3A;FE3A;3015;3015; # (︺; ︺; ︺; 〕; 〕; ) PRESENTATION FORM FOR VERTICAL RIGHT TORTOISE SHELL BRACKET
+FE3B;FE3B;FE3B;3010;3010; # (︻; ︻; ︻; ã€; ã€; ) PRESENTATION FORM FOR VERTICAL LEFT BLACK LENTICULAR BRACKET
+FE3C;FE3C;FE3C;3011;3011; # (︼; ︼; ︼; 】; 】; ) PRESENTATION FORM FOR VERTICAL RIGHT BLACK LENTICULAR BRACKET
+FE3D;FE3D;FE3D;300A;300A; # (︽; ︽; ︽; 《; 《; ) PRESENTATION FORM FOR VERTICAL LEFT DOUBLE ANGLE BRACKET
+FE3E;FE3E;FE3E;300B;300B; # (︾; ︾; ︾; 》; 》; ) PRESENTATION FORM FOR VERTICAL RIGHT DOUBLE ANGLE BRACKET
+FE3F;FE3F;FE3F;3008;3008; # (︿; ︿; ︿; 〈; 〈; ) PRESENTATION FORM FOR VERTICAL LEFT ANGLE BRACKET
+FE40;FE40;FE40;3009;3009; # (﹀; ﹀; ﹀; 〉; 〉; ) PRESENTATION FORM FOR VERTICAL RIGHT ANGLE BRACKET
+FE41;FE41;FE41;300C;300C; # (ï¹; ï¹; ï¹; 「; 「; ) PRESENTATION FORM FOR VERTICAL LEFT CORNER BRACKET
+FE42;FE42;FE42;300D;300D; # (﹂; ﹂; ﹂; ã€; ã€; ) PRESENTATION FORM FOR VERTICAL RIGHT CORNER BRACKET
+FE43;FE43;FE43;300E;300E; # (﹃; ﹃; ﹃; 『; 『; ) PRESENTATION FORM FOR VERTICAL LEFT WHITE CORNER BRACKET
+FE44;FE44;FE44;300F;300F; # (﹄; ﹄; ﹄; ã€; ã€; ) PRESENTATION FORM FOR VERTICAL RIGHT WHITE CORNER BRACKET
+FE47;FE47;FE47;005B;005B; # (﹇; ﹇; ﹇; [; [; ) PRESENTATION FORM FOR VERTICAL LEFT SQUARE BRACKET
+FE48;FE48;FE48;005D;005D; # (﹈; ﹈; ﹈; ]; ]; ) PRESENTATION FORM FOR VERTICAL RIGHT SQUARE BRACKET
+FE49;FE49;FE49;0020 0305;0020 0305; # (﹉; ﹉; ﹉; ◌̅; ◌̅; ) DASHED OVERLINE
+FE4A;FE4A;FE4A;0020 0305;0020 0305; # (﹊; ﹊; ﹊; ◌̅; ◌̅; ) CENTRELINE OVERLINE
+FE4B;FE4B;FE4B;0020 0305;0020 0305; # (﹋; ﹋; ﹋; ◌̅; ◌̅; ) WAVY OVERLINE
+FE4C;FE4C;FE4C;0020 0305;0020 0305; # (﹌; ﹌; ﹌; ◌̅; ◌̅; ) DOUBLE WAVY OVERLINE
+FE4D;FE4D;FE4D;005F;005F; # (ï¹; ï¹; ï¹; _; _; ) DASHED LOW LINE
+FE4E;FE4E;FE4E;005F;005F; # (﹎; ﹎; ﹎; _; _; ) CENTRELINE LOW LINE
+FE4F;FE4F;FE4F;005F;005F; # (ï¹; ï¹; ï¹; _; _; ) WAVY LOW LINE
+FE50;FE50;FE50;002C;002C; # (ï¹; ï¹; ï¹; ,; ,; ) SMALL COMMA
+FE51;FE51;FE51;3001;3001; # (﹑; ﹑; ﹑; ã€; ã€; ) SMALL IDEOGRAPHIC COMMA
+FE52;FE52;FE52;002E;002E; # (ï¹’; ï¹’; ï¹’; .; .; ) SMALL FULL STOP
+FE54;FE54;FE54;003B;003B; # (ï¹”; ï¹”; ï¹”; ;; ;; ) SMALL SEMICOLON
+FE55;FE55;FE55;003A;003A; # (﹕; ﹕; ﹕; :; :; ) SMALL COLON
+FE56;FE56;FE56;003F;003F; # (ï¹–; ï¹–; ï¹–; ?; ?; ) SMALL QUESTION MARK
+FE57;FE57;FE57;0021;0021; # (ï¹—; ï¹—; ï¹—; !; !; ) SMALL EXCLAMATION MARK
+FE58;FE58;FE58;2014;2014; # (﹘; ﹘; ﹘; —; —; ) SMALL EM DASH
+FE59;FE59;FE59;0028;0028; # (ï¹™; ï¹™; ï¹™; (; (; ) SMALL LEFT PARENTHESIS
+FE5A;FE5A;FE5A;0029;0029; # (﹚; ﹚; ﹚; ); ); ) SMALL RIGHT PARENTHESIS
+FE5B;FE5B;FE5B;007B;007B; # (ï¹›; ï¹›; ï¹›; {; {; ) SMALL LEFT CURLY BRACKET
+FE5C;FE5C;FE5C;007D;007D; # (﹜; ﹜; ﹜; }; }; ) SMALL RIGHT CURLY BRACKET
+FE5D;FE5D;FE5D;3014;3014; # (ï¹; ï¹; ï¹; 〔; 〔; ) SMALL LEFT TORTOISE SHELL BRACKET
+FE5E;FE5E;FE5E;3015;3015; # (﹞; ﹞; ﹞; 〕; 〕; ) SMALL RIGHT TORTOISE SHELL BRACKET
+FE5F;FE5F;FE5F;0023;0023; # (﹟; ﹟; ﹟; #; #; ) SMALL NUMBER SIGN
+FE60;FE60;FE60;0026;0026; # (ï¹ ; ï¹ ; ï¹ ; &; &; ) SMALL AMPERSAND
+FE61;FE61;FE61;002A;002A; # (﹡; ﹡; ﹡; *; *; ) SMALL ASTERISK
+FE62;FE62;FE62;002B;002B; # (ï¹¢; ï¹¢; ï¹¢; +; +; ) SMALL PLUS SIGN
+FE63;FE63;FE63;002D;002D; # (ï¹£; ï¹£; ï¹£; -; -; ) SMALL HYPHEN-MINUS
+FE64;FE64;FE64;003C;003C; # (﹤; ﹤; ﹤; <; <; ) SMALL LESS-THAN SIGN
+FE65;FE65;FE65;003E;003E; # (ï¹¥; ï¹¥; ï¹¥; >; >; ) SMALL GREATER-THAN SIGN
+FE66;FE66;FE66;003D;003D; # (﹦; ﹦; ﹦; =; =; ) SMALL EQUALS SIGN
+FE68;FE68;FE68;005C;005C; # (﹨; ﹨; ﹨; \; \; ) SMALL REVERSE SOLIDUS
+FE69;FE69;FE69;0024;0024; # (﹩; ﹩; ﹩; $; $; ) SMALL DOLLAR SIGN
+FE6A;FE6A;FE6A;0025;0025; # (﹪; ﹪; ﹪; %; %; ) SMALL PERCENT SIGN
+FE6B;FE6B;FE6B;0040;0040; # (﹫; ﹫; ﹫; @; @; ) SMALL COMMERCIAL AT
+FE70;FE70;FE70;0020 064B;0020 064B; # (ﹰ; ﹰ; ﹰ; ◌ً; ◌ً; ) ARABIC FATHATAN ISOLATED FORM
+FE71;FE71;FE71;0640 064B;0640 064B; # (ﹱ; ﹱ; ﹱ; ـ◌ً; ـ◌ً; ) ARABIC TATWEEL WITH FATHATAN ABOVE
+FE72;FE72;FE72;0020 064C;0020 064C; # (ﹲ; ﹲ; ﹲ; ◌ٌ; ◌ٌ; ) ARABIC DAMMATAN ISOLATED FORM
+FE74;FE74;FE74;0020 064D;0020 064D; # (ï¹´; ï¹´; ï¹´; â—ŒÙ; â—ŒÙ; ) ARABIC KASRATAN ISOLATED FORM
+FE76;FE76;FE76;0020 064E;0020 064E; # (ﹶ; ﹶ; ﹶ; ◌َ; ◌َ; ) ARABIC FATHA ISOLATED FORM
+FE77;FE77;FE77;0640 064E;0640 064E; # (ﹷ; ﹷ; ﹷ; ـ◌َ; ـ◌َ; ) ARABIC FATHA MEDIAL FORM
+FE78;FE78;FE78;0020 064F;0020 064F; # (ﹸ; ﹸ; ﹸ; â—ŒÙ; â—ŒÙ; ) ARABIC DAMMA ISOLATED FORM
+FE79;FE79;FE79;0640 064F;0640 064F; # (ï¹¹; ï¹¹; ï¹¹; ـ◌Ù; ـ◌Ù; ) ARABIC DAMMA MEDIAL FORM
+FE7A;FE7A;FE7A;0020 0650;0020 0650; # (ﹺ; ﹺ; ﹺ; â—ŒÙ; â—ŒÙ; ) ARABIC KASRA ISOLATED FORM
+FE7B;FE7B;FE7B;0640 0650;0640 0650; # (ï¹»; ï¹»; ï¹»; ـ◌Ù; ـ◌Ù; ) ARABIC KASRA MEDIAL FORM
+FE7C;FE7C;FE7C;0020 0651;0020 0651; # (ﹼ; ﹼ; ﹼ; ◌ّ; ◌ّ; ) ARABIC SHADDA ISOLATED FORM
+FE7D;FE7D;FE7D;0640 0651;0640 0651; # (ﹽ; ﹽ; ﹽ; ـ◌ّ; ـ◌ّ; ) ARABIC SHADDA MEDIAL FORM
+FE7E;FE7E;FE7E;0020 0652;0020 0652; # (ﹾ; ﹾ; ﹾ; ◌ْ; ◌ْ; ) ARABIC SUKUN ISOLATED FORM
+FE7F;FE7F;FE7F;0640 0652;0640 0652; # (ﹿ; ﹿ; ﹿ; ـ◌ْ; ـ◌ْ; ) ARABIC SUKUN MEDIAL FORM
+FE80;FE80;FE80;0621;0621; # (ﺀ; ﺀ; ﺀ; ء; ء; ) ARABIC LETTER HAMZA ISOLATED FORM
+FE81;FE81;FE81;0622;0627 0653; # (ïº; ïº; ïº; Ø¢; ا◌ٓ; ) ARABIC LETTER ALEF WITH MADDA ABOVE ISOLATED FORM
+FE82;FE82;FE82;0622;0627 0653; # (ﺂ; ﺂ; ﺂ; آ; ا◌ٓ; ) ARABIC LETTER ALEF WITH MADDA ABOVE FINAL FORM
+FE83;FE83;FE83;0623;0627 0654; # (ﺃ; ﺃ; ﺃ; أ; ا◌ٔ; ) ARABIC LETTER ALEF WITH HAMZA ABOVE ISOLATED FORM
+FE84;FE84;FE84;0623;0627 0654; # (ﺄ; ﺄ; ﺄ; أ; ا◌ٔ; ) ARABIC LETTER ALEF WITH HAMZA ABOVE FINAL FORM
+FE85;FE85;FE85;0624;0648 0654; # (ﺅ; ﺅ; ﺅ; ؤ; و◌ٔ; ) ARABIC LETTER WAW WITH HAMZA ABOVE ISOLATED FORM
+FE86;FE86;FE86;0624;0648 0654; # (ﺆ; ﺆ; ﺆ; ؤ; و◌ٔ; ) ARABIC LETTER WAW WITH HAMZA ABOVE FINAL FORM
+FE87;FE87;FE87;0625;0627 0655; # (ﺇ; ﺇ; ﺇ; إ; ا◌ٕ; ) ARABIC LETTER ALEF WITH HAMZA BELOW ISOLATED FORM
+FE88;FE88;FE88;0625;0627 0655; # (ﺈ; ﺈ; ﺈ; إ; ا◌ٕ; ) ARABIC LETTER ALEF WITH HAMZA BELOW FINAL FORM
+FE89;FE89;FE89;0626;064A 0654; # (ﺉ; ﺉ; ﺉ; ئ; ي◌ٔ; ) ARABIC LETTER YEH WITH HAMZA ABOVE ISOLATED FORM
+FE8A;FE8A;FE8A;0626;064A 0654; # (ﺊ; ﺊ; ﺊ; ئ; ي◌ٔ; ) ARABIC LETTER YEH WITH HAMZA ABOVE FINAL FORM
+FE8B;FE8B;FE8B;0626;064A 0654; # (ﺋ; ﺋ; ﺋ; ئ; ي◌ٔ; ) ARABIC LETTER YEH WITH HAMZA ABOVE INITIAL FORM
+FE8C;FE8C;FE8C;0626;064A 0654; # (ﺌ; ﺌ; ﺌ; ئ; ي◌ٔ; ) ARABIC LETTER YEH WITH HAMZA ABOVE MEDIAL FORM
+FE8D;FE8D;FE8D;0627;0627; # (ïº; ïº; ïº; ا; ا; ) ARABIC LETTER ALEF ISOLATED FORM
+FE8E;FE8E;FE8E;0627;0627; # (ﺎ; ﺎ; ﺎ; ا; ا; ) ARABIC LETTER ALEF FINAL FORM
+FE8F;FE8F;FE8F;0628;0628; # (ïº; ïº; ïº; ب; ب; ) ARABIC LETTER BEH ISOLATED FORM
+FE90;FE90;FE90;0628;0628; # (ïº; ïº; ïº; ب; ب; ) ARABIC LETTER BEH FINAL FORM
+FE91;FE91;FE91;0628;0628; # (ﺑ; ﺑ; ﺑ; ب; ب; ) ARABIC LETTER BEH INITIAL FORM
+FE92;FE92;FE92;0628;0628; # (ﺒ; ﺒ; ﺒ; ب; ب; ) ARABIC LETTER BEH MEDIAL FORM
+FE93;FE93;FE93;0629;0629; # (ﺓ; ﺓ; ﺓ; ة; ة; ) ARABIC LETTER TEH MARBUTA ISOLATED FORM
+FE94;FE94;FE94;0629;0629; # (ﺔ; ﺔ; ﺔ; ة; ة; ) ARABIC LETTER TEH MARBUTA FINAL FORM
+FE95;FE95;FE95;062A;062A; # (ﺕ; ﺕ; ﺕ; ت; ت; ) ARABIC LETTER TEH ISOLATED FORM
+FE96;FE96;FE96;062A;062A; # (ﺖ; ﺖ; ﺖ; ت; ت; ) ARABIC LETTER TEH FINAL FORM
+FE97;FE97;FE97;062A;062A; # (ﺗ; ﺗ; ﺗ; ت; ت; ) ARABIC LETTER TEH INITIAL FORM
+FE98;FE98;FE98;062A;062A; # (ﺘ; ﺘ; ﺘ; ت; ت; ) ARABIC LETTER TEH MEDIAL FORM
+FE99;FE99;FE99;062B;062B; # (ﺙ; ﺙ; ﺙ; ث; ث; ) ARABIC LETTER THEH ISOLATED FORM
+FE9A;FE9A;FE9A;062B;062B; # (ﺚ; ﺚ; ﺚ; ث; ث; ) ARABIC LETTER THEH FINAL FORM
+FE9B;FE9B;FE9B;062B;062B; # (ﺛ; ﺛ; ﺛ; ث; ث; ) ARABIC LETTER THEH INITIAL FORM
+FE9C;FE9C;FE9C;062B;062B; # (ﺜ; ﺜ; ﺜ; ث; ث; ) ARABIC LETTER THEH MEDIAL FORM
+FE9D;FE9D;FE9D;062C;062C; # (ïº; ïº; ïº; ج; ج; ) ARABIC LETTER JEEM ISOLATED FORM
+FE9E;FE9E;FE9E;062C;062C; # (ﺞ; ﺞ; ﺞ; ج; ج; ) ARABIC LETTER JEEM FINAL FORM
+FE9F;FE9F;FE9F;062C;062C; # (ﺟ; ﺟ; ﺟ; ج; ج; ) ARABIC LETTER JEEM INITIAL FORM
+FEA0;FEA0;FEA0;062C;062C; # (ﺠ; ﺠ; ﺠ; ج; ج; ) ARABIC LETTER JEEM MEDIAL FORM
+FEA1;FEA1;FEA1;062D;062D; # (ﺡ; ﺡ; ﺡ; ح; ح; ) ARABIC LETTER HAH ISOLATED FORM
+FEA2;FEA2;FEA2;062D;062D; # (ﺢ; ﺢ; ﺢ; ح; ح; ) ARABIC LETTER HAH FINAL FORM
+FEA3;FEA3;FEA3;062D;062D; # (ﺣ; ﺣ; ﺣ; ح; ح; ) ARABIC LETTER HAH INITIAL FORM
+FEA4;FEA4;FEA4;062D;062D; # (ﺤ; ﺤ; ﺤ; ح; ح; ) ARABIC LETTER HAH MEDIAL FORM
+FEA5;FEA5;FEA5;062E;062E; # (ﺥ; ﺥ; ﺥ; خ; خ; ) ARABIC LETTER KHAH ISOLATED FORM
+FEA6;FEA6;FEA6;062E;062E; # (ﺦ; ﺦ; ﺦ; خ; خ; ) ARABIC LETTER KHAH FINAL FORM
+FEA7;FEA7;FEA7;062E;062E; # (ﺧ; ﺧ; ﺧ; خ; خ; ) ARABIC LETTER KHAH INITIAL FORM
+FEA8;FEA8;FEA8;062E;062E; # (ﺨ; ﺨ; ﺨ; خ; خ; ) ARABIC LETTER KHAH MEDIAL FORM
+FEA9;FEA9;FEA9;062F;062F; # (ﺩ; ﺩ; ﺩ; د; د; ) ARABIC LETTER DAL ISOLATED FORM
+FEAA;FEAA;FEAA;062F;062F; # (ﺪ; ﺪ; ﺪ; د; د; ) ARABIC LETTER DAL FINAL FORM
+FEAB;FEAB;FEAB;0630;0630; # (ﺫ; ﺫ; ﺫ; ذ; ذ; ) ARABIC LETTER THAL ISOLATED FORM
+FEAC;FEAC;FEAC;0630;0630; # (ﺬ; ﺬ; ﺬ; ذ; ذ; ) ARABIC LETTER THAL FINAL FORM
+FEAD;FEAD;FEAD;0631;0631; # (ﺭ; ﺭ; ﺭ; ر; ر; ) ARABIC LETTER REH ISOLATED FORM
+FEAE;FEAE;FEAE;0631;0631; # (ﺮ; ﺮ; ﺮ; ر; ر; ) ARABIC LETTER REH FINAL FORM
+FEAF;FEAF;FEAF;0632;0632; # (ﺯ; ﺯ; ﺯ; ز; ز; ) ARABIC LETTER ZAIN ISOLATED FORM
+FEB0;FEB0;FEB0;0632;0632; # (ﺰ; ﺰ; ﺰ; ز; ز; ) ARABIC LETTER ZAIN FINAL FORM
+FEB1;FEB1;FEB1;0633;0633; # (ﺱ; ﺱ; ﺱ; س; س; ) ARABIC LETTER SEEN ISOLATED FORM
+FEB2;FEB2;FEB2;0633;0633; # (ﺲ; ﺲ; ﺲ; س; س; ) ARABIC LETTER SEEN FINAL FORM
+FEB3;FEB3;FEB3;0633;0633; # (ﺳ; ﺳ; ﺳ; س; س; ) ARABIC LETTER SEEN INITIAL FORM
+FEB4;FEB4;FEB4;0633;0633; # (ﺴ; ﺴ; ﺴ; س; س; ) ARABIC LETTER SEEN MEDIAL FORM
+FEB5;FEB5;FEB5;0634;0634; # (ﺵ; ﺵ; ﺵ; ش; ش; ) ARABIC LETTER SHEEN ISOLATED FORM
+FEB6;FEB6;FEB6;0634;0634; # (ﺶ; ﺶ; ﺶ; ش; ش; ) ARABIC LETTER SHEEN FINAL FORM
+FEB7;FEB7;FEB7;0634;0634; # (ﺷ; ﺷ; ﺷ; ش; ش; ) ARABIC LETTER SHEEN INITIAL FORM
+FEB8;FEB8;FEB8;0634;0634; # (ﺸ; ﺸ; ﺸ; ش; ش; ) ARABIC LETTER SHEEN MEDIAL FORM
+FEB9;FEB9;FEB9;0635;0635; # (ﺹ; ﺹ; ﺹ; ص; ص; ) ARABIC LETTER SAD ISOLATED FORM
+FEBA;FEBA;FEBA;0635;0635; # (ﺺ; ﺺ; ﺺ; ص; ص; ) ARABIC LETTER SAD FINAL FORM
+FEBB;FEBB;FEBB;0635;0635; # (ﺻ; ﺻ; ﺻ; ص; ص; ) ARABIC LETTER SAD INITIAL FORM
+FEBC;FEBC;FEBC;0635;0635; # (ﺼ; ﺼ; ﺼ; ص; ص; ) ARABIC LETTER SAD MEDIAL FORM
+FEBD;FEBD;FEBD;0636;0636; # (ﺽ; ﺽ; ﺽ; ض; ض; ) ARABIC LETTER DAD ISOLATED FORM
+FEBE;FEBE;FEBE;0636;0636; # (ﺾ; ﺾ; ﺾ; ض; ض; ) ARABIC LETTER DAD FINAL FORM
+FEBF;FEBF;FEBF;0636;0636; # (ﺿ; ﺿ; ﺿ; ض; ض; ) ARABIC LETTER DAD INITIAL FORM
+FEC0;FEC0;FEC0;0636;0636; # (ﻀ; ﻀ; ﻀ; ض; ض; ) ARABIC LETTER DAD MEDIAL FORM
+FEC1;FEC1;FEC1;0637;0637; # (ï»; ï»; ï»; Ø·; Ø·; ) ARABIC LETTER TAH ISOLATED FORM
+FEC2;FEC2;FEC2;0637;0637; # (ﻂ; ﻂ; ﻂ; ط; ط; ) ARABIC LETTER TAH FINAL FORM
+FEC3;FEC3;FEC3;0637;0637; # (ﻃ; ﻃ; ﻃ; ط; ط; ) ARABIC LETTER TAH INITIAL FORM
+FEC4;FEC4;FEC4;0637;0637; # (ﻄ; ﻄ; ﻄ; ط; ط; ) ARABIC LETTER TAH MEDIAL FORM
+FEC5;FEC5;FEC5;0638;0638; # (ﻅ; ﻅ; ﻅ; ظ; ظ; ) ARABIC LETTER ZAH ISOLATED FORM
+FEC6;FEC6;FEC6;0638;0638; # (ﻆ; ﻆ; ﻆ; ظ; ظ; ) ARABIC LETTER ZAH FINAL FORM
+FEC7;FEC7;FEC7;0638;0638; # (ﻇ; ﻇ; ﻇ; ظ; ظ; ) ARABIC LETTER ZAH INITIAL FORM
+FEC8;FEC8;FEC8;0638;0638; # (ﻈ; ﻈ; ﻈ; ظ; ظ; ) ARABIC LETTER ZAH MEDIAL FORM
+FEC9;FEC9;FEC9;0639;0639; # (ﻉ; ﻉ; ﻉ; ع; ع; ) ARABIC LETTER AIN ISOLATED FORM
+FECA;FECA;FECA;0639;0639; # (ﻊ; ﻊ; ﻊ; ع; ع; ) ARABIC LETTER AIN FINAL FORM
+FECB;FECB;FECB;0639;0639; # (ﻋ; ﻋ; ﻋ; ع; ع; ) ARABIC LETTER AIN INITIAL FORM
+FECC;FECC;FECC;0639;0639; # (ﻌ; ﻌ; ﻌ; ع; ع; ) ARABIC LETTER AIN MEDIAL FORM
+FECD;FECD;FECD;063A;063A; # (ï»; ï»; ï»; غ; غ; ) ARABIC LETTER GHAIN ISOLATED FORM
+FECE;FECE;FECE;063A;063A; # (ﻎ; ﻎ; ﻎ; غ; غ; ) ARABIC LETTER GHAIN FINAL FORM
+FECF;FECF;FECF;063A;063A; # (ï»; ï»; ï»; غ; غ; ) ARABIC LETTER GHAIN INITIAL FORM
+FED0;FED0;FED0;063A;063A; # (ï»; ï»; ï»; غ; غ; ) ARABIC LETTER GHAIN MEDIAL FORM
+FED1;FED1;FED1;0641;0641; # (ﻑ; ﻑ; ﻑ; Ù; Ù; ) ARABIC LETTER FEH ISOLATED FORM
+FED2;FED2;FED2;0641;0641; # (ï»’; ï»’; ï»’; Ù; Ù; ) ARABIC LETTER FEH FINAL FORM
+FED3;FED3;FED3;0641;0641; # (ﻓ; ﻓ; ﻓ; Ù; Ù; ) ARABIC LETTER FEH INITIAL FORM
+FED4;FED4;FED4;0641;0641; # (ï»”; ï»”; ï»”; Ù; Ù; ) ARABIC LETTER FEH MEDIAL FORM
+FED5;FED5;FED5;0642;0642; # (ﻕ; ﻕ; ﻕ; ق; ق; ) ARABIC LETTER QAF ISOLATED FORM
+FED6;FED6;FED6;0642;0642; # (ï»–; ï»–; ï»–; Ù‚; Ù‚; ) ARABIC LETTER QAF FINAL FORM
+FED7;FED7;FED7;0642;0642; # (ï»—; ï»—; ï»—; Ù‚; Ù‚; ) ARABIC LETTER QAF INITIAL FORM
+FED8;FED8;FED8;0642;0642; # (ﻘ; ﻘ; ﻘ; ق; ق; ) ARABIC LETTER QAF MEDIAL FORM
+FED9;FED9;FED9;0643;0643; # (ï»™; ï»™; ï»™; Ùƒ; Ùƒ; ) ARABIC LETTER KAF ISOLATED FORM
+FEDA;FEDA;FEDA;0643;0643; # (ﻚ; ﻚ; ﻚ; ك; ك; ) ARABIC LETTER KAF FINAL FORM
+FEDB;FEDB;FEDB;0643;0643; # (ï»›; ï»›; ï»›; Ùƒ; Ùƒ; ) ARABIC LETTER KAF INITIAL FORM
+FEDC;FEDC;FEDC;0643;0643; # (ﻜ; ﻜ; ﻜ; ك; ك; ) ARABIC LETTER KAF MEDIAL FORM
+FEDD;FEDD;FEDD;0644;0644; # (ï»; ï»; ï»; Ù„; Ù„; ) ARABIC LETTER LAM ISOLATED FORM
+FEDE;FEDE;FEDE;0644;0644; # (ﻞ; ﻞ; ﻞ; ل; ل; ) ARABIC LETTER LAM FINAL FORM
+FEDF;FEDF;FEDF;0644;0644; # (ﻟ; ﻟ; ﻟ; ل; ل; ) ARABIC LETTER LAM INITIAL FORM
+FEE0;FEE0;FEE0;0644;0644; # (ï» ; ï» ; ï» ; Ù„; Ù„; ) ARABIC LETTER LAM MEDIAL FORM
+FEE1;FEE1;FEE1;0645;0645; # (ﻡ; ﻡ; ﻡ; م; م; ) ARABIC LETTER MEEM ISOLATED FORM
+FEE2;FEE2;FEE2;0645;0645; # (ﻢ; ﻢ; ﻢ; م; م; ) ARABIC LETTER MEEM FINAL FORM
+FEE3;FEE3;FEE3;0645;0645; # (ﻣ; ﻣ; ﻣ; م; م; ) ARABIC LETTER MEEM INITIAL FORM
+FEE4;FEE4;FEE4;0645;0645; # (ﻤ; ﻤ; ﻤ; م; م; ) ARABIC LETTER MEEM MEDIAL FORM
+FEE5;FEE5;FEE5;0646;0646; # (ﻥ; ﻥ; ﻥ; ن; ن; ) ARABIC LETTER NOON ISOLATED FORM
+FEE6;FEE6;FEE6;0646;0646; # (ﻦ; ﻦ; ﻦ; ن; ن; ) ARABIC LETTER NOON FINAL FORM
+FEE7;FEE7;FEE7;0646;0646; # (ï»§; ï»§; ï»§; Ù†; Ù†; ) ARABIC LETTER NOON INITIAL FORM
+FEE8;FEE8;FEE8;0646;0646; # (ﻨ; ﻨ; ﻨ; ن; ن; ) ARABIC LETTER NOON MEDIAL FORM
+FEE9;FEE9;FEE9;0647;0647; # (ﻩ; ﻩ; ﻩ; ه; ه; ) ARABIC LETTER HEH ISOLATED FORM
+FEEA;FEEA;FEEA;0647;0647; # (ﻪ; ﻪ; ﻪ; ه; ه; ) ARABIC LETTER HEH FINAL FORM
+FEEB;FEEB;FEEB;0647;0647; # (ﻫ; ﻫ; ﻫ; ه; ه; ) ARABIC LETTER HEH INITIAL FORM
+FEEC;FEEC;FEEC;0647;0647; # (ﻬ; ﻬ; ﻬ; ه; ه; ) ARABIC LETTER HEH MEDIAL FORM
+FEED;FEED;FEED;0648;0648; # (ï»­; ï»­; ï»­; Ùˆ; Ùˆ; ) ARABIC LETTER WAW ISOLATED FORM
+FEEE;FEEE;FEEE;0648;0648; # (ï»®; ï»®; ï»®; Ùˆ; Ùˆ; ) ARABIC LETTER WAW FINAL FORM
+FEEF;FEEF;FEEF;0649;0649; # (ﻯ; ﻯ; ﻯ; ى; ى; ) ARABIC LETTER ALEF MAKSURA ISOLATED FORM
+FEF0;FEF0;FEF0;0649;0649; # (ï»°; ï»°; ï»°; Ù‰; Ù‰; ) ARABIC LETTER ALEF MAKSURA FINAL FORM
+FEF1;FEF1;FEF1;064A;064A; # (ï»±; ï»±; ï»±; ÙŠ; ÙŠ; ) ARABIC LETTER YEH ISOLATED FORM
+FEF2;FEF2;FEF2;064A;064A; # (ﻲ; ﻲ; ﻲ; ي; ي; ) ARABIC LETTER YEH FINAL FORM
+FEF3;FEF3;FEF3;064A;064A; # (ﻳ; ﻳ; ﻳ; ي; ي; ) ARABIC LETTER YEH INITIAL FORM
+FEF4;FEF4;FEF4;064A;064A; # (ï»´; ï»´; ï»´; ÙŠ; ÙŠ; ) ARABIC LETTER YEH MEDIAL FORM
+FEF5;FEF5;FEF5;0644 0622;0644 0627 0653; # (ﻵ; ﻵ; ﻵ; لآ; لا◌ٓ; ) ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE ISOLATED FORM
+FEF6;FEF6;FEF6;0644 0622;0644 0627 0653; # (ﻶ; ﻶ; ﻶ; لآ; لا◌ٓ; ) ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE FINAL FORM
+FEF7;FEF7;FEF7;0644 0623;0644 0627 0654; # (ﻷ; ﻷ; ﻷ; لأ; لا◌ٔ; ) ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE ISOLATED FORM
+FEF8;FEF8;FEF8;0644 0623;0644 0627 0654; # (ﻸ; ﻸ; ﻸ; لأ; لا◌ٔ; ) ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE FINAL FORM
+FEF9;FEF9;FEF9;0644 0625;0644 0627 0655; # (ﻹ; ﻹ; ﻹ; لإ; لا◌ٕ; ) ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW ISOLATED FORM
+FEFA;FEFA;FEFA;0644 0625;0644 0627 0655; # (ﻺ; ﻺ; ﻺ; لإ; لا◌ٕ; ) ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW FINAL FORM
+FEFB;FEFB;FEFB;0644 0627;0644 0627; # (ﻻ; ﻻ; ﻻ; لا; لا; ) ARABIC LIGATURE LAM WITH ALEF ISOLATED FORM
+FEFC;FEFC;FEFC;0644 0627;0644 0627; # (ﻼ; ﻼ; ﻼ; لا; لا; ) ARABIC LIGATURE LAM WITH ALEF FINAL FORM
+FF01;FF01;FF01;0021;0021; # (ï¼; ï¼; ï¼; !; !; ) FULLWIDTH EXCLAMATION MARK
+FF02;FF02;FF02;0022;0022; # ("; "; "; "; "; ) FULLWIDTH QUOTATION MARK
+FF03;FF03;FF03;0023;0023; # (#; #; #; #; #; ) FULLWIDTH NUMBER SIGN
+FF04;FF04;FF04;0024;0024; # ($; $; $; $; $; ) FULLWIDTH DOLLAR SIGN
+FF05;FF05;FF05;0025;0025; # (ï¼…; ï¼…; ï¼…; %; %; ) FULLWIDTH PERCENT SIGN
+FF06;FF06;FF06;0026;0026; # (&; &; &; &; &; ) FULLWIDTH AMPERSAND
+FF07;FF07;FF07;0027;0027; # ('; '; '; '; '; ) FULLWIDTH APOSTROPHE
+FF08;FF08;FF08;0028;0028; # ((; (; (; (; (; ) FULLWIDTH LEFT PARENTHESIS
+FF09;FF09;FF09;0029;0029; # (); ); ); ); ); ) FULLWIDTH RIGHT PARENTHESIS
+FF0A;FF0A;FF0A;002A;002A; # (*; *; *; *; *; ) FULLWIDTH ASTERISK
+FF0B;FF0B;FF0B;002B;002B; # (+; +; +; +; +; ) FULLWIDTH PLUS SIGN
+FF0C;FF0C;FF0C;002C;002C; # (,; ,; ,; ,; ,; ) FULLWIDTH COMMA
+FF0D;FF0D;FF0D;002D;002D; # (ï¼; ï¼; ï¼; -; -; ) FULLWIDTH HYPHEN-MINUS
+FF0E;FF0E;FF0E;002E;002E; # (.; .; .; .; .; ) FULLWIDTH FULL STOP
+FF0F;FF0F;FF0F;002F;002F; # (ï¼; ï¼; ï¼; /; /; ) FULLWIDTH SOLIDUS
+FF10;FF10;FF10;0030;0030; # (ï¼; ï¼; ï¼; 0; 0; ) FULLWIDTH DIGIT ZERO
+FF11;FF11;FF11;0031;0031; # (1; 1; 1; 1; 1; ) FULLWIDTH DIGIT ONE
+FF12;FF12;FF12;0032;0032; # (ï¼’; ï¼’; ï¼’; 2; 2; ) FULLWIDTH DIGIT TWO
+FF13;FF13;FF13;0033;0033; # (3; 3; 3; 3; 3; ) FULLWIDTH DIGIT THREE
+FF14;FF14;FF14;0034;0034; # (ï¼”; ï¼”; ï¼”; 4; 4; ) FULLWIDTH DIGIT FOUR
+FF15;FF15;FF15;0035;0035; # (5; 5; 5; 5; 5; ) FULLWIDTH DIGIT FIVE
+FF16;FF16;FF16;0036;0036; # (ï¼–; ï¼–; ï¼–; 6; 6; ) FULLWIDTH DIGIT SIX
+FF17;FF17;FF17;0037;0037; # (ï¼—; ï¼—; ï¼—; 7; 7; ) FULLWIDTH DIGIT SEVEN
+FF18;FF18;FF18;0038;0038; # (8; 8; 8; 8; 8; ) FULLWIDTH DIGIT EIGHT
+FF19;FF19;FF19;0039;0039; # (ï¼™; ï¼™; ï¼™; 9; 9; ) FULLWIDTH DIGIT NINE
+FF1A;FF1A;FF1A;003A;003A; # (:; :; :; :; :; ) FULLWIDTH COLON
+FF1B;FF1B;FF1B;003B;003B; # (ï¼›; ï¼›; ï¼›; ;; ;; ) FULLWIDTH SEMICOLON
+FF1C;FF1C;FF1C;003C;003C; # (<; <; <; <; <; ) FULLWIDTH LESS-THAN SIGN
+FF1D;FF1D;FF1D;003D;003D; # (ï¼; ï¼; ï¼; =; =; ) FULLWIDTH EQUALS SIGN
+FF1E;FF1E;FF1E;003E;003E; # (>; >; >; >; >; ) FULLWIDTH GREATER-THAN SIGN
+FF1F;FF1F;FF1F;003F;003F; # (?; ?; ?; ?; ?; ) FULLWIDTH QUESTION MARK
+FF20;FF20;FF20;0040;0040; # (ï¼ ; ï¼ ; ï¼ ; @; @; ) FULLWIDTH COMMERCIAL AT
+FF21;FF21;FF21;0041;0041; # (A; A; A; A; A; ) FULLWIDTH LATIN CAPITAL LETTER A
+FF22;FF22;FF22;0042;0042; # (ï¼¢; ï¼¢; ï¼¢; B; B; ) FULLWIDTH LATIN CAPITAL LETTER B
+FF23;FF23;FF23;0043;0043; # (ï¼£; ï¼£; ï¼£; C; C; ) FULLWIDTH LATIN CAPITAL LETTER C
+FF24;FF24;FF24;0044;0044; # (D; D; D; D; D; ) FULLWIDTH LATIN CAPITAL LETTER D
+FF25;FF25;FF25;0045;0045; # (ï¼¥; ï¼¥; ï¼¥; E; E; ) FULLWIDTH LATIN CAPITAL LETTER E
+FF26;FF26;FF26;0046;0046; # (F; F; F; F; F; ) FULLWIDTH LATIN CAPITAL LETTER F
+FF27;FF27;FF27;0047;0047; # (ï¼§; ï¼§; ï¼§; G; G; ) FULLWIDTH LATIN CAPITAL LETTER G
+FF28;FF28;FF28;0048;0048; # (H; H; H; H; H; ) FULLWIDTH LATIN CAPITAL LETTER H
+FF29;FF29;FF29;0049;0049; # (I; I; I; I; I; ) FULLWIDTH LATIN CAPITAL LETTER I
+FF2A;FF2A;FF2A;004A;004A; # (J; J; J; J; J; ) FULLWIDTH LATIN CAPITAL LETTER J
+FF2B;FF2B;FF2B;004B;004B; # (K; K; K; K; K; ) FULLWIDTH LATIN CAPITAL LETTER K
+FF2C;FF2C;FF2C;004C;004C; # (L; L; L; L; L; ) FULLWIDTH LATIN CAPITAL LETTER L
+FF2D;FF2D;FF2D;004D;004D; # (ï¼­; ï¼­; ï¼­; M; M; ) FULLWIDTH LATIN CAPITAL LETTER M
+FF2E;FF2E;FF2E;004E;004E; # (ï¼®; ï¼®; ï¼®; N; N; ) FULLWIDTH LATIN CAPITAL LETTER N
+FF2F;FF2F;FF2F;004F;004F; # (O; O; O; O; O; ) FULLWIDTH LATIN CAPITAL LETTER O
+FF30;FF30;FF30;0050;0050; # (ï¼°; ï¼°; ï¼°; P; P; ) FULLWIDTH LATIN CAPITAL LETTER P
+FF31;FF31;FF31;0051;0051; # (ï¼±; ï¼±; ï¼±; Q; Q; ) FULLWIDTH LATIN CAPITAL LETTER Q
+FF32;FF32;FF32;0052;0052; # (ï¼²; ï¼²; ï¼²; R; R; ) FULLWIDTH LATIN CAPITAL LETTER R
+FF33;FF33;FF33;0053;0053; # (ï¼³; ï¼³; ï¼³; S; S; ) FULLWIDTH LATIN CAPITAL LETTER S
+FF34;FF34;FF34;0054;0054; # (ï¼´; ï¼´; ï¼´; T; T; ) FULLWIDTH LATIN CAPITAL LETTER T
+FF35;FF35;FF35;0055;0055; # (ï¼µ; ï¼µ; ï¼µ; U; U; ) FULLWIDTH LATIN CAPITAL LETTER U
+FF36;FF36;FF36;0056;0056; # (ï¼¶; ï¼¶; ï¼¶; V; V; ) FULLWIDTH LATIN CAPITAL LETTER V
+FF37;FF37;FF37;0057;0057; # (ï¼·; ï¼·; ï¼·; W; W; ) FULLWIDTH LATIN CAPITAL LETTER W
+FF38;FF38;FF38;0058;0058; # (X; X; X; X; X; ) FULLWIDTH LATIN CAPITAL LETTER X
+FF39;FF39;FF39;0059;0059; # (ï¼¹; ï¼¹; ï¼¹; Y; Y; ) FULLWIDTH LATIN CAPITAL LETTER Y
+FF3A;FF3A;FF3A;005A;005A; # (Z; Z; Z; Z; Z; ) FULLWIDTH LATIN CAPITAL LETTER Z
+FF3B;FF3B;FF3B;005B;005B; # (ï¼»; ï¼»; ï¼»; [; [; ) FULLWIDTH LEFT SQUARE BRACKET
+FF3C;FF3C;FF3C;005C;005C; # (ï¼¼; ï¼¼; ï¼¼; \; \; ) FULLWIDTH REVERSE SOLIDUS
+FF3D;FF3D;FF3D;005D;005D; # (ï¼½; ï¼½; ï¼½; ]; ]; ) FULLWIDTH RIGHT SQUARE BRACKET
+FF3E;FF3E;FF3E;005E;005E; # (ï¼¾; ï¼¾; ï¼¾; ^; ^; ) FULLWIDTH CIRCUMFLEX ACCENT
+FF3F;FF3F;FF3F;005F;005F; # (_; _; _; _; _; ) FULLWIDTH LOW LINE
+FF40;FF40;FF40;0060;0060; # (ï½€; ï½€; ï½€; `; `; ) FULLWIDTH GRAVE ACCENT
+FF41;FF41;FF41;0061;0061; # (ï½; ï½; ï½; a; a; ) FULLWIDTH LATIN SMALL LETTER A
+FF42;FF42;FF42;0062;0062; # (b; b; b; b; b; ) FULLWIDTH LATIN SMALL LETTER B
+FF43;FF43;FF43;0063;0063; # (c; c; c; c; c; ) FULLWIDTH LATIN SMALL LETTER C
+FF44;FF44;FF44;0064;0064; # (d; d; d; d; d; ) FULLWIDTH LATIN SMALL LETTER D
+FF45;FF45;FF45;0065;0065; # (ï½…; ï½…; ï½…; e; e; ) FULLWIDTH LATIN SMALL LETTER E
+FF46;FF46;FF46;0066;0066; # (f; f; f; f; f; ) FULLWIDTH LATIN SMALL LETTER F
+FF47;FF47;FF47;0067;0067; # (g; g; g; g; g; ) FULLWIDTH LATIN SMALL LETTER G
+FF48;FF48;FF48;0068;0068; # (h; h; h; h; h; ) FULLWIDTH LATIN SMALL LETTER H
+FF49;FF49;FF49;0069;0069; # (i; i; i; i; i; ) FULLWIDTH LATIN SMALL LETTER I
+FF4A;FF4A;FF4A;006A;006A; # (j; j; j; j; j; ) FULLWIDTH LATIN SMALL LETTER J
+FF4B;FF4B;FF4B;006B;006B; # (k; k; k; k; k; ) FULLWIDTH LATIN SMALL LETTER K
+FF4C;FF4C;FF4C;006C;006C; # (l; l; l; l; l; ) FULLWIDTH LATIN SMALL LETTER L
+FF4D;FF4D;FF4D;006D;006D; # (ï½; ï½; ï½; m; m; ) FULLWIDTH LATIN SMALL LETTER M
+FF4E;FF4E;FF4E;006E;006E; # (n; n; n; n; n; ) FULLWIDTH LATIN SMALL LETTER N
+FF4F;FF4F;FF4F;006F;006F; # (ï½; ï½; ï½; o; o; ) FULLWIDTH LATIN SMALL LETTER O
+FF50;FF50;FF50;0070;0070; # (ï½; ï½; ï½; p; p; ) FULLWIDTH LATIN SMALL LETTER P
+FF51;FF51;FF51;0071;0071; # (q; q; q; q; q; ) FULLWIDTH LATIN SMALL LETTER Q
+FF52;FF52;FF52;0072;0072; # (ï½’; ï½’; ï½’; r; r; ) FULLWIDTH LATIN SMALL LETTER R
+FF53;FF53;FF53;0073;0073; # (s; s; s; s; s; ) FULLWIDTH LATIN SMALL LETTER S
+FF54;FF54;FF54;0074;0074; # (ï½”; ï½”; ï½”; t; t; ) FULLWIDTH LATIN SMALL LETTER T
+FF55;FF55;FF55;0075;0075; # (u; u; u; u; u; ) FULLWIDTH LATIN SMALL LETTER U
+FF56;FF56;FF56;0076;0076; # (ï½–; ï½–; ï½–; v; v; ) FULLWIDTH LATIN SMALL LETTER V
+FF57;FF57;FF57;0077;0077; # (ï½—; ï½—; ï½—; w; w; ) FULLWIDTH LATIN SMALL LETTER W
+FF58;FF58;FF58;0078;0078; # (x; x; x; x; x; ) FULLWIDTH LATIN SMALL LETTER X
+FF59;FF59;FF59;0079;0079; # (ï½™; ï½™; ï½™; y; y; ) FULLWIDTH LATIN SMALL LETTER Y
+FF5A;FF5A;FF5A;007A;007A; # (z; z; z; z; z; ) FULLWIDTH LATIN SMALL LETTER Z
+FF5B;FF5B;FF5B;007B;007B; # (ï½›; ï½›; ï½›; {; {; ) FULLWIDTH LEFT CURLY BRACKET
+FF5C;FF5C;FF5C;007C;007C; # (|; |; |; |; |; ) FULLWIDTH VERTICAL LINE
+FF5D;FF5D;FF5D;007D;007D; # (ï½; ï½; ï½; }; }; ) FULLWIDTH RIGHT CURLY BRACKET
+FF5E;FF5E;FF5E;007E;007E; # (~; ~; ~; ~; ~; ) FULLWIDTH TILDE
+FF5F;FF5F;FF5F;2985;2985; # (⦅; ⦅; ⦅; ⦅; ⦅; ) FULLWIDTH LEFT WHITE PARENTHESIS
+FF60;FF60;FF60;2986;2986; # (⦆; ⦆; ⦆; ⦆; ⦆; ) FULLWIDTH RIGHT WHITE PARENTHESIS
+FF61;FF61;FF61;3002;3002; # (。; 。; 。; 。; 。; ) HALFWIDTH IDEOGRAPHIC FULL STOP
+FF62;FF62;FF62;300C;300C; # (「; 「; 「; 「; 「; ) HALFWIDTH LEFT CORNER BRACKET
+FF63;FF63;FF63;300D;300D; # (ï½£; ï½£; ï½£; ã€; ã€; ) HALFWIDTH RIGHT CORNER BRACKET
+FF64;FF64;FF64;3001;3001; # (、; 、; 、; ã€; ã€; ) HALFWIDTH IDEOGRAPHIC COMMA
+FF65;FF65;FF65;30FB;30FB; # (・; ・; ・; ・; ・; ) HALFWIDTH KATAKANA MIDDLE DOT
+FF66;FF66;FF66;30F2;30F2; # (ヲ; ヲ; ヲ; ヲ; ヲ; ) HALFWIDTH KATAKANA LETTER WO
+FF67;FF67;FF67;30A1;30A1; # (ï½§; ï½§; ï½§; ã‚¡; ã‚¡; ) HALFWIDTH KATAKANA LETTER SMALL A
+FF68;FF68;FF68;30A3;30A3; # (ィ; ィ; ィ; ィ; ィ; ) HALFWIDTH KATAKANA LETTER SMALL I
+FF69;FF69;FF69;30A5;30A5; # (ゥ; ゥ; ゥ; ゥ; ゥ; ) HALFWIDTH KATAKANA LETTER SMALL U
+FF6A;FF6A;FF6A;30A7;30A7; # (ェ; ェ; ェ; ェ; ェ; ) HALFWIDTH KATAKANA LETTER SMALL E
+FF6B;FF6B;FF6B;30A9;30A9; # (ォ; ォ; ォ; ォ; ォ; ) HALFWIDTH KATAKANA LETTER SMALL O
+FF6C;FF6C;FF6C;30E3;30E3; # (ャ; ャ; ャ; ャ; ャ; ) HALFWIDTH KATAKANA LETTER SMALL YA
+FF6D;FF6D;FF6D;30E5;30E5; # (ュ; ュ; ュ; ュ; ュ; ) HALFWIDTH KATAKANA LETTER SMALL YU
+FF6E;FF6E;FF6E;30E7;30E7; # (ョ; ョ; ョ; ョ; ョ; ) HALFWIDTH KATAKANA LETTER SMALL YO
+FF6F;FF6F;FF6F;30C3;30C3; # (ッ; ッ; ッ; ッ; ッ; ) HALFWIDTH KATAKANA LETTER SMALL TU
+FF70;FF70;FF70;30FC;30FC; # (ー; ー; ー; ー; ー; ) HALFWIDTH KATAKANA-HIRAGANA PROLONGED SOUND MARK
+FF71;FF71;FF71;30A2;30A2; # (ï½±; ï½±; ï½±; ã‚¢; ã‚¢; ) HALFWIDTH KATAKANA LETTER A
+FF72;FF72;FF72;30A4;30A4; # (イ; イ; イ; イ; イ; ) HALFWIDTH KATAKANA LETTER I
+FF73;FF73;FF73;30A6;30A6; # (ウ; ウ; ウ; ウ; ウ; ) HALFWIDTH KATAKANA LETTER U
+FF74;FF74;FF74;30A8;30A8; # (エ; エ; エ; エ; エ; ) HALFWIDTH KATAKANA LETTER E
+FF75;FF75;FF75;30AA;30AA; # (オ; オ; オ; オ; オ; ) HALFWIDTH KATAKANA LETTER O
+FF76;FF76;FF76;30AB;30AB; # (ï½¶; ï½¶; ï½¶; ã‚«; ã‚«; ) HALFWIDTH KATAKANA LETTER KA
+FF77;FF77;FF77;30AD;30AD; # (ï½·; ï½·; ï½·; ã‚­; ã‚­; ) HALFWIDTH KATAKANA LETTER KI
+FF78;FF78;FF78;30AF;30AF; # (ク; ク; ク; ク; ク; ) HALFWIDTH KATAKANA LETTER KU
+FF79;FF79;FF79;30B1;30B1; # (ケ; ケ; ケ; ケ; ケ; ) HALFWIDTH KATAKANA LETTER KE
+FF7A;FF7A;FF7A;30B3;30B3; # (コ; コ; コ; コ; コ; ) HALFWIDTH KATAKANA LETTER KO
+FF7B;FF7B;FF7B;30B5;30B5; # (サ; サ; サ; サ; サ; ) HALFWIDTH KATAKANA LETTER SA
+FF7C;FF7C;FF7C;30B7;30B7; # (ï½¼; ï½¼; ï½¼; ã‚·; ã‚·; ) HALFWIDTH KATAKANA LETTER SI
+FF7D;FF7D;FF7D;30B9;30B9; # (ス; ス; ス; ス; ス; ) HALFWIDTH KATAKANA LETTER SU
+FF7E;FF7E;FF7E;30BB;30BB; # (ï½¾; ï½¾; ï½¾; ã‚»; ã‚»; ) HALFWIDTH KATAKANA LETTER SE
+FF7F;FF7F;FF7F;30BD;30BD; # (ソ; ソ; ソ; ソ; ソ; ) HALFWIDTH KATAKANA LETTER SO
+FF80;FF80;FF80;30BF;30BF; # (ï¾€; ï¾€; ï¾€; ã‚¿; ã‚¿; ) HALFWIDTH KATAKANA LETTER TA
+FF81;FF81;FF81;30C1;30C1; # (ï¾; ï¾; ï¾; ãƒ; ãƒ; ) HALFWIDTH KATAKANA LETTER TI
+FF82;FF82;FF82;30C4;30C4; # (ツ; ツ; ツ; ツ; ツ; ) HALFWIDTH KATAKANA LETTER TU
+FF83;FF83;FF83;30C6;30C6; # (テ; テ; テ; テ; テ; ) HALFWIDTH KATAKANA LETTER TE
+FF84;FF84;FF84;30C8;30C8; # (ト; ト; ト; ト; ト; ) HALFWIDTH KATAKANA LETTER TO
+FF85;FF85;FF85;30CA;30CA; # (ナ; ナ; ナ; ナ; ナ; ) HALFWIDTH KATAKANA LETTER NA
+FF86;FF86;FF86;30CB;30CB; # (ニ; ニ; ニ; ニ; ニ; ) HALFWIDTH KATAKANA LETTER NI
+FF87;FF87;FF87;30CC;30CC; # (ヌ; ヌ; ヌ; ヌ; ヌ; ) HALFWIDTH KATAKANA LETTER NU
+FF88;FF88;FF88;30CD;30CD; # (ネ; ネ; ネ; ãƒ; ãƒ; ) HALFWIDTH KATAKANA LETTER NE
+FF89;FF89;FF89;30CE;30CE; # (ノ; ノ; ノ; ノ; ノ; ) HALFWIDTH KATAKANA LETTER NO
+FF8A;FF8A;FF8A;30CF;30CF; # (ハ; ハ; ハ; ãƒ; ãƒ; ) HALFWIDTH KATAKANA LETTER HA
+FF8B;FF8B;FF8B;30D2;30D2; # (ヒ; ヒ; ヒ; ヒ; ヒ; ) HALFWIDTH KATAKANA LETTER HI
+FF8C;FF8C;FF8C;30D5;30D5; # (フ; フ; フ; フ; フ; ) HALFWIDTH KATAKANA LETTER HU
+FF8D;FF8D;FF8D;30D8;30D8; # (ï¾; ï¾; ï¾; ヘ; ヘ; ) HALFWIDTH KATAKANA LETTER HE
+FF8E;FF8E;FF8E;30DB;30DB; # (ホ; ホ; ホ; ホ; ホ; ) HALFWIDTH KATAKANA LETTER HO
+FF8F;FF8F;FF8F;30DE;30DE; # (ï¾; ï¾; ï¾; マ; マ; ) HALFWIDTH KATAKANA LETTER MA
+FF90;FF90;FF90;30DF;30DF; # (ï¾; ï¾; ï¾; ミ; ミ; ) HALFWIDTH KATAKANA LETTER MI
+FF91;FF91;FF91;30E0;30E0; # (ム; ム; ム; ム; ム; ) HALFWIDTH KATAKANA LETTER MU
+FF92;FF92;FF92;30E1;30E1; # (メ; メ; メ; メ; メ; ) HALFWIDTH KATAKANA LETTER ME
+FF93;FF93;FF93;30E2;30E2; # (モ; モ; モ; モ; モ; ) HALFWIDTH KATAKANA LETTER MO
+FF94;FF94;FF94;30E4;30E4; # (ヤ; ヤ; ヤ; ヤ; ヤ; ) HALFWIDTH KATAKANA LETTER YA
+FF95;FF95;FF95;30E6;30E6; # (ユ; ユ; ユ; ユ; ユ; ) HALFWIDTH KATAKANA LETTER YU
+FF96;FF96;FF96;30E8;30E8; # (ヨ; ヨ; ヨ; ヨ; ヨ; ) HALFWIDTH KATAKANA LETTER YO
+FF97;FF97;FF97;30E9;30E9; # (ラ; ラ; ラ; ラ; ラ; ) HALFWIDTH KATAKANA LETTER RA
+FF98;FF98;FF98;30EA;30EA; # (リ; リ; リ; リ; リ; ) HALFWIDTH KATAKANA LETTER RI
+FF99;FF99;FF99;30EB;30EB; # (ル; ル; ル; ル; ル; ) HALFWIDTH KATAKANA LETTER RU
+FF9A;FF9A;FF9A;30EC;30EC; # (レ; レ; レ; レ; レ; ) HALFWIDTH KATAKANA LETTER RE
+FF9B;FF9B;FF9B;30ED;30ED; # (ロ; ロ; ロ; ロ; ロ; ) HALFWIDTH KATAKANA LETTER RO
+FF9C;FF9C;FF9C;30EF;30EF; # (ワ; ワ; ワ; ワ; ワ; ) HALFWIDTH KATAKANA LETTER WA
+FF9D;FF9D;FF9D;30F3;30F3; # (ï¾; ï¾; ï¾; ン; ン; ) HALFWIDTH KATAKANA LETTER N
+FF9E;FF9E;FF9E;3099;3099; # (゙; ゙; ゙; ◌゙; ◌゙; ) HALFWIDTH KATAKANA VOICED SOUND MARK
+FF9F;FF9F;FF9F;309A;309A; # (゚; ゚; ゚; ◌゚; ◌゚; ) HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK
+FFA0;FFA0;FFA0;1160;1160; # (ï¾ ; ï¾ ; ï¾ ; á… ; á… ; ) HALFWIDTH HANGUL FILLER
+FFA1;FFA1;FFA1;1100;1100; # (ᄀ; ᄀ; ᄀ; ᄀ; ᄀ; ) HALFWIDTH HANGUL LETTER KIYEOK
+FFA2;FFA2;FFA2;1101;1101; # (ï¾¢; ï¾¢; ï¾¢; á„; á„; ) HALFWIDTH HANGUL LETTER SSANGKIYEOK
+FFA3;FFA3;FFA3;11AA;11AA; # (ᆪ; ᆪ; ᆪ; ᆪ; ᆪ; ) HALFWIDTH HANGUL LETTER KIYEOK-SIOS
+FFA4;FFA4;FFA4;1102;1102; # (ᄂ; ᄂ; ᄂ; ᄂ; ᄂ; ) HALFWIDTH HANGUL LETTER NIEUN
+FFA5;FFA5;FFA5;11AC;11AC; # (ᆬ; ᆬ; ᆬ; ᆬ; ᆬ; ) HALFWIDTH HANGUL LETTER NIEUN-CIEUC
+FFA6;FFA6;FFA6;11AD;11AD; # (ᆭ; ᆭ; ᆭ; ᆭ; ᆭ; ) HALFWIDTH HANGUL LETTER NIEUN-HIEUH
+FFA7;FFA7;FFA7;1103;1103; # (ᄃ; ᄃ; ᄃ; ᄃ; ᄃ; ) HALFWIDTH HANGUL LETTER TIKEUT
+FFA8;FFA8;FFA8;1104;1104; # (ᄄ; ᄄ; ᄄ; ᄄ; ᄄ; ) HALFWIDTH HANGUL LETTER SSANGTIKEUT
+FFA9;FFA9;FFA9;1105;1105; # (ᄅ; ᄅ; ᄅ; ᄅ; ᄅ; ) HALFWIDTH HANGUL LETTER RIEUL
+FFAA;FFAA;FFAA;11B0;11B0; # (ᆰ; ᆰ; ᆰ; ᆰ; ᆰ; ) HALFWIDTH HANGUL LETTER RIEUL-KIYEOK
+FFAB;FFAB;FFAB;11B1;11B1; # (ᆱ; ᆱ; ᆱ; ᆱ; ᆱ; ) HALFWIDTH HANGUL LETTER RIEUL-MIEUM
+FFAC;FFAC;FFAC;11B2;11B2; # (ᆲ; ᆲ; ᆲ; ᆲ; ᆲ; ) HALFWIDTH HANGUL LETTER RIEUL-PIEUP
+FFAD;FFAD;FFAD;11B3;11B3; # (ᆳ; ᆳ; ᆳ; ᆳ; ᆳ; ) HALFWIDTH HANGUL LETTER RIEUL-SIOS
+FFAE;FFAE;FFAE;11B4;11B4; # (ᆴ; ᆴ; ᆴ; ᆴ; ᆴ; ) HALFWIDTH HANGUL LETTER RIEUL-THIEUTH
+FFAF;FFAF;FFAF;11B5;11B5; # (ᆵ; ᆵ; ᆵ; ᆵ; ᆵ; ) HALFWIDTH HANGUL LETTER RIEUL-PHIEUPH
+FFB0;FFB0;FFB0;111A;111A; # (ᄚ; ᄚ; ᄚ; ᄚ; ᄚ; ) HALFWIDTH HANGUL LETTER RIEUL-HIEUH
+FFB1;FFB1;FFB1;1106;1106; # (ᄆ; ᄆ; ᄆ; ᄆ; ᄆ; ) HALFWIDTH HANGUL LETTER MIEUM
+FFB2;FFB2;FFB2;1107;1107; # (ᄇ; ᄇ; ᄇ; ᄇ; ᄇ; ) HALFWIDTH HANGUL LETTER PIEUP
+FFB3;FFB3;FFB3;1108;1108; # (ᄈ; ᄈ; ᄈ; ᄈ; ᄈ; ) HALFWIDTH HANGUL LETTER SSANGPIEUP
+FFB4;FFB4;FFB4;1121;1121; # (ï¾´; ï¾´; ï¾´; á„¡; á„¡; ) HALFWIDTH HANGUL LETTER PIEUP-SIOS
+FFB5;FFB5;FFB5;1109;1109; # (ᄉ; ᄉ; ᄉ; ᄉ; ᄉ; ) HALFWIDTH HANGUL LETTER SIOS
+FFB6;FFB6;FFB6;110A;110A; # (ᄊ; ᄊ; ᄊ; ᄊ; ᄊ; ) HALFWIDTH HANGUL LETTER SSANGSIOS
+FFB7;FFB7;FFB7;110B;110B; # (ï¾·; ï¾·; ï¾·; á„‹; á„‹; ) HALFWIDTH HANGUL LETTER IEUNG
+FFB8;FFB8;FFB8;110C;110C; # (ᄌ; ᄌ; ᄌ; ᄌ; ᄌ; ) HALFWIDTH HANGUL LETTER CIEUC
+FFB9;FFB9;FFB9;110D;110D; # (ï¾¹; ï¾¹; ï¾¹; á„; á„; ) HALFWIDTH HANGUL LETTER SSANGCIEUC
+FFBA;FFBA;FFBA;110E;110E; # (ᄎ; ᄎ; ᄎ; ᄎ; ᄎ; ) HALFWIDTH HANGUL LETTER CHIEUCH
+FFBB;FFBB;FFBB;110F;110F; # (ï¾»; ï¾»; ï¾»; á„; á„; ) HALFWIDTH HANGUL LETTER KHIEUKH
+FFBC;FFBC;FFBC;1110;1110; # (ï¾¼; ï¾¼; ï¾¼; á„; á„; ) HALFWIDTH HANGUL LETTER THIEUTH
+FFBD;FFBD;FFBD;1111;1111; # (ï¾½; ï¾½; ï¾½; á„‘; á„‘; ) HALFWIDTH HANGUL LETTER PHIEUPH
+FFBE;FFBE;FFBE;1112;1112; # (ï¾¾; ï¾¾; ï¾¾; á„’; á„’; ) HALFWIDTH HANGUL LETTER HIEUH
+FFC2;FFC2;FFC2;1161;1161; # (ï¿‚; ï¿‚; ï¿‚; á…¡; á…¡; ) HALFWIDTH HANGUL LETTER A
+FFC3;FFC3;FFC3;1162;1162; # (ᅢ; ᅢ; ᅢ; ᅢ; ᅢ; ) HALFWIDTH HANGUL LETTER AE
+FFC4;FFC4;FFC4;1163;1163; # (ï¿„; ï¿„; ï¿„; á…£; á…£; ) HALFWIDTH HANGUL LETTER YA
+FFC5;FFC5;FFC5;1164;1164; # (ï¿…; ï¿…; ï¿…; á…¤; á…¤; ) HALFWIDTH HANGUL LETTER YAE
+FFC6;FFC6;FFC6;1165;1165; # (ᅥ; ᅥ; ᅥ; ᅥ; ᅥ; ) HALFWIDTH HANGUL LETTER EO
+FFC7;FFC7;FFC7;1166;1166; # (ᅦ; ᅦ; ᅦ; ᅦ; ᅦ; ) HALFWIDTH HANGUL LETTER E
+FFCA;FFCA;FFCA;1167;1167; # (ᅧ; ᅧ; ᅧ; ᅧ; ᅧ; ) HALFWIDTH HANGUL LETTER YEO
+FFCB;FFCB;FFCB;1168;1168; # (ï¿‹; ï¿‹; ï¿‹; á…¨; á…¨; ) HALFWIDTH HANGUL LETTER YE
+FFCC;FFCC;FFCC;1169;1169; # (ᅩ; ᅩ; ᅩ; ᅩ; ᅩ; ) HALFWIDTH HANGUL LETTER O
+FFCD;FFCD;FFCD;116A;116A; # (ï¿; ï¿; ï¿; á…ª; á…ª; ) HALFWIDTH HANGUL LETTER WA
+FFCE;FFCE;FFCE;116B;116B; # (ᅫ; ᅫ; ᅫ; ᅫ; ᅫ; ) HALFWIDTH HANGUL LETTER WAE
+FFCF;FFCF;FFCF;116C;116C; # (ï¿; ï¿; ï¿; á…¬; á…¬; ) HALFWIDTH HANGUL LETTER OE
+FFD2;FFD2;FFD2;116D;116D; # (ï¿’; ï¿’; ï¿’; á…­; á…­; ) HALFWIDTH HANGUL LETTER YO
+FFD3;FFD3;FFD3;116E;116E; # (ï¿“; ï¿“; ï¿“; á…®; á…®; ) HALFWIDTH HANGUL LETTER U
+FFD4;FFD4;FFD4;116F;116F; # (ï¿”; ï¿”; ï¿”; á…¯; á…¯; ) HALFWIDTH HANGUL LETTER WEO
+FFD5;FFD5;FFD5;1170;1170; # (ï¿•; ï¿•; ï¿•; á…°; á…°; ) HALFWIDTH HANGUL LETTER WE
+FFD6;FFD6;FFD6;1171;1171; # (ï¿–; ï¿–; ï¿–; á…±; á…±; ) HALFWIDTH HANGUL LETTER WI
+FFD7;FFD7;FFD7;1172;1172; # (ï¿—; ï¿—; ï¿—; á…²; á…²; ) HALFWIDTH HANGUL LETTER YU
+FFDA;FFDA;FFDA;1173;1173; # (ᅳ; ᅳ; ᅳ; ᅳ; ᅳ; ) HALFWIDTH HANGUL LETTER EU
+FFDB;FFDB;FFDB;1174;1174; # (ï¿›; ï¿›; ï¿›; á…´; á…´; ) HALFWIDTH HANGUL LETTER YI
+FFDC;FFDC;FFDC;1175;1175; # (ᅵ; ᅵ; ᅵ; ᅵ; ᅵ; ) HALFWIDTH HANGUL LETTER I
+FFE0;FFE0;FFE0;00A2;00A2; # (¢; ¢; ¢; ¢; ¢; ) FULLWIDTH CENT SIGN
+FFE1;FFE1;FFE1;00A3;00A3; # (£; £; £; £; £; ) FULLWIDTH POUND SIGN
+FFE2;FFE2;FFE2;00AC;00AC; # (¬; ¬; ¬; ¬; ¬; ) FULLWIDTH NOT SIGN
+FFE3;FFE3;FFE3;0020 0304;0020 0304; # ( ̄;  ̄;  ̄; ◌̄; ◌̄; ) FULLWIDTH MACRON
+FFE4;FFE4;FFE4;00A6;00A6; # (¦; ¦; ¦; ¦; ¦; ) FULLWIDTH BROKEN BAR
+FFE5;FFE5;FFE5;00A5;00A5; # (¥; ¥; ¥; ¥; ¥; ) FULLWIDTH YEN SIGN
+FFE6;FFE6;FFE6;20A9;20A9; # (₩; ₩; ₩; ₩; ₩; ) FULLWIDTH WON SIGN
+FFE8;FFE8;FFE8;2502;2502; # (│; │; │; │; │; ) HALFWIDTH FORMS LIGHT VERTICAL
+FFE9;FFE9;FFE9;2190;2190; # (ï¿©; ï¿©; ï¿©; â†; â†; ) HALFWIDTH LEFTWARDS ARROW
+FFEA;FFEA;FFEA;2191;2191; # (↑; ↑; ↑; ↑; ↑; ) HALFWIDTH UPWARDS ARROW
+FFEB;FFEB;FFEB;2192;2192; # (→; →; →; →; →; ) HALFWIDTH RIGHTWARDS ARROW
+FFEC;FFEC;FFEC;2193;2193; # (↓; ↓; ↓; ↓; ↓; ) HALFWIDTH DOWNWARDS ARROW
+FFED;FFED;FFED;25A0;25A0; # (ï¿­; ï¿­; ï¿­; â– ; â– ; ) HALFWIDTH BLACK SQUARE
+FFEE;FFEE;FFEE;25CB;25CB; # (ï¿®; ï¿®; ï¿®; â—‹; â—‹; ) HALFWIDTH WHITE CIRCLE
+1D15E;1D157 1D165;1D157 1D165;1D157 1D165;1D157 1D165; # (ð…žð…ž; ð…—ð…—ð…¥ð…¥; ð…—ð…—ð…¥ð…¥; ð…—ð…—ð…¥ð…¥; ð…—ð…—ð…¥ð…¥; ) MUSICAL SYMBOL HALF NOTE
+1D15F;1D158 1D165;1D158 1D165;1D158 1D165;1D158 1D165; # (ð…Ÿð…Ÿ; ð…˜ð…˜ð…¥ð…¥; ð…˜ð…˜ð…¥ð…¥; ð…˜ð…˜ð…¥ð…¥; ð…˜ð…˜ð…¥ð…¥; ) MUSICAL SYMBOL QUARTER NOTE
+1D160;1D158 1D165 1D16E;1D158 1D165 1D16E;1D158 1D165 1D16E;1D158 1D165 1D16E; # (ð… ð… ; ð…˜ð…˜ð…¥ð…¥ð…®ð…®; ð…˜ð…˜ð…¥ð…¥ð…®ð…®; ð…˜ð…˜ð…¥ð…¥ð…®ð…®; ð…˜ð…˜ð…¥ð…¥ð…®ð…®; ) MUSICAL SYMBOL EIGHTH NOTE
+1D161;1D158 1D165 1D16F;1D158 1D165 1D16F;1D158 1D165 1D16F;1D158 1D165 1D16F; # (ð…¡ð…¡; ð…˜ð…˜ð…¥ð…¥ð…¯ð…¯; ð…˜ð…˜ð…¥ð…¥ð…¯ð…¯; ð…˜ð…˜ð…¥ð…¥ð…¯ð…¯; ð…˜ð…˜ð…¥ð…¥ð…¯ð…¯; ) MUSICAL SYMBOL SIXTEENTH NOTE
+1D162;1D158 1D165 1D170;1D158 1D165 1D170;1D158 1D165 1D170;1D158 1D165 1D170; # (ð…¢ð…¢; ð…˜ð…˜ð…¥ð…¥ð…°ð…°; ð…˜ð…˜ð…¥ð…¥ð…°ð…°; ð…˜ð…˜ð…¥ð…¥ð…°ð…°; ð…˜ð…˜ð…¥ð…¥ð…°ð…°; ) MUSICAL SYMBOL THIRTY-SECOND NOTE
+1D163;1D158 1D165 1D171;1D158 1D165 1D171;1D158 1D165 1D171;1D158 1D165 1D171; # (ð…£ð…£; ð…˜ð…˜ð…¥ð…¥ð…±ð…±; ð…˜ð…˜ð…¥ð…¥ð…±ð…±; ð…˜ð…˜ð…¥ð…¥ð…±ð…±; ð…˜ð…˜ð…¥ð…¥ð…±ð…±; ) MUSICAL SYMBOL SIXTY-FOURTH NOTE
+1D164;1D158 1D165 1D172;1D158 1D165 1D172;1D158 1D165 1D172;1D158 1D165 1D172; # (ð…¤ð…¤; ð…˜ð…˜ð…¥ð…¥ð…²ð…²; ð…˜ð…˜ð…¥ð…¥ð…²ð…²; ð…˜ð…˜ð…¥ð…¥ð…²ð…²; ð…˜ð…˜ð…¥ð…¥ð…²ð…²; ) MUSICAL SYMBOL ONE HUNDRED TWENTY-EIGHTH NOTE
+1D1BB;1D1B9 1D165;1D1B9 1D165;1D1B9 1D165;1D1B9 1D165; # (ð†»ð†»; ð†¹ð†¹ð…¥ð…¥; ð†¹ð†¹ð…¥ð…¥; ð†¹ð†¹ð…¥ð…¥; ð†¹ð†¹ð…¥ð…¥; ) MUSICAL SYMBOL MINIMA
+1D1BC;1D1BA 1D165;1D1BA 1D165;1D1BA 1D165;1D1BA 1D165; # (ð†¼ð†¼; ð†ºð†ºð…¥ð…¥; ð†ºð†ºð…¥ð…¥; ð†ºð†ºð…¥ð…¥; ð†ºð†ºð…¥ð…¥; ) MUSICAL SYMBOL MINIMA BLACK
+1D1BD;1D1B9 1D165 1D16E;1D1B9 1D165 1D16E;1D1B9 1D165 1D16E;1D1B9 1D165 1D16E; # (ð†½ð†½; ð†¹ð†¹ð…¥ð…¥ð…®ð…®; ð†¹ð†¹ð…¥ð…¥ð…®ð…®; ð†¹ð†¹ð…¥ð…¥ð…®ð…®; ð†¹ð†¹ð…¥ð…¥ð…®ð…®; ) MUSICAL SYMBOL SEMIMINIMA WHITE
+1D1BE;1D1BA 1D165 1D16E;1D1BA 1D165 1D16E;1D1BA 1D165 1D16E;1D1BA 1D165 1D16E; # (ð†¾ð†¾; ð†ºð†ºð…¥ð…¥ð…®ð…®; ð†ºð†ºð…¥ð…¥ð…®ð…®; ð†ºð†ºð…¥ð…¥ð…®ð…®; ð†ºð†ºð…¥ð…¥ð…®ð…®; ) MUSICAL SYMBOL SEMIMINIMA BLACK
+1D1BF;1D1B9 1D165 1D16F;1D1B9 1D165 1D16F;1D1B9 1D165 1D16F;1D1B9 1D165 1D16F; # (ð†¿ð†¿; ð†¹ð†¹ð…¥ð…¥ð…¯ð…¯; ð†¹ð†¹ð…¥ð…¥ð…¯ð…¯; ð†¹ð†¹ð…¥ð…¥ð…¯ð…¯; ð†¹ð†¹ð…¥ð…¥ð…¯ð…¯; ) MUSICAL SYMBOL FUSA WHITE
+1D1C0;1D1BA 1D165 1D16F;1D1BA 1D165 1D16F;1D1BA 1D165 1D16F;1D1BA 1D165 1D16F; # (ð‡€ð‡€; ð†ºð†ºð…¥ð…¥ð…¯ð…¯; ð†ºð†ºð…¥ð…¥ð…¯ð…¯; ð†ºð†ºð…¥ð…¥ð…¯ð…¯; ð†ºð†ºð…¥ð…¥ð…¯ð…¯; ) MUSICAL SYMBOL FUSA BLACK
+1D400;1D400;1D400;0041;0041; # (ð€ð€; ð€ð€; ð€ð€; A; A; ) MATHEMATICAL BOLD CAPITAL A
+1D401;1D401;1D401;0042;0042; # (ðð; ðð; ðð; B; B; ) MATHEMATICAL BOLD CAPITAL B
+1D402;1D402;1D402;0043;0043; # (ð‚ð‚; ð‚ð‚; ð‚ð‚; C; C; ) MATHEMATICAL BOLD CAPITAL C
+1D403;1D403;1D403;0044;0044; # (ðƒðƒ; ðƒðƒ; ðƒðƒ; D; D; ) MATHEMATICAL BOLD CAPITAL D
+1D404;1D404;1D404;0045;0045; # (ð„ð„; ð„ð„; ð„ð„; E; E; ) MATHEMATICAL BOLD CAPITAL E
+1D405;1D405;1D405;0046;0046; # (ð…ð…; ð…ð…; ð…ð…; F; F; ) MATHEMATICAL BOLD CAPITAL F
+1D406;1D406;1D406;0047;0047; # (ð†ð†; ð†ð†; ð†ð†; G; G; ) MATHEMATICAL BOLD CAPITAL G
+1D407;1D407;1D407;0048;0048; # (ð‡ð‡; ð‡ð‡; ð‡ð‡; H; H; ) MATHEMATICAL BOLD CAPITAL H
+1D408;1D408;1D408;0049;0049; # (ðˆðˆ; ðˆðˆ; ðˆðˆ; I; I; ) MATHEMATICAL BOLD CAPITAL I
+1D409;1D409;1D409;004A;004A; # (ð‰ð‰; ð‰ð‰; ð‰ð‰; J; J; ) MATHEMATICAL BOLD CAPITAL J
+1D40A;1D40A;1D40A;004B;004B; # (ðŠðŠ; ðŠðŠ; ðŠðŠ; K; K; ) MATHEMATICAL BOLD CAPITAL K
+1D40B;1D40B;1D40B;004C;004C; # (ð‹ð‹; ð‹ð‹; ð‹ð‹; L; L; ) MATHEMATICAL BOLD CAPITAL L
+1D40C;1D40C;1D40C;004D;004D; # (ðŒðŒ; ðŒðŒ; ðŒðŒ; M; M; ) MATHEMATICAL BOLD CAPITAL M
+1D40D;1D40D;1D40D;004E;004E; # (ðð; ðð; ðð; N; N; ) MATHEMATICAL BOLD CAPITAL N
+1D40E;1D40E;1D40E;004F;004F; # (ðŽðŽ; ðŽðŽ; ðŽðŽ; O; O; ) MATHEMATICAL BOLD CAPITAL O
+1D40F;1D40F;1D40F;0050;0050; # (ðð; ðð; ðð; P; P; ) MATHEMATICAL BOLD CAPITAL P
+1D410;1D410;1D410;0051;0051; # (ðð; ðð; ðð; Q; Q; ) MATHEMATICAL BOLD CAPITAL Q
+1D411;1D411;1D411;0052;0052; # (ð‘ð‘; ð‘ð‘; ð‘ð‘; R; R; ) MATHEMATICAL BOLD CAPITAL R
+1D412;1D412;1D412;0053;0053; # (ð’ð’; ð’ð’; ð’ð’; S; S; ) MATHEMATICAL BOLD CAPITAL S
+1D413;1D413;1D413;0054;0054; # (ð“ð“; ð“ð“; ð“ð“; T; T; ) MATHEMATICAL BOLD CAPITAL T
+1D414;1D414;1D414;0055;0055; # (ð”ð”; ð”ð”; ð”ð”; U; U; ) MATHEMATICAL BOLD CAPITAL U
+1D415;1D415;1D415;0056;0056; # (ð•ð•; ð•ð•; ð•ð•; V; V; ) MATHEMATICAL BOLD CAPITAL V
+1D416;1D416;1D416;0057;0057; # (ð–ð–; ð–ð–; ð–ð–; W; W; ) MATHEMATICAL BOLD CAPITAL W
+1D417;1D417;1D417;0058;0058; # (ð—ð—; ð—ð—; ð—ð—; X; X; ) MATHEMATICAL BOLD CAPITAL X
+1D418;1D418;1D418;0059;0059; # (ð˜ð˜; ð˜ð˜; ð˜ð˜; Y; Y; ) MATHEMATICAL BOLD CAPITAL Y
+1D419;1D419;1D419;005A;005A; # (ð™ð™; ð™ð™; ð™ð™; Z; Z; ) MATHEMATICAL BOLD CAPITAL Z
+1D41A;1D41A;1D41A;0061;0061; # (ðšðš; ðšðš; ðšðš; a; a; ) MATHEMATICAL BOLD SMALL A
+1D41B;1D41B;1D41B;0062;0062; # (ð›ð›; ð›ð›; ð›ð›; b; b; ) MATHEMATICAL BOLD SMALL B
+1D41C;1D41C;1D41C;0063;0063; # (ðœðœ; ðœðœ; ðœðœ; c; c; ) MATHEMATICAL BOLD SMALL C
+1D41D;1D41D;1D41D;0064;0064; # (ðð; ðð; ðð; d; d; ) MATHEMATICAL BOLD SMALL D
+1D41E;1D41E;1D41E;0065;0065; # (ðžðž; ðžðž; ðžðž; e; e; ) MATHEMATICAL BOLD SMALL E
+1D41F;1D41F;1D41F;0066;0066; # (ðŸðŸ; ðŸðŸ; ðŸðŸ; f; f; ) MATHEMATICAL BOLD SMALL F
+1D420;1D420;1D420;0067;0067; # (ð ð ; ð ð ; ð ð ; g; g; ) MATHEMATICAL BOLD SMALL G
+1D421;1D421;1D421;0068;0068; # (ð¡ð¡; ð¡ð¡; ð¡ð¡; h; h; ) MATHEMATICAL BOLD SMALL H
+1D422;1D422;1D422;0069;0069; # (ð¢ð¢; ð¢ð¢; ð¢ð¢; i; i; ) MATHEMATICAL BOLD SMALL I
+1D423;1D423;1D423;006A;006A; # (ð£ð£; ð£ð£; ð£ð£; j; j; ) MATHEMATICAL BOLD SMALL J
+1D424;1D424;1D424;006B;006B; # (ð¤ð¤; ð¤ð¤; ð¤ð¤; k; k; ) MATHEMATICAL BOLD SMALL K
+1D425;1D425;1D425;006C;006C; # (ð¥ð¥; ð¥ð¥; ð¥ð¥; l; l; ) MATHEMATICAL BOLD SMALL L
+1D426;1D426;1D426;006D;006D; # (ð¦ð¦; ð¦ð¦; ð¦ð¦; m; m; ) MATHEMATICAL BOLD SMALL M
+1D427;1D427;1D427;006E;006E; # (ð§ð§; ð§ð§; ð§ð§; n; n; ) MATHEMATICAL BOLD SMALL N
+1D428;1D428;1D428;006F;006F; # (ð¨ð¨; ð¨ð¨; ð¨ð¨; o; o; ) MATHEMATICAL BOLD SMALL O
+1D429;1D429;1D429;0070;0070; # (ð©ð©; ð©ð©; ð©ð©; p; p; ) MATHEMATICAL BOLD SMALL P
+1D42A;1D42A;1D42A;0071;0071; # (ðªðª; ðªðª; ðªðª; q; q; ) MATHEMATICAL BOLD SMALL Q
+1D42B;1D42B;1D42B;0072;0072; # (ð«ð«; ð«ð«; ð«ð«; r; r; ) MATHEMATICAL BOLD SMALL R
+1D42C;1D42C;1D42C;0073;0073; # (ð¬ð¬; ð¬ð¬; ð¬ð¬; s; s; ) MATHEMATICAL BOLD SMALL S
+1D42D;1D42D;1D42D;0074;0074; # (ð­ð­; ð­ð­; ð­ð­; t; t; ) MATHEMATICAL BOLD SMALL T
+1D42E;1D42E;1D42E;0075;0075; # (ð®ð®; ð®ð®; ð®ð®; u; u; ) MATHEMATICAL BOLD SMALL U
+1D42F;1D42F;1D42F;0076;0076; # (ð¯ð¯; ð¯ð¯; ð¯ð¯; v; v; ) MATHEMATICAL BOLD SMALL V
+1D430;1D430;1D430;0077;0077; # (ð°ð°; ð°ð°; ð°ð°; w; w; ) MATHEMATICAL BOLD SMALL W
+1D431;1D431;1D431;0078;0078; # (ð±ð±; ð±ð±; ð±ð±; x; x; ) MATHEMATICAL BOLD SMALL X
+1D432;1D432;1D432;0079;0079; # (ð²ð²; ð²ð²; ð²ð²; y; y; ) MATHEMATICAL BOLD SMALL Y
+1D433;1D433;1D433;007A;007A; # (ð³ð³; ð³ð³; ð³ð³; z; z; ) MATHEMATICAL BOLD SMALL Z
+1D434;1D434;1D434;0041;0041; # (ð´ð´; ð´ð´; ð´ð´; A; A; ) MATHEMATICAL ITALIC CAPITAL A
+1D435;1D435;1D435;0042;0042; # (ðµðµ; ðµðµ; ðµðµ; B; B; ) MATHEMATICAL ITALIC CAPITAL B
+1D436;1D436;1D436;0043;0043; # (ð¶ð¶; ð¶ð¶; ð¶ð¶; C; C; ) MATHEMATICAL ITALIC CAPITAL C
+1D437;1D437;1D437;0044;0044; # (ð·ð·; ð·ð·; ð·ð·; D; D; ) MATHEMATICAL ITALIC CAPITAL D
+1D438;1D438;1D438;0045;0045; # (ð¸ð¸; ð¸ð¸; ð¸ð¸; E; E; ) MATHEMATICAL ITALIC CAPITAL E
+1D439;1D439;1D439;0046;0046; # (ð¹ð¹; ð¹ð¹; ð¹ð¹; F; F; ) MATHEMATICAL ITALIC CAPITAL F
+1D43A;1D43A;1D43A;0047;0047; # (ðºðº; ðºðº; ðºðº; G; G; ) MATHEMATICAL ITALIC CAPITAL G
+1D43B;1D43B;1D43B;0048;0048; # (ð»ð»; ð»ð»; ð»ð»; H; H; ) MATHEMATICAL ITALIC CAPITAL H
+1D43C;1D43C;1D43C;0049;0049; # (ð¼ð¼; ð¼ð¼; ð¼ð¼; I; I; ) MATHEMATICAL ITALIC CAPITAL I
+1D43D;1D43D;1D43D;004A;004A; # (ð½ð½; ð½ð½; ð½ð½; J; J; ) MATHEMATICAL ITALIC CAPITAL J
+1D43E;1D43E;1D43E;004B;004B; # (ð¾ð¾; ð¾ð¾; ð¾ð¾; K; K; ) MATHEMATICAL ITALIC CAPITAL K
+1D43F;1D43F;1D43F;004C;004C; # (ð¿ð¿; ð¿ð¿; ð¿ð¿; L; L; ) MATHEMATICAL ITALIC CAPITAL L
+1D440;1D440;1D440;004D;004D; # (ð‘€ð‘€; ð‘€ð‘€; ð‘€ð‘€; M; M; ) MATHEMATICAL ITALIC CAPITAL M
+1D441;1D441;1D441;004E;004E; # (ð‘ð‘; ð‘ð‘; ð‘ð‘; N; N; ) MATHEMATICAL ITALIC CAPITAL N
+1D442;1D442;1D442;004F;004F; # (ð‘‚ð‘‚; ð‘‚ð‘‚; ð‘‚ð‘‚; O; O; ) MATHEMATICAL ITALIC CAPITAL O
+1D443;1D443;1D443;0050;0050; # (ð‘ƒð‘ƒ; ð‘ƒð‘ƒ; ð‘ƒð‘ƒ; P; P; ) MATHEMATICAL ITALIC CAPITAL P
+1D444;1D444;1D444;0051;0051; # (ð‘„ð‘„; ð‘„ð‘„; ð‘„ð‘„; Q; Q; ) MATHEMATICAL ITALIC CAPITAL Q
+1D445;1D445;1D445;0052;0052; # (ð‘…ð‘…; ð‘…ð‘…; ð‘…ð‘…; R; R; ) MATHEMATICAL ITALIC CAPITAL R
+1D446;1D446;1D446;0053;0053; # (ð‘†ð‘†; ð‘†ð‘†; ð‘†ð‘†; S; S; ) MATHEMATICAL ITALIC CAPITAL S
+1D447;1D447;1D447;0054;0054; # (ð‘‡ð‘‡; ð‘‡ð‘‡; ð‘‡ð‘‡; T; T; ) MATHEMATICAL ITALIC CAPITAL T
+1D448;1D448;1D448;0055;0055; # (ð‘ˆð‘ˆ; ð‘ˆð‘ˆ; ð‘ˆð‘ˆ; U; U; ) MATHEMATICAL ITALIC CAPITAL U
+1D449;1D449;1D449;0056;0056; # (ð‘‰ð‘‰; ð‘‰ð‘‰; ð‘‰ð‘‰; V; V; ) MATHEMATICAL ITALIC CAPITAL V
+1D44A;1D44A;1D44A;0057;0057; # (ð‘Šð‘Š; ð‘Šð‘Š; ð‘Šð‘Š; W; W; ) MATHEMATICAL ITALIC CAPITAL W
+1D44B;1D44B;1D44B;0058;0058; # (ð‘‹ð‘‹; ð‘‹ð‘‹; ð‘‹ð‘‹; X; X; ) MATHEMATICAL ITALIC CAPITAL X
+1D44C;1D44C;1D44C;0059;0059; # (ð‘Œð‘Œ; ð‘Œð‘Œ; ð‘Œð‘Œ; Y; Y; ) MATHEMATICAL ITALIC CAPITAL Y
+1D44D;1D44D;1D44D;005A;005A; # (ð‘ð‘; ð‘ð‘; ð‘ð‘; Z; Z; ) MATHEMATICAL ITALIC CAPITAL Z
+1D44E;1D44E;1D44E;0061;0061; # (ð‘Žð‘Ž; ð‘Žð‘Ž; ð‘Žð‘Ž; a; a; ) MATHEMATICAL ITALIC SMALL A
+1D44F;1D44F;1D44F;0062;0062; # (ð‘ð‘; ð‘ð‘; ð‘ð‘; b; b; ) MATHEMATICAL ITALIC SMALL B
+1D450;1D450;1D450;0063;0063; # (ð‘ð‘; ð‘ð‘; ð‘ð‘; c; c; ) MATHEMATICAL ITALIC SMALL C
+1D451;1D451;1D451;0064;0064; # (ð‘‘ð‘‘; ð‘‘ð‘‘; ð‘‘ð‘‘; d; d; ) MATHEMATICAL ITALIC SMALL D
+1D452;1D452;1D452;0065;0065; # (ð‘’ð‘’; ð‘’ð‘’; ð‘’ð‘’; e; e; ) MATHEMATICAL ITALIC SMALL E
+1D453;1D453;1D453;0066;0066; # (ð‘“ð‘“; ð‘“ð‘“; ð‘“ð‘“; f; f; ) MATHEMATICAL ITALIC SMALL F
+1D454;1D454;1D454;0067;0067; # (ð‘”ð‘”; ð‘”ð‘”; ð‘”ð‘”; g; g; ) MATHEMATICAL ITALIC SMALL G
+1D456;1D456;1D456;0069;0069; # (ð‘–ð‘–; ð‘–ð‘–; ð‘–ð‘–; i; i; ) MATHEMATICAL ITALIC SMALL I
+1D457;1D457;1D457;006A;006A; # (ð‘—ð‘—; ð‘—ð‘—; ð‘—ð‘—; j; j; ) MATHEMATICAL ITALIC SMALL J
+1D458;1D458;1D458;006B;006B; # (ð‘˜ð‘˜; ð‘˜ð‘˜; ð‘˜ð‘˜; k; k; ) MATHEMATICAL ITALIC SMALL K
+1D459;1D459;1D459;006C;006C; # (ð‘™ð‘™; ð‘™ð‘™; ð‘™ð‘™; l; l; ) MATHEMATICAL ITALIC SMALL L
+1D45A;1D45A;1D45A;006D;006D; # (ð‘šð‘š; ð‘šð‘š; ð‘šð‘š; m; m; ) MATHEMATICAL ITALIC SMALL M
+1D45B;1D45B;1D45B;006E;006E; # (ð‘›ð‘›; ð‘›ð‘›; ð‘›ð‘›; n; n; ) MATHEMATICAL ITALIC SMALL N
+1D45C;1D45C;1D45C;006F;006F; # (ð‘œð‘œ; ð‘œð‘œ; ð‘œð‘œ; o; o; ) MATHEMATICAL ITALIC SMALL O
+1D45D;1D45D;1D45D;0070;0070; # (ð‘ð‘; ð‘ð‘; ð‘ð‘; p; p; ) MATHEMATICAL ITALIC SMALL P
+1D45E;1D45E;1D45E;0071;0071; # (ð‘žð‘ž; ð‘žð‘ž; ð‘žð‘ž; q; q; ) MATHEMATICAL ITALIC SMALL Q
+1D45F;1D45F;1D45F;0072;0072; # (ð‘Ÿð‘Ÿ; ð‘Ÿð‘Ÿ; ð‘Ÿð‘Ÿ; r; r; ) MATHEMATICAL ITALIC SMALL R
+1D460;1D460;1D460;0073;0073; # (ð‘ ð‘ ; ð‘ ð‘ ; ð‘ ð‘ ; s; s; ) MATHEMATICAL ITALIC SMALL S
+1D461;1D461;1D461;0074;0074; # (ð‘¡ð‘¡; ð‘¡ð‘¡; ð‘¡ð‘¡; t; t; ) MATHEMATICAL ITALIC SMALL T
+1D462;1D462;1D462;0075;0075; # (ð‘¢ð‘¢; ð‘¢ð‘¢; ð‘¢ð‘¢; u; u; ) MATHEMATICAL ITALIC SMALL U
+1D463;1D463;1D463;0076;0076; # (ð‘£ð‘£; ð‘£ð‘£; ð‘£ð‘£; v; v; ) MATHEMATICAL ITALIC SMALL V
+1D464;1D464;1D464;0077;0077; # (ð‘¤ð‘¤; ð‘¤ð‘¤; ð‘¤ð‘¤; w; w; ) MATHEMATICAL ITALIC SMALL W
+1D465;1D465;1D465;0078;0078; # (ð‘¥ð‘¥; ð‘¥ð‘¥; ð‘¥ð‘¥; x; x; ) MATHEMATICAL ITALIC SMALL X
+1D466;1D466;1D466;0079;0079; # (ð‘¦ð‘¦; ð‘¦ð‘¦; ð‘¦ð‘¦; y; y; ) MATHEMATICAL ITALIC SMALL Y
+1D467;1D467;1D467;007A;007A; # (ð‘§ð‘§; ð‘§ð‘§; ð‘§ð‘§; z; z; ) MATHEMATICAL ITALIC SMALL Z
+1D468;1D468;1D468;0041;0041; # (ð‘¨ð‘¨; ð‘¨ð‘¨; ð‘¨ð‘¨; A; A; ) MATHEMATICAL BOLD ITALIC CAPITAL A
+1D469;1D469;1D469;0042;0042; # (ð‘©ð‘©; ð‘©ð‘©; ð‘©ð‘©; B; B; ) MATHEMATICAL BOLD ITALIC CAPITAL B
+1D46A;1D46A;1D46A;0043;0043; # (ð‘ªð‘ª; ð‘ªð‘ª; ð‘ªð‘ª; C; C; ) MATHEMATICAL BOLD ITALIC CAPITAL C
+1D46B;1D46B;1D46B;0044;0044; # (ð‘«ð‘«; ð‘«ð‘«; ð‘«ð‘«; D; D; ) MATHEMATICAL BOLD ITALIC CAPITAL D
+1D46C;1D46C;1D46C;0045;0045; # (ð‘¬ð‘¬; ð‘¬ð‘¬; ð‘¬ð‘¬; E; E; ) MATHEMATICAL BOLD ITALIC CAPITAL E
+1D46D;1D46D;1D46D;0046;0046; # (ð‘­ð‘­; ð‘­ð‘­; ð‘­ð‘­; F; F; ) MATHEMATICAL BOLD ITALIC CAPITAL F
+1D46E;1D46E;1D46E;0047;0047; # (ð‘®ð‘®; ð‘®ð‘®; ð‘®ð‘®; G; G; ) MATHEMATICAL BOLD ITALIC CAPITAL G
+1D46F;1D46F;1D46F;0048;0048; # (ð‘¯ð‘¯; ð‘¯ð‘¯; ð‘¯ð‘¯; H; H; ) MATHEMATICAL BOLD ITALIC CAPITAL H
+1D470;1D470;1D470;0049;0049; # (ð‘°ð‘°; ð‘°ð‘°; ð‘°ð‘°; I; I; ) MATHEMATICAL BOLD ITALIC CAPITAL I
+1D471;1D471;1D471;004A;004A; # (ð‘±ð‘±; ð‘±ð‘±; ð‘±ð‘±; J; J; ) MATHEMATICAL BOLD ITALIC CAPITAL J
+1D472;1D472;1D472;004B;004B; # (ð‘²ð‘²; ð‘²ð‘²; ð‘²ð‘²; K; K; ) MATHEMATICAL BOLD ITALIC CAPITAL K
+1D473;1D473;1D473;004C;004C; # (ð‘³ð‘³; ð‘³ð‘³; ð‘³ð‘³; L; L; ) MATHEMATICAL BOLD ITALIC CAPITAL L
+1D474;1D474;1D474;004D;004D; # (ð‘´ð‘´; ð‘´ð‘´; ð‘´ð‘´; M; M; ) MATHEMATICAL BOLD ITALIC CAPITAL M
+1D475;1D475;1D475;004E;004E; # (ð‘µð‘µ; ð‘µð‘µ; ð‘µð‘µ; N; N; ) MATHEMATICAL BOLD ITALIC CAPITAL N
+1D476;1D476;1D476;004F;004F; # (ð‘¶ð‘¶; ð‘¶ð‘¶; ð‘¶ð‘¶; O; O; ) MATHEMATICAL BOLD ITALIC CAPITAL O
+1D477;1D477;1D477;0050;0050; # (ð‘·ð‘·; ð‘·ð‘·; ð‘·ð‘·; P; P; ) MATHEMATICAL BOLD ITALIC CAPITAL P
+1D478;1D478;1D478;0051;0051; # (ð‘¸ð‘¸; ð‘¸ð‘¸; ð‘¸ð‘¸; Q; Q; ) MATHEMATICAL BOLD ITALIC CAPITAL Q
+1D479;1D479;1D479;0052;0052; # (ð‘¹ð‘¹; ð‘¹ð‘¹; ð‘¹ð‘¹; R; R; ) MATHEMATICAL BOLD ITALIC CAPITAL R
+1D47A;1D47A;1D47A;0053;0053; # (ð‘ºð‘º; ð‘ºð‘º; ð‘ºð‘º; S; S; ) MATHEMATICAL BOLD ITALIC CAPITAL S
+1D47B;1D47B;1D47B;0054;0054; # (ð‘»ð‘»; ð‘»ð‘»; ð‘»ð‘»; T; T; ) MATHEMATICAL BOLD ITALIC CAPITAL T
+1D47C;1D47C;1D47C;0055;0055; # (ð‘¼ð‘¼; ð‘¼ð‘¼; ð‘¼ð‘¼; U; U; ) MATHEMATICAL BOLD ITALIC CAPITAL U
+1D47D;1D47D;1D47D;0056;0056; # (ð‘½ð‘½; ð‘½ð‘½; ð‘½ð‘½; V; V; ) MATHEMATICAL BOLD ITALIC CAPITAL V
+1D47E;1D47E;1D47E;0057;0057; # (ð‘¾ð‘¾; ð‘¾ð‘¾; ð‘¾ð‘¾; W; W; ) MATHEMATICAL BOLD ITALIC CAPITAL W
+1D47F;1D47F;1D47F;0058;0058; # (ð‘¿ð‘¿; ð‘¿ð‘¿; ð‘¿ð‘¿; X; X; ) MATHEMATICAL BOLD ITALIC CAPITAL X
+1D480;1D480;1D480;0059;0059; # (ð’€ð’€; ð’€ð’€; ð’€ð’€; Y; Y; ) MATHEMATICAL BOLD ITALIC CAPITAL Y
+1D481;1D481;1D481;005A;005A; # (ð’ð’; ð’ð’; ð’ð’; Z; Z; ) MATHEMATICAL BOLD ITALIC CAPITAL Z
+1D482;1D482;1D482;0061;0061; # (ð’‚ð’‚; ð’‚ð’‚; ð’‚ð’‚; a; a; ) MATHEMATICAL BOLD ITALIC SMALL A
+1D483;1D483;1D483;0062;0062; # (ð’ƒð’ƒ; ð’ƒð’ƒ; ð’ƒð’ƒ; b; b; ) MATHEMATICAL BOLD ITALIC SMALL B
+1D484;1D484;1D484;0063;0063; # (ð’„ð’„; ð’„ð’„; ð’„ð’„; c; c; ) MATHEMATICAL BOLD ITALIC SMALL C
+1D485;1D485;1D485;0064;0064; # (ð’…ð’…; ð’…ð’…; ð’…ð’…; d; d; ) MATHEMATICAL BOLD ITALIC SMALL D
+1D486;1D486;1D486;0065;0065; # (ð’†ð’†; ð’†ð’†; ð’†ð’†; e; e; ) MATHEMATICAL BOLD ITALIC SMALL E
+1D487;1D487;1D487;0066;0066; # (ð’‡ð’‡; ð’‡ð’‡; ð’‡ð’‡; f; f; ) MATHEMATICAL BOLD ITALIC SMALL F
+1D488;1D488;1D488;0067;0067; # (ð’ˆð’ˆ; ð’ˆð’ˆ; ð’ˆð’ˆ; g; g; ) MATHEMATICAL BOLD ITALIC SMALL G
+1D489;1D489;1D489;0068;0068; # (ð’‰ð’‰; ð’‰ð’‰; ð’‰ð’‰; h; h; ) MATHEMATICAL BOLD ITALIC SMALL H
+1D48A;1D48A;1D48A;0069;0069; # (ð’Šð’Š; ð’Šð’Š; ð’Šð’Š; i; i; ) MATHEMATICAL BOLD ITALIC SMALL I
+1D48B;1D48B;1D48B;006A;006A; # (ð’‹ð’‹; ð’‹ð’‹; ð’‹ð’‹; j; j; ) MATHEMATICAL BOLD ITALIC SMALL J
+1D48C;1D48C;1D48C;006B;006B; # (ð’Œð’Œ; ð’Œð’Œ; ð’Œð’Œ; k; k; ) MATHEMATICAL BOLD ITALIC SMALL K
+1D48D;1D48D;1D48D;006C;006C; # (ð’ð’; ð’ð’; ð’ð’; l; l; ) MATHEMATICAL BOLD ITALIC SMALL L
+1D48E;1D48E;1D48E;006D;006D; # (ð’Žð’Ž; ð’Žð’Ž; ð’Žð’Ž; m; m; ) MATHEMATICAL BOLD ITALIC SMALL M
+1D48F;1D48F;1D48F;006E;006E; # (ð’ð’; ð’ð’; ð’ð’; n; n; ) MATHEMATICAL BOLD ITALIC SMALL N
+1D490;1D490;1D490;006F;006F; # (ð’ð’; ð’ð’; ð’ð’; o; o; ) MATHEMATICAL BOLD ITALIC SMALL O
+1D491;1D491;1D491;0070;0070; # (ð’‘ð’‘; ð’‘ð’‘; ð’‘ð’‘; p; p; ) MATHEMATICAL BOLD ITALIC SMALL P
+1D492;1D492;1D492;0071;0071; # (ð’’ð’’; ð’’ð’’; ð’’ð’’; q; q; ) MATHEMATICAL BOLD ITALIC SMALL Q
+1D493;1D493;1D493;0072;0072; # (ð’“ð’“; ð’“ð’“; ð’“ð’“; r; r; ) MATHEMATICAL BOLD ITALIC SMALL R
+1D494;1D494;1D494;0073;0073; # (ð’”ð’”; ð’”ð’”; ð’”ð’”; s; s; ) MATHEMATICAL BOLD ITALIC SMALL S
+1D495;1D495;1D495;0074;0074; # (ð’•ð’•; ð’•ð’•; ð’•ð’•; t; t; ) MATHEMATICAL BOLD ITALIC SMALL T
+1D496;1D496;1D496;0075;0075; # (ð’–ð’–; ð’–ð’–; ð’–ð’–; u; u; ) MATHEMATICAL BOLD ITALIC SMALL U
+1D497;1D497;1D497;0076;0076; # (ð’—ð’—; ð’—ð’—; ð’—ð’—; v; v; ) MATHEMATICAL BOLD ITALIC SMALL V
+1D498;1D498;1D498;0077;0077; # (ð’˜ð’˜; ð’˜ð’˜; ð’˜ð’˜; w; w; ) MATHEMATICAL BOLD ITALIC SMALL W
+1D499;1D499;1D499;0078;0078; # (ð’™ð’™; ð’™ð’™; ð’™ð’™; x; x; ) MATHEMATICAL BOLD ITALIC SMALL X
+1D49A;1D49A;1D49A;0079;0079; # (ð’šð’š; ð’šð’š; ð’šð’š; y; y; ) MATHEMATICAL BOLD ITALIC SMALL Y
+1D49B;1D49B;1D49B;007A;007A; # (ð’›ð’›; ð’›ð’›; ð’›ð’›; z; z; ) MATHEMATICAL BOLD ITALIC SMALL Z
+1D49C;1D49C;1D49C;0041;0041; # (ð’œð’œ; ð’œð’œ; ð’œð’œ; A; A; ) MATHEMATICAL SCRIPT CAPITAL A
+1D49E;1D49E;1D49E;0043;0043; # (ð’žð’ž; ð’žð’ž; ð’žð’ž; C; C; ) MATHEMATICAL SCRIPT CAPITAL C
+1D49F;1D49F;1D49F;0044;0044; # (ð’Ÿð’Ÿ; ð’Ÿð’Ÿ; ð’Ÿð’Ÿ; D; D; ) MATHEMATICAL SCRIPT CAPITAL D
+1D4A2;1D4A2;1D4A2;0047;0047; # (ð’¢ð’¢; ð’¢ð’¢; ð’¢ð’¢; G; G; ) MATHEMATICAL SCRIPT CAPITAL G
+1D4A5;1D4A5;1D4A5;004A;004A; # (ð’¥ð’¥; ð’¥ð’¥; ð’¥ð’¥; J; J; ) MATHEMATICAL SCRIPT CAPITAL J
+1D4A6;1D4A6;1D4A6;004B;004B; # (ð’¦ð’¦; ð’¦ð’¦; ð’¦ð’¦; K; K; ) MATHEMATICAL SCRIPT CAPITAL K
+1D4A9;1D4A9;1D4A9;004E;004E; # (ð’©ð’©; ð’©ð’©; ð’©ð’©; N; N; ) MATHEMATICAL SCRIPT CAPITAL N
+1D4AA;1D4AA;1D4AA;004F;004F; # (ð’ªð’ª; ð’ªð’ª; ð’ªð’ª; O; O; ) MATHEMATICAL SCRIPT CAPITAL O
+1D4AB;1D4AB;1D4AB;0050;0050; # (ð’«ð’«; ð’«ð’«; ð’«ð’«; P; P; ) MATHEMATICAL SCRIPT CAPITAL P
+1D4AC;1D4AC;1D4AC;0051;0051; # (ð’¬ð’¬; ð’¬ð’¬; ð’¬ð’¬; Q; Q; ) MATHEMATICAL SCRIPT CAPITAL Q
+1D4AE;1D4AE;1D4AE;0053;0053; # (ð’®ð’®; ð’®ð’®; ð’®ð’®; S; S; ) MATHEMATICAL SCRIPT CAPITAL S
+1D4AF;1D4AF;1D4AF;0054;0054; # (ð’¯ð’¯; ð’¯ð’¯; ð’¯ð’¯; T; T; ) MATHEMATICAL SCRIPT CAPITAL T
+1D4B0;1D4B0;1D4B0;0055;0055; # (ð’°ð’°; ð’°ð’°; ð’°ð’°; U; U; ) MATHEMATICAL SCRIPT CAPITAL U
+1D4B1;1D4B1;1D4B1;0056;0056; # (ð’±ð’±; ð’±ð’±; ð’±ð’±; V; V; ) MATHEMATICAL SCRIPT CAPITAL V
+1D4B2;1D4B2;1D4B2;0057;0057; # (ð’²ð’²; ð’²ð’²; ð’²ð’²; W; W; ) MATHEMATICAL SCRIPT CAPITAL W
+1D4B3;1D4B3;1D4B3;0058;0058; # (ð’³ð’³; ð’³ð’³; ð’³ð’³; X; X; ) MATHEMATICAL SCRIPT CAPITAL X
+1D4B4;1D4B4;1D4B4;0059;0059; # (ð’´ð’´; ð’´ð’´; ð’´ð’´; Y; Y; ) MATHEMATICAL SCRIPT CAPITAL Y
+1D4B5;1D4B5;1D4B5;005A;005A; # (ð’µð’µ; ð’µð’µ; ð’µð’µ; Z; Z; ) MATHEMATICAL SCRIPT CAPITAL Z
+1D4B6;1D4B6;1D4B6;0061;0061; # (ð’¶ð’¶; ð’¶ð’¶; ð’¶ð’¶; a; a; ) MATHEMATICAL SCRIPT SMALL A
+1D4B7;1D4B7;1D4B7;0062;0062; # (ð’·ð’·; ð’·ð’·; ð’·ð’·; b; b; ) MATHEMATICAL SCRIPT SMALL B
+1D4B8;1D4B8;1D4B8;0063;0063; # (ð’¸ð’¸; ð’¸ð’¸; ð’¸ð’¸; c; c; ) MATHEMATICAL SCRIPT SMALL C
+1D4B9;1D4B9;1D4B9;0064;0064; # (ð’¹ð’¹; ð’¹ð’¹; ð’¹ð’¹; d; d; ) MATHEMATICAL SCRIPT SMALL D
+1D4BB;1D4BB;1D4BB;0066;0066; # (ð’»ð’»; ð’»ð’»; ð’»ð’»; f; f; ) MATHEMATICAL SCRIPT SMALL F
+1D4BD;1D4BD;1D4BD;0068;0068; # (ð’½ð’½; ð’½ð’½; ð’½ð’½; h; h; ) MATHEMATICAL SCRIPT SMALL H
+1D4BE;1D4BE;1D4BE;0069;0069; # (ð’¾ð’¾; ð’¾ð’¾; ð’¾ð’¾; i; i; ) MATHEMATICAL SCRIPT SMALL I
+1D4BF;1D4BF;1D4BF;006A;006A; # (ð’¿ð’¿; ð’¿ð’¿; ð’¿ð’¿; j; j; ) MATHEMATICAL SCRIPT SMALL J
+1D4C0;1D4C0;1D4C0;006B;006B; # (ð“€ð“€; ð“€ð“€; ð“€ð“€; k; k; ) MATHEMATICAL SCRIPT SMALL K
+1D4C1;1D4C1;1D4C1;006C;006C; # (ð“ð“; ð“ð“; ð“ð“; l; l; ) MATHEMATICAL SCRIPT SMALL L
+1D4C2;1D4C2;1D4C2;006D;006D; # (ð“‚ð“‚; ð“‚ð“‚; ð“‚ð“‚; m; m; ) MATHEMATICAL SCRIPT SMALL M
+1D4C3;1D4C3;1D4C3;006E;006E; # (ð“ƒð“ƒ; ð“ƒð“ƒ; ð“ƒð“ƒ; n; n; ) MATHEMATICAL SCRIPT SMALL N
+1D4C5;1D4C5;1D4C5;0070;0070; # (ð“…ð“…; ð“…ð“…; ð“…ð“…; p; p; ) MATHEMATICAL SCRIPT SMALL P
+1D4C6;1D4C6;1D4C6;0071;0071; # (ð“†ð“†; ð“†ð“†; ð“†ð“†; q; q; ) MATHEMATICAL SCRIPT SMALL Q
+1D4C7;1D4C7;1D4C7;0072;0072; # (ð“‡ð“‡; ð“‡ð“‡; ð“‡ð“‡; r; r; ) MATHEMATICAL SCRIPT SMALL R
+1D4C8;1D4C8;1D4C8;0073;0073; # (ð“ˆð“ˆ; ð“ˆð“ˆ; ð“ˆð“ˆ; s; s; ) MATHEMATICAL SCRIPT SMALL S
+1D4C9;1D4C9;1D4C9;0074;0074; # (ð“‰ð“‰; ð“‰ð“‰; ð“‰ð“‰; t; t; ) MATHEMATICAL SCRIPT SMALL T
+1D4CA;1D4CA;1D4CA;0075;0075; # (ð“Šð“Š; ð“Šð“Š; ð“Šð“Š; u; u; ) MATHEMATICAL SCRIPT SMALL U
+1D4CB;1D4CB;1D4CB;0076;0076; # (ð“‹ð“‹; ð“‹ð“‹; ð“‹ð“‹; v; v; ) MATHEMATICAL SCRIPT SMALL V
+1D4CC;1D4CC;1D4CC;0077;0077; # (ð“Œð“Œ; ð“Œð“Œ; ð“Œð“Œ; w; w; ) MATHEMATICAL SCRIPT SMALL W
+1D4CD;1D4CD;1D4CD;0078;0078; # (ð“ð“; ð“ð“; ð“ð“; x; x; ) MATHEMATICAL SCRIPT SMALL X
+1D4CE;1D4CE;1D4CE;0079;0079; # (ð“Žð“Ž; ð“Žð“Ž; ð“Žð“Ž; y; y; ) MATHEMATICAL SCRIPT SMALL Y
+1D4CF;1D4CF;1D4CF;007A;007A; # (ð“ð“; ð“ð“; ð“ð“; z; z; ) MATHEMATICAL SCRIPT SMALL Z
+1D4D0;1D4D0;1D4D0;0041;0041; # (ð“ð“; ð“ð“; ð“ð“; A; A; ) MATHEMATICAL BOLD SCRIPT CAPITAL A
+1D4D1;1D4D1;1D4D1;0042;0042; # (ð“‘ð“‘; ð“‘ð“‘; ð“‘ð“‘; B; B; ) MATHEMATICAL BOLD SCRIPT CAPITAL B
+1D4D2;1D4D2;1D4D2;0043;0043; # (ð“’ð“’; ð“’ð“’; ð“’ð“’; C; C; ) MATHEMATICAL BOLD SCRIPT CAPITAL C
+1D4D3;1D4D3;1D4D3;0044;0044; # (ð““ð““; ð““ð““; ð““ð““; D; D; ) MATHEMATICAL BOLD SCRIPT CAPITAL D
+1D4D4;1D4D4;1D4D4;0045;0045; # (ð“”ð“”; ð“”ð“”; ð“”ð“”; E; E; ) MATHEMATICAL BOLD SCRIPT CAPITAL E
+1D4D5;1D4D5;1D4D5;0046;0046; # (ð“•ð“•; ð“•ð“•; ð“•ð“•; F; F; ) MATHEMATICAL BOLD SCRIPT CAPITAL F
+1D4D6;1D4D6;1D4D6;0047;0047; # (ð“–ð“–; ð“–ð“–; ð“–ð“–; G; G; ) MATHEMATICAL BOLD SCRIPT CAPITAL G
+1D4D7;1D4D7;1D4D7;0048;0048; # (ð“—ð“—; ð“—ð“—; ð“—ð“—; H; H; ) MATHEMATICAL BOLD SCRIPT CAPITAL H
+1D4D8;1D4D8;1D4D8;0049;0049; # (ð“˜ð“˜; ð“˜ð“˜; ð“˜ð“˜; I; I; ) MATHEMATICAL BOLD SCRIPT CAPITAL I
+1D4D9;1D4D9;1D4D9;004A;004A; # (ð“™ð“™; ð“™ð“™; ð“™ð“™; J; J; ) MATHEMATICAL BOLD SCRIPT CAPITAL J
+1D4DA;1D4DA;1D4DA;004B;004B; # (ð“šð“š; ð“šð“š; ð“šð“š; K; K; ) MATHEMATICAL BOLD SCRIPT CAPITAL K
+1D4DB;1D4DB;1D4DB;004C;004C; # (ð“›ð“›; ð“›ð“›; ð“›ð“›; L; L; ) MATHEMATICAL BOLD SCRIPT CAPITAL L
+1D4DC;1D4DC;1D4DC;004D;004D; # (ð“œð“œ; ð“œð“œ; ð“œð“œ; M; M; ) MATHEMATICAL BOLD SCRIPT CAPITAL M
+1D4DD;1D4DD;1D4DD;004E;004E; # (ð“ð“; ð“ð“; ð“ð“; N; N; ) MATHEMATICAL BOLD SCRIPT CAPITAL N
+1D4DE;1D4DE;1D4DE;004F;004F; # (ð“žð“ž; ð“žð“ž; ð“žð“ž; O; O; ) MATHEMATICAL BOLD SCRIPT CAPITAL O
+1D4DF;1D4DF;1D4DF;0050;0050; # (ð“Ÿð“Ÿ; ð“Ÿð“Ÿ; ð“Ÿð“Ÿ; P; P; ) MATHEMATICAL BOLD SCRIPT CAPITAL P
+1D4E0;1D4E0;1D4E0;0051;0051; # (ð“ ð“ ; ð“ ð“ ; ð“ ð“ ; Q; Q; ) MATHEMATICAL BOLD SCRIPT CAPITAL Q
+1D4E1;1D4E1;1D4E1;0052;0052; # (ð“¡ð“¡; ð“¡ð“¡; ð“¡ð“¡; R; R; ) MATHEMATICAL BOLD SCRIPT CAPITAL R
+1D4E2;1D4E2;1D4E2;0053;0053; # (ð“¢ð“¢; ð“¢ð“¢; ð“¢ð“¢; S; S; ) MATHEMATICAL BOLD SCRIPT CAPITAL S
+1D4E3;1D4E3;1D4E3;0054;0054; # (ð“£ð“£; ð“£ð“£; ð“£ð“£; T; T; ) MATHEMATICAL BOLD SCRIPT CAPITAL T
+1D4E4;1D4E4;1D4E4;0055;0055; # (ð“¤ð“¤; ð“¤ð“¤; ð“¤ð“¤; U; U; ) MATHEMATICAL BOLD SCRIPT CAPITAL U
+1D4E5;1D4E5;1D4E5;0056;0056; # (ð“¥ð“¥; ð“¥ð“¥; ð“¥ð“¥; V; V; ) MATHEMATICAL BOLD SCRIPT CAPITAL V
+1D4E6;1D4E6;1D4E6;0057;0057; # (ð“¦ð“¦; ð“¦ð“¦; ð“¦ð“¦; W; W; ) MATHEMATICAL BOLD SCRIPT CAPITAL W
+1D4E7;1D4E7;1D4E7;0058;0058; # (ð“§ð“§; ð“§ð“§; ð“§ð“§; X; X; ) MATHEMATICAL BOLD SCRIPT CAPITAL X
+1D4E8;1D4E8;1D4E8;0059;0059; # (ð“¨ð“¨; ð“¨ð“¨; ð“¨ð“¨; Y; Y; ) MATHEMATICAL BOLD SCRIPT CAPITAL Y
+1D4E9;1D4E9;1D4E9;005A;005A; # (ð“©ð“©; ð“©ð“©; ð“©ð“©; Z; Z; ) MATHEMATICAL BOLD SCRIPT CAPITAL Z
+1D4EA;1D4EA;1D4EA;0061;0061; # (ð“ªð“ª; ð“ªð“ª; ð“ªð“ª; a; a; ) MATHEMATICAL BOLD SCRIPT SMALL A
+1D4EB;1D4EB;1D4EB;0062;0062; # (ð“«ð“«; ð“«ð“«; ð“«ð“«; b; b; ) MATHEMATICAL BOLD SCRIPT SMALL B
+1D4EC;1D4EC;1D4EC;0063;0063; # (ð“¬ð“¬; ð“¬ð“¬; ð“¬ð“¬; c; c; ) MATHEMATICAL BOLD SCRIPT SMALL C
+1D4ED;1D4ED;1D4ED;0064;0064; # (ð“­ð“­; ð“­ð“­; ð“­ð“­; d; d; ) MATHEMATICAL BOLD SCRIPT SMALL D
+1D4EE;1D4EE;1D4EE;0065;0065; # (ð“®ð“®; ð“®ð“®; ð“®ð“®; e; e; ) MATHEMATICAL BOLD SCRIPT SMALL E
+1D4EF;1D4EF;1D4EF;0066;0066; # (ð“¯ð“¯; ð“¯ð“¯; ð“¯ð“¯; f; f; ) MATHEMATICAL BOLD SCRIPT SMALL F
+1D4F0;1D4F0;1D4F0;0067;0067; # (ð“°ð“°; ð“°ð“°; ð“°ð“°; g; g; ) MATHEMATICAL BOLD SCRIPT SMALL G
+1D4F1;1D4F1;1D4F1;0068;0068; # (ð“±ð“±; ð“±ð“±; ð“±ð“±; h; h; ) MATHEMATICAL BOLD SCRIPT SMALL H
+1D4F2;1D4F2;1D4F2;0069;0069; # (ð“²ð“²; ð“²ð“²; ð“²ð“²; i; i; ) MATHEMATICAL BOLD SCRIPT SMALL I
+1D4F3;1D4F3;1D4F3;006A;006A; # (ð“³ð“³; ð“³ð“³; ð“³ð“³; j; j; ) MATHEMATICAL BOLD SCRIPT SMALL J
+1D4F4;1D4F4;1D4F4;006B;006B; # (ð“´ð“´; ð“´ð“´; ð“´ð“´; k; k; ) MATHEMATICAL BOLD SCRIPT SMALL K
+1D4F5;1D4F5;1D4F5;006C;006C; # (ð“µð“µ; ð“µð“µ; ð“µð“µ; l; l; ) MATHEMATICAL BOLD SCRIPT SMALL L
+1D4F6;1D4F6;1D4F6;006D;006D; # (ð“¶ð“¶; ð“¶ð“¶; ð“¶ð“¶; m; m; ) MATHEMATICAL BOLD SCRIPT SMALL M
+1D4F7;1D4F7;1D4F7;006E;006E; # (ð“·ð“·; ð“·ð“·; ð“·ð“·; n; n; ) MATHEMATICAL BOLD SCRIPT SMALL N
+1D4F8;1D4F8;1D4F8;006F;006F; # (ð“¸ð“¸; ð“¸ð“¸; ð“¸ð“¸; o; o; ) MATHEMATICAL BOLD SCRIPT SMALL O
+1D4F9;1D4F9;1D4F9;0070;0070; # (ð“¹ð“¹; ð“¹ð“¹; ð“¹ð“¹; p; p; ) MATHEMATICAL BOLD SCRIPT SMALL P
+1D4FA;1D4FA;1D4FA;0071;0071; # (ð“ºð“º; ð“ºð“º; ð“ºð“º; q; q; ) MATHEMATICAL BOLD SCRIPT SMALL Q
+1D4FB;1D4FB;1D4FB;0072;0072; # (ð“»ð“»; ð“»ð“»; ð“»ð“»; r; r; ) MATHEMATICAL BOLD SCRIPT SMALL R
+1D4FC;1D4FC;1D4FC;0073;0073; # (ð“¼ð“¼; ð“¼ð“¼; ð“¼ð“¼; s; s; ) MATHEMATICAL BOLD SCRIPT SMALL S
+1D4FD;1D4FD;1D4FD;0074;0074; # (ð“½ð“½; ð“½ð“½; ð“½ð“½; t; t; ) MATHEMATICAL BOLD SCRIPT SMALL T
+1D4FE;1D4FE;1D4FE;0075;0075; # (ð“¾ð“¾; ð“¾ð“¾; ð“¾ð“¾; u; u; ) MATHEMATICAL BOLD SCRIPT SMALL U
+1D4FF;1D4FF;1D4FF;0076;0076; # (ð“¿ð“¿; ð“¿ð“¿; ð“¿ð“¿; v; v; ) MATHEMATICAL BOLD SCRIPT SMALL V
+1D500;1D500;1D500;0077;0077; # (ð”€ð”€; ð”€ð”€; ð”€ð”€; w; w; ) MATHEMATICAL BOLD SCRIPT SMALL W
+1D501;1D501;1D501;0078;0078; # (ð”ð”; ð”ð”; ð”ð”; x; x; ) MATHEMATICAL BOLD SCRIPT SMALL X
+1D502;1D502;1D502;0079;0079; # (ð”‚ð”‚; ð”‚ð”‚; ð”‚ð”‚; y; y; ) MATHEMATICAL BOLD SCRIPT SMALL Y
+1D503;1D503;1D503;007A;007A; # (ð”ƒð”ƒ; ð”ƒð”ƒ; ð”ƒð”ƒ; z; z; ) MATHEMATICAL BOLD SCRIPT SMALL Z
+1D504;1D504;1D504;0041;0041; # (ð”„ð”„; ð”„ð”„; ð”„ð”„; A; A; ) MATHEMATICAL FRAKTUR CAPITAL A
+1D505;1D505;1D505;0042;0042; # (ð”…ð”…; ð”…ð”…; ð”…ð”…; B; B; ) MATHEMATICAL FRAKTUR CAPITAL B
+1D507;1D507;1D507;0044;0044; # (ð”‡ð”‡; ð”‡ð”‡; ð”‡ð”‡; D; D; ) MATHEMATICAL FRAKTUR CAPITAL D
+1D508;1D508;1D508;0045;0045; # (ð”ˆð”ˆ; ð”ˆð”ˆ; ð”ˆð”ˆ; E; E; ) MATHEMATICAL FRAKTUR CAPITAL E
+1D509;1D509;1D509;0046;0046; # (ð”‰ð”‰; ð”‰ð”‰; ð”‰ð”‰; F; F; ) MATHEMATICAL FRAKTUR CAPITAL F
+1D50A;1D50A;1D50A;0047;0047; # (ð”Šð”Š; ð”Šð”Š; ð”Šð”Š; G; G; ) MATHEMATICAL FRAKTUR CAPITAL G
+1D50D;1D50D;1D50D;004A;004A; # (ð”ð”; ð”ð”; ð”ð”; J; J; ) MATHEMATICAL FRAKTUR CAPITAL J
+1D50E;1D50E;1D50E;004B;004B; # (ð”Žð”Ž; ð”Žð”Ž; ð”Žð”Ž; K; K; ) MATHEMATICAL FRAKTUR CAPITAL K
+1D50F;1D50F;1D50F;004C;004C; # (ð”ð”; ð”ð”; ð”ð”; L; L; ) MATHEMATICAL FRAKTUR CAPITAL L
+1D510;1D510;1D510;004D;004D; # (ð”ð”; ð”ð”; ð”ð”; M; M; ) MATHEMATICAL FRAKTUR CAPITAL M
+1D511;1D511;1D511;004E;004E; # (ð”‘ð”‘; ð”‘ð”‘; ð”‘ð”‘; N; N; ) MATHEMATICAL FRAKTUR CAPITAL N
+1D512;1D512;1D512;004F;004F; # (ð”’ð”’; ð”’ð”’; ð”’ð”’; O; O; ) MATHEMATICAL FRAKTUR CAPITAL O
+1D513;1D513;1D513;0050;0050; # (ð”“ð”“; ð”“ð”“; ð”“ð”“; P; P; ) MATHEMATICAL FRAKTUR CAPITAL P
+1D514;1D514;1D514;0051;0051; # (ð””ð””; ð””ð””; ð””ð””; Q; Q; ) MATHEMATICAL FRAKTUR CAPITAL Q
+1D516;1D516;1D516;0053;0053; # (ð”–ð”–; ð”–ð”–; ð”–ð”–; S; S; ) MATHEMATICAL FRAKTUR CAPITAL S
+1D517;1D517;1D517;0054;0054; # (ð”—ð”—; ð”—ð”—; ð”—ð”—; T; T; ) MATHEMATICAL FRAKTUR CAPITAL T
+1D518;1D518;1D518;0055;0055; # (ð”˜ð”˜; ð”˜ð”˜; ð”˜ð”˜; U; U; ) MATHEMATICAL FRAKTUR CAPITAL U
+1D519;1D519;1D519;0056;0056; # (ð”™ð”™; ð”™ð”™; ð”™ð”™; V; V; ) MATHEMATICAL FRAKTUR CAPITAL V
+1D51A;1D51A;1D51A;0057;0057; # (ð”šð”š; ð”šð”š; ð”šð”š; W; W; ) MATHEMATICAL FRAKTUR CAPITAL W
+1D51B;1D51B;1D51B;0058;0058; # (ð”›ð”›; ð”›ð”›; ð”›ð”›; X; X; ) MATHEMATICAL FRAKTUR CAPITAL X
+1D51C;1D51C;1D51C;0059;0059; # (ð”œð”œ; ð”œð”œ; ð”œð”œ; Y; Y; ) MATHEMATICAL FRAKTUR CAPITAL Y
+1D51E;1D51E;1D51E;0061;0061; # (ð”žð”ž; ð”žð”ž; ð”žð”ž; a; a; ) MATHEMATICAL FRAKTUR SMALL A
+1D51F;1D51F;1D51F;0062;0062; # (ð”Ÿð”Ÿ; ð”Ÿð”Ÿ; ð”Ÿð”Ÿ; b; b; ) MATHEMATICAL FRAKTUR SMALL B
+1D520;1D520;1D520;0063;0063; # (ð” ð” ; ð” ð” ; ð” ð” ; c; c; ) MATHEMATICAL FRAKTUR SMALL C
+1D521;1D521;1D521;0064;0064; # (ð”¡ð”¡; ð”¡ð”¡; ð”¡ð”¡; d; d; ) MATHEMATICAL FRAKTUR SMALL D
+1D522;1D522;1D522;0065;0065; # (ð”¢ð”¢; ð”¢ð”¢; ð”¢ð”¢; e; e; ) MATHEMATICAL FRAKTUR SMALL E
+1D523;1D523;1D523;0066;0066; # (ð”£ð”£; ð”£ð”£; ð”£ð”£; f; f; ) MATHEMATICAL FRAKTUR SMALL F
+1D524;1D524;1D524;0067;0067; # (ð”¤ð”¤; ð”¤ð”¤; ð”¤ð”¤; g; g; ) MATHEMATICAL FRAKTUR SMALL G
+1D525;1D525;1D525;0068;0068; # (ð”¥ð”¥; ð”¥ð”¥; ð”¥ð”¥; h; h; ) MATHEMATICAL FRAKTUR SMALL H
+1D526;1D526;1D526;0069;0069; # (ð”¦ð”¦; ð”¦ð”¦; ð”¦ð”¦; i; i; ) MATHEMATICAL FRAKTUR SMALL I
+1D527;1D527;1D527;006A;006A; # (ð”§ð”§; ð”§ð”§; ð”§ð”§; j; j; ) MATHEMATICAL FRAKTUR SMALL J
+1D528;1D528;1D528;006B;006B; # (ð”¨ð”¨; ð”¨ð”¨; ð”¨ð”¨; k; k; ) MATHEMATICAL FRAKTUR SMALL K
+1D529;1D529;1D529;006C;006C; # (ð”©ð”©; ð”©ð”©; ð”©ð”©; l; l; ) MATHEMATICAL FRAKTUR SMALL L
+1D52A;1D52A;1D52A;006D;006D; # (ð”ªð”ª; ð”ªð”ª; ð”ªð”ª; m; m; ) MATHEMATICAL FRAKTUR SMALL M
+1D52B;1D52B;1D52B;006E;006E; # (ð”«ð”«; ð”«ð”«; ð”«ð”«; n; n; ) MATHEMATICAL FRAKTUR SMALL N
+1D52C;1D52C;1D52C;006F;006F; # (ð”¬ð”¬; ð”¬ð”¬; ð”¬ð”¬; o; o; ) MATHEMATICAL FRAKTUR SMALL O
+1D52D;1D52D;1D52D;0070;0070; # (ð”­ð”­; ð”­ð”­; ð”­ð”­; p; p; ) MATHEMATICAL FRAKTUR SMALL P
+1D52E;1D52E;1D52E;0071;0071; # (ð”®ð”®; ð”®ð”®; ð”®ð”®; q; q; ) MATHEMATICAL FRAKTUR SMALL Q
+1D52F;1D52F;1D52F;0072;0072; # (ð”¯ð”¯; ð”¯ð”¯; ð”¯ð”¯; r; r; ) MATHEMATICAL FRAKTUR SMALL R
+1D530;1D530;1D530;0073;0073; # (ð”°ð”°; ð”°ð”°; ð”°ð”°; s; s; ) MATHEMATICAL FRAKTUR SMALL S
+1D531;1D531;1D531;0074;0074; # (ð”±ð”±; ð”±ð”±; ð”±ð”±; t; t; ) MATHEMATICAL FRAKTUR SMALL T
+1D532;1D532;1D532;0075;0075; # (ð”²ð”²; ð”²ð”²; ð”²ð”²; u; u; ) MATHEMATICAL FRAKTUR SMALL U
+1D533;1D533;1D533;0076;0076; # (ð”³ð”³; ð”³ð”³; ð”³ð”³; v; v; ) MATHEMATICAL FRAKTUR SMALL V
+1D534;1D534;1D534;0077;0077; # (ð”´ð”´; ð”´ð”´; ð”´ð”´; w; w; ) MATHEMATICAL FRAKTUR SMALL W
+1D535;1D535;1D535;0078;0078; # (ð”µð”µ; ð”µð”µ; ð”µð”µ; x; x; ) MATHEMATICAL FRAKTUR SMALL X
+1D536;1D536;1D536;0079;0079; # (ð”¶ð”¶; ð”¶ð”¶; ð”¶ð”¶; y; y; ) MATHEMATICAL FRAKTUR SMALL Y
+1D537;1D537;1D537;007A;007A; # (ð”·ð”·; ð”·ð”·; ð”·ð”·; z; z; ) MATHEMATICAL FRAKTUR SMALL Z
+1D538;1D538;1D538;0041;0041; # (ð”¸ð”¸; ð”¸ð”¸; ð”¸ð”¸; A; A; ) MATHEMATICAL DOUBLE-STRUCK CAPITAL A
+1D539;1D539;1D539;0042;0042; # (ð”¹ð”¹; ð”¹ð”¹; ð”¹ð”¹; B; B; ) MATHEMATICAL DOUBLE-STRUCK CAPITAL B
+1D53B;1D53B;1D53B;0044;0044; # (ð”»ð”»; ð”»ð”»; ð”»ð”»; D; D; ) MATHEMATICAL DOUBLE-STRUCK CAPITAL D
+1D53C;1D53C;1D53C;0045;0045; # (ð”¼ð”¼; ð”¼ð”¼; ð”¼ð”¼; E; E; ) MATHEMATICAL DOUBLE-STRUCK CAPITAL E
+1D53D;1D53D;1D53D;0046;0046; # (ð”½ð”½; ð”½ð”½; ð”½ð”½; F; F; ) MATHEMATICAL DOUBLE-STRUCK CAPITAL F
+1D53E;1D53E;1D53E;0047;0047; # (ð”¾ð”¾; ð”¾ð”¾; ð”¾ð”¾; G; G; ) MATHEMATICAL DOUBLE-STRUCK CAPITAL G
+1D540;1D540;1D540;0049;0049; # (ð•€ð•€; ð•€ð•€; ð•€ð•€; I; I; ) MATHEMATICAL DOUBLE-STRUCK CAPITAL I
+1D541;1D541;1D541;004A;004A; # (ð•ð•; ð•ð•; ð•ð•; J; J; ) MATHEMATICAL DOUBLE-STRUCK CAPITAL J
+1D542;1D542;1D542;004B;004B; # (ð•‚ð•‚; ð•‚ð•‚; ð•‚ð•‚; K; K; ) MATHEMATICAL DOUBLE-STRUCK CAPITAL K
+1D543;1D543;1D543;004C;004C; # (ð•ƒð•ƒ; ð•ƒð•ƒ; ð•ƒð•ƒ; L; L; ) MATHEMATICAL DOUBLE-STRUCK CAPITAL L
+1D544;1D544;1D544;004D;004D; # (ð•„ð•„; ð•„ð•„; ð•„ð•„; M; M; ) MATHEMATICAL DOUBLE-STRUCK CAPITAL M
+1D546;1D546;1D546;004F;004F; # (ð•†ð•†; ð•†ð•†; ð•†ð•†; O; O; ) MATHEMATICAL DOUBLE-STRUCK CAPITAL O
+1D54A;1D54A;1D54A;0053;0053; # (ð•Šð•Š; ð•Šð•Š; ð•Šð•Š; S; S; ) MATHEMATICAL DOUBLE-STRUCK CAPITAL S
+1D54B;1D54B;1D54B;0054;0054; # (ð•‹ð•‹; ð•‹ð•‹; ð•‹ð•‹; T; T; ) MATHEMATICAL DOUBLE-STRUCK CAPITAL T
+1D54C;1D54C;1D54C;0055;0055; # (ð•Œð•Œ; ð•Œð•Œ; ð•Œð•Œ; U; U; ) MATHEMATICAL DOUBLE-STRUCK CAPITAL U
+1D54D;1D54D;1D54D;0056;0056; # (ð•ð•; ð•ð•; ð•ð•; V; V; ) MATHEMATICAL DOUBLE-STRUCK CAPITAL V
+1D54E;1D54E;1D54E;0057;0057; # (ð•Žð•Ž; ð•Žð•Ž; ð•Žð•Ž; W; W; ) MATHEMATICAL DOUBLE-STRUCK CAPITAL W
+1D54F;1D54F;1D54F;0058;0058; # (ð•ð•; ð•ð•; ð•ð•; X; X; ) MATHEMATICAL DOUBLE-STRUCK CAPITAL X
+1D550;1D550;1D550;0059;0059; # (ð•ð•; ð•ð•; ð•ð•; Y; Y; ) MATHEMATICAL DOUBLE-STRUCK CAPITAL Y
+1D552;1D552;1D552;0061;0061; # (ð•’ð•’; ð•’ð•’; ð•’ð•’; a; a; ) MATHEMATICAL DOUBLE-STRUCK SMALL A
+1D553;1D553;1D553;0062;0062; # (ð•“ð•“; ð•“ð•“; ð•“ð•“; b; b; ) MATHEMATICAL DOUBLE-STRUCK SMALL B
+1D554;1D554;1D554;0063;0063; # (ð•”ð•”; ð•”ð•”; ð•”ð•”; c; c; ) MATHEMATICAL DOUBLE-STRUCK SMALL C
+1D555;1D555;1D555;0064;0064; # (ð••ð••; ð••ð••; ð••ð••; d; d; ) MATHEMATICAL DOUBLE-STRUCK SMALL D
+1D556;1D556;1D556;0065;0065; # (ð•–ð•–; ð•–ð•–; ð•–ð•–; e; e; ) MATHEMATICAL DOUBLE-STRUCK SMALL E
+1D557;1D557;1D557;0066;0066; # (ð•—ð•—; ð•—ð•—; ð•—ð•—; f; f; ) MATHEMATICAL DOUBLE-STRUCK SMALL F
+1D558;1D558;1D558;0067;0067; # (ð•˜ð•˜; ð•˜ð•˜; ð•˜ð•˜; g; g; ) MATHEMATICAL DOUBLE-STRUCK SMALL G
+1D559;1D559;1D559;0068;0068; # (ð•™ð•™; ð•™ð•™; ð•™ð•™; h; h; ) MATHEMATICAL DOUBLE-STRUCK SMALL H
+1D55A;1D55A;1D55A;0069;0069; # (ð•šð•š; ð•šð•š; ð•šð•š; i; i; ) MATHEMATICAL DOUBLE-STRUCK SMALL I
+1D55B;1D55B;1D55B;006A;006A; # (ð•›ð•›; ð•›ð•›; ð•›ð•›; j; j; ) MATHEMATICAL DOUBLE-STRUCK SMALL J
+1D55C;1D55C;1D55C;006B;006B; # (ð•œð•œ; ð•œð•œ; ð•œð•œ; k; k; ) MATHEMATICAL DOUBLE-STRUCK SMALL K
+1D55D;1D55D;1D55D;006C;006C; # (ð•ð•; ð•ð•; ð•ð•; l; l; ) MATHEMATICAL DOUBLE-STRUCK SMALL L
+1D55E;1D55E;1D55E;006D;006D; # (ð•žð•ž; ð•žð•ž; ð•žð•ž; m; m; ) MATHEMATICAL DOUBLE-STRUCK SMALL M
+1D55F;1D55F;1D55F;006E;006E; # (ð•Ÿð•Ÿ; ð•Ÿð•Ÿ; ð•Ÿð•Ÿ; n; n; ) MATHEMATICAL DOUBLE-STRUCK SMALL N
+1D560;1D560;1D560;006F;006F; # (ð• ð• ; ð• ð• ; ð• ð• ; o; o; ) MATHEMATICAL DOUBLE-STRUCK SMALL O
+1D561;1D561;1D561;0070;0070; # (ð•¡ð•¡; ð•¡ð•¡; ð•¡ð•¡; p; p; ) MATHEMATICAL DOUBLE-STRUCK SMALL P
+1D562;1D562;1D562;0071;0071; # (ð•¢ð•¢; ð•¢ð•¢; ð•¢ð•¢; q; q; ) MATHEMATICAL DOUBLE-STRUCK SMALL Q
+1D563;1D563;1D563;0072;0072; # (ð•£ð•£; ð•£ð•£; ð•£ð•£; r; r; ) MATHEMATICAL DOUBLE-STRUCK SMALL R
+1D564;1D564;1D564;0073;0073; # (ð•¤ð•¤; ð•¤ð•¤; ð•¤ð•¤; s; s; ) MATHEMATICAL DOUBLE-STRUCK SMALL S
+1D565;1D565;1D565;0074;0074; # (ð•¥ð•¥; ð•¥ð•¥; ð•¥ð•¥; t; t; ) MATHEMATICAL DOUBLE-STRUCK SMALL T
+1D566;1D566;1D566;0075;0075; # (ð•¦ð•¦; ð•¦ð•¦; ð•¦ð•¦; u; u; ) MATHEMATICAL DOUBLE-STRUCK SMALL U
+1D567;1D567;1D567;0076;0076; # (ð•§ð•§; ð•§ð•§; ð•§ð•§; v; v; ) MATHEMATICAL DOUBLE-STRUCK SMALL V
+1D568;1D568;1D568;0077;0077; # (ð•¨ð•¨; ð•¨ð•¨; ð•¨ð•¨; w; w; ) MATHEMATICAL DOUBLE-STRUCK SMALL W
+1D569;1D569;1D569;0078;0078; # (ð•©ð•©; ð•©ð•©; ð•©ð•©; x; x; ) MATHEMATICAL DOUBLE-STRUCK SMALL X
+1D56A;1D56A;1D56A;0079;0079; # (ð•ªð•ª; ð•ªð•ª; ð•ªð•ª; y; y; ) MATHEMATICAL DOUBLE-STRUCK SMALL Y
+1D56B;1D56B;1D56B;007A;007A; # (ð•«ð•«; ð•«ð•«; ð•«ð•«; z; z; ) MATHEMATICAL DOUBLE-STRUCK SMALL Z
+1D56C;1D56C;1D56C;0041;0041; # (ð•¬ð•¬; ð•¬ð•¬; ð•¬ð•¬; A; A; ) MATHEMATICAL BOLD FRAKTUR CAPITAL A
+1D56D;1D56D;1D56D;0042;0042; # (ð•­ð•­; ð•­ð•­; ð•­ð•­; B; B; ) MATHEMATICAL BOLD FRAKTUR CAPITAL B
+1D56E;1D56E;1D56E;0043;0043; # (ð•®ð•®; ð•®ð•®; ð•®ð•®; C; C; ) MATHEMATICAL BOLD FRAKTUR CAPITAL C
+1D56F;1D56F;1D56F;0044;0044; # (ð•¯ð•¯; ð•¯ð•¯; ð•¯ð•¯; D; D; ) MATHEMATICAL BOLD FRAKTUR CAPITAL D
+1D570;1D570;1D570;0045;0045; # (ð•°ð•°; ð•°ð•°; ð•°ð•°; E; E; ) MATHEMATICAL BOLD FRAKTUR CAPITAL E
+1D571;1D571;1D571;0046;0046; # (ð•±ð•±; ð•±ð•±; ð•±ð•±; F; F; ) MATHEMATICAL BOLD FRAKTUR CAPITAL F
+1D572;1D572;1D572;0047;0047; # (ð•²ð•²; ð•²ð•²; ð•²ð•²; G; G; ) MATHEMATICAL BOLD FRAKTUR CAPITAL G
+1D573;1D573;1D573;0048;0048; # (ð•³ð•³; ð•³ð•³; ð•³ð•³; H; H; ) MATHEMATICAL BOLD FRAKTUR CAPITAL H
+1D574;1D574;1D574;0049;0049; # (ð•´ð•´; ð•´ð•´; ð•´ð•´; I; I; ) MATHEMATICAL BOLD FRAKTUR CAPITAL I
+1D575;1D575;1D575;004A;004A; # (ð•µð•µ; ð•µð•µ; ð•µð•µ; J; J; ) MATHEMATICAL BOLD FRAKTUR CAPITAL J
+1D576;1D576;1D576;004B;004B; # (ð•¶ð•¶; ð•¶ð•¶; ð•¶ð•¶; K; K; ) MATHEMATICAL BOLD FRAKTUR CAPITAL K
+1D577;1D577;1D577;004C;004C; # (ð•·ð•·; ð•·ð•·; ð•·ð•·; L; L; ) MATHEMATICAL BOLD FRAKTUR CAPITAL L
+1D578;1D578;1D578;004D;004D; # (ð•¸ð•¸; ð•¸ð•¸; ð•¸ð•¸; M; M; ) MATHEMATICAL BOLD FRAKTUR CAPITAL M
+1D579;1D579;1D579;004E;004E; # (ð•¹ð•¹; ð•¹ð•¹; ð•¹ð•¹; N; N; ) MATHEMATICAL BOLD FRAKTUR CAPITAL N
+1D57A;1D57A;1D57A;004F;004F; # (ð•ºð•º; ð•ºð•º; ð•ºð•º; O; O; ) MATHEMATICAL BOLD FRAKTUR CAPITAL O
+1D57B;1D57B;1D57B;0050;0050; # (ð•»ð•»; ð•»ð•»; ð•»ð•»; P; P; ) MATHEMATICAL BOLD FRAKTUR CAPITAL P
+1D57C;1D57C;1D57C;0051;0051; # (ð•¼ð•¼; ð•¼ð•¼; ð•¼ð•¼; Q; Q; ) MATHEMATICAL BOLD FRAKTUR CAPITAL Q
+1D57D;1D57D;1D57D;0052;0052; # (ð•½ð•½; ð•½ð•½; ð•½ð•½; R; R; ) MATHEMATICAL BOLD FRAKTUR CAPITAL R
+1D57E;1D57E;1D57E;0053;0053; # (ð•¾ð•¾; ð•¾ð•¾; ð•¾ð•¾; S; S; ) MATHEMATICAL BOLD FRAKTUR CAPITAL S
+1D57F;1D57F;1D57F;0054;0054; # (ð•¿ð•¿; ð•¿ð•¿; ð•¿ð•¿; T; T; ) MATHEMATICAL BOLD FRAKTUR CAPITAL T
+1D580;1D580;1D580;0055;0055; # (ð–€ð–€; ð–€ð–€; ð–€ð–€; U; U; ) MATHEMATICAL BOLD FRAKTUR CAPITAL U
+1D581;1D581;1D581;0056;0056; # (ð–ð–; ð–ð–; ð–ð–; V; V; ) MATHEMATICAL BOLD FRAKTUR CAPITAL V
+1D582;1D582;1D582;0057;0057; # (ð–‚ð–‚; ð–‚ð–‚; ð–‚ð–‚; W; W; ) MATHEMATICAL BOLD FRAKTUR CAPITAL W
+1D583;1D583;1D583;0058;0058; # (ð–ƒð–ƒ; ð–ƒð–ƒ; ð–ƒð–ƒ; X; X; ) MATHEMATICAL BOLD FRAKTUR CAPITAL X
+1D584;1D584;1D584;0059;0059; # (ð–„ð–„; ð–„ð–„; ð–„ð–„; Y; Y; ) MATHEMATICAL BOLD FRAKTUR CAPITAL Y
+1D585;1D585;1D585;005A;005A; # (ð–…ð–…; ð–…ð–…; ð–…ð–…; Z; Z; ) MATHEMATICAL BOLD FRAKTUR CAPITAL Z
+1D586;1D586;1D586;0061;0061; # (ð–†ð–†; ð–†ð–†; ð–†ð–†; a; a; ) MATHEMATICAL BOLD FRAKTUR SMALL A
+1D587;1D587;1D587;0062;0062; # (ð–‡ð–‡; ð–‡ð–‡; ð–‡ð–‡; b; b; ) MATHEMATICAL BOLD FRAKTUR SMALL B
+1D588;1D588;1D588;0063;0063; # (ð–ˆð–ˆ; ð–ˆð–ˆ; ð–ˆð–ˆ; c; c; ) MATHEMATICAL BOLD FRAKTUR SMALL C
+1D589;1D589;1D589;0064;0064; # (ð–‰ð–‰; ð–‰ð–‰; ð–‰ð–‰; d; d; ) MATHEMATICAL BOLD FRAKTUR SMALL D
+1D58A;1D58A;1D58A;0065;0065; # (ð–Šð–Š; ð–Šð–Š; ð–Šð–Š; e; e; ) MATHEMATICAL BOLD FRAKTUR SMALL E
+1D58B;1D58B;1D58B;0066;0066; # (ð–‹ð–‹; ð–‹ð–‹; ð–‹ð–‹; f; f; ) MATHEMATICAL BOLD FRAKTUR SMALL F
+1D58C;1D58C;1D58C;0067;0067; # (ð–Œð–Œ; ð–Œð–Œ; ð–Œð–Œ; g; g; ) MATHEMATICAL BOLD FRAKTUR SMALL G
+1D58D;1D58D;1D58D;0068;0068; # (ð–ð–; ð–ð–; ð–ð–; h; h; ) MATHEMATICAL BOLD FRAKTUR SMALL H
+1D58E;1D58E;1D58E;0069;0069; # (ð–Žð–Ž; ð–Žð–Ž; ð–Žð–Ž; i; i; ) MATHEMATICAL BOLD FRAKTUR SMALL I
+1D58F;1D58F;1D58F;006A;006A; # (ð–ð–; ð–ð–; ð–ð–; j; j; ) MATHEMATICAL BOLD FRAKTUR SMALL J
+1D590;1D590;1D590;006B;006B; # (ð–ð–; ð–ð–; ð–ð–; k; k; ) MATHEMATICAL BOLD FRAKTUR SMALL K
+1D591;1D591;1D591;006C;006C; # (ð–‘ð–‘; ð–‘ð–‘; ð–‘ð–‘; l; l; ) MATHEMATICAL BOLD FRAKTUR SMALL L
+1D592;1D592;1D592;006D;006D; # (ð–’ð–’; ð–’ð–’; ð–’ð–’; m; m; ) MATHEMATICAL BOLD FRAKTUR SMALL M
+1D593;1D593;1D593;006E;006E; # (ð–“ð–“; ð–“ð–“; ð–“ð–“; n; n; ) MATHEMATICAL BOLD FRAKTUR SMALL N
+1D594;1D594;1D594;006F;006F; # (ð–”ð–”; ð–”ð–”; ð–”ð–”; o; o; ) MATHEMATICAL BOLD FRAKTUR SMALL O
+1D595;1D595;1D595;0070;0070; # (ð–•ð–•; ð–•ð–•; ð–•ð–•; p; p; ) MATHEMATICAL BOLD FRAKTUR SMALL P
+1D596;1D596;1D596;0071;0071; # (ð––ð––; ð––ð––; ð––ð––; q; q; ) MATHEMATICAL BOLD FRAKTUR SMALL Q
+1D597;1D597;1D597;0072;0072; # (ð–—ð–—; ð–—ð–—; ð–—ð–—; r; r; ) MATHEMATICAL BOLD FRAKTUR SMALL R
+1D598;1D598;1D598;0073;0073; # (ð–˜ð–˜; ð–˜ð–˜; ð–˜ð–˜; s; s; ) MATHEMATICAL BOLD FRAKTUR SMALL S
+1D599;1D599;1D599;0074;0074; # (ð–™ð–™; ð–™ð–™; ð–™ð–™; t; t; ) MATHEMATICAL BOLD FRAKTUR SMALL T
+1D59A;1D59A;1D59A;0075;0075; # (ð–šð–š; ð–šð–š; ð–šð–š; u; u; ) MATHEMATICAL BOLD FRAKTUR SMALL U
+1D59B;1D59B;1D59B;0076;0076; # (ð–›ð–›; ð–›ð–›; ð–›ð–›; v; v; ) MATHEMATICAL BOLD FRAKTUR SMALL V
+1D59C;1D59C;1D59C;0077;0077; # (ð–œð–œ; ð–œð–œ; ð–œð–œ; w; w; ) MATHEMATICAL BOLD FRAKTUR SMALL W
+1D59D;1D59D;1D59D;0078;0078; # (ð–ð–; ð–ð–; ð–ð–; x; x; ) MATHEMATICAL BOLD FRAKTUR SMALL X
+1D59E;1D59E;1D59E;0079;0079; # (ð–žð–ž; ð–žð–ž; ð–žð–ž; y; y; ) MATHEMATICAL BOLD FRAKTUR SMALL Y
+1D59F;1D59F;1D59F;007A;007A; # (ð–Ÿð–Ÿ; ð–Ÿð–Ÿ; ð–Ÿð–Ÿ; z; z; ) MATHEMATICAL BOLD FRAKTUR SMALL Z
+1D5A0;1D5A0;1D5A0;0041;0041; # (ð– ð– ; ð– ð– ; ð– ð– ; A; A; ) MATHEMATICAL SANS-SERIF CAPITAL A
+1D5A1;1D5A1;1D5A1;0042;0042; # (ð–¡ð–¡; ð–¡ð–¡; ð–¡ð–¡; B; B; ) MATHEMATICAL SANS-SERIF CAPITAL B
+1D5A2;1D5A2;1D5A2;0043;0043; # (ð–¢ð–¢; ð–¢ð–¢; ð–¢ð–¢; C; C; ) MATHEMATICAL SANS-SERIF CAPITAL C
+1D5A3;1D5A3;1D5A3;0044;0044; # (ð–£ð–£; ð–£ð–£; ð–£ð–£; D; D; ) MATHEMATICAL SANS-SERIF CAPITAL D
+1D5A4;1D5A4;1D5A4;0045;0045; # (ð–¤ð–¤; ð–¤ð–¤; ð–¤ð–¤; E; E; ) MATHEMATICAL SANS-SERIF CAPITAL E
+1D5A5;1D5A5;1D5A5;0046;0046; # (ð–¥ð–¥; ð–¥ð–¥; ð–¥ð–¥; F; F; ) MATHEMATICAL SANS-SERIF CAPITAL F
+1D5A6;1D5A6;1D5A6;0047;0047; # (ð–¦ð–¦; ð–¦ð–¦; ð–¦ð–¦; G; G; ) MATHEMATICAL SANS-SERIF CAPITAL G
+1D5A7;1D5A7;1D5A7;0048;0048; # (ð–§ð–§; ð–§ð–§; ð–§ð–§; H; H; ) MATHEMATICAL SANS-SERIF CAPITAL H
+1D5A8;1D5A8;1D5A8;0049;0049; # (ð–¨ð–¨; ð–¨ð–¨; ð–¨ð–¨; I; I; ) MATHEMATICAL SANS-SERIF CAPITAL I
+1D5A9;1D5A9;1D5A9;004A;004A; # (ð–©ð–©; ð–©ð–©; ð–©ð–©; J; J; ) MATHEMATICAL SANS-SERIF CAPITAL J
+1D5AA;1D5AA;1D5AA;004B;004B; # (ð–ªð–ª; ð–ªð–ª; ð–ªð–ª; K; K; ) MATHEMATICAL SANS-SERIF CAPITAL K
+1D5AB;1D5AB;1D5AB;004C;004C; # (ð–«ð–«; ð–«ð–«; ð–«ð–«; L; L; ) MATHEMATICAL SANS-SERIF CAPITAL L
+1D5AC;1D5AC;1D5AC;004D;004D; # (ð–¬ð–¬; ð–¬ð–¬; ð–¬ð–¬; M; M; ) MATHEMATICAL SANS-SERIF CAPITAL M
+1D5AD;1D5AD;1D5AD;004E;004E; # (ð–­ð–­; ð–­ð–­; ð–­ð–­; N; N; ) MATHEMATICAL SANS-SERIF CAPITAL N
+1D5AE;1D5AE;1D5AE;004F;004F; # (ð–®ð–®; ð–®ð–®; ð–®ð–®; O; O; ) MATHEMATICAL SANS-SERIF CAPITAL O
+1D5AF;1D5AF;1D5AF;0050;0050; # (ð–¯ð–¯; ð–¯ð–¯; ð–¯ð–¯; P; P; ) MATHEMATICAL SANS-SERIF CAPITAL P
+1D5B0;1D5B0;1D5B0;0051;0051; # (ð–°ð–°; ð–°ð–°; ð–°ð–°; Q; Q; ) MATHEMATICAL SANS-SERIF CAPITAL Q
+1D5B1;1D5B1;1D5B1;0052;0052; # (ð–±ð–±; ð–±ð–±; ð–±ð–±; R; R; ) MATHEMATICAL SANS-SERIF CAPITAL R
+1D5B2;1D5B2;1D5B2;0053;0053; # (ð–²ð–²; ð–²ð–²; ð–²ð–²; S; S; ) MATHEMATICAL SANS-SERIF CAPITAL S
+1D5B3;1D5B3;1D5B3;0054;0054; # (ð–³ð–³; ð–³ð–³; ð–³ð–³; T; T; ) MATHEMATICAL SANS-SERIF CAPITAL T
+1D5B4;1D5B4;1D5B4;0055;0055; # (ð–´ð–´; ð–´ð–´; ð–´ð–´; U; U; ) MATHEMATICAL SANS-SERIF CAPITAL U
+1D5B5;1D5B5;1D5B5;0056;0056; # (ð–µð–µ; ð–µð–µ; ð–µð–µ; V; V; ) MATHEMATICAL SANS-SERIF CAPITAL V
+1D5B6;1D5B6;1D5B6;0057;0057; # (ð–¶ð–¶; ð–¶ð–¶; ð–¶ð–¶; W; W; ) MATHEMATICAL SANS-SERIF CAPITAL W
+1D5B7;1D5B7;1D5B7;0058;0058; # (ð–·ð–·; ð–·ð–·; ð–·ð–·; X; X; ) MATHEMATICAL SANS-SERIF CAPITAL X
+1D5B8;1D5B8;1D5B8;0059;0059; # (ð–¸ð–¸; ð–¸ð–¸; ð–¸ð–¸; Y; Y; ) MATHEMATICAL SANS-SERIF CAPITAL Y
+1D5B9;1D5B9;1D5B9;005A;005A; # (ð–¹ð–¹; ð–¹ð–¹; ð–¹ð–¹; Z; Z; ) MATHEMATICAL SANS-SERIF CAPITAL Z
+1D5BA;1D5BA;1D5BA;0061;0061; # (ð–ºð–º; ð–ºð–º; ð–ºð–º; a; a; ) MATHEMATICAL SANS-SERIF SMALL A
+1D5BB;1D5BB;1D5BB;0062;0062; # (ð–»ð–»; ð–»ð–»; ð–»ð–»; b; b; ) MATHEMATICAL SANS-SERIF SMALL B
+1D5BC;1D5BC;1D5BC;0063;0063; # (ð–¼ð–¼; ð–¼ð–¼; ð–¼ð–¼; c; c; ) MATHEMATICAL SANS-SERIF SMALL C
+1D5BD;1D5BD;1D5BD;0064;0064; # (ð–½ð–½; ð–½ð–½; ð–½ð–½; d; d; ) MATHEMATICAL SANS-SERIF SMALL D
+1D5BE;1D5BE;1D5BE;0065;0065; # (ð–¾ð–¾; ð–¾ð–¾; ð–¾ð–¾; e; e; ) MATHEMATICAL SANS-SERIF SMALL E
+1D5BF;1D5BF;1D5BF;0066;0066; # (ð–¿ð–¿; ð–¿ð–¿; ð–¿ð–¿; f; f; ) MATHEMATICAL SANS-SERIF SMALL F
+1D5C0;1D5C0;1D5C0;0067;0067; # (ð—€ð—€; ð—€ð—€; ð—€ð—€; g; g; ) MATHEMATICAL SANS-SERIF SMALL G
+1D5C1;1D5C1;1D5C1;0068;0068; # (ð—ð—; ð—ð—; ð—ð—; h; h; ) MATHEMATICAL SANS-SERIF SMALL H
+1D5C2;1D5C2;1D5C2;0069;0069; # (ð—‚ð—‚; ð—‚ð—‚; ð—‚ð—‚; i; i; ) MATHEMATICAL SANS-SERIF SMALL I
+1D5C3;1D5C3;1D5C3;006A;006A; # (ð—ƒð—ƒ; ð—ƒð—ƒ; ð—ƒð—ƒ; j; j; ) MATHEMATICAL SANS-SERIF SMALL J
+1D5C4;1D5C4;1D5C4;006B;006B; # (ð—„ð—„; ð—„ð—„; ð—„ð—„; k; k; ) MATHEMATICAL SANS-SERIF SMALL K
+1D5C5;1D5C5;1D5C5;006C;006C; # (ð—…ð—…; ð—…ð—…; ð—…ð—…; l; l; ) MATHEMATICAL SANS-SERIF SMALL L
+1D5C6;1D5C6;1D5C6;006D;006D; # (ð—†ð—†; ð—†ð—†; ð—†ð—†; m; m; ) MATHEMATICAL SANS-SERIF SMALL M
+1D5C7;1D5C7;1D5C7;006E;006E; # (ð—‡ð—‡; ð—‡ð—‡; ð—‡ð—‡; n; n; ) MATHEMATICAL SANS-SERIF SMALL N
+1D5C8;1D5C8;1D5C8;006F;006F; # (ð—ˆð—ˆ; ð—ˆð—ˆ; ð—ˆð—ˆ; o; o; ) MATHEMATICAL SANS-SERIF SMALL O
+1D5C9;1D5C9;1D5C9;0070;0070; # (ð—‰ð—‰; ð—‰ð—‰; ð—‰ð—‰; p; p; ) MATHEMATICAL SANS-SERIF SMALL P
+1D5CA;1D5CA;1D5CA;0071;0071; # (ð—Šð—Š; ð—Šð—Š; ð—Šð—Š; q; q; ) MATHEMATICAL SANS-SERIF SMALL Q
+1D5CB;1D5CB;1D5CB;0072;0072; # (ð—‹ð—‹; ð—‹ð—‹; ð—‹ð—‹; r; r; ) MATHEMATICAL SANS-SERIF SMALL R
+1D5CC;1D5CC;1D5CC;0073;0073; # (ð—Œð—Œ; ð—Œð—Œ; ð—Œð—Œ; s; s; ) MATHEMATICAL SANS-SERIF SMALL S
+1D5CD;1D5CD;1D5CD;0074;0074; # (ð—ð—; ð—ð—; ð—ð—; t; t; ) MATHEMATICAL SANS-SERIF SMALL T
+1D5CE;1D5CE;1D5CE;0075;0075; # (ð—Žð—Ž; ð—Žð—Ž; ð—Žð—Ž; u; u; ) MATHEMATICAL SANS-SERIF SMALL U
+1D5CF;1D5CF;1D5CF;0076;0076; # (ð—ð—; ð—ð—; ð—ð—; v; v; ) MATHEMATICAL SANS-SERIF SMALL V
+1D5D0;1D5D0;1D5D0;0077;0077; # (ð—ð—; ð—ð—; ð—ð—; w; w; ) MATHEMATICAL SANS-SERIF SMALL W
+1D5D1;1D5D1;1D5D1;0078;0078; # (ð—‘ð—‘; ð—‘ð—‘; ð—‘ð—‘; x; x; ) MATHEMATICAL SANS-SERIF SMALL X
+1D5D2;1D5D2;1D5D2;0079;0079; # (ð—’ð—’; ð—’ð—’; ð—’ð—’; y; y; ) MATHEMATICAL SANS-SERIF SMALL Y
+1D5D3;1D5D3;1D5D3;007A;007A; # (ð—“ð—“; ð—“ð—“; ð—“ð—“; z; z; ) MATHEMATICAL SANS-SERIF SMALL Z
+1D5D4;1D5D4;1D5D4;0041;0041; # (ð—”ð—”; ð—”ð—”; ð—”ð—”; A; A; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL A
+1D5D5;1D5D5;1D5D5;0042;0042; # (ð—•ð—•; ð—•ð—•; ð—•ð—•; B; B; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL B
+1D5D6;1D5D6;1D5D6;0043;0043; # (ð—–ð—–; ð—–ð—–; ð—–ð—–; C; C; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL C
+1D5D7;1D5D7;1D5D7;0044;0044; # (ð——ð——; ð——ð——; ð——ð——; D; D; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL D
+1D5D8;1D5D8;1D5D8;0045;0045; # (ð—˜ð—˜; ð—˜ð—˜; ð—˜ð—˜; E; E; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL E
+1D5D9;1D5D9;1D5D9;0046;0046; # (ð—™ð—™; ð—™ð—™; ð—™ð—™; F; F; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL F
+1D5DA;1D5DA;1D5DA;0047;0047; # (ð—šð—š; ð—šð—š; ð—šð—š; G; G; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL G
+1D5DB;1D5DB;1D5DB;0048;0048; # (ð—›ð—›; ð—›ð—›; ð—›ð—›; H; H; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL H
+1D5DC;1D5DC;1D5DC;0049;0049; # (ð—œð—œ; ð—œð—œ; ð—œð—œ; I; I; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL I
+1D5DD;1D5DD;1D5DD;004A;004A; # (ð—ð—; ð—ð—; ð—ð—; J; J; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL J
+1D5DE;1D5DE;1D5DE;004B;004B; # (ð—žð—ž; ð—žð—ž; ð—žð—ž; K; K; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL K
+1D5DF;1D5DF;1D5DF;004C;004C; # (ð—Ÿð—Ÿ; ð—Ÿð—Ÿ; ð—Ÿð—Ÿ; L; L; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL L
+1D5E0;1D5E0;1D5E0;004D;004D; # (ð— ð— ; ð— ð— ; ð— ð— ; M; M; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL M
+1D5E1;1D5E1;1D5E1;004E;004E; # (ð—¡ð—¡; ð—¡ð—¡; ð—¡ð—¡; N; N; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL N
+1D5E2;1D5E2;1D5E2;004F;004F; # (ð—¢ð—¢; ð—¢ð—¢; ð—¢ð—¢; O; O; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL O
+1D5E3;1D5E3;1D5E3;0050;0050; # (ð—£ð—£; ð—£ð—£; ð—£ð—£; P; P; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL P
+1D5E4;1D5E4;1D5E4;0051;0051; # (ð—¤ð—¤; ð—¤ð—¤; ð—¤ð—¤; Q; Q; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL Q
+1D5E5;1D5E5;1D5E5;0052;0052; # (ð—¥ð—¥; ð—¥ð—¥; ð—¥ð—¥; R; R; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL R
+1D5E6;1D5E6;1D5E6;0053;0053; # (ð—¦ð—¦; ð—¦ð—¦; ð—¦ð—¦; S; S; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL S
+1D5E7;1D5E7;1D5E7;0054;0054; # (ð—§ð—§; ð—§ð—§; ð—§ð—§; T; T; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL T
+1D5E8;1D5E8;1D5E8;0055;0055; # (ð—¨ð—¨; ð—¨ð—¨; ð—¨ð—¨; U; U; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL U
+1D5E9;1D5E9;1D5E9;0056;0056; # (ð—©ð—©; ð—©ð—©; ð—©ð—©; V; V; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL V
+1D5EA;1D5EA;1D5EA;0057;0057; # (ð—ªð—ª; ð—ªð—ª; ð—ªð—ª; W; W; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL W
+1D5EB;1D5EB;1D5EB;0058;0058; # (ð—«ð—«; ð—«ð—«; ð—«ð—«; X; X; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL X
+1D5EC;1D5EC;1D5EC;0059;0059; # (ð—¬ð—¬; ð—¬ð—¬; ð—¬ð—¬; Y; Y; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL Y
+1D5ED;1D5ED;1D5ED;005A;005A; # (ð—­ð—­; ð—­ð—­; ð—­ð—­; Z; Z; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL Z
+1D5EE;1D5EE;1D5EE;0061;0061; # (ð—®ð—®; ð—®ð—®; ð—®ð—®; a; a; ) MATHEMATICAL SANS-SERIF BOLD SMALL A
+1D5EF;1D5EF;1D5EF;0062;0062; # (ð—¯ð—¯; ð—¯ð—¯; ð—¯ð—¯; b; b; ) MATHEMATICAL SANS-SERIF BOLD SMALL B
+1D5F0;1D5F0;1D5F0;0063;0063; # (ð—°ð—°; ð—°ð—°; ð—°ð—°; c; c; ) MATHEMATICAL SANS-SERIF BOLD SMALL C
+1D5F1;1D5F1;1D5F1;0064;0064; # (ð—±ð—±; ð—±ð—±; ð—±ð—±; d; d; ) MATHEMATICAL SANS-SERIF BOLD SMALL D
+1D5F2;1D5F2;1D5F2;0065;0065; # (ð—²ð—²; ð—²ð—²; ð—²ð—²; e; e; ) MATHEMATICAL SANS-SERIF BOLD SMALL E
+1D5F3;1D5F3;1D5F3;0066;0066; # (ð—³ð—³; ð—³ð—³; ð—³ð—³; f; f; ) MATHEMATICAL SANS-SERIF BOLD SMALL F
+1D5F4;1D5F4;1D5F4;0067;0067; # (ð—´ð—´; ð—´ð—´; ð—´ð—´; g; g; ) MATHEMATICAL SANS-SERIF BOLD SMALL G
+1D5F5;1D5F5;1D5F5;0068;0068; # (ð—µð—µ; ð—µð—µ; ð—µð—µ; h; h; ) MATHEMATICAL SANS-SERIF BOLD SMALL H
+1D5F6;1D5F6;1D5F6;0069;0069; # (ð—¶ð—¶; ð—¶ð—¶; ð—¶ð—¶; i; i; ) MATHEMATICAL SANS-SERIF BOLD SMALL I
+1D5F7;1D5F7;1D5F7;006A;006A; # (ð—·ð—·; ð—·ð—·; ð—·ð—·; j; j; ) MATHEMATICAL SANS-SERIF BOLD SMALL J
+1D5F8;1D5F8;1D5F8;006B;006B; # (ð—¸ð—¸; ð—¸ð—¸; ð—¸ð—¸; k; k; ) MATHEMATICAL SANS-SERIF BOLD SMALL K
+1D5F9;1D5F9;1D5F9;006C;006C; # (ð—¹ð—¹; ð—¹ð—¹; ð—¹ð—¹; l; l; ) MATHEMATICAL SANS-SERIF BOLD SMALL L
+1D5FA;1D5FA;1D5FA;006D;006D; # (ð—ºð—º; ð—ºð—º; ð—ºð—º; m; m; ) MATHEMATICAL SANS-SERIF BOLD SMALL M
+1D5FB;1D5FB;1D5FB;006E;006E; # (ð—»ð—»; ð—»ð—»; ð—»ð—»; n; n; ) MATHEMATICAL SANS-SERIF BOLD SMALL N
+1D5FC;1D5FC;1D5FC;006F;006F; # (ð—¼ð—¼; ð—¼ð—¼; ð—¼ð—¼; o; o; ) MATHEMATICAL SANS-SERIF BOLD SMALL O
+1D5FD;1D5FD;1D5FD;0070;0070; # (ð—½ð—½; ð—½ð—½; ð—½ð—½; p; p; ) MATHEMATICAL SANS-SERIF BOLD SMALL P
+1D5FE;1D5FE;1D5FE;0071;0071; # (ð—¾ð—¾; ð—¾ð—¾; ð—¾ð—¾; q; q; ) MATHEMATICAL SANS-SERIF BOLD SMALL Q
+1D5FF;1D5FF;1D5FF;0072;0072; # (ð—¿ð—¿; ð—¿ð—¿; ð—¿ð—¿; r; r; ) MATHEMATICAL SANS-SERIF BOLD SMALL R
+1D600;1D600;1D600;0073;0073; # (ð˜€ð˜€; ð˜€ð˜€; ð˜€ð˜€; s; s; ) MATHEMATICAL SANS-SERIF BOLD SMALL S
+1D601;1D601;1D601;0074;0074; # (ð˜ð˜; ð˜ð˜; ð˜ð˜; t; t; ) MATHEMATICAL SANS-SERIF BOLD SMALL T
+1D602;1D602;1D602;0075;0075; # (ð˜‚ð˜‚; ð˜‚ð˜‚; ð˜‚ð˜‚; u; u; ) MATHEMATICAL SANS-SERIF BOLD SMALL U
+1D603;1D603;1D603;0076;0076; # (ð˜ƒð˜ƒ; ð˜ƒð˜ƒ; ð˜ƒð˜ƒ; v; v; ) MATHEMATICAL SANS-SERIF BOLD SMALL V
+1D604;1D604;1D604;0077;0077; # (ð˜„ð˜„; ð˜„ð˜„; ð˜„ð˜„; w; w; ) MATHEMATICAL SANS-SERIF BOLD SMALL W
+1D605;1D605;1D605;0078;0078; # (ð˜…ð˜…; ð˜…ð˜…; ð˜…ð˜…; x; x; ) MATHEMATICAL SANS-SERIF BOLD SMALL X
+1D606;1D606;1D606;0079;0079; # (ð˜†ð˜†; ð˜†ð˜†; ð˜†ð˜†; y; y; ) MATHEMATICAL SANS-SERIF BOLD SMALL Y
+1D607;1D607;1D607;007A;007A; # (ð˜‡ð˜‡; ð˜‡ð˜‡; ð˜‡ð˜‡; z; z; ) MATHEMATICAL SANS-SERIF BOLD SMALL Z
+1D608;1D608;1D608;0041;0041; # (ð˜ˆð˜ˆ; ð˜ˆð˜ˆ; ð˜ˆð˜ˆ; A; A; ) MATHEMATICAL SANS-SERIF ITALIC CAPITAL A
+1D609;1D609;1D609;0042;0042; # (ð˜‰ð˜‰; ð˜‰ð˜‰; ð˜‰ð˜‰; B; B; ) MATHEMATICAL SANS-SERIF ITALIC CAPITAL B
+1D60A;1D60A;1D60A;0043;0043; # (ð˜Šð˜Š; ð˜Šð˜Š; ð˜Šð˜Š; C; C; ) MATHEMATICAL SANS-SERIF ITALIC CAPITAL C
+1D60B;1D60B;1D60B;0044;0044; # (ð˜‹ð˜‹; ð˜‹ð˜‹; ð˜‹ð˜‹; D; D; ) MATHEMATICAL SANS-SERIF ITALIC CAPITAL D
+1D60C;1D60C;1D60C;0045;0045; # (ð˜Œð˜Œ; ð˜Œð˜Œ; ð˜Œð˜Œ; E; E; ) MATHEMATICAL SANS-SERIF ITALIC CAPITAL E
+1D60D;1D60D;1D60D;0046;0046; # (ð˜ð˜; ð˜ð˜; ð˜ð˜; F; F; ) MATHEMATICAL SANS-SERIF ITALIC CAPITAL F
+1D60E;1D60E;1D60E;0047;0047; # (ð˜Žð˜Ž; ð˜Žð˜Ž; ð˜Žð˜Ž; G; G; ) MATHEMATICAL SANS-SERIF ITALIC CAPITAL G
+1D60F;1D60F;1D60F;0048;0048; # (ð˜ð˜; ð˜ð˜; ð˜ð˜; H; H; ) MATHEMATICAL SANS-SERIF ITALIC CAPITAL H
+1D610;1D610;1D610;0049;0049; # (ð˜ð˜; ð˜ð˜; ð˜ð˜; I; I; ) MATHEMATICAL SANS-SERIF ITALIC CAPITAL I
+1D611;1D611;1D611;004A;004A; # (ð˜‘ð˜‘; ð˜‘ð˜‘; ð˜‘ð˜‘; J; J; ) MATHEMATICAL SANS-SERIF ITALIC CAPITAL J
+1D612;1D612;1D612;004B;004B; # (ð˜’ð˜’; ð˜’ð˜’; ð˜’ð˜’; K; K; ) MATHEMATICAL SANS-SERIF ITALIC CAPITAL K
+1D613;1D613;1D613;004C;004C; # (ð˜“ð˜“; ð˜“ð˜“; ð˜“ð˜“; L; L; ) MATHEMATICAL SANS-SERIF ITALIC CAPITAL L
+1D614;1D614;1D614;004D;004D; # (ð˜”ð˜”; ð˜”ð˜”; ð˜”ð˜”; M; M; ) MATHEMATICAL SANS-SERIF ITALIC CAPITAL M
+1D615;1D615;1D615;004E;004E; # (ð˜•ð˜•; ð˜•ð˜•; ð˜•ð˜•; N; N; ) MATHEMATICAL SANS-SERIF ITALIC CAPITAL N
+1D616;1D616;1D616;004F;004F; # (ð˜–ð˜–; ð˜–ð˜–; ð˜–ð˜–; O; O; ) MATHEMATICAL SANS-SERIF ITALIC CAPITAL O
+1D617;1D617;1D617;0050;0050; # (ð˜—ð˜—; ð˜—ð˜—; ð˜—ð˜—; P; P; ) MATHEMATICAL SANS-SERIF ITALIC CAPITAL P
+1D618;1D618;1D618;0051;0051; # (ð˜˜ð˜˜; ð˜˜ð˜˜; ð˜˜ð˜˜; Q; Q; ) MATHEMATICAL SANS-SERIF ITALIC CAPITAL Q
+1D619;1D619;1D619;0052;0052; # (ð˜™ð˜™; ð˜™ð˜™; ð˜™ð˜™; R; R; ) MATHEMATICAL SANS-SERIF ITALIC CAPITAL R
+1D61A;1D61A;1D61A;0053;0053; # (ð˜šð˜š; ð˜šð˜š; ð˜šð˜š; S; S; ) MATHEMATICAL SANS-SERIF ITALIC CAPITAL S
+1D61B;1D61B;1D61B;0054;0054; # (ð˜›ð˜›; ð˜›ð˜›; ð˜›ð˜›; T; T; ) MATHEMATICAL SANS-SERIF ITALIC CAPITAL T
+1D61C;1D61C;1D61C;0055;0055; # (ð˜œð˜œ; ð˜œð˜œ; ð˜œð˜œ; U; U; ) MATHEMATICAL SANS-SERIF ITALIC CAPITAL U
+1D61D;1D61D;1D61D;0056;0056; # (ð˜ð˜; ð˜ð˜; ð˜ð˜; V; V; ) MATHEMATICAL SANS-SERIF ITALIC CAPITAL V
+1D61E;1D61E;1D61E;0057;0057; # (ð˜žð˜ž; ð˜žð˜ž; ð˜žð˜ž; W; W; ) MATHEMATICAL SANS-SERIF ITALIC CAPITAL W
+1D61F;1D61F;1D61F;0058;0058; # (ð˜Ÿð˜Ÿ; ð˜Ÿð˜Ÿ; ð˜Ÿð˜Ÿ; X; X; ) MATHEMATICAL SANS-SERIF ITALIC CAPITAL X
+1D620;1D620;1D620;0059;0059; # (ð˜ ð˜ ; ð˜ ð˜ ; ð˜ ð˜ ; Y; Y; ) MATHEMATICAL SANS-SERIF ITALIC CAPITAL Y
+1D621;1D621;1D621;005A;005A; # (ð˜¡ð˜¡; ð˜¡ð˜¡; ð˜¡ð˜¡; Z; Z; ) MATHEMATICAL SANS-SERIF ITALIC CAPITAL Z
+1D622;1D622;1D622;0061;0061; # (ð˜¢ð˜¢; ð˜¢ð˜¢; ð˜¢ð˜¢; a; a; ) MATHEMATICAL SANS-SERIF ITALIC SMALL A
+1D623;1D623;1D623;0062;0062; # (ð˜£ð˜£; ð˜£ð˜£; ð˜£ð˜£; b; b; ) MATHEMATICAL SANS-SERIF ITALIC SMALL B
+1D624;1D624;1D624;0063;0063; # (ð˜¤ð˜¤; ð˜¤ð˜¤; ð˜¤ð˜¤; c; c; ) MATHEMATICAL SANS-SERIF ITALIC SMALL C
+1D625;1D625;1D625;0064;0064; # (ð˜¥ð˜¥; ð˜¥ð˜¥; ð˜¥ð˜¥; d; d; ) MATHEMATICAL SANS-SERIF ITALIC SMALL D
+1D626;1D626;1D626;0065;0065; # (ð˜¦ð˜¦; ð˜¦ð˜¦; ð˜¦ð˜¦; e; e; ) MATHEMATICAL SANS-SERIF ITALIC SMALL E
+1D627;1D627;1D627;0066;0066; # (ð˜§ð˜§; ð˜§ð˜§; ð˜§ð˜§; f; f; ) MATHEMATICAL SANS-SERIF ITALIC SMALL F
+1D628;1D628;1D628;0067;0067; # (ð˜¨ð˜¨; ð˜¨ð˜¨; ð˜¨ð˜¨; g; g; ) MATHEMATICAL SANS-SERIF ITALIC SMALL G
+1D629;1D629;1D629;0068;0068; # (ð˜©ð˜©; ð˜©ð˜©; ð˜©ð˜©; h; h; ) MATHEMATICAL SANS-SERIF ITALIC SMALL H
+1D62A;1D62A;1D62A;0069;0069; # (ð˜ªð˜ª; ð˜ªð˜ª; ð˜ªð˜ª; i; i; ) MATHEMATICAL SANS-SERIF ITALIC SMALL I
+1D62B;1D62B;1D62B;006A;006A; # (ð˜«ð˜«; ð˜«ð˜«; ð˜«ð˜«; j; j; ) MATHEMATICAL SANS-SERIF ITALIC SMALL J
+1D62C;1D62C;1D62C;006B;006B; # (ð˜¬ð˜¬; ð˜¬ð˜¬; ð˜¬ð˜¬; k; k; ) MATHEMATICAL SANS-SERIF ITALIC SMALL K
+1D62D;1D62D;1D62D;006C;006C; # (ð˜­ð˜­; ð˜­ð˜­; ð˜­ð˜­; l; l; ) MATHEMATICAL SANS-SERIF ITALIC SMALL L
+1D62E;1D62E;1D62E;006D;006D; # (ð˜®ð˜®; ð˜®ð˜®; ð˜®ð˜®; m; m; ) MATHEMATICAL SANS-SERIF ITALIC SMALL M
+1D62F;1D62F;1D62F;006E;006E; # (ð˜¯ð˜¯; ð˜¯ð˜¯; ð˜¯ð˜¯; n; n; ) MATHEMATICAL SANS-SERIF ITALIC SMALL N
+1D630;1D630;1D630;006F;006F; # (ð˜°ð˜°; ð˜°ð˜°; ð˜°ð˜°; o; o; ) MATHEMATICAL SANS-SERIF ITALIC SMALL O
+1D631;1D631;1D631;0070;0070; # (ð˜±ð˜±; ð˜±ð˜±; ð˜±ð˜±; p; p; ) MATHEMATICAL SANS-SERIF ITALIC SMALL P
+1D632;1D632;1D632;0071;0071; # (ð˜²ð˜²; ð˜²ð˜²; ð˜²ð˜²; q; q; ) MATHEMATICAL SANS-SERIF ITALIC SMALL Q
+1D633;1D633;1D633;0072;0072; # (ð˜³ð˜³; ð˜³ð˜³; ð˜³ð˜³; r; r; ) MATHEMATICAL SANS-SERIF ITALIC SMALL R
+1D634;1D634;1D634;0073;0073; # (ð˜´ð˜´; ð˜´ð˜´; ð˜´ð˜´; s; s; ) MATHEMATICAL SANS-SERIF ITALIC SMALL S
+1D635;1D635;1D635;0074;0074; # (ð˜µð˜µ; ð˜µð˜µ; ð˜µð˜µ; t; t; ) MATHEMATICAL SANS-SERIF ITALIC SMALL T
+1D636;1D636;1D636;0075;0075; # (ð˜¶ð˜¶; ð˜¶ð˜¶; ð˜¶ð˜¶; u; u; ) MATHEMATICAL SANS-SERIF ITALIC SMALL U
+1D637;1D637;1D637;0076;0076; # (ð˜·ð˜·; ð˜·ð˜·; ð˜·ð˜·; v; v; ) MATHEMATICAL SANS-SERIF ITALIC SMALL V
+1D638;1D638;1D638;0077;0077; # (ð˜¸ð˜¸; ð˜¸ð˜¸; ð˜¸ð˜¸; w; w; ) MATHEMATICAL SANS-SERIF ITALIC SMALL W
+1D639;1D639;1D639;0078;0078; # (ð˜¹ð˜¹; ð˜¹ð˜¹; ð˜¹ð˜¹; x; x; ) MATHEMATICAL SANS-SERIF ITALIC SMALL X
+1D63A;1D63A;1D63A;0079;0079; # (ð˜ºð˜º; ð˜ºð˜º; ð˜ºð˜º; y; y; ) MATHEMATICAL SANS-SERIF ITALIC SMALL Y
+1D63B;1D63B;1D63B;007A;007A; # (ð˜»ð˜»; ð˜»ð˜»; ð˜»ð˜»; z; z; ) MATHEMATICAL SANS-SERIF ITALIC SMALL Z
+1D63C;1D63C;1D63C;0041;0041; # (ð˜¼ð˜¼; ð˜¼ð˜¼; ð˜¼ð˜¼; A; A; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL A
+1D63D;1D63D;1D63D;0042;0042; # (ð˜½ð˜½; ð˜½ð˜½; ð˜½ð˜½; B; B; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL B
+1D63E;1D63E;1D63E;0043;0043; # (ð˜¾ð˜¾; ð˜¾ð˜¾; ð˜¾ð˜¾; C; C; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL C
+1D63F;1D63F;1D63F;0044;0044; # (ð˜¿ð˜¿; ð˜¿ð˜¿; ð˜¿ð˜¿; D; D; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL D
+1D640;1D640;1D640;0045;0045; # (ð™€ð™€; ð™€ð™€; ð™€ð™€; E; E; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL E
+1D641;1D641;1D641;0046;0046; # (ð™ð™; ð™ð™; ð™ð™; F; F; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL F
+1D642;1D642;1D642;0047;0047; # (ð™‚ð™‚; ð™‚ð™‚; ð™‚ð™‚; G; G; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL G
+1D643;1D643;1D643;0048;0048; # (ð™ƒð™ƒ; ð™ƒð™ƒ; ð™ƒð™ƒ; H; H; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL H
+1D644;1D644;1D644;0049;0049; # (ð™„ð™„; ð™„ð™„; ð™„ð™„; I; I; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL I
+1D645;1D645;1D645;004A;004A; # (ð™…ð™…; ð™…ð™…; ð™…ð™…; J; J; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL J
+1D646;1D646;1D646;004B;004B; # (ð™†ð™†; ð™†ð™†; ð™†ð™†; K; K; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL K
+1D647;1D647;1D647;004C;004C; # (ð™‡ð™‡; ð™‡ð™‡; ð™‡ð™‡; L; L; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL L
+1D648;1D648;1D648;004D;004D; # (ð™ˆð™ˆ; ð™ˆð™ˆ; ð™ˆð™ˆ; M; M; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL M
+1D649;1D649;1D649;004E;004E; # (ð™‰ð™‰; ð™‰ð™‰; ð™‰ð™‰; N; N; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL N
+1D64A;1D64A;1D64A;004F;004F; # (ð™Šð™Š; ð™Šð™Š; ð™Šð™Š; O; O; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL O
+1D64B;1D64B;1D64B;0050;0050; # (ð™‹ð™‹; ð™‹ð™‹; ð™‹ð™‹; P; P; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL P
+1D64C;1D64C;1D64C;0051;0051; # (ð™Œð™Œ; ð™Œð™Œ; ð™Œð™Œ; Q; Q; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL Q
+1D64D;1D64D;1D64D;0052;0052; # (ð™ð™; ð™ð™; ð™ð™; R; R; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL R
+1D64E;1D64E;1D64E;0053;0053; # (ð™Žð™Ž; ð™Žð™Ž; ð™Žð™Ž; S; S; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL S
+1D64F;1D64F;1D64F;0054;0054; # (ð™ð™; ð™ð™; ð™ð™; T; T; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL T
+1D650;1D650;1D650;0055;0055; # (ð™ð™; ð™ð™; ð™ð™; U; U; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL U
+1D651;1D651;1D651;0056;0056; # (ð™‘ð™‘; ð™‘ð™‘; ð™‘ð™‘; V; V; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL V
+1D652;1D652;1D652;0057;0057; # (ð™’ð™’; ð™’ð™’; ð™’ð™’; W; W; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL W
+1D653;1D653;1D653;0058;0058; # (ð™“ð™“; ð™“ð™“; ð™“ð™“; X; X; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL X
+1D654;1D654;1D654;0059;0059; # (ð™”ð™”; ð™”ð™”; ð™”ð™”; Y; Y; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL Y
+1D655;1D655;1D655;005A;005A; # (ð™•ð™•; ð™•ð™•; ð™•ð™•; Z; Z; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL Z
+1D656;1D656;1D656;0061;0061; # (ð™–ð™–; ð™–ð™–; ð™–ð™–; a; a; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL A
+1D657;1D657;1D657;0062;0062; # (ð™—ð™—; ð™—ð™—; ð™—ð™—; b; b; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL B
+1D658;1D658;1D658;0063;0063; # (ð™˜ð™˜; ð™˜ð™˜; ð™˜ð™˜; c; c; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL C
+1D659;1D659;1D659;0064;0064; # (ð™™ð™™; ð™™ð™™; ð™™ð™™; d; d; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL D
+1D65A;1D65A;1D65A;0065;0065; # (ð™šð™š; ð™šð™š; ð™šð™š; e; e; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL E
+1D65B;1D65B;1D65B;0066;0066; # (ð™›ð™›; ð™›ð™›; ð™›ð™›; f; f; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL F
+1D65C;1D65C;1D65C;0067;0067; # (ð™œð™œ; ð™œð™œ; ð™œð™œ; g; g; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL G
+1D65D;1D65D;1D65D;0068;0068; # (ð™ð™; ð™ð™; ð™ð™; h; h; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL H
+1D65E;1D65E;1D65E;0069;0069; # (ð™žð™ž; ð™žð™ž; ð™žð™ž; i; i; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL I
+1D65F;1D65F;1D65F;006A;006A; # (ð™Ÿð™Ÿ; ð™Ÿð™Ÿ; ð™Ÿð™Ÿ; j; j; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL J
+1D660;1D660;1D660;006B;006B; # (ð™ ð™ ; ð™ ð™ ; ð™ ð™ ; k; k; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL K
+1D661;1D661;1D661;006C;006C; # (ð™¡ð™¡; ð™¡ð™¡; ð™¡ð™¡; l; l; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL L
+1D662;1D662;1D662;006D;006D; # (ð™¢ð™¢; ð™¢ð™¢; ð™¢ð™¢; m; m; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL M
+1D663;1D663;1D663;006E;006E; # (ð™£ð™£; ð™£ð™£; ð™£ð™£; n; n; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL N
+1D664;1D664;1D664;006F;006F; # (ð™¤ð™¤; ð™¤ð™¤; ð™¤ð™¤; o; o; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL O
+1D665;1D665;1D665;0070;0070; # (ð™¥ð™¥; ð™¥ð™¥; ð™¥ð™¥; p; p; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL P
+1D666;1D666;1D666;0071;0071; # (ð™¦ð™¦; ð™¦ð™¦; ð™¦ð™¦; q; q; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL Q
+1D667;1D667;1D667;0072;0072; # (ð™§ð™§; ð™§ð™§; ð™§ð™§; r; r; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL R
+1D668;1D668;1D668;0073;0073; # (ð™¨ð™¨; ð™¨ð™¨; ð™¨ð™¨; s; s; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL S
+1D669;1D669;1D669;0074;0074; # (ð™©ð™©; ð™©ð™©; ð™©ð™©; t; t; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL T
+1D66A;1D66A;1D66A;0075;0075; # (ð™ªð™ª; ð™ªð™ª; ð™ªð™ª; u; u; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL U
+1D66B;1D66B;1D66B;0076;0076; # (ð™«ð™«; ð™«ð™«; ð™«ð™«; v; v; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL V
+1D66C;1D66C;1D66C;0077;0077; # (ð™¬ð™¬; ð™¬ð™¬; ð™¬ð™¬; w; w; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL W
+1D66D;1D66D;1D66D;0078;0078; # (ð™­ð™­; ð™­ð™­; ð™­ð™­; x; x; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL X
+1D66E;1D66E;1D66E;0079;0079; # (ð™®ð™®; ð™®ð™®; ð™®ð™®; y; y; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL Y
+1D66F;1D66F;1D66F;007A;007A; # (ð™¯ð™¯; ð™¯ð™¯; ð™¯ð™¯; z; z; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL Z
+1D670;1D670;1D670;0041;0041; # (ð™°ð™°; ð™°ð™°; ð™°ð™°; A; A; ) MATHEMATICAL MONOSPACE CAPITAL A
+1D671;1D671;1D671;0042;0042; # (ð™±ð™±; ð™±ð™±; ð™±ð™±; B; B; ) MATHEMATICAL MONOSPACE CAPITAL B
+1D672;1D672;1D672;0043;0043; # (ð™²ð™²; ð™²ð™²; ð™²ð™²; C; C; ) MATHEMATICAL MONOSPACE CAPITAL C
+1D673;1D673;1D673;0044;0044; # (ð™³ð™³; ð™³ð™³; ð™³ð™³; D; D; ) MATHEMATICAL MONOSPACE CAPITAL D
+1D674;1D674;1D674;0045;0045; # (ð™´ð™´; ð™´ð™´; ð™´ð™´; E; E; ) MATHEMATICAL MONOSPACE CAPITAL E
+1D675;1D675;1D675;0046;0046; # (ð™µð™µ; ð™µð™µ; ð™µð™µ; F; F; ) MATHEMATICAL MONOSPACE CAPITAL F
+1D676;1D676;1D676;0047;0047; # (ð™¶ð™¶; ð™¶ð™¶; ð™¶ð™¶; G; G; ) MATHEMATICAL MONOSPACE CAPITAL G
+1D677;1D677;1D677;0048;0048; # (ð™·ð™·; ð™·ð™·; ð™·ð™·; H; H; ) MATHEMATICAL MONOSPACE CAPITAL H
+1D678;1D678;1D678;0049;0049; # (ð™¸ð™¸; ð™¸ð™¸; ð™¸ð™¸; I; I; ) MATHEMATICAL MONOSPACE CAPITAL I
+1D679;1D679;1D679;004A;004A; # (ð™¹ð™¹; ð™¹ð™¹; ð™¹ð™¹; J; J; ) MATHEMATICAL MONOSPACE CAPITAL J
+1D67A;1D67A;1D67A;004B;004B; # (ð™ºð™º; ð™ºð™º; ð™ºð™º; K; K; ) MATHEMATICAL MONOSPACE CAPITAL K
+1D67B;1D67B;1D67B;004C;004C; # (ð™»ð™»; ð™»ð™»; ð™»ð™»; L; L; ) MATHEMATICAL MONOSPACE CAPITAL L
+1D67C;1D67C;1D67C;004D;004D; # (ð™¼ð™¼; ð™¼ð™¼; ð™¼ð™¼; M; M; ) MATHEMATICAL MONOSPACE CAPITAL M
+1D67D;1D67D;1D67D;004E;004E; # (ð™½ð™½; ð™½ð™½; ð™½ð™½; N; N; ) MATHEMATICAL MONOSPACE CAPITAL N
+1D67E;1D67E;1D67E;004F;004F; # (ð™¾ð™¾; ð™¾ð™¾; ð™¾ð™¾; O; O; ) MATHEMATICAL MONOSPACE CAPITAL O
+1D67F;1D67F;1D67F;0050;0050; # (ð™¿ð™¿; ð™¿ð™¿; ð™¿ð™¿; P; P; ) MATHEMATICAL MONOSPACE CAPITAL P
+1D680;1D680;1D680;0051;0051; # (ðš€ðš€; ðš€ðš€; ðš€ðš€; Q; Q; ) MATHEMATICAL MONOSPACE CAPITAL Q
+1D681;1D681;1D681;0052;0052; # (ðšðš; ðšðš; ðšðš; R; R; ) MATHEMATICAL MONOSPACE CAPITAL R
+1D682;1D682;1D682;0053;0053; # (ðš‚ðš‚; ðš‚ðš‚; ðš‚ðš‚; S; S; ) MATHEMATICAL MONOSPACE CAPITAL S
+1D683;1D683;1D683;0054;0054; # (ðšƒðšƒ; ðšƒðšƒ; ðšƒðšƒ; T; T; ) MATHEMATICAL MONOSPACE CAPITAL T
+1D684;1D684;1D684;0055;0055; # (ðš„ðš„; ðš„ðš„; ðš„ðš„; U; U; ) MATHEMATICAL MONOSPACE CAPITAL U
+1D685;1D685;1D685;0056;0056; # (ðš…ðš…; ðš…ðš…; ðš…ðš…; V; V; ) MATHEMATICAL MONOSPACE CAPITAL V
+1D686;1D686;1D686;0057;0057; # (ðš†ðš†; ðš†ðš†; ðš†ðš†; W; W; ) MATHEMATICAL MONOSPACE CAPITAL W
+1D687;1D687;1D687;0058;0058; # (ðš‡ðš‡; ðš‡ðš‡; ðš‡ðš‡; X; X; ) MATHEMATICAL MONOSPACE CAPITAL X
+1D688;1D688;1D688;0059;0059; # (ðšˆðšˆ; ðšˆðšˆ; ðšˆðšˆ; Y; Y; ) MATHEMATICAL MONOSPACE CAPITAL Y
+1D689;1D689;1D689;005A;005A; # (ðš‰ðš‰; ðš‰ðš‰; ðš‰ðš‰; Z; Z; ) MATHEMATICAL MONOSPACE CAPITAL Z
+1D68A;1D68A;1D68A;0061;0061; # (ðšŠðšŠ; ðšŠðšŠ; ðšŠðšŠ; a; a; ) MATHEMATICAL MONOSPACE SMALL A
+1D68B;1D68B;1D68B;0062;0062; # (ðš‹ðš‹; ðš‹ðš‹; ðš‹ðš‹; b; b; ) MATHEMATICAL MONOSPACE SMALL B
+1D68C;1D68C;1D68C;0063;0063; # (ðšŒðšŒ; ðšŒðšŒ; ðšŒðšŒ; c; c; ) MATHEMATICAL MONOSPACE SMALL C
+1D68D;1D68D;1D68D;0064;0064; # (ðšðš; ðšðš; ðšðš; d; d; ) MATHEMATICAL MONOSPACE SMALL D
+1D68E;1D68E;1D68E;0065;0065; # (ðšŽðšŽ; ðšŽðšŽ; ðšŽðšŽ; e; e; ) MATHEMATICAL MONOSPACE SMALL E
+1D68F;1D68F;1D68F;0066;0066; # (ðšðš; ðšðš; ðšðš; f; f; ) MATHEMATICAL MONOSPACE SMALL F
+1D690;1D690;1D690;0067;0067; # (ðšðš; ðšðš; ðšðš; g; g; ) MATHEMATICAL MONOSPACE SMALL G
+1D691;1D691;1D691;0068;0068; # (ðš‘ðš‘; ðš‘ðš‘; ðš‘ðš‘; h; h; ) MATHEMATICAL MONOSPACE SMALL H
+1D692;1D692;1D692;0069;0069; # (ðš’ðš’; ðš’ðš’; ðš’ðš’; i; i; ) MATHEMATICAL MONOSPACE SMALL I
+1D693;1D693;1D693;006A;006A; # (ðš“ðš“; ðš“ðš“; ðš“ðš“; j; j; ) MATHEMATICAL MONOSPACE SMALL J
+1D694;1D694;1D694;006B;006B; # (ðš”ðš”; ðš”ðš”; ðš”ðš”; k; k; ) MATHEMATICAL MONOSPACE SMALL K
+1D695;1D695;1D695;006C;006C; # (ðš•ðš•; ðš•ðš•; ðš•ðš•; l; l; ) MATHEMATICAL MONOSPACE SMALL L
+1D696;1D696;1D696;006D;006D; # (ðš–ðš–; ðš–ðš–; ðš–ðš–; m; m; ) MATHEMATICAL MONOSPACE SMALL M
+1D697;1D697;1D697;006E;006E; # (ðš—ðš—; ðš—ðš—; ðš—ðš—; n; n; ) MATHEMATICAL MONOSPACE SMALL N
+1D698;1D698;1D698;006F;006F; # (ðš˜ðš˜; ðš˜ðš˜; ðš˜ðš˜; o; o; ) MATHEMATICAL MONOSPACE SMALL O
+1D699;1D699;1D699;0070;0070; # (ðš™ðš™; ðš™ðš™; ðš™ðš™; p; p; ) MATHEMATICAL MONOSPACE SMALL P
+1D69A;1D69A;1D69A;0071;0071; # (ðššðšš; ðššðšš; ðššðšš; q; q; ) MATHEMATICAL MONOSPACE SMALL Q
+1D69B;1D69B;1D69B;0072;0072; # (ðš›ðš›; ðš›ðš›; ðš›ðš›; r; r; ) MATHEMATICAL MONOSPACE SMALL R
+1D69C;1D69C;1D69C;0073;0073; # (ðšœðšœ; ðšœðšœ; ðšœðšœ; s; s; ) MATHEMATICAL MONOSPACE SMALL S
+1D69D;1D69D;1D69D;0074;0074; # (ðšðš; ðšðš; ðšðš; t; t; ) MATHEMATICAL MONOSPACE SMALL T
+1D69E;1D69E;1D69E;0075;0075; # (ðšžðšž; ðšžðšž; ðšžðšž; u; u; ) MATHEMATICAL MONOSPACE SMALL U
+1D69F;1D69F;1D69F;0076;0076; # (ðšŸðšŸ; ðšŸðšŸ; ðšŸðšŸ; v; v; ) MATHEMATICAL MONOSPACE SMALL V
+1D6A0;1D6A0;1D6A0;0077;0077; # (ðš ðš ; ðš ðš ; ðš ðš ; w; w; ) MATHEMATICAL MONOSPACE SMALL W
+1D6A1;1D6A1;1D6A1;0078;0078; # (ðš¡ðš¡; ðš¡ðš¡; ðš¡ðš¡; x; x; ) MATHEMATICAL MONOSPACE SMALL X
+1D6A2;1D6A2;1D6A2;0079;0079; # (ðš¢ðš¢; ðš¢ðš¢; ðš¢ðš¢; y; y; ) MATHEMATICAL MONOSPACE SMALL Y
+1D6A3;1D6A3;1D6A3;007A;007A; # (ðš£ðš£; ðš£ðš£; ðš£ðš£; z; z; ) MATHEMATICAL MONOSPACE SMALL Z
+1D6A8;1D6A8;1D6A8;0391;0391; # (ðš¨ðš¨; ðš¨ðš¨; ðš¨ðš¨; Α; Α; ) MATHEMATICAL BOLD CAPITAL ALPHA
+1D6A9;1D6A9;1D6A9;0392;0392; # (ðš©ðš©; ðš©ðš©; ðš©ðš©; Î’; Î’; ) MATHEMATICAL BOLD CAPITAL BETA
+1D6AA;1D6AA;1D6AA;0393;0393; # (ðšªðšª; ðšªðšª; ðšªðšª; Γ; Γ; ) MATHEMATICAL BOLD CAPITAL GAMMA
+1D6AB;1D6AB;1D6AB;0394;0394; # (ðš«ðš«; ðš«ðš«; ðš«ðš«; Δ; Δ; ) MATHEMATICAL BOLD CAPITAL DELTA
+1D6AC;1D6AC;1D6AC;0395;0395; # (ðš¬ðš¬; ðš¬ðš¬; ðš¬ðš¬; Ε; Ε; ) MATHEMATICAL BOLD CAPITAL EPSILON
+1D6AD;1D6AD;1D6AD;0396;0396; # (ðš­ðš­; ðš­ðš­; ðš­ðš­; Ζ; Ζ; ) MATHEMATICAL BOLD CAPITAL ZETA
+1D6AE;1D6AE;1D6AE;0397;0397; # (ðš®ðš®; ðš®ðš®; ðš®ðš®; Η; Η; ) MATHEMATICAL BOLD CAPITAL ETA
+1D6AF;1D6AF;1D6AF;0398;0398; # (ðš¯ðš¯; ðš¯ðš¯; ðš¯ðš¯; Θ; Θ; ) MATHEMATICAL BOLD CAPITAL THETA
+1D6B0;1D6B0;1D6B0;0399;0399; # (ðš°ðš°; ðš°ðš°; ðš°ðš°; Ι; Ι; ) MATHEMATICAL BOLD CAPITAL IOTA
+1D6B1;1D6B1;1D6B1;039A;039A; # (ðš±ðš±; ðš±ðš±; ðš±ðš±; Κ; Κ; ) MATHEMATICAL BOLD CAPITAL KAPPA
+1D6B2;1D6B2;1D6B2;039B;039B; # (ðš²ðš²; ðš²ðš²; ðš²ðš²; Λ; Λ; ) MATHEMATICAL BOLD CAPITAL LAMDA
+1D6B3;1D6B3;1D6B3;039C;039C; # (ðš³ðš³; ðš³ðš³; ðš³ðš³; Μ; Μ; ) MATHEMATICAL BOLD CAPITAL MU
+1D6B4;1D6B4;1D6B4;039D;039D; # (ðš´ðš´; ðš´ðš´; ðš´ðš´; Î; Î; ) MATHEMATICAL BOLD CAPITAL NU
+1D6B5;1D6B5;1D6B5;039E;039E; # (ðšµðšµ; ðšµðšµ; ðšµðšµ; Ξ; Ξ; ) MATHEMATICAL BOLD CAPITAL XI
+1D6B6;1D6B6;1D6B6;039F;039F; # (ðš¶ðš¶; ðš¶ðš¶; ðš¶ðš¶; Ο; Ο; ) MATHEMATICAL BOLD CAPITAL OMICRON
+1D6B7;1D6B7;1D6B7;03A0;03A0; # (ðš·ðš·; ðš·ðš·; ðš·ðš·; Π; Π; ) MATHEMATICAL BOLD CAPITAL PI
+1D6B8;1D6B8;1D6B8;03A1;03A1; # (ðš¸ðš¸; ðš¸ðš¸; ðš¸ðš¸; Ρ; Ρ; ) MATHEMATICAL BOLD CAPITAL RHO
+1D6B9;1D6B9;1D6B9;0398;0398; # (ðš¹ðš¹; ðš¹ðš¹; ðš¹ðš¹; Θ; Θ; ) MATHEMATICAL BOLD CAPITAL THETA SYMBOL
+1D6BA;1D6BA;1D6BA;03A3;03A3; # (ðšºðšº; ðšºðšº; ðšºðšº; Σ; Σ; ) MATHEMATICAL BOLD CAPITAL SIGMA
+1D6BB;1D6BB;1D6BB;03A4;03A4; # (ðš»ðš»; ðš»ðš»; ðš»ðš»; Τ; Τ; ) MATHEMATICAL BOLD CAPITAL TAU
+1D6BC;1D6BC;1D6BC;03A5;03A5; # (ðš¼ðš¼; ðš¼ðš¼; ðš¼ðš¼; Î¥; Î¥; ) MATHEMATICAL BOLD CAPITAL UPSILON
+1D6BD;1D6BD;1D6BD;03A6;03A6; # (ðš½ðš½; ðš½ðš½; ðš½ðš½; Φ; Φ; ) MATHEMATICAL BOLD CAPITAL PHI
+1D6BE;1D6BE;1D6BE;03A7;03A7; # (ðš¾ðš¾; ðš¾ðš¾; ðš¾ðš¾; Χ; Χ; ) MATHEMATICAL BOLD CAPITAL CHI
+1D6BF;1D6BF;1D6BF;03A8;03A8; # (ðš¿ðš¿; ðš¿ðš¿; ðš¿ðš¿; Ψ; Ψ; ) MATHEMATICAL BOLD CAPITAL PSI
+1D6C0;1D6C0;1D6C0;03A9;03A9; # (ð›€ð›€; ð›€ð›€; ð›€ð›€; Ω; Ω; ) MATHEMATICAL BOLD CAPITAL OMEGA
+1D6C1;1D6C1;1D6C1;2207;2207; # (ð›ð›; ð›ð›; ð›ð›; ∇; ∇; ) MATHEMATICAL BOLD NABLA
+1D6C2;1D6C2;1D6C2;03B1;03B1; # (ð›‚ð›‚; ð›‚ð›‚; ð›‚ð›‚; α; α; ) MATHEMATICAL BOLD SMALL ALPHA
+1D6C3;1D6C3;1D6C3;03B2;03B2; # (ð›ƒð›ƒ; ð›ƒð›ƒ; ð›ƒð›ƒ; β; β; ) MATHEMATICAL BOLD SMALL BETA
+1D6C4;1D6C4;1D6C4;03B3;03B3; # (ð›„ð›„; ð›„ð›„; ð›„ð›„; γ; γ; ) MATHEMATICAL BOLD SMALL GAMMA
+1D6C5;1D6C5;1D6C5;03B4;03B4; # (ð›…ð›…; ð›…ð›…; ð›…ð›…; δ; δ; ) MATHEMATICAL BOLD SMALL DELTA
+1D6C6;1D6C6;1D6C6;03B5;03B5; # (ð›†ð›†; ð›†ð›†; ð›†ð›†; ε; ε; ) MATHEMATICAL BOLD SMALL EPSILON
+1D6C7;1D6C7;1D6C7;03B6;03B6; # (ð›‡ð›‡; ð›‡ð›‡; ð›‡ð›‡; ζ; ζ; ) MATHEMATICAL BOLD SMALL ZETA
+1D6C8;1D6C8;1D6C8;03B7;03B7; # (ð›ˆð›ˆ; ð›ˆð›ˆ; ð›ˆð›ˆ; η; η; ) MATHEMATICAL BOLD SMALL ETA
+1D6C9;1D6C9;1D6C9;03B8;03B8; # (ð›‰ð›‰; ð›‰ð›‰; ð›‰ð›‰; θ; θ; ) MATHEMATICAL BOLD SMALL THETA
+1D6CA;1D6CA;1D6CA;03B9;03B9; # (ð›Šð›Š; ð›Šð›Š; ð›Šð›Š; ι; ι; ) MATHEMATICAL BOLD SMALL IOTA
+1D6CB;1D6CB;1D6CB;03BA;03BA; # (ð›‹ð›‹; ð›‹ð›‹; ð›‹ð›‹; κ; κ; ) MATHEMATICAL BOLD SMALL KAPPA
+1D6CC;1D6CC;1D6CC;03BB;03BB; # (ð›Œð›Œ; ð›Œð›Œ; ð›Œð›Œ; λ; λ; ) MATHEMATICAL BOLD SMALL LAMDA
+1D6CD;1D6CD;1D6CD;03BC;03BC; # (ð›ð›; ð›ð›; ð›ð›; μ; μ; ) MATHEMATICAL BOLD SMALL MU
+1D6CE;1D6CE;1D6CE;03BD;03BD; # (ð›Žð›Ž; ð›Žð›Ž; ð›Žð›Ž; ν; ν; ) MATHEMATICAL BOLD SMALL NU
+1D6CF;1D6CF;1D6CF;03BE;03BE; # (ð›ð›; ð›ð›; ð›ð›; ξ; ξ; ) MATHEMATICAL BOLD SMALL XI
+1D6D0;1D6D0;1D6D0;03BF;03BF; # (ð›ð›; ð›ð›; ð›ð›; ο; ο; ) MATHEMATICAL BOLD SMALL OMICRON
+1D6D1;1D6D1;1D6D1;03C0;03C0; # (ð›‘ð›‘; ð›‘ð›‘; ð›‘ð›‘; Ï€; Ï€; ) MATHEMATICAL BOLD SMALL PI
+1D6D2;1D6D2;1D6D2;03C1;03C1; # (ð›’ð›’; ð›’ð›’; ð›’ð›’; Ï; Ï; ) MATHEMATICAL BOLD SMALL RHO
+1D6D3;1D6D3;1D6D3;03C2;03C2; # (ð›“ð›“; ð›“ð›“; ð›“ð›“; Ï‚; Ï‚; ) MATHEMATICAL BOLD SMALL FINAL SIGMA
+1D6D4;1D6D4;1D6D4;03C3;03C3; # (ð›”ð›”; ð›”ð›”; ð›”ð›”; σ; σ; ) MATHEMATICAL BOLD SMALL SIGMA
+1D6D5;1D6D5;1D6D5;03C4;03C4; # (ð›•ð›•; ð›•ð›•; ð›•ð›•; Ï„; Ï„; ) MATHEMATICAL BOLD SMALL TAU
+1D6D6;1D6D6;1D6D6;03C5;03C5; # (ð›–ð›–; ð›–ð›–; ð›–ð›–; Ï…; Ï…; ) MATHEMATICAL BOLD SMALL UPSILON
+1D6D7;1D6D7;1D6D7;03C6;03C6; # (ð›—ð›—; ð›—ð›—; ð›—ð›—; φ; φ; ) MATHEMATICAL BOLD SMALL PHI
+1D6D8;1D6D8;1D6D8;03C7;03C7; # (ð›˜ð›˜; ð›˜ð›˜; ð›˜ð›˜; χ; χ; ) MATHEMATICAL BOLD SMALL CHI
+1D6D9;1D6D9;1D6D9;03C8;03C8; # (ð›™ð›™; ð›™ð›™; ð›™ð›™; ψ; ψ; ) MATHEMATICAL BOLD SMALL PSI
+1D6DA;1D6DA;1D6DA;03C9;03C9; # (ð›šð›š; ð›šð›š; ð›šð›š; ω; ω; ) MATHEMATICAL BOLD SMALL OMEGA
+1D6DB;1D6DB;1D6DB;2202;2202; # (ð››ð››; ð››ð››; ð››ð››; ∂; ∂; ) MATHEMATICAL BOLD PARTIAL DIFFERENTIAL
+1D6DC;1D6DC;1D6DC;03B5;03B5; # (ð›œð›œ; ð›œð›œ; ð›œð›œ; ε; ε; ) MATHEMATICAL BOLD EPSILON SYMBOL
+1D6DD;1D6DD;1D6DD;03B8;03B8; # (ð›ð›; ð›ð›; ð›ð›; θ; θ; ) MATHEMATICAL BOLD THETA SYMBOL
+1D6DE;1D6DE;1D6DE;03BA;03BA; # (ð›žð›ž; ð›žð›ž; ð›žð›ž; κ; κ; ) MATHEMATICAL BOLD KAPPA SYMBOL
+1D6DF;1D6DF;1D6DF;03C6;03C6; # (ð›Ÿð›Ÿ; ð›Ÿð›Ÿ; ð›Ÿð›Ÿ; φ; φ; ) MATHEMATICAL BOLD PHI SYMBOL
+1D6E0;1D6E0;1D6E0;03C1;03C1; # (ð› ð› ; ð› ð› ; ð› ð› ; Ï; Ï; ) MATHEMATICAL BOLD RHO SYMBOL
+1D6E1;1D6E1;1D6E1;03C0;03C0; # (ð›¡ð›¡; ð›¡ð›¡; ð›¡ð›¡; Ï€; Ï€; ) MATHEMATICAL BOLD PI SYMBOL
+1D6E2;1D6E2;1D6E2;0391;0391; # (ð›¢ð›¢; ð›¢ð›¢; ð›¢ð›¢; Α; Α; ) MATHEMATICAL ITALIC CAPITAL ALPHA
+1D6E3;1D6E3;1D6E3;0392;0392; # (ð›£ð›£; ð›£ð›£; ð›£ð›£; Î’; Î’; ) MATHEMATICAL ITALIC CAPITAL BETA
+1D6E4;1D6E4;1D6E4;0393;0393; # (ð›¤ð›¤; ð›¤ð›¤; ð›¤ð›¤; Γ; Γ; ) MATHEMATICAL ITALIC CAPITAL GAMMA
+1D6E5;1D6E5;1D6E5;0394;0394; # (ð›¥ð›¥; ð›¥ð›¥; ð›¥ð›¥; Δ; Δ; ) MATHEMATICAL ITALIC CAPITAL DELTA
+1D6E6;1D6E6;1D6E6;0395;0395; # (ð›¦ð›¦; ð›¦ð›¦; ð›¦ð›¦; Ε; Ε; ) MATHEMATICAL ITALIC CAPITAL EPSILON
+1D6E7;1D6E7;1D6E7;0396;0396; # (ð›§ð›§; ð›§ð›§; ð›§ð›§; Ζ; Ζ; ) MATHEMATICAL ITALIC CAPITAL ZETA
+1D6E8;1D6E8;1D6E8;0397;0397; # (ð›¨ð›¨; ð›¨ð›¨; ð›¨ð›¨; Η; Η; ) MATHEMATICAL ITALIC CAPITAL ETA
+1D6E9;1D6E9;1D6E9;0398;0398; # (ð›©ð›©; ð›©ð›©; ð›©ð›©; Θ; Θ; ) MATHEMATICAL ITALIC CAPITAL THETA
+1D6EA;1D6EA;1D6EA;0399;0399; # (ð›ªð›ª; ð›ªð›ª; ð›ªð›ª; Ι; Ι; ) MATHEMATICAL ITALIC CAPITAL IOTA
+1D6EB;1D6EB;1D6EB;039A;039A; # (ð›«ð›«; ð›«ð›«; ð›«ð›«; Κ; Κ; ) MATHEMATICAL ITALIC CAPITAL KAPPA
+1D6EC;1D6EC;1D6EC;039B;039B; # (ð›¬ð›¬; ð›¬ð›¬; ð›¬ð›¬; Λ; Λ; ) MATHEMATICAL ITALIC CAPITAL LAMDA
+1D6ED;1D6ED;1D6ED;039C;039C; # (ð›­ð›­; ð›­ð›­; ð›­ð›­; Μ; Μ; ) MATHEMATICAL ITALIC CAPITAL MU
+1D6EE;1D6EE;1D6EE;039D;039D; # (ð›®ð›®; ð›®ð›®; ð›®ð›®; Î; Î; ) MATHEMATICAL ITALIC CAPITAL NU
+1D6EF;1D6EF;1D6EF;039E;039E; # (ð›¯ð›¯; ð›¯ð›¯; ð›¯ð›¯; Ξ; Ξ; ) MATHEMATICAL ITALIC CAPITAL XI
+1D6F0;1D6F0;1D6F0;039F;039F; # (ð›°ð›°; ð›°ð›°; ð›°ð›°; Ο; Ο; ) MATHEMATICAL ITALIC CAPITAL OMICRON
+1D6F1;1D6F1;1D6F1;03A0;03A0; # (ð›±ð›±; ð›±ð›±; ð›±ð›±; Π; Π; ) MATHEMATICAL ITALIC CAPITAL PI
+1D6F2;1D6F2;1D6F2;03A1;03A1; # (ð›²ð›²; ð›²ð›²; ð›²ð›²; Ρ; Ρ; ) MATHEMATICAL ITALIC CAPITAL RHO
+1D6F3;1D6F3;1D6F3;0398;0398; # (ð›³ð›³; ð›³ð›³; ð›³ð›³; Θ; Θ; ) MATHEMATICAL ITALIC CAPITAL THETA SYMBOL
+1D6F4;1D6F4;1D6F4;03A3;03A3; # (ð›´ð›´; ð›´ð›´; ð›´ð›´; Σ; Σ; ) MATHEMATICAL ITALIC CAPITAL SIGMA
+1D6F5;1D6F5;1D6F5;03A4;03A4; # (ð›µð›µ; ð›µð›µ; ð›µð›µ; Τ; Τ; ) MATHEMATICAL ITALIC CAPITAL TAU
+1D6F6;1D6F6;1D6F6;03A5;03A5; # (ð›¶ð›¶; ð›¶ð›¶; ð›¶ð›¶; Î¥; Î¥; ) MATHEMATICAL ITALIC CAPITAL UPSILON
+1D6F7;1D6F7;1D6F7;03A6;03A6; # (ð›·ð›·; ð›·ð›·; ð›·ð›·; Φ; Φ; ) MATHEMATICAL ITALIC CAPITAL PHI
+1D6F8;1D6F8;1D6F8;03A7;03A7; # (ð›¸ð›¸; ð›¸ð›¸; ð›¸ð›¸; Χ; Χ; ) MATHEMATICAL ITALIC CAPITAL CHI
+1D6F9;1D6F9;1D6F9;03A8;03A8; # (ð›¹ð›¹; ð›¹ð›¹; ð›¹ð›¹; Ψ; Ψ; ) MATHEMATICAL ITALIC CAPITAL PSI
+1D6FA;1D6FA;1D6FA;03A9;03A9; # (ð›ºð›º; ð›ºð›º; ð›ºð›º; Ω; Ω; ) MATHEMATICAL ITALIC CAPITAL OMEGA
+1D6FB;1D6FB;1D6FB;2207;2207; # (ð›»ð›»; ð›»ð›»; ð›»ð›»; ∇; ∇; ) MATHEMATICAL ITALIC NABLA
+1D6FC;1D6FC;1D6FC;03B1;03B1; # (ð›¼ð›¼; ð›¼ð›¼; ð›¼ð›¼; α; α; ) MATHEMATICAL ITALIC SMALL ALPHA
+1D6FD;1D6FD;1D6FD;03B2;03B2; # (ð›½ð›½; ð›½ð›½; ð›½ð›½; β; β; ) MATHEMATICAL ITALIC SMALL BETA
+1D6FE;1D6FE;1D6FE;03B3;03B3; # (ð›¾ð›¾; ð›¾ð›¾; ð›¾ð›¾; γ; γ; ) MATHEMATICAL ITALIC SMALL GAMMA
+1D6FF;1D6FF;1D6FF;03B4;03B4; # (ð›¿ð›¿; ð›¿ð›¿; ð›¿ð›¿; δ; δ; ) MATHEMATICAL ITALIC SMALL DELTA
+1D700;1D700;1D700;03B5;03B5; # (ðœ€ðœ€; ðœ€ðœ€; ðœ€ðœ€; ε; ε; ) MATHEMATICAL ITALIC SMALL EPSILON
+1D701;1D701;1D701;03B6;03B6; # (ðœðœ; ðœðœ; ðœðœ; ζ; ζ; ) MATHEMATICAL ITALIC SMALL ZETA
+1D702;1D702;1D702;03B7;03B7; # (ðœ‚ðœ‚; ðœ‚ðœ‚; ðœ‚ðœ‚; η; η; ) MATHEMATICAL ITALIC SMALL ETA
+1D703;1D703;1D703;03B8;03B8; # (ðœƒðœƒ; ðœƒðœƒ; ðœƒðœƒ; θ; θ; ) MATHEMATICAL ITALIC SMALL THETA
+1D704;1D704;1D704;03B9;03B9; # (ðœ„ðœ„; ðœ„ðœ„; ðœ„ðœ„; ι; ι; ) MATHEMATICAL ITALIC SMALL IOTA
+1D705;1D705;1D705;03BA;03BA; # (ðœ…ðœ…; ðœ…ðœ…; ðœ…ðœ…; κ; κ; ) MATHEMATICAL ITALIC SMALL KAPPA
+1D706;1D706;1D706;03BB;03BB; # (ðœ†ðœ†; ðœ†ðœ†; ðœ†ðœ†; λ; λ; ) MATHEMATICAL ITALIC SMALL LAMDA
+1D707;1D707;1D707;03BC;03BC; # (ðœ‡ðœ‡; ðœ‡ðœ‡; ðœ‡ðœ‡; μ; μ; ) MATHEMATICAL ITALIC SMALL MU
+1D708;1D708;1D708;03BD;03BD; # (ðœˆðœˆ; ðœˆðœˆ; ðœˆðœˆ; ν; ν; ) MATHEMATICAL ITALIC SMALL NU
+1D709;1D709;1D709;03BE;03BE; # (ðœ‰ðœ‰; ðœ‰ðœ‰; ðœ‰ðœ‰; ξ; ξ; ) MATHEMATICAL ITALIC SMALL XI
+1D70A;1D70A;1D70A;03BF;03BF; # (ðœŠðœŠ; ðœŠðœŠ; ðœŠðœŠ; ο; ο; ) MATHEMATICAL ITALIC SMALL OMICRON
+1D70B;1D70B;1D70B;03C0;03C0; # (ðœ‹ðœ‹; ðœ‹ðœ‹; ðœ‹ðœ‹; Ï€; Ï€; ) MATHEMATICAL ITALIC SMALL PI
+1D70C;1D70C;1D70C;03C1;03C1; # (ðœŒðœŒ; ðœŒðœŒ; ðœŒðœŒ; Ï; Ï; ) MATHEMATICAL ITALIC SMALL RHO
+1D70D;1D70D;1D70D;03C2;03C2; # (ðœðœ; ðœðœ; ðœðœ; Ï‚; Ï‚; ) MATHEMATICAL ITALIC SMALL FINAL SIGMA
+1D70E;1D70E;1D70E;03C3;03C3; # (ðœŽðœŽ; ðœŽðœŽ; ðœŽðœŽ; σ; σ; ) MATHEMATICAL ITALIC SMALL SIGMA
+1D70F;1D70F;1D70F;03C4;03C4; # (ðœðœ; ðœðœ; ðœðœ; Ï„; Ï„; ) MATHEMATICAL ITALIC SMALL TAU
+1D710;1D710;1D710;03C5;03C5; # (ðœðœ; ðœðœ; ðœðœ; Ï…; Ï…; ) MATHEMATICAL ITALIC SMALL UPSILON
+1D711;1D711;1D711;03C6;03C6; # (ðœ‘ðœ‘; ðœ‘ðœ‘; ðœ‘ðœ‘; φ; φ; ) MATHEMATICAL ITALIC SMALL PHI
+1D712;1D712;1D712;03C7;03C7; # (ðœ’ðœ’; ðœ’ðœ’; ðœ’ðœ’; χ; χ; ) MATHEMATICAL ITALIC SMALL CHI
+1D713;1D713;1D713;03C8;03C8; # (ðœ“ðœ“; ðœ“ðœ“; ðœ“ðœ“; ψ; ψ; ) MATHEMATICAL ITALIC SMALL PSI
+1D714;1D714;1D714;03C9;03C9; # (ðœ”ðœ”; ðœ”ðœ”; ðœ”ðœ”; ω; ω; ) MATHEMATICAL ITALIC SMALL OMEGA
+1D715;1D715;1D715;2202;2202; # (ðœ•ðœ•; ðœ•ðœ•; ðœ•ðœ•; ∂; ∂; ) MATHEMATICAL ITALIC PARTIAL DIFFERENTIAL
+1D716;1D716;1D716;03B5;03B5; # (ðœ–ðœ–; ðœ–ðœ–; ðœ–ðœ–; ε; ε; ) MATHEMATICAL ITALIC EPSILON SYMBOL
+1D717;1D717;1D717;03B8;03B8; # (ðœ—ðœ—; ðœ—ðœ—; ðœ—ðœ—; θ; θ; ) MATHEMATICAL ITALIC THETA SYMBOL
+1D718;1D718;1D718;03BA;03BA; # (ðœ˜ðœ˜; ðœ˜ðœ˜; ðœ˜ðœ˜; κ; κ; ) MATHEMATICAL ITALIC KAPPA SYMBOL
+1D719;1D719;1D719;03C6;03C6; # (ðœ™ðœ™; ðœ™ðœ™; ðœ™ðœ™; φ; φ; ) MATHEMATICAL ITALIC PHI SYMBOL
+1D71A;1D71A;1D71A;03C1;03C1; # (ðœšðœš; ðœšðœš; ðœšðœš; Ï; Ï; ) MATHEMATICAL ITALIC RHO SYMBOL
+1D71B;1D71B;1D71B;03C0;03C0; # (ðœ›ðœ›; ðœ›ðœ›; ðœ›ðœ›; Ï€; Ï€; ) MATHEMATICAL ITALIC PI SYMBOL
+1D71C;1D71C;1D71C;0391;0391; # (ðœœðœœ; ðœœðœœ; ðœœðœœ; Α; Α; ) MATHEMATICAL BOLD ITALIC CAPITAL ALPHA
+1D71D;1D71D;1D71D;0392;0392; # (ðœðœ; ðœðœ; ðœðœ; Î’; Î’; ) MATHEMATICAL BOLD ITALIC CAPITAL BETA
+1D71E;1D71E;1D71E;0393;0393; # (ðœžðœž; ðœžðœž; ðœžðœž; Γ; Γ; ) MATHEMATICAL BOLD ITALIC CAPITAL GAMMA
+1D71F;1D71F;1D71F;0394;0394; # (ðœŸðœŸ; ðœŸðœŸ; ðœŸðœŸ; Δ; Δ; ) MATHEMATICAL BOLD ITALIC CAPITAL DELTA
+1D720;1D720;1D720;0395;0395; # (ðœ ðœ ; ðœ ðœ ; ðœ ðœ ; Ε; Ε; ) MATHEMATICAL BOLD ITALIC CAPITAL EPSILON
+1D721;1D721;1D721;0396;0396; # (ðœ¡ðœ¡; ðœ¡ðœ¡; ðœ¡ðœ¡; Ζ; Ζ; ) MATHEMATICAL BOLD ITALIC CAPITAL ZETA
+1D722;1D722;1D722;0397;0397; # (ðœ¢ðœ¢; ðœ¢ðœ¢; ðœ¢ðœ¢; Η; Η; ) MATHEMATICAL BOLD ITALIC CAPITAL ETA
+1D723;1D723;1D723;0398;0398; # (ðœ£ðœ£; ðœ£ðœ£; ðœ£ðœ£; Θ; Θ; ) MATHEMATICAL BOLD ITALIC CAPITAL THETA
+1D724;1D724;1D724;0399;0399; # (ðœ¤ðœ¤; ðœ¤ðœ¤; ðœ¤ðœ¤; Ι; Ι; ) MATHEMATICAL BOLD ITALIC CAPITAL IOTA
+1D725;1D725;1D725;039A;039A; # (ðœ¥ðœ¥; ðœ¥ðœ¥; ðœ¥ðœ¥; Κ; Κ; ) MATHEMATICAL BOLD ITALIC CAPITAL KAPPA
+1D726;1D726;1D726;039B;039B; # (ðœ¦ðœ¦; ðœ¦ðœ¦; ðœ¦ðœ¦; Λ; Λ; ) MATHEMATICAL BOLD ITALIC CAPITAL LAMDA
+1D727;1D727;1D727;039C;039C; # (ðœ§ðœ§; ðœ§ðœ§; ðœ§ðœ§; Μ; Μ; ) MATHEMATICAL BOLD ITALIC CAPITAL MU
+1D728;1D728;1D728;039D;039D; # (ðœ¨ðœ¨; ðœ¨ðœ¨; ðœ¨ðœ¨; Î; Î; ) MATHEMATICAL BOLD ITALIC CAPITAL NU
+1D729;1D729;1D729;039E;039E; # (ðœ©ðœ©; ðœ©ðœ©; ðœ©ðœ©; Ξ; Ξ; ) MATHEMATICAL BOLD ITALIC CAPITAL XI
+1D72A;1D72A;1D72A;039F;039F; # (ðœªðœª; ðœªðœª; ðœªðœª; Ο; Ο; ) MATHEMATICAL BOLD ITALIC CAPITAL OMICRON
+1D72B;1D72B;1D72B;03A0;03A0; # (ðœ«ðœ«; ðœ«ðœ«; ðœ«ðœ«; Π; Π; ) MATHEMATICAL BOLD ITALIC CAPITAL PI
+1D72C;1D72C;1D72C;03A1;03A1; # (ðœ¬ðœ¬; ðœ¬ðœ¬; ðœ¬ðœ¬; Ρ; Ρ; ) MATHEMATICAL BOLD ITALIC CAPITAL RHO
+1D72D;1D72D;1D72D;0398;0398; # (ðœ­ðœ­; ðœ­ðœ­; ðœ­ðœ­; Θ; Θ; ) MATHEMATICAL BOLD ITALIC CAPITAL THETA SYMBOL
+1D72E;1D72E;1D72E;03A3;03A3; # (ðœ®ðœ®; ðœ®ðœ®; ðœ®ðœ®; Σ; Σ; ) MATHEMATICAL BOLD ITALIC CAPITAL SIGMA
+1D72F;1D72F;1D72F;03A4;03A4; # (ðœ¯ðœ¯; ðœ¯ðœ¯; ðœ¯ðœ¯; Τ; Τ; ) MATHEMATICAL BOLD ITALIC CAPITAL TAU
+1D730;1D730;1D730;03A5;03A5; # (ðœ°ðœ°; ðœ°ðœ°; ðœ°ðœ°; Î¥; Î¥; ) MATHEMATICAL BOLD ITALIC CAPITAL UPSILON
+1D731;1D731;1D731;03A6;03A6; # (ðœ±ðœ±; ðœ±ðœ±; ðœ±ðœ±; Φ; Φ; ) MATHEMATICAL BOLD ITALIC CAPITAL PHI
+1D732;1D732;1D732;03A7;03A7; # (ðœ²ðœ²; ðœ²ðœ²; ðœ²ðœ²; Χ; Χ; ) MATHEMATICAL BOLD ITALIC CAPITAL CHI
+1D733;1D733;1D733;03A8;03A8; # (ðœ³ðœ³; ðœ³ðœ³; ðœ³ðœ³; Ψ; Ψ; ) MATHEMATICAL BOLD ITALIC CAPITAL PSI
+1D734;1D734;1D734;03A9;03A9; # (ðœ´ðœ´; ðœ´ðœ´; ðœ´ðœ´; Ω; Ω; ) MATHEMATICAL BOLD ITALIC CAPITAL OMEGA
+1D735;1D735;1D735;2207;2207; # (ðœµðœµ; ðœµðœµ; ðœµðœµ; ∇; ∇; ) MATHEMATICAL BOLD ITALIC NABLA
+1D736;1D736;1D736;03B1;03B1; # (ðœ¶ðœ¶; ðœ¶ðœ¶; ðœ¶ðœ¶; α; α; ) MATHEMATICAL BOLD ITALIC SMALL ALPHA
+1D737;1D737;1D737;03B2;03B2; # (ðœ·ðœ·; ðœ·ðœ·; ðœ·ðœ·; β; β; ) MATHEMATICAL BOLD ITALIC SMALL BETA
+1D738;1D738;1D738;03B3;03B3; # (ðœ¸ðœ¸; ðœ¸ðœ¸; ðœ¸ðœ¸; γ; γ; ) MATHEMATICAL BOLD ITALIC SMALL GAMMA
+1D739;1D739;1D739;03B4;03B4; # (ðœ¹ðœ¹; ðœ¹ðœ¹; ðœ¹ðœ¹; δ; δ; ) MATHEMATICAL BOLD ITALIC SMALL DELTA
+1D73A;1D73A;1D73A;03B5;03B5; # (ðœºðœº; ðœºðœº; ðœºðœº; ε; ε; ) MATHEMATICAL BOLD ITALIC SMALL EPSILON
+1D73B;1D73B;1D73B;03B6;03B6; # (ðœ»ðœ»; ðœ»ðœ»; ðœ»ðœ»; ζ; ζ; ) MATHEMATICAL BOLD ITALIC SMALL ZETA
+1D73C;1D73C;1D73C;03B7;03B7; # (ðœ¼ðœ¼; ðœ¼ðœ¼; ðœ¼ðœ¼; η; η; ) MATHEMATICAL BOLD ITALIC SMALL ETA
+1D73D;1D73D;1D73D;03B8;03B8; # (ðœ½ðœ½; ðœ½ðœ½; ðœ½ðœ½; θ; θ; ) MATHEMATICAL BOLD ITALIC SMALL THETA
+1D73E;1D73E;1D73E;03B9;03B9; # (ðœ¾ðœ¾; ðœ¾ðœ¾; ðœ¾ðœ¾; ι; ι; ) MATHEMATICAL BOLD ITALIC SMALL IOTA
+1D73F;1D73F;1D73F;03BA;03BA; # (ðœ¿ðœ¿; ðœ¿ðœ¿; ðœ¿ðœ¿; κ; κ; ) MATHEMATICAL BOLD ITALIC SMALL KAPPA
+1D740;1D740;1D740;03BB;03BB; # (ð€ð€; ð€ð€; ð€ð€; λ; λ; ) MATHEMATICAL BOLD ITALIC SMALL LAMDA
+1D741;1D741;1D741;03BC;03BC; # (ðð; ðð; ðð; μ; μ; ) MATHEMATICAL BOLD ITALIC SMALL MU
+1D742;1D742;1D742;03BD;03BD; # (ð‚ð‚; ð‚ð‚; ð‚ð‚; ν; ν; ) MATHEMATICAL BOLD ITALIC SMALL NU
+1D743;1D743;1D743;03BE;03BE; # (ðƒðƒ; ðƒðƒ; ðƒðƒ; ξ; ξ; ) MATHEMATICAL BOLD ITALIC SMALL XI
+1D744;1D744;1D744;03BF;03BF; # (ð„ð„; ð„ð„; ð„ð„; ο; ο; ) MATHEMATICAL BOLD ITALIC SMALL OMICRON
+1D745;1D745;1D745;03C0;03C0; # (ð…ð…; ð…ð…; ð…ð…; Ï€; Ï€; ) MATHEMATICAL BOLD ITALIC SMALL PI
+1D746;1D746;1D746;03C1;03C1; # (ð†ð†; ð†ð†; ð†ð†; Ï; Ï; ) MATHEMATICAL BOLD ITALIC SMALL RHO
+1D747;1D747;1D747;03C2;03C2; # (ð‡ð‡; ð‡ð‡; ð‡ð‡; Ï‚; Ï‚; ) MATHEMATICAL BOLD ITALIC SMALL FINAL SIGMA
+1D748;1D748;1D748;03C3;03C3; # (ðˆðˆ; ðˆðˆ; ðˆðˆ; σ; σ; ) MATHEMATICAL BOLD ITALIC SMALL SIGMA
+1D749;1D749;1D749;03C4;03C4; # (ð‰ð‰; ð‰ð‰; ð‰ð‰; Ï„; Ï„; ) MATHEMATICAL BOLD ITALIC SMALL TAU
+1D74A;1D74A;1D74A;03C5;03C5; # (ðŠðŠ; ðŠðŠ; ðŠðŠ; Ï…; Ï…; ) MATHEMATICAL BOLD ITALIC SMALL UPSILON
+1D74B;1D74B;1D74B;03C6;03C6; # (ð‹ð‹; ð‹ð‹; ð‹ð‹; φ; φ; ) MATHEMATICAL BOLD ITALIC SMALL PHI
+1D74C;1D74C;1D74C;03C7;03C7; # (ðŒðŒ; ðŒðŒ; ðŒðŒ; χ; χ; ) MATHEMATICAL BOLD ITALIC SMALL CHI
+1D74D;1D74D;1D74D;03C8;03C8; # (ðð; ðð; ðð; ψ; ψ; ) MATHEMATICAL BOLD ITALIC SMALL PSI
+1D74E;1D74E;1D74E;03C9;03C9; # (ðŽðŽ; ðŽðŽ; ðŽðŽ; ω; ω; ) MATHEMATICAL BOLD ITALIC SMALL OMEGA
+1D74F;1D74F;1D74F;2202;2202; # (ðð; ðð; ðð; ∂; ∂; ) MATHEMATICAL BOLD ITALIC PARTIAL DIFFERENTIAL
+1D750;1D750;1D750;03B5;03B5; # (ðð; ðð; ðð; ε; ε; ) MATHEMATICAL BOLD ITALIC EPSILON SYMBOL
+1D751;1D751;1D751;03B8;03B8; # (ð‘ð‘; ð‘ð‘; ð‘ð‘; θ; θ; ) MATHEMATICAL BOLD ITALIC THETA SYMBOL
+1D752;1D752;1D752;03BA;03BA; # (ð’ð’; ð’ð’; ð’ð’; κ; κ; ) MATHEMATICAL BOLD ITALIC KAPPA SYMBOL
+1D753;1D753;1D753;03C6;03C6; # (ð“ð“; ð“ð“; ð“ð“; φ; φ; ) MATHEMATICAL BOLD ITALIC PHI SYMBOL
+1D754;1D754;1D754;03C1;03C1; # (ð”ð”; ð”ð”; ð”ð”; Ï; Ï; ) MATHEMATICAL BOLD ITALIC RHO SYMBOL
+1D755;1D755;1D755;03C0;03C0; # (ð•ð•; ð•ð•; ð•ð•; Ï€; Ï€; ) MATHEMATICAL BOLD ITALIC PI SYMBOL
+1D756;1D756;1D756;0391;0391; # (ð–ð–; ð–ð–; ð–ð–; Α; Α; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL ALPHA
+1D757;1D757;1D757;0392;0392; # (ð—ð—; ð—ð—; ð—ð—; Î’; Î’; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL BETA
+1D758;1D758;1D758;0393;0393; # (ð˜ð˜; ð˜ð˜; ð˜ð˜; Γ; Γ; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL GAMMA
+1D759;1D759;1D759;0394;0394; # (ð™ð™; ð™ð™; ð™ð™; Δ; Δ; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL DELTA
+1D75A;1D75A;1D75A;0395;0395; # (ðšðš; ðšðš; ðšðš; Ε; Ε; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL EPSILON
+1D75B;1D75B;1D75B;0396;0396; # (ð›ð›; ð›ð›; ð›ð›; Ζ; Ζ; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL ZETA
+1D75C;1D75C;1D75C;0397;0397; # (ðœðœ; ðœðœ; ðœðœ; Η; Η; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL ETA
+1D75D;1D75D;1D75D;0398;0398; # (ðð; ðð; ðð; Θ; Θ; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL THETA
+1D75E;1D75E;1D75E;0399;0399; # (ðžðž; ðžðž; ðžðž; Ι; Ι; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL IOTA
+1D75F;1D75F;1D75F;039A;039A; # (ðŸðŸ; ðŸðŸ; ðŸðŸ; Κ; Κ; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL KAPPA
+1D760;1D760;1D760;039B;039B; # (ð ð ; ð ð ; ð ð ; Λ; Λ; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL LAMDA
+1D761;1D761;1D761;039C;039C; # (ð¡ð¡; ð¡ð¡; ð¡ð¡; Μ; Μ; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL MU
+1D762;1D762;1D762;039D;039D; # (ð¢ð¢; ð¢ð¢; ð¢ð¢; Î; Î; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL NU
+1D763;1D763;1D763;039E;039E; # (ð£ð£; ð£ð£; ð£ð£; Ξ; Ξ; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL XI
+1D764;1D764;1D764;039F;039F; # (ð¤ð¤; ð¤ð¤; ð¤ð¤; Ο; Ο; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL OMICRON
+1D765;1D765;1D765;03A0;03A0; # (ð¥ð¥; ð¥ð¥; ð¥ð¥; Π; Π; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL PI
+1D766;1D766;1D766;03A1;03A1; # (ð¦ð¦; ð¦ð¦; ð¦ð¦; Ρ; Ρ; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL RHO
+1D767;1D767;1D767;0398;0398; # (ð§ð§; ð§ð§; ð§ð§; Θ; Θ; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL THETA SYMBOL
+1D768;1D768;1D768;03A3;03A3; # (ð¨ð¨; ð¨ð¨; ð¨ð¨; Σ; Σ; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL SIGMA
+1D769;1D769;1D769;03A4;03A4; # (ð©ð©; ð©ð©; ð©ð©; Τ; Τ; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL TAU
+1D76A;1D76A;1D76A;03A5;03A5; # (ðªðª; ðªðª; ðªðª; Î¥; Î¥; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL UPSILON
+1D76B;1D76B;1D76B;03A6;03A6; # (ð«ð«; ð«ð«; ð«ð«; Φ; Φ; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL PHI
+1D76C;1D76C;1D76C;03A7;03A7; # (ð¬ð¬; ð¬ð¬; ð¬ð¬; Χ; Χ; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL CHI
+1D76D;1D76D;1D76D;03A8;03A8; # (ð­ð­; ð­ð­; ð­ð­; Ψ; Ψ; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL PSI
+1D76E;1D76E;1D76E;03A9;03A9; # (ð®ð®; ð®ð®; ð®ð®; Ω; Ω; ) MATHEMATICAL SANS-SERIF BOLD CAPITAL OMEGA
+1D76F;1D76F;1D76F;2207;2207; # (ð¯ð¯; ð¯ð¯; ð¯ð¯; ∇; ∇; ) MATHEMATICAL SANS-SERIF BOLD NABLA
+1D770;1D770;1D770;03B1;03B1; # (ð°ð°; ð°ð°; ð°ð°; α; α; ) MATHEMATICAL SANS-SERIF BOLD SMALL ALPHA
+1D771;1D771;1D771;03B2;03B2; # (ð±ð±; ð±ð±; ð±ð±; β; β; ) MATHEMATICAL SANS-SERIF BOLD SMALL BETA
+1D772;1D772;1D772;03B3;03B3; # (ð²ð²; ð²ð²; ð²ð²; γ; γ; ) MATHEMATICAL SANS-SERIF BOLD SMALL GAMMA
+1D773;1D773;1D773;03B4;03B4; # (ð³ð³; ð³ð³; ð³ð³; δ; δ; ) MATHEMATICAL SANS-SERIF BOLD SMALL DELTA
+1D774;1D774;1D774;03B5;03B5; # (ð´ð´; ð´ð´; ð´ð´; ε; ε; ) MATHEMATICAL SANS-SERIF BOLD SMALL EPSILON
+1D775;1D775;1D775;03B6;03B6; # (ðµðµ; ðµðµ; ðµðµ; ζ; ζ; ) MATHEMATICAL SANS-SERIF BOLD SMALL ZETA
+1D776;1D776;1D776;03B7;03B7; # (ð¶ð¶; ð¶ð¶; ð¶ð¶; η; η; ) MATHEMATICAL SANS-SERIF BOLD SMALL ETA
+1D777;1D777;1D777;03B8;03B8; # (ð·ð·; ð·ð·; ð·ð·; θ; θ; ) MATHEMATICAL SANS-SERIF BOLD SMALL THETA
+1D778;1D778;1D778;03B9;03B9; # (ð¸ð¸; ð¸ð¸; ð¸ð¸; ι; ι; ) MATHEMATICAL SANS-SERIF BOLD SMALL IOTA
+1D779;1D779;1D779;03BA;03BA; # (ð¹ð¹; ð¹ð¹; ð¹ð¹; κ; κ; ) MATHEMATICAL SANS-SERIF BOLD SMALL KAPPA
+1D77A;1D77A;1D77A;03BB;03BB; # (ðºðº; ðºðº; ðºðº; λ; λ; ) MATHEMATICAL SANS-SERIF BOLD SMALL LAMDA
+1D77B;1D77B;1D77B;03BC;03BC; # (ð»ð»; ð»ð»; ð»ð»; μ; μ; ) MATHEMATICAL SANS-SERIF BOLD SMALL MU
+1D77C;1D77C;1D77C;03BD;03BD; # (ð¼ð¼; ð¼ð¼; ð¼ð¼; ν; ν; ) MATHEMATICAL SANS-SERIF BOLD SMALL NU
+1D77D;1D77D;1D77D;03BE;03BE; # (ð½ð½; ð½ð½; ð½ð½; ξ; ξ; ) MATHEMATICAL SANS-SERIF BOLD SMALL XI
+1D77E;1D77E;1D77E;03BF;03BF; # (ð¾ð¾; ð¾ð¾; ð¾ð¾; ο; ο; ) MATHEMATICAL SANS-SERIF BOLD SMALL OMICRON
+1D77F;1D77F;1D77F;03C0;03C0; # (ð¿ð¿; ð¿ð¿; ð¿ð¿; Ï€; Ï€; ) MATHEMATICAL SANS-SERIF BOLD SMALL PI
+1D780;1D780;1D780;03C1;03C1; # (ðž€ðž€; ðž€ðž€; ðž€ðž€; Ï; Ï; ) MATHEMATICAL SANS-SERIF BOLD SMALL RHO
+1D781;1D781;1D781;03C2;03C2; # (ðžðž; ðžðž; ðžðž; Ï‚; Ï‚; ) MATHEMATICAL SANS-SERIF BOLD SMALL FINAL SIGMA
+1D782;1D782;1D782;03C3;03C3; # (ðž‚ðž‚; ðž‚ðž‚; ðž‚ðž‚; σ; σ; ) MATHEMATICAL SANS-SERIF BOLD SMALL SIGMA
+1D783;1D783;1D783;03C4;03C4; # (ðžƒðžƒ; ðžƒðžƒ; ðžƒðžƒ; Ï„; Ï„; ) MATHEMATICAL SANS-SERIF BOLD SMALL TAU
+1D784;1D784;1D784;03C5;03C5; # (ðž„ðž„; ðž„ðž„; ðž„ðž„; Ï…; Ï…; ) MATHEMATICAL SANS-SERIF BOLD SMALL UPSILON
+1D785;1D785;1D785;03C6;03C6; # (ðž…ðž…; ðž…ðž…; ðž…ðž…; φ; φ; ) MATHEMATICAL SANS-SERIF BOLD SMALL PHI
+1D786;1D786;1D786;03C7;03C7; # (ðž†ðž†; ðž†ðž†; ðž†ðž†; χ; χ; ) MATHEMATICAL SANS-SERIF BOLD SMALL CHI
+1D787;1D787;1D787;03C8;03C8; # (ðž‡ðž‡; ðž‡ðž‡; ðž‡ðž‡; ψ; ψ; ) MATHEMATICAL SANS-SERIF BOLD SMALL PSI
+1D788;1D788;1D788;03C9;03C9; # (ðžˆðžˆ; ðžˆðžˆ; ðžˆðžˆ; ω; ω; ) MATHEMATICAL SANS-SERIF BOLD SMALL OMEGA
+1D789;1D789;1D789;2202;2202; # (ðž‰ðž‰; ðž‰ðž‰; ðž‰ðž‰; ∂; ∂; ) MATHEMATICAL SANS-SERIF BOLD PARTIAL DIFFERENTIAL
+1D78A;1D78A;1D78A;03B5;03B5; # (ðžŠðžŠ; ðžŠðžŠ; ðžŠðžŠ; ε; ε; ) MATHEMATICAL SANS-SERIF BOLD EPSILON SYMBOL
+1D78B;1D78B;1D78B;03B8;03B8; # (ðž‹ðž‹; ðž‹ðž‹; ðž‹ðž‹; θ; θ; ) MATHEMATICAL SANS-SERIF BOLD THETA SYMBOL
+1D78C;1D78C;1D78C;03BA;03BA; # (ðžŒðžŒ; ðžŒðžŒ; ðžŒðžŒ; κ; κ; ) MATHEMATICAL SANS-SERIF BOLD KAPPA SYMBOL
+1D78D;1D78D;1D78D;03C6;03C6; # (ðžðž; ðžðž; ðžðž; φ; φ; ) MATHEMATICAL SANS-SERIF BOLD PHI SYMBOL
+1D78E;1D78E;1D78E;03C1;03C1; # (ðžŽðžŽ; ðžŽðžŽ; ðžŽðžŽ; Ï; Ï; ) MATHEMATICAL SANS-SERIF BOLD RHO SYMBOL
+1D78F;1D78F;1D78F;03C0;03C0; # (ðžðž; ðžðž; ðžðž; Ï€; Ï€; ) MATHEMATICAL SANS-SERIF BOLD PI SYMBOL
+1D790;1D790;1D790;0391;0391; # (ðžðž; ðžðž; ðžðž; Α; Α; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL ALPHA
+1D791;1D791;1D791;0392;0392; # (ðž‘ðž‘; ðž‘ðž‘; ðž‘ðž‘; Î’; Î’; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL BETA
+1D792;1D792;1D792;0393;0393; # (ðž’ðž’; ðž’ðž’; ðž’ðž’; Γ; Γ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL GAMMA
+1D793;1D793;1D793;0394;0394; # (ðž“ðž“; ðž“ðž“; ðž“ðž“; Δ; Δ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL DELTA
+1D794;1D794;1D794;0395;0395; # (ðž”ðž”; ðž”ðž”; ðž”ðž”; Ε; Ε; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL EPSILON
+1D795;1D795;1D795;0396;0396; # (ðž•ðž•; ðž•ðž•; ðž•ðž•; Ζ; Ζ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL ZETA
+1D796;1D796;1D796;0397;0397; # (ðž–ðž–; ðž–ðž–; ðž–ðž–; Η; Η; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL ETA
+1D797;1D797;1D797;0398;0398; # (ðž—ðž—; ðž—ðž—; ðž—ðž—; Θ; Θ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL THETA
+1D798;1D798;1D798;0399;0399; # (ðž˜ðž˜; ðž˜ðž˜; ðž˜ðž˜; Ι; Ι; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL IOTA
+1D799;1D799;1D799;039A;039A; # (ðž™ðž™; ðž™ðž™; ðž™ðž™; Κ; Κ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL KAPPA
+1D79A;1D79A;1D79A;039B;039B; # (ðžšðžš; ðžšðžš; ðžšðžš; Λ; Λ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL LAMDA
+1D79B;1D79B;1D79B;039C;039C; # (ðž›ðž›; ðž›ðž›; ðž›ðž›; Μ; Μ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL MU
+1D79C;1D79C;1D79C;039D;039D; # (ðžœðžœ; ðžœðžœ; ðžœðžœ; Î; Î; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL NU
+1D79D;1D79D;1D79D;039E;039E; # (ðžðž; ðžðž; ðžðž; Ξ; Ξ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL XI
+1D79E;1D79E;1D79E;039F;039F; # (ðžžðžž; ðžžðžž; ðžžðžž; Ο; Ο; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL OMICRON
+1D79F;1D79F;1D79F;03A0;03A0; # (ðžŸðžŸ; ðžŸðžŸ; ðžŸðžŸ; Π; Π; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL PI
+1D7A0;1D7A0;1D7A0;03A1;03A1; # (ðž ðž ; ðž ðž ; ðž ðž ; Ρ; Ρ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL RHO
+1D7A1;1D7A1;1D7A1;0398;0398; # (ðž¡ðž¡; ðž¡ðž¡; ðž¡ðž¡; Θ; Θ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL THETA SYMBOL
+1D7A2;1D7A2;1D7A2;03A3;03A3; # (ðž¢ðž¢; ðž¢ðž¢; ðž¢ðž¢; Σ; Σ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL SIGMA
+1D7A3;1D7A3;1D7A3;03A4;03A4; # (ðž£ðž£; ðž£ðž£; ðž£ðž£; Τ; Τ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL TAU
+1D7A4;1D7A4;1D7A4;03A5;03A5; # (ðž¤ðž¤; ðž¤ðž¤; ðž¤ðž¤; Î¥; Î¥; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL UPSILON
+1D7A5;1D7A5;1D7A5;03A6;03A6; # (ðž¥ðž¥; ðž¥ðž¥; ðž¥ðž¥; Φ; Φ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL PHI
+1D7A6;1D7A6;1D7A6;03A7;03A7; # (ðž¦ðž¦; ðž¦ðž¦; ðž¦ðž¦; Χ; Χ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL CHI
+1D7A7;1D7A7;1D7A7;03A8;03A8; # (ðž§ðž§; ðž§ðž§; ðž§ðž§; Ψ; Ψ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL PSI
+1D7A8;1D7A8;1D7A8;03A9;03A9; # (ðž¨ðž¨; ðž¨ðž¨; ðž¨ðž¨; Ω; Ω; ) MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL OMEGA
+1D7A9;1D7A9;1D7A9;2207;2207; # (ðž©ðž©; ðž©ðž©; ðž©ðž©; ∇; ∇; ) MATHEMATICAL SANS-SERIF BOLD ITALIC NABLA
+1D7AA;1D7AA;1D7AA;03B1;03B1; # (ðžªðžª; ðžªðžª; ðžªðžª; α; α; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL ALPHA
+1D7AB;1D7AB;1D7AB;03B2;03B2; # (ðž«ðž«; ðž«ðž«; ðž«ðž«; β; β; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL BETA
+1D7AC;1D7AC;1D7AC;03B3;03B3; # (ðž¬ðž¬; ðž¬ðž¬; ðž¬ðž¬; γ; γ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL GAMMA
+1D7AD;1D7AD;1D7AD;03B4;03B4; # (ðž­ðž­; ðž­ðž­; ðž­ðž­; δ; δ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL DELTA
+1D7AE;1D7AE;1D7AE;03B5;03B5; # (ðž®ðž®; ðž®ðž®; ðž®ðž®; ε; ε; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL EPSILON
+1D7AF;1D7AF;1D7AF;03B6;03B6; # (ðž¯ðž¯; ðž¯ðž¯; ðž¯ðž¯; ζ; ζ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL ZETA
+1D7B0;1D7B0;1D7B0;03B7;03B7; # (ðž°ðž°; ðž°ðž°; ðž°ðž°; η; η; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL ETA
+1D7B1;1D7B1;1D7B1;03B8;03B8; # (ðž±ðž±; ðž±ðž±; ðž±ðž±; θ; θ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL THETA
+1D7B2;1D7B2;1D7B2;03B9;03B9; # (ðž²ðž²; ðž²ðž²; ðž²ðž²; ι; ι; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL IOTA
+1D7B3;1D7B3;1D7B3;03BA;03BA; # (ðž³ðž³; ðž³ðž³; ðž³ðž³; κ; κ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL KAPPA
+1D7B4;1D7B4;1D7B4;03BB;03BB; # (ðž´ðž´; ðž´ðž´; ðž´ðž´; λ; λ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL LAMDA
+1D7B5;1D7B5;1D7B5;03BC;03BC; # (ðžµðžµ; ðžµðžµ; ðžµðžµ; μ; μ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL MU
+1D7B6;1D7B6;1D7B6;03BD;03BD; # (ðž¶ðž¶; ðž¶ðž¶; ðž¶ðž¶; ν; ν; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL NU
+1D7B7;1D7B7;1D7B7;03BE;03BE; # (ðž·ðž·; ðž·ðž·; ðž·ðž·; ξ; ξ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL XI
+1D7B8;1D7B8;1D7B8;03BF;03BF; # (ðž¸ðž¸; ðž¸ðž¸; ðž¸ðž¸; ο; ο; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL OMICRON
+1D7B9;1D7B9;1D7B9;03C0;03C0; # (ðž¹ðž¹; ðž¹ðž¹; ðž¹ðž¹; Ï€; Ï€; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL PI
+1D7BA;1D7BA;1D7BA;03C1;03C1; # (ðžºðžº; ðžºðžº; ðžºðžº; Ï; Ï; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL RHO
+1D7BB;1D7BB;1D7BB;03C2;03C2; # (ðž»ðž»; ðž»ðž»; ðž»ðž»; Ï‚; Ï‚; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL FINAL SIGMA
+1D7BC;1D7BC;1D7BC;03C3;03C3; # (ðž¼ðž¼; ðž¼ðž¼; ðž¼ðž¼; σ; σ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL SIGMA
+1D7BD;1D7BD;1D7BD;03C4;03C4; # (ðž½ðž½; ðž½ðž½; ðž½ðž½; Ï„; Ï„; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL TAU
+1D7BE;1D7BE;1D7BE;03C5;03C5; # (ðž¾ðž¾; ðž¾ðž¾; ðž¾ðž¾; Ï…; Ï…; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL UPSILON
+1D7BF;1D7BF;1D7BF;03C6;03C6; # (ðž¿ðž¿; ðž¿ðž¿; ðž¿ðž¿; φ; φ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL PHI
+1D7C0;1D7C0;1D7C0;03C7;03C7; # (ðŸ€ðŸ€; ðŸ€ðŸ€; ðŸ€ðŸ€; χ; χ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL CHI
+1D7C1;1D7C1;1D7C1;03C8;03C8; # (ðŸðŸ; ðŸðŸ; ðŸðŸ; ψ; ψ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL PSI
+1D7C2;1D7C2;1D7C2;03C9;03C9; # (ðŸ‚ðŸ‚; ðŸ‚ðŸ‚; ðŸ‚ðŸ‚; ω; ω; ) MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL OMEGA
+1D7C3;1D7C3;1D7C3;2202;2202; # (ðŸƒðŸƒ; ðŸƒðŸƒ; ðŸƒðŸƒ; ∂; ∂; ) MATHEMATICAL SANS-SERIF BOLD ITALIC PARTIAL DIFFERENTIAL
+1D7C4;1D7C4;1D7C4;03B5;03B5; # (ðŸ„ðŸ„; ðŸ„ðŸ„; ðŸ„ðŸ„; ε; ε; ) MATHEMATICAL SANS-SERIF BOLD ITALIC EPSILON SYMBOL
+1D7C5;1D7C5;1D7C5;03B8;03B8; # (ðŸ…ðŸ…; ðŸ…ðŸ…; ðŸ…ðŸ…; θ; θ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC THETA SYMBOL
+1D7C6;1D7C6;1D7C6;03BA;03BA; # (ðŸ†ðŸ†; ðŸ†ðŸ†; ðŸ†ðŸ†; κ; κ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC KAPPA SYMBOL
+1D7C7;1D7C7;1D7C7;03C6;03C6; # (ðŸ‡ðŸ‡; ðŸ‡ðŸ‡; ðŸ‡ðŸ‡; φ; φ; ) MATHEMATICAL SANS-SERIF BOLD ITALIC PHI SYMBOL
+1D7C8;1D7C8;1D7C8;03C1;03C1; # (ðŸˆðŸˆ; ðŸˆðŸˆ; ðŸˆðŸˆ; Ï; Ï; ) MATHEMATICAL SANS-SERIF BOLD ITALIC RHO SYMBOL
+1D7C9;1D7C9;1D7C9;03C0;03C0; # (ðŸ‰ðŸ‰; ðŸ‰ðŸ‰; ðŸ‰ðŸ‰; Ï€; Ï€; ) MATHEMATICAL SANS-SERIF BOLD ITALIC PI SYMBOL
+1D7CE;1D7CE;1D7CE;0030;0030; # (ðŸŽðŸŽ; ðŸŽðŸŽ; ðŸŽðŸŽ; 0; 0; ) MATHEMATICAL BOLD DIGIT ZERO
+1D7CF;1D7CF;1D7CF;0031;0031; # (ðŸðŸ; ðŸðŸ; ðŸðŸ; 1; 1; ) MATHEMATICAL BOLD DIGIT ONE
+1D7D0;1D7D0;1D7D0;0032;0032; # (ðŸðŸ; ðŸðŸ; ðŸðŸ; 2; 2; ) MATHEMATICAL BOLD DIGIT TWO
+1D7D1;1D7D1;1D7D1;0033;0033; # (ðŸ‘ðŸ‘; ðŸ‘ðŸ‘; ðŸ‘ðŸ‘; 3; 3; ) MATHEMATICAL BOLD DIGIT THREE
+1D7D2;1D7D2;1D7D2;0034;0034; # (ðŸ’ðŸ’; ðŸ’ðŸ’; ðŸ’ðŸ’; 4; 4; ) MATHEMATICAL BOLD DIGIT FOUR
+1D7D3;1D7D3;1D7D3;0035;0035; # (ðŸ“ðŸ“; ðŸ“ðŸ“; ðŸ“ðŸ“; 5; 5; ) MATHEMATICAL BOLD DIGIT FIVE
+1D7D4;1D7D4;1D7D4;0036;0036; # (ðŸ”ðŸ”; ðŸ”ðŸ”; ðŸ”ðŸ”; 6; 6; ) MATHEMATICAL BOLD DIGIT SIX
+1D7D5;1D7D5;1D7D5;0037;0037; # (ðŸ•ðŸ•; ðŸ•ðŸ•; ðŸ•ðŸ•; 7; 7; ) MATHEMATICAL BOLD DIGIT SEVEN
+1D7D6;1D7D6;1D7D6;0038;0038; # (ðŸ–ðŸ–; ðŸ–ðŸ–; ðŸ–ðŸ–; 8; 8; ) MATHEMATICAL BOLD DIGIT EIGHT
+1D7D7;1D7D7;1D7D7;0039;0039; # (ðŸ—ðŸ—; ðŸ—ðŸ—; ðŸ—ðŸ—; 9; 9; ) MATHEMATICAL BOLD DIGIT NINE
+1D7D8;1D7D8;1D7D8;0030;0030; # (ðŸ˜ðŸ˜; ðŸ˜ðŸ˜; ðŸ˜ðŸ˜; 0; 0; ) MATHEMATICAL DOUBLE-STRUCK DIGIT ZERO
+1D7D9;1D7D9;1D7D9;0031;0031; # (ðŸ™ðŸ™; ðŸ™ðŸ™; ðŸ™ðŸ™; 1; 1; ) MATHEMATICAL DOUBLE-STRUCK DIGIT ONE
+1D7DA;1D7DA;1D7DA;0032;0032; # (ðŸšðŸš; ðŸšðŸš; ðŸšðŸš; 2; 2; ) MATHEMATICAL DOUBLE-STRUCK DIGIT TWO
+1D7DB;1D7DB;1D7DB;0033;0033; # (ðŸ›ðŸ›; ðŸ›ðŸ›; ðŸ›ðŸ›; 3; 3; ) MATHEMATICAL DOUBLE-STRUCK DIGIT THREE
+1D7DC;1D7DC;1D7DC;0034;0034; # (ðŸœðŸœ; ðŸœðŸœ; ðŸœðŸœ; 4; 4; ) MATHEMATICAL DOUBLE-STRUCK DIGIT FOUR
+1D7DD;1D7DD;1D7DD;0035;0035; # (ðŸðŸ; ðŸðŸ; ðŸðŸ; 5; 5; ) MATHEMATICAL DOUBLE-STRUCK DIGIT FIVE
+1D7DE;1D7DE;1D7DE;0036;0036; # (ðŸžðŸž; ðŸžðŸž; ðŸžðŸž; 6; 6; ) MATHEMATICAL DOUBLE-STRUCK DIGIT SIX
+1D7DF;1D7DF;1D7DF;0037;0037; # (ðŸŸðŸŸ; ðŸŸðŸŸ; ðŸŸðŸŸ; 7; 7; ) MATHEMATICAL DOUBLE-STRUCK DIGIT SEVEN
+1D7E0;1D7E0;1D7E0;0038;0038; # (ðŸ ðŸ ; ðŸ ðŸ ; ðŸ ðŸ ; 8; 8; ) MATHEMATICAL DOUBLE-STRUCK DIGIT EIGHT
+1D7E1;1D7E1;1D7E1;0039;0039; # (ðŸ¡ðŸ¡; ðŸ¡ðŸ¡; ðŸ¡ðŸ¡; 9; 9; ) MATHEMATICAL DOUBLE-STRUCK DIGIT NINE
+1D7E2;1D7E2;1D7E2;0030;0030; # (ðŸ¢ðŸ¢; ðŸ¢ðŸ¢; ðŸ¢ðŸ¢; 0; 0; ) MATHEMATICAL SANS-SERIF DIGIT ZERO
+1D7E3;1D7E3;1D7E3;0031;0031; # (ðŸ£ðŸ£; ðŸ£ðŸ£; ðŸ£ðŸ£; 1; 1; ) MATHEMATICAL SANS-SERIF DIGIT ONE
+1D7E4;1D7E4;1D7E4;0032;0032; # (ðŸ¤ðŸ¤; ðŸ¤ðŸ¤; ðŸ¤ðŸ¤; 2; 2; ) MATHEMATICAL SANS-SERIF DIGIT TWO
+1D7E5;1D7E5;1D7E5;0033;0033; # (ðŸ¥ðŸ¥; ðŸ¥ðŸ¥; ðŸ¥ðŸ¥; 3; 3; ) MATHEMATICAL SANS-SERIF DIGIT THREE
+1D7E6;1D7E6;1D7E6;0034;0034; # (ðŸ¦ðŸ¦; ðŸ¦ðŸ¦; ðŸ¦ðŸ¦; 4; 4; ) MATHEMATICAL SANS-SERIF DIGIT FOUR
+1D7E7;1D7E7;1D7E7;0035;0035; # (ðŸ§ðŸ§; ðŸ§ðŸ§; ðŸ§ðŸ§; 5; 5; ) MATHEMATICAL SANS-SERIF DIGIT FIVE
+1D7E8;1D7E8;1D7E8;0036;0036; # (ðŸ¨ðŸ¨; ðŸ¨ðŸ¨; ðŸ¨ðŸ¨; 6; 6; ) MATHEMATICAL SANS-SERIF DIGIT SIX
+1D7E9;1D7E9;1D7E9;0037;0037; # (ðŸ©ðŸ©; ðŸ©ðŸ©; ðŸ©ðŸ©; 7; 7; ) MATHEMATICAL SANS-SERIF DIGIT SEVEN
+1D7EA;1D7EA;1D7EA;0038;0038; # (ðŸªðŸª; ðŸªðŸª; ðŸªðŸª; 8; 8; ) MATHEMATICAL SANS-SERIF DIGIT EIGHT
+1D7EB;1D7EB;1D7EB;0039;0039; # (ðŸ«ðŸ«; ðŸ«ðŸ«; ðŸ«ðŸ«; 9; 9; ) MATHEMATICAL SANS-SERIF DIGIT NINE
+1D7EC;1D7EC;1D7EC;0030;0030; # (ðŸ¬ðŸ¬; ðŸ¬ðŸ¬; ðŸ¬ðŸ¬; 0; 0; ) MATHEMATICAL SANS-SERIF BOLD DIGIT ZERO
+1D7ED;1D7ED;1D7ED;0031;0031; # (ðŸ­ðŸ­; ðŸ­ðŸ­; ðŸ­ðŸ­; 1; 1; ) MATHEMATICAL SANS-SERIF BOLD DIGIT ONE
+1D7EE;1D7EE;1D7EE;0032;0032; # (ðŸ®ðŸ®; ðŸ®ðŸ®; ðŸ®ðŸ®; 2; 2; ) MATHEMATICAL SANS-SERIF BOLD DIGIT TWO
+1D7EF;1D7EF;1D7EF;0033;0033; # (ðŸ¯ðŸ¯; ðŸ¯ðŸ¯; ðŸ¯ðŸ¯; 3; 3; ) MATHEMATICAL SANS-SERIF BOLD DIGIT THREE
+1D7F0;1D7F0;1D7F0;0034;0034; # (ðŸ°ðŸ°; ðŸ°ðŸ°; ðŸ°ðŸ°; 4; 4; ) MATHEMATICAL SANS-SERIF BOLD DIGIT FOUR
+1D7F1;1D7F1;1D7F1;0035;0035; # (ðŸ±ðŸ±; ðŸ±ðŸ±; ðŸ±ðŸ±; 5; 5; ) MATHEMATICAL SANS-SERIF BOLD DIGIT FIVE
+1D7F2;1D7F2;1D7F2;0036;0036; # (ðŸ²ðŸ²; ðŸ²ðŸ²; ðŸ²ðŸ²; 6; 6; ) MATHEMATICAL SANS-SERIF BOLD DIGIT SIX
+1D7F3;1D7F3;1D7F3;0037;0037; # (ðŸ³ðŸ³; ðŸ³ðŸ³; ðŸ³ðŸ³; 7; 7; ) MATHEMATICAL SANS-SERIF BOLD DIGIT SEVEN
+1D7F4;1D7F4;1D7F4;0038;0038; # (ðŸ´ðŸ´; ðŸ´ðŸ´; ðŸ´ðŸ´; 8; 8; ) MATHEMATICAL SANS-SERIF BOLD DIGIT EIGHT
+1D7F5;1D7F5;1D7F5;0039;0039; # (ðŸµðŸµ; ðŸµðŸµ; ðŸµðŸµ; 9; 9; ) MATHEMATICAL SANS-SERIF BOLD DIGIT NINE
+1D7F6;1D7F6;1D7F6;0030;0030; # (ðŸ¶ðŸ¶; ðŸ¶ðŸ¶; ðŸ¶ðŸ¶; 0; 0; ) MATHEMATICAL MONOSPACE DIGIT ZERO
+1D7F7;1D7F7;1D7F7;0031;0031; # (ðŸ·ðŸ·; ðŸ·ðŸ·; ðŸ·ðŸ·; 1; 1; ) MATHEMATICAL MONOSPACE DIGIT ONE
+1D7F8;1D7F8;1D7F8;0032;0032; # (ðŸ¸ðŸ¸; ðŸ¸ðŸ¸; ðŸ¸ðŸ¸; 2; 2; ) MATHEMATICAL MONOSPACE DIGIT TWO
+1D7F9;1D7F9;1D7F9;0033;0033; # (ðŸ¹ðŸ¹; ðŸ¹ðŸ¹; ðŸ¹ðŸ¹; 3; 3; ) MATHEMATICAL MONOSPACE DIGIT THREE
+1D7FA;1D7FA;1D7FA;0034;0034; # (ðŸºðŸº; ðŸºðŸº; ðŸºðŸº; 4; 4; ) MATHEMATICAL MONOSPACE DIGIT FOUR
+1D7FB;1D7FB;1D7FB;0035;0035; # (ðŸ»ðŸ»; ðŸ»ðŸ»; ðŸ»ðŸ»; 5; 5; ) MATHEMATICAL MONOSPACE DIGIT FIVE
+1D7FC;1D7FC;1D7FC;0036;0036; # (ðŸ¼ðŸ¼; ðŸ¼ðŸ¼; ðŸ¼ðŸ¼; 6; 6; ) MATHEMATICAL MONOSPACE DIGIT SIX
+1D7FD;1D7FD;1D7FD;0037;0037; # (ðŸ½ðŸ½; ðŸ½ðŸ½; ðŸ½ðŸ½; 7; 7; ) MATHEMATICAL MONOSPACE DIGIT SEVEN
+1D7FE;1D7FE;1D7FE;0038;0038; # (ðŸ¾ðŸ¾; ðŸ¾ðŸ¾; ðŸ¾ðŸ¾; 8; 8; ) MATHEMATICAL MONOSPACE DIGIT EIGHT
+1D7FF;1D7FF;1D7FF;0039;0039; # (ðŸ¿ðŸ¿; ðŸ¿ðŸ¿; ðŸ¿ðŸ¿; 9; 9; ) MATHEMATICAL MONOSPACE DIGIT NINE
+2F800;4E3D;4E3D;4E3D;4E3D; # (丽丽; 丽; 丽; 丽; 丽; ) CJK COMPATIBILITY IDEOGRAPH-2F800
+2F801;4E38;4E38;4E38;4E38; # (ð¯ ð¯ ; 丸; 丸; 丸; 丸; ) CJK COMPATIBILITY IDEOGRAPH-2F801
+2F802;4E41;4E41;4E41;4E41; # (乁乁; ä¹; ä¹; ä¹; ä¹; ) CJK COMPATIBILITY IDEOGRAPH-2F802
+2F803;20122;20122;20122;20122; # (𠄢𠄢; 𠄢𠄢; 𠄢𠄢; 𠄢𠄢; 𠄢𠄢; ) CJK COMPATIBILITY IDEOGRAPH-2F803
+2F804;4F60;4F60;4F60;4F60; # (你你; 你; 你; 你; 你; ) CJK COMPATIBILITY IDEOGRAPH-2F804
+2F805;4FAE;4FAE;4FAE;4FAE; # (侮侮; 侮; 侮; 侮; 侮; ) CJK COMPATIBILITY IDEOGRAPH-2F805
+2F806;4FBB;4FBB;4FBB;4FBB; # (侻侻; 侻; 侻; 侻; 侻; ) CJK COMPATIBILITY IDEOGRAPH-2F806
+2F807;5002;5002;5002;5002; # (倂倂; 倂; 倂; 倂; 倂; ) CJK COMPATIBILITY IDEOGRAPH-2F807
+2F808;507A;507A;507A;507A; # (偺偺; åº; åº; åº; åº; ) CJK COMPATIBILITY IDEOGRAPH-2F808
+2F809;5099;5099;5099;5099; # (備備; 備; 備; 備; 備; ) CJK COMPATIBILITY IDEOGRAPH-2F809
+2F80A;50E7;50E7;50E7;50E7; # (僧僧; 僧; 僧; 僧; 僧; ) CJK COMPATIBILITY IDEOGRAPH-2F80A
+2F80B;50CF;50CF;50CF;50CF; # (像像; åƒ; åƒ; åƒ; åƒ; ) CJK COMPATIBILITY IDEOGRAPH-2F80B
+2F80C;349E;349E;349E;349E; # (㒞㒞; 㒞; 㒞; 㒞; 㒞; ) CJK COMPATIBILITY IDEOGRAPH-2F80C
+2F80D;2063A;2063A;2063A;2063A; # (ð¯ ð¯ ; 𠘺𠘺; 𠘺𠘺; 𠘺𠘺; 𠘺𠘺; ) CJK COMPATIBILITY IDEOGRAPH-2F80D
+2F80E;514D;514D;514D;514D; # (免免; å…; å…; å…; å…; ) CJK COMPATIBILITY IDEOGRAPH-2F80E
+2F80F;5154;5154;5154;5154; # (ð¯ ð¯ ; å…”; å…”; å…”; å…”; ) CJK COMPATIBILITY IDEOGRAPH-2F80F
+2F810;5164;5164;5164;5164; # (ð¯ ð¯ ; å…¤; å…¤; å…¤; å…¤; ) CJK COMPATIBILITY IDEOGRAPH-2F810
+2F811;5177;5177;5177;5177; # (具具; 具; 具; 具; 具; ) CJK COMPATIBILITY IDEOGRAPH-2F811
+2F812;2051C;2051C;2051C;2051C; # (𠔜𠔜; 𠔜𠔜; 𠔜𠔜; 𠔜𠔜; 𠔜𠔜; ) CJK COMPATIBILITY IDEOGRAPH-2F812
+2F813;34B9;34B9;34B9;34B9; # (㒹㒹; 㒹; 㒹; 㒹; 㒹; ) CJK COMPATIBILITY IDEOGRAPH-2F813
+2F814;5167;5167;5167;5167; # (內內; 內; 內; 內; 內; ) CJK COMPATIBILITY IDEOGRAPH-2F814
+2F815;518D;518D;518D;518D; # (再再; å†; å†; å†; å†; ) CJK COMPATIBILITY IDEOGRAPH-2F815
+2F816;2054B;2054B;2054B;2054B; # (𠕋𠕋; 𠕋𠕋; 𠕋𠕋; 𠕋𠕋; 𠕋𠕋; ) CJK COMPATIBILITY IDEOGRAPH-2F816
+2F817;5197;5197;5197;5197; # (冗冗; 冗; 冗; 冗; 冗; ) CJK COMPATIBILITY IDEOGRAPH-2F817
+2F818;51A4;51A4;51A4;51A4; # (冤冤; 冤; 冤; 冤; 冤; ) CJK COMPATIBILITY IDEOGRAPH-2F818
+2F819;4ECC;4ECC;4ECC;4ECC; # (仌仌; 仌; 仌; 仌; 仌; ) CJK COMPATIBILITY IDEOGRAPH-2F819
+2F81A;51AC;51AC;51AC;51AC; # (冬冬; 冬; 冬; 冬; 冬; ) CJK COMPATIBILITY IDEOGRAPH-2F81A
+2F81B;51B5;51B5;51B5;51B5; # (况况; 况; 况; 况; 况; ) CJK COMPATIBILITY IDEOGRAPH-2F81B
+2F81C;291DF;291DF;291DF;291DF; # (𩇟𩇟; 𩇟𩇟; 𩇟𩇟; 𩇟𩇟; 𩇟𩇟; ) CJK COMPATIBILITY IDEOGRAPH-2F81C
+2F81D;51F5;51F5;51F5;51F5; # (ð¯ ð¯ ; 凵; 凵; 凵; 凵; ) CJK COMPATIBILITY IDEOGRAPH-2F81D
+2F81E;5203;5203;5203;5203; # (刃刃; 刃; 刃; 刃; 刃; ) CJK COMPATIBILITY IDEOGRAPH-2F81E
+2F81F;34DF;34DF;34DF;34DF; # (㓟㓟; 㓟; 㓟; 㓟; 㓟; ) CJK COMPATIBILITY IDEOGRAPH-2F81F
+2F820;523B;523B;523B;523B; # (刻刻; 刻; 刻; 刻; 刻; ) CJK COMPATIBILITY IDEOGRAPH-2F820
+2F821;5246;5246;5246;5246; # (剆剆; 剆; 剆; 剆; 剆; ) CJK COMPATIBILITY IDEOGRAPH-2F821
+2F822;5272;5272;5272;5272; # (割割; 割; 割; 割; 割; ) CJK COMPATIBILITY IDEOGRAPH-2F822
+2F823;5277;5277;5277;5277; # (剷剷; 剷; 剷; 剷; 剷; ) CJK COMPATIBILITY IDEOGRAPH-2F823
+2F824;3515;3515;3515;3515; # (㔕㔕; 㔕; 㔕; 㔕; 㔕; ) CJK COMPATIBILITY IDEOGRAPH-2F824
+2F825;52C7;52C7;52C7;52C7; # (勇勇; 勇; 勇; 勇; 勇; ) CJK COMPATIBILITY IDEOGRAPH-2F825
+2F826;52C9;52C9;52C9;52C9; # (勉勉; 勉; 勉; 勉; 勉; ) CJK COMPATIBILITY IDEOGRAPH-2F826
+2F827;52E4;52E4;52E4;52E4; # (勤勤; 勤; 勤; 勤; 勤; ) CJK COMPATIBILITY IDEOGRAPH-2F827
+2F828;52FA;52FA;52FA;52FA; # (勺勺; 勺; 勺; 勺; 勺; ) CJK COMPATIBILITY IDEOGRAPH-2F828
+2F829;5305;5305;5305;5305; # (包包; 包; 包; 包; 包; ) CJK COMPATIBILITY IDEOGRAPH-2F829
+2F82A;5306;5306;5306;5306; # (匆匆; 匆; 匆; 匆; 匆; ) CJK COMPATIBILITY IDEOGRAPH-2F82A
+2F82B;5317;5317;5317;5317; # (北北; 北; 北; 北; 北; ) CJK COMPATIBILITY IDEOGRAPH-2F82B
+2F82C;5349;5349;5349;5349; # (卉卉; å‰; å‰; å‰; å‰; ) CJK COMPATIBILITY IDEOGRAPH-2F82C
+2F82D;5351;5351;5351;5351; # (卑卑; å‘; å‘; å‘; å‘; ) CJK COMPATIBILITY IDEOGRAPH-2F82D
+2F82E;535A;535A;535A;535A; # (博博; åš; åš; åš; åš; ) CJK COMPATIBILITY IDEOGRAPH-2F82E
+2F82F;5373;5373;5373;5373; # (即即; å³; å³; å³; å³; ) CJK COMPATIBILITY IDEOGRAPH-2F82F
+2F830;537D;537D;537D;537D; # (卽卽; å½; å½; å½; å½; ) CJK COMPATIBILITY IDEOGRAPH-2F830
+2F831;537F;537F;537F;537F; # (卿卿; å¿; å¿; å¿; å¿; ) CJK COMPATIBILITY IDEOGRAPH-2F831
+2F832;537F;537F;537F;537F; # (卿卿; å¿; å¿; å¿; å¿; ) CJK COMPATIBILITY IDEOGRAPH-2F832
+2F833;537F;537F;537F;537F; # (卿卿; å¿; å¿; å¿; å¿; ) CJK COMPATIBILITY IDEOGRAPH-2F833
+2F834;20A2C;20A2C;20A2C;20A2C; # (𠨬𠨬; 𠨬𠨬; 𠨬𠨬; 𠨬𠨬; 𠨬𠨬; ) CJK COMPATIBILITY IDEOGRAPH-2F834
+2F835;7070;7070;7070;7070; # (灰灰; ç°; ç°; ç°; ç°; ) CJK COMPATIBILITY IDEOGRAPH-2F835
+2F836;53CA;53CA;53CA;53CA; # (及及; åŠ; åŠ; åŠ; åŠ; ) CJK COMPATIBILITY IDEOGRAPH-2F836
+2F837;53DF;53DF;53DF;53DF; # (叟叟; åŸ; åŸ; åŸ; åŸ; ) CJK COMPATIBILITY IDEOGRAPH-2F837
+2F838;20B63;20B63;20B63;20B63; # (𠭣𠭣; 𠭣𠭣; 𠭣𠭣; 𠭣𠭣; 𠭣𠭣; ) CJK COMPATIBILITY IDEOGRAPH-2F838
+2F839;53EB;53EB;53EB;53EB; # (叫叫; å«; å«; å«; å«; ) CJK COMPATIBILITY IDEOGRAPH-2F839
+2F83A;53F1;53F1;53F1;53F1; # (叱叱; å±; å±; å±; å±; ) CJK COMPATIBILITY IDEOGRAPH-2F83A
+2F83B;5406;5406;5406;5406; # (吆吆; å†; å†; å†; å†; ) CJK COMPATIBILITY IDEOGRAPH-2F83B
+2F83C;549E;549E;549E;549E; # (咞咞; 咞; 咞; 咞; 咞; ) CJK COMPATIBILITY IDEOGRAPH-2F83C
+2F83D;5438;5438;5438;5438; # (吸吸; å¸; å¸; å¸; å¸; ) CJK COMPATIBILITY IDEOGRAPH-2F83D
+2F83E;5448;5448;5448;5448; # (呈呈; 呈; 呈; 呈; 呈; ) CJK COMPATIBILITY IDEOGRAPH-2F83E
+2F83F;5468;5468;5468;5468; # (周周; 周; 周; 周; 周; ) CJK COMPATIBILITY IDEOGRAPH-2F83F
+2F840;54A2;54A2;54A2;54A2; # (咢咢; 咢; 咢; 咢; 咢; ) CJK COMPATIBILITY IDEOGRAPH-2F840
+2F841;54F6;54F6;54F6;54F6; # (ð¯¡ð¯¡; å“¶; å“¶; å“¶; å“¶; ) CJK COMPATIBILITY IDEOGRAPH-2F841
+2F842;5510;5510;5510;5510; # (唐唐; å”; å”; å”; å”; ) CJK COMPATIBILITY IDEOGRAPH-2F842
+2F843;5553;5553;5553;5553; # (啓啓; 啓; 啓; 啓; 啓; ) CJK COMPATIBILITY IDEOGRAPH-2F843
+2F844;5563;5563;5563;5563; # (啣啣; 啣; 啣; 啣; 啣; ) CJK COMPATIBILITY IDEOGRAPH-2F844
+2F845;5584;5584;5584;5584; # (善善; 善; 善; 善; 善; ) CJK COMPATIBILITY IDEOGRAPH-2F845
+2F846;5584;5584;5584;5584; # (善善; 善; 善; 善; 善; ) CJK COMPATIBILITY IDEOGRAPH-2F846
+2F847;5599;5599;5599;5599; # (喙喙; 喙; 喙; 喙; 喙; ) CJK COMPATIBILITY IDEOGRAPH-2F847
+2F848;55AB;55AB;55AB;55AB; # (喫喫; 喫; 喫; 喫; 喫; ) CJK COMPATIBILITY IDEOGRAPH-2F848
+2F849;55B3;55B3;55B3;55B3; # (喳喳; 喳; 喳; 喳; 喳; ) CJK COMPATIBILITY IDEOGRAPH-2F849
+2F84A;55C2;55C2;55C2;55C2; # (嗂嗂; 嗂; 嗂; 嗂; 嗂; ) CJK COMPATIBILITY IDEOGRAPH-2F84A
+2F84B;5716;5716;5716;5716; # (圖圖; 圖; 圖; 圖; 圖; ) CJK COMPATIBILITY IDEOGRAPH-2F84B
+2F84C;5606;5606;5606;5606; # (嘆嘆; 嘆; 嘆; 嘆; 嘆; ) CJK COMPATIBILITY IDEOGRAPH-2F84C
+2F84D;5717;5717;5717;5717; # (ð¯¡ð¯¡; 圗; 圗; 圗; 圗; ) CJK COMPATIBILITY IDEOGRAPH-2F84D
+2F84E;5651;5651;5651;5651; # (噑噑; 噑; 噑; 噑; 噑; ) CJK COMPATIBILITY IDEOGRAPH-2F84E
+2F84F;5674;5674;5674;5674; # (ð¯¡ð¯¡; å™´; å™´; å™´; å™´; ) CJK COMPATIBILITY IDEOGRAPH-2F84F
+2F850;5207;5207;5207;5207; # (ð¯¡ð¯¡; 切; 切; 切; 切; ) CJK COMPATIBILITY IDEOGRAPH-2F850
+2F851;58EE;58EE;58EE;58EE; # (壮壮; 壮; 壮; 壮; 壮; ) CJK COMPATIBILITY IDEOGRAPH-2F851
+2F852;57CE;57CE;57CE;57CE; # (城城; 城; 城; 城; 城; ) CJK COMPATIBILITY IDEOGRAPH-2F852
+2F853;57F4;57F4;57F4;57F4; # (埴埴; 埴; 埴; 埴; 埴; ) CJK COMPATIBILITY IDEOGRAPH-2F853
+2F854;580D;580D;580D;580D; # (堍堍; å ; å ; å ; å ; ) CJK COMPATIBILITY IDEOGRAPH-2F854
+2F855;578B;578B;578B;578B; # (型型; 型; 型; 型; 型; ) CJK COMPATIBILITY IDEOGRAPH-2F855
+2F856;5832;5832;5832;5832; # (堲堲; 堲; 堲; 堲; 堲; ) CJK COMPATIBILITY IDEOGRAPH-2F856
+2F857;5831;5831;5831;5831; # (報報; 報; 報; 報; 報; ) CJK COMPATIBILITY IDEOGRAPH-2F857
+2F858;58AC;58AC;58AC;58AC; # (墬墬; 墬; 墬; 墬; 墬; ) CJK COMPATIBILITY IDEOGRAPH-2F858
+2F859;214E4;214E4;214E4;214E4; # (𡓤𡓤; 𡓤𡓤; 𡓤𡓤; 𡓤𡓤; 𡓤𡓤; ) CJK COMPATIBILITY IDEOGRAPH-2F859
+2F85A;58F2;58F2;58F2;58F2; # (売売; 売; 売; 売; 売; ) CJK COMPATIBILITY IDEOGRAPH-2F85A
+2F85B;58F7;58F7;58F7;58F7; # (壷壷; 壷; 壷; 壷; 壷; ) CJK COMPATIBILITY IDEOGRAPH-2F85B
+2F85C;5906;5906;5906;5906; # (夆夆; 夆; 夆; 夆; 夆; ) CJK COMPATIBILITY IDEOGRAPH-2F85C
+2F85D;591A;591A;591A;591A; # (ð¯¡ð¯¡; 多; 多; 多; 多; ) CJK COMPATIBILITY IDEOGRAPH-2F85D
+2F85E;5922;5922;5922;5922; # (夢夢; 夢; 夢; 夢; 夢; ) CJK COMPATIBILITY IDEOGRAPH-2F85E
+2F85F;5962;5962;5962;5962; # (奢奢; 奢; 奢; 奢; 奢; ) CJK COMPATIBILITY IDEOGRAPH-2F85F
+2F860;216A8;216A8;216A8;216A8; # (𡚨𡚨; 𡚨𡚨; 𡚨𡚨; 𡚨𡚨; 𡚨𡚨; ) CJK COMPATIBILITY IDEOGRAPH-2F860
+2F861;216EA;216EA;216EA;216EA; # (𡛪𡛪; 𡛪𡛪; 𡛪𡛪; 𡛪𡛪; 𡛪𡛪; ) CJK COMPATIBILITY IDEOGRAPH-2F861
+2F862;59EC;59EC;59EC;59EC; # (姬姬; 姬; 姬; 姬; 姬; ) CJK COMPATIBILITY IDEOGRAPH-2F862
+2F863;5A1B;5A1B;5A1B;5A1B; # (娛娛; 娛; 娛; 娛; 娛; ) CJK COMPATIBILITY IDEOGRAPH-2F863
+2F864;5A27;5A27;5A27;5A27; # (娧娧; 娧; 娧; 娧; 娧; ) CJK COMPATIBILITY IDEOGRAPH-2F864
+2F865;59D8;59D8;59D8;59D8; # (姘姘; 姘; 姘; 姘; 姘; ) CJK COMPATIBILITY IDEOGRAPH-2F865
+2F866;5A66;5A66;5A66;5A66; # (婦婦; 婦; 婦; 婦; 婦; ) CJK COMPATIBILITY IDEOGRAPH-2F866
+2F867;36EE;36EE;36EE;36EE; # (㛮㛮; 㛮; 㛮; 㛮; 㛮; ) CJK COMPATIBILITY IDEOGRAPH-2F867
+2F868;36FC;36FC;36FC;36FC; # (㛼㛼; 㛼; 㛼; 㛼; 㛼; ) CJK COMPATIBILITY IDEOGRAPH-2F868
+2F869;5B08;5B08;5B08;5B08; # (嬈嬈; 嬈; 嬈; 嬈; 嬈; ) CJK COMPATIBILITY IDEOGRAPH-2F869
+2F86A;5B3E;5B3E;5B3E;5B3E; # (嬾嬾; 嬾; 嬾; 嬾; 嬾; ) CJK COMPATIBILITY IDEOGRAPH-2F86A
+2F86B;5B3E;5B3E;5B3E;5B3E; # (嬾嬾; 嬾; 嬾; 嬾; 嬾; ) CJK COMPATIBILITY IDEOGRAPH-2F86B
+2F86C;219C8;219C8;219C8;219C8; # (𡧈𡧈; 𡧈𡧈; 𡧈𡧈; 𡧈𡧈; 𡧈𡧈; ) CJK COMPATIBILITY IDEOGRAPH-2F86C
+2F86D;5BC3;5BC3;5BC3;5BC3; # (寃寃; 寃; 寃; 寃; 寃; ) CJK COMPATIBILITY IDEOGRAPH-2F86D
+2F86E;5BD8;5BD8;5BD8;5BD8; # (寘寘; 寘; 寘; 寘; 寘; ) CJK COMPATIBILITY IDEOGRAPH-2F86E
+2F86F;5BE7;5BE7;5BE7;5BE7; # (寧寧; 寧; 寧; 寧; 寧; ) CJK COMPATIBILITY IDEOGRAPH-2F86F
+2F870;5BF3;5BF3;5BF3;5BF3; # (寳寳; 寳; 寳; 寳; 寳; ) CJK COMPATIBILITY IDEOGRAPH-2F870
+2F871;21B18;21B18;21B18;21B18; # (𡬘𡬘; 𡬘𡬘; 𡬘𡬘; 𡬘𡬘; 𡬘𡬘; ) CJK COMPATIBILITY IDEOGRAPH-2F871
+2F872;5BFF;5BFF;5BFF;5BFF; # (寿寿; 寿; 寿; 寿; 寿; ) CJK COMPATIBILITY IDEOGRAPH-2F872
+2F873;5C06;5C06;5C06;5C06; # (将将; 将; 将; 将; 将; ) CJK COMPATIBILITY IDEOGRAPH-2F873
+2F874;5F53;5F53;5F53;5F53; # (当当; 当; 当; 当; 当; ) CJK COMPATIBILITY IDEOGRAPH-2F874
+2F875;5C22;5C22;5C22;5C22; # (尢尢; 尢; 尢; 尢; 尢; ) CJK COMPATIBILITY IDEOGRAPH-2F875
+2F876;3781;3781;3781;3781; # (㞁㞁; ãž; ãž; ãž; ãž; ) CJK COMPATIBILITY IDEOGRAPH-2F876
+2F877;5C60;5C60;5C60;5C60; # (屠屠; 屠; 屠; 屠; 屠; ) CJK COMPATIBILITY IDEOGRAPH-2F877
+2F878;5C6E;5C6E;5C6E;5C6E; # (屮屮; 屮; 屮; 屮; 屮; ) CJK COMPATIBILITY IDEOGRAPH-2F878
+2F879;5CC0;5CC0;5CC0;5CC0; # (峀峀; 峀; 峀; 峀; 峀; ) CJK COMPATIBILITY IDEOGRAPH-2F879
+2F87A;5C8D;5C8D;5C8D;5C8D; # (岍岍; å²; å²; å²; å²; ) CJK COMPATIBILITY IDEOGRAPH-2F87A
+2F87B;21DE4;21DE4;21DE4;21DE4; # (𡷤𡷤; 𡷤𡷤; 𡷤𡷤; 𡷤𡷤; 𡷤𡷤; ) CJK COMPATIBILITY IDEOGRAPH-2F87B
+2F87C;5D43;5D43;5D43;5D43; # (嵃嵃; 嵃; 嵃; 嵃; 嵃; ) CJK COMPATIBILITY IDEOGRAPH-2F87C
+2F87D;21DE6;21DE6;21DE6;21DE6; # (𡷦𡷦; 𡷦𡷦; 𡷦𡷦; 𡷦𡷦; 𡷦𡷦; ) CJK COMPATIBILITY IDEOGRAPH-2F87D
+2F87E;5D6E;5D6E;5D6E;5D6E; # (嵮嵮; 嵮; 嵮; 嵮; 嵮; ) CJK COMPATIBILITY IDEOGRAPH-2F87E
+2F87F;5D6B;5D6B;5D6B;5D6B; # (嵫嵫; 嵫; 嵫; 嵫; 嵫; ) CJK COMPATIBILITY IDEOGRAPH-2F87F
+2F880;5D7C;5D7C;5D7C;5D7C; # (嵼嵼; 嵼; 嵼; 嵼; 嵼; ) CJK COMPATIBILITY IDEOGRAPH-2F880
+2F881;5DE1;5DE1;5DE1;5DE1; # (ð¯¢ð¯¢; å·¡; å·¡; å·¡; å·¡; ) CJK COMPATIBILITY IDEOGRAPH-2F881
+2F882;5DE2;5DE2;5DE2;5DE2; # (巢巢; 巢; 巢; 巢; 巢; ) CJK COMPATIBILITY IDEOGRAPH-2F882
+2F883;382F;382F;382F;382F; # (㠯㠯; 㠯; 㠯; 㠯; 㠯; ) CJK COMPATIBILITY IDEOGRAPH-2F883
+2F884;5DFD;5DFD;5DFD;5DFD; # (巽巽; 巽; 巽; 巽; 巽; ) CJK COMPATIBILITY IDEOGRAPH-2F884
+2F885;5E28;5E28;5E28;5E28; # (帨帨; 帨; 帨; 帨; 帨; ) CJK COMPATIBILITY IDEOGRAPH-2F885
+2F886;5E3D;5E3D;5E3D;5E3D; # (帽帽; 帽; 帽; 帽; 帽; ) CJK COMPATIBILITY IDEOGRAPH-2F886
+2F887;5E69;5E69;5E69;5E69; # (幩幩; 幩; 幩; 幩; 幩; ) CJK COMPATIBILITY IDEOGRAPH-2F887
+2F888;3862;3862;3862;3862; # (㡢㡢; 㡢; 㡢; 㡢; 㡢; ) CJK COMPATIBILITY IDEOGRAPH-2F888
+2F889;22183;22183;22183;22183; # (𢆃𢆃; 𢆃𢆃; 𢆃𢆃; 𢆃𢆃; 𢆃𢆃; ) CJK COMPATIBILITY IDEOGRAPH-2F889
+2F88A;387C;387C;387C;387C; # (㡼㡼; 㡼; 㡼; 㡼; 㡼; ) CJK COMPATIBILITY IDEOGRAPH-2F88A
+2F88B;5EB0;5EB0;5EB0;5EB0; # (庰庰; 庰; 庰; 庰; 庰; ) CJK COMPATIBILITY IDEOGRAPH-2F88B
+2F88C;5EB3;5EB3;5EB3;5EB3; # (庳庳; 庳; 庳; 庳; 庳; ) CJK COMPATIBILITY IDEOGRAPH-2F88C
+2F88D;5EB6;5EB6;5EB6;5EB6; # (ð¯¢ð¯¢; 庶; 庶; 庶; 庶; ) CJK COMPATIBILITY IDEOGRAPH-2F88D
+2F88E;5ECA;5ECA;5ECA;5ECA; # (廊廊; 廊; 廊; 廊; 廊; ) CJK COMPATIBILITY IDEOGRAPH-2F88E
+2F88F;2A392;2A392;2A392;2A392; # (ð¯¢ð¯¢; 𪎒𪎒; 𪎒𪎒; 𪎒𪎒; 𪎒𪎒; ) CJK COMPATIBILITY IDEOGRAPH-2F88F
+2F890;5EFE;5EFE;5EFE;5EFE; # (ð¯¢ð¯¢; 廾; 廾; 廾; 廾; ) CJK COMPATIBILITY IDEOGRAPH-2F890
+2F891;22331;22331;22331;22331; # (𢌱𢌱; 𢌱𢌱; 𢌱𢌱; 𢌱𢌱; 𢌱𢌱; ) CJK COMPATIBILITY IDEOGRAPH-2F891
+2F892;22331;22331;22331;22331; # (𢌱𢌱; 𢌱𢌱; 𢌱𢌱; 𢌱𢌱; 𢌱𢌱; ) CJK COMPATIBILITY IDEOGRAPH-2F892
+2F893;8201;8201;8201;8201; # (舁舁; èˆ; èˆ; èˆ; èˆ; ) CJK COMPATIBILITY IDEOGRAPH-2F893
+2F894;5F22;5F22;5F22;5F22; # (弢弢; 弢; 弢; 弢; 弢; ) CJK COMPATIBILITY IDEOGRAPH-2F894
+2F895;5F22;5F22;5F22;5F22; # (弢弢; 弢; 弢; 弢; 弢; ) CJK COMPATIBILITY IDEOGRAPH-2F895
+2F896;38C7;38C7;38C7;38C7; # (㣇㣇; 㣇; 㣇; 㣇; 㣇; ) CJK COMPATIBILITY IDEOGRAPH-2F896
+2F897;232B8;232B8;232B8;232B8; # (𣊸𣊸; 𣊸𣊸; 𣊸𣊸; 𣊸𣊸; 𣊸𣊸; ) CJK COMPATIBILITY IDEOGRAPH-2F897
+2F898;261DA;261DA;261DA;261DA; # (𦇚𦇚; 𦇚𦇚; 𦇚𦇚; 𦇚𦇚; 𦇚𦇚; ) CJK COMPATIBILITY IDEOGRAPH-2F898
+2F899;5F62;5F62;5F62;5F62; # (形形; 形; 形; 形; 形; ) CJK COMPATIBILITY IDEOGRAPH-2F899
+2F89A;5F6B;5F6B;5F6B;5F6B; # (彫彫; 彫; 彫; 彫; 彫; ) CJK COMPATIBILITY IDEOGRAPH-2F89A
+2F89B;38E3;38E3;38E3;38E3; # (㣣㣣; 㣣; 㣣; 㣣; 㣣; ) CJK COMPATIBILITY IDEOGRAPH-2F89B
+2F89C;5F9A;5F9A;5F9A;5F9A; # (徚徚; 徚; 徚; 徚; 徚; ) CJK COMPATIBILITY IDEOGRAPH-2F89C
+2F89D;5FCD;5FCD;5FCD;5FCD; # (ð¯¢ð¯¢; å¿; å¿; å¿; å¿; ) CJK COMPATIBILITY IDEOGRAPH-2F89D
+2F89E;5FD7;5FD7;5FD7;5FD7; # (志志; 志; 志; 志; 志; ) CJK COMPATIBILITY IDEOGRAPH-2F89E
+2F89F;5FF9;5FF9;5FF9;5FF9; # (忹忹; 忹; 忹; 忹; 忹; ) CJK COMPATIBILITY IDEOGRAPH-2F89F
+2F8A0;6081;6081;6081;6081; # (悁悁; æ‚; æ‚; æ‚; æ‚; ) CJK COMPATIBILITY IDEOGRAPH-2F8A0
+2F8A1;393A;393A;393A;393A; # (㤺㤺; 㤺; 㤺; 㤺; 㤺; ) CJK COMPATIBILITY IDEOGRAPH-2F8A1
+2F8A2;391C;391C;391C;391C; # (㤜㤜; 㤜; 㤜; 㤜; 㤜; ) CJK COMPATIBILITY IDEOGRAPH-2F8A2
+2F8A3;6094;6094;6094;6094; # (悔悔; 悔; 悔; 悔; 悔; ) CJK COMPATIBILITY IDEOGRAPH-2F8A3
+2F8A4;226D4;226D4;226D4;226D4; # (𢛔𢛔; 𢛔𢛔; 𢛔𢛔; 𢛔𢛔; 𢛔𢛔; ) CJK COMPATIBILITY IDEOGRAPH-2F8A4
+2F8A5;60C7;60C7;60C7;60C7; # (惇惇; 惇; 惇; 惇; 惇; ) CJK COMPATIBILITY IDEOGRAPH-2F8A5
+2F8A6;6148;6148;6148;6148; # (慈慈; 慈; 慈; 慈; 慈; ) CJK COMPATIBILITY IDEOGRAPH-2F8A6
+2F8A7;614C;614C;614C;614C; # (慌慌; 慌; 慌; 慌; 慌; ) CJK COMPATIBILITY IDEOGRAPH-2F8A7
+2F8A8;614E;614E;614E;614E; # (慎慎; 慎; 慎; 慎; 慎; ) CJK COMPATIBILITY IDEOGRAPH-2F8A8
+2F8A9;614C;614C;614C;614C; # (慌慌; 慌; 慌; 慌; 慌; ) CJK COMPATIBILITY IDEOGRAPH-2F8A9
+2F8AA;617A;617A;617A;617A; # (慺慺; 慺; 慺; 慺; 慺; ) CJK COMPATIBILITY IDEOGRAPH-2F8AA
+2F8AB;618E;618E;618E;618E; # (憎憎; 憎; 憎; 憎; 憎; ) CJK COMPATIBILITY IDEOGRAPH-2F8AB
+2F8AC;61B2;61B2;61B2;61B2; # (憲憲; 憲; 憲; 憲; 憲; ) CJK COMPATIBILITY IDEOGRAPH-2F8AC
+2F8AD;61A4;61A4;61A4;61A4; # (憤憤; 憤; 憤; 憤; 憤; ) CJK COMPATIBILITY IDEOGRAPH-2F8AD
+2F8AE;61AF;61AF;61AF;61AF; # (憯憯; 憯; 憯; 憯; 憯; ) CJK COMPATIBILITY IDEOGRAPH-2F8AE
+2F8AF;61DE;61DE;61DE;61DE; # (懞懞; 懞; 懞; 懞; 懞; ) CJK COMPATIBILITY IDEOGRAPH-2F8AF
+2F8B0;61F2;61F2;61F2;61F2; # (懲懲; 懲; 懲; 懲; 懲; ) CJK COMPATIBILITY IDEOGRAPH-2F8B0
+2F8B1;61F6;61F6;61F6;61F6; # (懶懶; 懶; 懶; 懶; 懶; ) CJK COMPATIBILITY IDEOGRAPH-2F8B1
+2F8B2;6210;6210;6210;6210; # (成成; æˆ; æˆ; æˆ; æˆ; ) CJK COMPATIBILITY IDEOGRAPH-2F8B2
+2F8B3;621B;621B;621B;621B; # (戛戛; 戛; 戛; 戛; 戛; ) CJK COMPATIBILITY IDEOGRAPH-2F8B3
+2F8B4;625D;625D;625D;625D; # (扝扝; æ‰; æ‰; æ‰; æ‰; ) CJK COMPATIBILITY IDEOGRAPH-2F8B4
+2F8B5;62B1;62B1;62B1;62B1; # (抱抱; 抱; 抱; 抱; 抱; ) CJK COMPATIBILITY IDEOGRAPH-2F8B5
+2F8B6;62D4;62D4;62D4;62D4; # (拔拔; 拔; 拔; 拔; 拔; ) CJK COMPATIBILITY IDEOGRAPH-2F8B6
+2F8B7;6350;6350;6350;6350; # (捐捐; æ; æ; æ; æ; ) CJK COMPATIBILITY IDEOGRAPH-2F8B7
+2F8B8;22B0C;22B0C;22B0C;22B0C; # (𢬌𢬌; 𢬌𢬌; 𢬌𢬌; 𢬌𢬌; 𢬌𢬌; ) CJK COMPATIBILITY IDEOGRAPH-2F8B8
+2F8B9;633D;633D;633D;633D; # (挽挽; 挽; 挽; 挽; 挽; ) CJK COMPATIBILITY IDEOGRAPH-2F8B9
+2F8BA;62FC;62FC;62FC;62FC; # (拼拼; 拼; 拼; 拼; 拼; ) CJK COMPATIBILITY IDEOGRAPH-2F8BA
+2F8BB;6368;6368;6368;6368; # (捨捨; æ¨; æ¨; æ¨; æ¨; ) CJK COMPATIBILITY IDEOGRAPH-2F8BB
+2F8BC;6383;6383;6383;6383; # (掃掃; 掃; 掃; 掃; 掃; ) CJK COMPATIBILITY IDEOGRAPH-2F8BC
+2F8BD;63E4;63E4;63E4;63E4; # (揤揤; æ¤; æ¤; æ¤; æ¤; ) CJK COMPATIBILITY IDEOGRAPH-2F8BD
+2F8BE;22BF1;22BF1;22BF1;22BF1; # (𢯱𢯱; 𢯱𢯱; 𢯱𢯱; 𢯱𢯱; 𢯱𢯱; ) CJK COMPATIBILITY IDEOGRAPH-2F8BE
+2F8BF;6422;6422;6422;6422; # (搢搢; æ¢; æ¢; æ¢; æ¢; ) CJK COMPATIBILITY IDEOGRAPH-2F8BF
+2F8C0;63C5;63C5;63C5;63C5; # (揅揅; æ…; æ…; æ…; æ…; ) CJK COMPATIBILITY IDEOGRAPH-2F8C0
+2F8C1;63A9;63A9;63A9;63A9; # (ð¯£ð¯£; 掩; 掩; 掩; 掩; ) CJK COMPATIBILITY IDEOGRAPH-2F8C1
+2F8C2;3A2E;3A2E;3A2E;3A2E; # (㨮㨮; 㨮; 㨮; 㨮; 㨮; ) CJK COMPATIBILITY IDEOGRAPH-2F8C2
+2F8C3;6469;6469;6469;6469; # (摩摩; 摩; 摩; 摩; 摩; ) CJK COMPATIBILITY IDEOGRAPH-2F8C3
+2F8C4;647E;647E;647E;647E; # (摾摾; 摾; 摾; 摾; 摾; ) CJK COMPATIBILITY IDEOGRAPH-2F8C4
+2F8C5;649D;649D;649D;649D; # (撝撝; æ’; æ’; æ’; æ’; ) CJK COMPATIBILITY IDEOGRAPH-2F8C5
+2F8C6;6477;6477;6477;6477; # (摷摷; 摷; 摷; 摷; 摷; ) CJK COMPATIBILITY IDEOGRAPH-2F8C6
+2F8C7;3A6C;3A6C;3A6C;3A6C; # (㩬㩬; 㩬; 㩬; 㩬; 㩬; ) CJK COMPATIBILITY IDEOGRAPH-2F8C7
+2F8C8;654F;654F;654F;654F; # (敏敏; æ•; æ•; æ•; æ•; ) CJK COMPATIBILITY IDEOGRAPH-2F8C8
+2F8C9;656C;656C;656C;656C; # (敬敬; 敬; 敬; 敬; 敬; ) CJK COMPATIBILITY IDEOGRAPH-2F8C9
+2F8CA;2300A;2300A;2300A;2300A; # (𣀊𣀊; 𣀊𣀊; 𣀊𣀊; 𣀊𣀊; 𣀊𣀊; ) CJK COMPATIBILITY IDEOGRAPH-2F8CA
+2F8CB;65E3;65E3;65E3;65E3; # (旣旣; 旣; 旣; 旣; 旣; ) CJK COMPATIBILITY IDEOGRAPH-2F8CB
+2F8CC;66F8;66F8;66F8;66F8; # (書書; 書; 書; 書; 書; ) CJK COMPATIBILITY IDEOGRAPH-2F8CC
+2F8CD;6649;6649;6649;6649; # (ð¯£ð¯£; 晉; 晉; 晉; 晉; ) CJK COMPATIBILITY IDEOGRAPH-2F8CD
+2F8CE;3B19;3B19;3B19;3B19; # (㬙㬙; 㬙; 㬙; 㬙; 㬙; ) CJK COMPATIBILITY IDEOGRAPH-2F8CE
+2F8CF;6691;6691;6691;6691; # (ð¯£ð¯£; æš‘; æš‘; æš‘; æš‘; ) CJK COMPATIBILITY IDEOGRAPH-2F8CF
+2F8D0;3B08;3B08;3B08;3B08; # (ð¯£ð¯£; 㬈; 㬈; 㬈; 㬈; ) CJK COMPATIBILITY IDEOGRAPH-2F8D0
+2F8D1;3AE4;3AE4;3AE4;3AE4; # (㫤㫤; 㫤; 㫤; 㫤; 㫤; ) CJK COMPATIBILITY IDEOGRAPH-2F8D1
+2F8D2;5192;5192;5192;5192; # (冒冒; 冒; 冒; 冒; 冒; ) CJK COMPATIBILITY IDEOGRAPH-2F8D2
+2F8D3;5195;5195;5195;5195; # (冕冕; 冕; 冕; 冕; 冕; ) CJK COMPATIBILITY IDEOGRAPH-2F8D3
+2F8D4;6700;6700;6700;6700; # (最最; 最; 最; 最; 最; ) CJK COMPATIBILITY IDEOGRAPH-2F8D4
+2F8D5;669C;669C;669C;669C; # (暜暜; 暜; 暜; 暜; 暜; ) CJK COMPATIBILITY IDEOGRAPH-2F8D5
+2F8D6;80AD;80AD;80AD;80AD; # (肭肭; 肭; 肭; 肭; 肭; ) CJK COMPATIBILITY IDEOGRAPH-2F8D6
+2F8D7;43D9;43D9;43D9;43D9; # (䏙䏙; ä™; ä™; ä™; ä™; ) CJK COMPATIBILITY IDEOGRAPH-2F8D7
+2F8D8;6717;6717;6717;6717; # (朗朗; 朗; 朗; 朗; 朗; ) CJK COMPATIBILITY IDEOGRAPH-2F8D8
+2F8D9;671B;671B;671B;671B; # (望望; 望; 望; 望; 望; ) CJK COMPATIBILITY IDEOGRAPH-2F8D9
+2F8DA;6721;6721;6721;6721; # (朡朡; 朡; 朡; 朡; 朡; ) CJK COMPATIBILITY IDEOGRAPH-2F8DA
+2F8DB;675E;675E;675E;675E; # (杞杞; æž; æž; æž; æž; ) CJK COMPATIBILITY IDEOGRAPH-2F8DB
+2F8DC;6753;6753;6753;6753; # (杓杓; æ“; æ“; æ“; æ“; ) CJK COMPATIBILITY IDEOGRAPH-2F8DC
+2F8DD;233C3;233C3;233C3;233C3; # (ð¯£ð¯£; ð£ƒð£ƒ; ð£ƒð£ƒ; ð£ƒð£ƒ; ð£ƒð£ƒ; ) CJK COMPATIBILITY IDEOGRAPH-2F8DD
+2F8DE;3B49;3B49;3B49;3B49; # (㭉㭉; 㭉; 㭉; 㭉; 㭉; ) CJK COMPATIBILITY IDEOGRAPH-2F8DE
+2F8DF;67FA;67FA;67FA;67FA; # (柺柺; 柺; 柺; 柺; 柺; ) CJK COMPATIBILITY IDEOGRAPH-2F8DF
+2F8E0;6785;6785;6785;6785; # (枅枅; 枅; 枅; 枅; 枅; ) CJK COMPATIBILITY IDEOGRAPH-2F8E0
+2F8E1;6852;6852;6852;6852; # (桒桒; 桒; 桒; 桒; 桒; ) CJK COMPATIBILITY IDEOGRAPH-2F8E1
+2F8E2;6885;6885;6885;6885; # (梅梅; 梅; 梅; 梅; 梅; ) CJK COMPATIBILITY IDEOGRAPH-2F8E2
+2F8E3;2346D;2346D;2346D;2346D; # (𣑭𣑭; 𣑭𣑭; 𣑭𣑭; 𣑭𣑭; 𣑭𣑭; ) CJK COMPATIBILITY IDEOGRAPH-2F8E3
+2F8E4;688E;688E;688E;688E; # (梎梎; 梎; 梎; 梎; 梎; ) CJK COMPATIBILITY IDEOGRAPH-2F8E4
+2F8E5;681F;681F;681F;681F; # (栟栟; 栟; 栟; 栟; 栟; ) CJK COMPATIBILITY IDEOGRAPH-2F8E5
+2F8E6;6914;6914;6914;6914; # (椔椔; 椔; 椔; 椔; 椔; ) CJK COMPATIBILITY IDEOGRAPH-2F8E6
+2F8E7;3B9D;3B9D;3B9D;3B9D; # (㮝㮝; ã®; ã®; ã®; ã®; ) CJK COMPATIBILITY IDEOGRAPH-2F8E7
+2F8E8;6942;6942;6942;6942; # (楂楂; 楂; 楂; 楂; 楂; ) CJK COMPATIBILITY IDEOGRAPH-2F8E8
+2F8E9;69A3;69A3;69A3;69A3; # (榣榣; 榣; 榣; 榣; 榣; ) CJK COMPATIBILITY IDEOGRAPH-2F8E9
+2F8EA;69EA;69EA;69EA;69EA; # (槪槪; 槪; 槪; 槪; 槪; ) CJK COMPATIBILITY IDEOGRAPH-2F8EA
+2F8EB;6AA8;6AA8;6AA8;6AA8; # (檨檨; 檨; 檨; 檨; 檨; ) CJK COMPATIBILITY IDEOGRAPH-2F8EB
+2F8EC;236A3;236A3;236A3;236A3; # (𣚣𣚣; 𣚣𣚣; 𣚣𣚣; 𣚣𣚣; 𣚣𣚣; ) CJK COMPATIBILITY IDEOGRAPH-2F8EC
+2F8ED;6ADB;6ADB;6ADB;6ADB; # (櫛櫛; 櫛; 櫛; 櫛; 櫛; ) CJK COMPATIBILITY IDEOGRAPH-2F8ED
+2F8EE;3C18;3C18;3C18;3C18; # (㰘㰘; 㰘; 㰘; 㰘; 㰘; ) CJK COMPATIBILITY IDEOGRAPH-2F8EE
+2F8EF;6B21;6B21;6B21;6B21; # (次次; 次; 次; 次; 次; ) CJK COMPATIBILITY IDEOGRAPH-2F8EF
+2F8F0;238A7;238A7;238A7;238A7; # (𣢧𣢧; 𣢧𣢧; 𣢧𣢧; 𣢧𣢧; 𣢧𣢧; ) CJK COMPATIBILITY IDEOGRAPH-2F8F0
+2F8F1;6B54;6B54;6B54;6B54; # (歔歔; 歔; 歔; 歔; 歔; ) CJK COMPATIBILITY IDEOGRAPH-2F8F1
+2F8F2;3C4E;3C4E;3C4E;3C4E; # (㱎㱎; 㱎; 㱎; 㱎; 㱎; ) CJK COMPATIBILITY IDEOGRAPH-2F8F2
+2F8F3;6B72;6B72;6B72;6B72; # (歲歲; 歲; 歲; 歲; 歲; ) CJK COMPATIBILITY IDEOGRAPH-2F8F3
+2F8F4;6B9F;6B9F;6B9F;6B9F; # (殟殟; 殟; 殟; 殟; 殟; ) CJK COMPATIBILITY IDEOGRAPH-2F8F4
+2F8F5;6BBA;6BBA;6BBA;6BBA; # (殺殺; 殺; 殺; 殺; 殺; ) CJK COMPATIBILITY IDEOGRAPH-2F8F5
+2F8F6;6BBB;6BBB;6BBB;6BBB; # (殻殻; 殻; 殻; 殻; 殻; ) CJK COMPATIBILITY IDEOGRAPH-2F8F6
+2F8F7;23A8D;23A8D;23A8D;23A8D; # (𣪍𣪍; ð£ªð£ª; ð£ªð£ª; ð£ªð£ª; ð£ªð£ª; ) CJK COMPATIBILITY IDEOGRAPH-2F8F7
+2F8F8;21D0B;21D0B;21D0B;21D0B; # (𡴋𡴋; 𡴋𡴋; 𡴋𡴋; 𡴋𡴋; 𡴋𡴋; ) CJK COMPATIBILITY IDEOGRAPH-2F8F8
+2F8F9;23AFA;23AFA;23AFA;23AFA; # (𣫺𣫺; 𣫺𣫺; 𣫺𣫺; 𣫺𣫺; 𣫺𣫺; ) CJK COMPATIBILITY IDEOGRAPH-2F8F9
+2F8FA;6C4E;6C4E;6C4E;6C4E; # (汎汎; 汎; 汎; 汎; 汎; ) CJK COMPATIBILITY IDEOGRAPH-2F8FA
+2F8FB;23CBC;23CBC;23CBC;23CBC; # (𣲼𣲼; 𣲼𣲼; 𣲼𣲼; 𣲼𣲼; 𣲼𣲼; ) CJK COMPATIBILITY IDEOGRAPH-2F8FB
+2F8FC;6CBF;6CBF;6CBF;6CBF; # (沿沿; 沿; 沿; 沿; 沿; ) CJK COMPATIBILITY IDEOGRAPH-2F8FC
+2F8FD;6CCD;6CCD;6CCD;6CCD; # (泍泍; æ³; æ³; æ³; æ³; ) CJK COMPATIBILITY IDEOGRAPH-2F8FD
+2F8FE;6C67;6C67;6C67;6C67; # (汧汧; 汧; 汧; 汧; 汧; ) CJK COMPATIBILITY IDEOGRAPH-2F8FE
+2F8FF;6D16;6D16;6D16;6D16; # (洖洖; 洖; 洖; 洖; 洖; ) CJK COMPATIBILITY IDEOGRAPH-2F8FF
+2F900;6D3E;6D3E;6D3E;6D3E; # (派派; 派; 派; 派; 派; ) CJK COMPATIBILITY IDEOGRAPH-2F900
+2F901;6D77;6D77;6D77;6D77; # (ð¯¤ð¯¤; æµ·; æµ·; æµ·; æµ·; ) CJK COMPATIBILITY IDEOGRAPH-2F901
+2F902;6D41;6D41;6D41;6D41; # (流流; æµ; æµ; æµ; æµ; ) CJK COMPATIBILITY IDEOGRAPH-2F902
+2F903;6D69;6D69;6D69;6D69; # (浩浩; 浩; 浩; 浩; 浩; ) CJK COMPATIBILITY IDEOGRAPH-2F903
+2F904;6D78;6D78;6D78;6D78; # (浸浸; 浸; 浸; 浸; 浸; ) CJK COMPATIBILITY IDEOGRAPH-2F904
+2F905;6D85;6D85;6D85;6D85; # (涅涅; 涅; 涅; 涅; 涅; ) CJK COMPATIBILITY IDEOGRAPH-2F905
+2F906;23D1E;23D1E;23D1E;23D1E; # (𣴞𣴞; 𣴞𣴞; 𣴞𣴞; 𣴞𣴞; 𣴞𣴞; ) CJK COMPATIBILITY IDEOGRAPH-2F906
+2F907;6D34;6D34;6D34;6D34; # (洴洴; 洴; 洴; 洴; 洴; ) CJK COMPATIBILITY IDEOGRAPH-2F907
+2F908;6E2F;6E2F;6E2F;6E2F; # (港港; 港; 港; 港; 港; ) CJK COMPATIBILITY IDEOGRAPH-2F908
+2F909;6E6E;6E6E;6E6E;6E6E; # (湮湮; 湮; 湮; 湮; 湮; ) CJK COMPATIBILITY IDEOGRAPH-2F909
+2F90A;3D33;3D33;3D33;3D33; # (㴳㴳; 㴳; 㴳; 㴳; 㴳; ) CJK COMPATIBILITY IDEOGRAPH-2F90A
+2F90B;6ECB;6ECB;6ECB;6ECB; # (滋滋; 滋; 滋; 滋; 滋; ) CJK COMPATIBILITY IDEOGRAPH-2F90B
+2F90C;6EC7;6EC7;6EC7;6EC7; # (滇滇; 滇; 滇; 滇; 滇; ) CJK COMPATIBILITY IDEOGRAPH-2F90C
+2F90D;23ED1;23ED1;23ED1;23ED1; # (ð¯¤ð¯¤; 𣻑𣻑; 𣻑𣻑; 𣻑𣻑; 𣻑𣻑; ) CJK COMPATIBILITY IDEOGRAPH-2F90D
+2F90E;6DF9;6DF9;6DF9;6DF9; # (淹淹; 淹; 淹; 淹; 淹; ) CJK COMPATIBILITY IDEOGRAPH-2F90E
+2F90F;6F6E;6F6E;6F6E;6F6E; # (ð¯¤ð¯¤; æ½®; æ½®; æ½®; æ½®; ) CJK COMPATIBILITY IDEOGRAPH-2F90F
+2F910;23F5E;23F5E;23F5E;23F5E; # (ð¯¤ð¯¤; 𣽞𣽞; 𣽞𣽞; 𣽞𣽞; 𣽞𣽞; ) CJK COMPATIBILITY IDEOGRAPH-2F910
+2F911;23F8E;23F8E;23F8E;23F8E; # (𣾎𣾎; 𣾎𣾎; 𣾎𣾎; 𣾎𣾎; 𣾎𣾎; ) CJK COMPATIBILITY IDEOGRAPH-2F911
+2F912;6FC6;6FC6;6FC6;6FC6; # (濆濆; 濆; 濆; 濆; 濆; ) CJK COMPATIBILITY IDEOGRAPH-2F912
+2F913;7039;7039;7039;7039; # (瀹瀹; 瀹; 瀹; 瀹; 瀹; ) CJK COMPATIBILITY IDEOGRAPH-2F913
+2F914;701E;701E;701E;701E; # (瀞瀞; 瀞; 瀞; 瀞; 瀞; ) CJK COMPATIBILITY IDEOGRAPH-2F914
+2F915;701B;701B;701B;701B; # (瀛瀛; 瀛; 瀛; 瀛; 瀛; ) CJK COMPATIBILITY IDEOGRAPH-2F915
+2F916;3D96;3D96;3D96;3D96; # (㶖㶖; 㶖; 㶖; 㶖; 㶖; ) CJK COMPATIBILITY IDEOGRAPH-2F916
+2F917;704A;704A;704A;704A; # (灊灊; çŠ; çŠ; çŠ; çŠ; ) CJK COMPATIBILITY IDEOGRAPH-2F917
+2F918;707D;707D;707D;707D; # (災災; ç½; ç½; ç½; ç½; ) CJK COMPATIBILITY IDEOGRAPH-2F918
+2F919;7077;7077;7077;7077; # (灷灷; ç·; ç·; ç·; ç·; ) CJK COMPATIBILITY IDEOGRAPH-2F919
+2F91A;70AD;70AD;70AD;70AD; # (炭炭; 炭; 炭; 炭; 炭; ) CJK COMPATIBILITY IDEOGRAPH-2F91A
+2F91B;20525;20525;20525;20525; # (𠔥𠔥; 𠔥𠔥; 𠔥𠔥; 𠔥𠔥; 𠔥𠔥; ) CJK COMPATIBILITY IDEOGRAPH-2F91B
+2F91C;7145;7145;7145;7145; # (煅煅; 煅; 煅; 煅; 煅; ) CJK COMPATIBILITY IDEOGRAPH-2F91C
+2F91D;24263;24263;24263;24263; # (ð¯¤ð¯¤; 𤉣𤉣; 𤉣𤉣; 𤉣𤉣; 𤉣𤉣; ) CJK COMPATIBILITY IDEOGRAPH-2F91D
+2F91E;719C;719C;719C;719C; # (熜熜; 熜; 熜; 熜; 熜; ) CJK COMPATIBILITY IDEOGRAPH-2F91E
+2F91F;243AB;243AB;243AB;243AB; # (𤎫𤎫; 𤎫𤎫; 𤎫𤎫; 𤎫𤎫; 𤎫𤎫; ) CJK COMPATIBILITY IDEOGRAPH-2F91F
+2F920;7228;7228;7228;7228; # (爨爨; 爨; 爨; 爨; 爨; ) CJK COMPATIBILITY IDEOGRAPH-2F920
+2F921;7235;7235;7235;7235; # (爵爵; 爵; 爵; 爵; 爵; ) CJK COMPATIBILITY IDEOGRAPH-2F921
+2F922;7250;7250;7250;7250; # (牐牐; ç‰; ç‰; ç‰; ç‰; ) CJK COMPATIBILITY IDEOGRAPH-2F922
+2F923;24608;24608;24608;24608; # (𤘈𤘈; 𤘈𤘈; 𤘈𤘈; 𤘈𤘈; 𤘈𤘈; ) CJK COMPATIBILITY IDEOGRAPH-2F923
+2F924;7280;7280;7280;7280; # (犀犀; 犀; 犀; 犀; 犀; ) CJK COMPATIBILITY IDEOGRAPH-2F924
+2F925;7295;7295;7295;7295; # (犕犕; 犕; 犕; 犕; 犕; ) CJK COMPATIBILITY IDEOGRAPH-2F925
+2F926;24735;24735;24735;24735; # (𤜵𤜵; 𤜵𤜵; 𤜵𤜵; 𤜵𤜵; 𤜵𤜵; ) CJK COMPATIBILITY IDEOGRAPH-2F926
+2F927;24814;24814;24814;24814; # (𤠔𤠔; 𤠔𤠔; 𤠔𤠔; 𤠔𤠔; 𤠔𤠔; ) CJK COMPATIBILITY IDEOGRAPH-2F927
+2F928;737A;737A;737A;737A; # (獺獺; çº; çº; çº; çº; ) CJK COMPATIBILITY IDEOGRAPH-2F928
+2F929;738B;738B;738B;738B; # (王王; 王; 王; 王; 王; ) CJK COMPATIBILITY IDEOGRAPH-2F929
+2F92A;3EAC;3EAC;3EAC;3EAC; # (㺬㺬; 㺬; 㺬; 㺬; 㺬; ) CJK COMPATIBILITY IDEOGRAPH-2F92A
+2F92B;73A5;73A5;73A5;73A5; # (玥玥; 玥; 玥; 玥; 玥; ) CJK COMPATIBILITY IDEOGRAPH-2F92B
+2F92C;3EB8;3EB8;3EB8;3EB8; # (㺸㺸; 㺸; 㺸; 㺸; 㺸; ) CJK COMPATIBILITY IDEOGRAPH-2F92C
+2F92D;3EB8;3EB8;3EB8;3EB8; # (㺸㺸; 㺸; 㺸; 㺸; 㺸; ) CJK COMPATIBILITY IDEOGRAPH-2F92D
+2F92E;7447;7447;7447;7447; # (瑇瑇; 瑇; 瑇; 瑇; 瑇; ) CJK COMPATIBILITY IDEOGRAPH-2F92E
+2F92F;745C;745C;745C;745C; # (瑜瑜; 瑜; 瑜; 瑜; 瑜; ) CJK COMPATIBILITY IDEOGRAPH-2F92F
+2F930;7471;7471;7471;7471; # (瑱瑱; 瑱; 瑱; 瑱; 瑱; ) CJK COMPATIBILITY IDEOGRAPH-2F930
+2F931;7485;7485;7485;7485; # (璅璅; 璅; 璅; 璅; 璅; ) CJK COMPATIBILITY IDEOGRAPH-2F931
+2F932;74CA;74CA;74CA;74CA; # (瓊瓊; 瓊; 瓊; 瓊; 瓊; ) CJK COMPATIBILITY IDEOGRAPH-2F932
+2F933;3F1B;3F1B;3F1B;3F1B; # (㼛㼛; 㼛; 㼛; 㼛; 㼛; ) CJK COMPATIBILITY IDEOGRAPH-2F933
+2F934;7524;7524;7524;7524; # (甤甤; 甤; 甤; 甤; 甤; ) CJK COMPATIBILITY IDEOGRAPH-2F934
+2F935;24C36;24C36;24C36;24C36; # (𤰶𤰶; 𤰶𤰶; 𤰶𤰶; 𤰶𤰶; 𤰶𤰶; ) CJK COMPATIBILITY IDEOGRAPH-2F935
+2F936;753E;753E;753E;753E; # (甾甾; 甾; 甾; 甾; 甾; ) CJK COMPATIBILITY IDEOGRAPH-2F936
+2F937;24C92;24C92;24C92;24C92; # (𤲒𤲒; 𤲒𤲒; 𤲒𤲒; 𤲒𤲒; 𤲒𤲒; ) CJK COMPATIBILITY IDEOGRAPH-2F937
+2F938;7570;7570;7570;7570; # (異異; 異; 異; 異; 異; ) CJK COMPATIBILITY IDEOGRAPH-2F938
+2F939;2219F;2219F;2219F;2219F; # (𢆟𢆟; 𢆟𢆟; 𢆟𢆟; 𢆟𢆟; 𢆟𢆟; ) CJK COMPATIBILITY IDEOGRAPH-2F939
+2F93A;7610;7610;7610;7610; # (瘐瘐; ç˜; ç˜; ç˜; ç˜; ) CJK COMPATIBILITY IDEOGRAPH-2F93A
+2F93B;24FA1;24FA1;24FA1;24FA1; # (𤾡𤾡; 𤾡𤾡; 𤾡𤾡; 𤾡𤾡; 𤾡𤾡; ) CJK COMPATIBILITY IDEOGRAPH-2F93B
+2F93C;24FB8;24FB8;24FB8;24FB8; # (𤾸𤾸; 𤾸𤾸; 𤾸𤾸; 𤾸𤾸; 𤾸𤾸; ) CJK COMPATIBILITY IDEOGRAPH-2F93C
+2F93D;25044;25044;25044;25044; # (𥁄𥁄; ð¥„ð¥„; ð¥„ð¥„; ð¥„ð¥„; ð¥„ð¥„; ) CJK COMPATIBILITY IDEOGRAPH-2F93D
+2F93E;3FFC;3FFC;3FFC;3FFC; # (㿼㿼; 㿼; 㿼; 㿼; 㿼; ) CJK COMPATIBILITY IDEOGRAPH-2F93E
+2F93F;4008;4008;4008;4008; # (䀈䀈; 䀈; 䀈; 䀈; 䀈; ) CJK COMPATIBILITY IDEOGRAPH-2F93F
+2F940;76F4;76F4;76F4;76F4; # (直直; 直; 直; 直; 直; ) CJK COMPATIBILITY IDEOGRAPH-2F940
+2F941;250F3;250F3;250F3;250F3; # (ð¯¥ð¯¥; 𥃳𥃳; 𥃳𥃳; 𥃳𥃳; 𥃳𥃳; ) CJK COMPATIBILITY IDEOGRAPH-2F941
+2F942;250F2;250F2;250F2;250F2; # (𥃲𥃲; 𥃲𥃲; 𥃲𥃲; 𥃲𥃲; 𥃲𥃲; ) CJK COMPATIBILITY IDEOGRAPH-2F942
+2F943;25119;25119;25119;25119; # (𥄙𥄙; 𥄙𥄙; 𥄙𥄙; 𥄙𥄙; 𥄙𥄙; ) CJK COMPATIBILITY IDEOGRAPH-2F943
+2F944;25133;25133;25133;25133; # (𥄳𥄳; 𥄳𥄳; 𥄳𥄳; 𥄳𥄳; 𥄳𥄳; ) CJK COMPATIBILITY IDEOGRAPH-2F944
+2F945;771E;771E;771E;771E; # (眞眞; 眞; 眞; 眞; 眞; ) CJK COMPATIBILITY IDEOGRAPH-2F945
+2F946;771F;771F;771F;771F; # (真真; 真; 真; 真; 真; ) CJK COMPATIBILITY IDEOGRAPH-2F946
+2F947;771F;771F;771F;771F; # (真真; 真; 真; 真; 真; ) CJK COMPATIBILITY IDEOGRAPH-2F947
+2F948;774A;774A;774A;774A; # (睊睊; çŠ; çŠ; çŠ; çŠ; ) CJK COMPATIBILITY IDEOGRAPH-2F948
+2F949;4039;4039;4039;4039; # (䀹䀹; 䀹; 䀹; 䀹; 䀹; ) CJK COMPATIBILITY IDEOGRAPH-2F949
+2F94A;778B;778B;778B;778B; # (瞋瞋; 瞋; 瞋; 瞋; 瞋; ) CJK COMPATIBILITY IDEOGRAPH-2F94A
+2F94B;4046;4046;4046;4046; # (䁆䁆; ä†; ä†; ä†; ä†; ) CJK COMPATIBILITY IDEOGRAPH-2F94B
+2F94C;4096;4096;4096;4096; # (䂖䂖; 䂖; 䂖; 䂖; 䂖; ) CJK COMPATIBILITY IDEOGRAPH-2F94C
+2F94D;2541D;2541D;2541D;2541D; # (ð¯¥ð¯¥; ð¥ð¥; ð¥ð¥; ð¥ð¥; ð¥ð¥; ) CJK COMPATIBILITY IDEOGRAPH-2F94D
+2F94E;784E;784E;784E;784E; # (硎硎; 硎; 硎; 硎; 硎; ) CJK COMPATIBILITY IDEOGRAPH-2F94E
+2F94F;788C;788C;788C;788C; # (ð¯¥ð¯¥; 碌; 碌; 碌; 碌; ) CJK COMPATIBILITY IDEOGRAPH-2F94F
+2F950;78CC;78CC;78CC;78CC; # (ð¯¥ð¯¥; 磌; 磌; 磌; 磌; ) CJK COMPATIBILITY IDEOGRAPH-2F950
+2F951;40E3;40E3;40E3;40E3; # (䃣䃣; 䃣; 䃣; 䃣; 䃣; ) CJK COMPATIBILITY IDEOGRAPH-2F951
+2F952;25626;25626;25626;25626; # (𥘦𥘦; 𥘦𥘦; 𥘦𥘦; 𥘦𥘦; 𥘦𥘦; ) CJK COMPATIBILITY IDEOGRAPH-2F952
+2F953;7956;7956;7956;7956; # (祖祖; 祖; 祖; 祖; 祖; ) CJK COMPATIBILITY IDEOGRAPH-2F953
+2F954;2569A;2569A;2569A;2569A; # (𥚚𥚚; 𥚚𥚚; 𥚚𥚚; 𥚚𥚚; 𥚚𥚚; ) CJK COMPATIBILITY IDEOGRAPH-2F954
+2F955;256C5;256C5;256C5;256C5; # (𥛅𥛅; 𥛅𥛅; 𥛅𥛅; 𥛅𥛅; 𥛅𥛅; ) CJK COMPATIBILITY IDEOGRAPH-2F955
+2F956;798F;798F;798F;798F; # (福福; ç¦; ç¦; ç¦; ç¦; ) CJK COMPATIBILITY IDEOGRAPH-2F956
+2F957;79EB;79EB;79EB;79EB; # (秫秫; 秫; 秫; 秫; 秫; ) CJK COMPATIBILITY IDEOGRAPH-2F957
+2F958;412F;412F;412F;412F; # (䄯䄯; 䄯; 䄯; 䄯; 䄯; ) CJK COMPATIBILITY IDEOGRAPH-2F958
+2F959;7A40;7A40;7A40;7A40; # (穀穀; 穀; 穀; 穀; 穀; ) CJK COMPATIBILITY IDEOGRAPH-2F959
+2F95A;7A4A;7A4A;7A4A;7A4A; # (穊穊; 穊; 穊; 穊; 穊; ) CJK COMPATIBILITY IDEOGRAPH-2F95A
+2F95B;7A4F;7A4F;7A4F;7A4F; # (穏穏; ç©; ç©; ç©; ç©; ) CJK COMPATIBILITY IDEOGRAPH-2F95B
+2F95C;2597C;2597C;2597C;2597C; # (𥥼𥥼; 𥥼𥥼; 𥥼𥥼; 𥥼𥥼; 𥥼𥥼; ) CJK COMPATIBILITY IDEOGRAPH-2F95C
+2F95D;25AA7;25AA7;25AA7;25AA7; # (ð¯¥ð¯¥; 𥪧𥪧; 𥪧𥪧; 𥪧𥪧; 𥪧𥪧; ) CJK COMPATIBILITY IDEOGRAPH-2F95D
+2F95E;25AA7;25AA7;25AA7;25AA7; # (𥪧𥪧; 𥪧𥪧; 𥪧𥪧; 𥪧𥪧; 𥪧𥪧; ) CJK COMPATIBILITY IDEOGRAPH-2F95E
+2F95F;7AEE;7AEE;7AEE;7AEE; # (竮竮; 竮; 竮; 竮; 竮; ) CJK COMPATIBILITY IDEOGRAPH-2F95F
+2F960;4202;4202;4202;4202; # (䈂䈂; 䈂; 䈂; 䈂; 䈂; ) CJK COMPATIBILITY IDEOGRAPH-2F960
+2F961;25BAB;25BAB;25BAB;25BAB; # (𥮫𥮫; 𥮫𥮫; 𥮫𥮫; 𥮫𥮫; 𥮫𥮫; ) CJK COMPATIBILITY IDEOGRAPH-2F961
+2F962;7BC6;7BC6;7BC6;7BC6; # (篆篆; 篆; 篆; 篆; 篆; ) CJK COMPATIBILITY IDEOGRAPH-2F962
+2F963;7BC9;7BC9;7BC9;7BC9; # (築築; 築; 築; 築; 築; ) CJK COMPATIBILITY IDEOGRAPH-2F963
+2F964;4227;4227;4227;4227; # (䈧䈧; 䈧; 䈧; 䈧; 䈧; ) CJK COMPATIBILITY IDEOGRAPH-2F964
+2F965;25C80;25C80;25C80;25C80; # (𥲀𥲀; 𥲀𥲀; 𥲀𥲀; 𥲀𥲀; 𥲀𥲀; ) CJK COMPATIBILITY IDEOGRAPH-2F965
+2F966;7CD2;7CD2;7CD2;7CD2; # (糒糒; 糒; 糒; 糒; 糒; ) CJK COMPATIBILITY IDEOGRAPH-2F966
+2F967;42A0;42A0;42A0;42A0; # (䊠䊠; 䊠; 䊠; 䊠; 䊠; ) CJK COMPATIBILITY IDEOGRAPH-2F967
+2F968;7CE8;7CE8;7CE8;7CE8; # (糨糨; 糨; 糨; 糨; 糨; ) CJK COMPATIBILITY IDEOGRAPH-2F968
+2F969;7CE3;7CE3;7CE3;7CE3; # (糣糣; 糣; 糣; 糣; 糣; ) CJK COMPATIBILITY IDEOGRAPH-2F969
+2F96A;7D00;7D00;7D00;7D00; # (紀紀; 紀; 紀; 紀; 紀; ) CJK COMPATIBILITY IDEOGRAPH-2F96A
+2F96B;25F86;25F86;25F86;25F86; # (𥾆𥾆; 𥾆𥾆; 𥾆𥾆; 𥾆𥾆; 𥾆𥾆; ) CJK COMPATIBILITY IDEOGRAPH-2F96B
+2F96C;7D63;7D63;7D63;7D63; # (絣絣; 絣; 絣; 絣; 絣; ) CJK COMPATIBILITY IDEOGRAPH-2F96C
+2F96D;4301;4301;4301;4301; # (䌁䌁; äŒ; äŒ; äŒ; äŒ; ) CJK COMPATIBILITY IDEOGRAPH-2F96D
+2F96E;7DC7;7DC7;7DC7;7DC7; # (緇緇; 緇; 緇; 緇; 緇; ) CJK COMPATIBILITY IDEOGRAPH-2F96E
+2F96F;7E02;7E02;7E02;7E02; # (縂縂; 縂; 縂; 縂; 縂; ) CJK COMPATIBILITY IDEOGRAPH-2F96F
+2F970;7E45;7E45;7E45;7E45; # (繅繅; 繅; 繅; 繅; 繅; ) CJK COMPATIBILITY IDEOGRAPH-2F970
+2F971;4334;4334;4334;4334; # (䌴䌴; 䌴; 䌴; 䌴; 䌴; ) CJK COMPATIBILITY IDEOGRAPH-2F971
+2F972;26228;26228;26228;26228; # (𦈨𦈨; 𦈨𦈨; 𦈨𦈨; 𦈨𦈨; 𦈨𦈨; ) CJK COMPATIBILITY IDEOGRAPH-2F972
+2F973;26247;26247;26247;26247; # (𦉇𦉇; 𦉇𦉇; 𦉇𦉇; 𦉇𦉇; 𦉇𦉇; ) CJK COMPATIBILITY IDEOGRAPH-2F973
+2F974;4359;4359;4359;4359; # (䍙䍙; ä™; ä™; ä™; ä™; ) CJK COMPATIBILITY IDEOGRAPH-2F974
+2F975;262D9;262D9;262D9;262D9; # (𦋙𦋙; 𦋙𦋙; 𦋙𦋙; 𦋙𦋙; 𦋙𦋙; ) CJK COMPATIBILITY IDEOGRAPH-2F975
+2F976;7F7A;7F7A;7F7A;7F7A; # (罺罺; 罺; 罺; 罺; 罺; ) CJK COMPATIBILITY IDEOGRAPH-2F976
+2F977;2633E;2633E;2633E;2633E; # (𦌾𦌾; 𦌾𦌾; 𦌾𦌾; 𦌾𦌾; 𦌾𦌾; ) CJK COMPATIBILITY IDEOGRAPH-2F977
+2F978;7F95;7F95;7F95;7F95; # (羕羕; 羕; 羕; 羕; 羕; ) CJK COMPATIBILITY IDEOGRAPH-2F978
+2F979;7FFA;7FFA;7FFA;7FFA; # (翺翺; 翺; 翺; 翺; 翺; ) CJK COMPATIBILITY IDEOGRAPH-2F979
+2F97A;8005;8005;8005;8005; # (者者; 者; 者; 者; 者; ) CJK COMPATIBILITY IDEOGRAPH-2F97A
+2F97B;264DA;264DA;264DA;264DA; # (𦓚𦓚; 𦓚𦓚; 𦓚𦓚; 𦓚𦓚; 𦓚𦓚; ) CJK COMPATIBILITY IDEOGRAPH-2F97B
+2F97C;26523;26523;26523;26523; # (𦔣𦔣; 𦔣𦔣; 𦔣𦔣; 𦔣𦔣; 𦔣𦔣; ) CJK COMPATIBILITY IDEOGRAPH-2F97C
+2F97D;8060;8060;8060;8060; # (聠聠; è ; è ; è ; è ; ) CJK COMPATIBILITY IDEOGRAPH-2F97D
+2F97E;265A8;265A8;265A8;265A8; # (𦖨𦖨; 𦖨𦖨; 𦖨𦖨; 𦖨𦖨; 𦖨𦖨; ) CJK COMPATIBILITY IDEOGRAPH-2F97E
+2F97F;8070;8070;8070;8070; # (聰聰; è°; è°; è°; è°; ) CJK COMPATIBILITY IDEOGRAPH-2F97F
+2F980;2335F;2335F;2335F;2335F; # (𣍟𣍟; ð£Ÿð£Ÿ; ð£Ÿð£Ÿ; ð£Ÿð£Ÿ; ð£Ÿð£Ÿ; ) CJK COMPATIBILITY IDEOGRAPH-2F980
+2F981;43D5;43D5;43D5;43D5; # (ð¯¦ð¯¦; ä•; ä•; ä•; ä•; ) CJK COMPATIBILITY IDEOGRAPH-2F981
+2F982;80B2;80B2;80B2;80B2; # (育育; 育; 育; 育; 育; ) CJK COMPATIBILITY IDEOGRAPH-2F982
+2F983;8103;8103;8103;8103; # (脃脃; 脃; 脃; 脃; 脃; ) CJK COMPATIBILITY IDEOGRAPH-2F983
+2F984;440B;440B;440B;440B; # (䐋䐋; ä‹; ä‹; ä‹; ä‹; ) CJK COMPATIBILITY IDEOGRAPH-2F984
+2F985;813E;813E;813E;813E; # (脾脾; 脾; 脾; 脾; 脾; ) CJK COMPATIBILITY IDEOGRAPH-2F985
+2F986;5AB5;5AB5;5AB5;5AB5; # (媵媵; 媵; 媵; 媵; 媵; ) CJK COMPATIBILITY IDEOGRAPH-2F986
+2F987;267A7;267A7;267A7;267A7; # (𦞧𦞧; 𦞧𦞧; 𦞧𦞧; 𦞧𦞧; 𦞧𦞧; ) CJK COMPATIBILITY IDEOGRAPH-2F987
+2F988;267B5;267B5;267B5;267B5; # (𦞵𦞵; 𦞵𦞵; 𦞵𦞵; 𦞵𦞵; 𦞵𦞵; ) CJK COMPATIBILITY IDEOGRAPH-2F988
+2F989;23393;23393;23393;23393; # (𣎓𣎓; 𣎓𣎓; 𣎓𣎓; 𣎓𣎓; 𣎓𣎓; ) CJK COMPATIBILITY IDEOGRAPH-2F989
+2F98A;2339C;2339C;2339C;2339C; # (𣎜𣎜; 𣎜𣎜; 𣎜𣎜; 𣎜𣎜; 𣎜𣎜; ) CJK COMPATIBILITY IDEOGRAPH-2F98A
+2F98B;8201;8201;8201;8201; # (舁舁; èˆ; èˆ; èˆ; èˆ; ) CJK COMPATIBILITY IDEOGRAPH-2F98B
+2F98C;8204;8204;8204;8204; # (舄舄; 舄; 舄; 舄; 舄; ) CJK COMPATIBILITY IDEOGRAPH-2F98C
+2F98D;8F9E;8F9E;8F9E;8F9E; # (ð¯¦ð¯¦; 辞; 辞; 辞; 辞; ) CJK COMPATIBILITY IDEOGRAPH-2F98D
+2F98E;446B;446B;446B;446B; # (䑫䑫; 䑫; 䑫; 䑫; 䑫; ) CJK COMPATIBILITY IDEOGRAPH-2F98E
+2F98F;8291;8291;8291;8291; # (ð¯¦ð¯¦; 芑; 芑; 芑; 芑; ) CJK COMPATIBILITY IDEOGRAPH-2F98F
+2F990;828B;828B;828B;828B; # (ð¯¦ð¯¦; 芋; 芋; 芋; 芋; ) CJK COMPATIBILITY IDEOGRAPH-2F990
+2F991;829D;829D;829D;829D; # (芝芝; èŠ; èŠ; èŠ; èŠ; ) CJK COMPATIBILITY IDEOGRAPH-2F991
+2F992;52B3;52B3;52B3;52B3; # (劳劳; 劳; 劳; 劳; 劳; ) CJK COMPATIBILITY IDEOGRAPH-2F992
+2F993;82B1;82B1;82B1;82B1; # (花花; 花; 花; 花; 花; ) CJK COMPATIBILITY IDEOGRAPH-2F993
+2F994;82B3;82B3;82B3;82B3; # (芳芳; 芳; 芳; 芳; 芳; ) CJK COMPATIBILITY IDEOGRAPH-2F994
+2F995;82BD;82BD;82BD;82BD; # (芽芽; 芽; 芽; 芽; 芽; ) CJK COMPATIBILITY IDEOGRAPH-2F995
+2F996;82E6;82E6;82E6;82E6; # (苦苦; 苦; 苦; 苦; 苦; ) CJK COMPATIBILITY IDEOGRAPH-2F996
+2F997;26B3C;26B3C;26B3C;26B3C; # (𦬼𦬼; 𦬼𦬼; 𦬼𦬼; 𦬼𦬼; 𦬼𦬼; ) CJK COMPATIBILITY IDEOGRAPH-2F997
+2F998;82E5;82E5;82E5;82E5; # (若若; 若; 若; 若; 若; ) CJK COMPATIBILITY IDEOGRAPH-2F998
+2F999;831D;831D;831D;831D; # (茝茝; èŒ; èŒ; èŒ; èŒ; ) CJK COMPATIBILITY IDEOGRAPH-2F999
+2F99A;8363;8363;8363;8363; # (荣荣; è£; è£; è£; è£; ) CJK COMPATIBILITY IDEOGRAPH-2F99A
+2F99B;83AD;83AD;83AD;83AD; # (莭莭; 莭; 莭; 莭; 莭; ) CJK COMPATIBILITY IDEOGRAPH-2F99B
+2F99C;8323;8323;8323;8323; # (茣茣; 茣; 茣; 茣; 茣; ) CJK COMPATIBILITY IDEOGRAPH-2F99C
+2F99D;83BD;83BD;83BD;83BD; # (ð¯¦ð¯¦; 莽; 莽; 莽; 莽; ) CJK COMPATIBILITY IDEOGRAPH-2F99D
+2F99E;83E7;83E7;83E7;83E7; # (菧菧; è§; è§; è§; è§; ) CJK COMPATIBILITY IDEOGRAPH-2F99E
+2F99F;8457;8457;8457;8457; # (著著; 著; 著; 著; 著; ) CJK COMPATIBILITY IDEOGRAPH-2F99F
+2F9A0;8353;8353;8353;8353; # (荓荓; è“; è“; è“; è“; ) CJK COMPATIBILITY IDEOGRAPH-2F9A0
+2F9A1;83CA;83CA;83CA;83CA; # (菊菊; èŠ; èŠ; èŠ; èŠ; ) CJK COMPATIBILITY IDEOGRAPH-2F9A1
+2F9A2;83CC;83CC;83CC;83CC; # (菌菌; èŒ; èŒ; èŒ; èŒ; ) CJK COMPATIBILITY IDEOGRAPH-2F9A2
+2F9A3;83DC;83DC;83DC;83DC; # (菜菜; èœ; èœ; èœ; èœ; ) CJK COMPATIBILITY IDEOGRAPH-2F9A3
+2F9A4;26C36;26C36;26C36;26C36; # (𦰶𦰶; 𦰶𦰶; 𦰶𦰶; 𦰶𦰶; 𦰶𦰶; ) CJK COMPATIBILITY IDEOGRAPH-2F9A4
+2F9A5;26D6B;26D6B;26D6B;26D6B; # (𦵫𦵫; 𦵫𦵫; 𦵫𦵫; 𦵫𦵫; 𦵫𦵫; ) CJK COMPATIBILITY IDEOGRAPH-2F9A5
+2F9A6;26CD5;26CD5;26CD5;26CD5; # (𦳕𦳕; 𦳕𦳕; 𦳕𦳕; 𦳕𦳕; 𦳕𦳕; ) CJK COMPATIBILITY IDEOGRAPH-2F9A6
+2F9A7;452B;452B;452B;452B; # (䔫䔫; 䔫; 䔫; 䔫; 䔫; ) CJK COMPATIBILITY IDEOGRAPH-2F9A7
+2F9A8;84F1;84F1;84F1;84F1; # (蓱蓱; 蓱; 蓱; 蓱; 蓱; ) CJK COMPATIBILITY IDEOGRAPH-2F9A8
+2F9A9;84F3;84F3;84F3;84F3; # (蓳蓳; 蓳; 蓳; 蓳; 蓳; ) CJK COMPATIBILITY IDEOGRAPH-2F9A9
+2F9AA;8516;8516;8516;8516; # (蔖蔖; 蔖; 蔖; 蔖; 蔖; ) CJK COMPATIBILITY IDEOGRAPH-2F9AA
+2F9AB;273CA;273CA;273CA;273CA; # (𧏊𧏊; ð§Šð§Š; ð§Šð§Š; ð§Šð§Š; ð§Šð§Š; ) CJK COMPATIBILITY IDEOGRAPH-2F9AB
+2F9AC;8564;8564;8564;8564; # (蕤蕤; 蕤; 蕤; 蕤; 蕤; ) CJK COMPATIBILITY IDEOGRAPH-2F9AC
+2F9AD;26F2C;26F2C;26F2C;26F2C; # (𦼬𦼬; 𦼬𦼬; 𦼬𦼬; 𦼬𦼬; 𦼬𦼬; ) CJK COMPATIBILITY IDEOGRAPH-2F9AD
+2F9AE;455D;455D;455D;455D; # (䕝䕝; ä•; ä•; ä•; ä•; ) CJK COMPATIBILITY IDEOGRAPH-2F9AE
+2F9AF;4561;4561;4561;4561; # (䕡䕡; 䕡; 䕡; 䕡; 䕡; ) CJK COMPATIBILITY IDEOGRAPH-2F9AF
+2F9B0;26FB1;26FB1;26FB1;26FB1; # (𦾱𦾱; 𦾱𦾱; 𦾱𦾱; 𦾱𦾱; 𦾱𦾱; ) CJK COMPATIBILITY IDEOGRAPH-2F9B0
+2F9B1;270D2;270D2;270D2;270D2; # (𧃒𧃒; 𧃒𧃒; 𧃒𧃒; 𧃒𧃒; 𧃒𧃒; ) CJK COMPATIBILITY IDEOGRAPH-2F9B1
+2F9B2;456B;456B;456B;456B; # (䕫䕫; 䕫; 䕫; 䕫; 䕫; ) CJK COMPATIBILITY IDEOGRAPH-2F9B2
+2F9B3;8650;8650;8650;8650; # (虐虐; è™; è™; è™; è™; ) CJK COMPATIBILITY IDEOGRAPH-2F9B3
+2F9B4;865C;865C;865C;865C; # (虜虜; 虜; 虜; 虜; 虜; ) CJK COMPATIBILITY IDEOGRAPH-2F9B4
+2F9B5;8667;8667;8667;8667; # (虧虧; 虧; 虧; 虧; 虧; ) CJK COMPATIBILITY IDEOGRAPH-2F9B5
+2F9B6;8669;8669;8669;8669; # (虩虩; 虩; 虩; 虩; 虩; ) CJK COMPATIBILITY IDEOGRAPH-2F9B6
+2F9B7;86A9;86A9;86A9;86A9; # (蚩蚩; 蚩; 蚩; 蚩; 蚩; ) CJK COMPATIBILITY IDEOGRAPH-2F9B7
+2F9B8;8688;8688;8688;8688; # (蚈蚈; 蚈; 蚈; 蚈; 蚈; ) CJK COMPATIBILITY IDEOGRAPH-2F9B8
+2F9B9;870E;870E;870E;870E; # (蜎蜎; 蜎; 蜎; 蜎; 蜎; ) CJK COMPATIBILITY IDEOGRAPH-2F9B9
+2F9BA;86E2;86E2;86E2;86E2; # (蛢蛢; 蛢; 蛢; 蛢; 蛢; ) CJK COMPATIBILITY IDEOGRAPH-2F9BA
+2F9BB;8779;8779;8779;8779; # (蝹蝹; è¹; è¹; è¹; è¹; ) CJK COMPATIBILITY IDEOGRAPH-2F9BB
+2F9BC;8728;8728;8728;8728; # (蜨蜨; 蜨; 蜨; 蜨; 蜨; ) CJK COMPATIBILITY IDEOGRAPH-2F9BC
+2F9BD;876B;876B;876B;876B; # (蝫蝫; è«; è«; è«; è«; ) CJK COMPATIBILITY IDEOGRAPH-2F9BD
+2F9BE;8786;8786;8786;8786; # (螆螆; 螆; 螆; 螆; 螆; ) CJK COMPATIBILITY IDEOGRAPH-2F9BE
+2F9BF;45D7;45D7;45D7;45D7; # (䗗䗗; 䗗; 䗗; 䗗; 䗗; ) CJK COMPATIBILITY IDEOGRAPH-2F9BF
+2F9C0;87E1;87E1;87E1;87E1; # (蟡蟡; 蟡; 蟡; 蟡; 蟡; ) CJK COMPATIBILITY IDEOGRAPH-2F9C0
+2F9C1;8801;8801;8801;8801; # (ð¯§ð¯§; è ; è ; è ; è ; ) CJK COMPATIBILITY IDEOGRAPH-2F9C1
+2F9C2;45F9;45F9;45F9;45F9; # (䗹䗹; 䗹; 䗹; 䗹; 䗹; ) CJK COMPATIBILITY IDEOGRAPH-2F9C2
+2F9C3;8860;8860;8860;8860; # (衠衠; 衠; 衠; 衠; 衠; ) CJK COMPATIBILITY IDEOGRAPH-2F9C3
+2F9C4;8863;8863;8863;8863; # (衣衣; 衣; 衣; 衣; 衣; ) CJK COMPATIBILITY IDEOGRAPH-2F9C4
+2F9C5;27667;27667;27667;27667; # (𧙧𧙧; 𧙧𧙧; 𧙧𧙧; 𧙧𧙧; 𧙧𧙧; ) CJK COMPATIBILITY IDEOGRAPH-2F9C5
+2F9C6;88D7;88D7;88D7;88D7; # (裗裗; 裗; 裗; 裗; 裗; ) CJK COMPATIBILITY IDEOGRAPH-2F9C6
+2F9C7;88DE;88DE;88DE;88DE; # (裞裞; 裞; 裞; 裞; 裞; ) CJK COMPATIBILITY IDEOGRAPH-2F9C7
+2F9C8;4635;4635;4635;4635; # (䘵䘵; 䘵; 䘵; 䘵; 䘵; ) CJK COMPATIBILITY IDEOGRAPH-2F9C8
+2F9C9;88FA;88FA;88FA;88FA; # (裺裺; 裺; 裺; 裺; 裺; ) CJK COMPATIBILITY IDEOGRAPH-2F9C9
+2F9CA;34BB;34BB;34BB;34BB; # (㒻㒻; 㒻; 㒻; 㒻; 㒻; ) CJK COMPATIBILITY IDEOGRAPH-2F9CA
+2F9CB;278AE;278AE;278AE;278AE; # (𧢮𧢮; 𧢮𧢮; 𧢮𧢮; 𧢮𧢮; 𧢮𧢮; ) CJK COMPATIBILITY IDEOGRAPH-2F9CB
+2F9CC;27966;27966;27966;27966; # (𧥦𧥦; 𧥦𧥦; 𧥦𧥦; 𧥦𧥦; 𧥦𧥦; ) CJK COMPATIBILITY IDEOGRAPH-2F9CC
+2F9CD;46BE;46BE;46BE;46BE; # (ð¯§ð¯§; äš¾; äš¾; äš¾; äš¾; ) CJK COMPATIBILITY IDEOGRAPH-2F9CD
+2F9CE;46C7;46C7;46C7;46C7; # (䛇䛇; 䛇; 䛇; 䛇; 䛇; ) CJK COMPATIBILITY IDEOGRAPH-2F9CE
+2F9CF;8AA0;8AA0;8AA0;8AA0; # (ð¯§ð¯§; 誠; 誠; 誠; 誠; ) CJK COMPATIBILITY IDEOGRAPH-2F9CF
+2F9D0;8AED;8AED;8AED;8AED; # (ð¯§ð¯§; è«­; è«­; è«­; è«­; ) CJK COMPATIBILITY IDEOGRAPH-2F9D0
+2F9D1;8B8A;8B8A;8B8A;8B8A; # (變變; 變; 變; 變; 變; ) CJK COMPATIBILITY IDEOGRAPH-2F9D1
+2F9D2;8C55;8C55;8C55;8C55; # (豕豕; 豕; 豕; 豕; 豕; ) CJK COMPATIBILITY IDEOGRAPH-2F9D2
+2F9D3;27CA8;27CA8;27CA8;27CA8; # (𧲨𧲨; 𧲨𧲨; 𧲨𧲨; 𧲨𧲨; 𧲨𧲨; ) CJK COMPATIBILITY IDEOGRAPH-2F9D3
+2F9D4;8CAB;8CAB;8CAB;8CAB; # (貫貫; 貫; 貫; 貫; 貫; ) CJK COMPATIBILITY IDEOGRAPH-2F9D4
+2F9D5;8CC1;8CC1;8CC1;8CC1; # (賁賁; è³; è³; è³; è³; ) CJK COMPATIBILITY IDEOGRAPH-2F9D5
+2F9D6;8D1B;8D1B;8D1B;8D1B; # (贛贛; 贛; 贛; 贛; 贛; ) CJK COMPATIBILITY IDEOGRAPH-2F9D6
+2F9D7;8D77;8D77;8D77;8D77; # (起起; 起; 起; 起; 起; ) CJK COMPATIBILITY IDEOGRAPH-2F9D7
+2F9D8;27F2F;27F2F;27F2F;27F2F; # (𧼯𧼯; 𧼯𧼯; 𧼯𧼯; 𧼯𧼯; 𧼯𧼯; ) CJK COMPATIBILITY IDEOGRAPH-2F9D8
+2F9D9;20804;20804;20804;20804; # (𠠄𠠄; 𠠄𠠄; 𠠄𠠄; 𠠄𠠄; 𠠄𠠄; ) CJK COMPATIBILITY IDEOGRAPH-2F9D9
+2F9DA;8DCB;8DCB;8DCB;8DCB; # (跋跋; 跋; 跋; 跋; 跋; ) CJK COMPATIBILITY IDEOGRAPH-2F9DA
+2F9DB;8DBC;8DBC;8DBC;8DBC; # (趼趼; 趼; 趼; 趼; 趼; ) CJK COMPATIBILITY IDEOGRAPH-2F9DB
+2F9DC;8DF0;8DF0;8DF0;8DF0; # (跰跰; 跰; 跰; 跰; 跰; ) CJK COMPATIBILITY IDEOGRAPH-2F9DC
+2F9DD;208DE;208DE;208DE;208DE; # (ð¯§ð¯§; 𠣞𠣞; 𠣞𠣞; 𠣞𠣞; 𠣞𠣞; ) CJK COMPATIBILITY IDEOGRAPH-2F9DD
+2F9DE;8ED4;8ED4;8ED4;8ED4; # (軔軔; 軔; 軔; 軔; 軔; ) CJK COMPATIBILITY IDEOGRAPH-2F9DE
+2F9DF;8F38;8F38;8F38;8F38; # (輸輸; 輸; 輸; 輸; 輸; ) CJK COMPATIBILITY IDEOGRAPH-2F9DF
+2F9E0;285D2;285D2;285D2;285D2; # (𨗒𨗒; 𨗒𨗒; 𨗒𨗒; 𨗒𨗒; 𨗒𨗒; ) CJK COMPATIBILITY IDEOGRAPH-2F9E0
+2F9E1;285ED;285ED;285ED;285ED; # (𨗭𨗭; 𨗭𨗭; 𨗭𨗭; 𨗭𨗭; 𨗭𨗭; ) CJK COMPATIBILITY IDEOGRAPH-2F9E1
+2F9E2;9094;9094;9094;9094; # (邔邔; 邔; 邔; 邔; 邔; ) CJK COMPATIBILITY IDEOGRAPH-2F9E2
+2F9E3;90F1;90F1;90F1;90F1; # (郱郱; 郱; 郱; 郱; 郱; ) CJK COMPATIBILITY IDEOGRAPH-2F9E3
+2F9E4;9111;9111;9111;9111; # (鄑鄑; 鄑; 鄑; 鄑; 鄑; ) CJK COMPATIBILITY IDEOGRAPH-2F9E4
+2F9E5;2872E;2872E;2872E;2872E; # (𨜮𨜮; 𨜮𨜮; 𨜮𨜮; 𨜮𨜮; 𨜮𨜮; ) CJK COMPATIBILITY IDEOGRAPH-2F9E5
+2F9E6;911B;911B;911B;911B; # (鄛鄛; 鄛; 鄛; 鄛; 鄛; ) CJK COMPATIBILITY IDEOGRAPH-2F9E6
+2F9E7;9238;9238;9238;9238; # (鈸鈸; 鈸; 鈸; 鈸; 鈸; ) CJK COMPATIBILITY IDEOGRAPH-2F9E7
+2F9E8;92D7;92D7;92D7;92D7; # (鋗鋗; 鋗; 鋗; 鋗; 鋗; ) CJK COMPATIBILITY IDEOGRAPH-2F9E8
+2F9E9;92D8;92D8;92D8;92D8; # (鋘鋘; 鋘; 鋘; 鋘; 鋘; ) CJK COMPATIBILITY IDEOGRAPH-2F9E9
+2F9EA;927C;927C;927C;927C; # (鉼鉼; 鉼; 鉼; 鉼; 鉼; ) CJK COMPATIBILITY IDEOGRAPH-2F9EA
+2F9EB;93F9;93F9;93F9;93F9; # (鏹鏹; é¹; é¹; é¹; é¹; ) CJK COMPATIBILITY IDEOGRAPH-2F9EB
+2F9EC;9415;9415;9415;9415; # (鐕鐕; é•; é•; é•; é•; ) CJK COMPATIBILITY IDEOGRAPH-2F9EC
+2F9ED;28BFA;28BFA;28BFA;28BFA; # (𨯺𨯺; 𨯺𨯺; 𨯺𨯺; 𨯺𨯺; 𨯺𨯺; ) CJK COMPATIBILITY IDEOGRAPH-2F9ED
+2F9EE;958B;958B;958B;958B; # (開開; 開; 開; 開; 開; ) CJK COMPATIBILITY IDEOGRAPH-2F9EE
+2F9EF;4995;4995;4995;4995; # (䦕䦕; 䦕; 䦕; 䦕; 䦕; ) CJK COMPATIBILITY IDEOGRAPH-2F9EF
+2F9F0;95B7;95B7;95B7;95B7; # (閷閷; 閷; 閷; 閷; 閷; ) CJK COMPATIBILITY IDEOGRAPH-2F9F0
+2F9F1;28D77;28D77;28D77;28D77; # (𨵷𨵷; 𨵷𨵷; 𨵷𨵷; 𨵷𨵷; 𨵷𨵷; ) CJK COMPATIBILITY IDEOGRAPH-2F9F1
+2F9F2;49E6;49E6;49E6;49E6; # (䧦䧦; 䧦; 䧦; 䧦; 䧦; ) CJK COMPATIBILITY IDEOGRAPH-2F9F2
+2F9F3;96C3;96C3;96C3;96C3; # (雃雃; 雃; 雃; 雃; 雃; ) CJK COMPATIBILITY IDEOGRAPH-2F9F3
+2F9F4;5DB2;5DB2;5DB2;5DB2; # (嶲嶲; 嶲; 嶲; 嶲; 嶲; ) CJK COMPATIBILITY IDEOGRAPH-2F9F4
+2F9F5;9723;9723;9723;9723; # (霣霣; 霣; 霣; 霣; 霣; ) CJK COMPATIBILITY IDEOGRAPH-2F9F5
+2F9F6;29145;29145;29145;29145; # (𩅅𩅅; 𩅅𩅅; 𩅅𩅅; 𩅅𩅅; 𩅅𩅅; ) CJK COMPATIBILITY IDEOGRAPH-2F9F6
+2F9F7;2921A;2921A;2921A;2921A; # (𩈚𩈚; 𩈚𩈚; 𩈚𩈚; 𩈚𩈚; 𩈚𩈚; ) CJK COMPATIBILITY IDEOGRAPH-2F9F7
+2F9F8;4A6E;4A6E;4A6E;4A6E; # (䩮䩮; 䩮; 䩮; 䩮; 䩮; ) CJK COMPATIBILITY IDEOGRAPH-2F9F8
+2F9F9;4A76;4A76;4A76;4A76; # (䩶䩶; 䩶; 䩶; 䩶; 䩶; ) CJK COMPATIBILITY IDEOGRAPH-2F9F9
+2F9FA;97E0;97E0;97E0;97E0; # (韠韠; 韠; 韠; 韠; 韠; ) CJK COMPATIBILITY IDEOGRAPH-2F9FA
+2F9FB;2940A;2940A;2940A;2940A; # (𩐊𩐊; ð©Šð©Š; ð©Šð©Š; ð©Šð©Š; ð©Šð©Š; ) CJK COMPATIBILITY IDEOGRAPH-2F9FB
+2F9FC;4AB2;4AB2;4AB2;4AB2; # (䪲䪲; 䪲; 䪲; 䪲; 䪲; ) CJK COMPATIBILITY IDEOGRAPH-2F9FC
+2F9FD;29496;29496;29496;29496; # (𩒖𩒖; 𩒖𩒖; 𩒖𩒖; 𩒖𩒖; 𩒖𩒖; ) CJK COMPATIBILITY IDEOGRAPH-2F9FD
+2F9FE;980B;980B;980B;980B; # (頋頋; 頋; 頋; 頋; 頋; ) CJK COMPATIBILITY IDEOGRAPH-2F9FE
+2F9FF;980B;980B;980B;980B; # (頋頋; 頋; 頋; 頋; 頋; ) CJK COMPATIBILITY IDEOGRAPH-2F9FF
+2FA00;9829;9829;9829;9829; # (頩頩; 頩; 頩; 頩; 頩; ) CJK COMPATIBILITY IDEOGRAPH-2FA00
+2FA01;295B6;295B6;295B6;295B6; # (ð¯¨ð¯¨; ð©–¶ð©–¶; ð©–¶ð©–¶; ð©–¶ð©–¶; ð©–¶ð©–¶; ) CJK COMPATIBILITY IDEOGRAPH-2FA01
+2FA02;98E2;98E2;98E2;98E2; # (飢飢; 飢; 飢; 飢; 飢; ) CJK COMPATIBILITY IDEOGRAPH-2FA02
+2FA03;4B33;4B33;4B33;4B33; # (䬳䬳; 䬳; 䬳; 䬳; 䬳; ) CJK COMPATIBILITY IDEOGRAPH-2FA03
+2FA04;9929;9929;9929;9929; # (餩餩; 餩; 餩; 餩; 餩; ) CJK COMPATIBILITY IDEOGRAPH-2FA04
+2FA05;99A7;99A7;99A7;99A7; # (馧馧; 馧; 馧; 馧; 馧; ) CJK COMPATIBILITY IDEOGRAPH-2FA05
+2FA06;99C2;99C2;99C2;99C2; # (駂駂; 駂; 駂; 駂; 駂; ) CJK COMPATIBILITY IDEOGRAPH-2FA06
+2FA07;99FE;99FE;99FE;99FE; # (駾駾; 駾; 駾; 駾; 駾; ) CJK COMPATIBILITY IDEOGRAPH-2FA07
+2FA08;4BCE;4BCE;4BCE;4BCE; # (䯎䯎; 䯎; 䯎; 䯎; 䯎; ) CJK COMPATIBILITY IDEOGRAPH-2FA08
+2FA09;29B30;29B30;29B30;29B30; # (𩬰𩬰; 𩬰𩬰; 𩬰𩬰; 𩬰𩬰; 𩬰𩬰; ) CJK COMPATIBILITY IDEOGRAPH-2FA09
+2FA0A;9B12;9B12;9B12;9B12; # (鬒鬒; 鬒; 鬒; 鬒; 鬒; ) CJK COMPATIBILITY IDEOGRAPH-2FA0A
+2FA0B;9C40;9C40;9C40;9C40; # (鱀鱀; 鱀; 鱀; 鱀; 鱀; ) CJK COMPATIBILITY IDEOGRAPH-2FA0B
+2FA0C;9CFD;9CFD;9CFD;9CFD; # (鳽鳽; 鳽; 鳽; 鳽; 鳽; ) CJK COMPATIBILITY IDEOGRAPH-2FA0C
+2FA0D;4CCE;4CCE;4CCE;4CCE; # (ð¯¨ð¯¨; 䳎; 䳎; 䳎; 䳎; ) CJK COMPATIBILITY IDEOGRAPH-2FA0D
+2FA0E;4CED;4CED;4CED;4CED; # (䳭䳭; 䳭; 䳭; 䳭; 䳭; ) CJK COMPATIBILITY IDEOGRAPH-2FA0E
+2FA0F;9D67;9D67;9D67;9D67; # (ð¯¨ð¯¨; éµ§; éµ§; éµ§; éµ§; ) CJK COMPATIBILITY IDEOGRAPH-2FA0F
+2FA10;2A0CE;2A0CE;2A0CE;2A0CE; # (ð¯¨ð¯¨; 𪃎𪃎; 𪃎𪃎; 𪃎𪃎; 𪃎𪃎; ) CJK COMPATIBILITY IDEOGRAPH-2FA10
+2FA11;4CF8;4CF8;4CF8;4CF8; # (䳸䳸; 䳸; 䳸; 䳸; 䳸; ) CJK COMPATIBILITY IDEOGRAPH-2FA11
+2FA12;2A105;2A105;2A105;2A105; # (𪄅𪄅; 𪄅𪄅; 𪄅𪄅; 𪄅𪄅; 𪄅𪄅; ) CJK COMPATIBILITY IDEOGRAPH-2FA12
+2FA13;2A20E;2A20E;2A20E;2A20E; # (𪈎𪈎; 𪈎𪈎; 𪈎𪈎; 𪈎𪈎; 𪈎𪈎; ) CJK COMPATIBILITY IDEOGRAPH-2FA13
+2FA14;2A291;2A291;2A291;2A291; # (𪊑𪊑; 𪊑𪊑; 𪊑𪊑; 𪊑𪊑; 𪊑𪊑; ) CJK COMPATIBILITY IDEOGRAPH-2FA14
+2FA15;9EBB;9EBB;9EBB;9EBB; # (麻麻; 麻; 麻; 麻; 麻; ) CJK COMPATIBILITY IDEOGRAPH-2FA15
+2FA16;4D56;4D56;4D56;4D56; # (䵖䵖; 䵖; 䵖; 䵖; 䵖; ) CJK COMPATIBILITY IDEOGRAPH-2FA16
+2FA17;9EF9;9EF9;9EF9;9EF9; # (黹黹; 黹; 黹; 黹; 黹; ) CJK COMPATIBILITY IDEOGRAPH-2FA17
+2FA18;9EFE;9EFE;9EFE;9EFE; # (黾黾; 黾; 黾; 黾; 黾; ) CJK COMPATIBILITY IDEOGRAPH-2FA18
+2FA19;9F05;9F05;9F05;9F05; # (鼅鼅; 鼅; 鼅; 鼅; 鼅; ) CJK COMPATIBILITY IDEOGRAPH-2FA19
+2FA1A;9F0F;9F0F;9F0F;9F0F; # (鼏鼏; é¼; é¼; é¼; é¼; ) CJK COMPATIBILITY IDEOGRAPH-2FA1A
+2FA1B;9F16;9F16;9F16;9F16; # (鼖鼖; 鼖; 鼖; 鼖; 鼖; ) CJK COMPATIBILITY IDEOGRAPH-2FA1B
+2FA1C;9F3B;9F3B;9F3B;9F3B; # (鼻鼻; 鼻; 鼻; 鼻; 鼻; ) CJK COMPATIBILITY IDEOGRAPH-2FA1C
+2FA1D;2A600;2A600;2A600;2A600; # (ð¯¨ð¯¨; 𪘀𪘀; 𪘀𪘀; 𪘀𪘀; 𪘀𪘀; ) CJK COMPATIBILITY IDEOGRAPH-2FA1D
+#
+@Part2 # Canonical Order Test
+#
+0061 0315 0300 05AE 0300 0062;00E0 05AE 0300 0315 0062;0061 05AE 0300 0300 0315 0062;00E0 05AE 0300 0315 0062;0061 05AE 0300 0300 0315 0062; # (a◌̕◌̀◌֮◌̀b; à◌֮◌̀◌̕b; a◌֮◌̀◌̀◌̕b; à◌֮◌̀◌̕b; a◌֮◌̀◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING GRAVE ACCENT, LATIN SMALL LETTER B
+0061 0300 0315 0300 05AE 0062;00E0 05AE 0300 0315 0062;0061 05AE 0300 0300 0315 0062;00E0 05AE 0300 0315 0062;0061 05AE 0300 0300 0315 0062; # (a◌̀◌̕◌̀◌֮b; à◌֮◌̀◌̕b; a◌֮◌̀◌̀◌̕b; à◌֮◌̀◌̕b; a◌֮◌̀◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING GRAVE ACCENT, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0301 0062;00E0 05AE 0301 0315 0062;0061 05AE 0300 0301 0315 0062;00E0 05AE 0301 0315 0062;0061 05AE 0300 0301 0315 0062; # (a◌̕◌̀◌֮◌Ìb; à◌֮◌Ì◌̕b; a◌֮◌̀◌Ì◌̕b; à◌֮◌Ì◌̕b; a◌֮◌̀◌Ì◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING ACUTE ACCENT, LATIN SMALL LETTER B
+0061 0301 0315 0300 05AE 0062;00E1 05AE 0300 0315 0062;0061 05AE 0301 0300 0315 0062;00E1 05AE 0300 0315 0062;0061 05AE 0301 0300 0315 0062; # (aâ—ŒÌ◌̕◌̀◌֮b; á◌֮◌̀◌̕b; a◌֮◌Ì◌̀◌̕b; á◌֮◌̀◌̕b; a◌֮◌Ì◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING ACUTE ACCENT, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0302 0062;00E0 05AE 0302 0315 0062;0061 05AE 0300 0302 0315 0062;00E0 05AE 0302 0315 0062;0061 05AE 0300 0302 0315 0062; # (a◌̕◌̀◌֮◌̂b; à◌֮◌̂◌̕b; a◌֮◌̀◌̂◌̕b; à◌֮◌̂◌̕b; a◌֮◌̀◌̂◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING CIRCUMFLEX ACCENT, LATIN SMALL LETTER B
+0061 0302 0315 0300 05AE 0062;1EA7 05AE 0315 0062;0061 05AE 0302 0300 0315 0062;1EA7 05AE 0315 0062;0061 05AE 0302 0300 0315 0062; # (a◌̂◌̕◌̀◌֮b; ầ◌֮◌̕b; a◌֮◌̂◌̀◌̕b; ầ◌֮◌̕b; a◌֮◌̂◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING CIRCUMFLEX ACCENT, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0303 0062;00E0 05AE 0303 0315 0062;0061 05AE 0300 0303 0315 0062;00E0 05AE 0303 0315 0062;0061 05AE 0300 0303 0315 0062; # (a◌̕◌̀◌֮◌̃b; à◌֮◌̃◌̕b; a◌֮◌̀◌̃◌̕b; à◌֮◌̃◌̕b; a◌֮◌̀◌̃◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING TILDE, LATIN SMALL LETTER B
+0061 0303 0315 0300 05AE 0062;00E3 05AE 0300 0315 0062;0061 05AE 0303 0300 0315 0062;00E3 05AE 0300 0315 0062;0061 05AE 0303 0300 0315 0062; # (a◌̃◌̕◌̀◌֮b; ã◌֮◌̀◌̕b; a◌֮◌̃◌̀◌̕b; ã◌֮◌̀◌̕b; a◌֮◌̃◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING TILDE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0304 0062;00E0 05AE 0304 0315 0062;0061 05AE 0300 0304 0315 0062;00E0 05AE 0304 0315 0062;0061 05AE 0300 0304 0315 0062; # (a◌̕◌̀◌֮◌̄b; à◌֮◌̄◌̕b; a◌֮◌̀◌̄◌̕b; à◌֮◌̄◌̕b; a◌֮◌̀◌̄◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING MACRON, LATIN SMALL LETTER B
+0061 0304 0315 0300 05AE 0062;0101 05AE 0300 0315 0062;0061 05AE 0304 0300 0315 0062;0101 05AE 0300 0315 0062;0061 05AE 0304 0300 0315 0062; # (a◌̄◌̕◌̀◌֮b; Ä◌֮◌̀◌̕b; a◌֮◌̄◌̀◌̕b; Ä◌֮◌̀◌̕b; a◌֮◌̄◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING MACRON, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0305 0062;00E0 05AE 0305 0315 0062;0061 05AE 0300 0305 0315 0062;00E0 05AE 0305 0315 0062;0061 05AE 0300 0305 0315 0062; # (a◌̕◌̀◌֮◌̅b; à◌֮◌̅◌̕b; a◌֮◌̀◌̅◌̕b; à◌֮◌̅◌̕b; a◌֮◌̀◌̅◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING OVERLINE, LATIN SMALL LETTER B
+0061 0305 0315 0300 05AE 0062;0061 05AE 0305 0300 0315 0062;0061 05AE 0305 0300 0315 0062;0061 05AE 0305 0300 0315 0062;0061 05AE 0305 0300 0315 0062; # (a◌̅◌̕◌̀◌֮b; a◌֮◌̅◌̀◌̕b; a◌֮◌̅◌̀◌̕b; a◌֮◌̅◌̀◌̕b; a◌֮◌̅◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING OVERLINE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0306 0062;00E0 05AE 0306 0315 0062;0061 05AE 0300 0306 0315 0062;00E0 05AE 0306 0315 0062;0061 05AE 0300 0306 0315 0062; # (a◌̕◌̀◌֮◌̆b; à◌֮◌̆◌̕b; a◌֮◌̀◌̆◌̕b; à◌֮◌̆◌̕b; a◌֮◌̀◌̆◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING BREVE, LATIN SMALL LETTER B
+0061 0306 0315 0300 05AE 0062;1EB1 05AE 0315 0062;0061 05AE 0306 0300 0315 0062;1EB1 05AE 0315 0062;0061 05AE 0306 0300 0315 0062; # (a◌̆◌̕◌̀◌֮b; ằ◌֮◌̕b; a◌֮◌̆◌̀◌̕b; ằ◌֮◌̕b; a◌֮◌̆◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING BREVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0307 0062;00E0 05AE 0307 0315 0062;0061 05AE 0300 0307 0315 0062;00E0 05AE 0307 0315 0062;0061 05AE 0300 0307 0315 0062; # (a◌̕◌̀◌֮◌̇b; à◌֮◌̇◌̕b; a◌֮◌̀◌̇◌̕b; à◌֮◌̇◌̕b; a◌֮◌̀◌̇◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING DOT ABOVE, LATIN SMALL LETTER B
+0061 0307 0315 0300 05AE 0062;0227 05AE 0300 0315 0062;0061 05AE 0307 0300 0315 0062;0227 05AE 0300 0315 0062;0061 05AE 0307 0300 0315 0062; # (a◌̇◌̕◌̀◌֮b; ȧ◌֮◌̀◌̕b; a◌֮◌̇◌̀◌̕b; ȧ◌֮◌̀◌̕b; a◌֮◌̇◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING DOT ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0308 0062;00E0 05AE 0308 0315 0062;0061 05AE 0300 0308 0315 0062;00E0 05AE 0308 0315 0062;0061 05AE 0300 0308 0315 0062; # (a◌̕◌̀◌֮◌̈b; à◌֮◌̈◌̕b; a◌֮◌̀◌̈◌̕b; à◌֮◌̈◌̕b; a◌֮◌̀◌̈◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING DIAERESIS, LATIN SMALL LETTER B
+0061 0308 0315 0300 05AE 0062;00E4 05AE 0300 0315 0062;0061 05AE 0308 0300 0315 0062;00E4 05AE 0300 0315 0062;0061 05AE 0308 0300 0315 0062; # (a◌̈◌̕◌̀◌֮b; ä◌֮◌̀◌̕b; a◌֮◌̈◌̀◌̕b; ä◌֮◌̀◌̕b; a◌֮◌̈◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING DIAERESIS, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0309 0062;00E0 05AE 0309 0315 0062;0061 05AE 0300 0309 0315 0062;00E0 05AE 0309 0315 0062;0061 05AE 0300 0309 0315 0062; # (a◌̕◌̀◌֮◌̉b; à◌֮◌̉◌̕b; a◌֮◌̀◌̉◌̕b; à◌֮◌̉◌̕b; a◌֮◌̀◌̉◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING HOOK ABOVE, LATIN SMALL LETTER B
+0061 0309 0315 0300 05AE 0062;1EA3 05AE 0300 0315 0062;0061 05AE 0309 0300 0315 0062;1EA3 05AE 0300 0315 0062;0061 05AE 0309 0300 0315 0062; # (a◌̉◌̕◌̀◌֮b; ả◌֮◌̀◌̕b; a◌֮◌̉◌̀◌̕b; ả◌֮◌̀◌̕b; a◌֮◌̉◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING HOOK ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 030A 0062;00E0 05AE 030A 0315 0062;0061 05AE 0300 030A 0315 0062;00E0 05AE 030A 0315 0062;0061 05AE 0300 030A 0315 0062; # (a◌̕◌̀◌֮◌̊b; à◌֮◌̊◌̕b; a◌֮◌̀◌̊◌̕b; à◌֮◌̊◌̕b; a◌֮◌̀◌̊◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING RING ABOVE, LATIN SMALL LETTER B
+0061 030A 0315 0300 05AE 0062;00E5 05AE 0300 0315 0062;0061 05AE 030A 0300 0315 0062;00E5 05AE 0300 0315 0062;0061 05AE 030A 0300 0315 0062; # (a◌̊◌̕◌̀◌֮b; å◌֮◌̀◌̕b; a◌֮◌̊◌̀◌̕b; å◌֮◌̀◌̕b; a◌֮◌̊◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING RING ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 030B 0062;00E0 05AE 030B 0315 0062;0061 05AE 0300 030B 0315 0062;00E0 05AE 030B 0315 0062;0061 05AE 0300 030B 0315 0062; # (a◌̕◌̀◌֮◌̋b; à◌֮◌̋◌̕b; a◌֮◌̀◌̋◌̕b; à◌֮◌̋◌̕b; a◌֮◌̀◌̋◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING DOUBLE ACUTE ACCENT, LATIN SMALL LETTER B
+0061 030B 0315 0300 05AE 0062;0061 05AE 030B 0300 0315 0062;0061 05AE 030B 0300 0315 0062;0061 05AE 030B 0300 0315 0062;0061 05AE 030B 0300 0315 0062; # (a◌̋◌̕◌̀◌֮b; a◌֮◌̋◌̀◌̕b; a◌֮◌̋◌̀◌̕b; a◌֮◌̋◌̀◌̕b; a◌֮◌̋◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING DOUBLE ACUTE ACCENT, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 030C 0062;00E0 05AE 030C 0315 0062;0061 05AE 0300 030C 0315 0062;00E0 05AE 030C 0315 0062;0061 05AE 0300 030C 0315 0062; # (a◌̕◌̀◌֮◌̌b; à◌֮◌̌◌̕b; a◌֮◌̀◌̌◌̕b; à◌֮◌̌◌̕b; a◌֮◌̀◌̌◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING CARON, LATIN SMALL LETTER B
+0061 030C 0315 0300 05AE 0062;01CE 05AE 0300 0315 0062;0061 05AE 030C 0300 0315 0062;01CE 05AE 0300 0315 0062;0061 05AE 030C 0300 0315 0062; # (a◌̌◌̕◌̀◌֮b; ǎ◌֮◌̀◌̕b; a◌֮◌̌◌̀◌̕b; ǎ◌֮◌̀◌̕b; a◌֮◌̌◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING CARON, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 030D 0062;00E0 05AE 030D 0315 0062;0061 05AE 0300 030D 0315 0062;00E0 05AE 030D 0315 0062;0061 05AE 0300 030D 0315 0062; # (a◌̕◌̀◌֮◌Ìb; à◌֮◌Ì◌̕b; a◌֮◌̀◌Ì◌̕b; à◌֮◌Ì◌̕b; a◌֮◌̀◌Ì◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING VERTICAL LINE ABOVE, LATIN SMALL LETTER B
+0061 030D 0315 0300 05AE 0062;0061 05AE 030D 0300 0315 0062;0061 05AE 030D 0300 0315 0062;0061 05AE 030D 0300 0315 0062;0061 05AE 030D 0300 0315 0062; # (aâ—ŒÌ◌̕◌̀◌֮b; a◌֮◌Ì◌̀◌̕b; a◌֮◌Ì◌̀◌̕b; a◌֮◌Ì◌̀◌̕b; a◌֮◌Ì◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING VERTICAL LINE ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 030E 0062;00E0 05AE 030E 0315 0062;0061 05AE 0300 030E 0315 0062;00E0 05AE 030E 0315 0062;0061 05AE 0300 030E 0315 0062; # (a◌̕◌̀◌֮◌̎b; à◌֮◌̎◌̕b; a◌֮◌̀◌̎◌̕b; à◌֮◌̎◌̕b; a◌֮◌̀◌̎◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING DOUBLE VERTICAL LINE ABOVE, LATIN SMALL LETTER B
+0061 030E 0315 0300 05AE 0062;0061 05AE 030E 0300 0315 0062;0061 05AE 030E 0300 0315 0062;0061 05AE 030E 0300 0315 0062;0061 05AE 030E 0300 0315 0062; # (a◌̎◌̕◌̀◌֮b; a◌֮◌̎◌̀◌̕b; a◌֮◌̎◌̀◌̕b; a◌֮◌̎◌̀◌̕b; a◌֮◌̎◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING DOUBLE VERTICAL LINE ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 030F 0062;00E0 05AE 030F 0315 0062;0061 05AE 0300 030F 0315 0062;00E0 05AE 030F 0315 0062;0061 05AE 0300 030F 0315 0062; # (a◌̕◌̀◌֮◌Ìb; à◌֮◌Ì◌̕b; a◌֮◌̀◌Ì◌̕b; à◌֮◌Ì◌̕b; a◌֮◌̀◌Ì◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING DOUBLE GRAVE ACCENT, LATIN SMALL LETTER B
+0061 030F 0315 0300 05AE 0062;0201 05AE 0300 0315 0062;0061 05AE 030F 0300 0315 0062;0201 05AE 0300 0315 0062;0061 05AE 030F 0300 0315 0062; # (aâ—ŒÌ◌̕◌̀◌֮b; È◌֮◌̀◌̕b; a◌֮◌Ì◌̀◌̕b; È◌֮◌̀◌̕b; a◌֮◌Ì◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING DOUBLE GRAVE ACCENT, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0310 0062;00E0 05AE 0310 0315 0062;0061 05AE 0300 0310 0315 0062;00E0 05AE 0310 0315 0062;0061 05AE 0300 0310 0315 0062; # (a◌̕◌̀◌֮◌Ìb; à◌֮◌Ì◌̕b; a◌֮◌̀◌Ì◌̕b; à◌֮◌Ì◌̕b; a◌֮◌̀◌Ì◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING CANDRABINDU, LATIN SMALL LETTER B
+0061 0310 0315 0300 05AE 0062;0061 05AE 0310 0300 0315 0062;0061 05AE 0310 0300 0315 0062;0061 05AE 0310 0300 0315 0062;0061 05AE 0310 0300 0315 0062; # (aâ—ŒÌ◌̕◌̀◌֮b; a◌֮◌Ì◌̀◌̕b; a◌֮◌Ì◌̀◌̕b; a◌֮◌Ì◌̀◌̕b; a◌֮◌Ì◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING CANDRABINDU, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0311 0062;00E0 05AE 0311 0315 0062;0061 05AE 0300 0311 0315 0062;00E0 05AE 0311 0315 0062;0061 05AE 0300 0311 0315 0062; # (a◌̕◌̀◌֮◌̑b; à◌֮◌̑◌̕b; a◌֮◌̀◌̑◌̕b; à◌֮◌̑◌̕b; a◌֮◌̀◌̑◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING INVERTED BREVE, LATIN SMALL LETTER B
+0061 0311 0315 0300 05AE 0062;0203 05AE 0300 0315 0062;0061 05AE 0311 0300 0315 0062;0203 05AE 0300 0315 0062;0061 05AE 0311 0300 0315 0062; # (a◌̑◌̕◌̀◌֮b; ȃ◌֮◌̀◌̕b; a◌֮◌̑◌̀◌̕b; ȃ◌֮◌̀◌̕b; a◌֮◌̑◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING INVERTED BREVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0312 0062;00E0 05AE 0312 0315 0062;0061 05AE 0300 0312 0315 0062;00E0 05AE 0312 0315 0062;0061 05AE 0300 0312 0315 0062; # (a◌̕◌̀◌֮◌̒b; à◌֮◌̒◌̕b; a◌֮◌̀◌̒◌̕b; à◌֮◌̒◌̕b; a◌֮◌̀◌̒◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING TURNED COMMA ABOVE, LATIN SMALL LETTER B
+0061 0312 0315 0300 05AE 0062;0061 05AE 0312 0300 0315 0062;0061 05AE 0312 0300 0315 0062;0061 05AE 0312 0300 0315 0062;0061 05AE 0312 0300 0315 0062; # (a◌̒◌̕◌̀◌֮b; a◌֮◌̒◌̀◌̕b; a◌֮◌̒◌̀◌̕b; a◌֮◌̒◌̀◌̕b; a◌֮◌̒◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING TURNED COMMA ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0313 0062;00E0 05AE 0313 0315 0062;0061 05AE 0300 0313 0315 0062;00E0 05AE 0313 0315 0062;0061 05AE 0300 0313 0315 0062; # (a◌̕◌̀◌֮◌̓b; à◌֮◌̓◌̕b; a◌֮◌̀◌̓◌̕b; à◌֮◌̓◌̕b; a◌֮◌̀◌̓◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING COMMA ABOVE, LATIN SMALL LETTER B
+0061 0313 0315 0300 05AE 0062;0061 05AE 0313 0300 0315 0062;0061 05AE 0313 0300 0315 0062;0061 05AE 0313 0300 0315 0062;0061 05AE 0313 0300 0315 0062; # (a◌̓◌̕◌̀◌֮b; a◌֮◌̓◌̀◌̕b; a◌֮◌̓◌̀◌̕b; a◌֮◌̓◌̀◌̕b; a◌֮◌̓◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0314 0062;00E0 05AE 0314 0315 0062;0061 05AE 0300 0314 0315 0062;00E0 05AE 0314 0315 0062;0061 05AE 0300 0314 0315 0062; # (a◌̕◌̀◌֮◌̔b; à◌֮◌̔◌̕b; a◌֮◌̀◌̔◌̕b; à◌֮◌̔◌̕b; a◌֮◌̀◌̔◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING REVERSED COMMA ABOVE, LATIN SMALL LETTER B
+0061 0314 0315 0300 05AE 0062;0061 05AE 0314 0300 0315 0062;0061 05AE 0314 0300 0315 0062;0061 05AE 0314 0300 0315 0062;0061 05AE 0314 0300 0315 0062; # (a◌̔◌̕◌̀◌֮b; a◌֮◌̔◌̀◌̕b; a◌֮◌̔◌̀◌̕b; a◌֮◌̔◌̀◌̕b; a◌֮◌̔◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING REVERSED COMMA ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 035F 0315 0300 0315 0062;00E0 0315 0315 035F 0062;0061 0300 0315 0315 035F 0062;00E0 0315 0315 035F 0062;0061 0300 0315 0315 035F 0062; # (a◌͟◌̕◌̀◌̕b; à◌̕◌̕◌͟b; a◌̀◌̕◌̕◌͟b; à◌̕◌̕◌͟b; a◌̀◌̕◌̕◌͟b; ) LATIN SMALL LETTER A, COMBINING DOUBLE MACRON BELOW, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, COMBINING COMMA ABOVE RIGHT, LATIN SMALL LETTER B
+0061 0315 035F 0315 0300 0062;00E0 0315 0315 035F 0062;0061 0300 0315 0315 035F 0062;00E0 0315 0315 035F 0062;0061 0300 0315 0315 035F 0062; # (a◌̕◌͟◌̕◌̀b; à◌̕◌̕◌͟b; a◌̀◌̕◌̕◌͟b; à◌̕◌̕◌͟b; a◌̀◌̕◌̕◌͟b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING DOUBLE MACRON BELOW, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, LATIN SMALL LETTER B
+0061 059A 0316 302A 0316 0062;0061 302A 0316 0316 059A 0062;0061 302A 0316 0316 059A 0062;0061 302A 0316 0316 059A 0062;0061 302A 0316 0316 059A 0062; # (a◌֚◌̖◌〪◌̖b; a◌〪◌̖◌̖◌֚b; a◌〪◌̖◌̖◌֚b; a◌〪◌̖◌̖◌֚b; a◌〪◌̖◌̖◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING GRAVE ACCENT BELOW, LATIN SMALL LETTER B
+0061 0316 059A 0316 302A 0062;0061 302A 0316 0316 059A 0062;0061 302A 0316 0316 059A 0062;0061 302A 0316 0316 059A 0062;0061 302A 0316 0316 059A 0062; # (a◌̖◌֚◌̖◌〪b; a◌〪◌̖◌̖◌֚b; a◌〪◌̖◌̖◌֚b; a◌〪◌̖◌̖◌֚b; a◌〪◌̖◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING GRAVE ACCENT BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 059A 0316 302A 0317 0062;0061 302A 0316 0317 059A 0062;0061 302A 0316 0317 059A 0062;0061 302A 0316 0317 059A 0062;0061 302A 0316 0317 059A 0062; # (a◌֚◌̖◌〪◌̗b; a◌〪◌̖◌̗◌֚b; a◌〪◌̖◌̗◌֚b; a◌〪◌̖◌̗◌֚b; a◌〪◌̖◌̗◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING ACUTE ACCENT BELOW, LATIN SMALL LETTER B
+0061 0317 059A 0316 302A 0062;0061 302A 0317 0316 059A 0062;0061 302A 0317 0316 059A 0062;0061 302A 0317 0316 059A 0062;0061 302A 0317 0316 059A 0062; # (a◌̗◌֚◌̖◌〪b; a◌〪◌̗◌̖◌֚b; a◌〪◌̗◌̖◌֚b; a◌〪◌̗◌̖◌֚b; a◌〪◌̗◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING ACUTE ACCENT BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 059A 0316 302A 0318 0062;0061 302A 0316 0318 059A 0062;0061 302A 0316 0318 059A 0062;0061 302A 0316 0318 059A 0062;0061 302A 0316 0318 059A 0062; # (a◌֚◌̖◌〪◌̘b; a◌〪◌̖◌̘◌֚b; a◌〪◌̖◌̘◌֚b; a◌〪◌̖◌̘◌֚b; a◌〪◌̖◌̘◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING LEFT TACK BELOW, LATIN SMALL LETTER B
+0061 0318 059A 0316 302A 0062;0061 302A 0318 0316 059A 0062;0061 302A 0318 0316 059A 0062;0061 302A 0318 0316 059A 0062;0061 302A 0318 0316 059A 0062; # (a◌̘◌֚◌̖◌〪b; a◌〪◌̘◌̖◌֚b; a◌〪◌̘◌̖◌֚b; a◌〪◌̘◌̖◌֚b; a◌〪◌̘◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING LEFT TACK BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 059A 0316 302A 0319 0062;0061 302A 0316 0319 059A 0062;0061 302A 0316 0319 059A 0062;0061 302A 0316 0319 059A 0062;0061 302A 0316 0319 059A 0062; # (a◌֚◌̖◌〪◌̙b; a◌〪◌̖◌̙◌֚b; a◌〪◌̖◌̙◌֚b; a◌〪◌̖◌̙◌֚b; a◌〪◌̖◌̙◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING RIGHT TACK BELOW, LATIN SMALL LETTER B
+0061 0319 059A 0316 302A 0062;0061 302A 0319 0316 059A 0062;0061 302A 0319 0316 059A 0062;0061 302A 0319 0316 059A 0062;0061 302A 0319 0316 059A 0062; # (a◌̙◌֚◌̖◌〪b; a◌〪◌̙◌̖◌֚b; a◌〪◌̙◌̖◌֚b; a◌〪◌̙◌̖◌֚b; a◌〪◌̙◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING RIGHT TACK BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 035F 0315 0300 031A 0062;00E0 0315 031A 035F 0062;0061 0300 0315 031A 035F 0062;00E0 0315 031A 035F 0062;0061 0300 0315 031A 035F 0062; # (a◌͟◌̕◌̀◌̚b; à◌̕◌̚◌͟b; a◌̀◌̕◌̚◌͟b; à◌̕◌̚◌͟b; a◌̀◌̕◌̚◌͟b; ) LATIN SMALL LETTER A, COMBINING DOUBLE MACRON BELOW, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, COMBINING LEFT ANGLE ABOVE, LATIN SMALL LETTER B
+0061 031A 035F 0315 0300 0062;00E0 031A 0315 035F 0062;0061 0300 031A 0315 035F 0062;00E0 031A 0315 035F 0062;0061 0300 031A 0315 035F 0062; # (a◌̚◌͟◌̕◌̀b; à◌̚◌̕◌͟b; a◌̀◌̚◌̕◌͟b; à◌̚◌̕◌͟b; a◌̀◌̚◌̕◌͟b; ) LATIN SMALL LETTER A, COMBINING LEFT ANGLE ABOVE, COMBINING DOUBLE MACRON BELOW, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, LATIN SMALL LETTER B
+0061 302A 031B 0321 031B 0062;0061 0321 031B 031B 302A 0062;0061 0321 031B 031B 302A 0062;0061 0321 031B 031B 302A 0062;0061 0321 031B 031B 302A 0062; # (a◌〪◌̛◌̡◌̛b; a◌̡◌̛◌̛◌〪b; a◌̡◌̛◌̛◌〪b; a◌̡◌̛◌̛◌〪b; a◌̡◌̛◌̛◌〪b; ) LATIN SMALL LETTER A, IDEOGRAPHIC LEVEL TONE MARK, COMBINING HORN, COMBINING PALATALIZED HOOK BELOW, COMBINING HORN, LATIN SMALL LETTER B
+0061 031B 302A 031B 0321 0062;0061 0321 031B 031B 302A 0062;0061 0321 031B 031B 302A 0062;0061 0321 031B 031B 302A 0062;0061 0321 031B 031B 302A 0062; # (a◌̛◌〪◌̛◌̡b; a◌̡◌̛◌̛◌〪b; a◌̡◌̛◌̛◌〪b; a◌̡◌̛◌̛◌〪b; a◌̡◌̛◌̛◌〪b; ) LATIN SMALL LETTER A, COMBINING HORN, IDEOGRAPHIC LEVEL TONE MARK, COMBINING HORN, COMBINING PALATALIZED HOOK BELOW, LATIN SMALL LETTER B
+0061 059A 0316 302A 031C 0062;0061 302A 0316 031C 059A 0062;0061 302A 0316 031C 059A 0062;0061 302A 0316 031C 059A 0062;0061 302A 0316 031C 059A 0062; # (a◌֚◌̖◌〪◌̜b; a◌〪◌̖◌̜◌֚b; a◌〪◌̖◌̜◌֚b; a◌〪◌̖◌̜◌֚b; a◌〪◌̖◌̜◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING LEFT HALF RING BELOW, LATIN SMALL LETTER B
+0061 031C 059A 0316 302A 0062;0061 302A 031C 0316 059A 0062;0061 302A 031C 0316 059A 0062;0061 302A 031C 0316 059A 0062;0061 302A 031C 0316 059A 0062; # (a◌̜◌֚◌̖◌〪b; a◌〪◌̜◌̖◌֚b; a◌〪◌̜◌̖◌֚b; a◌〪◌̜◌̖◌֚b; a◌〪◌̜◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING LEFT HALF RING BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 059A 0316 302A 031D 0062;0061 302A 0316 031D 059A 0062;0061 302A 0316 031D 059A 0062;0061 302A 0316 031D 059A 0062;0061 302A 0316 031D 059A 0062; # (a◌֚◌̖◌〪◌Ìb; a◌〪◌̖◌Ì◌֚b; a◌〪◌̖◌Ì◌֚b; a◌〪◌̖◌Ì◌֚b; a◌〪◌̖◌Ì◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING UP TACK BELOW, LATIN SMALL LETTER B
+0061 031D 059A 0316 302A 0062;0061 302A 031D 0316 059A 0062;0061 302A 031D 0316 059A 0062;0061 302A 031D 0316 059A 0062;0061 302A 031D 0316 059A 0062; # (aâ—ŒÌ◌֚◌̖◌〪b; a◌〪◌Ì◌̖◌֚b; a◌〪◌Ì◌̖◌֚b; a◌〪◌Ì◌̖◌֚b; a◌〪◌Ì◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING UP TACK BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 059A 0316 302A 031E 0062;0061 302A 0316 031E 059A 0062;0061 302A 0316 031E 059A 0062;0061 302A 0316 031E 059A 0062;0061 302A 0316 031E 059A 0062; # (a◌֚◌̖◌〪◌̞b; a◌〪◌̖◌̞◌֚b; a◌〪◌̖◌̞◌֚b; a◌〪◌̖◌̞◌֚b; a◌〪◌̖◌̞◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING DOWN TACK BELOW, LATIN SMALL LETTER B
+0061 031E 059A 0316 302A 0062;0061 302A 031E 0316 059A 0062;0061 302A 031E 0316 059A 0062;0061 302A 031E 0316 059A 0062;0061 302A 031E 0316 059A 0062; # (a◌̞◌֚◌̖◌〪b; a◌〪◌̞◌̖◌֚b; a◌〪◌̞◌̖◌֚b; a◌〪◌̞◌̖◌֚b; a◌〪◌̞◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING DOWN TACK BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 059A 0316 302A 031F 0062;0061 302A 0316 031F 059A 0062;0061 302A 0316 031F 059A 0062;0061 302A 0316 031F 059A 0062;0061 302A 0316 031F 059A 0062; # (a◌֚◌̖◌〪◌̟b; a◌〪◌̖◌̟◌֚b; a◌〪◌̖◌̟◌֚b; a◌〪◌̖◌̟◌֚b; a◌〪◌̖◌̟◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING PLUS SIGN BELOW, LATIN SMALL LETTER B
+0061 031F 059A 0316 302A 0062;0061 302A 031F 0316 059A 0062;0061 302A 031F 0316 059A 0062;0061 302A 031F 0316 059A 0062;0061 302A 031F 0316 059A 0062; # (a◌̟◌֚◌̖◌〪b; a◌〪◌̟◌̖◌֚b; a◌〪◌̟◌̖◌֚b; a◌〪◌̟◌̖◌֚b; a◌〪◌̟◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING PLUS SIGN BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 059A 0316 302A 0320 0062;0061 302A 0316 0320 059A 0062;0061 302A 0316 0320 059A 0062;0061 302A 0316 0320 059A 0062;0061 302A 0316 0320 059A 0062; # (a◌֚◌̖◌〪◌̠b; a◌〪◌̖◌̠◌֚b; a◌〪◌̖◌̠◌֚b; a◌〪◌̖◌̠◌֚b; a◌〪◌̖◌̠◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING MINUS SIGN BELOW, LATIN SMALL LETTER B
+0061 0320 059A 0316 302A 0062;0061 302A 0320 0316 059A 0062;0061 302A 0320 0316 059A 0062;0061 302A 0320 0316 059A 0062;0061 302A 0320 0316 059A 0062; # (a◌̠◌֚◌̖◌〪b; a◌〪◌̠◌̖◌֚b; a◌〪◌̠◌̖◌֚b; a◌〪◌̠◌̖◌֚b; a◌〪◌̠◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING MINUS SIGN BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 031B 0321 0F74 0321 0062;0061 0F74 0321 0321 031B 0062;0061 0F74 0321 0321 031B 0062;0061 0F74 0321 0321 031B 0062;0061 0F74 0321 0321 031B 0062; # (a◌̛◌̡◌ུ◌̡b; a◌ུ◌̡◌̡◌̛b; a◌ུ◌̡◌̡◌̛b; a◌ུ◌̡◌̡◌̛b; a◌ུ◌̡◌̡◌̛b; ) LATIN SMALL LETTER A, COMBINING HORN, COMBINING PALATALIZED HOOK BELOW, TIBETAN VOWEL SIGN U, COMBINING PALATALIZED HOOK BELOW, LATIN SMALL LETTER B
+0061 0321 031B 0321 0F74 0062;0061 0F74 0321 0321 031B 0062;0061 0F74 0321 0321 031B 0062;0061 0F74 0321 0321 031B 0062;0061 0F74 0321 0321 031B 0062; # (a◌̡◌̛◌̡◌ུb; a◌ུ◌̡◌̡◌̛b; a◌ུ◌̡◌̡◌̛b; a◌ུ◌̡◌̡◌̛b; a◌ུ◌̡◌̡◌̛b; ) LATIN SMALL LETTER A, COMBINING PALATALIZED HOOK BELOW, COMBINING HORN, COMBINING PALATALIZED HOOK BELOW, TIBETAN VOWEL SIGN U, LATIN SMALL LETTER B
+0061 031B 0321 0F74 0322 0062;0061 0F74 0321 0322 031B 0062;0061 0F74 0321 0322 031B 0062;0061 0F74 0321 0322 031B 0062;0061 0F74 0321 0322 031B 0062; # (a◌̛◌̡◌ུ◌̢b; a◌ུ◌̡◌̢◌̛b; a◌ུ◌̡◌̢◌̛b; a◌ུ◌̡◌̢◌̛b; a◌ུ◌̡◌̢◌̛b; ) LATIN SMALL LETTER A, COMBINING HORN, COMBINING PALATALIZED HOOK BELOW, TIBETAN VOWEL SIGN U, COMBINING RETROFLEX HOOK BELOW, LATIN SMALL LETTER B
+0061 0322 031B 0321 0F74 0062;0061 0F74 0322 0321 031B 0062;0061 0F74 0322 0321 031B 0062;0061 0F74 0322 0321 031B 0062;0061 0F74 0322 0321 031B 0062; # (a◌̢◌̛◌̡◌ུb; a◌ུ◌̢◌̡◌̛b; a◌ུ◌̢◌̡◌̛b; a◌ུ◌̢◌̡◌̛b; a◌ུ◌̢◌̡◌̛b; ) LATIN SMALL LETTER A, COMBINING RETROFLEX HOOK BELOW, COMBINING HORN, COMBINING PALATALIZED HOOK BELOW, TIBETAN VOWEL SIGN U, LATIN SMALL LETTER B
+0061 059A 0316 302A 0323 0062;0061 302A 0316 0323 059A 0062;0061 302A 0316 0323 059A 0062;0061 302A 0316 0323 059A 0062;0061 302A 0316 0323 059A 0062; # (a◌֚◌̖◌〪◌̣b; a◌〪◌̖◌̣◌֚b; a◌〪◌̖◌̣◌֚b; a◌〪◌̖◌̣◌֚b; a◌〪◌̖◌̣◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING DOT BELOW, LATIN SMALL LETTER B
+0061 0323 059A 0316 302A 0062;1EA1 302A 0316 059A 0062;0061 302A 0323 0316 059A 0062;1EA1 302A 0316 059A 0062;0061 302A 0323 0316 059A 0062; # (a◌̣◌֚◌̖◌〪b; ạ◌〪◌̖◌֚b; a◌〪◌̣◌̖◌֚b; ạ◌〪◌̖◌֚b; a◌〪◌̣◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING DOT BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 059A 0316 302A 0324 0062;0061 302A 0316 0324 059A 0062;0061 302A 0316 0324 059A 0062;0061 302A 0316 0324 059A 0062;0061 302A 0316 0324 059A 0062; # (a◌֚◌̖◌〪◌̤b; a◌〪◌̖◌̤◌֚b; a◌〪◌̖◌̤◌֚b; a◌〪◌̖◌̤◌֚b; a◌〪◌̖◌̤◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING DIAERESIS BELOW, LATIN SMALL LETTER B
+0061 0324 059A 0316 302A 0062;0061 302A 0324 0316 059A 0062;0061 302A 0324 0316 059A 0062;0061 302A 0324 0316 059A 0062;0061 302A 0324 0316 059A 0062; # (a◌̤◌֚◌̖◌〪b; a◌〪◌̤◌̖◌֚b; a◌〪◌̤◌̖◌֚b; a◌〪◌̤◌̖◌֚b; a◌〪◌̤◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING DIAERESIS BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 059A 0316 302A 0325 0062;0061 302A 0316 0325 059A 0062;0061 302A 0316 0325 059A 0062;0061 302A 0316 0325 059A 0062;0061 302A 0316 0325 059A 0062; # (a◌֚◌̖◌〪◌̥b; a◌〪◌̖◌̥◌֚b; a◌〪◌̖◌̥◌֚b; a◌〪◌̖◌̥◌֚b; a◌〪◌̖◌̥◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING RING BELOW, LATIN SMALL LETTER B
+0061 0325 059A 0316 302A 0062;1E01 302A 0316 059A 0062;0061 302A 0325 0316 059A 0062;1E01 302A 0316 059A 0062;0061 302A 0325 0316 059A 0062; # (a◌̥◌֚◌̖◌〪b; á¸â—Œã€ªâ—ŒÌ–◌֚b; a◌〪◌̥◌̖◌֚b; á¸â—Œã€ªâ—ŒÌ–◌֚b; a◌〪◌̥◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING RING BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 059A 0316 302A 0326 0062;0061 302A 0316 0326 059A 0062;0061 302A 0316 0326 059A 0062;0061 302A 0316 0326 059A 0062;0061 302A 0316 0326 059A 0062; # (a◌֚◌̖◌〪◌̦b; a◌〪◌̖◌̦◌֚b; a◌〪◌̖◌̦◌֚b; a◌〪◌̖◌̦◌֚b; a◌〪◌̖◌̦◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING COMMA BELOW, LATIN SMALL LETTER B
+0061 0326 059A 0316 302A 0062;0061 302A 0326 0316 059A 0062;0061 302A 0326 0316 059A 0062;0061 302A 0326 0316 059A 0062;0061 302A 0326 0316 059A 0062; # (a◌̦◌֚◌̖◌〪b; a◌〪◌̦◌̖◌֚b; a◌〪◌̦◌̖◌֚b; a◌〪◌̦◌̖◌֚b; a◌〪◌̦◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING COMMA BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 031B 0321 0F74 0327 0062;0061 0F74 0321 0327 031B 0062;0061 0F74 0321 0327 031B 0062;0061 0F74 0321 0327 031B 0062;0061 0F74 0321 0327 031B 0062; # (a◌̛◌̡◌ུ◌̧b; a◌ུ◌̡◌̧◌̛b; a◌ུ◌̡◌̧◌̛b; a◌ུ◌̡◌̧◌̛b; a◌ུ◌̡◌̧◌̛b; ) LATIN SMALL LETTER A, COMBINING HORN, COMBINING PALATALIZED HOOK BELOW, TIBETAN VOWEL SIGN U, COMBINING CEDILLA, LATIN SMALL LETTER B
+0061 0327 031B 0321 0F74 0062;0061 0F74 0327 0321 031B 0062;0061 0F74 0327 0321 031B 0062;0061 0F74 0327 0321 031B 0062;0061 0F74 0327 0321 031B 0062; # (a◌̧◌̛◌̡◌ུb; a◌ུ◌̧◌̡◌̛b; a◌ུ◌̧◌̡◌̛b; a◌ུ◌̧◌̡◌̛b; a◌ུ◌̧◌̡◌̛b; ) LATIN SMALL LETTER A, COMBINING CEDILLA, COMBINING HORN, COMBINING PALATALIZED HOOK BELOW, TIBETAN VOWEL SIGN U, LATIN SMALL LETTER B
+0061 031B 0321 0F74 0328 0062;0061 0F74 0321 0328 031B 0062;0061 0F74 0321 0328 031B 0062;0061 0F74 0321 0328 031B 0062;0061 0F74 0321 0328 031B 0062; # (a◌̛◌̡◌ུ◌̨b; a◌ུ◌̡◌̨◌̛b; a◌ུ◌̡◌̨◌̛b; a◌ུ◌̡◌̨◌̛b; a◌ུ◌̡◌̨◌̛b; ) LATIN SMALL LETTER A, COMBINING HORN, COMBINING PALATALIZED HOOK BELOW, TIBETAN VOWEL SIGN U, COMBINING OGONEK, LATIN SMALL LETTER B
+0061 0328 031B 0321 0F74 0062;0105 0F74 0321 031B 0062;0061 0F74 0328 0321 031B 0062;0105 0F74 0321 031B 0062;0061 0F74 0328 0321 031B 0062; # (a◌̨◌̛◌̡◌ུb; ą◌ུ◌̡◌̛b; a◌ུ◌̨◌̡◌̛b; ą◌ུ◌̡◌̛b; a◌ུ◌̨◌̡◌̛b; ) LATIN SMALL LETTER A, COMBINING OGONEK, COMBINING HORN, COMBINING PALATALIZED HOOK BELOW, TIBETAN VOWEL SIGN U, LATIN SMALL LETTER B
+0061 059A 0316 302A 0329 0062;0061 302A 0316 0329 059A 0062;0061 302A 0316 0329 059A 0062;0061 302A 0316 0329 059A 0062;0061 302A 0316 0329 059A 0062; # (a◌֚◌̖◌〪◌̩b; a◌〪◌̖◌̩◌֚b; a◌〪◌̖◌̩◌֚b; a◌〪◌̖◌̩◌֚b; a◌〪◌̖◌̩◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING VERTICAL LINE BELOW, LATIN SMALL LETTER B
+0061 0329 059A 0316 302A 0062;0061 302A 0329 0316 059A 0062;0061 302A 0329 0316 059A 0062;0061 302A 0329 0316 059A 0062;0061 302A 0329 0316 059A 0062; # (a◌̩◌֚◌̖◌〪b; a◌〪◌̩◌̖◌֚b; a◌〪◌̩◌̖◌֚b; a◌〪◌̩◌̖◌֚b; a◌〪◌̩◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING VERTICAL LINE BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 059A 0316 302A 032A 0062;0061 302A 0316 032A 059A 0062;0061 302A 0316 032A 059A 0062;0061 302A 0316 032A 059A 0062;0061 302A 0316 032A 059A 0062; # (a◌֚◌̖◌〪◌̪b; a◌〪◌̖◌̪◌֚b; a◌〪◌̖◌̪◌֚b; a◌〪◌̖◌̪◌֚b; a◌〪◌̖◌̪◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING BRIDGE BELOW, LATIN SMALL LETTER B
+0061 032A 059A 0316 302A 0062;0061 302A 032A 0316 059A 0062;0061 302A 032A 0316 059A 0062;0061 302A 032A 0316 059A 0062;0061 302A 032A 0316 059A 0062; # (a◌̪◌֚◌̖◌〪b; a◌〪◌̪◌̖◌֚b; a◌〪◌̪◌̖◌֚b; a◌〪◌̪◌̖◌֚b; a◌〪◌̪◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING BRIDGE BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 059A 0316 302A 032B 0062;0061 302A 0316 032B 059A 0062;0061 302A 0316 032B 059A 0062;0061 302A 0316 032B 059A 0062;0061 302A 0316 032B 059A 0062; # (a◌֚◌̖◌〪◌̫b; a◌〪◌̖◌̫◌֚b; a◌〪◌̖◌̫◌֚b; a◌〪◌̖◌̫◌֚b; a◌〪◌̖◌̫◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING INVERTED DOUBLE ARCH BELOW, LATIN SMALL LETTER B
+0061 032B 059A 0316 302A 0062;0061 302A 032B 0316 059A 0062;0061 302A 032B 0316 059A 0062;0061 302A 032B 0316 059A 0062;0061 302A 032B 0316 059A 0062; # (a◌̫◌֚◌̖◌〪b; a◌〪◌̫◌̖◌֚b; a◌〪◌̫◌̖◌֚b; a◌〪◌̫◌̖◌֚b; a◌〪◌̫◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING INVERTED DOUBLE ARCH BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 059A 0316 302A 032C 0062;0061 302A 0316 032C 059A 0062;0061 302A 0316 032C 059A 0062;0061 302A 0316 032C 059A 0062;0061 302A 0316 032C 059A 0062; # (a◌֚◌̖◌〪◌̬b; a◌〪◌̖◌̬◌֚b; a◌〪◌̖◌̬◌֚b; a◌〪◌̖◌̬◌֚b; a◌〪◌̖◌̬◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING CARON BELOW, LATIN SMALL LETTER B
+0061 032C 059A 0316 302A 0062;0061 302A 032C 0316 059A 0062;0061 302A 032C 0316 059A 0062;0061 302A 032C 0316 059A 0062;0061 302A 032C 0316 059A 0062; # (a◌̬◌֚◌̖◌〪b; a◌〪◌̬◌̖◌֚b; a◌〪◌̬◌̖◌֚b; a◌〪◌̬◌̖◌֚b; a◌〪◌̬◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING CARON BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 059A 0316 302A 032D 0062;0061 302A 0316 032D 059A 0062;0061 302A 0316 032D 059A 0062;0061 302A 0316 032D 059A 0062;0061 302A 0316 032D 059A 0062; # (a◌֚◌̖◌〪◌̭b; a◌〪◌̖◌̭◌֚b; a◌〪◌̖◌̭◌֚b; a◌〪◌̖◌̭◌֚b; a◌〪◌̖◌̭◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING CIRCUMFLEX ACCENT BELOW, LATIN SMALL LETTER B
+0061 032D 059A 0316 302A 0062;0061 302A 032D 0316 059A 0062;0061 302A 032D 0316 059A 0062;0061 302A 032D 0316 059A 0062;0061 302A 032D 0316 059A 0062; # (a◌̭◌֚◌̖◌〪b; a◌〪◌̭◌̖◌֚b; a◌〪◌̭◌̖◌֚b; a◌〪◌̭◌̖◌֚b; a◌〪◌̭◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING CIRCUMFLEX ACCENT BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 059A 0316 302A 032E 0062;0061 302A 0316 032E 059A 0062;0061 302A 0316 032E 059A 0062;0061 302A 0316 032E 059A 0062;0061 302A 0316 032E 059A 0062; # (a◌֚◌̖◌〪◌̮b; a◌〪◌̖◌̮◌֚b; a◌〪◌̖◌̮◌֚b; a◌〪◌̖◌̮◌֚b; a◌〪◌̖◌̮◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING BREVE BELOW, LATIN SMALL LETTER B
+0061 032E 059A 0316 302A 0062;0061 302A 032E 0316 059A 0062;0061 302A 032E 0316 059A 0062;0061 302A 032E 0316 059A 0062;0061 302A 032E 0316 059A 0062; # (a◌̮◌֚◌̖◌〪b; a◌〪◌̮◌̖◌֚b; a◌〪◌̮◌̖◌֚b; a◌〪◌̮◌̖◌֚b; a◌〪◌̮◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING BREVE BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 059A 0316 302A 032F 0062;0061 302A 0316 032F 059A 0062;0061 302A 0316 032F 059A 0062;0061 302A 0316 032F 059A 0062;0061 302A 0316 032F 059A 0062; # (a◌֚◌̖◌〪◌̯b; a◌〪◌̖◌̯◌֚b; a◌〪◌̖◌̯◌֚b; a◌〪◌̖◌̯◌֚b; a◌〪◌̖◌̯◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING INVERTED BREVE BELOW, LATIN SMALL LETTER B
+0061 032F 059A 0316 302A 0062;0061 302A 032F 0316 059A 0062;0061 302A 032F 0316 059A 0062;0061 302A 032F 0316 059A 0062;0061 302A 032F 0316 059A 0062; # (a◌̯◌֚◌̖◌〪b; a◌〪◌̯◌̖◌֚b; a◌〪◌̯◌̖◌֚b; a◌〪◌̯◌̖◌֚b; a◌〪◌̯◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING INVERTED BREVE BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 059A 0316 302A 0330 0062;0061 302A 0316 0330 059A 0062;0061 302A 0316 0330 059A 0062;0061 302A 0316 0330 059A 0062;0061 302A 0316 0330 059A 0062; # (a◌֚◌̖◌〪◌̰b; a◌〪◌̖◌̰◌֚b; a◌〪◌̖◌̰◌֚b; a◌〪◌̖◌̰◌֚b; a◌〪◌̖◌̰◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING TILDE BELOW, LATIN SMALL LETTER B
+0061 0330 059A 0316 302A 0062;0061 302A 0330 0316 059A 0062;0061 302A 0330 0316 059A 0062;0061 302A 0330 0316 059A 0062;0061 302A 0330 0316 059A 0062; # (a◌̰◌֚◌̖◌〪b; a◌〪◌̰◌̖◌֚b; a◌〪◌̰◌̖◌֚b; a◌〪◌̰◌̖◌֚b; a◌〪◌̰◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING TILDE BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 059A 0316 302A 0331 0062;0061 302A 0316 0331 059A 0062;0061 302A 0316 0331 059A 0062;0061 302A 0316 0331 059A 0062;0061 302A 0316 0331 059A 0062; # (a◌֚◌̖◌〪◌̱b; a◌〪◌̖◌̱◌֚b; a◌〪◌̖◌̱◌֚b; a◌〪◌̖◌̱◌֚b; a◌〪◌̖◌̱◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING MACRON BELOW, LATIN SMALL LETTER B
+0061 0331 059A 0316 302A 0062;0061 302A 0331 0316 059A 0062;0061 302A 0331 0316 059A 0062;0061 302A 0331 0316 059A 0062;0061 302A 0331 0316 059A 0062; # (a◌̱◌֚◌̖◌〪b; a◌〪◌̱◌̖◌֚b; a◌〪◌̱◌̖◌֚b; a◌〪◌̱◌̖◌֚b; a◌〪◌̱◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING MACRON BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 059A 0316 302A 0332 0062;0061 302A 0316 0332 059A 0062;0061 302A 0316 0332 059A 0062;0061 302A 0316 0332 059A 0062;0061 302A 0316 0332 059A 0062; # (a◌֚◌̖◌〪◌̲b; a◌〪◌̖◌̲◌֚b; a◌〪◌̖◌̲◌֚b; a◌〪◌̖◌̲◌֚b; a◌〪◌̖◌̲◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING LOW LINE, LATIN SMALL LETTER B
+0061 0332 059A 0316 302A 0062;0061 302A 0332 0316 059A 0062;0061 302A 0332 0316 059A 0062;0061 302A 0332 0316 059A 0062;0061 302A 0332 0316 059A 0062; # (a◌̲◌֚◌̖◌〪b; a◌〪◌̲◌̖◌֚b; a◌〪◌̲◌̖◌֚b; a◌〪◌̲◌̖◌֚b; a◌〪◌̲◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING LOW LINE, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 059A 0316 302A 0333 0062;0061 302A 0316 0333 059A 0062;0061 302A 0316 0333 059A 0062;0061 302A 0316 0333 059A 0062;0061 302A 0316 0333 059A 0062; # (a◌֚◌̖◌〪◌̳b; a◌〪◌̖◌̳◌֚b; a◌〪◌̖◌̳◌֚b; a◌〪◌̖◌̳◌֚b; a◌〪◌̖◌̳◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING DOUBLE LOW LINE, LATIN SMALL LETTER B
+0061 0333 059A 0316 302A 0062;0061 302A 0333 0316 059A 0062;0061 302A 0333 0316 059A 0062;0061 302A 0333 0316 059A 0062;0061 302A 0333 0316 059A 0062; # (a◌̳◌֚◌̖◌〪b; a◌〪◌̳◌̖◌֚b; a◌〪◌̳◌̖◌֚b; a◌〪◌̳◌̖◌֚b; a◌〪◌̳◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING DOUBLE LOW LINE, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 093C 0334 0334 0062;0061 0334 0334 093C 0062;0061 0334 0334 093C 0062;0061 0334 0334 093C 0062;0061 0334 0334 093C 0062; # (a◌़◌̴◌̴b; a◌̴◌̴◌़b; a◌̴◌̴◌़b; a◌̴◌̴◌़b; a◌̴◌̴◌़b; ) LATIN SMALL LETTER A, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, COMBINING TILDE OVERLAY, LATIN SMALL LETTER B
+0061 0334 093C 0334 0062;0061 0334 0334 093C 0062;0061 0334 0334 093C 0062;0061 0334 0334 093C 0062;0061 0334 0334 093C 0062; # (a◌̴◌़◌̴b; a◌̴◌̴◌़b; a◌̴◌̴◌़b; a◌̴◌̴◌़b; a◌̴◌̴◌़b; ) LATIN SMALL LETTER A, COMBINING TILDE OVERLAY, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, LATIN SMALL LETTER B
+0061 093C 0334 0335 0062;0061 0334 0335 093C 0062;0061 0334 0335 093C 0062;0061 0334 0335 093C 0062;0061 0334 0335 093C 0062; # (a◌़◌̴◌̵b; a◌̴◌̵◌़b; a◌̴◌̵◌़b; a◌̴◌̵◌़b; a◌̴◌̵◌़b; ) LATIN SMALL LETTER A, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, COMBINING SHORT STROKE OVERLAY, LATIN SMALL LETTER B
+0061 0335 093C 0334 0062;0061 0335 0334 093C 0062;0061 0335 0334 093C 0062;0061 0335 0334 093C 0062;0061 0335 0334 093C 0062; # (a◌̵◌़◌̴b; a◌̵◌̴◌़b; a◌̵◌̴◌़b; a◌̵◌̴◌़b; a◌̵◌̴◌़b; ) LATIN SMALL LETTER A, COMBINING SHORT STROKE OVERLAY, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, LATIN SMALL LETTER B
+0061 093C 0334 0336 0062;0061 0334 0336 093C 0062;0061 0334 0336 093C 0062;0061 0334 0336 093C 0062;0061 0334 0336 093C 0062; # (a◌़◌̴◌̶b; a◌̴◌̶◌़b; a◌̴◌̶◌़b; a◌̴◌̶◌़b; a◌̴◌̶◌़b; ) LATIN SMALL LETTER A, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, COMBINING LONG STROKE OVERLAY, LATIN SMALL LETTER B
+0061 0336 093C 0334 0062;0061 0336 0334 093C 0062;0061 0336 0334 093C 0062;0061 0336 0334 093C 0062;0061 0336 0334 093C 0062; # (a◌̶◌़◌̴b; a◌̶◌̴◌़b; a◌̶◌̴◌़b; a◌̶◌̴◌़b; a◌̶◌̴◌़b; ) LATIN SMALL LETTER A, COMBINING LONG STROKE OVERLAY, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, LATIN SMALL LETTER B
+0061 093C 0334 0337 0062;0061 0334 0337 093C 0062;0061 0334 0337 093C 0062;0061 0334 0337 093C 0062;0061 0334 0337 093C 0062; # (a◌़◌̴◌̷b; a◌̴◌̷◌़b; a◌̴◌̷◌़b; a◌̴◌̷◌़b; a◌̴◌̷◌़b; ) LATIN SMALL LETTER A, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, COMBINING SHORT SOLIDUS OVERLAY, LATIN SMALL LETTER B
+0061 0337 093C 0334 0062;0061 0337 0334 093C 0062;0061 0337 0334 093C 0062;0061 0337 0334 093C 0062;0061 0337 0334 093C 0062; # (a◌̷◌़◌̴b; a◌̷◌̴◌़b; a◌̷◌̴◌़b; a◌̷◌̴◌़b; a◌̷◌̴◌़b; ) LATIN SMALL LETTER A, COMBINING SHORT SOLIDUS OVERLAY, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, LATIN SMALL LETTER B
+0061 093C 0334 0338 0062;0061 0334 0338 093C 0062;0061 0334 0338 093C 0062;0061 0334 0338 093C 0062;0061 0334 0338 093C 0062; # (a◌़◌̴◌̸b; a◌̴◌̸◌़b; a◌̴◌̸◌़b; a◌̴◌̸◌़b; a◌̴◌̸◌़b; ) LATIN SMALL LETTER A, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, COMBINING LONG SOLIDUS OVERLAY, LATIN SMALL LETTER B
+0061 0338 093C 0334 0062;0061 0338 0334 093C 0062;0061 0338 0334 093C 0062;0061 0338 0334 093C 0062;0061 0338 0334 093C 0062; # (a◌̸◌़◌̴b; a◌̸◌̴◌़b; a◌̸◌̴◌़b; a◌̸◌̴◌़b; a◌̸◌̴◌़b; ) LATIN SMALL LETTER A, COMBINING LONG SOLIDUS OVERLAY, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, LATIN SMALL LETTER B
+0061 059A 0316 302A 0339 0062;0061 302A 0316 0339 059A 0062;0061 302A 0316 0339 059A 0062;0061 302A 0316 0339 059A 0062;0061 302A 0316 0339 059A 0062; # (a◌֚◌̖◌〪◌̹b; a◌〪◌̖◌̹◌֚b; a◌〪◌̖◌̹◌֚b; a◌〪◌̖◌̹◌֚b; a◌〪◌̖◌̹◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING RIGHT HALF RING BELOW, LATIN SMALL LETTER B
+0061 0339 059A 0316 302A 0062;0061 302A 0339 0316 059A 0062;0061 302A 0339 0316 059A 0062;0061 302A 0339 0316 059A 0062;0061 302A 0339 0316 059A 0062; # (a◌̹◌֚◌̖◌〪b; a◌〪◌̹◌̖◌֚b; a◌〪◌̹◌̖◌֚b; a◌〪◌̹◌̖◌֚b; a◌〪◌̹◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING RIGHT HALF RING BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 059A 0316 302A 033A 0062;0061 302A 0316 033A 059A 0062;0061 302A 0316 033A 059A 0062;0061 302A 0316 033A 059A 0062;0061 302A 0316 033A 059A 0062; # (a◌֚◌̖◌〪◌̺b; a◌〪◌̖◌̺◌֚b; a◌〪◌̖◌̺◌֚b; a◌〪◌̖◌̺◌֚b; a◌〪◌̖◌̺◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING INVERTED BRIDGE BELOW, LATIN SMALL LETTER B
+0061 033A 059A 0316 302A 0062;0061 302A 033A 0316 059A 0062;0061 302A 033A 0316 059A 0062;0061 302A 033A 0316 059A 0062;0061 302A 033A 0316 059A 0062; # (a◌̺◌֚◌̖◌〪b; a◌〪◌̺◌̖◌֚b; a◌〪◌̺◌̖◌֚b; a◌〪◌̺◌̖◌֚b; a◌〪◌̺◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING INVERTED BRIDGE BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 059A 0316 302A 033B 0062;0061 302A 0316 033B 059A 0062;0061 302A 0316 033B 059A 0062;0061 302A 0316 033B 059A 0062;0061 302A 0316 033B 059A 0062; # (a◌֚◌̖◌〪◌̻b; a◌〪◌̖◌̻◌֚b; a◌〪◌̖◌̻◌֚b; a◌〪◌̖◌̻◌֚b; a◌〪◌̖◌̻◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING SQUARE BELOW, LATIN SMALL LETTER B
+0061 033B 059A 0316 302A 0062;0061 302A 033B 0316 059A 0062;0061 302A 033B 0316 059A 0062;0061 302A 033B 0316 059A 0062;0061 302A 033B 0316 059A 0062; # (a◌̻◌֚◌̖◌〪b; a◌〪◌̻◌̖◌֚b; a◌〪◌̻◌̖◌֚b; a◌〪◌̻◌̖◌֚b; a◌〪◌̻◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING SQUARE BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 059A 0316 302A 033C 0062;0061 302A 0316 033C 059A 0062;0061 302A 0316 033C 059A 0062;0061 302A 0316 033C 059A 0062;0061 302A 0316 033C 059A 0062; # (a◌֚◌̖◌〪◌̼b; a◌〪◌̖◌̼◌֚b; a◌〪◌̖◌̼◌֚b; a◌〪◌̖◌̼◌֚b; a◌〪◌̖◌̼◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING SEAGULL BELOW, LATIN SMALL LETTER B
+0061 033C 059A 0316 302A 0062;0061 302A 033C 0316 059A 0062;0061 302A 033C 0316 059A 0062;0061 302A 033C 0316 059A 0062;0061 302A 033C 0316 059A 0062; # (a◌̼◌֚◌̖◌〪b; a◌〪◌̼◌̖◌֚b; a◌〪◌̼◌̖◌֚b; a◌〪◌̼◌̖◌֚b; a◌〪◌̼◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING SEAGULL BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 0315 0300 05AE 033D 0062;00E0 05AE 033D 0315 0062;0061 05AE 0300 033D 0315 0062;00E0 05AE 033D 0315 0062;0061 05AE 0300 033D 0315 0062; # (a◌̕◌̀◌֮◌̽b; à◌֮◌̽◌̕b; a◌֮◌̀◌̽◌̕b; à◌֮◌̽◌̕b; a◌֮◌̀◌̽◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING X ABOVE, LATIN SMALL LETTER B
+0061 033D 0315 0300 05AE 0062;0061 05AE 033D 0300 0315 0062;0061 05AE 033D 0300 0315 0062;0061 05AE 033D 0300 0315 0062;0061 05AE 033D 0300 0315 0062; # (a◌̽◌̕◌̀◌֮b; a◌֮◌̽◌̀◌̕b; a◌֮◌̽◌̀◌̕b; a◌֮◌̽◌̀◌̕b; a◌֮◌̽◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING X ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 033E 0062;00E0 05AE 033E 0315 0062;0061 05AE 0300 033E 0315 0062;00E0 05AE 033E 0315 0062;0061 05AE 0300 033E 0315 0062; # (a◌̕◌̀◌֮◌̾b; à◌֮◌̾◌̕b; a◌֮◌̀◌̾◌̕b; à◌֮◌̾◌̕b; a◌֮◌̀◌̾◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING VERTICAL TILDE, LATIN SMALL LETTER B
+0061 033E 0315 0300 05AE 0062;0061 05AE 033E 0300 0315 0062;0061 05AE 033E 0300 0315 0062;0061 05AE 033E 0300 0315 0062;0061 05AE 033E 0300 0315 0062; # (a◌̾◌̕◌̀◌֮b; a◌֮◌̾◌̀◌̕b; a◌֮◌̾◌̀◌̕b; a◌֮◌̾◌̀◌̕b; a◌֮◌̾◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING VERTICAL TILDE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 033F 0062;00E0 05AE 033F 0315 0062;0061 05AE 0300 033F 0315 0062;00E0 05AE 033F 0315 0062;0061 05AE 0300 033F 0315 0062; # (a◌̕◌̀◌֮◌̿b; à◌֮◌̿◌̕b; a◌֮◌̀◌̿◌̕b; à◌֮◌̿◌̕b; a◌֮◌̀◌̿◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING DOUBLE OVERLINE, LATIN SMALL LETTER B
+0061 033F 0315 0300 05AE 0062;0061 05AE 033F 0300 0315 0062;0061 05AE 033F 0300 0315 0062;0061 05AE 033F 0300 0315 0062;0061 05AE 033F 0300 0315 0062; # (a◌̿◌̕◌̀◌֮b; a◌֮◌̿◌̀◌̕b; a◌֮◌̿◌̀◌̕b; a◌֮◌̿◌̀◌̕b; a◌֮◌̿◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING DOUBLE OVERLINE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0340 0062;00E0 05AE 0300 0315 0062;0061 05AE 0300 0300 0315 0062;00E0 05AE 0300 0315 0062;0061 05AE 0300 0300 0315 0062; # (a◌̕◌̀◌֮◌̀b; à◌֮◌̀◌̕b; a◌֮◌̀◌̀◌̕b; à◌֮◌̀◌̕b; a◌֮◌̀◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING GRAVE TONE MARK, LATIN SMALL LETTER B
+0061 0340 0315 0300 05AE 0062;00E0 05AE 0300 0315 0062;0061 05AE 0300 0300 0315 0062;00E0 05AE 0300 0315 0062;0061 05AE 0300 0300 0315 0062; # (a◌̀◌̕◌̀◌֮b; à◌֮◌̀◌̕b; a◌֮◌̀◌̀◌̕b; à◌֮◌̀◌̕b; a◌֮◌̀◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING GRAVE TONE MARK, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0341 0062;00E0 05AE 0301 0315 0062;0061 05AE 0300 0301 0315 0062;00E0 05AE 0301 0315 0062;0061 05AE 0300 0301 0315 0062; # (a◌̕◌̀◌֮◌Íb; à◌֮◌Ì◌̕b; a◌֮◌̀◌Ì◌̕b; à◌֮◌Ì◌̕b; a◌֮◌̀◌Ì◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING ACUTE TONE MARK, LATIN SMALL LETTER B
+0061 0341 0315 0300 05AE 0062;00E1 05AE 0300 0315 0062;0061 05AE 0301 0300 0315 0062;00E1 05AE 0300 0315 0062;0061 05AE 0301 0300 0315 0062; # (aâ—ŒÍ◌̕◌̀◌֮b; á◌֮◌̀◌̕b; a◌֮◌Ì◌̀◌̕b; á◌֮◌̀◌̕b; a◌֮◌Ì◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING ACUTE TONE MARK, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0342 0062;00E0 05AE 0342 0315 0062;0061 05AE 0300 0342 0315 0062;00E0 05AE 0342 0315 0062;0061 05AE 0300 0342 0315 0062; # (a◌̕◌̀◌֮◌͂b; à◌֮◌͂◌̕b; a◌֮◌̀◌͂◌̕b; à◌֮◌͂◌̕b; a◌֮◌̀◌͂◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING GREEK PERISPOMENI, LATIN SMALL LETTER B
+0061 0342 0315 0300 05AE 0062;0061 05AE 0342 0300 0315 0062;0061 05AE 0342 0300 0315 0062;0061 05AE 0342 0300 0315 0062;0061 05AE 0342 0300 0315 0062; # (a◌͂◌̕◌̀◌֮b; a◌֮◌͂◌̀◌̕b; a◌֮◌͂◌̀◌̕b; a◌֮◌͂◌̀◌̕b; a◌֮◌͂◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING GREEK PERISPOMENI, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0343 0062;00E0 05AE 0313 0315 0062;0061 05AE 0300 0313 0315 0062;00E0 05AE 0313 0315 0062;0061 05AE 0300 0313 0315 0062; # (a◌̕◌̀◌֮◌̓b; à◌֮◌̓◌̕b; a◌֮◌̀◌̓◌̕b; à◌֮◌̓◌̕b; a◌֮◌̀◌̓◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING GREEK KORONIS, LATIN SMALL LETTER B
+0061 0343 0315 0300 05AE 0062;0061 05AE 0313 0300 0315 0062;0061 05AE 0313 0300 0315 0062;0061 05AE 0313 0300 0315 0062;0061 05AE 0313 0300 0315 0062; # (a◌̓◌̕◌̀◌֮b; a◌֮◌̓◌̀◌̕b; a◌֮◌̓◌̀◌̕b; a◌֮◌̓◌̀◌̕b; a◌֮◌̓◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING GREEK KORONIS, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0344 0062;00E0 05AE 0308 0301 0315 0062;0061 05AE 0300 0308 0301 0315 0062;00E0 05AE 0308 0301 0315 0062;0061 05AE 0300 0308 0301 0315 0062; # (a◌̕◌̀◌֮◌̈́b; à◌֮◌̈◌Ì◌̕b; a◌֮◌̀◌̈◌Ì◌̕b; à◌֮◌̈◌Ì◌̕b; a◌֮◌̀◌̈◌Ì◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING GREEK DIALYTIKA TONOS, LATIN SMALL LETTER B
+0061 0344 0315 0300 05AE 0062;00E4 05AE 0301 0300 0315 0062;0061 05AE 0308 0301 0300 0315 0062;00E4 05AE 0301 0300 0315 0062;0061 05AE 0308 0301 0300 0315 0062; # (a◌̈́◌̕◌̀◌֮b; ä◌֮◌Ì◌̀◌̕b; a◌֮◌̈◌Ì◌̀◌̕b; ä◌֮◌Ì◌̀◌̕b; a◌֮◌̈◌Ì◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING GREEK DIALYTIKA TONOS, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0345 035D 0345 0062;0061 035D 0345 0345 0062;0061 035D 0345 0345 0062;0061 035D 0345 0345 0062;0061 035D 0345 0345 0062; # (a◌ͅ◌Í◌ͅb; aâ—ŒÍ◌ͅ◌ͅb; aâ—ŒÍ◌ͅ◌ͅb; aâ—ŒÍ◌ͅ◌ͅb; aâ—ŒÍ◌ͅ◌ͅb; ) LATIN SMALL LETTER A, COMBINING GREEK YPOGEGRAMMENI, COMBINING DOUBLE BREVE, COMBINING GREEK YPOGEGRAMMENI, LATIN SMALL LETTER B
+0061 0345 0345 035D 0062;0061 035D 0345 0345 0062;0061 035D 0345 0345 0062;0061 035D 0345 0345 0062;0061 035D 0345 0345 0062; # (a◌ͅ◌ͅ◌Íb; aâ—ŒÍ◌ͅ◌ͅb; aâ—ŒÍ◌ͅ◌ͅb; aâ—ŒÍ◌ͅ◌ͅb; aâ—ŒÍ◌ͅ◌ͅb; ) LATIN SMALL LETTER A, COMBINING GREEK YPOGEGRAMMENI, COMBINING GREEK YPOGEGRAMMENI, COMBINING DOUBLE BREVE, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0346 0062;00E0 05AE 0346 0315 0062;0061 05AE 0300 0346 0315 0062;00E0 05AE 0346 0315 0062;0061 05AE 0300 0346 0315 0062; # (a◌̕◌̀◌֮◌͆b; à◌֮◌͆◌̕b; a◌֮◌̀◌͆◌̕b; à◌֮◌͆◌̕b; a◌֮◌̀◌͆◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING BRIDGE ABOVE, LATIN SMALL LETTER B
+0061 0346 0315 0300 05AE 0062;0061 05AE 0346 0300 0315 0062;0061 05AE 0346 0300 0315 0062;0061 05AE 0346 0300 0315 0062;0061 05AE 0346 0300 0315 0062; # (a◌͆◌̕◌̀◌֮b; a◌֮◌͆◌̀◌̕b; a◌֮◌͆◌̀◌̕b; a◌֮◌͆◌̀◌̕b; a◌֮◌͆◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING BRIDGE ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 059A 0316 302A 0347 0062;0061 302A 0316 0347 059A 0062;0061 302A 0316 0347 059A 0062;0061 302A 0316 0347 059A 0062;0061 302A 0316 0347 059A 0062; # (a◌֚◌̖◌〪◌͇b; a◌〪◌̖◌͇◌֚b; a◌〪◌̖◌͇◌֚b; a◌〪◌̖◌͇◌֚b; a◌〪◌̖◌͇◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING EQUALS SIGN BELOW, LATIN SMALL LETTER B
+0061 0347 059A 0316 302A 0062;0061 302A 0347 0316 059A 0062;0061 302A 0347 0316 059A 0062;0061 302A 0347 0316 059A 0062;0061 302A 0347 0316 059A 0062; # (a◌͇◌֚◌̖◌〪b; a◌〪◌͇◌̖◌֚b; a◌〪◌͇◌̖◌֚b; a◌〪◌͇◌̖◌֚b; a◌〪◌͇◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING EQUALS SIGN BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 059A 0316 302A 0348 0062;0061 302A 0316 0348 059A 0062;0061 302A 0316 0348 059A 0062;0061 302A 0316 0348 059A 0062;0061 302A 0316 0348 059A 0062; # (a◌֚◌̖◌〪◌͈b; a◌〪◌̖◌͈◌֚b; a◌〪◌̖◌͈◌֚b; a◌〪◌̖◌͈◌֚b; a◌〪◌̖◌͈◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING DOUBLE VERTICAL LINE BELOW, LATIN SMALL LETTER B
+0061 0348 059A 0316 302A 0062;0061 302A 0348 0316 059A 0062;0061 302A 0348 0316 059A 0062;0061 302A 0348 0316 059A 0062;0061 302A 0348 0316 059A 0062; # (a◌͈◌֚◌̖◌〪b; a◌〪◌͈◌̖◌֚b; a◌〪◌͈◌̖◌֚b; a◌〪◌͈◌̖◌֚b; a◌〪◌͈◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING DOUBLE VERTICAL LINE BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 059A 0316 302A 0349 0062;0061 302A 0316 0349 059A 0062;0061 302A 0316 0349 059A 0062;0061 302A 0316 0349 059A 0062;0061 302A 0316 0349 059A 0062; # (a◌֚◌̖◌〪◌͉b; a◌〪◌̖◌͉◌֚b; a◌〪◌̖◌͉◌֚b; a◌〪◌̖◌͉◌֚b; a◌〪◌̖◌͉◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING LEFT ANGLE BELOW, LATIN SMALL LETTER B
+0061 0349 059A 0316 302A 0062;0061 302A 0349 0316 059A 0062;0061 302A 0349 0316 059A 0062;0061 302A 0349 0316 059A 0062;0061 302A 0349 0316 059A 0062; # (a◌͉◌֚◌̖◌〪b; a◌〪◌͉◌̖◌֚b; a◌〪◌͉◌̖◌֚b; a◌〪◌͉◌̖◌֚b; a◌〪◌͉◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING LEFT ANGLE BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 0315 0300 05AE 034A 0062;00E0 05AE 034A 0315 0062;0061 05AE 0300 034A 0315 0062;00E0 05AE 034A 0315 0062;0061 05AE 0300 034A 0315 0062; # (a◌̕◌̀◌֮◌͊b; à◌֮◌͊◌̕b; a◌֮◌̀◌͊◌̕b; à◌֮◌͊◌̕b; a◌֮◌̀◌͊◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING NOT TILDE ABOVE, LATIN SMALL LETTER B
+0061 034A 0315 0300 05AE 0062;0061 05AE 034A 0300 0315 0062;0061 05AE 034A 0300 0315 0062;0061 05AE 034A 0300 0315 0062;0061 05AE 034A 0300 0315 0062; # (a◌͊◌̕◌̀◌֮b; a◌֮◌͊◌̀◌̕b; a◌֮◌͊◌̀◌̕b; a◌֮◌͊◌̀◌̕b; a◌֮◌͊◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING NOT TILDE ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 034B 0062;00E0 05AE 034B 0315 0062;0061 05AE 0300 034B 0315 0062;00E0 05AE 034B 0315 0062;0061 05AE 0300 034B 0315 0062; # (a◌̕◌̀◌֮◌͋b; à◌֮◌͋◌̕b; a◌֮◌̀◌͋◌̕b; à◌֮◌͋◌̕b; a◌֮◌̀◌͋◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING HOMOTHETIC ABOVE, LATIN SMALL LETTER B
+0061 034B 0315 0300 05AE 0062;0061 05AE 034B 0300 0315 0062;0061 05AE 034B 0300 0315 0062;0061 05AE 034B 0300 0315 0062;0061 05AE 034B 0300 0315 0062; # (a◌͋◌̕◌̀◌֮b; a◌֮◌͋◌̀◌̕b; a◌֮◌͋◌̀◌̕b; a◌֮◌͋◌̀◌̕b; a◌֮◌͋◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING HOMOTHETIC ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 034C 0062;00E0 05AE 034C 0315 0062;0061 05AE 0300 034C 0315 0062;00E0 05AE 034C 0315 0062;0061 05AE 0300 034C 0315 0062; # (a◌̕◌̀◌֮◌͌b; à◌֮◌͌◌̕b; a◌֮◌̀◌͌◌̕b; à◌֮◌͌◌̕b; a◌֮◌̀◌͌◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING ALMOST EQUAL TO ABOVE, LATIN SMALL LETTER B
+0061 034C 0315 0300 05AE 0062;0061 05AE 034C 0300 0315 0062;0061 05AE 034C 0300 0315 0062;0061 05AE 034C 0300 0315 0062;0061 05AE 034C 0300 0315 0062; # (a◌͌◌̕◌̀◌֮b; a◌֮◌͌◌̀◌̕b; a◌֮◌͌◌̀◌̕b; a◌֮◌͌◌̀◌̕b; a◌֮◌͌◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING ALMOST EQUAL TO ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 059A 0316 302A 034D 0062;0061 302A 0316 034D 059A 0062;0061 302A 0316 034D 059A 0062;0061 302A 0316 034D 059A 0062;0061 302A 0316 034D 059A 0062; # (a◌֚◌̖◌〪◌Íb; a◌〪◌̖◌Í◌֚b; a◌〪◌̖◌Í◌֚b; a◌〪◌̖◌Í◌֚b; a◌〪◌̖◌Í◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING LEFT RIGHT ARROW BELOW, LATIN SMALL LETTER B
+0061 034D 059A 0316 302A 0062;0061 302A 034D 0316 059A 0062;0061 302A 034D 0316 059A 0062;0061 302A 034D 0316 059A 0062;0061 302A 034D 0316 059A 0062; # (aâ—ŒÍ◌֚◌̖◌〪b; a◌〪◌Í◌̖◌֚b; a◌〪◌Í◌̖◌֚b; a◌〪◌Í◌̖◌֚b; a◌〪◌Í◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING LEFT RIGHT ARROW BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 059A 0316 302A 034E 0062;0061 302A 0316 034E 059A 0062;0061 302A 0316 034E 059A 0062;0061 302A 0316 034E 059A 0062;0061 302A 0316 034E 059A 0062; # (a◌֚◌̖◌〪◌͎b; a◌〪◌̖◌͎◌֚b; a◌〪◌̖◌͎◌֚b; a◌〪◌̖◌͎◌֚b; a◌〪◌̖◌͎◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING UPWARDS ARROW BELOW, LATIN SMALL LETTER B
+0061 034E 059A 0316 302A 0062;0061 302A 034E 0316 059A 0062;0061 302A 034E 0316 059A 0062;0061 302A 034E 0316 059A 0062;0061 302A 034E 0316 059A 0062; # (a◌͎◌֚◌̖◌〪b; a◌〪◌͎◌̖◌֚b; a◌〪◌͎◌̖◌֚b; a◌〪◌͎◌̖◌֚b; a◌〪◌͎◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING UPWARDS ARROW BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0350 0062;00E0 05AE 0350 0315 0062;0061 05AE 0300 0350 0315 0062;00E0 05AE 0350 0315 0062;0061 05AE 0300 0350 0315 0062; # (a◌̕◌̀◌֮◌Íb; à◌֮◌Í◌̕b; a◌֮◌̀◌Í◌̕b; à◌֮◌Í◌̕b; a◌֮◌̀◌Í◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING RIGHT ARROWHEAD ABOVE, LATIN SMALL LETTER B
+0061 0350 0315 0300 05AE 0062;0061 05AE 0350 0300 0315 0062;0061 05AE 0350 0300 0315 0062;0061 05AE 0350 0300 0315 0062;0061 05AE 0350 0300 0315 0062; # (aâ—ŒÍ◌̕◌̀◌֮b; a◌֮◌Í◌̀◌̕b; a◌֮◌Í◌̀◌̕b; a◌֮◌Í◌̀◌̕b; a◌֮◌Í◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING RIGHT ARROWHEAD ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0351 0062;00E0 05AE 0351 0315 0062;0061 05AE 0300 0351 0315 0062;00E0 05AE 0351 0315 0062;0061 05AE 0300 0351 0315 0062; # (a◌̕◌̀◌֮◌͑b; à◌֮◌͑◌̕b; a◌֮◌̀◌͑◌̕b; à◌֮◌͑◌̕b; a◌֮◌̀◌͑◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING LEFT HALF RING ABOVE, LATIN SMALL LETTER B
+0061 0351 0315 0300 05AE 0062;0061 05AE 0351 0300 0315 0062;0061 05AE 0351 0300 0315 0062;0061 05AE 0351 0300 0315 0062;0061 05AE 0351 0300 0315 0062; # (a◌͑◌̕◌̀◌֮b; a◌֮◌͑◌̀◌̕b; a◌֮◌͑◌̀◌̕b; a◌֮◌͑◌̀◌̕b; a◌֮◌͑◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING LEFT HALF RING ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0352 0062;00E0 05AE 0352 0315 0062;0061 05AE 0300 0352 0315 0062;00E0 05AE 0352 0315 0062;0061 05AE 0300 0352 0315 0062; # (a◌̕◌̀◌֮◌͒b; à◌֮◌͒◌̕b; a◌֮◌̀◌͒◌̕b; à◌֮◌͒◌̕b; a◌֮◌̀◌͒◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING FERMATA, LATIN SMALL LETTER B
+0061 0352 0315 0300 05AE 0062;0061 05AE 0352 0300 0315 0062;0061 05AE 0352 0300 0315 0062;0061 05AE 0352 0300 0315 0062;0061 05AE 0352 0300 0315 0062; # (a◌͒◌̕◌̀◌֮b; a◌֮◌͒◌̀◌̕b; a◌֮◌͒◌̀◌̕b; a◌֮◌͒◌̀◌̕b; a◌֮◌͒◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING FERMATA, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 059A 0316 302A 0353 0062;0061 302A 0316 0353 059A 0062;0061 302A 0316 0353 059A 0062;0061 302A 0316 0353 059A 0062;0061 302A 0316 0353 059A 0062; # (a◌֚◌̖◌〪◌͓b; a◌〪◌̖◌͓◌֚b; a◌〪◌̖◌͓◌֚b; a◌〪◌̖◌͓◌֚b; a◌〪◌̖◌͓◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING X BELOW, LATIN SMALL LETTER B
+0061 0353 059A 0316 302A 0062;0061 302A 0353 0316 059A 0062;0061 302A 0353 0316 059A 0062;0061 302A 0353 0316 059A 0062;0061 302A 0353 0316 059A 0062; # (a◌͓◌֚◌̖◌〪b; a◌〪◌͓◌̖◌֚b; a◌〪◌͓◌̖◌֚b; a◌〪◌͓◌̖◌֚b; a◌〪◌͓◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING X BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 059A 0316 302A 0354 0062;0061 302A 0316 0354 059A 0062;0061 302A 0316 0354 059A 0062;0061 302A 0316 0354 059A 0062;0061 302A 0316 0354 059A 0062; # (a◌֚◌̖◌〪◌͔b; a◌〪◌̖◌͔◌֚b; a◌〪◌̖◌͔◌֚b; a◌〪◌̖◌͔◌֚b; a◌〪◌̖◌͔◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING LEFT ARROWHEAD BELOW, LATIN SMALL LETTER B
+0061 0354 059A 0316 302A 0062;0061 302A 0354 0316 059A 0062;0061 302A 0354 0316 059A 0062;0061 302A 0354 0316 059A 0062;0061 302A 0354 0316 059A 0062; # (a◌͔◌֚◌̖◌〪b; a◌〪◌͔◌̖◌֚b; a◌〪◌͔◌̖◌֚b; a◌〪◌͔◌̖◌֚b; a◌〪◌͔◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING LEFT ARROWHEAD BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 059A 0316 302A 0355 0062;0061 302A 0316 0355 059A 0062;0061 302A 0316 0355 059A 0062;0061 302A 0316 0355 059A 0062;0061 302A 0316 0355 059A 0062; # (a◌֚◌̖◌〪◌͕b; a◌〪◌̖◌͕◌֚b; a◌〪◌̖◌͕◌֚b; a◌〪◌̖◌͕◌֚b; a◌〪◌̖◌͕◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING RIGHT ARROWHEAD BELOW, LATIN SMALL LETTER B
+0061 0355 059A 0316 302A 0062;0061 302A 0355 0316 059A 0062;0061 302A 0355 0316 059A 0062;0061 302A 0355 0316 059A 0062;0061 302A 0355 0316 059A 0062; # (a◌͕◌֚◌̖◌〪b; a◌〪◌͕◌̖◌֚b; a◌〪◌͕◌̖◌֚b; a◌〪◌͕◌̖◌֚b; a◌〪◌͕◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING RIGHT ARROWHEAD BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 059A 0316 302A 0356 0062;0061 302A 0316 0356 059A 0062;0061 302A 0316 0356 059A 0062;0061 302A 0316 0356 059A 0062;0061 302A 0316 0356 059A 0062; # (a◌֚◌̖◌〪◌͖b; a◌〪◌̖◌͖◌֚b; a◌〪◌̖◌͖◌֚b; a◌〪◌̖◌͖◌֚b; a◌〪◌̖◌͖◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING RIGHT ARROWHEAD AND UP ARROWHEAD BELOW, LATIN SMALL LETTER B
+0061 0356 059A 0316 302A 0062;0061 302A 0356 0316 059A 0062;0061 302A 0356 0316 059A 0062;0061 302A 0356 0316 059A 0062;0061 302A 0356 0316 059A 0062; # (a◌͖◌֚◌̖◌〪b; a◌〪◌͖◌̖◌֚b; a◌〪◌͖◌̖◌֚b; a◌〪◌͖◌̖◌֚b; a◌〪◌͖◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING RIGHT ARROWHEAD AND UP ARROWHEAD BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0357 0062;00E0 05AE 0357 0315 0062;0061 05AE 0300 0357 0315 0062;00E0 05AE 0357 0315 0062;0061 05AE 0300 0357 0315 0062; # (a◌̕◌̀◌֮◌͗b; à◌֮◌͗◌̕b; a◌֮◌̀◌͗◌̕b; à◌֮◌͗◌̕b; a◌֮◌̀◌͗◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING RIGHT HALF RING ABOVE, LATIN SMALL LETTER B
+0061 0357 0315 0300 05AE 0062;0061 05AE 0357 0300 0315 0062;0061 05AE 0357 0300 0315 0062;0061 05AE 0357 0300 0315 0062;0061 05AE 0357 0300 0315 0062; # (a◌͗◌̕◌̀◌֮b; a◌֮◌͗◌̀◌̕b; a◌֮◌͗◌̀◌̕b; a◌֮◌͗◌̀◌̕b; a◌֮◌͗◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING RIGHT HALF RING ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0345 035D 035F 035D 0062;0061 035F 035D 035D 0345 0062;0061 035F 035D 035D 0345 0062;0061 035F 035D 035D 0345 0062;0061 035F 035D 035D 0345 0062; # (a◌ͅ◌Í◌͟◌Íb; a◌͟◌Íâ—ŒÍ◌ͅb; a◌͟◌Íâ—ŒÍ◌ͅb; a◌͟◌Íâ—ŒÍ◌ͅb; a◌͟◌Íâ—ŒÍ◌ͅb; ) LATIN SMALL LETTER A, COMBINING GREEK YPOGEGRAMMENI, COMBINING DOUBLE BREVE, COMBINING DOUBLE MACRON BELOW, COMBINING DOUBLE BREVE, LATIN SMALL LETTER B
+0061 035D 0345 035D 035F 0062;0061 035F 035D 035D 0345 0062;0061 035F 035D 035D 0345 0062;0061 035F 035D 035D 0345 0062;0061 035F 035D 035D 0345 0062; # (aâ—ŒÍ◌ͅ◌Í◌͟b; a◌͟◌Íâ—ŒÍ◌ͅb; a◌͟◌Íâ—ŒÍ◌ͅb; a◌͟◌Íâ—ŒÍ◌ͅb; a◌͟◌Íâ—ŒÍ◌ͅb; ) LATIN SMALL LETTER A, COMBINING DOUBLE BREVE, COMBINING GREEK YPOGEGRAMMENI, COMBINING DOUBLE BREVE, COMBINING DOUBLE MACRON BELOW, LATIN SMALL LETTER B
+0061 0345 035D 035F 035E 0062;0061 035F 035D 035E 0345 0062;0061 035F 035D 035E 0345 0062;0061 035F 035D 035E 0345 0062;0061 035F 035D 035E 0345 0062; # (a◌ͅ◌Í◌͟◌͞b; a◌͟◌Í◌͞◌ͅb; a◌͟◌Í◌͞◌ͅb; a◌͟◌Í◌͞◌ͅb; a◌͟◌Í◌͞◌ͅb; ) LATIN SMALL LETTER A, COMBINING GREEK YPOGEGRAMMENI, COMBINING DOUBLE BREVE, COMBINING DOUBLE MACRON BELOW, COMBINING DOUBLE MACRON, LATIN SMALL LETTER B
+0061 035E 0345 035D 035F 0062;0061 035F 035E 035D 0345 0062;0061 035F 035E 035D 0345 0062;0061 035F 035E 035D 0345 0062;0061 035F 035E 035D 0345 0062; # (a◌͞◌ͅ◌Í◌͟b; a◌͟◌͞◌Í◌ͅb; a◌͟◌͞◌Í◌ͅb; a◌͟◌͞◌Í◌ͅb; a◌͟◌͞◌Í◌ͅb; ) LATIN SMALL LETTER A, COMBINING DOUBLE MACRON, COMBINING GREEK YPOGEGRAMMENI, COMBINING DOUBLE BREVE, COMBINING DOUBLE MACRON BELOW, LATIN SMALL LETTER B
+0061 035D 035F 0315 035F 0062;0061 0315 035F 035F 035D 0062;0061 0315 035F 035F 035D 0062;0061 0315 035F 035F 035D 0062;0061 0315 035F 035F 035D 0062; # (aâ—ŒÍ◌͟◌̕◌͟b; a◌̕◌͟◌͟◌Íb; a◌̕◌͟◌͟◌Íb; a◌̕◌͟◌͟◌Íb; a◌̕◌͟◌͟◌Íb; ) LATIN SMALL LETTER A, COMBINING DOUBLE BREVE, COMBINING DOUBLE MACRON BELOW, COMBINING COMMA ABOVE RIGHT, COMBINING DOUBLE MACRON BELOW, LATIN SMALL LETTER B
+0061 035F 035D 035F 0315 0062;0061 0315 035F 035F 035D 0062;0061 0315 035F 035F 035D 0062;0061 0315 035F 035F 035D 0062;0061 0315 035F 035F 035D 0062; # (a◌͟◌Í◌͟◌̕b; a◌̕◌͟◌͟◌Íb; a◌̕◌͟◌͟◌Íb; a◌̕◌͟◌͟◌Íb; a◌̕◌͟◌͟◌Íb; ) LATIN SMALL LETTER A, COMBINING DOUBLE MACRON BELOW, COMBINING DOUBLE BREVE, COMBINING DOUBLE MACRON BELOW, COMBINING COMMA ABOVE RIGHT, LATIN SMALL LETTER B
+0061 0345 035D 035F 0360 0062;0061 035F 035D 0360 0345 0062;0061 035F 035D 0360 0345 0062;0061 035F 035D 0360 0345 0062;0061 035F 035D 0360 0345 0062; # (a◌ͅ◌Í◌͟◌͠b; a◌͟◌Í◌͠◌ͅb; a◌͟◌Í◌͠◌ͅb; a◌͟◌Í◌͠◌ͅb; a◌͟◌Í◌͠◌ͅb; ) LATIN SMALL LETTER A, COMBINING GREEK YPOGEGRAMMENI, COMBINING DOUBLE BREVE, COMBINING DOUBLE MACRON BELOW, COMBINING DOUBLE TILDE, LATIN SMALL LETTER B
+0061 0360 0345 035D 035F 0062;0061 035F 0360 035D 0345 0062;0061 035F 0360 035D 0345 0062;0061 035F 0360 035D 0345 0062;0061 035F 0360 035D 0345 0062; # (a◌͠◌ͅ◌Í◌͟b; a◌͟◌͠◌Í◌ͅb; a◌͟◌͠◌Í◌ͅb; a◌͟◌͠◌Í◌ͅb; a◌͟◌͠◌Í◌ͅb; ) LATIN SMALL LETTER A, COMBINING DOUBLE TILDE, COMBINING GREEK YPOGEGRAMMENI, COMBINING DOUBLE BREVE, COMBINING DOUBLE MACRON BELOW, LATIN SMALL LETTER B
+0061 0345 035D 035F 0361 0062;0061 035F 035D 0361 0345 0062;0061 035F 035D 0361 0345 0062;0061 035F 035D 0361 0345 0062;0061 035F 035D 0361 0345 0062; # (a◌ͅ◌Í◌͟◌͡b; a◌͟◌Í◌͡◌ͅb; a◌͟◌Í◌͡◌ͅb; a◌͟◌Í◌͡◌ͅb; a◌͟◌Í◌͡◌ͅb; ) LATIN SMALL LETTER A, COMBINING GREEK YPOGEGRAMMENI, COMBINING DOUBLE BREVE, COMBINING DOUBLE MACRON BELOW, COMBINING DOUBLE INVERTED BREVE, LATIN SMALL LETTER B
+0061 0361 0345 035D 035F 0062;0061 035F 0361 035D 0345 0062;0061 035F 0361 035D 0345 0062;0061 035F 0361 035D 0345 0062;0061 035F 0361 035D 0345 0062; # (a◌͡◌ͅ◌Í◌͟b; a◌͟◌͡◌Í◌ͅb; a◌͟◌͡◌Í◌ͅb; a◌͟◌͡◌Í◌ͅb; a◌͟◌͡◌Í◌ͅb; ) LATIN SMALL LETTER A, COMBINING DOUBLE INVERTED BREVE, COMBINING GREEK YPOGEGRAMMENI, COMBINING DOUBLE BREVE, COMBINING DOUBLE MACRON BELOW, LATIN SMALL LETTER B
+0061 035D 035F 0315 0362 0062;0061 0315 035F 0362 035D 0062;0061 0315 035F 0362 035D 0062;0061 0315 035F 0362 035D 0062;0061 0315 035F 0362 035D 0062; # (aâ—ŒÍ◌͟◌̕◌͢b; a◌̕◌͟◌͢◌Íb; a◌̕◌͟◌͢◌Íb; a◌̕◌͟◌͢◌Íb; a◌̕◌͟◌͢◌Íb; ) LATIN SMALL LETTER A, COMBINING DOUBLE BREVE, COMBINING DOUBLE MACRON BELOW, COMBINING COMMA ABOVE RIGHT, COMBINING DOUBLE RIGHTWARDS ARROW BELOW, LATIN SMALL LETTER B
+0061 0362 035D 035F 0315 0062;0061 0315 0362 035F 035D 0062;0061 0315 0362 035F 035D 0062;0061 0315 0362 035F 035D 0062;0061 0315 0362 035F 035D 0062; # (a◌͢◌Í◌͟◌̕b; a◌̕◌͢◌͟◌Íb; a◌̕◌͢◌͟◌Íb; a◌̕◌͢◌͟◌Íb; a◌̕◌͢◌͟◌Íb; ) LATIN SMALL LETTER A, COMBINING DOUBLE RIGHTWARDS ARROW BELOW, COMBINING DOUBLE BREVE, COMBINING DOUBLE MACRON BELOW, COMBINING COMMA ABOVE RIGHT, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0363 0062;00E0 05AE 0363 0315 0062;0061 05AE 0300 0363 0315 0062;00E0 05AE 0363 0315 0062;0061 05AE 0300 0363 0315 0062; # (a◌̕◌̀◌֮◌ͣb; à◌֮◌ͣ◌̕b; a◌֮◌̀◌ͣ◌̕b; à◌֮◌ͣ◌̕b; a◌֮◌̀◌ͣ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING LATIN SMALL LETTER A, LATIN SMALL LETTER B
+0061 0363 0315 0300 05AE 0062;0061 05AE 0363 0300 0315 0062;0061 05AE 0363 0300 0315 0062;0061 05AE 0363 0300 0315 0062;0061 05AE 0363 0300 0315 0062; # (a◌ͣ◌̕◌̀◌֮b; a◌֮◌ͣ◌̀◌̕b; a◌֮◌ͣ◌̀◌̕b; a◌֮◌ͣ◌̀◌̕b; a◌֮◌ͣ◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0364 0062;00E0 05AE 0364 0315 0062;0061 05AE 0300 0364 0315 0062;00E0 05AE 0364 0315 0062;0061 05AE 0300 0364 0315 0062; # (a◌̕◌̀◌֮◌ͤb; à◌֮◌ͤ◌̕b; a◌֮◌̀◌ͤ◌̕b; à◌֮◌ͤ◌̕b; a◌֮◌̀◌ͤ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING LATIN SMALL LETTER E, LATIN SMALL LETTER B
+0061 0364 0315 0300 05AE 0062;0061 05AE 0364 0300 0315 0062;0061 05AE 0364 0300 0315 0062;0061 05AE 0364 0300 0315 0062;0061 05AE 0364 0300 0315 0062; # (a◌ͤ◌̕◌̀◌֮b; a◌֮◌ͤ◌̀◌̕b; a◌֮◌ͤ◌̀◌̕b; a◌֮◌ͤ◌̀◌̕b; a◌֮◌ͤ◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING LATIN SMALL LETTER E, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0365 0062;00E0 05AE 0365 0315 0062;0061 05AE 0300 0365 0315 0062;00E0 05AE 0365 0315 0062;0061 05AE 0300 0365 0315 0062; # (a◌̕◌̀◌֮◌ͥb; à◌֮◌ͥ◌̕b; a◌֮◌̀◌ͥ◌̕b; à◌֮◌ͥ◌̕b; a◌֮◌̀◌ͥ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING LATIN SMALL LETTER I, LATIN SMALL LETTER B
+0061 0365 0315 0300 05AE 0062;0061 05AE 0365 0300 0315 0062;0061 05AE 0365 0300 0315 0062;0061 05AE 0365 0300 0315 0062;0061 05AE 0365 0300 0315 0062; # (a◌ͥ◌̕◌̀◌֮b; a◌֮◌ͥ◌̀◌̕b; a◌֮◌ͥ◌̀◌̕b; a◌֮◌ͥ◌̀◌̕b; a◌֮◌ͥ◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING LATIN SMALL LETTER I, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0366 0062;00E0 05AE 0366 0315 0062;0061 05AE 0300 0366 0315 0062;00E0 05AE 0366 0315 0062;0061 05AE 0300 0366 0315 0062; # (a◌̕◌̀◌֮◌ͦb; à◌֮◌ͦ◌̕b; a◌֮◌̀◌ͦ◌̕b; à◌֮◌ͦ◌̕b; a◌֮◌̀◌ͦ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING LATIN SMALL LETTER O, LATIN SMALL LETTER B
+0061 0366 0315 0300 05AE 0062;0061 05AE 0366 0300 0315 0062;0061 05AE 0366 0300 0315 0062;0061 05AE 0366 0300 0315 0062;0061 05AE 0366 0300 0315 0062; # (a◌ͦ◌̕◌̀◌֮b; a◌֮◌ͦ◌̀◌̕b; a◌֮◌ͦ◌̀◌̕b; a◌֮◌ͦ◌̀◌̕b; a◌֮◌ͦ◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING LATIN SMALL LETTER O, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0367 0062;00E0 05AE 0367 0315 0062;0061 05AE 0300 0367 0315 0062;00E0 05AE 0367 0315 0062;0061 05AE 0300 0367 0315 0062; # (a◌̕◌̀◌֮◌ͧb; à◌֮◌ͧ◌̕b; a◌֮◌̀◌ͧ◌̕b; à◌֮◌ͧ◌̕b; a◌֮◌̀◌ͧ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING LATIN SMALL LETTER U, LATIN SMALL LETTER B
+0061 0367 0315 0300 05AE 0062;0061 05AE 0367 0300 0315 0062;0061 05AE 0367 0300 0315 0062;0061 05AE 0367 0300 0315 0062;0061 05AE 0367 0300 0315 0062; # (a◌ͧ◌̕◌̀◌֮b; a◌֮◌ͧ◌̀◌̕b; a◌֮◌ͧ◌̀◌̕b; a◌֮◌ͧ◌̀◌̕b; a◌֮◌ͧ◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING LATIN SMALL LETTER U, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0368 0062;00E0 05AE 0368 0315 0062;0061 05AE 0300 0368 0315 0062;00E0 05AE 0368 0315 0062;0061 05AE 0300 0368 0315 0062; # (a◌̕◌̀◌֮◌ͨb; à◌֮◌ͨ◌̕b; a◌֮◌̀◌ͨ◌̕b; à◌֮◌ͨ◌̕b; a◌֮◌̀◌ͨ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING LATIN SMALL LETTER C, LATIN SMALL LETTER B
+0061 0368 0315 0300 05AE 0062;0061 05AE 0368 0300 0315 0062;0061 05AE 0368 0300 0315 0062;0061 05AE 0368 0300 0315 0062;0061 05AE 0368 0300 0315 0062; # (a◌ͨ◌̕◌̀◌֮b; a◌֮◌ͨ◌̀◌̕b; a◌֮◌ͨ◌̀◌̕b; a◌֮◌ͨ◌̀◌̕b; a◌֮◌ͨ◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING LATIN SMALL LETTER C, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0369 0062;00E0 05AE 0369 0315 0062;0061 05AE 0300 0369 0315 0062;00E0 05AE 0369 0315 0062;0061 05AE 0300 0369 0315 0062; # (a◌̕◌̀◌֮◌ͩb; à◌֮◌ͩ◌̕b; a◌֮◌̀◌ͩ◌̕b; à◌֮◌ͩ◌̕b; a◌֮◌̀◌ͩ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING LATIN SMALL LETTER D, LATIN SMALL LETTER B
+0061 0369 0315 0300 05AE 0062;0061 05AE 0369 0300 0315 0062;0061 05AE 0369 0300 0315 0062;0061 05AE 0369 0300 0315 0062;0061 05AE 0369 0300 0315 0062; # (a◌ͩ◌̕◌̀◌֮b; a◌֮◌ͩ◌̀◌̕b; a◌֮◌ͩ◌̀◌̕b; a◌֮◌ͩ◌̀◌̕b; a◌֮◌ͩ◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING LATIN SMALL LETTER D, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 036A 0062;00E0 05AE 036A 0315 0062;0061 05AE 0300 036A 0315 0062;00E0 05AE 036A 0315 0062;0061 05AE 0300 036A 0315 0062; # (a◌̕◌̀◌֮◌ͪb; à◌֮◌ͪ◌̕b; a◌֮◌̀◌ͪ◌̕b; à◌֮◌ͪ◌̕b; a◌֮◌̀◌ͪ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING LATIN SMALL LETTER H, LATIN SMALL LETTER B
+0061 036A 0315 0300 05AE 0062;0061 05AE 036A 0300 0315 0062;0061 05AE 036A 0300 0315 0062;0061 05AE 036A 0300 0315 0062;0061 05AE 036A 0300 0315 0062; # (a◌ͪ◌̕◌̀◌֮b; a◌֮◌ͪ◌̀◌̕b; a◌֮◌ͪ◌̀◌̕b; a◌֮◌ͪ◌̀◌̕b; a◌֮◌ͪ◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING LATIN SMALL LETTER H, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 036B 0062;00E0 05AE 036B 0315 0062;0061 05AE 0300 036B 0315 0062;00E0 05AE 036B 0315 0062;0061 05AE 0300 036B 0315 0062; # (a◌̕◌̀◌֮◌ͫb; à◌֮◌ͫ◌̕b; a◌֮◌̀◌ͫ◌̕b; à◌֮◌ͫ◌̕b; a◌֮◌̀◌ͫ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING LATIN SMALL LETTER M, LATIN SMALL LETTER B
+0061 036B 0315 0300 05AE 0062;0061 05AE 036B 0300 0315 0062;0061 05AE 036B 0300 0315 0062;0061 05AE 036B 0300 0315 0062;0061 05AE 036B 0300 0315 0062; # (a◌ͫ◌̕◌̀◌֮b; a◌֮◌ͫ◌̀◌̕b; a◌֮◌ͫ◌̀◌̕b; a◌֮◌ͫ◌̀◌̕b; a◌֮◌ͫ◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING LATIN SMALL LETTER M, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 036C 0062;00E0 05AE 036C 0315 0062;0061 05AE 0300 036C 0315 0062;00E0 05AE 036C 0315 0062;0061 05AE 0300 036C 0315 0062; # (a◌̕◌̀◌֮◌ͬb; à◌֮◌ͬ◌̕b; a◌֮◌̀◌ͬ◌̕b; à◌֮◌ͬ◌̕b; a◌֮◌̀◌ͬ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING LATIN SMALL LETTER R, LATIN SMALL LETTER B
+0061 036C 0315 0300 05AE 0062;0061 05AE 036C 0300 0315 0062;0061 05AE 036C 0300 0315 0062;0061 05AE 036C 0300 0315 0062;0061 05AE 036C 0300 0315 0062; # (a◌ͬ◌̕◌̀◌֮b; a◌֮◌ͬ◌̀◌̕b; a◌֮◌ͬ◌̀◌̕b; a◌֮◌ͬ◌̀◌̕b; a◌֮◌ͬ◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING LATIN SMALL LETTER R, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 036D 0062;00E0 05AE 036D 0315 0062;0061 05AE 0300 036D 0315 0062;00E0 05AE 036D 0315 0062;0061 05AE 0300 036D 0315 0062; # (a◌̕◌̀◌֮◌ͭb; à◌֮◌ͭ◌̕b; a◌֮◌̀◌ͭ◌̕b; à◌֮◌ͭ◌̕b; a◌֮◌̀◌ͭ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING LATIN SMALL LETTER T, LATIN SMALL LETTER B
+0061 036D 0315 0300 05AE 0062;0061 05AE 036D 0300 0315 0062;0061 05AE 036D 0300 0315 0062;0061 05AE 036D 0300 0315 0062;0061 05AE 036D 0300 0315 0062; # (a◌ͭ◌̕◌̀◌֮b; a◌֮◌ͭ◌̀◌̕b; a◌֮◌ͭ◌̀◌̕b; a◌֮◌ͭ◌̀◌̕b; a◌֮◌ͭ◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING LATIN SMALL LETTER T, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 036E 0062;00E0 05AE 036E 0315 0062;0061 05AE 0300 036E 0315 0062;00E0 05AE 036E 0315 0062;0061 05AE 0300 036E 0315 0062; # (a◌̕◌̀◌֮◌ͮb; à◌֮◌ͮ◌̕b; a◌֮◌̀◌ͮ◌̕b; à◌֮◌ͮ◌̕b; a◌֮◌̀◌ͮ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING LATIN SMALL LETTER V, LATIN SMALL LETTER B
+0061 036E 0315 0300 05AE 0062;0061 05AE 036E 0300 0315 0062;0061 05AE 036E 0300 0315 0062;0061 05AE 036E 0300 0315 0062;0061 05AE 036E 0300 0315 0062; # (a◌ͮ◌̕◌̀◌֮b; a◌֮◌ͮ◌̀◌̕b; a◌֮◌ͮ◌̀◌̕b; a◌֮◌ͮ◌̀◌̕b; a◌֮◌ͮ◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING LATIN SMALL LETTER V, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 036F 0062;00E0 05AE 036F 0315 0062;0061 05AE 0300 036F 0315 0062;00E0 05AE 036F 0315 0062;0061 05AE 0300 036F 0315 0062; # (a◌̕◌̀◌֮◌ͯb; à◌֮◌ͯ◌̕b; a◌֮◌̀◌ͯ◌̕b; à◌֮◌ͯ◌̕b; a◌֮◌̀◌ͯ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING LATIN SMALL LETTER X, LATIN SMALL LETTER B
+0061 036F 0315 0300 05AE 0062;0061 05AE 036F 0300 0315 0062;0061 05AE 036F 0300 0315 0062;0061 05AE 036F 0300 0315 0062;0061 05AE 036F 0300 0315 0062; # (a◌ͯ◌̕◌̀◌֮b; a◌֮◌ͯ◌̀◌̕b; a◌֮◌ͯ◌̀◌̕b; a◌֮◌ͯ◌̀◌̕b; a◌֮◌ͯ◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING LATIN SMALL LETTER X, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0483 0062;00E0 05AE 0483 0315 0062;0061 05AE 0300 0483 0315 0062;00E0 05AE 0483 0315 0062;0061 05AE 0300 0483 0315 0062; # (a◌̕◌̀◌֮◌҃b; à◌֮◌҃◌̕b; a◌֮◌̀◌҃◌̕b; à◌֮◌҃◌̕b; a◌֮◌̀◌҃◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING CYRILLIC TITLO, LATIN SMALL LETTER B
+0061 0483 0315 0300 05AE 0062;0061 05AE 0483 0300 0315 0062;0061 05AE 0483 0300 0315 0062;0061 05AE 0483 0300 0315 0062;0061 05AE 0483 0300 0315 0062; # (a◌҃◌̕◌̀◌֮b; a◌֮◌҃◌̀◌̕b; a◌֮◌҃◌̀◌̕b; a◌֮◌҃◌̀◌̕b; a◌֮◌҃◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING CYRILLIC TITLO, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0484 0062;00E0 05AE 0484 0315 0062;0061 05AE 0300 0484 0315 0062;00E0 05AE 0484 0315 0062;0061 05AE 0300 0484 0315 0062; # (a◌̕◌̀◌֮◌҄b; à◌֮◌҄◌̕b; a◌֮◌̀◌҄◌̕b; à◌֮◌҄◌̕b; a◌֮◌̀◌҄◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING CYRILLIC PALATALIZATION, LATIN SMALL LETTER B
+0061 0484 0315 0300 05AE 0062;0061 05AE 0484 0300 0315 0062;0061 05AE 0484 0300 0315 0062;0061 05AE 0484 0300 0315 0062;0061 05AE 0484 0300 0315 0062; # (a◌҄◌̕◌̀◌֮b; a◌֮◌҄◌̀◌̕b; a◌֮◌҄◌̀◌̕b; a◌֮◌҄◌̀◌̕b; a◌֮◌҄◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING CYRILLIC PALATALIZATION, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0485 0062;00E0 05AE 0485 0315 0062;0061 05AE 0300 0485 0315 0062;00E0 05AE 0485 0315 0062;0061 05AE 0300 0485 0315 0062; # (a◌̕◌̀◌֮◌҅b; à◌֮◌҅◌̕b; a◌֮◌̀◌҅◌̕b; à◌֮◌҅◌̕b; a◌֮◌̀◌҅◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING CYRILLIC DASIA PNEUMATA, LATIN SMALL LETTER B
+0061 0485 0315 0300 05AE 0062;0061 05AE 0485 0300 0315 0062;0061 05AE 0485 0300 0315 0062;0061 05AE 0485 0300 0315 0062;0061 05AE 0485 0300 0315 0062; # (a◌҅◌̕◌̀◌֮b; a◌֮◌҅◌̀◌̕b; a◌֮◌҅◌̀◌̕b; a◌֮◌҅◌̀◌̕b; a◌֮◌҅◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING CYRILLIC DASIA PNEUMATA, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0486 0062;00E0 05AE 0486 0315 0062;0061 05AE 0300 0486 0315 0062;00E0 05AE 0486 0315 0062;0061 05AE 0300 0486 0315 0062; # (a◌̕◌̀◌֮◌҆b; à◌֮◌҆◌̕b; a◌֮◌̀◌҆◌̕b; à◌֮◌҆◌̕b; a◌֮◌̀◌҆◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING CYRILLIC PSILI PNEUMATA, LATIN SMALL LETTER B
+0061 0486 0315 0300 05AE 0062;0061 05AE 0486 0300 0315 0062;0061 05AE 0486 0300 0315 0062;0061 05AE 0486 0300 0315 0062;0061 05AE 0486 0300 0315 0062; # (a◌҆◌̕◌̀◌֮b; a◌֮◌҆◌̀◌̕b; a◌֮◌҆◌̀◌̕b; a◌֮◌҆◌̀◌̕b; a◌֮◌҆◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING CYRILLIC PSILI PNEUMATA, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 059A 0316 302A 0591 0062;0061 302A 0316 0591 059A 0062;0061 302A 0316 0591 059A 0062;0061 302A 0316 0591 059A 0062;0061 302A 0316 0591 059A 0062; # (a◌֚◌̖◌〪◌֑b; a◌〪◌̖◌֑◌֚b; a◌〪◌̖◌֑◌֚b; a◌〪◌̖◌֑◌֚b; a◌〪◌̖◌֑◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, HEBREW ACCENT ETNAHTA, LATIN SMALL LETTER B
+0061 0591 059A 0316 302A 0062;0061 302A 0591 0316 059A 0062;0061 302A 0591 0316 059A 0062;0061 302A 0591 0316 059A 0062;0061 302A 0591 0316 059A 0062; # (a◌֑◌֚◌̖◌〪b; a◌〪◌֑◌̖◌֚b; a◌〪◌֑◌̖◌֚b; a◌〪◌֑◌̖◌֚b; a◌〪◌֑◌̖◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT ETNAHTA, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0592 0062;00E0 05AE 0592 0315 0062;0061 05AE 0300 0592 0315 0062;00E0 05AE 0592 0315 0062;0061 05AE 0300 0592 0315 0062; # (a◌̕◌̀◌֮◌֒b; à◌֮◌֒◌̕b; a◌֮◌̀◌֒◌̕b; à◌֮◌֒◌̕b; a◌֮◌̀◌֒◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, HEBREW ACCENT SEGOL, LATIN SMALL LETTER B
+0061 0592 0315 0300 05AE 0062;0061 05AE 0592 0300 0315 0062;0061 05AE 0592 0300 0315 0062;0061 05AE 0592 0300 0315 0062;0061 05AE 0592 0300 0315 0062; # (a◌֒◌̕◌̀◌֮b; a◌֮◌֒◌̀◌̕b; a◌֮◌֒◌̀◌̕b; a◌֮◌֒◌̀◌̕b; a◌֮◌֒◌̀◌̕b; ) LATIN SMALL LETTER A, HEBREW ACCENT SEGOL, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0593 0062;00E0 05AE 0593 0315 0062;0061 05AE 0300 0593 0315 0062;00E0 05AE 0593 0315 0062;0061 05AE 0300 0593 0315 0062; # (a◌̕◌̀◌֮◌֓b; à◌֮◌֓◌̕b; a◌֮◌̀◌֓◌̕b; à◌֮◌֓◌̕b; a◌֮◌̀◌֓◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, HEBREW ACCENT SHALSHELET, LATIN SMALL LETTER B
+0061 0593 0315 0300 05AE 0062;0061 05AE 0593 0300 0315 0062;0061 05AE 0593 0300 0315 0062;0061 05AE 0593 0300 0315 0062;0061 05AE 0593 0300 0315 0062; # (a◌֓◌̕◌̀◌֮b; a◌֮◌֓◌̀◌̕b; a◌֮◌֓◌̀◌̕b; a◌֮◌֓◌̀◌̕b; a◌֮◌֓◌̀◌̕b; ) LATIN SMALL LETTER A, HEBREW ACCENT SHALSHELET, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0594 0062;00E0 05AE 0594 0315 0062;0061 05AE 0300 0594 0315 0062;00E0 05AE 0594 0315 0062;0061 05AE 0300 0594 0315 0062; # (a◌̕◌̀◌֮◌֔b; à◌֮◌֔◌̕b; a◌֮◌̀◌֔◌̕b; à◌֮◌֔◌̕b; a◌֮◌̀◌֔◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, HEBREW ACCENT ZAQEF QATAN, LATIN SMALL LETTER B
+0061 0594 0315 0300 05AE 0062;0061 05AE 0594 0300 0315 0062;0061 05AE 0594 0300 0315 0062;0061 05AE 0594 0300 0315 0062;0061 05AE 0594 0300 0315 0062; # (a◌֔◌̕◌̀◌֮b; a◌֮◌֔◌̀◌̕b; a◌֮◌֔◌̀◌̕b; a◌֮◌֔◌̀◌̕b; a◌֮◌֔◌̀◌̕b; ) LATIN SMALL LETTER A, HEBREW ACCENT ZAQEF QATAN, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0595 0062;00E0 05AE 0595 0315 0062;0061 05AE 0300 0595 0315 0062;00E0 05AE 0595 0315 0062;0061 05AE 0300 0595 0315 0062; # (a◌̕◌̀◌֮◌֕b; à◌֮◌֕◌̕b; a◌֮◌̀◌֕◌̕b; à◌֮◌֕◌̕b; a◌֮◌̀◌֕◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, HEBREW ACCENT ZAQEF GADOL, LATIN SMALL LETTER B
+0061 0595 0315 0300 05AE 0062;0061 05AE 0595 0300 0315 0062;0061 05AE 0595 0300 0315 0062;0061 05AE 0595 0300 0315 0062;0061 05AE 0595 0300 0315 0062; # (a◌֕◌̕◌̀◌֮b; a◌֮◌֕◌̀◌̕b; a◌֮◌֕◌̀◌̕b; a◌֮◌֕◌̀◌̕b; a◌֮◌֕◌̀◌̕b; ) LATIN SMALL LETTER A, HEBREW ACCENT ZAQEF GADOL, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 059A 0316 302A 0596 0062;0061 302A 0316 0596 059A 0062;0061 302A 0316 0596 059A 0062;0061 302A 0316 0596 059A 0062;0061 302A 0316 0596 059A 0062; # (a◌֚◌̖◌〪◌֖b; a◌〪◌̖◌֖◌֚b; a◌〪◌̖◌֖◌֚b; a◌〪◌̖◌֖◌֚b; a◌〪◌̖◌֖◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, HEBREW ACCENT TIPEHA, LATIN SMALL LETTER B
+0061 0596 059A 0316 302A 0062;0061 302A 0596 0316 059A 0062;0061 302A 0596 0316 059A 0062;0061 302A 0596 0316 059A 0062;0061 302A 0596 0316 059A 0062; # (a◌֖◌֚◌̖◌〪b; a◌〪◌֖◌̖◌֚b; a◌〪◌֖◌̖◌֚b; a◌〪◌֖◌̖◌֚b; a◌〪◌֖◌̖◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT TIPEHA, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0597 0062;00E0 05AE 0597 0315 0062;0061 05AE 0300 0597 0315 0062;00E0 05AE 0597 0315 0062;0061 05AE 0300 0597 0315 0062; # (a◌̕◌̀◌֮◌֗b; à◌֮◌֗◌̕b; a◌֮◌̀◌֗◌̕b; à◌֮◌֗◌̕b; a◌֮◌̀◌֗◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, HEBREW ACCENT REVIA, LATIN SMALL LETTER B
+0061 0597 0315 0300 05AE 0062;0061 05AE 0597 0300 0315 0062;0061 05AE 0597 0300 0315 0062;0061 05AE 0597 0300 0315 0062;0061 05AE 0597 0300 0315 0062; # (a◌֗◌̕◌̀◌֮b; a◌֮◌֗◌̀◌̕b; a◌֮◌֗◌̀◌̕b; a◌֮◌֗◌̀◌̕b; a◌֮◌֗◌̀◌̕b; ) LATIN SMALL LETTER A, HEBREW ACCENT REVIA, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0598 0062;00E0 05AE 0598 0315 0062;0061 05AE 0300 0598 0315 0062;00E0 05AE 0598 0315 0062;0061 05AE 0300 0598 0315 0062; # (a◌̕◌̀◌֮◌֘b; à◌֮◌֘◌̕b; a◌֮◌̀◌֘◌̕b; à◌֮◌֘◌̕b; a◌֮◌̀◌֘◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, HEBREW ACCENT ZARQA, LATIN SMALL LETTER B
+0061 0598 0315 0300 05AE 0062;0061 05AE 0598 0300 0315 0062;0061 05AE 0598 0300 0315 0062;0061 05AE 0598 0300 0315 0062;0061 05AE 0598 0300 0315 0062; # (a◌֘◌̕◌̀◌֮b; a◌֮◌֘◌̀◌̕b; a◌֮◌֘◌̀◌̕b; a◌֮◌֘◌̀◌̕b; a◌֮◌֘◌̀◌̕b; ) LATIN SMALL LETTER A, HEBREW ACCENT ZARQA, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0599 0062;00E0 05AE 0599 0315 0062;0061 05AE 0300 0599 0315 0062;00E0 05AE 0599 0315 0062;0061 05AE 0300 0599 0315 0062; # (a◌̕◌̀◌֮◌֙b; à◌֮◌֙◌̕b; a◌֮◌̀◌֙◌̕b; à◌֮◌֙◌̕b; a◌֮◌̀◌֙◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, HEBREW ACCENT PASHTA, LATIN SMALL LETTER B
+0061 0599 0315 0300 05AE 0062;0061 05AE 0599 0300 0315 0062;0061 05AE 0599 0300 0315 0062;0061 05AE 0599 0300 0315 0062;0061 05AE 0599 0300 0315 0062; # (a◌֙◌̕◌̀◌֮b; a◌֮◌֙◌̀◌̕b; a◌֮◌֙◌̀◌̕b; a◌֮◌֙◌̀◌̕b; a◌֮◌֙◌̀◌̕b; ) LATIN SMALL LETTER A, HEBREW ACCENT PASHTA, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 302E 059A 0316 059A 0062;0061 0316 059A 059A 302E 0062;0061 0316 059A 059A 302E 0062;0061 0316 059A 059A 302E 0062;0061 0316 059A 059A 302E 0062; # (a◌〮◌֚◌̖◌֚b; a◌̖◌֚◌֚◌〮b; a◌̖◌֚◌֚◌〮b; a◌̖◌֚◌֚◌〮b; a◌̖◌֚◌֚◌〮b; ) LATIN SMALL LETTER A, HANGUL SINGLE DOT TONE MARK, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, HEBREW ACCENT YETIV, LATIN SMALL LETTER B
+0061 059A 302E 059A 0316 0062;0061 0316 059A 059A 302E 0062;0061 0316 059A 059A 302E 0062;0061 0316 059A 059A 302E 0062;0061 0316 059A 059A 302E 0062; # (a◌֚◌〮◌֚◌̖b; a◌̖◌֚◌֚◌〮b; a◌̖◌֚◌֚◌〮b; a◌̖◌֚◌֚◌〮b; a◌̖◌֚◌֚◌〮b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, HANGUL SINGLE DOT TONE MARK, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, LATIN SMALL LETTER B
+0061 059A 0316 302A 059B 0062;0061 302A 0316 059B 059A 0062;0061 302A 0316 059B 059A 0062;0061 302A 0316 059B 059A 0062;0061 302A 0316 059B 059A 0062; # (a◌֚◌̖◌〪◌֛b; a◌〪◌̖◌֛◌֚b; a◌〪◌̖◌֛◌֚b; a◌〪◌̖◌֛◌֚b; a◌〪◌̖◌֛◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, HEBREW ACCENT TEVIR, LATIN SMALL LETTER B
+0061 059B 059A 0316 302A 0062;0061 302A 059B 0316 059A 0062;0061 302A 059B 0316 059A 0062;0061 302A 059B 0316 059A 0062;0061 302A 059B 0316 059A 0062; # (a◌֛◌֚◌̖◌〪b; a◌〪◌֛◌̖◌֚b; a◌〪◌֛◌̖◌֚b; a◌〪◌֛◌̖◌֚b; a◌〪◌֛◌̖◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT TEVIR, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 0315 0300 05AE 059C 0062;00E0 05AE 059C 0315 0062;0061 05AE 0300 059C 0315 0062;00E0 05AE 059C 0315 0062;0061 05AE 0300 059C 0315 0062; # (a◌̕◌̀◌֮◌֜b; à◌֮◌֜◌̕b; a◌֮◌̀◌֜◌̕b; à◌֮◌֜◌̕b; a◌֮◌̀◌֜◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, HEBREW ACCENT GERESH, LATIN SMALL LETTER B
+0061 059C 0315 0300 05AE 0062;0061 05AE 059C 0300 0315 0062;0061 05AE 059C 0300 0315 0062;0061 05AE 059C 0300 0315 0062;0061 05AE 059C 0300 0315 0062; # (a◌֜◌̕◌̀◌֮b; a◌֮◌֜◌̀◌̕b; a◌֮◌֜◌̀◌̕b; a◌֮◌֜◌̀◌̕b; a◌֮◌֜◌̀◌̕b; ) LATIN SMALL LETTER A, HEBREW ACCENT GERESH, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 059D 0062;00E0 05AE 059D 0315 0062;0061 05AE 0300 059D 0315 0062;00E0 05AE 059D 0315 0062;0061 05AE 0300 059D 0315 0062; # (a◌̕◌̀◌֮◌Öb; à◌֮◌Ö◌̕b; a◌֮◌̀◌Ö◌̕b; à◌֮◌Ö◌̕b; a◌֮◌̀◌Ö◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, HEBREW ACCENT GERESH MUQDAM, LATIN SMALL LETTER B
+0061 059D 0315 0300 05AE 0062;0061 05AE 059D 0300 0315 0062;0061 05AE 059D 0300 0315 0062;0061 05AE 059D 0300 0315 0062;0061 05AE 059D 0300 0315 0062; # (aâ—ŒÖ◌̕◌̀◌֮b; a◌֮◌Ö◌̀◌̕b; a◌֮◌Ö◌̀◌̕b; a◌֮◌Ö◌̀◌̕b; a◌֮◌Ö◌̀◌̕b; ) LATIN SMALL LETTER A, HEBREW ACCENT GERESH MUQDAM, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 059E 0062;00E0 05AE 059E 0315 0062;0061 05AE 0300 059E 0315 0062;00E0 05AE 059E 0315 0062;0061 05AE 0300 059E 0315 0062; # (a◌̕◌̀◌֮◌֞b; à◌֮◌֞◌̕b; a◌֮◌̀◌֞◌̕b; à◌֮◌֞◌̕b; a◌֮◌̀◌֞◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, HEBREW ACCENT GERSHAYIM, LATIN SMALL LETTER B
+0061 059E 0315 0300 05AE 0062;0061 05AE 059E 0300 0315 0062;0061 05AE 059E 0300 0315 0062;0061 05AE 059E 0300 0315 0062;0061 05AE 059E 0300 0315 0062; # (a◌֞◌̕◌̀◌֮b; a◌֮◌֞◌̀◌̕b; a◌֮◌֞◌̀◌̕b; a◌֮◌֞◌̀◌̕b; a◌֮◌֞◌̀◌̕b; ) LATIN SMALL LETTER A, HEBREW ACCENT GERSHAYIM, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 059F 0062;00E0 05AE 059F 0315 0062;0061 05AE 0300 059F 0315 0062;00E0 05AE 059F 0315 0062;0061 05AE 0300 059F 0315 0062; # (a◌̕◌̀◌֮◌֟b; à◌֮◌֟◌̕b; a◌֮◌̀◌֟◌̕b; à◌֮◌֟◌̕b; a◌֮◌̀◌֟◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, HEBREW ACCENT QARNEY PARA, LATIN SMALL LETTER B
+0061 059F 0315 0300 05AE 0062;0061 05AE 059F 0300 0315 0062;0061 05AE 059F 0300 0315 0062;0061 05AE 059F 0300 0315 0062;0061 05AE 059F 0300 0315 0062; # (a◌֟◌̕◌̀◌֮b; a◌֮◌֟◌̀◌̕b; a◌֮◌֟◌̀◌̕b; a◌֮◌֟◌̀◌̕b; a◌֮◌֟◌̀◌̕b; ) LATIN SMALL LETTER A, HEBREW ACCENT QARNEY PARA, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 05A0 0062;00E0 05AE 05A0 0315 0062;0061 05AE 0300 05A0 0315 0062;00E0 05AE 05A0 0315 0062;0061 05AE 0300 05A0 0315 0062; # (a◌̕◌̀◌֮◌֠b; à◌֮◌֠◌̕b; a◌֮◌̀◌֠◌̕b; à◌֮◌֠◌̕b; a◌֮◌̀◌֠◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, HEBREW ACCENT TELISHA GEDOLA, LATIN SMALL LETTER B
+0061 05A0 0315 0300 05AE 0062;0061 05AE 05A0 0300 0315 0062;0061 05AE 05A0 0300 0315 0062;0061 05AE 05A0 0300 0315 0062;0061 05AE 05A0 0300 0315 0062; # (a◌֠◌̕◌̀◌֮b; a◌֮◌֠◌̀◌̕b; a◌֮◌֠◌̀◌̕b; a◌֮◌֠◌̀◌̕b; a◌֮◌֠◌̀◌̕b; ) LATIN SMALL LETTER A, HEBREW ACCENT TELISHA GEDOLA, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 05A1 0062;00E0 05AE 05A1 0315 0062;0061 05AE 0300 05A1 0315 0062;00E0 05AE 05A1 0315 0062;0061 05AE 0300 05A1 0315 0062; # (a◌̕◌̀◌֮◌֡b; à◌֮◌֡◌̕b; a◌֮◌̀◌֡◌̕b; à◌֮◌֡◌̕b; a◌֮◌̀◌֡◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, HEBREW ACCENT PAZER, LATIN SMALL LETTER B
+0061 05A1 0315 0300 05AE 0062;0061 05AE 05A1 0300 0315 0062;0061 05AE 05A1 0300 0315 0062;0061 05AE 05A1 0300 0315 0062;0061 05AE 05A1 0300 0315 0062; # (a◌֡◌̕◌̀◌֮b; a◌֮◌֡◌̀◌̕b; a◌֮◌֡◌̀◌̕b; a◌֮◌֡◌̀◌̕b; a◌֮◌֡◌̀◌̕b; ) LATIN SMALL LETTER A, HEBREW ACCENT PAZER, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 059A 0316 302A 05A3 0062;0061 302A 0316 05A3 059A 0062;0061 302A 0316 05A3 059A 0062;0061 302A 0316 05A3 059A 0062;0061 302A 0316 05A3 059A 0062; # (a◌֚◌̖◌〪◌֣b; a◌〪◌̖◌֣◌֚b; a◌〪◌̖◌֣◌֚b; a◌〪◌̖◌֣◌֚b; a◌〪◌̖◌֣◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, HEBREW ACCENT MUNAH, LATIN SMALL LETTER B
+0061 05A3 059A 0316 302A 0062;0061 302A 05A3 0316 059A 0062;0061 302A 05A3 0316 059A 0062;0061 302A 05A3 0316 059A 0062;0061 302A 05A3 0316 059A 0062; # (a◌֣◌֚◌̖◌〪b; a◌〪◌֣◌̖◌֚b; a◌〪◌֣◌̖◌֚b; a◌〪◌֣◌̖◌֚b; a◌〪◌֣◌̖◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT MUNAH, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 059A 0316 302A 05A4 0062;0061 302A 0316 05A4 059A 0062;0061 302A 0316 05A4 059A 0062;0061 302A 0316 05A4 059A 0062;0061 302A 0316 05A4 059A 0062; # (a◌֚◌̖◌〪◌֤b; a◌〪◌̖◌֤◌֚b; a◌〪◌̖◌֤◌֚b; a◌〪◌̖◌֤◌֚b; a◌〪◌̖◌֤◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, HEBREW ACCENT MAHAPAKH, LATIN SMALL LETTER B
+0061 05A4 059A 0316 302A 0062;0061 302A 05A4 0316 059A 0062;0061 302A 05A4 0316 059A 0062;0061 302A 05A4 0316 059A 0062;0061 302A 05A4 0316 059A 0062; # (a◌֤◌֚◌̖◌〪b; a◌〪◌֤◌̖◌֚b; a◌〪◌֤◌̖◌֚b; a◌〪◌֤◌̖◌֚b; a◌〪◌֤◌̖◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT MAHAPAKH, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 059A 0316 302A 05A5 0062;0061 302A 0316 05A5 059A 0062;0061 302A 0316 05A5 059A 0062;0061 302A 0316 05A5 059A 0062;0061 302A 0316 05A5 059A 0062; # (a◌֚◌̖◌〪◌֥b; a◌〪◌̖◌֥◌֚b; a◌〪◌̖◌֥◌֚b; a◌〪◌̖◌֥◌֚b; a◌〪◌̖◌֥◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, HEBREW ACCENT MERKHA, LATIN SMALL LETTER B
+0061 05A5 059A 0316 302A 0062;0061 302A 05A5 0316 059A 0062;0061 302A 05A5 0316 059A 0062;0061 302A 05A5 0316 059A 0062;0061 302A 05A5 0316 059A 0062; # (a◌֥◌֚◌̖◌〪b; a◌〪◌֥◌̖◌֚b; a◌〪◌֥◌̖◌֚b; a◌〪◌֥◌̖◌֚b; a◌〪◌֥◌̖◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT MERKHA, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 059A 0316 302A 05A6 0062;0061 302A 0316 05A6 059A 0062;0061 302A 0316 05A6 059A 0062;0061 302A 0316 05A6 059A 0062;0061 302A 0316 05A6 059A 0062; # (a◌֚◌̖◌〪◌֦b; a◌〪◌̖◌֦◌֚b; a◌〪◌̖◌֦◌֚b; a◌〪◌̖◌֦◌֚b; a◌〪◌̖◌֦◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, HEBREW ACCENT MERKHA KEFULA, LATIN SMALL LETTER B
+0061 05A6 059A 0316 302A 0062;0061 302A 05A6 0316 059A 0062;0061 302A 05A6 0316 059A 0062;0061 302A 05A6 0316 059A 0062;0061 302A 05A6 0316 059A 0062; # (a◌֦◌֚◌̖◌〪b; a◌〪◌֦◌̖◌֚b; a◌〪◌֦◌̖◌֚b; a◌〪◌֦◌̖◌֚b; a◌〪◌֦◌̖◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT MERKHA KEFULA, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 059A 0316 302A 05A7 0062;0061 302A 0316 05A7 059A 0062;0061 302A 0316 05A7 059A 0062;0061 302A 0316 05A7 059A 0062;0061 302A 0316 05A7 059A 0062; # (a◌֚◌̖◌〪◌֧b; a◌〪◌̖◌֧◌֚b; a◌〪◌̖◌֧◌֚b; a◌〪◌̖◌֧◌֚b; a◌〪◌̖◌֧◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, HEBREW ACCENT DARGA, LATIN SMALL LETTER B
+0061 05A7 059A 0316 302A 0062;0061 302A 05A7 0316 059A 0062;0061 302A 05A7 0316 059A 0062;0061 302A 05A7 0316 059A 0062;0061 302A 05A7 0316 059A 0062; # (a◌֧◌֚◌̖◌〪b; a◌〪◌֧◌̖◌֚b; a◌〪◌֧◌̖◌֚b; a◌〪◌֧◌̖◌֚b; a◌〪◌֧◌̖◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT DARGA, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 0315 0300 05AE 05A8 0062;00E0 05AE 05A8 0315 0062;0061 05AE 0300 05A8 0315 0062;00E0 05AE 05A8 0315 0062;0061 05AE 0300 05A8 0315 0062; # (a◌̕◌̀◌֮◌֨b; à◌֮◌֨◌̕b; a◌֮◌̀◌֨◌̕b; à◌֮◌֨◌̕b; a◌֮◌̀◌֨◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, HEBREW ACCENT QADMA, LATIN SMALL LETTER B
+0061 05A8 0315 0300 05AE 0062;0061 05AE 05A8 0300 0315 0062;0061 05AE 05A8 0300 0315 0062;0061 05AE 05A8 0300 0315 0062;0061 05AE 05A8 0300 0315 0062; # (a◌֨◌̕◌̀◌֮b; a◌֮◌֨◌̀◌̕b; a◌֮◌֨◌̀◌̕b; a◌֮◌֨◌̀◌̕b; a◌֮◌֨◌̀◌̕b; ) LATIN SMALL LETTER A, HEBREW ACCENT QADMA, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 05A9 0062;00E0 05AE 05A9 0315 0062;0061 05AE 0300 05A9 0315 0062;00E0 05AE 05A9 0315 0062;0061 05AE 0300 05A9 0315 0062; # (a◌̕◌̀◌֮◌֩b; à◌֮◌֩◌̕b; a◌֮◌̀◌֩◌̕b; à◌֮◌֩◌̕b; a◌֮◌̀◌֩◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, HEBREW ACCENT TELISHA QETANA, LATIN SMALL LETTER B
+0061 05A9 0315 0300 05AE 0062;0061 05AE 05A9 0300 0315 0062;0061 05AE 05A9 0300 0315 0062;0061 05AE 05A9 0300 0315 0062;0061 05AE 05A9 0300 0315 0062; # (a◌֩◌̕◌̀◌֮b; a◌֮◌֩◌̀◌̕b; a◌֮◌֩◌̀◌̕b; a◌֮◌֩◌̀◌̕b; a◌֮◌֩◌̀◌̕b; ) LATIN SMALL LETTER A, HEBREW ACCENT TELISHA QETANA, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 059A 0316 302A 05AA 0062;0061 302A 0316 05AA 059A 0062;0061 302A 0316 05AA 059A 0062;0061 302A 0316 05AA 059A 0062;0061 302A 0316 05AA 059A 0062; # (a◌֚◌̖◌〪◌֪b; a◌〪◌̖◌֪◌֚b; a◌〪◌̖◌֪◌֚b; a◌〪◌̖◌֪◌֚b; a◌〪◌̖◌֪◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, HEBREW ACCENT YERAH BEN YOMO, LATIN SMALL LETTER B
+0061 05AA 059A 0316 302A 0062;0061 302A 05AA 0316 059A 0062;0061 302A 05AA 0316 059A 0062;0061 302A 05AA 0316 059A 0062;0061 302A 05AA 0316 059A 0062; # (a◌֪◌֚◌̖◌〪b; a◌〪◌֪◌̖◌֚b; a◌〪◌֪◌̖◌֚b; a◌〪◌֪◌̖◌֚b; a◌〪◌֪◌̖◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YERAH BEN YOMO, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 0315 0300 05AE 05AB 0062;00E0 05AE 05AB 0315 0062;0061 05AE 0300 05AB 0315 0062;00E0 05AE 05AB 0315 0062;0061 05AE 0300 05AB 0315 0062; # (a◌̕◌̀◌֮◌֫b; à◌֮◌֫◌̕b; a◌֮◌̀◌֫◌̕b; à◌֮◌֫◌̕b; a◌֮◌̀◌֫◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, HEBREW ACCENT OLE, LATIN SMALL LETTER B
+0061 05AB 0315 0300 05AE 0062;0061 05AE 05AB 0300 0315 0062;0061 05AE 05AB 0300 0315 0062;0061 05AE 05AB 0300 0315 0062;0061 05AE 05AB 0300 0315 0062; # (a◌֫◌̕◌̀◌֮b; a◌֮◌֫◌̀◌̕b; a◌֮◌֫◌̀◌̕b; a◌֮◌֫◌̀◌̕b; a◌֮◌֫◌̀◌̕b; ) LATIN SMALL LETTER A, HEBREW ACCENT OLE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 05AC 0062;00E0 05AE 05AC 0315 0062;0061 05AE 0300 05AC 0315 0062;00E0 05AE 05AC 0315 0062;0061 05AE 0300 05AC 0315 0062; # (a◌̕◌̀◌֮◌֬b; à◌֮◌֬◌̕b; a◌֮◌̀◌֬◌̕b; à◌֮◌֬◌̕b; a◌֮◌̀◌֬◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, HEBREW ACCENT ILUY, LATIN SMALL LETTER B
+0061 05AC 0315 0300 05AE 0062;0061 05AE 05AC 0300 0315 0062;0061 05AE 05AC 0300 0315 0062;0061 05AE 05AC 0300 0315 0062;0061 05AE 05AC 0300 0315 0062; # (a◌֬◌̕◌̀◌֮b; a◌֮◌֬◌̀◌̕b; a◌֮◌֬◌̀◌̕b; a◌֮◌֬◌̀◌̕b; a◌֮◌֬◌̀◌̕b; ) LATIN SMALL LETTER A, HEBREW ACCENT ILUY, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 302E 059A 0316 05AD 0062;0061 0316 059A 05AD 302E 0062;0061 0316 059A 05AD 302E 0062;0061 0316 059A 05AD 302E 0062;0061 0316 059A 05AD 302E 0062; # (a◌〮◌֚◌̖◌֭b; a◌̖◌֚◌֭◌〮b; a◌̖◌֚◌֭◌〮b; a◌̖◌֚◌֭◌〮b; a◌̖◌֚◌֭◌〮b; ) LATIN SMALL LETTER A, HANGUL SINGLE DOT TONE MARK, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, HEBREW ACCENT DEHI, LATIN SMALL LETTER B
+0061 05AD 302E 059A 0316 0062;0061 0316 05AD 059A 302E 0062;0061 0316 05AD 059A 302E 0062;0061 0316 05AD 059A 302E 0062;0061 0316 05AD 059A 302E 0062; # (a◌֭◌〮◌֚◌̖b; a◌̖◌֭◌֚◌〮b; a◌̖◌֭◌֚◌〮b; a◌̖◌֭◌֚◌〮b; a◌̖◌֭◌֚◌〮b; ) LATIN SMALL LETTER A, HEBREW ACCENT DEHI, HANGUL SINGLE DOT TONE MARK, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, LATIN SMALL LETTER B
+0061 0300 05AE 1D16D 05AE 0062;00E0 1D16D 05AE 05AE 0062;0061 1D16D 05AE 05AE 0300 0062;00E0 1D16D 05AE 05AE 0062;0061 1D16D 05AE 05AE 0300 0062; # (a◌̀◌֮ð…­ð…­â—ŒÖ®b; àð…­ð…­â—ŒÖ®â—ŒÖ®b; að…­ð…­â—ŒÖ®â—ŒÖ®â—ŒÌ€b; àð…­ð…­â—ŒÖ®â—ŒÖ®b; að…­ð…­â—ŒÖ®â—ŒÖ®â—ŒÌ€b; ) LATIN SMALL LETTER A, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, MUSICAL SYMBOL COMBINING AUGMENTATION DOT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 05AE 0300 05AE 1D16D 0062;00E0 1D16D 05AE 05AE 0062;0061 1D16D 05AE 05AE 0300 0062;00E0 1D16D 05AE 05AE 0062;0061 1D16D 05AE 05AE 0300 0062; # (a◌֮◌̀◌֮ð…­ð…­b; àð…­ð…­â—ŒÖ®â—ŒÖ®b; að…­ð…­â—ŒÖ®â—ŒÖ®â—ŒÌ€b; àð…­ð…­â—ŒÖ®â—ŒÖ®b; að…­ð…­â—ŒÖ®â—ŒÖ®â—ŒÌ€b; ) LATIN SMALL LETTER A, HEBREW ACCENT ZINOR, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, MUSICAL SYMBOL COMBINING AUGMENTATION DOT, LATIN SMALL LETTER B
+0061 0315 0300 05AE 05AF 0062;00E0 05AE 05AF 0315 0062;0061 05AE 0300 05AF 0315 0062;00E0 05AE 05AF 0315 0062;0061 05AE 0300 05AF 0315 0062; # (a◌̕◌̀◌֮◌֯b; à◌֮◌֯◌̕b; a◌֮◌̀◌֯◌̕b; à◌֮◌֯◌̕b; a◌֮◌̀◌֯◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, HEBREW MARK MASORA CIRCLE, LATIN SMALL LETTER B
+0061 05AF 0315 0300 05AE 0062;0061 05AE 05AF 0300 0315 0062;0061 05AE 05AF 0300 0315 0062;0061 05AE 05AF 0300 0315 0062;0061 05AE 05AF 0300 0315 0062; # (a◌֯◌̕◌̀◌֮b; a◌֮◌֯◌̀◌̕b; a◌֮◌֯◌̀◌̕b; a◌֮◌֯◌̀◌̕b; a◌֮◌֯◌̀◌̕b; ) LATIN SMALL LETTER A, HEBREW MARK MASORA CIRCLE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 05B1 05B0 094D 05B0 0062;0061 094D 05B0 05B0 05B1 0062;0061 094D 05B0 05B0 05B1 0062;0061 094D 05B0 05B0 05B1 0062;0061 094D 05B0 05B0 05B1 0062; # (a◌ֱ◌ְ◌à¥â—ŒÖ°b; aâ—Œà¥â—ŒÖ°â—ŒÖ°â—ŒÖ±b; aâ—Œà¥â—ŒÖ°â—ŒÖ°â—ŒÖ±b; aâ—Œà¥â—ŒÖ°â—ŒÖ°â—ŒÖ±b; aâ—Œà¥â—ŒÖ°â—ŒÖ°â—ŒÖ±b; ) LATIN SMALL LETTER A, HEBREW POINT HATAF SEGOL, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, HEBREW POINT SHEVA, LATIN SMALL LETTER B
+0061 05B0 05B1 05B0 094D 0062;0061 094D 05B0 05B0 05B1 0062;0061 094D 05B0 05B0 05B1 0062;0061 094D 05B0 05B0 05B1 0062;0061 094D 05B0 05B0 05B1 0062; # (a◌ְ◌ֱ◌ְ◌à¥b; aâ—Œà¥â—ŒÖ°â—ŒÖ°â—ŒÖ±b; aâ—Œà¥â—ŒÖ°â—ŒÖ°â—ŒÖ±b; aâ—Œà¥â—ŒÖ°â—ŒÖ°â—ŒÖ±b; aâ—Œà¥â—ŒÖ°â—ŒÖ°â—ŒÖ±b; ) LATIN SMALL LETTER A, HEBREW POINT SHEVA, HEBREW POINT HATAF SEGOL, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, LATIN SMALL LETTER B
+0061 05B2 05B1 05B0 05B1 0062;0061 05B0 05B1 05B1 05B2 0062;0061 05B0 05B1 05B1 05B2 0062;0061 05B0 05B1 05B1 05B2 0062;0061 05B0 05B1 05B1 05B2 0062; # (a◌ֲ◌ֱ◌ְ◌ֱb; a◌ְ◌ֱ◌ֱ◌ֲb; a◌ְ◌ֱ◌ֱ◌ֲb; a◌ְ◌ֱ◌ֱ◌ֲb; a◌ְ◌ֱ◌ֱ◌ֲb; ) LATIN SMALL LETTER A, HEBREW POINT HATAF PATAH, HEBREW POINT HATAF SEGOL, HEBREW POINT SHEVA, HEBREW POINT HATAF SEGOL, LATIN SMALL LETTER B
+0061 05B1 05B2 05B1 05B0 0062;0061 05B0 05B1 05B1 05B2 0062;0061 05B0 05B1 05B1 05B2 0062;0061 05B0 05B1 05B1 05B2 0062;0061 05B0 05B1 05B1 05B2 0062; # (a◌ֱ◌ֲ◌ֱ◌ְb; a◌ְ◌ֱ◌ֱ◌ֲb; a◌ְ◌ֱ◌ֱ◌ֲb; a◌ְ◌ֱ◌ֱ◌ֲb; a◌ְ◌ֱ◌ֱ◌ֲb; ) LATIN SMALL LETTER A, HEBREW POINT HATAF SEGOL, HEBREW POINT HATAF PATAH, HEBREW POINT HATAF SEGOL, HEBREW POINT SHEVA, LATIN SMALL LETTER B
+0061 05B3 05B2 05B1 05B2 0062;0061 05B1 05B2 05B2 05B3 0062;0061 05B1 05B2 05B2 05B3 0062;0061 05B1 05B2 05B2 05B3 0062;0061 05B1 05B2 05B2 05B3 0062; # (a◌ֳ◌ֲ◌ֱ◌ֲb; a◌ֱ◌ֲ◌ֲ◌ֳb; a◌ֱ◌ֲ◌ֲ◌ֳb; a◌ֱ◌ֲ◌ֲ◌ֳb; a◌ֱ◌ֲ◌ֲ◌ֳb; ) LATIN SMALL LETTER A, HEBREW POINT HATAF QAMATS, HEBREW POINT HATAF PATAH, HEBREW POINT HATAF SEGOL, HEBREW POINT HATAF PATAH, LATIN SMALL LETTER B
+0061 05B2 05B3 05B2 05B1 0062;0061 05B1 05B2 05B2 05B3 0062;0061 05B1 05B2 05B2 05B3 0062;0061 05B1 05B2 05B2 05B3 0062;0061 05B1 05B2 05B2 05B3 0062; # (a◌ֲ◌ֳ◌ֲ◌ֱb; a◌ֱ◌ֲ◌ֲ◌ֳb; a◌ֱ◌ֲ◌ֲ◌ֳb; a◌ֱ◌ֲ◌ֲ◌ֳb; a◌ֱ◌ֲ◌ֲ◌ֳb; ) LATIN SMALL LETTER A, HEBREW POINT HATAF PATAH, HEBREW POINT HATAF QAMATS, HEBREW POINT HATAF PATAH, HEBREW POINT HATAF SEGOL, LATIN SMALL LETTER B
+0061 05B4 05B3 05B2 05B3 0062;0061 05B2 05B3 05B3 05B4 0062;0061 05B2 05B3 05B3 05B4 0062;0061 05B2 05B3 05B3 05B4 0062;0061 05B2 05B3 05B3 05B4 0062; # (a◌ִ◌ֳ◌ֲ◌ֳb; a◌ֲ◌ֳ◌ֳ◌ִb; a◌ֲ◌ֳ◌ֳ◌ִb; a◌ֲ◌ֳ◌ֳ◌ִb; a◌ֲ◌ֳ◌ֳ◌ִb; ) LATIN SMALL LETTER A, HEBREW POINT HIRIQ, HEBREW POINT HATAF QAMATS, HEBREW POINT HATAF PATAH, HEBREW POINT HATAF QAMATS, LATIN SMALL LETTER B
+0061 05B3 05B4 05B3 05B2 0062;0061 05B2 05B3 05B3 05B4 0062;0061 05B2 05B3 05B3 05B4 0062;0061 05B2 05B3 05B3 05B4 0062;0061 05B2 05B3 05B3 05B4 0062; # (a◌ֳ◌ִ◌ֳ◌ֲb; a◌ֲ◌ֳ◌ֳ◌ִb; a◌ֲ◌ֳ◌ֳ◌ִb; a◌ֲ◌ֳ◌ֳ◌ִb; a◌ֲ◌ֳ◌ֳ◌ִb; ) LATIN SMALL LETTER A, HEBREW POINT HATAF QAMATS, HEBREW POINT HIRIQ, HEBREW POINT HATAF QAMATS, HEBREW POINT HATAF PATAH, LATIN SMALL LETTER B
+0061 05B5 05B4 05B3 05B4 0062;0061 05B3 05B4 05B4 05B5 0062;0061 05B3 05B4 05B4 05B5 0062;0061 05B3 05B4 05B4 05B5 0062;0061 05B3 05B4 05B4 05B5 0062; # (a◌ֵ◌ִ◌ֳ◌ִb; a◌ֳ◌ִ◌ִ◌ֵb; a◌ֳ◌ִ◌ִ◌ֵb; a◌ֳ◌ִ◌ִ◌ֵb; a◌ֳ◌ִ◌ִ◌ֵb; ) LATIN SMALL LETTER A, HEBREW POINT TSERE, HEBREW POINT HIRIQ, HEBREW POINT HATAF QAMATS, HEBREW POINT HIRIQ, LATIN SMALL LETTER B
+0061 05B4 05B5 05B4 05B3 0062;0061 05B3 05B4 05B4 05B5 0062;0061 05B3 05B4 05B4 05B5 0062;0061 05B3 05B4 05B4 05B5 0062;0061 05B3 05B4 05B4 05B5 0062; # (a◌ִ◌ֵ◌ִ◌ֳb; a◌ֳ◌ִ◌ִ◌ֵb; a◌ֳ◌ִ◌ִ◌ֵb; a◌ֳ◌ִ◌ִ◌ֵb; a◌ֳ◌ִ◌ִ◌ֵb; ) LATIN SMALL LETTER A, HEBREW POINT HIRIQ, HEBREW POINT TSERE, HEBREW POINT HIRIQ, HEBREW POINT HATAF QAMATS, LATIN SMALL LETTER B
+0061 05B6 05B5 05B4 05B5 0062;0061 05B4 05B5 05B5 05B6 0062;0061 05B4 05B5 05B5 05B6 0062;0061 05B4 05B5 05B5 05B6 0062;0061 05B4 05B5 05B5 05B6 0062; # (a◌ֶ◌ֵ◌ִ◌ֵb; a◌ִ◌ֵ◌ֵ◌ֶb; a◌ִ◌ֵ◌ֵ◌ֶb; a◌ִ◌ֵ◌ֵ◌ֶb; a◌ִ◌ֵ◌ֵ◌ֶb; ) LATIN SMALL LETTER A, HEBREW POINT SEGOL, HEBREW POINT TSERE, HEBREW POINT HIRIQ, HEBREW POINT TSERE, LATIN SMALL LETTER B
+0061 05B5 05B6 05B5 05B4 0062;0061 05B4 05B5 05B5 05B6 0062;0061 05B4 05B5 05B5 05B6 0062;0061 05B4 05B5 05B5 05B6 0062;0061 05B4 05B5 05B5 05B6 0062; # (a◌ֵ◌ֶ◌ֵ◌ִb; a◌ִ◌ֵ◌ֵ◌ֶb; a◌ִ◌ֵ◌ֵ◌ֶb; a◌ִ◌ֵ◌ֵ◌ֶb; a◌ִ◌ֵ◌ֵ◌ֶb; ) LATIN SMALL LETTER A, HEBREW POINT TSERE, HEBREW POINT SEGOL, HEBREW POINT TSERE, HEBREW POINT HIRIQ, LATIN SMALL LETTER B
+0061 05B7 05B6 05B5 05B6 0062;0061 05B5 05B6 05B6 05B7 0062;0061 05B5 05B6 05B6 05B7 0062;0061 05B5 05B6 05B6 05B7 0062;0061 05B5 05B6 05B6 05B7 0062; # (a◌ַ◌ֶ◌ֵ◌ֶb; a◌ֵ◌ֶ◌ֶ◌ַb; a◌ֵ◌ֶ◌ֶ◌ַb; a◌ֵ◌ֶ◌ֶ◌ַb; a◌ֵ◌ֶ◌ֶ◌ַb; ) LATIN SMALL LETTER A, HEBREW POINT PATAH, HEBREW POINT SEGOL, HEBREW POINT TSERE, HEBREW POINT SEGOL, LATIN SMALL LETTER B
+0061 05B6 05B7 05B6 05B5 0062;0061 05B5 05B6 05B6 05B7 0062;0061 05B5 05B6 05B6 05B7 0062;0061 05B5 05B6 05B6 05B7 0062;0061 05B5 05B6 05B6 05B7 0062; # (a◌ֶ◌ַ◌ֶ◌ֵb; a◌ֵ◌ֶ◌ֶ◌ַb; a◌ֵ◌ֶ◌ֶ◌ַb; a◌ֵ◌ֶ◌ֶ◌ַb; a◌ֵ◌ֶ◌ֶ◌ַb; ) LATIN SMALL LETTER A, HEBREW POINT SEGOL, HEBREW POINT PATAH, HEBREW POINT SEGOL, HEBREW POINT TSERE, LATIN SMALL LETTER B
+0061 05B8 05B7 05B6 05B7 0062;0061 05B6 05B7 05B7 05B8 0062;0061 05B6 05B7 05B7 05B8 0062;0061 05B6 05B7 05B7 05B8 0062;0061 05B6 05B7 05B7 05B8 0062; # (a◌ָ◌ַ◌ֶ◌ַb; a◌ֶ◌ַ◌ַ◌ָb; a◌ֶ◌ַ◌ַ◌ָb; a◌ֶ◌ַ◌ַ◌ָb; a◌ֶ◌ַ◌ַ◌ָb; ) LATIN SMALL LETTER A, HEBREW POINT QAMATS, HEBREW POINT PATAH, HEBREW POINT SEGOL, HEBREW POINT PATAH, LATIN SMALL LETTER B
+0061 05B7 05B8 05B7 05B6 0062;0061 05B6 05B7 05B7 05B8 0062;0061 05B6 05B7 05B7 05B8 0062;0061 05B6 05B7 05B7 05B8 0062;0061 05B6 05B7 05B7 05B8 0062; # (a◌ַ◌ָ◌ַ◌ֶb; a◌ֶ◌ַ◌ַ◌ָb; a◌ֶ◌ַ◌ַ◌ָb; a◌ֶ◌ַ◌ַ◌ָb; a◌ֶ◌ַ◌ַ◌ָb; ) LATIN SMALL LETTER A, HEBREW POINT PATAH, HEBREW POINT QAMATS, HEBREW POINT PATAH, HEBREW POINT SEGOL, LATIN SMALL LETTER B
+0061 05B9 05B8 05B7 05B8 0062;0061 05B7 05B8 05B8 05B9 0062;0061 05B7 05B8 05B8 05B9 0062;0061 05B7 05B8 05B8 05B9 0062;0061 05B7 05B8 05B8 05B9 0062; # (a◌ֹ◌ָ◌ַ◌ָb; a◌ַ◌ָ◌ָ◌ֹb; a◌ַ◌ָ◌ָ◌ֹb; a◌ַ◌ָ◌ָ◌ֹb; a◌ַ◌ָ◌ָ◌ֹb; ) LATIN SMALL LETTER A, HEBREW POINT HOLAM, HEBREW POINT QAMATS, HEBREW POINT PATAH, HEBREW POINT QAMATS, LATIN SMALL LETTER B
+0061 05B8 05B9 05B8 05B7 0062;0061 05B7 05B8 05B8 05B9 0062;0061 05B7 05B8 05B8 05B9 0062;0061 05B7 05B8 05B8 05B9 0062;0061 05B7 05B8 05B8 05B9 0062; # (a◌ָ◌ֹ◌ָ◌ַb; a◌ַ◌ָ◌ָ◌ֹb; a◌ַ◌ָ◌ָ◌ֹb; a◌ַ◌ָ◌ָ◌ֹb; a◌ַ◌ָ◌ָ◌ֹb; ) LATIN SMALL LETTER A, HEBREW POINT QAMATS, HEBREW POINT HOLAM, HEBREW POINT QAMATS, HEBREW POINT PATAH, LATIN SMALL LETTER B
+0061 05BB 05B9 05B8 05B9 0062;0061 05B8 05B9 05B9 05BB 0062;0061 05B8 05B9 05B9 05BB 0062;0061 05B8 05B9 05B9 05BB 0062;0061 05B8 05B9 05B9 05BB 0062; # (a◌ֻ◌ֹ◌ָ◌ֹb; a◌ָ◌ֹ◌ֹ◌ֻb; a◌ָ◌ֹ◌ֹ◌ֻb; a◌ָ◌ֹ◌ֹ◌ֻb; a◌ָ◌ֹ◌ֹ◌ֻb; ) LATIN SMALL LETTER A, HEBREW POINT QUBUTS, HEBREW POINT HOLAM, HEBREW POINT QAMATS, HEBREW POINT HOLAM, LATIN SMALL LETTER B
+0061 05B9 05BB 05B9 05B8 0062;0061 05B8 05B9 05B9 05BB 0062;0061 05B8 05B9 05B9 05BB 0062;0061 05B8 05B9 05B9 05BB 0062;0061 05B8 05B9 05B9 05BB 0062; # (a◌ֹ◌ֻ◌ֹ◌ָb; a◌ָ◌ֹ◌ֹ◌ֻb; a◌ָ◌ֹ◌ֹ◌ֻb; a◌ָ◌ֹ◌ֹ◌ֻb; a◌ָ◌ֹ◌ֹ◌ֻb; ) LATIN SMALL LETTER A, HEBREW POINT HOLAM, HEBREW POINT QUBUTS, HEBREW POINT HOLAM, HEBREW POINT QAMATS, LATIN SMALL LETTER B
+0061 05BC 05BB 05B9 05BB 0062;0061 05B9 05BB 05BB 05BC 0062;0061 05B9 05BB 05BB 05BC 0062;0061 05B9 05BB 05BB 05BC 0062;0061 05B9 05BB 05BB 05BC 0062; # (a◌ּ◌ֻ◌ֹ◌ֻb; a◌ֹ◌ֻ◌ֻ◌ּb; a◌ֹ◌ֻ◌ֻ◌ּb; a◌ֹ◌ֻ◌ֻ◌ּb; a◌ֹ◌ֻ◌ֻ◌ּb; ) LATIN SMALL LETTER A, HEBREW POINT DAGESH OR MAPIQ, HEBREW POINT QUBUTS, HEBREW POINT HOLAM, HEBREW POINT QUBUTS, LATIN SMALL LETTER B
+0061 05BB 05BC 05BB 05B9 0062;0061 05B9 05BB 05BB 05BC 0062;0061 05B9 05BB 05BB 05BC 0062;0061 05B9 05BB 05BB 05BC 0062;0061 05B9 05BB 05BB 05BC 0062; # (a◌ֻ◌ּ◌ֻ◌ֹb; a◌ֹ◌ֻ◌ֻ◌ּb; a◌ֹ◌ֻ◌ֻ◌ּb; a◌ֹ◌ֻ◌ֻ◌ּb; a◌ֹ◌ֻ◌ֻ◌ּb; ) LATIN SMALL LETTER A, HEBREW POINT QUBUTS, HEBREW POINT DAGESH OR MAPIQ, HEBREW POINT QUBUTS, HEBREW POINT HOLAM, LATIN SMALL LETTER B
+0061 05BD 05BC 05BB 05BC 0062;0061 05BB 05BC 05BC 05BD 0062;0061 05BB 05BC 05BC 05BD 0062;0061 05BB 05BC 05BC 05BD 0062;0061 05BB 05BC 05BC 05BD 0062; # (a◌ֽ◌ּ◌ֻ◌ּb; a◌ֻ◌ּ◌ּ◌ֽb; a◌ֻ◌ּ◌ּ◌ֽb; a◌ֻ◌ּ◌ּ◌ֽb; a◌ֻ◌ּ◌ּ◌ֽb; ) LATIN SMALL LETTER A, HEBREW POINT METEG, HEBREW POINT DAGESH OR MAPIQ, HEBREW POINT QUBUTS, HEBREW POINT DAGESH OR MAPIQ, LATIN SMALL LETTER B
+0061 05BC 05BD 05BC 05BB 0062;0061 05BB 05BC 05BC 05BD 0062;0061 05BB 05BC 05BC 05BD 0062;0061 05BB 05BC 05BC 05BD 0062;0061 05BB 05BC 05BC 05BD 0062; # (a◌ּ◌ֽ◌ּ◌ֻb; a◌ֻ◌ּ◌ּ◌ֽb; a◌ֻ◌ּ◌ּ◌ֽb; a◌ֻ◌ּ◌ּ◌ֽb; a◌ֻ◌ּ◌ּ◌ֽb; ) LATIN SMALL LETTER A, HEBREW POINT DAGESH OR MAPIQ, HEBREW POINT METEG, HEBREW POINT DAGESH OR MAPIQ, HEBREW POINT QUBUTS, LATIN SMALL LETTER B
+0061 05BF 05BD 05BC 05BD 0062;0061 05BC 05BD 05BD 05BF 0062;0061 05BC 05BD 05BD 05BF 0062;0061 05BC 05BD 05BD 05BF 0062;0061 05BC 05BD 05BD 05BF 0062; # (a◌ֿ◌ֽ◌ּ◌ֽb; a◌ּ◌ֽ◌ֽ◌ֿb; a◌ּ◌ֽ◌ֽ◌ֿb; a◌ּ◌ֽ◌ֽ◌ֿb; a◌ּ◌ֽ◌ֽ◌ֿb; ) LATIN SMALL LETTER A, HEBREW POINT RAFE, HEBREW POINT METEG, HEBREW POINT DAGESH OR MAPIQ, HEBREW POINT METEG, LATIN SMALL LETTER B
+0061 05BD 05BF 05BD 05BC 0062;0061 05BC 05BD 05BD 05BF 0062;0061 05BC 05BD 05BD 05BF 0062;0061 05BC 05BD 05BD 05BF 0062;0061 05BC 05BD 05BD 05BF 0062; # (a◌ֽ◌ֿ◌ֽ◌ּb; a◌ּ◌ֽ◌ֽ◌ֿb; a◌ּ◌ֽ◌ֽ◌ֿb; a◌ּ◌ֽ◌ֽ◌ֿb; a◌ּ◌ֽ◌ֽ◌ֿb; ) LATIN SMALL LETTER A, HEBREW POINT METEG, HEBREW POINT RAFE, HEBREW POINT METEG, HEBREW POINT DAGESH OR MAPIQ, LATIN SMALL LETTER B
+0061 05C1 05BF 05BD 05BF 0062;0061 05BD 05BF 05BF 05C1 0062;0061 05BD 05BF 05BF 05C1 0062;0061 05BD 05BF 05BF 05C1 0062;0061 05BD 05BF 05BF 05C1 0062; # (aâ—Œ×◌ֿ◌ֽ◌ֿb; a◌ֽ◌ֿ◌ֿ◌×b; a◌ֽ◌ֿ◌ֿ◌×b; a◌ֽ◌ֿ◌ֿ◌×b; a◌ֽ◌ֿ◌ֿ◌×b; ) LATIN SMALL LETTER A, HEBREW POINT SHIN DOT, HEBREW POINT RAFE, HEBREW POINT METEG, HEBREW POINT RAFE, LATIN SMALL LETTER B
+0061 05BF 05C1 05BF 05BD 0062;0061 05BD 05BF 05BF 05C1 0062;0061 05BD 05BF 05BF 05C1 0062;0061 05BD 05BF 05BF 05C1 0062;0061 05BD 05BF 05BF 05C1 0062; # (a◌ֿ◌×◌ֿ◌ֽb; a◌ֽ◌ֿ◌ֿ◌×b; a◌ֽ◌ֿ◌ֿ◌×b; a◌ֽ◌ֿ◌ֿ◌×b; a◌ֽ◌ֿ◌ֿ◌×b; ) LATIN SMALL LETTER A, HEBREW POINT RAFE, HEBREW POINT SHIN DOT, HEBREW POINT RAFE, HEBREW POINT METEG, LATIN SMALL LETTER B
+0061 05C2 05C1 05BF 05C1 0062;0061 05BF 05C1 05C1 05C2 0062;0061 05BF 05C1 05C1 05C2 0062;0061 05BF 05C1 05C1 05C2 0062;0061 05BF 05C1 05C1 05C2 0062; # (a◌ׂ◌×◌ֿ◌×b; a◌ֿ◌×â—Œ×◌ׂb; a◌ֿ◌×â—Œ×◌ׂb; a◌ֿ◌×â—Œ×◌ׂb; a◌ֿ◌×â—Œ×◌ׂb; ) LATIN SMALL LETTER A, HEBREW POINT SIN DOT, HEBREW POINT SHIN DOT, HEBREW POINT RAFE, HEBREW POINT SHIN DOT, LATIN SMALL LETTER B
+0061 05C1 05C2 05C1 05BF 0062;0061 05BF 05C1 05C1 05C2 0062;0061 05BF 05C1 05C1 05C2 0062;0061 05BF 05C1 05C1 05C2 0062;0061 05BF 05C1 05C1 05C2 0062; # (aâ—Œ×◌ׂ◌×◌ֿb; a◌ֿ◌×â—Œ×◌ׂb; a◌ֿ◌×â—Œ×◌ׂb; a◌ֿ◌×â—Œ×◌ׂb; a◌ֿ◌×â—Œ×◌ׂb; ) LATIN SMALL LETTER A, HEBREW POINT SHIN DOT, HEBREW POINT SIN DOT, HEBREW POINT SHIN DOT, HEBREW POINT RAFE, LATIN SMALL LETTER B
+0061 FB1E 05C2 05C1 05C2 0062;0061 05C1 05C2 05C2 FB1E 0062;0061 05C1 05C2 05C2 FB1E 0062;0061 05C1 05C2 05C2 FB1E 0062;0061 05C1 05C2 05C2 FB1E 0062; # (a◌ﬞ◌ׂ◌×◌ׂb; aâ—Œ×◌ׂ◌ׂ◌ﬞb; aâ—Œ×◌ׂ◌ׂ◌ﬞb; aâ—Œ×◌ׂ◌ׂ◌ﬞb; aâ—Œ×◌ׂ◌ׂ◌ﬞb; ) LATIN SMALL LETTER A, HEBREW POINT JUDEO-SPANISH VARIKA, HEBREW POINT SIN DOT, HEBREW POINT SHIN DOT, HEBREW POINT SIN DOT, LATIN SMALL LETTER B
+0061 05C2 FB1E 05C2 05C1 0062;0061 05C1 05C2 05C2 FB1E 0062;0061 05C1 05C2 05C2 FB1E 0062;0061 05C1 05C2 05C2 FB1E 0062;0061 05C1 05C2 05C2 FB1E 0062; # (a◌ׂ◌ﬞ◌ׂ◌×b; aâ—Œ×◌ׂ◌ׂ◌ﬞb; aâ—Œ×◌ׂ◌ׂ◌ﬞb; aâ—Œ×◌ׂ◌ׂ◌ﬞb; aâ—Œ×◌ׂ◌ׂ◌ﬞb; ) LATIN SMALL LETTER A, HEBREW POINT SIN DOT, HEBREW POINT JUDEO-SPANISH VARIKA, HEBREW POINT SIN DOT, HEBREW POINT SHIN DOT, LATIN SMALL LETTER B
+0061 0315 0300 05AE 05C4 0062;00E0 05AE 05C4 0315 0062;0061 05AE 0300 05C4 0315 0062;00E0 05AE 05C4 0315 0062;0061 05AE 0300 05C4 0315 0062; # (a◌̕◌̀◌֮◌ׄb; à◌֮◌ׄ◌̕b; a◌֮◌̀◌ׄ◌̕b; à◌֮◌ׄ◌̕b; a◌֮◌̀◌ׄ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, HEBREW MARK UPPER DOT, LATIN SMALL LETTER B
+0061 05C4 0315 0300 05AE 0062;0061 05AE 05C4 0300 0315 0062;0061 05AE 05C4 0300 0315 0062;0061 05AE 05C4 0300 0315 0062;0061 05AE 05C4 0300 0315 0062; # (a◌ׄ◌̕◌̀◌֮b; a◌֮◌ׄ◌̀◌̕b; a◌֮◌ׄ◌̀◌̕b; a◌֮◌ׄ◌̀◌̕b; a◌֮◌ׄ◌̀◌̕b; ) LATIN SMALL LETTER A, HEBREW MARK UPPER DOT, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0610 0062;00E0 05AE 0610 0315 0062;0061 05AE 0300 0610 0315 0062;00E0 05AE 0610 0315 0062;0061 05AE 0300 0610 0315 0062; # (a◌̕◌̀◌֮◌Øb; à◌֮◌Øâ—ŒÌ•b; a◌֮◌̀◌Øâ—ŒÌ•b; à◌֮◌Øâ—ŒÌ•b; a◌֮◌̀◌Øâ—ŒÌ•b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, ARABIC SIGN SALLALLAHOU ALAYHE WASSALLAM, LATIN SMALL LETTER B
+0061 0610 0315 0300 05AE 0062;0061 05AE 0610 0300 0315 0062;0061 05AE 0610 0300 0315 0062;0061 05AE 0610 0300 0315 0062;0061 05AE 0610 0300 0315 0062; # (aâ—ŒØâ—ŒÌ•◌̀◌֮b; a◌֮◌Øâ—ŒÌ€â—ŒÌ•b; a◌֮◌Øâ—ŒÌ€â—ŒÌ•b; a◌֮◌Øâ—ŒÌ€â—ŒÌ•b; a◌֮◌Øâ—ŒÌ€â—ŒÌ•b; ) LATIN SMALL LETTER A, ARABIC SIGN SALLALLAHOU ALAYHE WASSALLAM, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0611 0062;00E0 05AE 0611 0315 0062;0061 05AE 0300 0611 0315 0062;00E0 05AE 0611 0315 0062;0061 05AE 0300 0611 0315 0062; # (a◌̕◌̀◌֮◌ؑb; à◌֮◌ؑ◌̕b; a◌֮◌̀◌ؑ◌̕b; à◌֮◌ؑ◌̕b; a◌֮◌̀◌ؑ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, ARABIC SIGN ALAYHE ASSALLAM, LATIN SMALL LETTER B
+0061 0611 0315 0300 05AE 0062;0061 05AE 0611 0300 0315 0062;0061 05AE 0611 0300 0315 0062;0061 05AE 0611 0300 0315 0062;0061 05AE 0611 0300 0315 0062; # (a◌ؑ◌̕◌̀◌֮b; a◌֮◌ؑ◌̀◌̕b; a◌֮◌ؑ◌̀◌̕b; a◌֮◌ؑ◌̀◌̕b; a◌֮◌ؑ◌̀◌̕b; ) LATIN SMALL LETTER A, ARABIC SIGN ALAYHE ASSALLAM, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0612 0062;00E0 05AE 0612 0315 0062;0061 05AE 0300 0612 0315 0062;00E0 05AE 0612 0315 0062;0061 05AE 0300 0612 0315 0062; # (a◌̕◌̀◌֮◌ؒb; à◌֮◌ؒ◌̕b; a◌֮◌̀◌ؒ◌̕b; à◌֮◌ؒ◌̕b; a◌֮◌̀◌ؒ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, ARABIC SIGN RAHMATULLAH ALAYHE, LATIN SMALL LETTER B
+0061 0612 0315 0300 05AE 0062;0061 05AE 0612 0300 0315 0062;0061 05AE 0612 0300 0315 0062;0061 05AE 0612 0300 0315 0062;0061 05AE 0612 0300 0315 0062; # (a◌ؒ◌̕◌̀◌֮b; a◌֮◌ؒ◌̀◌̕b; a◌֮◌ؒ◌̀◌̕b; a◌֮◌ؒ◌̀◌̕b; a◌֮◌ؒ◌̀◌̕b; ) LATIN SMALL LETTER A, ARABIC SIGN RAHMATULLAH ALAYHE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0613 0062;00E0 05AE 0613 0315 0062;0061 05AE 0300 0613 0315 0062;00E0 05AE 0613 0315 0062;0061 05AE 0300 0613 0315 0062; # (a◌̕◌̀◌֮◌ؓb; à◌֮◌ؓ◌̕b; a◌֮◌̀◌ؓ◌̕b; à◌֮◌ؓ◌̕b; a◌֮◌̀◌ؓ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, ARABIC SIGN RADI ALLAHOU ANHU, LATIN SMALL LETTER B
+0061 0613 0315 0300 05AE 0062;0061 05AE 0613 0300 0315 0062;0061 05AE 0613 0300 0315 0062;0061 05AE 0613 0300 0315 0062;0061 05AE 0613 0300 0315 0062; # (a◌ؓ◌̕◌̀◌֮b; a◌֮◌ؓ◌̀◌̕b; a◌֮◌ؓ◌̀◌̕b; a◌֮◌ؓ◌̀◌̕b; a◌֮◌ؓ◌̀◌̕b; ) LATIN SMALL LETTER A, ARABIC SIGN RADI ALLAHOU ANHU, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0614 0062;00E0 05AE 0614 0315 0062;0061 05AE 0300 0614 0315 0062;00E0 05AE 0614 0315 0062;0061 05AE 0300 0614 0315 0062; # (a◌̕◌̀◌֮◌ؔb; à◌֮◌ؔ◌̕b; a◌֮◌̀◌ؔ◌̕b; à◌֮◌ؔ◌̕b; a◌֮◌̀◌ؔ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, ARABIC SIGN TAKHALLUS, LATIN SMALL LETTER B
+0061 0614 0315 0300 05AE 0062;0061 05AE 0614 0300 0315 0062;0061 05AE 0614 0300 0315 0062;0061 05AE 0614 0300 0315 0062;0061 05AE 0614 0300 0315 0062; # (a◌ؔ◌̕◌̀◌֮b; a◌֮◌ؔ◌̀◌̕b; a◌֮◌ؔ◌̀◌̕b; a◌֮◌ؔ◌̀◌̕b; a◌֮◌ؔ◌̀◌̕b; ) LATIN SMALL LETTER A, ARABIC SIGN TAKHALLUS, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0615 0062;00E0 05AE 0615 0315 0062;0061 05AE 0300 0615 0315 0062;00E0 05AE 0615 0315 0062;0061 05AE 0300 0615 0315 0062; # (a◌̕◌̀◌֮◌ؕb; à◌֮◌ؕ◌̕b; a◌֮◌̀◌ؕ◌̕b; à◌֮◌ؕ◌̕b; a◌֮◌̀◌ؕ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, ARABIC SMALL HIGH TAH, LATIN SMALL LETTER B
+0061 0615 0315 0300 05AE 0062;0061 05AE 0615 0300 0315 0062;0061 05AE 0615 0300 0315 0062;0061 05AE 0615 0300 0315 0062;0061 05AE 0615 0300 0315 0062; # (a◌ؕ◌̕◌̀◌֮b; a◌֮◌ؕ◌̀◌̕b; a◌֮◌ؕ◌̀◌̕b; a◌֮◌ؕ◌̀◌̕b; a◌֮◌ؕ◌̀◌̕b; ) LATIN SMALL LETTER A, ARABIC SMALL HIGH TAH, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 064C 064B FB1E 064B 0062;0061 FB1E 064B 064B 064C 0062;0061 FB1E 064B 064B 064C 0062;0061 FB1E 064B 064B 064C 0062;0061 FB1E 064B 064B 064C 0062; # (a◌ٌ◌ً◌ﬞ◌ًb; a◌ﬞ◌ً◌ً◌ٌb; a◌ﬞ◌ً◌ً◌ٌb; a◌ﬞ◌ً◌ً◌ٌb; a◌ﬞ◌ً◌ً◌ٌb; ) LATIN SMALL LETTER A, ARABIC DAMMATAN, ARABIC FATHATAN, HEBREW POINT JUDEO-SPANISH VARIKA, ARABIC FATHATAN, LATIN SMALL LETTER B
+0061 064B 064C 064B FB1E 0062;0061 FB1E 064B 064B 064C 0062;0061 FB1E 064B 064B 064C 0062;0061 FB1E 064B 064B 064C 0062;0061 FB1E 064B 064B 064C 0062; # (a◌ً◌ٌ◌ً◌ﬞb; a◌ﬞ◌ً◌ً◌ٌb; a◌ﬞ◌ً◌ً◌ٌb; a◌ﬞ◌ً◌ً◌ٌb; a◌ﬞ◌ً◌ً◌ٌb; ) LATIN SMALL LETTER A, ARABIC FATHATAN, ARABIC DAMMATAN, ARABIC FATHATAN, HEBREW POINT JUDEO-SPANISH VARIKA, LATIN SMALL LETTER B
+0061 064D 064C 064B 064C 0062;0061 064B 064C 064C 064D 0062;0061 064B 064C 064C 064D 0062;0061 064B 064C 064C 064D 0062;0061 064B 064C 064C 064D 0062; # (aâ—ŒÙ◌ٌ◌ً◌ٌb; a◌ً◌ٌ◌ٌ◌Ùb; a◌ً◌ٌ◌ٌ◌Ùb; a◌ً◌ٌ◌ٌ◌Ùb; a◌ً◌ٌ◌ٌ◌Ùb; ) LATIN SMALL LETTER A, ARABIC KASRATAN, ARABIC DAMMATAN, ARABIC FATHATAN, ARABIC DAMMATAN, LATIN SMALL LETTER B
+0061 064C 064D 064C 064B 0062;0061 064B 064C 064C 064D 0062;0061 064B 064C 064C 064D 0062;0061 064B 064C 064C 064D 0062;0061 064B 064C 064C 064D 0062; # (a◌ٌ◌Ù◌ٌ◌ًb; a◌ً◌ٌ◌ٌ◌Ùb; a◌ً◌ٌ◌ٌ◌Ùb; a◌ً◌ٌ◌ٌ◌Ùb; a◌ً◌ٌ◌ٌ◌Ùb; ) LATIN SMALL LETTER A, ARABIC DAMMATAN, ARABIC KASRATAN, ARABIC DAMMATAN, ARABIC FATHATAN, LATIN SMALL LETTER B
+0061 064E 064D 064C 064D 0062;0061 064C 064D 064D 064E 0062;0061 064C 064D 064D 064E 0062;0061 064C 064D 064D 064E 0062;0061 064C 064D 064D 064E 0062; # (a◌َ◌Ù◌ٌ◌Ùb; a◌ٌ◌Ùâ—ŒÙ◌َb; a◌ٌ◌Ùâ—ŒÙ◌َb; a◌ٌ◌Ùâ—ŒÙ◌َb; a◌ٌ◌Ùâ—ŒÙ◌َb; ) LATIN SMALL LETTER A, ARABIC FATHA, ARABIC KASRATAN, ARABIC DAMMATAN, ARABIC KASRATAN, LATIN SMALL LETTER B
+0061 064D 064E 064D 064C 0062;0061 064C 064D 064D 064E 0062;0061 064C 064D 064D 064E 0062;0061 064C 064D 064D 064E 0062;0061 064C 064D 064D 064E 0062; # (aâ—ŒÙ◌َ◌Ù◌ٌb; a◌ٌ◌Ùâ—ŒÙ◌َb; a◌ٌ◌Ùâ—ŒÙ◌َb; a◌ٌ◌Ùâ—ŒÙ◌َb; a◌ٌ◌Ùâ—ŒÙ◌َb; ) LATIN SMALL LETTER A, ARABIC KASRATAN, ARABIC FATHA, ARABIC KASRATAN, ARABIC DAMMATAN, LATIN SMALL LETTER B
+0061 064F 064E 064D 064E 0062;0061 064D 064E 064E 064F 0062;0061 064D 064E 064E 064F 0062;0061 064D 064E 064E 064F 0062;0061 064D 064E 064E 064F 0062; # (aâ—ŒÙ◌َ◌Ù◌َb; aâ—ŒÙ◌َ◌َ◌Ùb; aâ—ŒÙ◌َ◌َ◌Ùb; aâ—ŒÙ◌َ◌َ◌Ùb; aâ—ŒÙ◌َ◌َ◌Ùb; ) LATIN SMALL LETTER A, ARABIC DAMMA, ARABIC FATHA, ARABIC KASRATAN, ARABIC FATHA, LATIN SMALL LETTER B
+0061 064E 064F 064E 064D 0062;0061 064D 064E 064E 064F 0062;0061 064D 064E 064E 064F 0062;0061 064D 064E 064E 064F 0062;0061 064D 064E 064E 064F 0062; # (a◌َ◌Ù◌َ◌Ùb; aâ—ŒÙ◌َ◌َ◌Ùb; aâ—ŒÙ◌َ◌َ◌Ùb; aâ—ŒÙ◌َ◌َ◌Ùb; aâ—ŒÙ◌َ◌َ◌Ùb; ) LATIN SMALL LETTER A, ARABIC FATHA, ARABIC DAMMA, ARABIC FATHA, ARABIC KASRATAN, LATIN SMALL LETTER B
+0061 0650 064F 064E 064F 0062;0061 064E 064F 064F 0650 0062;0061 064E 064F 064F 0650 0062;0061 064E 064F 064F 0650 0062;0061 064E 064F 064F 0650 0062; # (aâ—ŒÙâ—ŒÙ◌َ◌Ùb; a◌َ◌Ùâ—ŒÙâ—ŒÙb; a◌َ◌Ùâ—ŒÙâ—ŒÙb; a◌َ◌Ùâ—ŒÙâ—ŒÙb; a◌َ◌Ùâ—ŒÙâ—ŒÙb; ) LATIN SMALL LETTER A, ARABIC KASRA, ARABIC DAMMA, ARABIC FATHA, ARABIC DAMMA, LATIN SMALL LETTER B
+0061 064F 0650 064F 064E 0062;0061 064E 064F 064F 0650 0062;0061 064E 064F 064F 0650 0062;0061 064E 064F 064F 0650 0062;0061 064E 064F 064F 0650 0062; # (aâ—ŒÙâ—ŒÙâ—ŒÙ◌َb; a◌َ◌Ùâ—ŒÙâ—ŒÙb; a◌َ◌Ùâ—ŒÙâ—ŒÙb; a◌َ◌Ùâ—ŒÙâ—ŒÙb; a◌َ◌Ùâ—ŒÙâ—ŒÙb; ) LATIN SMALL LETTER A, ARABIC DAMMA, ARABIC KASRA, ARABIC DAMMA, ARABIC FATHA, LATIN SMALL LETTER B
+0061 0651 0650 064F 0650 0062;0061 064F 0650 0650 0651 0062;0061 064F 0650 0650 0651 0062;0061 064F 0650 0650 0651 0062;0061 064F 0650 0650 0651 0062; # (a◌ّ◌Ùâ—ŒÙâ—ŒÙb; aâ—ŒÙâ—ŒÙâ—ŒÙ◌ّb; aâ—ŒÙâ—ŒÙâ—ŒÙ◌ّb; aâ—ŒÙâ—ŒÙâ—ŒÙ◌ّb; aâ—ŒÙâ—ŒÙâ—ŒÙ◌ّb; ) LATIN SMALL LETTER A, ARABIC SHADDA, ARABIC KASRA, ARABIC DAMMA, ARABIC KASRA, LATIN SMALL LETTER B
+0061 0650 0651 0650 064F 0062;0061 064F 0650 0650 0651 0062;0061 064F 0650 0650 0651 0062;0061 064F 0650 0650 0651 0062;0061 064F 0650 0650 0651 0062; # (aâ—ŒÙ◌ّ◌Ùâ—ŒÙb; aâ—ŒÙâ—ŒÙâ—ŒÙ◌ّb; aâ—ŒÙâ—ŒÙâ—ŒÙ◌ّb; aâ—ŒÙâ—ŒÙâ—ŒÙ◌ّb; aâ—ŒÙâ—ŒÙâ—ŒÙ◌ّb; ) LATIN SMALL LETTER A, ARABIC KASRA, ARABIC SHADDA, ARABIC KASRA, ARABIC DAMMA, LATIN SMALL LETTER B
+0061 0652 0651 0650 0651 0062;0061 0650 0651 0651 0652 0062;0061 0650 0651 0651 0652 0062;0061 0650 0651 0651 0652 0062;0061 0650 0651 0651 0652 0062; # (a◌ْ◌ّ◌Ù◌ّb; aâ—ŒÙ◌ّ◌ّ◌ْb; aâ—ŒÙ◌ّ◌ّ◌ْb; aâ—ŒÙ◌ّ◌ّ◌ْb; aâ—ŒÙ◌ّ◌ّ◌ْb; ) LATIN SMALL LETTER A, ARABIC SUKUN, ARABIC SHADDA, ARABIC KASRA, ARABIC SHADDA, LATIN SMALL LETTER B
+0061 0651 0652 0651 0650 0062;0061 0650 0651 0651 0652 0062;0061 0650 0651 0651 0652 0062;0061 0650 0651 0651 0652 0062;0061 0650 0651 0651 0652 0062; # (a◌ّ◌ْ◌ّ◌Ùb; aâ—ŒÙ◌ّ◌ّ◌ْb; aâ—ŒÙ◌ّ◌ّ◌ْb; aâ—ŒÙ◌ّ◌ّ◌ْb; aâ—ŒÙ◌ّ◌ّ◌ْb; ) LATIN SMALL LETTER A, ARABIC SHADDA, ARABIC SUKUN, ARABIC SHADDA, ARABIC KASRA, LATIN SMALL LETTER B
+0061 0670 0652 0651 0652 0062;0061 0651 0652 0652 0670 0062;0061 0651 0652 0652 0670 0062;0061 0651 0652 0652 0670 0062;0061 0651 0652 0652 0670 0062; # (a◌ٰ◌ْ◌ّ◌ْb; a◌ّ◌ْ◌ْ◌ٰb; a◌ّ◌ْ◌ْ◌ٰb; a◌ّ◌ْ◌ْ◌ٰb; a◌ّ◌ْ◌ْ◌ٰb; ) LATIN SMALL LETTER A, ARABIC LETTER SUPERSCRIPT ALEF, ARABIC SUKUN, ARABIC SHADDA, ARABIC SUKUN, LATIN SMALL LETTER B
+0061 0652 0670 0652 0651 0062;0061 0651 0652 0652 0670 0062;0061 0651 0652 0652 0670 0062;0061 0651 0652 0652 0670 0062;0061 0651 0652 0652 0670 0062; # (a◌ْ◌ٰ◌ْ◌ّb; a◌ّ◌ْ◌ْ◌ٰb; a◌ّ◌ْ◌ْ◌ٰb; a◌ّ◌ْ◌ْ◌ٰb; a◌ّ◌ْ◌ْ◌ٰb; ) LATIN SMALL LETTER A, ARABIC SUKUN, ARABIC LETTER SUPERSCRIPT ALEF, ARABIC SUKUN, ARABIC SHADDA, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0653 0062;00E0 05AE 0653 0315 0062;0061 05AE 0300 0653 0315 0062;00E0 05AE 0653 0315 0062;0061 05AE 0300 0653 0315 0062; # (a◌̕◌̀◌֮◌ٓb; à◌֮◌ٓ◌̕b; a◌֮◌̀◌ٓ◌̕b; à◌֮◌ٓ◌̕b; a◌֮◌̀◌ٓ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, ARABIC MADDAH ABOVE, LATIN SMALL LETTER B
+0061 0653 0315 0300 05AE 0062;0061 05AE 0653 0300 0315 0062;0061 05AE 0653 0300 0315 0062;0061 05AE 0653 0300 0315 0062;0061 05AE 0653 0300 0315 0062; # (a◌ٓ◌̕◌̀◌֮b; a◌֮◌ٓ◌̀◌̕b; a◌֮◌ٓ◌̀◌̕b; a◌֮◌ٓ◌̀◌̕b; a◌֮◌ٓ◌̀◌̕b; ) LATIN SMALL LETTER A, ARABIC MADDAH ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0654 0062;00E0 05AE 0654 0315 0062;0061 05AE 0300 0654 0315 0062;00E0 05AE 0654 0315 0062;0061 05AE 0300 0654 0315 0062; # (a◌̕◌̀◌֮◌ٔb; à◌֮◌ٔ◌̕b; a◌֮◌̀◌ٔ◌̕b; à◌֮◌ٔ◌̕b; a◌֮◌̀◌ٔ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, ARABIC HAMZA ABOVE, LATIN SMALL LETTER B
+0061 0654 0315 0300 05AE 0062;0061 05AE 0654 0300 0315 0062;0061 05AE 0654 0300 0315 0062;0061 05AE 0654 0300 0315 0062;0061 05AE 0654 0300 0315 0062; # (a◌ٔ◌̕◌̀◌֮b; a◌֮◌ٔ◌̀◌̕b; a◌֮◌ٔ◌̀◌̕b; a◌֮◌ٔ◌̀◌̕b; a◌֮◌ٔ◌̀◌̕b; ) LATIN SMALL LETTER A, ARABIC HAMZA ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 059A 0316 302A 0655 0062;0061 302A 0316 0655 059A 0062;0061 302A 0316 0655 059A 0062;0061 302A 0316 0655 059A 0062;0061 302A 0316 0655 059A 0062; # (a◌֚◌̖◌〪◌ٕb; a◌〪◌̖◌ٕ◌֚b; a◌〪◌̖◌ٕ◌֚b; a◌〪◌̖◌ٕ◌֚b; a◌〪◌̖◌ٕ◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, ARABIC HAMZA BELOW, LATIN SMALL LETTER B
+0061 0655 059A 0316 302A 0062;0061 302A 0655 0316 059A 0062;0061 302A 0655 0316 059A 0062;0061 302A 0655 0316 059A 0062;0061 302A 0655 0316 059A 0062; # (a◌ٕ◌֚◌̖◌〪b; a◌〪◌ٕ◌̖◌֚b; a◌〪◌ٕ◌̖◌֚b; a◌〪◌ٕ◌̖◌֚b; a◌〪◌ٕ◌̖◌֚b; ) LATIN SMALL LETTER A, ARABIC HAMZA BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 059A 0316 302A 0656 0062;0061 302A 0316 0656 059A 0062;0061 302A 0316 0656 059A 0062;0061 302A 0316 0656 059A 0062;0061 302A 0316 0656 059A 0062; # (a◌֚◌̖◌〪◌ٖb; a◌〪◌̖◌ٖ◌֚b; a◌〪◌̖◌ٖ◌֚b; a◌〪◌̖◌ٖ◌֚b; a◌〪◌̖◌ٖ◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, ARABIC SUBSCRIPT ALEF, LATIN SMALL LETTER B
+0061 0656 059A 0316 302A 0062;0061 302A 0656 0316 059A 0062;0061 302A 0656 0316 059A 0062;0061 302A 0656 0316 059A 0062;0061 302A 0656 0316 059A 0062; # (a◌ٖ◌֚◌̖◌〪b; a◌〪◌ٖ◌̖◌֚b; a◌〪◌ٖ◌̖◌֚b; a◌〪◌ٖ◌̖◌֚b; a◌〪◌ٖ◌̖◌֚b; ) LATIN SMALL LETTER A, ARABIC SUBSCRIPT ALEF, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0657 0062;00E0 05AE 0657 0315 0062;0061 05AE 0300 0657 0315 0062;00E0 05AE 0657 0315 0062;0061 05AE 0300 0657 0315 0062; # (a◌̕◌̀◌֮◌ٗb; à◌֮◌ٗ◌̕b; a◌֮◌̀◌ٗ◌̕b; à◌֮◌ٗ◌̕b; a◌֮◌̀◌ٗ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, ARABIC INVERTED DAMMA, LATIN SMALL LETTER B
+0061 0657 0315 0300 05AE 0062;0061 05AE 0657 0300 0315 0062;0061 05AE 0657 0300 0315 0062;0061 05AE 0657 0300 0315 0062;0061 05AE 0657 0300 0315 0062; # (a◌ٗ◌̕◌̀◌֮b; a◌֮◌ٗ◌̀◌̕b; a◌֮◌ٗ◌̀◌̕b; a◌֮◌ٗ◌̀◌̕b; a◌֮◌ٗ◌̀◌̕b; ) LATIN SMALL LETTER A, ARABIC INVERTED DAMMA, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0658 0062;00E0 05AE 0658 0315 0062;0061 05AE 0300 0658 0315 0062;00E0 05AE 0658 0315 0062;0061 05AE 0300 0658 0315 0062; # (a◌̕◌̀◌֮◌٘b; à◌֮◌٘◌̕b; a◌֮◌̀◌٘◌̕b; à◌֮◌٘◌̕b; a◌֮◌̀◌٘◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, ARABIC MARK NOON GHUNNA, LATIN SMALL LETTER B
+0061 0658 0315 0300 05AE 0062;0061 05AE 0658 0300 0315 0062;0061 05AE 0658 0300 0315 0062;0061 05AE 0658 0300 0315 0062;0061 05AE 0658 0300 0315 0062; # (a◌٘◌̕◌̀◌֮b; a◌֮◌٘◌̀◌̕b; a◌֮◌٘◌̀◌̕b; a◌֮◌٘◌̀◌̕b; a◌֮◌٘◌̀◌̕b; ) LATIN SMALL LETTER A, ARABIC MARK NOON GHUNNA, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0711 0670 0652 0670 0062;0061 0652 0670 0670 0711 0062;0061 0652 0670 0670 0711 0062;0061 0652 0670 0670 0711 0062;0061 0652 0670 0670 0711 0062; # (a◌ܑ◌ٰ◌ْ◌ٰb; a◌ْ◌ٰ◌ٰ◌ܑb; a◌ْ◌ٰ◌ٰ◌ܑb; a◌ْ◌ٰ◌ٰ◌ܑb; a◌ْ◌ٰ◌ٰ◌ܑb; ) LATIN SMALL LETTER A, SYRIAC LETTER SUPERSCRIPT ALAPH, ARABIC LETTER SUPERSCRIPT ALEF, ARABIC SUKUN, ARABIC LETTER SUPERSCRIPT ALEF, LATIN SMALL LETTER B
+0061 0670 0711 0670 0652 0062;0061 0652 0670 0670 0711 0062;0061 0652 0670 0670 0711 0062;0061 0652 0670 0670 0711 0062;0061 0652 0670 0670 0711 0062; # (a◌ٰ◌ܑ◌ٰ◌ْb; a◌ْ◌ٰ◌ٰ◌ܑb; a◌ْ◌ٰ◌ٰ◌ܑb; a◌ْ◌ٰ◌ٰ◌ܑb; a◌ْ◌ٰ◌ٰ◌ܑb; ) LATIN SMALL LETTER A, ARABIC LETTER SUPERSCRIPT ALEF, SYRIAC LETTER SUPERSCRIPT ALAPH, ARABIC LETTER SUPERSCRIPT ALEF, ARABIC SUKUN, LATIN SMALL LETTER B
+0061 0315 0300 05AE 06D6 0062;00E0 05AE 06D6 0315 0062;0061 05AE 0300 06D6 0315 0062;00E0 05AE 06D6 0315 0062;0061 05AE 0300 06D6 0315 0062; # (a◌̕◌̀◌֮◌ۖb; à◌֮◌ۖ◌̕b; a◌֮◌̀◌ۖ◌̕b; à◌֮◌ۖ◌̕b; a◌֮◌̀◌ۖ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, ARABIC SMALL HIGH LIGATURE SAD WITH LAM WITH ALEF MAKSURA, LATIN SMALL LETTER B
+0061 06D6 0315 0300 05AE 0062;0061 05AE 06D6 0300 0315 0062;0061 05AE 06D6 0300 0315 0062;0061 05AE 06D6 0300 0315 0062;0061 05AE 06D6 0300 0315 0062; # (a◌ۖ◌̕◌̀◌֮b; a◌֮◌ۖ◌̀◌̕b; a◌֮◌ۖ◌̀◌̕b; a◌֮◌ۖ◌̀◌̕b; a◌֮◌ۖ◌̀◌̕b; ) LATIN SMALL LETTER A, ARABIC SMALL HIGH LIGATURE SAD WITH LAM WITH ALEF MAKSURA, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 06D7 0062;00E0 05AE 06D7 0315 0062;0061 05AE 0300 06D7 0315 0062;00E0 05AE 06D7 0315 0062;0061 05AE 0300 06D7 0315 0062; # (a◌̕◌̀◌֮◌ۗb; à◌֮◌ۗ◌̕b; a◌֮◌̀◌ۗ◌̕b; à◌֮◌ۗ◌̕b; a◌֮◌̀◌ۗ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, ARABIC SMALL HIGH LIGATURE QAF WITH LAM WITH ALEF MAKSURA, LATIN SMALL LETTER B
+0061 06D7 0315 0300 05AE 0062;0061 05AE 06D7 0300 0315 0062;0061 05AE 06D7 0300 0315 0062;0061 05AE 06D7 0300 0315 0062;0061 05AE 06D7 0300 0315 0062; # (a◌ۗ◌̕◌̀◌֮b; a◌֮◌ۗ◌̀◌̕b; a◌֮◌ۗ◌̀◌̕b; a◌֮◌ۗ◌̀◌̕b; a◌֮◌ۗ◌̀◌̕b; ) LATIN SMALL LETTER A, ARABIC SMALL HIGH LIGATURE QAF WITH LAM WITH ALEF MAKSURA, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 06D8 0062;00E0 05AE 06D8 0315 0062;0061 05AE 0300 06D8 0315 0062;00E0 05AE 06D8 0315 0062;0061 05AE 0300 06D8 0315 0062; # (a◌̕◌̀◌֮◌ۘb; à◌֮◌ۘ◌̕b; a◌֮◌̀◌ۘ◌̕b; à◌֮◌ۘ◌̕b; a◌֮◌̀◌ۘ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, ARABIC SMALL HIGH MEEM INITIAL FORM, LATIN SMALL LETTER B
+0061 06D8 0315 0300 05AE 0062;0061 05AE 06D8 0300 0315 0062;0061 05AE 06D8 0300 0315 0062;0061 05AE 06D8 0300 0315 0062;0061 05AE 06D8 0300 0315 0062; # (a◌ۘ◌̕◌̀◌֮b; a◌֮◌ۘ◌̀◌̕b; a◌֮◌ۘ◌̀◌̕b; a◌֮◌ۘ◌̀◌̕b; a◌֮◌ۘ◌̀◌̕b; ) LATIN SMALL LETTER A, ARABIC SMALL HIGH MEEM INITIAL FORM, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 06D9 0062;00E0 05AE 06D9 0315 0062;0061 05AE 0300 06D9 0315 0062;00E0 05AE 06D9 0315 0062;0061 05AE 0300 06D9 0315 0062; # (a◌̕◌̀◌֮◌ۙb; à◌֮◌ۙ◌̕b; a◌֮◌̀◌ۙ◌̕b; à◌֮◌ۙ◌̕b; a◌֮◌̀◌ۙ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, ARABIC SMALL HIGH LAM ALEF, LATIN SMALL LETTER B
+0061 06D9 0315 0300 05AE 0062;0061 05AE 06D9 0300 0315 0062;0061 05AE 06D9 0300 0315 0062;0061 05AE 06D9 0300 0315 0062;0061 05AE 06D9 0300 0315 0062; # (a◌ۙ◌̕◌̀◌֮b; a◌֮◌ۙ◌̀◌̕b; a◌֮◌ۙ◌̀◌̕b; a◌֮◌ۙ◌̀◌̕b; a◌֮◌ۙ◌̀◌̕b; ) LATIN SMALL LETTER A, ARABIC SMALL HIGH LAM ALEF, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 06DA 0062;00E0 05AE 06DA 0315 0062;0061 05AE 0300 06DA 0315 0062;00E0 05AE 06DA 0315 0062;0061 05AE 0300 06DA 0315 0062; # (a◌̕◌̀◌֮◌ۚb; à◌֮◌ۚ◌̕b; a◌֮◌̀◌ۚ◌̕b; à◌֮◌ۚ◌̕b; a◌֮◌̀◌ۚ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, ARABIC SMALL HIGH JEEM, LATIN SMALL LETTER B
+0061 06DA 0315 0300 05AE 0062;0061 05AE 06DA 0300 0315 0062;0061 05AE 06DA 0300 0315 0062;0061 05AE 06DA 0300 0315 0062;0061 05AE 06DA 0300 0315 0062; # (a◌ۚ◌̕◌̀◌֮b; a◌֮◌ۚ◌̀◌̕b; a◌֮◌ۚ◌̀◌̕b; a◌֮◌ۚ◌̀◌̕b; a◌֮◌ۚ◌̀◌̕b; ) LATIN SMALL LETTER A, ARABIC SMALL HIGH JEEM, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 06DB 0062;00E0 05AE 06DB 0315 0062;0061 05AE 0300 06DB 0315 0062;00E0 05AE 06DB 0315 0062;0061 05AE 0300 06DB 0315 0062; # (a◌̕◌̀◌֮◌ۛb; à◌֮◌ۛ◌̕b; a◌֮◌̀◌ۛ◌̕b; à◌֮◌ۛ◌̕b; a◌֮◌̀◌ۛ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, ARABIC SMALL HIGH THREE DOTS, LATIN SMALL LETTER B
+0061 06DB 0315 0300 05AE 0062;0061 05AE 06DB 0300 0315 0062;0061 05AE 06DB 0300 0315 0062;0061 05AE 06DB 0300 0315 0062;0061 05AE 06DB 0300 0315 0062; # (a◌ۛ◌̕◌̀◌֮b; a◌֮◌ۛ◌̀◌̕b; a◌֮◌ۛ◌̀◌̕b; a◌֮◌ۛ◌̀◌̕b; a◌֮◌ۛ◌̀◌̕b; ) LATIN SMALL LETTER A, ARABIC SMALL HIGH THREE DOTS, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 06DC 0062;00E0 05AE 06DC 0315 0062;0061 05AE 0300 06DC 0315 0062;00E0 05AE 06DC 0315 0062;0061 05AE 0300 06DC 0315 0062; # (a◌̕◌̀◌֮◌ۜb; à◌֮◌ۜ◌̕b; a◌֮◌̀◌ۜ◌̕b; à◌֮◌ۜ◌̕b; a◌֮◌̀◌ۜ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, ARABIC SMALL HIGH SEEN, LATIN SMALL LETTER B
+0061 06DC 0315 0300 05AE 0062;0061 05AE 06DC 0300 0315 0062;0061 05AE 06DC 0300 0315 0062;0061 05AE 06DC 0300 0315 0062;0061 05AE 06DC 0300 0315 0062; # (a◌ۜ◌̕◌̀◌֮b; a◌֮◌ۜ◌̀◌̕b; a◌֮◌ۜ◌̀◌̕b; a◌֮◌ۜ◌̀◌̕b; a◌֮◌ۜ◌̀◌̕b; ) LATIN SMALL LETTER A, ARABIC SMALL HIGH SEEN, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 06DF 0062;00E0 05AE 06DF 0315 0062;0061 05AE 0300 06DF 0315 0062;00E0 05AE 06DF 0315 0062;0061 05AE 0300 06DF 0315 0062; # (a◌̕◌̀◌֮◌۟b; à◌֮◌۟◌̕b; a◌֮◌̀◌۟◌̕b; à◌֮◌۟◌̕b; a◌֮◌̀◌۟◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, ARABIC SMALL HIGH ROUNDED ZERO, LATIN SMALL LETTER B
+0061 06DF 0315 0300 05AE 0062;0061 05AE 06DF 0300 0315 0062;0061 05AE 06DF 0300 0315 0062;0061 05AE 06DF 0300 0315 0062;0061 05AE 06DF 0300 0315 0062; # (a◌۟◌̕◌̀◌֮b; a◌֮◌۟◌̀◌̕b; a◌֮◌۟◌̀◌̕b; a◌֮◌۟◌̀◌̕b; a◌֮◌۟◌̀◌̕b; ) LATIN SMALL LETTER A, ARABIC SMALL HIGH ROUNDED ZERO, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 06E0 0062;00E0 05AE 06E0 0315 0062;0061 05AE 0300 06E0 0315 0062;00E0 05AE 06E0 0315 0062;0061 05AE 0300 06E0 0315 0062; # (a◌̕◌̀◌֮◌۠b; à◌֮◌۠◌̕b; a◌֮◌̀◌۠◌̕b; à◌֮◌۠◌̕b; a◌֮◌̀◌۠◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, ARABIC SMALL HIGH UPRIGHT RECTANGULAR ZERO, LATIN SMALL LETTER B
+0061 06E0 0315 0300 05AE 0062;0061 05AE 06E0 0300 0315 0062;0061 05AE 06E0 0300 0315 0062;0061 05AE 06E0 0300 0315 0062;0061 05AE 06E0 0300 0315 0062; # (a◌۠◌̕◌̀◌֮b; a◌֮◌۠◌̀◌̕b; a◌֮◌۠◌̀◌̕b; a◌֮◌۠◌̀◌̕b; a◌֮◌۠◌̀◌̕b; ) LATIN SMALL LETTER A, ARABIC SMALL HIGH UPRIGHT RECTANGULAR ZERO, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 06E1 0062;00E0 05AE 06E1 0315 0062;0061 05AE 0300 06E1 0315 0062;00E0 05AE 06E1 0315 0062;0061 05AE 0300 06E1 0315 0062; # (a◌̕◌̀◌֮◌ۡb; à◌֮◌ۡ◌̕b; a◌֮◌̀◌ۡ◌̕b; à◌֮◌ۡ◌̕b; a◌֮◌̀◌ۡ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, ARABIC SMALL HIGH DOTLESS HEAD OF KHAH, LATIN SMALL LETTER B
+0061 06E1 0315 0300 05AE 0062;0061 05AE 06E1 0300 0315 0062;0061 05AE 06E1 0300 0315 0062;0061 05AE 06E1 0300 0315 0062;0061 05AE 06E1 0300 0315 0062; # (a◌ۡ◌̕◌̀◌֮b; a◌֮◌ۡ◌̀◌̕b; a◌֮◌ۡ◌̀◌̕b; a◌֮◌ۡ◌̀◌̕b; a◌֮◌ۡ◌̀◌̕b; ) LATIN SMALL LETTER A, ARABIC SMALL HIGH DOTLESS HEAD OF KHAH, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 06E2 0062;00E0 05AE 06E2 0315 0062;0061 05AE 0300 06E2 0315 0062;00E0 05AE 06E2 0315 0062;0061 05AE 0300 06E2 0315 0062; # (a◌̕◌̀◌֮◌ۢb; à◌֮◌ۢ◌̕b; a◌֮◌̀◌ۢ◌̕b; à◌֮◌ۢ◌̕b; a◌֮◌̀◌ۢ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, ARABIC SMALL HIGH MEEM ISOLATED FORM, LATIN SMALL LETTER B
+0061 06E2 0315 0300 05AE 0062;0061 05AE 06E2 0300 0315 0062;0061 05AE 06E2 0300 0315 0062;0061 05AE 06E2 0300 0315 0062;0061 05AE 06E2 0300 0315 0062; # (a◌ۢ◌̕◌̀◌֮b; a◌֮◌ۢ◌̀◌̕b; a◌֮◌ۢ◌̀◌̕b; a◌֮◌ۢ◌̀◌̕b; a◌֮◌ۢ◌̀◌̕b; ) LATIN SMALL LETTER A, ARABIC SMALL HIGH MEEM ISOLATED FORM, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 059A 0316 302A 06E3 0062;0061 302A 0316 06E3 059A 0062;0061 302A 0316 06E3 059A 0062;0061 302A 0316 06E3 059A 0062;0061 302A 0316 06E3 059A 0062; # (a◌֚◌̖◌〪◌ۣb; a◌〪◌̖◌ۣ◌֚b; a◌〪◌̖◌ۣ◌֚b; a◌〪◌̖◌ۣ◌֚b; a◌〪◌̖◌ۣ◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, ARABIC SMALL LOW SEEN, LATIN SMALL LETTER B
+0061 06E3 059A 0316 302A 0062;0061 302A 06E3 0316 059A 0062;0061 302A 06E3 0316 059A 0062;0061 302A 06E3 0316 059A 0062;0061 302A 06E3 0316 059A 0062; # (a◌ۣ◌֚◌̖◌〪b; a◌〪◌ۣ◌̖◌֚b; a◌〪◌ۣ◌̖◌֚b; a◌〪◌ۣ◌̖◌֚b; a◌〪◌ۣ◌̖◌֚b; ) LATIN SMALL LETTER A, ARABIC SMALL LOW SEEN, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 0315 0300 05AE 06E4 0062;00E0 05AE 06E4 0315 0062;0061 05AE 0300 06E4 0315 0062;00E0 05AE 06E4 0315 0062;0061 05AE 0300 06E4 0315 0062; # (a◌̕◌̀◌֮◌ۤb; à◌֮◌ۤ◌̕b; a◌֮◌̀◌ۤ◌̕b; à◌֮◌ۤ◌̕b; a◌֮◌̀◌ۤ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, ARABIC SMALL HIGH MADDA, LATIN SMALL LETTER B
+0061 06E4 0315 0300 05AE 0062;0061 05AE 06E4 0300 0315 0062;0061 05AE 06E4 0300 0315 0062;0061 05AE 06E4 0300 0315 0062;0061 05AE 06E4 0300 0315 0062; # (a◌ۤ◌̕◌̀◌֮b; a◌֮◌ۤ◌̀◌̕b; a◌֮◌ۤ◌̀◌̕b; a◌֮◌ۤ◌̀◌̕b; a◌֮◌ۤ◌̀◌̕b; ) LATIN SMALL LETTER A, ARABIC SMALL HIGH MADDA, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 06E7 0062;00E0 05AE 06E7 0315 0062;0061 05AE 0300 06E7 0315 0062;00E0 05AE 06E7 0315 0062;0061 05AE 0300 06E7 0315 0062; # (a◌̕◌̀◌֮◌ۧb; à◌֮◌ۧ◌̕b; a◌֮◌̀◌ۧ◌̕b; à◌֮◌ۧ◌̕b; a◌֮◌̀◌ۧ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, ARABIC SMALL HIGH YEH, LATIN SMALL LETTER B
+0061 06E7 0315 0300 05AE 0062;0061 05AE 06E7 0300 0315 0062;0061 05AE 06E7 0300 0315 0062;0061 05AE 06E7 0300 0315 0062;0061 05AE 06E7 0300 0315 0062; # (a◌ۧ◌̕◌̀◌֮b; a◌֮◌ۧ◌̀◌̕b; a◌֮◌ۧ◌̀◌̕b; a◌֮◌ۧ◌̀◌̕b; a◌֮◌ۧ◌̀◌̕b; ) LATIN SMALL LETTER A, ARABIC SMALL HIGH YEH, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 06E8 0062;00E0 05AE 06E8 0315 0062;0061 05AE 0300 06E8 0315 0062;00E0 05AE 06E8 0315 0062;0061 05AE 0300 06E8 0315 0062; # (a◌̕◌̀◌֮◌ۨb; à◌֮◌ۨ◌̕b; a◌֮◌̀◌ۨ◌̕b; à◌֮◌ۨ◌̕b; a◌֮◌̀◌ۨ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, ARABIC SMALL HIGH NOON, LATIN SMALL LETTER B
+0061 06E8 0315 0300 05AE 0062;0061 05AE 06E8 0300 0315 0062;0061 05AE 06E8 0300 0315 0062;0061 05AE 06E8 0300 0315 0062;0061 05AE 06E8 0300 0315 0062; # (a◌ۨ◌̕◌̀◌֮b; a◌֮◌ۨ◌̀◌̕b; a◌֮◌ۨ◌̀◌̕b; a◌֮◌ۨ◌̀◌̕b; a◌֮◌ۨ◌̀◌̕b; ) LATIN SMALL LETTER A, ARABIC SMALL HIGH NOON, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 059A 0316 302A 06EA 0062;0061 302A 0316 06EA 059A 0062;0061 302A 0316 06EA 059A 0062;0061 302A 0316 06EA 059A 0062;0061 302A 0316 06EA 059A 0062; # (a◌֚◌̖◌〪◌۪b; a◌〪◌̖◌۪◌֚b; a◌〪◌̖◌۪◌֚b; a◌〪◌̖◌۪◌֚b; a◌〪◌̖◌۪◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, ARABIC EMPTY CENTRE LOW STOP, LATIN SMALL LETTER B
+0061 06EA 059A 0316 302A 0062;0061 302A 06EA 0316 059A 0062;0061 302A 06EA 0316 059A 0062;0061 302A 06EA 0316 059A 0062;0061 302A 06EA 0316 059A 0062; # (a◌۪◌֚◌̖◌〪b; a◌〪◌۪◌̖◌֚b; a◌〪◌۪◌̖◌֚b; a◌〪◌۪◌̖◌֚b; a◌〪◌۪◌̖◌֚b; ) LATIN SMALL LETTER A, ARABIC EMPTY CENTRE LOW STOP, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 0315 0300 05AE 06EB 0062;00E0 05AE 06EB 0315 0062;0061 05AE 0300 06EB 0315 0062;00E0 05AE 06EB 0315 0062;0061 05AE 0300 06EB 0315 0062; # (a◌̕◌̀◌֮◌۫b; à◌֮◌۫◌̕b; a◌֮◌̀◌۫◌̕b; à◌֮◌۫◌̕b; a◌֮◌̀◌۫◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, ARABIC EMPTY CENTRE HIGH STOP, LATIN SMALL LETTER B
+0061 06EB 0315 0300 05AE 0062;0061 05AE 06EB 0300 0315 0062;0061 05AE 06EB 0300 0315 0062;0061 05AE 06EB 0300 0315 0062;0061 05AE 06EB 0300 0315 0062; # (a◌۫◌̕◌̀◌֮b; a◌֮◌۫◌̀◌̕b; a◌֮◌۫◌̀◌̕b; a◌֮◌۫◌̀◌̕b; a◌֮◌۫◌̀◌̕b; ) LATIN SMALL LETTER A, ARABIC EMPTY CENTRE HIGH STOP, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 06EC 0062;00E0 05AE 06EC 0315 0062;0061 05AE 0300 06EC 0315 0062;00E0 05AE 06EC 0315 0062;0061 05AE 0300 06EC 0315 0062; # (a◌̕◌̀◌֮◌۬b; à◌֮◌۬◌̕b; a◌֮◌̀◌۬◌̕b; à◌֮◌۬◌̕b; a◌֮◌̀◌۬◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, ARABIC ROUNDED HIGH STOP WITH FILLED CENTRE, LATIN SMALL LETTER B
+0061 06EC 0315 0300 05AE 0062;0061 05AE 06EC 0300 0315 0062;0061 05AE 06EC 0300 0315 0062;0061 05AE 06EC 0300 0315 0062;0061 05AE 06EC 0300 0315 0062; # (a◌۬◌̕◌̀◌֮b; a◌֮◌۬◌̀◌̕b; a◌֮◌۬◌̀◌̕b; a◌֮◌۬◌̀◌̕b; a◌֮◌۬◌̀◌̕b; ) LATIN SMALL LETTER A, ARABIC ROUNDED HIGH STOP WITH FILLED CENTRE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 059A 0316 302A 06ED 0062;0061 302A 0316 06ED 059A 0062;0061 302A 0316 06ED 059A 0062;0061 302A 0316 06ED 059A 0062;0061 302A 0316 06ED 059A 0062; # (a◌֚◌̖◌〪◌ۭb; a◌〪◌̖◌ۭ◌֚b; a◌〪◌̖◌ۭ◌֚b; a◌〪◌̖◌ۭ◌֚b; a◌〪◌̖◌ۭ◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, ARABIC SMALL LOW MEEM, LATIN SMALL LETTER B
+0061 06ED 059A 0316 302A 0062;0061 302A 06ED 0316 059A 0062;0061 302A 06ED 0316 059A 0062;0061 302A 06ED 0316 059A 0062;0061 302A 06ED 0316 059A 0062; # (a◌ۭ◌֚◌̖◌〪b; a◌〪◌ۭ◌̖◌֚b; a◌〪◌ۭ◌̖◌֚b; a◌〪◌ۭ◌̖◌֚b; a◌〪◌ۭ◌̖◌֚b; ) LATIN SMALL LETTER A, ARABIC SMALL LOW MEEM, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 0C55 0711 0670 0711 0062;0061 0670 0711 0711 0C55 0062;0061 0670 0711 0711 0C55 0062;0061 0670 0711 0711 0C55 0062;0061 0670 0711 0711 0C55 0062; # (a◌ౕ◌ܑ◌ٰ◌ܑb; a◌ٰ◌ܑ◌ܑ◌ౕb; a◌ٰ◌ܑ◌ܑ◌ౕb; a◌ٰ◌ܑ◌ܑ◌ౕb; a◌ٰ◌ܑ◌ܑ◌ౕb; ) LATIN SMALL LETTER A, TELUGU LENGTH MARK, SYRIAC LETTER SUPERSCRIPT ALAPH, ARABIC LETTER SUPERSCRIPT ALEF, SYRIAC LETTER SUPERSCRIPT ALAPH, LATIN SMALL LETTER B
+0061 0711 0C55 0711 0670 0062;0061 0670 0711 0711 0C55 0062;0061 0670 0711 0711 0C55 0062;0061 0670 0711 0711 0C55 0062;0061 0670 0711 0711 0C55 0062; # (a◌ܑ◌ౕ◌ܑ◌ٰb; a◌ٰ◌ܑ◌ܑ◌ౕb; a◌ٰ◌ܑ◌ܑ◌ౕb; a◌ٰ◌ܑ◌ܑ◌ౕb; a◌ٰ◌ܑ◌ܑ◌ౕb; ) LATIN SMALL LETTER A, SYRIAC LETTER SUPERSCRIPT ALAPH, TELUGU LENGTH MARK, SYRIAC LETTER SUPERSCRIPT ALAPH, ARABIC LETTER SUPERSCRIPT ALEF, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0730 0062;00E0 05AE 0730 0315 0062;0061 05AE 0300 0730 0315 0062;00E0 05AE 0730 0315 0062;0061 05AE 0300 0730 0315 0062; # (a◌̕◌̀◌֮◌ܰb; à◌֮◌ܰ◌̕b; a◌֮◌̀◌ܰ◌̕b; à◌֮◌ܰ◌̕b; a◌֮◌̀◌ܰ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, SYRIAC PTHAHA ABOVE, LATIN SMALL LETTER B
+0061 0730 0315 0300 05AE 0062;0061 05AE 0730 0300 0315 0062;0061 05AE 0730 0300 0315 0062;0061 05AE 0730 0300 0315 0062;0061 05AE 0730 0300 0315 0062; # (a◌ܰ◌̕◌̀◌֮b; a◌֮◌ܰ◌̀◌̕b; a◌֮◌ܰ◌̀◌̕b; a◌֮◌ܰ◌̀◌̕b; a◌֮◌ܰ◌̀◌̕b; ) LATIN SMALL LETTER A, SYRIAC PTHAHA ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 059A 0316 302A 0731 0062;0061 302A 0316 0731 059A 0062;0061 302A 0316 0731 059A 0062;0061 302A 0316 0731 059A 0062;0061 302A 0316 0731 059A 0062; # (a◌֚◌̖◌〪◌ܱb; a◌〪◌̖◌ܱ◌֚b; a◌〪◌̖◌ܱ◌֚b; a◌〪◌̖◌ܱ◌֚b; a◌〪◌̖◌ܱ◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, SYRIAC PTHAHA BELOW, LATIN SMALL LETTER B
+0061 0731 059A 0316 302A 0062;0061 302A 0731 0316 059A 0062;0061 302A 0731 0316 059A 0062;0061 302A 0731 0316 059A 0062;0061 302A 0731 0316 059A 0062; # (a◌ܱ◌֚◌̖◌〪b; a◌〪◌ܱ◌̖◌֚b; a◌〪◌ܱ◌̖◌֚b; a◌〪◌ܱ◌̖◌֚b; a◌〪◌ܱ◌̖◌֚b; ) LATIN SMALL LETTER A, SYRIAC PTHAHA BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0732 0062;00E0 05AE 0732 0315 0062;0061 05AE 0300 0732 0315 0062;00E0 05AE 0732 0315 0062;0061 05AE 0300 0732 0315 0062; # (a◌̕◌̀◌֮◌ܲb; à◌֮◌ܲ◌̕b; a◌֮◌̀◌ܲ◌̕b; à◌֮◌ܲ◌̕b; a◌֮◌̀◌ܲ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, SYRIAC PTHAHA DOTTED, LATIN SMALL LETTER B
+0061 0732 0315 0300 05AE 0062;0061 05AE 0732 0300 0315 0062;0061 05AE 0732 0300 0315 0062;0061 05AE 0732 0300 0315 0062;0061 05AE 0732 0300 0315 0062; # (a◌ܲ◌̕◌̀◌֮b; a◌֮◌ܲ◌̀◌̕b; a◌֮◌ܲ◌̀◌̕b; a◌֮◌ܲ◌̀◌̕b; a◌֮◌ܲ◌̀◌̕b; ) LATIN SMALL LETTER A, SYRIAC PTHAHA DOTTED, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0733 0062;00E0 05AE 0733 0315 0062;0061 05AE 0300 0733 0315 0062;00E0 05AE 0733 0315 0062;0061 05AE 0300 0733 0315 0062; # (a◌̕◌̀◌֮◌ܳb; à◌֮◌ܳ◌̕b; a◌֮◌̀◌ܳ◌̕b; à◌֮◌ܳ◌̕b; a◌֮◌̀◌ܳ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, SYRIAC ZQAPHA ABOVE, LATIN SMALL LETTER B
+0061 0733 0315 0300 05AE 0062;0061 05AE 0733 0300 0315 0062;0061 05AE 0733 0300 0315 0062;0061 05AE 0733 0300 0315 0062;0061 05AE 0733 0300 0315 0062; # (a◌ܳ◌̕◌̀◌֮b; a◌֮◌ܳ◌̀◌̕b; a◌֮◌ܳ◌̀◌̕b; a◌֮◌ܳ◌̀◌̕b; a◌֮◌ܳ◌̀◌̕b; ) LATIN SMALL LETTER A, SYRIAC ZQAPHA ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 059A 0316 302A 0734 0062;0061 302A 0316 0734 059A 0062;0061 302A 0316 0734 059A 0062;0061 302A 0316 0734 059A 0062;0061 302A 0316 0734 059A 0062; # (a◌֚◌̖◌〪◌ܴb; a◌〪◌̖◌ܴ◌֚b; a◌〪◌̖◌ܴ◌֚b; a◌〪◌̖◌ܴ◌֚b; a◌〪◌̖◌ܴ◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, SYRIAC ZQAPHA BELOW, LATIN SMALL LETTER B
+0061 0734 059A 0316 302A 0062;0061 302A 0734 0316 059A 0062;0061 302A 0734 0316 059A 0062;0061 302A 0734 0316 059A 0062;0061 302A 0734 0316 059A 0062; # (a◌ܴ◌֚◌̖◌〪b; a◌〪◌ܴ◌̖◌֚b; a◌〪◌ܴ◌̖◌֚b; a◌〪◌ܴ◌̖◌֚b; a◌〪◌ܴ◌̖◌֚b; ) LATIN SMALL LETTER A, SYRIAC ZQAPHA BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0735 0062;00E0 05AE 0735 0315 0062;0061 05AE 0300 0735 0315 0062;00E0 05AE 0735 0315 0062;0061 05AE 0300 0735 0315 0062; # (a◌̕◌̀◌֮◌ܵb; à◌֮◌ܵ◌̕b; a◌֮◌̀◌ܵ◌̕b; à◌֮◌ܵ◌̕b; a◌֮◌̀◌ܵ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, SYRIAC ZQAPHA DOTTED, LATIN SMALL LETTER B
+0061 0735 0315 0300 05AE 0062;0061 05AE 0735 0300 0315 0062;0061 05AE 0735 0300 0315 0062;0061 05AE 0735 0300 0315 0062;0061 05AE 0735 0300 0315 0062; # (a◌ܵ◌̕◌̀◌֮b; a◌֮◌ܵ◌̀◌̕b; a◌֮◌ܵ◌̀◌̕b; a◌֮◌ܵ◌̀◌̕b; a◌֮◌ܵ◌̀◌̕b; ) LATIN SMALL LETTER A, SYRIAC ZQAPHA DOTTED, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0736 0062;00E0 05AE 0736 0315 0062;0061 05AE 0300 0736 0315 0062;00E0 05AE 0736 0315 0062;0061 05AE 0300 0736 0315 0062; # (a◌̕◌̀◌֮◌ܶb; à◌֮◌ܶ◌̕b; a◌֮◌̀◌ܶ◌̕b; à◌֮◌ܶ◌̕b; a◌֮◌̀◌ܶ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, SYRIAC RBASA ABOVE, LATIN SMALL LETTER B
+0061 0736 0315 0300 05AE 0062;0061 05AE 0736 0300 0315 0062;0061 05AE 0736 0300 0315 0062;0061 05AE 0736 0300 0315 0062;0061 05AE 0736 0300 0315 0062; # (a◌ܶ◌̕◌̀◌֮b; a◌֮◌ܶ◌̀◌̕b; a◌֮◌ܶ◌̀◌̕b; a◌֮◌ܶ◌̀◌̕b; a◌֮◌ܶ◌̀◌̕b; ) LATIN SMALL LETTER A, SYRIAC RBASA ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 059A 0316 302A 0737 0062;0061 302A 0316 0737 059A 0062;0061 302A 0316 0737 059A 0062;0061 302A 0316 0737 059A 0062;0061 302A 0316 0737 059A 0062; # (a◌֚◌̖◌〪◌ܷb; a◌〪◌̖◌ܷ◌֚b; a◌〪◌̖◌ܷ◌֚b; a◌〪◌̖◌ܷ◌֚b; a◌〪◌̖◌ܷ◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, SYRIAC RBASA BELOW, LATIN SMALL LETTER B
+0061 0737 059A 0316 302A 0062;0061 302A 0737 0316 059A 0062;0061 302A 0737 0316 059A 0062;0061 302A 0737 0316 059A 0062;0061 302A 0737 0316 059A 0062; # (a◌ܷ◌֚◌̖◌〪b; a◌〪◌ܷ◌̖◌֚b; a◌〪◌ܷ◌̖◌֚b; a◌〪◌ܷ◌̖◌֚b; a◌〪◌ܷ◌̖◌֚b; ) LATIN SMALL LETTER A, SYRIAC RBASA BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 059A 0316 302A 0738 0062;0061 302A 0316 0738 059A 0062;0061 302A 0316 0738 059A 0062;0061 302A 0316 0738 059A 0062;0061 302A 0316 0738 059A 0062; # (a◌֚◌̖◌〪◌ܸb; a◌〪◌̖◌ܸ◌֚b; a◌〪◌̖◌ܸ◌֚b; a◌〪◌̖◌ܸ◌֚b; a◌〪◌̖◌ܸ◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, SYRIAC DOTTED ZLAMA HORIZONTAL, LATIN SMALL LETTER B
+0061 0738 059A 0316 302A 0062;0061 302A 0738 0316 059A 0062;0061 302A 0738 0316 059A 0062;0061 302A 0738 0316 059A 0062;0061 302A 0738 0316 059A 0062; # (a◌ܸ◌֚◌̖◌〪b; a◌〪◌ܸ◌̖◌֚b; a◌〪◌ܸ◌̖◌֚b; a◌〪◌ܸ◌̖◌֚b; a◌〪◌ܸ◌̖◌֚b; ) LATIN SMALL LETTER A, SYRIAC DOTTED ZLAMA HORIZONTAL, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 059A 0316 302A 0739 0062;0061 302A 0316 0739 059A 0062;0061 302A 0316 0739 059A 0062;0061 302A 0316 0739 059A 0062;0061 302A 0316 0739 059A 0062; # (a◌֚◌̖◌〪◌ܹb; a◌〪◌̖◌ܹ◌֚b; a◌〪◌̖◌ܹ◌֚b; a◌〪◌̖◌ܹ◌֚b; a◌〪◌̖◌ܹ◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, SYRIAC DOTTED ZLAMA ANGULAR, LATIN SMALL LETTER B
+0061 0739 059A 0316 302A 0062;0061 302A 0739 0316 059A 0062;0061 302A 0739 0316 059A 0062;0061 302A 0739 0316 059A 0062;0061 302A 0739 0316 059A 0062; # (a◌ܹ◌֚◌̖◌〪b; a◌〪◌ܹ◌̖◌֚b; a◌〪◌ܹ◌̖◌֚b; a◌〪◌ܹ◌̖◌֚b; a◌〪◌ܹ◌̖◌֚b; ) LATIN SMALL LETTER A, SYRIAC DOTTED ZLAMA ANGULAR, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 0315 0300 05AE 073A 0062;00E0 05AE 073A 0315 0062;0061 05AE 0300 073A 0315 0062;00E0 05AE 073A 0315 0062;0061 05AE 0300 073A 0315 0062; # (a◌̕◌̀◌֮◌ܺb; à◌֮◌ܺ◌̕b; a◌֮◌̀◌ܺ◌̕b; à◌֮◌ܺ◌̕b; a◌֮◌̀◌ܺ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, SYRIAC HBASA ABOVE, LATIN SMALL LETTER B
+0061 073A 0315 0300 05AE 0062;0061 05AE 073A 0300 0315 0062;0061 05AE 073A 0300 0315 0062;0061 05AE 073A 0300 0315 0062;0061 05AE 073A 0300 0315 0062; # (a◌ܺ◌̕◌̀◌֮b; a◌֮◌ܺ◌̀◌̕b; a◌֮◌ܺ◌̀◌̕b; a◌֮◌ܺ◌̀◌̕b; a◌֮◌ܺ◌̀◌̕b; ) LATIN SMALL LETTER A, SYRIAC HBASA ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 059A 0316 302A 073B 0062;0061 302A 0316 073B 059A 0062;0061 302A 0316 073B 059A 0062;0061 302A 0316 073B 059A 0062;0061 302A 0316 073B 059A 0062; # (a◌֚◌̖◌〪◌ܻb; a◌〪◌̖◌ܻ◌֚b; a◌〪◌̖◌ܻ◌֚b; a◌〪◌̖◌ܻ◌֚b; a◌〪◌̖◌ܻ◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, SYRIAC HBASA BELOW, LATIN SMALL LETTER B
+0061 073B 059A 0316 302A 0062;0061 302A 073B 0316 059A 0062;0061 302A 073B 0316 059A 0062;0061 302A 073B 0316 059A 0062;0061 302A 073B 0316 059A 0062; # (a◌ܻ◌֚◌̖◌〪b; a◌〪◌ܻ◌̖◌֚b; a◌〪◌ܻ◌̖◌֚b; a◌〪◌ܻ◌̖◌֚b; a◌〪◌ܻ◌̖◌֚b; ) LATIN SMALL LETTER A, SYRIAC HBASA BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 059A 0316 302A 073C 0062;0061 302A 0316 073C 059A 0062;0061 302A 0316 073C 059A 0062;0061 302A 0316 073C 059A 0062;0061 302A 0316 073C 059A 0062; # (a◌֚◌̖◌〪◌ܼb; a◌〪◌̖◌ܼ◌֚b; a◌〪◌̖◌ܼ◌֚b; a◌〪◌̖◌ܼ◌֚b; a◌〪◌̖◌ܼ◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, SYRIAC HBASA-ESASA DOTTED, LATIN SMALL LETTER B
+0061 073C 059A 0316 302A 0062;0061 302A 073C 0316 059A 0062;0061 302A 073C 0316 059A 0062;0061 302A 073C 0316 059A 0062;0061 302A 073C 0316 059A 0062; # (a◌ܼ◌֚◌̖◌〪b; a◌〪◌ܼ◌̖◌֚b; a◌〪◌ܼ◌̖◌֚b; a◌〪◌ܼ◌̖◌֚b; a◌〪◌ܼ◌̖◌֚b; ) LATIN SMALL LETTER A, SYRIAC HBASA-ESASA DOTTED, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 0315 0300 05AE 073D 0062;00E0 05AE 073D 0315 0062;0061 05AE 0300 073D 0315 0062;00E0 05AE 073D 0315 0062;0061 05AE 0300 073D 0315 0062; # (a◌̕◌̀◌֮◌ܽb; à◌֮◌ܽ◌̕b; a◌֮◌̀◌ܽ◌̕b; à◌֮◌ܽ◌̕b; a◌֮◌̀◌ܽ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, SYRIAC ESASA ABOVE, LATIN SMALL LETTER B
+0061 073D 0315 0300 05AE 0062;0061 05AE 073D 0300 0315 0062;0061 05AE 073D 0300 0315 0062;0061 05AE 073D 0300 0315 0062;0061 05AE 073D 0300 0315 0062; # (a◌ܽ◌̕◌̀◌֮b; a◌֮◌ܽ◌̀◌̕b; a◌֮◌ܽ◌̀◌̕b; a◌֮◌ܽ◌̀◌̕b; a◌֮◌ܽ◌̀◌̕b; ) LATIN SMALL LETTER A, SYRIAC ESASA ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 059A 0316 302A 073E 0062;0061 302A 0316 073E 059A 0062;0061 302A 0316 073E 059A 0062;0061 302A 0316 073E 059A 0062;0061 302A 0316 073E 059A 0062; # (a◌֚◌̖◌〪◌ܾb; a◌〪◌̖◌ܾ◌֚b; a◌〪◌̖◌ܾ◌֚b; a◌〪◌̖◌ܾ◌֚b; a◌〪◌̖◌ܾ◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, SYRIAC ESASA BELOW, LATIN SMALL LETTER B
+0061 073E 059A 0316 302A 0062;0061 302A 073E 0316 059A 0062;0061 302A 073E 0316 059A 0062;0061 302A 073E 0316 059A 0062;0061 302A 073E 0316 059A 0062; # (a◌ܾ◌֚◌̖◌〪b; a◌〪◌ܾ◌̖◌֚b; a◌〪◌ܾ◌̖◌֚b; a◌〪◌ܾ◌̖◌֚b; a◌〪◌ܾ◌̖◌֚b; ) LATIN SMALL LETTER A, SYRIAC ESASA BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 0315 0300 05AE 073F 0062;00E0 05AE 073F 0315 0062;0061 05AE 0300 073F 0315 0062;00E0 05AE 073F 0315 0062;0061 05AE 0300 073F 0315 0062; # (a◌̕◌̀◌֮◌ܿb; à◌֮◌ܿ◌̕b; a◌֮◌̀◌ܿ◌̕b; à◌֮◌ܿ◌̕b; a◌֮◌̀◌ܿ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, SYRIAC RWAHA, LATIN SMALL LETTER B
+0061 073F 0315 0300 05AE 0062;0061 05AE 073F 0300 0315 0062;0061 05AE 073F 0300 0315 0062;0061 05AE 073F 0300 0315 0062;0061 05AE 073F 0300 0315 0062; # (a◌ܿ◌̕◌̀◌֮b; a◌֮◌ܿ◌̀◌̕b; a◌֮◌ܿ◌̀◌̕b; a◌֮◌ܿ◌̀◌̕b; a◌֮◌ܿ◌̀◌̕b; ) LATIN SMALL LETTER A, SYRIAC RWAHA, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0740 0062;00E0 05AE 0740 0315 0062;0061 05AE 0300 0740 0315 0062;00E0 05AE 0740 0315 0062;0061 05AE 0300 0740 0315 0062; # (a◌̕◌̀◌֮◌݀b; à◌֮◌݀◌̕b; a◌֮◌̀◌݀◌̕b; à◌֮◌݀◌̕b; a◌֮◌̀◌݀◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, SYRIAC FEMININE DOT, LATIN SMALL LETTER B
+0061 0740 0315 0300 05AE 0062;0061 05AE 0740 0300 0315 0062;0061 05AE 0740 0300 0315 0062;0061 05AE 0740 0300 0315 0062;0061 05AE 0740 0300 0315 0062; # (a◌݀◌̕◌̀◌֮b; a◌֮◌݀◌̀◌̕b; a◌֮◌݀◌̀◌̕b; a◌֮◌݀◌̀◌̕b; a◌֮◌݀◌̀◌̕b; ) LATIN SMALL LETTER A, SYRIAC FEMININE DOT, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0741 0062;00E0 05AE 0741 0315 0062;0061 05AE 0300 0741 0315 0062;00E0 05AE 0741 0315 0062;0061 05AE 0300 0741 0315 0062; # (a◌̕◌̀◌֮◌Ýb; à◌֮◌Ý◌̕b; a◌֮◌̀◌Ý◌̕b; à◌֮◌Ý◌̕b; a◌֮◌̀◌Ý◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, SYRIAC QUSHSHAYA, LATIN SMALL LETTER B
+0061 0741 0315 0300 05AE 0062;0061 05AE 0741 0300 0315 0062;0061 05AE 0741 0300 0315 0062;0061 05AE 0741 0300 0315 0062;0061 05AE 0741 0300 0315 0062; # (aâ—ŒÝ◌̕◌̀◌֮b; a◌֮◌Ý◌̀◌̕b; a◌֮◌Ý◌̀◌̕b; a◌֮◌Ý◌̀◌̕b; a◌֮◌Ý◌̀◌̕b; ) LATIN SMALL LETTER A, SYRIAC QUSHSHAYA, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 059A 0316 302A 0742 0062;0061 302A 0316 0742 059A 0062;0061 302A 0316 0742 059A 0062;0061 302A 0316 0742 059A 0062;0061 302A 0316 0742 059A 0062; # (a◌֚◌̖◌〪◌݂b; a◌〪◌̖◌݂◌֚b; a◌〪◌̖◌݂◌֚b; a◌〪◌̖◌݂◌֚b; a◌〪◌̖◌݂◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, SYRIAC RUKKAKHA, LATIN SMALL LETTER B
+0061 0742 059A 0316 302A 0062;0061 302A 0742 0316 059A 0062;0061 302A 0742 0316 059A 0062;0061 302A 0742 0316 059A 0062;0061 302A 0742 0316 059A 0062; # (a◌݂◌֚◌̖◌〪b; a◌〪◌݂◌̖◌֚b; a◌〪◌݂◌̖◌֚b; a◌〪◌݂◌̖◌֚b; a◌〪◌݂◌̖◌֚b; ) LATIN SMALL LETTER A, SYRIAC RUKKAKHA, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0743 0062;00E0 05AE 0743 0315 0062;0061 05AE 0300 0743 0315 0062;00E0 05AE 0743 0315 0062;0061 05AE 0300 0743 0315 0062; # (a◌̕◌̀◌֮◌݃b; à◌֮◌݃◌̕b; a◌֮◌̀◌݃◌̕b; à◌֮◌݃◌̕b; a◌֮◌̀◌݃◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, SYRIAC TWO VERTICAL DOTS ABOVE, LATIN SMALL LETTER B
+0061 0743 0315 0300 05AE 0062;0061 05AE 0743 0300 0315 0062;0061 05AE 0743 0300 0315 0062;0061 05AE 0743 0300 0315 0062;0061 05AE 0743 0300 0315 0062; # (a◌݃◌̕◌̀◌֮b; a◌֮◌݃◌̀◌̕b; a◌֮◌݃◌̀◌̕b; a◌֮◌݃◌̀◌̕b; a◌֮◌݃◌̀◌̕b; ) LATIN SMALL LETTER A, SYRIAC TWO VERTICAL DOTS ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 059A 0316 302A 0744 0062;0061 302A 0316 0744 059A 0062;0061 302A 0316 0744 059A 0062;0061 302A 0316 0744 059A 0062;0061 302A 0316 0744 059A 0062; # (a◌֚◌̖◌〪◌݄b; a◌〪◌̖◌݄◌֚b; a◌〪◌̖◌݄◌֚b; a◌〪◌̖◌݄◌֚b; a◌〪◌̖◌݄◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, SYRIAC TWO VERTICAL DOTS BELOW, LATIN SMALL LETTER B
+0061 0744 059A 0316 302A 0062;0061 302A 0744 0316 059A 0062;0061 302A 0744 0316 059A 0062;0061 302A 0744 0316 059A 0062;0061 302A 0744 0316 059A 0062; # (a◌݄◌֚◌̖◌〪b; a◌〪◌݄◌̖◌֚b; a◌〪◌݄◌̖◌֚b; a◌〪◌݄◌̖◌֚b; a◌〪◌݄◌̖◌֚b; ) LATIN SMALL LETTER A, SYRIAC TWO VERTICAL DOTS BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0745 0062;00E0 05AE 0745 0315 0062;0061 05AE 0300 0745 0315 0062;00E0 05AE 0745 0315 0062;0061 05AE 0300 0745 0315 0062; # (a◌̕◌̀◌֮◌݅b; à◌֮◌݅◌̕b; a◌֮◌̀◌݅◌̕b; à◌֮◌݅◌̕b; a◌֮◌̀◌݅◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, SYRIAC THREE DOTS ABOVE, LATIN SMALL LETTER B
+0061 0745 0315 0300 05AE 0062;0061 05AE 0745 0300 0315 0062;0061 05AE 0745 0300 0315 0062;0061 05AE 0745 0300 0315 0062;0061 05AE 0745 0300 0315 0062; # (a◌݅◌̕◌̀◌֮b; a◌֮◌݅◌̀◌̕b; a◌֮◌݅◌̀◌̕b; a◌֮◌݅◌̀◌̕b; a◌֮◌݅◌̀◌̕b; ) LATIN SMALL LETTER A, SYRIAC THREE DOTS ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 059A 0316 302A 0746 0062;0061 302A 0316 0746 059A 0062;0061 302A 0316 0746 059A 0062;0061 302A 0316 0746 059A 0062;0061 302A 0316 0746 059A 0062; # (a◌֚◌̖◌〪◌݆b; a◌〪◌̖◌݆◌֚b; a◌〪◌̖◌݆◌֚b; a◌〪◌̖◌݆◌֚b; a◌〪◌̖◌݆◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, SYRIAC THREE DOTS BELOW, LATIN SMALL LETTER B
+0061 0746 059A 0316 302A 0062;0061 302A 0746 0316 059A 0062;0061 302A 0746 0316 059A 0062;0061 302A 0746 0316 059A 0062;0061 302A 0746 0316 059A 0062; # (a◌݆◌֚◌̖◌〪b; a◌〪◌݆◌̖◌֚b; a◌〪◌݆◌̖◌֚b; a◌〪◌݆◌̖◌֚b; a◌〪◌݆◌̖◌֚b; ) LATIN SMALL LETTER A, SYRIAC THREE DOTS BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0747 0062;00E0 05AE 0747 0315 0062;0061 05AE 0300 0747 0315 0062;00E0 05AE 0747 0315 0062;0061 05AE 0300 0747 0315 0062; # (a◌̕◌̀◌֮◌݇b; à◌֮◌݇◌̕b; a◌֮◌̀◌݇◌̕b; à◌֮◌݇◌̕b; a◌֮◌̀◌݇◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, SYRIAC OBLIQUE LINE ABOVE, LATIN SMALL LETTER B
+0061 0747 0315 0300 05AE 0062;0061 05AE 0747 0300 0315 0062;0061 05AE 0747 0300 0315 0062;0061 05AE 0747 0300 0315 0062;0061 05AE 0747 0300 0315 0062; # (a◌݇◌̕◌̀◌֮b; a◌֮◌݇◌̀◌̕b; a◌֮◌݇◌̀◌̕b; a◌֮◌݇◌̀◌̕b; a◌֮◌݇◌̀◌̕b; ) LATIN SMALL LETTER A, SYRIAC OBLIQUE LINE ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 059A 0316 302A 0748 0062;0061 302A 0316 0748 059A 0062;0061 302A 0316 0748 059A 0062;0061 302A 0316 0748 059A 0062;0061 302A 0316 0748 059A 0062; # (a◌֚◌̖◌〪◌݈b; a◌〪◌̖◌݈◌֚b; a◌〪◌̖◌݈◌֚b; a◌〪◌̖◌݈◌֚b; a◌〪◌̖◌݈◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, SYRIAC OBLIQUE LINE BELOW, LATIN SMALL LETTER B
+0061 0748 059A 0316 302A 0062;0061 302A 0748 0316 059A 0062;0061 302A 0748 0316 059A 0062;0061 302A 0748 0316 059A 0062;0061 302A 0748 0316 059A 0062; # (a◌݈◌֚◌̖◌〪b; a◌〪◌݈◌̖◌֚b; a◌〪◌݈◌̖◌֚b; a◌〪◌݈◌̖◌֚b; a◌〪◌݈◌̖◌֚b; ) LATIN SMALL LETTER A, SYRIAC OBLIQUE LINE BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0749 0062;00E0 05AE 0749 0315 0062;0061 05AE 0300 0749 0315 0062;00E0 05AE 0749 0315 0062;0061 05AE 0300 0749 0315 0062; # (a◌̕◌̀◌֮◌݉b; à◌֮◌݉◌̕b; a◌֮◌̀◌݉◌̕b; à◌֮◌݉◌̕b; a◌֮◌̀◌݉◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, SYRIAC MUSIC, LATIN SMALL LETTER B
+0061 0749 0315 0300 05AE 0062;0061 05AE 0749 0300 0315 0062;0061 05AE 0749 0300 0315 0062;0061 05AE 0749 0300 0315 0062;0061 05AE 0749 0300 0315 0062; # (a◌݉◌̕◌̀◌֮b; a◌֮◌݉◌̀◌̕b; a◌֮◌݉◌̀◌̕b; a◌֮◌݉◌̀◌̕b; a◌֮◌݉◌̀◌̕b; ) LATIN SMALL LETTER A, SYRIAC MUSIC, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 074A 0062;00E0 05AE 074A 0315 0062;0061 05AE 0300 074A 0315 0062;00E0 05AE 074A 0315 0062;0061 05AE 0300 074A 0315 0062; # (a◌̕◌̀◌֮◌݊b; à◌֮◌݊◌̕b; a◌֮◌̀◌݊◌̕b; à◌֮◌݊◌̕b; a◌֮◌̀◌݊◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, SYRIAC BARREKH, LATIN SMALL LETTER B
+0061 074A 0315 0300 05AE 0062;0061 05AE 074A 0300 0315 0062;0061 05AE 074A 0300 0315 0062;0061 05AE 074A 0300 0315 0062;0061 05AE 074A 0300 0315 0062; # (a◌݊◌̕◌̀◌֮b; a◌֮◌݊◌̀◌̕b; a◌֮◌݊◌̀◌̕b; a◌֮◌݊◌̀◌̕b; a◌֮◌݊◌̀◌̕b; ) LATIN SMALL LETTER A, SYRIAC BARREKH, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 3099 093C 0334 093C 0062;0061 0334 093C 093C 3099 0062;0061 0334 093C 093C 3099 0062;0061 0334 093C 093C 3099 0062;0061 0334 093C 093C 3099 0062; # (a◌゙◌़◌̴◌़b; a◌̴◌़◌़◌゙b; a◌̴◌़◌़◌゙b; a◌̴◌़◌़◌゙b; a◌̴◌़◌़◌゙b; ) LATIN SMALL LETTER A, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, DEVANAGARI SIGN NUKTA, LATIN SMALL LETTER B
+0061 093C 3099 093C 0334 0062;0061 0334 093C 093C 3099 0062;0061 0334 093C 093C 3099 0062;0061 0334 093C 093C 3099 0062;0061 0334 093C 093C 3099 0062; # (a◌़◌゙◌़◌̴b; a◌̴◌़◌़◌゙b; a◌̴◌़◌़◌゙b; a◌̴◌़◌़◌゙b; a◌̴◌़◌़◌゙b; ) LATIN SMALL LETTER A, DEVANAGARI SIGN NUKTA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, LATIN SMALL LETTER B
+0061 05B0 094D 3099 094D 0062;0061 3099 094D 094D 05B0 0062;0061 3099 094D 094D 05B0 0062;0061 3099 094D 094D 05B0 0062;0061 3099 094D 094D 05B0 0062; # (a◌ְ◌à¥â—Œã‚™â—Œà¥b; a◌゙◌à¥â—Œà¥â—ŒÖ°b; a◌゙◌à¥â—Œà¥â—ŒÖ°b; a◌゙◌à¥â—Œà¥â—ŒÖ°b; a◌゙◌à¥â—Œà¥â—ŒÖ°b; ) LATIN SMALL LETTER A, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, DEVANAGARI SIGN VIRAMA, LATIN SMALL LETTER B
+0061 094D 05B0 094D 3099 0062;0061 3099 094D 094D 05B0 0062;0061 3099 094D 094D 05B0 0062;0061 3099 094D 094D 05B0 0062;0061 3099 094D 094D 05B0 0062; # (aâ—Œà¥â—ŒÖ°â—Œà¥â—Œã‚™b; a◌゙◌à¥â—Œà¥â—ŒÖ°b; a◌゙◌à¥â—Œà¥â—ŒÖ°b; a◌゙◌à¥â—Œà¥â—ŒÖ°b; a◌゙◌à¥â—Œà¥â—ŒÖ°b; ) LATIN SMALL LETTER A, DEVANAGARI SIGN VIRAMA, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0951 0062;00E0 05AE 0951 0315 0062;0061 05AE 0300 0951 0315 0062;00E0 05AE 0951 0315 0062;0061 05AE 0300 0951 0315 0062; # (a◌̕◌̀◌֮◌॑b; à◌֮◌॑◌̕b; a◌֮◌̀◌॑◌̕b; à◌֮◌॑◌̕b; a◌֮◌̀◌॑◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, DEVANAGARI STRESS SIGN UDATTA, LATIN SMALL LETTER B
+0061 0951 0315 0300 05AE 0062;0061 05AE 0951 0300 0315 0062;0061 05AE 0951 0300 0315 0062;0061 05AE 0951 0300 0315 0062;0061 05AE 0951 0300 0315 0062; # (a◌॑◌̕◌̀◌֮b; a◌֮◌॑◌̀◌̕b; a◌֮◌॑◌̀◌̕b; a◌֮◌॑◌̀◌̕b; a◌֮◌॑◌̀◌̕b; ) LATIN SMALL LETTER A, DEVANAGARI STRESS SIGN UDATTA, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 059A 0316 302A 0952 0062;0061 302A 0316 0952 059A 0062;0061 302A 0316 0952 059A 0062;0061 302A 0316 0952 059A 0062;0061 302A 0316 0952 059A 0062; # (a◌֚◌̖◌〪◌॒b; a◌〪◌̖◌॒◌֚b; a◌〪◌̖◌॒◌֚b; a◌〪◌̖◌॒◌֚b; a◌〪◌̖◌॒◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, DEVANAGARI STRESS SIGN ANUDATTA, LATIN SMALL LETTER B
+0061 0952 059A 0316 302A 0062;0061 302A 0952 0316 059A 0062;0061 302A 0952 0316 059A 0062;0061 302A 0952 0316 059A 0062;0061 302A 0952 0316 059A 0062; # (a◌॒◌֚◌̖◌〪b; a◌〪◌॒◌̖◌֚b; a◌〪◌॒◌̖◌֚b; a◌〪◌॒◌̖◌֚b; a◌〪◌॒◌̖◌֚b; ) LATIN SMALL LETTER A, DEVANAGARI STRESS SIGN ANUDATTA, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0953 0062;00E0 05AE 0953 0315 0062;0061 05AE 0300 0953 0315 0062;00E0 05AE 0953 0315 0062;0061 05AE 0300 0953 0315 0062; # (a◌̕◌̀◌֮◌॓b; à◌֮◌॓◌̕b; a◌֮◌̀◌॓◌̕b; à◌֮◌॓◌̕b; a◌֮◌̀◌॓◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, DEVANAGARI GRAVE ACCENT, LATIN SMALL LETTER B
+0061 0953 0315 0300 05AE 0062;0061 05AE 0953 0300 0315 0062;0061 05AE 0953 0300 0315 0062;0061 05AE 0953 0300 0315 0062;0061 05AE 0953 0300 0315 0062; # (a◌॓◌̕◌̀◌֮b; a◌֮◌॓◌̀◌̕b; a◌֮◌॓◌̀◌̕b; a◌֮◌॓◌̀◌̕b; a◌֮◌॓◌̀◌̕b; ) LATIN SMALL LETTER A, DEVANAGARI GRAVE ACCENT, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0954 0062;00E0 05AE 0954 0315 0062;0061 05AE 0300 0954 0315 0062;00E0 05AE 0954 0315 0062;0061 05AE 0300 0954 0315 0062; # (a◌̕◌̀◌֮◌॔b; à◌֮◌॔◌̕b; a◌֮◌̀◌॔◌̕b; à◌֮◌॔◌̕b; a◌֮◌̀◌॔◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, DEVANAGARI ACUTE ACCENT, LATIN SMALL LETTER B
+0061 0954 0315 0300 05AE 0062;0061 05AE 0954 0300 0315 0062;0061 05AE 0954 0300 0315 0062;0061 05AE 0954 0300 0315 0062;0061 05AE 0954 0300 0315 0062; # (a◌॔◌̕◌̀◌֮b; a◌֮◌॔◌̀◌̕b; a◌֮◌॔◌̀◌̕b; a◌֮◌॔◌̀◌̕b; a◌֮◌॔◌̀◌̕b; ) LATIN SMALL LETTER A, DEVANAGARI ACUTE ACCENT, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 3099 093C 0334 09BC 0062;0061 0334 093C 09BC 3099 0062;0061 0334 093C 09BC 3099 0062;0061 0334 093C 09BC 3099 0062;0061 0334 093C 09BC 3099 0062; # (a◌゙◌़◌̴◌়b; a◌̴◌़◌়◌゙b; a◌̴◌़◌়◌゙b; a◌̴◌़◌়◌゙b; a◌̴◌़◌়◌゙b; ) LATIN SMALL LETTER A, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, BENGALI SIGN NUKTA, LATIN SMALL LETTER B
+0061 09BC 3099 093C 0334 0062;0061 0334 09BC 093C 3099 0062;0061 0334 09BC 093C 3099 0062;0061 0334 09BC 093C 3099 0062;0061 0334 09BC 093C 3099 0062; # (a◌়◌゙◌़◌̴b; a◌̴◌়◌़◌゙b; a◌̴◌়◌़◌゙b; a◌̴◌়◌़◌゙b; a◌̴◌়◌़◌゙b; ) LATIN SMALL LETTER A, BENGALI SIGN NUKTA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, LATIN SMALL LETTER B
+0061 05B0 094D 3099 09CD 0062;0061 3099 094D 09CD 05B0 0062;0061 3099 094D 09CD 05B0 0062;0061 3099 094D 09CD 05B0 0062;0061 3099 094D 09CD 05B0 0062; # (a◌ְ◌à¥â—Œã‚™â—Œà§b; a◌゙◌à¥â—Œà§â—ŒÖ°b; a◌゙◌à¥â—Œà§â—ŒÖ°b; a◌゙◌à¥â—Œà§â—ŒÖ°b; a◌゙◌à¥â—Œà§â—ŒÖ°b; ) LATIN SMALL LETTER A, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, BENGALI SIGN VIRAMA, LATIN SMALL LETTER B
+0061 09CD 05B0 094D 3099 0062;0061 3099 09CD 094D 05B0 0062;0061 3099 09CD 094D 05B0 0062;0061 3099 09CD 094D 05B0 0062;0061 3099 09CD 094D 05B0 0062; # (aâ—Œà§â—ŒÖ°â—Œà¥â—Œã‚™b; a◌゙◌à§â—Œà¥â—ŒÖ°b; a◌゙◌à§â—Œà¥â—ŒÖ°b; a◌゙◌à§â—Œà¥â—ŒÖ°b; a◌゙◌à§â—Œà¥â—ŒÖ°b; ) LATIN SMALL LETTER A, BENGALI SIGN VIRAMA, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, LATIN SMALL LETTER B
+0061 3099 093C 0334 0A3C 0062;0061 0334 093C 0A3C 3099 0062;0061 0334 093C 0A3C 3099 0062;0061 0334 093C 0A3C 3099 0062;0061 0334 093C 0A3C 3099 0062; # (a◌゙◌़◌̴◌਼b; a◌̴◌़◌਼◌゙b; a◌̴◌़◌਼◌゙b; a◌̴◌़◌਼◌゙b; a◌̴◌़◌਼◌゙b; ) LATIN SMALL LETTER A, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, GURMUKHI SIGN NUKTA, LATIN SMALL LETTER B
+0061 0A3C 3099 093C 0334 0062;0061 0334 0A3C 093C 3099 0062;0061 0334 0A3C 093C 3099 0062;0061 0334 0A3C 093C 3099 0062;0061 0334 0A3C 093C 3099 0062; # (a◌਼◌゙◌़◌̴b; a◌̴◌਼◌़◌゙b; a◌̴◌਼◌़◌゙b; a◌̴◌਼◌़◌゙b; a◌̴◌਼◌़◌゙b; ) LATIN SMALL LETTER A, GURMUKHI SIGN NUKTA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, LATIN SMALL LETTER B
+0061 05B0 094D 3099 0A4D 0062;0061 3099 094D 0A4D 05B0 0062;0061 3099 094D 0A4D 05B0 0062;0061 3099 094D 0A4D 05B0 0062;0061 3099 094D 0A4D 05B0 0062; # (a◌ְ◌à¥â—Œã‚™â—Œà©b; a◌゙◌à¥â—Œà©â—ŒÖ°b; a◌゙◌à¥â—Œà©â—ŒÖ°b; a◌゙◌à¥â—Œà©â—ŒÖ°b; a◌゙◌à¥â—Œà©â—ŒÖ°b; ) LATIN SMALL LETTER A, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, GURMUKHI SIGN VIRAMA, LATIN SMALL LETTER B
+0061 0A4D 05B0 094D 3099 0062;0061 3099 0A4D 094D 05B0 0062;0061 3099 0A4D 094D 05B0 0062;0061 3099 0A4D 094D 05B0 0062;0061 3099 0A4D 094D 05B0 0062; # (aâ—Œà©â—ŒÖ°â—Œà¥â—Œã‚™b; a◌゙◌à©â—Œà¥â—ŒÖ°b; a◌゙◌à©â—Œà¥â—ŒÖ°b; a◌゙◌à©â—Œà¥â—ŒÖ°b; a◌゙◌à©â—Œà¥â—ŒÖ°b; ) LATIN SMALL LETTER A, GURMUKHI SIGN VIRAMA, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, LATIN SMALL LETTER B
+0061 3099 093C 0334 0ABC 0062;0061 0334 093C 0ABC 3099 0062;0061 0334 093C 0ABC 3099 0062;0061 0334 093C 0ABC 3099 0062;0061 0334 093C 0ABC 3099 0062; # (a◌゙◌़◌̴◌઼b; a◌̴◌़◌઼◌゙b; a◌̴◌़◌઼◌゙b; a◌̴◌़◌઼◌゙b; a◌̴◌़◌઼◌゙b; ) LATIN SMALL LETTER A, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, GUJARATI SIGN NUKTA, LATIN SMALL LETTER B
+0061 0ABC 3099 093C 0334 0062;0061 0334 0ABC 093C 3099 0062;0061 0334 0ABC 093C 3099 0062;0061 0334 0ABC 093C 3099 0062;0061 0334 0ABC 093C 3099 0062; # (a◌઼◌゙◌़◌̴b; a◌̴◌઼◌़◌゙b; a◌̴◌઼◌़◌゙b; a◌̴◌઼◌़◌゙b; a◌̴◌઼◌़◌゙b; ) LATIN SMALL LETTER A, GUJARATI SIGN NUKTA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, LATIN SMALL LETTER B
+0061 05B0 094D 3099 0ACD 0062;0061 3099 094D 0ACD 05B0 0062;0061 3099 094D 0ACD 05B0 0062;0061 3099 094D 0ACD 05B0 0062;0061 3099 094D 0ACD 05B0 0062; # (a◌ְ◌à¥â—Œã‚™â—Œà«b; a◌゙◌à¥â—Œà«â—ŒÖ°b; a◌゙◌à¥â—Œà«â—ŒÖ°b; a◌゙◌à¥â—Œà«â—ŒÖ°b; a◌゙◌à¥â—Œà«â—ŒÖ°b; ) LATIN SMALL LETTER A, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, GUJARATI SIGN VIRAMA, LATIN SMALL LETTER B
+0061 0ACD 05B0 094D 3099 0062;0061 3099 0ACD 094D 05B0 0062;0061 3099 0ACD 094D 05B0 0062;0061 3099 0ACD 094D 05B0 0062;0061 3099 0ACD 094D 05B0 0062; # (aâ—Œà«â—ŒÖ°â—Œà¥â—Œã‚™b; a◌゙◌à«â—Œà¥â—ŒÖ°b; a◌゙◌à«â—Œà¥â—ŒÖ°b; a◌゙◌à«â—Œà¥â—ŒÖ°b; a◌゙◌à«â—Œà¥â—ŒÖ°b; ) LATIN SMALL LETTER A, GUJARATI SIGN VIRAMA, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, LATIN SMALL LETTER B
+0061 3099 093C 0334 0B3C 0062;0061 0334 093C 0B3C 3099 0062;0061 0334 093C 0B3C 3099 0062;0061 0334 093C 0B3C 3099 0062;0061 0334 093C 0B3C 3099 0062; # (a◌゙◌़◌̴◌଼b; a◌̴◌़◌଼◌゙b; a◌̴◌़◌଼◌゙b; a◌̴◌़◌଼◌゙b; a◌̴◌़◌଼◌゙b; ) LATIN SMALL LETTER A, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, ORIYA SIGN NUKTA, LATIN SMALL LETTER B
+0061 0B3C 3099 093C 0334 0062;0061 0334 0B3C 093C 3099 0062;0061 0334 0B3C 093C 3099 0062;0061 0334 0B3C 093C 3099 0062;0061 0334 0B3C 093C 3099 0062; # (a◌଼◌゙◌़◌̴b; a◌̴◌଼◌़◌゙b; a◌̴◌଼◌़◌゙b; a◌̴◌଼◌़◌゙b; a◌̴◌଼◌़◌゙b; ) LATIN SMALL LETTER A, ORIYA SIGN NUKTA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, LATIN SMALL LETTER B
+0061 05B0 094D 3099 0B4D 0062;0061 3099 094D 0B4D 05B0 0062;0061 3099 094D 0B4D 05B0 0062;0061 3099 094D 0B4D 05B0 0062;0061 3099 094D 0B4D 05B0 0062; # (a◌ְ◌à¥â—Œã‚™â—Œà­b; a◌゙◌à¥â—Œà­â—ŒÖ°b; a◌゙◌à¥â—Œà­â—ŒÖ°b; a◌゙◌à¥â—Œà­â—ŒÖ°b; a◌゙◌à¥â—Œà­â—ŒÖ°b; ) LATIN SMALL LETTER A, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, ORIYA SIGN VIRAMA, LATIN SMALL LETTER B
+0061 0B4D 05B0 094D 3099 0062;0061 3099 0B4D 094D 05B0 0062;0061 3099 0B4D 094D 05B0 0062;0061 3099 0B4D 094D 05B0 0062;0061 3099 0B4D 094D 05B0 0062; # (aâ—Œà­â—ŒÖ°â—Œà¥â—Œã‚™b; a◌゙◌à­â—Œà¥â—ŒÖ°b; a◌゙◌à­â—Œà¥â—ŒÖ°b; a◌゙◌à­â—Œà¥â—ŒÖ°b; a◌゙◌à­â—Œà¥â—ŒÖ°b; ) LATIN SMALL LETTER A, ORIYA SIGN VIRAMA, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, LATIN SMALL LETTER B
+0061 05B0 094D 3099 0BCD 0062;0061 3099 094D 0BCD 05B0 0062;0061 3099 094D 0BCD 05B0 0062;0061 3099 094D 0BCD 05B0 0062;0061 3099 094D 0BCD 05B0 0062; # (a◌ְ◌à¥â—Œã‚™â—Œà¯b; a◌゙◌à¥â—Œà¯â—ŒÖ°b; a◌゙◌à¥â—Œà¯â—ŒÖ°b; a◌゙◌à¥â—Œà¯â—ŒÖ°b; a◌゙◌à¥â—Œà¯â—ŒÖ°b; ) LATIN SMALL LETTER A, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, TAMIL SIGN VIRAMA, LATIN SMALL LETTER B
+0061 0BCD 05B0 094D 3099 0062;0061 3099 0BCD 094D 05B0 0062;0061 3099 0BCD 094D 05B0 0062;0061 3099 0BCD 094D 05B0 0062;0061 3099 0BCD 094D 05B0 0062; # (aâ—Œà¯â—ŒÖ°â—Œà¥â—Œã‚™b; a◌゙◌à¯â—Œà¥â—ŒÖ°b; a◌゙◌à¯â—Œà¥â—ŒÖ°b; a◌゙◌à¯â—Œà¥â—ŒÖ°b; a◌゙◌à¯â—Œà¥â—ŒÖ°b; ) LATIN SMALL LETTER A, TAMIL SIGN VIRAMA, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, LATIN SMALL LETTER B
+0061 05B0 094D 3099 0C4D 0062;0061 3099 094D 0C4D 05B0 0062;0061 3099 094D 0C4D 05B0 0062;0061 3099 094D 0C4D 05B0 0062;0061 3099 094D 0C4D 05B0 0062; # (a◌ְ◌à¥â—Œã‚™â—Œà±b; a◌゙◌à¥â—Œà±â—ŒÖ°b; a◌゙◌à¥â—Œà±â—ŒÖ°b; a◌゙◌à¥â—Œà±â—ŒÖ°b; a◌゙◌à¥â—Œà±â—ŒÖ°b; ) LATIN SMALL LETTER A, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, TELUGU SIGN VIRAMA, LATIN SMALL LETTER B
+0061 0C4D 05B0 094D 3099 0062;0061 3099 0C4D 094D 05B0 0062;0061 3099 0C4D 094D 05B0 0062;0061 3099 0C4D 094D 05B0 0062;0061 3099 0C4D 094D 05B0 0062; # (aâ—Œà±â—ŒÖ°â—Œà¥â—Œã‚™b; a◌゙◌à±â—Œà¥â—ŒÖ°b; a◌゙◌à±â—Œà¥â—ŒÖ°b; a◌゙◌à±â—Œà¥â—ŒÖ°b; a◌゙◌à±â—Œà¥â—ŒÖ°b; ) LATIN SMALL LETTER A, TELUGU SIGN VIRAMA, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, LATIN SMALL LETTER B
+0061 0C56 0C55 0711 0C55 0062;0061 0711 0C55 0C55 0C56 0062;0061 0711 0C55 0C55 0C56 0062;0061 0711 0C55 0C55 0C56 0062;0061 0711 0C55 0C55 0C56 0062; # (a◌ౖ◌ౕ◌ܑ◌ౕb; a◌ܑ◌ౕ◌ౕ◌ౖb; a◌ܑ◌ౕ◌ౕ◌ౖb; a◌ܑ◌ౕ◌ౕ◌ౖb; a◌ܑ◌ౕ◌ౕ◌ౖb; ) LATIN SMALL LETTER A, TELUGU AI LENGTH MARK, TELUGU LENGTH MARK, SYRIAC LETTER SUPERSCRIPT ALAPH, TELUGU LENGTH MARK, LATIN SMALL LETTER B
+0061 0C55 0C56 0C55 0711 0062;0061 0711 0C55 0C55 0C56 0062;0061 0711 0C55 0C55 0C56 0062;0061 0711 0C55 0C55 0C56 0062;0061 0711 0C55 0C55 0C56 0062; # (a◌ౕ◌ౖ◌ౕ◌ܑb; a◌ܑ◌ౕ◌ౕ◌ౖb; a◌ܑ◌ౕ◌ౕ◌ౖb; a◌ܑ◌ౕ◌ౕ◌ౖb; a◌ܑ◌ౕ◌ౕ◌ౖb; ) LATIN SMALL LETTER A, TELUGU LENGTH MARK, TELUGU AI LENGTH MARK, TELUGU LENGTH MARK, SYRIAC LETTER SUPERSCRIPT ALAPH, LATIN SMALL LETTER B
+0061 0E38 0C56 0C55 0C56 0062;0061 0C55 0C56 0C56 0E38 0062;0061 0C55 0C56 0C56 0E38 0062;0061 0C55 0C56 0C56 0E38 0062;0061 0C55 0C56 0C56 0E38 0062; # (a◌ุ◌ౖ◌ౕ◌ౖb; a◌ౕ◌ౖ◌ౖ◌ุb; a◌ౕ◌ౖ◌ౖ◌ุb; a◌ౕ◌ౖ◌ౖ◌ุb; a◌ౕ◌ౖ◌ౖ◌ุb; ) LATIN SMALL LETTER A, THAI CHARACTER SARA U, TELUGU AI LENGTH MARK, TELUGU LENGTH MARK, TELUGU AI LENGTH MARK, LATIN SMALL LETTER B
+0061 0C56 0E38 0C56 0C55 0062;0061 0C55 0C56 0C56 0E38 0062;0061 0C55 0C56 0C56 0E38 0062;0061 0C55 0C56 0C56 0E38 0062;0061 0C55 0C56 0C56 0E38 0062; # (a◌ౖ◌ุ◌ౖ◌ౕb; a◌ౕ◌ౖ◌ౖ◌ุb; a◌ౕ◌ౖ◌ౖ◌ุb; a◌ౕ◌ౖ◌ౖ◌ุb; a◌ౕ◌ౖ◌ౖ◌ุb; ) LATIN SMALL LETTER A, TELUGU AI LENGTH MARK, THAI CHARACTER SARA U, TELUGU AI LENGTH MARK, TELUGU LENGTH MARK, LATIN SMALL LETTER B
+0061 3099 093C 0334 0CBC 0062;0061 0334 093C 0CBC 3099 0062;0061 0334 093C 0CBC 3099 0062;0061 0334 093C 0CBC 3099 0062;0061 0334 093C 0CBC 3099 0062; # (a◌゙◌़◌̴◌಼b; a◌̴◌़◌಼◌゙b; a◌̴◌़◌಼◌゙b; a◌̴◌़◌಼◌゙b; a◌̴◌़◌಼◌゙b; ) LATIN SMALL LETTER A, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, KANNADA SIGN NUKTA, LATIN SMALL LETTER B
+0061 0CBC 3099 093C 0334 0062;0061 0334 0CBC 093C 3099 0062;0061 0334 0CBC 093C 3099 0062;0061 0334 0CBC 093C 3099 0062;0061 0334 0CBC 093C 3099 0062; # (a◌಼◌゙◌़◌̴b; a◌̴◌಼◌़◌゙b; a◌̴◌಼◌़◌゙b; a◌̴◌಼◌़◌゙b; a◌̴◌಼◌़◌゙b; ) LATIN SMALL LETTER A, KANNADA SIGN NUKTA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, LATIN SMALL LETTER B
+0061 05B0 094D 3099 0CCD 0062;0061 3099 094D 0CCD 05B0 0062;0061 3099 094D 0CCD 05B0 0062;0061 3099 094D 0CCD 05B0 0062;0061 3099 094D 0CCD 05B0 0062; # (a◌ְ◌à¥â—Œã‚™â—Œà³b; a◌゙◌à¥â—Œà³â—ŒÖ°b; a◌゙◌à¥â—Œà³â—ŒÖ°b; a◌゙◌à¥â—Œà³â—ŒÖ°b; a◌゙◌à¥â—Œà³â—ŒÖ°b; ) LATIN SMALL LETTER A, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, KANNADA SIGN VIRAMA, LATIN SMALL LETTER B
+0061 0CCD 05B0 094D 3099 0062;0061 3099 0CCD 094D 05B0 0062;0061 3099 0CCD 094D 05B0 0062;0061 3099 0CCD 094D 05B0 0062;0061 3099 0CCD 094D 05B0 0062; # (aâ—Œà³â—ŒÖ°â—Œà¥â—Œã‚™b; a◌゙◌à³â—Œà¥â—ŒÖ°b; a◌゙◌à³â—Œà¥â—ŒÖ°b; a◌゙◌à³â—Œà¥â—ŒÖ°b; a◌゙◌à³â—Œà¥â—ŒÖ°b; ) LATIN SMALL LETTER A, KANNADA SIGN VIRAMA, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, LATIN SMALL LETTER B
+0061 05B0 094D 3099 0D4D 0062;0061 3099 094D 0D4D 05B0 0062;0061 3099 094D 0D4D 05B0 0062;0061 3099 094D 0D4D 05B0 0062;0061 3099 094D 0D4D 05B0 0062; # (a◌ְ◌à¥â—Œã‚™â—Œàµb; a◌゙◌à¥â—Œàµâ—ŒÖ°b; a◌゙◌à¥â—Œàµâ—ŒÖ°b; a◌゙◌à¥â—Œàµâ—ŒÖ°b; a◌゙◌à¥â—Œàµâ—ŒÖ°b; ) LATIN SMALL LETTER A, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, MALAYALAM SIGN VIRAMA, LATIN SMALL LETTER B
+0061 0D4D 05B0 094D 3099 0062;0061 3099 0D4D 094D 05B0 0062;0061 3099 0D4D 094D 05B0 0062;0061 3099 0D4D 094D 05B0 0062;0061 3099 0D4D 094D 05B0 0062; # (aâ—Œàµâ—ŒÖ°â—Œà¥â—Œã‚™b; a◌゙◌àµâ—Œà¥â—ŒÖ°b; a◌゙◌àµâ—Œà¥â—ŒÖ°b; a◌゙◌àµâ—Œà¥â—ŒÖ°b; a◌゙◌àµâ—Œà¥â—ŒÖ°b; ) LATIN SMALL LETTER A, MALAYALAM SIGN VIRAMA, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, LATIN SMALL LETTER B
+0061 05B0 094D 3099 0DCA 0062;0061 3099 094D 0DCA 05B0 0062;0061 3099 094D 0DCA 05B0 0062;0061 3099 094D 0DCA 05B0 0062;0061 3099 094D 0DCA 05B0 0062; # (a◌ְ◌à¥â—Œã‚™â—Œà·Šb; a◌゙◌à¥â—Œà·Šâ—ŒÖ°b; a◌゙◌à¥â—Œà·Šâ—ŒÖ°b; a◌゙◌à¥â—Œà·Šâ—ŒÖ°b; a◌゙◌à¥â—Œà·Šâ—ŒÖ°b; ) LATIN SMALL LETTER A, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, SINHALA SIGN AL-LAKUNA, LATIN SMALL LETTER B
+0061 0DCA 05B0 094D 3099 0062;0061 3099 0DCA 094D 05B0 0062;0061 3099 0DCA 094D 05B0 0062;0061 3099 0DCA 094D 05B0 0062;0061 3099 0DCA 094D 05B0 0062; # (a◌්◌ְ◌à¥â—Œã‚™b; a◌゙◌්◌à¥â—ŒÖ°b; a◌゙◌්◌à¥â—ŒÖ°b; a◌゙◌්◌à¥â—ŒÖ°b; a◌゙◌්◌à¥â—ŒÖ°b; ) LATIN SMALL LETTER A, SINHALA SIGN AL-LAKUNA, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, LATIN SMALL LETTER B
+0061 0E48 0E38 0C56 0E38 0062;0061 0C56 0E38 0E38 0E48 0062;0061 0C56 0E38 0E38 0E48 0062;0061 0C56 0E38 0E38 0E48 0062;0061 0C56 0E38 0E38 0E48 0062; # (a◌่◌ุ◌ౖ◌ุb; a◌ౖ◌ุ◌ุ◌่b; a◌ౖ◌ุ◌ุ◌่b; a◌ౖ◌ุ◌ุ◌่b; a◌ౖ◌ุ◌ุ◌่b; ) LATIN SMALL LETTER A, THAI CHARACTER MAI EK, THAI CHARACTER SARA U, TELUGU AI LENGTH MARK, THAI CHARACTER SARA U, LATIN SMALL LETTER B
+0061 0E38 0E48 0E38 0C56 0062;0061 0C56 0E38 0E38 0E48 0062;0061 0C56 0E38 0E38 0E48 0062;0061 0C56 0E38 0E38 0E48 0062;0061 0C56 0E38 0E38 0E48 0062; # (a◌ุ◌่◌ุ◌ౖb; a◌ౖ◌ุ◌ุ◌่b; a◌ౖ◌ุ◌ุ◌่b; a◌ౖ◌ุ◌ุ◌่b; a◌ౖ◌ุ◌ุ◌่b; ) LATIN SMALL LETTER A, THAI CHARACTER SARA U, THAI CHARACTER MAI EK, THAI CHARACTER SARA U, TELUGU AI LENGTH MARK, LATIN SMALL LETTER B
+0061 0E48 0E38 0C56 0E39 0062;0061 0C56 0E38 0E39 0E48 0062;0061 0C56 0E38 0E39 0E48 0062;0061 0C56 0E38 0E39 0E48 0062;0061 0C56 0E38 0E39 0E48 0062; # (a◌่◌ุ◌ౖ◌ูb; a◌ౖ◌ุ◌ู◌่b; a◌ౖ◌ุ◌ู◌่b; a◌ౖ◌ุ◌ู◌่b; a◌ౖ◌ุ◌ู◌่b; ) LATIN SMALL LETTER A, THAI CHARACTER MAI EK, THAI CHARACTER SARA U, TELUGU AI LENGTH MARK, THAI CHARACTER SARA UU, LATIN SMALL LETTER B
+0061 0E39 0E48 0E38 0C56 0062;0061 0C56 0E39 0E38 0E48 0062;0061 0C56 0E39 0E38 0E48 0062;0061 0C56 0E39 0E38 0E48 0062;0061 0C56 0E39 0E38 0E48 0062; # (a◌ู◌่◌ุ◌ౖb; a◌ౖ◌ู◌ุ◌่b; a◌ౖ◌ู◌ุ◌่b; a◌ౖ◌ู◌ุ◌่b; a◌ౖ◌ู◌ุ◌่b; ) LATIN SMALL LETTER A, THAI CHARACTER SARA UU, THAI CHARACTER MAI EK, THAI CHARACTER SARA U, TELUGU AI LENGTH MARK, LATIN SMALL LETTER B
+0061 05B0 094D 3099 0E3A 0062;0061 3099 094D 0E3A 05B0 0062;0061 3099 094D 0E3A 05B0 0062;0061 3099 094D 0E3A 05B0 0062;0061 3099 094D 0E3A 05B0 0062; # (a◌ְ◌à¥â—Œã‚™â—Œà¸ºb; a◌゙◌à¥â—Œà¸ºâ—ŒÖ°b; a◌゙◌à¥â—Œà¸ºâ—ŒÖ°b; a◌゙◌à¥â—Œà¸ºâ—ŒÖ°b; a◌゙◌à¥â—Œà¸ºâ—ŒÖ°b; ) LATIN SMALL LETTER A, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, THAI CHARACTER PHINTHU, LATIN SMALL LETTER B
+0061 0E3A 05B0 094D 3099 0062;0061 3099 0E3A 094D 05B0 0062;0061 3099 0E3A 094D 05B0 0062;0061 3099 0E3A 094D 05B0 0062;0061 3099 0E3A 094D 05B0 0062; # (a◌ฺ◌ְ◌à¥â—Œã‚™b; a◌゙◌ฺ◌à¥â—ŒÖ°b; a◌゙◌ฺ◌à¥â—ŒÖ°b; a◌゙◌ฺ◌à¥â—ŒÖ°b; a◌゙◌ฺ◌à¥â—ŒÖ°b; ) LATIN SMALL LETTER A, THAI CHARACTER PHINTHU, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, LATIN SMALL LETTER B
+0061 0EB8 0E48 0E38 0E48 0062;0061 0E38 0E48 0E48 0EB8 0062;0061 0E38 0E48 0E48 0EB8 0062;0061 0E38 0E48 0E48 0EB8 0062;0061 0E38 0E48 0E48 0EB8 0062; # (a◌ຸ◌่◌ุ◌่b; a◌ุ◌่◌่◌ຸb; a◌ุ◌่◌่◌ຸb; a◌ุ◌่◌่◌ຸb; a◌ุ◌่◌่◌ຸb; ) LATIN SMALL LETTER A, LAO VOWEL SIGN U, THAI CHARACTER MAI EK, THAI CHARACTER SARA U, THAI CHARACTER MAI EK, LATIN SMALL LETTER B
+0061 0E48 0EB8 0E48 0E38 0062;0061 0E38 0E48 0E48 0EB8 0062;0061 0E38 0E48 0E48 0EB8 0062;0061 0E38 0E48 0E48 0EB8 0062;0061 0E38 0E48 0E48 0EB8 0062; # (a◌่◌ຸ◌่◌ุb; a◌ุ◌่◌่◌ຸb; a◌ุ◌่◌่◌ຸb; a◌ุ◌่◌่◌ຸb; a◌ุ◌่◌่◌ຸb; ) LATIN SMALL LETTER A, THAI CHARACTER MAI EK, LAO VOWEL SIGN U, THAI CHARACTER MAI EK, THAI CHARACTER SARA U, LATIN SMALL LETTER B
+0061 0EB8 0E48 0E38 0E49 0062;0061 0E38 0E48 0E49 0EB8 0062;0061 0E38 0E48 0E49 0EB8 0062;0061 0E38 0E48 0E49 0EB8 0062;0061 0E38 0E48 0E49 0EB8 0062; # (a◌ຸ◌่◌ุ◌้b; a◌ุ◌่◌้◌ຸb; a◌ุ◌่◌้◌ຸb; a◌ุ◌่◌้◌ຸb; a◌ุ◌่◌้◌ຸb; ) LATIN SMALL LETTER A, LAO VOWEL SIGN U, THAI CHARACTER MAI EK, THAI CHARACTER SARA U, THAI CHARACTER MAI THO, LATIN SMALL LETTER B
+0061 0E49 0EB8 0E48 0E38 0062;0061 0E38 0E49 0E48 0EB8 0062;0061 0E38 0E49 0E48 0EB8 0062;0061 0E38 0E49 0E48 0EB8 0062;0061 0E38 0E49 0E48 0EB8 0062; # (a◌้◌ຸ◌่◌ุb; a◌ุ◌้◌่◌ຸb; a◌ุ◌้◌่◌ຸb; a◌ุ◌้◌่◌ຸb; a◌ุ◌้◌่◌ຸb; ) LATIN SMALL LETTER A, THAI CHARACTER MAI THO, LAO VOWEL SIGN U, THAI CHARACTER MAI EK, THAI CHARACTER SARA U, LATIN SMALL LETTER B
+0061 0EB8 0E48 0E38 0E4A 0062;0061 0E38 0E48 0E4A 0EB8 0062;0061 0E38 0E48 0E4A 0EB8 0062;0061 0E38 0E48 0E4A 0EB8 0062;0061 0E38 0E48 0E4A 0EB8 0062; # (a◌ຸ◌่◌ุ◌๊b; a◌ุ◌่◌๊◌ຸb; a◌ุ◌่◌๊◌ຸb; a◌ุ◌่◌๊◌ຸb; a◌ุ◌่◌๊◌ຸb; ) LATIN SMALL LETTER A, LAO VOWEL SIGN U, THAI CHARACTER MAI EK, THAI CHARACTER SARA U, THAI CHARACTER MAI TRI, LATIN SMALL LETTER B
+0061 0E4A 0EB8 0E48 0E38 0062;0061 0E38 0E4A 0E48 0EB8 0062;0061 0E38 0E4A 0E48 0EB8 0062;0061 0E38 0E4A 0E48 0EB8 0062;0061 0E38 0E4A 0E48 0EB8 0062; # (a◌๊◌ຸ◌่◌ุb; a◌ุ◌๊◌่◌ຸb; a◌ุ◌๊◌่◌ຸb; a◌ุ◌๊◌่◌ຸb; a◌ุ◌๊◌่◌ຸb; ) LATIN SMALL LETTER A, THAI CHARACTER MAI TRI, LAO VOWEL SIGN U, THAI CHARACTER MAI EK, THAI CHARACTER SARA U, LATIN SMALL LETTER B
+0061 0EB8 0E48 0E38 0E4B 0062;0061 0E38 0E48 0E4B 0EB8 0062;0061 0E38 0E48 0E4B 0EB8 0062;0061 0E38 0E48 0E4B 0EB8 0062;0061 0E38 0E48 0E4B 0EB8 0062; # (a◌ຸ◌่◌ุ◌๋b; a◌ุ◌่◌๋◌ຸb; a◌ุ◌่◌๋◌ຸb; a◌ุ◌่◌๋◌ຸb; a◌ุ◌่◌๋◌ຸb; ) LATIN SMALL LETTER A, LAO VOWEL SIGN U, THAI CHARACTER MAI EK, THAI CHARACTER SARA U, THAI CHARACTER MAI CHATTAWA, LATIN SMALL LETTER B
+0061 0E4B 0EB8 0E48 0E38 0062;0061 0E38 0E4B 0E48 0EB8 0062;0061 0E38 0E4B 0E48 0EB8 0062;0061 0E38 0E4B 0E48 0EB8 0062;0061 0E38 0E4B 0E48 0EB8 0062; # (a◌๋◌ຸ◌่◌ุb; a◌ุ◌๋◌่◌ຸb; a◌ุ◌๋◌่◌ຸb; a◌ุ◌๋◌่◌ຸb; a◌ุ◌๋◌่◌ຸb; ) LATIN SMALL LETTER A, THAI CHARACTER MAI CHATTAWA, LAO VOWEL SIGN U, THAI CHARACTER MAI EK, THAI CHARACTER SARA U, LATIN SMALL LETTER B
+0061 0EC8 0EB8 0E48 0EB8 0062;0061 0E48 0EB8 0EB8 0EC8 0062;0061 0E48 0EB8 0EB8 0EC8 0062;0061 0E48 0EB8 0EB8 0EC8 0062;0061 0E48 0EB8 0EB8 0EC8 0062; # (a◌່◌ຸ◌่◌ຸb; a◌่◌ຸ◌ຸ◌່b; a◌่◌ຸ◌ຸ◌່b; a◌่◌ຸ◌ຸ◌່b; a◌่◌ຸ◌ຸ◌່b; ) LATIN SMALL LETTER A, LAO TONE MAI EK, LAO VOWEL SIGN U, THAI CHARACTER MAI EK, LAO VOWEL SIGN U, LATIN SMALL LETTER B
+0061 0EB8 0EC8 0EB8 0E48 0062;0061 0E48 0EB8 0EB8 0EC8 0062;0061 0E48 0EB8 0EB8 0EC8 0062;0061 0E48 0EB8 0EB8 0EC8 0062;0061 0E48 0EB8 0EB8 0EC8 0062; # (a◌ຸ◌່◌ຸ◌่b; a◌่◌ຸ◌ຸ◌່b; a◌่◌ຸ◌ຸ◌່b; a◌่◌ຸ◌ຸ◌່b; a◌่◌ຸ◌ຸ◌່b; ) LATIN SMALL LETTER A, LAO VOWEL SIGN U, LAO TONE MAI EK, LAO VOWEL SIGN U, THAI CHARACTER MAI EK, LATIN SMALL LETTER B
+0061 0EC8 0EB8 0E48 0EB9 0062;0061 0E48 0EB8 0EB9 0EC8 0062;0061 0E48 0EB8 0EB9 0EC8 0062;0061 0E48 0EB8 0EB9 0EC8 0062;0061 0E48 0EB8 0EB9 0EC8 0062; # (a◌່◌ຸ◌่◌ູb; a◌่◌ຸ◌ູ◌່b; a◌่◌ຸ◌ູ◌່b; a◌่◌ຸ◌ູ◌່b; a◌่◌ຸ◌ູ◌່b; ) LATIN SMALL LETTER A, LAO TONE MAI EK, LAO VOWEL SIGN U, THAI CHARACTER MAI EK, LAO VOWEL SIGN UU, LATIN SMALL LETTER B
+0061 0EB9 0EC8 0EB8 0E48 0062;0061 0E48 0EB9 0EB8 0EC8 0062;0061 0E48 0EB9 0EB8 0EC8 0062;0061 0E48 0EB9 0EB8 0EC8 0062;0061 0E48 0EB9 0EB8 0EC8 0062; # (a◌ູ◌່◌ຸ◌่b; a◌่◌ູ◌ຸ◌່b; a◌่◌ູ◌ຸ◌່b; a◌่◌ູ◌ຸ◌່b; a◌่◌ູ◌ຸ◌່b; ) LATIN SMALL LETTER A, LAO VOWEL SIGN UU, LAO TONE MAI EK, LAO VOWEL SIGN U, THAI CHARACTER MAI EK, LATIN SMALL LETTER B
+0061 0F71 0EC8 0EB8 0EC8 0062;0061 0EB8 0EC8 0EC8 0F71 0062;0061 0EB8 0EC8 0EC8 0F71 0062;0061 0EB8 0EC8 0EC8 0F71 0062;0061 0EB8 0EC8 0EC8 0F71 0062; # (a◌ཱ◌່◌ຸ◌່b; a◌ຸ◌່◌່◌ཱb; a◌ຸ◌່◌່◌ཱb; a◌ຸ◌່◌່◌ཱb; a◌ຸ◌່◌່◌ཱb; ) LATIN SMALL LETTER A, TIBETAN VOWEL SIGN AA, LAO TONE MAI EK, LAO VOWEL SIGN U, LAO TONE MAI EK, LATIN SMALL LETTER B
+0061 0EC8 0F71 0EC8 0EB8 0062;0061 0EB8 0EC8 0EC8 0F71 0062;0061 0EB8 0EC8 0EC8 0F71 0062;0061 0EB8 0EC8 0EC8 0F71 0062;0061 0EB8 0EC8 0EC8 0F71 0062; # (a◌່◌ཱ◌່◌ຸb; a◌ຸ◌່◌່◌ཱb; a◌ຸ◌່◌່◌ཱb; a◌ຸ◌່◌່◌ཱb; a◌ຸ◌່◌່◌ཱb; ) LATIN SMALL LETTER A, LAO TONE MAI EK, TIBETAN VOWEL SIGN AA, LAO TONE MAI EK, LAO VOWEL SIGN U, LATIN SMALL LETTER B
+0061 0F71 0EC8 0EB8 0EC9 0062;0061 0EB8 0EC8 0EC9 0F71 0062;0061 0EB8 0EC8 0EC9 0F71 0062;0061 0EB8 0EC8 0EC9 0F71 0062;0061 0EB8 0EC8 0EC9 0F71 0062; # (a◌ཱ◌່◌ຸ◌້b; a◌ຸ◌່◌້◌ཱb; a◌ຸ◌່◌້◌ཱb; a◌ຸ◌່◌້◌ཱb; a◌ຸ◌່◌້◌ཱb; ) LATIN SMALL LETTER A, TIBETAN VOWEL SIGN AA, LAO TONE MAI EK, LAO VOWEL SIGN U, LAO TONE MAI THO, LATIN SMALL LETTER B
+0061 0EC9 0F71 0EC8 0EB8 0062;0061 0EB8 0EC9 0EC8 0F71 0062;0061 0EB8 0EC9 0EC8 0F71 0062;0061 0EB8 0EC9 0EC8 0F71 0062;0061 0EB8 0EC9 0EC8 0F71 0062; # (a◌້◌ཱ◌່◌ຸb; a◌ຸ◌້◌່◌ཱb; a◌ຸ◌້◌່◌ཱb; a◌ຸ◌້◌່◌ཱb; a◌ຸ◌້◌່◌ཱb; ) LATIN SMALL LETTER A, LAO TONE MAI THO, TIBETAN VOWEL SIGN AA, LAO TONE MAI EK, LAO VOWEL SIGN U, LATIN SMALL LETTER B
+0061 0F71 0EC8 0EB8 0ECA 0062;0061 0EB8 0EC8 0ECA 0F71 0062;0061 0EB8 0EC8 0ECA 0F71 0062;0061 0EB8 0EC8 0ECA 0F71 0062;0061 0EB8 0EC8 0ECA 0F71 0062; # (a◌ཱ◌່◌ຸ◌໊b; a◌ຸ◌່◌໊◌ཱb; a◌ຸ◌່◌໊◌ཱb; a◌ຸ◌່◌໊◌ཱb; a◌ຸ◌່◌໊◌ཱb; ) LATIN SMALL LETTER A, TIBETAN VOWEL SIGN AA, LAO TONE MAI EK, LAO VOWEL SIGN U, LAO TONE MAI TI, LATIN SMALL LETTER B
+0061 0ECA 0F71 0EC8 0EB8 0062;0061 0EB8 0ECA 0EC8 0F71 0062;0061 0EB8 0ECA 0EC8 0F71 0062;0061 0EB8 0ECA 0EC8 0F71 0062;0061 0EB8 0ECA 0EC8 0F71 0062; # (a◌໊◌ཱ◌່◌ຸb; a◌ຸ◌໊◌່◌ཱb; a◌ຸ◌໊◌່◌ཱb; a◌ຸ◌໊◌່◌ཱb; a◌ຸ◌໊◌່◌ཱb; ) LATIN SMALL LETTER A, LAO TONE MAI TI, TIBETAN VOWEL SIGN AA, LAO TONE MAI EK, LAO VOWEL SIGN U, LATIN SMALL LETTER B
+0061 0F71 0EC8 0EB8 0ECB 0062;0061 0EB8 0EC8 0ECB 0F71 0062;0061 0EB8 0EC8 0ECB 0F71 0062;0061 0EB8 0EC8 0ECB 0F71 0062;0061 0EB8 0EC8 0ECB 0F71 0062; # (a◌ཱ◌່◌ຸ◌໋b; a◌ຸ◌່◌໋◌ཱb; a◌ຸ◌່◌໋◌ཱb; a◌ຸ◌່◌໋◌ཱb; a◌ຸ◌່◌໋◌ཱb; ) LATIN SMALL LETTER A, TIBETAN VOWEL SIGN AA, LAO TONE MAI EK, LAO VOWEL SIGN U, LAO TONE MAI CATAWA, LATIN SMALL LETTER B
+0061 0ECB 0F71 0EC8 0EB8 0062;0061 0EB8 0ECB 0EC8 0F71 0062;0061 0EB8 0ECB 0EC8 0F71 0062;0061 0EB8 0ECB 0EC8 0F71 0062;0061 0EB8 0ECB 0EC8 0F71 0062; # (a◌໋◌ཱ◌່◌ຸb; a◌ຸ◌໋◌່◌ཱb; a◌ຸ◌໋◌່◌ཱb; a◌ຸ◌໋◌່◌ཱb; a◌ຸ◌໋◌່◌ཱb; ) LATIN SMALL LETTER A, LAO TONE MAI CATAWA, TIBETAN VOWEL SIGN AA, LAO TONE MAI EK, LAO VOWEL SIGN U, LATIN SMALL LETTER B
+0061 059A 0316 302A 0F18 0062;0061 302A 0316 0F18 059A 0062;0061 302A 0316 0F18 059A 0062;0061 302A 0316 0F18 059A 0062;0061 302A 0316 0F18 059A 0062; # (a◌֚◌̖◌〪◌༘b; a◌〪◌̖◌༘◌֚b; a◌〪◌̖◌༘◌֚b; a◌〪◌̖◌༘◌֚b; a◌〪◌̖◌༘◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, TIBETAN ASTROLOGICAL SIGN -KHYUD PA, LATIN SMALL LETTER B
+0061 0F18 059A 0316 302A 0062;0061 302A 0F18 0316 059A 0062;0061 302A 0F18 0316 059A 0062;0061 302A 0F18 0316 059A 0062;0061 302A 0F18 0316 059A 0062; # (a◌༘◌֚◌̖◌〪b; a◌〪◌༘◌̖◌֚b; a◌〪◌༘◌̖◌֚b; a◌〪◌༘◌̖◌֚b; a◌〪◌༘◌̖◌֚b; ) LATIN SMALL LETTER A, TIBETAN ASTROLOGICAL SIGN -KHYUD PA, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 059A 0316 302A 0F19 0062;0061 302A 0316 0F19 059A 0062;0061 302A 0316 0F19 059A 0062;0061 302A 0316 0F19 059A 0062;0061 302A 0316 0F19 059A 0062; # (a◌֚◌̖◌〪◌༙b; a◌〪◌̖◌༙◌֚b; a◌〪◌̖◌༙◌֚b; a◌〪◌̖◌༙◌֚b; a◌〪◌̖◌༙◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, TIBETAN ASTROLOGICAL SIGN SDONG TSHUGS, LATIN SMALL LETTER B
+0061 0F19 059A 0316 302A 0062;0061 302A 0F19 0316 059A 0062;0061 302A 0F19 0316 059A 0062;0061 302A 0F19 0316 059A 0062;0061 302A 0F19 0316 059A 0062; # (a◌༙◌֚◌̖◌〪b; a◌〪◌༙◌̖◌֚b; a◌〪◌༙◌̖◌֚b; a◌〪◌༙◌̖◌֚b; a◌〪◌༙◌̖◌֚b; ) LATIN SMALL LETTER A, TIBETAN ASTROLOGICAL SIGN SDONG TSHUGS, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 059A 0316 302A 0F35 0062;0061 302A 0316 0F35 059A 0062;0061 302A 0316 0F35 059A 0062;0061 302A 0316 0F35 059A 0062;0061 302A 0316 0F35 059A 0062; # (a◌֚◌̖◌〪◌༵b; a◌〪◌̖◌༵◌֚b; a◌〪◌̖◌༵◌֚b; a◌〪◌̖◌༵◌֚b; a◌〪◌̖◌༵◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, TIBETAN MARK NGAS BZUNG NYI ZLA, LATIN SMALL LETTER B
+0061 0F35 059A 0316 302A 0062;0061 302A 0F35 0316 059A 0062;0061 302A 0F35 0316 059A 0062;0061 302A 0F35 0316 059A 0062;0061 302A 0F35 0316 059A 0062; # (a◌༵◌֚◌̖◌〪b; a◌〪◌༵◌̖◌֚b; a◌〪◌༵◌̖◌֚b; a◌〪◌༵◌̖◌֚b; a◌〪◌༵◌̖◌֚b; ) LATIN SMALL LETTER A, TIBETAN MARK NGAS BZUNG NYI ZLA, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 059A 0316 302A 0F37 0062;0061 302A 0316 0F37 059A 0062;0061 302A 0316 0F37 059A 0062;0061 302A 0316 0F37 059A 0062;0061 302A 0316 0F37 059A 0062; # (a◌֚◌̖◌〪◌༷b; a◌〪◌̖◌༷◌֚b; a◌〪◌̖◌༷◌֚b; a◌〪◌̖◌༷◌֚b; a◌〪◌̖◌༷◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, TIBETAN MARK NGAS BZUNG SGOR RTAGS, LATIN SMALL LETTER B
+0061 0F37 059A 0316 302A 0062;0061 302A 0F37 0316 059A 0062;0061 302A 0F37 0316 059A 0062;0061 302A 0F37 0316 059A 0062;0061 302A 0F37 0316 059A 0062; # (a◌༷◌֚◌̖◌〪b; a◌〪◌༷◌̖◌֚b; a◌〪◌༷◌̖◌֚b; a◌〪◌༷◌̖◌֚b; a◌〪◌༷◌̖◌֚b; ) LATIN SMALL LETTER A, TIBETAN MARK NGAS BZUNG SGOR RTAGS, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 302A 031B 0321 0F39 0062;0061 0321 031B 0F39 302A 0062;0061 0321 031B 0F39 302A 0062;0061 0321 031B 0F39 302A 0062;0061 0321 031B 0F39 302A 0062; # (a◌〪◌̛◌̡◌༹b; a◌̡◌̛◌༹◌〪b; a◌̡◌̛◌༹◌〪b; a◌̡◌̛◌༹◌〪b; a◌̡◌̛◌༹◌〪b; ) LATIN SMALL LETTER A, IDEOGRAPHIC LEVEL TONE MARK, COMBINING HORN, COMBINING PALATALIZED HOOK BELOW, TIBETAN MARK TSA -PHRU, LATIN SMALL LETTER B
+0061 0F39 302A 031B 0321 0062;0061 0321 0F39 031B 302A 0062;0061 0321 0F39 031B 302A 0062;0061 0321 0F39 031B 302A 0062;0061 0321 0F39 031B 302A 0062; # (a◌༹◌〪◌̛◌̡b; a◌̡◌༹◌̛◌〪b; a◌̡◌༹◌̛◌〪b; a◌̡◌༹◌̛◌〪b; a◌̡◌༹◌̛◌〪b; ) LATIN SMALL LETTER A, TIBETAN MARK TSA -PHRU, IDEOGRAPHIC LEVEL TONE MARK, COMBINING HORN, COMBINING PALATALIZED HOOK BELOW, LATIN SMALL LETTER B
+0061 0F72 0F71 0EC8 0F71 0062;0061 0EC8 0F71 0F71 0F72 0062;0061 0EC8 0F71 0F71 0F72 0062;0061 0EC8 0F71 0F71 0F72 0062;0061 0EC8 0F71 0F71 0F72 0062; # (a◌ི◌ཱ◌່◌ཱb; a◌່◌ཱ◌ཱ◌ིb; a◌່◌ཱ◌ཱ◌ིb; a◌່◌ཱ◌ཱ◌ིb; a◌່◌ཱ◌ཱ◌ིb; ) LATIN SMALL LETTER A, TIBETAN VOWEL SIGN I, TIBETAN VOWEL SIGN AA, LAO TONE MAI EK, TIBETAN VOWEL SIGN AA, LATIN SMALL LETTER B
+0061 0F71 0F72 0F71 0EC8 0062;0061 0EC8 0F71 0F71 0F72 0062;0061 0EC8 0F71 0F71 0F72 0062;0061 0EC8 0F71 0F71 0F72 0062;0061 0EC8 0F71 0F71 0F72 0062; # (a◌ཱ◌ི◌ཱ◌່b; a◌່◌ཱ◌ཱ◌ིb; a◌່◌ཱ◌ཱ◌ིb; a◌່◌ཱ◌ཱ◌ིb; a◌່◌ཱ◌ཱ◌ིb; ) LATIN SMALL LETTER A, TIBETAN VOWEL SIGN AA, TIBETAN VOWEL SIGN I, TIBETAN VOWEL SIGN AA, LAO TONE MAI EK, LATIN SMALL LETTER B
+0061 0F74 0F72 0F71 0F72 0062;0061 0F71 0F72 0F72 0F74 0062;0061 0F71 0F72 0F72 0F74 0062;0061 0F71 0F72 0F72 0F74 0062;0061 0F71 0F72 0F72 0F74 0062; # (a◌ུ◌ི◌ཱ◌ིb; a◌ཱ◌ི◌ི◌ུb; a◌ཱ◌ི◌ི◌ུb; a◌ཱ◌ི◌ི◌ུb; a◌ཱ◌ི◌ི◌ུb; ) LATIN SMALL LETTER A, TIBETAN VOWEL SIGN U, TIBETAN VOWEL SIGN I, TIBETAN VOWEL SIGN AA, TIBETAN VOWEL SIGN I, LATIN SMALL LETTER B
+0061 0F72 0F74 0F72 0F71 0062;0061 0F71 0F72 0F72 0F74 0062;0061 0F71 0F72 0F72 0F74 0062;0061 0F71 0F72 0F72 0F74 0062;0061 0F71 0F72 0F72 0F74 0062; # (a◌ི◌ུ◌ི◌ཱb; a◌ཱ◌ི◌ི◌ུb; a◌ཱ◌ི◌ི◌ུb; a◌ཱ◌ི◌ི◌ུb; a◌ཱ◌ི◌ི◌ུb; ) LATIN SMALL LETTER A, TIBETAN VOWEL SIGN I, TIBETAN VOWEL SIGN U, TIBETAN VOWEL SIGN I, TIBETAN VOWEL SIGN AA, LATIN SMALL LETTER B
+0061 0321 0F74 0F72 0F74 0062;0061 0F72 0F74 0F74 0321 0062;0061 0F72 0F74 0F74 0321 0062;0061 0F72 0F74 0F74 0321 0062;0061 0F72 0F74 0F74 0321 0062; # (a◌̡◌ུ◌ི◌ུb; a◌ི◌ུ◌ུ◌̡b; a◌ི◌ུ◌ུ◌̡b; a◌ི◌ུ◌ུ◌̡b; a◌ི◌ུ◌ུ◌̡b; ) LATIN SMALL LETTER A, COMBINING PALATALIZED HOOK BELOW, TIBETAN VOWEL SIGN U, TIBETAN VOWEL SIGN I, TIBETAN VOWEL SIGN U, LATIN SMALL LETTER B
+0061 0F74 0321 0F74 0F72 0062;0061 0F72 0F74 0F74 0321 0062;0061 0F72 0F74 0F74 0321 0062;0061 0F72 0F74 0F74 0321 0062;0061 0F72 0F74 0F74 0321 0062; # (a◌ུ◌̡◌ུ◌ིb; a◌ི◌ུ◌ུ◌̡b; a◌ི◌ུ◌ུ◌̡b; a◌ི◌ུ◌ུ◌̡b; a◌ི◌ུ◌ུ◌̡b; ) LATIN SMALL LETTER A, TIBETAN VOWEL SIGN U, COMBINING PALATALIZED HOOK BELOW, TIBETAN VOWEL SIGN U, TIBETAN VOWEL SIGN I, LATIN SMALL LETTER B
+0061 0F74 0F72 0F71 0F7A 0062;0061 0F71 0F72 0F7A 0F74 0062;0061 0F71 0F72 0F7A 0F74 0062;0061 0F71 0F72 0F7A 0F74 0062;0061 0F71 0F72 0F7A 0F74 0062; # (a◌ུ◌ི◌ཱ◌ེb; a◌ཱ◌ི◌ེ◌ུb; a◌ཱ◌ི◌ེ◌ུb; a◌ཱ◌ི◌ེ◌ུb; a◌ཱ◌ི◌ེ◌ུb; ) LATIN SMALL LETTER A, TIBETAN VOWEL SIGN U, TIBETAN VOWEL SIGN I, TIBETAN VOWEL SIGN AA, TIBETAN VOWEL SIGN E, LATIN SMALL LETTER B
+0061 0F7A 0F74 0F72 0F71 0062;0061 0F71 0F7A 0F72 0F74 0062;0061 0F71 0F7A 0F72 0F74 0062;0061 0F71 0F7A 0F72 0F74 0062;0061 0F71 0F7A 0F72 0F74 0062; # (a◌ེ◌ུ◌ི◌ཱb; a◌ཱ◌ེ◌ི◌ུb; a◌ཱ◌ེ◌ི◌ུb; a◌ཱ◌ེ◌ི◌ུb; a◌ཱ◌ེ◌ི◌ུb; ) LATIN SMALL LETTER A, TIBETAN VOWEL SIGN E, TIBETAN VOWEL SIGN U, TIBETAN VOWEL SIGN I, TIBETAN VOWEL SIGN AA, LATIN SMALL LETTER B
+0061 0F74 0F72 0F71 0F7B 0062;0061 0F71 0F72 0F7B 0F74 0062;0061 0F71 0F72 0F7B 0F74 0062;0061 0F71 0F72 0F7B 0F74 0062;0061 0F71 0F72 0F7B 0F74 0062; # (a◌ུ◌ི◌ཱ◌ཻb; a◌ཱ◌ི◌ཻ◌ུb; a◌ཱ◌ི◌ཻ◌ུb; a◌ཱ◌ི◌ཻ◌ུb; a◌ཱ◌ི◌ཻ◌ུb; ) LATIN SMALL LETTER A, TIBETAN VOWEL SIGN U, TIBETAN VOWEL SIGN I, TIBETAN VOWEL SIGN AA, TIBETAN VOWEL SIGN EE, LATIN SMALL LETTER B
+0061 0F7B 0F74 0F72 0F71 0062;0061 0F71 0F7B 0F72 0F74 0062;0061 0F71 0F7B 0F72 0F74 0062;0061 0F71 0F7B 0F72 0F74 0062;0061 0F71 0F7B 0F72 0F74 0062; # (a◌ཻ◌ུ◌ི◌ཱb; a◌ཱ◌ཻ◌ི◌ུb; a◌ཱ◌ཻ◌ི◌ུb; a◌ཱ◌ཻ◌ི◌ུb; a◌ཱ◌ཻ◌ི◌ུb; ) LATIN SMALL LETTER A, TIBETAN VOWEL SIGN EE, TIBETAN VOWEL SIGN U, TIBETAN VOWEL SIGN I, TIBETAN VOWEL SIGN AA, LATIN SMALL LETTER B
+0061 0F74 0F72 0F71 0F7C 0062;0061 0F71 0F72 0F7C 0F74 0062;0061 0F71 0F72 0F7C 0F74 0062;0061 0F71 0F72 0F7C 0F74 0062;0061 0F71 0F72 0F7C 0F74 0062; # (a◌ུ◌ི◌ཱ◌ོb; a◌ཱ◌ི◌ོ◌ུb; a◌ཱ◌ི◌ོ◌ུb; a◌ཱ◌ི◌ོ◌ུb; a◌ཱ◌ི◌ོ◌ུb; ) LATIN SMALL LETTER A, TIBETAN VOWEL SIGN U, TIBETAN VOWEL SIGN I, TIBETAN VOWEL SIGN AA, TIBETAN VOWEL SIGN O, LATIN SMALL LETTER B
+0061 0F7C 0F74 0F72 0F71 0062;0061 0F71 0F7C 0F72 0F74 0062;0061 0F71 0F7C 0F72 0F74 0062;0061 0F71 0F7C 0F72 0F74 0062;0061 0F71 0F7C 0F72 0F74 0062; # (a◌ོ◌ུ◌ི◌ཱb; a◌ཱ◌ོ◌ི◌ུb; a◌ཱ◌ོ◌ི◌ུb; a◌ཱ◌ོ◌ི◌ུb; a◌ཱ◌ོ◌ི◌ུb; ) LATIN SMALL LETTER A, TIBETAN VOWEL SIGN O, TIBETAN VOWEL SIGN U, TIBETAN VOWEL SIGN I, TIBETAN VOWEL SIGN AA, LATIN SMALL LETTER B
+0061 0F74 0F72 0F71 0F7D 0062;0061 0F71 0F72 0F7D 0F74 0062;0061 0F71 0F72 0F7D 0F74 0062;0061 0F71 0F72 0F7D 0F74 0062;0061 0F71 0F72 0F7D 0F74 0062; # (a◌ུ◌ི◌ཱ◌ཽb; a◌ཱ◌ི◌ཽ◌ུb; a◌ཱ◌ི◌ཽ◌ུb; a◌ཱ◌ི◌ཽ◌ུb; a◌ཱ◌ི◌ཽ◌ུb; ) LATIN SMALL LETTER A, TIBETAN VOWEL SIGN U, TIBETAN VOWEL SIGN I, TIBETAN VOWEL SIGN AA, TIBETAN VOWEL SIGN OO, LATIN SMALL LETTER B
+0061 0F7D 0F74 0F72 0F71 0062;0061 0F71 0F7D 0F72 0F74 0062;0061 0F71 0F7D 0F72 0F74 0062;0061 0F71 0F7D 0F72 0F74 0062;0061 0F71 0F7D 0F72 0F74 0062; # (a◌ཽ◌ུ◌ི◌ཱb; a◌ཱ◌ཽ◌ི◌ུb; a◌ཱ◌ཽ◌ི◌ུb; a◌ཱ◌ཽ◌ི◌ུb; a◌ཱ◌ཽ◌ི◌ུb; ) LATIN SMALL LETTER A, TIBETAN VOWEL SIGN OO, TIBETAN VOWEL SIGN U, TIBETAN VOWEL SIGN I, TIBETAN VOWEL SIGN AA, LATIN SMALL LETTER B
+0061 0F74 0F72 0F71 0F80 0062;0061 0F71 0F72 0F80 0F74 0062;0061 0F71 0F72 0F80 0F74 0062;0061 0F71 0F72 0F80 0F74 0062;0061 0F71 0F72 0F80 0F74 0062; # (a◌ུ◌ི◌ཱ◌ྀb; a◌ཱ◌ི◌ྀ◌ུb; a◌ཱ◌ི◌ྀ◌ུb; a◌ཱ◌ི◌ྀ◌ུb; a◌ཱ◌ི◌ྀ◌ུb; ) LATIN SMALL LETTER A, TIBETAN VOWEL SIGN U, TIBETAN VOWEL SIGN I, TIBETAN VOWEL SIGN AA, TIBETAN VOWEL SIGN REVERSED I, LATIN SMALL LETTER B
+0061 0F80 0F74 0F72 0F71 0062;0061 0F71 0F80 0F72 0F74 0062;0061 0F71 0F80 0F72 0F74 0062;0061 0F71 0F80 0F72 0F74 0062;0061 0F71 0F80 0F72 0F74 0062; # (a◌ྀ◌ུ◌ི◌ཱb; a◌ཱ◌ྀ◌ི◌ུb; a◌ཱ◌ྀ◌ི◌ུb; a◌ཱ◌ྀ◌ི◌ུb; a◌ཱ◌ྀ◌ི◌ུb; ) LATIN SMALL LETTER A, TIBETAN VOWEL SIGN REVERSED I, TIBETAN VOWEL SIGN U, TIBETAN VOWEL SIGN I, TIBETAN VOWEL SIGN AA, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0F82 0062;00E0 05AE 0F82 0315 0062;0061 05AE 0300 0F82 0315 0062;00E0 05AE 0F82 0315 0062;0061 05AE 0300 0F82 0315 0062; # (a◌̕◌̀◌֮◌ྂb; à◌֮◌ྂ◌̕b; a◌֮◌̀◌ྂ◌̕b; à◌֮◌ྂ◌̕b; a◌֮◌̀◌ྂ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, TIBETAN SIGN NYI ZLA NAA DA, LATIN SMALL LETTER B
+0061 0F82 0315 0300 05AE 0062;0061 05AE 0F82 0300 0315 0062;0061 05AE 0F82 0300 0315 0062;0061 05AE 0F82 0300 0315 0062;0061 05AE 0F82 0300 0315 0062; # (a◌ྂ◌̕◌̀◌֮b; a◌֮◌ྂ◌̀◌̕b; a◌֮◌ྂ◌̀◌̕b; a◌֮◌ྂ◌̀◌̕b; a◌֮◌ྂ◌̀◌̕b; ) LATIN SMALL LETTER A, TIBETAN SIGN NYI ZLA NAA DA, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0F83 0062;00E0 05AE 0F83 0315 0062;0061 05AE 0300 0F83 0315 0062;00E0 05AE 0F83 0315 0062;0061 05AE 0300 0F83 0315 0062; # (a◌̕◌̀◌֮◌ྃb; à◌֮◌ྃ◌̕b; a◌֮◌̀◌ྃ◌̕b; à◌֮◌ྃ◌̕b; a◌֮◌̀◌ྃ◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, TIBETAN SIGN SNA LDAN, LATIN SMALL LETTER B
+0061 0F83 0315 0300 05AE 0062;0061 05AE 0F83 0300 0315 0062;0061 05AE 0F83 0300 0315 0062;0061 05AE 0F83 0300 0315 0062;0061 05AE 0F83 0300 0315 0062; # (a◌ྃ◌̕◌̀◌֮b; a◌֮◌ྃ◌̀◌̕b; a◌֮◌ྃ◌̀◌̕b; a◌֮◌ྃ◌̀◌̕b; a◌֮◌ྃ◌̀◌̕b; ) LATIN SMALL LETTER A, TIBETAN SIGN SNA LDAN, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 05B0 094D 3099 0F84 0062;0061 3099 094D 0F84 05B0 0062;0061 3099 094D 0F84 05B0 0062;0061 3099 094D 0F84 05B0 0062;0061 3099 094D 0F84 05B0 0062; # (a◌ְ◌à¥â—Œã‚™â—Œà¾„b; a◌゙◌à¥â—Œà¾„◌ְb; a◌゙◌à¥â—Œà¾„◌ְb; a◌゙◌à¥â—Œà¾„◌ְb; a◌゙◌à¥â—Œà¾„◌ְb; ) LATIN SMALL LETTER A, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, TIBETAN MARK HALANTA, LATIN SMALL LETTER B
+0061 0F84 05B0 094D 3099 0062;0061 3099 0F84 094D 05B0 0062;0061 3099 0F84 094D 05B0 0062;0061 3099 0F84 094D 05B0 0062;0061 3099 0F84 094D 05B0 0062; # (a◌྄◌ְ◌à¥â—Œã‚™b; a◌゙◌྄◌à¥â—ŒÖ°b; a◌゙◌྄◌à¥â—ŒÖ°b; a◌゙◌྄◌à¥â—ŒÖ°b; a◌゙◌྄◌à¥â—ŒÖ°b; ) LATIN SMALL LETTER A, TIBETAN MARK HALANTA, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0F86 0062;00E0 05AE 0F86 0315 0062;0061 05AE 0300 0F86 0315 0062;00E0 05AE 0F86 0315 0062;0061 05AE 0300 0F86 0315 0062; # (a◌̕◌̀◌֮◌྆b; à◌֮◌྆◌̕b; a◌֮◌̀◌྆◌̕b; à◌֮◌྆◌̕b; a◌֮◌̀◌྆◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, TIBETAN SIGN LCI RTAGS, LATIN SMALL LETTER B
+0061 0F86 0315 0300 05AE 0062;0061 05AE 0F86 0300 0315 0062;0061 05AE 0F86 0300 0315 0062;0061 05AE 0F86 0300 0315 0062;0061 05AE 0F86 0300 0315 0062; # (a◌྆◌̕◌̀◌֮b; a◌֮◌྆◌̀◌̕b; a◌֮◌྆◌̀◌̕b; a◌֮◌྆◌̀◌̕b; a◌֮◌྆◌̀◌̕b; ) LATIN SMALL LETTER A, TIBETAN SIGN LCI RTAGS, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 0F87 0062;00E0 05AE 0F87 0315 0062;0061 05AE 0300 0F87 0315 0062;00E0 05AE 0F87 0315 0062;0061 05AE 0300 0F87 0315 0062; # (a◌̕◌̀◌֮◌྇b; à◌֮◌྇◌̕b; a◌֮◌̀◌྇◌̕b; à◌֮◌྇◌̕b; a◌֮◌̀◌྇◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, TIBETAN SIGN YANG RTAGS, LATIN SMALL LETTER B
+0061 0F87 0315 0300 05AE 0062;0061 05AE 0F87 0300 0315 0062;0061 05AE 0F87 0300 0315 0062;0061 05AE 0F87 0300 0315 0062;0061 05AE 0F87 0300 0315 0062; # (a◌྇◌̕◌̀◌֮b; a◌֮◌྇◌̀◌̕b; a◌֮◌྇◌̀◌̕b; a◌֮◌྇◌̀◌̕b; a◌֮◌྇◌̀◌̕b; ) LATIN SMALL LETTER A, TIBETAN SIGN YANG RTAGS, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 059A 0316 302A 0FC6 0062;0061 302A 0316 0FC6 059A 0062;0061 302A 0316 0FC6 059A 0062;0061 302A 0316 0FC6 059A 0062;0061 302A 0316 0FC6 059A 0062; # (a◌֚◌̖◌〪◌࿆b; a◌〪◌̖◌࿆◌֚b; a◌〪◌̖◌࿆◌֚b; a◌〪◌̖◌࿆◌֚b; a◌〪◌̖◌࿆◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, TIBETAN SYMBOL PADMA GDAN, LATIN SMALL LETTER B
+0061 0FC6 059A 0316 302A 0062;0061 302A 0FC6 0316 059A 0062;0061 302A 0FC6 0316 059A 0062;0061 302A 0FC6 0316 059A 0062;0061 302A 0FC6 0316 059A 0062; # (a◌࿆◌֚◌̖◌〪b; a◌〪◌࿆◌̖◌֚b; a◌〪◌࿆◌̖◌֚b; a◌〪◌࿆◌̖◌֚b; a◌〪◌࿆◌̖◌֚b; ) LATIN SMALL LETTER A, TIBETAN SYMBOL PADMA GDAN, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 3099 093C 0334 1037 0062;0061 0334 093C 1037 3099 0062;0061 0334 093C 1037 3099 0062;0061 0334 093C 1037 3099 0062;0061 0334 093C 1037 3099 0062; # (a◌゙◌़◌̴◌့b; a◌̴◌़◌့◌゙b; a◌̴◌़◌့◌゙b; a◌̴◌़◌့◌゙b; a◌̴◌़◌့◌゙b; ) LATIN SMALL LETTER A, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, MYANMAR SIGN DOT BELOW, LATIN SMALL LETTER B
+0061 1037 3099 093C 0334 0062;0061 0334 1037 093C 3099 0062;0061 0334 1037 093C 3099 0062;0061 0334 1037 093C 3099 0062;0061 0334 1037 093C 3099 0062; # (a◌့◌゙◌़◌̴b; a◌̴◌့◌़◌゙b; a◌̴◌့◌़◌゙b; a◌̴◌့◌़◌゙b; a◌̴◌့◌़◌゙b; ) LATIN SMALL LETTER A, MYANMAR SIGN DOT BELOW, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, LATIN SMALL LETTER B
+0061 05B0 094D 3099 1039 0062;0061 3099 094D 1039 05B0 0062;0061 3099 094D 1039 05B0 0062;0061 3099 094D 1039 05B0 0062;0061 3099 094D 1039 05B0 0062; # (a◌ְ◌à¥â—Œã‚™â—Œá€¹b; a◌゙◌à¥â—Œá€¹â—ŒÖ°b; a◌゙◌à¥â—Œá€¹â—ŒÖ°b; a◌゙◌à¥â—Œá€¹â—ŒÖ°b; a◌゙◌à¥â—Œá€¹â—ŒÖ°b; ) LATIN SMALL LETTER A, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, MYANMAR SIGN VIRAMA, LATIN SMALL LETTER B
+0061 1039 05B0 094D 3099 0062;0061 3099 1039 094D 05B0 0062;0061 3099 1039 094D 05B0 0062;0061 3099 1039 094D 05B0 0062;0061 3099 1039 094D 05B0 0062; # (a◌္◌ְ◌à¥â—Œã‚™b; a◌゙◌္◌à¥â—ŒÖ°b; a◌゙◌္◌à¥â—ŒÖ°b; a◌゙◌္◌à¥â—ŒÖ°b; a◌゙◌္◌à¥â—ŒÖ°b; ) LATIN SMALL LETTER A, MYANMAR SIGN VIRAMA, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, LATIN SMALL LETTER B
+0061 05B0 094D 3099 1714 0062;0061 3099 094D 1714 05B0 0062;0061 3099 094D 1714 05B0 0062;0061 3099 094D 1714 05B0 0062;0061 3099 094D 1714 05B0 0062; # (a◌ְ◌à¥â—Œã‚™â—Œáœ”b; a◌゙◌à¥â—Œáœ”◌ְb; a◌゙◌à¥â—Œáœ”◌ְb; a◌゙◌à¥â—Œáœ”◌ְb; a◌゙◌à¥â—Œáœ”◌ְb; ) LATIN SMALL LETTER A, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, TAGALOG SIGN VIRAMA, LATIN SMALL LETTER B
+0061 1714 05B0 094D 3099 0062;0061 3099 1714 094D 05B0 0062;0061 3099 1714 094D 05B0 0062;0061 3099 1714 094D 05B0 0062;0061 3099 1714 094D 05B0 0062; # (a◌᜔◌ְ◌à¥â—Œã‚™b; a◌゙◌᜔◌à¥â—ŒÖ°b; a◌゙◌᜔◌à¥â—ŒÖ°b; a◌゙◌᜔◌à¥â—ŒÖ°b; a◌゙◌᜔◌à¥â—ŒÖ°b; ) LATIN SMALL LETTER A, TAGALOG SIGN VIRAMA, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, LATIN SMALL LETTER B
+0061 05B0 094D 3099 1734 0062;0061 3099 094D 1734 05B0 0062;0061 3099 094D 1734 05B0 0062;0061 3099 094D 1734 05B0 0062;0061 3099 094D 1734 05B0 0062; # (a◌ְ◌à¥â—Œã‚™â—Œáœ´b; a◌゙◌à¥â—Œáœ´â—ŒÖ°b; a◌゙◌à¥â—Œáœ´â—ŒÖ°b; a◌゙◌à¥â—Œáœ´â—ŒÖ°b; a◌゙◌à¥â—Œáœ´â—ŒÖ°b; ) LATIN SMALL LETTER A, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, HANUNOO SIGN PAMUDPOD, LATIN SMALL LETTER B
+0061 1734 05B0 094D 3099 0062;0061 3099 1734 094D 05B0 0062;0061 3099 1734 094D 05B0 0062;0061 3099 1734 094D 05B0 0062;0061 3099 1734 094D 05B0 0062; # (a◌᜴◌ְ◌à¥â—Œã‚™b; a◌゙◌᜴◌à¥â—ŒÖ°b; a◌゙◌᜴◌à¥â—ŒÖ°b; a◌゙◌᜴◌à¥â—ŒÖ°b; a◌゙◌᜴◌à¥â—ŒÖ°b; ) LATIN SMALL LETTER A, HANUNOO SIGN PAMUDPOD, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, LATIN SMALL LETTER B
+0061 05B0 094D 3099 17D2 0062;0061 3099 094D 17D2 05B0 0062;0061 3099 094D 17D2 05B0 0062;0061 3099 094D 17D2 05B0 0062;0061 3099 094D 17D2 05B0 0062; # (a◌ְ◌à¥â—Œã‚™â—ŒáŸ’b; a◌゙◌à¥â—ŒáŸ’◌ְb; a◌゙◌à¥â—ŒáŸ’◌ְb; a◌゙◌à¥â—ŒáŸ’◌ְb; a◌゙◌à¥â—ŒáŸ’◌ְb; ) LATIN SMALL LETTER A, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, KHMER SIGN COENG, LATIN SMALL LETTER B
+0061 17D2 05B0 094D 3099 0062;0061 3099 17D2 094D 05B0 0062;0061 3099 17D2 094D 05B0 0062;0061 3099 17D2 094D 05B0 0062;0061 3099 17D2 094D 05B0 0062; # (a◌្◌ְ◌à¥â—Œã‚™b; a◌゙◌្◌à¥â—ŒÖ°b; a◌゙◌្◌à¥â—ŒÖ°b; a◌゙◌្◌à¥â—ŒÖ°b; a◌゙◌្◌à¥â—ŒÖ°b; ) LATIN SMALL LETTER A, KHMER SIGN COENG, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, LATIN SMALL LETTER B
+0061 0315 0300 05AE 17DD 0062;00E0 05AE 17DD 0315 0062;0061 05AE 0300 17DD 0315 0062;00E0 05AE 17DD 0315 0062;0061 05AE 0300 17DD 0315 0062; # (a◌̕◌̀◌֮◌áŸb; à◌֮◌áŸâ—ŒÌ•b; a◌֮◌̀◌áŸâ—ŒÌ•b; à◌֮◌áŸâ—ŒÌ•b; a◌֮◌̀◌áŸâ—ŒÌ•b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, KHMER SIGN ATTHACAN, LATIN SMALL LETTER B
+0061 17DD 0315 0300 05AE 0062;0061 05AE 17DD 0300 0315 0062;0061 05AE 17DD 0300 0315 0062;0061 05AE 17DD 0300 0315 0062;0061 05AE 17DD 0300 0315 0062; # (aâ—ŒáŸâ—ŒÌ•◌̀◌֮b; a◌֮◌áŸâ—ŒÌ€â—ŒÌ•b; a◌֮◌áŸâ—ŒÌ€â—ŒÌ•b; a◌֮◌áŸâ—ŒÌ€â—ŒÌ•b; a◌֮◌áŸâ—ŒÌ€â—ŒÌ•b; ) LATIN SMALL LETTER A, KHMER SIGN ATTHACAN, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0300 05AE 1D16D 18A9 0062;00E0 1D16D 05AE 18A9 0062;0061 1D16D 05AE 18A9 0300 0062;00E0 1D16D 05AE 18A9 0062;0061 1D16D 05AE 18A9 0300 0062; # (a◌̀◌֮ð…­ð…­â—Œá¢©b; àð…­ð…­â—ŒÖ®â—Œá¢©b; að…­ð…­â—ŒÖ®â—Œá¢©â—ŒÌ€b; àð…­ð…­â—ŒÖ®â—Œá¢©b; að…­ð…­â—ŒÖ®â—Œá¢©â—ŒÌ€b; ) LATIN SMALL LETTER A, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, MUSICAL SYMBOL COMBINING AUGMENTATION DOT, MONGOLIAN LETTER ALI GALI DAGALGA, LATIN SMALL LETTER B
+0061 18A9 0300 05AE 1D16D 0062;00E0 1D16D 18A9 05AE 0062;0061 1D16D 18A9 05AE 0300 0062;00E0 1D16D 18A9 05AE 0062;0061 1D16D 18A9 05AE 0300 0062; # (a◌ᢩ◌̀◌֮ð…­ð…­b; àð…­ð…­â—Œá¢©â—ŒÖ®b; að…­ð…­â—Œá¢©â—ŒÖ®â—ŒÌ€b; àð…­ð…­â—Œá¢©â—ŒÖ®b; að…­ð…­â—Œá¢©â—ŒÖ®â—ŒÌ€b; ) LATIN SMALL LETTER A, MONGOLIAN LETTER ALI GALI DAGALGA, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, MUSICAL SYMBOL COMBINING AUGMENTATION DOT, LATIN SMALL LETTER B
+0061 302E 059A 0316 1939 0062;0061 0316 059A 1939 302E 0062;0061 0316 059A 1939 302E 0062;0061 0316 059A 1939 302E 0062;0061 0316 059A 1939 302E 0062; # (a◌〮◌֚◌̖◌᤹b; a◌̖◌֚◌᤹◌〮b; a◌̖◌֚◌᤹◌〮b; a◌̖◌֚◌᤹◌〮b; a◌̖◌֚◌᤹◌〮b; ) LATIN SMALL LETTER A, HANGUL SINGLE DOT TONE MARK, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, LIMBU SIGN MUKPHRENG, LATIN SMALL LETTER B
+0061 1939 302E 059A 0316 0062;0061 0316 1939 059A 302E 0062;0061 0316 1939 059A 302E 0062;0061 0316 1939 059A 302E 0062;0061 0316 1939 059A 302E 0062; # (a◌᤹◌〮◌֚◌̖b; a◌̖◌᤹◌֚◌〮b; a◌̖◌᤹◌֚◌〮b; a◌̖◌᤹◌֚◌〮b; a◌̖◌᤹◌֚◌〮b; ) LATIN SMALL LETTER A, LIMBU SIGN MUKPHRENG, HANGUL SINGLE DOT TONE MARK, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, LATIN SMALL LETTER B
+0061 0315 0300 05AE 193A 0062;00E0 05AE 193A 0315 0062;0061 05AE 0300 193A 0315 0062;00E0 05AE 193A 0315 0062;0061 05AE 0300 193A 0315 0062; # (a◌̕◌̀◌֮◌᤺b; à◌֮◌᤺◌̕b; a◌֮◌̀◌᤺◌̕b; à◌֮◌᤺◌̕b; a◌֮◌̀◌᤺◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LIMBU SIGN KEMPHRENG, LATIN SMALL LETTER B
+0061 193A 0315 0300 05AE 0062;0061 05AE 193A 0300 0315 0062;0061 05AE 193A 0300 0315 0062;0061 05AE 193A 0300 0315 0062;0061 05AE 193A 0300 0315 0062; # (a◌᤺◌̕◌̀◌֮b; a◌֮◌᤺◌̀◌̕b; a◌֮◌᤺◌̀◌̕b; a◌֮◌᤺◌̀◌̕b; a◌֮◌᤺◌̀◌̕b; ) LATIN SMALL LETTER A, LIMBU SIGN KEMPHRENG, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 059A 0316 302A 193B 0062;0061 302A 0316 193B 059A 0062;0061 302A 0316 193B 059A 0062;0061 302A 0316 193B 059A 0062;0061 302A 0316 193B 059A 0062; # (a◌֚◌̖◌〪◌᤻b; a◌〪◌̖◌᤻◌֚b; a◌〪◌̖◌᤻◌֚b; a◌〪◌̖◌᤻◌֚b; a◌〪◌̖◌᤻◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LIMBU SIGN SA-I, LATIN SMALL LETTER B
+0061 193B 059A 0316 302A 0062;0061 302A 193B 0316 059A 0062;0061 302A 193B 0316 059A 0062;0061 302A 193B 0316 059A 0062;0061 302A 193B 0316 059A 0062; # (a◌᤻◌֚◌̖◌〪b; a◌〪◌᤻◌̖◌֚b; a◌〪◌᤻◌̖◌֚b; a◌〪◌᤻◌̖◌֚b; a◌〪◌᤻◌̖◌֚b; ) LATIN SMALL LETTER A, LIMBU SIGN SA-I, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 0315 0300 05AE 20D0 0062;00E0 05AE 20D0 0315 0062;0061 05AE 0300 20D0 0315 0062;00E0 05AE 20D0 0315 0062;0061 05AE 0300 20D0 0315 0062; # (a◌̕◌̀◌֮◌âƒb; à◌֮◌âƒâ—ŒÌ•b; a◌֮◌̀◌âƒâ—ŒÌ•b; à◌֮◌âƒâ—ŒÌ•b; a◌֮◌̀◌âƒâ—ŒÌ•b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING LEFT HARPOON ABOVE, LATIN SMALL LETTER B
+0061 20D0 0315 0300 05AE 0062;0061 05AE 20D0 0300 0315 0062;0061 05AE 20D0 0300 0315 0062;0061 05AE 20D0 0300 0315 0062;0061 05AE 20D0 0300 0315 0062; # (aâ—Œâƒâ—ŒÌ•◌̀◌֮b; a◌֮◌âƒâ—ŒÌ€â—ŒÌ•b; a◌֮◌âƒâ—ŒÌ€â—ŒÌ•b; a◌֮◌âƒâ—ŒÌ€â—ŒÌ•b; a◌֮◌âƒâ—ŒÌ€â—ŒÌ•b; ) LATIN SMALL LETTER A, COMBINING LEFT HARPOON ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 20D1 0062;00E0 05AE 20D1 0315 0062;0061 05AE 0300 20D1 0315 0062;00E0 05AE 20D1 0315 0062;0061 05AE 0300 20D1 0315 0062; # (a◌̕◌̀◌֮◌⃑b; à◌֮◌⃑◌̕b; a◌֮◌̀◌⃑◌̕b; à◌֮◌⃑◌̕b; a◌֮◌̀◌⃑◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING RIGHT HARPOON ABOVE, LATIN SMALL LETTER B
+0061 20D1 0315 0300 05AE 0062;0061 05AE 20D1 0300 0315 0062;0061 05AE 20D1 0300 0315 0062;0061 05AE 20D1 0300 0315 0062;0061 05AE 20D1 0300 0315 0062; # (a◌⃑◌̕◌̀◌֮b; a◌֮◌⃑◌̀◌̕b; a◌֮◌⃑◌̀◌̕b; a◌֮◌⃑◌̀◌̕b; a◌֮◌⃑◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING RIGHT HARPOON ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 093C 0334 20D2 0062;0061 0334 20D2 093C 0062;0061 0334 20D2 093C 0062;0061 0334 20D2 093C 0062;0061 0334 20D2 093C 0062; # (a◌़◌̴◌⃒b; a◌̴◌⃒◌़b; a◌̴◌⃒◌़b; a◌̴◌⃒◌़b; a◌̴◌⃒◌़b; ) LATIN SMALL LETTER A, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, COMBINING LONG VERTICAL LINE OVERLAY, LATIN SMALL LETTER B
+0061 20D2 093C 0334 0062;0061 20D2 0334 093C 0062;0061 20D2 0334 093C 0062;0061 20D2 0334 093C 0062;0061 20D2 0334 093C 0062; # (a◌⃒◌़◌̴b; a◌⃒◌̴◌़b; a◌⃒◌̴◌़b; a◌⃒◌̴◌़b; a◌⃒◌̴◌़b; ) LATIN SMALL LETTER A, COMBINING LONG VERTICAL LINE OVERLAY, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, LATIN SMALL LETTER B
+0061 093C 0334 20D3 0062;0061 0334 20D3 093C 0062;0061 0334 20D3 093C 0062;0061 0334 20D3 093C 0062;0061 0334 20D3 093C 0062; # (a◌़◌̴◌⃓b; a◌̴◌⃓◌़b; a◌̴◌⃓◌़b; a◌̴◌⃓◌़b; a◌̴◌⃓◌़b; ) LATIN SMALL LETTER A, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, COMBINING SHORT VERTICAL LINE OVERLAY, LATIN SMALL LETTER B
+0061 20D3 093C 0334 0062;0061 20D3 0334 093C 0062;0061 20D3 0334 093C 0062;0061 20D3 0334 093C 0062;0061 20D3 0334 093C 0062; # (a◌⃓◌़◌̴b; a◌⃓◌̴◌़b; a◌⃓◌̴◌़b; a◌⃓◌̴◌़b; a◌⃓◌̴◌़b; ) LATIN SMALL LETTER A, COMBINING SHORT VERTICAL LINE OVERLAY, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, LATIN SMALL LETTER B
+0061 0315 0300 05AE 20D4 0062;00E0 05AE 20D4 0315 0062;0061 05AE 0300 20D4 0315 0062;00E0 05AE 20D4 0315 0062;0061 05AE 0300 20D4 0315 0062; # (a◌̕◌̀◌֮◌⃔b; à◌֮◌⃔◌̕b; a◌֮◌̀◌⃔◌̕b; à◌֮◌⃔◌̕b; a◌֮◌̀◌⃔◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING ANTICLOCKWISE ARROW ABOVE, LATIN SMALL LETTER B
+0061 20D4 0315 0300 05AE 0062;0061 05AE 20D4 0300 0315 0062;0061 05AE 20D4 0300 0315 0062;0061 05AE 20D4 0300 0315 0062;0061 05AE 20D4 0300 0315 0062; # (a◌⃔◌̕◌̀◌֮b; a◌֮◌⃔◌̀◌̕b; a◌֮◌⃔◌̀◌̕b; a◌֮◌⃔◌̀◌̕b; a◌֮◌⃔◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING ANTICLOCKWISE ARROW ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 20D5 0062;00E0 05AE 20D5 0315 0062;0061 05AE 0300 20D5 0315 0062;00E0 05AE 20D5 0315 0062;0061 05AE 0300 20D5 0315 0062; # (a◌̕◌̀◌֮◌⃕b; à◌֮◌⃕◌̕b; a◌֮◌̀◌⃕◌̕b; à◌֮◌⃕◌̕b; a◌֮◌̀◌⃕◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING CLOCKWISE ARROW ABOVE, LATIN SMALL LETTER B
+0061 20D5 0315 0300 05AE 0062;0061 05AE 20D5 0300 0315 0062;0061 05AE 20D5 0300 0315 0062;0061 05AE 20D5 0300 0315 0062;0061 05AE 20D5 0300 0315 0062; # (a◌⃕◌̕◌̀◌֮b; a◌֮◌⃕◌̀◌̕b; a◌֮◌⃕◌̀◌̕b; a◌֮◌⃕◌̀◌̕b; a◌֮◌⃕◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING CLOCKWISE ARROW ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 20D6 0062;00E0 05AE 20D6 0315 0062;0061 05AE 0300 20D6 0315 0062;00E0 05AE 20D6 0315 0062;0061 05AE 0300 20D6 0315 0062; # (a◌̕◌̀◌֮◌⃖b; à◌֮◌⃖◌̕b; a◌֮◌̀◌⃖◌̕b; à◌֮◌⃖◌̕b; a◌֮◌̀◌⃖◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING LEFT ARROW ABOVE, LATIN SMALL LETTER B
+0061 20D6 0315 0300 05AE 0062;0061 05AE 20D6 0300 0315 0062;0061 05AE 20D6 0300 0315 0062;0061 05AE 20D6 0300 0315 0062;0061 05AE 20D6 0300 0315 0062; # (a◌⃖◌̕◌̀◌֮b; a◌֮◌⃖◌̀◌̕b; a◌֮◌⃖◌̀◌̕b; a◌֮◌⃖◌̀◌̕b; a◌֮◌⃖◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING LEFT ARROW ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 20D7 0062;00E0 05AE 20D7 0315 0062;0061 05AE 0300 20D7 0315 0062;00E0 05AE 20D7 0315 0062;0061 05AE 0300 20D7 0315 0062; # (a◌̕◌̀◌֮◌⃗b; à◌֮◌⃗◌̕b; a◌֮◌̀◌⃗◌̕b; à◌֮◌⃗◌̕b; a◌֮◌̀◌⃗◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING RIGHT ARROW ABOVE, LATIN SMALL LETTER B
+0061 20D7 0315 0300 05AE 0062;0061 05AE 20D7 0300 0315 0062;0061 05AE 20D7 0300 0315 0062;0061 05AE 20D7 0300 0315 0062;0061 05AE 20D7 0300 0315 0062; # (a◌⃗◌̕◌̀◌֮b; a◌֮◌⃗◌̀◌̕b; a◌֮◌⃗◌̀◌̕b; a◌֮◌⃗◌̀◌̕b; a◌֮◌⃗◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING RIGHT ARROW ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 093C 0334 20D8 0062;0061 0334 20D8 093C 0062;0061 0334 20D8 093C 0062;0061 0334 20D8 093C 0062;0061 0334 20D8 093C 0062; # (a◌़◌̴◌⃘b; a◌̴◌⃘◌़b; a◌̴◌⃘◌़b; a◌̴◌⃘◌़b; a◌̴◌⃘◌़b; ) LATIN SMALL LETTER A, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, COMBINING RING OVERLAY, LATIN SMALL LETTER B
+0061 20D8 093C 0334 0062;0061 20D8 0334 093C 0062;0061 20D8 0334 093C 0062;0061 20D8 0334 093C 0062;0061 20D8 0334 093C 0062; # (a◌⃘◌़◌̴b; a◌⃘◌̴◌़b; a◌⃘◌̴◌़b; a◌⃘◌̴◌़b; a◌⃘◌̴◌़b; ) LATIN SMALL LETTER A, COMBINING RING OVERLAY, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, LATIN SMALL LETTER B
+0061 093C 0334 20D9 0062;0061 0334 20D9 093C 0062;0061 0334 20D9 093C 0062;0061 0334 20D9 093C 0062;0061 0334 20D9 093C 0062; # (a◌़◌̴◌⃙b; a◌̴◌⃙◌़b; a◌̴◌⃙◌़b; a◌̴◌⃙◌़b; a◌̴◌⃙◌़b; ) LATIN SMALL LETTER A, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, COMBINING CLOCKWISE RING OVERLAY, LATIN SMALL LETTER B
+0061 20D9 093C 0334 0062;0061 20D9 0334 093C 0062;0061 20D9 0334 093C 0062;0061 20D9 0334 093C 0062;0061 20D9 0334 093C 0062; # (a◌⃙◌़◌̴b; a◌⃙◌̴◌़b; a◌⃙◌̴◌़b; a◌⃙◌̴◌़b; a◌⃙◌̴◌़b; ) LATIN SMALL LETTER A, COMBINING CLOCKWISE RING OVERLAY, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, LATIN SMALL LETTER B
+0061 093C 0334 20DA 0062;0061 0334 20DA 093C 0062;0061 0334 20DA 093C 0062;0061 0334 20DA 093C 0062;0061 0334 20DA 093C 0062; # (a◌़◌̴◌⃚b; a◌̴◌⃚◌़b; a◌̴◌⃚◌़b; a◌̴◌⃚◌़b; a◌̴◌⃚◌़b; ) LATIN SMALL LETTER A, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, COMBINING ANTICLOCKWISE RING OVERLAY, LATIN SMALL LETTER B
+0061 20DA 093C 0334 0062;0061 20DA 0334 093C 0062;0061 20DA 0334 093C 0062;0061 20DA 0334 093C 0062;0061 20DA 0334 093C 0062; # (a◌⃚◌़◌̴b; a◌⃚◌̴◌़b; a◌⃚◌̴◌़b; a◌⃚◌̴◌़b; a◌⃚◌̴◌़b; ) LATIN SMALL LETTER A, COMBINING ANTICLOCKWISE RING OVERLAY, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, LATIN SMALL LETTER B
+0061 0315 0300 05AE 20DB 0062;00E0 05AE 20DB 0315 0062;0061 05AE 0300 20DB 0315 0062;00E0 05AE 20DB 0315 0062;0061 05AE 0300 20DB 0315 0062; # (a◌̕◌̀◌֮◌⃛b; à◌֮◌⃛◌̕b; a◌֮◌̀◌⃛◌̕b; à◌֮◌⃛◌̕b; a◌֮◌̀◌⃛◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING THREE DOTS ABOVE, LATIN SMALL LETTER B
+0061 20DB 0315 0300 05AE 0062;0061 05AE 20DB 0300 0315 0062;0061 05AE 20DB 0300 0315 0062;0061 05AE 20DB 0300 0315 0062;0061 05AE 20DB 0300 0315 0062; # (a◌⃛◌̕◌̀◌֮b; a◌֮◌⃛◌̀◌̕b; a◌֮◌⃛◌̀◌̕b; a◌֮◌⃛◌̀◌̕b; a◌֮◌⃛◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING THREE DOTS ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 20DC 0062;00E0 05AE 20DC 0315 0062;0061 05AE 0300 20DC 0315 0062;00E0 05AE 20DC 0315 0062;0061 05AE 0300 20DC 0315 0062; # (a◌̕◌̀◌֮◌⃜b; à◌֮◌⃜◌̕b; a◌֮◌̀◌⃜◌̕b; à◌֮◌⃜◌̕b; a◌֮◌̀◌⃜◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING FOUR DOTS ABOVE, LATIN SMALL LETTER B
+0061 20DC 0315 0300 05AE 0062;0061 05AE 20DC 0300 0315 0062;0061 05AE 20DC 0300 0315 0062;0061 05AE 20DC 0300 0315 0062;0061 05AE 20DC 0300 0315 0062; # (a◌⃜◌̕◌̀◌֮b; a◌֮◌⃜◌̀◌̕b; a◌֮◌⃜◌̀◌̕b; a◌֮◌⃜◌̀◌̕b; a◌֮◌⃜◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING FOUR DOTS ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 20E1 0062;00E0 05AE 20E1 0315 0062;0061 05AE 0300 20E1 0315 0062;00E0 05AE 20E1 0315 0062;0061 05AE 0300 20E1 0315 0062; # (a◌̕◌̀◌֮◌⃡b; à◌֮◌⃡◌̕b; a◌֮◌̀◌⃡◌̕b; à◌֮◌⃡◌̕b; a◌֮◌̀◌⃡◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING LEFT RIGHT ARROW ABOVE, LATIN SMALL LETTER B
+0061 20E1 0315 0300 05AE 0062;0061 05AE 20E1 0300 0315 0062;0061 05AE 20E1 0300 0315 0062;0061 05AE 20E1 0300 0315 0062;0061 05AE 20E1 0300 0315 0062; # (a◌⃡◌̕◌̀◌֮b; a◌֮◌⃡◌̀◌̕b; a◌֮◌⃡◌̀◌̕b; a◌֮◌⃡◌̀◌̕b; a◌֮◌⃡◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING LEFT RIGHT ARROW ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 093C 0334 20E5 0062;0061 0334 20E5 093C 0062;0061 0334 20E5 093C 0062;0061 0334 20E5 093C 0062;0061 0334 20E5 093C 0062; # (a◌़◌̴◌⃥b; a◌̴◌⃥◌़b; a◌̴◌⃥◌़b; a◌̴◌⃥◌़b; a◌̴◌⃥◌़b; ) LATIN SMALL LETTER A, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, COMBINING REVERSE SOLIDUS OVERLAY, LATIN SMALL LETTER B
+0061 20E5 093C 0334 0062;0061 20E5 0334 093C 0062;0061 20E5 0334 093C 0062;0061 20E5 0334 093C 0062;0061 20E5 0334 093C 0062; # (a◌⃥◌़◌̴b; a◌⃥◌̴◌़b; a◌⃥◌̴◌़b; a◌⃥◌̴◌़b; a◌⃥◌̴◌़b; ) LATIN SMALL LETTER A, COMBINING REVERSE SOLIDUS OVERLAY, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, LATIN SMALL LETTER B
+0061 093C 0334 20E6 0062;0061 0334 20E6 093C 0062;0061 0334 20E6 093C 0062;0061 0334 20E6 093C 0062;0061 0334 20E6 093C 0062; # (a◌़◌̴◌⃦b; a◌̴◌⃦◌़b; a◌̴◌⃦◌़b; a◌̴◌⃦◌़b; a◌̴◌⃦◌़b; ) LATIN SMALL LETTER A, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, COMBINING DOUBLE VERTICAL STROKE OVERLAY, LATIN SMALL LETTER B
+0061 20E6 093C 0334 0062;0061 20E6 0334 093C 0062;0061 20E6 0334 093C 0062;0061 20E6 0334 093C 0062;0061 20E6 0334 093C 0062; # (a◌⃦◌़◌̴b; a◌⃦◌̴◌़b; a◌⃦◌̴◌़b; a◌⃦◌̴◌़b; a◌⃦◌̴◌़b; ) LATIN SMALL LETTER A, COMBINING DOUBLE VERTICAL STROKE OVERLAY, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, LATIN SMALL LETTER B
+0061 0315 0300 05AE 20E7 0062;00E0 05AE 20E7 0315 0062;0061 05AE 0300 20E7 0315 0062;00E0 05AE 20E7 0315 0062;0061 05AE 0300 20E7 0315 0062; # (a◌̕◌̀◌֮◌⃧b; à◌֮◌⃧◌̕b; a◌֮◌̀◌⃧◌̕b; à◌֮◌⃧◌̕b; a◌֮◌̀◌⃧◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING ANNUITY SYMBOL, LATIN SMALL LETTER B
+0061 20E7 0315 0300 05AE 0062;0061 05AE 20E7 0300 0315 0062;0061 05AE 20E7 0300 0315 0062;0061 05AE 20E7 0300 0315 0062;0061 05AE 20E7 0300 0315 0062; # (a◌⃧◌̕◌̀◌֮b; a◌֮◌⃧◌̀◌̕b; a◌֮◌⃧◌̀◌̕b; a◌֮◌⃧◌̀◌̕b; a◌֮◌⃧◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING ANNUITY SYMBOL, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 059A 0316 302A 20E8 0062;0061 302A 0316 20E8 059A 0062;0061 302A 0316 20E8 059A 0062;0061 302A 0316 20E8 059A 0062;0061 302A 0316 20E8 059A 0062; # (a◌֚◌̖◌〪◌⃨b; a◌〪◌̖◌⃨◌֚b; a◌〪◌̖◌⃨◌֚b; a◌〪◌̖◌⃨◌֚b; a◌〪◌̖◌⃨◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING TRIPLE UNDERDOT, LATIN SMALL LETTER B
+0061 20E8 059A 0316 302A 0062;0061 302A 20E8 0316 059A 0062;0061 302A 20E8 0316 059A 0062;0061 302A 20E8 0316 059A 0062;0061 302A 20E8 0316 059A 0062; # (a◌⃨◌֚◌̖◌〪b; a◌〪◌⃨◌̖◌֚b; a◌〪◌⃨◌̖◌֚b; a◌〪◌⃨◌̖◌֚b; a◌〪◌⃨◌̖◌֚b; ) LATIN SMALL LETTER A, COMBINING TRIPLE UNDERDOT, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 0315 0300 05AE 20E9 0062;00E0 05AE 20E9 0315 0062;0061 05AE 0300 20E9 0315 0062;00E0 05AE 20E9 0315 0062;0061 05AE 0300 20E9 0315 0062; # (a◌̕◌̀◌֮◌⃩b; à◌֮◌⃩◌̕b; a◌֮◌̀◌⃩◌̕b; à◌֮◌⃩◌̕b; a◌֮◌̀◌⃩◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING WIDE BRIDGE ABOVE, LATIN SMALL LETTER B
+0061 20E9 0315 0300 05AE 0062;0061 05AE 20E9 0300 0315 0062;0061 05AE 20E9 0300 0315 0062;0061 05AE 20E9 0300 0315 0062;0061 05AE 20E9 0300 0315 0062; # (a◌⃩◌̕◌̀◌֮b; a◌֮◌⃩◌̀◌̕b; a◌֮◌⃩◌̀◌̕b; a◌֮◌⃩◌̀◌̕b; a◌֮◌⃩◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING WIDE BRIDGE ABOVE, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 093C 0334 20EA 0062;0061 0334 20EA 093C 0062;0061 0334 20EA 093C 0062;0061 0334 20EA 093C 0062;0061 0334 20EA 093C 0062; # (a◌़◌̴◌⃪b; a◌̴◌⃪◌़b; a◌̴◌⃪◌़b; a◌̴◌⃪◌़b; a◌̴◌⃪◌़b; ) LATIN SMALL LETTER A, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, COMBINING LEFTWARDS ARROW OVERLAY, LATIN SMALL LETTER B
+0061 20EA 093C 0334 0062;0061 20EA 0334 093C 0062;0061 20EA 0334 093C 0062;0061 20EA 0334 093C 0062;0061 20EA 0334 093C 0062; # (a◌⃪◌़◌̴b; a◌⃪◌̴◌़b; a◌⃪◌̴◌़b; a◌⃪◌̴◌़b; a◌⃪◌̴◌़b; ) LATIN SMALL LETTER A, COMBINING LEFTWARDS ARROW OVERLAY, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, LATIN SMALL LETTER B
+0061 0316 302A 031B 302A 0062;0061 031B 302A 302A 0316 0062;0061 031B 302A 302A 0316 0062;0061 031B 302A 302A 0316 0062;0061 031B 302A 302A 0316 0062; # (a◌̖◌〪◌̛◌〪b; a◌̛◌〪◌〪◌̖b; a◌̛◌〪◌〪◌̖b; a◌̛◌〪◌〪◌̖b; a◌̛◌〪◌〪◌̖b; ) LATIN SMALL LETTER A, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING HORN, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 302A 0316 302A 031B 0062;0061 031B 302A 302A 0316 0062;0061 031B 302A 302A 0316 0062;0061 031B 302A 302A 0316 0062;0061 031B 302A 302A 0316 0062; # (a◌〪◌̖◌〪◌̛b; a◌̛◌〪◌〪◌̖b; a◌̛◌〪◌〪◌̖b; a◌̛◌〪◌〪◌̖b; a◌̛◌〪◌〪◌̖b; ) LATIN SMALL LETTER A, IDEOGRAPHIC LEVEL TONE MARK, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, COMBINING HORN, LATIN SMALL LETTER B
+0061 0300 05AE 1D16D 302B 0062;00E0 1D16D 05AE 302B 0062;0061 1D16D 05AE 302B 0300 0062;00E0 1D16D 05AE 302B 0062;0061 1D16D 05AE 302B 0300 0062; # (a◌̀◌֮ð…­ð…­â—Œã€«b; àð…­ð…­â—ŒÖ®â—Œã€«b; að…­ð…­â—ŒÖ®â—Œã€«â—ŒÌ€b; àð…­ð…­â—ŒÖ®â—Œã€«b; að…­ð…­â—ŒÖ®â—Œã€«â—ŒÌ€b; ) LATIN SMALL LETTER A, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, MUSICAL SYMBOL COMBINING AUGMENTATION DOT, IDEOGRAPHIC RISING TONE MARK, LATIN SMALL LETTER B
+0061 302B 0300 05AE 1D16D 0062;00E0 1D16D 302B 05AE 0062;0061 1D16D 302B 05AE 0300 0062;00E0 1D16D 302B 05AE 0062;0061 1D16D 302B 05AE 0300 0062; # (a◌〫◌̀◌֮ð…­ð…­b; àð…­ð…­â—Œã€«â—ŒÖ®b; að…­ð…­â—Œã€«â—ŒÖ®â—ŒÌ€b; àð…­ð…­â—Œã€«â—ŒÖ®b; að…­ð…­â—Œã€«â—ŒÖ®â—ŒÌ€b; ) LATIN SMALL LETTER A, IDEOGRAPHIC RISING TONE MARK, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, MUSICAL SYMBOL COMBINING AUGMENTATION DOT, LATIN SMALL LETTER B
+0061 035F 0315 0300 302C 0062;00E0 0315 302C 035F 0062;0061 0300 0315 302C 035F 0062;00E0 0315 302C 035F 0062;0061 0300 0315 302C 035F 0062; # (a◌͟◌̕◌̀◌〬b; à◌̕◌〬◌͟b; a◌̀◌̕◌〬◌͟b; à◌̕◌〬◌͟b; a◌̀◌̕◌〬◌͟b; ) LATIN SMALL LETTER A, COMBINING DOUBLE MACRON BELOW, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, IDEOGRAPHIC DEPARTING TONE MARK, LATIN SMALL LETTER B
+0061 302C 035F 0315 0300 0062;00E0 302C 0315 035F 0062;0061 0300 302C 0315 035F 0062;00E0 302C 0315 035F 0062;0061 0300 302C 0315 035F 0062; # (a◌〬◌͟◌̕◌̀b; à◌〬◌̕◌͟b; a◌̀◌〬◌̕◌͟b; à◌〬◌̕◌͟b; a◌̀◌〬◌̕◌͟b; ) LATIN SMALL LETTER A, IDEOGRAPHIC DEPARTING TONE MARK, COMBINING DOUBLE MACRON BELOW, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, LATIN SMALL LETTER B
+0061 302E 059A 0316 302D 0062;0061 0316 059A 302D 302E 0062;0061 0316 059A 302D 302E 0062;0061 0316 059A 302D 302E 0062;0061 0316 059A 302D 302E 0062; # (a◌〮◌֚◌̖◌〭b; a◌̖◌֚◌〭◌〮b; a◌̖◌֚◌〭◌〮b; a◌̖◌֚◌〭◌〮b; a◌̖◌֚◌〭◌〮b; ) LATIN SMALL LETTER A, HANGUL SINGLE DOT TONE MARK, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC ENTERING TONE MARK, LATIN SMALL LETTER B
+0061 302D 302E 059A 0316 0062;0061 0316 302D 059A 302E 0062;0061 0316 302D 059A 302E 0062;0061 0316 302D 059A 302E 0062;0061 0316 302D 059A 302E 0062; # (a◌〭◌〮◌֚◌̖b; a◌̖◌〭◌֚◌〮b; a◌̖◌〭◌֚◌〮b; a◌̖◌〭◌֚◌〮b; a◌̖◌〭◌֚◌〮b; ) LATIN SMALL LETTER A, IDEOGRAPHIC ENTERING TONE MARK, HANGUL SINGLE DOT TONE MARK, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, LATIN SMALL LETTER B
+0061 1D16D 302E 059A 302E 0062;0061 059A 302E 302E 1D16D 0062;0061 059A 302E 302E 1D16D 0062;0061 059A 302E 302E 1D16D 0062;0061 059A 302E 302E 1D16D 0062; # (að…­ð…­â—Œã€®â—ŒÖšâ—Œã€®b; a◌֚◌〮◌〮ð…­ð…­b; a◌֚◌〮◌〮ð…­ð…­b; a◌֚◌〮◌〮ð…­ð…­b; a◌֚◌〮◌〮ð…­ð…­b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING AUGMENTATION DOT, HANGUL SINGLE DOT TONE MARK, HEBREW ACCENT YETIV, HANGUL SINGLE DOT TONE MARK, LATIN SMALL LETTER B
+0061 302E 1D16D 302E 059A 0062;0061 059A 302E 302E 1D16D 0062;0061 059A 302E 302E 1D16D 0062;0061 059A 302E 302E 1D16D 0062;0061 059A 302E 302E 1D16D 0062; # (a◌〮ð…­ð…­â—Œã€®â—ŒÖšb; a◌֚◌〮◌〮ð…­ð…­b; a◌֚◌〮◌〮ð…­ð…­b; a◌֚◌〮◌〮ð…­ð…­b; a◌֚◌〮◌〮ð…­ð…­b; ) LATIN SMALL LETTER A, HANGUL SINGLE DOT TONE MARK, MUSICAL SYMBOL COMBINING AUGMENTATION DOT, HANGUL SINGLE DOT TONE MARK, HEBREW ACCENT YETIV, LATIN SMALL LETTER B
+0061 1D16D 302E 059A 302F 0062;0061 059A 302E 302F 1D16D 0062;0061 059A 302E 302F 1D16D 0062;0061 059A 302E 302F 1D16D 0062;0061 059A 302E 302F 1D16D 0062; # (að…­ð…­â—Œã€®â—ŒÖšâ—Œã€¯b; a◌֚◌〮◌〯ð…­ð…­b; a◌֚◌〮◌〯ð…­ð…­b; a◌֚◌〮◌〯ð…­ð…­b; a◌֚◌〮◌〯ð…­ð…­b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING AUGMENTATION DOT, HANGUL SINGLE DOT TONE MARK, HEBREW ACCENT YETIV, HANGUL DOUBLE DOT TONE MARK, LATIN SMALL LETTER B
+0061 302F 1D16D 302E 059A 0062;0061 059A 302F 302E 1D16D 0062;0061 059A 302F 302E 1D16D 0062;0061 059A 302F 302E 1D16D 0062;0061 059A 302F 302E 1D16D 0062; # (a◌〯ð…­ð…­â—Œã€®â—ŒÖšb; a◌֚◌〯◌〮ð…­ð…­b; a◌֚◌〯◌〮ð…­ð…­b; a◌֚◌〯◌〮ð…­ð…­b; a◌֚◌〯◌〮ð…­ð…­b; ) LATIN SMALL LETTER A, HANGUL DOUBLE DOT TONE MARK, MUSICAL SYMBOL COMBINING AUGMENTATION DOT, HANGUL SINGLE DOT TONE MARK, HEBREW ACCENT YETIV, LATIN SMALL LETTER B
+0061 094D 3099 093C 3099 0062;0061 093C 3099 3099 094D 0062;0061 093C 3099 3099 094D 0062;0061 093C 3099 3099 094D 0062;0061 093C 3099 3099 094D 0062; # (aâ—Œà¥â—Œã‚™â—Œà¤¼â—Œã‚™b; a◌़◌゙◌゙◌à¥b; a◌़◌゙◌゙◌à¥b; a◌़◌゙◌゙◌à¥b; a◌़◌゙◌゙◌à¥b; ) LATIN SMALL LETTER A, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, DEVANAGARI SIGN NUKTA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, LATIN SMALL LETTER B
+0061 3099 094D 3099 093C 0062;0061 093C 3099 3099 094D 0062;0061 093C 3099 3099 094D 0062;0061 093C 3099 3099 094D 0062;0061 093C 3099 3099 094D 0062; # (a◌゙◌à¥â—Œã‚™â—Œà¤¼b; a◌़◌゙◌゙◌à¥b; a◌़◌゙◌゙◌à¥b; a◌़◌゙◌゙◌à¥b; a◌़◌゙◌゙◌à¥b; ) LATIN SMALL LETTER A, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, DEVANAGARI SIGN NUKTA, LATIN SMALL LETTER B
+0061 094D 3099 093C 309A 0062;0061 093C 3099 309A 094D 0062;0061 093C 3099 309A 094D 0062;0061 093C 3099 309A 094D 0062;0061 093C 3099 309A 094D 0062; # (aâ—Œà¥â—Œã‚™â—Œà¤¼â—Œã‚šb; a◌़◌゙◌゚◌à¥b; a◌़◌゙◌゚◌à¥b; a◌़◌゙◌゚◌à¥b; a◌़◌゙◌゚◌à¥b; ) LATIN SMALL LETTER A, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, DEVANAGARI SIGN NUKTA, COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK, LATIN SMALL LETTER B
+0061 309A 094D 3099 093C 0062;0061 093C 309A 3099 094D 0062;0061 093C 309A 3099 094D 0062;0061 093C 309A 3099 094D 0062;0061 093C 309A 3099 094D 0062; # (a◌゚◌à¥â—Œã‚™â—Œà¤¼b; a◌़◌゚◌゙◌à¥b; a◌़◌゚◌゙◌à¥b; a◌़◌゚◌゙◌à¥b; a◌़◌゚◌゙◌à¥b; ) LATIN SMALL LETTER A, COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, DEVANAGARI SIGN NUKTA, LATIN SMALL LETTER B
+0061 064B FB1E 05C2 FB1E 0062;0061 05C2 FB1E FB1E 064B 0062;0061 05C2 FB1E FB1E 064B 0062;0061 05C2 FB1E FB1E 064B 0062;0061 05C2 FB1E FB1E 064B 0062; # (a◌ً◌ﬞ◌ׂ◌ﬞb; a◌ׂ◌ﬞ◌ﬞ◌ًb; a◌ׂ◌ﬞ◌ﬞ◌ًb; a◌ׂ◌ﬞ◌ﬞ◌ًb; a◌ׂ◌ﬞ◌ﬞ◌ًb; ) LATIN SMALL LETTER A, ARABIC FATHATAN, HEBREW POINT JUDEO-SPANISH VARIKA, HEBREW POINT SIN DOT, HEBREW POINT JUDEO-SPANISH VARIKA, LATIN SMALL LETTER B
+0061 FB1E 064B FB1E 05C2 0062;0061 05C2 FB1E FB1E 064B 0062;0061 05C2 FB1E FB1E 064B 0062;0061 05C2 FB1E FB1E 064B 0062;0061 05C2 FB1E FB1E 064B 0062; # (a◌ﬞ◌ً◌ﬞ◌ׂb; a◌ׂ◌ﬞ◌ﬞ◌ًb; a◌ׂ◌ﬞ◌ﬞ◌ًb; a◌ׂ◌ﬞ◌ﬞ◌ًb; a◌ׂ◌ﬞ◌ﬞ◌ًb; ) LATIN SMALL LETTER A, HEBREW POINT JUDEO-SPANISH VARIKA, ARABIC FATHATAN, HEBREW POINT JUDEO-SPANISH VARIKA, HEBREW POINT SIN DOT, LATIN SMALL LETTER B
+0061 0315 0300 05AE FE20 0062;00E0 05AE FE20 0315 0062;0061 05AE 0300 FE20 0315 0062;00E0 05AE FE20 0315 0062;0061 05AE 0300 FE20 0315 0062; # (a◌̕◌̀◌֮◌︠b; à◌֮◌︠◌̕b; a◌֮◌̀◌︠◌̕b; à◌֮◌︠◌̕b; a◌֮◌̀◌︠◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING LIGATURE LEFT HALF, LATIN SMALL LETTER B
+0061 FE20 0315 0300 05AE 0062;0061 05AE FE20 0300 0315 0062;0061 05AE FE20 0300 0315 0062;0061 05AE FE20 0300 0315 0062;0061 05AE FE20 0300 0315 0062; # (a◌︠◌̕◌̀◌֮b; a◌֮◌︠◌̀◌̕b; a◌֮◌︠◌̀◌̕b; a◌֮◌︠◌̀◌̕b; a◌֮◌︠◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING LIGATURE LEFT HALF, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE FE21 0062;00E0 05AE FE21 0315 0062;0061 05AE 0300 FE21 0315 0062;00E0 05AE FE21 0315 0062;0061 05AE 0300 FE21 0315 0062; # (a◌̕◌̀◌֮◌︡b; à◌֮◌︡◌̕b; a◌֮◌̀◌︡◌̕b; à◌֮◌︡◌̕b; a◌֮◌̀◌︡◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING LIGATURE RIGHT HALF, LATIN SMALL LETTER B
+0061 FE21 0315 0300 05AE 0062;0061 05AE FE21 0300 0315 0062;0061 05AE FE21 0300 0315 0062;0061 05AE FE21 0300 0315 0062;0061 05AE FE21 0300 0315 0062; # (a◌︡◌̕◌̀◌֮b; a◌֮◌︡◌̀◌̕b; a◌֮◌︡◌̀◌̕b; a◌֮◌︡◌̀◌̕b; a◌֮◌︡◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING LIGATURE RIGHT HALF, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE FE22 0062;00E0 05AE FE22 0315 0062;0061 05AE 0300 FE22 0315 0062;00E0 05AE FE22 0315 0062;0061 05AE 0300 FE22 0315 0062; # (a◌̕◌̀◌֮◌︢b; à◌֮◌︢◌̕b; a◌֮◌̀◌︢◌̕b; à◌֮◌︢◌̕b; a◌֮◌̀◌︢◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING DOUBLE TILDE LEFT HALF, LATIN SMALL LETTER B
+0061 FE22 0315 0300 05AE 0062;0061 05AE FE22 0300 0315 0062;0061 05AE FE22 0300 0315 0062;0061 05AE FE22 0300 0315 0062;0061 05AE FE22 0300 0315 0062; # (a◌︢◌̕◌̀◌֮b; a◌֮◌︢◌̀◌̕b; a◌֮◌︢◌̀◌̕b; a◌֮◌︢◌̀◌̕b; a◌֮◌︢◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING DOUBLE TILDE LEFT HALF, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE FE23 0062;00E0 05AE FE23 0315 0062;0061 05AE 0300 FE23 0315 0062;00E0 05AE FE23 0315 0062;0061 05AE 0300 FE23 0315 0062; # (a◌̕◌̀◌֮◌︣b; à◌֮◌︣◌̕b; a◌֮◌̀◌︣◌̕b; à◌֮◌︣◌̕b; a◌֮◌̀◌︣◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING DOUBLE TILDE RIGHT HALF, LATIN SMALL LETTER B
+0061 FE23 0315 0300 05AE 0062;0061 05AE FE23 0300 0315 0062;0061 05AE FE23 0300 0315 0062;0061 05AE FE23 0300 0315 0062;0061 05AE FE23 0300 0315 0062; # (a◌︣◌̕◌̀◌֮b; a◌֮◌︣◌̀◌̕b; a◌֮◌︣◌̀◌̕b; a◌֮◌︣◌̀◌̕b; a◌֮◌︣◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING DOUBLE TILDE RIGHT HALF, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 302A 031B 0321 1D165 0062;0061 0321 031B 1D165 302A 0062;0061 0321 031B 1D165 302A 0062;0061 0321 031B 1D165 302A 0062;0061 0321 031B 1D165 302A 0062; # (a◌〪◌̛◌̡ð…¥ð…¥b; a◌̡◌̛ð…¥ð…¥â—Œã€ªb; a◌̡◌̛ð…¥ð…¥â—Œã€ªb; a◌̡◌̛ð…¥ð…¥â—Œã€ªb; a◌̡◌̛ð…¥ð…¥â—Œã€ªb; ) LATIN SMALL LETTER A, IDEOGRAPHIC LEVEL TONE MARK, COMBINING HORN, COMBINING PALATALIZED HOOK BELOW, MUSICAL SYMBOL COMBINING STEM, LATIN SMALL LETTER B
+0061 1D165 302A 031B 0321 0062;0061 0321 1D165 031B 302A 0062;0061 0321 1D165 031B 302A 0062;0061 0321 1D165 031B 302A 0062;0061 0321 1D165 031B 302A 0062; # (að…¥ð…¥â—Œã€ªâ—ŒÌ›â—ŒÌ¡b; a◌̡ð…¥ð…¥â—ŒÌ›â—Œã€ªb; a◌̡ð…¥ð…¥â—ŒÌ›â—Œã€ªb; a◌̡ð…¥ð…¥â—ŒÌ›â—Œã€ªb; a◌̡ð…¥ð…¥â—ŒÌ›â—Œã€ªb; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING STEM, IDEOGRAPHIC LEVEL TONE MARK, COMBINING HORN, COMBINING PALATALIZED HOOK BELOW, LATIN SMALL LETTER B
+0061 302A 031B 0321 1D166 0062;0061 0321 031B 1D166 302A 0062;0061 0321 031B 1D166 302A 0062;0061 0321 031B 1D166 302A 0062;0061 0321 031B 1D166 302A 0062; # (a◌〪◌̛◌̡ð…¦ð…¦b; a◌̡◌̛ð…¦ð…¦â—Œã€ªb; a◌̡◌̛ð…¦ð…¦â—Œã€ªb; a◌̡◌̛ð…¦ð…¦â—Œã€ªb; a◌̡◌̛ð…¦ð…¦â—Œã€ªb; ) LATIN SMALL LETTER A, IDEOGRAPHIC LEVEL TONE MARK, COMBINING HORN, COMBINING PALATALIZED HOOK BELOW, MUSICAL SYMBOL COMBINING SPRECHGESANG STEM, LATIN SMALL LETTER B
+0061 1D166 302A 031B 0321 0062;0061 0321 1D166 031B 302A 0062;0061 0321 1D166 031B 302A 0062;0061 0321 1D166 031B 302A 0062;0061 0321 1D166 031B 302A 0062; # (að…¦ð…¦â—Œã€ªâ—ŒÌ›â—ŒÌ¡b; a◌̡ð…¦ð…¦â—ŒÌ›â—Œã€ªb; a◌̡ð…¦ð…¦â—ŒÌ›â—Œã€ªb; a◌̡ð…¦ð…¦â—ŒÌ›â—Œã€ªb; a◌̡ð…¦ð…¦â—ŒÌ›â—Œã€ªb; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING SPRECHGESANG STEM, IDEOGRAPHIC LEVEL TONE MARK, COMBINING HORN, COMBINING PALATALIZED HOOK BELOW, LATIN SMALL LETTER B
+0061 093C 0334 1D167 0062;0061 0334 1D167 093C 0062;0061 0334 1D167 093C 0062;0061 0334 1D167 093C 0062;0061 0334 1D167 093C 0062; # (a◌़◌̴◌ð…§â—Œð…§b; a◌̴◌ð…§â—Œð…§â—Œà¤¼b; a◌̴◌ð…§â—Œð…§â—Œà¤¼b; a◌̴◌ð…§â—Œð…§â—Œà¤¼b; a◌̴◌ð…§â—Œð…§â—Œà¤¼b; ) LATIN SMALL LETTER A, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, MUSICAL SYMBOL COMBINING TREMOLO-1, LATIN SMALL LETTER B
+0061 1D167 093C 0334 0062;0061 1D167 0334 093C 0062;0061 1D167 0334 093C 0062;0061 1D167 0334 093C 0062;0061 1D167 0334 093C 0062; # (aâ—Œð…§â—Œð…§â—Œà¤¼â—ŒÌ´b; aâ—Œð…§â—Œð…§â—ŒÌ´â—Œà¤¼b; aâ—Œð…§â—Œð…§â—ŒÌ´â—Œà¤¼b; aâ—Œð…§â—Œð…§â—ŒÌ´â—Œà¤¼b; aâ—Œð…§â—Œð…§â—ŒÌ´â—Œà¤¼b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING TREMOLO-1, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, LATIN SMALL LETTER B
+0061 093C 0334 1D168 0062;0061 0334 1D168 093C 0062;0061 0334 1D168 093C 0062;0061 0334 1D168 093C 0062;0061 0334 1D168 093C 0062; # (a◌़◌̴◌ð…¨â—Œð…¨b; a◌̴◌ð…¨â—Œð…¨â—Œà¤¼b; a◌̴◌ð…¨â—Œð…¨â—Œà¤¼b; a◌̴◌ð…¨â—Œð…¨â—Œà¤¼b; a◌̴◌ð…¨â—Œð…¨â—Œà¤¼b; ) LATIN SMALL LETTER A, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, MUSICAL SYMBOL COMBINING TREMOLO-2, LATIN SMALL LETTER B
+0061 1D168 093C 0334 0062;0061 1D168 0334 093C 0062;0061 1D168 0334 093C 0062;0061 1D168 0334 093C 0062;0061 1D168 0334 093C 0062; # (aâ—Œð…¨â—Œð…¨â—Œà¤¼â—ŒÌ´b; aâ—Œð…¨â—Œð…¨â—ŒÌ´â—Œà¤¼b; aâ—Œð…¨â—Œð…¨â—ŒÌ´â—Œà¤¼b; aâ—Œð…¨â—Œð…¨â—ŒÌ´â—Œà¤¼b; aâ—Œð…¨â—Œð…¨â—ŒÌ´â—Œà¤¼b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING TREMOLO-2, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, LATIN SMALL LETTER B
+0061 093C 0334 1D169 0062;0061 0334 1D169 093C 0062;0061 0334 1D169 093C 0062;0061 0334 1D169 093C 0062;0061 0334 1D169 093C 0062; # (a◌़◌̴◌ð…©â—Œð…©b; a◌̴◌ð…©â—Œð…©â—Œà¤¼b; a◌̴◌ð…©â—Œð…©â—Œà¤¼b; a◌̴◌ð…©â—Œð…©â—Œà¤¼b; a◌̴◌ð…©â—Œð…©â—Œà¤¼b; ) LATIN SMALL LETTER A, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, MUSICAL SYMBOL COMBINING TREMOLO-3, LATIN SMALL LETTER B
+0061 1D169 093C 0334 0062;0061 1D169 0334 093C 0062;0061 1D169 0334 093C 0062;0061 1D169 0334 093C 0062;0061 1D169 0334 093C 0062; # (aâ—Œð…©â—Œð…©â—Œà¤¼â—ŒÌ´b; aâ—Œð…©â—Œð…©â—ŒÌ´â—Œà¤¼b; aâ—Œð…©â—Œð…©â—ŒÌ´â—Œà¤¼b; aâ—Œð…©â—Œð…©â—ŒÌ´â—Œà¤¼b; aâ—Œð…©â—Œð…©â—ŒÌ´â—Œà¤¼b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING TREMOLO-3, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, LATIN SMALL LETTER B
+0061 05AE 1D16D 302E 1D16D 0062;0061 302E 1D16D 1D16D 05AE 0062;0061 302E 1D16D 1D16D 05AE 0062;0061 302E 1D16D 1D16D 05AE 0062;0061 302E 1D16D 1D16D 05AE 0062; # (a◌֮ð…­ð…­â—Œã€®ð…­ð…­b; a◌〮ð…­ð…­ð…­ð…­â—ŒÖ®b; a◌〮ð…­ð…­ð…­ð…­â—ŒÖ®b; a◌〮ð…­ð…­ð…­ð…­â—ŒÖ®b; a◌〮ð…­ð…­ð…­ð…­â—ŒÖ®b; ) LATIN SMALL LETTER A, HEBREW ACCENT ZINOR, MUSICAL SYMBOL COMBINING AUGMENTATION DOT, HANGUL SINGLE DOT TONE MARK, MUSICAL SYMBOL COMBINING AUGMENTATION DOT, LATIN SMALL LETTER B
+0061 1D16D 05AE 1D16D 302E 0062;0061 302E 1D16D 1D16D 05AE 0062;0061 302E 1D16D 1D16D 05AE 0062;0061 302E 1D16D 1D16D 05AE 0062;0061 302E 1D16D 1D16D 05AE 0062; # (að…­ð…­â—ŒÖ®ð…­ð…­â—Œã€®b; a◌〮ð…­ð…­ð…­ð…­â—ŒÖ®b; a◌〮ð…­ð…­ð…­ð…­â—ŒÖ®b; a◌〮ð…­ð…­ð…­ð…­â—ŒÖ®b; a◌〮ð…­ð…­ð…­ð…­â—ŒÖ®b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING AUGMENTATION DOT, HEBREW ACCENT ZINOR, MUSICAL SYMBOL COMBINING AUGMENTATION DOT, HANGUL SINGLE DOT TONE MARK, LATIN SMALL LETTER B
+0061 302A 031B 0321 1D16E 0062;0061 0321 031B 1D16E 302A 0062;0061 0321 031B 1D16E 302A 0062;0061 0321 031B 1D16E 302A 0062;0061 0321 031B 1D16E 302A 0062; # (a◌〪◌̛◌̡ð…®ð…®b; a◌̡◌̛ð…®ð…®â—Œã€ªb; a◌̡◌̛ð…®ð…®â—Œã€ªb; a◌̡◌̛ð…®ð…®â—Œã€ªb; a◌̡◌̛ð…®ð…®â—Œã€ªb; ) LATIN SMALL LETTER A, IDEOGRAPHIC LEVEL TONE MARK, COMBINING HORN, COMBINING PALATALIZED HOOK BELOW, MUSICAL SYMBOL COMBINING FLAG-1, LATIN SMALL LETTER B
+0061 1D16E 302A 031B 0321 0062;0061 0321 1D16E 031B 302A 0062;0061 0321 1D16E 031B 302A 0062;0061 0321 1D16E 031B 302A 0062;0061 0321 1D16E 031B 302A 0062; # (að…®ð…®â—Œã€ªâ—ŒÌ›â—ŒÌ¡b; a◌̡ð…®ð…®â—ŒÌ›â—Œã€ªb; a◌̡ð…®ð…®â—ŒÌ›â—Œã€ªb; a◌̡ð…®ð…®â—ŒÌ›â—Œã€ªb; a◌̡ð…®ð…®â—ŒÌ›â—Œã€ªb; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING FLAG-1, IDEOGRAPHIC LEVEL TONE MARK, COMBINING HORN, COMBINING PALATALIZED HOOK BELOW, LATIN SMALL LETTER B
+0061 302A 031B 0321 1D16F 0062;0061 0321 031B 1D16F 302A 0062;0061 0321 031B 1D16F 302A 0062;0061 0321 031B 1D16F 302A 0062;0061 0321 031B 1D16F 302A 0062; # (a◌〪◌̛◌̡ð…¯ð…¯b; a◌̡◌̛ð…¯ð…¯â—Œã€ªb; a◌̡◌̛ð…¯ð…¯â—Œã€ªb; a◌̡◌̛ð…¯ð…¯â—Œã€ªb; a◌̡◌̛ð…¯ð…¯â—Œã€ªb; ) LATIN SMALL LETTER A, IDEOGRAPHIC LEVEL TONE MARK, COMBINING HORN, COMBINING PALATALIZED HOOK BELOW, MUSICAL SYMBOL COMBINING FLAG-2, LATIN SMALL LETTER B
+0061 1D16F 302A 031B 0321 0062;0061 0321 1D16F 031B 302A 0062;0061 0321 1D16F 031B 302A 0062;0061 0321 1D16F 031B 302A 0062;0061 0321 1D16F 031B 302A 0062; # (að…¯ð…¯â—Œã€ªâ—ŒÌ›â—ŒÌ¡b; a◌̡ð…¯ð…¯â—ŒÌ›â—Œã€ªb; a◌̡ð…¯ð…¯â—ŒÌ›â—Œã€ªb; a◌̡ð…¯ð…¯â—ŒÌ›â—Œã€ªb; a◌̡ð…¯ð…¯â—ŒÌ›â—Œã€ªb; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING FLAG-2, IDEOGRAPHIC LEVEL TONE MARK, COMBINING HORN, COMBINING PALATALIZED HOOK BELOW, LATIN SMALL LETTER B
+0061 302A 031B 0321 1D170 0062;0061 0321 031B 1D170 302A 0062;0061 0321 031B 1D170 302A 0062;0061 0321 031B 1D170 302A 0062;0061 0321 031B 1D170 302A 0062; # (a◌〪◌̛◌̡ð…°ð…°b; a◌̡◌̛ð…°ð…°â—Œã€ªb; a◌̡◌̛ð…°ð…°â—Œã€ªb; a◌̡◌̛ð…°ð…°â—Œã€ªb; a◌̡◌̛ð…°ð…°â—Œã€ªb; ) LATIN SMALL LETTER A, IDEOGRAPHIC LEVEL TONE MARK, COMBINING HORN, COMBINING PALATALIZED HOOK BELOW, MUSICAL SYMBOL COMBINING FLAG-3, LATIN SMALL LETTER B
+0061 1D170 302A 031B 0321 0062;0061 0321 1D170 031B 302A 0062;0061 0321 1D170 031B 302A 0062;0061 0321 1D170 031B 302A 0062;0061 0321 1D170 031B 302A 0062; # (að…°ð…°â—Œã€ªâ—ŒÌ›â—ŒÌ¡b; a◌̡ð…°ð…°â—ŒÌ›â—Œã€ªb; a◌̡ð…°ð…°â—ŒÌ›â—Œã€ªb; a◌̡ð…°ð…°â—ŒÌ›â—Œã€ªb; a◌̡ð…°ð…°â—ŒÌ›â—Œã€ªb; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING FLAG-3, IDEOGRAPHIC LEVEL TONE MARK, COMBINING HORN, COMBINING PALATALIZED HOOK BELOW, LATIN SMALL LETTER B
+0061 302A 031B 0321 1D171 0062;0061 0321 031B 1D171 302A 0062;0061 0321 031B 1D171 302A 0062;0061 0321 031B 1D171 302A 0062;0061 0321 031B 1D171 302A 0062; # (a◌〪◌̛◌̡ð…±ð…±b; a◌̡◌̛ð…±ð…±â—Œã€ªb; a◌̡◌̛ð…±ð…±â—Œã€ªb; a◌̡◌̛ð…±ð…±â—Œã€ªb; a◌̡◌̛ð…±ð…±â—Œã€ªb; ) LATIN SMALL LETTER A, IDEOGRAPHIC LEVEL TONE MARK, COMBINING HORN, COMBINING PALATALIZED HOOK BELOW, MUSICAL SYMBOL COMBINING FLAG-4, LATIN SMALL LETTER B
+0061 1D171 302A 031B 0321 0062;0061 0321 1D171 031B 302A 0062;0061 0321 1D171 031B 302A 0062;0061 0321 1D171 031B 302A 0062;0061 0321 1D171 031B 302A 0062; # (að…±ð…±â—Œã€ªâ—ŒÌ›â—ŒÌ¡b; a◌̡ð…±ð…±â—ŒÌ›â—Œã€ªb; a◌̡ð…±ð…±â—ŒÌ›â—Œã€ªb; a◌̡ð…±ð…±â—ŒÌ›â—Œã€ªb; a◌̡ð…±ð…±â—ŒÌ›â—Œã€ªb; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING FLAG-4, IDEOGRAPHIC LEVEL TONE MARK, COMBINING HORN, COMBINING PALATALIZED HOOK BELOW, LATIN SMALL LETTER B
+0061 302A 031B 0321 1D172 0062;0061 0321 031B 1D172 302A 0062;0061 0321 031B 1D172 302A 0062;0061 0321 031B 1D172 302A 0062;0061 0321 031B 1D172 302A 0062; # (a◌〪◌̛◌̡ð…²ð…²b; a◌̡◌̛ð…²ð…²â—Œã€ªb; a◌̡◌̛ð…²ð…²â—Œã€ªb; a◌̡◌̛ð…²ð…²â—Œã€ªb; a◌̡◌̛ð…²ð…²â—Œã€ªb; ) LATIN SMALL LETTER A, IDEOGRAPHIC LEVEL TONE MARK, COMBINING HORN, COMBINING PALATALIZED HOOK BELOW, MUSICAL SYMBOL COMBINING FLAG-5, LATIN SMALL LETTER B
+0061 1D172 302A 031B 0321 0062;0061 0321 1D172 031B 302A 0062;0061 0321 1D172 031B 302A 0062;0061 0321 1D172 031B 302A 0062;0061 0321 1D172 031B 302A 0062; # (að…²ð…²â—Œã€ªâ—ŒÌ›â—ŒÌ¡b; a◌̡ð…²ð…²â—ŒÌ›â—Œã€ªb; a◌̡ð…²ð…²â—ŒÌ›â—Œã€ªb; a◌̡ð…²ð…²â—ŒÌ›â—Œã€ªb; a◌̡ð…²ð…²â—ŒÌ›â—Œã€ªb; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING FLAG-5, IDEOGRAPHIC LEVEL TONE MARK, COMBINING HORN, COMBINING PALATALIZED HOOK BELOW, LATIN SMALL LETTER B
+0061 059A 0316 302A 1D17B 0062;0061 302A 0316 1D17B 059A 0062;0061 302A 0316 1D17B 059A 0062;0061 302A 0316 1D17B 059A 0062;0061 302A 0316 1D17B 059A 0062; # (a◌֚◌̖◌〪◌ð…»â—Œð…»b; a◌〪◌̖◌ð…»â—Œð…»â—ŒÖšb; a◌〪◌̖◌ð…»â—Œð…»â—ŒÖšb; a◌〪◌̖◌ð…»â—Œð…»â—ŒÖšb; a◌〪◌̖◌ð…»â—Œð…»â—ŒÖšb; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, MUSICAL SYMBOL COMBINING ACCENT, LATIN SMALL LETTER B
+0061 1D17B 059A 0316 302A 0062;0061 302A 1D17B 0316 059A 0062;0061 302A 1D17B 0316 059A 0062;0061 302A 1D17B 0316 059A 0062;0061 302A 1D17B 0316 059A 0062; # (aâ—Œð…»â—Œð…»â—ŒÖšâ—ŒÌ–◌〪b; a◌〪◌ð…»â—Œð…»â—ŒÌ–◌֚b; a◌〪◌ð…»â—Œð…»â—ŒÌ–◌֚b; a◌〪◌ð…»â—Œð…»â—ŒÌ–◌֚b; a◌〪◌ð…»â—Œð…»â—ŒÌ–◌֚b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING ACCENT, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 059A 0316 302A 1D17C 0062;0061 302A 0316 1D17C 059A 0062;0061 302A 0316 1D17C 059A 0062;0061 302A 0316 1D17C 059A 0062;0061 302A 0316 1D17C 059A 0062; # (a◌֚◌̖◌〪◌ð…¼â—Œð…¼b; a◌〪◌̖◌ð…¼â—Œð…¼â—ŒÖšb; a◌〪◌̖◌ð…¼â—Œð…¼â—ŒÖšb; a◌〪◌̖◌ð…¼â—Œð…¼â—ŒÖšb; a◌〪◌̖◌ð…¼â—Œð…¼â—ŒÖšb; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, MUSICAL SYMBOL COMBINING STACCATO, LATIN SMALL LETTER B
+0061 1D17C 059A 0316 302A 0062;0061 302A 1D17C 0316 059A 0062;0061 302A 1D17C 0316 059A 0062;0061 302A 1D17C 0316 059A 0062;0061 302A 1D17C 0316 059A 0062; # (aâ—Œð…¼â—Œð…¼â—ŒÖšâ—ŒÌ–◌〪b; a◌〪◌ð…¼â—Œð…¼â—ŒÌ–◌֚b; a◌〪◌ð…¼â—Œð…¼â—ŒÌ–◌֚b; a◌〪◌ð…¼â—Œð…¼â—ŒÌ–◌֚b; a◌〪◌ð…¼â—Œð…¼â—ŒÌ–◌֚b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING STACCATO, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 059A 0316 302A 1D17D 0062;0061 302A 0316 1D17D 059A 0062;0061 302A 0316 1D17D 059A 0062;0061 302A 0316 1D17D 059A 0062;0061 302A 0316 1D17D 059A 0062; # (a◌֚◌̖◌〪◌ð…½â—Œð…½b; a◌〪◌̖◌ð…½â—Œð…½â—ŒÖšb; a◌〪◌̖◌ð…½â—Œð…½â—ŒÖšb; a◌〪◌̖◌ð…½â—Œð…½â—ŒÖšb; a◌〪◌̖◌ð…½â—Œð…½â—ŒÖšb; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, MUSICAL SYMBOL COMBINING TENUTO, LATIN SMALL LETTER B
+0061 1D17D 059A 0316 302A 0062;0061 302A 1D17D 0316 059A 0062;0061 302A 1D17D 0316 059A 0062;0061 302A 1D17D 0316 059A 0062;0061 302A 1D17D 0316 059A 0062; # (aâ—Œð…½â—Œð…½â—ŒÖšâ—ŒÌ–◌〪b; a◌〪◌ð…½â—Œð…½â—ŒÌ–◌֚b; a◌〪◌ð…½â—Œð…½â—ŒÌ–◌֚b; a◌〪◌ð…½â—Œð…½â—ŒÌ–◌֚b; a◌〪◌ð…½â—Œð…½â—ŒÌ–◌֚b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING TENUTO, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 059A 0316 302A 1D17E 0062;0061 302A 0316 1D17E 059A 0062;0061 302A 0316 1D17E 059A 0062;0061 302A 0316 1D17E 059A 0062;0061 302A 0316 1D17E 059A 0062; # (a◌֚◌̖◌〪◌ð…¾â—Œð…¾b; a◌〪◌̖◌ð…¾â—Œð…¾â—ŒÖšb; a◌〪◌̖◌ð…¾â—Œð…¾â—ŒÖšb; a◌〪◌̖◌ð…¾â—Œð…¾â—ŒÖšb; a◌〪◌̖◌ð…¾â—Œð…¾â—ŒÖšb; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, MUSICAL SYMBOL COMBINING STACCATISSIMO, LATIN SMALL LETTER B
+0061 1D17E 059A 0316 302A 0062;0061 302A 1D17E 0316 059A 0062;0061 302A 1D17E 0316 059A 0062;0061 302A 1D17E 0316 059A 0062;0061 302A 1D17E 0316 059A 0062; # (aâ—Œð…¾â—Œð…¾â—ŒÖšâ—ŒÌ–◌〪b; a◌〪◌ð…¾â—Œð…¾â—ŒÌ–◌֚b; a◌〪◌ð…¾â—Œð…¾â—ŒÌ–◌֚b; a◌〪◌ð…¾â—Œð…¾â—ŒÌ–◌֚b; a◌〪◌ð…¾â—Œð…¾â—ŒÌ–◌֚b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING STACCATISSIMO, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 059A 0316 302A 1D17F 0062;0061 302A 0316 1D17F 059A 0062;0061 302A 0316 1D17F 059A 0062;0061 302A 0316 1D17F 059A 0062;0061 302A 0316 1D17F 059A 0062; # (a◌֚◌̖◌〪◌ð…¿â—Œð…¿b; a◌〪◌̖◌ð…¿â—Œð…¿â—ŒÖšb; a◌〪◌̖◌ð…¿â—Œð…¿â—ŒÖšb; a◌〪◌̖◌ð…¿â—Œð…¿â—ŒÖšb; a◌〪◌̖◌ð…¿â—Œð…¿â—ŒÖšb; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, MUSICAL SYMBOL COMBINING MARCATO, LATIN SMALL LETTER B
+0061 1D17F 059A 0316 302A 0062;0061 302A 1D17F 0316 059A 0062;0061 302A 1D17F 0316 059A 0062;0061 302A 1D17F 0316 059A 0062;0061 302A 1D17F 0316 059A 0062; # (aâ—Œð…¿â—Œð…¿â—ŒÖšâ—ŒÌ–◌〪b; a◌〪◌ð…¿â—Œð…¿â—ŒÌ–◌֚b; a◌〪◌ð…¿â—Œð…¿â—ŒÌ–◌֚b; a◌〪◌ð…¿â—Œð…¿â—ŒÌ–◌֚b; a◌〪◌ð…¿â—Œð…¿â—ŒÌ–◌֚b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING MARCATO, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 059A 0316 302A 1D180 0062;0061 302A 0316 1D180 059A 0062;0061 302A 0316 1D180 059A 0062;0061 302A 0316 1D180 059A 0062;0061 302A 0316 1D180 059A 0062; # (a◌֚◌̖◌〪◌ð†€â—Œð†€b; a◌〪◌̖◌ð†€â—Œð†€â—ŒÖšb; a◌〪◌̖◌ð†€â—Œð†€â—ŒÖšb; a◌〪◌̖◌ð†€â—Œð†€â—ŒÖšb; a◌〪◌̖◌ð†€â—Œð†€â—ŒÖšb; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, MUSICAL SYMBOL COMBINING MARCATO-STACCATO, LATIN SMALL LETTER B
+0061 1D180 059A 0316 302A 0062;0061 302A 1D180 0316 059A 0062;0061 302A 1D180 0316 059A 0062;0061 302A 1D180 0316 059A 0062;0061 302A 1D180 0316 059A 0062; # (aâ—Œð†€â—Œð†€â—ŒÖšâ—ŒÌ–◌〪b; a◌〪◌ð†€â—Œð†€â—ŒÌ–◌֚b; a◌〪◌ð†€â—Œð†€â—ŒÌ–◌֚b; a◌〪◌ð†€â—Œð†€â—ŒÌ–◌֚b; a◌〪◌ð†€â—Œð†€â—ŒÌ–◌֚b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING MARCATO-STACCATO, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 059A 0316 302A 1D181 0062;0061 302A 0316 1D181 059A 0062;0061 302A 0316 1D181 059A 0062;0061 302A 0316 1D181 059A 0062;0061 302A 0316 1D181 059A 0062; # (a◌֚◌̖◌〪◌ð†â—Œð†b; a◌〪◌̖◌ð†â—Œð†â—ŒÖšb; a◌〪◌̖◌ð†â—Œð†â—ŒÖšb; a◌〪◌̖◌ð†â—Œð†â—ŒÖšb; a◌〪◌̖◌ð†â—Œð†â—ŒÖšb; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, MUSICAL SYMBOL COMBINING ACCENT-STACCATO, LATIN SMALL LETTER B
+0061 1D181 059A 0316 302A 0062;0061 302A 1D181 0316 059A 0062;0061 302A 1D181 0316 059A 0062;0061 302A 1D181 0316 059A 0062;0061 302A 1D181 0316 059A 0062; # (aâ—Œð†â—Œð†â—ŒÖšâ—ŒÌ–◌〪b; a◌〪◌ð†â—Œð†â—ŒÌ–◌֚b; a◌〪◌ð†â—Œð†â—ŒÌ–◌֚b; a◌〪◌ð†â—Œð†â—ŒÌ–◌֚b; a◌〪◌ð†â—Œð†â—ŒÌ–◌֚b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING ACCENT-STACCATO, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 059A 0316 302A 1D182 0062;0061 302A 0316 1D182 059A 0062;0061 302A 0316 1D182 059A 0062;0061 302A 0316 1D182 059A 0062;0061 302A 0316 1D182 059A 0062; # (a◌֚◌̖◌〪◌ð†‚â—Œð†‚b; a◌〪◌̖◌ð†‚â—Œð†‚◌֚b; a◌〪◌̖◌ð†‚â—Œð†‚◌֚b; a◌〪◌̖◌ð†‚â—Œð†‚◌֚b; a◌〪◌̖◌ð†‚â—Œð†‚◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, MUSICAL SYMBOL COMBINING LOURE, LATIN SMALL LETTER B
+0061 1D182 059A 0316 302A 0062;0061 302A 1D182 0316 059A 0062;0061 302A 1D182 0316 059A 0062;0061 302A 1D182 0316 059A 0062;0061 302A 1D182 0316 059A 0062; # (aâ—Œð†‚â—Œð†‚◌֚◌̖◌〪b; a◌〪◌ð†‚â—Œð†‚◌̖◌֚b; a◌〪◌ð†‚â—Œð†‚◌̖◌֚b; a◌〪◌ð†‚â—Œð†‚◌̖◌֚b; a◌〪◌ð†‚â—Œð†‚◌̖◌֚b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING LOURE, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 0315 0300 05AE 1D185 0062;00E0 05AE 1D185 0315 0062;0061 05AE 0300 1D185 0315 0062;00E0 05AE 1D185 0315 0062;0061 05AE 0300 1D185 0315 0062; # (a◌̕◌̀◌֮◌ð†…â—Œð†…b; à◌֮◌ð†…â—Œð†…◌̕b; a◌֮◌̀◌ð†…â—Œð†…◌̕b; à◌֮◌ð†…â—Œð†…◌̕b; a◌֮◌̀◌ð†…â—Œð†…◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, MUSICAL SYMBOL COMBINING DOIT, LATIN SMALL LETTER B
+0061 1D185 0315 0300 05AE 0062;0061 05AE 1D185 0300 0315 0062;0061 05AE 1D185 0300 0315 0062;0061 05AE 1D185 0300 0315 0062;0061 05AE 1D185 0300 0315 0062; # (aâ—Œð†…â—Œð†…◌̕◌̀◌֮b; a◌֮◌ð†…â—Œð†…◌̀◌̕b; a◌֮◌ð†…â—Œð†…◌̀◌̕b; a◌֮◌ð†…â—Œð†…◌̀◌̕b; a◌֮◌ð†…â—Œð†…◌̀◌̕b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING DOIT, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 1D186 0062;00E0 05AE 1D186 0315 0062;0061 05AE 0300 1D186 0315 0062;00E0 05AE 1D186 0315 0062;0061 05AE 0300 1D186 0315 0062; # (a◌̕◌̀◌֮◌ð††â—Œð††b; à◌֮◌ð††â—Œð††â—ŒÌ•b; a◌֮◌̀◌ð††â—Œð††â—ŒÌ•b; à◌֮◌ð††â—Œð††â—ŒÌ•b; a◌֮◌̀◌ð††â—Œð††â—ŒÌ•b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, MUSICAL SYMBOL COMBINING RIP, LATIN SMALL LETTER B
+0061 1D186 0315 0300 05AE 0062;0061 05AE 1D186 0300 0315 0062;0061 05AE 1D186 0300 0315 0062;0061 05AE 1D186 0300 0315 0062;0061 05AE 1D186 0300 0315 0062; # (aâ—Œð††â—Œð††â—ŒÌ•◌̀◌֮b; a◌֮◌ð††â—Œð††â—ŒÌ€â—ŒÌ•b; a◌֮◌ð††â—Œð††â—ŒÌ€â—ŒÌ•b; a◌֮◌ð††â—Œð††â—ŒÌ€â—ŒÌ•b; a◌֮◌ð††â—Œð††â—ŒÌ€â—ŒÌ•b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING RIP, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 1D187 0062;00E0 05AE 1D187 0315 0062;0061 05AE 0300 1D187 0315 0062;00E0 05AE 1D187 0315 0062;0061 05AE 0300 1D187 0315 0062; # (a◌̕◌̀◌֮◌ð†‡â—Œð†‡b; à◌֮◌ð†‡â—Œð†‡â—ŒÌ•b; a◌֮◌̀◌ð†‡â—Œð†‡â—ŒÌ•b; à◌֮◌ð†‡â—Œð†‡â—ŒÌ•b; a◌֮◌̀◌ð†‡â—Œð†‡â—ŒÌ•b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, MUSICAL SYMBOL COMBINING FLIP, LATIN SMALL LETTER B
+0061 1D187 0315 0300 05AE 0062;0061 05AE 1D187 0300 0315 0062;0061 05AE 1D187 0300 0315 0062;0061 05AE 1D187 0300 0315 0062;0061 05AE 1D187 0300 0315 0062; # (aâ—Œð†‡â—Œð†‡â—ŒÌ•◌̀◌֮b; a◌֮◌ð†‡â—Œð†‡â—ŒÌ€â—ŒÌ•b; a◌֮◌ð†‡â—Œð†‡â—ŒÌ€â—ŒÌ•b; a◌֮◌ð†‡â—Œð†‡â—ŒÌ€â—ŒÌ•b; a◌֮◌ð†‡â—Œð†‡â—ŒÌ€â—ŒÌ•b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING FLIP, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 1D188 0062;00E0 05AE 1D188 0315 0062;0061 05AE 0300 1D188 0315 0062;00E0 05AE 1D188 0315 0062;0061 05AE 0300 1D188 0315 0062; # (a◌̕◌̀◌֮◌ð†ˆâ—Œð†ˆb; à◌֮◌ð†ˆâ—Œð†ˆâ—ŒÌ•b; a◌֮◌̀◌ð†ˆâ—Œð†ˆâ—ŒÌ•b; à◌֮◌ð†ˆâ—Œð†ˆâ—ŒÌ•b; a◌֮◌̀◌ð†ˆâ—Œð†ˆâ—ŒÌ•b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, MUSICAL SYMBOL COMBINING SMEAR, LATIN SMALL LETTER B
+0061 1D188 0315 0300 05AE 0062;0061 05AE 1D188 0300 0315 0062;0061 05AE 1D188 0300 0315 0062;0061 05AE 1D188 0300 0315 0062;0061 05AE 1D188 0300 0315 0062; # (aâ—Œð†ˆâ—Œð†ˆâ—ŒÌ•◌̀◌֮b; a◌֮◌ð†ˆâ—Œð†ˆâ—ŒÌ€â—ŒÌ•b; a◌֮◌ð†ˆâ—Œð†ˆâ—ŒÌ€â—ŒÌ•b; a◌֮◌ð†ˆâ—Œð†ˆâ—ŒÌ€â—ŒÌ•b; a◌֮◌ð†ˆâ—Œð†ˆâ—ŒÌ€â—ŒÌ•b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING SMEAR, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 1D189 0062;00E0 05AE 1D189 0315 0062;0061 05AE 0300 1D189 0315 0062;00E0 05AE 1D189 0315 0062;0061 05AE 0300 1D189 0315 0062; # (a◌̕◌̀◌֮◌ð†‰â—Œð†‰b; à◌֮◌ð†‰â—Œð†‰â—ŒÌ•b; a◌֮◌̀◌ð†‰â—Œð†‰â—ŒÌ•b; à◌֮◌ð†‰â—Œð†‰â—ŒÌ•b; a◌֮◌̀◌ð†‰â—Œð†‰â—ŒÌ•b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, MUSICAL SYMBOL COMBINING BEND, LATIN SMALL LETTER B
+0061 1D189 0315 0300 05AE 0062;0061 05AE 1D189 0300 0315 0062;0061 05AE 1D189 0300 0315 0062;0061 05AE 1D189 0300 0315 0062;0061 05AE 1D189 0300 0315 0062; # (aâ—Œð†‰â—Œð†‰â—ŒÌ•◌̀◌֮b; a◌֮◌ð†‰â—Œð†‰â—ŒÌ€â—ŒÌ•b; a◌֮◌ð†‰â—Œð†‰â—ŒÌ€â—ŒÌ•b; a◌֮◌ð†‰â—Œð†‰â—ŒÌ€â—ŒÌ•b; a◌֮◌ð†‰â—Œð†‰â—ŒÌ€â—ŒÌ•b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING BEND, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 059A 0316 302A 1D18A 0062;0061 302A 0316 1D18A 059A 0062;0061 302A 0316 1D18A 059A 0062;0061 302A 0316 1D18A 059A 0062;0061 302A 0316 1D18A 059A 0062; # (a◌֚◌̖◌〪◌ð†Šâ—Œð†Šb; a◌〪◌̖◌ð†Šâ—Œð†Šâ—ŒÖšb; a◌〪◌̖◌ð†Šâ—Œð†Šâ—ŒÖšb; a◌〪◌̖◌ð†Šâ—Œð†Šâ—ŒÖšb; a◌〪◌̖◌ð†Šâ—Œð†Šâ—ŒÖšb; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, MUSICAL SYMBOL COMBINING DOUBLE TONGUE, LATIN SMALL LETTER B
+0061 1D18A 059A 0316 302A 0062;0061 302A 1D18A 0316 059A 0062;0061 302A 1D18A 0316 059A 0062;0061 302A 1D18A 0316 059A 0062;0061 302A 1D18A 0316 059A 0062; # (aâ—Œð†Šâ—Œð†Šâ—ŒÖšâ—ŒÌ–◌〪b; a◌〪◌ð†Šâ—Œð†Šâ—ŒÌ–◌֚b; a◌〪◌ð†Šâ—Œð†Šâ—ŒÌ–◌֚b; a◌〪◌ð†Šâ—Œð†Šâ—ŒÌ–◌֚b; a◌〪◌ð†Šâ—Œð†Šâ—ŒÌ–◌֚b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING DOUBLE TONGUE, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 059A 0316 302A 1D18B 0062;0061 302A 0316 1D18B 059A 0062;0061 302A 0316 1D18B 059A 0062;0061 302A 0316 1D18B 059A 0062;0061 302A 0316 1D18B 059A 0062; # (a◌֚◌̖◌〪◌ð†‹â—Œð†‹b; a◌〪◌̖◌ð†‹â—Œð†‹â—ŒÖšb; a◌〪◌̖◌ð†‹â—Œð†‹â—ŒÖšb; a◌〪◌̖◌ð†‹â—Œð†‹â—ŒÖšb; a◌〪◌̖◌ð†‹â—Œð†‹â—ŒÖšb; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, MUSICAL SYMBOL COMBINING TRIPLE TONGUE, LATIN SMALL LETTER B
+0061 1D18B 059A 0316 302A 0062;0061 302A 1D18B 0316 059A 0062;0061 302A 1D18B 0316 059A 0062;0061 302A 1D18B 0316 059A 0062;0061 302A 1D18B 0316 059A 0062; # (aâ—Œð†‹â—Œð†‹â—ŒÖšâ—ŒÌ–◌〪b; a◌〪◌ð†‹â—Œð†‹â—ŒÌ–◌֚b; a◌〪◌ð†‹â—Œð†‹â—ŒÌ–◌֚b; a◌〪◌ð†‹â—Œð†‹â—ŒÌ–◌֚b; a◌〪◌ð†‹â—Œð†‹â—ŒÌ–◌֚b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING TRIPLE TONGUE, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
+0061 0315 0300 05AE 1D1AA 0062;00E0 05AE 1D1AA 0315 0062;0061 05AE 0300 1D1AA 0315 0062;00E0 05AE 1D1AA 0315 0062;0061 05AE 0300 1D1AA 0315 0062; # (a◌̕◌̀◌֮◌ð†ªâ—Œð†ªb; à◌֮◌ð†ªâ—Œð†ªâ—ŒÌ•b; a◌֮◌̀◌ð†ªâ—Œð†ªâ—ŒÌ•b; à◌֮◌ð†ªâ—Œð†ªâ—ŒÌ•b; a◌֮◌̀◌ð†ªâ—Œð†ªâ—ŒÌ•b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, MUSICAL SYMBOL COMBINING DOWN BOW, LATIN SMALL LETTER B
+0061 1D1AA 0315 0300 05AE 0062;0061 05AE 1D1AA 0300 0315 0062;0061 05AE 1D1AA 0300 0315 0062;0061 05AE 1D1AA 0300 0315 0062;0061 05AE 1D1AA 0300 0315 0062; # (aâ—Œð†ªâ—Œð†ªâ—ŒÌ•◌̀◌֮b; a◌֮◌ð†ªâ—Œð†ªâ—ŒÌ€â—ŒÌ•b; a◌֮◌ð†ªâ—Œð†ªâ—ŒÌ€â—ŒÌ•b; a◌֮◌ð†ªâ—Œð†ªâ—ŒÌ€â—ŒÌ•b; a◌֮◌ð†ªâ—Œð†ªâ—ŒÌ€â—ŒÌ•b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING DOWN BOW, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 1D1AB 0062;00E0 05AE 1D1AB 0315 0062;0061 05AE 0300 1D1AB 0315 0062;00E0 05AE 1D1AB 0315 0062;0061 05AE 0300 1D1AB 0315 0062; # (a◌̕◌̀◌֮◌ð†«â—Œð†«b; à◌֮◌ð†«â—Œð†«â—ŒÌ•b; a◌֮◌̀◌ð†«â—Œð†«â—ŒÌ•b; à◌֮◌ð†«â—Œð†«â—ŒÌ•b; a◌֮◌̀◌ð†«â—Œð†«â—ŒÌ•b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, MUSICAL SYMBOL COMBINING UP BOW, LATIN SMALL LETTER B
+0061 1D1AB 0315 0300 05AE 0062;0061 05AE 1D1AB 0300 0315 0062;0061 05AE 1D1AB 0300 0315 0062;0061 05AE 1D1AB 0300 0315 0062;0061 05AE 1D1AB 0300 0315 0062; # (aâ—Œð†«â—Œð†«â—ŒÌ•◌̀◌֮b; a◌֮◌ð†«â—Œð†«â—ŒÌ€â—ŒÌ•b; a◌֮◌ð†«â—Œð†«â—ŒÌ€â—ŒÌ•b; a◌֮◌ð†«â—Œð†«â—ŒÌ€â—ŒÌ•b; a◌֮◌ð†«â—Œð†«â—ŒÌ€â—ŒÌ•b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING UP BOW, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 1D1AC 0062;00E0 05AE 1D1AC 0315 0062;0061 05AE 0300 1D1AC 0315 0062;00E0 05AE 1D1AC 0315 0062;0061 05AE 0300 1D1AC 0315 0062; # (a◌̕◌̀◌֮◌ð†¬â—Œð†¬b; à◌֮◌ð†¬â—Œð†¬â—ŒÌ•b; a◌֮◌̀◌ð†¬â—Œð†¬â—ŒÌ•b; à◌֮◌ð†¬â—Œð†¬â—ŒÌ•b; a◌֮◌̀◌ð†¬â—Œð†¬â—ŒÌ•b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, MUSICAL SYMBOL COMBINING HARMONIC, LATIN SMALL LETTER B
+0061 1D1AC 0315 0300 05AE 0062;0061 05AE 1D1AC 0300 0315 0062;0061 05AE 1D1AC 0300 0315 0062;0061 05AE 1D1AC 0300 0315 0062;0061 05AE 1D1AC 0300 0315 0062; # (aâ—Œð†¬â—Œð†¬â—ŒÌ•◌̀◌֮b; a◌֮◌ð†¬â—Œð†¬â—ŒÌ€â—ŒÌ•b; a◌֮◌ð†¬â—Œð†¬â—ŒÌ€â—ŒÌ•b; a◌֮◌ð†¬â—Œð†¬â—ŒÌ€â—ŒÌ•b; a◌֮◌ð†¬â—Œð†¬â—ŒÌ€â—ŒÌ•b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING HARMONIC, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 1D1AD 0062;00E0 05AE 1D1AD 0315 0062;0061 05AE 0300 1D1AD 0315 0062;00E0 05AE 1D1AD 0315 0062;0061 05AE 0300 1D1AD 0315 0062; # (a◌̕◌̀◌֮◌ð†­â—Œð†­b; à◌֮◌ð†­â—Œð†­â—ŒÌ•b; a◌֮◌̀◌ð†­â—Œð†­â—ŒÌ•b; à◌֮◌ð†­â—Œð†­â—ŒÌ•b; a◌֮◌̀◌ð†­â—Œð†­â—ŒÌ•b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, MUSICAL SYMBOL COMBINING SNAP PIZZICATO, LATIN SMALL LETTER B
+0061 1D1AD 0315 0300 05AE 0062;0061 05AE 1D1AD 0300 0315 0062;0061 05AE 1D1AD 0300 0315 0062;0061 05AE 1D1AD 0300 0315 0062;0061 05AE 1D1AD 0300 0315 0062; # (aâ—Œð†­â—Œð†­â—ŒÌ•◌̀◌֮b; a◌֮◌ð†­â—Œð†­â—ŒÌ€â—ŒÌ•b; a◌֮◌ð†­â—Œð†­â—ŒÌ€â—ŒÌ•b; a◌֮◌ð†­â—Œð†­â—ŒÌ€â—ŒÌ•b; a◌֮◌ð†­â—Œð†­â—ŒÌ€â—ŒÌ•b; ) LATIN SMALL LETTER A, MUSICAL SYMBOL COMBINING SNAP PIZZICATO, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+#
+# END OF FILE
diff --git a/source4/heimdal/lib/wind/UnicodeData.py b/source4/heimdal/lib/wind/UnicodeData.py
new file mode 100644
index 0000000000..dd6dfa745d
--- /dev/null
+++ b/source4/heimdal/lib/wind/UnicodeData.py
@@ -0,0 +1,57 @@
+#!/usr/local/bin/python
+# -*- coding: iso-8859-1 -*-
+
+# $Id$
+
+# Copyright (c) 2004 Kungliga Tekniska Högskolan
+# (Royal Institute of Technology, Stockholm, Sweden).
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the Institute nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+import re
+import string
+
+def read(filename):
+ """return a dict of unicode characters"""
+ ud = open(filename, 'r')
+ ret = {}
+ while True:
+ l = ud.readline()
+ if not l:
+ break
+ l = re.sub('#.*$', '', l)
+ if l == "\n":
+ continue
+ f = l.split(';')
+ key = int(f[0], 0x10)
+ if key in ret:
+ raise Exception('Duplicate key in UnicodeData')
+ ret[key] = f[1:]
+ ud.close()
+ return ret
diff --git a/source4/heimdal/lib/wind/UnicodeData.txt b/source4/heimdal/lib/wind/UnicodeData.txt
new file mode 100644
index 0000000000..3710e17294
--- /dev/null
+++ b/source4/heimdal/lib/wind/UnicodeData.txt
@@ -0,0 +1,15100 @@
+0000;<control>;Cc;0;BN;;;;;N;NULL;;;;
+0001;<control>;Cc;0;BN;;;;;N;START OF HEADING;;;;
+0002;<control>;Cc;0;BN;;;;;N;START OF TEXT;;;;
+0003;<control>;Cc;0;BN;;;;;N;END OF TEXT;;;;
+0004;<control>;Cc;0;BN;;;;;N;END OF TRANSMISSION;;;;
+0005;<control>;Cc;0;BN;;;;;N;ENQUIRY;;;;
+0006;<control>;Cc;0;BN;;;;;N;ACKNOWLEDGE;;;;
+0007;<control>;Cc;0;BN;;;;;N;BELL;;;;
+0008;<control>;Cc;0;BN;;;;;N;BACKSPACE;;;;
+0009;<control>;Cc;0;S;;;;;N;CHARACTER TABULATION;;;;
+000A;<control>;Cc;0;B;;;;;N;LINE FEED (LF);;;;
+000B;<control>;Cc;0;S;;;;;N;LINE TABULATION;;;;
+000C;<control>;Cc;0;WS;;;;;N;FORM FEED (FF);;;;
+000D;<control>;Cc;0;B;;;;;N;CARRIAGE RETURN (CR);;;;
+000E;<control>;Cc;0;BN;;;;;N;SHIFT OUT;;;;
+000F;<control>;Cc;0;BN;;;;;N;SHIFT IN;;;;
+0010;<control>;Cc;0;BN;;;;;N;DATA LINK ESCAPE;;;;
+0011;<control>;Cc;0;BN;;;;;N;DEVICE CONTROL ONE;;;;
+0012;<control>;Cc;0;BN;;;;;N;DEVICE CONTROL TWO;;;;
+0013;<control>;Cc;0;BN;;;;;N;DEVICE CONTROL THREE;;;;
+0014;<control>;Cc;0;BN;;;;;N;DEVICE CONTROL FOUR;;;;
+0015;<control>;Cc;0;BN;;;;;N;NEGATIVE ACKNOWLEDGE;;;;
+0016;<control>;Cc;0;BN;;;;;N;SYNCHRONOUS IDLE;;;;
+0017;<control>;Cc;0;BN;;;;;N;END OF TRANSMISSION BLOCK;;;;
+0018;<control>;Cc;0;BN;;;;;N;CANCEL;;;;
+0019;<control>;Cc;0;BN;;;;;N;END OF MEDIUM;;;;
+001A;<control>;Cc;0;BN;;;;;N;SUBSTITUTE;;;;
+001B;<control>;Cc;0;BN;;;;;N;ESCAPE;;;;
+001C;<control>;Cc;0;B;;;;;N;INFORMATION SEPARATOR FOUR;;;;
+001D;<control>;Cc;0;B;;;;;N;INFORMATION SEPARATOR THREE;;;;
+001E;<control>;Cc;0;B;;;;;N;INFORMATION SEPARATOR TWO;;;;
+001F;<control>;Cc;0;S;;;;;N;INFORMATION SEPARATOR ONE;;;;
+0020;SPACE;Zs;0;WS;;;;;N;;;;;
+0021;EXCLAMATION MARK;Po;0;ON;;;;;N;;;;;
+0022;QUOTATION MARK;Po;0;ON;;;;;N;;;;;
+0023;NUMBER SIGN;Po;0;ET;;;;;N;;;;;
+0024;DOLLAR SIGN;Sc;0;ET;;;;;N;;;;;
+0025;PERCENT SIGN;Po;0;ET;;;;;N;;;;;
+0026;AMPERSAND;Po;0;ON;;;;;N;;;;;
+0027;APOSTROPHE;Po;0;ON;;;;;N;APOSTROPHE-QUOTE;;;;
+0028;LEFT PARENTHESIS;Ps;0;ON;;;;;Y;OPENING PARENTHESIS;;;;
+0029;RIGHT PARENTHESIS;Pe;0;ON;;;;;Y;CLOSING PARENTHESIS;;;;
+002A;ASTERISK;Po;0;ON;;;;;N;;;;;
+002B;PLUS SIGN;Sm;0;ES;;;;;N;;;;;
+002C;COMMA;Po;0;CS;;;;;N;;;;;
+002D;HYPHEN-MINUS;Pd;0;ES;;;;;N;;;;;
+002E;FULL STOP;Po;0;CS;;;;;N;PERIOD;;;;
+002F;SOLIDUS;Po;0;CS;;;;;N;SLASH;;;;
+0030;DIGIT ZERO;Nd;0;EN;;0;0;0;N;;;;;
+0031;DIGIT ONE;Nd;0;EN;;1;1;1;N;;;;;
+0032;DIGIT TWO;Nd;0;EN;;2;2;2;N;;;;;
+0033;DIGIT THREE;Nd;0;EN;;3;3;3;N;;;;;
+0034;DIGIT FOUR;Nd;0;EN;;4;4;4;N;;;;;
+0035;DIGIT FIVE;Nd;0;EN;;5;5;5;N;;;;;
+0036;DIGIT SIX;Nd;0;EN;;6;6;6;N;;;;;
+0037;DIGIT SEVEN;Nd;0;EN;;7;7;7;N;;;;;
+0038;DIGIT EIGHT;Nd;0;EN;;8;8;8;N;;;;;
+0039;DIGIT NINE;Nd;0;EN;;9;9;9;N;;;;;
+003A;COLON;Po;0;CS;;;;;N;;;;;
+003B;SEMICOLON;Po;0;ON;;;;;N;;;;;
+003C;LESS-THAN SIGN;Sm;0;ON;;;;;Y;;;;;
+003D;EQUALS SIGN;Sm;0;ON;;;;;N;;;;;
+003E;GREATER-THAN SIGN;Sm;0;ON;;;;;Y;;;;;
+003F;QUESTION MARK;Po;0;ON;;;;;N;;;;;
+0040;COMMERCIAL AT;Po;0;ON;;;;;N;;;;;
+0041;LATIN CAPITAL LETTER A;Lu;0;L;;;;;N;;;;0061;
+0042;LATIN CAPITAL LETTER B;Lu;0;L;;;;;N;;;;0062;
+0043;LATIN CAPITAL LETTER C;Lu;0;L;;;;;N;;;;0063;
+0044;LATIN CAPITAL LETTER D;Lu;0;L;;;;;N;;;;0064;
+0045;LATIN CAPITAL LETTER E;Lu;0;L;;;;;N;;;;0065;
+0046;LATIN CAPITAL LETTER F;Lu;0;L;;;;;N;;;;0066;
+0047;LATIN CAPITAL LETTER G;Lu;0;L;;;;;N;;;;0067;
+0048;LATIN CAPITAL LETTER H;Lu;0;L;;;;;N;;;;0068;
+0049;LATIN CAPITAL LETTER I;Lu;0;L;;;;;N;;;;0069;
+004A;LATIN CAPITAL LETTER J;Lu;0;L;;;;;N;;;;006A;
+004B;LATIN CAPITAL LETTER K;Lu;0;L;;;;;N;;;;006B;
+004C;LATIN CAPITAL LETTER L;Lu;0;L;;;;;N;;;;006C;
+004D;LATIN CAPITAL LETTER M;Lu;0;L;;;;;N;;;;006D;
+004E;LATIN CAPITAL LETTER N;Lu;0;L;;;;;N;;;;006E;
+004F;LATIN CAPITAL LETTER O;Lu;0;L;;;;;N;;;;006F;
+0050;LATIN CAPITAL LETTER P;Lu;0;L;;;;;N;;;;0070;
+0051;LATIN CAPITAL LETTER Q;Lu;0;L;;;;;N;;;;0071;
+0052;LATIN CAPITAL LETTER R;Lu;0;L;;;;;N;;;;0072;
+0053;LATIN CAPITAL LETTER S;Lu;0;L;;;;;N;;;;0073;
+0054;LATIN CAPITAL LETTER T;Lu;0;L;;;;;N;;;;0074;
+0055;LATIN CAPITAL LETTER U;Lu;0;L;;;;;N;;;;0075;
+0056;LATIN CAPITAL LETTER V;Lu;0;L;;;;;N;;;;0076;
+0057;LATIN CAPITAL LETTER W;Lu;0;L;;;;;N;;;;0077;
+0058;LATIN CAPITAL LETTER X;Lu;0;L;;;;;N;;;;0078;
+0059;LATIN CAPITAL LETTER Y;Lu;0;L;;;;;N;;;;0079;
+005A;LATIN CAPITAL LETTER Z;Lu;0;L;;;;;N;;;;007A;
+005B;LEFT SQUARE BRACKET;Ps;0;ON;;;;;Y;OPENING SQUARE BRACKET;;;;
+005C;REVERSE SOLIDUS;Po;0;ON;;;;;N;BACKSLASH;;;;
+005D;RIGHT SQUARE BRACKET;Pe;0;ON;;;;;Y;CLOSING SQUARE BRACKET;;;;
+005E;CIRCUMFLEX ACCENT;Sk;0;ON;;;;;N;SPACING CIRCUMFLEX;;;;
+005F;LOW LINE;Pc;0;ON;;;;;N;SPACING UNDERSCORE;;;;
+0060;GRAVE ACCENT;Sk;0;ON;;;;;N;SPACING GRAVE;;;;
+0061;LATIN SMALL LETTER A;Ll;0;L;;;;;N;;;0041;;0041
+0062;LATIN SMALL LETTER B;Ll;0;L;;;;;N;;;0042;;0042
+0063;LATIN SMALL LETTER C;Ll;0;L;;;;;N;;;0043;;0043
+0064;LATIN SMALL LETTER D;Ll;0;L;;;;;N;;;0044;;0044
+0065;LATIN SMALL LETTER E;Ll;0;L;;;;;N;;;0045;;0045
+0066;LATIN SMALL LETTER F;Ll;0;L;;;;;N;;;0046;;0046
+0067;LATIN SMALL LETTER G;Ll;0;L;;;;;N;;;0047;;0047
+0068;LATIN SMALL LETTER H;Ll;0;L;;;;;N;;;0048;;0048
+0069;LATIN SMALL LETTER I;Ll;0;L;;;;;N;;;0049;;0049
+006A;LATIN SMALL LETTER J;Ll;0;L;;;;;N;;;004A;;004A
+006B;LATIN SMALL LETTER K;Ll;0;L;;;;;N;;;004B;;004B
+006C;LATIN SMALL LETTER L;Ll;0;L;;;;;N;;;004C;;004C
+006D;LATIN SMALL LETTER M;Ll;0;L;;;;;N;;;004D;;004D
+006E;LATIN SMALL LETTER N;Ll;0;L;;;;;N;;;004E;;004E
+006F;LATIN SMALL LETTER O;Ll;0;L;;;;;N;;;004F;;004F
+0070;LATIN SMALL LETTER P;Ll;0;L;;;;;N;;;0050;;0050
+0071;LATIN SMALL LETTER Q;Ll;0;L;;;;;N;;;0051;;0051
+0072;LATIN SMALL LETTER R;Ll;0;L;;;;;N;;;0052;;0052
+0073;LATIN SMALL LETTER S;Ll;0;L;;;;;N;;;0053;;0053
+0074;LATIN SMALL LETTER T;Ll;0;L;;;;;N;;;0054;;0054
+0075;LATIN SMALL LETTER U;Ll;0;L;;;;;N;;;0055;;0055
+0076;LATIN SMALL LETTER V;Ll;0;L;;;;;N;;;0056;;0056
+0077;LATIN SMALL LETTER W;Ll;0;L;;;;;N;;;0057;;0057
+0078;LATIN SMALL LETTER X;Ll;0;L;;;;;N;;;0058;;0058
+0079;LATIN SMALL LETTER Y;Ll;0;L;;;;;N;;;0059;;0059
+007A;LATIN SMALL LETTER Z;Ll;0;L;;;;;N;;;005A;;005A
+007B;LEFT CURLY BRACKET;Ps;0;ON;;;;;Y;OPENING CURLY BRACKET;;;;
+007C;VERTICAL LINE;Sm;0;ON;;;;;N;VERTICAL BAR;;;;
+007D;RIGHT CURLY BRACKET;Pe;0;ON;;;;;Y;CLOSING CURLY BRACKET;;;;
+007E;TILDE;Sm;0;ON;;;;;N;;;;;
+007F;<control>;Cc;0;BN;;;;;N;DELETE;;;;
+0080;<control>;Cc;0;BN;;;;;N;;;;;
+0081;<control>;Cc;0;BN;;;;;N;;;;;
+0082;<control>;Cc;0;BN;;;;;N;BREAK PERMITTED HERE;;;;
+0083;<control>;Cc;0;BN;;;;;N;NO BREAK HERE;;;;
+0084;<control>;Cc;0;BN;;;;;N;;;;;
+0085;<control>;Cc;0;B;;;;;N;NEXT LINE (NEL);;;;
+0086;<control>;Cc;0;BN;;;;;N;START OF SELECTED AREA;;;;
+0087;<control>;Cc;0;BN;;;;;N;END OF SELECTED AREA;;;;
+0088;<control>;Cc;0;BN;;;;;N;CHARACTER TABULATION SET;;;;
+0089;<control>;Cc;0;BN;;;;;N;CHARACTER TABULATION WITH JUSTIFICATION;;;;
+008A;<control>;Cc;0;BN;;;;;N;LINE TABULATION SET;;;;
+008B;<control>;Cc;0;BN;;;;;N;PARTIAL LINE FORWARD;;;;
+008C;<control>;Cc;0;BN;;;;;N;PARTIAL LINE BACKWARD;;;;
+008D;<control>;Cc;0;BN;;;;;N;REVERSE LINE FEED;;;;
+008E;<control>;Cc;0;BN;;;;;N;SINGLE SHIFT TWO;;;;
+008F;<control>;Cc;0;BN;;;;;N;SINGLE SHIFT THREE;;;;
+0090;<control>;Cc;0;BN;;;;;N;DEVICE CONTROL STRING;;;;
+0091;<control>;Cc;0;BN;;;;;N;PRIVATE USE ONE;;;;
+0092;<control>;Cc;0;BN;;;;;N;PRIVATE USE TWO;;;;
+0093;<control>;Cc;0;BN;;;;;N;SET TRANSMIT STATE;;;;
+0094;<control>;Cc;0;BN;;;;;N;CANCEL CHARACTER;;;;
+0095;<control>;Cc;0;BN;;;;;N;MESSAGE WAITING;;;;
+0096;<control>;Cc;0;BN;;;;;N;START OF GUARDED AREA;;;;
+0097;<control>;Cc;0;BN;;;;;N;END OF GUARDED AREA;;;;
+0098;<control>;Cc;0;BN;;;;;N;START OF STRING;;;;
+0099;<control>;Cc;0;BN;;;;;N;;;;;
+009A;<control>;Cc;0;BN;;;;;N;SINGLE CHARACTER INTRODUCER;;;;
+009B;<control>;Cc;0;BN;;;;;N;CONTROL SEQUENCE INTRODUCER;;;;
+009C;<control>;Cc;0;BN;;;;;N;STRING TERMINATOR;;;;
+009D;<control>;Cc;0;BN;;;;;N;OPERATING SYSTEM COMMAND;;;;
+009E;<control>;Cc;0;BN;;;;;N;PRIVACY MESSAGE;;;;
+009F;<control>;Cc;0;BN;;;;;N;APPLICATION PROGRAM COMMAND;;;;
+00A0;NO-BREAK SPACE;Zs;0;CS;<noBreak> 0020;;;;N;NON-BREAKING SPACE;;;;
+00A1;INVERTED EXCLAMATION MARK;Po;0;ON;;;;;N;;;;;
+00A2;CENT SIGN;Sc;0;ET;;;;;N;;;;;
+00A3;POUND SIGN;Sc;0;ET;;;;;N;;;;;
+00A4;CURRENCY SIGN;Sc;0;ET;;;;;N;;;;;
+00A5;YEN SIGN;Sc;0;ET;;;;;N;;;;;
+00A6;BROKEN BAR;So;0;ON;;;;;N;BROKEN VERTICAL BAR;;;;
+00A7;SECTION SIGN;So;0;ON;;;;;N;;;;;
+00A8;DIAERESIS;Sk;0;ON;<compat> 0020 0308;;;;N;SPACING DIAERESIS;;;;
+00A9;COPYRIGHT SIGN;So;0;ON;;;;;N;;;;;
+00AA;FEMININE ORDINAL INDICATOR;Ll;0;L;<super> 0061;;;;N;;;;;
+00AB;LEFT-POINTING DOUBLE ANGLE QUOTATION MARK;Pi;0;ON;;;;;Y;LEFT POINTING GUILLEMET;*;;;
+00AC;NOT SIGN;Sm;0;ON;;;;;N;;;;;
+00AD;SOFT HYPHEN;Cf;0;BN;;;;;N;;;;;
+00AE;REGISTERED SIGN;So;0;ON;;;;;N;REGISTERED TRADE MARK SIGN;;;;
+00AF;MACRON;Sk;0;ON;<compat> 0020 0304;;;;N;SPACING MACRON;;;;
+00B0;DEGREE SIGN;So;0;ET;;;;;N;;;;;
+00B1;PLUS-MINUS SIGN;Sm;0;ET;;;;;N;PLUS-OR-MINUS SIGN;;;;
+00B2;SUPERSCRIPT TWO;No;0;EN;<super> 0032;;2;2;N;SUPERSCRIPT DIGIT TWO;;;;
+00B3;SUPERSCRIPT THREE;No;0;EN;<super> 0033;;3;3;N;SUPERSCRIPT DIGIT THREE;;;;
+00B4;ACUTE ACCENT;Sk;0;ON;<compat> 0020 0301;;;;N;SPACING ACUTE;;;;
+00B5;MICRO SIGN;Ll;0;L;<compat> 03BC;;;;N;;;039C;;039C
+00B6;PILCROW SIGN;So;0;ON;;;;;N;PARAGRAPH SIGN;;;;
+00B7;MIDDLE DOT;Po;0;ON;;;;;N;;;;;
+00B8;CEDILLA;Sk;0;ON;<compat> 0020 0327;;;;N;SPACING CEDILLA;;;;
+00B9;SUPERSCRIPT ONE;No;0;EN;<super> 0031;;1;1;N;SUPERSCRIPT DIGIT ONE;;;;
+00BA;MASCULINE ORDINAL INDICATOR;Ll;0;L;<super> 006F;;;;N;;;;;
+00BB;RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK;Pf;0;ON;;;;;Y;RIGHT POINTING GUILLEMET;*;;;
+00BC;VULGAR FRACTION ONE QUARTER;No;0;ON;<fraction> 0031 2044 0034;;;1/4;N;FRACTION ONE QUARTER;;;;
+00BD;VULGAR FRACTION ONE HALF;No;0;ON;<fraction> 0031 2044 0032;;;1/2;N;FRACTION ONE HALF;;;;
+00BE;VULGAR FRACTION THREE QUARTERS;No;0;ON;<fraction> 0033 2044 0034;;;3/4;N;FRACTION THREE QUARTERS;;;;
+00BF;INVERTED QUESTION MARK;Po;0;ON;;;;;N;;;;;
+00C0;LATIN CAPITAL LETTER A WITH GRAVE;Lu;0;L;0041 0300;;;;N;LATIN CAPITAL LETTER A GRAVE;;;00E0;
+00C1;LATIN CAPITAL LETTER A WITH ACUTE;Lu;0;L;0041 0301;;;;N;LATIN CAPITAL LETTER A ACUTE;;;00E1;
+00C2;LATIN CAPITAL LETTER A WITH CIRCUMFLEX;Lu;0;L;0041 0302;;;;N;LATIN CAPITAL LETTER A CIRCUMFLEX;;;00E2;
+00C3;LATIN CAPITAL LETTER A WITH TILDE;Lu;0;L;0041 0303;;;;N;LATIN CAPITAL LETTER A TILDE;;;00E3;
+00C4;LATIN CAPITAL LETTER A WITH DIAERESIS;Lu;0;L;0041 0308;;;;N;LATIN CAPITAL LETTER A DIAERESIS;;;00E4;
+00C5;LATIN CAPITAL LETTER A WITH RING ABOVE;Lu;0;L;0041 030A;;;;N;LATIN CAPITAL LETTER A RING;;;00E5;
+00C6;LATIN CAPITAL LETTER AE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER A E;ash *;;00E6;
+00C7;LATIN CAPITAL LETTER C WITH CEDILLA;Lu;0;L;0043 0327;;;;N;LATIN CAPITAL LETTER C CEDILLA;;;00E7;
+00C8;LATIN CAPITAL LETTER E WITH GRAVE;Lu;0;L;0045 0300;;;;N;LATIN CAPITAL LETTER E GRAVE;;;00E8;
+00C9;LATIN CAPITAL LETTER E WITH ACUTE;Lu;0;L;0045 0301;;;;N;LATIN CAPITAL LETTER E ACUTE;;;00E9;
+00CA;LATIN CAPITAL LETTER E WITH CIRCUMFLEX;Lu;0;L;0045 0302;;;;N;LATIN CAPITAL LETTER E CIRCUMFLEX;;;00EA;
+00CB;LATIN CAPITAL LETTER E WITH DIAERESIS;Lu;0;L;0045 0308;;;;N;LATIN CAPITAL LETTER E DIAERESIS;;;00EB;
+00CC;LATIN CAPITAL LETTER I WITH GRAVE;Lu;0;L;0049 0300;;;;N;LATIN CAPITAL LETTER I GRAVE;;;00EC;
+00CD;LATIN CAPITAL LETTER I WITH ACUTE;Lu;0;L;0049 0301;;;;N;LATIN CAPITAL LETTER I ACUTE;;;00ED;
+00CE;LATIN CAPITAL LETTER I WITH CIRCUMFLEX;Lu;0;L;0049 0302;;;;N;LATIN CAPITAL LETTER I CIRCUMFLEX;;;00EE;
+00CF;LATIN CAPITAL LETTER I WITH DIAERESIS;Lu;0;L;0049 0308;;;;N;LATIN CAPITAL LETTER I DIAERESIS;;;00EF;
+00D0;LATIN CAPITAL LETTER ETH;Lu;0;L;;;;;N;;Icelandic;;00F0;
+00D1;LATIN CAPITAL LETTER N WITH TILDE;Lu;0;L;004E 0303;;;;N;LATIN CAPITAL LETTER N TILDE;;;00F1;
+00D2;LATIN CAPITAL LETTER O WITH GRAVE;Lu;0;L;004F 0300;;;;N;LATIN CAPITAL LETTER O GRAVE;;;00F2;
+00D3;LATIN CAPITAL LETTER O WITH ACUTE;Lu;0;L;004F 0301;;;;N;LATIN CAPITAL LETTER O ACUTE;;;00F3;
+00D4;LATIN CAPITAL LETTER O WITH CIRCUMFLEX;Lu;0;L;004F 0302;;;;N;LATIN CAPITAL LETTER O CIRCUMFLEX;;;00F4;
+00D5;LATIN CAPITAL LETTER O WITH TILDE;Lu;0;L;004F 0303;;;;N;LATIN CAPITAL LETTER O TILDE;;;00F5;
+00D6;LATIN CAPITAL LETTER O WITH DIAERESIS;Lu;0;L;004F 0308;;;;N;LATIN CAPITAL LETTER O DIAERESIS;;;00F6;
+00D7;MULTIPLICATION SIGN;Sm;0;ON;;;;;N;;;;;
+00D8;LATIN CAPITAL LETTER O WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER O SLASH;;;00F8;
+00D9;LATIN CAPITAL LETTER U WITH GRAVE;Lu;0;L;0055 0300;;;;N;LATIN CAPITAL LETTER U GRAVE;;;00F9;
+00DA;LATIN CAPITAL LETTER U WITH ACUTE;Lu;0;L;0055 0301;;;;N;LATIN CAPITAL LETTER U ACUTE;;;00FA;
+00DB;LATIN CAPITAL LETTER U WITH CIRCUMFLEX;Lu;0;L;0055 0302;;;;N;LATIN CAPITAL LETTER U CIRCUMFLEX;;;00FB;
+00DC;LATIN CAPITAL LETTER U WITH DIAERESIS;Lu;0;L;0055 0308;;;;N;LATIN CAPITAL LETTER U DIAERESIS;;;00FC;
+00DD;LATIN CAPITAL LETTER Y WITH ACUTE;Lu;0;L;0059 0301;;;;N;LATIN CAPITAL LETTER Y ACUTE;;;00FD;
+00DE;LATIN CAPITAL LETTER THORN;Lu;0;L;;;;;N;;Icelandic;;00FE;
+00DF;LATIN SMALL LETTER SHARP S;Ll;0;L;;;;;N;;German;;;
+00E0;LATIN SMALL LETTER A WITH GRAVE;Ll;0;L;0061 0300;;;;N;LATIN SMALL LETTER A GRAVE;;00C0;;00C0
+00E1;LATIN SMALL LETTER A WITH ACUTE;Ll;0;L;0061 0301;;;;N;LATIN SMALL LETTER A ACUTE;;00C1;;00C1
+00E2;LATIN SMALL LETTER A WITH CIRCUMFLEX;Ll;0;L;0061 0302;;;;N;LATIN SMALL LETTER A CIRCUMFLEX;;00C2;;00C2
+00E3;LATIN SMALL LETTER A WITH TILDE;Ll;0;L;0061 0303;;;;N;LATIN SMALL LETTER A TILDE;;00C3;;00C3
+00E4;LATIN SMALL LETTER A WITH DIAERESIS;Ll;0;L;0061 0308;;;;N;LATIN SMALL LETTER A DIAERESIS;;00C4;;00C4
+00E5;LATIN SMALL LETTER A WITH RING ABOVE;Ll;0;L;0061 030A;;;;N;LATIN SMALL LETTER A RING;;00C5;;00C5
+00E6;LATIN SMALL LETTER AE;Ll;0;L;;;;;N;LATIN SMALL LETTER A E;ash *;00C6;;00C6
+00E7;LATIN SMALL LETTER C WITH CEDILLA;Ll;0;L;0063 0327;;;;N;LATIN SMALL LETTER C CEDILLA;;00C7;;00C7
+00E8;LATIN SMALL LETTER E WITH GRAVE;Ll;0;L;0065 0300;;;;N;LATIN SMALL LETTER E GRAVE;;00C8;;00C8
+00E9;LATIN SMALL LETTER E WITH ACUTE;Ll;0;L;0065 0301;;;;N;LATIN SMALL LETTER E ACUTE;;00C9;;00C9
+00EA;LATIN SMALL LETTER E WITH CIRCUMFLEX;Ll;0;L;0065 0302;;;;N;LATIN SMALL LETTER E CIRCUMFLEX;;00CA;;00CA
+00EB;LATIN SMALL LETTER E WITH DIAERESIS;Ll;0;L;0065 0308;;;;N;LATIN SMALL LETTER E DIAERESIS;;00CB;;00CB
+00EC;LATIN SMALL LETTER I WITH GRAVE;Ll;0;L;0069 0300;;;;N;LATIN SMALL LETTER I GRAVE;;00CC;;00CC
+00ED;LATIN SMALL LETTER I WITH ACUTE;Ll;0;L;0069 0301;;;;N;LATIN SMALL LETTER I ACUTE;;00CD;;00CD
+00EE;LATIN SMALL LETTER I WITH CIRCUMFLEX;Ll;0;L;0069 0302;;;;N;LATIN SMALL LETTER I CIRCUMFLEX;;00CE;;00CE
+00EF;LATIN SMALL LETTER I WITH DIAERESIS;Ll;0;L;0069 0308;;;;N;LATIN SMALL LETTER I DIAERESIS;;00CF;;00CF
+00F0;LATIN SMALL LETTER ETH;Ll;0;L;;;;;N;;Icelandic;00D0;;00D0
+00F1;LATIN SMALL LETTER N WITH TILDE;Ll;0;L;006E 0303;;;;N;LATIN SMALL LETTER N TILDE;;00D1;;00D1
+00F2;LATIN SMALL LETTER O WITH GRAVE;Ll;0;L;006F 0300;;;;N;LATIN SMALL LETTER O GRAVE;;00D2;;00D2
+00F3;LATIN SMALL LETTER O WITH ACUTE;Ll;0;L;006F 0301;;;;N;LATIN SMALL LETTER O ACUTE;;00D3;;00D3
+00F4;LATIN SMALL LETTER O WITH CIRCUMFLEX;Ll;0;L;006F 0302;;;;N;LATIN SMALL LETTER O CIRCUMFLEX;;00D4;;00D4
+00F5;LATIN SMALL LETTER O WITH TILDE;Ll;0;L;006F 0303;;;;N;LATIN SMALL LETTER O TILDE;;00D5;;00D5
+00F6;LATIN SMALL LETTER O WITH DIAERESIS;Ll;0;L;006F 0308;;;;N;LATIN SMALL LETTER O DIAERESIS;;00D6;;00D6
+00F7;DIVISION SIGN;Sm;0;ON;;;;;N;;;;;
+00F8;LATIN SMALL LETTER O WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER O SLASH;;00D8;;00D8
+00F9;LATIN SMALL LETTER U WITH GRAVE;Ll;0;L;0075 0300;;;;N;LATIN SMALL LETTER U GRAVE;;00D9;;00D9
+00FA;LATIN SMALL LETTER U WITH ACUTE;Ll;0;L;0075 0301;;;;N;LATIN SMALL LETTER U ACUTE;;00DA;;00DA
+00FB;LATIN SMALL LETTER U WITH CIRCUMFLEX;Ll;0;L;0075 0302;;;;N;LATIN SMALL LETTER U CIRCUMFLEX;;00DB;;00DB
+00FC;LATIN SMALL LETTER U WITH DIAERESIS;Ll;0;L;0075 0308;;;;N;LATIN SMALL LETTER U DIAERESIS;;00DC;;00DC
+00FD;LATIN SMALL LETTER Y WITH ACUTE;Ll;0;L;0079 0301;;;;N;LATIN SMALL LETTER Y ACUTE;;00DD;;00DD
+00FE;LATIN SMALL LETTER THORN;Ll;0;L;;;;;N;;Icelandic;00DE;;00DE
+00FF;LATIN SMALL LETTER Y WITH DIAERESIS;Ll;0;L;0079 0308;;;;N;LATIN SMALL LETTER Y DIAERESIS;;0178;;0178
+0100;LATIN CAPITAL LETTER A WITH MACRON;Lu;0;L;0041 0304;;;;N;LATIN CAPITAL LETTER A MACRON;;;0101;
+0101;LATIN SMALL LETTER A WITH MACRON;Ll;0;L;0061 0304;;;;N;LATIN SMALL LETTER A MACRON;;0100;;0100
+0102;LATIN CAPITAL LETTER A WITH BREVE;Lu;0;L;0041 0306;;;;N;LATIN CAPITAL LETTER A BREVE;;;0103;
+0103;LATIN SMALL LETTER A WITH BREVE;Ll;0;L;0061 0306;;;;N;LATIN SMALL LETTER A BREVE;;0102;;0102
+0104;LATIN CAPITAL LETTER A WITH OGONEK;Lu;0;L;0041 0328;;;;N;LATIN CAPITAL LETTER A OGONEK;;;0105;
+0105;LATIN SMALL LETTER A WITH OGONEK;Ll;0;L;0061 0328;;;;N;LATIN SMALL LETTER A OGONEK;;0104;;0104
+0106;LATIN CAPITAL LETTER C WITH ACUTE;Lu;0;L;0043 0301;;;;N;LATIN CAPITAL LETTER C ACUTE;;;0107;
+0107;LATIN SMALL LETTER C WITH ACUTE;Ll;0;L;0063 0301;;;;N;LATIN SMALL LETTER C ACUTE;;0106;;0106
+0108;LATIN CAPITAL LETTER C WITH CIRCUMFLEX;Lu;0;L;0043 0302;;;;N;LATIN CAPITAL LETTER C CIRCUMFLEX;;;0109;
+0109;LATIN SMALL LETTER C WITH CIRCUMFLEX;Ll;0;L;0063 0302;;;;N;LATIN SMALL LETTER C CIRCUMFLEX;;0108;;0108
+010A;LATIN CAPITAL LETTER C WITH DOT ABOVE;Lu;0;L;0043 0307;;;;N;LATIN CAPITAL LETTER C DOT;;;010B;
+010B;LATIN SMALL LETTER C WITH DOT ABOVE;Ll;0;L;0063 0307;;;;N;LATIN SMALL LETTER C DOT;;010A;;010A
+010C;LATIN CAPITAL LETTER C WITH CARON;Lu;0;L;0043 030C;;;;N;LATIN CAPITAL LETTER C HACEK;;;010D;
+010D;LATIN SMALL LETTER C WITH CARON;Ll;0;L;0063 030C;;;;N;LATIN SMALL LETTER C HACEK;;010C;;010C
+010E;LATIN CAPITAL LETTER D WITH CARON;Lu;0;L;0044 030C;;;;N;LATIN CAPITAL LETTER D HACEK;;;010F;
+010F;LATIN SMALL LETTER D WITH CARON;Ll;0;L;0064 030C;;;;N;LATIN SMALL LETTER D HACEK;;010E;;010E
+0110;LATIN CAPITAL LETTER D WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER D BAR;;;0111;
+0111;LATIN SMALL LETTER D WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER D BAR;;0110;;0110
+0112;LATIN CAPITAL LETTER E WITH MACRON;Lu;0;L;0045 0304;;;;N;LATIN CAPITAL LETTER E MACRON;;;0113;
+0113;LATIN SMALL LETTER E WITH MACRON;Ll;0;L;0065 0304;;;;N;LATIN SMALL LETTER E MACRON;;0112;;0112
+0114;LATIN CAPITAL LETTER E WITH BREVE;Lu;0;L;0045 0306;;;;N;LATIN CAPITAL LETTER E BREVE;;;0115;
+0115;LATIN SMALL LETTER E WITH BREVE;Ll;0;L;0065 0306;;;;N;LATIN SMALL LETTER E BREVE;;0114;;0114
+0116;LATIN CAPITAL LETTER E WITH DOT ABOVE;Lu;0;L;0045 0307;;;;N;LATIN CAPITAL LETTER E DOT;;;0117;
+0117;LATIN SMALL LETTER E WITH DOT ABOVE;Ll;0;L;0065 0307;;;;N;LATIN SMALL LETTER E DOT;;0116;;0116
+0118;LATIN CAPITAL LETTER E WITH OGONEK;Lu;0;L;0045 0328;;;;N;LATIN CAPITAL LETTER E OGONEK;;;0119;
+0119;LATIN SMALL LETTER E WITH OGONEK;Ll;0;L;0065 0328;;;;N;LATIN SMALL LETTER E OGONEK;;0118;;0118
+011A;LATIN CAPITAL LETTER E WITH CARON;Lu;0;L;0045 030C;;;;N;LATIN CAPITAL LETTER E HACEK;;;011B;
+011B;LATIN SMALL LETTER E WITH CARON;Ll;0;L;0065 030C;;;;N;LATIN SMALL LETTER E HACEK;;011A;;011A
+011C;LATIN CAPITAL LETTER G WITH CIRCUMFLEX;Lu;0;L;0047 0302;;;;N;LATIN CAPITAL LETTER G CIRCUMFLEX;;;011D;
+011D;LATIN SMALL LETTER G WITH CIRCUMFLEX;Ll;0;L;0067 0302;;;;N;LATIN SMALL LETTER G CIRCUMFLEX;;011C;;011C
+011E;LATIN CAPITAL LETTER G WITH BREVE;Lu;0;L;0047 0306;;;;N;LATIN CAPITAL LETTER G BREVE;;;011F;
+011F;LATIN SMALL LETTER G WITH BREVE;Ll;0;L;0067 0306;;;;N;LATIN SMALL LETTER G BREVE;;011E;;011E
+0120;LATIN CAPITAL LETTER G WITH DOT ABOVE;Lu;0;L;0047 0307;;;;N;LATIN CAPITAL LETTER G DOT;;;0121;
+0121;LATIN SMALL LETTER G WITH DOT ABOVE;Ll;0;L;0067 0307;;;;N;LATIN SMALL LETTER G DOT;;0120;;0120
+0122;LATIN CAPITAL LETTER G WITH CEDILLA;Lu;0;L;0047 0327;;;;N;LATIN CAPITAL LETTER G CEDILLA;;;0123;
+0123;LATIN SMALL LETTER G WITH CEDILLA;Ll;0;L;0067 0327;;;;N;LATIN SMALL LETTER G CEDILLA;;0122;;0122
+0124;LATIN CAPITAL LETTER H WITH CIRCUMFLEX;Lu;0;L;0048 0302;;;;N;LATIN CAPITAL LETTER H CIRCUMFLEX;;;0125;
+0125;LATIN SMALL LETTER H WITH CIRCUMFLEX;Ll;0;L;0068 0302;;;;N;LATIN SMALL LETTER H CIRCUMFLEX;;0124;;0124
+0126;LATIN CAPITAL LETTER H WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER H BAR;;;0127;
+0127;LATIN SMALL LETTER H WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER H BAR;;0126;;0126
+0128;LATIN CAPITAL LETTER I WITH TILDE;Lu;0;L;0049 0303;;;;N;LATIN CAPITAL LETTER I TILDE;;;0129;
+0129;LATIN SMALL LETTER I WITH TILDE;Ll;0;L;0069 0303;;;;N;LATIN SMALL LETTER I TILDE;;0128;;0128
+012A;LATIN CAPITAL LETTER I WITH MACRON;Lu;0;L;0049 0304;;;;N;LATIN CAPITAL LETTER I MACRON;;;012B;
+012B;LATIN SMALL LETTER I WITH MACRON;Ll;0;L;0069 0304;;;;N;LATIN SMALL LETTER I MACRON;;012A;;012A
+012C;LATIN CAPITAL LETTER I WITH BREVE;Lu;0;L;0049 0306;;;;N;LATIN CAPITAL LETTER I BREVE;;;012D;
+012D;LATIN SMALL LETTER I WITH BREVE;Ll;0;L;0069 0306;;;;N;LATIN SMALL LETTER I BREVE;;012C;;012C
+012E;LATIN CAPITAL LETTER I WITH OGONEK;Lu;0;L;0049 0328;;;;N;LATIN CAPITAL LETTER I OGONEK;;;012F;
+012F;LATIN SMALL LETTER I WITH OGONEK;Ll;0;L;0069 0328;;;;N;LATIN SMALL LETTER I OGONEK;;012E;;012E
+0130;LATIN CAPITAL LETTER I WITH DOT ABOVE;Lu;0;L;0049 0307;;;;N;LATIN CAPITAL LETTER I DOT;;;0069;
+0131;LATIN SMALL LETTER DOTLESS I;Ll;0;L;;;;;N;;;0049;;0049
+0132;LATIN CAPITAL LIGATURE IJ;Lu;0;L;<compat> 0049 004A;;;;N;LATIN CAPITAL LETTER I J;;;0133;
+0133;LATIN SMALL LIGATURE IJ;Ll;0;L;<compat> 0069 006A;;;;N;LATIN SMALL LETTER I J;;0132;;0132
+0134;LATIN CAPITAL LETTER J WITH CIRCUMFLEX;Lu;0;L;004A 0302;;;;N;LATIN CAPITAL LETTER J CIRCUMFLEX;;;0135;
+0135;LATIN SMALL LETTER J WITH CIRCUMFLEX;Ll;0;L;006A 0302;;;;N;LATIN SMALL LETTER J CIRCUMFLEX;;0134;;0134
+0136;LATIN CAPITAL LETTER K WITH CEDILLA;Lu;0;L;004B 0327;;;;N;LATIN CAPITAL LETTER K CEDILLA;;;0137;
+0137;LATIN SMALL LETTER K WITH CEDILLA;Ll;0;L;006B 0327;;;;N;LATIN SMALL LETTER K CEDILLA;;0136;;0136
+0138;LATIN SMALL LETTER KRA;Ll;0;L;;;;;N;;Greenlandic;;;
+0139;LATIN CAPITAL LETTER L WITH ACUTE;Lu;0;L;004C 0301;;;;N;LATIN CAPITAL LETTER L ACUTE;;;013A;
+013A;LATIN SMALL LETTER L WITH ACUTE;Ll;0;L;006C 0301;;;;N;LATIN SMALL LETTER L ACUTE;;0139;;0139
+013B;LATIN CAPITAL LETTER L WITH CEDILLA;Lu;0;L;004C 0327;;;;N;LATIN CAPITAL LETTER L CEDILLA;;;013C;
+013C;LATIN SMALL LETTER L WITH CEDILLA;Ll;0;L;006C 0327;;;;N;LATIN SMALL LETTER L CEDILLA;;013B;;013B
+013D;LATIN CAPITAL LETTER L WITH CARON;Lu;0;L;004C 030C;;;;N;LATIN CAPITAL LETTER L HACEK;;;013E;
+013E;LATIN SMALL LETTER L WITH CARON;Ll;0;L;006C 030C;;;;N;LATIN SMALL LETTER L HACEK;;013D;;013D
+013F;LATIN CAPITAL LETTER L WITH MIDDLE DOT;Lu;0;L;<compat> 004C 00B7;;;;N;;;;0140;
+0140;LATIN SMALL LETTER L WITH MIDDLE DOT;Ll;0;L;<compat> 006C 00B7;;;;N;;;013F;;013F
+0141;LATIN CAPITAL LETTER L WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER L SLASH;;;0142;
+0142;LATIN SMALL LETTER L WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER L SLASH;;0141;;0141
+0143;LATIN CAPITAL LETTER N WITH ACUTE;Lu;0;L;004E 0301;;;;N;LATIN CAPITAL LETTER N ACUTE;;;0144;
+0144;LATIN SMALL LETTER N WITH ACUTE;Ll;0;L;006E 0301;;;;N;LATIN SMALL LETTER N ACUTE;;0143;;0143
+0145;LATIN CAPITAL LETTER N WITH CEDILLA;Lu;0;L;004E 0327;;;;N;LATIN CAPITAL LETTER N CEDILLA;;;0146;
+0146;LATIN SMALL LETTER N WITH CEDILLA;Ll;0;L;006E 0327;;;;N;LATIN SMALL LETTER N CEDILLA;;0145;;0145
+0147;LATIN CAPITAL LETTER N WITH CARON;Lu;0;L;004E 030C;;;;N;LATIN CAPITAL LETTER N HACEK;;;0148;
+0148;LATIN SMALL LETTER N WITH CARON;Ll;0;L;006E 030C;;;;N;LATIN SMALL LETTER N HACEK;;0147;;0147
+0149;LATIN SMALL LETTER N PRECEDED BY APOSTROPHE;Ll;0;L;<compat> 02BC 006E;;;;N;LATIN SMALL LETTER APOSTROPHE N;;;;
+014A;LATIN CAPITAL LETTER ENG;Lu;0;L;;;;;N;;Sami;;014B;
+014B;LATIN SMALL LETTER ENG;Ll;0;L;;;;;N;;Sami;014A;;014A
+014C;LATIN CAPITAL LETTER O WITH MACRON;Lu;0;L;004F 0304;;;;N;LATIN CAPITAL LETTER O MACRON;;;014D;
+014D;LATIN SMALL LETTER O WITH MACRON;Ll;0;L;006F 0304;;;;N;LATIN SMALL LETTER O MACRON;;014C;;014C
+014E;LATIN CAPITAL LETTER O WITH BREVE;Lu;0;L;004F 0306;;;;N;LATIN CAPITAL LETTER O BREVE;;;014F;
+014F;LATIN SMALL LETTER O WITH BREVE;Ll;0;L;006F 0306;;;;N;LATIN SMALL LETTER O BREVE;;014E;;014E
+0150;LATIN CAPITAL LETTER O WITH DOUBLE ACUTE;Lu;0;L;004F 030B;;;;N;LATIN CAPITAL LETTER O DOUBLE ACUTE;;;0151;
+0151;LATIN SMALL LETTER O WITH DOUBLE ACUTE;Ll;0;L;006F 030B;;;;N;LATIN SMALL LETTER O DOUBLE ACUTE;;0150;;0150
+0152;LATIN CAPITAL LIGATURE OE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER O E;;;0153;
+0153;LATIN SMALL LIGATURE OE;Ll;0;L;;;;;N;LATIN SMALL LETTER O E;;0152;;0152
+0154;LATIN CAPITAL LETTER R WITH ACUTE;Lu;0;L;0052 0301;;;;N;LATIN CAPITAL LETTER R ACUTE;;;0155;
+0155;LATIN SMALL LETTER R WITH ACUTE;Ll;0;L;0072 0301;;;;N;LATIN SMALL LETTER R ACUTE;;0154;;0154
+0156;LATIN CAPITAL LETTER R WITH CEDILLA;Lu;0;L;0052 0327;;;;N;LATIN CAPITAL LETTER R CEDILLA;;;0157;
+0157;LATIN SMALL LETTER R WITH CEDILLA;Ll;0;L;0072 0327;;;;N;LATIN SMALL LETTER R CEDILLA;;0156;;0156
+0158;LATIN CAPITAL LETTER R WITH CARON;Lu;0;L;0052 030C;;;;N;LATIN CAPITAL LETTER R HACEK;;;0159;
+0159;LATIN SMALL LETTER R WITH CARON;Ll;0;L;0072 030C;;;;N;LATIN SMALL LETTER R HACEK;;0158;;0158
+015A;LATIN CAPITAL LETTER S WITH ACUTE;Lu;0;L;0053 0301;;;;N;LATIN CAPITAL LETTER S ACUTE;;;015B;
+015B;LATIN SMALL LETTER S WITH ACUTE;Ll;0;L;0073 0301;;;;N;LATIN SMALL LETTER S ACUTE;;015A;;015A
+015C;LATIN CAPITAL LETTER S WITH CIRCUMFLEX;Lu;0;L;0053 0302;;;;N;LATIN CAPITAL LETTER S CIRCUMFLEX;;;015D;
+015D;LATIN SMALL LETTER S WITH CIRCUMFLEX;Ll;0;L;0073 0302;;;;N;LATIN SMALL LETTER S CIRCUMFLEX;;015C;;015C
+015E;LATIN CAPITAL LETTER S WITH CEDILLA;Lu;0;L;0053 0327;;;;N;LATIN CAPITAL LETTER S CEDILLA;*;;015F;
+015F;LATIN SMALL LETTER S WITH CEDILLA;Ll;0;L;0073 0327;;;;N;LATIN SMALL LETTER S CEDILLA;*;015E;;015E
+0160;LATIN CAPITAL LETTER S WITH CARON;Lu;0;L;0053 030C;;;;N;LATIN CAPITAL LETTER S HACEK;;;0161;
+0161;LATIN SMALL LETTER S WITH CARON;Ll;0;L;0073 030C;;;;N;LATIN SMALL LETTER S HACEK;;0160;;0160
+0162;LATIN CAPITAL LETTER T WITH CEDILLA;Lu;0;L;0054 0327;;;;N;LATIN CAPITAL LETTER T CEDILLA;*;;0163;
+0163;LATIN SMALL LETTER T WITH CEDILLA;Ll;0;L;0074 0327;;;;N;LATIN SMALL LETTER T CEDILLA;*;0162;;0162
+0164;LATIN CAPITAL LETTER T WITH CARON;Lu;0;L;0054 030C;;;;N;LATIN CAPITAL LETTER T HACEK;;;0165;
+0165;LATIN SMALL LETTER T WITH CARON;Ll;0;L;0074 030C;;;;N;LATIN SMALL LETTER T HACEK;;0164;;0164
+0166;LATIN CAPITAL LETTER T WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER T BAR;;;0167;
+0167;LATIN SMALL LETTER T WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER T BAR;;0166;;0166
+0168;LATIN CAPITAL LETTER U WITH TILDE;Lu;0;L;0055 0303;;;;N;LATIN CAPITAL LETTER U TILDE;;;0169;
+0169;LATIN SMALL LETTER U WITH TILDE;Ll;0;L;0075 0303;;;;N;LATIN SMALL LETTER U TILDE;;0168;;0168
+016A;LATIN CAPITAL LETTER U WITH MACRON;Lu;0;L;0055 0304;;;;N;LATIN CAPITAL LETTER U MACRON;;;016B;
+016B;LATIN SMALL LETTER U WITH MACRON;Ll;0;L;0075 0304;;;;N;LATIN SMALL LETTER U MACRON;;016A;;016A
+016C;LATIN CAPITAL LETTER U WITH BREVE;Lu;0;L;0055 0306;;;;N;LATIN CAPITAL LETTER U BREVE;;;016D;
+016D;LATIN SMALL LETTER U WITH BREVE;Ll;0;L;0075 0306;;;;N;LATIN SMALL LETTER U BREVE;;016C;;016C
+016E;LATIN CAPITAL LETTER U WITH RING ABOVE;Lu;0;L;0055 030A;;;;N;LATIN CAPITAL LETTER U RING;;;016F;
+016F;LATIN SMALL LETTER U WITH RING ABOVE;Ll;0;L;0075 030A;;;;N;LATIN SMALL LETTER U RING;;016E;;016E
+0170;LATIN CAPITAL LETTER U WITH DOUBLE ACUTE;Lu;0;L;0055 030B;;;;N;LATIN CAPITAL LETTER U DOUBLE ACUTE;;;0171;
+0171;LATIN SMALL LETTER U WITH DOUBLE ACUTE;Ll;0;L;0075 030B;;;;N;LATIN SMALL LETTER U DOUBLE ACUTE;;0170;;0170
+0172;LATIN CAPITAL LETTER U WITH OGONEK;Lu;0;L;0055 0328;;;;N;LATIN CAPITAL LETTER U OGONEK;;;0173;
+0173;LATIN SMALL LETTER U WITH OGONEK;Ll;0;L;0075 0328;;;;N;LATIN SMALL LETTER U OGONEK;;0172;;0172
+0174;LATIN CAPITAL LETTER W WITH CIRCUMFLEX;Lu;0;L;0057 0302;;;;N;LATIN CAPITAL LETTER W CIRCUMFLEX;;;0175;
+0175;LATIN SMALL LETTER W WITH CIRCUMFLEX;Ll;0;L;0077 0302;;;;N;LATIN SMALL LETTER W CIRCUMFLEX;;0174;;0174
+0176;LATIN CAPITAL LETTER Y WITH CIRCUMFLEX;Lu;0;L;0059 0302;;;;N;LATIN CAPITAL LETTER Y CIRCUMFLEX;;;0177;
+0177;LATIN SMALL LETTER Y WITH CIRCUMFLEX;Ll;0;L;0079 0302;;;;N;LATIN SMALL LETTER Y CIRCUMFLEX;;0176;;0176
+0178;LATIN CAPITAL LETTER Y WITH DIAERESIS;Lu;0;L;0059 0308;;;;N;LATIN CAPITAL LETTER Y DIAERESIS;;;00FF;
+0179;LATIN CAPITAL LETTER Z WITH ACUTE;Lu;0;L;005A 0301;;;;N;LATIN CAPITAL LETTER Z ACUTE;;;017A;
+017A;LATIN SMALL LETTER Z WITH ACUTE;Ll;0;L;007A 0301;;;;N;LATIN SMALL LETTER Z ACUTE;;0179;;0179
+017B;LATIN CAPITAL LETTER Z WITH DOT ABOVE;Lu;0;L;005A 0307;;;;N;LATIN CAPITAL LETTER Z DOT;;;017C;
+017C;LATIN SMALL LETTER Z WITH DOT ABOVE;Ll;0;L;007A 0307;;;;N;LATIN SMALL LETTER Z DOT;;017B;;017B
+017D;LATIN CAPITAL LETTER Z WITH CARON;Lu;0;L;005A 030C;;;;N;LATIN CAPITAL LETTER Z HACEK;;;017E;
+017E;LATIN SMALL LETTER Z WITH CARON;Ll;0;L;007A 030C;;;;N;LATIN SMALL LETTER Z HACEK;;017D;;017D
+017F;LATIN SMALL LETTER LONG S;Ll;0;L;<compat> 0073;;;;N;;;0053;;0053
+0180;LATIN SMALL LETTER B WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER B BAR;;;;
+0181;LATIN CAPITAL LETTER B WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER B HOOK;;;0253;
+0182;LATIN CAPITAL LETTER B WITH TOPBAR;Lu;0;L;;;;;N;LATIN CAPITAL LETTER B TOPBAR;;;0183;
+0183;LATIN SMALL LETTER B WITH TOPBAR;Ll;0;L;;;;;N;LATIN SMALL LETTER B TOPBAR;;0182;;0182
+0184;LATIN CAPITAL LETTER TONE SIX;Lu;0;L;;;;;N;;;;0185;
+0185;LATIN SMALL LETTER TONE SIX;Ll;0;L;;;;;N;;;0184;;0184
+0186;LATIN CAPITAL LETTER OPEN O;Lu;0;L;;;;;N;;;;0254;
+0187;LATIN CAPITAL LETTER C WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER C HOOK;;;0188;
+0188;LATIN SMALL LETTER C WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER C HOOK;;0187;;0187
+0189;LATIN CAPITAL LETTER AFRICAN D;Lu;0;L;;;;;N;;*;;0256;
+018A;LATIN CAPITAL LETTER D WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER D HOOK;;;0257;
+018B;LATIN CAPITAL LETTER D WITH TOPBAR;Lu;0;L;;;;;N;LATIN CAPITAL LETTER D TOPBAR;;;018C;
+018C;LATIN SMALL LETTER D WITH TOPBAR;Ll;0;L;;;;;N;LATIN SMALL LETTER D TOPBAR;;018B;;018B
+018D;LATIN SMALL LETTER TURNED DELTA;Ll;0;L;;;;;N;;;;;
+018E;LATIN CAPITAL LETTER REVERSED E;Lu;0;L;;;;;N;LATIN CAPITAL LETTER TURNED E;;;01DD;
+018F;LATIN CAPITAL LETTER SCHWA;Lu;0;L;;;;;N;;;;0259;
+0190;LATIN CAPITAL LETTER OPEN E;Lu;0;L;;;;;N;LATIN CAPITAL LETTER EPSILON;;;025B;
+0191;LATIN CAPITAL LETTER F WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER F HOOK;;;0192;
+0192;LATIN SMALL LETTER F WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER SCRIPT F;;0191;;0191
+0193;LATIN CAPITAL LETTER G WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER G HOOK;;;0260;
+0194;LATIN CAPITAL LETTER GAMMA;Lu;0;L;;;;;N;;;;0263;
+0195;LATIN SMALL LETTER HV;Ll;0;L;;;;;N;LATIN SMALL LETTER H V;hwair;01F6;;01F6
+0196;LATIN CAPITAL LETTER IOTA;Lu;0;L;;;;;N;;;;0269;
+0197;LATIN CAPITAL LETTER I WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER BARRED I;;;0268;
+0198;LATIN CAPITAL LETTER K WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER K HOOK;;;0199;
+0199;LATIN SMALL LETTER K WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER K HOOK;;0198;;0198
+019A;LATIN SMALL LETTER L WITH BAR;Ll;0;L;;;;;N;LATIN SMALL LETTER BARRED L;;;;
+019B;LATIN SMALL LETTER LAMBDA WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER BARRED LAMBDA;;;;
+019C;LATIN CAPITAL LETTER TURNED M;Lu;0;L;;;;;N;;;;026F;
+019D;LATIN CAPITAL LETTER N WITH LEFT HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER N HOOK;;;0272;
+019E;LATIN SMALL LETTER N WITH LONG RIGHT LEG;Ll;0;L;;;;;N;;;0220;;0220
+019F;LATIN CAPITAL LETTER O WITH MIDDLE TILDE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER BARRED O;*;;0275;
+01A0;LATIN CAPITAL LETTER O WITH HORN;Lu;0;L;004F 031B;;;;N;LATIN CAPITAL LETTER O HORN;;;01A1;
+01A1;LATIN SMALL LETTER O WITH HORN;Ll;0;L;006F 031B;;;;N;LATIN SMALL LETTER O HORN;;01A0;;01A0
+01A2;LATIN CAPITAL LETTER OI;Lu;0;L;;;;;N;LATIN CAPITAL LETTER O I;gha;;01A3;
+01A3;LATIN SMALL LETTER OI;Ll;0;L;;;;;N;LATIN SMALL LETTER O I;gha;01A2;;01A2
+01A4;LATIN CAPITAL LETTER P WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER P HOOK;;;01A5;
+01A5;LATIN SMALL LETTER P WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER P HOOK;;01A4;;01A4
+01A6;LATIN LETTER YR;Lu;0;L;;;;;N;LATIN LETTER Y R;*;;0280;
+01A7;LATIN CAPITAL LETTER TONE TWO;Lu;0;L;;;;;N;;;;01A8;
+01A8;LATIN SMALL LETTER TONE TWO;Ll;0;L;;;;;N;;;01A7;;01A7
+01A9;LATIN CAPITAL LETTER ESH;Lu;0;L;;;;;N;;;;0283;
+01AA;LATIN LETTER REVERSED ESH LOOP;Ll;0;L;;;;;N;;;;;
+01AB;LATIN SMALL LETTER T WITH PALATAL HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER T PALATAL HOOK;;;;
+01AC;LATIN CAPITAL LETTER T WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER T HOOK;;;01AD;
+01AD;LATIN SMALL LETTER T WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER T HOOK;;01AC;;01AC
+01AE;LATIN CAPITAL LETTER T WITH RETROFLEX HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER T RETROFLEX HOOK;;;0288;
+01AF;LATIN CAPITAL LETTER U WITH HORN;Lu;0;L;0055 031B;;;;N;LATIN CAPITAL LETTER U HORN;;;01B0;
+01B0;LATIN SMALL LETTER U WITH HORN;Ll;0;L;0075 031B;;;;N;LATIN SMALL LETTER U HORN;;01AF;;01AF
+01B1;LATIN CAPITAL LETTER UPSILON;Lu;0;L;;;;;N;;;;028A;
+01B2;LATIN CAPITAL LETTER V WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER SCRIPT V;;;028B;
+01B3;LATIN CAPITAL LETTER Y WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER Y HOOK;;;01B4;
+01B4;LATIN SMALL LETTER Y WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER Y HOOK;;01B3;;01B3
+01B5;LATIN CAPITAL LETTER Z WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER Z BAR;;;01B6;
+01B6;LATIN SMALL LETTER Z WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER Z BAR;;01B5;;01B5
+01B7;LATIN CAPITAL LETTER EZH;Lu;0;L;;;;;N;LATIN CAPITAL LETTER YOGH;;;0292;
+01B8;LATIN CAPITAL LETTER EZH REVERSED;Lu;0;L;;;;;N;LATIN CAPITAL LETTER REVERSED YOGH;;;01B9;
+01B9;LATIN SMALL LETTER EZH REVERSED;Ll;0;L;;;;;N;LATIN SMALL LETTER REVERSED YOGH;;01B8;;01B8
+01BA;LATIN SMALL LETTER EZH WITH TAIL;Ll;0;L;;;;;N;LATIN SMALL LETTER YOGH WITH TAIL;;;;
+01BB;LATIN LETTER TWO WITH STROKE;Lo;0;L;;;;;N;LATIN LETTER TWO BAR;;;;
+01BC;LATIN CAPITAL LETTER TONE FIVE;Lu;0;L;;;;;N;;;;01BD;
+01BD;LATIN SMALL LETTER TONE FIVE;Ll;0;L;;;;;N;;;01BC;;01BC
+01BE;LATIN LETTER INVERTED GLOTTAL STOP WITH STROKE;Ll;0;L;;;;;N;LATIN LETTER INVERTED GLOTTAL STOP BAR;;;;
+01BF;LATIN LETTER WYNN;Ll;0;L;;;;;N;;;01F7;;01F7
+01C0;LATIN LETTER DENTAL CLICK;Lo;0;L;;;;;N;LATIN LETTER PIPE;;;;
+01C1;LATIN LETTER LATERAL CLICK;Lo;0;L;;;;;N;LATIN LETTER DOUBLE PIPE;;;;
+01C2;LATIN LETTER ALVEOLAR CLICK;Lo;0;L;;;;;N;LATIN LETTER PIPE DOUBLE BAR;;;;
+01C3;LATIN LETTER RETROFLEX CLICK;Lo;0;L;;;;;N;LATIN LETTER EXCLAMATION MARK;;;;
+01C4;LATIN CAPITAL LETTER DZ WITH CARON;Lu;0;L;<compat> 0044 017D;;;;N;LATIN CAPITAL LETTER D Z HACEK;;;01C6;01C5
+01C5;LATIN CAPITAL LETTER D WITH SMALL LETTER Z WITH CARON;Lt;0;L;<compat> 0044 017E;;;;N;LATIN LETTER CAPITAL D SMALL Z HACEK;;01C4;01C6;01C5
+01C6;LATIN SMALL LETTER DZ WITH CARON;Ll;0;L;<compat> 0064 017E;;;;N;LATIN SMALL LETTER D Z HACEK;;01C4;;01C5
+01C7;LATIN CAPITAL LETTER LJ;Lu;0;L;<compat> 004C 004A;;;;N;LATIN CAPITAL LETTER L J;;;01C9;01C8
+01C8;LATIN CAPITAL LETTER L WITH SMALL LETTER J;Lt;0;L;<compat> 004C 006A;;;;N;LATIN LETTER CAPITAL L SMALL J;;01C7;01C9;01C8
+01C9;LATIN SMALL LETTER LJ;Ll;0;L;<compat> 006C 006A;;;;N;LATIN SMALL LETTER L J;;01C7;;01C8
+01CA;LATIN CAPITAL LETTER NJ;Lu;0;L;<compat> 004E 004A;;;;N;LATIN CAPITAL LETTER N J;;;01CC;01CB
+01CB;LATIN CAPITAL LETTER N WITH SMALL LETTER J;Lt;0;L;<compat> 004E 006A;;;;N;LATIN LETTER CAPITAL N SMALL J;;01CA;01CC;01CB
+01CC;LATIN SMALL LETTER NJ;Ll;0;L;<compat> 006E 006A;;;;N;LATIN SMALL LETTER N J;;01CA;;01CB
+01CD;LATIN CAPITAL LETTER A WITH CARON;Lu;0;L;0041 030C;;;;N;LATIN CAPITAL LETTER A HACEK;;;01CE;
+01CE;LATIN SMALL LETTER A WITH CARON;Ll;0;L;0061 030C;;;;N;LATIN SMALL LETTER A HACEK;;01CD;;01CD
+01CF;LATIN CAPITAL LETTER I WITH CARON;Lu;0;L;0049 030C;;;;N;LATIN CAPITAL LETTER I HACEK;;;01D0;
+01D0;LATIN SMALL LETTER I WITH CARON;Ll;0;L;0069 030C;;;;N;LATIN SMALL LETTER I HACEK;;01CF;;01CF
+01D1;LATIN CAPITAL LETTER O WITH CARON;Lu;0;L;004F 030C;;;;N;LATIN CAPITAL LETTER O HACEK;;;01D2;
+01D2;LATIN SMALL LETTER O WITH CARON;Ll;0;L;006F 030C;;;;N;LATIN SMALL LETTER O HACEK;;01D1;;01D1
+01D3;LATIN CAPITAL LETTER U WITH CARON;Lu;0;L;0055 030C;;;;N;LATIN CAPITAL LETTER U HACEK;;;01D4;
+01D4;LATIN SMALL LETTER U WITH CARON;Ll;0;L;0075 030C;;;;N;LATIN SMALL LETTER U HACEK;;01D3;;01D3
+01D5;LATIN CAPITAL LETTER U WITH DIAERESIS AND MACRON;Lu;0;L;00DC 0304;;;;N;LATIN CAPITAL LETTER U DIAERESIS MACRON;;;01D6;
+01D6;LATIN SMALL LETTER U WITH DIAERESIS AND MACRON;Ll;0;L;00FC 0304;;;;N;LATIN SMALL LETTER U DIAERESIS MACRON;;01D5;;01D5
+01D7;LATIN CAPITAL LETTER U WITH DIAERESIS AND ACUTE;Lu;0;L;00DC 0301;;;;N;LATIN CAPITAL LETTER U DIAERESIS ACUTE;;;01D8;
+01D8;LATIN SMALL LETTER U WITH DIAERESIS AND ACUTE;Ll;0;L;00FC 0301;;;;N;LATIN SMALL LETTER U DIAERESIS ACUTE;;01D7;;01D7
+01D9;LATIN CAPITAL LETTER U WITH DIAERESIS AND CARON;Lu;0;L;00DC 030C;;;;N;LATIN CAPITAL LETTER U DIAERESIS HACEK;;;01DA;
+01DA;LATIN SMALL LETTER U WITH DIAERESIS AND CARON;Ll;0;L;00FC 030C;;;;N;LATIN SMALL LETTER U DIAERESIS HACEK;;01D9;;01D9
+01DB;LATIN CAPITAL LETTER U WITH DIAERESIS AND GRAVE;Lu;0;L;00DC 0300;;;;N;LATIN CAPITAL LETTER U DIAERESIS GRAVE;;;01DC;
+01DC;LATIN SMALL LETTER U WITH DIAERESIS AND GRAVE;Ll;0;L;00FC 0300;;;;N;LATIN SMALL LETTER U DIAERESIS GRAVE;;01DB;;01DB
+01DD;LATIN SMALL LETTER TURNED E;Ll;0;L;;;;;N;;;018E;;018E
+01DE;LATIN CAPITAL LETTER A WITH DIAERESIS AND MACRON;Lu;0;L;00C4 0304;;;;N;LATIN CAPITAL LETTER A DIAERESIS MACRON;;;01DF;
+01DF;LATIN SMALL LETTER A WITH DIAERESIS AND MACRON;Ll;0;L;00E4 0304;;;;N;LATIN SMALL LETTER A DIAERESIS MACRON;;01DE;;01DE
+01E0;LATIN CAPITAL LETTER A WITH DOT ABOVE AND MACRON;Lu;0;L;0226 0304;;;;N;LATIN CAPITAL LETTER A DOT MACRON;;;01E1;
+01E1;LATIN SMALL LETTER A WITH DOT ABOVE AND MACRON;Ll;0;L;0227 0304;;;;N;LATIN SMALL LETTER A DOT MACRON;;01E0;;01E0
+01E2;LATIN CAPITAL LETTER AE WITH MACRON;Lu;0;L;00C6 0304;;;;N;LATIN CAPITAL LETTER A E MACRON;ash *;;01E3;
+01E3;LATIN SMALL LETTER AE WITH MACRON;Ll;0;L;00E6 0304;;;;N;LATIN SMALL LETTER A E MACRON;ash *;01E2;;01E2
+01E4;LATIN CAPITAL LETTER G WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER G BAR;;;01E5;
+01E5;LATIN SMALL LETTER G WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER G BAR;;01E4;;01E4
+01E6;LATIN CAPITAL LETTER G WITH CARON;Lu;0;L;0047 030C;;;;N;LATIN CAPITAL LETTER G HACEK;;;01E7;
+01E7;LATIN SMALL LETTER G WITH CARON;Ll;0;L;0067 030C;;;;N;LATIN SMALL LETTER G HACEK;;01E6;;01E6
+01E8;LATIN CAPITAL LETTER K WITH CARON;Lu;0;L;004B 030C;;;;N;LATIN CAPITAL LETTER K HACEK;;;01E9;
+01E9;LATIN SMALL LETTER K WITH CARON;Ll;0;L;006B 030C;;;;N;LATIN SMALL LETTER K HACEK;;01E8;;01E8
+01EA;LATIN CAPITAL LETTER O WITH OGONEK;Lu;0;L;004F 0328;;;;N;LATIN CAPITAL LETTER O OGONEK;;;01EB;
+01EB;LATIN SMALL LETTER O WITH OGONEK;Ll;0;L;006F 0328;;;;N;LATIN SMALL LETTER O OGONEK;;01EA;;01EA
+01EC;LATIN CAPITAL LETTER O WITH OGONEK AND MACRON;Lu;0;L;01EA 0304;;;;N;LATIN CAPITAL LETTER O OGONEK MACRON;;;01ED;
+01ED;LATIN SMALL LETTER O WITH OGONEK AND MACRON;Ll;0;L;01EB 0304;;;;N;LATIN SMALL LETTER O OGONEK MACRON;;01EC;;01EC
+01EE;LATIN CAPITAL LETTER EZH WITH CARON;Lu;0;L;01B7 030C;;;;N;LATIN CAPITAL LETTER YOGH HACEK;;;01EF;
+01EF;LATIN SMALL LETTER EZH WITH CARON;Ll;0;L;0292 030C;;;;N;LATIN SMALL LETTER YOGH HACEK;;01EE;;01EE
+01F0;LATIN SMALL LETTER J WITH CARON;Ll;0;L;006A 030C;;;;N;LATIN SMALL LETTER J HACEK;;;;
+01F1;LATIN CAPITAL LETTER DZ;Lu;0;L;<compat> 0044 005A;;;;N;;;;01F3;01F2
+01F2;LATIN CAPITAL LETTER D WITH SMALL LETTER Z;Lt;0;L;<compat> 0044 007A;;;;N;;;01F1;01F3;01F2
+01F3;LATIN SMALL LETTER DZ;Ll;0;L;<compat> 0064 007A;;;;N;;;01F1;;01F2
+01F4;LATIN CAPITAL LETTER G WITH ACUTE;Lu;0;L;0047 0301;;;;N;;;;01F5;
+01F5;LATIN SMALL LETTER G WITH ACUTE;Ll;0;L;0067 0301;;;;N;;;01F4;;01F4
+01F6;LATIN CAPITAL LETTER HWAIR;Lu;0;L;;;;;N;;;;0195;
+01F7;LATIN CAPITAL LETTER WYNN;Lu;0;L;;;;;N;;;;01BF;
+01F8;LATIN CAPITAL LETTER N WITH GRAVE;Lu;0;L;004E 0300;;;;N;;;;01F9;
+01F9;LATIN SMALL LETTER N WITH GRAVE;Ll;0;L;006E 0300;;;;N;;;01F8;;01F8
+01FA;LATIN CAPITAL LETTER A WITH RING ABOVE AND ACUTE;Lu;0;L;00C5 0301;;;;N;;;;01FB;
+01FB;LATIN SMALL LETTER A WITH RING ABOVE AND ACUTE;Ll;0;L;00E5 0301;;;;N;;;01FA;;01FA
+01FC;LATIN CAPITAL LETTER AE WITH ACUTE;Lu;0;L;00C6 0301;;;;N;;ash *;;01FD;
+01FD;LATIN SMALL LETTER AE WITH ACUTE;Ll;0;L;00E6 0301;;;;N;;ash *;01FC;;01FC
+01FE;LATIN CAPITAL LETTER O WITH STROKE AND ACUTE;Lu;0;L;00D8 0301;;;;N;;;;01FF;
+01FF;LATIN SMALL LETTER O WITH STROKE AND ACUTE;Ll;0;L;00F8 0301;;;;N;;;01FE;;01FE
+0200;LATIN CAPITAL LETTER A WITH DOUBLE GRAVE;Lu;0;L;0041 030F;;;;N;;;;0201;
+0201;LATIN SMALL LETTER A WITH DOUBLE GRAVE;Ll;0;L;0061 030F;;;;N;;;0200;;0200
+0202;LATIN CAPITAL LETTER A WITH INVERTED BREVE;Lu;0;L;0041 0311;;;;N;;;;0203;
+0203;LATIN SMALL LETTER A WITH INVERTED BREVE;Ll;0;L;0061 0311;;;;N;;;0202;;0202
+0204;LATIN CAPITAL LETTER E WITH DOUBLE GRAVE;Lu;0;L;0045 030F;;;;N;;;;0205;
+0205;LATIN SMALL LETTER E WITH DOUBLE GRAVE;Ll;0;L;0065 030F;;;;N;;;0204;;0204
+0206;LATIN CAPITAL LETTER E WITH INVERTED BREVE;Lu;0;L;0045 0311;;;;N;;;;0207;
+0207;LATIN SMALL LETTER E WITH INVERTED BREVE;Ll;0;L;0065 0311;;;;N;;;0206;;0206
+0208;LATIN CAPITAL LETTER I WITH DOUBLE GRAVE;Lu;0;L;0049 030F;;;;N;;;;0209;
+0209;LATIN SMALL LETTER I WITH DOUBLE GRAVE;Ll;0;L;0069 030F;;;;N;;;0208;;0208
+020A;LATIN CAPITAL LETTER I WITH INVERTED BREVE;Lu;0;L;0049 0311;;;;N;;;;020B;
+020B;LATIN SMALL LETTER I WITH INVERTED BREVE;Ll;0;L;0069 0311;;;;N;;;020A;;020A
+020C;LATIN CAPITAL LETTER O WITH DOUBLE GRAVE;Lu;0;L;004F 030F;;;;N;;;;020D;
+020D;LATIN SMALL LETTER O WITH DOUBLE GRAVE;Ll;0;L;006F 030F;;;;N;;;020C;;020C
+020E;LATIN CAPITAL LETTER O WITH INVERTED BREVE;Lu;0;L;004F 0311;;;;N;;;;020F;
+020F;LATIN SMALL LETTER O WITH INVERTED BREVE;Ll;0;L;006F 0311;;;;N;;;020E;;020E
+0210;LATIN CAPITAL LETTER R WITH DOUBLE GRAVE;Lu;0;L;0052 030F;;;;N;;;;0211;
+0211;LATIN SMALL LETTER R WITH DOUBLE GRAVE;Ll;0;L;0072 030F;;;;N;;;0210;;0210
+0212;LATIN CAPITAL LETTER R WITH INVERTED BREVE;Lu;0;L;0052 0311;;;;N;;;;0213;
+0213;LATIN SMALL LETTER R WITH INVERTED BREVE;Ll;0;L;0072 0311;;;;N;;;0212;;0212
+0214;LATIN CAPITAL LETTER U WITH DOUBLE GRAVE;Lu;0;L;0055 030F;;;;N;;;;0215;
+0215;LATIN SMALL LETTER U WITH DOUBLE GRAVE;Ll;0;L;0075 030F;;;;N;;;0214;;0214
+0216;LATIN CAPITAL LETTER U WITH INVERTED BREVE;Lu;0;L;0055 0311;;;;N;;;;0217;
+0217;LATIN SMALL LETTER U WITH INVERTED BREVE;Ll;0;L;0075 0311;;;;N;;;0216;;0216
+0218;LATIN CAPITAL LETTER S WITH COMMA BELOW;Lu;0;L;0053 0326;;;;N;;*;;0219;
+0219;LATIN SMALL LETTER S WITH COMMA BELOW;Ll;0;L;0073 0326;;;;N;;*;0218;;0218
+021A;LATIN CAPITAL LETTER T WITH COMMA BELOW;Lu;0;L;0054 0326;;;;N;;*;;021B;
+021B;LATIN SMALL LETTER T WITH COMMA BELOW;Ll;0;L;0074 0326;;;;N;;*;021A;;021A
+021C;LATIN CAPITAL LETTER YOGH;Lu;0;L;;;;;N;;;;021D;
+021D;LATIN SMALL LETTER YOGH;Ll;0;L;;;;;N;;;021C;;021C
+021E;LATIN CAPITAL LETTER H WITH CARON;Lu;0;L;0048 030C;;;;N;;;;021F;
+021F;LATIN SMALL LETTER H WITH CARON;Ll;0;L;0068 030C;;;;N;;;021E;;021E
+0220;LATIN CAPITAL LETTER N WITH LONG RIGHT LEG;Lu;0;L;;;;;N;;;;019E;
+0221;LATIN SMALL LETTER D WITH CURL;Ll;0;L;;;;;N;;;;;
+0222;LATIN CAPITAL LETTER OU;Lu;0;L;;;;;N;;;;0223;
+0223;LATIN SMALL LETTER OU;Ll;0;L;;;;;N;;;0222;;0222
+0224;LATIN CAPITAL LETTER Z WITH HOOK;Lu;0;L;;;;;N;;;;0225;
+0225;LATIN SMALL LETTER Z WITH HOOK;Ll;0;L;;;;;N;;;0224;;0224
+0226;LATIN CAPITAL LETTER A WITH DOT ABOVE;Lu;0;L;0041 0307;;;;N;;;;0227;
+0227;LATIN SMALL LETTER A WITH DOT ABOVE;Ll;0;L;0061 0307;;;;N;;;0226;;0226
+0228;LATIN CAPITAL LETTER E WITH CEDILLA;Lu;0;L;0045 0327;;;;N;;;;0229;
+0229;LATIN SMALL LETTER E WITH CEDILLA;Ll;0;L;0065 0327;;;;N;;;0228;;0228
+022A;LATIN CAPITAL LETTER O WITH DIAERESIS AND MACRON;Lu;0;L;00D6 0304;;;;N;;;;022B;
+022B;LATIN SMALL LETTER O WITH DIAERESIS AND MACRON;Ll;0;L;00F6 0304;;;;N;;;022A;;022A
+022C;LATIN CAPITAL LETTER O WITH TILDE AND MACRON;Lu;0;L;00D5 0304;;;;N;;;;022D;
+022D;LATIN SMALL LETTER O WITH TILDE AND MACRON;Ll;0;L;00F5 0304;;;;N;;;022C;;022C
+022E;LATIN CAPITAL LETTER O WITH DOT ABOVE;Lu;0;L;004F 0307;;;;N;;;;022F;
+022F;LATIN SMALL LETTER O WITH DOT ABOVE;Ll;0;L;006F 0307;;;;N;;;022E;;022E
+0230;LATIN CAPITAL LETTER O WITH DOT ABOVE AND MACRON;Lu;0;L;022E 0304;;;;N;;;;0231;
+0231;LATIN SMALL LETTER O WITH DOT ABOVE AND MACRON;Ll;0;L;022F 0304;;;;N;;;0230;;0230
+0232;LATIN CAPITAL LETTER Y WITH MACRON;Lu;0;L;0059 0304;;;;N;;;;0233;
+0233;LATIN SMALL LETTER Y WITH MACRON;Ll;0;L;0079 0304;;;;N;;;0232;;0232
+0234;LATIN SMALL LETTER L WITH CURL;Ll;0;L;;;;;N;;;;;
+0235;LATIN SMALL LETTER N WITH CURL;Ll;0;L;;;;;N;;;;;
+0236;LATIN SMALL LETTER T WITH CURL;Ll;0;L;;;;;N;;;;;
+0250;LATIN SMALL LETTER TURNED A;Ll;0;L;;;;;N;;;;;
+0251;LATIN SMALL LETTER ALPHA;Ll;0;L;;;;;N;LATIN SMALL LETTER SCRIPT A;;;;
+0252;LATIN SMALL LETTER TURNED ALPHA;Ll;0;L;;;;;N;LATIN SMALL LETTER TURNED SCRIPT A;;;;
+0253;LATIN SMALL LETTER B WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER B HOOK;;0181;;0181
+0254;LATIN SMALL LETTER OPEN O;Ll;0;L;;;;;N;;;0186;;0186
+0255;LATIN SMALL LETTER C WITH CURL;Ll;0;L;;;;;N;LATIN SMALL LETTER C CURL;;;;
+0256;LATIN SMALL LETTER D WITH TAIL;Ll;0;L;;;;;N;LATIN SMALL LETTER D RETROFLEX HOOK;;0189;;0189
+0257;LATIN SMALL LETTER D WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER D HOOK;;018A;;018A
+0258;LATIN SMALL LETTER REVERSED E;Ll;0;L;;;;;N;;;;;
+0259;LATIN SMALL LETTER SCHWA;Ll;0;L;;;;;N;;;018F;;018F
+025A;LATIN SMALL LETTER SCHWA WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER SCHWA HOOK;;;;
+025B;LATIN SMALL LETTER OPEN E;Ll;0;L;;;;;N;LATIN SMALL LETTER EPSILON;;0190;;0190
+025C;LATIN SMALL LETTER REVERSED OPEN E;Ll;0;L;;;;;N;LATIN SMALL LETTER REVERSED EPSILON;;;;
+025D;LATIN SMALL LETTER REVERSED OPEN E WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER REVERSED EPSILON HOOK;;;;
+025E;LATIN SMALL LETTER CLOSED REVERSED OPEN E;Ll;0;L;;;;;N;LATIN SMALL LETTER CLOSED REVERSED EPSILON;;;;
+025F;LATIN SMALL LETTER DOTLESS J WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER DOTLESS J BAR;;;;
+0260;LATIN SMALL LETTER G WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER G HOOK;;0193;;0193
+0261;LATIN SMALL LETTER SCRIPT G;Ll;0;L;;;;;N;;;;;
+0262;LATIN LETTER SMALL CAPITAL G;Ll;0;L;;;;;N;;;;;
+0263;LATIN SMALL LETTER GAMMA;Ll;0;L;;;;;N;;;0194;;0194
+0264;LATIN SMALL LETTER RAMS HORN;Ll;0;L;;;;;N;LATIN SMALL LETTER BABY GAMMA;;;;
+0265;LATIN SMALL LETTER TURNED H;Ll;0;L;;;;;N;;;;;
+0266;LATIN SMALL LETTER H WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER H HOOK;;;;
+0267;LATIN SMALL LETTER HENG WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER HENG HOOK;;;;
+0268;LATIN SMALL LETTER I WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER BARRED I;;0197;;0197
+0269;LATIN SMALL LETTER IOTA;Ll;0;L;;;;;N;;;0196;;0196
+026A;LATIN LETTER SMALL CAPITAL I;Ll;0;L;;;;;N;;;;;
+026B;LATIN SMALL LETTER L WITH MIDDLE TILDE;Ll;0;L;;;;;N;;;;;
+026C;LATIN SMALL LETTER L WITH BELT;Ll;0;L;;;;;N;LATIN SMALL LETTER L BELT;;;;
+026D;LATIN SMALL LETTER L WITH RETROFLEX HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER L RETROFLEX HOOK;;;;
+026E;LATIN SMALL LETTER LEZH;Ll;0;L;;;;;N;LATIN SMALL LETTER L YOGH;;;;
+026F;LATIN SMALL LETTER TURNED M;Ll;0;L;;;;;N;;;019C;;019C
+0270;LATIN SMALL LETTER TURNED M WITH LONG LEG;Ll;0;L;;;;;N;;;;;
+0271;LATIN SMALL LETTER M WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER M HOOK;;;;
+0272;LATIN SMALL LETTER N WITH LEFT HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER N HOOK;;019D;;019D
+0273;LATIN SMALL LETTER N WITH RETROFLEX HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER N RETROFLEX HOOK;;;;
+0274;LATIN LETTER SMALL CAPITAL N;Ll;0;L;;;;;N;;;;;
+0275;LATIN SMALL LETTER BARRED O;Ll;0;L;;;;;N;;;019F;;019F
+0276;LATIN LETTER SMALL CAPITAL OE;Ll;0;L;;;;;N;LATIN LETTER SMALL CAPITAL O E;;;;
+0277;LATIN SMALL LETTER CLOSED OMEGA;Ll;0;L;;;;;N;;;;;
+0278;LATIN SMALL LETTER PHI;Ll;0;L;;;;;N;;;;;
+0279;LATIN SMALL LETTER TURNED R;Ll;0;L;;;;;N;;;;;
+027A;LATIN SMALL LETTER TURNED R WITH LONG LEG;Ll;0;L;;;;;N;;;;;
+027B;LATIN SMALL LETTER TURNED R WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER TURNED R HOOK;;;;
+027C;LATIN SMALL LETTER R WITH LONG LEG;Ll;0;L;;;;;N;;;;;
+027D;LATIN SMALL LETTER R WITH TAIL;Ll;0;L;;;;;N;LATIN SMALL LETTER R HOOK;;;;
+027E;LATIN SMALL LETTER R WITH FISHHOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER FISHHOOK R;;;;
+027F;LATIN SMALL LETTER REVERSED R WITH FISHHOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER REVERSED FISHHOOK R;;;;
+0280;LATIN LETTER SMALL CAPITAL R;Ll;0;L;;;;;N;;*;01A6;;01A6
+0281;LATIN LETTER SMALL CAPITAL INVERTED R;Ll;0;L;;;;;N;;;;;
+0282;LATIN SMALL LETTER S WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER S HOOK;;;;
+0283;LATIN SMALL LETTER ESH;Ll;0;L;;;;;N;;;01A9;;01A9
+0284;LATIN SMALL LETTER DOTLESS J WITH STROKE AND HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER DOTLESS J BAR HOOK;;;;
+0285;LATIN SMALL LETTER SQUAT REVERSED ESH;Ll;0;L;;;;;N;;;;;
+0286;LATIN SMALL LETTER ESH WITH CURL;Ll;0;L;;;;;N;LATIN SMALL LETTER ESH CURL;;;;
+0287;LATIN SMALL LETTER TURNED T;Ll;0;L;;;;;N;;;;;
+0288;LATIN SMALL LETTER T WITH RETROFLEX HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER T RETROFLEX HOOK;;01AE;;01AE
+0289;LATIN SMALL LETTER U BAR;Ll;0;L;;;;;N;;;;;
+028A;LATIN SMALL LETTER UPSILON;Ll;0;L;;;;;N;;;01B1;;01B1
+028B;LATIN SMALL LETTER V WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER SCRIPT V;;01B2;;01B2
+028C;LATIN SMALL LETTER TURNED V;Ll;0;L;;;;;N;;;;;
+028D;LATIN SMALL LETTER TURNED W;Ll;0;L;;;;;N;;;;;
+028E;LATIN SMALL LETTER TURNED Y;Ll;0;L;;;;;N;;;;;
+028F;LATIN LETTER SMALL CAPITAL Y;Ll;0;L;;;;;N;;;;;
+0290;LATIN SMALL LETTER Z WITH RETROFLEX HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER Z RETROFLEX HOOK;;;;
+0291;LATIN SMALL LETTER Z WITH CURL;Ll;0;L;;;;;N;LATIN SMALL LETTER Z CURL;;;;
+0292;LATIN SMALL LETTER EZH;Ll;0;L;;;;;N;LATIN SMALL LETTER YOGH;;01B7;;01B7
+0293;LATIN SMALL LETTER EZH WITH CURL;Ll;0;L;;;;;N;LATIN SMALL LETTER YOGH CURL;;;;
+0294;LATIN LETTER GLOTTAL STOP;Ll;0;L;;;;;N;;;;;
+0295;LATIN LETTER PHARYNGEAL VOICED FRICATIVE;Ll;0;L;;;;;N;LATIN LETTER REVERSED GLOTTAL STOP;;;;
+0296;LATIN LETTER INVERTED GLOTTAL STOP;Ll;0;L;;;;;N;;;;;
+0297;LATIN LETTER STRETCHED C;Ll;0;L;;;;;N;;;;;
+0298;LATIN LETTER BILABIAL CLICK;Ll;0;L;;;;;N;LATIN LETTER BULLSEYE;;;;
+0299;LATIN LETTER SMALL CAPITAL B;Ll;0;L;;;;;N;;;;;
+029A;LATIN SMALL LETTER CLOSED OPEN E;Ll;0;L;;;;;N;LATIN SMALL LETTER CLOSED EPSILON;;;;
+029B;LATIN LETTER SMALL CAPITAL G WITH HOOK;Ll;0;L;;;;;N;LATIN LETTER SMALL CAPITAL G HOOK;;;;
+029C;LATIN LETTER SMALL CAPITAL H;Ll;0;L;;;;;N;;;;;
+029D;LATIN SMALL LETTER J WITH CROSSED-TAIL;Ll;0;L;;;;;N;LATIN SMALL LETTER CROSSED-TAIL J;;;;
+029E;LATIN SMALL LETTER TURNED K;Ll;0;L;;;;;N;;;;;
+029F;LATIN LETTER SMALL CAPITAL L;Ll;0;L;;;;;N;;;;;
+02A0;LATIN SMALL LETTER Q WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER Q HOOK;;;;
+02A1;LATIN LETTER GLOTTAL STOP WITH STROKE;Ll;0;L;;;;;N;LATIN LETTER GLOTTAL STOP BAR;;;;
+02A2;LATIN LETTER REVERSED GLOTTAL STOP WITH STROKE;Ll;0;L;;;;;N;LATIN LETTER REVERSED GLOTTAL STOP BAR;;;;
+02A3;LATIN SMALL LETTER DZ DIGRAPH;Ll;0;L;;;;;N;LATIN SMALL LETTER D Z;;;;
+02A4;LATIN SMALL LETTER DEZH DIGRAPH;Ll;0;L;;;;;N;LATIN SMALL LETTER D YOGH;;;;
+02A5;LATIN SMALL LETTER DZ DIGRAPH WITH CURL;Ll;0;L;;;;;N;LATIN SMALL LETTER D Z CURL;;;;
+02A6;LATIN SMALL LETTER TS DIGRAPH;Ll;0;L;;;;;N;LATIN SMALL LETTER T S;;;;
+02A7;LATIN SMALL LETTER TESH DIGRAPH;Ll;0;L;;;;;N;LATIN SMALL LETTER T ESH;;;;
+02A8;LATIN SMALL LETTER TC DIGRAPH WITH CURL;Ll;0;L;;;;;N;LATIN SMALL LETTER T C CURL;;;;
+02A9;LATIN SMALL LETTER FENG DIGRAPH;Ll;0;L;;;;;N;;;;;
+02AA;LATIN SMALL LETTER LS DIGRAPH;Ll;0;L;;;;;N;;;;;
+02AB;LATIN SMALL LETTER LZ DIGRAPH;Ll;0;L;;;;;N;;;;;
+02AC;LATIN LETTER BILABIAL PERCUSSIVE;Ll;0;L;;;;;N;;;;;
+02AD;LATIN LETTER BIDENTAL PERCUSSIVE;Ll;0;L;;;;;N;;;;;
+02AE;LATIN SMALL LETTER TURNED H WITH FISHHOOK;Ll;0;L;;;;;N;;;;;
+02AF;LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL;Ll;0;L;;;;;N;;;;;
+02B0;MODIFIER LETTER SMALL H;Lm;0;L;<super> 0068;;;;N;;;;;
+02B1;MODIFIER LETTER SMALL H WITH HOOK;Lm;0;L;<super> 0266;;;;N;MODIFIER LETTER SMALL H HOOK;;;;
+02B2;MODIFIER LETTER SMALL J;Lm;0;L;<super> 006A;;;;N;;;;;
+02B3;MODIFIER LETTER SMALL R;Lm;0;L;<super> 0072;;;;N;;;;;
+02B4;MODIFIER LETTER SMALL TURNED R;Lm;0;L;<super> 0279;;;;N;;;;;
+02B5;MODIFIER LETTER SMALL TURNED R WITH HOOK;Lm;0;L;<super> 027B;;;;N;MODIFIER LETTER SMALL TURNED R HOOK;;;;
+02B6;MODIFIER LETTER SMALL CAPITAL INVERTED R;Lm;0;L;<super> 0281;;;;N;;;;;
+02B7;MODIFIER LETTER SMALL W;Lm;0;L;<super> 0077;;;;N;;;;;
+02B8;MODIFIER LETTER SMALL Y;Lm;0;L;<super> 0079;;;;N;;;;;
+02B9;MODIFIER LETTER PRIME;Lm;0;ON;;;;;N;;;;;
+02BA;MODIFIER LETTER DOUBLE PRIME;Lm;0;ON;;;;;N;;;;;
+02BB;MODIFIER LETTER TURNED COMMA;Lm;0;L;;;;;N;;;;;
+02BC;MODIFIER LETTER APOSTROPHE;Lm;0;L;;;;;N;;;;;
+02BD;MODIFIER LETTER REVERSED COMMA;Lm;0;L;;;;;N;;;;;
+02BE;MODIFIER LETTER RIGHT HALF RING;Lm;0;L;;;;;N;;;;;
+02BF;MODIFIER LETTER LEFT HALF RING;Lm;0;L;;;;;N;;;;;
+02C0;MODIFIER LETTER GLOTTAL STOP;Lm;0;L;;;;;N;;;;;
+02C1;MODIFIER LETTER REVERSED GLOTTAL STOP;Lm;0;L;;;;;N;;;;;
+02C2;MODIFIER LETTER LEFT ARROWHEAD;Sk;0;ON;;;;;N;;;;;
+02C3;MODIFIER LETTER RIGHT ARROWHEAD;Sk;0;ON;;;;;N;;;;;
+02C4;MODIFIER LETTER UP ARROWHEAD;Sk;0;ON;;;;;N;;;;;
+02C5;MODIFIER LETTER DOWN ARROWHEAD;Sk;0;ON;;;;;N;;;;;
+02C6;MODIFIER LETTER CIRCUMFLEX ACCENT;Lm;0;ON;;;;;N;MODIFIER LETTER CIRCUMFLEX;;;;
+02C7;CARON;Lm;0;ON;;;;;N;MODIFIER LETTER HACEK;Mandarin Chinese third tone;;;
+02C8;MODIFIER LETTER VERTICAL LINE;Lm;0;ON;;;;;N;;;;;
+02C9;MODIFIER LETTER MACRON;Lm;0;ON;;;;;N;;Mandarin Chinese first tone;;;
+02CA;MODIFIER LETTER ACUTE ACCENT;Lm;0;ON;;;;;N;MODIFIER LETTER ACUTE;Mandarin Chinese second tone;;;
+02CB;MODIFIER LETTER GRAVE ACCENT;Lm;0;ON;;;;;N;MODIFIER LETTER GRAVE;Mandarin Chinese fourth tone;;;
+02CC;MODIFIER LETTER LOW VERTICAL LINE;Lm;0;ON;;;;;N;;;;;
+02CD;MODIFIER LETTER LOW MACRON;Lm;0;ON;;;;;N;;;;;
+02CE;MODIFIER LETTER LOW GRAVE ACCENT;Lm;0;ON;;;;;N;MODIFIER LETTER LOW GRAVE;;;;
+02CF;MODIFIER LETTER LOW ACUTE ACCENT;Lm;0;ON;;;;;N;MODIFIER LETTER LOW ACUTE;;;;
+02D0;MODIFIER LETTER TRIANGULAR COLON;Lm;0;L;;;;;N;;;;;
+02D1;MODIFIER LETTER HALF TRIANGULAR COLON;Lm;0;L;;;;;N;;;;;
+02D2;MODIFIER LETTER CENTRED RIGHT HALF RING;Sk;0;ON;;;;;N;MODIFIER LETTER CENTERED RIGHT HALF RING;;;;
+02D3;MODIFIER LETTER CENTRED LEFT HALF RING;Sk;0;ON;;;;;N;MODIFIER LETTER CENTERED LEFT HALF RING;;;;
+02D4;MODIFIER LETTER UP TACK;Sk;0;ON;;;;;N;;;;;
+02D5;MODIFIER LETTER DOWN TACK;Sk;0;ON;;;;;N;;;;;
+02D6;MODIFIER LETTER PLUS SIGN;Sk;0;ON;;;;;N;;;;;
+02D7;MODIFIER LETTER MINUS SIGN;Sk;0;ON;;;;;N;;;;;
+02D8;BREVE;Sk;0;ON;<compat> 0020 0306;;;;N;SPACING BREVE;;;;
+02D9;DOT ABOVE;Sk;0;ON;<compat> 0020 0307;;;;N;SPACING DOT ABOVE;Mandarin Chinese light tone;;;
+02DA;RING ABOVE;Sk;0;ON;<compat> 0020 030A;;;;N;SPACING RING ABOVE;;;;
+02DB;OGONEK;Sk;0;ON;<compat> 0020 0328;;;;N;SPACING OGONEK;;;;
+02DC;SMALL TILDE;Sk;0;ON;<compat> 0020 0303;;;;N;SPACING TILDE;;;;
+02DD;DOUBLE ACUTE ACCENT;Sk;0;ON;<compat> 0020 030B;;;;N;SPACING DOUBLE ACUTE;;;;
+02DE;MODIFIER LETTER RHOTIC HOOK;Sk;0;ON;;;;;N;;;;;
+02DF;MODIFIER LETTER CROSS ACCENT;Sk;0;ON;;;;;N;;;;;
+02E0;MODIFIER LETTER SMALL GAMMA;Lm;0;L;<super> 0263;;;;N;;;;;
+02E1;MODIFIER LETTER SMALL L;Lm;0;L;<super> 006C;;;;N;;;;;
+02E2;MODIFIER LETTER SMALL S;Lm;0;L;<super> 0073;;;;N;;;;;
+02E3;MODIFIER LETTER SMALL X;Lm;0;L;<super> 0078;;;;N;;;;;
+02E4;MODIFIER LETTER SMALL REVERSED GLOTTAL STOP;Lm;0;L;<super> 0295;;;;N;;;;;
+02E5;MODIFIER LETTER EXTRA-HIGH TONE BAR;Sk;0;ON;;;;;N;;;;;
+02E6;MODIFIER LETTER HIGH TONE BAR;Sk;0;ON;;;;;N;;;;;
+02E7;MODIFIER LETTER MID TONE BAR;Sk;0;ON;;;;;N;;;;;
+02E8;MODIFIER LETTER LOW TONE BAR;Sk;0;ON;;;;;N;;;;;
+02E9;MODIFIER LETTER EXTRA-LOW TONE BAR;Sk;0;ON;;;;;N;;;;;
+02EA;MODIFIER LETTER YIN DEPARTING TONE MARK;Sk;0;ON;;;;;N;;;;;
+02EB;MODIFIER LETTER YANG DEPARTING TONE MARK;Sk;0;ON;;;;;N;;;;;
+02EC;MODIFIER LETTER VOICING;Sk;0;ON;;;;;N;;;;;
+02ED;MODIFIER LETTER UNASPIRATED;Sk;0;ON;;;;;N;;;;;
+02EE;MODIFIER LETTER DOUBLE APOSTROPHE;Lm;0;L;;;;;N;;;;;
+02EF;MODIFIER LETTER LOW DOWN ARROWHEAD;Sk;0;ON;;;;;N;;;;;
+02F0;MODIFIER LETTER LOW UP ARROWHEAD;Sk;0;ON;;;;;N;;;;;
+02F1;MODIFIER LETTER LOW LEFT ARROWHEAD;Sk;0;ON;;;;;N;;;;;
+02F2;MODIFIER LETTER LOW RIGHT ARROWHEAD;Sk;0;ON;;;;;N;;;;;
+02F3;MODIFIER LETTER LOW RING;Sk;0;ON;;;;;N;;;;;
+02F4;MODIFIER LETTER MIDDLE GRAVE ACCENT;Sk;0;ON;;;;;N;;;;;
+02F5;MODIFIER LETTER MIDDLE DOUBLE GRAVE ACCENT;Sk;0;ON;;;;;N;;;;;
+02F6;MODIFIER LETTER MIDDLE DOUBLE ACUTE ACCENT;Sk;0;ON;;;;;N;;;;;
+02F7;MODIFIER LETTER LOW TILDE;Sk;0;ON;;;;;N;;;;;
+02F8;MODIFIER LETTER RAISED COLON;Sk;0;ON;;;;;N;;;;;
+02F9;MODIFIER LETTER BEGIN HIGH TONE;Sk;0;ON;;;;;N;;;;;
+02FA;MODIFIER LETTER END HIGH TONE;Sk;0;ON;;;;;N;;;;;
+02FB;MODIFIER LETTER BEGIN LOW TONE;Sk;0;ON;;;;;N;;;;;
+02FC;MODIFIER LETTER END LOW TONE;Sk;0;ON;;;;;N;;;;;
+02FD;MODIFIER LETTER SHELF;Sk;0;ON;;;;;N;;;;;
+02FE;MODIFIER LETTER OPEN SHELF;Sk;0;ON;;;;;N;;;;;
+02FF;MODIFIER LETTER LOW LEFT ARROW;Sk;0;ON;;;;;N;;;;;
+0300;COMBINING GRAVE ACCENT;Mn;230;NSM;;;;;N;NON-SPACING GRAVE;Varia;;;
+0301;COMBINING ACUTE ACCENT;Mn;230;NSM;;;;;N;NON-SPACING ACUTE;Oxia, Tonos;;;
+0302;COMBINING CIRCUMFLEX ACCENT;Mn;230;NSM;;;;;N;NON-SPACING CIRCUMFLEX;;;;
+0303;COMBINING TILDE;Mn;230;NSM;;;;;N;NON-SPACING TILDE;;;;
+0304;COMBINING MACRON;Mn;230;NSM;;;;;N;NON-SPACING MACRON;;;;
+0305;COMBINING OVERLINE;Mn;230;NSM;;;;;N;NON-SPACING OVERSCORE;;;;
+0306;COMBINING BREVE;Mn;230;NSM;;;;;N;NON-SPACING BREVE;Vrachy;;;
+0307;COMBINING DOT ABOVE;Mn;230;NSM;;;;;N;NON-SPACING DOT ABOVE;;;;
+0308;COMBINING DIAERESIS;Mn;230;NSM;;;;;N;NON-SPACING DIAERESIS;Dialytika;;;
+0309;COMBINING HOOK ABOVE;Mn;230;NSM;;;;;N;NON-SPACING HOOK ABOVE;;;;
+030A;COMBINING RING ABOVE;Mn;230;NSM;;;;;N;NON-SPACING RING ABOVE;;;;
+030B;COMBINING DOUBLE ACUTE ACCENT;Mn;230;NSM;;;;;N;NON-SPACING DOUBLE ACUTE;;;;
+030C;COMBINING CARON;Mn;230;NSM;;;;;N;NON-SPACING HACEK;;;;
+030D;COMBINING VERTICAL LINE ABOVE;Mn;230;NSM;;;;;N;NON-SPACING VERTICAL LINE ABOVE;;;;
+030E;COMBINING DOUBLE VERTICAL LINE ABOVE;Mn;230;NSM;;;;;N;NON-SPACING DOUBLE VERTICAL LINE ABOVE;;;;
+030F;COMBINING DOUBLE GRAVE ACCENT;Mn;230;NSM;;;;;N;NON-SPACING DOUBLE GRAVE;;;;
+0310;COMBINING CANDRABINDU;Mn;230;NSM;;;;;N;NON-SPACING CANDRABINDU;;;;
+0311;COMBINING INVERTED BREVE;Mn;230;NSM;;;;;N;NON-SPACING INVERTED BREVE;;;;
+0312;COMBINING TURNED COMMA ABOVE;Mn;230;NSM;;;;;N;NON-SPACING TURNED COMMA ABOVE;;;;
+0313;COMBINING COMMA ABOVE;Mn;230;NSM;;;;;N;NON-SPACING COMMA ABOVE;Psili;;;
+0314;COMBINING REVERSED COMMA ABOVE;Mn;230;NSM;;;;;N;NON-SPACING REVERSED COMMA ABOVE;Dasia;;;
+0315;COMBINING COMMA ABOVE RIGHT;Mn;232;NSM;;;;;N;NON-SPACING COMMA ABOVE RIGHT;;;;
+0316;COMBINING GRAVE ACCENT BELOW;Mn;220;NSM;;;;;N;NON-SPACING GRAVE BELOW;;;;
+0317;COMBINING ACUTE ACCENT BELOW;Mn;220;NSM;;;;;N;NON-SPACING ACUTE BELOW;;;;
+0318;COMBINING LEFT TACK BELOW;Mn;220;NSM;;;;;N;NON-SPACING LEFT TACK BELOW;;;;
+0319;COMBINING RIGHT TACK BELOW;Mn;220;NSM;;;;;N;NON-SPACING RIGHT TACK BELOW;;;;
+031A;COMBINING LEFT ANGLE ABOVE;Mn;232;NSM;;;;;N;NON-SPACING LEFT ANGLE ABOVE;;;;
+031B;COMBINING HORN;Mn;216;NSM;;;;;N;NON-SPACING HORN;;;;
+031C;COMBINING LEFT HALF RING BELOW;Mn;220;NSM;;;;;N;NON-SPACING LEFT HALF RING BELOW;;;;
+031D;COMBINING UP TACK BELOW;Mn;220;NSM;;;;;N;NON-SPACING UP TACK BELOW;;;;
+031E;COMBINING DOWN TACK BELOW;Mn;220;NSM;;;;;N;NON-SPACING DOWN TACK BELOW;;;;
+031F;COMBINING PLUS SIGN BELOW;Mn;220;NSM;;;;;N;NON-SPACING PLUS SIGN BELOW;;;;
+0320;COMBINING MINUS SIGN BELOW;Mn;220;NSM;;;;;N;NON-SPACING MINUS SIGN BELOW;;;;
+0321;COMBINING PALATALIZED HOOK BELOW;Mn;202;NSM;;;;;N;NON-SPACING PALATALIZED HOOK BELOW;;;;
+0322;COMBINING RETROFLEX HOOK BELOW;Mn;202;NSM;;;;;N;NON-SPACING RETROFLEX HOOK BELOW;;;;
+0323;COMBINING DOT BELOW;Mn;220;NSM;;;;;N;NON-SPACING DOT BELOW;;;;
+0324;COMBINING DIAERESIS BELOW;Mn;220;NSM;;;;;N;NON-SPACING DOUBLE DOT BELOW;;;;
+0325;COMBINING RING BELOW;Mn;220;NSM;;;;;N;NON-SPACING RING BELOW;;;;
+0326;COMBINING COMMA BELOW;Mn;220;NSM;;;;;N;NON-SPACING COMMA BELOW;;;;
+0327;COMBINING CEDILLA;Mn;202;NSM;;;;;N;NON-SPACING CEDILLA;;;;
+0328;COMBINING OGONEK;Mn;202;NSM;;;;;N;NON-SPACING OGONEK;;;;
+0329;COMBINING VERTICAL LINE BELOW;Mn;220;NSM;;;;;N;NON-SPACING VERTICAL LINE BELOW;;;;
+032A;COMBINING BRIDGE BELOW;Mn;220;NSM;;;;;N;NON-SPACING BRIDGE BELOW;;;;
+032B;COMBINING INVERTED DOUBLE ARCH BELOW;Mn;220;NSM;;;;;N;NON-SPACING INVERTED DOUBLE ARCH BELOW;;;;
+032C;COMBINING CARON BELOW;Mn;220;NSM;;;;;N;NON-SPACING HACEK BELOW;;;;
+032D;COMBINING CIRCUMFLEX ACCENT BELOW;Mn;220;NSM;;;;;N;NON-SPACING CIRCUMFLEX BELOW;;;;
+032E;COMBINING BREVE BELOW;Mn;220;NSM;;;;;N;NON-SPACING BREVE BELOW;;;;
+032F;COMBINING INVERTED BREVE BELOW;Mn;220;NSM;;;;;N;NON-SPACING INVERTED BREVE BELOW;;;;
+0330;COMBINING TILDE BELOW;Mn;220;NSM;;;;;N;NON-SPACING TILDE BELOW;;;;
+0331;COMBINING MACRON BELOW;Mn;220;NSM;;;;;N;NON-SPACING MACRON BELOW;;;;
+0332;COMBINING LOW LINE;Mn;220;NSM;;;;;N;NON-SPACING UNDERSCORE;;;;
+0333;COMBINING DOUBLE LOW LINE;Mn;220;NSM;;;;;N;NON-SPACING DOUBLE UNDERSCORE;;;;
+0334;COMBINING TILDE OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING TILDE OVERLAY;;;;
+0335;COMBINING SHORT STROKE OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING SHORT BAR OVERLAY;;;;
+0336;COMBINING LONG STROKE OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING LONG BAR OVERLAY;;;;
+0337;COMBINING SHORT SOLIDUS OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING SHORT SLASH OVERLAY;;;;
+0338;COMBINING LONG SOLIDUS OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING LONG SLASH OVERLAY;;;;
+0339;COMBINING RIGHT HALF RING BELOW;Mn;220;NSM;;;;;N;NON-SPACING RIGHT HALF RING BELOW;;;;
+033A;COMBINING INVERTED BRIDGE BELOW;Mn;220;NSM;;;;;N;NON-SPACING INVERTED BRIDGE BELOW;;;;
+033B;COMBINING SQUARE BELOW;Mn;220;NSM;;;;;N;NON-SPACING SQUARE BELOW;;;;
+033C;COMBINING SEAGULL BELOW;Mn;220;NSM;;;;;N;NON-SPACING SEAGULL BELOW;;;;
+033D;COMBINING X ABOVE;Mn;230;NSM;;;;;N;NON-SPACING X ABOVE;;;;
+033E;COMBINING VERTICAL TILDE;Mn;230;NSM;;;;;N;NON-SPACING VERTICAL TILDE;;;;
+033F;COMBINING DOUBLE OVERLINE;Mn;230;NSM;;;;;N;NON-SPACING DOUBLE OVERSCORE;;;;
+0340;COMBINING GRAVE TONE MARK;Mn;230;NSM;0300;;;;N;NON-SPACING GRAVE TONE MARK;Vietnamese;;;
+0341;COMBINING ACUTE TONE MARK;Mn;230;NSM;0301;;;;N;NON-SPACING ACUTE TONE MARK;Vietnamese;;;
+0342;COMBINING GREEK PERISPOMENI;Mn;230;NSM;;;;;N;;;;;
+0343;COMBINING GREEK KORONIS;Mn;230;NSM;0313;;;;N;;;;;
+0344;COMBINING GREEK DIALYTIKA TONOS;Mn;230;NSM;0308 0301;;;;N;GREEK NON-SPACING DIAERESIS TONOS;;;;
+0345;COMBINING GREEK YPOGEGRAMMENI;Mn;240;NSM;;;;;N;GREEK NON-SPACING IOTA BELOW;;0399;;0399
+0346;COMBINING BRIDGE ABOVE;Mn;230;NSM;;;;;N;;;;;
+0347;COMBINING EQUALS SIGN BELOW;Mn;220;NSM;;;;;N;;;;;
+0348;COMBINING DOUBLE VERTICAL LINE BELOW;Mn;220;NSM;;;;;N;;;;;
+0349;COMBINING LEFT ANGLE BELOW;Mn;220;NSM;;;;;N;;;;;
+034A;COMBINING NOT TILDE ABOVE;Mn;230;NSM;;;;;N;;;;;
+034B;COMBINING HOMOTHETIC ABOVE;Mn;230;NSM;;;;;N;;;;;
+034C;COMBINING ALMOST EQUAL TO ABOVE;Mn;230;NSM;;;;;N;;;;;
+034D;COMBINING LEFT RIGHT ARROW BELOW;Mn;220;NSM;;;;;N;;;;;
+034E;COMBINING UPWARDS ARROW BELOW;Mn;220;NSM;;;;;N;;;;;
+034F;COMBINING GRAPHEME JOINER;Mn;0;NSM;;;;;N;;;;;
+0350;COMBINING RIGHT ARROWHEAD ABOVE;Mn;230;NSM;;;;;N;;;;;
+0351;COMBINING LEFT HALF RING ABOVE;Mn;230;NSM;;;;;N;;;;;
+0352;COMBINING FERMATA;Mn;230;NSM;;;;;N;;;;;
+0353;COMBINING X BELOW;Mn;220;NSM;;;;;N;;;;;
+0354;COMBINING LEFT ARROWHEAD BELOW;Mn;220;NSM;;;;;N;;;;;
+0355;COMBINING RIGHT ARROWHEAD BELOW;Mn;220;NSM;;;;;N;;;;;
+0356;COMBINING RIGHT ARROWHEAD AND UP ARROWHEAD BELOW;Mn;220;NSM;;;;;N;;;;;
+0357;COMBINING RIGHT HALF RING ABOVE;Mn;230;NSM;;;;;N;;;;;
+035D;COMBINING DOUBLE BREVE;Mn;234;NSM;;;;;N;;;;;
+035E;COMBINING DOUBLE MACRON;Mn;234;NSM;;;;;N;;;;;
+035F;COMBINING DOUBLE MACRON BELOW;Mn;233;NSM;;;;;N;;;;;
+0360;COMBINING DOUBLE TILDE;Mn;234;NSM;;;;;N;;;;;
+0361;COMBINING DOUBLE INVERTED BREVE;Mn;234;NSM;;;;;N;;;;;
+0362;COMBINING DOUBLE RIGHTWARDS ARROW BELOW;Mn;233;NSM;;;;;N;;;;;
+0363;COMBINING LATIN SMALL LETTER A;Mn;230;NSM;;;;;N;;;;;
+0364;COMBINING LATIN SMALL LETTER E;Mn;230;NSM;;;;;N;;;;;
+0365;COMBINING LATIN SMALL LETTER I;Mn;230;NSM;;;;;N;;;;;
+0366;COMBINING LATIN SMALL LETTER O;Mn;230;NSM;;;;;N;;;;;
+0367;COMBINING LATIN SMALL LETTER U;Mn;230;NSM;;;;;N;;;;;
+0368;COMBINING LATIN SMALL LETTER C;Mn;230;NSM;;;;;N;;;;;
+0369;COMBINING LATIN SMALL LETTER D;Mn;230;NSM;;;;;N;;;;;
+036A;COMBINING LATIN SMALL LETTER H;Mn;230;NSM;;;;;N;;;;;
+036B;COMBINING LATIN SMALL LETTER M;Mn;230;NSM;;;;;N;;;;;
+036C;COMBINING LATIN SMALL LETTER R;Mn;230;NSM;;;;;N;;;;;
+036D;COMBINING LATIN SMALL LETTER T;Mn;230;NSM;;;;;N;;;;;
+036E;COMBINING LATIN SMALL LETTER V;Mn;230;NSM;;;;;N;;;;;
+036F;COMBINING LATIN SMALL LETTER X;Mn;230;NSM;;;;;N;;;;;
+0374;GREEK NUMERAL SIGN;Sk;0;ON;02B9;;;;N;GREEK UPPER NUMERAL SIGN;Dexia keraia;;;
+0375;GREEK LOWER NUMERAL SIGN;Sk;0;ON;;;;;N;;Aristeri keraia;;;
+037A;GREEK YPOGEGRAMMENI;Lm;0;L;<compat> 0020 0345;;;;N;GREEK SPACING IOTA BELOW;;;;
+037E;GREEK QUESTION MARK;Po;0;ON;003B;;;;N;;Erotimatiko;;;
+0384;GREEK TONOS;Sk;0;ON;<compat> 0020 0301;;;;N;GREEK SPACING TONOS;;;;
+0385;GREEK DIALYTIKA TONOS;Sk;0;ON;00A8 0301;;;;N;GREEK SPACING DIAERESIS TONOS;;;;
+0386;GREEK CAPITAL LETTER ALPHA WITH TONOS;Lu;0;L;0391 0301;;;;N;GREEK CAPITAL LETTER ALPHA TONOS;;;03AC;
+0387;GREEK ANO TELEIA;Po;0;ON;00B7;;;;N;;;;;
+0388;GREEK CAPITAL LETTER EPSILON WITH TONOS;Lu;0;L;0395 0301;;;;N;GREEK CAPITAL LETTER EPSILON TONOS;;;03AD;
+0389;GREEK CAPITAL LETTER ETA WITH TONOS;Lu;0;L;0397 0301;;;;N;GREEK CAPITAL LETTER ETA TONOS;;;03AE;
+038A;GREEK CAPITAL LETTER IOTA WITH TONOS;Lu;0;L;0399 0301;;;;N;GREEK CAPITAL LETTER IOTA TONOS;;;03AF;
+038C;GREEK CAPITAL LETTER OMICRON WITH TONOS;Lu;0;L;039F 0301;;;;N;GREEK CAPITAL LETTER OMICRON TONOS;;;03CC;
+038E;GREEK CAPITAL LETTER UPSILON WITH TONOS;Lu;0;L;03A5 0301;;;;N;GREEK CAPITAL LETTER UPSILON TONOS;;;03CD;
+038F;GREEK CAPITAL LETTER OMEGA WITH TONOS;Lu;0;L;03A9 0301;;;;N;GREEK CAPITAL LETTER OMEGA TONOS;;;03CE;
+0390;GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS;Ll;0;L;03CA 0301;;;;N;GREEK SMALL LETTER IOTA DIAERESIS TONOS;;;;
+0391;GREEK CAPITAL LETTER ALPHA;Lu;0;L;;;;;N;;;;03B1;
+0392;GREEK CAPITAL LETTER BETA;Lu;0;L;;;;;N;;;;03B2;
+0393;GREEK CAPITAL LETTER GAMMA;Lu;0;L;;;;;N;;;;03B3;
+0394;GREEK CAPITAL LETTER DELTA;Lu;0;L;;;;;N;;;;03B4;
+0395;GREEK CAPITAL LETTER EPSILON;Lu;0;L;;;;;N;;;;03B5;
+0396;GREEK CAPITAL LETTER ZETA;Lu;0;L;;;;;N;;;;03B6;
+0397;GREEK CAPITAL LETTER ETA;Lu;0;L;;;;;N;;;;03B7;
+0398;GREEK CAPITAL LETTER THETA;Lu;0;L;;;;;N;;;;03B8;
+0399;GREEK CAPITAL LETTER IOTA;Lu;0;L;;;;;N;;;;03B9;
+039A;GREEK CAPITAL LETTER KAPPA;Lu;0;L;;;;;N;;;;03BA;
+039B;GREEK CAPITAL LETTER LAMDA;Lu;0;L;;;;;N;GREEK CAPITAL LETTER LAMBDA;;;03BB;
+039C;GREEK CAPITAL LETTER MU;Lu;0;L;;;;;N;;;;03BC;
+039D;GREEK CAPITAL LETTER NU;Lu;0;L;;;;;N;;;;03BD;
+039E;GREEK CAPITAL LETTER XI;Lu;0;L;;;;;N;;;;03BE;
+039F;GREEK CAPITAL LETTER OMICRON;Lu;0;L;;;;;N;;;;03BF;
+03A0;GREEK CAPITAL LETTER PI;Lu;0;L;;;;;N;;;;03C0;
+03A1;GREEK CAPITAL LETTER RHO;Lu;0;L;;;;;N;;;;03C1;
+03A3;GREEK CAPITAL LETTER SIGMA;Lu;0;L;;;;;N;;;;03C3;
+03A4;GREEK CAPITAL LETTER TAU;Lu;0;L;;;;;N;;;;03C4;
+03A5;GREEK CAPITAL LETTER UPSILON;Lu;0;L;;;;;N;;;;03C5;
+03A6;GREEK CAPITAL LETTER PHI;Lu;0;L;;;;;N;;;;03C6;
+03A7;GREEK CAPITAL LETTER CHI;Lu;0;L;;;;;N;;;;03C7;
+03A8;GREEK CAPITAL LETTER PSI;Lu;0;L;;;;;N;;;;03C8;
+03A9;GREEK CAPITAL LETTER OMEGA;Lu;0;L;;;;;N;;;;03C9;
+03AA;GREEK CAPITAL LETTER IOTA WITH DIALYTIKA;Lu;0;L;0399 0308;;;;N;GREEK CAPITAL LETTER IOTA DIAERESIS;;;03CA;
+03AB;GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA;Lu;0;L;03A5 0308;;;;N;GREEK CAPITAL LETTER UPSILON DIAERESIS;;;03CB;
+03AC;GREEK SMALL LETTER ALPHA WITH TONOS;Ll;0;L;03B1 0301;;;;N;GREEK SMALL LETTER ALPHA TONOS;;0386;;0386
+03AD;GREEK SMALL LETTER EPSILON WITH TONOS;Ll;0;L;03B5 0301;;;;N;GREEK SMALL LETTER EPSILON TONOS;;0388;;0388
+03AE;GREEK SMALL LETTER ETA WITH TONOS;Ll;0;L;03B7 0301;;;;N;GREEK SMALL LETTER ETA TONOS;;0389;;0389
+03AF;GREEK SMALL LETTER IOTA WITH TONOS;Ll;0;L;03B9 0301;;;;N;GREEK SMALL LETTER IOTA TONOS;;038A;;038A
+03B0;GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS;Ll;0;L;03CB 0301;;;;N;GREEK SMALL LETTER UPSILON DIAERESIS TONOS;;;;
+03B1;GREEK SMALL LETTER ALPHA;Ll;0;L;;;;;N;;;0391;;0391
+03B2;GREEK SMALL LETTER BETA;Ll;0;L;;;;;N;;;0392;;0392
+03B3;GREEK SMALL LETTER GAMMA;Ll;0;L;;;;;N;;;0393;;0393
+03B4;GREEK SMALL LETTER DELTA;Ll;0;L;;;;;N;;;0394;;0394
+03B5;GREEK SMALL LETTER EPSILON;Ll;0;L;;;;;N;;;0395;;0395
+03B6;GREEK SMALL LETTER ZETA;Ll;0;L;;;;;N;;;0396;;0396
+03B7;GREEK SMALL LETTER ETA;Ll;0;L;;;;;N;;;0397;;0397
+03B8;GREEK SMALL LETTER THETA;Ll;0;L;;;;;N;;;0398;;0398
+03B9;GREEK SMALL LETTER IOTA;Ll;0;L;;;;;N;;;0399;;0399
+03BA;GREEK SMALL LETTER KAPPA;Ll;0;L;;;;;N;;;039A;;039A
+03BB;GREEK SMALL LETTER LAMDA;Ll;0;L;;;;;N;GREEK SMALL LETTER LAMBDA;;039B;;039B
+03BC;GREEK SMALL LETTER MU;Ll;0;L;;;;;N;;;039C;;039C
+03BD;GREEK SMALL LETTER NU;Ll;0;L;;;;;N;;;039D;;039D
+03BE;GREEK SMALL LETTER XI;Ll;0;L;;;;;N;;;039E;;039E
+03BF;GREEK SMALL LETTER OMICRON;Ll;0;L;;;;;N;;;039F;;039F
+03C0;GREEK SMALL LETTER PI;Ll;0;L;;;;;N;;;03A0;;03A0
+03C1;GREEK SMALL LETTER RHO;Ll;0;L;;;;;N;;;03A1;;03A1
+03C2;GREEK SMALL LETTER FINAL SIGMA;Ll;0;L;;;;;N;;;03A3;;03A3
+03C3;GREEK SMALL LETTER SIGMA;Ll;0;L;;;;;N;;;03A3;;03A3
+03C4;GREEK SMALL LETTER TAU;Ll;0;L;;;;;N;;;03A4;;03A4
+03C5;GREEK SMALL LETTER UPSILON;Ll;0;L;;;;;N;;;03A5;;03A5
+03C6;GREEK SMALL LETTER PHI;Ll;0;L;;;;;N;;;03A6;;03A6
+03C7;GREEK SMALL LETTER CHI;Ll;0;L;;;;;N;;;03A7;;03A7
+03C8;GREEK SMALL LETTER PSI;Ll;0;L;;;;;N;;;03A8;;03A8
+03C9;GREEK SMALL LETTER OMEGA;Ll;0;L;;;;;N;;;03A9;;03A9
+03CA;GREEK SMALL LETTER IOTA WITH DIALYTIKA;Ll;0;L;03B9 0308;;;;N;GREEK SMALL LETTER IOTA DIAERESIS;;03AA;;03AA
+03CB;GREEK SMALL LETTER UPSILON WITH DIALYTIKA;Ll;0;L;03C5 0308;;;;N;GREEK SMALL LETTER UPSILON DIAERESIS;;03AB;;03AB
+03CC;GREEK SMALL LETTER OMICRON WITH TONOS;Ll;0;L;03BF 0301;;;;N;GREEK SMALL LETTER OMICRON TONOS;;038C;;038C
+03CD;GREEK SMALL LETTER UPSILON WITH TONOS;Ll;0;L;03C5 0301;;;;N;GREEK SMALL LETTER UPSILON TONOS;;038E;;038E
+03CE;GREEK SMALL LETTER OMEGA WITH TONOS;Ll;0;L;03C9 0301;;;;N;GREEK SMALL LETTER OMEGA TONOS;;038F;;038F
+03D0;GREEK BETA SYMBOL;Ll;0;L;<compat> 03B2;;;;N;GREEK SMALL LETTER CURLED BETA;;0392;;0392
+03D1;GREEK THETA SYMBOL;Ll;0;L;<compat> 03B8;;;;N;GREEK SMALL LETTER SCRIPT THETA;;0398;;0398
+03D2;GREEK UPSILON WITH HOOK SYMBOL;Lu;0;L;<compat> 03A5;;;;N;GREEK CAPITAL LETTER UPSILON HOOK;;;;
+03D3;GREEK UPSILON WITH ACUTE AND HOOK SYMBOL;Lu;0;L;03D2 0301;;;;N;GREEK CAPITAL LETTER UPSILON HOOK TONOS;;;;
+03D4;GREEK UPSILON WITH DIAERESIS AND HOOK SYMBOL;Lu;0;L;03D2 0308;;;;N;GREEK CAPITAL LETTER UPSILON HOOK DIAERESIS;;;;
+03D5;GREEK PHI SYMBOL;Ll;0;L;<compat> 03C6;;;;N;GREEK SMALL LETTER SCRIPT PHI;;03A6;;03A6
+03D6;GREEK PI SYMBOL;Ll;0;L;<compat> 03C0;;;;N;GREEK SMALL LETTER OMEGA PI;;03A0;;03A0
+03D7;GREEK KAI SYMBOL;Ll;0;L;;;;;N;;;;;
+03D8;GREEK LETTER ARCHAIC KOPPA;Lu;0;L;;;;;N;;*;;03D9;
+03D9;GREEK SMALL LETTER ARCHAIC KOPPA;Ll;0;L;;;;;N;;*;03D8;;03D8
+03DA;GREEK LETTER STIGMA;Lu;0;L;;;;;N;GREEK CAPITAL LETTER STIGMA;;;03DB;
+03DB;GREEK SMALL LETTER STIGMA;Ll;0;L;;;;;N;;;03DA;;03DA
+03DC;GREEK LETTER DIGAMMA;Lu;0;L;;;;;N;GREEK CAPITAL LETTER DIGAMMA;;;03DD;
+03DD;GREEK SMALL LETTER DIGAMMA;Ll;0;L;;;;;N;;;03DC;;03DC
+03DE;GREEK LETTER KOPPA;Lu;0;L;;;;;N;GREEK CAPITAL LETTER KOPPA;;;03DF;
+03DF;GREEK SMALL LETTER KOPPA;Ll;0;L;;;;;N;;;03DE;;03DE
+03E0;GREEK LETTER SAMPI;Lu;0;L;;;;;N;GREEK CAPITAL LETTER SAMPI;;;03E1;
+03E1;GREEK SMALL LETTER SAMPI;Ll;0;L;;;;;N;;;03E0;;03E0
+03E2;COPTIC CAPITAL LETTER SHEI;Lu;0;L;;;;;N;GREEK CAPITAL LETTER SHEI;;;03E3;
+03E3;COPTIC SMALL LETTER SHEI;Ll;0;L;;;;;N;GREEK SMALL LETTER SHEI;;03E2;;03E2
+03E4;COPTIC CAPITAL LETTER FEI;Lu;0;L;;;;;N;GREEK CAPITAL LETTER FEI;;;03E5;
+03E5;COPTIC SMALL LETTER FEI;Ll;0;L;;;;;N;GREEK SMALL LETTER FEI;;03E4;;03E4
+03E6;COPTIC CAPITAL LETTER KHEI;Lu;0;L;;;;;N;GREEK CAPITAL LETTER KHEI;;;03E7;
+03E7;COPTIC SMALL LETTER KHEI;Ll;0;L;;;;;N;GREEK SMALL LETTER KHEI;;03E6;;03E6
+03E8;COPTIC CAPITAL LETTER HORI;Lu;0;L;;;;;N;GREEK CAPITAL LETTER HORI;;;03E9;
+03E9;COPTIC SMALL LETTER HORI;Ll;0;L;;;;;N;GREEK SMALL LETTER HORI;;03E8;;03E8
+03EA;COPTIC CAPITAL LETTER GANGIA;Lu;0;L;;;;;N;GREEK CAPITAL LETTER GANGIA;;;03EB;
+03EB;COPTIC SMALL LETTER GANGIA;Ll;0;L;;;;;N;GREEK SMALL LETTER GANGIA;;03EA;;03EA
+03EC;COPTIC CAPITAL LETTER SHIMA;Lu;0;L;;;;;N;GREEK CAPITAL LETTER SHIMA;;;03ED;
+03ED;COPTIC SMALL LETTER SHIMA;Ll;0;L;;;;;N;GREEK SMALL LETTER SHIMA;;03EC;;03EC
+03EE;COPTIC CAPITAL LETTER DEI;Lu;0;L;;;;;N;GREEK CAPITAL LETTER DEI;;;03EF;
+03EF;COPTIC SMALL LETTER DEI;Ll;0;L;;;;;N;GREEK SMALL LETTER DEI;;03EE;;03EE
+03F0;GREEK KAPPA SYMBOL;Ll;0;L;<compat> 03BA;;;;N;GREEK SMALL LETTER SCRIPT KAPPA;;039A;;039A
+03F1;GREEK RHO SYMBOL;Ll;0;L;<compat> 03C1;;;;N;GREEK SMALL LETTER TAILED RHO;;03A1;;03A1
+03F2;GREEK LUNATE SIGMA SYMBOL;Ll;0;L;<compat> 03C2;;;;N;GREEK SMALL LETTER LUNATE SIGMA;;03F9;;03F9
+03F3;GREEK LETTER YOT;Ll;0;L;;;;;N;;;;;
+03F4;GREEK CAPITAL THETA SYMBOL;Lu;0;L;<compat> 0398;;;;N;;;;03B8;
+03F5;GREEK LUNATE EPSILON SYMBOL;Ll;0;L;<compat> 03B5;;;;N;;;0395;;0395
+03F6;GREEK REVERSED LUNATE EPSILON SYMBOL;Sm;0;ON;;;;;N;;;;;
+03F7;GREEK CAPITAL LETTER SHO;Lu;0;L;;;;;N;;;;03F8;
+03F8;GREEK SMALL LETTER SHO;Ll;0;L;;;;;N;;;03F7;;03F7
+03F9;GREEK CAPITAL LUNATE SIGMA SYMBOL;Lu;0;L;<compat> 03A3;;;;N;;;;03F2;
+03FA;GREEK CAPITAL LETTER SAN;Lu;0;L;;;;;N;;;;03FB;
+03FB;GREEK SMALL LETTER SAN;Ll;0;L;;;;;N;;;03FA;;03FA
+0400;CYRILLIC CAPITAL LETTER IE WITH GRAVE;Lu;0;L;0415 0300;;;;N;;;;0450;
+0401;CYRILLIC CAPITAL LETTER IO;Lu;0;L;0415 0308;;;;N;;;;0451;
+0402;CYRILLIC CAPITAL LETTER DJE;Lu;0;L;;;;;N;;Serbocroatian;;0452;
+0403;CYRILLIC CAPITAL LETTER GJE;Lu;0;L;0413 0301;;;;N;;;;0453;
+0404;CYRILLIC CAPITAL LETTER UKRAINIAN IE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER E;;;0454;
+0405;CYRILLIC CAPITAL LETTER DZE;Lu;0;L;;;;;N;;;;0455;
+0406;CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER I;;;0456;
+0407;CYRILLIC CAPITAL LETTER YI;Lu;0;L;0406 0308;;;;N;;Ukrainian;;0457;
+0408;CYRILLIC CAPITAL LETTER JE;Lu;0;L;;;;;N;;;;0458;
+0409;CYRILLIC CAPITAL LETTER LJE;Lu;0;L;;;;;N;;;;0459;
+040A;CYRILLIC CAPITAL LETTER NJE;Lu;0;L;;;;;N;;;;045A;
+040B;CYRILLIC CAPITAL LETTER TSHE;Lu;0;L;;;;;N;;Serbocroatian;;045B;
+040C;CYRILLIC CAPITAL LETTER KJE;Lu;0;L;041A 0301;;;;N;;;;045C;
+040D;CYRILLIC CAPITAL LETTER I WITH GRAVE;Lu;0;L;0418 0300;;;;N;;;;045D;
+040E;CYRILLIC CAPITAL LETTER SHORT U;Lu;0;L;0423 0306;;;;N;;Byelorussian;;045E;
+040F;CYRILLIC CAPITAL LETTER DZHE;Lu;0;L;;;;;N;;;;045F;
+0410;CYRILLIC CAPITAL LETTER A;Lu;0;L;;;;;N;;;;0430;
+0411;CYRILLIC CAPITAL LETTER BE;Lu;0;L;;;;;N;;;;0431;
+0412;CYRILLIC CAPITAL LETTER VE;Lu;0;L;;;;;N;;;;0432;
+0413;CYRILLIC CAPITAL LETTER GHE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER GE;;;0433;
+0414;CYRILLIC CAPITAL LETTER DE;Lu;0;L;;;;;N;;;;0434;
+0415;CYRILLIC CAPITAL LETTER IE;Lu;0;L;;;;;N;;;;0435;
+0416;CYRILLIC CAPITAL LETTER ZHE;Lu;0;L;;;;;N;;;;0436;
+0417;CYRILLIC CAPITAL LETTER ZE;Lu;0;L;;;;;N;;;;0437;
+0418;CYRILLIC CAPITAL LETTER I;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER II;;;0438;
+0419;CYRILLIC CAPITAL LETTER SHORT I;Lu;0;L;0418 0306;;;;N;CYRILLIC CAPITAL LETTER SHORT II;;;0439;
+041A;CYRILLIC CAPITAL LETTER KA;Lu;0;L;;;;;N;;;;043A;
+041B;CYRILLIC CAPITAL LETTER EL;Lu;0;L;;;;;N;;;;043B;
+041C;CYRILLIC CAPITAL LETTER EM;Lu;0;L;;;;;N;;;;043C;
+041D;CYRILLIC CAPITAL LETTER EN;Lu;0;L;;;;;N;;;;043D;
+041E;CYRILLIC CAPITAL LETTER O;Lu;0;L;;;;;N;;;;043E;
+041F;CYRILLIC CAPITAL LETTER PE;Lu;0;L;;;;;N;;;;043F;
+0420;CYRILLIC CAPITAL LETTER ER;Lu;0;L;;;;;N;;;;0440;
+0421;CYRILLIC CAPITAL LETTER ES;Lu;0;L;;;;;N;;;;0441;
+0422;CYRILLIC CAPITAL LETTER TE;Lu;0;L;;;;;N;;;;0442;
+0423;CYRILLIC CAPITAL LETTER U;Lu;0;L;;;;;N;;;;0443;
+0424;CYRILLIC CAPITAL LETTER EF;Lu;0;L;;;;;N;;;;0444;
+0425;CYRILLIC CAPITAL LETTER HA;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER KHA;;;0445;
+0426;CYRILLIC CAPITAL LETTER TSE;Lu;0;L;;;;;N;;;;0446;
+0427;CYRILLIC CAPITAL LETTER CHE;Lu;0;L;;;;;N;;;;0447;
+0428;CYRILLIC CAPITAL LETTER SHA;Lu;0;L;;;;;N;;;;0448;
+0429;CYRILLIC CAPITAL LETTER SHCHA;Lu;0;L;;;;;N;;;;0449;
+042A;CYRILLIC CAPITAL LETTER HARD SIGN;Lu;0;L;;;;;N;;;;044A;
+042B;CYRILLIC CAPITAL LETTER YERU;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER YERI;;;044B;
+042C;CYRILLIC CAPITAL LETTER SOFT SIGN;Lu;0;L;;;;;N;;;;044C;
+042D;CYRILLIC CAPITAL LETTER E;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER REVERSED E;;;044D;
+042E;CYRILLIC CAPITAL LETTER YU;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER IU;;;044E;
+042F;CYRILLIC CAPITAL LETTER YA;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER IA;;;044F;
+0430;CYRILLIC SMALL LETTER A;Ll;0;L;;;;;N;;;0410;;0410
+0431;CYRILLIC SMALL LETTER BE;Ll;0;L;;;;;N;;;0411;;0411
+0432;CYRILLIC SMALL LETTER VE;Ll;0;L;;;;;N;;;0412;;0412
+0433;CYRILLIC SMALL LETTER GHE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER GE;;0413;;0413
+0434;CYRILLIC SMALL LETTER DE;Ll;0;L;;;;;N;;;0414;;0414
+0435;CYRILLIC SMALL LETTER IE;Ll;0;L;;;;;N;;;0415;;0415
+0436;CYRILLIC SMALL LETTER ZHE;Ll;0;L;;;;;N;;;0416;;0416
+0437;CYRILLIC SMALL LETTER ZE;Ll;0;L;;;;;N;;;0417;;0417
+0438;CYRILLIC SMALL LETTER I;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER II;;0418;;0418
+0439;CYRILLIC SMALL LETTER SHORT I;Ll;0;L;0438 0306;;;;N;CYRILLIC SMALL LETTER SHORT II;;0419;;0419
+043A;CYRILLIC SMALL LETTER KA;Ll;0;L;;;;;N;;;041A;;041A
+043B;CYRILLIC SMALL LETTER EL;Ll;0;L;;;;;N;;;041B;;041B
+043C;CYRILLIC SMALL LETTER EM;Ll;0;L;;;;;N;;;041C;;041C
+043D;CYRILLIC SMALL LETTER EN;Ll;0;L;;;;;N;;;041D;;041D
+043E;CYRILLIC SMALL LETTER O;Ll;0;L;;;;;N;;;041E;;041E
+043F;CYRILLIC SMALL LETTER PE;Ll;0;L;;;;;N;;;041F;;041F
+0440;CYRILLIC SMALL LETTER ER;Ll;0;L;;;;;N;;;0420;;0420
+0441;CYRILLIC SMALL LETTER ES;Ll;0;L;;;;;N;;;0421;;0421
+0442;CYRILLIC SMALL LETTER TE;Ll;0;L;;;;;N;;;0422;;0422
+0443;CYRILLIC SMALL LETTER U;Ll;0;L;;;;;N;;;0423;;0423
+0444;CYRILLIC SMALL LETTER EF;Ll;0;L;;;;;N;;;0424;;0424
+0445;CYRILLIC SMALL LETTER HA;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER KHA;;0425;;0425
+0446;CYRILLIC SMALL LETTER TSE;Ll;0;L;;;;;N;;;0426;;0426
+0447;CYRILLIC SMALL LETTER CHE;Ll;0;L;;;;;N;;;0427;;0427
+0448;CYRILLIC SMALL LETTER SHA;Ll;0;L;;;;;N;;;0428;;0428
+0449;CYRILLIC SMALL LETTER SHCHA;Ll;0;L;;;;;N;;;0429;;0429
+044A;CYRILLIC SMALL LETTER HARD SIGN;Ll;0;L;;;;;N;;;042A;;042A
+044B;CYRILLIC SMALL LETTER YERU;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER YERI;;042B;;042B
+044C;CYRILLIC SMALL LETTER SOFT SIGN;Ll;0;L;;;;;N;;;042C;;042C
+044D;CYRILLIC SMALL LETTER E;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER REVERSED E;;042D;;042D
+044E;CYRILLIC SMALL LETTER YU;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER IU;;042E;;042E
+044F;CYRILLIC SMALL LETTER YA;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER IA;;042F;;042F
+0450;CYRILLIC SMALL LETTER IE WITH GRAVE;Ll;0;L;0435 0300;;;;N;;;0400;;0400
+0451;CYRILLIC SMALL LETTER IO;Ll;0;L;0435 0308;;;;N;;;0401;;0401
+0452;CYRILLIC SMALL LETTER DJE;Ll;0;L;;;;;N;;Serbocroatian;0402;;0402
+0453;CYRILLIC SMALL LETTER GJE;Ll;0;L;0433 0301;;;;N;;;0403;;0403
+0454;CYRILLIC SMALL LETTER UKRAINIAN IE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER E;;0404;;0404
+0455;CYRILLIC SMALL LETTER DZE;Ll;0;L;;;;;N;;;0405;;0405
+0456;CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER I;;0406;;0406
+0457;CYRILLIC SMALL LETTER YI;Ll;0;L;0456 0308;;;;N;;Ukrainian;0407;;0407
+0458;CYRILLIC SMALL LETTER JE;Ll;0;L;;;;;N;;;0408;;0408
+0459;CYRILLIC SMALL LETTER LJE;Ll;0;L;;;;;N;;;0409;;0409
+045A;CYRILLIC SMALL LETTER NJE;Ll;0;L;;;;;N;;;040A;;040A
+045B;CYRILLIC SMALL LETTER TSHE;Ll;0;L;;;;;N;;Serbocroatian;040B;;040B
+045C;CYRILLIC SMALL LETTER KJE;Ll;0;L;043A 0301;;;;N;;;040C;;040C
+045D;CYRILLIC SMALL LETTER I WITH GRAVE;Ll;0;L;0438 0300;;;;N;;;040D;;040D
+045E;CYRILLIC SMALL LETTER SHORT U;Ll;0;L;0443 0306;;;;N;;Byelorussian;040E;;040E
+045F;CYRILLIC SMALL LETTER DZHE;Ll;0;L;;;;;N;;;040F;;040F
+0460;CYRILLIC CAPITAL LETTER OMEGA;Lu;0;L;;;;;N;;;;0461;
+0461;CYRILLIC SMALL LETTER OMEGA;Ll;0;L;;;;;N;;;0460;;0460
+0462;CYRILLIC CAPITAL LETTER YAT;Lu;0;L;;;;;N;;;;0463;
+0463;CYRILLIC SMALL LETTER YAT;Ll;0;L;;;;;N;;;0462;;0462
+0464;CYRILLIC CAPITAL LETTER IOTIFIED E;Lu;0;L;;;;;N;;;;0465;
+0465;CYRILLIC SMALL LETTER IOTIFIED E;Ll;0;L;;;;;N;;;0464;;0464
+0466;CYRILLIC CAPITAL LETTER LITTLE YUS;Lu;0;L;;;;;N;;;;0467;
+0467;CYRILLIC SMALL LETTER LITTLE YUS;Ll;0;L;;;;;N;;;0466;;0466
+0468;CYRILLIC CAPITAL LETTER IOTIFIED LITTLE YUS;Lu;0;L;;;;;N;;;;0469;
+0469;CYRILLIC SMALL LETTER IOTIFIED LITTLE YUS;Ll;0;L;;;;;N;;;0468;;0468
+046A;CYRILLIC CAPITAL LETTER BIG YUS;Lu;0;L;;;;;N;;;;046B;
+046B;CYRILLIC SMALL LETTER BIG YUS;Ll;0;L;;;;;N;;;046A;;046A
+046C;CYRILLIC CAPITAL LETTER IOTIFIED BIG YUS;Lu;0;L;;;;;N;;;;046D;
+046D;CYRILLIC SMALL LETTER IOTIFIED BIG YUS;Ll;0;L;;;;;N;;;046C;;046C
+046E;CYRILLIC CAPITAL LETTER KSI;Lu;0;L;;;;;N;;;;046F;
+046F;CYRILLIC SMALL LETTER KSI;Ll;0;L;;;;;N;;;046E;;046E
+0470;CYRILLIC CAPITAL LETTER PSI;Lu;0;L;;;;;N;;;;0471;
+0471;CYRILLIC SMALL LETTER PSI;Ll;0;L;;;;;N;;;0470;;0470
+0472;CYRILLIC CAPITAL LETTER FITA;Lu;0;L;;;;;N;;;;0473;
+0473;CYRILLIC SMALL LETTER FITA;Ll;0;L;;;;;N;;;0472;;0472
+0474;CYRILLIC CAPITAL LETTER IZHITSA;Lu;0;L;;;;;N;;;;0475;
+0475;CYRILLIC SMALL LETTER IZHITSA;Ll;0;L;;;;;N;;;0474;;0474
+0476;CYRILLIC CAPITAL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT;Lu;0;L;0474 030F;;;;N;CYRILLIC CAPITAL LETTER IZHITSA DOUBLE GRAVE;;;0477;
+0477;CYRILLIC SMALL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT;Ll;0;L;0475 030F;;;;N;CYRILLIC SMALL LETTER IZHITSA DOUBLE GRAVE;;0476;;0476
+0478;CYRILLIC CAPITAL LETTER UK;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER UK DIGRAPH;;;0479;
+0479;CYRILLIC SMALL LETTER UK;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER UK DIGRAPH;;0478;;0478
+047A;CYRILLIC CAPITAL LETTER ROUND OMEGA;Lu;0;L;;;;;N;;;;047B;
+047B;CYRILLIC SMALL LETTER ROUND OMEGA;Ll;0;L;;;;;N;;;047A;;047A
+047C;CYRILLIC CAPITAL LETTER OMEGA WITH TITLO;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER OMEGA TITLO;;;047D;
+047D;CYRILLIC SMALL LETTER OMEGA WITH TITLO;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER OMEGA TITLO;;047C;;047C
+047E;CYRILLIC CAPITAL LETTER OT;Lu;0;L;;;;;N;;;;047F;
+047F;CYRILLIC SMALL LETTER OT;Ll;0;L;;;;;N;;;047E;;047E
+0480;CYRILLIC CAPITAL LETTER KOPPA;Lu;0;L;;;;;N;;;;0481;
+0481;CYRILLIC SMALL LETTER KOPPA;Ll;0;L;;;;;N;;;0480;;0480
+0482;CYRILLIC THOUSANDS SIGN;So;0;L;;;;;N;;;;;
+0483;COMBINING CYRILLIC TITLO;Mn;230;NSM;;;;;N;CYRILLIC NON-SPACING TITLO;;;;
+0484;COMBINING CYRILLIC PALATALIZATION;Mn;230;NSM;;;;;N;CYRILLIC NON-SPACING PALATALIZATION;;;;
+0485;COMBINING CYRILLIC DASIA PNEUMATA;Mn;230;NSM;;;;;N;CYRILLIC NON-SPACING DASIA PNEUMATA;;;;
+0486;COMBINING CYRILLIC PSILI PNEUMATA;Mn;230;NSM;;;;;N;CYRILLIC NON-SPACING PSILI PNEUMATA;;;;
+0488;COMBINING CYRILLIC HUNDRED THOUSANDS SIGN;Me;0;NSM;;;;;N;;;;;
+0489;COMBINING CYRILLIC MILLIONS SIGN;Me;0;NSM;;;;;N;;;;;
+048A;CYRILLIC CAPITAL LETTER SHORT I WITH TAIL;Lu;0;L;;;;;N;;;;048B;
+048B;CYRILLIC SMALL LETTER SHORT I WITH TAIL;Ll;0;L;;;;;N;;;048A;;048A
+048C;CYRILLIC CAPITAL LETTER SEMISOFT SIGN;Lu;0;L;;;;;N;;;;048D;
+048D;CYRILLIC SMALL LETTER SEMISOFT SIGN;Ll;0;L;;;;;N;;;048C;;048C
+048E;CYRILLIC CAPITAL LETTER ER WITH TICK;Lu;0;L;;;;;N;;;;048F;
+048F;CYRILLIC SMALL LETTER ER WITH TICK;Ll;0;L;;;;;N;;;048E;;048E
+0490;CYRILLIC CAPITAL LETTER GHE WITH UPTURN;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER GE WITH UPTURN;;;0491;
+0491;CYRILLIC SMALL LETTER GHE WITH UPTURN;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER GE WITH UPTURN;;0490;;0490
+0492;CYRILLIC CAPITAL LETTER GHE WITH STROKE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER GE BAR;;;0493;
+0493;CYRILLIC SMALL LETTER GHE WITH STROKE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER GE BAR;;0492;;0492
+0494;CYRILLIC CAPITAL LETTER GHE WITH MIDDLE HOOK;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER GE HOOK;;;0495;
+0495;CYRILLIC SMALL LETTER GHE WITH MIDDLE HOOK;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER GE HOOK;;0494;;0494
+0496;CYRILLIC CAPITAL LETTER ZHE WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER ZHE WITH RIGHT DESCENDER;;;0497;
+0497;CYRILLIC SMALL LETTER ZHE WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER ZHE WITH RIGHT DESCENDER;;0496;;0496
+0498;CYRILLIC CAPITAL LETTER ZE WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER ZE CEDILLA;;;0499;
+0499;CYRILLIC SMALL LETTER ZE WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER ZE CEDILLA;;0498;;0498
+049A;CYRILLIC CAPITAL LETTER KA WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER KA WITH RIGHT DESCENDER;;;049B;
+049B;CYRILLIC SMALL LETTER KA WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER KA WITH RIGHT DESCENDER;;049A;;049A
+049C;CYRILLIC CAPITAL LETTER KA WITH VERTICAL STROKE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER KA VERTICAL BAR;;;049D;
+049D;CYRILLIC SMALL LETTER KA WITH VERTICAL STROKE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER KA VERTICAL BAR;;049C;;049C
+049E;CYRILLIC CAPITAL LETTER KA WITH STROKE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER KA BAR;;;049F;
+049F;CYRILLIC SMALL LETTER KA WITH STROKE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER KA BAR;;049E;;049E
+04A0;CYRILLIC CAPITAL LETTER BASHKIR KA;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER REVERSED GE KA;;;04A1;
+04A1;CYRILLIC SMALL LETTER BASHKIR KA;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER REVERSED GE KA;;04A0;;04A0
+04A2;CYRILLIC CAPITAL LETTER EN WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER EN WITH RIGHT DESCENDER;;;04A3;
+04A3;CYRILLIC SMALL LETTER EN WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER EN WITH RIGHT DESCENDER;;04A2;;04A2
+04A4;CYRILLIC CAPITAL LIGATURE EN GHE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER EN GE;;;04A5;
+04A5;CYRILLIC SMALL LIGATURE EN GHE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER EN GE;;04A4;;04A4
+04A6;CYRILLIC CAPITAL LETTER PE WITH MIDDLE HOOK;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER PE HOOK;Abkhasian;;04A7;
+04A7;CYRILLIC SMALL LETTER PE WITH MIDDLE HOOK;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER PE HOOK;Abkhasian;04A6;;04A6
+04A8;CYRILLIC CAPITAL LETTER ABKHASIAN HA;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER O HOOK;;;04A9;
+04A9;CYRILLIC SMALL LETTER ABKHASIAN HA;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER O HOOK;;04A8;;04A8
+04AA;CYRILLIC CAPITAL LETTER ES WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER ES CEDILLA;;;04AB;
+04AB;CYRILLIC SMALL LETTER ES WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER ES CEDILLA;;04AA;;04AA
+04AC;CYRILLIC CAPITAL LETTER TE WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER TE WITH RIGHT DESCENDER;;;04AD;
+04AD;CYRILLIC SMALL LETTER TE WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER TE WITH RIGHT DESCENDER;;04AC;;04AC
+04AE;CYRILLIC CAPITAL LETTER STRAIGHT U;Lu;0;L;;;;;N;;;;04AF;
+04AF;CYRILLIC SMALL LETTER STRAIGHT U;Ll;0;L;;;;;N;;;04AE;;04AE
+04B0;CYRILLIC CAPITAL LETTER STRAIGHT U WITH STROKE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER STRAIGHT U BAR;;;04B1;
+04B1;CYRILLIC SMALL LETTER STRAIGHT U WITH STROKE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER STRAIGHT U BAR;;04B0;;04B0
+04B2;CYRILLIC CAPITAL LETTER HA WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER KHA WITH RIGHT DESCENDER;;;04B3;
+04B3;CYRILLIC SMALL LETTER HA WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER KHA WITH RIGHT DESCENDER;;04B2;;04B2
+04B4;CYRILLIC CAPITAL LIGATURE TE TSE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER TE TSE;Abkhasian;;04B5;
+04B5;CYRILLIC SMALL LIGATURE TE TSE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER TE TSE;Abkhasian;04B4;;04B4
+04B6;CYRILLIC CAPITAL LETTER CHE WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER CHE WITH RIGHT DESCENDER;;;04B7;
+04B7;CYRILLIC SMALL LETTER CHE WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER CHE WITH RIGHT DESCENDER;;04B6;;04B6
+04B8;CYRILLIC CAPITAL LETTER CHE WITH VERTICAL STROKE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER CHE VERTICAL BAR;;;04B9;
+04B9;CYRILLIC SMALL LETTER CHE WITH VERTICAL STROKE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER CHE VERTICAL BAR;;04B8;;04B8
+04BA;CYRILLIC CAPITAL LETTER SHHA;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER H;;;04BB;
+04BB;CYRILLIC SMALL LETTER SHHA;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER H;;04BA;;04BA
+04BC;CYRILLIC CAPITAL LETTER ABKHASIAN CHE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER IE HOOK;;;04BD;
+04BD;CYRILLIC SMALL LETTER ABKHASIAN CHE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER IE HOOK;;04BC;;04BC
+04BE;CYRILLIC CAPITAL LETTER ABKHASIAN CHE WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER IE HOOK OGONEK;;;04BF;
+04BF;CYRILLIC SMALL LETTER ABKHASIAN CHE WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER IE HOOK OGONEK;;04BE;;04BE
+04C0;CYRILLIC LETTER PALOCHKA;Lu;0;L;;;;;N;CYRILLIC LETTER I;;;;
+04C1;CYRILLIC CAPITAL LETTER ZHE WITH BREVE;Lu;0;L;0416 0306;;;;N;CYRILLIC CAPITAL LETTER SHORT ZHE;;;04C2;
+04C2;CYRILLIC SMALL LETTER ZHE WITH BREVE;Ll;0;L;0436 0306;;;;N;CYRILLIC SMALL LETTER SHORT ZHE;;04C1;;04C1
+04C3;CYRILLIC CAPITAL LETTER KA WITH HOOK;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER KA HOOK;;;04C4;
+04C4;CYRILLIC SMALL LETTER KA WITH HOOK;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER KA HOOK;;04C3;;04C3
+04C5;CYRILLIC CAPITAL LETTER EL WITH TAIL;Lu;0;L;;;;;N;;;;04C6;
+04C6;CYRILLIC SMALL LETTER EL WITH TAIL;Ll;0;L;;;;;N;;;04C5;;04C5
+04C7;CYRILLIC CAPITAL LETTER EN WITH HOOK;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER EN HOOK;;;04C8;
+04C8;CYRILLIC SMALL LETTER EN WITH HOOK;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER EN HOOK;;04C7;;04C7
+04C9;CYRILLIC CAPITAL LETTER EN WITH TAIL;Lu;0;L;;;;;N;;;;04CA;
+04CA;CYRILLIC SMALL LETTER EN WITH TAIL;Ll;0;L;;;;;N;;;04C9;;04C9
+04CB;CYRILLIC CAPITAL LETTER KHAKASSIAN CHE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER CHE WITH LEFT DESCENDER;;;04CC;
+04CC;CYRILLIC SMALL LETTER KHAKASSIAN CHE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER CHE WITH LEFT DESCENDER;;04CB;;04CB
+04CD;CYRILLIC CAPITAL LETTER EM WITH TAIL;Lu;0;L;;;;;N;;;;04CE;
+04CE;CYRILLIC SMALL LETTER EM WITH TAIL;Ll;0;L;;;;;N;;;04CD;;04CD
+04D0;CYRILLIC CAPITAL LETTER A WITH BREVE;Lu;0;L;0410 0306;;;;N;;;;04D1;
+04D1;CYRILLIC SMALL LETTER A WITH BREVE;Ll;0;L;0430 0306;;;;N;;;04D0;;04D0
+04D2;CYRILLIC CAPITAL LETTER A WITH DIAERESIS;Lu;0;L;0410 0308;;;;N;;;;04D3;
+04D3;CYRILLIC SMALL LETTER A WITH DIAERESIS;Ll;0;L;0430 0308;;;;N;;;04D2;;04D2
+04D4;CYRILLIC CAPITAL LIGATURE A IE;Lu;0;L;;;;;N;;;;04D5;
+04D5;CYRILLIC SMALL LIGATURE A IE;Ll;0;L;;;;;N;;;04D4;;04D4
+04D6;CYRILLIC CAPITAL LETTER IE WITH BREVE;Lu;0;L;0415 0306;;;;N;;;;04D7;
+04D7;CYRILLIC SMALL LETTER IE WITH BREVE;Ll;0;L;0435 0306;;;;N;;;04D6;;04D6
+04D8;CYRILLIC CAPITAL LETTER SCHWA;Lu;0;L;;;;;N;;;;04D9;
+04D9;CYRILLIC SMALL LETTER SCHWA;Ll;0;L;;;;;N;;;04D8;;04D8
+04DA;CYRILLIC CAPITAL LETTER SCHWA WITH DIAERESIS;Lu;0;L;04D8 0308;;;;N;;;;04DB;
+04DB;CYRILLIC SMALL LETTER SCHWA WITH DIAERESIS;Ll;0;L;04D9 0308;;;;N;;;04DA;;04DA
+04DC;CYRILLIC CAPITAL LETTER ZHE WITH DIAERESIS;Lu;0;L;0416 0308;;;;N;;;;04DD;
+04DD;CYRILLIC SMALL LETTER ZHE WITH DIAERESIS;Ll;0;L;0436 0308;;;;N;;;04DC;;04DC
+04DE;CYRILLIC CAPITAL LETTER ZE WITH DIAERESIS;Lu;0;L;0417 0308;;;;N;;;;04DF;
+04DF;CYRILLIC SMALL LETTER ZE WITH DIAERESIS;Ll;0;L;0437 0308;;;;N;;;04DE;;04DE
+04E0;CYRILLIC CAPITAL LETTER ABKHASIAN DZE;Lu;0;L;;;;;N;;;;04E1;
+04E1;CYRILLIC SMALL LETTER ABKHASIAN DZE;Ll;0;L;;;;;N;;;04E0;;04E0
+04E2;CYRILLIC CAPITAL LETTER I WITH MACRON;Lu;0;L;0418 0304;;;;N;;;;04E3;
+04E3;CYRILLIC SMALL LETTER I WITH MACRON;Ll;0;L;0438 0304;;;;N;;;04E2;;04E2
+04E4;CYRILLIC CAPITAL LETTER I WITH DIAERESIS;Lu;0;L;0418 0308;;;;N;;;;04E5;
+04E5;CYRILLIC SMALL LETTER I WITH DIAERESIS;Ll;0;L;0438 0308;;;;N;;;04E4;;04E4
+04E6;CYRILLIC CAPITAL LETTER O WITH DIAERESIS;Lu;0;L;041E 0308;;;;N;;;;04E7;
+04E7;CYRILLIC SMALL LETTER O WITH DIAERESIS;Ll;0;L;043E 0308;;;;N;;;04E6;;04E6
+04E8;CYRILLIC CAPITAL LETTER BARRED O;Lu;0;L;;;;;N;;;;04E9;
+04E9;CYRILLIC SMALL LETTER BARRED O;Ll;0;L;;;;;N;;;04E8;;04E8
+04EA;CYRILLIC CAPITAL LETTER BARRED O WITH DIAERESIS;Lu;0;L;04E8 0308;;;;N;;;;04EB;
+04EB;CYRILLIC SMALL LETTER BARRED O WITH DIAERESIS;Ll;0;L;04E9 0308;;;;N;;;04EA;;04EA
+04EC;CYRILLIC CAPITAL LETTER E WITH DIAERESIS;Lu;0;L;042D 0308;;;;N;;;;04ED;
+04ED;CYRILLIC SMALL LETTER E WITH DIAERESIS;Ll;0;L;044D 0308;;;;N;;;04EC;;04EC
+04EE;CYRILLIC CAPITAL LETTER U WITH MACRON;Lu;0;L;0423 0304;;;;N;;;;04EF;
+04EF;CYRILLIC SMALL LETTER U WITH MACRON;Ll;0;L;0443 0304;;;;N;;;04EE;;04EE
+04F0;CYRILLIC CAPITAL LETTER U WITH DIAERESIS;Lu;0;L;0423 0308;;;;N;;;;04F1;
+04F1;CYRILLIC SMALL LETTER U WITH DIAERESIS;Ll;0;L;0443 0308;;;;N;;;04F0;;04F0
+04F2;CYRILLIC CAPITAL LETTER U WITH DOUBLE ACUTE;Lu;0;L;0423 030B;;;;N;;;;04F3;
+04F3;CYRILLIC SMALL LETTER U WITH DOUBLE ACUTE;Ll;0;L;0443 030B;;;;N;;;04F2;;04F2
+04F4;CYRILLIC CAPITAL LETTER CHE WITH DIAERESIS;Lu;0;L;0427 0308;;;;N;;;;04F5;
+04F5;CYRILLIC SMALL LETTER CHE WITH DIAERESIS;Ll;0;L;0447 0308;;;;N;;;04F4;;04F4
+04F8;CYRILLIC CAPITAL LETTER YERU WITH DIAERESIS;Lu;0;L;042B 0308;;;;N;;;;04F9;
+04F9;CYRILLIC SMALL LETTER YERU WITH DIAERESIS;Ll;0;L;044B 0308;;;;N;;;04F8;;04F8
+0500;CYRILLIC CAPITAL LETTER KOMI DE;Lu;0;L;;;;;N;;;;0501;
+0501;CYRILLIC SMALL LETTER KOMI DE;Ll;0;L;;;;;N;;;0500;;0500
+0502;CYRILLIC CAPITAL LETTER KOMI DJE;Lu;0;L;;;;;N;;;;0503;
+0503;CYRILLIC SMALL LETTER KOMI DJE;Ll;0;L;;;;;N;;;0502;;0502
+0504;CYRILLIC CAPITAL LETTER KOMI ZJE;Lu;0;L;;;;;N;;;;0505;
+0505;CYRILLIC SMALL LETTER KOMI ZJE;Ll;0;L;;;;;N;;;0504;;0504
+0506;CYRILLIC CAPITAL LETTER KOMI DZJE;Lu;0;L;;;;;N;;;;0507;
+0507;CYRILLIC SMALL LETTER KOMI DZJE;Ll;0;L;;;;;N;;;0506;;0506
+0508;CYRILLIC CAPITAL LETTER KOMI LJE;Lu;0;L;;;;;N;;;;0509;
+0509;CYRILLIC SMALL LETTER KOMI LJE;Ll;0;L;;;;;N;;;0508;;0508
+050A;CYRILLIC CAPITAL LETTER KOMI NJE;Lu;0;L;;;;;N;;;;050B;
+050B;CYRILLIC SMALL LETTER KOMI NJE;Ll;0;L;;;;;N;;;050A;;050A
+050C;CYRILLIC CAPITAL LETTER KOMI SJE;Lu;0;L;;;;;N;;;;050D;
+050D;CYRILLIC SMALL LETTER KOMI SJE;Ll;0;L;;;;;N;;;050C;;050C
+050E;CYRILLIC CAPITAL LETTER KOMI TJE;Lu;0;L;;;;;N;;;;050F;
+050F;CYRILLIC SMALL LETTER KOMI TJE;Ll;0;L;;;;;N;;;050E;;050E
+0531;ARMENIAN CAPITAL LETTER AYB;Lu;0;L;;;;;N;;;;0561;
+0532;ARMENIAN CAPITAL LETTER BEN;Lu;0;L;;;;;N;;;;0562;
+0533;ARMENIAN CAPITAL LETTER GIM;Lu;0;L;;;;;N;;;;0563;
+0534;ARMENIAN CAPITAL LETTER DA;Lu;0;L;;;;;N;;;;0564;
+0535;ARMENIAN CAPITAL LETTER ECH;Lu;0;L;;;;;N;;;;0565;
+0536;ARMENIAN CAPITAL LETTER ZA;Lu;0;L;;;;;N;;;;0566;
+0537;ARMENIAN CAPITAL LETTER EH;Lu;0;L;;;;;N;;;;0567;
+0538;ARMENIAN CAPITAL LETTER ET;Lu;0;L;;;;;N;;;;0568;
+0539;ARMENIAN CAPITAL LETTER TO;Lu;0;L;;;;;N;;;;0569;
+053A;ARMENIAN CAPITAL LETTER ZHE;Lu;0;L;;;;;N;;;;056A;
+053B;ARMENIAN CAPITAL LETTER INI;Lu;0;L;;;;;N;;;;056B;
+053C;ARMENIAN CAPITAL LETTER LIWN;Lu;0;L;;;;;N;;;;056C;
+053D;ARMENIAN CAPITAL LETTER XEH;Lu;0;L;;;;;N;;;;056D;
+053E;ARMENIAN CAPITAL LETTER CA;Lu;0;L;;;;;N;;;;056E;
+053F;ARMENIAN CAPITAL LETTER KEN;Lu;0;L;;;;;N;;;;056F;
+0540;ARMENIAN CAPITAL LETTER HO;Lu;0;L;;;;;N;;;;0570;
+0541;ARMENIAN CAPITAL LETTER JA;Lu;0;L;;;;;N;;;;0571;
+0542;ARMENIAN CAPITAL LETTER GHAD;Lu;0;L;;;;;N;ARMENIAN CAPITAL LETTER LAD;;;0572;
+0543;ARMENIAN CAPITAL LETTER CHEH;Lu;0;L;;;;;N;;;;0573;
+0544;ARMENIAN CAPITAL LETTER MEN;Lu;0;L;;;;;N;;;;0574;
+0545;ARMENIAN CAPITAL LETTER YI;Lu;0;L;;;;;N;;;;0575;
+0546;ARMENIAN CAPITAL LETTER NOW;Lu;0;L;;;;;N;;;;0576;
+0547;ARMENIAN CAPITAL LETTER SHA;Lu;0;L;;;;;N;;;;0577;
+0548;ARMENIAN CAPITAL LETTER VO;Lu;0;L;;;;;N;;;;0578;
+0549;ARMENIAN CAPITAL LETTER CHA;Lu;0;L;;;;;N;;;;0579;
+054A;ARMENIAN CAPITAL LETTER PEH;Lu;0;L;;;;;N;;;;057A;
+054B;ARMENIAN CAPITAL LETTER JHEH;Lu;0;L;;;;;N;;;;057B;
+054C;ARMENIAN CAPITAL LETTER RA;Lu;0;L;;;;;N;;;;057C;
+054D;ARMENIAN CAPITAL LETTER SEH;Lu;0;L;;;;;N;;;;057D;
+054E;ARMENIAN CAPITAL LETTER VEW;Lu;0;L;;;;;N;;;;057E;
+054F;ARMENIAN CAPITAL LETTER TIWN;Lu;0;L;;;;;N;;;;057F;
+0550;ARMENIAN CAPITAL LETTER REH;Lu;0;L;;;;;N;;;;0580;
+0551;ARMENIAN CAPITAL LETTER CO;Lu;0;L;;;;;N;;;;0581;
+0552;ARMENIAN CAPITAL LETTER YIWN;Lu;0;L;;;;;N;;;;0582;
+0553;ARMENIAN CAPITAL LETTER PIWR;Lu;0;L;;;;;N;;;;0583;
+0554;ARMENIAN CAPITAL LETTER KEH;Lu;0;L;;;;;N;;;;0584;
+0555;ARMENIAN CAPITAL LETTER OH;Lu;0;L;;;;;N;;;;0585;
+0556;ARMENIAN CAPITAL LETTER FEH;Lu;0;L;;;;;N;;;;0586;
+0559;ARMENIAN MODIFIER LETTER LEFT HALF RING;Lm;0;L;;;;;N;;;;;
+055A;ARMENIAN APOSTROPHE;Po;0;L;;;;;N;ARMENIAN MODIFIER LETTER RIGHT HALF RING;;;;
+055B;ARMENIAN EMPHASIS MARK;Po;0;L;;;;;N;;;;;
+055C;ARMENIAN EXCLAMATION MARK;Po;0;L;;;;;N;;;;;
+055D;ARMENIAN COMMA;Po;0;L;;;;;N;;;;;
+055E;ARMENIAN QUESTION MARK;Po;0;L;;;;;N;;;;;
+055F;ARMENIAN ABBREVIATION MARK;Po;0;L;;;;;N;;;;;
+0561;ARMENIAN SMALL LETTER AYB;Ll;0;L;;;;;N;;;0531;;0531
+0562;ARMENIAN SMALL LETTER BEN;Ll;0;L;;;;;N;;;0532;;0532
+0563;ARMENIAN SMALL LETTER GIM;Ll;0;L;;;;;N;;;0533;;0533
+0564;ARMENIAN SMALL LETTER DA;Ll;0;L;;;;;N;;;0534;;0534
+0565;ARMENIAN SMALL LETTER ECH;Ll;0;L;;;;;N;;;0535;;0535
+0566;ARMENIAN SMALL LETTER ZA;Ll;0;L;;;;;N;;;0536;;0536
+0567;ARMENIAN SMALL LETTER EH;Ll;0;L;;;;;N;;;0537;;0537
+0568;ARMENIAN SMALL LETTER ET;Ll;0;L;;;;;N;;;0538;;0538
+0569;ARMENIAN SMALL LETTER TO;Ll;0;L;;;;;N;;;0539;;0539
+056A;ARMENIAN SMALL LETTER ZHE;Ll;0;L;;;;;N;;;053A;;053A
+056B;ARMENIAN SMALL LETTER INI;Ll;0;L;;;;;N;;;053B;;053B
+056C;ARMENIAN SMALL LETTER LIWN;Ll;0;L;;;;;N;;;053C;;053C
+056D;ARMENIAN SMALL LETTER XEH;Ll;0;L;;;;;N;;;053D;;053D
+056E;ARMENIAN SMALL LETTER CA;Ll;0;L;;;;;N;;;053E;;053E
+056F;ARMENIAN SMALL LETTER KEN;Ll;0;L;;;;;N;;;053F;;053F
+0570;ARMENIAN SMALL LETTER HO;Ll;0;L;;;;;N;;;0540;;0540
+0571;ARMENIAN SMALL LETTER JA;Ll;0;L;;;;;N;;;0541;;0541
+0572;ARMENIAN SMALL LETTER GHAD;Ll;0;L;;;;;N;ARMENIAN SMALL LETTER LAD;;0542;;0542
+0573;ARMENIAN SMALL LETTER CHEH;Ll;0;L;;;;;N;;;0543;;0543
+0574;ARMENIAN SMALL LETTER MEN;Ll;0;L;;;;;N;;;0544;;0544
+0575;ARMENIAN SMALL LETTER YI;Ll;0;L;;;;;N;;;0545;;0545
+0576;ARMENIAN SMALL LETTER NOW;Ll;0;L;;;;;N;;;0546;;0546
+0577;ARMENIAN SMALL LETTER SHA;Ll;0;L;;;;;N;;;0547;;0547
+0578;ARMENIAN SMALL LETTER VO;Ll;0;L;;;;;N;;;0548;;0548
+0579;ARMENIAN SMALL LETTER CHA;Ll;0;L;;;;;N;;;0549;;0549
+057A;ARMENIAN SMALL LETTER PEH;Ll;0;L;;;;;N;;;054A;;054A
+057B;ARMENIAN SMALL LETTER JHEH;Ll;0;L;;;;;N;;;054B;;054B
+057C;ARMENIAN SMALL LETTER RA;Ll;0;L;;;;;N;;;054C;;054C
+057D;ARMENIAN SMALL LETTER SEH;Ll;0;L;;;;;N;;;054D;;054D
+057E;ARMENIAN SMALL LETTER VEW;Ll;0;L;;;;;N;;;054E;;054E
+057F;ARMENIAN SMALL LETTER TIWN;Ll;0;L;;;;;N;;;054F;;054F
+0580;ARMENIAN SMALL LETTER REH;Ll;0;L;;;;;N;;;0550;;0550
+0581;ARMENIAN SMALL LETTER CO;Ll;0;L;;;;;N;;;0551;;0551
+0582;ARMENIAN SMALL LETTER YIWN;Ll;0;L;;;;;N;;;0552;;0552
+0583;ARMENIAN SMALL LETTER PIWR;Ll;0;L;;;;;N;;;0553;;0553
+0584;ARMENIAN SMALL LETTER KEH;Ll;0;L;;;;;N;;;0554;;0554
+0585;ARMENIAN SMALL LETTER OH;Ll;0;L;;;;;N;;;0555;;0555
+0586;ARMENIAN SMALL LETTER FEH;Ll;0;L;;;;;N;;;0556;;0556
+0587;ARMENIAN SMALL LIGATURE ECH YIWN;Ll;0;L;<compat> 0565 0582;;;;N;;;;;
+0589;ARMENIAN FULL STOP;Po;0;L;;;;;N;ARMENIAN PERIOD;;;;
+058A;ARMENIAN HYPHEN;Pd;0;ON;;;;;N;;;;;
+0591;HEBREW ACCENT ETNAHTA;Mn;220;NSM;;;;;N;;;;;
+0592;HEBREW ACCENT SEGOL;Mn;230;NSM;;;;;N;;;;;
+0593;HEBREW ACCENT SHALSHELET;Mn;230;NSM;;;;;N;;;;;
+0594;HEBREW ACCENT ZAQEF QATAN;Mn;230;NSM;;;;;N;;;;;
+0595;HEBREW ACCENT ZAQEF GADOL;Mn;230;NSM;;;;;N;;;;;
+0596;HEBREW ACCENT TIPEHA;Mn;220;NSM;;;;;N;;*;;;
+0597;HEBREW ACCENT REVIA;Mn;230;NSM;;;;;N;;;;;
+0598;HEBREW ACCENT ZARQA;Mn;230;NSM;;;;;N;;*;;;
+0599;HEBREW ACCENT PASHTA;Mn;230;NSM;;;;;N;;;;;
+059A;HEBREW ACCENT YETIV;Mn;222;NSM;;;;;N;;;;;
+059B;HEBREW ACCENT TEVIR;Mn;220;NSM;;;;;N;;;;;
+059C;HEBREW ACCENT GERESH;Mn;230;NSM;;;;;N;;;;;
+059D;HEBREW ACCENT GERESH MUQDAM;Mn;230;NSM;;;;;N;;;;;
+059E;HEBREW ACCENT GERSHAYIM;Mn;230;NSM;;;;;N;;;;;
+059F;HEBREW ACCENT QARNEY PARA;Mn;230;NSM;;;;;N;;;;;
+05A0;HEBREW ACCENT TELISHA GEDOLA;Mn;230;NSM;;;;;N;;;;;
+05A1;HEBREW ACCENT PAZER;Mn;230;NSM;;;;;N;;;;;
+05A3;HEBREW ACCENT MUNAH;Mn;220;NSM;;;;;N;;;;;
+05A4;HEBREW ACCENT MAHAPAKH;Mn;220;NSM;;;;;N;;;;;
+05A5;HEBREW ACCENT MERKHA;Mn;220;NSM;;;;;N;;*;;;
+05A6;HEBREW ACCENT MERKHA KEFULA;Mn;220;NSM;;;;;N;;;;;
+05A7;HEBREW ACCENT DARGA;Mn;220;NSM;;;;;N;;;;;
+05A8;HEBREW ACCENT QADMA;Mn;230;NSM;;;;;N;;*;;;
+05A9;HEBREW ACCENT TELISHA QETANA;Mn;230;NSM;;;;;N;;;;;
+05AA;HEBREW ACCENT YERAH BEN YOMO;Mn;220;NSM;;;;;N;;*;;;
+05AB;HEBREW ACCENT OLE;Mn;230;NSM;;;;;N;;;;;
+05AC;HEBREW ACCENT ILUY;Mn;230;NSM;;;;;N;;;;;
+05AD;HEBREW ACCENT DEHI;Mn;222;NSM;;;;;N;;;;;
+05AE;HEBREW ACCENT ZINOR;Mn;228;NSM;;;;;N;;;;;
+05AF;HEBREW MARK MASORA CIRCLE;Mn;230;NSM;;;;;N;;;;;
+05B0;HEBREW POINT SHEVA;Mn;10;NSM;;;;;N;;;;;
+05B1;HEBREW POINT HATAF SEGOL;Mn;11;NSM;;;;;N;;;;;
+05B2;HEBREW POINT HATAF PATAH;Mn;12;NSM;;;;;N;;;;;
+05B3;HEBREW POINT HATAF QAMATS;Mn;13;NSM;;;;;N;;;;;
+05B4;HEBREW POINT HIRIQ;Mn;14;NSM;;;;;N;;;;;
+05B5;HEBREW POINT TSERE;Mn;15;NSM;;;;;N;;;;;
+05B6;HEBREW POINT SEGOL;Mn;16;NSM;;;;;N;;;;;
+05B7;HEBREW POINT PATAH;Mn;17;NSM;;;;;N;;;;;
+05B8;HEBREW POINT QAMATS;Mn;18;NSM;;;;;N;;;;;
+05B9;HEBREW POINT HOLAM;Mn;19;NSM;;;;;N;;;;;
+05BB;HEBREW POINT QUBUTS;Mn;20;NSM;;;;;N;;;;;
+05BC;HEBREW POINT DAGESH OR MAPIQ;Mn;21;NSM;;;;;N;HEBREW POINT DAGESH;or shuruq;;;
+05BD;HEBREW POINT METEG;Mn;22;NSM;;;;;N;;*;;;
+05BE;HEBREW PUNCTUATION MAQAF;Po;0;R;;;;;N;;;;;
+05BF;HEBREW POINT RAFE;Mn;23;NSM;;;;;N;;;;;
+05C0;HEBREW PUNCTUATION PASEQ;Po;0;R;;;;;N;HEBREW POINT PASEQ;*;;;
+05C1;HEBREW POINT SHIN DOT;Mn;24;NSM;;;;;N;;;;;
+05C2;HEBREW POINT SIN DOT;Mn;25;NSM;;;;;N;;;;;
+05C3;HEBREW PUNCTUATION SOF PASUQ;Po;0;R;;;;;N;;*;;;
+05C4;HEBREW MARK UPPER DOT;Mn;230;NSM;;;;;N;;;;;
+05D0;HEBREW LETTER ALEF;Lo;0;R;;;;;N;;;;;
+05D1;HEBREW LETTER BET;Lo;0;R;;;;;N;;;;;
+05D2;HEBREW LETTER GIMEL;Lo;0;R;;;;;N;;;;;
+05D3;HEBREW LETTER DALET;Lo;0;R;;;;;N;;;;;
+05D4;HEBREW LETTER HE;Lo;0;R;;;;;N;;;;;
+05D5;HEBREW LETTER VAV;Lo;0;R;;;;;N;;;;;
+05D6;HEBREW LETTER ZAYIN;Lo;0;R;;;;;N;;;;;
+05D7;HEBREW LETTER HET;Lo;0;R;;;;;N;;;;;
+05D8;HEBREW LETTER TET;Lo;0;R;;;;;N;;;;;
+05D9;HEBREW LETTER YOD;Lo;0;R;;;;;N;;;;;
+05DA;HEBREW LETTER FINAL KAF;Lo;0;R;;;;;N;;;;;
+05DB;HEBREW LETTER KAF;Lo;0;R;;;;;N;;;;;
+05DC;HEBREW LETTER LAMED;Lo;0;R;;;;;N;;;;;
+05DD;HEBREW LETTER FINAL MEM;Lo;0;R;;;;;N;;;;;
+05DE;HEBREW LETTER MEM;Lo;0;R;;;;;N;;;;;
+05DF;HEBREW LETTER FINAL NUN;Lo;0;R;;;;;N;;;;;
+05E0;HEBREW LETTER NUN;Lo;0;R;;;;;N;;;;;
+05E1;HEBREW LETTER SAMEKH;Lo;0;R;;;;;N;;;;;
+05E2;HEBREW LETTER AYIN;Lo;0;R;;;;;N;;;;;
+05E3;HEBREW LETTER FINAL PE;Lo;0;R;;;;;N;;;;;
+05E4;HEBREW LETTER PE;Lo;0;R;;;;;N;;;;;
+05E5;HEBREW LETTER FINAL TSADI;Lo;0;R;;;;;N;;;;;
+05E6;HEBREW LETTER TSADI;Lo;0;R;;;;;N;;;;;
+05E7;HEBREW LETTER QOF;Lo;0;R;;;;;N;;;;;
+05E8;HEBREW LETTER RESH;Lo;0;R;;;;;N;;;;;
+05E9;HEBREW LETTER SHIN;Lo;0;R;;;;;N;;;;;
+05EA;HEBREW LETTER TAV;Lo;0;R;;;;;N;;;;;
+05F0;HEBREW LIGATURE YIDDISH DOUBLE VAV;Lo;0;R;;;;;N;HEBREW LETTER DOUBLE VAV;;;;
+05F1;HEBREW LIGATURE YIDDISH VAV YOD;Lo;0;R;;;;;N;HEBREW LETTER VAV YOD;;;;
+05F2;HEBREW LIGATURE YIDDISH DOUBLE YOD;Lo;0;R;;;;;N;HEBREW LETTER DOUBLE YOD;;;;
+05F3;HEBREW PUNCTUATION GERESH;Po;0;R;;;;;N;;;;;
+05F4;HEBREW PUNCTUATION GERSHAYIM;Po;0;R;;;;;N;;;;;
+0600;ARABIC NUMBER SIGN;Cf;0;AL;;;;;N;;;;;
+0601;ARABIC SIGN SANAH;Cf;0;AL;;;;;N;;;;;
+0602;ARABIC FOOTNOTE MARKER;Cf;0;AL;;;;;N;;;;;
+0603;ARABIC SIGN SAFHA;Cf;0;AL;;;;;N;;;;;
+060C;ARABIC COMMA;Po;0;CS;;;;;N;;;;;
+060D;ARABIC DATE SEPARATOR;Po;0;AL;;;;;N;;;;;
+060E;ARABIC POETIC VERSE SIGN;So;0;ON;;;;;N;;;;;
+060F;ARABIC SIGN MISRA;So;0;ON;;;;;N;;;;;
+0610;ARABIC SIGN SALLALLAHOU ALAYHE WASSALLAM;Mn;230;NSM;;;;;N;;;;;
+0611;ARABIC SIGN ALAYHE ASSALLAM;Mn;230;NSM;;;;;N;;;;;
+0612;ARABIC SIGN RAHMATULLAH ALAYHE;Mn;230;NSM;;;;;N;;;;;
+0613;ARABIC SIGN RADI ALLAHOU ANHU;Mn;230;NSM;;;;;N;;;;;
+0614;ARABIC SIGN TAKHALLUS;Mn;230;NSM;;;;;N;;;;;
+0615;ARABIC SMALL HIGH TAH;Mn;230;NSM;;;;;N;;;;;
+061B;ARABIC SEMICOLON;Po;0;AL;;;;;N;;;;;
+061F;ARABIC QUESTION MARK;Po;0;AL;;;;;N;;;;;
+0621;ARABIC LETTER HAMZA;Lo;0;AL;;;;;N;ARABIC LETTER HAMZAH;;;;
+0622;ARABIC LETTER ALEF WITH MADDA ABOVE;Lo;0;AL;0627 0653;;;;N;ARABIC LETTER MADDAH ON ALEF;;;;
+0623;ARABIC LETTER ALEF WITH HAMZA ABOVE;Lo;0;AL;0627 0654;;;;N;ARABIC LETTER HAMZAH ON ALEF;;;;
+0624;ARABIC LETTER WAW WITH HAMZA ABOVE;Lo;0;AL;0648 0654;;;;N;ARABIC LETTER HAMZAH ON WAW;;;;
+0625;ARABIC LETTER ALEF WITH HAMZA BELOW;Lo;0;AL;0627 0655;;;;N;ARABIC LETTER HAMZAH UNDER ALEF;;;;
+0626;ARABIC LETTER YEH WITH HAMZA ABOVE;Lo;0;AL;064A 0654;;;;N;ARABIC LETTER HAMZAH ON YA;;;;
+0627;ARABIC LETTER ALEF;Lo;0;AL;;;;;N;;;;;
+0628;ARABIC LETTER BEH;Lo;0;AL;;;;;N;ARABIC LETTER BAA;;;;
+0629;ARABIC LETTER TEH MARBUTA;Lo;0;AL;;;;;N;ARABIC LETTER TAA MARBUTAH;;;;
+062A;ARABIC LETTER TEH;Lo;0;AL;;;;;N;ARABIC LETTER TAA;;;;
+062B;ARABIC LETTER THEH;Lo;0;AL;;;;;N;ARABIC LETTER THAA;;;;
+062C;ARABIC LETTER JEEM;Lo;0;AL;;;;;N;;;;;
+062D;ARABIC LETTER HAH;Lo;0;AL;;;;;N;ARABIC LETTER HAA;;;;
+062E;ARABIC LETTER KHAH;Lo;0;AL;;;;;N;ARABIC LETTER KHAA;;;;
+062F;ARABIC LETTER DAL;Lo;0;AL;;;;;N;;;;;
+0630;ARABIC LETTER THAL;Lo;0;AL;;;;;N;;;;;
+0631;ARABIC LETTER REH;Lo;0;AL;;;;;N;ARABIC LETTER RA;;;;
+0632;ARABIC LETTER ZAIN;Lo;0;AL;;;;;N;;;;;
+0633;ARABIC LETTER SEEN;Lo;0;AL;;;;;N;;;;;
+0634;ARABIC LETTER SHEEN;Lo;0;AL;;;;;N;;;;;
+0635;ARABIC LETTER SAD;Lo;0;AL;;;;;N;;;;;
+0636;ARABIC LETTER DAD;Lo;0;AL;;;;;N;;;;;
+0637;ARABIC LETTER TAH;Lo;0;AL;;;;;N;;;;;
+0638;ARABIC LETTER ZAH;Lo;0;AL;;;;;N;ARABIC LETTER DHAH;;;;
+0639;ARABIC LETTER AIN;Lo;0;AL;;;;;N;;;;;
+063A;ARABIC LETTER GHAIN;Lo;0;AL;;;;;N;;;;;
+0640;ARABIC TATWEEL;Lm;0;AL;;;;;N;;;;;
+0641;ARABIC LETTER FEH;Lo;0;AL;;;;;N;ARABIC LETTER FA;;;;
+0642;ARABIC LETTER QAF;Lo;0;AL;;;;;N;;;;;
+0643;ARABIC LETTER KAF;Lo;0;AL;;;;;N;ARABIC LETTER CAF;;;;
+0644;ARABIC LETTER LAM;Lo;0;AL;;;;;N;;;;;
+0645;ARABIC LETTER MEEM;Lo;0;AL;;;;;N;;;;;
+0646;ARABIC LETTER NOON;Lo;0;AL;;;;;N;;;;;
+0647;ARABIC LETTER HEH;Lo;0;AL;;;;;N;ARABIC LETTER HA;;;;
+0648;ARABIC LETTER WAW;Lo;0;AL;;;;;N;;;;;
+0649;ARABIC LETTER ALEF MAKSURA;Lo;0;AL;;;;;N;ARABIC LETTER ALEF MAQSURAH;;;;
+064A;ARABIC LETTER YEH;Lo;0;AL;;;;;N;ARABIC LETTER YA;;;;
+064B;ARABIC FATHATAN;Mn;27;NSM;;;;;N;;;;;
+064C;ARABIC DAMMATAN;Mn;28;NSM;;;;;N;;;;;
+064D;ARABIC KASRATAN;Mn;29;NSM;;;;;N;;;;;
+064E;ARABIC FATHA;Mn;30;NSM;;;;;N;ARABIC FATHAH;;;;
+064F;ARABIC DAMMA;Mn;31;NSM;;;;;N;ARABIC DAMMAH;;;;
+0650;ARABIC KASRA;Mn;32;NSM;;;;;N;ARABIC KASRAH;;;;
+0651;ARABIC SHADDA;Mn;33;NSM;;;;;N;ARABIC SHADDAH;;;;
+0652;ARABIC SUKUN;Mn;34;NSM;;;;;N;;;;;
+0653;ARABIC MADDAH ABOVE;Mn;230;NSM;;;;;N;;;;;
+0654;ARABIC HAMZA ABOVE;Mn;230;NSM;;;;;N;;;;;
+0655;ARABIC HAMZA BELOW;Mn;220;NSM;;;;;N;;;;;
+0656;ARABIC SUBSCRIPT ALEF;Mn;220;NSM;;;;;N;;;;;
+0657;ARABIC INVERTED DAMMA;Mn;230;NSM;;;;;N;;;;;
+0658;ARABIC MARK NOON GHUNNA;Mn;230;NSM;;;;;N;;;;;
+0660;ARABIC-INDIC DIGIT ZERO;Nd;0;AN;;0;0;0;N;;;;;
+0661;ARABIC-INDIC DIGIT ONE;Nd;0;AN;;1;1;1;N;;;;;
+0662;ARABIC-INDIC DIGIT TWO;Nd;0;AN;;2;2;2;N;;;;;
+0663;ARABIC-INDIC DIGIT THREE;Nd;0;AN;;3;3;3;N;;;;;
+0664;ARABIC-INDIC DIGIT FOUR;Nd;0;AN;;4;4;4;N;;;;;
+0665;ARABIC-INDIC DIGIT FIVE;Nd;0;AN;;5;5;5;N;;;;;
+0666;ARABIC-INDIC DIGIT SIX;Nd;0;AN;;6;6;6;N;;;;;
+0667;ARABIC-INDIC DIGIT SEVEN;Nd;0;AN;;7;7;7;N;;;;;
+0668;ARABIC-INDIC DIGIT EIGHT;Nd;0;AN;;8;8;8;N;;;;;
+0669;ARABIC-INDIC DIGIT NINE;Nd;0;AN;;9;9;9;N;;;;;
+066A;ARABIC PERCENT SIGN;Po;0;ET;;;;;N;;;;;
+066B;ARABIC DECIMAL SEPARATOR;Po;0;AN;;;;;N;;;;;
+066C;ARABIC THOUSANDS SEPARATOR;Po;0;AN;;;;;N;;;;;
+066D;ARABIC FIVE POINTED STAR;Po;0;AL;;;;;N;;;;;
+066E;ARABIC LETTER DOTLESS BEH;Lo;0;AL;;;;;N;;;;;
+066F;ARABIC LETTER DOTLESS QAF;Lo;0;AL;;;;;N;;;;;
+0670;ARABIC LETTER SUPERSCRIPT ALEF;Mn;35;NSM;;;;;N;ARABIC ALEF ABOVE;;;;
+0671;ARABIC LETTER ALEF WASLA;Lo;0;AL;;;;;N;ARABIC LETTER HAMZAT WASL ON ALEF;;;;
+0672;ARABIC LETTER ALEF WITH WAVY HAMZA ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER WAVY HAMZAH ON ALEF;;;;
+0673;ARABIC LETTER ALEF WITH WAVY HAMZA BELOW;Lo;0;AL;;;;;N;ARABIC LETTER WAVY HAMZAH UNDER ALEF;;;;
+0674;ARABIC LETTER HIGH HAMZA;Lo;0;AL;;;;;N;ARABIC LETTER HIGH HAMZAH;;;;
+0675;ARABIC LETTER HIGH HAMZA ALEF;Lo;0;AL;<compat> 0627 0674;;;;N;ARABIC LETTER HIGH HAMZAH ALEF;;;;
+0676;ARABIC LETTER HIGH HAMZA WAW;Lo;0;AL;<compat> 0648 0674;;;;N;ARABIC LETTER HIGH HAMZAH WAW;;;;
+0677;ARABIC LETTER U WITH HAMZA ABOVE;Lo;0;AL;<compat> 06C7 0674;;;;N;ARABIC LETTER HIGH HAMZAH WAW WITH DAMMAH;;;;
+0678;ARABIC LETTER HIGH HAMZA YEH;Lo;0;AL;<compat> 064A 0674;;;;N;ARABIC LETTER HIGH HAMZAH YA;;;;
+0679;ARABIC LETTER TTEH;Lo;0;AL;;;;;N;ARABIC LETTER TAA WITH SMALL TAH;;;;
+067A;ARABIC LETTER TTEHEH;Lo;0;AL;;;;;N;ARABIC LETTER TAA WITH TWO DOTS VERTICAL ABOVE;;;;
+067B;ARABIC LETTER BEEH;Lo;0;AL;;;;;N;ARABIC LETTER BAA WITH TWO DOTS VERTICAL BELOW;;;;
+067C;ARABIC LETTER TEH WITH RING;Lo;0;AL;;;;;N;ARABIC LETTER TAA WITH RING;;;;
+067D;ARABIC LETTER TEH WITH THREE DOTS ABOVE DOWNWARDS;Lo;0;AL;;;;;N;ARABIC LETTER TAA WITH THREE DOTS ABOVE DOWNWARD;;;;
+067E;ARABIC LETTER PEH;Lo;0;AL;;;;;N;ARABIC LETTER TAA WITH THREE DOTS BELOW;;;;
+067F;ARABIC LETTER TEHEH;Lo;0;AL;;;;;N;ARABIC LETTER TAA WITH FOUR DOTS ABOVE;;;;
+0680;ARABIC LETTER BEHEH;Lo;0;AL;;;;;N;ARABIC LETTER BAA WITH FOUR DOTS BELOW;;;;
+0681;ARABIC LETTER HAH WITH HAMZA ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER HAMZAH ON HAA;;;;
+0682;ARABIC LETTER HAH WITH TWO DOTS VERTICAL ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER HAA WITH TWO DOTS VERTICAL ABOVE;;;;
+0683;ARABIC LETTER NYEH;Lo;0;AL;;;;;N;ARABIC LETTER HAA WITH MIDDLE TWO DOTS;;;;
+0684;ARABIC LETTER DYEH;Lo;0;AL;;;;;N;ARABIC LETTER HAA WITH MIDDLE TWO DOTS VERTICAL;;;;
+0685;ARABIC LETTER HAH WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER HAA WITH THREE DOTS ABOVE;;;;
+0686;ARABIC LETTER TCHEH;Lo;0;AL;;;;;N;ARABIC LETTER HAA WITH MIDDLE THREE DOTS DOWNWARD;;;;
+0687;ARABIC LETTER TCHEHEH;Lo;0;AL;;;;;N;ARABIC LETTER HAA WITH MIDDLE FOUR DOTS;;;;
+0688;ARABIC LETTER DDAL;Lo;0;AL;;;;;N;ARABIC LETTER DAL WITH SMALL TAH;;;;
+0689;ARABIC LETTER DAL WITH RING;Lo;0;AL;;;;;N;;;;;
+068A;ARABIC LETTER DAL WITH DOT BELOW;Lo;0;AL;;;;;N;;;;;
+068B;ARABIC LETTER DAL WITH DOT BELOW AND SMALL TAH;Lo;0;AL;;;;;N;;;;;
+068C;ARABIC LETTER DAHAL;Lo;0;AL;;;;;N;ARABIC LETTER DAL WITH TWO DOTS ABOVE;;;;
+068D;ARABIC LETTER DDAHAL;Lo;0;AL;;;;;N;ARABIC LETTER DAL WITH TWO DOTS BELOW;;;;
+068E;ARABIC LETTER DUL;Lo;0;AL;;;;;N;ARABIC LETTER DAL WITH THREE DOTS ABOVE;;;;
+068F;ARABIC LETTER DAL WITH THREE DOTS ABOVE DOWNWARDS;Lo;0;AL;;;;;N;ARABIC LETTER DAL WITH THREE DOTS ABOVE DOWNWARD;;;;
+0690;ARABIC LETTER DAL WITH FOUR DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+0691;ARABIC LETTER RREH;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH SMALL TAH;;;;
+0692;ARABIC LETTER REH WITH SMALL V;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH SMALL V;;;;
+0693;ARABIC LETTER REH WITH RING;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH RING;;;;
+0694;ARABIC LETTER REH WITH DOT BELOW;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH DOT BELOW;;;;
+0695;ARABIC LETTER REH WITH SMALL V BELOW;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH SMALL V BELOW;;;;
+0696;ARABIC LETTER REH WITH DOT BELOW AND DOT ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH DOT BELOW AND DOT ABOVE;;;;
+0697;ARABIC LETTER REH WITH TWO DOTS ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH TWO DOTS ABOVE;;;;
+0698;ARABIC LETTER JEH;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH THREE DOTS ABOVE;;;;
+0699;ARABIC LETTER REH WITH FOUR DOTS ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH FOUR DOTS ABOVE;;;;
+069A;ARABIC LETTER SEEN WITH DOT BELOW AND DOT ABOVE;Lo;0;AL;;;;;N;;;;;
+069B;ARABIC LETTER SEEN WITH THREE DOTS BELOW;Lo;0;AL;;;;;N;;;;;
+069C;ARABIC LETTER SEEN WITH THREE DOTS BELOW AND THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+069D;ARABIC LETTER SAD WITH TWO DOTS BELOW;Lo;0;AL;;;;;N;;;;;
+069E;ARABIC LETTER SAD WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+069F;ARABIC LETTER TAH WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+06A0;ARABIC LETTER AIN WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+06A1;ARABIC LETTER DOTLESS FEH;Lo;0;AL;;;;;N;ARABIC LETTER DOTLESS FA;;;;
+06A2;ARABIC LETTER FEH WITH DOT MOVED BELOW;Lo;0;AL;;;;;N;ARABIC LETTER FA WITH DOT MOVED BELOW;;;;
+06A3;ARABIC LETTER FEH WITH DOT BELOW;Lo;0;AL;;;;;N;ARABIC LETTER FA WITH DOT BELOW;;;;
+06A4;ARABIC LETTER VEH;Lo;0;AL;;;;;N;ARABIC LETTER FA WITH THREE DOTS ABOVE;;;;
+06A5;ARABIC LETTER FEH WITH THREE DOTS BELOW;Lo;0;AL;;;;;N;ARABIC LETTER FA WITH THREE DOTS BELOW;;;;
+06A6;ARABIC LETTER PEHEH;Lo;0;AL;;;;;N;ARABIC LETTER FA WITH FOUR DOTS ABOVE;;;;
+06A7;ARABIC LETTER QAF WITH DOT ABOVE;Lo;0;AL;;;;;N;;;;;
+06A8;ARABIC LETTER QAF WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+06A9;ARABIC LETTER KEHEH;Lo;0;AL;;;;;N;ARABIC LETTER OPEN CAF;;;;
+06AA;ARABIC LETTER SWASH KAF;Lo;0;AL;;;;;N;ARABIC LETTER SWASH CAF;;;;
+06AB;ARABIC LETTER KAF WITH RING;Lo;0;AL;;;;;N;ARABIC LETTER CAF WITH RING;;;;
+06AC;ARABIC LETTER KAF WITH DOT ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER CAF WITH DOT ABOVE;;;;
+06AD;ARABIC LETTER NG;Lo;0;AL;;;;;N;ARABIC LETTER CAF WITH THREE DOTS ABOVE;;;;
+06AE;ARABIC LETTER KAF WITH THREE DOTS BELOW;Lo;0;AL;;;;;N;ARABIC LETTER CAF WITH THREE DOTS BELOW;;;;
+06AF;ARABIC LETTER GAF;Lo;0;AL;;;;;N;;*;;;
+06B0;ARABIC LETTER GAF WITH RING;Lo;0;AL;;;;;N;;;;;
+06B1;ARABIC LETTER NGOEH;Lo;0;AL;;;;;N;ARABIC LETTER GAF WITH TWO DOTS ABOVE;;;;
+06B2;ARABIC LETTER GAF WITH TWO DOTS BELOW;Lo;0;AL;;;;;N;;;;;
+06B3;ARABIC LETTER GUEH;Lo;0;AL;;;;;N;ARABIC LETTER GAF WITH TWO DOTS VERTICAL BELOW;;;;
+06B4;ARABIC LETTER GAF WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+06B5;ARABIC LETTER LAM WITH SMALL V;Lo;0;AL;;;;;N;;;;;
+06B6;ARABIC LETTER LAM WITH DOT ABOVE;Lo;0;AL;;;;;N;;;;;
+06B7;ARABIC LETTER LAM WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+06B8;ARABIC LETTER LAM WITH THREE DOTS BELOW;Lo;0;AL;;;;;N;;;;;
+06B9;ARABIC LETTER NOON WITH DOT BELOW;Lo;0;AL;;;;;N;;;;;
+06BA;ARABIC LETTER NOON GHUNNA;Lo;0;AL;;;;;N;ARABIC LETTER DOTLESS NOON;;;;
+06BB;ARABIC LETTER RNOON;Lo;0;AL;;;;;N;ARABIC LETTER DOTLESS NOON WITH SMALL TAH;;;;
+06BC;ARABIC LETTER NOON WITH RING;Lo;0;AL;;;;;N;;;;;
+06BD;ARABIC LETTER NOON WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+06BE;ARABIC LETTER HEH DOACHASHMEE;Lo;0;AL;;;;;N;ARABIC LETTER KNOTTED HA;;;;
+06BF;ARABIC LETTER TCHEH WITH DOT ABOVE;Lo;0;AL;;;;;N;;;;;
+06C0;ARABIC LETTER HEH WITH YEH ABOVE;Lo;0;AL;06D5 0654;;;;N;ARABIC LETTER HAMZAH ON HA;;;;
+06C1;ARABIC LETTER HEH GOAL;Lo;0;AL;;;;;N;ARABIC LETTER HA GOAL;;;;
+06C2;ARABIC LETTER HEH GOAL WITH HAMZA ABOVE;Lo;0;AL;06C1 0654;;;;N;ARABIC LETTER HAMZAH ON HA GOAL;;;;
+06C3;ARABIC LETTER TEH MARBUTA GOAL;Lo;0;AL;;;;;N;ARABIC LETTER TAA MARBUTAH GOAL;;;;
+06C4;ARABIC LETTER WAW WITH RING;Lo;0;AL;;;;;N;;;;;
+06C5;ARABIC LETTER KIRGHIZ OE;Lo;0;AL;;;;;N;ARABIC LETTER WAW WITH BAR;;;;
+06C6;ARABIC LETTER OE;Lo;0;AL;;;;;N;ARABIC LETTER WAW WITH SMALL V;;;;
+06C7;ARABIC LETTER U;Lo;0;AL;;;;;N;ARABIC LETTER WAW WITH DAMMAH;;;;
+06C8;ARABIC LETTER YU;Lo;0;AL;;;;;N;ARABIC LETTER WAW WITH ALEF ABOVE;;;;
+06C9;ARABIC LETTER KIRGHIZ YU;Lo;0;AL;;;;;N;ARABIC LETTER WAW WITH INVERTED SMALL V;;;;
+06CA;ARABIC LETTER WAW WITH TWO DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+06CB;ARABIC LETTER VE;Lo;0;AL;;;;;N;ARABIC LETTER WAW WITH THREE DOTS ABOVE;;;;
+06CC;ARABIC LETTER FARSI YEH;Lo;0;AL;;;;;N;ARABIC LETTER DOTLESS YA;;;;
+06CD;ARABIC LETTER YEH WITH TAIL;Lo;0;AL;;;;;N;ARABIC LETTER YA WITH TAIL;;;;
+06CE;ARABIC LETTER YEH WITH SMALL V;Lo;0;AL;;;;;N;ARABIC LETTER YA WITH SMALL V;;;;
+06CF;ARABIC LETTER WAW WITH DOT ABOVE;Lo;0;AL;;;;;N;;;;;
+06D0;ARABIC LETTER E;Lo;0;AL;;;;;N;ARABIC LETTER YA WITH TWO DOTS VERTICAL BELOW;*;;;
+06D1;ARABIC LETTER YEH WITH THREE DOTS BELOW;Lo;0;AL;;;;;N;ARABIC LETTER YA WITH THREE DOTS BELOW;;;;
+06D2;ARABIC LETTER YEH BARREE;Lo;0;AL;;;;;N;ARABIC LETTER YA BARREE;;;;
+06D3;ARABIC LETTER YEH BARREE WITH HAMZA ABOVE;Lo;0;AL;06D2 0654;;;;N;ARABIC LETTER HAMZAH ON YA BARREE;;;;
+06D4;ARABIC FULL STOP;Po;0;AL;;;;;N;ARABIC PERIOD;;;;
+06D5;ARABIC LETTER AE;Lo;0;AL;;;;;N;;;;;
+06D6;ARABIC SMALL HIGH LIGATURE SAD WITH LAM WITH ALEF MAKSURA;Mn;230;NSM;;;;;N;;;;;
+06D7;ARABIC SMALL HIGH LIGATURE QAF WITH LAM WITH ALEF MAKSURA;Mn;230;NSM;;;;;N;;;;;
+06D8;ARABIC SMALL HIGH MEEM INITIAL FORM;Mn;230;NSM;;;;;N;;;;;
+06D9;ARABIC SMALL HIGH LAM ALEF;Mn;230;NSM;;;;;N;;;;;
+06DA;ARABIC SMALL HIGH JEEM;Mn;230;NSM;;;;;N;;;;;
+06DB;ARABIC SMALL HIGH THREE DOTS;Mn;230;NSM;;;;;N;;;;;
+06DC;ARABIC SMALL HIGH SEEN;Mn;230;NSM;;;;;N;;;;;
+06DD;ARABIC END OF AYAH;Cf;0;AL;;;;;N;;;;;
+06DE;ARABIC START OF RUB EL HIZB;Me;0;NSM;;;;;N;;;;;
+06DF;ARABIC SMALL HIGH ROUNDED ZERO;Mn;230;NSM;;;;;N;;;;;
+06E0;ARABIC SMALL HIGH UPRIGHT RECTANGULAR ZERO;Mn;230;NSM;;;;;N;;;;;
+06E1;ARABIC SMALL HIGH DOTLESS HEAD OF KHAH;Mn;230;NSM;;;;;N;;;;;
+06E2;ARABIC SMALL HIGH MEEM ISOLATED FORM;Mn;230;NSM;;;;;N;;;;;
+06E3;ARABIC SMALL LOW SEEN;Mn;220;NSM;;;;;N;;;;;
+06E4;ARABIC SMALL HIGH MADDA;Mn;230;NSM;;;;;N;;;;;
+06E5;ARABIC SMALL WAW;Lm;0;AL;;;;;N;;;;;
+06E6;ARABIC SMALL YEH;Lm;0;AL;;;;;N;;;;;
+06E7;ARABIC SMALL HIGH YEH;Mn;230;NSM;;;;;N;;;;;
+06E8;ARABIC SMALL HIGH NOON;Mn;230;NSM;;;;;N;;;;;
+06E9;ARABIC PLACE OF SAJDAH;So;0;ON;;;;;N;;;;;
+06EA;ARABIC EMPTY CENTRE LOW STOP;Mn;220;NSM;;;;;N;;;;;
+06EB;ARABIC EMPTY CENTRE HIGH STOP;Mn;230;NSM;;;;;N;;;;;
+06EC;ARABIC ROUNDED HIGH STOP WITH FILLED CENTRE;Mn;230;NSM;;;;;N;;;;;
+06ED;ARABIC SMALL LOW MEEM;Mn;220;NSM;;;;;N;;;;;
+06EE;ARABIC LETTER DAL WITH INVERTED V;Lo;0;AL;;;;;N;;;;;
+06EF;ARABIC LETTER REH WITH INVERTED V;Lo;0;AL;;;;;N;;;;;
+06F0;EXTENDED ARABIC-INDIC DIGIT ZERO;Nd;0;EN;;0;0;0;N;EASTERN ARABIC-INDIC DIGIT ZERO;;;;
+06F1;EXTENDED ARABIC-INDIC DIGIT ONE;Nd;0;EN;;1;1;1;N;EASTERN ARABIC-INDIC DIGIT ONE;;;;
+06F2;EXTENDED ARABIC-INDIC DIGIT TWO;Nd;0;EN;;2;2;2;N;EASTERN ARABIC-INDIC DIGIT TWO;;;;
+06F3;EXTENDED ARABIC-INDIC DIGIT THREE;Nd;0;EN;;3;3;3;N;EASTERN ARABIC-INDIC DIGIT THREE;;;;
+06F4;EXTENDED ARABIC-INDIC DIGIT FOUR;Nd;0;EN;;4;4;4;N;EASTERN ARABIC-INDIC DIGIT FOUR;;;;
+06F5;EXTENDED ARABIC-INDIC DIGIT FIVE;Nd;0;EN;;5;5;5;N;EASTERN ARABIC-INDIC DIGIT FIVE;;;;
+06F6;EXTENDED ARABIC-INDIC DIGIT SIX;Nd;0;EN;;6;6;6;N;EASTERN ARABIC-INDIC DIGIT SIX;;;;
+06F7;EXTENDED ARABIC-INDIC DIGIT SEVEN;Nd;0;EN;;7;7;7;N;EASTERN ARABIC-INDIC DIGIT SEVEN;;;;
+06F8;EXTENDED ARABIC-INDIC DIGIT EIGHT;Nd;0;EN;;8;8;8;N;EASTERN ARABIC-INDIC DIGIT EIGHT;;;;
+06F9;EXTENDED ARABIC-INDIC DIGIT NINE;Nd;0;EN;;9;9;9;N;EASTERN ARABIC-INDIC DIGIT NINE;;;;
+06FA;ARABIC LETTER SHEEN WITH DOT BELOW;Lo;0;AL;;;;;N;;;;;
+06FB;ARABIC LETTER DAD WITH DOT BELOW;Lo;0;AL;;;;;N;;;;;
+06FC;ARABIC LETTER GHAIN WITH DOT BELOW;Lo;0;AL;;;;;N;;;;;
+06FD;ARABIC SIGN SINDHI AMPERSAND;So;0;AL;;;;;N;;;;;
+06FE;ARABIC SIGN SINDHI POSTPOSITION MEN;So;0;AL;;;;;N;;;;;
+06FF;ARABIC LETTER HEH WITH INVERTED V;Lo;0;AL;;;;;N;;;;;
+0700;SYRIAC END OF PARAGRAPH;Po;0;AL;;;;;N;;;;;
+0701;SYRIAC SUPRALINEAR FULL STOP;Po;0;AL;;;;;N;;;;;
+0702;SYRIAC SUBLINEAR FULL STOP;Po;0;AL;;;;;N;;;;;
+0703;SYRIAC SUPRALINEAR COLON;Po;0;AL;;;;;N;;;;;
+0704;SYRIAC SUBLINEAR COLON;Po;0;AL;;;;;N;;;;;
+0705;SYRIAC HORIZONTAL COLON;Po;0;AL;;;;;N;;;;;
+0706;SYRIAC COLON SKEWED LEFT;Po;0;AL;;;;;N;;;;;
+0707;SYRIAC COLON SKEWED RIGHT;Po;0;AL;;;;;N;;;;;
+0708;SYRIAC SUPRALINEAR COLON SKEWED LEFT;Po;0;AL;;;;;N;;;;;
+0709;SYRIAC SUBLINEAR COLON SKEWED RIGHT;Po;0;AL;;;;;N;;;;;
+070A;SYRIAC CONTRACTION;Po;0;AL;;;;;N;;;;;
+070B;SYRIAC HARKLEAN OBELUS;Po;0;AL;;;;;N;;;;;
+070C;SYRIAC HARKLEAN METOBELUS;Po;0;AL;;;;;N;;;;;
+070D;SYRIAC HARKLEAN ASTERISCUS;Po;0;AL;;;;;N;;;;;
+070F;SYRIAC ABBREVIATION MARK;Cf;0;BN;;;;;N;;;;;
+0710;SYRIAC LETTER ALAPH;Lo;0;AL;;;;;N;;;;;
+0711;SYRIAC LETTER SUPERSCRIPT ALAPH;Mn;36;NSM;;;;;N;;;;;
+0712;SYRIAC LETTER BETH;Lo;0;AL;;;;;N;;;;;
+0713;SYRIAC LETTER GAMAL;Lo;0;AL;;;;;N;;;;;
+0714;SYRIAC LETTER GAMAL GARSHUNI;Lo;0;AL;;;;;N;;;;;
+0715;SYRIAC LETTER DALATH;Lo;0;AL;;;;;N;;;;;
+0716;SYRIAC LETTER DOTLESS DALATH RISH;Lo;0;AL;;;;;N;;;;;
+0717;SYRIAC LETTER HE;Lo;0;AL;;;;;N;;;;;
+0718;SYRIAC LETTER WAW;Lo;0;AL;;;;;N;;;;;
+0719;SYRIAC LETTER ZAIN;Lo;0;AL;;;;;N;;;;;
+071A;SYRIAC LETTER HETH;Lo;0;AL;;;;;N;;;;;
+071B;SYRIAC LETTER TETH;Lo;0;AL;;;;;N;;;;;
+071C;SYRIAC LETTER TETH GARSHUNI;Lo;0;AL;;;;;N;;;;;
+071D;SYRIAC LETTER YUDH;Lo;0;AL;;;;;N;;;;;
+071E;SYRIAC LETTER YUDH HE;Lo;0;AL;;;;;N;;;;;
+071F;SYRIAC LETTER KAPH;Lo;0;AL;;;;;N;;;;;
+0720;SYRIAC LETTER LAMADH;Lo;0;AL;;;;;N;;;;;
+0721;SYRIAC LETTER MIM;Lo;0;AL;;;;;N;;;;;
+0722;SYRIAC LETTER NUN;Lo;0;AL;;;;;N;;;;;
+0723;SYRIAC LETTER SEMKATH;Lo;0;AL;;;;;N;;;;;
+0724;SYRIAC LETTER FINAL SEMKATH;Lo;0;AL;;;;;N;;;;;
+0725;SYRIAC LETTER E;Lo;0;AL;;;;;N;;;;;
+0726;SYRIAC LETTER PE;Lo;0;AL;;;;;N;;;;;
+0727;SYRIAC LETTER REVERSED PE;Lo;0;AL;;;;;N;;;;;
+0728;SYRIAC LETTER SADHE;Lo;0;AL;;;;;N;;;;;
+0729;SYRIAC LETTER QAPH;Lo;0;AL;;;;;N;;;;;
+072A;SYRIAC LETTER RISH;Lo;0;AL;;;;;N;;;;;
+072B;SYRIAC LETTER SHIN;Lo;0;AL;;;;;N;;;;;
+072C;SYRIAC LETTER TAW;Lo;0;AL;;;;;N;;;;;
+072D;SYRIAC LETTER PERSIAN BHETH;Lo;0;AL;;;;;N;;;;;
+072E;SYRIAC LETTER PERSIAN GHAMAL;Lo;0;AL;;;;;N;;;;;
+072F;SYRIAC LETTER PERSIAN DHALATH;Lo;0;AL;;;;;N;;;;;
+0730;SYRIAC PTHAHA ABOVE;Mn;230;NSM;;;;;N;;;;;
+0731;SYRIAC PTHAHA BELOW;Mn;220;NSM;;;;;N;;;;;
+0732;SYRIAC PTHAHA DOTTED;Mn;230;NSM;;;;;N;;;;;
+0733;SYRIAC ZQAPHA ABOVE;Mn;230;NSM;;;;;N;;;;;
+0734;SYRIAC ZQAPHA BELOW;Mn;220;NSM;;;;;N;;;;;
+0735;SYRIAC ZQAPHA DOTTED;Mn;230;NSM;;;;;N;;;;;
+0736;SYRIAC RBASA ABOVE;Mn;230;NSM;;;;;N;;;;;
+0737;SYRIAC RBASA BELOW;Mn;220;NSM;;;;;N;;;;;
+0738;SYRIAC DOTTED ZLAMA HORIZONTAL;Mn;220;NSM;;;;;N;;;;;
+0739;SYRIAC DOTTED ZLAMA ANGULAR;Mn;220;NSM;;;;;N;;;;;
+073A;SYRIAC HBASA ABOVE;Mn;230;NSM;;;;;N;;;;;
+073B;SYRIAC HBASA BELOW;Mn;220;NSM;;;;;N;;;;;
+073C;SYRIAC HBASA-ESASA DOTTED;Mn;220;NSM;;;;;N;;;;;
+073D;SYRIAC ESASA ABOVE;Mn;230;NSM;;;;;N;;;;;
+073E;SYRIAC ESASA BELOW;Mn;220;NSM;;;;;N;;;;;
+073F;SYRIAC RWAHA;Mn;230;NSM;;;;;N;;;;;
+0740;SYRIAC FEMININE DOT;Mn;230;NSM;;;;;N;;;;;
+0741;SYRIAC QUSHSHAYA;Mn;230;NSM;;;;;N;;;;;
+0742;SYRIAC RUKKAKHA;Mn;220;NSM;;;;;N;;;;;
+0743;SYRIAC TWO VERTICAL DOTS ABOVE;Mn;230;NSM;;;;;N;;;;;
+0744;SYRIAC TWO VERTICAL DOTS BELOW;Mn;220;NSM;;;;;N;;;;;
+0745;SYRIAC THREE DOTS ABOVE;Mn;230;NSM;;;;;N;;;;;
+0746;SYRIAC THREE DOTS BELOW;Mn;220;NSM;;;;;N;;;;;
+0747;SYRIAC OBLIQUE LINE ABOVE;Mn;230;NSM;;;;;N;;;;;
+0748;SYRIAC OBLIQUE LINE BELOW;Mn;220;NSM;;;;;N;;;;;
+0749;SYRIAC MUSIC;Mn;230;NSM;;;;;N;;;;;
+074A;SYRIAC BARREKH;Mn;230;NSM;;;;;N;;;;;
+074D;SYRIAC LETTER SOGDIAN ZHAIN;Lo;0;AL;;;;;N;;;;;
+074E;SYRIAC LETTER SOGDIAN KHAPH;Lo;0;AL;;;;;N;;;;;
+074F;SYRIAC LETTER SOGDIAN FE;Lo;0;AL;;;;;N;;;;;
+0780;THAANA LETTER HAA;Lo;0;AL;;;;;N;;;;;
+0781;THAANA LETTER SHAVIYANI;Lo;0;AL;;;;;N;;;;;
+0782;THAANA LETTER NOONU;Lo;0;AL;;;;;N;;;;;
+0783;THAANA LETTER RAA;Lo;0;AL;;;;;N;;;;;
+0784;THAANA LETTER BAA;Lo;0;AL;;;;;N;;;;;
+0785;THAANA LETTER LHAVIYANI;Lo;0;AL;;;;;N;;;;;
+0786;THAANA LETTER KAAFU;Lo;0;AL;;;;;N;;;;;
+0787;THAANA LETTER ALIFU;Lo;0;AL;;;;;N;;;;;
+0788;THAANA LETTER VAAVU;Lo;0;AL;;;;;N;;;;;
+0789;THAANA LETTER MEEMU;Lo;0;AL;;;;;N;;;;;
+078A;THAANA LETTER FAAFU;Lo;0;AL;;;;;N;;;;;
+078B;THAANA LETTER DHAALU;Lo;0;AL;;;;;N;;;;;
+078C;THAANA LETTER THAA;Lo;0;AL;;;;;N;;;;;
+078D;THAANA LETTER LAAMU;Lo;0;AL;;;;;N;;;;;
+078E;THAANA LETTER GAAFU;Lo;0;AL;;;;;N;;;;;
+078F;THAANA LETTER GNAVIYANI;Lo;0;AL;;;;;N;;;;;
+0790;THAANA LETTER SEENU;Lo;0;AL;;;;;N;;;;;
+0791;THAANA LETTER DAVIYANI;Lo;0;AL;;;;;N;;;;;
+0792;THAANA LETTER ZAVIYANI;Lo;0;AL;;;;;N;;;;;
+0793;THAANA LETTER TAVIYANI;Lo;0;AL;;;;;N;;;;;
+0794;THAANA LETTER YAA;Lo;0;AL;;;;;N;;;;;
+0795;THAANA LETTER PAVIYANI;Lo;0;AL;;;;;N;;;;;
+0796;THAANA LETTER JAVIYANI;Lo;0;AL;;;;;N;;;;;
+0797;THAANA LETTER CHAVIYANI;Lo;0;AL;;;;;N;;;;;
+0798;THAANA LETTER TTAA;Lo;0;AL;;;;;N;;;;;
+0799;THAANA LETTER HHAA;Lo;0;AL;;;;;N;;;;;
+079A;THAANA LETTER KHAA;Lo;0;AL;;;;;N;;;;;
+079B;THAANA LETTER THAALU;Lo;0;AL;;;;;N;;;;;
+079C;THAANA LETTER ZAA;Lo;0;AL;;;;;N;;;;;
+079D;THAANA LETTER SHEENU;Lo;0;AL;;;;;N;;;;;
+079E;THAANA LETTER SAADHU;Lo;0;AL;;;;;N;;;;;
+079F;THAANA LETTER DAADHU;Lo;0;AL;;;;;N;;;;;
+07A0;THAANA LETTER TO;Lo;0;AL;;;;;N;;;;;
+07A1;THAANA LETTER ZO;Lo;0;AL;;;;;N;;;;;
+07A2;THAANA LETTER AINU;Lo;0;AL;;;;;N;;;;;
+07A3;THAANA LETTER GHAINU;Lo;0;AL;;;;;N;;;;;
+07A4;THAANA LETTER QAAFU;Lo;0;AL;;;;;N;;;;;
+07A5;THAANA LETTER WAAVU;Lo;0;AL;;;;;N;;;;;
+07A6;THAANA ABAFILI;Mn;0;NSM;;;;;N;;;;;
+07A7;THAANA AABAAFILI;Mn;0;NSM;;;;;N;;;;;
+07A8;THAANA IBIFILI;Mn;0;NSM;;;;;N;;;;;
+07A9;THAANA EEBEEFILI;Mn;0;NSM;;;;;N;;;;;
+07AA;THAANA UBUFILI;Mn;0;NSM;;;;;N;;;;;
+07AB;THAANA OOBOOFILI;Mn;0;NSM;;;;;N;;;;;
+07AC;THAANA EBEFILI;Mn;0;NSM;;;;;N;;;;;
+07AD;THAANA EYBEYFILI;Mn;0;NSM;;;;;N;;;;;
+07AE;THAANA OBOFILI;Mn;0;NSM;;;;;N;;;;;
+07AF;THAANA OABOAFILI;Mn;0;NSM;;;;;N;;;;;
+07B0;THAANA SUKUN;Mn;0;NSM;;;;;N;;;;;
+07B1;THAANA LETTER NAA;Lo;0;AL;;;;;N;;;;;
+0901;DEVANAGARI SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;;
+0902;DEVANAGARI SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;;
+0903;DEVANAGARI SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+0904;DEVANAGARI LETTER SHORT A;Lo;0;L;;;;;N;;;;;
+0905;DEVANAGARI LETTER A;Lo;0;L;;;;;N;;;;;
+0906;DEVANAGARI LETTER AA;Lo;0;L;;;;;N;;;;;
+0907;DEVANAGARI LETTER I;Lo;0;L;;;;;N;;;;;
+0908;DEVANAGARI LETTER II;Lo;0;L;;;;;N;;;;;
+0909;DEVANAGARI LETTER U;Lo;0;L;;;;;N;;;;;
+090A;DEVANAGARI LETTER UU;Lo;0;L;;;;;N;;;;;
+090B;DEVANAGARI LETTER VOCALIC R;Lo;0;L;;;;;N;;;;;
+090C;DEVANAGARI LETTER VOCALIC L;Lo;0;L;;;;;N;;;;;
+090D;DEVANAGARI LETTER CANDRA E;Lo;0;L;;;;;N;;;;;
+090E;DEVANAGARI LETTER SHORT E;Lo;0;L;;;;;N;;;;;
+090F;DEVANAGARI LETTER E;Lo;0;L;;;;;N;;;;;
+0910;DEVANAGARI LETTER AI;Lo;0;L;;;;;N;;;;;
+0911;DEVANAGARI LETTER CANDRA O;Lo;0;L;;;;;N;;;;;
+0912;DEVANAGARI LETTER SHORT O;Lo;0;L;;;;;N;;;;;
+0913;DEVANAGARI LETTER O;Lo;0;L;;;;;N;;;;;
+0914;DEVANAGARI LETTER AU;Lo;0;L;;;;;N;;;;;
+0915;DEVANAGARI LETTER KA;Lo;0;L;;;;;N;;;;;
+0916;DEVANAGARI LETTER KHA;Lo;0;L;;;;;N;;;;;
+0917;DEVANAGARI LETTER GA;Lo;0;L;;;;;N;;;;;
+0918;DEVANAGARI LETTER GHA;Lo;0;L;;;;;N;;;;;
+0919;DEVANAGARI LETTER NGA;Lo;0;L;;;;;N;;;;;
+091A;DEVANAGARI LETTER CA;Lo;0;L;;;;;N;;;;;
+091B;DEVANAGARI LETTER CHA;Lo;0;L;;;;;N;;;;;
+091C;DEVANAGARI LETTER JA;Lo;0;L;;;;;N;;;;;
+091D;DEVANAGARI LETTER JHA;Lo;0;L;;;;;N;;;;;
+091E;DEVANAGARI LETTER NYA;Lo;0;L;;;;;N;;;;;
+091F;DEVANAGARI LETTER TTA;Lo;0;L;;;;;N;;;;;
+0920;DEVANAGARI LETTER TTHA;Lo;0;L;;;;;N;;;;;
+0921;DEVANAGARI LETTER DDA;Lo;0;L;;;;;N;;;;;
+0922;DEVANAGARI LETTER DDHA;Lo;0;L;;;;;N;;;;;
+0923;DEVANAGARI LETTER NNA;Lo;0;L;;;;;N;;;;;
+0924;DEVANAGARI LETTER TA;Lo;0;L;;;;;N;;;;;
+0925;DEVANAGARI LETTER THA;Lo;0;L;;;;;N;;;;;
+0926;DEVANAGARI LETTER DA;Lo;0;L;;;;;N;;;;;
+0927;DEVANAGARI LETTER DHA;Lo;0;L;;;;;N;;;;;
+0928;DEVANAGARI LETTER NA;Lo;0;L;;;;;N;;;;;
+0929;DEVANAGARI LETTER NNNA;Lo;0;L;0928 093C;;;;N;;;;;
+092A;DEVANAGARI LETTER PA;Lo;0;L;;;;;N;;;;;
+092B;DEVANAGARI LETTER PHA;Lo;0;L;;;;;N;;;;;
+092C;DEVANAGARI LETTER BA;Lo;0;L;;;;;N;;;;;
+092D;DEVANAGARI LETTER BHA;Lo;0;L;;;;;N;;;;;
+092E;DEVANAGARI LETTER MA;Lo;0;L;;;;;N;;;;;
+092F;DEVANAGARI LETTER YA;Lo;0;L;;;;;N;;;;;
+0930;DEVANAGARI LETTER RA;Lo;0;L;;;;;N;;;;;
+0931;DEVANAGARI LETTER RRA;Lo;0;L;0930 093C;;;;N;;;;;
+0932;DEVANAGARI LETTER LA;Lo;0;L;;;;;N;;;;;
+0933;DEVANAGARI LETTER LLA;Lo;0;L;;;;;N;;;;;
+0934;DEVANAGARI LETTER LLLA;Lo;0;L;0933 093C;;;;N;;;;;
+0935;DEVANAGARI LETTER VA;Lo;0;L;;;;;N;;;;;
+0936;DEVANAGARI LETTER SHA;Lo;0;L;;;;;N;;;;;
+0937;DEVANAGARI LETTER SSA;Lo;0;L;;;;;N;;;;;
+0938;DEVANAGARI LETTER SA;Lo;0;L;;;;;N;;;;;
+0939;DEVANAGARI LETTER HA;Lo;0;L;;;;;N;;;;;
+093C;DEVANAGARI SIGN NUKTA;Mn;7;NSM;;;;;N;;;;;
+093D;DEVANAGARI SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;;
+093E;DEVANAGARI VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+093F;DEVANAGARI VOWEL SIGN I;Mc;0;L;;;;;N;;;;;
+0940;DEVANAGARI VOWEL SIGN II;Mc;0;L;;;;;N;;;;;
+0941;DEVANAGARI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+0942;DEVANAGARI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+0943;DEVANAGARI VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;;
+0944;DEVANAGARI VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;;
+0945;DEVANAGARI VOWEL SIGN CANDRA E;Mn;0;NSM;;;;;N;;;;;
+0946;DEVANAGARI VOWEL SIGN SHORT E;Mn;0;NSM;;;;;N;;;;;
+0947;DEVANAGARI VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;;
+0948;DEVANAGARI VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;;
+0949;DEVANAGARI VOWEL SIGN CANDRA O;Mc;0;L;;;;;N;;;;;
+094A;DEVANAGARI VOWEL SIGN SHORT O;Mc;0;L;;;;;N;;;;;
+094B;DEVANAGARI VOWEL SIGN O;Mc;0;L;;;;;N;;;;;
+094C;DEVANAGARI VOWEL SIGN AU;Mc;0;L;;;;;N;;;;;
+094D;DEVANAGARI SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+0950;DEVANAGARI OM;Lo;0;L;;;;;N;;;;;
+0951;DEVANAGARI STRESS SIGN UDATTA;Mn;230;NSM;;;;;N;;;;;
+0952;DEVANAGARI STRESS SIGN ANUDATTA;Mn;220;NSM;;;;;N;;;;;
+0953;DEVANAGARI GRAVE ACCENT;Mn;230;NSM;;;;;N;;;;;
+0954;DEVANAGARI ACUTE ACCENT;Mn;230;NSM;;;;;N;;;;;
+0958;DEVANAGARI LETTER QA;Lo;0;L;0915 093C;;;;N;;;;;
+0959;DEVANAGARI LETTER KHHA;Lo;0;L;0916 093C;;;;N;;;;;
+095A;DEVANAGARI LETTER GHHA;Lo;0;L;0917 093C;;;;N;;;;;
+095B;DEVANAGARI LETTER ZA;Lo;0;L;091C 093C;;;;N;;;;;
+095C;DEVANAGARI LETTER DDDHA;Lo;0;L;0921 093C;;;;N;;;;;
+095D;DEVANAGARI LETTER RHA;Lo;0;L;0922 093C;;;;N;;;;;
+095E;DEVANAGARI LETTER FA;Lo;0;L;092B 093C;;;;N;;;;;
+095F;DEVANAGARI LETTER YYA;Lo;0;L;092F 093C;;;;N;;;;;
+0960;DEVANAGARI LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;;
+0961;DEVANAGARI LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;;
+0962;DEVANAGARI VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;;
+0963;DEVANAGARI VOWEL SIGN VOCALIC LL;Mn;0;NSM;;;;;N;;;;;
+0964;DEVANAGARI DANDA;Po;0;L;;;;;N;;;;;
+0965;DEVANAGARI DOUBLE DANDA;Po;0;L;;;;;N;;;;;
+0966;DEVANAGARI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+0967;DEVANAGARI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+0968;DEVANAGARI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+0969;DEVANAGARI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+096A;DEVANAGARI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+096B;DEVANAGARI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+096C;DEVANAGARI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+096D;DEVANAGARI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+096E;DEVANAGARI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+096F;DEVANAGARI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+0970;DEVANAGARI ABBREVIATION SIGN;Po;0;L;;;;;N;;;;;
+0981;BENGALI SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;;
+0982;BENGALI SIGN ANUSVARA;Mc;0;L;;;;;N;;;;;
+0983;BENGALI SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+0985;BENGALI LETTER A;Lo;0;L;;;;;N;;;;;
+0986;BENGALI LETTER AA;Lo;0;L;;;;;N;;;;;
+0987;BENGALI LETTER I;Lo;0;L;;;;;N;;;;;
+0988;BENGALI LETTER II;Lo;0;L;;;;;N;;;;;
+0989;BENGALI LETTER U;Lo;0;L;;;;;N;;;;;
+098A;BENGALI LETTER UU;Lo;0;L;;;;;N;;;;;
+098B;BENGALI LETTER VOCALIC R;Lo;0;L;;;;;N;;;;;
+098C;BENGALI LETTER VOCALIC L;Lo;0;L;;;;;N;;;;;
+098F;BENGALI LETTER E;Lo;0;L;;;;;N;;;;;
+0990;BENGALI LETTER AI;Lo;0;L;;;;;N;;;;;
+0993;BENGALI LETTER O;Lo;0;L;;;;;N;;;;;
+0994;BENGALI LETTER AU;Lo;0;L;;;;;N;;;;;
+0995;BENGALI LETTER KA;Lo;0;L;;;;;N;;;;;
+0996;BENGALI LETTER KHA;Lo;0;L;;;;;N;;;;;
+0997;BENGALI LETTER GA;Lo;0;L;;;;;N;;;;;
+0998;BENGALI LETTER GHA;Lo;0;L;;;;;N;;;;;
+0999;BENGALI LETTER NGA;Lo;0;L;;;;;N;;;;;
+099A;BENGALI LETTER CA;Lo;0;L;;;;;N;;;;;
+099B;BENGALI LETTER CHA;Lo;0;L;;;;;N;;;;;
+099C;BENGALI LETTER JA;Lo;0;L;;;;;N;;;;;
+099D;BENGALI LETTER JHA;Lo;0;L;;;;;N;;;;;
+099E;BENGALI LETTER NYA;Lo;0;L;;;;;N;;;;;
+099F;BENGALI LETTER TTA;Lo;0;L;;;;;N;;;;;
+09A0;BENGALI LETTER TTHA;Lo;0;L;;;;;N;;;;;
+09A1;BENGALI LETTER DDA;Lo;0;L;;;;;N;;;;;
+09A2;BENGALI LETTER DDHA;Lo;0;L;;;;;N;;;;;
+09A3;BENGALI LETTER NNA;Lo;0;L;;;;;N;;;;;
+09A4;BENGALI LETTER TA;Lo;0;L;;;;;N;;;;;
+09A5;BENGALI LETTER THA;Lo;0;L;;;;;N;;;;;
+09A6;BENGALI LETTER DA;Lo;0;L;;;;;N;;;;;
+09A7;BENGALI LETTER DHA;Lo;0;L;;;;;N;;;;;
+09A8;BENGALI LETTER NA;Lo;0;L;;;;;N;;;;;
+09AA;BENGALI LETTER PA;Lo;0;L;;;;;N;;;;;
+09AB;BENGALI LETTER PHA;Lo;0;L;;;;;N;;;;;
+09AC;BENGALI LETTER BA;Lo;0;L;;;;;N;;;;;
+09AD;BENGALI LETTER BHA;Lo;0;L;;;;;N;;;;;
+09AE;BENGALI LETTER MA;Lo;0;L;;;;;N;;;;;
+09AF;BENGALI LETTER YA;Lo;0;L;;;;;N;;;;;
+09B0;BENGALI LETTER RA;Lo;0;L;;;;;N;;;;;
+09B2;BENGALI LETTER LA;Lo;0;L;;;;;N;;;;;
+09B6;BENGALI LETTER SHA;Lo;0;L;;;;;N;;;;;
+09B7;BENGALI LETTER SSA;Lo;0;L;;;;;N;;;;;
+09B8;BENGALI LETTER SA;Lo;0;L;;;;;N;;;;;
+09B9;BENGALI LETTER HA;Lo;0;L;;;;;N;;;;;
+09BC;BENGALI SIGN NUKTA;Mn;7;NSM;;;;;N;;;;;
+09BD;BENGALI SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;;
+09BE;BENGALI VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+09BF;BENGALI VOWEL SIGN I;Mc;0;L;;;;;N;;;;;
+09C0;BENGALI VOWEL SIGN II;Mc;0;L;;;;;N;;;;;
+09C1;BENGALI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+09C2;BENGALI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+09C3;BENGALI VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;;
+09C4;BENGALI VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;;
+09C7;BENGALI VOWEL SIGN E;Mc;0;L;;;;;N;;;;;
+09C8;BENGALI VOWEL SIGN AI;Mc;0;L;;;;;N;;;;;
+09CB;BENGALI VOWEL SIGN O;Mc;0;L;09C7 09BE;;;;N;;;;;
+09CC;BENGALI VOWEL SIGN AU;Mc;0;L;09C7 09D7;;;;N;;;;;
+09CD;BENGALI SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+09D7;BENGALI AU LENGTH MARK;Mc;0;L;;;;;N;;;;;
+09DC;BENGALI LETTER RRA;Lo;0;L;09A1 09BC;;;;N;;;;;
+09DD;BENGALI LETTER RHA;Lo;0;L;09A2 09BC;;;;N;;;;;
+09DF;BENGALI LETTER YYA;Lo;0;L;09AF 09BC;;;;N;;;;;
+09E0;BENGALI LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;;
+09E1;BENGALI LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;;
+09E2;BENGALI VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;;
+09E3;BENGALI VOWEL SIGN VOCALIC LL;Mn;0;NSM;;;;;N;;;;;
+09E6;BENGALI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+09E7;BENGALI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+09E8;BENGALI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+09E9;BENGALI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+09EA;BENGALI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+09EB;BENGALI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+09EC;BENGALI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+09ED;BENGALI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+09EE;BENGALI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+09EF;BENGALI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+09F0;BENGALI LETTER RA WITH MIDDLE DIAGONAL;Lo;0;L;;;;;N;;Assamese;;;
+09F1;BENGALI LETTER RA WITH LOWER DIAGONAL;Lo;0;L;;;;;N;BENGALI LETTER VA WITH LOWER DIAGONAL;Assamese;;;
+09F2;BENGALI RUPEE MARK;Sc;0;ET;;;;;N;;;;;
+09F3;BENGALI RUPEE SIGN;Sc;0;ET;;;;;N;;;;;
+09F4;BENGALI CURRENCY NUMERATOR ONE;No;0;L;;;;1;N;;;;;
+09F5;BENGALI CURRENCY NUMERATOR TWO;No;0;L;;;;2;N;;;;;
+09F6;BENGALI CURRENCY NUMERATOR THREE;No;0;L;;;;3;N;;;;;
+09F7;BENGALI CURRENCY NUMERATOR FOUR;No;0;L;;;;4;N;;;;;
+09F8;BENGALI CURRENCY NUMERATOR ONE LESS THAN THE DENOMINATOR;No;0;L;;;;;N;;;;;
+09F9;BENGALI CURRENCY DENOMINATOR SIXTEEN;No;0;L;;;;16;N;;;;;
+09FA;BENGALI ISSHAR;So;0;L;;;;;N;;;;;
+0A01;GURMUKHI SIGN ADAK BINDI;Mn;0;NSM;;;;;N;;;;;
+0A02;GURMUKHI SIGN BINDI;Mn;0;NSM;;;;;N;;;;;
+0A03;GURMUKHI SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+0A05;GURMUKHI LETTER A;Lo;0;L;;;;;N;;;;;
+0A06;GURMUKHI LETTER AA;Lo;0;L;;;;;N;;;;;
+0A07;GURMUKHI LETTER I;Lo;0;L;;;;;N;;;;;
+0A08;GURMUKHI LETTER II;Lo;0;L;;;;;N;;;;;
+0A09;GURMUKHI LETTER U;Lo;0;L;;;;;N;;;;;
+0A0A;GURMUKHI LETTER UU;Lo;0;L;;;;;N;;;;;
+0A0F;GURMUKHI LETTER EE;Lo;0;L;;;;;N;;;;;
+0A10;GURMUKHI LETTER AI;Lo;0;L;;;;;N;;;;;
+0A13;GURMUKHI LETTER OO;Lo;0;L;;;;;N;;;;;
+0A14;GURMUKHI LETTER AU;Lo;0;L;;;;;N;;;;;
+0A15;GURMUKHI LETTER KA;Lo;0;L;;;;;N;;;;;
+0A16;GURMUKHI LETTER KHA;Lo;0;L;;;;;N;;;;;
+0A17;GURMUKHI LETTER GA;Lo;0;L;;;;;N;;;;;
+0A18;GURMUKHI LETTER GHA;Lo;0;L;;;;;N;;;;;
+0A19;GURMUKHI LETTER NGA;Lo;0;L;;;;;N;;;;;
+0A1A;GURMUKHI LETTER CA;Lo;0;L;;;;;N;;;;;
+0A1B;GURMUKHI LETTER CHA;Lo;0;L;;;;;N;;;;;
+0A1C;GURMUKHI LETTER JA;Lo;0;L;;;;;N;;;;;
+0A1D;GURMUKHI LETTER JHA;Lo;0;L;;;;;N;;;;;
+0A1E;GURMUKHI LETTER NYA;Lo;0;L;;;;;N;;;;;
+0A1F;GURMUKHI LETTER TTA;Lo;0;L;;;;;N;;;;;
+0A20;GURMUKHI LETTER TTHA;Lo;0;L;;;;;N;;;;;
+0A21;GURMUKHI LETTER DDA;Lo;0;L;;;;;N;;;;;
+0A22;GURMUKHI LETTER DDHA;Lo;0;L;;;;;N;;;;;
+0A23;GURMUKHI LETTER NNA;Lo;0;L;;;;;N;;;;;
+0A24;GURMUKHI LETTER TA;Lo;0;L;;;;;N;;;;;
+0A25;GURMUKHI LETTER THA;Lo;0;L;;;;;N;;;;;
+0A26;GURMUKHI LETTER DA;Lo;0;L;;;;;N;;;;;
+0A27;GURMUKHI LETTER DHA;Lo;0;L;;;;;N;;;;;
+0A28;GURMUKHI LETTER NA;Lo;0;L;;;;;N;;;;;
+0A2A;GURMUKHI LETTER PA;Lo;0;L;;;;;N;;;;;
+0A2B;GURMUKHI LETTER PHA;Lo;0;L;;;;;N;;;;;
+0A2C;GURMUKHI LETTER BA;Lo;0;L;;;;;N;;;;;
+0A2D;GURMUKHI LETTER BHA;Lo;0;L;;;;;N;;;;;
+0A2E;GURMUKHI LETTER MA;Lo;0;L;;;;;N;;;;;
+0A2F;GURMUKHI LETTER YA;Lo;0;L;;;;;N;;;;;
+0A30;GURMUKHI LETTER RA;Lo;0;L;;;;;N;;;;;
+0A32;GURMUKHI LETTER LA;Lo;0;L;;;;;N;;;;;
+0A33;GURMUKHI LETTER LLA;Lo;0;L;0A32 0A3C;;;;N;;;;;
+0A35;GURMUKHI LETTER VA;Lo;0;L;;;;;N;;;;;
+0A36;GURMUKHI LETTER SHA;Lo;0;L;0A38 0A3C;;;;N;;;;;
+0A38;GURMUKHI LETTER SA;Lo;0;L;;;;;N;;;;;
+0A39;GURMUKHI LETTER HA;Lo;0;L;;;;;N;;;;;
+0A3C;GURMUKHI SIGN NUKTA;Mn;7;NSM;;;;;N;;;;;
+0A3E;GURMUKHI VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+0A3F;GURMUKHI VOWEL SIGN I;Mc;0;L;;;;;N;;;;;
+0A40;GURMUKHI VOWEL SIGN II;Mc;0;L;;;;;N;;;;;
+0A41;GURMUKHI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+0A42;GURMUKHI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+0A47;GURMUKHI VOWEL SIGN EE;Mn;0;NSM;;;;;N;;;;;
+0A48;GURMUKHI VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;;
+0A4B;GURMUKHI VOWEL SIGN OO;Mn;0;NSM;;;;;N;;;;;
+0A4C;GURMUKHI VOWEL SIGN AU;Mn;0;NSM;;;;;N;;;;;
+0A4D;GURMUKHI SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+0A59;GURMUKHI LETTER KHHA;Lo;0;L;0A16 0A3C;;;;N;;;;;
+0A5A;GURMUKHI LETTER GHHA;Lo;0;L;0A17 0A3C;;;;N;;;;;
+0A5B;GURMUKHI LETTER ZA;Lo;0;L;0A1C 0A3C;;;;N;;;;;
+0A5C;GURMUKHI LETTER RRA;Lo;0;L;;;;;N;;;;;
+0A5E;GURMUKHI LETTER FA;Lo;0;L;0A2B 0A3C;;;;N;;;;;
+0A66;GURMUKHI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+0A67;GURMUKHI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+0A68;GURMUKHI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+0A69;GURMUKHI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+0A6A;GURMUKHI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+0A6B;GURMUKHI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+0A6C;GURMUKHI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+0A6D;GURMUKHI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+0A6E;GURMUKHI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+0A6F;GURMUKHI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+0A70;GURMUKHI TIPPI;Mn;0;NSM;;;;;N;;;;;
+0A71;GURMUKHI ADDAK;Mn;0;NSM;;;;;N;;;;;
+0A72;GURMUKHI IRI;Lo;0;L;;;;;N;;;;;
+0A73;GURMUKHI URA;Lo;0;L;;;;;N;;;;;
+0A74;GURMUKHI EK ONKAR;Lo;0;L;;;;;N;;;;;
+0A81;GUJARATI SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;;
+0A82;GUJARATI SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;;
+0A83;GUJARATI SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+0A85;GUJARATI LETTER A;Lo;0;L;;;;;N;;;;;
+0A86;GUJARATI LETTER AA;Lo;0;L;;;;;N;;;;;
+0A87;GUJARATI LETTER I;Lo;0;L;;;;;N;;;;;
+0A88;GUJARATI LETTER II;Lo;0;L;;;;;N;;;;;
+0A89;GUJARATI LETTER U;Lo;0;L;;;;;N;;;;;
+0A8A;GUJARATI LETTER UU;Lo;0;L;;;;;N;;;;;
+0A8B;GUJARATI LETTER VOCALIC R;Lo;0;L;;;;;N;;;;;
+0A8C;GUJARATI LETTER VOCALIC L;Lo;0;L;;;;;N;;;;;
+0A8D;GUJARATI VOWEL CANDRA E;Lo;0;L;;;;;N;;;;;
+0A8F;GUJARATI LETTER E;Lo;0;L;;;;;N;;;;;
+0A90;GUJARATI LETTER AI;Lo;0;L;;;;;N;;;;;
+0A91;GUJARATI VOWEL CANDRA O;Lo;0;L;;;;;N;;;;;
+0A93;GUJARATI LETTER O;Lo;0;L;;;;;N;;;;;
+0A94;GUJARATI LETTER AU;Lo;0;L;;;;;N;;;;;
+0A95;GUJARATI LETTER KA;Lo;0;L;;;;;N;;;;;
+0A96;GUJARATI LETTER KHA;Lo;0;L;;;;;N;;;;;
+0A97;GUJARATI LETTER GA;Lo;0;L;;;;;N;;;;;
+0A98;GUJARATI LETTER GHA;Lo;0;L;;;;;N;;;;;
+0A99;GUJARATI LETTER NGA;Lo;0;L;;;;;N;;;;;
+0A9A;GUJARATI LETTER CA;Lo;0;L;;;;;N;;;;;
+0A9B;GUJARATI LETTER CHA;Lo;0;L;;;;;N;;;;;
+0A9C;GUJARATI LETTER JA;Lo;0;L;;;;;N;;;;;
+0A9D;GUJARATI LETTER JHA;Lo;0;L;;;;;N;;;;;
+0A9E;GUJARATI LETTER NYA;Lo;0;L;;;;;N;;;;;
+0A9F;GUJARATI LETTER TTA;Lo;0;L;;;;;N;;;;;
+0AA0;GUJARATI LETTER TTHA;Lo;0;L;;;;;N;;;;;
+0AA1;GUJARATI LETTER DDA;Lo;0;L;;;;;N;;;;;
+0AA2;GUJARATI LETTER DDHA;Lo;0;L;;;;;N;;;;;
+0AA3;GUJARATI LETTER NNA;Lo;0;L;;;;;N;;;;;
+0AA4;GUJARATI LETTER TA;Lo;0;L;;;;;N;;;;;
+0AA5;GUJARATI LETTER THA;Lo;0;L;;;;;N;;;;;
+0AA6;GUJARATI LETTER DA;Lo;0;L;;;;;N;;;;;
+0AA7;GUJARATI LETTER DHA;Lo;0;L;;;;;N;;;;;
+0AA8;GUJARATI LETTER NA;Lo;0;L;;;;;N;;;;;
+0AAA;GUJARATI LETTER PA;Lo;0;L;;;;;N;;;;;
+0AAB;GUJARATI LETTER PHA;Lo;0;L;;;;;N;;;;;
+0AAC;GUJARATI LETTER BA;Lo;0;L;;;;;N;;;;;
+0AAD;GUJARATI LETTER BHA;Lo;0;L;;;;;N;;;;;
+0AAE;GUJARATI LETTER MA;Lo;0;L;;;;;N;;;;;
+0AAF;GUJARATI LETTER YA;Lo;0;L;;;;;N;;;;;
+0AB0;GUJARATI LETTER RA;Lo;0;L;;;;;N;;;;;
+0AB2;GUJARATI LETTER LA;Lo;0;L;;;;;N;;;;;
+0AB3;GUJARATI LETTER LLA;Lo;0;L;;;;;N;;;;;
+0AB5;GUJARATI LETTER VA;Lo;0;L;;;;;N;;;;;
+0AB6;GUJARATI LETTER SHA;Lo;0;L;;;;;N;;;;;
+0AB7;GUJARATI LETTER SSA;Lo;0;L;;;;;N;;;;;
+0AB8;GUJARATI LETTER SA;Lo;0;L;;;;;N;;;;;
+0AB9;GUJARATI LETTER HA;Lo;0;L;;;;;N;;;;;
+0ABC;GUJARATI SIGN NUKTA;Mn;7;NSM;;;;;N;;;;;
+0ABD;GUJARATI SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;;
+0ABE;GUJARATI VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+0ABF;GUJARATI VOWEL SIGN I;Mc;0;L;;;;;N;;;;;
+0AC0;GUJARATI VOWEL SIGN II;Mc;0;L;;;;;N;;;;;
+0AC1;GUJARATI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+0AC2;GUJARATI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+0AC3;GUJARATI VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;;
+0AC4;GUJARATI VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;;
+0AC5;GUJARATI VOWEL SIGN CANDRA E;Mn;0;NSM;;;;;N;;;;;
+0AC7;GUJARATI VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;;
+0AC8;GUJARATI VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;;
+0AC9;GUJARATI VOWEL SIGN CANDRA O;Mc;0;L;;;;;N;;;;;
+0ACB;GUJARATI VOWEL SIGN O;Mc;0;L;;;;;N;;;;;
+0ACC;GUJARATI VOWEL SIGN AU;Mc;0;L;;;;;N;;;;;
+0ACD;GUJARATI SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+0AD0;GUJARATI OM;Lo;0;L;;;;;N;;;;;
+0AE0;GUJARATI LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;;
+0AE1;GUJARATI LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;;
+0AE2;GUJARATI VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;;
+0AE3;GUJARATI VOWEL SIGN VOCALIC LL;Mn;0;NSM;;;;;N;;;;;
+0AE6;GUJARATI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+0AE7;GUJARATI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+0AE8;GUJARATI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+0AE9;GUJARATI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+0AEA;GUJARATI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+0AEB;GUJARATI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+0AEC;GUJARATI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+0AED;GUJARATI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+0AEE;GUJARATI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+0AEF;GUJARATI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+0AF1;GUJARATI RUPEE SIGN;Sc;0;ET;;;;;N;;;;;
+0B01;ORIYA SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;;
+0B02;ORIYA SIGN ANUSVARA;Mc;0;L;;;;;N;;;;;
+0B03;ORIYA SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+0B05;ORIYA LETTER A;Lo;0;L;;;;;N;;;;;
+0B06;ORIYA LETTER AA;Lo;0;L;;;;;N;;;;;
+0B07;ORIYA LETTER I;Lo;0;L;;;;;N;;;;;
+0B08;ORIYA LETTER II;Lo;0;L;;;;;N;;;;;
+0B09;ORIYA LETTER U;Lo;0;L;;;;;N;;;;;
+0B0A;ORIYA LETTER UU;Lo;0;L;;;;;N;;;;;
+0B0B;ORIYA LETTER VOCALIC R;Lo;0;L;;;;;N;;;;;
+0B0C;ORIYA LETTER VOCALIC L;Lo;0;L;;;;;N;;;;;
+0B0F;ORIYA LETTER E;Lo;0;L;;;;;N;;;;;
+0B10;ORIYA LETTER AI;Lo;0;L;;;;;N;;;;;
+0B13;ORIYA LETTER O;Lo;0;L;;;;;N;;;;;
+0B14;ORIYA LETTER AU;Lo;0;L;;;;;N;;;;;
+0B15;ORIYA LETTER KA;Lo;0;L;;;;;N;;;;;
+0B16;ORIYA LETTER KHA;Lo;0;L;;;;;N;;;;;
+0B17;ORIYA LETTER GA;Lo;0;L;;;;;N;;;;;
+0B18;ORIYA LETTER GHA;Lo;0;L;;;;;N;;;;;
+0B19;ORIYA LETTER NGA;Lo;0;L;;;;;N;;;;;
+0B1A;ORIYA LETTER CA;Lo;0;L;;;;;N;;;;;
+0B1B;ORIYA LETTER CHA;Lo;0;L;;;;;N;;;;;
+0B1C;ORIYA LETTER JA;Lo;0;L;;;;;N;;;;;
+0B1D;ORIYA LETTER JHA;Lo;0;L;;;;;N;;;;;
+0B1E;ORIYA LETTER NYA;Lo;0;L;;;;;N;;;;;
+0B1F;ORIYA LETTER TTA;Lo;0;L;;;;;N;;;;;
+0B20;ORIYA LETTER TTHA;Lo;0;L;;;;;N;;;;;
+0B21;ORIYA LETTER DDA;Lo;0;L;;;;;N;;;;;
+0B22;ORIYA LETTER DDHA;Lo;0;L;;;;;N;;;;;
+0B23;ORIYA LETTER NNA;Lo;0;L;;;;;N;;;;;
+0B24;ORIYA LETTER TA;Lo;0;L;;;;;N;;;;;
+0B25;ORIYA LETTER THA;Lo;0;L;;;;;N;;;;;
+0B26;ORIYA LETTER DA;Lo;0;L;;;;;N;;;;;
+0B27;ORIYA LETTER DHA;Lo;0;L;;;;;N;;;;;
+0B28;ORIYA LETTER NA;Lo;0;L;;;;;N;;;;;
+0B2A;ORIYA LETTER PA;Lo;0;L;;;;;N;;;;;
+0B2B;ORIYA LETTER PHA;Lo;0;L;;;;;N;;;;;
+0B2C;ORIYA LETTER BA;Lo;0;L;;;;;N;;;;;
+0B2D;ORIYA LETTER BHA;Lo;0;L;;;;;N;;;;;
+0B2E;ORIYA LETTER MA;Lo;0;L;;;;;N;;;;;
+0B2F;ORIYA LETTER YA;Lo;0;L;;;;;N;;;;;
+0B30;ORIYA LETTER RA;Lo;0;L;;;;;N;;;;;
+0B32;ORIYA LETTER LA;Lo;0;L;;;;;N;;;;;
+0B33;ORIYA LETTER LLA;Lo;0;L;;;;;N;;;;;
+0B35;ORIYA LETTER VA;Lo;0;L;;;;;N;;;;;
+0B36;ORIYA LETTER SHA;Lo;0;L;;;;;N;;;;;
+0B37;ORIYA LETTER SSA;Lo;0;L;;;;;N;;;;;
+0B38;ORIYA LETTER SA;Lo;0;L;;;;;N;;;;;
+0B39;ORIYA LETTER HA;Lo;0;L;;;;;N;;;;;
+0B3C;ORIYA SIGN NUKTA;Mn;7;NSM;;;;;N;;;;;
+0B3D;ORIYA SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;;
+0B3E;ORIYA VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+0B3F;ORIYA VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
+0B40;ORIYA VOWEL SIGN II;Mc;0;L;;;;;N;;;;;
+0B41;ORIYA VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+0B42;ORIYA VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+0B43;ORIYA VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;;
+0B47;ORIYA VOWEL SIGN E;Mc;0;L;;;;;N;;;;;
+0B48;ORIYA VOWEL SIGN AI;Mc;0;L;0B47 0B56;;;;N;;;;;
+0B4B;ORIYA VOWEL SIGN O;Mc;0;L;0B47 0B3E;;;;N;;;;;
+0B4C;ORIYA VOWEL SIGN AU;Mc;0;L;0B47 0B57;;;;N;;;;;
+0B4D;ORIYA SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+0B56;ORIYA AI LENGTH MARK;Mn;0;NSM;;;;;N;;;;;
+0B57;ORIYA AU LENGTH MARK;Mc;0;L;;;;;N;;;;;
+0B5C;ORIYA LETTER RRA;Lo;0;L;0B21 0B3C;;;;N;;;;;
+0B5D;ORIYA LETTER RHA;Lo;0;L;0B22 0B3C;;;;N;;;;;
+0B5F;ORIYA LETTER YYA;Lo;0;L;;;;;N;;;;;
+0B60;ORIYA LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;;
+0B61;ORIYA LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;;
+0B66;ORIYA DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+0B67;ORIYA DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+0B68;ORIYA DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+0B69;ORIYA DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+0B6A;ORIYA DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+0B6B;ORIYA DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+0B6C;ORIYA DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+0B6D;ORIYA DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+0B6E;ORIYA DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+0B6F;ORIYA DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+0B70;ORIYA ISSHAR;So;0;L;;;;;N;;;;;
+0B71;ORIYA LETTER WA;Lo;0;L;;;;;N;;;;;
+0B82;TAMIL SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;;
+0B83;TAMIL SIGN VISARGA;Lo;0;L;;;;;N;;;;;
+0B85;TAMIL LETTER A;Lo;0;L;;;;;N;;;;;
+0B86;TAMIL LETTER AA;Lo;0;L;;;;;N;;;;;
+0B87;TAMIL LETTER I;Lo;0;L;;;;;N;;;;;
+0B88;TAMIL LETTER II;Lo;0;L;;;;;N;;;;;
+0B89;TAMIL LETTER U;Lo;0;L;;;;;N;;;;;
+0B8A;TAMIL LETTER UU;Lo;0;L;;;;;N;;;;;
+0B8E;TAMIL LETTER E;Lo;0;L;;;;;N;;;;;
+0B8F;TAMIL LETTER EE;Lo;0;L;;;;;N;;;;;
+0B90;TAMIL LETTER AI;Lo;0;L;;;;;N;;;;;
+0B92;TAMIL LETTER O;Lo;0;L;;;;;N;;;;;
+0B93;TAMIL LETTER OO;Lo;0;L;;;;;N;;;;;
+0B94;TAMIL LETTER AU;Lo;0;L;0B92 0BD7;;;;N;;;;;
+0B95;TAMIL LETTER KA;Lo;0;L;;;;;N;;;;;
+0B99;TAMIL LETTER NGA;Lo;0;L;;;;;N;;;;;
+0B9A;TAMIL LETTER CA;Lo;0;L;;;;;N;;;;;
+0B9C;TAMIL LETTER JA;Lo;0;L;;;;;N;;;;;
+0B9E;TAMIL LETTER NYA;Lo;0;L;;;;;N;;;;;
+0B9F;TAMIL LETTER TTA;Lo;0;L;;;;;N;;;;;
+0BA3;TAMIL LETTER NNA;Lo;0;L;;;;;N;;;;;
+0BA4;TAMIL LETTER TA;Lo;0;L;;;;;N;;;;;
+0BA8;TAMIL LETTER NA;Lo;0;L;;;;;N;;;;;
+0BA9;TAMIL LETTER NNNA;Lo;0;L;;;;;N;;;;;
+0BAA;TAMIL LETTER PA;Lo;0;L;;;;;N;;;;;
+0BAE;TAMIL LETTER MA;Lo;0;L;;;;;N;;;;;
+0BAF;TAMIL LETTER YA;Lo;0;L;;;;;N;;;;;
+0BB0;TAMIL LETTER RA;Lo;0;L;;;;;N;;;;;
+0BB1;TAMIL LETTER RRA;Lo;0;L;;;;;N;;;;;
+0BB2;TAMIL LETTER LA;Lo;0;L;;;;;N;;;;;
+0BB3;TAMIL LETTER LLA;Lo;0;L;;;;;N;;;;;
+0BB4;TAMIL LETTER LLLA;Lo;0;L;;;;;N;;;;;
+0BB5;TAMIL LETTER VA;Lo;0;L;;;;;N;;;;;
+0BB7;TAMIL LETTER SSA;Lo;0;L;;;;;N;;;;;
+0BB8;TAMIL LETTER SA;Lo;0;L;;;;;N;;;;;
+0BB9;TAMIL LETTER HA;Lo;0;L;;;;;N;;;;;
+0BBE;TAMIL VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+0BBF;TAMIL VOWEL SIGN I;Mc;0;L;;;;;N;;;;;
+0BC0;TAMIL VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;;
+0BC1;TAMIL VOWEL SIGN U;Mc;0;L;;;;;N;;;;;
+0BC2;TAMIL VOWEL SIGN UU;Mc;0;L;;;;;N;;;;;
+0BC6;TAMIL VOWEL SIGN E;Mc;0;L;;;;;N;;;;;
+0BC7;TAMIL VOWEL SIGN EE;Mc;0;L;;;;;N;;;;;
+0BC8;TAMIL VOWEL SIGN AI;Mc;0;L;;;;;N;;;;;
+0BCA;TAMIL VOWEL SIGN O;Mc;0;L;0BC6 0BBE;;;;N;;;;;
+0BCB;TAMIL VOWEL SIGN OO;Mc;0;L;0BC7 0BBE;;;;N;;;;;
+0BCC;TAMIL VOWEL SIGN AU;Mc;0;L;0BC6 0BD7;;;;N;;;;;
+0BCD;TAMIL SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+0BD7;TAMIL AU LENGTH MARK;Mc;0;L;;;;;N;;;;;
+0BE7;TAMIL DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+0BE8;TAMIL DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+0BE9;TAMIL DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+0BEA;TAMIL DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+0BEB;TAMIL DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+0BEC;TAMIL DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+0BED;TAMIL DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+0BEE;TAMIL DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+0BEF;TAMIL DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+0BF0;TAMIL NUMBER TEN;No;0;L;;;;10;N;;;;;
+0BF1;TAMIL NUMBER ONE HUNDRED;No;0;L;;;;100;N;;;;;
+0BF2;TAMIL NUMBER ONE THOUSAND;No;0;L;;;;1000;N;;;;;
+0BF3;TAMIL DAY SIGN;So;0;ON;;;;;N;;Naal;;;
+0BF4;TAMIL MONTH SIGN;So;0;ON;;;;;N;;Maatham;;;
+0BF5;TAMIL YEAR SIGN;So;0;ON;;;;;N;;Varudam;;;
+0BF6;TAMIL DEBIT SIGN;So;0;ON;;;;;N;;Patru;;;
+0BF7;TAMIL CREDIT SIGN;So;0;ON;;;;;N;;Varavu;;;
+0BF8;TAMIL AS ABOVE SIGN;So;0;ON;;;;;N;;Merpadi;;;
+0BF9;TAMIL RUPEE SIGN;Sc;0;ET;;;;;N;;Rupai;;;
+0BFA;TAMIL NUMBER SIGN;So;0;ON;;;;;N;;Enn;;;
+0C01;TELUGU SIGN CANDRABINDU;Mc;0;L;;;;;N;;;;;
+0C02;TELUGU SIGN ANUSVARA;Mc;0;L;;;;;N;;;;;
+0C03;TELUGU SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+0C05;TELUGU LETTER A;Lo;0;L;;;;;N;;;;;
+0C06;TELUGU LETTER AA;Lo;0;L;;;;;N;;;;;
+0C07;TELUGU LETTER I;Lo;0;L;;;;;N;;;;;
+0C08;TELUGU LETTER II;Lo;0;L;;;;;N;;;;;
+0C09;TELUGU LETTER U;Lo;0;L;;;;;N;;;;;
+0C0A;TELUGU LETTER UU;Lo;0;L;;;;;N;;;;;
+0C0B;TELUGU LETTER VOCALIC R;Lo;0;L;;;;;N;;;;;
+0C0C;TELUGU LETTER VOCALIC L;Lo;0;L;;;;;N;;;;;
+0C0E;TELUGU LETTER E;Lo;0;L;;;;;N;;;;;
+0C0F;TELUGU LETTER EE;Lo;0;L;;;;;N;;;;;
+0C10;TELUGU LETTER AI;Lo;0;L;;;;;N;;;;;
+0C12;TELUGU LETTER O;Lo;0;L;;;;;N;;;;;
+0C13;TELUGU LETTER OO;Lo;0;L;;;;;N;;;;;
+0C14;TELUGU LETTER AU;Lo;0;L;;;;;N;;;;;
+0C15;TELUGU LETTER KA;Lo;0;L;;;;;N;;;;;
+0C16;TELUGU LETTER KHA;Lo;0;L;;;;;N;;;;;
+0C17;TELUGU LETTER GA;Lo;0;L;;;;;N;;;;;
+0C18;TELUGU LETTER GHA;Lo;0;L;;;;;N;;;;;
+0C19;TELUGU LETTER NGA;Lo;0;L;;;;;N;;;;;
+0C1A;TELUGU LETTER CA;Lo;0;L;;;;;N;;;;;
+0C1B;TELUGU LETTER CHA;Lo;0;L;;;;;N;;;;;
+0C1C;TELUGU LETTER JA;Lo;0;L;;;;;N;;;;;
+0C1D;TELUGU LETTER JHA;Lo;0;L;;;;;N;;;;;
+0C1E;TELUGU LETTER NYA;Lo;0;L;;;;;N;;;;;
+0C1F;TELUGU LETTER TTA;Lo;0;L;;;;;N;;;;;
+0C20;TELUGU LETTER TTHA;Lo;0;L;;;;;N;;;;;
+0C21;TELUGU LETTER DDA;Lo;0;L;;;;;N;;;;;
+0C22;TELUGU LETTER DDHA;Lo;0;L;;;;;N;;;;;
+0C23;TELUGU LETTER NNA;Lo;0;L;;;;;N;;;;;
+0C24;TELUGU LETTER TA;Lo;0;L;;;;;N;;;;;
+0C25;TELUGU LETTER THA;Lo;0;L;;;;;N;;;;;
+0C26;TELUGU LETTER DA;Lo;0;L;;;;;N;;;;;
+0C27;TELUGU LETTER DHA;Lo;0;L;;;;;N;;;;;
+0C28;TELUGU LETTER NA;Lo;0;L;;;;;N;;;;;
+0C2A;TELUGU LETTER PA;Lo;0;L;;;;;N;;;;;
+0C2B;TELUGU LETTER PHA;Lo;0;L;;;;;N;;;;;
+0C2C;TELUGU LETTER BA;Lo;0;L;;;;;N;;;;;
+0C2D;TELUGU LETTER BHA;Lo;0;L;;;;;N;;;;;
+0C2E;TELUGU LETTER MA;Lo;0;L;;;;;N;;;;;
+0C2F;TELUGU LETTER YA;Lo;0;L;;;;;N;;;;;
+0C30;TELUGU LETTER RA;Lo;0;L;;;;;N;;;;;
+0C31;TELUGU LETTER RRA;Lo;0;L;;;;;N;;;;;
+0C32;TELUGU LETTER LA;Lo;0;L;;;;;N;;;;;
+0C33;TELUGU LETTER LLA;Lo;0;L;;;;;N;;;;;
+0C35;TELUGU LETTER VA;Lo;0;L;;;;;N;;;;;
+0C36;TELUGU LETTER SHA;Lo;0;L;;;;;N;;;;;
+0C37;TELUGU LETTER SSA;Lo;0;L;;;;;N;;;;;
+0C38;TELUGU LETTER SA;Lo;0;L;;;;;N;;;;;
+0C39;TELUGU LETTER HA;Lo;0;L;;;;;N;;;;;
+0C3E;TELUGU VOWEL SIGN AA;Mn;0;NSM;;;;;N;;;;;
+0C3F;TELUGU VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
+0C40;TELUGU VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;;
+0C41;TELUGU VOWEL SIGN U;Mc;0;L;;;;;N;;;;;
+0C42;TELUGU VOWEL SIGN UU;Mc;0;L;;;;;N;;;;;
+0C43;TELUGU VOWEL SIGN VOCALIC R;Mc;0;L;;;;;N;;;;;
+0C44;TELUGU VOWEL SIGN VOCALIC RR;Mc;0;L;;;;;N;;;;;
+0C46;TELUGU VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;;
+0C47;TELUGU VOWEL SIGN EE;Mn;0;NSM;;;;;N;;;;;
+0C48;TELUGU VOWEL SIGN AI;Mn;0;NSM;0C46 0C56;;;;N;;;;;
+0C4A;TELUGU VOWEL SIGN O;Mn;0;NSM;;;;;N;;;;;
+0C4B;TELUGU VOWEL SIGN OO;Mn;0;NSM;;;;;N;;;;;
+0C4C;TELUGU VOWEL SIGN AU;Mn;0;NSM;;;;;N;;;;;
+0C4D;TELUGU SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+0C55;TELUGU LENGTH MARK;Mn;84;NSM;;;;;N;;;;;
+0C56;TELUGU AI LENGTH MARK;Mn;91;NSM;;;;;N;;;;;
+0C60;TELUGU LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;;
+0C61;TELUGU LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;;
+0C66;TELUGU DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+0C67;TELUGU DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+0C68;TELUGU DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+0C69;TELUGU DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+0C6A;TELUGU DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+0C6B;TELUGU DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+0C6C;TELUGU DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+0C6D;TELUGU DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+0C6E;TELUGU DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+0C6F;TELUGU DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+0C82;KANNADA SIGN ANUSVARA;Mc;0;L;;;;;N;;;;;
+0C83;KANNADA SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+0C85;KANNADA LETTER A;Lo;0;L;;;;;N;;;;;
+0C86;KANNADA LETTER AA;Lo;0;L;;;;;N;;;;;
+0C87;KANNADA LETTER I;Lo;0;L;;;;;N;;;;;
+0C88;KANNADA LETTER II;Lo;0;L;;;;;N;;;;;
+0C89;KANNADA LETTER U;Lo;0;L;;;;;N;;;;;
+0C8A;KANNADA LETTER UU;Lo;0;L;;;;;N;;;;;
+0C8B;KANNADA LETTER VOCALIC R;Lo;0;L;;;;;N;;;;;
+0C8C;KANNADA LETTER VOCALIC L;Lo;0;L;;;;;N;;;;;
+0C8E;KANNADA LETTER E;Lo;0;L;;;;;N;;;;;
+0C8F;KANNADA LETTER EE;Lo;0;L;;;;;N;;;;;
+0C90;KANNADA LETTER AI;Lo;0;L;;;;;N;;;;;
+0C92;KANNADA LETTER O;Lo;0;L;;;;;N;;;;;
+0C93;KANNADA LETTER OO;Lo;0;L;;;;;N;;;;;
+0C94;KANNADA LETTER AU;Lo;0;L;;;;;N;;;;;
+0C95;KANNADA LETTER KA;Lo;0;L;;;;;N;;;;;
+0C96;KANNADA LETTER KHA;Lo;0;L;;;;;N;;;;;
+0C97;KANNADA LETTER GA;Lo;0;L;;;;;N;;;;;
+0C98;KANNADA LETTER GHA;Lo;0;L;;;;;N;;;;;
+0C99;KANNADA LETTER NGA;Lo;0;L;;;;;N;;;;;
+0C9A;KANNADA LETTER CA;Lo;0;L;;;;;N;;;;;
+0C9B;KANNADA LETTER CHA;Lo;0;L;;;;;N;;;;;
+0C9C;KANNADA LETTER JA;Lo;0;L;;;;;N;;;;;
+0C9D;KANNADA LETTER JHA;Lo;0;L;;;;;N;;;;;
+0C9E;KANNADA LETTER NYA;Lo;0;L;;;;;N;;;;;
+0C9F;KANNADA LETTER TTA;Lo;0;L;;;;;N;;;;;
+0CA0;KANNADA LETTER TTHA;Lo;0;L;;;;;N;;;;;
+0CA1;KANNADA LETTER DDA;Lo;0;L;;;;;N;;;;;
+0CA2;KANNADA LETTER DDHA;Lo;0;L;;;;;N;;;;;
+0CA3;KANNADA LETTER NNA;Lo;0;L;;;;;N;;;;;
+0CA4;KANNADA LETTER TA;Lo;0;L;;;;;N;;;;;
+0CA5;KANNADA LETTER THA;Lo;0;L;;;;;N;;;;;
+0CA6;KANNADA LETTER DA;Lo;0;L;;;;;N;;;;;
+0CA7;KANNADA LETTER DHA;Lo;0;L;;;;;N;;;;;
+0CA8;KANNADA LETTER NA;Lo;0;L;;;;;N;;;;;
+0CAA;KANNADA LETTER PA;Lo;0;L;;;;;N;;;;;
+0CAB;KANNADA LETTER PHA;Lo;0;L;;;;;N;;;;;
+0CAC;KANNADA LETTER BA;Lo;0;L;;;;;N;;;;;
+0CAD;KANNADA LETTER BHA;Lo;0;L;;;;;N;;;;;
+0CAE;KANNADA LETTER MA;Lo;0;L;;;;;N;;;;;
+0CAF;KANNADA LETTER YA;Lo;0;L;;;;;N;;;;;
+0CB0;KANNADA LETTER RA;Lo;0;L;;;;;N;;;;;
+0CB1;KANNADA LETTER RRA;Lo;0;L;;;;;N;;;;;
+0CB2;KANNADA LETTER LA;Lo;0;L;;;;;N;;;;;
+0CB3;KANNADA LETTER LLA;Lo;0;L;;;;;N;;;;;
+0CB5;KANNADA LETTER VA;Lo;0;L;;;;;N;;;;;
+0CB6;KANNADA LETTER SHA;Lo;0;L;;;;;N;;;;;
+0CB7;KANNADA LETTER SSA;Lo;0;L;;;;;N;;;;;
+0CB8;KANNADA LETTER SA;Lo;0;L;;;;;N;;;;;
+0CB9;KANNADA LETTER HA;Lo;0;L;;;;;N;;;;;
+0CBC;KANNADA SIGN NUKTA;Mn;7;NSM;;;;;N;;;;;
+0CBD;KANNADA SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;;
+0CBE;KANNADA VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+0CBF;KANNADA VOWEL SIGN I;Mn;0;L;;;;;N;;;;;
+0CC0;KANNADA VOWEL SIGN II;Mc;0;L;0CBF 0CD5;;;;N;;;;;
+0CC1;KANNADA VOWEL SIGN U;Mc;0;L;;;;;N;;;;;
+0CC2;KANNADA VOWEL SIGN UU;Mc;0;L;;;;;N;;;;;
+0CC3;KANNADA VOWEL SIGN VOCALIC R;Mc;0;L;;;;;N;;;;;
+0CC4;KANNADA VOWEL SIGN VOCALIC RR;Mc;0;L;;;;;N;;;;;
+0CC6;KANNADA VOWEL SIGN E;Mn;0;L;;;;;N;;;;;
+0CC7;KANNADA VOWEL SIGN EE;Mc;0;L;0CC6 0CD5;;;;N;;;;;
+0CC8;KANNADA VOWEL SIGN AI;Mc;0;L;0CC6 0CD6;;;;N;;;;;
+0CCA;KANNADA VOWEL SIGN O;Mc;0;L;0CC6 0CC2;;;;N;;;;;
+0CCB;KANNADA VOWEL SIGN OO;Mc;0;L;0CCA 0CD5;;;;N;;;;;
+0CCC;KANNADA VOWEL SIGN AU;Mn;0;NSM;;;;;N;;;;;
+0CCD;KANNADA SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+0CD5;KANNADA LENGTH MARK;Mc;0;L;;;;;N;;;;;
+0CD6;KANNADA AI LENGTH MARK;Mc;0;L;;;;;N;;;;;
+0CDE;KANNADA LETTER FA;Lo;0;L;;;;;N;;;;;
+0CE0;KANNADA LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;;
+0CE1;KANNADA LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;;
+0CE6;KANNADA DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+0CE7;KANNADA DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+0CE8;KANNADA DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+0CE9;KANNADA DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+0CEA;KANNADA DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+0CEB;KANNADA DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+0CEC;KANNADA DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+0CED;KANNADA DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+0CEE;KANNADA DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+0CEF;KANNADA DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+0D02;MALAYALAM SIGN ANUSVARA;Mc;0;L;;;;;N;;;;;
+0D03;MALAYALAM SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+0D05;MALAYALAM LETTER A;Lo;0;L;;;;;N;;;;;
+0D06;MALAYALAM LETTER AA;Lo;0;L;;;;;N;;;;;
+0D07;MALAYALAM LETTER I;Lo;0;L;;;;;N;;;;;
+0D08;MALAYALAM LETTER II;Lo;0;L;;;;;N;;;;;
+0D09;MALAYALAM LETTER U;Lo;0;L;;;;;N;;;;;
+0D0A;MALAYALAM LETTER UU;Lo;0;L;;;;;N;;;;;
+0D0B;MALAYALAM LETTER VOCALIC R;Lo;0;L;;;;;N;;;;;
+0D0C;MALAYALAM LETTER VOCALIC L;Lo;0;L;;;;;N;;;;;
+0D0E;MALAYALAM LETTER E;Lo;0;L;;;;;N;;;;;
+0D0F;MALAYALAM LETTER EE;Lo;0;L;;;;;N;;;;;
+0D10;MALAYALAM LETTER AI;Lo;0;L;;;;;N;;;;;
+0D12;MALAYALAM LETTER O;Lo;0;L;;;;;N;;;;;
+0D13;MALAYALAM LETTER OO;Lo;0;L;;;;;N;;;;;
+0D14;MALAYALAM LETTER AU;Lo;0;L;;;;;N;;;;;
+0D15;MALAYALAM LETTER KA;Lo;0;L;;;;;N;;;;;
+0D16;MALAYALAM LETTER KHA;Lo;0;L;;;;;N;;;;;
+0D17;MALAYALAM LETTER GA;Lo;0;L;;;;;N;;;;;
+0D18;MALAYALAM LETTER GHA;Lo;0;L;;;;;N;;;;;
+0D19;MALAYALAM LETTER NGA;Lo;0;L;;;;;N;;;;;
+0D1A;MALAYALAM LETTER CA;Lo;0;L;;;;;N;;;;;
+0D1B;MALAYALAM LETTER CHA;Lo;0;L;;;;;N;;;;;
+0D1C;MALAYALAM LETTER JA;Lo;0;L;;;;;N;;;;;
+0D1D;MALAYALAM LETTER JHA;Lo;0;L;;;;;N;;;;;
+0D1E;MALAYALAM LETTER NYA;Lo;0;L;;;;;N;;;;;
+0D1F;MALAYALAM LETTER TTA;Lo;0;L;;;;;N;;;;;
+0D20;MALAYALAM LETTER TTHA;Lo;0;L;;;;;N;;;;;
+0D21;MALAYALAM LETTER DDA;Lo;0;L;;;;;N;;;;;
+0D22;MALAYALAM LETTER DDHA;Lo;0;L;;;;;N;;;;;
+0D23;MALAYALAM LETTER NNA;Lo;0;L;;;;;N;;;;;
+0D24;MALAYALAM LETTER TA;Lo;0;L;;;;;N;;;;;
+0D25;MALAYALAM LETTER THA;Lo;0;L;;;;;N;;;;;
+0D26;MALAYALAM LETTER DA;Lo;0;L;;;;;N;;;;;
+0D27;MALAYALAM LETTER DHA;Lo;0;L;;;;;N;;;;;
+0D28;MALAYALAM LETTER NA;Lo;0;L;;;;;N;;;;;
+0D2A;MALAYALAM LETTER PA;Lo;0;L;;;;;N;;;;;
+0D2B;MALAYALAM LETTER PHA;Lo;0;L;;;;;N;;;;;
+0D2C;MALAYALAM LETTER BA;Lo;0;L;;;;;N;;;;;
+0D2D;MALAYALAM LETTER BHA;Lo;0;L;;;;;N;;;;;
+0D2E;MALAYALAM LETTER MA;Lo;0;L;;;;;N;;;;;
+0D2F;MALAYALAM LETTER YA;Lo;0;L;;;;;N;;;;;
+0D30;MALAYALAM LETTER RA;Lo;0;L;;;;;N;;;;;
+0D31;MALAYALAM LETTER RRA;Lo;0;L;;;;;N;;;;;
+0D32;MALAYALAM LETTER LA;Lo;0;L;;;;;N;;;;;
+0D33;MALAYALAM LETTER LLA;Lo;0;L;;;;;N;;;;;
+0D34;MALAYALAM LETTER LLLA;Lo;0;L;;;;;N;;;;;
+0D35;MALAYALAM LETTER VA;Lo;0;L;;;;;N;;;;;
+0D36;MALAYALAM LETTER SHA;Lo;0;L;;;;;N;;;;;
+0D37;MALAYALAM LETTER SSA;Lo;0;L;;;;;N;;;;;
+0D38;MALAYALAM LETTER SA;Lo;0;L;;;;;N;;;;;
+0D39;MALAYALAM LETTER HA;Lo;0;L;;;;;N;;;;;
+0D3E;MALAYALAM VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+0D3F;MALAYALAM VOWEL SIGN I;Mc;0;L;;;;;N;;;;;
+0D40;MALAYALAM VOWEL SIGN II;Mc;0;L;;;;;N;;;;;
+0D41;MALAYALAM VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+0D42;MALAYALAM VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+0D43;MALAYALAM VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;;
+0D46;MALAYALAM VOWEL SIGN E;Mc;0;L;;;;;N;;;;;
+0D47;MALAYALAM VOWEL SIGN EE;Mc;0;L;;;;;N;;;;;
+0D48;MALAYALAM VOWEL SIGN AI;Mc;0;L;;;;;N;;;;;
+0D4A;MALAYALAM VOWEL SIGN O;Mc;0;L;0D46 0D3E;;;;N;;;;;
+0D4B;MALAYALAM VOWEL SIGN OO;Mc;0;L;0D47 0D3E;;;;N;;;;;
+0D4C;MALAYALAM VOWEL SIGN AU;Mc;0;L;0D46 0D57;;;;N;;;;;
+0D4D;MALAYALAM SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+0D57;MALAYALAM AU LENGTH MARK;Mc;0;L;;;;;N;;;;;
+0D60;MALAYALAM LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;;
+0D61;MALAYALAM LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;;
+0D66;MALAYALAM DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+0D67;MALAYALAM DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+0D68;MALAYALAM DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+0D69;MALAYALAM DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+0D6A;MALAYALAM DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+0D6B;MALAYALAM DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+0D6C;MALAYALAM DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+0D6D;MALAYALAM DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+0D6E;MALAYALAM DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+0D6F;MALAYALAM DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+0D82;SINHALA SIGN ANUSVARAYA;Mc;0;L;;;;;N;;;;;
+0D83;SINHALA SIGN VISARGAYA;Mc;0;L;;;;;N;;;;;
+0D85;SINHALA LETTER AYANNA;Lo;0;L;;;;;N;;;;;
+0D86;SINHALA LETTER AAYANNA;Lo;0;L;;;;;N;;;;;
+0D87;SINHALA LETTER AEYANNA;Lo;0;L;;;;;N;;;;;
+0D88;SINHALA LETTER AEEYANNA;Lo;0;L;;;;;N;;;;;
+0D89;SINHALA LETTER IYANNA;Lo;0;L;;;;;N;;;;;
+0D8A;SINHALA LETTER IIYANNA;Lo;0;L;;;;;N;;;;;
+0D8B;SINHALA LETTER UYANNA;Lo;0;L;;;;;N;;;;;
+0D8C;SINHALA LETTER UUYANNA;Lo;0;L;;;;;N;;;;;
+0D8D;SINHALA LETTER IRUYANNA;Lo;0;L;;;;;N;;;;;
+0D8E;SINHALA LETTER IRUUYANNA;Lo;0;L;;;;;N;;;;;
+0D8F;SINHALA LETTER ILUYANNA;Lo;0;L;;;;;N;;;;;
+0D90;SINHALA LETTER ILUUYANNA;Lo;0;L;;;;;N;;;;;
+0D91;SINHALA LETTER EYANNA;Lo;0;L;;;;;N;;;;;
+0D92;SINHALA LETTER EEYANNA;Lo;0;L;;;;;N;;;;;
+0D93;SINHALA LETTER AIYANNA;Lo;0;L;;;;;N;;;;;
+0D94;SINHALA LETTER OYANNA;Lo;0;L;;;;;N;;;;;
+0D95;SINHALA LETTER OOYANNA;Lo;0;L;;;;;N;;;;;
+0D96;SINHALA LETTER AUYANNA;Lo;0;L;;;;;N;;;;;
+0D9A;SINHALA LETTER ALPAPRAANA KAYANNA;Lo;0;L;;;;;N;;;;;
+0D9B;SINHALA LETTER MAHAAPRAANA KAYANNA;Lo;0;L;;;;;N;;;;;
+0D9C;SINHALA LETTER ALPAPRAANA GAYANNA;Lo;0;L;;;;;N;;;;;
+0D9D;SINHALA LETTER MAHAAPRAANA GAYANNA;Lo;0;L;;;;;N;;;;;
+0D9E;SINHALA LETTER KANTAJA NAASIKYAYA;Lo;0;L;;;;;N;;;;;
+0D9F;SINHALA LETTER SANYAKA GAYANNA;Lo;0;L;;;;;N;;;;;
+0DA0;SINHALA LETTER ALPAPRAANA CAYANNA;Lo;0;L;;;;;N;;;;;
+0DA1;SINHALA LETTER MAHAAPRAANA CAYANNA;Lo;0;L;;;;;N;;;;;
+0DA2;SINHALA LETTER ALPAPRAANA JAYANNA;Lo;0;L;;;;;N;;;;;
+0DA3;SINHALA LETTER MAHAAPRAANA JAYANNA;Lo;0;L;;;;;N;;;;;
+0DA4;SINHALA LETTER TAALUJA NAASIKYAYA;Lo;0;L;;;;;N;;;;;
+0DA5;SINHALA LETTER TAALUJA SANYOOGA NAAKSIKYAYA;Lo;0;L;;;;;N;;;;;
+0DA6;SINHALA LETTER SANYAKA JAYANNA;Lo;0;L;;;;;N;;;;;
+0DA7;SINHALA LETTER ALPAPRAANA TTAYANNA;Lo;0;L;;;;;N;;;;;
+0DA8;SINHALA LETTER MAHAAPRAANA TTAYANNA;Lo;0;L;;;;;N;;;;;
+0DA9;SINHALA LETTER ALPAPRAANA DDAYANNA;Lo;0;L;;;;;N;;;;;
+0DAA;SINHALA LETTER MAHAAPRAANA DDAYANNA;Lo;0;L;;;;;N;;;;;
+0DAB;SINHALA LETTER MUURDHAJA NAYANNA;Lo;0;L;;;;;N;;;;;
+0DAC;SINHALA LETTER SANYAKA DDAYANNA;Lo;0;L;;;;;N;;;;;
+0DAD;SINHALA LETTER ALPAPRAANA TAYANNA;Lo;0;L;;;;;N;;;;;
+0DAE;SINHALA LETTER MAHAAPRAANA TAYANNA;Lo;0;L;;;;;N;;;;;
+0DAF;SINHALA LETTER ALPAPRAANA DAYANNA;Lo;0;L;;;;;N;;;;;
+0DB0;SINHALA LETTER MAHAAPRAANA DAYANNA;Lo;0;L;;;;;N;;;;;
+0DB1;SINHALA LETTER DANTAJA NAYANNA;Lo;0;L;;;;;N;;;;;
+0DB3;SINHALA LETTER SANYAKA DAYANNA;Lo;0;L;;;;;N;;;;;
+0DB4;SINHALA LETTER ALPAPRAANA PAYANNA;Lo;0;L;;;;;N;;;;;
+0DB5;SINHALA LETTER MAHAAPRAANA PAYANNA;Lo;0;L;;;;;N;;;;;
+0DB6;SINHALA LETTER ALPAPRAANA BAYANNA;Lo;0;L;;;;;N;;;;;
+0DB7;SINHALA LETTER MAHAAPRAANA BAYANNA;Lo;0;L;;;;;N;;;;;
+0DB8;SINHALA LETTER MAYANNA;Lo;0;L;;;;;N;;;;;
+0DB9;SINHALA LETTER AMBA BAYANNA;Lo;0;L;;;;;N;;;;;
+0DBA;SINHALA LETTER YAYANNA;Lo;0;L;;;;;N;;;;;
+0DBB;SINHALA LETTER RAYANNA;Lo;0;L;;;;;N;;;;;
+0DBD;SINHALA LETTER DANTAJA LAYANNA;Lo;0;L;;;;;N;;;;;
+0DC0;SINHALA LETTER VAYANNA;Lo;0;L;;;;;N;;;;;
+0DC1;SINHALA LETTER TAALUJA SAYANNA;Lo;0;L;;;;;N;;;;;
+0DC2;SINHALA LETTER MUURDHAJA SAYANNA;Lo;0;L;;;;;N;;;;;
+0DC3;SINHALA LETTER DANTAJA SAYANNA;Lo;0;L;;;;;N;;;;;
+0DC4;SINHALA LETTER HAYANNA;Lo;0;L;;;;;N;;;;;
+0DC5;SINHALA LETTER MUURDHAJA LAYANNA;Lo;0;L;;;;;N;;;;;
+0DC6;SINHALA LETTER FAYANNA;Lo;0;L;;;;;N;;;;;
+0DCA;SINHALA SIGN AL-LAKUNA;Mn;9;NSM;;;;;N;;;;;
+0DCF;SINHALA VOWEL SIGN AELA-PILLA;Mc;0;L;;;;;N;;;;;
+0DD0;SINHALA VOWEL SIGN KETTI AEDA-PILLA;Mc;0;L;;;;;N;;;;;
+0DD1;SINHALA VOWEL SIGN DIGA AEDA-PILLA;Mc;0;L;;;;;N;;;;;
+0DD2;SINHALA VOWEL SIGN KETTI IS-PILLA;Mn;0;NSM;;;;;N;;;;;
+0DD3;SINHALA VOWEL SIGN DIGA IS-PILLA;Mn;0;NSM;;;;;N;;;;;
+0DD4;SINHALA VOWEL SIGN KETTI PAA-PILLA;Mn;0;NSM;;;;;N;;;;;
+0DD6;SINHALA VOWEL SIGN DIGA PAA-PILLA;Mn;0;NSM;;;;;N;;;;;
+0DD8;SINHALA VOWEL SIGN GAETTA-PILLA;Mc;0;L;;;;;N;;;;;
+0DD9;SINHALA VOWEL SIGN KOMBUVA;Mc;0;L;;;;;N;;;;;
+0DDA;SINHALA VOWEL SIGN DIGA KOMBUVA;Mc;0;L;0DD9 0DCA;;;;N;;;;;
+0DDB;SINHALA VOWEL SIGN KOMBU DEKA;Mc;0;L;;;;;N;;;;;
+0DDC;SINHALA VOWEL SIGN KOMBUVA HAA AELA-PILLA;Mc;0;L;0DD9 0DCF;;;;N;;;;;
+0DDD;SINHALA VOWEL SIGN KOMBUVA HAA DIGA AELA-PILLA;Mc;0;L;0DDC 0DCA;;;;N;;;;;
+0DDE;SINHALA VOWEL SIGN KOMBUVA HAA GAYANUKITTA;Mc;0;L;0DD9 0DDF;;;;N;;;;;
+0DDF;SINHALA VOWEL SIGN GAYANUKITTA;Mc;0;L;;;;;N;;;;;
+0DF2;SINHALA VOWEL SIGN DIGA GAETTA-PILLA;Mc;0;L;;;;;N;;;;;
+0DF3;SINHALA VOWEL SIGN DIGA GAYANUKITTA;Mc;0;L;;;;;N;;;;;
+0DF4;SINHALA PUNCTUATION KUNDDALIYA;Po;0;L;;;;;N;;;;;
+0E01;THAI CHARACTER KO KAI;Lo;0;L;;;;;N;THAI LETTER KO KAI;;;;
+0E02;THAI CHARACTER KHO KHAI;Lo;0;L;;;;;N;THAI LETTER KHO KHAI;;;;
+0E03;THAI CHARACTER KHO KHUAT;Lo;0;L;;;;;N;THAI LETTER KHO KHUAT;;;;
+0E04;THAI CHARACTER KHO KHWAI;Lo;0;L;;;;;N;THAI LETTER KHO KHWAI;;;;
+0E05;THAI CHARACTER KHO KHON;Lo;0;L;;;;;N;THAI LETTER KHO KHON;;;;
+0E06;THAI CHARACTER KHO RAKHANG;Lo;0;L;;;;;N;THAI LETTER KHO RAKHANG;;;;
+0E07;THAI CHARACTER NGO NGU;Lo;0;L;;;;;N;THAI LETTER NGO NGU;;;;
+0E08;THAI CHARACTER CHO CHAN;Lo;0;L;;;;;N;THAI LETTER CHO CHAN;;;;
+0E09;THAI CHARACTER CHO CHING;Lo;0;L;;;;;N;THAI LETTER CHO CHING;;;;
+0E0A;THAI CHARACTER CHO CHANG;Lo;0;L;;;;;N;THAI LETTER CHO CHANG;;;;
+0E0B;THAI CHARACTER SO SO;Lo;0;L;;;;;N;THAI LETTER SO SO;;;;
+0E0C;THAI CHARACTER CHO CHOE;Lo;0;L;;;;;N;THAI LETTER CHO CHOE;;;;
+0E0D;THAI CHARACTER YO YING;Lo;0;L;;;;;N;THAI LETTER YO YING;;;;
+0E0E;THAI CHARACTER DO CHADA;Lo;0;L;;;;;N;THAI LETTER DO CHADA;;;;
+0E0F;THAI CHARACTER TO PATAK;Lo;0;L;;;;;N;THAI LETTER TO PATAK;;;;
+0E10;THAI CHARACTER THO THAN;Lo;0;L;;;;;N;THAI LETTER THO THAN;;;;
+0E11;THAI CHARACTER THO NANGMONTHO;Lo;0;L;;;;;N;THAI LETTER THO NANGMONTHO;;;;
+0E12;THAI CHARACTER THO PHUTHAO;Lo;0;L;;;;;N;THAI LETTER THO PHUTHAO;;;;
+0E13;THAI CHARACTER NO NEN;Lo;0;L;;;;;N;THAI LETTER NO NEN;;;;
+0E14;THAI CHARACTER DO DEK;Lo;0;L;;;;;N;THAI LETTER DO DEK;;;;
+0E15;THAI CHARACTER TO TAO;Lo;0;L;;;;;N;THAI LETTER TO TAO;;;;
+0E16;THAI CHARACTER THO THUNG;Lo;0;L;;;;;N;THAI LETTER THO THUNG;;;;
+0E17;THAI CHARACTER THO THAHAN;Lo;0;L;;;;;N;THAI LETTER THO THAHAN;;;;
+0E18;THAI CHARACTER THO THONG;Lo;0;L;;;;;N;THAI LETTER THO THONG;;;;
+0E19;THAI CHARACTER NO NU;Lo;0;L;;;;;N;THAI LETTER NO NU;;;;
+0E1A;THAI CHARACTER BO BAIMAI;Lo;0;L;;;;;N;THAI LETTER BO BAIMAI;;;;
+0E1B;THAI CHARACTER PO PLA;Lo;0;L;;;;;N;THAI LETTER PO PLA;;;;
+0E1C;THAI CHARACTER PHO PHUNG;Lo;0;L;;;;;N;THAI LETTER PHO PHUNG;;;;
+0E1D;THAI CHARACTER FO FA;Lo;0;L;;;;;N;THAI LETTER FO FA;;;;
+0E1E;THAI CHARACTER PHO PHAN;Lo;0;L;;;;;N;THAI LETTER PHO PHAN;;;;
+0E1F;THAI CHARACTER FO FAN;Lo;0;L;;;;;N;THAI LETTER FO FAN;;;;
+0E20;THAI CHARACTER PHO SAMPHAO;Lo;0;L;;;;;N;THAI LETTER PHO SAMPHAO;;;;
+0E21;THAI CHARACTER MO MA;Lo;0;L;;;;;N;THAI LETTER MO MA;;;;
+0E22;THAI CHARACTER YO YAK;Lo;0;L;;;;;N;THAI LETTER YO YAK;;;;
+0E23;THAI CHARACTER RO RUA;Lo;0;L;;;;;N;THAI LETTER RO RUA;;;;
+0E24;THAI CHARACTER RU;Lo;0;L;;;;;N;THAI LETTER RU;;;;
+0E25;THAI CHARACTER LO LING;Lo;0;L;;;;;N;THAI LETTER LO LING;;;;
+0E26;THAI CHARACTER LU;Lo;0;L;;;;;N;THAI LETTER LU;;;;
+0E27;THAI CHARACTER WO WAEN;Lo;0;L;;;;;N;THAI LETTER WO WAEN;;;;
+0E28;THAI CHARACTER SO SALA;Lo;0;L;;;;;N;THAI LETTER SO SALA;;;;
+0E29;THAI CHARACTER SO RUSI;Lo;0;L;;;;;N;THAI LETTER SO RUSI;;;;
+0E2A;THAI CHARACTER SO SUA;Lo;0;L;;;;;N;THAI LETTER SO SUA;;;;
+0E2B;THAI CHARACTER HO HIP;Lo;0;L;;;;;N;THAI LETTER HO HIP;;;;
+0E2C;THAI CHARACTER LO CHULA;Lo;0;L;;;;;N;THAI LETTER LO CHULA;;;;
+0E2D;THAI CHARACTER O ANG;Lo;0;L;;;;;N;THAI LETTER O ANG;;;;
+0E2E;THAI CHARACTER HO NOKHUK;Lo;0;L;;;;;N;THAI LETTER HO NOK HUK;;;;
+0E2F;THAI CHARACTER PAIYANNOI;Lo;0;L;;;;;N;THAI PAI YAN NOI;paiyan noi;;;
+0E30;THAI CHARACTER SARA A;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA A;;;;
+0E31;THAI CHARACTER MAI HAN-AKAT;Mn;0;NSM;;;;;N;THAI VOWEL SIGN MAI HAN-AKAT;;;;
+0E32;THAI CHARACTER SARA AA;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA AA;;;;
+0E33;THAI CHARACTER SARA AM;Lo;0;L;<compat> 0E4D 0E32;;;;N;THAI VOWEL SIGN SARA AM;;;;
+0E34;THAI CHARACTER SARA I;Mn;0;NSM;;;;;N;THAI VOWEL SIGN SARA I;;;;
+0E35;THAI CHARACTER SARA II;Mn;0;NSM;;;;;N;THAI VOWEL SIGN SARA II;;;;
+0E36;THAI CHARACTER SARA UE;Mn;0;NSM;;;;;N;THAI VOWEL SIGN SARA UE;;;;
+0E37;THAI CHARACTER SARA UEE;Mn;0;NSM;;;;;N;THAI VOWEL SIGN SARA UEE;sara uue;;;
+0E38;THAI CHARACTER SARA U;Mn;103;NSM;;;;;N;THAI VOWEL SIGN SARA U;;;;
+0E39;THAI CHARACTER SARA UU;Mn;103;NSM;;;;;N;THAI VOWEL SIGN SARA UU;;;;
+0E3A;THAI CHARACTER PHINTHU;Mn;9;NSM;;;;;N;THAI VOWEL SIGN PHINTHU;;;;
+0E3F;THAI CURRENCY SYMBOL BAHT;Sc;0;ET;;;;;N;THAI BAHT SIGN;;;;
+0E40;THAI CHARACTER SARA E;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA E;;;;
+0E41;THAI CHARACTER SARA AE;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA AE;;;;
+0E42;THAI CHARACTER SARA O;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA O;;;;
+0E43;THAI CHARACTER SARA AI MAIMUAN;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA MAI MUAN;sara ai mai muan;;;
+0E44;THAI CHARACTER SARA AI MAIMALAI;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA MAI MALAI;sara ai mai malai;;;
+0E45;THAI CHARACTER LAKKHANGYAO;Lo;0;L;;;;;N;THAI LAK KHANG YAO;lakkhang yao;;;
+0E46;THAI CHARACTER MAIYAMOK;Lm;0;L;;;;;N;THAI MAI YAMOK;mai yamok;;;
+0E47;THAI CHARACTER MAITAIKHU;Mn;0;NSM;;;;;N;THAI VOWEL SIGN MAI TAI KHU;mai taikhu;;;
+0E48;THAI CHARACTER MAI EK;Mn;107;NSM;;;;;N;THAI TONE MAI EK;;;;
+0E49;THAI CHARACTER MAI THO;Mn;107;NSM;;;;;N;THAI TONE MAI THO;;;;
+0E4A;THAI CHARACTER MAI TRI;Mn;107;NSM;;;;;N;THAI TONE MAI TRI;;;;
+0E4B;THAI CHARACTER MAI CHATTAWA;Mn;107;NSM;;;;;N;THAI TONE MAI CHATTAWA;;;;
+0E4C;THAI CHARACTER THANTHAKHAT;Mn;0;NSM;;;;;N;THAI THANTHAKHAT;;;;
+0E4D;THAI CHARACTER NIKHAHIT;Mn;0;NSM;;;;;N;THAI NIKKHAHIT;nikkhahit;;;
+0E4E;THAI CHARACTER YAMAKKAN;Mn;0;NSM;;;;;N;THAI YAMAKKAN;;;;
+0E4F;THAI CHARACTER FONGMAN;Po;0;L;;;;;N;THAI FONGMAN;;;;
+0E50;THAI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+0E51;THAI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+0E52;THAI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+0E53;THAI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+0E54;THAI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+0E55;THAI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+0E56;THAI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+0E57;THAI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+0E58;THAI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+0E59;THAI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+0E5A;THAI CHARACTER ANGKHANKHU;Po;0;L;;;;;N;THAI ANGKHANKHU;;;;
+0E5B;THAI CHARACTER KHOMUT;Po;0;L;;;;;N;THAI KHOMUT;;;;
+0E81;LAO LETTER KO;Lo;0;L;;;;;N;;;;;
+0E82;LAO LETTER KHO SUNG;Lo;0;L;;;;;N;;;;;
+0E84;LAO LETTER KHO TAM;Lo;0;L;;;;;N;;;;;
+0E87;LAO LETTER NGO;Lo;0;L;;;;;N;;;;;
+0E88;LAO LETTER CO;Lo;0;L;;;;;N;;;;;
+0E8A;LAO LETTER SO TAM;Lo;0;L;;;;;N;;;;;
+0E8D;LAO LETTER NYO;Lo;0;L;;;;;N;;;;;
+0E94;LAO LETTER DO;Lo;0;L;;;;;N;;;;;
+0E95;LAO LETTER TO;Lo;0;L;;;;;N;;;;;
+0E96;LAO LETTER THO SUNG;Lo;0;L;;;;;N;;;;;
+0E97;LAO LETTER THO TAM;Lo;0;L;;;;;N;;;;;
+0E99;LAO LETTER NO;Lo;0;L;;;;;N;;;;;
+0E9A;LAO LETTER BO;Lo;0;L;;;;;N;;;;;
+0E9B;LAO LETTER PO;Lo;0;L;;;;;N;;;;;
+0E9C;LAO LETTER PHO SUNG;Lo;0;L;;;;;N;;;;;
+0E9D;LAO LETTER FO TAM;Lo;0;L;;;;;N;;;;;
+0E9E;LAO LETTER PHO TAM;Lo;0;L;;;;;N;;;;;
+0E9F;LAO LETTER FO SUNG;Lo;0;L;;;;;N;;;;;
+0EA1;LAO LETTER MO;Lo;0;L;;;;;N;;;;;
+0EA2;LAO LETTER YO;Lo;0;L;;;;;N;;;;;
+0EA3;LAO LETTER LO LING;Lo;0;L;;;;;N;;;;;
+0EA5;LAO LETTER LO LOOT;Lo;0;L;;;;;N;;;;;
+0EA7;LAO LETTER WO;Lo;0;L;;;;;N;;;;;
+0EAA;LAO LETTER SO SUNG;Lo;0;L;;;;;N;;;;;
+0EAB;LAO LETTER HO SUNG;Lo;0;L;;;;;N;;;;;
+0EAD;LAO LETTER O;Lo;0;L;;;;;N;;;;;
+0EAE;LAO LETTER HO TAM;Lo;0;L;;;;;N;;;;;
+0EAF;LAO ELLIPSIS;Lo;0;L;;;;;N;;;;;
+0EB0;LAO VOWEL SIGN A;Lo;0;L;;;;;N;;;;;
+0EB1;LAO VOWEL SIGN MAI KAN;Mn;0;NSM;;;;;N;;;;;
+0EB2;LAO VOWEL SIGN AA;Lo;0;L;;;;;N;;;;;
+0EB3;LAO VOWEL SIGN AM;Lo;0;L;<compat> 0ECD 0EB2;;;;N;;;;;
+0EB4;LAO VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
+0EB5;LAO VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;;
+0EB6;LAO VOWEL SIGN Y;Mn;0;NSM;;;;;N;;;;;
+0EB7;LAO VOWEL SIGN YY;Mn;0;NSM;;;;;N;;;;;
+0EB8;LAO VOWEL SIGN U;Mn;118;NSM;;;;;N;;;;;
+0EB9;LAO VOWEL SIGN UU;Mn;118;NSM;;;;;N;;;;;
+0EBB;LAO VOWEL SIGN MAI KON;Mn;0;NSM;;;;;N;;;;;
+0EBC;LAO SEMIVOWEL SIGN LO;Mn;0;NSM;;;;;N;;;;;
+0EBD;LAO SEMIVOWEL SIGN NYO;Lo;0;L;;;;;N;;;;;
+0EC0;LAO VOWEL SIGN E;Lo;0;L;;;;;N;;;;;
+0EC1;LAO VOWEL SIGN EI;Lo;0;L;;;;;N;;;;;
+0EC2;LAO VOWEL SIGN O;Lo;0;L;;;;;N;;;;;
+0EC3;LAO VOWEL SIGN AY;Lo;0;L;;;;;N;;;;;
+0EC4;LAO VOWEL SIGN AI;Lo;0;L;;;;;N;;;;;
+0EC6;LAO KO LA;Lm;0;L;;;;;N;;;;;
+0EC8;LAO TONE MAI EK;Mn;122;NSM;;;;;N;;;;;
+0EC9;LAO TONE MAI THO;Mn;122;NSM;;;;;N;;;;;
+0ECA;LAO TONE MAI TI;Mn;122;NSM;;;;;N;;;;;
+0ECB;LAO TONE MAI CATAWA;Mn;122;NSM;;;;;N;;;;;
+0ECC;LAO CANCELLATION MARK;Mn;0;NSM;;;;;N;;;;;
+0ECD;LAO NIGGAHITA;Mn;0;NSM;;;;;N;;;;;
+0ED0;LAO DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+0ED1;LAO DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+0ED2;LAO DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+0ED3;LAO DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+0ED4;LAO DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+0ED5;LAO DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+0ED6;LAO DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+0ED7;LAO DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+0ED8;LAO DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+0ED9;LAO DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+0EDC;LAO HO NO;Lo;0;L;<compat> 0EAB 0E99;;;;N;;;;;
+0EDD;LAO HO MO;Lo;0;L;<compat> 0EAB 0EA1;;;;N;;;;;
+0F00;TIBETAN SYLLABLE OM;Lo;0;L;;;;;N;;;;;
+0F01;TIBETAN MARK GTER YIG MGO TRUNCATED A;So;0;L;;;;;N;;ter yik go a thung;;;
+0F02;TIBETAN MARK GTER YIG MGO -UM RNAM BCAD MA;So;0;L;;;;;N;;ter yik go wum nam chey ma;;;
+0F03;TIBETAN MARK GTER YIG MGO -UM GTER TSHEG MA;So;0;L;;;;;N;;ter yik go wum ter tsek ma;;;
+0F04;TIBETAN MARK INITIAL YIG MGO MDUN MA;Po;0;L;;;;;N;TIBETAN SINGLE ORNAMENT;yik go dun ma;;;
+0F05;TIBETAN MARK CLOSING YIG MGO SGAB MA;Po;0;L;;;;;N;;yik go kab ma;;;
+0F06;TIBETAN MARK CARET YIG MGO PHUR SHAD MA;Po;0;L;;;;;N;;yik go pur shey ma;;;
+0F07;TIBETAN MARK YIG MGO TSHEG SHAD MA;Po;0;L;;;;;N;;yik go tsek shey ma;;;
+0F08;TIBETAN MARK SBRUL SHAD;Po;0;L;;;;;N;TIBETAN RGYANSHAD;drul shey;;;
+0F09;TIBETAN MARK BSKUR YIG MGO;Po;0;L;;;;;N;;kur yik go;;;
+0F0A;TIBETAN MARK BKA- SHOG YIG MGO;Po;0;L;;;;;N;;ka sho yik go;;;
+0F0B;TIBETAN MARK INTERSYLLABIC TSHEG;Po;0;L;;;;;N;TIBETAN TSEG;tsek;;;
+0F0C;TIBETAN MARK DELIMITER TSHEG BSTAR;Po;0;L;<noBreak> 0F0B;;;;N;;tsek tar;;;
+0F0D;TIBETAN MARK SHAD;Po;0;L;;;;;N;TIBETAN SHAD;shey;;;
+0F0E;TIBETAN MARK NYIS SHAD;Po;0;L;;;;;N;TIBETAN DOUBLE SHAD;nyi shey;;;
+0F0F;TIBETAN MARK TSHEG SHAD;Po;0;L;;;;;N;;tsek shey;;;
+0F10;TIBETAN MARK NYIS TSHEG SHAD;Po;0;L;;;;;N;;nyi tsek shey;;;
+0F11;TIBETAN MARK RIN CHEN SPUNGS SHAD;Po;0;L;;;;;N;TIBETAN RINCHANPHUNGSHAD;rinchen pung shey;;;
+0F12;TIBETAN MARK RGYA GRAM SHAD;Po;0;L;;;;;N;;gya tram shey;;;
+0F13;TIBETAN MARK CARET -DZUD RTAGS ME LONG CAN;So;0;L;;;;;N;;dzu ta me long chen;;;
+0F14;TIBETAN MARK GTER TSHEG;So;0;L;;;;;N;TIBETAN COMMA;ter tsek;;;
+0F15;TIBETAN LOGOTYPE SIGN CHAD RTAGS;So;0;L;;;;;N;;che ta;;;
+0F16;TIBETAN LOGOTYPE SIGN LHAG RTAGS;So;0;L;;;;;N;;hlak ta;;;
+0F17;TIBETAN ASTROLOGICAL SIGN SGRA GCAN -CHAR RTAGS;So;0;L;;;;;N;;trachen char ta;;;
+0F18;TIBETAN ASTROLOGICAL SIGN -KHYUD PA;Mn;220;NSM;;;;;N;;kyu pa;;;
+0F19;TIBETAN ASTROLOGICAL SIGN SDONG TSHUGS;Mn;220;NSM;;;;;N;;dong tsu;;;
+0F1A;TIBETAN SIGN RDEL DKAR GCIG;So;0;L;;;;;N;;deka chig;;;
+0F1B;TIBETAN SIGN RDEL DKAR GNYIS;So;0;L;;;;;N;;deka nyi;;;
+0F1C;TIBETAN SIGN RDEL DKAR GSUM;So;0;L;;;;;N;;deka sum;;;
+0F1D;TIBETAN SIGN RDEL NAG GCIG;So;0;L;;;;;N;;dena chig;;;
+0F1E;TIBETAN SIGN RDEL NAG GNYIS;So;0;L;;;;;N;;dena nyi;;;
+0F1F;TIBETAN SIGN RDEL DKAR RDEL NAG;So;0;L;;;;;N;;deka dena;;;
+0F20;TIBETAN DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+0F21;TIBETAN DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+0F22;TIBETAN DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+0F23;TIBETAN DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+0F24;TIBETAN DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+0F25;TIBETAN DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+0F26;TIBETAN DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+0F27;TIBETAN DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+0F28;TIBETAN DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+0F29;TIBETAN DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+0F2A;TIBETAN DIGIT HALF ONE;No;0;L;;;;1/2;N;;;;;
+0F2B;TIBETAN DIGIT HALF TWO;No;0;L;;;;3/2;N;;;;;
+0F2C;TIBETAN DIGIT HALF THREE;No;0;L;;;;5/2;N;;;;;
+0F2D;TIBETAN DIGIT HALF FOUR;No;0;L;;;;7/2;N;;;;;
+0F2E;TIBETAN DIGIT HALF FIVE;No;0;L;;;;9/2;N;;;;;
+0F2F;TIBETAN DIGIT HALF SIX;No;0;L;;;;11/2;N;;;;;
+0F30;TIBETAN DIGIT HALF SEVEN;No;0;L;;;;13/2;N;;;;;
+0F31;TIBETAN DIGIT HALF EIGHT;No;0;L;;;;15/2;N;;;;;
+0F32;TIBETAN DIGIT HALF NINE;No;0;L;;;;17/2;N;;;;;
+0F33;TIBETAN DIGIT HALF ZERO;No;0;L;;;;-1/2;N;;;;;
+0F34;TIBETAN MARK BSDUS RTAGS;So;0;L;;;;;N;;du ta;;;
+0F35;TIBETAN MARK NGAS BZUNG NYI ZLA;Mn;220;NSM;;;;;N;TIBETAN HONORIFIC UNDER RING;nge zung nyi da;;;
+0F36;TIBETAN MARK CARET -DZUD RTAGS BZHI MIG CAN;So;0;L;;;;;N;;dzu ta shi mig chen;;;
+0F37;TIBETAN MARK NGAS BZUNG SGOR RTAGS;Mn;220;NSM;;;;;N;TIBETAN UNDER RING;nge zung gor ta;;;
+0F38;TIBETAN MARK CHE MGO;So;0;L;;;;;N;;che go;;;
+0F39;TIBETAN MARK TSA -PHRU;Mn;216;NSM;;;;;N;TIBETAN LENITION MARK;tsa tru;;;
+0F3A;TIBETAN MARK GUG RTAGS GYON;Ps;0;ON;;;;;N;;gug ta yun;;;
+0F3B;TIBETAN MARK GUG RTAGS GYAS;Pe;0;ON;;;;;N;;gug ta ye;;;
+0F3C;TIBETAN MARK ANG KHANG GYON;Ps;0;ON;;;;;N;TIBETAN LEFT BRACE;ang kang yun;;;
+0F3D;TIBETAN MARK ANG KHANG GYAS;Pe;0;ON;;;;;N;TIBETAN RIGHT BRACE;ang kang ye;;;
+0F3E;TIBETAN SIGN YAR TSHES;Mc;0;L;;;;;N;;yar tse;;;
+0F3F;TIBETAN SIGN MAR TSHES;Mc;0;L;;;;;N;;mar tse;;;
+0F40;TIBETAN LETTER KA;Lo;0;L;;;;;N;;;;;
+0F41;TIBETAN LETTER KHA;Lo;0;L;;;;;N;;;;;
+0F42;TIBETAN LETTER GA;Lo;0;L;;;;;N;;;;;
+0F43;TIBETAN LETTER GHA;Lo;0;L;0F42 0FB7;;;;N;;;;;
+0F44;TIBETAN LETTER NGA;Lo;0;L;;;;;N;;;;;
+0F45;TIBETAN LETTER CA;Lo;0;L;;;;;N;;;;;
+0F46;TIBETAN LETTER CHA;Lo;0;L;;;;;N;;;;;
+0F47;TIBETAN LETTER JA;Lo;0;L;;;;;N;;;;;
+0F49;TIBETAN LETTER NYA;Lo;0;L;;;;;N;;;;;
+0F4A;TIBETAN LETTER TTA;Lo;0;L;;;;;N;TIBETAN LETTER REVERSED TA;;;;
+0F4B;TIBETAN LETTER TTHA;Lo;0;L;;;;;N;TIBETAN LETTER REVERSED THA;;;;
+0F4C;TIBETAN LETTER DDA;Lo;0;L;;;;;N;TIBETAN LETTER REVERSED DA;;;;
+0F4D;TIBETAN LETTER DDHA;Lo;0;L;0F4C 0FB7;;;;N;;;;;
+0F4E;TIBETAN LETTER NNA;Lo;0;L;;;;;N;TIBETAN LETTER REVERSED NA;;;;
+0F4F;TIBETAN LETTER TA;Lo;0;L;;;;;N;;;;;
+0F50;TIBETAN LETTER THA;Lo;0;L;;;;;N;;;;;
+0F51;TIBETAN LETTER DA;Lo;0;L;;;;;N;;;;;
+0F52;TIBETAN LETTER DHA;Lo;0;L;0F51 0FB7;;;;N;;;;;
+0F53;TIBETAN LETTER NA;Lo;0;L;;;;;N;;;;;
+0F54;TIBETAN LETTER PA;Lo;0;L;;;;;N;;;;;
+0F55;TIBETAN LETTER PHA;Lo;0;L;;;;;N;;;;;
+0F56;TIBETAN LETTER BA;Lo;0;L;;;;;N;;;;;
+0F57;TIBETAN LETTER BHA;Lo;0;L;0F56 0FB7;;;;N;;;;;
+0F58;TIBETAN LETTER MA;Lo;0;L;;;;;N;;;;;
+0F59;TIBETAN LETTER TSA;Lo;0;L;;;;;N;;;;;
+0F5A;TIBETAN LETTER TSHA;Lo;0;L;;;;;N;;;;;
+0F5B;TIBETAN LETTER DZA;Lo;0;L;;;;;N;;;;;
+0F5C;TIBETAN LETTER DZHA;Lo;0;L;0F5B 0FB7;;;;N;;;;;
+0F5D;TIBETAN LETTER WA;Lo;0;L;;;;;N;;;;;
+0F5E;TIBETAN LETTER ZHA;Lo;0;L;;;;;N;;;;;
+0F5F;TIBETAN LETTER ZA;Lo;0;L;;;;;N;;;;;
+0F60;TIBETAN LETTER -A;Lo;0;L;;;;;N;TIBETAN LETTER AA;;;;
+0F61;TIBETAN LETTER YA;Lo;0;L;;;;;N;;;;;
+0F62;TIBETAN LETTER RA;Lo;0;L;;;;;N;;*;;;
+0F63;TIBETAN LETTER LA;Lo;0;L;;;;;N;;;;;
+0F64;TIBETAN LETTER SHA;Lo;0;L;;;;;N;;;;;
+0F65;TIBETAN LETTER SSA;Lo;0;L;;;;;N;TIBETAN LETTER REVERSED SHA;;;;
+0F66;TIBETAN LETTER SA;Lo;0;L;;;;;N;;;;;
+0F67;TIBETAN LETTER HA;Lo;0;L;;;;;N;;;;;
+0F68;TIBETAN LETTER A;Lo;0;L;;;;;N;;;;;
+0F69;TIBETAN LETTER KSSA;Lo;0;L;0F40 0FB5;;;;N;;;;;
+0F6A;TIBETAN LETTER FIXED-FORM RA;Lo;0;L;;;;;N;;*;;;
+0F71;TIBETAN VOWEL SIGN AA;Mn;129;NSM;;;;;N;;;;;
+0F72;TIBETAN VOWEL SIGN I;Mn;130;NSM;;;;;N;;;;;
+0F73;TIBETAN VOWEL SIGN II;Mn;0;NSM;0F71 0F72;;;;N;;;;;
+0F74;TIBETAN VOWEL SIGN U;Mn;132;NSM;;;;;N;;;;;
+0F75;TIBETAN VOWEL SIGN UU;Mn;0;NSM;0F71 0F74;;;;N;;;;;
+0F76;TIBETAN VOWEL SIGN VOCALIC R;Mn;0;NSM;0FB2 0F80;;;;N;;;;;
+0F77;TIBETAN VOWEL SIGN VOCALIC RR;Mn;0;NSM;<compat> 0FB2 0F81;;;;N;;;;;
+0F78;TIBETAN VOWEL SIGN VOCALIC L;Mn;0;NSM;0FB3 0F80;;;;N;;;;;
+0F79;TIBETAN VOWEL SIGN VOCALIC LL;Mn;0;NSM;<compat> 0FB3 0F81;;;;N;;;;;
+0F7A;TIBETAN VOWEL SIGN E;Mn;130;NSM;;;;;N;;;;;
+0F7B;TIBETAN VOWEL SIGN EE;Mn;130;NSM;;;;;N;TIBETAN VOWEL SIGN AI;;;;
+0F7C;TIBETAN VOWEL SIGN O;Mn;130;NSM;;;;;N;;;;;
+0F7D;TIBETAN VOWEL SIGN OO;Mn;130;NSM;;;;;N;TIBETAN VOWEL SIGN AU;;;;
+0F7E;TIBETAN SIGN RJES SU NGA RO;Mn;0;NSM;;;;;N;TIBETAN ANUSVARA;je su nga ro;;;
+0F7F;TIBETAN SIGN RNAM BCAD;Mc;0;L;;;;;N;TIBETAN VISARGA;nam chey;;;
+0F80;TIBETAN VOWEL SIGN REVERSED I;Mn;130;NSM;;;;;N;TIBETAN VOWEL SIGN SHORT I;;;;
+0F81;TIBETAN VOWEL SIGN REVERSED II;Mn;0;NSM;0F71 0F80;;;;N;;;;;
+0F82;TIBETAN SIGN NYI ZLA NAA DA;Mn;230;NSM;;;;;N;TIBETAN CANDRABINDU WITH ORNAMENT;nyi da na da;;;
+0F83;TIBETAN SIGN SNA LDAN;Mn;230;NSM;;;;;N;TIBETAN CANDRABINDU;nan de;;;
+0F84;TIBETAN MARK HALANTA;Mn;9;NSM;;;;;N;TIBETAN VIRAMA;;;;
+0F85;TIBETAN MARK PALUTA;Po;0;L;;;;;N;TIBETAN CHUCHENYIGE;;;;
+0F86;TIBETAN SIGN LCI RTAGS;Mn;230;NSM;;;;;N;;ji ta;;;
+0F87;TIBETAN SIGN YANG RTAGS;Mn;230;NSM;;;;;N;;yang ta;;;
+0F88;TIBETAN SIGN LCE TSA CAN;Lo;0;L;;;;;N;;che tsa chen;;;
+0F89;TIBETAN SIGN MCHU CAN;Lo;0;L;;;;;N;;chu chen;;;
+0F8A;TIBETAN SIGN GRU CAN RGYINGS;Lo;0;L;;;;;N;;tru chen ging;;;
+0F8B;TIBETAN SIGN GRU MED RGYINGS;Lo;0;L;;;;;N;;tru me ging;;;
+0F90;TIBETAN SUBJOINED LETTER KA;Mn;0;NSM;;;;;N;;;;;
+0F91;TIBETAN SUBJOINED LETTER KHA;Mn;0;NSM;;;;;N;;;;;
+0F92;TIBETAN SUBJOINED LETTER GA;Mn;0;NSM;;;;;N;;;;;
+0F93;TIBETAN SUBJOINED LETTER GHA;Mn;0;NSM;0F92 0FB7;;;;N;;;;;
+0F94;TIBETAN SUBJOINED LETTER NGA;Mn;0;NSM;;;;;N;;;;;
+0F95;TIBETAN SUBJOINED LETTER CA;Mn;0;NSM;;;;;N;;;;;
+0F96;TIBETAN SUBJOINED LETTER CHA;Mn;0;NSM;;;;;N;;;;;
+0F97;TIBETAN SUBJOINED LETTER JA;Mn;0;NSM;;;;;N;;;;;
+0F99;TIBETAN SUBJOINED LETTER NYA;Mn;0;NSM;;;;;N;;;;;
+0F9A;TIBETAN SUBJOINED LETTER TTA;Mn;0;NSM;;;;;N;;;;;
+0F9B;TIBETAN SUBJOINED LETTER TTHA;Mn;0;NSM;;;;;N;;;;;
+0F9C;TIBETAN SUBJOINED LETTER DDA;Mn;0;NSM;;;;;N;;;;;
+0F9D;TIBETAN SUBJOINED LETTER DDHA;Mn;0;NSM;0F9C 0FB7;;;;N;;;;;
+0F9E;TIBETAN SUBJOINED LETTER NNA;Mn;0;NSM;;;;;N;;;;;
+0F9F;TIBETAN SUBJOINED LETTER TA;Mn;0;NSM;;;;;N;;;;;
+0FA0;TIBETAN SUBJOINED LETTER THA;Mn;0;NSM;;;;;N;;;;;
+0FA1;TIBETAN SUBJOINED LETTER DA;Mn;0;NSM;;;;;N;;;;;
+0FA2;TIBETAN SUBJOINED LETTER DHA;Mn;0;NSM;0FA1 0FB7;;;;N;;;;;
+0FA3;TIBETAN SUBJOINED LETTER NA;Mn;0;NSM;;;;;N;;;;;
+0FA4;TIBETAN SUBJOINED LETTER PA;Mn;0;NSM;;;;;N;;;;;
+0FA5;TIBETAN SUBJOINED LETTER PHA;Mn;0;NSM;;;;;N;;;;;
+0FA6;TIBETAN SUBJOINED LETTER BA;Mn;0;NSM;;;;;N;;;;;
+0FA7;TIBETAN SUBJOINED LETTER BHA;Mn;0;NSM;0FA6 0FB7;;;;N;;;;;
+0FA8;TIBETAN SUBJOINED LETTER MA;Mn;0;NSM;;;;;N;;;;;
+0FA9;TIBETAN SUBJOINED LETTER TSA;Mn;0;NSM;;;;;N;;;;;
+0FAA;TIBETAN SUBJOINED LETTER TSHA;Mn;0;NSM;;;;;N;;;;;
+0FAB;TIBETAN SUBJOINED LETTER DZA;Mn;0;NSM;;;;;N;;;;;
+0FAC;TIBETAN SUBJOINED LETTER DZHA;Mn;0;NSM;0FAB 0FB7;;;;N;;;;;
+0FAD;TIBETAN SUBJOINED LETTER WA;Mn;0;NSM;;;;;N;;*;;;
+0FAE;TIBETAN SUBJOINED LETTER ZHA;Mn;0;NSM;;;;;N;;;;;
+0FAF;TIBETAN SUBJOINED LETTER ZA;Mn;0;NSM;;;;;N;;;;;
+0FB0;TIBETAN SUBJOINED LETTER -A;Mn;0;NSM;;;;;N;;;;;
+0FB1;TIBETAN SUBJOINED LETTER YA;Mn;0;NSM;;;;;N;;*;;;
+0FB2;TIBETAN SUBJOINED LETTER RA;Mn;0;NSM;;;;;N;;*;;;
+0FB3;TIBETAN SUBJOINED LETTER LA;Mn;0;NSM;;;;;N;;;;;
+0FB4;TIBETAN SUBJOINED LETTER SHA;Mn;0;NSM;;;;;N;;;;;
+0FB5;TIBETAN SUBJOINED LETTER SSA;Mn;0;NSM;;;;;N;;;;;
+0FB6;TIBETAN SUBJOINED LETTER SA;Mn;0;NSM;;;;;N;;;;;
+0FB7;TIBETAN SUBJOINED LETTER HA;Mn;0;NSM;;;;;N;;;;;
+0FB8;TIBETAN SUBJOINED LETTER A;Mn;0;NSM;;;;;N;;;;;
+0FB9;TIBETAN SUBJOINED LETTER KSSA;Mn;0;NSM;0F90 0FB5;;;;N;;;;;
+0FBA;TIBETAN SUBJOINED LETTER FIXED-FORM WA;Mn;0;NSM;;;;;N;;*;;;
+0FBB;TIBETAN SUBJOINED LETTER FIXED-FORM YA;Mn;0;NSM;;;;;N;;*;;;
+0FBC;TIBETAN SUBJOINED LETTER FIXED-FORM RA;Mn;0;NSM;;;;;N;;*;;;
+0FBE;TIBETAN KU RU KHA;So;0;L;;;;;N;;kuruka;;;
+0FBF;TIBETAN KU RU KHA BZHI MIG CAN;So;0;L;;;;;N;;kuruka shi mik chen;;;
+0FC0;TIBETAN CANTILLATION SIGN HEAVY BEAT;So;0;L;;;;;N;;;;;
+0FC1;TIBETAN CANTILLATION SIGN LIGHT BEAT;So;0;L;;;;;N;;;;;
+0FC2;TIBETAN CANTILLATION SIGN CANG TE-U;So;0;L;;;;;N;;chang tyu;;;
+0FC3;TIBETAN CANTILLATION SIGN SBUB -CHAL;So;0;L;;;;;N;;bub chey;;;
+0FC4;TIBETAN SYMBOL DRIL BU;So;0;L;;;;;N;;drilbu;;;
+0FC5;TIBETAN SYMBOL RDO RJE;So;0;L;;;;;N;;dorje;;;
+0FC6;TIBETAN SYMBOL PADMA GDAN;Mn;220;NSM;;;;;N;;pema den;;;
+0FC7;TIBETAN SYMBOL RDO RJE RGYA GRAM;So;0;L;;;;;N;;dorje gya dram;;;
+0FC8;TIBETAN SYMBOL PHUR PA;So;0;L;;;;;N;;phurba;;;
+0FC9;TIBETAN SYMBOL NOR BU;So;0;L;;;;;N;;norbu;;;
+0FCA;TIBETAN SYMBOL NOR BU NYIS -KHYIL;So;0;L;;;;;N;;norbu nyi khyi;;;
+0FCB;TIBETAN SYMBOL NOR BU GSUM -KHYIL;So;0;L;;;;;N;;norbu sum khyi;;;
+0FCC;TIBETAN SYMBOL NOR BU BZHI -KHYIL;So;0;L;;;;;N;;norbu shi khyi;;;
+0FCF;TIBETAN SIGN RDEL NAG GSUM;So;0;L;;;;;N;;dena sum;;;
+1000;MYANMAR LETTER KA;Lo;0;L;;;;;N;;;;;
+1001;MYANMAR LETTER KHA;Lo;0;L;;;;;N;;;;;
+1002;MYANMAR LETTER GA;Lo;0;L;;;;;N;;;;;
+1003;MYANMAR LETTER GHA;Lo;0;L;;;;;N;;;;;
+1004;MYANMAR LETTER NGA;Lo;0;L;;;;;N;;;;;
+1005;MYANMAR LETTER CA;Lo;0;L;;;;;N;;;;;
+1006;MYANMAR LETTER CHA;Lo;0;L;;;;;N;;;;;
+1007;MYANMAR LETTER JA;Lo;0;L;;;;;N;;;;;
+1008;MYANMAR LETTER JHA;Lo;0;L;;;;;N;;;;;
+1009;MYANMAR LETTER NYA;Lo;0;L;;;;;N;;;;;
+100A;MYANMAR LETTER NNYA;Lo;0;L;;;;;N;;;;;
+100B;MYANMAR LETTER TTA;Lo;0;L;;;;;N;;;;;
+100C;MYANMAR LETTER TTHA;Lo;0;L;;;;;N;;;;;
+100D;MYANMAR LETTER DDA;Lo;0;L;;;;;N;;;;;
+100E;MYANMAR LETTER DDHA;Lo;0;L;;;;;N;;;;;
+100F;MYANMAR LETTER NNA;Lo;0;L;;;;;N;;;;;
+1010;MYANMAR LETTER TA;Lo;0;L;;;;;N;;;;;
+1011;MYANMAR LETTER THA;Lo;0;L;;;;;N;;;;;
+1012;MYANMAR LETTER DA;Lo;0;L;;;;;N;;;;;
+1013;MYANMAR LETTER DHA;Lo;0;L;;;;;N;;;;;
+1014;MYANMAR LETTER NA;Lo;0;L;;;;;N;;;;;
+1015;MYANMAR LETTER PA;Lo;0;L;;;;;N;;;;;
+1016;MYANMAR LETTER PHA;Lo;0;L;;;;;N;;;;;
+1017;MYANMAR LETTER BA;Lo;0;L;;;;;N;;;;;
+1018;MYANMAR LETTER BHA;Lo;0;L;;;;;N;;;;;
+1019;MYANMAR LETTER MA;Lo;0;L;;;;;N;;;;;
+101A;MYANMAR LETTER YA;Lo;0;L;;;;;N;;;;;
+101B;MYANMAR LETTER RA;Lo;0;L;;;;;N;;;;;
+101C;MYANMAR LETTER LA;Lo;0;L;;;;;N;;;;;
+101D;MYANMAR LETTER WA;Lo;0;L;;;;;N;;;;;
+101E;MYANMAR LETTER SA;Lo;0;L;;;;;N;;;;;
+101F;MYANMAR LETTER HA;Lo;0;L;;;;;N;;;;;
+1020;MYANMAR LETTER LLA;Lo;0;L;;;;;N;;;;;
+1021;MYANMAR LETTER A;Lo;0;L;;;;;N;;;;;
+1023;MYANMAR LETTER I;Lo;0;L;;;;;N;;;;;
+1024;MYANMAR LETTER II;Lo;0;L;;;;;N;;;;;
+1025;MYANMAR LETTER U;Lo;0;L;;;;;N;;;;;
+1026;MYANMAR LETTER UU;Lo;0;L;1025 102E;;;;N;;;;;
+1027;MYANMAR LETTER E;Lo;0;L;;;;;N;;;;;
+1029;MYANMAR LETTER O;Lo;0;L;;;;;N;;;;;
+102A;MYANMAR LETTER AU;Lo;0;L;;;;;N;;;;;
+102C;MYANMAR VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+102D;MYANMAR VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
+102E;MYANMAR VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;;
+102F;MYANMAR VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+1030;MYANMAR VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+1031;MYANMAR VOWEL SIGN E;Mc;0;L;;;;;N;;;;;
+1032;MYANMAR VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;;
+1036;MYANMAR SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;;
+1037;MYANMAR SIGN DOT BELOW;Mn;7;NSM;;;;;N;;;;;
+1038;MYANMAR SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+1039;MYANMAR SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+1040;MYANMAR DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+1041;MYANMAR DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+1042;MYANMAR DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+1043;MYANMAR DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+1044;MYANMAR DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+1045;MYANMAR DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+1046;MYANMAR DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+1047;MYANMAR DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+1048;MYANMAR DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+1049;MYANMAR DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+104A;MYANMAR SIGN LITTLE SECTION;Po;0;L;;;;;N;;;;;
+104B;MYANMAR SIGN SECTION;Po;0;L;;;;;N;;;;;
+104C;MYANMAR SYMBOL LOCATIVE;Po;0;L;;;;;N;;;;;
+104D;MYANMAR SYMBOL COMPLETED;Po;0;L;;;;;N;;;;;
+104E;MYANMAR SYMBOL AFOREMENTIONED;Po;0;L;;;;;N;;;;;
+104F;MYANMAR SYMBOL GENITIVE;Po;0;L;;;;;N;;;;;
+1050;MYANMAR LETTER SHA;Lo;0;L;;;;;N;;;;;
+1051;MYANMAR LETTER SSA;Lo;0;L;;;;;N;;;;;
+1052;MYANMAR LETTER VOCALIC R;Lo;0;L;;;;;N;;;;;
+1053;MYANMAR LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;;
+1054;MYANMAR LETTER VOCALIC L;Lo;0;L;;;;;N;;;;;
+1055;MYANMAR LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;;
+1056;MYANMAR VOWEL SIGN VOCALIC R;Mc;0;L;;;;;N;;;;;
+1057;MYANMAR VOWEL SIGN VOCALIC RR;Mc;0;L;;;;;N;;;;;
+1058;MYANMAR VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;;
+1059;MYANMAR VOWEL SIGN VOCALIC LL;Mn;0;NSM;;;;;N;;;;;
+10A0;GEORGIAN CAPITAL LETTER AN;Lu;0;L;;;;;N;;Khutsuri;;;
+10A1;GEORGIAN CAPITAL LETTER BAN;Lu;0;L;;;;;N;;Khutsuri;;;
+10A2;GEORGIAN CAPITAL LETTER GAN;Lu;0;L;;;;;N;;Khutsuri;;;
+10A3;GEORGIAN CAPITAL LETTER DON;Lu;0;L;;;;;N;;Khutsuri;;;
+10A4;GEORGIAN CAPITAL LETTER EN;Lu;0;L;;;;;N;;Khutsuri;;;
+10A5;GEORGIAN CAPITAL LETTER VIN;Lu;0;L;;;;;N;;Khutsuri;;;
+10A6;GEORGIAN CAPITAL LETTER ZEN;Lu;0;L;;;;;N;;Khutsuri;;;
+10A7;GEORGIAN CAPITAL LETTER TAN;Lu;0;L;;;;;N;;Khutsuri;;;
+10A8;GEORGIAN CAPITAL LETTER IN;Lu;0;L;;;;;N;;Khutsuri;;;
+10A9;GEORGIAN CAPITAL LETTER KAN;Lu;0;L;;;;;N;;Khutsuri;;;
+10AA;GEORGIAN CAPITAL LETTER LAS;Lu;0;L;;;;;N;;Khutsuri;;;
+10AB;GEORGIAN CAPITAL LETTER MAN;Lu;0;L;;;;;N;;Khutsuri;;;
+10AC;GEORGIAN CAPITAL LETTER NAR;Lu;0;L;;;;;N;;Khutsuri;;;
+10AD;GEORGIAN CAPITAL LETTER ON;Lu;0;L;;;;;N;;Khutsuri;;;
+10AE;GEORGIAN CAPITAL LETTER PAR;Lu;0;L;;;;;N;;Khutsuri;;;
+10AF;GEORGIAN CAPITAL LETTER ZHAR;Lu;0;L;;;;;N;;Khutsuri;;;
+10B0;GEORGIAN CAPITAL LETTER RAE;Lu;0;L;;;;;N;;Khutsuri;;;
+10B1;GEORGIAN CAPITAL LETTER SAN;Lu;0;L;;;;;N;;Khutsuri;;;
+10B2;GEORGIAN CAPITAL LETTER TAR;Lu;0;L;;;;;N;;Khutsuri;;;
+10B3;GEORGIAN CAPITAL LETTER UN;Lu;0;L;;;;;N;;Khutsuri;;;
+10B4;GEORGIAN CAPITAL LETTER PHAR;Lu;0;L;;;;;N;;Khutsuri;;;
+10B5;GEORGIAN CAPITAL LETTER KHAR;Lu;0;L;;;;;N;;Khutsuri;;;
+10B6;GEORGIAN CAPITAL LETTER GHAN;Lu;0;L;;;;;N;;Khutsuri;;;
+10B7;GEORGIAN CAPITAL LETTER QAR;Lu;0;L;;;;;N;;Khutsuri;;;
+10B8;GEORGIAN CAPITAL LETTER SHIN;Lu;0;L;;;;;N;;Khutsuri;;;
+10B9;GEORGIAN CAPITAL LETTER CHIN;Lu;0;L;;;;;N;;Khutsuri;;;
+10BA;GEORGIAN CAPITAL LETTER CAN;Lu;0;L;;;;;N;;Khutsuri;;;
+10BB;GEORGIAN CAPITAL LETTER JIL;Lu;0;L;;;;;N;;Khutsuri;;;
+10BC;GEORGIAN CAPITAL LETTER CIL;Lu;0;L;;;;;N;;Khutsuri;;;
+10BD;GEORGIAN CAPITAL LETTER CHAR;Lu;0;L;;;;;N;;Khutsuri;;;
+10BE;GEORGIAN CAPITAL LETTER XAN;Lu;0;L;;;;;N;;Khutsuri;;;
+10BF;GEORGIAN CAPITAL LETTER JHAN;Lu;0;L;;;;;N;;Khutsuri;;;
+10C0;GEORGIAN CAPITAL LETTER HAE;Lu;0;L;;;;;N;;Khutsuri;;;
+10C1;GEORGIAN CAPITAL LETTER HE;Lu;0;L;;;;;N;;Khutsuri;;;
+10C2;GEORGIAN CAPITAL LETTER HIE;Lu;0;L;;;;;N;;Khutsuri;;;
+10C3;GEORGIAN CAPITAL LETTER WE;Lu;0;L;;;;;N;;Khutsuri;;;
+10C4;GEORGIAN CAPITAL LETTER HAR;Lu;0;L;;;;;N;;Khutsuri;;;
+10C5;GEORGIAN CAPITAL LETTER HOE;Lu;0;L;;;;;N;;Khutsuri;;;
+10D0;GEORGIAN LETTER AN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER AN;;;;
+10D1;GEORGIAN LETTER BAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER BAN;;;;
+10D2;GEORGIAN LETTER GAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER GAN;;;;
+10D3;GEORGIAN LETTER DON;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER DON;;;;
+10D4;GEORGIAN LETTER EN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER EN;;;;
+10D5;GEORGIAN LETTER VIN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER VIN;;;;
+10D6;GEORGIAN LETTER ZEN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER ZEN;;;;
+10D7;GEORGIAN LETTER TAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER TAN;;;;
+10D8;GEORGIAN LETTER IN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER IN;;;;
+10D9;GEORGIAN LETTER KAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER KAN;;;;
+10DA;GEORGIAN LETTER LAS;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER LAS;;;;
+10DB;GEORGIAN LETTER MAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER MAN;;;;
+10DC;GEORGIAN LETTER NAR;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER NAR;;;;
+10DD;GEORGIAN LETTER ON;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER ON;;;;
+10DE;GEORGIAN LETTER PAR;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER PAR;;;;
+10DF;GEORGIAN LETTER ZHAR;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER ZHAR;;;;
+10E0;GEORGIAN LETTER RAE;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER RAE;;;;
+10E1;GEORGIAN LETTER SAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER SAN;;;;
+10E2;GEORGIAN LETTER TAR;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER TAR;;;;
+10E3;GEORGIAN LETTER UN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER UN;;;;
+10E4;GEORGIAN LETTER PHAR;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER PHAR;;;;
+10E5;GEORGIAN LETTER KHAR;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER KHAR;;;;
+10E6;GEORGIAN LETTER GHAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER GHAN;;;;
+10E7;GEORGIAN LETTER QAR;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER QAR;;;;
+10E8;GEORGIAN LETTER SHIN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER SHIN;;;;
+10E9;GEORGIAN LETTER CHIN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER CHIN;;;;
+10EA;GEORGIAN LETTER CAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER CAN;;;;
+10EB;GEORGIAN LETTER JIL;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER JIL;;;;
+10EC;GEORGIAN LETTER CIL;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER CIL;;;;
+10ED;GEORGIAN LETTER CHAR;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER CHAR;;;;
+10EE;GEORGIAN LETTER XAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER XAN;;;;
+10EF;GEORGIAN LETTER JHAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER JHAN;;;;
+10F0;GEORGIAN LETTER HAE;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER HAE;;;;
+10F1;GEORGIAN LETTER HE;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER HE;;;;
+10F2;GEORGIAN LETTER HIE;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER HIE;;;;
+10F3;GEORGIAN LETTER WE;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER WE;;;;
+10F4;GEORGIAN LETTER HAR;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER HAR;;;;
+10F5;GEORGIAN LETTER HOE;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER HOE;;;;
+10F6;GEORGIAN LETTER FI;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER FI;;;;
+10F7;GEORGIAN LETTER YN;Lo;0;L;;;;;N;;;;;
+10F8;GEORGIAN LETTER ELIFI;Lo;0;L;;;;;N;;;;;
+10FB;GEORGIAN PARAGRAPH SEPARATOR;Po;0;L;;;;;N;;;;;
+1100;HANGUL CHOSEONG KIYEOK;Lo;0;L;;;;;N;;g *;;;
+1101;HANGUL CHOSEONG SSANGKIYEOK;Lo;0;L;;;;;N;;gg *;;;
+1102;HANGUL CHOSEONG NIEUN;Lo;0;L;;;;;N;;n *;;;
+1103;HANGUL CHOSEONG TIKEUT;Lo;0;L;;;;;N;;d *;;;
+1104;HANGUL CHOSEONG SSANGTIKEUT;Lo;0;L;;;;;N;;dd *;;;
+1105;HANGUL CHOSEONG RIEUL;Lo;0;L;;;;;N;;r *;;;
+1106;HANGUL CHOSEONG MIEUM;Lo;0;L;;;;;N;;m *;;;
+1107;HANGUL CHOSEONG PIEUP;Lo;0;L;;;;;N;;b *;;;
+1108;HANGUL CHOSEONG SSANGPIEUP;Lo;0;L;;;;;N;;bb *;;;
+1109;HANGUL CHOSEONG SIOS;Lo;0;L;;;;;N;;s *;;;
+110A;HANGUL CHOSEONG SSANGSIOS;Lo;0;L;;;;;N;;ss *;;;
+110B;HANGUL CHOSEONG IEUNG;Lo;0;L;;;;;N;;;;;
+110C;HANGUL CHOSEONG CIEUC;Lo;0;L;;;;;N;;j *;;;
+110D;HANGUL CHOSEONG SSANGCIEUC;Lo;0;L;;;;;N;;jj *;;;
+110E;HANGUL CHOSEONG CHIEUCH;Lo;0;L;;;;;N;;c *;;;
+110F;HANGUL CHOSEONG KHIEUKH;Lo;0;L;;;;;N;;k *;;;
+1110;HANGUL CHOSEONG THIEUTH;Lo;0;L;;;;;N;;t *;;;
+1111;HANGUL CHOSEONG PHIEUPH;Lo;0;L;;;;;N;;p *;;;
+1112;HANGUL CHOSEONG HIEUH;Lo;0;L;;;;;N;;h *;;;
+1113;HANGUL CHOSEONG NIEUN-KIYEOK;Lo;0;L;;;;;N;;;;;
+1114;HANGUL CHOSEONG SSANGNIEUN;Lo;0;L;;;;;N;;;;;
+1115;HANGUL CHOSEONG NIEUN-TIKEUT;Lo;0;L;;;;;N;;;;;
+1116;HANGUL CHOSEONG NIEUN-PIEUP;Lo;0;L;;;;;N;;;;;
+1117;HANGUL CHOSEONG TIKEUT-KIYEOK;Lo;0;L;;;;;N;;;;;
+1118;HANGUL CHOSEONG RIEUL-NIEUN;Lo;0;L;;;;;N;;;;;
+1119;HANGUL CHOSEONG SSANGRIEUL;Lo;0;L;;;;;N;;;;;
+111A;HANGUL CHOSEONG RIEUL-HIEUH;Lo;0;L;;;;;N;;;;;
+111B;HANGUL CHOSEONG KAPYEOUNRIEUL;Lo;0;L;;;;;N;;;;;
+111C;HANGUL CHOSEONG MIEUM-PIEUP;Lo;0;L;;;;;N;;;;;
+111D;HANGUL CHOSEONG KAPYEOUNMIEUM;Lo;0;L;;;;;N;;;;;
+111E;HANGUL CHOSEONG PIEUP-KIYEOK;Lo;0;L;;;;;N;;;;;
+111F;HANGUL CHOSEONG PIEUP-NIEUN;Lo;0;L;;;;;N;;;;;
+1120;HANGUL CHOSEONG PIEUP-TIKEUT;Lo;0;L;;;;;N;;;;;
+1121;HANGUL CHOSEONG PIEUP-SIOS;Lo;0;L;;;;;N;;;;;
+1122;HANGUL CHOSEONG PIEUP-SIOS-KIYEOK;Lo;0;L;;;;;N;;;;;
+1123;HANGUL CHOSEONG PIEUP-SIOS-TIKEUT;Lo;0;L;;;;;N;;;;;
+1124;HANGUL CHOSEONG PIEUP-SIOS-PIEUP;Lo;0;L;;;;;N;;;;;
+1125;HANGUL CHOSEONG PIEUP-SSANGSIOS;Lo;0;L;;;;;N;;;;;
+1126;HANGUL CHOSEONG PIEUP-SIOS-CIEUC;Lo;0;L;;;;;N;;;;;
+1127;HANGUL CHOSEONG PIEUP-CIEUC;Lo;0;L;;;;;N;;;;;
+1128;HANGUL CHOSEONG PIEUP-CHIEUCH;Lo;0;L;;;;;N;;;;;
+1129;HANGUL CHOSEONG PIEUP-THIEUTH;Lo;0;L;;;;;N;;;;;
+112A;HANGUL CHOSEONG PIEUP-PHIEUPH;Lo;0;L;;;;;N;;;;;
+112B;HANGUL CHOSEONG KAPYEOUNPIEUP;Lo;0;L;;;;;N;;;;;
+112C;HANGUL CHOSEONG KAPYEOUNSSANGPIEUP;Lo;0;L;;;;;N;;;;;
+112D;HANGUL CHOSEONG SIOS-KIYEOK;Lo;0;L;;;;;N;;;;;
+112E;HANGUL CHOSEONG SIOS-NIEUN;Lo;0;L;;;;;N;;;;;
+112F;HANGUL CHOSEONG SIOS-TIKEUT;Lo;0;L;;;;;N;;;;;
+1130;HANGUL CHOSEONG SIOS-RIEUL;Lo;0;L;;;;;N;;;;;
+1131;HANGUL CHOSEONG SIOS-MIEUM;Lo;0;L;;;;;N;;;;;
+1132;HANGUL CHOSEONG SIOS-PIEUP;Lo;0;L;;;;;N;;;;;
+1133;HANGUL CHOSEONG SIOS-PIEUP-KIYEOK;Lo;0;L;;;;;N;;;;;
+1134;HANGUL CHOSEONG SIOS-SSANGSIOS;Lo;0;L;;;;;N;;;;;
+1135;HANGUL CHOSEONG SIOS-IEUNG;Lo;0;L;;;;;N;;;;;
+1136;HANGUL CHOSEONG SIOS-CIEUC;Lo;0;L;;;;;N;;;;;
+1137;HANGUL CHOSEONG SIOS-CHIEUCH;Lo;0;L;;;;;N;;;;;
+1138;HANGUL CHOSEONG SIOS-KHIEUKH;Lo;0;L;;;;;N;;;;;
+1139;HANGUL CHOSEONG SIOS-THIEUTH;Lo;0;L;;;;;N;;;;;
+113A;HANGUL CHOSEONG SIOS-PHIEUPH;Lo;0;L;;;;;N;;;;;
+113B;HANGUL CHOSEONG SIOS-HIEUH;Lo;0;L;;;;;N;;;;;
+113C;HANGUL CHOSEONG CHITUEUMSIOS;Lo;0;L;;;;;N;;;;;
+113D;HANGUL CHOSEONG CHITUEUMSSANGSIOS;Lo;0;L;;;;;N;;;;;
+113E;HANGUL CHOSEONG CEONGCHIEUMSIOS;Lo;0;L;;;;;N;;;;;
+113F;HANGUL CHOSEONG CEONGCHIEUMSSANGSIOS;Lo;0;L;;;;;N;;;;;
+1140;HANGUL CHOSEONG PANSIOS;Lo;0;L;;;;;N;;;;;
+1141;HANGUL CHOSEONG IEUNG-KIYEOK;Lo;0;L;;;;;N;;;;;
+1142;HANGUL CHOSEONG IEUNG-TIKEUT;Lo;0;L;;;;;N;;;;;
+1143;HANGUL CHOSEONG IEUNG-MIEUM;Lo;0;L;;;;;N;;;;;
+1144;HANGUL CHOSEONG IEUNG-PIEUP;Lo;0;L;;;;;N;;;;;
+1145;HANGUL CHOSEONG IEUNG-SIOS;Lo;0;L;;;;;N;;;;;
+1146;HANGUL CHOSEONG IEUNG-PANSIOS;Lo;0;L;;;;;N;;;;;
+1147;HANGUL CHOSEONG SSANGIEUNG;Lo;0;L;;;;;N;;;;;
+1148;HANGUL CHOSEONG IEUNG-CIEUC;Lo;0;L;;;;;N;;;;;
+1149;HANGUL CHOSEONG IEUNG-CHIEUCH;Lo;0;L;;;;;N;;;;;
+114A;HANGUL CHOSEONG IEUNG-THIEUTH;Lo;0;L;;;;;N;;;;;
+114B;HANGUL CHOSEONG IEUNG-PHIEUPH;Lo;0;L;;;;;N;;;;;
+114C;HANGUL CHOSEONG YESIEUNG;Lo;0;L;;;;;N;;;;;
+114D;HANGUL CHOSEONG CIEUC-IEUNG;Lo;0;L;;;;;N;;;;;
+114E;HANGUL CHOSEONG CHITUEUMCIEUC;Lo;0;L;;;;;N;;;;;
+114F;HANGUL CHOSEONG CHITUEUMSSANGCIEUC;Lo;0;L;;;;;N;;;;;
+1150;HANGUL CHOSEONG CEONGCHIEUMCIEUC;Lo;0;L;;;;;N;;;;;
+1151;HANGUL CHOSEONG CEONGCHIEUMSSANGCIEUC;Lo;0;L;;;;;N;;;;;
+1152;HANGUL CHOSEONG CHIEUCH-KHIEUKH;Lo;0;L;;;;;N;;;;;
+1153;HANGUL CHOSEONG CHIEUCH-HIEUH;Lo;0;L;;;;;N;;;;;
+1154;HANGUL CHOSEONG CHITUEUMCHIEUCH;Lo;0;L;;;;;N;;;;;
+1155;HANGUL CHOSEONG CEONGCHIEUMCHIEUCH;Lo;0;L;;;;;N;;;;;
+1156;HANGUL CHOSEONG PHIEUPH-PIEUP;Lo;0;L;;;;;N;;;;;
+1157;HANGUL CHOSEONG KAPYEOUNPHIEUPH;Lo;0;L;;;;;N;;;;;
+1158;HANGUL CHOSEONG SSANGHIEUH;Lo;0;L;;;;;N;;;;;
+1159;HANGUL CHOSEONG YEORINHIEUH;Lo;0;L;;;;;N;;;;;
+115F;HANGUL CHOSEONG FILLER;Lo;0;L;;;;;N;;;;;
+1160;HANGUL JUNGSEONG FILLER;Lo;0;L;;;;;N;;;;;
+1161;HANGUL JUNGSEONG A;Lo;0;L;;;;;N;;;;;
+1162;HANGUL JUNGSEONG AE;Lo;0;L;;;;;N;;;;;
+1163;HANGUL JUNGSEONG YA;Lo;0;L;;;;;N;;;;;
+1164;HANGUL JUNGSEONG YAE;Lo;0;L;;;;;N;;;;;
+1165;HANGUL JUNGSEONG EO;Lo;0;L;;;;;N;;;;;
+1166;HANGUL JUNGSEONG E;Lo;0;L;;;;;N;;;;;
+1167;HANGUL JUNGSEONG YEO;Lo;0;L;;;;;N;;;;;
+1168;HANGUL JUNGSEONG YE;Lo;0;L;;;;;N;;;;;
+1169;HANGUL JUNGSEONG O;Lo;0;L;;;;;N;;;;;
+116A;HANGUL JUNGSEONG WA;Lo;0;L;;;;;N;;;;;
+116B;HANGUL JUNGSEONG WAE;Lo;0;L;;;;;N;;;;;
+116C;HANGUL JUNGSEONG OE;Lo;0;L;;;;;N;;;;;
+116D;HANGUL JUNGSEONG YO;Lo;0;L;;;;;N;;;;;
+116E;HANGUL JUNGSEONG U;Lo;0;L;;;;;N;;;;;
+116F;HANGUL JUNGSEONG WEO;Lo;0;L;;;;;N;;;;;
+1170;HANGUL JUNGSEONG WE;Lo;0;L;;;;;N;;;;;
+1171;HANGUL JUNGSEONG WI;Lo;0;L;;;;;N;;;;;
+1172;HANGUL JUNGSEONG YU;Lo;0;L;;;;;N;;;;;
+1173;HANGUL JUNGSEONG EU;Lo;0;L;;;;;N;;;;;
+1174;HANGUL JUNGSEONG YI;Lo;0;L;;;;;N;;;;;
+1175;HANGUL JUNGSEONG I;Lo;0;L;;;;;N;;;;;
+1176;HANGUL JUNGSEONG A-O;Lo;0;L;;;;;N;;;;;
+1177;HANGUL JUNGSEONG A-U;Lo;0;L;;;;;N;;;;;
+1178;HANGUL JUNGSEONG YA-O;Lo;0;L;;;;;N;;;;;
+1179;HANGUL JUNGSEONG YA-YO;Lo;0;L;;;;;N;;;;;
+117A;HANGUL JUNGSEONG EO-O;Lo;0;L;;;;;N;;;;;
+117B;HANGUL JUNGSEONG EO-U;Lo;0;L;;;;;N;;;;;
+117C;HANGUL JUNGSEONG EO-EU;Lo;0;L;;;;;N;;;;;
+117D;HANGUL JUNGSEONG YEO-O;Lo;0;L;;;;;N;;;;;
+117E;HANGUL JUNGSEONG YEO-U;Lo;0;L;;;;;N;;;;;
+117F;HANGUL JUNGSEONG O-EO;Lo;0;L;;;;;N;;;;;
+1180;HANGUL JUNGSEONG O-E;Lo;0;L;;;;;N;;;;;
+1181;HANGUL JUNGSEONG O-YE;Lo;0;L;;;;;N;;;;;
+1182;HANGUL JUNGSEONG O-O;Lo;0;L;;;;;N;;;;;
+1183;HANGUL JUNGSEONG O-U;Lo;0;L;;;;;N;;;;;
+1184;HANGUL JUNGSEONG YO-YA;Lo;0;L;;;;;N;;;;;
+1185;HANGUL JUNGSEONG YO-YAE;Lo;0;L;;;;;N;;;;;
+1186;HANGUL JUNGSEONG YO-YEO;Lo;0;L;;;;;N;;;;;
+1187;HANGUL JUNGSEONG YO-O;Lo;0;L;;;;;N;;;;;
+1188;HANGUL JUNGSEONG YO-I;Lo;0;L;;;;;N;;;;;
+1189;HANGUL JUNGSEONG U-A;Lo;0;L;;;;;N;;;;;
+118A;HANGUL JUNGSEONG U-AE;Lo;0;L;;;;;N;;;;;
+118B;HANGUL JUNGSEONG U-EO-EU;Lo;0;L;;;;;N;;;;;
+118C;HANGUL JUNGSEONG U-YE;Lo;0;L;;;;;N;;;;;
+118D;HANGUL JUNGSEONG U-U;Lo;0;L;;;;;N;;;;;
+118E;HANGUL JUNGSEONG YU-A;Lo;0;L;;;;;N;;;;;
+118F;HANGUL JUNGSEONG YU-EO;Lo;0;L;;;;;N;;;;;
+1190;HANGUL JUNGSEONG YU-E;Lo;0;L;;;;;N;;;;;
+1191;HANGUL JUNGSEONG YU-YEO;Lo;0;L;;;;;N;;;;;
+1192;HANGUL JUNGSEONG YU-YE;Lo;0;L;;;;;N;;;;;
+1193;HANGUL JUNGSEONG YU-U;Lo;0;L;;;;;N;;;;;
+1194;HANGUL JUNGSEONG YU-I;Lo;0;L;;;;;N;;;;;
+1195;HANGUL JUNGSEONG EU-U;Lo;0;L;;;;;N;;;;;
+1196;HANGUL JUNGSEONG EU-EU;Lo;0;L;;;;;N;;;;;
+1197;HANGUL JUNGSEONG YI-U;Lo;0;L;;;;;N;;;;;
+1198;HANGUL JUNGSEONG I-A;Lo;0;L;;;;;N;;;;;
+1199;HANGUL JUNGSEONG I-YA;Lo;0;L;;;;;N;;;;;
+119A;HANGUL JUNGSEONG I-O;Lo;0;L;;;;;N;;;;;
+119B;HANGUL JUNGSEONG I-U;Lo;0;L;;;;;N;;;;;
+119C;HANGUL JUNGSEONG I-EU;Lo;0;L;;;;;N;;;;;
+119D;HANGUL JUNGSEONG I-ARAEA;Lo;0;L;;;;;N;;;;;
+119E;HANGUL JUNGSEONG ARAEA;Lo;0;L;;;;;N;;;;;
+119F;HANGUL JUNGSEONG ARAEA-EO;Lo;0;L;;;;;N;;;;;
+11A0;HANGUL JUNGSEONG ARAEA-U;Lo;0;L;;;;;N;;;;;
+11A1;HANGUL JUNGSEONG ARAEA-I;Lo;0;L;;;;;N;;;;;
+11A2;HANGUL JUNGSEONG SSANGARAEA;Lo;0;L;;;;;N;;;;;
+11A8;HANGUL JONGSEONG KIYEOK;Lo;0;L;;;;;N;;g *;;;
+11A9;HANGUL JONGSEONG SSANGKIYEOK;Lo;0;L;;;;;N;;gg *;;;
+11AA;HANGUL JONGSEONG KIYEOK-SIOS;Lo;0;L;;;;;N;;gs *;;;
+11AB;HANGUL JONGSEONG NIEUN;Lo;0;L;;;;;N;;n *;;;
+11AC;HANGUL JONGSEONG NIEUN-CIEUC;Lo;0;L;;;;;N;;nj *;;;
+11AD;HANGUL JONGSEONG NIEUN-HIEUH;Lo;0;L;;;;;N;;nh *;;;
+11AE;HANGUL JONGSEONG TIKEUT;Lo;0;L;;;;;N;;d *;;;
+11AF;HANGUL JONGSEONG RIEUL;Lo;0;L;;;;;N;;l *;;;
+11B0;HANGUL JONGSEONG RIEUL-KIYEOK;Lo;0;L;;;;;N;;lg *;;;
+11B1;HANGUL JONGSEONG RIEUL-MIEUM;Lo;0;L;;;;;N;;lm *;;;
+11B2;HANGUL JONGSEONG RIEUL-PIEUP;Lo;0;L;;;;;N;;lb *;;;
+11B3;HANGUL JONGSEONG RIEUL-SIOS;Lo;0;L;;;;;N;;ls *;;;
+11B4;HANGUL JONGSEONG RIEUL-THIEUTH;Lo;0;L;;;;;N;;lt *;;;
+11B5;HANGUL JONGSEONG RIEUL-PHIEUPH;Lo;0;L;;;;;N;;lp *;;;
+11B6;HANGUL JONGSEONG RIEUL-HIEUH;Lo;0;L;;;;;N;;lh *;;;
+11B7;HANGUL JONGSEONG MIEUM;Lo;0;L;;;;;N;;m *;;;
+11B8;HANGUL JONGSEONG PIEUP;Lo;0;L;;;;;N;;b *;;;
+11B9;HANGUL JONGSEONG PIEUP-SIOS;Lo;0;L;;;;;N;;bs *;;;
+11BA;HANGUL JONGSEONG SIOS;Lo;0;L;;;;;N;;s *;;;
+11BB;HANGUL JONGSEONG SSANGSIOS;Lo;0;L;;;;;N;;ss *;;;
+11BC;HANGUL JONGSEONG IEUNG;Lo;0;L;;;;;N;;ng *;;;
+11BD;HANGUL JONGSEONG CIEUC;Lo;0;L;;;;;N;;j *;;;
+11BE;HANGUL JONGSEONG CHIEUCH;Lo;0;L;;;;;N;;c *;;;
+11BF;HANGUL JONGSEONG KHIEUKH;Lo;0;L;;;;;N;;k *;;;
+11C0;HANGUL JONGSEONG THIEUTH;Lo;0;L;;;;;N;;t *;;;
+11C1;HANGUL JONGSEONG PHIEUPH;Lo;0;L;;;;;N;;p *;;;
+11C2;HANGUL JONGSEONG HIEUH;Lo;0;L;;;;;N;;h *;;;
+11C3;HANGUL JONGSEONG KIYEOK-RIEUL;Lo;0;L;;;;;N;;;;;
+11C4;HANGUL JONGSEONG KIYEOK-SIOS-KIYEOK;Lo;0;L;;;;;N;;;;;
+11C5;HANGUL JONGSEONG NIEUN-KIYEOK;Lo;0;L;;;;;N;;;;;
+11C6;HANGUL JONGSEONG NIEUN-TIKEUT;Lo;0;L;;;;;N;;;;;
+11C7;HANGUL JONGSEONG NIEUN-SIOS;Lo;0;L;;;;;N;;;;;
+11C8;HANGUL JONGSEONG NIEUN-PANSIOS;Lo;0;L;;;;;N;;;;;
+11C9;HANGUL JONGSEONG NIEUN-THIEUTH;Lo;0;L;;;;;N;;;;;
+11CA;HANGUL JONGSEONG TIKEUT-KIYEOK;Lo;0;L;;;;;N;;;;;
+11CB;HANGUL JONGSEONG TIKEUT-RIEUL;Lo;0;L;;;;;N;;;;;
+11CC;HANGUL JONGSEONG RIEUL-KIYEOK-SIOS;Lo;0;L;;;;;N;;;;;
+11CD;HANGUL JONGSEONG RIEUL-NIEUN;Lo;0;L;;;;;N;;;;;
+11CE;HANGUL JONGSEONG RIEUL-TIKEUT;Lo;0;L;;;;;N;;;;;
+11CF;HANGUL JONGSEONG RIEUL-TIKEUT-HIEUH;Lo;0;L;;;;;N;;;;;
+11D0;HANGUL JONGSEONG SSANGRIEUL;Lo;0;L;;;;;N;;;;;
+11D1;HANGUL JONGSEONG RIEUL-MIEUM-KIYEOK;Lo;0;L;;;;;N;;;;;
+11D2;HANGUL JONGSEONG RIEUL-MIEUM-SIOS;Lo;0;L;;;;;N;;;;;
+11D3;HANGUL JONGSEONG RIEUL-PIEUP-SIOS;Lo;0;L;;;;;N;;;;;
+11D4;HANGUL JONGSEONG RIEUL-PIEUP-HIEUH;Lo;0;L;;;;;N;;;;;
+11D5;HANGUL JONGSEONG RIEUL-KAPYEOUNPIEUP;Lo;0;L;;;;;N;;;;;
+11D6;HANGUL JONGSEONG RIEUL-SSANGSIOS;Lo;0;L;;;;;N;;;;;
+11D7;HANGUL JONGSEONG RIEUL-PANSIOS;Lo;0;L;;;;;N;;;;;
+11D8;HANGUL JONGSEONG RIEUL-KHIEUKH;Lo;0;L;;;;;N;;;;;
+11D9;HANGUL JONGSEONG RIEUL-YEORINHIEUH;Lo;0;L;;;;;N;;;;;
+11DA;HANGUL JONGSEONG MIEUM-KIYEOK;Lo;0;L;;;;;N;;;;;
+11DB;HANGUL JONGSEONG MIEUM-RIEUL;Lo;0;L;;;;;N;;;;;
+11DC;HANGUL JONGSEONG MIEUM-PIEUP;Lo;0;L;;;;;N;;;;;
+11DD;HANGUL JONGSEONG MIEUM-SIOS;Lo;0;L;;;;;N;;;;;
+11DE;HANGUL JONGSEONG MIEUM-SSANGSIOS;Lo;0;L;;;;;N;;;;;
+11DF;HANGUL JONGSEONG MIEUM-PANSIOS;Lo;0;L;;;;;N;;;;;
+11E0;HANGUL JONGSEONG MIEUM-CHIEUCH;Lo;0;L;;;;;N;;;;;
+11E1;HANGUL JONGSEONG MIEUM-HIEUH;Lo;0;L;;;;;N;;;;;
+11E2;HANGUL JONGSEONG KAPYEOUNMIEUM;Lo;0;L;;;;;N;;;;;
+11E3;HANGUL JONGSEONG PIEUP-RIEUL;Lo;0;L;;;;;N;;;;;
+11E4;HANGUL JONGSEONG PIEUP-PHIEUPH;Lo;0;L;;;;;N;;;;;
+11E5;HANGUL JONGSEONG PIEUP-HIEUH;Lo;0;L;;;;;N;;;;;
+11E6;HANGUL JONGSEONG KAPYEOUNPIEUP;Lo;0;L;;;;;N;;;;;
+11E7;HANGUL JONGSEONG SIOS-KIYEOK;Lo;0;L;;;;;N;;;;;
+11E8;HANGUL JONGSEONG SIOS-TIKEUT;Lo;0;L;;;;;N;;;;;
+11E9;HANGUL JONGSEONG SIOS-RIEUL;Lo;0;L;;;;;N;;;;;
+11EA;HANGUL JONGSEONG SIOS-PIEUP;Lo;0;L;;;;;N;;;;;
+11EB;HANGUL JONGSEONG PANSIOS;Lo;0;L;;;;;N;;;;;
+11EC;HANGUL JONGSEONG IEUNG-KIYEOK;Lo;0;L;;;;;N;;;;;
+11ED;HANGUL JONGSEONG IEUNG-SSANGKIYEOK;Lo;0;L;;;;;N;;;;;
+11EE;HANGUL JONGSEONG SSANGIEUNG;Lo;0;L;;;;;N;;;;;
+11EF;HANGUL JONGSEONG IEUNG-KHIEUKH;Lo;0;L;;;;;N;;;;;
+11F0;HANGUL JONGSEONG YESIEUNG;Lo;0;L;;;;;N;;;;;
+11F1;HANGUL JONGSEONG YESIEUNG-SIOS;Lo;0;L;;;;;N;;;;;
+11F2;HANGUL JONGSEONG YESIEUNG-PANSIOS;Lo;0;L;;;;;N;;;;;
+11F3;HANGUL JONGSEONG PHIEUPH-PIEUP;Lo;0;L;;;;;N;;;;;
+11F4;HANGUL JONGSEONG KAPYEOUNPHIEUPH;Lo;0;L;;;;;N;;;;;
+11F5;HANGUL JONGSEONG HIEUH-NIEUN;Lo;0;L;;;;;N;;;;;
+11F6;HANGUL JONGSEONG HIEUH-RIEUL;Lo;0;L;;;;;N;;;;;
+11F7;HANGUL JONGSEONG HIEUH-MIEUM;Lo;0;L;;;;;N;;;;;
+11F8;HANGUL JONGSEONG HIEUH-PIEUP;Lo;0;L;;;;;N;;;;;
+11F9;HANGUL JONGSEONG YEORINHIEUH;Lo;0;L;;;;;N;;;;;
+1200;ETHIOPIC SYLLABLE HA;Lo;0;L;;;;;N;;;;;
+1201;ETHIOPIC SYLLABLE HU;Lo;0;L;;;;;N;;;;;
+1202;ETHIOPIC SYLLABLE HI;Lo;0;L;;;;;N;;;;;
+1203;ETHIOPIC SYLLABLE HAA;Lo;0;L;;;;;N;;;;;
+1204;ETHIOPIC SYLLABLE HEE;Lo;0;L;;;;;N;;;;;
+1205;ETHIOPIC SYLLABLE HE;Lo;0;L;;;;;N;;;;;
+1206;ETHIOPIC SYLLABLE HO;Lo;0;L;;;;;N;;;;;
+1208;ETHIOPIC SYLLABLE LA;Lo;0;L;;;;;N;;;;;
+1209;ETHIOPIC SYLLABLE LU;Lo;0;L;;;;;N;;;;;
+120A;ETHIOPIC SYLLABLE LI;Lo;0;L;;;;;N;;;;;
+120B;ETHIOPIC SYLLABLE LAA;Lo;0;L;;;;;N;;;;;
+120C;ETHIOPIC SYLLABLE LEE;Lo;0;L;;;;;N;;;;;
+120D;ETHIOPIC SYLLABLE LE;Lo;0;L;;;;;N;;;;;
+120E;ETHIOPIC SYLLABLE LO;Lo;0;L;;;;;N;;;;;
+120F;ETHIOPIC SYLLABLE LWA;Lo;0;L;;;;;N;;;;;
+1210;ETHIOPIC SYLLABLE HHA;Lo;0;L;;;;;N;;;;;
+1211;ETHIOPIC SYLLABLE HHU;Lo;0;L;;;;;N;;;;;
+1212;ETHIOPIC SYLLABLE HHI;Lo;0;L;;;;;N;;;;;
+1213;ETHIOPIC SYLLABLE HHAA;Lo;0;L;;;;;N;;;;;
+1214;ETHIOPIC SYLLABLE HHEE;Lo;0;L;;;;;N;;;;;
+1215;ETHIOPIC SYLLABLE HHE;Lo;0;L;;;;;N;;;;;
+1216;ETHIOPIC SYLLABLE HHO;Lo;0;L;;;;;N;;;;;
+1217;ETHIOPIC SYLLABLE HHWA;Lo;0;L;;;;;N;;;;;
+1218;ETHIOPIC SYLLABLE MA;Lo;0;L;;;;;N;;;;;
+1219;ETHIOPIC SYLLABLE MU;Lo;0;L;;;;;N;;;;;
+121A;ETHIOPIC SYLLABLE MI;Lo;0;L;;;;;N;;;;;
+121B;ETHIOPIC SYLLABLE MAA;Lo;0;L;;;;;N;;;;;
+121C;ETHIOPIC SYLLABLE MEE;Lo;0;L;;;;;N;;;;;
+121D;ETHIOPIC SYLLABLE ME;Lo;0;L;;;;;N;;;;;
+121E;ETHIOPIC SYLLABLE MO;Lo;0;L;;;;;N;;;;;
+121F;ETHIOPIC SYLLABLE MWA;Lo;0;L;;;;;N;;;;;
+1220;ETHIOPIC SYLLABLE SZA;Lo;0;L;;;;;N;;;;;
+1221;ETHIOPIC SYLLABLE SZU;Lo;0;L;;;;;N;;;;;
+1222;ETHIOPIC SYLLABLE SZI;Lo;0;L;;;;;N;;;;;
+1223;ETHIOPIC SYLLABLE SZAA;Lo;0;L;;;;;N;;;;;
+1224;ETHIOPIC SYLLABLE SZEE;Lo;0;L;;;;;N;;;;;
+1225;ETHIOPIC SYLLABLE SZE;Lo;0;L;;;;;N;;;;;
+1226;ETHIOPIC SYLLABLE SZO;Lo;0;L;;;;;N;;;;;
+1227;ETHIOPIC SYLLABLE SZWA;Lo;0;L;;;;;N;;;;;
+1228;ETHIOPIC SYLLABLE RA;Lo;0;L;;;;;N;;;;;
+1229;ETHIOPIC SYLLABLE RU;Lo;0;L;;;;;N;;;;;
+122A;ETHIOPIC SYLLABLE RI;Lo;0;L;;;;;N;;;;;
+122B;ETHIOPIC SYLLABLE RAA;Lo;0;L;;;;;N;;;;;
+122C;ETHIOPIC SYLLABLE REE;Lo;0;L;;;;;N;;;;;
+122D;ETHIOPIC SYLLABLE RE;Lo;0;L;;;;;N;;;;;
+122E;ETHIOPIC SYLLABLE RO;Lo;0;L;;;;;N;;;;;
+122F;ETHIOPIC SYLLABLE RWA;Lo;0;L;;;;;N;;;;;
+1230;ETHIOPIC SYLLABLE SA;Lo;0;L;;;;;N;;;;;
+1231;ETHIOPIC SYLLABLE SU;Lo;0;L;;;;;N;;;;;
+1232;ETHIOPIC SYLLABLE SI;Lo;0;L;;;;;N;;;;;
+1233;ETHIOPIC SYLLABLE SAA;Lo;0;L;;;;;N;;;;;
+1234;ETHIOPIC SYLLABLE SEE;Lo;0;L;;;;;N;;;;;
+1235;ETHIOPIC SYLLABLE SE;Lo;0;L;;;;;N;;;;;
+1236;ETHIOPIC SYLLABLE SO;Lo;0;L;;;;;N;;;;;
+1237;ETHIOPIC SYLLABLE SWA;Lo;0;L;;;;;N;;;;;
+1238;ETHIOPIC SYLLABLE SHA;Lo;0;L;;;;;N;;;;;
+1239;ETHIOPIC SYLLABLE SHU;Lo;0;L;;;;;N;;;;;
+123A;ETHIOPIC SYLLABLE SHI;Lo;0;L;;;;;N;;;;;
+123B;ETHIOPIC SYLLABLE SHAA;Lo;0;L;;;;;N;;;;;
+123C;ETHIOPIC SYLLABLE SHEE;Lo;0;L;;;;;N;;;;;
+123D;ETHIOPIC SYLLABLE SHE;Lo;0;L;;;;;N;;;;;
+123E;ETHIOPIC SYLLABLE SHO;Lo;0;L;;;;;N;;;;;
+123F;ETHIOPIC SYLLABLE SHWA;Lo;0;L;;;;;N;;;;;
+1240;ETHIOPIC SYLLABLE QA;Lo;0;L;;;;;N;;;;;
+1241;ETHIOPIC SYLLABLE QU;Lo;0;L;;;;;N;;;;;
+1242;ETHIOPIC SYLLABLE QI;Lo;0;L;;;;;N;;;;;
+1243;ETHIOPIC SYLLABLE QAA;Lo;0;L;;;;;N;;;;;
+1244;ETHIOPIC SYLLABLE QEE;Lo;0;L;;;;;N;;;;;
+1245;ETHIOPIC SYLLABLE QE;Lo;0;L;;;;;N;;;;;
+1246;ETHIOPIC SYLLABLE QO;Lo;0;L;;;;;N;;;;;
+1248;ETHIOPIC SYLLABLE QWA;Lo;0;L;;;;;N;;;;;
+124A;ETHIOPIC SYLLABLE QWI;Lo;0;L;;;;;N;;;;;
+124B;ETHIOPIC SYLLABLE QWAA;Lo;0;L;;;;;N;;;;;
+124C;ETHIOPIC SYLLABLE QWEE;Lo;0;L;;;;;N;;;;;
+124D;ETHIOPIC SYLLABLE QWE;Lo;0;L;;;;;N;;;;;
+1250;ETHIOPIC SYLLABLE QHA;Lo;0;L;;;;;N;;;;;
+1251;ETHIOPIC SYLLABLE QHU;Lo;0;L;;;;;N;;;;;
+1252;ETHIOPIC SYLLABLE QHI;Lo;0;L;;;;;N;;;;;
+1253;ETHIOPIC SYLLABLE QHAA;Lo;0;L;;;;;N;;;;;
+1254;ETHIOPIC SYLLABLE QHEE;Lo;0;L;;;;;N;;;;;
+1255;ETHIOPIC SYLLABLE QHE;Lo;0;L;;;;;N;;;;;
+1256;ETHIOPIC SYLLABLE QHO;Lo;0;L;;;;;N;;;;;
+1258;ETHIOPIC SYLLABLE QHWA;Lo;0;L;;;;;N;;;;;
+125A;ETHIOPIC SYLLABLE QHWI;Lo;0;L;;;;;N;;;;;
+125B;ETHIOPIC SYLLABLE QHWAA;Lo;0;L;;;;;N;;;;;
+125C;ETHIOPIC SYLLABLE QHWEE;Lo;0;L;;;;;N;;;;;
+125D;ETHIOPIC SYLLABLE QHWE;Lo;0;L;;;;;N;;;;;
+1260;ETHIOPIC SYLLABLE BA;Lo;0;L;;;;;N;;;;;
+1261;ETHIOPIC SYLLABLE BU;Lo;0;L;;;;;N;;;;;
+1262;ETHIOPIC SYLLABLE BI;Lo;0;L;;;;;N;;;;;
+1263;ETHIOPIC SYLLABLE BAA;Lo;0;L;;;;;N;;;;;
+1264;ETHIOPIC SYLLABLE BEE;Lo;0;L;;;;;N;;;;;
+1265;ETHIOPIC SYLLABLE BE;Lo;0;L;;;;;N;;;;;
+1266;ETHIOPIC SYLLABLE BO;Lo;0;L;;;;;N;;;;;
+1267;ETHIOPIC SYLLABLE BWA;Lo;0;L;;;;;N;;;;;
+1268;ETHIOPIC SYLLABLE VA;Lo;0;L;;;;;N;;;;;
+1269;ETHIOPIC SYLLABLE VU;Lo;0;L;;;;;N;;;;;
+126A;ETHIOPIC SYLLABLE VI;Lo;0;L;;;;;N;;;;;
+126B;ETHIOPIC SYLLABLE VAA;Lo;0;L;;;;;N;;;;;
+126C;ETHIOPIC SYLLABLE VEE;Lo;0;L;;;;;N;;;;;
+126D;ETHIOPIC SYLLABLE VE;Lo;0;L;;;;;N;;;;;
+126E;ETHIOPIC SYLLABLE VO;Lo;0;L;;;;;N;;;;;
+126F;ETHIOPIC SYLLABLE VWA;Lo;0;L;;;;;N;;;;;
+1270;ETHIOPIC SYLLABLE TA;Lo;0;L;;;;;N;;;;;
+1271;ETHIOPIC SYLLABLE TU;Lo;0;L;;;;;N;;;;;
+1272;ETHIOPIC SYLLABLE TI;Lo;0;L;;;;;N;;;;;
+1273;ETHIOPIC SYLLABLE TAA;Lo;0;L;;;;;N;;;;;
+1274;ETHIOPIC SYLLABLE TEE;Lo;0;L;;;;;N;;;;;
+1275;ETHIOPIC SYLLABLE TE;Lo;0;L;;;;;N;;;;;
+1276;ETHIOPIC SYLLABLE TO;Lo;0;L;;;;;N;;;;;
+1277;ETHIOPIC SYLLABLE TWA;Lo;0;L;;;;;N;;;;;
+1278;ETHIOPIC SYLLABLE CA;Lo;0;L;;;;;N;;;;;
+1279;ETHIOPIC SYLLABLE CU;Lo;0;L;;;;;N;;;;;
+127A;ETHIOPIC SYLLABLE CI;Lo;0;L;;;;;N;;;;;
+127B;ETHIOPIC SYLLABLE CAA;Lo;0;L;;;;;N;;;;;
+127C;ETHIOPIC SYLLABLE CEE;Lo;0;L;;;;;N;;;;;
+127D;ETHIOPIC SYLLABLE CE;Lo;0;L;;;;;N;;;;;
+127E;ETHIOPIC SYLLABLE CO;Lo;0;L;;;;;N;;;;;
+127F;ETHIOPIC SYLLABLE CWA;Lo;0;L;;;;;N;;;;;
+1280;ETHIOPIC SYLLABLE XA;Lo;0;L;;;;;N;;;;;
+1281;ETHIOPIC SYLLABLE XU;Lo;0;L;;;;;N;;;;;
+1282;ETHIOPIC SYLLABLE XI;Lo;0;L;;;;;N;;;;;
+1283;ETHIOPIC SYLLABLE XAA;Lo;0;L;;;;;N;;;;;
+1284;ETHIOPIC SYLLABLE XEE;Lo;0;L;;;;;N;;;;;
+1285;ETHIOPIC SYLLABLE XE;Lo;0;L;;;;;N;;;;;
+1286;ETHIOPIC SYLLABLE XO;Lo;0;L;;;;;N;;;;;
+1288;ETHIOPIC SYLLABLE XWA;Lo;0;L;;;;;N;;;;;
+128A;ETHIOPIC SYLLABLE XWI;Lo;0;L;;;;;N;;;;;
+128B;ETHIOPIC SYLLABLE XWAA;Lo;0;L;;;;;N;;;;;
+128C;ETHIOPIC SYLLABLE XWEE;Lo;0;L;;;;;N;;;;;
+128D;ETHIOPIC SYLLABLE XWE;Lo;0;L;;;;;N;;;;;
+1290;ETHIOPIC SYLLABLE NA;Lo;0;L;;;;;N;;;;;
+1291;ETHIOPIC SYLLABLE NU;Lo;0;L;;;;;N;;;;;
+1292;ETHIOPIC SYLLABLE NI;Lo;0;L;;;;;N;;;;;
+1293;ETHIOPIC SYLLABLE NAA;Lo;0;L;;;;;N;;;;;
+1294;ETHIOPIC SYLLABLE NEE;Lo;0;L;;;;;N;;;;;
+1295;ETHIOPIC SYLLABLE NE;Lo;0;L;;;;;N;;;;;
+1296;ETHIOPIC SYLLABLE NO;Lo;0;L;;;;;N;;;;;
+1297;ETHIOPIC SYLLABLE NWA;Lo;0;L;;;;;N;;;;;
+1298;ETHIOPIC SYLLABLE NYA;Lo;0;L;;;;;N;;;;;
+1299;ETHIOPIC SYLLABLE NYU;Lo;0;L;;;;;N;;;;;
+129A;ETHIOPIC SYLLABLE NYI;Lo;0;L;;;;;N;;;;;
+129B;ETHIOPIC SYLLABLE NYAA;Lo;0;L;;;;;N;;;;;
+129C;ETHIOPIC SYLLABLE NYEE;Lo;0;L;;;;;N;;;;;
+129D;ETHIOPIC SYLLABLE NYE;Lo;0;L;;;;;N;;;;;
+129E;ETHIOPIC SYLLABLE NYO;Lo;0;L;;;;;N;;;;;
+129F;ETHIOPIC SYLLABLE NYWA;Lo;0;L;;;;;N;;;;;
+12A0;ETHIOPIC SYLLABLE GLOTTAL A;Lo;0;L;;;;;N;;;;;
+12A1;ETHIOPIC SYLLABLE GLOTTAL U;Lo;0;L;;;;;N;;;;;
+12A2;ETHIOPIC SYLLABLE GLOTTAL I;Lo;0;L;;;;;N;;;;;
+12A3;ETHIOPIC SYLLABLE GLOTTAL AA;Lo;0;L;;;;;N;;;;;
+12A4;ETHIOPIC SYLLABLE GLOTTAL EE;Lo;0;L;;;;;N;;;;;
+12A5;ETHIOPIC SYLLABLE GLOTTAL E;Lo;0;L;;;;;N;;;;;
+12A6;ETHIOPIC SYLLABLE GLOTTAL O;Lo;0;L;;;;;N;;;;;
+12A7;ETHIOPIC SYLLABLE GLOTTAL WA;Lo;0;L;;;;;N;;;;;
+12A8;ETHIOPIC SYLLABLE KA;Lo;0;L;;;;;N;;;;;
+12A9;ETHIOPIC SYLLABLE KU;Lo;0;L;;;;;N;;;;;
+12AA;ETHIOPIC SYLLABLE KI;Lo;0;L;;;;;N;;;;;
+12AB;ETHIOPIC SYLLABLE KAA;Lo;0;L;;;;;N;;;;;
+12AC;ETHIOPIC SYLLABLE KEE;Lo;0;L;;;;;N;;;;;
+12AD;ETHIOPIC SYLLABLE KE;Lo;0;L;;;;;N;;;;;
+12AE;ETHIOPIC SYLLABLE KO;Lo;0;L;;;;;N;;;;;
+12B0;ETHIOPIC SYLLABLE KWA;Lo;0;L;;;;;N;;;;;
+12B2;ETHIOPIC SYLLABLE KWI;Lo;0;L;;;;;N;;;;;
+12B3;ETHIOPIC SYLLABLE KWAA;Lo;0;L;;;;;N;;;;;
+12B4;ETHIOPIC SYLLABLE KWEE;Lo;0;L;;;;;N;;;;;
+12B5;ETHIOPIC SYLLABLE KWE;Lo;0;L;;;;;N;;;;;
+12B8;ETHIOPIC SYLLABLE KXA;Lo;0;L;;;;;N;;;;;
+12B9;ETHIOPIC SYLLABLE KXU;Lo;0;L;;;;;N;;;;;
+12BA;ETHIOPIC SYLLABLE KXI;Lo;0;L;;;;;N;;;;;
+12BB;ETHIOPIC SYLLABLE KXAA;Lo;0;L;;;;;N;;;;;
+12BC;ETHIOPIC SYLLABLE KXEE;Lo;0;L;;;;;N;;;;;
+12BD;ETHIOPIC SYLLABLE KXE;Lo;0;L;;;;;N;;;;;
+12BE;ETHIOPIC SYLLABLE KXO;Lo;0;L;;;;;N;;;;;
+12C0;ETHIOPIC SYLLABLE KXWA;Lo;0;L;;;;;N;;;;;
+12C2;ETHIOPIC SYLLABLE KXWI;Lo;0;L;;;;;N;;;;;
+12C3;ETHIOPIC SYLLABLE KXWAA;Lo;0;L;;;;;N;;;;;
+12C4;ETHIOPIC SYLLABLE KXWEE;Lo;0;L;;;;;N;;;;;
+12C5;ETHIOPIC SYLLABLE KXWE;Lo;0;L;;;;;N;;;;;
+12C8;ETHIOPIC SYLLABLE WA;Lo;0;L;;;;;N;;;;;
+12C9;ETHIOPIC SYLLABLE WU;Lo;0;L;;;;;N;;;;;
+12CA;ETHIOPIC SYLLABLE WI;Lo;0;L;;;;;N;;;;;
+12CB;ETHIOPIC SYLLABLE WAA;Lo;0;L;;;;;N;;;;;
+12CC;ETHIOPIC SYLLABLE WEE;Lo;0;L;;;;;N;;;;;
+12CD;ETHIOPIC SYLLABLE WE;Lo;0;L;;;;;N;;;;;
+12CE;ETHIOPIC SYLLABLE WO;Lo;0;L;;;;;N;;;;;
+12D0;ETHIOPIC SYLLABLE PHARYNGEAL A;Lo;0;L;;;;;N;;;;;
+12D1;ETHIOPIC SYLLABLE PHARYNGEAL U;Lo;0;L;;;;;N;;;;;
+12D2;ETHIOPIC SYLLABLE PHARYNGEAL I;Lo;0;L;;;;;N;;;;;
+12D3;ETHIOPIC SYLLABLE PHARYNGEAL AA;Lo;0;L;;;;;N;;;;;
+12D4;ETHIOPIC SYLLABLE PHARYNGEAL EE;Lo;0;L;;;;;N;;;;;
+12D5;ETHIOPIC SYLLABLE PHARYNGEAL E;Lo;0;L;;;;;N;;;;;
+12D6;ETHIOPIC SYLLABLE PHARYNGEAL O;Lo;0;L;;;;;N;;;;;
+12D8;ETHIOPIC SYLLABLE ZA;Lo;0;L;;;;;N;;;;;
+12D9;ETHIOPIC SYLLABLE ZU;Lo;0;L;;;;;N;;;;;
+12DA;ETHIOPIC SYLLABLE ZI;Lo;0;L;;;;;N;;;;;
+12DB;ETHIOPIC SYLLABLE ZAA;Lo;0;L;;;;;N;;;;;
+12DC;ETHIOPIC SYLLABLE ZEE;Lo;0;L;;;;;N;;;;;
+12DD;ETHIOPIC SYLLABLE ZE;Lo;0;L;;;;;N;;;;;
+12DE;ETHIOPIC SYLLABLE ZO;Lo;0;L;;;;;N;;;;;
+12DF;ETHIOPIC SYLLABLE ZWA;Lo;0;L;;;;;N;;;;;
+12E0;ETHIOPIC SYLLABLE ZHA;Lo;0;L;;;;;N;;;;;
+12E1;ETHIOPIC SYLLABLE ZHU;Lo;0;L;;;;;N;;;;;
+12E2;ETHIOPIC SYLLABLE ZHI;Lo;0;L;;;;;N;;;;;
+12E3;ETHIOPIC SYLLABLE ZHAA;Lo;0;L;;;;;N;;;;;
+12E4;ETHIOPIC SYLLABLE ZHEE;Lo;0;L;;;;;N;;;;;
+12E5;ETHIOPIC SYLLABLE ZHE;Lo;0;L;;;;;N;;;;;
+12E6;ETHIOPIC SYLLABLE ZHO;Lo;0;L;;;;;N;;;;;
+12E7;ETHIOPIC SYLLABLE ZHWA;Lo;0;L;;;;;N;;;;;
+12E8;ETHIOPIC SYLLABLE YA;Lo;0;L;;;;;N;;;;;
+12E9;ETHIOPIC SYLLABLE YU;Lo;0;L;;;;;N;;;;;
+12EA;ETHIOPIC SYLLABLE YI;Lo;0;L;;;;;N;;;;;
+12EB;ETHIOPIC SYLLABLE YAA;Lo;0;L;;;;;N;;;;;
+12EC;ETHIOPIC SYLLABLE YEE;Lo;0;L;;;;;N;;;;;
+12ED;ETHIOPIC SYLLABLE YE;Lo;0;L;;;;;N;;;;;
+12EE;ETHIOPIC SYLLABLE YO;Lo;0;L;;;;;N;;;;;
+12F0;ETHIOPIC SYLLABLE DA;Lo;0;L;;;;;N;;;;;
+12F1;ETHIOPIC SYLLABLE DU;Lo;0;L;;;;;N;;;;;
+12F2;ETHIOPIC SYLLABLE DI;Lo;0;L;;;;;N;;;;;
+12F3;ETHIOPIC SYLLABLE DAA;Lo;0;L;;;;;N;;;;;
+12F4;ETHIOPIC SYLLABLE DEE;Lo;0;L;;;;;N;;;;;
+12F5;ETHIOPIC SYLLABLE DE;Lo;0;L;;;;;N;;;;;
+12F6;ETHIOPIC SYLLABLE DO;Lo;0;L;;;;;N;;;;;
+12F7;ETHIOPIC SYLLABLE DWA;Lo;0;L;;;;;N;;;;;
+12F8;ETHIOPIC SYLLABLE DDA;Lo;0;L;;;;;N;;;;;
+12F9;ETHIOPIC SYLLABLE DDU;Lo;0;L;;;;;N;;;;;
+12FA;ETHIOPIC SYLLABLE DDI;Lo;0;L;;;;;N;;;;;
+12FB;ETHIOPIC SYLLABLE DDAA;Lo;0;L;;;;;N;;;;;
+12FC;ETHIOPIC SYLLABLE DDEE;Lo;0;L;;;;;N;;;;;
+12FD;ETHIOPIC SYLLABLE DDE;Lo;0;L;;;;;N;;;;;
+12FE;ETHIOPIC SYLLABLE DDO;Lo;0;L;;;;;N;;;;;
+12FF;ETHIOPIC SYLLABLE DDWA;Lo;0;L;;;;;N;;;;;
+1300;ETHIOPIC SYLLABLE JA;Lo;0;L;;;;;N;;;;;
+1301;ETHIOPIC SYLLABLE JU;Lo;0;L;;;;;N;;;;;
+1302;ETHIOPIC SYLLABLE JI;Lo;0;L;;;;;N;;;;;
+1303;ETHIOPIC SYLLABLE JAA;Lo;0;L;;;;;N;;;;;
+1304;ETHIOPIC SYLLABLE JEE;Lo;0;L;;;;;N;;;;;
+1305;ETHIOPIC SYLLABLE JE;Lo;0;L;;;;;N;;;;;
+1306;ETHIOPIC SYLLABLE JO;Lo;0;L;;;;;N;;;;;
+1307;ETHIOPIC SYLLABLE JWA;Lo;0;L;;;;;N;;;;;
+1308;ETHIOPIC SYLLABLE GA;Lo;0;L;;;;;N;;;;;
+1309;ETHIOPIC SYLLABLE GU;Lo;0;L;;;;;N;;;;;
+130A;ETHIOPIC SYLLABLE GI;Lo;0;L;;;;;N;;;;;
+130B;ETHIOPIC SYLLABLE GAA;Lo;0;L;;;;;N;;;;;
+130C;ETHIOPIC SYLLABLE GEE;Lo;0;L;;;;;N;;;;;
+130D;ETHIOPIC SYLLABLE GE;Lo;0;L;;;;;N;;;;;
+130E;ETHIOPIC SYLLABLE GO;Lo;0;L;;;;;N;;;;;
+1310;ETHIOPIC SYLLABLE GWA;Lo;0;L;;;;;N;;;;;
+1312;ETHIOPIC SYLLABLE GWI;Lo;0;L;;;;;N;;;;;
+1313;ETHIOPIC SYLLABLE GWAA;Lo;0;L;;;;;N;;;;;
+1314;ETHIOPIC SYLLABLE GWEE;Lo;0;L;;;;;N;;;;;
+1315;ETHIOPIC SYLLABLE GWE;Lo;0;L;;;;;N;;;;;
+1318;ETHIOPIC SYLLABLE GGA;Lo;0;L;;;;;N;;;;;
+1319;ETHIOPIC SYLLABLE GGU;Lo;0;L;;;;;N;;;;;
+131A;ETHIOPIC SYLLABLE GGI;Lo;0;L;;;;;N;;;;;
+131B;ETHIOPIC SYLLABLE GGAA;Lo;0;L;;;;;N;;;;;
+131C;ETHIOPIC SYLLABLE GGEE;Lo;0;L;;;;;N;;;;;
+131D;ETHIOPIC SYLLABLE GGE;Lo;0;L;;;;;N;;;;;
+131E;ETHIOPIC SYLLABLE GGO;Lo;0;L;;;;;N;;;;;
+1320;ETHIOPIC SYLLABLE THA;Lo;0;L;;;;;N;;;;;
+1321;ETHIOPIC SYLLABLE THU;Lo;0;L;;;;;N;;;;;
+1322;ETHIOPIC SYLLABLE THI;Lo;0;L;;;;;N;;;;;
+1323;ETHIOPIC SYLLABLE THAA;Lo;0;L;;;;;N;;;;;
+1324;ETHIOPIC SYLLABLE THEE;Lo;0;L;;;;;N;;;;;
+1325;ETHIOPIC SYLLABLE THE;Lo;0;L;;;;;N;;;;;
+1326;ETHIOPIC SYLLABLE THO;Lo;0;L;;;;;N;;;;;
+1327;ETHIOPIC SYLLABLE THWA;Lo;0;L;;;;;N;;;;;
+1328;ETHIOPIC SYLLABLE CHA;Lo;0;L;;;;;N;;;;;
+1329;ETHIOPIC SYLLABLE CHU;Lo;0;L;;;;;N;;;;;
+132A;ETHIOPIC SYLLABLE CHI;Lo;0;L;;;;;N;;;;;
+132B;ETHIOPIC SYLLABLE CHAA;Lo;0;L;;;;;N;;;;;
+132C;ETHIOPIC SYLLABLE CHEE;Lo;0;L;;;;;N;;;;;
+132D;ETHIOPIC SYLLABLE CHE;Lo;0;L;;;;;N;;;;;
+132E;ETHIOPIC SYLLABLE CHO;Lo;0;L;;;;;N;;;;;
+132F;ETHIOPIC SYLLABLE CHWA;Lo;0;L;;;;;N;;;;;
+1330;ETHIOPIC SYLLABLE PHA;Lo;0;L;;;;;N;;;;;
+1331;ETHIOPIC SYLLABLE PHU;Lo;0;L;;;;;N;;;;;
+1332;ETHIOPIC SYLLABLE PHI;Lo;0;L;;;;;N;;;;;
+1333;ETHIOPIC SYLLABLE PHAA;Lo;0;L;;;;;N;;;;;
+1334;ETHIOPIC SYLLABLE PHEE;Lo;0;L;;;;;N;;;;;
+1335;ETHIOPIC SYLLABLE PHE;Lo;0;L;;;;;N;;;;;
+1336;ETHIOPIC SYLLABLE PHO;Lo;0;L;;;;;N;;;;;
+1337;ETHIOPIC SYLLABLE PHWA;Lo;0;L;;;;;N;;;;;
+1338;ETHIOPIC SYLLABLE TSA;Lo;0;L;;;;;N;;;;;
+1339;ETHIOPIC SYLLABLE TSU;Lo;0;L;;;;;N;;;;;
+133A;ETHIOPIC SYLLABLE TSI;Lo;0;L;;;;;N;;;;;
+133B;ETHIOPIC SYLLABLE TSAA;Lo;0;L;;;;;N;;;;;
+133C;ETHIOPIC SYLLABLE TSEE;Lo;0;L;;;;;N;;;;;
+133D;ETHIOPIC SYLLABLE TSE;Lo;0;L;;;;;N;;;;;
+133E;ETHIOPIC SYLLABLE TSO;Lo;0;L;;;;;N;;;;;
+133F;ETHIOPIC SYLLABLE TSWA;Lo;0;L;;;;;N;;;;;
+1340;ETHIOPIC SYLLABLE TZA;Lo;0;L;;;;;N;;;;;
+1341;ETHIOPIC SYLLABLE TZU;Lo;0;L;;;;;N;;;;;
+1342;ETHIOPIC SYLLABLE TZI;Lo;0;L;;;;;N;;;;;
+1343;ETHIOPIC SYLLABLE TZAA;Lo;0;L;;;;;N;;;;;
+1344;ETHIOPIC SYLLABLE TZEE;Lo;0;L;;;;;N;;;;;
+1345;ETHIOPIC SYLLABLE TZE;Lo;0;L;;;;;N;;;;;
+1346;ETHIOPIC SYLLABLE TZO;Lo;0;L;;;;;N;;;;;
+1348;ETHIOPIC SYLLABLE FA;Lo;0;L;;;;;N;;;;;
+1349;ETHIOPIC SYLLABLE FU;Lo;0;L;;;;;N;;;;;
+134A;ETHIOPIC SYLLABLE FI;Lo;0;L;;;;;N;;;;;
+134B;ETHIOPIC SYLLABLE FAA;Lo;0;L;;;;;N;;;;;
+134C;ETHIOPIC SYLLABLE FEE;Lo;0;L;;;;;N;;;;;
+134D;ETHIOPIC SYLLABLE FE;Lo;0;L;;;;;N;;;;;
+134E;ETHIOPIC SYLLABLE FO;Lo;0;L;;;;;N;;;;;
+134F;ETHIOPIC SYLLABLE FWA;Lo;0;L;;;;;N;;;;;
+1350;ETHIOPIC SYLLABLE PA;Lo;0;L;;;;;N;;;;;
+1351;ETHIOPIC SYLLABLE PU;Lo;0;L;;;;;N;;;;;
+1352;ETHIOPIC SYLLABLE PI;Lo;0;L;;;;;N;;;;;
+1353;ETHIOPIC SYLLABLE PAA;Lo;0;L;;;;;N;;;;;
+1354;ETHIOPIC SYLLABLE PEE;Lo;0;L;;;;;N;;;;;
+1355;ETHIOPIC SYLLABLE PE;Lo;0;L;;;;;N;;;;;
+1356;ETHIOPIC SYLLABLE PO;Lo;0;L;;;;;N;;;;;
+1357;ETHIOPIC SYLLABLE PWA;Lo;0;L;;;;;N;;;;;
+1358;ETHIOPIC SYLLABLE RYA;Lo;0;L;;;;;N;;;;;
+1359;ETHIOPIC SYLLABLE MYA;Lo;0;L;;;;;N;;;;;
+135A;ETHIOPIC SYLLABLE FYA;Lo;0;L;;;;;N;;;;;
+1361;ETHIOPIC WORDSPACE;Po;0;L;;;;;N;;;;;
+1362;ETHIOPIC FULL STOP;Po;0;L;;;;;N;;;;;
+1363;ETHIOPIC COMMA;Po;0;L;;;;;N;;;;;
+1364;ETHIOPIC SEMICOLON;Po;0;L;;;;;N;;;;;
+1365;ETHIOPIC COLON;Po;0;L;;;;;N;;;;;
+1366;ETHIOPIC PREFACE COLON;Po;0;L;;;;;N;;;;;
+1367;ETHIOPIC QUESTION MARK;Po;0;L;;;;;N;;;;;
+1368;ETHIOPIC PARAGRAPH SEPARATOR;Po;0;L;;;;;N;;;;;
+1369;ETHIOPIC DIGIT ONE;Nd;0;L;;;1;1;N;;;;;
+136A;ETHIOPIC DIGIT TWO;Nd;0;L;;;2;2;N;;;;;
+136B;ETHIOPIC DIGIT THREE;Nd;0;L;;;3;3;N;;;;;
+136C;ETHIOPIC DIGIT FOUR;Nd;0;L;;;4;4;N;;;;;
+136D;ETHIOPIC DIGIT FIVE;Nd;0;L;;;5;5;N;;;;;
+136E;ETHIOPIC DIGIT SIX;Nd;0;L;;;6;6;N;;;;;
+136F;ETHIOPIC DIGIT SEVEN;Nd;0;L;;;7;7;N;;;;;
+1370;ETHIOPIC DIGIT EIGHT;Nd;0;L;;;8;8;N;;;;;
+1371;ETHIOPIC DIGIT NINE;Nd;0;L;;;9;9;N;;;;;
+1372;ETHIOPIC NUMBER TEN;No;0;L;;;;10;N;;;;;
+1373;ETHIOPIC NUMBER TWENTY;No;0;L;;;;20;N;;;;;
+1374;ETHIOPIC NUMBER THIRTY;No;0;L;;;;30;N;;;;;
+1375;ETHIOPIC NUMBER FORTY;No;0;L;;;;40;N;;;;;
+1376;ETHIOPIC NUMBER FIFTY;No;0;L;;;;50;N;;;;;
+1377;ETHIOPIC NUMBER SIXTY;No;0;L;;;;60;N;;;;;
+1378;ETHIOPIC NUMBER SEVENTY;No;0;L;;;;70;N;;;;;
+1379;ETHIOPIC NUMBER EIGHTY;No;0;L;;;;80;N;;;;;
+137A;ETHIOPIC NUMBER NINETY;No;0;L;;;;90;N;;;;;
+137B;ETHIOPIC NUMBER HUNDRED;No;0;L;;;;100;N;;;;;
+137C;ETHIOPIC NUMBER TEN THOUSAND;No;0;L;;;;10000;N;;;;;
+13A0;CHEROKEE LETTER A;Lo;0;L;;;;;N;;;;;
+13A1;CHEROKEE LETTER E;Lo;0;L;;;;;N;;;;;
+13A2;CHEROKEE LETTER I;Lo;0;L;;;;;N;;;;;
+13A3;CHEROKEE LETTER O;Lo;0;L;;;;;N;;;;;
+13A4;CHEROKEE LETTER U;Lo;0;L;;;;;N;;;;;
+13A5;CHEROKEE LETTER V;Lo;0;L;;;;;N;;;;;
+13A6;CHEROKEE LETTER GA;Lo;0;L;;;;;N;;;;;
+13A7;CHEROKEE LETTER KA;Lo;0;L;;;;;N;;;;;
+13A8;CHEROKEE LETTER GE;Lo;0;L;;;;;N;;;;;
+13A9;CHEROKEE LETTER GI;Lo;0;L;;;;;N;;;;;
+13AA;CHEROKEE LETTER GO;Lo;0;L;;;;;N;;;;;
+13AB;CHEROKEE LETTER GU;Lo;0;L;;;;;N;;;;;
+13AC;CHEROKEE LETTER GV;Lo;0;L;;;;;N;;;;;
+13AD;CHEROKEE LETTER HA;Lo;0;L;;;;;N;;;;;
+13AE;CHEROKEE LETTER HE;Lo;0;L;;;;;N;;;;;
+13AF;CHEROKEE LETTER HI;Lo;0;L;;;;;N;;;;;
+13B0;CHEROKEE LETTER HO;Lo;0;L;;;;;N;;;;;
+13B1;CHEROKEE LETTER HU;Lo;0;L;;;;;N;;;;;
+13B2;CHEROKEE LETTER HV;Lo;0;L;;;;;N;;;;;
+13B3;CHEROKEE LETTER LA;Lo;0;L;;;;;N;;;;;
+13B4;CHEROKEE LETTER LE;Lo;0;L;;;;;N;;;;;
+13B5;CHEROKEE LETTER LI;Lo;0;L;;;;;N;;;;;
+13B6;CHEROKEE LETTER LO;Lo;0;L;;;;;N;;;;;
+13B7;CHEROKEE LETTER LU;Lo;0;L;;;;;N;;;;;
+13B8;CHEROKEE LETTER LV;Lo;0;L;;;;;N;;;;;
+13B9;CHEROKEE LETTER MA;Lo;0;L;;;;;N;;;;;
+13BA;CHEROKEE LETTER ME;Lo;0;L;;;;;N;;;;;
+13BB;CHEROKEE LETTER MI;Lo;0;L;;;;;N;;;;;
+13BC;CHEROKEE LETTER MO;Lo;0;L;;;;;N;;;;;
+13BD;CHEROKEE LETTER MU;Lo;0;L;;;;;N;;;;;
+13BE;CHEROKEE LETTER NA;Lo;0;L;;;;;N;;;;;
+13BF;CHEROKEE LETTER HNA;Lo;0;L;;;;;N;;;;;
+13C0;CHEROKEE LETTER NAH;Lo;0;L;;;;;N;;;;;
+13C1;CHEROKEE LETTER NE;Lo;0;L;;;;;N;;;;;
+13C2;CHEROKEE LETTER NI;Lo;0;L;;;;;N;;;;;
+13C3;CHEROKEE LETTER NO;Lo;0;L;;;;;N;;;;;
+13C4;CHEROKEE LETTER NU;Lo;0;L;;;;;N;;;;;
+13C5;CHEROKEE LETTER NV;Lo;0;L;;;;;N;;;;;
+13C6;CHEROKEE LETTER QUA;Lo;0;L;;;;;N;;;;;
+13C7;CHEROKEE LETTER QUE;Lo;0;L;;;;;N;;;;;
+13C8;CHEROKEE LETTER QUI;Lo;0;L;;;;;N;;;;;
+13C9;CHEROKEE LETTER QUO;Lo;0;L;;;;;N;;;;;
+13CA;CHEROKEE LETTER QUU;Lo;0;L;;;;;N;;;;;
+13CB;CHEROKEE LETTER QUV;Lo;0;L;;;;;N;;;;;
+13CC;CHEROKEE LETTER SA;Lo;0;L;;;;;N;;;;;
+13CD;CHEROKEE LETTER S;Lo;0;L;;;;;N;;;;;
+13CE;CHEROKEE LETTER SE;Lo;0;L;;;;;N;;;;;
+13CF;CHEROKEE LETTER SI;Lo;0;L;;;;;N;;;;;
+13D0;CHEROKEE LETTER SO;Lo;0;L;;;;;N;;;;;
+13D1;CHEROKEE LETTER SU;Lo;0;L;;;;;N;;;;;
+13D2;CHEROKEE LETTER SV;Lo;0;L;;;;;N;;;;;
+13D3;CHEROKEE LETTER DA;Lo;0;L;;;;;N;;;;;
+13D4;CHEROKEE LETTER TA;Lo;0;L;;;;;N;;;;;
+13D5;CHEROKEE LETTER DE;Lo;0;L;;;;;N;;;;;
+13D6;CHEROKEE LETTER TE;Lo;0;L;;;;;N;;;;;
+13D7;CHEROKEE LETTER DI;Lo;0;L;;;;;N;;;;;
+13D8;CHEROKEE LETTER TI;Lo;0;L;;;;;N;;;;;
+13D9;CHEROKEE LETTER DO;Lo;0;L;;;;;N;;;;;
+13DA;CHEROKEE LETTER DU;Lo;0;L;;;;;N;;;;;
+13DB;CHEROKEE LETTER DV;Lo;0;L;;;;;N;;;;;
+13DC;CHEROKEE LETTER DLA;Lo;0;L;;;;;N;;;;;
+13DD;CHEROKEE LETTER TLA;Lo;0;L;;;;;N;;;;;
+13DE;CHEROKEE LETTER TLE;Lo;0;L;;;;;N;;;;;
+13DF;CHEROKEE LETTER TLI;Lo;0;L;;;;;N;;;;;
+13E0;CHEROKEE LETTER TLO;Lo;0;L;;;;;N;;;;;
+13E1;CHEROKEE LETTER TLU;Lo;0;L;;;;;N;;;;;
+13E2;CHEROKEE LETTER TLV;Lo;0;L;;;;;N;;;;;
+13E3;CHEROKEE LETTER TSA;Lo;0;L;;;;;N;;;;;
+13E4;CHEROKEE LETTER TSE;Lo;0;L;;;;;N;;;;;
+13E5;CHEROKEE LETTER TSI;Lo;0;L;;;;;N;;;;;
+13E6;CHEROKEE LETTER TSO;Lo;0;L;;;;;N;;;;;
+13E7;CHEROKEE LETTER TSU;Lo;0;L;;;;;N;;;;;
+13E8;CHEROKEE LETTER TSV;Lo;0;L;;;;;N;;;;;
+13E9;CHEROKEE LETTER WA;Lo;0;L;;;;;N;;;;;
+13EA;CHEROKEE LETTER WE;Lo;0;L;;;;;N;;;;;
+13EB;CHEROKEE LETTER WI;Lo;0;L;;;;;N;;;;;
+13EC;CHEROKEE LETTER WO;Lo;0;L;;;;;N;;;;;
+13ED;CHEROKEE LETTER WU;Lo;0;L;;;;;N;;;;;
+13EE;CHEROKEE LETTER WV;Lo;0;L;;;;;N;;;;;
+13EF;CHEROKEE LETTER YA;Lo;0;L;;;;;N;;;;;
+13F0;CHEROKEE LETTER YE;Lo;0;L;;;;;N;;;;;
+13F1;CHEROKEE LETTER YI;Lo;0;L;;;;;N;;;;;
+13F2;CHEROKEE LETTER YO;Lo;0;L;;;;;N;;;;;
+13F3;CHEROKEE LETTER YU;Lo;0;L;;;;;N;;;;;
+13F4;CHEROKEE LETTER YV;Lo;0;L;;;;;N;;;;;
+1401;CANADIAN SYLLABICS E;Lo;0;L;;;;;N;;;;;
+1402;CANADIAN SYLLABICS AAI;Lo;0;L;;;;;N;;;;;
+1403;CANADIAN SYLLABICS I;Lo;0;L;;;;;N;;;;;
+1404;CANADIAN SYLLABICS II;Lo;0;L;;;;;N;;;;;
+1405;CANADIAN SYLLABICS O;Lo;0;L;;;;;N;;;;;
+1406;CANADIAN SYLLABICS OO;Lo;0;L;;;;;N;;;;;
+1407;CANADIAN SYLLABICS Y-CREE OO;Lo;0;L;;;;;N;;;;;
+1408;CANADIAN SYLLABICS CARRIER EE;Lo;0;L;;;;;N;;;;;
+1409;CANADIAN SYLLABICS CARRIER I;Lo;0;L;;;;;N;;;;;
+140A;CANADIAN SYLLABICS A;Lo;0;L;;;;;N;;;;;
+140B;CANADIAN SYLLABICS AA;Lo;0;L;;;;;N;;;;;
+140C;CANADIAN SYLLABICS WE;Lo;0;L;;;;;N;;;;;
+140D;CANADIAN SYLLABICS WEST-CREE WE;Lo;0;L;;;;;N;;;;;
+140E;CANADIAN SYLLABICS WI;Lo;0;L;;;;;N;;;;;
+140F;CANADIAN SYLLABICS WEST-CREE WI;Lo;0;L;;;;;N;;;;;
+1410;CANADIAN SYLLABICS WII;Lo;0;L;;;;;N;;;;;
+1411;CANADIAN SYLLABICS WEST-CREE WII;Lo;0;L;;;;;N;;;;;
+1412;CANADIAN SYLLABICS WO;Lo;0;L;;;;;N;;;;;
+1413;CANADIAN SYLLABICS WEST-CREE WO;Lo;0;L;;;;;N;;;;;
+1414;CANADIAN SYLLABICS WOO;Lo;0;L;;;;;N;;;;;
+1415;CANADIAN SYLLABICS WEST-CREE WOO;Lo;0;L;;;;;N;;;;;
+1416;CANADIAN SYLLABICS NASKAPI WOO;Lo;0;L;;;;;N;;;;;
+1417;CANADIAN SYLLABICS WA;Lo;0;L;;;;;N;;;;;
+1418;CANADIAN SYLLABICS WEST-CREE WA;Lo;0;L;;;;;N;;;;;
+1419;CANADIAN SYLLABICS WAA;Lo;0;L;;;;;N;;;;;
+141A;CANADIAN SYLLABICS WEST-CREE WAA;Lo;0;L;;;;;N;;;;;
+141B;CANADIAN SYLLABICS NASKAPI WAA;Lo;0;L;;;;;N;;;;;
+141C;CANADIAN SYLLABICS AI;Lo;0;L;;;;;N;;;;;
+141D;CANADIAN SYLLABICS Y-CREE W;Lo;0;L;;;;;N;;;;;
+141E;CANADIAN SYLLABICS GLOTTAL STOP;Lo;0;L;;;;;N;;;;;
+141F;CANADIAN SYLLABICS FINAL ACUTE;Lo;0;L;;;;;N;;;;;
+1420;CANADIAN SYLLABICS FINAL GRAVE;Lo;0;L;;;;;N;;;;;
+1421;CANADIAN SYLLABICS FINAL BOTTOM HALF RING;Lo;0;L;;;;;N;;;;;
+1422;CANADIAN SYLLABICS FINAL TOP HALF RING;Lo;0;L;;;;;N;;;;;
+1423;CANADIAN SYLLABICS FINAL RIGHT HALF RING;Lo;0;L;;;;;N;;;;;
+1424;CANADIAN SYLLABICS FINAL RING;Lo;0;L;;;;;N;;;;;
+1425;CANADIAN SYLLABICS FINAL DOUBLE ACUTE;Lo;0;L;;;;;N;;;;;
+1426;CANADIAN SYLLABICS FINAL DOUBLE SHORT VERTICAL STROKES;Lo;0;L;;;;;N;;;;;
+1427;CANADIAN SYLLABICS FINAL MIDDLE DOT;Lo;0;L;;;;;N;;;;;
+1428;CANADIAN SYLLABICS FINAL SHORT HORIZONTAL STROKE;Lo;0;L;;;;;N;;;;;
+1429;CANADIAN SYLLABICS FINAL PLUS;Lo;0;L;;;;;N;;;;;
+142A;CANADIAN SYLLABICS FINAL DOWN TACK;Lo;0;L;;;;;N;;;;;
+142B;CANADIAN SYLLABICS EN;Lo;0;L;;;;;N;;;;;
+142C;CANADIAN SYLLABICS IN;Lo;0;L;;;;;N;;;;;
+142D;CANADIAN SYLLABICS ON;Lo;0;L;;;;;N;;;;;
+142E;CANADIAN SYLLABICS AN;Lo;0;L;;;;;N;;;;;
+142F;CANADIAN SYLLABICS PE;Lo;0;L;;;;;N;;;;;
+1430;CANADIAN SYLLABICS PAAI;Lo;0;L;;;;;N;;;;;
+1431;CANADIAN SYLLABICS PI;Lo;0;L;;;;;N;;;;;
+1432;CANADIAN SYLLABICS PII;Lo;0;L;;;;;N;;;;;
+1433;CANADIAN SYLLABICS PO;Lo;0;L;;;;;N;;;;;
+1434;CANADIAN SYLLABICS POO;Lo;0;L;;;;;N;;;;;
+1435;CANADIAN SYLLABICS Y-CREE POO;Lo;0;L;;;;;N;;;;;
+1436;CANADIAN SYLLABICS CARRIER HEE;Lo;0;L;;;;;N;;;;;
+1437;CANADIAN SYLLABICS CARRIER HI;Lo;0;L;;;;;N;;;;;
+1438;CANADIAN SYLLABICS PA;Lo;0;L;;;;;N;;;;;
+1439;CANADIAN SYLLABICS PAA;Lo;0;L;;;;;N;;;;;
+143A;CANADIAN SYLLABICS PWE;Lo;0;L;;;;;N;;;;;
+143B;CANADIAN SYLLABICS WEST-CREE PWE;Lo;0;L;;;;;N;;;;;
+143C;CANADIAN SYLLABICS PWI;Lo;0;L;;;;;N;;;;;
+143D;CANADIAN SYLLABICS WEST-CREE PWI;Lo;0;L;;;;;N;;;;;
+143E;CANADIAN SYLLABICS PWII;Lo;0;L;;;;;N;;;;;
+143F;CANADIAN SYLLABICS WEST-CREE PWII;Lo;0;L;;;;;N;;;;;
+1440;CANADIAN SYLLABICS PWO;Lo;0;L;;;;;N;;;;;
+1441;CANADIAN SYLLABICS WEST-CREE PWO;Lo;0;L;;;;;N;;;;;
+1442;CANADIAN SYLLABICS PWOO;Lo;0;L;;;;;N;;;;;
+1443;CANADIAN SYLLABICS WEST-CREE PWOO;Lo;0;L;;;;;N;;;;;
+1444;CANADIAN SYLLABICS PWA;Lo;0;L;;;;;N;;;;;
+1445;CANADIAN SYLLABICS WEST-CREE PWA;Lo;0;L;;;;;N;;;;;
+1446;CANADIAN SYLLABICS PWAA;Lo;0;L;;;;;N;;;;;
+1447;CANADIAN SYLLABICS WEST-CREE PWAA;Lo;0;L;;;;;N;;;;;
+1448;CANADIAN SYLLABICS Y-CREE PWAA;Lo;0;L;;;;;N;;;;;
+1449;CANADIAN SYLLABICS P;Lo;0;L;;;;;N;;;;;
+144A;CANADIAN SYLLABICS WEST-CREE P;Lo;0;L;;;;;N;;;;;
+144B;CANADIAN SYLLABICS CARRIER H;Lo;0;L;;;;;N;;;;;
+144C;CANADIAN SYLLABICS TE;Lo;0;L;;;;;N;;;;;
+144D;CANADIAN SYLLABICS TAAI;Lo;0;L;;;;;N;;;;;
+144E;CANADIAN SYLLABICS TI;Lo;0;L;;;;;N;;;;;
+144F;CANADIAN SYLLABICS TII;Lo;0;L;;;;;N;;;;;
+1450;CANADIAN SYLLABICS TO;Lo;0;L;;;;;N;;;;;
+1451;CANADIAN SYLLABICS TOO;Lo;0;L;;;;;N;;;;;
+1452;CANADIAN SYLLABICS Y-CREE TOO;Lo;0;L;;;;;N;;;;;
+1453;CANADIAN SYLLABICS CARRIER DEE;Lo;0;L;;;;;N;;;;;
+1454;CANADIAN SYLLABICS CARRIER DI;Lo;0;L;;;;;N;;;;;
+1455;CANADIAN SYLLABICS TA;Lo;0;L;;;;;N;;;;;
+1456;CANADIAN SYLLABICS TAA;Lo;0;L;;;;;N;;;;;
+1457;CANADIAN SYLLABICS TWE;Lo;0;L;;;;;N;;;;;
+1458;CANADIAN SYLLABICS WEST-CREE TWE;Lo;0;L;;;;;N;;;;;
+1459;CANADIAN SYLLABICS TWI;Lo;0;L;;;;;N;;;;;
+145A;CANADIAN SYLLABICS WEST-CREE TWI;Lo;0;L;;;;;N;;;;;
+145B;CANADIAN SYLLABICS TWII;Lo;0;L;;;;;N;;;;;
+145C;CANADIAN SYLLABICS WEST-CREE TWII;Lo;0;L;;;;;N;;;;;
+145D;CANADIAN SYLLABICS TWO;Lo;0;L;;;;;N;;;;;
+145E;CANADIAN SYLLABICS WEST-CREE TWO;Lo;0;L;;;;;N;;;;;
+145F;CANADIAN SYLLABICS TWOO;Lo;0;L;;;;;N;;;;;
+1460;CANADIAN SYLLABICS WEST-CREE TWOO;Lo;0;L;;;;;N;;;;;
+1461;CANADIAN SYLLABICS TWA;Lo;0;L;;;;;N;;;;;
+1462;CANADIAN SYLLABICS WEST-CREE TWA;Lo;0;L;;;;;N;;;;;
+1463;CANADIAN SYLLABICS TWAA;Lo;0;L;;;;;N;;;;;
+1464;CANADIAN SYLLABICS WEST-CREE TWAA;Lo;0;L;;;;;N;;;;;
+1465;CANADIAN SYLLABICS NASKAPI TWAA;Lo;0;L;;;;;N;;;;;
+1466;CANADIAN SYLLABICS T;Lo;0;L;;;;;N;;;;;
+1467;CANADIAN SYLLABICS TTE;Lo;0;L;;;;;N;;;;;
+1468;CANADIAN SYLLABICS TTI;Lo;0;L;;;;;N;;;;;
+1469;CANADIAN SYLLABICS TTO;Lo;0;L;;;;;N;;;;;
+146A;CANADIAN SYLLABICS TTA;Lo;0;L;;;;;N;;;;;
+146B;CANADIAN SYLLABICS KE;Lo;0;L;;;;;N;;;;;
+146C;CANADIAN SYLLABICS KAAI;Lo;0;L;;;;;N;;;;;
+146D;CANADIAN SYLLABICS KI;Lo;0;L;;;;;N;;;;;
+146E;CANADIAN SYLLABICS KII;Lo;0;L;;;;;N;;;;;
+146F;CANADIAN SYLLABICS KO;Lo;0;L;;;;;N;;;;;
+1470;CANADIAN SYLLABICS KOO;Lo;0;L;;;;;N;;;;;
+1471;CANADIAN SYLLABICS Y-CREE KOO;Lo;0;L;;;;;N;;;;;
+1472;CANADIAN SYLLABICS KA;Lo;0;L;;;;;N;;;;;
+1473;CANADIAN SYLLABICS KAA;Lo;0;L;;;;;N;;;;;
+1474;CANADIAN SYLLABICS KWE;Lo;0;L;;;;;N;;;;;
+1475;CANADIAN SYLLABICS WEST-CREE KWE;Lo;0;L;;;;;N;;;;;
+1476;CANADIAN SYLLABICS KWI;Lo;0;L;;;;;N;;;;;
+1477;CANADIAN SYLLABICS WEST-CREE KWI;Lo;0;L;;;;;N;;;;;
+1478;CANADIAN SYLLABICS KWII;Lo;0;L;;;;;N;;;;;
+1479;CANADIAN SYLLABICS WEST-CREE KWII;Lo;0;L;;;;;N;;;;;
+147A;CANADIAN SYLLABICS KWO;Lo;0;L;;;;;N;;;;;
+147B;CANADIAN SYLLABICS WEST-CREE KWO;Lo;0;L;;;;;N;;;;;
+147C;CANADIAN SYLLABICS KWOO;Lo;0;L;;;;;N;;;;;
+147D;CANADIAN SYLLABICS WEST-CREE KWOO;Lo;0;L;;;;;N;;;;;
+147E;CANADIAN SYLLABICS KWA;Lo;0;L;;;;;N;;;;;
+147F;CANADIAN SYLLABICS WEST-CREE KWA;Lo;0;L;;;;;N;;;;;
+1480;CANADIAN SYLLABICS KWAA;Lo;0;L;;;;;N;;;;;
+1481;CANADIAN SYLLABICS WEST-CREE KWAA;Lo;0;L;;;;;N;;;;;
+1482;CANADIAN SYLLABICS NASKAPI KWAA;Lo;0;L;;;;;N;;;;;
+1483;CANADIAN SYLLABICS K;Lo;0;L;;;;;N;;;;;
+1484;CANADIAN SYLLABICS KW;Lo;0;L;;;;;N;;;;;
+1485;CANADIAN SYLLABICS SOUTH-SLAVEY KEH;Lo;0;L;;;;;N;;;;;
+1486;CANADIAN SYLLABICS SOUTH-SLAVEY KIH;Lo;0;L;;;;;N;;;;;
+1487;CANADIAN SYLLABICS SOUTH-SLAVEY KOH;Lo;0;L;;;;;N;;;;;
+1488;CANADIAN SYLLABICS SOUTH-SLAVEY KAH;Lo;0;L;;;;;N;;;;;
+1489;CANADIAN SYLLABICS CE;Lo;0;L;;;;;N;;;;;
+148A;CANADIAN SYLLABICS CAAI;Lo;0;L;;;;;N;;;;;
+148B;CANADIAN SYLLABICS CI;Lo;0;L;;;;;N;;;;;
+148C;CANADIAN SYLLABICS CII;Lo;0;L;;;;;N;;;;;
+148D;CANADIAN SYLLABICS CO;Lo;0;L;;;;;N;;;;;
+148E;CANADIAN SYLLABICS COO;Lo;0;L;;;;;N;;;;;
+148F;CANADIAN SYLLABICS Y-CREE COO;Lo;0;L;;;;;N;;;;;
+1490;CANADIAN SYLLABICS CA;Lo;0;L;;;;;N;;;;;
+1491;CANADIAN SYLLABICS CAA;Lo;0;L;;;;;N;;;;;
+1492;CANADIAN SYLLABICS CWE;Lo;0;L;;;;;N;;;;;
+1493;CANADIAN SYLLABICS WEST-CREE CWE;Lo;0;L;;;;;N;;;;;
+1494;CANADIAN SYLLABICS CWI;Lo;0;L;;;;;N;;;;;
+1495;CANADIAN SYLLABICS WEST-CREE CWI;Lo;0;L;;;;;N;;;;;
+1496;CANADIAN SYLLABICS CWII;Lo;0;L;;;;;N;;;;;
+1497;CANADIAN SYLLABICS WEST-CREE CWII;Lo;0;L;;;;;N;;;;;
+1498;CANADIAN SYLLABICS CWO;Lo;0;L;;;;;N;;;;;
+1499;CANADIAN SYLLABICS WEST-CREE CWO;Lo;0;L;;;;;N;;;;;
+149A;CANADIAN SYLLABICS CWOO;Lo;0;L;;;;;N;;;;;
+149B;CANADIAN SYLLABICS WEST-CREE CWOO;Lo;0;L;;;;;N;;;;;
+149C;CANADIAN SYLLABICS CWA;Lo;0;L;;;;;N;;;;;
+149D;CANADIAN SYLLABICS WEST-CREE CWA;Lo;0;L;;;;;N;;;;;
+149E;CANADIAN SYLLABICS CWAA;Lo;0;L;;;;;N;;;;;
+149F;CANADIAN SYLLABICS WEST-CREE CWAA;Lo;0;L;;;;;N;;;;;
+14A0;CANADIAN SYLLABICS NASKAPI CWAA;Lo;0;L;;;;;N;;;;;
+14A1;CANADIAN SYLLABICS C;Lo;0;L;;;;;N;;;;;
+14A2;CANADIAN SYLLABICS SAYISI TH;Lo;0;L;;;;;N;;;;;
+14A3;CANADIAN SYLLABICS ME;Lo;0;L;;;;;N;;;;;
+14A4;CANADIAN SYLLABICS MAAI;Lo;0;L;;;;;N;;;;;
+14A5;CANADIAN SYLLABICS MI;Lo;0;L;;;;;N;;;;;
+14A6;CANADIAN SYLLABICS MII;Lo;0;L;;;;;N;;;;;
+14A7;CANADIAN SYLLABICS MO;Lo;0;L;;;;;N;;;;;
+14A8;CANADIAN SYLLABICS MOO;Lo;0;L;;;;;N;;;;;
+14A9;CANADIAN SYLLABICS Y-CREE MOO;Lo;0;L;;;;;N;;;;;
+14AA;CANADIAN SYLLABICS MA;Lo;0;L;;;;;N;;;;;
+14AB;CANADIAN SYLLABICS MAA;Lo;0;L;;;;;N;;;;;
+14AC;CANADIAN SYLLABICS MWE;Lo;0;L;;;;;N;;;;;
+14AD;CANADIAN SYLLABICS WEST-CREE MWE;Lo;0;L;;;;;N;;;;;
+14AE;CANADIAN SYLLABICS MWI;Lo;0;L;;;;;N;;;;;
+14AF;CANADIAN SYLLABICS WEST-CREE MWI;Lo;0;L;;;;;N;;;;;
+14B0;CANADIAN SYLLABICS MWII;Lo;0;L;;;;;N;;;;;
+14B1;CANADIAN SYLLABICS WEST-CREE MWII;Lo;0;L;;;;;N;;;;;
+14B2;CANADIAN SYLLABICS MWO;Lo;0;L;;;;;N;;;;;
+14B3;CANADIAN SYLLABICS WEST-CREE MWO;Lo;0;L;;;;;N;;;;;
+14B4;CANADIAN SYLLABICS MWOO;Lo;0;L;;;;;N;;;;;
+14B5;CANADIAN SYLLABICS WEST-CREE MWOO;Lo;0;L;;;;;N;;;;;
+14B6;CANADIAN SYLLABICS MWA;Lo;0;L;;;;;N;;;;;
+14B7;CANADIAN SYLLABICS WEST-CREE MWA;Lo;0;L;;;;;N;;;;;
+14B8;CANADIAN SYLLABICS MWAA;Lo;0;L;;;;;N;;;;;
+14B9;CANADIAN SYLLABICS WEST-CREE MWAA;Lo;0;L;;;;;N;;;;;
+14BA;CANADIAN SYLLABICS NASKAPI MWAA;Lo;0;L;;;;;N;;;;;
+14BB;CANADIAN SYLLABICS M;Lo;0;L;;;;;N;;;;;
+14BC;CANADIAN SYLLABICS WEST-CREE M;Lo;0;L;;;;;N;;;;;
+14BD;CANADIAN SYLLABICS MH;Lo;0;L;;;;;N;;;;;
+14BE;CANADIAN SYLLABICS ATHAPASCAN M;Lo;0;L;;;;;N;;;;;
+14BF;CANADIAN SYLLABICS SAYISI M;Lo;0;L;;;;;N;;;;;
+14C0;CANADIAN SYLLABICS NE;Lo;0;L;;;;;N;;;;;
+14C1;CANADIAN SYLLABICS NAAI;Lo;0;L;;;;;N;;;;;
+14C2;CANADIAN SYLLABICS NI;Lo;0;L;;;;;N;;;;;
+14C3;CANADIAN SYLLABICS NII;Lo;0;L;;;;;N;;;;;
+14C4;CANADIAN SYLLABICS NO;Lo;0;L;;;;;N;;;;;
+14C5;CANADIAN SYLLABICS NOO;Lo;0;L;;;;;N;;;;;
+14C6;CANADIAN SYLLABICS Y-CREE NOO;Lo;0;L;;;;;N;;;;;
+14C7;CANADIAN SYLLABICS NA;Lo;0;L;;;;;N;;;;;
+14C8;CANADIAN SYLLABICS NAA;Lo;0;L;;;;;N;;;;;
+14C9;CANADIAN SYLLABICS NWE;Lo;0;L;;;;;N;;;;;
+14CA;CANADIAN SYLLABICS WEST-CREE NWE;Lo;0;L;;;;;N;;;;;
+14CB;CANADIAN SYLLABICS NWA;Lo;0;L;;;;;N;;;;;
+14CC;CANADIAN SYLLABICS WEST-CREE NWA;Lo;0;L;;;;;N;;;;;
+14CD;CANADIAN SYLLABICS NWAA;Lo;0;L;;;;;N;;;;;
+14CE;CANADIAN SYLLABICS WEST-CREE NWAA;Lo;0;L;;;;;N;;;;;
+14CF;CANADIAN SYLLABICS NASKAPI NWAA;Lo;0;L;;;;;N;;;;;
+14D0;CANADIAN SYLLABICS N;Lo;0;L;;;;;N;;;;;
+14D1;CANADIAN SYLLABICS CARRIER NG;Lo;0;L;;;;;N;;;;;
+14D2;CANADIAN SYLLABICS NH;Lo;0;L;;;;;N;;;;;
+14D3;CANADIAN SYLLABICS LE;Lo;0;L;;;;;N;;;;;
+14D4;CANADIAN SYLLABICS LAAI;Lo;0;L;;;;;N;;;;;
+14D5;CANADIAN SYLLABICS LI;Lo;0;L;;;;;N;;;;;
+14D6;CANADIAN SYLLABICS LII;Lo;0;L;;;;;N;;;;;
+14D7;CANADIAN SYLLABICS LO;Lo;0;L;;;;;N;;;;;
+14D8;CANADIAN SYLLABICS LOO;Lo;0;L;;;;;N;;;;;
+14D9;CANADIAN SYLLABICS Y-CREE LOO;Lo;0;L;;;;;N;;;;;
+14DA;CANADIAN SYLLABICS LA;Lo;0;L;;;;;N;;;;;
+14DB;CANADIAN SYLLABICS LAA;Lo;0;L;;;;;N;;;;;
+14DC;CANADIAN SYLLABICS LWE;Lo;0;L;;;;;N;;;;;
+14DD;CANADIAN SYLLABICS WEST-CREE LWE;Lo;0;L;;;;;N;;;;;
+14DE;CANADIAN SYLLABICS LWI;Lo;0;L;;;;;N;;;;;
+14DF;CANADIAN SYLLABICS WEST-CREE LWI;Lo;0;L;;;;;N;;;;;
+14E0;CANADIAN SYLLABICS LWII;Lo;0;L;;;;;N;;;;;
+14E1;CANADIAN SYLLABICS WEST-CREE LWII;Lo;0;L;;;;;N;;;;;
+14E2;CANADIAN SYLLABICS LWO;Lo;0;L;;;;;N;;;;;
+14E3;CANADIAN SYLLABICS WEST-CREE LWO;Lo;0;L;;;;;N;;;;;
+14E4;CANADIAN SYLLABICS LWOO;Lo;0;L;;;;;N;;;;;
+14E5;CANADIAN SYLLABICS WEST-CREE LWOO;Lo;0;L;;;;;N;;;;;
+14E6;CANADIAN SYLLABICS LWA;Lo;0;L;;;;;N;;;;;
+14E7;CANADIAN SYLLABICS WEST-CREE LWA;Lo;0;L;;;;;N;;;;;
+14E8;CANADIAN SYLLABICS LWAA;Lo;0;L;;;;;N;;;;;
+14E9;CANADIAN SYLLABICS WEST-CREE LWAA;Lo;0;L;;;;;N;;;;;
+14EA;CANADIAN SYLLABICS L;Lo;0;L;;;;;N;;;;;
+14EB;CANADIAN SYLLABICS WEST-CREE L;Lo;0;L;;;;;N;;;;;
+14EC;CANADIAN SYLLABICS MEDIAL L;Lo;0;L;;;;;N;;;;;
+14ED;CANADIAN SYLLABICS SE;Lo;0;L;;;;;N;;;;;
+14EE;CANADIAN SYLLABICS SAAI;Lo;0;L;;;;;N;;;;;
+14EF;CANADIAN SYLLABICS SI;Lo;0;L;;;;;N;;;;;
+14F0;CANADIAN SYLLABICS SII;Lo;0;L;;;;;N;;;;;
+14F1;CANADIAN SYLLABICS SO;Lo;0;L;;;;;N;;;;;
+14F2;CANADIAN SYLLABICS SOO;Lo;0;L;;;;;N;;;;;
+14F3;CANADIAN SYLLABICS Y-CREE SOO;Lo;0;L;;;;;N;;;;;
+14F4;CANADIAN SYLLABICS SA;Lo;0;L;;;;;N;;;;;
+14F5;CANADIAN SYLLABICS SAA;Lo;0;L;;;;;N;;;;;
+14F6;CANADIAN SYLLABICS SWE;Lo;0;L;;;;;N;;;;;
+14F7;CANADIAN SYLLABICS WEST-CREE SWE;Lo;0;L;;;;;N;;;;;
+14F8;CANADIAN SYLLABICS SWI;Lo;0;L;;;;;N;;;;;
+14F9;CANADIAN SYLLABICS WEST-CREE SWI;Lo;0;L;;;;;N;;;;;
+14FA;CANADIAN SYLLABICS SWII;Lo;0;L;;;;;N;;;;;
+14FB;CANADIAN SYLLABICS WEST-CREE SWII;Lo;0;L;;;;;N;;;;;
+14FC;CANADIAN SYLLABICS SWO;Lo;0;L;;;;;N;;;;;
+14FD;CANADIAN SYLLABICS WEST-CREE SWO;Lo;0;L;;;;;N;;;;;
+14FE;CANADIAN SYLLABICS SWOO;Lo;0;L;;;;;N;;;;;
+14FF;CANADIAN SYLLABICS WEST-CREE SWOO;Lo;0;L;;;;;N;;;;;
+1500;CANADIAN SYLLABICS SWA;Lo;0;L;;;;;N;;;;;
+1501;CANADIAN SYLLABICS WEST-CREE SWA;Lo;0;L;;;;;N;;;;;
+1502;CANADIAN SYLLABICS SWAA;Lo;0;L;;;;;N;;;;;
+1503;CANADIAN SYLLABICS WEST-CREE SWAA;Lo;0;L;;;;;N;;;;;
+1504;CANADIAN SYLLABICS NASKAPI SWAA;Lo;0;L;;;;;N;;;;;
+1505;CANADIAN SYLLABICS S;Lo;0;L;;;;;N;;;;;
+1506;CANADIAN SYLLABICS ATHAPASCAN S;Lo;0;L;;;;;N;;;;;
+1507;CANADIAN SYLLABICS SW;Lo;0;L;;;;;N;;;;;
+1508;CANADIAN SYLLABICS BLACKFOOT S;Lo;0;L;;;;;N;;;;;
+1509;CANADIAN SYLLABICS MOOSE-CREE SK;Lo;0;L;;;;;N;;;;;
+150A;CANADIAN SYLLABICS NASKAPI SKW;Lo;0;L;;;;;N;;;;;
+150B;CANADIAN SYLLABICS NASKAPI S-W;Lo;0;L;;;;;N;;;;;
+150C;CANADIAN SYLLABICS NASKAPI SPWA;Lo;0;L;;;;;N;;;;;
+150D;CANADIAN SYLLABICS NASKAPI STWA;Lo;0;L;;;;;N;;;;;
+150E;CANADIAN SYLLABICS NASKAPI SKWA;Lo;0;L;;;;;N;;;;;
+150F;CANADIAN SYLLABICS NASKAPI SCWA;Lo;0;L;;;;;N;;;;;
+1510;CANADIAN SYLLABICS SHE;Lo;0;L;;;;;N;;;;;
+1511;CANADIAN SYLLABICS SHI;Lo;0;L;;;;;N;;;;;
+1512;CANADIAN SYLLABICS SHII;Lo;0;L;;;;;N;;;;;
+1513;CANADIAN SYLLABICS SHO;Lo;0;L;;;;;N;;;;;
+1514;CANADIAN SYLLABICS SHOO;Lo;0;L;;;;;N;;;;;
+1515;CANADIAN SYLLABICS SHA;Lo;0;L;;;;;N;;;;;
+1516;CANADIAN SYLLABICS SHAA;Lo;0;L;;;;;N;;;;;
+1517;CANADIAN SYLLABICS SHWE;Lo;0;L;;;;;N;;;;;
+1518;CANADIAN SYLLABICS WEST-CREE SHWE;Lo;0;L;;;;;N;;;;;
+1519;CANADIAN SYLLABICS SHWI;Lo;0;L;;;;;N;;;;;
+151A;CANADIAN SYLLABICS WEST-CREE SHWI;Lo;0;L;;;;;N;;;;;
+151B;CANADIAN SYLLABICS SHWII;Lo;0;L;;;;;N;;;;;
+151C;CANADIAN SYLLABICS WEST-CREE SHWII;Lo;0;L;;;;;N;;;;;
+151D;CANADIAN SYLLABICS SHWO;Lo;0;L;;;;;N;;;;;
+151E;CANADIAN SYLLABICS WEST-CREE SHWO;Lo;0;L;;;;;N;;;;;
+151F;CANADIAN SYLLABICS SHWOO;Lo;0;L;;;;;N;;;;;
+1520;CANADIAN SYLLABICS WEST-CREE SHWOO;Lo;0;L;;;;;N;;;;;
+1521;CANADIAN SYLLABICS SHWA;Lo;0;L;;;;;N;;;;;
+1522;CANADIAN SYLLABICS WEST-CREE SHWA;Lo;0;L;;;;;N;;;;;
+1523;CANADIAN SYLLABICS SHWAA;Lo;0;L;;;;;N;;;;;
+1524;CANADIAN SYLLABICS WEST-CREE SHWAA;Lo;0;L;;;;;N;;;;;
+1525;CANADIAN SYLLABICS SH;Lo;0;L;;;;;N;;;;;
+1526;CANADIAN SYLLABICS YE;Lo;0;L;;;;;N;;;;;
+1527;CANADIAN SYLLABICS YAAI;Lo;0;L;;;;;N;;;;;
+1528;CANADIAN SYLLABICS YI;Lo;0;L;;;;;N;;;;;
+1529;CANADIAN SYLLABICS YII;Lo;0;L;;;;;N;;;;;
+152A;CANADIAN SYLLABICS YO;Lo;0;L;;;;;N;;;;;
+152B;CANADIAN SYLLABICS YOO;Lo;0;L;;;;;N;;;;;
+152C;CANADIAN SYLLABICS Y-CREE YOO;Lo;0;L;;;;;N;;;;;
+152D;CANADIAN SYLLABICS YA;Lo;0;L;;;;;N;;;;;
+152E;CANADIAN SYLLABICS YAA;Lo;0;L;;;;;N;;;;;
+152F;CANADIAN SYLLABICS YWE;Lo;0;L;;;;;N;;;;;
+1530;CANADIAN SYLLABICS WEST-CREE YWE;Lo;0;L;;;;;N;;;;;
+1531;CANADIAN SYLLABICS YWI;Lo;0;L;;;;;N;;;;;
+1532;CANADIAN SYLLABICS WEST-CREE YWI;Lo;0;L;;;;;N;;;;;
+1533;CANADIAN SYLLABICS YWII;Lo;0;L;;;;;N;;;;;
+1534;CANADIAN SYLLABICS WEST-CREE YWII;Lo;0;L;;;;;N;;;;;
+1535;CANADIAN SYLLABICS YWO;Lo;0;L;;;;;N;;;;;
+1536;CANADIAN SYLLABICS WEST-CREE YWO;Lo;0;L;;;;;N;;;;;
+1537;CANADIAN SYLLABICS YWOO;Lo;0;L;;;;;N;;;;;
+1538;CANADIAN SYLLABICS WEST-CREE YWOO;Lo;0;L;;;;;N;;;;;
+1539;CANADIAN SYLLABICS YWA;Lo;0;L;;;;;N;;;;;
+153A;CANADIAN SYLLABICS WEST-CREE YWA;Lo;0;L;;;;;N;;;;;
+153B;CANADIAN SYLLABICS YWAA;Lo;0;L;;;;;N;;;;;
+153C;CANADIAN SYLLABICS WEST-CREE YWAA;Lo;0;L;;;;;N;;;;;
+153D;CANADIAN SYLLABICS NASKAPI YWAA;Lo;0;L;;;;;N;;;;;
+153E;CANADIAN SYLLABICS Y;Lo;0;L;;;;;N;;;;;
+153F;CANADIAN SYLLABICS BIBLE-CREE Y;Lo;0;L;;;;;N;;;;;
+1540;CANADIAN SYLLABICS WEST-CREE Y;Lo;0;L;;;;;N;;;;;
+1541;CANADIAN SYLLABICS SAYISI YI;Lo;0;L;;;;;N;;;;;
+1542;CANADIAN SYLLABICS RE;Lo;0;L;;;;;N;;;;;
+1543;CANADIAN SYLLABICS R-CREE RE;Lo;0;L;;;;;N;;;;;
+1544;CANADIAN SYLLABICS WEST-CREE LE;Lo;0;L;;;;;N;;;;;
+1545;CANADIAN SYLLABICS RAAI;Lo;0;L;;;;;N;;;;;
+1546;CANADIAN SYLLABICS RI;Lo;0;L;;;;;N;;;;;
+1547;CANADIAN SYLLABICS RII;Lo;0;L;;;;;N;;;;;
+1548;CANADIAN SYLLABICS RO;Lo;0;L;;;;;N;;;;;
+1549;CANADIAN SYLLABICS ROO;Lo;0;L;;;;;N;;;;;
+154A;CANADIAN SYLLABICS WEST-CREE LO;Lo;0;L;;;;;N;;;;;
+154B;CANADIAN SYLLABICS RA;Lo;0;L;;;;;N;;;;;
+154C;CANADIAN SYLLABICS RAA;Lo;0;L;;;;;N;;;;;
+154D;CANADIAN SYLLABICS WEST-CREE LA;Lo;0;L;;;;;N;;;;;
+154E;CANADIAN SYLLABICS RWAA;Lo;0;L;;;;;N;;;;;
+154F;CANADIAN SYLLABICS WEST-CREE RWAA;Lo;0;L;;;;;N;;;;;
+1550;CANADIAN SYLLABICS R;Lo;0;L;;;;;N;;;;;
+1551;CANADIAN SYLLABICS WEST-CREE R;Lo;0;L;;;;;N;;;;;
+1552;CANADIAN SYLLABICS MEDIAL R;Lo;0;L;;;;;N;;;;;
+1553;CANADIAN SYLLABICS FE;Lo;0;L;;;;;N;;;;;
+1554;CANADIAN SYLLABICS FAAI;Lo;0;L;;;;;N;;;;;
+1555;CANADIAN SYLLABICS FI;Lo;0;L;;;;;N;;;;;
+1556;CANADIAN SYLLABICS FII;Lo;0;L;;;;;N;;;;;
+1557;CANADIAN SYLLABICS FO;Lo;0;L;;;;;N;;;;;
+1558;CANADIAN SYLLABICS FOO;Lo;0;L;;;;;N;;;;;
+1559;CANADIAN SYLLABICS FA;Lo;0;L;;;;;N;;;;;
+155A;CANADIAN SYLLABICS FAA;Lo;0;L;;;;;N;;;;;
+155B;CANADIAN SYLLABICS FWAA;Lo;0;L;;;;;N;;;;;
+155C;CANADIAN SYLLABICS WEST-CREE FWAA;Lo;0;L;;;;;N;;;;;
+155D;CANADIAN SYLLABICS F;Lo;0;L;;;;;N;;;;;
+155E;CANADIAN SYLLABICS THE;Lo;0;L;;;;;N;;;;;
+155F;CANADIAN SYLLABICS N-CREE THE;Lo;0;L;;;;;N;;;;;
+1560;CANADIAN SYLLABICS THI;Lo;0;L;;;;;N;;;;;
+1561;CANADIAN SYLLABICS N-CREE THI;Lo;0;L;;;;;N;;;;;
+1562;CANADIAN SYLLABICS THII;Lo;0;L;;;;;N;;;;;
+1563;CANADIAN SYLLABICS N-CREE THII;Lo;0;L;;;;;N;;;;;
+1564;CANADIAN SYLLABICS THO;Lo;0;L;;;;;N;;;;;
+1565;CANADIAN SYLLABICS THOO;Lo;0;L;;;;;N;;;;;
+1566;CANADIAN SYLLABICS THA;Lo;0;L;;;;;N;;;;;
+1567;CANADIAN SYLLABICS THAA;Lo;0;L;;;;;N;;;;;
+1568;CANADIAN SYLLABICS THWAA;Lo;0;L;;;;;N;;;;;
+1569;CANADIAN SYLLABICS WEST-CREE THWAA;Lo;0;L;;;;;N;;;;;
+156A;CANADIAN SYLLABICS TH;Lo;0;L;;;;;N;;;;;
+156B;CANADIAN SYLLABICS TTHE;Lo;0;L;;;;;N;;;;;
+156C;CANADIAN SYLLABICS TTHI;Lo;0;L;;;;;N;;;;;
+156D;CANADIAN SYLLABICS TTHO;Lo;0;L;;;;;N;;;;;
+156E;CANADIAN SYLLABICS TTHA;Lo;0;L;;;;;N;;;;;
+156F;CANADIAN SYLLABICS TTH;Lo;0;L;;;;;N;;;;;
+1570;CANADIAN SYLLABICS TYE;Lo;0;L;;;;;N;;;;;
+1571;CANADIAN SYLLABICS TYI;Lo;0;L;;;;;N;;;;;
+1572;CANADIAN SYLLABICS TYO;Lo;0;L;;;;;N;;;;;
+1573;CANADIAN SYLLABICS TYA;Lo;0;L;;;;;N;;;;;
+1574;CANADIAN SYLLABICS NUNAVIK HE;Lo;0;L;;;;;N;;;;;
+1575;CANADIAN SYLLABICS NUNAVIK HI;Lo;0;L;;;;;N;;;;;
+1576;CANADIAN SYLLABICS NUNAVIK HII;Lo;0;L;;;;;N;;;;;
+1577;CANADIAN SYLLABICS NUNAVIK HO;Lo;0;L;;;;;N;;;;;
+1578;CANADIAN SYLLABICS NUNAVIK HOO;Lo;0;L;;;;;N;;;;;
+1579;CANADIAN SYLLABICS NUNAVIK HA;Lo;0;L;;;;;N;;;;;
+157A;CANADIAN SYLLABICS NUNAVIK HAA;Lo;0;L;;;;;N;;;;;
+157B;CANADIAN SYLLABICS NUNAVIK H;Lo;0;L;;;;;N;;;;;
+157C;CANADIAN SYLLABICS NUNAVUT H;Lo;0;L;;;;;N;;;;;
+157D;CANADIAN SYLLABICS HK;Lo;0;L;;;;;N;;;;;
+157E;CANADIAN SYLLABICS QAAI;Lo;0;L;;;;;N;;;;;
+157F;CANADIAN SYLLABICS QI;Lo;0;L;;;;;N;;;;;
+1580;CANADIAN SYLLABICS QII;Lo;0;L;;;;;N;;;;;
+1581;CANADIAN SYLLABICS QO;Lo;0;L;;;;;N;;;;;
+1582;CANADIAN SYLLABICS QOO;Lo;0;L;;;;;N;;;;;
+1583;CANADIAN SYLLABICS QA;Lo;0;L;;;;;N;;;;;
+1584;CANADIAN SYLLABICS QAA;Lo;0;L;;;;;N;;;;;
+1585;CANADIAN SYLLABICS Q;Lo;0;L;;;;;N;;;;;
+1586;CANADIAN SYLLABICS TLHE;Lo;0;L;;;;;N;;;;;
+1587;CANADIAN SYLLABICS TLHI;Lo;0;L;;;;;N;;;;;
+1588;CANADIAN SYLLABICS TLHO;Lo;0;L;;;;;N;;;;;
+1589;CANADIAN SYLLABICS TLHA;Lo;0;L;;;;;N;;;;;
+158A;CANADIAN SYLLABICS WEST-CREE RE;Lo;0;L;;;;;N;;;;;
+158B;CANADIAN SYLLABICS WEST-CREE RI;Lo;0;L;;;;;N;;;;;
+158C;CANADIAN SYLLABICS WEST-CREE RO;Lo;0;L;;;;;N;;;;;
+158D;CANADIAN SYLLABICS WEST-CREE RA;Lo;0;L;;;;;N;;;;;
+158E;CANADIAN SYLLABICS NGAAI;Lo;0;L;;;;;N;;;;;
+158F;CANADIAN SYLLABICS NGI;Lo;0;L;;;;;N;;;;;
+1590;CANADIAN SYLLABICS NGII;Lo;0;L;;;;;N;;;;;
+1591;CANADIAN SYLLABICS NGO;Lo;0;L;;;;;N;;;;;
+1592;CANADIAN SYLLABICS NGOO;Lo;0;L;;;;;N;;;;;
+1593;CANADIAN SYLLABICS NGA;Lo;0;L;;;;;N;;;;;
+1594;CANADIAN SYLLABICS NGAA;Lo;0;L;;;;;N;;;;;
+1595;CANADIAN SYLLABICS NG;Lo;0;L;;;;;N;;;;;
+1596;CANADIAN SYLLABICS NNG;Lo;0;L;;;;;N;;;;;
+1597;CANADIAN SYLLABICS SAYISI SHE;Lo;0;L;;;;;N;;;;;
+1598;CANADIAN SYLLABICS SAYISI SHI;Lo;0;L;;;;;N;;;;;
+1599;CANADIAN SYLLABICS SAYISI SHO;Lo;0;L;;;;;N;;;;;
+159A;CANADIAN SYLLABICS SAYISI SHA;Lo;0;L;;;;;N;;;;;
+159B;CANADIAN SYLLABICS WOODS-CREE THE;Lo;0;L;;;;;N;;;;;
+159C;CANADIAN SYLLABICS WOODS-CREE THI;Lo;0;L;;;;;N;;;;;
+159D;CANADIAN SYLLABICS WOODS-CREE THO;Lo;0;L;;;;;N;;;;;
+159E;CANADIAN SYLLABICS WOODS-CREE THA;Lo;0;L;;;;;N;;;;;
+159F;CANADIAN SYLLABICS WOODS-CREE TH;Lo;0;L;;;;;N;;;;;
+15A0;CANADIAN SYLLABICS LHI;Lo;0;L;;;;;N;;;;;
+15A1;CANADIAN SYLLABICS LHII;Lo;0;L;;;;;N;;;;;
+15A2;CANADIAN SYLLABICS LHO;Lo;0;L;;;;;N;;;;;
+15A3;CANADIAN SYLLABICS LHOO;Lo;0;L;;;;;N;;;;;
+15A4;CANADIAN SYLLABICS LHA;Lo;0;L;;;;;N;;;;;
+15A5;CANADIAN SYLLABICS LHAA;Lo;0;L;;;;;N;;;;;
+15A6;CANADIAN SYLLABICS LH;Lo;0;L;;;;;N;;;;;
+15A7;CANADIAN SYLLABICS TH-CREE THE;Lo;0;L;;;;;N;;;;;
+15A8;CANADIAN SYLLABICS TH-CREE THI;Lo;0;L;;;;;N;;;;;
+15A9;CANADIAN SYLLABICS TH-CREE THII;Lo;0;L;;;;;N;;;;;
+15AA;CANADIAN SYLLABICS TH-CREE THO;Lo;0;L;;;;;N;;;;;
+15AB;CANADIAN SYLLABICS TH-CREE THOO;Lo;0;L;;;;;N;;;;;
+15AC;CANADIAN SYLLABICS TH-CREE THA;Lo;0;L;;;;;N;;;;;
+15AD;CANADIAN SYLLABICS TH-CREE THAA;Lo;0;L;;;;;N;;;;;
+15AE;CANADIAN SYLLABICS TH-CREE TH;Lo;0;L;;;;;N;;;;;
+15AF;CANADIAN SYLLABICS AIVILIK B;Lo;0;L;;;;;N;;;;;
+15B0;CANADIAN SYLLABICS BLACKFOOT E;Lo;0;L;;;;;N;;;;;
+15B1;CANADIAN SYLLABICS BLACKFOOT I;Lo;0;L;;;;;N;;;;;
+15B2;CANADIAN SYLLABICS BLACKFOOT O;Lo;0;L;;;;;N;;;;;
+15B3;CANADIAN SYLLABICS BLACKFOOT A;Lo;0;L;;;;;N;;;;;
+15B4;CANADIAN SYLLABICS BLACKFOOT WE;Lo;0;L;;;;;N;;;;;
+15B5;CANADIAN SYLLABICS BLACKFOOT WI;Lo;0;L;;;;;N;;;;;
+15B6;CANADIAN SYLLABICS BLACKFOOT WO;Lo;0;L;;;;;N;;;;;
+15B7;CANADIAN SYLLABICS BLACKFOOT WA;Lo;0;L;;;;;N;;;;;
+15B8;CANADIAN SYLLABICS BLACKFOOT NE;Lo;0;L;;;;;N;;;;;
+15B9;CANADIAN SYLLABICS BLACKFOOT NI;Lo;0;L;;;;;N;;;;;
+15BA;CANADIAN SYLLABICS BLACKFOOT NO;Lo;0;L;;;;;N;;;;;
+15BB;CANADIAN SYLLABICS BLACKFOOT NA;Lo;0;L;;;;;N;;;;;
+15BC;CANADIAN SYLLABICS BLACKFOOT KE;Lo;0;L;;;;;N;;;;;
+15BD;CANADIAN SYLLABICS BLACKFOOT KI;Lo;0;L;;;;;N;;;;;
+15BE;CANADIAN SYLLABICS BLACKFOOT KO;Lo;0;L;;;;;N;;;;;
+15BF;CANADIAN SYLLABICS BLACKFOOT KA;Lo;0;L;;;;;N;;;;;
+15C0;CANADIAN SYLLABICS SAYISI HE;Lo;0;L;;;;;N;;;;;
+15C1;CANADIAN SYLLABICS SAYISI HI;Lo;0;L;;;;;N;;;;;
+15C2;CANADIAN SYLLABICS SAYISI HO;Lo;0;L;;;;;N;;;;;
+15C3;CANADIAN SYLLABICS SAYISI HA;Lo;0;L;;;;;N;;;;;
+15C4;CANADIAN SYLLABICS CARRIER GHU;Lo;0;L;;;;;N;;;;;
+15C5;CANADIAN SYLLABICS CARRIER GHO;Lo;0;L;;;;;N;;;;;
+15C6;CANADIAN SYLLABICS CARRIER GHE;Lo;0;L;;;;;N;;;;;
+15C7;CANADIAN SYLLABICS CARRIER GHEE;Lo;0;L;;;;;N;;;;;
+15C8;CANADIAN SYLLABICS CARRIER GHI;Lo;0;L;;;;;N;;;;;
+15C9;CANADIAN SYLLABICS CARRIER GHA;Lo;0;L;;;;;N;;;;;
+15CA;CANADIAN SYLLABICS CARRIER RU;Lo;0;L;;;;;N;;;;;
+15CB;CANADIAN SYLLABICS CARRIER RO;Lo;0;L;;;;;N;;;;;
+15CC;CANADIAN SYLLABICS CARRIER RE;Lo;0;L;;;;;N;;;;;
+15CD;CANADIAN SYLLABICS CARRIER REE;Lo;0;L;;;;;N;;;;;
+15CE;CANADIAN SYLLABICS CARRIER RI;Lo;0;L;;;;;N;;;;;
+15CF;CANADIAN SYLLABICS CARRIER RA;Lo;0;L;;;;;N;;;;;
+15D0;CANADIAN SYLLABICS CARRIER WU;Lo;0;L;;;;;N;;;;;
+15D1;CANADIAN SYLLABICS CARRIER WO;Lo;0;L;;;;;N;;;;;
+15D2;CANADIAN SYLLABICS CARRIER WE;Lo;0;L;;;;;N;;;;;
+15D3;CANADIAN SYLLABICS CARRIER WEE;Lo;0;L;;;;;N;;;;;
+15D4;CANADIAN SYLLABICS CARRIER WI;Lo;0;L;;;;;N;;;;;
+15D5;CANADIAN SYLLABICS CARRIER WA;Lo;0;L;;;;;N;;;;;
+15D6;CANADIAN SYLLABICS CARRIER HWU;Lo;0;L;;;;;N;;;;;
+15D7;CANADIAN SYLLABICS CARRIER HWO;Lo;0;L;;;;;N;;;;;
+15D8;CANADIAN SYLLABICS CARRIER HWE;Lo;0;L;;;;;N;;;;;
+15D9;CANADIAN SYLLABICS CARRIER HWEE;Lo;0;L;;;;;N;;;;;
+15DA;CANADIAN SYLLABICS CARRIER HWI;Lo;0;L;;;;;N;;;;;
+15DB;CANADIAN SYLLABICS CARRIER HWA;Lo;0;L;;;;;N;;;;;
+15DC;CANADIAN SYLLABICS CARRIER THU;Lo;0;L;;;;;N;;;;;
+15DD;CANADIAN SYLLABICS CARRIER THO;Lo;0;L;;;;;N;;;;;
+15DE;CANADIAN SYLLABICS CARRIER THE;Lo;0;L;;;;;N;;;;;
+15DF;CANADIAN SYLLABICS CARRIER THEE;Lo;0;L;;;;;N;;;;;
+15E0;CANADIAN SYLLABICS CARRIER THI;Lo;0;L;;;;;N;;;;;
+15E1;CANADIAN SYLLABICS CARRIER THA;Lo;0;L;;;;;N;;;;;
+15E2;CANADIAN SYLLABICS CARRIER TTU;Lo;0;L;;;;;N;;;;;
+15E3;CANADIAN SYLLABICS CARRIER TTO;Lo;0;L;;;;;N;;;;;
+15E4;CANADIAN SYLLABICS CARRIER TTE;Lo;0;L;;;;;N;;;;;
+15E5;CANADIAN SYLLABICS CARRIER TTEE;Lo;0;L;;;;;N;;;;;
+15E6;CANADIAN SYLLABICS CARRIER TTI;Lo;0;L;;;;;N;;;;;
+15E7;CANADIAN SYLLABICS CARRIER TTA;Lo;0;L;;;;;N;;;;;
+15E8;CANADIAN SYLLABICS CARRIER PU;Lo;0;L;;;;;N;;;;;
+15E9;CANADIAN SYLLABICS CARRIER PO;Lo;0;L;;;;;N;;;;;
+15EA;CANADIAN SYLLABICS CARRIER PE;Lo;0;L;;;;;N;;;;;
+15EB;CANADIAN SYLLABICS CARRIER PEE;Lo;0;L;;;;;N;;;;;
+15EC;CANADIAN SYLLABICS CARRIER PI;Lo;0;L;;;;;N;;;;;
+15ED;CANADIAN SYLLABICS CARRIER PA;Lo;0;L;;;;;N;;;;;
+15EE;CANADIAN SYLLABICS CARRIER P;Lo;0;L;;;;;N;;;;;
+15EF;CANADIAN SYLLABICS CARRIER GU;Lo;0;L;;;;;N;;;;;
+15F0;CANADIAN SYLLABICS CARRIER GO;Lo;0;L;;;;;N;;;;;
+15F1;CANADIAN SYLLABICS CARRIER GE;Lo;0;L;;;;;N;;;;;
+15F2;CANADIAN SYLLABICS CARRIER GEE;Lo;0;L;;;;;N;;;;;
+15F3;CANADIAN SYLLABICS CARRIER GI;Lo;0;L;;;;;N;;;;;
+15F4;CANADIAN SYLLABICS CARRIER GA;Lo;0;L;;;;;N;;;;;
+15F5;CANADIAN SYLLABICS CARRIER KHU;Lo;0;L;;;;;N;;;;;
+15F6;CANADIAN SYLLABICS CARRIER KHO;Lo;0;L;;;;;N;;;;;
+15F7;CANADIAN SYLLABICS CARRIER KHE;Lo;0;L;;;;;N;;;;;
+15F8;CANADIAN SYLLABICS CARRIER KHEE;Lo;0;L;;;;;N;;;;;
+15F9;CANADIAN SYLLABICS CARRIER KHI;Lo;0;L;;;;;N;;;;;
+15FA;CANADIAN SYLLABICS CARRIER KHA;Lo;0;L;;;;;N;;;;;
+15FB;CANADIAN SYLLABICS CARRIER KKU;Lo;0;L;;;;;N;;;;;
+15FC;CANADIAN SYLLABICS CARRIER KKO;Lo;0;L;;;;;N;;;;;
+15FD;CANADIAN SYLLABICS CARRIER KKE;Lo;0;L;;;;;N;;;;;
+15FE;CANADIAN SYLLABICS CARRIER KKEE;Lo;0;L;;;;;N;;;;;
+15FF;CANADIAN SYLLABICS CARRIER KKI;Lo;0;L;;;;;N;;;;;
+1600;CANADIAN SYLLABICS CARRIER KKA;Lo;0;L;;;;;N;;;;;
+1601;CANADIAN SYLLABICS CARRIER KK;Lo;0;L;;;;;N;;;;;
+1602;CANADIAN SYLLABICS CARRIER NU;Lo;0;L;;;;;N;;;;;
+1603;CANADIAN SYLLABICS CARRIER NO;Lo;0;L;;;;;N;;;;;
+1604;CANADIAN SYLLABICS CARRIER NE;Lo;0;L;;;;;N;;;;;
+1605;CANADIAN SYLLABICS CARRIER NEE;Lo;0;L;;;;;N;;;;;
+1606;CANADIAN SYLLABICS CARRIER NI;Lo;0;L;;;;;N;;;;;
+1607;CANADIAN SYLLABICS CARRIER NA;Lo;0;L;;;;;N;;;;;
+1608;CANADIAN SYLLABICS CARRIER MU;Lo;0;L;;;;;N;;;;;
+1609;CANADIAN SYLLABICS CARRIER MO;Lo;0;L;;;;;N;;;;;
+160A;CANADIAN SYLLABICS CARRIER ME;Lo;0;L;;;;;N;;;;;
+160B;CANADIAN SYLLABICS CARRIER MEE;Lo;0;L;;;;;N;;;;;
+160C;CANADIAN SYLLABICS CARRIER MI;Lo;0;L;;;;;N;;;;;
+160D;CANADIAN SYLLABICS CARRIER MA;Lo;0;L;;;;;N;;;;;
+160E;CANADIAN SYLLABICS CARRIER YU;Lo;0;L;;;;;N;;;;;
+160F;CANADIAN SYLLABICS CARRIER YO;Lo;0;L;;;;;N;;;;;
+1610;CANADIAN SYLLABICS CARRIER YE;Lo;0;L;;;;;N;;;;;
+1611;CANADIAN SYLLABICS CARRIER YEE;Lo;0;L;;;;;N;;;;;
+1612;CANADIAN SYLLABICS CARRIER YI;Lo;0;L;;;;;N;;;;;
+1613;CANADIAN SYLLABICS CARRIER YA;Lo;0;L;;;;;N;;;;;
+1614;CANADIAN SYLLABICS CARRIER JU;Lo;0;L;;;;;N;;;;;
+1615;CANADIAN SYLLABICS SAYISI JU;Lo;0;L;;;;;N;;;;;
+1616;CANADIAN SYLLABICS CARRIER JO;Lo;0;L;;;;;N;;;;;
+1617;CANADIAN SYLLABICS CARRIER JE;Lo;0;L;;;;;N;;;;;
+1618;CANADIAN SYLLABICS CARRIER JEE;Lo;0;L;;;;;N;;;;;
+1619;CANADIAN SYLLABICS CARRIER JI;Lo;0;L;;;;;N;;;;;
+161A;CANADIAN SYLLABICS SAYISI JI;Lo;0;L;;;;;N;;;;;
+161B;CANADIAN SYLLABICS CARRIER JA;Lo;0;L;;;;;N;;;;;
+161C;CANADIAN SYLLABICS CARRIER JJU;Lo;0;L;;;;;N;;;;;
+161D;CANADIAN SYLLABICS CARRIER JJO;Lo;0;L;;;;;N;;;;;
+161E;CANADIAN SYLLABICS CARRIER JJE;Lo;0;L;;;;;N;;;;;
+161F;CANADIAN SYLLABICS CARRIER JJEE;Lo;0;L;;;;;N;;;;;
+1620;CANADIAN SYLLABICS CARRIER JJI;Lo;0;L;;;;;N;;;;;
+1621;CANADIAN SYLLABICS CARRIER JJA;Lo;0;L;;;;;N;;;;;
+1622;CANADIAN SYLLABICS CARRIER LU;Lo;0;L;;;;;N;;;;;
+1623;CANADIAN SYLLABICS CARRIER LO;Lo;0;L;;;;;N;;;;;
+1624;CANADIAN SYLLABICS CARRIER LE;Lo;0;L;;;;;N;;;;;
+1625;CANADIAN SYLLABICS CARRIER LEE;Lo;0;L;;;;;N;;;;;
+1626;CANADIAN SYLLABICS CARRIER LI;Lo;0;L;;;;;N;;;;;
+1627;CANADIAN SYLLABICS CARRIER LA;Lo;0;L;;;;;N;;;;;
+1628;CANADIAN SYLLABICS CARRIER DLU;Lo;0;L;;;;;N;;;;;
+1629;CANADIAN SYLLABICS CARRIER DLO;Lo;0;L;;;;;N;;;;;
+162A;CANADIAN SYLLABICS CARRIER DLE;Lo;0;L;;;;;N;;;;;
+162B;CANADIAN SYLLABICS CARRIER DLEE;Lo;0;L;;;;;N;;;;;
+162C;CANADIAN SYLLABICS CARRIER DLI;Lo;0;L;;;;;N;;;;;
+162D;CANADIAN SYLLABICS CARRIER DLA;Lo;0;L;;;;;N;;;;;
+162E;CANADIAN SYLLABICS CARRIER LHU;Lo;0;L;;;;;N;;;;;
+162F;CANADIAN SYLLABICS CARRIER LHO;Lo;0;L;;;;;N;;;;;
+1630;CANADIAN SYLLABICS CARRIER LHE;Lo;0;L;;;;;N;;;;;
+1631;CANADIAN SYLLABICS CARRIER LHEE;Lo;0;L;;;;;N;;;;;
+1632;CANADIAN SYLLABICS CARRIER LHI;Lo;0;L;;;;;N;;;;;
+1633;CANADIAN SYLLABICS CARRIER LHA;Lo;0;L;;;;;N;;;;;
+1634;CANADIAN SYLLABICS CARRIER TLHU;Lo;0;L;;;;;N;;;;;
+1635;CANADIAN SYLLABICS CARRIER TLHO;Lo;0;L;;;;;N;;;;;
+1636;CANADIAN SYLLABICS CARRIER TLHE;Lo;0;L;;;;;N;;;;;
+1637;CANADIAN SYLLABICS CARRIER TLHEE;Lo;0;L;;;;;N;;;;;
+1638;CANADIAN SYLLABICS CARRIER TLHI;Lo;0;L;;;;;N;;;;;
+1639;CANADIAN SYLLABICS CARRIER TLHA;Lo;0;L;;;;;N;;;;;
+163A;CANADIAN SYLLABICS CARRIER TLU;Lo;0;L;;;;;N;;;;;
+163B;CANADIAN SYLLABICS CARRIER TLO;Lo;0;L;;;;;N;;;;;
+163C;CANADIAN SYLLABICS CARRIER TLE;Lo;0;L;;;;;N;;;;;
+163D;CANADIAN SYLLABICS CARRIER TLEE;Lo;0;L;;;;;N;;;;;
+163E;CANADIAN SYLLABICS CARRIER TLI;Lo;0;L;;;;;N;;;;;
+163F;CANADIAN SYLLABICS CARRIER TLA;Lo;0;L;;;;;N;;;;;
+1640;CANADIAN SYLLABICS CARRIER ZU;Lo;0;L;;;;;N;;;;;
+1641;CANADIAN SYLLABICS CARRIER ZO;Lo;0;L;;;;;N;;;;;
+1642;CANADIAN SYLLABICS CARRIER ZE;Lo;0;L;;;;;N;;;;;
+1643;CANADIAN SYLLABICS CARRIER ZEE;Lo;0;L;;;;;N;;;;;
+1644;CANADIAN SYLLABICS CARRIER ZI;Lo;0;L;;;;;N;;;;;
+1645;CANADIAN SYLLABICS CARRIER ZA;Lo;0;L;;;;;N;;;;;
+1646;CANADIAN SYLLABICS CARRIER Z;Lo;0;L;;;;;N;;;;;
+1647;CANADIAN SYLLABICS CARRIER INITIAL Z;Lo;0;L;;;;;N;;;;;
+1648;CANADIAN SYLLABICS CARRIER DZU;Lo;0;L;;;;;N;;;;;
+1649;CANADIAN SYLLABICS CARRIER DZO;Lo;0;L;;;;;N;;;;;
+164A;CANADIAN SYLLABICS CARRIER DZE;Lo;0;L;;;;;N;;;;;
+164B;CANADIAN SYLLABICS CARRIER DZEE;Lo;0;L;;;;;N;;;;;
+164C;CANADIAN SYLLABICS CARRIER DZI;Lo;0;L;;;;;N;;;;;
+164D;CANADIAN SYLLABICS CARRIER DZA;Lo;0;L;;;;;N;;;;;
+164E;CANADIAN SYLLABICS CARRIER SU;Lo;0;L;;;;;N;;;;;
+164F;CANADIAN SYLLABICS CARRIER SO;Lo;0;L;;;;;N;;;;;
+1650;CANADIAN SYLLABICS CARRIER SE;Lo;0;L;;;;;N;;;;;
+1651;CANADIAN SYLLABICS CARRIER SEE;Lo;0;L;;;;;N;;;;;
+1652;CANADIAN SYLLABICS CARRIER SI;Lo;0;L;;;;;N;;;;;
+1653;CANADIAN SYLLABICS CARRIER SA;Lo;0;L;;;;;N;;;;;
+1654;CANADIAN SYLLABICS CARRIER SHU;Lo;0;L;;;;;N;;;;;
+1655;CANADIAN SYLLABICS CARRIER SHO;Lo;0;L;;;;;N;;;;;
+1656;CANADIAN SYLLABICS CARRIER SHE;Lo;0;L;;;;;N;;;;;
+1657;CANADIAN SYLLABICS CARRIER SHEE;Lo;0;L;;;;;N;;;;;
+1658;CANADIAN SYLLABICS CARRIER SHI;Lo;0;L;;;;;N;;;;;
+1659;CANADIAN SYLLABICS CARRIER SHA;Lo;0;L;;;;;N;;;;;
+165A;CANADIAN SYLLABICS CARRIER SH;Lo;0;L;;;;;N;;;;;
+165B;CANADIAN SYLLABICS CARRIER TSU;Lo;0;L;;;;;N;;;;;
+165C;CANADIAN SYLLABICS CARRIER TSO;Lo;0;L;;;;;N;;;;;
+165D;CANADIAN SYLLABICS CARRIER TSE;Lo;0;L;;;;;N;;;;;
+165E;CANADIAN SYLLABICS CARRIER TSEE;Lo;0;L;;;;;N;;;;;
+165F;CANADIAN SYLLABICS CARRIER TSI;Lo;0;L;;;;;N;;;;;
+1660;CANADIAN SYLLABICS CARRIER TSA;Lo;0;L;;;;;N;;;;;
+1661;CANADIAN SYLLABICS CARRIER CHU;Lo;0;L;;;;;N;;;;;
+1662;CANADIAN SYLLABICS CARRIER CHO;Lo;0;L;;;;;N;;;;;
+1663;CANADIAN SYLLABICS CARRIER CHE;Lo;0;L;;;;;N;;;;;
+1664;CANADIAN SYLLABICS CARRIER CHEE;Lo;0;L;;;;;N;;;;;
+1665;CANADIAN SYLLABICS CARRIER CHI;Lo;0;L;;;;;N;;;;;
+1666;CANADIAN SYLLABICS CARRIER CHA;Lo;0;L;;;;;N;;;;;
+1667;CANADIAN SYLLABICS CARRIER TTSU;Lo;0;L;;;;;N;;;;;
+1668;CANADIAN SYLLABICS CARRIER TTSO;Lo;0;L;;;;;N;;;;;
+1669;CANADIAN SYLLABICS CARRIER TTSE;Lo;0;L;;;;;N;;;;;
+166A;CANADIAN SYLLABICS CARRIER TTSEE;Lo;0;L;;;;;N;;;;;
+166B;CANADIAN SYLLABICS CARRIER TTSI;Lo;0;L;;;;;N;;;;;
+166C;CANADIAN SYLLABICS CARRIER TTSA;Lo;0;L;;;;;N;;;;;
+166D;CANADIAN SYLLABICS CHI SIGN;Po;0;L;;;;;N;;;;;
+166E;CANADIAN SYLLABICS FULL STOP;Po;0;L;;;;;N;;;;;
+166F;CANADIAN SYLLABICS QAI;Lo;0;L;;;;;N;;;;;
+1670;CANADIAN SYLLABICS NGAI;Lo;0;L;;;;;N;;;;;
+1671;CANADIAN SYLLABICS NNGI;Lo;0;L;;;;;N;;;;;
+1672;CANADIAN SYLLABICS NNGII;Lo;0;L;;;;;N;;;;;
+1673;CANADIAN SYLLABICS NNGO;Lo;0;L;;;;;N;;;;;
+1674;CANADIAN SYLLABICS NNGOO;Lo;0;L;;;;;N;;;;;
+1675;CANADIAN SYLLABICS NNGA;Lo;0;L;;;;;N;;;;;
+1676;CANADIAN SYLLABICS NNGAA;Lo;0;L;;;;;N;;;;;
+1680;OGHAM SPACE MARK;Zs;0;WS;;;;;N;;;;;
+1681;OGHAM LETTER BEITH;Lo;0;L;;;;;N;;;;;
+1682;OGHAM LETTER LUIS;Lo;0;L;;;;;N;;;;;
+1683;OGHAM LETTER FEARN;Lo;0;L;;;;;N;;;;;
+1684;OGHAM LETTER SAIL;Lo;0;L;;;;;N;;;;;
+1685;OGHAM LETTER NION;Lo;0;L;;;;;N;;;;;
+1686;OGHAM LETTER UATH;Lo;0;L;;;;;N;;;;;
+1687;OGHAM LETTER DAIR;Lo;0;L;;;;;N;;;;;
+1688;OGHAM LETTER TINNE;Lo;0;L;;;;;N;;;;;
+1689;OGHAM LETTER COLL;Lo;0;L;;;;;N;;;;;
+168A;OGHAM LETTER CEIRT;Lo;0;L;;;;;N;;;;;
+168B;OGHAM LETTER MUIN;Lo;0;L;;;;;N;;;;;
+168C;OGHAM LETTER GORT;Lo;0;L;;;;;N;;;;;
+168D;OGHAM LETTER NGEADAL;Lo;0;L;;;;;N;;;;;
+168E;OGHAM LETTER STRAIF;Lo;0;L;;;;;N;;;;;
+168F;OGHAM LETTER RUIS;Lo;0;L;;;;;N;;;;;
+1690;OGHAM LETTER AILM;Lo;0;L;;;;;N;;;;;
+1691;OGHAM LETTER ONN;Lo;0;L;;;;;N;;;;;
+1692;OGHAM LETTER UR;Lo;0;L;;;;;N;;;;;
+1693;OGHAM LETTER EADHADH;Lo;0;L;;;;;N;;;;;
+1694;OGHAM LETTER IODHADH;Lo;0;L;;;;;N;;;;;
+1695;OGHAM LETTER EABHADH;Lo;0;L;;;;;N;;;;;
+1696;OGHAM LETTER OR;Lo;0;L;;;;;N;;;;;
+1697;OGHAM LETTER UILLEANN;Lo;0;L;;;;;N;;;;;
+1698;OGHAM LETTER IFIN;Lo;0;L;;;;;N;;;;;
+1699;OGHAM LETTER EAMHANCHOLL;Lo;0;L;;;;;N;;;;;
+169A;OGHAM LETTER PEITH;Lo;0;L;;;;;N;;;;;
+169B;OGHAM FEATHER MARK;Ps;0;ON;;;;;N;;;;;
+169C;OGHAM REVERSED FEATHER MARK;Pe;0;ON;;;;;N;;;;;
+16A0;RUNIC LETTER FEHU FEOH FE F;Lo;0;L;;;;;N;;;;;
+16A1;RUNIC LETTER V;Lo;0;L;;;;;N;;;;;
+16A2;RUNIC LETTER URUZ UR U;Lo;0;L;;;;;N;;;;;
+16A3;RUNIC LETTER YR;Lo;0;L;;;;;N;;;;;
+16A4;RUNIC LETTER Y;Lo;0;L;;;;;N;;;;;
+16A5;RUNIC LETTER W;Lo;0;L;;;;;N;;;;;
+16A6;RUNIC LETTER THURISAZ THURS THORN;Lo;0;L;;;;;N;;;;;
+16A7;RUNIC LETTER ETH;Lo;0;L;;;;;N;;;;;
+16A8;RUNIC LETTER ANSUZ A;Lo;0;L;;;;;N;;;;;
+16A9;RUNIC LETTER OS O;Lo;0;L;;;;;N;;;;;
+16AA;RUNIC LETTER AC A;Lo;0;L;;;;;N;;;;;
+16AB;RUNIC LETTER AESC;Lo;0;L;;;;;N;;;;;
+16AC;RUNIC LETTER LONG-BRANCH-OSS O;Lo;0;L;;;;;N;;;;;
+16AD;RUNIC LETTER SHORT-TWIG-OSS O;Lo;0;L;;;;;N;;;;;
+16AE;RUNIC LETTER O;Lo;0;L;;;;;N;;;;;
+16AF;RUNIC LETTER OE;Lo;0;L;;;;;N;;;;;
+16B0;RUNIC LETTER ON;Lo;0;L;;;;;N;;;;;
+16B1;RUNIC LETTER RAIDO RAD REID R;Lo;0;L;;;;;N;;;;;
+16B2;RUNIC LETTER KAUNA;Lo;0;L;;;;;N;;;;;
+16B3;RUNIC LETTER CEN;Lo;0;L;;;;;N;;;;;
+16B4;RUNIC LETTER KAUN K;Lo;0;L;;;;;N;;;;;
+16B5;RUNIC LETTER G;Lo;0;L;;;;;N;;;;;
+16B6;RUNIC LETTER ENG;Lo;0;L;;;;;N;;;;;
+16B7;RUNIC LETTER GEBO GYFU G;Lo;0;L;;;;;N;;;;;
+16B8;RUNIC LETTER GAR;Lo;0;L;;;;;N;;;;;
+16B9;RUNIC LETTER WUNJO WYNN W;Lo;0;L;;;;;N;;;;;
+16BA;RUNIC LETTER HAGLAZ H;Lo;0;L;;;;;N;;;;;
+16BB;RUNIC LETTER HAEGL H;Lo;0;L;;;;;N;;;;;
+16BC;RUNIC LETTER LONG-BRANCH-HAGALL H;Lo;0;L;;;;;N;;;;;
+16BD;RUNIC LETTER SHORT-TWIG-HAGALL H;Lo;0;L;;;;;N;;;;;
+16BE;RUNIC LETTER NAUDIZ NYD NAUD N;Lo;0;L;;;;;N;;;;;
+16BF;RUNIC LETTER SHORT-TWIG-NAUD N;Lo;0;L;;;;;N;;;;;
+16C0;RUNIC LETTER DOTTED-N;Lo;0;L;;;;;N;;;;;
+16C1;RUNIC LETTER ISAZ IS ISS I;Lo;0;L;;;;;N;;;;;
+16C2;RUNIC LETTER E;Lo;0;L;;;;;N;;;;;
+16C3;RUNIC LETTER JERAN J;Lo;0;L;;;;;N;;;;;
+16C4;RUNIC LETTER GER;Lo;0;L;;;;;N;;;;;
+16C5;RUNIC LETTER LONG-BRANCH-AR AE;Lo;0;L;;;;;N;;;;;
+16C6;RUNIC LETTER SHORT-TWIG-AR A;Lo;0;L;;;;;N;;;;;
+16C7;RUNIC LETTER IWAZ EOH;Lo;0;L;;;;;N;;;;;
+16C8;RUNIC LETTER PERTHO PEORTH P;Lo;0;L;;;;;N;;;;;
+16C9;RUNIC LETTER ALGIZ EOLHX;Lo;0;L;;;;;N;;;;;
+16CA;RUNIC LETTER SOWILO S;Lo;0;L;;;;;N;;;;;
+16CB;RUNIC LETTER SIGEL LONG-BRANCH-SOL S;Lo;0;L;;;;;N;;;;;
+16CC;RUNIC LETTER SHORT-TWIG-SOL S;Lo;0;L;;;;;N;;;;;
+16CD;RUNIC LETTER C;Lo;0;L;;;;;N;;;;;
+16CE;RUNIC LETTER Z;Lo;0;L;;;;;N;;;;;
+16CF;RUNIC LETTER TIWAZ TIR TYR T;Lo;0;L;;;;;N;;;;;
+16D0;RUNIC LETTER SHORT-TWIG-TYR T;Lo;0;L;;;;;N;;;;;
+16D1;RUNIC LETTER D;Lo;0;L;;;;;N;;;;;
+16D2;RUNIC LETTER BERKANAN BEORC BJARKAN B;Lo;0;L;;;;;N;;;;;
+16D3;RUNIC LETTER SHORT-TWIG-BJARKAN B;Lo;0;L;;;;;N;;;;;
+16D4;RUNIC LETTER DOTTED-P;Lo;0;L;;;;;N;;;;;
+16D5;RUNIC LETTER OPEN-P;Lo;0;L;;;;;N;;;;;
+16D6;RUNIC LETTER EHWAZ EH E;Lo;0;L;;;;;N;;;;;
+16D7;RUNIC LETTER MANNAZ MAN M;Lo;0;L;;;;;N;;;;;
+16D8;RUNIC LETTER LONG-BRANCH-MADR M;Lo;0;L;;;;;N;;;;;
+16D9;RUNIC LETTER SHORT-TWIG-MADR M;Lo;0;L;;;;;N;;;;;
+16DA;RUNIC LETTER LAUKAZ LAGU LOGR L;Lo;0;L;;;;;N;;;;;
+16DB;RUNIC LETTER DOTTED-L;Lo;0;L;;;;;N;;;;;
+16DC;RUNIC LETTER INGWAZ;Lo;0;L;;;;;N;;;;;
+16DD;RUNIC LETTER ING;Lo;0;L;;;;;N;;;;;
+16DE;RUNIC LETTER DAGAZ DAEG D;Lo;0;L;;;;;N;;;;;
+16DF;RUNIC LETTER OTHALAN ETHEL O;Lo;0;L;;;;;N;;;;;
+16E0;RUNIC LETTER EAR;Lo;0;L;;;;;N;;;;;
+16E1;RUNIC LETTER IOR;Lo;0;L;;;;;N;;;;;
+16E2;RUNIC LETTER CWEORTH;Lo;0;L;;;;;N;;;;;
+16E3;RUNIC LETTER CALC;Lo;0;L;;;;;N;;;;;
+16E4;RUNIC LETTER CEALC;Lo;0;L;;;;;N;;;;;
+16E5;RUNIC LETTER STAN;Lo;0;L;;;;;N;;;;;
+16E6;RUNIC LETTER LONG-BRANCH-YR;Lo;0;L;;;;;N;;;;;
+16E7;RUNIC LETTER SHORT-TWIG-YR;Lo;0;L;;;;;N;;;;;
+16E8;RUNIC LETTER ICELANDIC-YR;Lo;0;L;;;;;N;;;;;
+16E9;RUNIC LETTER Q;Lo;0;L;;;;;N;;;;;
+16EA;RUNIC LETTER X;Lo;0;L;;;;;N;;;;;
+16EB;RUNIC SINGLE PUNCTUATION;Po;0;L;;;;;N;;;;;
+16EC;RUNIC MULTIPLE PUNCTUATION;Po;0;L;;;;;N;;;;;
+16ED;RUNIC CROSS PUNCTUATION;Po;0;L;;;;;N;;;;;
+16EE;RUNIC ARLAUG SYMBOL;Nl;0;L;;;;17;N;;golden number 17;;;
+16EF;RUNIC TVIMADUR SYMBOL;Nl;0;L;;;;18;N;;golden number 18;;;
+16F0;RUNIC BELGTHOR SYMBOL;Nl;0;L;;;;19;N;;golden number 19;;;
+1700;TAGALOG LETTER A;Lo;0;L;;;;;N;;;;;
+1701;TAGALOG LETTER I;Lo;0;L;;;;;N;;;;;
+1702;TAGALOG LETTER U;Lo;0;L;;;;;N;;;;;
+1703;TAGALOG LETTER KA;Lo;0;L;;;;;N;;;;;
+1704;TAGALOG LETTER GA;Lo;0;L;;;;;N;;;;;
+1705;TAGALOG LETTER NGA;Lo;0;L;;;;;N;;;;;
+1706;TAGALOG LETTER TA;Lo;0;L;;;;;N;;;;;
+1707;TAGALOG LETTER DA;Lo;0;L;;;;;N;;;;;
+1708;TAGALOG LETTER NA;Lo;0;L;;;;;N;;;;;
+1709;TAGALOG LETTER PA;Lo;0;L;;;;;N;;;;;
+170A;TAGALOG LETTER BA;Lo;0;L;;;;;N;;;;;
+170B;TAGALOG LETTER MA;Lo;0;L;;;;;N;;;;;
+170C;TAGALOG LETTER YA;Lo;0;L;;;;;N;;;;;
+170E;TAGALOG LETTER LA;Lo;0;L;;;;;N;;;;;
+170F;TAGALOG LETTER WA;Lo;0;L;;;;;N;;;;;
+1710;TAGALOG LETTER SA;Lo;0;L;;;;;N;;;;;
+1711;TAGALOG LETTER HA;Lo;0;L;;;;;N;;;;;
+1712;TAGALOG VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
+1713;TAGALOG VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+1714;TAGALOG SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+1720;HANUNOO LETTER A;Lo;0;L;;;;;N;;;;;
+1721;HANUNOO LETTER I;Lo;0;L;;;;;N;;;;;
+1722;HANUNOO LETTER U;Lo;0;L;;;;;N;;;;;
+1723;HANUNOO LETTER KA;Lo;0;L;;;;;N;;;;;
+1724;HANUNOO LETTER GA;Lo;0;L;;;;;N;;;;;
+1725;HANUNOO LETTER NGA;Lo;0;L;;;;;N;;;;;
+1726;HANUNOO LETTER TA;Lo;0;L;;;;;N;;;;;
+1727;HANUNOO LETTER DA;Lo;0;L;;;;;N;;;;;
+1728;HANUNOO LETTER NA;Lo;0;L;;;;;N;;;;;
+1729;HANUNOO LETTER PA;Lo;0;L;;;;;N;;;;;
+172A;HANUNOO LETTER BA;Lo;0;L;;;;;N;;;;;
+172B;HANUNOO LETTER MA;Lo;0;L;;;;;N;;;;;
+172C;HANUNOO LETTER YA;Lo;0;L;;;;;N;;;;;
+172D;HANUNOO LETTER RA;Lo;0;L;;;;;N;;;;;
+172E;HANUNOO LETTER LA;Lo;0;L;;;;;N;;;;;
+172F;HANUNOO LETTER WA;Lo;0;L;;;;;N;;;;;
+1730;HANUNOO LETTER SA;Lo;0;L;;;;;N;;;;;
+1731;HANUNOO LETTER HA;Lo;0;L;;;;;N;;;;;
+1732;HANUNOO VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
+1733;HANUNOO VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+1734;HANUNOO SIGN PAMUDPOD;Mn;9;NSM;;;;;N;;;;;
+1735;PHILIPPINE SINGLE PUNCTUATION;Po;0;L;;;;;N;;;;;
+1736;PHILIPPINE DOUBLE PUNCTUATION;Po;0;L;;;;;N;;;;;
+1740;BUHID LETTER A;Lo;0;L;;;;;N;;;;;
+1741;BUHID LETTER I;Lo;0;L;;;;;N;;;;;
+1742;BUHID LETTER U;Lo;0;L;;;;;N;;;;;
+1743;BUHID LETTER KA;Lo;0;L;;;;;N;;;;;
+1744;BUHID LETTER GA;Lo;0;L;;;;;N;;;;;
+1745;BUHID LETTER NGA;Lo;0;L;;;;;N;;;;;
+1746;BUHID LETTER TA;Lo;0;L;;;;;N;;;;;
+1747;BUHID LETTER DA;Lo;0;L;;;;;N;;;;;
+1748;BUHID LETTER NA;Lo;0;L;;;;;N;;;;;
+1749;BUHID LETTER PA;Lo;0;L;;;;;N;;;;;
+174A;BUHID LETTER BA;Lo;0;L;;;;;N;;;;;
+174B;BUHID LETTER MA;Lo;0;L;;;;;N;;;;;
+174C;BUHID LETTER YA;Lo;0;L;;;;;N;;;;;
+174D;BUHID LETTER RA;Lo;0;L;;;;;N;;;;;
+174E;BUHID LETTER LA;Lo;0;L;;;;;N;;;;;
+174F;BUHID LETTER WA;Lo;0;L;;;;;N;;;;;
+1750;BUHID LETTER SA;Lo;0;L;;;;;N;;;;;
+1751;BUHID LETTER HA;Lo;0;L;;;;;N;;;;;
+1752;BUHID VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
+1753;BUHID VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+1760;TAGBANWA LETTER A;Lo;0;L;;;;;N;;;;;
+1761;TAGBANWA LETTER I;Lo;0;L;;;;;N;;;;;
+1762;TAGBANWA LETTER U;Lo;0;L;;;;;N;;;;;
+1763;TAGBANWA LETTER KA;Lo;0;L;;;;;N;;;;;
+1764;TAGBANWA LETTER GA;Lo;0;L;;;;;N;;;;;
+1765;TAGBANWA LETTER NGA;Lo;0;L;;;;;N;;;;;
+1766;TAGBANWA LETTER TA;Lo;0;L;;;;;N;;;;;
+1767;TAGBANWA LETTER DA;Lo;0;L;;;;;N;;;;;
+1768;TAGBANWA LETTER NA;Lo;0;L;;;;;N;;;;;
+1769;TAGBANWA LETTER PA;Lo;0;L;;;;;N;;;;;
+176A;TAGBANWA LETTER BA;Lo;0;L;;;;;N;;;;;
+176B;TAGBANWA LETTER MA;Lo;0;L;;;;;N;;;;;
+176C;TAGBANWA LETTER YA;Lo;0;L;;;;;N;;;;;
+176E;TAGBANWA LETTER LA;Lo;0;L;;;;;N;;;;;
+176F;TAGBANWA LETTER WA;Lo;0;L;;;;;N;;;;;
+1770;TAGBANWA LETTER SA;Lo;0;L;;;;;N;;;;;
+1772;TAGBANWA VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
+1773;TAGBANWA VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+1780;KHMER LETTER KA;Lo;0;L;;;;;N;;;;;
+1781;KHMER LETTER KHA;Lo;0;L;;;;;N;;;;;
+1782;KHMER LETTER KO;Lo;0;L;;;;;N;;;;;
+1783;KHMER LETTER KHO;Lo;0;L;;;;;N;;;;;
+1784;KHMER LETTER NGO;Lo;0;L;;;;;N;;;;;
+1785;KHMER LETTER CA;Lo;0;L;;;;;N;;;;;
+1786;KHMER LETTER CHA;Lo;0;L;;;;;N;;;;;
+1787;KHMER LETTER CO;Lo;0;L;;;;;N;;;;;
+1788;KHMER LETTER CHO;Lo;0;L;;;;;N;;;;;
+1789;KHMER LETTER NYO;Lo;0;L;;;;;N;;;;;
+178A;KHMER LETTER DA;Lo;0;L;;;;;N;;;;;
+178B;KHMER LETTER TTHA;Lo;0;L;;;;;N;;;;;
+178C;KHMER LETTER DO;Lo;0;L;;;;;N;;;;;
+178D;KHMER LETTER TTHO;Lo;0;L;;;;;N;;;;;
+178E;KHMER LETTER NNO;Lo;0;L;;;;;N;;;;;
+178F;KHMER LETTER TA;Lo;0;L;;;;;N;;;;;
+1790;KHMER LETTER THA;Lo;0;L;;;;;N;;;;;
+1791;KHMER LETTER TO;Lo;0;L;;;;;N;;;;;
+1792;KHMER LETTER THO;Lo;0;L;;;;;N;;;;;
+1793;KHMER LETTER NO;Lo;0;L;;;;;N;;;;;
+1794;KHMER LETTER BA;Lo;0;L;;;;;N;;;;;
+1795;KHMER LETTER PHA;Lo;0;L;;;;;N;;;;;
+1796;KHMER LETTER PO;Lo;0;L;;;;;N;;;;;
+1797;KHMER LETTER PHO;Lo;0;L;;;;;N;;;;;
+1798;KHMER LETTER MO;Lo;0;L;;;;;N;;;;;
+1799;KHMER LETTER YO;Lo;0;L;;;;;N;;;;;
+179A;KHMER LETTER RO;Lo;0;L;;;;;N;;;;;
+179B;KHMER LETTER LO;Lo;0;L;;;;;N;;;;;
+179C;KHMER LETTER VO;Lo;0;L;;;;;N;;;;;
+179D;KHMER LETTER SHA;Lo;0;L;;;;;N;;;;;
+179E;KHMER LETTER SSO;Lo;0;L;;;;;N;;;;;
+179F;KHMER LETTER SA;Lo;0;L;;;;;N;;;;;
+17A0;KHMER LETTER HA;Lo;0;L;;;;;N;;;;;
+17A1;KHMER LETTER LA;Lo;0;L;;;;;N;;;;;
+17A2;KHMER LETTER QA;Lo;0;L;;;;;N;;;;;
+17A3;KHMER INDEPENDENT VOWEL QAQ;Lo;0;L;;;;;N;;*;;;
+17A4;KHMER INDEPENDENT VOWEL QAA;Lo;0;L;;;;;N;;*;;;
+17A5;KHMER INDEPENDENT VOWEL QI;Lo;0;L;;;;;N;;;;;
+17A6;KHMER INDEPENDENT VOWEL QII;Lo;0;L;;;;;N;;;;;
+17A7;KHMER INDEPENDENT VOWEL QU;Lo;0;L;;;;;N;;;;;
+17A8;KHMER INDEPENDENT VOWEL QUK;Lo;0;L;;;;;N;;;;;
+17A9;KHMER INDEPENDENT VOWEL QUU;Lo;0;L;;;;;N;;;;;
+17AA;KHMER INDEPENDENT VOWEL QUUV;Lo;0;L;;;;;N;;;;;
+17AB;KHMER INDEPENDENT VOWEL RY;Lo;0;L;;;;;N;;;;;
+17AC;KHMER INDEPENDENT VOWEL RYY;Lo;0;L;;;;;N;;;;;
+17AD;KHMER INDEPENDENT VOWEL LY;Lo;0;L;;;;;N;;;;;
+17AE;KHMER INDEPENDENT VOWEL LYY;Lo;0;L;;;;;N;;;;;
+17AF;KHMER INDEPENDENT VOWEL QE;Lo;0;L;;;;;N;;;;;
+17B0;KHMER INDEPENDENT VOWEL QAI;Lo;0;L;;;;;N;;;;;
+17B1;KHMER INDEPENDENT VOWEL QOO TYPE ONE;Lo;0;L;;;;;N;;;;;
+17B2;KHMER INDEPENDENT VOWEL QOO TYPE TWO;Lo;0;L;;;;;N;;;;;
+17B3;KHMER INDEPENDENT VOWEL QAU;Lo;0;L;;;;;N;;;;;
+17B4;KHMER VOWEL INHERENT AQ;Cf;0;L;;;;;N;;*;;;
+17B5;KHMER VOWEL INHERENT AA;Cf;0;L;;;;;N;;*;;;
+17B6;KHMER VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+17B7;KHMER VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
+17B8;KHMER VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;;
+17B9;KHMER VOWEL SIGN Y;Mn;0;NSM;;;;;N;;;;;
+17BA;KHMER VOWEL SIGN YY;Mn;0;NSM;;;;;N;;;;;
+17BB;KHMER VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+17BC;KHMER VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+17BD;KHMER VOWEL SIGN UA;Mn;0;NSM;;;;;N;;;;;
+17BE;KHMER VOWEL SIGN OE;Mc;0;L;;;;;N;;;;;
+17BF;KHMER VOWEL SIGN YA;Mc;0;L;;;;;N;;;;;
+17C0;KHMER VOWEL SIGN IE;Mc;0;L;;;;;N;;;;;
+17C1;KHMER VOWEL SIGN E;Mc;0;L;;;;;N;;;;;
+17C2;KHMER VOWEL SIGN AE;Mc;0;L;;;;;N;;;;;
+17C3;KHMER VOWEL SIGN AI;Mc;0;L;;;;;N;;;;;
+17C4;KHMER VOWEL SIGN OO;Mc;0;L;;;;;N;;;;;
+17C5;KHMER VOWEL SIGN AU;Mc;0;L;;;;;N;;;;;
+17C6;KHMER SIGN NIKAHIT;Mn;0;NSM;;;;;N;;;;;
+17C7;KHMER SIGN REAHMUK;Mc;0;L;;;;;N;;;;;
+17C8;KHMER SIGN YUUKALEAPINTU;Mc;0;L;;;;;N;;;;;
+17C9;KHMER SIGN MUUSIKATOAN;Mn;0;NSM;;;;;N;;;;;
+17CA;KHMER SIGN TRIISAP;Mn;0;NSM;;;;;N;;;;;
+17CB;KHMER SIGN BANTOC;Mn;0;NSM;;;;;N;;;;;
+17CC;KHMER SIGN ROBAT;Mn;0;NSM;;;;;N;;;;;
+17CD;KHMER SIGN TOANDAKHIAT;Mn;0;NSM;;;;;N;;;;;
+17CE;KHMER SIGN KAKABAT;Mn;0;NSM;;;;;N;;;;;
+17CF;KHMER SIGN AHSDA;Mn;0;NSM;;;;;N;;;;;
+17D0;KHMER SIGN SAMYOK SANNYA;Mn;0;NSM;;;;;N;;;;;
+17D1;KHMER SIGN VIRIAM;Mn;0;NSM;;;;;N;;;;;
+17D2;KHMER SIGN COENG;Mn;9;NSM;;;;;N;;;;;
+17D3;KHMER SIGN BATHAMASAT;Mn;0;NSM;;;;;N;;*;;;
+17D4;KHMER SIGN KHAN;Po;0;L;;;;;N;;;;;
+17D5;KHMER SIGN BARIYOOSAN;Po;0;L;;;;;N;;;;;
+17D6;KHMER SIGN CAMNUC PII KUUH;Po;0;L;;;;;N;;;;;
+17D7;KHMER SIGN LEK TOO;Lm;0;L;;;;;N;;;;;
+17D8;KHMER SIGN BEYYAL;Po;0;L;;;;;N;;*;;;
+17D9;KHMER SIGN PHNAEK MUAN;Po;0;L;;;;;N;;;;;
+17DA;KHMER SIGN KOOMUUT;Po;0;L;;;;;N;;;;;
+17DB;KHMER CURRENCY SYMBOL RIEL;Sc;0;ET;;;;;N;;;;;
+17DC;KHMER SIGN AVAKRAHASANYA;Lo;0;L;;;;;N;;;;;
+17DD;KHMER SIGN ATTHACAN;Mn;230;NSM;;;;;N;;;;;
+17E0;KHMER DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+17E1;KHMER DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+17E2;KHMER DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+17E3;KHMER DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+17E4;KHMER DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+17E5;KHMER DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+17E6;KHMER DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+17E7;KHMER DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+17E8;KHMER DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+17E9;KHMER DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+17F0;KHMER SYMBOL LEK ATTAK SON;No;0;ON;;;;0;N;;;;;
+17F1;KHMER SYMBOL LEK ATTAK MUOY;No;0;ON;;;;1;N;;;;;
+17F2;KHMER SYMBOL LEK ATTAK PII;No;0;ON;;;;2;N;;;;;
+17F3;KHMER SYMBOL LEK ATTAK BEI;No;0;ON;;;;3;N;;;;;
+17F4;KHMER SYMBOL LEK ATTAK BUON;No;0;ON;;;;4;N;;;;;
+17F5;KHMER SYMBOL LEK ATTAK PRAM;No;0;ON;;;;5;N;;;;;
+17F6;KHMER SYMBOL LEK ATTAK PRAM-MUOY;No;0;ON;;;;6;N;;;;;
+17F7;KHMER SYMBOL LEK ATTAK PRAM-PII;No;0;ON;;;;7;N;;;;;
+17F8;KHMER SYMBOL LEK ATTAK PRAM-BEI;No;0;ON;;;;8;N;;;;;
+17F9;KHMER SYMBOL LEK ATTAK PRAM-BUON;No;0;ON;;;;9;N;;;;;
+1800;MONGOLIAN BIRGA;Po;0;ON;;;;;N;;;;;
+1801;MONGOLIAN ELLIPSIS;Po;0;ON;;;;;N;;;;;
+1802;MONGOLIAN COMMA;Po;0;ON;;;;;N;;;;;
+1803;MONGOLIAN FULL STOP;Po;0;ON;;;;;N;;;;;
+1804;MONGOLIAN COLON;Po;0;ON;;;;;N;;;;;
+1805;MONGOLIAN FOUR DOTS;Po;0;ON;;;;;N;;;;;
+1806;MONGOLIAN TODO SOFT HYPHEN;Pd;0;ON;;;;;N;;;;;
+1807;MONGOLIAN SIBE SYLLABLE BOUNDARY MARKER;Po;0;ON;;;;;N;;;;;
+1808;MONGOLIAN MANCHU COMMA;Po;0;ON;;;;;N;;;;;
+1809;MONGOLIAN MANCHU FULL STOP;Po;0;ON;;;;;N;;;;;
+180A;MONGOLIAN NIRUGU;Po;0;ON;;;;;N;;;;;
+180B;MONGOLIAN FREE VARIATION SELECTOR ONE;Mn;0;NSM;;;;;N;;;;;
+180C;MONGOLIAN FREE VARIATION SELECTOR TWO;Mn;0;NSM;;;;;N;;;;;
+180D;MONGOLIAN FREE VARIATION SELECTOR THREE;Mn;0;NSM;;;;;N;;;;;
+180E;MONGOLIAN VOWEL SEPARATOR;Zs;0;WS;;;;;N;;;;;
+1810;MONGOLIAN DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+1811;MONGOLIAN DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+1812;MONGOLIAN DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+1813;MONGOLIAN DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+1814;MONGOLIAN DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+1815;MONGOLIAN DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+1816;MONGOLIAN DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+1817;MONGOLIAN DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+1818;MONGOLIAN DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+1819;MONGOLIAN DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+1820;MONGOLIAN LETTER A;Lo;0;L;;;;;N;;;;;
+1821;MONGOLIAN LETTER E;Lo;0;L;;;;;N;;;;;
+1822;MONGOLIAN LETTER I;Lo;0;L;;;;;N;;;;;
+1823;MONGOLIAN LETTER O;Lo;0;L;;;;;N;;;;;
+1824;MONGOLIAN LETTER U;Lo;0;L;;;;;N;;;;;
+1825;MONGOLIAN LETTER OE;Lo;0;L;;;;;N;;;;;
+1826;MONGOLIAN LETTER UE;Lo;0;L;;;;;N;;;;;
+1827;MONGOLIAN LETTER EE;Lo;0;L;;;;;N;;;;;
+1828;MONGOLIAN LETTER NA;Lo;0;L;;;;;N;;;;;
+1829;MONGOLIAN LETTER ANG;Lo;0;L;;;;;N;;;;;
+182A;MONGOLIAN LETTER BA;Lo;0;L;;;;;N;;;;;
+182B;MONGOLIAN LETTER PA;Lo;0;L;;;;;N;;;;;
+182C;MONGOLIAN LETTER QA;Lo;0;L;;;;;N;;;;;
+182D;MONGOLIAN LETTER GA;Lo;0;L;;;;;N;;;;;
+182E;MONGOLIAN LETTER MA;Lo;0;L;;;;;N;;;;;
+182F;MONGOLIAN LETTER LA;Lo;0;L;;;;;N;;;;;
+1830;MONGOLIAN LETTER SA;Lo;0;L;;;;;N;;;;;
+1831;MONGOLIAN LETTER SHA;Lo;0;L;;;;;N;;;;;
+1832;MONGOLIAN LETTER TA;Lo;0;L;;;;;N;;;;;
+1833;MONGOLIAN LETTER DA;Lo;0;L;;;;;N;;;;;
+1834;MONGOLIAN LETTER CHA;Lo;0;L;;;;;N;;;;;
+1835;MONGOLIAN LETTER JA;Lo;0;L;;;;;N;;;;;
+1836;MONGOLIAN LETTER YA;Lo;0;L;;;;;N;;;;;
+1837;MONGOLIAN LETTER RA;Lo;0;L;;;;;N;;;;;
+1838;MONGOLIAN LETTER WA;Lo;0;L;;;;;N;;;;;
+1839;MONGOLIAN LETTER FA;Lo;0;L;;;;;N;;;;;
+183A;MONGOLIAN LETTER KA;Lo;0;L;;;;;N;;;;;
+183B;MONGOLIAN LETTER KHA;Lo;0;L;;;;;N;;;;;
+183C;MONGOLIAN LETTER TSA;Lo;0;L;;;;;N;;;;;
+183D;MONGOLIAN LETTER ZA;Lo;0;L;;;;;N;;;;;
+183E;MONGOLIAN LETTER HAA;Lo;0;L;;;;;N;;;;;
+183F;MONGOLIAN LETTER ZRA;Lo;0;L;;;;;N;;;;;
+1840;MONGOLIAN LETTER LHA;Lo;0;L;;;;;N;;;;;
+1841;MONGOLIAN LETTER ZHI;Lo;0;L;;;;;N;;;;;
+1842;MONGOLIAN LETTER CHI;Lo;0;L;;;;;N;;;;;
+1843;MONGOLIAN LETTER TODO LONG VOWEL SIGN;Lm;0;L;;;;;N;;;;;
+1844;MONGOLIAN LETTER TODO E;Lo;0;L;;;;;N;;;;;
+1845;MONGOLIAN LETTER TODO I;Lo;0;L;;;;;N;;;;;
+1846;MONGOLIAN LETTER TODO O;Lo;0;L;;;;;N;;;;;
+1847;MONGOLIAN LETTER TODO U;Lo;0;L;;;;;N;;;;;
+1848;MONGOLIAN LETTER TODO OE;Lo;0;L;;;;;N;;;;;
+1849;MONGOLIAN LETTER TODO UE;Lo;0;L;;;;;N;;;;;
+184A;MONGOLIAN LETTER TODO ANG;Lo;0;L;;;;;N;;;;;
+184B;MONGOLIAN LETTER TODO BA;Lo;0;L;;;;;N;;;;;
+184C;MONGOLIAN LETTER TODO PA;Lo;0;L;;;;;N;;;;;
+184D;MONGOLIAN LETTER TODO QA;Lo;0;L;;;;;N;;;;;
+184E;MONGOLIAN LETTER TODO GA;Lo;0;L;;;;;N;;;;;
+184F;MONGOLIAN LETTER TODO MA;Lo;0;L;;;;;N;;;;;
+1850;MONGOLIAN LETTER TODO TA;Lo;0;L;;;;;N;;;;;
+1851;MONGOLIAN LETTER TODO DA;Lo;0;L;;;;;N;;;;;
+1852;MONGOLIAN LETTER TODO CHA;Lo;0;L;;;;;N;;;;;
+1853;MONGOLIAN LETTER TODO JA;Lo;0;L;;;;;N;;;;;
+1854;MONGOLIAN LETTER TODO TSA;Lo;0;L;;;;;N;;;;;
+1855;MONGOLIAN LETTER TODO YA;Lo;0;L;;;;;N;;;;;
+1856;MONGOLIAN LETTER TODO WA;Lo;0;L;;;;;N;;;;;
+1857;MONGOLIAN LETTER TODO KA;Lo;0;L;;;;;N;;;;;
+1858;MONGOLIAN LETTER TODO GAA;Lo;0;L;;;;;N;;;;;
+1859;MONGOLIAN LETTER TODO HAA;Lo;0;L;;;;;N;;;;;
+185A;MONGOLIAN LETTER TODO JIA;Lo;0;L;;;;;N;;;;;
+185B;MONGOLIAN LETTER TODO NIA;Lo;0;L;;;;;N;;;;;
+185C;MONGOLIAN LETTER TODO DZA;Lo;0;L;;;;;N;;;;;
+185D;MONGOLIAN LETTER SIBE E;Lo;0;L;;;;;N;;;;;
+185E;MONGOLIAN LETTER SIBE I;Lo;0;L;;;;;N;;;;;
+185F;MONGOLIAN LETTER SIBE IY;Lo;0;L;;;;;N;;;;;
+1860;MONGOLIAN LETTER SIBE UE;Lo;0;L;;;;;N;;;;;
+1861;MONGOLIAN LETTER SIBE U;Lo;0;L;;;;;N;;;;;
+1862;MONGOLIAN LETTER SIBE ANG;Lo;0;L;;;;;N;;;;;
+1863;MONGOLIAN LETTER SIBE KA;Lo;0;L;;;;;N;;;;;
+1864;MONGOLIAN LETTER SIBE GA;Lo;0;L;;;;;N;;;;;
+1865;MONGOLIAN LETTER SIBE HA;Lo;0;L;;;;;N;;;;;
+1866;MONGOLIAN LETTER SIBE PA;Lo;0;L;;;;;N;;;;;
+1867;MONGOLIAN LETTER SIBE SHA;Lo;0;L;;;;;N;;;;;
+1868;MONGOLIAN LETTER SIBE TA;Lo;0;L;;;;;N;;;;;
+1869;MONGOLIAN LETTER SIBE DA;Lo;0;L;;;;;N;;;;;
+186A;MONGOLIAN LETTER SIBE JA;Lo;0;L;;;;;N;;;;;
+186B;MONGOLIAN LETTER SIBE FA;Lo;0;L;;;;;N;;;;;
+186C;MONGOLIAN LETTER SIBE GAA;Lo;0;L;;;;;N;;;;;
+186D;MONGOLIAN LETTER SIBE HAA;Lo;0;L;;;;;N;;;;;
+186E;MONGOLIAN LETTER SIBE TSA;Lo;0;L;;;;;N;;;;;
+186F;MONGOLIAN LETTER SIBE ZA;Lo;0;L;;;;;N;;;;;
+1870;MONGOLIAN LETTER SIBE RAA;Lo;0;L;;;;;N;;;;;
+1871;MONGOLIAN LETTER SIBE CHA;Lo;0;L;;;;;N;;;;;
+1872;MONGOLIAN LETTER SIBE ZHA;Lo;0;L;;;;;N;;;;;
+1873;MONGOLIAN LETTER MANCHU I;Lo;0;L;;;;;N;;;;;
+1874;MONGOLIAN LETTER MANCHU KA;Lo;0;L;;;;;N;;;;;
+1875;MONGOLIAN LETTER MANCHU RA;Lo;0;L;;;;;N;;;;;
+1876;MONGOLIAN LETTER MANCHU FA;Lo;0;L;;;;;N;;;;;
+1877;MONGOLIAN LETTER MANCHU ZHA;Lo;0;L;;;;;N;;;;;
+1880;MONGOLIAN LETTER ALI GALI ANUSVARA ONE;Lo;0;L;;;;;N;;;;;
+1881;MONGOLIAN LETTER ALI GALI VISARGA ONE;Lo;0;L;;;;;N;;;;;
+1882;MONGOLIAN LETTER ALI GALI DAMARU;Lo;0;L;;;;;N;;;;;
+1883;MONGOLIAN LETTER ALI GALI UBADAMA;Lo;0;L;;;;;N;;;;;
+1884;MONGOLIAN LETTER ALI GALI INVERTED UBADAMA;Lo;0;L;;;;;N;;;;;
+1885;MONGOLIAN LETTER ALI GALI BALUDA;Lo;0;L;;;;;N;;;;;
+1886;MONGOLIAN LETTER ALI GALI THREE BALUDA;Lo;0;L;;;;;N;;;;;
+1887;MONGOLIAN LETTER ALI GALI A;Lo;0;L;;;;;N;;;;;
+1888;MONGOLIAN LETTER ALI GALI I;Lo;0;L;;;;;N;;;;;
+1889;MONGOLIAN LETTER ALI GALI KA;Lo;0;L;;;;;N;;;;;
+188A;MONGOLIAN LETTER ALI GALI NGA;Lo;0;L;;;;;N;;;;;
+188B;MONGOLIAN LETTER ALI GALI CA;Lo;0;L;;;;;N;;;;;
+188C;MONGOLIAN LETTER ALI GALI TTA;Lo;0;L;;;;;N;;;;;
+188D;MONGOLIAN LETTER ALI GALI TTHA;Lo;0;L;;;;;N;;;;;
+188E;MONGOLIAN LETTER ALI GALI DDA;Lo;0;L;;;;;N;;;;;
+188F;MONGOLIAN LETTER ALI GALI NNA;Lo;0;L;;;;;N;;;;;
+1890;MONGOLIAN LETTER ALI GALI TA;Lo;0;L;;;;;N;;;;;
+1891;MONGOLIAN LETTER ALI GALI DA;Lo;0;L;;;;;N;;;;;
+1892;MONGOLIAN LETTER ALI GALI PA;Lo;0;L;;;;;N;;;;;
+1893;MONGOLIAN LETTER ALI GALI PHA;Lo;0;L;;;;;N;;;;;
+1894;MONGOLIAN LETTER ALI GALI SSA;Lo;0;L;;;;;N;;;;;
+1895;MONGOLIAN LETTER ALI GALI ZHA;Lo;0;L;;;;;N;;;;;
+1896;MONGOLIAN LETTER ALI GALI ZA;Lo;0;L;;;;;N;;;;;
+1897;MONGOLIAN LETTER ALI GALI AH;Lo;0;L;;;;;N;;;;;
+1898;MONGOLIAN LETTER TODO ALI GALI TA;Lo;0;L;;;;;N;;;;;
+1899;MONGOLIAN LETTER TODO ALI GALI ZHA;Lo;0;L;;;;;N;;;;;
+189A;MONGOLIAN LETTER MANCHU ALI GALI GHA;Lo;0;L;;;;;N;;;;;
+189B;MONGOLIAN LETTER MANCHU ALI GALI NGA;Lo;0;L;;;;;N;;;;;
+189C;MONGOLIAN LETTER MANCHU ALI GALI CA;Lo;0;L;;;;;N;;;;;
+189D;MONGOLIAN LETTER MANCHU ALI GALI JHA;Lo;0;L;;;;;N;;;;;
+189E;MONGOLIAN LETTER MANCHU ALI GALI TTA;Lo;0;L;;;;;N;;;;;
+189F;MONGOLIAN LETTER MANCHU ALI GALI DDHA;Lo;0;L;;;;;N;;;;;
+18A0;MONGOLIAN LETTER MANCHU ALI GALI TA;Lo;0;L;;;;;N;;;;;
+18A1;MONGOLIAN LETTER MANCHU ALI GALI DHA;Lo;0;L;;;;;N;;;;;
+18A2;MONGOLIAN LETTER MANCHU ALI GALI SSA;Lo;0;L;;;;;N;;;;;
+18A3;MONGOLIAN LETTER MANCHU ALI GALI CYA;Lo;0;L;;;;;N;;;;;
+18A4;MONGOLIAN LETTER MANCHU ALI GALI ZHA;Lo;0;L;;;;;N;;;;;
+18A5;MONGOLIAN LETTER MANCHU ALI GALI ZA;Lo;0;L;;;;;N;;;;;
+18A6;MONGOLIAN LETTER ALI GALI HALF U;Lo;0;L;;;;;N;;;;;
+18A7;MONGOLIAN LETTER ALI GALI HALF YA;Lo;0;L;;;;;N;;;;;
+18A8;MONGOLIAN LETTER MANCHU ALI GALI BHA;Lo;0;L;;;;;N;;;;;
+18A9;MONGOLIAN LETTER ALI GALI DAGALGA;Mn;228;NSM;;;;;N;;;;;
+1900;LIMBU VOWEL-CARRIER LETTER;Lo;0;L;;;;;N;;;;;
+1901;LIMBU LETTER KA;Lo;0;L;;;;;N;;;;;
+1902;LIMBU LETTER KHA;Lo;0;L;;;;;N;;;;;
+1903;LIMBU LETTER GA;Lo;0;L;;;;;N;;;;;
+1904;LIMBU LETTER GHA;Lo;0;L;;;;;N;;;;;
+1905;LIMBU LETTER NGA;Lo;0;L;;;;;N;;;;;
+1906;LIMBU LETTER CA;Lo;0;L;;;;;N;;;;;
+1907;LIMBU LETTER CHA;Lo;0;L;;;;;N;;;;;
+1908;LIMBU LETTER JA;Lo;0;L;;;;;N;;;;;
+1909;LIMBU LETTER JHA;Lo;0;L;;;;;N;;;;;
+190A;LIMBU LETTER YAN;Lo;0;L;;;;;N;;;;;
+190B;LIMBU LETTER TA;Lo;0;L;;;;;N;;;;;
+190C;LIMBU LETTER THA;Lo;0;L;;;;;N;;;;;
+190D;LIMBU LETTER DA;Lo;0;L;;;;;N;;;;;
+190E;LIMBU LETTER DHA;Lo;0;L;;;;;N;;;;;
+190F;LIMBU LETTER NA;Lo;0;L;;;;;N;;;;;
+1910;LIMBU LETTER PA;Lo;0;L;;;;;N;;;;;
+1911;LIMBU LETTER PHA;Lo;0;L;;;;;N;;;;;
+1912;LIMBU LETTER BA;Lo;0;L;;;;;N;;;;;
+1913;LIMBU LETTER BHA;Lo;0;L;;;;;N;;;;;
+1914;LIMBU LETTER MA;Lo;0;L;;;;;N;;;;;
+1915;LIMBU LETTER YA;Lo;0;L;;;;;N;;;;;
+1916;LIMBU LETTER RA;Lo;0;L;;;;;N;;;;;
+1917;LIMBU LETTER LA;Lo;0;L;;;;;N;;;;;
+1918;LIMBU LETTER WA;Lo;0;L;;;;;N;;;;;
+1919;LIMBU LETTER SHA;Lo;0;L;;;;;N;;;;;
+191A;LIMBU LETTER SSA;Lo;0;L;;;;;N;;;;;
+191B;LIMBU LETTER SA;Lo;0;L;;;;;N;;;;;
+191C;LIMBU LETTER HA;Lo;0;L;;;;;N;;;;;
+1920;LIMBU VOWEL SIGN A;Mn;0;NSM;;;;;N;;;;;
+1921;LIMBU VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
+1922;LIMBU VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+1923;LIMBU VOWEL SIGN EE;Mc;0;L;;;;;N;;;;;
+1924;LIMBU VOWEL SIGN AI;Mc;0;L;;;;;N;;;;;
+1925;LIMBU VOWEL SIGN OO;Mc;0;L;;;;;N;;;;;
+1926;LIMBU VOWEL SIGN AU;Mc;0;L;;;;;N;;;;;
+1927;LIMBU VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;;
+1928;LIMBU VOWEL SIGN O;Mn;0;NSM;;;;;N;;;;;
+1929;LIMBU SUBJOINED LETTER YA;Mc;0;NSM;;;;;N;;;;;
+192A;LIMBU SUBJOINED LETTER RA;Mc;0;NSM;;;;;N;;;;;
+192B;LIMBU SUBJOINED LETTER WA;Mc;0;NSM;;;;;N;;;;;
+1930;LIMBU SMALL LETTER KA;Mc;0;L;;;;;N;;;;;
+1931;LIMBU SMALL LETTER NGA;Mc;0;L;;;;;N;;;;;
+1932;LIMBU SMALL LETTER ANUSVARA;Mn;0;NSM;;;;;N;;;;;
+1933;LIMBU SMALL LETTER TA;Mc;0;L;;;;;N;;;;;
+1934;LIMBU SMALL LETTER NA;Mc;0;L;;;;;N;;;;;
+1935;LIMBU SMALL LETTER PA;Mc;0;L;;;;;N;;;;;
+1936;LIMBU SMALL LETTER MA;Mc;0;L;;;;;N;;;;;
+1937;LIMBU SMALL LETTER RA;Mc;0;L;;;;;N;;;;;
+1938;LIMBU SMALL LETTER LA;Mc;0;L;;;;;N;;;;;
+1939;LIMBU SIGN MUKPHRENG;Mn;222;NSM;;;;;N;;;;;
+193A;LIMBU SIGN KEMPHRENG;Mn;230;NSM;;;;;N;;;;;
+193B;LIMBU SIGN SA-I;Mn;220;NSM;;;;;N;;;;;
+1940;LIMBU SIGN LOO;So;0;ON;;;;;N;;;;;
+1944;LIMBU EXCLAMATION MARK;Po;0;ON;;;;;N;;;;;
+1945;LIMBU QUESTION MARK;Po;0;ON;;;;;N;;;;;
+1946;LIMBU DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+1947;LIMBU DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+1948;LIMBU DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+1949;LIMBU DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+194A;LIMBU DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+194B;LIMBU DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+194C;LIMBU DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+194D;LIMBU DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+194E;LIMBU DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+194F;LIMBU DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+1950;TAI LE LETTER KA;Lo;0;L;;;;;N;;;;;
+1951;TAI LE LETTER XA;Lo;0;L;;;;;N;;;;;
+1952;TAI LE LETTER NGA;Lo;0;L;;;;;N;;;;;
+1953;TAI LE LETTER TSA;Lo;0;L;;;;;N;;;;;
+1954;TAI LE LETTER SA;Lo;0;L;;;;;N;;;;;
+1955;TAI LE LETTER YA;Lo;0;L;;;;;N;;;;;
+1956;TAI LE LETTER TA;Lo;0;L;;;;;N;;;;;
+1957;TAI LE LETTER THA;Lo;0;L;;;;;N;;;;;
+1958;TAI LE LETTER LA;Lo;0;L;;;;;N;;;;;
+1959;TAI LE LETTER PA;Lo;0;L;;;;;N;;;;;
+195A;TAI LE LETTER PHA;Lo;0;L;;;;;N;;;;;
+195B;TAI LE LETTER MA;Lo;0;L;;;;;N;;;;;
+195C;TAI LE LETTER FA;Lo;0;L;;;;;N;;;;;
+195D;TAI LE LETTER VA;Lo;0;L;;;;;N;;;;;
+195E;TAI LE LETTER HA;Lo;0;L;;;;;N;;;;;
+195F;TAI LE LETTER QA;Lo;0;L;;;;;N;;;;;
+1960;TAI LE LETTER KHA;Lo;0;L;;;;;N;;;;;
+1961;TAI LE LETTER TSHA;Lo;0;L;;;;;N;;;;;
+1962;TAI LE LETTER NA;Lo;0;L;;;;;N;;;;;
+1963;TAI LE LETTER A;Lo;0;L;;;;;N;;;;;
+1964;TAI LE LETTER I;Lo;0;L;;;;;N;;;;;
+1965;TAI LE LETTER EE;Lo;0;L;;;;;N;;;;;
+1966;TAI LE LETTER EH;Lo;0;L;;;;;N;;;;;
+1967;TAI LE LETTER U;Lo;0;L;;;;;N;;;;;
+1968;TAI LE LETTER OO;Lo;0;L;;;;;N;;;;;
+1969;TAI LE LETTER O;Lo;0;L;;;;;N;;;;;
+196A;TAI LE LETTER UE;Lo;0;L;;;;;N;;;;;
+196B;TAI LE LETTER E;Lo;0;L;;;;;N;;;;;
+196C;TAI LE LETTER AUE;Lo;0;L;;;;;N;;;;;
+196D;TAI LE LETTER AI;Lo;0;L;;;;;N;;;;;
+1970;TAI LE LETTER TONE-2;Lo;0;L;;;;;N;;;;;
+1971;TAI LE LETTER TONE-3;Lo;0;L;;;;;N;;;;;
+1972;TAI LE LETTER TONE-4;Lo;0;L;;;;;N;;;;;
+1973;TAI LE LETTER TONE-5;Lo;0;L;;;;;N;;;;;
+1974;TAI LE LETTER TONE-6;Lo;0;L;;;;;N;;;;;
+19E0;KHMER SYMBOL PATHAMASAT;So;0;ON;;;;;N;;;;;
+19E1;KHMER SYMBOL MUOY KOET;So;0;ON;;;;;N;;;;;
+19E2;KHMER SYMBOL PII KOET;So;0;ON;;;;;N;;;;;
+19E3;KHMER SYMBOL BEI KOET;So;0;ON;;;;;N;;;;;
+19E4;KHMER SYMBOL BUON KOET;So;0;ON;;;;;N;;;;;
+19E5;KHMER SYMBOL PRAM KOET;So;0;ON;;;;;N;;;;;
+19E6;KHMER SYMBOL PRAM-MUOY KOET;So;0;ON;;;;;N;;;;;
+19E7;KHMER SYMBOL PRAM-PII KOET;So;0;ON;;;;;N;;;;;
+19E8;KHMER SYMBOL PRAM-BEI KOET;So;0;ON;;;;;N;;;;;
+19E9;KHMER SYMBOL PRAM-BUON KOET;So;0;ON;;;;;N;;;;;
+19EA;KHMER SYMBOL DAP KOET;So;0;ON;;;;;N;;;;;
+19EB;KHMER SYMBOL DAP-MUOY KOET;So;0;ON;;;;;N;;;;;
+19EC;KHMER SYMBOL DAP-PII KOET;So;0;ON;;;;;N;;;;;
+19ED;KHMER SYMBOL DAP-BEI KOET;So;0;ON;;;;;N;;;;;
+19EE;KHMER SYMBOL DAP-BUON KOET;So;0;ON;;;;;N;;;;;
+19EF;KHMER SYMBOL DAP-PRAM KOET;So;0;ON;;;;;N;;;;;
+19F0;KHMER SYMBOL TUTEYASAT;So;0;ON;;;;;N;;;;;
+19F1;KHMER SYMBOL MUOY ROC;So;0;ON;;;;;N;;;;;
+19F2;KHMER SYMBOL PII ROC;So;0;ON;;;;;N;;;;;
+19F3;KHMER SYMBOL BEI ROC;So;0;ON;;;;;N;;;;;
+19F4;KHMER SYMBOL BUON ROC;So;0;ON;;;;;N;;;;;
+19F5;KHMER SYMBOL PRAM ROC;So;0;ON;;;;;N;;;;;
+19F6;KHMER SYMBOL PRAM-MUOY ROC;So;0;ON;;;;;N;;;;;
+19F7;KHMER SYMBOL PRAM-PII ROC;So;0;ON;;;;;N;;;;;
+19F8;KHMER SYMBOL PRAM-BEI ROC;So;0;ON;;;;;N;;;;;
+19F9;KHMER SYMBOL PRAM-BUON ROC;So;0;ON;;;;;N;;;;;
+19FA;KHMER SYMBOL DAP ROC;So;0;ON;;;;;N;;;;;
+19FB;KHMER SYMBOL DAP-MUOY ROC;So;0;ON;;;;;N;;;;;
+19FC;KHMER SYMBOL DAP-PII ROC;So;0;ON;;;;;N;;;;;
+19FD;KHMER SYMBOL DAP-BEI ROC;So;0;ON;;;;;N;;;;;
+19FE;KHMER SYMBOL DAP-BUON ROC;So;0;ON;;;;;N;;;;;
+19FF;KHMER SYMBOL DAP-PRAM ROC;So;0;ON;;;;;N;;;;;
+1D00;LATIN LETTER SMALL CAPITAL A;Ll;0;L;;;;;N;;;;;
+1D01;LATIN LETTER SMALL CAPITAL AE;Ll;0;L;;;;;N;;;;;
+1D02;LATIN SMALL LETTER TURNED AE;Ll;0;L;;;;;N;;;;;
+1D03;LATIN LETTER SMALL CAPITAL BARRED B;Ll;0;L;;;;;N;;;;;
+1D04;LATIN LETTER SMALL CAPITAL C;Ll;0;L;;;;;N;;;;;
+1D05;LATIN LETTER SMALL CAPITAL D;Ll;0;L;;;;;N;;;;;
+1D06;LATIN LETTER SMALL CAPITAL ETH;Ll;0;L;;;;;N;;;;;
+1D07;LATIN LETTER SMALL CAPITAL E;Ll;0;L;;;;;N;;;;;
+1D08;LATIN SMALL LETTER TURNED OPEN E;Ll;0;L;;;;;N;;;;;
+1D09;LATIN SMALL LETTER TURNED I;Ll;0;L;;;;;N;;;;;
+1D0A;LATIN LETTER SMALL CAPITAL J;Ll;0;L;;;;;N;;;;;
+1D0B;LATIN LETTER SMALL CAPITAL K;Ll;0;L;;;;;N;;;;;
+1D0C;LATIN LETTER SMALL CAPITAL L WITH STROKE;Ll;0;L;;;;;N;;;;;
+1D0D;LATIN LETTER SMALL CAPITAL M;Ll;0;L;;;;;N;;;;;
+1D0E;LATIN LETTER SMALL CAPITAL REVERSED N;Ll;0;L;;;;;N;;;;;
+1D0F;LATIN LETTER SMALL CAPITAL O;Ll;0;L;;;;;N;;;;;
+1D10;LATIN LETTER SMALL CAPITAL OPEN O;Ll;0;L;;;;;N;;;;;
+1D11;LATIN SMALL LETTER SIDEWAYS O;Ll;0;L;;;;;N;;;;;
+1D12;LATIN SMALL LETTER SIDEWAYS OPEN O;Ll;0;L;;;;;N;;;;;
+1D13;LATIN SMALL LETTER SIDEWAYS O WITH STROKE;Ll;0;L;;;;;N;;;;;
+1D14;LATIN SMALL LETTER TURNED OE;Ll;0;L;;;;;N;;;;;
+1D15;LATIN LETTER SMALL CAPITAL OU;Ll;0;L;;;;;N;;;;;
+1D16;LATIN SMALL LETTER TOP HALF O;Ll;0;L;;;;;N;;;;;
+1D17;LATIN SMALL LETTER BOTTOM HALF O;Ll;0;L;;;;;N;;;;;
+1D18;LATIN LETTER SMALL CAPITAL P;Ll;0;L;;;;;N;;;;;
+1D19;LATIN LETTER SMALL CAPITAL REVERSED R;Ll;0;L;;;;;N;;;;;
+1D1A;LATIN LETTER SMALL CAPITAL TURNED R;Ll;0;L;;;;;N;;;;;
+1D1B;LATIN LETTER SMALL CAPITAL T;Ll;0;L;;;;;N;;;;;
+1D1C;LATIN LETTER SMALL CAPITAL U;Ll;0;L;;;;;N;;;;;
+1D1D;LATIN SMALL LETTER SIDEWAYS U;Ll;0;L;;;;;N;;;;;
+1D1E;LATIN SMALL LETTER SIDEWAYS DIAERESIZED U;Ll;0;L;;;;;N;;;;;
+1D1F;LATIN SMALL LETTER SIDEWAYS TURNED M;Ll;0;L;;;;;N;;;;;
+1D20;LATIN LETTER SMALL CAPITAL V;Ll;0;L;;;;;N;;;;;
+1D21;LATIN LETTER SMALL CAPITAL W;Ll;0;L;;;;;N;;;;;
+1D22;LATIN LETTER SMALL CAPITAL Z;Ll;0;L;;;;;N;;;;;
+1D23;LATIN LETTER SMALL CAPITAL EZH;Ll;0;L;;;;;N;;;;;
+1D24;LATIN LETTER VOICED LARYNGEAL SPIRANT;Ll;0;L;;;;;N;;;;;
+1D25;LATIN LETTER AIN;Ll;0;L;;;;;N;;;;;
+1D26;GREEK LETTER SMALL CAPITAL GAMMA;Ll;0;L;;;;;N;;;;;
+1D27;GREEK LETTER SMALL CAPITAL LAMDA;Ll;0;L;;;;;N;;;;;
+1D28;GREEK LETTER SMALL CAPITAL PI;Ll;0;L;;;;;N;;;;;
+1D29;GREEK LETTER SMALL CAPITAL RHO;Ll;0;L;;;;;N;;;;;
+1D2A;GREEK LETTER SMALL CAPITAL PSI;Ll;0;L;;;;;N;;;;;
+1D2B;CYRILLIC LETTER SMALL CAPITAL EL;Ll;0;L;;;;;N;;;;;
+1D2C;MODIFIER LETTER CAPITAL A;Lm;0;L;<super> 0041;;;;N;;;;;
+1D2D;MODIFIER LETTER CAPITAL AE;Lm;0;L;<super> 00C6;;;;N;;;;;
+1D2E;MODIFIER LETTER CAPITAL B;Lm;0;L;<super> 0042;;;;N;;;;;
+1D2F;MODIFIER LETTER CAPITAL BARRED B;Lm;0;L;;;;;N;;;;;
+1D30;MODIFIER LETTER CAPITAL D;Lm;0;L;<super> 0044;;;;N;;;;;
+1D31;MODIFIER LETTER CAPITAL E;Lm;0;L;<super> 0045;;;;N;;;;;
+1D32;MODIFIER LETTER CAPITAL REVERSED E;Lm;0;L;<super> 018E;;;;N;;;;;
+1D33;MODIFIER LETTER CAPITAL G;Lm;0;L;<super> 0047;;;;N;;;;;
+1D34;MODIFIER LETTER CAPITAL H;Lm;0;L;<super> 0048;;;;N;;;;;
+1D35;MODIFIER LETTER CAPITAL I;Lm;0;L;<super> 0049;;;;N;;;;;
+1D36;MODIFIER LETTER CAPITAL J;Lm;0;L;<super> 004A;;;;N;;;;;
+1D37;MODIFIER LETTER CAPITAL K;Lm;0;L;<super> 004B;;;;N;;;;;
+1D38;MODIFIER LETTER CAPITAL L;Lm;0;L;<super> 004C;;;;N;;;;;
+1D39;MODIFIER LETTER CAPITAL M;Lm;0;L;<super> 004D;;;;N;;;;;
+1D3A;MODIFIER LETTER CAPITAL N;Lm;0;L;<super> 004E;;;;N;;;;;
+1D3B;MODIFIER LETTER CAPITAL REVERSED N;Lm;0;L;;;;;N;;;;;
+1D3C;MODIFIER LETTER CAPITAL O;Lm;0;L;<super> 004F;;;;N;;;;;
+1D3D;MODIFIER LETTER CAPITAL OU;Lm;0;L;<super> 0222;;;;N;;;;;
+1D3E;MODIFIER LETTER CAPITAL P;Lm;0;L;<super> 0050;;;;N;;;;;
+1D3F;MODIFIER LETTER CAPITAL R;Lm;0;L;<super> 0052;;;;N;;;;;
+1D40;MODIFIER LETTER CAPITAL T;Lm;0;L;<super> 0054;;;;N;;;;;
+1D41;MODIFIER LETTER CAPITAL U;Lm;0;L;<super> 0055;;;;N;;;;;
+1D42;MODIFIER LETTER CAPITAL W;Lm;0;L;<super> 0057;;;;N;;;;;
+1D43;MODIFIER LETTER SMALL A;Lm;0;L;<super> 0061;;;;N;;;;;
+1D44;MODIFIER LETTER SMALL TURNED A;Lm;0;L;<super> 0250;;;;N;;;;;
+1D45;MODIFIER LETTER SMALL ALPHA;Lm;0;L;<super> 0251;;;;N;;;;;
+1D46;MODIFIER LETTER SMALL TURNED AE;Lm;0;L;<super> 1D02;;;;N;;;;;
+1D47;MODIFIER LETTER SMALL B;Lm;0;L;<super> 0062;;;;N;;;;;
+1D48;MODIFIER LETTER SMALL D;Lm;0;L;<super> 0064;;;;N;;;;;
+1D49;MODIFIER LETTER SMALL E;Lm;0;L;<super> 0065;;;;N;;;;;
+1D4A;MODIFIER LETTER SMALL SCHWA;Lm;0;L;<super> 0259;;;;N;;;;;
+1D4B;MODIFIER LETTER SMALL OPEN E;Lm;0;L;<super> 025B;;;;N;;;;;
+1D4C;MODIFIER LETTER SMALL TURNED OPEN E;Lm;0;L;<super> 025C;;;;N;;;;;
+1D4D;MODIFIER LETTER SMALL G;Lm;0;L;<super> 0067;;;;N;;;;;
+1D4E;MODIFIER LETTER SMALL TURNED I;Lm;0;L;;;;;N;;;;;
+1D4F;MODIFIER LETTER SMALL K;Lm;0;L;<super> 006B;;;;N;;;;;
+1D50;MODIFIER LETTER SMALL M;Lm;0;L;<super> 006D;;;;N;;;;;
+1D51;MODIFIER LETTER SMALL ENG;Lm;0;L;<super> 014B;;;;N;;;;;
+1D52;MODIFIER LETTER SMALL O;Lm;0;L;<super> 006F;;;;N;;;;;
+1D53;MODIFIER LETTER SMALL OPEN O;Lm;0;L;<super> 0254;;;;N;;;;;
+1D54;MODIFIER LETTER SMALL TOP HALF O;Lm;0;L;<super> 1D16;;;;N;;;;;
+1D55;MODIFIER LETTER SMALL BOTTOM HALF O;Lm;0;L;<super> 1D17;;;;N;;;;;
+1D56;MODIFIER LETTER SMALL P;Lm;0;L;<super> 0070;;;;N;;;;;
+1D57;MODIFIER LETTER SMALL T;Lm;0;L;<super> 0074;;;;N;;;;;
+1D58;MODIFIER LETTER SMALL U;Lm;0;L;<super> 0075;;;;N;;;;;
+1D59;MODIFIER LETTER SMALL SIDEWAYS U;Lm;0;L;<super> 1D1D;;;;N;;;;;
+1D5A;MODIFIER LETTER SMALL TURNED M;Lm;0;L;<super> 026F;;;;N;;;;;
+1D5B;MODIFIER LETTER SMALL V;Lm;0;L;<super> 0076;;;;N;;;;;
+1D5C;MODIFIER LETTER SMALL AIN;Lm;0;L;<super> 1D25;;;;N;;;;;
+1D5D;MODIFIER LETTER SMALL BETA;Lm;0;L;<super> 03B2;;;;N;;;;;
+1D5E;MODIFIER LETTER SMALL GREEK GAMMA;Lm;0;L;<super> 03B3;;;;N;;;;;
+1D5F;MODIFIER LETTER SMALL DELTA;Lm;0;L;<super> 03B4;;;;N;;;;;
+1D60;MODIFIER LETTER SMALL GREEK PHI;Lm;0;L;<super> 03C6;;;;N;;;;;
+1D61;MODIFIER LETTER SMALL CHI;Lm;0;L;<super> 03C7;;;;N;;;;;
+1D62;LATIN SUBSCRIPT SMALL LETTER I;Ll;0;L;<sub> 0069;;;;N;;;;;
+1D63;LATIN SUBSCRIPT SMALL LETTER R;Ll;0;L;<sub> 0072;;;;N;;;;;
+1D64;LATIN SUBSCRIPT SMALL LETTER U;Ll;0;L;<sub> 0075;;;;N;;;;;
+1D65;LATIN SUBSCRIPT SMALL LETTER V;Ll;0;L;<sub> 0076;;;;N;;;;;
+1D66;GREEK SUBSCRIPT SMALL LETTER BETA;Ll;0;L;<sub> 03B2;;;;N;;;;;
+1D67;GREEK SUBSCRIPT SMALL LETTER GAMMA;Ll;0;L;<sub> 03B3;;;;N;;;;;
+1D68;GREEK SUBSCRIPT SMALL LETTER RHO;Ll;0;L;<sub> 03C1;;;;N;;;;;
+1D69;GREEK SUBSCRIPT SMALL LETTER PHI;Ll;0;L;<sub> 03C6;;;;N;;;;;
+1D6A;GREEK SUBSCRIPT SMALL LETTER CHI;Ll;0;L;<sub> 03C7;;;;N;;;;;
+1D6B;LATIN SMALL LETTER UE;Ll;0;L;;;;;N;;;;;
+1E00;LATIN CAPITAL LETTER A WITH RING BELOW;Lu;0;L;0041 0325;;;;N;;;;1E01;
+1E01;LATIN SMALL LETTER A WITH RING BELOW;Ll;0;L;0061 0325;;;;N;;;1E00;;1E00
+1E02;LATIN CAPITAL LETTER B WITH DOT ABOVE;Lu;0;L;0042 0307;;;;N;;;;1E03;
+1E03;LATIN SMALL LETTER B WITH DOT ABOVE;Ll;0;L;0062 0307;;;;N;;;1E02;;1E02
+1E04;LATIN CAPITAL LETTER B WITH DOT BELOW;Lu;0;L;0042 0323;;;;N;;;;1E05;
+1E05;LATIN SMALL LETTER B WITH DOT BELOW;Ll;0;L;0062 0323;;;;N;;;1E04;;1E04
+1E06;LATIN CAPITAL LETTER B WITH LINE BELOW;Lu;0;L;0042 0331;;;;N;;;;1E07;
+1E07;LATIN SMALL LETTER B WITH LINE BELOW;Ll;0;L;0062 0331;;;;N;;;1E06;;1E06
+1E08;LATIN CAPITAL LETTER C WITH CEDILLA AND ACUTE;Lu;0;L;00C7 0301;;;;N;;;;1E09;
+1E09;LATIN SMALL LETTER C WITH CEDILLA AND ACUTE;Ll;0;L;00E7 0301;;;;N;;;1E08;;1E08
+1E0A;LATIN CAPITAL LETTER D WITH DOT ABOVE;Lu;0;L;0044 0307;;;;N;;;;1E0B;
+1E0B;LATIN SMALL LETTER D WITH DOT ABOVE;Ll;0;L;0064 0307;;;;N;;;1E0A;;1E0A
+1E0C;LATIN CAPITAL LETTER D WITH DOT BELOW;Lu;0;L;0044 0323;;;;N;;;;1E0D;
+1E0D;LATIN SMALL LETTER D WITH DOT BELOW;Ll;0;L;0064 0323;;;;N;;;1E0C;;1E0C
+1E0E;LATIN CAPITAL LETTER D WITH LINE BELOW;Lu;0;L;0044 0331;;;;N;;;;1E0F;
+1E0F;LATIN SMALL LETTER D WITH LINE BELOW;Ll;0;L;0064 0331;;;;N;;;1E0E;;1E0E
+1E10;LATIN CAPITAL LETTER D WITH CEDILLA;Lu;0;L;0044 0327;;;;N;;;;1E11;
+1E11;LATIN SMALL LETTER D WITH CEDILLA;Ll;0;L;0064 0327;;;;N;;;1E10;;1E10
+1E12;LATIN CAPITAL LETTER D WITH CIRCUMFLEX BELOW;Lu;0;L;0044 032D;;;;N;;;;1E13;
+1E13;LATIN SMALL LETTER D WITH CIRCUMFLEX BELOW;Ll;0;L;0064 032D;;;;N;;;1E12;;1E12
+1E14;LATIN CAPITAL LETTER E WITH MACRON AND GRAVE;Lu;0;L;0112 0300;;;;N;;;;1E15;
+1E15;LATIN SMALL LETTER E WITH MACRON AND GRAVE;Ll;0;L;0113 0300;;;;N;;;1E14;;1E14
+1E16;LATIN CAPITAL LETTER E WITH MACRON AND ACUTE;Lu;0;L;0112 0301;;;;N;;;;1E17;
+1E17;LATIN SMALL LETTER E WITH MACRON AND ACUTE;Ll;0;L;0113 0301;;;;N;;;1E16;;1E16
+1E18;LATIN CAPITAL LETTER E WITH CIRCUMFLEX BELOW;Lu;0;L;0045 032D;;;;N;;;;1E19;
+1E19;LATIN SMALL LETTER E WITH CIRCUMFLEX BELOW;Ll;0;L;0065 032D;;;;N;;;1E18;;1E18
+1E1A;LATIN CAPITAL LETTER E WITH TILDE BELOW;Lu;0;L;0045 0330;;;;N;;;;1E1B;
+1E1B;LATIN SMALL LETTER E WITH TILDE BELOW;Ll;0;L;0065 0330;;;;N;;;1E1A;;1E1A
+1E1C;LATIN CAPITAL LETTER E WITH CEDILLA AND BREVE;Lu;0;L;0228 0306;;;;N;;;;1E1D;
+1E1D;LATIN SMALL LETTER E WITH CEDILLA AND BREVE;Ll;0;L;0229 0306;;;;N;;;1E1C;;1E1C
+1E1E;LATIN CAPITAL LETTER F WITH DOT ABOVE;Lu;0;L;0046 0307;;;;N;;;;1E1F;
+1E1F;LATIN SMALL LETTER F WITH DOT ABOVE;Ll;0;L;0066 0307;;;;N;;;1E1E;;1E1E
+1E20;LATIN CAPITAL LETTER G WITH MACRON;Lu;0;L;0047 0304;;;;N;;;;1E21;
+1E21;LATIN SMALL LETTER G WITH MACRON;Ll;0;L;0067 0304;;;;N;;;1E20;;1E20
+1E22;LATIN CAPITAL LETTER H WITH DOT ABOVE;Lu;0;L;0048 0307;;;;N;;;;1E23;
+1E23;LATIN SMALL LETTER H WITH DOT ABOVE;Ll;0;L;0068 0307;;;;N;;;1E22;;1E22
+1E24;LATIN CAPITAL LETTER H WITH DOT BELOW;Lu;0;L;0048 0323;;;;N;;;;1E25;
+1E25;LATIN SMALL LETTER H WITH DOT BELOW;Ll;0;L;0068 0323;;;;N;;;1E24;;1E24
+1E26;LATIN CAPITAL LETTER H WITH DIAERESIS;Lu;0;L;0048 0308;;;;N;;;;1E27;
+1E27;LATIN SMALL LETTER H WITH DIAERESIS;Ll;0;L;0068 0308;;;;N;;;1E26;;1E26
+1E28;LATIN CAPITAL LETTER H WITH CEDILLA;Lu;0;L;0048 0327;;;;N;;;;1E29;
+1E29;LATIN SMALL LETTER H WITH CEDILLA;Ll;0;L;0068 0327;;;;N;;;1E28;;1E28
+1E2A;LATIN CAPITAL LETTER H WITH BREVE BELOW;Lu;0;L;0048 032E;;;;N;;;;1E2B;
+1E2B;LATIN SMALL LETTER H WITH BREVE BELOW;Ll;0;L;0068 032E;;;;N;;;1E2A;;1E2A
+1E2C;LATIN CAPITAL LETTER I WITH TILDE BELOW;Lu;0;L;0049 0330;;;;N;;;;1E2D;
+1E2D;LATIN SMALL LETTER I WITH TILDE BELOW;Ll;0;L;0069 0330;;;;N;;;1E2C;;1E2C
+1E2E;LATIN CAPITAL LETTER I WITH DIAERESIS AND ACUTE;Lu;0;L;00CF 0301;;;;N;;;;1E2F;
+1E2F;LATIN SMALL LETTER I WITH DIAERESIS AND ACUTE;Ll;0;L;00EF 0301;;;;N;;;1E2E;;1E2E
+1E30;LATIN CAPITAL LETTER K WITH ACUTE;Lu;0;L;004B 0301;;;;N;;;;1E31;
+1E31;LATIN SMALL LETTER K WITH ACUTE;Ll;0;L;006B 0301;;;;N;;;1E30;;1E30
+1E32;LATIN CAPITAL LETTER K WITH DOT BELOW;Lu;0;L;004B 0323;;;;N;;;;1E33;
+1E33;LATIN SMALL LETTER K WITH DOT BELOW;Ll;0;L;006B 0323;;;;N;;;1E32;;1E32
+1E34;LATIN CAPITAL LETTER K WITH LINE BELOW;Lu;0;L;004B 0331;;;;N;;;;1E35;
+1E35;LATIN SMALL LETTER K WITH LINE BELOW;Ll;0;L;006B 0331;;;;N;;;1E34;;1E34
+1E36;LATIN CAPITAL LETTER L WITH DOT BELOW;Lu;0;L;004C 0323;;;;N;;;;1E37;
+1E37;LATIN SMALL LETTER L WITH DOT BELOW;Ll;0;L;006C 0323;;;;N;;;1E36;;1E36
+1E38;LATIN CAPITAL LETTER L WITH DOT BELOW AND MACRON;Lu;0;L;1E36 0304;;;;N;;;;1E39;
+1E39;LATIN SMALL LETTER L WITH DOT BELOW AND MACRON;Ll;0;L;1E37 0304;;;;N;;;1E38;;1E38
+1E3A;LATIN CAPITAL LETTER L WITH LINE BELOW;Lu;0;L;004C 0331;;;;N;;;;1E3B;
+1E3B;LATIN SMALL LETTER L WITH LINE BELOW;Ll;0;L;006C 0331;;;;N;;;1E3A;;1E3A
+1E3C;LATIN CAPITAL LETTER L WITH CIRCUMFLEX BELOW;Lu;0;L;004C 032D;;;;N;;;;1E3D;
+1E3D;LATIN SMALL LETTER L WITH CIRCUMFLEX BELOW;Ll;0;L;006C 032D;;;;N;;;1E3C;;1E3C
+1E3E;LATIN CAPITAL LETTER M WITH ACUTE;Lu;0;L;004D 0301;;;;N;;;;1E3F;
+1E3F;LATIN SMALL LETTER M WITH ACUTE;Ll;0;L;006D 0301;;;;N;;;1E3E;;1E3E
+1E40;LATIN CAPITAL LETTER M WITH DOT ABOVE;Lu;0;L;004D 0307;;;;N;;;;1E41;
+1E41;LATIN SMALL LETTER M WITH DOT ABOVE;Ll;0;L;006D 0307;;;;N;;;1E40;;1E40
+1E42;LATIN CAPITAL LETTER M WITH DOT BELOW;Lu;0;L;004D 0323;;;;N;;;;1E43;
+1E43;LATIN SMALL LETTER M WITH DOT BELOW;Ll;0;L;006D 0323;;;;N;;;1E42;;1E42
+1E44;LATIN CAPITAL LETTER N WITH DOT ABOVE;Lu;0;L;004E 0307;;;;N;;;;1E45;
+1E45;LATIN SMALL LETTER N WITH DOT ABOVE;Ll;0;L;006E 0307;;;;N;;;1E44;;1E44
+1E46;LATIN CAPITAL LETTER N WITH DOT BELOW;Lu;0;L;004E 0323;;;;N;;;;1E47;
+1E47;LATIN SMALL LETTER N WITH DOT BELOW;Ll;0;L;006E 0323;;;;N;;;1E46;;1E46
+1E48;LATIN CAPITAL LETTER N WITH LINE BELOW;Lu;0;L;004E 0331;;;;N;;;;1E49;
+1E49;LATIN SMALL LETTER N WITH LINE BELOW;Ll;0;L;006E 0331;;;;N;;;1E48;;1E48
+1E4A;LATIN CAPITAL LETTER N WITH CIRCUMFLEX BELOW;Lu;0;L;004E 032D;;;;N;;;;1E4B;
+1E4B;LATIN SMALL LETTER N WITH CIRCUMFLEX BELOW;Ll;0;L;006E 032D;;;;N;;;1E4A;;1E4A
+1E4C;LATIN CAPITAL LETTER O WITH TILDE AND ACUTE;Lu;0;L;00D5 0301;;;;N;;;;1E4D;
+1E4D;LATIN SMALL LETTER O WITH TILDE AND ACUTE;Ll;0;L;00F5 0301;;;;N;;;1E4C;;1E4C
+1E4E;LATIN CAPITAL LETTER O WITH TILDE AND DIAERESIS;Lu;0;L;00D5 0308;;;;N;;;;1E4F;
+1E4F;LATIN SMALL LETTER O WITH TILDE AND DIAERESIS;Ll;0;L;00F5 0308;;;;N;;;1E4E;;1E4E
+1E50;LATIN CAPITAL LETTER O WITH MACRON AND GRAVE;Lu;0;L;014C 0300;;;;N;;;;1E51;
+1E51;LATIN SMALL LETTER O WITH MACRON AND GRAVE;Ll;0;L;014D 0300;;;;N;;;1E50;;1E50
+1E52;LATIN CAPITAL LETTER O WITH MACRON AND ACUTE;Lu;0;L;014C 0301;;;;N;;;;1E53;
+1E53;LATIN SMALL LETTER O WITH MACRON AND ACUTE;Ll;0;L;014D 0301;;;;N;;;1E52;;1E52
+1E54;LATIN CAPITAL LETTER P WITH ACUTE;Lu;0;L;0050 0301;;;;N;;;;1E55;
+1E55;LATIN SMALL LETTER P WITH ACUTE;Ll;0;L;0070 0301;;;;N;;;1E54;;1E54
+1E56;LATIN CAPITAL LETTER P WITH DOT ABOVE;Lu;0;L;0050 0307;;;;N;;;;1E57;
+1E57;LATIN SMALL LETTER P WITH DOT ABOVE;Ll;0;L;0070 0307;;;;N;;;1E56;;1E56
+1E58;LATIN CAPITAL LETTER R WITH DOT ABOVE;Lu;0;L;0052 0307;;;;N;;;;1E59;
+1E59;LATIN SMALL LETTER R WITH DOT ABOVE;Ll;0;L;0072 0307;;;;N;;;1E58;;1E58
+1E5A;LATIN CAPITAL LETTER R WITH DOT BELOW;Lu;0;L;0052 0323;;;;N;;;;1E5B;
+1E5B;LATIN SMALL LETTER R WITH DOT BELOW;Ll;0;L;0072 0323;;;;N;;;1E5A;;1E5A
+1E5C;LATIN CAPITAL LETTER R WITH DOT BELOW AND MACRON;Lu;0;L;1E5A 0304;;;;N;;;;1E5D;
+1E5D;LATIN SMALL LETTER R WITH DOT BELOW AND MACRON;Ll;0;L;1E5B 0304;;;;N;;;1E5C;;1E5C
+1E5E;LATIN CAPITAL LETTER R WITH LINE BELOW;Lu;0;L;0052 0331;;;;N;;;;1E5F;
+1E5F;LATIN SMALL LETTER R WITH LINE BELOW;Ll;0;L;0072 0331;;;;N;;;1E5E;;1E5E
+1E60;LATIN CAPITAL LETTER S WITH DOT ABOVE;Lu;0;L;0053 0307;;;;N;;;;1E61;
+1E61;LATIN SMALL LETTER S WITH DOT ABOVE;Ll;0;L;0073 0307;;;;N;;;1E60;;1E60
+1E62;LATIN CAPITAL LETTER S WITH DOT BELOW;Lu;0;L;0053 0323;;;;N;;;;1E63;
+1E63;LATIN SMALL LETTER S WITH DOT BELOW;Ll;0;L;0073 0323;;;;N;;;1E62;;1E62
+1E64;LATIN CAPITAL LETTER S WITH ACUTE AND DOT ABOVE;Lu;0;L;015A 0307;;;;N;;;;1E65;
+1E65;LATIN SMALL LETTER S WITH ACUTE AND DOT ABOVE;Ll;0;L;015B 0307;;;;N;;;1E64;;1E64
+1E66;LATIN CAPITAL LETTER S WITH CARON AND DOT ABOVE;Lu;0;L;0160 0307;;;;N;;;;1E67;
+1E67;LATIN SMALL LETTER S WITH CARON AND DOT ABOVE;Ll;0;L;0161 0307;;;;N;;;1E66;;1E66
+1E68;LATIN CAPITAL LETTER S WITH DOT BELOW AND DOT ABOVE;Lu;0;L;1E62 0307;;;;N;;;;1E69;
+1E69;LATIN SMALL LETTER S WITH DOT BELOW AND DOT ABOVE;Ll;0;L;1E63 0307;;;;N;;;1E68;;1E68
+1E6A;LATIN CAPITAL LETTER T WITH DOT ABOVE;Lu;0;L;0054 0307;;;;N;;;;1E6B;
+1E6B;LATIN SMALL LETTER T WITH DOT ABOVE;Ll;0;L;0074 0307;;;;N;;;1E6A;;1E6A
+1E6C;LATIN CAPITAL LETTER T WITH DOT BELOW;Lu;0;L;0054 0323;;;;N;;;;1E6D;
+1E6D;LATIN SMALL LETTER T WITH DOT BELOW;Ll;0;L;0074 0323;;;;N;;;1E6C;;1E6C
+1E6E;LATIN CAPITAL LETTER T WITH LINE BELOW;Lu;0;L;0054 0331;;;;N;;;;1E6F;
+1E6F;LATIN SMALL LETTER T WITH LINE BELOW;Ll;0;L;0074 0331;;;;N;;;1E6E;;1E6E
+1E70;LATIN CAPITAL LETTER T WITH CIRCUMFLEX BELOW;Lu;0;L;0054 032D;;;;N;;;;1E71;
+1E71;LATIN SMALL LETTER T WITH CIRCUMFLEX BELOW;Ll;0;L;0074 032D;;;;N;;;1E70;;1E70
+1E72;LATIN CAPITAL LETTER U WITH DIAERESIS BELOW;Lu;0;L;0055 0324;;;;N;;;;1E73;
+1E73;LATIN SMALL LETTER U WITH DIAERESIS BELOW;Ll;0;L;0075 0324;;;;N;;;1E72;;1E72
+1E74;LATIN CAPITAL LETTER U WITH TILDE BELOW;Lu;0;L;0055 0330;;;;N;;;;1E75;
+1E75;LATIN SMALL LETTER U WITH TILDE BELOW;Ll;0;L;0075 0330;;;;N;;;1E74;;1E74
+1E76;LATIN CAPITAL LETTER U WITH CIRCUMFLEX BELOW;Lu;0;L;0055 032D;;;;N;;;;1E77;
+1E77;LATIN SMALL LETTER U WITH CIRCUMFLEX BELOW;Ll;0;L;0075 032D;;;;N;;;1E76;;1E76
+1E78;LATIN CAPITAL LETTER U WITH TILDE AND ACUTE;Lu;0;L;0168 0301;;;;N;;;;1E79;
+1E79;LATIN SMALL LETTER U WITH TILDE AND ACUTE;Ll;0;L;0169 0301;;;;N;;;1E78;;1E78
+1E7A;LATIN CAPITAL LETTER U WITH MACRON AND DIAERESIS;Lu;0;L;016A 0308;;;;N;;;;1E7B;
+1E7B;LATIN SMALL LETTER U WITH MACRON AND DIAERESIS;Ll;0;L;016B 0308;;;;N;;;1E7A;;1E7A
+1E7C;LATIN CAPITAL LETTER V WITH TILDE;Lu;0;L;0056 0303;;;;N;;;;1E7D;
+1E7D;LATIN SMALL LETTER V WITH TILDE;Ll;0;L;0076 0303;;;;N;;;1E7C;;1E7C
+1E7E;LATIN CAPITAL LETTER V WITH DOT BELOW;Lu;0;L;0056 0323;;;;N;;;;1E7F;
+1E7F;LATIN SMALL LETTER V WITH DOT BELOW;Ll;0;L;0076 0323;;;;N;;;1E7E;;1E7E
+1E80;LATIN CAPITAL LETTER W WITH GRAVE;Lu;0;L;0057 0300;;;;N;;;;1E81;
+1E81;LATIN SMALL LETTER W WITH GRAVE;Ll;0;L;0077 0300;;;;N;;;1E80;;1E80
+1E82;LATIN CAPITAL LETTER W WITH ACUTE;Lu;0;L;0057 0301;;;;N;;;;1E83;
+1E83;LATIN SMALL LETTER W WITH ACUTE;Ll;0;L;0077 0301;;;;N;;;1E82;;1E82
+1E84;LATIN CAPITAL LETTER W WITH DIAERESIS;Lu;0;L;0057 0308;;;;N;;;;1E85;
+1E85;LATIN SMALL LETTER W WITH DIAERESIS;Ll;0;L;0077 0308;;;;N;;;1E84;;1E84
+1E86;LATIN CAPITAL LETTER W WITH DOT ABOVE;Lu;0;L;0057 0307;;;;N;;;;1E87;
+1E87;LATIN SMALL LETTER W WITH DOT ABOVE;Ll;0;L;0077 0307;;;;N;;;1E86;;1E86
+1E88;LATIN CAPITAL LETTER W WITH DOT BELOW;Lu;0;L;0057 0323;;;;N;;;;1E89;
+1E89;LATIN SMALL LETTER W WITH DOT BELOW;Ll;0;L;0077 0323;;;;N;;;1E88;;1E88
+1E8A;LATIN CAPITAL LETTER X WITH DOT ABOVE;Lu;0;L;0058 0307;;;;N;;;;1E8B;
+1E8B;LATIN SMALL LETTER X WITH DOT ABOVE;Ll;0;L;0078 0307;;;;N;;;1E8A;;1E8A
+1E8C;LATIN CAPITAL LETTER X WITH DIAERESIS;Lu;0;L;0058 0308;;;;N;;;;1E8D;
+1E8D;LATIN SMALL LETTER X WITH DIAERESIS;Ll;0;L;0078 0308;;;;N;;;1E8C;;1E8C
+1E8E;LATIN CAPITAL LETTER Y WITH DOT ABOVE;Lu;0;L;0059 0307;;;;N;;;;1E8F;
+1E8F;LATIN SMALL LETTER Y WITH DOT ABOVE;Ll;0;L;0079 0307;;;;N;;;1E8E;;1E8E
+1E90;LATIN CAPITAL LETTER Z WITH CIRCUMFLEX;Lu;0;L;005A 0302;;;;N;;;;1E91;
+1E91;LATIN SMALL LETTER Z WITH CIRCUMFLEX;Ll;0;L;007A 0302;;;;N;;;1E90;;1E90
+1E92;LATIN CAPITAL LETTER Z WITH DOT BELOW;Lu;0;L;005A 0323;;;;N;;;;1E93;
+1E93;LATIN SMALL LETTER Z WITH DOT BELOW;Ll;0;L;007A 0323;;;;N;;;1E92;;1E92
+1E94;LATIN CAPITAL LETTER Z WITH LINE BELOW;Lu;0;L;005A 0331;;;;N;;;;1E95;
+1E95;LATIN SMALL LETTER Z WITH LINE BELOW;Ll;0;L;007A 0331;;;;N;;;1E94;;1E94
+1E96;LATIN SMALL LETTER H WITH LINE BELOW;Ll;0;L;0068 0331;;;;N;;;;;
+1E97;LATIN SMALL LETTER T WITH DIAERESIS;Ll;0;L;0074 0308;;;;N;;;;;
+1E98;LATIN SMALL LETTER W WITH RING ABOVE;Ll;0;L;0077 030A;;;;N;;;;;
+1E99;LATIN SMALL LETTER Y WITH RING ABOVE;Ll;0;L;0079 030A;;;;N;;;;;
+1E9A;LATIN SMALL LETTER A WITH RIGHT HALF RING;Ll;0;L;<compat> 0061 02BE;;;;N;;;;;
+1E9B;LATIN SMALL LETTER LONG S WITH DOT ABOVE;Ll;0;L;017F 0307;;;;N;;;1E60;;1E60
+1EA0;LATIN CAPITAL LETTER A WITH DOT BELOW;Lu;0;L;0041 0323;;;;N;;;;1EA1;
+1EA1;LATIN SMALL LETTER A WITH DOT BELOW;Ll;0;L;0061 0323;;;;N;;;1EA0;;1EA0
+1EA2;LATIN CAPITAL LETTER A WITH HOOK ABOVE;Lu;0;L;0041 0309;;;;N;;;;1EA3;
+1EA3;LATIN SMALL LETTER A WITH HOOK ABOVE;Ll;0;L;0061 0309;;;;N;;;1EA2;;1EA2
+1EA4;LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND ACUTE;Lu;0;L;00C2 0301;;;;N;;;;1EA5;
+1EA5;LATIN SMALL LETTER A WITH CIRCUMFLEX AND ACUTE;Ll;0;L;00E2 0301;;;;N;;;1EA4;;1EA4
+1EA6;LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND GRAVE;Lu;0;L;00C2 0300;;;;N;;;;1EA7;
+1EA7;LATIN SMALL LETTER A WITH CIRCUMFLEX AND GRAVE;Ll;0;L;00E2 0300;;;;N;;;1EA6;;1EA6
+1EA8;LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE;Lu;0;L;00C2 0309;;;;N;;;;1EA9;
+1EA9;LATIN SMALL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE;Ll;0;L;00E2 0309;;;;N;;;1EA8;;1EA8
+1EAA;LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND TILDE;Lu;0;L;00C2 0303;;;;N;;;;1EAB;
+1EAB;LATIN SMALL LETTER A WITH CIRCUMFLEX AND TILDE;Ll;0;L;00E2 0303;;;;N;;;1EAA;;1EAA
+1EAC;LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND DOT BELOW;Lu;0;L;1EA0 0302;;;;N;;;;1EAD;
+1EAD;LATIN SMALL LETTER A WITH CIRCUMFLEX AND DOT BELOW;Ll;0;L;1EA1 0302;;;;N;;;1EAC;;1EAC
+1EAE;LATIN CAPITAL LETTER A WITH BREVE AND ACUTE;Lu;0;L;0102 0301;;;;N;;;;1EAF;
+1EAF;LATIN SMALL LETTER A WITH BREVE AND ACUTE;Ll;0;L;0103 0301;;;;N;;;1EAE;;1EAE
+1EB0;LATIN CAPITAL LETTER A WITH BREVE AND GRAVE;Lu;0;L;0102 0300;;;;N;;;;1EB1;
+1EB1;LATIN SMALL LETTER A WITH BREVE AND GRAVE;Ll;0;L;0103 0300;;;;N;;;1EB0;;1EB0
+1EB2;LATIN CAPITAL LETTER A WITH BREVE AND HOOK ABOVE;Lu;0;L;0102 0309;;;;N;;;;1EB3;
+1EB3;LATIN SMALL LETTER A WITH BREVE AND HOOK ABOVE;Ll;0;L;0103 0309;;;;N;;;1EB2;;1EB2
+1EB4;LATIN CAPITAL LETTER A WITH BREVE AND TILDE;Lu;0;L;0102 0303;;;;N;;;;1EB5;
+1EB5;LATIN SMALL LETTER A WITH BREVE AND TILDE;Ll;0;L;0103 0303;;;;N;;;1EB4;;1EB4
+1EB6;LATIN CAPITAL LETTER A WITH BREVE AND DOT BELOW;Lu;0;L;1EA0 0306;;;;N;;;;1EB7;
+1EB7;LATIN SMALL LETTER A WITH BREVE AND DOT BELOW;Ll;0;L;1EA1 0306;;;;N;;;1EB6;;1EB6
+1EB8;LATIN CAPITAL LETTER E WITH DOT BELOW;Lu;0;L;0045 0323;;;;N;;;;1EB9;
+1EB9;LATIN SMALL LETTER E WITH DOT BELOW;Ll;0;L;0065 0323;;;;N;;;1EB8;;1EB8
+1EBA;LATIN CAPITAL LETTER E WITH HOOK ABOVE;Lu;0;L;0045 0309;;;;N;;;;1EBB;
+1EBB;LATIN SMALL LETTER E WITH HOOK ABOVE;Ll;0;L;0065 0309;;;;N;;;1EBA;;1EBA
+1EBC;LATIN CAPITAL LETTER E WITH TILDE;Lu;0;L;0045 0303;;;;N;;;;1EBD;
+1EBD;LATIN SMALL LETTER E WITH TILDE;Ll;0;L;0065 0303;;;;N;;;1EBC;;1EBC
+1EBE;LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND ACUTE;Lu;0;L;00CA 0301;;;;N;;;;1EBF;
+1EBF;LATIN SMALL LETTER E WITH CIRCUMFLEX AND ACUTE;Ll;0;L;00EA 0301;;;;N;;;1EBE;;1EBE
+1EC0;LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND GRAVE;Lu;0;L;00CA 0300;;;;N;;;;1EC1;
+1EC1;LATIN SMALL LETTER E WITH CIRCUMFLEX AND GRAVE;Ll;0;L;00EA 0300;;;;N;;;1EC0;;1EC0
+1EC2;LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE;Lu;0;L;00CA 0309;;;;N;;;;1EC3;
+1EC3;LATIN SMALL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE;Ll;0;L;00EA 0309;;;;N;;;1EC2;;1EC2
+1EC4;LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND TILDE;Lu;0;L;00CA 0303;;;;N;;;;1EC5;
+1EC5;LATIN SMALL LETTER E WITH CIRCUMFLEX AND TILDE;Ll;0;L;00EA 0303;;;;N;;;1EC4;;1EC4
+1EC6;LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND DOT BELOW;Lu;0;L;1EB8 0302;;;;N;;;;1EC7;
+1EC7;LATIN SMALL LETTER E WITH CIRCUMFLEX AND DOT BELOW;Ll;0;L;1EB9 0302;;;;N;;;1EC6;;1EC6
+1EC8;LATIN CAPITAL LETTER I WITH HOOK ABOVE;Lu;0;L;0049 0309;;;;N;;;;1EC9;
+1EC9;LATIN SMALL LETTER I WITH HOOK ABOVE;Ll;0;L;0069 0309;;;;N;;;1EC8;;1EC8
+1ECA;LATIN CAPITAL LETTER I WITH DOT BELOW;Lu;0;L;0049 0323;;;;N;;;;1ECB;
+1ECB;LATIN SMALL LETTER I WITH DOT BELOW;Ll;0;L;0069 0323;;;;N;;;1ECA;;1ECA
+1ECC;LATIN CAPITAL LETTER O WITH DOT BELOW;Lu;0;L;004F 0323;;;;N;;;;1ECD;
+1ECD;LATIN SMALL LETTER O WITH DOT BELOW;Ll;0;L;006F 0323;;;;N;;;1ECC;;1ECC
+1ECE;LATIN CAPITAL LETTER O WITH HOOK ABOVE;Lu;0;L;004F 0309;;;;N;;;;1ECF;
+1ECF;LATIN SMALL LETTER O WITH HOOK ABOVE;Ll;0;L;006F 0309;;;;N;;;1ECE;;1ECE
+1ED0;LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND ACUTE;Lu;0;L;00D4 0301;;;;N;;;;1ED1;
+1ED1;LATIN SMALL LETTER O WITH CIRCUMFLEX AND ACUTE;Ll;0;L;00F4 0301;;;;N;;;1ED0;;1ED0
+1ED2;LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND GRAVE;Lu;0;L;00D4 0300;;;;N;;;;1ED3;
+1ED3;LATIN SMALL LETTER O WITH CIRCUMFLEX AND GRAVE;Ll;0;L;00F4 0300;;;;N;;;1ED2;;1ED2
+1ED4;LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE;Lu;0;L;00D4 0309;;;;N;;;;1ED5;
+1ED5;LATIN SMALL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE;Ll;0;L;00F4 0309;;;;N;;;1ED4;;1ED4
+1ED6;LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND TILDE;Lu;0;L;00D4 0303;;;;N;;;;1ED7;
+1ED7;LATIN SMALL LETTER O WITH CIRCUMFLEX AND TILDE;Ll;0;L;00F4 0303;;;;N;;;1ED6;;1ED6
+1ED8;LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND DOT BELOW;Lu;0;L;1ECC 0302;;;;N;;;;1ED9;
+1ED9;LATIN SMALL LETTER O WITH CIRCUMFLEX AND DOT BELOW;Ll;0;L;1ECD 0302;;;;N;;;1ED8;;1ED8
+1EDA;LATIN CAPITAL LETTER O WITH HORN AND ACUTE;Lu;0;L;01A0 0301;;;;N;;;;1EDB;
+1EDB;LATIN SMALL LETTER O WITH HORN AND ACUTE;Ll;0;L;01A1 0301;;;;N;;;1EDA;;1EDA
+1EDC;LATIN CAPITAL LETTER O WITH HORN AND GRAVE;Lu;0;L;01A0 0300;;;;N;;;;1EDD;
+1EDD;LATIN SMALL LETTER O WITH HORN AND GRAVE;Ll;0;L;01A1 0300;;;;N;;;1EDC;;1EDC
+1EDE;LATIN CAPITAL LETTER O WITH HORN AND HOOK ABOVE;Lu;0;L;01A0 0309;;;;N;;;;1EDF;
+1EDF;LATIN SMALL LETTER O WITH HORN AND HOOK ABOVE;Ll;0;L;01A1 0309;;;;N;;;1EDE;;1EDE
+1EE0;LATIN CAPITAL LETTER O WITH HORN AND TILDE;Lu;0;L;01A0 0303;;;;N;;;;1EE1;
+1EE1;LATIN SMALL LETTER O WITH HORN AND TILDE;Ll;0;L;01A1 0303;;;;N;;;1EE0;;1EE0
+1EE2;LATIN CAPITAL LETTER O WITH HORN AND DOT BELOW;Lu;0;L;01A0 0323;;;;N;;;;1EE3;
+1EE3;LATIN SMALL LETTER O WITH HORN AND DOT BELOW;Ll;0;L;01A1 0323;;;;N;;;1EE2;;1EE2
+1EE4;LATIN CAPITAL LETTER U WITH DOT BELOW;Lu;0;L;0055 0323;;;;N;;;;1EE5;
+1EE5;LATIN SMALL LETTER U WITH DOT BELOW;Ll;0;L;0075 0323;;;;N;;;1EE4;;1EE4
+1EE6;LATIN CAPITAL LETTER U WITH HOOK ABOVE;Lu;0;L;0055 0309;;;;N;;;;1EE7;
+1EE7;LATIN SMALL LETTER U WITH HOOK ABOVE;Ll;0;L;0075 0309;;;;N;;;1EE6;;1EE6
+1EE8;LATIN CAPITAL LETTER U WITH HORN AND ACUTE;Lu;0;L;01AF 0301;;;;N;;;;1EE9;
+1EE9;LATIN SMALL LETTER U WITH HORN AND ACUTE;Ll;0;L;01B0 0301;;;;N;;;1EE8;;1EE8
+1EEA;LATIN CAPITAL LETTER U WITH HORN AND GRAVE;Lu;0;L;01AF 0300;;;;N;;;;1EEB;
+1EEB;LATIN SMALL LETTER U WITH HORN AND GRAVE;Ll;0;L;01B0 0300;;;;N;;;1EEA;;1EEA
+1EEC;LATIN CAPITAL LETTER U WITH HORN AND HOOK ABOVE;Lu;0;L;01AF 0309;;;;N;;;;1EED;
+1EED;LATIN SMALL LETTER U WITH HORN AND HOOK ABOVE;Ll;0;L;01B0 0309;;;;N;;;1EEC;;1EEC
+1EEE;LATIN CAPITAL LETTER U WITH HORN AND TILDE;Lu;0;L;01AF 0303;;;;N;;;;1EEF;
+1EEF;LATIN SMALL LETTER U WITH HORN AND TILDE;Ll;0;L;01B0 0303;;;;N;;;1EEE;;1EEE
+1EF0;LATIN CAPITAL LETTER U WITH HORN AND DOT BELOW;Lu;0;L;01AF 0323;;;;N;;;;1EF1;
+1EF1;LATIN SMALL LETTER U WITH HORN AND DOT BELOW;Ll;0;L;01B0 0323;;;;N;;;1EF0;;1EF0
+1EF2;LATIN CAPITAL LETTER Y WITH GRAVE;Lu;0;L;0059 0300;;;;N;;;;1EF3;
+1EF3;LATIN SMALL LETTER Y WITH GRAVE;Ll;0;L;0079 0300;;;;N;;;1EF2;;1EF2
+1EF4;LATIN CAPITAL LETTER Y WITH DOT BELOW;Lu;0;L;0059 0323;;;;N;;;;1EF5;
+1EF5;LATIN SMALL LETTER Y WITH DOT BELOW;Ll;0;L;0079 0323;;;;N;;;1EF4;;1EF4
+1EF6;LATIN CAPITAL LETTER Y WITH HOOK ABOVE;Lu;0;L;0059 0309;;;;N;;;;1EF7;
+1EF7;LATIN SMALL LETTER Y WITH HOOK ABOVE;Ll;0;L;0079 0309;;;;N;;;1EF6;;1EF6
+1EF8;LATIN CAPITAL LETTER Y WITH TILDE;Lu;0;L;0059 0303;;;;N;;;;1EF9;
+1EF9;LATIN SMALL LETTER Y WITH TILDE;Ll;0;L;0079 0303;;;;N;;;1EF8;;1EF8
+1F00;GREEK SMALL LETTER ALPHA WITH PSILI;Ll;0;L;03B1 0313;;;;N;;;1F08;;1F08
+1F01;GREEK SMALL LETTER ALPHA WITH DASIA;Ll;0;L;03B1 0314;;;;N;;;1F09;;1F09
+1F02;GREEK SMALL LETTER ALPHA WITH PSILI AND VARIA;Ll;0;L;1F00 0300;;;;N;;;1F0A;;1F0A
+1F03;GREEK SMALL LETTER ALPHA WITH DASIA AND VARIA;Ll;0;L;1F01 0300;;;;N;;;1F0B;;1F0B
+1F04;GREEK SMALL LETTER ALPHA WITH PSILI AND OXIA;Ll;0;L;1F00 0301;;;;N;;;1F0C;;1F0C
+1F05;GREEK SMALL LETTER ALPHA WITH DASIA AND OXIA;Ll;0;L;1F01 0301;;;;N;;;1F0D;;1F0D
+1F06;GREEK SMALL LETTER ALPHA WITH PSILI AND PERISPOMENI;Ll;0;L;1F00 0342;;;;N;;;1F0E;;1F0E
+1F07;GREEK SMALL LETTER ALPHA WITH DASIA AND PERISPOMENI;Ll;0;L;1F01 0342;;;;N;;;1F0F;;1F0F
+1F08;GREEK CAPITAL LETTER ALPHA WITH PSILI;Lu;0;L;0391 0313;;;;N;;;;1F00;
+1F09;GREEK CAPITAL LETTER ALPHA WITH DASIA;Lu;0;L;0391 0314;;;;N;;;;1F01;
+1F0A;GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA;Lu;0;L;1F08 0300;;;;N;;;;1F02;
+1F0B;GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA;Lu;0;L;1F09 0300;;;;N;;;;1F03;
+1F0C;GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA;Lu;0;L;1F08 0301;;;;N;;;;1F04;
+1F0D;GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA;Lu;0;L;1F09 0301;;;;N;;;;1F05;
+1F0E;GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI;Lu;0;L;1F08 0342;;;;N;;;;1F06;
+1F0F;GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI;Lu;0;L;1F09 0342;;;;N;;;;1F07;
+1F10;GREEK SMALL LETTER EPSILON WITH PSILI;Ll;0;L;03B5 0313;;;;N;;;1F18;;1F18
+1F11;GREEK SMALL LETTER EPSILON WITH DASIA;Ll;0;L;03B5 0314;;;;N;;;1F19;;1F19
+1F12;GREEK SMALL LETTER EPSILON WITH PSILI AND VARIA;Ll;0;L;1F10 0300;;;;N;;;1F1A;;1F1A
+1F13;GREEK SMALL LETTER EPSILON WITH DASIA AND VARIA;Ll;0;L;1F11 0300;;;;N;;;1F1B;;1F1B
+1F14;GREEK SMALL LETTER EPSILON WITH PSILI AND OXIA;Ll;0;L;1F10 0301;;;;N;;;1F1C;;1F1C
+1F15;GREEK SMALL LETTER EPSILON WITH DASIA AND OXIA;Ll;0;L;1F11 0301;;;;N;;;1F1D;;1F1D
+1F18;GREEK CAPITAL LETTER EPSILON WITH PSILI;Lu;0;L;0395 0313;;;;N;;;;1F10;
+1F19;GREEK CAPITAL LETTER EPSILON WITH DASIA;Lu;0;L;0395 0314;;;;N;;;;1F11;
+1F1A;GREEK CAPITAL LETTER EPSILON WITH PSILI AND VARIA;Lu;0;L;1F18 0300;;;;N;;;;1F12;
+1F1B;GREEK CAPITAL LETTER EPSILON WITH DASIA AND VARIA;Lu;0;L;1F19 0300;;;;N;;;;1F13;
+1F1C;GREEK CAPITAL LETTER EPSILON WITH PSILI AND OXIA;Lu;0;L;1F18 0301;;;;N;;;;1F14;
+1F1D;GREEK CAPITAL LETTER EPSILON WITH DASIA AND OXIA;Lu;0;L;1F19 0301;;;;N;;;;1F15;
+1F20;GREEK SMALL LETTER ETA WITH PSILI;Ll;0;L;03B7 0313;;;;N;;;1F28;;1F28
+1F21;GREEK SMALL LETTER ETA WITH DASIA;Ll;0;L;03B7 0314;;;;N;;;1F29;;1F29
+1F22;GREEK SMALL LETTER ETA WITH PSILI AND VARIA;Ll;0;L;1F20 0300;;;;N;;;1F2A;;1F2A
+1F23;GREEK SMALL LETTER ETA WITH DASIA AND VARIA;Ll;0;L;1F21 0300;;;;N;;;1F2B;;1F2B
+1F24;GREEK SMALL LETTER ETA WITH PSILI AND OXIA;Ll;0;L;1F20 0301;;;;N;;;1F2C;;1F2C
+1F25;GREEK SMALL LETTER ETA WITH DASIA AND OXIA;Ll;0;L;1F21 0301;;;;N;;;1F2D;;1F2D
+1F26;GREEK SMALL LETTER ETA WITH PSILI AND PERISPOMENI;Ll;0;L;1F20 0342;;;;N;;;1F2E;;1F2E
+1F27;GREEK SMALL LETTER ETA WITH DASIA AND PERISPOMENI;Ll;0;L;1F21 0342;;;;N;;;1F2F;;1F2F
+1F28;GREEK CAPITAL LETTER ETA WITH PSILI;Lu;0;L;0397 0313;;;;N;;;;1F20;
+1F29;GREEK CAPITAL LETTER ETA WITH DASIA;Lu;0;L;0397 0314;;;;N;;;;1F21;
+1F2A;GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA;Lu;0;L;1F28 0300;;;;N;;;;1F22;
+1F2B;GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA;Lu;0;L;1F29 0300;;;;N;;;;1F23;
+1F2C;GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA;Lu;0;L;1F28 0301;;;;N;;;;1F24;
+1F2D;GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA;Lu;0;L;1F29 0301;;;;N;;;;1F25;
+1F2E;GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI;Lu;0;L;1F28 0342;;;;N;;;;1F26;
+1F2F;GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI;Lu;0;L;1F29 0342;;;;N;;;;1F27;
+1F30;GREEK SMALL LETTER IOTA WITH PSILI;Ll;0;L;03B9 0313;;;;N;;;1F38;;1F38
+1F31;GREEK SMALL LETTER IOTA WITH DASIA;Ll;0;L;03B9 0314;;;;N;;;1F39;;1F39
+1F32;GREEK SMALL LETTER IOTA WITH PSILI AND VARIA;Ll;0;L;1F30 0300;;;;N;;;1F3A;;1F3A
+1F33;GREEK SMALL LETTER IOTA WITH DASIA AND VARIA;Ll;0;L;1F31 0300;;;;N;;;1F3B;;1F3B
+1F34;GREEK SMALL LETTER IOTA WITH PSILI AND OXIA;Ll;0;L;1F30 0301;;;;N;;;1F3C;;1F3C
+1F35;GREEK SMALL LETTER IOTA WITH DASIA AND OXIA;Ll;0;L;1F31 0301;;;;N;;;1F3D;;1F3D
+1F36;GREEK SMALL LETTER IOTA WITH PSILI AND PERISPOMENI;Ll;0;L;1F30 0342;;;;N;;;1F3E;;1F3E
+1F37;GREEK SMALL LETTER IOTA WITH DASIA AND PERISPOMENI;Ll;0;L;1F31 0342;;;;N;;;1F3F;;1F3F
+1F38;GREEK CAPITAL LETTER IOTA WITH PSILI;Lu;0;L;0399 0313;;;;N;;;;1F30;
+1F39;GREEK CAPITAL LETTER IOTA WITH DASIA;Lu;0;L;0399 0314;;;;N;;;;1F31;
+1F3A;GREEK CAPITAL LETTER IOTA WITH PSILI AND VARIA;Lu;0;L;1F38 0300;;;;N;;;;1F32;
+1F3B;GREEK CAPITAL LETTER IOTA WITH DASIA AND VARIA;Lu;0;L;1F39 0300;;;;N;;;;1F33;
+1F3C;GREEK CAPITAL LETTER IOTA WITH PSILI AND OXIA;Lu;0;L;1F38 0301;;;;N;;;;1F34;
+1F3D;GREEK CAPITAL LETTER IOTA WITH DASIA AND OXIA;Lu;0;L;1F39 0301;;;;N;;;;1F35;
+1F3E;GREEK CAPITAL LETTER IOTA WITH PSILI AND PERISPOMENI;Lu;0;L;1F38 0342;;;;N;;;;1F36;
+1F3F;GREEK CAPITAL LETTER IOTA WITH DASIA AND PERISPOMENI;Lu;0;L;1F39 0342;;;;N;;;;1F37;
+1F40;GREEK SMALL LETTER OMICRON WITH PSILI;Ll;0;L;03BF 0313;;;;N;;;1F48;;1F48
+1F41;GREEK SMALL LETTER OMICRON WITH DASIA;Ll;0;L;03BF 0314;;;;N;;;1F49;;1F49
+1F42;GREEK SMALL LETTER OMICRON WITH PSILI AND VARIA;Ll;0;L;1F40 0300;;;;N;;;1F4A;;1F4A
+1F43;GREEK SMALL LETTER OMICRON WITH DASIA AND VARIA;Ll;0;L;1F41 0300;;;;N;;;1F4B;;1F4B
+1F44;GREEK SMALL LETTER OMICRON WITH PSILI AND OXIA;Ll;0;L;1F40 0301;;;;N;;;1F4C;;1F4C
+1F45;GREEK SMALL LETTER OMICRON WITH DASIA AND OXIA;Ll;0;L;1F41 0301;;;;N;;;1F4D;;1F4D
+1F48;GREEK CAPITAL LETTER OMICRON WITH PSILI;Lu;0;L;039F 0313;;;;N;;;;1F40;
+1F49;GREEK CAPITAL LETTER OMICRON WITH DASIA;Lu;0;L;039F 0314;;;;N;;;;1F41;
+1F4A;GREEK CAPITAL LETTER OMICRON WITH PSILI AND VARIA;Lu;0;L;1F48 0300;;;;N;;;;1F42;
+1F4B;GREEK CAPITAL LETTER OMICRON WITH DASIA AND VARIA;Lu;0;L;1F49 0300;;;;N;;;;1F43;
+1F4C;GREEK CAPITAL LETTER OMICRON WITH PSILI AND OXIA;Lu;0;L;1F48 0301;;;;N;;;;1F44;
+1F4D;GREEK CAPITAL LETTER OMICRON WITH DASIA AND OXIA;Lu;0;L;1F49 0301;;;;N;;;;1F45;
+1F50;GREEK SMALL LETTER UPSILON WITH PSILI;Ll;0;L;03C5 0313;;;;N;;;;;
+1F51;GREEK SMALL LETTER UPSILON WITH DASIA;Ll;0;L;03C5 0314;;;;N;;;1F59;;1F59
+1F52;GREEK SMALL LETTER UPSILON WITH PSILI AND VARIA;Ll;0;L;1F50 0300;;;;N;;;;;
+1F53;GREEK SMALL LETTER UPSILON WITH DASIA AND VARIA;Ll;0;L;1F51 0300;;;;N;;;1F5B;;1F5B
+1F54;GREEK SMALL LETTER UPSILON WITH PSILI AND OXIA;Ll;0;L;1F50 0301;;;;N;;;;;
+1F55;GREEK SMALL LETTER UPSILON WITH DASIA AND OXIA;Ll;0;L;1F51 0301;;;;N;;;1F5D;;1F5D
+1F56;GREEK SMALL LETTER UPSILON WITH PSILI AND PERISPOMENI;Ll;0;L;1F50 0342;;;;N;;;;;
+1F57;GREEK SMALL LETTER UPSILON WITH DASIA AND PERISPOMENI;Ll;0;L;1F51 0342;;;;N;;;1F5F;;1F5F
+1F59;GREEK CAPITAL LETTER UPSILON WITH DASIA;Lu;0;L;03A5 0314;;;;N;;;;1F51;
+1F5B;GREEK CAPITAL LETTER UPSILON WITH DASIA AND VARIA;Lu;0;L;1F59 0300;;;;N;;;;1F53;
+1F5D;GREEK CAPITAL LETTER UPSILON WITH DASIA AND OXIA;Lu;0;L;1F59 0301;;;;N;;;;1F55;
+1F5F;GREEK CAPITAL LETTER UPSILON WITH DASIA AND PERISPOMENI;Lu;0;L;1F59 0342;;;;N;;;;1F57;
+1F60;GREEK SMALL LETTER OMEGA WITH PSILI;Ll;0;L;03C9 0313;;;;N;;;1F68;;1F68
+1F61;GREEK SMALL LETTER OMEGA WITH DASIA;Ll;0;L;03C9 0314;;;;N;;;1F69;;1F69
+1F62;GREEK SMALL LETTER OMEGA WITH PSILI AND VARIA;Ll;0;L;1F60 0300;;;;N;;;1F6A;;1F6A
+1F63;GREEK SMALL LETTER OMEGA WITH DASIA AND VARIA;Ll;0;L;1F61 0300;;;;N;;;1F6B;;1F6B
+1F64;GREEK SMALL LETTER OMEGA WITH PSILI AND OXIA;Ll;0;L;1F60 0301;;;;N;;;1F6C;;1F6C
+1F65;GREEK SMALL LETTER OMEGA WITH DASIA AND OXIA;Ll;0;L;1F61 0301;;;;N;;;1F6D;;1F6D
+1F66;GREEK SMALL LETTER OMEGA WITH PSILI AND PERISPOMENI;Ll;0;L;1F60 0342;;;;N;;;1F6E;;1F6E
+1F67;GREEK SMALL LETTER OMEGA WITH DASIA AND PERISPOMENI;Ll;0;L;1F61 0342;;;;N;;;1F6F;;1F6F
+1F68;GREEK CAPITAL LETTER OMEGA WITH PSILI;Lu;0;L;03A9 0313;;;;N;;;;1F60;
+1F69;GREEK CAPITAL LETTER OMEGA WITH DASIA;Lu;0;L;03A9 0314;;;;N;;;;1F61;
+1F6A;GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA;Lu;0;L;1F68 0300;;;;N;;;;1F62;
+1F6B;GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA;Lu;0;L;1F69 0300;;;;N;;;;1F63;
+1F6C;GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA;Lu;0;L;1F68 0301;;;;N;;;;1F64;
+1F6D;GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA;Lu;0;L;1F69 0301;;;;N;;;;1F65;
+1F6E;GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI;Lu;0;L;1F68 0342;;;;N;;;;1F66;
+1F6F;GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI;Lu;0;L;1F69 0342;;;;N;;;;1F67;
+1F70;GREEK SMALL LETTER ALPHA WITH VARIA;Ll;0;L;03B1 0300;;;;N;;;1FBA;;1FBA
+1F71;GREEK SMALL LETTER ALPHA WITH OXIA;Ll;0;L;03AC;;;;N;;;1FBB;;1FBB
+1F72;GREEK SMALL LETTER EPSILON WITH VARIA;Ll;0;L;03B5 0300;;;;N;;;1FC8;;1FC8
+1F73;GREEK SMALL LETTER EPSILON WITH OXIA;Ll;0;L;03AD;;;;N;;;1FC9;;1FC9
+1F74;GREEK SMALL LETTER ETA WITH VARIA;Ll;0;L;03B7 0300;;;;N;;;1FCA;;1FCA
+1F75;GREEK SMALL LETTER ETA WITH OXIA;Ll;0;L;03AE;;;;N;;;1FCB;;1FCB
+1F76;GREEK SMALL LETTER IOTA WITH VARIA;Ll;0;L;03B9 0300;;;;N;;;1FDA;;1FDA
+1F77;GREEK SMALL LETTER IOTA WITH OXIA;Ll;0;L;03AF;;;;N;;;1FDB;;1FDB
+1F78;GREEK SMALL LETTER OMICRON WITH VARIA;Ll;0;L;03BF 0300;;;;N;;;1FF8;;1FF8
+1F79;GREEK SMALL LETTER OMICRON WITH OXIA;Ll;0;L;03CC;;;;N;;;1FF9;;1FF9
+1F7A;GREEK SMALL LETTER UPSILON WITH VARIA;Ll;0;L;03C5 0300;;;;N;;;1FEA;;1FEA
+1F7B;GREEK SMALL LETTER UPSILON WITH OXIA;Ll;0;L;03CD;;;;N;;;1FEB;;1FEB
+1F7C;GREEK SMALL LETTER OMEGA WITH VARIA;Ll;0;L;03C9 0300;;;;N;;;1FFA;;1FFA
+1F7D;GREEK SMALL LETTER OMEGA WITH OXIA;Ll;0;L;03CE;;;;N;;;1FFB;;1FFB
+1F80;GREEK SMALL LETTER ALPHA WITH PSILI AND YPOGEGRAMMENI;Ll;0;L;1F00 0345;;;;N;;;1F88;;1F88
+1F81;GREEK SMALL LETTER ALPHA WITH DASIA AND YPOGEGRAMMENI;Ll;0;L;1F01 0345;;;;N;;;1F89;;1F89
+1F82;GREEK SMALL LETTER ALPHA WITH PSILI AND VARIA AND YPOGEGRAMMENI;Ll;0;L;1F02 0345;;;;N;;;1F8A;;1F8A
+1F83;GREEK SMALL LETTER ALPHA WITH DASIA AND VARIA AND YPOGEGRAMMENI;Ll;0;L;1F03 0345;;;;N;;;1F8B;;1F8B
+1F84;GREEK SMALL LETTER ALPHA WITH PSILI AND OXIA AND YPOGEGRAMMENI;Ll;0;L;1F04 0345;;;;N;;;1F8C;;1F8C
+1F85;GREEK SMALL LETTER ALPHA WITH DASIA AND OXIA AND YPOGEGRAMMENI;Ll;0;L;1F05 0345;;;;N;;;1F8D;;1F8D
+1F86;GREEK SMALL LETTER ALPHA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1F06 0345;;;;N;;;1F8E;;1F8E
+1F87;GREEK SMALL LETTER ALPHA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1F07 0345;;;;N;;;1F8F;;1F8F
+1F88;GREEK CAPITAL LETTER ALPHA WITH PSILI AND PROSGEGRAMMENI;Lt;0;L;1F08 0345;;;;N;;;;1F80;
+1F89;GREEK CAPITAL LETTER ALPHA WITH DASIA AND PROSGEGRAMMENI;Lt;0;L;1F09 0345;;;;N;;;;1F81;
+1F8A;GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA AND PROSGEGRAMMENI;Lt;0;L;1F0A 0345;;;;N;;;;1F82;
+1F8B;GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA AND PROSGEGRAMMENI;Lt;0;L;1F0B 0345;;;;N;;;;1F83;
+1F8C;GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA AND PROSGEGRAMMENI;Lt;0;L;1F0C 0345;;;;N;;;;1F84;
+1F8D;GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA AND PROSGEGRAMMENI;Lt;0;L;1F0D 0345;;;;N;;;;1F85;
+1F8E;GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI;Lt;0;L;1F0E 0345;;;;N;;;;1F86;
+1F8F;GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI;Lt;0;L;1F0F 0345;;;;N;;;;1F87;
+1F90;GREEK SMALL LETTER ETA WITH PSILI AND YPOGEGRAMMENI;Ll;0;L;1F20 0345;;;;N;;;1F98;;1F98
+1F91;GREEK SMALL LETTER ETA WITH DASIA AND YPOGEGRAMMENI;Ll;0;L;1F21 0345;;;;N;;;1F99;;1F99
+1F92;GREEK SMALL LETTER ETA WITH PSILI AND VARIA AND YPOGEGRAMMENI;Ll;0;L;1F22 0345;;;;N;;;1F9A;;1F9A
+1F93;GREEK SMALL LETTER ETA WITH DASIA AND VARIA AND YPOGEGRAMMENI;Ll;0;L;1F23 0345;;;;N;;;1F9B;;1F9B
+1F94;GREEK SMALL LETTER ETA WITH PSILI AND OXIA AND YPOGEGRAMMENI;Ll;0;L;1F24 0345;;;;N;;;1F9C;;1F9C
+1F95;GREEK SMALL LETTER ETA WITH DASIA AND OXIA AND YPOGEGRAMMENI;Ll;0;L;1F25 0345;;;;N;;;1F9D;;1F9D
+1F96;GREEK SMALL LETTER ETA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1F26 0345;;;;N;;;1F9E;;1F9E
+1F97;GREEK SMALL LETTER ETA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1F27 0345;;;;N;;;1F9F;;1F9F
+1F98;GREEK CAPITAL LETTER ETA WITH PSILI AND PROSGEGRAMMENI;Lt;0;L;1F28 0345;;;;N;;;;1F90;
+1F99;GREEK CAPITAL LETTER ETA WITH DASIA AND PROSGEGRAMMENI;Lt;0;L;1F29 0345;;;;N;;;;1F91;
+1F9A;GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA AND PROSGEGRAMMENI;Lt;0;L;1F2A 0345;;;;N;;;;1F92;
+1F9B;GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA AND PROSGEGRAMMENI;Lt;0;L;1F2B 0345;;;;N;;;;1F93;
+1F9C;GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA AND PROSGEGRAMMENI;Lt;0;L;1F2C 0345;;;;N;;;;1F94;
+1F9D;GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA AND PROSGEGRAMMENI;Lt;0;L;1F2D 0345;;;;N;;;;1F95;
+1F9E;GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI;Lt;0;L;1F2E 0345;;;;N;;;;1F96;
+1F9F;GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI;Lt;0;L;1F2F 0345;;;;N;;;;1F97;
+1FA0;GREEK SMALL LETTER OMEGA WITH PSILI AND YPOGEGRAMMENI;Ll;0;L;1F60 0345;;;;N;;;1FA8;;1FA8
+1FA1;GREEK SMALL LETTER OMEGA WITH DASIA AND YPOGEGRAMMENI;Ll;0;L;1F61 0345;;;;N;;;1FA9;;1FA9
+1FA2;GREEK SMALL LETTER OMEGA WITH PSILI AND VARIA AND YPOGEGRAMMENI;Ll;0;L;1F62 0345;;;;N;;;1FAA;;1FAA
+1FA3;GREEK SMALL LETTER OMEGA WITH DASIA AND VARIA AND YPOGEGRAMMENI;Ll;0;L;1F63 0345;;;;N;;;1FAB;;1FAB
+1FA4;GREEK SMALL LETTER OMEGA WITH PSILI AND OXIA AND YPOGEGRAMMENI;Ll;0;L;1F64 0345;;;;N;;;1FAC;;1FAC
+1FA5;GREEK SMALL LETTER OMEGA WITH DASIA AND OXIA AND YPOGEGRAMMENI;Ll;0;L;1F65 0345;;;;N;;;1FAD;;1FAD
+1FA6;GREEK SMALL LETTER OMEGA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1F66 0345;;;;N;;;1FAE;;1FAE
+1FA7;GREEK SMALL LETTER OMEGA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1F67 0345;;;;N;;;1FAF;;1FAF
+1FA8;GREEK CAPITAL LETTER OMEGA WITH PSILI AND PROSGEGRAMMENI;Lt;0;L;1F68 0345;;;;N;;;;1FA0;
+1FA9;GREEK CAPITAL LETTER OMEGA WITH DASIA AND PROSGEGRAMMENI;Lt;0;L;1F69 0345;;;;N;;;;1FA1;
+1FAA;GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA AND PROSGEGRAMMENI;Lt;0;L;1F6A 0345;;;;N;;;;1FA2;
+1FAB;GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA AND PROSGEGRAMMENI;Lt;0;L;1F6B 0345;;;;N;;;;1FA3;
+1FAC;GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA AND PROSGEGRAMMENI;Lt;0;L;1F6C 0345;;;;N;;;;1FA4;
+1FAD;GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA AND PROSGEGRAMMENI;Lt;0;L;1F6D 0345;;;;N;;;;1FA5;
+1FAE;GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI;Lt;0;L;1F6E 0345;;;;N;;;;1FA6;
+1FAF;GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI;Lt;0;L;1F6F 0345;;;;N;;;;1FA7;
+1FB0;GREEK SMALL LETTER ALPHA WITH VRACHY;Ll;0;L;03B1 0306;;;;N;;;1FB8;;1FB8
+1FB1;GREEK SMALL LETTER ALPHA WITH MACRON;Ll;0;L;03B1 0304;;;;N;;;1FB9;;1FB9
+1FB2;GREEK SMALL LETTER ALPHA WITH VARIA AND YPOGEGRAMMENI;Ll;0;L;1F70 0345;;;;N;;;;;
+1FB3;GREEK SMALL LETTER ALPHA WITH YPOGEGRAMMENI;Ll;0;L;03B1 0345;;;;N;;;1FBC;;1FBC
+1FB4;GREEK SMALL LETTER ALPHA WITH OXIA AND YPOGEGRAMMENI;Ll;0;L;03AC 0345;;;;N;;;;;
+1FB6;GREEK SMALL LETTER ALPHA WITH PERISPOMENI;Ll;0;L;03B1 0342;;;;N;;;;;
+1FB7;GREEK SMALL LETTER ALPHA WITH PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1FB6 0345;;;;N;;;;;
+1FB8;GREEK CAPITAL LETTER ALPHA WITH VRACHY;Lu;0;L;0391 0306;;;;N;;;;1FB0;
+1FB9;GREEK CAPITAL LETTER ALPHA WITH MACRON;Lu;0;L;0391 0304;;;;N;;;;1FB1;
+1FBA;GREEK CAPITAL LETTER ALPHA WITH VARIA;Lu;0;L;0391 0300;;;;N;;;;1F70;
+1FBB;GREEK CAPITAL LETTER ALPHA WITH OXIA;Lu;0;L;0386;;;;N;;;;1F71;
+1FBC;GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI;Lt;0;L;0391 0345;;;;N;;;;1FB3;
+1FBD;GREEK KORONIS;Sk;0;ON;<compat> 0020 0313;;;;N;;;;;
+1FBE;GREEK PROSGEGRAMMENI;Ll;0;L;03B9;;;;N;;;0399;;0399
+1FBF;GREEK PSILI;Sk;0;ON;<compat> 0020 0313;;;;N;;;;;
+1FC0;GREEK PERISPOMENI;Sk;0;ON;<compat> 0020 0342;;;;N;;;;;
+1FC1;GREEK DIALYTIKA AND PERISPOMENI;Sk;0;ON;00A8 0342;;;;N;;;;;
+1FC2;GREEK SMALL LETTER ETA WITH VARIA AND YPOGEGRAMMENI;Ll;0;L;1F74 0345;;;;N;;;;;
+1FC3;GREEK SMALL LETTER ETA WITH YPOGEGRAMMENI;Ll;0;L;03B7 0345;;;;N;;;1FCC;;1FCC
+1FC4;GREEK SMALL LETTER ETA WITH OXIA AND YPOGEGRAMMENI;Ll;0;L;03AE 0345;;;;N;;;;;
+1FC6;GREEK SMALL LETTER ETA WITH PERISPOMENI;Ll;0;L;03B7 0342;;;;N;;;;;
+1FC7;GREEK SMALL LETTER ETA WITH PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1FC6 0345;;;;N;;;;;
+1FC8;GREEK CAPITAL LETTER EPSILON WITH VARIA;Lu;0;L;0395 0300;;;;N;;;;1F72;
+1FC9;GREEK CAPITAL LETTER EPSILON WITH OXIA;Lu;0;L;0388;;;;N;;;;1F73;
+1FCA;GREEK CAPITAL LETTER ETA WITH VARIA;Lu;0;L;0397 0300;;;;N;;;;1F74;
+1FCB;GREEK CAPITAL LETTER ETA WITH OXIA;Lu;0;L;0389;;;;N;;;;1F75;
+1FCC;GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI;Lt;0;L;0397 0345;;;;N;;;;1FC3;
+1FCD;GREEK PSILI AND VARIA;Sk;0;ON;1FBF 0300;;;;N;;;;;
+1FCE;GREEK PSILI AND OXIA;Sk;0;ON;1FBF 0301;;;;N;;;;;
+1FCF;GREEK PSILI AND PERISPOMENI;Sk;0;ON;1FBF 0342;;;;N;;;;;
+1FD0;GREEK SMALL LETTER IOTA WITH VRACHY;Ll;0;L;03B9 0306;;;;N;;;1FD8;;1FD8
+1FD1;GREEK SMALL LETTER IOTA WITH MACRON;Ll;0;L;03B9 0304;;;;N;;;1FD9;;1FD9
+1FD2;GREEK SMALL LETTER IOTA WITH DIALYTIKA AND VARIA;Ll;0;L;03CA 0300;;;;N;;;;;
+1FD3;GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA;Ll;0;L;0390;;;;N;;;;;
+1FD6;GREEK SMALL LETTER IOTA WITH PERISPOMENI;Ll;0;L;03B9 0342;;;;N;;;;;
+1FD7;GREEK SMALL LETTER IOTA WITH DIALYTIKA AND PERISPOMENI;Ll;0;L;03CA 0342;;;;N;;;;;
+1FD8;GREEK CAPITAL LETTER IOTA WITH VRACHY;Lu;0;L;0399 0306;;;;N;;;;1FD0;
+1FD9;GREEK CAPITAL LETTER IOTA WITH MACRON;Lu;0;L;0399 0304;;;;N;;;;1FD1;
+1FDA;GREEK CAPITAL LETTER IOTA WITH VARIA;Lu;0;L;0399 0300;;;;N;;;;1F76;
+1FDB;GREEK CAPITAL LETTER IOTA WITH OXIA;Lu;0;L;038A;;;;N;;;;1F77;
+1FDD;GREEK DASIA AND VARIA;Sk;0;ON;1FFE 0300;;;;N;;;;;
+1FDE;GREEK DASIA AND OXIA;Sk;0;ON;1FFE 0301;;;;N;;;;;
+1FDF;GREEK DASIA AND PERISPOMENI;Sk;0;ON;1FFE 0342;;;;N;;;;;
+1FE0;GREEK SMALL LETTER UPSILON WITH VRACHY;Ll;0;L;03C5 0306;;;;N;;;1FE8;;1FE8
+1FE1;GREEK SMALL LETTER UPSILON WITH MACRON;Ll;0;L;03C5 0304;;;;N;;;1FE9;;1FE9
+1FE2;GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND VARIA;Ll;0;L;03CB 0300;;;;N;;;;;
+1FE3;GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND OXIA;Ll;0;L;03B0;;;;N;;;;;
+1FE4;GREEK SMALL LETTER RHO WITH PSILI;Ll;0;L;03C1 0313;;;;N;;;;;
+1FE5;GREEK SMALL LETTER RHO WITH DASIA;Ll;0;L;03C1 0314;;;;N;;;1FEC;;1FEC
+1FE6;GREEK SMALL LETTER UPSILON WITH PERISPOMENI;Ll;0;L;03C5 0342;;;;N;;;;;
+1FE7;GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND PERISPOMENI;Ll;0;L;03CB 0342;;;;N;;;;;
+1FE8;GREEK CAPITAL LETTER UPSILON WITH VRACHY;Lu;0;L;03A5 0306;;;;N;;;;1FE0;
+1FE9;GREEK CAPITAL LETTER UPSILON WITH MACRON;Lu;0;L;03A5 0304;;;;N;;;;1FE1;
+1FEA;GREEK CAPITAL LETTER UPSILON WITH VARIA;Lu;0;L;03A5 0300;;;;N;;;;1F7A;
+1FEB;GREEK CAPITAL LETTER UPSILON WITH OXIA;Lu;0;L;038E;;;;N;;;;1F7B;
+1FEC;GREEK CAPITAL LETTER RHO WITH DASIA;Lu;0;L;03A1 0314;;;;N;;;;1FE5;
+1FED;GREEK DIALYTIKA AND VARIA;Sk;0;ON;00A8 0300;;;;N;;;;;
+1FEE;GREEK DIALYTIKA AND OXIA;Sk;0;ON;0385;;;;N;;;;;
+1FEF;GREEK VARIA;Sk;0;ON;0060;;;;N;;;;;
+1FF2;GREEK SMALL LETTER OMEGA WITH VARIA AND YPOGEGRAMMENI;Ll;0;L;1F7C 0345;;;;N;;;;;
+1FF3;GREEK SMALL LETTER OMEGA WITH YPOGEGRAMMENI;Ll;0;L;03C9 0345;;;;N;;;1FFC;;1FFC
+1FF4;GREEK SMALL LETTER OMEGA WITH OXIA AND YPOGEGRAMMENI;Ll;0;L;03CE 0345;;;;N;;;;;
+1FF6;GREEK SMALL LETTER OMEGA WITH PERISPOMENI;Ll;0;L;03C9 0342;;;;N;;;;;
+1FF7;GREEK SMALL LETTER OMEGA WITH PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1FF6 0345;;;;N;;;;;
+1FF8;GREEK CAPITAL LETTER OMICRON WITH VARIA;Lu;0;L;039F 0300;;;;N;;;;1F78;
+1FF9;GREEK CAPITAL LETTER OMICRON WITH OXIA;Lu;0;L;038C;;;;N;;;;1F79;
+1FFA;GREEK CAPITAL LETTER OMEGA WITH VARIA;Lu;0;L;03A9 0300;;;;N;;;;1F7C;
+1FFB;GREEK CAPITAL LETTER OMEGA WITH OXIA;Lu;0;L;038F;;;;N;;;;1F7D;
+1FFC;GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI;Lt;0;L;03A9 0345;;;;N;;;;1FF3;
+1FFD;GREEK OXIA;Sk;0;ON;00B4;;;;N;;;;;
+1FFE;GREEK DASIA;Sk;0;ON;<compat> 0020 0314;;;;N;;;;;
+2000;EN QUAD;Zs;0;WS;2002;;;;N;;;;;
+2001;EM QUAD;Zs;0;WS;2003;;;;N;;;;;
+2002;EN SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;;
+2003;EM SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;;
+2004;THREE-PER-EM SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;;
+2005;FOUR-PER-EM SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;;
+2006;SIX-PER-EM SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;;
+2007;FIGURE SPACE;Zs;0;WS;<noBreak> 0020;;;;N;;;;;
+2008;PUNCTUATION SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;;
+2009;THIN SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;;
+200A;HAIR SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;;
+200B;ZERO WIDTH SPACE;Cf;0;BN;;;;;N;;;;;
+200C;ZERO WIDTH NON-JOINER;Cf;0;BN;;;;;N;;;;;
+200D;ZERO WIDTH JOINER;Cf;0;BN;;;;;N;;;;;
+200E;LEFT-TO-RIGHT MARK;Cf;0;L;;;;;N;;;;;
+200F;RIGHT-TO-LEFT MARK;Cf;0;R;;;;;N;;;;;
+2010;HYPHEN;Pd;0;ON;;;;;N;;;;;
+2011;NON-BREAKING HYPHEN;Pd;0;ON;<noBreak> 2010;;;;N;;;;;
+2012;FIGURE DASH;Pd;0;ON;;;;;N;;;;;
+2013;EN DASH;Pd;0;ON;;;;;N;;;;;
+2014;EM DASH;Pd;0;ON;;;;;N;;;;;
+2015;HORIZONTAL BAR;Pd;0;ON;;;;;N;QUOTATION DASH;;;;
+2016;DOUBLE VERTICAL LINE;Po;0;ON;;;;;N;DOUBLE VERTICAL BAR;;;;
+2017;DOUBLE LOW LINE;Po;0;ON;<compat> 0020 0333;;;;N;SPACING DOUBLE UNDERSCORE;;;;
+2018;LEFT SINGLE QUOTATION MARK;Pi;0;ON;;;;;N;SINGLE TURNED COMMA QUOTATION MARK;;;;
+2019;RIGHT SINGLE QUOTATION MARK;Pf;0;ON;;;;;N;SINGLE COMMA QUOTATION MARK;;;;
+201A;SINGLE LOW-9 QUOTATION MARK;Ps;0;ON;;;;;N;LOW SINGLE COMMA QUOTATION MARK;;;;
+201B;SINGLE HIGH-REVERSED-9 QUOTATION MARK;Pi;0;ON;;;;;N;SINGLE REVERSED COMMA QUOTATION MARK;;;;
+201C;LEFT DOUBLE QUOTATION MARK;Pi;0;ON;;;;;N;DOUBLE TURNED COMMA QUOTATION MARK;;;;
+201D;RIGHT DOUBLE QUOTATION MARK;Pf;0;ON;;;;;N;DOUBLE COMMA QUOTATION MARK;;;;
+201E;DOUBLE LOW-9 QUOTATION MARK;Ps;0;ON;;;;;N;LOW DOUBLE COMMA QUOTATION MARK;;;;
+201F;DOUBLE HIGH-REVERSED-9 QUOTATION MARK;Pi;0;ON;;;;;N;DOUBLE REVERSED COMMA QUOTATION MARK;;;;
+2020;DAGGER;Po;0;ON;;;;;N;;;;;
+2021;DOUBLE DAGGER;Po;0;ON;;;;;N;;;;;
+2022;BULLET;Po;0;ON;;;;;N;;;;;
+2023;TRIANGULAR BULLET;Po;0;ON;;;;;N;;;;;
+2024;ONE DOT LEADER;Po;0;ON;<compat> 002E;;;;N;;;;;
+2025;TWO DOT LEADER;Po;0;ON;<compat> 002E 002E;;;;N;;;;;
+2026;HORIZONTAL ELLIPSIS;Po;0;ON;<compat> 002E 002E 002E;;;;N;;;;;
+2027;HYPHENATION POINT;Po;0;ON;;;;;N;;;;;
+2028;LINE SEPARATOR;Zl;0;WS;;;;;N;;;;;
+2029;PARAGRAPH SEPARATOR;Zp;0;B;;;;;N;;;;;
+202A;LEFT-TO-RIGHT EMBEDDING;Cf;0;LRE;;;;;N;;;;;
+202B;RIGHT-TO-LEFT EMBEDDING;Cf;0;RLE;;;;;N;;;;;
+202C;POP DIRECTIONAL FORMATTING;Cf;0;PDF;;;;;N;;;;;
+202D;LEFT-TO-RIGHT OVERRIDE;Cf;0;LRO;;;;;N;;;;;
+202E;RIGHT-TO-LEFT OVERRIDE;Cf;0;RLO;;;;;N;;;;;
+202F;NARROW NO-BREAK SPACE;Zs;0;WS;<noBreak> 0020;;;;N;;;;;
+2030;PER MILLE SIGN;Po;0;ET;;;;;N;;;;;
+2031;PER TEN THOUSAND SIGN;Po;0;ET;;;;;N;;;;;
+2032;PRIME;Po;0;ET;;;;;N;;;;;
+2033;DOUBLE PRIME;Po;0;ET;<compat> 2032 2032;;;;N;;;;;
+2034;TRIPLE PRIME;Po;0;ET;<compat> 2032 2032 2032;;;;N;;;;;
+2035;REVERSED PRIME;Po;0;ON;;;;;N;;;;;
+2036;REVERSED DOUBLE PRIME;Po;0;ON;<compat> 2035 2035;;;;N;;;;;
+2037;REVERSED TRIPLE PRIME;Po;0;ON;<compat> 2035 2035 2035;;;;N;;;;;
+2038;CARET;Po;0;ON;;;;;N;;;;;
+2039;SINGLE LEFT-POINTING ANGLE QUOTATION MARK;Pi;0;ON;;;;;Y;LEFT POINTING SINGLE GUILLEMET;;;;
+203A;SINGLE RIGHT-POINTING ANGLE QUOTATION MARK;Pf;0;ON;;;;;Y;RIGHT POINTING SINGLE GUILLEMET;;;;
+203B;REFERENCE MARK;Po;0;ON;;;;;N;;;;;
+203C;DOUBLE EXCLAMATION MARK;Po;0;ON;<compat> 0021 0021;;;;N;;;;;
+203D;INTERROBANG;Po;0;ON;;;;;N;;;;;
+203E;OVERLINE;Po;0;ON;<compat> 0020 0305;;;;N;SPACING OVERSCORE;;;;
+203F;UNDERTIE;Pc;0;ON;;;;;N;;Enotikon;;;
+2040;CHARACTER TIE;Pc;0;ON;;;;;N;;;;;
+2041;CARET INSERTION POINT;Po;0;ON;;;;;N;;;;;
+2042;ASTERISM;Po;0;ON;;;;;N;;;;;
+2043;HYPHEN BULLET;Po;0;ON;;;;;N;;;;;
+2044;FRACTION SLASH;Sm;0;CS;;;;;N;;;;;
+2045;LEFT SQUARE BRACKET WITH QUILL;Ps;0;ON;;;;;Y;;;;;
+2046;RIGHT SQUARE BRACKET WITH QUILL;Pe;0;ON;;;;;Y;;;;;
+2047;DOUBLE QUESTION MARK;Po;0;ON;<compat> 003F 003F;;;;N;;;;;
+2048;QUESTION EXCLAMATION MARK;Po;0;ON;<compat> 003F 0021;;;;N;;;;;
+2049;EXCLAMATION QUESTION MARK;Po;0;ON;<compat> 0021 003F;;;;N;;;;;
+204A;TIRONIAN SIGN ET;Po;0;ON;;;;;N;;;;;
+204B;REVERSED PILCROW SIGN;Po;0;ON;;;;;N;;;;;
+204C;BLACK LEFTWARDS BULLET;Po;0;ON;;;;;N;;;;;
+204D;BLACK RIGHTWARDS BULLET;Po;0;ON;;;;;N;;;;;
+204E;LOW ASTERISK;Po;0;ON;;;;;N;;;;;
+204F;REVERSED SEMICOLON;Po;0;ON;;;;;N;;;;;
+2050;CLOSE UP;Po;0;ON;;;;;N;;;;;
+2051;TWO ASTERISKS ALIGNED VERTICALLY;Po;0;ON;;;;;N;;;;;
+2052;COMMERCIAL MINUS SIGN;Sm;0;ON;;;;;N;;;;;
+2053;SWUNG DASH;Po;0;ON;;;;;N;;;;;
+2054;INVERTED UNDERTIE;Pc;0;ON;;;;;N;;;;;
+2057;QUADRUPLE PRIME;Po;0;ON;<compat> 2032 2032 2032 2032;;;;N;;;;;
+205F;MEDIUM MATHEMATICAL SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;;
+2060;WORD JOINER;Cf;0;BN;;;;;N;;;;;
+2061;FUNCTION APPLICATION;Cf;0;BN;;;;;N;;;;;
+2062;INVISIBLE TIMES;Cf;0;BN;;;;;N;;;;;
+2063;INVISIBLE SEPARATOR;Cf;0;BN;;;;;N;;;;;
+206A;INHIBIT SYMMETRIC SWAPPING;Cf;0;BN;;;;;N;;;;;
+206B;ACTIVATE SYMMETRIC SWAPPING;Cf;0;BN;;;;;N;;;;;
+206C;INHIBIT ARABIC FORM SHAPING;Cf;0;BN;;;;;N;;;;;
+206D;ACTIVATE ARABIC FORM SHAPING;Cf;0;BN;;;;;N;;;;;
+206E;NATIONAL DIGIT SHAPES;Cf;0;BN;;;;;N;;;;;
+206F;NOMINAL DIGIT SHAPES;Cf;0;BN;;;;;N;;;;;
+2070;SUPERSCRIPT ZERO;No;0;EN;<super> 0030;;0;0;N;SUPERSCRIPT DIGIT ZERO;;;;
+2071;SUPERSCRIPT LATIN SMALL LETTER I;Ll;0;L;<super> 0069;;;;N;;;;;
+2074;SUPERSCRIPT FOUR;No;0;EN;<super> 0034;;4;4;N;SUPERSCRIPT DIGIT FOUR;;;;
+2075;SUPERSCRIPT FIVE;No;0;EN;<super> 0035;;5;5;N;SUPERSCRIPT DIGIT FIVE;;;;
+2076;SUPERSCRIPT SIX;No;0;EN;<super> 0036;;6;6;N;SUPERSCRIPT DIGIT SIX;;;;
+2077;SUPERSCRIPT SEVEN;No;0;EN;<super> 0037;;7;7;N;SUPERSCRIPT DIGIT SEVEN;;;;
+2078;SUPERSCRIPT EIGHT;No;0;EN;<super> 0038;;8;8;N;SUPERSCRIPT DIGIT EIGHT;;;;
+2079;SUPERSCRIPT NINE;No;0;EN;<super> 0039;;9;9;N;SUPERSCRIPT DIGIT NINE;;;;
+207A;SUPERSCRIPT PLUS SIGN;Sm;0;ET;<super> 002B;;;;N;;;;;
+207B;SUPERSCRIPT MINUS;Sm;0;ET;<super> 2212;;;;N;SUPERSCRIPT HYPHEN-MINUS;;;;
+207C;SUPERSCRIPT EQUALS SIGN;Sm;0;ON;<super> 003D;;;;N;;;;;
+207D;SUPERSCRIPT LEFT PARENTHESIS;Ps;0;ON;<super> 0028;;;;Y;SUPERSCRIPT OPENING PARENTHESIS;;;;
+207E;SUPERSCRIPT RIGHT PARENTHESIS;Pe;0;ON;<super> 0029;;;;Y;SUPERSCRIPT CLOSING PARENTHESIS;;;;
+207F;SUPERSCRIPT LATIN SMALL LETTER N;Ll;0;L;<super> 006E;;;;N;;;;;
+2080;SUBSCRIPT ZERO;No;0;EN;<sub> 0030;;0;0;N;SUBSCRIPT DIGIT ZERO;;;;
+2081;SUBSCRIPT ONE;No;0;EN;<sub> 0031;;1;1;N;SUBSCRIPT DIGIT ONE;;;;
+2082;SUBSCRIPT TWO;No;0;EN;<sub> 0032;;2;2;N;SUBSCRIPT DIGIT TWO;;;;
+2083;SUBSCRIPT THREE;No;0;EN;<sub> 0033;;3;3;N;SUBSCRIPT DIGIT THREE;;;;
+2084;SUBSCRIPT FOUR;No;0;EN;<sub> 0034;;4;4;N;SUBSCRIPT DIGIT FOUR;;;;
+2085;SUBSCRIPT FIVE;No;0;EN;<sub> 0035;;5;5;N;SUBSCRIPT DIGIT FIVE;;;;
+2086;SUBSCRIPT SIX;No;0;EN;<sub> 0036;;6;6;N;SUBSCRIPT DIGIT SIX;;;;
+2087;SUBSCRIPT SEVEN;No;0;EN;<sub> 0037;;7;7;N;SUBSCRIPT DIGIT SEVEN;;;;
+2088;SUBSCRIPT EIGHT;No;0;EN;<sub> 0038;;8;8;N;SUBSCRIPT DIGIT EIGHT;;;;
+2089;SUBSCRIPT NINE;No;0;EN;<sub> 0039;;9;9;N;SUBSCRIPT DIGIT NINE;;;;
+208A;SUBSCRIPT PLUS SIGN;Sm;0;ET;<sub> 002B;;;;N;;;;;
+208B;SUBSCRIPT MINUS;Sm;0;ET;<sub> 2212;;;;N;SUBSCRIPT HYPHEN-MINUS;;;;
+208C;SUBSCRIPT EQUALS SIGN;Sm;0;ON;<sub> 003D;;;;N;;;;;
+208D;SUBSCRIPT LEFT PARENTHESIS;Ps;0;ON;<sub> 0028;;;;Y;SUBSCRIPT OPENING PARENTHESIS;;;;
+208E;SUBSCRIPT RIGHT PARENTHESIS;Pe;0;ON;<sub> 0029;;;;Y;SUBSCRIPT CLOSING PARENTHESIS;;;;
+20A0;EURO-CURRENCY SIGN;Sc;0;ET;;;;;N;;;;;
+20A1;COLON SIGN;Sc;0;ET;;;;;N;;;;;
+20A2;CRUZEIRO SIGN;Sc;0;ET;;;;;N;;;;;
+20A3;FRENCH FRANC SIGN;Sc;0;ET;;;;;N;;;;;
+20A4;LIRA SIGN;Sc;0;ET;;;;;N;;;;;
+20A5;MILL SIGN;Sc;0;ET;;;;;N;;;;;
+20A6;NAIRA SIGN;Sc;0;ET;;;;;N;;;;;
+20A7;PESETA SIGN;Sc;0;ET;;;;;N;;;;;
+20A8;RUPEE SIGN;Sc;0;ET;<compat> 0052 0073;;;;N;;;;;
+20A9;WON SIGN;Sc;0;ET;;;;;N;;;;;
+20AA;NEW SHEQEL SIGN;Sc;0;ET;;;;;N;;;;;
+20AB;DONG SIGN;Sc;0;ET;;;;;N;;;;;
+20AC;EURO SIGN;Sc;0;ET;;;;;N;;;;;
+20AD;KIP SIGN;Sc;0;ET;;;;;N;;;;;
+20AE;TUGRIK SIGN;Sc;0;ET;;;;;N;;;;;
+20AF;DRACHMA SIGN;Sc;0;ET;;;;;N;;;;;
+20B0;GERMAN PENNY SIGN;Sc;0;ET;;;;;N;;;;;
+20B1;PESO SIGN;Sc;0;ET;;;;;N;;;;;
+20D0;COMBINING LEFT HARPOON ABOVE;Mn;230;NSM;;;;;N;NON-SPACING LEFT HARPOON ABOVE;;;;
+20D1;COMBINING RIGHT HARPOON ABOVE;Mn;230;NSM;;;;;N;NON-SPACING RIGHT HARPOON ABOVE;;;;
+20D2;COMBINING LONG VERTICAL LINE OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING LONG VERTICAL BAR OVERLAY;;;;
+20D3;COMBINING SHORT VERTICAL LINE OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING SHORT VERTICAL BAR OVERLAY;;;;
+20D4;COMBINING ANTICLOCKWISE ARROW ABOVE;Mn;230;NSM;;;;;N;NON-SPACING ANTICLOCKWISE ARROW ABOVE;;;;
+20D5;COMBINING CLOCKWISE ARROW ABOVE;Mn;230;NSM;;;;;N;NON-SPACING CLOCKWISE ARROW ABOVE;;;;
+20D6;COMBINING LEFT ARROW ABOVE;Mn;230;NSM;;;;;N;NON-SPACING LEFT ARROW ABOVE;;;;
+20D7;COMBINING RIGHT ARROW ABOVE;Mn;230;NSM;;;;;N;NON-SPACING RIGHT ARROW ABOVE;;;;
+20D8;COMBINING RING OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING RING OVERLAY;;;;
+20D9;COMBINING CLOCKWISE RING OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING CLOCKWISE RING OVERLAY;;;;
+20DA;COMBINING ANTICLOCKWISE RING OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING ANTICLOCKWISE RING OVERLAY;;;;
+20DB;COMBINING THREE DOTS ABOVE;Mn;230;NSM;;;;;N;NON-SPACING THREE DOTS ABOVE;;;;
+20DC;COMBINING FOUR DOTS ABOVE;Mn;230;NSM;;;;;N;NON-SPACING FOUR DOTS ABOVE;;;;
+20DD;COMBINING ENCLOSING CIRCLE;Me;0;NSM;;;;;N;ENCLOSING CIRCLE;;;;
+20DE;COMBINING ENCLOSING SQUARE;Me;0;NSM;;;;;N;ENCLOSING SQUARE;;;;
+20DF;COMBINING ENCLOSING DIAMOND;Me;0;NSM;;;;;N;ENCLOSING DIAMOND;;;;
+20E0;COMBINING ENCLOSING CIRCLE BACKSLASH;Me;0;NSM;;;;;N;ENCLOSING CIRCLE SLASH;;;;
+20E1;COMBINING LEFT RIGHT ARROW ABOVE;Mn;230;NSM;;;;;N;NON-SPACING LEFT RIGHT ARROW ABOVE;;;;
+20E2;COMBINING ENCLOSING SCREEN;Me;0;NSM;;;;;N;;;;;
+20E3;COMBINING ENCLOSING KEYCAP;Me;0;NSM;;;;;N;;;;;
+20E4;COMBINING ENCLOSING UPWARD POINTING TRIANGLE;Me;0;NSM;;;;;N;;;;;
+20E5;COMBINING REVERSE SOLIDUS OVERLAY;Mn;1;NSM;;;;;N;;;;;
+20E6;COMBINING DOUBLE VERTICAL STROKE OVERLAY;Mn;1;NSM;;;;;N;;;;;
+20E7;COMBINING ANNUITY SYMBOL;Mn;230;NSM;;;;;N;;;;;
+20E8;COMBINING TRIPLE UNDERDOT;Mn;220;NSM;;;;;N;;;;;
+20E9;COMBINING WIDE BRIDGE ABOVE;Mn;230;NSM;;;;;N;;;;;
+20EA;COMBINING LEFTWARDS ARROW OVERLAY;Mn;1;NSM;;;;;N;;;;;
+2100;ACCOUNT OF;So;0;ON;<compat> 0061 002F 0063;;;;N;;;;;
+2101;ADDRESSED TO THE SUBJECT;So;0;ON;<compat> 0061 002F 0073;;;;N;;;;;
+2102;DOUBLE-STRUCK CAPITAL C;Lu;0;L;<font> 0043;;;;N;DOUBLE-STRUCK C;;;;
+2103;DEGREE CELSIUS;So;0;ON;<compat> 00B0 0043;;;;N;DEGREES CENTIGRADE;;;;
+2104;CENTRE LINE SYMBOL;So;0;ON;;;;;N;C L SYMBOL;;;;
+2105;CARE OF;So;0;ON;<compat> 0063 002F 006F;;;;N;;;;;
+2106;CADA UNA;So;0;ON;<compat> 0063 002F 0075;;;;N;;;;;
+2107;EULER CONSTANT;Lu;0;L;<compat> 0190;;;;N;EULERS;;;;
+2108;SCRUPLE;So;0;ON;;;;;N;;;;;
+2109;DEGREE FAHRENHEIT;So;0;ON;<compat> 00B0 0046;;;;N;DEGREES FAHRENHEIT;;;;
+210A;SCRIPT SMALL G;Ll;0;L;<font> 0067;;;;N;;;;;
+210B;SCRIPT CAPITAL H;Lu;0;L;<font> 0048;;;;N;SCRIPT H;;;;
+210C;BLACK-LETTER CAPITAL H;Lu;0;L;<font> 0048;;;;N;BLACK-LETTER H;;;;
+210D;DOUBLE-STRUCK CAPITAL H;Lu;0;L;<font> 0048;;;;N;DOUBLE-STRUCK H;;;;
+210E;PLANCK CONSTANT;Ll;0;L;<font> 0068;;;;N;;;;;
+210F;PLANCK CONSTANT OVER TWO PI;Ll;0;L;<font> 0127;;;;N;PLANCK CONSTANT OVER 2 PI;;;;
+2110;SCRIPT CAPITAL I;Lu;0;L;<font> 0049;;;;N;SCRIPT I;;;;
+2111;BLACK-LETTER CAPITAL I;Lu;0;L;<font> 0049;;;;N;BLACK-LETTER I;;;;
+2112;SCRIPT CAPITAL L;Lu;0;L;<font> 004C;;;;N;SCRIPT L;;;;
+2113;SCRIPT SMALL L;Ll;0;L;<font> 006C;;;;N;;;;;
+2114;L B BAR SYMBOL;So;0;ON;;;;;N;;;;;
+2115;DOUBLE-STRUCK CAPITAL N;Lu;0;L;<font> 004E;;;;N;DOUBLE-STRUCK N;;;;
+2116;NUMERO SIGN;So;0;ON;<compat> 004E 006F;;;;N;NUMERO;;;;
+2117;SOUND RECORDING COPYRIGHT;So;0;ON;;;;;N;;;;;
+2118;SCRIPT CAPITAL P;So;0;ON;;;;;N;SCRIPT P;;;;
+2119;DOUBLE-STRUCK CAPITAL P;Lu;0;L;<font> 0050;;;;N;DOUBLE-STRUCK P;;;;
+211A;DOUBLE-STRUCK CAPITAL Q;Lu;0;L;<font> 0051;;;;N;DOUBLE-STRUCK Q;;;;
+211B;SCRIPT CAPITAL R;Lu;0;L;<font> 0052;;;;N;SCRIPT R;;;;
+211C;BLACK-LETTER CAPITAL R;Lu;0;L;<font> 0052;;;;N;BLACK-LETTER R;;;;
+211D;DOUBLE-STRUCK CAPITAL R;Lu;0;L;<font> 0052;;;;N;DOUBLE-STRUCK R;;;;
+211E;PRESCRIPTION TAKE;So;0;ON;;;;;N;;;;;
+211F;RESPONSE;So;0;ON;;;;;N;;;;;
+2120;SERVICE MARK;So;0;ON;<super> 0053 004D;;;;N;;;;;
+2121;TELEPHONE SIGN;So;0;ON;<compat> 0054 0045 004C;;;;N;T E L SYMBOL;;;;
+2122;TRADE MARK SIGN;So;0;ON;<super> 0054 004D;;;;N;TRADEMARK;;;;
+2123;VERSICLE;So;0;ON;;;;;N;;;;;
+2124;DOUBLE-STRUCK CAPITAL Z;Lu;0;L;<font> 005A;;;;N;DOUBLE-STRUCK Z;;;;
+2125;OUNCE SIGN;So;0;ON;;;;;N;OUNCE;;;;
+2126;OHM SIGN;Lu;0;L;03A9;;;;N;OHM;;;03C9;
+2127;INVERTED OHM SIGN;So;0;ON;;;;;N;MHO;;;;
+2128;BLACK-LETTER CAPITAL Z;Lu;0;L;<font> 005A;;;;N;BLACK-LETTER Z;;;;
+2129;TURNED GREEK SMALL LETTER IOTA;So;0;ON;;;;;N;;;;;
+212A;KELVIN SIGN;Lu;0;L;004B;;;;N;DEGREES KELVIN;;;006B;
+212B;ANGSTROM SIGN;Lu;0;L;00C5;;;;N;ANGSTROM UNIT;;;00E5;
+212C;SCRIPT CAPITAL B;Lu;0;L;<font> 0042;;;;N;SCRIPT B;;;;
+212D;BLACK-LETTER CAPITAL C;Lu;0;L;<font> 0043;;;;N;BLACK-LETTER C;;;;
+212E;ESTIMATED SYMBOL;So;0;ET;;;;;N;;;;;
+212F;SCRIPT SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+2130;SCRIPT CAPITAL E;Lu;0;L;<font> 0045;;;;N;SCRIPT E;;;;
+2131;SCRIPT CAPITAL F;Lu;0;L;<font> 0046;;;;N;SCRIPT F;;;;
+2132;TURNED CAPITAL F;So;0;ON;;;;;N;TURNED F;;;;
+2133;SCRIPT CAPITAL M;Lu;0;L;<font> 004D;;;;N;SCRIPT M;;;;
+2134;SCRIPT SMALL O;Ll;0;L;<font> 006F;;;;N;;;;;
+2135;ALEF SYMBOL;Lo;0;L;<compat> 05D0;;;;N;FIRST TRANSFINITE CARDINAL;;;;
+2136;BET SYMBOL;Lo;0;L;<compat> 05D1;;;;N;SECOND TRANSFINITE CARDINAL;;;;
+2137;GIMEL SYMBOL;Lo;0;L;<compat> 05D2;;;;N;THIRD TRANSFINITE CARDINAL;;;;
+2138;DALET SYMBOL;Lo;0;L;<compat> 05D3;;;;N;FOURTH TRANSFINITE CARDINAL;;;;
+2139;INFORMATION SOURCE;Ll;0;L;<font> 0069;;;;N;;;;;
+213A;ROTATED CAPITAL Q;So;0;ON;;;;;N;;;;;
+213B;FACSIMILE SIGN;So;0;ON;<compat> 0046 0041 0058;;;;N;;;;;
+213D;DOUBLE-STRUCK SMALL GAMMA;Ll;0;L;<font> 03B3;;;;N;;;;;
+213E;DOUBLE-STRUCK CAPITAL GAMMA;Lu;0;L;<font> 0393;;;;N;;;;;
+213F;DOUBLE-STRUCK CAPITAL PI;Lu;0;L;<font> 03A0;;;;N;;;;;
+2140;DOUBLE-STRUCK N-ARY SUMMATION;Sm;0;ON;<font> 2211;;;;Y;;;;;
+2141;TURNED SANS-SERIF CAPITAL G;Sm;0;ON;;;;;N;;;;;
+2142;TURNED SANS-SERIF CAPITAL L;Sm;0;ON;;;;;N;;;;;
+2143;REVERSED SANS-SERIF CAPITAL L;Sm;0;ON;;;;;N;;;;;
+2144;TURNED SANS-SERIF CAPITAL Y;Sm;0;ON;;;;;N;;;;;
+2145;DOUBLE-STRUCK ITALIC CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+2146;DOUBLE-STRUCK ITALIC SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+2147;DOUBLE-STRUCK ITALIC SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+2148;DOUBLE-STRUCK ITALIC SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+2149;DOUBLE-STRUCK ITALIC SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+214A;PROPERTY LINE;So;0;ON;;;;;N;;;;;
+214B;TURNED AMPERSAND;Sm;0;ON;;;;;N;;;;;
+2153;VULGAR FRACTION ONE THIRD;No;0;ON;<fraction> 0031 2044 0033;;;1/3;N;FRACTION ONE THIRD;;;;
+2154;VULGAR FRACTION TWO THIRDS;No;0;ON;<fraction> 0032 2044 0033;;;2/3;N;FRACTION TWO THIRDS;;;;
+2155;VULGAR FRACTION ONE FIFTH;No;0;ON;<fraction> 0031 2044 0035;;;1/5;N;FRACTION ONE FIFTH;;;;
+2156;VULGAR FRACTION TWO FIFTHS;No;0;ON;<fraction> 0032 2044 0035;;;2/5;N;FRACTION TWO FIFTHS;;;;
+2157;VULGAR FRACTION THREE FIFTHS;No;0;ON;<fraction> 0033 2044 0035;;;3/5;N;FRACTION THREE FIFTHS;;;;
+2158;VULGAR FRACTION FOUR FIFTHS;No;0;ON;<fraction> 0034 2044 0035;;;4/5;N;FRACTION FOUR FIFTHS;;;;
+2159;VULGAR FRACTION ONE SIXTH;No;0;ON;<fraction> 0031 2044 0036;;;1/6;N;FRACTION ONE SIXTH;;;;
+215A;VULGAR FRACTION FIVE SIXTHS;No;0;ON;<fraction> 0035 2044 0036;;;5/6;N;FRACTION FIVE SIXTHS;;;;
+215B;VULGAR FRACTION ONE EIGHTH;No;0;ON;<fraction> 0031 2044 0038;;;1/8;N;FRACTION ONE EIGHTH;;;;
+215C;VULGAR FRACTION THREE EIGHTHS;No;0;ON;<fraction> 0033 2044 0038;;;3/8;N;FRACTION THREE EIGHTHS;;;;
+215D;VULGAR FRACTION FIVE EIGHTHS;No;0;ON;<fraction> 0035 2044 0038;;;5/8;N;FRACTION FIVE EIGHTHS;;;;
+215E;VULGAR FRACTION SEVEN EIGHTHS;No;0;ON;<fraction> 0037 2044 0038;;;7/8;N;FRACTION SEVEN EIGHTHS;;;;
+215F;FRACTION NUMERATOR ONE;No;0;ON;<fraction> 0031 2044;;;1;N;;;;;
+2160;ROMAN NUMERAL ONE;Nl;0;L;<compat> 0049;;;1;N;;;;2170;
+2161;ROMAN NUMERAL TWO;Nl;0;L;<compat> 0049 0049;;;2;N;;;;2171;
+2162;ROMAN NUMERAL THREE;Nl;0;L;<compat> 0049 0049 0049;;;3;N;;;;2172;
+2163;ROMAN NUMERAL FOUR;Nl;0;L;<compat> 0049 0056;;;4;N;;;;2173;
+2164;ROMAN NUMERAL FIVE;Nl;0;L;<compat> 0056;;;5;N;;;;2174;
+2165;ROMAN NUMERAL SIX;Nl;0;L;<compat> 0056 0049;;;6;N;;;;2175;
+2166;ROMAN NUMERAL SEVEN;Nl;0;L;<compat> 0056 0049 0049;;;7;N;;;;2176;
+2167;ROMAN NUMERAL EIGHT;Nl;0;L;<compat> 0056 0049 0049 0049;;;8;N;;;;2177;
+2168;ROMAN NUMERAL NINE;Nl;0;L;<compat> 0049 0058;;;9;N;;;;2178;
+2169;ROMAN NUMERAL TEN;Nl;0;L;<compat> 0058;;;10;N;;;;2179;
+216A;ROMAN NUMERAL ELEVEN;Nl;0;L;<compat> 0058 0049;;;11;N;;;;217A;
+216B;ROMAN NUMERAL TWELVE;Nl;0;L;<compat> 0058 0049 0049;;;12;N;;;;217B;
+216C;ROMAN NUMERAL FIFTY;Nl;0;L;<compat> 004C;;;50;N;;;;217C;
+216D;ROMAN NUMERAL ONE HUNDRED;Nl;0;L;<compat> 0043;;;100;N;;;;217D;
+216E;ROMAN NUMERAL FIVE HUNDRED;Nl;0;L;<compat> 0044;;;500;N;;;;217E;
+216F;ROMAN NUMERAL ONE THOUSAND;Nl;0;L;<compat> 004D;;;1000;N;;;;217F;
+2170;SMALL ROMAN NUMERAL ONE;Nl;0;L;<compat> 0069;;;1;N;;;2160;;2160
+2171;SMALL ROMAN NUMERAL TWO;Nl;0;L;<compat> 0069 0069;;;2;N;;;2161;;2161
+2172;SMALL ROMAN NUMERAL THREE;Nl;0;L;<compat> 0069 0069 0069;;;3;N;;;2162;;2162
+2173;SMALL ROMAN NUMERAL FOUR;Nl;0;L;<compat> 0069 0076;;;4;N;;;2163;;2163
+2174;SMALL ROMAN NUMERAL FIVE;Nl;0;L;<compat> 0076;;;5;N;;;2164;;2164
+2175;SMALL ROMAN NUMERAL SIX;Nl;0;L;<compat> 0076 0069;;;6;N;;;2165;;2165
+2176;SMALL ROMAN NUMERAL SEVEN;Nl;0;L;<compat> 0076 0069 0069;;;7;N;;;2166;;2166
+2177;SMALL ROMAN NUMERAL EIGHT;Nl;0;L;<compat> 0076 0069 0069 0069;;;8;N;;;2167;;2167
+2178;SMALL ROMAN NUMERAL NINE;Nl;0;L;<compat> 0069 0078;;;9;N;;;2168;;2168
+2179;SMALL ROMAN NUMERAL TEN;Nl;0;L;<compat> 0078;;;10;N;;;2169;;2169
+217A;SMALL ROMAN NUMERAL ELEVEN;Nl;0;L;<compat> 0078 0069;;;11;N;;;216A;;216A
+217B;SMALL ROMAN NUMERAL TWELVE;Nl;0;L;<compat> 0078 0069 0069;;;12;N;;;216B;;216B
+217C;SMALL ROMAN NUMERAL FIFTY;Nl;0;L;<compat> 006C;;;50;N;;;216C;;216C
+217D;SMALL ROMAN NUMERAL ONE HUNDRED;Nl;0;L;<compat> 0063;;;100;N;;;216D;;216D
+217E;SMALL ROMAN NUMERAL FIVE HUNDRED;Nl;0;L;<compat> 0064;;;500;N;;;216E;;216E
+217F;SMALL ROMAN NUMERAL ONE THOUSAND;Nl;0;L;<compat> 006D;;;1000;N;;;216F;;216F
+2180;ROMAN NUMERAL ONE THOUSAND C D;Nl;0;L;;;;1000;N;;;;;
+2181;ROMAN NUMERAL FIVE THOUSAND;Nl;0;L;;;;5000;N;;;;;
+2182;ROMAN NUMERAL TEN THOUSAND;Nl;0;L;;;;10000;N;;;;;
+2183;ROMAN NUMERAL REVERSED ONE HUNDRED;Nl;0;L;;;;;N;;;;;
+2190;LEFTWARDS ARROW;Sm;0;ON;;;;;N;LEFT ARROW;;;;
+2191;UPWARDS ARROW;Sm;0;ON;;;;;N;UP ARROW;;;;
+2192;RIGHTWARDS ARROW;Sm;0;ON;;;;;N;RIGHT ARROW;;;;
+2193;DOWNWARDS ARROW;Sm;0;ON;;;;;N;DOWN ARROW;;;;
+2194;LEFT RIGHT ARROW;Sm;0;ON;;;;;N;;;;;
+2195;UP DOWN ARROW;So;0;ON;;;;;N;;;;;
+2196;NORTH WEST ARROW;So;0;ON;;;;;N;UPPER LEFT ARROW;;;;
+2197;NORTH EAST ARROW;So;0;ON;;;;;N;UPPER RIGHT ARROW;;;;
+2198;SOUTH EAST ARROW;So;0;ON;;;;;N;LOWER RIGHT ARROW;;;;
+2199;SOUTH WEST ARROW;So;0;ON;;;;;N;LOWER LEFT ARROW;;;;
+219A;LEFTWARDS ARROW WITH STROKE;Sm;0;ON;2190 0338;;;;N;LEFT ARROW WITH STROKE;;;;
+219B;RIGHTWARDS ARROW WITH STROKE;Sm;0;ON;2192 0338;;;;N;RIGHT ARROW WITH STROKE;;;;
+219C;LEFTWARDS WAVE ARROW;So;0;ON;;;;;N;LEFT WAVE ARROW;;;;
+219D;RIGHTWARDS WAVE ARROW;So;0;ON;;;;;N;RIGHT WAVE ARROW;;;;
+219E;LEFTWARDS TWO HEADED ARROW;So;0;ON;;;;;N;LEFT TWO HEADED ARROW;;;;
+219F;UPWARDS TWO HEADED ARROW;So;0;ON;;;;;N;UP TWO HEADED ARROW;;;;
+21A0;RIGHTWARDS TWO HEADED ARROW;Sm;0;ON;;;;;N;RIGHT TWO HEADED ARROW;;;;
+21A1;DOWNWARDS TWO HEADED ARROW;So;0;ON;;;;;N;DOWN TWO HEADED ARROW;;;;
+21A2;LEFTWARDS ARROW WITH TAIL;So;0;ON;;;;;N;LEFT ARROW WITH TAIL;;;;
+21A3;RIGHTWARDS ARROW WITH TAIL;Sm;0;ON;;;;;N;RIGHT ARROW WITH TAIL;;;;
+21A4;LEFTWARDS ARROW FROM BAR;So;0;ON;;;;;N;LEFT ARROW FROM BAR;;;;
+21A5;UPWARDS ARROW FROM BAR;So;0;ON;;;;;N;UP ARROW FROM BAR;;;;
+21A6;RIGHTWARDS ARROW FROM BAR;Sm;0;ON;;;;;N;RIGHT ARROW FROM BAR;;;;
+21A7;DOWNWARDS ARROW FROM BAR;So;0;ON;;;;;N;DOWN ARROW FROM BAR;;;;
+21A8;UP DOWN ARROW WITH BASE;So;0;ON;;;;;N;;;;;
+21A9;LEFTWARDS ARROW WITH HOOK;So;0;ON;;;;;N;LEFT ARROW WITH HOOK;;;;
+21AA;RIGHTWARDS ARROW WITH HOOK;So;0;ON;;;;;N;RIGHT ARROW WITH HOOK;;;;
+21AB;LEFTWARDS ARROW WITH LOOP;So;0;ON;;;;;N;LEFT ARROW WITH LOOP;;;;
+21AC;RIGHTWARDS ARROW WITH LOOP;So;0;ON;;;;;N;RIGHT ARROW WITH LOOP;;;;
+21AD;LEFT RIGHT WAVE ARROW;So;0;ON;;;;;N;;;;;
+21AE;LEFT RIGHT ARROW WITH STROKE;Sm;0;ON;2194 0338;;;;N;;;;;
+21AF;DOWNWARDS ZIGZAG ARROW;So;0;ON;;;;;N;DOWN ZIGZAG ARROW;;;;
+21B0;UPWARDS ARROW WITH TIP LEFTWARDS;So;0;ON;;;;;N;UP ARROW WITH TIP LEFT;;;;
+21B1;UPWARDS ARROW WITH TIP RIGHTWARDS;So;0;ON;;;;;N;UP ARROW WITH TIP RIGHT;;;;
+21B2;DOWNWARDS ARROW WITH TIP LEFTWARDS;So;0;ON;;;;;N;DOWN ARROW WITH TIP LEFT;;;;
+21B3;DOWNWARDS ARROW WITH TIP RIGHTWARDS;So;0;ON;;;;;N;DOWN ARROW WITH TIP RIGHT;;;;
+21B4;RIGHTWARDS ARROW WITH CORNER DOWNWARDS;So;0;ON;;;;;N;RIGHT ARROW WITH CORNER DOWN;;;;
+21B5;DOWNWARDS ARROW WITH CORNER LEFTWARDS;So;0;ON;;;;;N;DOWN ARROW WITH CORNER LEFT;;;;
+21B6;ANTICLOCKWISE TOP SEMICIRCLE ARROW;So;0;ON;;;;;N;;;;;
+21B7;CLOCKWISE TOP SEMICIRCLE ARROW;So;0;ON;;;;;N;;;;;
+21B8;NORTH WEST ARROW TO LONG BAR;So;0;ON;;;;;N;UPPER LEFT ARROW TO LONG BAR;;;;
+21B9;LEFTWARDS ARROW TO BAR OVER RIGHTWARDS ARROW TO BAR;So;0;ON;;;;;N;LEFT ARROW TO BAR OVER RIGHT ARROW TO BAR;;;;
+21BA;ANTICLOCKWISE OPEN CIRCLE ARROW;So;0;ON;;;;;N;;;;;
+21BB;CLOCKWISE OPEN CIRCLE ARROW;So;0;ON;;;;;N;;;;;
+21BC;LEFTWARDS HARPOON WITH BARB UPWARDS;So;0;ON;;;;;N;LEFT HARPOON WITH BARB UP;;;;
+21BD;LEFTWARDS HARPOON WITH BARB DOWNWARDS;So;0;ON;;;;;N;LEFT HARPOON WITH BARB DOWN;;;;
+21BE;UPWARDS HARPOON WITH BARB RIGHTWARDS;So;0;ON;;;;;N;UP HARPOON WITH BARB RIGHT;;;;
+21BF;UPWARDS HARPOON WITH BARB LEFTWARDS;So;0;ON;;;;;N;UP HARPOON WITH BARB LEFT;;;;
+21C0;RIGHTWARDS HARPOON WITH BARB UPWARDS;So;0;ON;;;;;N;RIGHT HARPOON WITH BARB UP;;;;
+21C1;RIGHTWARDS HARPOON WITH BARB DOWNWARDS;So;0;ON;;;;;N;RIGHT HARPOON WITH BARB DOWN;;;;
+21C2;DOWNWARDS HARPOON WITH BARB RIGHTWARDS;So;0;ON;;;;;N;DOWN HARPOON WITH BARB RIGHT;;;;
+21C3;DOWNWARDS HARPOON WITH BARB LEFTWARDS;So;0;ON;;;;;N;DOWN HARPOON WITH BARB LEFT;;;;
+21C4;RIGHTWARDS ARROW OVER LEFTWARDS ARROW;So;0;ON;;;;;N;RIGHT ARROW OVER LEFT ARROW;;;;
+21C5;UPWARDS ARROW LEFTWARDS OF DOWNWARDS ARROW;So;0;ON;;;;;N;UP ARROW LEFT OF DOWN ARROW;;;;
+21C6;LEFTWARDS ARROW OVER RIGHTWARDS ARROW;So;0;ON;;;;;N;LEFT ARROW OVER RIGHT ARROW;;;;
+21C7;LEFTWARDS PAIRED ARROWS;So;0;ON;;;;;N;LEFT PAIRED ARROWS;;;;
+21C8;UPWARDS PAIRED ARROWS;So;0;ON;;;;;N;UP PAIRED ARROWS;;;;
+21C9;RIGHTWARDS PAIRED ARROWS;So;0;ON;;;;;N;RIGHT PAIRED ARROWS;;;;
+21CA;DOWNWARDS PAIRED ARROWS;So;0;ON;;;;;N;DOWN PAIRED ARROWS;;;;
+21CB;LEFTWARDS HARPOON OVER RIGHTWARDS HARPOON;So;0;ON;;;;;N;LEFT HARPOON OVER RIGHT HARPOON;;;;
+21CC;RIGHTWARDS HARPOON OVER LEFTWARDS HARPOON;So;0;ON;;;;;N;RIGHT HARPOON OVER LEFT HARPOON;;;;
+21CD;LEFTWARDS DOUBLE ARROW WITH STROKE;So;0;ON;21D0 0338;;;;N;LEFT DOUBLE ARROW WITH STROKE;;;;
+21CE;LEFT RIGHT DOUBLE ARROW WITH STROKE;Sm;0;ON;21D4 0338;;;;N;;;;;
+21CF;RIGHTWARDS DOUBLE ARROW WITH STROKE;Sm;0;ON;21D2 0338;;;;N;RIGHT DOUBLE ARROW WITH STROKE;;;;
+21D0;LEFTWARDS DOUBLE ARROW;So;0;ON;;;;;N;LEFT DOUBLE ARROW;;;;
+21D1;UPWARDS DOUBLE ARROW;So;0;ON;;;;;N;UP DOUBLE ARROW;;;;
+21D2;RIGHTWARDS DOUBLE ARROW;Sm;0;ON;;;;;N;RIGHT DOUBLE ARROW;;;;
+21D3;DOWNWARDS DOUBLE ARROW;So;0;ON;;;;;N;DOWN DOUBLE ARROW;;;;
+21D4;LEFT RIGHT DOUBLE ARROW;Sm;0;ON;;;;;N;;;;;
+21D5;UP DOWN DOUBLE ARROW;So;0;ON;;;;;N;;;;;
+21D6;NORTH WEST DOUBLE ARROW;So;0;ON;;;;;N;UPPER LEFT DOUBLE ARROW;;;;
+21D7;NORTH EAST DOUBLE ARROW;So;0;ON;;;;;N;UPPER RIGHT DOUBLE ARROW;;;;
+21D8;SOUTH EAST DOUBLE ARROW;So;0;ON;;;;;N;LOWER RIGHT DOUBLE ARROW;;;;
+21D9;SOUTH WEST DOUBLE ARROW;So;0;ON;;;;;N;LOWER LEFT DOUBLE ARROW;;;;
+21DA;LEFTWARDS TRIPLE ARROW;So;0;ON;;;;;N;LEFT TRIPLE ARROW;;;;
+21DB;RIGHTWARDS TRIPLE ARROW;So;0;ON;;;;;N;RIGHT TRIPLE ARROW;;;;
+21DC;LEFTWARDS SQUIGGLE ARROW;So;0;ON;;;;;N;LEFT SQUIGGLE ARROW;;;;
+21DD;RIGHTWARDS SQUIGGLE ARROW;So;0;ON;;;;;N;RIGHT SQUIGGLE ARROW;;;;
+21DE;UPWARDS ARROW WITH DOUBLE STROKE;So;0;ON;;;;;N;UP ARROW WITH DOUBLE STROKE;;;;
+21DF;DOWNWARDS ARROW WITH DOUBLE STROKE;So;0;ON;;;;;N;DOWN ARROW WITH DOUBLE STROKE;;;;
+21E0;LEFTWARDS DASHED ARROW;So;0;ON;;;;;N;LEFT DASHED ARROW;;;;
+21E1;UPWARDS DASHED ARROW;So;0;ON;;;;;N;UP DASHED ARROW;;;;
+21E2;RIGHTWARDS DASHED ARROW;So;0;ON;;;;;N;RIGHT DASHED ARROW;;;;
+21E3;DOWNWARDS DASHED ARROW;So;0;ON;;;;;N;DOWN DASHED ARROW;;;;
+21E4;LEFTWARDS ARROW TO BAR;So;0;ON;;;;;N;LEFT ARROW TO BAR;;;;
+21E5;RIGHTWARDS ARROW TO BAR;So;0;ON;;;;;N;RIGHT ARROW TO BAR;;;;
+21E6;LEFTWARDS WHITE ARROW;So;0;ON;;;;;N;WHITE LEFT ARROW;;;;
+21E7;UPWARDS WHITE ARROW;So;0;ON;;;;;N;WHITE UP ARROW;;;;
+21E8;RIGHTWARDS WHITE ARROW;So;0;ON;;;;;N;WHITE RIGHT ARROW;;;;
+21E9;DOWNWARDS WHITE ARROW;So;0;ON;;;;;N;WHITE DOWN ARROW;;;;
+21EA;UPWARDS WHITE ARROW FROM BAR;So;0;ON;;;;;N;WHITE UP ARROW FROM BAR;;;;
+21EB;UPWARDS WHITE ARROW ON PEDESTAL;So;0;ON;;;;;N;;;;;
+21EC;UPWARDS WHITE ARROW ON PEDESTAL WITH HORIZONTAL BAR;So;0;ON;;;;;N;;;;;
+21ED;UPWARDS WHITE ARROW ON PEDESTAL WITH VERTICAL BAR;So;0;ON;;;;;N;;;;;
+21EE;UPWARDS WHITE DOUBLE ARROW;So;0;ON;;;;;N;;;;;
+21EF;UPWARDS WHITE DOUBLE ARROW ON PEDESTAL;So;0;ON;;;;;N;;;;;
+21F0;RIGHTWARDS WHITE ARROW FROM WALL;So;0;ON;;;;;N;;;;;
+21F1;NORTH WEST ARROW TO CORNER;So;0;ON;;;;;N;;;;;
+21F2;SOUTH EAST ARROW TO CORNER;So;0;ON;;;;;N;;;;;
+21F3;UP DOWN WHITE ARROW;So;0;ON;;;;;N;;;;;
+21F4;RIGHT ARROW WITH SMALL CIRCLE;Sm;0;ON;;;;;N;;;;;
+21F5;DOWNWARDS ARROW LEFTWARDS OF UPWARDS ARROW;Sm;0;ON;;;;;N;;;;;
+21F6;THREE RIGHTWARDS ARROWS;Sm;0;ON;;;;;N;;;;;
+21F7;LEFTWARDS ARROW WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+21F8;RIGHTWARDS ARROW WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+21F9;LEFT RIGHT ARROW WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+21FA;LEFTWARDS ARROW WITH DOUBLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+21FB;RIGHTWARDS ARROW WITH DOUBLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+21FC;LEFT RIGHT ARROW WITH DOUBLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+21FD;LEFTWARDS OPEN-HEADED ARROW;Sm;0;ON;;;;;N;;;;;
+21FE;RIGHTWARDS OPEN-HEADED ARROW;Sm;0;ON;;;;;N;;;;;
+21FF;LEFT RIGHT OPEN-HEADED ARROW;Sm;0;ON;;;;;N;;;;;
+2200;FOR ALL;Sm;0;ON;;;;;N;;;;;
+2201;COMPLEMENT;Sm;0;ON;;;;;Y;;;;;
+2202;PARTIAL DIFFERENTIAL;Sm;0;ON;;;;;Y;;;;;
+2203;THERE EXISTS;Sm;0;ON;;;;;Y;;;;;
+2204;THERE DOES NOT EXIST;Sm;0;ON;2203 0338;;;;Y;;;;;
+2205;EMPTY SET;Sm;0;ON;;;;;N;;;;;
+2206;INCREMENT;Sm;0;ON;;;;;N;;;;;
+2207;NABLA;Sm;0;ON;;;;;N;;;;;
+2208;ELEMENT OF;Sm;0;ON;;;;;Y;;;;;
+2209;NOT AN ELEMENT OF;Sm;0;ON;2208 0338;;;;Y;;;;;
+220A;SMALL ELEMENT OF;Sm;0;ON;;;;;Y;;;;;
+220B;CONTAINS AS MEMBER;Sm;0;ON;;;;;Y;;;;;
+220C;DOES NOT CONTAIN AS MEMBER;Sm;0;ON;220B 0338;;;;Y;;;;;
+220D;SMALL CONTAINS AS MEMBER;Sm;0;ON;;;;;Y;;;;;
+220E;END OF PROOF;Sm;0;ON;;;;;N;;;;;
+220F;N-ARY PRODUCT;Sm;0;ON;;;;;N;;;;;
+2210;N-ARY COPRODUCT;Sm;0;ON;;;;;N;;;;;
+2211;N-ARY SUMMATION;Sm;0;ON;;;;;Y;;;;;
+2212;MINUS SIGN;Sm;0;ET;;;;;N;;;;;
+2213;MINUS-OR-PLUS SIGN;Sm;0;ET;;;;;N;;;;;
+2214;DOT PLUS;Sm;0;ON;;;;;N;;;;;
+2215;DIVISION SLASH;Sm;0;ON;;;;;Y;;;;;
+2216;SET MINUS;Sm;0;ON;;;;;Y;;;;;
+2217;ASTERISK OPERATOR;Sm;0;ON;;;;;N;;;;;
+2218;RING OPERATOR;Sm;0;ON;;;;;N;;;;;
+2219;BULLET OPERATOR;Sm;0;ON;;;;;N;;;;;
+221A;SQUARE ROOT;Sm;0;ON;;;;;Y;;;;;
+221B;CUBE ROOT;Sm;0;ON;;;;;Y;;;;;
+221C;FOURTH ROOT;Sm;0;ON;;;;;Y;;;;;
+221D;PROPORTIONAL TO;Sm;0;ON;;;;;Y;;;;;
+221E;INFINITY;Sm;0;ON;;;;;N;;;;;
+221F;RIGHT ANGLE;Sm;0;ON;;;;;Y;;;;;
+2220;ANGLE;Sm;0;ON;;;;;Y;;;;;
+2221;MEASURED ANGLE;Sm;0;ON;;;;;Y;;;;;
+2222;SPHERICAL ANGLE;Sm;0;ON;;;;;Y;;;;;
+2223;DIVIDES;Sm;0;ON;;;;;N;;;;;
+2224;DOES NOT DIVIDE;Sm;0;ON;2223 0338;;;;Y;;;;;
+2225;PARALLEL TO;Sm;0;ON;;;;;N;;;;;
+2226;NOT PARALLEL TO;Sm;0;ON;2225 0338;;;;Y;;;;;
+2227;LOGICAL AND;Sm;0;ON;;;;;N;;;;;
+2228;LOGICAL OR;Sm;0;ON;;;;;N;;;;;
+2229;INTERSECTION;Sm;0;ON;;;;;N;;;;;
+222A;UNION;Sm;0;ON;;;;;N;;;;;
+222B;INTEGRAL;Sm;0;ON;;;;;Y;;;;;
+222C;DOUBLE INTEGRAL;Sm;0;ON;<compat> 222B 222B;;;;Y;;;;;
+222D;TRIPLE INTEGRAL;Sm;0;ON;<compat> 222B 222B 222B;;;;Y;;;;;
+222E;CONTOUR INTEGRAL;Sm;0;ON;;;;;Y;;;;;
+222F;SURFACE INTEGRAL;Sm;0;ON;<compat> 222E 222E;;;;Y;;;;;
+2230;VOLUME INTEGRAL;Sm;0;ON;<compat> 222E 222E 222E;;;;Y;;;;;
+2231;CLOCKWISE INTEGRAL;Sm;0;ON;;;;;Y;;;;;
+2232;CLOCKWISE CONTOUR INTEGRAL;Sm;0;ON;;;;;Y;;;;;
+2233;ANTICLOCKWISE CONTOUR INTEGRAL;Sm;0;ON;;;;;Y;;;;;
+2234;THEREFORE;Sm;0;ON;;;;;N;;;;;
+2235;BECAUSE;Sm;0;ON;;;;;N;;;;;
+2236;RATIO;Sm;0;ON;;;;;N;;;;;
+2237;PROPORTION;Sm;0;ON;;;;;N;;;;;
+2238;DOT MINUS;Sm;0;ON;;;;;N;;;;;
+2239;EXCESS;Sm;0;ON;;;;;Y;;;;;
+223A;GEOMETRIC PROPORTION;Sm;0;ON;;;;;N;;;;;
+223B;HOMOTHETIC;Sm;0;ON;;;;;Y;;;;;
+223C;TILDE OPERATOR;Sm;0;ON;;;;;Y;;;;;
+223D;REVERSED TILDE;Sm;0;ON;;;;;Y;;lazy S;;;
+223E;INVERTED LAZY S;Sm;0;ON;;;;;Y;;;;;
+223F;SINE WAVE;Sm;0;ON;;;;;Y;;;;;
+2240;WREATH PRODUCT;Sm;0;ON;;;;;Y;;;;;
+2241;NOT TILDE;Sm;0;ON;223C 0338;;;;Y;;;;;
+2242;MINUS TILDE;Sm;0;ON;;;;;Y;;;;;
+2243;ASYMPTOTICALLY EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2244;NOT ASYMPTOTICALLY EQUAL TO;Sm;0;ON;2243 0338;;;;Y;;;;;
+2245;APPROXIMATELY EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2246;APPROXIMATELY BUT NOT ACTUALLY EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2247;NEITHER APPROXIMATELY NOR ACTUALLY EQUAL TO;Sm;0;ON;2245 0338;;;;Y;;;;;
+2248;ALMOST EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2249;NOT ALMOST EQUAL TO;Sm;0;ON;2248 0338;;;;Y;;;;;
+224A;ALMOST EQUAL OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+224B;TRIPLE TILDE;Sm;0;ON;;;;;Y;;;;;
+224C;ALL EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+224D;EQUIVALENT TO;Sm;0;ON;;;;;N;;;;;
+224E;GEOMETRICALLY EQUIVALENT TO;Sm;0;ON;;;;;N;;;;;
+224F;DIFFERENCE BETWEEN;Sm;0;ON;;;;;N;;;;;
+2250;APPROACHES THE LIMIT;Sm;0;ON;;;;;N;;;;;
+2251;GEOMETRICALLY EQUAL TO;Sm;0;ON;;;;;N;;;;;
+2252;APPROXIMATELY EQUAL TO OR THE IMAGE OF;Sm;0;ON;;;;;Y;;;;;
+2253;IMAGE OF OR APPROXIMATELY EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2254;COLON EQUALS;Sm;0;ON;;;;;Y;COLON EQUAL;;;;
+2255;EQUALS COLON;Sm;0;ON;;;;;Y;EQUAL COLON;;;;
+2256;RING IN EQUAL TO;Sm;0;ON;;;;;N;;;;;
+2257;RING EQUAL TO;Sm;0;ON;;;;;N;;;;;
+2258;CORRESPONDS TO;Sm;0;ON;;;;;N;;;;;
+2259;ESTIMATES;Sm;0;ON;;;;;N;;;;;
+225A;EQUIANGULAR TO;Sm;0;ON;;;;;N;;;;;
+225B;STAR EQUALS;Sm;0;ON;;;;;N;;;;;
+225C;DELTA EQUAL TO;Sm;0;ON;;;;;N;;;;;
+225D;EQUAL TO BY DEFINITION;Sm;0;ON;;;;;N;;;;;
+225E;MEASURED BY;Sm;0;ON;;;;;N;;;;;
+225F;QUESTIONED EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2260;NOT EQUAL TO;Sm;0;ON;003D 0338;;;;Y;;;;;
+2261;IDENTICAL TO;Sm;0;ON;;;;;N;;;;;
+2262;NOT IDENTICAL TO;Sm;0;ON;2261 0338;;;;Y;;;;;
+2263;STRICTLY EQUIVALENT TO;Sm;0;ON;;;;;N;;;;;
+2264;LESS-THAN OR EQUAL TO;Sm;0;ON;;;;;Y;LESS THAN OR EQUAL TO;;;;
+2265;GREATER-THAN OR EQUAL TO;Sm;0;ON;;;;;Y;GREATER THAN OR EQUAL TO;;;;
+2266;LESS-THAN OVER EQUAL TO;Sm;0;ON;;;;;Y;LESS THAN OVER EQUAL TO;;;;
+2267;GREATER-THAN OVER EQUAL TO;Sm;0;ON;;;;;Y;GREATER THAN OVER EQUAL TO;;;;
+2268;LESS-THAN BUT NOT EQUAL TO;Sm;0;ON;;;;;Y;LESS THAN BUT NOT EQUAL TO;;;;
+2269;GREATER-THAN BUT NOT EQUAL TO;Sm;0;ON;;;;;Y;GREATER THAN BUT NOT EQUAL TO;;;;
+226A;MUCH LESS-THAN;Sm;0;ON;;;;;Y;MUCH LESS THAN;;;;
+226B;MUCH GREATER-THAN;Sm;0;ON;;;;;Y;MUCH GREATER THAN;;;;
+226C;BETWEEN;Sm;0;ON;;;;;N;;;;;
+226D;NOT EQUIVALENT TO;Sm;0;ON;224D 0338;;;;N;;;;;
+226E;NOT LESS-THAN;Sm;0;ON;003C 0338;;;;Y;NOT LESS THAN;;;;
+226F;NOT GREATER-THAN;Sm;0;ON;003E 0338;;;;Y;NOT GREATER THAN;;;;
+2270;NEITHER LESS-THAN NOR EQUAL TO;Sm;0;ON;2264 0338;;;;Y;NEITHER LESS THAN NOR EQUAL TO;;;;
+2271;NEITHER GREATER-THAN NOR EQUAL TO;Sm;0;ON;2265 0338;;;;Y;NEITHER GREATER THAN NOR EQUAL TO;;;;
+2272;LESS-THAN OR EQUIVALENT TO;Sm;0;ON;;;;;Y;LESS THAN OR EQUIVALENT TO;;;;
+2273;GREATER-THAN OR EQUIVALENT TO;Sm;0;ON;;;;;Y;GREATER THAN OR EQUIVALENT TO;;;;
+2274;NEITHER LESS-THAN NOR EQUIVALENT TO;Sm;0;ON;2272 0338;;;;Y;NEITHER LESS THAN NOR EQUIVALENT TO;;;;
+2275;NEITHER GREATER-THAN NOR EQUIVALENT TO;Sm;0;ON;2273 0338;;;;Y;NEITHER GREATER THAN NOR EQUIVALENT TO;;;;
+2276;LESS-THAN OR GREATER-THAN;Sm;0;ON;;;;;Y;LESS THAN OR GREATER THAN;;;;
+2277;GREATER-THAN OR LESS-THAN;Sm;0;ON;;;;;Y;GREATER THAN OR LESS THAN;;;;
+2278;NEITHER LESS-THAN NOR GREATER-THAN;Sm;0;ON;2276 0338;;;;Y;NEITHER LESS THAN NOR GREATER THAN;;;;
+2279;NEITHER GREATER-THAN NOR LESS-THAN;Sm;0;ON;2277 0338;;;;Y;NEITHER GREATER THAN NOR LESS THAN;;;;
+227A;PRECEDES;Sm;0;ON;;;;;Y;;;;;
+227B;SUCCEEDS;Sm;0;ON;;;;;Y;;;;;
+227C;PRECEDES OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+227D;SUCCEEDS OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+227E;PRECEDES OR EQUIVALENT TO;Sm;0;ON;;;;;Y;;;;;
+227F;SUCCEEDS OR EQUIVALENT TO;Sm;0;ON;;;;;Y;;;;;
+2280;DOES NOT PRECEDE;Sm;0;ON;227A 0338;;;;Y;;;;;
+2281;DOES NOT SUCCEED;Sm;0;ON;227B 0338;;;;Y;;;;;
+2282;SUBSET OF;Sm;0;ON;;;;;Y;;;;;
+2283;SUPERSET OF;Sm;0;ON;;;;;Y;;;;;
+2284;NOT A SUBSET OF;Sm;0;ON;2282 0338;;;;Y;;;;;
+2285;NOT A SUPERSET OF;Sm;0;ON;2283 0338;;;;Y;;;;;
+2286;SUBSET OF OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2287;SUPERSET OF OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2288;NEITHER A SUBSET OF NOR EQUAL TO;Sm;0;ON;2286 0338;;;;Y;;;;;
+2289;NEITHER A SUPERSET OF NOR EQUAL TO;Sm;0;ON;2287 0338;;;;Y;;;;;
+228A;SUBSET OF WITH NOT EQUAL TO;Sm;0;ON;;;;;Y;SUBSET OF OR NOT EQUAL TO;;;;
+228B;SUPERSET OF WITH NOT EQUAL TO;Sm;0;ON;;;;;Y;SUPERSET OF OR NOT EQUAL TO;;;;
+228C;MULTISET;Sm;0;ON;;;;;Y;;;;;
+228D;MULTISET MULTIPLICATION;Sm;0;ON;;;;;N;;;;;
+228E;MULTISET UNION;Sm;0;ON;;;;;N;;;;;
+228F;SQUARE IMAGE OF;Sm;0;ON;;;;;Y;;;;;
+2290;SQUARE ORIGINAL OF;Sm;0;ON;;;;;Y;;;;;
+2291;SQUARE IMAGE OF OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2292;SQUARE ORIGINAL OF OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2293;SQUARE CAP;Sm;0;ON;;;;;N;;;;;
+2294;SQUARE CUP;Sm;0;ON;;;;;N;;;;;
+2295;CIRCLED PLUS;Sm;0;ON;;;;;N;;;;;
+2296;CIRCLED MINUS;Sm;0;ON;;;;;N;;;;;
+2297;CIRCLED TIMES;Sm;0;ON;;;;;N;;;;;
+2298;CIRCLED DIVISION SLASH;Sm;0;ON;;;;;Y;;;;;
+2299;CIRCLED DOT OPERATOR;Sm;0;ON;;;;;N;;;;;
+229A;CIRCLED RING OPERATOR;Sm;0;ON;;;;;N;;;;;
+229B;CIRCLED ASTERISK OPERATOR;Sm;0;ON;;;;;N;;;;;
+229C;CIRCLED EQUALS;Sm;0;ON;;;;;N;;;;;
+229D;CIRCLED DASH;Sm;0;ON;;;;;N;;;;;
+229E;SQUARED PLUS;Sm;0;ON;;;;;N;;;;;
+229F;SQUARED MINUS;Sm;0;ON;;;;;N;;;;;
+22A0;SQUARED TIMES;Sm;0;ON;;;;;N;;;;;
+22A1;SQUARED DOT OPERATOR;Sm;0;ON;;;;;N;;;;;
+22A2;RIGHT TACK;Sm;0;ON;;;;;Y;;;;;
+22A3;LEFT TACK;Sm;0;ON;;;;;Y;;;;;
+22A4;DOWN TACK;Sm;0;ON;;;;;N;;;;;
+22A5;UP TACK;Sm;0;ON;;;;;N;;;;;
+22A6;ASSERTION;Sm;0;ON;;;;;Y;;;;;
+22A7;MODELS;Sm;0;ON;;;;;Y;;;;;
+22A8;TRUE;Sm;0;ON;;;;;Y;;;;;
+22A9;FORCES;Sm;0;ON;;;;;Y;;;;;
+22AA;TRIPLE VERTICAL BAR RIGHT TURNSTILE;Sm;0;ON;;;;;Y;;;;;
+22AB;DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE;Sm;0;ON;;;;;Y;;;;;
+22AC;DOES NOT PROVE;Sm;0;ON;22A2 0338;;;;Y;;;;;
+22AD;NOT TRUE;Sm;0;ON;22A8 0338;;;;Y;;;;;
+22AE;DOES NOT FORCE;Sm;0;ON;22A9 0338;;;;Y;;;;;
+22AF;NEGATED DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE;Sm;0;ON;22AB 0338;;;;Y;;;;;
+22B0;PRECEDES UNDER RELATION;Sm;0;ON;;;;;Y;;;;;
+22B1;SUCCEEDS UNDER RELATION;Sm;0;ON;;;;;Y;;;;;
+22B2;NORMAL SUBGROUP OF;Sm;0;ON;;;;;Y;;;;;
+22B3;CONTAINS AS NORMAL SUBGROUP;Sm;0;ON;;;;;Y;;;;;
+22B4;NORMAL SUBGROUP OF OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+22B5;CONTAINS AS NORMAL SUBGROUP OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+22B6;ORIGINAL OF;Sm;0;ON;;;;;Y;;;;;
+22B7;IMAGE OF;Sm;0;ON;;;;;Y;;;;;
+22B8;MULTIMAP;Sm;0;ON;;;;;Y;;;;;
+22B9;HERMITIAN CONJUGATE MATRIX;Sm;0;ON;;;;;N;;;;;
+22BA;INTERCALATE;Sm;0;ON;;;;;N;;;;;
+22BB;XOR;Sm;0;ON;;;;;N;;;;;
+22BC;NAND;Sm;0;ON;;;;;N;;;;;
+22BD;NOR;Sm;0;ON;;;;;N;;;;;
+22BE;RIGHT ANGLE WITH ARC;Sm;0;ON;;;;;Y;;;;;
+22BF;RIGHT TRIANGLE;Sm;0;ON;;;;;Y;;;;;
+22C0;N-ARY LOGICAL AND;Sm;0;ON;;;;;N;;;;;
+22C1;N-ARY LOGICAL OR;Sm;0;ON;;;;;N;;;;;
+22C2;N-ARY INTERSECTION;Sm;0;ON;;;;;N;;;;;
+22C3;N-ARY UNION;Sm;0;ON;;;;;N;;;;;
+22C4;DIAMOND OPERATOR;Sm;0;ON;;;;;N;;;;;
+22C5;DOT OPERATOR;Sm;0;ON;;;;;N;;;;;
+22C6;STAR OPERATOR;Sm;0;ON;;;;;N;;;;;
+22C7;DIVISION TIMES;Sm;0;ON;;;;;N;;;;;
+22C8;BOWTIE;Sm;0;ON;;;;;N;;;;;
+22C9;LEFT NORMAL FACTOR SEMIDIRECT PRODUCT;Sm;0;ON;;;;;Y;;;;;
+22CA;RIGHT NORMAL FACTOR SEMIDIRECT PRODUCT;Sm;0;ON;;;;;Y;;;;;
+22CB;LEFT SEMIDIRECT PRODUCT;Sm;0;ON;;;;;Y;;;;;
+22CC;RIGHT SEMIDIRECT PRODUCT;Sm;0;ON;;;;;Y;;;;;
+22CD;REVERSED TILDE EQUALS;Sm;0;ON;;;;;Y;;;;;
+22CE;CURLY LOGICAL OR;Sm;0;ON;;;;;N;;;;;
+22CF;CURLY LOGICAL AND;Sm;0;ON;;;;;N;;;;;
+22D0;DOUBLE SUBSET;Sm;0;ON;;;;;Y;;;;;
+22D1;DOUBLE SUPERSET;Sm;0;ON;;;;;Y;;;;;
+22D2;DOUBLE INTERSECTION;Sm;0;ON;;;;;N;;;;;
+22D3;DOUBLE UNION;Sm;0;ON;;;;;N;;;;;
+22D4;PITCHFORK;Sm;0;ON;;;;;N;;;;;
+22D5;EQUAL AND PARALLEL TO;Sm;0;ON;;;;;N;;;;;
+22D6;LESS-THAN WITH DOT;Sm;0;ON;;;;;Y;LESS THAN WITH DOT;;;;
+22D7;GREATER-THAN WITH DOT;Sm;0;ON;;;;;Y;GREATER THAN WITH DOT;;;;
+22D8;VERY MUCH LESS-THAN;Sm;0;ON;;;;;Y;VERY MUCH LESS THAN;;;;
+22D9;VERY MUCH GREATER-THAN;Sm;0;ON;;;;;Y;VERY MUCH GREATER THAN;;;;
+22DA;LESS-THAN EQUAL TO OR GREATER-THAN;Sm;0;ON;;;;;Y;LESS THAN EQUAL TO OR GREATER THAN;;;;
+22DB;GREATER-THAN EQUAL TO OR LESS-THAN;Sm;0;ON;;;;;Y;GREATER THAN EQUAL TO OR LESS THAN;;;;
+22DC;EQUAL TO OR LESS-THAN;Sm;0;ON;;;;;Y;EQUAL TO OR LESS THAN;;;;
+22DD;EQUAL TO OR GREATER-THAN;Sm;0;ON;;;;;Y;EQUAL TO OR GREATER THAN;;;;
+22DE;EQUAL TO OR PRECEDES;Sm;0;ON;;;;;Y;;;;;
+22DF;EQUAL TO OR SUCCEEDS;Sm;0;ON;;;;;Y;;;;;
+22E0;DOES NOT PRECEDE OR EQUAL;Sm;0;ON;227C 0338;;;;Y;;;;;
+22E1;DOES NOT SUCCEED OR EQUAL;Sm;0;ON;227D 0338;;;;Y;;;;;
+22E2;NOT SQUARE IMAGE OF OR EQUAL TO;Sm;0;ON;2291 0338;;;;Y;;;;;
+22E3;NOT SQUARE ORIGINAL OF OR EQUAL TO;Sm;0;ON;2292 0338;;;;Y;;;;;
+22E4;SQUARE IMAGE OF OR NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+22E5;SQUARE ORIGINAL OF OR NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+22E6;LESS-THAN BUT NOT EQUIVALENT TO;Sm;0;ON;;;;;Y;LESS THAN BUT NOT EQUIVALENT TO;;;;
+22E7;GREATER-THAN BUT NOT EQUIVALENT TO;Sm;0;ON;;;;;Y;GREATER THAN BUT NOT EQUIVALENT TO;;;;
+22E8;PRECEDES BUT NOT EQUIVALENT TO;Sm;0;ON;;;;;Y;;;;;
+22E9;SUCCEEDS BUT NOT EQUIVALENT TO;Sm;0;ON;;;;;Y;;;;;
+22EA;NOT NORMAL SUBGROUP OF;Sm;0;ON;22B2 0338;;;;Y;;;;;
+22EB;DOES NOT CONTAIN AS NORMAL SUBGROUP;Sm;0;ON;22B3 0338;;;;Y;;;;;
+22EC;NOT NORMAL SUBGROUP OF OR EQUAL TO;Sm;0;ON;22B4 0338;;;;Y;;;;;
+22ED;DOES NOT CONTAIN AS NORMAL SUBGROUP OR EQUAL;Sm;0;ON;22B5 0338;;;;Y;;;;;
+22EE;VERTICAL ELLIPSIS;Sm;0;ON;;;;;N;;;;;
+22EF;MIDLINE HORIZONTAL ELLIPSIS;Sm;0;ON;;;;;N;;;;;
+22F0;UP RIGHT DIAGONAL ELLIPSIS;Sm;0;ON;;;;;Y;;;;;
+22F1;DOWN RIGHT DIAGONAL ELLIPSIS;Sm;0;ON;;;;;Y;;;;;
+22F2;ELEMENT OF WITH LONG HORIZONTAL STROKE;Sm;0;ON;;;;;Y;;;;;
+22F3;ELEMENT OF WITH VERTICAL BAR AT END OF HORIZONTAL STROKE;Sm;0;ON;;;;;Y;;;;;
+22F4;SMALL ELEMENT OF WITH VERTICAL BAR AT END OF HORIZONTAL STROKE;Sm;0;ON;;;;;Y;;;;;
+22F5;ELEMENT OF WITH DOT ABOVE;Sm;0;ON;;;;;Y;;;;;
+22F6;ELEMENT OF WITH OVERBAR;Sm;0;ON;;;;;Y;;;;;
+22F7;SMALL ELEMENT OF WITH OVERBAR;Sm;0;ON;;;;;Y;;;;;
+22F8;ELEMENT OF WITH UNDERBAR;Sm;0;ON;;;;;Y;;;;;
+22F9;ELEMENT OF WITH TWO HORIZONTAL STROKES;Sm;0;ON;;;;;Y;;;;;
+22FA;CONTAINS WITH LONG HORIZONTAL STROKE;Sm;0;ON;;;;;Y;;;;;
+22FB;CONTAINS WITH VERTICAL BAR AT END OF HORIZONTAL STROKE;Sm;0;ON;;;;;Y;;;;;
+22FC;SMALL CONTAINS WITH VERTICAL BAR AT END OF HORIZONTAL STROKE;Sm;0;ON;;;;;Y;;;;;
+22FD;CONTAINS WITH OVERBAR;Sm;0;ON;;;;;Y;;;;;
+22FE;SMALL CONTAINS WITH OVERBAR;Sm;0;ON;;;;;Y;;;;;
+22FF;Z NOTATION BAG MEMBERSHIP;Sm;0;ON;;;;;Y;;;;;
+2300;DIAMETER SIGN;So;0;ON;;;;;N;;;;;
+2301;ELECTRIC ARROW;So;0;ON;;;;;N;;;;;
+2302;HOUSE;So;0;ON;;;;;N;;;;;
+2303;UP ARROWHEAD;So;0;ON;;;;;N;;;;;
+2304;DOWN ARROWHEAD;So;0;ON;;;;;N;;;;;
+2305;PROJECTIVE;So;0;ON;;;;;N;;;;;
+2306;PERSPECTIVE;So;0;ON;;;;;N;;;;;
+2307;WAVY LINE;So;0;ON;;;;;N;;;;;
+2308;LEFT CEILING;Sm;0;ON;;;;;Y;;;;;
+2309;RIGHT CEILING;Sm;0;ON;;;;;Y;;;;;
+230A;LEFT FLOOR;Sm;0;ON;;;;;Y;;;;;
+230B;RIGHT FLOOR;Sm;0;ON;;;;;Y;;;;;
+230C;BOTTOM RIGHT CROP;So;0;ON;;;;;N;;;;;
+230D;BOTTOM LEFT CROP;So;0;ON;;;;;N;;;;;
+230E;TOP RIGHT CROP;So;0;ON;;;;;N;;;;;
+230F;TOP LEFT CROP;So;0;ON;;;;;N;;;;;
+2310;REVERSED NOT SIGN;So;0;ON;;;;;N;;;;;
+2311;SQUARE LOZENGE;So;0;ON;;;;;N;;;;;
+2312;ARC;So;0;ON;;;;;N;;;;;
+2313;SEGMENT;So;0;ON;;;;;N;;;;;
+2314;SECTOR;So;0;ON;;;;;N;;;;;
+2315;TELEPHONE RECORDER;So;0;ON;;;;;N;;;;;
+2316;POSITION INDICATOR;So;0;ON;;;;;N;;;;;
+2317;VIEWDATA SQUARE;So;0;ON;;;;;N;;;;;
+2318;PLACE OF INTEREST SIGN;So;0;ON;;;;;N;COMMAND KEY;;;;
+2319;TURNED NOT SIGN;So;0;ON;;;;;N;;;;;
+231A;WATCH;So;0;ON;;;;;N;;;;;
+231B;HOURGLASS;So;0;ON;;;;;N;;;;;
+231C;TOP LEFT CORNER;So;0;ON;;;;;N;;;;;
+231D;TOP RIGHT CORNER;So;0;ON;;;;;N;;;;;
+231E;BOTTOM LEFT CORNER;So;0;ON;;;;;N;;;;;
+231F;BOTTOM RIGHT CORNER;So;0;ON;;;;;N;;;;;
+2320;TOP HALF INTEGRAL;Sm;0;ON;;;;;Y;;;;;
+2321;BOTTOM HALF INTEGRAL;Sm;0;ON;;;;;Y;;;;;
+2322;FROWN;So;0;ON;;;;;N;;;;;
+2323;SMILE;So;0;ON;;;;;N;;;;;
+2324;UP ARROWHEAD BETWEEN TWO HORIZONTAL BARS;So;0;ON;;;;;N;ENTER KEY;;;;
+2325;OPTION KEY;So;0;ON;;;;;N;;;;;
+2326;ERASE TO THE RIGHT;So;0;ON;;;;;N;DELETE TO THE RIGHT KEY;;;;
+2327;X IN A RECTANGLE BOX;So;0;ON;;;;;N;CLEAR KEY;;;;
+2328;KEYBOARD;So;0;ON;;;;;N;;;;;
+2329;LEFT-POINTING ANGLE BRACKET;Ps;0;ON;3008;;;;Y;BRA;;;;
+232A;RIGHT-POINTING ANGLE BRACKET;Pe;0;ON;3009;;;;Y;KET;;;;
+232B;ERASE TO THE LEFT;So;0;ON;;;;;N;DELETE TO THE LEFT KEY;;;;
+232C;BENZENE RING;So;0;ON;;;;;N;;;;;
+232D;CYLINDRICITY;So;0;ON;;;;;N;;;;;
+232E;ALL AROUND-PROFILE;So;0;ON;;;;;N;;;;;
+232F;SYMMETRY;So;0;ON;;;;;N;;;;;
+2330;TOTAL RUNOUT;So;0;ON;;;;;N;;;;;
+2331;DIMENSION ORIGIN;So;0;ON;;;;;N;;;;;
+2332;CONICAL TAPER;So;0;ON;;;;;N;;;;;
+2333;SLOPE;So;0;ON;;;;;N;;;;;
+2334;COUNTERBORE;So;0;ON;;;;;N;;;;;
+2335;COUNTERSINK;So;0;ON;;;;;N;;;;;
+2336;APL FUNCTIONAL SYMBOL I-BEAM;So;0;L;;;;;N;;;;;
+2337;APL FUNCTIONAL SYMBOL SQUISH QUAD;So;0;L;;;;;N;;;;;
+2338;APL FUNCTIONAL SYMBOL QUAD EQUAL;So;0;L;;;;;N;;;;;
+2339;APL FUNCTIONAL SYMBOL QUAD DIVIDE;So;0;L;;;;;N;;;;;
+233A;APL FUNCTIONAL SYMBOL QUAD DIAMOND;So;0;L;;;;;N;;;;;
+233B;APL FUNCTIONAL SYMBOL QUAD JOT;So;0;L;;;;;N;;;;;
+233C;APL FUNCTIONAL SYMBOL QUAD CIRCLE;So;0;L;;;;;N;;;;;
+233D;APL FUNCTIONAL SYMBOL CIRCLE STILE;So;0;L;;;;;N;;;;;
+233E;APL FUNCTIONAL SYMBOL CIRCLE JOT;So;0;L;;;;;N;;;;;
+233F;APL FUNCTIONAL SYMBOL SLASH BAR;So;0;L;;;;;N;;;;;
+2340;APL FUNCTIONAL SYMBOL BACKSLASH BAR;So;0;L;;;;;N;;;;;
+2341;APL FUNCTIONAL SYMBOL QUAD SLASH;So;0;L;;;;;N;;;;;
+2342;APL FUNCTIONAL SYMBOL QUAD BACKSLASH;So;0;L;;;;;N;;;;;
+2343;APL FUNCTIONAL SYMBOL QUAD LESS-THAN;So;0;L;;;;;N;;;;;
+2344;APL FUNCTIONAL SYMBOL QUAD GREATER-THAN;So;0;L;;;;;N;;;;;
+2345;APL FUNCTIONAL SYMBOL LEFTWARDS VANE;So;0;L;;;;;N;;;;;
+2346;APL FUNCTIONAL SYMBOL RIGHTWARDS VANE;So;0;L;;;;;N;;;;;
+2347;APL FUNCTIONAL SYMBOL QUAD LEFTWARDS ARROW;So;0;L;;;;;N;;;;;
+2348;APL FUNCTIONAL SYMBOL QUAD RIGHTWARDS ARROW;So;0;L;;;;;N;;;;;
+2349;APL FUNCTIONAL SYMBOL CIRCLE BACKSLASH;So;0;L;;;;;N;;;;;
+234A;APL FUNCTIONAL SYMBOL DOWN TACK UNDERBAR;So;0;L;;;;;N;;*;;;
+234B;APL FUNCTIONAL SYMBOL DELTA STILE;So;0;L;;;;;N;;;;;
+234C;APL FUNCTIONAL SYMBOL QUAD DOWN CARET;So;0;L;;;;;N;;;;;
+234D;APL FUNCTIONAL SYMBOL QUAD DELTA;So;0;L;;;;;N;;;;;
+234E;APL FUNCTIONAL SYMBOL DOWN TACK JOT;So;0;L;;;;;N;;*;;;
+234F;APL FUNCTIONAL SYMBOL UPWARDS VANE;So;0;L;;;;;N;;;;;
+2350;APL FUNCTIONAL SYMBOL QUAD UPWARDS ARROW;So;0;L;;;;;N;;;;;
+2351;APL FUNCTIONAL SYMBOL UP TACK OVERBAR;So;0;L;;;;;N;;*;;;
+2352;APL FUNCTIONAL SYMBOL DEL STILE;So;0;L;;;;;N;;;;;
+2353;APL FUNCTIONAL SYMBOL QUAD UP CARET;So;0;L;;;;;N;;;;;
+2354;APL FUNCTIONAL SYMBOL QUAD DEL;So;0;L;;;;;N;;;;;
+2355;APL FUNCTIONAL SYMBOL UP TACK JOT;So;0;L;;;;;N;;*;;;
+2356;APL FUNCTIONAL SYMBOL DOWNWARDS VANE;So;0;L;;;;;N;;;;;
+2357;APL FUNCTIONAL SYMBOL QUAD DOWNWARDS ARROW;So;0;L;;;;;N;;;;;
+2358;APL FUNCTIONAL SYMBOL QUOTE UNDERBAR;So;0;L;;;;;N;;;;;
+2359;APL FUNCTIONAL SYMBOL DELTA UNDERBAR;So;0;L;;;;;N;;;;;
+235A;APL FUNCTIONAL SYMBOL DIAMOND UNDERBAR;So;0;L;;;;;N;;;;;
+235B;APL FUNCTIONAL SYMBOL JOT UNDERBAR;So;0;L;;;;;N;;;;;
+235C;APL FUNCTIONAL SYMBOL CIRCLE UNDERBAR;So;0;L;;;;;N;;;;;
+235D;APL FUNCTIONAL SYMBOL UP SHOE JOT;So;0;L;;;;;N;;;;;
+235E;APL FUNCTIONAL SYMBOL QUOTE QUAD;So;0;L;;;;;N;;;;;
+235F;APL FUNCTIONAL SYMBOL CIRCLE STAR;So;0;L;;;;;N;;;;;
+2360;APL FUNCTIONAL SYMBOL QUAD COLON;So;0;L;;;;;N;;;;;
+2361;APL FUNCTIONAL SYMBOL UP TACK DIAERESIS;So;0;L;;;;;N;;*;;;
+2362;APL FUNCTIONAL SYMBOL DEL DIAERESIS;So;0;L;;;;;N;;;;;
+2363;APL FUNCTIONAL SYMBOL STAR DIAERESIS;So;0;L;;;;;N;;;;;
+2364;APL FUNCTIONAL SYMBOL JOT DIAERESIS;So;0;L;;;;;N;;;;;
+2365;APL FUNCTIONAL SYMBOL CIRCLE DIAERESIS;So;0;L;;;;;N;;;;;
+2366;APL FUNCTIONAL SYMBOL DOWN SHOE STILE;So;0;L;;;;;N;;;;;
+2367;APL FUNCTIONAL SYMBOL LEFT SHOE STILE;So;0;L;;;;;N;;;;;
+2368;APL FUNCTIONAL SYMBOL TILDE DIAERESIS;So;0;L;;;;;N;;;;;
+2369;APL FUNCTIONAL SYMBOL GREATER-THAN DIAERESIS;So;0;L;;;;;N;;;;;
+236A;APL FUNCTIONAL SYMBOL COMMA BAR;So;0;L;;;;;N;;;;;
+236B;APL FUNCTIONAL SYMBOL DEL TILDE;So;0;L;;;;;N;;;;;
+236C;APL FUNCTIONAL SYMBOL ZILDE;So;0;L;;;;;N;;;;;
+236D;APL FUNCTIONAL SYMBOL STILE TILDE;So;0;L;;;;;N;;;;;
+236E;APL FUNCTIONAL SYMBOL SEMICOLON UNDERBAR;So;0;L;;;;;N;;;;;
+236F;APL FUNCTIONAL SYMBOL QUAD NOT EQUAL;So;0;L;;;;;N;;;;;
+2370;APL FUNCTIONAL SYMBOL QUAD QUESTION;So;0;L;;;;;N;;;;;
+2371;APL FUNCTIONAL SYMBOL DOWN CARET TILDE;So;0;L;;;;;N;;;;;
+2372;APL FUNCTIONAL SYMBOL UP CARET TILDE;So;0;L;;;;;N;;;;;
+2373;APL FUNCTIONAL SYMBOL IOTA;So;0;L;;;;;N;;;;;
+2374;APL FUNCTIONAL SYMBOL RHO;So;0;L;;;;;N;;;;;
+2375;APL FUNCTIONAL SYMBOL OMEGA;So;0;L;;;;;N;;;;;
+2376;APL FUNCTIONAL SYMBOL ALPHA UNDERBAR;So;0;L;;;;;N;;;;;
+2377;APL FUNCTIONAL SYMBOL EPSILON UNDERBAR;So;0;L;;;;;N;;;;;
+2378;APL FUNCTIONAL SYMBOL IOTA UNDERBAR;So;0;L;;;;;N;;;;;
+2379;APL FUNCTIONAL SYMBOL OMEGA UNDERBAR;So;0;L;;;;;N;;;;;
+237A;APL FUNCTIONAL SYMBOL ALPHA;So;0;L;;;;;N;;;;;
+237B;NOT CHECK MARK;So;0;ON;;;;;N;;;;;
+237C;RIGHT ANGLE WITH DOWNWARDS ZIGZAG ARROW;Sm;0;ON;;;;;N;;;;;
+237D;SHOULDERED OPEN BOX;So;0;ON;;;;;N;;;;;
+237E;BELL SYMBOL;So;0;ON;;;;;N;;;;;
+237F;VERTICAL LINE WITH MIDDLE DOT;So;0;ON;;;;;N;;;;;
+2380;INSERTION SYMBOL;So;0;ON;;;;;N;;;;;
+2381;CONTINUOUS UNDERLINE SYMBOL;So;0;ON;;;;;N;;;;;
+2382;DISCONTINUOUS UNDERLINE SYMBOL;So;0;ON;;;;;N;;;;;
+2383;EMPHASIS SYMBOL;So;0;ON;;;;;N;;;;;
+2384;COMPOSITION SYMBOL;So;0;ON;;;;;N;;;;;
+2385;WHITE SQUARE WITH CENTRE VERTICAL LINE;So;0;ON;;;;;N;;;;;
+2386;ENTER SYMBOL;So;0;ON;;;;;N;;;;;
+2387;ALTERNATIVE KEY SYMBOL;So;0;ON;;;;;N;;;;;
+2388;HELM SYMBOL;So;0;ON;;;;;N;;;;;
+2389;CIRCLED HORIZONTAL BAR WITH NOTCH;So;0;ON;;;;;N;;pause;;;
+238A;CIRCLED TRIANGLE DOWN;So;0;ON;;;;;N;;break;;;
+238B;BROKEN CIRCLE WITH NORTHWEST ARROW;So;0;ON;;;;;N;;escape;;;
+238C;UNDO SYMBOL;So;0;ON;;;;;N;;;;;
+238D;MONOSTABLE SYMBOL;So;0;ON;;;;;N;;;;;
+238E;HYSTERESIS SYMBOL;So;0;ON;;;;;N;;;;;
+238F;OPEN-CIRCUIT-OUTPUT H-TYPE SYMBOL;So;0;ON;;;;;N;;;;;
+2390;OPEN-CIRCUIT-OUTPUT L-TYPE SYMBOL;So;0;ON;;;;;N;;;;;
+2391;PASSIVE-PULL-DOWN-OUTPUT SYMBOL;So;0;ON;;;;;N;;;;;
+2392;PASSIVE-PULL-UP-OUTPUT SYMBOL;So;0;ON;;;;;N;;;;;
+2393;DIRECT CURRENT SYMBOL FORM TWO;So;0;ON;;;;;N;;;;;
+2394;SOFTWARE-FUNCTION SYMBOL;So;0;ON;;;;;N;;;;;
+2395;APL FUNCTIONAL SYMBOL QUAD;So;0;L;;;;;N;;;;;
+2396;DECIMAL SEPARATOR KEY SYMBOL;So;0;ON;;;;;N;;;;;
+2397;PREVIOUS PAGE;So;0;ON;;;;;N;;;;;
+2398;NEXT PAGE;So;0;ON;;;;;N;;;;;
+2399;PRINT SCREEN SYMBOL;So;0;ON;;;;;N;;;;;
+239A;CLEAR SCREEN SYMBOL;So;0;ON;;;;;N;;;;;
+239B;LEFT PARENTHESIS UPPER HOOK;Sm;0;ON;;;;;N;;;;;
+239C;LEFT PARENTHESIS EXTENSION;Sm;0;ON;;;;;N;;;;;
+239D;LEFT PARENTHESIS LOWER HOOK;Sm;0;ON;;;;;N;;;;;
+239E;RIGHT PARENTHESIS UPPER HOOK;Sm;0;ON;;;;;N;;;;;
+239F;RIGHT PARENTHESIS EXTENSION;Sm;0;ON;;;;;N;;;;;
+23A0;RIGHT PARENTHESIS LOWER HOOK;Sm;0;ON;;;;;N;;;;;
+23A1;LEFT SQUARE BRACKET UPPER CORNER;Sm;0;ON;;;;;N;;;;;
+23A2;LEFT SQUARE BRACKET EXTENSION;Sm;0;ON;;;;;N;;;;;
+23A3;LEFT SQUARE BRACKET LOWER CORNER;Sm;0;ON;;;;;N;;;;;
+23A4;RIGHT SQUARE BRACKET UPPER CORNER;Sm;0;ON;;;;;N;;;;;
+23A5;RIGHT SQUARE BRACKET EXTENSION;Sm;0;ON;;;;;N;;;;;
+23A6;RIGHT SQUARE BRACKET LOWER CORNER;Sm;0;ON;;;;;N;;;;;
+23A7;LEFT CURLY BRACKET UPPER HOOK;Sm;0;ON;;;;;N;;;;;
+23A8;LEFT CURLY BRACKET MIDDLE PIECE;Sm;0;ON;;;;;N;;;;;
+23A9;LEFT CURLY BRACKET LOWER HOOK;Sm;0;ON;;;;;N;;;;;
+23AA;CURLY BRACKET EXTENSION;Sm;0;ON;;;;;N;;;;;
+23AB;RIGHT CURLY BRACKET UPPER HOOK;Sm;0;ON;;;;;N;;;;;
+23AC;RIGHT CURLY BRACKET MIDDLE PIECE;Sm;0;ON;;;;;N;;;;;
+23AD;RIGHT CURLY BRACKET LOWER HOOK;Sm;0;ON;;;;;N;;;;;
+23AE;INTEGRAL EXTENSION;Sm;0;ON;;;;;N;;;;;
+23AF;HORIZONTAL LINE EXTENSION;Sm;0;ON;;;;;N;;;;;
+23B0;UPPER LEFT OR LOWER RIGHT CURLY BRACKET SECTION;Sm;0;ON;;;;;N;;;;;
+23B1;UPPER RIGHT OR LOWER LEFT CURLY BRACKET SECTION;Sm;0;ON;;;;;N;;;;;
+23B2;SUMMATION TOP;Sm;0;ON;;;;;N;;;;;
+23B3;SUMMATION BOTTOM;Sm;0;ON;;;;;N;;;;;
+23B4;TOP SQUARE BRACKET;Ps;0;ON;;;;;N;;;;;
+23B5;BOTTOM SQUARE BRACKET;Pe;0;ON;;;;;N;;;;;
+23B6;BOTTOM SQUARE BRACKET OVER TOP SQUARE BRACKET;Po;0;ON;;;;;N;;;;;
+23B7;RADICAL SYMBOL BOTTOM;So;0;ON;;;;;N;;;;;
+23B8;LEFT VERTICAL BOX LINE;So;0;ON;;;;;N;;;;;
+23B9;RIGHT VERTICAL BOX LINE;So;0;ON;;;;;N;;;;;
+23BA;HORIZONTAL SCAN LINE-1;So;0;ON;;;;;N;;;;;
+23BB;HORIZONTAL SCAN LINE-3;So;0;ON;;;;;N;;;;;
+23BC;HORIZONTAL SCAN LINE-7;So;0;ON;;;;;N;;;;;
+23BD;HORIZONTAL SCAN LINE-9;So;0;ON;;;;;N;;;;;
+23BE;DENTISTRY SYMBOL LIGHT VERTICAL AND TOP RIGHT;So;0;ON;;;;;N;;;;;
+23BF;DENTISTRY SYMBOL LIGHT VERTICAL AND BOTTOM RIGHT;So;0;ON;;;;;N;;;;;
+23C0;DENTISTRY SYMBOL LIGHT VERTICAL WITH CIRCLE;So;0;ON;;;;;N;;;;;
+23C1;DENTISTRY SYMBOL LIGHT DOWN AND HORIZONTAL WITH CIRCLE;So;0;ON;;;;;N;;;;;
+23C2;DENTISTRY SYMBOL LIGHT UP AND HORIZONTAL WITH CIRCLE;So;0;ON;;;;;N;;;;;
+23C3;DENTISTRY SYMBOL LIGHT VERTICAL WITH TRIANGLE;So;0;ON;;;;;N;;;;;
+23C4;DENTISTRY SYMBOL LIGHT DOWN AND HORIZONTAL WITH TRIANGLE;So;0;ON;;;;;N;;;;;
+23C5;DENTISTRY SYMBOL LIGHT UP AND HORIZONTAL WITH TRIANGLE;So;0;ON;;;;;N;;;;;
+23C6;DENTISTRY SYMBOL LIGHT VERTICAL AND WAVE;So;0;ON;;;;;N;;;;;
+23C7;DENTISTRY SYMBOL LIGHT DOWN AND HORIZONTAL WITH WAVE;So;0;ON;;;;;N;;;;;
+23C8;DENTISTRY SYMBOL LIGHT UP AND HORIZONTAL WITH WAVE;So;0;ON;;;;;N;;;;;
+23C9;DENTISTRY SYMBOL LIGHT DOWN AND HORIZONTAL;So;0;ON;;;;;N;;;;;
+23CA;DENTISTRY SYMBOL LIGHT UP AND HORIZONTAL;So;0;ON;;;;;N;;;;;
+23CB;DENTISTRY SYMBOL LIGHT VERTICAL AND TOP LEFT;So;0;ON;;;;;N;;;;;
+23CC;DENTISTRY SYMBOL LIGHT VERTICAL AND BOTTOM LEFT;So;0;ON;;;;;N;;;;;
+23CD;SQUARE FOOT;So;0;ON;;;;;N;;;;;
+23CE;RETURN SYMBOL;So;0;ON;;;;;N;;;;;
+23CF;EJECT SYMBOL;So;0;ON;;;;;N;;;;;
+23D0;VERTICAL LINE EXTENSION;So;0;ON;;;;;N;;;;;
+2400;SYMBOL FOR NULL;So;0;ON;;;;;N;GRAPHIC FOR NULL;;;;
+2401;SYMBOL FOR START OF HEADING;So;0;ON;;;;;N;GRAPHIC FOR START OF HEADING;;;;
+2402;SYMBOL FOR START OF TEXT;So;0;ON;;;;;N;GRAPHIC FOR START OF TEXT;;;;
+2403;SYMBOL FOR END OF TEXT;So;0;ON;;;;;N;GRAPHIC FOR END OF TEXT;;;;
+2404;SYMBOL FOR END OF TRANSMISSION;So;0;ON;;;;;N;GRAPHIC FOR END OF TRANSMISSION;;;;
+2405;SYMBOL FOR ENQUIRY;So;0;ON;;;;;N;GRAPHIC FOR ENQUIRY;;;;
+2406;SYMBOL FOR ACKNOWLEDGE;So;0;ON;;;;;N;GRAPHIC FOR ACKNOWLEDGE;;;;
+2407;SYMBOL FOR BELL;So;0;ON;;;;;N;GRAPHIC FOR BELL;;;;
+2408;SYMBOL FOR BACKSPACE;So;0;ON;;;;;N;GRAPHIC FOR BACKSPACE;;;;
+2409;SYMBOL FOR HORIZONTAL TABULATION;So;0;ON;;;;;N;GRAPHIC FOR HORIZONTAL TABULATION;;;;
+240A;SYMBOL FOR LINE FEED;So;0;ON;;;;;N;GRAPHIC FOR LINE FEED;;;;
+240B;SYMBOL FOR VERTICAL TABULATION;So;0;ON;;;;;N;GRAPHIC FOR VERTICAL TABULATION;;;;
+240C;SYMBOL FOR FORM FEED;So;0;ON;;;;;N;GRAPHIC FOR FORM FEED;;;;
+240D;SYMBOL FOR CARRIAGE RETURN;So;0;ON;;;;;N;GRAPHIC FOR CARRIAGE RETURN;;;;
+240E;SYMBOL FOR SHIFT OUT;So;0;ON;;;;;N;GRAPHIC FOR SHIFT OUT;;;;
+240F;SYMBOL FOR SHIFT IN;So;0;ON;;;;;N;GRAPHIC FOR SHIFT IN;;;;
+2410;SYMBOL FOR DATA LINK ESCAPE;So;0;ON;;;;;N;GRAPHIC FOR DATA LINK ESCAPE;;;;
+2411;SYMBOL FOR DEVICE CONTROL ONE;So;0;ON;;;;;N;GRAPHIC FOR DEVICE CONTROL ONE;;;;
+2412;SYMBOL FOR DEVICE CONTROL TWO;So;0;ON;;;;;N;GRAPHIC FOR DEVICE CONTROL TWO;;;;
+2413;SYMBOL FOR DEVICE CONTROL THREE;So;0;ON;;;;;N;GRAPHIC FOR DEVICE CONTROL THREE;;;;
+2414;SYMBOL FOR DEVICE CONTROL FOUR;So;0;ON;;;;;N;GRAPHIC FOR DEVICE CONTROL FOUR;;;;
+2415;SYMBOL FOR NEGATIVE ACKNOWLEDGE;So;0;ON;;;;;N;GRAPHIC FOR NEGATIVE ACKNOWLEDGE;;;;
+2416;SYMBOL FOR SYNCHRONOUS IDLE;So;0;ON;;;;;N;GRAPHIC FOR SYNCHRONOUS IDLE;;;;
+2417;SYMBOL FOR END OF TRANSMISSION BLOCK;So;0;ON;;;;;N;GRAPHIC FOR END OF TRANSMISSION BLOCK;;;;
+2418;SYMBOL FOR CANCEL;So;0;ON;;;;;N;GRAPHIC FOR CANCEL;;;;
+2419;SYMBOL FOR END OF MEDIUM;So;0;ON;;;;;N;GRAPHIC FOR END OF MEDIUM;;;;
+241A;SYMBOL FOR SUBSTITUTE;So;0;ON;;;;;N;GRAPHIC FOR SUBSTITUTE;;;;
+241B;SYMBOL FOR ESCAPE;So;0;ON;;;;;N;GRAPHIC FOR ESCAPE;;;;
+241C;SYMBOL FOR FILE SEPARATOR;So;0;ON;;;;;N;GRAPHIC FOR FILE SEPARATOR;;;;
+241D;SYMBOL FOR GROUP SEPARATOR;So;0;ON;;;;;N;GRAPHIC FOR GROUP SEPARATOR;;;;
+241E;SYMBOL FOR RECORD SEPARATOR;So;0;ON;;;;;N;GRAPHIC FOR RECORD SEPARATOR;;;;
+241F;SYMBOL FOR UNIT SEPARATOR;So;0;ON;;;;;N;GRAPHIC FOR UNIT SEPARATOR;;;;
+2420;SYMBOL FOR SPACE;So;0;ON;;;;;N;GRAPHIC FOR SPACE;;;;
+2421;SYMBOL FOR DELETE;So;0;ON;;;;;N;GRAPHIC FOR DELETE;;;;
+2422;BLANK SYMBOL;So;0;ON;;;;;N;BLANK;;;;
+2423;OPEN BOX;So;0;ON;;;;;N;;;;;
+2424;SYMBOL FOR NEWLINE;So;0;ON;;;;;N;GRAPHIC FOR NEWLINE;;;;
+2425;SYMBOL FOR DELETE FORM TWO;So;0;ON;;;;;N;;;;;
+2426;SYMBOL FOR SUBSTITUTE FORM TWO;So;0;ON;;;;;N;;;;;
+2440;OCR HOOK;So;0;ON;;;;;N;;;;;
+2441;OCR CHAIR;So;0;ON;;;;;N;;;;;
+2442;OCR FORK;So;0;ON;;;;;N;;;;;
+2443;OCR INVERTED FORK;So;0;ON;;;;;N;;;;;
+2444;OCR BELT BUCKLE;So;0;ON;;;;;N;;;;;
+2445;OCR BOW TIE;So;0;ON;;;;;N;;;;;
+2446;OCR BRANCH BANK IDENTIFICATION;So;0;ON;;;;;N;;;;;
+2447;OCR AMOUNT OF CHECK;So;0;ON;;;;;N;;;;;
+2448;OCR DASH;So;0;ON;;;;;N;;;;;
+2449;OCR CUSTOMER ACCOUNT NUMBER;So;0;ON;;;;;N;;;;;
+244A;OCR DOUBLE BACKSLASH;So;0;ON;;;;;N;;;;;
+2460;CIRCLED DIGIT ONE;No;0;ON;<circle> 0031;;1;1;N;;;;;
+2461;CIRCLED DIGIT TWO;No;0;ON;<circle> 0032;;2;2;N;;;;;
+2462;CIRCLED DIGIT THREE;No;0;ON;<circle> 0033;;3;3;N;;;;;
+2463;CIRCLED DIGIT FOUR;No;0;ON;<circle> 0034;;4;4;N;;;;;
+2464;CIRCLED DIGIT FIVE;No;0;ON;<circle> 0035;;5;5;N;;;;;
+2465;CIRCLED DIGIT SIX;No;0;ON;<circle> 0036;;6;6;N;;;;;
+2466;CIRCLED DIGIT SEVEN;No;0;ON;<circle> 0037;;7;7;N;;;;;
+2467;CIRCLED DIGIT EIGHT;No;0;ON;<circle> 0038;;8;8;N;;;;;
+2468;CIRCLED DIGIT NINE;No;0;ON;<circle> 0039;;9;9;N;;;;;
+2469;CIRCLED NUMBER TEN;No;0;ON;<circle> 0031 0030;;;10;N;;;;;
+246A;CIRCLED NUMBER ELEVEN;No;0;ON;<circle> 0031 0031;;;11;N;;;;;
+246B;CIRCLED NUMBER TWELVE;No;0;ON;<circle> 0031 0032;;;12;N;;;;;
+246C;CIRCLED NUMBER THIRTEEN;No;0;ON;<circle> 0031 0033;;;13;N;;;;;
+246D;CIRCLED NUMBER FOURTEEN;No;0;ON;<circle> 0031 0034;;;14;N;;;;;
+246E;CIRCLED NUMBER FIFTEEN;No;0;ON;<circle> 0031 0035;;;15;N;;;;;
+246F;CIRCLED NUMBER SIXTEEN;No;0;ON;<circle> 0031 0036;;;16;N;;;;;
+2470;CIRCLED NUMBER SEVENTEEN;No;0;ON;<circle> 0031 0037;;;17;N;;;;;
+2471;CIRCLED NUMBER EIGHTEEN;No;0;ON;<circle> 0031 0038;;;18;N;;;;;
+2472;CIRCLED NUMBER NINETEEN;No;0;ON;<circle> 0031 0039;;;19;N;;;;;
+2473;CIRCLED NUMBER TWENTY;No;0;ON;<circle> 0032 0030;;;20;N;;;;;
+2474;PARENTHESIZED DIGIT ONE;No;0;ON;<compat> 0028 0031 0029;;1;1;N;;;;;
+2475;PARENTHESIZED DIGIT TWO;No;0;ON;<compat> 0028 0032 0029;;2;2;N;;;;;
+2476;PARENTHESIZED DIGIT THREE;No;0;ON;<compat> 0028 0033 0029;;3;3;N;;;;;
+2477;PARENTHESIZED DIGIT FOUR;No;0;ON;<compat> 0028 0034 0029;;4;4;N;;;;;
+2478;PARENTHESIZED DIGIT FIVE;No;0;ON;<compat> 0028 0035 0029;;5;5;N;;;;;
+2479;PARENTHESIZED DIGIT SIX;No;0;ON;<compat> 0028 0036 0029;;6;6;N;;;;;
+247A;PARENTHESIZED DIGIT SEVEN;No;0;ON;<compat> 0028 0037 0029;;7;7;N;;;;;
+247B;PARENTHESIZED DIGIT EIGHT;No;0;ON;<compat> 0028 0038 0029;;8;8;N;;;;;
+247C;PARENTHESIZED DIGIT NINE;No;0;ON;<compat> 0028 0039 0029;;9;9;N;;;;;
+247D;PARENTHESIZED NUMBER TEN;No;0;ON;<compat> 0028 0031 0030 0029;;;10;N;;;;;
+247E;PARENTHESIZED NUMBER ELEVEN;No;0;ON;<compat> 0028 0031 0031 0029;;;11;N;;;;;
+247F;PARENTHESIZED NUMBER TWELVE;No;0;ON;<compat> 0028 0031 0032 0029;;;12;N;;;;;
+2480;PARENTHESIZED NUMBER THIRTEEN;No;0;ON;<compat> 0028 0031 0033 0029;;;13;N;;;;;
+2481;PARENTHESIZED NUMBER FOURTEEN;No;0;ON;<compat> 0028 0031 0034 0029;;;14;N;;;;;
+2482;PARENTHESIZED NUMBER FIFTEEN;No;0;ON;<compat> 0028 0031 0035 0029;;;15;N;;;;;
+2483;PARENTHESIZED NUMBER SIXTEEN;No;0;ON;<compat> 0028 0031 0036 0029;;;16;N;;;;;
+2484;PARENTHESIZED NUMBER SEVENTEEN;No;0;ON;<compat> 0028 0031 0037 0029;;;17;N;;;;;
+2485;PARENTHESIZED NUMBER EIGHTEEN;No;0;ON;<compat> 0028 0031 0038 0029;;;18;N;;;;;
+2486;PARENTHESIZED NUMBER NINETEEN;No;0;ON;<compat> 0028 0031 0039 0029;;;19;N;;;;;
+2487;PARENTHESIZED NUMBER TWENTY;No;0;ON;<compat> 0028 0032 0030 0029;;;20;N;;;;;
+2488;DIGIT ONE FULL STOP;No;0;EN;<compat> 0031 002E;;1;1;N;DIGIT ONE PERIOD;;;;
+2489;DIGIT TWO FULL STOP;No;0;EN;<compat> 0032 002E;;2;2;N;DIGIT TWO PERIOD;;;;
+248A;DIGIT THREE FULL STOP;No;0;EN;<compat> 0033 002E;;3;3;N;DIGIT THREE PERIOD;;;;
+248B;DIGIT FOUR FULL STOP;No;0;EN;<compat> 0034 002E;;4;4;N;DIGIT FOUR PERIOD;;;;
+248C;DIGIT FIVE FULL STOP;No;0;EN;<compat> 0035 002E;;5;5;N;DIGIT FIVE PERIOD;;;;
+248D;DIGIT SIX FULL STOP;No;0;EN;<compat> 0036 002E;;6;6;N;DIGIT SIX PERIOD;;;;
+248E;DIGIT SEVEN FULL STOP;No;0;EN;<compat> 0037 002E;;7;7;N;DIGIT SEVEN PERIOD;;;;
+248F;DIGIT EIGHT FULL STOP;No;0;EN;<compat> 0038 002E;;8;8;N;DIGIT EIGHT PERIOD;;;;
+2490;DIGIT NINE FULL STOP;No;0;EN;<compat> 0039 002E;;9;9;N;DIGIT NINE PERIOD;;;;
+2491;NUMBER TEN FULL STOP;No;0;EN;<compat> 0031 0030 002E;;;10;N;NUMBER TEN PERIOD;;;;
+2492;NUMBER ELEVEN FULL STOP;No;0;EN;<compat> 0031 0031 002E;;;11;N;NUMBER ELEVEN PERIOD;;;;
+2493;NUMBER TWELVE FULL STOP;No;0;EN;<compat> 0031 0032 002E;;;12;N;NUMBER TWELVE PERIOD;;;;
+2494;NUMBER THIRTEEN FULL STOP;No;0;EN;<compat> 0031 0033 002E;;;13;N;NUMBER THIRTEEN PERIOD;;;;
+2495;NUMBER FOURTEEN FULL STOP;No;0;EN;<compat> 0031 0034 002E;;;14;N;NUMBER FOURTEEN PERIOD;;;;
+2496;NUMBER FIFTEEN FULL STOP;No;0;EN;<compat> 0031 0035 002E;;;15;N;NUMBER FIFTEEN PERIOD;;;;
+2497;NUMBER SIXTEEN FULL STOP;No;0;EN;<compat> 0031 0036 002E;;;16;N;NUMBER SIXTEEN PERIOD;;;;
+2498;NUMBER SEVENTEEN FULL STOP;No;0;EN;<compat> 0031 0037 002E;;;17;N;NUMBER SEVENTEEN PERIOD;;;;
+2499;NUMBER EIGHTEEN FULL STOP;No;0;EN;<compat> 0031 0038 002E;;;18;N;NUMBER EIGHTEEN PERIOD;;;;
+249A;NUMBER NINETEEN FULL STOP;No;0;EN;<compat> 0031 0039 002E;;;19;N;NUMBER NINETEEN PERIOD;;;;
+249B;NUMBER TWENTY FULL STOP;No;0;EN;<compat> 0032 0030 002E;;;20;N;NUMBER TWENTY PERIOD;;;;
+249C;PARENTHESIZED LATIN SMALL LETTER A;So;0;L;<compat> 0028 0061 0029;;;;N;;;;;
+249D;PARENTHESIZED LATIN SMALL LETTER B;So;0;L;<compat> 0028 0062 0029;;;;N;;;;;
+249E;PARENTHESIZED LATIN SMALL LETTER C;So;0;L;<compat> 0028 0063 0029;;;;N;;;;;
+249F;PARENTHESIZED LATIN SMALL LETTER D;So;0;L;<compat> 0028 0064 0029;;;;N;;;;;
+24A0;PARENTHESIZED LATIN SMALL LETTER E;So;0;L;<compat> 0028 0065 0029;;;;N;;;;;
+24A1;PARENTHESIZED LATIN SMALL LETTER F;So;0;L;<compat> 0028 0066 0029;;;;N;;;;;
+24A2;PARENTHESIZED LATIN SMALL LETTER G;So;0;L;<compat> 0028 0067 0029;;;;N;;;;;
+24A3;PARENTHESIZED LATIN SMALL LETTER H;So;0;L;<compat> 0028 0068 0029;;;;N;;;;;
+24A4;PARENTHESIZED LATIN SMALL LETTER I;So;0;L;<compat> 0028 0069 0029;;;;N;;;;;
+24A5;PARENTHESIZED LATIN SMALL LETTER J;So;0;L;<compat> 0028 006A 0029;;;;N;;;;;
+24A6;PARENTHESIZED LATIN SMALL LETTER K;So;0;L;<compat> 0028 006B 0029;;;;N;;;;;
+24A7;PARENTHESIZED LATIN SMALL LETTER L;So;0;L;<compat> 0028 006C 0029;;;;N;;;;;
+24A8;PARENTHESIZED LATIN SMALL LETTER M;So;0;L;<compat> 0028 006D 0029;;;;N;;;;;
+24A9;PARENTHESIZED LATIN SMALL LETTER N;So;0;L;<compat> 0028 006E 0029;;;;N;;;;;
+24AA;PARENTHESIZED LATIN SMALL LETTER O;So;0;L;<compat> 0028 006F 0029;;;;N;;;;;
+24AB;PARENTHESIZED LATIN SMALL LETTER P;So;0;L;<compat> 0028 0070 0029;;;;N;;;;;
+24AC;PARENTHESIZED LATIN SMALL LETTER Q;So;0;L;<compat> 0028 0071 0029;;;;N;;;;;
+24AD;PARENTHESIZED LATIN SMALL LETTER R;So;0;L;<compat> 0028 0072 0029;;;;N;;;;;
+24AE;PARENTHESIZED LATIN SMALL LETTER S;So;0;L;<compat> 0028 0073 0029;;;;N;;;;;
+24AF;PARENTHESIZED LATIN SMALL LETTER T;So;0;L;<compat> 0028 0074 0029;;;;N;;;;;
+24B0;PARENTHESIZED LATIN SMALL LETTER U;So;0;L;<compat> 0028 0075 0029;;;;N;;;;;
+24B1;PARENTHESIZED LATIN SMALL LETTER V;So;0;L;<compat> 0028 0076 0029;;;;N;;;;;
+24B2;PARENTHESIZED LATIN SMALL LETTER W;So;0;L;<compat> 0028 0077 0029;;;;N;;;;;
+24B3;PARENTHESIZED LATIN SMALL LETTER X;So;0;L;<compat> 0028 0078 0029;;;;N;;;;;
+24B4;PARENTHESIZED LATIN SMALL LETTER Y;So;0;L;<compat> 0028 0079 0029;;;;N;;;;;
+24B5;PARENTHESIZED LATIN SMALL LETTER Z;So;0;L;<compat> 0028 007A 0029;;;;N;;;;;
+24B6;CIRCLED LATIN CAPITAL LETTER A;So;0;L;<circle> 0041;;;;N;;;;24D0;
+24B7;CIRCLED LATIN CAPITAL LETTER B;So;0;L;<circle> 0042;;;;N;;;;24D1;
+24B8;CIRCLED LATIN CAPITAL LETTER C;So;0;L;<circle> 0043;;;;N;;;;24D2;
+24B9;CIRCLED LATIN CAPITAL LETTER D;So;0;L;<circle> 0044;;;;N;;;;24D3;
+24BA;CIRCLED LATIN CAPITAL LETTER E;So;0;L;<circle> 0045;;;;N;;;;24D4;
+24BB;CIRCLED LATIN CAPITAL LETTER F;So;0;L;<circle> 0046;;;;N;;;;24D5;
+24BC;CIRCLED LATIN CAPITAL LETTER G;So;0;L;<circle> 0047;;;;N;;;;24D6;
+24BD;CIRCLED LATIN CAPITAL LETTER H;So;0;L;<circle> 0048;;;;N;;;;24D7;
+24BE;CIRCLED LATIN CAPITAL LETTER I;So;0;L;<circle> 0049;;;;N;;;;24D8;
+24BF;CIRCLED LATIN CAPITAL LETTER J;So;0;L;<circle> 004A;;;;N;;;;24D9;
+24C0;CIRCLED LATIN CAPITAL LETTER K;So;0;L;<circle> 004B;;;;N;;;;24DA;
+24C1;CIRCLED LATIN CAPITAL LETTER L;So;0;L;<circle> 004C;;;;N;;;;24DB;
+24C2;CIRCLED LATIN CAPITAL LETTER M;So;0;L;<circle> 004D;;;;N;;;;24DC;
+24C3;CIRCLED LATIN CAPITAL LETTER N;So;0;L;<circle> 004E;;;;N;;;;24DD;
+24C4;CIRCLED LATIN CAPITAL LETTER O;So;0;L;<circle> 004F;;;;N;;;;24DE;
+24C5;CIRCLED LATIN CAPITAL LETTER P;So;0;L;<circle> 0050;;;;N;;;;24DF;
+24C6;CIRCLED LATIN CAPITAL LETTER Q;So;0;L;<circle> 0051;;;;N;;;;24E0;
+24C7;CIRCLED LATIN CAPITAL LETTER R;So;0;L;<circle> 0052;;;;N;;;;24E1;
+24C8;CIRCLED LATIN CAPITAL LETTER S;So;0;L;<circle> 0053;;;;N;;;;24E2;
+24C9;CIRCLED LATIN CAPITAL LETTER T;So;0;L;<circle> 0054;;;;N;;;;24E3;
+24CA;CIRCLED LATIN CAPITAL LETTER U;So;0;L;<circle> 0055;;;;N;;;;24E4;
+24CB;CIRCLED LATIN CAPITAL LETTER V;So;0;L;<circle> 0056;;;;N;;;;24E5;
+24CC;CIRCLED LATIN CAPITAL LETTER W;So;0;L;<circle> 0057;;;;N;;;;24E6;
+24CD;CIRCLED LATIN CAPITAL LETTER X;So;0;L;<circle> 0058;;;;N;;;;24E7;
+24CE;CIRCLED LATIN CAPITAL LETTER Y;So;0;L;<circle> 0059;;;;N;;;;24E8;
+24CF;CIRCLED LATIN CAPITAL LETTER Z;So;0;L;<circle> 005A;;;;N;;;;24E9;
+24D0;CIRCLED LATIN SMALL LETTER A;So;0;L;<circle> 0061;;;;N;;;24B6;;24B6
+24D1;CIRCLED LATIN SMALL LETTER B;So;0;L;<circle> 0062;;;;N;;;24B7;;24B7
+24D2;CIRCLED LATIN SMALL LETTER C;So;0;L;<circle> 0063;;;;N;;;24B8;;24B8
+24D3;CIRCLED LATIN SMALL LETTER D;So;0;L;<circle> 0064;;;;N;;;24B9;;24B9
+24D4;CIRCLED LATIN SMALL LETTER E;So;0;L;<circle> 0065;;;;N;;;24BA;;24BA
+24D5;CIRCLED LATIN SMALL LETTER F;So;0;L;<circle> 0066;;;;N;;;24BB;;24BB
+24D6;CIRCLED LATIN SMALL LETTER G;So;0;L;<circle> 0067;;;;N;;;24BC;;24BC
+24D7;CIRCLED LATIN SMALL LETTER H;So;0;L;<circle> 0068;;;;N;;;24BD;;24BD
+24D8;CIRCLED LATIN SMALL LETTER I;So;0;L;<circle> 0069;;;;N;;;24BE;;24BE
+24D9;CIRCLED LATIN SMALL LETTER J;So;0;L;<circle> 006A;;;;N;;;24BF;;24BF
+24DA;CIRCLED LATIN SMALL LETTER K;So;0;L;<circle> 006B;;;;N;;;24C0;;24C0
+24DB;CIRCLED LATIN SMALL LETTER L;So;0;L;<circle> 006C;;;;N;;;24C1;;24C1
+24DC;CIRCLED LATIN SMALL LETTER M;So;0;L;<circle> 006D;;;;N;;;24C2;;24C2
+24DD;CIRCLED LATIN SMALL LETTER N;So;0;L;<circle> 006E;;;;N;;;24C3;;24C3
+24DE;CIRCLED LATIN SMALL LETTER O;So;0;L;<circle> 006F;;;;N;;;24C4;;24C4
+24DF;CIRCLED LATIN SMALL LETTER P;So;0;L;<circle> 0070;;;;N;;;24C5;;24C5
+24E0;CIRCLED LATIN SMALL LETTER Q;So;0;L;<circle> 0071;;;;N;;;24C6;;24C6
+24E1;CIRCLED LATIN SMALL LETTER R;So;0;L;<circle> 0072;;;;N;;;24C7;;24C7
+24E2;CIRCLED LATIN SMALL LETTER S;So;0;L;<circle> 0073;;;;N;;;24C8;;24C8
+24E3;CIRCLED LATIN SMALL LETTER T;So;0;L;<circle> 0074;;;;N;;;24C9;;24C9
+24E4;CIRCLED LATIN SMALL LETTER U;So;0;L;<circle> 0075;;;;N;;;24CA;;24CA
+24E5;CIRCLED LATIN SMALL LETTER V;So;0;L;<circle> 0076;;;;N;;;24CB;;24CB
+24E6;CIRCLED LATIN SMALL LETTER W;So;0;L;<circle> 0077;;;;N;;;24CC;;24CC
+24E7;CIRCLED LATIN SMALL LETTER X;So;0;L;<circle> 0078;;;;N;;;24CD;;24CD
+24E8;CIRCLED LATIN SMALL LETTER Y;So;0;L;<circle> 0079;;;;N;;;24CE;;24CE
+24E9;CIRCLED LATIN SMALL LETTER Z;So;0;L;<circle> 007A;;;;N;;;24CF;;24CF
+24EA;CIRCLED DIGIT ZERO;No;0;ON;<circle> 0030;;0;0;N;;;;;
+24EB;NEGATIVE CIRCLED NUMBER ELEVEN;No;0;ON;;;;11;N;;;;;
+24EC;NEGATIVE CIRCLED NUMBER TWELVE;No;0;ON;;;;12;N;;;;;
+24ED;NEGATIVE CIRCLED NUMBER THIRTEEN;No;0;ON;;;;13;N;;;;;
+24EE;NEGATIVE CIRCLED NUMBER FOURTEEN;No;0;ON;;;;14;N;;;;;
+24EF;NEGATIVE CIRCLED NUMBER FIFTEEN;No;0;ON;;;;15;N;;;;;
+24F0;NEGATIVE CIRCLED NUMBER SIXTEEN;No;0;ON;;;;16;N;;;;;
+24F1;NEGATIVE CIRCLED NUMBER SEVENTEEN;No;0;ON;;;;17;N;;;;;
+24F2;NEGATIVE CIRCLED NUMBER EIGHTEEN;No;0;ON;;;;18;N;;;;;
+24F3;NEGATIVE CIRCLED NUMBER NINETEEN;No;0;ON;;;;19;N;;;;;
+24F4;NEGATIVE CIRCLED NUMBER TWENTY;No;0;ON;;;;20;N;;;;;
+24F5;DOUBLE CIRCLED DIGIT ONE;No;0;ON;;;1;1;N;;;;;
+24F6;DOUBLE CIRCLED DIGIT TWO;No;0;ON;;;2;2;N;;;;;
+24F7;DOUBLE CIRCLED DIGIT THREE;No;0;ON;;;3;3;N;;;;;
+24F8;DOUBLE CIRCLED DIGIT FOUR;No;0;ON;;;4;4;N;;;;;
+24F9;DOUBLE CIRCLED DIGIT FIVE;No;0;ON;;;5;5;N;;;;;
+24FA;DOUBLE CIRCLED DIGIT SIX;No;0;ON;;;6;6;N;;;;;
+24FB;DOUBLE CIRCLED DIGIT SEVEN;No;0;ON;;;7;7;N;;;;;
+24FC;DOUBLE CIRCLED DIGIT EIGHT;No;0;ON;;;8;8;N;;;;;
+24FD;DOUBLE CIRCLED DIGIT NINE;No;0;ON;;;9;9;N;;;;;
+24FE;DOUBLE CIRCLED NUMBER TEN;No;0;ON;;;;10;N;;;;;
+24FF;NEGATIVE CIRCLED DIGIT ZERO;No;0;ON;;;0;0;N;;;;;
+2500;BOX DRAWINGS LIGHT HORIZONTAL;So;0;ON;;;;;N;FORMS LIGHT HORIZONTAL;;;;
+2501;BOX DRAWINGS HEAVY HORIZONTAL;So;0;ON;;;;;N;FORMS HEAVY HORIZONTAL;;;;
+2502;BOX DRAWINGS LIGHT VERTICAL;So;0;ON;;;;;N;FORMS LIGHT VERTICAL;;;;
+2503;BOX DRAWINGS HEAVY VERTICAL;So;0;ON;;;;;N;FORMS HEAVY VERTICAL;;;;
+2504;BOX DRAWINGS LIGHT TRIPLE DASH HORIZONTAL;So;0;ON;;;;;N;FORMS LIGHT TRIPLE DASH HORIZONTAL;;;;
+2505;BOX DRAWINGS HEAVY TRIPLE DASH HORIZONTAL;So;0;ON;;;;;N;FORMS HEAVY TRIPLE DASH HORIZONTAL;;;;
+2506;BOX DRAWINGS LIGHT TRIPLE DASH VERTICAL;So;0;ON;;;;;N;FORMS LIGHT TRIPLE DASH VERTICAL;;;;
+2507;BOX DRAWINGS HEAVY TRIPLE DASH VERTICAL;So;0;ON;;;;;N;FORMS HEAVY TRIPLE DASH VERTICAL;;;;
+2508;BOX DRAWINGS LIGHT QUADRUPLE DASH HORIZONTAL;So;0;ON;;;;;N;FORMS LIGHT QUADRUPLE DASH HORIZONTAL;;;;
+2509;BOX DRAWINGS HEAVY QUADRUPLE DASH HORIZONTAL;So;0;ON;;;;;N;FORMS HEAVY QUADRUPLE DASH HORIZONTAL;;;;
+250A;BOX DRAWINGS LIGHT QUADRUPLE DASH VERTICAL;So;0;ON;;;;;N;FORMS LIGHT QUADRUPLE DASH VERTICAL;;;;
+250B;BOX DRAWINGS HEAVY QUADRUPLE DASH VERTICAL;So;0;ON;;;;;N;FORMS HEAVY QUADRUPLE DASH VERTICAL;;;;
+250C;BOX DRAWINGS LIGHT DOWN AND RIGHT;So;0;ON;;;;;N;FORMS LIGHT DOWN AND RIGHT;;;;
+250D;BOX DRAWINGS DOWN LIGHT AND RIGHT HEAVY;So;0;ON;;;;;N;FORMS DOWN LIGHT AND RIGHT HEAVY;;;;
+250E;BOX DRAWINGS DOWN HEAVY AND RIGHT LIGHT;So;0;ON;;;;;N;FORMS DOWN HEAVY AND RIGHT LIGHT;;;;
+250F;BOX DRAWINGS HEAVY DOWN AND RIGHT;So;0;ON;;;;;N;FORMS HEAVY DOWN AND RIGHT;;;;
+2510;BOX DRAWINGS LIGHT DOWN AND LEFT;So;0;ON;;;;;N;FORMS LIGHT DOWN AND LEFT;;;;
+2511;BOX DRAWINGS DOWN LIGHT AND LEFT HEAVY;So;0;ON;;;;;N;FORMS DOWN LIGHT AND LEFT HEAVY;;;;
+2512;BOX DRAWINGS DOWN HEAVY AND LEFT LIGHT;So;0;ON;;;;;N;FORMS DOWN HEAVY AND LEFT LIGHT;;;;
+2513;BOX DRAWINGS HEAVY DOWN AND LEFT;So;0;ON;;;;;N;FORMS HEAVY DOWN AND LEFT;;;;
+2514;BOX DRAWINGS LIGHT UP AND RIGHT;So;0;ON;;;;;N;FORMS LIGHT UP AND RIGHT;;;;
+2515;BOX DRAWINGS UP LIGHT AND RIGHT HEAVY;So;0;ON;;;;;N;FORMS UP LIGHT AND RIGHT HEAVY;;;;
+2516;BOX DRAWINGS UP HEAVY AND RIGHT LIGHT;So;0;ON;;;;;N;FORMS UP HEAVY AND RIGHT LIGHT;;;;
+2517;BOX DRAWINGS HEAVY UP AND RIGHT;So;0;ON;;;;;N;FORMS HEAVY UP AND RIGHT;;;;
+2518;BOX DRAWINGS LIGHT UP AND LEFT;So;0;ON;;;;;N;FORMS LIGHT UP AND LEFT;;;;
+2519;BOX DRAWINGS UP LIGHT AND LEFT HEAVY;So;0;ON;;;;;N;FORMS UP LIGHT AND LEFT HEAVY;;;;
+251A;BOX DRAWINGS UP HEAVY AND LEFT LIGHT;So;0;ON;;;;;N;FORMS UP HEAVY AND LEFT LIGHT;;;;
+251B;BOX DRAWINGS HEAVY UP AND LEFT;So;0;ON;;;;;N;FORMS HEAVY UP AND LEFT;;;;
+251C;BOX DRAWINGS LIGHT VERTICAL AND RIGHT;So;0;ON;;;;;N;FORMS LIGHT VERTICAL AND RIGHT;;;;
+251D;BOX DRAWINGS VERTICAL LIGHT AND RIGHT HEAVY;So;0;ON;;;;;N;FORMS VERTICAL LIGHT AND RIGHT HEAVY;;;;
+251E;BOX DRAWINGS UP HEAVY AND RIGHT DOWN LIGHT;So;0;ON;;;;;N;FORMS UP HEAVY AND RIGHT DOWN LIGHT;;;;
+251F;BOX DRAWINGS DOWN HEAVY AND RIGHT UP LIGHT;So;0;ON;;;;;N;FORMS DOWN HEAVY AND RIGHT UP LIGHT;;;;
+2520;BOX DRAWINGS VERTICAL HEAVY AND RIGHT LIGHT;So;0;ON;;;;;N;FORMS VERTICAL HEAVY AND RIGHT LIGHT;;;;
+2521;BOX DRAWINGS DOWN LIGHT AND RIGHT UP HEAVY;So;0;ON;;;;;N;FORMS DOWN LIGHT AND RIGHT UP HEAVY;;;;
+2522;BOX DRAWINGS UP LIGHT AND RIGHT DOWN HEAVY;So;0;ON;;;;;N;FORMS UP LIGHT AND RIGHT DOWN HEAVY;;;;
+2523;BOX DRAWINGS HEAVY VERTICAL AND RIGHT;So;0;ON;;;;;N;FORMS HEAVY VERTICAL AND RIGHT;;;;
+2524;BOX DRAWINGS LIGHT VERTICAL AND LEFT;So;0;ON;;;;;N;FORMS LIGHT VERTICAL AND LEFT;;;;
+2525;BOX DRAWINGS VERTICAL LIGHT AND LEFT HEAVY;So;0;ON;;;;;N;FORMS VERTICAL LIGHT AND LEFT HEAVY;;;;
+2526;BOX DRAWINGS UP HEAVY AND LEFT DOWN LIGHT;So;0;ON;;;;;N;FORMS UP HEAVY AND LEFT DOWN LIGHT;;;;
+2527;BOX DRAWINGS DOWN HEAVY AND LEFT UP LIGHT;So;0;ON;;;;;N;FORMS DOWN HEAVY AND LEFT UP LIGHT;;;;
+2528;BOX DRAWINGS VERTICAL HEAVY AND LEFT LIGHT;So;0;ON;;;;;N;FORMS VERTICAL HEAVY AND LEFT LIGHT;;;;
+2529;BOX DRAWINGS DOWN LIGHT AND LEFT UP HEAVY;So;0;ON;;;;;N;FORMS DOWN LIGHT AND LEFT UP HEAVY;;;;
+252A;BOX DRAWINGS UP LIGHT AND LEFT DOWN HEAVY;So;0;ON;;;;;N;FORMS UP LIGHT AND LEFT DOWN HEAVY;;;;
+252B;BOX DRAWINGS HEAVY VERTICAL AND LEFT;So;0;ON;;;;;N;FORMS HEAVY VERTICAL AND LEFT;;;;
+252C;BOX DRAWINGS LIGHT DOWN AND HORIZONTAL;So;0;ON;;;;;N;FORMS LIGHT DOWN AND HORIZONTAL;;;;
+252D;BOX DRAWINGS LEFT HEAVY AND RIGHT DOWN LIGHT;So;0;ON;;;;;N;FORMS LEFT HEAVY AND RIGHT DOWN LIGHT;;;;
+252E;BOX DRAWINGS RIGHT HEAVY AND LEFT DOWN LIGHT;So;0;ON;;;;;N;FORMS RIGHT HEAVY AND LEFT DOWN LIGHT;;;;
+252F;BOX DRAWINGS DOWN LIGHT AND HORIZONTAL HEAVY;So;0;ON;;;;;N;FORMS DOWN LIGHT AND HORIZONTAL HEAVY;;;;
+2530;BOX DRAWINGS DOWN HEAVY AND HORIZONTAL LIGHT;So;0;ON;;;;;N;FORMS DOWN HEAVY AND HORIZONTAL LIGHT;;;;
+2531;BOX DRAWINGS RIGHT LIGHT AND LEFT DOWN HEAVY;So;0;ON;;;;;N;FORMS RIGHT LIGHT AND LEFT DOWN HEAVY;;;;
+2532;BOX DRAWINGS LEFT LIGHT AND RIGHT DOWN HEAVY;So;0;ON;;;;;N;FORMS LEFT LIGHT AND RIGHT DOWN HEAVY;;;;
+2533;BOX DRAWINGS HEAVY DOWN AND HORIZONTAL;So;0;ON;;;;;N;FORMS HEAVY DOWN AND HORIZONTAL;;;;
+2534;BOX DRAWINGS LIGHT UP AND HORIZONTAL;So;0;ON;;;;;N;FORMS LIGHT UP AND HORIZONTAL;;;;
+2535;BOX DRAWINGS LEFT HEAVY AND RIGHT UP LIGHT;So;0;ON;;;;;N;FORMS LEFT HEAVY AND RIGHT UP LIGHT;;;;
+2536;BOX DRAWINGS RIGHT HEAVY AND LEFT UP LIGHT;So;0;ON;;;;;N;FORMS RIGHT HEAVY AND LEFT UP LIGHT;;;;
+2537;BOX DRAWINGS UP LIGHT AND HORIZONTAL HEAVY;So;0;ON;;;;;N;FORMS UP LIGHT AND HORIZONTAL HEAVY;;;;
+2538;BOX DRAWINGS UP HEAVY AND HORIZONTAL LIGHT;So;0;ON;;;;;N;FORMS UP HEAVY AND HORIZONTAL LIGHT;;;;
+2539;BOX DRAWINGS RIGHT LIGHT AND LEFT UP HEAVY;So;0;ON;;;;;N;FORMS RIGHT LIGHT AND LEFT UP HEAVY;;;;
+253A;BOX DRAWINGS LEFT LIGHT AND RIGHT UP HEAVY;So;0;ON;;;;;N;FORMS LEFT LIGHT AND RIGHT UP HEAVY;;;;
+253B;BOX DRAWINGS HEAVY UP AND HORIZONTAL;So;0;ON;;;;;N;FORMS HEAVY UP AND HORIZONTAL;;;;
+253C;BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL;So;0;ON;;;;;N;FORMS LIGHT VERTICAL AND HORIZONTAL;;;;
+253D;BOX DRAWINGS LEFT HEAVY AND RIGHT VERTICAL LIGHT;So;0;ON;;;;;N;FORMS LEFT HEAVY AND RIGHT VERTICAL LIGHT;;;;
+253E;BOX DRAWINGS RIGHT HEAVY AND LEFT VERTICAL LIGHT;So;0;ON;;;;;N;FORMS RIGHT HEAVY AND LEFT VERTICAL LIGHT;;;;
+253F;BOX DRAWINGS VERTICAL LIGHT AND HORIZONTAL HEAVY;So;0;ON;;;;;N;FORMS VERTICAL LIGHT AND HORIZONTAL HEAVY;;;;
+2540;BOX DRAWINGS UP HEAVY AND DOWN HORIZONTAL LIGHT;So;0;ON;;;;;N;FORMS UP HEAVY AND DOWN HORIZONTAL LIGHT;;;;
+2541;BOX DRAWINGS DOWN HEAVY AND UP HORIZONTAL LIGHT;So;0;ON;;;;;N;FORMS DOWN HEAVY AND UP HORIZONTAL LIGHT;;;;
+2542;BOX DRAWINGS VERTICAL HEAVY AND HORIZONTAL LIGHT;So;0;ON;;;;;N;FORMS VERTICAL HEAVY AND HORIZONTAL LIGHT;;;;
+2543;BOX DRAWINGS LEFT UP HEAVY AND RIGHT DOWN LIGHT;So;0;ON;;;;;N;FORMS LEFT UP HEAVY AND RIGHT DOWN LIGHT;;;;
+2544;BOX DRAWINGS RIGHT UP HEAVY AND LEFT DOWN LIGHT;So;0;ON;;;;;N;FORMS RIGHT UP HEAVY AND LEFT DOWN LIGHT;;;;
+2545;BOX DRAWINGS LEFT DOWN HEAVY AND RIGHT UP LIGHT;So;0;ON;;;;;N;FORMS LEFT DOWN HEAVY AND RIGHT UP LIGHT;;;;
+2546;BOX DRAWINGS RIGHT DOWN HEAVY AND LEFT UP LIGHT;So;0;ON;;;;;N;FORMS RIGHT DOWN HEAVY AND LEFT UP LIGHT;;;;
+2547;BOX DRAWINGS DOWN LIGHT AND UP HORIZONTAL HEAVY;So;0;ON;;;;;N;FORMS DOWN LIGHT AND UP HORIZONTAL HEAVY;;;;
+2548;BOX DRAWINGS UP LIGHT AND DOWN HORIZONTAL HEAVY;So;0;ON;;;;;N;FORMS UP LIGHT AND DOWN HORIZONTAL HEAVY;;;;
+2549;BOX DRAWINGS RIGHT LIGHT AND LEFT VERTICAL HEAVY;So;0;ON;;;;;N;FORMS RIGHT LIGHT AND LEFT VERTICAL HEAVY;;;;
+254A;BOX DRAWINGS LEFT LIGHT AND RIGHT VERTICAL HEAVY;So;0;ON;;;;;N;FORMS LEFT LIGHT AND RIGHT VERTICAL HEAVY;;;;
+254B;BOX DRAWINGS HEAVY VERTICAL AND HORIZONTAL;So;0;ON;;;;;N;FORMS HEAVY VERTICAL AND HORIZONTAL;;;;
+254C;BOX DRAWINGS LIGHT DOUBLE DASH HORIZONTAL;So;0;ON;;;;;N;FORMS LIGHT DOUBLE DASH HORIZONTAL;;;;
+254D;BOX DRAWINGS HEAVY DOUBLE DASH HORIZONTAL;So;0;ON;;;;;N;FORMS HEAVY DOUBLE DASH HORIZONTAL;;;;
+254E;BOX DRAWINGS LIGHT DOUBLE DASH VERTICAL;So;0;ON;;;;;N;FORMS LIGHT DOUBLE DASH VERTICAL;;;;
+254F;BOX DRAWINGS HEAVY DOUBLE DASH VERTICAL;So;0;ON;;;;;N;FORMS HEAVY DOUBLE DASH VERTICAL;;;;
+2550;BOX DRAWINGS DOUBLE HORIZONTAL;So;0;ON;;;;;N;FORMS DOUBLE HORIZONTAL;;;;
+2551;BOX DRAWINGS DOUBLE VERTICAL;So;0;ON;;;;;N;FORMS DOUBLE VERTICAL;;;;
+2552;BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE;So;0;ON;;;;;N;FORMS DOWN SINGLE AND RIGHT DOUBLE;;;;
+2553;BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE;So;0;ON;;;;;N;FORMS DOWN DOUBLE AND RIGHT SINGLE;;;;
+2554;BOX DRAWINGS DOUBLE DOWN AND RIGHT;So;0;ON;;;;;N;FORMS DOUBLE DOWN AND RIGHT;;;;
+2555;BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE;So;0;ON;;;;;N;FORMS DOWN SINGLE AND LEFT DOUBLE;;;;
+2556;BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE;So;0;ON;;;;;N;FORMS DOWN DOUBLE AND LEFT SINGLE;;;;
+2557;BOX DRAWINGS DOUBLE DOWN AND LEFT;So;0;ON;;;;;N;FORMS DOUBLE DOWN AND LEFT;;;;
+2558;BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE;So;0;ON;;;;;N;FORMS UP SINGLE AND RIGHT DOUBLE;;;;
+2559;BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE;So;0;ON;;;;;N;FORMS UP DOUBLE AND RIGHT SINGLE;;;;
+255A;BOX DRAWINGS DOUBLE UP AND RIGHT;So;0;ON;;;;;N;FORMS DOUBLE UP AND RIGHT;;;;
+255B;BOX DRAWINGS UP SINGLE AND LEFT DOUBLE;So;0;ON;;;;;N;FORMS UP SINGLE AND LEFT DOUBLE;;;;
+255C;BOX DRAWINGS UP DOUBLE AND LEFT SINGLE;So;0;ON;;;;;N;FORMS UP DOUBLE AND LEFT SINGLE;;;;
+255D;BOX DRAWINGS DOUBLE UP AND LEFT;So;0;ON;;;;;N;FORMS DOUBLE UP AND LEFT;;;;
+255E;BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE;So;0;ON;;;;;N;FORMS VERTICAL SINGLE AND RIGHT DOUBLE;;;;
+255F;BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE;So;0;ON;;;;;N;FORMS VERTICAL DOUBLE AND RIGHT SINGLE;;;;
+2560;BOX DRAWINGS DOUBLE VERTICAL AND RIGHT;So;0;ON;;;;;N;FORMS DOUBLE VERTICAL AND RIGHT;;;;
+2561;BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE;So;0;ON;;;;;N;FORMS VERTICAL SINGLE AND LEFT DOUBLE;;;;
+2562;BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE;So;0;ON;;;;;N;FORMS VERTICAL DOUBLE AND LEFT SINGLE;;;;
+2563;BOX DRAWINGS DOUBLE VERTICAL AND LEFT;So;0;ON;;;;;N;FORMS DOUBLE VERTICAL AND LEFT;;;;
+2564;BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE;So;0;ON;;;;;N;FORMS DOWN SINGLE AND HORIZONTAL DOUBLE;;;;
+2565;BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE;So;0;ON;;;;;N;FORMS DOWN DOUBLE AND HORIZONTAL SINGLE;;;;
+2566;BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL;So;0;ON;;;;;N;FORMS DOUBLE DOWN AND HORIZONTAL;;;;
+2567;BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE;So;0;ON;;;;;N;FORMS UP SINGLE AND HORIZONTAL DOUBLE;;;;
+2568;BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE;So;0;ON;;;;;N;FORMS UP DOUBLE AND HORIZONTAL SINGLE;;;;
+2569;BOX DRAWINGS DOUBLE UP AND HORIZONTAL;So;0;ON;;;;;N;FORMS DOUBLE UP AND HORIZONTAL;;;;
+256A;BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE;So;0;ON;;;;;N;FORMS VERTICAL SINGLE AND HORIZONTAL DOUBLE;;;;
+256B;BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE;So;0;ON;;;;;N;FORMS VERTICAL DOUBLE AND HORIZONTAL SINGLE;;;;
+256C;BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL;So;0;ON;;;;;N;FORMS DOUBLE VERTICAL AND HORIZONTAL;;;;
+256D;BOX DRAWINGS LIGHT ARC DOWN AND RIGHT;So;0;ON;;;;;N;FORMS LIGHT ARC DOWN AND RIGHT;;;;
+256E;BOX DRAWINGS LIGHT ARC DOWN AND LEFT;So;0;ON;;;;;N;FORMS LIGHT ARC DOWN AND LEFT;;;;
+256F;BOX DRAWINGS LIGHT ARC UP AND LEFT;So;0;ON;;;;;N;FORMS LIGHT ARC UP AND LEFT;;;;
+2570;BOX DRAWINGS LIGHT ARC UP AND RIGHT;So;0;ON;;;;;N;FORMS LIGHT ARC UP AND RIGHT;;;;
+2571;BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT;So;0;ON;;;;;N;FORMS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT;;;;
+2572;BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT;So;0;ON;;;;;N;FORMS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT;;;;
+2573;BOX DRAWINGS LIGHT DIAGONAL CROSS;So;0;ON;;;;;N;FORMS LIGHT DIAGONAL CROSS;;;;
+2574;BOX DRAWINGS LIGHT LEFT;So;0;ON;;;;;N;FORMS LIGHT LEFT;;;;
+2575;BOX DRAWINGS LIGHT UP;So;0;ON;;;;;N;FORMS LIGHT UP;;;;
+2576;BOX DRAWINGS LIGHT RIGHT;So;0;ON;;;;;N;FORMS LIGHT RIGHT;;;;
+2577;BOX DRAWINGS LIGHT DOWN;So;0;ON;;;;;N;FORMS LIGHT DOWN;;;;
+2578;BOX DRAWINGS HEAVY LEFT;So;0;ON;;;;;N;FORMS HEAVY LEFT;;;;
+2579;BOX DRAWINGS HEAVY UP;So;0;ON;;;;;N;FORMS HEAVY UP;;;;
+257A;BOX DRAWINGS HEAVY RIGHT;So;0;ON;;;;;N;FORMS HEAVY RIGHT;;;;
+257B;BOX DRAWINGS HEAVY DOWN;So;0;ON;;;;;N;FORMS HEAVY DOWN;;;;
+257C;BOX DRAWINGS LIGHT LEFT AND HEAVY RIGHT;So;0;ON;;;;;N;FORMS LIGHT LEFT AND HEAVY RIGHT;;;;
+257D;BOX DRAWINGS LIGHT UP AND HEAVY DOWN;So;0;ON;;;;;N;FORMS LIGHT UP AND HEAVY DOWN;;;;
+257E;BOX DRAWINGS HEAVY LEFT AND LIGHT RIGHT;So;0;ON;;;;;N;FORMS HEAVY LEFT AND LIGHT RIGHT;;;;
+257F;BOX DRAWINGS HEAVY UP AND LIGHT DOWN;So;0;ON;;;;;N;FORMS HEAVY UP AND LIGHT DOWN;;;;
+2580;UPPER HALF BLOCK;So;0;ON;;;;;N;;;;;
+2581;LOWER ONE EIGHTH BLOCK;So;0;ON;;;;;N;;;;;
+2582;LOWER ONE QUARTER BLOCK;So;0;ON;;;;;N;;;;;
+2583;LOWER THREE EIGHTHS BLOCK;So;0;ON;;;;;N;;;;;
+2584;LOWER HALF BLOCK;So;0;ON;;;;;N;;;;;
+2585;LOWER FIVE EIGHTHS BLOCK;So;0;ON;;;;;N;;;;;
+2586;LOWER THREE QUARTERS BLOCK;So;0;ON;;;;;N;LOWER THREE QUARTER BLOCK;;;;
+2587;LOWER SEVEN EIGHTHS BLOCK;So;0;ON;;;;;N;;;;;
+2588;FULL BLOCK;So;0;ON;;;;;N;;;;;
+2589;LEFT SEVEN EIGHTHS BLOCK;So;0;ON;;;;;N;;;;;
+258A;LEFT THREE QUARTERS BLOCK;So;0;ON;;;;;N;LEFT THREE QUARTER BLOCK;;;;
+258B;LEFT FIVE EIGHTHS BLOCK;So;0;ON;;;;;N;;;;;
+258C;LEFT HALF BLOCK;So;0;ON;;;;;N;;;;;
+258D;LEFT THREE EIGHTHS BLOCK;So;0;ON;;;;;N;;;;;
+258E;LEFT ONE QUARTER BLOCK;So;0;ON;;;;;N;;;;;
+258F;LEFT ONE EIGHTH BLOCK;So;0;ON;;;;;N;;;;;
+2590;RIGHT HALF BLOCK;So;0;ON;;;;;N;;;;;
+2591;LIGHT SHADE;So;0;ON;;;;;N;;;;;
+2592;MEDIUM SHADE;So;0;ON;;;;;N;;;;;
+2593;DARK SHADE;So;0;ON;;;;;N;;;;;
+2594;UPPER ONE EIGHTH BLOCK;So;0;ON;;;;;N;;;;;
+2595;RIGHT ONE EIGHTH BLOCK;So;0;ON;;;;;N;;;;;
+2596;QUADRANT LOWER LEFT;So;0;ON;;;;;N;;;;;
+2597;QUADRANT LOWER RIGHT;So;0;ON;;;;;N;;;;;
+2598;QUADRANT UPPER LEFT;So;0;ON;;;;;N;;;;;
+2599;QUADRANT UPPER LEFT AND LOWER LEFT AND LOWER RIGHT;So;0;ON;;;;;N;;;;;
+259A;QUADRANT UPPER LEFT AND LOWER RIGHT;So;0;ON;;;;;N;;;;;
+259B;QUADRANT UPPER LEFT AND UPPER RIGHT AND LOWER LEFT;So;0;ON;;;;;N;;;;;
+259C;QUADRANT UPPER LEFT AND UPPER RIGHT AND LOWER RIGHT;So;0;ON;;;;;N;;;;;
+259D;QUADRANT UPPER RIGHT;So;0;ON;;;;;N;;;;;
+259E;QUADRANT UPPER RIGHT AND LOWER LEFT;So;0;ON;;;;;N;;;;;
+259F;QUADRANT UPPER RIGHT AND LOWER LEFT AND LOWER RIGHT;So;0;ON;;;;;N;;;;;
+25A0;BLACK SQUARE;So;0;ON;;;;;N;;;;;
+25A1;WHITE SQUARE;So;0;ON;;;;;N;;;;;
+25A2;WHITE SQUARE WITH ROUNDED CORNERS;So;0;ON;;;;;N;;;;;
+25A3;WHITE SQUARE CONTAINING BLACK SMALL SQUARE;So;0;ON;;;;;N;;;;;
+25A4;SQUARE WITH HORIZONTAL FILL;So;0;ON;;;;;N;;;;;
+25A5;SQUARE WITH VERTICAL FILL;So;0;ON;;;;;N;;;;;
+25A6;SQUARE WITH ORTHOGONAL CROSSHATCH FILL;So;0;ON;;;;;N;;;;;
+25A7;SQUARE WITH UPPER LEFT TO LOWER RIGHT FILL;So;0;ON;;;;;N;;;;;
+25A8;SQUARE WITH UPPER RIGHT TO LOWER LEFT FILL;So;0;ON;;;;;N;;;;;
+25A9;SQUARE WITH DIAGONAL CROSSHATCH FILL;So;0;ON;;;;;N;;;;;
+25AA;BLACK SMALL SQUARE;So;0;ON;;;;;N;;;;;
+25AB;WHITE SMALL SQUARE;So;0;ON;;;;;N;;;;;
+25AC;BLACK RECTANGLE;So;0;ON;;;;;N;;;;;
+25AD;WHITE RECTANGLE;So;0;ON;;;;;N;;;;;
+25AE;BLACK VERTICAL RECTANGLE;So;0;ON;;;;;N;;;;;
+25AF;WHITE VERTICAL RECTANGLE;So;0;ON;;;;;N;;;;;
+25B0;BLACK PARALLELOGRAM;So;0;ON;;;;;N;;;;;
+25B1;WHITE PARALLELOGRAM;So;0;ON;;;;;N;;;;;
+25B2;BLACK UP-POINTING TRIANGLE;So;0;ON;;;;;N;BLACK UP POINTING TRIANGLE;;;;
+25B3;WHITE UP-POINTING TRIANGLE;So;0;ON;;;;;N;WHITE UP POINTING TRIANGLE;;;;
+25B4;BLACK UP-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;BLACK UP POINTING SMALL TRIANGLE;;;;
+25B5;WHITE UP-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;WHITE UP POINTING SMALL TRIANGLE;;;;
+25B6;BLACK RIGHT-POINTING TRIANGLE;So;0;ON;;;;;N;BLACK RIGHT POINTING TRIANGLE;;;;
+25B7;WHITE RIGHT-POINTING TRIANGLE;Sm;0;ON;;;;;N;WHITE RIGHT POINTING TRIANGLE;;;;
+25B8;BLACK RIGHT-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;BLACK RIGHT POINTING SMALL TRIANGLE;;;;
+25B9;WHITE RIGHT-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;WHITE RIGHT POINTING SMALL TRIANGLE;;;;
+25BA;BLACK RIGHT-POINTING POINTER;So;0;ON;;;;;N;BLACK RIGHT POINTING POINTER;;;;
+25BB;WHITE RIGHT-POINTING POINTER;So;0;ON;;;;;N;WHITE RIGHT POINTING POINTER;;;;
+25BC;BLACK DOWN-POINTING TRIANGLE;So;0;ON;;;;;N;BLACK DOWN POINTING TRIANGLE;;;;
+25BD;WHITE DOWN-POINTING TRIANGLE;So;0;ON;;;;;N;WHITE DOWN POINTING TRIANGLE;;;;
+25BE;BLACK DOWN-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;BLACK DOWN POINTING SMALL TRIANGLE;;;;
+25BF;WHITE DOWN-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;WHITE DOWN POINTING SMALL TRIANGLE;;;;
+25C0;BLACK LEFT-POINTING TRIANGLE;So;0;ON;;;;;N;BLACK LEFT POINTING TRIANGLE;;;;
+25C1;WHITE LEFT-POINTING TRIANGLE;Sm;0;ON;;;;;N;WHITE LEFT POINTING TRIANGLE;;;;
+25C2;BLACK LEFT-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;BLACK LEFT POINTING SMALL TRIANGLE;;;;
+25C3;WHITE LEFT-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;WHITE LEFT POINTING SMALL TRIANGLE;;;;
+25C4;BLACK LEFT-POINTING POINTER;So;0;ON;;;;;N;BLACK LEFT POINTING POINTER;;;;
+25C5;WHITE LEFT-POINTING POINTER;So;0;ON;;;;;N;WHITE LEFT POINTING POINTER;;;;
+25C6;BLACK DIAMOND;So;0;ON;;;;;N;;;;;
+25C7;WHITE DIAMOND;So;0;ON;;;;;N;;;;;
+25C8;WHITE DIAMOND CONTAINING BLACK SMALL DIAMOND;So;0;ON;;;;;N;;;;;
+25C9;FISHEYE;So;0;ON;;;;;N;;;;;
+25CA;LOZENGE;So;0;ON;;;;;N;;;;;
+25CB;WHITE CIRCLE;So;0;ON;;;;;N;;;;;
+25CC;DOTTED CIRCLE;So;0;ON;;;;;N;;;;;
+25CD;CIRCLE WITH VERTICAL FILL;So;0;ON;;;;;N;;;;;
+25CE;BULLSEYE;So;0;ON;;;;;N;;;;;
+25CF;BLACK CIRCLE;So;0;ON;;;;;N;;;;;
+25D0;CIRCLE WITH LEFT HALF BLACK;So;0;ON;;;;;N;;;;;
+25D1;CIRCLE WITH RIGHT HALF BLACK;So;0;ON;;;;;N;;;;;
+25D2;CIRCLE WITH LOWER HALF BLACK;So;0;ON;;;;;N;;;;;
+25D3;CIRCLE WITH UPPER HALF BLACK;So;0;ON;;;;;N;;;;;
+25D4;CIRCLE WITH UPPER RIGHT QUADRANT BLACK;So;0;ON;;;;;N;;;;;
+25D5;CIRCLE WITH ALL BUT UPPER LEFT QUADRANT BLACK;So;0;ON;;;;;N;;;;;
+25D6;LEFT HALF BLACK CIRCLE;So;0;ON;;;;;N;;;;;
+25D7;RIGHT HALF BLACK CIRCLE;So;0;ON;;;;;N;;;;;
+25D8;INVERSE BULLET;So;0;ON;;;;;N;;;;;
+25D9;INVERSE WHITE CIRCLE;So;0;ON;;;;;N;;;;;
+25DA;UPPER HALF INVERSE WHITE CIRCLE;So;0;ON;;;;;N;;;;;
+25DB;LOWER HALF INVERSE WHITE CIRCLE;So;0;ON;;;;;N;;;;;
+25DC;UPPER LEFT QUADRANT CIRCULAR ARC;So;0;ON;;;;;N;;;;;
+25DD;UPPER RIGHT QUADRANT CIRCULAR ARC;So;0;ON;;;;;N;;;;;
+25DE;LOWER RIGHT QUADRANT CIRCULAR ARC;So;0;ON;;;;;N;;;;;
+25DF;LOWER LEFT QUADRANT CIRCULAR ARC;So;0;ON;;;;;N;;;;;
+25E0;UPPER HALF CIRCLE;So;0;ON;;;;;N;;;;;
+25E1;LOWER HALF CIRCLE;So;0;ON;;;;;N;;;;;
+25E2;BLACK LOWER RIGHT TRIANGLE;So;0;ON;;;;;N;;;;;
+25E3;BLACK LOWER LEFT TRIANGLE;So;0;ON;;;;;N;;;;;
+25E4;BLACK UPPER LEFT TRIANGLE;So;0;ON;;;;;N;;;;;
+25E5;BLACK UPPER RIGHT TRIANGLE;So;0;ON;;;;;N;;;;;
+25E6;WHITE BULLET;So;0;ON;;;;;N;;;;;
+25E7;SQUARE WITH LEFT HALF BLACK;So;0;ON;;;;;N;;;;;
+25E8;SQUARE WITH RIGHT HALF BLACK;So;0;ON;;;;;N;;;;;
+25E9;SQUARE WITH UPPER LEFT DIAGONAL HALF BLACK;So;0;ON;;;;;N;;;;;
+25EA;SQUARE WITH LOWER RIGHT DIAGONAL HALF BLACK;So;0;ON;;;;;N;;;;;
+25EB;WHITE SQUARE WITH VERTICAL BISECTING LINE;So;0;ON;;;;;N;;;;;
+25EC;WHITE UP-POINTING TRIANGLE WITH DOT;So;0;ON;;;;;N;WHITE UP POINTING TRIANGLE WITH DOT;;;;
+25ED;UP-POINTING TRIANGLE WITH LEFT HALF BLACK;So;0;ON;;;;;N;UP POINTING TRIANGLE WITH LEFT HALF BLACK;;;;
+25EE;UP-POINTING TRIANGLE WITH RIGHT HALF BLACK;So;0;ON;;;;;N;UP POINTING TRIANGLE WITH RIGHT HALF BLACK;;;;
+25EF;LARGE CIRCLE;So;0;ON;;;;;N;;;;;
+25F0;WHITE SQUARE WITH UPPER LEFT QUADRANT;So;0;ON;;;;;N;;;;;
+25F1;WHITE SQUARE WITH LOWER LEFT QUADRANT;So;0;ON;;;;;N;;;;;
+25F2;WHITE SQUARE WITH LOWER RIGHT QUADRANT;So;0;ON;;;;;N;;;;;
+25F3;WHITE SQUARE WITH UPPER RIGHT QUADRANT;So;0;ON;;;;;N;;;;;
+25F4;WHITE CIRCLE WITH UPPER LEFT QUADRANT;So;0;ON;;;;;N;;;;;
+25F5;WHITE CIRCLE WITH LOWER LEFT QUADRANT;So;0;ON;;;;;N;;;;;
+25F6;WHITE CIRCLE WITH LOWER RIGHT QUADRANT;So;0;ON;;;;;N;;;;;
+25F7;WHITE CIRCLE WITH UPPER RIGHT QUADRANT;So;0;ON;;;;;N;;;;;
+25F8;UPPER LEFT TRIANGLE;Sm;0;ON;;;;;N;;;;;
+25F9;UPPER RIGHT TRIANGLE;Sm;0;ON;;;;;N;;;;;
+25FA;LOWER LEFT TRIANGLE;Sm;0;ON;;;;;N;;;;;
+25FB;WHITE MEDIUM SQUARE;Sm;0;ON;;;;;N;;;;;
+25FC;BLACK MEDIUM SQUARE;Sm;0;ON;;;;;N;;;;;
+25FD;WHITE MEDIUM SMALL SQUARE;Sm;0;ON;;;;;N;;;;;
+25FE;BLACK MEDIUM SMALL SQUARE;Sm;0;ON;;;;;N;;;;;
+25FF;LOWER RIGHT TRIANGLE;Sm;0;ON;;;;;N;;;;;
+2600;BLACK SUN WITH RAYS;So;0;ON;;;;;N;;;;;
+2601;CLOUD;So;0;ON;;;;;N;;;;;
+2602;UMBRELLA;So;0;ON;;;;;N;;;;;
+2603;SNOWMAN;So;0;ON;;;;;N;;;;;
+2604;COMET;So;0;ON;;;;;N;;;;;
+2605;BLACK STAR;So;0;ON;;;;;N;;;;;
+2606;WHITE STAR;So;0;ON;;;;;N;;;;;
+2607;LIGHTNING;So;0;ON;;;;;N;;;;;
+2608;THUNDERSTORM;So;0;ON;;;;;N;;;;;
+2609;SUN;So;0;ON;;;;;N;;;;;
+260A;ASCENDING NODE;So;0;ON;;;;;N;;;;;
+260B;DESCENDING NODE;So;0;ON;;;;;N;;;;;
+260C;CONJUNCTION;So;0;ON;;;;;N;;;;;
+260D;OPPOSITION;So;0;ON;;;;;N;;;;;
+260E;BLACK TELEPHONE;So;0;ON;;;;;N;;;;;
+260F;WHITE TELEPHONE;So;0;ON;;;;;N;;;;;
+2610;BALLOT BOX;So;0;ON;;;;;N;;;;;
+2611;BALLOT BOX WITH CHECK;So;0;ON;;;;;N;;;;;
+2612;BALLOT BOX WITH X;So;0;ON;;;;;N;;;;;
+2613;SALTIRE;So;0;ON;;;;;N;;;;;
+2614;UMBRELLA WITH RAIN DROPS;So;0;ON;;;;;N;;;;;
+2615;HOT BEVERAGE;So;0;ON;;;;;N;;;;;
+2616;WHITE SHOGI PIECE;So;0;ON;;;;;N;;;;;
+2617;BLACK SHOGI PIECE;So;0;ON;;;;;N;;;;;
+2619;REVERSED ROTATED FLORAL HEART BULLET;So;0;ON;;;;;N;;;;;
+261A;BLACK LEFT POINTING INDEX;So;0;ON;;;;;N;;;;;
+261B;BLACK RIGHT POINTING INDEX;So;0;ON;;;;;N;;;;;
+261C;WHITE LEFT POINTING INDEX;So;0;ON;;;;;N;;;;;
+261D;WHITE UP POINTING INDEX;So;0;ON;;;;;N;;;;;
+261E;WHITE RIGHT POINTING INDEX;So;0;ON;;;;;N;;;;;
+261F;WHITE DOWN POINTING INDEX;So;0;ON;;;;;N;;;;;
+2620;SKULL AND CROSSBONES;So;0;ON;;;;;N;;;;;
+2621;CAUTION SIGN;So;0;ON;;;;;N;;;;;
+2622;RADIOACTIVE SIGN;So;0;ON;;;;;N;;;;;
+2623;BIOHAZARD SIGN;So;0;ON;;;;;N;;;;;
+2624;CADUCEUS;So;0;ON;;;;;N;;;;;
+2625;ANKH;So;0;ON;;;;;N;;;;;
+2626;ORTHODOX CROSS;So;0;ON;;;;;N;;;;;
+2627;CHI RHO;So;0;ON;;;;;N;;;;;
+2628;CROSS OF LORRAINE;So;0;ON;;;;;N;;;;;
+2629;CROSS OF JERUSALEM;So;0;ON;;;;;N;;;;;
+262A;STAR AND CRESCENT;So;0;ON;;;;;N;;;;;
+262B;FARSI SYMBOL;So;0;ON;;;;;N;SYMBOL OF IRAN;;;;
+262C;ADI SHAKTI;So;0;ON;;;;;N;;;;;
+262D;HAMMER AND SICKLE;So;0;ON;;;;;N;;;;;
+262E;PEACE SYMBOL;So;0;ON;;;;;N;;;;;
+262F;YIN YANG;So;0;ON;;;;;N;;;;;
+2630;TRIGRAM FOR HEAVEN;So;0;ON;;;;;N;;;;;
+2631;TRIGRAM FOR LAKE;So;0;ON;;;;;N;;;;;
+2632;TRIGRAM FOR FIRE;So;0;ON;;;;;N;;;;;
+2633;TRIGRAM FOR THUNDER;So;0;ON;;;;;N;;;;;
+2634;TRIGRAM FOR WIND;So;0;ON;;;;;N;;;;;
+2635;TRIGRAM FOR WATER;So;0;ON;;;;;N;;;;;
+2636;TRIGRAM FOR MOUNTAIN;So;0;ON;;;;;N;;;;;
+2637;TRIGRAM FOR EARTH;So;0;ON;;;;;N;;;;;
+2638;WHEEL OF DHARMA;So;0;ON;;;;;N;;;;;
+2639;WHITE FROWNING FACE;So;0;ON;;;;;N;;;;;
+263A;WHITE SMILING FACE;So;0;ON;;;;;N;;;;;
+263B;BLACK SMILING FACE;So;0;ON;;;;;N;;;;;
+263C;WHITE SUN WITH RAYS;So;0;ON;;;;;N;;;;;
+263D;FIRST QUARTER MOON;So;0;ON;;;;;N;;;;;
+263E;LAST QUARTER MOON;So;0;ON;;;;;N;;;;;
+263F;MERCURY;So;0;ON;;;;;N;;;;;
+2640;FEMALE SIGN;So;0;ON;;;;;N;;;;;
+2641;EARTH;So;0;ON;;;;;N;;;;;
+2642;MALE SIGN;So;0;ON;;;;;N;;;;;
+2643;JUPITER;So;0;ON;;;;;N;;;;;
+2644;SATURN;So;0;ON;;;;;N;;;;;
+2645;URANUS;So;0;ON;;;;;N;;;;;
+2646;NEPTUNE;So;0;ON;;;;;N;;;;;
+2647;PLUTO;So;0;ON;;;;;N;;;;;
+2648;ARIES;So;0;ON;;;;;N;;;;;
+2649;TAURUS;So;0;ON;;;;;N;;;;;
+264A;GEMINI;So;0;ON;;;;;N;;;;;
+264B;CANCER;So;0;ON;;;;;N;;;;;
+264C;LEO;So;0;ON;;;;;N;;;;;
+264D;VIRGO;So;0;ON;;;;;N;;;;;
+264E;LIBRA;So;0;ON;;;;;N;;;;;
+264F;SCORPIUS;So;0;ON;;;;;N;;;;;
+2650;SAGITTARIUS;So;0;ON;;;;;N;;;;;
+2651;CAPRICORN;So;0;ON;;;;;N;;;;;
+2652;AQUARIUS;So;0;ON;;;;;N;;;;;
+2653;PISCES;So;0;ON;;;;;N;;;;;
+2654;WHITE CHESS KING;So;0;ON;;;;;N;;;;;
+2655;WHITE CHESS QUEEN;So;0;ON;;;;;N;;;;;
+2656;WHITE CHESS ROOK;So;0;ON;;;;;N;;;;;
+2657;WHITE CHESS BISHOP;So;0;ON;;;;;N;;;;;
+2658;WHITE CHESS KNIGHT;So;0;ON;;;;;N;;;;;
+2659;WHITE CHESS PAWN;So;0;ON;;;;;N;;;;;
+265A;BLACK CHESS KING;So;0;ON;;;;;N;;;;;
+265B;BLACK CHESS QUEEN;So;0;ON;;;;;N;;;;;
+265C;BLACK CHESS ROOK;So;0;ON;;;;;N;;;;;
+265D;BLACK CHESS BISHOP;So;0;ON;;;;;N;;;;;
+265E;BLACK CHESS KNIGHT;So;0;ON;;;;;N;;;;;
+265F;BLACK CHESS PAWN;So;0;ON;;;;;N;;;;;
+2660;BLACK SPADE SUIT;So;0;ON;;;;;N;;;;;
+2661;WHITE HEART SUIT;So;0;ON;;;;;N;;;;;
+2662;WHITE DIAMOND SUIT;So;0;ON;;;;;N;;;;;
+2663;BLACK CLUB SUIT;So;0;ON;;;;;N;;;;;
+2664;WHITE SPADE SUIT;So;0;ON;;;;;N;;;;;
+2665;BLACK HEART SUIT;So;0;ON;;;;;N;;;;;
+2666;BLACK DIAMOND SUIT;So;0;ON;;;;;N;;;;;
+2667;WHITE CLUB SUIT;So;0;ON;;;;;N;;;;;
+2668;HOT SPRINGS;So;0;ON;;;;;N;;;;;
+2669;QUARTER NOTE;So;0;ON;;;;;N;;;;;
+266A;EIGHTH NOTE;So;0;ON;;;;;N;;;;;
+266B;BEAMED EIGHTH NOTES;So;0;ON;;;;;N;BARRED EIGHTH NOTES;;;;
+266C;BEAMED SIXTEENTH NOTES;So;0;ON;;;;;N;BARRED SIXTEENTH NOTES;;;;
+266D;MUSIC FLAT SIGN;So;0;ON;;;;;N;FLAT;;;;
+266E;MUSIC NATURAL SIGN;So;0;ON;;;;;N;NATURAL;;;;
+266F;MUSIC SHARP SIGN;Sm;0;ON;;;;;N;SHARP;;;;
+2670;WEST SYRIAC CROSS;So;0;ON;;;;;N;;;;;
+2671;EAST SYRIAC CROSS;So;0;ON;;;;;N;;;;;
+2672;UNIVERSAL RECYCLING SYMBOL;So;0;ON;;;;;N;;;;;
+2673;RECYCLING SYMBOL FOR TYPE-1 PLASTICS;So;0;ON;;;;;N;;pete;;;
+2674;RECYCLING SYMBOL FOR TYPE-2 PLASTICS;So;0;ON;;;;;N;;hdpe;;;
+2675;RECYCLING SYMBOL FOR TYPE-3 PLASTICS;So;0;ON;;;;;N;;pvc;;;
+2676;RECYCLING SYMBOL FOR TYPE-4 PLASTICS;So;0;ON;;;;;N;;ldpe;;;
+2677;RECYCLING SYMBOL FOR TYPE-5 PLASTICS;So;0;ON;;;;;N;;pp;;;
+2678;RECYCLING SYMBOL FOR TYPE-6 PLASTICS;So;0;ON;;;;;N;;ps;;;
+2679;RECYCLING SYMBOL FOR TYPE-7 PLASTICS;So;0;ON;;;;;N;;other;;;
+267A;RECYCLING SYMBOL FOR GENERIC MATERIALS;So;0;ON;;;;;N;;;;;
+267B;BLACK UNIVERSAL RECYCLING SYMBOL;So;0;ON;;;;;N;;;;;
+267C;RECYCLED PAPER SYMBOL;So;0;ON;;;;;N;;;;;
+267D;PARTIALLY-RECYCLED PAPER SYMBOL;So;0;ON;;;;;N;;;;;
+2680;DIE FACE-1;So;0;ON;;;;;N;;;;;
+2681;DIE FACE-2;So;0;ON;;;;;N;;;;;
+2682;DIE FACE-3;So;0;ON;;;;;N;;;;;
+2683;DIE FACE-4;So;0;ON;;;;;N;;;;;
+2684;DIE FACE-5;So;0;ON;;;;;N;;;;;
+2685;DIE FACE-6;So;0;ON;;;;;N;;;;;
+2686;WHITE CIRCLE WITH DOT RIGHT;So;0;ON;;;;;N;;;;;
+2687;WHITE CIRCLE WITH TWO DOTS;So;0;ON;;;;;N;;;;;
+2688;BLACK CIRCLE WITH WHITE DOT RIGHT;So;0;ON;;;;;N;;;;;
+2689;BLACK CIRCLE WITH TWO WHITE DOTS;So;0;ON;;;;;N;;;;;
+268A;MONOGRAM FOR YANG;So;0;ON;;;;;N;;;;;
+268B;MONOGRAM FOR YIN;So;0;ON;;;;;N;;;;;
+268C;DIGRAM FOR GREATER YANG;So;0;ON;;;;;N;;;;;
+268D;DIGRAM FOR LESSER YIN;So;0;ON;;;;;N;;;;;
+268E;DIGRAM FOR LESSER YANG;So;0;ON;;;;;N;;;;;
+268F;DIGRAM FOR GREATER YIN;So;0;ON;;;;;N;;;;;
+2690;WHITE FLAG;So;0;ON;;;;;N;;;;;
+2691;BLACK FLAG;So;0;ON;;;;;N;;;;;
+26A0;WARNING SIGN;So;0;ON;;;;;N;;;;;
+26A1;HIGH VOLTAGE SIGN;So;0;ON;;;;;N;;;;;
+2701;UPPER BLADE SCISSORS;So;0;ON;;;;;N;;;;;
+2702;BLACK SCISSORS;So;0;ON;;;;;N;;;;;
+2703;LOWER BLADE SCISSORS;So;0;ON;;;;;N;;;;;
+2704;WHITE SCISSORS;So;0;ON;;;;;N;;;;;
+2706;TELEPHONE LOCATION SIGN;So;0;ON;;;;;N;;;;;
+2707;TAPE DRIVE;So;0;ON;;;;;N;;;;;
+2708;AIRPLANE;So;0;ON;;;;;N;;;;;
+2709;ENVELOPE;So;0;ON;;;;;N;;;;;
+270C;VICTORY HAND;So;0;ON;;;;;N;;;;;
+270D;WRITING HAND;So;0;ON;;;;;N;;;;;
+270E;LOWER RIGHT PENCIL;So;0;ON;;;;;N;;;;;
+270F;PENCIL;So;0;ON;;;;;N;;;;;
+2710;UPPER RIGHT PENCIL;So;0;ON;;;;;N;;;;;
+2711;WHITE NIB;So;0;ON;;;;;N;;;;;
+2712;BLACK NIB;So;0;ON;;;;;N;;;;;
+2713;CHECK MARK;So;0;ON;;;;;N;;;;;
+2714;HEAVY CHECK MARK;So;0;ON;;;;;N;;;;;
+2715;MULTIPLICATION X;So;0;ON;;;;;N;;;;;
+2716;HEAVY MULTIPLICATION X;So;0;ON;;;;;N;;;;;
+2717;BALLOT X;So;0;ON;;;;;N;;;;;
+2718;HEAVY BALLOT X;So;0;ON;;;;;N;;;;;
+2719;OUTLINED GREEK CROSS;So;0;ON;;;;;N;;;;;
+271A;HEAVY GREEK CROSS;So;0;ON;;;;;N;;;;;
+271B;OPEN CENTRE CROSS;So;0;ON;;;;;N;OPEN CENTER CROSS;;;;
+271C;HEAVY OPEN CENTRE CROSS;So;0;ON;;;;;N;HEAVY OPEN CENTER CROSS;;;;
+271D;LATIN CROSS;So;0;ON;;;;;N;;;;;
+271E;SHADOWED WHITE LATIN CROSS;So;0;ON;;;;;N;;;;;
+271F;OUTLINED LATIN CROSS;So;0;ON;;;;;N;;;;;
+2720;MALTESE CROSS;So;0;ON;;;;;N;;;;;
+2721;STAR OF DAVID;So;0;ON;;;;;N;;;;;
+2722;FOUR TEARDROP-SPOKED ASTERISK;So;0;ON;;;;;N;;;;;
+2723;FOUR BALLOON-SPOKED ASTERISK;So;0;ON;;;;;N;;;;;
+2724;HEAVY FOUR BALLOON-SPOKED ASTERISK;So;0;ON;;;;;N;;;;;
+2725;FOUR CLUB-SPOKED ASTERISK;So;0;ON;;;;;N;;;;;
+2726;BLACK FOUR POINTED STAR;So;0;ON;;;;;N;;;;;
+2727;WHITE FOUR POINTED STAR;So;0;ON;;;;;N;;;;;
+2729;STRESS OUTLINED WHITE STAR;So;0;ON;;;;;N;;;;;
+272A;CIRCLED WHITE STAR;So;0;ON;;;;;N;;;;;
+272B;OPEN CENTRE BLACK STAR;So;0;ON;;;;;N;OPEN CENTER BLACK STAR;;;;
+272C;BLACK CENTRE WHITE STAR;So;0;ON;;;;;N;BLACK CENTER WHITE STAR;;;;
+272D;OUTLINED BLACK STAR;So;0;ON;;;;;N;;;;;
+272E;HEAVY OUTLINED BLACK STAR;So;0;ON;;;;;N;;;;;
+272F;PINWHEEL STAR;So;0;ON;;;;;N;;;;;
+2730;SHADOWED WHITE STAR;So;0;ON;;;;;N;;;;;
+2731;HEAVY ASTERISK;So;0;ON;;;;;N;;;;;
+2732;OPEN CENTRE ASTERISK;So;0;ON;;;;;N;OPEN CENTER ASTERISK;;;;
+2733;EIGHT SPOKED ASTERISK;So;0;ON;;;;;N;;;;;
+2734;EIGHT POINTED BLACK STAR;So;0;ON;;;;;N;;;;;
+2735;EIGHT POINTED PINWHEEL STAR;So;0;ON;;;;;N;;;;;
+2736;SIX POINTED BLACK STAR;So;0;ON;;;;;N;;;;;
+2737;EIGHT POINTED RECTILINEAR BLACK STAR;So;0;ON;;;;;N;;;;;
+2738;HEAVY EIGHT POINTED RECTILINEAR BLACK STAR;So;0;ON;;;;;N;;;;;
+2739;TWELVE POINTED BLACK STAR;So;0;ON;;;;;N;;;;;
+273A;SIXTEEN POINTED ASTERISK;So;0;ON;;;;;N;;;;;
+273B;TEARDROP-SPOKED ASTERISK;So;0;ON;;;;;N;;;;;
+273C;OPEN CENTRE TEARDROP-SPOKED ASTERISK;So;0;ON;;;;;N;OPEN CENTER TEARDROP-SPOKED ASTERISK;;;;
+273D;HEAVY TEARDROP-SPOKED ASTERISK;So;0;ON;;;;;N;;;;;
+273E;SIX PETALLED BLACK AND WHITE FLORETTE;So;0;ON;;;;;N;;;;;
+273F;BLACK FLORETTE;So;0;ON;;;;;N;;;;;
+2740;WHITE FLORETTE;So;0;ON;;;;;N;;;;;
+2741;EIGHT PETALLED OUTLINED BLACK FLORETTE;So;0;ON;;;;;N;;;;;
+2742;CIRCLED OPEN CENTRE EIGHT POINTED STAR;So;0;ON;;;;;N;CIRCLED OPEN CENTER EIGHT POINTED STAR;;;;
+2743;HEAVY TEARDROP-SPOKED PINWHEEL ASTERISK;So;0;ON;;;;;N;;;;;
+2744;SNOWFLAKE;So;0;ON;;;;;N;;;;;
+2745;TIGHT TRIFOLIATE SNOWFLAKE;So;0;ON;;;;;N;;;;;
+2746;HEAVY CHEVRON SNOWFLAKE;So;0;ON;;;;;N;;;;;
+2747;SPARKLE;So;0;ON;;;;;N;;;;;
+2748;HEAVY SPARKLE;So;0;ON;;;;;N;;;;;
+2749;BALLOON-SPOKED ASTERISK;So;0;ON;;;;;N;;;;;
+274A;EIGHT TEARDROP-SPOKED PROPELLER ASTERISK;So;0;ON;;;;;N;;;;;
+274B;HEAVY EIGHT TEARDROP-SPOKED PROPELLER ASTERISK;So;0;ON;;;;;N;;;;;
+274D;SHADOWED WHITE CIRCLE;So;0;ON;;;;;N;;;;;
+274F;LOWER RIGHT DROP-SHADOWED WHITE SQUARE;So;0;ON;;;;;N;;;;;
+2750;UPPER RIGHT DROP-SHADOWED WHITE SQUARE;So;0;ON;;;;;N;;;;;
+2751;LOWER RIGHT SHADOWED WHITE SQUARE;So;0;ON;;;;;N;;;;;
+2752;UPPER RIGHT SHADOWED WHITE SQUARE;So;0;ON;;;;;N;;;;;
+2756;BLACK DIAMOND MINUS WHITE X;So;0;ON;;;;;N;;;;;
+2758;LIGHT VERTICAL BAR;So;0;ON;;;;;N;;;;;
+2759;MEDIUM VERTICAL BAR;So;0;ON;;;;;N;;;;;
+275A;HEAVY VERTICAL BAR;So;0;ON;;;;;N;;;;;
+275B;HEAVY SINGLE TURNED COMMA QUOTATION MARK ORNAMENT;So;0;ON;;;;;N;;;;;
+275C;HEAVY SINGLE COMMA QUOTATION MARK ORNAMENT;So;0;ON;;;;;N;;;;;
+275D;HEAVY DOUBLE TURNED COMMA QUOTATION MARK ORNAMENT;So;0;ON;;;;;N;;;;;
+275E;HEAVY DOUBLE COMMA QUOTATION MARK ORNAMENT;So;0;ON;;;;;N;;;;;
+2761;CURVED STEM PARAGRAPH SIGN ORNAMENT;So;0;ON;;;;;N;;;;;
+2762;HEAVY EXCLAMATION MARK ORNAMENT;So;0;ON;;;;;N;;;;;
+2763;HEAVY HEART EXCLAMATION MARK ORNAMENT;So;0;ON;;;;;N;;;;;
+2764;HEAVY BLACK HEART;So;0;ON;;;;;N;;;;;
+2765;ROTATED HEAVY BLACK HEART BULLET;So;0;ON;;;;;N;;;;;
+2766;FLORAL HEART;So;0;ON;;;;;N;;;;;
+2767;ROTATED FLORAL HEART BULLET;So;0;ON;;;;;N;;;;;
+2768;MEDIUM LEFT PARENTHESIS ORNAMENT;Ps;0;ON;;;;;Y;;;;;
+2769;MEDIUM RIGHT PARENTHESIS ORNAMENT;Pe;0;ON;;;;;Y;;;;;
+276A;MEDIUM FLATTENED LEFT PARENTHESIS ORNAMENT;Ps;0;ON;;;;;Y;;;;;
+276B;MEDIUM FLATTENED RIGHT PARENTHESIS ORNAMENT;Pe;0;ON;;;;;Y;;;;;
+276C;MEDIUM LEFT-POINTING ANGLE BRACKET ORNAMENT;Ps;0;ON;;;;;Y;;;;;
+276D;MEDIUM RIGHT-POINTING ANGLE BRACKET ORNAMENT;Pe;0;ON;;;;;Y;;;;;
+276E;HEAVY LEFT-POINTING ANGLE QUOTATION MARK ORNAMENT;Ps;0;ON;;;;;Y;;;;;
+276F;HEAVY RIGHT-POINTING ANGLE QUOTATION MARK ORNAMENT;Pe;0;ON;;;;;Y;;;;;
+2770;HEAVY LEFT-POINTING ANGLE BRACKET ORNAMENT;Ps;0;ON;;;;;Y;;;;;
+2771;HEAVY RIGHT-POINTING ANGLE BRACKET ORNAMENT;Pe;0;ON;;;;;Y;;;;;
+2772;LIGHT LEFT TORTOISE SHELL BRACKET ORNAMENT;Ps;0;ON;;;;;Y;;;;;
+2773;LIGHT RIGHT TORTOISE SHELL BRACKET ORNAMENT;Pe;0;ON;;;;;Y;;;;;
+2774;MEDIUM LEFT CURLY BRACKET ORNAMENT;Ps;0;ON;;;;;Y;;;;;
+2775;MEDIUM RIGHT CURLY BRACKET ORNAMENT;Pe;0;ON;;;;;Y;;;;;
+2776;DINGBAT NEGATIVE CIRCLED DIGIT ONE;No;0;ON;;;1;1;N;INVERSE CIRCLED DIGIT ONE;;;;
+2777;DINGBAT NEGATIVE CIRCLED DIGIT TWO;No;0;ON;;;2;2;N;INVERSE CIRCLED DIGIT TWO;;;;
+2778;DINGBAT NEGATIVE CIRCLED DIGIT THREE;No;0;ON;;;3;3;N;INVERSE CIRCLED DIGIT THREE;;;;
+2779;DINGBAT NEGATIVE CIRCLED DIGIT FOUR;No;0;ON;;;4;4;N;INVERSE CIRCLED DIGIT FOUR;;;;
+277A;DINGBAT NEGATIVE CIRCLED DIGIT FIVE;No;0;ON;;;5;5;N;INVERSE CIRCLED DIGIT FIVE;;;;
+277B;DINGBAT NEGATIVE CIRCLED DIGIT SIX;No;0;ON;;;6;6;N;INVERSE CIRCLED DIGIT SIX;;;;
+277C;DINGBAT NEGATIVE CIRCLED DIGIT SEVEN;No;0;ON;;;7;7;N;INVERSE CIRCLED DIGIT SEVEN;;;;
+277D;DINGBAT NEGATIVE CIRCLED DIGIT EIGHT;No;0;ON;;;8;8;N;INVERSE CIRCLED DIGIT EIGHT;;;;
+277E;DINGBAT NEGATIVE CIRCLED DIGIT NINE;No;0;ON;;;9;9;N;INVERSE CIRCLED DIGIT NINE;;;;
+277F;DINGBAT NEGATIVE CIRCLED NUMBER TEN;No;0;ON;;;;10;N;INVERSE CIRCLED NUMBER TEN;;;;
+2780;DINGBAT CIRCLED SANS-SERIF DIGIT ONE;No;0;ON;;;1;1;N;CIRCLED SANS-SERIF DIGIT ONE;;;;
+2781;DINGBAT CIRCLED SANS-SERIF DIGIT TWO;No;0;ON;;;2;2;N;CIRCLED SANS-SERIF DIGIT TWO;;;;
+2782;DINGBAT CIRCLED SANS-SERIF DIGIT THREE;No;0;ON;;;3;3;N;CIRCLED SANS-SERIF DIGIT THREE;;;;
+2783;DINGBAT CIRCLED SANS-SERIF DIGIT FOUR;No;0;ON;;;4;4;N;CIRCLED SANS-SERIF DIGIT FOUR;;;;
+2784;DINGBAT CIRCLED SANS-SERIF DIGIT FIVE;No;0;ON;;;5;5;N;CIRCLED SANS-SERIF DIGIT FIVE;;;;
+2785;DINGBAT CIRCLED SANS-SERIF DIGIT SIX;No;0;ON;;;6;6;N;CIRCLED SANS-SERIF DIGIT SIX;;;;
+2786;DINGBAT CIRCLED SANS-SERIF DIGIT SEVEN;No;0;ON;;;7;7;N;CIRCLED SANS-SERIF DIGIT SEVEN;;;;
+2787;DINGBAT CIRCLED SANS-SERIF DIGIT EIGHT;No;0;ON;;;8;8;N;CIRCLED SANS-SERIF DIGIT EIGHT;;;;
+2788;DINGBAT CIRCLED SANS-SERIF DIGIT NINE;No;0;ON;;;9;9;N;CIRCLED SANS-SERIF DIGIT NINE;;;;
+2789;DINGBAT CIRCLED SANS-SERIF NUMBER TEN;No;0;ON;;;;10;N;CIRCLED SANS-SERIF NUMBER TEN;;;;
+278A;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT ONE;No;0;ON;;;1;1;N;INVERSE CIRCLED SANS-SERIF DIGIT ONE;;;;
+278B;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT TWO;No;0;ON;;;2;2;N;INVERSE CIRCLED SANS-SERIF DIGIT TWO;;;;
+278C;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT THREE;No;0;ON;;;3;3;N;INVERSE CIRCLED SANS-SERIF DIGIT THREE;;;;
+278D;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT FOUR;No;0;ON;;;4;4;N;INVERSE CIRCLED SANS-SERIF DIGIT FOUR;;;;
+278E;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT FIVE;No;0;ON;;;5;5;N;INVERSE CIRCLED SANS-SERIF DIGIT FIVE;;;;
+278F;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT SIX;No;0;ON;;;6;6;N;INVERSE CIRCLED SANS-SERIF DIGIT SIX;;;;
+2790;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT SEVEN;No;0;ON;;;7;7;N;INVERSE CIRCLED SANS-SERIF DIGIT SEVEN;;;;
+2791;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT EIGHT;No;0;ON;;;8;8;N;INVERSE CIRCLED SANS-SERIF DIGIT EIGHT;;;;
+2792;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT NINE;No;0;ON;;;9;9;N;INVERSE CIRCLED SANS-SERIF DIGIT NINE;;;;
+2793;DINGBAT NEGATIVE CIRCLED SANS-SERIF NUMBER TEN;No;0;ON;;;;10;N;INVERSE CIRCLED SANS-SERIF NUMBER TEN;;;;
+2794;HEAVY WIDE-HEADED RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY WIDE-HEADED RIGHT ARROW;;;;
+2798;HEAVY SOUTH EAST ARROW;So;0;ON;;;;;N;HEAVY LOWER RIGHT ARROW;;;;
+2799;HEAVY RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY RIGHT ARROW;;;;
+279A;HEAVY NORTH EAST ARROW;So;0;ON;;;;;N;HEAVY UPPER RIGHT ARROW;;;;
+279B;DRAFTING POINT RIGHTWARDS ARROW;So;0;ON;;;;;N;DRAFTING POINT RIGHT ARROW;;;;
+279C;HEAVY ROUND-TIPPED RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY ROUND-TIPPED RIGHT ARROW;;;;
+279D;TRIANGLE-HEADED RIGHTWARDS ARROW;So;0;ON;;;;;N;TRIANGLE-HEADED RIGHT ARROW;;;;
+279E;HEAVY TRIANGLE-HEADED RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY TRIANGLE-HEADED RIGHT ARROW;;;;
+279F;DASHED TRIANGLE-HEADED RIGHTWARDS ARROW;So;0;ON;;;;;N;DASHED TRIANGLE-HEADED RIGHT ARROW;;;;
+27A0;HEAVY DASHED TRIANGLE-HEADED RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY DASHED TRIANGLE-HEADED RIGHT ARROW;;;;
+27A1;BLACK RIGHTWARDS ARROW;So;0;ON;;;;;N;BLACK RIGHT ARROW;;;;
+27A2;THREE-D TOP-LIGHTED RIGHTWARDS ARROWHEAD;So;0;ON;;;;;N;THREE-D TOP-LIGHTED RIGHT ARROWHEAD;;;;
+27A3;THREE-D BOTTOM-LIGHTED RIGHTWARDS ARROWHEAD;So;0;ON;;;;;N;THREE-D BOTTOM-LIGHTED RIGHT ARROWHEAD;;;;
+27A4;BLACK RIGHTWARDS ARROWHEAD;So;0;ON;;;;;N;BLACK RIGHT ARROWHEAD;;;;
+27A5;HEAVY BLACK CURVED DOWNWARDS AND RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY BLACK CURVED DOWN AND RIGHT ARROW;;;;
+27A6;HEAVY BLACK CURVED UPWARDS AND RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY BLACK CURVED UP AND RIGHT ARROW;;;;
+27A7;SQUAT BLACK RIGHTWARDS ARROW;So;0;ON;;;;;N;SQUAT BLACK RIGHT ARROW;;;;
+27A8;HEAVY CONCAVE-POINTED BLACK RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY CONCAVE-POINTED BLACK RIGHT ARROW;;;;
+27A9;RIGHT-SHADED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;RIGHT-SHADED WHITE RIGHT ARROW;;;;
+27AA;LEFT-SHADED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;LEFT-SHADED WHITE RIGHT ARROW;;;;
+27AB;BACK-TILTED SHADOWED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;BACK-TILTED SHADOWED WHITE RIGHT ARROW;;;;
+27AC;FRONT-TILTED SHADOWED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;FRONT-TILTED SHADOWED WHITE RIGHT ARROW;;;;
+27AD;HEAVY LOWER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY LOWER RIGHT-SHADOWED WHITE RIGHT ARROW;;;;
+27AE;HEAVY UPPER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY UPPER RIGHT-SHADOWED WHITE RIGHT ARROW;;;;
+27AF;NOTCHED LOWER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;NOTCHED LOWER RIGHT-SHADOWED WHITE RIGHT ARROW;;;;
+27B1;NOTCHED UPPER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;NOTCHED UPPER RIGHT-SHADOWED WHITE RIGHT ARROW;;;;
+27B2;CIRCLED HEAVY WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;CIRCLED HEAVY WHITE RIGHT ARROW;;;;
+27B3;WHITE-FEATHERED RIGHTWARDS ARROW;So;0;ON;;;;;N;WHITE-FEATHERED RIGHT ARROW;;;;
+27B4;BLACK-FEATHERED SOUTH EAST ARROW;So;0;ON;;;;;N;BLACK-FEATHERED LOWER RIGHT ARROW;;;;
+27B5;BLACK-FEATHERED RIGHTWARDS ARROW;So;0;ON;;;;;N;BLACK-FEATHERED RIGHT ARROW;;;;
+27B6;BLACK-FEATHERED NORTH EAST ARROW;So;0;ON;;;;;N;BLACK-FEATHERED UPPER RIGHT ARROW;;;;
+27B7;HEAVY BLACK-FEATHERED SOUTH EAST ARROW;So;0;ON;;;;;N;HEAVY BLACK-FEATHERED LOWER RIGHT ARROW;;;;
+27B8;HEAVY BLACK-FEATHERED RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY BLACK-FEATHERED RIGHT ARROW;;;;
+27B9;HEAVY BLACK-FEATHERED NORTH EAST ARROW;So;0;ON;;;;;N;HEAVY BLACK-FEATHERED UPPER RIGHT ARROW;;;;
+27BA;TEARDROP-BARBED RIGHTWARDS ARROW;So;0;ON;;;;;N;TEARDROP-BARBED RIGHT ARROW;;;;
+27BB;HEAVY TEARDROP-SHANKED RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY TEARDROP-SHANKED RIGHT ARROW;;;;
+27BC;WEDGE-TAILED RIGHTWARDS ARROW;So;0;ON;;;;;N;WEDGE-TAILED RIGHT ARROW;;;;
+27BD;HEAVY WEDGE-TAILED RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY WEDGE-TAILED RIGHT ARROW;;;;
+27BE;OPEN-OUTLINED RIGHTWARDS ARROW;So;0;ON;;;;;N;OPEN-OUTLINED RIGHT ARROW;;;;
+27D0;WHITE DIAMOND WITH CENTRED DOT;Sm;0;ON;;;;;N;;;;;
+27D1;AND WITH DOT;Sm;0;ON;;;;;N;;;;;
+27D2;ELEMENT OF OPENING UPWARDS;Sm;0;ON;;;;;N;;;;;
+27D3;LOWER RIGHT CORNER WITH DOT;Sm;0;ON;;;;;Y;;;;;
+27D4;UPPER LEFT CORNER WITH DOT;Sm;0;ON;;;;;Y;;;;;
+27D5;LEFT OUTER JOIN;Sm;0;ON;;;;;Y;;;;;
+27D6;RIGHT OUTER JOIN;Sm;0;ON;;;;;Y;;;;;
+27D7;FULL OUTER JOIN;Sm;0;ON;;;;;N;;;;;
+27D8;LARGE UP TACK;Sm;0;ON;;;;;N;;;;;
+27D9;LARGE DOWN TACK;Sm;0;ON;;;;;N;;;;;
+27DA;LEFT AND RIGHT DOUBLE TURNSTILE;Sm;0;ON;;;;;N;;;;;
+27DB;LEFT AND RIGHT TACK;Sm;0;ON;;;;;N;;;;;
+27DC;LEFT MULTIMAP;Sm;0;ON;;;;;Y;;;;;
+27DD;LONG RIGHT TACK;Sm;0;ON;;;;;Y;;;;;
+27DE;LONG LEFT TACK;Sm;0;ON;;;;;Y;;;;;
+27DF;UP TACK WITH CIRCLE ABOVE;Sm;0;ON;;;;;N;;;;;
+27E0;LOZENGE DIVIDED BY HORIZONTAL RULE;Sm;0;ON;;;;;N;;;;;
+27E1;WHITE CONCAVE-SIDED DIAMOND;Sm;0;ON;;;;;N;;;;;
+27E2;WHITE CONCAVE-SIDED DIAMOND WITH LEFTWARDS TICK;Sm;0;ON;;;;;Y;;;;;
+27E3;WHITE CONCAVE-SIDED DIAMOND WITH RIGHTWARDS TICK;Sm;0;ON;;;;;Y;;;;;
+27E4;WHITE SQUARE WITH LEFTWARDS TICK;Sm;0;ON;;;;;Y;;;;;
+27E5;WHITE SQUARE WITH RIGHTWARDS TICK;Sm;0;ON;;;;;Y;;;;;
+27E6;MATHEMATICAL LEFT WHITE SQUARE BRACKET;Ps;0;ON;;;;;Y;;;;;
+27E7;MATHEMATICAL RIGHT WHITE SQUARE BRACKET;Pe;0;ON;;;;;Y;;;;;
+27E8;MATHEMATICAL LEFT ANGLE BRACKET;Ps;0;ON;;;;;Y;;;;;
+27E9;MATHEMATICAL RIGHT ANGLE BRACKET;Pe;0;ON;;;;;Y;;;;;
+27EA;MATHEMATICAL LEFT DOUBLE ANGLE BRACKET;Ps;0;ON;;;;;Y;;;;;
+27EB;MATHEMATICAL RIGHT DOUBLE ANGLE BRACKET;Pe;0;ON;;;;;Y;;;;;
+27F0;UPWARDS QUADRUPLE ARROW;Sm;0;ON;;;;;N;;;;;
+27F1;DOWNWARDS QUADRUPLE ARROW;Sm;0;ON;;;;;N;;;;;
+27F2;ANTICLOCKWISE GAPPED CIRCLE ARROW;Sm;0;ON;;;;;N;;;;;
+27F3;CLOCKWISE GAPPED CIRCLE ARROW;Sm;0;ON;;;;;N;;;;;
+27F4;RIGHT ARROW WITH CIRCLED PLUS;Sm;0;ON;;;;;N;;;;;
+27F5;LONG LEFTWARDS ARROW;Sm;0;ON;;;;;N;;;;;
+27F6;LONG RIGHTWARDS ARROW;Sm;0;ON;;;;;N;;;;;
+27F7;LONG LEFT RIGHT ARROW;Sm;0;ON;;;;;N;;;;;
+27F8;LONG LEFTWARDS DOUBLE ARROW;Sm;0;ON;;;;;N;;;;;
+27F9;LONG RIGHTWARDS DOUBLE ARROW;Sm;0;ON;;;;;N;;;;;
+27FA;LONG LEFT RIGHT DOUBLE ARROW;Sm;0;ON;;;;;N;;;;;
+27FB;LONG LEFTWARDS ARROW FROM BAR;Sm;0;ON;;;;;N;;;;;
+27FC;LONG RIGHTWARDS ARROW FROM BAR;Sm;0;ON;;;;;N;;;;;
+27FD;LONG LEFTWARDS DOUBLE ARROW FROM BAR;Sm;0;ON;;;;;N;;;;;
+27FE;LONG RIGHTWARDS DOUBLE ARROW FROM BAR;Sm;0;ON;;;;;N;;;;;
+27FF;LONG RIGHTWARDS SQUIGGLE ARROW;Sm;0;ON;;;;;N;;;;;
+2800;BRAILLE PATTERN BLANK;So;0;L;;;;;N;;;;;
+2801;BRAILLE PATTERN DOTS-1;So;0;L;;;;;N;;;;;
+2802;BRAILLE PATTERN DOTS-2;So;0;L;;;;;N;;;;;
+2803;BRAILLE PATTERN DOTS-12;So;0;L;;;;;N;;;;;
+2804;BRAILLE PATTERN DOTS-3;So;0;L;;;;;N;;;;;
+2805;BRAILLE PATTERN DOTS-13;So;0;L;;;;;N;;;;;
+2806;BRAILLE PATTERN DOTS-23;So;0;L;;;;;N;;;;;
+2807;BRAILLE PATTERN DOTS-123;So;0;L;;;;;N;;;;;
+2808;BRAILLE PATTERN DOTS-4;So;0;L;;;;;N;;;;;
+2809;BRAILLE PATTERN DOTS-14;So;0;L;;;;;N;;;;;
+280A;BRAILLE PATTERN DOTS-24;So;0;L;;;;;N;;;;;
+280B;BRAILLE PATTERN DOTS-124;So;0;L;;;;;N;;;;;
+280C;BRAILLE PATTERN DOTS-34;So;0;L;;;;;N;;;;;
+280D;BRAILLE PATTERN DOTS-134;So;0;L;;;;;N;;;;;
+280E;BRAILLE PATTERN DOTS-234;So;0;L;;;;;N;;;;;
+280F;BRAILLE PATTERN DOTS-1234;So;0;L;;;;;N;;;;;
+2810;BRAILLE PATTERN DOTS-5;So;0;L;;;;;N;;;;;
+2811;BRAILLE PATTERN DOTS-15;So;0;L;;;;;N;;;;;
+2812;BRAILLE PATTERN DOTS-25;So;0;L;;;;;N;;;;;
+2813;BRAILLE PATTERN DOTS-125;So;0;L;;;;;N;;;;;
+2814;BRAILLE PATTERN DOTS-35;So;0;L;;;;;N;;;;;
+2815;BRAILLE PATTERN DOTS-135;So;0;L;;;;;N;;;;;
+2816;BRAILLE PATTERN DOTS-235;So;0;L;;;;;N;;;;;
+2817;BRAILLE PATTERN DOTS-1235;So;0;L;;;;;N;;;;;
+2818;BRAILLE PATTERN DOTS-45;So;0;L;;;;;N;;;;;
+2819;BRAILLE PATTERN DOTS-145;So;0;L;;;;;N;;;;;
+281A;BRAILLE PATTERN DOTS-245;So;0;L;;;;;N;;;;;
+281B;BRAILLE PATTERN DOTS-1245;So;0;L;;;;;N;;;;;
+281C;BRAILLE PATTERN DOTS-345;So;0;L;;;;;N;;;;;
+281D;BRAILLE PATTERN DOTS-1345;So;0;L;;;;;N;;;;;
+281E;BRAILLE PATTERN DOTS-2345;So;0;L;;;;;N;;;;;
+281F;BRAILLE PATTERN DOTS-12345;So;0;L;;;;;N;;;;;
+2820;BRAILLE PATTERN DOTS-6;So;0;L;;;;;N;;;;;
+2821;BRAILLE PATTERN DOTS-16;So;0;L;;;;;N;;;;;
+2822;BRAILLE PATTERN DOTS-26;So;0;L;;;;;N;;;;;
+2823;BRAILLE PATTERN DOTS-126;So;0;L;;;;;N;;;;;
+2824;BRAILLE PATTERN DOTS-36;So;0;L;;;;;N;;;;;
+2825;BRAILLE PATTERN DOTS-136;So;0;L;;;;;N;;;;;
+2826;BRAILLE PATTERN DOTS-236;So;0;L;;;;;N;;;;;
+2827;BRAILLE PATTERN DOTS-1236;So;0;L;;;;;N;;;;;
+2828;BRAILLE PATTERN DOTS-46;So;0;L;;;;;N;;;;;
+2829;BRAILLE PATTERN DOTS-146;So;0;L;;;;;N;;;;;
+282A;BRAILLE PATTERN DOTS-246;So;0;L;;;;;N;;;;;
+282B;BRAILLE PATTERN DOTS-1246;So;0;L;;;;;N;;;;;
+282C;BRAILLE PATTERN DOTS-346;So;0;L;;;;;N;;;;;
+282D;BRAILLE PATTERN DOTS-1346;So;0;L;;;;;N;;;;;
+282E;BRAILLE PATTERN DOTS-2346;So;0;L;;;;;N;;;;;
+282F;BRAILLE PATTERN DOTS-12346;So;0;L;;;;;N;;;;;
+2830;BRAILLE PATTERN DOTS-56;So;0;L;;;;;N;;;;;
+2831;BRAILLE PATTERN DOTS-156;So;0;L;;;;;N;;;;;
+2832;BRAILLE PATTERN DOTS-256;So;0;L;;;;;N;;;;;
+2833;BRAILLE PATTERN DOTS-1256;So;0;L;;;;;N;;;;;
+2834;BRAILLE PATTERN DOTS-356;So;0;L;;;;;N;;;;;
+2835;BRAILLE PATTERN DOTS-1356;So;0;L;;;;;N;;;;;
+2836;BRAILLE PATTERN DOTS-2356;So;0;L;;;;;N;;;;;
+2837;BRAILLE PATTERN DOTS-12356;So;0;L;;;;;N;;;;;
+2838;BRAILLE PATTERN DOTS-456;So;0;L;;;;;N;;;;;
+2839;BRAILLE PATTERN DOTS-1456;So;0;L;;;;;N;;;;;
+283A;BRAILLE PATTERN DOTS-2456;So;0;L;;;;;N;;;;;
+283B;BRAILLE PATTERN DOTS-12456;So;0;L;;;;;N;;;;;
+283C;BRAILLE PATTERN DOTS-3456;So;0;L;;;;;N;;;;;
+283D;BRAILLE PATTERN DOTS-13456;So;0;L;;;;;N;;;;;
+283E;BRAILLE PATTERN DOTS-23456;So;0;L;;;;;N;;;;;
+283F;BRAILLE PATTERN DOTS-123456;So;0;L;;;;;N;;;;;
+2840;BRAILLE PATTERN DOTS-7;So;0;L;;;;;N;;;;;
+2841;BRAILLE PATTERN DOTS-17;So;0;L;;;;;N;;;;;
+2842;BRAILLE PATTERN DOTS-27;So;0;L;;;;;N;;;;;
+2843;BRAILLE PATTERN DOTS-127;So;0;L;;;;;N;;;;;
+2844;BRAILLE PATTERN DOTS-37;So;0;L;;;;;N;;;;;
+2845;BRAILLE PATTERN DOTS-137;So;0;L;;;;;N;;;;;
+2846;BRAILLE PATTERN DOTS-237;So;0;L;;;;;N;;;;;
+2847;BRAILLE PATTERN DOTS-1237;So;0;L;;;;;N;;;;;
+2848;BRAILLE PATTERN DOTS-47;So;0;L;;;;;N;;;;;
+2849;BRAILLE PATTERN DOTS-147;So;0;L;;;;;N;;;;;
+284A;BRAILLE PATTERN DOTS-247;So;0;L;;;;;N;;;;;
+284B;BRAILLE PATTERN DOTS-1247;So;0;L;;;;;N;;;;;
+284C;BRAILLE PATTERN DOTS-347;So;0;L;;;;;N;;;;;
+284D;BRAILLE PATTERN DOTS-1347;So;0;L;;;;;N;;;;;
+284E;BRAILLE PATTERN DOTS-2347;So;0;L;;;;;N;;;;;
+284F;BRAILLE PATTERN DOTS-12347;So;0;L;;;;;N;;;;;
+2850;BRAILLE PATTERN DOTS-57;So;0;L;;;;;N;;;;;
+2851;BRAILLE PATTERN DOTS-157;So;0;L;;;;;N;;;;;
+2852;BRAILLE PATTERN DOTS-257;So;0;L;;;;;N;;;;;
+2853;BRAILLE PATTERN DOTS-1257;So;0;L;;;;;N;;;;;
+2854;BRAILLE PATTERN DOTS-357;So;0;L;;;;;N;;;;;
+2855;BRAILLE PATTERN DOTS-1357;So;0;L;;;;;N;;;;;
+2856;BRAILLE PATTERN DOTS-2357;So;0;L;;;;;N;;;;;
+2857;BRAILLE PATTERN DOTS-12357;So;0;L;;;;;N;;;;;
+2858;BRAILLE PATTERN DOTS-457;So;0;L;;;;;N;;;;;
+2859;BRAILLE PATTERN DOTS-1457;So;0;L;;;;;N;;;;;
+285A;BRAILLE PATTERN DOTS-2457;So;0;L;;;;;N;;;;;
+285B;BRAILLE PATTERN DOTS-12457;So;0;L;;;;;N;;;;;
+285C;BRAILLE PATTERN DOTS-3457;So;0;L;;;;;N;;;;;
+285D;BRAILLE PATTERN DOTS-13457;So;0;L;;;;;N;;;;;
+285E;BRAILLE PATTERN DOTS-23457;So;0;L;;;;;N;;;;;
+285F;BRAILLE PATTERN DOTS-123457;So;0;L;;;;;N;;;;;
+2860;BRAILLE PATTERN DOTS-67;So;0;L;;;;;N;;;;;
+2861;BRAILLE PATTERN DOTS-167;So;0;L;;;;;N;;;;;
+2862;BRAILLE PATTERN DOTS-267;So;0;L;;;;;N;;;;;
+2863;BRAILLE PATTERN DOTS-1267;So;0;L;;;;;N;;;;;
+2864;BRAILLE PATTERN DOTS-367;So;0;L;;;;;N;;;;;
+2865;BRAILLE PATTERN DOTS-1367;So;0;L;;;;;N;;;;;
+2866;BRAILLE PATTERN DOTS-2367;So;0;L;;;;;N;;;;;
+2867;BRAILLE PATTERN DOTS-12367;So;0;L;;;;;N;;;;;
+2868;BRAILLE PATTERN DOTS-467;So;0;L;;;;;N;;;;;
+2869;BRAILLE PATTERN DOTS-1467;So;0;L;;;;;N;;;;;
+286A;BRAILLE PATTERN DOTS-2467;So;0;L;;;;;N;;;;;
+286B;BRAILLE PATTERN DOTS-12467;So;0;L;;;;;N;;;;;
+286C;BRAILLE PATTERN DOTS-3467;So;0;L;;;;;N;;;;;
+286D;BRAILLE PATTERN DOTS-13467;So;0;L;;;;;N;;;;;
+286E;BRAILLE PATTERN DOTS-23467;So;0;L;;;;;N;;;;;
+286F;BRAILLE PATTERN DOTS-123467;So;0;L;;;;;N;;;;;
+2870;BRAILLE PATTERN DOTS-567;So;0;L;;;;;N;;;;;
+2871;BRAILLE PATTERN DOTS-1567;So;0;L;;;;;N;;;;;
+2872;BRAILLE PATTERN DOTS-2567;So;0;L;;;;;N;;;;;
+2873;BRAILLE PATTERN DOTS-12567;So;0;L;;;;;N;;;;;
+2874;BRAILLE PATTERN DOTS-3567;So;0;L;;;;;N;;;;;
+2875;BRAILLE PATTERN DOTS-13567;So;0;L;;;;;N;;;;;
+2876;BRAILLE PATTERN DOTS-23567;So;0;L;;;;;N;;;;;
+2877;BRAILLE PATTERN DOTS-123567;So;0;L;;;;;N;;;;;
+2878;BRAILLE PATTERN DOTS-4567;So;0;L;;;;;N;;;;;
+2879;BRAILLE PATTERN DOTS-14567;So;0;L;;;;;N;;;;;
+287A;BRAILLE PATTERN DOTS-24567;So;0;L;;;;;N;;;;;
+287B;BRAILLE PATTERN DOTS-124567;So;0;L;;;;;N;;;;;
+287C;BRAILLE PATTERN DOTS-34567;So;0;L;;;;;N;;;;;
+287D;BRAILLE PATTERN DOTS-134567;So;0;L;;;;;N;;;;;
+287E;BRAILLE PATTERN DOTS-234567;So;0;L;;;;;N;;;;;
+287F;BRAILLE PATTERN DOTS-1234567;So;0;L;;;;;N;;;;;
+2880;BRAILLE PATTERN DOTS-8;So;0;L;;;;;N;;;;;
+2881;BRAILLE PATTERN DOTS-18;So;0;L;;;;;N;;;;;
+2882;BRAILLE PATTERN DOTS-28;So;0;L;;;;;N;;;;;
+2883;BRAILLE PATTERN DOTS-128;So;0;L;;;;;N;;;;;
+2884;BRAILLE PATTERN DOTS-38;So;0;L;;;;;N;;;;;
+2885;BRAILLE PATTERN DOTS-138;So;0;L;;;;;N;;;;;
+2886;BRAILLE PATTERN DOTS-238;So;0;L;;;;;N;;;;;
+2887;BRAILLE PATTERN DOTS-1238;So;0;L;;;;;N;;;;;
+2888;BRAILLE PATTERN DOTS-48;So;0;L;;;;;N;;;;;
+2889;BRAILLE PATTERN DOTS-148;So;0;L;;;;;N;;;;;
+288A;BRAILLE PATTERN DOTS-248;So;0;L;;;;;N;;;;;
+288B;BRAILLE PATTERN DOTS-1248;So;0;L;;;;;N;;;;;
+288C;BRAILLE PATTERN DOTS-348;So;0;L;;;;;N;;;;;
+288D;BRAILLE PATTERN DOTS-1348;So;0;L;;;;;N;;;;;
+288E;BRAILLE PATTERN DOTS-2348;So;0;L;;;;;N;;;;;
+288F;BRAILLE PATTERN DOTS-12348;So;0;L;;;;;N;;;;;
+2890;BRAILLE PATTERN DOTS-58;So;0;L;;;;;N;;;;;
+2891;BRAILLE PATTERN DOTS-158;So;0;L;;;;;N;;;;;
+2892;BRAILLE PATTERN DOTS-258;So;0;L;;;;;N;;;;;
+2893;BRAILLE PATTERN DOTS-1258;So;0;L;;;;;N;;;;;
+2894;BRAILLE PATTERN DOTS-358;So;0;L;;;;;N;;;;;
+2895;BRAILLE PATTERN DOTS-1358;So;0;L;;;;;N;;;;;
+2896;BRAILLE PATTERN DOTS-2358;So;0;L;;;;;N;;;;;
+2897;BRAILLE PATTERN DOTS-12358;So;0;L;;;;;N;;;;;
+2898;BRAILLE PATTERN DOTS-458;So;0;L;;;;;N;;;;;
+2899;BRAILLE PATTERN DOTS-1458;So;0;L;;;;;N;;;;;
+289A;BRAILLE PATTERN DOTS-2458;So;0;L;;;;;N;;;;;
+289B;BRAILLE PATTERN DOTS-12458;So;0;L;;;;;N;;;;;
+289C;BRAILLE PATTERN DOTS-3458;So;0;L;;;;;N;;;;;
+289D;BRAILLE PATTERN DOTS-13458;So;0;L;;;;;N;;;;;
+289E;BRAILLE PATTERN DOTS-23458;So;0;L;;;;;N;;;;;
+289F;BRAILLE PATTERN DOTS-123458;So;0;L;;;;;N;;;;;
+28A0;BRAILLE PATTERN DOTS-68;So;0;L;;;;;N;;;;;
+28A1;BRAILLE PATTERN DOTS-168;So;0;L;;;;;N;;;;;
+28A2;BRAILLE PATTERN DOTS-268;So;0;L;;;;;N;;;;;
+28A3;BRAILLE PATTERN DOTS-1268;So;0;L;;;;;N;;;;;
+28A4;BRAILLE PATTERN DOTS-368;So;0;L;;;;;N;;;;;
+28A5;BRAILLE PATTERN DOTS-1368;So;0;L;;;;;N;;;;;
+28A6;BRAILLE PATTERN DOTS-2368;So;0;L;;;;;N;;;;;
+28A7;BRAILLE PATTERN DOTS-12368;So;0;L;;;;;N;;;;;
+28A8;BRAILLE PATTERN DOTS-468;So;0;L;;;;;N;;;;;
+28A9;BRAILLE PATTERN DOTS-1468;So;0;L;;;;;N;;;;;
+28AA;BRAILLE PATTERN DOTS-2468;So;0;L;;;;;N;;;;;
+28AB;BRAILLE PATTERN DOTS-12468;So;0;L;;;;;N;;;;;
+28AC;BRAILLE PATTERN DOTS-3468;So;0;L;;;;;N;;;;;
+28AD;BRAILLE PATTERN DOTS-13468;So;0;L;;;;;N;;;;;
+28AE;BRAILLE PATTERN DOTS-23468;So;0;L;;;;;N;;;;;
+28AF;BRAILLE PATTERN DOTS-123468;So;0;L;;;;;N;;;;;
+28B0;BRAILLE PATTERN DOTS-568;So;0;L;;;;;N;;;;;
+28B1;BRAILLE PATTERN DOTS-1568;So;0;L;;;;;N;;;;;
+28B2;BRAILLE PATTERN DOTS-2568;So;0;L;;;;;N;;;;;
+28B3;BRAILLE PATTERN DOTS-12568;So;0;L;;;;;N;;;;;
+28B4;BRAILLE PATTERN DOTS-3568;So;0;L;;;;;N;;;;;
+28B5;BRAILLE PATTERN DOTS-13568;So;0;L;;;;;N;;;;;
+28B6;BRAILLE PATTERN DOTS-23568;So;0;L;;;;;N;;;;;
+28B7;BRAILLE PATTERN DOTS-123568;So;0;L;;;;;N;;;;;
+28B8;BRAILLE PATTERN DOTS-4568;So;0;L;;;;;N;;;;;
+28B9;BRAILLE PATTERN DOTS-14568;So;0;L;;;;;N;;;;;
+28BA;BRAILLE PATTERN DOTS-24568;So;0;L;;;;;N;;;;;
+28BB;BRAILLE PATTERN DOTS-124568;So;0;L;;;;;N;;;;;
+28BC;BRAILLE PATTERN DOTS-34568;So;0;L;;;;;N;;;;;
+28BD;BRAILLE PATTERN DOTS-134568;So;0;L;;;;;N;;;;;
+28BE;BRAILLE PATTERN DOTS-234568;So;0;L;;;;;N;;;;;
+28BF;BRAILLE PATTERN DOTS-1234568;So;0;L;;;;;N;;;;;
+28C0;BRAILLE PATTERN DOTS-78;So;0;L;;;;;N;;;;;
+28C1;BRAILLE PATTERN DOTS-178;So;0;L;;;;;N;;;;;
+28C2;BRAILLE PATTERN DOTS-278;So;0;L;;;;;N;;;;;
+28C3;BRAILLE PATTERN DOTS-1278;So;0;L;;;;;N;;;;;
+28C4;BRAILLE PATTERN DOTS-378;So;0;L;;;;;N;;;;;
+28C5;BRAILLE PATTERN DOTS-1378;So;0;L;;;;;N;;;;;
+28C6;BRAILLE PATTERN DOTS-2378;So;0;L;;;;;N;;;;;
+28C7;BRAILLE PATTERN DOTS-12378;So;0;L;;;;;N;;;;;
+28C8;BRAILLE PATTERN DOTS-478;So;0;L;;;;;N;;;;;
+28C9;BRAILLE PATTERN DOTS-1478;So;0;L;;;;;N;;;;;
+28CA;BRAILLE PATTERN DOTS-2478;So;0;L;;;;;N;;;;;
+28CB;BRAILLE PATTERN DOTS-12478;So;0;L;;;;;N;;;;;
+28CC;BRAILLE PATTERN DOTS-3478;So;0;L;;;;;N;;;;;
+28CD;BRAILLE PATTERN DOTS-13478;So;0;L;;;;;N;;;;;
+28CE;BRAILLE PATTERN DOTS-23478;So;0;L;;;;;N;;;;;
+28CF;BRAILLE PATTERN DOTS-123478;So;0;L;;;;;N;;;;;
+28D0;BRAILLE PATTERN DOTS-578;So;0;L;;;;;N;;;;;
+28D1;BRAILLE PATTERN DOTS-1578;So;0;L;;;;;N;;;;;
+28D2;BRAILLE PATTERN DOTS-2578;So;0;L;;;;;N;;;;;
+28D3;BRAILLE PATTERN DOTS-12578;So;0;L;;;;;N;;;;;
+28D4;BRAILLE PATTERN DOTS-3578;So;0;L;;;;;N;;;;;
+28D5;BRAILLE PATTERN DOTS-13578;So;0;L;;;;;N;;;;;
+28D6;BRAILLE PATTERN DOTS-23578;So;0;L;;;;;N;;;;;
+28D7;BRAILLE PATTERN DOTS-123578;So;0;L;;;;;N;;;;;
+28D8;BRAILLE PATTERN DOTS-4578;So;0;L;;;;;N;;;;;
+28D9;BRAILLE PATTERN DOTS-14578;So;0;L;;;;;N;;;;;
+28DA;BRAILLE PATTERN DOTS-24578;So;0;L;;;;;N;;;;;
+28DB;BRAILLE PATTERN DOTS-124578;So;0;L;;;;;N;;;;;
+28DC;BRAILLE PATTERN DOTS-34578;So;0;L;;;;;N;;;;;
+28DD;BRAILLE PATTERN DOTS-134578;So;0;L;;;;;N;;;;;
+28DE;BRAILLE PATTERN DOTS-234578;So;0;L;;;;;N;;;;;
+28DF;BRAILLE PATTERN DOTS-1234578;So;0;L;;;;;N;;;;;
+28E0;BRAILLE PATTERN DOTS-678;So;0;L;;;;;N;;;;;
+28E1;BRAILLE PATTERN DOTS-1678;So;0;L;;;;;N;;;;;
+28E2;BRAILLE PATTERN DOTS-2678;So;0;L;;;;;N;;;;;
+28E3;BRAILLE PATTERN DOTS-12678;So;0;L;;;;;N;;;;;
+28E4;BRAILLE PATTERN DOTS-3678;So;0;L;;;;;N;;;;;
+28E5;BRAILLE PATTERN DOTS-13678;So;0;L;;;;;N;;;;;
+28E6;BRAILLE PATTERN DOTS-23678;So;0;L;;;;;N;;;;;
+28E7;BRAILLE PATTERN DOTS-123678;So;0;L;;;;;N;;;;;
+28E8;BRAILLE PATTERN DOTS-4678;So;0;L;;;;;N;;;;;
+28E9;BRAILLE PATTERN DOTS-14678;So;0;L;;;;;N;;;;;
+28EA;BRAILLE PATTERN DOTS-24678;So;0;L;;;;;N;;;;;
+28EB;BRAILLE PATTERN DOTS-124678;So;0;L;;;;;N;;;;;
+28EC;BRAILLE PATTERN DOTS-34678;So;0;L;;;;;N;;;;;
+28ED;BRAILLE PATTERN DOTS-134678;So;0;L;;;;;N;;;;;
+28EE;BRAILLE PATTERN DOTS-234678;So;0;L;;;;;N;;;;;
+28EF;BRAILLE PATTERN DOTS-1234678;So;0;L;;;;;N;;;;;
+28F0;BRAILLE PATTERN DOTS-5678;So;0;L;;;;;N;;;;;
+28F1;BRAILLE PATTERN DOTS-15678;So;0;L;;;;;N;;;;;
+28F2;BRAILLE PATTERN DOTS-25678;So;0;L;;;;;N;;;;;
+28F3;BRAILLE PATTERN DOTS-125678;So;0;L;;;;;N;;;;;
+28F4;BRAILLE PATTERN DOTS-35678;So;0;L;;;;;N;;;;;
+28F5;BRAILLE PATTERN DOTS-135678;So;0;L;;;;;N;;;;;
+28F6;BRAILLE PATTERN DOTS-235678;So;0;L;;;;;N;;;;;
+28F7;BRAILLE PATTERN DOTS-1235678;So;0;L;;;;;N;;;;;
+28F8;BRAILLE PATTERN DOTS-45678;So;0;L;;;;;N;;;;;
+28F9;BRAILLE PATTERN DOTS-145678;So;0;L;;;;;N;;;;;
+28FA;BRAILLE PATTERN DOTS-245678;So;0;L;;;;;N;;;;;
+28FB;BRAILLE PATTERN DOTS-1245678;So;0;L;;;;;N;;;;;
+28FC;BRAILLE PATTERN DOTS-345678;So;0;L;;;;;N;;;;;
+28FD;BRAILLE PATTERN DOTS-1345678;So;0;L;;;;;N;;;;;
+28FE;BRAILLE PATTERN DOTS-2345678;So;0;L;;;;;N;;;;;
+28FF;BRAILLE PATTERN DOTS-12345678;So;0;L;;;;;N;;;;;
+2900;RIGHTWARDS TWO-HEADED ARROW WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+2901;RIGHTWARDS TWO-HEADED ARROW WITH DOUBLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+2902;LEFTWARDS DOUBLE ARROW WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+2903;RIGHTWARDS DOUBLE ARROW WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+2904;LEFT RIGHT DOUBLE ARROW WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+2905;RIGHTWARDS TWO-HEADED ARROW FROM BAR;Sm;0;ON;;;;;N;;;;;
+2906;LEFTWARDS DOUBLE ARROW FROM BAR;Sm;0;ON;;;;;N;;;;;
+2907;RIGHTWARDS DOUBLE ARROW FROM BAR;Sm;0;ON;;;;;N;;;;;
+2908;DOWNWARDS ARROW WITH HORIZONTAL STROKE;Sm;0;ON;;;;;N;;;;;
+2909;UPWARDS ARROW WITH HORIZONTAL STROKE;Sm;0;ON;;;;;N;;;;;
+290A;UPWARDS TRIPLE ARROW;Sm;0;ON;;;;;N;;;;;
+290B;DOWNWARDS TRIPLE ARROW;Sm;0;ON;;;;;N;;;;;
+290C;LEFTWARDS DOUBLE DASH ARROW;Sm;0;ON;;;;;N;;;;;
+290D;RIGHTWARDS DOUBLE DASH ARROW;Sm;0;ON;;;;;N;;;;;
+290E;LEFTWARDS TRIPLE DASH ARROW;Sm;0;ON;;;;;N;;;;;
+290F;RIGHTWARDS TRIPLE DASH ARROW;Sm;0;ON;;;;;N;;;;;
+2910;RIGHTWARDS TWO-HEADED TRIPLE DASH ARROW;Sm;0;ON;;;;;N;;;;;
+2911;RIGHTWARDS ARROW WITH DOTTED STEM;Sm;0;ON;;;;;N;;;;;
+2912;UPWARDS ARROW TO BAR;Sm;0;ON;;;;;N;;;;;
+2913;DOWNWARDS ARROW TO BAR;Sm;0;ON;;;;;N;;;;;
+2914;RIGHTWARDS ARROW WITH TAIL WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+2915;RIGHTWARDS ARROW WITH TAIL WITH DOUBLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+2916;RIGHTWARDS TWO-HEADED ARROW WITH TAIL;Sm;0;ON;;;;;N;;;;;
+2917;RIGHTWARDS TWO-HEADED ARROW WITH TAIL WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+2918;RIGHTWARDS TWO-HEADED ARROW WITH TAIL WITH DOUBLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+2919;LEFTWARDS ARROW-TAIL;Sm;0;ON;;;;;N;;;;;
+291A;RIGHTWARDS ARROW-TAIL;Sm;0;ON;;;;;N;;;;;
+291B;LEFTWARDS DOUBLE ARROW-TAIL;Sm;0;ON;;;;;N;;;;;
+291C;RIGHTWARDS DOUBLE ARROW-TAIL;Sm;0;ON;;;;;N;;;;;
+291D;LEFTWARDS ARROW TO BLACK DIAMOND;Sm;0;ON;;;;;N;;;;;
+291E;RIGHTWARDS ARROW TO BLACK DIAMOND;Sm;0;ON;;;;;N;;;;;
+291F;LEFTWARDS ARROW FROM BAR TO BLACK DIAMOND;Sm;0;ON;;;;;N;;;;;
+2920;RIGHTWARDS ARROW FROM BAR TO BLACK DIAMOND;Sm;0;ON;;;;;N;;;;;
+2921;NORTH WEST AND SOUTH EAST ARROW;Sm;0;ON;;;;;N;;;;;
+2922;NORTH EAST AND SOUTH WEST ARROW;Sm;0;ON;;;;;N;;;;;
+2923;NORTH WEST ARROW WITH HOOK;Sm;0;ON;;;;;N;;;;;
+2924;NORTH EAST ARROW WITH HOOK;Sm;0;ON;;;;;N;;;;;
+2925;SOUTH EAST ARROW WITH HOOK;Sm;0;ON;;;;;N;;;;;
+2926;SOUTH WEST ARROW WITH HOOK;Sm;0;ON;;;;;N;;;;;
+2927;NORTH WEST ARROW AND NORTH EAST ARROW;Sm;0;ON;;;;;N;;;;;
+2928;NORTH EAST ARROW AND SOUTH EAST ARROW;Sm;0;ON;;;;;N;;;;;
+2929;SOUTH EAST ARROW AND SOUTH WEST ARROW;Sm;0;ON;;;;;N;;;;;
+292A;SOUTH WEST ARROW AND NORTH WEST ARROW;Sm;0;ON;;;;;N;;;;;
+292B;RISING DIAGONAL CROSSING FALLING DIAGONAL;Sm;0;ON;;;;;N;;;;;
+292C;FALLING DIAGONAL CROSSING RISING DIAGONAL;Sm;0;ON;;;;;N;;;;;
+292D;SOUTH EAST ARROW CROSSING NORTH EAST ARROW;Sm;0;ON;;;;;N;;;;;
+292E;NORTH EAST ARROW CROSSING SOUTH EAST ARROW;Sm;0;ON;;;;;N;;;;;
+292F;FALLING DIAGONAL CROSSING NORTH EAST ARROW;Sm;0;ON;;;;;N;;;;;
+2930;RISING DIAGONAL CROSSING SOUTH EAST ARROW;Sm;0;ON;;;;;N;;;;;
+2931;NORTH EAST ARROW CROSSING NORTH WEST ARROW;Sm;0;ON;;;;;N;;;;;
+2932;NORTH WEST ARROW CROSSING NORTH EAST ARROW;Sm;0;ON;;;;;N;;;;;
+2933;WAVE ARROW POINTING DIRECTLY RIGHT;Sm;0;ON;;;;;N;;;;;
+2934;ARROW POINTING RIGHTWARDS THEN CURVING UPWARDS;Sm;0;ON;;;;;N;;;;;
+2935;ARROW POINTING RIGHTWARDS THEN CURVING DOWNWARDS;Sm;0;ON;;;;;N;;;;;
+2936;ARROW POINTING DOWNWARDS THEN CURVING LEFTWARDS;Sm;0;ON;;;;;N;;;;;
+2937;ARROW POINTING DOWNWARDS THEN CURVING RIGHTWARDS;Sm;0;ON;;;;;N;;;;;
+2938;RIGHT-SIDE ARC CLOCKWISE ARROW;Sm;0;ON;;;;;N;;;;;
+2939;LEFT-SIDE ARC ANTICLOCKWISE ARROW;Sm;0;ON;;;;;N;;;;;
+293A;TOP ARC ANTICLOCKWISE ARROW;Sm;0;ON;;;;;N;;;;;
+293B;BOTTOM ARC ANTICLOCKWISE ARROW;Sm;0;ON;;;;;N;;;;;
+293C;TOP ARC CLOCKWISE ARROW WITH MINUS;Sm;0;ON;;;;;N;;;;;
+293D;TOP ARC ANTICLOCKWISE ARROW WITH PLUS;Sm;0;ON;;;;;N;;;;;
+293E;LOWER RIGHT SEMICIRCULAR CLOCKWISE ARROW;Sm;0;ON;;;;;N;;;;;
+293F;LOWER LEFT SEMICIRCULAR ANTICLOCKWISE ARROW;Sm;0;ON;;;;;N;;;;;
+2940;ANTICLOCKWISE CLOSED CIRCLE ARROW;Sm;0;ON;;;;;N;;;;;
+2941;CLOCKWISE CLOSED CIRCLE ARROW;Sm;0;ON;;;;;N;;;;;
+2942;RIGHTWARDS ARROW ABOVE SHORT LEFTWARDS ARROW;Sm;0;ON;;;;;N;;;;;
+2943;LEFTWARDS ARROW ABOVE SHORT RIGHTWARDS ARROW;Sm;0;ON;;;;;N;;;;;
+2944;SHORT RIGHTWARDS ARROW ABOVE LEFTWARDS ARROW;Sm;0;ON;;;;;N;;;;;
+2945;RIGHTWARDS ARROW WITH PLUS BELOW;Sm;0;ON;;;;;N;;;;;
+2946;LEFTWARDS ARROW WITH PLUS BELOW;Sm;0;ON;;;;;N;;;;;
+2947;RIGHTWARDS ARROW THROUGH X;Sm;0;ON;;;;;N;;;;;
+2948;LEFT RIGHT ARROW THROUGH SMALL CIRCLE;Sm;0;ON;;;;;N;;;;;
+2949;UPWARDS TWO-HEADED ARROW FROM SMALL CIRCLE;Sm;0;ON;;;;;N;;;;;
+294A;LEFT BARB UP RIGHT BARB DOWN HARPOON;Sm;0;ON;;;;;N;;;;;
+294B;LEFT BARB DOWN RIGHT BARB UP HARPOON;Sm;0;ON;;;;;N;;;;;
+294C;UP BARB RIGHT DOWN BARB LEFT HARPOON;Sm;0;ON;;;;;N;;;;;
+294D;UP BARB LEFT DOWN BARB RIGHT HARPOON;Sm;0;ON;;;;;N;;;;;
+294E;LEFT BARB UP RIGHT BARB UP HARPOON;Sm;0;ON;;;;;N;;;;;
+294F;UP BARB RIGHT DOWN BARB RIGHT HARPOON;Sm;0;ON;;;;;N;;;;;
+2950;LEFT BARB DOWN RIGHT BARB DOWN HARPOON;Sm;0;ON;;;;;N;;;;;
+2951;UP BARB LEFT DOWN BARB LEFT HARPOON;Sm;0;ON;;;;;N;;;;;
+2952;LEFTWARDS HARPOON WITH BARB UP TO BAR;Sm;0;ON;;;;;N;;;;;
+2953;RIGHTWARDS HARPOON WITH BARB UP TO BAR;Sm;0;ON;;;;;N;;;;;
+2954;UPWARDS HARPOON WITH BARB RIGHT TO BAR;Sm;0;ON;;;;;N;;;;;
+2955;DOWNWARDS HARPOON WITH BARB RIGHT TO BAR;Sm;0;ON;;;;;N;;;;;
+2956;LEFTWARDS HARPOON WITH BARB DOWN TO BAR;Sm;0;ON;;;;;N;;;;;
+2957;RIGHTWARDS HARPOON WITH BARB DOWN TO BAR;Sm;0;ON;;;;;N;;;;;
+2958;UPWARDS HARPOON WITH BARB LEFT TO BAR;Sm;0;ON;;;;;N;;;;;
+2959;DOWNWARDS HARPOON WITH BARB LEFT TO BAR;Sm;0;ON;;;;;N;;;;;
+295A;LEFTWARDS HARPOON WITH BARB UP FROM BAR;Sm;0;ON;;;;;N;;;;;
+295B;RIGHTWARDS HARPOON WITH BARB UP FROM BAR;Sm;0;ON;;;;;N;;;;;
+295C;UPWARDS HARPOON WITH BARB RIGHT FROM BAR;Sm;0;ON;;;;;N;;;;;
+295D;DOWNWARDS HARPOON WITH BARB RIGHT FROM BAR;Sm;0;ON;;;;;N;;;;;
+295E;LEFTWARDS HARPOON WITH BARB DOWN FROM BAR;Sm;0;ON;;;;;N;;;;;
+295F;RIGHTWARDS HARPOON WITH BARB DOWN FROM BAR;Sm;0;ON;;;;;N;;;;;
+2960;UPWARDS HARPOON WITH BARB LEFT FROM BAR;Sm;0;ON;;;;;N;;;;;
+2961;DOWNWARDS HARPOON WITH BARB LEFT FROM BAR;Sm;0;ON;;;;;N;;;;;
+2962;LEFTWARDS HARPOON WITH BARB UP ABOVE LEFTWARDS HARPOON WITH BARB DOWN;Sm;0;ON;;;;;N;;;;;
+2963;UPWARDS HARPOON WITH BARB LEFT BESIDE UPWARDS HARPOON WITH BARB RIGHT;Sm;0;ON;;;;;N;;;;;
+2964;RIGHTWARDS HARPOON WITH BARB UP ABOVE RIGHTWARDS HARPOON WITH BARB DOWN;Sm;0;ON;;;;;N;;;;;
+2965;DOWNWARDS HARPOON WITH BARB LEFT BESIDE DOWNWARDS HARPOON WITH BARB RIGHT;Sm;0;ON;;;;;N;;;;;
+2966;LEFTWARDS HARPOON WITH BARB UP ABOVE RIGHTWARDS HARPOON WITH BARB UP;Sm;0;ON;;;;;N;;;;;
+2967;LEFTWARDS HARPOON WITH BARB DOWN ABOVE RIGHTWARDS HARPOON WITH BARB DOWN;Sm;0;ON;;;;;N;;;;;
+2968;RIGHTWARDS HARPOON WITH BARB UP ABOVE LEFTWARDS HARPOON WITH BARB UP;Sm;0;ON;;;;;N;;;;;
+2969;RIGHTWARDS HARPOON WITH BARB DOWN ABOVE LEFTWARDS HARPOON WITH BARB DOWN;Sm;0;ON;;;;;N;;;;;
+296A;LEFTWARDS HARPOON WITH BARB UP ABOVE LONG DASH;Sm;0;ON;;;;;N;;;;;
+296B;LEFTWARDS HARPOON WITH BARB DOWN BELOW LONG DASH;Sm;0;ON;;;;;N;;;;;
+296C;RIGHTWARDS HARPOON WITH BARB UP ABOVE LONG DASH;Sm;0;ON;;;;;N;;;;;
+296D;RIGHTWARDS HARPOON WITH BARB DOWN BELOW LONG DASH;Sm;0;ON;;;;;N;;;;;
+296E;UPWARDS HARPOON WITH BARB LEFT BESIDE DOWNWARDS HARPOON WITH BARB RIGHT;Sm;0;ON;;;;;N;;;;;
+296F;DOWNWARDS HARPOON WITH BARB LEFT BESIDE UPWARDS HARPOON WITH BARB RIGHT;Sm;0;ON;;;;;N;;;;;
+2970;RIGHT DOUBLE ARROW WITH ROUNDED HEAD;Sm;0;ON;;;;;N;;;;;
+2971;EQUALS SIGN ABOVE RIGHTWARDS ARROW;Sm;0;ON;;;;;N;;;;;
+2972;TILDE OPERATOR ABOVE RIGHTWARDS ARROW;Sm;0;ON;;;;;N;;;;;
+2973;LEFTWARDS ARROW ABOVE TILDE OPERATOR;Sm;0;ON;;;;;N;;;;;
+2974;RIGHTWARDS ARROW ABOVE TILDE OPERATOR;Sm;0;ON;;;;;N;;;;;
+2975;RIGHTWARDS ARROW ABOVE ALMOST EQUAL TO;Sm;0;ON;;;;;N;;;;;
+2976;LESS-THAN ABOVE LEFTWARDS ARROW;Sm;0;ON;;;;;N;;;;;
+2977;LEFTWARDS ARROW THROUGH LESS-THAN;Sm;0;ON;;;;;N;;;;;
+2978;GREATER-THAN ABOVE RIGHTWARDS ARROW;Sm;0;ON;;;;;N;;;;;
+2979;SUBSET ABOVE RIGHTWARDS ARROW;Sm;0;ON;;;;;N;;;;;
+297A;LEFTWARDS ARROW THROUGH SUBSET;Sm;0;ON;;;;;N;;;;;
+297B;SUPERSET ABOVE LEFTWARDS ARROW;Sm;0;ON;;;;;N;;;;;
+297C;LEFT FISH TAIL;Sm;0;ON;;;;;N;;;;;
+297D;RIGHT FISH TAIL;Sm;0;ON;;;;;N;;;;;
+297E;UP FISH TAIL;Sm;0;ON;;;;;N;;;;;
+297F;DOWN FISH TAIL;Sm;0;ON;;;;;N;;;;;
+2980;TRIPLE VERTICAL BAR DELIMITER;Sm;0;ON;;;;;N;;;;;
+2981;Z NOTATION SPOT;Sm;0;ON;;;;;N;;;;;
+2982;Z NOTATION TYPE COLON;Sm;0;ON;;;;;N;;;;;
+2983;LEFT WHITE CURLY BRACKET;Ps;0;ON;;;;;Y;;;;;
+2984;RIGHT WHITE CURLY BRACKET;Pe;0;ON;;;;;Y;;;;;
+2985;LEFT WHITE PARENTHESIS;Ps;0;ON;;;;;Y;;;;;
+2986;RIGHT WHITE PARENTHESIS;Pe;0;ON;;;;;Y;;;;;
+2987;Z NOTATION LEFT IMAGE BRACKET;Ps;0;ON;;;;;Y;;;;;
+2988;Z NOTATION RIGHT IMAGE BRACKET;Pe;0;ON;;;;;Y;;;;;
+2989;Z NOTATION LEFT BINDING BRACKET;Ps;0;ON;;;;;Y;;;;;
+298A;Z NOTATION RIGHT BINDING BRACKET;Pe;0;ON;;;;;Y;;;;;
+298B;LEFT SQUARE BRACKET WITH UNDERBAR;Ps;0;ON;;;;;Y;;;;;
+298C;RIGHT SQUARE BRACKET WITH UNDERBAR;Pe;0;ON;;;;;Y;;;;;
+298D;LEFT SQUARE BRACKET WITH TICK IN TOP CORNER;Ps;0;ON;;;;;Y;;;;;
+298E;RIGHT SQUARE BRACKET WITH TICK IN BOTTOM CORNER;Pe;0;ON;;;;;Y;;;;;
+298F;LEFT SQUARE BRACKET WITH TICK IN BOTTOM CORNER;Ps;0;ON;;;;;Y;;;;;
+2990;RIGHT SQUARE BRACKET WITH TICK IN TOP CORNER;Pe;0;ON;;;;;Y;;;;;
+2991;LEFT ANGLE BRACKET WITH DOT;Ps;0;ON;;;;;Y;;;;;
+2992;RIGHT ANGLE BRACKET WITH DOT;Pe;0;ON;;;;;Y;;;;;
+2993;LEFT ARC LESS-THAN BRACKET;Ps;0;ON;;;;;Y;;;;;
+2994;RIGHT ARC GREATER-THAN BRACKET;Pe;0;ON;;;;;Y;;;;;
+2995;DOUBLE LEFT ARC GREATER-THAN BRACKET;Ps;0;ON;;;;;Y;;;;;
+2996;DOUBLE RIGHT ARC LESS-THAN BRACKET;Pe;0;ON;;;;;Y;;;;;
+2997;LEFT BLACK TORTOISE SHELL BRACKET;Ps;0;ON;;;;;Y;;;;;
+2998;RIGHT BLACK TORTOISE SHELL BRACKET;Pe;0;ON;;;;;Y;;;;;
+2999;DOTTED FENCE;Sm;0;ON;;;;;N;;;;;
+299A;VERTICAL ZIGZAG LINE;Sm;0;ON;;;;;N;;;;;
+299B;MEASURED ANGLE OPENING LEFT;Sm;0;ON;;;;;Y;;;;;
+299C;RIGHT ANGLE VARIANT WITH SQUARE;Sm;0;ON;;;;;Y;;;;;
+299D;MEASURED RIGHT ANGLE WITH DOT;Sm;0;ON;;;;;Y;;;;;
+299E;ANGLE WITH S INSIDE;Sm;0;ON;;;;;Y;;;;;
+299F;ACUTE ANGLE;Sm;0;ON;;;;;Y;;;;;
+29A0;SPHERICAL ANGLE OPENING LEFT;Sm;0;ON;;;;;Y;;;;;
+29A1;SPHERICAL ANGLE OPENING UP;Sm;0;ON;;;;;Y;;;;;
+29A2;TURNED ANGLE;Sm;0;ON;;;;;Y;;;;;
+29A3;REVERSED ANGLE;Sm;0;ON;;;;;Y;;;;;
+29A4;ANGLE WITH UNDERBAR;Sm;0;ON;;;;;Y;;;;;
+29A5;REVERSED ANGLE WITH UNDERBAR;Sm;0;ON;;;;;Y;;;;;
+29A6;OBLIQUE ANGLE OPENING UP;Sm;0;ON;;;;;Y;;;;;
+29A7;OBLIQUE ANGLE OPENING DOWN;Sm;0;ON;;;;;Y;;;;;
+29A8;MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING UP AND RIGHT;Sm;0;ON;;;;;Y;;;;;
+29A9;MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING UP AND LEFT;Sm;0;ON;;;;;Y;;;;;
+29AA;MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING DOWN AND RIGHT;Sm;0;ON;;;;;Y;;;;;
+29AB;MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING DOWN AND LEFT;Sm;0;ON;;;;;Y;;;;;
+29AC;MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING RIGHT AND UP;Sm;0;ON;;;;;Y;;;;;
+29AD;MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING LEFT AND UP;Sm;0;ON;;;;;Y;;;;;
+29AE;MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING RIGHT AND DOWN;Sm;0;ON;;;;;Y;;;;;
+29AF;MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING LEFT AND DOWN;Sm;0;ON;;;;;Y;;;;;
+29B0;REVERSED EMPTY SET;Sm;0;ON;;;;;N;;;;;
+29B1;EMPTY SET WITH OVERBAR;Sm;0;ON;;;;;N;;;;;
+29B2;EMPTY SET WITH SMALL CIRCLE ABOVE;Sm;0;ON;;;;;N;;;;;
+29B3;EMPTY SET WITH RIGHT ARROW ABOVE;Sm;0;ON;;;;;N;;;;;
+29B4;EMPTY SET WITH LEFT ARROW ABOVE;Sm;0;ON;;;;;N;;;;;
+29B5;CIRCLE WITH HORIZONTAL BAR;Sm;0;ON;;;;;N;;;;;
+29B6;CIRCLED VERTICAL BAR;Sm;0;ON;;;;;N;;;;;
+29B7;CIRCLED PARALLEL;Sm;0;ON;;;;;N;;;;;
+29B8;CIRCLED REVERSE SOLIDUS;Sm;0;ON;;;;;Y;;;;;
+29B9;CIRCLED PERPENDICULAR;Sm;0;ON;;;;;N;;;;;
+29BA;CIRCLE DIVIDED BY HORIZONTAL BAR AND TOP HALF DIVIDED BY VERTICAL BAR;Sm;0;ON;;;;;N;;;;;
+29BB;CIRCLE WITH SUPERIMPOSED X;Sm;0;ON;;;;;N;;;;;
+29BC;CIRCLED ANTICLOCKWISE-ROTATED DIVISION SIGN;Sm;0;ON;;;;;N;;;;;
+29BD;UP ARROW THROUGH CIRCLE;Sm;0;ON;;;;;N;;;;;
+29BE;CIRCLED WHITE BULLET;Sm;0;ON;;;;;N;;;;;
+29BF;CIRCLED BULLET;Sm;0;ON;;;;;N;;;;;
+29C0;CIRCLED LESS-THAN;Sm;0;ON;;;;;Y;;;;;
+29C1;CIRCLED GREATER-THAN;Sm;0;ON;;;;;Y;;;;;
+29C2;CIRCLE WITH SMALL CIRCLE TO THE RIGHT;Sm;0;ON;;;;;Y;;;;;
+29C3;CIRCLE WITH TWO HORIZONTAL STROKES TO THE RIGHT;Sm;0;ON;;;;;Y;;;;;
+29C4;SQUARED RISING DIAGONAL SLASH;Sm;0;ON;;;;;Y;;;;;
+29C5;SQUARED FALLING DIAGONAL SLASH;Sm;0;ON;;;;;Y;;;;;
+29C6;SQUARED ASTERISK;Sm;0;ON;;;;;N;;;;;
+29C7;SQUARED SMALL CIRCLE;Sm;0;ON;;;;;N;;;;;
+29C8;SQUARED SQUARE;Sm;0;ON;;;;;N;;;;;
+29C9;TWO JOINED SQUARES;Sm;0;ON;;;;;Y;;;;;
+29CA;TRIANGLE WITH DOT ABOVE;Sm;0;ON;;;;;N;;;;;
+29CB;TRIANGLE WITH UNDERBAR;Sm;0;ON;;;;;N;;;;;
+29CC;S IN TRIANGLE;Sm;0;ON;;;;;N;;;;;
+29CD;TRIANGLE WITH SERIFS AT BOTTOM;Sm;0;ON;;;;;N;;;;;
+29CE;RIGHT TRIANGLE ABOVE LEFT TRIANGLE;Sm;0;ON;;;;;Y;;;;;
+29CF;LEFT TRIANGLE BESIDE VERTICAL BAR;Sm;0;ON;;;;;Y;;;;;
+29D0;VERTICAL BAR BESIDE RIGHT TRIANGLE;Sm;0;ON;;;;;Y;;;;;
+29D1;BOWTIE WITH LEFT HALF BLACK;Sm;0;ON;;;;;Y;;;;;
+29D2;BOWTIE WITH RIGHT HALF BLACK;Sm;0;ON;;;;;Y;;;;;
+29D3;BLACK BOWTIE;Sm;0;ON;;;;;N;;;;;
+29D4;TIMES WITH LEFT HALF BLACK;Sm;0;ON;;;;;Y;;;;;
+29D5;TIMES WITH RIGHT HALF BLACK;Sm;0;ON;;;;;Y;;;;;
+29D6;WHITE HOURGLASS;Sm;0;ON;;;;;N;;;;;
+29D7;BLACK HOURGLASS;Sm;0;ON;;;;;N;;;;;
+29D8;LEFT WIGGLY FENCE;Ps;0;ON;;;;;Y;;;;;
+29D9;RIGHT WIGGLY FENCE;Pe;0;ON;;;;;Y;;;;;
+29DA;LEFT DOUBLE WIGGLY FENCE;Ps;0;ON;;;;;Y;;;;;
+29DB;RIGHT DOUBLE WIGGLY FENCE;Pe;0;ON;;;;;Y;;;;;
+29DC;INCOMPLETE INFINITY;Sm;0;ON;;;;;Y;;;;;
+29DD;TIE OVER INFINITY;Sm;0;ON;;;;;N;;;;;
+29DE;INFINITY NEGATED WITH VERTICAL BAR;Sm;0;ON;;;;;N;;;;;
+29DF;DOUBLE-ENDED MULTIMAP;Sm;0;ON;;;;;N;;;;;
+29E0;SQUARE WITH CONTOURED OUTLINE;Sm;0;ON;;;;;N;;;;;
+29E1;INCREASES AS;Sm;0;ON;;;;;Y;;;;;
+29E2;SHUFFLE PRODUCT;Sm;0;ON;;;;;N;;;;;
+29E3;EQUALS SIGN AND SLANTED PARALLEL;Sm;0;ON;;;;;Y;;;;;
+29E4;EQUALS SIGN AND SLANTED PARALLEL WITH TILDE ABOVE;Sm;0;ON;;;;;Y;;;;;
+29E5;IDENTICAL TO AND SLANTED PARALLEL;Sm;0;ON;;;;;Y;;;;;
+29E6;GLEICH STARK;Sm;0;ON;;;;;N;;;;;
+29E7;THERMODYNAMIC;Sm;0;ON;;;;;N;;;;;
+29E8;DOWN-POINTING TRIANGLE WITH LEFT HALF BLACK;Sm;0;ON;;;;;Y;;;;;
+29E9;DOWN-POINTING TRIANGLE WITH RIGHT HALF BLACK;Sm;0;ON;;;;;Y;;;;;
+29EA;BLACK DIAMOND WITH DOWN ARROW;Sm;0;ON;;;;;N;;;;;
+29EB;BLACK LOZENGE;Sm;0;ON;;;;;N;;;;;
+29EC;WHITE CIRCLE WITH DOWN ARROW;Sm;0;ON;;;;;N;;;;;
+29ED;BLACK CIRCLE WITH DOWN ARROW;Sm;0;ON;;;;;N;;;;;
+29EE;ERROR-BARRED WHITE SQUARE;Sm;0;ON;;;;;N;;;;;
+29EF;ERROR-BARRED BLACK SQUARE;Sm;0;ON;;;;;N;;;;;
+29F0;ERROR-BARRED WHITE DIAMOND;Sm;0;ON;;;;;N;;;;;
+29F1;ERROR-BARRED BLACK DIAMOND;Sm;0;ON;;;;;N;;;;;
+29F2;ERROR-BARRED WHITE CIRCLE;Sm;0;ON;;;;;N;;;;;
+29F3;ERROR-BARRED BLACK CIRCLE;Sm;0;ON;;;;;N;;;;;
+29F4;RULE-DELAYED;Sm;0;ON;;;;;Y;;;;;
+29F5;REVERSE SOLIDUS OPERATOR;Sm;0;ON;;;;;Y;;;;;
+29F6;SOLIDUS WITH OVERBAR;Sm;0;ON;;;;;Y;;;;;
+29F7;REVERSE SOLIDUS WITH HORIZONTAL STROKE;Sm;0;ON;;;;;Y;;;;;
+29F8;BIG SOLIDUS;Sm;0;ON;;;;;Y;;;;;
+29F9;BIG REVERSE SOLIDUS;Sm;0;ON;;;;;Y;;;;;
+29FA;DOUBLE PLUS;Sm;0;ON;;;;;N;;;;;
+29FB;TRIPLE PLUS;Sm;0;ON;;;;;N;;;;;
+29FC;LEFT-POINTING CURVED ANGLE BRACKET;Ps;0;ON;;;;;Y;;;;;
+29FD;RIGHT-POINTING CURVED ANGLE BRACKET;Pe;0;ON;;;;;Y;;;;;
+29FE;TINY;Sm;0;ON;;;;;N;;;;;
+29FF;MINY;Sm;0;ON;;;;;N;;;;;
+2A00;N-ARY CIRCLED DOT OPERATOR;Sm;0;ON;;;;;N;;;;;
+2A01;N-ARY CIRCLED PLUS OPERATOR;Sm;0;ON;;;;;N;;;;;
+2A02;N-ARY CIRCLED TIMES OPERATOR;Sm;0;ON;;;;;N;;;;;
+2A03;N-ARY UNION OPERATOR WITH DOT;Sm;0;ON;;;;;N;;;;;
+2A04;N-ARY UNION OPERATOR WITH PLUS;Sm;0;ON;;;;;N;;;;;
+2A05;N-ARY SQUARE INTERSECTION OPERATOR;Sm;0;ON;;;;;N;;;;;
+2A06;N-ARY SQUARE UNION OPERATOR;Sm;0;ON;;;;;N;;;;;
+2A07;TWO LOGICAL AND OPERATOR;Sm;0;ON;;;;;N;;;;;
+2A08;TWO LOGICAL OR OPERATOR;Sm;0;ON;;;;;N;;;;;
+2A09;N-ARY TIMES OPERATOR;Sm;0;ON;;;;;N;;;;;
+2A0A;MODULO TWO SUM;Sm;0;ON;;;;;Y;;;;;
+2A0B;SUMMATION WITH INTEGRAL;Sm;0;ON;;;;;Y;;;;;
+2A0C;QUADRUPLE INTEGRAL OPERATOR;Sm;0;ON;<compat> 222B 222B 222B 222B;;;;Y;;;;;
+2A0D;FINITE PART INTEGRAL;Sm;0;ON;;;;;Y;;;;;
+2A0E;INTEGRAL WITH DOUBLE STROKE;Sm;0;ON;;;;;Y;;;;;
+2A0F;INTEGRAL AVERAGE WITH SLASH;Sm;0;ON;;;;;Y;;;;;
+2A10;CIRCULATION FUNCTION;Sm;0;ON;;;;;Y;;;;;
+2A11;ANTICLOCKWISE INTEGRATION;Sm;0;ON;;;;;Y;;;;;
+2A12;LINE INTEGRATION WITH RECTANGULAR PATH AROUND POLE;Sm;0;ON;;;;;Y;;;;;
+2A13;LINE INTEGRATION WITH SEMICIRCULAR PATH AROUND POLE;Sm;0;ON;;;;;Y;;;;;
+2A14;LINE INTEGRATION NOT INCLUDING THE POLE;Sm;0;ON;;;;;Y;;;;;
+2A15;INTEGRAL AROUND A POINT OPERATOR;Sm;0;ON;;;;;Y;;;;;
+2A16;QUATERNION INTEGRAL OPERATOR;Sm;0;ON;;;;;Y;;;;;
+2A17;INTEGRAL WITH LEFTWARDS ARROW WITH HOOK;Sm;0;ON;;;;;Y;;;;;
+2A18;INTEGRAL WITH TIMES SIGN;Sm;0;ON;;;;;Y;;;;;
+2A19;INTEGRAL WITH INTERSECTION;Sm;0;ON;;;;;Y;;;;;
+2A1A;INTEGRAL WITH UNION;Sm;0;ON;;;;;Y;;;;;
+2A1B;INTEGRAL WITH OVERBAR;Sm;0;ON;;;;;Y;;;;;
+2A1C;INTEGRAL WITH UNDERBAR;Sm;0;ON;;;;;Y;;;;;
+2A1D;JOIN;Sm;0;ON;;;;;N;;;;;
+2A1E;LARGE LEFT TRIANGLE OPERATOR;Sm;0;ON;;;;;Y;;;;;
+2A1F;Z NOTATION SCHEMA COMPOSITION;Sm;0;ON;;;;;Y;;;;;
+2A20;Z NOTATION SCHEMA PIPING;Sm;0;ON;;;;;Y;;;;;
+2A21;Z NOTATION SCHEMA PROJECTION;Sm;0;ON;;;;;Y;;;;;
+2A22;PLUS SIGN WITH SMALL CIRCLE ABOVE;Sm;0;ON;;;;;N;;;;;
+2A23;PLUS SIGN WITH CIRCUMFLEX ACCENT ABOVE;Sm;0;ON;;;;;N;;;;;
+2A24;PLUS SIGN WITH TILDE ABOVE;Sm;0;ON;;;;;Y;;;;;
+2A25;PLUS SIGN WITH DOT BELOW;Sm;0;ON;;;;;N;;;;;
+2A26;PLUS SIGN WITH TILDE BELOW;Sm;0;ON;;;;;Y;;;;;
+2A27;PLUS SIGN WITH SUBSCRIPT TWO;Sm;0;ON;;;;;N;;;;;
+2A28;PLUS SIGN WITH BLACK TRIANGLE;Sm;0;ON;;;;;N;;;;;
+2A29;MINUS SIGN WITH COMMA ABOVE;Sm;0;ON;;;;;Y;;;;;
+2A2A;MINUS SIGN WITH DOT BELOW;Sm;0;ON;;;;;N;;;;;
+2A2B;MINUS SIGN WITH FALLING DOTS;Sm;0;ON;;;;;Y;;;;;
+2A2C;MINUS SIGN WITH RISING DOTS;Sm;0;ON;;;;;Y;;;;;
+2A2D;PLUS SIGN IN LEFT HALF CIRCLE;Sm;0;ON;;;;;Y;;;;;
+2A2E;PLUS SIGN IN RIGHT HALF CIRCLE;Sm;0;ON;;;;;Y;;;;;
+2A2F;VECTOR OR CROSS PRODUCT;Sm;0;ON;;;;;N;;;;;
+2A30;MULTIPLICATION SIGN WITH DOT ABOVE;Sm;0;ON;;;;;N;;;;;
+2A31;MULTIPLICATION SIGN WITH UNDERBAR;Sm;0;ON;;;;;N;;;;;
+2A32;SEMIDIRECT PRODUCT WITH BOTTOM CLOSED;Sm;0;ON;;;;;N;;;;;
+2A33;SMASH PRODUCT;Sm;0;ON;;;;;N;;;;;
+2A34;MULTIPLICATION SIGN IN LEFT HALF CIRCLE;Sm;0;ON;;;;;Y;;;;;
+2A35;MULTIPLICATION SIGN IN RIGHT HALF CIRCLE;Sm;0;ON;;;;;Y;;;;;
+2A36;CIRCLED MULTIPLICATION SIGN WITH CIRCUMFLEX ACCENT;Sm;0;ON;;;;;N;;;;;
+2A37;MULTIPLICATION SIGN IN DOUBLE CIRCLE;Sm;0;ON;;;;;N;;;;;
+2A38;CIRCLED DIVISION SIGN;Sm;0;ON;;;;;N;;;;;
+2A39;PLUS SIGN IN TRIANGLE;Sm;0;ON;;;;;N;;;;;
+2A3A;MINUS SIGN IN TRIANGLE;Sm;0;ON;;;;;N;;;;;
+2A3B;MULTIPLICATION SIGN IN TRIANGLE;Sm;0;ON;;;;;N;;;;;
+2A3C;INTERIOR PRODUCT;Sm;0;ON;;;;;Y;;;;;
+2A3D;RIGHTHAND INTERIOR PRODUCT;Sm;0;ON;;;;;Y;;;;;
+2A3E;Z NOTATION RELATIONAL COMPOSITION;Sm;0;ON;;;;;Y;;;;;
+2A3F;AMALGAMATION OR COPRODUCT;Sm;0;ON;;;;;N;;;;;
+2A40;INTERSECTION WITH DOT;Sm;0;ON;;;;;N;;;;;
+2A41;UNION WITH MINUS SIGN;Sm;0;ON;;;;;N;;;;;
+2A42;UNION WITH OVERBAR;Sm;0;ON;;;;;N;;;;;
+2A43;INTERSECTION WITH OVERBAR;Sm;0;ON;;;;;N;;;;;
+2A44;INTERSECTION WITH LOGICAL AND;Sm;0;ON;;;;;N;;;;;
+2A45;UNION WITH LOGICAL OR;Sm;0;ON;;;;;N;;;;;
+2A46;UNION ABOVE INTERSECTION;Sm;0;ON;;;;;N;;;;;
+2A47;INTERSECTION ABOVE UNION;Sm;0;ON;;;;;N;;;;;
+2A48;UNION ABOVE BAR ABOVE INTERSECTION;Sm;0;ON;;;;;N;;;;;
+2A49;INTERSECTION ABOVE BAR ABOVE UNION;Sm;0;ON;;;;;N;;;;;
+2A4A;UNION BESIDE AND JOINED WITH UNION;Sm;0;ON;;;;;N;;;;;
+2A4B;INTERSECTION BESIDE AND JOINED WITH INTERSECTION;Sm;0;ON;;;;;N;;;;;
+2A4C;CLOSED UNION WITH SERIFS;Sm;0;ON;;;;;N;;;;;
+2A4D;CLOSED INTERSECTION WITH SERIFS;Sm;0;ON;;;;;N;;;;;
+2A4E;DOUBLE SQUARE INTERSECTION;Sm;0;ON;;;;;N;;;;;
+2A4F;DOUBLE SQUARE UNION;Sm;0;ON;;;;;N;;;;;
+2A50;CLOSED UNION WITH SERIFS AND SMASH PRODUCT;Sm;0;ON;;;;;N;;;;;
+2A51;LOGICAL AND WITH DOT ABOVE;Sm;0;ON;;;;;N;;;;;
+2A52;LOGICAL OR WITH DOT ABOVE;Sm;0;ON;;;;;N;;;;;
+2A53;DOUBLE LOGICAL AND;Sm;0;ON;;;;;N;;;;;
+2A54;DOUBLE LOGICAL OR;Sm;0;ON;;;;;N;;;;;
+2A55;TWO INTERSECTING LOGICAL AND;Sm;0;ON;;;;;N;;;;;
+2A56;TWO INTERSECTING LOGICAL OR;Sm;0;ON;;;;;N;;;;;
+2A57;SLOPING LARGE OR;Sm;0;ON;;;;;Y;;;;;
+2A58;SLOPING LARGE AND;Sm;0;ON;;;;;Y;;;;;
+2A59;LOGICAL OR OVERLAPPING LOGICAL AND;Sm;0;ON;;;;;N;;;;;
+2A5A;LOGICAL AND WITH MIDDLE STEM;Sm;0;ON;;;;;N;;;;;
+2A5B;LOGICAL OR WITH MIDDLE STEM;Sm;0;ON;;;;;N;;;;;
+2A5C;LOGICAL AND WITH HORIZONTAL DASH;Sm;0;ON;;;;;N;;;;;
+2A5D;LOGICAL OR WITH HORIZONTAL DASH;Sm;0;ON;;;;;N;;;;;
+2A5E;LOGICAL AND WITH DOUBLE OVERBAR;Sm;0;ON;;;;;N;;;;;
+2A5F;LOGICAL AND WITH UNDERBAR;Sm;0;ON;;;;;N;;;;;
+2A60;LOGICAL AND WITH DOUBLE UNDERBAR;Sm;0;ON;;;;;N;;;;;
+2A61;SMALL VEE WITH UNDERBAR;Sm;0;ON;;;;;N;;;;;
+2A62;LOGICAL OR WITH DOUBLE OVERBAR;Sm;0;ON;;;;;N;;;;;
+2A63;LOGICAL OR WITH DOUBLE UNDERBAR;Sm;0;ON;;;;;N;;;;;
+2A64;Z NOTATION DOMAIN ANTIRESTRICTION;Sm;0;ON;;;;;Y;;;;;
+2A65;Z NOTATION RANGE ANTIRESTRICTION;Sm;0;ON;;;;;Y;;;;;
+2A66;EQUALS SIGN WITH DOT BELOW;Sm;0;ON;;;;;N;;;;;
+2A67;IDENTICAL WITH DOT ABOVE;Sm;0;ON;;;;;N;;;;;
+2A68;TRIPLE HORIZONTAL BAR WITH DOUBLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+2A69;TRIPLE HORIZONTAL BAR WITH TRIPLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+2A6A;TILDE OPERATOR WITH DOT ABOVE;Sm;0;ON;;;;;Y;;;;;
+2A6B;TILDE OPERATOR WITH RISING DOTS;Sm;0;ON;;;;;Y;;;;;
+2A6C;SIMILAR MINUS SIMILAR;Sm;0;ON;;;;;Y;;;;;
+2A6D;CONGRUENT WITH DOT ABOVE;Sm;0;ON;;;;;Y;;;;;
+2A6E;EQUALS WITH ASTERISK;Sm;0;ON;;;;;N;;;;;
+2A6F;ALMOST EQUAL TO WITH CIRCUMFLEX ACCENT;Sm;0;ON;;;;;Y;;;;;
+2A70;APPROXIMATELY EQUAL OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2A71;EQUALS SIGN ABOVE PLUS SIGN;Sm;0;ON;;;;;N;;;;;
+2A72;PLUS SIGN ABOVE EQUALS SIGN;Sm;0;ON;;;;;N;;;;;
+2A73;EQUALS SIGN ABOVE TILDE OPERATOR;Sm;0;ON;;;;;Y;;;;;
+2A74;DOUBLE COLON EQUAL;Sm;0;ON;<compat> 003A 003A 003D;;;;Y;;;;;
+2A75;TWO CONSECUTIVE EQUALS SIGNS;Sm;0;ON;<compat> 003D 003D;;;;N;;;;;
+2A76;THREE CONSECUTIVE EQUALS SIGNS;Sm;0;ON;<compat> 003D 003D 003D;;;;N;;;;;
+2A77;EQUALS SIGN WITH TWO DOTS ABOVE AND TWO DOTS BELOW;Sm;0;ON;;;;;N;;;;;
+2A78;EQUIVALENT WITH FOUR DOTS ABOVE;Sm;0;ON;;;;;N;;;;;
+2A79;LESS-THAN WITH CIRCLE INSIDE;Sm;0;ON;;;;;Y;;;;;
+2A7A;GREATER-THAN WITH CIRCLE INSIDE;Sm;0;ON;;;;;Y;;;;;
+2A7B;LESS-THAN WITH QUESTION MARK ABOVE;Sm;0;ON;;;;;Y;;;;;
+2A7C;GREATER-THAN WITH QUESTION MARK ABOVE;Sm;0;ON;;;;;Y;;;;;
+2A7D;LESS-THAN OR SLANTED EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2A7E;GREATER-THAN OR SLANTED EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2A7F;LESS-THAN OR SLANTED EQUAL TO WITH DOT INSIDE;Sm;0;ON;;;;;Y;;;;;
+2A80;GREATER-THAN OR SLANTED EQUAL TO WITH DOT INSIDE;Sm;0;ON;;;;;Y;;;;;
+2A81;LESS-THAN OR SLANTED EQUAL TO WITH DOT ABOVE;Sm;0;ON;;;;;Y;;;;;
+2A82;GREATER-THAN OR SLANTED EQUAL TO WITH DOT ABOVE;Sm;0;ON;;;;;Y;;;;;
+2A83;LESS-THAN OR SLANTED EQUAL TO WITH DOT ABOVE RIGHT;Sm;0;ON;;;;;Y;;;;;
+2A84;GREATER-THAN OR SLANTED EQUAL TO WITH DOT ABOVE LEFT;Sm;0;ON;;;;;Y;;;;;
+2A85;LESS-THAN OR APPROXIMATE;Sm;0;ON;;;;;Y;;;;;
+2A86;GREATER-THAN OR APPROXIMATE;Sm;0;ON;;;;;Y;;;;;
+2A87;LESS-THAN AND SINGLE-LINE NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2A88;GREATER-THAN AND SINGLE-LINE NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2A89;LESS-THAN AND NOT APPROXIMATE;Sm;0;ON;;;;;Y;;;;;
+2A8A;GREATER-THAN AND NOT APPROXIMATE;Sm;0;ON;;;;;Y;;;;;
+2A8B;LESS-THAN ABOVE DOUBLE-LINE EQUAL ABOVE GREATER-THAN;Sm;0;ON;;;;;Y;;;;;
+2A8C;GREATER-THAN ABOVE DOUBLE-LINE EQUAL ABOVE LESS-THAN;Sm;0;ON;;;;;Y;;;;;
+2A8D;LESS-THAN ABOVE SIMILAR OR EQUAL;Sm;0;ON;;;;;Y;;;;;
+2A8E;GREATER-THAN ABOVE SIMILAR OR EQUAL;Sm;0;ON;;;;;Y;;;;;
+2A8F;LESS-THAN ABOVE SIMILAR ABOVE GREATER-THAN;Sm;0;ON;;;;;Y;;;;;
+2A90;GREATER-THAN ABOVE SIMILAR ABOVE LESS-THAN;Sm;0;ON;;;;;Y;;;;;
+2A91;LESS-THAN ABOVE GREATER-THAN ABOVE DOUBLE-LINE EQUAL;Sm;0;ON;;;;;Y;;;;;
+2A92;GREATER-THAN ABOVE LESS-THAN ABOVE DOUBLE-LINE EQUAL;Sm;0;ON;;;;;Y;;;;;
+2A93;LESS-THAN ABOVE SLANTED EQUAL ABOVE GREATER-THAN ABOVE SLANTED EQUAL;Sm;0;ON;;;;;Y;;;;;
+2A94;GREATER-THAN ABOVE SLANTED EQUAL ABOVE LESS-THAN ABOVE SLANTED EQUAL;Sm;0;ON;;;;;Y;;;;;
+2A95;SLANTED EQUAL TO OR LESS-THAN;Sm;0;ON;;;;;Y;;;;;
+2A96;SLANTED EQUAL TO OR GREATER-THAN;Sm;0;ON;;;;;Y;;;;;
+2A97;SLANTED EQUAL TO OR LESS-THAN WITH DOT INSIDE;Sm;0;ON;;;;;Y;;;;;
+2A98;SLANTED EQUAL TO OR GREATER-THAN WITH DOT INSIDE;Sm;0;ON;;;;;Y;;;;;
+2A99;DOUBLE-LINE EQUAL TO OR LESS-THAN;Sm;0;ON;;;;;Y;;;;;
+2A9A;DOUBLE-LINE EQUAL TO OR GREATER-THAN;Sm;0;ON;;;;;Y;;;;;
+2A9B;DOUBLE-LINE SLANTED EQUAL TO OR LESS-THAN;Sm;0;ON;;;;;Y;;;;;
+2A9C;DOUBLE-LINE SLANTED EQUAL TO OR GREATER-THAN;Sm;0;ON;;;;;Y;;;;;
+2A9D;SIMILAR OR LESS-THAN;Sm;0;ON;;;;;Y;;;;;
+2A9E;SIMILAR OR GREATER-THAN;Sm;0;ON;;;;;Y;;;;;
+2A9F;SIMILAR ABOVE LESS-THAN ABOVE EQUALS SIGN;Sm;0;ON;;;;;Y;;;;;
+2AA0;SIMILAR ABOVE GREATER-THAN ABOVE EQUALS SIGN;Sm;0;ON;;;;;Y;;;;;
+2AA1;DOUBLE NESTED LESS-THAN;Sm;0;ON;;;;;Y;;;;;
+2AA2;DOUBLE NESTED GREATER-THAN;Sm;0;ON;;;;;Y;;;;;
+2AA3;DOUBLE NESTED LESS-THAN WITH UNDERBAR;Sm;0;ON;;;;;Y;;;;;
+2AA4;GREATER-THAN OVERLAPPING LESS-THAN;Sm;0;ON;;;;;N;;;;;
+2AA5;GREATER-THAN BESIDE LESS-THAN;Sm;0;ON;;;;;N;;;;;
+2AA6;LESS-THAN CLOSED BY CURVE;Sm;0;ON;;;;;Y;;;;;
+2AA7;GREATER-THAN CLOSED BY CURVE;Sm;0;ON;;;;;Y;;;;;
+2AA8;LESS-THAN CLOSED BY CURVE ABOVE SLANTED EQUAL;Sm;0;ON;;;;;Y;;;;;
+2AA9;GREATER-THAN CLOSED BY CURVE ABOVE SLANTED EQUAL;Sm;0;ON;;;;;Y;;;;;
+2AAA;SMALLER THAN;Sm;0;ON;;;;;Y;;;;;
+2AAB;LARGER THAN;Sm;0;ON;;;;;Y;;;;;
+2AAC;SMALLER THAN OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2AAD;LARGER THAN OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2AAE;EQUALS SIGN WITH BUMPY ABOVE;Sm;0;ON;;;;;N;;;;;
+2AAF;PRECEDES ABOVE SINGLE-LINE EQUALS SIGN;Sm;0;ON;;;;;Y;;;;;
+2AB0;SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN;Sm;0;ON;;;;;Y;;;;;
+2AB1;PRECEDES ABOVE SINGLE-LINE NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2AB2;SUCCEEDS ABOVE SINGLE-LINE NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2AB3;PRECEDES ABOVE EQUALS SIGN;Sm;0;ON;;;;;Y;;;;;
+2AB4;SUCCEEDS ABOVE EQUALS SIGN;Sm;0;ON;;;;;Y;;;;;
+2AB5;PRECEDES ABOVE NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2AB6;SUCCEEDS ABOVE NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2AB7;PRECEDES ABOVE ALMOST EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2AB8;SUCCEEDS ABOVE ALMOST EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2AB9;PRECEDES ABOVE NOT ALMOST EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2ABA;SUCCEEDS ABOVE NOT ALMOST EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2ABB;DOUBLE PRECEDES;Sm;0;ON;;;;;Y;;;;;
+2ABC;DOUBLE SUCCEEDS;Sm;0;ON;;;;;Y;;;;;
+2ABD;SUBSET WITH DOT;Sm;0;ON;;;;;Y;;;;;
+2ABE;SUPERSET WITH DOT;Sm;0;ON;;;;;Y;;;;;
+2ABF;SUBSET WITH PLUS SIGN BELOW;Sm;0;ON;;;;;Y;;;;;
+2AC0;SUPERSET WITH PLUS SIGN BELOW;Sm;0;ON;;;;;Y;;;;;
+2AC1;SUBSET WITH MULTIPLICATION SIGN BELOW;Sm;0;ON;;;;;Y;;;;;
+2AC2;SUPERSET WITH MULTIPLICATION SIGN BELOW;Sm;0;ON;;;;;Y;;;;;
+2AC3;SUBSET OF OR EQUAL TO WITH DOT ABOVE;Sm;0;ON;;;;;Y;;;;;
+2AC4;SUPERSET OF OR EQUAL TO WITH DOT ABOVE;Sm;0;ON;;;;;Y;;;;;
+2AC5;SUBSET OF ABOVE EQUALS SIGN;Sm;0;ON;;;;;Y;;;;;
+2AC6;SUPERSET OF ABOVE EQUALS SIGN;Sm;0;ON;;;;;Y;;;;;
+2AC7;SUBSET OF ABOVE TILDE OPERATOR;Sm;0;ON;;;;;Y;;;;;
+2AC8;SUPERSET OF ABOVE TILDE OPERATOR;Sm;0;ON;;;;;Y;;;;;
+2AC9;SUBSET OF ABOVE ALMOST EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2ACA;SUPERSET OF ABOVE ALMOST EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2ACB;SUBSET OF ABOVE NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2ACC;SUPERSET OF ABOVE NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2ACD;SQUARE LEFT OPEN BOX OPERATOR;Sm;0;ON;;;;;Y;;;;;
+2ACE;SQUARE RIGHT OPEN BOX OPERATOR;Sm;0;ON;;;;;Y;;;;;
+2ACF;CLOSED SUBSET;Sm;0;ON;;;;;Y;;;;;
+2AD0;CLOSED SUPERSET;Sm;0;ON;;;;;Y;;;;;
+2AD1;CLOSED SUBSET OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2AD2;CLOSED SUPERSET OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2AD3;SUBSET ABOVE SUPERSET;Sm;0;ON;;;;;Y;;;;;
+2AD4;SUPERSET ABOVE SUBSET;Sm;0;ON;;;;;Y;;;;;
+2AD5;SUBSET ABOVE SUBSET;Sm;0;ON;;;;;Y;;;;;
+2AD6;SUPERSET ABOVE SUPERSET;Sm;0;ON;;;;;Y;;;;;
+2AD7;SUPERSET BESIDE SUBSET;Sm;0;ON;;;;;N;;;;;
+2AD8;SUPERSET BESIDE AND JOINED BY DASH WITH SUBSET;Sm;0;ON;;;;;N;;;;;
+2AD9;ELEMENT OF OPENING DOWNWARDS;Sm;0;ON;;;;;N;;;;;
+2ADA;PITCHFORK WITH TEE TOP;Sm;0;ON;;;;;N;;;;;
+2ADB;TRANSVERSAL INTERSECTION;Sm;0;ON;;;;;N;;;;;
+2ADC;FORKING;Sm;0;ON;2ADD 0338;;;;Y;;not independent;;;
+2ADD;NONFORKING;Sm;0;ON;;;;;N;;independent;;;
+2ADE;SHORT LEFT TACK;Sm;0;ON;;;;;Y;;;;;
+2ADF;SHORT DOWN TACK;Sm;0;ON;;;;;N;;;;;
+2AE0;SHORT UP TACK;Sm;0;ON;;;;;N;;;;;
+2AE1;PERPENDICULAR WITH S;Sm;0;ON;;;;;N;;;;;
+2AE2;VERTICAL BAR TRIPLE RIGHT TURNSTILE;Sm;0;ON;;;;;Y;;;;;
+2AE3;DOUBLE VERTICAL BAR LEFT TURNSTILE;Sm;0;ON;;;;;Y;;;;;
+2AE4;VERTICAL BAR DOUBLE LEFT TURNSTILE;Sm;0;ON;;;;;Y;;;;;
+2AE5;DOUBLE VERTICAL BAR DOUBLE LEFT TURNSTILE;Sm;0;ON;;;;;Y;;;;;
+2AE6;LONG DASH FROM LEFT MEMBER OF DOUBLE VERTICAL;Sm;0;ON;;;;;Y;;;;;
+2AE7;SHORT DOWN TACK WITH OVERBAR;Sm;0;ON;;;;;N;;;;;
+2AE8;SHORT UP TACK WITH UNDERBAR;Sm;0;ON;;;;;N;;;;;
+2AE9;SHORT UP TACK ABOVE SHORT DOWN TACK;Sm;0;ON;;;;;N;;;;;
+2AEA;DOUBLE DOWN TACK;Sm;0;ON;;;;;N;;;;;
+2AEB;DOUBLE UP TACK;Sm;0;ON;;;;;N;;;;;
+2AEC;DOUBLE STROKE NOT SIGN;Sm;0;ON;;;;;Y;;;;;
+2AED;REVERSED DOUBLE STROKE NOT SIGN;Sm;0;ON;;;;;Y;;;;;
+2AEE;DOES NOT DIVIDE WITH REVERSED NEGATION SLASH;Sm;0;ON;;;;;Y;;;;;
+2AEF;VERTICAL LINE WITH CIRCLE ABOVE;Sm;0;ON;;;;;N;;;;;
+2AF0;VERTICAL LINE WITH CIRCLE BELOW;Sm;0;ON;;;;;N;;;;;
+2AF1;DOWN TACK WITH CIRCLE BELOW;Sm;0;ON;;;;;N;;;;;
+2AF2;PARALLEL WITH HORIZONTAL STROKE;Sm;0;ON;;;;;N;;;;;
+2AF3;PARALLEL WITH TILDE OPERATOR;Sm;0;ON;;;;;Y;;;;;
+2AF4;TRIPLE VERTICAL BAR BINARY RELATION;Sm;0;ON;;;;;N;;;;;
+2AF5;TRIPLE VERTICAL BAR WITH HORIZONTAL STROKE;Sm;0;ON;;;;;N;;;;;
+2AF6;TRIPLE COLON OPERATOR;Sm;0;ON;;;;;N;;;;;
+2AF7;TRIPLE NESTED LESS-THAN;Sm;0;ON;;;;;Y;;;;;
+2AF8;TRIPLE NESTED GREATER-THAN;Sm;0;ON;;;;;Y;;;;;
+2AF9;DOUBLE-LINE SLANTED LESS-THAN OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2AFA;DOUBLE-LINE SLANTED GREATER-THAN OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2AFB;TRIPLE SOLIDUS BINARY RELATION;Sm;0;ON;;;;;Y;;;;;
+2AFC;LARGE TRIPLE VERTICAL BAR OPERATOR;Sm;0;ON;;;;;N;;;;;
+2AFD;DOUBLE SOLIDUS OPERATOR;Sm;0;ON;;;;;Y;;;;;
+2AFE;WHITE VERTICAL BAR;Sm;0;ON;;;;;N;;;;;
+2AFF;N-ARY WHITE VERTICAL BAR;Sm;0;ON;;;;;N;;;;;
+2B00;NORTH EAST WHITE ARROW;So;0;ON;;;;;N;;;;;
+2B01;NORTH WEST WHITE ARROW;So;0;ON;;;;;N;;;;;
+2B02;SOUTH EAST WHITE ARROW;So;0;ON;;;;;N;;;;;
+2B03;SOUTH WEST WHITE ARROW;So;0;ON;;;;;N;;;;;
+2B04;LEFT RIGHT WHITE ARROW;So;0;ON;;;;;N;;;;;
+2B05;LEFTWARDS BLACK ARROW;So;0;ON;;;;;N;;;;;
+2B06;UPWARDS BLACK ARROW;So;0;ON;;;;;N;;;;;
+2B07;DOWNWARDS BLACK ARROW;So;0;ON;;;;;N;;;;;
+2B08;NORTH EAST BLACK ARROW;So;0;ON;;;;;N;;;;;
+2B09;NORTH WEST BLACK ARROW;So;0;ON;;;;;N;;;;;
+2B0A;SOUTH EAST BLACK ARROW;So;0;ON;;;;;N;;;;;
+2B0B;SOUTH WEST BLACK ARROW;So;0;ON;;;;;N;;;;;
+2B0C;LEFT RIGHT BLACK ARROW;So;0;ON;;;;;N;;;;;
+2B0D;UP DOWN BLACK ARROW;So;0;ON;;;;;N;;;;;
+2E80;CJK RADICAL REPEAT;So;0;ON;;;;;N;;;;;
+2E81;CJK RADICAL CLIFF;So;0;ON;;;;;N;;;;;
+2E82;CJK RADICAL SECOND ONE;So;0;ON;;;;;N;;;;;
+2E83;CJK RADICAL SECOND TWO;So;0;ON;;;;;N;;;;;
+2E84;CJK RADICAL SECOND THREE;So;0;ON;;;;;N;;;;;
+2E85;CJK RADICAL PERSON;So;0;ON;;;;;N;;;;;
+2E86;CJK RADICAL BOX;So;0;ON;;;;;N;;;;;
+2E87;CJK RADICAL TABLE;So;0;ON;;;;;N;;;;;
+2E88;CJK RADICAL KNIFE ONE;So;0;ON;;;;;N;;;;;
+2E89;CJK RADICAL KNIFE TWO;So;0;ON;;;;;N;;;;;
+2E8A;CJK RADICAL DIVINATION;So;0;ON;;;;;N;;;;;
+2E8B;CJK RADICAL SEAL;So;0;ON;;;;;N;;;;;
+2E8C;CJK RADICAL SMALL ONE;So;0;ON;;;;;N;;;;;
+2E8D;CJK RADICAL SMALL TWO;So;0;ON;;;;;N;;;;;
+2E8E;CJK RADICAL LAME ONE;So;0;ON;;;;;N;;;;;
+2E8F;CJK RADICAL LAME TWO;So;0;ON;;;;;N;;;;;
+2E90;CJK RADICAL LAME THREE;So;0;ON;;;;;N;;;;;
+2E91;CJK RADICAL LAME FOUR;So;0;ON;;;;;N;;;;;
+2E92;CJK RADICAL SNAKE;So;0;ON;;;;;N;;;;;
+2E93;CJK RADICAL THREAD;So;0;ON;;;;;N;;;;;
+2E94;CJK RADICAL SNOUT ONE;So;0;ON;;;;;N;;;;;
+2E95;CJK RADICAL SNOUT TWO;So;0;ON;;;;;N;;;;;
+2E96;CJK RADICAL HEART ONE;So;0;ON;;;;;N;;;;;
+2E97;CJK RADICAL HEART TWO;So;0;ON;;;;;N;;;;;
+2E98;CJK RADICAL HAND;So;0;ON;;;;;N;;;;;
+2E99;CJK RADICAL RAP;So;0;ON;;;;;N;;;;;
+2E9B;CJK RADICAL CHOKE;So;0;ON;;;;;N;;;;;
+2E9C;CJK RADICAL SUN;So;0;ON;;;;;N;;;;;
+2E9D;CJK RADICAL MOON;So;0;ON;;;;;N;;;;;
+2E9E;CJK RADICAL DEATH;So;0;ON;;;;;N;;;;;
+2E9F;CJK RADICAL MOTHER;So;0;ON;<compat> 6BCD;;;;N;;;;;
+2EA0;CJK RADICAL CIVILIAN;So;0;ON;;;;;N;;;;;
+2EA1;CJK RADICAL WATER ONE;So;0;ON;;;;;N;;;;;
+2EA2;CJK RADICAL WATER TWO;So;0;ON;;;;;N;;;;;
+2EA3;CJK RADICAL FIRE;So;0;ON;;;;;N;;;;;
+2EA4;CJK RADICAL PAW ONE;So;0;ON;;;;;N;;;;;
+2EA5;CJK RADICAL PAW TWO;So;0;ON;;;;;N;;;;;
+2EA6;CJK RADICAL SIMPLIFIED HALF TREE TRUNK;So;0;ON;;;;;N;;;;;
+2EA7;CJK RADICAL COW;So;0;ON;;;;;N;;;;;
+2EA8;CJK RADICAL DOG;So;0;ON;;;;;N;;;;;
+2EA9;CJK RADICAL JADE;So;0;ON;;;;;N;;;;;
+2EAA;CJK RADICAL BOLT OF CLOTH;So;0;ON;;;;;N;;;;;
+2EAB;CJK RADICAL EYE;So;0;ON;;;;;N;;;;;
+2EAC;CJK RADICAL SPIRIT ONE;So;0;ON;;;;;N;;;;;
+2EAD;CJK RADICAL SPIRIT TWO;So;0;ON;;;;;N;;;;;
+2EAE;CJK RADICAL BAMBOO;So;0;ON;;;;;N;;;;;
+2EAF;CJK RADICAL SILK;So;0;ON;;;;;N;;;;;
+2EB0;CJK RADICAL C-SIMPLIFIED SILK;So;0;ON;;;;;N;;;;;
+2EB1;CJK RADICAL NET ONE;So;0;ON;;;;;N;;;;;
+2EB2;CJK RADICAL NET TWO;So;0;ON;;;;;N;;;;;
+2EB3;CJK RADICAL NET THREE;So;0;ON;;;;;N;;;;;
+2EB4;CJK RADICAL NET FOUR;So;0;ON;;;;;N;;;;;
+2EB5;CJK RADICAL MESH;So;0;ON;;;;;N;;;;;
+2EB6;CJK RADICAL SHEEP;So;0;ON;;;;;N;;;;;
+2EB7;CJK RADICAL RAM;So;0;ON;;;;;N;;;;;
+2EB8;CJK RADICAL EWE;So;0;ON;;;;;N;;;;;
+2EB9;CJK RADICAL OLD;So;0;ON;;;;;N;;;;;
+2EBA;CJK RADICAL BRUSH ONE;So;0;ON;;;;;N;;;;;
+2EBB;CJK RADICAL BRUSH TWO;So;0;ON;;;;;N;;;;;
+2EBC;CJK RADICAL MEAT;So;0;ON;;;;;N;;;;;
+2EBD;CJK RADICAL MORTAR;So;0;ON;;;;;N;;;;;
+2EBE;CJK RADICAL GRASS ONE;So;0;ON;;;;;N;;;;;
+2EBF;CJK RADICAL GRASS TWO;So;0;ON;;;;;N;;;;;
+2EC0;CJK RADICAL GRASS THREE;So;0;ON;;;;;N;;;;;
+2EC1;CJK RADICAL TIGER;So;0;ON;;;;;N;;;;;
+2EC2;CJK RADICAL CLOTHES;So;0;ON;;;;;N;;;;;
+2EC3;CJK RADICAL WEST ONE;So;0;ON;;;;;N;;;;;
+2EC4;CJK RADICAL WEST TWO;So;0;ON;;;;;N;;;;;
+2EC5;CJK RADICAL C-SIMPLIFIED SEE;So;0;ON;;;;;N;;;;;
+2EC6;CJK RADICAL SIMPLIFIED HORN;So;0;ON;;;;;N;;;;;
+2EC7;CJK RADICAL HORN;So;0;ON;;;;;N;;;;;
+2EC8;CJK RADICAL C-SIMPLIFIED SPEECH;So;0;ON;;;;;N;;;;;
+2EC9;CJK RADICAL C-SIMPLIFIED SHELL;So;0;ON;;;;;N;;;;;
+2ECA;CJK RADICAL FOOT;So;0;ON;;;;;N;;;;;
+2ECB;CJK RADICAL C-SIMPLIFIED CART;So;0;ON;;;;;N;;;;;
+2ECC;CJK RADICAL SIMPLIFIED WALK;So;0;ON;;;;;N;;;;;
+2ECD;CJK RADICAL WALK ONE;So;0;ON;;;;;N;;;;;
+2ECE;CJK RADICAL WALK TWO;So;0;ON;;;;;N;;;;;
+2ECF;CJK RADICAL CITY;So;0;ON;;;;;N;;;;;
+2ED0;CJK RADICAL C-SIMPLIFIED GOLD;So;0;ON;;;;;N;;;;;
+2ED1;CJK RADICAL LONG ONE;So;0;ON;;;;;N;;;;;
+2ED2;CJK RADICAL LONG TWO;So;0;ON;;;;;N;;;;;
+2ED3;CJK RADICAL C-SIMPLIFIED LONG;So;0;ON;;;;;N;;;;;
+2ED4;CJK RADICAL C-SIMPLIFIED GATE;So;0;ON;;;;;N;;;;;
+2ED5;CJK RADICAL MOUND ONE;So;0;ON;;;;;N;;;;;
+2ED6;CJK RADICAL MOUND TWO;So;0;ON;;;;;N;;;;;
+2ED7;CJK RADICAL RAIN;So;0;ON;;;;;N;;;;;
+2ED8;CJK RADICAL BLUE;So;0;ON;;;;;N;;;;;
+2ED9;CJK RADICAL C-SIMPLIFIED TANNED LEATHER;So;0;ON;;;;;N;;;;;
+2EDA;CJK RADICAL C-SIMPLIFIED LEAF;So;0;ON;;;;;N;;;;;
+2EDB;CJK RADICAL C-SIMPLIFIED WIND;So;0;ON;;;;;N;;;;;
+2EDC;CJK RADICAL C-SIMPLIFIED FLY;So;0;ON;;;;;N;;;;;
+2EDD;CJK RADICAL EAT ONE;So;0;ON;;;;;N;;;;;
+2EDE;CJK RADICAL EAT TWO;So;0;ON;;;;;N;;;;;
+2EDF;CJK RADICAL EAT THREE;So;0;ON;;;;;N;;;;;
+2EE0;CJK RADICAL C-SIMPLIFIED EAT;So;0;ON;;;;;N;;;;;
+2EE1;CJK RADICAL HEAD;So;0;ON;;;;;N;;;;;
+2EE2;CJK RADICAL C-SIMPLIFIED HORSE;So;0;ON;;;;;N;;;;;
+2EE3;CJK RADICAL BONE;So;0;ON;;;;;N;;;;;
+2EE4;CJK RADICAL GHOST;So;0;ON;;;;;N;;;;;
+2EE5;CJK RADICAL C-SIMPLIFIED FISH;So;0;ON;;;;;N;;;;;
+2EE6;CJK RADICAL C-SIMPLIFIED BIRD;So;0;ON;;;;;N;;;;;
+2EE7;CJK RADICAL C-SIMPLIFIED SALT;So;0;ON;;;;;N;;;;;
+2EE8;CJK RADICAL SIMPLIFIED WHEAT;So;0;ON;;;;;N;;;;;
+2EE9;CJK RADICAL SIMPLIFIED YELLOW;So;0;ON;;;;;N;;;;;
+2EEA;CJK RADICAL C-SIMPLIFIED FROG;So;0;ON;;;;;N;;;;;
+2EEB;CJK RADICAL J-SIMPLIFIED EVEN;So;0;ON;;;;;N;;;;;
+2EEC;CJK RADICAL C-SIMPLIFIED EVEN;So;0;ON;;;;;N;;;;;
+2EED;CJK RADICAL J-SIMPLIFIED TOOTH;So;0;ON;;;;;N;;;;;
+2EEE;CJK RADICAL C-SIMPLIFIED TOOTH;So;0;ON;;;;;N;;;;;
+2EEF;CJK RADICAL J-SIMPLIFIED DRAGON;So;0;ON;;;;;N;;;;;
+2EF0;CJK RADICAL C-SIMPLIFIED DRAGON;So;0;ON;;;;;N;;;;;
+2EF1;CJK RADICAL TURTLE;So;0;ON;;;;;N;;;;;
+2EF2;CJK RADICAL J-SIMPLIFIED TURTLE;So;0;ON;;;;;N;;;;;
+2EF3;CJK RADICAL C-SIMPLIFIED TURTLE;So;0;ON;<compat> 9F9F;;;;N;;;;;
+2F00;KANGXI RADICAL ONE;So;0;ON;<compat> 4E00;;;;N;;;;;
+2F01;KANGXI RADICAL LINE;So;0;ON;<compat> 4E28;;;;N;;;;;
+2F02;KANGXI RADICAL DOT;So;0;ON;<compat> 4E36;;;;N;;;;;
+2F03;KANGXI RADICAL SLASH;So;0;ON;<compat> 4E3F;;;;N;;;;;
+2F04;KANGXI RADICAL SECOND;So;0;ON;<compat> 4E59;;;;N;;;;;
+2F05;KANGXI RADICAL HOOK;So;0;ON;<compat> 4E85;;;;N;;;;;
+2F06;KANGXI RADICAL TWO;So;0;ON;<compat> 4E8C;;;;N;;;;;
+2F07;KANGXI RADICAL LID;So;0;ON;<compat> 4EA0;;;;N;;;;;
+2F08;KANGXI RADICAL MAN;So;0;ON;<compat> 4EBA;;;;N;;;;;
+2F09;KANGXI RADICAL LEGS;So;0;ON;<compat> 513F;;;;N;;;;;
+2F0A;KANGXI RADICAL ENTER;So;0;ON;<compat> 5165;;;;N;;;;;
+2F0B;KANGXI RADICAL EIGHT;So;0;ON;<compat> 516B;;;;N;;;;;
+2F0C;KANGXI RADICAL DOWN BOX;So;0;ON;<compat> 5182;;;;N;;;;;
+2F0D;KANGXI RADICAL COVER;So;0;ON;<compat> 5196;;;;N;;;;;
+2F0E;KANGXI RADICAL ICE;So;0;ON;<compat> 51AB;;;;N;;;;;
+2F0F;KANGXI RADICAL TABLE;So;0;ON;<compat> 51E0;;;;N;;;;;
+2F10;KANGXI RADICAL OPEN BOX;So;0;ON;<compat> 51F5;;;;N;;;;;
+2F11;KANGXI RADICAL KNIFE;So;0;ON;<compat> 5200;;;;N;;;;;
+2F12;KANGXI RADICAL POWER;So;0;ON;<compat> 529B;;;;N;;;;;
+2F13;KANGXI RADICAL WRAP;So;0;ON;<compat> 52F9;;;;N;;;;;
+2F14;KANGXI RADICAL SPOON;So;0;ON;<compat> 5315;;;;N;;;;;
+2F15;KANGXI RADICAL RIGHT OPEN BOX;So;0;ON;<compat> 531A;;;;N;;;;;
+2F16;KANGXI RADICAL HIDING ENCLOSURE;So;0;ON;<compat> 5338;;;;N;;;;;
+2F17;KANGXI RADICAL TEN;So;0;ON;<compat> 5341;;;;N;;;;;
+2F18;KANGXI RADICAL DIVINATION;So;0;ON;<compat> 535C;;;;N;;;;;
+2F19;KANGXI RADICAL SEAL;So;0;ON;<compat> 5369;;;;N;;;;;
+2F1A;KANGXI RADICAL CLIFF;So;0;ON;<compat> 5382;;;;N;;;;;
+2F1B;KANGXI RADICAL PRIVATE;So;0;ON;<compat> 53B6;;;;N;;;;;
+2F1C;KANGXI RADICAL AGAIN;So;0;ON;<compat> 53C8;;;;N;;;;;
+2F1D;KANGXI RADICAL MOUTH;So;0;ON;<compat> 53E3;;;;N;;;;;
+2F1E;KANGXI RADICAL ENCLOSURE;So;0;ON;<compat> 56D7;;;;N;;;;;
+2F1F;KANGXI RADICAL EARTH;So;0;ON;<compat> 571F;;;;N;;;;;
+2F20;KANGXI RADICAL SCHOLAR;So;0;ON;<compat> 58EB;;;;N;;;;;
+2F21;KANGXI RADICAL GO;So;0;ON;<compat> 5902;;;;N;;;;;
+2F22;KANGXI RADICAL GO SLOWLY;So;0;ON;<compat> 590A;;;;N;;;;;
+2F23;KANGXI RADICAL EVENING;So;0;ON;<compat> 5915;;;;N;;;;;
+2F24;KANGXI RADICAL BIG;So;0;ON;<compat> 5927;;;;N;;;;;
+2F25;KANGXI RADICAL WOMAN;So;0;ON;<compat> 5973;;;;N;;;;;
+2F26;KANGXI RADICAL CHILD;So;0;ON;<compat> 5B50;;;;N;;;;;
+2F27;KANGXI RADICAL ROOF;So;0;ON;<compat> 5B80;;;;N;;;;;
+2F28;KANGXI RADICAL INCH;So;0;ON;<compat> 5BF8;;;;N;;;;;
+2F29;KANGXI RADICAL SMALL;So;0;ON;<compat> 5C0F;;;;N;;;;;
+2F2A;KANGXI RADICAL LAME;So;0;ON;<compat> 5C22;;;;N;;;;;
+2F2B;KANGXI RADICAL CORPSE;So;0;ON;<compat> 5C38;;;;N;;;;;
+2F2C;KANGXI RADICAL SPROUT;So;0;ON;<compat> 5C6E;;;;N;;;;;
+2F2D;KANGXI RADICAL MOUNTAIN;So;0;ON;<compat> 5C71;;;;N;;;;;
+2F2E;KANGXI RADICAL RIVER;So;0;ON;<compat> 5DDB;;;;N;;;;;
+2F2F;KANGXI RADICAL WORK;So;0;ON;<compat> 5DE5;;;;N;;;;;
+2F30;KANGXI RADICAL ONESELF;So;0;ON;<compat> 5DF1;;;;N;;;;;
+2F31;KANGXI RADICAL TURBAN;So;0;ON;<compat> 5DFE;;;;N;;;;;
+2F32;KANGXI RADICAL DRY;So;0;ON;<compat> 5E72;;;;N;;;;;
+2F33;KANGXI RADICAL SHORT THREAD;So;0;ON;<compat> 5E7A;;;;N;;;;;
+2F34;KANGXI RADICAL DOTTED CLIFF;So;0;ON;<compat> 5E7F;;;;N;;;;;
+2F35;KANGXI RADICAL LONG STRIDE;So;0;ON;<compat> 5EF4;;;;N;;;;;
+2F36;KANGXI RADICAL TWO HANDS;So;0;ON;<compat> 5EFE;;;;N;;;;;
+2F37;KANGXI RADICAL SHOOT;So;0;ON;<compat> 5F0B;;;;N;;;;;
+2F38;KANGXI RADICAL BOW;So;0;ON;<compat> 5F13;;;;N;;;;;
+2F39;KANGXI RADICAL SNOUT;So;0;ON;<compat> 5F50;;;;N;;;;;
+2F3A;KANGXI RADICAL BRISTLE;So;0;ON;<compat> 5F61;;;;N;;;;;
+2F3B;KANGXI RADICAL STEP;So;0;ON;<compat> 5F73;;;;N;;;;;
+2F3C;KANGXI RADICAL HEART;So;0;ON;<compat> 5FC3;;;;N;;;;;
+2F3D;KANGXI RADICAL HALBERD;So;0;ON;<compat> 6208;;;;N;;;;;
+2F3E;KANGXI RADICAL DOOR;So;0;ON;<compat> 6236;;;;N;;;;;
+2F3F;KANGXI RADICAL HAND;So;0;ON;<compat> 624B;;;;N;;;;;
+2F40;KANGXI RADICAL BRANCH;So;0;ON;<compat> 652F;;;;N;;;;;
+2F41;KANGXI RADICAL RAP;So;0;ON;<compat> 6534;;;;N;;;;;
+2F42;KANGXI RADICAL SCRIPT;So;0;ON;<compat> 6587;;;;N;;;;;
+2F43;KANGXI RADICAL DIPPER;So;0;ON;<compat> 6597;;;;N;;;;;
+2F44;KANGXI RADICAL AXE;So;0;ON;<compat> 65A4;;;;N;;;;;
+2F45;KANGXI RADICAL SQUARE;So;0;ON;<compat> 65B9;;;;N;;;;;
+2F46;KANGXI RADICAL NOT;So;0;ON;<compat> 65E0;;;;N;;;;;
+2F47;KANGXI RADICAL SUN;So;0;ON;<compat> 65E5;;;;N;;;;;
+2F48;KANGXI RADICAL SAY;So;0;ON;<compat> 66F0;;;;N;;;;;
+2F49;KANGXI RADICAL MOON;So;0;ON;<compat> 6708;;;;N;;;;;
+2F4A;KANGXI RADICAL TREE;So;0;ON;<compat> 6728;;;;N;;;;;
+2F4B;KANGXI RADICAL LACK;So;0;ON;<compat> 6B20;;;;N;;;;;
+2F4C;KANGXI RADICAL STOP;So;0;ON;<compat> 6B62;;;;N;;;;;
+2F4D;KANGXI RADICAL DEATH;So;0;ON;<compat> 6B79;;;;N;;;;;
+2F4E;KANGXI RADICAL WEAPON;So;0;ON;<compat> 6BB3;;;;N;;;;;
+2F4F;KANGXI RADICAL DO NOT;So;0;ON;<compat> 6BCB;;;;N;;;;;
+2F50;KANGXI RADICAL COMPARE;So;0;ON;<compat> 6BD4;;;;N;;;;;
+2F51;KANGXI RADICAL FUR;So;0;ON;<compat> 6BDB;;;;N;;;;;
+2F52;KANGXI RADICAL CLAN;So;0;ON;<compat> 6C0F;;;;N;;;;;
+2F53;KANGXI RADICAL STEAM;So;0;ON;<compat> 6C14;;;;N;;;;;
+2F54;KANGXI RADICAL WATER;So;0;ON;<compat> 6C34;;;;N;;;;;
+2F55;KANGXI RADICAL FIRE;So;0;ON;<compat> 706B;;;;N;;;;;
+2F56;KANGXI RADICAL CLAW;So;0;ON;<compat> 722A;;;;N;;;;;
+2F57;KANGXI RADICAL FATHER;So;0;ON;<compat> 7236;;;;N;;;;;
+2F58;KANGXI RADICAL DOUBLE X;So;0;ON;<compat> 723B;;;;N;;;;;
+2F59;KANGXI RADICAL HALF TREE TRUNK;So;0;ON;<compat> 723F;;;;N;;;;;
+2F5A;KANGXI RADICAL SLICE;So;0;ON;<compat> 7247;;;;N;;;;;
+2F5B;KANGXI RADICAL FANG;So;0;ON;<compat> 7259;;;;N;;;;;
+2F5C;KANGXI RADICAL COW;So;0;ON;<compat> 725B;;;;N;;;;;
+2F5D;KANGXI RADICAL DOG;So;0;ON;<compat> 72AC;;;;N;;;;;
+2F5E;KANGXI RADICAL PROFOUND;So;0;ON;<compat> 7384;;;;N;;;;;
+2F5F;KANGXI RADICAL JADE;So;0;ON;<compat> 7389;;;;N;;;;;
+2F60;KANGXI RADICAL MELON;So;0;ON;<compat> 74DC;;;;N;;;;;
+2F61;KANGXI RADICAL TILE;So;0;ON;<compat> 74E6;;;;N;;;;;
+2F62;KANGXI RADICAL SWEET;So;0;ON;<compat> 7518;;;;N;;;;;
+2F63;KANGXI RADICAL LIFE;So;0;ON;<compat> 751F;;;;N;;;;;
+2F64;KANGXI RADICAL USE;So;0;ON;<compat> 7528;;;;N;;;;;
+2F65;KANGXI RADICAL FIELD;So;0;ON;<compat> 7530;;;;N;;;;;
+2F66;KANGXI RADICAL BOLT OF CLOTH;So;0;ON;<compat> 758B;;;;N;;;;;
+2F67;KANGXI RADICAL SICKNESS;So;0;ON;<compat> 7592;;;;N;;;;;
+2F68;KANGXI RADICAL DOTTED TENT;So;0;ON;<compat> 7676;;;;N;;;;;
+2F69;KANGXI RADICAL WHITE;So;0;ON;<compat> 767D;;;;N;;;;;
+2F6A;KANGXI RADICAL SKIN;So;0;ON;<compat> 76AE;;;;N;;;;;
+2F6B;KANGXI RADICAL DISH;So;0;ON;<compat> 76BF;;;;N;;;;;
+2F6C;KANGXI RADICAL EYE;So;0;ON;<compat> 76EE;;;;N;;;;;
+2F6D;KANGXI RADICAL SPEAR;So;0;ON;<compat> 77DB;;;;N;;;;;
+2F6E;KANGXI RADICAL ARROW;So;0;ON;<compat> 77E2;;;;N;;;;;
+2F6F;KANGXI RADICAL STONE;So;0;ON;<compat> 77F3;;;;N;;;;;
+2F70;KANGXI RADICAL SPIRIT;So;0;ON;<compat> 793A;;;;N;;;;;
+2F71;KANGXI RADICAL TRACK;So;0;ON;<compat> 79B8;;;;N;;;;;
+2F72;KANGXI RADICAL GRAIN;So;0;ON;<compat> 79BE;;;;N;;;;;
+2F73;KANGXI RADICAL CAVE;So;0;ON;<compat> 7A74;;;;N;;;;;
+2F74;KANGXI RADICAL STAND;So;0;ON;<compat> 7ACB;;;;N;;;;;
+2F75;KANGXI RADICAL BAMBOO;So;0;ON;<compat> 7AF9;;;;N;;;;;
+2F76;KANGXI RADICAL RICE;So;0;ON;<compat> 7C73;;;;N;;;;;
+2F77;KANGXI RADICAL SILK;So;0;ON;<compat> 7CF8;;;;N;;;;;
+2F78;KANGXI RADICAL JAR;So;0;ON;<compat> 7F36;;;;N;;;;;
+2F79;KANGXI RADICAL NET;So;0;ON;<compat> 7F51;;;;N;;;;;
+2F7A;KANGXI RADICAL SHEEP;So;0;ON;<compat> 7F8A;;;;N;;;;;
+2F7B;KANGXI RADICAL FEATHER;So;0;ON;<compat> 7FBD;;;;N;;;;;
+2F7C;KANGXI RADICAL OLD;So;0;ON;<compat> 8001;;;;N;;;;;
+2F7D;KANGXI RADICAL AND;So;0;ON;<compat> 800C;;;;N;;;;;
+2F7E;KANGXI RADICAL PLOW;So;0;ON;<compat> 8012;;;;N;;;;;
+2F7F;KANGXI RADICAL EAR;So;0;ON;<compat> 8033;;;;N;;;;;
+2F80;KANGXI RADICAL BRUSH;So;0;ON;<compat> 807F;;;;N;;;;;
+2F81;KANGXI RADICAL MEAT;So;0;ON;<compat> 8089;;;;N;;;;;
+2F82;KANGXI RADICAL MINISTER;So;0;ON;<compat> 81E3;;;;N;;;;;
+2F83;KANGXI RADICAL SELF;So;0;ON;<compat> 81EA;;;;N;;;;;
+2F84;KANGXI RADICAL ARRIVE;So;0;ON;<compat> 81F3;;;;N;;;;;
+2F85;KANGXI RADICAL MORTAR;So;0;ON;<compat> 81FC;;;;N;;;;;
+2F86;KANGXI RADICAL TONGUE;So;0;ON;<compat> 820C;;;;N;;;;;
+2F87;KANGXI RADICAL OPPOSE;So;0;ON;<compat> 821B;;;;N;;;;;
+2F88;KANGXI RADICAL BOAT;So;0;ON;<compat> 821F;;;;N;;;;;
+2F89;KANGXI RADICAL STOPPING;So;0;ON;<compat> 826E;;;;N;;;;;
+2F8A;KANGXI RADICAL COLOR;So;0;ON;<compat> 8272;;;;N;;;;;
+2F8B;KANGXI RADICAL GRASS;So;0;ON;<compat> 8278;;;;N;;;;;
+2F8C;KANGXI RADICAL TIGER;So;0;ON;<compat> 864D;;;;N;;;;;
+2F8D;KANGXI RADICAL INSECT;So;0;ON;<compat> 866B;;;;N;;;;;
+2F8E;KANGXI RADICAL BLOOD;So;0;ON;<compat> 8840;;;;N;;;;;
+2F8F;KANGXI RADICAL WALK ENCLOSURE;So;0;ON;<compat> 884C;;;;N;;;;;
+2F90;KANGXI RADICAL CLOTHES;So;0;ON;<compat> 8863;;;;N;;;;;
+2F91;KANGXI RADICAL WEST;So;0;ON;<compat> 897E;;;;N;;;;;
+2F92;KANGXI RADICAL SEE;So;0;ON;<compat> 898B;;;;N;;;;;
+2F93;KANGXI RADICAL HORN;So;0;ON;<compat> 89D2;;;;N;;;;;
+2F94;KANGXI RADICAL SPEECH;So;0;ON;<compat> 8A00;;;;N;;;;;
+2F95;KANGXI RADICAL VALLEY;So;0;ON;<compat> 8C37;;;;N;;;;;
+2F96;KANGXI RADICAL BEAN;So;0;ON;<compat> 8C46;;;;N;;;;;
+2F97;KANGXI RADICAL PIG;So;0;ON;<compat> 8C55;;;;N;;;;;
+2F98;KANGXI RADICAL BADGER;So;0;ON;<compat> 8C78;;;;N;;;;;
+2F99;KANGXI RADICAL SHELL;So;0;ON;<compat> 8C9D;;;;N;;;;;
+2F9A;KANGXI RADICAL RED;So;0;ON;<compat> 8D64;;;;N;;;;;
+2F9B;KANGXI RADICAL RUN;So;0;ON;<compat> 8D70;;;;N;;;;;
+2F9C;KANGXI RADICAL FOOT;So;0;ON;<compat> 8DB3;;;;N;;;;;
+2F9D;KANGXI RADICAL BODY;So;0;ON;<compat> 8EAB;;;;N;;;;;
+2F9E;KANGXI RADICAL CART;So;0;ON;<compat> 8ECA;;;;N;;;;;
+2F9F;KANGXI RADICAL BITTER;So;0;ON;<compat> 8F9B;;;;N;;;;;
+2FA0;KANGXI RADICAL MORNING;So;0;ON;<compat> 8FB0;;;;N;;;;;
+2FA1;KANGXI RADICAL WALK;So;0;ON;<compat> 8FB5;;;;N;;;;;
+2FA2;KANGXI RADICAL CITY;So;0;ON;<compat> 9091;;;;N;;;;;
+2FA3;KANGXI RADICAL WINE;So;0;ON;<compat> 9149;;;;N;;;;;
+2FA4;KANGXI RADICAL DISTINGUISH;So;0;ON;<compat> 91C6;;;;N;;;;;
+2FA5;KANGXI RADICAL VILLAGE;So;0;ON;<compat> 91CC;;;;N;;;;;
+2FA6;KANGXI RADICAL GOLD;So;0;ON;<compat> 91D1;;;;N;;;;;
+2FA7;KANGXI RADICAL LONG;So;0;ON;<compat> 9577;;;;N;;;;;
+2FA8;KANGXI RADICAL GATE;So;0;ON;<compat> 9580;;;;N;;;;;
+2FA9;KANGXI RADICAL MOUND;So;0;ON;<compat> 961C;;;;N;;;;;
+2FAA;KANGXI RADICAL SLAVE;So;0;ON;<compat> 96B6;;;;N;;;;;
+2FAB;KANGXI RADICAL SHORT TAILED BIRD;So;0;ON;<compat> 96B9;;;;N;;;;;
+2FAC;KANGXI RADICAL RAIN;So;0;ON;<compat> 96E8;;;;N;;;;;
+2FAD;KANGXI RADICAL BLUE;So;0;ON;<compat> 9751;;;;N;;;;;
+2FAE;KANGXI RADICAL WRONG;So;0;ON;<compat> 975E;;;;N;;;;;
+2FAF;KANGXI RADICAL FACE;So;0;ON;<compat> 9762;;;;N;;;;;
+2FB0;KANGXI RADICAL LEATHER;So;0;ON;<compat> 9769;;;;N;;;;;
+2FB1;KANGXI RADICAL TANNED LEATHER;So;0;ON;<compat> 97CB;;;;N;;;;;
+2FB2;KANGXI RADICAL LEEK;So;0;ON;<compat> 97ED;;;;N;;;;;
+2FB3;KANGXI RADICAL SOUND;So;0;ON;<compat> 97F3;;;;N;;;;;
+2FB4;KANGXI RADICAL LEAF;So;0;ON;<compat> 9801;;;;N;;;;;
+2FB5;KANGXI RADICAL WIND;So;0;ON;<compat> 98A8;;;;N;;;;;
+2FB6;KANGXI RADICAL FLY;So;0;ON;<compat> 98DB;;;;N;;;;;
+2FB7;KANGXI RADICAL EAT;So;0;ON;<compat> 98DF;;;;N;;;;;
+2FB8;KANGXI RADICAL HEAD;So;0;ON;<compat> 9996;;;;N;;;;;
+2FB9;KANGXI RADICAL FRAGRANT;So;0;ON;<compat> 9999;;;;N;;;;;
+2FBA;KANGXI RADICAL HORSE;So;0;ON;<compat> 99AC;;;;N;;;;;
+2FBB;KANGXI RADICAL BONE;So;0;ON;<compat> 9AA8;;;;N;;;;;
+2FBC;KANGXI RADICAL TALL;So;0;ON;<compat> 9AD8;;;;N;;;;;
+2FBD;KANGXI RADICAL HAIR;So;0;ON;<compat> 9ADF;;;;N;;;;;
+2FBE;KANGXI RADICAL FIGHT;So;0;ON;<compat> 9B25;;;;N;;;;;
+2FBF;KANGXI RADICAL SACRIFICIAL WINE;So;0;ON;<compat> 9B2F;;;;N;;;;;
+2FC0;KANGXI RADICAL CAULDRON;So;0;ON;<compat> 9B32;;;;N;;;;;
+2FC1;KANGXI RADICAL GHOST;So;0;ON;<compat> 9B3C;;;;N;;;;;
+2FC2;KANGXI RADICAL FISH;So;0;ON;<compat> 9B5A;;;;N;;;;;
+2FC3;KANGXI RADICAL BIRD;So;0;ON;<compat> 9CE5;;;;N;;;;;
+2FC4;KANGXI RADICAL SALT;So;0;ON;<compat> 9E75;;;;N;;;;;
+2FC5;KANGXI RADICAL DEER;So;0;ON;<compat> 9E7F;;;;N;;;;;
+2FC6;KANGXI RADICAL WHEAT;So;0;ON;<compat> 9EA5;;;;N;;;;;
+2FC7;KANGXI RADICAL HEMP;So;0;ON;<compat> 9EBB;;;;N;;;;;
+2FC8;KANGXI RADICAL YELLOW;So;0;ON;<compat> 9EC3;;;;N;;;;;
+2FC9;KANGXI RADICAL MILLET;So;0;ON;<compat> 9ECD;;;;N;;;;;
+2FCA;KANGXI RADICAL BLACK;So;0;ON;<compat> 9ED1;;;;N;;;;;
+2FCB;KANGXI RADICAL EMBROIDERY;So;0;ON;<compat> 9EF9;;;;N;;;;;
+2FCC;KANGXI RADICAL FROG;So;0;ON;<compat> 9EFD;;;;N;;;;;
+2FCD;KANGXI RADICAL TRIPOD;So;0;ON;<compat> 9F0E;;;;N;;;;;
+2FCE;KANGXI RADICAL DRUM;So;0;ON;<compat> 9F13;;;;N;;;;;
+2FCF;KANGXI RADICAL RAT;So;0;ON;<compat> 9F20;;;;N;;;;;
+2FD0;KANGXI RADICAL NOSE;So;0;ON;<compat> 9F3B;;;;N;;;;;
+2FD1;KANGXI RADICAL EVEN;So;0;ON;<compat> 9F4A;;;;N;;;;;
+2FD2;KANGXI RADICAL TOOTH;So;0;ON;<compat> 9F52;;;;N;;;;;
+2FD3;KANGXI RADICAL DRAGON;So;0;ON;<compat> 9F8D;;;;N;;;;;
+2FD4;KANGXI RADICAL TURTLE;So;0;ON;<compat> 9F9C;;;;N;;;;;
+2FD5;KANGXI RADICAL FLUTE;So;0;ON;<compat> 9FA0;;;;N;;;;;
+2FF0;IDEOGRAPHIC DESCRIPTION CHARACTER LEFT TO RIGHT;So;0;ON;;;;;N;;;;;
+2FF1;IDEOGRAPHIC DESCRIPTION CHARACTER ABOVE TO BELOW;So;0;ON;;;;;N;;;;;
+2FF2;IDEOGRAPHIC DESCRIPTION CHARACTER LEFT TO MIDDLE AND RIGHT;So;0;ON;;;;;N;;;;;
+2FF3;IDEOGRAPHIC DESCRIPTION CHARACTER ABOVE TO MIDDLE AND BELOW;So;0;ON;;;;;N;;;;;
+2FF4;IDEOGRAPHIC DESCRIPTION CHARACTER FULL SURROUND;So;0;ON;;;;;N;;;;;
+2FF5;IDEOGRAPHIC DESCRIPTION CHARACTER SURROUND FROM ABOVE;So;0;ON;;;;;N;;;;;
+2FF6;IDEOGRAPHIC DESCRIPTION CHARACTER SURROUND FROM BELOW;So;0;ON;;;;;N;;;;;
+2FF7;IDEOGRAPHIC DESCRIPTION CHARACTER SURROUND FROM LEFT;So;0;ON;;;;;N;;;;;
+2FF8;IDEOGRAPHIC DESCRIPTION CHARACTER SURROUND FROM UPPER LEFT;So;0;ON;;;;;N;;;;;
+2FF9;IDEOGRAPHIC DESCRIPTION CHARACTER SURROUND FROM UPPER RIGHT;So;0;ON;;;;;N;;;;;
+2FFA;IDEOGRAPHIC DESCRIPTION CHARACTER SURROUND FROM LOWER LEFT;So;0;ON;;;;;N;;;;;
+2FFB;IDEOGRAPHIC DESCRIPTION CHARACTER OVERLAID;So;0;ON;;;;;N;;;;;
+3000;IDEOGRAPHIC SPACE;Zs;0;WS;<wide> 0020;;;;N;;;;;
+3001;IDEOGRAPHIC COMMA;Po;0;ON;;;;;N;;;;;
+3002;IDEOGRAPHIC FULL STOP;Po;0;ON;;;;;N;IDEOGRAPHIC PERIOD;;;;
+3003;DITTO MARK;Po;0;ON;;;;;N;;;;;
+3004;JAPANESE INDUSTRIAL STANDARD SYMBOL;So;0;ON;;;;;N;;;;;
+3005;IDEOGRAPHIC ITERATION MARK;Lm;0;L;;;;;N;;;;;
+3006;IDEOGRAPHIC CLOSING MARK;Lo;0;L;;;;;N;;;;;
+3007;IDEOGRAPHIC NUMBER ZERO;Nl;0;L;;;;0;N;;;;;
+3008;LEFT ANGLE BRACKET;Ps;0;ON;;;;;Y;OPENING ANGLE BRACKET;;;;
+3009;RIGHT ANGLE BRACKET;Pe;0;ON;;;;;Y;CLOSING ANGLE BRACKET;;;;
+300A;LEFT DOUBLE ANGLE BRACKET;Ps;0;ON;;;;;Y;OPENING DOUBLE ANGLE BRACKET;;;;
+300B;RIGHT DOUBLE ANGLE BRACKET;Pe;0;ON;;;;;Y;CLOSING DOUBLE ANGLE BRACKET;;;;
+300C;LEFT CORNER BRACKET;Ps;0;ON;;;;;Y;OPENING CORNER BRACKET;;;;
+300D;RIGHT CORNER BRACKET;Pe;0;ON;;;;;Y;CLOSING CORNER BRACKET;;;;
+300E;LEFT WHITE CORNER BRACKET;Ps;0;ON;;;;;Y;OPENING WHITE CORNER BRACKET;;;;
+300F;RIGHT WHITE CORNER BRACKET;Pe;0;ON;;;;;Y;CLOSING WHITE CORNER BRACKET;;;;
+3010;LEFT BLACK LENTICULAR BRACKET;Ps;0;ON;;;;;Y;OPENING BLACK LENTICULAR BRACKET;;;;
+3011;RIGHT BLACK LENTICULAR BRACKET;Pe;0;ON;;;;;Y;CLOSING BLACK LENTICULAR BRACKET;;;;
+3012;POSTAL MARK;So;0;ON;;;;;N;;;;;
+3013;GETA MARK;So;0;ON;;;;;N;;;;;
+3014;LEFT TORTOISE SHELL BRACKET;Ps;0;ON;;;;;Y;OPENING TORTOISE SHELL BRACKET;;;;
+3015;RIGHT TORTOISE SHELL BRACKET;Pe;0;ON;;;;;Y;CLOSING TORTOISE SHELL BRACKET;;;;
+3016;LEFT WHITE LENTICULAR BRACKET;Ps;0;ON;;;;;Y;OPENING WHITE LENTICULAR BRACKET;;;;
+3017;RIGHT WHITE LENTICULAR BRACKET;Pe;0;ON;;;;;Y;CLOSING WHITE LENTICULAR BRACKET;;;;
+3018;LEFT WHITE TORTOISE SHELL BRACKET;Ps;0;ON;;;;;Y;OPENING WHITE TORTOISE SHELL BRACKET;;;;
+3019;RIGHT WHITE TORTOISE SHELL BRACKET;Pe;0;ON;;;;;Y;CLOSING WHITE TORTOISE SHELL BRACKET;;;;
+301A;LEFT WHITE SQUARE BRACKET;Ps;0;ON;;;;;Y;OPENING WHITE SQUARE BRACKET;;;;
+301B;RIGHT WHITE SQUARE BRACKET;Pe;0;ON;;;;;Y;CLOSING WHITE SQUARE BRACKET;;;;
+301C;WAVE DASH;Pd;0;ON;;;;;N;;;;;
+301D;REVERSED DOUBLE PRIME QUOTATION MARK;Ps;0;ON;;;;;N;;;;;
+301E;DOUBLE PRIME QUOTATION MARK;Pe;0;ON;;;;;N;;;;;
+301F;LOW DOUBLE PRIME QUOTATION MARK;Pe;0;ON;;;;;N;;;;;
+3020;POSTAL MARK FACE;So;0;ON;;;;;N;;;;;
+3021;HANGZHOU NUMERAL ONE;Nl;0;L;;;;1;N;;;;;
+3022;HANGZHOU NUMERAL TWO;Nl;0;L;;;;2;N;;;;;
+3023;HANGZHOU NUMERAL THREE;Nl;0;L;;;;3;N;;;;;
+3024;HANGZHOU NUMERAL FOUR;Nl;0;L;;;;4;N;;;;;
+3025;HANGZHOU NUMERAL FIVE;Nl;0;L;;;;5;N;;;;;
+3026;HANGZHOU NUMERAL SIX;Nl;0;L;;;;6;N;;;;;
+3027;HANGZHOU NUMERAL SEVEN;Nl;0;L;;;;7;N;;;;;
+3028;HANGZHOU NUMERAL EIGHT;Nl;0;L;;;;8;N;;;;;
+3029;HANGZHOU NUMERAL NINE;Nl;0;L;;;;9;N;;;;;
+302A;IDEOGRAPHIC LEVEL TONE MARK;Mn;218;NSM;;;;;N;;;;;
+302B;IDEOGRAPHIC RISING TONE MARK;Mn;228;NSM;;;;;N;;;;;
+302C;IDEOGRAPHIC DEPARTING TONE MARK;Mn;232;NSM;;;;;N;;;;;
+302D;IDEOGRAPHIC ENTERING TONE MARK;Mn;222;NSM;;;;;N;;;;;
+302E;HANGUL SINGLE DOT TONE MARK;Mn;224;NSM;;;;;N;;;;;
+302F;HANGUL DOUBLE DOT TONE MARK;Mn;224;NSM;;;;;N;;;;;
+3030;WAVY DASH;Pd;0;ON;;;;;N;;;;;
+3031;VERTICAL KANA REPEAT MARK;Lm;0;L;;;;;N;;;;;
+3032;VERTICAL KANA REPEAT WITH VOICED SOUND MARK;Lm;0;L;;;;;N;;;;;
+3033;VERTICAL KANA REPEAT MARK UPPER HALF;Lm;0;L;;;;;N;;;;;
+3034;VERTICAL KANA REPEAT WITH VOICED SOUND MARK UPPER HALF;Lm;0;L;;;;;N;;;;;
+3035;VERTICAL KANA REPEAT MARK LOWER HALF;Lm;0;L;;;;;N;;;;;
+3036;CIRCLED POSTAL MARK;So;0;ON;<compat> 3012;;;;N;;;;;
+3037;IDEOGRAPHIC TELEGRAPH LINE FEED SEPARATOR SYMBOL;So;0;ON;;;;;N;;;;;
+3038;HANGZHOU NUMERAL TEN;Nl;0;L;<compat> 5341;;;10;N;;;;;
+3039;HANGZHOU NUMERAL TWENTY;Nl;0;L;<compat> 5344;;;20;N;;;;;
+303A;HANGZHOU NUMERAL THIRTY;Nl;0;L;<compat> 5345;;;30;N;;;;;
+303B;VERTICAL IDEOGRAPHIC ITERATION MARK;Lm;0;L;;;;;N;;;;;
+303C;MASU MARK;Lo;0;L;;;;;N;;;;;
+303D;PART ALTERNATION MARK;Po;0;ON;;;;;N;;;;;
+303E;IDEOGRAPHIC VARIATION INDICATOR;So;0;ON;;;;;N;;;;;
+303F;IDEOGRAPHIC HALF FILL SPACE;So;0;ON;;;;;N;;;;;
+3041;HIRAGANA LETTER SMALL A;Lo;0;L;;;;;N;;;;;
+3042;HIRAGANA LETTER A;Lo;0;L;;;;;N;;;;;
+3043;HIRAGANA LETTER SMALL I;Lo;0;L;;;;;N;;;;;
+3044;HIRAGANA LETTER I;Lo;0;L;;;;;N;;;;;
+3045;HIRAGANA LETTER SMALL U;Lo;0;L;;;;;N;;;;;
+3046;HIRAGANA LETTER U;Lo;0;L;;;;;N;;;;;
+3047;HIRAGANA LETTER SMALL E;Lo;0;L;;;;;N;;;;;
+3048;HIRAGANA LETTER E;Lo;0;L;;;;;N;;;;;
+3049;HIRAGANA LETTER SMALL O;Lo;0;L;;;;;N;;;;;
+304A;HIRAGANA LETTER O;Lo;0;L;;;;;N;;;;;
+304B;HIRAGANA LETTER KA;Lo;0;L;;;;;N;;;;;
+304C;HIRAGANA LETTER GA;Lo;0;L;304B 3099;;;;N;;;;;
+304D;HIRAGANA LETTER KI;Lo;0;L;;;;;N;;;;;
+304E;HIRAGANA LETTER GI;Lo;0;L;304D 3099;;;;N;;;;;
+304F;HIRAGANA LETTER KU;Lo;0;L;;;;;N;;;;;
+3050;HIRAGANA LETTER GU;Lo;0;L;304F 3099;;;;N;;;;;
+3051;HIRAGANA LETTER KE;Lo;0;L;;;;;N;;;;;
+3052;HIRAGANA LETTER GE;Lo;0;L;3051 3099;;;;N;;;;;
+3053;HIRAGANA LETTER KO;Lo;0;L;;;;;N;;;;;
+3054;HIRAGANA LETTER GO;Lo;0;L;3053 3099;;;;N;;;;;
+3055;HIRAGANA LETTER SA;Lo;0;L;;;;;N;;;;;
+3056;HIRAGANA LETTER ZA;Lo;0;L;3055 3099;;;;N;;;;;
+3057;HIRAGANA LETTER SI;Lo;0;L;;;;;N;;;;;
+3058;HIRAGANA LETTER ZI;Lo;0;L;3057 3099;;;;N;;;;;
+3059;HIRAGANA LETTER SU;Lo;0;L;;;;;N;;;;;
+305A;HIRAGANA LETTER ZU;Lo;0;L;3059 3099;;;;N;;;;;
+305B;HIRAGANA LETTER SE;Lo;0;L;;;;;N;;;;;
+305C;HIRAGANA LETTER ZE;Lo;0;L;305B 3099;;;;N;;;;;
+305D;HIRAGANA LETTER SO;Lo;0;L;;;;;N;;;;;
+305E;HIRAGANA LETTER ZO;Lo;0;L;305D 3099;;;;N;;;;;
+305F;HIRAGANA LETTER TA;Lo;0;L;;;;;N;;;;;
+3060;HIRAGANA LETTER DA;Lo;0;L;305F 3099;;;;N;;;;;
+3061;HIRAGANA LETTER TI;Lo;0;L;;;;;N;;;;;
+3062;HIRAGANA LETTER DI;Lo;0;L;3061 3099;;;;N;;;;;
+3063;HIRAGANA LETTER SMALL TU;Lo;0;L;;;;;N;;;;;
+3064;HIRAGANA LETTER TU;Lo;0;L;;;;;N;;;;;
+3065;HIRAGANA LETTER DU;Lo;0;L;3064 3099;;;;N;;;;;
+3066;HIRAGANA LETTER TE;Lo;0;L;;;;;N;;;;;
+3067;HIRAGANA LETTER DE;Lo;0;L;3066 3099;;;;N;;;;;
+3068;HIRAGANA LETTER TO;Lo;0;L;;;;;N;;;;;
+3069;HIRAGANA LETTER DO;Lo;0;L;3068 3099;;;;N;;;;;
+306A;HIRAGANA LETTER NA;Lo;0;L;;;;;N;;;;;
+306B;HIRAGANA LETTER NI;Lo;0;L;;;;;N;;;;;
+306C;HIRAGANA LETTER NU;Lo;0;L;;;;;N;;;;;
+306D;HIRAGANA LETTER NE;Lo;0;L;;;;;N;;;;;
+306E;HIRAGANA LETTER NO;Lo;0;L;;;;;N;;;;;
+306F;HIRAGANA LETTER HA;Lo;0;L;;;;;N;;;;;
+3070;HIRAGANA LETTER BA;Lo;0;L;306F 3099;;;;N;;;;;
+3071;HIRAGANA LETTER PA;Lo;0;L;306F 309A;;;;N;;;;;
+3072;HIRAGANA LETTER HI;Lo;0;L;;;;;N;;;;;
+3073;HIRAGANA LETTER BI;Lo;0;L;3072 3099;;;;N;;;;;
+3074;HIRAGANA LETTER PI;Lo;0;L;3072 309A;;;;N;;;;;
+3075;HIRAGANA LETTER HU;Lo;0;L;;;;;N;;;;;
+3076;HIRAGANA LETTER BU;Lo;0;L;3075 3099;;;;N;;;;;
+3077;HIRAGANA LETTER PU;Lo;0;L;3075 309A;;;;N;;;;;
+3078;HIRAGANA LETTER HE;Lo;0;L;;;;;N;;;;;
+3079;HIRAGANA LETTER BE;Lo;0;L;3078 3099;;;;N;;;;;
+307A;HIRAGANA LETTER PE;Lo;0;L;3078 309A;;;;N;;;;;
+307B;HIRAGANA LETTER HO;Lo;0;L;;;;;N;;;;;
+307C;HIRAGANA LETTER BO;Lo;0;L;307B 3099;;;;N;;;;;
+307D;HIRAGANA LETTER PO;Lo;0;L;307B 309A;;;;N;;;;;
+307E;HIRAGANA LETTER MA;Lo;0;L;;;;;N;;;;;
+307F;HIRAGANA LETTER MI;Lo;0;L;;;;;N;;;;;
+3080;HIRAGANA LETTER MU;Lo;0;L;;;;;N;;;;;
+3081;HIRAGANA LETTER ME;Lo;0;L;;;;;N;;;;;
+3082;HIRAGANA LETTER MO;Lo;0;L;;;;;N;;;;;
+3083;HIRAGANA LETTER SMALL YA;Lo;0;L;;;;;N;;;;;
+3084;HIRAGANA LETTER YA;Lo;0;L;;;;;N;;;;;
+3085;HIRAGANA LETTER SMALL YU;Lo;0;L;;;;;N;;;;;
+3086;HIRAGANA LETTER YU;Lo;0;L;;;;;N;;;;;
+3087;HIRAGANA LETTER SMALL YO;Lo;0;L;;;;;N;;;;;
+3088;HIRAGANA LETTER YO;Lo;0;L;;;;;N;;;;;
+3089;HIRAGANA LETTER RA;Lo;0;L;;;;;N;;;;;
+308A;HIRAGANA LETTER RI;Lo;0;L;;;;;N;;;;;
+308B;HIRAGANA LETTER RU;Lo;0;L;;;;;N;;;;;
+308C;HIRAGANA LETTER RE;Lo;0;L;;;;;N;;;;;
+308D;HIRAGANA LETTER RO;Lo;0;L;;;;;N;;;;;
+308E;HIRAGANA LETTER SMALL WA;Lo;0;L;;;;;N;;;;;
+308F;HIRAGANA LETTER WA;Lo;0;L;;;;;N;;;;;
+3090;HIRAGANA LETTER WI;Lo;0;L;;;;;N;;;;;
+3091;HIRAGANA LETTER WE;Lo;0;L;;;;;N;;;;;
+3092;HIRAGANA LETTER WO;Lo;0;L;;;;;N;;;;;
+3093;HIRAGANA LETTER N;Lo;0;L;;;;;N;;;;;
+3094;HIRAGANA LETTER VU;Lo;0;L;3046 3099;;;;N;;;;;
+3095;HIRAGANA LETTER SMALL KA;Lo;0;L;;;;;N;;;;;
+3096;HIRAGANA LETTER SMALL KE;Lo;0;L;;;;;N;;;;;
+3099;COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK;Mn;8;NSM;;;;;N;NON-SPACING KATAKANA-HIRAGANA VOICED SOUND MARK;;;;
+309A;COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK;Mn;8;NSM;;;;;N;NON-SPACING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK;;;;
+309B;KATAKANA-HIRAGANA VOICED SOUND MARK;Sk;0;ON;<compat> 0020 3099;;;;N;;;;;
+309C;KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK;Sk;0;ON;<compat> 0020 309A;;;;N;;;;;
+309D;HIRAGANA ITERATION MARK;Lm;0;L;;;;;N;;;;;
+309E;HIRAGANA VOICED ITERATION MARK;Lm;0;L;309D 3099;;;;N;;;;;
+309F;HIRAGANA DIGRAPH YORI;Lo;0;L;<vertical> 3088 308A;;;;N;;;;;
+30A0;KATAKANA-HIRAGANA DOUBLE HYPHEN;Pd;0;ON;;;;;N;;;;;
+30A1;KATAKANA LETTER SMALL A;Lo;0;L;;;;;N;;;;;
+30A2;KATAKANA LETTER A;Lo;0;L;;;;;N;;;;;
+30A3;KATAKANA LETTER SMALL I;Lo;0;L;;;;;N;;;;;
+30A4;KATAKANA LETTER I;Lo;0;L;;;;;N;;;;;
+30A5;KATAKANA LETTER SMALL U;Lo;0;L;;;;;N;;;;;
+30A6;KATAKANA LETTER U;Lo;0;L;;;;;N;;;;;
+30A7;KATAKANA LETTER SMALL E;Lo;0;L;;;;;N;;;;;
+30A8;KATAKANA LETTER E;Lo;0;L;;;;;N;;;;;
+30A9;KATAKANA LETTER SMALL O;Lo;0;L;;;;;N;;;;;
+30AA;KATAKANA LETTER O;Lo;0;L;;;;;N;;;;;
+30AB;KATAKANA LETTER KA;Lo;0;L;;;;;N;;;;;
+30AC;KATAKANA LETTER GA;Lo;0;L;30AB 3099;;;;N;;;;;
+30AD;KATAKANA LETTER KI;Lo;0;L;;;;;N;;;;;
+30AE;KATAKANA LETTER GI;Lo;0;L;30AD 3099;;;;N;;;;;
+30AF;KATAKANA LETTER KU;Lo;0;L;;;;;N;;;;;
+30B0;KATAKANA LETTER GU;Lo;0;L;30AF 3099;;;;N;;;;;
+30B1;KATAKANA LETTER KE;Lo;0;L;;;;;N;;;;;
+30B2;KATAKANA LETTER GE;Lo;0;L;30B1 3099;;;;N;;;;;
+30B3;KATAKANA LETTER KO;Lo;0;L;;;;;N;;;;;
+30B4;KATAKANA LETTER GO;Lo;0;L;30B3 3099;;;;N;;;;;
+30B5;KATAKANA LETTER SA;Lo;0;L;;;;;N;;;;;
+30B6;KATAKANA LETTER ZA;Lo;0;L;30B5 3099;;;;N;;;;;
+30B7;KATAKANA LETTER SI;Lo;0;L;;;;;N;;;;;
+30B8;KATAKANA LETTER ZI;Lo;0;L;30B7 3099;;;;N;;;;;
+30B9;KATAKANA LETTER SU;Lo;0;L;;;;;N;;;;;
+30BA;KATAKANA LETTER ZU;Lo;0;L;30B9 3099;;;;N;;;;;
+30BB;KATAKANA LETTER SE;Lo;0;L;;;;;N;;;;;
+30BC;KATAKANA LETTER ZE;Lo;0;L;30BB 3099;;;;N;;;;;
+30BD;KATAKANA LETTER SO;Lo;0;L;;;;;N;;;;;
+30BE;KATAKANA LETTER ZO;Lo;0;L;30BD 3099;;;;N;;;;;
+30BF;KATAKANA LETTER TA;Lo;0;L;;;;;N;;;;;
+30C0;KATAKANA LETTER DA;Lo;0;L;30BF 3099;;;;N;;;;;
+30C1;KATAKANA LETTER TI;Lo;0;L;;;;;N;;;;;
+30C2;KATAKANA LETTER DI;Lo;0;L;30C1 3099;;;;N;;;;;
+30C3;KATAKANA LETTER SMALL TU;Lo;0;L;;;;;N;;;;;
+30C4;KATAKANA LETTER TU;Lo;0;L;;;;;N;;;;;
+30C5;KATAKANA LETTER DU;Lo;0;L;30C4 3099;;;;N;;;;;
+30C6;KATAKANA LETTER TE;Lo;0;L;;;;;N;;;;;
+30C7;KATAKANA LETTER DE;Lo;0;L;30C6 3099;;;;N;;;;;
+30C8;KATAKANA LETTER TO;Lo;0;L;;;;;N;;;;;
+30C9;KATAKANA LETTER DO;Lo;0;L;30C8 3099;;;;N;;;;;
+30CA;KATAKANA LETTER NA;Lo;0;L;;;;;N;;;;;
+30CB;KATAKANA LETTER NI;Lo;0;L;;;;;N;;;;;
+30CC;KATAKANA LETTER NU;Lo;0;L;;;;;N;;;;;
+30CD;KATAKANA LETTER NE;Lo;0;L;;;;;N;;;;;
+30CE;KATAKANA LETTER NO;Lo;0;L;;;;;N;;;;;
+30CF;KATAKANA LETTER HA;Lo;0;L;;;;;N;;;;;
+30D0;KATAKANA LETTER BA;Lo;0;L;30CF 3099;;;;N;;;;;
+30D1;KATAKANA LETTER PA;Lo;0;L;30CF 309A;;;;N;;;;;
+30D2;KATAKANA LETTER HI;Lo;0;L;;;;;N;;;;;
+30D3;KATAKANA LETTER BI;Lo;0;L;30D2 3099;;;;N;;;;;
+30D4;KATAKANA LETTER PI;Lo;0;L;30D2 309A;;;;N;;;;;
+30D5;KATAKANA LETTER HU;Lo;0;L;;;;;N;;;;;
+30D6;KATAKANA LETTER BU;Lo;0;L;30D5 3099;;;;N;;;;;
+30D7;KATAKANA LETTER PU;Lo;0;L;30D5 309A;;;;N;;;;;
+30D8;KATAKANA LETTER HE;Lo;0;L;;;;;N;;;;;
+30D9;KATAKANA LETTER BE;Lo;0;L;30D8 3099;;;;N;;;;;
+30DA;KATAKANA LETTER PE;Lo;0;L;30D8 309A;;;;N;;;;;
+30DB;KATAKANA LETTER HO;Lo;0;L;;;;;N;;;;;
+30DC;KATAKANA LETTER BO;Lo;0;L;30DB 3099;;;;N;;;;;
+30DD;KATAKANA LETTER PO;Lo;0;L;30DB 309A;;;;N;;;;;
+30DE;KATAKANA LETTER MA;Lo;0;L;;;;;N;;;;;
+30DF;KATAKANA LETTER MI;Lo;0;L;;;;;N;;;;;
+30E0;KATAKANA LETTER MU;Lo;0;L;;;;;N;;;;;
+30E1;KATAKANA LETTER ME;Lo;0;L;;;;;N;;;;;
+30E2;KATAKANA LETTER MO;Lo;0;L;;;;;N;;;;;
+30E3;KATAKANA LETTER SMALL YA;Lo;0;L;;;;;N;;;;;
+30E4;KATAKANA LETTER YA;Lo;0;L;;;;;N;;;;;
+30E5;KATAKANA LETTER SMALL YU;Lo;0;L;;;;;N;;;;;
+30E6;KATAKANA LETTER YU;Lo;0;L;;;;;N;;;;;
+30E7;KATAKANA LETTER SMALL YO;Lo;0;L;;;;;N;;;;;
+30E8;KATAKANA LETTER YO;Lo;0;L;;;;;N;;;;;
+30E9;KATAKANA LETTER RA;Lo;0;L;;;;;N;;;;;
+30EA;KATAKANA LETTER RI;Lo;0;L;;;;;N;;;;;
+30EB;KATAKANA LETTER RU;Lo;0;L;;;;;N;;;;;
+30EC;KATAKANA LETTER RE;Lo;0;L;;;;;N;;;;;
+30ED;KATAKANA LETTER RO;Lo;0;L;;;;;N;;;;;
+30EE;KATAKANA LETTER SMALL WA;Lo;0;L;;;;;N;;;;;
+30EF;KATAKANA LETTER WA;Lo;0;L;;;;;N;;;;;
+30F0;KATAKANA LETTER WI;Lo;0;L;;;;;N;;;;;
+30F1;KATAKANA LETTER WE;Lo;0;L;;;;;N;;;;;
+30F2;KATAKANA LETTER WO;Lo;0;L;;;;;N;;;;;
+30F3;KATAKANA LETTER N;Lo;0;L;;;;;N;;;;;
+30F4;KATAKANA LETTER VU;Lo;0;L;30A6 3099;;;;N;;;;;
+30F5;KATAKANA LETTER SMALL KA;Lo;0;L;;;;;N;;;;;
+30F6;KATAKANA LETTER SMALL KE;Lo;0;L;;;;;N;;;;;
+30F7;KATAKANA LETTER VA;Lo;0;L;30EF 3099;;;;N;;;;;
+30F8;KATAKANA LETTER VI;Lo;0;L;30F0 3099;;;;N;;;;;
+30F9;KATAKANA LETTER VE;Lo;0;L;30F1 3099;;;;N;;;;;
+30FA;KATAKANA LETTER VO;Lo;0;L;30F2 3099;;;;N;;;;;
+30FB;KATAKANA MIDDLE DOT;Pc;0;ON;;;;;N;;;;;
+30FC;KATAKANA-HIRAGANA PROLONGED SOUND MARK;Lm;0;L;;;;;N;;;;;
+30FD;KATAKANA ITERATION MARK;Lm;0;L;;;;;N;;;;;
+30FE;KATAKANA VOICED ITERATION MARK;Lm;0;L;30FD 3099;;;;N;;;;;
+30FF;KATAKANA DIGRAPH KOTO;Lo;0;L;<vertical> 30B3 30C8;;;;N;;;;;
+3105;BOPOMOFO LETTER B;Lo;0;L;;;;;N;;;;;
+3106;BOPOMOFO LETTER P;Lo;0;L;;;;;N;;;;;
+3107;BOPOMOFO LETTER M;Lo;0;L;;;;;N;;;;;
+3108;BOPOMOFO LETTER F;Lo;0;L;;;;;N;;;;;
+3109;BOPOMOFO LETTER D;Lo;0;L;;;;;N;;;;;
+310A;BOPOMOFO LETTER T;Lo;0;L;;;;;N;;;;;
+310B;BOPOMOFO LETTER N;Lo;0;L;;;;;N;;;;;
+310C;BOPOMOFO LETTER L;Lo;0;L;;;;;N;;;;;
+310D;BOPOMOFO LETTER G;Lo;0;L;;;;;N;;;;;
+310E;BOPOMOFO LETTER K;Lo;0;L;;;;;N;;;;;
+310F;BOPOMOFO LETTER H;Lo;0;L;;;;;N;;;;;
+3110;BOPOMOFO LETTER J;Lo;0;L;;;;;N;;;;;
+3111;BOPOMOFO LETTER Q;Lo;0;L;;;;;N;;;;;
+3112;BOPOMOFO LETTER X;Lo;0;L;;;;;N;;;;;
+3113;BOPOMOFO LETTER ZH;Lo;0;L;;;;;N;;;;;
+3114;BOPOMOFO LETTER CH;Lo;0;L;;;;;N;;;;;
+3115;BOPOMOFO LETTER SH;Lo;0;L;;;;;N;;;;;
+3116;BOPOMOFO LETTER R;Lo;0;L;;;;;N;;;;;
+3117;BOPOMOFO LETTER Z;Lo;0;L;;;;;N;;;;;
+3118;BOPOMOFO LETTER C;Lo;0;L;;;;;N;;;;;
+3119;BOPOMOFO LETTER S;Lo;0;L;;;;;N;;;;;
+311A;BOPOMOFO LETTER A;Lo;0;L;;;;;N;;;;;
+311B;BOPOMOFO LETTER O;Lo;0;L;;;;;N;;;;;
+311C;BOPOMOFO LETTER E;Lo;0;L;;;;;N;;;;;
+311D;BOPOMOFO LETTER EH;Lo;0;L;;;;;N;;;;;
+311E;BOPOMOFO LETTER AI;Lo;0;L;;;;;N;;;;;
+311F;BOPOMOFO LETTER EI;Lo;0;L;;;;;N;;;;;
+3120;BOPOMOFO LETTER AU;Lo;0;L;;;;;N;;;;;
+3121;BOPOMOFO LETTER OU;Lo;0;L;;;;;N;;;;;
+3122;BOPOMOFO LETTER AN;Lo;0;L;;;;;N;;;;;
+3123;BOPOMOFO LETTER EN;Lo;0;L;;;;;N;;;;;
+3124;BOPOMOFO LETTER ANG;Lo;0;L;;;;;N;;;;;
+3125;BOPOMOFO LETTER ENG;Lo;0;L;;;;;N;;;;;
+3126;BOPOMOFO LETTER ER;Lo;0;L;;;;;N;;;;;
+3127;BOPOMOFO LETTER I;Lo;0;L;;;;;N;;;;;
+3128;BOPOMOFO LETTER U;Lo;0;L;;;;;N;;;;;
+3129;BOPOMOFO LETTER IU;Lo;0;L;;;;;N;;;;;
+312A;BOPOMOFO LETTER V;Lo;0;L;;;;;N;;;;;
+312B;BOPOMOFO LETTER NG;Lo;0;L;;;;;N;;;;;
+312C;BOPOMOFO LETTER GN;Lo;0;L;;;;;N;;;;;
+3131;HANGUL LETTER KIYEOK;Lo;0;L;<compat> 1100;;;;N;HANGUL LETTER GIYEOG;;;;
+3132;HANGUL LETTER SSANGKIYEOK;Lo;0;L;<compat> 1101;;;;N;HANGUL LETTER SSANG GIYEOG;;;;
+3133;HANGUL LETTER KIYEOK-SIOS;Lo;0;L;<compat> 11AA;;;;N;HANGUL LETTER GIYEOG SIOS;;;;
+3134;HANGUL LETTER NIEUN;Lo;0;L;<compat> 1102;;;;N;;;;;
+3135;HANGUL LETTER NIEUN-CIEUC;Lo;0;L;<compat> 11AC;;;;N;HANGUL LETTER NIEUN JIEUJ;;;;
+3136;HANGUL LETTER NIEUN-HIEUH;Lo;0;L;<compat> 11AD;;;;N;HANGUL LETTER NIEUN HIEUH;;;;
+3137;HANGUL LETTER TIKEUT;Lo;0;L;<compat> 1103;;;;N;HANGUL LETTER DIGEUD;;;;
+3138;HANGUL LETTER SSANGTIKEUT;Lo;0;L;<compat> 1104;;;;N;HANGUL LETTER SSANG DIGEUD;;;;
+3139;HANGUL LETTER RIEUL;Lo;0;L;<compat> 1105;;;;N;HANGUL LETTER LIEUL;;;;
+313A;HANGUL LETTER RIEUL-KIYEOK;Lo;0;L;<compat> 11B0;;;;N;HANGUL LETTER LIEUL GIYEOG;;;;
+313B;HANGUL LETTER RIEUL-MIEUM;Lo;0;L;<compat> 11B1;;;;N;HANGUL LETTER LIEUL MIEUM;;;;
+313C;HANGUL LETTER RIEUL-PIEUP;Lo;0;L;<compat> 11B2;;;;N;HANGUL LETTER LIEUL BIEUB;;;;
+313D;HANGUL LETTER RIEUL-SIOS;Lo;0;L;<compat> 11B3;;;;N;HANGUL LETTER LIEUL SIOS;;;;
+313E;HANGUL LETTER RIEUL-THIEUTH;Lo;0;L;<compat> 11B4;;;;N;HANGUL LETTER LIEUL TIEUT;;;;
+313F;HANGUL LETTER RIEUL-PHIEUPH;Lo;0;L;<compat> 11B5;;;;N;HANGUL LETTER LIEUL PIEUP;;;;
+3140;HANGUL LETTER RIEUL-HIEUH;Lo;0;L;<compat> 111A;;;;N;HANGUL LETTER LIEUL HIEUH;;;;
+3141;HANGUL LETTER MIEUM;Lo;0;L;<compat> 1106;;;;N;;;;;
+3142;HANGUL LETTER PIEUP;Lo;0;L;<compat> 1107;;;;N;HANGUL LETTER BIEUB;;;;
+3143;HANGUL LETTER SSANGPIEUP;Lo;0;L;<compat> 1108;;;;N;HANGUL LETTER SSANG BIEUB;;;;
+3144;HANGUL LETTER PIEUP-SIOS;Lo;0;L;<compat> 1121;;;;N;HANGUL LETTER BIEUB SIOS;;;;
+3145;HANGUL LETTER SIOS;Lo;0;L;<compat> 1109;;;;N;;;;;
+3146;HANGUL LETTER SSANGSIOS;Lo;0;L;<compat> 110A;;;;N;HANGUL LETTER SSANG SIOS;;;;
+3147;HANGUL LETTER IEUNG;Lo;0;L;<compat> 110B;;;;N;;;;;
+3148;HANGUL LETTER CIEUC;Lo;0;L;<compat> 110C;;;;N;HANGUL LETTER JIEUJ;;;;
+3149;HANGUL LETTER SSANGCIEUC;Lo;0;L;<compat> 110D;;;;N;HANGUL LETTER SSANG JIEUJ;;;;
+314A;HANGUL LETTER CHIEUCH;Lo;0;L;<compat> 110E;;;;N;HANGUL LETTER CIEUC;;;;
+314B;HANGUL LETTER KHIEUKH;Lo;0;L;<compat> 110F;;;;N;HANGUL LETTER KIYEOK;;;;
+314C;HANGUL LETTER THIEUTH;Lo;0;L;<compat> 1110;;;;N;HANGUL LETTER TIEUT;;;;
+314D;HANGUL LETTER PHIEUPH;Lo;0;L;<compat> 1111;;;;N;HANGUL LETTER PIEUP;;;;
+314E;HANGUL LETTER HIEUH;Lo;0;L;<compat> 1112;;;;N;;;;;
+314F;HANGUL LETTER A;Lo;0;L;<compat> 1161;;;;N;;;;;
+3150;HANGUL LETTER AE;Lo;0;L;<compat> 1162;;;;N;;;;;
+3151;HANGUL LETTER YA;Lo;0;L;<compat> 1163;;;;N;;;;;
+3152;HANGUL LETTER YAE;Lo;0;L;<compat> 1164;;;;N;;;;;
+3153;HANGUL LETTER EO;Lo;0;L;<compat> 1165;;;;N;;;;;
+3154;HANGUL LETTER E;Lo;0;L;<compat> 1166;;;;N;;;;;
+3155;HANGUL LETTER YEO;Lo;0;L;<compat> 1167;;;;N;;;;;
+3156;HANGUL LETTER YE;Lo;0;L;<compat> 1168;;;;N;;;;;
+3157;HANGUL LETTER O;Lo;0;L;<compat> 1169;;;;N;;;;;
+3158;HANGUL LETTER WA;Lo;0;L;<compat> 116A;;;;N;;;;;
+3159;HANGUL LETTER WAE;Lo;0;L;<compat> 116B;;;;N;;;;;
+315A;HANGUL LETTER OE;Lo;0;L;<compat> 116C;;;;N;;;;;
+315B;HANGUL LETTER YO;Lo;0;L;<compat> 116D;;;;N;;;;;
+315C;HANGUL LETTER U;Lo;0;L;<compat> 116E;;;;N;;;;;
+315D;HANGUL LETTER WEO;Lo;0;L;<compat> 116F;;;;N;;;;;
+315E;HANGUL LETTER WE;Lo;0;L;<compat> 1170;;;;N;;;;;
+315F;HANGUL LETTER WI;Lo;0;L;<compat> 1171;;;;N;;;;;
+3160;HANGUL LETTER YU;Lo;0;L;<compat> 1172;;;;N;;;;;
+3161;HANGUL LETTER EU;Lo;0;L;<compat> 1173;;;;N;;;;;
+3162;HANGUL LETTER YI;Lo;0;L;<compat> 1174;;;;N;;;;;
+3163;HANGUL LETTER I;Lo;0;L;<compat> 1175;;;;N;;;;;
+3164;HANGUL FILLER;Lo;0;L;<compat> 1160;;;;N;HANGUL CAE OM;;;;
+3165;HANGUL LETTER SSANGNIEUN;Lo;0;L;<compat> 1114;;;;N;HANGUL LETTER SSANG NIEUN;;;;
+3166;HANGUL LETTER NIEUN-TIKEUT;Lo;0;L;<compat> 1115;;;;N;HANGUL LETTER NIEUN DIGEUD;;;;
+3167;HANGUL LETTER NIEUN-SIOS;Lo;0;L;<compat> 11C7;;;;N;HANGUL LETTER NIEUN SIOS;;;;
+3168;HANGUL LETTER NIEUN-PANSIOS;Lo;0;L;<compat> 11C8;;;;N;HANGUL LETTER NIEUN BAN CHI EUM;;;;
+3169;HANGUL LETTER RIEUL-KIYEOK-SIOS;Lo;0;L;<compat> 11CC;;;;N;HANGUL LETTER LIEUL GIYEOG SIOS;;;;
+316A;HANGUL LETTER RIEUL-TIKEUT;Lo;0;L;<compat> 11CE;;;;N;HANGUL LETTER LIEUL DIGEUD;;;;
+316B;HANGUL LETTER RIEUL-PIEUP-SIOS;Lo;0;L;<compat> 11D3;;;;N;HANGUL LETTER LIEUL BIEUB SIOS;;;;
+316C;HANGUL LETTER RIEUL-PANSIOS;Lo;0;L;<compat> 11D7;;;;N;HANGUL LETTER LIEUL BAN CHI EUM;;;;
+316D;HANGUL LETTER RIEUL-YEORINHIEUH;Lo;0;L;<compat> 11D9;;;;N;HANGUL LETTER LIEUL YEOLIN HIEUH;;;;
+316E;HANGUL LETTER MIEUM-PIEUP;Lo;0;L;<compat> 111C;;;;N;HANGUL LETTER MIEUM BIEUB;;;;
+316F;HANGUL LETTER MIEUM-SIOS;Lo;0;L;<compat> 11DD;;;;N;HANGUL LETTER MIEUM SIOS;;;;
+3170;HANGUL LETTER MIEUM-PANSIOS;Lo;0;L;<compat> 11DF;;;;N;HANGUL LETTER BIEUB BAN CHI EUM;;;;
+3171;HANGUL LETTER KAPYEOUNMIEUM;Lo;0;L;<compat> 111D;;;;N;HANGUL LETTER MIEUM SUN GYEONG EUM;;;;
+3172;HANGUL LETTER PIEUP-KIYEOK;Lo;0;L;<compat> 111E;;;;N;HANGUL LETTER BIEUB GIYEOG;;;;
+3173;HANGUL LETTER PIEUP-TIKEUT;Lo;0;L;<compat> 1120;;;;N;HANGUL LETTER BIEUB DIGEUD;;;;
+3174;HANGUL LETTER PIEUP-SIOS-KIYEOK;Lo;0;L;<compat> 1122;;;;N;HANGUL LETTER BIEUB SIOS GIYEOG;;;;
+3175;HANGUL LETTER PIEUP-SIOS-TIKEUT;Lo;0;L;<compat> 1123;;;;N;HANGUL LETTER BIEUB SIOS DIGEUD;;;;
+3176;HANGUL LETTER PIEUP-CIEUC;Lo;0;L;<compat> 1127;;;;N;HANGUL LETTER BIEUB JIEUJ;;;;
+3177;HANGUL LETTER PIEUP-THIEUTH;Lo;0;L;<compat> 1129;;;;N;HANGUL LETTER BIEUB TIEUT;;;;
+3178;HANGUL LETTER KAPYEOUNPIEUP;Lo;0;L;<compat> 112B;;;;N;HANGUL LETTER BIEUB SUN GYEONG EUM;;;;
+3179;HANGUL LETTER KAPYEOUNSSANGPIEUP;Lo;0;L;<compat> 112C;;;;N;HANGUL LETTER SSANG BIEUB SUN GYEONG EUM;;;;
+317A;HANGUL LETTER SIOS-KIYEOK;Lo;0;L;<compat> 112D;;;;N;HANGUL LETTER SIOS GIYEOG;;;;
+317B;HANGUL LETTER SIOS-NIEUN;Lo;0;L;<compat> 112E;;;;N;HANGUL LETTER SIOS NIEUN;;;;
+317C;HANGUL LETTER SIOS-TIKEUT;Lo;0;L;<compat> 112F;;;;N;HANGUL LETTER SIOS DIGEUD;;;;
+317D;HANGUL LETTER SIOS-PIEUP;Lo;0;L;<compat> 1132;;;;N;HANGUL LETTER SIOS BIEUB;;;;
+317E;HANGUL LETTER SIOS-CIEUC;Lo;0;L;<compat> 1136;;;;N;HANGUL LETTER SIOS JIEUJ;;;;
+317F;HANGUL LETTER PANSIOS;Lo;0;L;<compat> 1140;;;;N;HANGUL LETTER BAN CHI EUM;;;;
+3180;HANGUL LETTER SSANGIEUNG;Lo;0;L;<compat> 1147;;;;N;HANGUL LETTER SSANG IEUNG;;;;
+3181;HANGUL LETTER YESIEUNG;Lo;0;L;<compat> 114C;;;;N;HANGUL LETTER NGIEUNG;;;;
+3182;HANGUL LETTER YESIEUNG-SIOS;Lo;0;L;<compat> 11F1;;;;N;HANGUL LETTER NGIEUNG SIOS;;;;
+3183;HANGUL LETTER YESIEUNG-PANSIOS;Lo;0;L;<compat> 11F2;;;;N;HANGUL LETTER NGIEUNG BAN CHI EUM;;;;
+3184;HANGUL LETTER KAPYEOUNPHIEUPH;Lo;0;L;<compat> 1157;;;;N;HANGUL LETTER PIEUP SUN GYEONG EUM;;;;
+3185;HANGUL LETTER SSANGHIEUH;Lo;0;L;<compat> 1158;;;;N;HANGUL LETTER SSANG HIEUH;;;;
+3186;HANGUL LETTER YEORINHIEUH;Lo;0;L;<compat> 1159;;;;N;HANGUL LETTER YEOLIN HIEUH;;;;
+3187;HANGUL LETTER YO-YA;Lo;0;L;<compat> 1184;;;;N;HANGUL LETTER YOYA;;;;
+3188;HANGUL LETTER YO-YAE;Lo;0;L;<compat> 1185;;;;N;HANGUL LETTER YOYAE;;;;
+3189;HANGUL LETTER YO-I;Lo;0;L;<compat> 1188;;;;N;HANGUL LETTER YOI;;;;
+318A;HANGUL LETTER YU-YEO;Lo;0;L;<compat> 1191;;;;N;HANGUL LETTER YUYEO;;;;
+318B;HANGUL LETTER YU-YE;Lo;0;L;<compat> 1192;;;;N;HANGUL LETTER YUYE;;;;
+318C;HANGUL LETTER YU-I;Lo;0;L;<compat> 1194;;;;N;HANGUL LETTER YUI;;;;
+318D;HANGUL LETTER ARAEA;Lo;0;L;<compat> 119E;;;;N;HANGUL LETTER ALAE A;;;;
+318E;HANGUL LETTER ARAEAE;Lo;0;L;<compat> 11A1;;;;N;HANGUL LETTER ALAE AE;;;;
+3190;IDEOGRAPHIC ANNOTATION LINKING MARK;So;0;L;;;;;N;KANBUN TATETEN;Kanbun Tateten;;;
+3191;IDEOGRAPHIC ANNOTATION REVERSE MARK;So;0;L;;;;;N;KAERITEN RE;Kaeriten;;;
+3192;IDEOGRAPHIC ANNOTATION ONE MARK;No;0;L;<super> 4E00;;;1;N;KAERITEN ITI;Kaeriten;;;
+3193;IDEOGRAPHIC ANNOTATION TWO MARK;No;0;L;<super> 4E8C;;;2;N;KAERITEN NI;Kaeriten;;;
+3194;IDEOGRAPHIC ANNOTATION THREE MARK;No;0;L;<super> 4E09;;;3;N;KAERITEN SAN;Kaeriten;;;
+3195;IDEOGRAPHIC ANNOTATION FOUR MARK;No;0;L;<super> 56DB;;;4;N;KAERITEN SI;Kaeriten;;;
+3196;IDEOGRAPHIC ANNOTATION TOP MARK;So;0;L;<super> 4E0A;;;;N;KAERITEN ZYOU;Kaeriten;;;
+3197;IDEOGRAPHIC ANNOTATION MIDDLE MARK;So;0;L;<super> 4E2D;;;;N;KAERITEN TYUU;Kaeriten;;;
+3198;IDEOGRAPHIC ANNOTATION BOTTOM MARK;So;0;L;<super> 4E0B;;;;N;KAERITEN GE;Kaeriten;;;
+3199;IDEOGRAPHIC ANNOTATION FIRST MARK;So;0;L;<super> 7532;;;;N;KAERITEN KOU;Kaeriten;;;
+319A;IDEOGRAPHIC ANNOTATION SECOND MARK;So;0;L;<super> 4E59;;;;N;KAERITEN OTU;Kaeriten;;;
+319B;IDEOGRAPHIC ANNOTATION THIRD MARK;So;0;L;<super> 4E19;;;;N;KAERITEN HEI;Kaeriten;;;
+319C;IDEOGRAPHIC ANNOTATION FOURTH MARK;So;0;L;<super> 4E01;;;;N;KAERITEN TEI;Kaeriten;;;
+319D;IDEOGRAPHIC ANNOTATION HEAVEN MARK;So;0;L;<super> 5929;;;;N;KAERITEN TEN;Kaeriten;;;
+319E;IDEOGRAPHIC ANNOTATION EARTH MARK;So;0;L;<super> 5730;;;;N;KAERITEN TI;Kaeriten;;;
+319F;IDEOGRAPHIC ANNOTATION MAN MARK;So;0;L;<super> 4EBA;;;;N;KAERITEN ZIN;Kaeriten;;;
+31A0;BOPOMOFO LETTER BU;Lo;0;L;;;;;N;;;;;
+31A1;BOPOMOFO LETTER ZI;Lo;0;L;;;;;N;;;;;
+31A2;BOPOMOFO LETTER JI;Lo;0;L;;;;;N;;;;;
+31A3;BOPOMOFO LETTER GU;Lo;0;L;;;;;N;;;;;
+31A4;BOPOMOFO LETTER EE;Lo;0;L;;;;;N;;;;;
+31A5;BOPOMOFO LETTER ENN;Lo;0;L;;;;;N;;;;;
+31A6;BOPOMOFO LETTER OO;Lo;0;L;;;;;N;;;;;
+31A7;BOPOMOFO LETTER ONN;Lo;0;L;;;;;N;;;;;
+31A8;BOPOMOFO LETTER IR;Lo;0;L;;;;;N;;;;;
+31A9;BOPOMOFO LETTER ANN;Lo;0;L;;;;;N;;;;;
+31AA;BOPOMOFO LETTER INN;Lo;0;L;;;;;N;;;;;
+31AB;BOPOMOFO LETTER UNN;Lo;0;L;;;;;N;;;;;
+31AC;BOPOMOFO LETTER IM;Lo;0;L;;;;;N;;;;;
+31AD;BOPOMOFO LETTER NGG;Lo;0;L;;;;;N;;;;;
+31AE;BOPOMOFO LETTER AINN;Lo;0;L;;;;;N;;;;;
+31AF;BOPOMOFO LETTER AUNN;Lo;0;L;;;;;N;;;;;
+31B0;BOPOMOFO LETTER AM;Lo;0;L;;;;;N;;;;;
+31B1;BOPOMOFO LETTER OM;Lo;0;L;;;;;N;;;;;
+31B2;BOPOMOFO LETTER ONG;Lo;0;L;;;;;N;;;;;
+31B3;BOPOMOFO LETTER INNN;Lo;0;L;;;;;N;;;;;
+31B4;BOPOMOFO FINAL LETTER P;Lo;0;L;;;;;N;;;;;
+31B5;BOPOMOFO FINAL LETTER T;Lo;0;L;;;;;N;;;;;
+31B6;BOPOMOFO FINAL LETTER K;Lo;0;L;;;;;N;;;;;
+31B7;BOPOMOFO FINAL LETTER H;Lo;0;L;;;;;N;;;;;
+31F0;KATAKANA LETTER SMALL KU;Lo;0;L;;;;;N;;;;;
+31F1;KATAKANA LETTER SMALL SI;Lo;0;L;;;;;N;;;;;
+31F2;KATAKANA LETTER SMALL SU;Lo;0;L;;;;;N;;;;;
+31F3;KATAKANA LETTER SMALL TO;Lo;0;L;;;;;N;;;;;
+31F4;KATAKANA LETTER SMALL NU;Lo;0;L;;;;;N;;;;;
+31F5;KATAKANA LETTER SMALL HA;Lo;0;L;;;;;N;;;;;
+31F6;KATAKANA LETTER SMALL HI;Lo;0;L;;;;;N;;;;;
+31F7;KATAKANA LETTER SMALL HU;Lo;0;L;;;;;N;;;;;
+31F8;KATAKANA LETTER SMALL HE;Lo;0;L;;;;;N;;;;;
+31F9;KATAKANA LETTER SMALL HO;Lo;0;L;;;;;N;;;;;
+31FA;KATAKANA LETTER SMALL MU;Lo;0;L;;;;;N;;;;;
+31FB;KATAKANA LETTER SMALL RA;Lo;0;L;;;;;N;;;;;
+31FC;KATAKANA LETTER SMALL RI;Lo;0;L;;;;;N;;;;;
+31FD;KATAKANA LETTER SMALL RU;Lo;0;L;;;;;N;;;;;
+31FE;KATAKANA LETTER SMALL RE;Lo;0;L;;;;;N;;;;;
+31FF;KATAKANA LETTER SMALL RO;Lo;0;L;;;;;N;;;;;
+3200;PARENTHESIZED HANGUL KIYEOK;So;0;L;<compat> 0028 1100 0029;;;;N;PARENTHESIZED HANGUL GIYEOG;;;;
+3201;PARENTHESIZED HANGUL NIEUN;So;0;L;<compat> 0028 1102 0029;;;;N;;;;;
+3202;PARENTHESIZED HANGUL TIKEUT;So;0;L;<compat> 0028 1103 0029;;;;N;PARENTHESIZED HANGUL DIGEUD;;;;
+3203;PARENTHESIZED HANGUL RIEUL;So;0;L;<compat> 0028 1105 0029;;;;N;PARENTHESIZED HANGUL LIEUL;;;;
+3204;PARENTHESIZED HANGUL MIEUM;So;0;L;<compat> 0028 1106 0029;;;;N;;;;;
+3205;PARENTHESIZED HANGUL PIEUP;So;0;L;<compat> 0028 1107 0029;;;;N;PARENTHESIZED HANGUL BIEUB;;;;
+3206;PARENTHESIZED HANGUL SIOS;So;0;L;<compat> 0028 1109 0029;;;;N;;;;;
+3207;PARENTHESIZED HANGUL IEUNG;So;0;L;<compat> 0028 110B 0029;;;;N;;;;;
+3208;PARENTHESIZED HANGUL CIEUC;So;0;L;<compat> 0028 110C 0029;;;;N;PARENTHESIZED HANGUL JIEUJ;;;;
+3209;PARENTHESIZED HANGUL CHIEUCH;So;0;L;<compat> 0028 110E 0029;;;;N;PARENTHESIZED HANGUL CIEUC;;;;
+320A;PARENTHESIZED HANGUL KHIEUKH;So;0;L;<compat> 0028 110F 0029;;;;N;PARENTHESIZED HANGUL KIYEOK;;;;
+320B;PARENTHESIZED HANGUL THIEUTH;So;0;L;<compat> 0028 1110 0029;;;;N;PARENTHESIZED HANGUL TIEUT;;;;
+320C;PARENTHESIZED HANGUL PHIEUPH;So;0;L;<compat> 0028 1111 0029;;;;N;PARENTHESIZED HANGUL PIEUP;;;;
+320D;PARENTHESIZED HANGUL HIEUH;So;0;L;<compat> 0028 1112 0029;;;;N;;;;;
+320E;PARENTHESIZED HANGUL KIYEOK A;So;0;L;<compat> 0028 1100 1161 0029;;;;N;PARENTHESIZED HANGUL GA;;;;
+320F;PARENTHESIZED HANGUL NIEUN A;So;0;L;<compat> 0028 1102 1161 0029;;;;N;PARENTHESIZED HANGUL NA;;;;
+3210;PARENTHESIZED HANGUL TIKEUT A;So;0;L;<compat> 0028 1103 1161 0029;;;;N;PARENTHESIZED HANGUL DA;;;;
+3211;PARENTHESIZED HANGUL RIEUL A;So;0;L;<compat> 0028 1105 1161 0029;;;;N;PARENTHESIZED HANGUL LA;;;;
+3212;PARENTHESIZED HANGUL MIEUM A;So;0;L;<compat> 0028 1106 1161 0029;;;;N;PARENTHESIZED HANGUL MA;;;;
+3213;PARENTHESIZED HANGUL PIEUP A;So;0;L;<compat> 0028 1107 1161 0029;;;;N;PARENTHESIZED HANGUL BA;;;;
+3214;PARENTHESIZED HANGUL SIOS A;So;0;L;<compat> 0028 1109 1161 0029;;;;N;PARENTHESIZED HANGUL SA;;;;
+3215;PARENTHESIZED HANGUL IEUNG A;So;0;L;<compat> 0028 110B 1161 0029;;;;N;PARENTHESIZED HANGUL A;;;;
+3216;PARENTHESIZED HANGUL CIEUC A;So;0;L;<compat> 0028 110C 1161 0029;;;;N;PARENTHESIZED HANGUL JA;;;;
+3217;PARENTHESIZED HANGUL CHIEUCH A;So;0;L;<compat> 0028 110E 1161 0029;;;;N;PARENTHESIZED HANGUL CA;;;;
+3218;PARENTHESIZED HANGUL KHIEUKH A;So;0;L;<compat> 0028 110F 1161 0029;;;;N;PARENTHESIZED HANGUL KA;;;;
+3219;PARENTHESIZED HANGUL THIEUTH A;So;0;L;<compat> 0028 1110 1161 0029;;;;N;PARENTHESIZED HANGUL TA;;;;
+321A;PARENTHESIZED HANGUL PHIEUPH A;So;0;L;<compat> 0028 1111 1161 0029;;;;N;PARENTHESIZED HANGUL PA;;;;
+321B;PARENTHESIZED HANGUL HIEUH A;So;0;L;<compat> 0028 1112 1161 0029;;;;N;PARENTHESIZED HANGUL HA;;;;
+321C;PARENTHESIZED HANGUL CIEUC U;So;0;L;<compat> 0028 110C 116E 0029;;;;N;PARENTHESIZED HANGUL JU;;;;
+321D;PARENTHESIZED KOREAN CHARACTER OJEON;So;0;ON;<compat> 0028 110B 1169 110C 1165 11AB 0029;;;;N;;;;;
+321E;PARENTHESIZED KOREAN CHARACTER O HU;So;0;ON;<compat> 0028 110B 1169 1112 116E 0029;;;;N;;;;;
+3220;PARENTHESIZED IDEOGRAPH ONE;No;0;L;<compat> 0028 4E00 0029;;;1;N;;;;;
+3221;PARENTHESIZED IDEOGRAPH TWO;No;0;L;<compat> 0028 4E8C 0029;;;2;N;;;;;
+3222;PARENTHESIZED IDEOGRAPH THREE;No;0;L;<compat> 0028 4E09 0029;;;3;N;;;;;
+3223;PARENTHESIZED IDEOGRAPH FOUR;No;0;L;<compat> 0028 56DB 0029;;;4;N;;;;;
+3224;PARENTHESIZED IDEOGRAPH FIVE;No;0;L;<compat> 0028 4E94 0029;;;5;N;;;;;
+3225;PARENTHESIZED IDEOGRAPH SIX;No;0;L;<compat> 0028 516D 0029;;;6;N;;;;;
+3226;PARENTHESIZED IDEOGRAPH SEVEN;No;0;L;<compat> 0028 4E03 0029;;;7;N;;;;;
+3227;PARENTHESIZED IDEOGRAPH EIGHT;No;0;L;<compat> 0028 516B 0029;;;8;N;;;;;
+3228;PARENTHESIZED IDEOGRAPH NINE;No;0;L;<compat> 0028 4E5D 0029;;;9;N;;;;;
+3229;PARENTHESIZED IDEOGRAPH TEN;No;0;L;<compat> 0028 5341 0029;;;10;N;;;;;
+322A;PARENTHESIZED IDEOGRAPH MOON;So;0;L;<compat> 0028 6708 0029;;;;N;;;;;
+322B;PARENTHESIZED IDEOGRAPH FIRE;So;0;L;<compat> 0028 706B 0029;;;;N;;;;;
+322C;PARENTHESIZED IDEOGRAPH WATER;So;0;L;<compat> 0028 6C34 0029;;;;N;;;;;
+322D;PARENTHESIZED IDEOGRAPH WOOD;So;0;L;<compat> 0028 6728 0029;;;;N;;;;;
+322E;PARENTHESIZED IDEOGRAPH METAL;So;0;L;<compat> 0028 91D1 0029;;;;N;;;;;
+322F;PARENTHESIZED IDEOGRAPH EARTH;So;0;L;<compat> 0028 571F 0029;;;;N;;;;;
+3230;PARENTHESIZED IDEOGRAPH SUN;So;0;L;<compat> 0028 65E5 0029;;;;N;;;;;
+3231;PARENTHESIZED IDEOGRAPH STOCK;So;0;L;<compat> 0028 682A 0029;;;;N;;;;;
+3232;PARENTHESIZED IDEOGRAPH HAVE;So;0;L;<compat> 0028 6709 0029;;;;N;;;;;
+3233;PARENTHESIZED IDEOGRAPH SOCIETY;So;0;L;<compat> 0028 793E 0029;;;;N;;;;;
+3234;PARENTHESIZED IDEOGRAPH NAME;So;0;L;<compat> 0028 540D 0029;;;;N;;;;;
+3235;PARENTHESIZED IDEOGRAPH SPECIAL;So;0;L;<compat> 0028 7279 0029;;;;N;;;;;
+3236;PARENTHESIZED IDEOGRAPH FINANCIAL;So;0;L;<compat> 0028 8CA1 0029;;;;N;;;;;
+3237;PARENTHESIZED IDEOGRAPH CONGRATULATION;So;0;L;<compat> 0028 795D 0029;;;;N;;;;;
+3238;PARENTHESIZED IDEOGRAPH LABOR;So;0;L;<compat> 0028 52B4 0029;;;;N;;;;;
+3239;PARENTHESIZED IDEOGRAPH REPRESENT;So;0;L;<compat> 0028 4EE3 0029;;;;N;;;;;
+323A;PARENTHESIZED IDEOGRAPH CALL;So;0;L;<compat> 0028 547C 0029;;;;N;;;;;
+323B;PARENTHESIZED IDEOGRAPH STUDY;So;0;L;<compat> 0028 5B66 0029;;;;N;;;;;
+323C;PARENTHESIZED IDEOGRAPH SUPERVISE;So;0;L;<compat> 0028 76E3 0029;;;;N;;;;;
+323D;PARENTHESIZED IDEOGRAPH ENTERPRISE;So;0;L;<compat> 0028 4F01 0029;;;;N;;;;;
+323E;PARENTHESIZED IDEOGRAPH RESOURCE;So;0;L;<compat> 0028 8CC7 0029;;;;N;;;;;
+323F;PARENTHESIZED IDEOGRAPH ALLIANCE;So;0;L;<compat> 0028 5354 0029;;;;N;;;;;
+3240;PARENTHESIZED IDEOGRAPH FESTIVAL;So;0;L;<compat> 0028 796D 0029;;;;N;;;;;
+3241;PARENTHESIZED IDEOGRAPH REST;So;0;L;<compat> 0028 4F11 0029;;;;N;;;;;
+3242;PARENTHESIZED IDEOGRAPH SELF;So;0;L;<compat> 0028 81EA 0029;;;;N;;;;;
+3243;PARENTHESIZED IDEOGRAPH REACH;So;0;L;<compat> 0028 81F3 0029;;;;N;;;;;
+3250;PARTNERSHIP SIGN;So;0;ON;<square> 0050 0054 0045;;;;N;;;;;
+3251;CIRCLED NUMBER TWENTY ONE;No;0;ON;<circle> 0032 0031;;;21;N;;;;;
+3252;CIRCLED NUMBER TWENTY TWO;No;0;ON;<circle> 0032 0032;;;22;N;;;;;
+3253;CIRCLED NUMBER TWENTY THREE;No;0;ON;<circle> 0032 0033;;;23;N;;;;;
+3254;CIRCLED NUMBER TWENTY FOUR;No;0;ON;<circle> 0032 0034;;;24;N;;;;;
+3255;CIRCLED NUMBER TWENTY FIVE;No;0;ON;<circle> 0032 0035;;;25;N;;;;;
+3256;CIRCLED NUMBER TWENTY SIX;No;0;ON;<circle> 0032 0036;;;26;N;;;;;
+3257;CIRCLED NUMBER TWENTY SEVEN;No;0;ON;<circle> 0032 0037;;;27;N;;;;;
+3258;CIRCLED NUMBER TWENTY EIGHT;No;0;ON;<circle> 0032 0038;;;28;N;;;;;
+3259;CIRCLED NUMBER TWENTY NINE;No;0;ON;<circle> 0032 0039;;;29;N;;;;;
+325A;CIRCLED NUMBER THIRTY;No;0;ON;<circle> 0033 0030;;;30;N;;;;;
+325B;CIRCLED NUMBER THIRTY ONE;No;0;ON;<circle> 0033 0031;;;31;N;;;;;
+325C;CIRCLED NUMBER THIRTY TWO;No;0;ON;<circle> 0033 0032;;;32;N;;;;;
+325D;CIRCLED NUMBER THIRTY THREE;No;0;ON;<circle> 0033 0033;;;33;N;;;;;
+325E;CIRCLED NUMBER THIRTY FOUR;No;0;ON;<circle> 0033 0034;;;34;N;;;;;
+325F;CIRCLED NUMBER THIRTY FIVE;No;0;ON;<circle> 0033 0035;;;35;N;;;;;
+3260;CIRCLED HANGUL KIYEOK;So;0;L;<circle> 1100;;;;N;CIRCLED HANGUL GIYEOG;;;;
+3261;CIRCLED HANGUL NIEUN;So;0;L;<circle> 1102;;;;N;;;;;
+3262;CIRCLED HANGUL TIKEUT;So;0;L;<circle> 1103;;;;N;CIRCLED HANGUL DIGEUD;;;;
+3263;CIRCLED HANGUL RIEUL;So;0;L;<circle> 1105;;;;N;CIRCLED HANGUL LIEUL;;;;
+3264;CIRCLED HANGUL MIEUM;So;0;L;<circle> 1106;;;;N;;;;;
+3265;CIRCLED HANGUL PIEUP;So;0;L;<circle> 1107;;;;N;CIRCLED HANGUL BIEUB;;;;
+3266;CIRCLED HANGUL SIOS;So;0;L;<circle> 1109;;;;N;;;;;
+3267;CIRCLED HANGUL IEUNG;So;0;L;<circle> 110B;;;;N;;;;;
+3268;CIRCLED HANGUL CIEUC;So;0;L;<circle> 110C;;;;N;CIRCLED HANGUL JIEUJ;;;;
+3269;CIRCLED HANGUL CHIEUCH;So;0;L;<circle> 110E;;;;N;CIRCLED HANGUL CIEUC;;;;
+326A;CIRCLED HANGUL KHIEUKH;So;0;L;<circle> 110F;;;;N;CIRCLED HANGUL KIYEOK;;;;
+326B;CIRCLED HANGUL THIEUTH;So;0;L;<circle> 1110;;;;N;CIRCLED HANGUL TIEUT;;;;
+326C;CIRCLED HANGUL PHIEUPH;So;0;L;<circle> 1111;;;;N;CIRCLED HANGUL PIEUP;;;;
+326D;CIRCLED HANGUL HIEUH;So;0;L;<circle> 1112;;;;N;;;;;
+326E;CIRCLED HANGUL KIYEOK A;So;0;L;<circle> 1100 1161;;;;N;CIRCLED HANGUL GA;;;;
+326F;CIRCLED HANGUL NIEUN A;So;0;L;<circle> 1102 1161;;;;N;CIRCLED HANGUL NA;;;;
+3270;CIRCLED HANGUL TIKEUT A;So;0;L;<circle> 1103 1161;;;;N;CIRCLED HANGUL DA;;;;
+3271;CIRCLED HANGUL RIEUL A;So;0;L;<circle> 1105 1161;;;;N;CIRCLED HANGUL LA;;;;
+3272;CIRCLED HANGUL MIEUM A;So;0;L;<circle> 1106 1161;;;;N;CIRCLED HANGUL MA;;;;
+3273;CIRCLED HANGUL PIEUP A;So;0;L;<circle> 1107 1161;;;;N;CIRCLED HANGUL BA;;;;
+3274;CIRCLED HANGUL SIOS A;So;0;L;<circle> 1109 1161;;;;N;CIRCLED HANGUL SA;;;;
+3275;CIRCLED HANGUL IEUNG A;So;0;L;<circle> 110B 1161;;;;N;CIRCLED HANGUL A;;;;
+3276;CIRCLED HANGUL CIEUC A;So;0;L;<circle> 110C 1161;;;;N;CIRCLED HANGUL JA;;;;
+3277;CIRCLED HANGUL CHIEUCH A;So;0;L;<circle> 110E 1161;;;;N;CIRCLED HANGUL CA;;;;
+3278;CIRCLED HANGUL KHIEUKH A;So;0;L;<circle> 110F 1161;;;;N;CIRCLED HANGUL KA;;;;
+3279;CIRCLED HANGUL THIEUTH A;So;0;L;<circle> 1110 1161;;;;N;CIRCLED HANGUL TA;;;;
+327A;CIRCLED HANGUL PHIEUPH A;So;0;L;<circle> 1111 1161;;;;N;CIRCLED HANGUL PA;;;;
+327B;CIRCLED HANGUL HIEUH A;So;0;L;<circle> 1112 1161;;;;N;CIRCLED HANGUL HA;;;;
+327C;CIRCLED KOREAN CHARACTER CHAMKO;So;0;ON;<circle> 110E 1161 11B7 1100 1169;;;;N;;;;;
+327D;CIRCLED KOREAN CHARACTER JUEUI;So;0;ON;<circle> 110C 116E 110B 1174;;;;N;;;;;
+327F;KOREAN STANDARD SYMBOL;So;0;L;;;;;N;;;;;
+3280;CIRCLED IDEOGRAPH ONE;No;0;L;<circle> 4E00;;;1;N;;;;;
+3281;CIRCLED IDEOGRAPH TWO;No;0;L;<circle> 4E8C;;;2;N;;;;;
+3282;CIRCLED IDEOGRAPH THREE;No;0;L;<circle> 4E09;;;3;N;;;;;
+3283;CIRCLED IDEOGRAPH FOUR;No;0;L;<circle> 56DB;;;4;N;;;;;
+3284;CIRCLED IDEOGRAPH FIVE;No;0;L;<circle> 4E94;;;5;N;;;;;
+3285;CIRCLED IDEOGRAPH SIX;No;0;L;<circle> 516D;;;6;N;;;;;
+3286;CIRCLED IDEOGRAPH SEVEN;No;0;L;<circle> 4E03;;;7;N;;;;;
+3287;CIRCLED IDEOGRAPH EIGHT;No;0;L;<circle> 516B;;;8;N;;;;;
+3288;CIRCLED IDEOGRAPH NINE;No;0;L;<circle> 4E5D;;;9;N;;;;;
+3289;CIRCLED IDEOGRAPH TEN;No;0;L;<circle> 5341;;;10;N;;;;;
+328A;CIRCLED IDEOGRAPH MOON;So;0;L;<circle> 6708;;;;N;;;;;
+328B;CIRCLED IDEOGRAPH FIRE;So;0;L;<circle> 706B;;;;N;;;;;
+328C;CIRCLED IDEOGRAPH WATER;So;0;L;<circle> 6C34;;;;N;;;;;
+328D;CIRCLED IDEOGRAPH WOOD;So;0;L;<circle> 6728;;;;N;;;;;
+328E;CIRCLED IDEOGRAPH METAL;So;0;L;<circle> 91D1;;;;N;;;;;
+328F;CIRCLED IDEOGRAPH EARTH;So;0;L;<circle> 571F;;;;N;;;;;
+3290;CIRCLED IDEOGRAPH SUN;So;0;L;<circle> 65E5;;;;N;;;;;
+3291;CIRCLED IDEOGRAPH STOCK;So;0;L;<circle> 682A;;;;N;;;;;
+3292;CIRCLED IDEOGRAPH HAVE;So;0;L;<circle> 6709;;;;N;;;;;
+3293;CIRCLED IDEOGRAPH SOCIETY;So;0;L;<circle> 793E;;;;N;;;;;
+3294;CIRCLED IDEOGRAPH NAME;So;0;L;<circle> 540D;;;;N;;;;;
+3295;CIRCLED IDEOGRAPH SPECIAL;So;0;L;<circle> 7279;;;;N;;;;;
+3296;CIRCLED IDEOGRAPH FINANCIAL;So;0;L;<circle> 8CA1;;;;N;;;;;
+3297;CIRCLED IDEOGRAPH CONGRATULATION;So;0;L;<circle> 795D;;;;N;;;;;
+3298;CIRCLED IDEOGRAPH LABOR;So;0;L;<circle> 52B4;;;;N;;;;;
+3299;CIRCLED IDEOGRAPH SECRET;So;0;L;<circle> 79D8;;;;N;;;;;
+329A;CIRCLED IDEOGRAPH MALE;So;0;L;<circle> 7537;;;;N;;;;;
+329B;CIRCLED IDEOGRAPH FEMALE;So;0;L;<circle> 5973;;;;N;;;;;
+329C;CIRCLED IDEOGRAPH SUITABLE;So;0;L;<circle> 9069;;;;N;;;;;
+329D;CIRCLED IDEOGRAPH EXCELLENT;So;0;L;<circle> 512A;;;;N;;;;;
+329E;CIRCLED IDEOGRAPH PRINT;So;0;L;<circle> 5370;;;;N;;;;;
+329F;CIRCLED IDEOGRAPH ATTENTION;So;0;L;<circle> 6CE8;;;;N;;;;;
+32A0;CIRCLED IDEOGRAPH ITEM;So;0;L;<circle> 9805;;;;N;;;;;
+32A1;CIRCLED IDEOGRAPH REST;So;0;L;<circle> 4F11;;;;N;;;;;
+32A2;CIRCLED IDEOGRAPH COPY;So;0;L;<circle> 5199;;;;N;;;;;
+32A3;CIRCLED IDEOGRAPH CORRECT;So;0;L;<circle> 6B63;;;;N;;;;;
+32A4;CIRCLED IDEOGRAPH HIGH;So;0;L;<circle> 4E0A;;;;N;;;;;
+32A5;CIRCLED IDEOGRAPH CENTRE;So;0;L;<circle> 4E2D;;;;N;CIRCLED IDEOGRAPH CENTER;;;;
+32A6;CIRCLED IDEOGRAPH LOW;So;0;L;<circle> 4E0B;;;;N;;;;;
+32A7;CIRCLED IDEOGRAPH LEFT;So;0;L;<circle> 5DE6;;;;N;;;;;
+32A8;CIRCLED IDEOGRAPH RIGHT;So;0;L;<circle> 53F3;;;;N;;;;;
+32A9;CIRCLED IDEOGRAPH MEDICINE;So;0;L;<circle> 533B;;;;N;;;;;
+32AA;CIRCLED IDEOGRAPH RELIGION;So;0;L;<circle> 5B97;;;;N;;;;;
+32AB;CIRCLED IDEOGRAPH STUDY;So;0;L;<circle> 5B66;;;;N;;;;;
+32AC;CIRCLED IDEOGRAPH SUPERVISE;So;0;L;<circle> 76E3;;;;N;;;;;
+32AD;CIRCLED IDEOGRAPH ENTERPRISE;So;0;L;<circle> 4F01;;;;N;;;;;
+32AE;CIRCLED IDEOGRAPH RESOURCE;So;0;L;<circle> 8CC7;;;;N;;;;;
+32AF;CIRCLED IDEOGRAPH ALLIANCE;So;0;L;<circle> 5354;;;;N;;;;;
+32B0;CIRCLED IDEOGRAPH NIGHT;So;0;L;<circle> 591C;;;;N;;;;;
+32B1;CIRCLED NUMBER THIRTY SIX;No;0;ON;<circle> 0033 0036;;;36;N;;;;;
+32B2;CIRCLED NUMBER THIRTY SEVEN;No;0;ON;<circle> 0033 0037;;;37;N;;;;;
+32B3;CIRCLED NUMBER THIRTY EIGHT;No;0;ON;<circle> 0033 0038;;;38;N;;;;;
+32B4;CIRCLED NUMBER THIRTY NINE;No;0;ON;<circle> 0033 0039;;;39;N;;;;;
+32B5;CIRCLED NUMBER FORTY;No;0;ON;<circle> 0034 0030;;;40;N;;;;;
+32B6;CIRCLED NUMBER FORTY ONE;No;0;ON;<circle> 0034 0031;;;41;N;;;;;
+32B7;CIRCLED NUMBER FORTY TWO;No;0;ON;<circle> 0034 0032;;;42;N;;;;;
+32B8;CIRCLED NUMBER FORTY THREE;No;0;ON;<circle> 0034 0033;;;43;N;;;;;
+32B9;CIRCLED NUMBER FORTY FOUR;No;0;ON;<circle> 0034 0034;;;44;N;;;;;
+32BA;CIRCLED NUMBER FORTY FIVE;No;0;ON;<circle> 0034 0035;;;45;N;;;;;
+32BB;CIRCLED NUMBER FORTY SIX;No;0;ON;<circle> 0034 0036;;;46;N;;;;;
+32BC;CIRCLED NUMBER FORTY SEVEN;No;0;ON;<circle> 0034 0037;;;47;N;;;;;
+32BD;CIRCLED NUMBER FORTY EIGHT;No;0;ON;<circle> 0034 0038;;;48;N;;;;;
+32BE;CIRCLED NUMBER FORTY NINE;No;0;ON;<circle> 0034 0039;;;49;N;;;;;
+32BF;CIRCLED NUMBER FIFTY;No;0;ON;<circle> 0035 0030;;;50;N;;;;;
+32C0;IDEOGRAPHIC TELEGRAPH SYMBOL FOR JANUARY;So;0;L;<compat> 0031 6708;;;;N;;;;;
+32C1;IDEOGRAPHIC TELEGRAPH SYMBOL FOR FEBRUARY;So;0;L;<compat> 0032 6708;;;;N;;;;;
+32C2;IDEOGRAPHIC TELEGRAPH SYMBOL FOR MARCH;So;0;L;<compat> 0033 6708;;;;N;;;;;
+32C3;IDEOGRAPHIC TELEGRAPH SYMBOL FOR APRIL;So;0;L;<compat> 0034 6708;;;;N;;;;;
+32C4;IDEOGRAPHIC TELEGRAPH SYMBOL FOR MAY;So;0;L;<compat> 0035 6708;;;;N;;;;;
+32C5;IDEOGRAPHIC TELEGRAPH SYMBOL FOR JUNE;So;0;L;<compat> 0036 6708;;;;N;;;;;
+32C6;IDEOGRAPHIC TELEGRAPH SYMBOL FOR JULY;So;0;L;<compat> 0037 6708;;;;N;;;;;
+32C7;IDEOGRAPHIC TELEGRAPH SYMBOL FOR AUGUST;So;0;L;<compat> 0038 6708;;;;N;;;;;
+32C8;IDEOGRAPHIC TELEGRAPH SYMBOL FOR SEPTEMBER;So;0;L;<compat> 0039 6708;;;;N;;;;;
+32C9;IDEOGRAPHIC TELEGRAPH SYMBOL FOR OCTOBER;So;0;L;<compat> 0031 0030 6708;;;;N;;;;;
+32CA;IDEOGRAPHIC TELEGRAPH SYMBOL FOR NOVEMBER;So;0;L;<compat> 0031 0031 6708;;;;N;;;;;
+32CB;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DECEMBER;So;0;L;<compat> 0031 0032 6708;;;;N;;;;;
+32CC;SQUARE HG;So;0;ON;<square> 0048 0067;;;;N;;;;;
+32CD;SQUARE ERG;So;0;ON;<square> 0065 0072 0067;;;;N;;;;;
+32CE;SQUARE EV;So;0;ON;<square> 0065 0056;;;;N;;;;;
+32CF;LIMITED LIABILITY SIGN;So;0;ON;<square> 004C 0054 0044;;;;N;;;;;
+32D0;CIRCLED KATAKANA A;So;0;L;<circle> 30A2;;;;N;;;;;
+32D1;CIRCLED KATAKANA I;So;0;L;<circle> 30A4;;;;N;;;;;
+32D2;CIRCLED KATAKANA U;So;0;L;<circle> 30A6;;;;N;;;;;
+32D3;CIRCLED KATAKANA E;So;0;L;<circle> 30A8;;;;N;;;;;
+32D4;CIRCLED KATAKANA O;So;0;L;<circle> 30AA;;;;N;;;;;
+32D5;CIRCLED KATAKANA KA;So;0;L;<circle> 30AB;;;;N;;;;;
+32D6;CIRCLED KATAKANA KI;So;0;L;<circle> 30AD;;;;N;;;;;
+32D7;CIRCLED KATAKANA KU;So;0;L;<circle> 30AF;;;;N;;;;;
+32D8;CIRCLED KATAKANA KE;So;0;L;<circle> 30B1;;;;N;;;;;
+32D9;CIRCLED KATAKANA KO;So;0;L;<circle> 30B3;;;;N;;;;;
+32DA;CIRCLED KATAKANA SA;So;0;L;<circle> 30B5;;;;N;;;;;
+32DB;CIRCLED KATAKANA SI;So;0;L;<circle> 30B7;;;;N;;;;;
+32DC;CIRCLED KATAKANA SU;So;0;L;<circle> 30B9;;;;N;;;;;
+32DD;CIRCLED KATAKANA SE;So;0;L;<circle> 30BB;;;;N;;;;;
+32DE;CIRCLED KATAKANA SO;So;0;L;<circle> 30BD;;;;N;;;;;
+32DF;CIRCLED KATAKANA TA;So;0;L;<circle> 30BF;;;;N;;;;;
+32E0;CIRCLED KATAKANA TI;So;0;L;<circle> 30C1;;;;N;;;;;
+32E1;CIRCLED KATAKANA TU;So;0;L;<circle> 30C4;;;;N;;;;;
+32E2;CIRCLED KATAKANA TE;So;0;L;<circle> 30C6;;;;N;;;;;
+32E3;CIRCLED KATAKANA TO;So;0;L;<circle> 30C8;;;;N;;;;;
+32E4;CIRCLED KATAKANA NA;So;0;L;<circle> 30CA;;;;N;;;;;
+32E5;CIRCLED KATAKANA NI;So;0;L;<circle> 30CB;;;;N;;;;;
+32E6;CIRCLED KATAKANA NU;So;0;L;<circle> 30CC;;;;N;;;;;
+32E7;CIRCLED KATAKANA NE;So;0;L;<circle> 30CD;;;;N;;;;;
+32E8;CIRCLED KATAKANA NO;So;0;L;<circle> 30CE;;;;N;;;;;
+32E9;CIRCLED KATAKANA HA;So;0;L;<circle> 30CF;;;;N;;;;;
+32EA;CIRCLED KATAKANA HI;So;0;L;<circle> 30D2;;;;N;;;;;
+32EB;CIRCLED KATAKANA HU;So;0;L;<circle> 30D5;;;;N;;;;;
+32EC;CIRCLED KATAKANA HE;So;0;L;<circle> 30D8;;;;N;;;;;
+32ED;CIRCLED KATAKANA HO;So;0;L;<circle> 30DB;;;;N;;;;;
+32EE;CIRCLED KATAKANA MA;So;0;L;<circle> 30DE;;;;N;;;;;
+32EF;CIRCLED KATAKANA MI;So;0;L;<circle> 30DF;;;;N;;;;;
+32F0;CIRCLED KATAKANA MU;So;0;L;<circle> 30E0;;;;N;;;;;
+32F1;CIRCLED KATAKANA ME;So;0;L;<circle> 30E1;;;;N;;;;;
+32F2;CIRCLED KATAKANA MO;So;0;L;<circle> 30E2;;;;N;;;;;
+32F3;CIRCLED KATAKANA YA;So;0;L;<circle> 30E4;;;;N;;;;;
+32F4;CIRCLED KATAKANA YU;So;0;L;<circle> 30E6;;;;N;;;;;
+32F5;CIRCLED KATAKANA YO;So;0;L;<circle> 30E8;;;;N;;;;;
+32F6;CIRCLED KATAKANA RA;So;0;L;<circle> 30E9;;;;N;;;;;
+32F7;CIRCLED KATAKANA RI;So;0;L;<circle> 30EA;;;;N;;;;;
+32F8;CIRCLED KATAKANA RU;So;0;L;<circle> 30EB;;;;N;;;;;
+32F9;CIRCLED KATAKANA RE;So;0;L;<circle> 30EC;;;;N;;;;;
+32FA;CIRCLED KATAKANA RO;So;0;L;<circle> 30ED;;;;N;;;;;
+32FB;CIRCLED KATAKANA WA;So;0;L;<circle> 30EF;;;;N;;;;;
+32FC;CIRCLED KATAKANA WI;So;0;L;<circle> 30F0;;;;N;;;;;
+32FD;CIRCLED KATAKANA WE;So;0;L;<circle> 30F1;;;;N;;;;;
+32FE;CIRCLED KATAKANA WO;So;0;L;<circle> 30F2;;;;N;;;;;
+3300;SQUARE APAATO;So;0;L;<square> 30A2 30D1 30FC 30C8;;;;N;SQUARED APAATO;;;;
+3301;SQUARE ARUHUA;So;0;L;<square> 30A2 30EB 30D5 30A1;;;;N;SQUARED ARUHUA;;;;
+3302;SQUARE ANPEA;So;0;L;<square> 30A2 30F3 30DA 30A2;;;;N;SQUARED ANPEA;;;;
+3303;SQUARE AARU;So;0;L;<square> 30A2 30FC 30EB;;;;N;SQUARED AARU;;;;
+3304;SQUARE ININGU;So;0;L;<square> 30A4 30CB 30F3 30B0;;;;N;SQUARED ININGU;;;;
+3305;SQUARE INTI;So;0;L;<square> 30A4 30F3 30C1;;;;N;SQUARED INTI;;;;
+3306;SQUARE UON;So;0;L;<square> 30A6 30A9 30F3;;;;N;SQUARED UON;;;;
+3307;SQUARE ESUKUUDO;So;0;L;<square> 30A8 30B9 30AF 30FC 30C9;;;;N;SQUARED ESUKUUDO;;;;
+3308;SQUARE EEKAA;So;0;L;<square> 30A8 30FC 30AB 30FC;;;;N;SQUARED EEKAA;;;;
+3309;SQUARE ONSU;So;0;L;<square> 30AA 30F3 30B9;;;;N;SQUARED ONSU;;;;
+330A;SQUARE OOMU;So;0;L;<square> 30AA 30FC 30E0;;;;N;SQUARED OOMU;;;;
+330B;SQUARE KAIRI;So;0;L;<square> 30AB 30A4 30EA;;;;N;SQUARED KAIRI;;;;
+330C;SQUARE KARATTO;So;0;L;<square> 30AB 30E9 30C3 30C8;;;;N;SQUARED KARATTO;;;;
+330D;SQUARE KARORII;So;0;L;<square> 30AB 30ED 30EA 30FC;;;;N;SQUARED KARORII;;;;
+330E;SQUARE GARON;So;0;L;<square> 30AC 30ED 30F3;;;;N;SQUARED GARON;;;;
+330F;SQUARE GANMA;So;0;L;<square> 30AC 30F3 30DE;;;;N;SQUARED GANMA;;;;
+3310;SQUARE GIGA;So;0;L;<square> 30AE 30AC;;;;N;SQUARED GIGA;;;;
+3311;SQUARE GINII;So;0;L;<square> 30AE 30CB 30FC;;;;N;SQUARED GINII;;;;
+3312;SQUARE KYURII;So;0;L;<square> 30AD 30E5 30EA 30FC;;;;N;SQUARED KYURII;;;;
+3313;SQUARE GIRUDAA;So;0;L;<square> 30AE 30EB 30C0 30FC;;;;N;SQUARED GIRUDAA;;;;
+3314;SQUARE KIRO;So;0;L;<square> 30AD 30ED;;;;N;SQUARED KIRO;;;;
+3315;SQUARE KIROGURAMU;So;0;L;<square> 30AD 30ED 30B0 30E9 30E0;;;;N;SQUARED KIROGURAMU;;;;
+3316;SQUARE KIROMEETORU;So;0;L;<square> 30AD 30ED 30E1 30FC 30C8 30EB;;;;N;SQUARED KIROMEETORU;;;;
+3317;SQUARE KIROWATTO;So;0;L;<square> 30AD 30ED 30EF 30C3 30C8;;;;N;SQUARED KIROWATTO;;;;
+3318;SQUARE GURAMU;So;0;L;<square> 30B0 30E9 30E0;;;;N;SQUARED GURAMU;;;;
+3319;SQUARE GURAMUTON;So;0;L;<square> 30B0 30E9 30E0 30C8 30F3;;;;N;SQUARED GURAMUTON;;;;
+331A;SQUARE KURUZEIRO;So;0;L;<square> 30AF 30EB 30BC 30A4 30ED;;;;N;SQUARED KURUZEIRO;;;;
+331B;SQUARE KUROONE;So;0;L;<square> 30AF 30ED 30FC 30CD;;;;N;SQUARED KUROONE;;;;
+331C;SQUARE KEESU;So;0;L;<square> 30B1 30FC 30B9;;;;N;SQUARED KEESU;;;;
+331D;SQUARE KORUNA;So;0;L;<square> 30B3 30EB 30CA;;;;N;SQUARED KORUNA;;;;
+331E;SQUARE KOOPO;So;0;L;<square> 30B3 30FC 30DD;;;;N;SQUARED KOOPO;;;;
+331F;SQUARE SAIKURU;So;0;L;<square> 30B5 30A4 30AF 30EB;;;;N;SQUARED SAIKURU;;;;
+3320;SQUARE SANTIIMU;So;0;L;<square> 30B5 30F3 30C1 30FC 30E0;;;;N;SQUARED SANTIIMU;;;;
+3321;SQUARE SIRINGU;So;0;L;<square> 30B7 30EA 30F3 30B0;;;;N;SQUARED SIRINGU;;;;
+3322;SQUARE SENTI;So;0;L;<square> 30BB 30F3 30C1;;;;N;SQUARED SENTI;;;;
+3323;SQUARE SENTO;So;0;L;<square> 30BB 30F3 30C8;;;;N;SQUARED SENTO;;;;
+3324;SQUARE DAASU;So;0;L;<square> 30C0 30FC 30B9;;;;N;SQUARED DAASU;;;;
+3325;SQUARE DESI;So;0;L;<square> 30C7 30B7;;;;N;SQUARED DESI;;;;
+3326;SQUARE DORU;So;0;L;<square> 30C9 30EB;;;;N;SQUARED DORU;;;;
+3327;SQUARE TON;So;0;L;<square> 30C8 30F3;;;;N;SQUARED TON;;;;
+3328;SQUARE NANO;So;0;L;<square> 30CA 30CE;;;;N;SQUARED NANO;;;;
+3329;SQUARE NOTTO;So;0;L;<square> 30CE 30C3 30C8;;;;N;SQUARED NOTTO;;;;
+332A;SQUARE HAITU;So;0;L;<square> 30CF 30A4 30C4;;;;N;SQUARED HAITU;;;;
+332B;SQUARE PAASENTO;So;0;L;<square> 30D1 30FC 30BB 30F3 30C8;;;;N;SQUARED PAASENTO;;;;
+332C;SQUARE PAATU;So;0;L;<square> 30D1 30FC 30C4;;;;N;SQUARED PAATU;;;;
+332D;SQUARE BAARERU;So;0;L;<square> 30D0 30FC 30EC 30EB;;;;N;SQUARED BAARERU;;;;
+332E;SQUARE PIASUTORU;So;0;L;<square> 30D4 30A2 30B9 30C8 30EB;;;;N;SQUARED PIASUTORU;;;;
+332F;SQUARE PIKURU;So;0;L;<square> 30D4 30AF 30EB;;;;N;SQUARED PIKURU;;;;
+3330;SQUARE PIKO;So;0;L;<square> 30D4 30B3;;;;N;SQUARED PIKO;;;;
+3331;SQUARE BIRU;So;0;L;<square> 30D3 30EB;;;;N;SQUARED BIRU;;;;
+3332;SQUARE HUARADDO;So;0;L;<square> 30D5 30A1 30E9 30C3 30C9;;;;N;SQUARED HUARADDO;;;;
+3333;SQUARE HUIITO;So;0;L;<square> 30D5 30A3 30FC 30C8;;;;N;SQUARED HUIITO;;;;
+3334;SQUARE BUSSYERU;So;0;L;<square> 30D6 30C3 30B7 30A7 30EB;;;;N;SQUARED BUSSYERU;;;;
+3335;SQUARE HURAN;So;0;L;<square> 30D5 30E9 30F3;;;;N;SQUARED HURAN;;;;
+3336;SQUARE HEKUTAARU;So;0;L;<square> 30D8 30AF 30BF 30FC 30EB;;;;N;SQUARED HEKUTAARU;;;;
+3337;SQUARE PESO;So;0;L;<square> 30DA 30BD;;;;N;SQUARED PESO;;;;
+3338;SQUARE PENIHI;So;0;L;<square> 30DA 30CB 30D2;;;;N;SQUARED PENIHI;;;;
+3339;SQUARE HERUTU;So;0;L;<square> 30D8 30EB 30C4;;;;N;SQUARED HERUTU;;;;
+333A;SQUARE PENSU;So;0;L;<square> 30DA 30F3 30B9;;;;N;SQUARED PENSU;;;;
+333B;SQUARE PEEZI;So;0;L;<square> 30DA 30FC 30B8;;;;N;SQUARED PEEZI;;;;
+333C;SQUARE BEETA;So;0;L;<square> 30D9 30FC 30BF;;;;N;SQUARED BEETA;;;;
+333D;SQUARE POINTO;So;0;L;<square> 30DD 30A4 30F3 30C8;;;;N;SQUARED POINTO;;;;
+333E;SQUARE BORUTO;So;0;L;<square> 30DC 30EB 30C8;;;;N;SQUARED BORUTO;;;;
+333F;SQUARE HON;So;0;L;<square> 30DB 30F3;;;;N;SQUARED HON;;;;
+3340;SQUARE PONDO;So;0;L;<square> 30DD 30F3 30C9;;;;N;SQUARED PONDO;;;;
+3341;SQUARE HOORU;So;0;L;<square> 30DB 30FC 30EB;;;;N;SQUARED HOORU;;;;
+3342;SQUARE HOON;So;0;L;<square> 30DB 30FC 30F3;;;;N;SQUARED HOON;;;;
+3343;SQUARE MAIKURO;So;0;L;<square> 30DE 30A4 30AF 30ED;;;;N;SQUARED MAIKURO;;;;
+3344;SQUARE MAIRU;So;0;L;<square> 30DE 30A4 30EB;;;;N;SQUARED MAIRU;;;;
+3345;SQUARE MAHHA;So;0;L;<square> 30DE 30C3 30CF;;;;N;SQUARED MAHHA;;;;
+3346;SQUARE MARUKU;So;0;L;<square> 30DE 30EB 30AF;;;;N;SQUARED MARUKU;;;;
+3347;SQUARE MANSYON;So;0;L;<square> 30DE 30F3 30B7 30E7 30F3;;;;N;SQUARED MANSYON;;;;
+3348;SQUARE MIKURON;So;0;L;<square> 30DF 30AF 30ED 30F3;;;;N;SQUARED MIKURON;;;;
+3349;SQUARE MIRI;So;0;L;<square> 30DF 30EA;;;;N;SQUARED MIRI;;;;
+334A;SQUARE MIRIBAARU;So;0;L;<square> 30DF 30EA 30D0 30FC 30EB;;;;N;SQUARED MIRIBAARU;;;;
+334B;SQUARE MEGA;So;0;L;<square> 30E1 30AC;;;;N;SQUARED MEGA;;;;
+334C;SQUARE MEGATON;So;0;L;<square> 30E1 30AC 30C8 30F3;;;;N;SQUARED MEGATON;;;;
+334D;SQUARE MEETORU;So;0;L;<square> 30E1 30FC 30C8 30EB;;;;N;SQUARED MEETORU;;;;
+334E;SQUARE YAADO;So;0;L;<square> 30E4 30FC 30C9;;;;N;SQUARED YAADO;;;;
+334F;SQUARE YAARU;So;0;L;<square> 30E4 30FC 30EB;;;;N;SQUARED YAARU;;;;
+3350;SQUARE YUAN;So;0;L;<square> 30E6 30A2 30F3;;;;N;SQUARED YUAN;;;;
+3351;SQUARE RITTORU;So;0;L;<square> 30EA 30C3 30C8 30EB;;;;N;SQUARED RITTORU;;;;
+3352;SQUARE RIRA;So;0;L;<square> 30EA 30E9;;;;N;SQUARED RIRA;;;;
+3353;SQUARE RUPII;So;0;L;<square> 30EB 30D4 30FC;;;;N;SQUARED RUPII;;;;
+3354;SQUARE RUUBURU;So;0;L;<square> 30EB 30FC 30D6 30EB;;;;N;SQUARED RUUBURU;;;;
+3355;SQUARE REMU;So;0;L;<square> 30EC 30E0;;;;N;SQUARED REMU;;;;
+3356;SQUARE RENTOGEN;So;0;L;<square> 30EC 30F3 30C8 30B2 30F3;;;;N;SQUARED RENTOGEN;;;;
+3357;SQUARE WATTO;So;0;L;<square> 30EF 30C3 30C8;;;;N;SQUARED WATTO;;;;
+3358;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR ZERO;So;0;L;<compat> 0030 70B9;;;;N;;;;;
+3359;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR ONE;So;0;L;<compat> 0031 70B9;;;;N;;;;;
+335A;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWO;So;0;L;<compat> 0032 70B9;;;;N;;;;;
+335B;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR THREE;So;0;L;<compat> 0033 70B9;;;;N;;;;;
+335C;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR FOUR;So;0;L;<compat> 0034 70B9;;;;N;;;;;
+335D;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR FIVE;So;0;L;<compat> 0035 70B9;;;;N;;;;;
+335E;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR SIX;So;0;L;<compat> 0036 70B9;;;;N;;;;;
+335F;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR SEVEN;So;0;L;<compat> 0037 70B9;;;;N;;;;;
+3360;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR EIGHT;So;0;L;<compat> 0038 70B9;;;;N;;;;;
+3361;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR NINE;So;0;L;<compat> 0039 70B9;;;;N;;;;;
+3362;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TEN;So;0;L;<compat> 0031 0030 70B9;;;;N;;;;;
+3363;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR ELEVEN;So;0;L;<compat> 0031 0031 70B9;;;;N;;;;;
+3364;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWELVE;So;0;L;<compat> 0031 0032 70B9;;;;N;;;;;
+3365;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR THIRTEEN;So;0;L;<compat> 0031 0033 70B9;;;;N;;;;;
+3366;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR FOURTEEN;So;0;L;<compat> 0031 0034 70B9;;;;N;;;;;
+3367;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR FIFTEEN;So;0;L;<compat> 0031 0035 70B9;;;;N;;;;;
+3368;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR SIXTEEN;So;0;L;<compat> 0031 0036 70B9;;;;N;;;;;
+3369;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR SEVENTEEN;So;0;L;<compat> 0031 0037 70B9;;;;N;;;;;
+336A;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR EIGHTEEN;So;0;L;<compat> 0031 0038 70B9;;;;N;;;;;
+336B;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR NINETEEN;So;0;L;<compat> 0031 0039 70B9;;;;N;;;;;
+336C;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY;So;0;L;<compat> 0032 0030 70B9;;;;N;;;;;
+336D;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY-ONE;So;0;L;<compat> 0032 0031 70B9;;;;N;;;;;
+336E;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY-TWO;So;0;L;<compat> 0032 0032 70B9;;;;N;;;;;
+336F;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY-THREE;So;0;L;<compat> 0032 0033 70B9;;;;N;;;;;
+3370;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY-FOUR;So;0;L;<compat> 0032 0034 70B9;;;;N;;;;;
+3371;SQUARE HPA;So;0;L;<square> 0068 0050 0061;;;;N;;;;;
+3372;SQUARE DA;So;0;L;<square> 0064 0061;;;;N;;;;;
+3373;SQUARE AU;So;0;L;<square> 0041 0055;;;;N;;;;;
+3374;SQUARE BAR;So;0;L;<square> 0062 0061 0072;;;;N;;;;;
+3375;SQUARE OV;So;0;L;<square> 006F 0056;;;;N;;;;;
+3376;SQUARE PC;So;0;L;<square> 0070 0063;;;;N;;;;;
+3377;SQUARE DM;So;0;ON;<square> 0064 006D;;;;N;;;;;
+3378;SQUARE DM SQUARED;So;0;ON;<square> 0064 006D 00B2;;;;N;;;;;
+3379;SQUARE DM CUBED;So;0;ON;<square> 0064 006D 00B3;;;;N;;;;;
+337A;SQUARE IU;So;0;ON;<square> 0049 0055;;;;N;;;;;
+337B;SQUARE ERA NAME HEISEI;So;0;L;<square> 5E73 6210;;;;N;SQUARED TWO IDEOGRAPHS ERA NAME HEISEI;;;;
+337C;SQUARE ERA NAME SYOUWA;So;0;L;<square> 662D 548C;;;;N;SQUARED TWO IDEOGRAPHS ERA NAME SYOUWA;;;;
+337D;SQUARE ERA NAME TAISYOU;So;0;L;<square> 5927 6B63;;;;N;SQUARED TWO IDEOGRAPHS ERA NAME TAISYOU;;;;
+337E;SQUARE ERA NAME MEIZI;So;0;L;<square> 660E 6CBB;;;;N;SQUARED TWO IDEOGRAPHS ERA NAME MEIZI;;;;
+337F;SQUARE CORPORATION;So;0;L;<square> 682A 5F0F 4F1A 793E;;;;N;SQUARED FOUR IDEOGRAPHS CORPORATION;;;;
+3380;SQUARE PA AMPS;So;0;L;<square> 0070 0041;;;;N;SQUARED PA AMPS;;;;
+3381;SQUARE NA;So;0;L;<square> 006E 0041;;;;N;SQUARED NA;;;;
+3382;SQUARE MU A;So;0;L;<square> 03BC 0041;;;;N;SQUARED MU A;;;;
+3383;SQUARE MA;So;0;L;<square> 006D 0041;;;;N;SQUARED MA;;;;
+3384;SQUARE KA;So;0;L;<square> 006B 0041;;;;N;SQUARED KA;;;;
+3385;SQUARE KB;So;0;L;<square> 004B 0042;;;;N;SQUARED KB;;;;
+3386;SQUARE MB;So;0;L;<square> 004D 0042;;;;N;SQUARED MB;;;;
+3387;SQUARE GB;So;0;L;<square> 0047 0042;;;;N;SQUARED GB;;;;
+3388;SQUARE CAL;So;0;L;<square> 0063 0061 006C;;;;N;SQUARED CAL;;;;
+3389;SQUARE KCAL;So;0;L;<square> 006B 0063 0061 006C;;;;N;SQUARED KCAL;;;;
+338A;SQUARE PF;So;0;L;<square> 0070 0046;;;;N;SQUARED PF;;;;
+338B;SQUARE NF;So;0;L;<square> 006E 0046;;;;N;SQUARED NF;;;;
+338C;SQUARE MU F;So;0;L;<square> 03BC 0046;;;;N;SQUARED MU F;;;;
+338D;SQUARE MU G;So;0;L;<square> 03BC 0067;;;;N;SQUARED MU G;;;;
+338E;SQUARE MG;So;0;L;<square> 006D 0067;;;;N;SQUARED MG;;;;
+338F;SQUARE KG;So;0;L;<square> 006B 0067;;;;N;SQUARED KG;;;;
+3390;SQUARE HZ;So;0;L;<square> 0048 007A;;;;N;SQUARED HZ;;;;
+3391;SQUARE KHZ;So;0;L;<square> 006B 0048 007A;;;;N;SQUARED KHZ;;;;
+3392;SQUARE MHZ;So;0;L;<square> 004D 0048 007A;;;;N;SQUARED MHZ;;;;
+3393;SQUARE GHZ;So;0;L;<square> 0047 0048 007A;;;;N;SQUARED GHZ;;;;
+3394;SQUARE THZ;So;0;L;<square> 0054 0048 007A;;;;N;SQUARED THZ;;;;
+3395;SQUARE MU L;So;0;L;<square> 03BC 2113;;;;N;SQUARED MU L;;;;
+3396;SQUARE ML;So;0;L;<square> 006D 2113;;;;N;SQUARED ML;;;;
+3397;SQUARE DL;So;0;L;<square> 0064 2113;;;;N;SQUARED DL;;;;
+3398;SQUARE KL;So;0;L;<square> 006B 2113;;;;N;SQUARED KL;;;;
+3399;SQUARE FM;So;0;L;<square> 0066 006D;;;;N;SQUARED FM;;;;
+339A;SQUARE NM;So;0;L;<square> 006E 006D;;;;N;SQUARED NM;;;;
+339B;SQUARE MU M;So;0;L;<square> 03BC 006D;;;;N;SQUARED MU M;;;;
+339C;SQUARE MM;So;0;L;<square> 006D 006D;;;;N;SQUARED MM;;;;
+339D;SQUARE CM;So;0;L;<square> 0063 006D;;;;N;SQUARED CM;;;;
+339E;SQUARE KM;So;0;L;<square> 006B 006D;;;;N;SQUARED KM;;;;
+339F;SQUARE MM SQUARED;So;0;L;<square> 006D 006D 00B2;;;;N;SQUARED MM SQUARED;;;;
+33A0;SQUARE CM SQUARED;So;0;L;<square> 0063 006D 00B2;;;;N;SQUARED CM SQUARED;;;;
+33A1;SQUARE M SQUARED;So;0;L;<square> 006D 00B2;;;;N;SQUARED M SQUARED;;;;
+33A2;SQUARE KM SQUARED;So;0;L;<square> 006B 006D 00B2;;;;N;SQUARED KM SQUARED;;;;
+33A3;SQUARE MM CUBED;So;0;L;<square> 006D 006D 00B3;;;;N;SQUARED MM CUBED;;;;
+33A4;SQUARE CM CUBED;So;0;L;<square> 0063 006D 00B3;;;;N;SQUARED CM CUBED;;;;
+33A5;SQUARE M CUBED;So;0;L;<square> 006D 00B3;;;;N;SQUARED M CUBED;;;;
+33A6;SQUARE KM CUBED;So;0;L;<square> 006B 006D 00B3;;;;N;SQUARED KM CUBED;;;;
+33A7;SQUARE M OVER S;So;0;L;<square> 006D 2215 0073;;;;N;SQUARED M OVER S;;;;
+33A8;SQUARE M OVER S SQUARED;So;0;L;<square> 006D 2215 0073 00B2;;;;N;SQUARED M OVER S SQUARED;;;;
+33A9;SQUARE PA;So;0;L;<square> 0050 0061;;;;N;SQUARED PA;;;;
+33AA;SQUARE KPA;So;0;L;<square> 006B 0050 0061;;;;N;SQUARED KPA;;;;
+33AB;SQUARE MPA;So;0;L;<square> 004D 0050 0061;;;;N;SQUARED MPA;;;;
+33AC;SQUARE GPA;So;0;L;<square> 0047 0050 0061;;;;N;SQUARED GPA;;;;
+33AD;SQUARE RAD;So;0;L;<square> 0072 0061 0064;;;;N;SQUARED RAD;;;;
+33AE;SQUARE RAD OVER S;So;0;L;<square> 0072 0061 0064 2215 0073;;;;N;SQUARED RAD OVER S;;;;
+33AF;SQUARE RAD OVER S SQUARED;So;0;L;<square> 0072 0061 0064 2215 0073 00B2;;;;N;SQUARED RAD OVER S SQUARED;;;;
+33B0;SQUARE PS;So;0;L;<square> 0070 0073;;;;N;SQUARED PS;;;;
+33B1;SQUARE NS;So;0;L;<square> 006E 0073;;;;N;SQUARED NS;;;;
+33B2;SQUARE MU S;So;0;L;<square> 03BC 0073;;;;N;SQUARED MU S;;;;
+33B3;SQUARE MS;So;0;L;<square> 006D 0073;;;;N;SQUARED MS;;;;
+33B4;SQUARE PV;So;0;L;<square> 0070 0056;;;;N;SQUARED PV;;;;
+33B5;SQUARE NV;So;0;L;<square> 006E 0056;;;;N;SQUARED NV;;;;
+33B6;SQUARE MU V;So;0;L;<square> 03BC 0056;;;;N;SQUARED MU V;;;;
+33B7;SQUARE MV;So;0;L;<square> 006D 0056;;;;N;SQUARED MV;;;;
+33B8;SQUARE KV;So;0;L;<square> 006B 0056;;;;N;SQUARED KV;;;;
+33B9;SQUARE MV MEGA;So;0;L;<square> 004D 0056;;;;N;SQUARED MV MEGA;;;;
+33BA;SQUARE PW;So;0;L;<square> 0070 0057;;;;N;SQUARED PW;;;;
+33BB;SQUARE NW;So;0;L;<square> 006E 0057;;;;N;SQUARED NW;;;;
+33BC;SQUARE MU W;So;0;L;<square> 03BC 0057;;;;N;SQUARED MU W;;;;
+33BD;SQUARE MW;So;0;L;<square> 006D 0057;;;;N;SQUARED MW;;;;
+33BE;SQUARE KW;So;0;L;<square> 006B 0057;;;;N;SQUARED KW;;;;
+33BF;SQUARE MW MEGA;So;0;L;<square> 004D 0057;;;;N;SQUARED MW MEGA;;;;
+33C0;SQUARE K OHM;So;0;L;<square> 006B 03A9;;;;N;SQUARED K OHM;;;;
+33C1;SQUARE M OHM;So;0;L;<square> 004D 03A9;;;;N;SQUARED M OHM;;;;
+33C2;SQUARE AM;So;0;L;<square> 0061 002E 006D 002E;;;;N;SQUARED AM;;;;
+33C3;SQUARE BQ;So;0;L;<square> 0042 0071;;;;N;SQUARED BQ;;;;
+33C4;SQUARE CC;So;0;L;<square> 0063 0063;;;;N;SQUARED CC;;;;
+33C5;SQUARE CD;So;0;L;<square> 0063 0064;;;;N;SQUARED CD;;;;
+33C6;SQUARE C OVER KG;So;0;L;<square> 0043 2215 006B 0067;;;;N;SQUARED C OVER KG;;;;
+33C7;SQUARE CO;So;0;L;<square> 0043 006F 002E;;;;N;SQUARED CO;;;;
+33C8;SQUARE DB;So;0;L;<square> 0064 0042;;;;N;SQUARED DB;;;;
+33C9;SQUARE GY;So;0;L;<square> 0047 0079;;;;N;SQUARED GY;;;;
+33CA;SQUARE HA;So;0;L;<square> 0068 0061;;;;N;SQUARED HA;;;;
+33CB;SQUARE HP;So;0;L;<square> 0048 0050;;;;N;SQUARED HP;;;;
+33CC;SQUARE IN;So;0;L;<square> 0069 006E;;;;N;SQUARED IN;;;;
+33CD;SQUARE KK;So;0;L;<square> 004B 004B;;;;N;SQUARED KK;;;;
+33CE;SQUARE KM CAPITAL;So;0;L;<square> 004B 004D;;;;N;SQUARED KM CAPITAL;;;;
+33CF;SQUARE KT;So;0;L;<square> 006B 0074;;;;N;SQUARED KT;;;;
+33D0;SQUARE LM;So;0;L;<square> 006C 006D;;;;N;SQUARED LM;;;;
+33D1;SQUARE LN;So;0;L;<square> 006C 006E;;;;N;SQUARED LN;;;;
+33D2;SQUARE LOG;So;0;L;<square> 006C 006F 0067;;;;N;SQUARED LOG;;;;
+33D3;SQUARE LX;So;0;L;<square> 006C 0078;;;;N;SQUARED LX;;;;
+33D4;SQUARE MB SMALL;So;0;L;<square> 006D 0062;;;;N;SQUARED MB SMALL;;;;
+33D5;SQUARE MIL;So;0;L;<square> 006D 0069 006C;;;;N;SQUARED MIL;;;;
+33D6;SQUARE MOL;So;0;L;<square> 006D 006F 006C;;;;N;SQUARED MOL;;;;
+33D7;SQUARE PH;So;0;L;<square> 0050 0048;;;;N;SQUARED PH;;;;
+33D8;SQUARE PM;So;0;L;<square> 0070 002E 006D 002E;;;;N;SQUARED PM;;;;
+33D9;SQUARE PPM;So;0;L;<square> 0050 0050 004D;;;;N;SQUARED PPM;;;;
+33DA;SQUARE PR;So;0;L;<square> 0050 0052;;;;N;SQUARED PR;;;;
+33DB;SQUARE SR;So;0;L;<square> 0073 0072;;;;N;SQUARED SR;;;;
+33DC;SQUARE SV;So;0;L;<square> 0053 0076;;;;N;SQUARED SV;;;;
+33DD;SQUARE WB;So;0;L;<square> 0057 0062;;;;N;SQUARED WB;;;;
+33DE;SQUARE V OVER M;So;0;ON;<square> 0056 2215 006D;;;;N;;;;;
+33DF;SQUARE A OVER M;So;0;ON;<square> 0041 2215 006D;;;;N;;;;;
+33E0;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY ONE;So;0;L;<compat> 0031 65E5;;;;N;;;;;
+33E1;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWO;So;0;L;<compat> 0032 65E5;;;;N;;;;;
+33E2;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY THREE;So;0;L;<compat> 0033 65E5;;;;N;;;;;
+33E3;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FOUR;So;0;L;<compat> 0034 65E5;;;;N;;;;;
+33E4;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FIVE;So;0;L;<compat> 0035 65E5;;;;N;;;;;
+33E5;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SIX;So;0;L;<compat> 0036 65E5;;;;N;;;;;
+33E6;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SEVEN;So;0;L;<compat> 0037 65E5;;;;N;;;;;
+33E7;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY EIGHT;So;0;L;<compat> 0038 65E5;;;;N;;;;;
+33E8;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY NINE;So;0;L;<compat> 0039 65E5;;;;N;;;;;
+33E9;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TEN;So;0;L;<compat> 0031 0030 65E5;;;;N;;;;;
+33EA;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY ELEVEN;So;0;L;<compat> 0031 0031 65E5;;;;N;;;;;
+33EB;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWELVE;So;0;L;<compat> 0031 0032 65E5;;;;N;;;;;
+33EC;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY THIRTEEN;So;0;L;<compat> 0031 0033 65E5;;;;N;;;;;
+33ED;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FOURTEEN;So;0;L;<compat> 0031 0034 65E5;;;;N;;;;;
+33EE;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FIFTEEN;So;0;L;<compat> 0031 0035 65E5;;;;N;;;;;
+33EF;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SIXTEEN;So;0;L;<compat> 0031 0036 65E5;;;;N;;;;;
+33F0;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SEVENTEEN;So;0;L;<compat> 0031 0037 65E5;;;;N;;;;;
+33F1;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY EIGHTEEN;So;0;L;<compat> 0031 0038 65E5;;;;N;;;;;
+33F2;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY NINETEEN;So;0;L;<compat> 0031 0039 65E5;;;;N;;;;;
+33F3;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY;So;0;L;<compat> 0032 0030 65E5;;;;N;;;;;
+33F4;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-ONE;So;0;L;<compat> 0032 0031 65E5;;;;N;;;;;
+33F5;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-TWO;So;0;L;<compat> 0032 0032 65E5;;;;N;;;;;
+33F6;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-THREE;So;0;L;<compat> 0032 0033 65E5;;;;N;;;;;
+33F7;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-FOUR;So;0;L;<compat> 0032 0034 65E5;;;;N;;;;;
+33F8;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-FIVE;So;0;L;<compat> 0032 0035 65E5;;;;N;;;;;
+33F9;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-SIX;So;0;L;<compat> 0032 0036 65E5;;;;N;;;;;
+33FA;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-SEVEN;So;0;L;<compat> 0032 0037 65E5;;;;N;;;;;
+33FB;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-EIGHT;So;0;L;<compat> 0032 0038 65E5;;;;N;;;;;
+33FC;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-NINE;So;0;L;<compat> 0032 0039 65E5;;;;N;;;;;
+33FD;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY THIRTY;So;0;L;<compat> 0033 0030 65E5;;;;N;;;;;
+33FE;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY THIRTY-ONE;So;0;L;<compat> 0033 0031 65E5;;;;N;;;;;
+33FF;SQUARE GAL;So;0;ON;<square> 0067 0061 006C;;;;N;;;;;
+3400;<CJK Ideograph Extension A, First>;Lo;0;L;;;;;N;;;;;
+4DB5;<CJK Ideograph Extension A, Last>;Lo;0;L;;;;;N;;;;;
+4DC0;HEXAGRAM FOR THE CREATIVE HEAVEN;So;0;ON;;;;;N;;;;;
+4DC1;HEXAGRAM FOR THE RECEPTIVE EARTH;So;0;ON;;;;;N;;;;;
+4DC2;HEXAGRAM FOR DIFFICULTY AT THE BEGINNING;So;0;ON;;;;;N;;;;;
+4DC3;HEXAGRAM FOR YOUTHFUL FOLLY;So;0;ON;;;;;N;;;;;
+4DC4;HEXAGRAM FOR WAITING;So;0;ON;;;;;N;;;;;
+4DC5;HEXAGRAM FOR CONFLICT;So;0;ON;;;;;N;;;;;
+4DC6;HEXAGRAM FOR THE ARMY;So;0;ON;;;;;N;;;;;
+4DC7;HEXAGRAM FOR HOLDING TOGETHER;So;0;ON;;;;;N;;;;;
+4DC8;HEXAGRAM FOR SMALL TAMING;So;0;ON;;;;;N;;;;;
+4DC9;HEXAGRAM FOR TREADING;So;0;ON;;;;;N;;;;;
+4DCA;HEXAGRAM FOR PEACE;So;0;ON;;;;;N;;;;;
+4DCB;HEXAGRAM FOR STANDSTILL;So;0;ON;;;;;N;;;;;
+4DCC;HEXAGRAM FOR FELLOWSHIP;So;0;ON;;;;;N;;;;;
+4DCD;HEXAGRAM FOR GREAT POSSESSION;So;0;ON;;;;;N;;;;;
+4DCE;HEXAGRAM FOR MODESTY;So;0;ON;;;;;N;;;;;
+4DCF;HEXAGRAM FOR ENTHUSIASM;So;0;ON;;;;;N;;;;;
+4DD0;HEXAGRAM FOR FOLLOWING;So;0;ON;;;;;N;;;;;
+4DD1;HEXAGRAM FOR WORK ON THE DECAYED;So;0;ON;;;;;N;;;;;
+4DD2;HEXAGRAM FOR APPROACH;So;0;ON;;;;;N;;;;;
+4DD3;HEXAGRAM FOR CONTEMPLATION;So;0;ON;;;;;N;;;;;
+4DD4;HEXAGRAM FOR BITING THROUGH;So;0;ON;;;;;N;;;;;
+4DD5;HEXAGRAM FOR GRACE;So;0;ON;;;;;N;;;;;
+4DD6;HEXAGRAM FOR SPLITTING APART;So;0;ON;;;;;N;;;;;
+4DD7;HEXAGRAM FOR RETURN;So;0;ON;;;;;N;;;;;
+4DD8;HEXAGRAM FOR INNOCENCE;So;0;ON;;;;;N;;;;;
+4DD9;HEXAGRAM FOR GREAT TAMING;So;0;ON;;;;;N;;;;;
+4DDA;HEXAGRAM FOR MOUTH CORNERS;So;0;ON;;;;;N;;;;;
+4DDB;HEXAGRAM FOR GREAT PREPONDERANCE;So;0;ON;;;;;N;;;;;
+4DDC;HEXAGRAM FOR THE ABYSMAL WATER;So;0;ON;;;;;N;;;;;
+4DDD;HEXAGRAM FOR THE CLINGING FIRE;So;0;ON;;;;;N;;;;;
+4DDE;HEXAGRAM FOR INFLUENCE;So;0;ON;;;;;N;;;;;
+4DDF;HEXAGRAM FOR DURATION;So;0;ON;;;;;N;;;;;
+4DE0;HEXAGRAM FOR RETREAT;So;0;ON;;;;;N;;;;;
+4DE1;HEXAGRAM FOR GREAT POWER;So;0;ON;;;;;N;;;;;
+4DE2;HEXAGRAM FOR PROGRESS;So;0;ON;;;;;N;;;;;
+4DE3;HEXAGRAM FOR DARKENING OF THE LIGHT;So;0;ON;;;;;N;;;;;
+4DE4;HEXAGRAM FOR THE FAMILY;So;0;ON;;;;;N;;;;;
+4DE5;HEXAGRAM FOR OPPOSITION;So;0;ON;;;;;N;;;;;
+4DE6;HEXAGRAM FOR OBSTRUCTION;So;0;ON;;;;;N;;;;;
+4DE7;HEXAGRAM FOR DELIVERANCE;So;0;ON;;;;;N;;;;;
+4DE8;HEXAGRAM FOR DECREASE;So;0;ON;;;;;N;;;;;
+4DE9;HEXAGRAM FOR INCREASE;So;0;ON;;;;;N;;;;;
+4DEA;HEXAGRAM FOR BREAKTHROUGH;So;0;ON;;;;;N;;;;;
+4DEB;HEXAGRAM FOR COMING TO MEET;So;0;ON;;;;;N;;;;;
+4DEC;HEXAGRAM FOR GATHERING TOGETHER;So;0;ON;;;;;N;;;;;
+4DED;HEXAGRAM FOR PUSHING UPWARD;So;0;ON;;;;;N;;;;;
+4DEE;HEXAGRAM FOR OPPRESSION;So;0;ON;;;;;N;;;;;
+4DEF;HEXAGRAM FOR THE WELL;So;0;ON;;;;;N;;;;;
+4DF0;HEXAGRAM FOR REVOLUTION;So;0;ON;;;;;N;;;;;
+4DF1;HEXAGRAM FOR THE CAULDRON;So;0;ON;;;;;N;;;;;
+4DF2;HEXAGRAM FOR THE AROUSING THUNDER;So;0;ON;;;;;N;;;;;
+4DF3;HEXAGRAM FOR THE KEEPING STILL MOUNTAIN;So;0;ON;;;;;N;;;;;
+4DF4;HEXAGRAM FOR DEVELOPMENT;So;0;ON;;;;;N;;;;;
+4DF5;HEXAGRAM FOR THE MARRYING MAIDEN;So;0;ON;;;;;N;;;;;
+4DF6;HEXAGRAM FOR ABUNDANCE;So;0;ON;;;;;N;;;;;
+4DF7;HEXAGRAM FOR THE WANDERER;So;0;ON;;;;;N;;;;;
+4DF8;HEXAGRAM FOR THE GENTLE WIND;So;0;ON;;;;;N;;;;;
+4DF9;HEXAGRAM FOR THE JOYOUS LAKE;So;0;ON;;;;;N;;;;;
+4DFA;HEXAGRAM FOR DISPERSION;So;0;ON;;;;;N;;;;;
+4DFB;HEXAGRAM FOR LIMITATION;So;0;ON;;;;;N;;;;;
+4DFC;HEXAGRAM FOR INNER TRUTH;So;0;ON;;;;;N;;;;;
+4DFD;HEXAGRAM FOR SMALL PREPONDERANCE;So;0;ON;;;;;N;;;;;
+4DFE;HEXAGRAM FOR AFTER COMPLETION;So;0;ON;;;;;N;;;;;
+4DFF;HEXAGRAM FOR BEFORE COMPLETION;So;0;ON;;;;;N;;;;;
+4E00;<CJK Ideograph, First>;Lo;0;L;;;;;N;;;;;
+9FA5;<CJK Ideograph, Last>;Lo;0;L;;;;;N;;;;;
+A000;YI SYLLABLE IT;Lo;0;L;;;;;N;;;;;
+A001;YI SYLLABLE IX;Lo;0;L;;;;;N;;;;;
+A002;YI SYLLABLE I;Lo;0;L;;;;;N;;;;;
+A003;YI SYLLABLE IP;Lo;0;L;;;;;N;;;;;
+A004;YI SYLLABLE IET;Lo;0;L;;;;;N;;;;;
+A005;YI SYLLABLE IEX;Lo;0;L;;;;;N;;;;;
+A006;YI SYLLABLE IE;Lo;0;L;;;;;N;;;;;
+A007;YI SYLLABLE IEP;Lo;0;L;;;;;N;;;;;
+A008;YI SYLLABLE AT;Lo;0;L;;;;;N;;;;;
+A009;YI SYLLABLE AX;Lo;0;L;;;;;N;;;;;
+A00A;YI SYLLABLE A;Lo;0;L;;;;;N;;;;;
+A00B;YI SYLLABLE AP;Lo;0;L;;;;;N;;;;;
+A00C;YI SYLLABLE UOX;Lo;0;L;;;;;N;;;;;
+A00D;YI SYLLABLE UO;Lo;0;L;;;;;N;;;;;
+A00E;YI SYLLABLE UOP;Lo;0;L;;;;;N;;;;;
+A00F;YI SYLLABLE OT;Lo;0;L;;;;;N;;;;;
+A010;YI SYLLABLE OX;Lo;0;L;;;;;N;;;;;
+A011;YI SYLLABLE O;Lo;0;L;;;;;N;;;;;
+A012;YI SYLLABLE OP;Lo;0;L;;;;;N;;;;;
+A013;YI SYLLABLE EX;Lo;0;L;;;;;N;;;;;
+A014;YI SYLLABLE E;Lo;0;L;;;;;N;;;;;
+A015;YI SYLLABLE WU;Lo;0;L;;;;;N;;;;;
+A016;YI SYLLABLE BIT;Lo;0;L;;;;;N;;;;;
+A017;YI SYLLABLE BIX;Lo;0;L;;;;;N;;;;;
+A018;YI SYLLABLE BI;Lo;0;L;;;;;N;;;;;
+A019;YI SYLLABLE BIP;Lo;0;L;;;;;N;;;;;
+A01A;YI SYLLABLE BIET;Lo;0;L;;;;;N;;;;;
+A01B;YI SYLLABLE BIEX;Lo;0;L;;;;;N;;;;;
+A01C;YI SYLLABLE BIE;Lo;0;L;;;;;N;;;;;
+A01D;YI SYLLABLE BIEP;Lo;0;L;;;;;N;;;;;
+A01E;YI SYLLABLE BAT;Lo;0;L;;;;;N;;;;;
+A01F;YI SYLLABLE BAX;Lo;0;L;;;;;N;;;;;
+A020;YI SYLLABLE BA;Lo;0;L;;;;;N;;;;;
+A021;YI SYLLABLE BAP;Lo;0;L;;;;;N;;;;;
+A022;YI SYLLABLE BUOX;Lo;0;L;;;;;N;;;;;
+A023;YI SYLLABLE BUO;Lo;0;L;;;;;N;;;;;
+A024;YI SYLLABLE BUOP;Lo;0;L;;;;;N;;;;;
+A025;YI SYLLABLE BOT;Lo;0;L;;;;;N;;;;;
+A026;YI SYLLABLE BOX;Lo;0;L;;;;;N;;;;;
+A027;YI SYLLABLE BO;Lo;0;L;;;;;N;;;;;
+A028;YI SYLLABLE BOP;Lo;0;L;;;;;N;;;;;
+A029;YI SYLLABLE BEX;Lo;0;L;;;;;N;;;;;
+A02A;YI SYLLABLE BE;Lo;0;L;;;;;N;;;;;
+A02B;YI SYLLABLE BEP;Lo;0;L;;;;;N;;;;;
+A02C;YI SYLLABLE BUT;Lo;0;L;;;;;N;;;;;
+A02D;YI SYLLABLE BUX;Lo;0;L;;;;;N;;;;;
+A02E;YI SYLLABLE BU;Lo;0;L;;;;;N;;;;;
+A02F;YI SYLLABLE BUP;Lo;0;L;;;;;N;;;;;
+A030;YI SYLLABLE BURX;Lo;0;L;;;;;N;;;;;
+A031;YI SYLLABLE BUR;Lo;0;L;;;;;N;;;;;
+A032;YI SYLLABLE BYT;Lo;0;L;;;;;N;;;;;
+A033;YI SYLLABLE BYX;Lo;0;L;;;;;N;;;;;
+A034;YI SYLLABLE BY;Lo;0;L;;;;;N;;;;;
+A035;YI SYLLABLE BYP;Lo;0;L;;;;;N;;;;;
+A036;YI SYLLABLE BYRX;Lo;0;L;;;;;N;;;;;
+A037;YI SYLLABLE BYR;Lo;0;L;;;;;N;;;;;
+A038;YI SYLLABLE PIT;Lo;0;L;;;;;N;;;;;
+A039;YI SYLLABLE PIX;Lo;0;L;;;;;N;;;;;
+A03A;YI SYLLABLE PI;Lo;0;L;;;;;N;;;;;
+A03B;YI SYLLABLE PIP;Lo;0;L;;;;;N;;;;;
+A03C;YI SYLLABLE PIEX;Lo;0;L;;;;;N;;;;;
+A03D;YI SYLLABLE PIE;Lo;0;L;;;;;N;;;;;
+A03E;YI SYLLABLE PIEP;Lo;0;L;;;;;N;;;;;
+A03F;YI SYLLABLE PAT;Lo;0;L;;;;;N;;;;;
+A040;YI SYLLABLE PAX;Lo;0;L;;;;;N;;;;;
+A041;YI SYLLABLE PA;Lo;0;L;;;;;N;;;;;
+A042;YI SYLLABLE PAP;Lo;0;L;;;;;N;;;;;
+A043;YI SYLLABLE PUOX;Lo;0;L;;;;;N;;;;;
+A044;YI SYLLABLE PUO;Lo;0;L;;;;;N;;;;;
+A045;YI SYLLABLE PUOP;Lo;0;L;;;;;N;;;;;
+A046;YI SYLLABLE POT;Lo;0;L;;;;;N;;;;;
+A047;YI SYLLABLE POX;Lo;0;L;;;;;N;;;;;
+A048;YI SYLLABLE PO;Lo;0;L;;;;;N;;;;;
+A049;YI SYLLABLE POP;Lo;0;L;;;;;N;;;;;
+A04A;YI SYLLABLE PUT;Lo;0;L;;;;;N;;;;;
+A04B;YI SYLLABLE PUX;Lo;0;L;;;;;N;;;;;
+A04C;YI SYLLABLE PU;Lo;0;L;;;;;N;;;;;
+A04D;YI SYLLABLE PUP;Lo;0;L;;;;;N;;;;;
+A04E;YI SYLLABLE PURX;Lo;0;L;;;;;N;;;;;
+A04F;YI SYLLABLE PUR;Lo;0;L;;;;;N;;;;;
+A050;YI SYLLABLE PYT;Lo;0;L;;;;;N;;;;;
+A051;YI SYLLABLE PYX;Lo;0;L;;;;;N;;;;;
+A052;YI SYLLABLE PY;Lo;0;L;;;;;N;;;;;
+A053;YI SYLLABLE PYP;Lo;0;L;;;;;N;;;;;
+A054;YI SYLLABLE PYRX;Lo;0;L;;;;;N;;;;;
+A055;YI SYLLABLE PYR;Lo;0;L;;;;;N;;;;;
+A056;YI SYLLABLE BBIT;Lo;0;L;;;;;N;;;;;
+A057;YI SYLLABLE BBIX;Lo;0;L;;;;;N;;;;;
+A058;YI SYLLABLE BBI;Lo;0;L;;;;;N;;;;;
+A059;YI SYLLABLE BBIP;Lo;0;L;;;;;N;;;;;
+A05A;YI SYLLABLE BBIET;Lo;0;L;;;;;N;;;;;
+A05B;YI SYLLABLE BBIEX;Lo;0;L;;;;;N;;;;;
+A05C;YI SYLLABLE BBIE;Lo;0;L;;;;;N;;;;;
+A05D;YI SYLLABLE BBIEP;Lo;0;L;;;;;N;;;;;
+A05E;YI SYLLABLE BBAT;Lo;0;L;;;;;N;;;;;
+A05F;YI SYLLABLE BBAX;Lo;0;L;;;;;N;;;;;
+A060;YI SYLLABLE BBA;Lo;0;L;;;;;N;;;;;
+A061;YI SYLLABLE BBAP;Lo;0;L;;;;;N;;;;;
+A062;YI SYLLABLE BBUOX;Lo;0;L;;;;;N;;;;;
+A063;YI SYLLABLE BBUO;Lo;0;L;;;;;N;;;;;
+A064;YI SYLLABLE BBUOP;Lo;0;L;;;;;N;;;;;
+A065;YI SYLLABLE BBOT;Lo;0;L;;;;;N;;;;;
+A066;YI SYLLABLE BBOX;Lo;0;L;;;;;N;;;;;
+A067;YI SYLLABLE BBO;Lo;0;L;;;;;N;;;;;
+A068;YI SYLLABLE BBOP;Lo;0;L;;;;;N;;;;;
+A069;YI SYLLABLE BBEX;Lo;0;L;;;;;N;;;;;
+A06A;YI SYLLABLE BBE;Lo;0;L;;;;;N;;;;;
+A06B;YI SYLLABLE BBEP;Lo;0;L;;;;;N;;;;;
+A06C;YI SYLLABLE BBUT;Lo;0;L;;;;;N;;;;;
+A06D;YI SYLLABLE BBUX;Lo;0;L;;;;;N;;;;;
+A06E;YI SYLLABLE BBU;Lo;0;L;;;;;N;;;;;
+A06F;YI SYLLABLE BBUP;Lo;0;L;;;;;N;;;;;
+A070;YI SYLLABLE BBURX;Lo;0;L;;;;;N;;;;;
+A071;YI SYLLABLE BBUR;Lo;0;L;;;;;N;;;;;
+A072;YI SYLLABLE BBYT;Lo;0;L;;;;;N;;;;;
+A073;YI SYLLABLE BBYX;Lo;0;L;;;;;N;;;;;
+A074;YI SYLLABLE BBY;Lo;0;L;;;;;N;;;;;
+A075;YI SYLLABLE BBYP;Lo;0;L;;;;;N;;;;;
+A076;YI SYLLABLE NBIT;Lo;0;L;;;;;N;;;;;
+A077;YI SYLLABLE NBIX;Lo;0;L;;;;;N;;;;;
+A078;YI SYLLABLE NBI;Lo;0;L;;;;;N;;;;;
+A079;YI SYLLABLE NBIP;Lo;0;L;;;;;N;;;;;
+A07A;YI SYLLABLE NBIEX;Lo;0;L;;;;;N;;;;;
+A07B;YI SYLLABLE NBIE;Lo;0;L;;;;;N;;;;;
+A07C;YI SYLLABLE NBIEP;Lo;0;L;;;;;N;;;;;
+A07D;YI SYLLABLE NBAT;Lo;0;L;;;;;N;;;;;
+A07E;YI SYLLABLE NBAX;Lo;0;L;;;;;N;;;;;
+A07F;YI SYLLABLE NBA;Lo;0;L;;;;;N;;;;;
+A080;YI SYLLABLE NBAP;Lo;0;L;;;;;N;;;;;
+A081;YI SYLLABLE NBOT;Lo;0;L;;;;;N;;;;;
+A082;YI SYLLABLE NBOX;Lo;0;L;;;;;N;;;;;
+A083;YI SYLLABLE NBO;Lo;0;L;;;;;N;;;;;
+A084;YI SYLLABLE NBOP;Lo;0;L;;;;;N;;;;;
+A085;YI SYLLABLE NBUT;Lo;0;L;;;;;N;;;;;
+A086;YI SYLLABLE NBUX;Lo;0;L;;;;;N;;;;;
+A087;YI SYLLABLE NBU;Lo;0;L;;;;;N;;;;;
+A088;YI SYLLABLE NBUP;Lo;0;L;;;;;N;;;;;
+A089;YI SYLLABLE NBURX;Lo;0;L;;;;;N;;;;;
+A08A;YI SYLLABLE NBUR;Lo;0;L;;;;;N;;;;;
+A08B;YI SYLLABLE NBYT;Lo;0;L;;;;;N;;;;;
+A08C;YI SYLLABLE NBYX;Lo;0;L;;;;;N;;;;;
+A08D;YI SYLLABLE NBY;Lo;0;L;;;;;N;;;;;
+A08E;YI SYLLABLE NBYP;Lo;0;L;;;;;N;;;;;
+A08F;YI SYLLABLE NBYRX;Lo;0;L;;;;;N;;;;;
+A090;YI SYLLABLE NBYR;Lo;0;L;;;;;N;;;;;
+A091;YI SYLLABLE HMIT;Lo;0;L;;;;;N;;;;;
+A092;YI SYLLABLE HMIX;Lo;0;L;;;;;N;;;;;
+A093;YI SYLLABLE HMI;Lo;0;L;;;;;N;;;;;
+A094;YI SYLLABLE HMIP;Lo;0;L;;;;;N;;;;;
+A095;YI SYLLABLE HMIEX;Lo;0;L;;;;;N;;;;;
+A096;YI SYLLABLE HMIE;Lo;0;L;;;;;N;;;;;
+A097;YI SYLLABLE HMIEP;Lo;0;L;;;;;N;;;;;
+A098;YI SYLLABLE HMAT;Lo;0;L;;;;;N;;;;;
+A099;YI SYLLABLE HMAX;Lo;0;L;;;;;N;;;;;
+A09A;YI SYLLABLE HMA;Lo;0;L;;;;;N;;;;;
+A09B;YI SYLLABLE HMAP;Lo;0;L;;;;;N;;;;;
+A09C;YI SYLLABLE HMUOX;Lo;0;L;;;;;N;;;;;
+A09D;YI SYLLABLE HMUO;Lo;0;L;;;;;N;;;;;
+A09E;YI SYLLABLE HMUOP;Lo;0;L;;;;;N;;;;;
+A09F;YI SYLLABLE HMOT;Lo;0;L;;;;;N;;;;;
+A0A0;YI SYLLABLE HMOX;Lo;0;L;;;;;N;;;;;
+A0A1;YI SYLLABLE HMO;Lo;0;L;;;;;N;;;;;
+A0A2;YI SYLLABLE HMOP;Lo;0;L;;;;;N;;;;;
+A0A3;YI SYLLABLE HMUT;Lo;0;L;;;;;N;;;;;
+A0A4;YI SYLLABLE HMUX;Lo;0;L;;;;;N;;;;;
+A0A5;YI SYLLABLE HMU;Lo;0;L;;;;;N;;;;;
+A0A6;YI SYLLABLE HMUP;Lo;0;L;;;;;N;;;;;
+A0A7;YI SYLLABLE HMURX;Lo;0;L;;;;;N;;;;;
+A0A8;YI SYLLABLE HMUR;Lo;0;L;;;;;N;;;;;
+A0A9;YI SYLLABLE HMYX;Lo;0;L;;;;;N;;;;;
+A0AA;YI SYLLABLE HMY;Lo;0;L;;;;;N;;;;;
+A0AB;YI SYLLABLE HMYP;Lo;0;L;;;;;N;;;;;
+A0AC;YI SYLLABLE HMYRX;Lo;0;L;;;;;N;;;;;
+A0AD;YI SYLLABLE HMYR;Lo;0;L;;;;;N;;;;;
+A0AE;YI SYLLABLE MIT;Lo;0;L;;;;;N;;;;;
+A0AF;YI SYLLABLE MIX;Lo;0;L;;;;;N;;;;;
+A0B0;YI SYLLABLE MI;Lo;0;L;;;;;N;;;;;
+A0B1;YI SYLLABLE MIP;Lo;0;L;;;;;N;;;;;
+A0B2;YI SYLLABLE MIEX;Lo;0;L;;;;;N;;;;;
+A0B3;YI SYLLABLE MIE;Lo;0;L;;;;;N;;;;;
+A0B4;YI SYLLABLE MIEP;Lo;0;L;;;;;N;;;;;
+A0B5;YI SYLLABLE MAT;Lo;0;L;;;;;N;;;;;
+A0B6;YI SYLLABLE MAX;Lo;0;L;;;;;N;;;;;
+A0B7;YI SYLLABLE MA;Lo;0;L;;;;;N;;;;;
+A0B8;YI SYLLABLE MAP;Lo;0;L;;;;;N;;;;;
+A0B9;YI SYLLABLE MUOT;Lo;0;L;;;;;N;;;;;
+A0BA;YI SYLLABLE MUOX;Lo;0;L;;;;;N;;;;;
+A0BB;YI SYLLABLE MUO;Lo;0;L;;;;;N;;;;;
+A0BC;YI SYLLABLE MUOP;Lo;0;L;;;;;N;;;;;
+A0BD;YI SYLLABLE MOT;Lo;0;L;;;;;N;;;;;
+A0BE;YI SYLLABLE MOX;Lo;0;L;;;;;N;;;;;
+A0BF;YI SYLLABLE MO;Lo;0;L;;;;;N;;;;;
+A0C0;YI SYLLABLE MOP;Lo;0;L;;;;;N;;;;;
+A0C1;YI SYLLABLE MEX;Lo;0;L;;;;;N;;;;;
+A0C2;YI SYLLABLE ME;Lo;0;L;;;;;N;;;;;
+A0C3;YI SYLLABLE MUT;Lo;0;L;;;;;N;;;;;
+A0C4;YI SYLLABLE MUX;Lo;0;L;;;;;N;;;;;
+A0C5;YI SYLLABLE MU;Lo;0;L;;;;;N;;;;;
+A0C6;YI SYLLABLE MUP;Lo;0;L;;;;;N;;;;;
+A0C7;YI SYLLABLE MURX;Lo;0;L;;;;;N;;;;;
+A0C8;YI SYLLABLE MUR;Lo;0;L;;;;;N;;;;;
+A0C9;YI SYLLABLE MYT;Lo;0;L;;;;;N;;;;;
+A0CA;YI SYLLABLE MYX;Lo;0;L;;;;;N;;;;;
+A0CB;YI SYLLABLE MY;Lo;0;L;;;;;N;;;;;
+A0CC;YI SYLLABLE MYP;Lo;0;L;;;;;N;;;;;
+A0CD;YI SYLLABLE FIT;Lo;0;L;;;;;N;;;;;
+A0CE;YI SYLLABLE FIX;Lo;0;L;;;;;N;;;;;
+A0CF;YI SYLLABLE FI;Lo;0;L;;;;;N;;;;;
+A0D0;YI SYLLABLE FIP;Lo;0;L;;;;;N;;;;;
+A0D1;YI SYLLABLE FAT;Lo;0;L;;;;;N;;;;;
+A0D2;YI SYLLABLE FAX;Lo;0;L;;;;;N;;;;;
+A0D3;YI SYLLABLE FA;Lo;0;L;;;;;N;;;;;
+A0D4;YI SYLLABLE FAP;Lo;0;L;;;;;N;;;;;
+A0D5;YI SYLLABLE FOX;Lo;0;L;;;;;N;;;;;
+A0D6;YI SYLLABLE FO;Lo;0;L;;;;;N;;;;;
+A0D7;YI SYLLABLE FOP;Lo;0;L;;;;;N;;;;;
+A0D8;YI SYLLABLE FUT;Lo;0;L;;;;;N;;;;;
+A0D9;YI SYLLABLE FUX;Lo;0;L;;;;;N;;;;;
+A0DA;YI SYLLABLE FU;Lo;0;L;;;;;N;;;;;
+A0DB;YI SYLLABLE FUP;Lo;0;L;;;;;N;;;;;
+A0DC;YI SYLLABLE FURX;Lo;0;L;;;;;N;;;;;
+A0DD;YI SYLLABLE FUR;Lo;0;L;;;;;N;;;;;
+A0DE;YI SYLLABLE FYT;Lo;0;L;;;;;N;;;;;
+A0DF;YI SYLLABLE FYX;Lo;0;L;;;;;N;;;;;
+A0E0;YI SYLLABLE FY;Lo;0;L;;;;;N;;;;;
+A0E1;YI SYLLABLE FYP;Lo;0;L;;;;;N;;;;;
+A0E2;YI SYLLABLE VIT;Lo;0;L;;;;;N;;;;;
+A0E3;YI SYLLABLE VIX;Lo;0;L;;;;;N;;;;;
+A0E4;YI SYLLABLE VI;Lo;0;L;;;;;N;;;;;
+A0E5;YI SYLLABLE VIP;Lo;0;L;;;;;N;;;;;
+A0E6;YI SYLLABLE VIET;Lo;0;L;;;;;N;;;;;
+A0E7;YI SYLLABLE VIEX;Lo;0;L;;;;;N;;;;;
+A0E8;YI SYLLABLE VIE;Lo;0;L;;;;;N;;;;;
+A0E9;YI SYLLABLE VIEP;Lo;0;L;;;;;N;;;;;
+A0EA;YI SYLLABLE VAT;Lo;0;L;;;;;N;;;;;
+A0EB;YI SYLLABLE VAX;Lo;0;L;;;;;N;;;;;
+A0EC;YI SYLLABLE VA;Lo;0;L;;;;;N;;;;;
+A0ED;YI SYLLABLE VAP;Lo;0;L;;;;;N;;;;;
+A0EE;YI SYLLABLE VOT;Lo;0;L;;;;;N;;;;;
+A0EF;YI SYLLABLE VOX;Lo;0;L;;;;;N;;;;;
+A0F0;YI SYLLABLE VO;Lo;0;L;;;;;N;;;;;
+A0F1;YI SYLLABLE VOP;Lo;0;L;;;;;N;;;;;
+A0F2;YI SYLLABLE VEX;Lo;0;L;;;;;N;;;;;
+A0F3;YI SYLLABLE VEP;Lo;0;L;;;;;N;;;;;
+A0F4;YI SYLLABLE VUT;Lo;0;L;;;;;N;;;;;
+A0F5;YI SYLLABLE VUX;Lo;0;L;;;;;N;;;;;
+A0F6;YI SYLLABLE VU;Lo;0;L;;;;;N;;;;;
+A0F7;YI SYLLABLE VUP;Lo;0;L;;;;;N;;;;;
+A0F8;YI SYLLABLE VURX;Lo;0;L;;;;;N;;;;;
+A0F9;YI SYLLABLE VUR;Lo;0;L;;;;;N;;;;;
+A0FA;YI SYLLABLE VYT;Lo;0;L;;;;;N;;;;;
+A0FB;YI SYLLABLE VYX;Lo;0;L;;;;;N;;;;;
+A0FC;YI SYLLABLE VY;Lo;0;L;;;;;N;;;;;
+A0FD;YI SYLLABLE VYP;Lo;0;L;;;;;N;;;;;
+A0FE;YI SYLLABLE VYRX;Lo;0;L;;;;;N;;;;;
+A0FF;YI SYLLABLE VYR;Lo;0;L;;;;;N;;;;;
+A100;YI SYLLABLE DIT;Lo;0;L;;;;;N;;;;;
+A101;YI SYLLABLE DIX;Lo;0;L;;;;;N;;;;;
+A102;YI SYLLABLE DI;Lo;0;L;;;;;N;;;;;
+A103;YI SYLLABLE DIP;Lo;0;L;;;;;N;;;;;
+A104;YI SYLLABLE DIEX;Lo;0;L;;;;;N;;;;;
+A105;YI SYLLABLE DIE;Lo;0;L;;;;;N;;;;;
+A106;YI SYLLABLE DIEP;Lo;0;L;;;;;N;;;;;
+A107;YI SYLLABLE DAT;Lo;0;L;;;;;N;;;;;
+A108;YI SYLLABLE DAX;Lo;0;L;;;;;N;;;;;
+A109;YI SYLLABLE DA;Lo;0;L;;;;;N;;;;;
+A10A;YI SYLLABLE DAP;Lo;0;L;;;;;N;;;;;
+A10B;YI SYLLABLE DUOX;Lo;0;L;;;;;N;;;;;
+A10C;YI SYLLABLE DUO;Lo;0;L;;;;;N;;;;;
+A10D;YI SYLLABLE DOT;Lo;0;L;;;;;N;;;;;
+A10E;YI SYLLABLE DOX;Lo;0;L;;;;;N;;;;;
+A10F;YI SYLLABLE DO;Lo;0;L;;;;;N;;;;;
+A110;YI SYLLABLE DOP;Lo;0;L;;;;;N;;;;;
+A111;YI SYLLABLE DEX;Lo;0;L;;;;;N;;;;;
+A112;YI SYLLABLE DE;Lo;0;L;;;;;N;;;;;
+A113;YI SYLLABLE DEP;Lo;0;L;;;;;N;;;;;
+A114;YI SYLLABLE DUT;Lo;0;L;;;;;N;;;;;
+A115;YI SYLLABLE DUX;Lo;0;L;;;;;N;;;;;
+A116;YI SYLLABLE DU;Lo;0;L;;;;;N;;;;;
+A117;YI SYLLABLE DUP;Lo;0;L;;;;;N;;;;;
+A118;YI SYLLABLE DURX;Lo;0;L;;;;;N;;;;;
+A119;YI SYLLABLE DUR;Lo;0;L;;;;;N;;;;;
+A11A;YI SYLLABLE TIT;Lo;0;L;;;;;N;;;;;
+A11B;YI SYLLABLE TIX;Lo;0;L;;;;;N;;;;;
+A11C;YI SYLLABLE TI;Lo;0;L;;;;;N;;;;;
+A11D;YI SYLLABLE TIP;Lo;0;L;;;;;N;;;;;
+A11E;YI SYLLABLE TIEX;Lo;0;L;;;;;N;;;;;
+A11F;YI SYLLABLE TIE;Lo;0;L;;;;;N;;;;;
+A120;YI SYLLABLE TIEP;Lo;0;L;;;;;N;;;;;
+A121;YI SYLLABLE TAT;Lo;0;L;;;;;N;;;;;
+A122;YI SYLLABLE TAX;Lo;0;L;;;;;N;;;;;
+A123;YI SYLLABLE TA;Lo;0;L;;;;;N;;;;;
+A124;YI SYLLABLE TAP;Lo;0;L;;;;;N;;;;;
+A125;YI SYLLABLE TUOT;Lo;0;L;;;;;N;;;;;
+A126;YI SYLLABLE TUOX;Lo;0;L;;;;;N;;;;;
+A127;YI SYLLABLE TUO;Lo;0;L;;;;;N;;;;;
+A128;YI SYLLABLE TUOP;Lo;0;L;;;;;N;;;;;
+A129;YI SYLLABLE TOT;Lo;0;L;;;;;N;;;;;
+A12A;YI SYLLABLE TOX;Lo;0;L;;;;;N;;;;;
+A12B;YI SYLLABLE TO;Lo;0;L;;;;;N;;;;;
+A12C;YI SYLLABLE TOP;Lo;0;L;;;;;N;;;;;
+A12D;YI SYLLABLE TEX;Lo;0;L;;;;;N;;;;;
+A12E;YI SYLLABLE TE;Lo;0;L;;;;;N;;;;;
+A12F;YI SYLLABLE TEP;Lo;0;L;;;;;N;;;;;
+A130;YI SYLLABLE TUT;Lo;0;L;;;;;N;;;;;
+A131;YI SYLLABLE TUX;Lo;0;L;;;;;N;;;;;
+A132;YI SYLLABLE TU;Lo;0;L;;;;;N;;;;;
+A133;YI SYLLABLE TUP;Lo;0;L;;;;;N;;;;;
+A134;YI SYLLABLE TURX;Lo;0;L;;;;;N;;;;;
+A135;YI SYLLABLE TUR;Lo;0;L;;;;;N;;;;;
+A136;YI SYLLABLE DDIT;Lo;0;L;;;;;N;;;;;
+A137;YI SYLLABLE DDIX;Lo;0;L;;;;;N;;;;;
+A138;YI SYLLABLE DDI;Lo;0;L;;;;;N;;;;;
+A139;YI SYLLABLE DDIP;Lo;0;L;;;;;N;;;;;
+A13A;YI SYLLABLE DDIEX;Lo;0;L;;;;;N;;;;;
+A13B;YI SYLLABLE DDIE;Lo;0;L;;;;;N;;;;;
+A13C;YI SYLLABLE DDIEP;Lo;0;L;;;;;N;;;;;
+A13D;YI SYLLABLE DDAT;Lo;0;L;;;;;N;;;;;
+A13E;YI SYLLABLE DDAX;Lo;0;L;;;;;N;;;;;
+A13F;YI SYLLABLE DDA;Lo;0;L;;;;;N;;;;;
+A140;YI SYLLABLE DDAP;Lo;0;L;;;;;N;;;;;
+A141;YI SYLLABLE DDUOX;Lo;0;L;;;;;N;;;;;
+A142;YI SYLLABLE DDUO;Lo;0;L;;;;;N;;;;;
+A143;YI SYLLABLE DDUOP;Lo;0;L;;;;;N;;;;;
+A144;YI SYLLABLE DDOT;Lo;0;L;;;;;N;;;;;
+A145;YI SYLLABLE DDOX;Lo;0;L;;;;;N;;;;;
+A146;YI SYLLABLE DDO;Lo;0;L;;;;;N;;;;;
+A147;YI SYLLABLE DDOP;Lo;0;L;;;;;N;;;;;
+A148;YI SYLLABLE DDEX;Lo;0;L;;;;;N;;;;;
+A149;YI SYLLABLE DDE;Lo;0;L;;;;;N;;;;;
+A14A;YI SYLLABLE DDEP;Lo;0;L;;;;;N;;;;;
+A14B;YI SYLLABLE DDUT;Lo;0;L;;;;;N;;;;;
+A14C;YI SYLLABLE DDUX;Lo;0;L;;;;;N;;;;;
+A14D;YI SYLLABLE DDU;Lo;0;L;;;;;N;;;;;
+A14E;YI SYLLABLE DDUP;Lo;0;L;;;;;N;;;;;
+A14F;YI SYLLABLE DDURX;Lo;0;L;;;;;N;;;;;
+A150;YI SYLLABLE DDUR;Lo;0;L;;;;;N;;;;;
+A151;YI SYLLABLE NDIT;Lo;0;L;;;;;N;;;;;
+A152;YI SYLLABLE NDIX;Lo;0;L;;;;;N;;;;;
+A153;YI SYLLABLE NDI;Lo;0;L;;;;;N;;;;;
+A154;YI SYLLABLE NDIP;Lo;0;L;;;;;N;;;;;
+A155;YI SYLLABLE NDIEX;Lo;0;L;;;;;N;;;;;
+A156;YI SYLLABLE NDIE;Lo;0;L;;;;;N;;;;;
+A157;YI SYLLABLE NDAT;Lo;0;L;;;;;N;;;;;
+A158;YI SYLLABLE NDAX;Lo;0;L;;;;;N;;;;;
+A159;YI SYLLABLE NDA;Lo;0;L;;;;;N;;;;;
+A15A;YI SYLLABLE NDAP;Lo;0;L;;;;;N;;;;;
+A15B;YI SYLLABLE NDOT;Lo;0;L;;;;;N;;;;;
+A15C;YI SYLLABLE NDOX;Lo;0;L;;;;;N;;;;;
+A15D;YI SYLLABLE NDO;Lo;0;L;;;;;N;;;;;
+A15E;YI SYLLABLE NDOP;Lo;0;L;;;;;N;;;;;
+A15F;YI SYLLABLE NDEX;Lo;0;L;;;;;N;;;;;
+A160;YI SYLLABLE NDE;Lo;0;L;;;;;N;;;;;
+A161;YI SYLLABLE NDEP;Lo;0;L;;;;;N;;;;;
+A162;YI SYLLABLE NDUT;Lo;0;L;;;;;N;;;;;
+A163;YI SYLLABLE NDUX;Lo;0;L;;;;;N;;;;;
+A164;YI SYLLABLE NDU;Lo;0;L;;;;;N;;;;;
+A165;YI SYLLABLE NDUP;Lo;0;L;;;;;N;;;;;
+A166;YI SYLLABLE NDURX;Lo;0;L;;;;;N;;;;;
+A167;YI SYLLABLE NDUR;Lo;0;L;;;;;N;;;;;
+A168;YI SYLLABLE HNIT;Lo;0;L;;;;;N;;;;;
+A169;YI SYLLABLE HNIX;Lo;0;L;;;;;N;;;;;
+A16A;YI SYLLABLE HNI;Lo;0;L;;;;;N;;;;;
+A16B;YI SYLLABLE HNIP;Lo;0;L;;;;;N;;;;;
+A16C;YI SYLLABLE HNIET;Lo;0;L;;;;;N;;;;;
+A16D;YI SYLLABLE HNIEX;Lo;0;L;;;;;N;;;;;
+A16E;YI SYLLABLE HNIE;Lo;0;L;;;;;N;;;;;
+A16F;YI SYLLABLE HNIEP;Lo;0;L;;;;;N;;;;;
+A170;YI SYLLABLE HNAT;Lo;0;L;;;;;N;;;;;
+A171;YI SYLLABLE HNAX;Lo;0;L;;;;;N;;;;;
+A172;YI SYLLABLE HNA;Lo;0;L;;;;;N;;;;;
+A173;YI SYLLABLE HNAP;Lo;0;L;;;;;N;;;;;
+A174;YI SYLLABLE HNUOX;Lo;0;L;;;;;N;;;;;
+A175;YI SYLLABLE HNUO;Lo;0;L;;;;;N;;;;;
+A176;YI SYLLABLE HNOT;Lo;0;L;;;;;N;;;;;
+A177;YI SYLLABLE HNOX;Lo;0;L;;;;;N;;;;;
+A178;YI SYLLABLE HNOP;Lo;0;L;;;;;N;;;;;
+A179;YI SYLLABLE HNEX;Lo;0;L;;;;;N;;;;;
+A17A;YI SYLLABLE HNE;Lo;0;L;;;;;N;;;;;
+A17B;YI SYLLABLE HNEP;Lo;0;L;;;;;N;;;;;
+A17C;YI SYLLABLE HNUT;Lo;0;L;;;;;N;;;;;
+A17D;YI SYLLABLE NIT;Lo;0;L;;;;;N;;;;;
+A17E;YI SYLLABLE NIX;Lo;0;L;;;;;N;;;;;
+A17F;YI SYLLABLE NI;Lo;0;L;;;;;N;;;;;
+A180;YI SYLLABLE NIP;Lo;0;L;;;;;N;;;;;
+A181;YI SYLLABLE NIEX;Lo;0;L;;;;;N;;;;;
+A182;YI SYLLABLE NIE;Lo;0;L;;;;;N;;;;;
+A183;YI SYLLABLE NIEP;Lo;0;L;;;;;N;;;;;
+A184;YI SYLLABLE NAX;Lo;0;L;;;;;N;;;;;
+A185;YI SYLLABLE NA;Lo;0;L;;;;;N;;;;;
+A186;YI SYLLABLE NAP;Lo;0;L;;;;;N;;;;;
+A187;YI SYLLABLE NUOX;Lo;0;L;;;;;N;;;;;
+A188;YI SYLLABLE NUO;Lo;0;L;;;;;N;;;;;
+A189;YI SYLLABLE NUOP;Lo;0;L;;;;;N;;;;;
+A18A;YI SYLLABLE NOT;Lo;0;L;;;;;N;;;;;
+A18B;YI SYLLABLE NOX;Lo;0;L;;;;;N;;;;;
+A18C;YI SYLLABLE NO;Lo;0;L;;;;;N;;;;;
+A18D;YI SYLLABLE NOP;Lo;0;L;;;;;N;;;;;
+A18E;YI SYLLABLE NEX;Lo;0;L;;;;;N;;;;;
+A18F;YI SYLLABLE NE;Lo;0;L;;;;;N;;;;;
+A190;YI SYLLABLE NEP;Lo;0;L;;;;;N;;;;;
+A191;YI SYLLABLE NUT;Lo;0;L;;;;;N;;;;;
+A192;YI SYLLABLE NUX;Lo;0;L;;;;;N;;;;;
+A193;YI SYLLABLE NU;Lo;0;L;;;;;N;;;;;
+A194;YI SYLLABLE NUP;Lo;0;L;;;;;N;;;;;
+A195;YI SYLLABLE NURX;Lo;0;L;;;;;N;;;;;
+A196;YI SYLLABLE NUR;Lo;0;L;;;;;N;;;;;
+A197;YI SYLLABLE HLIT;Lo;0;L;;;;;N;;;;;
+A198;YI SYLLABLE HLIX;Lo;0;L;;;;;N;;;;;
+A199;YI SYLLABLE HLI;Lo;0;L;;;;;N;;;;;
+A19A;YI SYLLABLE HLIP;Lo;0;L;;;;;N;;;;;
+A19B;YI SYLLABLE HLIEX;Lo;0;L;;;;;N;;;;;
+A19C;YI SYLLABLE HLIE;Lo;0;L;;;;;N;;;;;
+A19D;YI SYLLABLE HLIEP;Lo;0;L;;;;;N;;;;;
+A19E;YI SYLLABLE HLAT;Lo;0;L;;;;;N;;;;;
+A19F;YI SYLLABLE HLAX;Lo;0;L;;;;;N;;;;;
+A1A0;YI SYLLABLE HLA;Lo;0;L;;;;;N;;;;;
+A1A1;YI SYLLABLE HLAP;Lo;0;L;;;;;N;;;;;
+A1A2;YI SYLLABLE HLUOX;Lo;0;L;;;;;N;;;;;
+A1A3;YI SYLLABLE HLUO;Lo;0;L;;;;;N;;;;;
+A1A4;YI SYLLABLE HLUOP;Lo;0;L;;;;;N;;;;;
+A1A5;YI SYLLABLE HLOX;Lo;0;L;;;;;N;;;;;
+A1A6;YI SYLLABLE HLO;Lo;0;L;;;;;N;;;;;
+A1A7;YI SYLLABLE HLOP;Lo;0;L;;;;;N;;;;;
+A1A8;YI SYLLABLE HLEX;Lo;0;L;;;;;N;;;;;
+A1A9;YI SYLLABLE HLE;Lo;0;L;;;;;N;;;;;
+A1AA;YI SYLLABLE HLEP;Lo;0;L;;;;;N;;;;;
+A1AB;YI SYLLABLE HLUT;Lo;0;L;;;;;N;;;;;
+A1AC;YI SYLLABLE HLUX;Lo;0;L;;;;;N;;;;;
+A1AD;YI SYLLABLE HLU;Lo;0;L;;;;;N;;;;;
+A1AE;YI SYLLABLE HLUP;Lo;0;L;;;;;N;;;;;
+A1AF;YI SYLLABLE HLURX;Lo;0;L;;;;;N;;;;;
+A1B0;YI SYLLABLE HLUR;Lo;0;L;;;;;N;;;;;
+A1B1;YI SYLLABLE HLYT;Lo;0;L;;;;;N;;;;;
+A1B2;YI SYLLABLE HLYX;Lo;0;L;;;;;N;;;;;
+A1B3;YI SYLLABLE HLY;Lo;0;L;;;;;N;;;;;
+A1B4;YI SYLLABLE HLYP;Lo;0;L;;;;;N;;;;;
+A1B5;YI SYLLABLE HLYRX;Lo;0;L;;;;;N;;;;;
+A1B6;YI SYLLABLE HLYR;Lo;0;L;;;;;N;;;;;
+A1B7;YI SYLLABLE LIT;Lo;0;L;;;;;N;;;;;
+A1B8;YI SYLLABLE LIX;Lo;0;L;;;;;N;;;;;
+A1B9;YI SYLLABLE LI;Lo;0;L;;;;;N;;;;;
+A1BA;YI SYLLABLE LIP;Lo;0;L;;;;;N;;;;;
+A1BB;YI SYLLABLE LIET;Lo;0;L;;;;;N;;;;;
+A1BC;YI SYLLABLE LIEX;Lo;0;L;;;;;N;;;;;
+A1BD;YI SYLLABLE LIE;Lo;0;L;;;;;N;;;;;
+A1BE;YI SYLLABLE LIEP;Lo;0;L;;;;;N;;;;;
+A1BF;YI SYLLABLE LAT;Lo;0;L;;;;;N;;;;;
+A1C0;YI SYLLABLE LAX;Lo;0;L;;;;;N;;;;;
+A1C1;YI SYLLABLE LA;Lo;0;L;;;;;N;;;;;
+A1C2;YI SYLLABLE LAP;Lo;0;L;;;;;N;;;;;
+A1C3;YI SYLLABLE LUOT;Lo;0;L;;;;;N;;;;;
+A1C4;YI SYLLABLE LUOX;Lo;0;L;;;;;N;;;;;
+A1C5;YI SYLLABLE LUO;Lo;0;L;;;;;N;;;;;
+A1C6;YI SYLLABLE LUOP;Lo;0;L;;;;;N;;;;;
+A1C7;YI SYLLABLE LOT;Lo;0;L;;;;;N;;;;;
+A1C8;YI SYLLABLE LOX;Lo;0;L;;;;;N;;;;;
+A1C9;YI SYLLABLE LO;Lo;0;L;;;;;N;;;;;
+A1CA;YI SYLLABLE LOP;Lo;0;L;;;;;N;;;;;
+A1CB;YI SYLLABLE LEX;Lo;0;L;;;;;N;;;;;
+A1CC;YI SYLLABLE LE;Lo;0;L;;;;;N;;;;;
+A1CD;YI SYLLABLE LEP;Lo;0;L;;;;;N;;;;;
+A1CE;YI SYLLABLE LUT;Lo;0;L;;;;;N;;;;;
+A1CF;YI SYLLABLE LUX;Lo;0;L;;;;;N;;;;;
+A1D0;YI SYLLABLE LU;Lo;0;L;;;;;N;;;;;
+A1D1;YI SYLLABLE LUP;Lo;0;L;;;;;N;;;;;
+A1D2;YI SYLLABLE LURX;Lo;0;L;;;;;N;;;;;
+A1D3;YI SYLLABLE LUR;Lo;0;L;;;;;N;;;;;
+A1D4;YI SYLLABLE LYT;Lo;0;L;;;;;N;;;;;
+A1D5;YI SYLLABLE LYX;Lo;0;L;;;;;N;;;;;
+A1D6;YI SYLLABLE LY;Lo;0;L;;;;;N;;;;;
+A1D7;YI SYLLABLE LYP;Lo;0;L;;;;;N;;;;;
+A1D8;YI SYLLABLE LYRX;Lo;0;L;;;;;N;;;;;
+A1D9;YI SYLLABLE LYR;Lo;0;L;;;;;N;;;;;
+A1DA;YI SYLLABLE GIT;Lo;0;L;;;;;N;;;;;
+A1DB;YI SYLLABLE GIX;Lo;0;L;;;;;N;;;;;
+A1DC;YI SYLLABLE GI;Lo;0;L;;;;;N;;;;;
+A1DD;YI SYLLABLE GIP;Lo;0;L;;;;;N;;;;;
+A1DE;YI SYLLABLE GIET;Lo;0;L;;;;;N;;;;;
+A1DF;YI SYLLABLE GIEX;Lo;0;L;;;;;N;;;;;
+A1E0;YI SYLLABLE GIE;Lo;0;L;;;;;N;;;;;
+A1E1;YI SYLLABLE GIEP;Lo;0;L;;;;;N;;;;;
+A1E2;YI SYLLABLE GAT;Lo;0;L;;;;;N;;;;;
+A1E3;YI SYLLABLE GAX;Lo;0;L;;;;;N;;;;;
+A1E4;YI SYLLABLE GA;Lo;0;L;;;;;N;;;;;
+A1E5;YI SYLLABLE GAP;Lo;0;L;;;;;N;;;;;
+A1E6;YI SYLLABLE GUOT;Lo;0;L;;;;;N;;;;;
+A1E7;YI SYLLABLE GUOX;Lo;0;L;;;;;N;;;;;
+A1E8;YI SYLLABLE GUO;Lo;0;L;;;;;N;;;;;
+A1E9;YI SYLLABLE GUOP;Lo;0;L;;;;;N;;;;;
+A1EA;YI SYLLABLE GOT;Lo;0;L;;;;;N;;;;;
+A1EB;YI SYLLABLE GOX;Lo;0;L;;;;;N;;;;;
+A1EC;YI SYLLABLE GO;Lo;0;L;;;;;N;;;;;
+A1ED;YI SYLLABLE GOP;Lo;0;L;;;;;N;;;;;
+A1EE;YI SYLLABLE GET;Lo;0;L;;;;;N;;;;;
+A1EF;YI SYLLABLE GEX;Lo;0;L;;;;;N;;;;;
+A1F0;YI SYLLABLE GE;Lo;0;L;;;;;N;;;;;
+A1F1;YI SYLLABLE GEP;Lo;0;L;;;;;N;;;;;
+A1F2;YI SYLLABLE GUT;Lo;0;L;;;;;N;;;;;
+A1F3;YI SYLLABLE GUX;Lo;0;L;;;;;N;;;;;
+A1F4;YI SYLLABLE GU;Lo;0;L;;;;;N;;;;;
+A1F5;YI SYLLABLE GUP;Lo;0;L;;;;;N;;;;;
+A1F6;YI SYLLABLE GURX;Lo;0;L;;;;;N;;;;;
+A1F7;YI SYLLABLE GUR;Lo;0;L;;;;;N;;;;;
+A1F8;YI SYLLABLE KIT;Lo;0;L;;;;;N;;;;;
+A1F9;YI SYLLABLE KIX;Lo;0;L;;;;;N;;;;;
+A1FA;YI SYLLABLE KI;Lo;0;L;;;;;N;;;;;
+A1FB;YI SYLLABLE KIP;Lo;0;L;;;;;N;;;;;
+A1FC;YI SYLLABLE KIEX;Lo;0;L;;;;;N;;;;;
+A1FD;YI SYLLABLE KIE;Lo;0;L;;;;;N;;;;;
+A1FE;YI SYLLABLE KIEP;Lo;0;L;;;;;N;;;;;
+A1FF;YI SYLLABLE KAT;Lo;0;L;;;;;N;;;;;
+A200;YI SYLLABLE KAX;Lo;0;L;;;;;N;;;;;
+A201;YI SYLLABLE KA;Lo;0;L;;;;;N;;;;;
+A202;YI SYLLABLE KAP;Lo;0;L;;;;;N;;;;;
+A203;YI SYLLABLE KUOX;Lo;0;L;;;;;N;;;;;
+A204;YI SYLLABLE KUO;Lo;0;L;;;;;N;;;;;
+A205;YI SYLLABLE KUOP;Lo;0;L;;;;;N;;;;;
+A206;YI SYLLABLE KOT;Lo;0;L;;;;;N;;;;;
+A207;YI SYLLABLE KOX;Lo;0;L;;;;;N;;;;;
+A208;YI SYLLABLE KO;Lo;0;L;;;;;N;;;;;
+A209;YI SYLLABLE KOP;Lo;0;L;;;;;N;;;;;
+A20A;YI SYLLABLE KET;Lo;0;L;;;;;N;;;;;
+A20B;YI SYLLABLE KEX;Lo;0;L;;;;;N;;;;;
+A20C;YI SYLLABLE KE;Lo;0;L;;;;;N;;;;;
+A20D;YI SYLLABLE KEP;Lo;0;L;;;;;N;;;;;
+A20E;YI SYLLABLE KUT;Lo;0;L;;;;;N;;;;;
+A20F;YI SYLLABLE KUX;Lo;0;L;;;;;N;;;;;
+A210;YI SYLLABLE KU;Lo;0;L;;;;;N;;;;;
+A211;YI SYLLABLE KUP;Lo;0;L;;;;;N;;;;;
+A212;YI SYLLABLE KURX;Lo;0;L;;;;;N;;;;;
+A213;YI SYLLABLE KUR;Lo;0;L;;;;;N;;;;;
+A214;YI SYLLABLE GGIT;Lo;0;L;;;;;N;;;;;
+A215;YI SYLLABLE GGIX;Lo;0;L;;;;;N;;;;;
+A216;YI SYLLABLE GGI;Lo;0;L;;;;;N;;;;;
+A217;YI SYLLABLE GGIEX;Lo;0;L;;;;;N;;;;;
+A218;YI SYLLABLE GGIE;Lo;0;L;;;;;N;;;;;
+A219;YI SYLLABLE GGIEP;Lo;0;L;;;;;N;;;;;
+A21A;YI SYLLABLE GGAT;Lo;0;L;;;;;N;;;;;
+A21B;YI SYLLABLE GGAX;Lo;0;L;;;;;N;;;;;
+A21C;YI SYLLABLE GGA;Lo;0;L;;;;;N;;;;;
+A21D;YI SYLLABLE GGAP;Lo;0;L;;;;;N;;;;;
+A21E;YI SYLLABLE GGUOT;Lo;0;L;;;;;N;;;;;
+A21F;YI SYLLABLE GGUOX;Lo;0;L;;;;;N;;;;;
+A220;YI SYLLABLE GGUO;Lo;0;L;;;;;N;;;;;
+A221;YI SYLLABLE GGUOP;Lo;0;L;;;;;N;;;;;
+A222;YI SYLLABLE GGOT;Lo;0;L;;;;;N;;;;;
+A223;YI SYLLABLE GGOX;Lo;0;L;;;;;N;;;;;
+A224;YI SYLLABLE GGO;Lo;0;L;;;;;N;;;;;
+A225;YI SYLLABLE GGOP;Lo;0;L;;;;;N;;;;;
+A226;YI SYLLABLE GGET;Lo;0;L;;;;;N;;;;;
+A227;YI SYLLABLE GGEX;Lo;0;L;;;;;N;;;;;
+A228;YI SYLLABLE GGE;Lo;0;L;;;;;N;;;;;
+A229;YI SYLLABLE GGEP;Lo;0;L;;;;;N;;;;;
+A22A;YI SYLLABLE GGUT;Lo;0;L;;;;;N;;;;;
+A22B;YI SYLLABLE GGUX;Lo;0;L;;;;;N;;;;;
+A22C;YI SYLLABLE GGU;Lo;0;L;;;;;N;;;;;
+A22D;YI SYLLABLE GGUP;Lo;0;L;;;;;N;;;;;
+A22E;YI SYLLABLE GGURX;Lo;0;L;;;;;N;;;;;
+A22F;YI SYLLABLE GGUR;Lo;0;L;;;;;N;;;;;
+A230;YI SYLLABLE MGIEX;Lo;0;L;;;;;N;;;;;
+A231;YI SYLLABLE MGIE;Lo;0;L;;;;;N;;;;;
+A232;YI SYLLABLE MGAT;Lo;0;L;;;;;N;;;;;
+A233;YI SYLLABLE MGAX;Lo;0;L;;;;;N;;;;;
+A234;YI SYLLABLE MGA;Lo;0;L;;;;;N;;;;;
+A235;YI SYLLABLE MGAP;Lo;0;L;;;;;N;;;;;
+A236;YI SYLLABLE MGUOX;Lo;0;L;;;;;N;;;;;
+A237;YI SYLLABLE MGUO;Lo;0;L;;;;;N;;;;;
+A238;YI SYLLABLE MGUOP;Lo;0;L;;;;;N;;;;;
+A239;YI SYLLABLE MGOT;Lo;0;L;;;;;N;;;;;
+A23A;YI SYLLABLE MGOX;Lo;0;L;;;;;N;;;;;
+A23B;YI SYLLABLE MGO;Lo;0;L;;;;;N;;;;;
+A23C;YI SYLLABLE MGOP;Lo;0;L;;;;;N;;;;;
+A23D;YI SYLLABLE MGEX;Lo;0;L;;;;;N;;;;;
+A23E;YI SYLLABLE MGE;Lo;0;L;;;;;N;;;;;
+A23F;YI SYLLABLE MGEP;Lo;0;L;;;;;N;;;;;
+A240;YI SYLLABLE MGUT;Lo;0;L;;;;;N;;;;;
+A241;YI SYLLABLE MGUX;Lo;0;L;;;;;N;;;;;
+A242;YI SYLLABLE MGU;Lo;0;L;;;;;N;;;;;
+A243;YI SYLLABLE MGUP;Lo;0;L;;;;;N;;;;;
+A244;YI SYLLABLE MGURX;Lo;0;L;;;;;N;;;;;
+A245;YI SYLLABLE MGUR;Lo;0;L;;;;;N;;;;;
+A246;YI SYLLABLE HXIT;Lo;0;L;;;;;N;;;;;
+A247;YI SYLLABLE HXIX;Lo;0;L;;;;;N;;;;;
+A248;YI SYLLABLE HXI;Lo;0;L;;;;;N;;;;;
+A249;YI SYLLABLE HXIP;Lo;0;L;;;;;N;;;;;
+A24A;YI SYLLABLE HXIET;Lo;0;L;;;;;N;;;;;
+A24B;YI SYLLABLE HXIEX;Lo;0;L;;;;;N;;;;;
+A24C;YI SYLLABLE HXIE;Lo;0;L;;;;;N;;;;;
+A24D;YI SYLLABLE HXIEP;Lo;0;L;;;;;N;;;;;
+A24E;YI SYLLABLE HXAT;Lo;0;L;;;;;N;;;;;
+A24F;YI SYLLABLE HXAX;Lo;0;L;;;;;N;;;;;
+A250;YI SYLLABLE HXA;Lo;0;L;;;;;N;;;;;
+A251;YI SYLLABLE HXAP;Lo;0;L;;;;;N;;;;;
+A252;YI SYLLABLE HXUOT;Lo;0;L;;;;;N;;;;;
+A253;YI SYLLABLE HXUOX;Lo;0;L;;;;;N;;;;;
+A254;YI SYLLABLE HXUO;Lo;0;L;;;;;N;;;;;
+A255;YI SYLLABLE HXUOP;Lo;0;L;;;;;N;;;;;
+A256;YI SYLLABLE HXOT;Lo;0;L;;;;;N;;;;;
+A257;YI SYLLABLE HXOX;Lo;0;L;;;;;N;;;;;
+A258;YI SYLLABLE HXO;Lo;0;L;;;;;N;;;;;
+A259;YI SYLLABLE HXOP;Lo;0;L;;;;;N;;;;;
+A25A;YI SYLLABLE HXEX;Lo;0;L;;;;;N;;;;;
+A25B;YI SYLLABLE HXE;Lo;0;L;;;;;N;;;;;
+A25C;YI SYLLABLE HXEP;Lo;0;L;;;;;N;;;;;
+A25D;YI SYLLABLE NGIEX;Lo;0;L;;;;;N;;;;;
+A25E;YI SYLLABLE NGIE;Lo;0;L;;;;;N;;;;;
+A25F;YI SYLLABLE NGIEP;Lo;0;L;;;;;N;;;;;
+A260;YI SYLLABLE NGAT;Lo;0;L;;;;;N;;;;;
+A261;YI SYLLABLE NGAX;Lo;0;L;;;;;N;;;;;
+A262;YI SYLLABLE NGA;Lo;0;L;;;;;N;;;;;
+A263;YI SYLLABLE NGAP;Lo;0;L;;;;;N;;;;;
+A264;YI SYLLABLE NGUOT;Lo;0;L;;;;;N;;;;;
+A265;YI SYLLABLE NGUOX;Lo;0;L;;;;;N;;;;;
+A266;YI SYLLABLE NGUO;Lo;0;L;;;;;N;;;;;
+A267;YI SYLLABLE NGOT;Lo;0;L;;;;;N;;;;;
+A268;YI SYLLABLE NGOX;Lo;0;L;;;;;N;;;;;
+A269;YI SYLLABLE NGO;Lo;0;L;;;;;N;;;;;
+A26A;YI SYLLABLE NGOP;Lo;0;L;;;;;N;;;;;
+A26B;YI SYLLABLE NGEX;Lo;0;L;;;;;N;;;;;
+A26C;YI SYLLABLE NGE;Lo;0;L;;;;;N;;;;;
+A26D;YI SYLLABLE NGEP;Lo;0;L;;;;;N;;;;;
+A26E;YI SYLLABLE HIT;Lo;0;L;;;;;N;;;;;
+A26F;YI SYLLABLE HIEX;Lo;0;L;;;;;N;;;;;
+A270;YI SYLLABLE HIE;Lo;0;L;;;;;N;;;;;
+A271;YI SYLLABLE HAT;Lo;0;L;;;;;N;;;;;
+A272;YI SYLLABLE HAX;Lo;0;L;;;;;N;;;;;
+A273;YI SYLLABLE HA;Lo;0;L;;;;;N;;;;;
+A274;YI SYLLABLE HAP;Lo;0;L;;;;;N;;;;;
+A275;YI SYLLABLE HUOT;Lo;0;L;;;;;N;;;;;
+A276;YI SYLLABLE HUOX;Lo;0;L;;;;;N;;;;;
+A277;YI SYLLABLE HUO;Lo;0;L;;;;;N;;;;;
+A278;YI SYLLABLE HUOP;Lo;0;L;;;;;N;;;;;
+A279;YI SYLLABLE HOT;Lo;0;L;;;;;N;;;;;
+A27A;YI SYLLABLE HOX;Lo;0;L;;;;;N;;;;;
+A27B;YI SYLLABLE HO;Lo;0;L;;;;;N;;;;;
+A27C;YI SYLLABLE HOP;Lo;0;L;;;;;N;;;;;
+A27D;YI SYLLABLE HEX;Lo;0;L;;;;;N;;;;;
+A27E;YI SYLLABLE HE;Lo;0;L;;;;;N;;;;;
+A27F;YI SYLLABLE HEP;Lo;0;L;;;;;N;;;;;
+A280;YI SYLLABLE WAT;Lo;0;L;;;;;N;;;;;
+A281;YI SYLLABLE WAX;Lo;0;L;;;;;N;;;;;
+A282;YI SYLLABLE WA;Lo;0;L;;;;;N;;;;;
+A283;YI SYLLABLE WAP;Lo;0;L;;;;;N;;;;;
+A284;YI SYLLABLE WUOX;Lo;0;L;;;;;N;;;;;
+A285;YI SYLLABLE WUO;Lo;0;L;;;;;N;;;;;
+A286;YI SYLLABLE WUOP;Lo;0;L;;;;;N;;;;;
+A287;YI SYLLABLE WOX;Lo;0;L;;;;;N;;;;;
+A288;YI SYLLABLE WO;Lo;0;L;;;;;N;;;;;
+A289;YI SYLLABLE WOP;Lo;0;L;;;;;N;;;;;
+A28A;YI SYLLABLE WEX;Lo;0;L;;;;;N;;;;;
+A28B;YI SYLLABLE WE;Lo;0;L;;;;;N;;;;;
+A28C;YI SYLLABLE WEP;Lo;0;L;;;;;N;;;;;
+A28D;YI SYLLABLE ZIT;Lo;0;L;;;;;N;;;;;
+A28E;YI SYLLABLE ZIX;Lo;0;L;;;;;N;;;;;
+A28F;YI SYLLABLE ZI;Lo;0;L;;;;;N;;;;;
+A290;YI SYLLABLE ZIP;Lo;0;L;;;;;N;;;;;
+A291;YI SYLLABLE ZIEX;Lo;0;L;;;;;N;;;;;
+A292;YI SYLLABLE ZIE;Lo;0;L;;;;;N;;;;;
+A293;YI SYLLABLE ZIEP;Lo;0;L;;;;;N;;;;;
+A294;YI SYLLABLE ZAT;Lo;0;L;;;;;N;;;;;
+A295;YI SYLLABLE ZAX;Lo;0;L;;;;;N;;;;;
+A296;YI SYLLABLE ZA;Lo;0;L;;;;;N;;;;;
+A297;YI SYLLABLE ZAP;Lo;0;L;;;;;N;;;;;
+A298;YI SYLLABLE ZUOX;Lo;0;L;;;;;N;;;;;
+A299;YI SYLLABLE ZUO;Lo;0;L;;;;;N;;;;;
+A29A;YI SYLLABLE ZUOP;Lo;0;L;;;;;N;;;;;
+A29B;YI SYLLABLE ZOT;Lo;0;L;;;;;N;;;;;
+A29C;YI SYLLABLE ZOX;Lo;0;L;;;;;N;;;;;
+A29D;YI SYLLABLE ZO;Lo;0;L;;;;;N;;;;;
+A29E;YI SYLLABLE ZOP;Lo;0;L;;;;;N;;;;;
+A29F;YI SYLLABLE ZEX;Lo;0;L;;;;;N;;;;;
+A2A0;YI SYLLABLE ZE;Lo;0;L;;;;;N;;;;;
+A2A1;YI SYLLABLE ZEP;Lo;0;L;;;;;N;;;;;
+A2A2;YI SYLLABLE ZUT;Lo;0;L;;;;;N;;;;;
+A2A3;YI SYLLABLE ZUX;Lo;0;L;;;;;N;;;;;
+A2A4;YI SYLLABLE ZU;Lo;0;L;;;;;N;;;;;
+A2A5;YI SYLLABLE ZUP;Lo;0;L;;;;;N;;;;;
+A2A6;YI SYLLABLE ZURX;Lo;0;L;;;;;N;;;;;
+A2A7;YI SYLLABLE ZUR;Lo;0;L;;;;;N;;;;;
+A2A8;YI SYLLABLE ZYT;Lo;0;L;;;;;N;;;;;
+A2A9;YI SYLLABLE ZYX;Lo;0;L;;;;;N;;;;;
+A2AA;YI SYLLABLE ZY;Lo;0;L;;;;;N;;;;;
+A2AB;YI SYLLABLE ZYP;Lo;0;L;;;;;N;;;;;
+A2AC;YI SYLLABLE ZYRX;Lo;0;L;;;;;N;;;;;
+A2AD;YI SYLLABLE ZYR;Lo;0;L;;;;;N;;;;;
+A2AE;YI SYLLABLE CIT;Lo;0;L;;;;;N;;;;;
+A2AF;YI SYLLABLE CIX;Lo;0;L;;;;;N;;;;;
+A2B0;YI SYLLABLE CI;Lo;0;L;;;;;N;;;;;
+A2B1;YI SYLLABLE CIP;Lo;0;L;;;;;N;;;;;
+A2B2;YI SYLLABLE CIET;Lo;0;L;;;;;N;;;;;
+A2B3;YI SYLLABLE CIEX;Lo;0;L;;;;;N;;;;;
+A2B4;YI SYLLABLE CIE;Lo;0;L;;;;;N;;;;;
+A2B5;YI SYLLABLE CIEP;Lo;0;L;;;;;N;;;;;
+A2B6;YI SYLLABLE CAT;Lo;0;L;;;;;N;;;;;
+A2B7;YI SYLLABLE CAX;Lo;0;L;;;;;N;;;;;
+A2B8;YI SYLLABLE CA;Lo;0;L;;;;;N;;;;;
+A2B9;YI SYLLABLE CAP;Lo;0;L;;;;;N;;;;;
+A2BA;YI SYLLABLE CUOX;Lo;0;L;;;;;N;;;;;
+A2BB;YI SYLLABLE CUO;Lo;0;L;;;;;N;;;;;
+A2BC;YI SYLLABLE CUOP;Lo;0;L;;;;;N;;;;;
+A2BD;YI SYLLABLE COT;Lo;0;L;;;;;N;;;;;
+A2BE;YI SYLLABLE COX;Lo;0;L;;;;;N;;;;;
+A2BF;YI SYLLABLE CO;Lo;0;L;;;;;N;;;;;
+A2C0;YI SYLLABLE COP;Lo;0;L;;;;;N;;;;;
+A2C1;YI SYLLABLE CEX;Lo;0;L;;;;;N;;;;;
+A2C2;YI SYLLABLE CE;Lo;0;L;;;;;N;;;;;
+A2C3;YI SYLLABLE CEP;Lo;0;L;;;;;N;;;;;
+A2C4;YI SYLLABLE CUT;Lo;0;L;;;;;N;;;;;
+A2C5;YI SYLLABLE CUX;Lo;0;L;;;;;N;;;;;
+A2C6;YI SYLLABLE CU;Lo;0;L;;;;;N;;;;;
+A2C7;YI SYLLABLE CUP;Lo;0;L;;;;;N;;;;;
+A2C8;YI SYLLABLE CURX;Lo;0;L;;;;;N;;;;;
+A2C9;YI SYLLABLE CUR;Lo;0;L;;;;;N;;;;;
+A2CA;YI SYLLABLE CYT;Lo;0;L;;;;;N;;;;;
+A2CB;YI SYLLABLE CYX;Lo;0;L;;;;;N;;;;;
+A2CC;YI SYLLABLE CY;Lo;0;L;;;;;N;;;;;
+A2CD;YI SYLLABLE CYP;Lo;0;L;;;;;N;;;;;
+A2CE;YI SYLLABLE CYRX;Lo;0;L;;;;;N;;;;;
+A2CF;YI SYLLABLE CYR;Lo;0;L;;;;;N;;;;;
+A2D0;YI SYLLABLE ZZIT;Lo;0;L;;;;;N;;;;;
+A2D1;YI SYLLABLE ZZIX;Lo;0;L;;;;;N;;;;;
+A2D2;YI SYLLABLE ZZI;Lo;0;L;;;;;N;;;;;
+A2D3;YI SYLLABLE ZZIP;Lo;0;L;;;;;N;;;;;
+A2D4;YI SYLLABLE ZZIET;Lo;0;L;;;;;N;;;;;
+A2D5;YI SYLLABLE ZZIEX;Lo;0;L;;;;;N;;;;;
+A2D6;YI SYLLABLE ZZIE;Lo;0;L;;;;;N;;;;;
+A2D7;YI SYLLABLE ZZIEP;Lo;0;L;;;;;N;;;;;
+A2D8;YI SYLLABLE ZZAT;Lo;0;L;;;;;N;;;;;
+A2D9;YI SYLLABLE ZZAX;Lo;0;L;;;;;N;;;;;
+A2DA;YI SYLLABLE ZZA;Lo;0;L;;;;;N;;;;;
+A2DB;YI SYLLABLE ZZAP;Lo;0;L;;;;;N;;;;;
+A2DC;YI SYLLABLE ZZOX;Lo;0;L;;;;;N;;;;;
+A2DD;YI SYLLABLE ZZO;Lo;0;L;;;;;N;;;;;
+A2DE;YI SYLLABLE ZZOP;Lo;0;L;;;;;N;;;;;
+A2DF;YI SYLLABLE ZZEX;Lo;0;L;;;;;N;;;;;
+A2E0;YI SYLLABLE ZZE;Lo;0;L;;;;;N;;;;;
+A2E1;YI SYLLABLE ZZEP;Lo;0;L;;;;;N;;;;;
+A2E2;YI SYLLABLE ZZUX;Lo;0;L;;;;;N;;;;;
+A2E3;YI SYLLABLE ZZU;Lo;0;L;;;;;N;;;;;
+A2E4;YI SYLLABLE ZZUP;Lo;0;L;;;;;N;;;;;
+A2E5;YI SYLLABLE ZZURX;Lo;0;L;;;;;N;;;;;
+A2E6;YI SYLLABLE ZZUR;Lo;0;L;;;;;N;;;;;
+A2E7;YI SYLLABLE ZZYT;Lo;0;L;;;;;N;;;;;
+A2E8;YI SYLLABLE ZZYX;Lo;0;L;;;;;N;;;;;
+A2E9;YI SYLLABLE ZZY;Lo;0;L;;;;;N;;;;;
+A2EA;YI SYLLABLE ZZYP;Lo;0;L;;;;;N;;;;;
+A2EB;YI SYLLABLE ZZYRX;Lo;0;L;;;;;N;;;;;
+A2EC;YI SYLLABLE ZZYR;Lo;0;L;;;;;N;;;;;
+A2ED;YI SYLLABLE NZIT;Lo;0;L;;;;;N;;;;;
+A2EE;YI SYLLABLE NZIX;Lo;0;L;;;;;N;;;;;
+A2EF;YI SYLLABLE NZI;Lo;0;L;;;;;N;;;;;
+A2F0;YI SYLLABLE NZIP;Lo;0;L;;;;;N;;;;;
+A2F1;YI SYLLABLE NZIEX;Lo;0;L;;;;;N;;;;;
+A2F2;YI SYLLABLE NZIE;Lo;0;L;;;;;N;;;;;
+A2F3;YI SYLLABLE NZIEP;Lo;0;L;;;;;N;;;;;
+A2F4;YI SYLLABLE NZAT;Lo;0;L;;;;;N;;;;;
+A2F5;YI SYLLABLE NZAX;Lo;0;L;;;;;N;;;;;
+A2F6;YI SYLLABLE NZA;Lo;0;L;;;;;N;;;;;
+A2F7;YI SYLLABLE NZAP;Lo;0;L;;;;;N;;;;;
+A2F8;YI SYLLABLE NZUOX;Lo;0;L;;;;;N;;;;;
+A2F9;YI SYLLABLE NZUO;Lo;0;L;;;;;N;;;;;
+A2FA;YI SYLLABLE NZOX;Lo;0;L;;;;;N;;;;;
+A2FB;YI SYLLABLE NZOP;Lo;0;L;;;;;N;;;;;
+A2FC;YI SYLLABLE NZEX;Lo;0;L;;;;;N;;;;;
+A2FD;YI SYLLABLE NZE;Lo;0;L;;;;;N;;;;;
+A2FE;YI SYLLABLE NZUX;Lo;0;L;;;;;N;;;;;
+A2FF;YI SYLLABLE NZU;Lo;0;L;;;;;N;;;;;
+A300;YI SYLLABLE NZUP;Lo;0;L;;;;;N;;;;;
+A301;YI SYLLABLE NZURX;Lo;0;L;;;;;N;;;;;
+A302;YI SYLLABLE NZUR;Lo;0;L;;;;;N;;;;;
+A303;YI SYLLABLE NZYT;Lo;0;L;;;;;N;;;;;
+A304;YI SYLLABLE NZYX;Lo;0;L;;;;;N;;;;;
+A305;YI SYLLABLE NZY;Lo;0;L;;;;;N;;;;;
+A306;YI SYLLABLE NZYP;Lo;0;L;;;;;N;;;;;
+A307;YI SYLLABLE NZYRX;Lo;0;L;;;;;N;;;;;
+A308;YI SYLLABLE NZYR;Lo;0;L;;;;;N;;;;;
+A309;YI SYLLABLE SIT;Lo;0;L;;;;;N;;;;;
+A30A;YI SYLLABLE SIX;Lo;0;L;;;;;N;;;;;
+A30B;YI SYLLABLE SI;Lo;0;L;;;;;N;;;;;
+A30C;YI SYLLABLE SIP;Lo;0;L;;;;;N;;;;;
+A30D;YI SYLLABLE SIEX;Lo;0;L;;;;;N;;;;;
+A30E;YI SYLLABLE SIE;Lo;0;L;;;;;N;;;;;
+A30F;YI SYLLABLE SIEP;Lo;0;L;;;;;N;;;;;
+A310;YI SYLLABLE SAT;Lo;0;L;;;;;N;;;;;
+A311;YI SYLLABLE SAX;Lo;0;L;;;;;N;;;;;
+A312;YI SYLLABLE SA;Lo;0;L;;;;;N;;;;;
+A313;YI SYLLABLE SAP;Lo;0;L;;;;;N;;;;;
+A314;YI SYLLABLE SUOX;Lo;0;L;;;;;N;;;;;
+A315;YI SYLLABLE SUO;Lo;0;L;;;;;N;;;;;
+A316;YI SYLLABLE SUOP;Lo;0;L;;;;;N;;;;;
+A317;YI SYLLABLE SOT;Lo;0;L;;;;;N;;;;;
+A318;YI SYLLABLE SOX;Lo;0;L;;;;;N;;;;;
+A319;YI SYLLABLE SO;Lo;0;L;;;;;N;;;;;
+A31A;YI SYLLABLE SOP;Lo;0;L;;;;;N;;;;;
+A31B;YI SYLLABLE SEX;Lo;0;L;;;;;N;;;;;
+A31C;YI SYLLABLE SE;Lo;0;L;;;;;N;;;;;
+A31D;YI SYLLABLE SEP;Lo;0;L;;;;;N;;;;;
+A31E;YI SYLLABLE SUT;Lo;0;L;;;;;N;;;;;
+A31F;YI SYLLABLE SUX;Lo;0;L;;;;;N;;;;;
+A320;YI SYLLABLE SU;Lo;0;L;;;;;N;;;;;
+A321;YI SYLLABLE SUP;Lo;0;L;;;;;N;;;;;
+A322;YI SYLLABLE SURX;Lo;0;L;;;;;N;;;;;
+A323;YI SYLLABLE SUR;Lo;0;L;;;;;N;;;;;
+A324;YI SYLLABLE SYT;Lo;0;L;;;;;N;;;;;
+A325;YI SYLLABLE SYX;Lo;0;L;;;;;N;;;;;
+A326;YI SYLLABLE SY;Lo;0;L;;;;;N;;;;;
+A327;YI SYLLABLE SYP;Lo;0;L;;;;;N;;;;;
+A328;YI SYLLABLE SYRX;Lo;0;L;;;;;N;;;;;
+A329;YI SYLLABLE SYR;Lo;0;L;;;;;N;;;;;
+A32A;YI SYLLABLE SSIT;Lo;0;L;;;;;N;;;;;
+A32B;YI SYLLABLE SSIX;Lo;0;L;;;;;N;;;;;
+A32C;YI SYLLABLE SSI;Lo;0;L;;;;;N;;;;;
+A32D;YI SYLLABLE SSIP;Lo;0;L;;;;;N;;;;;
+A32E;YI SYLLABLE SSIEX;Lo;0;L;;;;;N;;;;;
+A32F;YI SYLLABLE SSIE;Lo;0;L;;;;;N;;;;;
+A330;YI SYLLABLE SSIEP;Lo;0;L;;;;;N;;;;;
+A331;YI SYLLABLE SSAT;Lo;0;L;;;;;N;;;;;
+A332;YI SYLLABLE SSAX;Lo;0;L;;;;;N;;;;;
+A333;YI SYLLABLE SSA;Lo;0;L;;;;;N;;;;;
+A334;YI SYLLABLE SSAP;Lo;0;L;;;;;N;;;;;
+A335;YI SYLLABLE SSOT;Lo;0;L;;;;;N;;;;;
+A336;YI SYLLABLE SSOX;Lo;0;L;;;;;N;;;;;
+A337;YI SYLLABLE SSO;Lo;0;L;;;;;N;;;;;
+A338;YI SYLLABLE SSOP;Lo;0;L;;;;;N;;;;;
+A339;YI SYLLABLE SSEX;Lo;0;L;;;;;N;;;;;
+A33A;YI SYLLABLE SSE;Lo;0;L;;;;;N;;;;;
+A33B;YI SYLLABLE SSEP;Lo;0;L;;;;;N;;;;;
+A33C;YI SYLLABLE SSUT;Lo;0;L;;;;;N;;;;;
+A33D;YI SYLLABLE SSUX;Lo;0;L;;;;;N;;;;;
+A33E;YI SYLLABLE SSU;Lo;0;L;;;;;N;;;;;
+A33F;YI SYLLABLE SSUP;Lo;0;L;;;;;N;;;;;
+A340;YI SYLLABLE SSYT;Lo;0;L;;;;;N;;;;;
+A341;YI SYLLABLE SSYX;Lo;0;L;;;;;N;;;;;
+A342;YI SYLLABLE SSY;Lo;0;L;;;;;N;;;;;
+A343;YI SYLLABLE SSYP;Lo;0;L;;;;;N;;;;;
+A344;YI SYLLABLE SSYRX;Lo;0;L;;;;;N;;;;;
+A345;YI SYLLABLE SSYR;Lo;0;L;;;;;N;;;;;
+A346;YI SYLLABLE ZHAT;Lo;0;L;;;;;N;;;;;
+A347;YI SYLLABLE ZHAX;Lo;0;L;;;;;N;;;;;
+A348;YI SYLLABLE ZHA;Lo;0;L;;;;;N;;;;;
+A349;YI SYLLABLE ZHAP;Lo;0;L;;;;;N;;;;;
+A34A;YI SYLLABLE ZHUOX;Lo;0;L;;;;;N;;;;;
+A34B;YI SYLLABLE ZHUO;Lo;0;L;;;;;N;;;;;
+A34C;YI SYLLABLE ZHUOP;Lo;0;L;;;;;N;;;;;
+A34D;YI SYLLABLE ZHOT;Lo;0;L;;;;;N;;;;;
+A34E;YI SYLLABLE ZHOX;Lo;0;L;;;;;N;;;;;
+A34F;YI SYLLABLE ZHO;Lo;0;L;;;;;N;;;;;
+A350;YI SYLLABLE ZHOP;Lo;0;L;;;;;N;;;;;
+A351;YI SYLLABLE ZHET;Lo;0;L;;;;;N;;;;;
+A352;YI SYLLABLE ZHEX;Lo;0;L;;;;;N;;;;;
+A353;YI SYLLABLE ZHE;Lo;0;L;;;;;N;;;;;
+A354;YI SYLLABLE ZHEP;Lo;0;L;;;;;N;;;;;
+A355;YI SYLLABLE ZHUT;Lo;0;L;;;;;N;;;;;
+A356;YI SYLLABLE ZHUX;Lo;0;L;;;;;N;;;;;
+A357;YI SYLLABLE ZHU;Lo;0;L;;;;;N;;;;;
+A358;YI SYLLABLE ZHUP;Lo;0;L;;;;;N;;;;;
+A359;YI SYLLABLE ZHURX;Lo;0;L;;;;;N;;;;;
+A35A;YI SYLLABLE ZHUR;Lo;0;L;;;;;N;;;;;
+A35B;YI SYLLABLE ZHYT;Lo;0;L;;;;;N;;;;;
+A35C;YI SYLLABLE ZHYX;Lo;0;L;;;;;N;;;;;
+A35D;YI SYLLABLE ZHY;Lo;0;L;;;;;N;;;;;
+A35E;YI SYLLABLE ZHYP;Lo;0;L;;;;;N;;;;;
+A35F;YI SYLLABLE ZHYRX;Lo;0;L;;;;;N;;;;;
+A360;YI SYLLABLE ZHYR;Lo;0;L;;;;;N;;;;;
+A361;YI SYLLABLE CHAT;Lo;0;L;;;;;N;;;;;
+A362;YI SYLLABLE CHAX;Lo;0;L;;;;;N;;;;;
+A363;YI SYLLABLE CHA;Lo;0;L;;;;;N;;;;;
+A364;YI SYLLABLE CHAP;Lo;0;L;;;;;N;;;;;
+A365;YI SYLLABLE CHUOT;Lo;0;L;;;;;N;;;;;
+A366;YI SYLLABLE CHUOX;Lo;0;L;;;;;N;;;;;
+A367;YI SYLLABLE CHUO;Lo;0;L;;;;;N;;;;;
+A368;YI SYLLABLE CHUOP;Lo;0;L;;;;;N;;;;;
+A369;YI SYLLABLE CHOT;Lo;0;L;;;;;N;;;;;
+A36A;YI SYLLABLE CHOX;Lo;0;L;;;;;N;;;;;
+A36B;YI SYLLABLE CHO;Lo;0;L;;;;;N;;;;;
+A36C;YI SYLLABLE CHOP;Lo;0;L;;;;;N;;;;;
+A36D;YI SYLLABLE CHET;Lo;0;L;;;;;N;;;;;
+A36E;YI SYLLABLE CHEX;Lo;0;L;;;;;N;;;;;
+A36F;YI SYLLABLE CHE;Lo;0;L;;;;;N;;;;;
+A370;YI SYLLABLE CHEP;Lo;0;L;;;;;N;;;;;
+A371;YI SYLLABLE CHUX;Lo;0;L;;;;;N;;;;;
+A372;YI SYLLABLE CHU;Lo;0;L;;;;;N;;;;;
+A373;YI SYLLABLE CHUP;Lo;0;L;;;;;N;;;;;
+A374;YI SYLLABLE CHURX;Lo;0;L;;;;;N;;;;;
+A375;YI SYLLABLE CHUR;Lo;0;L;;;;;N;;;;;
+A376;YI SYLLABLE CHYT;Lo;0;L;;;;;N;;;;;
+A377;YI SYLLABLE CHYX;Lo;0;L;;;;;N;;;;;
+A378;YI SYLLABLE CHY;Lo;0;L;;;;;N;;;;;
+A379;YI SYLLABLE CHYP;Lo;0;L;;;;;N;;;;;
+A37A;YI SYLLABLE CHYRX;Lo;0;L;;;;;N;;;;;
+A37B;YI SYLLABLE CHYR;Lo;0;L;;;;;N;;;;;
+A37C;YI SYLLABLE RRAX;Lo;0;L;;;;;N;;;;;
+A37D;YI SYLLABLE RRA;Lo;0;L;;;;;N;;;;;
+A37E;YI SYLLABLE RRUOX;Lo;0;L;;;;;N;;;;;
+A37F;YI SYLLABLE RRUO;Lo;0;L;;;;;N;;;;;
+A380;YI SYLLABLE RROT;Lo;0;L;;;;;N;;;;;
+A381;YI SYLLABLE RROX;Lo;0;L;;;;;N;;;;;
+A382;YI SYLLABLE RRO;Lo;0;L;;;;;N;;;;;
+A383;YI SYLLABLE RROP;Lo;0;L;;;;;N;;;;;
+A384;YI SYLLABLE RRET;Lo;0;L;;;;;N;;;;;
+A385;YI SYLLABLE RREX;Lo;0;L;;;;;N;;;;;
+A386;YI SYLLABLE RRE;Lo;0;L;;;;;N;;;;;
+A387;YI SYLLABLE RREP;Lo;0;L;;;;;N;;;;;
+A388;YI SYLLABLE RRUT;Lo;0;L;;;;;N;;;;;
+A389;YI SYLLABLE RRUX;Lo;0;L;;;;;N;;;;;
+A38A;YI SYLLABLE RRU;Lo;0;L;;;;;N;;;;;
+A38B;YI SYLLABLE RRUP;Lo;0;L;;;;;N;;;;;
+A38C;YI SYLLABLE RRURX;Lo;0;L;;;;;N;;;;;
+A38D;YI SYLLABLE RRUR;Lo;0;L;;;;;N;;;;;
+A38E;YI SYLLABLE RRYT;Lo;0;L;;;;;N;;;;;
+A38F;YI SYLLABLE RRYX;Lo;0;L;;;;;N;;;;;
+A390;YI SYLLABLE RRY;Lo;0;L;;;;;N;;;;;
+A391;YI SYLLABLE RRYP;Lo;0;L;;;;;N;;;;;
+A392;YI SYLLABLE RRYRX;Lo;0;L;;;;;N;;;;;
+A393;YI SYLLABLE RRYR;Lo;0;L;;;;;N;;;;;
+A394;YI SYLLABLE NRAT;Lo;0;L;;;;;N;;;;;
+A395;YI SYLLABLE NRAX;Lo;0;L;;;;;N;;;;;
+A396;YI SYLLABLE NRA;Lo;0;L;;;;;N;;;;;
+A397;YI SYLLABLE NRAP;Lo;0;L;;;;;N;;;;;
+A398;YI SYLLABLE NROX;Lo;0;L;;;;;N;;;;;
+A399;YI SYLLABLE NRO;Lo;0;L;;;;;N;;;;;
+A39A;YI SYLLABLE NROP;Lo;0;L;;;;;N;;;;;
+A39B;YI SYLLABLE NRET;Lo;0;L;;;;;N;;;;;
+A39C;YI SYLLABLE NREX;Lo;0;L;;;;;N;;;;;
+A39D;YI SYLLABLE NRE;Lo;0;L;;;;;N;;;;;
+A39E;YI SYLLABLE NREP;Lo;0;L;;;;;N;;;;;
+A39F;YI SYLLABLE NRUT;Lo;0;L;;;;;N;;;;;
+A3A0;YI SYLLABLE NRUX;Lo;0;L;;;;;N;;;;;
+A3A1;YI SYLLABLE NRU;Lo;0;L;;;;;N;;;;;
+A3A2;YI SYLLABLE NRUP;Lo;0;L;;;;;N;;;;;
+A3A3;YI SYLLABLE NRURX;Lo;0;L;;;;;N;;;;;
+A3A4;YI SYLLABLE NRUR;Lo;0;L;;;;;N;;;;;
+A3A5;YI SYLLABLE NRYT;Lo;0;L;;;;;N;;;;;
+A3A6;YI SYLLABLE NRYX;Lo;0;L;;;;;N;;;;;
+A3A7;YI SYLLABLE NRY;Lo;0;L;;;;;N;;;;;
+A3A8;YI SYLLABLE NRYP;Lo;0;L;;;;;N;;;;;
+A3A9;YI SYLLABLE NRYRX;Lo;0;L;;;;;N;;;;;
+A3AA;YI SYLLABLE NRYR;Lo;0;L;;;;;N;;;;;
+A3AB;YI SYLLABLE SHAT;Lo;0;L;;;;;N;;;;;
+A3AC;YI SYLLABLE SHAX;Lo;0;L;;;;;N;;;;;
+A3AD;YI SYLLABLE SHA;Lo;0;L;;;;;N;;;;;
+A3AE;YI SYLLABLE SHAP;Lo;0;L;;;;;N;;;;;
+A3AF;YI SYLLABLE SHUOX;Lo;0;L;;;;;N;;;;;
+A3B0;YI SYLLABLE SHUO;Lo;0;L;;;;;N;;;;;
+A3B1;YI SYLLABLE SHUOP;Lo;0;L;;;;;N;;;;;
+A3B2;YI SYLLABLE SHOT;Lo;0;L;;;;;N;;;;;
+A3B3;YI SYLLABLE SHOX;Lo;0;L;;;;;N;;;;;
+A3B4;YI SYLLABLE SHO;Lo;0;L;;;;;N;;;;;
+A3B5;YI SYLLABLE SHOP;Lo;0;L;;;;;N;;;;;
+A3B6;YI SYLLABLE SHET;Lo;0;L;;;;;N;;;;;
+A3B7;YI SYLLABLE SHEX;Lo;0;L;;;;;N;;;;;
+A3B8;YI SYLLABLE SHE;Lo;0;L;;;;;N;;;;;
+A3B9;YI SYLLABLE SHEP;Lo;0;L;;;;;N;;;;;
+A3BA;YI SYLLABLE SHUT;Lo;0;L;;;;;N;;;;;
+A3BB;YI SYLLABLE SHUX;Lo;0;L;;;;;N;;;;;
+A3BC;YI SYLLABLE SHU;Lo;0;L;;;;;N;;;;;
+A3BD;YI SYLLABLE SHUP;Lo;0;L;;;;;N;;;;;
+A3BE;YI SYLLABLE SHURX;Lo;0;L;;;;;N;;;;;
+A3BF;YI SYLLABLE SHUR;Lo;0;L;;;;;N;;;;;
+A3C0;YI SYLLABLE SHYT;Lo;0;L;;;;;N;;;;;
+A3C1;YI SYLLABLE SHYX;Lo;0;L;;;;;N;;;;;
+A3C2;YI SYLLABLE SHY;Lo;0;L;;;;;N;;;;;
+A3C3;YI SYLLABLE SHYP;Lo;0;L;;;;;N;;;;;
+A3C4;YI SYLLABLE SHYRX;Lo;0;L;;;;;N;;;;;
+A3C5;YI SYLLABLE SHYR;Lo;0;L;;;;;N;;;;;
+A3C6;YI SYLLABLE RAT;Lo;0;L;;;;;N;;;;;
+A3C7;YI SYLLABLE RAX;Lo;0;L;;;;;N;;;;;
+A3C8;YI SYLLABLE RA;Lo;0;L;;;;;N;;;;;
+A3C9;YI SYLLABLE RAP;Lo;0;L;;;;;N;;;;;
+A3CA;YI SYLLABLE RUOX;Lo;0;L;;;;;N;;;;;
+A3CB;YI SYLLABLE RUO;Lo;0;L;;;;;N;;;;;
+A3CC;YI SYLLABLE RUOP;Lo;0;L;;;;;N;;;;;
+A3CD;YI SYLLABLE ROT;Lo;0;L;;;;;N;;;;;
+A3CE;YI SYLLABLE ROX;Lo;0;L;;;;;N;;;;;
+A3CF;YI SYLLABLE RO;Lo;0;L;;;;;N;;;;;
+A3D0;YI SYLLABLE ROP;Lo;0;L;;;;;N;;;;;
+A3D1;YI SYLLABLE REX;Lo;0;L;;;;;N;;;;;
+A3D2;YI SYLLABLE RE;Lo;0;L;;;;;N;;;;;
+A3D3;YI SYLLABLE REP;Lo;0;L;;;;;N;;;;;
+A3D4;YI SYLLABLE RUT;Lo;0;L;;;;;N;;;;;
+A3D5;YI SYLLABLE RUX;Lo;0;L;;;;;N;;;;;
+A3D6;YI SYLLABLE RU;Lo;0;L;;;;;N;;;;;
+A3D7;YI SYLLABLE RUP;Lo;0;L;;;;;N;;;;;
+A3D8;YI SYLLABLE RURX;Lo;0;L;;;;;N;;;;;
+A3D9;YI SYLLABLE RUR;Lo;0;L;;;;;N;;;;;
+A3DA;YI SYLLABLE RYT;Lo;0;L;;;;;N;;;;;
+A3DB;YI SYLLABLE RYX;Lo;0;L;;;;;N;;;;;
+A3DC;YI SYLLABLE RY;Lo;0;L;;;;;N;;;;;
+A3DD;YI SYLLABLE RYP;Lo;0;L;;;;;N;;;;;
+A3DE;YI SYLLABLE RYRX;Lo;0;L;;;;;N;;;;;
+A3DF;YI SYLLABLE RYR;Lo;0;L;;;;;N;;;;;
+A3E0;YI SYLLABLE JIT;Lo;0;L;;;;;N;;;;;
+A3E1;YI SYLLABLE JIX;Lo;0;L;;;;;N;;;;;
+A3E2;YI SYLLABLE JI;Lo;0;L;;;;;N;;;;;
+A3E3;YI SYLLABLE JIP;Lo;0;L;;;;;N;;;;;
+A3E4;YI SYLLABLE JIET;Lo;0;L;;;;;N;;;;;
+A3E5;YI SYLLABLE JIEX;Lo;0;L;;;;;N;;;;;
+A3E6;YI SYLLABLE JIE;Lo;0;L;;;;;N;;;;;
+A3E7;YI SYLLABLE JIEP;Lo;0;L;;;;;N;;;;;
+A3E8;YI SYLLABLE JUOT;Lo;0;L;;;;;N;;;;;
+A3E9;YI SYLLABLE JUOX;Lo;0;L;;;;;N;;;;;
+A3EA;YI SYLLABLE JUO;Lo;0;L;;;;;N;;;;;
+A3EB;YI SYLLABLE JUOP;Lo;0;L;;;;;N;;;;;
+A3EC;YI SYLLABLE JOT;Lo;0;L;;;;;N;;;;;
+A3ED;YI SYLLABLE JOX;Lo;0;L;;;;;N;;;;;
+A3EE;YI SYLLABLE JO;Lo;0;L;;;;;N;;;;;
+A3EF;YI SYLLABLE JOP;Lo;0;L;;;;;N;;;;;
+A3F0;YI SYLLABLE JUT;Lo;0;L;;;;;N;;;;;
+A3F1;YI SYLLABLE JUX;Lo;0;L;;;;;N;;;;;
+A3F2;YI SYLLABLE JU;Lo;0;L;;;;;N;;;;;
+A3F3;YI SYLLABLE JUP;Lo;0;L;;;;;N;;;;;
+A3F4;YI SYLLABLE JURX;Lo;0;L;;;;;N;;;;;
+A3F5;YI SYLLABLE JUR;Lo;0;L;;;;;N;;;;;
+A3F6;YI SYLLABLE JYT;Lo;0;L;;;;;N;;;;;
+A3F7;YI SYLLABLE JYX;Lo;0;L;;;;;N;;;;;
+A3F8;YI SYLLABLE JY;Lo;0;L;;;;;N;;;;;
+A3F9;YI SYLLABLE JYP;Lo;0;L;;;;;N;;;;;
+A3FA;YI SYLLABLE JYRX;Lo;0;L;;;;;N;;;;;
+A3FB;YI SYLLABLE JYR;Lo;0;L;;;;;N;;;;;
+A3FC;YI SYLLABLE QIT;Lo;0;L;;;;;N;;;;;
+A3FD;YI SYLLABLE QIX;Lo;0;L;;;;;N;;;;;
+A3FE;YI SYLLABLE QI;Lo;0;L;;;;;N;;;;;
+A3FF;YI SYLLABLE QIP;Lo;0;L;;;;;N;;;;;
+A400;YI SYLLABLE QIET;Lo;0;L;;;;;N;;;;;
+A401;YI SYLLABLE QIEX;Lo;0;L;;;;;N;;;;;
+A402;YI SYLLABLE QIE;Lo;0;L;;;;;N;;;;;
+A403;YI SYLLABLE QIEP;Lo;0;L;;;;;N;;;;;
+A404;YI SYLLABLE QUOT;Lo;0;L;;;;;N;;;;;
+A405;YI SYLLABLE QUOX;Lo;0;L;;;;;N;;;;;
+A406;YI SYLLABLE QUO;Lo;0;L;;;;;N;;;;;
+A407;YI SYLLABLE QUOP;Lo;0;L;;;;;N;;;;;
+A408;YI SYLLABLE QOT;Lo;0;L;;;;;N;;;;;
+A409;YI SYLLABLE QOX;Lo;0;L;;;;;N;;;;;
+A40A;YI SYLLABLE QO;Lo;0;L;;;;;N;;;;;
+A40B;YI SYLLABLE QOP;Lo;0;L;;;;;N;;;;;
+A40C;YI SYLLABLE QUT;Lo;0;L;;;;;N;;;;;
+A40D;YI SYLLABLE QUX;Lo;0;L;;;;;N;;;;;
+A40E;YI SYLLABLE QU;Lo;0;L;;;;;N;;;;;
+A40F;YI SYLLABLE QUP;Lo;0;L;;;;;N;;;;;
+A410;YI SYLLABLE QURX;Lo;0;L;;;;;N;;;;;
+A411;YI SYLLABLE QUR;Lo;0;L;;;;;N;;;;;
+A412;YI SYLLABLE QYT;Lo;0;L;;;;;N;;;;;
+A413;YI SYLLABLE QYX;Lo;0;L;;;;;N;;;;;
+A414;YI SYLLABLE QY;Lo;0;L;;;;;N;;;;;
+A415;YI SYLLABLE QYP;Lo;0;L;;;;;N;;;;;
+A416;YI SYLLABLE QYRX;Lo;0;L;;;;;N;;;;;
+A417;YI SYLLABLE QYR;Lo;0;L;;;;;N;;;;;
+A418;YI SYLLABLE JJIT;Lo;0;L;;;;;N;;;;;
+A419;YI SYLLABLE JJIX;Lo;0;L;;;;;N;;;;;
+A41A;YI SYLLABLE JJI;Lo;0;L;;;;;N;;;;;
+A41B;YI SYLLABLE JJIP;Lo;0;L;;;;;N;;;;;
+A41C;YI SYLLABLE JJIET;Lo;0;L;;;;;N;;;;;
+A41D;YI SYLLABLE JJIEX;Lo;0;L;;;;;N;;;;;
+A41E;YI SYLLABLE JJIE;Lo;0;L;;;;;N;;;;;
+A41F;YI SYLLABLE JJIEP;Lo;0;L;;;;;N;;;;;
+A420;YI SYLLABLE JJUOX;Lo;0;L;;;;;N;;;;;
+A421;YI SYLLABLE JJUO;Lo;0;L;;;;;N;;;;;
+A422;YI SYLLABLE JJUOP;Lo;0;L;;;;;N;;;;;
+A423;YI SYLLABLE JJOT;Lo;0;L;;;;;N;;;;;
+A424;YI SYLLABLE JJOX;Lo;0;L;;;;;N;;;;;
+A425;YI SYLLABLE JJO;Lo;0;L;;;;;N;;;;;
+A426;YI SYLLABLE JJOP;Lo;0;L;;;;;N;;;;;
+A427;YI SYLLABLE JJUT;Lo;0;L;;;;;N;;;;;
+A428;YI SYLLABLE JJUX;Lo;0;L;;;;;N;;;;;
+A429;YI SYLLABLE JJU;Lo;0;L;;;;;N;;;;;
+A42A;YI SYLLABLE JJUP;Lo;0;L;;;;;N;;;;;
+A42B;YI SYLLABLE JJURX;Lo;0;L;;;;;N;;;;;
+A42C;YI SYLLABLE JJUR;Lo;0;L;;;;;N;;;;;
+A42D;YI SYLLABLE JJYT;Lo;0;L;;;;;N;;;;;
+A42E;YI SYLLABLE JJYX;Lo;0;L;;;;;N;;;;;
+A42F;YI SYLLABLE JJY;Lo;0;L;;;;;N;;;;;
+A430;YI SYLLABLE JJYP;Lo;0;L;;;;;N;;;;;
+A431;YI SYLLABLE NJIT;Lo;0;L;;;;;N;;;;;
+A432;YI SYLLABLE NJIX;Lo;0;L;;;;;N;;;;;
+A433;YI SYLLABLE NJI;Lo;0;L;;;;;N;;;;;
+A434;YI SYLLABLE NJIP;Lo;0;L;;;;;N;;;;;
+A435;YI SYLLABLE NJIET;Lo;0;L;;;;;N;;;;;
+A436;YI SYLLABLE NJIEX;Lo;0;L;;;;;N;;;;;
+A437;YI SYLLABLE NJIE;Lo;0;L;;;;;N;;;;;
+A438;YI SYLLABLE NJIEP;Lo;0;L;;;;;N;;;;;
+A439;YI SYLLABLE NJUOX;Lo;0;L;;;;;N;;;;;
+A43A;YI SYLLABLE NJUO;Lo;0;L;;;;;N;;;;;
+A43B;YI SYLLABLE NJOT;Lo;0;L;;;;;N;;;;;
+A43C;YI SYLLABLE NJOX;Lo;0;L;;;;;N;;;;;
+A43D;YI SYLLABLE NJO;Lo;0;L;;;;;N;;;;;
+A43E;YI SYLLABLE NJOP;Lo;0;L;;;;;N;;;;;
+A43F;YI SYLLABLE NJUX;Lo;0;L;;;;;N;;;;;
+A440;YI SYLLABLE NJU;Lo;0;L;;;;;N;;;;;
+A441;YI SYLLABLE NJUP;Lo;0;L;;;;;N;;;;;
+A442;YI SYLLABLE NJURX;Lo;0;L;;;;;N;;;;;
+A443;YI SYLLABLE NJUR;Lo;0;L;;;;;N;;;;;
+A444;YI SYLLABLE NJYT;Lo;0;L;;;;;N;;;;;
+A445;YI SYLLABLE NJYX;Lo;0;L;;;;;N;;;;;
+A446;YI SYLLABLE NJY;Lo;0;L;;;;;N;;;;;
+A447;YI SYLLABLE NJYP;Lo;0;L;;;;;N;;;;;
+A448;YI SYLLABLE NJYRX;Lo;0;L;;;;;N;;;;;
+A449;YI SYLLABLE NJYR;Lo;0;L;;;;;N;;;;;
+A44A;YI SYLLABLE NYIT;Lo;0;L;;;;;N;;;;;
+A44B;YI SYLLABLE NYIX;Lo;0;L;;;;;N;;;;;
+A44C;YI SYLLABLE NYI;Lo;0;L;;;;;N;;;;;
+A44D;YI SYLLABLE NYIP;Lo;0;L;;;;;N;;;;;
+A44E;YI SYLLABLE NYIET;Lo;0;L;;;;;N;;;;;
+A44F;YI SYLLABLE NYIEX;Lo;0;L;;;;;N;;;;;
+A450;YI SYLLABLE NYIE;Lo;0;L;;;;;N;;;;;
+A451;YI SYLLABLE NYIEP;Lo;0;L;;;;;N;;;;;
+A452;YI SYLLABLE NYUOX;Lo;0;L;;;;;N;;;;;
+A453;YI SYLLABLE NYUO;Lo;0;L;;;;;N;;;;;
+A454;YI SYLLABLE NYUOP;Lo;0;L;;;;;N;;;;;
+A455;YI SYLLABLE NYOT;Lo;0;L;;;;;N;;;;;
+A456;YI SYLLABLE NYOX;Lo;0;L;;;;;N;;;;;
+A457;YI SYLLABLE NYO;Lo;0;L;;;;;N;;;;;
+A458;YI SYLLABLE NYOP;Lo;0;L;;;;;N;;;;;
+A459;YI SYLLABLE NYUT;Lo;0;L;;;;;N;;;;;
+A45A;YI SYLLABLE NYUX;Lo;0;L;;;;;N;;;;;
+A45B;YI SYLLABLE NYU;Lo;0;L;;;;;N;;;;;
+A45C;YI SYLLABLE NYUP;Lo;0;L;;;;;N;;;;;
+A45D;YI SYLLABLE XIT;Lo;0;L;;;;;N;;;;;
+A45E;YI SYLLABLE XIX;Lo;0;L;;;;;N;;;;;
+A45F;YI SYLLABLE XI;Lo;0;L;;;;;N;;;;;
+A460;YI SYLLABLE XIP;Lo;0;L;;;;;N;;;;;
+A461;YI SYLLABLE XIET;Lo;0;L;;;;;N;;;;;
+A462;YI SYLLABLE XIEX;Lo;0;L;;;;;N;;;;;
+A463;YI SYLLABLE XIE;Lo;0;L;;;;;N;;;;;
+A464;YI SYLLABLE XIEP;Lo;0;L;;;;;N;;;;;
+A465;YI SYLLABLE XUOX;Lo;0;L;;;;;N;;;;;
+A466;YI SYLLABLE XUO;Lo;0;L;;;;;N;;;;;
+A467;YI SYLLABLE XOT;Lo;0;L;;;;;N;;;;;
+A468;YI SYLLABLE XOX;Lo;0;L;;;;;N;;;;;
+A469;YI SYLLABLE XO;Lo;0;L;;;;;N;;;;;
+A46A;YI SYLLABLE XOP;Lo;0;L;;;;;N;;;;;
+A46B;YI SYLLABLE XYT;Lo;0;L;;;;;N;;;;;
+A46C;YI SYLLABLE XYX;Lo;0;L;;;;;N;;;;;
+A46D;YI SYLLABLE XY;Lo;0;L;;;;;N;;;;;
+A46E;YI SYLLABLE XYP;Lo;0;L;;;;;N;;;;;
+A46F;YI SYLLABLE XYRX;Lo;0;L;;;;;N;;;;;
+A470;YI SYLLABLE XYR;Lo;0;L;;;;;N;;;;;
+A471;YI SYLLABLE YIT;Lo;0;L;;;;;N;;;;;
+A472;YI SYLLABLE YIX;Lo;0;L;;;;;N;;;;;
+A473;YI SYLLABLE YI;Lo;0;L;;;;;N;;;;;
+A474;YI SYLLABLE YIP;Lo;0;L;;;;;N;;;;;
+A475;YI SYLLABLE YIET;Lo;0;L;;;;;N;;;;;
+A476;YI SYLLABLE YIEX;Lo;0;L;;;;;N;;;;;
+A477;YI SYLLABLE YIE;Lo;0;L;;;;;N;;;;;
+A478;YI SYLLABLE YIEP;Lo;0;L;;;;;N;;;;;
+A479;YI SYLLABLE YUOT;Lo;0;L;;;;;N;;;;;
+A47A;YI SYLLABLE YUOX;Lo;0;L;;;;;N;;;;;
+A47B;YI SYLLABLE YUO;Lo;0;L;;;;;N;;;;;
+A47C;YI SYLLABLE YUOP;Lo;0;L;;;;;N;;;;;
+A47D;YI SYLLABLE YOT;Lo;0;L;;;;;N;;;;;
+A47E;YI SYLLABLE YOX;Lo;0;L;;;;;N;;;;;
+A47F;YI SYLLABLE YO;Lo;0;L;;;;;N;;;;;
+A480;YI SYLLABLE YOP;Lo;0;L;;;;;N;;;;;
+A481;YI SYLLABLE YUT;Lo;0;L;;;;;N;;;;;
+A482;YI SYLLABLE YUX;Lo;0;L;;;;;N;;;;;
+A483;YI SYLLABLE YU;Lo;0;L;;;;;N;;;;;
+A484;YI SYLLABLE YUP;Lo;0;L;;;;;N;;;;;
+A485;YI SYLLABLE YURX;Lo;0;L;;;;;N;;;;;
+A486;YI SYLLABLE YUR;Lo;0;L;;;;;N;;;;;
+A487;YI SYLLABLE YYT;Lo;0;L;;;;;N;;;;;
+A488;YI SYLLABLE YYX;Lo;0;L;;;;;N;;;;;
+A489;YI SYLLABLE YY;Lo;0;L;;;;;N;;;;;
+A48A;YI SYLLABLE YYP;Lo;0;L;;;;;N;;;;;
+A48B;YI SYLLABLE YYRX;Lo;0;L;;;;;N;;;;;
+A48C;YI SYLLABLE YYR;Lo;0;L;;;;;N;;;;;
+A490;YI RADICAL QOT;So;0;ON;;;;;N;;;;;
+A491;YI RADICAL LI;So;0;ON;;;;;N;;;;;
+A492;YI RADICAL KIT;So;0;ON;;;;;N;;;;;
+A493;YI RADICAL NYIP;So;0;ON;;;;;N;;;;;
+A494;YI RADICAL CYP;So;0;ON;;;;;N;;;;;
+A495;YI RADICAL SSI;So;0;ON;;;;;N;;;;;
+A496;YI RADICAL GGOP;So;0;ON;;;;;N;;;;;
+A497;YI RADICAL GEP;So;0;ON;;;;;N;;;;;
+A498;YI RADICAL MI;So;0;ON;;;;;N;;;;;
+A499;YI RADICAL HXIT;So;0;ON;;;;;N;;;;;
+A49A;YI RADICAL LYR;So;0;ON;;;;;N;;;;;
+A49B;YI RADICAL BBUT;So;0;ON;;;;;N;;;;;
+A49C;YI RADICAL MOP;So;0;ON;;;;;N;;;;;
+A49D;YI RADICAL YO;So;0;ON;;;;;N;;;;;
+A49E;YI RADICAL PUT;So;0;ON;;;;;N;;;;;
+A49F;YI RADICAL HXUO;So;0;ON;;;;;N;;;;;
+A4A0;YI RADICAL TAT;So;0;ON;;;;;N;;;;;
+A4A1;YI RADICAL GA;So;0;ON;;;;;N;;;;;
+A4A2;YI RADICAL ZUP;So;0;ON;;;;;N;;;;;
+A4A3;YI RADICAL CYT;So;0;ON;;;;;N;;;;;
+A4A4;YI RADICAL DDUR;So;0;ON;;;;;N;;;;;
+A4A5;YI RADICAL BUR;So;0;ON;;;;;N;;;;;
+A4A6;YI RADICAL GGUO;So;0;ON;;;;;N;;;;;
+A4A7;YI RADICAL NYOP;So;0;ON;;;;;N;;;;;
+A4A8;YI RADICAL TU;So;0;ON;;;;;N;;;;;
+A4A9;YI RADICAL OP;So;0;ON;;;;;N;;;;;
+A4AA;YI RADICAL JJUT;So;0;ON;;;;;N;;;;;
+A4AB;YI RADICAL ZOT;So;0;ON;;;;;N;;;;;
+A4AC;YI RADICAL PYT;So;0;ON;;;;;N;;;;;
+A4AD;YI RADICAL HMO;So;0;ON;;;;;N;;;;;
+A4AE;YI RADICAL YIT;So;0;ON;;;;;N;;;;;
+A4AF;YI RADICAL VUR;So;0;ON;;;;;N;;;;;
+A4B0;YI RADICAL SHY;So;0;ON;;;;;N;;;;;
+A4B1;YI RADICAL VEP;So;0;ON;;;;;N;;;;;
+A4B2;YI RADICAL ZA;So;0;ON;;;;;N;;;;;
+A4B3;YI RADICAL JO;So;0;ON;;;;;N;;;;;
+A4B4;YI RADICAL NZUP;So;0;ON;;;;;N;;;;;
+A4B5;YI RADICAL JJY;So;0;ON;;;;;N;;;;;
+A4B6;YI RADICAL GOT;So;0;ON;;;;;N;;;;;
+A4B7;YI RADICAL JJIE;So;0;ON;;;;;N;;;;;
+A4B8;YI RADICAL WO;So;0;ON;;;;;N;;;;;
+A4B9;YI RADICAL DU;So;0;ON;;;;;N;;;;;
+A4BA;YI RADICAL SHUR;So;0;ON;;;;;N;;;;;
+A4BB;YI RADICAL LIE;So;0;ON;;;;;N;;;;;
+A4BC;YI RADICAL CY;So;0;ON;;;;;N;;;;;
+A4BD;YI RADICAL CUOP;So;0;ON;;;;;N;;;;;
+A4BE;YI RADICAL CIP;So;0;ON;;;;;N;;;;;
+A4BF;YI RADICAL HXOP;So;0;ON;;;;;N;;;;;
+A4C0;YI RADICAL SHAT;So;0;ON;;;;;N;;;;;
+A4C1;YI RADICAL ZUR;So;0;ON;;;;;N;;;;;
+A4C2;YI RADICAL SHOP;So;0;ON;;;;;N;;;;;
+A4C3;YI RADICAL CHE;So;0;ON;;;;;N;;;;;
+A4C4;YI RADICAL ZZIET;So;0;ON;;;;;N;;;;;
+A4C5;YI RADICAL NBIE;So;0;ON;;;;;N;;;;;
+A4C6;YI RADICAL KE;So;0;ON;;;;;N;;;;;
+AC00;<Hangul Syllable, First>;Lo;0;L;;;;;N;;;;;
+D7A3;<Hangul Syllable, Last>;Lo;0;L;;;;;N;;;;;
+D800;<Non Private Use High Surrogate, First>;Cs;0;L;;;;;N;;;;;
+DB7F;<Non Private Use High Surrogate, Last>;Cs;0;L;;;;;N;;;;;
+DB80;<Private Use High Surrogate, First>;Cs;0;L;;;;;N;;;;;
+DBFF;<Private Use High Surrogate, Last>;Cs;0;L;;;;;N;;;;;
+DC00;<Low Surrogate, First>;Cs;0;L;;;;;N;;;;;
+DFFF;<Low Surrogate, Last>;Cs;0;L;;;;;N;;;;;
+E000;<Private Use, First>;Co;0;L;;;;;N;;;;;
+F8FF;<Private Use, Last>;Co;0;L;;;;;N;;;;;
+F900;CJK COMPATIBILITY IDEOGRAPH-F900;Lo;0;L;8C48;;;;N;;;;;
+F901;CJK COMPATIBILITY IDEOGRAPH-F901;Lo;0;L;66F4;;;;N;;;;;
+F902;CJK COMPATIBILITY IDEOGRAPH-F902;Lo;0;L;8ECA;;;;N;;;;;
+F903;CJK COMPATIBILITY IDEOGRAPH-F903;Lo;0;L;8CC8;;;;N;;;;;
+F904;CJK COMPATIBILITY IDEOGRAPH-F904;Lo;0;L;6ED1;;;;N;;;;;
+F905;CJK COMPATIBILITY IDEOGRAPH-F905;Lo;0;L;4E32;;;;N;;;;;
+F906;CJK COMPATIBILITY IDEOGRAPH-F906;Lo;0;L;53E5;;;;N;;;;;
+F907;CJK COMPATIBILITY IDEOGRAPH-F907;Lo;0;L;9F9C;;;;N;;;;;
+F908;CJK COMPATIBILITY IDEOGRAPH-F908;Lo;0;L;9F9C;;;;N;;;;;
+F909;CJK COMPATIBILITY IDEOGRAPH-F909;Lo;0;L;5951;;;;N;;;;;
+F90A;CJK COMPATIBILITY IDEOGRAPH-F90A;Lo;0;L;91D1;;;;N;;;;;
+F90B;CJK COMPATIBILITY IDEOGRAPH-F90B;Lo;0;L;5587;;;;N;;;;;
+F90C;CJK COMPATIBILITY IDEOGRAPH-F90C;Lo;0;L;5948;;;;N;;;;;
+F90D;CJK COMPATIBILITY IDEOGRAPH-F90D;Lo;0;L;61F6;;;;N;;;;;
+F90E;CJK COMPATIBILITY IDEOGRAPH-F90E;Lo;0;L;7669;;;;N;;;;;
+F90F;CJK COMPATIBILITY IDEOGRAPH-F90F;Lo;0;L;7F85;;;;N;;;;;
+F910;CJK COMPATIBILITY IDEOGRAPH-F910;Lo;0;L;863F;;;;N;;;;;
+F911;CJK COMPATIBILITY IDEOGRAPH-F911;Lo;0;L;87BA;;;;N;;;;;
+F912;CJK COMPATIBILITY IDEOGRAPH-F912;Lo;0;L;88F8;;;;N;;;;;
+F913;CJK COMPATIBILITY IDEOGRAPH-F913;Lo;0;L;908F;;;;N;;;;;
+F914;CJK COMPATIBILITY IDEOGRAPH-F914;Lo;0;L;6A02;;;;N;;;;;
+F915;CJK COMPATIBILITY IDEOGRAPH-F915;Lo;0;L;6D1B;;;;N;;;;;
+F916;CJK COMPATIBILITY IDEOGRAPH-F916;Lo;0;L;70D9;;;;N;;;;;
+F917;CJK COMPATIBILITY IDEOGRAPH-F917;Lo;0;L;73DE;;;;N;;;;;
+F918;CJK COMPATIBILITY IDEOGRAPH-F918;Lo;0;L;843D;;;;N;;;;;
+F919;CJK COMPATIBILITY IDEOGRAPH-F919;Lo;0;L;916A;;;;N;;;;;
+F91A;CJK COMPATIBILITY IDEOGRAPH-F91A;Lo;0;L;99F1;;;;N;;;;;
+F91B;CJK COMPATIBILITY IDEOGRAPH-F91B;Lo;0;L;4E82;;;;N;;;;;
+F91C;CJK COMPATIBILITY IDEOGRAPH-F91C;Lo;0;L;5375;;;;N;;;;;
+F91D;CJK COMPATIBILITY IDEOGRAPH-F91D;Lo;0;L;6B04;;;;N;;;;;
+F91E;CJK COMPATIBILITY IDEOGRAPH-F91E;Lo;0;L;721B;;;;N;;;;;
+F91F;CJK COMPATIBILITY IDEOGRAPH-F91F;Lo;0;L;862D;;;;N;;;;;
+F920;CJK COMPATIBILITY IDEOGRAPH-F920;Lo;0;L;9E1E;;;;N;;;;;
+F921;CJK COMPATIBILITY IDEOGRAPH-F921;Lo;0;L;5D50;;;;N;;;;;
+F922;CJK COMPATIBILITY IDEOGRAPH-F922;Lo;0;L;6FEB;;;;N;;;;;
+F923;CJK COMPATIBILITY IDEOGRAPH-F923;Lo;0;L;85CD;;;;N;;;;;
+F924;CJK COMPATIBILITY IDEOGRAPH-F924;Lo;0;L;8964;;;;N;;;;;
+F925;CJK COMPATIBILITY IDEOGRAPH-F925;Lo;0;L;62C9;;;;N;;;;;
+F926;CJK COMPATIBILITY IDEOGRAPH-F926;Lo;0;L;81D8;;;;N;;;;;
+F927;CJK COMPATIBILITY IDEOGRAPH-F927;Lo;0;L;881F;;;;N;;;;;
+F928;CJK COMPATIBILITY IDEOGRAPH-F928;Lo;0;L;5ECA;;;;N;;;;;
+F929;CJK COMPATIBILITY IDEOGRAPH-F929;Lo;0;L;6717;;;;N;;;;;
+F92A;CJK COMPATIBILITY IDEOGRAPH-F92A;Lo;0;L;6D6A;;;;N;;;;;
+F92B;CJK COMPATIBILITY IDEOGRAPH-F92B;Lo;0;L;72FC;;;;N;;;;;
+F92C;CJK COMPATIBILITY IDEOGRAPH-F92C;Lo;0;L;90CE;;;;N;;;;;
+F92D;CJK COMPATIBILITY IDEOGRAPH-F92D;Lo;0;L;4F86;;;;N;;;;;
+F92E;CJK COMPATIBILITY IDEOGRAPH-F92E;Lo;0;L;51B7;;;;N;;;;;
+F92F;CJK COMPATIBILITY IDEOGRAPH-F92F;Lo;0;L;52DE;;;;N;;;;;
+F930;CJK COMPATIBILITY IDEOGRAPH-F930;Lo;0;L;64C4;;;;N;;;;;
+F931;CJK COMPATIBILITY IDEOGRAPH-F931;Lo;0;L;6AD3;;;;N;;;;;
+F932;CJK COMPATIBILITY IDEOGRAPH-F932;Lo;0;L;7210;;;;N;;;;;
+F933;CJK COMPATIBILITY IDEOGRAPH-F933;Lo;0;L;76E7;;;;N;;;;;
+F934;CJK COMPATIBILITY IDEOGRAPH-F934;Lo;0;L;8001;;;;N;;;;;
+F935;CJK COMPATIBILITY IDEOGRAPH-F935;Lo;0;L;8606;;;;N;;;;;
+F936;CJK COMPATIBILITY IDEOGRAPH-F936;Lo;0;L;865C;;;;N;;;;;
+F937;CJK COMPATIBILITY IDEOGRAPH-F937;Lo;0;L;8DEF;;;;N;;;;;
+F938;CJK COMPATIBILITY IDEOGRAPH-F938;Lo;0;L;9732;;;;N;;;;;
+F939;CJK COMPATIBILITY IDEOGRAPH-F939;Lo;0;L;9B6F;;;;N;;;;;
+F93A;CJK COMPATIBILITY IDEOGRAPH-F93A;Lo;0;L;9DFA;;;;N;;;;;
+F93B;CJK COMPATIBILITY IDEOGRAPH-F93B;Lo;0;L;788C;;;;N;;;;;
+F93C;CJK COMPATIBILITY IDEOGRAPH-F93C;Lo;0;L;797F;;;;N;;;;;
+F93D;CJK COMPATIBILITY IDEOGRAPH-F93D;Lo;0;L;7DA0;;;;N;;;;;
+F93E;CJK COMPATIBILITY IDEOGRAPH-F93E;Lo;0;L;83C9;;;;N;;;;;
+F93F;CJK COMPATIBILITY IDEOGRAPH-F93F;Lo;0;L;9304;;;;N;;;;;
+F940;CJK COMPATIBILITY IDEOGRAPH-F940;Lo;0;L;9E7F;;;;N;;;;;
+F941;CJK COMPATIBILITY IDEOGRAPH-F941;Lo;0;L;8AD6;;;;N;;;;;
+F942;CJK COMPATIBILITY IDEOGRAPH-F942;Lo;0;L;58DF;;;;N;;;;;
+F943;CJK COMPATIBILITY IDEOGRAPH-F943;Lo;0;L;5F04;;;;N;;;;;
+F944;CJK COMPATIBILITY IDEOGRAPH-F944;Lo;0;L;7C60;;;;N;;;;;
+F945;CJK COMPATIBILITY IDEOGRAPH-F945;Lo;0;L;807E;;;;N;;;;;
+F946;CJK COMPATIBILITY IDEOGRAPH-F946;Lo;0;L;7262;;;;N;;;;;
+F947;CJK COMPATIBILITY IDEOGRAPH-F947;Lo;0;L;78CA;;;;N;;;;;
+F948;CJK COMPATIBILITY IDEOGRAPH-F948;Lo;0;L;8CC2;;;;N;;;;;
+F949;CJK COMPATIBILITY IDEOGRAPH-F949;Lo;0;L;96F7;;;;N;;;;;
+F94A;CJK COMPATIBILITY IDEOGRAPH-F94A;Lo;0;L;58D8;;;;N;;;;;
+F94B;CJK COMPATIBILITY IDEOGRAPH-F94B;Lo;0;L;5C62;;;;N;;;;;
+F94C;CJK COMPATIBILITY IDEOGRAPH-F94C;Lo;0;L;6A13;;;;N;;;;;
+F94D;CJK COMPATIBILITY IDEOGRAPH-F94D;Lo;0;L;6DDA;;;;N;;;;;
+F94E;CJK COMPATIBILITY IDEOGRAPH-F94E;Lo;0;L;6F0F;;;;N;;;;;
+F94F;CJK COMPATIBILITY IDEOGRAPH-F94F;Lo;0;L;7D2F;;;;N;;;;;
+F950;CJK COMPATIBILITY IDEOGRAPH-F950;Lo;0;L;7E37;;;;N;;;;;
+F951;CJK COMPATIBILITY IDEOGRAPH-F951;Lo;0;L;964B;;;;N;;;;;
+F952;CJK COMPATIBILITY IDEOGRAPH-F952;Lo;0;L;52D2;;;;N;;;;;
+F953;CJK COMPATIBILITY IDEOGRAPH-F953;Lo;0;L;808B;;;;N;;;;;
+F954;CJK COMPATIBILITY IDEOGRAPH-F954;Lo;0;L;51DC;;;;N;;;;;
+F955;CJK COMPATIBILITY IDEOGRAPH-F955;Lo;0;L;51CC;;;;N;;;;;
+F956;CJK COMPATIBILITY IDEOGRAPH-F956;Lo;0;L;7A1C;;;;N;;;;;
+F957;CJK COMPATIBILITY IDEOGRAPH-F957;Lo;0;L;7DBE;;;;N;;;;;
+F958;CJK COMPATIBILITY IDEOGRAPH-F958;Lo;0;L;83F1;;;;N;;;;;
+F959;CJK COMPATIBILITY IDEOGRAPH-F959;Lo;0;L;9675;;;;N;;;;;
+F95A;CJK COMPATIBILITY IDEOGRAPH-F95A;Lo;0;L;8B80;;;;N;;;;;
+F95B;CJK COMPATIBILITY IDEOGRAPH-F95B;Lo;0;L;62CF;;;;N;;;;;
+F95C;CJK COMPATIBILITY IDEOGRAPH-F95C;Lo;0;L;6A02;;;;N;;;;;
+F95D;CJK COMPATIBILITY IDEOGRAPH-F95D;Lo;0;L;8AFE;;;;N;;;;;
+F95E;CJK COMPATIBILITY IDEOGRAPH-F95E;Lo;0;L;4E39;;;;N;;;;;
+F95F;CJK COMPATIBILITY IDEOGRAPH-F95F;Lo;0;L;5BE7;;;;N;;;;;
+F960;CJK COMPATIBILITY IDEOGRAPH-F960;Lo;0;L;6012;;;;N;;;;;
+F961;CJK COMPATIBILITY IDEOGRAPH-F961;Lo;0;L;7387;;;;N;;;;;
+F962;CJK COMPATIBILITY IDEOGRAPH-F962;Lo;0;L;7570;;;;N;;;;;
+F963;CJK COMPATIBILITY IDEOGRAPH-F963;Lo;0;L;5317;;;;N;;;;;
+F964;CJK COMPATIBILITY IDEOGRAPH-F964;Lo;0;L;78FB;;;;N;;;;;
+F965;CJK COMPATIBILITY IDEOGRAPH-F965;Lo;0;L;4FBF;;;;N;;;;;
+F966;CJK COMPATIBILITY IDEOGRAPH-F966;Lo;0;L;5FA9;;;;N;;;;;
+F967;CJK COMPATIBILITY IDEOGRAPH-F967;Lo;0;L;4E0D;;;;N;;;;;
+F968;CJK COMPATIBILITY IDEOGRAPH-F968;Lo;0;L;6CCC;;;;N;;;;;
+F969;CJK COMPATIBILITY IDEOGRAPH-F969;Lo;0;L;6578;;;;N;;;;;
+F96A;CJK COMPATIBILITY IDEOGRAPH-F96A;Lo;0;L;7D22;;;;N;;;;;
+F96B;CJK COMPATIBILITY IDEOGRAPH-F96B;Lo;0;L;53C3;;;;N;;;;;
+F96C;CJK COMPATIBILITY IDEOGRAPH-F96C;Lo;0;L;585E;;;;N;;;;;
+F96D;CJK COMPATIBILITY IDEOGRAPH-F96D;Lo;0;L;7701;;;;N;;;;;
+F96E;CJK COMPATIBILITY IDEOGRAPH-F96E;Lo;0;L;8449;;;;N;;;;;
+F96F;CJK COMPATIBILITY IDEOGRAPH-F96F;Lo;0;L;8AAA;;;;N;;;;;
+F970;CJK COMPATIBILITY IDEOGRAPH-F970;Lo;0;L;6BBA;;;;N;;;;;
+F971;CJK COMPATIBILITY IDEOGRAPH-F971;Lo;0;L;8FB0;;;;N;;;;;
+F972;CJK COMPATIBILITY IDEOGRAPH-F972;Lo;0;L;6C88;;;;N;;;;;
+F973;CJK COMPATIBILITY IDEOGRAPH-F973;Lo;0;L;62FE;;;;N;;;;;
+F974;CJK COMPATIBILITY IDEOGRAPH-F974;Lo;0;L;82E5;;;;N;;;;;
+F975;CJK COMPATIBILITY IDEOGRAPH-F975;Lo;0;L;63A0;;;;N;;;;;
+F976;CJK COMPATIBILITY IDEOGRAPH-F976;Lo;0;L;7565;;;;N;;;;;
+F977;CJK COMPATIBILITY IDEOGRAPH-F977;Lo;0;L;4EAE;;;;N;;;;;
+F978;CJK COMPATIBILITY IDEOGRAPH-F978;Lo;0;L;5169;;;;N;;;;;
+F979;CJK COMPATIBILITY IDEOGRAPH-F979;Lo;0;L;51C9;;;;N;;;;;
+F97A;CJK COMPATIBILITY IDEOGRAPH-F97A;Lo;0;L;6881;;;;N;;;;;
+F97B;CJK COMPATIBILITY IDEOGRAPH-F97B;Lo;0;L;7CE7;;;;N;;;;;
+F97C;CJK COMPATIBILITY IDEOGRAPH-F97C;Lo;0;L;826F;;;;N;;;;;
+F97D;CJK COMPATIBILITY IDEOGRAPH-F97D;Lo;0;L;8AD2;;;;N;;;;;
+F97E;CJK COMPATIBILITY IDEOGRAPH-F97E;Lo;0;L;91CF;;;;N;;;;;
+F97F;CJK COMPATIBILITY IDEOGRAPH-F97F;Lo;0;L;52F5;;;;N;;;;;
+F980;CJK COMPATIBILITY IDEOGRAPH-F980;Lo;0;L;5442;;;;N;;;;;
+F981;CJK COMPATIBILITY IDEOGRAPH-F981;Lo;0;L;5973;;;;N;;;;;
+F982;CJK COMPATIBILITY IDEOGRAPH-F982;Lo;0;L;5EEC;;;;N;;;;;
+F983;CJK COMPATIBILITY IDEOGRAPH-F983;Lo;0;L;65C5;;;;N;;;;;
+F984;CJK COMPATIBILITY IDEOGRAPH-F984;Lo;0;L;6FFE;;;;N;;;;;
+F985;CJK COMPATIBILITY IDEOGRAPH-F985;Lo;0;L;792A;;;;N;;;;;
+F986;CJK COMPATIBILITY IDEOGRAPH-F986;Lo;0;L;95AD;;;;N;;;;;
+F987;CJK COMPATIBILITY IDEOGRAPH-F987;Lo;0;L;9A6A;;;;N;;;;;
+F988;CJK COMPATIBILITY IDEOGRAPH-F988;Lo;0;L;9E97;;;;N;;;;;
+F989;CJK COMPATIBILITY IDEOGRAPH-F989;Lo;0;L;9ECE;;;;N;;;;;
+F98A;CJK COMPATIBILITY IDEOGRAPH-F98A;Lo;0;L;529B;;;;N;;;;;
+F98B;CJK COMPATIBILITY IDEOGRAPH-F98B;Lo;0;L;66C6;;;;N;;;;;
+F98C;CJK COMPATIBILITY IDEOGRAPH-F98C;Lo;0;L;6B77;;;;N;;;;;
+F98D;CJK COMPATIBILITY IDEOGRAPH-F98D;Lo;0;L;8F62;;;;N;;;;;
+F98E;CJK COMPATIBILITY IDEOGRAPH-F98E;Lo;0;L;5E74;;;;N;;;;;
+F98F;CJK COMPATIBILITY IDEOGRAPH-F98F;Lo;0;L;6190;;;;N;;;;;
+F990;CJK COMPATIBILITY IDEOGRAPH-F990;Lo;0;L;6200;;;;N;;;;;
+F991;CJK COMPATIBILITY IDEOGRAPH-F991;Lo;0;L;649A;;;;N;;;;;
+F992;CJK COMPATIBILITY IDEOGRAPH-F992;Lo;0;L;6F23;;;;N;;;;;
+F993;CJK COMPATIBILITY IDEOGRAPH-F993;Lo;0;L;7149;;;;N;;;;;
+F994;CJK COMPATIBILITY IDEOGRAPH-F994;Lo;0;L;7489;;;;N;;;;;
+F995;CJK COMPATIBILITY IDEOGRAPH-F995;Lo;0;L;79CA;;;;N;;;;;
+F996;CJK COMPATIBILITY IDEOGRAPH-F996;Lo;0;L;7DF4;;;;N;;;;;
+F997;CJK COMPATIBILITY IDEOGRAPH-F997;Lo;0;L;806F;;;;N;;;;;
+F998;CJK COMPATIBILITY IDEOGRAPH-F998;Lo;0;L;8F26;;;;N;;;;;
+F999;CJK COMPATIBILITY IDEOGRAPH-F999;Lo;0;L;84EE;;;;N;;;;;
+F99A;CJK COMPATIBILITY IDEOGRAPH-F99A;Lo;0;L;9023;;;;N;;;;;
+F99B;CJK COMPATIBILITY IDEOGRAPH-F99B;Lo;0;L;934A;;;;N;;;;;
+F99C;CJK COMPATIBILITY IDEOGRAPH-F99C;Lo;0;L;5217;;;;N;;;;;
+F99D;CJK COMPATIBILITY IDEOGRAPH-F99D;Lo;0;L;52A3;;;;N;;;;;
+F99E;CJK COMPATIBILITY IDEOGRAPH-F99E;Lo;0;L;54BD;;;;N;;;;;
+F99F;CJK COMPATIBILITY IDEOGRAPH-F99F;Lo;0;L;70C8;;;;N;;;;;
+F9A0;CJK COMPATIBILITY IDEOGRAPH-F9A0;Lo;0;L;88C2;;;;N;;;;;
+F9A1;CJK COMPATIBILITY IDEOGRAPH-F9A1;Lo;0;L;8AAA;;;;N;;;;;
+F9A2;CJK COMPATIBILITY IDEOGRAPH-F9A2;Lo;0;L;5EC9;;;;N;;;;;
+F9A3;CJK COMPATIBILITY IDEOGRAPH-F9A3;Lo;0;L;5FF5;;;;N;;;;;
+F9A4;CJK COMPATIBILITY IDEOGRAPH-F9A4;Lo;0;L;637B;;;;N;;;;;
+F9A5;CJK COMPATIBILITY IDEOGRAPH-F9A5;Lo;0;L;6BAE;;;;N;;;;;
+F9A6;CJK COMPATIBILITY IDEOGRAPH-F9A6;Lo;0;L;7C3E;;;;N;;;;;
+F9A7;CJK COMPATIBILITY IDEOGRAPH-F9A7;Lo;0;L;7375;;;;N;;;;;
+F9A8;CJK COMPATIBILITY IDEOGRAPH-F9A8;Lo;0;L;4EE4;;;;N;;;;;
+F9A9;CJK COMPATIBILITY IDEOGRAPH-F9A9;Lo;0;L;56F9;;;;N;;;;;
+F9AA;CJK COMPATIBILITY IDEOGRAPH-F9AA;Lo;0;L;5BE7;;;;N;;;;;
+F9AB;CJK COMPATIBILITY IDEOGRAPH-F9AB;Lo;0;L;5DBA;;;;N;;;;;
+F9AC;CJK COMPATIBILITY IDEOGRAPH-F9AC;Lo;0;L;601C;;;;N;;;;;
+F9AD;CJK COMPATIBILITY IDEOGRAPH-F9AD;Lo;0;L;73B2;;;;N;;;;;
+F9AE;CJK COMPATIBILITY IDEOGRAPH-F9AE;Lo;0;L;7469;;;;N;;;;;
+F9AF;CJK COMPATIBILITY IDEOGRAPH-F9AF;Lo;0;L;7F9A;;;;N;;;;;
+F9B0;CJK COMPATIBILITY IDEOGRAPH-F9B0;Lo;0;L;8046;;;;N;;;;;
+F9B1;CJK COMPATIBILITY IDEOGRAPH-F9B1;Lo;0;L;9234;;;;N;;;;;
+F9B2;CJK COMPATIBILITY IDEOGRAPH-F9B2;Lo;0;L;96F6;;;;N;;;;;
+F9B3;CJK COMPATIBILITY IDEOGRAPH-F9B3;Lo;0;L;9748;;;;N;;;;;
+F9B4;CJK COMPATIBILITY IDEOGRAPH-F9B4;Lo;0;L;9818;;;;N;;;;;
+F9B5;CJK COMPATIBILITY IDEOGRAPH-F9B5;Lo;0;L;4F8B;;;;N;;;;;
+F9B6;CJK COMPATIBILITY IDEOGRAPH-F9B6;Lo;0;L;79AE;;;;N;;;;;
+F9B7;CJK COMPATIBILITY IDEOGRAPH-F9B7;Lo;0;L;91B4;;;;N;;;;;
+F9B8;CJK COMPATIBILITY IDEOGRAPH-F9B8;Lo;0;L;96B8;;;;N;;;;;
+F9B9;CJK COMPATIBILITY IDEOGRAPH-F9B9;Lo;0;L;60E1;;;;N;;;;;
+F9BA;CJK COMPATIBILITY IDEOGRAPH-F9BA;Lo;0;L;4E86;;;;N;;;;;
+F9BB;CJK COMPATIBILITY IDEOGRAPH-F9BB;Lo;0;L;50DA;;;;N;;;;;
+F9BC;CJK COMPATIBILITY IDEOGRAPH-F9BC;Lo;0;L;5BEE;;;;N;;;;;
+F9BD;CJK COMPATIBILITY IDEOGRAPH-F9BD;Lo;0;L;5C3F;;;;N;;;;;
+F9BE;CJK COMPATIBILITY IDEOGRAPH-F9BE;Lo;0;L;6599;;;;N;;;;;
+F9BF;CJK COMPATIBILITY IDEOGRAPH-F9BF;Lo;0;L;6A02;;;;N;;;;;
+F9C0;CJK COMPATIBILITY IDEOGRAPH-F9C0;Lo;0;L;71CE;;;;N;;;;;
+F9C1;CJK COMPATIBILITY IDEOGRAPH-F9C1;Lo;0;L;7642;;;;N;;;;;
+F9C2;CJK COMPATIBILITY IDEOGRAPH-F9C2;Lo;0;L;84FC;;;;N;;;;;
+F9C3;CJK COMPATIBILITY IDEOGRAPH-F9C3;Lo;0;L;907C;;;;N;;;;;
+F9C4;CJK COMPATIBILITY IDEOGRAPH-F9C4;Lo;0;L;9F8D;;;;N;;;;;
+F9C5;CJK COMPATIBILITY IDEOGRAPH-F9C5;Lo;0;L;6688;;;;N;;;;;
+F9C6;CJK COMPATIBILITY IDEOGRAPH-F9C6;Lo;0;L;962E;;;;N;;;;;
+F9C7;CJK COMPATIBILITY IDEOGRAPH-F9C7;Lo;0;L;5289;;;;N;;;;;
+F9C8;CJK COMPATIBILITY IDEOGRAPH-F9C8;Lo;0;L;677B;;;;N;;;;;
+F9C9;CJK COMPATIBILITY IDEOGRAPH-F9C9;Lo;0;L;67F3;;;;N;;;;;
+F9CA;CJK COMPATIBILITY IDEOGRAPH-F9CA;Lo;0;L;6D41;;;;N;;;;;
+F9CB;CJK COMPATIBILITY IDEOGRAPH-F9CB;Lo;0;L;6E9C;;;;N;;;;;
+F9CC;CJK COMPATIBILITY IDEOGRAPH-F9CC;Lo;0;L;7409;;;;N;;;;;
+F9CD;CJK COMPATIBILITY IDEOGRAPH-F9CD;Lo;0;L;7559;;;;N;;;;;
+F9CE;CJK COMPATIBILITY IDEOGRAPH-F9CE;Lo;0;L;786B;;;;N;;;;;
+F9CF;CJK COMPATIBILITY IDEOGRAPH-F9CF;Lo;0;L;7D10;;;;N;;;;;
+F9D0;CJK COMPATIBILITY IDEOGRAPH-F9D0;Lo;0;L;985E;;;;N;;;;;
+F9D1;CJK COMPATIBILITY IDEOGRAPH-F9D1;Lo;0;L;516D;;;;N;;;;;
+F9D2;CJK COMPATIBILITY IDEOGRAPH-F9D2;Lo;0;L;622E;;;;N;;;;;
+F9D3;CJK COMPATIBILITY IDEOGRAPH-F9D3;Lo;0;L;9678;;;;N;;;;;
+F9D4;CJK COMPATIBILITY IDEOGRAPH-F9D4;Lo;0;L;502B;;;;N;;;;;
+F9D5;CJK COMPATIBILITY IDEOGRAPH-F9D5;Lo;0;L;5D19;;;;N;;;;;
+F9D6;CJK COMPATIBILITY IDEOGRAPH-F9D6;Lo;0;L;6DEA;;;;N;;;;;
+F9D7;CJK COMPATIBILITY IDEOGRAPH-F9D7;Lo;0;L;8F2A;;;;N;;;;;
+F9D8;CJK COMPATIBILITY IDEOGRAPH-F9D8;Lo;0;L;5F8B;;;;N;;;;;
+F9D9;CJK COMPATIBILITY IDEOGRAPH-F9D9;Lo;0;L;6144;;;;N;;;;;
+F9DA;CJK COMPATIBILITY IDEOGRAPH-F9DA;Lo;0;L;6817;;;;N;;;;;
+F9DB;CJK COMPATIBILITY IDEOGRAPH-F9DB;Lo;0;L;7387;;;;N;;;;;
+F9DC;CJK COMPATIBILITY IDEOGRAPH-F9DC;Lo;0;L;9686;;;;N;;;;;
+F9DD;CJK COMPATIBILITY IDEOGRAPH-F9DD;Lo;0;L;5229;;;;N;;;;;
+F9DE;CJK COMPATIBILITY IDEOGRAPH-F9DE;Lo;0;L;540F;;;;N;;;;;
+F9DF;CJK COMPATIBILITY IDEOGRAPH-F9DF;Lo;0;L;5C65;;;;N;;;;;
+F9E0;CJK COMPATIBILITY IDEOGRAPH-F9E0;Lo;0;L;6613;;;;N;;;;;
+F9E1;CJK COMPATIBILITY IDEOGRAPH-F9E1;Lo;0;L;674E;;;;N;;;;;
+F9E2;CJK COMPATIBILITY IDEOGRAPH-F9E2;Lo;0;L;68A8;;;;N;;;;;
+F9E3;CJK COMPATIBILITY IDEOGRAPH-F9E3;Lo;0;L;6CE5;;;;N;;;;;
+F9E4;CJK COMPATIBILITY IDEOGRAPH-F9E4;Lo;0;L;7406;;;;N;;;;;
+F9E5;CJK COMPATIBILITY IDEOGRAPH-F9E5;Lo;0;L;75E2;;;;N;;;;;
+F9E6;CJK COMPATIBILITY IDEOGRAPH-F9E6;Lo;0;L;7F79;;;;N;;;;;
+F9E7;CJK COMPATIBILITY IDEOGRAPH-F9E7;Lo;0;L;88CF;;;;N;;;;;
+F9E8;CJK COMPATIBILITY IDEOGRAPH-F9E8;Lo;0;L;88E1;;;;N;;;;;
+F9E9;CJK COMPATIBILITY IDEOGRAPH-F9E9;Lo;0;L;91CC;;;;N;;;;;
+F9EA;CJK COMPATIBILITY IDEOGRAPH-F9EA;Lo;0;L;96E2;;;;N;;;;;
+F9EB;CJK COMPATIBILITY IDEOGRAPH-F9EB;Lo;0;L;533F;;;;N;;;;;
+F9EC;CJK COMPATIBILITY IDEOGRAPH-F9EC;Lo;0;L;6EBA;;;;N;;;;;
+F9ED;CJK COMPATIBILITY IDEOGRAPH-F9ED;Lo;0;L;541D;;;;N;;;;;
+F9EE;CJK COMPATIBILITY IDEOGRAPH-F9EE;Lo;0;L;71D0;;;;N;;;;;
+F9EF;CJK COMPATIBILITY IDEOGRAPH-F9EF;Lo;0;L;7498;;;;N;;;;;
+F9F0;CJK COMPATIBILITY IDEOGRAPH-F9F0;Lo;0;L;85FA;;;;N;;;;;
+F9F1;CJK COMPATIBILITY IDEOGRAPH-F9F1;Lo;0;L;96A3;;;;N;;;;;
+F9F2;CJK COMPATIBILITY IDEOGRAPH-F9F2;Lo;0;L;9C57;;;;N;;;;;
+F9F3;CJK COMPATIBILITY IDEOGRAPH-F9F3;Lo;0;L;9E9F;;;;N;;;;;
+F9F4;CJK COMPATIBILITY IDEOGRAPH-F9F4;Lo;0;L;6797;;;;N;;;;;
+F9F5;CJK COMPATIBILITY IDEOGRAPH-F9F5;Lo;0;L;6DCB;;;;N;;;;;
+F9F6;CJK COMPATIBILITY IDEOGRAPH-F9F6;Lo;0;L;81E8;;;;N;;;;;
+F9F7;CJK COMPATIBILITY IDEOGRAPH-F9F7;Lo;0;L;7ACB;;;;N;;;;;
+F9F8;CJK COMPATIBILITY IDEOGRAPH-F9F8;Lo;0;L;7B20;;;;N;;;;;
+F9F9;CJK COMPATIBILITY IDEOGRAPH-F9F9;Lo;0;L;7C92;;;;N;;;;;
+F9FA;CJK COMPATIBILITY IDEOGRAPH-F9FA;Lo;0;L;72C0;;;;N;;;;;
+F9FB;CJK COMPATIBILITY IDEOGRAPH-F9FB;Lo;0;L;7099;;;;N;;;;;
+F9FC;CJK COMPATIBILITY IDEOGRAPH-F9FC;Lo;0;L;8B58;;;;N;;;;;
+F9FD;CJK COMPATIBILITY IDEOGRAPH-F9FD;Lo;0;L;4EC0;;;;N;;;;;
+F9FE;CJK COMPATIBILITY IDEOGRAPH-F9FE;Lo;0;L;8336;;;;N;;;;;
+F9FF;CJK COMPATIBILITY IDEOGRAPH-F9FF;Lo;0;L;523A;;;;N;;;;;
+FA00;CJK COMPATIBILITY IDEOGRAPH-FA00;Lo;0;L;5207;;;;N;;;;;
+FA01;CJK COMPATIBILITY IDEOGRAPH-FA01;Lo;0;L;5EA6;;;;N;;;;;
+FA02;CJK COMPATIBILITY IDEOGRAPH-FA02;Lo;0;L;62D3;;;;N;;;;;
+FA03;CJK COMPATIBILITY IDEOGRAPH-FA03;Lo;0;L;7CD6;;;;N;;;;;
+FA04;CJK COMPATIBILITY IDEOGRAPH-FA04;Lo;0;L;5B85;;;;N;;;;;
+FA05;CJK COMPATIBILITY IDEOGRAPH-FA05;Lo;0;L;6D1E;;;;N;;;;;
+FA06;CJK COMPATIBILITY IDEOGRAPH-FA06;Lo;0;L;66B4;;;;N;;;;;
+FA07;CJK COMPATIBILITY IDEOGRAPH-FA07;Lo;0;L;8F3B;;;;N;;;;;
+FA08;CJK COMPATIBILITY IDEOGRAPH-FA08;Lo;0;L;884C;;;;N;;;;;
+FA09;CJK COMPATIBILITY IDEOGRAPH-FA09;Lo;0;L;964D;;;;N;;;;;
+FA0A;CJK COMPATIBILITY IDEOGRAPH-FA0A;Lo;0;L;898B;;;;N;;;;;
+FA0B;CJK COMPATIBILITY IDEOGRAPH-FA0B;Lo;0;L;5ED3;;;;N;;;;;
+FA0C;CJK COMPATIBILITY IDEOGRAPH-FA0C;Lo;0;L;5140;;;;N;;;;;
+FA0D;CJK COMPATIBILITY IDEOGRAPH-FA0D;Lo;0;L;55C0;;;;N;;;;;
+FA0E;CJK COMPATIBILITY IDEOGRAPH-FA0E;Lo;0;L;;;;;N;;;;;
+FA0F;CJK COMPATIBILITY IDEOGRAPH-FA0F;Lo;0;L;;;;;N;;;;;
+FA10;CJK COMPATIBILITY IDEOGRAPH-FA10;Lo;0;L;585A;;;;N;;;;;
+FA11;CJK COMPATIBILITY IDEOGRAPH-FA11;Lo;0;L;;;;;N;;;;;
+FA12;CJK COMPATIBILITY IDEOGRAPH-FA12;Lo;0;L;6674;;;;N;;;;;
+FA13;CJK COMPATIBILITY IDEOGRAPH-FA13;Lo;0;L;;;;;N;;;;;
+FA14;CJK COMPATIBILITY IDEOGRAPH-FA14;Lo;0;L;;;;;N;;;;;
+FA15;CJK COMPATIBILITY IDEOGRAPH-FA15;Lo;0;L;51DE;;;;N;;;;;
+FA16;CJK COMPATIBILITY IDEOGRAPH-FA16;Lo;0;L;732A;;;;N;;;;;
+FA17;CJK COMPATIBILITY IDEOGRAPH-FA17;Lo;0;L;76CA;;;;N;;;;;
+FA18;CJK COMPATIBILITY IDEOGRAPH-FA18;Lo;0;L;793C;;;;N;;;;;
+FA19;CJK COMPATIBILITY IDEOGRAPH-FA19;Lo;0;L;795E;;;;N;;;;;
+FA1A;CJK COMPATIBILITY IDEOGRAPH-FA1A;Lo;0;L;7965;;;;N;;;;;
+FA1B;CJK COMPATIBILITY IDEOGRAPH-FA1B;Lo;0;L;798F;;;;N;;;;;
+FA1C;CJK COMPATIBILITY IDEOGRAPH-FA1C;Lo;0;L;9756;;;;N;;;;;
+FA1D;CJK COMPATIBILITY IDEOGRAPH-FA1D;Lo;0;L;7CBE;;;;N;;;;;
+FA1E;CJK COMPATIBILITY IDEOGRAPH-FA1E;Lo;0;L;7FBD;;;;N;;;;;
+FA1F;CJK COMPATIBILITY IDEOGRAPH-FA1F;Lo;0;L;;;;;N;;*;;;
+FA20;CJK COMPATIBILITY IDEOGRAPH-FA20;Lo;0;L;8612;;;;N;;;;;
+FA21;CJK COMPATIBILITY IDEOGRAPH-FA21;Lo;0;L;;;;;N;;;;;
+FA22;CJK COMPATIBILITY IDEOGRAPH-FA22;Lo;0;L;8AF8;;;;N;;;;;
+FA23;CJK COMPATIBILITY IDEOGRAPH-FA23;Lo;0;L;;;;;N;;*;;;
+FA24;CJK COMPATIBILITY IDEOGRAPH-FA24;Lo;0;L;;;;;N;;;;;
+FA25;CJK COMPATIBILITY IDEOGRAPH-FA25;Lo;0;L;9038;;;;N;;;;;
+FA26;CJK COMPATIBILITY IDEOGRAPH-FA26;Lo;0;L;90FD;;;;N;;;;;
+FA27;CJK COMPATIBILITY IDEOGRAPH-FA27;Lo;0;L;;;;;N;;;;;
+FA28;CJK COMPATIBILITY IDEOGRAPH-FA28;Lo;0;L;;;;;N;;;;;
+FA29;CJK COMPATIBILITY IDEOGRAPH-FA29;Lo;0;L;;;;;N;;;;;
+FA2A;CJK COMPATIBILITY IDEOGRAPH-FA2A;Lo;0;L;98EF;;;;N;;;;;
+FA2B;CJK COMPATIBILITY IDEOGRAPH-FA2B;Lo;0;L;98FC;;;;N;;;;;
+FA2C;CJK COMPATIBILITY IDEOGRAPH-FA2C;Lo;0;L;9928;;;;N;;;;;
+FA2D;CJK COMPATIBILITY IDEOGRAPH-FA2D;Lo;0;L;9DB4;;;;N;;;;;
+FA30;CJK COMPATIBILITY IDEOGRAPH-FA30;Lo;0;L;4FAE;;;;N;;;;;
+FA31;CJK COMPATIBILITY IDEOGRAPH-FA31;Lo;0;L;50E7;;;;N;;;;;
+FA32;CJK COMPATIBILITY IDEOGRAPH-FA32;Lo;0;L;514D;;;;N;;;;;
+FA33;CJK COMPATIBILITY IDEOGRAPH-FA33;Lo;0;L;52C9;;;;N;;;;;
+FA34;CJK COMPATIBILITY IDEOGRAPH-FA34;Lo;0;L;52E4;;;;N;;;;;
+FA35;CJK COMPATIBILITY IDEOGRAPH-FA35;Lo;0;L;5351;;;;N;;;;;
+FA36;CJK COMPATIBILITY IDEOGRAPH-FA36;Lo;0;L;559D;;;;N;;;;;
+FA37;CJK COMPATIBILITY IDEOGRAPH-FA37;Lo;0;L;5606;;;;N;;;;;
+FA38;CJK COMPATIBILITY IDEOGRAPH-FA38;Lo;0;L;5668;;;;N;;;;;
+FA39;CJK COMPATIBILITY IDEOGRAPH-FA39;Lo;0;L;5840;;;;N;;;;;
+FA3A;CJK COMPATIBILITY IDEOGRAPH-FA3A;Lo;0;L;58A8;;;;N;;;;;
+FA3B;CJK COMPATIBILITY IDEOGRAPH-FA3B;Lo;0;L;5C64;;;;N;;;;;
+FA3C;CJK COMPATIBILITY IDEOGRAPH-FA3C;Lo;0;L;5C6E;;;;N;;;;;
+FA3D;CJK COMPATIBILITY IDEOGRAPH-FA3D;Lo;0;L;6094;;;;N;;;;;
+FA3E;CJK COMPATIBILITY IDEOGRAPH-FA3E;Lo;0;L;6168;;;;N;;;;;
+FA3F;CJK COMPATIBILITY IDEOGRAPH-FA3F;Lo;0;L;618E;;;;N;;;;;
+FA40;CJK COMPATIBILITY IDEOGRAPH-FA40;Lo;0;L;61F2;;;;N;;;;;
+FA41;CJK COMPATIBILITY IDEOGRAPH-FA41;Lo;0;L;654F;;;;N;;;;;
+FA42;CJK COMPATIBILITY IDEOGRAPH-FA42;Lo;0;L;65E2;;;;N;;;;;
+FA43;CJK COMPATIBILITY IDEOGRAPH-FA43;Lo;0;L;6691;;;;N;;;;;
+FA44;CJK COMPATIBILITY IDEOGRAPH-FA44;Lo;0;L;6885;;;;N;;;;;
+FA45;CJK COMPATIBILITY IDEOGRAPH-FA45;Lo;0;L;6D77;;;;N;;;;;
+FA46;CJK COMPATIBILITY IDEOGRAPH-FA46;Lo;0;L;6E1A;;;;N;;;;;
+FA47;CJK COMPATIBILITY IDEOGRAPH-FA47;Lo;0;L;6F22;;;;N;;;;;
+FA48;CJK COMPATIBILITY IDEOGRAPH-FA48;Lo;0;L;716E;;;;N;;;;;
+FA49;CJK COMPATIBILITY IDEOGRAPH-FA49;Lo;0;L;722B;;;;N;;;;;
+FA4A;CJK COMPATIBILITY IDEOGRAPH-FA4A;Lo;0;L;7422;;;;N;;;;;
+FA4B;CJK COMPATIBILITY IDEOGRAPH-FA4B;Lo;0;L;7891;;;;N;;;;;
+FA4C;CJK COMPATIBILITY IDEOGRAPH-FA4C;Lo;0;L;793E;;;;N;;;;;
+FA4D;CJK COMPATIBILITY IDEOGRAPH-FA4D;Lo;0;L;7949;;;;N;;;;;
+FA4E;CJK COMPATIBILITY IDEOGRAPH-FA4E;Lo;0;L;7948;;;;N;;;;;
+FA4F;CJK COMPATIBILITY IDEOGRAPH-FA4F;Lo;0;L;7950;;;;N;;;;;
+FA50;CJK COMPATIBILITY IDEOGRAPH-FA50;Lo;0;L;7956;;;;N;;;;;
+FA51;CJK COMPATIBILITY IDEOGRAPH-FA51;Lo;0;L;795D;;;;N;;;;;
+FA52;CJK COMPATIBILITY IDEOGRAPH-FA52;Lo;0;L;798D;;;;N;;;;;
+FA53;CJK COMPATIBILITY IDEOGRAPH-FA53;Lo;0;L;798E;;;;N;;;;;
+FA54;CJK COMPATIBILITY IDEOGRAPH-FA54;Lo;0;L;7A40;;;;N;;;;;
+FA55;CJK COMPATIBILITY IDEOGRAPH-FA55;Lo;0;L;7A81;;;;N;;;;;
+FA56;CJK COMPATIBILITY IDEOGRAPH-FA56;Lo;0;L;7BC0;;;;N;;;;;
+FA57;CJK COMPATIBILITY IDEOGRAPH-FA57;Lo;0;L;7DF4;;;;N;;;;;
+FA58;CJK COMPATIBILITY IDEOGRAPH-FA58;Lo;0;L;7E09;;;;N;;;;;
+FA59;CJK COMPATIBILITY IDEOGRAPH-FA59;Lo;0;L;7E41;;;;N;;;;;
+FA5A;CJK COMPATIBILITY IDEOGRAPH-FA5A;Lo;0;L;7F72;;;;N;;;;;
+FA5B;CJK COMPATIBILITY IDEOGRAPH-FA5B;Lo;0;L;8005;;;;N;;;;;
+FA5C;CJK COMPATIBILITY IDEOGRAPH-FA5C;Lo;0;L;81ED;;;;N;;;;;
+FA5D;CJK COMPATIBILITY IDEOGRAPH-FA5D;Lo;0;L;8279;;;;N;;;;;
+FA5E;CJK COMPATIBILITY IDEOGRAPH-FA5E;Lo;0;L;8279;;;;N;;;;;
+FA5F;CJK COMPATIBILITY IDEOGRAPH-FA5F;Lo;0;L;8457;;;;N;;;;;
+FA60;CJK COMPATIBILITY IDEOGRAPH-FA60;Lo;0;L;8910;;;;N;;;;;
+FA61;CJK COMPATIBILITY IDEOGRAPH-FA61;Lo;0;L;8996;;;;N;;;;;
+FA62;CJK COMPATIBILITY IDEOGRAPH-FA62;Lo;0;L;8B01;;;;N;;;;;
+FA63;CJK COMPATIBILITY IDEOGRAPH-FA63;Lo;0;L;8B39;;;;N;;;;;
+FA64;CJK COMPATIBILITY IDEOGRAPH-FA64;Lo;0;L;8CD3;;;;N;;;;;
+FA65;CJK COMPATIBILITY IDEOGRAPH-FA65;Lo;0;L;8D08;;;;N;;;;;
+FA66;CJK COMPATIBILITY IDEOGRAPH-FA66;Lo;0;L;8FB6;;;;N;;;;;
+FA67;CJK COMPATIBILITY IDEOGRAPH-FA67;Lo;0;L;9038;;;;N;;;;;
+FA68;CJK COMPATIBILITY IDEOGRAPH-FA68;Lo;0;L;96E3;;;;N;;;;;
+FA69;CJK COMPATIBILITY IDEOGRAPH-FA69;Lo;0;L;97FF;;;;N;;;;;
+FA6A;CJK COMPATIBILITY IDEOGRAPH-FA6A;Lo;0;L;983B;;;;N;;;;;
+FB00;LATIN SMALL LIGATURE FF;Ll;0;L;<compat> 0066 0066;;;;N;;;;;
+FB01;LATIN SMALL LIGATURE FI;Ll;0;L;<compat> 0066 0069;;;;N;;;;;
+FB02;LATIN SMALL LIGATURE FL;Ll;0;L;<compat> 0066 006C;;;;N;;;;;
+FB03;LATIN SMALL LIGATURE FFI;Ll;0;L;<compat> 0066 0066 0069;;;;N;;;;;
+FB04;LATIN SMALL LIGATURE FFL;Ll;0;L;<compat> 0066 0066 006C;;;;N;;;;;
+FB05;LATIN SMALL LIGATURE LONG S T;Ll;0;L;<compat> 017F 0074;;;;N;;;;;
+FB06;LATIN SMALL LIGATURE ST;Ll;0;L;<compat> 0073 0074;;;;N;;;;;
+FB13;ARMENIAN SMALL LIGATURE MEN NOW;Ll;0;L;<compat> 0574 0576;;;;N;;;;;
+FB14;ARMENIAN SMALL LIGATURE MEN ECH;Ll;0;L;<compat> 0574 0565;;;;N;;;;;
+FB15;ARMENIAN SMALL LIGATURE MEN INI;Ll;0;L;<compat> 0574 056B;;;;N;;;;;
+FB16;ARMENIAN SMALL LIGATURE VEW NOW;Ll;0;L;<compat> 057E 0576;;;;N;;;;;
+FB17;ARMENIAN SMALL LIGATURE MEN XEH;Ll;0;L;<compat> 0574 056D;;;;N;;;;;
+FB1D;HEBREW LETTER YOD WITH HIRIQ;Lo;0;R;05D9 05B4;;;;N;;;;;
+FB1E;HEBREW POINT JUDEO-SPANISH VARIKA;Mn;26;NSM;;;;;N;HEBREW POINT VARIKA;;;;
+FB1F;HEBREW LIGATURE YIDDISH YOD YOD PATAH;Lo;0;R;05F2 05B7;;;;N;;;;;
+FB20;HEBREW LETTER ALTERNATIVE AYIN;Lo;0;R;<font> 05E2;;;;N;;;;;
+FB21;HEBREW LETTER WIDE ALEF;Lo;0;R;<font> 05D0;;;;N;;;;;
+FB22;HEBREW LETTER WIDE DALET;Lo;0;R;<font> 05D3;;;;N;;;;;
+FB23;HEBREW LETTER WIDE HE;Lo;0;R;<font> 05D4;;;;N;;;;;
+FB24;HEBREW LETTER WIDE KAF;Lo;0;R;<font> 05DB;;;;N;;;;;
+FB25;HEBREW LETTER WIDE LAMED;Lo;0;R;<font> 05DC;;;;N;;;;;
+FB26;HEBREW LETTER WIDE FINAL MEM;Lo;0;R;<font> 05DD;;;;N;;;;;
+FB27;HEBREW LETTER WIDE RESH;Lo;0;R;<font> 05E8;;;;N;;;;;
+FB28;HEBREW LETTER WIDE TAV;Lo;0;R;<font> 05EA;;;;N;;;;;
+FB29;HEBREW LETTER ALTERNATIVE PLUS SIGN;Sm;0;ET;<font> 002B;;;;N;;;;;
+FB2A;HEBREW LETTER SHIN WITH SHIN DOT;Lo;0;R;05E9 05C1;;;;N;;;;;
+FB2B;HEBREW LETTER SHIN WITH SIN DOT;Lo;0;R;05E9 05C2;;;;N;;;;;
+FB2C;HEBREW LETTER SHIN WITH DAGESH AND SHIN DOT;Lo;0;R;FB49 05C1;;;;N;;;;;
+FB2D;HEBREW LETTER SHIN WITH DAGESH AND SIN DOT;Lo;0;R;FB49 05C2;;;;N;;;;;
+FB2E;HEBREW LETTER ALEF WITH PATAH;Lo;0;R;05D0 05B7;;;;N;;;;;
+FB2F;HEBREW LETTER ALEF WITH QAMATS;Lo;0;R;05D0 05B8;;;;N;;;;;
+FB30;HEBREW LETTER ALEF WITH MAPIQ;Lo;0;R;05D0 05BC;;;;N;;;;;
+FB31;HEBREW LETTER BET WITH DAGESH;Lo;0;R;05D1 05BC;;;;N;;;;;
+FB32;HEBREW LETTER GIMEL WITH DAGESH;Lo;0;R;05D2 05BC;;;;N;;;;;
+FB33;HEBREW LETTER DALET WITH DAGESH;Lo;0;R;05D3 05BC;;;;N;;;;;
+FB34;HEBREW LETTER HE WITH MAPIQ;Lo;0;R;05D4 05BC;;;;N;;;;;
+FB35;HEBREW LETTER VAV WITH DAGESH;Lo;0;R;05D5 05BC;;;;N;;;;;
+FB36;HEBREW LETTER ZAYIN WITH DAGESH;Lo;0;R;05D6 05BC;;;;N;;;;;
+FB38;HEBREW LETTER TET WITH DAGESH;Lo;0;R;05D8 05BC;;;;N;;;;;
+FB39;HEBREW LETTER YOD WITH DAGESH;Lo;0;R;05D9 05BC;;;;N;;;;;
+FB3A;HEBREW LETTER FINAL KAF WITH DAGESH;Lo;0;R;05DA 05BC;;;;N;;;;;
+FB3B;HEBREW LETTER KAF WITH DAGESH;Lo;0;R;05DB 05BC;;;;N;;;;;
+FB3C;HEBREW LETTER LAMED WITH DAGESH;Lo;0;R;05DC 05BC;;;;N;;;;;
+FB3E;HEBREW LETTER MEM WITH DAGESH;Lo;0;R;05DE 05BC;;;;N;;;;;
+FB40;HEBREW LETTER NUN WITH DAGESH;Lo;0;R;05E0 05BC;;;;N;;;;;
+FB41;HEBREW LETTER SAMEKH WITH DAGESH;Lo;0;R;05E1 05BC;;;;N;;;;;
+FB43;HEBREW LETTER FINAL PE WITH DAGESH;Lo;0;R;05E3 05BC;;;;N;;;;;
+FB44;HEBREW LETTER PE WITH DAGESH;Lo;0;R;05E4 05BC;;;;N;;;;;
+FB46;HEBREW LETTER TSADI WITH DAGESH;Lo;0;R;05E6 05BC;;;;N;;;;;
+FB47;HEBREW LETTER QOF WITH DAGESH;Lo;0;R;05E7 05BC;;;;N;;;;;
+FB48;HEBREW LETTER RESH WITH DAGESH;Lo;0;R;05E8 05BC;;;;N;;;;;
+FB49;HEBREW LETTER SHIN WITH DAGESH;Lo;0;R;05E9 05BC;;;;N;;;;;
+FB4A;HEBREW LETTER TAV WITH DAGESH;Lo;0;R;05EA 05BC;;;;N;;;;;
+FB4B;HEBREW LETTER VAV WITH HOLAM;Lo;0;R;05D5 05B9;;;;N;;;;;
+FB4C;HEBREW LETTER BET WITH RAFE;Lo;0;R;05D1 05BF;;;;N;;;;;
+FB4D;HEBREW LETTER KAF WITH RAFE;Lo;0;R;05DB 05BF;;;;N;;;;;
+FB4E;HEBREW LETTER PE WITH RAFE;Lo;0;R;05E4 05BF;;;;N;;;;;
+FB4F;HEBREW LIGATURE ALEF LAMED;Lo;0;R;<compat> 05D0 05DC;;;;N;;;;;
+FB50;ARABIC LETTER ALEF WASLA ISOLATED FORM;Lo;0;AL;<isolated> 0671;;;;N;;;;;
+FB51;ARABIC LETTER ALEF WASLA FINAL FORM;Lo;0;AL;<final> 0671;;;;N;;;;;
+FB52;ARABIC LETTER BEEH ISOLATED FORM;Lo;0;AL;<isolated> 067B;;;;N;;;;;
+FB53;ARABIC LETTER BEEH FINAL FORM;Lo;0;AL;<final> 067B;;;;N;;;;;
+FB54;ARABIC LETTER BEEH INITIAL FORM;Lo;0;AL;<initial> 067B;;;;N;;;;;
+FB55;ARABIC LETTER BEEH MEDIAL FORM;Lo;0;AL;<medial> 067B;;;;N;;;;;
+FB56;ARABIC LETTER PEH ISOLATED FORM;Lo;0;AL;<isolated> 067E;;;;N;;;;;
+FB57;ARABIC LETTER PEH FINAL FORM;Lo;0;AL;<final> 067E;;;;N;;;;;
+FB58;ARABIC LETTER PEH INITIAL FORM;Lo;0;AL;<initial> 067E;;;;N;;;;;
+FB59;ARABIC LETTER PEH MEDIAL FORM;Lo;0;AL;<medial> 067E;;;;N;;;;;
+FB5A;ARABIC LETTER BEHEH ISOLATED FORM;Lo;0;AL;<isolated> 0680;;;;N;;;;;
+FB5B;ARABIC LETTER BEHEH FINAL FORM;Lo;0;AL;<final> 0680;;;;N;;;;;
+FB5C;ARABIC LETTER BEHEH INITIAL FORM;Lo;0;AL;<initial> 0680;;;;N;;;;;
+FB5D;ARABIC LETTER BEHEH MEDIAL FORM;Lo;0;AL;<medial> 0680;;;;N;;;;;
+FB5E;ARABIC LETTER TTEHEH ISOLATED FORM;Lo;0;AL;<isolated> 067A;;;;N;;;;;
+FB5F;ARABIC LETTER TTEHEH FINAL FORM;Lo;0;AL;<final> 067A;;;;N;;;;;
+FB60;ARABIC LETTER TTEHEH INITIAL FORM;Lo;0;AL;<initial> 067A;;;;N;;;;;
+FB61;ARABIC LETTER TTEHEH MEDIAL FORM;Lo;0;AL;<medial> 067A;;;;N;;;;;
+FB62;ARABIC LETTER TEHEH ISOLATED FORM;Lo;0;AL;<isolated> 067F;;;;N;;;;;
+FB63;ARABIC LETTER TEHEH FINAL FORM;Lo;0;AL;<final> 067F;;;;N;;;;;
+FB64;ARABIC LETTER TEHEH INITIAL FORM;Lo;0;AL;<initial> 067F;;;;N;;;;;
+FB65;ARABIC LETTER TEHEH MEDIAL FORM;Lo;0;AL;<medial> 067F;;;;N;;;;;
+FB66;ARABIC LETTER TTEH ISOLATED FORM;Lo;0;AL;<isolated> 0679;;;;N;;;;;
+FB67;ARABIC LETTER TTEH FINAL FORM;Lo;0;AL;<final> 0679;;;;N;;;;;
+FB68;ARABIC LETTER TTEH INITIAL FORM;Lo;0;AL;<initial> 0679;;;;N;;;;;
+FB69;ARABIC LETTER TTEH MEDIAL FORM;Lo;0;AL;<medial> 0679;;;;N;;;;;
+FB6A;ARABIC LETTER VEH ISOLATED FORM;Lo;0;AL;<isolated> 06A4;;;;N;;;;;
+FB6B;ARABIC LETTER VEH FINAL FORM;Lo;0;AL;<final> 06A4;;;;N;;;;;
+FB6C;ARABIC LETTER VEH INITIAL FORM;Lo;0;AL;<initial> 06A4;;;;N;;;;;
+FB6D;ARABIC LETTER VEH MEDIAL FORM;Lo;0;AL;<medial> 06A4;;;;N;;;;;
+FB6E;ARABIC LETTER PEHEH ISOLATED FORM;Lo;0;AL;<isolated> 06A6;;;;N;;;;;
+FB6F;ARABIC LETTER PEHEH FINAL FORM;Lo;0;AL;<final> 06A6;;;;N;;;;;
+FB70;ARABIC LETTER PEHEH INITIAL FORM;Lo;0;AL;<initial> 06A6;;;;N;;;;;
+FB71;ARABIC LETTER PEHEH MEDIAL FORM;Lo;0;AL;<medial> 06A6;;;;N;;;;;
+FB72;ARABIC LETTER DYEH ISOLATED FORM;Lo;0;AL;<isolated> 0684;;;;N;;;;;
+FB73;ARABIC LETTER DYEH FINAL FORM;Lo;0;AL;<final> 0684;;;;N;;;;;
+FB74;ARABIC LETTER DYEH INITIAL FORM;Lo;0;AL;<initial> 0684;;;;N;;;;;
+FB75;ARABIC LETTER DYEH MEDIAL FORM;Lo;0;AL;<medial> 0684;;;;N;;;;;
+FB76;ARABIC LETTER NYEH ISOLATED FORM;Lo;0;AL;<isolated> 0683;;;;N;;;;;
+FB77;ARABIC LETTER NYEH FINAL FORM;Lo;0;AL;<final> 0683;;;;N;;;;;
+FB78;ARABIC LETTER NYEH INITIAL FORM;Lo;0;AL;<initial> 0683;;;;N;;;;;
+FB79;ARABIC LETTER NYEH MEDIAL FORM;Lo;0;AL;<medial> 0683;;;;N;;;;;
+FB7A;ARABIC LETTER TCHEH ISOLATED FORM;Lo;0;AL;<isolated> 0686;;;;N;;;;;
+FB7B;ARABIC LETTER TCHEH FINAL FORM;Lo;0;AL;<final> 0686;;;;N;;;;;
+FB7C;ARABIC LETTER TCHEH INITIAL FORM;Lo;0;AL;<initial> 0686;;;;N;;;;;
+FB7D;ARABIC LETTER TCHEH MEDIAL FORM;Lo;0;AL;<medial> 0686;;;;N;;;;;
+FB7E;ARABIC LETTER TCHEHEH ISOLATED FORM;Lo;0;AL;<isolated> 0687;;;;N;;;;;
+FB7F;ARABIC LETTER TCHEHEH FINAL FORM;Lo;0;AL;<final> 0687;;;;N;;;;;
+FB80;ARABIC LETTER TCHEHEH INITIAL FORM;Lo;0;AL;<initial> 0687;;;;N;;;;;
+FB81;ARABIC LETTER TCHEHEH MEDIAL FORM;Lo;0;AL;<medial> 0687;;;;N;;;;;
+FB82;ARABIC LETTER DDAHAL ISOLATED FORM;Lo;0;AL;<isolated> 068D;;;;N;;;;;
+FB83;ARABIC LETTER DDAHAL FINAL FORM;Lo;0;AL;<final> 068D;;;;N;;;;;
+FB84;ARABIC LETTER DAHAL ISOLATED FORM;Lo;0;AL;<isolated> 068C;;;;N;;;;;
+FB85;ARABIC LETTER DAHAL FINAL FORM;Lo;0;AL;<final> 068C;;;;N;;;;;
+FB86;ARABIC LETTER DUL ISOLATED FORM;Lo;0;AL;<isolated> 068E;;;;N;;;;;
+FB87;ARABIC LETTER DUL FINAL FORM;Lo;0;AL;<final> 068E;;;;N;;;;;
+FB88;ARABIC LETTER DDAL ISOLATED FORM;Lo;0;AL;<isolated> 0688;;;;N;;;;;
+FB89;ARABIC LETTER DDAL FINAL FORM;Lo;0;AL;<final> 0688;;;;N;;;;;
+FB8A;ARABIC LETTER JEH ISOLATED FORM;Lo;0;AL;<isolated> 0698;;;;N;;;;;
+FB8B;ARABIC LETTER JEH FINAL FORM;Lo;0;AL;<final> 0698;;;;N;;;;;
+FB8C;ARABIC LETTER RREH ISOLATED FORM;Lo;0;AL;<isolated> 0691;;;;N;;;;;
+FB8D;ARABIC LETTER RREH FINAL FORM;Lo;0;AL;<final> 0691;;;;N;;;;;
+FB8E;ARABIC LETTER KEHEH ISOLATED FORM;Lo;0;AL;<isolated> 06A9;;;;N;;;;;
+FB8F;ARABIC LETTER KEHEH FINAL FORM;Lo;0;AL;<final> 06A9;;;;N;;;;;
+FB90;ARABIC LETTER KEHEH INITIAL FORM;Lo;0;AL;<initial> 06A9;;;;N;;;;;
+FB91;ARABIC LETTER KEHEH MEDIAL FORM;Lo;0;AL;<medial> 06A9;;;;N;;;;;
+FB92;ARABIC LETTER GAF ISOLATED FORM;Lo;0;AL;<isolated> 06AF;;;;N;;;;;
+FB93;ARABIC LETTER GAF FINAL FORM;Lo;0;AL;<final> 06AF;;;;N;;;;;
+FB94;ARABIC LETTER GAF INITIAL FORM;Lo;0;AL;<initial> 06AF;;;;N;;;;;
+FB95;ARABIC LETTER GAF MEDIAL FORM;Lo;0;AL;<medial> 06AF;;;;N;;;;;
+FB96;ARABIC LETTER GUEH ISOLATED FORM;Lo;0;AL;<isolated> 06B3;;;;N;;;;;
+FB97;ARABIC LETTER GUEH FINAL FORM;Lo;0;AL;<final> 06B3;;;;N;;;;;
+FB98;ARABIC LETTER GUEH INITIAL FORM;Lo;0;AL;<initial> 06B3;;;;N;;;;;
+FB99;ARABIC LETTER GUEH MEDIAL FORM;Lo;0;AL;<medial> 06B3;;;;N;;;;;
+FB9A;ARABIC LETTER NGOEH ISOLATED FORM;Lo;0;AL;<isolated> 06B1;;;;N;;;;;
+FB9B;ARABIC LETTER NGOEH FINAL FORM;Lo;0;AL;<final> 06B1;;;;N;;;;;
+FB9C;ARABIC LETTER NGOEH INITIAL FORM;Lo;0;AL;<initial> 06B1;;;;N;;;;;
+FB9D;ARABIC LETTER NGOEH MEDIAL FORM;Lo;0;AL;<medial> 06B1;;;;N;;;;;
+FB9E;ARABIC LETTER NOON GHUNNA ISOLATED FORM;Lo;0;AL;<isolated> 06BA;;;;N;;;;;
+FB9F;ARABIC LETTER NOON GHUNNA FINAL FORM;Lo;0;AL;<final> 06BA;;;;N;;;;;
+FBA0;ARABIC LETTER RNOON ISOLATED FORM;Lo;0;AL;<isolated> 06BB;;;;N;;;;;
+FBA1;ARABIC LETTER RNOON FINAL FORM;Lo;0;AL;<final> 06BB;;;;N;;;;;
+FBA2;ARABIC LETTER RNOON INITIAL FORM;Lo;0;AL;<initial> 06BB;;;;N;;;;;
+FBA3;ARABIC LETTER RNOON MEDIAL FORM;Lo;0;AL;<medial> 06BB;;;;N;;;;;
+FBA4;ARABIC LETTER HEH WITH YEH ABOVE ISOLATED FORM;Lo;0;AL;<isolated> 06C0;;;;N;;;;;
+FBA5;ARABIC LETTER HEH WITH YEH ABOVE FINAL FORM;Lo;0;AL;<final> 06C0;;;;N;;;;;
+FBA6;ARABIC LETTER HEH GOAL ISOLATED FORM;Lo;0;AL;<isolated> 06C1;;;;N;;;;;
+FBA7;ARABIC LETTER HEH GOAL FINAL FORM;Lo;0;AL;<final> 06C1;;;;N;;;;;
+FBA8;ARABIC LETTER HEH GOAL INITIAL FORM;Lo;0;AL;<initial> 06C1;;;;N;;;;;
+FBA9;ARABIC LETTER HEH GOAL MEDIAL FORM;Lo;0;AL;<medial> 06C1;;;;N;;;;;
+FBAA;ARABIC LETTER HEH DOACHASHMEE ISOLATED FORM;Lo;0;AL;<isolated> 06BE;;;;N;;;;;
+FBAB;ARABIC LETTER HEH DOACHASHMEE FINAL FORM;Lo;0;AL;<final> 06BE;;;;N;;;;;
+FBAC;ARABIC LETTER HEH DOACHASHMEE INITIAL FORM;Lo;0;AL;<initial> 06BE;;;;N;;;;;
+FBAD;ARABIC LETTER HEH DOACHASHMEE MEDIAL FORM;Lo;0;AL;<medial> 06BE;;;;N;;;;;
+FBAE;ARABIC LETTER YEH BARREE ISOLATED FORM;Lo;0;AL;<isolated> 06D2;;;;N;;;;;
+FBAF;ARABIC LETTER YEH BARREE FINAL FORM;Lo;0;AL;<final> 06D2;;;;N;;;;;
+FBB0;ARABIC LETTER YEH BARREE WITH HAMZA ABOVE ISOLATED FORM;Lo;0;AL;<isolated> 06D3;;;;N;;;;;
+FBB1;ARABIC LETTER YEH BARREE WITH HAMZA ABOVE FINAL FORM;Lo;0;AL;<final> 06D3;;;;N;;;;;
+FBD3;ARABIC LETTER NG ISOLATED FORM;Lo;0;AL;<isolated> 06AD;;;;N;;;;;
+FBD4;ARABIC LETTER NG FINAL FORM;Lo;0;AL;<final> 06AD;;;;N;;;;;
+FBD5;ARABIC LETTER NG INITIAL FORM;Lo;0;AL;<initial> 06AD;;;;N;;;;;
+FBD6;ARABIC LETTER NG MEDIAL FORM;Lo;0;AL;<medial> 06AD;;;;N;;;;;
+FBD7;ARABIC LETTER U ISOLATED FORM;Lo;0;AL;<isolated> 06C7;;;;N;;;;;
+FBD8;ARABIC LETTER U FINAL FORM;Lo;0;AL;<final> 06C7;;;;N;;;;;
+FBD9;ARABIC LETTER OE ISOLATED FORM;Lo;0;AL;<isolated> 06C6;;;;N;;;;;
+FBDA;ARABIC LETTER OE FINAL FORM;Lo;0;AL;<final> 06C6;;;;N;;;;;
+FBDB;ARABIC LETTER YU ISOLATED FORM;Lo;0;AL;<isolated> 06C8;;;;N;;;;;
+FBDC;ARABIC LETTER YU FINAL FORM;Lo;0;AL;<final> 06C8;;;;N;;;;;
+FBDD;ARABIC LETTER U WITH HAMZA ABOVE ISOLATED FORM;Lo;0;AL;<isolated> 0677;;;;N;;;;;
+FBDE;ARABIC LETTER VE ISOLATED FORM;Lo;0;AL;<isolated> 06CB;;;;N;;;;;
+FBDF;ARABIC LETTER VE FINAL FORM;Lo;0;AL;<final> 06CB;;;;N;;;;;
+FBE0;ARABIC LETTER KIRGHIZ OE ISOLATED FORM;Lo;0;AL;<isolated> 06C5;;;;N;;;;;
+FBE1;ARABIC LETTER KIRGHIZ OE FINAL FORM;Lo;0;AL;<final> 06C5;;;;N;;;;;
+FBE2;ARABIC LETTER KIRGHIZ YU ISOLATED FORM;Lo;0;AL;<isolated> 06C9;;;;N;;;;;
+FBE3;ARABIC LETTER KIRGHIZ YU FINAL FORM;Lo;0;AL;<final> 06C9;;;;N;;;;;
+FBE4;ARABIC LETTER E ISOLATED FORM;Lo;0;AL;<isolated> 06D0;;;;N;;;;;
+FBE5;ARABIC LETTER E FINAL FORM;Lo;0;AL;<final> 06D0;;;;N;;;;;
+FBE6;ARABIC LETTER E INITIAL FORM;Lo;0;AL;<initial> 06D0;;;;N;;;;;
+FBE7;ARABIC LETTER E MEDIAL FORM;Lo;0;AL;<medial> 06D0;;;;N;;;;;
+FBE8;ARABIC LETTER UIGHUR KAZAKH KIRGHIZ ALEF MAKSURA INITIAL FORM;Lo;0;AL;<initial> 0649;;;;N;;;;;
+FBE9;ARABIC LETTER UIGHUR KAZAKH KIRGHIZ ALEF MAKSURA MEDIAL FORM;Lo;0;AL;<medial> 0649;;;;N;;;;;
+FBEA;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF ISOLATED FORM;Lo;0;AL;<isolated> 0626 0627;;;;N;;;;;
+FBEB;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF FINAL FORM;Lo;0;AL;<final> 0626 0627;;;;N;;;;;
+FBEC;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH AE ISOLATED FORM;Lo;0;AL;<isolated> 0626 06D5;;;;N;;;;;
+FBED;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH AE FINAL FORM;Lo;0;AL;<final> 0626 06D5;;;;N;;;;;
+FBEE;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH WAW ISOLATED FORM;Lo;0;AL;<isolated> 0626 0648;;;;N;;;;;
+FBEF;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH WAW FINAL FORM;Lo;0;AL;<final> 0626 0648;;;;N;;;;;
+FBF0;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH U ISOLATED FORM;Lo;0;AL;<isolated> 0626 06C7;;;;N;;;;;
+FBF1;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH U FINAL FORM;Lo;0;AL;<final> 0626 06C7;;;;N;;;;;
+FBF2;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH OE ISOLATED FORM;Lo;0;AL;<isolated> 0626 06C6;;;;N;;;;;
+FBF3;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH OE FINAL FORM;Lo;0;AL;<final> 0626 06C6;;;;N;;;;;
+FBF4;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH YU ISOLATED FORM;Lo;0;AL;<isolated> 0626 06C8;;;;N;;;;;
+FBF5;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH YU FINAL FORM;Lo;0;AL;<final> 0626 06C8;;;;N;;;;;
+FBF6;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH E ISOLATED FORM;Lo;0;AL;<isolated> 0626 06D0;;;;N;;;;;
+FBF7;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH E FINAL FORM;Lo;0;AL;<final> 0626 06D0;;;;N;;;;;
+FBF8;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH E INITIAL FORM;Lo;0;AL;<initial> 0626 06D0;;;;N;;;;;
+FBF9;ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH HAMZA ABOVE WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0626 0649;;;;N;;;;;
+FBFA;ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH HAMZA ABOVE WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0626 0649;;;;N;;;;;
+FBFB;ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH HAMZA ABOVE WITH ALEF MAKSURA INITIAL FORM;Lo;0;AL;<initial> 0626 0649;;;;N;;;;;
+FBFC;ARABIC LETTER FARSI YEH ISOLATED FORM;Lo;0;AL;<isolated> 06CC;;;;N;;;;;
+FBFD;ARABIC LETTER FARSI YEH FINAL FORM;Lo;0;AL;<final> 06CC;;;;N;;;;;
+FBFE;ARABIC LETTER FARSI YEH INITIAL FORM;Lo;0;AL;<initial> 06CC;;;;N;;;;;
+FBFF;ARABIC LETTER FARSI YEH MEDIAL FORM;Lo;0;AL;<medial> 06CC;;;;N;;;;;
+FC00;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0626 062C;;;;N;;;;;
+FC01;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0626 062D;;;;N;;;;;
+FC02;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0626 0645;;;;N;;;;;
+FC03;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0626 0649;;;;N;;;;;
+FC04;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0626 064A;;;;N;;;;;
+FC05;ARABIC LIGATURE BEH WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0628 062C;;;;N;;;;;
+FC06;ARABIC LIGATURE BEH WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0628 062D;;;;N;;;;;
+FC07;ARABIC LIGATURE BEH WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 0628 062E;;;;N;;;;;
+FC08;ARABIC LIGATURE BEH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0628 0645;;;;N;;;;;
+FC09;ARABIC LIGATURE BEH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0628 0649;;;;N;;;;;
+FC0A;ARABIC LIGATURE BEH WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0628 064A;;;;N;;;;;
+FC0B;ARABIC LIGATURE TEH WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 062A 062C;;;;N;;;;;
+FC0C;ARABIC LIGATURE TEH WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 062A 062D;;;;N;;;;;
+FC0D;ARABIC LIGATURE TEH WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 062A 062E;;;;N;;;;;
+FC0E;ARABIC LIGATURE TEH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 062A 0645;;;;N;;;;;
+FC0F;ARABIC LIGATURE TEH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 062A 0649;;;;N;;;;;
+FC10;ARABIC LIGATURE TEH WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 062A 064A;;;;N;;;;;
+FC11;ARABIC LIGATURE THEH WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 062B 062C;;;;N;;;;;
+FC12;ARABIC LIGATURE THEH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 062B 0645;;;;N;;;;;
+FC13;ARABIC LIGATURE THEH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 062B 0649;;;;N;;;;;
+FC14;ARABIC LIGATURE THEH WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 062B 064A;;;;N;;;;;
+FC15;ARABIC LIGATURE JEEM WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 062C 062D;;;;N;;;;;
+FC16;ARABIC LIGATURE JEEM WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 062C 0645;;;;N;;;;;
+FC17;ARABIC LIGATURE HAH WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 062D 062C;;;;N;;;;;
+FC18;ARABIC LIGATURE HAH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 062D 0645;;;;N;;;;;
+FC19;ARABIC LIGATURE KHAH WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 062E 062C;;;;N;;;;;
+FC1A;ARABIC LIGATURE KHAH WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 062E 062D;;;;N;;;;;
+FC1B;ARABIC LIGATURE KHAH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 062E 0645;;;;N;;;;;
+FC1C;ARABIC LIGATURE SEEN WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0633 062C;;;;N;;;;;
+FC1D;ARABIC LIGATURE SEEN WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0633 062D;;;;N;;;;;
+FC1E;ARABIC LIGATURE SEEN WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 0633 062E;;;;N;;;;;
+FC1F;ARABIC LIGATURE SEEN WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0633 0645;;;;N;;;;;
+FC20;ARABIC LIGATURE SAD WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0635 062D;;;;N;;;;;
+FC21;ARABIC LIGATURE SAD WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0635 0645;;;;N;;;;;
+FC22;ARABIC LIGATURE DAD WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0636 062C;;;;N;;;;;
+FC23;ARABIC LIGATURE DAD WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0636 062D;;;;N;;;;;
+FC24;ARABIC LIGATURE DAD WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 0636 062E;;;;N;;;;;
+FC25;ARABIC LIGATURE DAD WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0636 0645;;;;N;;;;;
+FC26;ARABIC LIGATURE TAH WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0637 062D;;;;N;;;;;
+FC27;ARABIC LIGATURE TAH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0637 0645;;;;N;;;;;
+FC28;ARABIC LIGATURE ZAH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0638 0645;;;;N;;;;;
+FC29;ARABIC LIGATURE AIN WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0639 062C;;;;N;;;;;
+FC2A;ARABIC LIGATURE AIN WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0639 0645;;;;N;;;;;
+FC2B;ARABIC LIGATURE GHAIN WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 063A 062C;;;;N;;;;;
+FC2C;ARABIC LIGATURE GHAIN WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 063A 0645;;;;N;;;;;
+FC2D;ARABIC LIGATURE FEH WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0641 062C;;;;N;;;;;
+FC2E;ARABIC LIGATURE FEH WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0641 062D;;;;N;;;;;
+FC2F;ARABIC LIGATURE FEH WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 0641 062E;;;;N;;;;;
+FC30;ARABIC LIGATURE FEH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0641 0645;;;;N;;;;;
+FC31;ARABIC LIGATURE FEH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0641 0649;;;;N;;;;;
+FC32;ARABIC LIGATURE FEH WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0641 064A;;;;N;;;;;
+FC33;ARABIC LIGATURE QAF WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0642 062D;;;;N;;;;;
+FC34;ARABIC LIGATURE QAF WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0642 0645;;;;N;;;;;
+FC35;ARABIC LIGATURE QAF WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0642 0649;;;;N;;;;;
+FC36;ARABIC LIGATURE QAF WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0642 064A;;;;N;;;;;
+FC37;ARABIC LIGATURE KAF WITH ALEF ISOLATED FORM;Lo;0;AL;<isolated> 0643 0627;;;;N;;;;;
+FC38;ARABIC LIGATURE KAF WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0643 062C;;;;N;;;;;
+FC39;ARABIC LIGATURE KAF WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0643 062D;;;;N;;;;;
+FC3A;ARABIC LIGATURE KAF WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 0643 062E;;;;N;;;;;
+FC3B;ARABIC LIGATURE KAF WITH LAM ISOLATED FORM;Lo;0;AL;<isolated> 0643 0644;;;;N;;;;;
+FC3C;ARABIC LIGATURE KAF WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0643 0645;;;;N;;;;;
+FC3D;ARABIC LIGATURE KAF WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0643 0649;;;;N;;;;;
+FC3E;ARABIC LIGATURE KAF WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0643 064A;;;;N;;;;;
+FC3F;ARABIC LIGATURE LAM WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0644 062C;;;;N;;;;;
+FC40;ARABIC LIGATURE LAM WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0644 062D;;;;N;;;;;
+FC41;ARABIC LIGATURE LAM WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 0644 062E;;;;N;;;;;
+FC42;ARABIC LIGATURE LAM WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0644 0645;;;;N;;;;;
+FC43;ARABIC LIGATURE LAM WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0644 0649;;;;N;;;;;
+FC44;ARABIC LIGATURE LAM WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0644 064A;;;;N;;;;;
+FC45;ARABIC LIGATURE MEEM WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0645 062C;;;;N;;;;;
+FC46;ARABIC LIGATURE MEEM WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0645 062D;;;;N;;;;;
+FC47;ARABIC LIGATURE MEEM WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 0645 062E;;;;N;;;;;
+FC48;ARABIC LIGATURE MEEM WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0645 0645;;;;N;;;;;
+FC49;ARABIC LIGATURE MEEM WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0645 0649;;;;N;;;;;
+FC4A;ARABIC LIGATURE MEEM WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0645 064A;;;;N;;;;;
+FC4B;ARABIC LIGATURE NOON WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0646 062C;;;;N;;;;;
+FC4C;ARABIC LIGATURE NOON WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0646 062D;;;;N;;;;;
+FC4D;ARABIC LIGATURE NOON WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 0646 062E;;;;N;;;;;
+FC4E;ARABIC LIGATURE NOON WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0646 0645;;;;N;;;;;
+FC4F;ARABIC LIGATURE NOON WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0646 0649;;;;N;;;;;
+FC50;ARABIC LIGATURE NOON WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0646 064A;;;;N;;;;;
+FC51;ARABIC LIGATURE HEH WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0647 062C;;;;N;;;;;
+FC52;ARABIC LIGATURE HEH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0647 0645;;;;N;;;;;
+FC53;ARABIC LIGATURE HEH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0647 0649;;;;N;;;;;
+FC54;ARABIC LIGATURE HEH WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0647 064A;;;;N;;;;;
+FC55;ARABIC LIGATURE YEH WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 064A 062C;;;;N;;;;;
+FC56;ARABIC LIGATURE YEH WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 064A 062D;;;;N;;;;;
+FC57;ARABIC LIGATURE YEH WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 064A 062E;;;;N;;;;;
+FC58;ARABIC LIGATURE YEH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 064A 0645;;;;N;;;;;
+FC59;ARABIC LIGATURE YEH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 064A 0649;;;;N;;;;;
+FC5A;ARABIC LIGATURE YEH WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 064A 064A;;;;N;;;;;
+FC5B;ARABIC LIGATURE THAL WITH SUPERSCRIPT ALEF ISOLATED FORM;Lo;0;AL;<isolated> 0630 0670;;;;N;;;;;
+FC5C;ARABIC LIGATURE REH WITH SUPERSCRIPT ALEF ISOLATED FORM;Lo;0;AL;<isolated> 0631 0670;;;;N;;;;;
+FC5D;ARABIC LIGATURE ALEF MAKSURA WITH SUPERSCRIPT ALEF ISOLATED FORM;Lo;0;AL;<isolated> 0649 0670;;;;N;;;;;
+FC5E;ARABIC LIGATURE SHADDA WITH DAMMATAN ISOLATED FORM;Lo;0;AL;<isolated> 0020 064C 0651;;;;N;;;;;
+FC5F;ARABIC LIGATURE SHADDA WITH KASRATAN ISOLATED FORM;Lo;0;AL;<isolated> 0020 064D 0651;;;;N;;;;;
+FC60;ARABIC LIGATURE SHADDA WITH FATHA ISOLATED FORM;Lo;0;AL;<isolated> 0020 064E 0651;;;;N;;;;;
+FC61;ARABIC LIGATURE SHADDA WITH DAMMA ISOLATED FORM;Lo;0;AL;<isolated> 0020 064F 0651;;;;N;;;;;
+FC62;ARABIC LIGATURE SHADDA WITH KASRA ISOLATED FORM;Lo;0;AL;<isolated> 0020 0650 0651;;;;N;;;;;
+FC63;ARABIC LIGATURE SHADDA WITH SUPERSCRIPT ALEF ISOLATED FORM;Lo;0;AL;<isolated> 0020 0651 0670;;;;N;;;;;
+FC64;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH REH FINAL FORM;Lo;0;AL;<final> 0626 0631;;;;N;;;;;
+FC65;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ZAIN FINAL FORM;Lo;0;AL;<final> 0626 0632;;;;N;;;;;
+FC66;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH MEEM FINAL FORM;Lo;0;AL;<final> 0626 0645;;;;N;;;;;
+FC67;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH NOON FINAL FORM;Lo;0;AL;<final> 0626 0646;;;;N;;;;;
+FC68;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0626 0649;;;;N;;;;;
+FC69;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH YEH FINAL FORM;Lo;0;AL;<final> 0626 064A;;;;N;;;;;
+FC6A;ARABIC LIGATURE BEH WITH REH FINAL FORM;Lo;0;AL;<final> 0628 0631;;;;N;;;;;
+FC6B;ARABIC LIGATURE BEH WITH ZAIN FINAL FORM;Lo;0;AL;<final> 0628 0632;;;;N;;;;;
+FC6C;ARABIC LIGATURE BEH WITH MEEM FINAL FORM;Lo;0;AL;<final> 0628 0645;;;;N;;;;;
+FC6D;ARABIC LIGATURE BEH WITH NOON FINAL FORM;Lo;0;AL;<final> 0628 0646;;;;N;;;;;
+FC6E;ARABIC LIGATURE BEH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0628 0649;;;;N;;;;;
+FC6F;ARABIC LIGATURE BEH WITH YEH FINAL FORM;Lo;0;AL;<final> 0628 064A;;;;N;;;;;
+FC70;ARABIC LIGATURE TEH WITH REH FINAL FORM;Lo;0;AL;<final> 062A 0631;;;;N;;;;;
+FC71;ARABIC LIGATURE TEH WITH ZAIN FINAL FORM;Lo;0;AL;<final> 062A 0632;;;;N;;;;;
+FC72;ARABIC LIGATURE TEH WITH MEEM FINAL FORM;Lo;0;AL;<final> 062A 0645;;;;N;;;;;
+FC73;ARABIC LIGATURE TEH WITH NOON FINAL FORM;Lo;0;AL;<final> 062A 0646;;;;N;;;;;
+FC74;ARABIC LIGATURE TEH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062A 0649;;;;N;;;;;
+FC75;ARABIC LIGATURE TEH WITH YEH FINAL FORM;Lo;0;AL;<final> 062A 064A;;;;N;;;;;
+FC76;ARABIC LIGATURE THEH WITH REH FINAL FORM;Lo;0;AL;<final> 062B 0631;;;;N;;;;;
+FC77;ARABIC LIGATURE THEH WITH ZAIN FINAL FORM;Lo;0;AL;<final> 062B 0632;;;;N;;;;;
+FC78;ARABIC LIGATURE THEH WITH MEEM FINAL FORM;Lo;0;AL;<final> 062B 0645;;;;N;;;;;
+FC79;ARABIC LIGATURE THEH WITH NOON FINAL FORM;Lo;0;AL;<final> 062B 0646;;;;N;;;;;
+FC7A;ARABIC LIGATURE THEH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062B 0649;;;;N;;;;;
+FC7B;ARABIC LIGATURE THEH WITH YEH FINAL FORM;Lo;0;AL;<final> 062B 064A;;;;N;;;;;
+FC7C;ARABIC LIGATURE FEH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0641 0649;;;;N;;;;;
+FC7D;ARABIC LIGATURE FEH WITH YEH FINAL FORM;Lo;0;AL;<final> 0641 064A;;;;N;;;;;
+FC7E;ARABIC LIGATURE QAF WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0642 0649;;;;N;;;;;
+FC7F;ARABIC LIGATURE QAF WITH YEH FINAL FORM;Lo;0;AL;<final> 0642 064A;;;;N;;;;;
+FC80;ARABIC LIGATURE KAF WITH ALEF FINAL FORM;Lo;0;AL;<final> 0643 0627;;;;N;;;;;
+FC81;ARABIC LIGATURE KAF WITH LAM FINAL FORM;Lo;0;AL;<final> 0643 0644;;;;N;;;;;
+FC82;ARABIC LIGATURE KAF WITH MEEM FINAL FORM;Lo;0;AL;<final> 0643 0645;;;;N;;;;;
+FC83;ARABIC LIGATURE KAF WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0643 0649;;;;N;;;;;
+FC84;ARABIC LIGATURE KAF WITH YEH FINAL FORM;Lo;0;AL;<final> 0643 064A;;;;N;;;;;
+FC85;ARABIC LIGATURE LAM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0644 0645;;;;N;;;;;
+FC86;ARABIC LIGATURE LAM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0644 0649;;;;N;;;;;
+FC87;ARABIC LIGATURE LAM WITH YEH FINAL FORM;Lo;0;AL;<final> 0644 064A;;;;N;;;;;
+FC88;ARABIC LIGATURE MEEM WITH ALEF FINAL FORM;Lo;0;AL;<final> 0645 0627;;;;N;;;;;
+FC89;ARABIC LIGATURE MEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0645 0645;;;;N;;;;;
+FC8A;ARABIC LIGATURE NOON WITH REH FINAL FORM;Lo;0;AL;<final> 0646 0631;;;;N;;;;;
+FC8B;ARABIC LIGATURE NOON WITH ZAIN FINAL FORM;Lo;0;AL;<final> 0646 0632;;;;N;;;;;
+FC8C;ARABIC LIGATURE NOON WITH MEEM FINAL FORM;Lo;0;AL;<final> 0646 0645;;;;N;;;;;
+FC8D;ARABIC LIGATURE NOON WITH NOON FINAL FORM;Lo;0;AL;<final> 0646 0646;;;;N;;;;;
+FC8E;ARABIC LIGATURE NOON WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0646 0649;;;;N;;;;;
+FC8F;ARABIC LIGATURE NOON WITH YEH FINAL FORM;Lo;0;AL;<final> 0646 064A;;;;N;;;;;
+FC90;ARABIC LIGATURE ALEF MAKSURA WITH SUPERSCRIPT ALEF FINAL FORM;Lo;0;AL;<final> 0649 0670;;;;N;;;;;
+FC91;ARABIC LIGATURE YEH WITH REH FINAL FORM;Lo;0;AL;<final> 064A 0631;;;;N;;;;;
+FC92;ARABIC LIGATURE YEH WITH ZAIN FINAL FORM;Lo;0;AL;<final> 064A 0632;;;;N;;;;;
+FC93;ARABIC LIGATURE YEH WITH MEEM FINAL FORM;Lo;0;AL;<final> 064A 0645;;;;N;;;;;
+FC94;ARABIC LIGATURE YEH WITH NOON FINAL FORM;Lo;0;AL;<final> 064A 0646;;;;N;;;;;
+FC95;ARABIC LIGATURE YEH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 064A 0649;;;;N;;;;;
+FC96;ARABIC LIGATURE YEH WITH YEH FINAL FORM;Lo;0;AL;<final> 064A 064A;;;;N;;;;;
+FC97;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0626 062C;;;;N;;;;;
+FC98;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0626 062D;;;;N;;;;;
+FC99;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0626 062E;;;;N;;;;;
+FC9A;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0626 0645;;;;N;;;;;
+FC9B;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH HEH INITIAL FORM;Lo;0;AL;<initial> 0626 0647;;;;N;;;;;
+FC9C;ARABIC LIGATURE BEH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0628 062C;;;;N;;;;;
+FC9D;ARABIC LIGATURE BEH WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0628 062D;;;;N;;;;;
+FC9E;ARABIC LIGATURE BEH WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0628 062E;;;;N;;;;;
+FC9F;ARABIC LIGATURE BEH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0628 0645;;;;N;;;;;
+FCA0;ARABIC LIGATURE BEH WITH HEH INITIAL FORM;Lo;0;AL;<initial> 0628 0647;;;;N;;;;;
+FCA1;ARABIC LIGATURE TEH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 062A 062C;;;;N;;;;;
+FCA2;ARABIC LIGATURE TEH WITH HAH INITIAL FORM;Lo;0;AL;<initial> 062A 062D;;;;N;;;;;
+FCA3;ARABIC LIGATURE TEH WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 062A 062E;;;;N;;;;;
+FCA4;ARABIC LIGATURE TEH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 062A 0645;;;;N;;;;;
+FCA5;ARABIC LIGATURE TEH WITH HEH INITIAL FORM;Lo;0;AL;<initial> 062A 0647;;;;N;;;;;
+FCA6;ARABIC LIGATURE THEH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 062B 0645;;;;N;;;;;
+FCA7;ARABIC LIGATURE JEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 062C 062D;;;;N;;;;;
+FCA8;ARABIC LIGATURE JEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 062C 0645;;;;N;;;;;
+FCA9;ARABIC LIGATURE HAH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 062D 062C;;;;N;;;;;
+FCAA;ARABIC LIGATURE HAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 062D 0645;;;;N;;;;;
+FCAB;ARABIC LIGATURE KHAH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 062E 062C;;;;N;;;;;
+FCAC;ARABIC LIGATURE KHAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 062E 0645;;;;N;;;;;
+FCAD;ARABIC LIGATURE SEEN WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0633 062C;;;;N;;;;;
+FCAE;ARABIC LIGATURE SEEN WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0633 062D;;;;N;;;;;
+FCAF;ARABIC LIGATURE SEEN WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0633 062E;;;;N;;;;;
+FCB0;ARABIC LIGATURE SEEN WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0633 0645;;;;N;;;;;
+FCB1;ARABIC LIGATURE SAD WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0635 062D;;;;N;;;;;
+FCB2;ARABIC LIGATURE SAD WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0635 062E;;;;N;;;;;
+FCB3;ARABIC LIGATURE SAD WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0635 0645;;;;N;;;;;
+FCB4;ARABIC LIGATURE DAD WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0636 062C;;;;N;;;;;
+FCB5;ARABIC LIGATURE DAD WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0636 062D;;;;N;;;;;
+FCB6;ARABIC LIGATURE DAD WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0636 062E;;;;N;;;;;
+FCB7;ARABIC LIGATURE DAD WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0636 0645;;;;N;;;;;
+FCB8;ARABIC LIGATURE TAH WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0637 062D;;;;N;;;;;
+FCB9;ARABIC LIGATURE ZAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0638 0645;;;;N;;;;;
+FCBA;ARABIC LIGATURE AIN WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0639 062C;;;;N;;;;;
+FCBB;ARABIC LIGATURE AIN WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0639 0645;;;;N;;;;;
+FCBC;ARABIC LIGATURE GHAIN WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 063A 062C;;;;N;;;;;
+FCBD;ARABIC LIGATURE GHAIN WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 063A 0645;;;;N;;;;;
+FCBE;ARABIC LIGATURE FEH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0641 062C;;;;N;;;;;
+FCBF;ARABIC LIGATURE FEH WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0641 062D;;;;N;;;;;
+FCC0;ARABIC LIGATURE FEH WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0641 062E;;;;N;;;;;
+FCC1;ARABIC LIGATURE FEH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0641 0645;;;;N;;;;;
+FCC2;ARABIC LIGATURE QAF WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0642 062D;;;;N;;;;;
+FCC3;ARABIC LIGATURE QAF WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0642 0645;;;;N;;;;;
+FCC4;ARABIC LIGATURE KAF WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0643 062C;;;;N;;;;;
+FCC5;ARABIC LIGATURE KAF WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0643 062D;;;;N;;;;;
+FCC6;ARABIC LIGATURE KAF WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0643 062E;;;;N;;;;;
+FCC7;ARABIC LIGATURE KAF WITH LAM INITIAL FORM;Lo;0;AL;<initial> 0643 0644;;;;N;;;;;
+FCC8;ARABIC LIGATURE KAF WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0643 0645;;;;N;;;;;
+FCC9;ARABIC LIGATURE LAM WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0644 062C;;;;N;;;;;
+FCCA;ARABIC LIGATURE LAM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0644 062D;;;;N;;;;;
+FCCB;ARABIC LIGATURE LAM WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0644 062E;;;;N;;;;;
+FCCC;ARABIC LIGATURE LAM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0644 0645;;;;N;;;;;
+FCCD;ARABIC LIGATURE LAM WITH HEH INITIAL FORM;Lo;0;AL;<initial> 0644 0647;;;;N;;;;;
+FCCE;ARABIC LIGATURE MEEM WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0645 062C;;;;N;;;;;
+FCCF;ARABIC LIGATURE MEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0645 062D;;;;N;;;;;
+FCD0;ARABIC LIGATURE MEEM WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0645 062E;;;;N;;;;;
+FCD1;ARABIC LIGATURE MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0645 0645;;;;N;;;;;
+FCD2;ARABIC LIGATURE NOON WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0646 062C;;;;N;;;;;
+FCD3;ARABIC LIGATURE NOON WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0646 062D;;;;N;;;;;
+FCD4;ARABIC LIGATURE NOON WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0646 062E;;;;N;;;;;
+FCD5;ARABIC LIGATURE NOON WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0646 0645;;;;N;;;;;
+FCD6;ARABIC LIGATURE NOON WITH HEH INITIAL FORM;Lo;0;AL;<initial> 0646 0647;;;;N;;;;;
+FCD7;ARABIC LIGATURE HEH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0647 062C;;;;N;;;;;
+FCD8;ARABIC LIGATURE HEH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0647 0645;;;;N;;;;;
+FCD9;ARABIC LIGATURE HEH WITH SUPERSCRIPT ALEF INITIAL FORM;Lo;0;AL;<initial> 0647 0670;;;;N;;;;;
+FCDA;ARABIC LIGATURE YEH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 064A 062C;;;;N;;;;;
+FCDB;ARABIC LIGATURE YEH WITH HAH INITIAL FORM;Lo;0;AL;<initial> 064A 062D;;;;N;;;;;
+FCDC;ARABIC LIGATURE YEH WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 064A 062E;;;;N;;;;;
+FCDD;ARABIC LIGATURE YEH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 064A 0645;;;;N;;;;;
+FCDE;ARABIC LIGATURE YEH WITH HEH INITIAL FORM;Lo;0;AL;<initial> 064A 0647;;;;N;;;;;
+FCDF;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 0626 0645;;;;N;;;;;
+FCE0;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH HEH MEDIAL FORM;Lo;0;AL;<medial> 0626 0647;;;;N;;;;;
+FCE1;ARABIC LIGATURE BEH WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 0628 0645;;;;N;;;;;
+FCE2;ARABIC LIGATURE BEH WITH HEH MEDIAL FORM;Lo;0;AL;<medial> 0628 0647;;;;N;;;;;
+FCE3;ARABIC LIGATURE TEH WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 062A 0645;;;;N;;;;;
+FCE4;ARABIC LIGATURE TEH WITH HEH MEDIAL FORM;Lo;0;AL;<medial> 062A 0647;;;;N;;;;;
+FCE5;ARABIC LIGATURE THEH WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 062B 0645;;;;N;;;;;
+FCE6;ARABIC LIGATURE THEH WITH HEH MEDIAL FORM;Lo;0;AL;<medial> 062B 0647;;;;N;;;;;
+FCE7;ARABIC LIGATURE SEEN WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 0633 0645;;;;N;;;;;
+FCE8;ARABIC LIGATURE SEEN WITH HEH MEDIAL FORM;Lo;0;AL;<medial> 0633 0647;;;;N;;;;;
+FCE9;ARABIC LIGATURE SHEEN WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 0634 0645;;;;N;;;;;
+FCEA;ARABIC LIGATURE SHEEN WITH HEH MEDIAL FORM;Lo;0;AL;<medial> 0634 0647;;;;N;;;;;
+FCEB;ARABIC LIGATURE KAF WITH LAM MEDIAL FORM;Lo;0;AL;<medial> 0643 0644;;;;N;;;;;
+FCEC;ARABIC LIGATURE KAF WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 0643 0645;;;;N;;;;;
+FCED;ARABIC LIGATURE LAM WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 0644 0645;;;;N;;;;;
+FCEE;ARABIC LIGATURE NOON WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 0646 0645;;;;N;;;;;
+FCEF;ARABIC LIGATURE NOON WITH HEH MEDIAL FORM;Lo;0;AL;<medial> 0646 0647;;;;N;;;;;
+FCF0;ARABIC LIGATURE YEH WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 064A 0645;;;;N;;;;;
+FCF1;ARABIC LIGATURE YEH WITH HEH MEDIAL FORM;Lo;0;AL;<medial> 064A 0647;;;;N;;;;;
+FCF2;ARABIC LIGATURE SHADDA WITH FATHA MEDIAL FORM;Lo;0;AL;<medial> 0640 064E 0651;;;;N;;;;;
+FCF3;ARABIC LIGATURE SHADDA WITH DAMMA MEDIAL FORM;Lo;0;AL;<medial> 0640 064F 0651;;;;N;;;;;
+FCF4;ARABIC LIGATURE SHADDA WITH KASRA MEDIAL FORM;Lo;0;AL;<medial> 0640 0650 0651;;;;N;;;;;
+FCF5;ARABIC LIGATURE TAH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0637 0649;;;;N;;;;;
+FCF6;ARABIC LIGATURE TAH WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0637 064A;;;;N;;;;;
+FCF7;ARABIC LIGATURE AIN WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0639 0649;;;;N;;;;;
+FCF8;ARABIC LIGATURE AIN WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0639 064A;;;;N;;;;;
+FCF9;ARABIC LIGATURE GHAIN WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 063A 0649;;;;N;;;;;
+FCFA;ARABIC LIGATURE GHAIN WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 063A 064A;;;;N;;;;;
+FCFB;ARABIC LIGATURE SEEN WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0633 0649;;;;N;;;;;
+FCFC;ARABIC LIGATURE SEEN WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0633 064A;;;;N;;;;;
+FCFD;ARABIC LIGATURE SHEEN WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0634 0649;;;;N;;;;;
+FCFE;ARABIC LIGATURE SHEEN WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0634 064A;;;;N;;;;;
+FCFF;ARABIC LIGATURE HAH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 062D 0649;;;;N;;;;;
+FD00;ARABIC LIGATURE HAH WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 062D 064A;;;;N;;;;;
+FD01;ARABIC LIGATURE JEEM WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 062C 0649;;;;N;;;;;
+FD02;ARABIC LIGATURE JEEM WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 062C 064A;;;;N;;;;;
+FD03;ARABIC LIGATURE KHAH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 062E 0649;;;;N;;;;;
+FD04;ARABIC LIGATURE KHAH WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 062E 064A;;;;N;;;;;
+FD05;ARABIC LIGATURE SAD WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0635 0649;;;;N;;;;;
+FD06;ARABIC LIGATURE SAD WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0635 064A;;;;N;;;;;
+FD07;ARABIC LIGATURE DAD WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0636 0649;;;;N;;;;;
+FD08;ARABIC LIGATURE DAD WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0636 064A;;;;N;;;;;
+FD09;ARABIC LIGATURE SHEEN WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0634 062C;;;;N;;;;;
+FD0A;ARABIC LIGATURE SHEEN WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0634 062D;;;;N;;;;;
+FD0B;ARABIC LIGATURE SHEEN WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 0634 062E;;;;N;;;;;
+FD0C;ARABIC LIGATURE SHEEN WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0634 0645;;;;N;;;;;
+FD0D;ARABIC LIGATURE SHEEN WITH REH ISOLATED FORM;Lo;0;AL;<isolated> 0634 0631;;;;N;;;;;
+FD0E;ARABIC LIGATURE SEEN WITH REH ISOLATED FORM;Lo;0;AL;<isolated> 0633 0631;;;;N;;;;;
+FD0F;ARABIC LIGATURE SAD WITH REH ISOLATED FORM;Lo;0;AL;<isolated> 0635 0631;;;;N;;;;;
+FD10;ARABIC LIGATURE DAD WITH REH ISOLATED FORM;Lo;0;AL;<isolated> 0636 0631;;;;N;;;;;
+FD11;ARABIC LIGATURE TAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0637 0649;;;;N;;;;;
+FD12;ARABIC LIGATURE TAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0637 064A;;;;N;;;;;
+FD13;ARABIC LIGATURE AIN WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0639 0649;;;;N;;;;;
+FD14;ARABIC LIGATURE AIN WITH YEH FINAL FORM;Lo;0;AL;<final> 0639 064A;;;;N;;;;;
+FD15;ARABIC LIGATURE GHAIN WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 063A 0649;;;;N;;;;;
+FD16;ARABIC LIGATURE GHAIN WITH YEH FINAL FORM;Lo;0;AL;<final> 063A 064A;;;;N;;;;;
+FD17;ARABIC LIGATURE SEEN WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0633 0649;;;;N;;;;;
+FD18;ARABIC LIGATURE SEEN WITH YEH FINAL FORM;Lo;0;AL;<final> 0633 064A;;;;N;;;;;
+FD19;ARABIC LIGATURE SHEEN WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0634 0649;;;;N;;;;;
+FD1A;ARABIC LIGATURE SHEEN WITH YEH FINAL FORM;Lo;0;AL;<final> 0634 064A;;;;N;;;;;
+FD1B;ARABIC LIGATURE HAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062D 0649;;;;N;;;;;
+FD1C;ARABIC LIGATURE HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 062D 064A;;;;N;;;;;
+FD1D;ARABIC LIGATURE JEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062C 0649;;;;N;;;;;
+FD1E;ARABIC LIGATURE JEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 062C 064A;;;;N;;;;;
+FD1F;ARABIC LIGATURE KHAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062E 0649;;;;N;;;;;
+FD20;ARABIC LIGATURE KHAH WITH YEH FINAL FORM;Lo;0;AL;<final> 062E 064A;;;;N;;;;;
+FD21;ARABIC LIGATURE SAD WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0635 0649;;;;N;;;;;
+FD22;ARABIC LIGATURE SAD WITH YEH FINAL FORM;Lo;0;AL;<final> 0635 064A;;;;N;;;;;
+FD23;ARABIC LIGATURE DAD WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0636 0649;;;;N;;;;;
+FD24;ARABIC LIGATURE DAD WITH YEH FINAL FORM;Lo;0;AL;<final> 0636 064A;;;;N;;;;;
+FD25;ARABIC LIGATURE SHEEN WITH JEEM FINAL FORM;Lo;0;AL;<final> 0634 062C;;;;N;;;;;
+FD26;ARABIC LIGATURE SHEEN WITH HAH FINAL FORM;Lo;0;AL;<final> 0634 062D;;;;N;;;;;
+FD27;ARABIC LIGATURE SHEEN WITH KHAH FINAL FORM;Lo;0;AL;<final> 0634 062E;;;;N;;;;;
+FD28;ARABIC LIGATURE SHEEN WITH MEEM FINAL FORM;Lo;0;AL;<final> 0634 0645;;;;N;;;;;
+FD29;ARABIC LIGATURE SHEEN WITH REH FINAL FORM;Lo;0;AL;<final> 0634 0631;;;;N;;;;;
+FD2A;ARABIC LIGATURE SEEN WITH REH FINAL FORM;Lo;0;AL;<final> 0633 0631;;;;N;;;;;
+FD2B;ARABIC LIGATURE SAD WITH REH FINAL FORM;Lo;0;AL;<final> 0635 0631;;;;N;;;;;
+FD2C;ARABIC LIGATURE DAD WITH REH FINAL FORM;Lo;0;AL;<final> 0636 0631;;;;N;;;;;
+FD2D;ARABIC LIGATURE SHEEN WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0634 062C;;;;N;;;;;
+FD2E;ARABIC LIGATURE SHEEN WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0634 062D;;;;N;;;;;
+FD2F;ARABIC LIGATURE SHEEN WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0634 062E;;;;N;;;;;
+FD30;ARABIC LIGATURE SHEEN WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0634 0645;;;;N;;;;;
+FD31;ARABIC LIGATURE SEEN WITH HEH INITIAL FORM;Lo;0;AL;<initial> 0633 0647;;;;N;;;;;
+FD32;ARABIC LIGATURE SHEEN WITH HEH INITIAL FORM;Lo;0;AL;<initial> 0634 0647;;;;N;;;;;
+FD33;ARABIC LIGATURE TAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0637 0645;;;;N;;;;;
+FD34;ARABIC LIGATURE SEEN WITH JEEM MEDIAL FORM;Lo;0;AL;<medial> 0633 062C;;;;N;;;;;
+FD35;ARABIC LIGATURE SEEN WITH HAH MEDIAL FORM;Lo;0;AL;<medial> 0633 062D;;;;N;;;;;
+FD36;ARABIC LIGATURE SEEN WITH KHAH MEDIAL FORM;Lo;0;AL;<medial> 0633 062E;;;;N;;;;;
+FD37;ARABIC LIGATURE SHEEN WITH JEEM MEDIAL FORM;Lo;0;AL;<medial> 0634 062C;;;;N;;;;;
+FD38;ARABIC LIGATURE SHEEN WITH HAH MEDIAL FORM;Lo;0;AL;<medial> 0634 062D;;;;N;;;;;
+FD39;ARABIC LIGATURE SHEEN WITH KHAH MEDIAL FORM;Lo;0;AL;<medial> 0634 062E;;;;N;;;;;
+FD3A;ARABIC LIGATURE TAH WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 0637 0645;;;;N;;;;;
+FD3B;ARABIC LIGATURE ZAH WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 0638 0645;;;;N;;;;;
+FD3C;ARABIC LIGATURE ALEF WITH FATHATAN FINAL FORM;Lo;0;AL;<final> 0627 064B;;;;N;;;;;
+FD3D;ARABIC LIGATURE ALEF WITH FATHATAN ISOLATED FORM;Lo;0;AL;<isolated> 0627 064B;;;;N;;;;;
+FD3E;ORNATE LEFT PARENTHESIS;Ps;0;ON;;;;;N;;;;;
+FD3F;ORNATE RIGHT PARENTHESIS;Pe;0;ON;;;;;N;;;;;
+FD50;ARABIC LIGATURE TEH WITH JEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 062A 062C 0645;;;;N;;;;;
+FD51;ARABIC LIGATURE TEH WITH HAH WITH JEEM FINAL FORM;Lo;0;AL;<final> 062A 062D 062C;;;;N;;;;;
+FD52;ARABIC LIGATURE TEH WITH HAH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 062A 062D 062C;;;;N;;;;;
+FD53;ARABIC LIGATURE TEH WITH HAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 062A 062D 0645;;;;N;;;;;
+FD54;ARABIC LIGATURE TEH WITH KHAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 062A 062E 0645;;;;N;;;;;
+FD55;ARABIC LIGATURE TEH WITH MEEM WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 062A 0645 062C;;;;N;;;;;
+FD56;ARABIC LIGATURE TEH WITH MEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 062A 0645 062D;;;;N;;;;;
+FD57;ARABIC LIGATURE TEH WITH MEEM WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 062A 0645 062E;;;;N;;;;;
+FD58;ARABIC LIGATURE JEEM WITH MEEM WITH HAH FINAL FORM;Lo;0;AL;<final> 062C 0645 062D;;;;N;;;;;
+FD59;ARABIC LIGATURE JEEM WITH MEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 062C 0645 062D;;;;N;;;;;
+FD5A;ARABIC LIGATURE HAH WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 062D 0645 064A;;;;N;;;;;
+FD5B;ARABIC LIGATURE HAH WITH MEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062D 0645 0649;;;;N;;;;;
+FD5C;ARABIC LIGATURE SEEN WITH HAH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0633 062D 062C;;;;N;;;;;
+FD5D;ARABIC LIGATURE SEEN WITH JEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0633 062C 062D;;;;N;;;;;
+FD5E;ARABIC LIGATURE SEEN WITH JEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0633 062C 0649;;;;N;;;;;
+FD5F;ARABIC LIGATURE SEEN WITH MEEM WITH HAH FINAL FORM;Lo;0;AL;<final> 0633 0645 062D;;;;N;;;;;
+FD60;ARABIC LIGATURE SEEN WITH MEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0633 0645 062D;;;;N;;;;;
+FD61;ARABIC LIGATURE SEEN WITH MEEM WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0633 0645 062C;;;;N;;;;;
+FD62;ARABIC LIGATURE SEEN WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0633 0645 0645;;;;N;;;;;
+FD63;ARABIC LIGATURE SEEN WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0633 0645 0645;;;;N;;;;;
+FD64;ARABIC LIGATURE SAD WITH HAH WITH HAH FINAL FORM;Lo;0;AL;<final> 0635 062D 062D;;;;N;;;;;
+FD65;ARABIC LIGATURE SAD WITH HAH WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0635 062D 062D;;;;N;;;;;
+FD66;ARABIC LIGATURE SAD WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0635 0645 0645;;;;N;;;;;
+FD67;ARABIC LIGATURE SHEEN WITH HAH WITH MEEM FINAL FORM;Lo;0;AL;<final> 0634 062D 0645;;;;N;;;;;
+FD68;ARABIC LIGATURE SHEEN WITH HAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0634 062D 0645;;;;N;;;;;
+FD69;ARABIC LIGATURE SHEEN WITH JEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0634 062C 064A;;;;N;;;;;
+FD6A;ARABIC LIGATURE SHEEN WITH MEEM WITH KHAH FINAL FORM;Lo;0;AL;<final> 0634 0645 062E;;;;N;;;;;
+FD6B;ARABIC LIGATURE SHEEN WITH MEEM WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0634 0645 062E;;;;N;;;;;
+FD6C;ARABIC LIGATURE SHEEN WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0634 0645 0645;;;;N;;;;;
+FD6D;ARABIC LIGATURE SHEEN WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0634 0645 0645;;;;N;;;;;
+FD6E;ARABIC LIGATURE DAD WITH HAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0636 062D 0649;;;;N;;;;;
+FD6F;ARABIC LIGATURE DAD WITH KHAH WITH MEEM FINAL FORM;Lo;0;AL;<final> 0636 062E 0645;;;;N;;;;;
+FD70;ARABIC LIGATURE DAD WITH KHAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0636 062E 0645;;;;N;;;;;
+FD71;ARABIC LIGATURE TAH WITH MEEM WITH HAH FINAL FORM;Lo;0;AL;<final> 0637 0645 062D;;;;N;;;;;
+FD72;ARABIC LIGATURE TAH WITH MEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0637 0645 062D;;;;N;;;;;
+FD73;ARABIC LIGATURE TAH WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0637 0645 0645;;;;N;;;;;
+FD74;ARABIC LIGATURE TAH WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0637 0645 064A;;;;N;;;;;
+FD75;ARABIC LIGATURE AIN WITH JEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0639 062C 0645;;;;N;;;;;
+FD76;ARABIC LIGATURE AIN WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0639 0645 0645;;;;N;;;;;
+FD77;ARABIC LIGATURE AIN WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0639 0645 0645;;;;N;;;;;
+FD78;ARABIC LIGATURE AIN WITH MEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0639 0645 0649;;;;N;;;;;
+FD79;ARABIC LIGATURE GHAIN WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 063A 0645 0645;;;;N;;;;;
+FD7A;ARABIC LIGATURE GHAIN WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 063A 0645 064A;;;;N;;;;;
+FD7B;ARABIC LIGATURE GHAIN WITH MEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 063A 0645 0649;;;;N;;;;;
+FD7C;ARABIC LIGATURE FEH WITH KHAH WITH MEEM FINAL FORM;Lo;0;AL;<final> 0641 062E 0645;;;;N;;;;;
+FD7D;ARABIC LIGATURE FEH WITH KHAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0641 062E 0645;;;;N;;;;;
+FD7E;ARABIC LIGATURE QAF WITH MEEM WITH HAH FINAL FORM;Lo;0;AL;<final> 0642 0645 062D;;;;N;;;;;
+FD7F;ARABIC LIGATURE QAF WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0642 0645 0645;;;;N;;;;;
+FD80;ARABIC LIGATURE LAM WITH HAH WITH MEEM FINAL FORM;Lo;0;AL;<final> 0644 062D 0645;;;;N;;;;;
+FD81;ARABIC LIGATURE LAM WITH HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0644 062D 064A;;;;N;;;;;
+FD82;ARABIC LIGATURE LAM WITH HAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0644 062D 0649;;;;N;;;;;
+FD83;ARABIC LIGATURE LAM WITH JEEM WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0644 062C 062C;;;;N;;;;;
+FD84;ARABIC LIGATURE LAM WITH JEEM WITH JEEM FINAL FORM;Lo;0;AL;<final> 0644 062C 062C;;;;N;;;;;
+FD85;ARABIC LIGATURE LAM WITH KHAH WITH MEEM FINAL FORM;Lo;0;AL;<final> 0644 062E 0645;;;;N;;;;;
+FD86;ARABIC LIGATURE LAM WITH KHAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0644 062E 0645;;;;N;;;;;
+FD87;ARABIC LIGATURE LAM WITH MEEM WITH HAH FINAL FORM;Lo;0;AL;<final> 0644 0645 062D;;;;N;;;;;
+FD88;ARABIC LIGATURE LAM WITH MEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0644 0645 062D;;;;N;;;;;
+FD89;ARABIC LIGATURE MEEM WITH HAH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0645 062D 062C;;;;N;;;;;
+FD8A;ARABIC LIGATURE MEEM WITH HAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0645 062D 0645;;;;N;;;;;
+FD8B;ARABIC LIGATURE MEEM WITH HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0645 062D 064A;;;;N;;;;;
+FD8C;ARABIC LIGATURE MEEM WITH JEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0645 062C 062D;;;;N;;;;;
+FD8D;ARABIC LIGATURE MEEM WITH JEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0645 062C 0645;;;;N;;;;;
+FD8E;ARABIC LIGATURE MEEM WITH KHAH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0645 062E 062C;;;;N;;;;;
+FD8F;ARABIC LIGATURE MEEM WITH KHAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0645 062E 0645;;;;N;;;;;
+FD92;ARABIC LIGATURE MEEM WITH JEEM WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0645 062C 062E;;;;N;;;;;
+FD93;ARABIC LIGATURE HEH WITH MEEM WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0647 0645 062C;;;;N;;;;;
+FD94;ARABIC LIGATURE HEH WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0647 0645 0645;;;;N;;;;;
+FD95;ARABIC LIGATURE NOON WITH HAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0646 062D 0645;;;;N;;;;;
+FD96;ARABIC LIGATURE NOON WITH HAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0646 062D 0649;;;;N;;;;;
+FD97;ARABIC LIGATURE NOON WITH JEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0646 062C 0645;;;;N;;;;;
+FD98;ARABIC LIGATURE NOON WITH JEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0646 062C 0645;;;;N;;;;;
+FD99;ARABIC LIGATURE NOON WITH JEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0646 062C 0649;;;;N;;;;;
+FD9A;ARABIC LIGATURE NOON WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0646 0645 064A;;;;N;;;;;
+FD9B;ARABIC LIGATURE NOON WITH MEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0646 0645 0649;;;;N;;;;;
+FD9C;ARABIC LIGATURE YEH WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 064A 0645 0645;;;;N;;;;;
+FD9D;ARABIC LIGATURE YEH WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 064A 0645 0645;;;;N;;;;;
+FD9E;ARABIC LIGATURE BEH WITH KHAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0628 062E 064A;;;;N;;;;;
+FD9F;ARABIC LIGATURE TEH WITH JEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 062A 062C 064A;;;;N;;;;;
+FDA0;ARABIC LIGATURE TEH WITH JEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062A 062C 0649;;;;N;;;;;
+FDA1;ARABIC LIGATURE TEH WITH KHAH WITH YEH FINAL FORM;Lo;0;AL;<final> 062A 062E 064A;;;;N;;;;;
+FDA2;ARABIC LIGATURE TEH WITH KHAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062A 062E 0649;;;;N;;;;;
+FDA3;ARABIC LIGATURE TEH WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 062A 0645 064A;;;;N;;;;;
+FDA4;ARABIC LIGATURE TEH WITH MEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062A 0645 0649;;;;N;;;;;
+FDA5;ARABIC LIGATURE JEEM WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 062C 0645 064A;;;;N;;;;;
+FDA6;ARABIC LIGATURE JEEM WITH HAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062C 062D 0649;;;;N;;;;;
+FDA7;ARABIC LIGATURE JEEM WITH MEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062C 0645 0649;;;;N;;;;;
+FDA8;ARABIC LIGATURE SEEN WITH KHAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0633 062E 0649;;;;N;;;;;
+FDA9;ARABIC LIGATURE SAD WITH HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0635 062D 064A;;;;N;;;;;
+FDAA;ARABIC LIGATURE SHEEN WITH HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0634 062D 064A;;;;N;;;;;
+FDAB;ARABIC LIGATURE DAD WITH HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0636 062D 064A;;;;N;;;;;
+FDAC;ARABIC LIGATURE LAM WITH JEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0644 062C 064A;;;;N;;;;;
+FDAD;ARABIC LIGATURE LAM WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0644 0645 064A;;;;N;;;;;
+FDAE;ARABIC LIGATURE YEH WITH HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 064A 062D 064A;;;;N;;;;;
+FDAF;ARABIC LIGATURE YEH WITH JEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 064A 062C 064A;;;;N;;;;;
+FDB0;ARABIC LIGATURE YEH WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 064A 0645 064A;;;;N;;;;;
+FDB1;ARABIC LIGATURE MEEM WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0645 0645 064A;;;;N;;;;;
+FDB2;ARABIC LIGATURE QAF WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0642 0645 064A;;;;N;;;;;
+FDB3;ARABIC LIGATURE NOON WITH HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0646 062D 064A;;;;N;;;;;
+FDB4;ARABIC LIGATURE QAF WITH MEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0642 0645 062D;;;;N;;;;;
+FDB5;ARABIC LIGATURE LAM WITH HAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0644 062D 0645;;;;N;;;;;
+FDB6;ARABIC LIGATURE AIN WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0639 0645 064A;;;;N;;;;;
+FDB7;ARABIC LIGATURE KAF WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0643 0645 064A;;;;N;;;;;
+FDB8;ARABIC LIGATURE NOON WITH JEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0646 062C 062D;;;;N;;;;;
+FDB9;ARABIC LIGATURE MEEM WITH KHAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0645 062E 064A;;;;N;;;;;
+FDBA;ARABIC LIGATURE LAM WITH JEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0644 062C 0645;;;;N;;;;;
+FDBB;ARABIC LIGATURE KAF WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0643 0645 0645;;;;N;;;;;
+FDBC;ARABIC LIGATURE LAM WITH JEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0644 062C 0645;;;;N;;;;;
+FDBD;ARABIC LIGATURE NOON WITH JEEM WITH HAH FINAL FORM;Lo;0;AL;<final> 0646 062C 062D;;;;N;;;;;
+FDBE;ARABIC LIGATURE JEEM WITH HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 062C 062D 064A;;;;N;;;;;
+FDBF;ARABIC LIGATURE HAH WITH JEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 062D 062C 064A;;;;N;;;;;
+FDC0;ARABIC LIGATURE MEEM WITH JEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0645 062C 064A;;;;N;;;;;
+FDC1;ARABIC LIGATURE FEH WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0641 0645 064A;;;;N;;;;;
+FDC2;ARABIC LIGATURE BEH WITH HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0628 062D 064A;;;;N;;;;;
+FDC3;ARABIC LIGATURE KAF WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0643 0645 0645;;;;N;;;;;
+FDC4;ARABIC LIGATURE AIN WITH JEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0639 062C 0645;;;;N;;;;;
+FDC5;ARABIC LIGATURE SAD WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0635 0645 0645;;;;N;;;;;
+FDC6;ARABIC LIGATURE SEEN WITH KHAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0633 062E 064A;;;;N;;;;;
+FDC7;ARABIC LIGATURE NOON WITH JEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0646 062C 064A;;;;N;;;;;
+FDF0;ARABIC LIGATURE SALLA USED AS KORANIC STOP SIGN ISOLATED FORM;Lo;0;AL;<isolated> 0635 0644 06D2;;;;N;;;;;
+FDF1;ARABIC LIGATURE QALA USED AS KORANIC STOP SIGN ISOLATED FORM;Lo;0;AL;<isolated> 0642 0644 06D2;;;;N;;;;;
+FDF2;ARABIC LIGATURE ALLAH ISOLATED FORM;Lo;0;AL;<isolated> 0627 0644 0644 0647;;;;N;;;;;
+FDF3;ARABIC LIGATURE AKBAR ISOLATED FORM;Lo;0;AL;<isolated> 0627 0643 0628 0631;;;;N;;;;;
+FDF4;ARABIC LIGATURE MOHAMMAD ISOLATED FORM;Lo;0;AL;<isolated> 0645 062D 0645 062F;;;;N;;;;;
+FDF5;ARABIC LIGATURE SALAM ISOLATED FORM;Lo;0;AL;<isolated> 0635 0644 0639 0645;;;;N;;;;;
+FDF6;ARABIC LIGATURE RASOUL ISOLATED FORM;Lo;0;AL;<isolated> 0631 0633 0648 0644;;;;N;;;;;
+FDF7;ARABIC LIGATURE ALAYHE ISOLATED FORM;Lo;0;AL;<isolated> 0639 0644 064A 0647;;;;N;;;;;
+FDF8;ARABIC LIGATURE WASALLAM ISOLATED FORM;Lo;0;AL;<isolated> 0648 0633 0644 0645;;;;N;;;;;
+FDF9;ARABIC LIGATURE SALLA ISOLATED FORM;Lo;0;AL;<isolated> 0635 0644 0649;;;;N;;;;;
+FDFA;ARABIC LIGATURE SALLALLAHOU ALAYHE WASALLAM;Lo;0;AL;<isolated> 0635 0644 0649 0020 0627 0644 0644 0647 0020 0639 0644 064A 0647 0020 0648 0633 0644 0645;;;;N;ARABIC LETTER SALLALLAHOU ALAYHE WASALLAM;;;;
+FDFB;ARABIC LIGATURE JALLAJALALOUHOU;Lo;0;AL;<isolated> 062C 0644 0020 062C 0644 0627 0644 0647;;;;N;ARABIC LETTER JALLAJALALOUHOU;;;;
+FDFC;RIAL SIGN;Sc;0;AL;<isolated> 0631 06CC 0627 0644;;;;N;;;;;
+FDFD;ARABIC LIGATURE BISMILLAH AR-RAHMAN AR-RAHEEM;So;0;ON;;;;;N;;;;;
+FE00;VARIATION SELECTOR-1;Mn;0;NSM;;;;;N;;;;;
+FE01;VARIATION SELECTOR-2;Mn;0;NSM;;;;;N;;;;;
+FE02;VARIATION SELECTOR-3;Mn;0;NSM;;;;;N;;;;;
+FE03;VARIATION SELECTOR-4;Mn;0;NSM;;;;;N;;;;;
+FE04;VARIATION SELECTOR-5;Mn;0;NSM;;;;;N;;;;;
+FE05;VARIATION SELECTOR-6;Mn;0;NSM;;;;;N;;;;;
+FE06;VARIATION SELECTOR-7;Mn;0;NSM;;;;;N;;;;;
+FE07;VARIATION SELECTOR-8;Mn;0;NSM;;;;;N;;;;;
+FE08;VARIATION SELECTOR-9;Mn;0;NSM;;;;;N;;;;;
+FE09;VARIATION SELECTOR-10;Mn;0;NSM;;;;;N;;;;;
+FE0A;VARIATION SELECTOR-11;Mn;0;NSM;;;;;N;;;;;
+FE0B;VARIATION SELECTOR-12;Mn;0;NSM;;;;;N;;;;;
+FE0C;VARIATION SELECTOR-13;Mn;0;NSM;;;;;N;;;;;
+FE0D;VARIATION SELECTOR-14;Mn;0;NSM;;;;;N;;;;;
+FE0E;VARIATION SELECTOR-15;Mn;0;NSM;;;;;N;;;;;
+FE0F;VARIATION SELECTOR-16;Mn;0;NSM;;;;;N;;;;;
+FE20;COMBINING LIGATURE LEFT HALF;Mn;230;NSM;;;;;N;;;;;
+FE21;COMBINING LIGATURE RIGHT HALF;Mn;230;NSM;;;;;N;;;;;
+FE22;COMBINING DOUBLE TILDE LEFT HALF;Mn;230;NSM;;;;;N;;;;;
+FE23;COMBINING DOUBLE TILDE RIGHT HALF;Mn;230;NSM;;;;;N;;;;;
+FE30;PRESENTATION FORM FOR VERTICAL TWO DOT LEADER;Po;0;ON;<vertical> 2025;;;;N;GLYPH FOR VERTICAL TWO DOT LEADER;;;;
+FE31;PRESENTATION FORM FOR VERTICAL EM DASH;Pd;0;ON;<vertical> 2014;;;;N;GLYPH FOR VERTICAL EM DASH;;;;
+FE32;PRESENTATION FORM FOR VERTICAL EN DASH;Pd;0;ON;<vertical> 2013;;;;N;GLYPH FOR VERTICAL EN DASH;;;;
+FE33;PRESENTATION FORM FOR VERTICAL LOW LINE;Pc;0;ON;<vertical> 005F;;;;N;GLYPH FOR VERTICAL SPACING UNDERSCORE;;;;
+FE34;PRESENTATION FORM FOR VERTICAL WAVY LOW LINE;Pc;0;ON;<vertical> 005F;;;;N;GLYPH FOR VERTICAL SPACING WAVY UNDERSCORE;;;;
+FE35;PRESENTATION FORM FOR VERTICAL LEFT PARENTHESIS;Ps;0;ON;<vertical> 0028;;;;N;GLYPH FOR VERTICAL OPENING PARENTHESIS;;;;
+FE36;PRESENTATION FORM FOR VERTICAL RIGHT PARENTHESIS;Pe;0;ON;<vertical> 0029;;;;N;GLYPH FOR VERTICAL CLOSING PARENTHESIS;;;;
+FE37;PRESENTATION FORM FOR VERTICAL LEFT CURLY BRACKET;Ps;0;ON;<vertical> 007B;;;;N;GLYPH FOR VERTICAL OPENING CURLY BRACKET;;;;
+FE38;PRESENTATION FORM FOR VERTICAL RIGHT CURLY BRACKET;Pe;0;ON;<vertical> 007D;;;;N;GLYPH FOR VERTICAL CLOSING CURLY BRACKET;;;;
+FE39;PRESENTATION FORM FOR VERTICAL LEFT TORTOISE SHELL BRACKET;Ps;0;ON;<vertical> 3014;;;;N;GLYPH FOR VERTICAL OPENING TORTOISE SHELL BRACKET;;;;
+FE3A;PRESENTATION FORM FOR VERTICAL RIGHT TORTOISE SHELL BRACKET;Pe;0;ON;<vertical> 3015;;;;N;GLYPH FOR VERTICAL CLOSING TORTOISE SHELL BRACKET;;;;
+FE3B;PRESENTATION FORM FOR VERTICAL LEFT BLACK LENTICULAR BRACKET;Ps;0;ON;<vertical> 3010;;;;N;GLYPH FOR VERTICAL OPENING BLACK LENTICULAR BRACKET;;;;
+FE3C;PRESENTATION FORM FOR VERTICAL RIGHT BLACK LENTICULAR BRACKET;Pe;0;ON;<vertical> 3011;;;;N;GLYPH FOR VERTICAL CLOSING BLACK LENTICULAR BRACKET;;;;
+FE3D;PRESENTATION FORM FOR VERTICAL LEFT DOUBLE ANGLE BRACKET;Ps;0;ON;<vertical> 300A;;;;N;GLYPH FOR VERTICAL OPENING DOUBLE ANGLE BRACKET;;;;
+FE3E;PRESENTATION FORM FOR VERTICAL RIGHT DOUBLE ANGLE BRACKET;Pe;0;ON;<vertical> 300B;;;;N;GLYPH FOR VERTICAL CLOSING DOUBLE ANGLE BRACKET;;;;
+FE3F;PRESENTATION FORM FOR VERTICAL LEFT ANGLE BRACKET;Ps;0;ON;<vertical> 3008;;;;N;GLYPH FOR VERTICAL OPENING ANGLE BRACKET;;;;
+FE40;PRESENTATION FORM FOR VERTICAL RIGHT ANGLE BRACKET;Pe;0;ON;<vertical> 3009;;;;N;GLYPH FOR VERTICAL CLOSING ANGLE BRACKET;;;;
+FE41;PRESENTATION FORM FOR VERTICAL LEFT CORNER BRACKET;Ps;0;ON;<vertical> 300C;;;;N;GLYPH FOR VERTICAL OPENING CORNER BRACKET;;;;
+FE42;PRESENTATION FORM FOR VERTICAL RIGHT CORNER BRACKET;Pe;0;ON;<vertical> 300D;;;;N;GLYPH FOR VERTICAL CLOSING CORNER BRACKET;;;;
+FE43;PRESENTATION FORM FOR VERTICAL LEFT WHITE CORNER BRACKET;Ps;0;ON;<vertical> 300E;;;;N;GLYPH FOR VERTICAL OPENING WHITE CORNER BRACKET;;;;
+FE44;PRESENTATION FORM FOR VERTICAL RIGHT WHITE CORNER BRACKET;Pe;0;ON;<vertical> 300F;;;;N;GLYPH FOR VERTICAL CLOSING WHITE CORNER BRACKET;;;;
+FE45;SESAME DOT;Po;0;ON;;;;;N;;;;;
+FE46;WHITE SESAME DOT;Po;0;ON;;;;;N;;;;;
+FE47;PRESENTATION FORM FOR VERTICAL LEFT SQUARE BRACKET;Ps;0;ON;<vertical> 005B;;;;N;;;;;
+FE48;PRESENTATION FORM FOR VERTICAL RIGHT SQUARE BRACKET;Pe;0;ON;<vertical> 005D;;;;N;;;;;
+FE49;DASHED OVERLINE;Po;0;ON;<compat> 203E;;;;N;SPACING DASHED OVERSCORE;;;;
+FE4A;CENTRELINE OVERLINE;Po;0;ON;<compat> 203E;;;;N;SPACING CENTERLINE OVERSCORE;;;;
+FE4B;WAVY OVERLINE;Po;0;ON;<compat> 203E;;;;N;SPACING WAVY OVERSCORE;;;;
+FE4C;DOUBLE WAVY OVERLINE;Po;0;ON;<compat> 203E;;;;N;SPACING DOUBLE WAVY OVERSCORE;;;;
+FE4D;DASHED LOW LINE;Pc;0;ON;<compat> 005F;;;;N;SPACING DASHED UNDERSCORE;;;;
+FE4E;CENTRELINE LOW LINE;Pc;0;ON;<compat> 005F;;;;N;SPACING CENTERLINE UNDERSCORE;;;;
+FE4F;WAVY LOW LINE;Pc;0;ON;<compat> 005F;;;;N;SPACING WAVY UNDERSCORE;;;;
+FE50;SMALL COMMA;Po;0;CS;<small> 002C;;;;N;;;;;
+FE51;SMALL IDEOGRAPHIC COMMA;Po;0;ON;<small> 3001;;;;N;;;;;
+FE52;SMALL FULL STOP;Po;0;CS;<small> 002E;;;;N;SMALL PERIOD;;;;
+FE54;SMALL SEMICOLON;Po;0;ON;<small> 003B;;;;N;;;;;
+FE55;SMALL COLON;Po;0;CS;<small> 003A;;;;N;;;;;
+FE56;SMALL QUESTION MARK;Po;0;ON;<small> 003F;;;;N;;;;;
+FE57;SMALL EXCLAMATION MARK;Po;0;ON;<small> 0021;;;;N;;;;;
+FE58;SMALL EM DASH;Pd;0;ON;<small> 2014;;;;N;;;;;
+FE59;SMALL LEFT PARENTHESIS;Ps;0;ON;<small> 0028;;;;N;SMALL OPENING PARENTHESIS;;;;
+FE5A;SMALL RIGHT PARENTHESIS;Pe;0;ON;<small> 0029;;;;N;SMALL CLOSING PARENTHESIS;;;;
+FE5B;SMALL LEFT CURLY BRACKET;Ps;0;ON;<small> 007B;;;;N;SMALL OPENING CURLY BRACKET;;;;
+FE5C;SMALL RIGHT CURLY BRACKET;Pe;0;ON;<small> 007D;;;;N;SMALL CLOSING CURLY BRACKET;;;;
+FE5D;SMALL LEFT TORTOISE SHELL BRACKET;Ps;0;ON;<small> 3014;;;;N;SMALL OPENING TORTOISE SHELL BRACKET;;;;
+FE5E;SMALL RIGHT TORTOISE SHELL BRACKET;Pe;0;ON;<small> 3015;;;;N;SMALL CLOSING TORTOISE SHELL BRACKET;;;;
+FE5F;SMALL NUMBER SIGN;Po;0;ET;<small> 0023;;;;N;;;;;
+FE60;SMALL AMPERSAND;Po;0;ON;<small> 0026;;;;N;;;;;
+FE61;SMALL ASTERISK;Po;0;ON;<small> 002A;;;;N;;;;;
+FE62;SMALL PLUS SIGN;Sm;0;ET;<small> 002B;;;;N;;;;;
+FE63;SMALL HYPHEN-MINUS;Pd;0;ET;<small> 002D;;;;N;;;;;
+FE64;SMALL LESS-THAN SIGN;Sm;0;ON;<small> 003C;;;;N;;;;;
+FE65;SMALL GREATER-THAN SIGN;Sm;0;ON;<small> 003E;;;;N;;;;;
+FE66;SMALL EQUALS SIGN;Sm;0;ON;<small> 003D;;;;N;;;;;
+FE68;SMALL REVERSE SOLIDUS;Po;0;ON;<small> 005C;;;;N;SMALL BACKSLASH;;;;
+FE69;SMALL DOLLAR SIGN;Sc;0;ET;<small> 0024;;;;N;;;;;
+FE6A;SMALL PERCENT SIGN;Po;0;ET;<small> 0025;;;;N;;;;;
+FE6B;SMALL COMMERCIAL AT;Po;0;ON;<small> 0040;;;;N;;;;;
+FE70;ARABIC FATHATAN ISOLATED FORM;Lo;0;AL;<isolated> 0020 064B;;;;N;ARABIC SPACING FATHATAN;;;;
+FE71;ARABIC TATWEEL WITH FATHATAN ABOVE;Lo;0;AL;<medial> 0640 064B;;;;N;ARABIC FATHATAN ON TATWEEL;;;;
+FE72;ARABIC DAMMATAN ISOLATED FORM;Lo;0;AL;<isolated> 0020 064C;;;;N;ARABIC SPACING DAMMATAN;;;;
+FE73;ARABIC TAIL FRAGMENT;Lo;0;AL;;;;;N;;;;;
+FE74;ARABIC KASRATAN ISOLATED FORM;Lo;0;AL;<isolated> 0020 064D;;;;N;ARABIC SPACING KASRATAN;;;;
+FE76;ARABIC FATHA ISOLATED FORM;Lo;0;AL;<isolated> 0020 064E;;;;N;ARABIC SPACING FATHAH;;;;
+FE77;ARABIC FATHA MEDIAL FORM;Lo;0;AL;<medial> 0640 064E;;;;N;ARABIC FATHAH ON TATWEEL;;;;
+FE78;ARABIC DAMMA ISOLATED FORM;Lo;0;AL;<isolated> 0020 064F;;;;N;ARABIC SPACING DAMMAH;;;;
+FE79;ARABIC DAMMA MEDIAL FORM;Lo;0;AL;<medial> 0640 064F;;;;N;ARABIC DAMMAH ON TATWEEL;;;;
+FE7A;ARABIC KASRA ISOLATED FORM;Lo;0;AL;<isolated> 0020 0650;;;;N;ARABIC SPACING KASRAH;;;;
+FE7B;ARABIC KASRA MEDIAL FORM;Lo;0;AL;<medial> 0640 0650;;;;N;ARABIC KASRAH ON TATWEEL;;;;
+FE7C;ARABIC SHADDA ISOLATED FORM;Lo;0;AL;<isolated> 0020 0651;;;;N;ARABIC SPACING SHADDAH;;;;
+FE7D;ARABIC SHADDA MEDIAL FORM;Lo;0;AL;<medial> 0640 0651;;;;N;ARABIC SHADDAH ON TATWEEL;;;;
+FE7E;ARABIC SUKUN ISOLATED FORM;Lo;0;AL;<isolated> 0020 0652;;;;N;ARABIC SPACING SUKUN;;;;
+FE7F;ARABIC SUKUN MEDIAL FORM;Lo;0;AL;<medial> 0640 0652;;;;N;ARABIC SUKUN ON TATWEEL;;;;
+FE80;ARABIC LETTER HAMZA ISOLATED FORM;Lo;0;AL;<isolated> 0621;;;;N;GLYPH FOR ISOLATE ARABIC HAMZAH;;;;
+FE81;ARABIC LETTER ALEF WITH MADDA ABOVE ISOLATED FORM;Lo;0;AL;<isolated> 0622;;;;N;GLYPH FOR ISOLATE ARABIC MADDAH ON ALEF;;;;
+FE82;ARABIC LETTER ALEF WITH MADDA ABOVE FINAL FORM;Lo;0;AL;<final> 0622;;;;N;GLYPH FOR FINAL ARABIC MADDAH ON ALEF;;;;
+FE83;ARABIC LETTER ALEF WITH HAMZA ABOVE ISOLATED FORM;Lo;0;AL;<isolated> 0623;;;;N;GLYPH FOR ISOLATE ARABIC HAMZAH ON ALEF;;;;
+FE84;ARABIC LETTER ALEF WITH HAMZA ABOVE FINAL FORM;Lo;0;AL;<final> 0623;;;;N;GLYPH FOR FINAL ARABIC HAMZAH ON ALEF;;;;
+FE85;ARABIC LETTER WAW WITH HAMZA ABOVE ISOLATED FORM;Lo;0;AL;<isolated> 0624;;;;N;GLYPH FOR ISOLATE ARABIC HAMZAH ON WAW;;;;
+FE86;ARABIC LETTER WAW WITH HAMZA ABOVE FINAL FORM;Lo;0;AL;<final> 0624;;;;N;GLYPH FOR FINAL ARABIC HAMZAH ON WAW;;;;
+FE87;ARABIC LETTER ALEF WITH HAMZA BELOW ISOLATED FORM;Lo;0;AL;<isolated> 0625;;;;N;GLYPH FOR ISOLATE ARABIC HAMZAH UNDER ALEF;;;;
+FE88;ARABIC LETTER ALEF WITH HAMZA BELOW FINAL FORM;Lo;0;AL;<final> 0625;;;;N;GLYPH FOR FINAL ARABIC HAMZAH UNDER ALEF;;;;
+FE89;ARABIC LETTER YEH WITH HAMZA ABOVE ISOLATED FORM;Lo;0;AL;<isolated> 0626;;;;N;GLYPH FOR ISOLATE ARABIC HAMZAH ON YA;;;;
+FE8A;ARABIC LETTER YEH WITH HAMZA ABOVE FINAL FORM;Lo;0;AL;<final> 0626;;;;N;GLYPH FOR FINAL ARABIC HAMZAH ON YA;;;;
+FE8B;ARABIC LETTER YEH WITH HAMZA ABOVE INITIAL FORM;Lo;0;AL;<initial> 0626;;;;N;GLYPH FOR INITIAL ARABIC HAMZAH ON YA;;;;
+FE8C;ARABIC LETTER YEH WITH HAMZA ABOVE MEDIAL FORM;Lo;0;AL;<medial> 0626;;;;N;GLYPH FOR MEDIAL ARABIC HAMZAH ON YA;;;;
+FE8D;ARABIC LETTER ALEF ISOLATED FORM;Lo;0;AL;<isolated> 0627;;;;N;GLYPH FOR ISOLATE ARABIC ALEF;;;;
+FE8E;ARABIC LETTER ALEF FINAL FORM;Lo;0;AL;<final> 0627;;;;N;GLYPH FOR FINAL ARABIC ALEF;;;;
+FE8F;ARABIC LETTER BEH ISOLATED FORM;Lo;0;AL;<isolated> 0628;;;;N;GLYPH FOR ISOLATE ARABIC BAA;;;;
+FE90;ARABIC LETTER BEH FINAL FORM;Lo;0;AL;<final> 0628;;;;N;GLYPH FOR FINAL ARABIC BAA;;;;
+FE91;ARABIC LETTER BEH INITIAL FORM;Lo;0;AL;<initial> 0628;;;;N;GLYPH FOR INITIAL ARABIC BAA;;;;
+FE92;ARABIC LETTER BEH MEDIAL FORM;Lo;0;AL;<medial> 0628;;;;N;GLYPH FOR MEDIAL ARABIC BAA;;;;
+FE93;ARABIC LETTER TEH MARBUTA ISOLATED FORM;Lo;0;AL;<isolated> 0629;;;;N;GLYPH FOR ISOLATE ARABIC TAA MARBUTAH;;;;
+FE94;ARABIC LETTER TEH MARBUTA FINAL FORM;Lo;0;AL;<final> 0629;;;;N;GLYPH FOR FINAL ARABIC TAA MARBUTAH;;;;
+FE95;ARABIC LETTER TEH ISOLATED FORM;Lo;0;AL;<isolated> 062A;;;;N;GLYPH FOR ISOLATE ARABIC TAA;;;;
+FE96;ARABIC LETTER TEH FINAL FORM;Lo;0;AL;<final> 062A;;;;N;GLYPH FOR FINAL ARABIC TAA;;;;
+FE97;ARABIC LETTER TEH INITIAL FORM;Lo;0;AL;<initial> 062A;;;;N;GLYPH FOR INITIAL ARABIC TAA;;;;
+FE98;ARABIC LETTER TEH MEDIAL FORM;Lo;0;AL;<medial> 062A;;;;N;GLYPH FOR MEDIAL ARABIC TAA;;;;
+FE99;ARABIC LETTER THEH ISOLATED FORM;Lo;0;AL;<isolated> 062B;;;;N;GLYPH FOR ISOLATE ARABIC THAA;;;;
+FE9A;ARABIC LETTER THEH FINAL FORM;Lo;0;AL;<final> 062B;;;;N;GLYPH FOR FINAL ARABIC THAA;;;;
+FE9B;ARABIC LETTER THEH INITIAL FORM;Lo;0;AL;<initial> 062B;;;;N;GLYPH FOR INITIAL ARABIC THAA;;;;
+FE9C;ARABIC LETTER THEH MEDIAL FORM;Lo;0;AL;<medial> 062B;;;;N;GLYPH FOR MEDIAL ARABIC THAA;;;;
+FE9D;ARABIC LETTER JEEM ISOLATED FORM;Lo;0;AL;<isolated> 062C;;;;N;GLYPH FOR ISOLATE ARABIC JEEM;;;;
+FE9E;ARABIC LETTER JEEM FINAL FORM;Lo;0;AL;<final> 062C;;;;N;GLYPH FOR FINAL ARABIC JEEM;;;;
+FE9F;ARABIC LETTER JEEM INITIAL FORM;Lo;0;AL;<initial> 062C;;;;N;GLYPH FOR INITIAL ARABIC JEEM;;;;
+FEA0;ARABIC LETTER JEEM MEDIAL FORM;Lo;0;AL;<medial> 062C;;;;N;GLYPH FOR MEDIAL ARABIC JEEM;;;;
+FEA1;ARABIC LETTER HAH ISOLATED FORM;Lo;0;AL;<isolated> 062D;;;;N;GLYPH FOR ISOLATE ARABIC HAA;;;;
+FEA2;ARABIC LETTER HAH FINAL FORM;Lo;0;AL;<final> 062D;;;;N;GLYPH FOR FINAL ARABIC HAA;;;;
+FEA3;ARABIC LETTER HAH INITIAL FORM;Lo;0;AL;<initial> 062D;;;;N;GLYPH FOR INITIAL ARABIC HAA;;;;
+FEA4;ARABIC LETTER HAH MEDIAL FORM;Lo;0;AL;<medial> 062D;;;;N;GLYPH FOR MEDIAL ARABIC HAA;;;;
+FEA5;ARABIC LETTER KHAH ISOLATED FORM;Lo;0;AL;<isolated> 062E;;;;N;GLYPH FOR ISOLATE ARABIC KHAA;;;;
+FEA6;ARABIC LETTER KHAH FINAL FORM;Lo;0;AL;<final> 062E;;;;N;GLYPH FOR FINAL ARABIC KHAA;;;;
+FEA7;ARABIC LETTER KHAH INITIAL FORM;Lo;0;AL;<initial> 062E;;;;N;GLYPH FOR INITIAL ARABIC KHAA;;;;
+FEA8;ARABIC LETTER KHAH MEDIAL FORM;Lo;0;AL;<medial> 062E;;;;N;GLYPH FOR MEDIAL ARABIC KHAA;;;;
+FEA9;ARABIC LETTER DAL ISOLATED FORM;Lo;0;AL;<isolated> 062F;;;;N;GLYPH FOR ISOLATE ARABIC DAL;;;;
+FEAA;ARABIC LETTER DAL FINAL FORM;Lo;0;AL;<final> 062F;;;;N;GLYPH FOR FINAL ARABIC DAL;;;;
+FEAB;ARABIC LETTER THAL ISOLATED FORM;Lo;0;AL;<isolated> 0630;;;;N;GLYPH FOR ISOLATE ARABIC THAL;;;;
+FEAC;ARABIC LETTER THAL FINAL FORM;Lo;0;AL;<final> 0630;;;;N;GLYPH FOR FINAL ARABIC THAL;;;;
+FEAD;ARABIC LETTER REH ISOLATED FORM;Lo;0;AL;<isolated> 0631;;;;N;GLYPH FOR ISOLATE ARABIC RA;;;;
+FEAE;ARABIC LETTER REH FINAL FORM;Lo;0;AL;<final> 0631;;;;N;GLYPH FOR FINAL ARABIC RA;;;;
+FEAF;ARABIC LETTER ZAIN ISOLATED FORM;Lo;0;AL;<isolated> 0632;;;;N;GLYPH FOR ISOLATE ARABIC ZAIN;;;;
+FEB0;ARABIC LETTER ZAIN FINAL FORM;Lo;0;AL;<final> 0632;;;;N;GLYPH FOR FINAL ARABIC ZAIN;;;;
+FEB1;ARABIC LETTER SEEN ISOLATED FORM;Lo;0;AL;<isolated> 0633;;;;N;GLYPH FOR ISOLATE ARABIC SEEN;;;;
+FEB2;ARABIC LETTER SEEN FINAL FORM;Lo;0;AL;<final> 0633;;;;N;GLYPH FOR FINAL ARABIC SEEN;;;;
+FEB3;ARABIC LETTER SEEN INITIAL FORM;Lo;0;AL;<initial> 0633;;;;N;GLYPH FOR INITIAL ARABIC SEEN;;;;
+FEB4;ARABIC LETTER SEEN MEDIAL FORM;Lo;0;AL;<medial> 0633;;;;N;GLYPH FOR MEDIAL ARABIC SEEN;;;;
+FEB5;ARABIC LETTER SHEEN ISOLATED FORM;Lo;0;AL;<isolated> 0634;;;;N;GLYPH FOR ISOLATE ARABIC SHEEN;;;;
+FEB6;ARABIC LETTER SHEEN FINAL FORM;Lo;0;AL;<final> 0634;;;;N;GLYPH FOR FINAL ARABIC SHEEN;;;;
+FEB7;ARABIC LETTER SHEEN INITIAL FORM;Lo;0;AL;<initial> 0634;;;;N;GLYPH FOR INITIAL ARABIC SHEEN;;;;
+FEB8;ARABIC LETTER SHEEN MEDIAL FORM;Lo;0;AL;<medial> 0634;;;;N;GLYPH FOR MEDIAL ARABIC SHEEN;;;;
+FEB9;ARABIC LETTER SAD ISOLATED FORM;Lo;0;AL;<isolated> 0635;;;;N;GLYPH FOR ISOLATE ARABIC SAD;;;;
+FEBA;ARABIC LETTER SAD FINAL FORM;Lo;0;AL;<final> 0635;;;;N;GLYPH FOR FINAL ARABIC SAD;;;;
+FEBB;ARABIC LETTER SAD INITIAL FORM;Lo;0;AL;<initial> 0635;;;;N;GLYPH FOR INITIAL ARABIC SAD;;;;
+FEBC;ARABIC LETTER SAD MEDIAL FORM;Lo;0;AL;<medial> 0635;;;;N;GLYPH FOR MEDIAL ARABIC SAD;;;;
+FEBD;ARABIC LETTER DAD ISOLATED FORM;Lo;0;AL;<isolated> 0636;;;;N;GLYPH FOR ISOLATE ARABIC DAD;;;;
+FEBE;ARABIC LETTER DAD FINAL FORM;Lo;0;AL;<final> 0636;;;;N;GLYPH FOR FINAL ARABIC DAD;;;;
+FEBF;ARABIC LETTER DAD INITIAL FORM;Lo;0;AL;<initial> 0636;;;;N;GLYPH FOR INITIAL ARABIC DAD;;;;
+FEC0;ARABIC LETTER DAD MEDIAL FORM;Lo;0;AL;<medial> 0636;;;;N;GLYPH FOR MEDIAL ARABIC DAD;;;;
+FEC1;ARABIC LETTER TAH ISOLATED FORM;Lo;0;AL;<isolated> 0637;;;;N;GLYPH FOR ISOLATE ARABIC TAH;;;;
+FEC2;ARABIC LETTER TAH FINAL FORM;Lo;0;AL;<final> 0637;;;;N;GLYPH FOR FINAL ARABIC TAH;;;;
+FEC3;ARABIC LETTER TAH INITIAL FORM;Lo;0;AL;<initial> 0637;;;;N;GLYPH FOR INITIAL ARABIC TAH;;;;
+FEC4;ARABIC LETTER TAH MEDIAL FORM;Lo;0;AL;<medial> 0637;;;;N;GLYPH FOR MEDIAL ARABIC TAH;;;;
+FEC5;ARABIC LETTER ZAH ISOLATED FORM;Lo;0;AL;<isolated> 0638;;;;N;GLYPH FOR ISOLATE ARABIC DHAH;;;;
+FEC6;ARABIC LETTER ZAH FINAL FORM;Lo;0;AL;<final> 0638;;;;N;GLYPH FOR FINAL ARABIC DHAH;;;;
+FEC7;ARABIC LETTER ZAH INITIAL FORM;Lo;0;AL;<initial> 0638;;;;N;GLYPH FOR INITIAL ARABIC DHAH;;;;
+FEC8;ARABIC LETTER ZAH MEDIAL FORM;Lo;0;AL;<medial> 0638;;;;N;GLYPH FOR MEDIAL ARABIC DHAH;;;;
+FEC9;ARABIC LETTER AIN ISOLATED FORM;Lo;0;AL;<isolated> 0639;;;;N;GLYPH FOR ISOLATE ARABIC AIN;;;;
+FECA;ARABIC LETTER AIN FINAL FORM;Lo;0;AL;<final> 0639;;;;N;GLYPH FOR FINAL ARABIC AIN;;;;
+FECB;ARABIC LETTER AIN INITIAL FORM;Lo;0;AL;<initial> 0639;;;;N;GLYPH FOR INITIAL ARABIC AIN;;;;
+FECC;ARABIC LETTER AIN MEDIAL FORM;Lo;0;AL;<medial> 0639;;;;N;GLYPH FOR MEDIAL ARABIC AIN;;;;
+FECD;ARABIC LETTER GHAIN ISOLATED FORM;Lo;0;AL;<isolated> 063A;;;;N;GLYPH FOR ISOLATE ARABIC GHAIN;;;;
+FECE;ARABIC LETTER GHAIN FINAL FORM;Lo;0;AL;<final> 063A;;;;N;GLYPH FOR FINAL ARABIC GHAIN;;;;
+FECF;ARABIC LETTER GHAIN INITIAL FORM;Lo;0;AL;<initial> 063A;;;;N;GLYPH FOR INITIAL ARABIC GHAIN;;;;
+FED0;ARABIC LETTER GHAIN MEDIAL FORM;Lo;0;AL;<medial> 063A;;;;N;GLYPH FOR MEDIAL ARABIC GHAIN;;;;
+FED1;ARABIC LETTER FEH ISOLATED FORM;Lo;0;AL;<isolated> 0641;;;;N;GLYPH FOR ISOLATE ARABIC FA;;;;
+FED2;ARABIC LETTER FEH FINAL FORM;Lo;0;AL;<final> 0641;;;;N;GLYPH FOR FINAL ARABIC FA;;;;
+FED3;ARABIC LETTER FEH INITIAL FORM;Lo;0;AL;<initial> 0641;;;;N;GLYPH FOR INITIAL ARABIC FA;;;;
+FED4;ARABIC LETTER FEH MEDIAL FORM;Lo;0;AL;<medial> 0641;;;;N;GLYPH FOR MEDIAL ARABIC FA;;;;
+FED5;ARABIC LETTER QAF ISOLATED FORM;Lo;0;AL;<isolated> 0642;;;;N;GLYPH FOR ISOLATE ARABIC QAF;;;;
+FED6;ARABIC LETTER QAF FINAL FORM;Lo;0;AL;<final> 0642;;;;N;GLYPH FOR FINAL ARABIC QAF;;;;
+FED7;ARABIC LETTER QAF INITIAL FORM;Lo;0;AL;<initial> 0642;;;;N;GLYPH FOR INITIAL ARABIC QAF;;;;
+FED8;ARABIC LETTER QAF MEDIAL FORM;Lo;0;AL;<medial> 0642;;;;N;GLYPH FOR MEDIAL ARABIC QAF;;;;
+FED9;ARABIC LETTER KAF ISOLATED FORM;Lo;0;AL;<isolated> 0643;;;;N;GLYPH FOR ISOLATE ARABIC CAF;;;;
+FEDA;ARABIC LETTER KAF FINAL FORM;Lo;0;AL;<final> 0643;;;;N;GLYPH FOR FINAL ARABIC CAF;;;;
+FEDB;ARABIC LETTER KAF INITIAL FORM;Lo;0;AL;<initial> 0643;;;;N;GLYPH FOR INITIAL ARABIC CAF;;;;
+FEDC;ARABIC LETTER KAF MEDIAL FORM;Lo;0;AL;<medial> 0643;;;;N;GLYPH FOR MEDIAL ARABIC CAF;;;;
+FEDD;ARABIC LETTER LAM ISOLATED FORM;Lo;0;AL;<isolated> 0644;;;;N;GLYPH FOR ISOLATE ARABIC LAM;;;;
+FEDE;ARABIC LETTER LAM FINAL FORM;Lo;0;AL;<final> 0644;;;;N;GLYPH FOR FINAL ARABIC LAM;;;;
+FEDF;ARABIC LETTER LAM INITIAL FORM;Lo;0;AL;<initial> 0644;;;;N;GLYPH FOR INITIAL ARABIC LAM;;;;
+FEE0;ARABIC LETTER LAM MEDIAL FORM;Lo;0;AL;<medial> 0644;;;;N;GLYPH FOR MEDIAL ARABIC LAM;;;;
+FEE1;ARABIC LETTER MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0645;;;;N;GLYPH FOR ISOLATE ARABIC MEEM;;;;
+FEE2;ARABIC LETTER MEEM FINAL FORM;Lo;0;AL;<final> 0645;;;;N;GLYPH FOR FINAL ARABIC MEEM;;;;
+FEE3;ARABIC LETTER MEEM INITIAL FORM;Lo;0;AL;<initial> 0645;;;;N;GLYPH FOR INITIAL ARABIC MEEM;;;;
+FEE4;ARABIC LETTER MEEM MEDIAL FORM;Lo;0;AL;<medial> 0645;;;;N;GLYPH FOR MEDIAL ARABIC MEEM;;;;
+FEE5;ARABIC LETTER NOON ISOLATED FORM;Lo;0;AL;<isolated> 0646;;;;N;GLYPH FOR ISOLATE ARABIC NOON;;;;
+FEE6;ARABIC LETTER NOON FINAL FORM;Lo;0;AL;<final> 0646;;;;N;GLYPH FOR FINAL ARABIC NOON;;;;
+FEE7;ARABIC LETTER NOON INITIAL FORM;Lo;0;AL;<initial> 0646;;;;N;GLYPH FOR INITIAL ARABIC NOON;;;;
+FEE8;ARABIC LETTER NOON MEDIAL FORM;Lo;0;AL;<medial> 0646;;;;N;GLYPH FOR MEDIAL ARABIC NOON;;;;
+FEE9;ARABIC LETTER HEH ISOLATED FORM;Lo;0;AL;<isolated> 0647;;;;N;GLYPH FOR ISOLATE ARABIC HA;;;;
+FEEA;ARABIC LETTER HEH FINAL FORM;Lo;0;AL;<final> 0647;;;;N;GLYPH FOR FINAL ARABIC HA;;;;
+FEEB;ARABIC LETTER HEH INITIAL FORM;Lo;0;AL;<initial> 0647;;;;N;GLYPH FOR INITIAL ARABIC HA;;;;
+FEEC;ARABIC LETTER HEH MEDIAL FORM;Lo;0;AL;<medial> 0647;;;;N;GLYPH FOR MEDIAL ARABIC HA;;;;
+FEED;ARABIC LETTER WAW ISOLATED FORM;Lo;0;AL;<isolated> 0648;;;;N;GLYPH FOR ISOLATE ARABIC WAW;;;;
+FEEE;ARABIC LETTER WAW FINAL FORM;Lo;0;AL;<final> 0648;;;;N;GLYPH FOR FINAL ARABIC WAW;;;;
+FEEF;ARABIC LETTER ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0649;;;;N;GLYPH FOR ISOLATE ARABIC ALEF MAQSURAH;;;;
+FEF0;ARABIC LETTER ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0649;;;;N;GLYPH FOR FINAL ARABIC ALEF MAQSURAH;;;;
+FEF1;ARABIC LETTER YEH ISOLATED FORM;Lo;0;AL;<isolated> 064A;;;;N;GLYPH FOR ISOLATE ARABIC YA;;;;
+FEF2;ARABIC LETTER YEH FINAL FORM;Lo;0;AL;<final> 064A;;;;N;GLYPH FOR FINAL ARABIC YA;;;;
+FEF3;ARABIC LETTER YEH INITIAL FORM;Lo;0;AL;<initial> 064A;;;;N;GLYPH FOR INITIAL ARABIC YA;;;;
+FEF4;ARABIC LETTER YEH MEDIAL FORM;Lo;0;AL;<medial> 064A;;;;N;GLYPH FOR MEDIAL ARABIC YA;;;;
+FEF5;ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE ISOLATED FORM;Lo;0;AL;<isolated> 0644 0622;;;;N;GLYPH FOR ISOLATE ARABIC MADDAH ON LIGATURE LAM ALEF;;;;
+FEF6;ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE FINAL FORM;Lo;0;AL;<final> 0644 0622;;;;N;GLYPH FOR FINAL ARABIC MADDAH ON LIGATURE LAM ALEF;;;;
+FEF7;ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE ISOLATED FORM;Lo;0;AL;<isolated> 0644 0623;;;;N;GLYPH FOR ISOLATE ARABIC HAMZAH ON LIGATURE LAM ALEF;;;;
+FEF8;ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE FINAL FORM;Lo;0;AL;<final> 0644 0623;;;;N;GLYPH FOR FINAL ARABIC HAMZAH ON LIGATURE LAM ALEF;;;;
+FEF9;ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW ISOLATED FORM;Lo;0;AL;<isolated> 0644 0625;;;;N;GLYPH FOR ISOLATE ARABIC HAMZAH UNDER LIGATURE LAM ALEF;;;;
+FEFA;ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW FINAL FORM;Lo;0;AL;<final> 0644 0625;;;;N;GLYPH FOR FINAL ARABIC HAMZAH UNDER LIGATURE LAM ALEF;;;;
+FEFB;ARABIC LIGATURE LAM WITH ALEF ISOLATED FORM;Lo;0;AL;<isolated> 0644 0627;;;;N;GLYPH FOR ISOLATE ARABIC LIGATURE LAM ALEF;;;;
+FEFC;ARABIC LIGATURE LAM WITH ALEF FINAL FORM;Lo;0;AL;<final> 0644 0627;;;;N;GLYPH FOR FINAL ARABIC LIGATURE LAM ALEF;;;;
+FEFF;ZERO WIDTH NO-BREAK SPACE;Cf;0;BN;;;;;N;BYTE ORDER MARK;;;;
+FF01;FULLWIDTH EXCLAMATION MARK;Po;0;ON;<wide> 0021;;;;N;;;;;
+FF02;FULLWIDTH QUOTATION MARK;Po;0;ON;<wide> 0022;;;;N;;;;;
+FF03;FULLWIDTH NUMBER SIGN;Po;0;ET;<wide> 0023;;;;N;;;;;
+FF04;FULLWIDTH DOLLAR SIGN;Sc;0;ET;<wide> 0024;;;;N;;;;;
+FF05;FULLWIDTH PERCENT SIGN;Po;0;ET;<wide> 0025;;;;N;;;;;
+FF06;FULLWIDTH AMPERSAND;Po;0;ON;<wide> 0026;;;;N;;;;;
+FF07;FULLWIDTH APOSTROPHE;Po;0;ON;<wide> 0027;;;;N;;;;;
+FF08;FULLWIDTH LEFT PARENTHESIS;Ps;0;ON;<wide> 0028;;;;Y;FULLWIDTH OPENING PARENTHESIS;;;;
+FF09;FULLWIDTH RIGHT PARENTHESIS;Pe;0;ON;<wide> 0029;;;;Y;FULLWIDTH CLOSING PARENTHESIS;;;;
+FF0A;FULLWIDTH ASTERISK;Po;0;ON;<wide> 002A;;;;N;;;;;
+FF0B;FULLWIDTH PLUS SIGN;Sm;0;ET;<wide> 002B;;;;N;;;;;
+FF0C;FULLWIDTH COMMA;Po;0;CS;<wide> 002C;;;;N;;;;;
+FF0D;FULLWIDTH HYPHEN-MINUS;Pd;0;ET;<wide> 002D;;;;N;;;;;
+FF0E;FULLWIDTH FULL STOP;Po;0;CS;<wide> 002E;;;;N;FULLWIDTH PERIOD;;;;
+FF0F;FULLWIDTH SOLIDUS;Po;0;ES;<wide> 002F;;;;N;FULLWIDTH SLASH;;;;
+FF10;FULLWIDTH DIGIT ZERO;Nd;0;EN;<wide> 0030;0;0;0;N;;;;;
+FF11;FULLWIDTH DIGIT ONE;Nd;0;EN;<wide> 0031;1;1;1;N;;;;;
+FF12;FULLWIDTH DIGIT TWO;Nd;0;EN;<wide> 0032;2;2;2;N;;;;;
+FF13;FULLWIDTH DIGIT THREE;Nd;0;EN;<wide> 0033;3;3;3;N;;;;;
+FF14;FULLWIDTH DIGIT FOUR;Nd;0;EN;<wide> 0034;4;4;4;N;;;;;
+FF15;FULLWIDTH DIGIT FIVE;Nd;0;EN;<wide> 0035;5;5;5;N;;;;;
+FF16;FULLWIDTH DIGIT SIX;Nd;0;EN;<wide> 0036;6;6;6;N;;;;;
+FF17;FULLWIDTH DIGIT SEVEN;Nd;0;EN;<wide> 0037;7;7;7;N;;;;;
+FF18;FULLWIDTH DIGIT EIGHT;Nd;0;EN;<wide> 0038;8;8;8;N;;;;;
+FF19;FULLWIDTH DIGIT NINE;Nd;0;EN;<wide> 0039;9;9;9;N;;;;;
+FF1A;FULLWIDTH COLON;Po;0;CS;<wide> 003A;;;;N;;;;;
+FF1B;FULLWIDTH SEMICOLON;Po;0;ON;<wide> 003B;;;;N;;;;;
+FF1C;FULLWIDTH LESS-THAN SIGN;Sm;0;ON;<wide> 003C;;;;Y;;;;;
+FF1D;FULLWIDTH EQUALS SIGN;Sm;0;ON;<wide> 003D;;;;N;;;;;
+FF1E;FULLWIDTH GREATER-THAN SIGN;Sm;0;ON;<wide> 003E;;;;Y;;;;;
+FF1F;FULLWIDTH QUESTION MARK;Po;0;ON;<wide> 003F;;;;N;;;;;
+FF20;FULLWIDTH COMMERCIAL AT;Po;0;ON;<wide> 0040;;;;N;;;;;
+FF21;FULLWIDTH LATIN CAPITAL LETTER A;Lu;0;L;<wide> 0041;;;;N;;;;FF41;
+FF22;FULLWIDTH LATIN CAPITAL LETTER B;Lu;0;L;<wide> 0042;;;;N;;;;FF42;
+FF23;FULLWIDTH LATIN CAPITAL LETTER C;Lu;0;L;<wide> 0043;;;;N;;;;FF43;
+FF24;FULLWIDTH LATIN CAPITAL LETTER D;Lu;0;L;<wide> 0044;;;;N;;;;FF44;
+FF25;FULLWIDTH LATIN CAPITAL LETTER E;Lu;0;L;<wide> 0045;;;;N;;;;FF45;
+FF26;FULLWIDTH LATIN CAPITAL LETTER F;Lu;0;L;<wide> 0046;;;;N;;;;FF46;
+FF27;FULLWIDTH LATIN CAPITAL LETTER G;Lu;0;L;<wide> 0047;;;;N;;;;FF47;
+FF28;FULLWIDTH LATIN CAPITAL LETTER H;Lu;0;L;<wide> 0048;;;;N;;;;FF48;
+FF29;FULLWIDTH LATIN CAPITAL LETTER I;Lu;0;L;<wide> 0049;;;;N;;;;FF49;
+FF2A;FULLWIDTH LATIN CAPITAL LETTER J;Lu;0;L;<wide> 004A;;;;N;;;;FF4A;
+FF2B;FULLWIDTH LATIN CAPITAL LETTER K;Lu;0;L;<wide> 004B;;;;N;;;;FF4B;
+FF2C;FULLWIDTH LATIN CAPITAL LETTER L;Lu;0;L;<wide> 004C;;;;N;;;;FF4C;
+FF2D;FULLWIDTH LATIN CAPITAL LETTER M;Lu;0;L;<wide> 004D;;;;N;;;;FF4D;
+FF2E;FULLWIDTH LATIN CAPITAL LETTER N;Lu;0;L;<wide> 004E;;;;N;;;;FF4E;
+FF2F;FULLWIDTH LATIN CAPITAL LETTER O;Lu;0;L;<wide> 004F;;;;N;;;;FF4F;
+FF30;FULLWIDTH LATIN CAPITAL LETTER P;Lu;0;L;<wide> 0050;;;;N;;;;FF50;
+FF31;FULLWIDTH LATIN CAPITAL LETTER Q;Lu;0;L;<wide> 0051;;;;N;;;;FF51;
+FF32;FULLWIDTH LATIN CAPITAL LETTER R;Lu;0;L;<wide> 0052;;;;N;;;;FF52;
+FF33;FULLWIDTH LATIN CAPITAL LETTER S;Lu;0;L;<wide> 0053;;;;N;;;;FF53;
+FF34;FULLWIDTH LATIN CAPITAL LETTER T;Lu;0;L;<wide> 0054;;;;N;;;;FF54;
+FF35;FULLWIDTH LATIN CAPITAL LETTER U;Lu;0;L;<wide> 0055;;;;N;;;;FF55;
+FF36;FULLWIDTH LATIN CAPITAL LETTER V;Lu;0;L;<wide> 0056;;;;N;;;;FF56;
+FF37;FULLWIDTH LATIN CAPITAL LETTER W;Lu;0;L;<wide> 0057;;;;N;;;;FF57;
+FF38;FULLWIDTH LATIN CAPITAL LETTER X;Lu;0;L;<wide> 0058;;;;N;;;;FF58;
+FF39;FULLWIDTH LATIN CAPITAL LETTER Y;Lu;0;L;<wide> 0059;;;;N;;;;FF59;
+FF3A;FULLWIDTH LATIN CAPITAL LETTER Z;Lu;0;L;<wide> 005A;;;;N;;;;FF5A;
+FF3B;FULLWIDTH LEFT SQUARE BRACKET;Ps;0;ON;<wide> 005B;;;;Y;FULLWIDTH OPENING SQUARE BRACKET;;;;
+FF3C;FULLWIDTH REVERSE SOLIDUS;Po;0;ON;<wide> 005C;;;;N;FULLWIDTH BACKSLASH;;;;
+FF3D;FULLWIDTH RIGHT SQUARE BRACKET;Pe;0;ON;<wide> 005D;;;;Y;FULLWIDTH CLOSING SQUARE BRACKET;;;;
+FF3E;FULLWIDTH CIRCUMFLEX ACCENT;Sk;0;ON;<wide> 005E;;;;N;FULLWIDTH SPACING CIRCUMFLEX;;;;
+FF3F;FULLWIDTH LOW LINE;Pc;0;ON;<wide> 005F;;;;N;FULLWIDTH SPACING UNDERSCORE;;;;
+FF40;FULLWIDTH GRAVE ACCENT;Sk;0;ON;<wide> 0060;;;;N;FULLWIDTH SPACING GRAVE;;;;
+FF41;FULLWIDTH LATIN SMALL LETTER A;Ll;0;L;<wide> 0061;;;;N;;;FF21;;FF21
+FF42;FULLWIDTH LATIN SMALL LETTER B;Ll;0;L;<wide> 0062;;;;N;;;FF22;;FF22
+FF43;FULLWIDTH LATIN SMALL LETTER C;Ll;0;L;<wide> 0063;;;;N;;;FF23;;FF23
+FF44;FULLWIDTH LATIN SMALL LETTER D;Ll;0;L;<wide> 0064;;;;N;;;FF24;;FF24
+FF45;FULLWIDTH LATIN SMALL LETTER E;Ll;0;L;<wide> 0065;;;;N;;;FF25;;FF25
+FF46;FULLWIDTH LATIN SMALL LETTER F;Ll;0;L;<wide> 0066;;;;N;;;FF26;;FF26
+FF47;FULLWIDTH LATIN SMALL LETTER G;Ll;0;L;<wide> 0067;;;;N;;;FF27;;FF27
+FF48;FULLWIDTH LATIN SMALL LETTER H;Ll;0;L;<wide> 0068;;;;N;;;FF28;;FF28
+FF49;FULLWIDTH LATIN SMALL LETTER I;Ll;0;L;<wide> 0069;;;;N;;;FF29;;FF29
+FF4A;FULLWIDTH LATIN SMALL LETTER J;Ll;0;L;<wide> 006A;;;;N;;;FF2A;;FF2A
+FF4B;FULLWIDTH LATIN SMALL LETTER K;Ll;0;L;<wide> 006B;;;;N;;;FF2B;;FF2B
+FF4C;FULLWIDTH LATIN SMALL LETTER L;Ll;0;L;<wide> 006C;;;;N;;;FF2C;;FF2C
+FF4D;FULLWIDTH LATIN SMALL LETTER M;Ll;0;L;<wide> 006D;;;;N;;;FF2D;;FF2D
+FF4E;FULLWIDTH LATIN SMALL LETTER N;Ll;0;L;<wide> 006E;;;;N;;;FF2E;;FF2E
+FF4F;FULLWIDTH LATIN SMALL LETTER O;Ll;0;L;<wide> 006F;;;;N;;;FF2F;;FF2F
+FF50;FULLWIDTH LATIN SMALL LETTER P;Ll;0;L;<wide> 0070;;;;N;;;FF30;;FF30
+FF51;FULLWIDTH LATIN SMALL LETTER Q;Ll;0;L;<wide> 0071;;;;N;;;FF31;;FF31
+FF52;FULLWIDTH LATIN SMALL LETTER R;Ll;0;L;<wide> 0072;;;;N;;;FF32;;FF32
+FF53;FULLWIDTH LATIN SMALL LETTER S;Ll;0;L;<wide> 0073;;;;N;;;FF33;;FF33
+FF54;FULLWIDTH LATIN SMALL LETTER T;Ll;0;L;<wide> 0074;;;;N;;;FF34;;FF34
+FF55;FULLWIDTH LATIN SMALL LETTER U;Ll;0;L;<wide> 0075;;;;N;;;FF35;;FF35
+FF56;FULLWIDTH LATIN SMALL LETTER V;Ll;0;L;<wide> 0076;;;;N;;;FF36;;FF36
+FF57;FULLWIDTH LATIN SMALL LETTER W;Ll;0;L;<wide> 0077;;;;N;;;FF37;;FF37
+FF58;FULLWIDTH LATIN SMALL LETTER X;Ll;0;L;<wide> 0078;;;;N;;;FF38;;FF38
+FF59;FULLWIDTH LATIN SMALL LETTER Y;Ll;0;L;<wide> 0079;;;;N;;;FF39;;FF39
+FF5A;FULLWIDTH LATIN SMALL LETTER Z;Ll;0;L;<wide> 007A;;;;N;;;FF3A;;FF3A
+FF5B;FULLWIDTH LEFT CURLY BRACKET;Ps;0;ON;<wide> 007B;;;;Y;FULLWIDTH OPENING CURLY BRACKET;;;;
+FF5C;FULLWIDTH VERTICAL LINE;Sm;0;ON;<wide> 007C;;;;N;FULLWIDTH VERTICAL BAR;;;;
+FF5D;FULLWIDTH RIGHT CURLY BRACKET;Pe;0;ON;<wide> 007D;;;;Y;FULLWIDTH CLOSING CURLY BRACKET;;;;
+FF5E;FULLWIDTH TILDE;Sm;0;ON;<wide> 007E;;;;N;FULLWIDTH SPACING TILDE;;;;
+FF5F;FULLWIDTH LEFT WHITE PARENTHESIS;Ps;0;ON;<wide> 2985;;;;Y;;*;;;
+FF60;FULLWIDTH RIGHT WHITE PARENTHESIS;Pe;0;ON;<wide> 2986;;;;Y;;*;;;
+FF61;HALFWIDTH IDEOGRAPHIC FULL STOP;Po;0;ON;<narrow> 3002;;;;N;HALFWIDTH IDEOGRAPHIC PERIOD;;;;
+FF62;HALFWIDTH LEFT CORNER BRACKET;Ps;0;ON;<narrow> 300C;;;;Y;HALFWIDTH OPENING CORNER BRACKET;;;;
+FF63;HALFWIDTH RIGHT CORNER BRACKET;Pe;0;ON;<narrow> 300D;;;;Y;HALFWIDTH CLOSING CORNER BRACKET;;;;
+FF64;HALFWIDTH IDEOGRAPHIC COMMA;Po;0;ON;<narrow> 3001;;;;N;;;;;
+FF65;HALFWIDTH KATAKANA MIDDLE DOT;Pc;0;ON;<narrow> 30FB;;;;N;;;;;
+FF66;HALFWIDTH KATAKANA LETTER WO;Lo;0;L;<narrow> 30F2;;;;N;;;;;
+FF67;HALFWIDTH KATAKANA LETTER SMALL A;Lo;0;L;<narrow> 30A1;;;;N;;;;;
+FF68;HALFWIDTH KATAKANA LETTER SMALL I;Lo;0;L;<narrow> 30A3;;;;N;;;;;
+FF69;HALFWIDTH KATAKANA LETTER SMALL U;Lo;0;L;<narrow> 30A5;;;;N;;;;;
+FF6A;HALFWIDTH KATAKANA LETTER SMALL E;Lo;0;L;<narrow> 30A7;;;;N;;;;;
+FF6B;HALFWIDTH KATAKANA LETTER SMALL O;Lo;0;L;<narrow> 30A9;;;;N;;;;;
+FF6C;HALFWIDTH KATAKANA LETTER SMALL YA;Lo;0;L;<narrow> 30E3;;;;N;;;;;
+FF6D;HALFWIDTH KATAKANA LETTER SMALL YU;Lo;0;L;<narrow> 30E5;;;;N;;;;;
+FF6E;HALFWIDTH KATAKANA LETTER SMALL YO;Lo;0;L;<narrow> 30E7;;;;N;;;;;
+FF6F;HALFWIDTH KATAKANA LETTER SMALL TU;Lo;0;L;<narrow> 30C3;;;;N;;;;;
+FF70;HALFWIDTH KATAKANA-HIRAGANA PROLONGED SOUND MARK;Lm;0;L;<narrow> 30FC;;;;N;;;;;
+FF71;HALFWIDTH KATAKANA LETTER A;Lo;0;L;<narrow> 30A2;;;;N;;;;;
+FF72;HALFWIDTH KATAKANA LETTER I;Lo;0;L;<narrow> 30A4;;;;N;;;;;
+FF73;HALFWIDTH KATAKANA LETTER U;Lo;0;L;<narrow> 30A6;;;;N;;;;;
+FF74;HALFWIDTH KATAKANA LETTER E;Lo;0;L;<narrow> 30A8;;;;N;;;;;
+FF75;HALFWIDTH KATAKANA LETTER O;Lo;0;L;<narrow> 30AA;;;;N;;;;;
+FF76;HALFWIDTH KATAKANA LETTER KA;Lo;0;L;<narrow> 30AB;;;;N;;;;;
+FF77;HALFWIDTH KATAKANA LETTER KI;Lo;0;L;<narrow> 30AD;;;;N;;;;;
+FF78;HALFWIDTH KATAKANA LETTER KU;Lo;0;L;<narrow> 30AF;;;;N;;;;;
+FF79;HALFWIDTH KATAKANA LETTER KE;Lo;0;L;<narrow> 30B1;;;;N;;;;;
+FF7A;HALFWIDTH KATAKANA LETTER KO;Lo;0;L;<narrow> 30B3;;;;N;;;;;
+FF7B;HALFWIDTH KATAKANA LETTER SA;Lo;0;L;<narrow> 30B5;;;;N;;;;;
+FF7C;HALFWIDTH KATAKANA LETTER SI;Lo;0;L;<narrow> 30B7;;;;N;;;;;
+FF7D;HALFWIDTH KATAKANA LETTER SU;Lo;0;L;<narrow> 30B9;;;;N;;;;;
+FF7E;HALFWIDTH KATAKANA LETTER SE;Lo;0;L;<narrow> 30BB;;;;N;;;;;
+FF7F;HALFWIDTH KATAKANA LETTER SO;Lo;0;L;<narrow> 30BD;;;;N;;;;;
+FF80;HALFWIDTH KATAKANA LETTER TA;Lo;0;L;<narrow> 30BF;;;;N;;;;;
+FF81;HALFWIDTH KATAKANA LETTER TI;Lo;0;L;<narrow> 30C1;;;;N;;;;;
+FF82;HALFWIDTH KATAKANA LETTER TU;Lo;0;L;<narrow> 30C4;;;;N;;;;;
+FF83;HALFWIDTH KATAKANA LETTER TE;Lo;0;L;<narrow> 30C6;;;;N;;;;;
+FF84;HALFWIDTH KATAKANA LETTER TO;Lo;0;L;<narrow> 30C8;;;;N;;;;;
+FF85;HALFWIDTH KATAKANA LETTER NA;Lo;0;L;<narrow> 30CA;;;;N;;;;;
+FF86;HALFWIDTH KATAKANA LETTER NI;Lo;0;L;<narrow> 30CB;;;;N;;;;;
+FF87;HALFWIDTH KATAKANA LETTER NU;Lo;0;L;<narrow> 30CC;;;;N;;;;;
+FF88;HALFWIDTH KATAKANA LETTER NE;Lo;0;L;<narrow> 30CD;;;;N;;;;;
+FF89;HALFWIDTH KATAKANA LETTER NO;Lo;0;L;<narrow> 30CE;;;;N;;;;;
+FF8A;HALFWIDTH KATAKANA LETTER HA;Lo;0;L;<narrow> 30CF;;;;N;;;;;
+FF8B;HALFWIDTH KATAKANA LETTER HI;Lo;0;L;<narrow> 30D2;;;;N;;;;;
+FF8C;HALFWIDTH KATAKANA LETTER HU;Lo;0;L;<narrow> 30D5;;;;N;;;;;
+FF8D;HALFWIDTH KATAKANA LETTER HE;Lo;0;L;<narrow> 30D8;;;;N;;;;;
+FF8E;HALFWIDTH KATAKANA LETTER HO;Lo;0;L;<narrow> 30DB;;;;N;;;;;
+FF8F;HALFWIDTH KATAKANA LETTER MA;Lo;0;L;<narrow> 30DE;;;;N;;;;;
+FF90;HALFWIDTH KATAKANA LETTER MI;Lo;0;L;<narrow> 30DF;;;;N;;;;;
+FF91;HALFWIDTH KATAKANA LETTER MU;Lo;0;L;<narrow> 30E0;;;;N;;;;;
+FF92;HALFWIDTH KATAKANA LETTER ME;Lo;0;L;<narrow> 30E1;;;;N;;;;;
+FF93;HALFWIDTH KATAKANA LETTER MO;Lo;0;L;<narrow> 30E2;;;;N;;;;;
+FF94;HALFWIDTH KATAKANA LETTER YA;Lo;0;L;<narrow> 30E4;;;;N;;;;;
+FF95;HALFWIDTH KATAKANA LETTER YU;Lo;0;L;<narrow> 30E6;;;;N;;;;;
+FF96;HALFWIDTH KATAKANA LETTER YO;Lo;0;L;<narrow> 30E8;;;;N;;;;;
+FF97;HALFWIDTH KATAKANA LETTER RA;Lo;0;L;<narrow> 30E9;;;;N;;;;;
+FF98;HALFWIDTH KATAKANA LETTER RI;Lo;0;L;<narrow> 30EA;;;;N;;;;;
+FF99;HALFWIDTH KATAKANA LETTER RU;Lo;0;L;<narrow> 30EB;;;;N;;;;;
+FF9A;HALFWIDTH KATAKANA LETTER RE;Lo;0;L;<narrow> 30EC;;;;N;;;;;
+FF9B;HALFWIDTH KATAKANA LETTER RO;Lo;0;L;<narrow> 30ED;;;;N;;;;;
+FF9C;HALFWIDTH KATAKANA LETTER WA;Lo;0;L;<narrow> 30EF;;;;N;;;;;
+FF9D;HALFWIDTH KATAKANA LETTER N;Lo;0;L;<narrow> 30F3;;;;N;;;;;
+FF9E;HALFWIDTH KATAKANA VOICED SOUND MARK;Lm;0;L;<narrow> 3099;;;;N;;halfwidth katakana-hiragana voiced sound mark;;;
+FF9F;HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK;Lm;0;L;<narrow> 309A;;;;N;;halfwidth katakana-hiragana semi-voiced sound mark;;;
+FFA0;HALFWIDTH HANGUL FILLER;Lo;0;L;<narrow> 3164;;;;N;HALFWIDTH HANGUL CAE OM;;;;
+FFA1;HALFWIDTH HANGUL LETTER KIYEOK;Lo;0;L;<narrow> 3131;;;;N;HALFWIDTH HANGUL LETTER GIYEOG;;;;
+FFA2;HALFWIDTH HANGUL LETTER SSANGKIYEOK;Lo;0;L;<narrow> 3132;;;;N;HALFWIDTH HANGUL LETTER SSANG GIYEOG;;;;
+FFA3;HALFWIDTH HANGUL LETTER KIYEOK-SIOS;Lo;0;L;<narrow> 3133;;;;N;HALFWIDTH HANGUL LETTER GIYEOG SIOS;;;;
+FFA4;HALFWIDTH HANGUL LETTER NIEUN;Lo;0;L;<narrow> 3134;;;;N;;;;;
+FFA5;HALFWIDTH HANGUL LETTER NIEUN-CIEUC;Lo;0;L;<narrow> 3135;;;;N;HALFWIDTH HANGUL LETTER NIEUN JIEUJ;;;;
+FFA6;HALFWIDTH HANGUL LETTER NIEUN-HIEUH;Lo;0;L;<narrow> 3136;;;;N;HALFWIDTH HANGUL LETTER NIEUN HIEUH;;;;
+FFA7;HALFWIDTH HANGUL LETTER TIKEUT;Lo;0;L;<narrow> 3137;;;;N;HALFWIDTH HANGUL LETTER DIGEUD;;;;
+FFA8;HALFWIDTH HANGUL LETTER SSANGTIKEUT;Lo;0;L;<narrow> 3138;;;;N;HALFWIDTH HANGUL LETTER SSANG DIGEUD;;;;
+FFA9;HALFWIDTH HANGUL LETTER RIEUL;Lo;0;L;<narrow> 3139;;;;N;HALFWIDTH HANGUL LETTER LIEUL;;;;
+FFAA;HALFWIDTH HANGUL LETTER RIEUL-KIYEOK;Lo;0;L;<narrow> 313A;;;;N;HALFWIDTH HANGUL LETTER LIEUL GIYEOG;;;;
+FFAB;HALFWIDTH HANGUL LETTER RIEUL-MIEUM;Lo;0;L;<narrow> 313B;;;;N;HALFWIDTH HANGUL LETTER LIEUL MIEUM;;;;
+FFAC;HALFWIDTH HANGUL LETTER RIEUL-PIEUP;Lo;0;L;<narrow> 313C;;;;N;HALFWIDTH HANGUL LETTER LIEUL BIEUB;;;;
+FFAD;HALFWIDTH HANGUL LETTER RIEUL-SIOS;Lo;0;L;<narrow> 313D;;;;N;HALFWIDTH HANGUL LETTER LIEUL SIOS;;;;
+FFAE;HALFWIDTH HANGUL LETTER RIEUL-THIEUTH;Lo;0;L;<narrow> 313E;;;;N;HALFWIDTH HANGUL LETTER LIEUL TIEUT;;;;
+FFAF;HALFWIDTH HANGUL LETTER RIEUL-PHIEUPH;Lo;0;L;<narrow> 313F;;;;N;HALFWIDTH HANGUL LETTER LIEUL PIEUP;;;;
+FFB0;HALFWIDTH HANGUL LETTER RIEUL-HIEUH;Lo;0;L;<narrow> 3140;;;;N;HALFWIDTH HANGUL LETTER LIEUL HIEUH;;;;
+FFB1;HALFWIDTH HANGUL LETTER MIEUM;Lo;0;L;<narrow> 3141;;;;N;;;;;
+FFB2;HALFWIDTH HANGUL LETTER PIEUP;Lo;0;L;<narrow> 3142;;;;N;HALFWIDTH HANGUL LETTER BIEUB;;;;
+FFB3;HALFWIDTH HANGUL LETTER SSANGPIEUP;Lo;0;L;<narrow> 3143;;;;N;HALFWIDTH HANGUL LETTER SSANG BIEUB;;;;
+FFB4;HALFWIDTH HANGUL LETTER PIEUP-SIOS;Lo;0;L;<narrow> 3144;;;;N;HALFWIDTH HANGUL LETTER BIEUB SIOS;;;;
+FFB5;HALFWIDTH HANGUL LETTER SIOS;Lo;0;L;<narrow> 3145;;;;N;;;;;
+FFB6;HALFWIDTH HANGUL LETTER SSANGSIOS;Lo;0;L;<narrow> 3146;;;;N;HALFWIDTH HANGUL LETTER SSANG SIOS;;;;
+FFB7;HALFWIDTH HANGUL LETTER IEUNG;Lo;0;L;<narrow> 3147;;;;N;;;;;
+FFB8;HALFWIDTH HANGUL LETTER CIEUC;Lo;0;L;<narrow> 3148;;;;N;HALFWIDTH HANGUL LETTER JIEUJ;;;;
+FFB9;HALFWIDTH HANGUL LETTER SSANGCIEUC;Lo;0;L;<narrow> 3149;;;;N;HALFWIDTH HANGUL LETTER SSANG JIEUJ;;;;
+FFBA;HALFWIDTH HANGUL LETTER CHIEUCH;Lo;0;L;<narrow> 314A;;;;N;HALFWIDTH HANGUL LETTER CIEUC;;;;
+FFBB;HALFWIDTH HANGUL LETTER KHIEUKH;Lo;0;L;<narrow> 314B;;;;N;HALFWIDTH HANGUL LETTER KIYEOK;;;;
+FFBC;HALFWIDTH HANGUL LETTER THIEUTH;Lo;0;L;<narrow> 314C;;;;N;HALFWIDTH HANGUL LETTER TIEUT;;;;
+FFBD;HALFWIDTH HANGUL LETTER PHIEUPH;Lo;0;L;<narrow> 314D;;;;N;HALFWIDTH HANGUL LETTER PIEUP;;;;
+FFBE;HALFWIDTH HANGUL LETTER HIEUH;Lo;0;L;<narrow> 314E;;;;N;;;;;
+FFC2;HALFWIDTH HANGUL LETTER A;Lo;0;L;<narrow> 314F;;;;N;;;;;
+FFC3;HALFWIDTH HANGUL LETTER AE;Lo;0;L;<narrow> 3150;;;;N;;;;;
+FFC4;HALFWIDTH HANGUL LETTER YA;Lo;0;L;<narrow> 3151;;;;N;;;;;
+FFC5;HALFWIDTH HANGUL LETTER YAE;Lo;0;L;<narrow> 3152;;;;N;;;;;
+FFC6;HALFWIDTH HANGUL LETTER EO;Lo;0;L;<narrow> 3153;;;;N;;;;;
+FFC7;HALFWIDTH HANGUL LETTER E;Lo;0;L;<narrow> 3154;;;;N;;;;;
+FFCA;HALFWIDTH HANGUL LETTER YEO;Lo;0;L;<narrow> 3155;;;;N;;;;;
+FFCB;HALFWIDTH HANGUL LETTER YE;Lo;0;L;<narrow> 3156;;;;N;;;;;
+FFCC;HALFWIDTH HANGUL LETTER O;Lo;0;L;<narrow> 3157;;;;N;;;;;
+FFCD;HALFWIDTH HANGUL LETTER WA;Lo;0;L;<narrow> 3158;;;;N;;;;;
+FFCE;HALFWIDTH HANGUL LETTER WAE;Lo;0;L;<narrow> 3159;;;;N;;;;;
+FFCF;HALFWIDTH HANGUL LETTER OE;Lo;0;L;<narrow> 315A;;;;N;;;;;
+FFD2;HALFWIDTH HANGUL LETTER YO;Lo;0;L;<narrow> 315B;;;;N;;;;;
+FFD3;HALFWIDTH HANGUL LETTER U;Lo;0;L;<narrow> 315C;;;;N;;;;;
+FFD4;HALFWIDTH HANGUL LETTER WEO;Lo;0;L;<narrow> 315D;;;;N;;;;;
+FFD5;HALFWIDTH HANGUL LETTER WE;Lo;0;L;<narrow> 315E;;;;N;;;;;
+FFD6;HALFWIDTH HANGUL LETTER WI;Lo;0;L;<narrow> 315F;;;;N;;;;;
+FFD7;HALFWIDTH HANGUL LETTER YU;Lo;0;L;<narrow> 3160;;;;N;;;;;
+FFDA;HALFWIDTH HANGUL LETTER EU;Lo;0;L;<narrow> 3161;;;;N;;;;;
+FFDB;HALFWIDTH HANGUL LETTER YI;Lo;0;L;<narrow> 3162;;;;N;;;;;
+FFDC;HALFWIDTH HANGUL LETTER I;Lo;0;L;<narrow> 3163;;;;N;;;;;
+FFE0;FULLWIDTH CENT SIGN;Sc;0;ET;<wide> 00A2;;;;N;;;;;
+FFE1;FULLWIDTH POUND SIGN;Sc;0;ET;<wide> 00A3;;;;N;;;;;
+FFE2;FULLWIDTH NOT SIGN;Sm;0;ON;<wide> 00AC;;;;N;;;;;
+FFE3;FULLWIDTH MACRON;Sk;0;ON;<wide> 00AF;;;;N;FULLWIDTH SPACING MACRON;*;;;
+FFE4;FULLWIDTH BROKEN BAR;So;0;ON;<wide> 00A6;;;;N;FULLWIDTH BROKEN VERTICAL BAR;;;;
+FFE5;FULLWIDTH YEN SIGN;Sc;0;ET;<wide> 00A5;;;;N;;;;;
+FFE6;FULLWIDTH WON SIGN;Sc;0;ET;<wide> 20A9;;;;N;;;;;
+FFE8;HALFWIDTH FORMS LIGHT VERTICAL;So;0;ON;<narrow> 2502;;;;N;;;;;
+FFE9;HALFWIDTH LEFTWARDS ARROW;Sm;0;ON;<narrow> 2190;;;;N;;;;;
+FFEA;HALFWIDTH UPWARDS ARROW;Sm;0;ON;<narrow> 2191;;;;N;;;;;
+FFEB;HALFWIDTH RIGHTWARDS ARROW;Sm;0;ON;<narrow> 2192;;;;N;;;;;
+FFEC;HALFWIDTH DOWNWARDS ARROW;Sm;0;ON;<narrow> 2193;;;;N;;;;;
+FFED;HALFWIDTH BLACK SQUARE;So;0;ON;<narrow> 25A0;;;;N;;;;;
+FFEE;HALFWIDTH WHITE CIRCLE;So;0;ON;<narrow> 25CB;;;;N;;;;;
+FFF9;INTERLINEAR ANNOTATION ANCHOR;Cf;0;ON;;;;;N;;;;;
+FFFA;INTERLINEAR ANNOTATION SEPARATOR;Cf;0;ON;;;;;N;;;;;
+FFFB;INTERLINEAR ANNOTATION TERMINATOR;Cf;0;ON;;;;;N;;;;;
+FFFC;OBJECT REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
+FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
+10000;LINEAR B SYLLABLE B008 A;Lo;0;L;;;;;N;;;;;
+10001;LINEAR B SYLLABLE B038 E;Lo;0;L;;;;;N;;;;;
+10002;LINEAR B SYLLABLE B028 I;Lo;0;L;;;;;N;;;;;
+10003;LINEAR B SYLLABLE B061 O;Lo;0;L;;;;;N;;;;;
+10004;LINEAR B SYLLABLE B010 U;Lo;0;L;;;;;N;;;;;
+10005;LINEAR B SYLLABLE B001 DA;Lo;0;L;;;;;N;;;;;
+10006;LINEAR B SYLLABLE B045 DE;Lo;0;L;;;;;N;;;;;
+10007;LINEAR B SYLLABLE B007 DI;Lo;0;L;;;;;N;;;;;
+10008;LINEAR B SYLLABLE B014 DO;Lo;0;L;;;;;N;;;;;
+10009;LINEAR B SYLLABLE B051 DU;Lo;0;L;;;;;N;;;;;
+1000A;LINEAR B SYLLABLE B057 JA;Lo;0;L;;;;;N;;;;;
+1000B;LINEAR B SYLLABLE B046 JE;Lo;0;L;;;;;N;;;;;
+1000D;LINEAR B SYLLABLE B036 JO;Lo;0;L;;;;;N;;;;;
+1000E;LINEAR B SYLLABLE B065 JU;Lo;0;L;;;;;N;;;;;
+1000F;LINEAR B SYLLABLE B077 KA;Lo;0;L;;;;;N;;;;;
+10010;LINEAR B SYLLABLE B044 KE;Lo;0;L;;;;;N;;;;;
+10011;LINEAR B SYLLABLE B067 KI;Lo;0;L;;;;;N;;;;;
+10012;LINEAR B SYLLABLE B070 KO;Lo;0;L;;;;;N;;;;;
+10013;LINEAR B SYLLABLE B081 KU;Lo;0;L;;;;;N;;;;;
+10014;LINEAR B SYLLABLE B080 MA;Lo;0;L;;;;;N;;;;;
+10015;LINEAR B SYLLABLE B013 ME;Lo;0;L;;;;;N;;;;;
+10016;LINEAR B SYLLABLE B073 MI;Lo;0;L;;;;;N;;;;;
+10017;LINEAR B SYLLABLE B015 MO;Lo;0;L;;;;;N;;;;;
+10018;LINEAR B SYLLABLE B023 MU;Lo;0;L;;;;;N;;;;;
+10019;LINEAR B SYLLABLE B006 NA;Lo;0;L;;;;;N;;;;;
+1001A;LINEAR B SYLLABLE B024 NE;Lo;0;L;;;;;N;;;;;
+1001B;LINEAR B SYLLABLE B030 NI;Lo;0;L;;;;;N;;;;;
+1001C;LINEAR B SYLLABLE B052 NO;Lo;0;L;;;;;N;;;;;
+1001D;LINEAR B SYLLABLE B055 NU;Lo;0;L;;;;;N;;;;;
+1001E;LINEAR B SYLLABLE B003 PA;Lo;0;L;;;;;N;;;;;
+1001F;LINEAR B SYLLABLE B072 PE;Lo;0;L;;;;;N;;;;;
+10020;LINEAR B SYLLABLE B039 PI;Lo;0;L;;;;;N;;;;;
+10021;LINEAR B SYLLABLE B011 PO;Lo;0;L;;;;;N;;;;;
+10022;LINEAR B SYLLABLE B050 PU;Lo;0;L;;;;;N;;;;;
+10023;LINEAR B SYLLABLE B016 QA;Lo;0;L;;;;;N;;;;;
+10024;LINEAR B SYLLABLE B078 QE;Lo;0;L;;;;;N;;;;;
+10025;LINEAR B SYLLABLE B021 QI;Lo;0;L;;;;;N;;;;;
+10026;LINEAR B SYLLABLE B032 QO;Lo;0;L;;;;;N;;;;;
+10028;LINEAR B SYLLABLE B060 RA;Lo;0;L;;;;;N;;;;;
+10029;LINEAR B SYLLABLE B027 RE;Lo;0;L;;;;;N;;;;;
+1002A;LINEAR B SYLLABLE B053 RI;Lo;0;L;;;;;N;;;;;
+1002B;LINEAR B SYLLABLE B002 RO;Lo;0;L;;;;;N;;;;;
+1002C;LINEAR B SYLLABLE B026 RU;Lo;0;L;;;;;N;;;;;
+1002D;LINEAR B SYLLABLE B031 SA;Lo;0;L;;;;;N;;;;;
+1002E;LINEAR B SYLLABLE B009 SE;Lo;0;L;;;;;N;;;;;
+1002F;LINEAR B SYLLABLE B041 SI;Lo;0;L;;;;;N;;;;;
+10030;LINEAR B SYLLABLE B012 SO;Lo;0;L;;;;;N;;;;;
+10031;LINEAR B SYLLABLE B058 SU;Lo;0;L;;;;;N;;;;;
+10032;LINEAR B SYLLABLE B059 TA;Lo;0;L;;;;;N;;;;;
+10033;LINEAR B SYLLABLE B004 TE;Lo;0;L;;;;;N;;;;;
+10034;LINEAR B SYLLABLE B037 TI;Lo;0;L;;;;;N;;;;;
+10035;LINEAR B SYLLABLE B005 TO;Lo;0;L;;;;;N;;;;;
+10036;LINEAR B SYLLABLE B069 TU;Lo;0;L;;;;;N;;;;;
+10037;LINEAR B SYLLABLE B054 WA;Lo;0;L;;;;;N;;;;;
+10038;LINEAR B SYLLABLE B075 WE;Lo;0;L;;;;;N;;;;;
+10039;LINEAR B SYLLABLE B040 WI;Lo;0;L;;;;;N;;;;;
+1003A;LINEAR B SYLLABLE B042 WO;Lo;0;L;;;;;N;;;;;
+1003C;LINEAR B SYLLABLE B017 ZA;Lo;0;L;;;;;N;;;;;
+1003D;LINEAR B SYLLABLE B074 ZE;Lo;0;L;;;;;N;;;;;
+1003F;LINEAR B SYLLABLE B020 ZO;Lo;0;L;;;;;N;;;;;
+10040;LINEAR B SYLLABLE B025 A2;Lo;0;L;;;;;N;;;;;
+10041;LINEAR B SYLLABLE B043 A3;Lo;0;L;;;;;N;;;;;
+10042;LINEAR B SYLLABLE B085 AU;Lo;0;L;;;;;N;;;;;
+10043;LINEAR B SYLLABLE B071 DWE;Lo;0;L;;;;;N;;;;;
+10044;LINEAR B SYLLABLE B090 DWO;Lo;0;L;;;;;N;;;;;
+10045;LINEAR B SYLLABLE B048 NWA;Lo;0;L;;;;;N;;;;;
+10046;LINEAR B SYLLABLE B029 PU2;Lo;0;L;;;;;N;;;;;
+10047;LINEAR B SYLLABLE B062 PTE;Lo;0;L;;;;;N;;;;;
+10048;LINEAR B SYLLABLE B076 RA2;Lo;0;L;;;;;N;;;;;
+10049;LINEAR B SYLLABLE B033 RA3;Lo;0;L;;;;;N;;;;;
+1004A;LINEAR B SYLLABLE B068 RO2;Lo;0;L;;;;;N;;;;;
+1004B;LINEAR B SYLLABLE B066 TA2;Lo;0;L;;;;;N;;;;;
+1004C;LINEAR B SYLLABLE B087 TWE;Lo;0;L;;;;;N;;;;;
+1004D;LINEAR B SYLLABLE B091 TWO;Lo;0;L;;;;;N;;;;;
+10050;LINEAR B SYMBOL B018;Lo;0;L;;;;;N;;;;;
+10051;LINEAR B SYMBOL B019;Lo;0;L;;;;;N;;;;;
+10052;LINEAR B SYMBOL B022;Lo;0;L;;;;;N;;;;;
+10053;LINEAR B SYMBOL B034;Lo;0;L;;;;;N;;;;;
+10054;LINEAR B SYMBOL B047;Lo;0;L;;;;;N;;;;;
+10055;LINEAR B SYMBOL B049;Lo;0;L;;;;;N;;;;;
+10056;LINEAR B SYMBOL B056;Lo;0;L;;;;;N;;;;;
+10057;LINEAR B SYMBOL B063;Lo;0;L;;;;;N;;;;;
+10058;LINEAR B SYMBOL B064;Lo;0;L;;;;;N;;;;;
+10059;LINEAR B SYMBOL B079;Lo;0;L;;;;;N;;;;;
+1005A;LINEAR B SYMBOL B082;Lo;0;L;;;;;N;;;;;
+1005B;LINEAR B SYMBOL B083;Lo;0;L;;;;;N;;;;;
+1005C;LINEAR B SYMBOL B086;Lo;0;L;;;;;N;;;;;
+1005D;LINEAR B SYMBOL B089;Lo;0;L;;;;;N;;;;;
+10080;LINEAR B IDEOGRAM B100 MAN;Lo;0;L;;;;;N;;;;;
+10081;LINEAR B IDEOGRAM B102 WOMAN;Lo;0;L;;;;;N;;;;;
+10082;LINEAR B IDEOGRAM B104 DEER;Lo;0;L;;;;;N;;;;;
+10083;LINEAR B IDEOGRAM B105 EQUID;Lo;0;L;;;;;N;;;;;
+10084;LINEAR B IDEOGRAM B105F MARE;Lo;0;L;;;;;N;;;;;
+10085;LINEAR B IDEOGRAM B105M STALLION;Lo;0;L;;;;;N;;;;;
+10086;LINEAR B IDEOGRAM B106F EWE;Lo;0;L;;;;;N;;;;;
+10087;LINEAR B IDEOGRAM B106M RAM;Lo;0;L;;;;;N;;;;;
+10088;LINEAR B IDEOGRAM B107F SHE-GOAT;Lo;0;L;;;;;N;;;;;
+10089;LINEAR B IDEOGRAM B107M HE-GOAT;Lo;0;L;;;;;N;;;;;
+1008A;LINEAR B IDEOGRAM B108F SOW;Lo;0;L;;;;;N;;;;;
+1008B;LINEAR B IDEOGRAM B108M BOAR;Lo;0;L;;;;;N;;;;;
+1008C;LINEAR B IDEOGRAM B109F COW;Lo;0;L;;;;;N;;;;;
+1008D;LINEAR B IDEOGRAM B109M BULL;Lo;0;L;;;;;N;;;;;
+1008E;LINEAR B IDEOGRAM B120 WHEAT;Lo;0;L;;;;;N;;;;;
+1008F;LINEAR B IDEOGRAM B121 BARLEY;Lo;0;L;;;;;N;;;;;
+10090;LINEAR B IDEOGRAM B122 OLIVE;Lo;0;L;;;;;N;;;;;
+10091;LINEAR B IDEOGRAM B123 SPICE;Lo;0;L;;;;;N;;;;;
+10092;LINEAR B IDEOGRAM B125 CYPERUS;Lo;0;L;;;;;N;;;;;
+10093;LINEAR B MONOGRAM B127 KAPO;Lo;0;L;;;;;N;;;;;
+10094;LINEAR B MONOGRAM B128 KANAKO;Lo;0;L;;;;;N;;;;;
+10095;LINEAR B IDEOGRAM B130 OIL;Lo;0;L;;;;;N;;;;;
+10096;LINEAR B IDEOGRAM B131 WINE;Lo;0;L;;;;;N;;;;;
+10097;LINEAR B IDEOGRAM B132;Lo;0;L;;;;;N;;;;;
+10098;LINEAR B MONOGRAM B133 AREPA;Lo;0;L;;;;;N;;;;;
+10099;LINEAR B MONOGRAM B135 MERI;Lo;0;L;;;;;N;;;;;
+1009A;LINEAR B IDEOGRAM B140 BRONZE;Lo;0;L;;;;;N;;;;;
+1009B;LINEAR B IDEOGRAM B141 GOLD;Lo;0;L;;;;;N;;;;;
+1009C;LINEAR B IDEOGRAM B142;Lo;0;L;;;;;N;;;;;
+1009D;LINEAR B IDEOGRAM B145 WOOL;Lo;0;L;;;;;N;;;;;
+1009E;LINEAR B IDEOGRAM B146;Lo;0;L;;;;;N;;;;;
+1009F;LINEAR B IDEOGRAM B150;Lo;0;L;;;;;N;;;;;
+100A0;LINEAR B IDEOGRAM B151 HORN;Lo;0;L;;;;;N;;;;;
+100A1;LINEAR B IDEOGRAM B152;Lo;0;L;;;;;N;;;;;
+100A2;LINEAR B IDEOGRAM B153;Lo;0;L;;;;;N;;;;;
+100A3;LINEAR B IDEOGRAM B154;Lo;0;L;;;;;N;;;;;
+100A4;LINEAR B MONOGRAM B156 TURO2;Lo;0;L;;;;;N;;;;;
+100A5;LINEAR B IDEOGRAM B157;Lo;0;L;;;;;N;;;;;
+100A6;LINEAR B IDEOGRAM B158;Lo;0;L;;;;;N;;;;;
+100A7;LINEAR B IDEOGRAM B159 CLOTH;Lo;0;L;;;;;N;;;;;
+100A8;LINEAR B IDEOGRAM B160;Lo;0;L;;;;;N;;;;;
+100A9;LINEAR B IDEOGRAM B161;Lo;0;L;;;;;N;;;;;
+100AA;LINEAR B IDEOGRAM B162 GARMENT;Lo;0;L;;;;;N;;;;;
+100AB;LINEAR B IDEOGRAM B163 ARMOUR;Lo;0;L;;;;;N;;;;;
+100AC;LINEAR B IDEOGRAM B164;Lo;0;L;;;;;N;;;;;
+100AD;LINEAR B IDEOGRAM B165;Lo;0;L;;;;;N;;;;;
+100AE;LINEAR B IDEOGRAM B166;Lo;0;L;;;;;N;;;;;
+100AF;LINEAR B IDEOGRAM B167;Lo;0;L;;;;;N;;;;;
+100B0;LINEAR B IDEOGRAM B168;Lo;0;L;;;;;N;;;;;
+100B1;LINEAR B IDEOGRAM B169;Lo;0;L;;;;;N;;;;;
+100B2;LINEAR B IDEOGRAM B170;Lo;0;L;;;;;N;;;;;
+100B3;LINEAR B IDEOGRAM B171;Lo;0;L;;;;;N;;;;;
+100B4;LINEAR B IDEOGRAM B172;Lo;0;L;;;;;N;;;;;
+100B5;LINEAR B IDEOGRAM B173 MONTH;Lo;0;L;;;;;N;;;;;
+100B6;LINEAR B IDEOGRAM B174;Lo;0;L;;;;;N;;;;;
+100B7;LINEAR B IDEOGRAM B176 TREE;Lo;0;L;;;;;N;;;;;
+100B8;LINEAR B IDEOGRAM B177;Lo;0;L;;;;;N;;;;;
+100B9;LINEAR B IDEOGRAM B178;Lo;0;L;;;;;N;;;;;
+100BA;LINEAR B IDEOGRAM B179;Lo;0;L;;;;;N;;;;;
+100BB;LINEAR B IDEOGRAM B180;Lo;0;L;;;;;N;;;;;
+100BC;LINEAR B IDEOGRAM B181;Lo;0;L;;;;;N;;;;;
+100BD;LINEAR B IDEOGRAM B182;Lo;0;L;;;;;N;;;;;
+100BE;LINEAR B IDEOGRAM B183;Lo;0;L;;;;;N;;;;;
+100BF;LINEAR B IDEOGRAM B184;Lo;0;L;;;;;N;;;;;
+100C0;LINEAR B IDEOGRAM B185;Lo;0;L;;;;;N;;;;;
+100C1;LINEAR B IDEOGRAM B189;Lo;0;L;;;;;N;;;;;
+100C2;LINEAR B IDEOGRAM B190;Lo;0;L;;;;;N;;;;;
+100C3;LINEAR B IDEOGRAM B191 HELMET;Lo;0;L;;;;;N;;;;;
+100C4;LINEAR B IDEOGRAM B220 FOOTSTOOL;Lo;0;L;;;;;N;;;;;
+100C5;LINEAR B IDEOGRAM B225 BATHTUB;Lo;0;L;;;;;N;;;;;
+100C6;LINEAR B IDEOGRAM B230 SPEAR;Lo;0;L;;;;;N;;;;;
+100C7;LINEAR B IDEOGRAM B231 ARROW;Lo;0;L;;;;;N;;;;;
+100C8;LINEAR B IDEOGRAM B232;Lo;0;L;;;;;N;;;;;
+100C9;LINEAR B IDEOGRAM B233 SWORD;Lo;0;L;;;;;N;;pug;;;
+100CA;LINEAR B IDEOGRAM B234;Lo;0;L;;;;;N;;;;;
+100CB;LINEAR B IDEOGRAM B236;Lo;0;L;;;;;N;;gup;;;
+100CC;LINEAR B IDEOGRAM B240 WHEELED CHARIOT;Lo;0;L;;;;;N;;;;;
+100CD;LINEAR B IDEOGRAM B241 CHARIOT;Lo;0;L;;;;;N;;;;;
+100CE;LINEAR B IDEOGRAM B242 CHARIOT FRAME;Lo;0;L;;;;;N;;;;;
+100CF;LINEAR B IDEOGRAM B243 WHEEL;Lo;0;L;;;;;N;;;;;
+100D0;LINEAR B IDEOGRAM B245;Lo;0;L;;;;;N;;;;;
+100D1;LINEAR B IDEOGRAM B246;Lo;0;L;;;;;N;;;;;
+100D2;LINEAR B MONOGRAM B247 DIPTE;Lo;0;L;;;;;N;;;;;
+100D3;LINEAR B IDEOGRAM B248;Lo;0;L;;;;;N;;;;;
+100D4;LINEAR B IDEOGRAM B249;Lo;0;L;;;;;N;;;;;
+100D5;LINEAR B IDEOGRAM B251;Lo;0;L;;;;;N;;;;;
+100D6;LINEAR B IDEOGRAM B252;Lo;0;L;;;;;N;;;;;
+100D7;LINEAR B IDEOGRAM B253;Lo;0;L;;;;;N;;;;;
+100D8;LINEAR B IDEOGRAM B254 DART;Lo;0;L;;;;;N;;;;;
+100D9;LINEAR B IDEOGRAM B255;Lo;0;L;;;;;N;;;;;
+100DA;LINEAR B IDEOGRAM B256;Lo;0;L;;;;;N;;;;;
+100DB;LINEAR B IDEOGRAM B257;Lo;0;L;;;;;N;;;;;
+100DC;LINEAR B IDEOGRAM B258;Lo;0;L;;;;;N;;;;;
+100DD;LINEAR B IDEOGRAM B259;Lo;0;L;;;;;N;;;;;
+100DE;LINEAR B IDEOGRAM VESSEL B155;Lo;0;L;;;;;N;;;;;
+100DF;LINEAR B IDEOGRAM VESSEL B200;Lo;0;L;;;;;N;;;;;
+100E0;LINEAR B IDEOGRAM VESSEL B201;Lo;0;L;;;;;N;;;;;
+100E1;LINEAR B IDEOGRAM VESSEL B202;Lo;0;L;;;;;N;;;;;
+100E2;LINEAR B IDEOGRAM VESSEL B203;Lo;0;L;;;;;N;;;;;
+100E3;LINEAR B IDEOGRAM VESSEL B204;Lo;0;L;;;;;N;;;;;
+100E4;LINEAR B IDEOGRAM VESSEL B205;Lo;0;L;;;;;N;;;;;
+100E5;LINEAR B IDEOGRAM VESSEL B206;Lo;0;L;;;;;N;;;;;
+100E6;LINEAR B IDEOGRAM VESSEL B207;Lo;0;L;;;;;N;;;;;
+100E7;LINEAR B IDEOGRAM VESSEL B208;Lo;0;L;;;;;N;;;;;
+100E8;LINEAR B IDEOGRAM VESSEL B209;Lo;0;L;;;;;N;;;;;
+100E9;LINEAR B IDEOGRAM VESSEL B210;Lo;0;L;;;;;N;;;;;
+100EA;LINEAR B IDEOGRAM VESSEL B211;Lo;0;L;;;;;N;;;;;
+100EB;LINEAR B IDEOGRAM VESSEL B212;Lo;0;L;;;;;N;;;;;
+100EC;LINEAR B IDEOGRAM VESSEL B213;Lo;0;L;;;;;N;;;;;
+100ED;LINEAR B IDEOGRAM VESSEL B214;Lo;0;L;;;;;N;;;;;
+100EE;LINEAR B IDEOGRAM VESSEL B215;Lo;0;L;;;;;N;;;;;
+100EF;LINEAR B IDEOGRAM VESSEL B216;Lo;0;L;;;;;N;;;;;
+100F0;LINEAR B IDEOGRAM VESSEL B217;Lo;0;L;;;;;N;;;;;
+100F1;LINEAR B IDEOGRAM VESSEL B218;Lo;0;L;;;;;N;;;;;
+100F2;LINEAR B IDEOGRAM VESSEL B219;Lo;0;L;;;;;N;;;;;
+100F3;LINEAR B IDEOGRAM VESSEL B221;Lo;0;L;;;;;N;;;;;
+100F4;LINEAR B IDEOGRAM VESSEL B222;Lo;0;L;;;;;N;;;;;
+100F5;LINEAR B IDEOGRAM VESSEL B226;Lo;0;L;;;;;N;;;;;
+100F6;LINEAR B IDEOGRAM VESSEL B227;Lo;0;L;;;;;N;;;;;
+100F7;LINEAR B IDEOGRAM VESSEL B228;Lo;0;L;;;;;N;;;;;
+100F8;LINEAR B IDEOGRAM VESSEL B229;Lo;0;L;;;;;N;;;;;
+100F9;LINEAR B IDEOGRAM VESSEL B250;Lo;0;L;;;;;N;;;;;
+100FA;LINEAR B IDEOGRAM VESSEL B305;Lo;0;L;;;;;N;;;;;
+10100;AEGEAN WORD SEPARATOR LINE;Po;0;L;;;;;N;;;;;
+10101;AEGEAN WORD SEPARATOR DOT;Po;0;ON;;;;;N;;;;;
+10102;AEGEAN CHECK MARK;So;0;L;;;;;N;;;;;
+10107;AEGEAN NUMBER ONE;No;0;L;;;;1;N;;;;;
+10108;AEGEAN NUMBER TWO;No;0;L;;;;2;N;;;;;
+10109;AEGEAN NUMBER THREE;No;0;L;;;;3;N;;;;;
+1010A;AEGEAN NUMBER FOUR;No;0;L;;;;4;N;;;;;
+1010B;AEGEAN NUMBER FIVE;No;0;L;;;;5;N;;;;;
+1010C;AEGEAN NUMBER SIX;No;0;L;;;;6;N;;;;;
+1010D;AEGEAN NUMBER SEVEN;No;0;L;;;;7;N;;;;;
+1010E;AEGEAN NUMBER EIGHT;No;0;L;;;;8;N;;;;;
+1010F;AEGEAN NUMBER NINE;No;0;L;;;;9;N;;;;;
+10110;AEGEAN NUMBER TEN;No;0;L;;;;10;N;;;;;
+10111;AEGEAN NUMBER TWENTY;No;0;L;;;;20;N;;;;;
+10112;AEGEAN NUMBER THIRTY;No;0;L;;;;30;N;;;;;
+10113;AEGEAN NUMBER FORTY;No;0;L;;;;40;N;;;;;
+10114;AEGEAN NUMBER FIFTY;No;0;L;;;;50;N;;;;;
+10115;AEGEAN NUMBER SIXTY;No;0;L;;;;60;N;;;;;
+10116;AEGEAN NUMBER SEVENTY;No;0;L;;;;70;N;;;;;
+10117;AEGEAN NUMBER EIGHTY;No;0;L;;;;80;N;;;;;
+10118;AEGEAN NUMBER NINETY;No;0;L;;;;90;N;;;;;
+10119;AEGEAN NUMBER ONE HUNDRED;No;0;L;;;;100;N;;;;;
+1011A;AEGEAN NUMBER TWO HUNDRED;No;0;L;;;;200;N;;;;;
+1011B;AEGEAN NUMBER THREE HUNDRED;No;0;L;;;;300;N;;;;;
+1011C;AEGEAN NUMBER FOUR HUNDRED;No;0;L;;;;400;N;;;;;
+1011D;AEGEAN NUMBER FIVE HUNDRED;No;0;L;;;;500;N;;;;;
+1011E;AEGEAN NUMBER SIX HUNDRED;No;0;L;;;;600;N;;;;;
+1011F;AEGEAN NUMBER SEVEN HUNDRED;No;0;L;;;;700;N;;;;;
+10120;AEGEAN NUMBER EIGHT HUNDRED;No;0;L;;;;800;N;;;;;
+10121;AEGEAN NUMBER NINE HUNDRED;No;0;L;;;;900;N;;;;;
+10122;AEGEAN NUMBER ONE THOUSAND;No;0;L;;;;1000;N;;;;;
+10123;AEGEAN NUMBER TWO THOUSAND;No;0;L;;;;2000;N;;;;;
+10124;AEGEAN NUMBER THREE THOUSAND;No;0;L;;;;3000;N;;;;;
+10125;AEGEAN NUMBER FOUR THOUSAND;No;0;L;;;;4000;N;;;;;
+10126;AEGEAN NUMBER FIVE THOUSAND;No;0;L;;;;5000;N;;;;;
+10127;AEGEAN NUMBER SIX THOUSAND;No;0;L;;;;6000;N;;;;;
+10128;AEGEAN NUMBER SEVEN THOUSAND;No;0;L;;;;7000;N;;;;;
+10129;AEGEAN NUMBER EIGHT THOUSAND;No;0;L;;;;8000;N;;;;;
+1012A;AEGEAN NUMBER NINE THOUSAND;No;0;L;;;;9000;N;;;;;
+1012B;AEGEAN NUMBER TEN THOUSAND;No;0;L;;;;10000;N;;;;;
+1012C;AEGEAN NUMBER TWENTY THOUSAND;No;0;L;;;;20000;N;;;;;
+1012D;AEGEAN NUMBER THIRTY THOUSAND;No;0;L;;;;30000;N;;;;;
+1012E;AEGEAN NUMBER FORTY THOUSAND;No;0;L;;;;40000;N;;;;;
+1012F;AEGEAN NUMBER FIFTY THOUSAND;No;0;L;;;;50000;N;;;;;
+10130;AEGEAN NUMBER SIXTY THOUSAND;No;0;L;;;;60000;N;;;;;
+10131;AEGEAN NUMBER SEVENTY THOUSAND;No;0;L;;;;70000;N;;;;;
+10132;AEGEAN NUMBER EIGHTY THOUSAND;No;0;L;;;;80000;N;;;;;
+10133;AEGEAN NUMBER NINETY THOUSAND;No;0;L;;;;90000;N;;;;;
+10137;AEGEAN WEIGHT BASE UNIT;So;0;L;;;;;N;;;;;
+10138;AEGEAN WEIGHT FIRST SUBUNIT;So;0;L;;;;;N;;;;;
+10139;AEGEAN WEIGHT SECOND SUBUNIT;So;0;L;;;;;N;;;;;
+1013A;AEGEAN WEIGHT THIRD SUBUNIT;So;0;L;;;;;N;;;;;
+1013B;AEGEAN WEIGHT FOURTH SUBUNIT;So;0;L;;;;;N;;;;;
+1013C;AEGEAN DRY MEASURE FIRST SUBUNIT;So;0;L;;;;;N;;;;;
+1013D;AEGEAN LIQUID MEASURE FIRST SUBUNIT;So;0;L;;;;;N;;;;;
+1013E;AEGEAN MEASURE SECOND SUBUNIT;So;0;L;;;;;N;;;;;
+1013F;AEGEAN MEASURE THIRD SUBUNIT;So;0;L;;;;;N;;;;;
+10300;OLD ITALIC LETTER A;Lo;0;L;;;;;N;;;;;
+10301;OLD ITALIC LETTER BE;Lo;0;L;;;;;N;;;;;
+10302;OLD ITALIC LETTER KE;Lo;0;L;;;;;N;;;;;
+10303;OLD ITALIC LETTER DE;Lo;0;L;;;;;N;;;;;
+10304;OLD ITALIC LETTER E;Lo;0;L;;;;;N;;;;;
+10305;OLD ITALIC LETTER VE;Lo;0;L;;;;;N;;;;;
+10306;OLD ITALIC LETTER ZE;Lo;0;L;;;;;N;;;;;
+10307;OLD ITALIC LETTER HE;Lo;0;L;;;;;N;;;;;
+10308;OLD ITALIC LETTER THE;Lo;0;L;;;;;N;;;;;
+10309;OLD ITALIC LETTER I;Lo;0;L;;;;;N;;;;;
+1030A;OLD ITALIC LETTER KA;Lo;0;L;;;;;N;;;;;
+1030B;OLD ITALIC LETTER EL;Lo;0;L;;;;;N;;;;;
+1030C;OLD ITALIC LETTER EM;Lo;0;L;;;;;N;;;;;
+1030D;OLD ITALIC LETTER EN;Lo;0;L;;;;;N;;;;;
+1030E;OLD ITALIC LETTER ESH;Lo;0;L;;;;;N;;;;;
+1030F;OLD ITALIC LETTER O;Lo;0;L;;;;;N;;Faliscan;;;
+10310;OLD ITALIC LETTER PE;Lo;0;L;;;;;N;;;;;
+10311;OLD ITALIC LETTER SHE;Lo;0;L;;;;;N;;;;;
+10312;OLD ITALIC LETTER KU;Lo;0;L;;;;;N;;;;;
+10313;OLD ITALIC LETTER ER;Lo;0;L;;;;;N;;;;;
+10314;OLD ITALIC LETTER ES;Lo;0;L;;;;;N;;;;;
+10315;OLD ITALIC LETTER TE;Lo;0;L;;;;;N;;;;;
+10316;OLD ITALIC LETTER U;Lo;0;L;;;;;N;;;;;
+10317;OLD ITALIC LETTER EKS;Lo;0;L;;;;;N;;Faliscan;;;
+10318;OLD ITALIC LETTER PHE;Lo;0;L;;;;;N;;;;;
+10319;OLD ITALIC LETTER KHE;Lo;0;L;;;;;N;;;;;
+1031A;OLD ITALIC LETTER EF;Lo;0;L;;;;;N;;;;;
+1031B;OLD ITALIC LETTER ERS;Lo;0;L;;;;;N;;Umbrian;;;
+1031C;OLD ITALIC LETTER CHE;Lo;0;L;;;;;N;;Umbrian;;;
+1031D;OLD ITALIC LETTER II;Lo;0;L;;;;;N;;Oscan;;;
+1031E;OLD ITALIC LETTER UU;Lo;0;L;;;;;N;;Oscan;;;
+10320;OLD ITALIC NUMERAL ONE;No;0;L;;;;1;N;;;;;
+10321;OLD ITALIC NUMERAL FIVE;No;0;L;;;;5;N;;;;;
+10322;OLD ITALIC NUMERAL TEN;No;0;L;;;;10;N;;;;;
+10323;OLD ITALIC NUMERAL FIFTY;No;0;L;;;;50;N;;;;;
+10330;GOTHIC LETTER AHSA;Lo;0;L;;;;;N;;;;;
+10331;GOTHIC LETTER BAIRKAN;Lo;0;L;;;;;N;;;;;
+10332;GOTHIC LETTER GIBA;Lo;0;L;;;;;N;;;;;
+10333;GOTHIC LETTER DAGS;Lo;0;L;;;;;N;;;;;
+10334;GOTHIC LETTER AIHVUS;Lo;0;L;;;;;N;;;;;
+10335;GOTHIC LETTER QAIRTHRA;Lo;0;L;;;;;N;;;;;
+10336;GOTHIC LETTER IUJA;Lo;0;L;;;;;N;;;;;
+10337;GOTHIC LETTER HAGL;Lo;0;L;;;;;N;;;;;
+10338;GOTHIC LETTER THIUTH;Lo;0;L;;;;;N;;;;;
+10339;GOTHIC LETTER EIS;Lo;0;L;;;;;N;;;;;
+1033A;GOTHIC LETTER KUSMA;Lo;0;L;;;;;N;;;;;
+1033B;GOTHIC LETTER LAGUS;Lo;0;L;;;;;N;;;;;
+1033C;GOTHIC LETTER MANNA;Lo;0;L;;;;;N;;;;;
+1033D;GOTHIC LETTER NAUTHS;Lo;0;L;;;;;N;;;;;
+1033E;GOTHIC LETTER JER;Lo;0;L;;;;;N;;;;;
+1033F;GOTHIC LETTER URUS;Lo;0;L;;;;;N;;;;;
+10340;GOTHIC LETTER PAIRTHRA;Lo;0;L;;;;;N;;;;;
+10341;GOTHIC LETTER NINETY;Lo;0;L;;;;;N;;;;;
+10342;GOTHIC LETTER RAIDA;Lo;0;L;;;;;N;;;;;
+10343;GOTHIC LETTER SAUIL;Lo;0;L;;;;;N;;;;;
+10344;GOTHIC LETTER TEIWS;Lo;0;L;;;;;N;;;;;
+10345;GOTHIC LETTER WINJA;Lo;0;L;;;;;N;;;;;
+10346;GOTHIC LETTER FAIHU;Lo;0;L;;;;;N;;;;;
+10347;GOTHIC LETTER IGGWS;Lo;0;L;;;;;N;;;;;
+10348;GOTHIC LETTER HWAIR;Lo;0;L;;;;;N;;;;;
+10349;GOTHIC LETTER OTHAL;Lo;0;L;;;;;N;;;;;
+1034A;GOTHIC LETTER NINE HUNDRED;Nl;0;L;;;;;N;;;;;
+10380;UGARITIC LETTER ALPA;Lo;0;L;;;;;N;;;;;
+10381;UGARITIC LETTER BETA;Lo;0;L;;;;;N;;;;;
+10382;UGARITIC LETTER GAMLA;Lo;0;L;;;;;N;;;;;
+10383;UGARITIC LETTER KHA;Lo;0;L;;;;;N;;;;;
+10384;UGARITIC LETTER DELTA;Lo;0;L;;;;;N;;;;;
+10385;UGARITIC LETTER HO;Lo;0;L;;;;;N;;;;;
+10386;UGARITIC LETTER WO;Lo;0;L;;;;;N;;;;;
+10387;UGARITIC LETTER ZETA;Lo;0;L;;;;;N;;;;;
+10388;UGARITIC LETTER HOTA;Lo;0;L;;;;;N;;;;;
+10389;UGARITIC LETTER TET;Lo;0;L;;;;;N;;;;;
+1038A;UGARITIC LETTER YOD;Lo;0;L;;;;;N;;;;;
+1038B;UGARITIC LETTER KAF;Lo;0;L;;;;;N;;;;;
+1038C;UGARITIC LETTER SHIN;Lo;0;L;;;;;N;;;;;
+1038D;UGARITIC LETTER LAMDA;Lo;0;L;;;;;N;;;;;
+1038E;UGARITIC LETTER MEM;Lo;0;L;;;;;N;;;;;
+1038F;UGARITIC LETTER DHAL;Lo;0;L;;;;;N;;;;;
+10390;UGARITIC LETTER NUN;Lo;0;L;;;;;N;;;;;
+10391;UGARITIC LETTER ZU;Lo;0;L;;;;;N;;;;;
+10392;UGARITIC LETTER SAMKA;Lo;0;L;;;;;N;;;;;
+10393;UGARITIC LETTER AIN;Lo;0;L;;;;;N;;;;;
+10394;UGARITIC LETTER PU;Lo;0;L;;;;;N;;;;;
+10395;UGARITIC LETTER SADE;Lo;0;L;;;;;N;;;;;
+10396;UGARITIC LETTER QOPA;Lo;0;L;;;;;N;;;;;
+10397;UGARITIC LETTER RASHA;Lo;0;L;;;;;N;;;;;
+10398;UGARITIC LETTER THANNA;Lo;0;L;;;;;N;;;;;
+10399;UGARITIC LETTER GHAIN;Lo;0;L;;;;;N;;;;;
+1039A;UGARITIC LETTER TO;Lo;0;L;;;;;N;;;;;
+1039B;UGARITIC LETTER I;Lo;0;L;;;;;N;;;;;
+1039C;UGARITIC LETTER U;Lo;0;L;;;;;N;;;;;
+1039D;UGARITIC LETTER SSU;Lo;0;L;;;;;N;;;;;
+1039F;UGARITIC WORD DIVIDER;Po;0;L;;;;;N;;;;;
+10400;DESERET CAPITAL LETTER LONG I;Lu;0;L;;;;;N;;;;10428;
+10401;DESERET CAPITAL LETTER LONG E;Lu;0;L;;;;;N;;;;10429;
+10402;DESERET CAPITAL LETTER LONG A;Lu;0;L;;;;;N;;;;1042A;
+10403;DESERET CAPITAL LETTER LONG AH;Lu;0;L;;;;;N;;;;1042B;
+10404;DESERET CAPITAL LETTER LONG O;Lu;0;L;;;;;N;;;;1042C;
+10405;DESERET CAPITAL LETTER LONG OO;Lu;0;L;;;;;N;;;;1042D;
+10406;DESERET CAPITAL LETTER SHORT I;Lu;0;L;;;;;N;;;;1042E;
+10407;DESERET CAPITAL LETTER SHORT E;Lu;0;L;;;;;N;;;;1042F;
+10408;DESERET CAPITAL LETTER SHORT A;Lu;0;L;;;;;N;;;;10430;
+10409;DESERET CAPITAL LETTER SHORT AH;Lu;0;L;;;;;N;;;;10431;
+1040A;DESERET CAPITAL LETTER SHORT O;Lu;0;L;;;;;N;;;;10432;
+1040B;DESERET CAPITAL LETTER SHORT OO;Lu;0;L;;;;;N;;;;10433;
+1040C;DESERET CAPITAL LETTER AY;Lu;0;L;;;;;N;;;;10434;
+1040D;DESERET CAPITAL LETTER OW;Lu;0;L;;;;;N;;;;10435;
+1040E;DESERET CAPITAL LETTER WU;Lu;0;L;;;;;N;;;;10436;
+1040F;DESERET CAPITAL LETTER YEE;Lu;0;L;;;;;N;;;;10437;
+10410;DESERET CAPITAL LETTER H;Lu;0;L;;;;;N;;;;10438;
+10411;DESERET CAPITAL LETTER PEE;Lu;0;L;;;;;N;;;;10439;
+10412;DESERET CAPITAL LETTER BEE;Lu;0;L;;;;;N;;;;1043A;
+10413;DESERET CAPITAL LETTER TEE;Lu;0;L;;;;;N;;;;1043B;
+10414;DESERET CAPITAL LETTER DEE;Lu;0;L;;;;;N;;;;1043C;
+10415;DESERET CAPITAL LETTER CHEE;Lu;0;L;;;;;N;;;;1043D;
+10416;DESERET CAPITAL LETTER JEE;Lu;0;L;;;;;N;;;;1043E;
+10417;DESERET CAPITAL LETTER KAY;Lu;0;L;;;;;N;;;;1043F;
+10418;DESERET CAPITAL LETTER GAY;Lu;0;L;;;;;N;;;;10440;
+10419;DESERET CAPITAL LETTER EF;Lu;0;L;;;;;N;;;;10441;
+1041A;DESERET CAPITAL LETTER VEE;Lu;0;L;;;;;N;;;;10442;
+1041B;DESERET CAPITAL LETTER ETH;Lu;0;L;;;;;N;;;;10443;
+1041C;DESERET CAPITAL LETTER THEE;Lu;0;L;;;;;N;;;;10444;
+1041D;DESERET CAPITAL LETTER ES;Lu;0;L;;;;;N;;;;10445;
+1041E;DESERET CAPITAL LETTER ZEE;Lu;0;L;;;;;N;;;;10446;
+1041F;DESERET CAPITAL LETTER ESH;Lu;0;L;;;;;N;;;;10447;
+10420;DESERET CAPITAL LETTER ZHEE;Lu;0;L;;;;;N;;;;10448;
+10421;DESERET CAPITAL LETTER ER;Lu;0;L;;;;;N;;;;10449;
+10422;DESERET CAPITAL LETTER EL;Lu;0;L;;;;;N;;;;1044A;
+10423;DESERET CAPITAL LETTER EM;Lu;0;L;;;;;N;;;;1044B;
+10424;DESERET CAPITAL LETTER EN;Lu;0;L;;;;;N;;;;1044C;
+10425;DESERET CAPITAL LETTER ENG;Lu;0;L;;;;;N;;;;1044D;
+10426;DESERET CAPITAL LETTER OI;Lu;0;L;;;;;N;;;;1044E;
+10427;DESERET CAPITAL LETTER EW;Lu;0;L;;;;;N;;;;1044F;
+10428;DESERET SMALL LETTER LONG I;Ll;0;L;;;;;N;;;10400;;10400
+10429;DESERET SMALL LETTER LONG E;Ll;0;L;;;;;N;;;10401;;10401
+1042A;DESERET SMALL LETTER LONG A;Ll;0;L;;;;;N;;;10402;;10402
+1042B;DESERET SMALL LETTER LONG AH;Ll;0;L;;;;;N;;;10403;;10403
+1042C;DESERET SMALL LETTER LONG O;Ll;0;L;;;;;N;;;10404;;10404
+1042D;DESERET SMALL LETTER LONG OO;Ll;0;L;;;;;N;;;10405;;10405
+1042E;DESERET SMALL LETTER SHORT I;Ll;0;L;;;;;N;;;10406;;10406
+1042F;DESERET SMALL LETTER SHORT E;Ll;0;L;;;;;N;;;10407;;10407
+10430;DESERET SMALL LETTER SHORT A;Ll;0;L;;;;;N;;;10408;;10408
+10431;DESERET SMALL LETTER SHORT AH;Ll;0;L;;;;;N;;;10409;;10409
+10432;DESERET SMALL LETTER SHORT O;Ll;0;L;;;;;N;;;1040A;;1040A
+10433;DESERET SMALL LETTER SHORT OO;Ll;0;L;;;;;N;;;1040B;;1040B
+10434;DESERET SMALL LETTER AY;Ll;0;L;;;;;N;;;1040C;;1040C
+10435;DESERET SMALL LETTER OW;Ll;0;L;;;;;N;;;1040D;;1040D
+10436;DESERET SMALL LETTER WU;Ll;0;L;;;;;N;;;1040E;;1040E
+10437;DESERET SMALL LETTER YEE;Ll;0;L;;;;;N;;;1040F;;1040F
+10438;DESERET SMALL LETTER H;Ll;0;L;;;;;N;;;10410;;10410
+10439;DESERET SMALL LETTER PEE;Ll;0;L;;;;;N;;;10411;;10411
+1043A;DESERET SMALL LETTER BEE;Ll;0;L;;;;;N;;;10412;;10412
+1043B;DESERET SMALL LETTER TEE;Ll;0;L;;;;;N;;;10413;;10413
+1043C;DESERET SMALL LETTER DEE;Ll;0;L;;;;;N;;;10414;;10414
+1043D;DESERET SMALL LETTER CHEE;Ll;0;L;;;;;N;;;10415;;10415
+1043E;DESERET SMALL LETTER JEE;Ll;0;L;;;;;N;;;10416;;10416
+1043F;DESERET SMALL LETTER KAY;Ll;0;L;;;;;N;;;10417;;10417
+10440;DESERET SMALL LETTER GAY;Ll;0;L;;;;;N;;;10418;;10418
+10441;DESERET SMALL LETTER EF;Ll;0;L;;;;;N;;;10419;;10419
+10442;DESERET SMALL LETTER VEE;Ll;0;L;;;;;N;;;1041A;;1041A
+10443;DESERET SMALL LETTER ETH;Ll;0;L;;;;;N;;;1041B;;1041B
+10444;DESERET SMALL LETTER THEE;Ll;0;L;;;;;N;;;1041C;;1041C
+10445;DESERET SMALL LETTER ES;Ll;0;L;;;;;N;;;1041D;;1041D
+10446;DESERET SMALL LETTER ZEE;Ll;0;L;;;;;N;;;1041E;;1041E
+10447;DESERET SMALL LETTER ESH;Ll;0;L;;;;;N;;;1041F;;1041F
+10448;DESERET SMALL LETTER ZHEE;Ll;0;L;;;;;N;;;10420;;10420
+10449;DESERET SMALL LETTER ER;Ll;0;L;;;;;N;;;10421;;10421
+1044A;DESERET SMALL LETTER EL;Ll;0;L;;;;;N;;;10422;;10422
+1044B;DESERET SMALL LETTER EM;Ll;0;L;;;;;N;;;10423;;10423
+1044C;DESERET SMALL LETTER EN;Ll;0;L;;;;;N;;;10424;;10424
+1044D;DESERET SMALL LETTER ENG;Ll;0;L;;;;;N;;;10425;;10425
+1044E;DESERET SMALL LETTER OI;Ll;0;L;;;;;N;;;10426;;10426
+1044F;DESERET SMALL LETTER EW;Ll;0;L;;;;;N;;;10427;;10427
+10450;SHAVIAN LETTER PEEP;Lo;0;L;;;;;N;;;;;
+10451;SHAVIAN LETTER TOT;Lo;0;L;;;;;N;;;;;
+10452;SHAVIAN LETTER KICK;Lo;0;L;;;;;N;;;;;
+10453;SHAVIAN LETTER FEE;Lo;0;L;;;;;N;;;;;
+10454;SHAVIAN LETTER THIGH;Lo;0;L;;;;;N;;;;;
+10455;SHAVIAN LETTER SO;Lo;0;L;;;;;N;;;;;
+10456;SHAVIAN LETTER SURE;Lo;0;L;;;;;N;;;;;
+10457;SHAVIAN LETTER CHURCH;Lo;0;L;;;;;N;;;;;
+10458;SHAVIAN LETTER YEA;Lo;0;L;;;;;N;;;;;
+10459;SHAVIAN LETTER HUNG;Lo;0;L;;;;;N;;;;;
+1045A;SHAVIAN LETTER BIB;Lo;0;L;;;;;N;;;;;
+1045B;SHAVIAN LETTER DEAD;Lo;0;L;;;;;N;;;;;
+1045C;SHAVIAN LETTER GAG;Lo;0;L;;;;;N;;;;;
+1045D;SHAVIAN LETTER VOW;Lo;0;L;;;;;N;;;;;
+1045E;SHAVIAN LETTER THEY;Lo;0;L;;;;;N;;;;;
+1045F;SHAVIAN LETTER ZOO;Lo;0;L;;;;;N;;;;;
+10460;SHAVIAN LETTER MEASURE;Lo;0;L;;;;;N;;;;;
+10461;SHAVIAN LETTER JUDGE;Lo;0;L;;;;;N;;;;;
+10462;SHAVIAN LETTER WOE;Lo;0;L;;;;;N;;;;;
+10463;SHAVIAN LETTER HA-HA;Lo;0;L;;;;;N;;;;;
+10464;SHAVIAN LETTER LOLL;Lo;0;L;;;;;N;;;;;
+10465;SHAVIAN LETTER MIME;Lo;0;L;;;;;N;;;;;
+10466;SHAVIAN LETTER IF;Lo;0;L;;;;;N;;;;;
+10467;SHAVIAN LETTER EGG;Lo;0;L;;;;;N;;;;;
+10468;SHAVIAN LETTER ASH;Lo;0;L;;;;;N;;;;;
+10469;SHAVIAN LETTER ADO;Lo;0;L;;;;;N;;;;;
+1046A;SHAVIAN LETTER ON;Lo;0;L;;;;;N;;;;;
+1046B;SHAVIAN LETTER WOOL;Lo;0;L;;;;;N;;;;;
+1046C;SHAVIAN LETTER OUT;Lo;0;L;;;;;N;;;;;
+1046D;SHAVIAN LETTER AH;Lo;0;L;;;;;N;;;;;
+1046E;SHAVIAN LETTER ROAR;Lo;0;L;;;;;N;;;;;
+1046F;SHAVIAN LETTER NUN;Lo;0;L;;;;;N;;;;;
+10470;SHAVIAN LETTER EAT;Lo;0;L;;;;;N;;;;;
+10471;SHAVIAN LETTER AGE;Lo;0;L;;;;;N;;;;;
+10472;SHAVIAN LETTER ICE;Lo;0;L;;;;;N;;;;;
+10473;SHAVIAN LETTER UP;Lo;0;L;;;;;N;;;;;
+10474;SHAVIAN LETTER OAK;Lo;0;L;;;;;N;;;;;
+10475;SHAVIAN LETTER OOZE;Lo;0;L;;;;;N;;;;;
+10476;SHAVIAN LETTER OIL;Lo;0;L;;;;;N;;;;;
+10477;SHAVIAN LETTER AWE;Lo;0;L;;;;;N;;;;;
+10478;SHAVIAN LETTER ARE;Lo;0;L;;;;;N;;;;;
+10479;SHAVIAN LETTER OR;Lo;0;L;;;;;N;;;;;
+1047A;SHAVIAN LETTER AIR;Lo;0;L;;;;;N;;;;;
+1047B;SHAVIAN LETTER ERR;Lo;0;L;;;;;N;;;;;
+1047C;SHAVIAN LETTER ARRAY;Lo;0;L;;;;;N;;;;;
+1047D;SHAVIAN LETTER EAR;Lo;0;L;;;;;N;;;;;
+1047E;SHAVIAN LETTER IAN;Lo;0;L;;;;;N;;;;;
+1047F;SHAVIAN LETTER YEW;Lo;0;L;;;;;N;;;;;
+10480;OSMANYA LETTER ALEF;Lo;0;L;;;;;N;;;;;
+10481;OSMANYA LETTER BA;Lo;0;L;;;;;N;;;;;
+10482;OSMANYA LETTER TA;Lo;0;L;;;;;N;;;;;
+10483;OSMANYA LETTER JA;Lo;0;L;;;;;N;;;;;
+10484;OSMANYA LETTER XA;Lo;0;L;;;;;N;;;;;
+10485;OSMANYA LETTER KHA;Lo;0;L;;;;;N;;;;;
+10486;OSMANYA LETTER DEEL;Lo;0;L;;;;;N;;;;;
+10487;OSMANYA LETTER RA;Lo;0;L;;;;;N;;;;;
+10488;OSMANYA LETTER SA;Lo;0;L;;;;;N;;;;;
+10489;OSMANYA LETTER SHIIN;Lo;0;L;;;;;N;;;;;
+1048A;OSMANYA LETTER DHA;Lo;0;L;;;;;N;;;;;
+1048B;OSMANYA LETTER CAYN;Lo;0;L;;;;;N;;;;;
+1048C;OSMANYA LETTER GA;Lo;0;L;;;;;N;;;;;
+1048D;OSMANYA LETTER FA;Lo;0;L;;;;;N;;;;;
+1048E;OSMANYA LETTER QAAF;Lo;0;L;;;;;N;;;;;
+1048F;OSMANYA LETTER KAAF;Lo;0;L;;;;;N;;;;;
+10490;OSMANYA LETTER LAAN;Lo;0;L;;;;;N;;;;;
+10491;OSMANYA LETTER MIIN;Lo;0;L;;;;;N;;;;;
+10492;OSMANYA LETTER NUUN;Lo;0;L;;;;;N;;;;;
+10493;OSMANYA LETTER WAW;Lo;0;L;;;;;N;;;;;
+10494;OSMANYA LETTER HA;Lo;0;L;;;;;N;;;;;
+10495;OSMANYA LETTER YA;Lo;0;L;;;;;N;;;;;
+10496;OSMANYA LETTER A;Lo;0;L;;;;;N;;;;;
+10497;OSMANYA LETTER E;Lo;0;L;;;;;N;;;;;
+10498;OSMANYA LETTER I;Lo;0;L;;;;;N;;;;;
+10499;OSMANYA LETTER O;Lo;0;L;;;;;N;;;;;
+1049A;OSMANYA LETTER U;Lo;0;L;;;;;N;;;;;
+1049B;OSMANYA LETTER AA;Lo;0;L;;;;;N;;;;;
+1049C;OSMANYA LETTER EE;Lo;0;L;;;;;N;;;;;
+1049D;OSMANYA LETTER OO;Lo;0;L;;;;;N;;;;;
+104A0;OSMANYA DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+104A1;OSMANYA DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+104A2;OSMANYA DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+104A3;OSMANYA DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+104A4;OSMANYA DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+104A5;OSMANYA DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+104A6;OSMANYA DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+104A7;OSMANYA DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+104A8;OSMANYA DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+104A9;OSMANYA DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+10800;CYPRIOT SYLLABLE A;Lo;0;R;;;;;N;;;;;
+10801;CYPRIOT SYLLABLE E;Lo;0;R;;;;;N;;;;;
+10802;CYPRIOT SYLLABLE I;Lo;0;R;;;;;N;;;;;
+10803;CYPRIOT SYLLABLE O;Lo;0;R;;;;;N;;;;;
+10804;CYPRIOT SYLLABLE U;Lo;0;R;;;;;N;;;;;
+10805;CYPRIOT SYLLABLE JA;Lo;0;R;;;;;N;;;;;
+10808;CYPRIOT SYLLABLE JO;Lo;0;R;;;;;N;;;;;
+1080A;CYPRIOT SYLLABLE KA;Lo;0;R;;;;;N;;;;;
+1080B;CYPRIOT SYLLABLE KE;Lo;0;R;;;;;N;;;;;
+1080C;CYPRIOT SYLLABLE KI;Lo;0;R;;;;;N;;;;;
+1080D;CYPRIOT SYLLABLE KO;Lo;0;R;;;;;N;;;;;
+1080E;CYPRIOT SYLLABLE KU;Lo;0;R;;;;;N;;;;;
+1080F;CYPRIOT SYLLABLE LA;Lo;0;R;;;;;N;;;;;
+10810;CYPRIOT SYLLABLE LE;Lo;0;R;;;;;N;;;;;
+10811;CYPRIOT SYLLABLE LI;Lo;0;R;;;;;N;;;;;
+10812;CYPRIOT SYLLABLE LO;Lo;0;R;;;;;N;;;;;
+10813;CYPRIOT SYLLABLE LU;Lo;0;R;;;;;N;;;;;
+10814;CYPRIOT SYLLABLE MA;Lo;0;R;;;;;N;;;;;
+10815;CYPRIOT SYLLABLE ME;Lo;0;R;;;;;N;;;;;
+10816;CYPRIOT SYLLABLE MI;Lo;0;R;;;;;N;;;;;
+10817;CYPRIOT SYLLABLE MO;Lo;0;R;;;;;N;;;;;
+10818;CYPRIOT SYLLABLE MU;Lo;0;R;;;;;N;;;;;
+10819;CYPRIOT SYLLABLE NA;Lo;0;R;;;;;N;;;;;
+1081A;CYPRIOT SYLLABLE NE;Lo;0;R;;;;;N;;;;;
+1081B;CYPRIOT SYLLABLE NI;Lo;0;R;;;;;N;;;;;
+1081C;CYPRIOT SYLLABLE NO;Lo;0;R;;;;;N;;;;;
+1081D;CYPRIOT SYLLABLE NU;Lo;0;R;;;;;N;;;;;
+1081E;CYPRIOT SYLLABLE PA;Lo;0;R;;;;;N;;;;;
+1081F;CYPRIOT SYLLABLE PE;Lo;0;R;;;;;N;;;;;
+10820;CYPRIOT SYLLABLE PI;Lo;0;R;;;;;N;;;;;
+10821;CYPRIOT SYLLABLE PO;Lo;0;R;;;;;N;;;;;
+10822;CYPRIOT SYLLABLE PU;Lo;0;R;;;;;N;;;;;
+10823;CYPRIOT SYLLABLE RA;Lo;0;R;;;;;N;;;;;
+10824;CYPRIOT SYLLABLE RE;Lo;0;R;;;;;N;;;;;
+10825;CYPRIOT SYLLABLE RI;Lo;0;R;;;;;N;;;;;
+10826;CYPRIOT SYLLABLE RO;Lo;0;R;;;;;N;;;;;
+10827;CYPRIOT SYLLABLE RU;Lo;0;R;;;;;N;;;;;
+10828;CYPRIOT SYLLABLE SA;Lo;0;R;;;;;N;;;;;
+10829;CYPRIOT SYLLABLE SE;Lo;0;R;;;;;N;;;;;
+1082A;CYPRIOT SYLLABLE SI;Lo;0;R;;;;;N;;;;;
+1082B;CYPRIOT SYLLABLE SO;Lo;0;R;;;;;N;;;;;
+1082C;CYPRIOT SYLLABLE SU;Lo;0;R;;;;;N;;;;;
+1082D;CYPRIOT SYLLABLE TA;Lo;0;R;;;;;N;;;;;
+1082E;CYPRIOT SYLLABLE TE;Lo;0;R;;;;;N;;;;;
+1082F;CYPRIOT SYLLABLE TI;Lo;0;R;;;;;N;;;;;
+10830;CYPRIOT SYLLABLE TO;Lo;0;R;;;;;N;;;;;
+10831;CYPRIOT SYLLABLE TU;Lo;0;R;;;;;N;;;;;
+10832;CYPRIOT SYLLABLE WA;Lo;0;R;;;;;N;;;;;
+10833;CYPRIOT SYLLABLE WE;Lo;0;R;;;;;N;;;;;
+10834;CYPRIOT SYLLABLE WI;Lo;0;R;;;;;N;;;;;
+10835;CYPRIOT SYLLABLE WO;Lo;0;R;;;;;N;;;;;
+10837;CYPRIOT SYLLABLE XA;Lo;0;R;;;;;N;;;;;
+10838;CYPRIOT SYLLABLE XE;Lo;0;R;;;;;N;;;;;
+1083C;CYPRIOT SYLLABLE ZA;Lo;0;R;;;;;N;;;;;
+1083F;CYPRIOT SYLLABLE ZO;Lo;0;R;;;;;N;;;;;
+1D000;BYZANTINE MUSICAL SYMBOL PSILI;So;0;L;;;;;N;;;;;
+1D001;BYZANTINE MUSICAL SYMBOL DASEIA;So;0;L;;;;;N;;;;;
+1D002;BYZANTINE MUSICAL SYMBOL PERISPOMENI;So;0;L;;;;;N;;;;;
+1D003;BYZANTINE MUSICAL SYMBOL OXEIA EKFONITIKON;So;0;L;;;;;N;;;;;
+1D004;BYZANTINE MUSICAL SYMBOL OXEIA DIPLI;So;0;L;;;;;N;;;;;
+1D005;BYZANTINE MUSICAL SYMBOL VAREIA EKFONITIKON;So;0;L;;;;;N;;;;;
+1D006;BYZANTINE MUSICAL SYMBOL VAREIA DIPLI;So;0;L;;;;;N;;;;;
+1D007;BYZANTINE MUSICAL SYMBOL KATHISTI;So;0;L;;;;;N;;;;;
+1D008;BYZANTINE MUSICAL SYMBOL SYRMATIKI;So;0;L;;;;;N;;;;;
+1D009;BYZANTINE MUSICAL SYMBOL PARAKLITIKI;So;0;L;;;;;N;;;;;
+1D00A;BYZANTINE MUSICAL SYMBOL YPOKRISIS;So;0;L;;;;;N;;;;;
+1D00B;BYZANTINE MUSICAL SYMBOL YPOKRISIS DIPLI;So;0;L;;;;;N;;;;;
+1D00C;BYZANTINE MUSICAL SYMBOL KREMASTI;So;0;L;;;;;N;;;;;
+1D00D;BYZANTINE MUSICAL SYMBOL APESO EKFONITIKON;So;0;L;;;;;N;;;;;
+1D00E;BYZANTINE MUSICAL SYMBOL EXO EKFONITIKON;So;0;L;;;;;N;;;;;
+1D00F;BYZANTINE MUSICAL SYMBOL TELEIA;So;0;L;;;;;N;;;;;
+1D010;BYZANTINE MUSICAL SYMBOL KENTIMATA;So;0;L;;;;;N;;;;;
+1D011;BYZANTINE MUSICAL SYMBOL APOSTROFOS;So;0;L;;;;;N;;;;;
+1D012;BYZANTINE MUSICAL SYMBOL APOSTROFOS DIPLI;So;0;L;;;;;N;;;;;
+1D013;BYZANTINE MUSICAL SYMBOL SYNEVMA;So;0;L;;;;;N;;;;;
+1D014;BYZANTINE MUSICAL SYMBOL THITA;So;0;L;;;;;N;;;;;
+1D015;BYZANTINE MUSICAL SYMBOL OLIGON ARCHAION;So;0;L;;;;;N;;;;;
+1D016;BYZANTINE MUSICAL SYMBOL GORGON ARCHAION;So;0;L;;;;;N;;;;;
+1D017;BYZANTINE MUSICAL SYMBOL PSILON;So;0;L;;;;;N;;;;;
+1D018;BYZANTINE MUSICAL SYMBOL CHAMILON;So;0;L;;;;;N;;;;;
+1D019;BYZANTINE MUSICAL SYMBOL VATHY;So;0;L;;;;;N;;;;;
+1D01A;BYZANTINE MUSICAL SYMBOL ISON ARCHAION;So;0;L;;;;;N;;;;;
+1D01B;BYZANTINE MUSICAL SYMBOL KENTIMA ARCHAION;So;0;L;;;;;N;;;;;
+1D01C;BYZANTINE MUSICAL SYMBOL KENTIMATA ARCHAION;So;0;L;;;;;N;;;;;
+1D01D;BYZANTINE MUSICAL SYMBOL SAXIMATA;So;0;L;;;;;N;;;;;
+1D01E;BYZANTINE MUSICAL SYMBOL PARICHON;So;0;L;;;;;N;;;;;
+1D01F;BYZANTINE MUSICAL SYMBOL STAVROS APODEXIA;So;0;L;;;;;N;;;;;
+1D020;BYZANTINE MUSICAL SYMBOL OXEIAI ARCHAION;So;0;L;;;;;N;;;;;
+1D021;BYZANTINE MUSICAL SYMBOL VAREIAI ARCHAION;So;0;L;;;;;N;;;;;
+1D022;BYZANTINE MUSICAL SYMBOL APODERMA ARCHAION;So;0;L;;;;;N;;;;;
+1D023;BYZANTINE MUSICAL SYMBOL APOTHEMA;So;0;L;;;;;N;;;;;
+1D024;BYZANTINE MUSICAL SYMBOL KLASMA;So;0;L;;;;;N;;;;;
+1D025;BYZANTINE MUSICAL SYMBOL REVMA;So;0;L;;;;;N;;;;;
+1D026;BYZANTINE MUSICAL SYMBOL PIASMA ARCHAION;So;0;L;;;;;N;;;;;
+1D027;BYZANTINE MUSICAL SYMBOL TINAGMA;So;0;L;;;;;N;;;;;
+1D028;BYZANTINE MUSICAL SYMBOL ANATRICHISMA;So;0;L;;;;;N;;;;;
+1D029;BYZANTINE MUSICAL SYMBOL SEISMA;So;0;L;;;;;N;;;;;
+1D02A;BYZANTINE MUSICAL SYMBOL SYNAGMA ARCHAION;So;0;L;;;;;N;;;;;
+1D02B;BYZANTINE MUSICAL SYMBOL SYNAGMA META STAVROU;So;0;L;;;;;N;;;;;
+1D02C;BYZANTINE MUSICAL SYMBOL OYRANISMA ARCHAION;So;0;L;;;;;N;;;;;
+1D02D;BYZANTINE MUSICAL SYMBOL THEMA;So;0;L;;;;;N;;;;;
+1D02E;BYZANTINE MUSICAL SYMBOL LEMOI;So;0;L;;;;;N;;;;;
+1D02F;BYZANTINE MUSICAL SYMBOL DYO;So;0;L;;;;;N;;;;;
+1D030;BYZANTINE MUSICAL SYMBOL TRIA;So;0;L;;;;;N;;;;;
+1D031;BYZANTINE MUSICAL SYMBOL TESSERA;So;0;L;;;;;N;;;;;
+1D032;BYZANTINE MUSICAL SYMBOL KRATIMATA;So;0;L;;;;;N;;;;;
+1D033;BYZANTINE MUSICAL SYMBOL APESO EXO NEO;So;0;L;;;;;N;;;;;
+1D034;BYZANTINE MUSICAL SYMBOL FTHORA ARCHAION;So;0;L;;;;;N;;;;;
+1D035;BYZANTINE MUSICAL SYMBOL IMIFTHORA;So;0;L;;;;;N;;;;;
+1D036;BYZANTINE MUSICAL SYMBOL TROMIKON ARCHAION;So;0;L;;;;;N;;;;;
+1D037;BYZANTINE MUSICAL SYMBOL KATAVA TROMIKON;So;0;L;;;;;N;;;;;
+1D038;BYZANTINE MUSICAL SYMBOL PELASTON;So;0;L;;;;;N;;;;;
+1D039;BYZANTINE MUSICAL SYMBOL PSIFISTON;So;0;L;;;;;N;;;;;
+1D03A;BYZANTINE MUSICAL SYMBOL KONTEVMA;So;0;L;;;;;N;;;;;
+1D03B;BYZANTINE MUSICAL SYMBOL CHOREVMA ARCHAION;So;0;L;;;;;N;;;;;
+1D03C;BYZANTINE MUSICAL SYMBOL RAPISMA;So;0;L;;;;;N;;;;;
+1D03D;BYZANTINE MUSICAL SYMBOL PARAKALESMA ARCHAION;So;0;L;;;;;N;;;;;
+1D03E;BYZANTINE MUSICAL SYMBOL PARAKLITIKI ARCHAION;So;0;L;;;;;N;;;;;
+1D03F;BYZANTINE MUSICAL SYMBOL ICHADIN;So;0;L;;;;;N;;;;;
+1D040;BYZANTINE MUSICAL SYMBOL NANA;So;0;L;;;;;N;;;;;
+1D041;BYZANTINE MUSICAL SYMBOL PETASMA;So;0;L;;;;;N;;;;;
+1D042;BYZANTINE MUSICAL SYMBOL KONTEVMA ALLO;So;0;L;;;;;N;;;;;
+1D043;BYZANTINE MUSICAL SYMBOL TROMIKON ALLO;So;0;L;;;;;N;;;;;
+1D044;BYZANTINE MUSICAL SYMBOL STRAGGISMATA;So;0;L;;;;;N;;;;;
+1D045;BYZANTINE MUSICAL SYMBOL GRONTHISMATA;So;0;L;;;;;N;;;;;
+1D046;BYZANTINE MUSICAL SYMBOL ISON NEO;So;0;L;;;;;N;;;;;
+1D047;BYZANTINE MUSICAL SYMBOL OLIGON NEO;So;0;L;;;;;N;;;;;
+1D048;BYZANTINE MUSICAL SYMBOL OXEIA NEO;So;0;L;;;;;N;;;;;
+1D049;BYZANTINE MUSICAL SYMBOL PETASTI;So;0;L;;;;;N;;;;;
+1D04A;BYZANTINE MUSICAL SYMBOL KOUFISMA;So;0;L;;;;;N;;;;;
+1D04B;BYZANTINE MUSICAL SYMBOL PETASTOKOUFISMA;So;0;L;;;;;N;;;;;
+1D04C;BYZANTINE MUSICAL SYMBOL KRATIMOKOUFISMA;So;0;L;;;;;N;;;;;
+1D04D;BYZANTINE MUSICAL SYMBOL PELASTON NEO;So;0;L;;;;;N;;;;;
+1D04E;BYZANTINE MUSICAL SYMBOL KENTIMATA NEO ANO;So;0;L;;;;;N;;;;;
+1D04F;BYZANTINE MUSICAL SYMBOL KENTIMA NEO ANO;So;0;L;;;;;N;;;;;
+1D050;BYZANTINE MUSICAL SYMBOL YPSILI;So;0;L;;;;;N;;;;;
+1D051;BYZANTINE MUSICAL SYMBOL APOSTROFOS NEO;So;0;L;;;;;N;;;;;
+1D052;BYZANTINE MUSICAL SYMBOL APOSTROFOI SYNDESMOS NEO;So;0;L;;;;;N;;;;;
+1D053;BYZANTINE MUSICAL SYMBOL YPORROI;So;0;L;;;;;N;;;;;
+1D054;BYZANTINE MUSICAL SYMBOL KRATIMOYPORROON;So;0;L;;;;;N;;;;;
+1D055;BYZANTINE MUSICAL SYMBOL ELAFRON;So;0;L;;;;;N;;;;;
+1D056;BYZANTINE MUSICAL SYMBOL CHAMILI;So;0;L;;;;;N;;;;;
+1D057;BYZANTINE MUSICAL SYMBOL MIKRON ISON;So;0;L;;;;;N;;;;;
+1D058;BYZANTINE MUSICAL SYMBOL VAREIA NEO;So;0;L;;;;;N;;;;;
+1D059;BYZANTINE MUSICAL SYMBOL PIASMA NEO;So;0;L;;;;;N;;;;;
+1D05A;BYZANTINE MUSICAL SYMBOL PSIFISTON NEO;So;0;L;;;;;N;;;;;
+1D05B;BYZANTINE MUSICAL SYMBOL OMALON;So;0;L;;;;;N;;;;;
+1D05C;BYZANTINE MUSICAL SYMBOL ANTIKENOMA;So;0;L;;;;;N;;;;;
+1D05D;BYZANTINE MUSICAL SYMBOL LYGISMA;So;0;L;;;;;N;;;;;
+1D05E;BYZANTINE MUSICAL SYMBOL PARAKLITIKI NEO;So;0;L;;;;;N;;;;;
+1D05F;BYZANTINE MUSICAL SYMBOL PARAKALESMA NEO;So;0;L;;;;;N;;;;;
+1D060;BYZANTINE MUSICAL SYMBOL ETERON PARAKALESMA;So;0;L;;;;;N;;;;;
+1D061;BYZANTINE MUSICAL SYMBOL KYLISMA;So;0;L;;;;;N;;;;;
+1D062;BYZANTINE MUSICAL SYMBOL ANTIKENOKYLISMA;So;0;L;;;;;N;;;;;
+1D063;BYZANTINE MUSICAL SYMBOL TROMIKON NEO;So;0;L;;;;;N;;;;;
+1D064;BYZANTINE MUSICAL SYMBOL EKSTREPTON;So;0;L;;;;;N;;;;;
+1D065;BYZANTINE MUSICAL SYMBOL SYNAGMA NEO;So;0;L;;;;;N;;;;;
+1D066;BYZANTINE MUSICAL SYMBOL SYRMA;So;0;L;;;;;N;;;;;
+1D067;BYZANTINE MUSICAL SYMBOL CHOREVMA NEO;So;0;L;;;;;N;;;;;
+1D068;BYZANTINE MUSICAL SYMBOL EPEGERMA;So;0;L;;;;;N;;;;;
+1D069;BYZANTINE MUSICAL SYMBOL SEISMA NEO;So;0;L;;;;;N;;;;;
+1D06A;BYZANTINE MUSICAL SYMBOL XIRON KLASMA;So;0;L;;;;;N;;;;;
+1D06B;BYZANTINE MUSICAL SYMBOL TROMIKOPSIFISTON;So;0;L;;;;;N;;;;;
+1D06C;BYZANTINE MUSICAL SYMBOL PSIFISTOLYGISMA;So;0;L;;;;;N;;;;;
+1D06D;BYZANTINE MUSICAL SYMBOL TROMIKOLYGISMA;So;0;L;;;;;N;;;;;
+1D06E;BYZANTINE MUSICAL SYMBOL TROMIKOPARAKALESMA;So;0;L;;;;;N;;;;;
+1D06F;BYZANTINE MUSICAL SYMBOL PSIFISTOPARAKALESMA;So;0;L;;;;;N;;;;;
+1D070;BYZANTINE MUSICAL SYMBOL TROMIKOSYNAGMA;So;0;L;;;;;N;;;;;
+1D071;BYZANTINE MUSICAL SYMBOL PSIFISTOSYNAGMA;So;0;L;;;;;N;;;;;
+1D072;BYZANTINE MUSICAL SYMBOL GORGOSYNTHETON;So;0;L;;;;;N;;;;;
+1D073;BYZANTINE MUSICAL SYMBOL ARGOSYNTHETON;So;0;L;;;;;N;;;;;
+1D074;BYZANTINE MUSICAL SYMBOL ETERON ARGOSYNTHETON;So;0;L;;;;;N;;;;;
+1D075;BYZANTINE MUSICAL SYMBOL OYRANISMA NEO;So;0;L;;;;;N;;;;;
+1D076;BYZANTINE MUSICAL SYMBOL THEMATISMOS ESO;So;0;L;;;;;N;;;;;
+1D077;BYZANTINE MUSICAL SYMBOL THEMATISMOS EXO;So;0;L;;;;;N;;;;;
+1D078;BYZANTINE MUSICAL SYMBOL THEMA APLOUN;So;0;L;;;;;N;;;;;
+1D079;BYZANTINE MUSICAL SYMBOL THES KAI APOTHES;So;0;L;;;;;N;;;;;
+1D07A;BYZANTINE MUSICAL SYMBOL KATAVASMA;So;0;L;;;;;N;;;;;
+1D07B;BYZANTINE MUSICAL SYMBOL ENDOFONON;So;0;L;;;;;N;;;;;
+1D07C;BYZANTINE MUSICAL SYMBOL YFEN KATO;So;0;L;;;;;N;;;;;
+1D07D;BYZANTINE MUSICAL SYMBOL YFEN ANO;So;0;L;;;;;N;;;;;
+1D07E;BYZANTINE MUSICAL SYMBOL STAVROS;So;0;L;;;;;N;;;;;
+1D07F;BYZANTINE MUSICAL SYMBOL KLASMA ANO;So;0;L;;;;;N;;;;;
+1D080;BYZANTINE MUSICAL SYMBOL DIPLI ARCHAION;So;0;L;;;;;N;;;;;
+1D081;BYZANTINE MUSICAL SYMBOL KRATIMA ARCHAION;So;0;L;;;;;N;;;;;
+1D082;BYZANTINE MUSICAL SYMBOL KRATIMA ALLO;So;0;L;;;;;N;;;;;
+1D083;BYZANTINE MUSICAL SYMBOL KRATIMA NEO;So;0;L;;;;;N;;;;;
+1D084;BYZANTINE MUSICAL SYMBOL APODERMA NEO;So;0;L;;;;;N;;;;;
+1D085;BYZANTINE MUSICAL SYMBOL APLI;So;0;L;;;;;N;;;;;
+1D086;BYZANTINE MUSICAL SYMBOL DIPLI;So;0;L;;;;;N;;;;;
+1D087;BYZANTINE MUSICAL SYMBOL TRIPLI;So;0;L;;;;;N;;;;;
+1D088;BYZANTINE MUSICAL SYMBOL TETRAPLI;So;0;L;;;;;N;;;;;
+1D089;BYZANTINE MUSICAL SYMBOL KORONIS;So;0;L;;;;;N;;;;;
+1D08A;BYZANTINE MUSICAL SYMBOL LEIMMA ENOS CHRONOU;So;0;L;;;;;N;;;;;
+1D08B;BYZANTINE MUSICAL SYMBOL LEIMMA DYO CHRONON;So;0;L;;;;;N;;;;;
+1D08C;BYZANTINE MUSICAL SYMBOL LEIMMA TRION CHRONON;So;0;L;;;;;N;;;;;
+1D08D;BYZANTINE MUSICAL SYMBOL LEIMMA TESSARON CHRONON;So;0;L;;;;;N;;;;;
+1D08E;BYZANTINE MUSICAL SYMBOL LEIMMA IMISEOS CHRONOU;So;0;L;;;;;N;;;;;
+1D08F;BYZANTINE MUSICAL SYMBOL GORGON NEO ANO;So;0;L;;;;;N;;;;;
+1D090;BYZANTINE MUSICAL SYMBOL GORGON PARESTIGMENON ARISTERA;So;0;L;;;;;N;;;;;
+1D091;BYZANTINE MUSICAL SYMBOL GORGON PARESTIGMENON DEXIA;So;0;L;;;;;N;;;;;
+1D092;BYZANTINE MUSICAL SYMBOL DIGORGON;So;0;L;;;;;N;;;;;
+1D093;BYZANTINE MUSICAL SYMBOL DIGORGON PARESTIGMENON ARISTERA KATO;So;0;L;;;;;N;;;;;
+1D094;BYZANTINE MUSICAL SYMBOL DIGORGON PARESTIGMENON ARISTERA ANO;So;0;L;;;;;N;;;;;
+1D095;BYZANTINE MUSICAL SYMBOL DIGORGON PARESTIGMENON DEXIA;So;0;L;;;;;N;;;;;
+1D096;BYZANTINE MUSICAL SYMBOL TRIGORGON;So;0;L;;;;;N;;;;;
+1D097;BYZANTINE MUSICAL SYMBOL ARGON;So;0;L;;;;;N;;;;;
+1D098;BYZANTINE MUSICAL SYMBOL IMIDIARGON;So;0;L;;;;;N;;;;;
+1D099;BYZANTINE MUSICAL SYMBOL DIARGON;So;0;L;;;;;N;;;;;
+1D09A;BYZANTINE MUSICAL SYMBOL AGOGI POLI ARGI;So;0;L;;;;;N;;;;;
+1D09B;BYZANTINE MUSICAL SYMBOL AGOGI ARGOTERI;So;0;L;;;;;N;;;;;
+1D09C;BYZANTINE MUSICAL SYMBOL AGOGI ARGI;So;0;L;;;;;N;;;;;
+1D09D;BYZANTINE MUSICAL SYMBOL AGOGI METRIA;So;0;L;;;;;N;;;;;
+1D09E;BYZANTINE MUSICAL SYMBOL AGOGI MESI;So;0;L;;;;;N;;;;;
+1D09F;BYZANTINE MUSICAL SYMBOL AGOGI GORGI;So;0;L;;;;;N;;;;;
+1D0A0;BYZANTINE MUSICAL SYMBOL AGOGI GORGOTERI;So;0;L;;;;;N;;;;;
+1D0A1;BYZANTINE MUSICAL SYMBOL AGOGI POLI GORGI;So;0;L;;;;;N;;;;;
+1D0A2;BYZANTINE MUSICAL SYMBOL MARTYRIA PROTOS ICHOS;So;0;L;;;;;N;;;;;
+1D0A3;BYZANTINE MUSICAL SYMBOL MARTYRIA ALLI PROTOS ICHOS;So;0;L;;;;;N;;;;;
+1D0A4;BYZANTINE MUSICAL SYMBOL MARTYRIA DEYTEROS ICHOS;So;0;L;;;;;N;;;;;
+1D0A5;BYZANTINE MUSICAL SYMBOL MARTYRIA ALLI DEYTEROS ICHOS;So;0;L;;;;;N;;;;;
+1D0A6;BYZANTINE MUSICAL SYMBOL MARTYRIA TRITOS ICHOS;So;0;L;;;;;N;;;;;
+1D0A7;BYZANTINE MUSICAL SYMBOL MARTYRIA TRIFONIAS;So;0;L;;;;;N;;;;;
+1D0A8;BYZANTINE MUSICAL SYMBOL MARTYRIA TETARTOS ICHOS;So;0;L;;;;;N;;;;;
+1D0A9;BYZANTINE MUSICAL SYMBOL MARTYRIA TETARTOS LEGETOS ICHOS;So;0;L;;;;;N;;;;;
+1D0AA;BYZANTINE MUSICAL SYMBOL MARTYRIA LEGETOS ICHOS;So;0;L;;;;;N;;;;;
+1D0AB;BYZANTINE MUSICAL SYMBOL MARTYRIA PLAGIOS ICHOS;So;0;L;;;;;N;;;;;
+1D0AC;BYZANTINE MUSICAL SYMBOL ISAKIA TELOUS ICHIMATOS;So;0;L;;;;;N;;;;;
+1D0AD;BYZANTINE MUSICAL SYMBOL APOSTROFOI TELOUS ICHIMATOS;So;0;L;;;;;N;;;;;
+1D0AE;BYZANTINE MUSICAL SYMBOL FANEROSIS TETRAFONIAS;So;0;L;;;;;N;;;;;
+1D0AF;BYZANTINE MUSICAL SYMBOL FANEROSIS MONOFONIAS;So;0;L;;;;;N;;;;;
+1D0B0;BYZANTINE MUSICAL SYMBOL FANEROSIS DIFONIAS;So;0;L;;;;;N;;;;;
+1D0B1;BYZANTINE MUSICAL SYMBOL MARTYRIA VARYS ICHOS;So;0;L;;;;;N;;;;;
+1D0B2;BYZANTINE MUSICAL SYMBOL MARTYRIA PROTOVARYS ICHOS;So;0;L;;;;;N;;;;;
+1D0B3;BYZANTINE MUSICAL SYMBOL MARTYRIA PLAGIOS TETARTOS ICHOS;So;0;L;;;;;N;;;;;
+1D0B4;BYZANTINE MUSICAL SYMBOL GORTHMIKON N APLOUN;So;0;L;;;;;N;;;;;
+1D0B5;BYZANTINE MUSICAL SYMBOL GORTHMIKON N DIPLOUN;So;0;L;;;;;N;;;;;
+1D0B6;BYZANTINE MUSICAL SYMBOL ENARXIS KAI FTHORA VOU;So;0;L;;;;;N;;;;;
+1D0B7;BYZANTINE MUSICAL SYMBOL IMIFONON;So;0;L;;;;;N;;;;;
+1D0B8;BYZANTINE MUSICAL SYMBOL IMIFTHORON;So;0;L;;;;;N;;;;;
+1D0B9;BYZANTINE MUSICAL SYMBOL FTHORA ARCHAION DEYTEROU ICHOU;So;0;L;;;;;N;;;;;
+1D0BA;BYZANTINE MUSICAL SYMBOL FTHORA DIATONIKI PA;So;0;L;;;;;N;;;;;
+1D0BB;BYZANTINE MUSICAL SYMBOL FTHORA DIATONIKI NANA;So;0;L;;;;;N;;;;;
+1D0BC;BYZANTINE MUSICAL SYMBOL FTHORA NAOS ICHOS;So;0;L;;;;;N;;;;;
+1D0BD;BYZANTINE MUSICAL SYMBOL FTHORA DIATONIKI DI;So;0;L;;;;;N;;;;;
+1D0BE;BYZANTINE MUSICAL SYMBOL FTHORA SKLIRON DIATONON DI;So;0;L;;;;;N;;;;;
+1D0BF;BYZANTINE MUSICAL SYMBOL FTHORA DIATONIKI KE;So;0;L;;;;;N;;;;;
+1D0C0;BYZANTINE MUSICAL SYMBOL FTHORA DIATONIKI ZO;So;0;L;;;;;N;;;;;
+1D0C1;BYZANTINE MUSICAL SYMBOL FTHORA DIATONIKI NI KATO;So;0;L;;;;;N;;;;;
+1D0C2;BYZANTINE MUSICAL SYMBOL FTHORA DIATONIKI NI ANO;So;0;L;;;;;N;;;;;
+1D0C3;BYZANTINE MUSICAL SYMBOL FTHORA MALAKON CHROMA DIFONIAS;So;0;L;;;;;N;;;;;
+1D0C4;BYZANTINE MUSICAL SYMBOL FTHORA MALAKON CHROMA MONOFONIAS;So;0;L;;;;;N;;;;;
+1D0C5;BYZANTINE MUSICAL SYMBOL FHTORA SKLIRON CHROMA VASIS;So;0;L;;;;;N;;;;;
+1D0C6;BYZANTINE MUSICAL SYMBOL FTHORA SKLIRON CHROMA SYNAFI;So;0;L;;;;;N;;;;;
+1D0C7;BYZANTINE MUSICAL SYMBOL FTHORA NENANO;So;0;L;;;;;N;;;;;
+1D0C8;BYZANTINE MUSICAL SYMBOL CHROA ZYGOS;So;0;L;;;;;N;;;;;
+1D0C9;BYZANTINE MUSICAL SYMBOL CHROA KLITON;So;0;L;;;;;N;;;;;
+1D0CA;BYZANTINE MUSICAL SYMBOL CHROA SPATHI;So;0;L;;;;;N;;;;;
+1D0CB;BYZANTINE MUSICAL SYMBOL FTHORA I YFESIS TETARTIMORION;So;0;L;;;;;N;;;;;
+1D0CC;BYZANTINE MUSICAL SYMBOL FTHORA ENARMONIOS ANTIFONIA;So;0;L;;;;;N;;;;;
+1D0CD;BYZANTINE MUSICAL SYMBOL YFESIS TRITIMORION;So;0;L;;;;;N;;;;;
+1D0CE;BYZANTINE MUSICAL SYMBOL DIESIS TRITIMORION;So;0;L;;;;;N;;;;;
+1D0CF;BYZANTINE MUSICAL SYMBOL DIESIS TETARTIMORION;So;0;L;;;;;N;;;;;
+1D0D0;BYZANTINE MUSICAL SYMBOL DIESIS APLI DYO DODEKATA;So;0;L;;;;;N;;;;;
+1D0D1;BYZANTINE MUSICAL SYMBOL DIESIS MONOGRAMMOS TESSERA DODEKATA;So;0;L;;;;;N;;;;;
+1D0D2;BYZANTINE MUSICAL SYMBOL DIESIS DIGRAMMOS EX DODEKATA;So;0;L;;;;;N;;;;;
+1D0D3;BYZANTINE MUSICAL SYMBOL DIESIS TRIGRAMMOS OKTO DODEKATA;So;0;L;;;;;N;;;;;
+1D0D4;BYZANTINE MUSICAL SYMBOL YFESIS APLI DYO DODEKATA;So;0;L;;;;;N;;;;;
+1D0D5;BYZANTINE MUSICAL SYMBOL YFESIS MONOGRAMMOS TESSERA DODEKATA;So;0;L;;;;;N;;;;;
+1D0D6;BYZANTINE MUSICAL SYMBOL YFESIS DIGRAMMOS EX DODEKATA;So;0;L;;;;;N;;;;;
+1D0D7;BYZANTINE MUSICAL SYMBOL YFESIS TRIGRAMMOS OKTO DODEKATA;So;0;L;;;;;N;;;;;
+1D0D8;BYZANTINE MUSICAL SYMBOL GENIKI DIESIS;So;0;L;;;;;N;;;;;
+1D0D9;BYZANTINE MUSICAL SYMBOL GENIKI YFESIS;So;0;L;;;;;N;;;;;
+1D0DA;BYZANTINE MUSICAL SYMBOL DIASTOLI APLI MIKRI;So;0;L;;;;;N;;;;;
+1D0DB;BYZANTINE MUSICAL SYMBOL DIASTOLI APLI MEGALI;So;0;L;;;;;N;;;;;
+1D0DC;BYZANTINE MUSICAL SYMBOL DIASTOLI DIPLI;So;0;L;;;;;N;;;;;
+1D0DD;BYZANTINE MUSICAL SYMBOL DIASTOLI THESEOS;So;0;L;;;;;N;;;;;
+1D0DE;BYZANTINE MUSICAL SYMBOL SIMANSIS THESEOS;So;0;L;;;;;N;;;;;
+1D0DF;BYZANTINE MUSICAL SYMBOL SIMANSIS THESEOS DISIMOU;So;0;L;;;;;N;;;;;
+1D0E0;BYZANTINE MUSICAL SYMBOL SIMANSIS THESEOS TRISIMOU;So;0;L;;;;;N;;;;;
+1D0E1;BYZANTINE MUSICAL SYMBOL SIMANSIS THESEOS TETRASIMOU;So;0;L;;;;;N;;;;;
+1D0E2;BYZANTINE MUSICAL SYMBOL SIMANSIS ARSEOS;So;0;L;;;;;N;;;;;
+1D0E3;BYZANTINE MUSICAL SYMBOL SIMANSIS ARSEOS DISIMOU;So;0;L;;;;;N;;;;;
+1D0E4;BYZANTINE MUSICAL SYMBOL SIMANSIS ARSEOS TRISIMOU;So;0;L;;;;;N;;;;;
+1D0E5;BYZANTINE MUSICAL SYMBOL SIMANSIS ARSEOS TETRASIMOU;So;0;L;;;;;N;;;;;
+1D0E6;BYZANTINE MUSICAL SYMBOL DIGRAMMA GG;So;0;L;;;;;N;;;;;
+1D0E7;BYZANTINE MUSICAL SYMBOL DIFTOGGOS OU;So;0;L;;;;;N;;;;;
+1D0E8;BYZANTINE MUSICAL SYMBOL STIGMA;So;0;L;;;;;N;;;;;
+1D0E9;BYZANTINE MUSICAL SYMBOL ARKTIKO PA;So;0;L;;;;;N;;;;;
+1D0EA;BYZANTINE MUSICAL SYMBOL ARKTIKO VOU;So;0;L;;;;;N;;;;;
+1D0EB;BYZANTINE MUSICAL SYMBOL ARKTIKO GA;So;0;L;;;;;N;;;;;
+1D0EC;BYZANTINE MUSICAL SYMBOL ARKTIKO DI;So;0;L;;;;;N;;;;;
+1D0ED;BYZANTINE MUSICAL SYMBOL ARKTIKO KE;So;0;L;;;;;N;;;;;
+1D0EE;BYZANTINE MUSICAL SYMBOL ARKTIKO ZO;So;0;L;;;;;N;;;;;
+1D0EF;BYZANTINE MUSICAL SYMBOL ARKTIKO NI;So;0;L;;;;;N;;;;;
+1D0F0;BYZANTINE MUSICAL SYMBOL KENTIMATA NEO MESO;So;0;L;;;;;N;;;;;
+1D0F1;BYZANTINE MUSICAL SYMBOL KENTIMA NEO MESO;So;0;L;;;;;N;;;;;
+1D0F2;BYZANTINE MUSICAL SYMBOL KENTIMATA NEO KATO;So;0;L;;;;;N;;;;;
+1D0F3;BYZANTINE MUSICAL SYMBOL KENTIMA NEO KATO;So;0;L;;;;;N;;;;;
+1D0F4;BYZANTINE MUSICAL SYMBOL KLASMA KATO;So;0;L;;;;;N;;;;;
+1D0F5;BYZANTINE MUSICAL SYMBOL GORGON NEO KATO;So;0;L;;;;;N;;;;;
+1D100;MUSICAL SYMBOL SINGLE BARLINE;So;0;L;;;;;N;;;;;
+1D101;MUSICAL SYMBOL DOUBLE BARLINE;So;0;L;;;;;N;;;;;
+1D102;MUSICAL SYMBOL FINAL BARLINE;So;0;L;;;;;N;;;;;
+1D103;MUSICAL SYMBOL REVERSE FINAL BARLINE;So;0;L;;;;;N;;;;;
+1D104;MUSICAL SYMBOL DASHED BARLINE;So;0;L;;;;;N;;;;;
+1D105;MUSICAL SYMBOL SHORT BARLINE;So;0;L;;;;;N;;;;;
+1D106;MUSICAL SYMBOL LEFT REPEAT SIGN;So;0;L;;;;;N;;;;;
+1D107;MUSICAL SYMBOL RIGHT REPEAT SIGN;So;0;L;;;;;N;;;;;
+1D108;MUSICAL SYMBOL REPEAT DOTS;So;0;L;;;;;N;;;;;
+1D109;MUSICAL SYMBOL DAL SEGNO;So;0;L;;;;;N;;;;;
+1D10A;MUSICAL SYMBOL DA CAPO;So;0;L;;;;;N;;;;;
+1D10B;MUSICAL SYMBOL SEGNO;So;0;L;;;;;N;;;;;
+1D10C;MUSICAL SYMBOL CODA;So;0;L;;;;;N;;;;;
+1D10D;MUSICAL SYMBOL REPEATED FIGURE-1;So;0;L;;;;;N;;;;;
+1D10E;MUSICAL SYMBOL REPEATED FIGURE-2;So;0;L;;;;;N;;;;;
+1D10F;MUSICAL SYMBOL REPEATED FIGURE-3;So;0;L;;;;;N;;;;;
+1D110;MUSICAL SYMBOL FERMATA;So;0;L;;;;;N;;;;;
+1D111;MUSICAL SYMBOL FERMATA BELOW;So;0;L;;;;;N;;;;;
+1D112;MUSICAL SYMBOL BREATH MARK;So;0;L;;;;;N;;;;;
+1D113;MUSICAL SYMBOL CAESURA;So;0;L;;;;;N;;;;;
+1D114;MUSICAL SYMBOL BRACE;So;0;L;;;;;N;;;;;
+1D115;MUSICAL SYMBOL BRACKET;So;0;L;;;;;N;;;;;
+1D116;MUSICAL SYMBOL ONE-LINE STAFF;So;0;L;;;;;N;;;;;
+1D117;MUSICAL SYMBOL TWO-LINE STAFF;So;0;L;;;;;N;;;;;
+1D118;MUSICAL SYMBOL THREE-LINE STAFF;So;0;L;;;;;N;;;;;
+1D119;MUSICAL SYMBOL FOUR-LINE STAFF;So;0;L;;;;;N;;;;;
+1D11A;MUSICAL SYMBOL FIVE-LINE STAFF;So;0;L;;;;;N;;;;;
+1D11B;MUSICAL SYMBOL SIX-LINE STAFF;So;0;L;;;;;N;;;;;
+1D11C;MUSICAL SYMBOL SIX-STRING FRETBOARD;So;0;L;;;;;N;;;;;
+1D11D;MUSICAL SYMBOL FOUR-STRING FRETBOARD;So;0;L;;;;;N;;;;;
+1D11E;MUSICAL SYMBOL G CLEF;So;0;L;;;;;N;;;;;
+1D11F;MUSICAL SYMBOL G CLEF OTTAVA ALTA;So;0;L;;;;;N;;;;;
+1D120;MUSICAL SYMBOL G CLEF OTTAVA BASSA;So;0;L;;;;;N;;;;;
+1D121;MUSICAL SYMBOL C CLEF;So;0;L;;;;;N;;;;;
+1D122;MUSICAL SYMBOL F CLEF;So;0;L;;;;;N;;;;;
+1D123;MUSICAL SYMBOL F CLEF OTTAVA ALTA;So;0;L;;;;;N;;;;;
+1D124;MUSICAL SYMBOL F CLEF OTTAVA BASSA;So;0;L;;;;;N;;;;;
+1D125;MUSICAL SYMBOL DRUM CLEF-1;So;0;L;;;;;N;;;;;
+1D126;MUSICAL SYMBOL DRUM CLEF-2;So;0;L;;;;;N;;;;;
+1D12A;MUSICAL SYMBOL DOUBLE SHARP;So;0;L;;;;;N;;;;;
+1D12B;MUSICAL SYMBOL DOUBLE FLAT;So;0;L;;;;;N;;;;;
+1D12C;MUSICAL SYMBOL FLAT UP;So;0;L;;;;;N;;;;;
+1D12D;MUSICAL SYMBOL FLAT DOWN;So;0;L;;;;;N;;;;;
+1D12E;MUSICAL SYMBOL NATURAL UP;So;0;L;;;;;N;;;;;
+1D12F;MUSICAL SYMBOL NATURAL DOWN;So;0;L;;;;;N;;;;;
+1D130;MUSICAL SYMBOL SHARP UP;So;0;L;;;;;N;;;;;
+1D131;MUSICAL SYMBOL SHARP DOWN;So;0;L;;;;;N;;;;;
+1D132;MUSICAL SYMBOL QUARTER TONE SHARP;So;0;L;;;;;N;;;;;
+1D133;MUSICAL SYMBOL QUARTER TONE FLAT;So;0;L;;;;;N;;;;;
+1D134;MUSICAL SYMBOL COMMON TIME;So;0;L;;;;;N;;;;;
+1D135;MUSICAL SYMBOL CUT TIME;So;0;L;;;;;N;;;;;
+1D136;MUSICAL SYMBOL OTTAVA ALTA;So;0;L;;;;;N;;;;;
+1D137;MUSICAL SYMBOL OTTAVA BASSA;So;0;L;;;;;N;;;;;
+1D138;MUSICAL SYMBOL QUINDICESIMA ALTA;So;0;L;;;;;N;;;;;
+1D139;MUSICAL SYMBOL QUINDICESIMA BASSA;So;0;L;;;;;N;;;;;
+1D13A;MUSICAL SYMBOL MULTI REST;So;0;L;;;;;N;;;;;
+1D13B;MUSICAL SYMBOL WHOLE REST;So;0;L;;;;;N;;;;;
+1D13C;MUSICAL SYMBOL HALF REST;So;0;L;;;;;N;;;;;
+1D13D;MUSICAL SYMBOL QUARTER REST;So;0;L;;;;;N;;;;;
+1D13E;MUSICAL SYMBOL EIGHTH REST;So;0;L;;;;;N;;;;;
+1D13F;MUSICAL SYMBOL SIXTEENTH REST;So;0;L;;;;;N;;;;;
+1D140;MUSICAL SYMBOL THIRTY-SECOND REST;So;0;L;;;;;N;;;;;
+1D141;MUSICAL SYMBOL SIXTY-FOURTH REST;So;0;L;;;;;N;;;;;
+1D142;MUSICAL SYMBOL ONE HUNDRED TWENTY-EIGHTH REST;So;0;L;;;;;N;;;;;
+1D143;MUSICAL SYMBOL X NOTEHEAD;So;0;L;;;;;N;;;;;
+1D144;MUSICAL SYMBOL PLUS NOTEHEAD;So;0;L;;;;;N;;;;;
+1D145;MUSICAL SYMBOL CIRCLE X NOTEHEAD;So;0;L;;;;;N;;;;;
+1D146;MUSICAL SYMBOL SQUARE NOTEHEAD WHITE;So;0;L;;;;;N;;;;;
+1D147;MUSICAL SYMBOL SQUARE NOTEHEAD BLACK;So;0;L;;;;;N;;;;;
+1D148;MUSICAL SYMBOL TRIANGLE NOTEHEAD UP WHITE;So;0;L;;;;;N;;;;;
+1D149;MUSICAL SYMBOL TRIANGLE NOTEHEAD UP BLACK;So;0;L;;;;;N;;;;;
+1D14A;MUSICAL SYMBOL TRIANGLE NOTEHEAD LEFT WHITE;So;0;L;;;;;N;;;;;
+1D14B;MUSICAL SYMBOL TRIANGLE NOTEHEAD LEFT BLACK;So;0;L;;;;;N;;;;;
+1D14C;MUSICAL SYMBOL TRIANGLE NOTEHEAD RIGHT WHITE;So;0;L;;;;;N;;;;;
+1D14D;MUSICAL SYMBOL TRIANGLE NOTEHEAD RIGHT BLACK;So;0;L;;;;;N;;;;;
+1D14E;MUSICAL SYMBOL TRIANGLE NOTEHEAD DOWN WHITE;So;0;L;;;;;N;;;;;
+1D14F;MUSICAL SYMBOL TRIANGLE NOTEHEAD DOWN BLACK;So;0;L;;;;;N;;;;;
+1D150;MUSICAL SYMBOL TRIANGLE NOTEHEAD UP RIGHT WHITE;So;0;L;;;;;N;;;;;
+1D151;MUSICAL SYMBOL TRIANGLE NOTEHEAD UP RIGHT BLACK;So;0;L;;;;;N;;;;;
+1D152;MUSICAL SYMBOL MOON NOTEHEAD WHITE;So;0;L;;;;;N;;;;;
+1D153;MUSICAL SYMBOL MOON NOTEHEAD BLACK;So;0;L;;;;;N;;;;;
+1D154;MUSICAL SYMBOL TRIANGLE-ROUND NOTEHEAD DOWN WHITE;So;0;L;;;;;N;;;;;
+1D155;MUSICAL SYMBOL TRIANGLE-ROUND NOTEHEAD DOWN BLACK;So;0;L;;;;;N;;;;;
+1D156;MUSICAL SYMBOL PARENTHESIS NOTEHEAD;So;0;L;;;;;N;;;;;
+1D157;MUSICAL SYMBOL VOID NOTEHEAD;So;0;L;;;;;N;;;;;
+1D158;MUSICAL SYMBOL NOTEHEAD BLACK;So;0;L;;;;;N;;;;;
+1D159;MUSICAL SYMBOL NULL NOTEHEAD;So;0;L;;;;;N;;;;;
+1D15A;MUSICAL SYMBOL CLUSTER NOTEHEAD WHITE;So;0;L;;;;;N;;;;;
+1D15B;MUSICAL SYMBOL CLUSTER NOTEHEAD BLACK;So;0;L;;;;;N;;;;;
+1D15C;MUSICAL SYMBOL BREVE;So;0;L;;;;;N;;;;;
+1D15D;MUSICAL SYMBOL WHOLE NOTE;So;0;L;;;;;N;;;;;
+1D15E;MUSICAL SYMBOL HALF NOTE;So;0;L;1D157 1D165;;;;N;;;;;
+1D15F;MUSICAL SYMBOL QUARTER NOTE;So;0;L;1D158 1D165;;;;N;;;;;
+1D160;MUSICAL SYMBOL EIGHTH NOTE;So;0;L;1D15F 1D16E;;;;N;;;;;
+1D161;MUSICAL SYMBOL SIXTEENTH NOTE;So;0;L;1D15F 1D16F;;;;N;;;;;
+1D162;MUSICAL SYMBOL THIRTY-SECOND NOTE;So;0;L;1D15F 1D170;;;;N;;;;;
+1D163;MUSICAL SYMBOL SIXTY-FOURTH NOTE;So;0;L;1D15F 1D171;;;;N;;;;;
+1D164;MUSICAL SYMBOL ONE HUNDRED TWENTY-EIGHTH NOTE;So;0;L;1D15F 1D172;;;;N;;;;;
+1D165;MUSICAL SYMBOL COMBINING STEM;Mc;216;L;;;;;N;;;;;
+1D166;MUSICAL SYMBOL COMBINING SPRECHGESANG STEM;Mc;216;L;;;;;N;;;;;
+1D167;MUSICAL SYMBOL COMBINING TREMOLO-1;Mn;1;NSM;;;;;N;;;;;
+1D168;MUSICAL SYMBOL COMBINING TREMOLO-2;Mn;1;NSM;;;;;N;;;;;
+1D169;MUSICAL SYMBOL COMBINING TREMOLO-3;Mn;1;NSM;;;;;N;;;;;
+1D16A;MUSICAL SYMBOL FINGERED TREMOLO-1;So;0;L;;;;;N;;;;;
+1D16B;MUSICAL SYMBOL FINGERED TREMOLO-2;So;0;L;;;;;N;;;;;
+1D16C;MUSICAL SYMBOL FINGERED TREMOLO-3;So;0;L;;;;;N;;;;;
+1D16D;MUSICAL SYMBOL COMBINING AUGMENTATION DOT;Mc;226;L;;;;;N;;;;;
+1D16E;MUSICAL SYMBOL COMBINING FLAG-1;Mc;216;L;;;;;N;;;;;
+1D16F;MUSICAL SYMBOL COMBINING FLAG-2;Mc;216;L;;;;;N;;;;;
+1D170;MUSICAL SYMBOL COMBINING FLAG-3;Mc;216;L;;;;;N;;;;;
+1D171;MUSICAL SYMBOL COMBINING FLAG-4;Mc;216;L;;;;;N;;;;;
+1D172;MUSICAL SYMBOL COMBINING FLAG-5;Mc;216;L;;;;;N;;;;;
+1D173;MUSICAL SYMBOL BEGIN BEAM;Cf;0;BN;;;;;N;;;;;
+1D174;MUSICAL SYMBOL END BEAM;Cf;0;BN;;;;;N;;;;;
+1D175;MUSICAL SYMBOL BEGIN TIE;Cf;0;BN;;;;;N;;;;;
+1D176;MUSICAL SYMBOL END TIE;Cf;0;BN;;;;;N;;;;;
+1D177;MUSICAL SYMBOL BEGIN SLUR;Cf;0;BN;;;;;N;;;;;
+1D178;MUSICAL SYMBOL END SLUR;Cf;0;BN;;;;;N;;;;;
+1D179;MUSICAL SYMBOL BEGIN PHRASE;Cf;0;BN;;;;;N;;;;;
+1D17A;MUSICAL SYMBOL END PHRASE;Cf;0;BN;;;;;N;;;;;
+1D17B;MUSICAL SYMBOL COMBINING ACCENT;Mn;220;NSM;;;;;N;;;;;
+1D17C;MUSICAL SYMBOL COMBINING STACCATO;Mn;220;NSM;;;;;N;;;;;
+1D17D;MUSICAL SYMBOL COMBINING TENUTO;Mn;220;NSM;;;;;N;;;;;
+1D17E;MUSICAL SYMBOL COMBINING STACCATISSIMO;Mn;220;NSM;;;;;N;;;;;
+1D17F;MUSICAL SYMBOL COMBINING MARCATO;Mn;220;NSM;;;;;N;;;;;
+1D180;MUSICAL SYMBOL COMBINING MARCATO-STACCATO;Mn;220;NSM;;;;;N;;;;;
+1D181;MUSICAL SYMBOL COMBINING ACCENT-STACCATO;Mn;220;NSM;;;;;N;;;;;
+1D182;MUSICAL SYMBOL COMBINING LOURE;Mn;220;NSM;;;;;N;;;;;
+1D183;MUSICAL SYMBOL ARPEGGIATO UP;So;0;L;;;;;N;;;;;
+1D184;MUSICAL SYMBOL ARPEGGIATO DOWN;So;0;L;;;;;N;;;;;
+1D185;MUSICAL SYMBOL COMBINING DOIT;Mn;230;NSM;;;;;N;;;;;
+1D186;MUSICAL SYMBOL COMBINING RIP;Mn;230;NSM;;;;;N;;;;;
+1D187;MUSICAL SYMBOL COMBINING FLIP;Mn;230;NSM;;;;;N;;;;;
+1D188;MUSICAL SYMBOL COMBINING SMEAR;Mn;230;NSM;;;;;N;;;;;
+1D189;MUSICAL SYMBOL COMBINING BEND;Mn;230;NSM;;;;;N;;;;;
+1D18A;MUSICAL SYMBOL COMBINING DOUBLE TONGUE;Mn;220;NSM;;;;;N;;;;;
+1D18B;MUSICAL SYMBOL COMBINING TRIPLE TONGUE;Mn;220;NSM;;;;;N;;;;;
+1D18C;MUSICAL SYMBOL RINFORZANDO;So;0;L;;;;;N;;;;;
+1D18D;MUSICAL SYMBOL SUBITO;So;0;L;;;;;N;;;;;
+1D18E;MUSICAL SYMBOL Z;So;0;L;;;;;N;;;;;
+1D18F;MUSICAL SYMBOL PIANO;So;0;L;;;;;N;;;;;
+1D190;MUSICAL SYMBOL MEZZO;So;0;L;;;;;N;;;;;
+1D191;MUSICAL SYMBOL FORTE;So;0;L;;;;;N;;;;;
+1D192;MUSICAL SYMBOL CRESCENDO;So;0;L;;;;;N;;;;;
+1D193;MUSICAL SYMBOL DECRESCENDO;So;0;L;;;;;N;;;;;
+1D194;MUSICAL SYMBOL GRACE NOTE SLASH;So;0;L;;;;;N;;;;;
+1D195;MUSICAL SYMBOL GRACE NOTE NO SLASH;So;0;L;;;;;N;;;;;
+1D196;MUSICAL SYMBOL TR;So;0;L;;;;;N;;;;;
+1D197;MUSICAL SYMBOL TURN;So;0;L;;;;;N;;;;;
+1D198;MUSICAL SYMBOL INVERTED TURN;So;0;L;;;;;N;;;;;
+1D199;MUSICAL SYMBOL TURN SLASH;So;0;L;;;;;N;;;;;
+1D19A;MUSICAL SYMBOL TURN UP;So;0;L;;;;;N;;;;;
+1D19B;MUSICAL SYMBOL ORNAMENT STROKE-1;So;0;L;;;;;N;;;;;
+1D19C;MUSICAL SYMBOL ORNAMENT STROKE-2;So;0;L;;;;;N;;;;;
+1D19D;MUSICAL SYMBOL ORNAMENT STROKE-3;So;0;L;;;;;N;;;;;
+1D19E;MUSICAL SYMBOL ORNAMENT STROKE-4;So;0;L;;;;;N;;;;;
+1D19F;MUSICAL SYMBOL ORNAMENT STROKE-5;So;0;L;;;;;N;;;;;
+1D1A0;MUSICAL SYMBOL ORNAMENT STROKE-6;So;0;L;;;;;N;;;;;
+1D1A1;MUSICAL SYMBOL ORNAMENT STROKE-7;So;0;L;;;;;N;;;;;
+1D1A2;MUSICAL SYMBOL ORNAMENT STROKE-8;So;0;L;;;;;N;;;;;
+1D1A3;MUSICAL SYMBOL ORNAMENT STROKE-9;So;0;L;;;;;N;;;;;
+1D1A4;MUSICAL SYMBOL ORNAMENT STROKE-10;So;0;L;;;;;N;;;;;
+1D1A5;MUSICAL SYMBOL ORNAMENT STROKE-11;So;0;L;;;;;N;;;;;
+1D1A6;MUSICAL SYMBOL HAUPTSTIMME;So;0;L;;;;;N;;;;;
+1D1A7;MUSICAL SYMBOL NEBENSTIMME;So;0;L;;;;;N;;;;;
+1D1A8;MUSICAL SYMBOL END OF STIMME;So;0;L;;;;;N;;;;;
+1D1A9;MUSICAL SYMBOL DEGREE SLASH;So;0;L;;;;;N;;;;;
+1D1AA;MUSICAL SYMBOL COMBINING DOWN BOW;Mn;230;NSM;;;;;N;;;;;
+1D1AB;MUSICAL SYMBOL COMBINING UP BOW;Mn;230;NSM;;;;;N;;;;;
+1D1AC;MUSICAL SYMBOL COMBINING HARMONIC;Mn;230;NSM;;;;;N;;;;;
+1D1AD;MUSICAL SYMBOL COMBINING SNAP PIZZICATO;Mn;230;NSM;;;;;N;;;;;
+1D1AE;MUSICAL SYMBOL PEDAL MARK;So;0;L;;;;;N;;;;;
+1D1AF;MUSICAL SYMBOL PEDAL UP MARK;So;0;L;;;;;N;;;;;
+1D1B0;MUSICAL SYMBOL HALF PEDAL MARK;So;0;L;;;;;N;;;;;
+1D1B1;MUSICAL SYMBOL GLISSANDO UP;So;0;L;;;;;N;;;;;
+1D1B2;MUSICAL SYMBOL GLISSANDO DOWN;So;0;L;;;;;N;;;;;
+1D1B3;MUSICAL SYMBOL WITH FINGERNAILS;So;0;L;;;;;N;;;;;
+1D1B4;MUSICAL SYMBOL DAMP;So;0;L;;;;;N;;;;;
+1D1B5;MUSICAL SYMBOL DAMP ALL;So;0;L;;;;;N;;;;;
+1D1B6;MUSICAL SYMBOL MAXIMA;So;0;L;;;;;N;;;;;
+1D1B7;MUSICAL SYMBOL LONGA;So;0;L;;;;;N;;;;;
+1D1B8;MUSICAL SYMBOL BREVIS;So;0;L;;;;;N;;;;;
+1D1B9;MUSICAL SYMBOL SEMIBREVIS WHITE;So;0;L;;;;;N;;;;;
+1D1BA;MUSICAL SYMBOL SEMIBREVIS BLACK;So;0;L;;;;;N;;;;;
+1D1BB;MUSICAL SYMBOL MINIMA;So;0;L;1D1B9 1D165;;;;N;;;;;
+1D1BC;MUSICAL SYMBOL MINIMA BLACK;So;0;L;1D1BA 1D165;;;;N;;;;;
+1D1BD;MUSICAL SYMBOL SEMIMINIMA WHITE;So;0;L;1D1BB 1D16E;;;;N;;;;;
+1D1BE;MUSICAL SYMBOL SEMIMINIMA BLACK;So;0;L;1D1BC 1D16E;;;;N;;;;;
+1D1BF;MUSICAL SYMBOL FUSA WHITE;So;0;L;1D1BB 1D16F;;;;N;;;;;
+1D1C0;MUSICAL SYMBOL FUSA BLACK;So;0;L;1D1BC 1D16F;;;;N;;;;;
+1D1C1;MUSICAL SYMBOL LONGA PERFECTA REST;So;0;L;;;;;N;;;;;
+1D1C2;MUSICAL SYMBOL LONGA IMPERFECTA REST;So;0;L;;;;;N;;;;;
+1D1C3;MUSICAL SYMBOL BREVIS REST;So;0;L;;;;;N;;;;;
+1D1C4;MUSICAL SYMBOL SEMIBREVIS REST;So;0;L;;;;;N;;;;;
+1D1C5;MUSICAL SYMBOL MINIMA REST;So;0;L;;;;;N;;;;;
+1D1C6;MUSICAL SYMBOL SEMIMINIMA REST;So;0;L;;;;;N;;;;;
+1D1C7;MUSICAL SYMBOL TEMPUS PERFECTUM CUM PROLATIONE PERFECTA;So;0;L;;;;;N;;;;;
+1D1C8;MUSICAL SYMBOL TEMPUS PERFECTUM CUM PROLATIONE IMPERFECTA;So;0;L;;;;;N;;;;;
+1D1C9;MUSICAL SYMBOL TEMPUS PERFECTUM CUM PROLATIONE PERFECTA DIMINUTION-1;So;0;L;;;;;N;;;;;
+1D1CA;MUSICAL SYMBOL TEMPUS IMPERFECTUM CUM PROLATIONE PERFECTA;So;0;L;;;;;N;;;;;
+1D1CB;MUSICAL SYMBOL TEMPUS IMPERFECTUM CUM PROLATIONE IMPERFECTA;So;0;L;;;;;N;;;;;
+1D1CC;MUSICAL SYMBOL TEMPUS IMPERFECTUM CUM PROLATIONE IMPERFECTA DIMINUTION-1;So;0;L;;;;;N;;;;;
+1D1CD;MUSICAL SYMBOL TEMPUS IMPERFECTUM CUM PROLATIONE IMPERFECTA DIMINUTION-2;So;0;L;;;;;N;;;;;
+1D1CE;MUSICAL SYMBOL TEMPUS IMPERFECTUM CUM PROLATIONE IMPERFECTA DIMINUTION-3;So;0;L;;;;;N;;;;;
+1D1CF;MUSICAL SYMBOL CROIX;So;0;L;;;;;N;;;;;
+1D1D0;MUSICAL SYMBOL GREGORIAN C CLEF;So;0;L;;;;;N;;;;;
+1D1D1;MUSICAL SYMBOL GREGORIAN F CLEF;So;0;L;;;;;N;;;;;
+1D1D2;MUSICAL SYMBOL SQUARE B;So;0;L;;;;;N;;;;;
+1D1D3;MUSICAL SYMBOL VIRGA;So;0;L;;;;;N;;;;;
+1D1D4;MUSICAL SYMBOL PODATUS;So;0;L;;;;;N;;;;;
+1D1D5;MUSICAL SYMBOL CLIVIS;So;0;L;;;;;N;;;;;
+1D1D6;MUSICAL SYMBOL SCANDICUS;So;0;L;;;;;N;;;;;
+1D1D7;MUSICAL SYMBOL CLIMACUS;So;0;L;;;;;N;;;;;
+1D1D8;MUSICAL SYMBOL TORCULUS;So;0;L;;;;;N;;;;;
+1D1D9;MUSICAL SYMBOL PORRECTUS;So;0;L;;;;;N;;;;;
+1D1DA;MUSICAL SYMBOL PORRECTUS FLEXUS;So;0;L;;;;;N;;;;;
+1D1DB;MUSICAL SYMBOL SCANDICUS FLEXUS;So;0;L;;;;;N;;;;;
+1D1DC;MUSICAL SYMBOL TORCULUS RESUPINUS;So;0;L;;;;;N;;;;;
+1D1DD;MUSICAL SYMBOL PES SUBPUNCTIS;So;0;L;;;;;N;;;;;
+1D300;MONOGRAM FOR EARTH;So;0;ON;;;;;N;;;;;
+1D301;DIGRAM FOR HEAVENLY EARTH;So;0;ON;;;;;N;;;;;
+1D302;DIGRAM FOR HUMAN EARTH;So;0;ON;;;;;N;;;;;
+1D303;DIGRAM FOR EARTHLY HEAVEN;So;0;ON;;;;;N;;;;;
+1D304;DIGRAM FOR EARTHLY HUMAN;So;0;ON;;;;;N;;;;;
+1D305;DIGRAM FOR EARTH;So;0;ON;;;;;N;;;;;
+1D306;TETRAGRAM FOR CENTRE;So;0;ON;;;;;N;;;;;
+1D307;TETRAGRAM FOR FULL CIRCLE;So;0;ON;;;;;N;;;;;
+1D308;TETRAGRAM FOR MIRED;So;0;ON;;;;;N;;;;;
+1D309;TETRAGRAM FOR BARRIER;So;0;ON;;;;;N;;;;;
+1D30A;TETRAGRAM FOR KEEPING SMALL;So;0;ON;;;;;N;;;;;
+1D30B;TETRAGRAM FOR CONTRARIETY;So;0;ON;;;;;N;;;;;
+1D30C;TETRAGRAM FOR ASCENT;So;0;ON;;;;;N;;;;;
+1D30D;TETRAGRAM FOR OPPOSITION;So;0;ON;;;;;N;;;;;
+1D30E;TETRAGRAM FOR BRANCHING OUT;So;0;ON;;;;;N;;;;;
+1D30F;TETRAGRAM FOR DEFECTIVENESS OR DISTORTION;So;0;ON;;;;;N;;;;;
+1D310;TETRAGRAM FOR DIVERGENCE;So;0;ON;;;;;N;;;;;
+1D311;TETRAGRAM FOR YOUTHFULNESS;So;0;ON;;;;;N;;;;;
+1D312;TETRAGRAM FOR INCREASE;So;0;ON;;;;;N;;;;;
+1D313;TETRAGRAM FOR PENETRATION;So;0;ON;;;;;N;;;;;
+1D314;TETRAGRAM FOR REACH;So;0;ON;;;;;N;;;;;
+1D315;TETRAGRAM FOR CONTACT;So;0;ON;;;;;N;;;;;
+1D316;TETRAGRAM FOR HOLDING BACK;So;0;ON;;;;;N;;;;;
+1D317;TETRAGRAM FOR WAITING;So;0;ON;;;;;N;;;;;
+1D318;TETRAGRAM FOR FOLLOWING;So;0;ON;;;;;N;;;;;
+1D319;TETRAGRAM FOR ADVANCE;So;0;ON;;;;;N;;;;;
+1D31A;TETRAGRAM FOR RELEASE;So;0;ON;;;;;N;;;;;
+1D31B;TETRAGRAM FOR RESISTANCE;So;0;ON;;;;;N;;;;;
+1D31C;TETRAGRAM FOR EASE;So;0;ON;;;;;N;;;;;
+1D31D;TETRAGRAM FOR JOY;So;0;ON;;;;;N;;;;;
+1D31E;TETRAGRAM FOR CONTENTION;So;0;ON;;;;;N;;;;;
+1D31F;TETRAGRAM FOR ENDEAVOUR;So;0;ON;;;;;N;;;;;
+1D320;TETRAGRAM FOR DUTIES;So;0;ON;;;;;N;;;;;
+1D321;TETRAGRAM FOR CHANGE;So;0;ON;;;;;N;;;;;
+1D322;TETRAGRAM FOR DECISIVENESS;So;0;ON;;;;;N;;;;;
+1D323;TETRAGRAM FOR BOLD RESOLUTION;So;0;ON;;;;;N;;;;;
+1D324;TETRAGRAM FOR PACKING;So;0;ON;;;;;N;;;;;
+1D325;TETRAGRAM FOR LEGION;So;0;ON;;;;;N;;;;;
+1D326;TETRAGRAM FOR CLOSENESS;So;0;ON;;;;;N;;;;;
+1D327;TETRAGRAM FOR KINSHIP;So;0;ON;;;;;N;;;;;
+1D328;TETRAGRAM FOR GATHERING;So;0;ON;;;;;N;;;;;
+1D329;TETRAGRAM FOR STRENGTH;So;0;ON;;;;;N;;;;;
+1D32A;TETRAGRAM FOR PURITY;So;0;ON;;;;;N;;;;;
+1D32B;TETRAGRAM FOR FULLNESS;So;0;ON;;;;;N;;;;;
+1D32C;TETRAGRAM FOR RESIDENCE;So;0;ON;;;;;N;;;;;
+1D32D;TETRAGRAM FOR LAW OR MODEL;So;0;ON;;;;;N;;;;;
+1D32E;TETRAGRAM FOR RESPONSE;So;0;ON;;;;;N;;;;;
+1D32F;TETRAGRAM FOR GOING TO MEET;So;0;ON;;;;;N;;;;;
+1D330;TETRAGRAM FOR ENCOUNTERS;So;0;ON;;;;;N;;;;;
+1D331;TETRAGRAM FOR STOVE;So;0;ON;;;;;N;;;;;
+1D332;TETRAGRAM FOR GREATNESS;So;0;ON;;;;;N;;;;;
+1D333;TETRAGRAM FOR ENLARGEMENT;So;0;ON;;;;;N;;;;;
+1D334;TETRAGRAM FOR PATTERN;So;0;ON;;;;;N;;;;;
+1D335;TETRAGRAM FOR RITUAL;So;0;ON;;;;;N;;;;;
+1D336;TETRAGRAM FOR FLIGHT;So;0;ON;;;;;N;;;;;
+1D337;TETRAGRAM FOR VASTNESS OR WASTING;So;0;ON;;;;;N;;;;;
+1D338;TETRAGRAM FOR CONSTANCY;So;0;ON;;;;;N;;;;;
+1D339;TETRAGRAM FOR MEASURE;So;0;ON;;;;;N;;;;;
+1D33A;TETRAGRAM FOR ETERNITY;So;0;ON;;;;;N;;;;;
+1D33B;TETRAGRAM FOR UNITY;So;0;ON;;;;;N;;;;;
+1D33C;TETRAGRAM FOR DIMINISHMENT;So;0;ON;;;;;N;;;;;
+1D33D;TETRAGRAM FOR CLOSED MOUTH;So;0;ON;;;;;N;;;;;
+1D33E;TETRAGRAM FOR GUARDEDNESS;So;0;ON;;;;;N;;;;;
+1D33F;TETRAGRAM FOR GATHERING IN;So;0;ON;;;;;N;;;;;
+1D340;TETRAGRAM FOR MASSING;So;0;ON;;;;;N;;;;;
+1D341;TETRAGRAM FOR ACCUMULATION;So;0;ON;;;;;N;;;;;
+1D342;TETRAGRAM FOR EMBELLISHMENT;So;0;ON;;;;;N;;;;;
+1D343;TETRAGRAM FOR DOUBT;So;0;ON;;;;;N;;;;;
+1D344;TETRAGRAM FOR WATCH;So;0;ON;;;;;N;;;;;
+1D345;TETRAGRAM FOR SINKING;So;0;ON;;;;;N;;;;;
+1D346;TETRAGRAM FOR INNER;So;0;ON;;;;;N;;;;;
+1D347;TETRAGRAM FOR DEPARTURE;So;0;ON;;;;;N;;;;;
+1D348;TETRAGRAM FOR DARKENING;So;0;ON;;;;;N;;;;;
+1D349;TETRAGRAM FOR DIMMING;So;0;ON;;;;;N;;;;;
+1D34A;TETRAGRAM FOR EXHAUSTION;So;0;ON;;;;;N;;;;;
+1D34B;TETRAGRAM FOR SEVERANCE;So;0;ON;;;;;N;;;;;
+1D34C;TETRAGRAM FOR STOPPAGE;So;0;ON;;;;;N;;;;;
+1D34D;TETRAGRAM FOR HARDNESS;So;0;ON;;;;;N;;;;;
+1D34E;TETRAGRAM FOR COMPLETION;So;0;ON;;;;;N;;;;;
+1D34F;TETRAGRAM FOR CLOSURE;So;0;ON;;;;;N;;;;;
+1D350;TETRAGRAM FOR FAILURE;So;0;ON;;;;;N;;;;;
+1D351;TETRAGRAM FOR AGGRAVATION;So;0;ON;;;;;N;;;;;
+1D352;TETRAGRAM FOR COMPLIANCE;So;0;ON;;;;;N;;;;;
+1D353;TETRAGRAM FOR ON THE VERGE;So;0;ON;;;;;N;;;;;
+1D354;TETRAGRAM FOR DIFFICULTIES;So;0;ON;;;;;N;;;;;
+1D355;TETRAGRAM FOR LABOURING;So;0;ON;;;;;N;;;;;
+1D356;TETRAGRAM FOR FOSTERING;So;0;ON;;;;;N;;;;;
+1D400;MATHEMATICAL BOLD CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;;
+1D401;MATHEMATICAL BOLD CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;;
+1D402;MATHEMATICAL BOLD CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;;
+1D403;MATHEMATICAL BOLD CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+1D404;MATHEMATICAL BOLD CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;;
+1D405;MATHEMATICAL BOLD CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;;
+1D406;MATHEMATICAL BOLD CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;;
+1D407;MATHEMATICAL BOLD CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;;
+1D408;MATHEMATICAL BOLD CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;;
+1D409;MATHEMATICAL BOLD CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;;
+1D40A;MATHEMATICAL BOLD CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;;
+1D40B;MATHEMATICAL BOLD CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;;
+1D40C;MATHEMATICAL BOLD CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;;
+1D40D;MATHEMATICAL BOLD CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;;
+1D40E;MATHEMATICAL BOLD CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;;
+1D40F;MATHEMATICAL BOLD CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;;
+1D410;MATHEMATICAL BOLD CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;;
+1D411;MATHEMATICAL BOLD CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;;
+1D412;MATHEMATICAL BOLD CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;;
+1D413;MATHEMATICAL BOLD CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;;
+1D414;MATHEMATICAL BOLD CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;;
+1D415;MATHEMATICAL BOLD CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;;
+1D416;MATHEMATICAL BOLD CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;;
+1D417;MATHEMATICAL BOLD CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;;
+1D418;MATHEMATICAL BOLD CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;;
+1D419;MATHEMATICAL BOLD CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;;
+1D41A;MATHEMATICAL BOLD SMALL A;Ll;0;L;<font> 0061;;;;N;;;;;
+1D41B;MATHEMATICAL BOLD SMALL B;Ll;0;L;<font> 0062;;;;N;;;;;
+1D41C;MATHEMATICAL BOLD SMALL C;Ll;0;L;<font> 0063;;;;N;;;;;
+1D41D;MATHEMATICAL BOLD SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+1D41E;MATHEMATICAL BOLD SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+1D41F;MATHEMATICAL BOLD SMALL F;Ll;0;L;<font> 0066;;;;N;;;;;
+1D420;MATHEMATICAL BOLD SMALL G;Ll;0;L;<font> 0067;;;;N;;;;;
+1D421;MATHEMATICAL BOLD SMALL H;Ll;0;L;<font> 0068;;;;N;;;;;
+1D422;MATHEMATICAL BOLD SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+1D423;MATHEMATICAL BOLD SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+1D424;MATHEMATICAL BOLD SMALL K;Ll;0;L;<font> 006B;;;;N;;;;;
+1D425;MATHEMATICAL BOLD SMALL L;Ll;0;L;<font> 006C;;;;N;;;;;
+1D426;MATHEMATICAL BOLD SMALL M;Ll;0;L;<font> 006D;;;;N;;;;;
+1D427;MATHEMATICAL BOLD SMALL N;Ll;0;L;<font> 006E;;;;N;;;;;
+1D428;MATHEMATICAL BOLD SMALL O;Ll;0;L;<font> 006F;;;;N;;;;;
+1D429;MATHEMATICAL BOLD SMALL P;Ll;0;L;<font> 0070;;;;N;;;;;
+1D42A;MATHEMATICAL BOLD SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;;
+1D42B;MATHEMATICAL BOLD SMALL R;Ll;0;L;<font> 0072;;;;N;;;;;
+1D42C;MATHEMATICAL BOLD SMALL S;Ll;0;L;<font> 0073;;;;N;;;;;
+1D42D;MATHEMATICAL BOLD SMALL T;Ll;0;L;<font> 0074;;;;N;;;;;
+1D42E;MATHEMATICAL BOLD SMALL U;Ll;0;L;<font> 0075;;;;N;;;;;
+1D42F;MATHEMATICAL BOLD SMALL V;Ll;0;L;<font> 0076;;;;N;;;;;
+1D430;MATHEMATICAL BOLD SMALL W;Ll;0;L;<font> 0077;;;;N;;;;;
+1D431;MATHEMATICAL BOLD SMALL X;Ll;0;L;<font> 0078;;;;N;;;;;
+1D432;MATHEMATICAL BOLD SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;;
+1D433;MATHEMATICAL BOLD SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;;
+1D434;MATHEMATICAL ITALIC CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;;
+1D435;MATHEMATICAL ITALIC CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;;
+1D436;MATHEMATICAL ITALIC CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;;
+1D437;MATHEMATICAL ITALIC CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+1D438;MATHEMATICAL ITALIC CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;;
+1D439;MATHEMATICAL ITALIC CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;;
+1D43A;MATHEMATICAL ITALIC CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;;
+1D43B;MATHEMATICAL ITALIC CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;;
+1D43C;MATHEMATICAL ITALIC CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;;
+1D43D;MATHEMATICAL ITALIC CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;;
+1D43E;MATHEMATICAL ITALIC CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;;
+1D43F;MATHEMATICAL ITALIC CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;;
+1D440;MATHEMATICAL ITALIC CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;;
+1D441;MATHEMATICAL ITALIC CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;;
+1D442;MATHEMATICAL ITALIC CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;;
+1D443;MATHEMATICAL ITALIC CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;;
+1D444;MATHEMATICAL ITALIC CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;;
+1D445;MATHEMATICAL ITALIC CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;;
+1D446;MATHEMATICAL ITALIC CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;;
+1D447;MATHEMATICAL ITALIC CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;;
+1D448;MATHEMATICAL ITALIC CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;;
+1D449;MATHEMATICAL ITALIC CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;;
+1D44A;MATHEMATICAL ITALIC CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;;
+1D44B;MATHEMATICAL ITALIC CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;;
+1D44C;MATHEMATICAL ITALIC CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;;
+1D44D;MATHEMATICAL ITALIC CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;;
+1D44E;MATHEMATICAL ITALIC SMALL A;Ll;0;L;<font> 0061;;;;N;;;;;
+1D44F;MATHEMATICAL ITALIC SMALL B;Ll;0;L;<font> 0062;;;;N;;;;;
+1D450;MATHEMATICAL ITALIC SMALL C;Ll;0;L;<font> 0063;;;;N;;;;;
+1D451;MATHEMATICAL ITALIC SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+1D452;MATHEMATICAL ITALIC SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+1D453;MATHEMATICAL ITALIC SMALL F;Ll;0;L;<font> 0066;;;;N;;;;;
+1D454;MATHEMATICAL ITALIC SMALL G;Ll;0;L;<font> 0067;;;;N;;;;;
+1D456;MATHEMATICAL ITALIC SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+1D457;MATHEMATICAL ITALIC SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+1D458;MATHEMATICAL ITALIC SMALL K;Ll;0;L;<font> 006B;;;;N;;;;;
+1D459;MATHEMATICAL ITALIC SMALL L;Ll;0;L;<font> 006C;;;;N;;;;;
+1D45A;MATHEMATICAL ITALIC SMALL M;Ll;0;L;<font> 006D;;;;N;;;;;
+1D45B;MATHEMATICAL ITALIC SMALL N;Ll;0;L;<font> 006E;;;;N;;;;;
+1D45C;MATHEMATICAL ITALIC SMALL O;Ll;0;L;<font> 006F;;;;N;;;;;
+1D45D;MATHEMATICAL ITALIC SMALL P;Ll;0;L;<font> 0070;;;;N;;;;;
+1D45E;MATHEMATICAL ITALIC SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;;
+1D45F;MATHEMATICAL ITALIC SMALL R;Ll;0;L;<font> 0072;;;;N;;;;;
+1D460;MATHEMATICAL ITALIC SMALL S;Ll;0;L;<font> 0073;;;;N;;;;;
+1D461;MATHEMATICAL ITALIC SMALL T;Ll;0;L;<font> 0074;;;;N;;;;;
+1D462;MATHEMATICAL ITALIC SMALL U;Ll;0;L;<font> 0075;;;;N;;;;;
+1D463;MATHEMATICAL ITALIC SMALL V;Ll;0;L;<font> 0076;;;;N;;;;;
+1D464;MATHEMATICAL ITALIC SMALL W;Ll;0;L;<font> 0077;;;;N;;;;;
+1D465;MATHEMATICAL ITALIC SMALL X;Ll;0;L;<font> 0078;;;;N;;;;;
+1D466;MATHEMATICAL ITALIC SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;;
+1D467;MATHEMATICAL ITALIC SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;;
+1D468;MATHEMATICAL BOLD ITALIC CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;;
+1D469;MATHEMATICAL BOLD ITALIC CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;;
+1D46A;MATHEMATICAL BOLD ITALIC CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;;
+1D46B;MATHEMATICAL BOLD ITALIC CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+1D46C;MATHEMATICAL BOLD ITALIC CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;;
+1D46D;MATHEMATICAL BOLD ITALIC CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;;
+1D46E;MATHEMATICAL BOLD ITALIC CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;;
+1D46F;MATHEMATICAL BOLD ITALIC CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;;
+1D470;MATHEMATICAL BOLD ITALIC CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;;
+1D471;MATHEMATICAL BOLD ITALIC CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;;
+1D472;MATHEMATICAL BOLD ITALIC CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;;
+1D473;MATHEMATICAL BOLD ITALIC CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;;
+1D474;MATHEMATICAL BOLD ITALIC CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;;
+1D475;MATHEMATICAL BOLD ITALIC CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;;
+1D476;MATHEMATICAL BOLD ITALIC CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;;
+1D477;MATHEMATICAL BOLD ITALIC CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;;
+1D478;MATHEMATICAL BOLD ITALIC CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;;
+1D479;MATHEMATICAL BOLD ITALIC CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;;
+1D47A;MATHEMATICAL BOLD ITALIC CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;;
+1D47B;MATHEMATICAL BOLD ITALIC CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;;
+1D47C;MATHEMATICAL BOLD ITALIC CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;;
+1D47D;MATHEMATICAL BOLD ITALIC CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;;
+1D47E;MATHEMATICAL BOLD ITALIC CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;;
+1D47F;MATHEMATICAL BOLD ITALIC CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;;
+1D480;MATHEMATICAL BOLD ITALIC CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;;
+1D481;MATHEMATICAL BOLD ITALIC CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;;
+1D482;MATHEMATICAL BOLD ITALIC SMALL A;Ll;0;L;<font> 0061;;;;N;;;;;
+1D483;MATHEMATICAL BOLD ITALIC SMALL B;Ll;0;L;<font> 0062;;;;N;;;;;
+1D484;MATHEMATICAL BOLD ITALIC SMALL C;Ll;0;L;<font> 0063;;;;N;;;;;
+1D485;MATHEMATICAL BOLD ITALIC SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+1D486;MATHEMATICAL BOLD ITALIC SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+1D487;MATHEMATICAL BOLD ITALIC SMALL F;Ll;0;L;<font> 0066;;;;N;;;;;
+1D488;MATHEMATICAL BOLD ITALIC SMALL G;Ll;0;L;<font> 0067;;;;N;;;;;
+1D489;MATHEMATICAL BOLD ITALIC SMALL H;Ll;0;L;<font> 0068;;;;N;;;;;
+1D48A;MATHEMATICAL BOLD ITALIC SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+1D48B;MATHEMATICAL BOLD ITALIC SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+1D48C;MATHEMATICAL BOLD ITALIC SMALL K;Ll;0;L;<font> 006B;;;;N;;;;;
+1D48D;MATHEMATICAL BOLD ITALIC SMALL L;Ll;0;L;<font> 006C;;;;N;;;;;
+1D48E;MATHEMATICAL BOLD ITALIC SMALL M;Ll;0;L;<font> 006D;;;;N;;;;;
+1D48F;MATHEMATICAL BOLD ITALIC SMALL N;Ll;0;L;<font> 006E;;;;N;;;;;
+1D490;MATHEMATICAL BOLD ITALIC SMALL O;Ll;0;L;<font> 006F;;;;N;;;;;
+1D491;MATHEMATICAL BOLD ITALIC SMALL P;Ll;0;L;<font> 0070;;;;N;;;;;
+1D492;MATHEMATICAL BOLD ITALIC SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;;
+1D493;MATHEMATICAL BOLD ITALIC SMALL R;Ll;0;L;<font> 0072;;;;N;;;;;
+1D494;MATHEMATICAL BOLD ITALIC SMALL S;Ll;0;L;<font> 0073;;;;N;;;;;
+1D495;MATHEMATICAL BOLD ITALIC SMALL T;Ll;0;L;<font> 0074;;;;N;;;;;
+1D496;MATHEMATICAL BOLD ITALIC SMALL U;Ll;0;L;<font> 0075;;;;N;;;;;
+1D497;MATHEMATICAL BOLD ITALIC SMALL V;Ll;0;L;<font> 0076;;;;N;;;;;
+1D498;MATHEMATICAL BOLD ITALIC SMALL W;Ll;0;L;<font> 0077;;;;N;;;;;
+1D499;MATHEMATICAL BOLD ITALIC SMALL X;Ll;0;L;<font> 0078;;;;N;;;;;
+1D49A;MATHEMATICAL BOLD ITALIC SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;;
+1D49B;MATHEMATICAL BOLD ITALIC SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;;
+1D49C;MATHEMATICAL SCRIPT CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;;
+1D49E;MATHEMATICAL SCRIPT CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;;
+1D49F;MATHEMATICAL SCRIPT CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+1D4A2;MATHEMATICAL SCRIPT CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;;
+1D4A5;MATHEMATICAL SCRIPT CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;;
+1D4A6;MATHEMATICAL SCRIPT CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;;
+1D4A9;MATHEMATICAL SCRIPT CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;;
+1D4AA;MATHEMATICAL SCRIPT CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;;
+1D4AB;MATHEMATICAL SCRIPT CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;;
+1D4AC;MATHEMATICAL SCRIPT CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;;
+1D4AE;MATHEMATICAL SCRIPT CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;;
+1D4AF;MATHEMATICAL SCRIPT CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;;
+1D4B0;MATHEMATICAL SCRIPT CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;;
+1D4B1;MATHEMATICAL SCRIPT CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;;
+1D4B2;MATHEMATICAL SCRIPT CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;;
+1D4B3;MATHEMATICAL SCRIPT CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;;
+1D4B4;MATHEMATICAL SCRIPT CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;;
+1D4B5;MATHEMATICAL SCRIPT CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;;
+1D4B6;MATHEMATICAL SCRIPT SMALL A;Ll;0;L;<font> 0061;;;;N;;;;;
+1D4B7;MATHEMATICAL SCRIPT SMALL B;Ll;0;L;<font> 0062;;;;N;;;;;
+1D4B8;MATHEMATICAL SCRIPT SMALL C;Ll;0;L;<font> 0063;;;;N;;;;;
+1D4B9;MATHEMATICAL SCRIPT SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+1D4BB;MATHEMATICAL SCRIPT SMALL F;Ll;0;L;<font> 0066;;;;N;;;;;
+1D4BD;MATHEMATICAL SCRIPT SMALL H;Ll;0;L;<font> 0068;;;;N;;;;;
+1D4BE;MATHEMATICAL SCRIPT SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+1D4BF;MATHEMATICAL SCRIPT SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+1D4C0;MATHEMATICAL SCRIPT SMALL K;Ll;0;L;<font> 006B;;;;N;;;;;
+1D4C1;MATHEMATICAL SCRIPT SMALL L;Ll;0;L;<font> 006C;;;;N;;;;;
+1D4C2;MATHEMATICAL SCRIPT SMALL M;Ll;0;L;<font> 006D;;;;N;;;;;
+1D4C3;MATHEMATICAL SCRIPT SMALL N;Ll;0;L;<font> 006E;;;;N;;;;;
+1D4C5;MATHEMATICAL SCRIPT SMALL P;Ll;0;L;<font> 0070;;;;N;;;;;
+1D4C6;MATHEMATICAL SCRIPT SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;;
+1D4C7;MATHEMATICAL SCRIPT SMALL R;Ll;0;L;<font> 0072;;;;N;;;;;
+1D4C8;MATHEMATICAL SCRIPT SMALL S;Ll;0;L;<font> 0073;;;;N;;;;;
+1D4C9;MATHEMATICAL SCRIPT SMALL T;Ll;0;L;<font> 0074;;;;N;;;;;
+1D4CA;MATHEMATICAL SCRIPT SMALL U;Ll;0;L;<font> 0075;;;;N;;;;;
+1D4CB;MATHEMATICAL SCRIPT SMALL V;Ll;0;L;<font> 0076;;;;N;;;;;
+1D4CC;MATHEMATICAL SCRIPT SMALL W;Ll;0;L;<font> 0077;;;;N;;;;;
+1D4CD;MATHEMATICAL SCRIPT SMALL X;Ll;0;L;<font> 0078;;;;N;;;;;
+1D4CE;MATHEMATICAL SCRIPT SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;;
+1D4CF;MATHEMATICAL SCRIPT SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;;
+1D4D0;MATHEMATICAL BOLD SCRIPT CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;;
+1D4D1;MATHEMATICAL BOLD SCRIPT CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;;
+1D4D2;MATHEMATICAL BOLD SCRIPT CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;;
+1D4D3;MATHEMATICAL BOLD SCRIPT CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+1D4D4;MATHEMATICAL BOLD SCRIPT CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;;
+1D4D5;MATHEMATICAL BOLD SCRIPT CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;;
+1D4D6;MATHEMATICAL BOLD SCRIPT CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;;
+1D4D7;MATHEMATICAL BOLD SCRIPT CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;;
+1D4D8;MATHEMATICAL BOLD SCRIPT CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;;
+1D4D9;MATHEMATICAL BOLD SCRIPT CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;;
+1D4DA;MATHEMATICAL BOLD SCRIPT CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;;
+1D4DB;MATHEMATICAL BOLD SCRIPT CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;;
+1D4DC;MATHEMATICAL BOLD SCRIPT CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;;
+1D4DD;MATHEMATICAL BOLD SCRIPT CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;;
+1D4DE;MATHEMATICAL BOLD SCRIPT CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;;
+1D4DF;MATHEMATICAL BOLD SCRIPT CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;;
+1D4E0;MATHEMATICAL BOLD SCRIPT CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;;
+1D4E1;MATHEMATICAL BOLD SCRIPT CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;;
+1D4E2;MATHEMATICAL BOLD SCRIPT CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;;
+1D4E3;MATHEMATICAL BOLD SCRIPT CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;;
+1D4E4;MATHEMATICAL BOLD SCRIPT CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;;
+1D4E5;MATHEMATICAL BOLD SCRIPT CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;;
+1D4E6;MATHEMATICAL BOLD SCRIPT CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;;
+1D4E7;MATHEMATICAL BOLD SCRIPT CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;;
+1D4E8;MATHEMATICAL BOLD SCRIPT CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;;
+1D4E9;MATHEMATICAL BOLD SCRIPT CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;;
+1D4EA;MATHEMATICAL BOLD SCRIPT SMALL A;Ll;0;L;<font> 0061;;;;N;;;;;
+1D4EB;MATHEMATICAL BOLD SCRIPT SMALL B;Ll;0;L;<font> 0062;;;;N;;;;;
+1D4EC;MATHEMATICAL BOLD SCRIPT SMALL C;Ll;0;L;<font> 0063;;;;N;;;;;
+1D4ED;MATHEMATICAL BOLD SCRIPT SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+1D4EE;MATHEMATICAL BOLD SCRIPT SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+1D4EF;MATHEMATICAL BOLD SCRIPT SMALL F;Ll;0;L;<font> 0066;;;;N;;;;;
+1D4F0;MATHEMATICAL BOLD SCRIPT SMALL G;Ll;0;L;<font> 0067;;;;N;;;;;
+1D4F1;MATHEMATICAL BOLD SCRIPT SMALL H;Ll;0;L;<font> 0068;;;;N;;;;;
+1D4F2;MATHEMATICAL BOLD SCRIPT SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+1D4F3;MATHEMATICAL BOLD SCRIPT SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+1D4F4;MATHEMATICAL BOLD SCRIPT SMALL K;Ll;0;L;<font> 006B;;;;N;;;;;
+1D4F5;MATHEMATICAL BOLD SCRIPT SMALL L;Ll;0;L;<font> 006C;;;;N;;;;;
+1D4F6;MATHEMATICAL BOLD SCRIPT SMALL M;Ll;0;L;<font> 006D;;;;N;;;;;
+1D4F7;MATHEMATICAL BOLD SCRIPT SMALL N;Ll;0;L;<font> 006E;;;;N;;;;;
+1D4F8;MATHEMATICAL BOLD SCRIPT SMALL O;Ll;0;L;<font> 006F;;;;N;;;;;
+1D4F9;MATHEMATICAL BOLD SCRIPT SMALL P;Ll;0;L;<font> 0070;;;;N;;;;;
+1D4FA;MATHEMATICAL BOLD SCRIPT SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;;
+1D4FB;MATHEMATICAL BOLD SCRIPT SMALL R;Ll;0;L;<font> 0072;;;;N;;;;;
+1D4FC;MATHEMATICAL BOLD SCRIPT SMALL S;Ll;0;L;<font> 0073;;;;N;;;;;
+1D4FD;MATHEMATICAL BOLD SCRIPT SMALL T;Ll;0;L;<font> 0074;;;;N;;;;;
+1D4FE;MATHEMATICAL BOLD SCRIPT SMALL U;Ll;0;L;<font> 0075;;;;N;;;;;
+1D4FF;MATHEMATICAL BOLD SCRIPT SMALL V;Ll;0;L;<font> 0076;;;;N;;;;;
+1D500;MATHEMATICAL BOLD SCRIPT SMALL W;Ll;0;L;<font> 0077;;;;N;;;;;
+1D501;MATHEMATICAL BOLD SCRIPT SMALL X;Ll;0;L;<font> 0078;;;;N;;;;;
+1D502;MATHEMATICAL BOLD SCRIPT SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;;
+1D503;MATHEMATICAL BOLD SCRIPT SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;;
+1D504;MATHEMATICAL FRAKTUR CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;;
+1D505;MATHEMATICAL FRAKTUR CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;;
+1D507;MATHEMATICAL FRAKTUR CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+1D508;MATHEMATICAL FRAKTUR CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;;
+1D509;MATHEMATICAL FRAKTUR CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;;
+1D50A;MATHEMATICAL FRAKTUR CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;;
+1D50D;MATHEMATICAL FRAKTUR CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;;
+1D50E;MATHEMATICAL FRAKTUR CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;;
+1D50F;MATHEMATICAL FRAKTUR CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;;
+1D510;MATHEMATICAL FRAKTUR CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;;
+1D511;MATHEMATICAL FRAKTUR CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;;
+1D512;MATHEMATICAL FRAKTUR CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;;
+1D513;MATHEMATICAL FRAKTUR CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;;
+1D514;MATHEMATICAL FRAKTUR CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;;
+1D516;MATHEMATICAL FRAKTUR CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;;
+1D517;MATHEMATICAL FRAKTUR CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;;
+1D518;MATHEMATICAL FRAKTUR CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;;
+1D519;MATHEMATICAL FRAKTUR CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;;
+1D51A;MATHEMATICAL FRAKTUR CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;;
+1D51B;MATHEMATICAL FRAKTUR CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;;
+1D51C;MATHEMATICAL FRAKTUR CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;;
+1D51E;MATHEMATICAL FRAKTUR SMALL A;Ll;0;L;<font> 0061;;;;N;;;;;
+1D51F;MATHEMATICAL FRAKTUR SMALL B;Ll;0;L;<font> 0062;;;;N;;;;;
+1D520;MATHEMATICAL FRAKTUR SMALL C;Ll;0;L;<font> 0063;;;;N;;;;;
+1D521;MATHEMATICAL FRAKTUR SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+1D522;MATHEMATICAL FRAKTUR SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+1D523;MATHEMATICAL FRAKTUR SMALL F;Ll;0;L;<font> 0066;;;;N;;;;;
+1D524;MATHEMATICAL FRAKTUR SMALL G;Ll;0;L;<font> 0067;;;;N;;;;;
+1D525;MATHEMATICAL FRAKTUR SMALL H;Ll;0;L;<font> 0068;;;;N;;;;;
+1D526;MATHEMATICAL FRAKTUR SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+1D527;MATHEMATICAL FRAKTUR SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+1D528;MATHEMATICAL FRAKTUR SMALL K;Ll;0;L;<font> 006B;;;;N;;;;;
+1D529;MATHEMATICAL FRAKTUR SMALL L;Ll;0;L;<font> 006C;;;;N;;;;;
+1D52A;MATHEMATICAL FRAKTUR SMALL M;Ll;0;L;<font> 006D;;;;N;;;;;
+1D52B;MATHEMATICAL FRAKTUR SMALL N;Ll;0;L;<font> 006E;;;;N;;;;;
+1D52C;MATHEMATICAL FRAKTUR SMALL O;Ll;0;L;<font> 006F;;;;N;;;;;
+1D52D;MATHEMATICAL FRAKTUR SMALL P;Ll;0;L;<font> 0070;;;;N;;;;;
+1D52E;MATHEMATICAL FRAKTUR SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;;
+1D52F;MATHEMATICAL FRAKTUR SMALL R;Ll;0;L;<font> 0072;;;;N;;;;;
+1D530;MATHEMATICAL FRAKTUR SMALL S;Ll;0;L;<font> 0073;;;;N;;;;;
+1D531;MATHEMATICAL FRAKTUR SMALL T;Ll;0;L;<font> 0074;;;;N;;;;;
+1D532;MATHEMATICAL FRAKTUR SMALL U;Ll;0;L;<font> 0075;;;;N;;;;;
+1D533;MATHEMATICAL FRAKTUR SMALL V;Ll;0;L;<font> 0076;;;;N;;;;;
+1D534;MATHEMATICAL FRAKTUR SMALL W;Ll;0;L;<font> 0077;;;;N;;;;;
+1D535;MATHEMATICAL FRAKTUR SMALL X;Ll;0;L;<font> 0078;;;;N;;;;;
+1D536;MATHEMATICAL FRAKTUR SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;;
+1D537;MATHEMATICAL FRAKTUR SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;;
+1D538;MATHEMATICAL DOUBLE-STRUCK CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;;
+1D539;MATHEMATICAL DOUBLE-STRUCK CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;;
+1D53B;MATHEMATICAL DOUBLE-STRUCK CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+1D53C;MATHEMATICAL DOUBLE-STRUCK CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;;
+1D53D;MATHEMATICAL DOUBLE-STRUCK CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;;
+1D53E;MATHEMATICAL DOUBLE-STRUCK CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;;
+1D540;MATHEMATICAL DOUBLE-STRUCK CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;;
+1D541;MATHEMATICAL DOUBLE-STRUCK CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;;
+1D542;MATHEMATICAL DOUBLE-STRUCK CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;;
+1D543;MATHEMATICAL DOUBLE-STRUCK CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;;
+1D544;MATHEMATICAL DOUBLE-STRUCK CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;;
+1D546;MATHEMATICAL DOUBLE-STRUCK CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;;
+1D54A;MATHEMATICAL DOUBLE-STRUCK CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;;
+1D54B;MATHEMATICAL DOUBLE-STRUCK CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;;
+1D54C;MATHEMATICAL DOUBLE-STRUCK CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;;
+1D54D;MATHEMATICAL DOUBLE-STRUCK CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;;
+1D54E;MATHEMATICAL DOUBLE-STRUCK CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;;
+1D54F;MATHEMATICAL DOUBLE-STRUCK CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;;
+1D550;MATHEMATICAL DOUBLE-STRUCK CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;;
+1D552;MATHEMATICAL DOUBLE-STRUCK SMALL A;Ll;0;L;<font> 0061;;;;N;;;;;
+1D553;MATHEMATICAL DOUBLE-STRUCK SMALL B;Ll;0;L;<font> 0062;;;;N;;;;;
+1D554;MATHEMATICAL DOUBLE-STRUCK SMALL C;Ll;0;L;<font> 0063;;;;N;;;;;
+1D555;MATHEMATICAL DOUBLE-STRUCK SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+1D556;MATHEMATICAL DOUBLE-STRUCK SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+1D557;MATHEMATICAL DOUBLE-STRUCK SMALL F;Ll;0;L;<font> 0066;;;;N;;;;;
+1D558;MATHEMATICAL DOUBLE-STRUCK SMALL G;Ll;0;L;<font> 0067;;;;N;;;;;
+1D559;MATHEMATICAL DOUBLE-STRUCK SMALL H;Ll;0;L;<font> 0068;;;;N;;;;;
+1D55A;MATHEMATICAL DOUBLE-STRUCK SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+1D55B;MATHEMATICAL DOUBLE-STRUCK SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+1D55C;MATHEMATICAL DOUBLE-STRUCK SMALL K;Ll;0;L;<font> 006B;;;;N;;;;;
+1D55D;MATHEMATICAL DOUBLE-STRUCK SMALL L;Ll;0;L;<font> 006C;;;;N;;;;;
+1D55E;MATHEMATICAL DOUBLE-STRUCK SMALL M;Ll;0;L;<font> 006D;;;;N;;;;;
+1D55F;MATHEMATICAL DOUBLE-STRUCK SMALL N;Ll;0;L;<font> 006E;;;;N;;;;;
+1D560;MATHEMATICAL DOUBLE-STRUCK SMALL O;Ll;0;L;<font> 006F;;;;N;;;;;
+1D561;MATHEMATICAL DOUBLE-STRUCK SMALL P;Ll;0;L;<font> 0070;;;;N;;;;;
+1D562;MATHEMATICAL DOUBLE-STRUCK SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;;
+1D563;MATHEMATICAL DOUBLE-STRUCK SMALL R;Ll;0;L;<font> 0072;;;;N;;;;;
+1D564;MATHEMATICAL DOUBLE-STRUCK SMALL S;Ll;0;L;<font> 0073;;;;N;;;;;
+1D565;MATHEMATICAL DOUBLE-STRUCK SMALL T;Ll;0;L;<font> 0074;;;;N;;;;;
+1D566;MATHEMATICAL DOUBLE-STRUCK SMALL U;Ll;0;L;<font> 0075;;;;N;;;;;
+1D567;MATHEMATICAL DOUBLE-STRUCK SMALL V;Ll;0;L;<font> 0076;;;;N;;;;;
+1D568;MATHEMATICAL DOUBLE-STRUCK SMALL W;Ll;0;L;<font> 0077;;;;N;;;;;
+1D569;MATHEMATICAL DOUBLE-STRUCK SMALL X;Ll;0;L;<font> 0078;;;;N;;;;;
+1D56A;MATHEMATICAL DOUBLE-STRUCK SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;;
+1D56B;MATHEMATICAL DOUBLE-STRUCK SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;;
+1D56C;MATHEMATICAL BOLD FRAKTUR CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;;
+1D56D;MATHEMATICAL BOLD FRAKTUR CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;;
+1D56E;MATHEMATICAL BOLD FRAKTUR CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;;
+1D56F;MATHEMATICAL BOLD FRAKTUR CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+1D570;MATHEMATICAL BOLD FRAKTUR CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;;
+1D571;MATHEMATICAL BOLD FRAKTUR CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;;
+1D572;MATHEMATICAL BOLD FRAKTUR CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;;
+1D573;MATHEMATICAL BOLD FRAKTUR CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;;
+1D574;MATHEMATICAL BOLD FRAKTUR CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;;
+1D575;MATHEMATICAL BOLD FRAKTUR CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;;
+1D576;MATHEMATICAL BOLD FRAKTUR CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;;
+1D577;MATHEMATICAL BOLD FRAKTUR CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;;
+1D578;MATHEMATICAL BOLD FRAKTUR CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;;
+1D579;MATHEMATICAL BOLD FRAKTUR CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;;
+1D57A;MATHEMATICAL BOLD FRAKTUR CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;;
+1D57B;MATHEMATICAL BOLD FRAKTUR CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;;
+1D57C;MATHEMATICAL BOLD FRAKTUR CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;;
+1D57D;MATHEMATICAL BOLD FRAKTUR CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;;
+1D57E;MATHEMATICAL BOLD FRAKTUR CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;;
+1D57F;MATHEMATICAL BOLD FRAKTUR CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;;
+1D580;MATHEMATICAL BOLD FRAKTUR CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;;
+1D581;MATHEMATICAL BOLD FRAKTUR CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;;
+1D582;MATHEMATICAL BOLD FRAKTUR CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;;
+1D583;MATHEMATICAL BOLD FRAKTUR CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;;
+1D584;MATHEMATICAL BOLD FRAKTUR CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;;
+1D585;MATHEMATICAL BOLD FRAKTUR CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;;
+1D586;MATHEMATICAL BOLD FRAKTUR SMALL A;Ll;0;L;<font> 0061;;;;N;;;;;
+1D587;MATHEMATICAL BOLD FRAKTUR SMALL B;Ll;0;L;<font> 0062;;;;N;;;;;
+1D588;MATHEMATICAL BOLD FRAKTUR SMALL C;Ll;0;L;<font> 0063;;;;N;;;;;
+1D589;MATHEMATICAL BOLD FRAKTUR SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+1D58A;MATHEMATICAL BOLD FRAKTUR SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+1D58B;MATHEMATICAL BOLD FRAKTUR SMALL F;Ll;0;L;<font> 0066;;;;N;;;;;
+1D58C;MATHEMATICAL BOLD FRAKTUR SMALL G;Ll;0;L;<font> 0067;;;;N;;;;;
+1D58D;MATHEMATICAL BOLD FRAKTUR SMALL H;Ll;0;L;<font> 0068;;;;N;;;;;
+1D58E;MATHEMATICAL BOLD FRAKTUR SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+1D58F;MATHEMATICAL BOLD FRAKTUR SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+1D590;MATHEMATICAL BOLD FRAKTUR SMALL K;Ll;0;L;<font> 006B;;;;N;;;;;
+1D591;MATHEMATICAL BOLD FRAKTUR SMALL L;Ll;0;L;<font> 006C;;;;N;;;;;
+1D592;MATHEMATICAL BOLD FRAKTUR SMALL M;Ll;0;L;<font> 006D;;;;N;;;;;
+1D593;MATHEMATICAL BOLD FRAKTUR SMALL N;Ll;0;L;<font> 006E;;;;N;;;;;
+1D594;MATHEMATICAL BOLD FRAKTUR SMALL O;Ll;0;L;<font> 006F;;;;N;;;;;
+1D595;MATHEMATICAL BOLD FRAKTUR SMALL P;Ll;0;L;<font> 0070;;;;N;;;;;
+1D596;MATHEMATICAL BOLD FRAKTUR SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;;
+1D597;MATHEMATICAL BOLD FRAKTUR SMALL R;Ll;0;L;<font> 0072;;;;N;;;;;
+1D598;MATHEMATICAL BOLD FRAKTUR SMALL S;Ll;0;L;<font> 0073;;;;N;;;;;
+1D599;MATHEMATICAL BOLD FRAKTUR SMALL T;Ll;0;L;<font> 0074;;;;N;;;;;
+1D59A;MATHEMATICAL BOLD FRAKTUR SMALL U;Ll;0;L;<font> 0075;;;;N;;;;;
+1D59B;MATHEMATICAL BOLD FRAKTUR SMALL V;Ll;0;L;<font> 0076;;;;N;;;;;
+1D59C;MATHEMATICAL BOLD FRAKTUR SMALL W;Ll;0;L;<font> 0077;;;;N;;;;;
+1D59D;MATHEMATICAL BOLD FRAKTUR SMALL X;Ll;0;L;<font> 0078;;;;N;;;;;
+1D59E;MATHEMATICAL BOLD FRAKTUR SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;;
+1D59F;MATHEMATICAL BOLD FRAKTUR SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;;
+1D5A0;MATHEMATICAL SANS-SERIF CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;;
+1D5A1;MATHEMATICAL SANS-SERIF CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;;
+1D5A2;MATHEMATICAL SANS-SERIF CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;;
+1D5A3;MATHEMATICAL SANS-SERIF CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+1D5A4;MATHEMATICAL SANS-SERIF CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;;
+1D5A5;MATHEMATICAL SANS-SERIF CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;;
+1D5A6;MATHEMATICAL SANS-SERIF CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;;
+1D5A7;MATHEMATICAL SANS-SERIF CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;;
+1D5A8;MATHEMATICAL SANS-SERIF CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;;
+1D5A9;MATHEMATICAL SANS-SERIF CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;;
+1D5AA;MATHEMATICAL SANS-SERIF CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;;
+1D5AB;MATHEMATICAL SANS-SERIF CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;;
+1D5AC;MATHEMATICAL SANS-SERIF CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;;
+1D5AD;MATHEMATICAL SANS-SERIF CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;;
+1D5AE;MATHEMATICAL SANS-SERIF CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;;
+1D5AF;MATHEMATICAL SANS-SERIF CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;;
+1D5B0;MATHEMATICAL SANS-SERIF CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;;
+1D5B1;MATHEMATICAL SANS-SERIF CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;;
+1D5B2;MATHEMATICAL SANS-SERIF CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;;
+1D5B3;MATHEMATICAL SANS-SERIF CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;;
+1D5B4;MATHEMATICAL SANS-SERIF CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;;
+1D5B5;MATHEMATICAL SANS-SERIF CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;;
+1D5B6;MATHEMATICAL SANS-SERIF CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;;
+1D5B7;MATHEMATICAL SANS-SERIF CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;;
+1D5B8;MATHEMATICAL SANS-SERIF CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;;
+1D5B9;MATHEMATICAL SANS-SERIF CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;;
+1D5BA;MATHEMATICAL SANS-SERIF SMALL A;Ll;0;L;<font> 0061;;;;N;;;;;
+1D5BB;MATHEMATICAL SANS-SERIF SMALL B;Ll;0;L;<font> 0062;;;;N;;;;;
+1D5BC;MATHEMATICAL SANS-SERIF SMALL C;Ll;0;L;<font> 0063;;;;N;;;;;
+1D5BD;MATHEMATICAL SANS-SERIF SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+1D5BE;MATHEMATICAL SANS-SERIF SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+1D5BF;MATHEMATICAL SANS-SERIF SMALL F;Ll;0;L;<font> 0066;;;;N;;;;;
+1D5C0;MATHEMATICAL SANS-SERIF SMALL G;Ll;0;L;<font> 0067;;;;N;;;;;
+1D5C1;MATHEMATICAL SANS-SERIF SMALL H;Ll;0;L;<font> 0068;;;;N;;;;;
+1D5C2;MATHEMATICAL SANS-SERIF SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+1D5C3;MATHEMATICAL SANS-SERIF SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+1D5C4;MATHEMATICAL SANS-SERIF SMALL K;Ll;0;L;<font> 006B;;;;N;;;;;
+1D5C5;MATHEMATICAL SANS-SERIF SMALL L;Ll;0;L;<font> 006C;;;;N;;;;;
+1D5C6;MATHEMATICAL SANS-SERIF SMALL M;Ll;0;L;<font> 006D;;;;N;;;;;
+1D5C7;MATHEMATICAL SANS-SERIF SMALL N;Ll;0;L;<font> 006E;;;;N;;;;;
+1D5C8;MATHEMATICAL SANS-SERIF SMALL O;Ll;0;L;<font> 006F;;;;N;;;;;
+1D5C9;MATHEMATICAL SANS-SERIF SMALL P;Ll;0;L;<font> 0070;;;;N;;;;;
+1D5CA;MATHEMATICAL SANS-SERIF SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;;
+1D5CB;MATHEMATICAL SANS-SERIF SMALL R;Ll;0;L;<font> 0072;;;;N;;;;;
+1D5CC;MATHEMATICAL SANS-SERIF SMALL S;Ll;0;L;<font> 0073;;;;N;;;;;
+1D5CD;MATHEMATICAL SANS-SERIF SMALL T;Ll;0;L;<font> 0074;;;;N;;;;;
+1D5CE;MATHEMATICAL SANS-SERIF SMALL U;Ll;0;L;<font> 0075;;;;N;;;;;
+1D5CF;MATHEMATICAL SANS-SERIF SMALL V;Ll;0;L;<font> 0076;;;;N;;;;;
+1D5D0;MATHEMATICAL SANS-SERIF SMALL W;Ll;0;L;<font> 0077;;;;N;;;;;
+1D5D1;MATHEMATICAL SANS-SERIF SMALL X;Ll;0;L;<font> 0078;;;;N;;;;;
+1D5D2;MATHEMATICAL SANS-SERIF SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;;
+1D5D3;MATHEMATICAL SANS-SERIF SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;;
+1D5D4;MATHEMATICAL SANS-SERIF BOLD CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;;
+1D5D5;MATHEMATICAL SANS-SERIF BOLD CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;;
+1D5D6;MATHEMATICAL SANS-SERIF BOLD CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;;
+1D5D7;MATHEMATICAL SANS-SERIF BOLD CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+1D5D8;MATHEMATICAL SANS-SERIF BOLD CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;;
+1D5D9;MATHEMATICAL SANS-SERIF BOLD CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;;
+1D5DA;MATHEMATICAL SANS-SERIF BOLD CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;;
+1D5DB;MATHEMATICAL SANS-SERIF BOLD CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;;
+1D5DC;MATHEMATICAL SANS-SERIF BOLD CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;;
+1D5DD;MATHEMATICAL SANS-SERIF BOLD CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;;
+1D5DE;MATHEMATICAL SANS-SERIF BOLD CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;;
+1D5DF;MATHEMATICAL SANS-SERIF BOLD CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;;
+1D5E0;MATHEMATICAL SANS-SERIF BOLD CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;;
+1D5E1;MATHEMATICAL SANS-SERIF BOLD CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;;
+1D5E2;MATHEMATICAL SANS-SERIF BOLD CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;;
+1D5E3;MATHEMATICAL SANS-SERIF BOLD CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;;
+1D5E4;MATHEMATICAL SANS-SERIF BOLD CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;;
+1D5E5;MATHEMATICAL SANS-SERIF BOLD CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;;
+1D5E6;MATHEMATICAL SANS-SERIF BOLD CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;;
+1D5E7;MATHEMATICAL SANS-SERIF BOLD CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;;
+1D5E8;MATHEMATICAL SANS-SERIF BOLD CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;;
+1D5E9;MATHEMATICAL SANS-SERIF BOLD CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;;
+1D5EA;MATHEMATICAL SANS-SERIF BOLD CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;;
+1D5EB;MATHEMATICAL SANS-SERIF BOLD CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;;
+1D5EC;MATHEMATICAL SANS-SERIF BOLD CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;;
+1D5ED;MATHEMATICAL SANS-SERIF BOLD CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;;
+1D5EE;MATHEMATICAL SANS-SERIF BOLD SMALL A;Ll;0;L;<font> 0061;;;;N;;;;;
+1D5EF;MATHEMATICAL SANS-SERIF BOLD SMALL B;Ll;0;L;<font> 0062;;;;N;;;;;
+1D5F0;MATHEMATICAL SANS-SERIF BOLD SMALL C;Ll;0;L;<font> 0063;;;;N;;;;;
+1D5F1;MATHEMATICAL SANS-SERIF BOLD SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+1D5F2;MATHEMATICAL SANS-SERIF BOLD SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+1D5F3;MATHEMATICAL SANS-SERIF BOLD SMALL F;Ll;0;L;<font> 0066;;;;N;;;;;
+1D5F4;MATHEMATICAL SANS-SERIF BOLD SMALL G;Ll;0;L;<font> 0067;;;;N;;;;;
+1D5F5;MATHEMATICAL SANS-SERIF BOLD SMALL H;Ll;0;L;<font> 0068;;;;N;;;;;
+1D5F6;MATHEMATICAL SANS-SERIF BOLD SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+1D5F7;MATHEMATICAL SANS-SERIF BOLD SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+1D5F8;MATHEMATICAL SANS-SERIF BOLD SMALL K;Ll;0;L;<font> 006B;;;;N;;;;;
+1D5F9;MATHEMATICAL SANS-SERIF BOLD SMALL L;Ll;0;L;<font> 006C;;;;N;;;;;
+1D5FA;MATHEMATICAL SANS-SERIF BOLD SMALL M;Ll;0;L;<font> 006D;;;;N;;;;;
+1D5FB;MATHEMATICAL SANS-SERIF BOLD SMALL N;Ll;0;L;<font> 006E;;;;N;;;;;
+1D5FC;MATHEMATICAL SANS-SERIF BOLD SMALL O;Ll;0;L;<font> 006F;;;;N;;;;;
+1D5FD;MATHEMATICAL SANS-SERIF BOLD SMALL P;Ll;0;L;<font> 0070;;;;N;;;;;
+1D5FE;MATHEMATICAL SANS-SERIF BOLD SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;;
+1D5FF;MATHEMATICAL SANS-SERIF BOLD SMALL R;Ll;0;L;<font> 0072;;;;N;;;;;
+1D600;MATHEMATICAL SANS-SERIF BOLD SMALL S;Ll;0;L;<font> 0073;;;;N;;;;;
+1D601;MATHEMATICAL SANS-SERIF BOLD SMALL T;Ll;0;L;<font> 0074;;;;N;;;;;
+1D602;MATHEMATICAL SANS-SERIF BOLD SMALL U;Ll;0;L;<font> 0075;;;;N;;;;;
+1D603;MATHEMATICAL SANS-SERIF BOLD SMALL V;Ll;0;L;<font> 0076;;;;N;;;;;
+1D604;MATHEMATICAL SANS-SERIF BOLD SMALL W;Ll;0;L;<font> 0077;;;;N;;;;;
+1D605;MATHEMATICAL SANS-SERIF BOLD SMALL X;Ll;0;L;<font> 0078;;;;N;;;;;
+1D606;MATHEMATICAL SANS-SERIF BOLD SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;;
+1D607;MATHEMATICAL SANS-SERIF BOLD SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;;
+1D608;MATHEMATICAL SANS-SERIF ITALIC CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;;
+1D609;MATHEMATICAL SANS-SERIF ITALIC CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;;
+1D60A;MATHEMATICAL SANS-SERIF ITALIC CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;;
+1D60B;MATHEMATICAL SANS-SERIF ITALIC CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+1D60C;MATHEMATICAL SANS-SERIF ITALIC CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;;
+1D60D;MATHEMATICAL SANS-SERIF ITALIC CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;;
+1D60E;MATHEMATICAL SANS-SERIF ITALIC CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;;
+1D60F;MATHEMATICAL SANS-SERIF ITALIC CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;;
+1D610;MATHEMATICAL SANS-SERIF ITALIC CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;;
+1D611;MATHEMATICAL SANS-SERIF ITALIC CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;;
+1D612;MATHEMATICAL SANS-SERIF ITALIC CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;;
+1D613;MATHEMATICAL SANS-SERIF ITALIC CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;;
+1D614;MATHEMATICAL SANS-SERIF ITALIC CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;;
+1D615;MATHEMATICAL SANS-SERIF ITALIC CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;;
+1D616;MATHEMATICAL SANS-SERIF ITALIC CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;;
+1D617;MATHEMATICAL SANS-SERIF ITALIC CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;;
+1D618;MATHEMATICAL SANS-SERIF ITALIC CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;;
+1D619;MATHEMATICAL SANS-SERIF ITALIC CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;;
+1D61A;MATHEMATICAL SANS-SERIF ITALIC CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;;
+1D61B;MATHEMATICAL SANS-SERIF ITALIC CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;;
+1D61C;MATHEMATICAL SANS-SERIF ITALIC CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;;
+1D61D;MATHEMATICAL SANS-SERIF ITALIC CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;;
+1D61E;MATHEMATICAL SANS-SERIF ITALIC CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;;
+1D61F;MATHEMATICAL SANS-SERIF ITALIC CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;;
+1D620;MATHEMATICAL SANS-SERIF ITALIC CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;;
+1D621;MATHEMATICAL SANS-SERIF ITALIC CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;;
+1D622;MATHEMATICAL SANS-SERIF ITALIC SMALL A;Ll;0;L;<font> 0061;;;;N;;;;;
+1D623;MATHEMATICAL SANS-SERIF ITALIC SMALL B;Ll;0;L;<font> 0062;;;;N;;;;;
+1D624;MATHEMATICAL SANS-SERIF ITALIC SMALL C;Ll;0;L;<font> 0063;;;;N;;;;;
+1D625;MATHEMATICAL SANS-SERIF ITALIC SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+1D626;MATHEMATICAL SANS-SERIF ITALIC SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+1D627;MATHEMATICAL SANS-SERIF ITALIC SMALL F;Ll;0;L;<font> 0066;;;;N;;;;;
+1D628;MATHEMATICAL SANS-SERIF ITALIC SMALL G;Ll;0;L;<font> 0067;;;;N;;;;;
+1D629;MATHEMATICAL SANS-SERIF ITALIC SMALL H;Ll;0;L;<font> 0068;;;;N;;;;;
+1D62A;MATHEMATICAL SANS-SERIF ITALIC SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+1D62B;MATHEMATICAL SANS-SERIF ITALIC SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+1D62C;MATHEMATICAL SANS-SERIF ITALIC SMALL K;Ll;0;L;<font> 006B;;;;N;;;;;
+1D62D;MATHEMATICAL SANS-SERIF ITALIC SMALL L;Ll;0;L;<font> 006C;;;;N;;;;;
+1D62E;MATHEMATICAL SANS-SERIF ITALIC SMALL M;Ll;0;L;<font> 006D;;;;N;;;;;
+1D62F;MATHEMATICAL SANS-SERIF ITALIC SMALL N;Ll;0;L;<font> 006E;;;;N;;;;;
+1D630;MATHEMATICAL SANS-SERIF ITALIC SMALL O;Ll;0;L;<font> 006F;;;;N;;;;;
+1D631;MATHEMATICAL SANS-SERIF ITALIC SMALL P;Ll;0;L;<font> 0070;;;;N;;;;;
+1D632;MATHEMATICAL SANS-SERIF ITALIC SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;;
+1D633;MATHEMATICAL SANS-SERIF ITALIC SMALL R;Ll;0;L;<font> 0072;;;;N;;;;;
+1D634;MATHEMATICAL SANS-SERIF ITALIC SMALL S;Ll;0;L;<font> 0073;;;;N;;;;;
+1D635;MATHEMATICAL SANS-SERIF ITALIC SMALL T;Ll;0;L;<font> 0074;;;;N;;;;;
+1D636;MATHEMATICAL SANS-SERIF ITALIC SMALL U;Ll;0;L;<font> 0075;;;;N;;;;;
+1D637;MATHEMATICAL SANS-SERIF ITALIC SMALL V;Ll;0;L;<font> 0076;;;;N;;;;;
+1D638;MATHEMATICAL SANS-SERIF ITALIC SMALL W;Ll;0;L;<font> 0077;;;;N;;;;;
+1D639;MATHEMATICAL SANS-SERIF ITALIC SMALL X;Ll;0;L;<font> 0078;;;;N;;;;;
+1D63A;MATHEMATICAL SANS-SERIF ITALIC SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;;
+1D63B;MATHEMATICAL SANS-SERIF ITALIC SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;;
+1D63C;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;;
+1D63D;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;;
+1D63E;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;;
+1D63F;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+1D640;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;;
+1D641;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;;
+1D642;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;;
+1D643;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;;
+1D644;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;;
+1D645;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;;
+1D646;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;;
+1D647;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;;
+1D648;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;;
+1D649;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;;
+1D64A;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;;
+1D64B;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;;
+1D64C;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;;
+1D64D;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;;
+1D64E;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;;
+1D64F;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;;
+1D650;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;;
+1D651;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;;
+1D652;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;;
+1D653;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;;
+1D654;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;;
+1D655;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;;
+1D656;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL A;Ll;0;L;<font> 0061;;;;N;;;;;
+1D657;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL B;Ll;0;L;<font> 0062;;;;N;;;;;
+1D658;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL C;Ll;0;L;<font> 0063;;;;N;;;;;
+1D659;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+1D65A;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+1D65B;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL F;Ll;0;L;<font> 0066;;;;N;;;;;
+1D65C;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL G;Ll;0;L;<font> 0067;;;;N;;;;;
+1D65D;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL H;Ll;0;L;<font> 0068;;;;N;;;;;
+1D65E;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+1D65F;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+1D660;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL K;Ll;0;L;<font> 006B;;;;N;;;;;
+1D661;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL L;Ll;0;L;<font> 006C;;;;N;;;;;
+1D662;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL M;Ll;0;L;<font> 006D;;;;N;;;;;
+1D663;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL N;Ll;0;L;<font> 006E;;;;N;;;;;
+1D664;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL O;Ll;0;L;<font> 006F;;;;N;;;;;
+1D665;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL P;Ll;0;L;<font> 0070;;;;N;;;;;
+1D666;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;;
+1D667;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL R;Ll;0;L;<font> 0072;;;;N;;;;;
+1D668;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL S;Ll;0;L;<font> 0073;;;;N;;;;;
+1D669;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL T;Ll;0;L;<font> 0074;;;;N;;;;;
+1D66A;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL U;Ll;0;L;<font> 0075;;;;N;;;;;
+1D66B;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL V;Ll;0;L;<font> 0076;;;;N;;;;;
+1D66C;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL W;Ll;0;L;<font> 0077;;;;N;;;;;
+1D66D;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL X;Ll;0;L;<font> 0078;;;;N;;;;;
+1D66E;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;;
+1D66F;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;;
+1D670;MATHEMATICAL MONOSPACE CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;;
+1D671;MATHEMATICAL MONOSPACE CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;;
+1D672;MATHEMATICAL MONOSPACE CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;;
+1D673;MATHEMATICAL MONOSPACE CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+1D674;MATHEMATICAL MONOSPACE CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;;
+1D675;MATHEMATICAL MONOSPACE CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;;
+1D676;MATHEMATICAL MONOSPACE CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;;
+1D677;MATHEMATICAL MONOSPACE CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;;
+1D678;MATHEMATICAL MONOSPACE CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;;
+1D679;MATHEMATICAL MONOSPACE CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;;
+1D67A;MATHEMATICAL MONOSPACE CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;;
+1D67B;MATHEMATICAL MONOSPACE CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;;
+1D67C;MATHEMATICAL MONOSPACE CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;;
+1D67D;MATHEMATICAL MONOSPACE CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;;
+1D67E;MATHEMATICAL MONOSPACE CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;;
+1D67F;MATHEMATICAL MONOSPACE CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;;
+1D680;MATHEMATICAL MONOSPACE CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;;
+1D681;MATHEMATICAL MONOSPACE CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;;
+1D682;MATHEMATICAL MONOSPACE CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;;
+1D683;MATHEMATICAL MONOSPACE CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;;
+1D684;MATHEMATICAL MONOSPACE CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;;
+1D685;MATHEMATICAL MONOSPACE CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;;
+1D686;MATHEMATICAL MONOSPACE CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;;
+1D687;MATHEMATICAL MONOSPACE CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;;
+1D688;MATHEMATICAL MONOSPACE CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;;
+1D689;MATHEMATICAL MONOSPACE CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;;
+1D68A;MATHEMATICAL MONOSPACE SMALL A;Ll;0;L;<font> 0061;;;;N;;;;;
+1D68B;MATHEMATICAL MONOSPACE SMALL B;Ll;0;L;<font> 0062;;;;N;;;;;
+1D68C;MATHEMATICAL MONOSPACE SMALL C;Ll;0;L;<font> 0063;;;;N;;;;;
+1D68D;MATHEMATICAL MONOSPACE SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+1D68E;MATHEMATICAL MONOSPACE SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+1D68F;MATHEMATICAL MONOSPACE SMALL F;Ll;0;L;<font> 0066;;;;N;;;;;
+1D690;MATHEMATICAL MONOSPACE SMALL G;Ll;0;L;<font> 0067;;;;N;;;;;
+1D691;MATHEMATICAL MONOSPACE SMALL H;Ll;0;L;<font> 0068;;;;N;;;;;
+1D692;MATHEMATICAL MONOSPACE SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+1D693;MATHEMATICAL MONOSPACE SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+1D694;MATHEMATICAL MONOSPACE SMALL K;Ll;0;L;<font> 006B;;;;N;;;;;
+1D695;MATHEMATICAL MONOSPACE SMALL L;Ll;0;L;<font> 006C;;;;N;;;;;
+1D696;MATHEMATICAL MONOSPACE SMALL M;Ll;0;L;<font> 006D;;;;N;;;;;
+1D697;MATHEMATICAL MONOSPACE SMALL N;Ll;0;L;<font> 006E;;;;N;;;;;
+1D698;MATHEMATICAL MONOSPACE SMALL O;Ll;0;L;<font> 006F;;;;N;;;;;
+1D699;MATHEMATICAL MONOSPACE SMALL P;Ll;0;L;<font> 0070;;;;N;;;;;
+1D69A;MATHEMATICAL MONOSPACE SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;;
+1D69B;MATHEMATICAL MONOSPACE SMALL R;Ll;0;L;<font> 0072;;;;N;;;;;
+1D69C;MATHEMATICAL MONOSPACE SMALL S;Ll;0;L;<font> 0073;;;;N;;;;;
+1D69D;MATHEMATICAL MONOSPACE SMALL T;Ll;0;L;<font> 0074;;;;N;;;;;
+1D69E;MATHEMATICAL MONOSPACE SMALL U;Ll;0;L;<font> 0075;;;;N;;;;;
+1D69F;MATHEMATICAL MONOSPACE SMALL V;Ll;0;L;<font> 0076;;;;N;;;;;
+1D6A0;MATHEMATICAL MONOSPACE SMALL W;Ll;0;L;<font> 0077;;;;N;;;;;
+1D6A1;MATHEMATICAL MONOSPACE SMALL X;Ll;0;L;<font> 0078;;;;N;;;;;
+1D6A2;MATHEMATICAL MONOSPACE SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;;
+1D6A3;MATHEMATICAL MONOSPACE SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;;
+1D6A8;MATHEMATICAL BOLD CAPITAL ALPHA;Lu;0;L;<font> 0391;;;;N;;;;;
+1D6A9;MATHEMATICAL BOLD CAPITAL BETA;Lu;0;L;<font> 0392;;;;N;;;;;
+1D6AA;MATHEMATICAL BOLD CAPITAL GAMMA;Lu;0;L;<font> 0393;;;;N;;;;;
+1D6AB;MATHEMATICAL BOLD CAPITAL DELTA;Lu;0;L;<font> 0394;;;;N;;;;;
+1D6AC;MATHEMATICAL BOLD CAPITAL EPSILON;Lu;0;L;<font> 0395;;;;N;;;;;
+1D6AD;MATHEMATICAL BOLD CAPITAL ZETA;Lu;0;L;<font> 0396;;;;N;;;;;
+1D6AE;MATHEMATICAL BOLD CAPITAL ETA;Lu;0;L;<font> 0397;;;;N;;;;;
+1D6AF;MATHEMATICAL BOLD CAPITAL THETA;Lu;0;L;<font> 0398;;;;N;;;;;
+1D6B0;MATHEMATICAL BOLD CAPITAL IOTA;Lu;0;L;<font> 0399;;;;N;;;;;
+1D6B1;MATHEMATICAL BOLD CAPITAL KAPPA;Lu;0;L;<font> 039A;;;;N;;;;;
+1D6B2;MATHEMATICAL BOLD CAPITAL LAMDA;Lu;0;L;<font> 039B;;;;N;;;;;
+1D6B3;MATHEMATICAL BOLD CAPITAL MU;Lu;0;L;<font> 039C;;;;N;;;;;
+1D6B4;MATHEMATICAL BOLD CAPITAL NU;Lu;0;L;<font> 039D;;;;N;;;;;
+1D6B5;MATHEMATICAL BOLD CAPITAL XI;Lu;0;L;<font> 039E;;;;N;;;;;
+1D6B6;MATHEMATICAL BOLD CAPITAL OMICRON;Lu;0;L;<font> 039F;;;;N;;;;;
+1D6B7;MATHEMATICAL BOLD CAPITAL PI;Lu;0;L;<font> 03A0;;;;N;;;;;
+1D6B8;MATHEMATICAL BOLD CAPITAL RHO;Lu;0;L;<font> 03A1;;;;N;;;;;
+1D6B9;MATHEMATICAL BOLD CAPITAL THETA SYMBOL;Lu;0;L;<font> 03F4;;;;N;;;;;
+1D6BA;MATHEMATICAL BOLD CAPITAL SIGMA;Lu;0;L;<font> 03A3;;;;N;;;;;
+1D6BB;MATHEMATICAL BOLD CAPITAL TAU;Lu;0;L;<font> 03A4;;;;N;;;;;
+1D6BC;MATHEMATICAL BOLD CAPITAL UPSILON;Lu;0;L;<font> 03A5;;;;N;;;;;
+1D6BD;MATHEMATICAL BOLD CAPITAL PHI;Lu;0;L;<font> 03A6;;;;N;;;;;
+1D6BE;MATHEMATICAL BOLD CAPITAL CHI;Lu;0;L;<font> 03A7;;;;N;;;;;
+1D6BF;MATHEMATICAL BOLD CAPITAL PSI;Lu;0;L;<font> 03A8;;;;N;;;;;
+1D6C0;MATHEMATICAL BOLD CAPITAL OMEGA;Lu;0;L;<font> 03A9;;;;N;;;;;
+1D6C1;MATHEMATICAL BOLD NABLA;Sm;0;L;<font> 2207;;;;N;;;;;
+1D6C2;MATHEMATICAL BOLD SMALL ALPHA;Ll;0;L;<font> 03B1;;;;N;;;;;
+1D6C3;MATHEMATICAL BOLD SMALL BETA;Ll;0;L;<font> 03B2;;;;N;;;;;
+1D6C4;MATHEMATICAL BOLD SMALL GAMMA;Ll;0;L;<font> 03B3;;;;N;;;;;
+1D6C5;MATHEMATICAL BOLD SMALL DELTA;Ll;0;L;<font> 03B4;;;;N;;;;;
+1D6C6;MATHEMATICAL BOLD SMALL EPSILON;Ll;0;L;<font> 03B5;;;;N;;;;;
+1D6C7;MATHEMATICAL BOLD SMALL ZETA;Ll;0;L;<font> 03B6;;;;N;;;;;
+1D6C8;MATHEMATICAL BOLD SMALL ETA;Ll;0;L;<font> 03B7;;;;N;;;;;
+1D6C9;MATHEMATICAL BOLD SMALL THETA;Ll;0;L;<font> 03B8;;;;N;;;;;
+1D6CA;MATHEMATICAL BOLD SMALL IOTA;Ll;0;L;<font> 03B9;;;;N;;;;;
+1D6CB;MATHEMATICAL BOLD SMALL KAPPA;Ll;0;L;<font> 03BA;;;;N;;;;;
+1D6CC;MATHEMATICAL BOLD SMALL LAMDA;Ll;0;L;<font> 03BB;;;;N;;;;;
+1D6CD;MATHEMATICAL BOLD SMALL MU;Ll;0;L;<font> 03BC;;;;N;;;;;
+1D6CE;MATHEMATICAL BOLD SMALL NU;Ll;0;L;<font> 03BD;;;;N;;;;;
+1D6CF;MATHEMATICAL BOLD SMALL XI;Ll;0;L;<font> 03BE;;;;N;;;;;
+1D6D0;MATHEMATICAL BOLD SMALL OMICRON;Ll;0;L;<font> 03BF;;;;N;;;;;
+1D6D1;MATHEMATICAL BOLD SMALL PI;Ll;0;L;<font> 03C0;;;;N;;;;;
+1D6D2;MATHEMATICAL BOLD SMALL RHO;Ll;0;L;<font> 03C1;;;;N;;;;;
+1D6D3;MATHEMATICAL BOLD SMALL FINAL SIGMA;Ll;0;L;<font> 03C2;;;;N;;;;;
+1D6D4;MATHEMATICAL BOLD SMALL SIGMA;Ll;0;L;<font> 03C3;;;;N;;;;;
+1D6D5;MATHEMATICAL BOLD SMALL TAU;Ll;0;L;<font> 03C4;;;;N;;;;;
+1D6D6;MATHEMATICAL BOLD SMALL UPSILON;Ll;0;L;<font> 03C5;;;;N;;;;;
+1D6D7;MATHEMATICAL BOLD SMALL PHI;Ll;0;L;<font> 03C6;;;;N;;;;;
+1D6D8;MATHEMATICAL BOLD SMALL CHI;Ll;0;L;<font> 03C7;;;;N;;;;;
+1D6D9;MATHEMATICAL BOLD SMALL PSI;Ll;0;L;<font> 03C8;;;;N;;;;;
+1D6DA;MATHEMATICAL BOLD SMALL OMEGA;Ll;0;L;<font> 03C9;;;;N;;;;;
+1D6DB;MATHEMATICAL BOLD PARTIAL DIFFERENTIAL;Sm;0;L;<font> 2202;;;;N;;;;;
+1D6DC;MATHEMATICAL BOLD EPSILON SYMBOL;Ll;0;L;<font> 03F5;;;;N;;;;;
+1D6DD;MATHEMATICAL BOLD THETA SYMBOL;Ll;0;L;<font> 03D1;;;;N;;;;;
+1D6DE;MATHEMATICAL BOLD KAPPA SYMBOL;Ll;0;L;<font> 03F0;;;;N;;;;;
+1D6DF;MATHEMATICAL BOLD PHI SYMBOL;Ll;0;L;<font> 03D5;;;;N;;;;;
+1D6E0;MATHEMATICAL BOLD RHO SYMBOL;Ll;0;L;<font> 03F1;;;;N;;;;;
+1D6E1;MATHEMATICAL BOLD PI SYMBOL;Ll;0;L;<font> 03D6;;;;N;;;;;
+1D6E2;MATHEMATICAL ITALIC CAPITAL ALPHA;Lu;0;L;<font> 0391;;;;N;;;;;
+1D6E3;MATHEMATICAL ITALIC CAPITAL BETA;Lu;0;L;<font> 0392;;;;N;;;;;
+1D6E4;MATHEMATICAL ITALIC CAPITAL GAMMA;Lu;0;L;<font> 0393;;;;N;;;;;
+1D6E5;MATHEMATICAL ITALIC CAPITAL DELTA;Lu;0;L;<font> 0394;;;;N;;;;;
+1D6E6;MATHEMATICAL ITALIC CAPITAL EPSILON;Lu;0;L;<font> 0395;;;;N;;;;;
+1D6E7;MATHEMATICAL ITALIC CAPITAL ZETA;Lu;0;L;<font> 0396;;;;N;;;;;
+1D6E8;MATHEMATICAL ITALIC CAPITAL ETA;Lu;0;L;<font> 0397;;;;N;;;;;
+1D6E9;MATHEMATICAL ITALIC CAPITAL THETA;Lu;0;L;<font> 0398;;;;N;;;;;
+1D6EA;MATHEMATICAL ITALIC CAPITAL IOTA;Lu;0;L;<font> 0399;;;;N;;;;;
+1D6EB;MATHEMATICAL ITALIC CAPITAL KAPPA;Lu;0;L;<font> 039A;;;;N;;;;;
+1D6EC;MATHEMATICAL ITALIC CAPITAL LAMDA;Lu;0;L;<font> 039B;;;;N;;;;;
+1D6ED;MATHEMATICAL ITALIC CAPITAL MU;Lu;0;L;<font> 039C;;;;N;;;;;
+1D6EE;MATHEMATICAL ITALIC CAPITAL NU;Lu;0;L;<font> 039D;;;;N;;;;;
+1D6EF;MATHEMATICAL ITALIC CAPITAL XI;Lu;0;L;<font> 039E;;;;N;;;;;
+1D6F0;MATHEMATICAL ITALIC CAPITAL OMICRON;Lu;0;L;<font> 039F;;;;N;;;;;
+1D6F1;MATHEMATICAL ITALIC CAPITAL PI;Lu;0;L;<font> 03A0;;;;N;;;;;
+1D6F2;MATHEMATICAL ITALIC CAPITAL RHO;Lu;0;L;<font> 03A1;;;;N;;;;;
+1D6F3;MATHEMATICAL ITALIC CAPITAL THETA SYMBOL;Lu;0;L;<font> 03F4;;;;N;;;;;
+1D6F4;MATHEMATICAL ITALIC CAPITAL SIGMA;Lu;0;L;<font> 03A3;;;;N;;;;;
+1D6F5;MATHEMATICAL ITALIC CAPITAL TAU;Lu;0;L;<font> 03A4;;;;N;;;;;
+1D6F6;MATHEMATICAL ITALIC CAPITAL UPSILON;Lu;0;L;<font> 03A5;;;;N;;;;;
+1D6F7;MATHEMATICAL ITALIC CAPITAL PHI;Lu;0;L;<font> 03A6;;;;N;;;;;
+1D6F8;MATHEMATICAL ITALIC CAPITAL CHI;Lu;0;L;<font> 03A7;;;;N;;;;;
+1D6F9;MATHEMATICAL ITALIC CAPITAL PSI;Lu;0;L;<font> 03A8;;;;N;;;;;
+1D6FA;MATHEMATICAL ITALIC CAPITAL OMEGA;Lu;0;L;<font> 03A9;;;;N;;;;;
+1D6FB;MATHEMATICAL ITALIC NABLA;Sm;0;L;<font> 2207;;;;N;;;;;
+1D6FC;MATHEMATICAL ITALIC SMALL ALPHA;Ll;0;L;<font> 03B1;;;;N;;;;;
+1D6FD;MATHEMATICAL ITALIC SMALL BETA;Ll;0;L;<font> 03B2;;;;N;;;;;
+1D6FE;MATHEMATICAL ITALIC SMALL GAMMA;Ll;0;L;<font> 03B3;;;;N;;;;;
+1D6FF;MATHEMATICAL ITALIC SMALL DELTA;Ll;0;L;<font> 03B4;;;;N;;;;;
+1D700;MATHEMATICAL ITALIC SMALL EPSILON;Ll;0;L;<font> 03B5;;;;N;;;;;
+1D701;MATHEMATICAL ITALIC SMALL ZETA;Ll;0;L;<font> 03B6;;;;N;;;;;
+1D702;MATHEMATICAL ITALIC SMALL ETA;Ll;0;L;<font> 03B7;;;;N;;;;;
+1D703;MATHEMATICAL ITALIC SMALL THETA;Ll;0;L;<font> 03B8;;;;N;;;;;
+1D704;MATHEMATICAL ITALIC SMALL IOTA;Ll;0;L;<font> 03B9;;;;N;;;;;
+1D705;MATHEMATICAL ITALIC SMALL KAPPA;Ll;0;L;<font> 03BA;;;;N;;;;;
+1D706;MATHEMATICAL ITALIC SMALL LAMDA;Ll;0;L;<font> 03BB;;;;N;;;;;
+1D707;MATHEMATICAL ITALIC SMALL MU;Ll;0;L;<font> 03BC;;;;N;;;;;
+1D708;MATHEMATICAL ITALIC SMALL NU;Ll;0;L;<font> 03BD;;;;N;;;;;
+1D709;MATHEMATICAL ITALIC SMALL XI;Ll;0;L;<font> 03BE;;;;N;;;;;
+1D70A;MATHEMATICAL ITALIC SMALL OMICRON;Ll;0;L;<font> 03BF;;;;N;;;;;
+1D70B;MATHEMATICAL ITALIC SMALL PI;Ll;0;L;<font> 03C0;;;;N;;;;;
+1D70C;MATHEMATICAL ITALIC SMALL RHO;Ll;0;L;<font> 03C1;;;;N;;;;;
+1D70D;MATHEMATICAL ITALIC SMALL FINAL SIGMA;Ll;0;L;<font> 03C2;;;;N;;;;;
+1D70E;MATHEMATICAL ITALIC SMALL SIGMA;Ll;0;L;<font> 03C3;;;;N;;;;;
+1D70F;MATHEMATICAL ITALIC SMALL TAU;Ll;0;L;<font> 03C4;;;;N;;;;;
+1D710;MATHEMATICAL ITALIC SMALL UPSILON;Ll;0;L;<font> 03C5;;;;N;;;;;
+1D711;MATHEMATICAL ITALIC SMALL PHI;Ll;0;L;<font> 03C6;;;;N;;;;;
+1D712;MATHEMATICAL ITALIC SMALL CHI;Ll;0;L;<font> 03C7;;;;N;;;;;
+1D713;MATHEMATICAL ITALIC SMALL PSI;Ll;0;L;<font> 03C8;;;;N;;;;;
+1D714;MATHEMATICAL ITALIC SMALL OMEGA;Ll;0;L;<font> 03C9;;;;N;;;;;
+1D715;MATHEMATICAL ITALIC PARTIAL DIFFERENTIAL;Sm;0;L;<font> 2202;;;;N;;;;;
+1D716;MATHEMATICAL ITALIC EPSILON SYMBOL;Ll;0;L;<font> 03F5;;;;N;;;;;
+1D717;MATHEMATICAL ITALIC THETA SYMBOL;Ll;0;L;<font> 03D1;;;;N;;;;;
+1D718;MATHEMATICAL ITALIC KAPPA SYMBOL;Ll;0;L;<font> 03F0;;;;N;;;;;
+1D719;MATHEMATICAL ITALIC PHI SYMBOL;Ll;0;L;<font> 03D5;;;;N;;;;;
+1D71A;MATHEMATICAL ITALIC RHO SYMBOL;Ll;0;L;<font> 03F1;;;;N;;;;;
+1D71B;MATHEMATICAL ITALIC PI SYMBOL;Ll;0;L;<font> 03D6;;;;N;;;;;
+1D71C;MATHEMATICAL BOLD ITALIC CAPITAL ALPHA;Lu;0;L;<font> 0391;;;;N;;;;;
+1D71D;MATHEMATICAL BOLD ITALIC CAPITAL BETA;Lu;0;L;<font> 0392;;;;N;;;;;
+1D71E;MATHEMATICAL BOLD ITALIC CAPITAL GAMMA;Lu;0;L;<font> 0393;;;;N;;;;;
+1D71F;MATHEMATICAL BOLD ITALIC CAPITAL DELTA;Lu;0;L;<font> 0394;;;;N;;;;;
+1D720;MATHEMATICAL BOLD ITALIC CAPITAL EPSILON;Lu;0;L;<font> 0395;;;;N;;;;;
+1D721;MATHEMATICAL BOLD ITALIC CAPITAL ZETA;Lu;0;L;<font> 0396;;;;N;;;;;
+1D722;MATHEMATICAL BOLD ITALIC CAPITAL ETA;Lu;0;L;<font> 0397;;;;N;;;;;
+1D723;MATHEMATICAL BOLD ITALIC CAPITAL THETA;Lu;0;L;<font> 0398;;;;N;;;;;
+1D724;MATHEMATICAL BOLD ITALIC CAPITAL IOTA;Lu;0;L;<font> 0399;;;;N;;;;;
+1D725;MATHEMATICAL BOLD ITALIC CAPITAL KAPPA;Lu;0;L;<font> 039A;;;;N;;;;;
+1D726;MATHEMATICAL BOLD ITALIC CAPITAL LAMDA;Lu;0;L;<font> 039B;;;;N;;;;;
+1D727;MATHEMATICAL BOLD ITALIC CAPITAL MU;Lu;0;L;<font> 039C;;;;N;;;;;
+1D728;MATHEMATICAL BOLD ITALIC CAPITAL NU;Lu;0;L;<font> 039D;;;;N;;;;;
+1D729;MATHEMATICAL BOLD ITALIC CAPITAL XI;Lu;0;L;<font> 039E;;;;N;;;;;
+1D72A;MATHEMATICAL BOLD ITALIC CAPITAL OMICRON;Lu;0;L;<font> 039F;;;;N;;;;;
+1D72B;MATHEMATICAL BOLD ITALIC CAPITAL PI;Lu;0;L;<font> 03A0;;;;N;;;;;
+1D72C;MATHEMATICAL BOLD ITALIC CAPITAL RHO;Lu;0;L;<font> 03A1;;;;N;;;;;
+1D72D;MATHEMATICAL BOLD ITALIC CAPITAL THETA SYMBOL;Lu;0;L;<font> 03F4;;;;N;;;;;
+1D72E;MATHEMATICAL BOLD ITALIC CAPITAL SIGMA;Lu;0;L;<font> 03A3;;;;N;;;;;
+1D72F;MATHEMATICAL BOLD ITALIC CAPITAL TAU;Lu;0;L;<font> 03A4;;;;N;;;;;
+1D730;MATHEMATICAL BOLD ITALIC CAPITAL UPSILON;Lu;0;L;<font> 03A5;;;;N;;;;;
+1D731;MATHEMATICAL BOLD ITALIC CAPITAL PHI;Lu;0;L;<font> 03A6;;;;N;;;;;
+1D732;MATHEMATICAL BOLD ITALIC CAPITAL CHI;Lu;0;L;<font> 03A7;;;;N;;;;;
+1D733;MATHEMATICAL BOLD ITALIC CAPITAL PSI;Lu;0;L;<font> 03A8;;;;N;;;;;
+1D734;MATHEMATICAL BOLD ITALIC CAPITAL OMEGA;Lu;0;L;<font> 03A9;;;;N;;;;;
+1D735;MATHEMATICAL BOLD ITALIC NABLA;Sm;0;L;<font> 2207;;;;N;;;;;
+1D736;MATHEMATICAL BOLD ITALIC SMALL ALPHA;Ll;0;L;<font> 03B1;;;;N;;;;;
+1D737;MATHEMATICAL BOLD ITALIC SMALL BETA;Ll;0;L;<font> 03B2;;;;N;;;;;
+1D738;MATHEMATICAL BOLD ITALIC SMALL GAMMA;Ll;0;L;<font> 03B3;;;;N;;;;;
+1D739;MATHEMATICAL BOLD ITALIC SMALL DELTA;Ll;0;L;<font> 03B4;;;;N;;;;;
+1D73A;MATHEMATICAL BOLD ITALIC SMALL EPSILON;Ll;0;L;<font> 03B5;;;;N;;;;;
+1D73B;MATHEMATICAL BOLD ITALIC SMALL ZETA;Ll;0;L;<font> 03B6;;;;N;;;;;
+1D73C;MATHEMATICAL BOLD ITALIC SMALL ETA;Ll;0;L;<font> 03B7;;;;N;;;;;
+1D73D;MATHEMATICAL BOLD ITALIC SMALL THETA;Ll;0;L;<font> 03B8;;;;N;;;;;
+1D73E;MATHEMATICAL BOLD ITALIC SMALL IOTA;Ll;0;L;<font> 03B9;;;;N;;;;;
+1D73F;MATHEMATICAL BOLD ITALIC SMALL KAPPA;Ll;0;L;<font> 03BA;;;;N;;;;;
+1D740;MATHEMATICAL BOLD ITALIC SMALL LAMDA;Ll;0;L;<font> 03BB;;;;N;;;;;
+1D741;MATHEMATICAL BOLD ITALIC SMALL MU;Ll;0;L;<font> 03BC;;;;N;;;;;
+1D742;MATHEMATICAL BOLD ITALIC SMALL NU;Ll;0;L;<font> 03BD;;;;N;;;;;
+1D743;MATHEMATICAL BOLD ITALIC SMALL XI;Ll;0;L;<font> 03BE;;;;N;;;;;
+1D744;MATHEMATICAL BOLD ITALIC SMALL OMICRON;Ll;0;L;<font> 03BF;;;;N;;;;;
+1D745;MATHEMATICAL BOLD ITALIC SMALL PI;Ll;0;L;<font> 03C0;;;;N;;;;;
+1D746;MATHEMATICAL BOLD ITALIC SMALL RHO;Ll;0;L;<font> 03C1;;;;N;;;;;
+1D747;MATHEMATICAL BOLD ITALIC SMALL FINAL SIGMA;Ll;0;L;<font> 03C2;;;;N;;;;;
+1D748;MATHEMATICAL BOLD ITALIC SMALL SIGMA;Ll;0;L;<font> 03C3;;;;N;;;;;
+1D749;MATHEMATICAL BOLD ITALIC SMALL TAU;Ll;0;L;<font> 03C4;;;;N;;;;;
+1D74A;MATHEMATICAL BOLD ITALIC SMALL UPSILON;Ll;0;L;<font> 03C5;;;;N;;;;;
+1D74B;MATHEMATICAL BOLD ITALIC SMALL PHI;Ll;0;L;<font> 03C6;;;;N;;;;;
+1D74C;MATHEMATICAL BOLD ITALIC SMALL CHI;Ll;0;L;<font> 03C7;;;;N;;;;;
+1D74D;MATHEMATICAL BOLD ITALIC SMALL PSI;Ll;0;L;<font> 03C8;;;;N;;;;;
+1D74E;MATHEMATICAL BOLD ITALIC SMALL OMEGA;Ll;0;L;<font> 03C9;;;;N;;;;;
+1D74F;MATHEMATICAL BOLD ITALIC PARTIAL DIFFERENTIAL;Sm;0;L;<font> 2202;;;;N;;;;;
+1D750;MATHEMATICAL BOLD ITALIC EPSILON SYMBOL;Ll;0;L;<font> 03F5;;;;N;;;;;
+1D751;MATHEMATICAL BOLD ITALIC THETA SYMBOL;Ll;0;L;<font> 03D1;;;;N;;;;;
+1D752;MATHEMATICAL BOLD ITALIC KAPPA SYMBOL;Ll;0;L;<font> 03F0;;;;N;;;;;
+1D753;MATHEMATICAL BOLD ITALIC PHI SYMBOL;Ll;0;L;<font> 03D5;;;;N;;;;;
+1D754;MATHEMATICAL BOLD ITALIC RHO SYMBOL;Ll;0;L;<font> 03F1;;;;N;;;;;
+1D755;MATHEMATICAL BOLD ITALIC PI SYMBOL;Ll;0;L;<font> 03D6;;;;N;;;;;
+1D756;MATHEMATICAL SANS-SERIF BOLD CAPITAL ALPHA;Lu;0;L;<font> 0391;;;;N;;;;;
+1D757;MATHEMATICAL SANS-SERIF BOLD CAPITAL BETA;Lu;0;L;<font> 0392;;;;N;;;;;
+1D758;MATHEMATICAL SANS-SERIF BOLD CAPITAL GAMMA;Lu;0;L;<font> 0393;;;;N;;;;;
+1D759;MATHEMATICAL SANS-SERIF BOLD CAPITAL DELTA;Lu;0;L;<font> 0394;;;;N;;;;;
+1D75A;MATHEMATICAL SANS-SERIF BOLD CAPITAL EPSILON;Lu;0;L;<font> 0395;;;;N;;;;;
+1D75B;MATHEMATICAL SANS-SERIF BOLD CAPITAL ZETA;Lu;0;L;<font> 0396;;;;N;;;;;
+1D75C;MATHEMATICAL SANS-SERIF BOLD CAPITAL ETA;Lu;0;L;<font> 0397;;;;N;;;;;
+1D75D;MATHEMATICAL SANS-SERIF BOLD CAPITAL THETA;Lu;0;L;<font> 0398;;;;N;;;;;
+1D75E;MATHEMATICAL SANS-SERIF BOLD CAPITAL IOTA;Lu;0;L;<font> 0399;;;;N;;;;;
+1D75F;MATHEMATICAL SANS-SERIF BOLD CAPITAL KAPPA;Lu;0;L;<font> 039A;;;;N;;;;;
+1D760;MATHEMATICAL SANS-SERIF BOLD CAPITAL LAMDA;Lu;0;L;<font> 039B;;;;N;;;;;
+1D761;MATHEMATICAL SANS-SERIF BOLD CAPITAL MU;Lu;0;L;<font> 039C;;;;N;;;;;
+1D762;MATHEMATICAL SANS-SERIF BOLD CAPITAL NU;Lu;0;L;<font> 039D;;;;N;;;;;
+1D763;MATHEMATICAL SANS-SERIF BOLD CAPITAL XI;Lu;0;L;<font> 039E;;;;N;;;;;
+1D764;MATHEMATICAL SANS-SERIF BOLD CAPITAL OMICRON;Lu;0;L;<font> 039F;;;;N;;;;;
+1D765;MATHEMATICAL SANS-SERIF BOLD CAPITAL PI;Lu;0;L;<font> 03A0;;;;N;;;;;
+1D766;MATHEMATICAL SANS-SERIF BOLD CAPITAL RHO;Lu;0;L;<font> 03A1;;;;N;;;;;
+1D767;MATHEMATICAL SANS-SERIF BOLD CAPITAL THETA SYMBOL;Lu;0;L;<font> 03F4;;;;N;;;;;
+1D768;MATHEMATICAL SANS-SERIF BOLD CAPITAL SIGMA;Lu;0;L;<font> 03A3;;;;N;;;;;
+1D769;MATHEMATICAL SANS-SERIF BOLD CAPITAL TAU;Lu;0;L;<font> 03A4;;;;N;;;;;
+1D76A;MATHEMATICAL SANS-SERIF BOLD CAPITAL UPSILON;Lu;0;L;<font> 03A5;;;;N;;;;;
+1D76B;MATHEMATICAL SANS-SERIF BOLD CAPITAL PHI;Lu;0;L;<font> 03A6;;;;N;;;;;
+1D76C;MATHEMATICAL SANS-SERIF BOLD CAPITAL CHI;Lu;0;L;<font> 03A7;;;;N;;;;;
+1D76D;MATHEMATICAL SANS-SERIF BOLD CAPITAL PSI;Lu;0;L;<font> 03A8;;;;N;;;;;
+1D76E;MATHEMATICAL SANS-SERIF BOLD CAPITAL OMEGA;Lu;0;L;<font> 03A9;;;;N;;;;;
+1D76F;MATHEMATICAL SANS-SERIF BOLD NABLA;Sm;0;L;<font> 2207;;;;N;;;;;
+1D770;MATHEMATICAL SANS-SERIF BOLD SMALL ALPHA;Ll;0;L;<font> 03B1;;;;N;;;;;
+1D771;MATHEMATICAL SANS-SERIF BOLD SMALL BETA;Ll;0;L;<font> 03B2;;;;N;;;;;
+1D772;MATHEMATICAL SANS-SERIF BOLD SMALL GAMMA;Ll;0;L;<font> 03B3;;;;N;;;;;
+1D773;MATHEMATICAL SANS-SERIF BOLD SMALL DELTA;Ll;0;L;<font> 03B4;;;;N;;;;;
+1D774;MATHEMATICAL SANS-SERIF BOLD SMALL EPSILON;Ll;0;L;<font> 03B5;;;;N;;;;;
+1D775;MATHEMATICAL SANS-SERIF BOLD SMALL ZETA;Ll;0;L;<font> 03B6;;;;N;;;;;
+1D776;MATHEMATICAL SANS-SERIF BOLD SMALL ETA;Ll;0;L;<font> 03B7;;;;N;;;;;
+1D777;MATHEMATICAL SANS-SERIF BOLD SMALL THETA;Ll;0;L;<font> 03B8;;;;N;;;;;
+1D778;MATHEMATICAL SANS-SERIF BOLD SMALL IOTA;Ll;0;L;<font> 03B9;;;;N;;;;;
+1D779;MATHEMATICAL SANS-SERIF BOLD SMALL KAPPA;Ll;0;L;<font> 03BA;;;;N;;;;;
+1D77A;MATHEMATICAL SANS-SERIF BOLD SMALL LAMDA;Ll;0;L;<font> 03BB;;;;N;;;;;
+1D77B;MATHEMATICAL SANS-SERIF BOLD SMALL MU;Ll;0;L;<font> 03BC;;;;N;;;;;
+1D77C;MATHEMATICAL SANS-SERIF BOLD SMALL NU;Ll;0;L;<font> 03BD;;;;N;;;;;
+1D77D;MATHEMATICAL SANS-SERIF BOLD SMALL XI;Ll;0;L;<font> 03BE;;;;N;;;;;
+1D77E;MATHEMATICAL SANS-SERIF BOLD SMALL OMICRON;Ll;0;L;<font> 03BF;;;;N;;;;;
+1D77F;MATHEMATICAL SANS-SERIF BOLD SMALL PI;Ll;0;L;<font> 03C0;;;;N;;;;;
+1D780;MATHEMATICAL SANS-SERIF BOLD SMALL RHO;Ll;0;L;<font> 03C1;;;;N;;;;;
+1D781;MATHEMATICAL SANS-SERIF BOLD SMALL FINAL SIGMA;Ll;0;L;<font> 03C2;;;;N;;;;;
+1D782;MATHEMATICAL SANS-SERIF BOLD SMALL SIGMA;Ll;0;L;<font> 03C3;;;;N;;;;;
+1D783;MATHEMATICAL SANS-SERIF BOLD SMALL TAU;Ll;0;L;<font> 03C4;;;;N;;;;;
+1D784;MATHEMATICAL SANS-SERIF BOLD SMALL UPSILON;Ll;0;L;<font> 03C5;;;;N;;;;;
+1D785;MATHEMATICAL SANS-SERIF BOLD SMALL PHI;Ll;0;L;<font> 03C6;;;;N;;;;;
+1D786;MATHEMATICAL SANS-SERIF BOLD SMALL CHI;Ll;0;L;<font> 03C7;;;;N;;;;;
+1D787;MATHEMATICAL SANS-SERIF BOLD SMALL PSI;Ll;0;L;<font> 03C8;;;;N;;;;;
+1D788;MATHEMATICAL SANS-SERIF BOLD SMALL OMEGA;Ll;0;L;<font> 03C9;;;;N;;;;;
+1D789;MATHEMATICAL SANS-SERIF BOLD PARTIAL DIFFERENTIAL;Sm;0;L;<font> 2202;;;;N;;;;;
+1D78A;MATHEMATICAL SANS-SERIF BOLD EPSILON SYMBOL;Ll;0;L;<font> 03F5;;;;N;;;;;
+1D78B;MATHEMATICAL SANS-SERIF BOLD THETA SYMBOL;Ll;0;L;<font> 03D1;;;;N;;;;;
+1D78C;MATHEMATICAL SANS-SERIF BOLD KAPPA SYMBOL;Ll;0;L;<font> 03F0;;;;N;;;;;
+1D78D;MATHEMATICAL SANS-SERIF BOLD PHI SYMBOL;Ll;0;L;<font> 03D5;;;;N;;;;;
+1D78E;MATHEMATICAL SANS-SERIF BOLD RHO SYMBOL;Ll;0;L;<font> 03F1;;;;N;;;;;
+1D78F;MATHEMATICAL SANS-SERIF BOLD PI SYMBOL;Ll;0;L;<font> 03D6;;;;N;;;;;
+1D790;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL ALPHA;Lu;0;L;<font> 0391;;;;N;;;;;
+1D791;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL BETA;Lu;0;L;<font> 0392;;;;N;;;;;
+1D792;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL GAMMA;Lu;0;L;<font> 0393;;;;N;;;;;
+1D793;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL DELTA;Lu;0;L;<font> 0394;;;;N;;;;;
+1D794;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL EPSILON;Lu;0;L;<font> 0395;;;;N;;;;;
+1D795;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL ZETA;Lu;0;L;<font> 0396;;;;N;;;;;
+1D796;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL ETA;Lu;0;L;<font> 0397;;;;N;;;;;
+1D797;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL THETA;Lu;0;L;<font> 0398;;;;N;;;;;
+1D798;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL IOTA;Lu;0;L;<font> 0399;;;;N;;;;;
+1D799;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL KAPPA;Lu;0;L;<font> 039A;;;;N;;;;;
+1D79A;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL LAMDA;Lu;0;L;<font> 039B;;;;N;;;;;
+1D79B;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL MU;Lu;0;L;<font> 039C;;;;N;;;;;
+1D79C;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL NU;Lu;0;L;<font> 039D;;;;N;;;;;
+1D79D;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL XI;Lu;0;L;<font> 039E;;;;N;;;;;
+1D79E;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL OMICRON;Lu;0;L;<font> 039F;;;;N;;;;;
+1D79F;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL PI;Lu;0;L;<font> 03A0;;;;N;;;;;
+1D7A0;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL RHO;Lu;0;L;<font> 03A1;;;;N;;;;;
+1D7A1;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL THETA SYMBOL;Lu;0;L;<font> 03F4;;;;N;;;;;
+1D7A2;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL SIGMA;Lu;0;L;<font> 03A3;;;;N;;;;;
+1D7A3;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL TAU;Lu;0;L;<font> 03A4;;;;N;;;;;
+1D7A4;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL UPSILON;Lu;0;L;<font> 03A5;;;;N;;;;;
+1D7A5;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL PHI;Lu;0;L;<font> 03A6;;;;N;;;;;
+1D7A6;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL CHI;Lu;0;L;<font> 03A7;;;;N;;;;;
+1D7A7;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL PSI;Lu;0;L;<font> 03A8;;;;N;;;;;
+1D7A8;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL OMEGA;Lu;0;L;<font> 03A9;;;;N;;;;;
+1D7A9;MATHEMATICAL SANS-SERIF BOLD ITALIC NABLA;Sm;0;L;<font> 2207;;;;N;;;;;
+1D7AA;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL ALPHA;Ll;0;L;<font> 03B1;;;;N;;;;;
+1D7AB;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL BETA;Ll;0;L;<font> 03B2;;;;N;;;;;
+1D7AC;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL GAMMA;Ll;0;L;<font> 03B3;;;;N;;;;;
+1D7AD;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL DELTA;Ll;0;L;<font> 03B4;;;;N;;;;;
+1D7AE;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL EPSILON;Ll;0;L;<font> 03B5;;;;N;;;;;
+1D7AF;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL ZETA;Ll;0;L;<font> 03B6;;;;N;;;;;
+1D7B0;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL ETA;Ll;0;L;<font> 03B7;;;;N;;;;;
+1D7B1;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL THETA;Ll;0;L;<font> 03B8;;;;N;;;;;
+1D7B2;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL IOTA;Ll;0;L;<font> 03B9;;;;N;;;;;
+1D7B3;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL KAPPA;Ll;0;L;<font> 03BA;;;;N;;;;;
+1D7B4;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL LAMDA;Ll;0;L;<font> 03BB;;;;N;;;;;
+1D7B5;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL MU;Ll;0;L;<font> 03BC;;;;N;;;;;
+1D7B6;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL NU;Ll;0;L;<font> 03BD;;;;N;;;;;
+1D7B7;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL XI;Ll;0;L;<font> 03BE;;;;N;;;;;
+1D7B8;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL OMICRON;Ll;0;L;<font> 03BF;;;;N;;;;;
+1D7B9;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL PI;Ll;0;L;<font> 03C0;;;;N;;;;;
+1D7BA;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL RHO;Ll;0;L;<font> 03C1;;;;N;;;;;
+1D7BB;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL FINAL SIGMA;Ll;0;L;<font> 03C2;;;;N;;;;;
+1D7BC;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL SIGMA;Ll;0;L;<font> 03C3;;;;N;;;;;
+1D7BD;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL TAU;Ll;0;L;<font> 03C4;;;;N;;;;;
+1D7BE;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL UPSILON;Ll;0;L;<font> 03C5;;;;N;;;;;
+1D7BF;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL PHI;Ll;0;L;<font> 03C6;;;;N;;;;;
+1D7C0;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL CHI;Ll;0;L;<font> 03C7;;;;N;;;;;
+1D7C1;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL PSI;Ll;0;L;<font> 03C8;;;;N;;;;;
+1D7C2;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL OMEGA;Ll;0;L;<font> 03C9;;;;N;;;;;
+1D7C3;MATHEMATICAL SANS-SERIF BOLD ITALIC PARTIAL DIFFERENTIAL;Sm;0;L;<font> 2202;;;;N;;;;;
+1D7C4;MATHEMATICAL SANS-SERIF BOLD ITALIC EPSILON SYMBOL;Ll;0;L;<font> 03F5;;;;N;;;;;
+1D7C5;MATHEMATICAL SANS-SERIF BOLD ITALIC THETA SYMBOL;Ll;0;L;<font> 03D1;;;;N;;;;;
+1D7C6;MATHEMATICAL SANS-SERIF BOLD ITALIC KAPPA SYMBOL;Ll;0;L;<font> 03F0;;;;N;;;;;
+1D7C7;MATHEMATICAL SANS-SERIF BOLD ITALIC PHI SYMBOL;Ll;0;L;<font> 03D5;;;;N;;;;;
+1D7C8;MATHEMATICAL SANS-SERIF BOLD ITALIC RHO SYMBOL;Ll;0;L;<font> 03F1;;;;N;;;;;
+1D7C9;MATHEMATICAL SANS-SERIF BOLD ITALIC PI SYMBOL;Ll;0;L;<font> 03D6;;;;N;;;;;
+1D7CE;MATHEMATICAL BOLD DIGIT ZERO;Nd;0;EN;<font> 0030;0;0;0;N;;;;;
+1D7CF;MATHEMATICAL BOLD DIGIT ONE;Nd;0;EN;<font> 0031;1;1;1;N;;;;;
+1D7D0;MATHEMATICAL BOLD DIGIT TWO;Nd;0;EN;<font> 0032;2;2;2;N;;;;;
+1D7D1;MATHEMATICAL BOLD DIGIT THREE;Nd;0;EN;<font> 0033;3;3;3;N;;;;;
+1D7D2;MATHEMATICAL BOLD DIGIT FOUR;Nd;0;EN;<font> 0034;4;4;4;N;;;;;
+1D7D3;MATHEMATICAL BOLD DIGIT FIVE;Nd;0;EN;<font> 0035;5;5;5;N;;;;;
+1D7D4;MATHEMATICAL BOLD DIGIT SIX;Nd;0;EN;<font> 0036;6;6;6;N;;;;;
+1D7D5;MATHEMATICAL BOLD DIGIT SEVEN;Nd;0;EN;<font> 0037;7;7;7;N;;;;;
+1D7D6;MATHEMATICAL BOLD DIGIT EIGHT;Nd;0;EN;<font> 0038;8;8;8;N;;;;;
+1D7D7;MATHEMATICAL BOLD DIGIT NINE;Nd;0;EN;<font> 0039;9;9;9;N;;;;;
+1D7D8;MATHEMATICAL DOUBLE-STRUCK DIGIT ZERO;Nd;0;EN;<font> 0030;0;0;0;N;;;;;
+1D7D9;MATHEMATICAL DOUBLE-STRUCK DIGIT ONE;Nd;0;EN;<font> 0031;1;1;1;N;;;;;
+1D7DA;MATHEMATICAL DOUBLE-STRUCK DIGIT TWO;Nd;0;EN;<font> 0032;2;2;2;N;;;;;
+1D7DB;MATHEMATICAL DOUBLE-STRUCK DIGIT THREE;Nd;0;EN;<font> 0033;3;3;3;N;;;;;
+1D7DC;MATHEMATICAL DOUBLE-STRUCK DIGIT FOUR;Nd;0;EN;<font> 0034;4;4;4;N;;;;;
+1D7DD;MATHEMATICAL DOUBLE-STRUCK DIGIT FIVE;Nd;0;EN;<font> 0035;5;5;5;N;;;;;
+1D7DE;MATHEMATICAL DOUBLE-STRUCK DIGIT SIX;Nd;0;EN;<font> 0036;6;6;6;N;;;;;
+1D7DF;MATHEMATICAL DOUBLE-STRUCK DIGIT SEVEN;Nd;0;EN;<font> 0037;7;7;7;N;;;;;
+1D7E0;MATHEMATICAL DOUBLE-STRUCK DIGIT EIGHT;Nd;0;EN;<font> 0038;8;8;8;N;;;;;
+1D7E1;MATHEMATICAL DOUBLE-STRUCK DIGIT NINE;Nd;0;EN;<font> 0039;9;9;9;N;;;;;
+1D7E2;MATHEMATICAL SANS-SERIF DIGIT ZERO;Nd;0;EN;<font> 0030;0;0;0;N;;;;;
+1D7E3;MATHEMATICAL SANS-SERIF DIGIT ONE;Nd;0;EN;<font> 0031;1;1;1;N;;;;;
+1D7E4;MATHEMATICAL SANS-SERIF DIGIT TWO;Nd;0;EN;<font> 0032;2;2;2;N;;;;;
+1D7E5;MATHEMATICAL SANS-SERIF DIGIT THREE;Nd;0;EN;<font> 0033;3;3;3;N;;;;;
+1D7E6;MATHEMATICAL SANS-SERIF DIGIT FOUR;Nd;0;EN;<font> 0034;4;4;4;N;;;;;
+1D7E7;MATHEMATICAL SANS-SERIF DIGIT FIVE;Nd;0;EN;<font> 0035;5;5;5;N;;;;;
+1D7E8;MATHEMATICAL SANS-SERIF DIGIT SIX;Nd;0;EN;<font> 0036;6;6;6;N;;;;;
+1D7E9;MATHEMATICAL SANS-SERIF DIGIT SEVEN;Nd;0;EN;<font> 0037;7;7;7;N;;;;;
+1D7EA;MATHEMATICAL SANS-SERIF DIGIT EIGHT;Nd;0;EN;<font> 0038;8;8;8;N;;;;;
+1D7EB;MATHEMATICAL SANS-SERIF DIGIT NINE;Nd;0;EN;<font> 0039;9;9;9;N;;;;;
+1D7EC;MATHEMATICAL SANS-SERIF BOLD DIGIT ZERO;Nd;0;EN;<font> 0030;0;0;0;N;;;;;
+1D7ED;MATHEMATICAL SANS-SERIF BOLD DIGIT ONE;Nd;0;EN;<font> 0031;1;1;1;N;;;;;
+1D7EE;MATHEMATICAL SANS-SERIF BOLD DIGIT TWO;Nd;0;EN;<font> 0032;2;2;2;N;;;;;
+1D7EF;MATHEMATICAL SANS-SERIF BOLD DIGIT THREE;Nd;0;EN;<font> 0033;3;3;3;N;;;;;
+1D7F0;MATHEMATICAL SANS-SERIF BOLD DIGIT FOUR;Nd;0;EN;<font> 0034;4;4;4;N;;;;;
+1D7F1;MATHEMATICAL SANS-SERIF BOLD DIGIT FIVE;Nd;0;EN;<font> 0035;5;5;5;N;;;;;
+1D7F2;MATHEMATICAL SANS-SERIF BOLD DIGIT SIX;Nd;0;EN;<font> 0036;6;6;6;N;;;;;
+1D7F3;MATHEMATICAL SANS-SERIF BOLD DIGIT SEVEN;Nd;0;EN;<font> 0037;7;7;7;N;;;;;
+1D7F4;MATHEMATICAL SANS-SERIF BOLD DIGIT EIGHT;Nd;0;EN;<font> 0038;8;8;8;N;;;;;
+1D7F5;MATHEMATICAL SANS-SERIF BOLD DIGIT NINE;Nd;0;EN;<font> 0039;9;9;9;N;;;;;
+1D7F6;MATHEMATICAL MONOSPACE DIGIT ZERO;Nd;0;EN;<font> 0030;0;0;0;N;;;;;
+1D7F7;MATHEMATICAL MONOSPACE DIGIT ONE;Nd;0;EN;<font> 0031;1;1;1;N;;;;;
+1D7F8;MATHEMATICAL MONOSPACE DIGIT TWO;Nd;0;EN;<font> 0032;2;2;2;N;;;;;
+1D7F9;MATHEMATICAL MONOSPACE DIGIT THREE;Nd;0;EN;<font> 0033;3;3;3;N;;;;;
+1D7FA;MATHEMATICAL MONOSPACE DIGIT FOUR;Nd;0;EN;<font> 0034;4;4;4;N;;;;;
+1D7FB;MATHEMATICAL MONOSPACE DIGIT FIVE;Nd;0;EN;<font> 0035;5;5;5;N;;;;;
+1D7FC;MATHEMATICAL MONOSPACE DIGIT SIX;Nd;0;EN;<font> 0036;6;6;6;N;;;;;
+1D7FD;MATHEMATICAL MONOSPACE DIGIT SEVEN;Nd;0;EN;<font> 0037;7;7;7;N;;;;;
+1D7FE;MATHEMATICAL MONOSPACE DIGIT EIGHT;Nd;0;EN;<font> 0038;8;8;8;N;;;;;
+1D7FF;MATHEMATICAL MONOSPACE DIGIT NINE;Nd;0;EN;<font> 0039;9;9;9;N;;;;;
+20000;<CJK Ideograph Extension B, First>;Lo;0;L;;;;;N;;;;;
+2A6D6;<CJK Ideograph Extension B, Last>;Lo;0;L;;;;;N;;;;;
+2F800;CJK COMPATIBILITY IDEOGRAPH-2F800;Lo;0;L;4E3D;;;;N;;;;;
+2F801;CJK COMPATIBILITY IDEOGRAPH-2F801;Lo;0;L;4E38;;;;N;;;;;
+2F802;CJK COMPATIBILITY IDEOGRAPH-2F802;Lo;0;L;4E41;;;;N;;;;;
+2F803;CJK COMPATIBILITY IDEOGRAPH-2F803;Lo;0;L;20122;;;;N;;;;;
+2F804;CJK COMPATIBILITY IDEOGRAPH-2F804;Lo;0;L;4F60;;;;N;;;;;
+2F805;CJK COMPATIBILITY IDEOGRAPH-2F805;Lo;0;L;4FAE;;;;N;;;;;
+2F806;CJK COMPATIBILITY IDEOGRAPH-2F806;Lo;0;L;4FBB;;;;N;;;;;
+2F807;CJK COMPATIBILITY IDEOGRAPH-2F807;Lo;0;L;5002;;;;N;;;;;
+2F808;CJK COMPATIBILITY IDEOGRAPH-2F808;Lo;0;L;507A;;;;N;;;;;
+2F809;CJK COMPATIBILITY IDEOGRAPH-2F809;Lo;0;L;5099;;;;N;;;;;
+2F80A;CJK COMPATIBILITY IDEOGRAPH-2F80A;Lo;0;L;50E7;;;;N;;;;;
+2F80B;CJK COMPATIBILITY IDEOGRAPH-2F80B;Lo;0;L;50CF;;;;N;;;;;
+2F80C;CJK COMPATIBILITY IDEOGRAPH-2F80C;Lo;0;L;349E;;;;N;;;;;
+2F80D;CJK COMPATIBILITY IDEOGRAPH-2F80D;Lo;0;L;2063A;;;;N;;;;;
+2F80E;CJK COMPATIBILITY IDEOGRAPH-2F80E;Lo;0;L;514D;;;;N;;;;;
+2F80F;CJK COMPATIBILITY IDEOGRAPH-2F80F;Lo;0;L;5154;;;;N;;;;;
+2F810;CJK COMPATIBILITY IDEOGRAPH-2F810;Lo;0;L;5164;;;;N;;;;;
+2F811;CJK COMPATIBILITY IDEOGRAPH-2F811;Lo;0;L;5177;;;;N;;;;;
+2F812;CJK COMPATIBILITY IDEOGRAPH-2F812;Lo;0;L;2051C;;;;N;;;;;
+2F813;CJK COMPATIBILITY IDEOGRAPH-2F813;Lo;0;L;34B9;;;;N;;;;;
+2F814;CJK COMPATIBILITY IDEOGRAPH-2F814;Lo;0;L;5167;;;;N;;;;;
+2F815;CJK COMPATIBILITY IDEOGRAPH-2F815;Lo;0;L;518D;;;;N;;;;;
+2F816;CJK COMPATIBILITY IDEOGRAPH-2F816;Lo;0;L;2054B;;;;N;;;;;
+2F817;CJK COMPATIBILITY IDEOGRAPH-2F817;Lo;0;L;5197;;;;N;;;;;
+2F818;CJK COMPATIBILITY IDEOGRAPH-2F818;Lo;0;L;51A4;;;;N;;;;;
+2F819;CJK COMPATIBILITY IDEOGRAPH-2F819;Lo;0;L;4ECC;;;;N;;;;;
+2F81A;CJK COMPATIBILITY IDEOGRAPH-2F81A;Lo;0;L;51AC;;;;N;;;;;
+2F81B;CJK COMPATIBILITY IDEOGRAPH-2F81B;Lo;0;L;51B5;;;;N;;;;;
+2F81C;CJK COMPATIBILITY IDEOGRAPH-2F81C;Lo;0;L;291DF;;;;N;;;;;
+2F81D;CJK COMPATIBILITY IDEOGRAPH-2F81D;Lo;0;L;51F5;;;;N;;;;;
+2F81E;CJK COMPATIBILITY IDEOGRAPH-2F81E;Lo;0;L;5203;;;;N;;;;;
+2F81F;CJK COMPATIBILITY IDEOGRAPH-2F81F;Lo;0;L;34DF;;;;N;;;;;
+2F820;CJK COMPATIBILITY IDEOGRAPH-2F820;Lo;0;L;523B;;;;N;;;;;
+2F821;CJK COMPATIBILITY IDEOGRAPH-2F821;Lo;0;L;5246;;;;N;;;;;
+2F822;CJK COMPATIBILITY IDEOGRAPH-2F822;Lo;0;L;5272;;;;N;;;;;
+2F823;CJK COMPATIBILITY IDEOGRAPH-2F823;Lo;0;L;5277;;;;N;;;;;
+2F824;CJK COMPATIBILITY IDEOGRAPH-2F824;Lo;0;L;3515;;;;N;;;;;
+2F825;CJK COMPATIBILITY IDEOGRAPH-2F825;Lo;0;L;52C7;;;;N;;;;;
+2F826;CJK COMPATIBILITY IDEOGRAPH-2F826;Lo;0;L;52C9;;;;N;;;;;
+2F827;CJK COMPATIBILITY IDEOGRAPH-2F827;Lo;0;L;52E4;;;;N;;;;;
+2F828;CJK COMPATIBILITY IDEOGRAPH-2F828;Lo;0;L;52FA;;;;N;;;;;
+2F829;CJK COMPATIBILITY IDEOGRAPH-2F829;Lo;0;L;5305;;;;N;;;;;
+2F82A;CJK COMPATIBILITY IDEOGRAPH-2F82A;Lo;0;L;5306;;;;N;;;;;
+2F82B;CJK COMPATIBILITY IDEOGRAPH-2F82B;Lo;0;L;5317;;;;N;;;;;
+2F82C;CJK COMPATIBILITY IDEOGRAPH-2F82C;Lo;0;L;5349;;;;N;;;;;
+2F82D;CJK COMPATIBILITY IDEOGRAPH-2F82D;Lo;0;L;5351;;;;N;;;;;
+2F82E;CJK COMPATIBILITY IDEOGRAPH-2F82E;Lo;0;L;535A;;;;N;;;;;
+2F82F;CJK COMPATIBILITY IDEOGRAPH-2F82F;Lo;0;L;5373;;;;N;;;;;
+2F830;CJK COMPATIBILITY IDEOGRAPH-2F830;Lo;0;L;537D;;;;N;;;;;
+2F831;CJK COMPATIBILITY IDEOGRAPH-2F831;Lo;0;L;537F;;;;N;;;;;
+2F832;CJK COMPATIBILITY IDEOGRAPH-2F832;Lo;0;L;537F;;;;N;;;;;
+2F833;CJK COMPATIBILITY IDEOGRAPH-2F833;Lo;0;L;537F;;;;N;;;;;
+2F834;CJK COMPATIBILITY IDEOGRAPH-2F834;Lo;0;L;20A2C;;;;N;;;;;
+2F835;CJK COMPATIBILITY IDEOGRAPH-2F835;Lo;0;L;7070;;;;N;;;;;
+2F836;CJK COMPATIBILITY IDEOGRAPH-2F836;Lo;0;L;53CA;;;;N;;;;;
+2F837;CJK COMPATIBILITY IDEOGRAPH-2F837;Lo;0;L;53DF;;;;N;;;;;
+2F838;CJK COMPATIBILITY IDEOGRAPH-2F838;Lo;0;L;20B63;;;;N;;;;;
+2F839;CJK COMPATIBILITY IDEOGRAPH-2F839;Lo;0;L;53EB;;;;N;;;;;
+2F83A;CJK COMPATIBILITY IDEOGRAPH-2F83A;Lo;0;L;53F1;;;;N;;;;;
+2F83B;CJK COMPATIBILITY IDEOGRAPH-2F83B;Lo;0;L;5406;;;;N;;;;;
+2F83C;CJK COMPATIBILITY IDEOGRAPH-2F83C;Lo;0;L;549E;;;;N;;;;;
+2F83D;CJK COMPATIBILITY IDEOGRAPH-2F83D;Lo;0;L;5438;;;;N;;;;;
+2F83E;CJK COMPATIBILITY IDEOGRAPH-2F83E;Lo;0;L;5448;;;;N;;;;;
+2F83F;CJK COMPATIBILITY IDEOGRAPH-2F83F;Lo;0;L;5468;;;;N;;;;;
+2F840;CJK COMPATIBILITY IDEOGRAPH-2F840;Lo;0;L;54A2;;;;N;;;;;
+2F841;CJK COMPATIBILITY IDEOGRAPH-2F841;Lo;0;L;54F6;;;;N;;;;;
+2F842;CJK COMPATIBILITY IDEOGRAPH-2F842;Lo;0;L;5510;;;;N;;;;;
+2F843;CJK COMPATIBILITY IDEOGRAPH-2F843;Lo;0;L;5553;;;;N;;;;;
+2F844;CJK COMPATIBILITY IDEOGRAPH-2F844;Lo;0;L;5563;;;;N;;;;;
+2F845;CJK COMPATIBILITY IDEOGRAPH-2F845;Lo;0;L;5584;;;;N;;;;;
+2F846;CJK COMPATIBILITY IDEOGRAPH-2F846;Lo;0;L;5584;;;;N;;;;;
+2F847;CJK COMPATIBILITY IDEOGRAPH-2F847;Lo;0;L;5599;;;;N;;;;;
+2F848;CJK COMPATIBILITY IDEOGRAPH-2F848;Lo;0;L;55AB;;;;N;;;;;
+2F849;CJK COMPATIBILITY IDEOGRAPH-2F849;Lo;0;L;55B3;;;;N;;;;;
+2F84A;CJK COMPATIBILITY IDEOGRAPH-2F84A;Lo;0;L;55C2;;;;N;;;;;
+2F84B;CJK COMPATIBILITY IDEOGRAPH-2F84B;Lo;0;L;5716;;;;N;;;;;
+2F84C;CJK COMPATIBILITY IDEOGRAPH-2F84C;Lo;0;L;5606;;;;N;;;;;
+2F84D;CJK COMPATIBILITY IDEOGRAPH-2F84D;Lo;0;L;5717;;;;N;;;;;
+2F84E;CJK COMPATIBILITY IDEOGRAPH-2F84E;Lo;0;L;5651;;;;N;;;;;
+2F84F;CJK COMPATIBILITY IDEOGRAPH-2F84F;Lo;0;L;5674;;;;N;;;;;
+2F850;CJK COMPATIBILITY IDEOGRAPH-2F850;Lo;0;L;5207;;;;N;;;;;
+2F851;CJK COMPATIBILITY IDEOGRAPH-2F851;Lo;0;L;58EE;;;;N;;;;;
+2F852;CJK COMPATIBILITY IDEOGRAPH-2F852;Lo;0;L;57CE;;;;N;;;;;
+2F853;CJK COMPATIBILITY IDEOGRAPH-2F853;Lo;0;L;57F4;;;;N;;;;;
+2F854;CJK COMPATIBILITY IDEOGRAPH-2F854;Lo;0;L;580D;;;;N;;;;;
+2F855;CJK COMPATIBILITY IDEOGRAPH-2F855;Lo;0;L;578B;;;;N;;;;;
+2F856;CJK COMPATIBILITY IDEOGRAPH-2F856;Lo;0;L;5832;;;;N;;;;;
+2F857;CJK COMPATIBILITY IDEOGRAPH-2F857;Lo;0;L;5831;;;;N;;;;;
+2F858;CJK COMPATIBILITY IDEOGRAPH-2F858;Lo;0;L;58AC;;;;N;;;;;
+2F859;CJK COMPATIBILITY IDEOGRAPH-2F859;Lo;0;L;214E4;;;;N;;;;;
+2F85A;CJK COMPATIBILITY IDEOGRAPH-2F85A;Lo;0;L;58F2;;;;N;;;;;
+2F85B;CJK COMPATIBILITY IDEOGRAPH-2F85B;Lo;0;L;58F7;;;;N;;;;;
+2F85C;CJK COMPATIBILITY IDEOGRAPH-2F85C;Lo;0;L;5906;;;;N;;;;;
+2F85D;CJK COMPATIBILITY IDEOGRAPH-2F85D;Lo;0;L;591A;;;;N;;;;;
+2F85E;CJK COMPATIBILITY IDEOGRAPH-2F85E;Lo;0;L;5922;;;;N;;;;;
+2F85F;CJK COMPATIBILITY IDEOGRAPH-2F85F;Lo;0;L;5962;;;;N;;;;;
+2F860;CJK COMPATIBILITY IDEOGRAPH-2F860;Lo;0;L;216A8;;;;N;;;;;
+2F861;CJK COMPATIBILITY IDEOGRAPH-2F861;Lo;0;L;216EA;;;;N;;;;;
+2F862;CJK COMPATIBILITY IDEOGRAPH-2F862;Lo;0;L;59EC;;;;N;;;;;
+2F863;CJK COMPATIBILITY IDEOGRAPH-2F863;Lo;0;L;5A1B;;;;N;;;;;
+2F864;CJK COMPATIBILITY IDEOGRAPH-2F864;Lo;0;L;5A27;;;;N;;;;;
+2F865;CJK COMPATIBILITY IDEOGRAPH-2F865;Lo;0;L;59D8;;;;N;;;;;
+2F866;CJK COMPATIBILITY IDEOGRAPH-2F866;Lo;0;L;5A66;;;;N;;;;;
+2F867;CJK COMPATIBILITY IDEOGRAPH-2F867;Lo;0;L;36EE;;;;N;;;;;
+2F868;CJK COMPATIBILITY IDEOGRAPH-2F868;Lo;0;L;36FC;;;;N;;;;;
+2F869;CJK COMPATIBILITY IDEOGRAPH-2F869;Lo;0;L;5B08;;;;N;;;;;
+2F86A;CJK COMPATIBILITY IDEOGRAPH-2F86A;Lo;0;L;5B3E;;;;N;;;;;
+2F86B;CJK COMPATIBILITY IDEOGRAPH-2F86B;Lo;0;L;5B3E;;;;N;;;;;
+2F86C;CJK COMPATIBILITY IDEOGRAPH-2F86C;Lo;0;L;219C8;;;;N;;;;;
+2F86D;CJK COMPATIBILITY IDEOGRAPH-2F86D;Lo;0;L;5BC3;;;;N;;;;;
+2F86E;CJK COMPATIBILITY IDEOGRAPH-2F86E;Lo;0;L;5BD8;;;;N;;;;;
+2F86F;CJK COMPATIBILITY IDEOGRAPH-2F86F;Lo;0;L;5BE7;;;;N;;;;;
+2F870;CJK COMPATIBILITY IDEOGRAPH-2F870;Lo;0;L;5BF3;;;;N;;;;;
+2F871;CJK COMPATIBILITY IDEOGRAPH-2F871;Lo;0;L;21B18;;;;N;;;;;
+2F872;CJK COMPATIBILITY IDEOGRAPH-2F872;Lo;0;L;5BFF;;;;N;;;;;
+2F873;CJK COMPATIBILITY IDEOGRAPH-2F873;Lo;0;L;5C06;;;;N;;;;;
+2F874;CJK COMPATIBILITY IDEOGRAPH-2F874;Lo;0;L;5F53;;;;N;;;;;
+2F875;CJK COMPATIBILITY IDEOGRAPH-2F875;Lo;0;L;5C22;;;;N;;;;;
+2F876;CJK COMPATIBILITY IDEOGRAPH-2F876;Lo;0;L;3781;;;;N;;;;;
+2F877;CJK COMPATIBILITY IDEOGRAPH-2F877;Lo;0;L;5C60;;;;N;;;;;
+2F878;CJK COMPATIBILITY IDEOGRAPH-2F878;Lo;0;L;5C6E;;;;N;;;;;
+2F879;CJK COMPATIBILITY IDEOGRAPH-2F879;Lo;0;L;5CC0;;;;N;;;;;
+2F87A;CJK COMPATIBILITY IDEOGRAPH-2F87A;Lo;0;L;5C8D;;;;N;;;;;
+2F87B;CJK COMPATIBILITY IDEOGRAPH-2F87B;Lo;0;L;21DE4;;;;N;;;;;
+2F87C;CJK COMPATIBILITY IDEOGRAPH-2F87C;Lo;0;L;5D43;;;;N;;;;;
+2F87D;CJK COMPATIBILITY IDEOGRAPH-2F87D;Lo;0;L;21DE6;;;;N;;;;;
+2F87E;CJK COMPATIBILITY IDEOGRAPH-2F87E;Lo;0;L;5D6E;;;;N;;;;;
+2F87F;CJK COMPATIBILITY IDEOGRAPH-2F87F;Lo;0;L;5D6B;;;;N;;;;;
+2F880;CJK COMPATIBILITY IDEOGRAPH-2F880;Lo;0;L;5D7C;;;;N;;;;;
+2F881;CJK COMPATIBILITY IDEOGRAPH-2F881;Lo;0;L;5DE1;;;;N;;;;;
+2F882;CJK COMPATIBILITY IDEOGRAPH-2F882;Lo;0;L;5DE2;;;;N;;;;;
+2F883;CJK COMPATIBILITY IDEOGRAPH-2F883;Lo;0;L;382F;;;;N;;;;;
+2F884;CJK COMPATIBILITY IDEOGRAPH-2F884;Lo;0;L;5DFD;;;;N;;;;;
+2F885;CJK COMPATIBILITY IDEOGRAPH-2F885;Lo;0;L;5E28;;;;N;;;;;
+2F886;CJK COMPATIBILITY IDEOGRAPH-2F886;Lo;0;L;5E3D;;;;N;;;;;
+2F887;CJK COMPATIBILITY IDEOGRAPH-2F887;Lo;0;L;5E69;;;;N;;;;;
+2F888;CJK COMPATIBILITY IDEOGRAPH-2F888;Lo;0;L;3862;;;;N;;;;;
+2F889;CJK COMPATIBILITY IDEOGRAPH-2F889;Lo;0;L;22183;;;;N;;;;;
+2F88A;CJK COMPATIBILITY IDEOGRAPH-2F88A;Lo;0;L;387C;;;;N;;;;;
+2F88B;CJK COMPATIBILITY IDEOGRAPH-2F88B;Lo;0;L;5EB0;;;;N;;;;;
+2F88C;CJK COMPATIBILITY IDEOGRAPH-2F88C;Lo;0;L;5EB3;;;;N;;;;;
+2F88D;CJK COMPATIBILITY IDEOGRAPH-2F88D;Lo;0;L;5EB6;;;;N;;;;;
+2F88E;CJK COMPATIBILITY IDEOGRAPH-2F88E;Lo;0;L;5ECA;;;;N;;;;;
+2F88F;CJK COMPATIBILITY IDEOGRAPH-2F88F;Lo;0;L;2A392;;;;N;;;;;
+2F890;CJK COMPATIBILITY IDEOGRAPH-2F890;Lo;0;L;5EFE;;;;N;;;;;
+2F891;CJK COMPATIBILITY IDEOGRAPH-2F891;Lo;0;L;22331;;;;N;;;;;
+2F892;CJK COMPATIBILITY IDEOGRAPH-2F892;Lo;0;L;22331;;;;N;;;;;
+2F893;CJK COMPATIBILITY IDEOGRAPH-2F893;Lo;0;L;8201;;;;N;;;;;
+2F894;CJK COMPATIBILITY IDEOGRAPH-2F894;Lo;0;L;5F22;;;;N;;;;;
+2F895;CJK COMPATIBILITY IDEOGRAPH-2F895;Lo;0;L;5F22;;;;N;;;;;
+2F896;CJK COMPATIBILITY IDEOGRAPH-2F896;Lo;0;L;38C7;;;;N;;;;;
+2F897;CJK COMPATIBILITY IDEOGRAPH-2F897;Lo;0;L;232B8;;;;N;;;;;
+2F898;CJK COMPATIBILITY IDEOGRAPH-2F898;Lo;0;L;261DA;;;;N;;;;;
+2F899;CJK COMPATIBILITY IDEOGRAPH-2F899;Lo;0;L;5F62;;;;N;;;;;
+2F89A;CJK COMPATIBILITY IDEOGRAPH-2F89A;Lo;0;L;5F6B;;;;N;;;;;
+2F89B;CJK COMPATIBILITY IDEOGRAPH-2F89B;Lo;0;L;38E3;;;;N;;;;;
+2F89C;CJK COMPATIBILITY IDEOGRAPH-2F89C;Lo;0;L;5F9A;;;;N;;;;;
+2F89D;CJK COMPATIBILITY IDEOGRAPH-2F89D;Lo;0;L;5FCD;;;;N;;;;;
+2F89E;CJK COMPATIBILITY IDEOGRAPH-2F89E;Lo;0;L;5FD7;;;;N;;;;;
+2F89F;CJK COMPATIBILITY IDEOGRAPH-2F89F;Lo;0;L;5FF9;;;;N;;;;;
+2F8A0;CJK COMPATIBILITY IDEOGRAPH-2F8A0;Lo;0;L;6081;;;;N;;;;;
+2F8A1;CJK COMPATIBILITY IDEOGRAPH-2F8A1;Lo;0;L;393A;;;;N;;;;;
+2F8A2;CJK COMPATIBILITY IDEOGRAPH-2F8A2;Lo;0;L;391C;;;;N;;;;;
+2F8A3;CJK COMPATIBILITY IDEOGRAPH-2F8A3;Lo;0;L;6094;;;;N;;;;;
+2F8A4;CJK COMPATIBILITY IDEOGRAPH-2F8A4;Lo;0;L;226D4;;;;N;;;;;
+2F8A5;CJK COMPATIBILITY IDEOGRAPH-2F8A5;Lo;0;L;60C7;;;;N;;;;;
+2F8A6;CJK COMPATIBILITY IDEOGRAPH-2F8A6;Lo;0;L;6148;;;;N;;;;;
+2F8A7;CJK COMPATIBILITY IDEOGRAPH-2F8A7;Lo;0;L;614C;;;;N;;;;;
+2F8A8;CJK COMPATIBILITY IDEOGRAPH-2F8A8;Lo;0;L;614E;;;;N;;;;;
+2F8A9;CJK COMPATIBILITY IDEOGRAPH-2F8A9;Lo;0;L;614C;;;;N;;;;;
+2F8AA;CJK COMPATIBILITY IDEOGRAPH-2F8AA;Lo;0;L;617A;;;;N;;;;;
+2F8AB;CJK COMPATIBILITY IDEOGRAPH-2F8AB;Lo;0;L;618E;;;;N;;;;;
+2F8AC;CJK COMPATIBILITY IDEOGRAPH-2F8AC;Lo;0;L;61B2;;;;N;;;;;
+2F8AD;CJK COMPATIBILITY IDEOGRAPH-2F8AD;Lo;0;L;61A4;;;;N;;;;;
+2F8AE;CJK COMPATIBILITY IDEOGRAPH-2F8AE;Lo;0;L;61AF;;;;N;;;;;
+2F8AF;CJK COMPATIBILITY IDEOGRAPH-2F8AF;Lo;0;L;61DE;;;;N;;;;;
+2F8B0;CJK COMPATIBILITY IDEOGRAPH-2F8B0;Lo;0;L;61F2;;;;N;;;;;
+2F8B1;CJK COMPATIBILITY IDEOGRAPH-2F8B1;Lo;0;L;61F6;;;;N;;;;;
+2F8B2;CJK COMPATIBILITY IDEOGRAPH-2F8B2;Lo;0;L;6210;;;;N;;;;;
+2F8B3;CJK COMPATIBILITY IDEOGRAPH-2F8B3;Lo;0;L;621B;;;;N;;;;;
+2F8B4;CJK COMPATIBILITY IDEOGRAPH-2F8B4;Lo;0;L;625D;;;;N;;;;;
+2F8B5;CJK COMPATIBILITY IDEOGRAPH-2F8B5;Lo;0;L;62B1;;;;N;;;;;
+2F8B6;CJK COMPATIBILITY IDEOGRAPH-2F8B6;Lo;0;L;62D4;;;;N;;;;;
+2F8B7;CJK COMPATIBILITY IDEOGRAPH-2F8B7;Lo;0;L;6350;;;;N;;;;;
+2F8B8;CJK COMPATIBILITY IDEOGRAPH-2F8B8;Lo;0;L;22B0C;;;;N;;;;;
+2F8B9;CJK COMPATIBILITY IDEOGRAPH-2F8B9;Lo;0;L;633D;;;;N;;;;;
+2F8BA;CJK COMPATIBILITY IDEOGRAPH-2F8BA;Lo;0;L;62FC;;;;N;;;;;
+2F8BB;CJK COMPATIBILITY IDEOGRAPH-2F8BB;Lo;0;L;6368;;;;N;;;;;
+2F8BC;CJK COMPATIBILITY IDEOGRAPH-2F8BC;Lo;0;L;6383;;;;N;;;;;
+2F8BD;CJK COMPATIBILITY IDEOGRAPH-2F8BD;Lo;0;L;63E4;;;;N;;;;;
+2F8BE;CJK COMPATIBILITY IDEOGRAPH-2F8BE;Lo;0;L;22BF1;;;;N;;;;;
+2F8BF;CJK COMPATIBILITY IDEOGRAPH-2F8BF;Lo;0;L;6422;;;;N;;;;;
+2F8C0;CJK COMPATIBILITY IDEOGRAPH-2F8C0;Lo;0;L;63C5;;;;N;;;;;
+2F8C1;CJK COMPATIBILITY IDEOGRAPH-2F8C1;Lo;0;L;63A9;;;;N;;;;;
+2F8C2;CJK COMPATIBILITY IDEOGRAPH-2F8C2;Lo;0;L;3A2E;;;;N;;;;;
+2F8C3;CJK COMPATIBILITY IDEOGRAPH-2F8C3;Lo;0;L;6469;;;;N;;;;;
+2F8C4;CJK COMPATIBILITY IDEOGRAPH-2F8C4;Lo;0;L;647E;;;;N;;;;;
+2F8C5;CJK COMPATIBILITY IDEOGRAPH-2F8C5;Lo;0;L;649D;;;;N;;;;;
+2F8C6;CJK COMPATIBILITY IDEOGRAPH-2F8C6;Lo;0;L;6477;;;;N;;;;;
+2F8C7;CJK COMPATIBILITY IDEOGRAPH-2F8C7;Lo;0;L;3A6C;;;;N;;;;;
+2F8C8;CJK COMPATIBILITY IDEOGRAPH-2F8C8;Lo;0;L;654F;;;;N;;;;;
+2F8C9;CJK COMPATIBILITY IDEOGRAPH-2F8C9;Lo;0;L;656C;;;;N;;;;;
+2F8CA;CJK COMPATIBILITY IDEOGRAPH-2F8CA;Lo;0;L;2300A;;;;N;;;;;
+2F8CB;CJK COMPATIBILITY IDEOGRAPH-2F8CB;Lo;0;L;65E3;;;;N;;;;;
+2F8CC;CJK COMPATIBILITY IDEOGRAPH-2F8CC;Lo;0;L;66F8;;;;N;;;;;
+2F8CD;CJK COMPATIBILITY IDEOGRAPH-2F8CD;Lo;0;L;6649;;;;N;;;;;
+2F8CE;CJK COMPATIBILITY IDEOGRAPH-2F8CE;Lo;0;L;3B19;;;;N;;;;;
+2F8CF;CJK COMPATIBILITY IDEOGRAPH-2F8CF;Lo;0;L;6691;;;;N;;;;;
+2F8D0;CJK COMPATIBILITY IDEOGRAPH-2F8D0;Lo;0;L;3B08;;;;N;;;;;
+2F8D1;CJK COMPATIBILITY IDEOGRAPH-2F8D1;Lo;0;L;3AE4;;;;N;;;;;
+2F8D2;CJK COMPATIBILITY IDEOGRAPH-2F8D2;Lo;0;L;5192;;;;N;;;;;
+2F8D3;CJK COMPATIBILITY IDEOGRAPH-2F8D3;Lo;0;L;5195;;;;N;;;;;
+2F8D4;CJK COMPATIBILITY IDEOGRAPH-2F8D4;Lo;0;L;6700;;;;N;;;;;
+2F8D5;CJK COMPATIBILITY IDEOGRAPH-2F8D5;Lo;0;L;669C;;;;N;;;;;
+2F8D6;CJK COMPATIBILITY IDEOGRAPH-2F8D6;Lo;0;L;80AD;;;;N;;;;;
+2F8D7;CJK COMPATIBILITY IDEOGRAPH-2F8D7;Lo;0;L;43D9;;;;N;;;;;
+2F8D8;CJK COMPATIBILITY IDEOGRAPH-2F8D8;Lo;0;L;6717;;;;N;;;;;
+2F8D9;CJK COMPATIBILITY IDEOGRAPH-2F8D9;Lo;0;L;671B;;;;N;;;;;
+2F8DA;CJK COMPATIBILITY IDEOGRAPH-2F8DA;Lo;0;L;6721;;;;N;;;;;
+2F8DB;CJK COMPATIBILITY IDEOGRAPH-2F8DB;Lo;0;L;675E;;;;N;;;;;
+2F8DC;CJK COMPATIBILITY IDEOGRAPH-2F8DC;Lo;0;L;6753;;;;N;;;;;
+2F8DD;CJK COMPATIBILITY IDEOGRAPH-2F8DD;Lo;0;L;233C3;;;;N;;;;;
+2F8DE;CJK COMPATIBILITY IDEOGRAPH-2F8DE;Lo;0;L;3B49;;;;N;;;;;
+2F8DF;CJK COMPATIBILITY IDEOGRAPH-2F8DF;Lo;0;L;67FA;;;;N;;;;;
+2F8E0;CJK COMPATIBILITY IDEOGRAPH-2F8E0;Lo;0;L;6785;;;;N;;;;;
+2F8E1;CJK COMPATIBILITY IDEOGRAPH-2F8E1;Lo;0;L;6852;;;;N;;;;;
+2F8E2;CJK COMPATIBILITY IDEOGRAPH-2F8E2;Lo;0;L;6885;;;;N;;;;;
+2F8E3;CJK COMPATIBILITY IDEOGRAPH-2F8E3;Lo;0;L;2346D;;;;N;;;;;
+2F8E4;CJK COMPATIBILITY IDEOGRAPH-2F8E4;Lo;0;L;688E;;;;N;;;;;
+2F8E5;CJK COMPATIBILITY IDEOGRAPH-2F8E5;Lo;0;L;681F;;;;N;;;;;
+2F8E6;CJK COMPATIBILITY IDEOGRAPH-2F8E6;Lo;0;L;6914;;;;N;;;;;
+2F8E7;CJK COMPATIBILITY IDEOGRAPH-2F8E7;Lo;0;L;3B9D;;;;N;;;;;
+2F8E8;CJK COMPATIBILITY IDEOGRAPH-2F8E8;Lo;0;L;6942;;;;N;;;;;
+2F8E9;CJK COMPATIBILITY IDEOGRAPH-2F8E9;Lo;0;L;69A3;;;;N;;;;;
+2F8EA;CJK COMPATIBILITY IDEOGRAPH-2F8EA;Lo;0;L;69EA;;;;N;;;;;
+2F8EB;CJK COMPATIBILITY IDEOGRAPH-2F8EB;Lo;0;L;6AA8;;;;N;;;;;
+2F8EC;CJK COMPATIBILITY IDEOGRAPH-2F8EC;Lo;0;L;236A3;;;;N;;;;;
+2F8ED;CJK COMPATIBILITY IDEOGRAPH-2F8ED;Lo;0;L;6ADB;;;;N;;;;;
+2F8EE;CJK COMPATIBILITY IDEOGRAPH-2F8EE;Lo;0;L;3C18;;;;N;;;;;
+2F8EF;CJK COMPATIBILITY IDEOGRAPH-2F8EF;Lo;0;L;6B21;;;;N;;;;;
+2F8F0;CJK COMPATIBILITY IDEOGRAPH-2F8F0;Lo;0;L;238A7;;;;N;;;;;
+2F8F1;CJK COMPATIBILITY IDEOGRAPH-2F8F1;Lo;0;L;6B54;;;;N;;;;;
+2F8F2;CJK COMPATIBILITY IDEOGRAPH-2F8F2;Lo;0;L;3C4E;;;;N;;;;;
+2F8F3;CJK COMPATIBILITY IDEOGRAPH-2F8F3;Lo;0;L;6B72;;;;N;;;;;
+2F8F4;CJK COMPATIBILITY IDEOGRAPH-2F8F4;Lo;0;L;6B9F;;;;N;;;;;
+2F8F5;CJK COMPATIBILITY IDEOGRAPH-2F8F5;Lo;0;L;6BBA;;;;N;;;;;
+2F8F6;CJK COMPATIBILITY IDEOGRAPH-2F8F6;Lo;0;L;6BBB;;;;N;;;;;
+2F8F7;CJK COMPATIBILITY IDEOGRAPH-2F8F7;Lo;0;L;23A8D;;;;N;;;;;
+2F8F8;CJK COMPATIBILITY IDEOGRAPH-2F8F8;Lo;0;L;21D0B;;;;N;;;;;
+2F8F9;CJK COMPATIBILITY IDEOGRAPH-2F8F9;Lo;0;L;23AFA;;;;N;;;;;
+2F8FA;CJK COMPATIBILITY IDEOGRAPH-2F8FA;Lo;0;L;6C4E;;;;N;;;;;
+2F8FB;CJK COMPATIBILITY IDEOGRAPH-2F8FB;Lo;0;L;23CBC;;;;N;;;;;
+2F8FC;CJK COMPATIBILITY IDEOGRAPH-2F8FC;Lo;0;L;6CBF;;;;N;;;;;
+2F8FD;CJK COMPATIBILITY IDEOGRAPH-2F8FD;Lo;0;L;6CCD;;;;N;;;;;
+2F8FE;CJK COMPATIBILITY IDEOGRAPH-2F8FE;Lo;0;L;6C67;;;;N;;;;;
+2F8FF;CJK COMPATIBILITY IDEOGRAPH-2F8FF;Lo;0;L;6D16;;;;N;;;;;
+2F900;CJK COMPATIBILITY IDEOGRAPH-2F900;Lo;0;L;6D3E;;;;N;;;;;
+2F901;CJK COMPATIBILITY IDEOGRAPH-2F901;Lo;0;L;6D77;;;;N;;;;;
+2F902;CJK COMPATIBILITY IDEOGRAPH-2F902;Lo;0;L;6D41;;;;N;;;;;
+2F903;CJK COMPATIBILITY IDEOGRAPH-2F903;Lo;0;L;6D69;;;;N;;;;;
+2F904;CJK COMPATIBILITY IDEOGRAPH-2F904;Lo;0;L;6D78;;;;N;;;;;
+2F905;CJK COMPATIBILITY IDEOGRAPH-2F905;Lo;0;L;6D85;;;;N;;;;;
+2F906;CJK COMPATIBILITY IDEOGRAPH-2F906;Lo;0;L;23D1E;;;;N;;;;;
+2F907;CJK COMPATIBILITY IDEOGRAPH-2F907;Lo;0;L;6D34;;;;N;;;;;
+2F908;CJK COMPATIBILITY IDEOGRAPH-2F908;Lo;0;L;6E2F;;;;N;;;;;
+2F909;CJK COMPATIBILITY IDEOGRAPH-2F909;Lo;0;L;6E6E;;;;N;;;;;
+2F90A;CJK COMPATIBILITY IDEOGRAPH-2F90A;Lo;0;L;3D33;;;;N;;;;;
+2F90B;CJK COMPATIBILITY IDEOGRAPH-2F90B;Lo;0;L;6ECB;;;;N;;;;;
+2F90C;CJK COMPATIBILITY IDEOGRAPH-2F90C;Lo;0;L;6EC7;;;;N;;;;;
+2F90D;CJK COMPATIBILITY IDEOGRAPH-2F90D;Lo;0;L;23ED1;;;;N;;;;;
+2F90E;CJK COMPATIBILITY IDEOGRAPH-2F90E;Lo;0;L;6DF9;;;;N;;;;;
+2F90F;CJK COMPATIBILITY IDEOGRAPH-2F90F;Lo;0;L;6F6E;;;;N;;;;;
+2F910;CJK COMPATIBILITY IDEOGRAPH-2F910;Lo;0;L;23F5E;;;;N;;;;;
+2F911;CJK COMPATIBILITY IDEOGRAPH-2F911;Lo;0;L;23F8E;;;;N;;;;;
+2F912;CJK COMPATIBILITY IDEOGRAPH-2F912;Lo;0;L;6FC6;;;;N;;;;;
+2F913;CJK COMPATIBILITY IDEOGRAPH-2F913;Lo;0;L;7039;;;;N;;;;;
+2F914;CJK COMPATIBILITY IDEOGRAPH-2F914;Lo;0;L;701E;;;;N;;;;;
+2F915;CJK COMPATIBILITY IDEOGRAPH-2F915;Lo;0;L;701B;;;;N;;;;;
+2F916;CJK COMPATIBILITY IDEOGRAPH-2F916;Lo;0;L;3D96;;;;N;;;;;
+2F917;CJK COMPATIBILITY IDEOGRAPH-2F917;Lo;0;L;704A;;;;N;;;;;
+2F918;CJK COMPATIBILITY IDEOGRAPH-2F918;Lo;0;L;707D;;;;N;;;;;
+2F919;CJK COMPATIBILITY IDEOGRAPH-2F919;Lo;0;L;7077;;;;N;;;;;
+2F91A;CJK COMPATIBILITY IDEOGRAPH-2F91A;Lo;0;L;70AD;;;;N;;;;;
+2F91B;CJK COMPATIBILITY IDEOGRAPH-2F91B;Lo;0;L;20525;;;;N;;;;;
+2F91C;CJK COMPATIBILITY IDEOGRAPH-2F91C;Lo;0;L;7145;;;;N;;;;;
+2F91D;CJK COMPATIBILITY IDEOGRAPH-2F91D;Lo;0;L;24263;;;;N;;;;;
+2F91E;CJK COMPATIBILITY IDEOGRAPH-2F91E;Lo;0;L;719C;;;;N;;;;;
+2F91F;CJK COMPATIBILITY IDEOGRAPH-2F91F;Lo;0;L;243AB;;;;N;;;;;
+2F920;CJK COMPATIBILITY IDEOGRAPH-2F920;Lo;0;L;7228;;;;N;;;;;
+2F921;CJK COMPATIBILITY IDEOGRAPH-2F921;Lo;0;L;7235;;;;N;;;;;
+2F922;CJK COMPATIBILITY IDEOGRAPH-2F922;Lo;0;L;7250;;;;N;;;;;
+2F923;CJK COMPATIBILITY IDEOGRAPH-2F923;Lo;0;L;24608;;;;N;;;;;
+2F924;CJK COMPATIBILITY IDEOGRAPH-2F924;Lo;0;L;7280;;;;N;;;;;
+2F925;CJK COMPATIBILITY IDEOGRAPH-2F925;Lo;0;L;7295;;;;N;;;;;
+2F926;CJK COMPATIBILITY IDEOGRAPH-2F926;Lo;0;L;24735;;;;N;;;;;
+2F927;CJK COMPATIBILITY IDEOGRAPH-2F927;Lo;0;L;24814;;;;N;;;;;
+2F928;CJK COMPATIBILITY IDEOGRAPH-2F928;Lo;0;L;737A;;;;N;;;;;
+2F929;CJK COMPATIBILITY IDEOGRAPH-2F929;Lo;0;L;738B;;;;N;;;;;
+2F92A;CJK COMPATIBILITY IDEOGRAPH-2F92A;Lo;0;L;3EAC;;;;N;;;;;
+2F92B;CJK COMPATIBILITY IDEOGRAPH-2F92B;Lo;0;L;73A5;;;;N;;;;;
+2F92C;CJK COMPATIBILITY IDEOGRAPH-2F92C;Lo;0;L;3EB8;;;;N;;;;;
+2F92D;CJK COMPATIBILITY IDEOGRAPH-2F92D;Lo;0;L;3EB8;;;;N;;;;;
+2F92E;CJK COMPATIBILITY IDEOGRAPH-2F92E;Lo;0;L;7447;;;;N;;;;;
+2F92F;CJK COMPATIBILITY IDEOGRAPH-2F92F;Lo;0;L;745C;;;;N;;;;;
+2F930;CJK COMPATIBILITY IDEOGRAPH-2F930;Lo;0;L;7471;;;;N;;;;;
+2F931;CJK COMPATIBILITY IDEOGRAPH-2F931;Lo;0;L;7485;;;;N;;;;;
+2F932;CJK COMPATIBILITY IDEOGRAPH-2F932;Lo;0;L;74CA;;;;N;;;;;
+2F933;CJK COMPATIBILITY IDEOGRAPH-2F933;Lo;0;L;3F1B;;;;N;;;;;
+2F934;CJK COMPATIBILITY IDEOGRAPH-2F934;Lo;0;L;7524;;;;N;;;;;
+2F935;CJK COMPATIBILITY IDEOGRAPH-2F935;Lo;0;L;24C36;;;;N;;;;;
+2F936;CJK COMPATIBILITY IDEOGRAPH-2F936;Lo;0;L;753E;;;;N;;;;;
+2F937;CJK COMPATIBILITY IDEOGRAPH-2F937;Lo;0;L;24C92;;;;N;;;;;
+2F938;CJK COMPATIBILITY IDEOGRAPH-2F938;Lo;0;L;7570;;;;N;;;;;
+2F939;CJK COMPATIBILITY IDEOGRAPH-2F939;Lo;0;L;2219F;;;;N;;;;;
+2F93A;CJK COMPATIBILITY IDEOGRAPH-2F93A;Lo;0;L;7610;;;;N;;;;;
+2F93B;CJK COMPATIBILITY IDEOGRAPH-2F93B;Lo;0;L;24FA1;;;;N;;;;;
+2F93C;CJK COMPATIBILITY IDEOGRAPH-2F93C;Lo;0;L;24FB8;;;;N;;;;;
+2F93D;CJK COMPATIBILITY IDEOGRAPH-2F93D;Lo;0;L;25044;;;;N;;;;;
+2F93E;CJK COMPATIBILITY IDEOGRAPH-2F93E;Lo;0;L;3FFC;;;;N;;;;;
+2F93F;CJK COMPATIBILITY IDEOGRAPH-2F93F;Lo;0;L;4008;;;;N;;;;;
+2F940;CJK COMPATIBILITY IDEOGRAPH-2F940;Lo;0;L;76F4;;;;N;;;;;
+2F941;CJK COMPATIBILITY IDEOGRAPH-2F941;Lo;0;L;250F3;;;;N;;;;;
+2F942;CJK COMPATIBILITY IDEOGRAPH-2F942;Lo;0;L;250F2;;;;N;;;;;
+2F943;CJK COMPATIBILITY IDEOGRAPH-2F943;Lo;0;L;25119;;;;N;;;;;
+2F944;CJK COMPATIBILITY IDEOGRAPH-2F944;Lo;0;L;25133;;;;N;;;;;
+2F945;CJK COMPATIBILITY IDEOGRAPH-2F945;Lo;0;L;771E;;;;N;;;;;
+2F946;CJK COMPATIBILITY IDEOGRAPH-2F946;Lo;0;L;771F;;;;N;;;;;
+2F947;CJK COMPATIBILITY IDEOGRAPH-2F947;Lo;0;L;771F;;;;N;;;;;
+2F948;CJK COMPATIBILITY IDEOGRAPH-2F948;Lo;0;L;774A;;;;N;;;;;
+2F949;CJK COMPATIBILITY IDEOGRAPH-2F949;Lo;0;L;4039;;;;N;;;;;
+2F94A;CJK COMPATIBILITY IDEOGRAPH-2F94A;Lo;0;L;778B;;;;N;;;;;
+2F94B;CJK COMPATIBILITY IDEOGRAPH-2F94B;Lo;0;L;4046;;;;N;;;;;
+2F94C;CJK COMPATIBILITY IDEOGRAPH-2F94C;Lo;0;L;4096;;;;N;;;;;
+2F94D;CJK COMPATIBILITY IDEOGRAPH-2F94D;Lo;0;L;2541D;;;;N;;;;;
+2F94E;CJK COMPATIBILITY IDEOGRAPH-2F94E;Lo;0;L;784E;;;;N;;;;;
+2F94F;CJK COMPATIBILITY IDEOGRAPH-2F94F;Lo;0;L;788C;;;;N;;;;;
+2F950;CJK COMPATIBILITY IDEOGRAPH-2F950;Lo;0;L;78CC;;;;N;;;;;
+2F951;CJK COMPATIBILITY IDEOGRAPH-2F951;Lo;0;L;40E3;;;;N;;;;;
+2F952;CJK COMPATIBILITY IDEOGRAPH-2F952;Lo;0;L;25626;;;;N;;;;;
+2F953;CJK COMPATIBILITY IDEOGRAPH-2F953;Lo;0;L;7956;;;;N;;;;;
+2F954;CJK COMPATIBILITY IDEOGRAPH-2F954;Lo;0;L;2569A;;;;N;;;;;
+2F955;CJK COMPATIBILITY IDEOGRAPH-2F955;Lo;0;L;256C5;;;;N;;;;;
+2F956;CJK COMPATIBILITY IDEOGRAPH-2F956;Lo;0;L;798F;;;;N;;;;;
+2F957;CJK COMPATIBILITY IDEOGRAPH-2F957;Lo;0;L;79EB;;;;N;;;;;
+2F958;CJK COMPATIBILITY IDEOGRAPH-2F958;Lo;0;L;412F;;;;N;;;;;
+2F959;CJK COMPATIBILITY IDEOGRAPH-2F959;Lo;0;L;7A40;;;;N;;;;;
+2F95A;CJK COMPATIBILITY IDEOGRAPH-2F95A;Lo;0;L;7A4A;;;;N;;;;;
+2F95B;CJK COMPATIBILITY IDEOGRAPH-2F95B;Lo;0;L;7A4F;;;;N;;;;;
+2F95C;CJK COMPATIBILITY IDEOGRAPH-2F95C;Lo;0;L;2597C;;;;N;;;;;
+2F95D;CJK COMPATIBILITY IDEOGRAPH-2F95D;Lo;0;L;25AA7;;;;N;;;;;
+2F95E;CJK COMPATIBILITY IDEOGRAPH-2F95E;Lo;0;L;25AA7;;;;N;;;;;
+2F95F;CJK COMPATIBILITY IDEOGRAPH-2F95F;Lo;0;L;7AEE;;;;N;;;;;
+2F960;CJK COMPATIBILITY IDEOGRAPH-2F960;Lo;0;L;4202;;;;N;;;;;
+2F961;CJK COMPATIBILITY IDEOGRAPH-2F961;Lo;0;L;25BAB;;;;N;;;;;
+2F962;CJK COMPATIBILITY IDEOGRAPH-2F962;Lo;0;L;7BC6;;;;N;;;;;
+2F963;CJK COMPATIBILITY IDEOGRAPH-2F963;Lo;0;L;7BC9;;;;N;;;;;
+2F964;CJK COMPATIBILITY IDEOGRAPH-2F964;Lo;0;L;4227;;;;N;;;;;
+2F965;CJK COMPATIBILITY IDEOGRAPH-2F965;Lo;0;L;25C80;;;;N;;;;;
+2F966;CJK COMPATIBILITY IDEOGRAPH-2F966;Lo;0;L;7CD2;;;;N;;;;;
+2F967;CJK COMPATIBILITY IDEOGRAPH-2F967;Lo;0;L;42A0;;;;N;;;;;
+2F968;CJK COMPATIBILITY IDEOGRAPH-2F968;Lo;0;L;7CE8;;;;N;;;;;
+2F969;CJK COMPATIBILITY IDEOGRAPH-2F969;Lo;0;L;7CE3;;;;N;;;;;
+2F96A;CJK COMPATIBILITY IDEOGRAPH-2F96A;Lo;0;L;7D00;;;;N;;;;;
+2F96B;CJK COMPATIBILITY IDEOGRAPH-2F96B;Lo;0;L;25F86;;;;N;;;;;
+2F96C;CJK COMPATIBILITY IDEOGRAPH-2F96C;Lo;0;L;7D63;;;;N;;;;;
+2F96D;CJK COMPATIBILITY IDEOGRAPH-2F96D;Lo;0;L;4301;;;;N;;;;;
+2F96E;CJK COMPATIBILITY IDEOGRAPH-2F96E;Lo;0;L;7DC7;;;;N;;;;;
+2F96F;CJK COMPATIBILITY IDEOGRAPH-2F96F;Lo;0;L;7E02;;;;N;;;;;
+2F970;CJK COMPATIBILITY IDEOGRAPH-2F970;Lo;0;L;7E45;;;;N;;;;;
+2F971;CJK COMPATIBILITY IDEOGRAPH-2F971;Lo;0;L;4334;;;;N;;;;;
+2F972;CJK COMPATIBILITY IDEOGRAPH-2F972;Lo;0;L;26228;;;;N;;;;;
+2F973;CJK COMPATIBILITY IDEOGRAPH-2F973;Lo;0;L;26247;;;;N;;;;;
+2F974;CJK COMPATIBILITY IDEOGRAPH-2F974;Lo;0;L;4359;;;;N;;;;;
+2F975;CJK COMPATIBILITY IDEOGRAPH-2F975;Lo;0;L;262D9;;;;N;;;;;
+2F976;CJK COMPATIBILITY IDEOGRAPH-2F976;Lo;0;L;7F7A;;;;N;;;;;
+2F977;CJK COMPATIBILITY IDEOGRAPH-2F977;Lo;0;L;2633E;;;;N;;;;;
+2F978;CJK COMPATIBILITY IDEOGRAPH-2F978;Lo;0;L;7F95;;;;N;;;;;
+2F979;CJK COMPATIBILITY IDEOGRAPH-2F979;Lo;0;L;7FFA;;;;N;;;;;
+2F97A;CJK COMPATIBILITY IDEOGRAPH-2F97A;Lo;0;L;8005;;;;N;;;;;
+2F97B;CJK COMPATIBILITY IDEOGRAPH-2F97B;Lo;0;L;264DA;;;;N;;;;;
+2F97C;CJK COMPATIBILITY IDEOGRAPH-2F97C;Lo;0;L;26523;;;;N;;;;;
+2F97D;CJK COMPATIBILITY IDEOGRAPH-2F97D;Lo;0;L;8060;;;;N;;;;;
+2F97E;CJK COMPATIBILITY IDEOGRAPH-2F97E;Lo;0;L;265A8;;;;N;;;;;
+2F97F;CJK COMPATIBILITY IDEOGRAPH-2F97F;Lo;0;L;8070;;;;N;;;;;
+2F980;CJK COMPATIBILITY IDEOGRAPH-2F980;Lo;0;L;2335F;;;;N;;;;;
+2F981;CJK COMPATIBILITY IDEOGRAPH-2F981;Lo;0;L;43D5;;;;N;;;;;
+2F982;CJK COMPATIBILITY IDEOGRAPH-2F982;Lo;0;L;80B2;;;;N;;;;;
+2F983;CJK COMPATIBILITY IDEOGRAPH-2F983;Lo;0;L;8103;;;;N;;;;;
+2F984;CJK COMPATIBILITY IDEOGRAPH-2F984;Lo;0;L;440B;;;;N;;;;;
+2F985;CJK COMPATIBILITY IDEOGRAPH-2F985;Lo;0;L;813E;;;;N;;;;;
+2F986;CJK COMPATIBILITY IDEOGRAPH-2F986;Lo;0;L;5AB5;;;;N;;;;;
+2F987;CJK COMPATIBILITY IDEOGRAPH-2F987;Lo;0;L;267A7;;;;N;;;;;
+2F988;CJK COMPATIBILITY IDEOGRAPH-2F988;Lo;0;L;267B5;;;;N;;;;;
+2F989;CJK COMPATIBILITY IDEOGRAPH-2F989;Lo;0;L;23393;;;;N;;;;;
+2F98A;CJK COMPATIBILITY IDEOGRAPH-2F98A;Lo;0;L;2339C;;;;N;;;;;
+2F98B;CJK COMPATIBILITY IDEOGRAPH-2F98B;Lo;0;L;8201;;;;N;;;;;
+2F98C;CJK COMPATIBILITY IDEOGRAPH-2F98C;Lo;0;L;8204;;;;N;;;;;
+2F98D;CJK COMPATIBILITY IDEOGRAPH-2F98D;Lo;0;L;8F9E;;;;N;;;;;
+2F98E;CJK COMPATIBILITY IDEOGRAPH-2F98E;Lo;0;L;446B;;;;N;;;;;
+2F98F;CJK COMPATIBILITY IDEOGRAPH-2F98F;Lo;0;L;8291;;;;N;;;;;
+2F990;CJK COMPATIBILITY IDEOGRAPH-2F990;Lo;0;L;828B;;;;N;;;;;
+2F991;CJK COMPATIBILITY IDEOGRAPH-2F991;Lo;0;L;829D;;;;N;;;;;
+2F992;CJK COMPATIBILITY IDEOGRAPH-2F992;Lo;0;L;52B3;;;;N;;;;;
+2F993;CJK COMPATIBILITY IDEOGRAPH-2F993;Lo;0;L;82B1;;;;N;;;;;
+2F994;CJK COMPATIBILITY IDEOGRAPH-2F994;Lo;0;L;82B3;;;;N;;;;;
+2F995;CJK COMPATIBILITY IDEOGRAPH-2F995;Lo;0;L;82BD;;;;N;;;;;
+2F996;CJK COMPATIBILITY IDEOGRAPH-2F996;Lo;0;L;82E6;;;;N;;;;;
+2F997;CJK COMPATIBILITY IDEOGRAPH-2F997;Lo;0;L;26B3C;;;;N;;;;;
+2F998;CJK COMPATIBILITY IDEOGRAPH-2F998;Lo;0;L;82E5;;;;N;;;;;
+2F999;CJK COMPATIBILITY IDEOGRAPH-2F999;Lo;0;L;831D;;;;N;;;;;
+2F99A;CJK COMPATIBILITY IDEOGRAPH-2F99A;Lo;0;L;8363;;;;N;;;;;
+2F99B;CJK COMPATIBILITY IDEOGRAPH-2F99B;Lo;0;L;83AD;;;;N;;;;;
+2F99C;CJK COMPATIBILITY IDEOGRAPH-2F99C;Lo;0;L;8323;;;;N;;;;;
+2F99D;CJK COMPATIBILITY IDEOGRAPH-2F99D;Lo;0;L;83BD;;;;N;;;;;
+2F99E;CJK COMPATIBILITY IDEOGRAPH-2F99E;Lo;0;L;83E7;;;;N;;;;;
+2F99F;CJK COMPATIBILITY IDEOGRAPH-2F99F;Lo;0;L;8457;;;;N;;;;;
+2F9A0;CJK COMPATIBILITY IDEOGRAPH-2F9A0;Lo;0;L;8353;;;;N;;;;;
+2F9A1;CJK COMPATIBILITY IDEOGRAPH-2F9A1;Lo;0;L;83CA;;;;N;;;;;
+2F9A2;CJK COMPATIBILITY IDEOGRAPH-2F9A2;Lo;0;L;83CC;;;;N;;;;;
+2F9A3;CJK COMPATIBILITY IDEOGRAPH-2F9A3;Lo;0;L;83DC;;;;N;;;;;
+2F9A4;CJK COMPATIBILITY IDEOGRAPH-2F9A4;Lo;0;L;26C36;;;;N;;;;;
+2F9A5;CJK COMPATIBILITY IDEOGRAPH-2F9A5;Lo;0;L;26D6B;;;;N;;;;;
+2F9A6;CJK COMPATIBILITY IDEOGRAPH-2F9A6;Lo;0;L;26CD5;;;;N;;;;;
+2F9A7;CJK COMPATIBILITY IDEOGRAPH-2F9A7;Lo;0;L;452B;;;;N;;;;;
+2F9A8;CJK COMPATIBILITY IDEOGRAPH-2F9A8;Lo;0;L;84F1;;;;N;;;;;
+2F9A9;CJK COMPATIBILITY IDEOGRAPH-2F9A9;Lo;0;L;84F3;;;;N;;;;;
+2F9AA;CJK COMPATIBILITY IDEOGRAPH-2F9AA;Lo;0;L;8516;;;;N;;;;;
+2F9AB;CJK COMPATIBILITY IDEOGRAPH-2F9AB;Lo;0;L;273CA;;;;N;;;;;
+2F9AC;CJK COMPATIBILITY IDEOGRAPH-2F9AC;Lo;0;L;8564;;;;N;;;;;
+2F9AD;CJK COMPATIBILITY IDEOGRAPH-2F9AD;Lo;0;L;26F2C;;;;N;;;;;
+2F9AE;CJK COMPATIBILITY IDEOGRAPH-2F9AE;Lo;0;L;455D;;;;N;;;;;
+2F9AF;CJK COMPATIBILITY IDEOGRAPH-2F9AF;Lo;0;L;4561;;;;N;;;;;
+2F9B0;CJK COMPATIBILITY IDEOGRAPH-2F9B0;Lo;0;L;26FB1;;;;N;;;;;
+2F9B1;CJK COMPATIBILITY IDEOGRAPH-2F9B1;Lo;0;L;270D2;;;;N;;;;;
+2F9B2;CJK COMPATIBILITY IDEOGRAPH-2F9B2;Lo;0;L;456B;;;;N;;;;;
+2F9B3;CJK COMPATIBILITY IDEOGRAPH-2F9B3;Lo;0;L;8650;;;;N;;;;;
+2F9B4;CJK COMPATIBILITY IDEOGRAPH-2F9B4;Lo;0;L;865C;;;;N;;;;;
+2F9B5;CJK COMPATIBILITY IDEOGRAPH-2F9B5;Lo;0;L;8667;;;;N;;;;;
+2F9B6;CJK COMPATIBILITY IDEOGRAPH-2F9B6;Lo;0;L;8669;;;;N;;;;;
+2F9B7;CJK COMPATIBILITY IDEOGRAPH-2F9B7;Lo;0;L;86A9;;;;N;;;;;
+2F9B8;CJK COMPATIBILITY IDEOGRAPH-2F9B8;Lo;0;L;8688;;;;N;;;;;
+2F9B9;CJK COMPATIBILITY IDEOGRAPH-2F9B9;Lo;0;L;870E;;;;N;;;;;
+2F9BA;CJK COMPATIBILITY IDEOGRAPH-2F9BA;Lo;0;L;86E2;;;;N;;;;;
+2F9BB;CJK COMPATIBILITY IDEOGRAPH-2F9BB;Lo;0;L;8779;;;;N;;;;;
+2F9BC;CJK COMPATIBILITY IDEOGRAPH-2F9BC;Lo;0;L;8728;;;;N;;;;;
+2F9BD;CJK COMPATIBILITY IDEOGRAPH-2F9BD;Lo;0;L;876B;;;;N;;;;;
+2F9BE;CJK COMPATIBILITY IDEOGRAPH-2F9BE;Lo;0;L;8786;;;;N;;;;;
+2F9BF;CJK COMPATIBILITY IDEOGRAPH-2F9BF;Lo;0;L;45D7;;;;N;;;;;
+2F9C0;CJK COMPATIBILITY IDEOGRAPH-2F9C0;Lo;0;L;87E1;;;;N;;;;;
+2F9C1;CJK COMPATIBILITY IDEOGRAPH-2F9C1;Lo;0;L;8801;;;;N;;;;;
+2F9C2;CJK COMPATIBILITY IDEOGRAPH-2F9C2;Lo;0;L;45F9;;;;N;;;;;
+2F9C3;CJK COMPATIBILITY IDEOGRAPH-2F9C3;Lo;0;L;8860;;;;N;;;;;
+2F9C4;CJK COMPATIBILITY IDEOGRAPH-2F9C4;Lo;0;L;8863;;;;N;;;;;
+2F9C5;CJK COMPATIBILITY IDEOGRAPH-2F9C5;Lo;0;L;27667;;;;N;;;;;
+2F9C6;CJK COMPATIBILITY IDEOGRAPH-2F9C6;Lo;0;L;88D7;;;;N;;;;;
+2F9C7;CJK COMPATIBILITY IDEOGRAPH-2F9C7;Lo;0;L;88DE;;;;N;;;;;
+2F9C8;CJK COMPATIBILITY IDEOGRAPH-2F9C8;Lo;0;L;4635;;;;N;;;;;
+2F9C9;CJK COMPATIBILITY IDEOGRAPH-2F9C9;Lo;0;L;88FA;;;;N;;;;;
+2F9CA;CJK COMPATIBILITY IDEOGRAPH-2F9CA;Lo;0;L;34BB;;;;N;;;;;
+2F9CB;CJK COMPATIBILITY IDEOGRAPH-2F9CB;Lo;0;L;278AE;;;;N;;;;;
+2F9CC;CJK COMPATIBILITY IDEOGRAPH-2F9CC;Lo;0;L;27966;;;;N;;;;;
+2F9CD;CJK COMPATIBILITY IDEOGRAPH-2F9CD;Lo;0;L;46BE;;;;N;;;;;
+2F9CE;CJK COMPATIBILITY IDEOGRAPH-2F9CE;Lo;0;L;46C7;;;;N;;;;;
+2F9CF;CJK COMPATIBILITY IDEOGRAPH-2F9CF;Lo;0;L;8AA0;;;;N;;;;;
+2F9D0;CJK COMPATIBILITY IDEOGRAPH-2F9D0;Lo;0;L;8AED;;;;N;;;;;
+2F9D1;CJK COMPATIBILITY IDEOGRAPH-2F9D1;Lo;0;L;8B8A;;;;N;;;;;
+2F9D2;CJK COMPATIBILITY IDEOGRAPH-2F9D2;Lo;0;L;8C55;;;;N;;;;;
+2F9D3;CJK COMPATIBILITY IDEOGRAPH-2F9D3;Lo;0;L;27CA8;;;;N;;;;;
+2F9D4;CJK COMPATIBILITY IDEOGRAPH-2F9D4;Lo;0;L;8CAB;;;;N;;;;;
+2F9D5;CJK COMPATIBILITY IDEOGRAPH-2F9D5;Lo;0;L;8CC1;;;;N;;;;;
+2F9D6;CJK COMPATIBILITY IDEOGRAPH-2F9D6;Lo;0;L;8D1B;;;;N;;;;;
+2F9D7;CJK COMPATIBILITY IDEOGRAPH-2F9D7;Lo;0;L;8D77;;;;N;;;;;
+2F9D8;CJK COMPATIBILITY IDEOGRAPH-2F9D8;Lo;0;L;27F2F;;;;N;;;;;
+2F9D9;CJK COMPATIBILITY IDEOGRAPH-2F9D9;Lo;0;L;20804;;;;N;;;;;
+2F9DA;CJK COMPATIBILITY IDEOGRAPH-2F9DA;Lo;0;L;8DCB;;;;N;;;;;
+2F9DB;CJK COMPATIBILITY IDEOGRAPH-2F9DB;Lo;0;L;8DBC;;;;N;;;;;
+2F9DC;CJK COMPATIBILITY IDEOGRAPH-2F9DC;Lo;0;L;8DF0;;;;N;;;;;
+2F9DD;CJK COMPATIBILITY IDEOGRAPH-2F9DD;Lo;0;L;208DE;;;;N;;;;;
+2F9DE;CJK COMPATIBILITY IDEOGRAPH-2F9DE;Lo;0;L;8ED4;;;;N;;;;;
+2F9DF;CJK COMPATIBILITY IDEOGRAPH-2F9DF;Lo;0;L;8F38;;;;N;;;;;
+2F9E0;CJK COMPATIBILITY IDEOGRAPH-2F9E0;Lo;0;L;285D2;;;;N;;;;;
+2F9E1;CJK COMPATIBILITY IDEOGRAPH-2F9E1;Lo;0;L;285ED;;;;N;;;;;
+2F9E2;CJK COMPATIBILITY IDEOGRAPH-2F9E2;Lo;0;L;9094;;;;N;;;;;
+2F9E3;CJK COMPATIBILITY IDEOGRAPH-2F9E3;Lo;0;L;90F1;;;;N;;;;;
+2F9E4;CJK COMPATIBILITY IDEOGRAPH-2F9E4;Lo;0;L;9111;;;;N;;;;;
+2F9E5;CJK COMPATIBILITY IDEOGRAPH-2F9E5;Lo;0;L;2872E;;;;N;;;;;
+2F9E6;CJK COMPATIBILITY IDEOGRAPH-2F9E6;Lo;0;L;911B;;;;N;;;;;
+2F9E7;CJK COMPATIBILITY IDEOGRAPH-2F9E7;Lo;0;L;9238;;;;N;;;;;
+2F9E8;CJK COMPATIBILITY IDEOGRAPH-2F9E8;Lo;0;L;92D7;;;;N;;;;;
+2F9E9;CJK COMPATIBILITY IDEOGRAPH-2F9E9;Lo;0;L;92D8;;;;N;;;;;
+2F9EA;CJK COMPATIBILITY IDEOGRAPH-2F9EA;Lo;0;L;927C;;;;N;;;;;
+2F9EB;CJK COMPATIBILITY IDEOGRAPH-2F9EB;Lo;0;L;93F9;;;;N;;;;;
+2F9EC;CJK COMPATIBILITY IDEOGRAPH-2F9EC;Lo;0;L;9415;;;;N;;;;;
+2F9ED;CJK COMPATIBILITY IDEOGRAPH-2F9ED;Lo;0;L;28BFA;;;;N;;;;;
+2F9EE;CJK COMPATIBILITY IDEOGRAPH-2F9EE;Lo;0;L;958B;;;;N;;;;;
+2F9EF;CJK COMPATIBILITY IDEOGRAPH-2F9EF;Lo;0;L;4995;;;;N;;;;;
+2F9F0;CJK COMPATIBILITY IDEOGRAPH-2F9F0;Lo;0;L;95B7;;;;N;;;;;
+2F9F1;CJK COMPATIBILITY IDEOGRAPH-2F9F1;Lo;0;L;28D77;;;;N;;;;;
+2F9F2;CJK COMPATIBILITY IDEOGRAPH-2F9F2;Lo;0;L;49E6;;;;N;;;;;
+2F9F3;CJK COMPATIBILITY IDEOGRAPH-2F9F3;Lo;0;L;96C3;;;;N;;;;;
+2F9F4;CJK COMPATIBILITY IDEOGRAPH-2F9F4;Lo;0;L;5DB2;;;;N;;;;;
+2F9F5;CJK COMPATIBILITY IDEOGRAPH-2F9F5;Lo;0;L;9723;;;;N;;;;;
+2F9F6;CJK COMPATIBILITY IDEOGRAPH-2F9F6;Lo;0;L;29145;;;;N;;;;;
+2F9F7;CJK COMPATIBILITY IDEOGRAPH-2F9F7;Lo;0;L;2921A;;;;N;;;;;
+2F9F8;CJK COMPATIBILITY IDEOGRAPH-2F9F8;Lo;0;L;4A6E;;;;N;;;;;
+2F9F9;CJK COMPATIBILITY IDEOGRAPH-2F9F9;Lo;0;L;4A76;;;;N;;;;;
+2F9FA;CJK COMPATIBILITY IDEOGRAPH-2F9FA;Lo;0;L;97E0;;;;N;;;;;
+2F9FB;CJK COMPATIBILITY IDEOGRAPH-2F9FB;Lo;0;L;2940A;;;;N;;;;;
+2F9FC;CJK COMPATIBILITY IDEOGRAPH-2F9FC;Lo;0;L;4AB2;;;;N;;;;;
+2F9FD;CJK COMPATIBILITY IDEOGRAPH-2F9FD;Lo;0;L;29496;;;;N;;;;;
+2F9FE;CJK COMPATIBILITY IDEOGRAPH-2F9FE;Lo;0;L;980B;;;;N;;;;;
+2F9FF;CJK COMPATIBILITY IDEOGRAPH-2F9FF;Lo;0;L;980B;;;;N;;;;;
+2FA00;CJK COMPATIBILITY IDEOGRAPH-2FA00;Lo;0;L;9829;;;;N;;;;;
+2FA01;CJK COMPATIBILITY IDEOGRAPH-2FA01;Lo;0;L;295B6;;;;N;;;;;
+2FA02;CJK COMPATIBILITY IDEOGRAPH-2FA02;Lo;0;L;98E2;;;;N;;;;;
+2FA03;CJK COMPATIBILITY IDEOGRAPH-2FA03;Lo;0;L;4B33;;;;N;;;;;
+2FA04;CJK COMPATIBILITY IDEOGRAPH-2FA04;Lo;0;L;9929;;;;N;;;;;
+2FA05;CJK COMPATIBILITY IDEOGRAPH-2FA05;Lo;0;L;99A7;;;;N;;;;;
+2FA06;CJK COMPATIBILITY IDEOGRAPH-2FA06;Lo;0;L;99C2;;;;N;;;;;
+2FA07;CJK COMPATIBILITY IDEOGRAPH-2FA07;Lo;0;L;99FE;;;;N;;;;;
+2FA08;CJK COMPATIBILITY IDEOGRAPH-2FA08;Lo;0;L;4BCE;;;;N;;;;;
+2FA09;CJK COMPATIBILITY IDEOGRAPH-2FA09;Lo;0;L;29B30;;;;N;;;;;
+2FA0A;CJK COMPATIBILITY IDEOGRAPH-2FA0A;Lo;0;L;9B12;;;;N;;;;;
+2FA0B;CJK COMPATIBILITY IDEOGRAPH-2FA0B;Lo;0;L;9C40;;;;N;;;;;
+2FA0C;CJK COMPATIBILITY IDEOGRAPH-2FA0C;Lo;0;L;9CFD;;;;N;;;;;
+2FA0D;CJK COMPATIBILITY IDEOGRAPH-2FA0D;Lo;0;L;4CCE;;;;N;;;;;
+2FA0E;CJK COMPATIBILITY IDEOGRAPH-2FA0E;Lo;0;L;4CED;;;;N;;;;;
+2FA0F;CJK COMPATIBILITY IDEOGRAPH-2FA0F;Lo;0;L;9D67;;;;N;;;;;
+2FA10;CJK COMPATIBILITY IDEOGRAPH-2FA10;Lo;0;L;2A0CE;;;;N;;;;;
+2FA11;CJK COMPATIBILITY IDEOGRAPH-2FA11;Lo;0;L;4CF8;;;;N;;;;;
+2FA12;CJK COMPATIBILITY IDEOGRAPH-2FA12;Lo;0;L;2A105;;;;N;;;;;
+2FA13;CJK COMPATIBILITY IDEOGRAPH-2FA13;Lo;0;L;2A20E;;;;N;;;;;
+2FA14;CJK COMPATIBILITY IDEOGRAPH-2FA14;Lo;0;L;2A291;;;;N;;;;;
+2FA15;CJK COMPATIBILITY IDEOGRAPH-2FA15;Lo;0;L;9EBB;;;;N;;;;;
+2FA16;CJK COMPATIBILITY IDEOGRAPH-2FA16;Lo;0;L;4D56;;;;N;;;;;
+2FA17;CJK COMPATIBILITY IDEOGRAPH-2FA17;Lo;0;L;9EF9;;;;N;;;;;
+2FA18;CJK COMPATIBILITY IDEOGRAPH-2FA18;Lo;0;L;9EFE;;;;N;;;;;
+2FA19;CJK COMPATIBILITY IDEOGRAPH-2FA19;Lo;0;L;9F05;;;;N;;;;;
+2FA1A;CJK COMPATIBILITY IDEOGRAPH-2FA1A;Lo;0;L;9F0F;;;;N;;;;;
+2FA1B;CJK COMPATIBILITY IDEOGRAPH-2FA1B;Lo;0;L;9F16;;;;N;;;;;
+2FA1C;CJK COMPATIBILITY IDEOGRAPH-2FA1C;Lo;0;L;9F3B;;;;N;;;;;
+2FA1D;CJK COMPATIBILITY IDEOGRAPH-2FA1D;Lo;0;L;2A600;;;;N;;;;;
+E0001;LANGUAGE TAG;Cf;0;BN;;;;;N;;;;;
+E0020;TAG SPACE;Cf;0;BN;;;;;N;;;;;
+E0021;TAG EXCLAMATION MARK;Cf;0;BN;;;;;N;;;;;
+E0022;TAG QUOTATION MARK;Cf;0;BN;;;;;N;;;;;
+E0023;TAG NUMBER SIGN;Cf;0;BN;;;;;N;;;;;
+E0024;TAG DOLLAR SIGN;Cf;0;BN;;;;;N;;;;;
+E0025;TAG PERCENT SIGN;Cf;0;BN;;;;;N;;;;;
+E0026;TAG AMPERSAND;Cf;0;BN;;;;;N;;;;;
+E0027;TAG APOSTROPHE;Cf;0;BN;;;;;N;;;;;
+E0028;TAG LEFT PARENTHESIS;Cf;0;BN;;;;;N;;;;;
+E0029;TAG RIGHT PARENTHESIS;Cf;0;BN;;;;;N;;;;;
+E002A;TAG ASTERISK;Cf;0;BN;;;;;N;;;;;
+E002B;TAG PLUS SIGN;Cf;0;BN;;;;;N;;;;;
+E002C;TAG COMMA;Cf;0;BN;;;;;N;;;;;
+E002D;TAG HYPHEN-MINUS;Cf;0;BN;;;;;N;;;;;
+E002E;TAG FULL STOP;Cf;0;BN;;;;;N;;;;;
+E002F;TAG SOLIDUS;Cf;0;BN;;;;;N;;;;;
+E0030;TAG DIGIT ZERO;Cf;0;BN;;;;;N;;;;;
+E0031;TAG DIGIT ONE;Cf;0;BN;;;;;N;;;;;
+E0032;TAG DIGIT TWO;Cf;0;BN;;;;;N;;;;;
+E0033;TAG DIGIT THREE;Cf;0;BN;;;;;N;;;;;
+E0034;TAG DIGIT FOUR;Cf;0;BN;;;;;N;;;;;
+E0035;TAG DIGIT FIVE;Cf;0;BN;;;;;N;;;;;
+E0036;TAG DIGIT SIX;Cf;0;BN;;;;;N;;;;;
+E0037;TAG DIGIT SEVEN;Cf;0;BN;;;;;N;;;;;
+E0038;TAG DIGIT EIGHT;Cf;0;BN;;;;;N;;;;;
+E0039;TAG DIGIT NINE;Cf;0;BN;;;;;N;;;;;
+E003A;TAG COLON;Cf;0;BN;;;;;N;;;;;
+E003B;TAG SEMICOLON;Cf;0;BN;;;;;N;;;;;
+E003C;TAG LESS-THAN SIGN;Cf;0;BN;;;;;N;;;;;
+E003D;TAG EQUALS SIGN;Cf;0;BN;;;;;N;;;;;
+E003E;TAG GREATER-THAN SIGN;Cf;0;BN;;;;;N;;;;;
+E003F;TAG QUESTION MARK;Cf;0;BN;;;;;N;;;;;
+E0040;TAG COMMERCIAL AT;Cf;0;BN;;;;;N;;;;;
+E0041;TAG LATIN CAPITAL LETTER A;Cf;0;BN;;;;;N;;;;;
+E0042;TAG LATIN CAPITAL LETTER B;Cf;0;BN;;;;;N;;;;;
+E0043;TAG LATIN CAPITAL LETTER C;Cf;0;BN;;;;;N;;;;;
+E0044;TAG LATIN CAPITAL LETTER D;Cf;0;BN;;;;;N;;;;;
+E0045;TAG LATIN CAPITAL LETTER E;Cf;0;BN;;;;;N;;;;;
+E0046;TAG LATIN CAPITAL LETTER F;Cf;0;BN;;;;;N;;;;;
+E0047;TAG LATIN CAPITAL LETTER G;Cf;0;BN;;;;;N;;;;;
+E0048;TAG LATIN CAPITAL LETTER H;Cf;0;BN;;;;;N;;;;;
+E0049;TAG LATIN CAPITAL LETTER I;Cf;0;BN;;;;;N;;;;;
+E004A;TAG LATIN CAPITAL LETTER J;Cf;0;BN;;;;;N;;;;;
+E004B;TAG LATIN CAPITAL LETTER K;Cf;0;BN;;;;;N;;;;;
+E004C;TAG LATIN CAPITAL LETTER L;Cf;0;BN;;;;;N;;;;;
+E004D;TAG LATIN CAPITAL LETTER M;Cf;0;BN;;;;;N;;;;;
+E004E;TAG LATIN CAPITAL LETTER N;Cf;0;BN;;;;;N;;;;;
+E004F;TAG LATIN CAPITAL LETTER O;Cf;0;BN;;;;;N;;;;;
+E0050;TAG LATIN CAPITAL LETTER P;Cf;0;BN;;;;;N;;;;;
+E0051;TAG LATIN CAPITAL LETTER Q;Cf;0;BN;;;;;N;;;;;
+E0052;TAG LATIN CAPITAL LETTER R;Cf;0;BN;;;;;N;;;;;
+E0053;TAG LATIN CAPITAL LETTER S;Cf;0;BN;;;;;N;;;;;
+E0054;TAG LATIN CAPITAL LETTER T;Cf;0;BN;;;;;N;;;;;
+E0055;TAG LATIN CAPITAL LETTER U;Cf;0;BN;;;;;N;;;;;
+E0056;TAG LATIN CAPITAL LETTER V;Cf;0;BN;;;;;N;;;;;
+E0057;TAG LATIN CAPITAL LETTER W;Cf;0;BN;;;;;N;;;;;
+E0058;TAG LATIN CAPITAL LETTER X;Cf;0;BN;;;;;N;;;;;
+E0059;TAG LATIN CAPITAL LETTER Y;Cf;0;BN;;;;;N;;;;;
+E005A;TAG LATIN CAPITAL LETTER Z;Cf;0;BN;;;;;N;;;;;
+E005B;TAG LEFT SQUARE BRACKET;Cf;0;BN;;;;;N;;;;;
+E005C;TAG REVERSE SOLIDUS;Cf;0;BN;;;;;N;;;;;
+E005D;TAG RIGHT SQUARE BRACKET;Cf;0;BN;;;;;N;;;;;
+E005E;TAG CIRCUMFLEX ACCENT;Cf;0;BN;;;;;N;;;;;
+E005F;TAG LOW LINE;Cf;0;BN;;;;;N;;;;;
+E0060;TAG GRAVE ACCENT;Cf;0;BN;;;;;N;;;;;
+E0061;TAG LATIN SMALL LETTER A;Cf;0;BN;;;;;N;;;;;
+E0062;TAG LATIN SMALL LETTER B;Cf;0;BN;;;;;N;;;;;
+E0063;TAG LATIN SMALL LETTER C;Cf;0;BN;;;;;N;;;;;
+E0064;TAG LATIN SMALL LETTER D;Cf;0;BN;;;;;N;;;;;
+E0065;TAG LATIN SMALL LETTER E;Cf;0;BN;;;;;N;;;;;
+E0066;TAG LATIN SMALL LETTER F;Cf;0;BN;;;;;N;;;;;
+E0067;TAG LATIN SMALL LETTER G;Cf;0;BN;;;;;N;;;;;
+E0068;TAG LATIN SMALL LETTER H;Cf;0;BN;;;;;N;;;;;
+E0069;TAG LATIN SMALL LETTER I;Cf;0;BN;;;;;N;;;;;
+E006A;TAG LATIN SMALL LETTER J;Cf;0;BN;;;;;N;;;;;
+E006B;TAG LATIN SMALL LETTER K;Cf;0;BN;;;;;N;;;;;
+E006C;TAG LATIN SMALL LETTER L;Cf;0;BN;;;;;N;;;;;
+E006D;TAG LATIN SMALL LETTER M;Cf;0;BN;;;;;N;;;;;
+E006E;TAG LATIN SMALL LETTER N;Cf;0;BN;;;;;N;;;;;
+E006F;TAG LATIN SMALL LETTER O;Cf;0;BN;;;;;N;;;;;
+E0070;TAG LATIN SMALL LETTER P;Cf;0;BN;;;;;N;;;;;
+E0071;TAG LATIN SMALL LETTER Q;Cf;0;BN;;;;;N;;;;;
+E0072;TAG LATIN SMALL LETTER R;Cf;0;BN;;;;;N;;;;;
+E0073;TAG LATIN SMALL LETTER S;Cf;0;BN;;;;;N;;;;;
+E0074;TAG LATIN SMALL LETTER T;Cf;0;BN;;;;;N;;;;;
+E0075;TAG LATIN SMALL LETTER U;Cf;0;BN;;;;;N;;;;;
+E0076;TAG LATIN SMALL LETTER V;Cf;0;BN;;;;;N;;;;;
+E0077;TAG LATIN SMALL LETTER W;Cf;0;BN;;;;;N;;;;;
+E0078;TAG LATIN SMALL LETTER X;Cf;0;BN;;;;;N;;;;;
+E0079;TAG LATIN SMALL LETTER Y;Cf;0;BN;;;;;N;;;;;
+E007A;TAG LATIN SMALL LETTER Z;Cf;0;BN;;;;;N;;;;;
+E007B;TAG LEFT CURLY BRACKET;Cf;0;BN;;;;;N;;;;;
+E007C;TAG VERTICAL LINE;Cf;0;BN;;;;;N;;;;;
+E007D;TAG RIGHT CURLY BRACKET;Cf;0;BN;;;;;N;;;;;
+E007E;TAG TILDE;Cf;0;BN;;;;;N;;;;;
+E007F;CANCEL TAG;Cf;0;BN;;;;;N;;;;;
+E0100;VARIATION SELECTOR-17;Mn;0;NSM;;;;;N;;;;;
+E0101;VARIATION SELECTOR-18;Mn;0;NSM;;;;;N;;;;;
+E0102;VARIATION SELECTOR-19;Mn;0;NSM;;;;;N;;;;;
+E0103;VARIATION SELECTOR-20;Mn;0;NSM;;;;;N;;;;;
+E0104;VARIATION SELECTOR-21;Mn;0;NSM;;;;;N;;;;;
+E0105;VARIATION SELECTOR-22;Mn;0;NSM;;;;;N;;;;;
+E0106;VARIATION SELECTOR-23;Mn;0;NSM;;;;;N;;;;;
+E0107;VARIATION SELECTOR-24;Mn;0;NSM;;;;;N;;;;;
+E0108;VARIATION SELECTOR-25;Mn;0;NSM;;;;;N;;;;;
+E0109;VARIATION SELECTOR-26;Mn;0;NSM;;;;;N;;;;;
+E010A;VARIATION SELECTOR-27;Mn;0;NSM;;;;;N;;;;;
+E010B;VARIATION SELECTOR-28;Mn;0;NSM;;;;;N;;;;;
+E010C;VARIATION SELECTOR-29;Mn;0;NSM;;;;;N;;;;;
+E010D;VARIATION SELECTOR-30;Mn;0;NSM;;;;;N;;;;;
+E010E;VARIATION SELECTOR-31;Mn;0;NSM;;;;;N;;;;;
+E010F;VARIATION SELECTOR-32;Mn;0;NSM;;;;;N;;;;;
+E0110;VARIATION SELECTOR-33;Mn;0;NSM;;;;;N;;;;;
+E0111;VARIATION SELECTOR-34;Mn;0;NSM;;;;;N;;;;;
+E0112;VARIATION SELECTOR-35;Mn;0;NSM;;;;;N;;;;;
+E0113;VARIATION SELECTOR-36;Mn;0;NSM;;;;;N;;;;;
+E0114;VARIATION SELECTOR-37;Mn;0;NSM;;;;;N;;;;;
+E0115;VARIATION SELECTOR-38;Mn;0;NSM;;;;;N;;;;;
+E0116;VARIATION SELECTOR-39;Mn;0;NSM;;;;;N;;;;;
+E0117;VARIATION SELECTOR-40;Mn;0;NSM;;;;;N;;;;;
+E0118;VARIATION SELECTOR-41;Mn;0;NSM;;;;;N;;;;;
+E0119;VARIATION SELECTOR-42;Mn;0;NSM;;;;;N;;;;;
+E011A;VARIATION SELECTOR-43;Mn;0;NSM;;;;;N;;;;;
+E011B;VARIATION SELECTOR-44;Mn;0;NSM;;;;;N;;;;;
+E011C;VARIATION SELECTOR-45;Mn;0;NSM;;;;;N;;;;;
+E011D;VARIATION SELECTOR-46;Mn;0;NSM;;;;;N;;;;;
+E011E;VARIATION SELECTOR-47;Mn;0;NSM;;;;;N;;;;;
+E011F;VARIATION SELECTOR-48;Mn;0;NSM;;;;;N;;;;;
+E0120;VARIATION SELECTOR-49;Mn;0;NSM;;;;;N;;;;;
+E0121;VARIATION SELECTOR-50;Mn;0;NSM;;;;;N;;;;;
+E0122;VARIATION SELECTOR-51;Mn;0;NSM;;;;;N;;;;;
+E0123;VARIATION SELECTOR-52;Mn;0;NSM;;;;;N;;;;;
+E0124;VARIATION SELECTOR-53;Mn;0;NSM;;;;;N;;;;;
+E0125;VARIATION SELECTOR-54;Mn;0;NSM;;;;;N;;;;;
+E0126;VARIATION SELECTOR-55;Mn;0;NSM;;;;;N;;;;;
+E0127;VARIATION SELECTOR-56;Mn;0;NSM;;;;;N;;;;;
+E0128;VARIATION SELECTOR-57;Mn;0;NSM;;;;;N;;;;;
+E0129;VARIATION SELECTOR-58;Mn;0;NSM;;;;;N;;;;;
+E012A;VARIATION SELECTOR-59;Mn;0;NSM;;;;;N;;;;;
+E012B;VARIATION SELECTOR-60;Mn;0;NSM;;;;;N;;;;;
+E012C;VARIATION SELECTOR-61;Mn;0;NSM;;;;;N;;;;;
+E012D;VARIATION SELECTOR-62;Mn;0;NSM;;;;;N;;;;;
+E012E;VARIATION SELECTOR-63;Mn;0;NSM;;;;;N;;;;;
+E012F;VARIATION SELECTOR-64;Mn;0;NSM;;;;;N;;;;;
+E0130;VARIATION SELECTOR-65;Mn;0;NSM;;;;;N;;;;;
+E0131;VARIATION SELECTOR-66;Mn;0;NSM;;;;;N;;;;;
+E0132;VARIATION SELECTOR-67;Mn;0;NSM;;;;;N;;;;;
+E0133;VARIATION SELECTOR-68;Mn;0;NSM;;;;;N;;;;;
+E0134;VARIATION SELECTOR-69;Mn;0;NSM;;;;;N;;;;;
+E0135;VARIATION SELECTOR-70;Mn;0;NSM;;;;;N;;;;;
+E0136;VARIATION SELECTOR-71;Mn;0;NSM;;;;;N;;;;;
+E0137;VARIATION SELECTOR-72;Mn;0;NSM;;;;;N;;;;;
+E0138;VARIATION SELECTOR-73;Mn;0;NSM;;;;;N;;;;;
+E0139;VARIATION SELECTOR-74;Mn;0;NSM;;;;;N;;;;;
+E013A;VARIATION SELECTOR-75;Mn;0;NSM;;;;;N;;;;;
+E013B;VARIATION SELECTOR-76;Mn;0;NSM;;;;;N;;;;;
+E013C;VARIATION SELECTOR-77;Mn;0;NSM;;;;;N;;;;;
+E013D;VARIATION SELECTOR-78;Mn;0;NSM;;;;;N;;;;;
+E013E;VARIATION SELECTOR-79;Mn;0;NSM;;;;;N;;;;;
+E013F;VARIATION SELECTOR-80;Mn;0;NSM;;;;;N;;;;;
+E0140;VARIATION SELECTOR-81;Mn;0;NSM;;;;;N;;;;;
+E0141;VARIATION SELECTOR-82;Mn;0;NSM;;;;;N;;;;;
+E0142;VARIATION SELECTOR-83;Mn;0;NSM;;;;;N;;;;;
+E0143;VARIATION SELECTOR-84;Mn;0;NSM;;;;;N;;;;;
+E0144;VARIATION SELECTOR-85;Mn;0;NSM;;;;;N;;;;;
+E0145;VARIATION SELECTOR-86;Mn;0;NSM;;;;;N;;;;;
+E0146;VARIATION SELECTOR-87;Mn;0;NSM;;;;;N;;;;;
+E0147;VARIATION SELECTOR-88;Mn;0;NSM;;;;;N;;;;;
+E0148;VARIATION SELECTOR-89;Mn;0;NSM;;;;;N;;;;;
+E0149;VARIATION SELECTOR-90;Mn;0;NSM;;;;;N;;;;;
+E014A;VARIATION SELECTOR-91;Mn;0;NSM;;;;;N;;;;;
+E014B;VARIATION SELECTOR-92;Mn;0;NSM;;;;;N;;;;;
+E014C;VARIATION SELECTOR-93;Mn;0;NSM;;;;;N;;;;;
+E014D;VARIATION SELECTOR-94;Mn;0;NSM;;;;;N;;;;;
+E014E;VARIATION SELECTOR-95;Mn;0;NSM;;;;;N;;;;;
+E014F;VARIATION SELECTOR-96;Mn;0;NSM;;;;;N;;;;;
+E0150;VARIATION SELECTOR-97;Mn;0;NSM;;;;;N;;;;;
+E0151;VARIATION SELECTOR-98;Mn;0;NSM;;;;;N;;;;;
+E0152;VARIATION SELECTOR-99;Mn;0;NSM;;;;;N;;;;;
+E0153;VARIATION SELECTOR-100;Mn;0;NSM;;;;;N;;;;;
+E0154;VARIATION SELECTOR-101;Mn;0;NSM;;;;;N;;;;;
+E0155;VARIATION SELECTOR-102;Mn;0;NSM;;;;;N;;;;;
+E0156;VARIATION SELECTOR-103;Mn;0;NSM;;;;;N;;;;;
+E0157;VARIATION SELECTOR-104;Mn;0;NSM;;;;;N;;;;;
+E0158;VARIATION SELECTOR-105;Mn;0;NSM;;;;;N;;;;;
+E0159;VARIATION SELECTOR-106;Mn;0;NSM;;;;;N;;;;;
+E015A;VARIATION SELECTOR-107;Mn;0;NSM;;;;;N;;;;;
+E015B;VARIATION SELECTOR-108;Mn;0;NSM;;;;;N;;;;;
+E015C;VARIATION SELECTOR-109;Mn;0;NSM;;;;;N;;;;;
+E015D;VARIATION SELECTOR-110;Mn;0;NSM;;;;;N;;;;;
+E015E;VARIATION SELECTOR-111;Mn;0;NSM;;;;;N;;;;;
+E015F;VARIATION SELECTOR-112;Mn;0;NSM;;;;;N;;;;;
+E0160;VARIATION SELECTOR-113;Mn;0;NSM;;;;;N;;;;;
+E0161;VARIATION SELECTOR-114;Mn;0;NSM;;;;;N;;;;;
+E0162;VARIATION SELECTOR-115;Mn;0;NSM;;;;;N;;;;;
+E0163;VARIATION SELECTOR-116;Mn;0;NSM;;;;;N;;;;;
+E0164;VARIATION SELECTOR-117;Mn;0;NSM;;;;;N;;;;;
+E0165;VARIATION SELECTOR-118;Mn;0;NSM;;;;;N;;;;;
+E0166;VARIATION SELECTOR-119;Mn;0;NSM;;;;;N;;;;;
+E0167;VARIATION SELECTOR-120;Mn;0;NSM;;;;;N;;;;;
+E0168;VARIATION SELECTOR-121;Mn;0;NSM;;;;;N;;;;;
+E0169;VARIATION SELECTOR-122;Mn;0;NSM;;;;;N;;;;;
+E016A;VARIATION SELECTOR-123;Mn;0;NSM;;;;;N;;;;;
+E016B;VARIATION SELECTOR-124;Mn;0;NSM;;;;;N;;;;;
+E016C;VARIATION SELECTOR-125;Mn;0;NSM;;;;;N;;;;;
+E016D;VARIATION SELECTOR-126;Mn;0;NSM;;;;;N;;;;;
+E016E;VARIATION SELECTOR-127;Mn;0;NSM;;;;;N;;;;;
+E016F;VARIATION SELECTOR-128;Mn;0;NSM;;;;;N;;;;;
+E0170;VARIATION SELECTOR-129;Mn;0;NSM;;;;;N;;;;;
+E0171;VARIATION SELECTOR-130;Mn;0;NSM;;;;;N;;;;;
+E0172;VARIATION SELECTOR-131;Mn;0;NSM;;;;;N;;;;;
+E0173;VARIATION SELECTOR-132;Mn;0;NSM;;;;;N;;;;;
+E0174;VARIATION SELECTOR-133;Mn;0;NSM;;;;;N;;;;;
+E0175;VARIATION SELECTOR-134;Mn;0;NSM;;;;;N;;;;;
+E0176;VARIATION SELECTOR-135;Mn;0;NSM;;;;;N;;;;;
+E0177;VARIATION SELECTOR-136;Mn;0;NSM;;;;;N;;;;;
+E0178;VARIATION SELECTOR-137;Mn;0;NSM;;;;;N;;;;;
+E0179;VARIATION SELECTOR-138;Mn;0;NSM;;;;;N;;;;;
+E017A;VARIATION SELECTOR-139;Mn;0;NSM;;;;;N;;;;;
+E017B;VARIATION SELECTOR-140;Mn;0;NSM;;;;;N;;;;;
+E017C;VARIATION SELECTOR-141;Mn;0;NSM;;;;;N;;;;;
+E017D;VARIATION SELECTOR-142;Mn;0;NSM;;;;;N;;;;;
+E017E;VARIATION SELECTOR-143;Mn;0;NSM;;;;;N;;;;;
+E017F;VARIATION SELECTOR-144;Mn;0;NSM;;;;;N;;;;;
+E0180;VARIATION SELECTOR-145;Mn;0;NSM;;;;;N;;;;;
+E0181;VARIATION SELECTOR-146;Mn;0;NSM;;;;;N;;;;;
+E0182;VARIATION SELECTOR-147;Mn;0;NSM;;;;;N;;;;;
+E0183;VARIATION SELECTOR-148;Mn;0;NSM;;;;;N;;;;;
+E0184;VARIATION SELECTOR-149;Mn;0;NSM;;;;;N;;;;;
+E0185;VARIATION SELECTOR-150;Mn;0;NSM;;;;;N;;;;;
+E0186;VARIATION SELECTOR-151;Mn;0;NSM;;;;;N;;;;;
+E0187;VARIATION SELECTOR-152;Mn;0;NSM;;;;;N;;;;;
+E0188;VARIATION SELECTOR-153;Mn;0;NSM;;;;;N;;;;;
+E0189;VARIATION SELECTOR-154;Mn;0;NSM;;;;;N;;;;;
+E018A;VARIATION SELECTOR-155;Mn;0;NSM;;;;;N;;;;;
+E018B;VARIATION SELECTOR-156;Mn;0;NSM;;;;;N;;;;;
+E018C;VARIATION SELECTOR-157;Mn;0;NSM;;;;;N;;;;;
+E018D;VARIATION SELECTOR-158;Mn;0;NSM;;;;;N;;;;;
+E018E;VARIATION SELECTOR-159;Mn;0;NSM;;;;;N;;;;;
+E018F;VARIATION SELECTOR-160;Mn;0;NSM;;;;;N;;;;;
+E0190;VARIATION SELECTOR-161;Mn;0;NSM;;;;;N;;;;;
+E0191;VARIATION SELECTOR-162;Mn;0;NSM;;;;;N;;;;;
+E0192;VARIATION SELECTOR-163;Mn;0;NSM;;;;;N;;;;;
+E0193;VARIATION SELECTOR-164;Mn;0;NSM;;;;;N;;;;;
+E0194;VARIATION SELECTOR-165;Mn;0;NSM;;;;;N;;;;;
+E0195;VARIATION SELECTOR-166;Mn;0;NSM;;;;;N;;;;;
+E0196;VARIATION SELECTOR-167;Mn;0;NSM;;;;;N;;;;;
+E0197;VARIATION SELECTOR-168;Mn;0;NSM;;;;;N;;;;;
+E0198;VARIATION SELECTOR-169;Mn;0;NSM;;;;;N;;;;;
+E0199;VARIATION SELECTOR-170;Mn;0;NSM;;;;;N;;;;;
+E019A;VARIATION SELECTOR-171;Mn;0;NSM;;;;;N;;;;;
+E019B;VARIATION SELECTOR-172;Mn;0;NSM;;;;;N;;;;;
+E019C;VARIATION SELECTOR-173;Mn;0;NSM;;;;;N;;;;;
+E019D;VARIATION SELECTOR-174;Mn;0;NSM;;;;;N;;;;;
+E019E;VARIATION SELECTOR-175;Mn;0;NSM;;;;;N;;;;;
+E019F;VARIATION SELECTOR-176;Mn;0;NSM;;;;;N;;;;;
+E01A0;VARIATION SELECTOR-177;Mn;0;NSM;;;;;N;;;;;
+E01A1;VARIATION SELECTOR-178;Mn;0;NSM;;;;;N;;;;;
+E01A2;VARIATION SELECTOR-179;Mn;0;NSM;;;;;N;;;;;
+E01A3;VARIATION SELECTOR-180;Mn;0;NSM;;;;;N;;;;;
+E01A4;VARIATION SELECTOR-181;Mn;0;NSM;;;;;N;;;;;
+E01A5;VARIATION SELECTOR-182;Mn;0;NSM;;;;;N;;;;;
+E01A6;VARIATION SELECTOR-183;Mn;0;NSM;;;;;N;;;;;
+E01A7;VARIATION SELECTOR-184;Mn;0;NSM;;;;;N;;;;;
+E01A8;VARIATION SELECTOR-185;Mn;0;NSM;;;;;N;;;;;
+E01A9;VARIATION SELECTOR-186;Mn;0;NSM;;;;;N;;;;;
+E01AA;VARIATION SELECTOR-187;Mn;0;NSM;;;;;N;;;;;
+E01AB;VARIATION SELECTOR-188;Mn;0;NSM;;;;;N;;;;;
+E01AC;VARIATION SELECTOR-189;Mn;0;NSM;;;;;N;;;;;
+E01AD;VARIATION SELECTOR-190;Mn;0;NSM;;;;;N;;;;;
+E01AE;VARIATION SELECTOR-191;Mn;0;NSM;;;;;N;;;;;
+E01AF;VARIATION SELECTOR-192;Mn;0;NSM;;;;;N;;;;;
+E01B0;VARIATION SELECTOR-193;Mn;0;NSM;;;;;N;;;;;
+E01B1;VARIATION SELECTOR-194;Mn;0;NSM;;;;;N;;;;;
+E01B2;VARIATION SELECTOR-195;Mn;0;NSM;;;;;N;;;;;
+E01B3;VARIATION SELECTOR-196;Mn;0;NSM;;;;;N;;;;;
+E01B4;VARIATION SELECTOR-197;Mn;0;NSM;;;;;N;;;;;
+E01B5;VARIATION SELECTOR-198;Mn;0;NSM;;;;;N;;;;;
+E01B6;VARIATION SELECTOR-199;Mn;0;NSM;;;;;N;;;;;
+E01B7;VARIATION SELECTOR-200;Mn;0;NSM;;;;;N;;;;;
+E01B8;VARIATION SELECTOR-201;Mn;0;NSM;;;;;N;;;;;
+E01B9;VARIATION SELECTOR-202;Mn;0;NSM;;;;;N;;;;;
+E01BA;VARIATION SELECTOR-203;Mn;0;NSM;;;;;N;;;;;
+E01BB;VARIATION SELECTOR-204;Mn;0;NSM;;;;;N;;;;;
+E01BC;VARIATION SELECTOR-205;Mn;0;NSM;;;;;N;;;;;
+E01BD;VARIATION SELECTOR-206;Mn;0;NSM;;;;;N;;;;;
+E01BE;VARIATION SELECTOR-207;Mn;0;NSM;;;;;N;;;;;
+E01BF;VARIATION SELECTOR-208;Mn;0;NSM;;;;;N;;;;;
+E01C0;VARIATION SELECTOR-209;Mn;0;NSM;;;;;N;;;;;
+E01C1;VARIATION SELECTOR-210;Mn;0;NSM;;;;;N;;;;;
+E01C2;VARIATION SELECTOR-211;Mn;0;NSM;;;;;N;;;;;
+E01C3;VARIATION SELECTOR-212;Mn;0;NSM;;;;;N;;;;;
+E01C4;VARIATION SELECTOR-213;Mn;0;NSM;;;;;N;;;;;
+E01C5;VARIATION SELECTOR-214;Mn;0;NSM;;;;;N;;;;;
+E01C6;VARIATION SELECTOR-215;Mn;0;NSM;;;;;N;;;;;
+E01C7;VARIATION SELECTOR-216;Mn;0;NSM;;;;;N;;;;;
+E01C8;VARIATION SELECTOR-217;Mn;0;NSM;;;;;N;;;;;
+E01C9;VARIATION SELECTOR-218;Mn;0;NSM;;;;;N;;;;;
+E01CA;VARIATION SELECTOR-219;Mn;0;NSM;;;;;N;;;;;
+E01CB;VARIATION SELECTOR-220;Mn;0;NSM;;;;;N;;;;;
+E01CC;VARIATION SELECTOR-221;Mn;0;NSM;;;;;N;;;;;
+E01CD;VARIATION SELECTOR-222;Mn;0;NSM;;;;;N;;;;;
+E01CE;VARIATION SELECTOR-223;Mn;0;NSM;;;;;N;;;;;
+E01CF;VARIATION SELECTOR-224;Mn;0;NSM;;;;;N;;;;;
+E01D0;VARIATION SELECTOR-225;Mn;0;NSM;;;;;N;;;;;
+E01D1;VARIATION SELECTOR-226;Mn;0;NSM;;;;;N;;;;;
+E01D2;VARIATION SELECTOR-227;Mn;0;NSM;;;;;N;;;;;
+E01D3;VARIATION SELECTOR-228;Mn;0;NSM;;;;;N;;;;;
+E01D4;VARIATION SELECTOR-229;Mn;0;NSM;;;;;N;;;;;
+E01D5;VARIATION SELECTOR-230;Mn;0;NSM;;;;;N;;;;;
+E01D6;VARIATION SELECTOR-231;Mn;0;NSM;;;;;N;;;;;
+E01D7;VARIATION SELECTOR-232;Mn;0;NSM;;;;;N;;;;;
+E01D8;VARIATION SELECTOR-233;Mn;0;NSM;;;;;N;;;;;
+E01D9;VARIATION SELECTOR-234;Mn;0;NSM;;;;;N;;;;;
+E01DA;VARIATION SELECTOR-235;Mn;0;NSM;;;;;N;;;;;
+E01DB;VARIATION SELECTOR-236;Mn;0;NSM;;;;;N;;;;;
+E01DC;VARIATION SELECTOR-237;Mn;0;NSM;;;;;N;;;;;
+E01DD;VARIATION SELECTOR-238;Mn;0;NSM;;;;;N;;;;;
+E01DE;VARIATION SELECTOR-239;Mn;0;NSM;;;;;N;;;;;
+E01DF;VARIATION SELECTOR-240;Mn;0;NSM;;;;;N;;;;;
+E01E0;VARIATION SELECTOR-241;Mn;0;NSM;;;;;N;;;;;
+E01E1;VARIATION SELECTOR-242;Mn;0;NSM;;;;;N;;;;;
+E01E2;VARIATION SELECTOR-243;Mn;0;NSM;;;;;N;;;;;
+E01E3;VARIATION SELECTOR-244;Mn;0;NSM;;;;;N;;;;;
+E01E4;VARIATION SELECTOR-245;Mn;0;NSM;;;;;N;;;;;
+E01E5;VARIATION SELECTOR-246;Mn;0;NSM;;;;;N;;;;;
+E01E6;VARIATION SELECTOR-247;Mn;0;NSM;;;;;N;;;;;
+E01E7;VARIATION SELECTOR-248;Mn;0;NSM;;;;;N;;;;;
+E01E8;VARIATION SELECTOR-249;Mn;0;NSM;;;;;N;;;;;
+E01E9;VARIATION SELECTOR-250;Mn;0;NSM;;;;;N;;;;;
+E01EA;VARIATION SELECTOR-251;Mn;0;NSM;;;;;N;;;;;
+E01EB;VARIATION SELECTOR-252;Mn;0;NSM;;;;;N;;;;;
+E01EC;VARIATION SELECTOR-253;Mn;0;NSM;;;;;N;;;;;
+E01ED;VARIATION SELECTOR-254;Mn;0;NSM;;;;;N;;;;;
+E01EE;VARIATION SELECTOR-255;Mn;0;NSM;;;;;N;;;;;
+E01EF;VARIATION SELECTOR-256;Mn;0;NSM;;;;;N;;;;;
+F0000;<Plane 15 Private Use, First>;Co;0;L;;;;;N;;;;;
+FFFFD;<Plane 15 Private Use, Last>;Co;0;L;;;;;N;;;;;
+100000;<Plane 16 Private Use, First>;Co;0;L;;;;;N;;;;;
+10FFFD;<Plane 16 Private Use, Last>;Co;0;L;;;;;N;;;;;
diff --git a/source4/heimdal/lib/wind/bidi.c b/source4/heimdal/lib/wind/bidi.c
new file mode 100644
index 0000000000..022a2a17c4
--- /dev/null
+++ b/source4/heimdal/lib/wind/bidi.c
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "windlocl.h"
+
+#include <stdlib.h>
+
+#include "bidi_table.h"
+
+static int
+range_entry_cmp(const void *a, const void *b)
+{
+ const struct range_entry *ea = (const struct range_entry*)a;
+ const struct range_entry *eb = (const struct range_entry*)b;
+
+ if (ea->start >= eb->start && ea->start < eb->start + eb->len)
+ return 0;
+ return ea->start - eb->start;
+}
+
+static int
+is_ral(uint32_t cp)
+{
+ struct range_entry ee = {cp};
+ void *s = bsearch(&ee, _wind_ral_table, _wind_ral_table_size,
+ sizeof(_wind_ral_table[0]),
+ range_entry_cmp);
+ return s != NULL;
+}
+
+static int
+is_l(uint32_t cp)
+{
+ struct range_entry ee = {cp};
+ void *s = bsearch(&ee, _wind_l_table, _wind_l_table_size,
+ sizeof(_wind_l_table[0]),
+ range_entry_cmp);
+ return s != NULL;
+}
+
+int
+_wind_stringprep_testbidi(const uint32_t *in, size_t in_len, wind_profile_flags flags)
+{
+ size_t i;
+ unsigned ral = 0;
+ unsigned l = 0;
+
+ if ((flags & (WIND_PROFILE_NAME|WIND_PROFILE_SASL)) == 0)
+ return 0;
+
+ for (i = 0; i < in_len; ++i) {
+ ral |= is_ral(in[i]);
+ l |= is_l(in[i]);
+ }
+ if (ral) {
+ if (l)
+ return 1;
+ if (!is_ral(in[0]) || !is_ral(in[in_len - 1]))
+ return 1;
+ }
+ return 0;
+}
diff --git a/source4/heimdal/lib/wind/combining.c b/source4/heimdal/lib/wind/combining.c
new file mode 100644
index 0000000000..22fbf38350
--- /dev/null
+++ b/source4/heimdal/lib/wind/combining.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "windlocl.h"
+
+#include <stdlib.h>
+
+#include "combining_table.h"
+
+static int
+translation_cmp(const void *key, const void *data)
+{
+ const struct translation *t1 = (const struct translation *)key;
+ const struct translation *t2 = (const struct translation *)data;
+
+ return t1->key - t2->key;
+}
+
+int
+_wind_combining_class(uint32_t code_point)
+{
+ struct translation ts = {code_point};
+ void *s = bsearch(&ts, _wind_combining_table, _wind_combining_table_size,
+ sizeof(_wind_combining_table[0]),
+ translation_cmp);
+ if (s != NULL) {
+ const struct translation *t = (const struct translation *)s;
+ return t->combining_class;
+ } else {
+ return 0;
+ }
+}
diff --git a/source4/heimdal/lib/wind/errorlist.c b/source4/heimdal/lib/wind/errorlist.c
new file mode 100644
index 0000000000..b2e2210493
--- /dev/null
+++ b/source4/heimdal/lib/wind/errorlist.c
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "windlocl.h"
+
+#include <stdlib.h>
+
+#include "errorlist_table.h"
+
+static int
+error_entry_cmp(const void *a, const void *b)
+{
+ const struct error_entry *ea = (const struct error_entry*)a;
+ const struct error_entry *eb = (const struct error_entry*)b;
+
+ if (ea->start >= eb->start && ea->start < eb->start + eb->len)
+ return 0;
+ return ea->start - eb->start;
+}
+
+int
+_wind_stringprep_error(uint32_t cp, wind_profile_flags flags)
+{
+ struct error_entry ee = {cp};
+ const struct error_entry *s;
+
+ s = (const struct error_entry *)
+ bsearch(&ee, _wind_errorlist_table,
+ _wind_errorlist_table_size,
+ sizeof(_wind_errorlist_table[0]),
+ error_entry_cmp);
+ if (s == NULL)
+ return 0;
+ return (s->flags & flags);
+}
+
+int
+_wind_stringprep_prohibited(const uint32_t *in, size_t in_len,
+ wind_profile_flags flags)
+{
+ unsigned i;
+
+ for (i = 0; i < in_len; ++i)
+ if (_wind_stringprep_error(in[i], flags))
+ return 1;
+ return 0;
+}
diff --git a/source4/heimdal/lib/wind/gen-bidi.py b/source4/heimdal/lib/wind/gen-bidi.py
new file mode 100644
index 0000000000..42af72f3bf
--- /dev/null
+++ b/source4/heimdal/lib/wind/gen-bidi.py
@@ -0,0 +1,101 @@
+#!/usr/local/bin/python
+# -*- coding: iso-8859-1 -*-
+
+# $Id$
+
+# Copyright (c) 2004 Kungliga Tekniska Högskolan
+# (Royal Institute of Technology, Stockholm, Sweden).
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the Institute nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+import re
+import string
+import sys
+
+import generate
+import rfc3454
+
+if len(sys.argv) != 3:
+ print "usage: %s rfc3454.txt outdir" % sys.argv[0]
+ sys.exit(1)
+
+tables = rfc3454.read(sys.argv[1])
+
+bidi_h = generate.Header('%s/bidi_table.h' % sys.argv[2])
+
+bidi_c = generate.Implementation('%s/bidi_table.c' % sys.argv[2])
+
+bidi_h.file.write(
+'''
+#include <krb5-types.h>
+
+struct range_entry {
+ uint32_t start;
+ unsigned len;
+};
+
+extern const struct range_entry _wind_ral_table[];
+extern const struct range_entry _wind_l_table[];
+
+extern const size_t _wind_ral_table_size;
+extern const size_t _wind_l_table_size;
+
+''')
+
+bidi_c.file.write(
+'''
+#include "bidi_table.h"
+
+''')
+
+def printTable(file, table, variable):
+ """print table to file named as variable"""
+ file.write("const struct range_entry %s[] = {\n" % variable)
+ count = 0
+ for l in tables[table]:
+ m = re.search('^ *([0-9A-F]+)-([0-9A-F]+) *$', l)
+ if m:
+ start = int(m.group(1), 0x10)
+ end = int(m.group(2), 0x10)
+ file.write(" {0x%x, 0x%x},\n" % (start, end - start + 1))
+ count += 1
+ else:
+ m = re.search('^ *([0-9A-F]+) *$', l)
+ if m:
+ v = int(m.group(1), 0x10)
+ file.write(" {0x%x, 1},\n" % v)
+ count += 1
+ file.write("};\n\n")
+ file.write("const size_t %s_size = %u;\n\n" % (variable, count))
+
+printTable(bidi_c.file, 'D.1', '_wind_ral_table')
+printTable(bidi_c.file, 'D.2', '_wind_l_table')
+
+bidi_h.close()
+bidi_c.close()
diff --git a/source4/heimdal/lib/wind/gen-combining.py b/source4/heimdal/lib/wind/gen-combining.py
new file mode 100644
index 0000000000..f7e9bf881f
--- /dev/null
+++ b/source4/heimdal/lib/wind/gen-combining.py
@@ -0,0 +1,104 @@
+#!/usr/local/bin/python
+# -*- coding: iso-8859-1 -*-
+
+# $Id$
+
+# Copyright (c) 2004 Kungliga Tekniska Högskolan
+# (Royal Institute of Technology, Stockholm, Sweden).
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the Institute nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+import re
+import string
+import sys
+
+import generate
+import UnicodeData
+
+if len(sys.argv) != 3:
+ print "usage: %s UnicodeData.txt out-dir" % sys.argv[0]
+ sys.exit(1)
+
+ud = UnicodeData.read(sys.argv[1])
+
+trans = {}
+for k,v in ud.items():
+ if int(v[2]) != 0 :
+ trans[k] = [int(v[2]), v[1]]
+
+# trans = [(x[0], int(x[3]), x[1]) for x in UnicodeData.read() if int(x[3]) != 0]
+
+combining_h = generate.Header('%s/combining_table.h' % sys.argv[2])
+combining_c = generate.Implementation('%s/combining_table.c' % sys.argv[2])
+
+combining_h.file.write(
+'''
+#include <krb5-types.h>
+
+struct translation {
+ uint32_t key;
+ unsigned combining_class;
+};
+
+extern const struct translation _wind_combining_table[];
+
+extern const size_t _wind_combining_table_size;
+''')
+
+combining_c.file.write(
+'''
+#include "combining_table.h"
+
+const struct translation _wind_combining_table[] = {
+''')
+
+s = trans.keys()
+s.sort()
+for k in s:
+ v = trans[k]
+ combining_c.file.write("{0x%x, %u}, /* %s */\n"
+ % (k, v[0], v[1]))
+
+
+#trans.sort()
+#for x in trans:
+# combining_c.file.write("{0x%x, %u}, /* %s */\n"
+# % (x[0], x[1], x[2]))
+
+combining_c.file.write(
+'''
+};
+''')
+
+combining_c.file.write(
+ "const size_t _wind_combining_table_size = %u;\n" % len(trans))
+
+
+combining_h.close()
+combining_c.close()
diff --git a/source4/heimdal/lib/wind/gen-errorlist.py b/source4/heimdal/lib/wind/gen-errorlist.py
new file mode 100644
index 0000000000..6ea88d7bc5
--- /dev/null
+++ b/source4/heimdal/lib/wind/gen-errorlist.py
@@ -0,0 +1,120 @@
+#!/usr/local/bin/python
+# -*- coding: iso-8859-1 -*-
+
+# $Id$
+
+# Copyright (c) 2004 Kungliga Tekniska Högskolan
+# (Royal Institute of Technology, Stockholm, Sweden).
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the Institute nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+import re
+import string
+import sys
+
+import generate
+import rfc3454
+import rfc4518
+import stringprep
+
+if len(sys.argv) != 3:
+ print "usage: %s rfc3454.txt out-dir" % sys.argv[0]
+ sys.exit(1)
+
+tables = rfc3454.read(sys.argv[1])
+t2 = rfc4518.read()
+
+for x in t2.iterkeys():
+ tables[x] = t2[x]
+
+error_list = stringprep.get_errorlist()
+
+errorlist_h = generate.Header('%s/errorlist_table.h' % sys.argv[2])
+
+errorlist_c = generate.Implementation('%s/errorlist_table.c' % sys.argv[2])
+
+errorlist_h.file.write(
+'''
+#include "windlocl.h"
+
+struct error_entry {
+ uint32_t start;
+ unsigned len;
+ wind_profile_flags flags;
+};
+
+extern const struct error_entry _wind_errorlist_table[];
+
+extern const size_t _wind_errorlist_table_size;
+
+''')
+
+errorlist_c.file.write(
+'''
+#include "errorlist_table.h"
+
+const struct error_entry _wind_errorlist_table[] = {
+''')
+
+trans=[]
+
+for t in error_list.iterkeys():
+ for l in tables[t]:
+ m = re.search('^ *([0-9A-F]+)-([0-9A-F]+); *(.*) *$', l)
+ if m:
+ start = int(m.group(1), 0x10)
+ end = int(m.group(2), 0x10)
+ desc = m.group(3)
+ trans.append([start, end - start + 1, desc, [t]])
+ else:
+ m = re.search('^ *([0-9A-F]+); *(.*) *$', l)
+ if m:
+ trans.append([int(m.group(1), 0x10), 1, m.group(2), [t]])
+
+trans = stringprep.sort_merge_trans(trans)
+
+for x in trans:
+ (start, length, description, tables) = x
+ symbols = stringprep.symbols(error_list, tables)
+ if len(symbols) == 0:
+ print "no symbol for %s" % description
+ sys.exit(1)
+ errorlist_c.file.write(" {0x%x, 0x%x, %s}, /* %s: %s */\n"
+ % (start, length, symbols, ",".join(tables), description))
+
+errorlist_c.file.write(
+'''};
+
+''')
+
+errorlist_c.file.write(
+ "const size_t _wind_errorlist_table_size = %u;\n" % len(trans))
+
+errorlist_h.close()
+errorlist_c.close()
diff --git a/source4/heimdal/lib/wind/gen-map.py b/source4/heimdal/lib/wind/gen-map.py
new file mode 100644
index 0000000000..d4f02af1f2
--- /dev/null
+++ b/source4/heimdal/lib/wind/gen-map.py
@@ -0,0 +1,158 @@
+#!/usr/local/bin/python
+# -*- coding: iso-8859-1 -*-
+
+# $Id$
+
+# Copyright (c) 2004 Kungliga Tekniska Högskolan
+# (Royal Institute of Technology, Stockholm, Sweden).
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the Institute nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+import re
+import string
+import sys
+
+import generate
+import rfc3454
+import rfc4518
+import stringprep
+import util
+
+if len(sys.argv) != 3:
+ print "usage: %s rfc3454.txt out-dir" % sys.argv[0]
+ sys.exit(1)
+
+tables = rfc3454.read(sys.argv[1])
+t2 = rfc4518.read()
+
+for x in t2.iterkeys():
+ tables[x] = t2[x]
+
+map_list = stringprep.get_maplist()
+
+map_h = generate.Header('%s/map_table.h' % sys.argv[2])
+
+map_c = generate.Implementation('%s/map_table.c' % sys.argv[2])
+
+map_h.file.write(
+'''
+#include "windlocl.h"
+
+struct translation {
+ uint32_t key;
+ unsigned short val_len;
+ unsigned short val_offset;
+ wind_profile_flags flags;
+};
+
+extern const struct translation _wind_map_table[];
+
+extern const size_t _wind_map_table_size;
+
+extern const uint32_t _wind_map_table_val[];
+
+''')
+
+map_c.file.write(
+'''
+#include "map_table.h"
+
+const struct translation _wind_map_table[] = {
+''')
+
+trans=[]
+
+for t in map_list.iterkeys():
+ for l in tables[t]:
+ m = re.search('^ *([0-9A-F]+)-([0-9A-F]+); *([^;]+); *(.*) *$', l)
+ if m:
+ start = int(m.group(1), 0x10)
+ end = int(m.group(2), 0x10)
+ value = m.group(3)
+ desc = m.group(4)
+ for key in xrange(start,end,1):
+ trans.append((key, value, desc, [t]))
+ continue
+ m = re.search('^ *([^;]+); *([^;]+); *(.*) *$', l)
+ if m:
+ key = int(m.group(1), 0x10)
+ value = m.group(2)
+ desc = m.group(3)
+ trans.append((key, value, desc, [t]))
+ continue
+
+valTable = []
+offsetTable = {}
+
+trans = stringprep.sort_merge_trans(trans)
+
+for x in trans:
+ if x[0] == 0xad:
+ print "fooresult %s" % ",".join(x[3])
+
+for x in trans:
+ (key, value, description, table) = x
+ v = value.split()
+ i = util.subList(valTable, v)
+ if i:
+ offsetTable[key] = i
+ else:
+ offsetTable[key] = len(valTable)
+ valTable.extend(v)
+
+for x in trans:
+ (key, value, description, tables) = x
+ symbols = stringprep.symbols(map_list, tables)
+ if len(symbols) == 0:
+ print "no symbol for %s %s (%s)" % (key, description, tables)
+ sys.exit(1)
+ v = value.split()
+ map_c.file.write(" {0x%x, %u, %u, %s}, /* %s: %s */\n"
+ % (key, len(v), offsetTable[key], symbols, ",".join(tables), description))
+
+map_c.file.write(
+'''
+};
+
+''')
+
+map_c.file.write(
+ "const size_t _wind_map_table_size = %u;\n\n" % len(trans))
+
+map_c.file.write(
+ "const uint32_t _wind_map_table_val[] = {\n")
+
+for x in valTable:
+ map_c.file.write(" 0x%s,\n" % x)
+
+map_c.file.write(
+ "};\n\n")
+
+map_h.close()
+map_c.close()
diff --git a/source4/heimdal/lib/wind/gen-normalize.py b/source4/heimdal/lib/wind/gen-normalize.py
new file mode 100644
index 0000000000..0a20b71cfa
--- /dev/null
+++ b/source4/heimdal/lib/wind/gen-normalize.py
@@ -0,0 +1,210 @@
+#!/usr/local/bin/python
+# -*- coding: iso-8859-1 -*-
+
+# $Id$
+
+# Copyright (c) 2004 Kungliga Tekniska Högskolan
+# (Royal Institute of Technology, Stockholm, Sweden).
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the Institute nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+import re
+import string
+import sys
+
+import generate
+import UnicodeData
+import util
+
+if len(sys.argv) != 4:
+ print "usage: %s UnicodeData.txt"
+ " CompositionExclusions-3.2.0.txt out-dir" % sys.argv[0]
+ sys.exit(1)
+
+ud = UnicodeData.read(sys.argv[1])
+
+def sortedKeys(d):
+ """Return a sorted list of the keys of a dict"""
+ keys = d.keys()
+ keys.sort()
+ return keys
+
+trans = dict([(k, [re.sub('<[a-zA-Z]+>', '', v[4]), v[0]])
+ for k,v in ud.items() if v[4]])
+
+maxLength = 0
+for v in trans.values():
+ maxLength = max(maxLength, len(v[0].split()))
+
+normalize_h = generate.Header('%s/normalize_table.h' % sys.argv[3])
+normalize_c = generate.Implementation('%s/normalize_table.c' % sys.argv[3])
+
+normalize_h.file.write(
+'''
+#include <krb5-types.h>
+
+#define MAX_LENGTH_CANON %u
+
+struct translation {
+ uint32_t key;
+ unsigned short val_len;
+ unsigned short val_offset;
+};
+
+extern const struct translation _wind_normalize_table[];
+
+extern const uint32_t _wind_normalize_val_table[];
+
+extern const size_t _wind_normalize_table_size;
+
+struct canon_node {
+ uint32_t val;
+ unsigned char next_start;
+ unsigned char next_end;
+ unsigned short next_offset;
+};
+
+extern const struct canon_node _wind_canon_table[];
+
+extern const unsigned short _wind_canon_next_table[];
+''' % maxLength)
+
+normalize_c.file.write(
+'''
+#include "normalize_table.h"
+
+const struct translation _wind_normalize_table[] = {
+''')
+
+normalizeValTable = []
+
+for k in sortedKeys(trans) :
+ v = trans[k]
+ (key, value, description) = k, v[0], v[1]
+ vec = [int(x, 0x10) for x in value.split()];
+ offset = util.subList(normalizeValTable, vec)
+ if not offset:
+ offset = len(normalizeValTable)
+ normalizeValTable.extend(vec) # [("0x%s" % i) for i in vec])
+ normalize_c.file.write(" {0x%x, %u, %u}, /* %s */\n"
+ % (key, len(vec), offset, description))
+
+normalize_c.file.write(
+'''};
+
+''')
+
+normalize_c.file.write(
+ "const size_t _wind_normalize_table_size = %u;\n\n" % len(trans))
+
+normalize_c.file.write("const uint32_t _wind_normalize_val_table[] = {\n")
+
+for v in normalizeValTable:
+ normalize_c.file.write(" 0x%x,\n" % v)
+
+normalize_c.file.write("};\n\n");
+
+exclusions = UnicodeData.read(sys.argv[2])
+
+inv = dict([(''.join(["%05x" % int(x, 0x10) for x in v[4].split(' ')]),
+ [k, v[0]])
+ for k,v in ud.items()
+ if v[4] and not re.search('<[a-zA-Z]+> *', v[4]) and not exclusions.has_key(k)])
+
+table = 0
+
+tables = {}
+
+def createTable():
+ """add a new table"""
+ global table, tables
+ ret = table
+ table += 1
+ tables[ret] = [0] + [None] * 16
+ return ret
+
+def add(table, k, v):
+ """add an entry (k, v) to table (recursively)"""
+ if len(k) == 0:
+ table[0] = v[0]
+ else:
+ i = int(k[0], 0x10) + 1
+ if table[i] == None:
+ table[i] = createTable()
+ add(tables[table[i]], k[1:], v)
+
+top = createTable()
+
+for k,v in inv.items():
+ add(tables[top], k, v)
+
+next_table = []
+tableToNext = {}
+tableEnd = {}
+tableStart = {}
+
+for k in sortedKeys(tables) :
+ t = tables[k]
+ tableToNext[k] = len(next_table)
+ l = t[1:]
+ start = 0
+ while start < 16 and l[start] == None:
+ start += 1
+ end = 16
+ while end > start and l[end - 1] == None:
+ end -= 1
+ tableStart[k] = start
+ tableEnd[k] = end
+ n = []
+ for i in range(start, end):
+ x = l[i]
+ if x:
+ n.append(x)
+ else:
+ n.append(0)
+ next_table.extend(n)
+
+normalize_c.file.write("const struct canon_node _wind_canon_table[] = {\n")
+
+for k in sortedKeys(tables) :
+ t = tables[k]
+ normalize_c.file.write(" {0x%x, %u, %u, %u},\n" %
+ (t[0], tableStart[k], tableEnd[k], tableToNext[k]))
+
+normalize_c.file.write("};\n\n")
+
+normalize_c.file.write("const unsigned short _wind_canon_next_table[] = {\n")
+
+for k in next_table:
+ normalize_c.file.write(" %u,\n" % k)
+
+normalize_c.file.write("};\n\n")
+
+normalize_h.close()
+normalize_c.close()
diff --git a/source4/heimdal/lib/wind/generate.py b/source4/heimdal/lib/wind/generate.py
new file mode 100644
index 0000000000..1d708c6a3c
--- /dev/null
+++ b/source4/heimdal/lib/wind/generate.py
@@ -0,0 +1,81 @@
+#!/usr/local/bin/python
+# -*- coding: iso-8859-1 -*-
+
+# $Id$
+
+# Copyright (c) 2004 Kungliga Tekniska Högskolan
+# (Royal Institute of Technology, Stockholm, Sweden).
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the Institute nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+import datetime
+import string
+import os
+
+class GeneratedFile :
+ "Represents a generated file"
+ def __init__(self, name) :
+ "Create a new GeneratedFile with name"
+ self.name = os.path.basename(name)
+ self.file = open(name, 'w')
+ self.file.write('/* ' + name + ' */\n')
+ self.file.write('/* Automatically generated at ' +
+ datetime.datetime.now().isoformat() +
+ ' */\n\n')
+
+ def close(self) :
+ """End and close the file header"""
+ self.file.close()
+
+
+class Header(GeneratedFile) :
+ "Represents a generated header file"
+ guardTrans = string.maketrans('-.', '__')
+ def makeGuard(self) :
+ """Return a name to be used as ifdef guard"""
+ return string.upper(string.translate(self.name, self.guardTrans))
+
+ def __init__(self, name) :
+ "Create a new Header with name"
+ GeneratedFile.__init__(self, name)
+ self.guard = self.makeGuard()
+ self.file.write('#ifndef ' + self.guard + '\n')
+ self.file.write('#define ' + self.guard + ' 1\n')
+
+ def close(self) :
+ """End and close the file header"""
+ self.file.write('#endif /* ' + self.guard + ' */\n')
+ GeneratedFile.close(self)
+
+
+class Implementation(GeneratedFile) :
+ "Represents a generated implementation file"
+ def __init__(self, name) :
+ "Create a new Implementation with name"
+ GeneratedFile.__init__(self, name)
diff --git a/source4/heimdal/lib/wind/ldap.c b/source4/heimdal/lib/wind/ldap.c
new file mode 100644
index 0000000000..503ec75a9f
--- /dev/null
+++ b/source4/heimdal/lib/wind/ldap.c
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "windlocl.h"
+#include <assert.h>
+
+static int
+put_char(uint32_t *out, size_t *o, uint32_t c, size_t out_len)
+{
+ if (*o >= out_len)
+ return 1;
+ out[*o] = c;
+ (*o)++;
+ return 0;
+}
+
+int
+_wind_ldap_case_exact_attribute(const uint32_t *tmp,
+ size_t olen,
+ uint32_t *out,
+ size_t *out_len)
+{
+ size_t o = 0, i = 0;
+
+ if (olen == 0) {
+ *out_len = 0;
+ return 0;
+ }
+
+ if (put_char(out, &o, 0x20, *out_len))
+ return WIND_ERR_OVERRUN;
+ while(i < olen && tmp[i] == 0x20) /* skip initial spaces */
+ i++;
+
+ while (i < olen) {
+ if (tmp[i] == 0x20) {
+ if (put_char(out, &o, 0x20, *out_len) ||
+ put_char(out, &o, 0x20, *out_len))
+ return WIND_ERR_OVERRUN;
+ while(i < olen && tmp[i] == 0x20) /* skip middle spaces */
+ i++;
+ } else {
+ if (put_char(out, &o, tmp[i++], *out_len))
+ return WIND_ERR_OVERRUN;
+ }
+ }
+ assert(o > 0);
+
+ /* only one spaces at the end */
+ if (o == 1 && out[0] == 0x20)
+ o = 0;
+ else if (out[o - 1] == 0x20) {
+ if (out[o - 2] == 0x20)
+ o--;
+ } else
+ put_char(out, &o, 0x20, *out_len);
+
+ *out_len = o;
+
+ return 0;
+}
diff --git a/source4/heimdal/lib/wind/map.c b/source4/heimdal/lib/wind/map.c
new file mode 100644
index 0000000000..a005acccec
--- /dev/null
+++ b/source4/heimdal/lib/wind/map.c
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include "windlocl.h"
+
+#include <stdlib.h>
+
+#include "map_table.h"
+
+RCSID("$Id$");
+
+static int
+translation_cmp(const void *key, const void *data)
+{
+ const struct translation *t1 = (const struct translation *)key;
+ const struct translation *t2 = (const struct translation *)data;
+
+ return t1->key - t2->key;
+}
+
+int
+_wind_stringprep_map(const uint32_t *in, size_t in_len,
+ uint32_t *out, size_t *out_len,
+ wind_profile_flags flags)
+{
+ unsigned i;
+ unsigned o = 0;
+
+ for (i = 0; i < in_len; ++i) {
+ struct translation ts = {in[i]};
+ const struct translation *s;
+
+ s = (const struct translation *)
+ bsearch(&ts, _wind_map_table, _wind_map_table_size,
+ sizeof(_wind_map_table[0]),
+ translation_cmp);
+ if (s != NULL && (s->flags & flags)) {
+ unsigned j;
+
+ for (j = 0; j < s->val_len; ++j) {
+ if (o >= *out_len)
+ return WIND_ERR_OVERRUN;
+ out[o++] = _wind_map_table_val[s->val_offset + j];
+ }
+ } else {
+ if (o >= *out_len)
+ return WIND_ERR_OVERRUN;
+ out[o++] = in[i];
+
+ }
+ }
+ *out_len = o;
+ return 0;
+}
diff --git a/source4/heimdal/lib/wind/normalize.c b/source4/heimdal/lib/wind/normalize.c
new file mode 100644
index 0000000000..c7af0e4958
--- /dev/null
+++ b/source4/heimdal/lib/wind/normalize.c
@@ -0,0 +1,301 @@
+/*
+ * Copyright (c) 2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include "windlocl.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "normalize_table.h"
+
+RCSID("$Id$");
+
+static int
+translation_cmp(const void *key, const void *data)
+{
+ const struct translation *t1 = (const struct translation *)key;
+ const struct translation *t2 = (const struct translation *)data;
+
+ return t1->key - t2->key;
+}
+
+enum { s_base = 0xAC00};
+enum { s_count = 11172};
+enum { l_base = 0x1100};
+enum { l_count = 19};
+enum { v_base = 0x1161};
+enum { v_count = 21};
+enum { t_base = 0x11A7};
+enum { t_count = 28};
+enum { n_count = v_count * t_count};
+
+static int
+hangul_decomp(const uint32_t *in, size_t in_len,
+ uint32_t *out, size_t *out_len)
+{
+ uint32_t u = *in;
+ unsigned s_index;
+ unsigned l, v, t;
+ unsigned o;
+
+ if (u < s_base || u >= s_base + s_count)
+ return 0;
+ s_index = u - s_base;
+ l = l_base + s_index / n_count;
+ v = v_base + (s_index % n_count) / t_count;
+ t = t_base + s_index % t_count;
+ o = 2;
+ if (t != t_base)
+ ++o;
+ if (*out_len < o)
+ return WIND_ERR_OVERRUN;
+ out[0] = l;
+ out[1] = v;
+ if (t != t_base)
+ out[2] = t;
+ *out_len = o;
+ return 1;
+}
+
+static uint32_t
+hangul_composition(const uint32_t *in, size_t in_len)
+{
+ if (in_len < 2)
+ return 0;
+ if (in[0] >= l_base && in[0] < l_base + l_count) {
+ unsigned l_index = in[0] - l_base;
+ unsigned v_index;
+
+ if (in[1] < v_base || in[1] >= v_base + v_count)
+ return 0;
+ v_index = in[1] - v_base;
+ return (l_index * v_count + v_index) * t_count + s_base;
+ } else if (in[0] >= s_base && in[0] < s_base + s_count) {
+ unsigned s_index = in[0] - s_base;
+ unsigned t_index;
+
+ if (s_index % t_count != 0)
+ return 0;
+ if (in[1] < t_base || in[1] >= t_base + t_count)
+ return 0;
+ t_index = in[1] - t_base;
+ return in[0] + t_index;
+ }
+ return 0;
+}
+
+static int
+compat_decomp(const uint32_t *in, size_t in_len,
+ uint32_t *out, size_t *out_len)
+{
+ unsigned i;
+ unsigned o = 0;
+
+ for (i = 0; i < in_len; ++i) {
+ struct translation ts = {in[i]};
+ size_t sub_len = *out_len - o;
+ int ret;
+
+ ret = hangul_decomp(in + i, in_len - i,
+ out + o, &sub_len);
+ if (ret) {
+ if (ret == WIND_ERR_OVERRUN)
+ return ret;
+ o += sub_len;
+ } else {
+ void *s = bsearch(&ts,
+ _wind_normalize_table,
+ _wind_normalize_table_size,
+ sizeof(_wind_normalize_table[0]),
+ translation_cmp);
+ if (s != NULL) {
+ const struct translation *t = (const struct translation *)s;
+
+ ret = compat_decomp(_wind_normalize_val_table + t->val_offset,
+ t->val_len,
+ out + o, &sub_len);
+ if (ret)
+ return ret;
+ o += sub_len;
+ } else {
+ if (o >= *out_len)
+ return WIND_ERR_OVERRUN;
+ out[o++] = in[i];
+
+ }
+ }
+ }
+ *out_len = o;
+ return 0;
+}
+
+static int
+cc_cmp(const void *a, const void *b)
+{
+ const uint32_t *ua = (const uint32_t *)a;
+ const uint32_t *ub = (const uint32_t *)b;
+
+ return _wind_combining_class(*ua) - _wind_combining_class(*ub);
+}
+
+static void
+canonical_reorder(uint32_t *tmp, size_t tmp_len)
+{
+ unsigned i;
+
+ for (i = 0; i < tmp_len; ++i) {
+ int cc = _wind_combining_class(tmp[i]);
+ if (cc) {
+ size_t j;
+ for (j = i + 1;
+ j < tmp_len && _wind_combining_class(tmp[j]);
+ ++j)
+ ;
+ qsort(&tmp[i], j - i, sizeof(unsigned),
+ cc_cmp);
+ i = j;
+ }
+ }
+}
+
+static uint32_t
+find_composition(const uint32_t *in, unsigned in_len)
+{
+ unsigned short canon_index = 0;
+ uint32_t cur;
+ unsigned n = 0;
+
+ cur = hangul_composition(in, in_len);
+ if (cur)
+ return cur;
+
+ do {
+ const struct canon_node *c = &_wind_canon_table[canon_index];
+ unsigned i;
+
+ if (n % 5 == 0) {
+ cur = *in++;
+ if (in_len-- == 0)
+ return c->val;
+ }
+
+ i = cur >> 16;
+ if (i < c->next_start || i >= c->next_end)
+ canon_index = 0;
+ else
+ canon_index =
+ _wind_canon_next_table[c->next_offset + i - c->next_start];
+ if (canon_index != 0) {
+ cur = (cur << 4) & 0xFFFFF;
+ ++n;
+ }
+ } while (canon_index != 0);
+ return 0;
+}
+
+static int
+combine(const uint32_t *in, size_t in_len,
+ uint32_t *out, size_t *out_len)
+{
+ unsigned i;
+ int ostarter;
+ unsigned o = 0;
+ int old_cc;
+ int cc;
+
+ for (i = 0; i < in_len;) {
+ while (i < in_len && (cc = _wind_combining_class(in[i])) != 0) {
+ out[o++] = in[i++];
+ }
+ if (i < in_len) {
+ if (o >= *out_len)
+ return WIND_ERR_OVERRUN;
+ ostarter = o;
+ out[o++] = in[i++];
+ old_cc = -1;
+
+ while (i < in_len) {
+ uint32_t comb;
+ uint32_t v[2];
+
+ v[0] = out[ostarter];
+ v[1] = in[i];
+
+ cc = _wind_combining_class(in[i]);
+ if (old_cc != cc && (comb = find_composition(v, 2))) {
+ out[ostarter] = comb;
+ } else if (cc == 0) {
+ break;
+ } else {
+ if (o >= *out_len)
+ return WIND_ERR_OVERRUN;
+ out[o++] = in[i];
+ old_cc = cc;
+ }
+ ++i;
+ }
+ }
+ }
+ *out_len = o;
+ return 0;
+}
+
+int
+_wind_stringprep_normalize(const uint32_t *in, size_t in_len,
+ uint32_t *out, size_t *out_len)
+{
+ size_t tmp_len;
+ uint32_t *tmp;
+ int ret;
+
+ tmp_len = in_len * 4;
+ if (tmp_len < MAX_LENGTH_CANON)
+ tmp_len = MAX_LENGTH_CANON;
+ tmp = malloc(tmp_len * sizeof(uint32_t));
+ if (tmp == NULL)
+ return ENOMEM;
+
+ ret = compat_decomp(in, in_len, tmp, &tmp_len);
+ if (ret) {
+ free(tmp);
+ return ret;
+ }
+ canonical_reorder(tmp, tmp_len);
+ ret = combine(tmp, tmp_len, out, out_len);
+ free(tmp);
+ return ret;
+}
diff --git a/source4/heimdal/lib/wind/rfc3454.py b/source4/heimdal/lib/wind/rfc3454.py
new file mode 100644
index 0000000000..33f70a9b75
--- /dev/null
+++ b/source4/heimdal/lib/wind/rfc3454.py
@@ -0,0 +1,60 @@
+#!/usr/local/bin/python
+# -*- coding: iso-8859-1 -*-
+
+# $Id$
+
+# Copyright (c) 2004 Kungliga Tekniska Högskolan
+# (Royal Institute of Technology, Stockholm, Sweden).
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the Institute nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+import re
+import string
+
+def read(filename):
+ """return a dict of tables from rfc3454"""
+ f = open(filename, 'r')
+ inTable = False
+ ret = {}
+ while True:
+ l = f.readline()
+ if not l:
+ break
+ if inTable:
+ m = re.search('^ *----- End Table ([A-Z0-9\.]+) ----- *$', l)
+ if m:
+ ret[m.group(1)] = t
+ inTable = False
+ else:
+ t.append(l)
+ if re.search('^ *----- Start Table ([A-Z0-9\.]+) ----- *$', l):
+ inTable = True
+ t = []
+ f.close()
+ return ret
diff --git a/source4/heimdal/lib/wind/rfc3454.txt b/source4/heimdal/lib/wind/rfc3454.txt
new file mode 100644
index 0000000000..26a1e6c74d
--- /dev/null
+++ b/source4/heimdal/lib/wind/rfc3454.txt
@@ -0,0 +1,5099 @@
+
+
+
+
+
+
+Network Working Group P. Hoffman
+Request for Comments: 3454 IMC & VPNC
+Category: Standards Track M. Blanchet
+ Viagenie
+ December 2002
+
+
+ Preparation of Internationalized Strings ("stringprep")
+
+Status of this Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2002). All Rights Reserved.
+
+Abstract
+
+ This document describes a framework for preparing Unicode text
+ strings in order to increase the likelihood that string input and
+ string comparison work in ways that make sense for typical users
+ throughout the world. The stringprep protocol is useful for protocol
+ identifier values, company and personal names, internationalized
+ domain names, and other text strings.
+
+ This document does not specify how protocols should prepare text
+ strings. Protocols must create profiles of stringprep in order to
+ fully specify the processing options.
+
+Table of Contents
+
+ 1. Introduction....................................................3
+ 1.1 Terminology..................................................4
+ 1.2 Using stringprep in protocols................................4
+ 2. Preparation Overview............................................6
+ 3. Mapping.........................................................7
+ 3.1 Commonly mapped to nothing...................................7
+ 3.2 Case folding.................................................8
+ 4. Normalization...................................................9
+ 5. Prohibited Output..............................................10
+ 5.1 Space characters............................................11
+ 5.2 Control characters..........................................11
+ 5.3 Private use.................................................12
+
+
+
+Hoffman & Blanchet Standards Track [Page 1]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 5.4 Non-character code points...................................12
+ 5.5 Surrogate codes.............................................13
+ 5.6 Inappropriate for plain text................................13
+ 5.7 Inappropriate for canonical representation..................13
+ 5.8 Change display properties or deprecated.....................13
+ 5.9 Tagging characters..........................................14
+ 6. Bidirectional Characters.......................................14
+ 7. Unassigned Code Points in Stringprep Profiles..................15
+ 7.1 Categories of code points...................................16
+ 7.2 Reasons for difference between stored strings and queries...17
+ 7.3 Versions of applications and stored strings.................18
+ 8. References.....................................................19
+ 8.1 Normative references........................................19
+ 8.2 Informative references......................................19
+ 9. Security Considerations........................................19
+ 9.1 Stringprep-specific security considerations.................19
+ 9.2 Generic Unicode security considerations.....................20
+ 10. IANA Considerations...........................................21
+ 11. Acknowledgements..............................................22
+ A. Unicode repertoires............................................23
+ A.1 Unassigned code points in Unicode 3.2.......................23
+ B. Mapping Tables.................................................31
+ B.1 Commonly mapped to nothing..................................31
+ B.2 Mapping for case-folding used with NFKC.....................32
+ B.3 Mapping for case-folding used with no normalization.........61
+ C. Prohibition tables.............................................78
+ C.1 Space characters............................................78
+ C.1.1 ASCII space characters..................................78
+ C.1.2 Non-ASCII space characters..............................79
+ C.2 Control characters..........................................79
+ C.2.1 ASCII control characters................................79
+ C.2.2 Non-ASCII control characters............................79
+ C.3 Private use.................................................80
+ C.4 Non-character code points...................................80
+ C.5 Surrogate codes.............................................80
+ C.6 Inappropriate for plain text................................80
+ C.7 Inappropriate for canonical representation..................81
+ C.8 Change display properties or are deprecated.................81
+ C.9 Tagging characters..........................................81
+ D. Bidirectional tables...........................................81
+ D.1 Characters with bidirectional property "R" or "AL"..........81
+ D.2 Characters with bidirectional property "L"..................82
+ Authors' Addresses................................................90
+ Full Copyright Statement..........................................91
+
+
+
+
+
+
+
+Hoffman & Blanchet Standards Track [Page 2]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+1. Introduction
+
+ Application programs can display text in many different ways.
+ Similarly, a user can enter text into an application program in a
+ myriad of fashions. Internationalized text (that is, text that is
+ not restricted to the narrow set of US-ASCII characters) has many
+ input and display behaviors that make it difficult to compare text in
+ a consistent fashion.
+
+ This document specifies a framework of processing rules for Unicode
+ text. Other protocols can create profiles of these rules; these
+ profiles will allow users to enter internationalized text strings in
+ applications and have the highest chance of getting the content of
+ the strings correct. In this case, "correct" means that if two
+ different people enter what they think is the same string into two
+ different input mechanisms, the strings should match on a character-
+ by-character basis.
+
+ This framework does not describe how data is transcoded from other
+ character sets into Unicode. In systems that uses non-Unicode
+ character sets, the transcoding algorithm is a critical part of
+ enabling secure and "correct" operation of internationalized text
+ strings.
+
+ In addition to helping string matching, profiles of stringprep can
+ also exclude characters that should not normally appear in text that
+ is used in the protocol. The profile can prevent such characters by
+ changing the characters to be excluded to other characters, by
+ removing those characters, or by causing an error if the characters
+ would appear in the output. For example, because the backspace
+ character can cause unpredictable display results, a profile can
+ specify that a string containing a backspace character would cause an
+ error.
+
+ A profile of stringprep converts a single string of input characters
+ to a string of output characters, or returns an error if the output
+ string would contain a prohibited character. Stringprep profiles
+ cannot both emit a string and return an error.
+
+ Stringprep profiles cannot account for all of the variations that
+ might occur or that a user might expect. In particular, a profile
+ will not be able to account for choice of spellings in all languages
+ for all scripts because the number of alternative spellings of words
+ and phrases is immense. Users would probably expect all spelling
+ equivalents to be made equivalent, or none of them to be. Examples
+ of spelling equivalents include "theater" vs. "theatre", and
+ "hemoglobin" vs. "h<U+00E6>moglobin" in American vs. British English.
+ Other examples are simplified Chinese spellings of names (for
+
+
+
+Hoffman & Blanchet Standards Track [Page 3]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ example,"<U+7EDF><U+4E00><U+7801>") vs. the equivalent traditional
+ Chinese spelling (for example, "<U+7D71><U+4E00><U+78BC>").
+ Language-specific equivalences such as "Aepfel" vs. "<U+00C4>pfel",
+ which are sometimes considered equivalent in German, may not be
+ considered equivalent in other languages.
+
+1.1 Terminology
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in BCP 14, RFC 2119
+ [RFC2119].
+
+ Note: A glossary of terms used in Unicode and ISO/IEC 10646 can be
+ found in [Glossary]. Information on the 10646/Unicode character
+ encoding model can be found in [CharModel].
+
+ Character names in this document use the notation for code points and
+ names from the Unicode Standard [Unicode3.2] and ISO/IEC 10646
+ [ISO10646]. For example, the letter "a" may be represented as either
+ "U+0061" or "LATIN SMALL LETTER A". In the lists of mappings and the
+ prohibited characters, the "U+" is left off to make the lists easier
+ to read. The comments for character ranges are shown in square
+ brackets (such as "[CONTROL CHARACTERS]") and do not come from the
+ standards.
+
+1.2 Using stringprep in protocols
+
+ The stringprep protocol does not stand on its own; it has to be used
+ by other protocols at precisely-defined places in those other
+ protocols. For example, a protocol that has strings that come from
+ the entire ISO/IEC 10646 [ISO10646] character repertoire might
+ specify that only strings that have been processed with a particular
+ profile of stringprep are legal. Another example would be a protocol
+ that does string comparison as a step in the protocol; that protocol
+ might specify that such comparison is done only after processing the
+ strings with a specific profile of stringprep.
+
+ When two protocols that use different profiles of stringprep
+ interoperate, there may be conflict about what characters are and are
+ not allowed in the final string. Thus, protocol developers should
+ strongly consider re-using existing profiles of stringprep.
+
+ When developers wish to allow users as wide of a range of characters
+ as possible in input text strings, they should, where possible, cause
+ stringprep to convert characters from the input string to a canonical
+ form instead of prohibiting them.
+
+
+
+
+Hoffman & Blanchet Standards Track [Page 4]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ Although it would be easy to use the stringprep process to "correct"
+ perceived mis-features or bugs in the current character standards,
+ stringprep profiles SHOULD NOT do so.
+
+ A profile of stringprep can create tables different from those in the
+ appendixes of this document, but it will be an exception when they
+ do. The intention of stringprep is to define the tables and have the
+ profiles of stringprep select among those defined tables.
+
+ A profile of stringprep MUST include all of the following:
+
+ - The intended applicability of the profile
+
+ - The character repertoire that is the input and output to stringprep
+ (which is Unicode 3.2 for this version of stringprep)
+
+ - The mapping tables from this document used (as described in section
+ 3)
+
+ - Any additional mapping tables specific to the profile
+
+ - The Unicode normalization used, if any (as described in section 4)
+
+ - The tables from this document of characters that are prohibited as
+ output (as described in section 5)
+
+ - The bidirectional string testing used, if any (as described in
+ section 6)
+
+ - Any additional characters that are prohibited as output specific to
+ the profile
+
+ Each profile MUST state the character repertoire on which the profile
+ will operate. Appendix A lists the Unicode repertoires that can be
+ selected. No repertoire is ever complete, and it is expected that
+ characters will be added to the Unicode repertoire for the
+ foreseeable future. Section 7 of this document describes how to
+ handle characters that are assigned in later versions of the Unicode
+ repertories. Subsections of appendix A also list unassigned code
+ points for each repertoire.
+
+ This document is for Unicode version 3.2, and should not be
+ considered to automatically apply to later Unicode versions. The
+ IETF, through an explicit standards action, may update this document
+ as appropriate to handle later Unicode versions.
+
+
+
+
+
+
+Hoffman & Blanchet Standards Track [Page 5]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ This document lists the unassigned code points in the range 0 to
+ 10FFFF for Unicode 3.2 in appendix A. The list in appendix A MUST be
+ used by implementations of this specification. If there are any
+ discrepancies between the list in appendix A and the Unicode 3.2
+ specification, the list in appendix A always takes precedence.
+
+ Each profile of stringprep MUST be registered with IANA. The
+ registration procedure is described in the IANA Considerations
+ appendix; basically, the IESG must review each profile of stringprep.
+ Protocol developers are strongly encouraged to look through the IANA
+ profile registry when creating new profiles for stringprep, and to
+ re-use logic from earlier profiles where possible in new profiles.
+ In some cases, an existing profile can be reused by a different
+ protocol.
+
+2. Preparation Overview
+
+ The steps for preparing strings are:
+
+ 1) Map -- For each character in the input, check if it has a mapping
+ and, if so, replace it with its mapping. This is described in
+ section 3.
+
+ 2) Normalize -- Possibly normalize the result of step 1 using Unicode
+ normalization. This is described in section 4.
+
+ 3) Prohibit -- Check for any characters that are not allowed in the
+ output. If any are found, return an error. This is described in
+ section 5.
+
+ 4) Check bidi -- Possibly check for right-to-left characters, and if
+ any are found, make sure that the whole string satisfies the
+ requirements for bidirectional strings. If the string does not
+ satisfy the requirements for bidirectional strings, return an
+ error. This is described in section 6.
+
+ The above steps MUST be performed in the order given to comply with
+ this specification.
+
+ The mappings described in section 3, and the optional Unicode
+ normalization described in section 4, can be one-to-none, one-to-one,
+ one-to-many, many-to-one, or many-to-many. That is, some characters
+ might be eliminated or replaced by more than one character, and the
+ output of this step might be shorter or longer than the input.
+ Because of this, the system using stringprep MUST be prepared to
+ receive a longer or shorter string than the one input in the
+ stringprep algorithm.
+
+
+
+
+Hoffman & Blanchet Standards Track [Page 6]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+3. Mapping
+
+ Each character in the input stream MUST be checked against a mapping
+ table. The mapping table SHOULD come from this document, although
+ the mapping table MAY be added to or altered by the profile. The
+ mapping tables are subsections of appendix B.
+
+ The lists in appendix B MUST be used by implementations of this
+ specification. If there are any discrepancies between the lists in
+ appendix B and subsections below, the lists in appendix B always
+ takes precedence.
+
+ For any individual character, the mapping table MAY specify that a
+ character be mapped to nothing, or mapped to one other character, or
+ mapped to a string of other characters.
+
+ Mapped characters are not re-scanned during the mapping step. That
+ is, if character A at position X is mapped to character B, character
+ B which is now at position X is not checked against the mapping
+ table.
+
+3.1 Commonly mapped to nothing
+
+ The following characters are simply deleted from the input (that is,
+ they are mapped to nothing) because their presence or absence in
+ protocol identifiers should not make two strings different. They are
+ listed in Table B.1.
+
+ Some characters are only useful in line-based text, and are otherwise
+ invisible and ignored.
+
+ 00AD; SOFT HYPHEN
+ 1806; MONGOLIAN TODO SOFT HYPHEN
+ 200B; ZERO WIDTH SPACE
+ 2060; WORD JOINER
+ FEFF; ZERO WIDTH NO-BREAK SPACE
+
+ Some characters affect glyph choice and glyph placement, but do not
+ bear semantics.
+
+ 034F; COMBINING GRAPHEME JOINER
+ 180B; MONGOLIAN FREE VARIATION SELECTOR ONE
+ 180C; MONGOLIAN FREE VARIATION SELECTOR TWO
+ 180D; MONGOLIAN FREE VARIATION SELECTOR THREE
+ 200C; ZERO WIDTH NON-JOINER
+ 200D; ZERO WIDTH JOINER
+ FE00; VARIATION SELECTOR-1
+ FE01; VARIATION SELECTOR-2
+
+
+
+Hoffman & Blanchet Standards Track [Page 7]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ FE02; VARIATION SELECTOR-3
+ FE03; VARIATION SELECTOR-4
+ FE04; VARIATION SELECTOR-5
+ FE05; VARIATION SELECTOR-6
+ FE06; VARIATION SELECTOR-7
+ FE07; VARIATION SELECTOR-8
+ FE08; VARIATION SELECTOR-9
+ FE09; VARIATION SELECTOR-10
+ FE0A; VARIATION SELECTOR-11
+ FE0B; VARIATION SELECTOR-12
+ FE0C; VARIATION SELECTOR-13
+ FE0D; VARIATION SELECTOR-14
+ FE0E; VARIATION SELECTOR-15
+ FE0F; VARIATION SELECTOR-16
+
+3.2 Case folding
+
+ If a profile is going to map characters for case-insensitive
+ comparison, that profile SHOULD map using either appendix B.2 or
+ appendix B.3. appendix B.2 is for profiles that also use Unicode
+ normalization form KC, while appendix B.3 is for profiles that do
+ not use Unicode normalization. These tables map from uppercase to
+ lowercase characters. Note that this could have been "change all
+ lowercase characters into uppercase characters". However, the
+ upper-to-lower folding was chosen because there is a tradition of
+ using lowercase in current Internet applications and protocols.
+
+ If a profile creates its own mapping tables for case folding, they
+ SHOULD be based on [UTR21], and SHOULD map from uppercase characters
+ to lowercase. The "CaseFolding.txt" file from the Unicode database
+ SHOULD be used to prepare the mapping table. The profile SHOULD do
+ full case mapping (that is, using statuses C, F, and I).
+
+ If the profile is using Unicode normalization form KC (as described
+ in section 4 of this document), it is important to note that there
+ are some characters that do not have mappings in [UTR21] but still
+ need processing. These characters include a few Greek characters and
+ many symbols that contain Latin characters. The list of characters
+ to add to the mapping table can determined by the following
+ algorithm:
+
+ b = NormalizeWithKC(Fold(a));
+ c = NormalizeWithKC(Fold(b));
+ if c is not the same as b, add a mapping for "a to c".
+
+ Because NormalizeWithKC(Fold(c)) always equals c, the table is stable
+ from that point on.
+
+
+
+
+Hoffman & Blanchet Standards Track [Page 8]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ Appendix B.3 is derived from the CaseFolding-3.txt file associated
+ with Unicode 3.2; appendix B.2 is based on appendix B.3 with the
+ additional characters added from the algorithm above.
+
+ Authors of profiles of this document need to consider the effects of
+ changing the mapping of any currently-assigned character when
+ updating their profiles. Adding a new mapping for a currently-
+ assigned character, or changing an existing mapping, could cause a
+ variance between the behavior of systems that have been updated and
+ systems that have not been updated.
+
+4. Normalization
+
+ The output of the mapping step is optionally normalized using one of
+ the Unicode normalization forms, as described in [UAX15]. A profile
+ can specify one of two options for Unicode normalization:
+
+ - no normalization
+
+ - Unicode normalization with form KC
+
+ A profile MAY choose to do no normalization. However, such a profile
+ can easily yield results that will be surprising to typical users,
+ depending on the input mechanism they use. For example, some input
+ mechanisms enter compatibility characters that look exactly like the
+ underlying characters, but have different code points. Another
+ example of where Unicode normalization helps create predictable
+ results is with characters that have multiple combining diacritics:
+ normalization orders those diacritics in a predictable fashion.
+
+ On the other hand, Unicode normalization requires fairly large tables
+ and somewhat complicated character reordering logic. The size and
+ complexity should not be considered daunting except in the most
+ restricted of environments, and needs to be weighed against the
+ problems of user surprise from comparing unnormalized strings. Note
+ that the tables used for normalization are not given in this
+ document, but instead must be derived from the Unicode database, as
+ described in [UAX15].
+
+ There is a third form of normalization, Unicode normalization with
+ form C. If a profile is going to use a Unicode normalization, it
+ MUST use Unicode normalization form KC. Form KC maps many
+ "compatibility characters" to their equivalents. Some user interface
+ systems make it possible to enter compatibility characters instead of
+ the base equivalents. Thus, using form KC instead of form C will
+ cause more strings that users would expect to match to actually
+ match.
+
+
+
+
+Hoffman & Blanchet Standards Track [Page 9]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ A profile that specifies Unicode normalization MUST use the
+ normalization in [UAX15] that is associated with the version of the
+ Unicode character set specified for the profile.
+
+ The composition process described in [UAX15] requires a fixed
+ composition version of Unicode to ensure that strings normalized
+ under one version of Unicode remain normalized under all future
+ versions of Unicode.
+
+ The IETF is relying on Unicode not to change the normalization of
+ currently-assigned characters in future versions of normalization.
+ If a future version of the normalization tables changes the
+ normalized value of an existing character, authors of profiles of
+ this document have to look at the changes very carefully before they
+ update their normalization tables. Such a change could cause a
+ variance between the behavior of systems that have been updated and
+ systems that have not been updated.
+
+5. Prohibited Output
+
+ Before the text can be emitted, it MUST be checked for prohibited
+ code points. There are a variety of prohibited code points, as
+ described in this section. A profile of this document MAY use all or
+ some of the tables in appendix C.
+
+ The stringprep process never emits both an error and a string. If an
+ error is detected during the checking for prohibited code points,
+ only an error is returned.
+
+ Note that the subsections below describe how the tables in appendix C
+ were formed. They are here for people who want to understand more,
+ but they should be ignored by implementors. Implementations that use
+ tables MUST map based on the tables themselves, not based on the
+ descriptions in this section of how the tables were created.
+
+ The lists in appendix C MUST be used by implementations of this
+ specification. If there are any discrepancies between the lists in
+ appendix C and subsections below, the lists in appendix C always take
+ precedence.
+
+ Some code points listed in one section may also appear in other
+ sections.
+
+ It is important to note that a profile of this document MAY prohibit
+ additional characters.
+
+
+
+
+
+
+Hoffman & Blanchet Standards Track [Page 10]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ Each subsection of this section has a matching subsection in appendix
+ C. For example, the characters listed in section 5.1 are listed in
+ appendix C.1.
+
+5.1 Space characters
+
+ Space characters can make accurate visual transcription of strings
+ nearly impossible and could lead to user entry errors in many ways.
+ Note that the list below is split into two tables in appendix C:
+ Table C.1.1 contains the ASCII code points, while Table C.1.2
+ contains the non-ASCII code points. Most profiles of this document
+ that want to prohibit space characters will want to include both
+ tables.
+
+ 0020; SPACE
+ 00A0; NO-BREAK SPACE
+ 1680; OGHAM SPACE MARK
+ 2000; EN QUAD
+ 2001; EM QUAD
+ 2002; EN SPACE
+ 2003; EM SPACE
+ 2004; THREE-PER-EM SPACE
+ 2005; FOUR-PER-EM SPACE
+ 2006; SIX-PER-EM SPACE
+ 2007; FIGURE SPACE
+ 2008; PUNCTUATION SPACE
+ 2009; THIN SPACE
+ 200A; HAIR SPACE
+ 200B; ZERO WIDTH SPACE
+ 202F; NARROW NO-BREAK SPACE
+ 205F; MEDIUM MATHEMATICAL SPACE
+ 3000; IDEOGRAPHIC SPACE
+
+5.2 Control characters
+
+ Control characters (or characters with control function) cannot be
+ seen and can cause unpredictable results when displayed. Note that
+ the list below is split into two tables in appendix C: Table C.2.1
+ contains the ASCII code points, while Table C.2.2 contains the non-
+ ASCII code points. Most profiles of this document that want to
+ prohibit control characters will want to include both tables.
+
+ 0000-001F; [CONTROL CHARACTERS]
+ 007F; DELETE
+ 0080-009F; [CONTROL CHARACTERS]
+ 06DD; ARABIC END OF AYAH
+ 070F; SYRIAC ABBREVIATION MARK
+ 180E; MONGOLIAN VOWEL SEPARATOR
+
+
+
+Hoffman & Blanchet Standards Track [Page 11]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 200C; ZERO WIDTH NON-JOINER
+ 200D; ZERO WIDTH JOINER
+ 2028; LINE SEPARATOR
+ 2029; PARAGRAPH SEPARATOR
+ 2060; WORD JOINER
+ 2061; FUNCTION APPLICATION
+ 2062; INVISIBLE TIMES
+ 2063; INVISIBLE SEPARATOR
+ 206A-206F; [CONTROL CHARACTERS]
+ FEFF; ZERO WIDTH NO-BREAK SPACE
+ FFF9-FFFC; [CONTROL CHARACTERS]
+ 1D173-1D17A; [MUSICAL CONTROL CHARACTERS]
+
+5.3 Private use
+
+ Because private-use characters do not have defined meanings, they are
+ likely to be prohibited. The private-use characters are:
+
+ E000-F8FF; [PRIVATE USE, PLANE 0]
+ F0000-FFFFD; [PRIVATE USE, PLANE 15]
+ 100000-10FFFD; [PRIVATE USE, PLANE 16]
+
+5.4 Non-character code points
+
+ Non-character code points are code points that have been allocated in
+ ISO/IEC 10646 but are not characters. Because they are already
+ assigned, they are guaranteed not to later change into characters.
+
+ FDD0-FDEF; [NONCHARACTER CODE POINTS]
+ FFFE-FFFF; [NONCHARACTER CODE POINTS]
+ 1FFFE-1FFFF; [NONCHARACTER CODE POINTS]
+ 2FFFE-2FFFF; [NONCHARACTER CODE POINTS]
+ 3FFFE-3FFFF; [NONCHARACTER CODE POINTS]
+ 4FFFE-4FFFF; [NONCHARACTER CODE POINTS]
+ 5FFFE-5FFFF; [NONCHARACTER CODE POINTS]
+ 6FFFE-6FFFF; [NONCHARACTER CODE POINTS]
+ 7FFFE-7FFFF; [NONCHARACTER CODE POINTS]
+ 8FFFE-8FFFF; [NONCHARACTER CODE POINTS]
+ 9FFFE-9FFFF; [NONCHARACTER CODE POINTS]
+ AFFFE-AFFFF; [NONCHARACTER CODE POINTS]
+ BFFFE-BFFFF; [NONCHARACTER CODE POINTS]
+ CFFFE-CFFFF; [NONCHARACTER CODE POINTS]
+ DFFFE-DFFFF; [NONCHARACTER CODE POINTS]
+ EFFFE-EFFFF; [NONCHARACTER CODE POINTS]
+ FFFFE-FFFFF; [NONCHARACTER CODE POINTS]
+ 10FFFE-10FFFF; [NONCHARACTER CODE POINTS]
+
+
+
+
+
+Hoffman & Blanchet Standards Track [Page 12]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ The non-character code points are listed in the PropList.txt file
+ from the Unicode database.
+
+5.5 Surrogate codes
+
+ The following code points are permanently reserved for use as
+ surrogate code values in the UTF-16 encoding, will never be assigned
+ to characters in the Unicode repertoire, and are therefore
+ prohibited:
+
+ D800-DFFF; [SURROGATE CODES]
+
+5.6 Inappropriate for plain text
+
+ The following characters do not appear in regular text.
+
+ FFF9; INTERLINEAR ANNOTATION ANCHOR
+ FFFA; INTERLINEAR ANNOTATION SEPARATOR
+ FFFB; INTERLINEAR ANNOTATION TERMINATOR
+ FFFC; OBJECT REPLACEMENT CHARACTER
+
+ Although the replacement character (U+FFFD) might be used when a
+ string is displayed, it doesn't make sense for it to be part of the
+ string itself. It is often displayed by renderers to indicate "there
+ would be some character here, but it cannot be rendered". For
+ example, on a computer with no Asian fonts, a string with three
+ ideographs might be rendered with three replacement characters.
+
+ FFFD; REPLACEMENT CHARACTER
+
+5.7 Inappropriate for canonical representation
+
+ The ideographic description characters allow different sequences of
+ characters to be rendered the same way, which makes them
+ inappropriate for strings that have to have a single canonical
+ representation.
+
+ 2FF0-2FFB; [IDEOGRAPHIC DESCRIPTION CHARACTERS]
+
+5.8 Change display properties or are deprecated
+
+ The following characters can cause changes in display or the order in
+ which characters appear when rendered, or are deprecated in Unicode.
+
+ 0340; COMBINING GRAVE TONE MARK
+ 0341; COMBINING ACUTE TONE MARK
+ 200E; LEFT-TO-RIGHT MARK
+ 200F; RIGHT-TO-LEFT MARK
+
+
+
+Hoffman & Blanchet Standards Track [Page 13]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 202A; LEFT-TO-RIGHT EMBEDDING
+ 202B; RIGHT-TO-LEFT EMBEDDING
+ 202C; POP DIRECTIONAL FORMATTING
+ 202D; LEFT-TO-RIGHT OVERRIDE
+ 202E; RIGHT-TO-LEFT OVERRIDE
+ 206A; INHIBIT SYMMETRIC SWAPPING
+ 206B; ACTIVATE SYMMETRIC SWAPPING
+ 206C; INHIBIT ARABIC FORM SHAPING
+ 206D; ACTIVATE ARABIC FORM SHAPING
+ 206E; NATIONAL DIGIT SHAPES
+ 206F; NOMINAL DIGIT SHAPES
+
+5.9 Tagging characters
+
+ The following characters are used for tagging text and are invisible.
+
+ E0001; LANGUAGE TAG
+ E0020-E007F; [TAGGING CHARACTERS]
+
+6. Bidirectional Characters
+
+ Most characters are displayed from left to right, but some are
+ displayed from right to left. This feature of Unicode is called
+ "bidirectional text", or "bidi" for short. The Unicode standard has
+ an extensive discussion of how to reorder glyphs for display when
+ dealing with bidirectional text such as Arabic or Hebrew. See [UAX9]
+ for more information. In particular, all Unicode text is stored in
+ logical order.
+
+ A profile MAY choose to ignore bidirectional text. However, ignoring
+ bidirectional text can cause display ambiguities. For example, it is
+ quite easy to create two different strings with the same characters
+ (but in different order) that are correctly displayed identically.
+ Therefore, in order to avoid most problems with ambiguous
+ bidirectional text display, profile creators should strongly consider
+ including the bidirectional character handling described in this
+ section in their profile.
+
+ The stringprep process never emits both an error and a string. If an
+ error is detected during the checking of bidirectional strings, only
+ an error is returned.
+
+ [Unicode3.2] defines several bidirectional categories; each character
+ has one bidirectional category assigned to it. For the purposes of
+ the requirements below, an "RandALCat character" is a character that
+ has Unicode bidirectional categories "R" or "AL"; an "LCat character"
+ is a character that has Unicode bidirectional category "L". Note
+
+
+
+
+Hoffman & Blanchet Standards Track [Page 14]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ that there are many characters which fall in neither of the above
+ definitions; Latin digits (<U+0030> through <U+0039>) are examples of
+ this because they have bidirectional category "EN".
+
+ In any profile that specifies bidirectional character handling, all
+ three of the following requirements MUST be met:
+
+ 1) The characters in section 5.8 MUST be prohibited.
+
+ 2) If a string contains any RandALCat character, the string MUST NOT
+ contain any LCat character.
+
+ 3) If a string contains any RandALCat character, a RandALCat
+ character MUST be the first character of the string, and a
+ RandALCat character MUST be the last character of the string.
+
+ Note that requirement 3 prohibits strings such as <U+0627><U+0031>
+ ("aleph 1") but allows strings such as <U+0627><U+0031><U+0628>
+ ("aleph 1 beh"). [UAX9] goes into great detail about the display
+ order of strings that contain particular categories of characters in
+ particular sequences.
+
+ Table D.1 lists the characters that belong to Unicode bidirectional
+ categories "R" and "AL". Table D.2 lists all the characters that
+ belong to Unicode bidirectonal category "L". These tables are
+ derived from [Unicode3.2].
+
+7. Unassigned Code Points in Stringprep Profiles
+
+ This section describes two different types of strings in typical
+ protocols where internationalized strings are used: "stored strings"
+ and "queries". Of course, different Internet protocols use strings
+ very differently, so these terms cannot be used exactly in every
+ protocol that needs to use stringprep. In general, "stored strings"
+ are strings that are used in protocol identifiers and named entities,
+ such as names in digital certificates and DNS domain name parts.
+ "Queries" are strings that are used to match against strings that are
+ stored identifiers, such as user-entered names for digital
+ certificate authorities and DNS lookups.
+
+ All code points not assigned in the character repertoire named in a
+ stringprep profile are called "unassigned code points". Stored
+ strings using the profile MUST NOT contain any unassigned code
+ points. Queries for matching strings MAY contain unassigned code
+ points. Note that this is the only part of this document where the
+ requirements for queries differs from the requirements for stored
+ strings.
+
+
+
+
+Hoffman & Blanchet Standards Track [Page 15]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ Using two different policies for where unassigned code points can
+ appear removes the need for versioning in protocols that use
+ stringprep profiles. This is very useful since it makes the overall
+ processing simpler and does not impose a "protocol" to handle
+ versioning. It is expected that the ISO/IEC 10646 and Unicode
+ repertoires will be updated fairly frequently; at the time that this
+ document is being written, it has happened approximately once a year.
+ Each time a new version of a repertoire appears, a new version of a
+ profile MAY be created. Some end users will want to use the new code
+ points as soon as they are defined.
+
+ The list of unassigned code points MUST be given in a profile, and
+ that list MUST be used by implementations of the profile.
+
+ The goal of the requirements in this section is to prevent
+ comparisons between two strings that were both permitted to contain
+ unassigned code points. When two strings X and Y are compared and
+ string Y was prepared in a way that permits unassigned code points, a
+ negative result to the comparison is not definitive; it's possible
+ that the strings don't match even though they would match if a more
+ recent version of the profile were used for Y. However, if both X
+ and Y were prepared in a way that permits unassigned code points,
+ something worse can happen: even a positive result for the comparison
+ is not definitive. It is possible that the strings do match even
+ though they would not match if a more recent version of the profile
+ were used (one that prohibits a code point appearing in both X and
+ Y).
+
+ Due to the way that versioning is handled in this section, stored
+ strings that are embedded in structures that cannot be changed (such
+ as the signed parts of digital certificates) MUST NOT contain any
+ unassigned code points.
+
+7.1 Categories of code points
+
+ Each code point in a repertoire named by a profile of stringprep can
+ be categorized by how it acts in the process described in earlier
+ sections of this document:
+
+ AO Code points that can be in the output
+
+ MN Code points that cannot be in the output because they
+ never appear as output from mapping or normalization
+
+ D Code points that cannot be in the output because they are
+ disallowed in the prohibition step
+
+ U Unassigned code points
+
+
+
+Hoffman & Blanchet Standards Track [Page 16]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ A subsequent version of a profile that references a newer version of
+ a repertoire with new code points will inherently have some code
+ points move from category U to either D, MN, or AO. For backwards
+ compatibility, a subsequent version of a profile MUST NOT move code
+ points from any other category. That is, current AO, MN, or D code
+ points MUST NOT ever change to a different category.
+
+ Stored strings MUST NOT contain any code points outside of AO for the
+ latest version of a profile. That is, they are forbidden to contain
+ code points from the MN, D, or U categories.
+
+ Applications creating queries MUST treat U code points as if they
+ were AO when preparing the query to be entered in the process
+ described by a profile of stringprep. Those applications MAY
+ optionally have a preprocessor that provide stricter checks: treating
+ unassigned code points in the input as errors, or warning the user
+ about the fact that the code point is unassigned in the version of a
+ profile that the software is based on; such a choice is a local
+ matter for the software.
+
+7.2 Reasons for the difference between stored strings and queries
+
+ Different software using different versions of a stringprep profile
+ need to interoperate with maximal compatibility. The scheme
+ described in this section (stored strings MUST NOT contain unassigned
+ code points, queries MAY include unassigned code points) allows that
+ compatibility without introducing any known security or
+ interoperability issues.
+
+ The list below shows what happens if a query contains a code point
+ from category U that is allowed in a newer version of a profile. The
+ query either matches the string that was intended, or matches no
+ string at all. In this list, the query comes from an application
+ using version "oldVersion" of a profile, the stored string was
+ created using version "newVersion" of the same profile, and the code
+ point X was in category U in oldVersion, and has changed category to
+ AO, MN, or D. There are 3 possible scenarios:
+
+ 1. X is assigned to AO -- In newVersion, X is in category AO.
+ Because the application passed X through, it gets back a positive
+ match with the stored string. There is one exceptional case,
+ where X is a combining mark.
+
+ The order of combining marks is normalized, so if another
+ combining mark Y has a lower combining class than X then XY will
+ be put in the canonical order YX. (Unassigned code points are
+ never reordered, so this doesn't happen in oldVersion). If the
+ query contains YX, the query will get positive match with the
+
+
+
+Hoffman & Blanchet Standards Track [Page 17]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ stored string. However, no string can be stored with XY, so a
+ query with XY will get a negative answer to the test for matching.
+
+ 2. X is assigned to MN -- In newVersion, X is normalized to code
+ point "nX" and therefore X is now put in category MN. This cannot
+ exist in any stored string, so any query containing X will get a
+ negative answer to the test for matching. Note, however, if the
+ query had contained the letter nX, it would have positively
+ matched.
+
+ 3. X is assigned to D -- In newVersion, X is in category D. This
+ cannot exist in any stored string, so any query containing X will
+ get a negative answer to the test for matching.
+
+ In none of the cases does the query get data for a stored string
+ other than the one it actually tried to match against.
+
+ Profiles are stable between versions in the following sense: If a
+ string S has been prepared using newVersion, then it will not change
+ if it is subsequently prepared using oldVersion.
+
+7.3 Versions of applications and stored strings
+
+ Another way to see that this versioning system works is to compare
+ what happens when an application uses a newer or older version of a
+ profile.
+
+ Newer query application -- Suppose that a querying application is
+ using version newVersion and the stored string was created using
+ version oldVersion. This case is simple: there will be no characters
+ in the stored string that cannot be queried by the application
+ because the new profile uses a superset of the code points used for
+ making the stored string.
+
+ Newer stored string -- Suppose that a querying application is using
+ oldVersion and the stored string was created using a profile that
+ uses newVersion. Because the querying application let unassigned
+ code points pass through, the user can query on stored strings that
+ use code points in newVersion. No stored strings can have code
+ points that are unassigned in newVersion, since that is illegal. In
+ order to get a match, the querying application has to enter the
+ unassigned code points in the proper order, and has to use unassigned
+ code points that would make it through both the mapping and the
+ normalization steps.
+
+
+
+
+
+
+
+Hoffman & Blanchet Standards Track [Page 18]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+8. References
+
+8.1 Normative references
+
+ [UAX15] Mark Davis and Martin Duerst. Unicode Standard Annex
+ #15: Unicode Normalization Forms, Version 3.2.0.
+ <http://www.unicode.org/unicode/reports/tr15/tr15-
+ 22.html>.
+
+ [Unicode3.2] The Unicode Consortium. The Unicode Standard, Version
+ 3.2.0 is defined by The Unicode Standard, Version 3.0
+ (Reading, MA, Addison-Wesley, 2000. ISBN 0-201-61633-5),
+ as amended by the Unicode Standard Annex #27: Unicode
+ 3.1 (http://www.unicode.org/reports/tr27/) and by the
+ Unicode Standard Annex #28: Unicode 3.2
+ (http://www.unicode.org/reports/tr28/).
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+8.2 Informative references
+
+ [CharModel] Unicode Technical Report;17, Character Encoding Model.
+ <http://www.unicode.org/unicode/reports/tr17/>.
+
+ [Glossary] Unicode Glossary, <http://www.unicode.org/glossary/>.
+
+ [ISO10646] ISO/IEC, "Information Technology - Universal Multiple-
+ Octet Coded Character Set (UCS) - Part 1: Architecture
+ and Basic Multilingual Plane", ISO/IEC 10646-1:2000,
+ October 2000.
+
+ [RFC2434] Narten, T. and H. Alvestrand, "Guidelines for IANA
+ Considerations", BCP 26, RFC 2434, October 1998.
+
+ [UAX9] The Unicode Consortium. Unicode Standard Annex #9, The
+ Bidirectional Algorithm,
+ <http://www.unicode.org/unicode/reports/tr9/>.
+
+ [UTR21] Mark Davis. Case Mappings. Unicode Technical Report 21.
+ <http://www.unicode.org/unicode/reports/tr21/>.
+
+9. Security Considerations
+
+ Stringprep is used with Unicode characters. There are security
+ considerations that are specific to stringprep, and others that are
+ generic to using Unicode.
+
+
+
+
+Hoffman & Blanchet Standards Track [Page 19]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+9.1 Stringprep-specific security considerations
+
+ The Unicode and ISO/IEC 10646 repertoires have many characters that
+ look similar. In many cases, users of security protocols might do
+ visual matching, such as when comparing the names of trusted third
+ parties. Because it is impossible to map similar-looking characters
+ without a great deal of context such as knowing the fonts used,
+ stringprep does nothing to map similar-looking characters together
+ nor to prohibit some characters because they look like others. User
+ applications can help disambiguate some similar-looking characters by
+ showing the user when a string changes between scripts.
+
+ Most profiles of stringprep can cause changes in strings that are
+ input to stringprep. Because of this, protocols that have sets of
+ non-allowed characters or sequences MUST check for the non-allowed
+ characters or sequences after the stringprep processing.
+
+ This document does not mandate the checking of bidirectional
+ characters in section 6. If the requirements in section 6 are not
+ used in a profile of stringprep, it is easy to create many strings
+ whose characters are in different order but are displayed
+ identically. This can cause security-related user confusion similar
+ to look-alike characters, as described above.
+
+ Stringprep does not do anything to assure that any algorithms
+ translating characters from non-Unicode into Unicode produce the same
+ output in all implementations.
+
+ Some Unicode codepoints are invisible. Protocols that allow these
+ characters (that is, do not map them out or prohibit them in
+ stringprep) can cause users confusion when two identical-looking
+ strings do not match.
+
+9.2 Generic Unicode security considerations
+
+ Using Unicode characters explicitly forces applications to use
+ multi-octet characters. Converting an application from one that uses
+ single-octet characters to one that uses multi-octet characters must
+ be done very carefully, particularly in an application that checks
+ for values of characters or sorts characters.
+
+ Protocols that use stringprep usually also use encodings of Unicode,
+ such as UTF-8 or UTF-16. Some applications using those encodings
+ have been known to not check for illegal or ill-formed sequences in
+ the encodings, and thereby have not detected sequences of octets that
+ would have been detected if they used just ASCII. For example, in
+
+
+
+
+
+Hoffman & Blanchet Standards Track [Page 20]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ UTF-8 the octet sequence "0xC0 0xAB" is an illegal formation of
+ U+002B (plus sign). All programs should reject any string that is an
+ illegal or ill-formed octet sequence for the encoding being used.
+
+ Both Unicode normalization and conversion between Unicode encodings
+ can cause strings to grow or shrink. Programs that used fixed-size
+ buffers, or that make assumptions that buffers will always be greater
+ than or less than particular sizes, are likely to fail in insecure
+ fashions when using Unicode normalization or encoding conversions.
+
+ Covering an extensive list of security threats and considerations on
+ the use of current and future versions of Unicode is outside of the
+ scope of this document.
+
+10. IANA Considerations
+
+ Stringprep profiles MUST have IETF consensus as described in
+ [RFC2434]. Each profile MUST be reviewed by the IESG before it is
+ registered. The IESG MAY change a profile before registration.
+
+ IANA has set up a registry of stringprep profiles. This registry is
+ a single text file that lists the known profiles. Each entry in the
+ registry has three fields:
+
+ - Profile name
+
+ - RFC in which the profile is defined
+
+ - Indicator whether or not this is the newest version of the profile
+
+ Each version of a profile will remain listed in the registry forever.
+ That is, if a new version of a profile supersedes an earlier version,
+ both versions will continue to be listed in the registry, but the
+ current version indicator will be turned off for the earlier version
+ and turned on for the newer version.
+
+ It is probably harmful if a large number of profiles of stringprep
+ proliferate. Therefore, the IESG may reject proposals for new
+ profiles and instead suggest that protocols reuse existing profiles.
+
+
+
+
+
+
+
+
+
+
+
+
+Hoffman & Blanchet Standards Track [Page 21]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+11. Acknowledgements
+
+ Many people from the IETF IDN Working Group and the Unicode Technical
+ Committee contributed ideas that went into the first document of this
+ document. Mark Davis and Patrik Faltstrom were particularly helpful
+ in some of the ideas, such as the versioning description.
+
+ The IDN nameprep design team made many useful changes to the first
+ document. That team and its advisors include:
+
+ Asmus Freytag
+ Cathy Wissink
+ Francois Yergeau
+ James Seng
+ Marc Blanchet
+ Mark Davis
+ Martin Duerst
+ Patrik Faltstrom
+ Paul Hoffman
+
+ Additional significant improvements were proposed by:
+
+ Jonathan Rosenne
+ Kent Karlsson
+ Scott Hollenbeck
+ Dave Crocker
+ Erik Nordmark
+ Matitiahu Allouche
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Hoffman & Blanchet Standards Track [Page 22]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+A. Unicode repertoires
+
+ The following is the only repertoire covered in this document:
+
+ Unicode 3.2, as defined in [Unicode3.2].
+
+A.1 Unassigned code points in Unicode 3.2
+
+ ----- Start Table A.1 -----
+ 0221
+ 0234-024F
+ 02AE-02AF
+ 02EF-02FF
+ 0350-035F
+ 0370-0373
+ 0376-0379
+ 037B-037D
+ 037F-0383
+ 038B
+ 038D
+ 03A2
+ 03CF
+ 03F7-03FF
+ 0487
+ 04CF
+ 04F6-04F7
+ 04FA-04FF
+ 0510-0530
+ 0557-0558
+ 0560
+ 0588
+ 058B-0590
+ 05A2
+ 05BA
+ 05C5-05CF
+ 05EB-05EF
+ 05F5-060B
+ 060D-061A
+ 061C-061E
+ 0620
+ 063B-063F
+ 0656-065F
+ 06EE-06EF
+ 06FF
+ 070E
+ 072D-072F
+ 074B-077F
+ 07B2-0900
+
+
+
+Hoffman & Blanchet Standards Track [Page 23]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 0904
+ 093A-093B
+ 094E-094F
+ 0955-0957
+ 0971-0980
+ 0984
+ 098D-098E
+ 0991-0992
+ 09A9
+ 09B1
+ 09B3-09B5
+ 09BA-09BB
+ 09BD
+ 09C5-09C6
+ 09C9-09CA
+ 09CE-09D6
+ 09D8-09DB
+ 09DE
+ 09E4-09E5
+ 09FB-0A01
+ 0A03-0A04
+ 0A0B-0A0E
+ 0A11-0A12
+ 0A29
+ 0A31
+ 0A34
+ 0A37
+ 0A3A-0A3B
+ 0A3D
+ 0A43-0A46
+ 0A49-0A4A
+ 0A4E-0A58
+ 0A5D
+ 0A5F-0A65
+ 0A75-0A80
+ 0A84
+ 0A8C
+ 0A8E
+ 0A92
+ 0AA9
+ 0AB1
+ 0AB4
+ 0ABA-0ABB
+ 0AC6
+ 0ACA
+ 0ACE-0ACF
+ 0AD1-0ADF
+ 0AE1-0AE5
+
+
+
+Hoffman & Blanchet Standards Track [Page 24]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 0AF0-0B00
+ 0B04
+ 0B0D-0B0E
+ 0B11-0B12
+ 0B29
+ 0B31
+ 0B34-0B35
+ 0B3A-0B3B
+ 0B44-0B46
+ 0B49-0B4A
+ 0B4E-0B55
+ 0B58-0B5B
+ 0B5E
+ 0B62-0B65
+ 0B71-0B81
+ 0B84
+ 0B8B-0B8D
+ 0B91
+ 0B96-0B98
+ 0B9B
+ 0B9D
+ 0BA0-0BA2
+ 0BA5-0BA7
+ 0BAB-0BAD
+ 0BB6
+ 0BBA-0BBD
+ 0BC3-0BC5
+ 0BC9
+ 0BCE-0BD6
+ 0BD8-0BE6
+ 0BF3-0C00
+ 0C04
+ 0C0D
+ 0C11
+ 0C29
+ 0C34
+ 0C3A-0C3D
+ 0C45
+ 0C49
+ 0C4E-0C54
+ 0C57-0C5F
+ 0C62-0C65
+ 0C70-0C81
+ 0C84
+ 0C8D
+ 0C91
+ 0CA9
+ 0CB4
+
+
+
+Hoffman & Blanchet Standards Track [Page 25]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 0CBA-0CBD
+ 0CC5
+ 0CC9
+ 0CCE-0CD4
+ 0CD7-0CDD
+ 0CDF
+ 0CE2-0CE5
+ 0CF0-0D01
+ 0D04
+ 0D0D
+ 0D11
+ 0D29
+ 0D3A-0D3D
+ 0D44-0D45
+ 0D49
+ 0D4E-0D56
+ 0D58-0D5F
+ 0D62-0D65
+ 0D70-0D81
+ 0D84
+ 0D97-0D99
+ 0DB2
+ 0DBC
+ 0DBE-0DBF
+ 0DC7-0DC9
+ 0DCB-0DCE
+ 0DD5
+ 0DD7
+ 0DE0-0DF1
+ 0DF5-0E00
+ 0E3B-0E3E
+ 0E5C-0E80
+ 0E83
+ 0E85-0E86
+ 0E89
+ 0E8B-0E8C
+ 0E8E-0E93
+ 0E98
+ 0EA0
+ 0EA4
+ 0EA6
+ 0EA8-0EA9
+ 0EAC
+ 0EBA
+ 0EBE-0EBF
+ 0EC5
+ 0EC7
+ 0ECE-0ECF
+
+
+
+Hoffman & Blanchet Standards Track [Page 26]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 0EDA-0EDB
+ 0EDE-0EFF
+ 0F48
+ 0F6B-0F70
+ 0F8C-0F8F
+ 0F98
+ 0FBD
+ 0FCD-0FCE
+ 0FD0-0FFF
+ 1022
+ 1028
+ 102B
+ 1033-1035
+ 103A-103F
+ 105A-109F
+ 10C6-10CF
+ 10F9-10FA
+ 10FC-10FF
+ 115A-115E
+ 11A3-11A7
+ 11FA-11FF
+ 1207
+ 1247
+ 1249
+ 124E-124F
+ 1257
+ 1259
+ 125E-125F
+ 1287
+ 1289
+ 128E-128F
+ 12AF
+ 12B1
+ 12B6-12B7
+ 12BF
+ 12C1
+ 12C6-12C7
+ 12CF
+ 12D7
+ 12EF
+ 130F
+ 1311
+ 1316-1317
+ 131F
+ 1347
+ 135B-1360
+ 137D-139F
+ 13F5-1400
+
+
+
+Hoffman & Blanchet Standards Track [Page 27]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 1677-167F
+ 169D-169F
+ 16F1-16FF
+ 170D
+ 1715-171F
+ 1737-173F
+ 1754-175F
+ 176D
+ 1771
+ 1774-177F
+ 17DD-17DF
+ 17EA-17FF
+ 180F
+ 181A-181F
+ 1878-187F
+ 18AA-1DFF
+ 1E9C-1E9F
+ 1EFA-1EFF
+ 1F16-1F17
+ 1F1E-1F1F
+ 1F46-1F47
+ 1F4E-1F4F
+ 1F58
+ 1F5A
+ 1F5C
+ 1F5E
+ 1F7E-1F7F
+ 1FB5
+ 1FC5
+ 1FD4-1FD5
+ 1FDC
+ 1FF0-1FF1
+ 1FF5
+ 1FFF
+ 2053-2056
+ 2058-205E
+ 2064-2069
+ 2072-2073
+ 208F-209F
+ 20B2-20CF
+ 20EB-20FF
+ 213B-213C
+ 214C-2152
+ 2184-218F
+ 23CF-23FF
+ 2427-243F
+ 244B-245F
+ 24FF
+
+
+
+Hoffman & Blanchet Standards Track [Page 28]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 2614-2615
+ 2618
+ 267E-267F
+ 268A-2700
+ 2705
+ 270A-270B
+ 2728
+ 274C
+ 274E
+ 2753-2755
+ 2757
+ 275F-2760
+ 2795-2797
+ 27B0
+ 27BF-27CF
+ 27EC-27EF
+ 2B00-2E7F
+ 2E9A
+ 2EF4-2EFF
+ 2FD6-2FEF
+ 2FFC-2FFF
+ 3040
+ 3097-3098
+ 3100-3104
+ 312D-3130
+ 318F
+ 31B8-31EF
+ 321D-321F
+ 3244-3250
+ 327C-327E
+ 32CC-32CF
+ 32FF
+ 3377-337A
+ 33DE-33DF
+ 33FF
+ 4DB6-4DFF
+ 9FA6-9FFF
+ A48D-A48F
+ A4C7-ABFF
+ D7A4-D7FF
+ FA2E-FA2F
+ FA6B-FAFF
+ FB07-FB12
+ FB18-FB1C
+ FB37
+ FB3D
+ FB3F
+ FB42
+
+
+
+Hoffman & Blanchet Standards Track [Page 29]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ FB45
+ FBB2-FBD2
+ FD40-FD4F
+ FD90-FD91
+ FDC8-FDCF
+ FDFD-FDFF
+ FE10-FE1F
+ FE24-FE2F
+ FE47-FE48
+ FE53
+ FE67
+ FE6C-FE6F
+ FE75
+ FEFD-FEFE
+ FF00
+ FFBF-FFC1
+ FFC8-FFC9
+ FFD0-FFD1
+ FFD8-FFD9
+ FFDD-FFDF
+ FFE7
+ FFEF-FFF8
+ 10000-102FF
+ 1031F
+ 10324-1032F
+ 1034B-103FF
+ 10426-10427
+ 1044E-1CFFF
+ 1D0F6-1D0FF
+ 1D127-1D129
+ 1D1DE-1D3FF
+ 1D455
+ 1D49D
+ 1D4A0-1D4A1
+ 1D4A3-1D4A4
+ 1D4A7-1D4A8
+ 1D4AD
+ 1D4BA
+ 1D4BC
+ 1D4C1
+ 1D4C4
+ 1D506
+ 1D50B-1D50C
+ 1D515
+ 1D51D
+ 1D53A
+ 1D53F
+ 1D545
+
+
+
+Hoffman & Blanchet Standards Track [Page 30]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 1D547-1D549
+ 1D551
+ 1D6A4-1D6A7
+ 1D7CA-1D7CD
+ 1D800-1FFFD
+ 2A6D7-2F7FF
+ 2FA1E-2FFFD
+ 30000-3FFFD
+ 40000-4FFFD
+ 50000-5FFFD
+ 60000-6FFFD
+ 70000-7FFFD
+ 80000-8FFFD
+ 90000-9FFFD
+ A0000-AFFFD
+ B0000-BFFFD
+ C0000-CFFFD
+ D0000-DFFFD
+ E0000
+ E0002-E001F
+ E0080-EFFFD
+ ----- End Table A.1 -----
+
+B. Mapping Tables
+
+ The following is the mapping table from section 3. The table has
+ three columns:
+
+ - the code point that is mapped from
+ - the zero or more code points that it is mapped to
+ - the reason for the mapping
+
+ The columns are separated by semicolons. Note that the second column
+ may be empty, or it may have one code point, or it may have more than
+ one code point, with each code point separated by a space.
+
+B.1 Commonly mapped to nothing
+
+ ----- Start Table B.1 -----
+ 00AD; ; Map to nothing
+ 034F; ; Map to nothing
+ 1806; ; Map to nothing
+ 180B; ; Map to nothing
+ 180C; ; Map to nothing
+ 180D; ; Map to nothing
+ 200B; ; Map to nothing
+ 200C; ; Map to nothing
+ 200D; ; Map to nothing
+
+
+
+Hoffman & Blanchet Standards Track [Page 31]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 2060; ; Map to nothing
+ FE00; ; Map to nothing
+ FE01; ; Map to nothing
+ FE02; ; Map to nothing
+ FE03; ; Map to nothing
+ FE04; ; Map to nothing
+ FE05; ; Map to nothing
+ FE06; ; Map to nothing
+ FE07; ; Map to nothing
+ FE08; ; Map to nothing
+ FE09; ; Map to nothing
+ FE0A; ; Map to nothing
+ FE0B; ; Map to nothing
+ FE0C; ; Map to nothing
+ FE0D; ; Map to nothing
+ FE0E; ; Map to nothing
+ FE0F; ; Map to nothing
+ FEFF; ; Map to nothing
+ ----- End Table B.1 -----
+
+B.2 Mapping for case-folding used with NFKC
+
+ ----- Start Table B.2 -----
+ 0041; 0061; Case map
+ 0042; 0062; Case map
+ 0043; 0063; Case map
+ 0044; 0064; Case map
+ 0045; 0065; Case map
+ 0046; 0066; Case map
+ 0047; 0067; Case map
+ 0048; 0068; Case map
+ 0049; 0069; Case map
+ 004A; 006A; Case map
+ 004B; 006B; Case map
+ 004C; 006C; Case map
+ 004D; 006D; Case map
+ 004E; 006E; Case map
+ 004F; 006F; Case map
+ 0050; 0070; Case map
+ 0051; 0071; Case map
+ 0052; 0072; Case map
+ 0053; 0073; Case map
+ 0054; 0074; Case map
+ 0055; 0075; Case map
+ 0056; 0076; Case map
+ 0057; 0077; Case map
+ 0058; 0078; Case map
+ 0059; 0079; Case map
+
+
+
+Hoffman & Blanchet Standards Track [Page 32]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 005A; 007A; Case map
+ 00B5; 03BC; Case map
+ 00C0; 00E0; Case map
+ 00C1; 00E1; Case map
+ 00C2; 00E2; Case map
+ 00C3; 00E3; Case map
+ 00C4; 00E4; Case map
+ 00C5; 00E5; Case map
+ 00C6; 00E6; Case map
+ 00C7; 00E7; Case map
+ 00C8; 00E8; Case map
+ 00C9; 00E9; Case map
+ 00CA; 00EA; Case map
+ 00CB; 00EB; Case map
+ 00CC; 00EC; Case map
+ 00CD; 00ED; Case map
+ 00CE; 00EE; Case map
+ 00CF; 00EF; Case map
+ 00D0; 00F0; Case map
+ 00D1; 00F1; Case map
+ 00D2; 00F2; Case map
+ 00D3; 00F3; Case map
+ 00D4; 00F4; Case map
+ 00D5; 00F5; Case map
+ 00D6; 00F6; Case map
+ 00D8; 00F8; Case map
+ 00D9; 00F9; Case map
+ 00DA; 00FA; Case map
+ 00DB; 00FB; Case map
+ 00DC; 00FC; Case map
+ 00DD; 00FD; Case map
+ 00DE; 00FE; Case map
+ 00DF; 0073 0073; Case map
+ 0100; 0101; Case map
+ 0102; 0103; Case map
+ 0104; 0105; Case map
+ 0106; 0107; Case map
+ 0108; 0109; Case map
+ 010A; 010B; Case map
+ 010C; 010D; Case map
+ 010E; 010F; Case map
+ 0110; 0111; Case map
+ 0112; 0113; Case map
+ 0114; 0115; Case map
+ 0116; 0117; Case map
+ 0118; 0119; Case map
+ 011A; 011B; Case map
+ 011C; 011D; Case map
+
+
+
+Hoffman & Blanchet Standards Track [Page 33]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 011E; 011F; Case map
+ 0120; 0121; Case map
+ 0122; 0123; Case map
+ 0124; 0125; Case map
+ 0126; 0127; Case map
+ 0128; 0129; Case map
+ 012A; 012B; Case map
+ 012C; 012D; Case map
+ 012E; 012F; Case map
+ 0130; 0069 0307; Case map
+ 0132; 0133; Case map
+ 0134; 0135; Case map
+ 0136; 0137; Case map
+ 0139; 013A; Case map
+ 013B; 013C; Case map
+ 013D; 013E; Case map
+ 013F; 0140; Case map
+ 0141; 0142; Case map
+ 0143; 0144; Case map
+ 0145; 0146; Case map
+ 0147; 0148; Case map
+ 0149; 02BC 006E; Case map
+ 014A; 014B; Case map
+ 014C; 014D; Case map
+ 014E; 014F; Case map
+ 0150; 0151; Case map
+ 0152; 0153; Case map
+ 0154; 0155; Case map
+ 0156; 0157; Case map
+ 0158; 0159; Case map
+ 015A; 015B; Case map
+ 015C; 015D; Case map
+ 015E; 015F; Case map
+ 0160; 0161; Case map
+ 0162; 0163; Case map
+ 0164; 0165; Case map
+ 0166; 0167; Case map
+ 0168; 0169; Case map
+ 016A; 016B; Case map
+ 016C; 016D; Case map
+ 016E; 016F; Case map
+ 0170; 0171; Case map
+ 0172; 0173; Case map
+ 0174; 0175; Case map
+ 0176; 0177; Case map
+ 0178; 00FF; Case map
+ 0179; 017A; Case map
+ 017B; 017C; Case map
+
+
+
+Hoffman & Blanchet Standards Track [Page 34]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 017D; 017E; Case map
+ 017F; 0073; Case map
+ 0181; 0253; Case map
+ 0182; 0183; Case map
+ 0184; 0185; Case map
+ 0186; 0254; Case map
+ 0187; 0188; Case map
+ 0189; 0256; Case map
+ 018A; 0257; Case map
+ 018B; 018C; Case map
+ 018E; 01DD; Case map
+ 018F; 0259; Case map
+ 0190; 025B; Case map
+ 0191; 0192; Case map
+ 0193; 0260; Case map
+ 0194; 0263; Case map
+ 0196; 0269; Case map
+ 0197; 0268; Case map
+ 0198; 0199; Case map
+ 019C; 026F; Case map
+ 019D; 0272; Case map
+ 019F; 0275; Case map
+ 01A0; 01A1; Case map
+ 01A2; 01A3; Case map
+ 01A4; 01A5; Case map
+ 01A6; 0280; Case map
+ 01A7; 01A8; Case map
+ 01A9; 0283; Case map
+ 01AC; 01AD; Case map
+ 01AE; 0288; Case map
+ 01AF; 01B0; Case map
+ 01B1; 028A; Case map
+ 01B2; 028B; Case map
+ 01B3; 01B4; Case map
+ 01B5; 01B6; Case map
+ 01B7; 0292; Case map
+ 01B8; 01B9; Case map
+ 01BC; 01BD; Case map
+ 01C4; 01C6; Case map
+ 01C5; 01C6; Case map
+ 01C7; 01C9; Case map
+ 01C8; 01C9; Case map
+ 01CA; 01CC; Case map
+ 01CB; 01CC; Case map
+ 01CD; 01CE; Case map
+ 01CF; 01D0; Case map
+ 01D1; 01D2; Case map
+ 01D3; 01D4; Case map
+
+
+
+Hoffman & Blanchet Standards Track [Page 35]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 01D5; 01D6; Case map
+ 01D7; 01D8; Case map
+ 01D9; 01DA; Case map
+ 01DB; 01DC; Case map
+ 01DE; 01DF; Case map
+ 01E0; 01E1; Case map
+ 01E2; 01E3; Case map
+ 01E4; 01E5; Case map
+ 01E6; 01E7; Case map
+ 01E8; 01E9; Case map
+ 01EA; 01EB; Case map
+ 01EC; 01ED; Case map
+ 01EE; 01EF; Case map
+ 01F0; 006A 030C; Case map
+ 01F1; 01F3; Case map
+ 01F2; 01F3; Case map
+ 01F4; 01F5; Case map
+ 01F6; 0195; Case map
+ 01F7; 01BF; Case map
+ 01F8; 01F9; Case map
+ 01FA; 01FB; Case map
+ 01FC; 01FD; Case map
+ 01FE; 01FF; Case map
+ 0200; 0201; Case map
+ 0202; 0203; Case map
+ 0204; 0205; Case map
+ 0206; 0207; Case map
+ 0208; 0209; Case map
+ 020A; 020B; Case map
+ 020C; 020D; Case map
+ 020E; 020F; Case map
+ 0210; 0211; Case map
+ 0212; 0213; Case map
+ 0214; 0215; Case map
+ 0216; 0217; Case map
+ 0218; 0219; Case map
+ 021A; 021B; Case map
+ 021C; 021D; Case map
+ 021E; 021F; Case map
+ 0220; 019E; Case map
+ 0222; 0223; Case map
+ 0224; 0225; Case map
+ 0226; 0227; Case map
+ 0228; 0229; Case map
+ 022A; 022B; Case map
+ 022C; 022D; Case map
+ 022E; 022F; Case map
+ 0230; 0231; Case map
+
+
+
+Hoffman & Blanchet Standards Track [Page 36]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 0232; 0233; Case map
+ 0345; 03B9; Case map
+ 037A; 0020 03B9; Additional folding
+ 0386; 03AC; Case map
+ 0388; 03AD; Case map
+ 0389; 03AE; Case map
+ 038A; 03AF; Case map
+ 038C; 03CC; Case map
+ 038E; 03CD; Case map
+ 038F; 03CE; Case map
+ 0390; 03B9 0308 0301; Case map
+ 0391; 03B1; Case map
+ 0392; 03B2; Case map
+ 0393; 03B3; Case map
+ 0394; 03B4; Case map
+ 0395; 03B5; Case map
+ 0396; 03B6; Case map
+ 0397; 03B7; Case map
+ 0398; 03B8; Case map
+ 0399; 03B9; Case map
+ 039A; 03BA; Case map
+ 039B; 03BB; Case map
+ 039C; 03BC; Case map
+ 039D; 03BD; Case map
+ 039E; 03BE; Case map
+ 039F; 03BF; Case map
+ 03A0; 03C0; Case map
+ 03A1; 03C1; Case map
+ 03A3; 03C3; Case map
+ 03A4; 03C4; Case map
+ 03A5; 03C5; Case map
+ 03A6; 03C6; Case map
+ 03A7; 03C7; Case map
+ 03A8; 03C8; Case map
+ 03A9; 03C9; Case map
+ 03AA; 03CA; Case map
+ 03AB; 03CB; Case map
+ 03B0; 03C5 0308 0301; Case map
+ 03C2; 03C3; Case map
+ 03D0; 03B2; Case map
+ 03D1; 03B8; Case map
+ 03D2; 03C5; Additional folding
+ 03D3; 03CD; Additional folding
+ 03D4; 03CB; Additional folding
+ 03D5; 03C6; Case map
+ 03D6; 03C0; Case map
+ 03D8; 03D9; Case map
+ 03DA; 03DB; Case map
+
+
+
+Hoffman & Blanchet Standards Track [Page 37]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 03DC; 03DD; Case map
+ 03DE; 03DF; Case map
+ 03E0; 03E1; Case map
+ 03E2; 03E3; Case map
+ 03E4; 03E5; Case map
+ 03E6; 03E7; Case map
+ 03E8; 03E9; Case map
+ 03EA; 03EB; Case map
+ 03EC; 03ED; Case map
+ 03EE; 03EF; Case map
+ 03F0; 03BA; Case map
+ 03F1; 03C1; Case map
+ 03F2; 03C3; Case map
+ 03F4; 03B8; Case map
+ 03F5; 03B5; Case map
+ 0400; 0450; Case map
+ 0401; 0451; Case map
+ 0402; 0452; Case map
+ 0403; 0453; Case map
+ 0404; 0454; Case map
+ 0405; 0455; Case map
+ 0406; 0456; Case map
+ 0407; 0457; Case map
+ 0408; 0458; Case map
+ 0409; 0459; Case map
+ 040A; 045A; Case map
+ 040B; 045B; Case map
+ 040C; 045C; Case map
+ 040D; 045D; Case map
+ 040E; 045E; Case map
+ 040F; 045F; Case map
+ 0410; 0430; Case map
+ 0411; 0431; Case map
+ 0412; 0432; Case map
+ 0413; 0433; Case map
+ 0414; 0434; Case map
+ 0415; 0435; Case map
+ 0416; 0436; Case map
+ 0417; 0437; Case map
+ 0418; 0438; Case map
+ 0419; 0439; Case map
+ 041A; 043A; Case map
+ 041B; 043B; Case map
+ 041C; 043C; Case map
+ 041D; 043D; Case map
+ 041E; 043E; Case map
+ 041F; 043F; Case map
+ 0420; 0440; Case map
+
+
+
+Hoffman & Blanchet Standards Track [Page 38]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 0421; 0441; Case map
+ 0422; 0442; Case map
+ 0423; 0443; Case map
+ 0424; 0444; Case map
+ 0425; 0445; Case map
+ 0426; 0446; Case map
+ 0427; 0447; Case map
+ 0428; 0448; Case map
+ 0429; 0449; Case map
+ 042A; 044A; Case map
+ 042B; 044B; Case map
+ 042C; 044C; Case map
+ 042D; 044D; Case map
+ 042E; 044E; Case map
+ 042F; 044F; Case map
+ 0460; 0461; Case map
+ 0462; 0463; Case map
+ 0464; 0465; Case map
+ 0466; 0467; Case map
+ 0468; 0469; Case map
+ 046A; 046B; Case map
+ 046C; 046D; Case map
+ 046E; 046F; Case map
+ 0470; 0471; Case map
+ 0472; 0473; Case map
+ 0474; 0475; Case map
+ 0476; 0477; Case map
+ 0478; 0479; Case map
+ 047A; 047B; Case map
+ 047C; 047D; Case map
+ 047E; 047F; Case map
+ 0480; 0481; Case map
+ 048A; 048B; Case map
+ 048C; 048D; Case map
+ 048E; 048F; Case map
+ 0490; 0491; Case map
+ 0492; 0493; Case map
+ 0494; 0495; Case map
+ 0496; 0497; Case map
+ 0498; 0499; Case map
+ 049A; 049B; Case map
+ 049C; 049D; Case map
+ 049E; 049F; Case map
+ 04A0; 04A1; Case map
+ 04A2; 04A3; Case map
+ 04A4; 04A5; Case map
+ 04A6; 04A7; Case map
+ 04A8; 04A9; Case map
+
+
+
+Hoffman & Blanchet Standards Track [Page 39]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 04AA; 04AB; Case map
+ 04AC; 04AD; Case map
+ 04AE; 04AF; Case map
+ 04B0; 04B1; Case map
+ 04B2; 04B3; Case map
+ 04B4; 04B5; Case map
+ 04B6; 04B7; Case map
+ 04B8; 04B9; Case map
+ 04BA; 04BB; Case map
+ 04BC; 04BD; Case map
+ 04BE; 04BF; Case map
+ 04C1; 04C2; Case map
+ 04C3; 04C4; Case map
+ 04C5; 04C6; Case map
+ 04C7; 04C8; Case map
+ 04C9; 04CA; Case map
+ 04CB; 04CC; Case map
+ 04CD; 04CE; Case map
+ 04D0; 04D1; Case map
+ 04D2; 04D3; Case map
+ 04D4; 04D5; Case map
+ 04D6; 04D7; Case map
+ 04D8; 04D9; Case map
+ 04DA; 04DB; Case map
+ 04DC; 04DD; Case map
+ 04DE; 04DF; Case map
+ 04E0; 04E1; Case map
+ 04E2; 04E3; Case map
+ 04E4; 04E5; Case map
+ 04E6; 04E7; Case map
+ 04E8; 04E9; Case map
+ 04EA; 04EB; Case map
+ 04EC; 04ED; Case map
+ 04EE; 04EF; Case map
+ 04F0; 04F1; Case map
+ 04F2; 04F3; Case map
+ 04F4; 04F5; Case map
+ 04F8; 04F9; Case map
+ 0500; 0501; Case map
+ 0502; 0503; Case map
+ 0504; 0505; Case map
+ 0506; 0507; Case map
+ 0508; 0509; Case map
+ 050A; 050B; Case map
+ 050C; 050D; Case map
+ 050E; 050F; Case map
+ 0531; 0561; Case map
+ 0532; 0562; Case map
+
+
+
+Hoffman & Blanchet Standards Track [Page 40]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 0533; 0563; Case map
+ 0534; 0564; Case map
+ 0535; 0565; Case map
+ 0536; 0566; Case map
+ 0537; 0567; Case map
+ 0538; 0568; Case map
+ 0539; 0569; Case map
+ 053A; 056A; Case map
+ 053B; 056B; Case map
+ 053C; 056C; Case map
+ 053D; 056D; Case map
+ 053E; 056E; Case map
+ 053F; 056F; Case map
+ 0540; 0570; Case map
+ 0541; 0571; Case map
+ 0542; 0572; Case map
+ 0543; 0573; Case map
+ 0544; 0574; Case map
+ 0545; 0575; Case map
+ 0546; 0576; Case map
+ 0547; 0577; Case map
+ 0548; 0578; Case map
+ 0549; 0579; Case map
+ 054A; 057A; Case map
+ 054B; 057B; Case map
+ 054C; 057C; Case map
+ 054D; 057D; Case map
+ 054E; 057E; Case map
+ 054F; 057F; Case map
+ 0550; 0580; Case map
+ 0551; 0581; Case map
+ 0552; 0582; Case map
+ 0553; 0583; Case map
+ 0554; 0584; Case map
+ 0555; 0585; Case map
+ 0556; 0586; Case map
+ 0587; 0565 0582; Case map
+ 1E00; 1E01; Case map
+ 1E02; 1E03; Case map
+ 1E04; 1E05; Case map
+ 1E06; 1E07; Case map
+ 1E08; 1E09; Case map
+ 1E0A; 1E0B; Case map
+ 1E0C; 1E0D; Case map
+ 1E0E; 1E0F; Case map
+ 1E10; 1E11; Case map
+ 1E12; 1E13; Case map
+ 1E14; 1E15; Case map
+
+
+
+Hoffman & Blanchet Standards Track [Page 41]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 1E16; 1E17; Case map
+ 1E18; 1E19; Case map
+ 1E1A; 1E1B; Case map
+ 1E1C; 1E1D; Case map
+ 1E1E; 1E1F; Case map
+ 1E20; 1E21; Case map
+ 1E22; 1E23; Case map
+ 1E24; 1E25; Case map
+ 1E26; 1E27; Case map
+ 1E28; 1E29; Case map
+ 1E2A; 1E2B; Case map
+ 1E2C; 1E2D; Case map
+ 1E2E; 1E2F; Case map
+ 1E30; 1E31; Case map
+ 1E32; 1E33; Case map
+ 1E34; 1E35; Case map
+ 1E36; 1E37; Case map
+ 1E38; 1E39; Case map
+ 1E3A; 1E3B; Case map
+ 1E3C; 1E3D; Case map
+ 1E3E; 1E3F; Case map
+ 1E40; 1E41; Case map
+ 1E42; 1E43; Case map
+ 1E44; 1E45; Case map
+ 1E46; 1E47; Case map
+ 1E48; 1E49; Case map
+ 1E4A; 1E4B; Case map
+ 1E4C; 1E4D; Case map
+ 1E4E; 1E4F; Case map
+ 1E50; 1E51; Case map
+ 1E52; 1E53; Case map
+ 1E54; 1E55; Case map
+ 1E56; 1E57; Case map
+ 1E58; 1E59; Case map
+ 1E5A; 1E5B; Case map
+ 1E5C; 1E5D; Case map
+ 1E5E; 1E5F; Case map
+ 1E60; 1E61; Case map
+ 1E62; 1E63; Case map
+ 1E64; 1E65; Case map
+ 1E66; 1E67; Case map
+ 1E68; 1E69; Case map
+ 1E6A; 1E6B; Case map
+ 1E6C; 1E6D; Case map
+ 1E6E; 1E6F; Case map
+ 1E70; 1E71; Case map
+ 1E72; 1E73; Case map
+ 1E74; 1E75; Case map
+
+
+
+Hoffman & Blanchet Standards Track [Page 42]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 1E76; 1E77; Case map
+ 1E78; 1E79; Case map
+ 1E7A; 1E7B; Case map
+ 1E7C; 1E7D; Case map
+ 1E7E; 1E7F; Case map
+ 1E80; 1E81; Case map
+ 1E82; 1E83; Case map
+ 1E84; 1E85; Case map
+ 1E86; 1E87; Case map
+ 1E88; 1E89; Case map
+ 1E8A; 1E8B; Case map
+ 1E8C; 1E8D; Case map
+ 1E8E; 1E8F; Case map
+ 1E90; 1E91; Case map
+ 1E92; 1E93; Case map
+ 1E94; 1E95; Case map
+ 1E96; 0068 0331; Case map
+ 1E97; 0074 0308; Case map
+ 1E98; 0077 030A; Case map
+ 1E99; 0079 030A; Case map
+ 1E9A; 0061 02BE; Case map
+ 1E9B; 1E61; Case map
+ 1EA0; 1EA1; Case map
+ 1EA2; 1EA3; Case map
+ 1EA4; 1EA5; Case map
+ 1EA6; 1EA7; Case map
+ 1EA8; 1EA9; Case map
+ 1EAA; 1EAB; Case map
+ 1EAC; 1EAD; Case map
+ 1EAE; 1EAF; Case map
+ 1EB0; 1EB1; Case map
+ 1EB2; 1EB3; Case map
+ 1EB4; 1EB5; Case map
+ 1EB6; 1EB7; Case map
+ 1EB8; 1EB9; Case map
+ 1EBA; 1EBB; Case map
+ 1EBC; 1EBD; Case map
+ 1EBE; 1EBF; Case map
+ 1EC0; 1EC1; Case map
+ 1EC2; 1EC3; Case map
+ 1EC4; 1EC5; Case map
+ 1EC6; 1EC7; Case map
+ 1EC8; 1EC9; Case map
+ 1ECA; 1ECB; Case map
+ 1ECC; 1ECD; Case map
+ 1ECE; 1ECF; Case map
+ 1ED0; 1ED1; Case map
+ 1ED2; 1ED3; Case map
+
+
+
+Hoffman & Blanchet Standards Track [Page 43]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 1ED4; 1ED5; Case map
+ 1ED6; 1ED7; Case map
+ 1ED8; 1ED9; Case map
+ 1EDA; 1EDB; Case map
+ 1EDC; 1EDD; Case map
+ 1EDE; 1EDF; Case map
+ 1EE0; 1EE1; Case map
+ 1EE2; 1EE3; Case map
+ 1EE4; 1EE5; Case map
+ 1EE6; 1EE7; Case map
+ 1EE8; 1EE9; Case map
+ 1EEA; 1EEB; Case map
+ 1EEC; 1EED; Case map
+ 1EEE; 1EEF; Case map
+ 1EF0; 1EF1; Case map
+ 1EF2; 1EF3; Case map
+ 1EF4; 1EF5; Case map
+ 1EF6; 1EF7; Case map
+ 1EF8; 1EF9; Case map
+ 1F08; 1F00; Case map
+ 1F09; 1F01; Case map
+ 1F0A; 1F02; Case map
+ 1F0B; 1F03; Case map
+ 1F0C; 1F04; Case map
+ 1F0D; 1F05; Case map
+ 1F0E; 1F06; Case map
+ 1F0F; 1F07; Case map
+ 1F18; 1F10; Case map
+ 1F19; 1F11; Case map
+ 1F1A; 1F12; Case map
+ 1F1B; 1F13; Case map
+ 1F1C; 1F14; Case map
+ 1F1D; 1F15; Case map
+ 1F28; 1F20; Case map
+ 1F29; 1F21; Case map
+ 1F2A; 1F22; Case map
+ 1F2B; 1F23; Case map
+ 1F2C; 1F24; Case map
+ 1F2D; 1F25; Case map
+ 1F2E; 1F26; Case map
+ 1F2F; 1F27; Case map
+ 1F38; 1F30; Case map
+ 1F39; 1F31; Case map
+ 1F3A; 1F32; Case map
+ 1F3B; 1F33; Case map
+ 1F3C; 1F34; Case map
+ 1F3D; 1F35; Case map
+ 1F3E; 1F36; Case map
+
+
+
+Hoffman & Blanchet Standards Track [Page 44]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 1F3F; 1F37; Case map
+ 1F48; 1F40; Case map
+ 1F49; 1F41; Case map
+ 1F4A; 1F42; Case map
+ 1F4B; 1F43; Case map
+ 1F4C; 1F44; Case map
+ 1F4D; 1F45; Case map
+ 1F50; 03C5 0313; Case map
+ 1F52; 03C5 0313 0300; Case map
+ 1F54; 03C5 0313 0301; Case map
+ 1F56; 03C5 0313 0342; Case map
+ 1F59; 1F51; Case map
+ 1F5B; 1F53; Case map
+ 1F5D; 1F55; Case map
+ 1F5F; 1F57; Case map
+ 1F68; 1F60; Case map
+ 1F69; 1F61; Case map
+ 1F6A; 1F62; Case map
+ 1F6B; 1F63; Case map
+ 1F6C; 1F64; Case map
+ 1F6D; 1F65; Case map
+ 1F6E; 1F66; Case map
+ 1F6F; 1F67; Case map
+ 1F80; 1F00 03B9; Case map
+ 1F81; 1F01 03B9; Case map
+ 1F82; 1F02 03B9; Case map
+ 1F83; 1F03 03B9; Case map
+ 1F84; 1F04 03B9; Case map
+ 1F85; 1F05 03B9; Case map
+ 1F86; 1F06 03B9; Case map
+ 1F87; 1F07 03B9; Case map
+ 1F88; 1F00 03B9; Case map
+ 1F89; 1F01 03B9; Case map
+ 1F8A; 1F02 03B9; Case map
+ 1F8B; 1F03 03B9; Case map
+ 1F8C; 1F04 03B9; Case map
+ 1F8D; 1F05 03B9; Case map
+ 1F8E; 1F06 03B9; Case map
+ 1F8F; 1F07 03B9; Case map
+ 1F90; 1F20 03B9; Case map
+ 1F91; 1F21 03B9; Case map
+ 1F92; 1F22 03B9; Case map
+ 1F93; 1F23 03B9; Case map
+ 1F94; 1F24 03B9; Case map
+ 1F95; 1F25 03B9; Case map
+ 1F96; 1F26 03B9; Case map
+ 1F97; 1F27 03B9; Case map
+ 1F98; 1F20 03B9; Case map
+
+
+
+Hoffman & Blanchet Standards Track [Page 45]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 1F99; 1F21 03B9; Case map
+ 1F9A; 1F22 03B9; Case map
+ 1F9B; 1F23 03B9; Case map
+ 1F9C; 1F24 03B9; Case map
+ 1F9D; 1F25 03B9; Case map
+ 1F9E; 1F26 03B9; Case map
+ 1F9F; 1F27 03B9; Case map
+ 1FA0; 1F60 03B9; Case map
+ 1FA1; 1F61 03B9; Case map
+ 1FA2; 1F62 03B9; Case map
+ 1FA3; 1F63 03B9; Case map
+ 1FA4; 1F64 03B9; Case map
+ 1FA5; 1F65 03B9; Case map
+ 1FA6; 1F66 03B9; Case map
+ 1FA7; 1F67 03B9; Case map
+ 1FA8; 1F60 03B9; Case map
+ 1FA9; 1F61 03B9; Case map
+ 1FAA; 1F62 03B9; Case map
+ 1FAB; 1F63 03B9; Case map
+ 1FAC; 1F64 03B9; Case map
+ 1FAD; 1F65 03B9; Case map
+ 1FAE; 1F66 03B9; Case map
+ 1FAF; 1F67 03B9; Case map
+ 1FB2; 1F70 03B9; Case map
+ 1FB3; 03B1 03B9; Case map
+ 1FB4; 03AC 03B9; Case map
+ 1FB6; 03B1 0342; Case map
+ 1FB7; 03B1 0342 03B9; Case map
+ 1FB8; 1FB0; Case map
+ 1FB9; 1FB1; Case map
+ 1FBA; 1F70; Case map
+ 1FBB; 1F71; Case map
+ 1FBC; 03B1 03B9; Case map
+ 1FBE; 03B9; Case map
+ 1FC2; 1F74 03B9; Case map
+ 1FC3; 03B7 03B9; Case map
+ 1FC4; 03AE 03B9; Case map
+ 1FC6; 03B7 0342; Case map
+ 1FC7; 03B7 0342 03B9; Case map
+ 1FC8; 1F72; Case map
+ 1FC9; 1F73; Case map
+ 1FCA; 1F74; Case map
+ 1FCB; 1F75; Case map
+ 1FCC; 03B7 03B9; Case map
+ 1FD2; 03B9 0308 0300; Case map
+ 1FD3; 03B9 0308 0301; Case map
+ 1FD6; 03B9 0342; Case map
+ 1FD7; 03B9 0308 0342; Case map
+
+
+
+Hoffman & Blanchet Standards Track [Page 46]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 1FD8; 1FD0; Case map
+ 1FD9; 1FD1; Case map
+ 1FDA; 1F76; Case map
+ 1FDB; 1F77; Case map
+ 1FE2; 03C5 0308 0300; Case map
+ 1FE3; 03C5 0308 0301; Case map
+ 1FE4; 03C1 0313; Case map
+ 1FE6; 03C5 0342; Case map
+ 1FE7; 03C5 0308 0342; Case map
+ 1FE8; 1FE0; Case map
+ 1FE9; 1FE1; Case map
+ 1FEA; 1F7A; Case map
+ 1FEB; 1F7B; Case map
+ 1FEC; 1FE5; Case map
+ 1FF2; 1F7C 03B9; Case map
+ 1FF3; 03C9 03B9; Case map
+ 1FF4; 03CE 03B9; Case map
+ 1FF6; 03C9 0342; Case map
+ 1FF7; 03C9 0342 03B9; Case map
+ 1FF8; 1F78; Case map
+ 1FF9; 1F79; Case map
+ 1FFA; 1F7C; Case map
+ 1FFB; 1F7D; Case map
+ 1FFC; 03C9 03B9; Case map
+ 20A8; 0072 0073; Additional folding
+ 2102; 0063; Additional folding
+ 2103; 00B0 0063; Additional folding
+ 2107; 025B; Additional folding
+ 2109; 00B0 0066; Additional folding
+ 210B; 0068; Additional folding
+ 210C; 0068; Additional folding
+ 210D; 0068; Additional folding
+ 2110; 0069; Additional folding
+ 2111; 0069; Additional folding
+ 2112; 006C; Additional folding
+ 2115; 006E; Additional folding
+ 2116; 006E 006F; Additional folding
+ 2119; 0070; Additional folding
+ 211A; 0071; Additional folding
+ 211B; 0072; Additional folding
+ 211C; 0072; Additional folding
+ 211D; 0072; Additional folding
+ 2120; 0073 006D; Additional folding
+ 2121; 0074 0065 006C; Additional folding
+ 2122; 0074 006D; Additional folding
+ 2124; 007A; Additional folding
+ 2126; 03C9; Case map
+ 2128; 007A; Additional folding
+
+
+
+Hoffman & Blanchet Standards Track [Page 47]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 212A; 006B; Case map
+ 212B; 00E5; Case map
+ 212C; 0062; Additional folding
+ 212D; 0063; Additional folding
+ 2130; 0065; Additional folding
+ 2131; 0066; Additional folding
+ 2133; 006D; Additional folding
+ 213E; 03B3; Additional folding
+ 213F; 03C0; Additional folding
+ 2145; 0064; Additional folding
+ 2160; 2170; Case map
+ 2161; 2171; Case map
+ 2162; 2172; Case map
+ 2163; 2173; Case map
+ 2164; 2174; Case map
+ 2165; 2175; Case map
+ 2166; 2176; Case map
+ 2167; 2177; Case map
+ 2168; 2178; Case map
+ 2169; 2179; Case map
+ 216A; 217A; Case map
+ 216B; 217B; Case map
+ 216C; 217C; Case map
+ 216D; 217D; Case map
+ 216E; 217E; Case map
+ 216F; 217F; Case map
+ 24B6; 24D0; Case map
+ 24B7; 24D1; Case map
+ 24B8; 24D2; Case map
+ 24B9; 24D3; Case map
+ 24BA; 24D4; Case map
+ 24BB; 24D5; Case map
+ 24BC; 24D6; Case map
+ 24BD; 24D7; Case map
+ 24BE; 24D8; Case map
+ 24BF; 24D9; Case map
+ 24C0; 24DA; Case map
+ 24C1; 24DB; Case map
+ 24C2; 24DC; Case map
+ 24C3; 24DD; Case map
+ 24C4; 24DE; Case map
+ 24C5; 24DF; Case map
+ 24C6; 24E0; Case map
+ 24C7; 24E1; Case map
+ 24C8; 24E2; Case map
+ 24C9; 24E3; Case map
+ 24CA; 24E4; Case map
+ 24CB; 24E5; Case map
+
+
+
+Hoffman & Blanchet Standards Track [Page 48]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 24CC; 24E6; Case map
+ 24CD; 24E7; Case map
+ 24CE; 24E8; Case map
+ 24CF; 24E9; Case map
+ 3371; 0068 0070 0061; Additional folding
+ 3373; 0061 0075; Additional folding
+ 3375; 006F 0076; Additional folding
+ 3380; 0070 0061; Additional folding
+ 3381; 006E 0061; Additional folding
+ 3382; 03BC 0061; Additional folding
+ 3383; 006D 0061; Additional folding
+ 3384; 006B 0061; Additional folding
+ 3385; 006B 0062; Additional folding
+ 3386; 006D 0062; Additional folding
+ 3387; 0067 0062; Additional folding
+ 338A; 0070 0066; Additional folding
+ 338B; 006E 0066; Additional folding
+ 338C; 03BC 0066; Additional folding
+ 3390; 0068 007A; Additional folding
+ 3391; 006B 0068 007A; Additional folding
+ 3392; 006D 0068 007A; Additional folding
+ 3393; 0067 0068 007A; Additional folding
+ 3394; 0074 0068 007A; Additional folding
+ 33A9; 0070 0061; Additional folding
+ 33AA; 006B 0070 0061; Additional folding
+ 33AB; 006D 0070 0061; Additional folding
+ 33AC; 0067 0070 0061; Additional folding
+ 33B4; 0070 0076; Additional folding
+ 33B5; 006E 0076; Additional folding
+ 33B6; 03BC 0076; Additional folding
+ 33B7; 006D 0076; Additional folding
+ 33B8; 006B 0076; Additional folding
+ 33B9; 006D 0076; Additional folding
+ 33BA; 0070 0077; Additional folding
+ 33BB; 006E 0077; Additional folding
+ 33BC; 03BC 0077; Additional folding
+ 33BD; 006D 0077; Additional folding
+ 33BE; 006B 0077; Additional folding
+ 33BF; 006D 0077; Additional folding
+ 33C0; 006B 03C9; Additional folding
+ 33C1; 006D 03C9; Additional folding
+ 33C3; 0062 0071; Additional folding
+ 33C6; 0063 2215 006B 0067; Additional folding
+ 33C7; 0063 006F 002E; Additional folding
+ 33C8; 0064 0062; Additional folding
+ 33C9; 0067 0079; Additional folding
+ 33CB; 0068 0070; Additional folding
+ 33CD; 006B 006B; Additional folding
+
+
+
+Hoffman & Blanchet Standards Track [Page 49]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 33CE; 006B 006D; Additional folding
+ 33D7; 0070 0068; Additional folding
+ 33D9; 0070 0070 006D; Additional folding
+ 33DA; 0070 0072; Additional folding
+ 33DC; 0073 0076; Additional folding
+ 33DD; 0077 0062; Additional folding
+ FB00; 0066 0066; Case map
+ FB01; 0066 0069; Case map
+ FB02; 0066 006C; Case map
+ FB03; 0066 0066 0069; Case map
+ FB04; 0066 0066 006C; Case map
+ FB05; 0073 0074; Case map
+ FB06; 0073 0074; Case map
+ FB13; 0574 0576; Case map
+ FB14; 0574 0565; Case map
+ FB15; 0574 056B; Case map
+ FB16; 057E 0576; Case map
+ FB17; 0574 056D; Case map
+ FF21; FF41; Case map
+ FF22; FF42; Case map
+ FF23; FF43; Case map
+ FF24; FF44; Case map
+ FF25; FF45; Case map
+ FF26; FF46; Case map
+ FF27; FF47; Case map
+ FF28; FF48; Case map
+ FF29; FF49; Case map
+ FF2A; FF4A; Case map
+ FF2B; FF4B; Case map
+ FF2C; FF4C; Case map
+ FF2D; FF4D; Case map
+ FF2E; FF4E; Case map
+ FF2F; FF4F; Case map
+ FF30; FF50; Case map
+ FF31; FF51; Case map
+ FF32; FF52; Case map
+ FF33; FF53; Case map
+ FF34; FF54; Case map
+ FF35; FF55; Case map
+ FF36; FF56; Case map
+ FF37; FF57; Case map
+ FF38; FF58; Case map
+ FF39; FF59; Case map
+ FF3A; FF5A; Case map
+ 10400; 10428; Case map
+ 10401; 10429; Case map
+ 10402; 1042A; Case map
+ 10403; 1042B; Case map
+
+
+
+Hoffman & Blanchet Standards Track [Page 50]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 10404; 1042C; Case map
+ 10405; 1042D; Case map
+ 10406; 1042E; Case map
+ 10407; 1042F; Case map
+ 10408; 10430; Case map
+ 10409; 10431; Case map
+ 1040A; 10432; Case map
+ 1040B; 10433; Case map
+ 1040C; 10434; Case map
+ 1040D; 10435; Case map
+ 1040E; 10436; Case map
+ 1040F; 10437; Case map
+ 10410; 10438; Case map
+ 10411; 10439; Case map
+ 10412; 1043A; Case map
+ 10413; 1043B; Case map
+ 10414; 1043C; Case map
+ 10415; 1043D; Case map
+ 10416; 1043E; Case map
+ 10417; 1043F; Case map
+ 10418; 10440; Case map
+ 10419; 10441; Case map
+ 1041A; 10442; Case map
+ 1041B; 10443; Case map
+ 1041C; 10444; Case map
+ 1041D; 10445; Case map
+ 1041E; 10446; Case map
+ 1041F; 10447; Case map
+ 10420; 10448; Case map
+ 10421; 10449; Case map
+ 10422; 1044A; Case map
+ 10423; 1044B; Case map
+ 10424; 1044C; Case map
+ 10425; 1044D; Case map
+ 1D400; 0061; Additional folding
+ 1D401; 0062; Additional folding
+ 1D402; 0063; Additional folding
+ 1D403; 0064; Additional folding
+ 1D404; 0065; Additional folding
+ 1D405; 0066; Additional folding
+ 1D406; 0067; Additional folding
+ 1D407; 0068; Additional folding
+ 1D408; 0069; Additional folding
+ 1D409; 006A; Additional folding
+ 1D40A; 006B; Additional folding
+ 1D40B; 006C; Additional folding
+ 1D40C; 006D; Additional folding
+ 1D40D; 006E; Additional folding
+
+
+
+Hoffman & Blanchet Standards Track [Page 51]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 1D40E; 006F; Additional folding
+ 1D40F; 0070; Additional folding
+ 1D410; 0071; Additional folding
+ 1D411; 0072; Additional folding
+ 1D412; 0073; Additional folding
+ 1D413; 0074; Additional folding
+ 1D414; 0075; Additional folding
+ 1D415; 0076; Additional folding
+ 1D416; 0077; Additional folding
+ 1D417; 0078; Additional folding
+ 1D418; 0079; Additional folding
+ 1D419; 007A; Additional folding
+ 1D434; 0061; Additional folding
+ 1D435; 0062; Additional folding
+ 1D436; 0063; Additional folding
+ 1D437; 0064; Additional folding
+ 1D438; 0065; Additional folding
+ 1D439; 0066; Additional folding
+ 1D43A; 0067; Additional folding
+ 1D43B; 0068; Additional folding
+ 1D43C; 0069; Additional folding
+ 1D43D; 006A; Additional folding
+ 1D43E; 006B; Additional folding
+ 1D43F; 006C; Additional folding
+ 1D440; 006D; Additional folding
+ 1D441; 006E; Additional folding
+ 1D442; 006F; Additional folding
+ 1D443; 0070; Additional folding
+ 1D444; 0071; Additional folding
+ 1D445; 0072; Additional folding
+ 1D446; 0073; Additional folding
+ 1D447; 0074; Additional folding
+ 1D448; 0075; Additional folding
+ 1D449; 0076; Additional folding
+ 1D44A; 0077; Additional folding
+ 1D44B; 0078; Additional folding
+ 1D44C; 0079; Additional folding
+ 1D44D; 007A; Additional folding
+ 1D468; 0061; Additional folding
+ 1D469; 0062; Additional folding
+ 1D46A; 0063; Additional folding
+ 1D46B; 0064; Additional folding
+ 1D46C; 0065; Additional folding
+ 1D46D; 0066; Additional folding
+ 1D46E; 0067; Additional folding
+ 1D46F; 0068; Additional folding
+ 1D470; 0069; Additional folding
+ 1D471; 006A; Additional folding
+
+
+
+Hoffman & Blanchet Standards Track [Page 52]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 1D472; 006B; Additional folding
+ 1D473; 006C; Additional folding
+ 1D474; 006D; Additional folding
+ 1D475; 006E; Additional folding
+ 1D476; 006F; Additional folding
+ 1D477; 0070; Additional folding
+ 1D478; 0071; Additional folding
+ 1D479; 0072; Additional folding
+ 1D47A; 0073; Additional folding
+ 1D47B; 0074; Additional folding
+ 1D47C; 0075; Additional folding
+ 1D47D; 0076; Additional folding
+ 1D47E; 0077; Additional folding
+ 1D47F; 0078; Additional folding
+ 1D480; 0079; Additional folding
+ 1D481; 007A; Additional folding
+ 1D49C; 0061; Additional folding
+ 1D49E; 0063; Additional folding
+ 1D49F; 0064; Additional folding
+ 1D4A2; 0067; Additional folding
+ 1D4A5; 006A; Additional folding
+ 1D4A6; 006B; Additional folding
+ 1D4A9; 006E; Additional folding
+ 1D4AA; 006F; Additional folding
+ 1D4AB; 0070; Additional folding
+ 1D4AC; 0071; Additional folding
+ 1D4AE; 0073; Additional folding
+ 1D4AF; 0074; Additional folding
+ 1D4B0; 0075; Additional folding
+ 1D4B1; 0076; Additional folding
+ 1D4B2; 0077; Additional folding
+ 1D4B3; 0078; Additional folding
+ 1D4B4; 0079; Additional folding
+ 1D4B5; 007A; Additional folding
+ 1D4D0; 0061; Additional folding
+ 1D4D1; 0062; Additional folding
+ 1D4D2; 0063; Additional folding
+ 1D4D3; 0064; Additional folding
+ 1D4D4; 0065; Additional folding
+ 1D4D5; 0066; Additional folding
+ 1D4D6; 0067; Additional folding
+ 1D4D7; 0068; Additional folding
+ 1D4D8; 0069; Additional folding
+ 1D4D9; 006A; Additional folding
+ 1D4DA; 006B; Additional folding
+ 1D4DB; 006C; Additional folding
+ 1D4DC; 006D; Additional folding
+ 1D4DD; 006E; Additional folding
+
+
+
+Hoffman & Blanchet Standards Track [Page 53]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 1D4DE; 006F; Additional folding
+ 1D4DF; 0070; Additional folding
+ 1D4E0; 0071; Additional folding
+ 1D4E1; 0072; Additional folding
+ 1D4E2; 0073; Additional folding
+ 1D4E3; 0074; Additional folding
+ 1D4E4; 0075; Additional folding
+ 1D4E5; 0076; Additional folding
+ 1D4E6; 0077; Additional folding
+ 1D4E7; 0078; Additional folding
+ 1D4E8; 0079; Additional folding
+ 1D4E9; 007A; Additional folding
+ 1D504; 0061; Additional folding
+ 1D505; 0062; Additional folding
+ 1D507; 0064; Additional folding
+ 1D508; 0065; Additional folding
+ 1D509; 0066; Additional folding
+ 1D50A; 0067; Additional folding
+ 1D50D; 006A; Additional folding
+ 1D50E; 006B; Additional folding
+ 1D50F; 006C; Additional folding
+ 1D510; 006D; Additional folding
+ 1D511; 006E; Additional folding
+ 1D512; 006F; Additional folding
+ 1D513; 0070; Additional folding
+ 1D514; 0071; Additional folding
+ 1D516; 0073; Additional folding
+ 1D517; 0074; Additional folding
+ 1D518; 0075; Additional folding
+ 1D519; 0076; Additional folding
+ 1D51A; 0077; Additional folding
+ 1D51B; 0078; Additional folding
+ 1D51C; 0079; Additional folding
+ 1D538; 0061; Additional folding
+ 1D539; 0062; Additional folding
+ 1D53B; 0064; Additional folding
+ 1D53C; 0065; Additional folding
+ 1D53D; 0066; Additional folding
+ 1D53E; 0067; Additional folding
+ 1D540; 0069; Additional folding
+ 1D541; 006A; Additional folding
+ 1D542; 006B; Additional folding
+ 1D543; 006C; Additional folding
+ 1D544; 006D; Additional folding
+ 1D546; 006F; Additional folding
+ 1D54A; 0073; Additional folding
+ 1D54B; 0074; Additional folding
+ 1D54C; 0075; Additional folding
+
+
+
+Hoffman & Blanchet Standards Track [Page 54]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 1D54D; 0076; Additional folding
+ 1D54E; 0077; Additional folding
+ 1D54F; 0078; Additional folding
+ 1D550; 0079; Additional folding
+ 1D56C; 0061; Additional folding
+ 1D56D; 0062; Additional folding
+ 1D56E; 0063; Additional folding
+ 1D56F; 0064; Additional folding
+ 1D570; 0065; Additional folding
+ 1D571; 0066; Additional folding
+ 1D572; 0067; Additional folding
+ 1D573; 0068; Additional folding
+ 1D574; 0069; Additional folding
+ 1D575; 006A; Additional folding
+ 1D576; 006B; Additional folding
+ 1D577; 006C; Additional folding
+ 1D578; 006D; Additional folding
+ 1D579; 006E; Additional folding
+ 1D57A; 006F; Additional folding
+ 1D57B; 0070; Additional folding
+ 1D57C; 0071; Additional folding
+ 1D57D; 0072; Additional folding
+ 1D57E; 0073; Additional folding
+ 1D57F; 0074; Additional folding
+ 1D580; 0075; Additional folding
+ 1D581; 0076; Additional folding
+ 1D582; 0077; Additional folding
+ 1D583; 0078; Additional folding
+ 1D584; 0079; Additional folding
+ 1D585; 007A; Additional folding
+ 1D5A0; 0061; Additional folding
+ 1D5A1; 0062; Additional folding
+ 1D5A2; 0063; Additional folding
+ 1D5A3; 0064; Additional folding
+ 1D5A4; 0065; Additional folding
+ 1D5A5; 0066; Additional folding
+ 1D5A6; 0067; Additional folding
+ 1D5A7; 0068; Additional folding
+ 1D5A8; 0069; Additional folding
+ 1D5A9; 006A; Additional folding
+ 1D5AA; 006B; Additional folding
+ 1D5AB; 006C; Additional folding
+ 1D5AC; 006D; Additional folding
+ 1D5AD; 006E; Additional folding
+ 1D5AE; 006F; Additional folding
+ 1D5AF; 0070; Additional folding
+ 1D5B0; 0071; Additional folding
+ 1D5B1; 0072; Additional folding
+
+
+
+Hoffman & Blanchet Standards Track [Page 55]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 1D5B2; 0073; Additional folding
+ 1D5B3; 0074; Additional folding
+ 1D5B4; 0075; Additional folding
+ 1D5B5; 0076; Additional folding
+ 1D5B6; 0077; Additional folding
+ 1D5B7; 0078; Additional folding
+ 1D5B8; 0079; Additional folding
+ 1D5B9; 007A; Additional folding
+ 1D5D4; 0061; Additional folding
+ 1D5D5; 0062; Additional folding
+ 1D5D6; 0063; Additional folding
+ 1D5D7; 0064; Additional folding
+ 1D5D8; 0065; Additional folding
+ 1D5D9; 0066; Additional folding
+ 1D5DA; 0067; Additional folding
+ 1D5DB; 0068; Additional folding
+ 1D5DC; 0069; Additional folding
+ 1D5DD; 006A; Additional folding
+ 1D5DE; 006B; Additional folding
+ 1D5DF; 006C; Additional folding
+ 1D5E0; 006D; Additional folding
+ 1D5E1; 006E; Additional folding
+ 1D5E2; 006F; Additional folding
+ 1D5E3; 0070; Additional folding
+ 1D5E4; 0071; Additional folding
+ 1D5E5; 0072; Additional folding
+ 1D5E6; 0073; Additional folding
+ 1D5E7; 0074; Additional folding
+ 1D5E8; 0075; Additional folding
+ 1D5E9; 0076; Additional folding
+ 1D5EA; 0077; Additional folding
+ 1D5EB; 0078; Additional folding
+ 1D5EC; 0079; Additional folding
+ 1D5ED; 007A; Additional folding
+ 1D608; 0061; Additional folding
+ 1D609; 0062; Additional folding
+ 1D60A; 0063; Additional folding
+ 1D60B; 0064; Additional folding
+ 1D60C; 0065; Additional folding
+ 1D60D; 0066; Additional folding
+ 1D60E; 0067; Additional folding
+ 1D60F; 0068; Additional folding
+ 1D610; 0069; Additional folding
+ 1D611; 006A; Additional folding
+ 1D612; 006B; Additional folding
+ 1D613; 006C; Additional folding
+ 1D614; 006D; Additional folding
+ 1D615; 006E; Additional folding
+
+
+
+Hoffman & Blanchet Standards Track [Page 56]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 1D616; 006F; Additional folding
+ 1D617; 0070; Additional folding
+ 1D618; 0071; Additional folding
+ 1D619; 0072; Additional folding
+ 1D61A; 0073; Additional folding
+ 1D61B; 0074; Additional folding
+ 1D61C; 0075; Additional folding
+ 1D61D; 0076; Additional folding
+ 1D61E; 0077; Additional folding
+ 1D61F; 0078; Additional folding
+ 1D620; 0079; Additional folding
+ 1D621; 007A; Additional folding
+ 1D63C; 0061; Additional folding
+ 1D63D; 0062; Additional folding
+ 1D63E; 0063; Additional folding
+ 1D63F; 0064; Additional folding
+ 1D640; 0065; Additional folding
+ 1D641; 0066; Additional folding
+ 1D642; 0067; Additional folding
+ 1D643; 0068; Additional folding
+ 1D644; 0069; Additional folding
+ 1D645; 006A; Additional folding
+ 1D646; 006B; Additional folding
+ 1D647; 006C; Additional folding
+ 1D648; 006D; Additional folding
+ 1D649; 006E; Additional folding
+ 1D64A; 006F; Additional folding
+ 1D64B; 0070; Additional folding
+ 1D64C; 0071; Additional folding
+ 1D64D; 0072; Additional folding
+ 1D64E; 0073; Additional folding
+ 1D64F; 0074; Additional folding
+ 1D650; 0075; Additional folding
+ 1D651; 0076; Additional folding
+ 1D652; 0077; Additional folding
+ 1D653; 0078; Additional folding
+ 1D654; 0079; Additional folding
+ 1D655; 007A; Additional folding
+ 1D670; 0061; Additional folding
+ 1D671; 0062; Additional folding
+ 1D672; 0063; Additional folding
+ 1D673; 0064; Additional folding
+ 1D674; 0065; Additional folding
+ 1D675; 0066; Additional folding
+ 1D676; 0067; Additional folding
+ 1D677; 0068; Additional folding
+ 1D678; 0069; Additional folding
+ 1D679; 006A; Additional folding
+
+
+
+Hoffman & Blanchet Standards Track [Page 57]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 1D67A; 006B; Additional folding
+ 1D67B; 006C; Additional folding
+ 1D67C; 006D; Additional folding
+ 1D67D; 006E; Additional folding
+ 1D67E; 006F; Additional folding
+ 1D67F; 0070; Additional folding
+ 1D680; 0071; Additional folding
+ 1D681; 0072; Additional folding
+ 1D682; 0073; Additional folding
+ 1D683; 0074; Additional folding
+ 1D684; 0075; Additional folding
+ 1D685; 0076; Additional folding
+ 1D686; 0077; Additional folding
+ 1D687; 0078; Additional folding
+ 1D688; 0079; Additional folding
+ 1D689; 007A; Additional folding
+ 1D6A8; 03B1; Additional folding
+ 1D6A9; 03B2; Additional folding
+ 1D6AA; 03B3; Additional folding
+ 1D6AB; 03B4; Additional folding
+ 1D6AC; 03B5; Additional folding
+ 1D6AD; 03B6; Additional folding
+ 1D6AE; 03B7; Additional folding
+ 1D6AF; 03B8; Additional folding
+ 1D6B0; 03B9; Additional folding
+ 1D6B1; 03BA; Additional folding
+ 1D6B2; 03BB; Additional folding
+ 1D6B3; 03BC; Additional folding
+ 1D6B4; 03BD; Additional folding
+ 1D6B5; 03BE; Additional folding
+ 1D6B6; 03BF; Additional folding
+ 1D6B7; 03C0; Additional folding
+ 1D6B8; 03C1; Additional folding
+ 1D6B9; 03B8; Additional folding
+ 1D6BA; 03C3; Additional folding
+ 1D6BB; 03C4; Additional folding
+ 1D6BC; 03C5; Additional folding
+ 1D6BD; 03C6; Additional folding
+ 1D6BE; 03C7; Additional folding
+ 1D6BF; 03C8; Additional folding
+ 1D6C0; 03C9; Additional folding
+ 1D6D3; 03C3; Additional folding
+ 1D6E2; 03B1; Additional folding
+ 1D6E3; 03B2; Additional folding
+ 1D6E4; 03B3; Additional folding
+ 1D6E5; 03B4; Additional folding
+ 1D6E6; 03B5; Additional folding
+ 1D6E7; 03B6; Additional folding
+
+
+
+Hoffman & Blanchet Standards Track [Page 58]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 1D6E8; 03B7; Additional folding
+ 1D6E9; 03B8; Additional folding
+ 1D6EA; 03B9; Additional folding
+ 1D6EB; 03BA; Additional folding
+ 1D6EC; 03BB; Additional folding
+ 1D6ED; 03BC; Additional folding
+ 1D6EE; 03BD; Additional folding
+ 1D6EF; 03BE; Additional folding
+ 1D6F0; 03BF; Additional folding
+ 1D6F1; 03C0; Additional folding
+ 1D6F2; 03C1; Additional folding
+ 1D6F3; 03B8; Additional folding
+ 1D6F4; 03C3; Additional folding
+ 1D6F5; 03C4; Additional folding
+ 1D6F6; 03C5; Additional folding
+ 1D6F7; 03C6; Additional folding
+ 1D6F8; 03C7; Additional folding
+ 1D6F9; 03C8; Additional folding
+ 1D6FA; 03C9; Additional folding
+ 1D70D; 03C3; Additional folding
+ 1D71C; 03B1; Additional folding
+ 1D71D; 03B2; Additional folding
+ 1D71E; 03B3; Additional folding
+ 1D71F; 03B4; Additional folding
+ 1D720; 03B5; Additional folding
+ 1D721; 03B6; Additional folding
+ 1D722; 03B7; Additional folding
+ 1D723; 03B8; Additional folding
+ 1D724; 03B9; Additional folding
+ 1D725; 03BA; Additional folding
+ 1D726; 03BB; Additional folding
+ 1D727; 03BC; Additional folding
+ 1D728; 03BD; Additional folding
+ 1D729; 03BE; Additional folding
+ 1D72A; 03BF; Additional folding
+ 1D72B; 03C0; Additional folding
+ 1D72C; 03C1; Additional folding
+ 1D72D; 03B8; Additional folding
+ 1D72E; 03C3; Additional folding
+ 1D72F; 03C4; Additional folding
+ 1D730; 03C5; Additional folding
+ 1D731; 03C6; Additional folding
+ 1D732; 03C7; Additional folding
+ 1D733; 03C8; Additional folding
+ 1D734; 03C9; Additional folding
+ 1D747; 03C3; Additional folding
+ 1D756; 03B1; Additional folding
+ 1D757; 03B2; Additional folding
+
+
+
+Hoffman & Blanchet Standards Track [Page 59]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 1D758; 03B3; Additional folding
+ 1D759; 03B4; Additional folding
+ 1D75A; 03B5; Additional folding
+ 1D75B; 03B6; Additional folding
+ 1D75C; 03B7; Additional folding
+ 1D75D; 03B8; Additional folding
+ 1D75E; 03B9; Additional folding
+ 1D75F; 03BA; Additional folding
+ 1D760; 03BB; Additional folding
+ 1D761; 03BC; Additional folding
+ 1D762; 03BD; Additional folding
+ 1D763; 03BE; Additional folding
+ 1D764; 03BF; Additional folding
+ 1D765; 03C0; Additional folding
+ 1D766; 03C1; Additional folding
+ 1D767; 03B8; Additional folding
+ 1D768; 03C3; Additional folding
+ 1D769; 03C4; Additional folding
+ 1D76A; 03C5; Additional folding
+ 1D76B; 03C6; Additional folding
+ 1D76C; 03C7; Additional folding
+ 1D76D; 03C8; Additional folding
+ 1D76E; 03C9; Additional folding
+ 1D781; 03C3; Additional folding
+ 1D790; 03B1; Additional folding
+ 1D791; 03B2; Additional folding
+ 1D792; 03B3; Additional folding
+ 1D793; 03B4; Additional folding
+ 1D794; 03B5; Additional folding
+ 1D795; 03B6; Additional folding
+ 1D796; 03B7; Additional folding
+ 1D797; 03B8; Additional folding
+ 1D798; 03B9; Additional folding
+ 1D799; 03BA; Additional folding
+ 1D79A; 03BB; Additional folding
+ 1D79B; 03BC; Additional folding
+ 1D79C; 03BD; Additional folding
+ 1D79D; 03BE; Additional folding
+ 1D79E; 03BF; Additional folding
+ 1D79F; 03C0; Additional folding
+ 1D7A0; 03C1; Additional folding
+ 1D7A1; 03B8; Additional folding
+ 1D7A2; 03C3; Additional folding
+ 1D7A3; 03C4; Additional folding
+ 1D7A4; 03C5; Additional folding
+ 1D7A5; 03C6; Additional folding
+ 1D7A6; 03C7; Additional folding
+ 1D7A7; 03C8; Additional folding
+
+
+
+Hoffman & Blanchet Standards Track [Page 60]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 1D7A8; 03C9; Additional folding
+ 1D7BB; 03C3; Additional folding
+ ----- End Table B.2 -----
+
+B.3 Mapping for case-folding used with no normalization
+
+ ----- Start Table B.3 -----
+ 0041; 0061; Case map
+ 0042; 0062; Case map
+ 0043; 0063; Case map
+ 0044; 0064; Case map
+ 0045; 0065; Case map
+ 0046; 0066; Case map
+ 0047; 0067; Case map
+ 0048; 0068; Case map
+ 0049; 0069; Case map
+ 004A; 006A; Case map
+ 004B; 006B; Case map
+ 004C; 006C; Case map
+ 004D; 006D; Case map
+ 004E; 006E; Case map
+ 004F; 006F; Case map
+ 0050; 0070; Case map
+ 0051; 0071; Case map
+ 0052; 0072; Case map
+ 0053; 0073; Case map
+ 0054; 0074; Case map
+ 0055; 0075; Case map
+ 0056; 0076; Case map
+ 0057; 0077; Case map
+ 0058; 0078; Case map
+ 0059; 0079; Case map
+ 005A; 007A; Case map
+ 00B5; 03BC; Case map
+ 00C0; 00E0; Case map
+ 00C1; 00E1; Case map
+ 00C2; 00E2; Case map
+ 00C3; 00E3; Case map
+ 00C4; 00E4; Case map
+ 00C5; 00E5; Case map
+ 00C6; 00E6; Case map
+ 00C7; 00E7; Case map
+ 00C8; 00E8; Case map
+ 00C9; 00E9; Case map
+ 00CA; 00EA; Case map
+ 00CB; 00EB; Case map
+ 00CC; 00EC; Case map
+ 00CD; 00ED; Case map
+
+
+
+Hoffman & Blanchet Standards Track [Page 61]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 00CE; 00EE; Case map
+ 00CF; 00EF; Case map
+ 00D0; 00F0; Case map
+ 00D1; 00F1; Case map
+ 00D2; 00F2; Case map
+ 00D3; 00F3; Case map
+ 00D4; 00F4; Case map
+ 00D5; 00F5; Case map
+ 00D6; 00F6; Case map
+ 00D8; 00F8; Case map
+ 00D9; 00F9; Case map
+ 00DA; 00FA; Case map
+ 00DB; 00FB; Case map
+ 00DC; 00FC; Case map
+ 00DD; 00FD; Case map
+ 00DE; 00FE; Case map
+ 00DF; 0073 0073; Case map
+ 0100; 0101; Case map
+ 0102; 0103; Case map
+ 0104; 0105; Case map
+ 0106; 0107; Case map
+ 0108; 0109; Case map
+ 010A; 010B; Case map
+ 010C; 010D; Case map
+ 010E; 010F; Case map
+ 0110; 0111; Case map
+ 0112; 0113; Case map
+ 0114; 0115; Case map
+ 0116; 0117; Case map
+ 0118; 0119; Case map
+ 011A; 011B; Case map
+ 011C; 011D; Case map
+ 011E; 011F; Case map
+ 0120; 0121; Case map
+ 0122; 0123; Case map
+ 0124; 0125; Case map
+ 0126; 0127; Case map
+ 0128; 0129; Case map
+ 012A; 012B; Case map
+ 012C; 012D; Case map
+ 012E; 012F; Case map
+ 0130; 0069 0307; Case map
+ 0132; 0133; Case map
+ 0134; 0135; Case map
+ 0136; 0137; Case map
+ 0139; 013A; Case map
+ 013B; 013C; Case map
+ 013D; 013E; Case map
+
+
+
+Hoffman & Blanchet Standards Track [Page 62]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 013F; 0140; Case map
+ 0141; 0142; Case map
+ 0143; 0144; Case map
+ 0145; 0146; Case map
+ 0147; 0148; Case map
+ 0149; 02BC 006E; Case map
+ 014A; 014B; Case map
+ 014C; 014D; Case map
+ 014E; 014F; Case map
+ 0150; 0151; Case map
+ 0152; 0153; Case map
+ 0154; 0155; Case map
+ 0156; 0157; Case map
+ 0158; 0159; Case map
+ 015A; 015B; Case map
+ 015C; 015D; Case map
+ 015E; 015F; Case map
+ 0160; 0161; Case map
+ 0162; 0163; Case map
+ 0164; 0165; Case map
+ 0166; 0167; Case map
+ 0168; 0169; Case map
+ 016A; 016B; Case map
+ 016C; 016D; Case map
+ 016E; 016F; Case map
+ 0170; 0171; Case map
+ 0172; 0173; Case map
+ 0174; 0175; Case map
+ 0176; 0177; Case map
+ 0178; 00FF; Case map
+ 0179; 017A; Case map
+ 017B; 017C; Case map
+ 017D; 017E; Case map
+ 017F; 0073; Case map
+ 0181; 0253; Case map
+ 0182; 0183; Case map
+ 0184; 0185; Case map
+ 0186; 0254; Case map
+ 0187; 0188; Case map
+ 0189; 0256; Case map
+ 018A; 0257; Case map
+ 018B; 018C; Case map
+ 018E; 01DD; Case map
+ 018F; 0259; Case map
+ 0190; 025B; Case map
+ 0191; 0192; Case map
+ 0193; 0260; Case map
+ 0194; 0263; Case map
+
+
+
+Hoffman & Blanchet Standards Track [Page 63]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 0196; 0269; Case map
+ 0197; 0268; Case map
+ 0198; 0199; Case map
+ 019C; 026F; Case map
+ 019D; 0272; Case map
+ 019F; 0275; Case map
+ 01A0; 01A1; Case map
+ 01A2; 01A3; Case map
+ 01A4; 01A5; Case map
+ 01A6; 0280; Case map
+ 01A7; 01A8; Case map
+ 01A9; 0283; Case map
+ 01AC; 01AD; Case map
+ 01AE; 0288; Case map
+ 01AF; 01B0; Case map
+ 01B1; 028A; Case map
+ 01B2; 028B; Case map
+ 01B3; 01B4; Case map
+ 01B5; 01B6; Case map
+ 01B7; 0292; Case map
+ 01B8; 01B9; Case map
+ 01BC; 01BD; Case map
+ 01C4; 01C6; Case map
+ 01C5; 01C6; Case map
+ 01C7; 01C9; Case map
+ 01C8; 01C9; Case map
+ 01CA; 01CC; Case map
+ 01CB; 01CC; Case map
+ 01CD; 01CE; Case map
+ 01CF; 01D0; Case map
+ 01D1; 01D2; Case map
+ 01D3; 01D4; Case map
+ 01D5; 01D6; Case map
+ 01D7; 01D8; Case map
+ 01D9; 01DA; Case map
+ 01DB; 01DC; Case map
+ 01DE; 01DF; Case map
+ 01E0; 01E1; Case map
+ 01E2; 01E3; Case map
+ 01E4; 01E5; Case map
+ 01E6; 01E7; Case map
+ 01E8; 01E9; Case map
+ 01EA; 01EB; Case map
+ 01EC; 01ED; Case map
+ 01EE; 01EF; Case map
+ 01F0; 006A 030C; Case map
+ 01F1; 01F3; Case map
+ 01F2; 01F3; Case map
+
+
+
+Hoffman & Blanchet Standards Track [Page 64]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 01F4; 01F5; Case map
+ 01F6; 0195; Case map
+ 01F7; 01BF; Case map
+ 01F8; 01F9; Case map
+ 01FA; 01FB; Case map
+ 01FC; 01FD; Case map
+ 01FE; 01FF; Case map
+ 0200; 0201; Case map
+ 0202; 0203; Case map
+ 0204; 0205; Case map
+ 0206; 0207; Case map
+ 0208; 0209; Case map
+ 020A; 020B; Case map
+ 020C; 020D; Case map
+ 020E; 020F; Case map
+ 0210; 0211; Case map
+ 0212; 0213; Case map
+ 0214; 0215; Case map
+ 0216; 0217; Case map
+ 0218; 0219; Case map
+ 021A; 021B; Case map
+ 021C; 021D; Case map
+ 021E; 021F; Case map
+ 0220; 019E; Case map
+ 0222; 0223; Case map
+ 0224; 0225; Case map
+ 0226; 0227; Case map
+ 0228; 0229; Case map
+ 022A; 022B; Case map
+ 022C; 022D; Case map
+ 022E; 022F; Case map
+ 0230; 0231; Case map
+ 0232; 0233; Case map
+ 0345; 03B9; Case map
+ 0386; 03AC; Case map
+ 0388; 03AD; Case map
+ 0389; 03AE; Case map
+ 038A; 03AF; Case map
+ 038C; 03CC; Case map
+ 038E; 03CD; Case map
+ 038F; 03CE; Case map
+ 0390; 03B9 0308 0301; Case map
+ 0391; 03B1; Case map
+ 0392; 03B2; Case map
+ 0393; 03B3; Case map
+ 0394; 03B4; Case map
+ 0395; 03B5; Case map
+ 0396; 03B6; Case map
+
+
+
+Hoffman & Blanchet Standards Track [Page 65]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 0397; 03B7; Case map
+ 0398; 03B8; Case map
+ 0399; 03B9; Case map
+ 039A; 03BA; Case map
+ 039B; 03BB; Case map
+ 039C; 03BC; Case map
+ 039D; 03BD; Case map
+ 039E; 03BE; Case map
+ 039F; 03BF; Case map
+ 03A0; 03C0; Case map
+ 03A1; 03C1; Case map
+ 03A3; 03C3; Case map
+ 03A4; 03C4; Case map
+ 03A5; 03C5; Case map
+ 03A6; 03C6; Case map
+ 03A7; 03C7; Case map
+ 03A8; 03C8; Case map
+ 03A9; 03C9; Case map
+ 03AA; 03CA; Case map
+ 03AB; 03CB; Case map
+ 03B0; 03C5 0308 0301; Case map
+ 03C2; 03C3; Case map
+ 03D0; 03B2; Case map
+ 03D1; 03B8; Case map
+ 03D5; 03C6; Case map
+ 03D6; 03C0; Case map
+ 03D8; 03D9; Case map
+ 03DA; 03DB; Case map
+ 03DC; 03DD; Case map
+ 03DE; 03DF; Case map
+ 03E0; 03E1; Case map
+ 03E2; 03E3; Case map
+ 03E4; 03E5; Case map
+ 03E6; 03E7; Case map
+ 03E8; 03E9; Case map
+ 03EA; 03EB; Case map
+ 03EC; 03ED; Case map
+ 03EE; 03EF; Case map
+ 03F0; 03BA; Case map
+ 03F1; 03C1; Case map
+ 03F2; 03C3; Case map
+ 03F4; 03B8; Case map
+ 03F5; 03B5; Case map
+ 0400; 0450; Case map
+ 0401; 0451; Case map
+ 0402; 0452; Case map
+ 0403; 0453; Case map
+ 0404; 0454; Case map
+
+
+
+Hoffman & Blanchet Standards Track [Page 66]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 0405; 0455; Case map
+ 0406; 0456; Case map
+ 0407; 0457; Case map
+ 0408; 0458; Case map
+ 0409; 0459; Case map
+ 040A; 045A; Case map
+ 040B; 045B; Case map
+ 040C; 045C; Case map
+ 040D; 045D; Case map
+ 040E; 045E; Case map
+ 040F; 045F; Case map
+ 0410; 0430; Case map
+ 0411; 0431; Case map
+ 0412; 0432; Case map
+ 0413; 0433; Case map
+ 0414; 0434; Case map
+ 0415; 0435; Case map
+ 0416; 0436; Case map
+ 0417; 0437; Case map
+ 0418; 0438; Case map
+ 0419; 0439; Case map
+ 041A; 043A; Case map
+ 041B; 043B; Case map
+ 041C; 043C; Case map
+ 041D; 043D; Case map
+ 041E; 043E; Case map
+ 041F; 043F; Case map
+ 0420; 0440; Case map
+ 0421; 0441; Case map
+ 0422; 0442; Case map
+ 0423; 0443; Case map
+ 0424; 0444; Case map
+ 0425; 0445; Case map
+ 0426; 0446; Case map
+ 0427; 0447; Case map
+ 0428; 0448; Case map
+ 0429; 0449; Case map
+ 042A; 044A; Case map
+ 042B; 044B; Case map
+ 042C; 044C; Case map
+ 042D; 044D; Case map
+ 042E; 044E; Case map
+ 042F; 044F; Case map
+ 0460; 0461; Case map
+ 0462; 0463; Case map
+ 0464; 0465; Case map
+ 0466; 0467; Case map
+ 0468; 0469; Case map
+
+
+
+Hoffman & Blanchet Standards Track [Page 67]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 046A; 046B; Case map
+ 046C; 046D; Case map
+ 046E; 046F; Case map
+ 0470; 0471; Case map
+ 0472; 0473; Case map
+ 0474; 0475; Case map
+ 0476; 0477; Case map
+ 0478; 0479; Case map
+ 047A; 047B; Case map
+ 047C; 047D; Case map
+ 047E; 047F; Case map
+ 0480; 0481; Case map
+ 048A; 048B; Case map
+ 048C; 048D; Case map
+ 048E; 048F; Case map
+ 0490; 0491; Case map
+ 0492; 0493; Case map
+ 0494; 0495; Case map
+ 0496; 0497; Case map
+ 0498; 0499; Case map
+ 049A; 049B; Case map
+ 049C; 049D; Case map
+ 049E; 049F; Case map
+ 04A0; 04A1; Case map
+ 04A2; 04A3; Case map
+ 04A4; 04A5; Case map
+ 04A6; 04A7; Case map
+ 04A8; 04A9; Case map
+ 04AA; 04AB; Case map
+ 04AC; 04AD; Case map
+ 04AE; 04AF; Case map
+ 04B0; 04B1; Case map
+ 04B2; 04B3; Case map
+ 04B4; 04B5; Case map
+ 04B6; 04B7; Case map
+ 04B8; 04B9; Case map
+ 04BA; 04BB; Case map
+ 04BC; 04BD; Case map
+ 04BE; 04BF; Case map
+ 04C1; 04C2; Case map
+ 04C3; 04C4; Case map
+ 04C5; 04C6; Case map
+ 04C7; 04C8; Case map
+ 04C9; 04CA; Case map
+ 04CB; 04CC; Case map
+ 04CD; 04CE; Case map
+ 04D0; 04D1; Case map
+ 04D2; 04D3; Case map
+
+
+
+Hoffman & Blanchet Standards Track [Page 68]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 04D4; 04D5; Case map
+ 04D6; 04D7; Case map
+ 04D8; 04D9; Case map
+ 04DA; 04DB; Case map
+ 04DC; 04DD; Case map
+ 04DE; 04DF; Case map
+ 04E0; 04E1; Case map
+ 04E2; 04E3; Case map
+ 04E4; 04E5; Case map
+ 04E6; 04E7; Case map
+ 04E8; 04E9; Case map
+ 04EA; 04EB; Case map
+ 04EC; 04ED; Case map
+ 04EE; 04EF; Case map
+ 04F0; 04F1; Case map
+ 04F2; 04F3; Case map
+ 04F4; 04F5; Case map
+ 04F8; 04F9; Case map
+ 0500; 0501; Case map
+ 0502; 0503; Case map
+ 0504; 0505; Case map
+ 0506; 0507; Case map
+ 0508; 0509; Case map
+ 050A; 050B; Case map
+ 050C; 050D; Case map
+ 050E; 050F; Case map
+ 0531; 0561; Case map
+ 0532; 0562; Case map
+ 0533; 0563; Case map
+ 0534; 0564; Case map
+ 0535; 0565; Case map
+ 0536; 0566; Case map
+ 0537; 0567; Case map
+ 0538; 0568; Case map
+ 0539; 0569; Case map
+ 053A; 056A; Case map
+ 053B; 056B; Case map
+ 053C; 056C; Case map
+ 053D; 056D; Case map
+ 053E; 056E; Case map
+ 053F; 056F; Case map
+ 0540; 0570; Case map
+ 0541; 0571; Case map
+ 0542; 0572; Case map
+ 0543; 0573; Case map
+ 0544; 0574; Case map
+ 0545; 0575; Case map
+ 0546; 0576; Case map
+
+
+
+Hoffman & Blanchet Standards Track [Page 69]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 0547; 0577; Case map
+ 0548; 0578; Case map
+ 0549; 0579; Case map
+ 054A; 057A; Case map
+ 054B; 057B; Case map
+ 054C; 057C; Case map
+ 054D; 057D; Case map
+ 054E; 057E; Case map
+ 054F; 057F; Case map
+ 0550; 0580; Case map
+ 0551; 0581; Case map
+ 0552; 0582; Case map
+ 0553; 0583; Case map
+ 0554; 0584; Case map
+ 0555; 0585; Case map
+ 0556; 0586; Case map
+ 0587; 0565 0582; Case map
+ 1E00; 1E01; Case map
+ 1E02; 1E03; Case map
+ 1E04; 1E05; Case map
+ 1E06; 1E07; Case map
+ 1E08; 1E09; Case map
+ 1E0A; 1E0B; Case map
+ 1E0C; 1E0D; Case map
+ 1E0E; 1E0F; Case map
+ 1E10; 1E11; Case map
+ 1E12; 1E13; Case map
+ 1E14; 1E15; Case map
+ 1E16; 1E17; Case map
+ 1E18; 1E19; Case map
+ 1E1A; 1E1B; Case map
+ 1E1C; 1E1D; Case map
+ 1E1E; 1E1F; Case map
+ 1E20; 1E21; Case map
+ 1E22; 1E23; Case map
+ 1E24; 1E25; Case map
+ 1E26; 1E27; Case map
+ 1E28; 1E29; Case map
+ 1E2A; 1E2B; Case map
+ 1E2C; 1E2D; Case map
+ 1E2E; 1E2F; Case map
+ 1E30; 1E31; Case map
+ 1E32; 1E33; Case map
+ 1E34; 1E35; Case map
+ 1E36; 1E37; Case map
+ 1E38; 1E39; Case map
+ 1E3A; 1E3B; Case map
+ 1E3C; 1E3D; Case map
+
+
+
+Hoffman & Blanchet Standards Track [Page 70]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 1E3E; 1E3F; Case map
+ 1E40; 1E41; Case map
+ 1E42; 1E43; Case map
+ 1E44; 1E45; Case map
+ 1E46; 1E47; Case map
+ 1E48; 1E49; Case map
+ 1E4A; 1E4B; Case map
+ 1E4C; 1E4D; Case map
+ 1E4E; 1E4F; Case map
+ 1E50; 1E51; Case map
+ 1E52; 1E53; Case map
+ 1E54; 1E55; Case map
+ 1E56; 1E57; Case map
+ 1E58; 1E59; Case map
+ 1E5A; 1E5B; Case map
+ 1E5C; 1E5D; Case map
+ 1E5E; 1E5F; Case map
+ 1E60; 1E61; Case map
+ 1E62; 1E63; Case map
+ 1E64; 1E65; Case map
+ 1E66; 1E67; Case map
+ 1E68; 1E69; Case map
+ 1E6A; 1E6B; Case map
+ 1E6C; 1E6D; Case map
+ 1E6E; 1E6F; Case map
+ 1E70; 1E71; Case map
+ 1E72; 1E73; Case map
+ 1E74; 1E75; Case map
+ 1E76; 1E77; Case map
+ 1E78; 1E79; Case map
+ 1E7A; 1E7B; Case map
+ 1E7C; 1E7D; Case map
+ 1E7E; 1E7F; Case map
+ 1E80; 1E81; Case map
+ 1E82; 1E83; Case map
+ 1E84; 1E85; Case map
+ 1E86; 1E87; Case map
+ 1E88; 1E89; Case map
+ 1E8A; 1E8B; Case map
+ 1E8C; 1E8D; Case map
+ 1E8E; 1E8F; Case map
+ 1E90; 1E91; Case map
+ 1E92; 1E93; Case map
+ 1E94; 1E95; Case map
+ 1E96; 0068 0331; Case map
+ 1E97; 0074 0308; Case map
+ 1E98; 0077 030A; Case map
+ 1E99; 0079 030A; Case map
+
+
+
+Hoffman & Blanchet Standards Track [Page 71]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 1E9A; 0061 02BE; Case map
+ 1E9B; 1E61; Case map
+ 1EA0; 1EA1; Case map
+ 1EA2; 1EA3; Case map
+ 1EA4; 1EA5; Case map
+ 1EA6; 1EA7; Case map
+ 1EA8; 1EA9; Case map
+ 1EAA; 1EAB; Case map
+ 1EAC; 1EAD; Case map
+ 1EAE; 1EAF; Case map
+ 1EB0; 1EB1; Case map
+ 1EB2; 1EB3; Case map
+ 1EB4; 1EB5; Case map
+ 1EB6; 1EB7; Case map
+ 1EB8; 1EB9; Case map
+ 1EBA; 1EBB; Case map
+ 1EBC; 1EBD; Case map
+ 1EBE; 1EBF; Case map
+ 1EC0; 1EC1; Case map
+ 1EC2; 1EC3; Case map
+ 1EC4; 1EC5; Case map
+ 1EC6; 1EC7; Case map
+ 1EC8; 1EC9; Case map
+ 1ECA; 1ECB; Case map
+ 1ECC; 1ECD; Case map
+ 1ECE; 1ECF; Case map
+ 1ED0; 1ED1; Case map
+ 1ED2; 1ED3; Case map
+ 1ED4; 1ED5; Case map
+ 1ED6; 1ED7; Case map
+ 1ED8; 1ED9; Case map
+ 1EDA; 1EDB; Case map
+ 1EDC; 1EDD; Case map
+ 1EDE; 1EDF; Case map
+ 1EE0; 1EE1; Case map
+ 1EE2; 1EE3; Case map
+ 1EE4; 1EE5; Case map
+ 1EE6; 1EE7; Case map
+ 1EE8; 1EE9; Case map
+ 1EEA; 1EEB; Case map
+ 1EEC; 1EED; Case map
+ 1EEE; 1EEF; Case map
+ 1EF0; 1EF1; Case map
+ 1EF2; 1EF3; Case map
+ 1EF4; 1EF5; Case map
+ 1EF6; 1EF7; Case map
+ 1EF8; 1EF9; Case map
+ 1F08; 1F00; Case map
+
+
+
+Hoffman & Blanchet Standards Track [Page 72]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 1F09; 1F01; Case map
+ 1F0A; 1F02; Case map
+ 1F0B; 1F03; Case map
+ 1F0C; 1F04; Case map
+ 1F0D; 1F05; Case map
+ 1F0E; 1F06; Case map
+ 1F0F; 1F07; Case map
+ 1F18; 1F10; Case map
+ 1F19; 1F11; Case map
+ 1F1A; 1F12; Case map
+ 1F1B; 1F13; Case map
+ 1F1C; 1F14; Case map
+ 1F1D; 1F15; Case map
+ 1F28; 1F20; Case map
+ 1F29; 1F21; Case map
+ 1F2A; 1F22; Case map
+ 1F2B; 1F23; Case map
+ 1F2C; 1F24; Case map
+ 1F2D; 1F25; Case map
+ 1F2E; 1F26; Case map
+ 1F2F; 1F27; Case map
+ 1F38; 1F30; Case map
+ 1F39; 1F31; Case map
+ 1F3A; 1F32; Case map
+ 1F3B; 1F33; Case map
+ 1F3C; 1F34; Case map
+ 1F3D; 1F35; Case map
+ 1F3E; 1F36; Case map
+ 1F3F; 1F37; Case map
+ 1F48; 1F40; Case map
+ 1F49; 1F41; Case map
+ 1F4A; 1F42; Case map
+ 1F4B; 1F43; Case map
+ 1F4C; 1F44; Case map
+ 1F4D; 1F45; Case map
+ 1F50; 03C5 0313; Case map
+ 1F52; 03C5 0313 0300; Case map
+ 1F54; 03C5 0313 0301; Case map
+ 1F56; 03C5 0313 0342; Case map
+ 1F59; 1F51; Case map
+ 1F5B; 1F53; Case map
+ 1F5D; 1F55; Case map
+ 1F5F; 1F57; Case map
+ 1F68; 1F60; Case map
+ 1F69; 1F61; Case map
+ 1F6A; 1F62; Case map
+ 1F6B; 1F63; Case map
+ 1F6C; 1F64; Case map
+
+
+
+Hoffman & Blanchet Standards Track [Page 73]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 1F6D; 1F65; Case map
+ 1F6E; 1F66; Case map
+ 1F6F; 1F67; Case map
+ 1F80; 1F00 03B9; Case map
+ 1F81; 1F01 03B9; Case map
+ 1F82; 1F02 03B9; Case map
+ 1F83; 1F03 03B9; Case map
+ 1F84; 1F04 03B9; Case map
+ 1F85; 1F05 03B9; Case map
+ 1F86; 1F06 03B9; Case map
+ 1F87; 1F07 03B9; Case map
+ 1F88; 1F00 03B9; Case map
+ 1F89; 1F01 03B9; Case map
+ 1F8A; 1F02 03B9; Case map
+ 1F8B; 1F03 03B9; Case map
+ 1F8C; 1F04 03B9; Case map
+ 1F8D; 1F05 03B9; Case map
+ 1F8E; 1F06 03B9; Case map
+ 1F8F; 1F07 03B9; Case map
+ 1F90; 1F20 03B9; Case map
+ 1F91; 1F21 03B9; Case map
+ 1F92; 1F22 03B9; Case map
+ 1F93; 1F23 03B9; Case map
+ 1F94; 1F24 03B9; Case map
+ 1F95; 1F25 03B9; Case map
+ 1F96; 1F26 03B9; Case map
+ 1F97; 1F27 03B9; Case map
+ 1F98; 1F20 03B9; Case map
+ 1F99; 1F21 03B9; Case map
+ 1F9A; 1F22 03B9; Case map
+ 1F9B; 1F23 03B9; Case map
+ 1F9C; 1F24 03B9; Case map
+ 1F9D; 1F25 03B9; Case map
+ 1F9E; 1F26 03B9; Case map
+ 1F9F; 1F27 03B9; Case map
+ 1FA0; 1F60 03B9; Case map
+ 1FA1; 1F61 03B9; Case map
+ 1FA2; 1F62 03B9; Case map
+ 1FA3; 1F63 03B9; Case map
+ 1FA4; 1F64 03B9; Case map
+ 1FA5; 1F65 03B9; Case map
+ 1FA6; 1F66 03B9; Case map
+ 1FA7; 1F67 03B9; Case map
+ 1FA8; 1F60 03B9; Case map
+ 1FA9; 1F61 03B9; Case map
+ 1FAA; 1F62 03B9; Case map
+ 1FAB; 1F63 03B9; Case map
+ 1FAC; 1F64 03B9; Case map
+
+
+
+Hoffman & Blanchet Standards Track [Page 74]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 1FAD; 1F65 03B9; Case map
+ 1FAE; 1F66 03B9; Case map
+ 1FAF; 1F67 03B9; Case map
+ 1FB2; 1F70 03B9; Case map
+ 1FB3; 03B1 03B9; Case map
+ 1FB4; 03AC 03B9; Case map
+ 1FB6; 03B1 0342; Case map
+ 1FB7; 03B1 0342 03B9; Case map
+ 1FB8; 1FB0; Case map
+ 1FB9; 1FB1; Case map
+ 1FBA; 1F70; Case map
+ 1FBB; 1F71; Case map
+ 1FBC; 03B1 03B9; Case map
+ 1FBE; 03B9; Case map
+ 1FC2; 1F74 03B9; Case map
+ 1FC3; 03B7 03B9; Case map
+ 1FC4; 03AE 03B9; Case map
+ 1FC6; 03B7 0342; Case map
+ 1FC7; 03B7 0342 03B9; Case map
+ 1FC8; 1F72; Case map
+ 1FC9; 1F73; Case map
+ 1FCA; 1F74; Case map
+ 1FCB; 1F75; Case map
+ 1FCC; 03B7 03B9; Case map
+ 1FD2; 03B9 0308 0300; Case map
+ 1FD3; 03B9 0308 0301; Case map
+ 1FD6; 03B9 0342; Case map
+ 1FD7; 03B9 0308 0342; Case map
+ 1FD8; 1FD0; Case map
+ 1FD9; 1FD1; Case map
+ 1FDA; 1F76; Case map
+ 1FDB; 1F77; Case map
+ 1FE2; 03C5 0308 0300; Case map
+ 1FE3; 03C5 0308 0301; Case map
+ 1FE4; 03C1 0313; Case map
+ 1FE6; 03C5 0342; Case map
+ 1FE7; 03C5 0308 0342; Case map
+ 1FE8; 1FE0; Case map
+ 1FE9; 1FE1; Case map
+ 1FEA; 1F7A; Case map
+ 1FEB; 1F7B; Case map
+ 1FEC; 1FE5; Case map
+ 1FF2; 1F7C 03B9; Case map
+ 1FF3; 03C9 03B9; Case map
+ 1FF4; 03CE 03B9; Case map
+ 1FF6; 03C9 0342; Case map
+ 1FF7; 03C9 0342 03B9; Case map
+ 1FF8; 1F78; Case map
+
+
+
+Hoffman & Blanchet Standards Track [Page 75]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 1FF9; 1F79; Case map
+ 1FFA; 1F7C; Case map
+ 1FFB; 1F7D; Case map
+ 1FFC; 03C9 03B9; Case map
+ 2126; 03C9; Case map
+ 212A; 006B; Case map
+ 212B; 00E5; Case map
+ 2160; 2170; Case map
+ 2161; 2171; Case map
+ 2162; 2172; Case map
+ 2163; 2173; Case map
+ 2164; 2174; Case map
+ 2165; 2175; Case map
+ 2166; 2176; Case map
+ 2167; 2177; Case map
+ 2168; 2178; Case map
+ 2169; 2179; Case map
+ 216A; 217A; Case map
+ 216B; 217B; Case map
+ 216C; 217C; Case map
+ 216D; 217D; Case map
+ 216E; 217E; Case map
+ 216F; 217F; Case map
+ 24B6; 24D0; Case map
+ 24B7; 24D1; Case map
+ 24B8; 24D2; Case map
+ 24B9; 24D3; Case map
+ 24BA; 24D4; Case map
+ 24BB; 24D5; Case map
+ 24BC; 24D6; Case map
+ 24BD; 24D7; Case map
+ 24BE; 24D8; Case map
+ 24BF; 24D9; Case map
+ 24C0; 24DA; Case map
+ 24C1; 24DB; Case map
+ 24C2; 24DC; Case map
+ 24C3; 24DD; Case map
+ 24C4; 24DE; Case map
+ 24C5; 24DF; Case map
+ 24C6; 24E0; Case map
+ 24C7; 24E1; Case map
+ 24C8; 24E2; Case map
+ 24C9; 24E3; Case map
+ 24CA; 24E4; Case map
+ 24CB; 24E5; Case map
+ 24CC; 24E6; Case map
+ 24CD; 24E7; Case map
+ 24CE; 24E8; Case map
+
+
+
+Hoffman & Blanchet Standards Track [Page 76]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 24CF; 24E9; Case map
+ FB00; 0066 0066; Case map
+ FB01; 0066 0069; Case map
+ FB02; 0066 006C; Case map
+ FB03; 0066 0066 0069; Case map
+ FB04; 0066 0066 006C; Case map
+ FB05; 0073 0074; Case map
+ FB06; 0073 0074; Case map
+ FB13; 0574 0576; Case map
+ FB14; 0574 0565; Case map
+ FB15; 0574 056B; Case map
+ FB16; 057E 0576; Case map
+ FB17; 0574 056D; Case map
+ FF21; FF41; Case map
+ FF22; FF42; Case map
+ FF23; FF43; Case map
+ FF24; FF44; Case map
+ FF25; FF45; Case map
+ FF26; FF46; Case map
+ FF27; FF47; Case map
+ FF28; FF48; Case map
+ FF29; FF49; Case map
+ FF2A; FF4A; Case map
+ FF2B; FF4B; Case map
+ FF2C; FF4C; Case map
+ FF2D; FF4D; Case map
+ FF2E; FF4E; Case map
+ FF2F; FF4F; Case map
+ FF30; FF50; Case map
+ FF31; FF51; Case map
+ FF32; FF52; Case map
+ FF33; FF53; Case map
+ FF34; FF54; Case map
+ FF35; FF55; Case map
+ FF36; FF56; Case map
+ FF37; FF57; Case map
+ FF38; FF58; Case map
+ FF39; FF59; Case map
+ FF3A; FF5A; Case map
+ 10400; 10428; Case map
+ 10401; 10429; Case map
+ 10402; 1042A; Case map
+ 10403; 1042B; Case map
+ 10404; 1042C; Case map
+ 10405; 1042D; Case map
+ 10406; 1042E; Case map
+ 10407; 1042F; Case map
+ 10408; 10430; Case map
+
+
+
+Hoffman & Blanchet Standards Track [Page 77]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 10409; 10431; Case map
+ 1040A; 10432; Case map
+ 1040B; 10433; Case map
+ 1040C; 10434; Case map
+ 1040D; 10435; Case map
+ 1040E; 10436; Case map
+ 1040F; 10437; Case map
+ 10410; 10438; Case map
+ 10411; 10439; Case map
+ 10412; 1043A; Case map
+ 10413; 1043B; Case map
+ 10414; 1043C; Case map
+ 10415; 1043D; Case map
+ 10416; 1043E; Case map
+ 10417; 1043F; Case map
+ 10418; 10440; Case map
+ 10419; 10441; Case map
+ 1041A; 10442; Case map
+ 1041B; 10443; Case map
+ 1041C; 10444; Case map
+ 1041D; 10445; Case map
+ 1041E; 10446; Case map
+ 1041F; 10447; Case map
+ 10420; 10448; Case map
+ 10421; 10449; Case map
+ 10422; 1044A; Case map
+ 10423; 1044B; Case map
+ 10424; 1044C; Case map
+ 10425; 1044D; Case map
+ ----- End Table B.3 -----
+
+C. Prohibition tables
+
+ The tables in this appendix consist of lines with one prohibited code
+ point per line. The format of the lines are the value of the code
+ point, a semicolon, and a comment which is the name of the code
+ point.
+
+C.1 Space characters
+
+C.1.1 ASCII space characters
+
+ ----- Start Table C.1.1 -----
+ 0020; SPACE
+ ----- End Table C.1.1 -----
+
+
+
+
+
+
+Hoffman & Blanchet Standards Track [Page 78]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+C.1.2 Non-ASCII space characters
+ ----- Start Table C.1.2 -----
+ 00A0; NO-BREAK SPACE
+ 1680; OGHAM SPACE MARK
+ 2000; EN QUAD
+ 2001; EM QUAD
+ 2002; EN SPACE
+ 2003; EM SPACE
+ 2004; THREE-PER-EM SPACE
+ 2005; FOUR-PER-EM SPACE
+ 2006; SIX-PER-EM SPACE
+ 2007; FIGURE SPACE
+ 2008; PUNCTUATION SPACE
+ 2009; THIN SPACE
+ 200A; HAIR SPACE
+ 200B; ZERO WIDTH SPACE
+ 202F; NARROW NO-BREAK SPACE
+ 205F; MEDIUM MATHEMATICAL SPACE
+ 3000; IDEOGRAPHIC SPACE
+ ----- End Table C.1.2 -----
+
+C.2 Control characters
+
+C.2.1 ASCII control characters
+
+ ----- Start Table C.2.1 -----
+ 0000-001F; [CONTROL CHARACTERS]
+ 007F; DELETE
+ ----- End Table C.2.1 -----
+
+C.2.2 Non-ASCII control characters
+
+ ----- Start Table C.2.2 -----
+ 0080-009F; [CONTROL CHARACTERS]
+ 06DD; ARABIC END OF AYAH
+ 070F; SYRIAC ABBREVIATION MARK
+ 180E; MONGOLIAN VOWEL SEPARATOR
+ 200C; ZERO WIDTH NON-JOINER
+ 200D; ZERO WIDTH JOINER
+ 2028; LINE SEPARATOR
+ 2029; PARAGRAPH SEPARATOR
+ 2060; WORD JOINER
+ 2061; FUNCTION APPLICATION
+ 2062; INVISIBLE TIMES
+ 2063; INVISIBLE SEPARATOR
+ 206A-206F; [CONTROL CHARACTERS]
+ FEFF; ZERO WIDTH NO-BREAK SPACE
+ FFF9-FFFC; [CONTROL CHARACTERS]
+
+
+
+Hoffman & Blanchet Standards Track [Page 79]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 1D173-1D17A; [MUSICAL CONTROL CHARACTERS]
+ ----- End Table C.2.2 -----
+
+C.3 Private use
+
+ ----- Start Table C.3 -----
+ E000-F8FF; [PRIVATE USE, PLANE 0]
+ F0000-FFFFD; [PRIVATE USE, PLANE 15]
+ 100000-10FFFD; [PRIVATE USE, PLANE 16]
+ ----- End Table C.3 -----
+
+C.4 Non-character code points
+
+ ----- Start Table C.4 -----
+ FDD0-FDEF; [NONCHARACTER CODE POINTS]
+ FFFE-FFFF; [NONCHARACTER CODE POINTS]
+ 1FFFE-1FFFF; [NONCHARACTER CODE POINTS]
+ 2FFFE-2FFFF; [NONCHARACTER CODE POINTS]
+ 3FFFE-3FFFF; [NONCHARACTER CODE POINTS]
+ 4FFFE-4FFFF; [NONCHARACTER CODE POINTS]
+ 5FFFE-5FFFF; [NONCHARACTER CODE POINTS]
+ 6FFFE-6FFFF; [NONCHARACTER CODE POINTS]
+ 7FFFE-7FFFF; [NONCHARACTER CODE POINTS]
+ 8FFFE-8FFFF; [NONCHARACTER CODE POINTS]
+ 9FFFE-9FFFF; [NONCHARACTER CODE POINTS]
+ AFFFE-AFFFF; [NONCHARACTER CODE POINTS]
+ BFFFE-BFFFF; [NONCHARACTER CODE POINTS]
+ CFFFE-CFFFF; [NONCHARACTER CODE POINTS]
+ DFFFE-DFFFF; [NONCHARACTER CODE POINTS]
+ EFFFE-EFFFF; [NONCHARACTER CODE POINTS]
+ FFFFE-FFFFF; [NONCHARACTER CODE POINTS]
+ 10FFFE-10FFFF; [NONCHARACTER CODE POINTS]
+ ----- End Table C.4 -----
+
+C.5 Surrogate codes
+
+ ----- Start Table C.5 -----
+ D800-DFFF; [SURROGATE CODES]
+ ----- End Table C.5 -----
+
+C.6 Inappropriate for plain text
+
+ ----- Start Table C.6 -----
+ FFF9; INTERLINEAR ANNOTATION ANCHOR
+ FFFA; INTERLINEAR ANNOTATION SEPARATOR
+ FFFB; INTERLINEAR ANNOTATION TERMINATOR
+ FFFC; OBJECT REPLACEMENT CHARACTER
+ FFFD; REPLACEMENT CHARACTER
+
+
+
+Hoffman & Blanchet Standards Track [Page 80]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ ----- End Table C.6 -----
+
+C.7 Inappropriate for canonical representation
+
+ ----- Start Table C.7 -----
+ 2FF0-2FFB; [IDEOGRAPHIC DESCRIPTION CHARACTERS]
+ ----- End Table C.7 -----
+
+C.8 Change display properties or are deprecated
+
+ ----- Start Table C.8 -----
+ 0340; COMBINING GRAVE TONE MARK
+ 0341; COMBINING ACUTE TONE MARK
+ 200E; LEFT-TO-RIGHT MARK
+ 200F; RIGHT-TO-LEFT MARK
+ 202A; LEFT-TO-RIGHT EMBEDDING
+ 202B; RIGHT-TO-LEFT EMBEDDING
+ 202C; POP DIRECTIONAL FORMATTING
+ 202D; LEFT-TO-RIGHT OVERRIDE
+ 202E; RIGHT-TO-LEFT OVERRIDE
+ 206A; INHIBIT SYMMETRIC SWAPPING
+ 206B; ACTIVATE SYMMETRIC SWAPPING
+ 206C; INHIBIT ARABIC FORM SHAPING
+ 206D; ACTIVATE ARABIC FORM SHAPING
+ 206E; NATIONAL DIGIT SHAPES
+ 206F; NOMINAL DIGIT SHAPES
+ ----- End Table C.8 -----
+
+C.9 Tagging characters
+
+ ----- Start Table C.9 -----
+ E0001; LANGUAGE TAG
+ E0020-E007F; [TAGGING CHARACTERS]
+ ----- End Table C.9 -----
+
+D. Bidirectional tables
+
+D.1 Characters with bidirectional property "R" or "AL"
+
+ ----- Start Table D.1 -----
+ 05BE
+ 05C0
+ 05C3
+ 05D0-05EA
+ 05F0-05F4
+ 061B
+ 061F
+ 0621-063A
+
+
+
+Hoffman & Blanchet Standards Track [Page 81]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 0640-064A
+ 066D-066F
+ 0671-06D5
+ 06DD
+ 06E5-06E6
+ 06FA-06FE
+ 0700-070D
+ 0710
+ 0712-072C
+ 0780-07A5
+ 07B1
+ 200F
+ FB1D
+ FB1F-FB28
+ FB2A-FB36
+ FB38-FB3C
+ FB3E
+ FB40-FB41
+ FB43-FB44
+ FB46-FBB1
+ FBD3-FD3D
+ FD50-FD8F
+ FD92-FDC7
+ FDF0-FDFC
+ FE70-FE74
+ FE76-FEFC
+ ----- End Table D.1 -----
+
+D.2 Characters with bidirectional property "L"
+
+ ----- Start Table D.2 -----
+ 0041-005A
+ 0061-007A
+ 00AA
+ 00B5
+ 00BA
+ 00C0-00D6
+ 00D8-00F6
+ 00F8-0220
+ 0222-0233
+ 0250-02AD
+ 02B0-02B8
+ 02BB-02C1
+ 02D0-02D1
+ 02E0-02E4
+ 02EE
+ 037A
+ 0386
+
+
+
+Hoffman & Blanchet Standards Track [Page 82]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 0388-038A
+ 038C
+ 038E-03A1
+ 03A3-03CE
+ 03D0-03F5
+ 0400-0482
+ 048A-04CE
+ 04D0-04F5
+ 04F8-04F9
+ 0500-050F
+ 0531-0556
+ 0559-055F
+ 0561-0587
+ 0589
+ 0903
+ 0905-0939
+ 093D-0940
+ 0949-094C
+ 0950
+ 0958-0961
+ 0964-0970
+ 0982-0983
+ 0985-098C
+ 098F-0990
+ 0993-09A8
+ 09AA-09B0
+ 09B2
+ 09B6-09B9
+ 09BE-09C0
+ 09C7-09C8
+ 09CB-09CC
+ 09D7
+ 09DC-09DD
+ 09DF-09E1
+ 09E6-09F1
+ 09F4-09FA
+ 0A05-0A0A
+ 0A0F-0A10
+ 0A13-0A28
+ 0A2A-0A30
+ 0A32-0A33
+ 0A35-0A36
+ 0A38-0A39
+ 0A3E-0A40
+ 0A59-0A5C
+ 0A5E
+ 0A66-0A6F
+ 0A72-0A74
+
+
+
+Hoffman & Blanchet Standards Track [Page 83]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 0A83
+ 0A85-0A8B
+ 0A8D
+ 0A8F-0A91
+ 0A93-0AA8
+ 0AAA-0AB0
+ 0AB2-0AB3
+ 0AB5-0AB9
+ 0ABD-0AC0
+ 0AC9
+ 0ACB-0ACC
+ 0AD0
+ 0AE0
+ 0AE6-0AEF
+ 0B02-0B03
+ 0B05-0B0C
+ 0B0F-0B10
+ 0B13-0B28
+ 0B2A-0B30
+ 0B32-0B33
+ 0B36-0B39
+ 0B3D-0B3E
+ 0B40
+ 0B47-0B48
+ 0B4B-0B4C
+ 0B57
+ 0B5C-0B5D
+ 0B5F-0B61
+ 0B66-0B70
+ 0B83
+ 0B85-0B8A
+ 0B8E-0B90
+ 0B92-0B95
+ 0B99-0B9A
+ 0B9C
+ 0B9E-0B9F
+ 0BA3-0BA4
+ 0BA8-0BAA
+ 0BAE-0BB5
+ 0BB7-0BB9
+ 0BBE-0BBF
+ 0BC1-0BC2
+ 0BC6-0BC8
+ 0BCA-0BCC
+ 0BD7
+ 0BE7-0BF2
+ 0C01-0C03
+ 0C05-0C0C
+
+
+
+Hoffman & Blanchet Standards Track [Page 84]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 0C0E-0C10
+ 0C12-0C28
+ 0C2A-0C33
+ 0C35-0C39
+ 0C41-0C44
+ 0C60-0C61
+ 0C66-0C6F
+ 0C82-0C83
+ 0C85-0C8C
+ 0C8E-0C90
+ 0C92-0CA8
+ 0CAA-0CB3
+ 0CB5-0CB9
+ 0CBE
+ 0CC0-0CC4
+ 0CC7-0CC8
+ 0CCA-0CCB
+ 0CD5-0CD6
+ 0CDE
+ 0CE0-0CE1
+ 0CE6-0CEF
+ 0D02-0D03
+ 0D05-0D0C
+ 0D0E-0D10
+ 0D12-0D28
+ 0D2A-0D39
+ 0D3E-0D40
+ 0D46-0D48
+ 0D4A-0D4C
+ 0D57
+ 0D60-0D61
+ 0D66-0D6F
+ 0D82-0D83
+ 0D85-0D96
+ 0D9A-0DB1
+ 0DB3-0DBB
+ 0DBD
+ 0DC0-0DC6
+ 0DCF-0DD1
+ 0DD8-0DDF
+ 0DF2-0DF4
+ 0E01-0E30
+ 0E32-0E33
+ 0E40-0E46
+ 0E4F-0E5B
+ 0E81-0E82
+ 0E84
+ 0E87-0E88
+
+
+
+Hoffman & Blanchet Standards Track [Page 85]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 0E8A
+ 0E8D
+ 0E94-0E97
+ 0E99-0E9F
+ 0EA1-0EA3
+ 0EA5
+ 0EA7
+ 0EAA-0EAB
+ 0EAD-0EB0
+ 0EB2-0EB3
+ 0EBD
+ 0EC0-0EC4
+ 0EC6
+ 0ED0-0ED9
+ 0EDC-0EDD
+ 0F00-0F17
+ 0F1A-0F34
+ 0F36
+ 0F38
+ 0F3E-0F47
+ 0F49-0F6A
+ 0F7F
+ 0F85
+ 0F88-0F8B
+ 0FBE-0FC5
+ 0FC7-0FCC
+ 0FCF
+ 1000-1021
+ 1023-1027
+ 1029-102A
+ 102C
+ 1031
+ 1038
+ 1040-1057
+ 10A0-10C5
+ 10D0-10F8
+ 10FB
+ 1100-1159
+ 115F-11A2
+ 11A8-11F9
+ 1200-1206
+ 1208-1246
+ 1248
+ 124A-124D
+ 1250-1256
+ 1258
+ 125A-125D
+ 1260-1286
+
+
+
+Hoffman & Blanchet Standards Track [Page 86]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 1288
+ 128A-128D
+ 1290-12AE
+ 12B0
+ 12B2-12B5
+ 12B8-12BE
+ 12C0
+ 12C2-12C5
+ 12C8-12CE
+ 12D0-12D6
+ 12D8-12EE
+ 12F0-130E
+ 1310
+ 1312-1315
+ 1318-131E
+ 1320-1346
+ 1348-135A
+ 1361-137C
+ 13A0-13F4
+ 1401-1676
+ 1681-169A
+ 16A0-16F0
+ 1700-170C
+ 170E-1711
+ 1720-1731
+ 1735-1736
+ 1740-1751
+ 1760-176C
+ 176E-1770
+ 1780-17B6
+ 17BE-17C5
+ 17C7-17C8
+ 17D4-17DA
+ 17DC
+ 17E0-17E9
+ 1810-1819
+ 1820-1877
+ 1880-18A8
+ 1E00-1E9B
+ 1EA0-1EF9
+ 1F00-1F15
+ 1F18-1F1D
+ 1F20-1F45
+ 1F48-1F4D
+ 1F50-1F57
+ 1F59
+ 1F5B
+ 1F5D
+
+
+
+Hoffman & Blanchet Standards Track [Page 87]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 1F5F-1F7D
+ 1F80-1FB4
+ 1FB6-1FBC
+ 1FBE
+ 1FC2-1FC4
+ 1FC6-1FCC
+ 1FD0-1FD3
+ 1FD6-1FDB
+ 1FE0-1FEC
+ 1FF2-1FF4
+ 1FF6-1FFC
+ 200E
+ 2071
+ 207F
+ 2102
+ 2107
+ 210A-2113
+ 2115
+ 2119-211D
+ 2124
+ 2126
+ 2128
+ 212A-212D
+ 212F-2131
+ 2133-2139
+ 213D-213F
+ 2145-2149
+ 2160-2183
+ 2336-237A
+ 2395
+ 249C-24E9
+ 3005-3007
+ 3021-3029
+ 3031-3035
+ 3038-303C
+ 3041-3096
+ 309D-309F
+ 30A1-30FA
+ 30FC-30FF
+ 3105-312C
+ 3131-318E
+ 3190-31B7
+ 31F0-321C
+ 3220-3243
+ 3260-327B
+ 327F-32B0
+ 32C0-32CB
+ 32D0-32FE
+
+
+
+Hoffman & Blanchet Standards Track [Page 88]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 3300-3376
+ 337B-33DD
+ 33E0-33FE
+ 3400-4DB5
+ 4E00-9FA5
+ A000-A48C
+ AC00-D7A3
+ D800-FA2D
+ FA30-FA6A
+ FB00-FB06
+ FB13-FB17
+ FF21-FF3A
+ FF41-FF5A
+ FF66-FFBE
+ FFC2-FFC7
+ FFCA-FFCF
+ FFD2-FFD7
+ FFDA-FFDC
+ 10300-1031E
+ 10320-10323
+ 10330-1034A
+ 10400-10425
+ 10428-1044D
+ 1D000-1D0F5
+ 1D100-1D126
+ 1D12A-1D166
+ 1D16A-1D172
+ 1D183-1D184
+ 1D18C-1D1A9
+ 1D1AE-1D1DD
+ 1D400-1D454
+ 1D456-1D49C
+ 1D49E-1D49F
+ 1D4A2
+ 1D4A5-1D4A6
+ 1D4A9-1D4AC
+ 1D4AE-1D4B9
+ 1D4BB
+ 1D4BD-1D4C0
+ 1D4C2-1D4C3
+ 1D4C5-1D505
+ 1D507-1D50A
+ 1D50D-1D514
+ 1D516-1D51C
+ 1D51E-1D539
+ 1D53B-1D53E
+ 1D540-1D544
+ 1D546
+
+
+
+Hoffman & Blanchet Standards Track [Page 89]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+ 1D54A-1D550
+ 1D552-1D6A3
+ 1D6A8-1D7C9
+ 20000-2A6D6
+ 2F800-2FA1D
+ F0000-FFFFD
+ 100000-10FFFD
+ ----- End Table D.2 -----
+
+Authors' Addresses
+
+ Paul Hoffman
+ Internet Mail Consortium and VPN Consortium
+ 127 Segre Place
+ Santa Cruz, CA 95060 USA
+
+ EMail: paul.hoffman@imc.org and paul.hoffman@vpnc.org
+
+
+ Marc Blanchet
+ Viagenie inc.
+ 2875 boul. Laurier, bur. 300
+ Ste-Foy, Quebec, Canada, G1V 2M2
+
+ EMail: Marc.Blanchet@viagenie.qc.ca
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Hoffman & Blanchet Standards Track [Page 90]
+
+RFC 3454 Preparation of Internationalized Strings December 2002
+
+
+Full Copyright Statement
+
+ Copyright (C) The Internet Society (2002). All Rights Reserved.
+
+ This document and translations of it may be copied and furnished to
+ others, and derivative works that comment on or otherwise explain it
+ or assist in its implementation may be prepared, copied, published
+ and distributed, in whole or in part, without restriction of any
+ kind, provided that the above copyright notice and this paragraph are
+ included on all such copies and derivative works. However, this
+ document itself may not be modified in any way, such as by removing
+ the copyright notice or references to the Internet Society or other
+ Internet organizations, except as needed for the purpose of
+ developing Internet standards in which case the procedures for
+ copyrights defined in the Internet Standards process must be
+ followed, or as required to translate it into languages other than
+ English.
+
+ The limited permissions granted above are perpetual and will not be
+ revoked by the Internet Society or its successors or assigns.
+
+ This document and the information contained herein is provided on an
+ "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+ TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is currently provided by the
+ Internet Society.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Hoffman & Blanchet Standards Track [Page 91]
+
diff --git a/source4/heimdal/lib/wind/rfc3490.txt b/source4/heimdal/lib/wind/rfc3490.txt
new file mode 100644
index 0000000000..d2e0b3b75a
--- /dev/null
+++ b/source4/heimdal/lib/wind/rfc3490.txt
@@ -0,0 +1,1235 @@
+
+
+
+
+
+
+Network Working Group P. Faltstrom
+Request for Comments: 3490 Cisco
+Category: Standards Track P. Hoffman
+ IMC & VPNC
+ A. Costello
+ UC Berkeley
+ March 2003
+
+
+ Internationalizing Domain Names in Applications (IDNA)
+
+Status of this Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2003). All Rights Reserved.
+
+Abstract
+
+ Until now, there has been no standard method for domain names to use
+ characters outside the ASCII repertoire. This document defines
+ internationalized domain names (IDNs) and a mechanism called
+ Internationalizing Domain Names in Applications (IDNA) for handling
+ them in a standard fashion. IDNs use characters drawn from a large
+ repertoire (Unicode), but IDNA allows the non-ASCII characters to be
+ represented using only the ASCII characters already allowed in so-
+ called host names today. This backward-compatible representation is
+ required in existing protocols like DNS, so that IDNs can be
+ introduced with no changes to the existing infrastructure. IDNA is
+ only meant for processing domain names, not free text.
+
+Table of Contents
+
+ 1. Introduction.................................................. 2
+ 1.1 Problem Statement......................................... 3
+ 1.2 Limitations of IDNA....................................... 3
+ 1.3 Brief overview for application developers................. 4
+ 2. Terminology................................................... 5
+ 3. Requirements and applicability................................ 7
+ 3.1 Requirements.............................................. 7
+ 3.2 Applicability............................................. 8
+ 3.2.1. DNS resource records................................ 8
+
+
+
+Faltstrom, et al. Standards Track [Page 1]
+
+RFC 3490 IDNA March 2003
+
+
+ 3.2.2. Non-domain-name data types stored in domain names... 9
+ 4. Conversion operations......................................... 9
+ 4.1 ToASCII................................................... 10
+ 4.2 ToUnicode................................................. 11
+ 5. ACE prefix.................................................... 12
+ 6. Implications for typical applications using DNS............... 13
+ 6.1 Entry and display in applications......................... 14
+ 6.2 Applications and resolver libraries....................... 15
+ 6.3 DNS servers............................................... 15
+ 6.4 Avoiding exposing users to the raw ACE encoding........... 16
+ 6.5 DNSSEC authentication of IDN domain names................ 16
+ 7. Name server considerations.................................... 17
+ 8. Root server considerations.................................... 17
+ 9. References.................................................... 18
+ 9.1 Normative References...................................... 18
+ 9.2 Informative References.................................... 18
+ 10. Security Considerations...................................... 19
+ 11. IANA Considerations.......................................... 20
+ 12. Authors' Addresses........................................... 21
+ 13. Full Copyright Statement..................................... 22
+
+1. Introduction
+
+ IDNA works by allowing applications to use certain ASCII name labels
+ (beginning with a special prefix) to represent non-ASCII name labels.
+ Lower-layer protocols need not be aware of this; therefore IDNA does
+ not depend on changes to any infrastructure. In particular, IDNA
+ does not depend on any changes to DNS servers, resolvers, or protocol
+ elements, because the ASCII name service provided by the existing DNS
+ is entirely sufficient for IDNA.
+
+ This document does not require any applications to conform to IDNA,
+ but applications can elect to use IDNA in order to support IDN while
+ maintaining interoperability with existing infrastructure. If an
+ application wants to use non-ASCII characters in domain names, IDNA
+ is the only currently-defined option. Adding IDNA support to an
+ existing application entails changes to the application only, and
+ leaves room for flexibility in the user interface.
+
+ A great deal of the discussion of IDN solutions has focused on
+ transition issues and how IDN will work in a world where not all of
+ the components have been updated. Proposals that were not chosen by
+ the IDN Working Group would depend on user applications, resolvers,
+ and DNS servers being updated in order for a user to use an
+ internationalized domain name. Rather than rely on widespread
+ updating of all components, IDNA depends on updates to user
+ applications only; no changes are needed to the DNS protocol or any
+ DNS servers or the resolvers on user's computers.
+
+
+
+Faltstrom, et al. Standards Track [Page 2]
+
+RFC 3490 IDNA March 2003
+
+
+1.1 Problem Statement
+
+ The IDNA specification solves the problem of extending the repertoire
+ of characters that can be used in domain names to include the Unicode
+ repertoire (with some restrictions).
+
+ IDNA does not extend the service offered by DNS to the applications.
+ Instead, the applications (and, by implication, the users) continue
+ to see an exact-match lookup service. Either there is a single
+ exactly-matching name or there is no match. This model has served
+ the existing applications well, but it requires, with or without
+ internationalized domain names, that users know the exact spelling of
+ the domain names that the users type into applications such as web
+ browsers and mail user agents. The introduction of the larger
+ repertoire of characters potentially makes the set of misspellings
+ larger, especially given that in some cases the same appearance, for
+ example on a business card, might visually match several Unicode code
+ points or several sequences of code points.
+
+ IDNA allows the graceful introduction of IDNs not only by avoiding
+ upgrades to existing infrastructure (such as DNS servers and mail
+ transport agents), but also by allowing some rudimentary use of IDNs
+ in applications by using the ASCII representation of the non-ASCII
+ name labels. While such names are very user-unfriendly to read and
+ type, and hence are not suitable for user input, they allow (for
+ instance) replying to email and clicking on URLs even though the
+ domain name displayed is incomprehensible to the user. In order to
+ allow user-friendly input and output of the IDNs, the applications
+ need to be modified to conform to this specification.
+
+ IDNA uses the Unicode character repertoire, which avoids the
+ significant delays that would be inherent in waiting for a different
+ and specific character set be defined for IDN purposes by some other
+ standards developing organization.
+
+1.2 Limitations of IDNA
+
+ The IDNA protocol does not solve all linguistic issues with users
+ inputting names in different scripts. Many important language-based
+ and script-based mappings are not covered in IDNA and need to be
+ handled outside the protocol. For example, names that are entered in
+ a mix of traditional and simplified Chinese characters will not be
+ mapped to a single canonical name. Another example is Scandinavian
+ names that are entered with U+00F6 (LATIN SMALL LETTER O WITH
+ DIAERESIS) will not be mapped to U+00F8 (LATIN SMALL LETTER O WITH
+ STROKE).
+
+
+
+
+
+Faltstrom, et al. Standards Track [Page 3]
+
+RFC 3490 IDNA March 2003
+
+
+ An example of an important issue that is not considered in detail in
+ IDNA is how to provide a high probability that a user who is entering
+ a domain name based on visual information (such as from a business
+ card or billboard) or aural information (such as from a telephone or
+ radio) would correctly enter the IDN. Similar issues exist for ASCII
+ domain names, for example the possible visual confusion between the
+ letter 'O' and the digit zero, but the introduction of the larger
+ repertoire of characters creates more opportunities of similar
+ looking and similar sounding names. Note that this is a complex
+ issue relating to languages, input methods on computers, and so on.
+ Furthermore, the kind of matching and searching necessary for a high
+ probability of success would not fit the role of the DNS and its
+ exact matching function.
+
+1.3 Brief overview for application developers
+
+ Applications can use IDNA to support internationalized domain names
+ anywhere that ASCII domain names are already supported, including DNS
+ master files and resolver interfaces. (Applications can also define
+ protocols and interfaces that support IDNs directly using non-ASCII
+ representations. IDNA does not prescribe any particular
+ representation for new protocols, but it still defines which names
+ are valid and how they are compared.)
+
+ The IDNA protocol is contained completely within applications. It is
+ not a client-server or peer-to-peer protocol: everything is done
+ inside the application itself. When used with a DNS resolver
+ library, IDNA is inserted as a "shim" between the application and the
+ resolver library. When used for writing names into a DNS zone, IDNA
+ is used just before the name is committed to the zone.
+
+ There are two operations described in section 4 of this document:
+
+ - The ToASCII operation is used before sending an IDN to something
+ that expects ASCII names (such as a resolver) or writing an IDN
+ into a place that expects ASCII names (such as a DNS master file).
+
+ - The ToUnicode operation is used when displaying names to users,
+ for example names obtained from a DNS zone.
+
+ It is important to note that the ToASCII operation can fail. If it
+ fails when processing a domain name, that domain name cannot be used
+ as an internationalized domain name and the application has to have
+ some method of dealing with this failure.
+
+ IDNA requires that implementations process input strings with
+ Nameprep [NAMEPREP], which is a profile of Stringprep [STRINGPREP],
+ and then with Punycode [PUNYCODE]. Implementations of IDNA MUST
+
+
+
+Faltstrom, et al. Standards Track [Page 4]
+
+RFC 3490 IDNA March 2003
+
+
+ fully implement Nameprep and Punycode; neither Nameprep nor Punycode
+ are optional.
+
+2. Terminology
+
+ The key words "MUST", "SHALL", "REQUIRED", "SHOULD", "RECOMMENDED",
+ and "MAY" in this document are to be interpreted as described in BCP
+ 14, RFC 2119 [RFC2119].
+
+ A code point is an integer value associated with a character in a
+ coded character set.
+
+ Unicode [UNICODE] is a coded character set containing tens of
+ thousands of characters. A single Unicode code point is denoted by
+ "U+" followed by four to six hexadecimal digits, while a range of
+ Unicode code points is denoted by two hexadecimal numbers separated
+ by "..", with no prefixes.
+
+ ASCII means US-ASCII [USASCII], a coded character set containing 128
+ characters associated with code points in the range 0..7F. Unicode
+ is an extension of ASCII: it includes all the ASCII characters and
+ associates them with the same code points.
+
+ The term "LDH code points" is defined in this document to mean the
+ code points associated with ASCII letters, digits, and the hyphen-
+ minus; that is, U+002D, 30..39, 41..5A, and 61..7A. "LDH" is an
+ abbreviation for "letters, digits, hyphen".
+
+ [STD13] talks about "domain names" and "host names", but many people
+ use the terms interchangeably. Further, because [STD13] was not
+ terribly clear, many people who are sure they know the exact
+ definitions of each of these terms disagree on the definitions. In
+ this document the term "domain name" is used in general. This
+ document explicitly cites [STD3] whenever referring to the host name
+ syntax restrictions defined therein.
+
+ A label is an individual part of a domain name. Labels are usually
+ shown separated by dots; for example, the domain name
+ "www.example.com" is composed of three labels: "www", "example", and
+ "com". (The zero-length root label described in [STD13], which can
+ be explicit as in "www.example.com." or implicit as in
+ "www.example.com", is not considered a label in this specification.)
+ IDNA extends the set of usable characters in labels that are text.
+ For the rest of this document, the term "label" is shorthand for
+ "text label", and "every label" means "every text label".
+
+
+
+
+
+
+Faltstrom, et al. Standards Track [Page 5]
+
+RFC 3490 IDNA March 2003
+
+
+ An "internationalized label" is a label to which the ToASCII
+ operation (see section 4) can be applied without failing (with the
+ UseSTD3ASCIIRules flag unset). This implies that every ASCII label
+ that satisfies the [STD13] length restriction is an internationalized
+ label. Therefore the term "internationalized label" is a
+ generalization, embracing both old ASCII labels and new non-ASCII
+ labels. Although most Unicode characters can appear in
+ internationalized labels, ToASCII will fail for some input strings,
+ and such strings are not valid internationalized labels.
+
+ An "internationalized domain name" (IDN) is a domain name in which
+ every label is an internationalized label. This implies that every
+ ASCII domain name is an IDN (which implies that it is possible for a
+ name to be an IDN without it containing any non-ASCII characters).
+ This document does not attempt to define an "internationalized host
+ name". Just as has been the case with ASCII names, some DNS zone
+ administrators may impose restrictions, beyond those imposed by DNS
+ or IDNA, on the characters or strings that may be registered as
+ labels in their zones. Such restrictions have no impact on the
+ syntax or semantics of DNS protocol messages; a query for a name that
+ matches no records will yield the same response regardless of the
+ reason why it is not in the zone. Clients issuing queries or
+ interpreting responses cannot be assumed to have any knowledge of
+ zone-specific restrictions or conventions.
+
+ In IDNA, equivalence of labels is defined in terms of the ToASCII
+ operation, which constructs an ASCII form for a given label, whether
+ or not the label was already an ASCII label. Labels are defined to
+ be equivalent if and only if their ASCII forms produced by ToASCII
+ match using a case-insensitive ASCII comparison. ASCII labels
+ already have a notion of equivalence: upper case and lower case are
+ considered equivalent. The IDNA notion of equivalence is an
+ extension of that older notion. Equivalent labels in IDNA are
+ treated as alternate forms of the same label, just as "foo" and "Foo"
+ are treated as alternate forms of the same label.
+
+ To allow internationalized labels to be handled by existing
+ applications, IDNA uses an "ACE label" (ACE stands for ASCII
+ Compatible Encoding). An ACE label is an internationalized label
+ that can be rendered in ASCII and is equivalent to an
+ internationalized label that cannot be rendered in ASCII. Given any
+ internationalized label that cannot be rendered in ASCII, the ToASCII
+ operation will convert it to an equivalent ACE label (whereas an
+ ASCII label will be left unaltered by ToASCII). ACE labels are
+ unsuitable for display to users. The ToUnicode operation will
+ convert any label to an equivalent non-ACE label. In fact, an ACE
+ label is formally defined to be any label that the ToUnicode
+ operation would alter (whereas non-ACE labels are left unaltered by
+
+
+
+Faltstrom, et al. Standards Track [Page 6]
+
+RFC 3490 IDNA March 2003
+
+
+ ToUnicode). Every ACE label begins with the ACE prefix specified in
+ section 5. The ToASCII and ToUnicode operations are specified in
+ section 4.
+
+ The "ACE prefix" is defined in this document to be a string of ASCII
+ characters that appears at the beginning of every ACE label. It is
+ specified in section 5.
+
+ A "domain name slot" is defined in this document to be a protocol
+ element or a function argument or a return value (and so on)
+ explicitly designated for carrying a domain name. Examples of domain
+ name slots include: the QNAME field of a DNS query; the name argument
+ of the gethostbyname() library function; the part of an email address
+ following the at-sign (@) in the From: field of an email message
+ header; and the host portion of the URI in the src attribute of an
+ HTML <IMG> tag. General text that just happens to contain a domain
+ name is not a domain name slot; for example, a domain name appearing
+ in the plain text body of an email message is not occupying a domain
+ name slot.
+
+ An "IDN-aware domain name slot" is defined in this document to be a
+ domain name slot explicitly designated for carrying an
+ internationalized domain name as defined in this document. The
+ designation may be static (for example, in the specification of the
+ protocol or interface) or dynamic (for example, as a result of
+ negotiation in an interactive session).
+
+ An "IDN-unaware domain name slot" is defined in this document to be
+ any domain name slot that is not an IDN-aware domain name slot.
+ Obviously, this includes any domain name slot whose specification
+ predates IDNA.
+
+3. Requirements and applicability
+
+3.1 Requirements
+
+ IDNA conformance means adherence to the following four requirements:
+
+ 1) Whenever dots are used as label separators, the following
+ characters MUST be recognized as dots: U+002E (full stop), U+3002
+ (ideographic full stop), U+FF0E (fullwidth full stop), U+FF61
+ (halfwidth ideographic full stop).
+
+ 2) Whenever a domain name is put into an IDN-unaware domain name slot
+ (see section 2), it MUST contain only ASCII characters. Given an
+ internationalized domain name (IDN), an equivalent domain name
+ satisfying this requirement can be obtained by applying the
+
+
+
+
+Faltstrom, et al. Standards Track [Page 7]
+
+RFC 3490 IDNA March 2003
+
+
+ ToASCII operation (see section 4) to each label and, if dots are
+ used as label separators, changing all the label separators to
+ U+002E.
+
+ 3) ACE labels obtained from domain name slots SHOULD be hidden from
+ users when it is known that the environment can handle the non-ACE
+ form, except when the ACE form is explicitly requested. When it
+ is not known whether or not the environment can handle the non-ACE
+ form, the application MAY use the non-ACE form (which might fail,
+ such as by not being displayed properly), or it MAY use the ACE
+ form (which will look unintelligle to the user). Given an
+ internationalized domain name, an equivalent domain name
+ containing no ACE labels can be obtained by applying the ToUnicode
+ operation (see section 4) to each label. When requirements 2 and
+ 3 both apply, requirement 2 takes precedence.
+
+ 4) Whenever two labels are compared, they MUST be considered to match
+ if and only if they are equivalent, that is, their ASCII forms
+ (obtained by applying ToASCII) match using a case-insensitive
+ ASCII comparison. Whenever two names are compared, they MUST be
+ considered to match if and only if their corresponding labels
+ match, regardless of whether the names use the same forms of label
+ separators.
+
+3.2 Applicability
+
+ IDNA is applicable to all domain names in all domain name slots
+ except where it is explicitly excluded.
+
+ This implies that IDNA is applicable to many protocols that predate
+ IDNA. Note that IDNs occupying domain name slots in those protocols
+ MUST be in ASCII form (see section 3.1, requirement 2).
+
+3.2.1. DNS resource records
+
+ IDNA does not apply to domain names in the NAME and RDATA fields of
+ DNS resource records whose CLASS is not IN. This exclusion applies
+ to every non-IN class, present and future, except where future
+ standards override this exclusion by explicitly inviting the use of
+ IDNA.
+
+ There are currently no other exclusions on the applicability of IDNA
+ to DNS resource records; it depends entirely on the CLASS, and not on
+ the TYPE. This will remain true, even as new types are defined,
+ unless there is a compelling reason for a new type to complicate
+ matters by imposing type-specific rules.
+
+
+
+
+
+Faltstrom, et al. Standards Track [Page 8]
+
+RFC 3490 IDNA March 2003
+
+
+3.2.2. Non-domain-name data types stored in domain names
+
+ Although IDNA enables the representation of non-ASCII characters in
+ domain names, that does not imply that IDNA enables the
+ representation of non-ASCII characters in other data types that are
+ stored in domain names. For example, an email address local part is
+ sometimes stored in a domain label (hostmaster@example.com would be
+ represented as hostmaster.example.com in the RDATA field of an SOA
+ record). IDNA does not update the existing email standards, which
+ allow only ASCII characters in local parts. Therefore, unless the
+ email standards are revised to invite the use of IDNA for local
+ parts, a domain label that holds the local part of an email address
+ SHOULD NOT begin with the ACE prefix, and even if it does, it is to
+ be interpreted literally as a local part that happens to begin with
+ the ACE prefix.
+
+4. Conversion operations
+
+ An application converts a domain name put into an IDN-unaware slot or
+ displayed to a user. This section specifies the steps to perform in
+ the conversion, and the ToASCII and ToUnicode operations.
+
+ The input to ToASCII or ToUnicode is a single label that is a
+ sequence of Unicode code points (remember that all ASCII code points
+ are also Unicode code points). If a domain name is represented using
+ a character set other than Unicode or US-ASCII, it will first need to
+ be transcoded to Unicode.
+
+ Starting from a whole domain name, the steps that an application
+ takes to do the conversions are:
+
+ 1) Decide whether the domain name is a "stored string" or a "query
+ string" as described in [STRINGPREP]. If this conversion follows
+ the "queries" rule from [STRINGPREP], set the flag called
+ "AllowUnassigned".
+
+ 2) Split the domain name into individual labels as described in
+ section 3.1. The labels do not include the separator.
+
+ 3) For each label, decide whether or not to enforce the restrictions
+ on ASCII characters in host names [STD3]. (Applications already
+ faced this choice before the introduction of IDNA, and can
+ continue to make the decision the same way they always have; IDNA
+ makes no new recommendations regarding this choice.) If the
+ restrictions are to be enforced, set the flag called
+ "UseSTD3ASCIIRules" for that label.
+
+
+
+
+
+Faltstrom, et al. Standards Track [Page 9]
+
+RFC 3490 IDNA March 2003
+
+
+ 4) Process each label with either the ToASCII or the ToUnicode
+ operation as appropriate. Typically, you use the ToASCII
+ operation if you are about to put the name into an IDN-unaware
+ slot, and you use the ToUnicode operation if you are displaying
+ the name to a user; section 3.1 gives greater detail on the
+ applicable requirements.
+
+ 5) If ToASCII was applied in step 4 and dots are used as label
+ separators, change all the label separators to U+002E (full stop).
+
+ The following two subsections define the ToASCII and ToUnicode
+ operations that are used in step 4.
+
+ This description of the protocol uses specific procedure names, names
+ of flags, and so on, in order to facilitate the specification of the
+ protocol. These names, as well as the actual steps of the
+ procedures, are not required of an implementation. In fact, any
+ implementation which has the same external behavior as specified in
+ this document conforms to this specification.
+
+4.1 ToASCII
+
+ The ToASCII operation takes a sequence of Unicode code points that
+ make up one label and transforms it into a sequence of code points in
+ the ASCII range (0..7F). If ToASCII succeeds, the original sequence
+ and the resulting sequence are equivalent labels.
+
+ It is important to note that the ToASCII operation can fail. ToASCII
+ fails if any step of it fails. If any step of the ToASCII operation
+ fails on any label in a domain name, that domain name MUST NOT be
+ used as an internationalized domain name. The method for dealing
+ with this failure is application-specific.
+
+ The inputs to ToASCII are a sequence of code points, the
+ AllowUnassigned flag, and the UseSTD3ASCIIRules flag. The output of
+ ToASCII is either a sequence of ASCII code points or a failure
+ condition.
+
+ ToASCII never alters a sequence of code points that are all in the
+ ASCII range to begin with (although it could fail). Applying the
+ ToASCII operation multiple times has exactly the same effect as
+ applying it just once.
+
+ ToASCII consists of the following steps:
+
+ 1. If the sequence contains any code points outside the ASCII range
+ (0..7F) then proceed to step 2, otherwise skip to step 3.
+
+
+
+
+Faltstrom, et al. Standards Track [Page 10]
+
+RFC 3490 IDNA March 2003
+
+
+ 2. Perform the steps specified in [NAMEPREP] and fail if there is an
+ error. The AllowUnassigned flag is used in [NAMEPREP].
+
+ 3. If the UseSTD3ASCIIRules flag is set, then perform these checks:
+
+ (a) Verify the absence of non-LDH ASCII code points; that is, the
+ absence of 0..2C, 2E..2F, 3A..40, 5B..60, and 7B..7F.
+
+ (b) Verify the absence of leading and trailing hyphen-minus; that
+ is, the absence of U+002D at the beginning and end of the
+ sequence.
+
+ 4. If the sequence contains any code points outside the ASCII range
+ (0..7F) then proceed to step 5, otherwise skip to step 8.
+
+ 5. Verify that the sequence does NOT begin with the ACE prefix.
+
+ 6. Encode the sequence using the encoding algorithm in [PUNYCODE] and
+ fail if there is an error.
+
+ 7. Prepend the ACE prefix.
+
+ 8. Verify that the number of code points is in the range 1 to 63
+ inclusive.
+
+4.2 ToUnicode
+
+ The ToUnicode operation takes a sequence of Unicode code points that
+ make up one label and returns a sequence of Unicode code points. If
+ the input sequence is a label in ACE form, then the result is an
+ equivalent internationalized label that is not in ACE form, otherwise
+ the original sequence is returned unaltered.
+
+ ToUnicode never fails. If any step fails, then the original input
+ sequence is returned immediately in that step.
+
+ The ToUnicode output never contains more code points than its input.
+ Note that the number of octets needed to represent a sequence of code
+ points depends on the particular character encoding used.
+
+ The inputs to ToUnicode are a sequence of code points, the
+ AllowUnassigned flag, and the UseSTD3ASCIIRules flag. The output of
+ ToUnicode is always a sequence of Unicode code points.
+
+ 1. If all code points in the sequence are in the ASCII range (0..7F)
+ then skip to step 3.
+
+
+
+
+
+Faltstrom, et al. Standards Track [Page 11]
+
+RFC 3490 IDNA March 2003
+
+
+ 2. Perform the steps specified in [NAMEPREP] and fail if there is an
+ error. (If step 3 of ToASCII is also performed here, it will not
+ affect the overall behavior of ToUnicode, but it is not
+ necessary.) The AllowUnassigned flag is used in [NAMEPREP].
+
+ 3. Verify that the sequence begins with the ACE prefix, and save a
+ copy of the sequence.
+
+ 4. Remove the ACE prefix.
+
+ 5. Decode the sequence using the decoding algorithm in [PUNYCODE] and
+ fail if there is an error. Save a copy of the result of this
+ step.
+
+ 6. Apply ToASCII.
+
+ 7. Verify that the result of step 6 matches the saved copy from step
+ 3, using a case-insensitive ASCII comparison.
+
+ 8. Return the saved copy from step 5.
+
+5. ACE prefix
+
+ The ACE prefix, used in the conversion operations (section 4), is two
+ alphanumeric ASCII characters followed by two hyphen-minuses. It
+ cannot be any of the prefixes already used in earlier documents,
+ which includes the following: "bl--", "bq--", "dq--", "lq--", "mq--",
+ "ra--", "wq--" and "zq--". The ToASCII and ToUnicode operations MUST
+ recognize the ACE prefix in a case-insensitive manner.
+
+ The ACE prefix for IDNA is "xn--" or any capitalization thereof.
+
+ This means that an ACE label might be "xn--de-jg4avhby1noc0d", where
+ "de-jg4avhby1noc0d" is the part of the ACE label that is generated by
+ the encoding steps in [PUNYCODE].
+
+ While all ACE labels begin with the ACE prefix, not all labels
+ beginning with the ACE prefix are necessarily ACE labels. Non-ACE
+ labels that begin with the ACE prefix will confuse users and SHOULD
+ NOT be allowed in DNS zones.
+
+
+
+
+
+
+
+
+
+
+
+Faltstrom, et al. Standards Track [Page 12]
+
+RFC 3490 IDNA March 2003
+
+
+6. Implications for typical applications using DNS
+
+ In IDNA, applications perform the processing needed to input
+ internationalized domain names from users, display internationalized
+ domain names to users, and process the inputs and outputs from DNS
+ and other protocols that carry domain names.
+
+ The components and interfaces between them can be represented
+ pictorially as:
+
+ +------+
+ | User |
+ +------+
+ ^
+ | Input and display: local interface methods
+ | (pen, keyboard, glowing phosphorus, ...)
+ +-------------------|-------------------------------+
+ | v |
+ | +-----------------------------+ |
+ | | Application | |
+ | | (ToASCII and ToUnicode | |
+ | | operations may be | |
+ | | called here) | |
+ | +-----------------------------+ |
+ | ^ ^ | End system
+ | | | |
+ | Call to resolver: | | Application-specific |
+ | ACE | | protocol: |
+ | v | ACE unless the |
+ | +----------+ | protocol is updated |
+ | | Resolver | | to handle other |
+ | +----------+ | encodings |
+ | ^ | |
+ +-----------------|----------|----------------------+
+ DNS protocol: | |
+ ACE | |
+ v v
+ +-------------+ +---------------------+
+ | DNS servers | | Application servers |
+ +-------------+ +---------------------+
+
+ The box labeled "Application" is where the application splits a
+ domain name into labels, sets the appropriate flags, and performs the
+ ToASCII and ToUnicode operations. This is described in section 4.
+
+
+
+
+
+
+
+Faltstrom, et al. Standards Track [Page 13]
+
+RFC 3490 IDNA March 2003
+
+
+6.1 Entry and display in applications
+
+ Applications can accept domain names using any character set or sets
+ desired by the application developer, and can display domain names in
+ any charset. That is, the IDNA protocol does not affect the
+ interface between users and applications.
+
+ An IDNA-aware application can accept and display internationalized
+ domain names in two formats: the internationalized character set(s)
+ supported by the application, and as an ACE label. ACE labels that
+ are displayed or input MUST always include the ACE prefix.
+ Applications MAY allow input and display of ACE labels, but are not
+ encouraged to do so except as an interface for special purposes,
+ possibly for debugging, or to cope with display limitations as
+ described in section 6.4.. ACE encoding is opaque and ugly, and
+ should thus only be exposed to users who absolutely need it. Because
+ name labels encoded as ACE name labels can be rendered either as the
+ encoded ASCII characters or the proper decoded characters, the
+ application MAY have an option for the user to select the preferred
+ method of display; if it does, rendering the ACE SHOULD NOT be the
+ default.
+
+ Domain names are often stored and transported in many places. For
+ example, they are part of documents such as mail messages and web
+ pages. They are transported in many parts of many protocols, such as
+ both the control commands and the RFC 2822 body parts of SMTP, and
+ the headers and the body content in HTTP. It is important to
+ remember that domain names appear both in domain name slots and in
+ the content that is passed over protocols.
+
+ In protocols and document formats that define how to handle
+ specification or negotiation of charsets, labels can be encoded in
+ any charset allowed by the protocol or document format. If a
+ protocol or document format only allows one charset, the labels MUST
+ be given in that charset.
+
+ In any place where a protocol or document format allows transmission
+ of the characters in internationalized labels, internationalized
+ labels SHOULD be transmitted using whatever character encoding and
+ escape mechanism that the protocol or document format uses at that
+ place.
+
+ All protocols that use domain name slots already have the capacity
+ for handling domain names in the ASCII charset. Thus, ACE labels
+ (internationalized labels that have been processed with the ToASCII
+ operation) can inherently be handled by those protocols.
+
+
+
+
+
+Faltstrom, et al. Standards Track [Page 14]
+
+RFC 3490 IDNA March 2003
+
+
+6.2 Applications and resolver libraries
+
+ Applications normally use functions in the operating system when they
+ resolve DNS queries. Those functions in the operating system are
+ often called "the resolver library", and the applications communicate
+ with the resolver libraries through a programming interface (API).
+
+ Because these resolver libraries today expect only domain names in
+ ASCII, applications MUST prepare labels that are passed to the
+ resolver library using the ToASCII operation. Labels received from
+ the resolver library contain only ASCII characters; internationalized
+ labels that cannot be represented directly in ASCII use the ACE form.
+ ACE labels always include the ACE prefix.
+
+ An operating system might have a set of libraries for performing the
+ ToASCII operation. The input to such a library might be in one or
+ more charsets that are used in applications (UTF-8 and UTF-16 are
+ likely candidates for almost any operating system, and script-
+ specific charsets are likely for localized operating systems).
+
+ IDNA-aware applications MUST be able to work with both non-
+ internationalized labels (those that conform to [STD13] and [STD3])
+ and internationalized labels.
+
+ It is expected that new versions of the resolver libraries in the
+ future will be able to accept domain names in other charsets than
+ ASCII, and application developers might one day pass not only domain
+ names in Unicode, but also in local script to a new API for the
+ resolver libraries in the operating system. Thus the ToASCII and
+ ToUnicode operations might be performed inside these new versions of
+ the resolver libraries.
+
+ Domain names passed to resolvers or put into the question section of
+ DNS requests follow the rules for "queries" from [STRINGPREP].
+
+6.3 DNS servers
+
+ Domain names stored in zones follow the rules for "stored strings"
+ from [STRINGPREP].
+
+ For internationalized labels that cannot be represented directly in
+ ASCII, DNS servers MUST use the ACE form produced by the ToASCII
+ operation. All IDNs served by DNS servers MUST contain only ASCII
+ characters.
+
+ If a signaling system which makes negotiation possible between old
+ and new DNS clients and servers is standardized in the future, the
+ encoding of the query in the DNS protocol itself can be changed from
+
+
+
+Faltstrom, et al. Standards Track [Page 15]
+
+RFC 3490 IDNA March 2003
+
+
+ ACE to something else, such as UTF-8. The question whether or not
+ this should be used is, however, a separate problem and is not
+ discussed in this memo.
+
+6.4 Avoiding exposing users to the raw ACE encoding
+
+ Any application that might show the user a domain name obtained from
+ a domain name slot, such as from gethostbyaddr or part of a mail
+ header, will need to be updated if it is to prevent users from seeing
+ the ACE.
+
+ If an application decodes an ACE name using ToUnicode but cannot show
+ all of the characters in the decoded name, such as if the name
+ contains characters that the output system cannot display, the
+ application SHOULD show the name in ACE format (which always includes
+ the ACE prefix) instead of displaying the name with the replacement
+ character (U+FFFD). This is to make it easier for the user to
+ transfer the name correctly to other programs. Programs that by
+ default show the ACE form when they cannot show all the characters in
+ a name label SHOULD also have a mechanism to show the name that is
+ produced by the ToUnicode operation with as many characters as
+ possible and replacement characters in the positions where characters
+ cannot be displayed.
+
+ The ToUnicode operation does not alter labels that are not valid ACE
+ labels, even if they begin with the ACE prefix. After ToUnicode has
+ been applied, if a label still begins with the ACE prefix, then it is
+ not a valid ACE label, and is not equivalent to any of the
+ intermediate Unicode strings constructed by ToUnicode.
+
+6.5 DNSSEC authentication of IDN domain names
+
+ DNS Security [RFC2535] is a method for supplying cryptographic
+ verification information along with DNS messages. Public Key
+ Cryptography is used in conjunction with digital signatures to
+ provide a means for a requester of domain information to authenticate
+ the source of the data. This ensures that it can be traced back to a
+ trusted source, either directly, or via a chain of trust linking the
+ source of the information to the top of the DNS hierarchy.
+
+ IDNA specifies that all internationalized domain names served by DNS
+ servers that cannot be represented directly in ASCII must use the ACE
+ form produced by the ToASCII operation. This operation must be
+ performed prior to a zone being signed by the private key for that
+ zone. Because of this ordering, it is important to recognize that
+ DNSSEC authenticates the ASCII domain name, not the Unicode form or
+
+
+
+
+
+Faltstrom, et al. Standards Track [Page 16]
+
+RFC 3490 IDNA March 2003
+
+
+ the mapping between the Unicode form and the ASCII form. In the
+ presence of DNSSEC, this is the name that MUST be signed in the zone
+ and MUST be validated against.
+
+ One consequence of this for sites deploying IDNA in the presence of
+ DNSSEC is that any special purpose proxies or forwarders used to
+ transform user input into IDNs must be earlier in the resolution flow
+ than DNSSEC authenticating nameservers for DNSSEC to work.
+
+7. Name server considerations
+
+ Existing DNS servers do not know the IDNA rules for handling non-
+ ASCII forms of IDNs, and therefore need to be shielded from them.
+ All existing channels through which names can enter a DNS server
+ database (for example, master files [STD13] and DNS update messages
+ [RFC2136]) are IDN-unaware because they predate IDNA, and therefore
+ requirement 2 of section 3.1 of this document provides the needed
+ shielding, by ensuring that internationalized domain names entering
+ DNS server databases through such channels have already been
+ converted to their equivalent ASCII forms.
+
+ It is imperative that there be only one ASCII encoding for a
+ particular domain name. Because of the design of the ToASCII and
+ ToUnicode operations, there are no ACE labels that decode to ASCII
+ labels, and therefore name servers cannot contain multiple ASCII
+ encodings of the same domain name.
+
+ [RFC2181] explicitly allows domain labels to contain octets beyond
+ the ASCII range (0..7F), and this document does not change that.
+ Note, however, that there is no defined interpretation of octets
+ 80..FF as characters. If labels containing these octets are returned
+ to applications, unpredictable behavior could result. The ASCII form
+ defined by ToASCII is the only standard representation for
+ internationalized labels in the current DNS protocol.
+
+8. Root server considerations
+
+ IDNs are likely to be somewhat longer than current domain names, so
+ the bandwidth needed by the root servers is likely to go up by a
+ small amount. Also, queries and responses for IDNs will probably be
+ somewhat longer than typical queries today, so more queries and
+ responses may be forced to go to TCP instead of UDP.
+
+
+
+
+
+
+
+
+
+Faltstrom, et al. Standards Track [Page 17]
+
+RFC 3490 IDNA March 2003
+
+
+9. References
+
+9.1 Normative References
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [STRINGPREP] Hoffman, P. and M. Blanchet, "Preparation of
+ Internationalized Strings ("stringprep")", RFC 3454,
+ December 2002.
+
+ [NAMEPREP] Hoffman, P. and M. Blanchet, "Nameprep: A Stringprep
+ Profile for Internationalized Domain Names (IDN)", RFC
+ 3491, March 2003.
+
+ [PUNYCODE] Costello, A., "Punycode: A Bootstring encoding of
+ Unicode for use with Internationalized Domain Names in
+ Applications (IDNA)", RFC 3492, March 2003.
+
+ [STD3] Braden, R., "Requirements for Internet Hosts --
+ Communication Layers", STD 3, RFC 1122, and
+ "Requirements for Internet Hosts -- Application and
+ Support", STD 3, RFC 1123, October 1989.
+
+ [STD13] Mockapetris, P., "Domain names - concepts and
+ facilities", STD 13, RFC 1034 and "Domain names -
+ implementation and specification", STD 13, RFC 1035,
+ November 1987.
+
+9.2 Informative References
+
+ [RFC2535] Eastlake, D., "Domain Name System Security Extensions",
+ RFC 2535, March 1999.
+
+ [RFC2181] Elz, R. and R. Bush, "Clarifications to the DNS
+ Specification", RFC 2181, July 1997.
+
+ [UAX9] Unicode Standard Annex #9, The Bidirectional Algorithm,
+ <http://www.unicode.org/unicode/reports/tr9/>.
+
+ [UNICODE] The Unicode Consortium. The Unicode Standard, Version
+ 3.2.0 is defined by The Unicode Standard, Version 3.0
+ (Reading, MA, Addison-Wesley, 2000. ISBN 0-201-61633-5),
+ as amended by the Unicode Standard Annex #27: Unicode
+ 3.1 (http://www.unicode.org/reports/tr27/) and by the
+ Unicode Standard Annex #28: Unicode 3.2
+ (http://www.unicode.org/reports/tr28/).
+
+
+
+
+Faltstrom, et al. Standards Track [Page 18]
+
+RFC 3490 IDNA March 2003
+
+
+ [USASCII] Cerf, V., "ASCII format for Network Interchange", RFC
+ 20, October 1969.
+
+10. Security Considerations
+
+ Security on the Internet partly relies on the DNS. Thus, any change
+ to the characteristics of the DNS can change the security of much of
+ the Internet.
+
+ This memo describes an algorithm which encodes characters that are
+ not valid according to STD3 and STD13 into octet values that are
+ valid. No security issues such as string length increases or new
+ allowed values are introduced by the encoding process or the use of
+ these encoded values, apart from those introduced by the ACE encoding
+ itself.
+
+ Domain names are used by users to identify and connect to Internet
+ servers. The security of the Internet is compromised if a user
+ entering a single internationalized name is connected to different
+ servers based on different interpretations of the internationalized
+ domain name.
+
+ When systems use local character sets other than ASCII and Unicode,
+ this specification leaves the the problem of transcoding between the
+ local character set and Unicode up to the application. If different
+ applications (or different versions of one application) implement
+ different transcoding rules, they could interpret the same name
+ differently and contact different servers. This problem is not
+ solved by security protocols like TLS that do not take local
+ character sets into account.
+
+ Because this document normatively refers to [NAMEPREP], [PUNYCODE],
+ and [STRINGPREP], it includes the security considerations from those
+ documents as well.
+
+ If or when this specification is updated to use a more recent Unicode
+ normalization table, the new normalization table will need to be
+ compared with the old to spot backwards incompatible changes. If
+ there are such changes, they will need to be handled somehow, or
+ there will be security as well as operational implications. Methods
+ to handle the conflicts could include keeping the old normalization,
+ or taking care of the conflicting characters by operational means, or
+ some other method.
+
+ Implementations MUST NOT use more recent normalization tables than
+ the one referenced from this document, even though more recent tables
+ may be provided by operating systems. If an application is unsure of
+ which version of the normalization tables are in the operating
+
+
+
+Faltstrom, et al. Standards Track [Page 19]
+
+RFC 3490 IDNA March 2003
+
+
+ system, the application needs to include the normalization tables
+ itself. Using normalization tables other than the one referenced
+ from this specification could have security and operational
+ implications.
+
+ To help prevent confusion between characters that are visually
+ similar, it is suggested that implementations provide visual
+ indications where a domain name contains multiple scripts. Such
+ mechanisms can also be used to show when a name contains a mixture of
+ simplified and traditional Chinese characters, or to distinguish zero
+ and one from O and l. DNS zone adminstrators may impose restrictions
+ (subject to the limitations in section 2) that try to minimize
+ homographs.
+
+ Domain names (or portions of them) are sometimes compared against a
+ set of privileged or anti-privileged domains. In such situations it
+ is especially important that the comparisons be done properly, as
+ specified in section 3.1 requirement 4. For labels already in ASCII
+ form, the proper comparison reduces to the same case-insensitive
+ ASCII comparison that has always been used for ASCII labels.
+
+ The introduction of IDNA means that any existing labels that start
+ with the ACE prefix and would be altered by ToUnicode will
+ automatically be ACE labels, and will be considered equivalent to
+ non-ASCII labels, whether or not that was the intent of the zone
+ adminstrator or registrant.
+
+11. IANA Considerations
+
+ IANA has assigned the ACE prefix in consultation with the IESG.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Faltstrom, et al. Standards Track [Page 20]
+
+RFC 3490 IDNA March 2003
+
+
+12. Authors' Addresses
+
+ Patrik Faltstrom
+ Cisco Systems
+ Arstaangsvagen 31 J
+ S-117 43 Stockholm Sweden
+
+ EMail: paf@cisco.com
+
+
+ Paul Hoffman
+ Internet Mail Consortium and VPN Consortium
+ 127 Segre Place
+ Santa Cruz, CA 95060 USA
+
+ EMail: phoffman@imc.org
+
+
+ Adam M. Costello
+ University of California, Berkeley
+
+ URL: http://www.nicemice.net/amc/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Faltstrom, et al. Standards Track [Page 21]
+
+RFC 3490 IDNA March 2003
+
+
+13. Full Copyright Statement
+
+ Copyright (C) The Internet Society (2003). All Rights Reserved.
+
+ This document and translations of it may be copied and furnished to
+ others, and derivative works that comment on or otherwise explain it
+ or assist in its implementation may be prepared, copied, published
+ and distributed, in whole or in part, without restriction of any
+ kind, provided that the above copyright notice and this paragraph are
+ included on all such copies and derivative works. However, this
+ document itself may not be modified in any way, such as by removing
+ the copyright notice or references to the Internet Society or other
+ Internet organizations, except as needed for the purpose of
+ developing Internet standards in which case the procedures for
+ copyrights defined in the Internet Standards process must be
+ followed, or as required to translate it into languages other than
+ English.
+
+ The limited permissions granted above are perpetual and will not be
+ revoked by the Internet Society or its successors or assigns.
+
+ This document and the information contained herein is provided on an
+ "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+ TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is currently provided by the
+ Internet Society.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Faltstrom, et al. Standards Track [Page 22]
+
diff --git a/source4/heimdal/lib/wind/rfc3491.txt b/source4/heimdal/lib/wind/rfc3491.txt
new file mode 100644
index 0000000000..dbc86c7fe4
--- /dev/null
+++ b/source4/heimdal/lib/wind/rfc3491.txt
@@ -0,0 +1,395 @@
+
+
+
+
+
+
+Network Working Group P. Hoffman
+Request for Comments: 3491 IMC & VPNC
+Category: Standards Track M. Blanchet
+ Viagenie
+ March 2003
+
+
+ Nameprep: A Stringprep Profile for
+ Internationalized Domain Names (IDN)
+
+Status of this Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2003). All Rights Reserved.
+
+Abstract
+
+ This document describes how to prepare internationalized domain name
+ (IDN) labels in order to increase the likelihood that name input and
+ name comparison work in ways that make sense for typical users
+ throughout the world. This profile of the stringprep protocol is
+ used as part of a suite of on-the-wire protocols for
+ internationalizing the Domain Name System (DNS).
+
+1. Introduction
+
+ This document specifies processing rules that will allow users to
+ enter internationalized domain names (IDNs) into applications and
+ have the highest chance of getting the content of the strings
+ correct. It is a profile of stringprep [STRINGPREP]. These
+ processing rules are only intended for internationalized domain
+ names, not for arbitrary text.
+
+ This profile defines the following, as required by [STRINGPREP].
+
+ - The intended applicability of the profile: internationalized
+ domain names processed by IDNA.
+
+ - The character repertoire that is the input and output to
+ stringprep: Unicode 3.2, specified in section 2.
+
+
+
+
+Hoffman & Blanchet Standards Track [Page 1]
+
+RFC 3491 IDN Nameprep March 2003
+
+
+ - The mappings used: specified in section 3.
+
+ - The Unicode normalization used: specified in section 4.
+
+ - The characters that are prohibited as output: specified in section
+ 5.
+
+ - Bidirectional character handling: specified in section 6.
+
+1.1 Interaction of protocol parts
+
+ Nameprep is used by the IDNA [IDNA] protocol for preparing domain
+ names; it is not designed for any other purpose. It is explicitly
+ not designed for processing arbitrary free text and SHOULD NOT be
+ used for that purpose. Nameprep is a profile of Stringprep
+ [STRINGPREP]. Implementations of Nameprep MUST fully implement
+ Stringprep.
+
+ Nameprep is used to process domain name labels, not domain names.
+ IDNA calls nameprep for each label in a domain name, not for the
+ whole domain name.
+
+1.2 Terminology
+
+ The key words "MUST", "MUST NOT", "SHOULD", "SHOULD NOT", and "MAY"
+ in this document are to be interpreted as described in BCP 14, RFC
+ 2119 [RFC2119].
+
+2. Character Repertoire
+
+ This profile uses Unicode 3.2, as defined in [STRINGPREP] Appendix A.
+
+3. Mapping
+
+ This profile specifies mapping using the following tables from
+ [STRINGPREP]:
+
+ Table B.1
+ Table B.2
+
+4. Normalization
+
+ This profile specifies using Unicode normalization form KC, as
+ described in [STRINGPREP].
+
+
+
+
+
+
+
+Hoffman & Blanchet Standards Track [Page 2]
+
+RFC 3491 IDN Nameprep March 2003
+
+
+5. Prohibited Output
+
+ This profile specifies prohibiting using the following tables from
+ [STRINGPREP]:
+
+ Table C.1.2
+ Table C.2.2
+ Table C.3
+ Table C.4
+ Table C.5
+ Table C.6
+ Table C.7
+ Table C.8
+ Table C.9
+
+ IMPORTANT NOTE: This profile MUST be used with the IDNA protocol.
+ The IDNA protocol has additional prohibitions that are checked
+ outside of this profile.
+
+6. Bidirectional characters
+
+ This profile specifies checking bidirectional strings as described in
+ [STRINGPREP] section 6.
+
+7. Unassigned Code Points in Internationalized Domain Names
+
+ If the processing in [IDNA] specifies that a list of unassigned code
+ points be used, the system uses table A.1 from [STRINGPREP] as its
+ list of unassigned code points.
+
+8. References
+
+8.1 Normative References
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [STRINGPREP] Hoffman, P. and M. Blanchet, "Preparation of
+ Internationalized Strings ("stringprep")", RFC 3454,
+ December 2002.
+
+ [IDNA] Faltstrom, P., Hoffman, P. and A. Costello,
+ "Internationalizing Domain Names in Applications
+ (IDNA)", RFC 3490, March 2003.
+
+
+
+
+
+
+
+Hoffman & Blanchet Standards Track [Page 3]
+
+RFC 3491 IDN Nameprep March 2003
+
+
+8.2 Informative references
+
+ [STD13] Mockapetris, P., "Domain names - concepts and
+ facilities", STD 13, RFC 1034, and "Domain names -
+ implementation and specification", STD 13, RFC 1035,
+ November 1987.
+
+9. Security Considerations
+
+ The Unicode and ISO/IEC 10646 repertoires have many characters that
+ look similar. In many cases, users of security protocols might do
+ visual matching, such as when comparing the names of trusted third
+ parties. Because it is impossible to map similar-looking characters
+ without a great deal of context such as knowing the fonts used,
+ stringprep does nothing to map similar-looking characters together
+ nor to prohibit some characters because they look like others.
+
+ Security on the Internet partly relies on the DNS. Thus, any change
+ to the characteristics of the DNS can change the security of much of
+ the Internet.
+
+ Domain names are used by users to connect to Internet servers. The
+ security of the Internet would be compromised if a user entering a
+ single internationalized name could be connected to different servers
+ based on different interpretations of the internationalized domain
+ name.
+
+ Current applications might assume that the characters allowed in
+ domain names will always be the same as they are in [STD13]. This
+ document vastly increases the number of characters available in
+ domain names. Every program that uses "special" characters in
+ conjunction with domain names may be vulnerable to attack based on
+ the new characters allowed by this specification.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Hoffman & Blanchet Standards Track [Page 4]
+
+RFC 3491 IDN Nameprep March 2003
+
+
+10. IANA Considerations
+
+ This is a profile of stringprep. It has been registered by the IANA
+ in the stringprep profile registry
+ (www.iana.org/assignments/stringprep-profiles).
+
+ Name of this profile:
+ Nameprep
+
+ RFC in which the profile is defined:
+ This document.
+
+ Indicator whether or not this is the newest version of the
+ profile:
+ This is the first version of Nameprep.
+
+11. Acknowledgements
+
+ Many people from the IETF IDN Working Group and the Unicode Technical
+ Committee contributed ideas that went into this document.
+
+ The IDN Nameprep design team made many useful changes to the
+ document. That team and its advisors include:
+
+ Asmus Freytag
+ Cathy Wissink
+ Francois Yergeau
+ James Seng
+ Marc Blanchet
+ Mark Davis
+ Martin Duerst
+ Patrik Faltstrom
+ Paul Hoffman
+
+ Additional significant improvements were proposed by:
+
+ Jonathan Rosenne
+ Kent Karlsson
+ Scott Hollenbeck
+ Dave Crocker
+ Erik Nordmark
+ Matitiahu Allouche
+
+
+
+
+
+
+
+
+
+Hoffman & Blanchet Standards Track [Page 5]
+
+RFC 3491 IDN Nameprep March 2003
+
+
+12. Authors' Addresses
+
+ Paul Hoffman
+ Internet Mail Consortium and VPN Consortium
+ 127 Segre Place
+ Santa Cruz, CA 95060 USA
+
+ EMail: paul.hoffman@imc.org and paul.hoffman@vpnc.org
+
+
+ Marc Blanchet
+ Viagenie inc.
+ 2875 boul. Laurier, bur. 300
+ Ste-Foy, Quebec, Canada, G1V 2M2
+
+ EMail: Marc.Blanchet@viagenie.qc.ca
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Hoffman & Blanchet Standards Track [Page 6]
+
+RFC 3491 IDN Nameprep March 2003
+
+
+13. Full Copyright Statement
+
+ Copyright (C) The Internet Society (2003). All Rights Reserved.
+
+ This document and translations of it may be copied and furnished to
+ others, and derivative works that comment on or otherwise explain it
+ or assist in its implementation may be prepared, copied, published
+ and distributed, in whole or in part, without restriction of any
+ kind, provided that the above copyright notice and this paragraph are
+ included on all such copies and derivative works. However, this
+ document itself may not be modified in any way, such as by removing
+ the copyright notice or references to the Internet Society or other
+ Internet organizations, except as needed for the purpose of
+ developing Internet standards in which case the procedures for
+ copyrights defined in the Internet Standards process must be
+ followed, or as required to translate it into languages other than
+ English.
+
+ The limited permissions granted above are perpetual and will not be
+ revoked by the Internet Society or its successors or assigns.
+
+ This document and the information contained herein is provided on an
+ "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+ TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is currently provided by the
+ Internet Society.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Hoffman & Blanchet Standards Track [Page 7]
+
diff --git a/source4/heimdal/lib/wind/rfc3492.txt b/source4/heimdal/lib/wind/rfc3492.txt
new file mode 100644
index 0000000000..e72ad81a27
--- /dev/null
+++ b/source4/heimdal/lib/wind/rfc3492.txt
@@ -0,0 +1,1963 @@
+
+
+
+
+
+
+Network Working Group A. Costello
+Request for Comments: 3492 Univ. of California, Berkeley
+Category: Standards Track March 2003
+
+
+ Punycode: A Bootstring encoding of Unicode
+ for Internationalized Domain Names in Applications (IDNA)
+
+Status of this Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2003). All Rights Reserved.
+
+Abstract
+
+ Punycode is a simple and efficient transfer encoding syntax designed
+ for use with Internationalized Domain Names in Applications (IDNA).
+ It uniquely and reversibly transforms a Unicode string into an ASCII
+ string. ASCII characters in the Unicode string are represented
+ literally, and non-ASCII characters are represented by ASCII
+ characters that are allowed in host name labels (letters, digits, and
+ hyphens). This document defines a general algorithm called
+ Bootstring that allows a string of basic code points to uniquely
+ represent any string of code points drawn from a larger set.
+ Punycode is an instance of Bootstring that uses particular parameter
+ values specified by this document, appropriate for IDNA.
+
+Table of Contents
+
+ 1. Introduction...............................................2
+ 1.1 Features..............................................2
+ 1.2 Interaction of protocol parts.........................3
+ 2. Terminology................................................3
+ 3. Bootstring description.....................................4
+ 3.1 Basic code point segregation..........................4
+ 3.2 Insertion unsort coding...............................4
+ 3.3 Generalized variable-length integers..................5
+ 3.4 Bias adaptation.......................................7
+ 4. Bootstring parameters......................................8
+ 5. Parameter values for Punycode..............................8
+ 6. Bootstring algorithms......................................9
+
+
+
+Costello Standards Track [Page 1]
+
+RFC 3492 IDNA Punycode March 2003
+
+
+ 6.1 Bias adaptation function.............................10
+ 6.2 Decoding procedure...................................11
+ 6.3 Encoding procedure...................................12
+ 6.4 Overflow handling....................................13
+ 7. Punycode examples.........................................14
+ 7.1 Sample strings.......................................14
+ 7.2 Decoding traces......................................17
+ 7.3 Encoding traces......................................19
+ 8. Security Considerations...................................20
+ 9. References................................................21
+ 9.1 Normative References.................................21
+ 9.2 Informative References...............................21
+ A. Mixed-case annotation.....................................22
+ B. Disclaimer and license....................................22
+ C. Punycode sample implementation............................23
+ Author's Address.............................................34
+ Full Copyright Statement.....................................35
+
+1. Introduction
+
+ [IDNA] describes an architecture for supporting internationalized
+ domain names. Labels containing non-ASCII characters can be
+ represented by ACE labels, which begin with a special ACE prefix and
+ contain only ASCII characters. The remainder of the label after the
+ prefix is a Punycode encoding of a Unicode string satisfying certain
+ constraints. For the details of the prefix and constraints, see
+ [IDNA] and [NAMEPREP].
+
+ Punycode is an instance of a more general algorithm called
+ Bootstring, which allows strings composed from a small set of "basic"
+ code points to uniquely represent any string of code points drawn
+ from a larger set. Punycode is Bootstring with particular parameter
+ values appropriate for IDNA.
+
+1.1 Features
+
+ Bootstring has been designed to have the following features:
+
+ * Completeness: Every extended string (sequence of arbitrary code
+ points) can be represented by a basic string (sequence of basic
+ code points). Restrictions on what strings are allowed, and on
+ length, can be imposed by higher layers.
+
+ * Uniqueness: There is at most one basic string that represents a
+ given extended string.
+
+ * Reversibility: Any extended string mapped to a basic string can
+ be recovered from that basic string.
+
+
+
+Costello Standards Track [Page 2]
+
+RFC 3492 IDNA Punycode March 2003
+
+
+ * Efficient encoding: The ratio of basic string length to extended
+ string length is small. This is important in the context of
+ domain names because RFC 1034 [RFC1034] restricts the length of a
+ domain label to 63 characters.
+
+ * Simplicity: The encoding and decoding algorithms are reasonably
+ simple to implement. The goals of efficiency and simplicity are
+ at odds; Bootstring aims at a good balance between them.
+
+ * Readability: Basic code points appearing in the extended string
+ are represented as themselves in the basic string (although the
+ main purpose is to improve efficiency, not readability).
+
+ Punycode can also support an additional feature that is not used by
+ the ToASCII and ToUnicode operations of [IDNA]. When extended
+ strings are case-folded prior to encoding, the basic string can use
+ mixed case to tell how to convert the folded string into a mixed-case
+ string. See appendix A "Mixed-case annotation".
+
+1.2 Interaction of protocol parts
+
+ Punycode is used by the IDNA protocol [IDNA] for converting domain
+ labels into ASCII; it is not designed for any other purpose. It is
+ explicitly not designed for processing arbitrary free text.
+
+2. Terminology
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in BCP 14, RFC 2119
+ [RFC2119].
+
+ A code point is an integral value associated with a character in a
+ coded character set.
+
+ As in the Unicode Standard [UNICODE], Unicode code points are denoted
+ by "U+" followed by four to six hexadecimal digits, while a range of
+ code points is denoted by two hexadecimal numbers separated by "..",
+ with no prefixes.
+
+ The operators div and mod perform integer division; (x div y) is the
+ quotient of x divided by y, discarding the remainder, and (x mod y)
+ is the remainder, so (x div y) * y + (x mod y) == x. Bootstring uses
+ these operators only with nonnegative operands, so the quotient and
+ remainder are always nonnegative.
+
+ The break statement jumps out of the innermost loop (as in C).
+
+
+
+
+Costello Standards Track [Page 3]
+
+RFC 3492 IDNA Punycode March 2003
+
+
+ An overflow is an attempt to compute a value that exceeds the maximum
+ value of an integer variable.
+
+3. Bootstring description
+
+ Bootstring represents an arbitrary sequence of code points (the
+ "extended string") as a sequence of basic code points (the "basic
+ string"). This section describes the representation. Section 6
+ "Bootstring algorithms" presents the algorithms as pseudocode.
+ Sections 7.1 "Decoding traces" and 7.2 "Encoding traces" trace the
+ algorithms for sample inputs.
+
+ The following sections describe the four techniques used in
+ Bootstring. "Basic code point segregation" is a very simple and
+ efficient encoding for basic code points occurring in the extended
+ string: they are simply copied all at once. "Insertion unsort
+ coding" encodes the non-basic code points as deltas, and processes
+ the code points in numerical order rather than in order of
+ appearance, which typically results in smaller deltas. The deltas
+ are represented as "generalized variable-length integers", which use
+ basic code points to represent nonnegative integers. The parameters
+ of this integer representation are dynamically adjusted using "bias
+ adaptation", to improve efficiency when consecutive deltas have
+ similar magnitudes.
+
+3.1 Basic code point segregation
+
+ All basic code points appearing in the extended string are
+ represented literally at the beginning of the basic string, in their
+ original order, followed by a delimiter if (and only if) the number
+ of basic code points is nonzero. The delimiter is a particular basic
+ code point, which never appears in the remainder of the basic string.
+ The decoder can therefore find the end of the literal portion (if
+ there is one) by scanning for the last delimiter.
+
+3.2 Insertion unsort coding
+
+ The remainder of the basic string (after the last delimiter if there
+ is one) represents a sequence of nonnegative integral deltas as
+ generalized variable-length integers, described in section 3.3. The
+ meaning of the deltas is best understood in terms of the decoder.
+
+ The decoder builds the extended string incrementally. Initially, the
+ extended string is a copy of the literal portion of the basic string
+ (excluding the last delimiter). The decoder inserts non-basic code
+ points, one for each delta, into the extended string, ultimately
+ arriving at the final decoded string.
+
+
+
+
+Costello Standards Track [Page 4]
+
+RFC 3492 IDNA Punycode March 2003
+
+
+ At the heart of this process is a state machine with two state
+ variables: an index i and a counter n. The index i refers to a
+ position in the extended string; it ranges from 0 (the first
+ position) to the current length of the extended string (which refers
+ to a potential position beyond the current end). If the current
+ state is <n,i>, the next state is <n,i+1> if i is less than the
+ length of the extended string, or <n+1,0> if i equals the length of
+ the extended string. In other words, each state change causes i to
+ increment, wrapping around to zero if necessary, and n counts the
+ number of wrap-arounds.
+
+ Notice that the state always advances monotonically (there is no way
+ for the decoder to return to an earlier state). At each state, an
+ insertion is either performed or not performed. At most one
+ insertion is performed in a given state. An insertion inserts the
+ value of n at position i in the extended string. The deltas are a
+ run-length encoding of this sequence of events: they are the lengths
+ of the runs of non-insertion states preceeding the insertion states.
+ Hence, for each delta, the decoder performs delta state changes, then
+ an insertion, and then one more state change. (An implementation
+ need not perform each state change individually, but can instead use
+ division and remainder calculations to compute the next insertion
+ state directly.) It is an error if the inserted code point is a
+ basic code point (because basic code points were supposed to be
+ segregated as described in section 3.1).
+
+ The encoder's main task is to derive the sequence of deltas that will
+ cause the decoder to construct the desired string. It can do this by
+ repeatedly scanning the extended string for the next code point that
+ the decoder would need to insert, and counting the number of state
+ changes the decoder would need to perform, mindful of the fact that
+ the decoder's extended string will include only those code points
+ that have already been inserted. Section 6.3 "Encoding procedure"
+ gives a precise algorithm.
+
+3.3 Generalized variable-length integers
+
+ In a conventional integer representation the base is the number of
+ distinct symbols for digits, whose values are 0 through base-1. Let
+ digit_0 denote the least significant digit, digit_1 the next least
+ significant, and so on. The value represented is the sum over j of
+ digit_j * w(j), where w(j) = base^j is the weight (scale factor) for
+ position j. For example, in the base 8 integer 437, the digits are
+ 7, 3, and 4, and the weights are 1, 8, and 64, so the value is 7 +
+ 3*8 + 4*64 = 287. This representation has two disadvantages: First,
+ there are multiple encodings of each value (because there can be
+ extra zeros in the most significant positions), which is inconvenient
+
+
+
+
+Costello Standards Track [Page 5]
+
+RFC 3492 IDNA Punycode March 2003
+
+
+ when unique encodings are needed. Second, the integer is not self-
+ delimiting, so if multiple integers are concatenated the boundaries
+ between them are lost.
+
+ The generalized variable-length representation solves these two
+ problems. The digit values are still 0 through base-1, but now the
+ integer is self-delimiting by means of thresholds t(j), each of which
+ is in the range 0 through base-1. Exactly one digit, the most
+ significant, satisfies digit_j < t(j). Therefore, if several
+ integers are concatenated, it is easy to separate them, starting with
+ the first if they are little-endian (least significant digit first),
+ or starting with the last if they are big-endian (most significant
+ digit first). As before, the value is the sum over j of digit_j *
+ w(j), but the weights are different:
+
+ w(0) = 1
+ w(j) = w(j-1) * (base - t(j-1)) for j > 0
+
+ For example, consider the little-endian sequence of base 8 digits
+ 734251... Suppose the thresholds are 2, 3, 5, 5, 5, 5... This
+ implies that the weights are 1, 1*(8-2) = 6, 6*(8-3) = 30, 30*(8-5) =
+ 90, 90*(8-5) = 270, and so on. 7 is not less than 2, and 3 is not
+ less than 3, but 4 is less than 5, so 4 is the last digit. The value
+ of 734 is 7*1 + 3*6 + 4*30 = 145. The next integer is 251, with
+ value 2*1 + 5*6 + 1*30 = 62. Decoding this representation is very
+ similar to decoding a conventional integer: Start with a current
+ value of N = 0 and a weight w = 1. Fetch the next digit d and
+ increase N by d * w. If d is less than the current threshold (t)
+ then stop, otherwise increase w by a factor of (base - t), update t
+ for the next position, and repeat.
+
+ Encoding this representation is similar to encoding a conventional
+ integer: If N < t then output one digit for N and stop, otherwise
+ output the digit for t + ((N - t) mod (base - t)), then replace N
+ with (N - t) div (base - t), update t for the next position, and
+ repeat.
+
+ For any particular set of values of t(j), there is exactly one
+ generalized variable-length representation of each nonnegative
+ integral value.
+
+ Bootstring uses little-endian ordering so that the deltas can be
+ separated starting with the first. The t(j) values are defined in
+ terms of the constants base, tmin, and tmax, and a state variable
+ called bias:
+
+ t(j) = base * (j + 1) - bias,
+ clamped to the range tmin through tmax
+
+
+
+Costello Standards Track [Page 6]
+
+RFC 3492 IDNA Punycode March 2003
+
+
+ The clamping means that if the formula yields a value less than tmin
+ or greater than tmax, then t(j) = tmin or tmax, respectively. (In
+ the pseudocode in section 6 "Bootstring algorithms", the expression
+ base * (j + 1) is denoted by k for performance reasons.) These t(j)
+ values cause the representation to favor integers within a particular
+ range determined by the bias.
+
+3.4 Bias adaptation
+
+ After each delta is encoded or decoded, bias is set for the next
+ delta as follows:
+
+ 1. Delta is scaled in order to avoid overflow in the next step:
+
+ let delta = delta div 2
+
+ But when this is the very first delta, the divisor is not 2, but
+ instead a constant called damp. This compensates for the fact
+ that the second delta is usually much smaller than the first.
+
+ 2. Delta is increased to compensate for the fact that the next delta
+ will be inserting into a longer string:
+
+ let delta = delta + (delta div numpoints)
+
+ numpoints is the total number of code points encoded/decoded so
+ far (including the one corresponding to this delta itself, and
+ including the basic code points).
+
+ 3. Delta is repeatedly divided until it falls within a threshold, to
+ predict the minimum number of digits needed to represent the next
+ delta:
+
+ while delta > ((base - tmin) * tmax) div 2
+ do let delta = delta div (base - tmin)
+
+ 4. The bias is set:
+
+ let bias =
+ (base * the number of divisions performed in step 3) +
+ (((base - tmin + 1) * delta) div (delta + skew))
+
+ The motivation for this procedure is that the current delta
+ provides a hint about the likely size of the next delta, and so
+ t(j) is set to tmax for the more significant digits starting with
+ the one expected to be last, tmin for the less significant digits
+ up through the one expected to be third-last, and somewhere
+ between tmin and tmax for the digit expected to be second-last
+
+
+
+Costello Standards Track [Page 7]
+
+RFC 3492 IDNA Punycode March 2003
+
+
+ (balancing the hope of the expected-last digit being unnecessary
+ against the danger of it being insufficient).
+
+4. Bootstring parameters
+
+ Given a set of basic code points, one needs to be designated as the
+ delimiter. The base cannot be greater than the number of
+ distinguishable basic code points remaining. The digit-values in the
+ range 0 through base-1 need to be associated with distinct non-
+ delimiter basic code points. In some cases multiple code points need
+ to have the same digit-value; for example, uppercase and lowercase
+ versions of the same letter need to be equivalent if basic strings
+ are case-insensitive.
+
+ The initial value of n cannot be greater than the minimum non-basic
+ code point that could appear in extended strings.
+
+ The remaining five parameters (tmin, tmax, skew, damp, and the
+ initial value of bias) need to satisfy the following constraints:
+
+ 0 <= tmin <= tmax <= base-1
+ skew >= 1
+ damp >= 2
+ initial_bias mod base <= base - tmin
+
+ Provided the constraints are satisfied, these five parameters affect
+ efficiency but not correctness. They are best chosen empirically.
+
+ If support for mixed-case annotation is desired (see appendix A),
+ make sure that the code points corresponding to 0 through tmax-1 all
+ have both uppercase and lowercase forms.
+
+5. Parameter values for Punycode
+
+ Punycode uses the following Bootstring parameter values:
+
+ base = 36
+ tmin = 1
+ tmax = 26
+ skew = 38
+ damp = 700
+ initial_bias = 72
+ initial_n = 128 = 0x80
+
+ Although the only restriction Punycode imposes on the input integers
+ is that they be nonnegative, these parameters are especially designed
+ to work well with Unicode [UNICODE] code points, which are integers
+ in the range 0..10FFFF (but not D800..DFFF, which are reserved for
+
+
+
+Costello Standards Track [Page 8]
+
+RFC 3492 IDNA Punycode March 2003
+
+
+ use by the UTF-16 encoding of Unicode). The basic code points are
+ the ASCII [ASCII] code points (0..7F), of which U+002D (-) is the
+ delimiter, and some of the others have digit-values as follows:
+
+ code points digit-values
+ ------------ ----------------------
+ 41..5A (A-Z) = 0 to 25, respectively
+ 61..7A (a-z) = 0 to 25, respectively
+ 30..39 (0-9) = 26 to 35, respectively
+
+ Using hyphen-minus as the delimiter implies that the encoded string
+ can end with a hyphen-minus only if the Unicode string consists
+ entirely of basic code points, but IDNA forbids such strings from
+ being encoded. The encoded string can begin with a hyphen-minus, but
+ IDNA prepends a prefix. Therefore IDNA using Punycode conforms to
+ the RFC 952 rule that host name labels neither begin nor end with a
+ hyphen-minus [RFC952].
+
+ A decoder MUST recognize the letters in both uppercase and lowercase
+ forms (including mixtures of both forms). An encoder SHOULD output
+ only uppercase forms or only lowercase forms, unless it uses mixed-
+ case annotation (see appendix A).
+
+ Presumably most users will not manually write or type encoded strings
+ (as opposed to cutting and pasting them), but those who do will need
+ to be alert to the potential visual ambiguity between the following
+ sets of characters:
+
+ G 6
+ I l 1
+ O 0
+ S 5
+ U V
+ Z 2
+
+ Such ambiguities are usually resolved by context, but in a Punycode
+ encoded string there is no context apparent to humans.
+
+6. Bootstring algorithms
+
+ Some parts of the pseudocode can be omitted if the parameters satisfy
+ certain conditions (for which Punycode qualifies). These parts are
+ enclosed in {braces}, and notes immediately following the pseudocode
+ explain the conditions under which they can be omitted.
+
+
+
+
+
+
+
+Costello Standards Track [Page 9]
+
+RFC 3492 IDNA Punycode March 2003
+
+
+ Formally, code points are integers, and hence the pseudocode assumes
+ that arithmetic operations can be performed directly on code points.
+ In some programming languages, explicit conversion between code
+ points and integers might be necessary.
+
+6.1 Bias adaptation function
+
+ function adapt(delta,numpoints,firsttime):
+ if firsttime then let delta = delta div damp
+ else let delta = delta div 2
+ let delta = delta + (delta div numpoints)
+ let k = 0
+ while delta > ((base - tmin) * tmax) div 2 do begin
+ let delta = delta div (base - tmin)
+ let k = k + base
+ end
+ return k + (((base - tmin + 1) * delta) div (delta + skew))
+
+ It does not matter whether the modifications to delta and k inside
+ adapt() affect variables of the same name inside the
+ encoding/decoding procedures, because after calling adapt() the
+ caller does not read those variables before overwriting them.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Costello Standards Track [Page 10]
+
+RFC 3492 IDNA Punycode March 2003
+
+
+6.2 Decoding procedure
+
+ let n = initial_n
+ let i = 0
+ let bias = initial_bias
+ let output = an empty string indexed from 0
+ consume all code points before the last delimiter (if there is one)
+ and copy them to output, fail on any non-basic code point
+ if more than zero code points were consumed then consume one more
+ (which will be the last delimiter)
+ while the input is not exhausted do begin
+ let oldi = i
+ let w = 1
+ for k = base to infinity in steps of base do begin
+ consume a code point, or fail if there was none to consume
+ let digit = the code point's digit-value, fail if it has none
+ let i = i + digit * w, fail on overflow
+ let t = tmin if k <= bias {+ tmin}, or
+ tmax if k >= bias + tmax, or k - bias otherwise
+ if digit < t then break
+ let w = w * (base - t), fail on overflow
+ end
+ let bias = adapt(i - oldi, length(output) + 1, test oldi is 0?)
+ let n = n + i div (length(output) + 1), fail on overflow
+ let i = i mod (length(output) + 1)
+ {if n is a basic code point then fail}
+ insert n into output at position i
+ increment i
+ end
+
+ The full statement enclosed in braces (checking whether n is a basic
+ code point) can be omitted if initial_n exceeds all basic code points
+ (which is true for Punycode), because n is never less than initial_n.
+
+ In the assignment of t, where t is clamped to the range tmin through
+ tmax, "+ tmin" can always be omitted. This makes the clamping
+ calculation incorrect when bias < k < bias + tmin, but that cannot
+ happen because of the way bias is computed and because of the
+ constraints on the parameters.
+
+ Because the decoder state can only advance monotonically, and there
+ is only one representation of any delta, there is therefore only one
+ encoded string that can represent a given sequence of integers. The
+ only error conditions are invalid code points, unexpected end-of-
+ input, overflow, and basic code points encoded using deltas instead
+ of appearing literally. If the decoder fails on these errors as
+ shown above, then it cannot produce the same output for two distinct
+ inputs. Without this property it would have been necessary to re-
+
+
+
+Costello Standards Track [Page 11]
+
+RFC 3492 IDNA Punycode March 2003
+
+
+ encode the output and verify that it matches the input in order to
+ guarantee the uniqueness of the encoding.
+
+6.3 Encoding procedure
+
+ let n = initial_n
+ let delta = 0
+ let bias = initial_bias
+ let h = b = the number of basic code points in the input
+ copy them to the output in order, followed by a delimiter if b > 0
+ {if the input contains a non-basic code point < n then fail}
+ while h < length(input) do begin
+ let m = the minimum {non-basic} code point >= n in the input
+ let delta = delta + (m - n) * (h + 1), fail on overflow
+ let n = m
+ for each code point c in the input (in order) do begin
+ if c < n {or c is basic} then increment delta, fail on overflow
+ if c == n then begin
+ let q = delta
+ for k = base to infinity in steps of base do begin
+ let t = tmin if k <= bias {+ tmin}, or
+ tmax if k >= bias + tmax, or k - bias otherwise
+ if q < t then break
+ output the code point for digit t + ((q - t) mod (base - t))
+ let q = (q - t) div (base - t)
+ end
+ output the code point for digit q
+ let bias = adapt(delta, h + 1, test h equals b?)
+ let delta = 0
+ increment h
+ end
+ end
+ increment delta and n
+ end
+
+ The full statement enclosed in braces (checking whether the input
+ contains a non-basic code point less than n) can be omitted if all
+ code points less than initial_n are basic code points (which is true
+ for Punycode if code points are unsigned).
+
+ The brace-enclosed conditions "non-basic" and "or c is basic" can be
+ omitted if initial_n exceeds all basic code points (which is true for
+ Punycode), because the code point being tested is never less than
+ initial_n.
+
+ In the assignment of t, where t is clamped to the range tmin through
+ tmax, "+ tmin" can always be omitted. This makes the clamping
+ calculation incorrect when bias < k < bias + tmin, but that cannot
+
+
+
+Costello Standards Track [Page 12]
+
+RFC 3492 IDNA Punycode March 2003
+
+
+ happen because of the way bias is computed and because of the
+ constraints on the parameters.
+
+ The checks for overflow are necessary to avoid producing invalid
+ output when the input contains very large values or is very long.
+
+ The increment of delta at the bottom of the outer loop cannot
+ overflow because delta < length(input) before the increment, and
+ length(input) is already assumed to be representable. The increment
+ of n could overflow, but only if h == length(input), in which case
+ the procedure is finished anyway.
+
+6.4 Overflow handling
+
+ For IDNA, 26-bit unsigned integers are sufficient to handle all valid
+ IDNA labels without overflow, because any string that needed a 27-bit
+ delta would have to exceed either the code point limit (0..10FFFF) or
+ the label length limit (63 characters). However, overflow handling
+ is necessary because the inputs are not necessarily valid IDNA
+ labels.
+
+ If the programming language does not provide overflow detection, the
+ following technique can be used. Suppose A, B, and C are
+ representable nonnegative integers and C is nonzero. Then A + B
+ overflows if and only if B > maxint - A, and A + (B * C) overflows if
+ and only if B > (maxint - A) div C, where maxint is the greatest
+ integer for which maxint + 1 cannot be represented. Refer to
+ appendix C "Punycode sample implementation" for demonstrations of
+ this technique in the C language.
+
+ The decoding and encoding algorithms shown in sections 6.2 and 6.3
+ handle overflow by detecting it whenever it happens. Another
+ approach is to enforce limits on the inputs that prevent overflow
+ from happening. For example, if the encoder were to verify that no
+ input code points exceed M and that the input length does not exceed
+ L, then no delta could ever exceed (M - initial_n) * (L + 1), and
+ hence no overflow could occur if integer variables were capable of
+ representing values that large. This prevention approach would
+ impose more restrictions on the input than the detection approach
+ does, but might be considered simpler in some programming languages.
+
+ In theory, the decoder could use an analogous approach, limiting the
+ number of digits in a variable-length integer (that is, limiting the
+ number of iterations in the innermost loop). However, the number of
+ digits that suffice to represent a given delta can sometimes
+ represent much larger deltas (because of the adaptation), and hence
+ this approach would probably need integers wider than 32 bits.
+
+
+
+
+Costello Standards Track [Page 13]
+
+RFC 3492 IDNA Punycode March 2003
+
+
+ Yet another approach for the decoder is to allow overflow to occur,
+ but to check the final output string by re-encoding it and comparing
+ to the decoder input. If and only if they do not match (using a
+ case-insensitive ASCII comparison) overflow has occurred. This
+ delayed-detection approach would not impose any more restrictions on
+ the input than the immediate-detection approach does, and might be
+ considered simpler in some programming languages.
+
+ In fact, if the decoder is used only inside the IDNA ToUnicode
+ operation [IDNA], then it need not check for overflow at all, because
+ ToUnicode performs a higher level re-encoding and comparison, and a
+ mismatch has the same consequence as if the Punycode decoder had
+ failed.
+
+7. Punycode examples
+
+7.1 Sample strings
+
+ In the Punycode encodings below, the ACE prefix is not shown.
+ Backslashes show where line breaks have been inserted in strings too
+ long for one line.
+
+ The first several examples are all translations of the sentence "Why
+ can't they just speak in <language>?" (courtesy of Michael Kaplan's
+ "provincial" page [PROVINCIAL]). Word breaks and punctuation have
+ been removed, as is often done in domain names.
+
+ (A) Arabic (Egyptian):
+ u+0644 u+064A u+0647 u+0645 u+0627 u+0628 u+062A u+0643 u+0644
+ u+0645 u+0648 u+0634 u+0639 u+0631 u+0628 u+064A u+061F
+ Punycode: egbpdaj6bu4bxfgehfvwxn
+
+ (B) Chinese (simplified):
+ u+4ED6 u+4EEC u+4E3A u+4EC0 u+4E48 u+4E0D u+8BF4 u+4E2D u+6587
+ Punycode: ihqwcrb4cv8a8dqg056pqjye
+
+ (C) Chinese (traditional):
+ u+4ED6 u+5011 u+7232 u+4EC0 u+9EBD u+4E0D u+8AAA u+4E2D u+6587
+ Punycode: ihqwctvzc91f659drss3x8bo0yb
+
+ (D) Czech: Pro<ccaron>prost<ecaron>nemluv<iacute><ccaron>esky
+ U+0050 u+0072 u+006F u+010D u+0070 u+0072 u+006F u+0073 u+0074
+ u+011B u+006E u+0065 u+006D u+006C u+0075 u+0076 u+00ED u+010D
+ u+0065 u+0073 u+006B u+0079
+ Punycode: Proprostnemluvesky-uyb24dma41a
+
+
+
+
+
+
+Costello Standards Track [Page 14]
+
+RFC 3492 IDNA Punycode March 2003
+
+
+ (E) Hebrew:
+ u+05DC u+05DE u+05D4 u+05D4 u+05DD u+05E4 u+05E9 u+05D5 u+05D8
+ u+05DC u+05D0 u+05DE u+05D3 u+05D1 u+05E8 u+05D9 u+05DD u+05E2
+ u+05D1 u+05E8 u+05D9 u+05EA
+ Punycode: 4dbcagdahymbxekheh6e0a7fei0b
+
+ (F) Hindi (Devanagari):
+ u+092F u+0939 u+0932 u+094B u+0917 u+0939 u+093F u+0928 u+094D
+ u+0926 u+0940 u+0915 u+094D u+092F u+094B u+0902 u+0928 u+0939
+ u+0940 u+0902 u+092C u+094B u+0932 u+0938 u+0915 u+0924 u+0947
+ u+0939 u+0948 u+0902
+ Punycode: i1baa7eci9glrd9b2ae1bj0hfcgg6iyaf8o0a1dig0cd
+
+ (G) Japanese (kanji and hiragana):
+ u+306A u+305C u+307F u+3093 u+306A u+65E5 u+672C u+8A9E u+3092
+ u+8A71 u+3057 u+3066 u+304F u+308C u+306A u+3044 u+306E u+304B
+ Punycode: n8jok5ay5dzabd5bym9f0cm5685rrjetr6pdxa
+
+ (H) Korean (Hangul syllables):
+ u+C138 u+ACC4 u+C758 u+BAA8 u+B4E0 u+C0AC u+B78C u+B4E4 u+C774
+ u+D55C u+AD6D u+C5B4 u+B97C u+C774 u+D574 u+D55C u+B2E4 u+BA74
+ u+C5BC u+B9C8 u+B098 u+C88B u+C744 u+AE4C
+ Punycode: 989aomsvi5e83db1d2a355cv1e0vak1dwrv93d5xbh15a0dt30a5j\
+ psd879ccm6fea98c
+
+ (I) Russian (Cyrillic):
+ U+043F u+043E u+0447 u+0435 u+043C u+0443 u+0436 u+0435 u+043E
+ u+043D u+0438 u+043D u+0435 u+0433 u+043E u+0432 u+043E u+0440
+ u+044F u+0442 u+043F u+043E u+0440 u+0443 u+0441 u+0441 u+043A
+ u+0438
+ Punycode: b1abfaaepdrnnbgefbaDotcwatmq2g4l
+
+ (J) Spanish: Porqu<eacute>nopuedensimplementehablarenEspa<ntilde>ol
+ U+0050 u+006F u+0072 u+0071 u+0075 u+00E9 u+006E u+006F u+0070
+ u+0075 u+0065 u+0064 u+0065 u+006E u+0073 u+0069 u+006D u+0070
+ u+006C u+0065 u+006D u+0065 u+006E u+0074 u+0065 u+0068 u+0061
+ u+0062 u+006C u+0061 u+0072 u+0065 u+006E U+0045 u+0073 u+0070
+ u+0061 u+00F1 u+006F u+006C
+ Punycode: PorqunopuedensimplementehablarenEspaol-fmd56a
+
+ (K) Vietnamese:
+ T<adotbelow>isaoh<odotbelow>kh<ocirc>ngth<ecirchookabove>ch\
+ <ihookabove>n<oacute>iti<ecircacute>ngVi<ecircdotbelow>t
+ U+0054 u+1EA1 u+0069 u+0073 u+0061 u+006F u+0068 u+1ECD u+006B
+ u+0068 u+00F4 u+006E u+0067 u+0074 u+0068 u+1EC3 u+0063 u+0068
+ u+1EC9 u+006E u+00F3 u+0069 u+0074 u+0069 u+1EBF u+006E u+0067
+ U+0056 u+0069 u+1EC7 u+0074
+ Punycode: TisaohkhngthchnitingVit-kjcr8268qyxafd2f1b9g
+
+
+
+Costello Standards Track [Page 15]
+
+RFC 3492 IDNA Punycode March 2003
+
+
+ The next several examples are all names of Japanese music artists,
+ song titles, and TV programs, just because the author happens to have
+ them handy (but Japanese is useful for providing examples of single-
+ row text, two-row text, ideographic text, and various mixtures
+ thereof).
+
+ (L) 3<nen>B<gumi><kinpachi><sensei>
+ u+0033 u+5E74 U+0042 u+7D44 u+91D1 u+516B u+5148 u+751F
+ Punycode: 3B-ww4c5e180e575a65lsy2b
+
+ (M) <amuro><namie>-with-SUPER-MONKEYS
+ u+5B89 u+5BA4 u+5948 u+7F8E u+6075 u+002D u+0077 u+0069 u+0074
+ u+0068 u+002D U+0053 U+0055 U+0050 U+0045 U+0052 u+002D U+004D
+ U+004F U+004E U+004B U+0045 U+0059 U+0053
+ Punycode: -with-SUPER-MONKEYS-pc58ag80a8qai00g7n9n
+
+ (N) Hello-Another-Way-<sorezore><no><basho>
+ U+0048 u+0065 u+006C u+006C u+006F u+002D U+0041 u+006E u+006F
+ u+0074 u+0068 u+0065 u+0072 u+002D U+0057 u+0061 u+0079 u+002D
+ u+305D u+308C u+305E u+308C u+306E u+5834 u+6240
+ Punycode: Hello-Another-Way--fc4qua05auwb3674vfr0b
+
+ (O) <hitotsu><yane><no><shita>2
+ u+3072 u+3068 u+3064 u+5C4B u+6839 u+306E u+4E0B u+0032
+ Punycode: 2-u9tlzr9756bt3uc0v
+
+ (P) Maji<de>Koi<suru>5<byou><mae>
+ U+004D u+0061 u+006A u+0069 u+3067 U+004B u+006F u+0069 u+3059
+ u+308B u+0035 u+79D2 u+524D
+ Punycode: MajiKoi5-783gue6qz075azm5e
+
+ (Q) <pafii>de<runba>
+ u+30D1 u+30D5 u+30A3 u+30FC u+0064 u+0065 u+30EB u+30F3 u+30D0
+ Punycode: de-jg4avhby1noc0d
+
+ (R) <sono><supiido><de>
+ u+305D u+306E u+30B9 u+30D4 u+30FC u+30C9 u+3067
+ Punycode: d9juau41awczczp
+
+ The last example is an ASCII string that breaks the existing rules
+ for host name labels. (It is not a realistic example for IDNA,
+ because IDNA never encodes pure ASCII labels.)
+
+ (S) -> $1.00 <-
+ u+002D u+003E u+0020 u+0024 u+0031 u+002E u+0030 u+0030 u+0020
+ u+003C u+002D
+ Punycode: -> $1.00 <--
+
+
+
+
+Costello Standards Track [Page 16]
+
+RFC 3492 IDNA Punycode March 2003
+
+
+7.2 Decoding traces
+
+ In the following traces, the evolving state of the decoder is shown
+ as a sequence of hexadecimal values, representing the code points in
+ the extended string. An asterisk appears just after the most
+ recently inserted code point, indicating both n (the value preceeding
+ the asterisk) and i (the position of the value just after the
+ asterisk). Other numerical values are decimal.
+
+ Decoding trace of example B from section 7.1:
+
+ n is 128, i is 0, bias is 72
+ input is "ihqwcrb4cv8a8dqg056pqjye"
+ there is no delimiter, so extended string starts empty
+ delta "ihq" decodes to 19853
+ bias becomes 21
+ 4E0D *
+ delta "wc" decodes to 64
+ bias becomes 20
+ 4E0D 4E2D *
+ delta "rb" decodes to 37
+ bias becomes 13
+ 4E3A * 4E0D 4E2D
+ delta "4c" decodes to 56
+ bias becomes 17
+ 4E3A 4E48 * 4E0D 4E2D
+ delta "v8a" decodes to 599
+ bias becomes 32
+ 4E3A 4EC0 * 4E48 4E0D 4E2D
+ delta "8d" decodes to 130
+ bias becomes 23
+ 4ED6 * 4E3A 4EC0 4E48 4E0D 4E2D
+ delta "qg" decodes to 154
+ bias becomes 25
+ 4ED6 4EEC * 4E3A 4EC0 4E48 4E0D 4E2D
+ delta "056p" decodes to 46301
+ bias becomes 84
+ 4ED6 4EEC 4E3A 4EC0 4E48 4E0D 4E2D 6587 *
+ delta "qjye" decodes to 88531
+ bias becomes 90
+ 4ED6 4EEC 4E3A 4EC0 4E48 4E0D 8BF4 * 4E2D 6587
+
+
+
+
+
+
+
+
+
+
+Costello Standards Track [Page 17]
+
+RFC 3492 IDNA Punycode March 2003
+
+
+ Decoding trace of example L from section 7.1:
+
+ n is 128, i is 0, bias is 72
+ input is "3B-ww4c5e180e575a65lsy2b"
+ literal portion is "3B-", so extended string starts as:
+ 0033 0042
+ delta "ww4c" decodes to 62042
+ bias becomes 27
+ 0033 0042 5148 *
+ delta "5e" decodes to 139
+ bias becomes 24
+ 0033 0042 516B * 5148
+ delta "180e" decodes to 16683
+ bias becomes 67
+ 0033 5E74 * 0042 516B 5148
+ delta "575a" decodes to 34821
+ bias becomes 82
+ 0033 5E74 0042 516B 5148 751F *
+ delta "65l" decodes to 14592
+ bias becomes 67
+ 0033 5E74 0042 7D44 * 516B 5148 751F
+ delta "sy2b" decodes to 42088
+ bias becomes 84
+ 0033 5E74 0042 7D44 91D1 * 516B 5148 751F
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Costello Standards Track [Page 18]
+
+RFC 3492 IDNA Punycode March 2003
+
+
+7.3 Encoding traces
+
+ In the following traces, code point values are hexadecimal, while
+ other numerical values are decimal.
+
+ Encoding trace of example B from section 7.1:
+
+ bias is 72
+ input is:
+ 4ED6 4EEC 4E3A 4EC0 4E48 4E0D 8BF4 4E2D 6587
+ there are no basic code points, so no literal portion
+ next code point to insert is 4E0D
+ needed delta is 19853, encodes as "ihq"
+ bias becomes 21
+ next code point to insert is 4E2D
+ needed delta is 64, encodes as "wc"
+ bias becomes 20
+ next code point to insert is 4E3A
+ needed delta is 37, encodes as "rb"
+ bias becomes 13
+ next code point to insert is 4E48
+ needed delta is 56, encodes as "4c"
+ bias becomes 17
+ next code point to insert is 4EC0
+ needed delta is 599, encodes as "v8a"
+ bias becomes 32
+ next code point to insert is 4ED6
+ needed delta is 130, encodes as "8d"
+ bias becomes 23
+ next code point to insert is 4EEC
+ needed delta is 154, encodes as "qg"
+ bias becomes 25
+ next code point to insert is 6587
+ needed delta is 46301, encodes as "056p"
+ bias becomes 84
+ next code point to insert is 8BF4
+ needed delta is 88531, encodes as "qjye"
+ bias becomes 90
+ output is "ihqwcrb4cv8a8dqg056pqjye"
+
+
+
+
+
+
+
+
+
+
+
+
+Costello Standards Track [Page 19]
+
+RFC 3492 IDNA Punycode March 2003
+
+
+ Encoding trace of example L from section 7.1:
+
+ bias is 72
+ input is:
+ 0033 5E74 0042 7D44 91D1 516B 5148 751F
+ basic code points (0033, 0042) are copied to literal portion: "3B-"
+ next code point to insert is 5148
+ needed delta is 62042, encodes as "ww4c"
+ bias becomes 27
+ next code point to insert is 516B
+ needed delta is 139, encodes as "5e"
+ bias becomes 24
+ next code point to insert is 5E74
+ needed delta is 16683, encodes as "180e"
+ bias becomes 67
+ next code point to insert is 751F
+ needed delta is 34821, encodes as "575a"
+ bias becomes 82
+ next code point to insert is 7D44
+ needed delta is 14592, encodes as "65l"
+ bias becomes 67
+ next code point to insert is 91D1
+ needed delta is 42088, encodes as "sy2b"
+ bias becomes 84
+ output is "3B-ww4c5e180e575a65lsy2b"
+
+8. Security Considerations
+
+ Users expect each domain name in DNS to be controlled by a single
+ authority. If a Unicode string intended for use as a domain label
+ could map to multiple ACE labels, then an internationalized domain
+ name could map to multiple ASCII domain names, each controlled by a
+ different authority, some of which could be spoofs that hijack
+ service requests intended for another. Therefore Punycode is
+ designed so that each Unicode string has a unique encoding.
+
+ However, there can still be multiple Unicode representations of the
+ "same" text, for various definitions of "same". This problem is
+ addressed to some extent by the Unicode standard under the topic of
+ canonicalization, and this work is leveraged for domain names by
+ Nameprep [NAMEPREP].
+
+
+
+
+
+
+
+
+
+
+Costello Standards Track [Page 20]
+
+RFC 3492 IDNA Punycode March 2003
+
+
+9. References
+
+9.1 Normative References
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+9.2 Informative References
+
+ [RFC952] Harrenstien, K., Stahl, M. and E. Feinler, "DOD Internet
+ Host Table Specification", RFC 952, October 1985.
+
+ [RFC1034] Mockapetris, P., "Domain Names - Concepts and
+ Facilities", STD 13, RFC 1034, November 1987.
+
+ [IDNA] Faltstrom, P., Hoffman, P. and A. Costello,
+ "Internationalizing Domain Names in Applications
+ (IDNA)", RFC 3490, March 2003.
+
+ [NAMEPREP] Hoffman, P. and M. Blanchet, "Nameprep: A Stringprep
+ Profile for Internationalized Domain Names (IDN)", RFC
+ 3491, March 2003.
+
+ [ASCII] Cerf, V., "ASCII format for Network Interchange", RFC
+ 20, October 1969.
+
+ [PROVINCIAL] Kaplan, M., "The 'anyone can be provincial!' page",
+ http://www.trigeminal.com/samples/provincial.html.
+
+ [UNICODE] The Unicode Consortium, "The Unicode Standard",
+ http://www.unicode.org/unicode/standard/standard.html.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Costello Standards Track [Page 21]
+
+RFC 3492 IDNA Punycode March 2003
+
+
+A. Mixed-case annotation
+
+ In order to use Punycode to represent case-insensitive strings,
+ higher layers need to case-fold the strings prior to Punycode
+ encoding. The encoded string can use mixed case as an annotation
+ telling how to convert the folded string into a mixed-case string for
+ display purposes. Note, however, that mixed-case annotation is not
+ used by the ToASCII and ToUnicode operations specified in [IDNA], and
+ therefore implementors of IDNA can disregard this appendix.
+
+ Basic code points can use mixed case directly, because the decoder
+ copies them verbatim, leaving lowercase code points lowercase, and
+ leaving uppercase code points uppercase. Each non-basic code point
+ is represented by a delta, which is represented by a sequence of
+ basic code points, the last of which provides the annotation. If it
+ is uppercase, it is a suggestion to map the non-basic code point to
+ uppercase (if possible); if it is lowercase, it is a suggestion to
+ map the non-basic code point to lowercase (if possible).
+
+ These annotations do not alter the code points returned by decoders;
+ the annotations are returned separately, for the caller to use or
+ ignore. Encoders can accept annotations in addition to code points,
+ but the annotations do not alter the output, except to influence the
+ uppercase/lowercase form of ASCII letters.
+
+ Punycode encoders and decoders need not support these annotations,
+ and higher layers need not use them.
+
+B. Disclaimer and license
+
+ Regarding this entire document or any portion of it (including the
+ pseudocode and C code), the author makes no guarantees and is not
+ responsible for any damage resulting from its use. The author grants
+ irrevocable permission to anyone to use, modify, and distribute it in
+ any way that does not diminish the rights of anyone else to use,
+ modify, and distribute it, provided that redistributed derivative
+ works do not contain misleading author or version information.
+ Derivative works need not be licensed under similar terms.
+
+
+
+
+
+
+
+
+
+
+
+
+
+Costello Standards Track [Page 22]
+
+RFC 3492 IDNA Punycode March 2003
+
+
+C. Punycode sample implementation
+
+/*
+punycode.c from RFC 3492
+http://www.nicemice.net/idn/
+Adam M. Costello
+http://www.nicemice.net/amc/
+
+This is ANSI C code (C89) implementing Punycode (RFC 3492).
+
+*/
+
+
+/************************************************************/
+/* Public interface (would normally go in its own .h file): */
+
+#include <limits.h>
+
+enum punycode_status {
+ punycode_success,
+ punycode_bad_input, /* Input is invalid. */
+ punycode_big_output, /* Output would exceed the space provided. */
+ punycode_overflow /* Input needs wider integers to process. */
+};
+
+#if UINT_MAX >= (1 << 26) - 1
+typedef unsigned int punycode_uint;
+#else
+typedef unsigned long punycode_uint;
+#endif
+
+enum punycode_status punycode_encode(
+ punycode_uint input_length,
+ const punycode_uint input[],
+ const unsigned char case_flags[],
+ punycode_uint *output_length,
+ char output[] );
+
+ /* punycode_encode() converts Unicode to Punycode. The input */
+ /* is represented as an array of Unicode code points (not code */
+ /* units; surrogate pairs are not allowed), and the output */
+ /* will be represented as an array of ASCII code points. The */
+ /* output string is *not* null-terminated; it will contain */
+ /* zeros if and only if the input contains zeros. (Of course */
+ /* the caller can leave room for a terminator and add one if */
+ /* needed.) The input_length is the number of code points in */
+ /* the input. The output_length is an in/out argument: the */
+ /* caller passes in the maximum number of code points that it */
+
+
+
+Costello Standards Track [Page 23]
+
+RFC 3492 IDNA Punycode March 2003
+
+
+ /* can receive, and on successful return it will contain the */
+ /* number of code points actually output. The case_flags array */
+ /* holds input_length boolean values, where nonzero suggests that */
+ /* the corresponding Unicode character be forced to uppercase */
+ /* after being decoded (if possible), and zero suggests that */
+ /* it be forced to lowercase (if possible). ASCII code points */
+ /* are encoded literally, except that ASCII letters are forced */
+ /* to uppercase or lowercase according to the corresponding */
+ /* uppercase flags. If case_flags is a null pointer then ASCII */
+ /* letters are left as they are, and other code points are */
+ /* treated as if their uppercase flags were zero. The return */
+ /* value can be any of the punycode_status values defined above */
+ /* except punycode_bad_input; if not punycode_success, then */
+ /* output_size and output might contain garbage. */
+
+enum punycode_status punycode_decode(
+ punycode_uint input_length,
+ const char input[],
+ punycode_uint *output_length,
+ punycode_uint output[],
+ unsigned char case_flags[] );
+
+ /* punycode_decode() converts Punycode to Unicode. The input is */
+ /* represented as an array of ASCII code points, and the output */
+ /* will be represented as an array of Unicode code points. The */
+ /* input_length is the number of code points in the input. The */
+ /* output_length is an in/out argument: the caller passes in */
+ /* the maximum number of code points that it can receive, and */
+ /* on successful return it will contain the actual number of */
+ /* code points output. The case_flags array needs room for at */
+ /* least output_length values, or it can be a null pointer if the */
+ /* case information is not needed. A nonzero flag suggests that */
+ /* the corresponding Unicode character be forced to uppercase */
+ /* by the caller (if possible), while zero suggests that it be */
+ /* forced to lowercase (if possible). ASCII code points are */
+ /* output already in the proper case, but their flags will be set */
+ /* appropriately so that applying the flags would be harmless. */
+ /* The return value can be any of the punycode_status values */
+ /* defined above; if not punycode_success, then output_length, */
+ /* output, and case_flags might contain garbage. On success, the */
+ /* decoder will never need to write an output_length greater than */
+ /* input_length, because of how the encoding is defined. */
+
+/**********************************************************/
+/* Implementation (would normally go in its own .c file): */
+
+#include <string.h>
+
+
+
+
+Costello Standards Track [Page 24]
+
+RFC 3492 IDNA Punycode March 2003
+
+
+/*** Bootstring parameters for Punycode ***/
+
+enum { base = 36, tmin = 1, tmax = 26, skew = 38, damp = 700,
+ initial_bias = 72, initial_n = 0x80, delimiter = 0x2D };
+
+/* basic(cp) tests whether cp is a basic code point: */
+#define basic(cp) ((punycode_uint)(cp) < 0x80)
+
+/* delim(cp) tests whether cp is a delimiter: */
+#define delim(cp) ((cp) == delimiter)
+
+/* decode_digit(cp) returns the numeric value of a basic code */
+/* point (for use in representing integers) in the range 0 to */
+/* base-1, or base if cp is does not represent a value. */
+
+static punycode_uint decode_digit(punycode_uint cp)
+{
+ return cp - 48 < 10 ? cp - 22 : cp - 65 < 26 ? cp - 65 :
+ cp - 97 < 26 ? cp - 97 : base;
+}
+
+/* encode_digit(d,flag) returns the basic code point whose value */
+/* (when used for representing integers) is d, which needs to be in */
+/* the range 0 to base-1. The lowercase form is used unless flag is */
+/* nonzero, in which case the uppercase form is used. The behavior */
+/* is undefined if flag is nonzero and digit d has no uppercase form. */
+
+static char encode_digit(punycode_uint d, int flag)
+{
+ return d + 22 + 75 * (d < 26) - ((flag != 0) << 5);
+ /* 0..25 map to ASCII a..z or A..Z */
+ /* 26..35 map to ASCII 0..9 */
+}
+
+/* flagged(bcp) tests whether a basic code point is flagged */
+/* (uppercase). The behavior is undefined if bcp is not a */
+/* basic code point. */
+
+#define flagged(bcp) ((punycode_uint)(bcp) - 65 < 26)
+
+/* encode_basic(bcp,flag) forces a basic code point to lowercase */
+/* if flag is zero, uppercase if flag is nonzero, and returns */
+/* the resulting code point. The code point is unchanged if it */
+/* is caseless. The behavior is undefined if bcp is not a basic */
+/* code point. */
+
+static char encode_basic(punycode_uint bcp, int flag)
+{
+
+
+
+Costello Standards Track [Page 25]
+
+RFC 3492 IDNA Punycode March 2003
+
+
+ bcp -= (bcp - 97 < 26) << 5;
+ return bcp + ((!flag && (bcp - 65 < 26)) << 5);
+}
+
+/*** Platform-specific constants ***/
+
+/* maxint is the maximum value of a punycode_uint variable: */
+static const punycode_uint maxint = -1;
+/* Because maxint is unsigned, -1 becomes the maximum value. */
+
+/*** Bias adaptation function ***/
+
+static punycode_uint adapt(
+ punycode_uint delta, punycode_uint numpoints, int firsttime )
+{
+ punycode_uint k;
+
+ delta = firsttime ? delta / damp : delta >> 1;
+ /* delta >> 1 is a faster way of doing delta / 2 */
+ delta += delta / numpoints;
+
+ for (k = 0; delta > ((base - tmin) * tmax) / 2; k += base) {
+ delta /= base - tmin;
+ }
+
+ return k + (base - tmin + 1) * delta / (delta + skew);
+}
+
+/*** Main encode function ***/
+
+enum punycode_status punycode_encode(
+ punycode_uint input_length,
+ const punycode_uint input[],
+ const unsigned char case_flags[],
+ punycode_uint *output_length,
+ char output[] )
+{
+ punycode_uint n, delta, h, b, out, max_out, bias, j, m, q, k, t;
+
+ /* Initialize the state: */
+
+ n = initial_n;
+ delta = out = 0;
+ max_out = *output_length;
+ bias = initial_bias;
+
+ /* Handle the basic code points: */
+
+
+
+
+Costello Standards Track [Page 26]
+
+RFC 3492 IDNA Punycode March 2003
+
+
+ for (j = 0; j < input_length; ++j) {
+ if (basic(input[j])) {
+ if (max_out - out < 2) return punycode_big_output;
+ output[out++] =
+ case_flags ? encode_basic(input[j], case_flags[j]) : input[j];
+ }
+ /* else if (input[j] < n) return punycode_bad_input; */
+ /* (not needed for Punycode with unsigned code points) */
+ }
+
+ h = b = out;
+
+ /* h is the number of code points that have been handled, b is the */
+ /* number of basic code points, and out is the number of characters */
+ /* that have been output. */
+
+ if (b > 0) output[out++] = delimiter;
+
+ /* Main encoding loop: */
+
+ while (h < input_length) {
+ /* All non-basic code points < n have been */
+ /* handled already. Find the next larger one: */
+
+ for (m = maxint, j = 0; j < input_length; ++j) {
+ /* if (basic(input[j])) continue; */
+ /* (not needed for Punycode) */
+ if (input[j] >= n && input[j] < m) m = input[j];
+ }
+
+ /* Increase delta enough to advance the decoder's */
+ /* <n,i> state to <m,0>, but guard against overflow: */
+
+ if (m - n > (maxint - delta) / (h + 1)) return punycode_overflow;
+ delta += (m - n) * (h + 1);
+ n = m;
+
+ for (j = 0; j < input_length; ++j) {
+ /* Punycode does not need to check whether input[j] is basic: */
+ if (input[j] < n /* || basic(input[j]) */ ) {
+ if (++delta == 0) return punycode_overflow;
+ }
+
+ if (input[j] == n) {
+ /* Represent delta as a generalized variable-length integer: */
+
+ for (q = delta, k = base; ; k += base) {
+ if (out >= max_out) return punycode_big_output;
+
+
+
+Costello Standards Track [Page 27]
+
+RFC 3492 IDNA Punycode March 2003
+
+
+ t = k <= bias /* + tmin */ ? tmin : /* +tmin not needed */
+ k >= bias + tmax ? tmax : k - bias;
+ if (q < t) break;
+ output[out++] = encode_digit(t + (q - t) % (base - t), 0);
+ q = (q - t) / (base - t);
+ }
+
+ output[out++] = encode_digit(q, case_flags && case_flags[j]);
+ bias = adapt(delta, h + 1, h == b);
+ delta = 0;
+ ++h;
+ }
+ }
+
+ ++delta, ++n;
+ }
+
+ *output_length = out;
+ return punycode_success;
+}
+
+/*** Main decode function ***/
+
+enum punycode_status punycode_decode(
+ punycode_uint input_length,
+ const char input[],
+ punycode_uint *output_length,
+ punycode_uint output[],
+ unsigned char case_flags[] )
+{
+ punycode_uint n, out, i, max_out, bias,
+ b, j, in, oldi, w, k, digit, t;
+
+ /* Initialize the state: */
+
+ n = initial_n;
+ out = i = 0;
+ max_out = *output_length;
+ bias = initial_bias;
+
+ /* Handle the basic code points: Let b be the number of input code */
+ /* points before the last delimiter, or 0 if there is none, then */
+ /* copy the first b code points to the output. */
+
+ for (b = j = 0; j < input_length; ++j) if (delim(input[j])) b = j;
+ if (b > max_out) return punycode_big_output;
+
+ for (j = 0; j < b; ++j) {
+
+
+
+Costello Standards Track [Page 28]
+
+RFC 3492 IDNA Punycode March 2003
+
+
+ if (case_flags) case_flags[out] = flagged(input[j]);
+ if (!basic(input[j])) return punycode_bad_input;
+ output[out++] = input[j];
+ }
+
+ /* Main decoding loop: Start just after the last delimiter if any */
+ /* basic code points were copied; start at the beginning otherwise. */
+
+ for (in = b > 0 ? b + 1 : 0; in < input_length; ++out) {
+
+ /* in is the index of the next character to be consumed, and */
+ /* out is the number of code points in the output array. */
+
+ /* Decode a generalized variable-length integer into delta, */
+ /* which gets added to i. The overflow checking is easier */
+ /* if we increase i as we go, then subtract off its starting */
+ /* value at the end to obtain delta. */
+
+ for (oldi = i, w = 1, k = base; ; k += base) {
+ if (in >= input_length) return punycode_bad_input;
+ digit = decode_digit(input[in++]);
+ if (digit >= base) return punycode_bad_input;
+ if (digit > (maxint - i) / w) return punycode_overflow;
+ i += digit * w;
+ t = k <= bias /* + tmin */ ? tmin : /* +tmin not needed */
+ k >= bias + tmax ? tmax : k - bias;
+ if (digit < t) break;
+ if (w > maxint / (base - t)) return punycode_overflow;
+ w *= (base - t);
+ }
+
+ bias = adapt(i - oldi, out + 1, oldi == 0);
+
+ /* i was supposed to wrap around from out+1 to 0, */
+ /* incrementing n each time, so we'll fix that now: */
+
+ if (i / (out + 1) > maxint - n) return punycode_overflow;
+ n += i / (out + 1);
+ i %= (out + 1);
+
+ /* Insert n at position i of the output: */
+
+ /* not needed for Punycode: */
+ /* if (decode_digit(n) <= base) return punycode_invalid_input; */
+ if (out >= max_out) return punycode_big_output;
+
+ if (case_flags) {
+ memmove(case_flags + i + 1, case_flags + i, out - i);
+
+
+
+Costello Standards Track [Page 29]
+
+RFC 3492 IDNA Punycode March 2003
+
+
+ /* Case of last character determines uppercase flag: */
+ case_flags[i] = flagged(input[in - 1]);
+ }
+
+ memmove(output + i + 1, output + i, (out - i) * sizeof *output);
+ output[i++] = n;
+ }
+
+ *output_length = out;
+ return punycode_success;
+}
+
+/******************************************************************/
+/* Wrapper for testing (would normally go in a separate .c file): */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* For testing, we'll just set some compile-time limits rather than */
+/* use malloc(), and set a compile-time option rather than using a */
+/* command-line option. */
+
+enum {
+ unicode_max_length = 256,
+ ace_max_length = 256
+};
+
+static void usage(char **argv)
+{
+ fprintf(stderr,
+ "\n"
+ "%s -e reads code points and writes a Punycode string.\n"
+ "%s -d reads a Punycode string and writes code points.\n"
+ "\n"
+ "Input and output are plain text in the native character set.\n"
+ "Code points are in the form u+hex separated by whitespace.\n"
+ "Although the specification allows Punycode strings to contain\n"
+ "any characters from the ASCII repertoire, this test code\n"
+ "supports only the printable characters, and needs the Punycode\n"
+ "string to be followed by a newline.\n"
+ "The case of the u in u+hex is the force-to-uppercase flag.\n"
+ , argv[0], argv[0]);
+ exit(EXIT_FAILURE);
+}
+
+static void fail(const char *msg)
+
+
+
+Costello Standards Track [Page 30]
+
+RFC 3492 IDNA Punycode March 2003
+
+
+{
+ fputs(msg,stderr);
+ exit(EXIT_FAILURE);
+}
+
+static const char too_big[] =
+ "input or output is too large, recompile with larger limits\n";
+static const char invalid_input[] = "invalid input\n";
+static const char overflow[] = "arithmetic overflow\n";
+static const char io_error[] = "I/O error\n";
+
+/* The following string is used to convert printable */
+/* characters between ASCII and the native charset: */
+
+static const char print_ascii[] =
+ "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
+ "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
+ " !\"#$%&'()*+,-./"
+ "0123456789:;<=>?"
+ "@ABCDEFGHIJKLMNO"
+ "PQRSTUVWXYZ[\\]^_"
+ "`abcdefghijklmno"
+ "pqrstuvwxyz{|}~\n";
+
+int main(int argc, char **argv)
+{
+ enum punycode_status status;
+ int r;
+ unsigned int input_length, output_length, j;
+ unsigned char case_flags[unicode_max_length];
+
+ if (argc != 2) usage(argv);
+ if (argv[1][0] != '-') usage(argv);
+ if (argv[1][2] != 0) usage(argv);
+
+ if (argv[1][1] == 'e') {
+ punycode_uint input[unicode_max_length];
+ unsigned long codept;
+ char output[ace_max_length+1], uplus[3];
+ int c;
+
+ /* Read the input code points: */
+
+ input_length = 0;
+
+ for (;;) {
+ r = scanf("%2s%lx", uplus, &codept);
+ if (ferror(stdin)) fail(io_error);
+
+
+
+Costello Standards Track [Page 31]
+
+RFC 3492 IDNA Punycode March 2003
+
+
+ if (r == EOF || r == 0) break;
+
+ if (r != 2 || uplus[1] != '+' || codept > (punycode_uint)-1) {
+ fail(invalid_input);
+ }
+
+ if (input_length == unicode_max_length) fail(too_big);
+
+ if (uplus[0] == 'u') case_flags[input_length] = 0;
+ else if (uplus[0] == 'U') case_flags[input_length] = 1;
+ else fail(invalid_input);
+
+ input[input_length++] = codept;
+ }
+
+ /* Encode: */
+
+ output_length = ace_max_length;
+ status = punycode_encode(input_length, input, case_flags,
+ &output_length, output);
+ if (status == punycode_bad_input) fail(invalid_input);
+ if (status == punycode_big_output) fail(too_big);
+ if (status == punycode_overflow) fail(overflow);
+ assert(status == punycode_success);
+
+ /* Convert to native charset and output: */
+
+ for (j = 0; j < output_length; ++j) {
+ c = output[j];
+ assert(c >= 0 && c <= 127);
+ if (print_ascii[c] == 0) fail(invalid_input);
+ output[j] = print_ascii[c];
+ }
+
+ output[j] = 0;
+ r = puts(output);
+ if (r == EOF) fail(io_error);
+ return EXIT_SUCCESS;
+ }
+
+ if (argv[1][1] == 'd') {
+ char input[ace_max_length+2], *p, *pp;
+ punycode_uint output[unicode_max_length];
+
+ /* Read the Punycode input string and convert to ASCII: */
+
+ fgets(input, ace_max_length+2, stdin);
+ if (ferror(stdin)) fail(io_error);
+
+
+
+Costello Standards Track [Page 32]
+
+RFC 3492 IDNA Punycode March 2003
+
+
+ if (feof(stdin)) fail(invalid_input);
+ input_length = strlen(input) - 1;
+ if (input[input_length] != '\n') fail(too_big);
+ input[input_length] = 0;
+
+ for (p = input; *p != 0; ++p) {
+ pp = strchr(print_ascii, *p);
+ if (pp == 0) fail(invalid_input);
+ *p = pp - print_ascii;
+ }
+
+ /* Decode: */
+
+ output_length = unicode_max_length;
+ status = punycode_decode(input_length, input, &output_length,
+ output, case_flags);
+ if (status == punycode_bad_input) fail(invalid_input);
+ if (status == punycode_big_output) fail(too_big);
+ if (status == punycode_overflow) fail(overflow);
+ assert(status == punycode_success);
+
+ /* Output the result: */
+
+ for (j = 0; j < output_length; ++j) {
+ r = printf("%s+%04lX\n",
+ case_flags[j] ? "U" : "u",
+ (unsigned long) output[j] );
+ if (r < 0) fail(io_error);
+ }
+
+ return EXIT_SUCCESS;
+ }
+
+ usage(argv);
+ return EXIT_SUCCESS; /* not reached, but quiets compiler warning */
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Costello Standards Track [Page 33]
+
+RFC 3492 IDNA Punycode March 2003
+
+
+Author's Address
+
+ Adam M. Costello
+ University of California, Berkeley
+ http://www.nicemice.net/amc/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Costello Standards Track [Page 34]
+
+RFC 3492 IDNA Punycode March 2003
+
+
+Full Copyright Statement
+
+ Copyright (C) The Internet Society (2003). All Rights Reserved.
+
+ This document and translations of it may be copied and furnished to
+ others, and derivative works that comment on or otherwise explain it
+ or assist in its implementation may be prepared, copied, published
+ and distributed, in whole or in part, without restriction of any
+ kind, provided that the above copyright notice and this paragraph are
+ included on all such copies and derivative works. However, this
+ document itself may not be modified in any way, such as by removing
+ the copyright notice or references to the Internet Society or other
+ Internet organizations, except as needed for the purpose of
+ developing Internet standards in which case the procedures for
+ copyrights defined in the Internet Standards process must be
+ followed, or as required to translate it into languages other than
+ English.
+
+ The limited permissions granted above are perpetual and will not be
+ revoked by the Internet Society or its successors or assigns.
+
+ This document and the information contained herein is provided on an
+ "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+ TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is currently provided by the
+ Internet Society.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Costello Standards Track [Page 35]
+
diff --git a/source4/heimdal/lib/wind/rfc4013.txt b/source4/heimdal/lib/wind/rfc4013.txt
new file mode 100644
index 0000000000..54a1d31581
--- /dev/null
+++ b/source4/heimdal/lib/wind/rfc4013.txt
@@ -0,0 +1,339 @@
+
+
+
+
+
+
+Network Working Group K. Zeilenga
+Request for Comments: 4013 OpenLDAP Foundation
+Category: Standards Track February 2005
+
+
+ SASLprep: Stringprep Profile for User Names and Passwords
+
+Status of This Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2005).
+
+Abstract
+
+ This document describes how to prepare Unicode strings representing
+ user names and passwords for comparison. The document defines the
+ "SASLprep" profile of the "stringprep" algorithm to be used for both
+ user names and passwords. This profile is intended to be used by
+ Simple Authentication and Security Layer (SASL) mechanisms (such as
+ PLAIN, CRAM-MD5, and DIGEST-MD5), as well as other protocols
+ exchanging simple user names and/or passwords.
+
+1. Introduction
+
+ The use of simple user names and passwords in authentication and
+ authorization is pervasive on the Internet. To increase the
+ likelihood that user name and password input and comparison work in
+ ways that make sense for typical users throughout the world, this
+ document defines rules for preparing internationalized user names and
+ passwords for comparison. For simplicity and implementation ease, a
+ single algorithm is defined for both user names and passwords.
+
+ The algorithm assumes all strings are comprised of characters from
+ the Unicode [Unicode] character set.
+
+ This document defines the "SASLprep" profile of the "stringprep"
+ algorithm [StringPrep].
+
+ The profile is designed for use in Simple Authentication and Security
+ Layer ([SASL]) mechanisms, such as [PLAIN], [CRAM-MD5], and
+ [DIGEST-MD5]. It may be applicable where simple user names and
+
+
+
+Zeilenga Standards Track [Page 1]
+
+RFC 4013 SASLprep February 2005
+
+
+ passwords are used. This profile is not intended for use in
+ preparing identity strings that are not simple user names (e.g.,
+ email addresses, domain names, distinguished names), or where
+ identity or password strings that are not character data, or require
+ different handling (e.g., case folding).
+
+ This document does not alter the technical specification of any
+ existing protocols. Any specification that wishes to use the
+ algorithm described in this document needs to explicitly incorporate
+ this document and provide precise details as to where and how this
+ algorithm is used by implementations of that specification.
+
+2. The SASLprep Profile
+
+ This section defines the "SASLprep" profile of the "stringprep"
+ algorithm [StringPrep]. This profile is intended for use in
+ preparing strings representing simple user names and passwords.
+
+ This profile uses Unicode 3.2 [Unicode].
+
+ Character names in this document use the notation for code points and
+ names from the Unicode Standard [Unicode]. For example, the letter
+ "a" may be represented as either <U+0061> or <LATIN SMALL LETTER A>.
+ In the lists of mappings and the prohibited characters, the "U+" is
+ left off to make the lists easier to read. The comments for
+ character ranges are shown in square brackets (such as "[CONTROL
+ CHARACTERS]") and do not come from the standard.
+
+ Note: A glossary of terms used in Unicode can be found in [Glossary].
+ Information on the Unicode character encoding model can be found in
+ [CharModel].
+
+2.1. Mapping
+
+ This profile specifies:
+
+ - non-ASCII space characters [StringPrep, C.1.2] that can be
+ mapped to SPACE (U+0020), and
+
+ - the "commonly mapped to nothing" characters [StringPrep, B.1]
+ that can be mapped to nothing.
+
+2.2. Normalization
+
+ This profile specifies using Unicode normalization form KC, as
+ described in Section 4 of [StringPrep].
+
+
+
+
+
+Zeilenga Standards Track [Page 2]
+
+RFC 4013 SASLprep February 2005
+
+
+2.3. Prohibited Output
+
+ This profile specifies the following characters as prohibited input:
+
+ - Non-ASCII space characters [StringPrep, C.1.2]
+ - ASCII control characters [StringPrep, C.2.1]
+ - Non-ASCII control characters [StringPrep, C.2.2]
+ - Private Use characters [StringPrep, C.3]
+ - Non-character code points [StringPrep, C.4]
+ - Surrogate code points [StringPrep, C.5]
+ - Inappropriate for plain text characters [StringPrep, C.6]
+ - Inappropriate for canonical representation characters
+ [StringPrep, C.7]
+ - Change display properties or deprecated characters
+ [StringPrep, C.8]
+ - Tagging characters [StringPrep, C.9]
+
+2.4. Bidirectional Characters
+
+ This profile specifies checking bidirectional strings as described in
+ [StringPrep, Section 6].
+
+2.5. Unassigned Code Points
+
+ This profile specifies the [StringPrep, A.1] table as its list of
+ unassigned code points.
+
+3. Examples
+
+ The following table provides examples of how various character data
+ is transformed by the SASLprep string preparation algorithm
+
+ # Input Output Comments
+ - ----- ------ --------
+ 1 I<U+00AD>X IX SOFT HYPHEN mapped to nothing
+ 2 user user no transformation
+ 3 USER USER case preserved, will not match #2
+ 4 <U+00AA> a output is NFKC, input in ISO 8859-1
+ 5 <U+2168> IX output is NFKC, will match #1
+ 6 <U+0007> Error - prohibited character
+ 7 <U+0627><U+0031> Error - bidirectional check
+
+4. Security Considerations
+
+ This profile is intended to prepare simple user name and password
+ strings for comparison or use in cryptographic functions (e.g.,
+ message digests). The preparation algorithm was specifically
+ designed such that its output is canonical, and it is well-formed.
+
+
+
+Zeilenga Standards Track [Page 3]
+
+RFC 4013 SASLprep February 2005
+
+
+ However, due to an anomaly [PR29] in the specification of Unicode
+ normalization, canonical equivalence is not guaranteed for a select
+ few character sequences. These sequences, however, do not appear in
+ well-formed text. This specification was published despite this
+ known technical problem. It is expected that this specification will
+ be revised before further progression on the Standards Track (after
+ [Unicode] and/or [StringPrep] specifications have been updated to
+ address this problem).
+
+ It is not intended for preparing identity strings that are not simple
+ user names (e.g., distinguished names, domain names), nor is the
+ profile intended for use of simple user names that require different
+ handling (such as case folding). Protocols (or applications of those
+ protocols) that have application-specific identity forms and/or
+ comparison algorithms should use mechanisms specifically designed for
+ these forms and algorithms.
+
+ Application of string preparation may have an impact upon the
+ feasibility of brute force and dictionary attacks. While the number
+ of possible prepared strings is less than the number of possible
+ Unicode strings, the number of usable names and passwords is greater
+ than as if only ASCII was used. Though SASLprep eliminates some
+ Unicode code point sequences as possible prepared strings, that
+ elimination generally makes the (canonical) output forms practicable
+ and prohibits nonsensical inputs.
+
+ User names and passwords should be protected from eavesdropping.
+
+ General "stringprep" and Unicode security considerations apply. Both
+ are discussed in [StringPrep].
+
+5. IANA Considerations
+
+ This document details the "SASLprep" profile of the [StringPrep]
+ protocol. This profile has been registered in the stringprep profile
+ registry.
+
+ Name of this profile: SASLprep
+ RFC in which the profile is defined: RFC 4013
+ Indicator whether or not this is the newest version of the
+ profile: This is the first version of the SASPprep profile.
+
+6. Acknowledgement
+
+ This document borrows text from "Preparation of Internationalized
+ Strings ('stringprep')" and "Nameprep: A Stringprep Profile for
+ Internationalized Domain Names", both by Paul Hoffman and Marc
+ Blanchet. This document is a product of the IETF SASL WG.
+
+
+
+Zeilenga Standards Track [Page 4]
+
+RFC 4013 SASLprep February 2005
+
+
+7. Normative References
+
+ [StringPrep] Hoffman, P. and M. Blanchet, "Preparation of
+ Internationalized Strings ("stringprep")", RFC 3454,
+ December 2002.
+
+ [Unicode] The Unicode Consortium, "The Unicode Standard, Version
+ 3.2.0" is defined by "The Unicode Standard, Version
+ 3.0" (Reading, MA, Addison-Wesley, 2000. ISBN 0-201-
+ 61633-5), as amended by the "Unicode Standard Annex
+ #27: Unicode 3.1"
+ (http://www.unicode.org/reports/tr27/) and by the
+ "Unicode Standard Annex #28: Unicode 3.2"
+ (http://www.unicode.org/reports/tr28/).
+
+8. Informative References
+
+ [Glossary] The Unicode Consortium, "Unicode Glossary",
+ <http://www.unicode.org/glossary/>.
+
+ [CharModel] Whistler, K. and M. Davis, "Unicode Technical Report
+ #17, Character Encoding Model", UTR17,
+ <http://www.unicode.org/unicode/reports/tr17/>, August
+ 2000.
+
+ [SASL] Melnikov, A., Ed., "Simple Authentication and Security
+ Layer (SASL)", Work in Progress.
+
+ [CRAM-MD5] Nerenberg, L., "The CRAM-MD5 SASL Mechanism", Work in
+ Progress.
+
+ [DIGEST-MD5] Leach, P., Newman, C., and A. Melnikov, "Using Digest
+ Authentication as a SASL Mechanism", Work in Progress.
+
+ [PLAIN] Zeilenga, K., Ed., "The Plain SASL Mechanism", Work in
+ Progress.
+
+ [PR29] "Public Review Issue #29: Normalization Issue",
+ <http://www.unicode.org/review/pr-29.html>, February
+ 2004.
+
+Author's Address
+
+ Kurt D. Zeilenga
+ OpenLDAP Foundation
+
+ EMail: Kurt@OpenLDAP.org
+
+
+
+
+Zeilenga Standards Track [Page 5]
+
+RFC 4013 SASLprep February 2005
+
+
+Full Copyright Statement
+
+ Copyright (C) The Internet Society (2005).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
+ ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
+ INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
+ INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the IETF's procedures with respect to rights in IETF Documents can
+ be found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at ietf-
+ ipr@ietf.org.
+
+
+Acknowledgement
+
+ Funding for the RFC Editor function is currently provided by the
+ Internet Society.
+
+
+
+
+
+
+Zeilenga Standards Track [Page 6]
+
diff --git a/source4/heimdal/lib/wind/rfc4518.py b/source4/heimdal/lib/wind/rfc4518.py
new file mode 100644
index 0000000000..a222fce16a
--- /dev/null
+++ b/source4/heimdal/lib/wind/rfc4518.py
@@ -0,0 +1,150 @@
+#!/usr/local/bin/python
+# -*- coding: iso-8859-1 -*-
+
+# $Id$
+
+# Copyright (c) 2004, 2008 Kungliga Tekniska Högskolan
+# (Royal Institute of Technology, Stockholm, Sweden).
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the Institute nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+import re
+import string
+
+def read():
+ """return a dict of tables from rfc4518"""
+
+ ret = {}
+
+#2.2. Map
+#
+# SOFT HYPHEN (U+00AD) and MONGOLIAN TODO SOFT HYPHEN (U+1806) code
+# points are mapped to nothing. COMBINING GRAPHEME JOINER (U+034F) and
+# VARIATION SELECTORs (U+180B-180D, FF00-FE0F) code points are also
+# mapped to nothing. The OBJECT REPLACEMENT CHARACTER (U+FFFC) is
+# mapped to nothing.
+
+ t = []
+ t.append(" 00AD; ; Map to nothing")
+ t.append(" 1806; ; Map to nothing")
+ t.append(" 034F; ; Map to nothing")
+
+ t.append(" 180B; ; Map to nothing")
+ t.append(" 180C; ; Map to nothing")
+ t.append(" 180D; ; Map to nothing")
+
+ t.append(" FE00; ; Map to nothing")
+ t.append(" FE01; ; Map to nothing")
+ t.append(" FE02; ; Map to nothing")
+ t.append(" FE03; ; Map to nothing")
+ t.append(" FE04; ; Map to nothing")
+ t.append(" FE05; ; Map to nothing")
+ t.append(" FE06; ; Map to nothing")
+ t.append(" FE07; ; Map to nothing")
+ t.append(" FE08; ; Map to nothing")
+ t.append(" FE09; ; Map to nothing")
+ t.append(" FE0A; ; Map to nothing")
+ t.append(" FE0B; ; Map to nothing")
+ t.append(" FE0C; ; Map to nothing")
+ t.append(" FE0D; ; Map to nothing")
+ t.append(" FE0E; ; Map to nothing")
+ t.append(" FE0F; ; Map to nothing")
+
+ t.append(" FFFC; ; Map to nothing")
+
+# CHARACTER TABULATION (U+0009), LINE FEED (LF) (U+000A), LINE
+# TABULATION (U+000B), FORM FEED (FF) (U+000C), CARRIAGE RETURN (CR)
+# (U+000D), and NEXT LINE (NEL) (U+0085) are mapped to SPACE (U+0020).
+
+ t.append(" 0009; 0020 ; Map to SPACE")
+ t.append(" 000A; 0020 ; Map to SPACE")
+ t.append(" 000B; 0020 ; Map to SPACE")
+ t.append(" 000C; 0020 ; Map to SPACE")
+ t.append(" 000D; 0020 ; Map to SPACE")
+ t.append(" 0085; 0020 ; Map to SPACE")
+
+# All other control code (e.g., Cc) points or code points with a
+# control function (e.g., Cf) are mapped to nothing. The following is
+# a complete list of these code points: U+0000-0008, 000E-001F, 007F-
+# 0084, 0086-009F, 06DD, 070F, 180E, 200C-200F, 202A-202E, 2060-2063,
+# 206A-206F, FEFF, FFF9-FFFB, 1D173-1D17A, E0001, E0020-E007F.
+
+ t.append(" 0000-0008; ; Map to nothing")
+ t.append(" 000E-001F; ; Map to nothing")
+ t.append(" 007F-0084; ; Map to nothing")
+ t.append(" 0086-009F; ; Map to nothing")
+ t.append(" 06DD; ; Map to nothing")
+ t.append(" 070F; ; Map to nothing")
+ t.append(" 180E; ; Map to nothing")
+ t.append(" 200C-200F; ; Map to nothing")
+ t.append(" 202A-202E; ; Map to nothing")
+ t.append(" 2060-2063; ; Map to nothing")
+ t.append(" 206A-206F; ; Map to nothing")
+ t.append(" FEFF; ; Map to nothing")
+ t.append(" FFF9-FFFB; ; Map to nothing")
+ t.append(" 1D173-1D17A; ; Map to nothing")
+ t.append(" E0001; ; Map to nothing")
+ t.append(" E0020-E007F; ; Map to nothing")
+
+# ZERO WIDTH SPACE (U+200B) is mapped to nothing. All other code
+# points with Separator (space, line, or paragraph) property (e.g., Zs,
+# Zl, or Zp) are mapped to SPACE (U+0020). The following is a complete
+# list of these code points: U+0020, 00A0, 1680, 2000-200A, 2028-2029,
+# 202F, 205F, 3000.
+
+ t.append(" 200B; ; Map to nothing")
+ t.append(" 0020; 0020; Map to SPACE")
+ t.append(" 00A0; 0020; Map to SPACE")
+ t.append(" 1680; 0020; Map to SPACE")
+ t.append(" 2000-200A; 0020; Map to SPACE")
+ t.append(" 2028-2029; 0020; Map to SPACE")
+ t.append(" 202F; 0020; Map to SPACE")
+ t.append(" 205F; 0020; Map to SPACE")
+ t.append(" 3000; 0020; Map to SPACE")
+
+ ret["rfc4518-map"] = t
+
+# For case ignore, numeric, and stored prefix string matching rules,
+# characters are case folded per B.2 of [RFC3454].
+
+ t = []
+
+#2.4. Prohibit
+
+# The REPLACEMENT CHARACTER (U+FFFD) code point is prohibited.
+
+ t.append(" FFFD;")
+
+ ret["rfc4518-error"] = t
+
+ t = []
+
+
+
+ return ret
diff --git a/source4/heimdal/lib/wind/rfc4518.txt b/source4/heimdal/lib/wind/rfc4518.txt
new file mode 100644
index 0000000000..f886bdfb5d
--- /dev/null
+++ b/source4/heimdal/lib/wind/rfc4518.txt
@@ -0,0 +1,787 @@
+
+
+
+
+
+
+Network Working Group K. Zeilenga
+Request for Comments: 4518 OpenLDAP Foundation
+Category: Standards Track June 2006
+
+
+ Lightweight Directory Access Protocol (LDAP):
+ Internationalized String Preparation
+
+Status of This Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2006).
+
+Abstract
+
+ The previous Lightweight Directory Access Protocol (LDAP) technical
+ specifications did not precisely define how character string matching
+ is to be performed. This led to a number of usability and
+ interoperability problems. This document defines string preparation
+ algorithms for character-based matching rules defined for use in
+ LDAP.
+
+1. Introduction
+
+1.1. Background
+
+ A Lightweight Directory Access Protocol (LDAP) [RFC4510] matching
+ rule [RFC4517] defines an algorithm for determining whether a
+ presented value matches an attribute value in accordance with the
+ criteria defined for the rule. The proposition may be evaluated to
+ True, False, or Undefined.
+
+ True - the attribute contains a matching value,
+
+ False - the attribute contains no matching value,
+
+ Undefined - it cannot be determined whether the attribute contains
+ a matching value.
+
+
+
+
+
+
+Zeilenga Standards Track [Page 1]
+
+RFC 4518 LDAP: Internationalized String Preparation June 2006
+
+
+ For instance, the caseIgnoreMatch matching rule may be used to
+ compare whether the commonName attribute contains a particular value
+ without regard for case and insignificant spaces.
+
+1.2. X.500 String Matching Rules
+
+ "X.520: Selected attribute types" [X.520] provides (among other
+ things) value syntaxes and matching rules for comparing values
+ commonly used in the directory [X.500]. These specifications are
+ inadequate for strings composed of Unicode [Unicode] characters.
+
+ The caseIgnoreMatch matching rule [X.520], for example, is simply
+ defined as being a case-insensitive comparison where insignificant
+ spaces are ignored. For printableString, there is only one space
+ character and case mapping is bijective, hence this definition is
+ sufficient. However, for Unicode string types such as
+ universalString, this is not sufficient. For example, a case-
+ insensitive matching implementation that folded lowercase characters
+ to uppercase would yield different results than an implementation
+ that used uppercase to lowercase folding. Or one implementation may
+ view space as referring to only SPACE (U+0020), a second
+ implementation may view any character with the space separator (Zs)
+ property as a space, and another implementation may view any
+ character with the whitespace (WS) category as a space.
+
+ The lack of precise specification for character string matching has
+ led to significant interoperability problems. When used in
+ certificate chain validation, security vulnerabilities can arise. To
+ address these problems, this document defines precise algorithms for
+ preparing character strings for matching.
+
+1.3. Relationship to "stringprep"
+
+ The character string preparation algorithms described in this
+ document are based upon the "stringprep" approach [RFC3454]. In
+ "stringprep", presented and stored values are first prepared for
+ comparison so that a character-by-character comparison yields the
+ "correct" result.
+
+ The approach used here is a refinement of the "stringprep" [RFC3454]
+ approach. Each algorithm involves two additional preparation steps.
+
+ a) Prior to applying the Unicode string preparation steps outlined in
+ "stringprep", the string is transcoded to Unicode.
+
+ b) After applying the Unicode string preparation steps outlined in
+ "stringprep", the string is modified to appropriately handle
+ characters insignificant to the matching rule.
+
+
+
+Zeilenga Standards Track [Page 2]
+
+RFC 4518 LDAP: Internationalized String Preparation June 2006
+
+
+ Hence, preparation of character strings for X.500 [X.500] matching
+ [X.501] involves the following steps:
+
+ 1) Transcode
+ 2) Map
+ 3) Normalize
+ 4) Prohibit
+ 5) Check Bidi (Bidirectional)
+ 6) Insignificant Character Handling
+
+ These steps are described in Section 2.
+
+ It is noted that while various tables of Unicode characters included
+ or referenced by this specification are derived from Unicode
+ [Unicode] data, these tables are to be considered definitive for the
+ purpose of implementing this specification.
+
+1.4. Relationship to the LDAP Technical Specification
+
+ This document is an integral part of the LDAP technical specification
+ [RFC4510], which obsoletes the previously defined LDAP technical
+ specification [RFC3377] in its entirety.
+
+ This document details new LDAP internationalized character string
+ preparation algorithms used by [RFC4517] and possible other technical
+ specifications defining LDAP syntaxes and/or matching rules.
+
+1.5. Relationship to X.500
+
+ LDAP is defined [RFC4510] in X.500 terms as an X.500 access
+ mechanism. As such, there is a strong desire for alignment between
+ LDAP and X.500 syntax and semantics. The character string
+ preparation algorithms described in this document are based upon
+ "Internationalized String Matching Rules for X.500" [XMATCH] proposal
+ to ITU/ISO Joint Study Group 2.
+
+1.6. Conventions and Terms
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in BCP 14 [RFC2119].
+
+ Character names in this document use the notation for code points and
+ names from the Unicode Standard [Unicode]. For example, the letter
+ "a" may be represented as either <U+0061> or <LATIN SMALL LETTER A>.
+ In the lists of mappings and the prohibited characters, the "U+" is
+
+
+
+
+
+Zeilenga Standards Track [Page 3]
+
+RFC 4518 LDAP: Internationalized String Preparation June 2006
+
+
+ left off to make the lists easier to read. The comments for
+ character ranges are shown in square brackets (such as "[CONTROL
+ CHARACTERS]") and do not come from the standard.
+
+ Note: a glossary of terms used in Unicode can be found in [Glossary].
+ Information on the Unicode character encoding model can be found in
+ [CharModel].
+
+ The term "combining mark", as used in this specification, refers to
+ any Unicode [Unicode] code point that has a mark property (Mn, Mc,
+ Me). Appendix A provides a definitive list of combining marks.
+
+2. String Preparation
+
+ The following six-step process SHALL be applied to each presented and
+ attribute value in preparation for character string matching rule
+ evaluation.
+
+ 1) Transcode
+ 2) Map
+ 3) Normalize
+ 4) Prohibit
+ 5) Check bidi
+ 6) Insignificant Character Handling
+
+ Failure in any step causes the assertion to evaluate to Undefined.
+
+ The character repertoire of this process is Unicode 3.2 [Unicode].
+
+ Note that this six-step process specification is intended to describe
+ expected matching behavior. Implementations are free to use
+ alternative processes so long as the matching rule evaluation
+ behavior provided is consistent with the behavior described by this
+ specification.
+
+2.1. Transcode
+
+ Each non-Unicode string value is transcoded to Unicode.
+
+ PrintableString [X.680] values are transcoded directly to Unicode.
+
+ UniversalString, UTF8String, and bmpString [X.680] values need not be
+ transcoded as they are Unicode-based strings (in the case of
+ bmpString, a subset of Unicode).
+
+ TeletexString [X.680] values are transcoded to Unicode. As there is
+ no standard for mapping TeletexString values to Unicode, the mapping
+ is left a local matter.
+
+
+
+Zeilenga Standards Track [Page 4]
+
+RFC 4518 LDAP: Internationalized String Preparation June 2006
+
+
+ For these and other reasons, use of TeletexString is NOT RECOMMENDED.
+
+ The output is the transcoded string.
+
+2.2. Map
+
+ SOFT HYPHEN (U+00AD) and MONGOLIAN TODO SOFT HYPHEN (U+1806) code
+ points are mapped to nothing. COMBINING GRAPHEME JOINER (U+034F) and
+ VARIATION SELECTORs (U+180B-180D, FF00-FE0F) code points are also
+ mapped to nothing. The OBJECT REPLACEMENT CHARACTER (U+FFFC) is
+ mapped to nothing.
+
+ CHARACTER TABULATION (U+0009), LINE FEED (LF) (U+000A), LINE
+ TABULATION (U+000B), FORM FEED (FF) (U+000C), CARRIAGE RETURN (CR)
+ (U+000D), and NEXT LINE (NEL) (U+0085) are mapped to SPACE (U+0020).
+
+ All other control code (e.g., Cc) points or code points with a
+ control function (e.g., Cf) are mapped to nothing. The following is
+ a complete list of these code points: U+0000-0008, 000E-001F, 007F-
+ 0084, 0086-009F, 06DD, 070F, 180E, 200C-200F, 202A-202E, 2060-2063,
+ 206A-206F, FEFF, FFF9-FFFB, 1D173-1D17A, E0001, E0020-E007F.
+
+ ZERO WIDTH SPACE (U+200B) is mapped to nothing. All other code
+ points with Separator (space, line, or paragraph) property (e.g., Zs,
+ Zl, or Zp) are mapped to SPACE (U+0020). The following is a complete
+ list of these code points: U+0020, 00A0, 1680, 2000-200A, 2028-2029,
+ 202F, 205F, 3000.
+
+ For case ignore, numeric, and stored prefix string matching rules,
+ characters are case folded per B.2 of [RFC3454].
+
+ The output is the mapped string.
+
+2.3. Normalize
+
+ The input string is to be normalized to Unicode Form KC
+ (compatibility composed) as described in [UAX15]. The output is the
+ normalized string.
+
+2.4. Prohibit
+
+ All Unassigned code points are prohibited. Unassigned code points
+ are listed in Table A.1 of [RFC3454].
+
+ Characters that, per Section 5.8 of [RFC3454], change display
+ properties or are deprecated are prohibited. These characters are
+ listed in Table C.8 of [RFC3454].
+
+
+
+
+Zeilenga Standards Track [Page 5]
+
+RFC 4518 LDAP: Internationalized String Preparation June 2006
+
+
+ Private Use code points are prohibited. These characters are listed
+ in Table C.3 of [RFC3454].
+
+ All non-character code points are prohibited. These code points are
+ listed in Table C.4 of [RFC3454].
+
+ Surrogate codes are prohibited. These characters are listed in Table
+ C.5 of [RFC3454].
+
+ The REPLACEMENT CHARACTER (U+FFFD) code point is prohibited.
+
+ The step fails if the input string contains any prohibited code
+ point. Otherwise, the output is the input string.
+
+2.5. Check bidi
+
+ Bidirectional characters are ignored.
+
+2.6. Insignificant Character Handling
+
+ In this step, the string is modified to ensure proper handling of
+ characters insignificant to the matching rule. This modification
+ differs from matching rule to matching rule.
+
+ Section 2.6.1 applies to case ignore and exact string matching.
+ Section 2.6.2 applies to numericString matching.
+ Section 2.6.3 applies to telephoneNumber matching.
+
+2.6.1. Insignificant Space Handling
+
+ For the purposes of this section, a space is defined to be the SPACE
+ (U+0020) code point followed by no combining marks.
+
+ NOTE - The previous steps ensure that the string cannot contain
+ any code points in the separator class, other than SPACE
+ (U+0020).
+
+ For input strings that are attribute values or non-substring
+ assertion values: If the input string contains no non-space
+ character, then the output is exactly two SPACEs. Otherwise (the
+ input string contains at least one non-space character), the string
+ is modified such that the string starts with exactly one space
+ character, ends with exactly one SPACE character, and any inner
+ (non-empty) sequence of space characters is replaced with exactly two
+ SPACE characters. For instance, the input strings
+ "foo<SPACE>bar<SPACE><SPACE>", result in the output
+ "<SPACE>foo<SPACE><SPACE>bar<SPACE>".
+
+
+
+
+Zeilenga Standards Track [Page 6]
+
+RFC 4518 LDAP: Internationalized String Preparation June 2006
+
+
+ For input strings that are substring assertion values: If the string
+ being prepared contains no non-space characters, then the output
+ string is exactly one SPACE. Otherwise, the following steps are
+ taken:
+
+ - If the input string is an initial substring, it is modified to
+ start with exactly one SPACE character;
+
+ - If the input string is an initial or an any substring that ends in
+ one or more space characters, it is modified to end with exactly
+ one SPACE character;
+
+ - If the input string is an any or a final substring that starts in
+ one or more space characters, it is modified to start with exactly
+ one SPACE character; and
+
+ - If the input string is a final substring, it is modified to end
+ with exactly one SPACE character.
+
+ For instance, for the input string "foo<SPACE>bar<SPACE><SPACE>" as
+ an initial substring, the output would be
+ "<SPACE>foo<SPACE><SPACE>bar<SPACE>". As an any or final substring,
+ the same input would result in "foo<SPACE>bar<SPACE>".
+
+ Appendix B discusses the rationale for the behavior.
+
+2.6.2. numericString Insignificant Character Handling
+
+ For the purposes of this section, a space is defined to be the SPACE
+ (U+0020) code point followed by no combining marks.
+
+ All spaces are regarded as insignificant and are to be removed.
+
+ For example, removal of spaces from the Form KC string:
+ "<SPACE><SPACE>123<SPACE><SPACE>456<SPACE><SPACE>"
+ would result in the output string:
+ "123456"
+ and the Form KC string:
+ "<SPACE><SPACE><SPACE>"
+ would result in the output string:
+ "" (an empty string).
+
+2.6.3. telephoneNumber Insignificant Character Handling
+
+ For the purposes of this section, a hyphen is defined to be a
+ HYPHEN-MINUS (U+002D), ARMENIAN HYPHEN (U+058A), HYPHEN (U+2010),
+ NON-BREAKING HYPHEN (U+2011), MINUS SIGN (U+2212), SMALL HYPHEN-MINUS
+ (U+FE63), or FULLWIDTH HYPHEN-MINUS (U+FF0D) code point followed by
+
+
+
+Zeilenga Standards Track [Page 7]
+
+RFC 4518 LDAP: Internationalized String Preparation June 2006
+
+
+ no combining marks and a space is defined to be the SPACE (U+0020)
+ code point followed by no combining marks.
+
+ All hyphens and spaces are considered insignificant and are to be
+ removed.
+
+ For example, removal of hyphens and spaces from the Form KC string:
+ "<SPACE><HYPHEN>123<SPACE><SPACE>456<SPACE><HYPHEN>"
+ would result in the output string:
+ "123456"
+ and the Form KC string:
+ "<HYPHEN><HYPHEN><HYPHEN>"
+ would result in the (empty) output string:
+ "".
+
+3. Security Considerations
+
+ "Preparation of Internationalized Strings ("stringprep")" [RFC3454]
+ security considerations generally apply to the algorithms described
+ here.
+
+4. Acknowledgements
+
+ The approach used in this document is based upon design principles
+ and algorithms described in "Preparation of Internationalized Strings
+ ('stringprep')" [RFC3454] by Paul Hoffman and Marc Blanchet. Some
+ additional guidance was drawn from Unicode Technical Standards,
+ Technical Reports, and Notes.
+
+ This document is a product of the IETF LDAP Revision (LDAPBIS)
+ Working Group.
+
+5. References
+
+5.1. Normative References
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [RFC3454] Hoffman, P. and M. Blanchet, "Preparation of
+ Internationalized Strings ("stringprep")", RFC 3454,
+ December 2002.
+
+ [RFC4510] Zeilenga, K., "Lightweight Directory Access Protocol
+ (LDAP): Technical Specification Road Map", RFC 4510,
+ June 2006.
+
+
+
+
+
+Zeilenga Standards Track [Page 8]
+
+RFC 4518 LDAP: Internationalized String Preparation June 2006
+
+
+ [RFC4517] Legg, S., Ed., "Lightweight Directory Access Protocol
+ (LDAP): Syntaxes and Matching Rules", RFC 4517, June
+ 2006.
+
+ [Unicode] The Unicode Consortium, "The Unicode Standard, Version
+ 3.2.0" is defined by "The Unicode Standard, Version
+ 3.0" (Reading, MA, Addison-Wesley, 2000. ISBN 0-201-
+ 61633-5), as amended by the "Unicode Standard Annex
+ #27: Unicode 3.1"
+ (http://www.unicode.org/reports/tr27/) and by the
+ "Unicode Standard Annex #28: Unicode 3.2"
+ (http://www.unicode.org/reports/tr28/).
+
+ [UAX15] Davis, M. and M. Duerst, "Unicode Standard Annex #15:
+ Unicode Normalization Forms, Version 3.2.0".
+ <http://www.unicode.org/unicode/reports/tr15/tr15-
+ 22.html>, March 2002.
+
+ [X.680] International Telecommunication Union -
+ Telecommunication Standardization Sector, "Abstract
+ Syntax Notation One (ASN.1) - Specification of Basic
+ Notation", X.680(2002) (also ISO/IEC 8824-1:2002).
+
+5.2. Informative References
+
+ [X.500] International Telecommunication Union -
+ Telecommunication Standardization Sector, "The
+ Directory -- Overview of concepts, models and
+ services," X.500(1993) (also ISO/IEC 9594-1:1994).
+
+ [X.501] International Telecommunication Union -
+ Telecommunication Standardization Sector, "The
+ Directory -- Models," X.501(1993) (also ISO/IEC 9594-
+ 2:1994).
+
+ [X.520] International Telecommunication Union -
+ Telecommunication Standardization Sector, "The
+ Directory: Selected Attribute Types", X.520(1993) (also
+ ISO/IEC 9594-6:1994).
+
+ [Glossary] The Unicode Consortium, "Unicode Glossary",
+ <http://www.unicode.org/glossary/>.
+
+ [CharModel] Whistler, K. and M. Davis, "Unicode Technical Report
+ #17, Character Encoding Model", UTR17,
+ <http://www.unicode.org/unicode/reports/tr17/>, August
+ 2000.
+
+
+
+
+Zeilenga Standards Track [Page 9]
+
+RFC 4518 LDAP: Internationalized String Preparation June 2006
+
+
+ [RFC3377] Hodges, J. and R. Morgan, "Lightweight Directory Access
+ Protocol (v3): Technical Specification", RFC 3377,
+ September 2002.
+
+ [RFC4515] Smith, M., Ed. and T. Howes, "Lightweight Directory
+ Access Protocol (LDAP): String Representation of Search
+ Filters", RFC 4515, June 2006.
+
+ [XMATCH] Zeilenga, K., "Internationalized String Matching Rules
+ for X.500", Work in Progress.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 10]
+
+RFC 4518 LDAP: Internationalized String Preparation June 2006
+
+
+Appendix A. Combining Marks
+
+ This appendix is normative.
+
+ This table was derived from Unicode [Unicode] data files; it lists
+ all code points with the Mn, Mc, or Me properties. This table is to
+ be considered definitive for the purposes of implementation of this
+ specification.
+
+ 0300-034F 0360-036F 0483-0486 0488-0489 0591-05A1
+ 05A3-05B9 05BB-05BC 05BF 05C1-05C2 05C4 064B-0655 0670
+ 06D6-06DC 06DE-06E4 06E7-06E8 06EA-06ED 0711 0730-074A
+ 07A6-07B0 0901-0903 093C 093E-094F 0951-0954 0962-0963
+ 0981-0983 09BC 09BE-09C4 09C7-09C8 09CB-09CD 09D7
+ 09E2-09E3 0A02 0A3C 0A3E-0A42 0A47-0A48 0A4B-0A4D
+ 0A70-0A71 0A81-0A83 0ABC 0ABE-0AC5 0AC7-0AC9 0ACB-0ACD
+ 0B01-0B03 0B3C 0B3E-0B43 0B47-0B48 0B4B-0B4D 0B56-0B57
+ 0B82 0BBE-0BC2 0BC6-0BC8 0BCA-0BCD 0BD7 0C01-0C03
+ 0C3E-0C44 0C46-0C48 0C4A-0C4D 0C55-0C56 0C82-0C83
+ 0CBE-0CC4 0CC6-0CC8 0CCA-0CCD 0CD5-0CD6 0D02-0D03
+ 0D3E-0D43 0D46-0D48 0D4A-0D4D 0D57 0D82-0D83 0DCA
+ 0DCF-0DD4 0DD6 0DD8-0DDF 0DF2-0DF3 0E31 0E34-0E3A
+ 0E47-0E4E 0EB1 0EB4-0EB9 0EBB-0EBC 0EC8-0ECD 0F18-0F19
+ 0F35 0F37 0F39 0F3E-0F3F 0F71-0F84 0F86-0F87 0F90-0F97
+ 0F99-0FBC 0FC6 102C-1032 1036-1039 1056-1059 1712-1714
+ 1732-1734 1752-1753 1772-1773 17B4-17D3 180B-180D 18A9
+ 20D0-20EA 302A-302F 3099-309A FB1E FE00-FE0F FE20-FE23
+ 1D165-1D169 1D16D-1D172 1D17B-1D182 1D185-1D18B
+ 1D1AA-1D1AD
+
+Appendix B. Substrings Matching
+
+ This appendix is non-normative.
+
+ In the absence of substrings matching, the insignificant space
+ handling for case ignore/exact matching could be simplified.
+ Specifically, the handling could be to require that all sequences of
+ one or more spaces be replaced with one space and, if the string
+ contains non-space characters, removal of all leading spaces and
+ trailing spaces.
+
+ In the presence of substrings matching, this simplified space
+ handling would lead to unexpected and undesirable matching behavior.
+ For instance:
+
+ 1) (CN=foo\20*\20bar) would match the CN value "foobar";
+
+
+
+
+
+Zeilenga Standards Track [Page 11]
+
+RFC 4518 LDAP: Internationalized String Preparation June 2006
+
+
+ 2) (CN=*\20foobar\20*) would match "foobar", but
+ (CN=*\20*foobar*\20*) would not.
+
+ Note to readers not familiar with LDAP substrings matching: the LDAP
+ filter [RFC4515] assertion (CN=A*B*C) says to "match any value (of
+ the attribute CN) that begins with A, contains B after A, ends with C
+ where C is also after B."
+
+ The first case illustrates that this simplified space handling would
+ cause leading and trailing spaces in substrings of the string to be
+ regarded as insignificant. However, only leading and trailing (as
+ well as multiple consecutive spaces) of the string (as a whole) are
+ insignificant.
+
+ The second case illustrates that this simplified space handling would
+ cause sub-partitioning failures. That is, if a prepared any
+ substring matches a partition of the attribute value, then an
+ assertion constructed by subdividing that substring into multiple
+ substrings should also match.
+
+ In designing an appropriate approach for space handling for
+ substrings matching, one must study key aspects of X.500 case
+ exact/ignore matching. X.520 [X.520] says:
+
+ The [substrings] rule returns TRUE if there is a partitioning of
+ the attribute value (into portions) such that:
+
+ - the specified substrings (initial, any, final) match
+ different portions of the value in the order of the strings
+ sequence;
+
+ - initial, if present, matches the first portion of the value;
+
+ - final, if present, matches the last portion of the value;
+
+ - any, if present, matches some arbitrary portion of the
+ value.
+
+ That is, the substrings assertion (CN=foo\20*\20bar) matches the
+ attribute value "foo<SPACE><SPACE>bar" as the value can be
+ partitioned into the portions "foo<SPACE>" and "<SPACE>bar" meeting
+ the above requirements.
+
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 12]
+
+RFC 4518 LDAP: Internationalized String Preparation June 2006
+
+
+ X.520 also says:
+
+ [T]he following spaces are regarded as not significant:
+
+ - leading spaces (i.e., those preceding the first character
+ that is not a space);
+
+ - trailing spaces (i.e., those following the last character
+ that is not a space);
+
+ - multiple consecutive spaces (these are taken as equivalent
+ to a single space character).
+
+ This statement applies to the assertion values and attribute values
+ as whole strings, and not individually to substrings of an assertion
+ value. In particular, the statements should be taken to mean that if
+ an assertion value and attribute value match without any
+ consideration to insignificant characters, then that assertion value
+ should also match any attribute value that differs only by inclusion
+ nor removal of insignificant characters.
+
+ Hence the assertion (CN=foo\20*\20bar) matches
+ "foo<SPACE><SPACE><SPACE>bar" and "foo<SPACE>bar" as these values
+ only differ from "foo<SPACE><SPACE>bar" by the inclusion or removal
+ of insignificant spaces.
+
+ Astute readers of this text will also note that there are special
+ cases where the specified space handling does not ignore spaces that
+ could be considered insignificant. For instance, the assertion
+ (CN=\20*\20*\20) does not match "<SPACE><SPACE><SPACE>"
+ (insignificant spaces present in value) or " " (insignificant spaces
+ not present in value). However, as these cases have no practical
+ application that cannot be met by simple assertions, e.g., (cn=\20),
+ and this minor anomaly can only be fully addressed by a preparation
+ algorithm to be used in conjunction with character-by-character
+ partitioning and matching, the anomaly is considered acceptable.
+
+Author's Address
+
+ Kurt D. Zeilenga
+ OpenLDAP Foundation
+
+ EMail: Kurt@OpenLDAP.org
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 13]
+
+RFC 4518 LDAP: Internationalized String Preparation June 2006
+
+
+Full Copyright Statement
+
+ Copyright (C) The Internet Society (2006).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
+ ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
+ INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
+ INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be
+ found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at
+ ietf-ipr@ietf.org.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is provided by the IETF
+ Administrative Support Activity (IASA).
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 14]
+
diff --git a/source4/heimdal/lib/wind/stringprep.c b/source4/heimdal/lib/wind/stringprep.c
new file mode 100644
index 0000000000..6e99cfc86b
--- /dev/null
+++ b/source4/heimdal/lib/wind/stringprep.c
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2004, 2006, 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include "windlocl.h"
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+RCSID("$Id$");
+
+/**
+ * Process a input UCS4 string according a string-prep profile.
+ *
+ * @param in input UCS4 string to process
+ * @param in_len length of the input string
+ * @param out output UCS4 string
+ * @param out_len length of the output string.
+ * @param flags stringprep profile.
+ *
+ * @return returns 0 on success, an wind error code otherwise
+ * @ingroup wind
+ */
+
+int
+wind_stringprep(const uint32_t *in, size_t in_len,
+ uint32_t *out, size_t *out_len,
+ wind_profile_flags flags)
+{
+ size_t tmp_len = in_len * 3;
+ uint32_t *tmp = malloc(tmp_len * sizeof(uint32_t));
+ int ret;
+ size_t olen;
+
+ if (tmp == NULL)
+ return ENOMEM;
+
+ ret = _wind_stringprep_map(in, in_len, tmp, &tmp_len, flags);
+ if (ret) {
+ free(tmp);
+ return ret;
+ }
+
+ olen = *out_len;
+ ret = _wind_stringprep_normalize(tmp, tmp_len, tmp, &olen);
+ if (ret) {
+ free(tmp);
+ return ret;
+ }
+ ret = _wind_stringprep_prohibited(tmp, olen, flags);
+ if (ret) {
+ free(tmp);
+ return ret;
+ }
+ ret = _wind_stringprep_testbidi(tmp, olen, flags);
+ if (ret) {
+ free(tmp);
+ return ret;
+ }
+
+ /* Insignificant Character Handling for ldap-prep */
+ if (flags & WIND_PROFILE_LDAP_CASE_EXACT_ATTRIBUTE) {
+ ret = _wind_ldap_case_exact_attribute(tmp, olen, out, out_len);
+#if 0
+ } else if (flags & WIND_PROFILE_LDAP_CASE_EXACT_ASSERTION) {
+ } else if (flags & WIND_PROFILE_LDAP_NUMERIC) {
+ } else if (flags & WIND_PROFILE_LDAP_TELEPHONE) {
+#endif
+ } else {
+ memcpy(out, tmp, sizeof(out[0]) * olen);
+ *out_len = olen;
+ }
+ free(tmp);
+
+ return ret;
+}
+
+static struct {
+ const char *name;
+ wind_profile_flags flags;
+} profiles[] = {
+ { "nameprep", WIND_PROFILE_NAME },
+ { "saslprep", WIND_PROFILE_SASL },
+ { "ldapprep", WIND_PROFILE_LDAP }
+};
+
+/**
+ * Try to find the profile given a name.
+ *
+ * @param name name of the profile.
+ * @param flags the resulting profile.
+ *
+ * @return returns 0 on success, an wind error code otherwise
+ * @ingroup wind
+ */
+
+int
+wind_profile(const char *name, wind_profile_flags *flags)
+{
+ unsigned int i;
+
+ for (i = 0; i < sizeof(profiles)/sizeof(profiles[0]); i++) {
+ if (strcasecmp(profiles[i].name, name) == 0) {
+ *flags = profiles[i].flags;
+ return 0;
+ }
+ }
+ return WIND_ERR_NO_PROFILE;
+}
diff --git a/source4/heimdal/lib/wind/stringprep.py b/source4/heimdal/lib/wind/stringprep.py
new file mode 100644
index 0000000000..d64686a252
--- /dev/null
+++ b/source4/heimdal/lib/wind/stringprep.py
@@ -0,0 +1,90 @@
+#!/usr/local/bin/python
+# -*- coding: iso-8859-1 -*-
+
+# $Id$
+
+# Copyright (c) 2008 Kungliga Tekniska Högskolan
+# (Royal Institute of Technology, Stockholm, Sweden).
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the Institute nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+import re
+import string
+
+def _merge_table(res, source):
+ for table in source.keys():
+ res[table] = res.get(table, []) + source.get(table, [])
+
+name_error = ['C.1.2', 'C.2.2', 'C.3', 'C.4', 'C.5', 'C.6', 'C.7', 'C.8', 'C.9']
+ldap_error = ['A.1', 'C.3', 'C.4', 'C.5', 'C.8', 'rfc4518-error' ]
+sasl_error = ['C.1.2', 'C.2.1', 'C.2.2', 'C.3', 'C.4', 'C.5', 'C.6', 'C.7', 'C.8', 'C.9']
+
+name_map = ['B.1', 'B.2']
+ldap_map = ['rfc4518-map', 'B.2']
+sasl_map = ['C.1.2', 'B.1']
+
+def symbols(tabledict, tables):
+ """return CPP symbols to use for this symbols"""
+ list = []
+ for x in tables:
+ list = list + tabledict.get(x, [])
+ if len(list) == 0:
+ return ""
+ return "|".join(map(lambda x: "WIND_PROFILE_%s" % (string.upper(x)), list))
+
+def get_errorlist():
+ d = dict()
+ _merge_table(d, dict(map(lambda x: [x, ['name']], name_error)))
+ _merge_table(d, dict(map(lambda x: [x, ['ldap']], ldap_error)))
+ _merge_table(d, dict(map(lambda x: [x, ['sasl']], sasl_error)))
+ return d
+
+def get_maplist():
+ d = dict()
+ _merge_table(d, dict(map(lambda x: [x, ['name']], name_map)))
+ _merge_table(d, dict(map(lambda x: [x, ['ldap']], ldap_map)))
+ _merge_table(d, dict(map(lambda x: [x, ['sasl']], sasl_map)))
+ return d
+
+def sort_merge_trans(trans):
+ trans.sort()
+ ret = []
+ last = 0
+ for x in trans:
+ if last:
+ if last[0] == x[0]:
+ last = (last[0], last[1], last[2], last[3] + x[3])
+ else:
+ ret.append(last)
+ last = x
+ else:
+ last = x
+ if last:
+ ret.append(last)
+ return ret
diff --git a/source4/heimdal/lib/wind/utf8.c b/source4/heimdal/lib/wind/utf8.c
new file mode 100644
index 0000000000..f563b79107
--- /dev/null
+++ b/source4/heimdal/lib/wind/utf8.c
@@ -0,0 +1,520 @@
+/*
+ * Copyright (c) 2004, 2006, 2007, 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include "windlocl.h"
+
+RCSID("$Id$");
+
+static int
+utf8toutf32(const unsigned char **pp, uint32_t *out)
+{
+ const unsigned char *p = *pp;
+ unsigned c = *p;
+
+ if (c & 0x80) {
+ if ((c & 0xE0) == 0xC0) {
+ const unsigned c2 = *++p;
+ if ((c2 & 0xC0) == 0x80) {
+ *out = ((c & 0x1F) << 6)
+ | (c2 & 0x3F);
+ } else {
+ return WIND_ERR_INVALID_UTF8;
+ }
+ } else if ((c & 0xF0) == 0xE0) {
+ const unsigned c2 = *++p;
+ if ((c2 & 0xC0) == 0x80) {
+ const unsigned c3 = *++p;
+ if ((c3 & 0xC0) == 0x80) {
+ *out = ((c & 0x0F) << 12)
+ | ((c2 & 0x3F) << 6)
+ | (c3 & 0x3F);
+ } else {
+ return WIND_ERR_INVALID_UTF8;
+ }
+ } else {
+ return WIND_ERR_INVALID_UTF8;
+ }
+ } else if ((c & 0xF8) == 0xF0) {
+ const unsigned c2 = *++p;
+ if ((c2 & 0xC0) == 0x80) {
+ const unsigned c3 = *++p;
+ if ((c3 & 0xC0) == 0x80) {
+ const unsigned c4 = *++p;
+ if ((c4 & 0xC0) == 0x80) {
+ *out = ((c & 0x07) << 18)
+ | ((c2 & 0x3F) << 12)
+ | ((c3 & 0x3F) << 6)
+ | (c4 & 0x3F);
+ } else {
+ return WIND_ERR_INVALID_UTF8;
+ }
+ } else {
+ return WIND_ERR_INVALID_UTF8;
+ }
+ } else {
+ return WIND_ERR_INVALID_UTF8;
+ }
+ } else {
+ return WIND_ERR_INVALID_UTF8;
+ }
+ } else {
+ *out = c;
+ }
+
+ *pp = p;
+
+ return 0;
+}
+
+/**
+ * Convert an UTF-8 string to an UCS4 string.
+ *
+ * @param in an UTF-8 string to convert.
+ * @param out the resulting UCS4 strint, must be at least
+ * wind_utf8ucs4_length() long. If out is NULL, the function will
+ * calculate the needed space for the out variable (just like
+ * wind_utf8ucs4_length()).
+ * @param out_len before processing out_len should be the length of
+ * the out variable, after processing it will be the length of the out
+ * string.
+ *
+ * @return returns 0 on success, an wind error code otherwise
+ * @ingroup wind
+ */
+
+int
+wind_utf8ucs4(const char *in, uint32_t *out, size_t *out_len)
+{
+ const unsigned char *p;
+ size_t o = 0;
+ int ret;
+
+ for (p = (const unsigned char *)in; *p != '\0'; ++p) {
+ uint32_t u;
+
+ ret = utf8toutf32(&p, &u);
+ if (ret)
+ return ret;
+
+ if (out) {
+ if (o >= *out_len)
+ return WIND_ERR_OVERRUN;
+ out[o] = u;
+ }
+ o++;
+ }
+ *out_len = o;
+ return 0;
+}
+
+/**
+ * Calculate the length of from converting a UTF-8 string to a UCS4
+ * string.
+ *
+ * @param in an UTF-8 string to convert.
+ * @param out_len the length of the resulting UCS4 string.
+ *
+ * @return returns 0 on success, an wind error code otherwise
+ * @ingroup wind
+ */
+
+int
+wind_utf8ucs4_length(const char *in, size_t *out_len)
+{
+ return wind_utf8ucs4(in, NULL, out_len);
+}
+
+static const char first_char[4] =
+ { 0x00, 0xC0, 0xE0, 0xF0 };
+
+/**
+ * Convert an UCS4 string to a UTF-8 string.
+ *
+ * @param in an UCS4 string to convert.
+ * @param in_len the length input array.
+
+ * @param out the resulting UTF-8 strint, must be at least
+ * wind_ucs4utf8_length() + 1 long (the extra char for the NUL). If
+ * out is NULL, the function will calculate the needed space for the
+ * out variable (just like wind_ucs4utf8_length()).
+
+ * @param out_len before processing out_len should be the length of
+ * the out variable, after processing it will be the length of the out
+ * string.
+ *
+ * @return returns 0 on success, an wind error code otherwise
+ * @ingroup wind
+ */
+
+int
+wind_ucs4utf8(const uint32_t *in, size_t in_len, char *out, size_t *out_len)
+{
+ uint32_t ch;
+ size_t i, len, o;
+
+ for (o = 0, i = 0; i < in_len; i++) {
+ ch = in[i];
+
+ if (ch < 0x80) {
+ len = 1;
+ } else if (ch < 0x800) {
+ len = 2;
+ } else if (ch < 0x10000) {
+ len = 3;
+ } else if (ch <= 0x10FFFF) {
+ len = 4;
+ } else
+ return WIND_ERR_INVALID_UTF32;
+
+ o += len;
+
+ if (out) {
+ if (o >= *out_len)
+ return WIND_ERR_OVERRUN;
+
+ switch(len) {
+ case 4:
+ out[3] = (ch | 0x80) & 0xbf;
+ ch = ch << 6;
+ case 3:
+ out[2] = (ch | 0x80) & 0xbf;
+ ch = ch << 6;
+ case 2:
+ out[1] = (ch | 0x80) & 0xbf;
+ ch = ch << 6;
+ case 1:
+ out[0] = ch | first_char[len - 1];
+ }
+ }
+ out += len;
+ }
+ if (out) {
+ if (o + 1 >= *out_len)
+ return WIND_ERR_OVERRUN;
+ *out = '\0';
+ }
+ *out_len = o;
+ return 0;
+}
+
+/**
+ * Calculate the length of from converting a UCS4 string to an UTF-8 string.
+ *
+ * @param in an UCS4 string to convert.
+ * @param in_len the length of UCS4 string to convert.
+ * @param out_len the length of the resulting UTF-8 string.
+ *
+ * @return returns 0 on success, an wind error code otherwise
+ * @ingroup wind
+ */
+
+int
+wind_ucs4utf8_length(const uint32_t *in, size_t in_len, size_t *out_len)
+{
+ return wind_ucs4utf8(in, in_len, NULL, out_len);
+}
+
+/**
+ * Read in an UCS2 from a buffer.
+ *
+ * @param ptr The input buffer to read from.
+ * @param len the length of the input buffer.
+ * @param flags Flags to control the behavior of the function.
+ * @param out the output UCS2, the array must be at least out/2 long.
+ * @param out_len the output length
+ *
+ * @return returns 0 on success, an wind error code otherwise.
+ * @ingroup wind
+ */
+
+int
+wind_ucs2read(const void *ptr, size_t len, unsigned int *flags,
+ uint16_t *out, size_t *out_len)
+{
+ const unsigned char *p = ptr;
+ int little = ((*flags) & WIND_RW_LE);
+ size_t olen = *out_len;
+
+ /** if len is zero, flags are unchanged */
+ if (len == 0) {
+ *out_len = 0;
+ return 0;
+ }
+
+ /** if len is odd, WIND_ERR_LENGTH_NOT_MOD2 is returned */
+ if (len & 1)
+ return WIND_ERR_LENGTH_NOT_MOD2;
+
+ /**
+ * If the flags WIND_RW_BOM is set, check for BOM. If not BOM is
+ * found, check is LE/BE flag is already and use that otherwise
+ * fail with WIND_ERR_NO_BOM. When done, clear WIND_RW_BOM and
+ * the LE/BE flag and set the resulting LE/BE flag.
+ */
+ if ((*flags) & WIND_RW_BOM) {
+ uint16_t bom = (p[0] << 8) + p[1];
+ if (bom == 0xfffe || bom == 0xfeff) {
+ little = (bom == 0xfffe);
+ p += 2;
+ len -= 2;
+ } else if (((*flags) & (WIND_RW_LE|WIND_RW_BE)) != 0) {
+ /* little already set */
+ } else
+ return WIND_ERR_NO_BOM;
+ *flags = ((*flags) & ~(WIND_RW_BOM|WIND_RW_LE|WIND_RW_BE));
+ *flags |= little ? WIND_RW_LE : WIND_RW_BE;
+ }
+
+ while (len) {
+ if (olen < 1)
+ return WIND_ERR_OVERRUN;
+ if (little)
+ *out = (p[1] << 8) + p[0];
+ else
+ *out = (p[0] << 8) + p[1];
+ out++; p += 2; len -= 2; olen--;
+ }
+ *out_len -= olen;
+ return 0;
+}
+
+/**
+ * Write an UCS2 string to a buffer.
+ *
+ * @param in The input UCS2 string.
+ * @param in_len the length of the input buffer.
+ * @param flags Flags to control the behavior of the function.
+ * @param ptr The input buffer to write to, the array must be at least
+ * (in + 1) * 2 bytes long.
+ * @param out_len the output length
+ *
+ * @return returns 0 on success, an wind error code otherwise.
+ * @ingroup wind
+ */
+
+int
+wind_ucs2write(const uint16_t *in, size_t in_len, unsigned int *flags,
+ void *ptr, size_t *out_len)
+{
+ unsigned char *p = ptr;
+ size_t len = *out_len;
+
+ /** If in buffer is not of length be mod 2, WIND_ERR_LENGTH_NOT_MOD2 is returned*/
+ if (len & 1)
+ return WIND_ERR_LENGTH_NOT_MOD2;
+
+ /** On zero input length, flags are preserved */
+ if (in_len == 0) {
+ *out_len = 0;
+ return 0;
+ }
+ /** If flags have WIND_RW_BOM set, the byte order mark is written
+ * first to the output data */
+ if ((*flags) & WIND_RW_BOM) {
+ uint16_t bom = 0xfffe;
+
+ if (len < 2)
+ return WIND_ERR_OVERRUN;
+
+ if ((*flags) & WIND_RW_LE) {
+ p[0] = (bom >> 8) & 0xff;
+ p[1] = (bom ) & 0xff;
+ } else {
+ p[1] = (bom ) & 0xff;
+ p[0] = (bom >> 8) & 0xff;
+ }
+ len -= 2;
+ }
+
+ while (in_len) {
+ /** If the output wont fit into out_len, WIND_ERR_OVERRUN is returned */
+ if (len < 2)
+ return WIND_ERR_OVERRUN;
+ if ((*flags) & WIND_RW_LE) {
+ p[0] = (in[0] >> 8) & 0xff;
+ p[1] = (in[0] ) & 0xff;
+ } else {
+ p[1] = (in[0] ) & 0xff;
+ p[0] = (in[0] >> 8) & 0xff;
+ }
+ len -= 2;
+ in_len--;
+ p += 2;
+ in++;
+ }
+ *out_len -= len;
+ return 0;
+}
+
+
+/**
+ * Convert an UTF-8 string to an UCS2 string.
+ *
+ * @param in an UTF-8 string to convert.
+ * @param out the resulting UCS2 strint, must be at least
+ * wind_utf8ucs2_length() long. If out is NULL, the function will
+ * calculate the needed space for the out variable (just like
+ * wind_utf8ucs2_length()).
+ * @param out_len before processing out_len should be the length of
+ * the out variable, after processing it will be the length of the out
+ * string.
+ *
+ * @return returns 0 on success, an wind error code otherwise
+ * @ingroup wind
+ */
+
+int
+wind_utf8ucs2(const char *in, uint16_t *out, size_t *out_len)
+{
+ const unsigned char *p;
+ size_t o = 0;
+ int ret;
+
+ for (p = (const unsigned char *)in; *p != '\0'; ++p) {
+ uint32_t u;
+
+ ret = utf8toutf32(&p, &u);
+ if (ret)
+ return ret;
+
+ if (u & 0xffff0000)
+ return WIND_ERR_NOT_UTF16;
+
+ if (out) {
+ if (o >= *out_len)
+ return WIND_ERR_OVERRUN;
+ out[o] = u;
+ }
+ o++;
+ }
+ *out_len = o;
+ return 0;
+}
+
+/**
+ * Calculate the length of from converting a UTF-8 string to a UCS2
+ * string.
+ *
+ * @param in an UTF-8 string to convert.
+ * @param out_len the length of the resulting UCS4 string.
+ *
+ * @return returns 0 on success, an wind error code otherwise
+ * @ingroup wind
+ */
+
+int
+wind_utf8ucs2_length(const char *in, size_t *out_len)
+{
+ return wind_utf8ucs2(in, NULL, out_len);
+}
+
+/**
+ * Convert an UCS2 string to a UTF-8 string.
+ *
+ * @param in an UCS2 string to convert.
+ * @param in_len the length of the in UCS2 string.
+ * @param out the resulting UTF-8 strint, must be at least
+ * wind_ucs2utf8_length() long. If out is NULL, the function will
+ * calculate the needed space for the out variable (just like
+ * wind_ucs2utf8_length()).
+ * @param out_len before processing out_len should be the length of
+ * the out variable, after processing it will be the length of the out
+ * string.
+ *
+ * @return returns 0 on success, an wind error code otherwise
+ * @ingroup wind
+ */
+
+int
+wind_ucs2utf8(const uint16_t *in, size_t in_len, char *out, size_t *out_len)
+{
+ uint16_t ch;
+ size_t i, len, o;
+
+ for (o = 0, i = 0; i < in_len; i++) {
+ ch = in[i];
+
+ if (ch < 0x80) {
+ len = 1;
+ } else if (ch < 0x800) {
+ len = 2;
+ } else
+ len = 3;
+
+ o += len;
+
+ if (out) {
+ if (o >= *out_len)
+ return WIND_ERR_OVERRUN;
+
+ switch(len) {
+ case 3:
+ out[2] = (ch | 0x80) & 0xbf;
+ ch = ch << 6;
+ case 2:
+ out[1] = (ch | 0x80) & 0xbf;
+ ch = ch << 6;
+ case 1:
+ out[0] = ch | first_char[len - 1];
+ }
+ out += len;
+ }
+ }
+ if (out) {
+ if (o >= *out_len)
+ return WIND_ERR_OVERRUN;
+ *out = '\0';
+ }
+ *out_len = o;
+ return 0;
+}
+
+/**
+ * Calculate the length of from converting a UCS2 string to an UTF-8 string.
+ *
+ * @param in an UCS2 string to convert.
+ * @param in_len an UCS2 string length to convert.
+ * @param out_len the length of the resulting UTF-8 string.
+ *
+ * @return returns 0 on success, an wind error code otherwise
+ * @ingroup wind
+ */
+
+int
+wind_ucs2utf8_length(const uint16_t *in, size_t in_len, size_t *out_len)
+{
+ return wind_ucs2utf8(in, in_len, NULL, out_len);
+}
diff --git a/source4/heimdal/lib/wind/util.py b/source4/heimdal/lib/wind/util.py
new file mode 100644
index 0000000000..a05c7398f5
--- /dev/null
+++ b/source4/heimdal/lib/wind/util.py
@@ -0,0 +1,48 @@
+#!/usr/local/bin/python
+# -*- coding: iso-8859-1 -*-
+
+# $Id$
+
+# Copyright (c) 2004 Kungliga Tekniska Högskolan
+# (Royal Institute of Technology, Stockholm, Sweden).
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the Institute nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+def subList(l, sl) :
+ """return the index of sl in l or None"""
+ lLen = len(l)
+ slLen = len(sl)
+ for i in range(lLen - slLen + 1):
+ j = 0
+ while j < slLen and l[i + j] == sl[j]:
+ j += 1
+ if j == slLen:
+ return i
+ return None
+
diff --git a/source4/heimdal/lib/wind/wind.h b/source4/heimdal/lib/wind/wind.h
new file mode 100644
index 0000000000..e890bebeee
--- /dev/null
+++ b/source4/heimdal/lib/wind/wind.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef _WIND_H_
+#define _WIND_H_
+
+#include <stddef.h>
+#include <krb5-types.h>
+
+#include <wind_err.h>
+
+typedef unsigned int wind_profile_flags;
+
+#define WIND_PROFILE_NAME 0x00000001
+#define WIND_PROFILE_SASL 0x00000002
+#define WIND_PROFILE_LDAP 0x00000004
+
+#define WIND_PROFILE_LDAP_CASE_EXACT_ATTRIBUTE 0x00010000
+#define WIND_PROFILE_LDAP_CASE_EXACT_ASSERTION 0x00020000
+#define WIND_PROFILE_LDAP_NUMERIC 0x00040000
+#define WIND_PROFILE_LDAP_TELEPHONE 0x00080000
+
+
+/* flags to wind_ucs2read/wind_ucs2write */
+#define WIND_RW_LE 1
+#define WIND_RW_BE 2
+#define WIND_RW_BOM 4
+
+int wind_stringprep(const uint32_t *, size_t,
+ uint32_t *, size_t *,
+ wind_profile_flags);
+int wind_profile(const char *, wind_profile_flags *);
+
+int wind_punycode_label_toascii(const uint32_t *, size_t,
+ char *, size_t *);
+
+int wind_utf8ucs4(const char *, uint32_t *, size_t *);
+int wind_utf8ucs4_length(const char *, size_t *);
+
+int wind_ucs4utf8(const uint32_t *, size_t, char *, size_t *);
+int wind_ucs4utf8_length(const uint32_t *, size_t, size_t *);
+
+int wind_utf8ucs2(const char *, uint16_t *, size_t *);
+int wind_utf8ucs2_length(const char *, size_t *);
+
+int wind_ucs2utf8(const uint16_t *, size_t, char *, size_t *);
+int wind_ucs2utf8_length(const uint16_t *, size_t, size_t *);
+
+
+int wind_ucs2read(const void *, size_t, unsigned int *, uint16_t *, size_t *);
+int wind_ucs2write(const uint16_t *, size_t, unsigned int *, void *, size_t *);
+
+#endif /* _WIND_H_ */
diff --git a/source4/heimdal/lib/wind/wind_err.et b/source4/heimdal/lib/wind/wind_err.et
new file mode 100644
index 0000000000..f90c252e7d
--- /dev/null
+++ b/source4/heimdal/lib/wind/wind_err.et
@@ -0,0 +1,23 @@
+#
+# Error messages for the wind library
+#
+# This might look like a com_err file, but is not
+#
+id "$Id$"
+
+error_table wind
+
+prefix WIND_ERR
+error_code NONE, "No error"
+error_code NO_PROFILE, "No such profile"
+error_code OVERRUN, "Buffer overrun"
+error_code UNDERUN, "Buffer underrun"
+error_code LENGTH_NOT_MOD2, "Lenght not mod2"
+error_code LENGTH_NOT_MOD4, "Lenght not mod4"
+error_code INVALID_UTF8, "Invalid UTF-8 combination in string"
+error_code INVALID_UTF16, "Invalid UTF-16 combination in string"
+error_code INVALID_UTF32, "Invalid UTF-32 combination in string"
+error_code NO_BOM, "No byte order mark (BOM) in string"
+error_code NOT_UTF16, "Code can't be represented as UTF-16"
+
+end
diff --git a/source4/heimdal/lib/wind/windlocl.h b/source4/heimdal/lib/wind/windlocl.h
new file mode 100644
index 0000000000..fb5e0b25da
--- /dev/null
+++ b/source4/heimdal/lib/wind/windlocl.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef _WINDLOCL_H_
+#define _WINDLOCL_H_
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <krb5-types.h>
+
+#include "wind.h"
+#include "wind_err.h"
+
+int _wind_combining_class(uint32_t);
+
+int _wind_stringprep_testbidi(const uint32_t *, size_t, wind_profile_flags);
+
+int _wind_stringprep_error(const uint32_t, wind_profile_flags);
+
+int _wind_stringprep_prohibited(const uint32_t *, size_t, wind_profile_flags);
+
+int _wind_stringprep_map(const uint32_t *, size_t,
+ uint32_t *, size_t *,
+ wind_profile_flags);
+
+int _wind_stringprep_normalize(const uint32_t *, size_t, uint32_t *, size_t *);
+
+int _wind_ldap_case_exact_attribute(const uint32_t *, size_t,
+ uint32_t *, size_t *);
+
+
+#endif /* _WINDLOCL_H_ */
diff --git a/source4/heimdal_build/asn1_compile_wrapper.sh b/source4/heimdal_build/asn1_compile_wrapper.sh
new file mode 100755
index 0000000000..c449bf8065
--- /dev/null
+++ b/source4/heimdal_build/asn1_compile_wrapper.sh
@@ -0,0 +1,59 @@
+#!/bin/sh
+#
+
+SELF=$0
+SELFDIR=`dirname ${SELF}`
+
+BUILDDIR=$1
+DESTDIR=$2
+
+CMD=$3
+FILE=$4
+NAME=$5
+shift 5
+OPTIONS="$@"
+
+test -z "${BUILDDIR}" && {
+ echo "${SELF}:BUILDDIR: '${BUILDDIR}'" >&2;
+ exit 1;
+}
+
+test -z "${DESTDIR}" && {
+ echo "${SELF}:DESTDIR: '${DESTDIR}'" >&2;
+ exit 1;
+}
+
+test -z "${CMD}" && {
+ echo "${SELF}:CMD: '${CMD}'" >&2;
+ exit 1;
+}
+
+test -z "${FILE}" && {
+ echo "${SELF}:FILE: '${FILE}'" >&2;
+ exit 1;
+}
+
+test -z "${NAME}" && {
+ echo "${SELF}:NAME: '${NAME}'" >&2;
+ exit 1;
+}
+
+CURDIR=`pwd`
+
+cd ${BUILDDIR} && {
+ ABS_BUILDDIR=`pwd`
+ cd ${CURDIR}
+} || {
+ echo "${SELF}:cannot cd into '${BUILDDIR}'" >&2;
+ exit 1;
+}
+
+cd ${DESTDIR} && {
+ ${ABS_BUILDDIR}/${CMD} ${OPTIONS} ${FILE} ${NAME} >&2 || exit 1;
+ cd ${CURDIR}
+} || {
+ echo "${SELF}:cannot cd into '${BUILDDIR}'" >&2;
+ exit 1;
+}
+
+exit 0;
diff --git a/source4/heimdal_build/asn1_deps.pl b/source4/heimdal_build/asn1_deps.pl
new file mode 100755
index 0000000000..6c4d10d262
--- /dev/null
+++ b/source4/heimdal_build/asn1_deps.pl
@@ -0,0 +1,111 @@
+#!/usr/bin/perl
+# Generate make dependency rules for asn1 files
+# Jelmer Vernooij <jelmer@samba.org> 2005
+# Andrew Bartlett <abartlet@samba.org> 2006
+# Stefan Metzmacher <metze@samba.org> 2007
+# GPL
+
+use File::Basename;
+
+my $file = shift;
+my $prefix = shift;
+my $dirname = shift;
+my $options = join(' ', @ARGV);
+my $x_file;
+my @x_files = ();
+my $c_file;
+my @c_files = ();
+my $o_file;
+my @o_files = ();
+my $import;
+my @imports = ();
+my $dep;
+my @deps = ();
+
+$basename = basename($file);
+if (not defined $options) {
+ $options = "";
+}
+
+my $header = "$dirname/$prefix.h";
+
+print "basics:: $header\n";
+print "$header: \$(heimdalsrcdir)/$file \$(ASN1C)\n";
+print "\t\@echo \"Compiling ASN1 file \$(heimdalsrcdir)/$file\"\n";
+print "\t\@\$(heimdalbuildsrcdir)/asn1_compile_wrapper.sh \$(builddir) $dirname \$(ASN1C) \$(call abspath,\$(heimdalsrcdir)/$file) $prefix $options\n\n";
+
+open(IN,"heimdal/$file") or die("Can't open heimdal/$file: $!");
+my @lines = <IN>;
+close(IN);
+foreach my $line (@lines) {
+ if ($line =~ /^([\w]+[\w\-]+)(\s+OBJECT IDENTIFIER)?\s*::=/) {
+ my $output = $1;
+ $output =~ s/-/_/g;
+ $c_file = "$dirname/asn1_$output.c";
+ $x_file = "$dirname/asn1_$output.x";
+ $o_file = "$dirname/asn1_$output.o";
+ print "$x_file: $header\n";
+ print "$c_file: $dirname/asn1_$output.x\n";
+ print "\t\@echo \"#include \\\"config.h\\\"\" > $c_file && cat $x_file >> $c_file\n\n";
+ push @x_files, $x_file;
+ push @c_files, $c_file;
+ push @o_files, $o_file;
+ } elsif ($line =~ /^(\s*IMPORT)([\w\,\s])*(\s+FROM\s+)([\w]+[\w\-]+);/) {
+ $import = $line;
+ chomp $import;
+ push @imports, $import;
+ $import = undef;
+ } elsif ($line =~ /^(\s*IMPORT).*/) {
+ $import = $line;
+ chomp $import;
+ } elsif (defined($import) and ($line =~ /;/)) {
+ $import .= $line;
+ chomp $import;
+ push @imports, $import;
+ $import = undef;
+ } elsif (defined($import)) {
+ $import .= $line;
+ chomp $import;
+ }
+}
+
+foreach $import (@imports) {
+ next unless ($import =~ /^(\s*IMPORT)([\w\,\s])*(\s+FROM\s+)([\w]+[\w\-]+);/);
+
+ my @froms = split (/\s+FROM\s+/, $import);
+ foreach my $from (@froms) {
+ next if ($from =~ /^(\s*IMPORT).*/);
+ if ($from =~ /^(\w+)/) {
+ my $f = $1;
+ $dep = 'HEIMDAL_'.uc($f).'_ASN1';
+ push @deps, $dep;
+ }
+ }
+}
+
+unshift @deps, "HEIMDAL_HEIM_ASN1" unless grep /HEIMDAL_HEIM_ASN1/, @deps;
+my $depstr = join(' ', @deps);
+
+print '[SUBSYSTEM::HEIMDAL_'.uc($prefix).']'."\n";
+print "CFLAGS = -Iheimdal_build -Iheimdal/lib/roken -I$dirname\n";
+print "PUBLIC_DEPENDENCIES = $depstr\n\n";
+
+print "HEIMDAL_".uc($prefix)."_OBJ_FILES = ";
+foreach $o_file (@o_files) {
+ print "\\\n\t$o_file";
+}
+
+print "\n\n";
+
+print "clean:: \n";
+print "\t\@echo \"Deleting ASN1 output files generated from $file\"\n";
+print "\t\@rm -f $header\n";
+foreach $c_file (@c_files) {
+ print "\t\@rm -f $c_file\n";
+}
+foreach $x_file (@x_files) {
+ print "\t\@rm -f $x_file\n";
+}
+print "\t\@rm -f $dirname/$prefix\_files\n";
+print "\t\@rm -f $dirname/$prefix\.h\n";
+print "\n";
diff --git a/source4/heimdal_build/config.h b/source4/heimdal_build/config.h
new file mode 100644
index 0000000000..6a82637b2d
--- /dev/null
+++ b/source4/heimdal_build/config.h
@@ -0,0 +1,30 @@
+/*
+ this is a replacement config.h for building the heimdal parts of the
+ Samba source tree
+*/
+
+#ifndef HAVE_HEIMDAL_CONFIG_H
+#define HAVE_HEIMDAL_CONFIG_H
+
+#include "include/config.h"
+#include "../replace/replace.h"
+
+#if !defined(HAVE_DIRFD) && !defined(HAVE_DIRFD_DECL) && !defined(dirfd)
+#define dirfd(d) (-1)
+#endif
+
+#define RCSID(msg) struct __rcsid { int __rcsdi; }
+#define KRB5
+
+/* This needs to be defined for roken too */
+#ifdef VOID_RETSIGTYPE
+#define SIGRETURN(x) return
+#else
+#define SIGRETURN(x) return (RETSIGTYPE)(x)
+#endif
+
+#define HDB_DB_DIR ""
+
+#undef HAVE_KRB5_ENCRYPT_BLOCK
+
+#endif
diff --git a/source4/heimdal_build/config.m4 b/source4/heimdal_build/config.m4
new file mode 100644
index 0000000000..99aed8c093
--- /dev/null
+++ b/source4/heimdal_build/config.m4
@@ -0,0 +1,23 @@
+
+external_heimdal=no
+AC_MSG_CHECKING([Whether to use external heimdal libraries])
+AC_ARG_ENABLE(external-heimdal,
+[ --enable-external-heimdal Enable external heimdal libraries (experimental,default=no)],
+[ external_heimdal=$enableval ],
+[ external_heimdal=no ])
+AC_MSG_RESULT($external_heimdal)
+
+if test x"$external_heimdal" = x"yes"; then
+
+# external_heimdal_start
+m4_include(heimdal_build/external.m4)
+# external_heimdal_end
+
+else
+
+# internal_heimdal_start
+m4_include(heimdal_build/internal.m4)
+# internal_heimdal_end
+
+fi
+
diff --git a/source4/heimdal_build/crypto-headers.h b/source4/heimdal_build/crypto-headers.h
new file mode 100644
index 0000000000..6c3949c993
--- /dev/null
+++ b/source4/heimdal_build/crypto-headers.h
@@ -0,0 +1,20 @@
+#ifndef __crypto_headers_h__
+#define __crypto_headers_h__
+#ifdef KRB5
+#include <krb5-types.h>
+#endif
+#include <hcrypto/evp.h>
+#include <hcrypto/des.h>
+#include <hcrypto/md2.h>
+#include <hcrypto/md4.h>
+#include <hcrypto/md5.h>
+#include <hcrypto/sha.h>
+#include <hcrypto/rc4.h>
+#include <hcrypto/rc2.h>
+#include <hcrypto/aes.h>
+#include <hcrypto/ui.h>
+#include <hcrypto/rand.h>
+#include <hcrypto/engine.h>
+#include <hcrypto/pkcs12.h>
+#include <hcrypto/hmac.h>
+#endif /* __crypto_headers_h__ */
diff --git a/source4/heimdal_build/et_compile_wrapper.sh b/source4/heimdal_build/et_compile_wrapper.sh
new file mode 100755
index 0000000000..ec3b39ff56
--- /dev/null
+++ b/source4/heimdal_build/et_compile_wrapper.sh
@@ -0,0 +1,65 @@
+#!/bin/sh
+#
+
+SELF=$0
+SELFDIR=`dirname ${SELF}`
+
+BUILDDIR=$1
+DESTDIR=$2
+
+CMD=$3
+FILE=$4
+SOURCE=$5
+shift 5
+
+test -z "${BUILDDIR}" && {
+ echo "${SELF}:BUILDDIR: '${BUILDDIR}'" >&2;
+ exit 1;
+}
+
+test -z "${DESTDIR}" && {
+ echo "${SELF}:DESTDIR: '${DESTDIR}'" >&2;
+ exit 1;
+}
+
+test -z "${CMD}" && {
+ echo "${SELF}:CMD: '${CMD}'" >&2;
+ exit 1;
+}
+
+test -z "${FILE}" && {
+ echo "${SELF}:FILE: '${FILE}'" >&2;
+ exit 1;
+}
+
+test -z "${SOURCE}" && {
+ echo "${SELF}:SOURCE: '${SOURCE}'" >&2;
+ exit 1;
+}
+
+CURDIR=`pwd`
+
+cd ${BUILDDIR} && {
+ ABS_BUILDDIR=`pwd`
+ cd ${CURDIR}
+} || {
+ echo "${SELF}:cannot cd into '${BUILDDIR}'" >&2;
+ exit 1;
+}
+
+cd ${DESTDIR} && {
+ ${ABS_BUILDDIR}/${CMD} ${FILE} >&2 || exit 1;
+ cd ${CURDIR}
+ TMP="${SOURCE}.$$"
+ mv ${SOURCE} ${TMP} && {
+ echo "#include \"config.h\"" > ${SOURCE} && {
+ cat ${TMP} >> ${SOURCE}
+ }
+ }
+ rm ${TMP}
+} || {
+ echo "${SELF}:cannot cd into '${BUILDDIR}'" >&2;
+ exit 1;
+}
+
+exit 0;
diff --git a/source4/heimdal_build/et_deps.pl b/source4/heimdal_build/et_deps.pl
new file mode 100755
index 0000000000..5914425998
--- /dev/null
+++ b/source4/heimdal_build/et_deps.pl
@@ -0,0 +1,17 @@
+#!/usr/bin/perl
+
+use File::Basename;
+
+my $file = shift;
+my $dirname = shift;
+my $basename = basename($file);
+
+my $header = "$dirname/$basename"; $header =~ s/\.et$/.h/;
+my $source = "$dirname/$basename"; $source =~ s/\.et$/.c/;
+print "basics:: $header\n";
+print "$header $source: \$(heimdalsrcdir)/$file \$(ET_COMPILER)\n";
+print "\t\@echo \"Compiling error table $file\"\n";
+print "\t\@\$(heimdalbuildsrcdir)/et_compile_wrapper.sh \$(builddir) $dirname \$(ET_COMPILER) \$(call abspath,\$(heimdalsrcdir)/$file) $source\n\n";
+
+print "clean:: \n";
+print "\t\@rm -f $header $source\n\n";
diff --git a/source4/heimdal_build/external.m4 b/source4/heimdal_build/external.m4
new file mode 100644
index 0000000000..3cd8900a12
--- /dev/null
+++ b/source4/heimdal_build/external.m4
@@ -0,0 +1,54 @@
+# This is every experimental
+# We should really add configure checks
+# to verify the installed heimdal libraries
+# are good enough for our usage!!!
+
+HEIMDAL_KRB5_LIBS="-lkrb5 -lcom_err"
+HEIMDAL_KRB5_CFLAGS=""
+HEIMDAL_KRB5_CPPFLAGS=""
+HEIMDAL_KRB5_LDFLAGS=""
+SMB_EXT_LIB(HEIMDAL_KRB5,
+ [${HEIMDAL_KRB5_LIBS}],
+ [${HEIMDAL_KRB5_CFLAGS}],
+ [${HEIMDAL_KRB5_CPPFLAGS}],
+ [${HEIMDAL_KRB5_LDFLAGS}])
+SMB_ENABLE(HEIMDAL_KRB5)
+AC_DEFINE(HAVE_KRB5,1,[Whether kerberos is available])
+HAVE_KRB5=YES
+AC_DEFINE(HAVE_COM_ERR,1,[Whether com_err is available])
+HAVE_COM_ERR=YES
+
+HEIMDAL_GSSAPI_LIBS="-lgssapi"
+HEIMDAL_GSSAPI_CFLAGS=""
+HEIMDAL_GSSAPI_CPPFLAGS=""
+HEIMDAL_GSSAPI_LDFLAGS=""
+SMB_EXT_LIB(HEIMDAL_GSSAPI,
+ [${HEIMDAL_GSSAPI_LIBS}],
+ [${HEIMDAL_GSSAPI_CFLAGS}],
+ [${HEIMDAL_GSSAPI_CPPFLAGS}],
+ [${HEIMDAL_GSSAPI_LDFLAGS}])
+SMB_ENABLE(HEIMDAL_GSSAPI)
+AC_DEFINE(HAVE_GSSAPI,1,[Whether GSSAPI is available])
+HAVE_GSSAPI=YES
+
+HEIMDAL_HDB_LIBS="-lhdb"
+HEIMDAL_HDB_CFLAGS=""
+HEIMDAL_HDB_CPPFLAGS=""
+HEIMDAL_HDB_LDFLAGS=""
+SMB_EXT_LIB(HEIMDAL_HDB,
+ [${HEIMDAL_HDB_LIBS}],
+ [${HEIMDAL_HDB_CFLAGS}],
+ [${HEIMDAL_HDB_CPPFLAGS}],
+ [${HEIMDAL_HDB_LDFLAGS}])
+SMB_ENABLE(HEIMDAL_HDB)
+
+HEIMDAL_KDC_LIBS="-lkdc"
+HEIMDAL_KDC_CFLAGS=""
+HEIMDAL_KDC_CPPFLAGS=""
+HEIMDAL_KDC_LDFLAGS=""
+SMB_EXT_LIB(HEIMDAL_KDC,
+ [${HEIMDAL_KDC_LIBS}],
+ [${HEIMDAL_KDC_CFLAGS}],
+ [${HEIMDAL_KDC_CPPFLAGS}],
+ [${HEIMDAL_KDC_LDFLAGS}])
+SMB_ENABLE(HEIMDAL_KDC)
diff --git a/source4/heimdal_build/gssapi-glue.c b/source4/heimdal_build/gssapi-glue.c
new file mode 100644
index 0000000000..0c27f5100f
--- /dev/null
+++ b/source4/heimdal_build/gssapi-glue.c
@@ -0,0 +1,28 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ provide glue functions between heimdal and samba
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "../replace/replace.h"
+#include "heimdal/lib/gssapi/gssapi_mech.h"
+
+gssapi_mech_interface __gss_ntlm_initialize(void)
+{
+ return NULL;
+}
diff --git a/source4/heimdal_build/internal.m4 b/source4/heimdal_build/internal.m4
new file mode 100644
index 0000000000..4cd7521c37
--- /dev/null
+++ b/source4/heimdal_build/internal.m4
@@ -0,0 +1,290 @@
+m4_define([upcase],`echo $1 | tr abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ`)dnl
+
+m4_ifndef([AC_WARNING_ENABLE],[AC_DEFUN([AC_WARNING_ENABLE],[])])
+
+dnl love_FIND_FUNC(func, includes, arguments)
+dnl kind of like AC_CHECK_FUNC, but with headerfiles
+AC_DEFUN([love_FIND_FUNC], [
+
+AC_MSG_CHECKING([for $1])
+AC_CACHE_VAL(ac_cv_love_func_$1,
+[
+AC_LINK_IFELSE([AC_LANG_PROGRAM([[$2]],[[$1($3)]])],
+[eval "ac_cv_love_func_$1=yes"],[eval "ac_cv_love_func_$1=no"])])
+
+eval "ac_res=\$ac_cv_love_func_$1"
+
+if false; then
+ AC_CHECK_FUNCS($1)
+fi
+# $1
+eval "ac_tr_func=HAVE_[]upcase($1)"
+
+case "$ac_res" in
+ yes)
+ AC_DEFINE_UNQUOTED($ac_tr_func)
+ AC_MSG_RESULT([yes])
+ ;;
+ no)
+ AC_MSG_RESULT([no])
+ ;;
+esac
+
+
+])
+
+AC_CHECK_TYPE(u_char, uint8_t)
+AC_CHECK_TYPE(u_int32_t, uint32_t)
+
+dnl Not all systems have err.h, so we provide a replacement. Heimdal
+dnl unconditionally #includes <err.h>, so we need to create an err.h,
+dnl but we can't just have a static one because we don't want to use
+dnl it on systems that have a real err.h. If the system has a real
+dnl err.h, we should use that (eg. on Darwin, the declarations get
+dnl linker attributes added, so we can't guarantee that our local
+dnl declarations will be correct). Phew!
+AC_CHECK_HEADERS([err.h], [],
+ [ cp heimdal/lib/roken/err.hin heimdal_build/err.h ])
+
+AC_CHECK_HEADERS([ \
+ crypt.h \
+ curses.h \
+ errno.h \
+ inttypes.h \
+ netdb.h \
+ pty.h \
+ signal.h \
+ sys/bswap.h \
+ sys/file.h \
+ sys/stropts.h \
+ sys/timeb.h \
+ sys/times.h \
+ sys/uio.h \
+ sys/un.h \
+ sys/utsname.h \
+ term.h \
+ termcap.h \
+ time.h \
+ timezone.h \
+ ttyname.h \
+ netinet/in.h \
+ netinet/in6.h \
+ netinet6/in6.h \
+ libintl.h
+])
+
+AC_CHECK_FUNCS([ \
+ atexit \
+ cgetent \
+ getprogname \
+ setprogname \
+ inet_aton \
+ gethostname \
+ getnameinfo \
+ iruserok \
+ putenv \
+ rcmd \
+ readv \
+ sendmsg \
+ setitimer \
+ socket \
+ strlwr \
+ strncasecmp \
+ strptime \
+ strsep \
+ strsep_copy \
+ strtok_r \
+ strupr \
+ swab \
+ umask \
+ uname \
+ unsetenv \
+ closefrom \
+ hstrerror \
+ err \
+ warn \
+ errx \
+ warnx \
+ flock \
+ getipnodebyname \
+ getipnodebyaddr \
+ freehostent \
+ writev
+])
+
+love_FIND_FUNC(bswap16, [#ifdef HAVE_SYS_BSWAP_H
+#include <sys/bswap.h>
+#endif], 0)
+
+love_FIND_FUNC(bswap32, [#ifdef HAVE_SYS_BSWAP_H
+#include <sys/bswap.h>
+#endif], 0)
+
+AC_DEFUN([AC_KRB_STRUCT_WINSIZE], [
+AC_MSG_CHECKING(for struct winsize)
+AC_CACHE_VAL(ac_cv_struct_winsize, [
+ac_cv_struct_winsize=no
+for i in sys/termios.h sys/ioctl.h; do
+AC_EGREP_HEADER(
+struct[[ ]]*winsize,dnl
+$i, ac_cv_struct_winsize=yes; break)dnl
+done
+])
+if test "$ac_cv_struct_winsize" = "yes"; then
+ AC_DEFINE(HAVE_STRUCT_WINSIZE, 1, [define if struct winsize is declared in sys/termios.h])
+fi
+AC_MSG_RESULT($ac_cv_struct_winsize)
+AC_EGREP_HEADER(ws_xpixel, termios.h,
+ AC_DEFINE(HAVE_WS_XPIXEL, 1, [define if struct winsize has ws_xpixel]))
+AC_EGREP_HEADER(ws_ypixel, termios.h,
+ AC_DEFINE(HAVE_WS_YPIXEL, 1, [define if struct winsize has ws_ypixel]))
+])
+
+AC_KRB_STRUCT_WINSIZE
+
+AC_TYPE_SIGNAL
+if test "$ac_cv_type_signal" = "void" ; then
+ AC_DEFINE(VOID_RETSIGTYPE, 1, [Define if signal handlers return void.])
+fi
+AC_SUBST(VOID_RETSIGTYPE)
+
+
+m4_include(heimdal/cf/check-var.m4)
+
+rk_CHECK_VAR(h_errno,
+[#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif])
+
+m4_include(heimdal/cf/find-func.m4)
+m4_include(heimdal/cf/find-func-no-libs.m4)
+m4_include(heimdal/cf/find-func-no-libs2.m4)
+m4_include(heimdal/cf/resolv.m4)
+
+
+AC_CHECK_LIB_EXT(util, OPENPTY_LIBS, openpty)
+
+SMB_ENABLE(OPENPTY,YES)
+
+SMB_EXT_LIB(OPENPTY,[${OPENPTY_LIBS}],[${OPENPTY_CFLAGS}],[${OPENPTY_CPPFLAGS}],[${OPENPTY_LDFLAGS}])
+
+AC_CHECK_LIB_EXT(intl, INTL_LIBS, gettext)
+
+SMB_ENABLE(INTL,YES)
+
+SMB_EXT_LIB(INTL, $INTL_LIBS)
+
+smb_save_LIBS=$LIBS
+RESOLV_LIBS=""
+LIBS=""
+
+dnl This fills in the global LIBS...
+rk_RESOLV
+
+dnl AC_CHECK_LIB_EXT(resolv, RESOLV_LIBS, res_search)
+ SMB_ENABLE(RESOLV,YES)
+
+if test x"$LIBS" != "x"; then
+ RESOLV_LIBS=$LIBS
+fi
+
+LIBS=$smb_save_LIBS
+
+SMB_EXT_LIB(RESOLV,[${RESOLV_LIBS}],[${RESOLV_CFLAGS}],[${RESOLV_CPPFLAGS}],[${RESOLV_LDFLAGS}])
+
+
+# these are disabled unless heimdal is found below
+SMB_ENABLE(KERBEROS_LIB, NO)
+SMB_ENABLE(asn1_compile, NO)
+SMB_ENABLE(compile_et, NO)
+
+#
+# We need bison -y and flex in new versions
+# Otherwise we get random runtime failures
+#
+LEX_YACC_COMBINATIONS=""
+LEX_YACC_COMBINATIONS="$LEX_YACC_COMBINATIONS flex-2.5.33:bison-2.3"
+LEX_YACC_COMBINATIONS="$LEX_YACC_COMBINATIONS flex-2.5.34:bison-2.3"
+
+AC_PROG_LEX
+LEX_BASENAME=`basename "$LEX"`
+if test x"$LEX_BASENAME" = x"flex"; then
+ # "flex 2.5.33"
+ FLEX_VERSION=`$LEX --version | cut -d ' ' -f2`
+ AC_MSG_CHECKING(flex version)
+ AC_MSG_RESULT($FLEX_VERSION)
+ FLEX_MAJOR=`echo $FLEX_VERSION | cut -d '.' -f1`
+ FLEX_MINOR=`echo $FLEX_VERSION | cut -d '.' -f2`
+ FLEX_RELEASE=`echo $FLEX_VERSION | cut -d '.' -f3`
+
+ LEX_VERSION="flex-$FLEX_MAJOR.$FLEX_MINOR.$FLEX_RELEASE"
+fi
+
+AC_PROG_YACC
+YACC_BASENAME=`basename "$YACC"`
+if test x"$YACC_BASENAME" = x"bison -y"; then
+ # bison (GNU Bison) 2.3
+ BISON_VERSION=`$YACC --version | head -1 | cut -d ' ' -f4`
+ AC_MSG_CHECKING(bison version)
+ AC_MSG_RESULT($BISON_VERSION)
+ BISON_MAJOR=`echo $BISON_VERSION | cut -d '.' -f1`
+ BISON_MINOR=`echo $BISON_VERSION | cut -d '.' -f2`
+
+ YACC_VERSION="bison-$BISON_MAJOR.$BISON_MINOR"
+fi
+
+AC_MSG_CHECKING(working LEX YACC combination)
+LEX_YACC="no"
+if test x"$LEX_VERSION" != x"" -a x"$YACC_VERSION" != x""; then
+ V="$LEX_VERSION:$YACC_VERSION"
+ for C in $LEX_YACC_COMBINATIONS; do
+ if test x"$V" = x"$C"; then
+ LEX_YACC=$V
+ break;
+ fi
+ done
+fi
+if test x"$LEX_YACC" = x"no"; then
+ LEX=false
+ YACC=false
+fi
+AC_MSG_RESULT($LEX_YACC)
+
+# Portions of heimdal kerberos are unpacked into source/heimdal
+# of the samba source tree.
+
+# if we ever get to using a host kerberos, we might add conditionals here
+AC_DEFINE(HAVE_COM_ERR,1,[Whether com_err is available])
+HAVE_COM_ERR=YES
+AC_DEFINE(HAVE_KRB5,1,[Whether kerberos is available])
+HAVE_KRB5=YES
+AC_DEFINE(HAVE_GSSAPI,1,[Whether GSSAPI is available])
+HAVE_GSSAPI=YES
+SMB_ENABLE(KERBEROS_LIB, YES)
+SMB_ENABLE(asn1_compile, YES)
+SMB_ENABLE(compile_et, YES)
+
+# only add closefrom if needed
+SMB_ENABLE(HEIMDAL_ROKEN_CLOSEFROM, NO)
+SMB_ENABLE(HEIMDAL_ROKEN_CLOSEFROM_H, NO)
+if test t$ac_cv_func_closefrom != tyes; then
+ SMB_ENABLE(HEIMDAL_ROKEN_CLOSEFROM, YES)
+ SMB_ENABLE(HEIMDAL_ROKEN_CLOSEFROM_H, YES)
+fi
+
+# only add getprogname if needed
+SMB_ENABLE(HEIMDAL_ROKEN_PROGNAME, NO)
+SMB_ENABLE(HEIMDAL_ROKEN_PROGNAME_H, NO)
+if test t$ac_cv_func_getprogname != tyes; then
+ SMB_ENABLE(HEIMDAL_ROKEN_PROGNAME, YES)
+ SMB_ENABLE(HEIMDAL_ROKEN_PROGNAME_H, YES)
+fi
+
+VPATH="$VPATH:\$(HEIMDAL_VPATH)"
+
+AC_DEFINE(SAMBA4_INTERNAL_HEIMDAL,1,[Whether we use in internal heimdal build])
+
+SMB_INCLUDE_MK(heimdal_build/internal.mk)
diff --git a/source4/heimdal_build/internal.mk b/source4/heimdal_build/internal.mk
new file mode 100644
index 0000000000..ea8d4731db
--- /dev/null
+++ b/source4/heimdal_build/internal.mk
@@ -0,0 +1,797 @@
+heimdalbuildsrcdir = $(heimdalsrcdir)/../heimdal_build
+
+HEIMDAL_VPATH = $(heimdalbuildsrcdir):$(heimdalsrcdir)/lib/asn1:$(heimdalsrcdir)/lib/krb5:$(heimdalsrcdir)/lib/gssapi:$(heimdalsrcdir)/lib/hdb:$(heimdalsrcdir)/lib/roken:$(heimdalsrcdir)/lib/des
+
+# Create a prototype header
+# Arguments: header file, arguments, c files, deps
+define heimdal_proto_header_template
+
+proto:: $(1)
+
+clean:: ;
+ rm -f $(1)
+
+$(4):: $(1)
+
+$(1): $(3) ;
+ @echo "Creating $$@"
+ @$$(PERL) $$(heimdalsrcdir)/cf/make-proto.pl $(2) $(1) $(3)
+
+endef
+
+#######################
+# Start SUBSYSTEM HEIMDAL_KDC
+[SUBSYSTEM::HEIMDAL_KDC]
+CFLAGS = -I$(heimdalbuildsrcdir) -I$(heimdalsrcdir)/kdc
+PRIVATE_DEPENDENCIES = HEIMDAL_ROKEN HEIMDAL_KRB5 HEIMDAL_HDB HEIMDAL_HEIM_ASN1 \
+ HEIMDAL_DIGEST_ASN1 HEIMDAL_KX509_ASN1 HEIMDAL_NTLM HEIMDAL_HCRYPTO
+# End SUBSYSTEM HEIMDAL_KDC
+#######################
+
+
+HEIMDAL_KDC_OBJ_FILES = \
+ $(heimdalsrcdir)/kdc/default_config.o \
+ $(heimdalsrcdir)/kdc/kerberos5.o \
+ $(heimdalsrcdir)/kdc/krb5tgs.o \
+ $(heimdalsrcdir)/kdc/pkinit.o \
+ $(heimdalsrcdir)/kdc/log.o \
+ $(heimdalsrcdir)/kdc/misc.o \
+ $(heimdalsrcdir)/kdc/524.o \
+ $(heimdalsrcdir)/kdc/kerberos4.o \
+ $(heimdalsrcdir)/kdc/kaserver.o \
+ $(heimdalsrcdir)/kdc/digest.o \
+ $(heimdalsrcdir)/kdc/process.o \
+ $(heimdalsrcdir)/kdc/windc.o \
+ $(heimdalsrcdir)/kdc/kx509.o
+
+$(eval $(call heimdal_proto_header_template, \
+ $(heimdalsrcdir)/kdc/kdc-protos.h, \
+ -q -P comment -o, \
+ $(HEIMDAL_KDC_OBJ_FILES:.o=.c), \
+ $(HEIMDAL_KDC_OBJ_FILES) $(HEIMDAL_KDC_OBJ_FILES:.o=.d) \
+))
+
+$(eval $(call heimdal_proto_header_template, \
+ $(heimdalsrcdir)/kdc/kdc-private.h, \
+ -q -P comment -p, \
+ $(HEIMDAL_KDC_OBJ_FILES:.o=.c), \
+ $(HEIMDAL_KDC_OBJ_FILES) $(HEIMDAL_KDC_OBJ_FILES:.o=.d) \
+))
+
+[SUBSYSTEM::HEIMDAL_NTLM]
+CFLAGS = -I$(heimdalbuildsrcdir) -I$(heimdalsrcdir)/lib/ntlm
+PRIVATE_DEPENDENCIES = HEIMDAL_ROKEN HEIMDAL_HCRYPTO HEIMDAL_KRB5
+
+HEIMDAL_NTLM_OBJ_FILES = \
+ $(heimdalsrcdir)/lib/ntlm/ntlm.o
+
+$(eval $(call heimdal_proto_header_template, \
+ $(heimdalsrcdir)/lib/ntlm/heimntlm-protos.h, \
+ -q -P comment -o, \
+ $(HEIMDAL_NTLM_OBJ_FILES:.o=.c), \
+ $(HEIMDAL_NTLM_OBJ_FILES) $(HEIMDAL_NTLM_OBJ_FILES:.o=.d) \
+))
+
+[SUBSYSTEM::HEIMDAL_HDB_KEYS]
+CFLAGS = -I$(heimdalbuildsrcdir) -I$(heimdalsrcdir)/lib/hdb
+PRIVATE_DEPENDENCIES = HEIMDAL_ROKEN HEIMDAL_HCRYPTO HEIMDAL_KRB5 \
+ HEIMDAL_HDB_ASN1
+
+HEIMDAL_HDB_KEYS_OBJ_FILES = $(heimdalsrcdir)/lib/hdb/keys.o
+
+#######################
+# Start SUBSYSTEM HEIMDAL_HDB
+[SUBSYSTEM::HEIMDAL_HDB]
+CFLAGS = -I$(heimdalbuildsrcdir) -I$(heimdalsrcdir)/lib/hdb
+PRIVATE_DEPENDENCIES = HDB_LDB HEIMDAL_KRB5 HEIMDAL_HDB_KEYS HEIMDAL_ROKEN HEIMDAL_HCRYPTO HEIMDAL_COM_ERR HEIMDAL_HDB_ASN1
+# End SUBSYSTEM HEIMDAL_HDB
+#######################
+
+HEIMDAL_HDB_OBJ_FILES = \
+ $(heimdalsrcdir)/lib/hdb/db.o \
+ $(heimdalsrcdir)/lib/hdb/dbinfo.o \
+ $(heimdalsrcdir)/lib/hdb/hdb.o \
+ $(heimdalsrcdir)/lib/hdb/ext.o \
+ $(heimdalsrcdir)/lib/hdb/keytab.o \
+ $(heimdalsrcdir)/lib/hdb/mkey.o \
+ $(heimdalsrcdir)/lib/hdb/ndbm.o \
+ $(heimdalsrcdir)/lib/hdb/hdb_err.o
+
+$(eval $(call heimdal_proto_header_template, \
+ $(heimdalsrcdir)/lib/hdb/hdb-protos.h, \
+ -q -P comment -o, \
+ $(HEIMDAL_HDB_OBJ_FILES:.o=.c), \
+ $(HEIMDAL_HDB_OBJ_FILES) $(HEIMDAL_HDB_OBJ_FILES:.o=.d) \
+))
+
+$(eval $(call heimdal_proto_header_template, \
+ $(heimdalsrcdir)/lib/hdb/hdb-private.h, \
+ -q -P comment -p, \
+ $(HEIMDAL_HDB_OBJ_FILES:.o=.c), \
+ $(HEIMDAL_HDB_OBJ_FILES) $(HEIMDAL_HDB_OBJ_FILES:.o=.d) \
+))
+
+#######################
+# Start SUBSYSTEM HEIMDAL_GSSAPI
+[SUBSYSTEM::HEIMDAL_GSSAPI]
+CFLAGS = -I$(heimdalbuildsrcdir) -I$(heimdalsrcdir)/lib/gssapi -I$(heimdalsrcdir)/lib/gssapi/gssapi -I$(heimdalsrcdir)/lib/gssapi/spnego -I$(heimdalsrcdir)/lib/gssapi/krb5 -I$(heimdalsrcdir)/lib/gssapi/mech
+PRIVATE_DEPENDENCIES = HEIMDAL_HCRYPTO HEIMDAL_HEIM_ASN1 HEIMDAL_SPNEGO_ASN1 HEIMDAL_ROKEN HEIMDAL_KRB5
+# End SUBSYSTEM HEIMDAL_GSSAPI
+#######################
+
+HEIMDAL_GSSAPI_SPNEGO_OBJ_FILES = \
+ $(heimdalsrcdir)/lib/gssapi/spnego/init_sec_context.o \
+ $(heimdalsrcdir)/lib/gssapi/spnego/external.o \
+ $(heimdalsrcdir)/lib/gssapi/spnego/compat.o \
+ $(heimdalsrcdir)/lib/gssapi/spnego/context_stubs.o \
+ $(heimdalsrcdir)/lib/gssapi/spnego/cred_stubs.o \
+ $(heimdalsrcdir)/lib/gssapi/spnego/accept_sec_context.o \
+
+$(eval $(call heimdal_proto_header_template, \
+ $(heimdalsrcdir)/lib/gssapi/spnego/spnego-private.h, \
+ -q -P comment -p, \
+ $(HEIMDAL_GSSAPI_SPNEGO_OBJ_FILES:.o=.c), \
+ $(HEIMDAL_GSSAPI_SPNEGO_OBJ_FILES) $(HEIMDAL_GSSAPI_SPNEGO_OBJ_FILES:.o=.d) \
+))
+
+HEIMDAL_GSSAPI_KRB5_OBJ_FILES = \
+ $(heimdalsrcdir)/lib/gssapi/krb5/copy_ccache.o \
+ $(heimdalsrcdir)/lib/gssapi/krb5/delete_sec_context.o \
+ $(heimdalsrcdir)/lib/gssapi/krb5/init_sec_context.o \
+ $(heimdalsrcdir)/lib/gssapi/krb5/context_time.o \
+ $(heimdalsrcdir)/lib/gssapi/krb5/init.o \
+ $(heimdalsrcdir)/lib/gssapi/krb5/address_to_krb5addr.o \
+ $(heimdalsrcdir)/lib/gssapi/krb5/get_mic.o \
+ $(heimdalsrcdir)/lib/gssapi/krb5/inquire_context.o \
+ $(heimdalsrcdir)/lib/gssapi/krb5/add_cred.o \
+ $(heimdalsrcdir)/lib/gssapi/krb5/inquire_cred.o \
+ $(heimdalsrcdir)/lib/gssapi/krb5/inquire_cred_by_oid.o \
+ $(heimdalsrcdir)/lib/gssapi/krb5/inquire_cred_by_mech.o \
+ $(heimdalsrcdir)/lib/gssapi/krb5/inquire_mechs_for_name.o \
+ $(heimdalsrcdir)/lib/gssapi/krb5/inquire_names_for_mech.o \
+ $(heimdalsrcdir)/lib/gssapi/krb5/indicate_mechs.o \
+ $(heimdalsrcdir)/lib/gssapi/krb5/inquire_sec_context_by_oid.o \
+ $(heimdalsrcdir)/lib/gssapi/krb5/export_sec_context.o \
+ $(heimdalsrcdir)/lib/gssapi/krb5/import_sec_context.o \
+ $(heimdalsrcdir)/lib/gssapi/krb5/duplicate_name.o \
+ $(heimdalsrcdir)/lib/gssapi/krb5/import_name.o \
+ $(heimdalsrcdir)/lib/gssapi/krb5/compare_name.o \
+ $(heimdalsrcdir)/lib/gssapi/krb5/export_name.o \
+ $(heimdalsrcdir)/lib/gssapi/krb5/canonicalize_name.o \
+ $(heimdalsrcdir)/lib/gssapi/krb5/unwrap.o \
+ $(heimdalsrcdir)/lib/gssapi/krb5/wrap.o \
+ $(heimdalsrcdir)/lib/gssapi/krb5/release_name.o \
+ $(heimdalsrcdir)/lib/gssapi/krb5/cfx.o \
+ $(heimdalsrcdir)/lib/gssapi/krb5/8003.o \
+ $(heimdalsrcdir)/lib/gssapi/krb5/arcfour.o \
+ $(heimdalsrcdir)/lib/gssapi/krb5/encapsulate.o \
+ $(heimdalsrcdir)/lib/gssapi/krb5/display_name.o \
+ $(heimdalsrcdir)/lib/gssapi/krb5/sequence.o \
+ $(heimdalsrcdir)/lib/gssapi/krb5/display_status.o \
+ $(heimdalsrcdir)/lib/gssapi/krb5/release_buffer.o \
+ $(heimdalsrcdir)/lib/gssapi/krb5/external.o \
+ $(heimdalsrcdir)/lib/gssapi/krb5/compat.o \
+ $(heimdalsrcdir)/lib/gssapi/krb5/acquire_cred.o \
+ $(heimdalsrcdir)/lib/gssapi/krb5/release_cred.o \
+ $(heimdalsrcdir)/lib/gssapi/krb5/set_cred_option.o \
+ $(heimdalsrcdir)/lib/gssapi/krb5/decapsulate.o \
+ $(heimdalsrcdir)/lib/gssapi/krb5/verify_mic.o \
+ $(heimdalsrcdir)/lib/gssapi/krb5/accept_sec_context.o \
+ $(heimdalsrcdir)/lib/gssapi/krb5/set_sec_context_option.o \
+ $(heimdalsrcdir)/lib/gssapi/krb5/process_context_token.o \
+ $(heimdalsrcdir)/lib/gssapi/krb5/prf.o
+
+$(eval $(call heimdal_proto_header_template, \
+ $(heimdalsrcdir)/lib/gssapi/krb5/gsskrb5-private.h, \
+ -q -P comment -p, \
+ $(HEIMDAL_GSSAPI_KRB5_OBJ_FILES:.o=.c), \
+ $(HEIMDAL_GSSAPI_KRB5_OBJ_FILES) $(HEIMDAL_GSSAPI_KRB5_OBJ_FILES:.o=.d) \
+))
+
+HEIMDAL_GSSAPI_OBJ_FILES = \
+ $(HEIMDAL_GSSAPI_SPNEGO_OBJ_FILES) \
+ $(HEIMDAL_GSSAPI_KRB5_OBJ_FILES) \
+ $(heimdalsrcdir)/lib/gssapi/mech/context.o \
+ $(heimdalsrcdir)/lib/gssapi/mech/gss_krb5.o \
+ $(heimdalsrcdir)/lib/gssapi/mech/gss_mech_switch.o \
+ $(heimdalsrcdir)/lib/gssapi/mech/gss_process_context_token.o \
+ $(heimdalsrcdir)/lib/gssapi/mech/gss_buffer_set.o \
+ $(heimdalsrcdir)/lib/gssapi/mech/gss_add_cred.o \
+ $(heimdalsrcdir)/lib/gssapi/mech/gss_add_oid_set_member.o \
+ $(heimdalsrcdir)/lib/gssapi/mech/gss_compare_name.o \
+ $(heimdalsrcdir)/lib/gssapi/mech/gss_release_oid_set.o \
+ $(heimdalsrcdir)/lib/gssapi/mech/gss_create_empty_oid_set.o \
+ $(heimdalsrcdir)/lib/gssapi/mech/gss_decapsulate_token.o \
+ $(heimdalsrcdir)/lib/gssapi/mech/gss_inquire_cred_by_oid.o \
+ $(heimdalsrcdir)/lib/gssapi/mech/gss_canonicalize_name.o \
+ $(heimdalsrcdir)/lib/gssapi/mech/gss_inquire_sec_context_by_oid.o \
+ $(heimdalsrcdir)/lib/gssapi/mech/gss_inquire_names_for_mech.o \
+ $(heimdalsrcdir)/lib/gssapi/mech/gss_inquire_mechs_for_name.o \
+ $(heimdalsrcdir)/lib/gssapi/mech/gss_wrap_size_limit.o \
+ $(heimdalsrcdir)/lib/gssapi/mech/gss_names.o \
+ $(heimdalsrcdir)/lib/gssapi/mech/gss_verify.o \
+ $(heimdalsrcdir)/lib/gssapi/mech/gss_display_name.o \
+ $(heimdalsrcdir)/lib/gssapi/mech/gss_duplicate_oid.o \
+ $(heimdalsrcdir)/lib/gssapi/mech/gss_display_status.o \
+ $(heimdalsrcdir)/lib/gssapi/mech/gss_release_buffer.o \
+ $(heimdalsrcdir)/lib/gssapi/mech/gss_release_oid.o \
+ $(heimdalsrcdir)/lib/gssapi/mech/gss_test_oid_set_member.o \
+ $(heimdalsrcdir)/lib/gssapi/mech/gss_release_cred.o \
+ $(heimdalsrcdir)/lib/gssapi/mech/gss_set_sec_context_option.o \
+ $(heimdalsrcdir)/lib/gssapi/mech/gss_export_name.o \
+ $(heimdalsrcdir)/lib/gssapi/mech/gss_seal.o \
+ $(heimdalsrcdir)/lib/gssapi/mech/gss_acquire_cred.o \
+ $(heimdalsrcdir)/lib/gssapi/mech/gss_unseal.o \
+ $(heimdalsrcdir)/lib/gssapi/mech/gss_verify_mic.o \
+ $(heimdalsrcdir)/lib/gssapi/mech/gss_accept_sec_context.o \
+ $(heimdalsrcdir)/lib/gssapi/mech/gss_inquire_cred_by_mech.o \
+ $(heimdalsrcdir)/lib/gssapi/mech/gss_indicate_mechs.o \
+ $(heimdalsrcdir)/lib/gssapi/mech/gss_delete_sec_context.o \
+ $(heimdalsrcdir)/lib/gssapi/mech/gss_sign.o \
+ $(heimdalsrcdir)/lib/gssapi/mech/gss_utils.o \
+ $(heimdalsrcdir)/lib/gssapi/mech/gss_init_sec_context.o \
+ $(heimdalsrcdir)/lib/gssapi/mech/gss_oid_equal.o \
+ $(heimdalsrcdir)/lib/gssapi/mech/gss_oid_to_str.o \
+ $(heimdalsrcdir)/lib/gssapi/mech/gss_context_time.o \
+ $(heimdalsrcdir)/lib/gssapi/mech/gss_encapsulate_token.o \
+ $(heimdalsrcdir)/lib/gssapi/mech/gss_get_mic.o \
+ $(heimdalsrcdir)/lib/gssapi/mech/gss_import_sec_context.o \
+ $(heimdalsrcdir)/lib/gssapi/mech/gss_inquire_cred.o \
+ $(heimdalsrcdir)/lib/gssapi/mech/gss_wrap.o \
+ $(heimdalsrcdir)/lib/gssapi/mech/gss_import_name.o \
+ $(heimdalsrcdir)/lib/gssapi/mech/gss_duplicate_name.o \
+ $(heimdalsrcdir)/lib/gssapi/mech/gss_unwrap.o \
+ $(heimdalsrcdir)/lib/gssapi/mech/gss_export_sec_context.o \
+ $(heimdalsrcdir)/lib/gssapi/mech/gss_inquire_context.o \
+ $(heimdalsrcdir)/lib/gssapi/mech/gss_release_name.o \
+ $(heimdalsrcdir)/lib/gssapi/mech/gss_set_cred_option.o \
+ $(heimdalsrcdir)/lib/gssapi/mech/gss_pseudo_random.o \
+ $(heimdalsrcdir)/lib/gssapi/asn1_GSSAPIContextToken.o \
+ $(heimdalbuildsrcdir)/gssapi-glue.o
+
+#######################
+# Start SUBSYSTEM HEIMDAL_KRB5
+[SUBSYSTEM::HEIMDAL_KRB5]
+CFLAGS = -I$(heimdalbuildsrcdir) -I$(heimdalsrcdir)/lib/krb5 -I$(heimdalsrcdir)/lib/asn1 -I$(heimdalsrcdir)/lib/com_err
+PRIVATE_DEPENDENCIES = HEIMDAL_ROKEN HEIMDAL_PKINIT_ASN1 HEIMDAL_WIND \
+ HEIMDAL_KRB5_ASN1 HEIMDAL_HX509 HEIMDAL_HCRYPTO \
+ LIBNETIF LIBSAMBA-HOSTCONFIG INTL
+PUBLIC_DEPENDENCIES = HEIMDAL_COM_ERR
+# End SUBSYSTEM HEIMDAL_KRB5
+#######################
+
+HEIMDAL_KRB5_OBJ_FILES = \
+ $(heimdalsrcdir)/lib/krb5/acache.o \
+ $(heimdalsrcdir)/lib/krb5/add_et_list.o \
+ $(heimdalsrcdir)/lib/krb5/addr_families.o \
+ $(heimdalsrcdir)/lib/krb5/appdefault.o \
+ $(heimdalsrcdir)/lib/krb5/asn1_glue.o \
+ $(heimdalsrcdir)/lib/krb5/auth_context.o \
+ $(heimdalsrcdir)/lib/krb5/build_ap_req.o \
+ $(heimdalsrcdir)/lib/krb5/build_auth.o \
+ $(heimdalsrcdir)/lib/krb5/cache.o \
+ $(heimdalsrcdir)/lib/krb5/changepw.o \
+ $(heimdalsrcdir)/lib/krb5/codec.o \
+ $(heimdalsrcdir)/lib/krb5/config_file.o \
+ $(heimdalsrcdir)/lib/krb5/config_file_netinfo.o \
+ $(heimdalsrcdir)/lib/krb5/constants.o \
+ $(heimdalsrcdir)/lib/krb5/context.o \
+ $(heimdalsrcdir)/lib/krb5/convert_creds.o \
+ $(heimdalsrcdir)/lib/krb5/copy_host_realm.o \
+ $(heimdalsrcdir)/lib/krb5/crc.o \
+ $(heimdalsrcdir)/lib/krb5/creds.o \
+ $(heimdalsrcdir)/lib/krb5/crypto.o \
+ $(heimdalsrcdir)/lib/krb5/data.o \
+ $(heimdalsrcdir)/lib/krb5/eai_to_heim_errno.o \
+ $(heimdalsrcdir)/lib/krb5/error_string.o \
+ $(heimdalsrcdir)/lib/krb5/expand_hostname.o \
+ $(heimdalsrcdir)/lib/krb5/fcache.o \
+ $(heimdalsrcdir)/lib/krb5/free.o \
+ $(heimdalsrcdir)/lib/krb5/free_host_realm.o \
+ $(heimdalsrcdir)/lib/krb5/generate_seq_number.o \
+ $(heimdalsrcdir)/lib/krb5/generate_subkey.o \
+ $(heimdalsrcdir)/lib/krb5/get_addrs.o \
+ $(heimdalsrcdir)/lib/krb5/get_cred.o \
+ $(heimdalsrcdir)/lib/krb5/get_default_principal.o \
+ $(heimdalsrcdir)/lib/krb5/get_default_realm.o \
+ $(heimdalsrcdir)/lib/krb5/get_for_creds.o \
+ $(heimdalsrcdir)/lib/krb5/get_host_realm.o \
+ $(heimdalsrcdir)/lib/krb5/get_in_tkt.o \
+ $(heimdalsrcdir)/lib/krb5/get_in_tkt_with_keytab.o \
+ $(heimdalsrcdir)/lib/krb5/get_port.o \
+ $(heimdalsrcdir)/lib/krb5/init_creds.o \
+ $(heimdalsrcdir)/lib/krb5/init_creds_pw.o \
+ $(heimdalsrcdir)/lib/krb5/kcm.o \
+ $(heimdalsrcdir)/lib/krb5/keyblock.o \
+ $(heimdalsrcdir)/lib/krb5/keytab.o \
+ $(heimdalsrcdir)/lib/krb5/keytab_any.o \
+ $(heimdalsrcdir)/lib/krb5/keytab_file.o \
+ $(heimdalsrcdir)/lib/krb5/keytab_memory.o \
+ $(heimdalsrcdir)/lib/krb5/keytab_keyfile.o \
+ $(heimdalsrcdir)/lib/krb5/krbhst.o \
+ $(heimdalsrcdir)/lib/krb5/log.o \
+ $(heimdalsrcdir)/lib/krb5/mcache.o \
+ $(heimdalsrcdir)/lib/krb5/misc.o \
+ $(heimdalsrcdir)/lib/krb5/mk_error.o \
+ $(heimdalsrcdir)/lib/krb5/mk_priv.o \
+ $(heimdalsrcdir)/lib/krb5/mk_rep.o \
+ $(heimdalsrcdir)/lib/krb5/mk_req.o \
+ $(heimdalsrcdir)/lib/krb5/mk_req_ext.o \
+ $(heimdalsrcdir)/lib/krb5/mit_glue.o \
+ $(heimdalsrcdir)/lib/krb5/n-fold.o \
+ $(heimdalsrcdir)/lib/krb5/padata.o \
+ $(heimdalsrcdir)/lib/krb5/pkinit.o \
+ $(heimdalsrcdir)/lib/krb5/plugin.o \
+ $(heimdalsrcdir)/lib/krb5/principal.o \
+ $(heimdalsrcdir)/lib/krb5/prog_setup.o \
+ $(heimdalsrcdir)/lib/krb5/pac.o \
+ $(heimdalsrcdir)/lib/krb5/prompter_posix.o \
+ $(heimdalsrcdir)/lib/krb5/rd_cred.o \
+ $(heimdalsrcdir)/lib/krb5/rd_error.o \
+ $(heimdalsrcdir)/lib/krb5/rd_priv.o \
+ $(heimdalsrcdir)/lib/krb5/rd_rep.o \
+ $(heimdalsrcdir)/lib/krb5/rd_req.o \
+ $(heimdalsrcdir)/lib/krb5/replay.o \
+ $(heimdalsrcdir)/lib/krb5/send_to_kdc.o \
+ $(heimdalsrcdir)/lib/krb5/set_default_realm.o \
+ $(heimdalsrcdir)/lib/krb5/store.o \
+ $(heimdalsrcdir)/lib/krb5/store_emem.o \
+ $(heimdalsrcdir)/lib/krb5/store_fd.o \
+ $(heimdalsrcdir)/lib/krb5/store_mem.o \
+ $(heimdalsrcdir)/lib/krb5/ticket.o \
+ $(heimdalsrcdir)/lib/krb5/time.o \
+ $(heimdalsrcdir)/lib/krb5/transited.o \
+ $(heimdalsrcdir)/lib/krb5/v4_glue.o \
+ $(heimdalsrcdir)/lib/krb5/version.o \
+ $(heimdalsrcdir)/lib/krb5/warn.o \
+ $(heimdalsrcdir)/lib/krb5/krb5_err.o \
+ $(heimdalsrcdir)/lib/krb5/heim_err.o \
+ $(heimdalsrcdir)/lib/krb5/k524_err.o \
+ $(heimdalsrcdir)/lib/krb5/krb_err.o \
+ $(heimdalsrcdir)/lib/hcrypto/evp-aes-cts.o \
+ $(heimdalbuildsrcdir)/krb5-glue.o
+
+$(eval $(call heimdal_proto_header_template, \
+ $(heimdalsrcdir)/lib/krb5/krb5-protos.h, \
+ -E KRB5_LIB -q -P comment -o, \
+ $(HEIMDAL_KRB5_OBJ_FILES:.o=.c), \
+ $(HEIMDAL_KRB5_OBJ_FILES) $(HEIMDAL_KRB5_OBJ_FILES:.o=.d) \
+))
+
+$(eval $(call heimdal_proto_header_template, \
+ $(heimdalsrcdir)/lib/krb5/krb5-private.h, \
+ -q -P comment -p, \
+ $(HEIMDAL_KRB5_OBJ_FILES:.o=.c), \
+ $(HEIMDAL_KRB5_OBJ_FILES) $(HEIMDAL_KRB5_OBJ_FILES:.o=.d) \
+))
+
+#######################
+# Start SUBSYSTEM HEIMDAL_HEIM_ASN1
+[SUBSYSTEM::HEIMDAL_HEIM_ASN1]
+CFLAGS = -I$(heimdalbuildsrcdir) -I$(heimdalsrcdir)/lib/asn1
+PRIVATE_DEPENDENCIES = HEIMDAL_ROKEN HEIMDAL_COM_ERR
+# End SUBSYSTEM HEIMDAL_KRB5
+#######################
+
+HEIMDAL_HEIM_ASN1_DER_OBJ_FILES = \
+ $(heimdalsrcdir)/lib/asn1/der_get.o \
+ $(heimdalsrcdir)/lib/asn1/der_put.o \
+ $(heimdalsrcdir)/lib/asn1/der_free.o \
+ $(heimdalsrcdir)/lib/asn1/der_format.o \
+ $(heimdalsrcdir)/lib/asn1/der_length.o \
+ $(heimdalsrcdir)/lib/asn1/der_copy.o \
+ $(heimdalsrcdir)/lib/asn1/der_cmp.o \
+
+HEIMDAL_HEIM_ASN1_OBJ_FILES = \
+ $(HEIMDAL_HEIM_ASN1_DER_OBJ_FILES) \
+ $(heimdalsrcdir)/lib/asn1/extra.o \
+ $(heimdalsrcdir)/lib/asn1/timegm.o \
+ $(heimdalsrcdir)/lib/asn1/asn1_err.o
+
+#######################
+# Start SUBSYSTEM HEIMDAL_HCRYPTO_IMATH
+[SUBSYSTEM::HEIMDAL_HCRYPTO_IMATH]
+CFLAGS = -I$(heimdalbuildsrcdir) -I$(heimdalsrcdir)/lib/hcrypto/imath
+PRIVATE_DEPENDENCIES = HEIMDAL_ROKEN
+# End SUBSYSTEM HEIMDAL_HCRYPTO_IMATH
+#######################
+
+HEIMDAL_HCRYPTO_IMATH_OBJ_FILES = \
+ $(heimdalsrcdir)/lib/hcrypto/imath/imath.o \
+ $(heimdalsrcdir)/lib/hcrypto/imath/iprime.o
+
+[SUBSYSTEM::HEIMDAL_HCRYPTO]
+CFLAGS = -I$(heimdalbuildsrcdir) -I$(heimdalsrcdir)/lib/hcrypto -I$(heimdalsrcdir)/lib
+PRIVATE_DEPENDENCIES = HEIMDAL_ROKEN HEIMDAL_HEIM_ASN1 HEIMDAL_HCRYPTO_IMATH HEIMDAL_RFC2459_ASN1
+# End SUBSYSTEM HEIMDAL_HCRYPTO
+#######################
+
+HEIMDAL_HCRYPTO_OBJ_FILES = \
+ $(heimdalsrcdir)/lib/hcrypto/aes.o \
+ $(heimdalsrcdir)/lib/hcrypto/bn.o \
+ $(heimdalsrcdir)/lib/hcrypto/dh.o \
+ $(heimdalsrcdir)/lib/hcrypto/dh-imath.o \
+ $(heimdalsrcdir)/lib/hcrypto/des.o \
+ $(heimdalsrcdir)/lib/hcrypto/dsa.o \
+ $(heimdalsrcdir)/lib/hcrypto/engine.o \
+ $(heimdalsrcdir)/lib/hcrypto/md2.o \
+ $(heimdalsrcdir)/lib/hcrypto/md4.o \
+ $(heimdalsrcdir)/lib/hcrypto/md5.o \
+ $(heimdalsrcdir)/lib/hcrypto/rsa.o \
+ $(heimdalsrcdir)/lib/hcrypto/rsa-imath.o \
+ $(heimdalsrcdir)/lib/hcrypto/rc2.o \
+ $(heimdalsrcdir)/lib/hcrypto/rc4.o \
+ $(heimdalsrcdir)/lib/hcrypto/rijndael-alg-fst.o \
+ $(heimdalsrcdir)/lib/hcrypto/rnd_keys.o \
+ $(heimdalsrcdir)/lib/hcrypto/sha.o \
+ $(heimdalsrcdir)/lib/hcrypto/sha256.o \
+ $(heimdalsrcdir)/lib/hcrypto/ui.o \
+ $(heimdalsrcdir)/lib/hcrypto/evp.o \
+ $(heimdalsrcdir)/lib/hcrypto/evp-hcrypto.o \
+ $(heimdalsrcdir)/lib/hcrypto/pkcs5.o \
+ $(heimdalsrcdir)/lib/hcrypto/pkcs12.o \
+ $(heimdalsrcdir)/lib/hcrypto/rand.o \
+ $(heimdalsrcdir)/lib/hcrypto/rand-egd.o \
+ $(heimdalsrcdir)/lib/hcrypto/rand-unix.o \
+ $(heimdalsrcdir)/lib/hcrypto/rand-fortuna.o \
+ $(heimdalsrcdir)/lib/hcrypto/rand-timer.o \
+ $(heimdalsrcdir)/lib/hcrypto/hmac.o \
+ $(heimdalsrcdir)/lib/hcrypto/camellia.o \
+ $(heimdalsrcdir)/lib/hcrypto/camellia-ntt.o
+
+#######################
+# Start SUBSYSTEM HEIMDAL_HX509
+[SUBSYSTEM::HEIMDAL_HX509]
+CFLAGS = -I$(heimdalbuildsrcdir) -I$(heimdalsrcdir)/lib/hx509
+PRIVATE_DEPENDENCIES = \
+ HEIMDAL_ROKEN HEIMDAL_COM_ERR \
+ HEIMDAL_HEIM_ASN1 HEIMDAL_HCRYPTO \
+ HEIMDAL_CMS_ASN1 HEIMDAL_RFC2459_ASN1 \
+ HEIMDAL_OCSP_ASN1 HEIMDAL_PKCS8_ASN1 \
+ HEIMDAL_PKCS9_ASN1 HEIMDAL_PKCS12_ASN1 \
+ HEIMDAL_PKINIT_ASN1 HEIMDAL_PKCS10_ASN1 \
+ HEIMDAL_WIND
+# End SUBSYSTEM HEIMDAL_HX509
+#######################
+
+HEIMDAL_HX509_OBJH_FILES = \
+ $(heimdalsrcdir)/lib/hx509/ca.o \
+ $(heimdalsrcdir)/lib/hx509/cert.o \
+ $(heimdalsrcdir)/lib/hx509/cms.o \
+ $(heimdalsrcdir)/lib/hx509/collector.o \
+ $(heimdalsrcdir)/lib/hx509/crypto.o \
+ $(heimdalsrcdir)/lib/hx509/error.o \
+ $(heimdalsrcdir)/lib/hx509/env.o \
+ $(heimdalsrcdir)/lib/hx509/file.o \
+ $(heimdalsrcdir)/lib/hx509/keyset.o \
+ $(heimdalsrcdir)/lib/hx509/ks_dir.o \
+ $(heimdalsrcdir)/lib/hx509/ks_file.o \
+ $(heimdalsrcdir)/lib/hx509/ks_keychain.o \
+ $(heimdalsrcdir)/lib/hx509/ks_mem.o \
+ $(heimdalsrcdir)/lib/hx509/ks_null.o \
+ $(heimdalsrcdir)/lib/hx509/ks_p11.o \
+ $(heimdalsrcdir)/lib/hx509/ks_p12.o \
+ $(heimdalsrcdir)/lib/hx509/lock.o \
+ $(heimdalsrcdir)/lib/hx509/name.o \
+ $(heimdalsrcdir)/lib/hx509/peer.o \
+ $(heimdalsrcdir)/lib/hx509/print.o \
+ $(heimdalsrcdir)/lib/hx509/req.o \
+ $(heimdalsrcdir)/lib/hx509/revoke.o \
+ $(heimdalsrcdir)/lib/hx509/sel.o \
+ $(heimdalsrcdir)/lib/hx509/hx509_err.o
+
+HEIMDAL_HX509_OBJG_FILES = \
+ $(heimdalsrcdir)/lib/hx509/sel-lex.o \
+ $(heimdalsrcdir)/lib/hx509/sel-gram.o
+
+$(heimdalsrcdir)/lib/hx509/sel-lex.c:: $(heimdalsrcdir)/lib/hx509/sel-gram.c
+dist:: $(heimdalsrcdir)/lib/hx509/sel-lex.c
+
+HEIMDAL_HX509_OBJ_FILES = $(HEIMDAL_HX509_OBJH_FILES) $(HEIMDAL_HX509_OBJG_FILES)
+
+$(eval $(call heimdal_proto_header_template, \
+ $(heimdalsrcdir)/lib/hx509/hx509-protos.h, \
+ -R '^(_|^C)' -E HX509_LIB -q -P comment -o, \
+ $(HEIMDAL_HX509_OBJH_FILES:.o=.c), \
+ $(HEIMDAL_HX509_OBJH_FILES) $(HEIMDAL_HX509_OBJH_FILES:.o=.d) \
+))
+
+$(eval $(call heimdal_proto_header_template, \
+ $(heimdalsrcdir)/lib/hx509/hx509-private.h, \
+ -q -P comment -p, \
+ $(HEIMDAL_HX509_OBJH_FILES:.o=.c), \
+ $(HEIMDAL_HX509_OBJH_FILES) $(HEIMDAL_HX509_OBJH_FILES:.o=.d) \
+))
+
+#######################
+# Start SUBSYSTEM HEIMDAL_WIND
+[SUBSYSTEM::HEIMDAL_WIND]
+CFLAGS = -I$(heimdalbuildsrcdir) -I$(heimdalsrcdir)/lib/wind
+PRIVATE_DEPENDENCIES = \
+ HEIMDAL_ROKEN HEIMDAL_COM_ERR
+
+HEIMDAL_WIND_OBJ_FILES = \
+ $(heimdalsrcdir)/lib/wind/wind_err.o \
+ $(heimdalsrcdir)/lib/wind/stringprep.o \
+ $(heimdalsrcdir)/lib/wind/errorlist.o \
+ $(heimdalsrcdir)/lib/wind/errorlist_table.o \
+ $(heimdalsrcdir)/lib/wind/normalize.o \
+ $(heimdalsrcdir)/lib/wind/normalize_table.o \
+ $(heimdalsrcdir)/lib/wind/combining.o \
+ $(heimdalsrcdir)/lib/wind/combining_table.o \
+ $(heimdalsrcdir)/lib/wind/utf8.o \
+ $(heimdalsrcdir)/lib/wind/bidi.o \
+ $(heimdalsrcdir)/lib/wind/bidi_table.o \
+ $(heimdalsrcdir)/lib/wind/ldap.o \
+ $(heimdalsrcdir)/lib/wind/map.o \
+ $(heimdalsrcdir)/lib/wind/map_table.o
+# End SUBSYSTEM HEIMDAL_WIND
+#######################
+
+$(HEIMDAL_WIND_OBJ_FILES) $(HEIMDAL_WIND_OBJ_FILES:.o=.d):: $(heimdalsrcdir)/lib/wind/map_table.h
+
+$(heimdalsrcdir)/lib/wind/map_table.h $(heimdalsrcdir)/lib/wind/map_table.c: $(heimdalsrcdir)/lib/wind/rfc3454.txt $(heimdalsrcdir)/lib/wind/gen-map.py $(heimdalsrcdir)/lib/wind/stringprep.py
+ $(PYTHON) $(heimdalsrcdir)/lib/wind/gen-map.py $(heimdalsrcdir)/lib/wind/rfc3454.txt $(heimdalsrcdir)/lib/wind/
+
+clean::
+ @rm -f $(heimdalsrcdir)/lib/wind/map_table.h $(heimdalsrcdir)/lib/wind/map_table.c
+
+$(HEIMDAL_WIND_OBJ_FILES) $(HEIMDAL_WIND_OBJ_FILES:.o=.d):: $(heimdalsrcdir)/lib/wind/errorlist_table.h
+
+$(heimdalsrcdir)/lib/wind/errorlist_table.h $(heimdalsrcdir)/lib/wind/errorlist_table.c: $(heimdalsrcdir)/lib/wind/rfc3454.txt $(heimdalsrcdir)/lib/wind/gen-errorlist.py $(heimdalsrcdir)/lib/wind/stringprep.py
+ $(PYTHON) $(heimdalsrcdir)/lib/wind/gen-errorlist.py $(heimdalsrcdir)/lib/wind/rfc3454.txt $(heimdalsrcdir)/lib/wind/
+
+clean::
+ @rm -f $(heimdalsrcdir)/lib/wind/errorlist_table.h $(heimdalsrcdir)/lib/wind/errorlist_table.c
+
+$(HEIMDAL_WIND_OBJ_FILES) $(HEIMDAL_WIND_OBJ_FILES:.o=.d):: $(heimdalsrcdir)/lib/wind/normalize_table.h
+
+$(heimdalsrcdir)/lib/wind/normalize_table.h $(heimdalsrcdir)/lib/wind/normalize_table.c: $(heimdalsrcdir)/lib/wind/UnicodeData.txt $(heimdalsrcdir)/lib/wind/CompositionExclusions-3.2.0.txt $(heimdalsrcdir)/lib/wind/gen-normalize.py
+ $(PYTHON) $(heimdalsrcdir)/lib/wind/gen-normalize.py $(heimdalsrcdir)/lib/wind/UnicodeData.txt $(heimdalsrcdir)/lib/wind/CompositionExclusions-3.2.0.txt $(heimdalsrcdir)/lib/wind/
+
+clean::
+ @rm -f $(heimdalsrcdir)/lib/wind/normalize_table.h $(heimdalsrcdir)/lib/wind/normalize_table.c
+
+$(HEIMDAL_WIND_OBJ_FILES) $(HEIMDAL_WIND_OBJ_FILES:.o=.d):: $(heimdalsrcdir)/lib/wind/combining_table.h
+
+$(heimdalsrcdir)/lib/wind/combining_table.h $(heimdalsrcdir)/lib/wind/combining_table.c: $(heimdalsrcdir)/lib/wind/UnicodeData.txt $(heimdalsrcdir)/lib/wind/gen-combining.py
+ $(PYTHON) $(heimdalsrcdir)/lib/wind/gen-combining.py $(heimdalsrcdir)/lib/wind/UnicodeData.txt $(heimdalsrcdir)/lib/wind/
+
+clean::
+ @rm -f $(heimdalsrcdir)/lib/wind/combining_table.h $(heimdalsrcdir)/lib/wind/combining_table.c
+
+$(HEIMDAL_WIND_OBJ_FILES) $(HEIMDAL_WIND_OBJ_FILES:.o=.d):: $(heimdalsrcdir)/lib/wind/bidi_table.h
+
+$(heimdalsrcdir)/lib/wind/bidi_table.h $(heimdalsrcdir)/lib/wind/bidi_table.c: $(heimdalsrcdir)/lib/wind/rfc3454.txt $(heimdalsrcdir)/lib/wind/gen-bidi.py
+ $(PYTHON) $(heimdalsrcdir)/lib/wind/gen-bidi.py $(heimdalsrcdir)/lib/wind/rfc3454.txt $(heimdalsrcdir)/lib/wind/
+
+clean::
+ @rm -f $(heimdalsrcdir)/lib/wind/bidi_table.h $(heimdalsrcdir)/lib/wind/bidi_table.c
+
+[SUBSYSTEM::HEIMDAL_ROKEN_PROGNAME]
+
+HEIMDAL_ROKEN_PROGNAME_OBJ_FILES = \
+ $(heimdalsrcdir)/lib/roken/getprogname.o \
+ $(heimdalsrcdir)/lib/roken/setprogname.o
+$(HEIMDAL_ROKEN_PROGNAME_OBJ_FILES): CFLAGS+=-I$(heimdalbuildsrcdir) -I$(heimdalsrcdir)/lib/roken -I$(socketwrappersrcdir)
+
+[SUBSYSTEM::HEIMDAL_ROKEN_CLOSEFROM]
+
+HEIMDAL_ROKEN_CLOSEFROM_OBJ_FILES = $(heimdalsrcdir)/lib/roken/closefrom.o
+$(HEIMDAL_ROKEN_CLOSEFROM_OBJ_FILES): CFLAGS+=-I$(heimdalbuildsrcdir) -I$(heimdalsrcdir)/lib/roken -I$(socketwrappersrcdir)
+
+[SUBSYSTEM::HEIMDAL_ROKEN_PROGNAME_H]
+
+HEIMDAL_ROKEN_PROGNAME_H_OBJ_FILES = \
+ $(heimdalsrcdir)/lib/roken/getprogname.ho \
+ $(heimdalsrcdir)/lib/roken/setprogname.ho
+$(HEIMDAL_ROKEN_PROGNAME_H_OBJ_FILES): CFLAGS+=-I$(heimdalbuildsrcdir) -I$(heimdalsrcdir)/lib/roken -DSOCKET_WRAPPER_DISABLE=1
+
+[SUBSYSTEM::HEIMDAL_ROKEN_CLOSEFROM_H]
+
+HEIMDAL_ROKEN_CLOSEFROM_H_OBJ_FILES = $(heimdalsrcdir)/lib/roken/closefrom.ho
+$(HEIMDAL_ROKEN_CLOSEFROM_H_OBJ_FILES): CFLAGS+=-I$(heimdalbuildsrcdir) -I$(heimdalsrcdir)/lib/roken -DSOCKET_WRAPPER_DISABLE=1
+
+#######################
+# Start SUBSYSTEM HEIMDAL_ROKEN
+[SUBSYSTEM::HEIMDAL_ROKEN]
+CFLAGS = -I$(heimdalbuildsrcdir) -I$(heimdalsrcdir)/lib/roken -I$(socketwrappersrcdir)
+PRIVATE_DEPENDENCIES = \
+ HEIMDAL_ROKEN_PROGNAME \
+ HEIMDAL_ROKEN_CLOSEFROM \
+ RESOLV \
+ LIBREPLACE_NETWORK
+# End SUBSYSTEM HEIMDAL_ROKEN
+#######################
+
+HEIMDAL_ROKEN_OBJ_FILES = \
+ $(heimdalsrcdir)/lib/roken/base64.o \
+ $(heimdalsrcdir)/lib/roken/hex.o \
+ $(heimdalsrcdir)/lib/roken/bswap.o \
+ $(heimdalsrcdir)/lib/roken/dumpdata.o \
+ $(heimdalsrcdir)/lib/roken/emalloc.o \
+ $(heimdalsrcdir)/lib/roken/ecalloc.o \
+ $(heimdalsrcdir)/lib/roken/getarg.o \
+ $(heimdalsrcdir)/lib/roken/get_window_size.o \
+ $(heimdalsrcdir)/lib/roken/h_errno.o \
+ $(heimdalsrcdir)/lib/roken/issuid.o \
+ $(heimdalsrcdir)/lib/roken/net_read.o \
+ $(heimdalsrcdir)/lib/roken/net_write.o \
+ $(heimdalsrcdir)/lib/roken/socket.o \
+ $(heimdalsrcdir)/lib/roken/parse_time.o \
+ $(heimdalsrcdir)/lib/roken/parse_units.o \
+ $(heimdalsrcdir)/lib/roken/resolve.o \
+ $(heimdalsrcdir)/lib/roken/roken_gethostby.o \
+ $(heimdalsrcdir)/lib/roken/signal.o \
+ $(heimdalsrcdir)/lib/roken/vis.o \
+ $(heimdalsrcdir)/lib/roken/strlwr.o \
+ $(heimdalsrcdir)/lib/roken/strsep_copy.o \
+ $(heimdalsrcdir)/lib/roken/strsep.o \
+ $(heimdalsrcdir)/lib/roken/strupr.o \
+ $(heimdalsrcdir)/lib/roken/strpool.o \
+ $(heimdalsrcdir)/lib/roken/estrdup.o \
+ $(heimdalsrcdir)/lib/roken/erealloc.o \
+ $(heimdalsrcdir)/lib/roken/simple_exec.o \
+ $(heimdalsrcdir)/lib/roken/strcollect.o \
+ $(heimdalsrcdir)/lib/roken/rtbl.o \
+ $(heimdalsrcdir)/lib/roken/cloexec.o \
+ $(heimdalsrcdir)/lib/roken/xfree.o \
+ $(heimdalbuildsrcdir)/replace.o
+
+$(HEIMDAL_ROKEN_OBJ_FILES) $(HEIMDAL_ROKEN_OBJ_FILES:.o=.d):: $(heimdalsrcdir)/lib/roken/roken.h
+
+[SUBSYSTEM::HEIMDAL_ROKEN_H]
+CFLAGS = -I$(heimdalbuildsrcdir) -I$(heimdalsrcdir)/lib/roken -DSOCKET_WRAPPER_DISABLE=1
+PRIVATE_DEPENDENCIES = \
+ HEIMDAL_ROKEN_PROGNAME_H \
+ HEIMDAL_ROKEN_CLOSEFROM_H \
+ RESOLV \
+ LIBREPLACE_NETWORK
+
+HEIMDAL_ROKEN_H_OBJ_FILES = $(HEIMDAL_ROKEN_OBJ_FILES:.o=.ho)
+$(HEIMDAL_ROKEN_H_OBJ_FILES:.ho=.hd):: $(heimdalsrcdir)/lib/roken/roken.h
+
+$(heimdalsrcdir)/lib/roken/roken.h:
+ @echo 'Creating $(heimdalsrcdir)/lib/roken/roken.h'
+ @echo '#include "heimdal_build/roken.h"' > $(heimdalsrcdir)/lib/roken/roken.h
+
+clean::
+ @rm -f $(heimdalsrcdir)/lib/roken/roken.h
+
+#######################
+# Start SUBSYSTEM HEIMDAL_COM_ERR
+[SUBSYSTEM::HEIMDAL_COM_ERR]
+CFLAGS = -I$(heimdalbuildsrcdir) -I$(heimdalsrcdir)/lib/com_err
+PRIVATE_DEPENDENCIES = HEIMDAL_ROKEN
+# End SUBSYSTEM HEIMDAL_COM_ERR
+#######################
+
+HEIMDAL_COM_ERR_OBJ_FILES = \
+ $(heimdalsrcdir)/lib/com_err/com_err.o \
+ $(heimdalsrcdir)/lib/com_err/error.o
+
+#######################
+# Start BINARY asn1_compile
+[BINARY::asn1_compile]
+USE_HOSTCC = YES
+PRIVATE_DEPENDENCIES = HEIMDAL_ROKEN_H
+
+ASN1C = $(builddir)/bin/asn1_compile
+
+asn1_compile_ASN1_OBJ_FILES = \
+ $(heimdalsrcdir)/lib/asn1/main.ho \
+ $(heimdalsrcdir)/lib/asn1/gen.ho \
+ $(heimdalsrcdir)/lib/asn1/gen_copy.ho \
+ $(heimdalsrcdir)/lib/asn1/gen_decode.ho \
+ $(heimdalsrcdir)/lib/asn1/gen_encode.ho \
+ $(heimdalsrcdir)/lib/asn1/gen_free.ho \
+ $(heimdalsrcdir)/lib/asn1/gen_glue.ho \
+ $(heimdalsrcdir)/lib/asn1/gen_length.ho \
+ $(heimdalsrcdir)/lib/asn1/gen_seq.ho \
+ $(heimdalsrcdir)/lib/asn1/hash.ho \
+ $(heimdalsrcdir)/lib/asn1/symbol.ho \
+ $(heimdalsrcdir)/lib/asn1/parse.ho \
+ $(heimdalsrcdir)/lib/asn1/lex.ho
+
+$(heimdalsrcdir)/lib/asn1/lex.c:: $(heimdalsrcdir)/lib/asn1/parse.c
+dist:: $(heimdalsrcdir)/lib/asn1/lex.c
+
+asn1_compile_OBJ_FILES = \
+ $(asn1_compile_ASN1_OBJ_FILES) \
+ $(heimdalsrcdir)/lib/vers/print_version.ho
+
+$(asn1_compile_OBJ_FILES): CFLAGS+=-I$(heimdalbuildsrcdir) -I$(heimdalsrcdir)/lib/asn1 -I$(heimdalsrcdir)/lib/roken -DSOCKET_WRAPPER_DISABLE=1
+
+$(eval $(call heimdal_proto_header_template, \
+ $(heimdalsrcdir)/lib/asn1/der-protos.h, \
+ -q -P comment -o, \
+ $(HEIMDAL_HEIM_ASN1_DER_OBJ_FILES:.o=.c), \
+ $(asn1_compile_ASN1_OBJ_FILES) $(asn1_compile_ASN1_OBJ_FILES:.ho=.hd) \
+))
+
+# End BINARY asn1_compile
+#######################
+
+#######################
+# Start BINARY compile_et
+[BINARY::compile_et]
+USE_HOSTCC = YES
+PRIVATE_DEPENDENCIES = HEIMDAL_ROKEN_H
+# End BINARY compile_et
+#######################
+
+ET_COMPILER = $(builddir)/bin/compile_et
+
+compile_et_OBJ_FILES = $(heimdalsrcdir)/lib/vers/print_version.ho \
+ $(heimdalsrcdir)/lib/com_err/parse.ho \
+ $(heimdalsrcdir)/lib/com_err/lex.ho \
+ $(heimdalsrcdir)/lib/com_err/compile_et.ho
+
+$(compile_et_OBJ_FILES): CFLAGS+=-I$(heimdalbuildsrcdir) -I$(heimdalsrcdir)/lib/com_err -I$(heimdalsrcdir)/lib/roken -DSOCKET_WRAPPER_DISABLE=1
+
+$(heimdalsrcdir)/lib/com_err/lex.c:: $(heimdalsrcdir)/lib/com_err/parse.c
+dist:: $(heimdalsrcdir)/lib/com_err/lex.c
+
+mkinclude perl_path_wrapper.sh asn1_deps.pl lib/hdb/hdb.asn1 hdb_asn1 \$\(heimdalsrcdir\)/lib/hdb |
+mkinclude perl_path_wrapper.sh asn1_deps.pl lib/gssapi/spnego/spnego.asn1 spnego_asn1 \$\(heimdalsrcdir\)/lib/gssapi --sequence=MechTypeList |
+mkinclude perl_path_wrapper.sh asn1_deps.pl lib/gssapi/mech/gssapi.asn1 gssapi_asn1 \$\(heimdalsrcdir\)/lib/gssapi|
+mkinclude perl_path_wrapper.sh asn1_deps.pl lib/asn1/k5.asn1 krb5_asn1 \$\(heimdalsrcdir\)/lib/asn1 --encode-rfc1510-bit-string --sequence=KRB5SignedPathPrincipals --sequence=AuthorizationData --sequence=METHOD-DATA|
+mkinclude perl_path_wrapper.sh asn1_deps.pl lib/asn1/digest.asn1 digest_asn1 \$\(heimdalsrcdir\)/lib/asn1|
+mkinclude perl_path_wrapper.sh asn1_deps.pl lib/asn1/pkcs8.asn1 pkcs8_asn1 \$\(heimdalsrcdir\)/lib/asn1|
+mkinclude perl_path_wrapper.sh asn1_deps.pl lib/asn1/pkcs9.asn1 pkcs9_asn1 \$\(heimdalsrcdir\)/lib/asn1|
+mkinclude perl_path_wrapper.sh asn1_deps.pl lib/asn1/pkcs12.asn1 pkcs12_asn1 \$\(heimdalsrcdir\)/lib/asn1|
+mkinclude perl_path_wrapper.sh asn1_deps.pl lib/asn1/rfc2459.asn1 rfc2459_asn1 \$\(heimdalsrcdir\)/lib/asn1 --preserve-binary=TBSCertificate --preserve-binary=TBSCRLCertList --preserve-binary=Name --sequence=GeneralNames --sequence=Extensions --sequence=CRLDistributionPoints|
+mkinclude perl_path_wrapper.sh asn1_deps.pl lib/asn1/pkinit.asn1 pkinit_asn1 \$\(heimdalsrcdir\)/lib/asn1|
+mkinclude perl_path_wrapper.sh asn1_deps.pl lib/asn1/CMS.asn1 cms_asn1 \$\(heimdalsrcdir\)/lib/asn1|
+mkinclude perl_path_wrapper.sh asn1_deps.pl lib/hx509/ocsp.asn1 ocsp_asn1 \$\(heimdalsrcdir\)/lib/hx509 --preserve-binary=OCSPTBSRequest --preserve-binary=OCSPResponseData|
+mkinclude perl_path_wrapper.sh asn1_deps.pl lib/asn1/kx509.asn1 kx509_asn1 \$\(heimdalsrcdir\)/lib/asn1|
+mkinclude perl_path_wrapper.sh asn1_deps.pl lib/hx509/pkcs10.asn1 pkcs10_asn1 \$\(heimdalsrcdir\)/lib/hx509 --preserve-binary=CertificationRequestInfo|
+
+mkinclude perl_path_wrapper.sh et_deps.pl lib/asn1/asn1_err.et \$\(heimdalsrcdir\)/lib/asn1|
+mkinclude perl_path_wrapper.sh et_deps.pl lib/hdb/hdb_err.et \$\(heimdalsrcdir\)/lib/hdb|
+mkinclude perl_path_wrapper.sh et_deps.pl lib/krb5/heim_err.et \$\(heimdalsrcdir\)/lib/krb5|
+mkinclude perl_path_wrapper.sh et_deps.pl lib/krb5/k524_err.et \$\(heimdalsrcdir\)/lib/krb5|
+mkinclude perl_path_wrapper.sh et_deps.pl lib/krb5/krb_err.et \$\(heimdalsrcdir\)/lib/krb5|
+mkinclude perl_path_wrapper.sh et_deps.pl lib/krb5/krb5_err.et \$\(heimdalsrcdir\)/lib/krb5|
+mkinclude perl_path_wrapper.sh et_deps.pl lib/gssapi/krb5/gkrb5_err.et \$\(heimdalsrcdir\)/lib/gssapi|
+mkinclude perl_path_wrapper.sh et_deps.pl lib/hx509/hx509_err.et \$\(heimdalsrcdir\)/lib/hx509|
+mkinclude perl_path_wrapper.sh et_deps.pl lib/wind/wind_err.et \$\(heimdalsrcdir\)/lib/wind|
+
+clean::
+ @-rm -f bin/compile_et bin/asn1_compile
+
+#######################
+# Start BINARY compile_et
+[BINARY::samba4kinit]
+PRIVATE_DEPENDENCIES = HEIMDAL_KRB5 HEIMDAL_NTLM
+# End BINARY compile_et
+#######################
+
+samba4kinit_OBJ_FILES = $(heimdalsrcdir)/kuser/kinit.o \
+ $(heimdalsrcdir)/lib/vers/print_version.o
+
+$(samba4kinit_OBJ_FILES): CFLAGS+=-I$(heimdalbuildsrcdir) -I$(heimdalsrcdir)/lib/roken
+
+#######################
+# Start BINARY compile_et
+[BINARY::samba4kpasswd]
+PRIVATE_DEPENDENCIES = HEIMDAL_KRB5 HEIMDAL_NTLM
+# End BINARY compile_et
+#######################
+
+samba4kpasswd_OBJ_FILES = $(heimdalsrcdir)/kpasswd/kpasswd.o \
+ $(heimdalsrcdir)/lib/vers/print_version.o
+
+$(samba4kpasswd_OBJ_FILES): CFLAGS+=-I$(heimdalbuildsrcdir) -I$(heimdalsrcdir)/lib/roken
+
+#######################
+# Start BINARY compile_et
+[BINARY::rkpty]
+PRIVATE_DEPENDENCIES = HEIMDAL_ROKEN OPENPTY
+# End BINARY compile_et
+#######################
+
+rkpty_OBJ_FILES = $(heimdalsrcdir)/lib/roken/rkpty.o \
+ $(socketwrappersrcdir)/socket_wrapper.o
+
+$(rkpty_OBJ_FILES): CFLAGS+=-I$(heimdalbuildsrcdir) -I$(heimdalsrcdir)/lib/roken -DPACKAGE=\"Samba\"
diff --git a/source4/heimdal_build/kafs.h b/source4/heimdal_build/kafs.h
new file mode 100644
index 0000000000..878dd08397
--- /dev/null
+++ b/source4/heimdal_build/kafs.h
@@ -0,0 +1,19 @@
+int k_hasafs (void) {
+ return 0;
+};
+
+int krb_afslog (const char *cell, const char *realm) {
+ return 0;
+};
+int k_unlog (void) {
+ return 0;
+};
+int k_setpag (void) {
+ return 0;
+};
+krb5_error_code krb5_afslog (krb5_context context,
+ krb5_ccache id,
+ const char *cell,
+ krb5_const_realm realm) {
+ return 0;
+};
diff --git a/source4/heimdal_build/kpasswdd-glue.h b/source4/heimdal_build/kpasswdd-glue.h
new file mode 100644
index 0000000000..47ac26d285
--- /dev/null
+++ b/source4/heimdal_build/kpasswdd-glue.h
@@ -0,0 +1,6 @@
+/* TODO: remove this file */
+struct _krb5_krb_auth_data;
+struct krb5_dh_moduli;
+struct AlgorithmIdentifier;
+#include "heimdal/lib/hcrypto/evp.h"
+#include "heimdal/lib/krb5/krb5-private.h"
diff --git a/source4/heimdal_build/krb5-glue.c b/source4/heimdal_build/krb5-glue.c
new file mode 100644
index 0000000000..8a09a91f3e
--- /dev/null
+++ b/source4/heimdal_build/krb5-glue.c
@@ -0,0 +1,47 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ provide glue functions between heimdal and samba
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/network.h"
+#include "system/kerberos.h"
+#include "lib/socket/netif.h"
+#include "param/param.h"
+
+#include "heimdal/lib/krb5/krb5_locl.h"
+
+const krb5_cc_ops krb5_scc_ops = {
+ KRB5_CC_OPS_VERSION,
+ "_NOTSUPPORTED_SDB",
+ NULL, /* scc_retrieve */
+ NULL, /* scc_get_principal */
+ NULL, /* scc_get_first */
+ NULL, /* scc_get_next */
+ NULL, /* scc_end_get */
+ NULL, /* scc_remove_cred */
+ NULL, /* scc_set_flags */
+ NULL,
+ NULL, /* scc_get_cache_first */
+ NULL, /* scc_get_cache_next */
+ NULL, /* scc_end_cache_get */
+ NULL, /* scc_move */
+ NULL, /* scc_get_default_name */
+ NULL /* scc_set_default */
+};
diff --git a/source4/heimdal_build/krb5-types.h b/source4/heimdal_build/krb5-types.h
new file mode 100644
index 0000000000..cdc5a3cba9
--- /dev/null
+++ b/source4/heimdal_build/krb5-types.h
@@ -0,0 +1,13 @@
+/* krb5-types.h -- this file was generated for i686-pc-linux-gnu by
+ $Id: bits.c,v 1.23 2005/01/05 15:22:02 lha Exp $ */
+
+#ifndef __krb5_types_h__
+#define __krb5_types_h__
+
+#include "replace.h"
+#include "system/network.h"
+
+typedef socklen_t krb5_socklen_t;
+typedef ssize_t krb5_ssize_t;
+
+#endif /* __krb5_types_h__ */
diff --git a/source4/heimdal_build/krb5/windc_plugin.h b/source4/heimdal_build/krb5/windc_plugin.h
new file mode 100644
index 0000000000..1df5fd3eb6
--- /dev/null
+++ b/source4/heimdal_build/krb5/windc_plugin.h
@@ -0,0 +1 @@
+#include "heimdal/kdc/windc_plugin.h"
diff --git a/source4/heimdal_build/perl_path_wrapper.sh b/source4/heimdal_build/perl_path_wrapper.sh
new file mode 100755
index 0000000000..f739171206
--- /dev/null
+++ b/source4/heimdal_build/perl_path_wrapper.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+#
+
+SELF=$0
+DIR=`dirname $SELF`
+
+$PERL $DIR/$@
diff --git a/source4/heimdal_build/print_version.h b/source4/heimdal_build/print_version.h
new file mode 100644
index 0000000000..e4790c5166
--- /dev/null
+++ b/source4/heimdal_build/print_version.h
@@ -0,0 +1 @@
+/* this should be empty */
diff --git a/source4/heimdal_build/replace.c b/source4/heimdal_build/replace.c
new file mode 100644
index 0000000000..41309fea6e
--- /dev/null
+++ b/source4/heimdal_build/replace.c
@@ -0,0 +1,86 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ some replacement functions for parts of roken that don't fit easily into
+ our build system
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "config.h"
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include "err.h"
+#include "roken.h"
+
+#ifndef HAVE_ERR
+ void err(int eval, const char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ vfprintf(stderr, format, ap);
+ perror("");
+ va_end(ap);
+ exit(eval);
+}
+#endif
+
+#ifndef HAVE_ERRX
+ void errx(int eval, const char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ vfprintf(stderr, format, ap);
+ va_end(ap);
+ exit(eval);
+}
+#endif
+
+#ifndef HAVE_WARNX
+ void warnx(const char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ vfprintf(stderr, format, ap);
+ va_end(ap);
+}
+#endif
+
+#ifndef HAVE_FLOCK
+ int flock(int fd, int op)
+{
+ struct flock lock;
+ lock.l_whence = 0;
+ lock.l_start = 0;
+ lock.l_len = 0;
+ lock.l_pid = 0;
+
+ switch (op & (LOCK_UN|LOCK_SH|LOCK_EX)) {
+ case LOCK_UN:
+ lock.l_type = F_UNLCK;
+ return fcntl(fd, F_SETLK, &lock);
+ case LOCK_SH:
+ lock.l_type = F_RDLCK;
+ return fcntl(fd, (op&LOCK_NB)?F_SETLK:F_SETLKW, &lock);
+ case LOCK_EX:
+ lock.l_type = F_WRLCK;
+ return fcntl(fd, (op&LOCK_NB)?F_SETLK:F_SETLKW, &lock);
+ }
+ errno = EINVAL;
+ return -1;
+}
+#endif
diff --git a/source4/heimdal_build/roken.h b/source4/heimdal_build/roken.h
new file mode 100644
index 0000000000..3edeb2fb2e
--- /dev/null
+++ b/source4/heimdal_build/roken.h
@@ -0,0 +1,87 @@
+/*
+ a wrapper to override some of the defines that the heimdal roken system looks at
+ */
+#ifndef _ROKEN_H_
+#define _ROKEN_H_
+
+/* path to sysconf - should we force this to samba LIBDIR ? */
+#define SYSCONFDIR "/etc"
+
+/* HDB module dir - set to Samba LIBDIR/hdb ? */
+#define HDBDIR "/usr/heimdal/lib"
+#define LIBDIR "/usr/heimdal/lib"
+
+/* Maximum values on all known systems */
+#define MaxHostNameLen (64+4)
+#define MaxPathLen (1024+4)
+
+/* We want PKINIT */
+#define PKINIT 1
+
+#define VERSIONLIST {"Lorikeet-Heimdal, Modified for Samba4 0.8pre"}
+
+#define VERSION "Samba"
+
+#define ROKEN_LIB_FUNCTION
+
+#define GETHOSTBYADDR_PROTO_COMPATIBLE
+#define GETSERVBYNAME_PROTO_COMPATIBLE
+#define OPENLOG_PROTO_COMPATIBLE
+#define GETSOCKNAME_PROTO_COMPATIBLE
+
+/* even if we do have dlopen, we don't want heimdal using it */
+#undef HAVE_DLOPEN
+
+/* we need to tell roken about the functions that Samba replaces in lib/replace */
+#ifndef HAVE_SETEUID
+#define HAVE_SETEUID 1
+#endif
+
+#ifndef HAVE_STRNDUP
+#define HAVE_STRNDUP
+#endif
+
+#ifndef HAVE_SETENV
+#define HAVE_SETENV
+#endif
+
+#ifndef HAVE_UNSETENV
+#define HAVE_UNSETENV
+#endif
+
+#ifndef HAVE_VSYSLOG
+#define HAVE_VSYSLOG
+#endif
+
+#ifndef HAVE_SSIZE_T
+#define HAVE_SSIZE_T
+#endif
+
+#ifndef HAVE_STRPTIME
+#define HAVE_STRPTIME
+#endif
+
+#ifndef HAVE_TIMEGM
+#define HAVE_TIMEGM
+#endif
+
+#ifndef HAVE_INNETGR
+#define HAVE_INNETGR
+#endif
+
+#if (__GNUC__ >= 3) && (__GNUC_MINOR__ >= 1 )
+#ifndef HAVE___ATTRIBUTE__
+#define HAVE___ATTRIBUTE__
+#endif
+#endif
+
+#include "system/network.h"
+
+/*
+ * we don't want that roken.h.in includes socket_wrapper
+ * we include socket_wrapper via "system/network.h"
+ */
+#undef SOCKET_WRAPPER_REPLACE
+#include "heimdal/lib/roken/roken.h.in"
+
+#endif
diff --git a/source4/heimdal_build/vis.h b/source4/heimdal_build/vis.h
new file mode 100644
index 0000000000..4389993ebd
--- /dev/null
+++ b/source4/heimdal_build/vis.h
@@ -0,0 +1,15 @@
+#ifndef _HEIMDAL_BUILD_VIS_H
+#define _HEIMDAL_BUILD_VIS_H
+
+#include "system/locale.h"
+
+#ifndef __BEGIN_DECLS
+#define __BEGIN_DECLS
+#endif
+
+#ifndef __END_DECLS
+#define __END_DECLS
+#endif
+
+#include "heimdal/lib/roken/vis.hin"
+#endif
diff --git a/source4/include/includes.h b/source4/include/includes.h
new file mode 100644
index 0000000000..d9b7759e7e
--- /dev/null
+++ b/source4/include/includes.h
@@ -0,0 +1,72 @@
+#ifndef _INCLUDES_H
+#define _INCLUDES_H
+/*
+ Unix SMB/CIFS implementation.
+ Machine customisation and include handling
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) 2002 by Martin Pool <mbp@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "../replace/replace.h"
+
+/* make sure we have included the correct config.h */
+#ifndef NO_CONFIG_H /* for some tests */
+#ifndef CONFIG_H_IS_FROM_SAMBA
+#error "make sure you have removed all config.h files from standalone builds!"
+#error "the included config.h isn't from samba!"
+#endif
+#endif /* NO_CONFIG_H */
+
+#include "system/time.h"
+#include "system/wait.h"
+
+/* only do the C++ reserved word check when we compile
+ to include --with-developer since too many systems
+ still have comflicts with their header files (e.g. IRIX 6.4) */
+
+#if !defined(__cplusplus) && defined(DEVELOPER)
+#define class #error DONT_USE_CPLUSPLUS_RESERVED_NAMES
+#define private #error DONT_USE_CPLUSPLUS_RESERVED_NAMES
+#define public #error DONT_USE_CPLUSPLUS_RESERVED_NAMES
+#define protected #error DONT_USE_CPLUSPLUS_RESERVED_NAMES
+#define template #error DONT_USE_CPLUSPLUS_RESERVED_NAMES
+#define this #error DONT_USE_CPLUSPLUS_RESERVED_NAMES
+#define new #error DONT_USE_CPLUSPLUS_RESERVED_NAMES
+#define delete #error DONT_USE_CPLUSPLUS_RESERVED_NAMES
+#define friend #error DONT_USE_CPLUSPLUS_RESERVED_NAMES
+#endif
+
+/* Lists, trees, caching, database... */
+#include <talloc.h>
+#ifndef _PRINTF_ATTRIBUTE
+#define _PRINTF_ATTRIBUTE(a1, a2) PRINTF_ATTRIBUTE(a1, a2)
+#endif
+#include "../lib/util/xfile.h"
+#include "../lib/util/attr.h"
+#include "../lib/util/debug.h"
+#include "../lib/util/util.h"
+
+#include "libcli/util/error.h"
+
+/* String routines */
+#include "../lib/util/safe_string.h"
+
+#if 0
+/* darn, we can't do this now that we don't link the ldb tools to all the smb libs */
+#define TALLOC_ABORT(reason) smb_panic(reason)
+#endif
+
+#endif /* _INCLUDES_H */
diff --git a/source4/install-sh b/source4/install-sh
new file mode 100755
index 0000000000..58719246f0
--- /dev/null
+++ b/source4/install-sh
@@ -0,0 +1,238 @@
+#! /bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.
+#
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+transformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+ case $1 in
+ -c) instcmd="$cpprog"
+ shift
+ continue;;
+
+ -d) dir_arg=true
+ shift
+ continue;;
+
+ -m) chmodcmd="$chmodprog $2"
+ shift
+ shift
+ continue;;
+
+ -o) chowncmd="$chownprog $2"
+ shift
+ shift
+ continue;;
+
+ -g) chgrpcmd="$chgrpprog $2"
+ shift
+ shift
+ continue;;
+
+ -s) stripcmd="$stripprog"
+ shift
+ continue;;
+
+ -t=*) transformarg=`echo $1 | sed 's/-t=//'`
+ shift
+ continue;;
+
+ -b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+ shift
+ continue;;
+
+ *) if [ x"$src" = x ]
+ then
+ src=$1
+ else
+ # this colon is to work around a 386BSD /bin/sh bug
+ :
+ dst=$1
+ fi
+ shift
+ continue;;
+ esac
+done
+
+if [ x"$src" = x ]
+then
+ echo "install: no input file specified"
+ exit 1
+else
+ true
+fi
+
+if [ x"$dir_arg" != x ]; then
+ dst=$src
+ src=""
+
+ if [ -d $dst ]; then
+ instcmd=:
+ else
+ instcmd=mkdir
+ fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad
+# if $src (and thus $dsttmp) contains '*'.
+
+ if [ -f $src -o -d $src ]
+ then
+ true
+ else
+ echo "install: $src does not exist"
+ exit 1
+ fi
+
+ if [ x"$dst" = x ]
+ then
+ echo "install: no destination specified"
+ exit 1
+ else
+ true
+ fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+ if [ -d $dst ]
+ then
+ dst="$dst"/`basename $src`
+ else
+ true
+ fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+# this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+ pathcomp="${pathcomp}${1}"
+ shift
+
+ if [ ! -d "${pathcomp}" ] ;
+ then
+ $mkdirprog "${pathcomp}"
+ else
+ true
+ fi
+
+ pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+ $doit $instcmd $dst &&
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+ if [ x"$transformarg" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ dstfile=`basename $dst $transformbasename |
+ sed $transformarg`$transformbasename
+ fi
+
+# don't allow the sed command to completely eliminate the filename
+
+ if [ x"$dstfile" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ true
+ fi
+
+# Make a temp file name in the proper directory.
+
+ dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+ $doit $instcmd $src $dsttmp &&
+
+ trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing. If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+ $doit $rmcmd -f $dstdir/$dstfile &&
+ $doit $mvcmd $dsttmp $dstdir/$dstfile
+
+fi &&
+
+
+exit 0
diff --git a/source4/kdc/config.m4 b/source4/kdc/config.m4
new file mode 100644
index 0000000000..409968e8b5
--- /dev/null
+++ b/source4/kdc/config.m4
@@ -0,0 +1 @@
+SMB_ENABLE(server_service_kdc, $HAVE_KRB5)
diff --git a/source4/kdc/config.mk b/source4/kdc/config.mk
new file mode 100644
index 0000000000..bd8a313316
--- /dev/null
+++ b/source4/kdc/config.mk
@@ -0,0 +1,26 @@
+# KDC server subsystem
+
+#######################
+# Start SUBSYSTEM KDC
+[MODULE::KDC]
+INIT_FUNCTION = server_service_kdc_init
+SUBSYSTEM = service
+PRIVATE_DEPENDENCIES = \
+ HEIMDAL_KDC HDB_SAMBA4
+# End SUBSYSTEM KDC
+#######################
+
+KDC_OBJ_FILES = $(addprefix $(kdcsrcdir)/, kdc.o kpasswdd.o)
+
+#######################
+# Start SUBSYSTEM KDC
+[SUBSYSTEM::HDB_SAMBA4]
+CFLAGS = -Iheimdal/kdc -Iheimdal/lib/hdb
+PRIVATE_DEPENDENCIES = \
+ LIBLDB auth_sam auth_sam_reply CREDENTIALS \
+ HEIMDAL_HDB
+# End SUBSYSTEM KDC
+#######################
+
+HDB_SAMBA4_OBJ_FILES = $(addprefix $(kdcsrcdir)/, hdb-samba4.o pac-glue.o)
+$(eval $(call proto_header_template,$(kdcsrcdir)/pac_glue.h,$(HDB_SAMBA4_OBJ_FILES:.o=.c)))
diff --git a/source4/kdc/hdb-samba4.c b/source4/kdc/hdb-samba4.c
new file mode 100644
index 0000000000..daeed77975
--- /dev/null
+++ b/source4/kdc/hdb-samba4.c
@@ -0,0 +1,1558 @@
+/*
+ * Copyright (c) 1999-2001, 2003, PADL Software Pty Ltd.
+ * Copyright (c) 2004, Andrew Bartlett <abartlet@samba.org>.
+ * Copyright (c) 2004, Stefan Metzmacher <metze@samba.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of PADL Software nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "includes.h"
+#include "system/time.h"
+#include "dsdb/common/flags.h"
+#include "lib/ldb/include/ldb.h"
+#include "lib/ldb/include/ldb_errors.h"
+#include "librpc/gen_ndr/netlogon.h"
+#include "auth/auth.h"
+#include "auth/credentials/credentials.h"
+#include "auth/auth_sam.h"
+#include "../lib/util/util_ldb.h"
+#include "dsdb/samdb/samdb.h"
+#include "librpc/ndr/libndr.h"
+#include "librpc/gen_ndr/ndr_drsblobs.h"
+#include "librpc/gen_ndr/lsa.h"
+#include "libcli/auth/libcli_auth.h"
+#include "param/param.h"
+#include "events/events.h"
+#include "kdc/kdc.h"
+#include "../lib/crypto/md4.h"
+
+enum hdb_ldb_ent_type
+{ HDB_SAMBA4_ENT_TYPE_CLIENT, HDB_SAMBA4_ENT_TYPE_SERVER,
+ HDB_SAMBA4_ENT_TYPE_KRBTGT, HDB_SAMBA4_ENT_TYPE_TRUST, HDB_SAMBA4_ENT_TYPE_ANY };
+
+enum trust_direction {
+ UNKNOWN = 0,
+ INBOUND = LSA_TRUST_DIRECTION_INBOUND,
+ OUTBOUND = LSA_TRUST_DIRECTION_OUTBOUND
+};
+
+static const char *realm_ref_attrs[] = {
+ "nCName",
+ "dnsRoot",
+ NULL
+};
+
+static const char *trust_attrs[] = {
+ "trustPartner",
+ "trustAuthIncoming",
+ "trustAuthOutgoing",
+ "whenCreated",
+ "msDS-SupportedEncryptionTypes",
+ "trustAttributes",
+ "trustDirection",
+ "trustType",
+ NULL
+};
+
+static KerberosTime ldb_msg_find_krb5time_ldap_time(struct ldb_message *msg, const char *attr, KerberosTime default_val)
+{
+ const char *tmp;
+ const char *gentime;
+ struct tm tm;
+
+ gentime = ldb_msg_find_attr_as_string(msg, attr, NULL);
+ if (!gentime)
+ return default_val;
+
+ tmp = strptime(gentime, "%Y%m%d%H%M%SZ", &tm);
+ if (tmp == NULL) {
+ return default_val;
+ }
+
+ return timegm(&tm);
+}
+
+static HDBFlags uf2HDBFlags(krb5_context context, int userAccountControl, enum hdb_ldb_ent_type ent_type)
+{
+ HDBFlags flags = int2HDBFlags(0);
+
+ /* we don't allow kadmin deletes */
+ flags.immutable = 1;
+
+ /* mark the principal as invalid to start with */
+ flags.invalid = 1;
+
+ flags.renewable = 1;
+
+ /* All accounts are servers, but this may be disabled again in the caller */
+ flags.server = 1;
+
+ /* Account types - clear the invalid bit if it turns out to be valid */
+ if (userAccountControl & UF_NORMAL_ACCOUNT) {
+ if (ent_type == HDB_SAMBA4_ENT_TYPE_CLIENT || ent_type == HDB_SAMBA4_ENT_TYPE_ANY) {
+ flags.client = 1;
+ }
+ flags.invalid = 0;
+ }
+
+ if (userAccountControl & UF_INTERDOMAIN_TRUST_ACCOUNT) {
+ if (ent_type == HDB_SAMBA4_ENT_TYPE_CLIENT || ent_type == HDB_SAMBA4_ENT_TYPE_ANY) {
+ flags.client = 1;
+ }
+ flags.invalid = 0;
+ }
+ if (userAccountControl & UF_WORKSTATION_TRUST_ACCOUNT) {
+ if (ent_type == HDB_SAMBA4_ENT_TYPE_CLIENT || ent_type == HDB_SAMBA4_ENT_TYPE_ANY) {
+ flags.client = 1;
+ }
+ flags.invalid = 0;
+ }
+ if (userAccountControl & UF_SERVER_TRUST_ACCOUNT) {
+ if (ent_type == HDB_SAMBA4_ENT_TYPE_CLIENT || ent_type == HDB_SAMBA4_ENT_TYPE_ANY) {
+ flags.client = 1;
+ }
+ flags.invalid = 0;
+ }
+
+ /* Not permitted to act as a client if disabled */
+ if (userAccountControl & UF_ACCOUNTDISABLE) {
+ flags.client = 0;
+ }
+ if (userAccountControl & UF_LOCKOUT) {
+ flags.invalid = 1;
+ }
+/*
+ if (userAccountControl & UF_PASSWORD_NOTREQD) {
+ flags.invalid = 1;
+ }
+*/
+/*
+ UF_PASSWORD_CANT_CHANGE and UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED are irrelevent
+*/
+ if (userAccountControl & UF_TEMP_DUPLICATE_ACCOUNT) {
+ flags.invalid = 1;
+ }
+
+/* UF_DONT_EXPIRE_PASSWD and UF_USE_DES_KEY_ONLY handled in LDB_message2entry() */
+
+/*
+ if (userAccountControl & UF_MNS_LOGON_ACCOUNT) {
+ flags.invalid = 1;
+ }
+*/
+ if (userAccountControl & UF_SMARTCARD_REQUIRED) {
+ flags.require_hwauth = 1;
+ }
+ if (userAccountControl & UF_TRUSTED_FOR_DELEGATION) {
+ flags.ok_as_delegate = 1;
+ }
+ if (!(userAccountControl & UF_NOT_DELEGATED)) {
+ flags.forwardable = 1;
+ flags.proxiable = 1;
+ }
+
+ if (userAccountControl & UF_DONT_REQUIRE_PREAUTH) {
+ flags.require_preauth = 0;
+ } else {
+ flags.require_preauth = 1;
+
+ }
+ return flags;
+}
+
+static int hdb_ldb_destructor(struct hdb_ldb_private *p)
+{
+ hdb_entry_ex *entry_ex = p->entry_ex;
+ free_hdb_entry(&entry_ex->entry);
+ return 0;
+}
+
+static void hdb_ldb_free_entry(krb5_context context, hdb_entry_ex *entry_ex)
+{
+ talloc_free(entry_ex->ctx);
+}
+
+static krb5_error_code LDB_message2entry_keys(krb5_context context,
+ struct smb_iconv_convenience *iconv_convenience,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message *msg,
+ unsigned int userAccountControl,
+ hdb_entry_ex *entry_ex)
+{
+ krb5_error_code ret = 0;
+ enum ndr_err_code ndr_err;
+ struct samr_Password *hash;
+ const struct ldb_val *sc_val;
+ struct supplementalCredentialsBlob scb;
+ struct supplementalCredentialsPackage *scpk = NULL;
+ bool newer_keys = false;
+ struct package_PrimaryKerberosBlob _pkb;
+ struct package_PrimaryKerberosCtr3 *pkb3 = NULL;
+ struct package_PrimaryKerberosCtr4 *pkb4 = NULL;
+ uint32_t i;
+ uint32_t allocated_keys = 0;
+
+ entry_ex->entry.keys.val = NULL;
+ entry_ex->entry.keys.len = 0;
+
+ entry_ex->entry.kvno = ldb_msg_find_attr_as_int(msg, "msDS-KeyVersionNumber", 0);
+
+ /* Get keys from the db */
+
+ hash = samdb_result_hash(mem_ctx, msg, "unicodePwd");
+ sc_val = ldb_msg_find_ldb_val(msg, "supplementalCredentials");
+
+ /* unicodePwd for enctype 0x17 (23) if present */
+ if (hash) {
+ allocated_keys++;
+ }
+
+ /* supplementalCredentials if present */
+ if (sc_val) {
+ ndr_err = ndr_pull_struct_blob_all(sc_val, mem_ctx, iconv_convenience, &scb,
+ (ndr_pull_flags_fn_t)ndr_pull_supplementalCredentialsBlob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ dump_data(0, sc_val->data, sc_val->length);
+ ret = EINVAL;
+ goto out;
+ }
+
+ if (scb.sub.signature != SUPPLEMENTAL_CREDENTIALS_SIGNATURE) {
+ NDR_PRINT_DEBUG(supplementalCredentialsBlob, &scb);
+ ret = EINVAL;
+ goto out;
+ }
+
+ for (i=0; i < scb.sub.num_packages; i++) {
+ if (strcmp("Primary:Kerberos-Newer-Keys", scb.sub.packages[i].name) == 0) {
+ scpk = &scb.sub.packages[i];
+ if (!scpk->data || !scpk->data[0]) {
+ scpk = NULL;
+ continue;
+ }
+ newer_keys = true;
+ break;
+ } else if (strcmp("Primary:Kerberos", scb.sub.packages[i].name) == 0) {
+ scpk = &scb.sub.packages[i];
+ if (!scpk->data || !scpk->data[0]) {
+ scpk = NULL;
+ }
+ /*
+ * we don't break here in hope to find
+ * a Kerberos-Newer-Keys package
+ */
+ }
+ }
+ }
+ /*
+ * Primary:Kerberos-Newer-Keys or Primary:Kerberos element
+ * of supplementalCredentials
+ */
+ if (scpk) {
+ DATA_BLOB blob;
+
+ blob = strhex_to_data_blob(mem_ctx, scpk->data);
+ if (!blob.data) {
+ ret = ENOMEM;
+ goto out;
+ }
+
+ /* we cannot use ndr_pull_struct_blob_all() here, as w2k and w2k3 add padding bytes */
+ ndr_err = ndr_pull_struct_blob(&blob, mem_ctx, iconv_convenience, &_pkb,
+ (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryKerberosBlob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ krb5_set_error_string(context, "LDB_message2entry_keys: could not parse package_PrimaryKerberosBlob");
+ krb5_warnx(context, "LDB_message2entry_keys: could not parse package_PrimaryKerberosBlob");
+ ret = EINVAL;
+ goto out;
+ }
+
+ if (newer_keys && _pkb.version != 4) {
+ krb5_set_error_string(context, "LDB_message2entry_keys: Primary:Kerberos-Newer-Keys not version 4");
+ krb5_warnx(context, "LDB_message2entry_keys: Primary:Kerberos-Newer-Keys not version 4");
+ ret = EINVAL;
+ goto out;
+ }
+
+ if (!newer_keys && _pkb.version != 3) {
+ krb5_set_error_string(context, "LDB_message2entry_keys: could not parse Primary:Kerberos not version 3");
+ krb5_warnx(context, "LDB_message2entry_keys: could not parse Primary:Kerberos not version 3");
+ ret = EINVAL;
+ goto out;
+ }
+
+ if (_pkb.version == 4) {
+ pkb4 = &_pkb.ctr.ctr4;
+ allocated_keys += pkb4->num_keys;
+ } else if (_pkb.version == 3) {
+ pkb3 = &_pkb.ctr.ctr3;
+ allocated_keys += pkb3->num_keys;
+ }
+ }
+
+ if (allocated_keys == 0) {
+ /* oh, no password. Apparently (comment in
+ * hdb-ldap.c) this violates the ASN.1, but this
+ * allows an entry with no keys (yet). */
+ return 0;
+ }
+
+ /* allocate space to decode into */
+ entry_ex->entry.keys.len = 0;
+ entry_ex->entry.keys.val = calloc(allocated_keys, sizeof(Key));
+ if (entry_ex->entry.keys.val == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+
+ if (hash && !(userAccountControl & UF_USE_DES_KEY_ONLY)) {
+ Key key;
+
+ key.mkvno = 0;
+ key.salt = NULL; /* No salt for this enc type */
+
+ ret = krb5_keyblock_init(context,
+ ENCTYPE_ARCFOUR_HMAC_MD5,
+ hash->hash, sizeof(hash->hash),
+ &key.key);
+ if (ret) {
+ goto out;
+ }
+
+ entry_ex->entry.keys.val[entry_ex->entry.keys.len] = key;
+ entry_ex->entry.keys.len++;
+ }
+
+ if (pkb4) {
+ for (i=0; i < pkb4->num_keys; i++) {
+ bool use = true;
+ Key key;
+
+ if (!pkb4->keys[i].value) continue;
+
+ if (userAccountControl & UF_USE_DES_KEY_ONLY) {
+ switch (pkb4->keys[i].keytype) {
+ case ENCTYPE_DES_CBC_CRC:
+ case ENCTYPE_DES_CBC_MD5:
+ break;
+ default:
+ use = false;
+ break;
+ }
+ }
+
+ if (!use) continue;
+
+ key.mkvno = 0;
+ key.salt = NULL;
+
+ if (pkb4->salt.string) {
+ DATA_BLOB salt;
+
+ salt = data_blob_string_const(pkb4->salt.string);
+
+ key.salt = calloc(1, sizeof(*key.salt));
+ if (key.salt == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+
+ key.salt->type = hdb_pw_salt;
+
+ ret = krb5_data_copy(&key.salt->salt, salt.data, salt.length);
+ if (ret) {
+ free(key.salt);
+ key.salt = NULL;
+ goto out;
+ }
+ }
+
+ /* TODO: maybe pass the iteration_count somehow... */
+
+ ret = krb5_keyblock_init(context,
+ pkb4->keys[i].keytype,
+ pkb4->keys[i].value->data,
+ pkb4->keys[i].value->length,
+ &key.key);
+ if (ret) {
+ if (key.salt) {
+ free_Salt(key.salt);
+ free(key.salt);
+ key.salt = NULL;
+ }
+ goto out;
+ }
+
+ entry_ex->entry.keys.val[entry_ex->entry.keys.len] = key;
+ entry_ex->entry.keys.len++;
+ }
+ } else if (pkb3) {
+ for (i=0; i < pkb3->num_keys; i++) {
+ bool use = true;
+ Key key;
+
+ if (!pkb3->keys[i].value) continue;
+
+ if (userAccountControl & UF_USE_DES_KEY_ONLY) {
+ switch (pkb3->keys[i].keytype) {
+ case ENCTYPE_DES_CBC_CRC:
+ case ENCTYPE_DES_CBC_MD5:
+ break;
+ default:
+ use = false;
+ break;
+ }
+ }
+
+ if (!use) continue;
+
+ key.mkvno = 0;
+ key.salt = NULL;
+
+ if (pkb3->salt.string) {
+ DATA_BLOB salt;
+
+ salt = data_blob_string_const(pkb3->salt.string);
+
+ key.salt = calloc(1, sizeof(*key.salt));
+ if (key.salt == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+
+ key.salt->type = hdb_pw_salt;
+
+ ret = krb5_data_copy(&key.salt->salt, salt.data, salt.length);
+ if (ret) {
+ free(key.salt);
+ key.salt = NULL;
+ goto out;
+ }
+ }
+
+ ret = krb5_keyblock_init(context,
+ pkb3->keys[i].keytype,
+ pkb3->keys[i].value->data,
+ pkb3->keys[i].value->length,
+ &key.key);
+ if (ret) {
+ if (key.salt) {
+ free_Salt(key.salt);
+ free(key.salt);
+ key.salt = NULL;
+ }
+ goto out;
+ }
+
+ entry_ex->entry.keys.val[entry_ex->entry.keys.len] = key;
+ entry_ex->entry.keys.len++;
+ }
+ }
+
+out:
+ if (ret != 0) {
+ entry_ex->entry.keys.len = 0;
+ }
+ if (entry_ex->entry.keys.len == 0 && entry_ex->entry.keys.val) {
+ free(entry_ex->entry.keys.val);
+ entry_ex->entry.keys.val = NULL;
+ }
+ return ret;
+}
+
+/*
+ * Construct an hdb_entry from a directory entry.
+ */
+static krb5_error_code LDB_message2entry(krb5_context context, HDB *db,
+ TALLOC_CTX *mem_ctx, krb5_const_principal principal,
+ enum hdb_ldb_ent_type ent_type,
+ struct ldb_message *msg,
+ struct ldb_message *realm_ref_msg,
+ hdb_entry_ex *entry_ex)
+{
+ unsigned int userAccountControl;
+ int i;
+ krb5_error_code ret = 0;
+ krb5_boolean is_computer = FALSE;
+ const char *dnsdomain = ldb_msg_find_attr_as_string(realm_ref_msg, "dnsRoot", NULL);
+ char *realm = strupper_talloc(mem_ctx, dnsdomain);
+ struct loadparm_context *lp_ctx = ldb_get_opaque((struct ldb_context *)db->hdb_db, "loadparm");
+ struct ldb_dn *domain_dn = samdb_result_dn((struct ldb_context *)db->hdb_db,
+ mem_ctx,
+ realm_ref_msg,
+ "nCName",
+ ldb_dn_new(mem_ctx, (struct ldb_context *)db->hdb_db, NULL));
+
+ struct hdb_ldb_private *p;
+ NTTIME acct_expiry;
+
+ struct ldb_message_element *objectclasses;
+ struct ldb_val computer_val;
+ computer_val.data = discard_const_p(uint8_t,"computer");
+ computer_val.length = strlen((const char *)computer_val.data);
+
+ objectclasses = ldb_msg_find_element(msg, "objectClass");
+
+ if (objectclasses && ldb_msg_find_val(objectclasses, &computer_val)) {
+ is_computer = TRUE;
+ }
+
+ memset(entry_ex, 0, sizeof(*entry_ex));
+
+ if (!realm) {
+ krb5_set_error_string(context, "talloc_strdup: out of memory");
+ ret = ENOMEM;
+ goto out;
+ }
+
+ p = talloc(mem_ctx, struct hdb_ldb_private);
+ if (!p) {
+ ret = ENOMEM;
+ goto out;
+ }
+
+ p->entry_ex = entry_ex;
+ p->iconv_convenience = lp_iconv_convenience(lp_ctx);
+ p->netbios_name = lp_netbios_name(lp_ctx);
+
+ talloc_set_destructor(p, hdb_ldb_destructor);
+
+ entry_ex->ctx = p;
+ entry_ex->free_entry = hdb_ldb_free_entry;
+
+ userAccountControl = ldb_msg_find_attr_as_uint(msg, "userAccountControl", 0);
+
+
+ entry_ex->entry.principal = malloc(sizeof(*(entry_ex->entry.principal)));
+ if (ent_type == HDB_SAMBA4_ENT_TYPE_ANY && principal == NULL) {
+ const char *samAccountName = ldb_msg_find_attr_as_string(msg, "samAccountName", NULL);
+ if (!samAccountName) {
+ krb5_set_error_string(context, "LDB_message2entry: no samAccountName present");
+ ret = ENOENT;
+ goto out;
+ }
+ samAccountName = ldb_msg_find_attr_as_string(msg, "samAccountName", NULL);
+ krb5_make_principal(context, &entry_ex->entry.principal, realm, samAccountName, NULL);
+ } else {
+ char *strdup_realm;
+ ret = copy_Principal(principal, entry_ex->entry.principal);
+ if (ret) {
+ krb5_clear_error_string(context);
+ goto out;
+ }
+
+ /* While we have copied the client principal, tests
+ * show that Win2k3 returns the 'corrected' realm, not
+ * the client-specified realm. This code attempts to
+ * replace the client principal's realm with the one
+ * we determine from our records */
+
+ /* this has to be with malloc() */
+ strdup_realm = strdup(realm);
+ if (!strdup_realm) {
+ ret = ENOMEM;
+ krb5_clear_error_string(context);
+ goto out;
+ }
+ free(*krb5_princ_realm(context, entry_ex->entry.principal));
+ krb5_princ_set_realm(context, entry_ex->entry.principal, &strdup_realm);
+ }
+
+ entry_ex->entry.flags = uf2HDBFlags(context, userAccountControl, ent_type);
+
+ if (ent_type == HDB_SAMBA4_ENT_TYPE_KRBTGT) {
+ entry_ex->entry.flags.invalid = 0;
+ entry_ex->entry.flags.server = 1;
+ entry_ex->entry.flags.forwardable = 1;
+ entry_ex->entry.flags.ok_as_delegate = 1;
+ }
+
+ if (lp_parm_bool(lp_ctx, NULL, "kdc", "require spn for service", true)) {
+ if (!is_computer && !ldb_msg_find_attr_as_string(msg, "servicePrincipalName", NULL)) {
+ entry_ex->entry.flags.server = 0;
+ }
+ }
+
+ /* use 'whenCreated' */
+ entry_ex->entry.created_by.time = ldb_msg_find_krb5time_ldap_time(msg, "whenCreated", 0);
+ /* use '???' */
+ entry_ex->entry.created_by.principal = NULL;
+
+ entry_ex->entry.modified_by = (Event *) malloc(sizeof(Event));
+ if (entry_ex->entry.modified_by == NULL) {
+ krb5_set_error_string(context, "malloc: out of memory");
+ ret = ENOMEM;
+ goto out;
+ }
+
+ /* use 'whenChanged' */
+ entry_ex->entry.modified_by->time = ldb_msg_find_krb5time_ldap_time(msg, "whenChanged", 0);
+ /* use '???' */
+ entry_ex->entry.modified_by->principal = NULL;
+
+ entry_ex->entry.valid_start = NULL;
+
+ acct_expiry = samdb_result_account_expires(msg);
+ if (acct_expiry == 0x7FFFFFFFFFFFFFFFULL) {
+ entry_ex->entry.valid_end = NULL;
+ } else {
+ entry_ex->entry.valid_end = malloc(sizeof(*entry_ex->entry.valid_end));
+ if (entry_ex->entry.valid_end == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+ *entry_ex->entry.valid_end = nt_time_to_unix(acct_expiry);
+ }
+
+ if (ent_type != HDB_SAMBA4_ENT_TYPE_KRBTGT) {
+ NTTIME must_change_time
+ = samdb_result_force_password_change((struct ldb_context *)db->hdb_db, mem_ctx,
+ domain_dn, msg);
+ if (must_change_time == 0x7FFFFFFFFFFFFFFFULL) {
+ entry_ex->entry.pw_end = NULL;
+ } else {
+ entry_ex->entry.pw_end = malloc(sizeof(*entry_ex->entry.pw_end));
+ if (entry_ex->entry.pw_end == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+ *entry_ex->entry.pw_end = nt_time_to_unix(must_change_time);
+ }
+ } else {
+ entry_ex->entry.pw_end = NULL;
+ }
+
+ entry_ex->entry.max_life = NULL;
+
+ entry_ex->entry.max_renew = NULL;
+
+ entry_ex->entry.generation = NULL;
+
+ /* Get keys from the db */
+ ret = LDB_message2entry_keys(context, p->iconv_convenience, p, msg, userAccountControl, entry_ex);
+ if (ret) {
+ /* Could be bougus data in the entry, or out of memory */
+ goto out;
+ }
+
+ entry_ex->entry.etypes = malloc(sizeof(*(entry_ex->entry.etypes)));
+ if (entry_ex->entry.etypes == NULL) {
+ krb5_clear_error_string(context);
+ ret = ENOMEM;
+ goto out;
+ }
+ entry_ex->entry.etypes->len = entry_ex->entry.keys.len;
+ entry_ex->entry.etypes->val = calloc(entry_ex->entry.etypes->len, sizeof(int));
+ if (entry_ex->entry.etypes->val == NULL) {
+ krb5_clear_error_string(context);
+ ret = ENOMEM;
+ goto out;
+ }
+ for (i=0; i < entry_ex->entry.etypes->len; i++) {
+ entry_ex->entry.etypes->val[i] = entry_ex->entry.keys.val[i].key.keytype;
+ }
+
+
+ p->msg = talloc_steal(p, msg);
+ p->realm_ref_msg = talloc_steal(p, realm_ref_msg);
+ p->samdb = (struct ldb_context *)db->hdb_db;
+
+out:
+ if (ret != 0) {
+ /* This doesn't free ent itself, that is for the eventual caller to do */
+ hdb_free_entry(context, entry_ex);
+ } else {
+ talloc_steal(db, entry_ex->ctx);
+ }
+
+ return ret;
+}
+
+/*
+ * Construct an hdb_entry from a directory entry.
+ */
+static krb5_error_code LDB_trust_message2entry(krb5_context context, HDB *db,
+ struct loadparm_context *lp_ctx,
+ TALLOC_CTX *mem_ctx, krb5_const_principal principal,
+ enum trust_direction direction,
+ struct ldb_message *msg,
+ hdb_entry_ex *entry_ex)
+{
+
+ const char *dnsdomain;
+ char *realm;
+ char *strdup_realm;
+ DATA_BLOB password_utf16;
+ struct samr_Password password_hash;
+ const struct ldb_val *password_val;
+ struct trustAuthInOutBlob password_blob;
+ struct hdb_ldb_private *p;
+
+ enum ndr_err_code ndr_err;
+ int i, ret, trust_direction_flags;
+
+ p = talloc(mem_ctx, struct hdb_ldb_private);
+ if (!p) {
+ ret = ENOMEM;
+ goto out;
+ }
+
+ p->entry_ex = entry_ex;
+ p->iconv_convenience = lp_iconv_convenience(lp_ctx);
+ p->netbios_name = lp_netbios_name(lp_ctx);
+
+ talloc_set_destructor(p, hdb_ldb_destructor);
+
+ entry_ex->ctx = p;
+ entry_ex->free_entry = hdb_ldb_free_entry;
+
+ /* use 'whenCreated' */
+ entry_ex->entry.created_by.time = ldb_msg_find_krb5time_ldap_time(msg, "whenCreated", 0);
+ /* use '???' */
+ entry_ex->entry.created_by.principal = NULL;
+
+ entry_ex->entry.valid_start = NULL;
+
+ trust_direction_flags = ldb_msg_find_attr_as_int(msg, "trustDirection", 0);
+
+ if (direction == INBOUND) {
+ realm = strupper_talloc(mem_ctx, lp_realm(lp_ctx));
+ password_val = ldb_msg_find_ldb_val(msg, "trustAuthIncoming");
+
+ } else { /* OUTBOUND */
+ dnsdomain = ldb_msg_find_attr_as_string(msg, "trustPartner", NULL);
+ realm = strupper_talloc(mem_ctx, dnsdomain);
+ password_val = ldb_msg_find_ldb_val(msg, "trustAuthOutgoing");
+ }
+
+ if (!password_val || !(trust_direction_flags & direction)) {
+ ret = ENOENT;
+ goto out;
+ }
+
+ ndr_err = ndr_pull_struct_blob(password_val, mem_ctx, p->iconv_convenience, &password_blob,
+ (ndr_pull_flags_fn_t)ndr_pull_trustAuthInOutBlob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ ret = EINVAL;
+ goto out;
+ }
+
+ entry_ex->entry.kvno = -1;
+ for (i=0; i < password_blob.count; i++) {
+ if (password_blob.current->array[i].AuthType == TRUST_AUTH_TYPE_VERSION) {
+ entry_ex->entry.kvno = password_blob.current->array[i].AuthInfo.version.version;
+ }
+ }
+
+ for (i=0; i < password_blob.count; i++) {
+ if (password_blob.current->array[i].AuthType == TRUST_AUTH_TYPE_CLEAR) {
+ password_utf16 = data_blob_const(password_blob.current->array[i].AuthInfo.clear.password,
+ password_blob.current->array[i].AuthInfo.clear.size);
+ /* In the future, generate all sorts of
+ * hashes, but for now we can't safely convert
+ * the random strings windows uses into
+ * utf8 */
+
+ /* but as it is utf16 already, we can get the NT password/arcfour-hmac-md5 key */
+ mdfour(password_hash.hash, password_utf16.data, password_utf16.length);
+ break;
+ } else if (password_blob.current->array[i].AuthType == TRUST_AUTH_TYPE_NT4OWF) {
+ password_hash = password_blob.current->array[i].AuthInfo.nt4owf.password;
+ break;
+ }
+ }
+ entry_ex->entry.keys.len = 0;
+ entry_ex->entry.keys.val = NULL;
+
+ if (i < password_blob.count) {
+ Key key;
+ /* Must have found a cleartext or MD4 password */
+ entry_ex->entry.keys.val = calloc(1, sizeof(Key));
+
+ key.mkvno = 0;
+ key.salt = NULL; /* No salt for this enc type */
+
+ if (entry_ex->entry.keys.val == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+
+ ret = krb5_keyblock_init(context,
+ ENCTYPE_ARCFOUR_HMAC_MD5,
+ password_hash.hash, sizeof(password_hash.hash),
+ &key.key);
+
+ entry_ex->entry.keys.val[entry_ex->entry.keys.len] = key;
+ entry_ex->entry.keys.len++;
+ }
+
+ entry_ex->entry.principal = malloc(sizeof(*(entry_ex->entry.principal)));
+
+ ret = copy_Principal(principal, entry_ex->entry.principal);
+ if (ret) {
+ krb5_clear_error_string(context);
+ goto out;
+ }
+
+ /* While we have copied the client principal, tests
+ * show that Win2k3 returns the 'corrected' realm, not
+ * the client-specified realm. This code attempts to
+ * replace the client principal's realm with the one
+ * we determine from our records */
+
+ /* this has to be with malloc() */
+ strdup_realm = strdup(realm);
+ if (!strdup_realm) {
+ ret = ENOMEM;
+ krb5_clear_error_string(context);
+ goto out;
+ }
+ free(*krb5_princ_realm(context, entry_ex->entry.principal));
+ krb5_princ_set_realm(context, entry_ex->entry.principal, &strdup_realm);
+
+ entry_ex->entry.flags = int2HDBFlags(0);
+ entry_ex->entry.flags.immutable = 1;
+ entry_ex->entry.flags.invalid = 0;
+ entry_ex->entry.flags.server = 1;
+ entry_ex->entry.flags.require_preauth = 1;
+
+ entry_ex->entry.pw_end = NULL;
+
+ entry_ex->entry.max_life = NULL;
+
+ entry_ex->entry.max_renew = NULL;
+
+ entry_ex->entry.generation = NULL;
+
+ entry_ex->entry.etypes = malloc(sizeof(*(entry_ex->entry.etypes)));
+ if (entry_ex->entry.etypes == NULL) {
+ krb5_clear_error_string(context);
+ ret = ENOMEM;
+ goto out;
+ }
+ entry_ex->entry.etypes->len = entry_ex->entry.keys.len;
+ entry_ex->entry.etypes->val = calloc(entry_ex->entry.etypes->len, sizeof(int));
+ if (entry_ex->entry.etypes->val == NULL) {
+ krb5_clear_error_string(context);
+ ret = ENOMEM;
+ goto out;
+ }
+ for (i=0; i < entry_ex->entry.etypes->len; i++) {
+ entry_ex->entry.etypes->val[i] = entry_ex->entry.keys.val[i].key.keytype;
+ }
+
+
+ p->msg = talloc_steal(p, msg);
+ p->realm_ref_msg = NULL;
+ p->samdb = (struct ldb_context *)db->hdb_db;
+
+out:
+ if (ret != 0) {
+ /* This doesn't free ent itself, that is for the eventual caller to do */
+ hdb_free_entry(context, entry_ex);
+ } else {
+ talloc_steal(db, entry_ex->ctx);
+ }
+
+ return ret;
+
+}
+
+static krb5_error_code LDB_lookup_principal(krb5_context context, struct ldb_context *ldb_ctx,
+ TALLOC_CTX *mem_ctx,
+ krb5_const_principal principal,
+ enum hdb_ldb_ent_type ent_type,
+ struct ldb_dn *realm_dn,
+ struct ldb_message ***pmsg)
+{
+ krb5_error_code ret;
+ int lret;
+ char *filter = NULL;
+ const char * const *princ_attrs = user_attrs;
+
+ char *short_princ;
+ char *short_princ_talloc;
+
+ struct ldb_result *res = NULL;
+
+ ret = krb5_unparse_name_flags(context, principal, KRB5_PRINCIPAL_UNPARSE_NO_REALM, &short_princ);
+
+ if (ret != 0) {
+ krb5_set_error_string(context, "LDB_lookup_principal: could not parse principal");
+ krb5_warnx(context, "LDB_lookup_principal: could not parse principal");
+ return ret;
+ }
+
+ short_princ_talloc = talloc_strdup(mem_ctx, short_princ);
+ free(short_princ);
+ if (!short_princ_talloc) {
+ krb5_set_error_string(context, "LDB_lookup_principal: talloc_strdup() failed!");
+ return ENOMEM;
+ }
+
+ switch (ent_type) {
+ case HDB_SAMBA4_ENT_TYPE_CLIENT:
+ case HDB_SAMBA4_ENT_TYPE_TRUST:
+ case HDB_SAMBA4_ENT_TYPE_ANY:
+ /* Can't happen */
+ return EINVAL;
+ case HDB_SAMBA4_ENT_TYPE_KRBTGT:
+ filter = talloc_asprintf(mem_ctx, "(&(objectClass=user)(samAccountName=%s))",
+ KRB5_TGS_NAME);
+ break;
+ case HDB_SAMBA4_ENT_TYPE_SERVER:
+ filter = talloc_asprintf(mem_ctx, "(&(objectClass=user)(samAccountName=%s))",
+ short_princ_talloc);
+ break;
+ }
+
+ if (!filter) {
+ krb5_set_error_string(context, "talloc_asprintf: out of memory");
+ return ENOMEM;
+ }
+
+ lret = ldb_search(ldb_ctx, mem_ctx, &res, realm_dn,
+ LDB_SCOPE_SUBTREE, princ_attrs, "%s", filter);
+ if (lret != LDB_SUCCESS) {
+ DEBUG(3, ("Failed to search for %s: %s\n", filter, ldb_errstring(ldb_ctx)));
+ return HDB_ERR_NOENTRY;
+ } else if (res->count == 0 || res->count > 1) {
+ DEBUG(3, ("Failed find a single entry for %s: got %d\n", filter, res->count));
+ talloc_free(res);
+ return HDB_ERR_NOENTRY;
+ }
+ talloc_steal(mem_ctx, res->msgs);
+ *pmsg = res->msgs;
+ talloc_free(res);
+ return 0;
+}
+
+static krb5_error_code LDB_lookup_trust(krb5_context context, struct ldb_context *ldb_ctx,
+ TALLOC_CTX *mem_ctx,
+ const char *realm,
+ struct ldb_dn *realm_dn,
+ struct ldb_message ***pmsg)
+{
+ int lret;
+ char *filter = NULL;
+ const char * const *attrs = trust_attrs;
+
+ struct ldb_result *res = NULL;
+ filter = talloc_asprintf(mem_ctx, "(&(objectClass=trustedDomain)(|(flatname=%s)(trustPartner=%s)))", realm, realm);
+
+ if (!filter) {
+ krb5_set_error_string(context, "talloc_asprintf: out of memory");
+ return ENOMEM;
+ }
+
+ lret = ldb_search(ldb_ctx, mem_ctx, &res,
+ ldb_get_default_basedn(ldb_ctx),
+ LDB_SCOPE_SUBTREE, attrs, "%s", filter);
+ if (lret != LDB_SUCCESS) {
+ DEBUG(3, ("Failed to search for %s: %s\n", filter, ldb_errstring(ldb_ctx)));
+ return HDB_ERR_NOENTRY;
+ } else if (res->count == 0 || res->count > 1) {
+ DEBUG(3, ("Failed find a single entry for %s: got %d\n", filter, res->count));
+ talloc_free(res);
+ return HDB_ERR_NOENTRY;
+ }
+ talloc_steal(mem_ctx, res->msgs);
+ *pmsg = res->msgs;
+ talloc_free(res);
+ return 0;
+}
+
+static krb5_error_code LDB_lookup_realm(krb5_context context, struct ldb_context *ldb_ctx,
+ TALLOC_CTX *mem_ctx,
+ const char *realm,
+ struct ldb_message ***pmsg)
+{
+ int ret;
+ struct ldb_result *cross_ref_res;
+ struct ldb_dn *partitions_basedn = samdb_partitions_dn(ldb_ctx, mem_ctx);
+
+ ret = ldb_search(ldb_ctx, mem_ctx, &cross_ref_res,
+ partitions_basedn, LDB_SCOPE_SUBTREE, realm_ref_attrs,
+ "(&(&(|(&(dnsRoot=%s)(nETBIOSName=*))(nETBIOSName=%s))(objectclass=crossRef))(ncName=*))",
+ realm, realm);
+
+ if (ret != LDB_SUCCESS) {
+ DEBUG(3, ("Failed to search to lookup realm(%s): %s\n", realm, ldb_errstring(ldb_ctx)));
+ talloc_free(cross_ref_res);
+ return HDB_ERR_NOENTRY;
+ } else if (cross_ref_res->count == 0 || cross_ref_res->count > 1) {
+ DEBUG(3, ("Failed find a single entry for realm %s: got %d\n", realm, cross_ref_res->count));
+ talloc_free(cross_ref_res);
+ return HDB_ERR_NOENTRY;
+ }
+
+ if (pmsg) {
+ *pmsg = cross_ref_res->msgs;
+ talloc_steal(mem_ctx, cross_ref_res->msgs);
+ }
+ talloc_free(cross_ref_res);
+
+ return 0;
+}
+
+
+static krb5_error_code LDB_open(krb5_context context, HDB *db, int flags, mode_t mode)
+{
+ if (db->hdb_master_key_set) {
+ krb5_warnx(context, "LDB_open: use of a master key incompatible with LDB\n");
+ krb5_set_error_string(context, "LDB_open: use of a master key incompatible with LDB\n");
+ return HDB_ERR_NOENTRY;
+ }
+
+ return 0;
+}
+
+static krb5_error_code LDB_close(krb5_context context, HDB *db)
+{
+ return 0;
+}
+
+static krb5_error_code LDB_lock(krb5_context context, HDB *db, int operation)
+{
+ return 0;
+}
+
+static krb5_error_code LDB_unlock(krb5_context context, HDB *db)
+{
+ return 0;
+}
+
+static krb5_error_code LDB_rename(krb5_context context, HDB *db, const char *new_name)
+{
+ return HDB_ERR_DB_INUSE;
+}
+
+static krb5_error_code LDB_fetch_client(krb5_context context, HDB *db,
+ TALLOC_CTX *mem_ctx,
+ krb5_const_principal principal,
+ unsigned flags,
+ hdb_entry_ex *entry_ex) {
+ NTSTATUS nt_status;
+ char *principal_string;
+ krb5_error_code ret;
+ struct ldb_message **msg = NULL;
+ struct ldb_message **realm_ref_msg = NULL;
+
+ ret = krb5_unparse_name(context, principal, &principal_string);
+
+ if (ret != 0) {
+ return ret;
+ }
+
+ nt_status = sam_get_results_principal((struct ldb_context *)db->hdb_db,
+ mem_ctx, principal_string,
+ &msg, &realm_ref_msg);
+ free(principal_string);
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_SUCH_USER)) {
+ return HDB_ERR_NOENTRY;
+ } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_MEMORY)) {
+ return ENOMEM;
+ } else if (!NT_STATUS_IS_OK(nt_status)) {
+ return EINVAL;
+ }
+
+ ret = LDB_message2entry(context, db, mem_ctx,
+ principal, HDB_SAMBA4_ENT_TYPE_CLIENT,
+ msg[0], realm_ref_msg[0], entry_ex);
+ return ret;
+}
+
+static krb5_error_code LDB_fetch_krbtgt(krb5_context context, HDB *db,
+ TALLOC_CTX *mem_ctx,
+ krb5_const_principal principal,
+ unsigned flags,
+ hdb_entry_ex *entry_ex)
+{
+ krb5_error_code ret;
+ struct ldb_message **msg = NULL;
+ struct ldb_message **realm_ref_msg_1 = NULL;
+ struct ldb_message **realm_ref_msg_2 = NULL;
+ struct ldb_dn *realm_dn;
+ const char *realm;
+
+ krb5_principal alloc_principal = NULL;
+ if (principal->name.name_string.len != 2
+ || (strcmp(principal->name.name_string.val[0], KRB5_TGS_NAME) != 0)) {
+ /* Not a krbtgt */
+ return HDB_ERR_NOENTRY;
+ }
+
+ /* krbtgt case. Either us or a trusted realm */
+
+ if ((LDB_lookup_realm(context, (struct ldb_context *)db->hdb_db,
+ mem_ctx, principal->realm, &realm_ref_msg_1) == 0)
+ && (LDB_lookup_realm(context, (struct ldb_context *)db->hdb_db,
+ mem_ctx, principal->name.name_string.val[1], &realm_ref_msg_2) == 0)
+ && (ldb_dn_compare(realm_ref_msg_1[0]->dn, realm_ref_msg_1[0]->dn) == 0)) {
+ /* us */
+ /* Cludge, cludge cludge. If the realm part of krbtgt/realm,
+ * is in our db, then direct the caller at our primary
+ * krbtgt */
+
+ const char *dnsdomain = ldb_msg_find_attr_as_string(realm_ref_msg_1[0], "dnsRoot", NULL);
+ char *realm_fixed = strupper_talloc(mem_ctx, dnsdomain);
+ if (!realm_fixed) {
+ krb5_set_error_string(context, "strupper_talloc: out of memory");
+ return ENOMEM;
+ }
+
+ ret = krb5_copy_principal(context, principal, &alloc_principal);
+ if (ret) {
+ return ret;
+ }
+
+ free(alloc_principal->name.name_string.val[1]);
+ alloc_principal->name.name_string.val[1] = strdup(realm_fixed);
+ talloc_free(realm_fixed);
+ if (!alloc_principal->name.name_string.val[1]) {
+ krb5_set_error_string(context, "LDB_fetch: strdup() failed!");
+ return ENOMEM;
+ }
+ principal = alloc_principal;
+ realm_dn = samdb_result_dn((struct ldb_context *)db->hdb_db, mem_ctx, realm_ref_msg_1[0], "nCName", NULL);
+
+ ret = LDB_lookup_principal(context, (struct ldb_context *)db->hdb_db,
+ mem_ctx,
+ principal, HDB_SAMBA4_ENT_TYPE_KRBTGT, realm_dn, &msg);
+
+ if (ret != 0) {
+ krb5_warnx(context, "LDB_fetch: could not find principal in DB");
+ krb5_set_error_string(context, "LDB_fetch: could not find principal in DB");
+ return ret;
+ }
+
+ ret = LDB_message2entry(context, db, mem_ctx,
+ principal, HDB_SAMBA4_ENT_TYPE_KRBTGT,
+ msg[0], realm_ref_msg_1[0], entry_ex);
+ if (ret != 0) {
+ krb5_warnx(context, "LDB_fetch: self krbtgt message2entry failed");
+ }
+ return ret;
+
+ } else {
+ enum trust_direction direction = UNKNOWN;
+
+ struct loadparm_context *lp_ctx = talloc_get_type(ldb_get_opaque(db->hdb_db, "loadparm"), struct loadparm_context);
+ /* Either an inbound or outbound trust */
+
+ if (strcasecmp(lp_realm(lp_ctx), principal->realm) == 0) {
+ /* look for inbound trust */
+ direction = INBOUND;
+ realm = principal->name.name_string.val[1];
+ }
+
+ if (strcasecmp(lp_realm(lp_ctx), principal->name.name_string.val[1]) == 0) {
+ /* look for outbound trust */
+ direction = OUTBOUND;
+ realm = principal->realm;
+ }
+
+ /* Trusted domains are under CN=system */
+
+ ret = LDB_lookup_trust(context, (struct ldb_context *)db->hdb_db,
+ mem_ctx,
+ realm, realm_dn, &msg);
+
+ if (ret != 0) {
+ krb5_warnx(context, "LDB_fetch: could not find principal in DB");
+ krb5_set_error_string(context, "LDB_fetch: could not find principal in DB");
+ return ret;
+ }
+
+ ret = LDB_trust_message2entry(context, db, lp_ctx, mem_ctx,
+ principal, direction,
+ msg[0], entry_ex);
+ if (ret != 0) {
+ krb5_warnx(context, "LDB_fetch: trust_message2entry failed");
+ }
+ return ret;
+
+
+ /* we should lookup trusted domains */
+ return HDB_ERR_NOENTRY;
+ }
+
+}
+
+static krb5_error_code LDB_fetch_server(krb5_context context, HDB *db,
+ TALLOC_CTX *mem_ctx,
+ krb5_const_principal principal,
+ unsigned flags,
+ hdb_entry_ex *entry_ex)
+{
+ krb5_error_code ret;
+ const char *realm;
+ struct ldb_message **msg = NULL;
+ struct ldb_message **realm_ref_msg = NULL;
+ struct ldb_dn *partitions_basedn = samdb_partitions_dn(db->hdb_db, mem_ctx);
+ if (principal->name.name_string.len >= 2) {
+ /* 'normal server' case */
+ int ldb_ret;
+ NTSTATUS nt_status;
+ struct ldb_dn *user_dn, *domain_dn;
+ char *principal_string;
+
+ ret = krb5_unparse_name_flags(context, principal,
+ KRB5_PRINCIPAL_UNPARSE_NO_REALM,
+ &principal_string);
+ if (ret != 0) {
+ return ret;
+ }
+
+ /* At this point we may find the host is known to be
+ * in a different realm, so we should generate a
+ * referral instead */
+ nt_status = crack_service_principal_name((struct ldb_context *)db->hdb_db,
+ mem_ctx, principal_string,
+ &user_dn, &domain_dn);
+ free(principal_string);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return HDB_ERR_NOENTRY;
+ }
+
+ ldb_ret = gendb_search_dn((struct ldb_context *)db->hdb_db,
+ mem_ctx, user_dn, &msg, user_attrs);
+
+ if (ldb_ret != 1) {
+ return HDB_ERR_NOENTRY;
+ }
+
+ ldb_ret = gendb_search((struct ldb_context *)db->hdb_db,
+ mem_ctx, partitions_basedn, &realm_ref_msg, realm_ref_attrs,
+ "ncName=%s", ldb_dn_get_linearized(domain_dn));
+
+ if (ldb_ret != 1) {
+ return HDB_ERR_NOENTRY;
+ }
+
+ } else {
+ struct ldb_dn *realm_dn;
+ /* server as client principal case, but we must not lookup userPrincipalNames */
+
+ realm = krb5_principal_get_realm(context, principal);
+
+ ret = LDB_lookup_realm(context, (struct ldb_context *)db->hdb_db,
+ mem_ctx, realm, &realm_ref_msg);
+ if (ret != 0) {
+ return HDB_ERR_NOENTRY;
+ }
+
+ realm_dn = samdb_result_dn((struct ldb_context *)db->hdb_db, mem_ctx, realm_ref_msg[0], "nCName", NULL);
+
+ ret = LDB_lookup_principal(context, (struct ldb_context *)db->hdb_db,
+ mem_ctx,
+ principal, HDB_SAMBA4_ENT_TYPE_SERVER, realm_dn, &msg);
+
+ if (ret != 0) {
+ return ret;
+ }
+ }
+
+ ret = LDB_message2entry(context, db, mem_ctx,
+ principal, HDB_SAMBA4_ENT_TYPE_SERVER,
+ msg[0], realm_ref_msg[0], entry_ex);
+ if (ret != 0) {
+ krb5_warnx(context, "LDB_fetch: message2entry failed");
+ }
+
+ return ret;
+}
+
+static krb5_error_code LDB_fetch(krb5_context context, HDB *db,
+ krb5_const_principal principal,
+ unsigned flags,
+ hdb_entry_ex *entry_ex)
+{
+ krb5_error_code ret = HDB_ERR_NOENTRY;
+
+ TALLOC_CTX *mem_ctx = talloc_named(db, 0, "LDB_fetch context");
+
+ if (!mem_ctx) {
+ krb5_set_error_string(context, "LDB_fetch: talloc_named() failed!");
+ return ENOMEM;
+ }
+
+ if (flags & HDB_F_GET_CLIENT) {
+ ret = LDB_fetch_client(context, db, mem_ctx, principal, flags, entry_ex);
+ if (ret != HDB_ERR_NOENTRY) goto done;
+ }
+ if (flags & HDB_F_GET_SERVER) {
+ /* krbtgt fits into this situation for trusted realms, and for resolving different versions of our own realm name */
+ ret = LDB_fetch_krbtgt(context, db, mem_ctx, principal, flags, entry_ex);
+ if (ret != HDB_ERR_NOENTRY) goto done;
+
+ /* We return 'no entry' if it does not start with krbtgt/, so move to the common case quickly */
+ ret = LDB_fetch_server(context, db, mem_ctx, principal, flags, entry_ex);
+ if (ret != HDB_ERR_NOENTRY) goto done;
+ }
+ if (flags & HDB_F_GET_KRBTGT) {
+ ret = LDB_fetch_krbtgt(context, db, mem_ctx, principal, flags, entry_ex);
+ if (ret != HDB_ERR_NOENTRY) goto done;
+ }
+
+done:
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+static krb5_error_code LDB_store(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry)
+{
+ return HDB_ERR_DB_INUSE;
+}
+
+static krb5_error_code LDB_remove(krb5_context context, HDB *db, krb5_const_principal principal)
+{
+ return HDB_ERR_DB_INUSE;
+}
+
+struct hdb_ldb_seq {
+ struct ldb_context *ctx;
+ int index;
+ int count;
+ struct ldb_message **msgs;
+ struct ldb_message **realm_ref_msgs;
+};
+
+static krb5_error_code LDB_seq(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry)
+{
+ krb5_error_code ret;
+ struct hdb_ldb_seq *priv = (struct hdb_ldb_seq *)db->hdb_dbc;
+ TALLOC_CTX *mem_ctx;
+ hdb_entry_ex entry_ex;
+ memset(&entry_ex, '\0', sizeof(entry_ex));
+
+ if (!priv) {
+ return HDB_ERR_NOENTRY;
+ }
+
+ mem_ctx = talloc_named(priv, 0, "LDB_seq context");
+
+ if (!mem_ctx) {
+ krb5_set_error_string(context, "LDB_seq: talloc_named() failed!");
+ return ENOMEM;
+ }
+
+ if (priv->index < priv->count) {
+ ret = LDB_message2entry(context, db, mem_ctx,
+ NULL, HDB_SAMBA4_ENT_TYPE_ANY,
+ priv->msgs[priv->index++],
+ priv->realm_ref_msgs[0], entry);
+ } else {
+ ret = HDB_ERR_NOENTRY;
+ }
+
+ if (ret != 0) {
+ talloc_free(priv);
+ db->hdb_dbc = NULL;
+ } else {
+ talloc_free(mem_ctx);
+ }
+
+ return ret;
+}
+
+static krb5_error_code LDB_firstkey(krb5_context context, HDB *db, unsigned flags,
+ hdb_entry_ex *entry)
+{
+ struct ldb_context *ldb_ctx = (struct ldb_context *)db->hdb_db;
+ struct hdb_ldb_seq *priv = (struct hdb_ldb_seq *)db->hdb_dbc;
+ char *realm;
+ struct ldb_dn *realm_dn = NULL;
+ struct ldb_result *res = NULL;
+ struct ldb_message **realm_ref_msgs = NULL;
+ krb5_error_code ret;
+ TALLOC_CTX *mem_ctx;
+ int lret;
+
+ if (priv) {
+ talloc_free(priv);
+ db->hdb_dbc = NULL;
+ }
+
+ priv = (struct hdb_ldb_seq *) talloc(db, struct hdb_ldb_seq);
+ if (!priv) {
+ krb5_set_error_string(context, "talloc: out of memory");
+ return ENOMEM;
+ }
+
+ priv->ctx = ldb_ctx;
+ priv->index = 0;
+ priv->msgs = NULL;
+ priv->realm_ref_msgs = NULL;
+ priv->count = 0;
+
+ mem_ctx = talloc_named(priv, 0, "LDB_firstkey context");
+
+ if (!mem_ctx) {
+ krb5_set_error_string(context, "LDB_firstkey: talloc_named() failed!");
+ return ENOMEM;
+ }
+
+ ret = krb5_get_default_realm(context, &realm);
+ if (ret != 0) {
+ talloc_free(priv);
+ return ret;
+ }
+
+ ret = LDB_lookup_realm(context, (struct ldb_context *)db->hdb_db,
+ mem_ctx, realm, &realm_ref_msgs);
+
+ free(realm);
+
+ if (ret != 0) {
+ talloc_free(priv);
+ krb5_warnx(context, "LDB_firstkey: could not find realm\n");
+ return HDB_ERR_NOENTRY;
+ }
+
+ realm_dn = samdb_result_dn((struct ldb_context *)db->hdb_db, mem_ctx, realm_ref_msgs[0], "nCName", NULL);
+
+ priv->realm_ref_msgs = talloc_steal(priv, realm_ref_msgs);
+
+ lret = ldb_search(ldb_ctx, priv, &res,
+ realm_dn, LDB_SCOPE_SUBTREE, user_attrs,
+ "(objectClass=user)");
+
+ if (lret != LDB_SUCCESS) {
+ talloc_free(priv);
+ return HDB_ERR_NOENTRY;
+ }
+
+ priv->count = res->count;
+ priv->msgs = talloc_steal(priv, res->msgs);
+ talloc_free(res);
+
+ db->hdb_dbc = priv;
+
+ ret = LDB_seq(context, db, flags, entry);
+
+ if (ret != 0) {
+ talloc_free(priv);
+ db->hdb_dbc = NULL;
+ } else {
+ talloc_free(mem_ctx);
+ }
+ return ret;
+}
+
+static krb5_error_code LDB_nextkey(krb5_context context, HDB *db, unsigned flags,
+ hdb_entry_ex *entry)
+{
+ return LDB_seq(context, db, flags, entry);
+}
+
+static krb5_error_code LDB_destroy(krb5_context context, HDB *db)
+{
+ talloc_free(db);
+ return 0;
+}
+
+/* This interface is to be called by the KDC, which is expecting Samba
+ * calling conventions. It is also called by a wrapper
+ * (hdb_ldb_create) from the kpasswdd -> krb5 -> keytab_hdb -> hdb
+ * code */
+
+NTSTATUS kdc_hdb_samba4_create(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx,
+ krb5_context context, struct HDB **db, const char *arg)
+{
+ NTSTATUS nt_status;
+ struct auth_session_info *session_info;
+ *db = talloc(mem_ctx, HDB);
+ if (!*db) {
+ krb5_set_error_string(context, "malloc: out of memory");
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*db)->hdb_master_key_set = 0;
+ (*db)->hdb_db = NULL;
+
+ nt_status = auth_system_session_info(*db, lp_ctx, &session_info);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ /* The idea here is very simple. Using Kerberos to
+ * authenticate the KDC to the LDAP server is higly likely to
+ * be circular.
+ *
+ * In future we may set this up to use EXERNAL and SSL
+ * certificates, for now it will almost certainly be NTLMSSP
+ */
+
+ cli_credentials_set_kerberos_state(session_info->credentials,
+ CRED_DONT_USE_KERBEROS);
+
+ /* Setup the link to LDB */
+ (*db)->hdb_db = samdb_connect(*db, ev_ctx, lp_ctx, session_info);
+ if ((*db)->hdb_db == NULL) {
+ DEBUG(1, ("hdb_ldb_create: Cannot open samdb for KDC backend!"));
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ }
+
+ (*db)->hdb_dbc = NULL;
+ (*db)->hdb_open = LDB_open;
+ (*db)->hdb_close = LDB_close;
+ (*db)->hdb_fetch = LDB_fetch;
+ (*db)->hdb_store = LDB_store;
+ (*db)->hdb_remove = LDB_remove;
+ (*db)->hdb_firstkey = LDB_firstkey;
+ (*db)->hdb_nextkey = LDB_nextkey;
+ (*db)->hdb_lock = LDB_lock;
+ (*db)->hdb_unlock = LDB_unlock;
+ (*db)->hdb_rename = LDB_rename;
+ /* we don't implement these, as we are not a lockable database */
+ (*db)->hdb__get = NULL;
+ (*db)->hdb__put = NULL;
+ /* kadmin should not be used for deletes - use other tools instead */
+ (*db)->hdb__del = NULL;
+ (*db)->hdb_destroy = LDB_destroy;
+
+ return NT_STATUS_OK;
+}
+
+krb5_error_code hdb_samba4_create(krb5_context context, struct HDB **db, const char *arg)
+{
+ NTSTATUS nt_status;
+ /* The global kdc_mem_ctx and kdc_lp_ctx, Disgusting, ugly hack, but it means one less private hook */
+ nt_status = kdc_hdb_samba4_create(kdc_mem_ctx, kdc_ev_ctx, kdc_lp_ctx,
+ context, db, arg);
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ return 0;
+ }
+ return EINVAL;
+}
diff --git a/source4/kdc/kdc.c b/source4/kdc/kdc.c
new file mode 100644
index 0000000000..1cfe9852f0
--- /dev/null
+++ b/source4/kdc/kdc.c
@@ -0,0 +1,795 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ KDC Server startup
+
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2008
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "smbd/service_task.h"
+#include "smbd/service.h"
+#include "smbd/service_stream.h"
+#include "smbd/process_model.h"
+#include "lib/events/events.h"
+#include "lib/socket/socket.h"
+#include "system/network.h"
+#include "../lib/util/dlinklist.h"
+#include "lib/messaging/irpc.h"
+#include "lib/stream/packet.h"
+#include "librpc/gen_ndr/samr.h"
+#include "librpc/gen_ndr/ndr_irpc.h"
+#include "librpc/gen_ndr/ndr_krb5pac.h"
+#include "lib/socket/netif.h"
+#include "param/param.h"
+#include "kdc/kdc.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+
+
+/* Disgusting hack to get a mem_ctx and lp_ctx into the hdb plugin, when
+ * used as a keytab */
+TALLOC_CTX *kdc_mem_ctx;
+struct tevent_context *kdc_ev_ctx;
+struct loadparm_context *kdc_lp_ctx;
+
+/* hold all the info needed to send a reply */
+struct kdc_reply {
+ struct kdc_reply *next, *prev;
+ struct socket_address *dest;
+ DATA_BLOB packet;
+};
+
+typedef bool (*kdc_process_fn_t)(struct kdc_server *kdc,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *input,
+ DATA_BLOB *reply,
+ struct socket_address *peer_addr,
+ struct socket_address *my_addr,
+ int datagram);
+
+/* hold information about one kdc socket */
+struct kdc_socket {
+ struct socket_context *sock;
+ struct kdc_server *kdc;
+ struct tevent_fd *fde;
+
+ /* a queue of outgoing replies that have been deferred */
+ struct kdc_reply *send_queue;
+
+ kdc_process_fn_t process;
+};
+/*
+ state of an open tcp connection
+*/
+struct kdc_tcp_connection {
+ /* stream connection we belong to */
+ struct stream_connection *conn;
+
+ /* the kdc_server the connection belongs to */
+ struct kdc_server *kdc;
+
+ struct packet_context *packet;
+
+ kdc_process_fn_t process;
+};
+
+/*
+ handle fd send events on a KDC socket
+*/
+static void kdc_send_handler(struct kdc_socket *kdc_socket)
+{
+ while (kdc_socket->send_queue) {
+ struct kdc_reply *rep = kdc_socket->send_queue;
+ NTSTATUS status;
+ size_t sendlen;
+
+ status = socket_sendto(kdc_socket->sock, &rep->packet, &sendlen,
+ rep->dest);
+ if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
+ break;
+ }
+ if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_BUFFER_SIZE)) {
+ /* Replace with a krb err, response to big */
+ }
+
+ DLIST_REMOVE(kdc_socket->send_queue, rep);
+ talloc_free(rep);
+ }
+
+ if (kdc_socket->send_queue == NULL) {
+ EVENT_FD_NOT_WRITEABLE(kdc_socket->fde);
+ }
+}
+
+
+/*
+ handle fd recv events on a KDC socket
+*/
+static void kdc_recv_handler(struct kdc_socket *kdc_socket)
+{
+ NTSTATUS status;
+ TALLOC_CTX *tmp_ctx = talloc_new(kdc_socket);
+ DATA_BLOB blob;
+ struct kdc_reply *rep;
+ DATA_BLOB reply;
+ size_t nread, dsize;
+ struct socket_address *src;
+ struct socket_address *my_addr;
+ int ret;
+
+ status = socket_pending(kdc_socket->sock, &dsize);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ return;
+ }
+
+ blob = data_blob_talloc(tmp_ctx, NULL, dsize);
+ if (blob.data == NULL) {
+ /* hope this is a temporary low memory condition */
+ talloc_free(tmp_ctx);
+ return;
+ }
+
+ status = socket_recvfrom(kdc_socket->sock, blob.data, blob.length, &nread,
+ tmp_ctx, &src);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ return;
+ }
+ blob.length = nread;
+
+ DEBUG(10,("Received krb5 UDP packet of length %lu from %s:%u\n",
+ (long)blob.length, src->addr, (uint16_t)src->port));
+
+ my_addr = socket_get_my_addr(kdc_socket->sock, tmp_ctx);
+ if (!my_addr) {
+ talloc_free(tmp_ctx);
+ return;
+ }
+
+
+ /* Call krb5 */
+ ret = kdc_socket->process(kdc_socket->kdc,
+ tmp_ctx,
+ &blob,
+ &reply,
+ src, my_addr,
+ 1 /* Datagram */);
+ if (!ret) {
+ talloc_free(tmp_ctx);
+ return;
+ }
+
+ /* queue a pending reply */
+ rep = talloc(kdc_socket, struct kdc_reply);
+ if (rep == NULL) {
+ talloc_free(tmp_ctx);
+ return;
+ }
+ rep->dest = talloc_steal(rep, src);
+ rep->packet = reply;
+ talloc_steal(rep, reply.data);
+
+ if (rep->packet.data == NULL) {
+ talloc_free(rep);
+ talloc_free(tmp_ctx);
+ return;
+ }
+
+ DLIST_ADD_END(kdc_socket->send_queue, rep, struct kdc_reply *);
+ EVENT_FD_WRITEABLE(kdc_socket->fde);
+ talloc_free(tmp_ctx);
+}
+
+/*
+ handle fd events on a KDC socket
+*/
+static void kdc_socket_handler(struct tevent_context *ev, struct tevent_fd *fde,
+ uint16_t flags, void *private_data)
+{
+ struct kdc_socket *kdc_socket = talloc_get_type(private_data, struct kdc_socket);
+ if (flags & EVENT_FD_WRITE) {
+ kdc_send_handler(kdc_socket);
+ }
+ if (flags & EVENT_FD_READ) {
+ kdc_recv_handler(kdc_socket);
+ }
+}
+
+static void kdc_tcp_terminate_connection(struct kdc_tcp_connection *kdcconn, const char *reason)
+{
+ stream_terminate_connection(kdcconn->conn, reason);
+}
+
+/*
+ receive a full packet on a KDC connection
+*/
+static NTSTATUS kdc_tcp_recv(void *private_data, DATA_BLOB blob)
+{
+ struct kdc_tcp_connection *kdcconn = talloc_get_type(private_data,
+ struct kdc_tcp_connection);
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ TALLOC_CTX *tmp_ctx = talloc_new(kdcconn);
+ int ret;
+ DATA_BLOB input, reply;
+ struct socket_address *src_addr;
+ struct socket_address *my_addr;
+
+ talloc_steal(tmp_ctx, blob.data);
+
+ src_addr = socket_get_peer_addr(kdcconn->conn->socket, tmp_ctx);
+ if (!src_addr) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ my_addr = socket_get_my_addr(kdcconn->conn->socket, tmp_ctx);
+ if (!my_addr) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* Call krb5 */
+ input = data_blob_const(blob.data + 4, blob.length - 4);
+
+ ret = kdcconn->process(kdcconn->kdc,
+ tmp_ctx,
+ &input,
+ &reply,
+ src_addr,
+ my_addr,
+ 0 /* Not datagram */);
+ if (!ret) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ /* and now encode the reply */
+ blob = data_blob_talloc(kdcconn, NULL, reply.length + 4);
+ if (!blob.data) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ RSIVAL(blob.data, 0, reply.length);
+ memcpy(blob.data + 4, reply.data, reply.length);
+
+ status = packet_send(kdcconn->packet, blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ /* the call isn't needed any more */
+ talloc_free(tmp_ctx);
+ return NT_STATUS_OK;
+}
+
+/*
+ receive some data on a KDC connection
+*/
+static void kdc_tcp_recv_handler(struct stream_connection *conn, uint16_t flags)
+{
+ struct kdc_tcp_connection *kdcconn = talloc_get_type(conn->private_data,
+ struct kdc_tcp_connection);
+ packet_recv(kdcconn->packet);
+}
+
+/*
+ called on a tcp recv error
+*/
+static void kdc_tcp_recv_error(void *private_data, NTSTATUS status)
+{
+ struct kdc_tcp_connection *kdcconn = talloc_get_type(private_data,
+ struct kdc_tcp_connection);
+ kdc_tcp_terminate_connection(kdcconn, nt_errstr(status));
+}
+
+/*
+ called when we can write to a connection
+*/
+static void kdc_tcp_send(struct stream_connection *conn, uint16_t flags)
+{
+ struct kdc_tcp_connection *kdcconn = talloc_get_type(conn->private_data,
+ struct kdc_tcp_connection);
+ packet_queue_run(kdcconn->packet);
+}
+
+/**
+ Wrapper for krb5_kdc_process_krb5_request, converting to/from Samba
+ calling conventions
+*/
+
+static bool kdc_process(struct kdc_server *kdc,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *input,
+ DATA_BLOB *reply,
+ struct socket_address *peer_addr,
+ struct socket_address *my_addr,
+ int datagram_reply)
+{
+ int ret;
+ krb5_data k5_reply;
+ krb5_data_zero(&k5_reply);
+
+ krb5_kdc_update_time(NULL);
+
+ DEBUG(10,("Received KDC packet of length %lu from %s:%d\n",
+ (long)input->length - 4, peer_addr->addr, peer_addr->port));
+
+ ret = krb5_kdc_process_krb5_request(kdc->smb_krb5_context->krb5_context,
+ kdc->config,
+ input->data, input->length,
+ &k5_reply,
+ peer_addr->addr,
+ peer_addr->sockaddr,
+ datagram_reply);
+ if (ret == -1) {
+ *reply = data_blob(NULL, 0);
+ return false;
+ }
+ if (k5_reply.length) {
+ *reply = data_blob_talloc(mem_ctx, k5_reply.data, k5_reply.length);
+ krb5_free_data_contents(kdc->smb_krb5_context->krb5_context, &k5_reply);
+ } else {
+ *reply = data_blob(NULL, 0);
+ }
+ return true;
+}
+
+/*
+ called when we get a new connection
+*/
+static void kdc_tcp_generic_accept(struct stream_connection *conn, kdc_process_fn_t process_fn)
+{
+ struct kdc_server *kdc = talloc_get_type(conn->private_data, struct kdc_server);
+ struct kdc_tcp_connection *kdcconn;
+
+ kdcconn = talloc_zero(conn, struct kdc_tcp_connection);
+ if (!kdcconn) {
+ stream_terminate_connection(conn, "kdc_tcp_accept: out of memory");
+ return;
+ }
+ kdcconn->conn = conn;
+ kdcconn->kdc = kdc;
+ kdcconn->process = process_fn;
+ conn->private_data = kdcconn;
+
+ kdcconn->packet = packet_init(kdcconn);
+ if (kdcconn->packet == NULL) {
+ kdc_tcp_terminate_connection(kdcconn, "kdc_tcp_accept: out of memory");
+ return;
+ }
+ packet_set_private(kdcconn->packet, kdcconn);
+ packet_set_socket(kdcconn->packet, conn->socket);
+ packet_set_callback(kdcconn->packet, kdc_tcp_recv);
+ packet_set_full_request(kdcconn->packet, packet_full_request_u32);
+ packet_set_error_handler(kdcconn->packet, kdc_tcp_recv_error);
+ packet_set_event_context(kdcconn->packet, conn->event.ctx);
+ packet_set_fde(kdcconn->packet, conn->event.fde);
+ packet_set_serialise(kdcconn->packet);
+}
+
+static void kdc_tcp_accept(struct stream_connection *conn)
+{
+ kdc_tcp_generic_accept(conn, kdc_process);
+}
+
+static const struct stream_server_ops kdc_tcp_stream_ops = {
+ .name = "kdc_tcp",
+ .accept_connection = kdc_tcp_accept,
+ .recv_handler = kdc_tcp_recv_handler,
+ .send_handler = kdc_tcp_send
+};
+
+static void kpasswdd_tcp_accept(struct stream_connection *conn)
+{
+ kdc_tcp_generic_accept(conn, kpasswdd_process);
+}
+
+static const struct stream_server_ops kpasswdd_tcp_stream_ops = {
+ .name = "kpasswdd_tcp",
+ .accept_connection = kpasswdd_tcp_accept,
+ .recv_handler = kdc_tcp_recv_handler,
+ .send_handler = kdc_tcp_send
+};
+
+/*
+ start listening on the given address
+*/
+static NTSTATUS kdc_add_socket(struct kdc_server *kdc, const char *address,
+ uint16_t kdc_port, uint16_t kpasswd_port)
+{
+ const struct model_ops *model_ops;
+ struct kdc_socket *kdc_socket;
+ struct kdc_socket *kpasswd_socket;
+ struct socket_address *kdc_address, *kpasswd_address;
+ NTSTATUS status;
+
+ kdc_socket = talloc(kdc, struct kdc_socket);
+ NT_STATUS_HAVE_NO_MEMORY(kdc_socket);
+
+ kpasswd_socket = talloc(kdc, struct kdc_socket);
+ NT_STATUS_HAVE_NO_MEMORY(kpasswd_socket);
+
+ status = socket_create("ip", SOCKET_TYPE_DGRAM, &kdc_socket->sock, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(kdc_socket);
+ return status;
+ }
+
+ status = socket_create("ip", SOCKET_TYPE_DGRAM, &kpasswd_socket->sock, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(kpasswd_socket);
+ return status;
+ }
+
+ kdc_socket->kdc = kdc;
+ kdc_socket->send_queue = NULL;
+ kdc_socket->process = kdc_process;
+
+ talloc_steal(kdc_socket, kdc_socket->sock);
+
+ kdc_socket->fde = event_add_fd(kdc->task->event_ctx, kdc,
+ socket_get_fd(kdc_socket->sock), EVENT_FD_READ,
+ kdc_socket_handler, kdc_socket);
+
+ kdc_address = socket_address_from_strings(kdc_socket, kdc_socket->sock->backend_name,
+ address, kdc_port);
+ NT_STATUS_HAVE_NO_MEMORY(kdc_address);
+
+ status = socket_listen(kdc_socket->sock, kdc_address, 0, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Failed to bind to %s:%d UDP for kdc - %s\n",
+ address, kdc_port, nt_errstr(status)));
+ talloc_free(kdc_socket);
+ return status;
+ }
+
+ kpasswd_socket->kdc = kdc;
+ kpasswd_socket->send_queue = NULL;
+ kpasswd_socket->process = kpasswdd_process;
+
+ talloc_steal(kpasswd_socket, kpasswd_socket->sock);
+
+ kpasswd_socket->fde = event_add_fd(kdc->task->event_ctx, kdc,
+ socket_get_fd(kpasswd_socket->sock), EVENT_FD_READ,
+ kdc_socket_handler, kpasswd_socket);
+
+ kpasswd_address = socket_address_from_strings(kpasswd_socket, kpasswd_socket->sock->backend_name,
+ address, kpasswd_port);
+ NT_STATUS_HAVE_NO_MEMORY(kpasswd_address);
+
+ status = socket_listen(kpasswd_socket->sock, kpasswd_address, 0, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Failed to bind to %s:%d UDP for kpasswd - %s\n",
+ address, kpasswd_port, nt_errstr(status)));
+ talloc_free(kpasswd_socket);
+ return status;
+ }
+
+ /* within the kdc task we want to be a single process, so
+ ask for the single process model ops and pass these to the
+ stream_setup_socket() call. */
+ model_ops = process_model_startup(kdc->task->event_ctx, "single");
+ if (!model_ops) {
+ DEBUG(0,("Can't find 'single' process model_ops\n"));
+ talloc_free(kdc_socket);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ status = stream_setup_socket(kdc->task->event_ctx,
+ kdc->task->lp_ctx,
+ model_ops,
+ &kdc_tcp_stream_ops,
+ "ip", address, &kdc_port,
+ lp_socket_options(kdc->task->lp_ctx),
+ kdc);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Failed to bind to %s:%u TCP - %s\n",
+ address, kdc_port, nt_errstr(status)));
+ talloc_free(kdc_socket);
+ return status;
+ }
+
+ status = stream_setup_socket(kdc->task->event_ctx,
+ kdc->task->lp_ctx,
+ model_ops,
+ &kpasswdd_tcp_stream_ops,
+ "ip", address, &kpasswd_port,
+ lp_socket_options(kdc->task->lp_ctx),
+ kdc);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Failed to bind to %s:%u TCP - %s\n",
+ address, kpasswd_port, nt_errstr(status)));
+ talloc_free(kdc_socket);
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ setup our listening sockets on the configured network interfaces
+*/
+static NTSTATUS kdc_startup_interfaces(struct kdc_server *kdc, struct loadparm_context *lp_ctx,
+ struct interface *ifaces)
+{
+ int num_interfaces;
+ TALLOC_CTX *tmp_ctx = talloc_new(kdc);
+ NTSTATUS status;
+ int i;
+
+ num_interfaces = iface_count(ifaces);
+
+ for (i=0; i<num_interfaces; i++) {
+ const char *address = talloc_strdup(tmp_ctx, iface_n_ip(ifaces, i));
+ status = kdc_add_socket(kdc, address, lp_krb5_port(lp_ctx),
+ lp_kpasswd_port(lp_ctx));
+ NT_STATUS_NOT_OK_RETURN(status);
+ }
+
+ talloc_free(tmp_ctx);
+
+ return NT_STATUS_OK;
+}
+
+static struct krb5plugin_windc_ftable windc_plugin_table = {
+ .minor_version = KRB5_WINDC_PLUGING_MINOR,
+ .init = samba_kdc_plugin_init,
+ .fini = samba_kdc_plugin_fini,
+ .pac_generate = samba_kdc_get_pac,
+ .pac_verify = samba_kdc_reget_pac,
+ .client_access = samba_kdc_check_client_access,
+};
+
+
+static NTSTATUS kdc_check_generic_kerberos(struct irpc_message *msg,
+ struct kdc_check_generic_kerberos *r)
+{
+ struct PAC_Validate pac_validate;
+ DATA_BLOB srv_sig;
+ struct PAC_SIGNATURE_DATA kdc_sig;
+ struct kdc_server *kdc = talloc_get_type(msg->private_data, struct kdc_server);
+ enum ndr_err_code ndr_err;
+ krb5_enctype etype;
+ int ret;
+ hdb_entry_ex ent;
+ krb5_principal principal;
+ krb5_keyblock keyblock;
+ Key *key;
+
+ /* There is no reply to this request */
+ r->out.generic_reply = data_blob(NULL, 0);
+
+ ndr_err = ndr_pull_struct_blob(&r->in.generic_request, msg,
+ lp_iconv_convenience(kdc->task->lp_ctx),
+ &pac_validate,
+ (ndr_pull_flags_fn_t)ndr_pull_PAC_Validate);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (pac_validate.MessageType != 3) {
+ /* We don't implement any other message types - such as certificate validation - yet */
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (pac_validate.ChecksumAndSignature.length != (pac_validate.ChecksumLength + pac_validate.SignatureLength)
+ || pac_validate.ChecksumAndSignature.length < pac_validate.ChecksumLength
+ || pac_validate.ChecksumAndSignature.length < pac_validate.SignatureLength ) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ srv_sig = data_blob_const(pac_validate.ChecksumAndSignature.data,
+ pac_validate.ChecksumLength);
+
+ if (pac_validate.SignatureType == CKSUMTYPE_HMAC_MD5) {
+ etype = ETYPE_ARCFOUR_HMAC_MD5;
+ } else {
+ ret = krb5_cksumtype_to_enctype(kdc->smb_krb5_context->krb5_context, pac_validate.SignatureType,
+ &etype);
+ if (ret != 0) {
+ return NT_STATUS_LOGON_FAILURE;
+ }
+ }
+
+ ret = krb5_make_principal(kdc->smb_krb5_context->krb5_context, &principal,
+ lp_realm(kdc->task->lp_ctx),
+ "krbtgt", lp_realm(kdc->task->lp_ctx),
+ NULL);
+
+ if (ret != 0) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = kdc->config->db[0]->hdb_fetch(kdc->smb_krb5_context->krb5_context,
+ kdc->config->db[0],
+ principal,
+ HDB_F_GET_KRBTGT | HDB_F_DECRYPT,
+ &ent);
+
+ if (ret != 0) {
+ hdb_free_entry(kdc->smb_krb5_context->krb5_context, &ent);
+ krb5_free_principal(kdc->smb_krb5_context->krb5_context, principal);
+
+ return NT_STATUS_LOGON_FAILURE;
+ }
+
+ ret = hdb_enctype2key(kdc->smb_krb5_context->krb5_context, &ent.entry, etype, &key);
+
+ if (ret != 0) {
+ hdb_free_entry(kdc->smb_krb5_context->krb5_context, &ent);
+ krb5_free_principal(kdc->smb_krb5_context->krb5_context, principal);
+ return NT_STATUS_LOGON_FAILURE;
+ }
+
+ keyblock = key->key;
+
+ kdc_sig.type = pac_validate.SignatureType;
+ kdc_sig.signature = data_blob_const(&pac_validate.ChecksumAndSignature.data[pac_validate.ChecksumLength],
+ pac_validate.SignatureLength);
+ ret = check_pac_checksum(msg, srv_sig, &kdc_sig,
+ kdc->smb_krb5_context->krb5_context, &keyblock);
+
+ hdb_free_entry(kdc->smb_krb5_context->krb5_context, &ent);
+ krb5_free_principal(kdc->smb_krb5_context->krb5_context, principal);
+
+ if (ret != 0) {
+ return NT_STATUS_LOGON_FAILURE;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+static struct hdb_method hdb_samba4 = {
+ .interface_version = HDB_INTERFACE_VERSION,
+ .prefix = "samba4:",
+ .create = hdb_samba4_create
+};
+
+/*
+ startup the kdc task
+*/
+static void kdc_task_init(struct task_server *task)
+{
+ struct kdc_server *kdc;
+ NTSTATUS status;
+ krb5_error_code ret;
+ struct interface *ifaces;
+
+ switch (lp_server_role(task->lp_ctx)) {
+ case ROLE_STANDALONE:
+ task_server_terminate(task, "kdc: no KDC required in standalone configuration");
+ return;
+ case ROLE_DOMAIN_MEMBER:
+ task_server_terminate(task, "kdc: no KDC required in member server configuration");
+ return;
+ case ROLE_DOMAIN_CONTROLLER:
+ /* Yes, we want a KDC */
+ break;
+ }
+
+ load_interfaces(task, lp_interfaces(task->lp_ctx), &ifaces);
+
+ if (iface_count(ifaces) == 0) {
+ task_server_terminate(task, "kdc: no network interfaces configured");
+ return;
+ }
+
+ task_server_set_title(task, "task[kdc]");
+
+ kdc = talloc(task, struct kdc_server);
+ if (kdc == NULL) {
+ task_server_terminate(task, "kdc: out of memory");
+ return;
+ }
+
+ kdc->task = task;
+
+ initialize_krb5_error_table();
+
+ ret = smb_krb5_init_context(kdc, task->event_ctx, task->lp_ctx, &kdc->smb_krb5_context);
+ if (ret) {
+ DEBUG(1,("kdc_task_init: krb5_init_context failed (%s)\n",
+ error_message(ret)));
+ task_server_terminate(task, "kdc: krb5_init_context failed");
+ return;
+ }
+
+ krb5_add_et_list(kdc->smb_krb5_context->krb5_context, initialize_hdb_error_table_r);
+
+ ret = krb5_kdc_get_config(kdc->smb_krb5_context->krb5_context,
+ &kdc->config);
+ if(ret) {
+ task_server_terminate(task, "kdc: failed to get KDC configuration");
+ return;
+ }
+
+ kdc->config->logf = kdc->smb_krb5_context->logf;
+ kdc->config->db = talloc(kdc, struct HDB *);
+ if (!kdc->config->db) {
+ task_server_terminate(task, "kdc: out of memory");
+ return;
+ }
+ kdc->config->num_db = 1;
+
+ status = kdc_hdb_samba4_create(kdc, task->event_ctx, task->lp_ctx,
+ kdc->smb_krb5_context->krb5_context,
+ &kdc->config->db[0], NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ task_server_terminate(task, "kdc: hdb_ldb_create (setup KDC database) failed");
+ return;
+ }
+
+
+ /* Register hdb-samba4 hooks */
+ ret = krb5_plugin_register(kdc->smb_krb5_context->krb5_context,
+ PLUGIN_TYPE_DATA, "hdb",
+ &hdb_samba4);
+ if(ret) {
+ task_server_terminate(task, "kdc: failed to register hdb keytab");
+ return;
+ }
+
+ ret = krb5_kt_register(kdc->smb_krb5_context->krb5_context, &hdb_kt_ops);
+ if(ret) {
+ task_server_terminate(task, "kdc: failed to register hdb keytab");
+ return;
+ }
+
+ /* Registar WinDC hooks */
+ ret = krb5_plugin_register(kdc->smb_krb5_context->krb5_context,
+ PLUGIN_TYPE_DATA, "windc",
+ &windc_plugin_table);
+ if(ret) {
+ task_server_terminate(task, "kdc: failed to register hdb keytab");
+ return;
+ }
+
+ krb5_kdc_windc_init(kdc->smb_krb5_context->krb5_context);
+
+ kdc_mem_ctx = kdc->smb_krb5_context;
+ kdc_ev_ctx = task->event_ctx;
+ kdc_lp_ctx = task->lp_ctx;
+
+ /* start listening on the configured network interfaces */
+ status = kdc_startup_interfaces(kdc, task->lp_ctx, ifaces);
+ if (!NT_STATUS_IS_OK(status)) {
+ task_server_terminate(task, "kdc failed to setup interfaces");
+ return;
+ }
+
+ status = IRPC_REGISTER(task->msg_ctx, irpc, KDC_CHECK_GENERIC_KERBEROS,
+ kdc_check_generic_kerberos, kdc);
+ if (!NT_STATUS_IS_OK(status)) {
+ task_server_terminate(task, "nbtd failed to setup monitoring");
+ return;
+ }
+
+ irpc_add_name(task->msg_ctx, "kdc_server");
+}
+
+
+/* called at smbd startup - register ourselves as a server service */
+NTSTATUS server_service_kdc_init(void)
+{
+ return register_server_service("kdc", kdc_task_init);
+}
diff --git a/source4/kdc/kdc.h b/source4/kdc/kdc.h
new file mode 100644
index 0000000000..417f327a57
--- /dev/null
+++ b/source4/kdc/kdc.h
@@ -0,0 +1,62 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ KDC structures
+
+ Copyright (C) Andrew Tridgell 2005
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "system/kerberos.h"
+#include "auth/kerberos/kerberos.h"
+#include <kdc.h>
+#include <hdb.h>
+#include <krb5/windc_plugin.h>
+#include "kdc/pac_glue.h"
+
+struct kdc_server;
+struct socket_address;
+
+extern TALLOC_CTX *kdc_mem_ctx;
+extern struct tevent_context *kdc_ev_ctx;
+extern struct loadparm_context *kdc_lp_ctx;
+
+bool kpasswdd_process(struct kdc_server *kdc,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *input,
+ DATA_BLOB *reply,
+ struct socket_address *peer_addr,
+ struct socket_address *my_addr,
+ int datagram_reply);
+
+/*
+ top level context structure for the kdc server
+*/
+struct kdc_server {
+ struct task_server *task;
+ krb5_kdc_configuration *config;
+ struct smb_krb5_context *smb_krb5_context;
+};
+
+
+struct hdb_ldb_private {
+ struct ldb_context *samdb;
+ struct smb_iconv_convenience *iconv_convenience;
+ struct ldb_message *msg;
+ struct ldb_message *realm_ref_msg;
+ hdb_entry_ex *entry_ex;
+ const char *netbios_name;
+};
diff --git a/source4/kdc/kpasswdd.c b/source4/kdc/kpasswdd.c
new file mode 100644
index 0000000000..8f2cb68129
--- /dev/null
+++ b/source4/kdc/kpasswdd.c
@@ -0,0 +1,628 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ kpasswd Server implementation
+
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "smbd/service_task.h"
+#include "lib/events/events.h"
+#include "lib/socket/socket.h"
+#include "system/network.h"
+#include "../lib/util/dlinklist.h"
+#include "lib/ldb/include/ldb.h"
+#include "auth/gensec/gensec.h"
+#include "auth/credentials/credentials.h"
+#include "auth/credentials/credentials_krb5.h"
+#include "auth/auth.h"
+#include "dsdb/samdb/samdb.h"
+#include "rpc_server/dcerpc_server.h"
+#include "rpc_server/samr/proto.h"
+#include "libcli/security/security.h"
+#include "param/param.h"
+#include "kdc/kdc.h"
+
+/* TODO: remove all SAMBA4_INTERNAL_HEIMDAL stuff from this file */
+#ifdef SAMBA4_INTERNAL_HEIMDAL
+#include "heimdal_build/kpasswdd-glue.h"
+#endif
+
+/* hold information about one kdc socket */
+struct kpasswd_socket {
+ struct socket_context *sock;
+ struct kdc_server *kdc;
+ struct tevent_fd *fde;
+
+ /* a queue of outgoing replies that have been deferred */
+ struct kdc_reply *send_queue;
+};
+
+/* Return true if there is a valid error packet formed in the error_blob */
+static bool kpasswdd_make_error_reply(struct kdc_server *kdc,
+ TALLOC_CTX *mem_ctx,
+ uint16_t result_code,
+ const char *error_string,
+ DATA_BLOB *error_blob)
+{
+ char *error_string_utf8;
+ size_t len;
+
+ DEBUG(result_code ? 3 : 10, ("kpasswdd: %s\n", error_string));
+
+ if (!push_utf8_talloc(mem_ctx, &error_string_utf8, error_string, &len)) {
+ return false;
+ }
+
+ *error_blob = data_blob_talloc(mem_ctx, NULL, 2 + len + 1);
+ if (!error_blob->data) {
+ return false;
+ }
+ RSSVAL(error_blob->data, 0, result_code);
+ memcpy(error_blob->data + 2, error_string_utf8, len + 1);
+ return true;
+}
+
+/* Return true if there is a valid error packet formed in the error_blob */
+static bool kpasswdd_make_unauth_error_reply(struct kdc_server *kdc,
+ TALLOC_CTX *mem_ctx,
+ uint16_t result_code,
+ const char *error_string,
+ DATA_BLOB *error_blob)
+{
+ bool ret;
+ int kret;
+ DATA_BLOB error_bytes;
+ krb5_data k5_error_bytes, k5_error_blob;
+ ret = kpasswdd_make_error_reply(kdc, mem_ctx, result_code, error_string,
+ &error_bytes);
+ if (!ret) {
+ return false;
+ }
+ k5_error_bytes.data = error_bytes.data;
+ k5_error_bytes.length = error_bytes.length;
+ kret = krb5_mk_error(kdc->smb_krb5_context->krb5_context,
+ result_code, NULL, &k5_error_bytes,
+ NULL, NULL, NULL, NULL, &k5_error_blob);
+ if (kret) {
+ return false;
+ }
+ *error_blob = data_blob_talloc(mem_ctx, k5_error_blob.data, k5_error_blob.length);
+ krb5_data_free(&k5_error_blob);
+ if (!error_blob->data) {
+ return false;
+ }
+ return true;
+}
+
+static bool kpasswd_make_pwchange_reply(struct kdc_server *kdc,
+ TALLOC_CTX *mem_ctx,
+ NTSTATUS status,
+ enum samr_RejectReason reject_reason,
+ struct samr_DomInfo1 *dominfo,
+ DATA_BLOB *error_blob)
+{
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
+ return kpasswdd_make_error_reply(kdc, mem_ctx,
+ KRB5_KPASSWD_ACCESSDENIED,
+ "No such user when changing password",
+ error_blob);
+ }
+ if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ return kpasswdd_make_error_reply(kdc, mem_ctx,
+ KRB5_KPASSWD_ACCESSDENIED,
+ "Not permitted to change password",
+ error_blob);
+ }
+ if (dominfo && NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION)) {
+ const char *reject_string;
+ switch (reject_reason) {
+ case SAMR_REJECT_TOO_SHORT:
+ reject_string = talloc_asprintf(mem_ctx, "Password too short, password must be at least %d characters long",
+ dominfo->min_password_length);
+ break;
+ case SAMR_REJECT_COMPLEXITY:
+ reject_string = "Password does not meet complexity requirements";
+ break;
+ case SAMR_REJECT_IN_HISTORY:
+ reject_string = "Password is already in password history";
+ break;
+ case SAMR_REJECT_OTHER:
+ default:
+ reject_string = talloc_asprintf(mem_ctx, "Password must be at least %d characters long, and cannot match any of your %d previous passwords",
+ dominfo->min_password_length, dominfo->password_history_length);
+ break;
+ }
+ return kpasswdd_make_error_reply(kdc, mem_ctx,
+ KRB5_KPASSWD_SOFTERROR,
+ reject_string,
+ error_blob);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return kpasswdd_make_error_reply(kdc, mem_ctx,
+ KRB5_KPASSWD_HARDERROR,
+ talloc_asprintf(mem_ctx, "failed to set password: %s", nt_errstr(status)),
+ error_blob);
+
+ }
+ return kpasswdd_make_error_reply(kdc, mem_ctx, KRB5_KPASSWD_SUCCESS,
+ "Password changed",
+ error_blob);
+}
+
+/*
+ A user password change
+
+ Return true if there is a valid error packet (or sucess) formed in
+ the error_blob
+*/
+static bool kpasswdd_change_password(struct kdc_server *kdc,
+ TALLOC_CTX *mem_ctx,
+ struct auth_session_info *session_info,
+ const DATA_BLOB *password,
+ DATA_BLOB *reply)
+{
+ NTSTATUS status;
+ enum samr_RejectReason reject_reason;
+ struct samr_DomInfo1 *dominfo;
+ struct ldb_context *samdb;
+
+ samdb = samdb_connect(mem_ctx, kdc->task->event_ctx, kdc->task->lp_ctx, system_session(mem_ctx, kdc->task->lp_ctx));
+ if (!samdb) {
+ return kpasswdd_make_error_reply(kdc, mem_ctx,
+ KRB5_KPASSWD_HARDERROR,
+ "Failed to open samdb",
+ reply);
+ }
+
+ DEBUG(3, ("Changing password of %s\\%s (%s)\n",
+ session_info->server_info->domain_name,
+ session_info->server_info->account_name,
+ dom_sid_string(mem_ctx, session_info->security_token->user_sid)));
+
+ /* User password change */
+ status = samdb_set_password_sid(samdb, mem_ctx,
+ session_info->security_token->user_sid,
+ password, NULL, NULL,
+ true, /* this is a user password change */
+ &reject_reason,
+ &dominfo);
+ return kpasswd_make_pwchange_reply(kdc, mem_ctx,
+ status,
+ reject_reason,
+ dominfo,
+ reply);
+
+}
+
+static bool kpasswd_process_request(struct kdc_server *kdc,
+ TALLOC_CTX *mem_ctx,
+ struct gensec_security *gensec_security,
+ uint16_t version,
+ DATA_BLOB *input,
+ DATA_BLOB *reply)
+{
+ struct auth_session_info *session_info;
+ size_t pw_len;
+
+ if (!NT_STATUS_IS_OK(gensec_session_info(gensec_security,
+ &session_info))) {
+ return kpasswdd_make_error_reply(kdc, mem_ctx,
+ KRB5_KPASSWD_HARDERROR,
+ "gensec_session_info failed!",
+ reply);
+ }
+
+ switch (version) {
+ case KRB5_KPASSWD_VERS_CHANGEPW:
+ {
+ DATA_BLOB password;
+ if (!convert_string_talloc_convenience(mem_ctx, lp_iconv_convenience(kdc->task->lp_ctx),
+ CH_UTF8, CH_UTF16,
+ (const char *)input->data,
+ input->length,
+ (void **)&password.data, &pw_len, false)) {
+ return false;
+ }
+ password.length = pw_len;
+
+ return kpasswdd_change_password(kdc, mem_ctx, session_info,
+ &password, reply);
+ break;
+ }
+ case KRB5_KPASSWD_VERS_SETPW:
+ {
+ NTSTATUS status;
+ enum samr_RejectReason reject_reason = SAMR_REJECT_OTHER;
+ struct samr_DomInfo1 *dominfo = NULL;
+ struct ldb_context *samdb;
+ struct ldb_message *msg;
+ krb5_context context = kdc->smb_krb5_context->krb5_context;
+
+ ChangePasswdDataMS chpw;
+ DATA_BLOB password;
+
+ krb5_principal principal;
+ char *set_password_on_princ;
+ struct ldb_dn *set_password_on_dn;
+
+ size_t len;
+ int ret;
+
+ msg = ldb_msg_new(mem_ctx);
+ if (!msg) {
+ return false;
+ }
+
+ ret = decode_ChangePasswdDataMS(input->data, input->length,
+ &chpw, &len);
+ if (ret) {
+ return kpasswdd_make_error_reply(kdc, mem_ctx,
+ KRB5_KPASSWD_MALFORMED,
+ "failed to decode password change structure",
+ reply);
+ }
+
+ if (!convert_string_talloc_convenience(mem_ctx, lp_iconv_convenience(kdc->task->lp_ctx),
+ CH_UTF8, CH_UTF16,
+ (const char *)chpw.newpasswd.data,
+ chpw.newpasswd.length,
+ (void **)&password.data, &pw_len, false)) {
+ free_ChangePasswdDataMS(&chpw);
+ return false;
+ }
+
+ password.length = pw_len;
+
+ if ((chpw.targname && !chpw.targrealm)
+ || (!chpw.targname && chpw.targrealm)) {
+ return kpasswdd_make_error_reply(kdc, mem_ctx,
+ KRB5_KPASSWD_MALFORMED,
+ "Realm and principal must be both present, or neither present",
+ reply);
+ }
+ if (chpw.targname && chpw.targrealm) {
+#ifdef SAMBA4_INTERNAL_HEIMDAL
+ if (_krb5_principalname2krb5_principal(kdc->smb_krb5_context->krb5_context,
+ &principal, *chpw.targname,
+ *chpw.targrealm) != 0) {
+ free_ChangePasswdDataMS(&chpw);
+ return kpasswdd_make_error_reply(kdc, mem_ctx,
+ KRB5_KPASSWD_MALFORMED,
+ "failed to extract principal to set",
+ reply);
+
+ }
+#else /* SAMBA4_INTERNAL_HEIMDAL */
+ return kpasswdd_make_error_reply(kdc, mem_ctx,
+ KRB5_KPASSWD_BAD_VERSION,
+ "Operation Not Implemented",
+ reply);
+#endif /* SAMBA4_INTERNAL_HEIMDAL */
+ } else {
+ free_ChangePasswdDataMS(&chpw);
+ return kpasswdd_change_password(kdc, mem_ctx, session_info,
+ &password, reply);
+ }
+ free_ChangePasswdDataMS(&chpw);
+
+ if (krb5_unparse_name(context, principal, &set_password_on_princ) != 0) {
+ krb5_free_principal(context, principal);
+ return kpasswdd_make_error_reply(kdc, mem_ctx,
+ KRB5_KPASSWD_MALFORMED,
+ "krb5_unparse_name failed!",
+ reply);
+ }
+
+ krb5_free_principal(context, principal);
+
+ samdb = samdb_connect(mem_ctx, kdc->task->event_ctx, kdc->task->lp_ctx, session_info);
+ if (!samdb) {
+ return kpasswdd_make_error_reply(kdc, mem_ctx,
+ KRB5_KPASSWD_HARDERROR,
+ "Unable to open database!",
+ reply);
+ }
+
+ DEBUG(3, ("%s\\%s (%s) is changing password of %s\n",
+ session_info->server_info->domain_name,
+ session_info->server_info->account_name,
+ dom_sid_string(mem_ctx, session_info->security_token->user_sid),
+ set_password_on_princ));
+ ret = ldb_transaction_start(samdb);
+ if (ret) {
+ status = NT_STATUS_TRANSACTION_ABORTED;
+ return kpasswd_make_pwchange_reply(kdc, mem_ctx,
+ status,
+ SAMR_REJECT_OTHER,
+ NULL,
+ reply);
+ }
+
+ status = crack_user_principal_name(samdb, mem_ctx,
+ set_password_on_princ,
+ &set_password_on_dn, NULL);
+ free(set_password_on_princ);
+ if (!NT_STATUS_IS_OK(status)) {
+ ldb_transaction_cancel(samdb);
+ return kpasswd_make_pwchange_reply(kdc, mem_ctx,
+ status,
+ SAMR_REJECT_OTHER,
+ NULL,
+ reply);
+ }
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ ldb_transaction_cancel(samdb);
+ status = NT_STATUS_NO_MEMORY;
+ } else {
+ msg->dn = ldb_dn_copy(msg, set_password_on_dn);
+ if (!msg->dn) {
+ status = NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ if (NT_STATUS_IS_OK(status)) {
+ /* Admin password set */
+ status = samdb_set_password(samdb, mem_ctx,
+ set_password_on_dn, NULL,
+ msg, &password, NULL, NULL,
+ false, /* this is not a user password change */
+ &reject_reason, &dominfo);
+ }
+
+ if (NT_STATUS_IS_OK(status)) {
+ /* modify the samdb record */
+ ret = samdb_replace(samdb, mem_ctx, msg);
+ if (ret != 0) {
+ DEBUG(2,("Failed to modify record to set password on %s: %s\n",
+ ldb_dn_get_linearized(msg->dn),
+ ldb_errstring(samdb)));
+ status = NT_STATUS_ACCESS_DENIED;
+ }
+ }
+ if (NT_STATUS_IS_OK(status)) {
+ ret = ldb_transaction_commit(samdb);
+ if (ret != 0) {
+ DEBUG(1,("Failed to commit transaction to set password on %s: %s\n",
+ ldb_dn_get_linearized(msg->dn),
+ ldb_errstring(samdb)));
+ status = NT_STATUS_TRANSACTION_ABORTED;
+ }
+ } else {
+ ldb_transaction_cancel(samdb);
+ }
+ return kpasswd_make_pwchange_reply(kdc, mem_ctx,
+ status,
+ reject_reason,
+ dominfo,
+ reply);
+ }
+ default:
+ return kpasswdd_make_error_reply(kdc, mem_ctx,
+ KRB5_KPASSWD_BAD_VERSION,
+ talloc_asprintf(mem_ctx,
+ "Protocol version %u not supported",
+ version),
+ reply);
+ }
+ return true;
+}
+
+bool kpasswdd_process(struct kdc_server *kdc,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *input,
+ DATA_BLOB *reply,
+ struct socket_address *peer_addr,
+ struct socket_address *my_addr,
+ int datagram_reply)
+{
+ bool ret;
+ const uint16_t header_len = 6;
+ uint16_t len;
+ uint16_t ap_req_len;
+ uint16_t krb_priv_len;
+ uint16_t version;
+ NTSTATUS nt_status;
+ DATA_BLOB ap_req, krb_priv_req;
+ DATA_BLOB krb_priv_rep = data_blob(NULL, 0);
+ DATA_BLOB ap_rep = data_blob(NULL, 0);
+ DATA_BLOB kpasswd_req, kpasswd_rep;
+ struct cli_credentials *server_credentials;
+ struct gensec_security *gensec_security;
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+
+ if (!tmp_ctx) {
+ return false;
+ }
+
+ /* Be parinoid. We need to ensure we don't just let the
+ * caller lead us into a buffer overflow */
+ if (input->length <= header_len) {
+ talloc_free(tmp_ctx);
+ return false;
+ }
+
+ len = RSVAL(input->data, 0);
+ if (input->length != len) {
+ talloc_free(tmp_ctx);
+ return false;
+ }
+
+ /* There are two different versions of this protocol so far,
+ * plus others in the standards pipe. Fortunetly they all
+ * take a very similar framing */
+ version = RSVAL(input->data, 2);
+ ap_req_len = RSVAL(input->data, 4);
+ if ((ap_req_len >= len) || (ap_req_len + header_len) >= len) {
+ talloc_free(tmp_ctx);
+ return false;
+ }
+
+ krb_priv_len = len - ap_req_len;
+ ap_req = data_blob_const(&input->data[header_len], ap_req_len);
+ krb_priv_req = data_blob_const(&input->data[header_len + ap_req_len], krb_priv_len);
+
+ server_credentials = cli_credentials_init(tmp_ctx);
+ if (!server_credentials) {
+ DEBUG(1, ("Failed to init server credentials\n"));
+ return false;
+ }
+
+ /* We want the credentials subsystem to use the krb5 context
+ * we already have, rather than a new context */
+ cli_credentials_set_krb5_context(server_credentials, kdc->smb_krb5_context);
+ cli_credentials_set_conf(server_credentials, kdc->task->lp_ctx);
+ nt_status = cli_credentials_set_stored_principal(server_credentials, kdc->task->event_ctx, kdc->task->lp_ctx, "kadmin/changepw");
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ ret = kpasswdd_make_unauth_error_reply(kdc, mem_ctx,
+ KRB5_KPASSWD_HARDERROR,
+ talloc_asprintf(mem_ctx,
+ "Failed to obtain server credentials for kadmin/changepw: %s\n",
+ nt_errstr(nt_status)),
+ &krb_priv_rep);
+ ap_rep.length = 0;
+ if (ret) {
+ goto reply;
+ }
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ /* We don't strictly need to call this wrapper, and could call
+ * gensec_server_start directly, as we have no need for NTLM
+ * and we have a PAC, but this ensures that the wrapper can be
+ * safely extended for other helpful things in future */
+ nt_status = samba_server_gensec_start(tmp_ctx, kdc->task->event_ctx,
+ kdc->task->msg_ctx,
+ kdc->task->lp_ctx,
+ server_credentials,
+ "kpasswd",
+ &gensec_security);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(tmp_ctx);
+ return false;
+ }
+
+ /* The kerberos PRIV packets include these addresses. MIT
+ * clients check that they are present */
+ nt_status = gensec_set_peer_addr(gensec_security, peer_addr);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(tmp_ctx);
+ return false;
+ }
+ nt_status = gensec_set_my_addr(gensec_security, my_addr);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(tmp_ctx);
+ return false;
+ }
+
+ /* We want the GENSEC wrap calls to generate PRIV tokens */
+ gensec_want_feature(gensec_security, GENSEC_FEATURE_SEAL);
+
+ nt_status = gensec_start_mech_by_name(gensec_security, "krb5");
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(tmp_ctx);
+ return false;
+ }
+
+ /* Accept the AP-REQ and generate teh AP-REP we need for the reply */
+ nt_status = gensec_update(gensec_security, tmp_ctx, ap_req, &ap_rep);
+ if (!NT_STATUS_IS_OK(nt_status) && !NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+
+ ret = kpasswdd_make_unauth_error_reply(kdc, mem_ctx,
+ KRB5_KPASSWD_HARDERROR,
+ talloc_asprintf(mem_ctx,
+ "gensec_update failed: %s",
+ nt_errstr(nt_status)),
+ &krb_priv_rep);
+ ap_rep.length = 0;
+ if (ret) {
+ goto reply;
+ }
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ /* Extract the data from the KRB-PRIV half of the message */
+ nt_status = gensec_unwrap(gensec_security, tmp_ctx, &krb_priv_req, &kpasswd_req);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ ret = kpasswdd_make_unauth_error_reply(kdc, mem_ctx,
+ KRB5_KPASSWD_HARDERROR,
+ talloc_asprintf(mem_ctx,
+ "gensec_unwrap failed: %s",
+ nt_errstr(nt_status)),
+ &krb_priv_rep);
+ ap_rep.length = 0;
+ if (ret) {
+ goto reply;
+ }
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ /* Figure out something to do with it (probably changing a password...) */
+ ret = kpasswd_process_request(kdc, tmp_ctx,
+ gensec_security,
+ version,
+ &kpasswd_req, &kpasswd_rep);
+ if (!ret) {
+ /* Argh! */
+ return false;
+ }
+
+ /* And wrap up the reply: This ensures that the error message
+ * or success can be verified by the client */
+ nt_status = gensec_wrap(gensec_security, tmp_ctx,
+ &kpasswd_rep, &krb_priv_rep);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ ret = kpasswdd_make_unauth_error_reply(kdc, mem_ctx,
+ KRB5_KPASSWD_HARDERROR,
+ talloc_asprintf(mem_ctx,
+ "gensec_wrap failed: %s",
+ nt_errstr(nt_status)),
+ &krb_priv_rep);
+ ap_rep.length = 0;
+ if (ret) {
+ goto reply;
+ }
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+reply:
+ *reply = data_blob_talloc(mem_ctx, NULL, krb_priv_rep.length + ap_rep.length + header_len);
+ if (!reply->data) {
+ return false;
+ }
+
+ RSSVAL(reply->data, 0, reply->length);
+ RSSVAL(reply->data, 2, 1); /* This is a version 1 reply, MS change/set or otherwise */
+ RSSVAL(reply->data, 4, ap_rep.length);
+ memcpy(reply->data + header_len,
+ ap_rep.data,
+ ap_rep.length);
+ memcpy(reply->data + header_len + ap_rep.length,
+ krb_priv_rep.data,
+ krb_priv_rep.length);
+
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
diff --git a/source4/kdc/pac-glue.c b/source4/kdc/pac-glue.c
new file mode 100644
index 0000000000..1a0df8e4a1
--- /dev/null
+++ b/source4/kdc/pac-glue.c
@@ -0,0 +1,307 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ PAC Glue between Samba and the KDC
+
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "dsdb/common/flags.h"
+#include "lib/ldb/include/ldb.h"
+#include "librpc/gen_ndr/ndr_krb5pac.h"
+#include "librpc/gen_ndr/krb5pac.h"
+#include "auth/auth.h"
+#include "auth/auth_sam.h"
+#include "auth/auth_sam_reply.h"
+#include "kdc/kdc.h"
+
+struct krb5_dh_moduli;
+struct _krb5_krb_auth_data;
+
+krb5_error_code samba_kdc_plugin_init(krb5_context context, void **ptr)
+{
+ *ptr = NULL;
+ return 0;
+}
+
+void samba_kdc_plugin_fini(void *ptr)
+{
+ return;
+}
+
+static krb5_error_code make_pac(krb5_context context,
+ TALLOC_CTX *mem_ctx,
+ struct smb_iconv_convenience *iconv_convenience,
+ struct auth_serversupplied_info *server_info,
+ krb5_pac *pac)
+{
+ union PAC_INFO info;
+ struct netr_SamInfo3 *info3;
+ krb5_data pac_data;
+ NTSTATUS nt_status;
+ enum ndr_err_code ndr_err;
+ DATA_BLOB pac_out;
+ krb5_error_code ret;
+
+ ZERO_STRUCT(info);
+
+ nt_status = auth_convert_server_info_saminfo3(mem_ctx, server_info, &info3);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(1, ("Getting Samba info failed: %s\n", nt_errstr(nt_status)));
+ return EINVAL;
+ }
+
+ info.logon_info.info = talloc_zero(mem_ctx, struct PAC_LOGON_INFO);
+ if (!mem_ctx) {
+ return ENOMEM;
+ }
+
+ info.logon_info.info->info3 = *info3;
+
+ ndr_err = ndr_push_union_blob(&pac_out, mem_ctx, iconv_convenience, &info,
+ PAC_TYPE_LOGON_INFO,
+ (ndr_push_flags_fn_t)ndr_push_PAC_INFO);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ nt_status = ndr_map_error2ntstatus(ndr_err);
+ DEBUG(1, ("PAC (presig) push failed: %s\n", nt_errstr(nt_status)));
+ return EINVAL;
+ }
+
+ ret = krb5_data_copy(&pac_data, pac_out.data, pac_out.length);
+ if (ret != 0) {
+ return ret;
+ }
+
+ ret = krb5_pac_init(context, pac);
+ if (ret != 0) {
+ krb5_data_free(&pac_data);
+ return ret;
+ }
+
+ ret = krb5_pac_add_buffer(context, *pac, PAC_TYPE_LOGON_INFO, &pac_data);
+ krb5_data_free(&pac_data);
+ if (ret != 0) {
+ return ret;
+ }
+
+ return ret;
+}
+
+/* Given the right private pointer from hdb_ldb, get a PAC from the attached ldb messages */
+krb5_error_code samba_kdc_get_pac(void *priv,
+ krb5_context context,
+ struct hdb_entry_ex *client,
+ krb5_pac *pac)
+{
+ krb5_error_code ret;
+ NTSTATUS nt_status;
+ struct auth_serversupplied_info *server_info;
+ struct hdb_ldb_private *p = talloc_get_type(client->ctx, struct hdb_ldb_private);
+ TALLOC_CTX *mem_ctx = talloc_named(p, 0, "samba_get_pac context");
+ unsigned int userAccountControl;
+
+ if (!mem_ctx) {
+ return ENOMEM;
+ }
+
+ /* The user account may be set not to want the PAC */
+ userAccountControl = ldb_msg_find_attr_as_uint(p->msg, "userAccountControl", 0);
+ if (userAccountControl & UF_NO_AUTH_DATA_REQUIRED) {
+ *pac = NULL;
+ return 0;
+ }
+
+ nt_status = authsam_make_server_info(mem_ctx, p->samdb,
+ p->netbios_name,
+ p->msg,
+ p->realm_ref_msg,
+ data_blob(NULL, 0),
+ data_blob(NULL, 0),
+ &server_info);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("Getting user info for PAC failed: %s\n",
+ nt_errstr(nt_status)));
+ return ENOMEM;
+ }
+
+ ret = make_pac(context, mem_ctx, p->iconv_convenience, server_info, pac);
+
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+/* Resign (and reform, including possibly new groups) a PAC */
+
+krb5_error_code samba_kdc_reget_pac(void *priv, krb5_context context,
+ const krb5_principal client_principal,
+ struct hdb_entry_ex *client,
+ struct hdb_entry_ex *server, krb5_pac *pac)
+{
+ krb5_error_code ret;
+
+ unsigned int userAccountControl;
+
+ struct hdb_ldb_private *p = talloc_get_type(server->ctx, struct hdb_ldb_private);
+
+ struct auth_serversupplied_info *server_info_out;
+
+ TALLOC_CTX *mem_ctx = talloc_named(p, 0, "samba_get_pac context");
+
+ if (!mem_ctx) {
+ return ENOMEM;
+ }
+
+ /* The service account may be set not to want the PAC */
+ userAccountControl = ldb_msg_find_attr_as_uint(p->msg, "userAccountControl", 0);
+ if (userAccountControl & UF_NO_AUTH_DATA_REQUIRED) {
+ talloc_free(mem_ctx);
+ *pac = NULL;
+ return 0;
+ }
+
+ ret = kerberos_pac_to_server_info(mem_ctx, p->iconv_convenience,
+ *pac, context, &server_info_out);
+
+ /* We will compleatly regenerate this pac */
+ krb5_pac_free(context, *pac);
+
+ if (ret) {
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ ret = make_pac(context, mem_ctx, p->iconv_convenience, server_info_out, pac);
+
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+static void samba_kdc_build_edata_reply(TALLOC_CTX *tmp_ctx, krb5_data *e_data,
+ NTSTATUS nt_status)
+{
+ PA_DATA pa;
+ unsigned char *buf;
+ size_t len;
+ krb5_error_code ret = 0;
+
+ if (!e_data)
+ return;
+
+ pa.padata_type = KRB5_PADATA_PW_SALT;
+ pa.padata_value.length = 12;
+ pa.padata_value.data = malloc(pa.padata_value.length);
+ if (!pa.padata_value.data) {
+ e_data->length = 0;
+ e_data->data = NULL;
+ return;
+ }
+
+ SIVAL(pa.padata_value.data, 0, NT_STATUS_V(nt_status));
+ SIVAL(pa.padata_value.data, 4, 0);
+ SIVAL(pa.padata_value.data, 8, 1);
+
+ ASN1_MALLOC_ENCODE(PA_DATA, buf, len, &pa, &len, ret);
+ free(pa.padata_value.data);
+
+ e_data->data = buf;
+ e_data->length = len;
+
+ return;
+}
+
+/* Given an hdb entry (and in particular it's private member), consult
+ * the account_ok routine in auth/auth_sam.c for consistancy */
+
+
+krb5_error_code samba_kdc_check_client_access(void *priv,
+ krb5_context context, hdb_entry_ex *entry_ex,
+ KDC_REQ *req,
+ krb5_data *e_data)
+{
+ krb5_error_code ret;
+ NTSTATUS nt_status;
+ TALLOC_CTX *tmp_ctx = talloc_new(entry_ex->ctx);
+ struct hdb_ldb_private *p = talloc_get_type(entry_ex->ctx, struct hdb_ldb_private);
+ char *name, *workstation = NULL;
+ HostAddresses *addresses = req->req_body.addresses;
+ int i;
+
+ if (!tmp_ctx) {
+ return ENOMEM;
+ }
+
+ ret = krb5_unparse_name(context, entry_ex->entry.principal, &name);
+ if (ret != 0) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ if (addresses) {
+ for (i=0; i < addresses->len; i++) {
+ if (addresses->val->addr_type == KRB5_ADDRESS_NETBIOS) {
+ workstation = talloc_strndup(tmp_ctx, addresses->val->address.data, MIN(addresses->val->address.length, 15));
+ if (workstation) {
+ break;
+ }
+ }
+ }
+ }
+
+ /* Strip space padding */
+ if (workstation) {
+ i = MIN(strlen(workstation), 15);
+ for (; i > 0 && workstation[i - 1] == ' '; i--) {
+ workstation[i - 1] = '\0';
+ }
+ }
+
+ /* we allow all kinds of trusts here */
+ nt_status = authsam_account_ok(tmp_ctx,
+ p->samdb,
+ MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT | MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT,
+ p->msg,
+ p->realm_ref_msg,
+ workstation,
+ name, true);
+ free(name);
+
+ if (NT_STATUS_IS_OK(nt_status))
+ return 0;
+
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_PASSWORD_MUST_CHANGE))
+ ret = KRB5KDC_ERR_KEY_EXPIRED;
+ else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_PASSWORD_EXPIRED))
+ ret = KRB5KDC_ERR_KEY_EXPIRED;
+ else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCOUNT_EXPIRED))
+ ret = KRB5KDC_ERR_CLIENT_REVOKED;
+ else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCOUNT_DISABLED))
+ ret = KRB5KDC_ERR_CLIENT_REVOKED;
+ else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_INVALID_LOGON_HOURS))
+ ret = KRB5KDC_ERR_CLIENT_REVOKED;
+ else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCOUNT_LOCKED_OUT))
+ ret = KRB5KDC_ERR_CLIENT_REVOKED;
+ else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_INVALID_WORKSTATION))
+ ret = KRB5KDC_ERR_POLICY;
+ else
+ ret = KRB5KDC_ERR_POLICY;
+
+ samba_kdc_build_edata_reply(tmp_ctx, e_data, nt_status);
+
+ return ret;
+}
+
diff --git a/source4/ldap_server/config.mk b/source4/ldap_server/config.mk
new file mode 100644
index 0000000000..c8c25931fe
--- /dev/null
+++ b/source4/ldap_server/config.mk
@@ -0,0 +1,22 @@
+# LDAP server subsystem
+
+#######################
+# Start SUBSYSTEM LDAP
+[MODULE::LDAP]
+INIT_FUNCTION = server_service_ldap_init
+SUBSYSTEM = service
+PRIVATE_DEPENDENCIES = CREDENTIALS \
+ LIBCLI_LDAP SAMDB \
+ process_model \
+ gensec \
+ LIBSAMBA-HOSTCONFIG
+# End SUBSYSTEM SMB
+#######################
+
+LDAP_OBJ_FILES = $(addprefix $(ldap_serversrcdir)/, \
+ ldap_server.o \
+ ldap_backend.o \
+ ldap_bind.o \
+ ldap_extended.o)
+
+$(eval $(call proto_header_template,$(ldap_serversrcdir)/proto.h,$(LDAP_OBJ_FILES:.o=.c)))
diff --git a/source4/ldap_server/devdocs/AD-Syntaxes.txt b/source4/ldap_server/devdocs/AD-Syntaxes.txt
new file mode 100644
index 0000000000..4ac3f8874a
--- /dev/null
+++ b/source4/ldap_server/devdocs/AD-Syntaxes.txt
@@ -0,0 +1,79 @@
+Description LDAP OID oMSyntax oMObjectClass attributeSyntax MS-Name
+-------------------------------------------------------------------------------------------------------------------------------------------------------------
+Boolean 1.3.6.1.4.1.1466.115.121.1.7 1 2.5.5.8 Boolean
+Integer 1.3.6.1.4.1.1466.115.121.1.27 2 2.5.5.9 Integer
+Octet String 1.3.6.1.4.1.1466.115.121.1.40 4 2.5.5.10 String(Octet)
+Octet String 1.3.6.1.4.1.1466.115.121.1.40 4 2.5.5.17 String(Sid)
+OID 1.3.6.1.4.1.1466.115.121.1.38 6 2.5.5.2 String(Object-Identifier)
+Integer 1.3.6.1.4.1.1466.115.121.1.27 10 2.5.5.9 Enumeration
+Numeric String 1.3.6.1.4.1.1466.115.121.1.36 18 2.5.5.6 String(Numeric)
+Printable String 1.3.6.1.4.1.1466.115.121.1.44 19 2.5.5.5 String(Printable)
+CaseIgnoreString * 1.2.840.113556.1.4.905 20 2.5.5.4 String(Teletex)
+IA5 String 1.3.6.1.4.1.1466.115.121.1.26 22 2.5.5.5 String(IA5)
+UTC Time 1.3.6.1.4.1.1466.115.121.1.53 23 2.5.5.11 String(UTC-Time)
+Generalized Time 1.3.6.1.4.1.1466.115.121.1.24 24 2.5.5.11 String(Generalized-Time)
+ 1.2.840.113556.1.4.1362 27 2.5.5.3 String(Case Sensitive)
+Directory String 1.3.6.1.4.1.1466.115.121.1.15 64 2.5.5.12 String(Unicode)
+Large-Integer * 1.2.840.113556.1.4.906 65 2.5.5.16 Interval/LargeInteger
+Object-Security-Descriptor * 1.2.840.113556.1.4.907 66 2.5.5.15 String(NT-Sec-Desc)
+DN 1.3.6.1.4.1.1466.115.121.1.12 127 2b0c 0287 731c 0085 4a 2.5.5.1 Object(DS-DN)
+DNWithOctetString * 1.2.840.113556.1.4.903 127 2a86 4886 f714 0101 010b 2.5.5.7 Object(DN-Binary)
+OR-Name * 1.2.840.113556.1.4.1221 127 5606 0102 050b 1D 2.5.5.7 Object(OR-Name)
+Octet String 1.3.6.1.4.1.1466.115.121.1.40 127 2a86 4886 f714 0101 0106 2.5.5.10 Object(Replica-Link)
+Presentation Address 1.3.6.1.4.1.1466.115.121.1.43 127 2b0c 0287 731c 0085 5c 2.5.5.13 Object(Presentation-Address)
+Access Point 1.3.6.1.4.1.1466.115.121.1.2 127 2b0c 0287 731c 0085 3e 2.5.5.14 Object(Access-Point)
+DNWithString * 1.2.840.113556.1.4.904 127 2a86 4886 f714 0101 010c 2.5.5.14 Object(DN-String)
+
+
+Unrepresent Syntaxes:
+
+ACI Item 1.3.6.1.4.1.1466.115.121.1.1
+Access Point 1.3.6.1.4.1.1466.115.121.1.2
+Attribute Type Description 1.3.6.1.4.1.1466.115.121.1.3
+Audio 1.3.6.1.4.1.1466.115.121.1.4
+Binary 1.3.6.1.4.1.1466.115.121.1.5
+Bit String 1.3.6.1.4.1.1466.115.121.1.6
+Certificate 1.3.6.1.4.1.1466.115.121.1.8
+Certificate List 1.3.6.1.4.1.1466.115.121.1.9
+Certificate Pair 1.3.6.1.4.1.1466.115.121.1.10
+Country String 1.3.6.1.4.1.1466.115.121.1.11
+Data Quality Syntax 1.3.6.1.4.1.1466.115.121.1.13
+Delivery Method 1.3.6.1.4.1.1466.115.121.1.14
+DIT Content Rule Description 1.3.6.1.4.1.1466.115.121.1.16
+DIT Structure Rule Description 1.3.6.1.4.1.1466.115.121.1.17
+DL Submit Permission 1.3.6.1.4.1.1466.115.121.1.18
+DSA Quality Syntax 1.3.6.1.4.1.1466.115.121.1.19
+DSE Type 1.3.6.1.4.1.1466.115.121.1.20
+Enhanced Guide 1.3.6.1.4.1.1466.115.121.1.21
+Facsimile Telephone Number 1.3.6.1.4.1.1466.115.121.1.22
+Fax 1.3.6.1.4.1.1466.115.121.1.23
+Guide 1.3.6.1.4.1.1466.115.121.1.25
+JPEG 1.3.6.1.4.1.1466.115.121.1.28
+Master And Shadow Access Points 1.3.6.1.4.1.1466.115.121.1.29
+Matching Rule Description 1.3.6.1.4.1.1466.115.121.1.30
+Matching Rule Use Description 1.3.6.1.4.1.1466.115.121.1.31
+Mail Preference 1.3.6.1.4.1.1466.115.121.1.32
+MHS OR Address 1.3.6.1.4.1.1466.115.121.1.33
+Name And Optional UID 1.3.6.1.4.1.1466.115.121.1.34
+Name Form Description 1.3.6.1.4.1.1466.115.121.1.35
+Object Class Description 1.3.6.1.4.1.1466.115.121.1.37
+Other Mailbox 1.3.6.1.4.1.1466.115.121.1.39
+Postal Address 1.3.6.1.4.1.1466.115.121.1.41
+Protocol Information 1.3.6.1.4.1.1466.115.121.1.42
+Subtree Specification 1.3.6.1.4.1.1466.115.121.1.45
+Supplier Information 1.3.6.1.4.1.1466.115.121.1.46
+Supplier Or Consumer 1.3.6.1.4.1.1466.115.121.1.47
+Supplier And Consumer 1.3.6.1.4.1.1466.115.121.1.48
+Supported Algorithm 1.3.6.1.4.1.1466.115.121.1.49
+Telephone Number 1.3.6.1.4.1.1466.115.121.1.50
+Teletex Terminal Identifier 1.3.6.1.4.1.1466.115.121.1.51
+Telex Number 1.3.6.1.4.1.1466.115.121.1.52
+LDAP Syntax Description 1.3.6.1.4.1.1466.115.121.1.54
+Modify Rights 1.3.6.1.4.1.1466.115.121.1.55
+LDAP Schema Definition 1.3.6.1.4.1.1466.115.121.1.56
+LDAP Schema Description 1.3.6.1.4.1.1466.115.121.1.57
+Substring Assertion 1.3.6.1.4.1.1466.115.121.1.58
+
+
+* names come from: draft-armijo-ldap-syntax-00.txt
+
diff --git a/source4/ldap_server/devdocs/Index b/source4/ldap_server/devdocs/Index
new file mode 100644
index 0000000000..a081b4d4a8
--- /dev/null
+++ b/source4/ldap_server/devdocs/Index
@@ -0,0 +1,34 @@
+RFC 1823 - The LDAP Application Program Interface
+RFC 2307 - An Approach for Using LDAP as a Network Information Service
+RFC 2696 - LDAP Control Extension for Simple Paged Results Manipulation
+RFC 2849 - The LDAP Data Interchange Format (LDIF) - Technical Specification
+RFC 2891 - LDAP Control Extension for Server Side Sorting of Search Results
+RFC 3296 - Named Subordinate References in LDAP Directories
+
+Expired but used Draft:
+ldapext-ldapv3-vlv-04: LDAP Extensions for Scrolling View Browsing of Search Results
+
+RFC 4510 - Lightweight Directory Access Protocol (LDAP): Technical Specification Road Map
+RFC 4511 - Lightweight Directory Access Protocol (LDAP): The Protocol
+RFC 4512 - Lightweight Directory Access Protocol (LDAP): Directory Information Models
+RFC 4513 - Lightweight Directory Access Protocol (LDAP): Authentication Methods and Security Mechanisms
+RFC 4514 - Lightweight Directory Access Protocol (LDAP): String Representation of Distinguished Names
+RFC 4515 - Lightweight Directory Access Protocol (LDAP): String Representation of Search Filters
+RFC 4516 - Lightweight Directory Access Protocol (LDAP): Uniform Resource Locator
+RFC 4517 - Lightweight Directory Access Protocol (LDAP): Syntaxes and Matching Rules
+RFC 4518 - Lightweight Directory Access Protocol (LDAP): Internationalized String Preparation
+RFC 4519 - Lightweight Directory Access Protocol (LDAP) Schema for User Applications
+RFC 4520 - Internet Assigned Numbers Authority (IANA) Considerations for the Lightweight Directory Access Protocol (LDAP)
+RFC 4521 - Considerations for Lightweight Directory Access Protocol (LDAP) Extensions
+RFC 4522 - Lightweight Directory Access Protocol (LDAP): The Binary Encoding Option
+RFC 4523 - Lightweight Directory Access Protocol (LDAP) Schema Definitions for X.509 Certificates
+RFC 4524 - COSINE LDAP/X.500 Schema
+RFC 4525 - Lightweight Directory Access Protocol (LDAP) Modify-Increment Extension
+RFC 4526 - Lightweight Directory Access Protocol (LDAP) Absolute True and False Filters
+RFC 4527 - Lightweight Directory Access Protocol (LDAP) Read Entry Controls
+RFC 4528 - Lightweight Directory Access Protocol (LDAP) Assertion Control
+RFC 4529 - Requesting Attributes by Object Class in the Lightweight Directory Access Protocol (LDAP)
+RFC 4530 - Lightweight Directory Access Protocol (LDAP) entryUUID Operational Attribute
+RFC 4531 - Lightweight Directory Access Protocol (LDAP) Turn Operation
+RFC 4532 - Lightweight Directory Access Protocol (LDAP) "Who am I?" Operation
+RFC 4533 - Lightweight Directory Access Protocol (LDAP) Content Synchronization Operation
diff --git a/source4/ldap_server/devdocs/draft-armijo-ldap-syntax-00.txt b/source4/ldap_server/devdocs/draft-armijo-ldap-syntax-00.txt
new file mode 100644
index 0000000000..3cd0aebba9
--- /dev/null
+++ b/source4/ldap_server/devdocs/draft-armijo-ldap-syntax-00.txt
@@ -0,0 +1,137 @@
+INTERNET-DRAFT Michael P. Armijo
+Status: Informational Microsoft Corporation
+January 1999
+Expires July 1999
+
+
+ Active Directory Syntaxes
+ draft-armijo-ldap-syntax-00.txt
+
+
+1. Status of this Memo
+
+
+This memo provides information for the Internet community. It does not specify
+an Internet standard of any kind. Distribution of this memo is unlimited.
+
+This document is an Internet-Draft. Internet-Drafts are working documents of the
+Internet Engineering Task Force (IETF), its areas, and its working groups. Note
+that other groups may also distribute working documents as Internet-Drafts.
+
+Internet-Drafts are draft documents valid for a maximum of six months and may be
+updated, replaced, or obsoleted by other documents at any time. It is
+inappropriate to use Internet- Drafts as reference material or to cite them
+other than as "work in progress."
+
+To view the entire list of current Internet-Drafts, please check the "1id-
+abstracts.txt" listing contained in the Internet-Drafts Shadow Directories on
+ftp.is.co.za (Africa), ftp.nordu.net (Northern Europe), ftp.nis.garr.it
+(Southern Europe), munnari.oz.au (Pacific Rim), ftp.ietf.org (US East Coast), or
+ftp.isi.edu (US West Coast).
+
+2. Abstract
+
+The purpose of this document is to inform the Internet community of LDAP
+syntaxes available in the Windows NT Active Directory. These syntaxes provide
+additional functionality to the Active Directory.
+
+
+3. RFC Key Words
+
+The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD",
+"SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be
+interpreted as described in RFC 2119.
+
+
+4. LDAP Syntaxes
+
+CaseIgnoreString: 1.2.840.113556.1.4.905
+ Encoded as a Printable String (OID 1.3.6.1.4.1.1466.115.121.1.44)
+
+
+OR-Name: 1.2.840.113556.1.4.1221
+ Encoded as:
+ ORName = DN | "X400:" ORaddress "#X500:" DN | "X400:"ORaddress
+ DN = normally encoded rfc 1779 name
+ ORaddress = some string encoding for OR addresses.
+
+Note that an unescaped # character must not be legal in this encoding.
+This is necessary to be able to identify where the #X500 starts if the
+middle choice of the encoding is chosen.
+
+
+DNWithOctetString: 1.2.840.113556.1.4.903
+ Encoded as a :
+ DNWithOctetString = OctetTag ':' Count ':' OctetString ':' DN
+ OctetTag = 'B' | 'b'
+ Count = positive decimal number, counting number of encoded characters
+ in OctetString
+ OctetString = [EncodedByte]* // Note: the number of characters in the
+ string encoding of the OctetString is Count.
+ EncodedByte = [0-9 | a-f | A-F] [0-9 | a-f | A-F]
+ DN = <normal string encoding of a DN>
+
+ As an example, the string encoding of the combination of 0x74 0x65 0x73
+ 0x74 and DC=Microsoft,DC=Com is
+
+ B:8:74657374:DC=Microsoft,DC=Com
+
+
+DNWithString: 1.2.840.113556.1.4.904
+ Encoded as a :
+ DNWithString = StringTag ':' Count ':' String ':' DN
+ OctetTag = 'S' | 's'
+ Count = positive decimal number, counting number of bytes in String
+ String = <normally encoded (i.e. UTF8 for V3) string> // Note: the number
+ of bytes in the string encoding of the String is Count.
+
+ DN = <normal string encoding of a DN>
+
+ As an example, the string encoding of the combination of "test" and
+ DC=Microsoft,DC=Com is
+
+ B:4:test:DC=Microsoft,DC=Com
+
+ As an example, the string encoding of the combination of XYZ (where X, Y,
+ and Z all have two byte UTF-8 encodings) and DC=Microsoft,DC=Com is
+
+ B:6:XYZ:DC=Microsoft,DC=Com
+
+Note: Characters with multibyte UTF-8 encodings contribute more than one to the count
+
+
+Large-Integer: 1.2.840.113556.1.4.906
+ Encoded as an Integer (OID 1.3.6.1.4.1.1466.115.121.1.27), but guaranteed
+ to support 64 bit numbers.
+
+
+Object-Security-Descriptor: 1.2.840.113556.1.4.907
+ Encoded as an Octet-String (OID 1.3.6.1.4.1.1466.115.121.1.40)
+
+
+5. References
+
+[RFC 2251]
+ M. Wahl, T. Howes, S. Kille, "Lightweight Directory Access Protocol
+ (v3)", RFC 2251, December 1997. 1997.
+
+[RFC 2119]
+ Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels,"
+ RFC 2119, Harvard University, March 1997.
+
+
+6. Authors Address
+
+ Michael P. Armijo
+ One Microsoft Way
+ Redmond, WA
+ 98052
+ USA
+
+ (425)882-8080
+ micharm@microsoft.com
+
+
+
+
+
diff --git a/source4/ldap_server/devdocs/ldapext-ldapv3-vlv-04.txt b/source4/ldap_server/devdocs/ldapext-ldapv3-vlv-04.txt
new file mode 100644
index 0000000000..e7bb99ef8a
--- /dev/null
+++ b/source4/ldap_server/devdocs/ldapext-ldapv3-vlv-04.txt
@@ -0,0 +1,655 @@
+
+INTERNET-DRAFT David Boreham, Netscape
+ Jim Sermersheim, Novell
+ Anoop Anantha, Microsoft
+ Michael Armijo, Microsoft
+ldapext Working Group 6 April, 2000
+
+
+ LDAP Extensions for Scrolling View Browsing of Search Results
+
+ draft-ietf-ldapext-ldapv3-vlv-04.txt
+ This document expires on 5 October 2000
+
+1. Status of this Memo
+
+This document is an Internet-Draft and is in full conformance with all
+provisions of Section 10 of RFC2026. Internet-Drafts are working docu-
+ments of the Internet Engineering Task Force (IETF), its areas, and its
+working groups. Note that other groups may also distribute working
+documents as Internet-Drafts.
+
+Internet-Drafts are draft documents valid for a maximum of six months
+and may be updated, replaced, or obsoleted by other documents at any
+time. It is inappropriate to use Internet- Drafts as reference material
+or to cite them other than as "work in progress."
+
+The list of current Internet-Drafts can be accessed at
+http://www.ietf.org/ietf/1id-abstracts.txt
+
+The list of Internet-Draft Shadow Directories can be accessed at
+http://www.ietf.org/shadow.html.
+
+2. Abstract
+
+This document describes a Virtual List View control extension for the
+LDAP Search operation. This control is designed to allow the "virtual
+list box" feature, common in existing commercial e-mail address book
+applications, to be supported efficiently by LDAP servers. LDAP servers'
+inability to support this client feature is a significant impediment to
+LDAP replacing proprietary protocols in commercial e-mail systems.
+
+The control allows a client to specify that the server return, for a
+given LDAP search with associated sort keys, a contiguous subset of the
+search result set. This subset is specified in terms of offsets into the
+ordered list, or in terms of a greater than or equal comparison value.
+
+3. Background
+
+A Virtual List is a graphical user interface technique employed where
+
+
+
+Boreham et al [Page 1]
+
+
+
+
+
+RFC DRAFT April 2000
+
+
+ordered lists containing a large number of entries need to be displayed.
+A window containing a small number of visible list entries is drawn. The
+visible portion of the list may be relocated to different points within
+the list by means of user input. This input can be to a scroll bar
+slider; from cursor keys; from page up/down keys; from alphanumeric keys
+for "typedown". The user is given the impression that they may browse
+the complete list at will, even though it may contain millions of
+entries. It is the fact that the complete list contents are never
+required at any one time that characterizes Virtual List View. Rather
+than fetch the complete list from wherever it is stored (typically from
+disk or a remote server), only that information which is required to
+display the part of the list currently in view is fetched. The subject
+of this document is the interaction between client and server required
+to implement this functionality in the context of the results from a
+sorted LDAP search request.
+
+For example, suppose an e-mail address book application displays a list
+view onto the list containing the names of all the holders of e-mail
+accounts at a large university. The list is sorted alphabetically.
+While there may be tens of thousands of entries in this list, the
+address book list view displays only 20 such accounts at any one time.
+The list has an accompanying scroll bar and text input window for type-
+down. When first displayed, the list view shows the first 20 entries in
+the list, and the scroll bar slider is positioned at the top of its
+range. Should the user drag the slider to the bottom of its range, the
+displayed contents of the list view should be updated to show the last
+20 entries in the list. Similarly, if the slider is positioned somewhere
+in the middle of its travel, the displayed contents of the list view
+should be updated to contain the 20 entries located at that relative
+position within the complete list. Starting from any display point, if
+the user uses the cursor keys or clicks on the scroll bar to request
+that the list be scrolled up or down by one entry, the displayed con-
+tents should be updated to reflect this. Similarly the list should be
+displayed correctly when the user requests a page scroll up or down.
+Finally, when the user types characters in the type-down window, the
+displayed contents of the list should "jump" or "seek" to the appropri-
+ate point within the list. For example, if the user types "B", the
+displayed list could center around the first user with a name beginning
+with the letter "B". When this happens, the scroll bar slider should
+also be updated to reflect the new relative location within the list.
+
+This document defines a request control which extends the LDAP search
+operation. Always used in conjunction with the server side sorting
+control[SSS], this allows a client to retrieve selected portions of
+large search result set in a fashion suitable for the implementation of
+a virtual list view.
+
+The key words "MUST", "SHOULD", and "MAY" used in this document are to
+
+
+
+Boreham et al [Page 2]
+
+
+
+
+
+RFC DRAFT April 2000
+
+
+be interpreted as described in [Bradner97].
+
+4. Client-Server Interaction
+
+The Virtual List View control extends a regular LDAP Search operation
+which must also include a server-side sorting control[SSS]. Rather than
+returning the complete set of appropriate SearchResultEntry messages,
+the server is instructed to return a contiguous subset of those entries,
+taken from the sorted result set, centered around a particular target
+entry. Henceforth, in the interests of brevity, the sorted search result
+set will be referred to as "the list".
+
+The sort control MAY contain any sort specification valid for the
+server. The attributeType field in the first SortKeyList sequence ele-
+ment has special significance for "typedown".
+
+The desired target entry, and the number of entries to be returned both
+before, and after, that target entry in the list, are determined by the
+client's VirtualListViewRequest control.
+
+When the server returns the set of entries to the client, it attaches a
+VirtualListViewResponse control to the SearchResultDone message. The
+server returns in this control: its current estimate for the list con-
+tent count, the location within the list corresponding to the target
+entry, and any error codes.
+
+The target entry is specified in the VirtualListViewRequest control by
+one of two methods. The first method is for the client to indicate the
+target entry's offset within the list. The second way is for the client
+to supply an attribute assertion value. The value is compared against
+the values of the attribute specified as the primary sort key in the
+sort control attached to the search operation. The first sort key in
+the SortKeyList is the primary sort key. The target entry is the first
+entry in the list with value greater than or equal to (in the primary
+sort order), the presented value. The order is determined by rules
+defined in [SSS]. Selection of the target entry by this means is
+designed to implement "typedown". Note that it is possible that no
+entry satisfies these conditions, in which case there is no target
+entry. This condition is indicated by the server returning the special
+value contentCount + 1 in the target position field.
+
+Because the server may not have an accurate estimate of the number of
+entries in the list, and to take account of cases where the list size is
+changing during the time the user browses the list, and because the
+client needs a way to indicate specific list targets "beginning" and
+"end", offsets within the list are transmitted between client and server
+as ratios---offset to content count. The server sends its latest esti-
+mate as to the number of entries in the list (content count) to the
+
+
+
+Boreham et al [Page 3]
+
+
+
+
+
+RFC DRAFT April 2000
+
+
+client in every response control. The client sends its assumed value
+for the content count in every request control. The server examines the
+content count and offsets presented by the client and computes the
+corresponding offsets within the list, based on its own idea of the con-
+tent count.
+
+ Si = Sc * (Ci / Cc)
+
+ Where:
+ Si is the actual list offset used by the server
+ Sc is the server's estimate for content count
+ Ci is the client's submitted offset
+ Cc is the client's submitted content count
+ The result is rounded to the nearest integer.
+
+If the content count is stable, and the client returns to the server the
+content count most recently received, Cc = Sc and the offsets transmit-
+ted become the actual server list offsets.
+
+The following special cases are allowed: a client sending a content
+count of zero (Cc = 0) means "client has no idea what the content count
+is, server MUST use its own content count estimate in place of the
+client's". An offset value of one (Ci = 1) always means that the target
+is the first entry in the list. Client specifying an offset which equals
+the content count specified in the same request control (Ci = Cc) means
+that the target is the last entry in the list. Ci may only equal zero
+when Cc is also zero. This signifies the last entry in the list.
+
+Because the server always returns contentCount and targetPosition, the
+client can always determine which of the returned entries is the target
+entry. Where the number of entries returned is the same as the number
+requested, the client is able to identify the target by simple arith-
+metic. Where the number of entries returned is not the same as the
+number requested (because the requested range crosses the beginning or
+end of the list, or both), the client must use the target position and
+content count values returned by the server to identify the target
+entry. For example, suppose that 10 entries before and 10 after the tar-
+get were requested, but the server returns 13 entries, a content count
+of 100 and a target position of 3. The client can determine that the
+first entry must be entry number 1 in the list, therefore the 13 entries
+returned are the first 13 entries in the list, and the target is the
+third one.
+
+A server-generated context identifier MAY be returned to clients. A
+client receiving a context identifier SHOULD return it unchanged in a
+subsequent request which relates to the same list. The purpose of this
+interaction is to enhance the performance and effectiveness of servers
+which employ approximate positioning.
+
+
+
+Boreham et al [Page 4]
+
+
+
+
+
+RFC DRAFT April 2000
+
+
+5. The Controls
+
+Support for the virtual list view control extension is indicated by the
+presence of the OID "2.16.840.1.113730.3.4.9" in the supportedControl
+attribute of a server's root DSE.
+
+5.1. Request Control
+
+This control is included in the SearchRequest message as part of the
+controls field of the LDAPMessage, as defined in Section 4.1.12 of
+[LDAPv3]. The controlType is set to "2.16.840.1.113730.3.4.9". The cri-
+ticality SHOULD be set to TRUE. If this control is included in a Sear-
+chRequest message, a Server Side Sorting request control [SSS] MUST also
+be present in the message. The controlValue is an OCTET STRING whose
+value is the BER-encoding of the following SEQUENCE:
+
+ VirtualListViewRequest ::= SEQUENCE {
+ beforeCount INTEGER (0..maxInt),
+ afterCount INTEGER (0..maxInt),
+ CHOICE {
+ byoffset [0] SEQUENCE {
+ offset INTEGER (0 .. maxInt),
+ contentCount INTEGER (0 .. maxInt) },
+ greaterThanOrEqual [1] AssertionValue },
+ contextID OCTET STRING OPTIONAL }
+
+beforeCount indicates how many entries before the target entry the
+client wants the server to send. afterCount indicates the number of
+entries after the target entry the client wants the server to send.
+offset and contentCount identify the target entry as detailed in section
+4. greaterThanOrEqual is an attribute assertion value defined in
+[LDAPv3]. If present, the value supplied in greaterThanOrEqual is used
+to determine the target entry by comparison with the values of the
+attribute specified as the primary sort key. The first list entry who's
+value is no less than (less than or equal to when the sort order is
+reversed) the supplied value is the target entry. If present, the con-
+textID field contains the value of the most recently received contextID
+field from a VirtualListViewResponse control. The type AssertionValue
+and value maxInt are defined in [LDAPv3]. contextID values have no
+validity outwith the connection on which they were received. That is, a
+client should not submit a contextID which it received from another con-
+nection, a connection now closed, or a different server.
+
+
+5.2. Response Control
+
+This control is included in the SearchResultDone message as part of the
+controls field of the LDAPMessage, as defined in Section 4.1.12 of
+
+
+
+Boreham et al [Page 5]
+
+
+
+
+
+RFC DRAFT April 2000
+
+
+[LDAPv3].
+
+The controlType is set to "2.16.840.1.113730.3.4.10". The criticality is
+FALSE (MAY be absent). The controlValue is an OCTET STRING, whose value
+is the BER encoding of a value of the following SEQUENCE:
+
+ VirtualListViewResponse ::= SEQUENCE {
+ targetPosition INTEGER (0 .. maxInt),
+ contentCount INTEGER (0 .. maxInt),
+ virtualListViewResult ENUMERATED {
+ success (0),
+ operationsError (1),
+ unwillingToPerform (53),
+ insufficientAccessRights (50),
+ busy (51),
+ timeLimitExceeded (3),
+ adminLimitExceeded (11),
+ sortControlMissing (60),
+ offsetRangeError (61),
+ other (80) },
+ contextID OCTET STRING OPTIONAL }
+
+targetPosition gives the list offset for the target entry. contentCount
+gives the server's estimate of the current number of entries in the
+list. Together these give sufficient information for the client to
+update a list box slider position to match the newly retrieved entries
+and identify the target entry. The contentCount value returned SHOULD be
+used in a subsequent VirtualListViewRequest control. contextID is a
+server-defined octet string. If present, the contents of the contextID
+field SHOULD be returned to the server by a client in a subsequent Vir-
+tualListViewRequest control.
+
+The virtualListViewResult codes which are common to the LDAP sear-
+chResponse (adminLimitExceeded, timeLimitExceeded, busy, operationsEr-
+ror, unwillingToPerform, insufficientAccessRights) have the same mean-
+ings as defined in [LDAPv3], but they pertain specifically to the VLV
+operation. For example, the server could exceed an administration limit
+processing a SearchRequest with a VirtualListViewRequest control. How-
+ever, the same administration limit would not be exceeded should the
+same SearchRequest be submitted by the client without the VirtualList-
+ViewRequest control. In this case, the client can determine that an
+administration limit has been exceeded in servicing the VLV request, and
+can if it chooses resubmit the SearchRequest without the VirtualList-
+ViewRequest control.
+
+insufficientAccessRights means that the server denied the client permis-
+sion to perform the VLV operation.
+
+
+
+
+Boreham et al [Page 6]
+
+
+
+
+
+RFC DRAFT April 2000
+
+
+If the server determines that the results of the search presented exceed
+the range provided by the 32-bit offset values, it MUST return
+offsetRangeError.
+
+6. Protocol Example
+
+Here we walk through the client-server interaction for a specific vir-
+tual list view example: The task is to display a list of all 78564 peo-
+ple in the US company "Ace Industry". This will be done by creating a
+graphical user interface object to display the list contents, and by
+repeatedly sending different versions of the same virtual list view
+search request to the server. The list view displays 20 entries on the
+screen at a time.
+
+We form a search with baseDN "o=Ace Industry, c=us"; search scope sub-
+tree; filter "objectClass=inetOrgPerson". We attach a server sort order
+control to the search, specifying ascending sort on attribute "cn". To
+this base search, we attach a virtual list view request control with
+contents determined by the user activity and send the search to the
+server. We display the results from each search in the list window and
+update the slider position.
+
+When the list view is first displayed, we want to initialize the con-
+tents showing the beginning of the list. Therefore, we set beforeCount =
+0, afterCount = 19, contentCount = 0, offset = 1 and send the request to
+the server. The server duly returns the first 20 entries in the list,
+plus the content count = 78564 and targetPosition = 1. We therefore
+leave the scroll bar slider at its current location (the top of its
+range).
+
+Say that next the user drags the scroll bar slider down to the bottom of
+its range. We now wish to display the last 20 entries in the list, so
+we set beforeCount = 19, afterCount = 0, contentCount = 78564, offset =
+78564 and send the request to the server. The server returns the last 20
+entries in the list, plus the content count = 78564 and targetPosition =
+78564.
+
+Next the user presses a page up key. Our page size is 20, so we set
+beforeCount = 0, afterCount = 19, contentCount = 78564, offset =
+78564-19-20 and send the request to the server. The server returns the
+preceding 20 entries in the list, plus the content count = 78564 and
+targetPosition = 78525.
+
+Now the user grabs the scroll bar slider and drags it to 68% of the way
+down its travel. 68% of 78564 is 53424 so we set beforeCount = 9, after-
+Count = 10, contentCount = 78564, offset = 53424 and send the request to
+the server. The server returns the preceding 20 entries in the list,
+plus the content count = 78564 and targetPosition = 53424.
+
+
+
+Boreham et al [Page 7]
+
+
+
+
+
+RFC DRAFT April 2000
+
+
+Lastly, the user types the letter "B". We set beforeCount = 9, after-
+Count = 10 and greaterThanOrEqual = "B". The server finds the first
+entry in the list not less than "B", let's say "Babs Jensen", and
+returns the nine preceding entries, the target entry, and the proceeding
+10 entries. The server returns content count = 78564 and targetPosition
+= 5234 and so the client updates its scroll bar slider to 6.7% of full
+scale.
+
+7. Notes for Implementers
+
+While the feature is expected to be generally useful for arbitrary
+search and sort specifications, it is specifically designed for those
+cases where the result set is very large. The intention is that this
+feature be implemented efficiently by means of pre-computed indices per-
+taining to a set of specific cases. For example, an offset relating to
+"all the employees in the local organization, sorted by surname" would
+be a common case.
+
+The intention for client software is that the feature should fit easily
+with the host platform's graphical user interface facilities for the
+display of scrolling lists. Thus the task of the client implementers
+should be one of reformatting up the requests for information received
+from the list view code to match the format of the virtual list view
+request and response controls.
+
+Client implementers should note that any offset value returned by the
+server may be approximate. Do not design clients > which only operate
+correctly when offsets are exact.
+
+Server implementers using indexing technology which features approximate
+positioning should consider returning context identifiers to clients.
+The use of a context identifier will allow the server to distinguish
+between client requests which relate to different displayed lists on the
+client. Consequently the server can decide more intelligently whether to
+reposition an existing database cursor accurately to within a short dis-
+tance of its current position, or to reposition to an approximate posi-
+tion. Thus the client will see precise offsets for "short" repositioning
+(e.g. paging up or down), but approximate offsets for a "long" reposi-
+tion (e.g. a slider movement).
+
+Server implementers are free to return status code unwillingToPerform
+should their server be unable to service any particular VLV search.
+This might be because the resolution of the search is computationally
+infeasible, or because excessive server resources would be required to
+service the search.
+
+Client implementers should note that this control is only defined on a
+client interaction with a single server. If a server returns referrals
+
+
+
+Boreham et al [Page 8]
+
+
+
+
+
+RFC DRAFT April 2000
+
+
+as a part of its response to the search request, the client is responsi-
+ble for deciding when and how to apply this control to the referred-to
+servers, and how to collate the results from multiple servers.
+
+
+8. Relationship to "Simple Paged Results"
+
+These controls are designed to support the virtual list view, which has
+proved hard to implement with the Simple Paged Results mechanism
+[SPaged]. However, the controls described here support any operation
+possible with the Simple Paged Results mechanism. The two mechanisms are
+not complementary, rather one has a superset of the other's features.
+One area where the mechanism presented here is not a strict superset of
+the Simple Paged Results scheme is that here we require a sort order to
+be specified. No such requirement is made for paged results.
+
+
+9. Security Considerations
+
+Server implementers may wish to consider whether clients are able to
+consume excessive server resources in requesting virtual list opera-
+tions. Access control to the feature itself; configuration options lim-
+iting the feature's use to certain predetermined search base DNs and
+filters; throttling mechanisms designed to limit the ability for one
+client to soak up server resources, may be appropriate.
+
+Consideration should be given as to whether a client will be able to
+retrieve the complete contents, or a significant subset of the complete
+contents of the directory using this feature. This may be undesirable in
+some circumstances and consequently it may be necessary to enforce some
+access control.
+
+Clients can, using this control, determine how many entries are con-
+tained within a portion of the DIT. This may constitute a security
+hazard. Again, access controls may be appropriate.
+
+Server implementers SHOULD exercise caution concerning the content of
+the contextID. Should the contextID contain internal server state, it
+may be possible for a malicious client to use that information to gain
+unauthorized access to information.
+
+10. Acknowledgements
+
+Chris Weider of Microsoft co-authored a previous version of this docu-
+ment.
+
+
+
+
+
+
+Boreham et al [Page 9]
+
+
+
+
+
+RFC DRAFT April 2000
+
+
+11. References
+
+[LDAPv3]
+ Wahl, M, S. Kille and T. Howes, "Lightweight Directory Access Pro-
+ tocol (v3)", Internet Standard, December, 1997. RFC2251.
+
+[SPaged]
+ Weider, C, A. Herron, A. Anantha, and T. Howes, "LDAP Control
+ Extension for Simple Paged Results Manipulation", September
+ 1999. RFC2696
+
+[SSS]Wahl, M, A. Herron and T. Howes, "LDAP Control Extension for Server
+ Side Sorting of Search Results", Internet Draft, April, 1999.
+ Available as draft-ietf-asid-ldapv3-sorting-02.txt.
+
+[Bradner97]
+ Bradner, S., "Key Words for use in RFCs to Indicate Requirement
+ Levels", BCP 14, RFC 2119, March 1997.
+
+12. Authors' Addresses
+
+ David Boreham
+ iPlanet e-commerce solutions
+ 501 E. Middlefield Road
+ Mountain View, CA 94043, USA
+ +1 650 937-5206
+ dboreham@netscape.com
+
+ Jim Sermersheim
+ Novell
+ 122 East 1700 South
+ Provo, Utah 84606, USA
+ jimse@novell.com
+
+ Anoop Anantha
+ Microsoft Corp.
+ 1 Microsoft Way
+ Redmond, WA 98052, USA
+ +1 425 882-8080
+ anoopa@microsoft.com
+
+ Michael Armijo
+ Microsoft Corp.
+ 1 Microsoft Way
+ Redmond, WA 98052, USA
+ +1 425 882-8080
+ micharm@microsoft.com
+ This document expires on 5 October 2000
+
+
+
+Boreham et al [Page 10]
+
+
+
+
+
+RFC DRAFT April 2000
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Boreham et al [Page 11]
+
+
diff --git a/source4/ldap_server/devdocs/rfc2307.txt b/source4/ldap_server/devdocs/rfc2307.txt
new file mode 100644
index 0000000000..f46519f127
--- /dev/null
+++ b/source4/ldap_server/devdocs/rfc2307.txt
@@ -0,0 +1,1179 @@
+
+
+
+
+
+
+Network Working Group L. Howard
+Request for Comments: 2307 Independent Consultant
+Category: Experimental March 1998
+
+
+ An Approach for Using LDAP as a Network Information Service
+
+Status of this Memo
+
+ This memo defines an Experimental Protocol for the Internet
+ community. It does not specify an Internet standard of any kind.
+ Discussion and suggestions for improvement are requested.
+ Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (1998). All Rights Reserved.
+
+Abstract
+
+ This document describes an experimental mechanism for mapping
+ entities related to TCP/IP and the UNIX system into X.500 [X500]
+ entries so that they may be resolved with the Lightweight Directory
+ Access Protocol [RFC2251]. A set of attribute types and object
+ classes are proposed, along with specific guidelines for interpreting
+ them.
+
+ The intention is to assist the deployment of LDAP as an
+ organizational nameservice. No proposed solutions are intended as
+ standards for the Internet. Rather, it is hoped that a general
+ consensus will emerge as to the appropriate solution to such
+ problems, leading eventually to the adoption of standards. The
+ proposed mechanism has already been implemented with some success.
+
+1. Background and Motivation
+
+ The UNIX (R) operating system, and its derivatives (specifically,
+ those which support TCP/IP and conform to the X/Open Single UNIX
+ specification [XOPEN]) require a means of looking up entities, by
+ matching them against search criteria or by enumeration. (Other
+ operating systems that support TCP/IP may provide some means of
+ resolving some of these entities. This schema is applicable to those
+ environments also.)
+
+ These entities include users, groups, IP services (which map names to
+ IP ports and protocols, and vice versa), IP protocols (which map
+ names to IP protocol numbers and vice versa), RPCs (which map names
+ to ONC Remote Procedure Call [RFC1057] numbers and vice versa), NIS
+
+
+
+Howard Experimental [Page 1]
+
+RFC 2307 Using LDAP as a Network Information Service March 1998
+
+
+ netgroups, booting information (boot parameters and MAC address
+ mappings), filesystem mounts, IP hosts and networks, and RFC822 mail
+ aliases.
+
+ Resolution requests are made through a set of C functions, provided
+ in the UNIX system's C library. For example, the UNIX system utility
+ "ls", which enumerates the contents of a filesystem directory, uses
+ the C library function getpwuid() in order to map user IDs to login
+ names. Once the request is made, it is resolved using a "nameservice"
+ which is supported by the client library. The nameservice may be, at
+ its simplest, a collection of files in the local filesystem which are
+ opened and searched by the C library. Other common nameservices
+ include the Network Information Service (NIS) and the Domain Name
+ System (DNS). (The latter is typically used for resolving hosts,
+ services and networks.) Both these nameservices have the advantage of
+ being distributed and thus permitting a common set of entities to be
+ shared amongst many clients.
+
+ LDAP is a distributed, hierarchical directory service access protocol
+ which is used to access repositories of users and other network-
+ related entities. Because LDAP is often not tightly integrated with
+ the host operating system, information such as users may need to be
+ kept both in LDAP and in an operating system supported nameservice
+ such as NIS. By using LDAP as the the primary means of resolving
+ these entities, these redundancy issues are minimized and the
+ scalability of LDAP can be exploited. (By comparison, NIS services
+ based on flat files do not have the scalability or extensibility of
+ LDAP or X.500.)
+
+ The object classes and attributes defined below are suitable for
+ representing the aforementioned entities in a form compatible with
+ LDAP and X.500 directory services.
+
+2. General Issues
+
+2.1. Terminology
+
+ The key words "MUST", "SHOULD", and "MAY" used in this document are
+ to be interpreted as described in [RFC2119].
+
+ For the purposes of this document, the term "nameservice" refers to a
+ service, such as NIS or flat files, that is used by the operating
+ system to resolve entities within a single, local naming context.
+ Contrast this with a "directory service" such as LDAP, which supports
+ extensible schema and multiple naming contexts.
+
+
+
+
+
+
+Howard Experimental [Page 2]
+
+RFC 2307 Using LDAP as a Network Information Service March 1998
+
+
+ The term "NIS-related entities" broadly refers to entities which are
+ typically resolved using the Network Information Service. (NIS was
+ previously known as YP.) Deploying LDAP for resolving these entities
+ does not imply that NIS be used, as a gateway or otherwise. In
+ particular, the host and network classes are generically applicable,
+ and may be implemented on any system that wishes to use LDAP or X.500
+ for host and network resolution.
+
+ The "DUA" (directory user agent) refers to the LDAP client querying
+ these entities, such as an LDAP to NIS gateway or the C library. The
+ "client" refers to the application which ultimately makes use of the
+ information returned by the resolution. It is irrelevant whether the
+ DUA and the client reside within the same address space. The act of
+ the DUA making this information to the client is termed
+ "republishing".
+
+ To avoid confusion, the term "login name" refers to the user's login
+ name (being the value of the uid attribute) and the term "user ID"
+ refers to he user's integer identification number (being the value of
+ the uidNumber attribute).
+
+ The phrases "resolving an entity" and "resolution of entities" refer
+ respectively to enumerating NIS-related entities of a given type, and
+ matching them against a given search criterion. One or more entities
+ are returned as a result of successful "resolutions" (a "match"
+ operation will only return one entity).
+
+ The use of the term UNIX does not confer upon this schema the
+ endorsement of owners of the UNIX trademark. Where necessary, the
+ term "TCP/IP entity" is used to refer to protocols, services, hosts,
+ and networks, and the term "UNIX entity" to its complement. (The
+ former category does not mandate the host operating system supporting
+ the interfaces required for resolving UNIX entities.)
+
+ The OIDs defined below are derived from iso(1) org(3) dod(6)
+ internet(1) directory(1) nisSchema(1).
+
+2.2. Attributes
+
+ The attributes and classes defined in this document are summarized
+ below.
+
+ The following attributes are defined in this document:
+
+ uidNumber
+ gidNumber
+ gecos
+ homeDirectory
+
+
+
+Howard Experimental [Page 3]
+
+RFC 2307 Using LDAP as a Network Information Service March 1998
+
+
+ loginShell
+ shadowLastChange
+ shadowMin
+ shadowMax
+ shadowWarning
+ shadowInactive
+ shadowExpire
+ shadowFlag
+ memberUid
+ memberNisNetgroup
+ nisNetgroupTriple
+ ipServicePort
+ ipServiceProtocol
+ ipProtocolNumber
+ oncRpcNumber
+ ipHostNumber
+ ipNetworkNumber
+ ipNetmaskNumber
+ macAddress
+ bootParameter
+ bootFile
+ nisMapName
+ nisMapEntry
+
+ Additionally, some of the attributes defined in [RFC2256] are
+ required.
+
+2.3. Object classes
+
+ The following object classes are defined in this document:
+
+ posixAccount
+ shadowAccount
+ posixGroup
+ ipService
+ ipProtocol
+ oncRpc
+ ipHost
+ ipNetwork
+ nisNetgroup
+ nisMap
+ nisObject
+ ieee802Device
+ bootableDevice
+
+ Additionally, some of the classes defined in [RFC2256] are required.
+
+
+
+
+
+Howard Experimental [Page 4]
+
+RFC 2307 Using LDAP as a Network Information Service March 1998
+
+
+2.4. Syntax definitions
+
+ The following syntax definitions [RFC2252] are used by this schema.
+ The nisNetgroupTripleSyntax represents NIS netgroup triples:
+
+ ( nisSchema.0.0 NAME 'nisNetgroupTripleSyntax'
+ DESC 'NIS netgroup triple' )
+
+ Values in this syntax are represented by the following:
+
+ nisnetgrouptriple = "(" hostname "," username "," domainname ")"
+ hostname = "" / "-" / keystring
+ username = "" / "-" / keystring
+ domainname = "" / "-" / keystring
+
+ X.500 servers may use the following representation of the above
+ syntax:
+
+ nisNetgroupTripleSyntax ::= SEQUENCE {
+ hostname [0] IA5String OPTIONAL,
+ username [1] IA5String OPTIONAL,
+ domainname [2] IA5String OPTIONAL
+ }
+
+ The bootParameterSyntax syntax represents boot parameters:
+
+ ( nisSchema.0.1 NAME 'bootParameterSyntax'
+ DESC 'Boot parameter' )
+
+ where:
+
+ bootparameter = key "=" server ":" path
+ key = keystring
+ server = keystring
+ path = keystring
+
+ X.500 servers may use the following representation of the above
+ syntax:
+
+ bootParameterSyntax ::= SEQUENCE {
+ key IA5String,
+ server IA5String,
+ path IA5String
+ }
+
+ Values adhering to these syntaxes are encoded as strings by LDAP
+ servers.
+
+
+
+
+Howard Experimental [Page 5]
+
+RFC 2307 Using LDAP as a Network Information Service March 1998
+
+
+3. Attribute definitions
+
+ This section contains attribute definitions to be implemented by DUAs
+ supporting this schema.
+
+ ( nisSchema.1.0 NAME 'uidNumber'
+ DESC 'An integer uniquely identifying a user in an
+ administrative domain'
+ EQUALITY integerMatch SYNTAX 'INTEGER' SINGLE-VALUE )
+
+ ( nisSchema.1.1 NAME 'gidNumber'
+ DESC 'An integer uniquely identifying a group in an
+ administrative domain'
+ EQUALITY integerMatch SYNTAX 'INTEGER' SINGLE-VALUE )
+
+ ( nisSchema.1.2 NAME 'gecos'
+ DESC 'The GECOS field; the common name'
+ EQUALITY caseIgnoreIA5Match
+ SUBSTRINGS caseIgnoreIA5SubstringsMatch
+ SYNTAX 'IA5String' SINGLE-VALUE )
+
+ ( nisSchema.1.3 NAME 'homeDirectory'
+ DESC 'The absolute path to the home directory'
+ EQUALITY caseExactIA5Match
+ SYNTAX 'IA5String' SINGLE-VALUE )
+
+ ( nisSchema.1.4 NAME 'loginShell'
+ DESC 'The path to the login shell'
+ EQUALITY caseExactIA5Match
+ SYNTAX 'IA5String' SINGLE-VALUE )
+
+ ( nisSchema.1.5 NAME 'shadowLastChange'
+ EQUALITY integerMatch
+ SYNTAX 'INTEGER' SINGLE-VALUE )
+
+ ( nisSchema.1.6 NAME 'shadowMin'
+ EQUALITY integerMatch
+ SYNTAX 'INTEGER' SINGLE-VALUE )
+
+ ( nisSchema.1.7 NAME 'shadowMax'
+ EQUALITY integerMatch
+ SYNTAX 'INTEGER' SINGLE-VALUE )
+
+ ( nisSchema.1.8 NAME 'shadowWarning'
+ EQUALITY integerMatch
+ SYNTAX 'INTEGER' SINGLE-VALUE )
+
+ ( nisSchema.1.9 NAME 'shadowInactive'
+
+
+
+Howard Experimental [Page 6]
+
+RFC 2307 Using LDAP as a Network Information Service March 1998
+
+
+ EQUALITY integerMatch
+ SYNTAX 'INTEGER' SINGLE-VALUE )
+
+ ( nisSchema.1.10 NAME 'shadowExpire'
+ EQUALITY integerMatch
+ SYNTAX 'INTEGER' SINGLE-VALUE )
+
+ ( nisSchema.1.11 NAME 'shadowFlag'
+ EQUALITY integerMatch
+ SYNTAX 'INTEGER' SINGLE-VALUE )
+
+ ( nisSchema.1.12 NAME 'memberUid'
+ EQUALITY caseExactIA5Match
+ SUBSTRINGS caseExactIA5SubstringsMatch
+ SYNTAX 'IA5String' )
+
+ ( nisSchema.1.13 NAME 'memberNisNetgroup'
+ EQUALITY caseExactIA5Match
+ SUBSTRINGS caseExactIA5SubstringsMatch
+ SYNTAX 'IA5String' )
+
+ ( nisSchema.1.14 NAME 'nisNetgroupTriple'
+ DESC 'Netgroup triple'
+ SYNTAX 'nisNetgroupTripleSyntax' )
+
+ ( nisSchema.1.15 NAME 'ipServicePort'
+ EQUALITY integerMatch
+ SYNTAX 'INTEGER' SINGLE-VALUE )
+
+ ( nisSchema.1.16 NAME 'ipServiceProtocol'
+ SUP name )
+
+ ( nisSchema.1.17 NAME 'ipProtocolNumber'
+ EQUALITY integerMatch
+ SYNTAX 'INTEGER' SINGLE-VALUE )
+
+ ( nisSchema.1.18 NAME 'oncRpcNumber'
+ EQUALITY integerMatch
+ SYNTAX 'INTEGER' SINGLE-VALUE )
+
+ ( nisSchema.1.19 NAME 'ipHostNumber'
+ DESC 'IP address as a dotted decimal, eg. 192.168.1.1,
+ omitting leading zeros'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 'IA5String{128}' )
+
+ ( nisSchema.1.20 NAME 'ipNetworkNumber'
+ DESC 'IP network as a dotted decimal, eg. 192.168,
+
+
+
+Howard Experimental [Page 7]
+
+RFC 2307 Using LDAP as a Network Information Service March 1998
+
+
+ omitting leading zeros'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 'IA5String{128}' SINGLE-VALUE )
+
+ ( nisSchema.1.21 NAME 'ipNetmaskNumber'
+ DESC 'IP netmask as a dotted decimal, eg. 255.255.255.0,
+ omitting leading zeros'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 'IA5String{128}' SINGLE-VALUE )
+
+ ( nisSchema.1.22 NAME 'macAddress'
+ DESC 'MAC address in maximal, colon separated hex
+ notation, eg. 00:00:92:90:ee:e2'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 'IA5String{128}' )
+
+ ( nisSchema.1.23 NAME 'bootParameter'
+ DESC 'rpc.bootparamd parameter'
+ SYNTAX 'bootParameterSyntax' )
+
+ ( nisSchema.1.24 NAME 'bootFile'
+ DESC 'Boot image name'
+ EQUALITY caseExactIA5Match
+ SYNTAX 'IA5String' )
+
+ ( nisSchema.1.26 NAME 'nisMapName'
+ SUP name )
+
+ ( nisSchema.1.27 NAME 'nisMapEntry'
+ EQUALITY caseExactIA5Match
+ SUBSTRINGS caseExactIA5SubstringsMatch
+ SYNTAX 'IA5String{1024}' SINGLE-VALUE )
+
+4. Class definitions
+
+ This section contains class definitions to be implemented by DUAs
+ supporting the schema.
+
+ The rfc822MailGroup object class MAY be used to represent a mail
+ group for the purpose of alias expansion. Several alternative schemes
+ for mail routing and delivery using LDAP directories, which are
+ outside the scope of this document.
+
+ ( nisSchema.2.0 NAME 'posixAccount' SUP top AUXILIARY
+ DESC 'Abstraction of an account with POSIX attributes'
+ MUST ( cn $ uid $ uidNumber $ gidNumber $ homeDirectory )
+ MAY ( userPassword $ loginShell $ gecos $ description ) )
+
+
+
+
+Howard Experimental [Page 8]
+
+RFC 2307 Using LDAP as a Network Information Service March 1998
+
+
+ ( nisSchema.2.1 NAME 'shadowAccount' SUP top AUXILIARY
+ DESC 'Additional attributes for shadow passwords'
+ MUST uid
+ MAY ( userPassword $ shadowLastChange $ shadowMin
+ shadowMax $ shadowWarning $ shadowInactive $
+ shadowExpire $ shadowFlag $ description ) )
+
+ ( nisSchema.2.2 NAME 'posixGroup' SUP top STRUCTURAL
+ DESC 'Abstraction of a group of accounts'
+ MUST ( cn $ gidNumber )
+ MAY ( userPassword $ memberUid $ description ) )
+
+ ( nisSchema.2.3 NAME 'ipService' SUP top STRUCTURAL
+ DESC 'Abstraction an Internet Protocol service.
+ Maps an IP port and protocol (such as tcp or udp)
+ to one or more names; the distinguished value of
+ the cn attribute denotes the service's canonical
+ name'
+ MUST ( cn $ ipServicePort $ ipServiceProtocol )
+ MAY ( description ) )
+
+ ( nisSchema.2.4 NAME 'ipProtocol' SUP top STRUCTURAL
+ DESC 'Abstraction of an IP protocol. Maps a protocol number
+ to one or more names. The distinguished value of the cn
+ attribute denotes the protocol's canonical name'
+ MUST ( cn $ ipProtocolNumber $ description )
+ MAY description )
+
+ ( nisSchema.2.5 NAME 'oncRpc' SUP top STRUCTURAL
+ DESC 'Abstraction of an Open Network Computing (ONC)
+ [RFC1057] Remote Procedure Call (RPC) binding.
+ This class maps an ONC RPC number to a name.
+ The distinguished value of the cn attribute denotes
+ the RPC service's canonical name'
+ MUST ( cn $ oncRpcNumber $ description )
+ MAY description )
+
+ ( nisSchema.2.6 NAME 'ipHost' SUP top AUXILIARY
+
+ DESC 'Abstraction of a host, an IP device. The distinguished
+ value of the cn attribute denotes the host's canonical
+ name. Device SHOULD be used as a structural class'
+ MUST ( cn $ ipHostNumber )
+ MAY ( l $ description $ manager ) )
+
+ ( nisSchema.2.7 NAME 'ipNetwork' SUP top STRUCTURAL
+ DESC 'Abstraction of a network. The distinguished value of
+ the cn attribute denotes the network's canonical name'
+
+
+
+Howard Experimental [Page 9]
+
+RFC 2307 Using LDAP as a Network Information Service March 1998
+
+
+ MUST ( cn $ ipNetworkNumber )
+ MAY ( ipNetmaskNumber $ l $ description $ manager ) )
+
+ ( nisSchema.2.8 NAME 'nisNetgroup' SUP top STRUCTURAL
+ DESC 'Abstraction of a netgroup. May refer to other netgroups'
+ MUST cn
+ MAY ( nisNetgroupTriple $ memberNisNetgroup $ description ) )
+
+ ( nisSchema.2.09 NAME 'nisMap' SUP top STRUCTURAL
+ DESC 'A generic abstraction of a NIS map'
+ MUST nisMapName
+ MAY description )
+
+ ( nisSchema.2.10 NAME 'nisObject' SUP top STRUCTURAL
+ DESC 'An entry in a NIS map'
+ MUST ( cn $ nisMapEntry $ nisMapName )
+ MAY description )
+
+ ( nisSchema.2.11 NAME 'ieee802Device' SUP top AUXILIARY
+ DESC 'A device with a MAC address; device SHOULD be
+ used as a structural class'
+ MAY macAddress )
+
+ ( nisSchema.2.12 NAME 'bootableDevice' SUP top AUXILIARY
+ DESC 'A device with boot parameters; device SHOULD be
+ used as a structural class'
+ MAY ( bootFile $ bootParameter ) )
+
+5. Implementation details
+
+5.1. Suggested resolution methods
+
+ The preferred means of directing a client application (one using the
+ shared services of the C library) to use LDAP as its information
+ source for the functions listed in 5.2 is to modify the source code
+ to directly query LDAP. As the source to commercial C libraries and
+ applications is rarely available to the end-user, one could emulate a
+ supported nameservice (such as NIS). (This is also an appropriate
+ opportunity to perform caching of entries across process address
+ spaces.) In the case of NIS, reference implementations are widely
+ available and the RPC interface is well known.
+
+ The means by which the operating system is directed to use LDAP is
+ implementation dependent. For example, some operating systems and C
+ libraries support end-user extensible resolvers using dynamically
+ loadable libraries and a nameservice "switch". The means in which the
+ DUA locates LDAP servers is also implementation dependent.
+
+
+
+
+Howard Experimental [Page 10]
+
+RFC 2307 Using LDAP as a Network Information Service March 1998
+
+
+5.2. Affected library functions
+
+ The following functions are typically found in the C libraries of
+ most UNIX and POSIX compliant systems. An LDAP search filter
+ [RFC2254] which may be used to satisfy the function call is included
+ alongside each function name. Parameters are denoted by %s and %d for
+ string and integer arguments, respectively. Long lines are broken.
+
+ getpwnam() (&(objectClass=posixAccount)(uid=%s))
+ getpwuid() (&(objectClass=posixAccount)
+ (uidNumber=%d))
+ getpwent() (objectClass=posixAccount)
+
+ getspnam() (&(objectClass=shadowAccount)(uid=%s))
+ getspent() (objectClass=shadowAccount)
+
+ getgrnam() (&(objectClass=posixGroup)(cn=%s))
+ getgrgid() (&(objectClass=posixGroup)
+ (gidNumber=%d))
+ getgrent() (objectClass=posixGroup)
+
+ getservbyname() (&(objectClass=ipService)
+ (cn=%s)(ipServiceProtocol=%s))
+ getservbyport() (&(objectClass=ipService)
+ (ipServicePort=%d)
+ (ipServiceProtocol=%s))
+ getservent() (objectClass=ipService)
+
+ getrpcbyname() (&(objectClass=oncRpc)(cn=%s))
+ getrpcbynumber() (&(objectClass=oncRpc)(oncRpcNumber=%d))
+ getrpcent() (objectClass=oncRpc)
+
+ getprotobyname() (&(objectClass=ipProtocol)(cn=%s))
+ getprotobynumber() (&(objectClass=ipProtocol)
+ (ipProtocolNumber=%d))
+ getprotoent() (objectClass=ipProtocol)
+
+ gethostbyname() (&(objectClass=ipHost)(cn=%s))
+ gethostbyaddr() (&(objectClass=ipHost)(ipHostNumber=%s))
+ gethostent() (objectClass=ipHost)
+
+ getnetbyname() (&(objectClass=ipNetwork)(cn=%s))
+ getnetbyaddr() (&(objectClass=ipNetwork)
+ (ipNetworkNumber=%s))
+ getnetent() (objectClass=ipNetwork)
+
+ setnetgrent() (&(objectClass=nisNetgroup)(cn=%s))
+
+
+
+
+Howard Experimental [Page 11]
+
+RFC 2307 Using LDAP as a Network Information Service March 1998
+
+
+5.3. Interpreting user and group entries
+
+ User and group resolution is initiated by the functions prefixed by
+ getpw and getgr respectively. The uid attribute contains the user's
+ login name. The cn attribute, in posixGroup entries, contains the
+ group's name.
+
+ The account object class provides a convenient structural class for
+ posixAccount, and SHOULD be used where additional attributes are not
+ required.
+
+ It is suggested that uid and cn are used as the RDN attribute type
+ for posixAccount and posixGroup entries, respectively.
+
+ An account's GECOS field is preferably determined by a value of the
+ gecos attribute. If no gecos attribute exists, the value of the cn
+ attribute MUST be used. (The existence of the gecos attribute allows
+ information embedded in the GECOS field, such as a user's telephone
+ number, to be returned to the client without overloading the cn
+ attribute. It also accommodates directories where the common name
+ does not contain the user's full name.)
+
+ An entry of class posixAccount, posixGroup, or shadowAccount without
+ a userPassword attribute MUST NOT be used for authentication. The
+ client should be returned a non-matchable password such as "x".
+
+ userPassword values MUST be represented by following syntax:
+
+ passwordvalue = schemeprefix encryptedpassword
+ schemeprefix = "{" scheme "}"
+ scheme = "crypt" / "md5" / "sha" / altscheme
+ altscheme = "x-" keystring
+ encryptedpassword = encrypted password
+
+ The encrypted password contains of a plaintext key hashed using the
+ algorithm scheme.
+
+ userPassword values which do not adhere to this syntax MUST NOT be
+ used for authentication. The DUA MUST iterate through the values of
+ the attribute until a value matching the above syntax is found. Only
+ if encryptedpassword is an empty string does the user have no
+ password. DUAs are not required to consider encryption schemes which
+ the client will not recognize; in most cases, it may be sufficient to
+ consider only "crypt".
+
+ Below is an example of a userPassword attribute:
+
+ userPassword: {crypt}X5/DBrWPOQQaI
+
+
+
+Howard Experimental [Page 12]
+
+RFC 2307 Using LDAP as a Network Information Service March 1998
+
+
+ A future standard may specify LDAP v3 attribute descriptions to
+ represent hashed userPasswords, as noted below. This schema MUST NOT
+ be used with LDAP v2 DUAs and DSAs.
+
+ attributetype = attributename sep attributeoption
+ attributename = "userPassword"
+ sep = ";"
+ attributeoption = schemeclass "-" scheme
+ schemeclass = "hash" / altschemeclass
+ scheme = "crypt" / "md5" / "sha" / altscheme
+ altschemeclass = "x-" keystring
+ altscheme = keystring
+
+
+ Below is an example of a userPassword attribute, represented with an
+ LDAP v3 attribute description:
+
+ userPassword;hash-crypt: X5/DBrWPOQQaI
+
+
+ A DUA MAY utilise the attributes in the shadowAccount class to
+ provide shadow password service (getspnam() and getspent()). In such
+ cases, the DUA MUST NOT make use of the userPassword attribute for
+ getpwnam() et al, and MUST return a non-matchable password (such as
+ "x") to the client instead.
+
+5.4. Interpreting hosts and networks
+
+ The ipHostNumber and ipNetworkNumber attributes are defined in
+ preference to dNSRecord (defined in [RFC1279]), in order to simplify
+ the DUA's role in interpreting entries in the directory. A dNSRecord
+ expresses a complete resource record, including time to live and
+ class data, which is extraneous to this schema.
+
+ Additionally, the ipHost and ipNetwork classes permit a host or
+ network (respectively) and all its aliases to be represented by a
+ single entry in the directory. This is not necessarily possible if a
+ DNS resource record is mapped directly to an LDAP entry.
+ Implementations that wish to use LDAP to master DNS zone information
+ are not precluded from doing so, and may simply avoid the ipHost and
+ ipNetwork classes.
+
+ This document redefines, although not exclusively, the ipNetwork
+ class defined in [RFC1279], in order to achieve consistent naming
+ with ipHost. The ipNetworkNumber attribute is also used in the
+ siteContact object class [ROSE].
+
+
+
+
+
+Howard Experimental [Page 13]
+
+RFC 2307 Using LDAP as a Network Information Service March 1998
+
+
+ The trailing zeros in a network address MUST be omitted. CIDR-style
+ network addresses (eg. 192.168.1/24) MAY be used.
+
+ Hosts with IPv6 addresses MUST be written in their "preferred" form
+ as defined in section 2.2.1 of [RFC1884], such that all components of
+ the address are indicated and leading zeros are omitted. This
+ provides a consistent means of resolving ipHosts by address.
+
+5.5. Interpreting other entities
+
+ In general, a one-to-one mapping between entities and LDAP entries is
+ proposed, in that each entity has exactly one representation in the
+ DIT. In some cases this is not feasible; for example, a service which
+ is represented in more than one protocol domain. Consider the
+ following entry:
+
+ dn: cn=domain, dc=aja, dc=com
+ cn: domain
+ cn: nameserver
+ objectClass: top
+ objectClass: ipService
+ ipServicePort: 53
+ ipServiceProtocol: tcp
+ ipServiceProtocol: udp
+
+ This entry MUST map to the following two (2) services entities:
+
+ domain 53/tcp nameserver
+ domain 53/udp nameserver
+
+ While the above two entities may be represented as separate LDAP
+ entities, with different distinguished names (such as
+ cn=domain+ipServiceProtocol=tcp, ... and
+ cn=domain+ipServiceProtocol=udp, ...) it is convenient to represent
+ them as a single entry. (If a service is represented in multiple
+ protocol domains with different ports, then multiple entries are
+ required; multivalued RDNs may be used to distinguish them.)
+
+ With the exception of userPassword values, which are parsed according
+ to the syntax considered in section 5.2, any empty values (consisting
+ of a zero length string) are returned by the DUA to the client. The
+ DUA MUST reject any entries which do not conform to the schema
+ (missing mandatory attributes). Non-conforming entries SHOULD be
+ ignored while enumerating entries.
+
+ The nisObject object class MAY be used as a generic means of
+ representing NIS entities. Its use is not encouraged; where support
+ for entities not described in this schema is desired, an appropriate
+
+
+
+Howard Experimental [Page 14]
+
+RFC 2307 Using LDAP as a Network Information Service March 1998
+
+
+ schema should be devised. Implementors are strongly advised to
+ support end-user extensible mappings between NIS entities and object
+ classes. (Where the nisObject class is used, the nisMapName attribute
+ may be used as a RDN.)
+
+5.6. Canonicalizing entries with multi-valued naming attributes
+
+ For entities such as hosts, services, networks, protocols, and RPCs,
+ where there may be one or more aliases, the respective entry's
+ relative distinguished name SHOULD be used to determine the canonical
+ name. Any other values for the same attribute are used as aliases.
+ For example, the service described in section 5.5 has the canonical
+ name "domain" and exactly one alias, "nameserver".
+
+ The schema in this document generally only defines one attribute per
+ class which is suitable for distinguishing an entity (excluding any
+ attributes with integer syntax; it is assumed that entries will be
+ distinguished on name). Usually, this is the common name (cn)
+ attribute. This aids the DUA in determining the canonical name of an
+ entity, as it can examine the value of the relative distinguished
+ name. Aliases are thus any values of the distinguishing attribute
+ (such as cn) which do not match the canonical name of the entity.
+
+ In the event that a different attribute is used to distinguish the
+ entry, as may be the case where these object classes are used as
+ auxiliary classes, the entry's canonical name may not be present in
+ the RDN. In this case, the DUA MUST choose one of the non-
+ distinguished values to represent the entity's canonical name. As the
+ directory server guarantees no ordering of attribute values, it may
+ not be possible to distinguish an entry deterministically. This
+ ambiguity SHOULD NOT be resolved by mapping one directory entry into
+ multiple entities.
+
+6. Implementation focus
+
+ A NIS server which uses LDAP instead of local files has been
+ developed which supports the schema defined in this document.
+
+ A reference implementation of the C library resolution code has been
+ written for the Free Software Foundation. It may support other C
+ libraries which support the Name Service Switch (NSS) or the
+ Information Retrieval Service (IRS).
+
+ The author has made available a freely distributable set of scripts
+ which parses local databases such as /etc/passwd and /etc/hosts into
+ a form suitable for loading into an LDAP server.
+
+
+
+
+
+Howard Experimental [Page 15]
+
+RFC 2307 Using LDAP as a Network Information Service March 1998
+
+
+7. Security Considerations
+
+ The entirety of related security considerations are outside the scope
+ of this document. It is noted that making passwords encrypted with a
+ widely understood hash function (such as crypt()) available to non-
+ privileged users is dangerous because it exposes them to dictionary
+ and brute-force attacks. This is proposed only for compatibility
+ with existing UNIX system implementations. Sites where security is
+ critical SHOULD consider using a strong authentication service for
+ user authentication.
+
+ Alternatively, the encrypted password could be made available only to
+ a subset of privileged DUAs, which would provide "shadow" password
+ service to client applications. This may be difficult to enforce.
+
+ Because the schema represents operating system-level entities, access
+ to these entities SHOULD be granted on a discretionary basis. (There
+ is little point in restricting access to data which will be
+ republished without restriction, however.) It is particularly
+ important that only administrators can modify entries defined in this
+ schema, with the exception of allowing a principal to change their
+ password (which may be done on behalf of the user by a client bound
+ as a superior principal, such that password restrictions may be
+ enforced). For example, if a user were allowed to change the value of
+ their uidNumber attribute, they could subvert security by
+ equivalencing their account with the superuser account.
+
+ A subtree of the DIT which is to be republished by a DUA (such as a
+ NIS gateway) SHOULD be within the same administrative domain that the
+ republishing DUA represents. (For example, principals outside an
+ organization, while conceivably part of the DIT, should not be
+ considered with the same degree of authority as those within the
+ organization.)
+
+ Finally, care should be exercised with integer attributes of a
+ sensitive nature (particularly the uidNumber and gidNumber
+ attributes) which contain zero-length values. DUAs MAY treat such
+ values as corresponding to the "nobody" or "nogroup" user and group,
+ respectively.
+
+8. Acknowledgements
+
+ Thanks to Leif Hedstrom of Netscape Communications Corporation,
+ Michael Grant and Rosanna Lee of Sun Microsystems Inc., Ed Reed of
+ Novell Inc., and Mark Wahl of Critical Angle Inc. for their valuable
+ contributions to the development of this schema. Thanks to Andrew
+ Josey of The Open Group for clarifying the use of the UNIX trademark,
+ and to Tim Howes and Peter J. Cherny for their support.
+
+
+
+Howard Experimental [Page 16]
+
+RFC 2307 Using LDAP as a Network Information Service March 1998
+
+
+ UNIX is a registered trademark of The Open Group.
+
+9. References
+
+ [RFC1057]
+ Sun Microsystems, Inc., "RPC: Remote Procedure Call: Protocol
+ Specification Version 2", RFC 1057, June 1988.
+
+ [RFC1279]
+ Kille, S., "X.500 and Domains", RFC 1279, November 1991.
+
+ [RFC1884]
+ Hinden, R., and S. Deering, "IP Version 6 Addressing
+ Architecture", RFC 1884, December 1995.
+
+ [RFC2119]
+ Bradner, S., "Key Words for use in RFCs to Indicate Requirement
+ Levels", BCP 14, RFC 2119, March 1997.
+
+ [RFC2251]
+ Wahl, M., Howes, T., and S. Kille, "Lightweight Directory Access
+ Protocol (v3)", RFC 2251, December 1997.
+
+ [RFC2252]
+ Wahl, M., Coulbeck, A., Howes, T., and S. Kille, "Lightweight
+ Directory Access Protocol (v3): Attribute Syntax Definitions",
+ RFC 2252, December 1997.
+
+ [RFC2254]
+ Howes, T., "The String Representation of LDAP Search Filters",
+ RFC 2254, December 1997.
+
+ [RFC2256]
+ Wahl, M., "A Summary of the X.500(96) User Schema for use with
+ LDAPv3", RFC 2256, December 1997.
+
+ [ROSE]
+ M. T. Rose, "The Little Black Book: Mail Bonding with OSI
+ Directory Services", ISBN 0-13-683210-5, Prentice-Hall, Inc.,
+ 1992.
+
+ [X500]
+ "Information Processing Systems - Open Systems Interconnection -
+ The Directory: Overview of Concepts, Models and Service",
+ ISO/IEC JTC 1/SC21, International Standard 9594-1, 1988.
+
+
+
+
+
+
+Howard Experimental [Page 17]
+
+RFC 2307 Using LDAP as a Network Information Service March 1998
+
+
+ [XOPEN]
+ ISO/IEC 9945-1:1990, Information Technology - Portable Operating
+ Systems Interface (POSIX) - Part 1: Systems Application
+ Programming Interface (API) [C Language]
+
+10. Author's Address
+
+ Luke Howard
+ PO Box 59
+ Central Park Vic 3145
+ Australia
+
+ EMail: lukeh@xedoc.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Howard Experimental [Page 18]
+
+RFC 2307 Using LDAP as a Network Information Service March 1998
+
+
+A. Example entries
+
+ The examples described in this section are provided to illustrate the
+ schema described in this memo. They are not meant to be exhaustive.
+
+ The following entry is an example of the posixAccount class:
+
+ dn: uid=lester, dc=aja, dc=com
+ objectClass: top
+ objectClass: account
+ objectClass: posixAccount
+ uid: lester
+ cn: Lester the Nightfly
+ userPassword: {crypt}X5/DBrWPOQQaI
+ gecos: Lester
+ loginShell: /bin/csh
+ uidNumber: 10
+ gidNumber: 10
+ homeDirectory: /home/lester
+
+
+ This corresponds the UNIX system password file entry:
+
+ lester:X5/DBrWPOQQaI:10:10:Lester:/home/lester:/bin/sh
+
+ The following entry is an example of the ipHost class:
+
+ dn: cn=peg.aja.com, dc=aja, dc=com
+ objectClass: top
+ objectClass: device
+ objectClass: ipHost
+ objectClass: bootableDevice
+ objectClass: ieee802Device
+ cn: peg.aja.com
+ cn: www.aja.com
+ ipHostNumber: 10.0.0.1
+ macAddress: 00:00:92:90:ee:e2
+ bootFile: mach
+ bootParameter: root=fs:/nfsroot/peg
+ bootParameter: swap=fs:/nfsswap/peg
+ bootParameter: dump=fs:/nfsdump/peg
+
+ This entry represents the host canonically peg.aja.com, also known as
+ www.aja.com. The Ethernet address and four boot parameters are also
+ specified.
+
+
+
+
+
+
+Howard Experimental [Page 19]
+
+RFC 2307 Using LDAP as a Network Information Service March 1998
+
+
+ An example of the nisNetgroup class:
+
+ dn: cn=nightfly, dc=aja, dc=com
+ objectClass: top
+ objectClass: nisNetgroup
+ cn: nightfly
+ nisNetgroupTriple: (charlemagne,peg,dunes.aja.com)
+ nisNetgroupTriple: (lester,-,)
+ memberNisNetgroup: kamakiriad
+
+ This entry represents the netgroup nightfly, which contains two
+ triples (the user charlemagne, the host peg, and the domain
+ dunes.aja.com; and, the user lester, no host, and any domain) and one
+ netgroup (kamakiriad).
+
+ Finally, an example of the nisObject class:
+
+ dn: nisMapName=tracks, dc=dunes, dc=aja, dc=com
+ objectClass: top
+ objectClass: nisMap
+ nisMapName: tracks
+
+ dn: cn=Maxine, nisMapName=tracks, dc=dunes, dc=aja, dc=com
+ objectClass: top
+ objectClass: nisObject
+ cn: Maxine
+ nisMapName: tracks
+ nisMapEntry: Nightfly$4
+
+ This entry represents the NIS map tracks, and a single map entry.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Howard Experimental [Page 20]
+
+RFC 2307 Using LDAP as a Network Information Service March 1998
+
+
+Full Copyright Statement
+
+ Copyright (C) The Internet Society (1998). All Rights Reserved.
+
+ This document and translations of it may be copied and furnished to
+ others, and derivative works that comment on or otherwise explain it
+ or assist in its implementation may be prepared, copied, published
+ and distributed, in whole or in part, without restriction of any
+ kind, provided that the above copyright notice and this paragraph are
+ included on all such copies and derivative works. However, this
+ document itself may not be modified in any way, such as by removing
+ the copyright notice or references to the Internet Society or other
+ Internet organizations, except as needed for the purpose of
+ developing Internet standards in which case the procedures for
+ copyrights defined in the Internet Standards process must be
+ followed, or as required to translate it into languages other than
+ English.
+
+ The limited permissions granted above are perpetual and will not be
+ revoked by the Internet Society or its successors or assigns.
+
+ This document and the information contained herein is provided on an
+ "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+ TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Howard Experimental [Page 21]
+
diff --git a/source4/ldap_server/devdocs/rfc2696.txt b/source4/ldap_server/devdocs/rfc2696.txt
new file mode 100644
index 0000000000..4ccc4c169a
--- /dev/null
+++ b/source4/ldap_server/devdocs/rfc2696.txt
@@ -0,0 +1,395 @@
+
+
+
+
+
+
+Network Working Group C. Weider
+Request for Comments: 2696 A. Herron
+Category: Informational A. Anantha
+ Microsoft
+ T. Howes
+ Netscape
+ September 1999
+
+
+ LDAP Control Extension for Simple Paged Results Manipulation
+
+Status of this Memo
+
+ This memo provides information for the Internet community. It does
+ not specify an Internet standard of any kind. Distribution of this
+ memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (1999). All Rights Reserved.
+
+1. Abstract
+
+ This document describes an LDAPv3 control extension for simple paging
+ of search results. This control extension allows a client to control
+ the rate at which an LDAP server returns the results of an LDAP
+ search operation. This control may be useful when the LDAP client has
+ limited resources and may not be able to process the entire result
+ set from a given LDAP query, or when the LDAP client is connected
+ over a low-bandwidth connection. Other operations on the result set
+ are not defined in this extension. This extension is not designed to
+ provide more sophisticated result set management.
+
+ The key words "MUST", "SHOULD", and "MAY" used in this document are
+ to be interpreted as described in [bradner97].
+
+2. The Control
+
+ This control is included in the searchRequest and searchResultDone
+ messages as part of the controls field of the LDAPMessage, as defined
+ in Section 4.1.12 of [LDAPv3]. The structure of this control is as
+ follows:
+
+
+
+
+
+
+
+
+
+Weider, et al. Informational [Page 1]
+
+RFC 2696 LDAP Control Ext. for Simple Paged Results September 1999
+
+
+pagedResultsControl ::= SEQUENCE {
+ controlType 1.2.840.113556.1.4.319,
+ criticality BOOLEAN DEFAULT FALSE,
+ controlValue searchControlValue
+}
+
+The searchControlValue is an OCTET STRING wrapping the BER-encoded
+version of the following SEQUENCE:
+
+realSearchControlValue ::= SEQUENCE {
+ size INTEGER (0..maxInt),
+ -- requested page size from client
+ -- result set size estimate from server
+ cookie OCTET STRING
+}
+
+3. Client-Server Interaction
+
+ An LDAP client application that needs to control the rate at which
+ results are returned MAY specify on the searchRequest a
+ pagedResultsControl with size set to the desired page size and cookie
+ set to the zero-length string. The page size specified MAY be greater
+ than zero and less than the sizeLimit value specified in the
+ searchRequest.
+
+ If the page size is greater than or equal to the sizeLimit value, the
+ server should ignore the control as the request can be satisfied in a
+ single page. If the server does not support this control, the server
+ MUST return an error of unsupportedCriticalExtension if the client
+ requested it as critical, otherwise the server SHOULD ignore the
+ control. The remainder of this section assumes the server does not
+ ignore the client's pagedResultsControl.
+
+ Each time the server returns a set of results to the client when
+ processing a search request containing the pagedResultsControl, the
+ server includes the pagedResultsControl control in the
+ searchResultDone message. In the control returned to the client, the
+ size MAY be set to the server's estimate of the total number of
+ entries in the entire result set. Servers that cannot provide such an
+ estimate MAY set this size to zero (0). The cookie MUST be set to an
+ empty value if there are no more entries to return (i.e., the page of
+ search results returned was the last), or, if there are more entries
+ to return, to an octet string of the server's choosing,used to resume
+ the search.
+
+ The client MUST consider the cookie to be an opaque structure and
+ make no assumptions about its internal organization or value. When
+ the client wants to retrieve more entries for the result set, it MUST
+
+
+
+Weider, et al. Informational [Page 2]
+
+RFC 2696 LDAP Control Ext. for Simple Paged Results September 1999
+
+
+ send to the server a searchRequest with all values identical to the
+ initial request with the exception of the messageID, the cookie, and
+ optionally a modified pageSize. The cookie MUST be the octet string
+ on the last searchResultDone response returned by the server.
+ Returning cookies from previous searchResultDone responses besides
+ the last one is undefined, as the server implementation may restrict
+ cookies from being reused.
+
+ The server will then return the next set of results from the whole
+ result set. This interaction will continue until the client has
+ retrieved all the results, in which case the cookie in the
+ searchResultDone field will be empty, or until the client abandons
+ the search sequence as described below. Once the paged search
+ sequence has been completed, the cookie is no longer valid and MUST
+ NOT be used.
+
+ A sequence of paged search requests is abandoned by the client
+ sending a search request containing a pagedResultsControl with the
+ size set to zero (0) and the cookie set to the last cookie returned
+ by the server. A client MAY use the LDAP Abandon operation to
+ abandon one paged search request in progress, but this is discouraged
+ as it MAY invalidate the client's cookie.
+
+ If, for any reason, the server cannot resume a paged search operation
+ for a client, then it SHOULD return the appropriate error in a
+ searchResultDone entry. If this occurs, both client and server should
+ assume the paged result set is closed and no longer resumable.
+
+ A client may have any number of outstanding search requests pending,
+ any of which may have used the pagedResultsControl. A server
+ implementation which requires a limit on the number of outstanding
+ paged search requests from a given client MAY either return
+ unwillingToPerform when the client attempts to create a new paged
+ search request, or age out an older result set. If the server
+ implementation ages out an older paged search request, it SHOULD
+ return "unwilling to perform" if the client attempts to resume the
+ paged search that was aged out.
+
+ A client may safely assume that all entries that satisfy a given
+ search query are returned once and only once during the set of paged
+ search requests/responses necessary to enumerate the entire result
+ set, unless the result set for that query has changed since the
+ searchRequest starting the request/response sequence was processed.
+ In that case, the client may receive a given entry multiple times
+ and/or may not receive all entries matching the given search
+ criteria.
+
+
+
+
+
+Weider, et al. Informational [Page 3]
+
+RFC 2696 LDAP Control Ext. for Simple Paged Results September 1999
+
+
+4. Example
+
+ The following example illustrates the client-server interaction
+ between a client doing a search requesting a page size limit of 3.
+ The entire result set returned by the server contains 5 entries.
+
+ Lines beginning with "C:" indicate requests sent from client to
+ server. Lines beginning with "S:" indicate responses sent from server
+ to client. Lines beginning with "--" are comments to help explain the
+ example.
+
+ -- Client sends a search request asking for paged results
+ -- with a page size of 3.
+ C: SearchRequest + pagedResultsControl(3,"")
+ -- Server responds with three entries plus an indication
+ -- of 5 total entries in the search result and an opaque
+ -- cooking to be used by the client when retrieving subsequent
+ -- pages.
+ S: SearchResultEntry
+ S: SearchResultEntry
+ S: SearchResultEntry
+ S: SearchResultDone + pagedResultsControl(5, "opaque")
+ -- Client sends an identical search request (except for
+ -- message id), returning the opaque cooking, asking for
+ -- the next page.
+ C: SearchRequest + PagedResultsControl(3, "opaque")
+ -- Server responds with two entries plus an indication
+ -- that there are no more entries (null cookie).
+ S: SearchResultEntry
+ S: SearchResultEntry
+ S: SearchResultDone + pagedResultsControl(5,"")
+
+5. Relationship to X.500
+
+ For LDAP servers providing a front end to X.500 (93) directories, the
+ paged results control defined in this document may be mapped directly
+ onto the X.500 (93) PagedResultsRequest defined in X.511 [x500]. The
+ size parameter may be mapped onto pageSize. The cookie parameter may
+ be mapped onto queryReference. The sortKeys and reverse fields in
+ the X.500 PagedResultsRequest are excluded.
+
+
+
+
+
+
+
+
+
+
+
+Weider, et al. Informational [Page 4]
+
+RFC 2696 LDAP Control Ext. for Simple Paged Results September 1999
+
+
+6. Security Considerations
+
+ Server implementors should consider the resources used when clients
+ send searches with the simple paged control, to ensure that a
+ client's misuse of this control does not lock out other legitimate
+ operations.
+
+ Servers implementations may enforce an overriding sizelimit, to
+ prevent the retrieval of large portions of a publically-accessible
+ directory.
+
+ Clients can, using this control, determine how many entries match a
+ particular filter, before the entries are returned to the client.
+ This may require special processing in servers which perform access
+ control checks on entries to determine whether the existence of the
+ entry can be disclosed to the client.
+
+7. References
+
+ [LDAPv3] Wahl, M., Howes, T. and S. Kille, "Lightweight Directory
+ Access Protocol (v3)", RFC 2251, December 1997.
+
+ [Bradner97] Bradner, S., "Key Words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Weider, et al. Informational [Page 5]
+
+RFC 2696 LDAP Control Ext. for Simple Paged Results September 1999
+
+
+8. Authors' Addresses
+
+ Chris Weider
+ Microsoft Corp.
+ 1 Microsoft Way
+ Redmond, WA 98052
+ USA
+
+ Phone: +1 425 882-8080
+ EMail: cweider@microsoft.com
+
+
+ Andy Herron
+ Microsoft Corp.
+ 1 Microsoft Way
+ Redmond, WA 98052
+ USA
+
+ Phone: +1 425 882-8080
+ EMail: andyhe@microsoft.com
+
+
+ Anoop Anantha
+ Microsoft Corp.
+ 1 Microsoft Way
+ Redmond, WA 98052
+ USA
+
+ Phone: +1 425 882-8080
+ EMail: anoopa@microsoft.com
+
+
+ Tim Howes
+ Netscape Communications Corp.
+ 501 E. Middlefield Road
+ Mountain View, CA 94043
+ USA
+
+ Phone: +1 415 937-2600
+ EMail: howes@netscape.com
+
+
+
+
+
+
+
+
+
+
+
+Weider, et al. Informational [Page 6]
+
+RFC 2696 LDAP Control Ext. for Simple Paged Results September 1999
+
+
+9. Full Copyright Statement
+
+ Copyright (C) The Internet Society (1999). All Rights Reserved.
+
+ This document and translations of it may be copied and furnished to
+ others, and derivative works that comment on or otherwise explain it
+ or assist in its implementation may be prepared, copied, published
+ and distributed, in whole or in part, without restriction of any
+ kind, provided that the above copyright notice and this paragraph are
+ included on all such copies and derivative works. However, this
+ document itself may not be modified in any way, such as by removing
+ the copyright notice or references to the Internet Society or other
+ Internet organizations, except as needed for the purpose of
+ developing Internet standards in which case the procedures for
+ copyrights defined in the Internet Standards process must be
+ followed, or as required to translate it into languages other than
+ English.
+
+ The limited permissions granted above are perpetual and will not be
+ revoked by the Internet Society or its successors or assigns.
+
+ This document and the information contained herein is provided on an
+ "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+ TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is currently provided by the
+ Internet Society.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Weider, et al. Informational [Page 7]
+
diff --git a/source4/ldap_server/devdocs/rfc2849.txt b/source4/ldap_server/devdocs/rfc2849.txt
new file mode 100644
index 0000000000..2bf645500a
--- /dev/null
+++ b/source4/ldap_server/devdocs/rfc2849.txt
@@ -0,0 +1,787 @@
+
+
+
+
+
+
+Network Working Group G. Good
+Request for Comments: 2849 iPlanet e-commerce Solutions
+Category: Standards Track June 2000
+
+
+ The LDAP Data Interchange Format (LDIF) - Technical Specification
+
+Status of this Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2000). All Rights Reserved.
+
+Abstract
+
+ This document describes a file format suitable for describing
+ directory information or modifications made to directory information.
+ The file format, known as LDIF, for LDAP Data Interchange Format, is
+ typically used to import and export directory information between
+ LDAP-based directory servers, or to describe a set of changes which
+ are to be applied to a directory.
+
+Background and Intended Usage
+
+ There are a number of situations where a common interchange format is
+ desirable. For example, one might wish to export a copy of the
+ contents of a directory server to a file, move that file to a
+ different machine, and import the contents into a second directory
+ server.
+
+ Additionally, by using a well-defined interchange format, development
+ of data import tools from legacy systems is facilitated. A fairly
+ simple set of tools written in awk or perl can, for example, convert
+ a database of personnel information into an LDIF file. This file can
+ then be imported into a directory server, regardless of the internal
+ database representation the target directory server uses.
+
+ The LDIF format was originally developed and used in the University
+ of Michigan LDAP implementation. The first use of LDIF was in
+ describing directory entries. Later, the format was expanded to
+ allow representation of changes to directory entries.
+
+
+
+
+Good Standards Track [Page 1]
+
+RFC 2849 LDAP Data Interchange Format June 2000
+
+
+ Relationship to the application/directory MIME content-type:
+
+ The application/directory MIME content-type [1] is a general
+ framework and format for conveying directory information, and is
+ independent of any particular directory service. The LDIF format is
+ a simpler format which is perhaps easier to create, and may also be
+ used, as noted, to describe a set of changes to be applied to a
+ directory.
+
+ The key words "MUST", "MUST NOT", "MAY", "SHOULD", and "SHOULD NOT"
+ used in this document are to be interpreted as described in [7].
+
+Definition of the LDAP Data Interchange Format
+
+ The LDIF format is used to convey directory information, or a
+ description of a set of changes made to directory entries. An LDIF
+ file consists of a series of records separated by line separators. A
+ record consists of a sequence of lines describing a directory entry,
+ or a sequence of lines describing a set of changes to a directory
+ entry. An LDIF file specifies a set of directory entries, or a set
+ of changes to be applied to directory entries, but not both.
+
+ There is a one-to-one correlation between LDAP operations that modify
+ the directory (add, delete, modify, and modrdn), and the types of
+ changerecords described below ("add", "delete", "modify", and
+ "modrdn" or "moddn"). This correspondence is intentional, and
+ permits a straightforward translation from LDIF changerecords to
+ protocol operations.
+
+Formal Syntax Definition of LDIF
+
+ The following definition uses the augmented Backus-Naur Form
+ specified in RFC 2234 [2].
+
+ldif-file = ldif-content / ldif-changes
+
+ldif-content = version-spec 1*(1*SEP ldif-attrval-record)
+
+ldif-changes = version-spec 1*(1*SEP ldif-change-record)
+
+ldif-attrval-record = dn-spec SEP 1*attrval-spec
+
+ldif-change-record = dn-spec SEP *control changerecord
+
+version-spec = "version:" FILL version-number
+
+
+
+
+
+
+Good Standards Track [Page 2]
+
+RFC 2849 LDAP Data Interchange Format June 2000
+
+
+version-number = 1*DIGIT
+ ; version-number MUST be "1" for the
+ ; LDIF format described in this document.
+
+dn-spec = "dn:" (FILL distinguishedName /
+ ":" FILL base64-distinguishedName)
+
+distinguishedName = SAFE-STRING
+ ; a distinguished name, as defined in [3]
+
+base64-distinguishedName = BASE64-UTF8-STRING
+ ; a distinguishedName which has been base64
+ ; encoded (see note 10, below)
+
+rdn = SAFE-STRING
+ ; a relative distinguished name, defined as
+ ; <name-component> in [3]
+
+base64-rdn = BASE64-UTF8-STRING
+ ; an rdn which has been base64 encoded (see
+ ; note 10, below)
+
+control = "control:" FILL ldap-oid ; controlType
+ 0*1(1*SPACE ("true" / "false")) ; criticality
+ 0*1(value-spec) ; controlValue
+ SEP
+ ; (See note 9, below)
+
+ldap-oid = 1*DIGIT 0*1("." 1*DIGIT)
+ ; An LDAPOID, as defined in [4]
+
+attrval-spec = AttributeDescription value-spec SEP
+
+value-spec = ":" ( FILL 0*1(SAFE-STRING) /
+ ":" FILL (BASE64-STRING) /
+ "<" FILL url)
+ ; See notes 7 and 8, below
+
+url = <a Uniform Resource Locator,
+ as defined in [6]>
+ ; (See Note 6, below)
+
+AttributeDescription = AttributeType [";" options]
+ ; Definition taken from [4]
+
+AttributeType = ldap-oid / (ALPHA *(attr-type-chars))
+
+options = option / (option ";" options)
+
+
+
+Good Standards Track [Page 3]
+
+RFC 2849 LDAP Data Interchange Format June 2000
+
+
+option = 1*opt-char
+
+attr-type-chars = ALPHA / DIGIT / "-"
+
+opt-char = attr-type-chars
+
+changerecord = "changetype:" FILL
+ (change-add / change-delete /
+ change-modify / change-moddn)
+
+change-add = "add" SEP 1*attrval-spec
+
+change-delete = "delete" SEP
+
+change-moddn = ("modrdn" / "moddn") SEP
+ "newrdn:" ( FILL rdn /
+ ":" FILL base64-rdn) SEP
+ "deleteoldrdn:" FILL ("0" / "1") SEP
+ 0*1("newsuperior:"
+ ( FILL distinguishedName /
+ ":" FILL base64-distinguishedName) SEP)
+
+change-modify = "modify" SEP *mod-spec
+
+mod-spec = ("add:" / "delete:" / "replace:")
+ FILL AttributeDescription SEP
+ *attrval-spec
+ "-" SEP
+
+SPACE = %x20
+ ; ASCII SP, space
+
+FILL = *SPACE
+
+SEP = (CR LF / LF)
+
+CR = %x0D
+ ; ASCII CR, carriage return
+
+LF = %x0A
+ ; ASCII LF, line feed
+
+ALPHA = %x41-5A / %x61-7A
+ ; A-Z / a-z
+
+DIGIT = %x30-39
+ ; 0-9
+
+
+
+
+Good Standards Track [Page 4]
+
+RFC 2849 LDAP Data Interchange Format June 2000
+
+
+UTF8-1 = %x80-BF
+
+UTF8-2 = %xC0-DF UTF8-1
+
+UTF8-3 = %xE0-EF 2UTF8-1
+
+UTF8-4 = %xF0-F7 3UTF8-1
+
+UTF8-5 = %xF8-FB 4UTF8-1
+
+UTF8-6 = %xFC-FD 5UTF8-1
+
+SAFE-CHAR = %x01-09 / %x0B-0C / %x0E-7F
+ ; any value <= 127 decimal except NUL, LF,
+ ; and CR
+
+SAFE-INIT-CHAR = %x01-09 / %x0B-0C / %x0E-1F /
+ %x21-39 / %x3B / %x3D-7F
+ ; any value <= 127 except NUL, LF, CR,
+ ; SPACE, colon (":", ASCII 58 decimal)
+ ; and less-than ("<" , ASCII 60 decimal)
+
+SAFE-STRING = [SAFE-INIT-CHAR *SAFE-CHAR]
+
+UTF8-CHAR = SAFE-CHAR / UTF8-2 / UTF8-3 /
+ UTF8-4 / UTF8-5 / UTF8-6
+
+UTF8-STRING = *UTF8-CHAR
+
+BASE64-UTF8-STRING = BASE64-STRING
+ ; MUST be the base64 encoding of a
+ ; UTF8-STRING
+
+BASE64-CHAR = %x2B / %x2F / %x30-39 / %x3D / %x41-5A /
+ %x61-7A
+ ; +, /, 0-9, =, A-Z, and a-z
+ ; as specified in [5]
+
+BASE64-STRING = [*(BASE64-CHAR)]
+
+
+ Notes on LDIF Syntax
+
+ 1) For the LDIF format described in this document, the version
+ number MUST be "1". If the version number is absent,
+ implementations MAY choose to interpret the contents as an
+ older LDIF file format, supported by the University of
+ Michigan ldap-3.3 implementation [8].
+
+
+
+Good Standards Track [Page 5]
+
+RFC 2849 LDAP Data Interchange Format June 2000
+
+
+ 2) Any non-empty line, including comment lines, in an LDIF file
+ MAY be folded by inserting a line separator (SEP) and a SPACE.
+ Folding MUST NOT occur before the first character of the line.
+ In other words, folding a line into two lines, the first of
+ which is empty, is not permitted. Any line that begins with a
+ single space MUST be treated as a continuation of the previous
+ (non-empty) line. When joining folded lines, exactly one space
+ character at the beginning of each continued line must be
+ discarded. Implementations SHOULD NOT fold lines in the middle
+ of a multi-byte UTF-8 character.
+
+ 3) Any line that begins with a pound-sign ("#", ASCII 35) is a
+ comment line, and MUST be ignored when parsing an LDIF file.
+
+ 4) Any dn or rdn that contains characters other than those
+ defined as "SAFE-UTF8-CHAR", or begins with a character other
+ than those defined as "SAFE-INIT-UTF8-CHAR", above, MUST be
+ base-64 encoded. Other values MAY be base-64 encoded. Any
+ value that contains characters other than those defined as
+ "SAFE-CHAR", or begins with a character other than those
+ defined as "SAFE-INIT-CHAR", above, MUST be base-64 encoded.
+ Other values MAY be base-64 encoded.
+
+ 5) When a zero-length attribute value is to be included directly
+ in an LDIF file, it MUST be represented as
+ AttributeDescription ":" FILL SEP. For example, "seeAlso:"
+ followed by a newline represents a zero-length "seeAlso"
+ attribute value. It is also permissible for the value
+ referred to by a URL to be of zero length.
+
+ 6) When a URL is specified in an attrval-spec, the following
+ conventions apply:
+
+ a) Implementations SHOULD support the file:// URL format. The
+ contents of the referenced file are to be included verbatim
+ in the interpreted output of the LDIF file.
+ b) Implementations MAY support other URL formats. The
+ semantics associated with each supported URL will be
+ documented in an associated Applicability Statement.
+
+ 7) Distinguished names, relative distinguished names, and
+ attribute values of DirectoryString syntax MUST be valid UTF-8
+ strings. Implementations that read LDIF MAY interpret files
+ in which these entities are stored in some other character set
+ encoding, but implementations MUST NOT generate LDIF content
+ which does not contain valid UTF-8 data.
+
+
+
+
+
+Good Standards Track [Page 6]
+
+RFC 2849 LDAP Data Interchange Format June 2000
+
+
+ 8) Values or distinguished names that end with SPACE SHOULD be
+ base-64 encoded.
+
+ 9) When controls are included in an LDIF file, implementations
+ MAY choose to ignore some or all of them. This may be
+ necessary if the changes described in the LDIF file are being
+ sent on an LDAPv2 connection (LDAPv2 does not support
+ controls), or the particular controls are not supported by the
+ remote server. If the criticality of a control is "true", then
+ the implementation MUST either include the control, or MUST
+ NOT send the operation to a remote server.
+
+ 10) When an attrval-spec, distinguishedName, or rdn is base64-
+ encoded, the encoding rules specified in [5] are used with the
+ following exceptions: a) The requirement that base64 output
+ streams must be represented as lines of no more than 76
+ characters is removed. Lines in LDIF files may only be folded
+ according to the folding rules described in note 2, above. b)
+ Base64 strings in [5] may contain characters other than those
+ defined in BASE64-CHAR, and are ignored. LDIF does not permit
+ any extraneous characters, other than those used for line
+ folding.
+
+Examples of LDAP Data Interchange Format
+
+Example 1: An simple LDAP file with two entries
+
+version: 1
+dn: cn=Barbara Jensen, ou=Product Development, dc=airius, dc=com
+objectclass: top
+objectclass: person
+objectclass: organizationalPerson
+cn: Barbara Jensen
+cn: Barbara J Jensen
+cn: Babs Jensen
+sn: Jensen
+uid: bjensen
+telephonenumber: +1 408 555 1212
+description: A big sailing fan.
+
+dn: cn=Bjorn Jensen, ou=Accounting, dc=airius, dc=com
+objectclass: top
+objectclass: person
+objectclass: organizationalPerson
+cn: Bjorn Jensen
+sn: Jensen
+telephonenumber: +1 408 555 1212
+
+
+
+
+Good Standards Track [Page 7]
+
+RFC 2849 LDAP Data Interchange Format June 2000
+
+
+Example 2: A file containing an entry with a folded attribute value
+
+version: 1
+dn:cn=Barbara Jensen, ou=Product Development, dc=airius, dc=com
+objectclass:top
+objectclass:person
+objectclass:organizationalPerson
+cn:Barbara Jensen
+cn:Barbara J Jensen
+cn:Babs Jensen
+sn:Jensen
+uid:bjensen
+telephonenumber:+1 408 555 1212
+description:Babs is a big sailing fan, and travels extensively in sea
+ rch of perfect sailing conditions.
+title:Product Manager, Rod and Reel Division
+
+Example 3: A file containing a base-64-encoded value
+
+version: 1
+dn: cn=Gern Jensen, ou=Product Testing, dc=airius, dc=com
+objectclass: top
+objectclass: person
+objectclass: organizationalPerson
+cn: Gern Jensen
+cn: Gern O Jensen
+sn: Jensen
+uid: gernj
+telephonenumber: +1 408 555 1212
+description:: V2hhdCBhIGNhcmVmdWwgcmVhZGVyIHlvdSBhcmUhICBUaGlzIHZhbHVl
+IGlzIGJhc2UtNjQtZW5jb2RlZCBiZWNhdXNlIGl0IGhhcyBhIGNvbnRyb2wgY2hhcmFjdG
+VyIGluIGl0IChhIENSKS4NICBCeSB0aGUgd2F5LCB5b3Ugc2hvdWxkIHJlYWxseSBnZXQg
+b3V0IG1vcmUu
+
+Example 4: A file containing an entries with UTF-8-encoded attribute
+values, including language tags. Comments indicate the contents
+of UTF-8-encoded attributes and distinguished names.
+
+version: 1
+dn:: b3U95Za25qWt6YOoLG89QWlyaXVz
+# dn:: ou=<JapaneseOU>,o=Airius
+objectclass: top
+objectclass: organizationalUnit
+ou:: 5Za25qWt6YOo
+# ou:: <JapaneseOU>
+ou;lang-ja:: 5Za25qWt6YOo
+# ou;lang-ja:: <JapaneseOU>
+ou;lang-ja;phonetic:: 44GI44GE44GO44KH44GG44G2
+
+
+
+Good Standards Track [Page 8]
+
+RFC 2849 LDAP Data Interchange Format June 2000
+
+
+# ou;lang-ja:: <JapaneseOU_in_phonetic_representation>
+ou;lang-en: Sales
+description: Japanese office
+
+dn:: dWlkPXJvZ2FzYXdhcmEsb3U95Za25qWt6YOoLG89QWlyaXVz
+# dn:: uid=<uid>,ou=<JapaneseOU>,o=Airius
+userpassword: {SHA}O3HSv1MusyL4kTjP+HKI5uxuNoM=
+objectclass: top
+objectclass: person
+objectclass: organizationalPerson
+objectclass: inetOrgPerson
+uid: rogasawara
+mail: rogasawara@airius.co.jp
+givenname;lang-ja:: 44Ot44OJ44OL44O8
+# givenname;lang-ja:: <JapaneseGivenname>
+sn;lang-ja:: 5bCP56yg5Y6f
+# sn;lang-ja:: <JapaneseSn>
+cn;lang-ja:: 5bCP56yg5Y6fIOODreODieODi+ODvA==
+# cn;lang-ja:: <JapaneseCn>
+title;lang-ja:: 5Za25qWt6YOoIOmDqOmVtw==
+# title;lang-ja:: <JapaneseTitle>
+preferredlanguage: ja
+givenname:: 44Ot44OJ44OL44O8
+# givenname:: <JapaneseGivenname>
+sn:: 5bCP56yg5Y6f
+# sn:: <JapaneseSn>
+cn:: 5bCP56yg5Y6fIOODreODieODi+ODvA==
+# cn:: <JapaneseCn>
+title:: 5Za25qWt6YOoIOmDqOmVtw==
+# title:: <JapaneseTitle>
+givenname;lang-ja;phonetic:: 44KN44Gp44Gr44O8
+# givenname;lang-ja;phonetic::
+<JapaneseGivenname_in_phonetic_representation_kana>
+sn;lang-ja;phonetic:: 44GK44GM44GV44KP44KJ
+# sn;lang-ja;phonetic:: <JapaneseSn_in_phonetic_representation_kana>
+cn;lang-ja;phonetic:: 44GK44GM44GV44KP44KJIOOCjeOBqeOBq+ODvA==
+# cn;lang-ja;phonetic:: <JapaneseCn_in_phonetic_representation_kana>
+title;lang-ja;phonetic:: 44GI44GE44GO44KH44GG44G2IOOBtuOBoeOCh+OBhg==
+# title;lang-ja;phonetic::
+# <JapaneseTitle_in_phonetic_representation_kana>
+givenname;lang-en: Rodney
+sn;lang-en: Ogasawara
+cn;lang-en: Rodney Ogasawara
+title;lang-en: Sales, Director
+
+
+
+
+
+
+
+Good Standards Track [Page 9]
+
+RFC 2849 LDAP Data Interchange Format June 2000
+
+
+Example 5: A file containing a reference to an external file
+
+version: 1
+dn: cn=Horatio Jensen, ou=Product Testing, dc=airius, dc=com
+objectclass: top
+objectclass: person
+objectclass: organizationalPerson
+cn: Horatio Jensen
+
+cn: Horatio N Jensen
+sn: Jensen
+uid: hjensen
+telephonenumber: +1 408 555 1212
+jpegphoto:< file:///usr/local/directory/photos/hjensen.jpg
+
+Example 6: A file containing a series of change records and comments
+
+version: 1
+# Add a new entry
+dn: cn=Fiona Jensen, ou=Marketing, dc=airius, dc=com
+changetype: add
+objectclass: top
+objectclass: person
+objectclass: organizationalPerson
+cn: Fiona Jensen
+sn: Jensen
+uid: fiona
+telephonenumber: +1 408 555 1212
+jpegphoto:< file:///usr/local/directory/photos/fiona.jpg
+
+# Delete an existing entry
+dn: cn=Robert Jensen, ou=Marketing, dc=airius, dc=com
+changetype: delete
+
+# Modify an entry's relative distinguished name
+dn: cn=Paul Jensen, ou=Product Development, dc=airius, dc=com
+changetype: modrdn
+newrdn: cn=Paula Jensen
+deleteoldrdn: 1
+
+# Rename an entry and move all of its children to a new location in
+# the directory tree (only implemented by LDAPv3 servers).
+dn: ou=PD Accountants, ou=Product Development, dc=airius, dc=com
+changetype: modrdn
+newrdn: ou=Product Development Accountants
+deleteoldrdn: 0
+newsuperior: ou=Accounting, dc=airius, dc=com
+
+
+
+
+Good Standards Track [Page 10]
+
+RFC 2849 LDAP Data Interchange Format June 2000
+
+
+# Modify an entry: add an additional value to the postaladdress
+# attribute, completely delete the description attribute, replace
+# the telephonenumber attribute with two values, and delete a specific
+# value from the facsimiletelephonenumber attribute
+dn: cn=Paula Jensen, ou=Product Development, dc=airius, dc=com
+changetype: modify
+add: postaladdress
+postaladdress: 123 Anystreet $ Sunnyvale, CA $ 94086
+-
+
+delete: description
+-
+replace: telephonenumber
+telephonenumber: +1 408 555 1234
+telephonenumber: +1 408 555 5678
+-
+delete: facsimiletelephonenumber
+facsimiletelephonenumber: +1 408 555 9876
+-
+
+# Modify an entry: replace the postaladdress attribute with an empty
+# set of values (which will cause the attribute to be removed), and
+# delete the entire description attribute. Note that the first will
+# always succeed, while the second will only succeed if at least
+# one value for the description attribute is present.
+dn: cn=Ingrid Jensen, ou=Product Support, dc=airius, dc=com
+changetype: modify
+replace: postaladdress
+-
+delete: description
+-
+
+Example 7: An LDIF file containing a change record with a control
+version: 1
+# Delete an entry. The operation will attach the LDAPv3
+# Tree Delete Control defined in [9]. The criticality
+# field is "true" and the controlValue field is
+# absent, as required by [9].
+dn: ou=Product Development, dc=airius, dc=com
+control: 1.2.840.113556.1.4.805 true
+changetype: delete
+
+
+
+
+
+
+
+
+
+
+Good Standards Track [Page 11]
+
+RFC 2849 LDAP Data Interchange Format June 2000
+
+
+Security Considerations
+
+ Given typical directory applications, an LDIF file is likely to
+ contain sensitive personal data. Appropriate measures should be
+ taken to protect the privacy of those persons whose data is contained
+ in an LDIF file.
+
+ Since ":<" directives can cause external content to be included when
+ processing an LDIF file, one should be cautious of accepting LDIF
+ files from external sources. A "trojan" LDIF file could name a file
+ with sensitive contents and cause it to be included in a directory
+ entry, which a hostile entity could read via LDAP.
+
+ LDIF does not provide any method for carrying authentication
+ information with an LDIF file. Users of LDIF files must take care to
+ verify the integrity of an LDIF file received from an external
+ source.
+
+Acknowledgments
+
+ The LDAP Interchange Format was developed as part of the University
+ of Michigan LDAP reference implementation, and was developed by Tim
+ Howes, Mark Smith, and Gordon Good. It is based in part upon work
+ supported by the National Science Foundation under Grant No. NCR-
+ 9416667.
+
+ Members of the IETF LDAP Extensions Working group provided many
+ helpful suggestions. In particular, Hallvard B. Furuseth of the
+ University of Oslo made many significant contributions to this
+ document, including a thorough review and rewrite of the BNF.
+
+References
+
+ [1] Howes, T. and M. Smith, "A MIME Content-Type for Directory
+ Information", RFC 2425, September 1998.
+
+ [2] Crocker, D., and P. Overell, "Augmented BNF for Syntax
+ Specifications: ABNF", RFC 2234, November 1997.
+
+ [3] Wahl, M., Kille, S. and T. Howes, "A String Representation of
+ Distinguished Names", RFC 2253, December 1997.
+
+ [4] Wahl, M., Howes, T. and S. Kille, "Lightweight Directory Access
+ Protocol (v3)", RFC 2251, July 1997.
+
+ [5] Freed, N. and N. Borenstein, "Multipurpose Internet Mail
+ Extensions (MIME) Part One: Format of Internet Message Bodies",
+ RFC 2045, November 1996.
+
+
+
+Good Standards Track [Page 12]
+
+RFC 2849 LDAP Data Interchange Format June 2000
+
+
+ [6] Berners-Lee, T., Masinter, L. and M. McCahill, "Uniform
+ Resource Locators (URL)", RFC 1738, December 1994.
+
+ [7] Bradner, S., "Key Words for use in RFCs to Indicate Requirement
+ Levels", BCP 14, RFC 2119, March 1997.
+
+ [8] The SLAPD and SLURPD Administrators Guide. University of
+ Michigan, April 1996. <URL:
+ http://www.umich.edu/~dirsvcs/ldap/doc/guides/slapd/toc.html>
+
+ [9] M. P. Armijo, "Tree Delete Control", Work in Progress.
+
+Author's Address
+
+ Gordon Good
+ iPlanet e-commerce Solutions
+ 150 Network Circle
+ Mailstop USCA17-201
+ Santa Clara, CA 95054, USA
+
+ Phone: +1 408 276 4351
+ EMail: ggood@netscape.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Good Standards Track [Page 13]
+
+RFC 2849 LDAP Data Interchange Format June 2000
+
+
+Full Copyright Statement
+
+ Copyright (C) The Internet Society (2000). All Rights Reserved.
+
+ This document and translations of it may be copied and furnished to
+ others, and derivative works that comment on or otherwise explain it
+ or assist in its implementation may be prepared, copied, published
+ and distributed, in whole or in part, without restriction of any
+ kind, provided that the above copyright notice and this paragraph are
+ included on all such copies and derivative works. However, this
+ document itself may not be modified in any way, such as by removing
+ the copyright notice or references to the Internet Society or other
+ Internet organizations, except as needed for the purpose of
+ developing Internet standards in which case the procedures for
+ copyrights defined in the Internet Standards process must be
+ followed, or as required to translate it into languages other than
+ English.
+
+ The limited permissions granted above are perpetual and will not be
+ revoked by the Internet Society or its successors or assigns.
+
+ This document and the information contained herein is provided on an
+ "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+ TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is currently provided by the
+ Internet Society.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Good Standards Track [Page 14]
+
diff --git a/source4/ldap_server/devdocs/rfc2891.txt b/source4/ldap_server/devdocs/rfc2891.txt
new file mode 100644
index 0000000000..1d91e07783
--- /dev/null
+++ b/source4/ldap_server/devdocs/rfc2891.txt
@@ -0,0 +1,451 @@
+
+
+
+
+
+
+Network Working Group T. Howes
+Request for Comments: 2891 Loudcloud
+Category: Standards Track M. Wahl
+ Sun Microsystems
+ A. Anantha
+ Microsoft
+ August 2000
+
+
+ LDAP Control Extension for Server Side Sorting of Search Results
+
+Status of this Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2000). All Rights Reserved.
+
+Abstract
+
+ This document describes two LDAPv3 control extensions for server side
+ sorting of search results. These controls allows a client to specify
+ the attribute types and matching rules a server should use when
+ returning the results to an LDAP search request. The controls may be
+ useful when the LDAP client has limited functionality or for some
+ other reason cannot sort the results but still needs them sorted.
+ Other permissible controls on search operations are not defined in
+ this extension.
+
+ The sort controls allow a server to return a result code for the
+ sorting of the results that is independent of the result code
+ returned for the search operation.
+
+ The key words "MUST", "SHOULD", and "MAY" used in this document are
+ to be interpreted as described in [bradner97].
+
+
+
+
+
+
+
+
+
+
+
+Howes, et al. Standards Track [Page 1]
+
+RFC 2891 LDAP Control Extension for Server Side Sorting August 2000
+
+
+1. The Controls
+
+1.1 Request Control
+
+ This control is included in the searchRequest message as part of the
+ controls field of the LDAPMessage, as defined in Section 4.1.12 of
+ [LDAPv3].
+
+ The controlType is set to "1.2.840.113556.1.4.473". The criticality
+ MAY be either TRUE or FALSE (where absent is also equivalent to
+ FALSE) at the client's option. The controlValue is an OCTET STRING,
+ whose value is the BER encoding of a value of the following SEQUENCE:
+
+ SortKeyList ::= SEQUENCE OF SEQUENCE {
+ attributeType AttributeDescription,
+ orderingRule [0] MatchingRuleId OPTIONAL,
+ reverseOrder [1] BOOLEAN DEFAULT FALSE }
+
+ The SortKeyList sequence is in order of highest to lowest sort key
+ precedence.
+
+ The MatchingRuleId, as defined in section 4.1.9 of [LDAPv3], SHOULD
+ be one that is valid for the attribute type it applies to. If it is
+ not, the server will return inappropriateMatching.
+
+ Each attributeType should only occur in the SortKeyList once. If an
+ attributeType is included in the sort key list multiple times, the
+ server should return an error in the sortResult of
+ unwillingToPerform.
+
+ If the orderingRule is omitted, the ordering MatchingRule defined for
+ use with this attribute MUST be used.
+
+ Any conformant implementation of this control MUST allow a sort key
+ list with at least one key.
+
+1.2 Response Control
+
+ This control is included in the searchResultDone message as part of
+ the controls field of the LDAPMessage, as defined in Section 4.1.12
+ of [LDAPv3].
+
+ The controlType is set to "1.2.840.113556.1.4.474". The criticality
+ is FALSE (MAY be absent). The controlValue is an OCTET STRING, whose
+ value is the BER encoding of a value of the following SEQUENCE:
+
+
+
+
+
+
+Howes, et al. Standards Track [Page 2]
+
+RFC 2891 LDAP Control Extension for Server Side Sorting August 2000
+
+
+ SortResult ::= SEQUENCE {
+ sortResult ENUMERATED {
+ success (0), -- results are sorted
+ operationsError (1), -- server internal failure
+ timeLimitExceeded (3), -- timelimit reached before
+ -- sorting was completed
+ strongAuthRequired (8), -- refused to return sorted
+ -- results via insecure
+ -- protocol
+ adminLimitExceeded (11), -- too many matching entries
+ -- for the server to sort
+ noSuchAttribute (16), -- unrecognized attribute
+ -- type in sort key
+ inappropriateMatching (18), -- unrecognized or
+ -- inappropriate matching
+ -- rule in sort key
+ insufficientAccessRights (50), -- refused to return sorted
+ -- results to this client
+ busy (51), -- too busy to process
+ unwillingToPerform (53), -- unable to sort
+ other (80)
+ },
+ attributeType [0] AttributeDescription OPTIONAL }
+
+2. Client-Server Interaction
+
+ The sortKeyRequestControl specifies one or more attribute types and
+ matching rules for the results returned by a search request. The
+ server SHOULD return all results for the search request in the order
+ specified by the sort keys. If the reverseOrder field is set to TRUE,
+ then the entries will be presented in reverse sorted order for the
+ specified key.
+
+ There are six possible scenarios that may occur as a result of the
+ sort control being included on the search request:
+
+ 1 - If the server does not support this sorting control and the
+ client specified TRUE for the control's criticality field, then
+ the server MUST return unavailableCriticalExtension as a return
+ code in the searchResultDone message and not send back any other
+ results. This behavior is specified in section 4.1.12 of
+ [LDAPv3].
+
+ 2 - If the server does not support this sorting control and the
+ client specified FALSE for the control's criticality field, then
+ the server MUST ignore the sort control and process the search
+ request as if it were not present. This behavior is specified in
+ section 4.1.12 of [LDAPv3].
+
+
+
+Howes, et al. Standards Track [Page 3]
+
+RFC 2891 LDAP Control Extension for Server Side Sorting August 2000
+
+
+ 3 - If the server supports this sorting control but for some reason
+ cannot sort the search results using the specified sort keys and
+ the client specified TRUE for the control's criticality field,
+ then the server SHOULD do the following: return
+ unavailableCriticalExtension as a return code in the
+ searchResultDone message; include the sortKeyResponseControl in
+ the searchResultDone message, and not send back any search result
+ entries.
+
+ 4 - If the server supports this sorting control but for some reason
+ cannot sort the search results using the specified sort keys and
+ the client specified FALSE for the control's criticality field,
+ then the server should return all search results unsorted and
+ include the sortKeyResponseControl in the searchResultDone
+ message.
+
+ 5 - If the server supports this sorting control and can sort the
+ search results using the specified sort keys, then it should
+ include the sortKeyResponseControl in the searchResultDone
+ message with a sortResult of success.
+
+ 6 - If the search request failed for any reason and/or there are no
+ searchResultEntry messages returned for the search response, then
+ the server SHOULD omit the sortKeyResponseControl from the
+ searchResultDone message.
+
+ The client application is assured that the results are sorted in the
+ specified key order if and only if the result code in the
+ sortKeyResponseControl is success. If the server omits the
+ sortKeyResponseControl from the searchResultDone message, the client
+ SHOULD assume that the sort control was ignored by the server.
+
+ The sortKeyResponseControl, if included by the server in the
+ searchResultDone message, should have the sortResult set to either
+ success if the results were sorted in accordance with the keys
+ specified in the sortKeyRequestControl or set to the appropriate
+ error code as to why it could not sort the data (such as
+ noSuchAttribute or inappropriateMatching). Optionally, the server MAY
+ set the attributeType to the first attribute type specified in the
+ SortKeyList that was in error. The client SHOULD ignore the
+ attributeType field if the sortResult is success.
+
+ The server may not be able to sort the results using the specified
+ sort keys because it may not recognize one of the attribute types,
+ the matching rule associated with an attribute type is not
+ applicable, or none of the attributes in the search response are of
+ these types. Servers may also restrict the number of keys allowed in
+ the control, such as only supporting a single key.
+
+
+
+Howes, et al. Standards Track [Page 4]
+
+RFC 2891 LDAP Control Extension for Server Side Sorting August 2000
+
+
+ Servers that chain requests to other LDAP servers should ensure that
+ the server satisfying the client's request sort the entire result set
+ prior to sending back the results.
+
+2.1 Behavior in a chained environment
+
+ If a server receives a sort request, the client expects to receive a
+ set of sorted results. If a client submits a sort request to a server
+ which chains the request and gets entries from multiple servers, and
+ the client has set the criticality of the sort extension to TRUE, the
+ server MUST merge sort the results before returning them to the
+ client or MUST return unwillingToPerform.
+
+2.2 Other sort issues
+
+ An entry that meets the search criteria may be missing one or more of
+ the sort keys. In that case, the entry is considered to have a value
+ of NULL for that key. This standard considers NULL to be a larger
+ value than all other valid values for that key. For example, if only
+ one key is specified, entries which meet the search criteria but do
+ not have that key collate after all the entries which do have that
+ key. If the reverseOrder flag is set, and only one key is specified,
+ entries which meet the search criteria but do not have that key
+ collate BEFORE all the entries which do have that key.
+
+ If a sort key is a multi-valued attribute, and an entry happens to
+ have multiple values for that attribute and no other controls are
+ present that affect the sorting order, then the server SHOULD use the
+ least value (according to the ORDERING rule for that attribute).
+
+3. Interaction with other search controls
+
+ When the sortKeyRequestControl control is included with the
+ pagedResultsControl control as specified in [LdapPaged], then the
+ server should send the searchResultEntry messages sorted according to
+ the sort keys applied to the entire result set. The server should not
+ simply sort each page, as this will give erroneous results to the
+ client.
+
+ The sortKeyList must be present on each searchRequest message for the
+ paged result. It also must not change between searchRequests for the
+ same result set. If the server has sorted the data, then it SHOULD
+ send back a sortKeyResponseControl control on every searchResultDone
+ message for each page. This will allow clients to quickly determine
+ if the result set is sorted, rather than waiting to receive the
+ entire result set.
+
+
+
+
+
+Howes, et al. Standards Track [Page 5]
+
+RFC 2891 LDAP Control Extension for Server Side Sorting August 2000
+
+
+4. Security Considerations
+
+ Implementors and administrators should be aware that allowing sorting
+ of results could enable the retrieval of a large number of records
+ from a given directory service, regardless of administrative limits
+ set on the maximum number of records to return.
+
+ A client that desired to pull all records out of a directory service
+ could use a combination of sorting and updating of search filters to
+ retrieve all records in a database in small result sets, thus
+ circumventing administrative limits.
+
+ This behavior can be overcome by the judicious use of permissions on
+ the directory entries by the administrator and by intelligent
+ implementations of administrative limits on the number of records
+ retrieved by a client.
+
+5. References
+
+ [LDAPv3] Wahl, M, Kille, S. and T. Howes, "Lightweight Directory
+ Access Protocol (v3)", RFC 2251, December 1997.
+
+ [Bradner97] Bradner, S., "Key Words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [LdapPaged] Weider, C., Herron, A., Anantha, A. and T. Howes, "LDAP
+ Control Extension for Simple Paged Results Manipulation",
+ RFC 2696, September 1999.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Howes, et al. Standards Track [Page 6]
+
+RFC 2891 LDAP Control Extension for Server Side Sorting August 2000
+
+
+6. Authors' Addresses
+
+ Anoop Anantha
+ Microsoft Corp.
+ 1 Microsoft Way
+ Redmond, WA 98052
+ USA
+
+ Phone: +1 425 882-8080
+ EMail: anoopa@microsoft.com
+
+
+ Tim Howes
+ Loudcloud, Inc.
+ 615 Tasman Dr.
+ Sunnyvale, CA 94089
+ USA
+
+ EMail: howes@loudcloud.com
+
+
+ Mark Wahl
+ Sun Microsystems, Inc.
+ 8911 Capital of Texas Hwy Suite 4140
+ Austin, TX 78759
+ USA
+
+ EMail: Mark.Wahl@sun.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Howes, et al. Standards Track [Page 7]
+
+RFC 2891 LDAP Control Extension for Server Side Sorting August 2000
+
+
+7. Full Copyright Statement
+
+ Copyright (C) The Internet Society (2000). All Rights Reserved.
+
+ This document and translations of it may be copied and furnished to
+ others, and derivative works that comment on or otherwise explain it
+ or assist in its implementation may be prepared, copied, published
+ and distributed, in whole or in part, without restriction of any
+ kind, provided that the above copyright notice and this paragraph are
+ included on all such copies and derivative works. However, this
+ document itself may not be modified in any way, such as by removing
+ the copyright notice or references to the Internet Society or other
+ Internet organizations, except as needed for the purpose of
+ developing Internet standards in which case the procedures for
+ copyrights defined in the Internet Standards process must be
+ followed, or as required to translate it into languages other than
+ English.
+
+ The limited permissions granted above are perpetual and will not be
+ revoked by the Internet Society or its successors or assigns.
+
+ This document and the information contained herein is provided on an
+ "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+ TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is currently provided by the
+ Internet Society.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Howes, et al. Standards Track [Page 8]
+
diff --git a/source4/ldap_server/devdocs/rfc3296.txt b/source4/ldap_server/devdocs/rfc3296.txt
new file mode 100644
index 0000000000..32cf91cca7
--- /dev/null
+++ b/source4/ldap_server/devdocs/rfc3296.txt
@@ -0,0 +1,787 @@
+
+
+
+
+
+
+Network Working Group K. Zeilenga
+Request for Comments: 3296 OpenLDAP Foundation
+Category: Standards Track July 2002
+
+
+ Named Subordinate References in
+ Lightweight Directory Access Protocol (LDAP) Directories
+
+Status of this Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2002). All Rights Reserved.
+
+Abstract
+
+ This document details schema and protocol elements for representing
+ and managing named subordinate references in Lightweight Directory
+ Access Protocol (LDAP) Directories.
+
+Conventions
+
+ Schema definitions are provided using LDAPv3 description formats
+ [RFC2252]. Definitions provided here are formatted (line wrapped)
+ for readability.
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" used in
+ this document are to be interpreted as described in BCP 14 [RFC2119].
+
+1. Background and Intended Usage
+
+ The broadening of interest in LDAP (Lightweight Directory Access
+ Protocol) [RFC2251] directories beyond their use as front ends to
+ X.500 [X.500] directories has created a need to represent knowledge
+ information in a more general way. Knowledge information is
+ information about one or more servers maintained in another server,
+ used to link servers and services together.
+
+ This document details schema and protocol elements for representing
+ and manipulating named subordinate references in LDAP directories. A
+ referral object is used to hold subordinate reference information in
+
+
+
+Zeilenga Standards Track [Page 1]
+
+RFC 3296 Named Subordinate References in LDAP Directories July 2002
+
+
+ the directory. These referral objects hold one or more URIs
+ [RFC2396] contained in values of the ref attribute type and are used
+ to generate protocol referrals and continuations.
+
+ A control, ManageDsaIT, is defined to allow manipulation of referral
+ and other special objects as normal objects. As the name of control
+ implies, it is intended to be analogous to the ManageDsaIT service
+ option described in X.511(97) [X.511].
+
+ Other forms of knowledge information are not detailed by this
+ document. These forms may be described in subsequent documents.
+
+ This document details subordinate referral processing requirements
+ for servers. This document does not describe protocol syntax and
+ semantics. This is detailed in RFC 2251 [RFC2251].
+
+ This document does not detail use of subordinate knowledge references
+ to support replicated environments nor distributed operations (e.g.,
+ chaining of operations from one server to other servers).
+
+2. Schema
+
+2.1. The referral Object Class
+
+ A referral object is a directory entry whose structural object class
+ is (or is derived from) the referral object class.
+
+ ( 2.16.840.1.113730.3.2.6
+ NAME 'referral'
+ DESC 'named subordinate reference object'
+ STRUCTURAL
+ MUST ref )
+
+ The referral object class is a structural object class used to
+ represent a subordinate reference in the directory. The referral
+ object class SHOULD be used in conjunction with the extensibleObject
+ object class to support the naming attributes used in the entry's
+ Distinguished Name (DN) [RFC2253].
+
+ Referral objects are normally instantiated at DSEs immediately
+ subordinate to object entries within a naming context held by the
+ DSA. Referral objects are analogous to X.500 subordinate knowledge
+ (subr) DSEs [X.501].
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 2]
+
+RFC 3296 Named Subordinate References in LDAP Directories July 2002
+
+
+ In the presence of a ManageDsaIT control, referral objects are
+ treated as normal entries as described in section 3. Note that the
+ ref attribute is operational and will only be returned in a search
+ entry response when requested.
+
+ In the absence of a ManageDsaIT control, the content of referral
+ objects are used to construct referrals and search references as
+ described in Section 4 and, as such, the referral entries are not
+ themselves visible to clients.
+
+2.2 The ref Attribute Type
+
+ ( 2.16.840.1.113730.3.1.34
+ NAME 'ref'
+ DESC 'named reference - a labeledURI'
+ EQUALITY caseExactMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+ USAGE distributedOperation )
+
+ The ref attribute type has directoryString syntax and is case
+ sensitive. The ref attribute is multi-valued. Values placed in the
+ attribute MUST conform to the specification given for the labeledURI
+ attribute [RFC2079]. The labeledURI specification defines a format
+ that is a URI, optionally followed by whitespace and a label. This
+ document does not make use of the label portion of the syntax.
+ Future documents MAY enable new functionality by imposing additional
+ structure on the label portion of the syntax as it appears in the ref
+ attribute.
+
+ If the URI contained in a ref attribute value refers to a LDAP
+ [RFC2251] server, it MUST be in the form of a LDAP URL [RFC2255].
+ The LDAP URL SHOULD NOT contain an explicit scope specifier, filter,
+ attribute description list, or any extensions. The LDAP URL SHOULD
+ contain a non-empty DN. The handling of LDAP URLs with absent or
+ empty DN parts or with explicit scope specifier is not defined by
+ this specification.
+
+ Other URI schemes MAY be used so long as all operations returning
+ referrals based upon the value could be performed. This document
+ does not detail use of non-LDAP URIs. This is left to future
+ specifications.
+
+ The referential integrity of the URI SHOULD NOT be validated by the
+ server holding or returning the URI (whether as a value of the
+ attribute or as part of a referral result or search reference
+ response).
+
+
+
+
+
+Zeilenga Standards Track [Page 3]
+
+RFC 3296 Named Subordinate References in LDAP Directories July 2002
+
+
+ When returning a referral result or search continuation, the server
+ MUST NOT return the separator or label portions of the attribute
+ values as part of the reference. When the attribute contains
+ multiple values, the URI part of each value is used to construct the
+ referral result or search continuation.
+
+ The ref attribute values SHOULD NOT be used as a relative name-
+ component of an entry's DN [RFC2253].
+
+ This document uses the ref attribute in conjunction with the referral
+ object class to represent subordinate references. The ref attribute
+ may be used for other purposes as defined by other documents.
+
+3. The ManageDsaIT Control
+
+ The client may provide the ManageDsaIT control with an operation to
+ indicate that the operation is intended to manage objects within the
+ DSA (server) Information Tree. The control causes Directory-specific
+ entries (DSEs), regardless of type, to be treated as normal entries
+ allowing clients to interrogate and update these entries using LDAP
+ operations.
+
+ A client MAY specify the following control when issuing an add,
+ compare, delete, modify, modifyDN, search request or an extended
+ operation for which the control is defined.
+
+ The control type is 2.16.840.1.113730.3.4.2. The control criticality
+ may be TRUE or, if FALSE, absent. The control value is absent.
+
+ When the control is present in the request, the server SHALL NOT
+ generate a referral or continuation reference based upon information
+ held in referral objects and instead SHALL treat the referral object
+ as a normal entry. The server, however, is still free to return
+ referrals for other reasons. When not present, referral objects
+ SHALL be handled as described above.
+
+ The control MAY cause other objects to be treated as normal entries
+ as defined by subsequent documents.
+
+4. Named Subordinate References
+
+ A named subordinate reference is constructed by instantiating a
+ referral object in the referencing server with ref attribute values
+ which point to the corresponding subtree maintained in the referenced
+ server. In general, the name of the referral object is the same as
+ the referenced object and this referenced object is a context prefix
+ [X.501].
+
+
+
+
+Zeilenga Standards Track [Page 4]
+
+RFC 3296 Named Subordinate References in LDAP Directories July 2002
+
+
+ That is, if server A holds "DC=example,DC=net" and server B holds
+ "DC=sub,DC=example,DC=net", server A may contain a referral object
+ named "DC=sub,DC=example,DC=net" which contains a ref attribute with
+ value of "ldap://B/DC=sub,DC=example,DC=net".
+
+ dn: DC=sub,DC=example,DC=net
+ dc: sub
+ ref: ldap://B/DC=sub,DC=example,DC=net
+ objectClass: referral
+ objectClass: extensibleObject
+
+ Typically the DN of the referral object and the DN of the object in
+ the referenced server are the same.
+
+ If the ref attribute has multiple values, all the DNs contained
+ within the LDAP URLs SHOULD be equivalent. Administrators SHOULD
+ avoid configuring naming loops using referrals.
+
+ Named references MUST be treated as normal entries if the request
+ includes the ManageDsaIT control as described in section 3.
+
+5. Scenarios
+
+ The following sections contain specifications of how referral objects
+ should be used in different scenarios followed by examples that
+ illustrate that usage. The scenarios described here consist of
+ referral object handling when finding target of a non-search
+ operation, when finding the base of a search operation, and when
+ generating search references. Lastly, other operation processing
+ considerations are presented.
+
+ It is to be noted that, in this document, a search operation is
+ conceptually divided into two distinct, sequential phases: (1)
+ finding the base object where the search is to begin, and (2)
+ performing the search itself. The first phase is similar to, but not
+ the same as, finding the target of a non-search operation.
+
+ It should also be noted that the ref attribute may have multiple
+ values and, where these sections refer to a single ref attribute
+ value, multiple ref attribute values may be substituted and SHOULD be
+ processed and returned (in any order) as a group in a referral or
+ search reference in the same way as described for a single ref
+ attribute value.
+
+ Search references returned for a given request may be returned in any
+ order.
+
+
+
+
+
+Zeilenga Standards Track [Page 5]
+
+RFC 3296 Named Subordinate References in LDAP Directories July 2002
+
+
+5.1. Example Configuration
+
+ For example, suppose the contacted server (hosta) holds the entry
+ "O=MNN,C=WW" and the entry "CN=Manager,O=MNN,C=WW" and the following
+ referral objects:
+
+ dn: OU=People,O=MNN,C=WW
+ ou: People
+ ref: ldap://hostb/OU=People,O=MNN,C=US
+ ref: ldap://hostc/OU=People,O=MNN,C=US
+ objectClass: referral
+ objectClass: extensibleObject
+
+ dn: OU=Roles,O=MNN,C=WW
+ ou: Roles
+ ref: ldap://hostd/OU=Roles,O=MNN,C=WW
+ objectClass: referral
+ objectClass: extensibleObject
+
+ The first referral object provides the server with the knowledge that
+ subtree "OU=People,O=MNN,C=WW" is held by hostb and hostc (e.g., one
+ is the master and the other a shadow). The second referral object
+ provides the server with the knowledge that the subtree
+ "OU=Roles,O=MNN,C=WW" is held by hostd.
+
+ Also, in the context of this document, the "nearest naming context"
+ means the deepest context which the object is within. That is, if
+ the object is within multiple naming contexts, the nearest naming
+ context is the one which is subordinate to all other naming contexts
+ the object is within.
+
+5.2. Target Object Considerations
+
+ This section details referral handling for add, compare, delete,
+ modify, and modify DN operations. If the client requests any of
+ these operations, there are four cases that the server must handle
+ with respect to the target object.
+
+ The DN part MUST be modified such that it refers to the appropriate
+ target in the referenced server (as detailed below). Even where the
+ DN to be returned is the same as the target DN, the DN part SHOULD
+ NOT be trimmed.
+
+ In cases where the URI to be returned is a LDAP URL, the server
+ SHOULD trim any present scope, filter, or attribute list from the URI
+ before returning it. Critical extensions MUST NOT be trimmed or
+ modified.
+
+
+
+
+Zeilenga Standards Track [Page 6]
+
+RFC 3296 Named Subordinate References in LDAP Directories July 2002
+
+
+ Case 1: The target object is not held by the server and is not within
+ or subordinate to any naming context nor subordinate to any
+ referral object held by the server.
+
+ The server SHOULD process the request normally as appropriate for
+ a non-existent base which is not within any naming context of the
+ server (generally return noSuchObject or a referral based upon
+ superior knowledge reference information). This document does not
+ detail management or processing of superior knowledge reference
+ information.
+
+ Case 2: The target object is held by the server and is a referral
+ object.
+
+ The server SHOULD return the URI value contained in the ref
+ attribute of the referral object appropriately modified as
+ described above.
+
+ Example: If the client issues a modify request for the target object
+ of "OU=People,O=MNN,c=WW", the server will return:
+
+ ModifyResponse (referral) {
+ ldap://hostb/OU=People,O=MNN,C=WW
+ ldap://hostc/OU=People,O=MNN,C=WW
+ }
+
+ Case 3: The target object is not held by the server, but the nearest
+ naming context contains no referral object which the target object
+ is subordinate to.
+
+ If the nearest naming context contains no referral object which
+ the target is subordinate to, the server SHOULD process the
+ request as appropriate for a nonexistent target (generally return
+ noSuchObject).
+
+ Case 4: The target object is not held by the server, but the nearest
+ naming context contains a referral object which the target object
+ is subordinate to.
+
+ If a client requests an operation for which the target object is
+ not held by the server and the nearest naming context contains a
+ referral object which the target object is subordinate to, the
+ server SHOULD return a referral response constructed from the URI
+ portion of the ref value of the referral object.
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 7]
+
+RFC 3296 Named Subordinate References in LDAP Directories July 2002
+
+
+ Example: If the client issues an add request where the target object
+ has a DN of "CN=Manager,OU=Roles,O=MNN,C=WW", the server will
+ return:
+
+ AddResponse (referral) {
+ ldap://hostd/CN=Manager,OU=Roles,O=MNN,C=WW"
+ }
+
+ Note that the DN part of the LDAP URL is modified such that it
+ refers to the appropriate entry in the referenced server.
+
+5.3. Base Object Considerations
+
+ This section details referral handling for base object processing
+ within search operations. Like target object considerations for
+ non-search operations, there are the four cases.
+
+ In cases where the URI to be returned is a LDAP URL, the server MUST
+ provide an explicit scope specifier from the LDAP URL prior to
+ returning it. In addition, the DN part MUST be modified such that it
+ refers to the appropriate target in the referenced server (as
+ detailed below).
+
+ If aliasing dereferencing was necessary in finding the referral
+ object, the DN part of the URI MUST be replaced with the base DN as
+ modified by the alias dereferencing such that the return URL refers
+ to the new target object per [RFC2251, 4.1.11].
+
+ Critical extensions MUST NOT be trimmed nor modified.
+
+ Case 1: The base object is not held by the server and is not within
+ nor subordinate to any naming context held by the server.
+
+ The server SHOULD process the request normally as appropriate for
+ a non-existent base which not within any naming context of the
+ server (generally return a superior referral or noSuchObject).
+ This document does not detail management or processing of superior
+ knowledge references.
+
+ Case 2: The base object is held by the server and is a referral
+ object.
+
+ The server SHOULD return the URI value contained in the ref
+ attribute of the referral object appropriately modified as
+ described above.
+
+
+
+
+
+
+Zeilenga Standards Track [Page 8]
+
+RFC 3296 Named Subordinate References in LDAP Directories July 2002
+
+
+ Example: If the client issues a subtree search in which the base
+ object is "OU=Roles,O=MNN,C=WW", the server will return
+
+ SearchResultDone (referral) {
+ ldap://hostd/OU=Roles,O=MNN,C=WW??sub
+ }
+
+ If the client were to issue a base or oneLevel search instead of
+ subtree, the returned LDAP URL would explicitly specify "base" or
+ "one", respectively, instead of "sub".
+
+ Case 3: The base object is not held by the server, but the nearest
+ naming context contains no referral object which the base object
+ is subordinate to.
+
+ If the nearest naming context contains no referral object which
+ the base is subordinate to, the request SHOULD be processed
+ normally as appropriate for a nonexistent base (generally return
+ noSuchObject).
+
+ Case 4: The base object is not held by the server, but the nearest
+ naming context contains a referral object which the base object is
+ subordinate to.
+
+ If a client requests an operation for which the target object is
+ not held by the server and the nearest naming context contains a
+ referral object which the target object is subordinate to, the
+ server SHOULD return a referral response which is constructed from
+ the URI portion of the ref value of the referral object.
+
+ Example: If the client issues a base search request for
+ "CN=Manager,OU=Roles,O=MNN,C=WW", the server will return
+
+ SearchResultDone (referral) {
+ ldap://hostd/CN=Manager,OU=Roles,O=MNN,C=WW??base"
+ }
+
+ If the client were to issue a subtree or oneLevel search instead
+ of subtree, the returned LDAP URL would explicitly specify "sub"
+ or "one", respectively, instead of "base".
+
+ Note that the DN part of the LDAP URL is modified such that it
+ refers to the appropriate entry in the referenced server.
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 9]
+
+RFC 3296 Named Subordinate References in LDAP Directories July 2002
+
+
+5.4. Search Continuation Considerations
+
+ For search operations, once the base object has been found and
+ determined not to be a referral object, the search may progress. Any
+ entry matching the filter and scope of the search which is not a
+ referral object is returned to the client normally as described in
+ [RFC2251].
+
+ For each referral object within the requested scope, regardless of
+ the search filter, the server SHOULD return a SearchResultReference
+ which is constructed from the URI component of values of the ref
+ attribute. If the URI component is not a LDAP URL, it should be
+ returned as is. If the LDAP URL's DN part is absent or empty, the DN
+ part must be modified to contain the DN of the referral object. If
+ the URI component is a LDAP URL, the URI SHOULD be modified to add an
+ explicit scope specifier.
+
+ Subtree Example:
+
+ If a client requests a subtree search of "O=MNN,C=WW", then in
+ addition to any entries within scope which match the filter, hosta
+ will also return two search references as the two referral objects
+ are within scope. One possible response might be:
+
+ SearchEntry for O=MNN,C=WW
+ SearchResultReference {
+ ldap://hostb/OU=People,O=MNN,C=WW??sub
+ ldap://hostc/OU=People,O=MNN,C=WW??sub
+ }
+ SearchEntry for CN=Manager,O=MNN,C=WW
+ SearchResultReference {
+ ldap://hostd/OU=Roles,O=MNN,C=WW??sub
+ }
+ SearchResultDone (success)
+
+ One Level Example:
+
+ If a client requests a one level search of "O=MNN,C=WW" then, in
+ addition to any entries one level below the "O=MNN,C=WW" entry
+ matching the filter, the server will also return two search
+ references as the two referral objects are within scope. One
+ possible sequence is shown:
+
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 10]
+
+RFC 3296 Named Subordinate References in LDAP Directories July 2002
+
+
+ SearchResultReference {
+ ldap://hostb/OU=People,O=MNN,C=WW??base
+ ldap://hostc/OU=People,O=MNN,C=WW??base
+ }
+ SearchEntry for CN=Manager,O=MNN,C=WW
+ SearchResultReference {
+ ldap://hostd/OU=Roles,O=MNN,C=WW??base
+ }
+ SearchResultDone (success)
+
+ Note: Unlike the examples in Section 4.5.3.1 of RFC 2251, the LDAP
+ URLs returned with the SearchResultReference messages contain, as
+ required by this specification, an explicit scope specifier.
+
+5.6. Other Considerations
+
+ This section details processing considerations for other operations.
+
+5.6.1 Bind
+
+ Servers SHOULD NOT return referral result code if the bind name (or
+ authentication identity or authorization identity) is (or is
+ subordinate to) a referral object but MAY use the knowledge
+ information to process the bind request (such as in support a future
+ distributed operation specification). Where the server makes no use
+ of the knowledge information, the server processes the request
+ normally as appropriate for a non-existent authentication or
+ authorization identity (e.g., return invalidCredentials).
+
+5.6.2 Modify DN
+
+ If the newSuperior is a referral object or is subordinate to a
+ referral object, the server SHOULD return affectsMultipleDSAs. If
+ the newRDN already exists but is a referral object, the server SHOULD
+ return affectsMultipleDSAs instead of entryAlreadyExists.
+
+6. Security Considerations
+
+ This document defines mechanisms that can be used to tie LDAP (and
+ other) servers together. The information used to tie services
+ together should be protected from unauthorized modification. If the
+ server topology information is not public information, it should be
+ protected from unauthorized disclosure as well.
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 11]
+
+RFC 3296 Named Subordinate References in LDAP Directories July 2002
+
+
+7. Acknowledgments
+
+ This document borrows heavily from previous work by IETF LDAPext
+ Working Group. In particular, this document is based upon "Named
+ Referral in LDAP Directories" (an expired Internet Draft) by
+ Christopher Lukas, Tim Howes, Michael Roszkowski, Mark C. Smith, and
+ Mark Wahl.
+
+8. Normative References
+
+ [RFC2079] Smith, M., "Definition of an X.500 Attribute Type and an
+ Object Class to Hold Uniform Resource Identifiers (URIs)",
+ RFC 2079, January 1997.
+
+ [RFC2119] Bradner, S., "Key Words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [RFC2251] Wahl, M., Howes, T. and S. Kille, "Lightweight Directory
+ Access Protocol (v3)", RFC 2251, December 1997.
+
+ [RFC2252] Wahl, M., Coulbeck, A., Howes, T. and S. Kille,
+ "Lightweight Directory Access Protocol (v3): Attribute
+ Syntax Definitions", RFC 2252, December 1997.
+
+ [RFC2253] Wahl, M., Kille, S. and T. Howes, "Lightweight Directory
+ Access Protocol (v3): UTF-8 String Representation of
+ Distinguished Names", RFC 2253, December 1997.
+
+ [RFC2255] Howes, T. and M. Smith, "The LDAP URL Format", RFC 2255,
+ December, 1997.
+
+ [RFC2396] Berners-Lee, T., Fielding, R. and L. Masinter, "Uniform
+ Resource Identifiers (URI): Generic Syntax", RFC 2396,
+ August 1998.
+
+ [X.501] ITU-T, "The Directory: Models", X.501, 1993.
+
+9. Informative References
+
+ [X.500] ITU-T, "The Directory: Overview of Concepts, Models, and
+ Services", X.500, 1993.
+
+ [X.511] ITU-T, "The Directory: Abstract Service Definition", X.500,
+ 1997.
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 12]
+
+RFC 3296 Named Subordinate References in LDAP Directories July 2002
+
+
+10. Author's Address
+
+ Kurt D. Zeilenga
+ OpenLDAP Foundation
+
+ EMail: Kurt@OpenLDAP.org
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 13]
+
+RFC 3296 Named Subordinate References in LDAP Directories July 2002
+
+
+11. Full Copyright Statement
+
+ Copyright (C) The Internet Society (2002). All Rights Reserved.
+
+ This document and translations of it may be copied and furnished to
+ others, and derivative works that comment on or otherwise explain it
+ or assist in its implementation may be prepared, copied, published
+ and distributed, in whole or in part, without restriction of any
+ kind, provided that the above copyright notice and this paragraph are
+ included on all such copies and derivative works. However, this
+ document itself may not be modified in any way, such as by removing
+ the copyright notice or references to the Internet Society or other
+ Internet organizations, except as needed for the purpose of
+ developing Internet standards in which case the procedures for
+ copyrights defined in the Internet Standards process must be
+ followed, or as required to translate it into languages other than
+ English.
+
+ The limited permissions granted above are perpetual and will not be
+ revoked by the Internet Society or its successors or assigns.
+
+ This document and the information contained herein is provided on an
+ "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+ TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is currently provided by the
+ Internet Society.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 14]
+
diff --git a/source4/ldap_server/devdocs/rfc4510.txt b/source4/ldap_server/devdocs/rfc4510.txt
new file mode 100644
index 0000000000..8ba41d1d93
--- /dev/null
+++ b/source4/ldap_server/devdocs/rfc4510.txt
@@ -0,0 +1,395 @@
+
+
+
+
+
+
+Network Working Group K. Zeilenga, Ed.
+Request for Comments: 4510 OpenLDAP Foundation
+Obsoletes: 2251, 2252, 2253, 2254, 2255, June 2006
+ 2256, 2829, 2830, 3377, 3771
+Category: Standards Track
+
+
+ Lightweight Directory Access Protocol (LDAP):
+ Technical Specification Road Map
+
+Status of This Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2006).
+
+Abstract
+
+ The Lightweight Directory Access Protocol (LDAP) is an Internet
+ protocol for accessing distributed directory services that act in
+ accordance with X.500 data and service models. This document
+ provides a road map of the LDAP Technical Specification.
+
+1. The LDAP Technical Specification
+
+ The technical specification detailing version 3 of the Lightweight
+ Directory Access Protocol (LDAP), an Internet Protocol, consists of
+ this document and the following documents:
+
+ LDAP: The Protocol [RFC4511]
+ LDAP: Directory Information Models [RFC4512]
+ LDAP: Authentication Methods and Security Mechanisms [RFC4513]
+ LDAP: String Representation of Distinguished Names [RFC4514]
+ LDAP: String Representation of Search Filters [RFC4515]
+ LDAP: Uniform Resource Locator [RFC4516]
+ LDAP: Syntaxes and Matching Rules [RFC4517]
+ LDAP: Internationalized String Preparation [RFC4518]
+ LDAP: Schema for User Applications [RFC4519]
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 1]
+
+RFC 4510 LDAP: TS Road Map June 2006
+
+
+ The terms "LDAP" and "LDAPv3" are commonly used to refer informally
+ to the protocol specified by this technical specification. The LDAP
+ suite, as defined here, should be formally identified in other
+ documents by a normative reference to this document.
+
+ LDAP is an extensible protocol. Extensions to LDAP may be specified
+ in other documents. Nomenclature denoting such combinations of
+ LDAP-plus-extensions is not defined by this document but may be
+ defined in some future document(s). Extensions are expected to be
+ truly optional. Considerations for the LDAP extensions described in
+ BCP 118, RFC 4521 [RFC4521] fully apply to this revision of the LDAP
+ Technical Specification.
+
+ IANA (Internet Assigned Numbers Authority) considerations for LDAP
+ described in BCP 64, RFC 4520 [RFC4520] apply fully to this revision
+ of the LDAP technical specification.
+
+1.1. Conventions
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in BCP 14 [RFC2119].
+
+2. Relationship to X.500
+
+ This technical specification defines LDAP in terms of [X.500] as an
+ X.500 access mechanism. An LDAP server MUST act in accordance with
+ the X.500 (1993) series of International Telecommunication Union -
+ Telecommunication Standardization (ITU-T) Recommendations when
+ providing the service. However, it is not required that an LDAP
+ server make use of any X.500 protocols in providing this service.
+ For example, LDAP can be mapped onto any other directory system so
+ long as the X.500 data and service models [X.501][X.511], as used in
+ LDAP, are not violated in the LDAP interface.
+
+ This technical specification explicitly incorporates portions of
+ X.500(93). Later revisions of X.500 do not automatically apply to
+ this technical specification.
+
+3. Relationship to Obsolete Specifications
+
+ This technical specification, as defined in Section 1, obsoletes
+ entirely the previously defined LDAP technical specification defined
+ in RFC 3377 (and consisting of RFCs 2251-2256, 2829, 2830, 3771, and
+ 3377 itself). The technical specification was significantly
+ reorganized.
+
+
+
+
+
+Zeilenga Standards Track [Page 2]
+
+RFC 4510 LDAP: TS Road Map June 2006
+
+
+ This document replaces RFC 3377 as well as Section 3.3 of RFC 2251.
+ [RFC4512] replaces portions of RFC 2251, RFC 2252, and RFC 2256.
+ [RFC4511] replaces the majority RFC 2251, portions of RFC 2252, and
+ all of RFC 3771. [RFC4513] replaces RFC 2829, RFC 2830, and portions
+ of RFC 2251. [RFC4517] replaces the majority of RFC 2252 and
+ portions of RFC 2256. [RFC4519] replaces the majority of RFC 2256.
+ [RFC4514] replaces RFC 2253. [RFC4515] replaces RFC 2254. [RFC4516]
+ replaces RFC 2255.
+
+ [RFC4518] is new to this revision of the LDAP technical
+ specification.
+
+ Each document of this specification contains appendices summarizing
+ changes to all sections of the specifications they replace. Appendix
+ A.1 of this document details changes made to RFC 3377. Appendix A.2
+ of this document details changes made to Section 3.3 of RFC 2251.
+
+ Additionally, portions of this technical specification update and/or
+ replace a number of other documents not listed above. These
+ relationships are discussed in the documents detailing these portions
+ of this technical specification.
+
+4. Security Considerations
+
+ LDAP security considerations are discussed in each document
+ comprising the technical specification.
+
+5. Acknowledgements
+
+ This document is based largely on RFC 3377 by J. Hodges and R.
+ Morgan, a product of the LDAPBIS and LDAPEXT Working Groups. The
+ document also borrows from RFC 2251 by M. Wahl, T. Howes, and S.
+ Kille, a product of the ASID Working Group.
+
+ This document is a product of the IETF LDAPBIS Working Group.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 3]
+
+RFC 4510 LDAP: TS Road Map June 2006
+
+
+6. Normative References
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [RFC4511] Sermersheim, J., Ed., "Lightweight Directory Access
+ Protocol (LDAP): The Protocol", RFC 4511, June 2006.
+
+ [RFC4512] Zeilenga, K., "Lightweight Directory Access Protocol
+ (LDAP): Directory Information Models", RFC 4512, June
+ 2006.
+
+ [RFC4513] Harrison, R., Ed., "Lightweight Directory Access
+ Protocol (LDAP): Authentication Methods and Security
+ Mechanisms", RFC 4513, June 2006.
+
+ [RFC4514] Zeilenga, K., Ed., "Lightweight Directory Access
+ Protocol (LDAP): String Representation of Distinguished
+ Names", RFC 4514, June 2006.
+
+ [RFC4515] Smith, M., Ed. and T. Howes, "Lightweight Directory
+ Access Protocol (LDAP): String Representation of Search
+ Filters", RFC 4515, June 2006.
+
+ [RFC4516] Smith, M., Ed. and T. Howes, "Lightweight Directory
+ Access Protocol (LDAP): Uniform Resource Locator", RFC
+ 4516, June 2006.
+
+ [RFC4517] Legg, S., Ed., "Lightweight Directory Access Protocol
+ (LDAP): Syntaxes and Matching Rules", RFC 4517, June
+ 2006.
+
+ [RFC4518] Zeilenga, K., "Lightweight Directory Access Protocol
+ (LDAP): Internationalized String Preparation", RFC
+ 4518, June 2006.
+
+ [RFC4519] Sciberras, A., Ed., "Lightweight Directory Access
+ Protocol (LDAP): Schema for User Applications", RFC
+ 4519, June 2006.
+
+ [RFC4520] Zeilenga, K., "Internet Assigned Numbers Authority
+ (IANA) Considerations for the Lightweight Directory
+ Access Protocol (LDAP)", BCP 64, RFC 4520, June 2006.
+
+ [RFC4521] Zeilenga, K., "Considerations for LDAP Extensions", BCP
+ 118, RFC 4521, June 2006.
+
+
+
+
+
+Zeilenga Standards Track [Page 4]
+
+RFC 4510 LDAP: TS Road Map June 2006
+
+
+ [X.500] International Telecommunication Union -
+ Telecommunication Standardization Sector, "The
+ Directory -- Overview of concepts, models and
+ services", X.500(1993) (also ISO/IEC 9594-1:1994).
+
+ [X.501] International Telecommunication Union -
+ Telecommunication Standardization Sector, "The
+ Directory -- Models", X.501(1993) (also ISO/IEC 9594-
+ 2:1994).
+
+ [X.511] International Telecommunication Union -
+ Telecommunication Standardization Sector, "The
+ Directory: Abstract Service Definition", X.511(1993)
+ (also ISO/IEC 9594-3:1993).
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 5]
+
+RFC 4510 LDAP: TS Road Map June 2006
+
+
+Appendix A. Changes to Previous Documents
+
+ This appendix outlines changes this document makes relative to the
+ documents it replaces (in whole or in part).
+
+A.1. Changes to RFC 3377
+
+ This document is nearly a complete rewrite of RFC 3377 as much of the
+ material of RFC 3377 is no longer applicable. The changes include
+ redefining the terms "LDAP" and "LDAPv3" to refer to this revision of
+ the technical specification.
+
+A.2. Changes to Section 3.3 of RFC 2251
+
+ The section was modified slightly (the word "document" was replaced
+ with "technical specification") to clarify that it applies to the
+ entire LDAP technical specification.
+
+Author's Address
+
+ Kurt D. Zeilenga
+ OpenLDAP Foundation
+
+ EMail: Kurt@OpenLDAP.org
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 6]
+
+RFC 4510 LDAP: TS Road Map June 2006
+
+
+Full Copyright Statement
+
+ Copyright (C) The Internet Society (2006).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
+ ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
+ INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
+ INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be
+ found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at
+ ietf-ipr@ietf.org.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is provided by the IETF
+ Administrative Support Activity (IASA).
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 7]
+
diff --git a/source4/ldap_server/devdocs/rfc4511.txt b/source4/ldap_server/devdocs/rfc4511.txt
new file mode 100644
index 0000000000..8041f30544
--- /dev/null
+++ b/source4/ldap_server/devdocs/rfc4511.txt
@@ -0,0 +1,3811 @@
+
+
+
+
+
+
+Network Working Group J. Sermersheim, Ed.
+Request for Comments: 4511 Novell, Inc.
+Obsoletes: 2251, 2830, 3771 June 2006
+Category: Standards Track
+
+
+ Lightweight Directory Access Protocol (LDAP): The Protocol
+
+Status of This Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2006).
+
+Abstract
+
+ This document describes the protocol elements, along with their
+ semantics and encodings, of the Lightweight Directory Access Protocol
+ (LDAP). LDAP provides access to distributed directory services that
+ act in accordance with X.500 data and service models. These protocol
+ elements are based on those described in the X.500 Directory Access
+ Protocol (DAP).
+
+Table of Contents
+
+ 1. Introduction ....................................................3
+ 1.1. Relationship to Other LDAP Specifications ..................3
+ 2. Conventions .....................................................3
+ 3. Protocol Model ..................................................4
+ 3.1. Operation and LDAP Message Layer Relationship ..............5
+ 4. Elements of Protocol ............................................5
+ 4.1. Common Elements ............................................5
+ 4.1.1. Message Envelope ....................................6
+ 4.1.2. String Types ........................................7
+ 4.1.3. Distinguished Name and Relative Distinguished Name ..8
+ 4.1.4. Attribute Descriptions ..............................8
+ 4.1.5. Attribute Value .....................................8
+ 4.1.6. Attribute Value Assertion ...........................9
+ 4.1.7. Attribute and PartialAttribute ......................9
+ 4.1.8. Matching Rule Identifier ...........................10
+ 4.1.9. Result Message .....................................10
+ 4.1.10. Referral ..........................................12
+
+
+
+Sermersheim Standards Track [Page 1]
+
+RFC 4511 LDAPv3 June 2006
+
+
+ 4.1.11. Controls ..........................................14
+ 4.2. Bind Operation ............................................16
+ 4.2.1. Processing of the Bind Request .....................17
+ 4.2.2. Bind Response ......................................18
+ 4.3. Unbind Operation ..........................................18
+ 4.4. Unsolicited Notification ..................................19
+ 4.4.1. Notice of Disconnection ............................19
+ 4.5. Search Operation ..........................................20
+ 4.5.1. Search Request .....................................20
+ 4.5.2. Search Result ......................................27
+ 4.5.3. Continuation References in the Search Result .......28
+ 4.6. Modify Operation ..........................................31
+ 4.7. Add Operation .............................................33
+ 4.8. Delete Operation ..........................................34
+ 4.9. Modify DN Operation .......................................34
+ 4.10. Compare Operation ........................................36
+ 4.11. Abandon Operation ........................................36
+ 4.12. Extended Operation .......................................37
+ 4.13. IntermediateResponse Message .............................39
+ 4.13.1. Usage with LDAP ExtendedRequest and
+ ExtendedResponse ..................................40
+ 4.13.2. Usage with LDAP Request Controls ..................40
+ 4.14. StartTLS Operation .......................................40
+ 4.14.1. StartTLS Request ..................................40
+ 4.14.2. StartTLS Response .................................41
+ 4.14.3. Removal of the TLS Layer ..........................41
+ 5. Protocol Encoding, Connection, and Transfer ....................42
+ 5.1. Protocol Encoding .........................................42
+ 5.2. Transmission Control Protocol (TCP) .......................43
+ 5.3. Termination of the LDAP session ...........................43
+ 6. Security Considerations ........................................43
+ 7. Acknowledgements ...............................................45
+ 8. Normative References ...........................................46
+ 9. Informative References .........................................48
+ 10. IANA Considerations ...........................................48
+ Appendix A. LDAP Result Codes .....................................49
+ A.1. Non-Error Result Codes ....................................49
+ A.2. Result Codes ..............................................49
+ Appendix B. Complete ASN.1 Definition .............................54
+ Appendix C. Changes ...............................................60
+ C.1. Changes Made to RFC 2251 ..................................60
+ C.2. Changes Made to RFC 2830 ..................................66
+ C.3. Changes Made to RFC 3771 ..................................66
+
+
+
+
+
+
+
+
+Sermersheim Standards Track [Page 2]
+
+RFC 4511 LDAPv3 June 2006
+
+
+1. Introduction
+
+ The Directory is "a collection of open systems cooperating to provide
+ directory services" [X.500]. A directory user, which may be a human
+ or other entity, accesses the Directory through a client (or
+ Directory User Agent (DUA)). The client, on behalf of the directory
+ user, interacts with one or more servers (or Directory System Agents
+ (DSA)). Clients interact with servers using a directory access
+ protocol.
+
+ This document details the protocol elements of the Lightweight
+ Directory Access Protocol (LDAP), along with their semantics.
+ Following the description of protocol elements, it describes the way
+ in which the protocol elements are encoded and transferred.
+
+1.1. Relationship to Other LDAP Specifications
+
+ This document is an integral part of the LDAP Technical Specification
+ [RFC4510], which obsoletes the previously defined LDAP technical
+ specification, RFC 3377, in its entirety.
+
+ This document, together with [RFC4510], [RFC4513], and [RFC4512],
+ obsoletes RFC 2251 in its entirety. Section 3.3 is obsoleted by
+ [RFC4510]. Sections 4.2.1 (portions) and 4.2.2 are obsoleted by
+ [RFC4513]. Sections 3.2, 3.4, 4.1.3 (last paragraph), 4.1.4, 4.1.5,
+ 4.1.5.1, 4.1.9 (last paragraph), 5.1, 6.1, and 6.2 (last paragraph)
+ are obsoleted by [RFC4512]. The remainder of RFC 2251 is obsoleted
+ by this document. Appendix C.1 summarizes substantive changes in the
+ remainder.
+
+ This document obsoletes RFC 2830, Sections 2 and 4. The remainder of
+ RFC 2830 is obsoleted by [RFC4513]. Appendix C.2 summarizes
+ substantive changes to the remaining sections.
+
+ This document also obsoletes RFC 3771 in entirety.
+
+2. Conventions
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", and "MAY" in this document are
+ to be interpreted as described in [RFC2119].
+
+ Character names in this document use the notation for code points and
+ names from the Unicode Standard [Unicode]. For example, the letter
+ "a" may be represented as either <U+0061> or <LATIN SMALL LETTER A>.
+
+
+
+
+
+
+Sermersheim Standards Track [Page 3]
+
+RFC 4511 LDAPv3 June 2006
+
+
+ Note: a glossary of terms used in Unicode can be found in [Glossary].
+ Information on the Unicode character encoding model can be found in
+ [CharModel].
+
+ The term "transport connection" refers to the underlying transport
+ services used to carry the protocol exchange, as well as associations
+ established by these services.
+
+ The term "TLS layer" refers to Transport Layer Security (TLS)
+ services used in providing security services, as well as associations
+ established by these services.
+
+ The term "SASL layer" refers to Simply Authentication and Security
+ Layer (SASL) services used in providing security services, as well as
+ associations established by these services.
+
+ The term "LDAP message layer" refers to the LDAP Message Protocol
+ Data Unit (PDU) services used in providing directory services, as
+ well as associations established by these services.
+
+ The term "LDAP session" refers to combined services (transport
+ connection, TLS layer, SASL layer, LDAP message layer) and their
+ associations.
+
+ See the table in Section 5 for an illustration of these four terms.
+
+3. Protocol Model
+
+ The general model adopted by this protocol is one of clients
+ performing protocol operations against servers. In this model, a
+ client transmits a protocol request describing the operation to be
+ performed to a server. The server is then responsible for performing
+ the necessary operation(s) in the Directory. Upon completion of an
+ operation, the server typically returns a response containing
+ appropriate data to the requesting client.
+
+ Protocol operations are generally independent of one another. Each
+ operation is processed as an atomic action, leaving the directory in
+ a consistent state.
+
+ Although servers are required to return responses whenever such
+ responses are defined in the protocol, there is no requirement for
+ synchronous behavior on the part of either clients or servers.
+ Requests and responses for multiple operations generally may be
+ exchanged between a client and server in any order. If required,
+ synchronous behavior may be controlled by client applications.
+
+
+
+
+
+Sermersheim Standards Track [Page 4]
+
+RFC 4511 LDAPv3 June 2006
+
+
+ The core protocol operations defined in this document can be mapped
+ to a subset of the X.500 (1993) Directory Abstract Service [X.511].
+ However, there is not a one-to-one mapping between LDAP operations
+ and X.500 Directory Access Protocol (DAP) operations. Server
+ implementations acting as a gateway to X.500 directories may need to
+ make multiple DAP requests to service a single LDAP request.
+
+3.1. Operation and LDAP Message Layer Relationship
+
+ Protocol operations are exchanged at the LDAP message layer. When
+ the transport connection is closed, any uncompleted operations at the
+ LDAP message layer are abandoned (when possible) or are completed
+ without transmission of the response (when abandoning them is not
+ possible). Also, when the transport connection is closed, the client
+ MUST NOT assume that any uncompleted update operations have succeeded
+ or failed.
+
+4. Elements of Protocol
+
+ The protocol is described using Abstract Syntax Notation One
+ ([ASN.1]) and is transferred using a subset of ASN.1 Basic Encoding
+ Rules ([BER]). Section 5 specifies how the protocol elements are
+ encoded and transferred.
+
+ In order to support future extensions to this protocol, extensibility
+ is implied where it is allowed per ASN.1 (i.e., sequence, set,
+ choice, and enumerated types are extensible). In addition, ellipses
+ (...) have been supplied in ASN.1 types that are explicitly
+ extensible as discussed in [RFC4520]. Because of the implied
+ extensibility, clients and servers MUST (unless otherwise specified)
+ ignore trailing SEQUENCE components whose tags they do not recognize.
+
+ Changes to the protocol other than through the extension mechanisms
+ described here require a different version number. A client
+ indicates the version it is using as part of the BindRequest,
+ described in Section 4.2. If a client has not sent a Bind, the
+ server MUST assume the client is using version 3 or later.
+
+ Clients may attempt to determine the protocol versions a server
+ supports by reading the 'supportedLDAPVersion' attribute from the
+ root DSE (DSA-Specific Entry) [RFC4512].
+
+4.1. Common Elements
+
+ This section describes the LDAPMessage envelope Protocol Data Unit
+ (PDU) format, as well as data type definitions, which are used in the
+ protocol operations.
+
+
+
+
+Sermersheim Standards Track [Page 5]
+
+RFC 4511 LDAPv3 June 2006
+
+
+4.1.1. Message Envelope
+
+ For the purposes of protocol exchanges, all protocol operations are
+ encapsulated in a common envelope, the LDAPMessage, which is defined
+ as follows:
+
+ LDAPMessage ::= SEQUENCE {
+ messageID MessageID,
+ protocolOp CHOICE {
+ bindRequest BindRequest,
+ bindResponse BindResponse,
+ unbindRequest UnbindRequest,
+ searchRequest SearchRequest,
+ searchResEntry SearchResultEntry,
+ searchResDone SearchResultDone,
+ searchResRef SearchResultReference,
+ modifyRequest ModifyRequest,
+ modifyResponse ModifyResponse,
+ addRequest AddRequest,
+ addResponse AddResponse,
+ delRequest DelRequest,
+ delResponse DelResponse,
+ modDNRequest ModifyDNRequest,
+ modDNResponse ModifyDNResponse,
+ compareRequest CompareRequest,
+ compareResponse CompareResponse,
+ abandonRequest AbandonRequest,
+ extendedReq ExtendedRequest,
+ extendedResp ExtendedResponse,
+ ...,
+ intermediateResponse IntermediateResponse },
+ controls [0] Controls OPTIONAL }
+
+ MessageID ::= INTEGER (0 .. maxInt)
+
+ maxInt INTEGER ::= 2147483647 -- (2^^31 - 1) --
+
+ The ASN.1 type Controls is defined in Section 4.1.11.
+
+ The function of the LDAPMessage is to provide an envelope containing
+ common fields required in all protocol exchanges. At this time, the
+ only common fields are the messageID and the controls.
+
+ If the server receives an LDAPMessage from the client in which the
+ LDAPMessage SEQUENCE tag cannot be recognized, the messageID cannot
+ be parsed, the tag of the protocolOp is not recognized as a request,
+ or the encoding structures or lengths of data fields are found to be
+ incorrect, then the server SHOULD return the Notice of Disconnection
+
+
+
+Sermersheim Standards Track [Page 6]
+
+RFC 4511 LDAPv3 June 2006
+
+
+ described in Section 4.4.1, with the resultCode set to protocolError,
+ and MUST immediately terminate the LDAP session as described in
+ Section 5.3.
+
+ In other cases where the client or server cannot parse an LDAP PDU,
+ it SHOULD abruptly terminate the LDAP session (Section 5.3) where
+ further communication (including providing notice) would be
+ pernicious. Otherwise, server implementations MUST return an
+ appropriate response to the request, with the resultCode set to
+ protocolError.
+
+4.1.1.1. MessageID
+
+ All LDAPMessage envelopes encapsulating responses contain the
+ messageID value of the corresponding request LDAPMessage.
+
+ The messageID of a request MUST have a non-zero value different from
+ the messageID of any other request in progress in the same LDAP
+ session. The zero value is reserved for the unsolicited notification
+ message.
+
+ Typical clients increment a counter for each request.
+
+ A client MUST NOT send a request with the same messageID as an
+ earlier request in the same LDAP session unless it can be determined
+ that the server is no longer servicing the earlier request (e.g.,
+ after the final response is received, or a subsequent Bind
+ completes). Otherwise, the behavior is undefined. For this purpose,
+ note that Abandon and successfully abandoned operations do not send
+ responses.
+
+4.1.2. String Types
+
+ The LDAPString is a notational convenience to indicate that, although
+ strings of LDAPString type encode as ASN.1 OCTET STRING types, the
+ [ISO10646] character set (a superset of [Unicode]) is used, encoded
+ following the UTF-8 [RFC3629] algorithm. Note that Unicode
+ characters U+0000 through U+007F are the same as ASCII 0 through 127,
+ respectively, and have the same single octet UTF-8 encoding. Other
+ Unicode characters have a multiple octet UTF-8 encoding.
+
+ LDAPString ::= OCTET STRING -- UTF-8 encoded,
+ -- [ISO10646] characters
+
+ The LDAPOID is a notational convenience to indicate that the
+ permitted value of this string is a (UTF-8 encoded) dotted-decimal
+ representation of an OBJECT IDENTIFIER. Although an LDAPOID is
+
+
+
+
+Sermersheim Standards Track [Page 7]
+
+RFC 4511 LDAPv3 June 2006
+
+
+ encoded as an OCTET STRING, values are limited to the definition of
+ <numericoid> given in Section 1.4 of [RFC4512].
+
+ LDAPOID ::= OCTET STRING -- Constrained to <numericoid>
+ -- [RFC4512]
+
+ For example,
+
+ 1.3.6.1.4.1.1466.1.2.3
+
+4.1.3. Distinguished Name and Relative Distinguished Name
+
+ An LDAPDN is defined to be the representation of a Distinguished Name
+ (DN) after encoding according to the specification in [RFC4514].
+
+ LDAPDN ::= LDAPString
+ -- Constrained to <distinguishedName> [RFC4514]
+
+ A RelativeLDAPDN is defined to be the representation of a Relative
+ Distinguished Name (RDN) after encoding according to the
+ specification in [RFC4514].
+
+ RelativeLDAPDN ::= LDAPString
+ -- Constrained to <name-component> [RFC4514]
+
+4.1.4. Attribute Descriptions
+
+ The definition and encoding rules for attribute descriptions are
+ defined in Section 2.5 of [RFC4512]. Briefly, an attribute
+ description is an attribute type and zero or more options.
+
+ AttributeDescription ::= LDAPString
+ -- Constrained to <attributedescription>
+ -- [RFC4512]
+
+4.1.5. Attribute Value
+
+ A field of type AttributeValue is an OCTET STRING containing an
+ encoded attribute value. The attribute value is encoded according to
+ the LDAP-specific encoding definition of its corresponding syntax.
+ The LDAP-specific encoding definitions for different syntaxes and
+ attribute types may be found in other documents and in particular
+ [RFC4517].
+
+ AttributeValue ::= OCTET STRING
+
+
+
+
+
+
+Sermersheim Standards Track [Page 8]
+
+RFC 4511 LDAPv3 June 2006
+
+
+ Note that there is no defined limit on the size of this encoding;
+ thus, protocol values may include multi-megabyte attribute values
+ (e.g., photographs).
+
+ Attribute values may be defined that have arbitrary and non-printable
+ syntax. Implementations MUST NOT display or attempt to decode an
+ attribute value if its syntax is not known. The implementation may
+ attempt to discover the subschema of the source entry and to retrieve
+ the descriptions of 'attributeTypes' from it [RFC4512].
+
+ Clients MUST only send attribute values in a request that are valid
+ according to the syntax defined for the attributes.
+
+4.1.6. Attribute Value Assertion
+
+ The AttributeValueAssertion (AVA) type definition is similar to the
+ one in the X.500 Directory standards. It contains an attribute
+ description and a matching rule ([RFC4512], Section 4.1.3) assertion
+ value suitable for that type. Elements of this type are typically
+ used to assert that the value in assertionValue matches a value of an
+ attribute.
+
+ AttributeValueAssertion ::= SEQUENCE {
+ attributeDesc AttributeDescription,
+ assertionValue AssertionValue }
+
+ AssertionValue ::= OCTET STRING
+
+ The syntax of the AssertionValue depends on the context of the LDAP
+ operation being performed. For example, the syntax of the EQUALITY
+ matching rule for an attribute is used when performing a Compare
+ operation. Often this is the same syntax used for values of the
+ attribute type, but in some cases the assertion syntax differs from
+ the value syntax. See objectIdentiferFirstComponentMatch in
+ [RFC4517] for an example.
+
+4.1.7. Attribute and PartialAttribute
+
+ Attributes and partial attributes consist of an attribute description
+ and attribute values. A PartialAttribute allows zero values, while
+ Attribute requires at least one value.
+
+ PartialAttribute ::= SEQUENCE {
+ type AttributeDescription,
+ vals SET OF value AttributeValue }
+
+
+
+
+
+
+Sermersheim Standards Track [Page 9]
+
+RFC 4511 LDAPv3 June 2006
+
+
+ Attribute ::= PartialAttribute(WITH COMPONENTS {
+ ...,
+ vals (SIZE(1..MAX))})
+
+ No two of the attribute values may be equivalent as described by
+ Section 2.2 of [RFC4512]. The set of attribute values is unordered.
+ Implementations MUST NOT rely upon the ordering being repeatable.
+
+4.1.8. Matching Rule Identifier
+
+ Matching rules are defined in Section 4.1.3 of [RFC4512]. A matching
+ rule is identified in the protocol by the printable representation of
+ either its <numericoid> or one of its short name descriptors
+ [RFC4512], e.g., 'caseIgnoreMatch' or '2.5.13.2'.
+
+ MatchingRuleId ::= LDAPString
+
+4.1.9. Result Message
+
+ The LDAPResult is the construct used in this protocol to return
+ success or failure indications from servers to clients. To various
+ requests, servers will return responses containing the elements found
+ in LDAPResult to indicate the final status of the protocol operation
+ request.
+
+ LDAPResult ::= SEQUENCE {
+ resultCode ENUMERATED {
+ success (0),
+ operationsError (1),
+ protocolError (2),
+ timeLimitExceeded (3),
+ sizeLimitExceeded (4),
+ compareFalse (5),
+ compareTrue (6),
+ authMethodNotSupported (7),
+ strongerAuthRequired (8),
+ -- 9 reserved --
+ referral (10),
+ adminLimitExceeded (11),
+ unavailableCriticalExtension (12),
+ confidentialityRequired (13),
+ saslBindInProgress (14),
+ noSuchAttribute (16),
+ undefinedAttributeType (17),
+ inappropriateMatching (18),
+ constraintViolation (19),
+ attributeOrValueExists (20),
+ invalidAttributeSyntax (21),
+
+
+
+Sermersheim Standards Track [Page 10]
+
+RFC 4511 LDAPv3 June 2006
+
+
+ -- 22-31 unused --
+ noSuchObject (32),
+ aliasProblem (33),
+ invalidDNSyntax (34),
+ -- 35 reserved for undefined isLeaf --
+ aliasDereferencingProblem (36),
+ -- 37-47 unused --
+ inappropriateAuthentication (48),
+ invalidCredentials (49),
+ insufficientAccessRights (50),
+ busy (51),
+ unavailable (52),
+ unwillingToPerform (53),
+ loopDetect (54),
+ -- 55-63 unused --
+ namingViolation (64),
+ objectClassViolation (65),
+ notAllowedOnNonLeaf (66),
+ notAllowedOnRDN (67),
+ entryAlreadyExists (68),
+ objectClassModsProhibited (69),
+ -- 70 reserved for CLDAP --
+ affectsMultipleDSAs (71),
+ -- 72-79 unused --
+ other (80),
+ ... },
+ matchedDN LDAPDN,
+ diagnosticMessage LDAPString,
+ referral [3] Referral OPTIONAL }
+
+ The resultCode enumeration is extensible as defined in Section 3.8 of
+ [RFC4520]. The meanings of the listed result codes are given in
+ Appendix A. If a server detects multiple errors for an operation,
+ only one result code is returned. The server should return the
+ result code that best indicates the nature of the error encountered.
+ Servers may return substituted result codes to prevent unauthorized
+ disclosures.
+
+ The diagnosticMessage field of this construct may, at the server's
+ option, be used to return a string containing a textual, human-
+ readable diagnostic message (terminal control and page formatting
+ characters should be avoided). As this diagnostic message is not
+ standardized, implementations MUST NOT rely on the values returned.
+ Diagnostic messages typically supplement the resultCode with
+ additional information. If the server chooses not to return a
+ textual diagnostic, the diagnosticMessage field MUST be empty.
+
+
+
+
+
+Sermersheim Standards Track [Page 11]
+
+RFC 4511 LDAPv3 June 2006
+
+
+ For certain result codes (typically, but not restricted to
+ noSuchObject, aliasProblem, invalidDNSyntax, and
+ aliasDereferencingProblem), the matchedDN field is set (subject to
+ access controls) to the name of the last entry (object or alias) used
+ in finding the target (or base) object. This will be a truncated
+ form of the provided name or, if an alias was dereferenced while
+ attempting to locate the entry, of the resulting name. Otherwise,
+ the matchedDN field is empty.
+
+4.1.10. Referral
+
+ The referral result code indicates that the contacted server cannot
+ or will not perform the operation and that one or more other servers
+ may be able to. Reasons for this include:
+
+ - The target entry of the request is not held locally, but the server
+ has knowledge of its possible existence elsewhere.
+
+ - The operation is restricted on this server -- perhaps due to a
+ read-only copy of an entry to be modified.
+
+ The referral field is present in an LDAPResult if the resultCode is
+ set to referral, and it is absent with all other result codes. It
+ contains one or more references to one or more servers or services
+ that may be accessed via LDAP or other protocols. Referrals can be
+ returned in response to any operation request (except Unbind and
+ Abandon, which do not have responses). At least one URI MUST be
+ present in the Referral.
+
+ During a Search operation, after the baseObject is located, and
+ entries are being evaluated, the referral is not returned. Instead,
+ continuation references, described in Section 4.5.3, are returned
+ when other servers would need to be contacted to complete the
+ operation.
+
+ Referral ::= SEQUENCE SIZE (1..MAX) OF uri URI
+
+ URI ::= LDAPString -- limited to characters permitted in
+ -- URIs
+
+ If the client wishes to progress the operation, it contacts one of
+ the supported services found in the referral. If multiple URIs are
+ present, the client assumes that any supported URI may be used to
+ progress the operation.
+
+ Clients that follow referrals MUST ensure that they do not loop
+ between servers. They MUST NOT repeatedly contact the same server
+ for the same request with the same parameters. Some clients use a
+
+
+
+Sermersheim Standards Track [Page 12]
+
+RFC 4511 LDAPv3 June 2006
+
+
+ counter that is incremented each time referral handling occurs for an
+ operation, and these kinds of clients MUST be able to handle at least
+ ten nested referrals while progressing the operation.
+
+ A URI for a server implementing LDAP and accessible via TCP/IP (v4 or
+ v6) [RFC793][RFC791] is written as an LDAP URL according to
+ [RFC4516].
+
+ Referral values that are LDAP URLs follow these rules:
+
+ - If an alias was dereferenced, the <dn> part of the LDAP URL MUST be
+ present, with the new target object name.
+
+ - It is RECOMMENDED that the <dn> part be present to avoid ambiguity.
+
+ - If the <dn> part is present, the client uses this name in its next
+ request to progress the operation, and if it is not present the
+ client uses the same name as in the original request.
+
+ - Some servers (e.g., participating in distributed indexing) may
+ provide a different filter in a URL of a referral for a Search
+ operation.
+
+ - If the <filter> part of the LDAP URL is present, the client uses
+ this filter in its next request to progress this Search, and if it
+ is not present the client uses the same filter as it used for that
+ Search.
+
+ - For Search, it is RECOMMENDED that the <scope> part be present to
+ avoid ambiguity.
+
+ - If the <scope> part is missing, the scope of the original Search is
+ used by the client to progress the operation.
+
+ - Other aspects of the new request may be the same as or different
+ from the request that generated the referral.
+
+ Other kinds of URIs may be returned. The syntax and semantics of
+ such URIs is left to future specifications. Clients may ignore URIs
+ that they do not support.
+
+ UTF-8 encoded characters appearing in the string representation of a
+ DN, search filter, or other fields of the referral value may not be
+ legal for URIs (e.g., spaces) and MUST be escaped using the % method
+ in [RFC3986].
+
+
+
+
+
+
+Sermersheim Standards Track [Page 13]
+
+RFC 4511 LDAPv3 June 2006
+
+
+4.1.11. Controls
+
+ Controls provide a mechanism whereby the semantics and arguments of
+ existing LDAP operations may be extended. One or more controls may
+ be attached to a single LDAP message. A control only affects the
+ semantics of the message it is attached to.
+
+ Controls sent by clients are termed 'request controls', and those
+ sent by servers are termed 'response controls'.
+
+ Controls ::= SEQUENCE OF control Control
+
+ Control ::= SEQUENCE {
+ controlType LDAPOID,
+ criticality BOOLEAN DEFAULT FALSE,
+ controlValue OCTET STRING OPTIONAL }
+
+ The controlType field is the dotted-decimal representation of an
+ OBJECT IDENTIFIER that uniquely identifies the control. This
+ provides unambiguous naming of controls. Often, response control(s)
+ solicited by a request control share controlType values with the
+ request control.
+
+ The criticality field only has meaning in controls attached to
+ request messages (except UnbindRequest). For controls attached to
+ response messages and the UnbindRequest, the criticality field SHOULD
+ be FALSE, and MUST be ignored by the receiving protocol peer. A
+ value of TRUE indicates that it is unacceptable to perform the
+ operation without applying the semantics of the control.
+ Specifically, the criticality field is applied as follows:
+
+ - If the server does not recognize the control type, determines that
+ it is not appropriate for the operation, or is otherwise unwilling
+ to perform the operation with the control, and if the criticality
+ field is TRUE, the server MUST NOT perform the operation, and for
+ operations that have a response message, it MUST return with the
+ resultCode set to unavailableCriticalExtension.
+
+ - If the server does not recognize the control type, determines that
+ it is not appropriate for the operation, or is otherwise unwilling
+ to perform the operation with the control, and if the criticality
+ field is FALSE, the server MUST ignore the control.
+
+ - Regardless of criticality, if a control is applied to an
+ operation, it is applied consistently and impartially to the
+ entire operation.
+
+
+
+
+
+Sermersheim Standards Track [Page 14]
+
+RFC 4511 LDAPv3 June 2006
+
+
+ The controlValue may contain information associated with the
+ controlType. Its format is defined by the specification of the
+ control. Implementations MUST be prepared to handle arbitrary
+ contents of the controlValue octet string, including zero bytes. It
+ is absent only if there is no value information that is associated
+ with a control of its type. When a controlValue is defined in terms
+ of ASN.1, and BER-encoded according to Section 5.1, it also follows
+ the extensibility rules in Section 4.
+
+ Servers list the controlType of request controls they recognize in
+ the 'supportedControl' attribute in the root DSE (Section 5.1 of
+ [RFC4512]).
+
+ Controls SHOULD NOT be combined unless the semantics of the
+ combination has been specified. The semantics of control
+ combinations, if specified, are generally found in the control
+ specification most recently published. When a combination of
+ controls is encountered whose semantics are invalid, not specified
+ (or not known), the message is considered not well-formed; thus, the
+ operation fails with protocolError. Controls with a criticality of
+ FALSE may be ignored in order to arrive at a valid combination.
+ Additionally, unless order-dependent semantics are given in a
+ specification, the order of a combination of controls in the SEQUENCE
+ is ignored. Where the order is to be ignored but cannot be ignored
+ by the server, the message is considered not well-formed, and the
+ operation fails with protocolError. Again, controls with a
+ criticality of FALSE may be ignored in order to arrive at a valid
+ combination.
+
+ This document does not specify any controls. Controls may be
+ specified in other documents. Documents detailing control extensions
+ are to provide for each control:
+
+ - the OBJECT IDENTIFIER assigned to the control,
+
+ - direction as to what value the sender should provide for the
+ criticality field (note: the semantics of the criticality field are
+ defined above should not be altered by the control's
+ specification),
+
+ - whether the controlValue field is present, and if so, the format of
+ its contents,
+
+ - the semantics of the control, and
+
+ - optionally, semantics regarding the combination of the control with
+ other controls.
+
+
+
+
+Sermersheim Standards Track [Page 15]
+
+RFC 4511 LDAPv3 June 2006
+
+
+4.2. Bind Operation
+
+ The function of the Bind operation is to allow authentication
+ information to be exchanged between the client and server. The Bind
+ operation should be thought of as the "authenticate" operation.
+ Operational, authentication, and security-related semantics of this
+ operation are given in [RFC4513].
+
+ The Bind request is defined as follows:
+
+ BindRequest ::= [APPLICATION 0] SEQUENCE {
+ version INTEGER (1 .. 127),
+ name LDAPDN,
+ authentication AuthenticationChoice }
+
+ AuthenticationChoice ::= CHOICE {
+ simple [0] OCTET STRING,
+ -- 1 and 2 reserved
+ sasl [3] SaslCredentials,
+ ... }
+
+ SaslCredentials ::= SEQUENCE {
+ mechanism LDAPString,
+ credentials OCTET STRING OPTIONAL }
+
+ Fields of the BindRequest are:
+
+ - version: A version number indicating the version of the protocol to
+ be used at the LDAP message layer. This document describes version
+ 3 of the protocol. There is no version negotiation. The client
+ sets this field to the version it desires. If the server does not
+ support the specified version, it MUST respond with a BindResponse
+ where the resultCode is set to protocolError.
+
+ - name: If not empty, the name of the Directory object that the
+ client wishes to bind as. This field may take on a null value (a
+ zero-length string) for the purposes of anonymous binds ([RFC4513],
+ Section 5.1) or when using SASL [RFC4422] authentication
+ ([RFC4513], Section 5.2). Where the server attempts to locate the
+ named object, it SHALL NOT perform alias dereferencing.
+
+ - authentication: Information used in authentication. This type is
+ extensible as defined in Section 3.7 of [RFC4520]. Servers that do
+ not support a choice supplied by a client return a BindResponse
+ with the resultCode set to authMethodNotSupported.
+
+
+
+
+
+
+Sermersheim Standards Track [Page 16]
+
+RFC 4511 LDAPv3 June 2006
+
+
+ Textual passwords (consisting of a character sequence with a known
+ character set and encoding) transferred to the server using the
+ simple AuthenticationChoice SHALL be transferred as UTF-8 [RFC3629]
+ encoded [Unicode]. Prior to transfer, clients SHOULD prepare text
+ passwords as "query" strings by applying the SASLprep [RFC4013]
+ profile of the stringprep [RFC3454] algorithm. Passwords
+ consisting of other data (such as random octets) MUST NOT be
+ altered. The determination of whether a password is textual is a
+ local client matter.
+
+4.2.1. Processing of the Bind Request
+
+ Before processing a BindRequest, all uncompleted operations MUST
+ either complete or be abandoned. The server may either wait for the
+ uncompleted operations to complete, or abandon them. The server then
+ proceeds to authenticate the client in either a single-step or
+ multi-step Bind process. Each step requires the server to return a
+ BindResponse to indicate the status of authentication.
+
+ After sending a BindRequest, clients MUST NOT send further LDAP PDUs
+ until receiving the BindResponse. Similarly, servers SHOULD NOT
+ process or respond to requests received while processing a
+ BindRequest.
+
+ If the client did not bind before sending a request and receives an
+ operationsError to that request, it may then send a BindRequest. If
+ this also fails or the client chooses not to bind on the existing
+ LDAP session, it may terminate the LDAP session, re-establish it, and
+ begin again by first sending a BindRequest. This will aid in
+ interoperating with servers implementing other versions of LDAP.
+
+ Clients may send multiple Bind requests to change the authentication
+ and/or security associations or to complete a multi-stage Bind
+ process. Authentication from earlier binds is subsequently ignored.
+
+ For some SASL authentication mechanisms, it may be necessary for the
+ client to invoke the BindRequest multiple times ([RFC4513], Section
+ 5.2). Clients MUST NOT invoke operations between two Bind requests
+ made as part of a multi-stage Bind.
+
+ A client may abort a SASL bind negotiation by sending a BindRequest
+ with a different value in the mechanism field of SaslCredentials, or
+ an AuthenticationChoice other than sasl.
+
+
+
+
+
+
+
+
+Sermersheim Standards Track [Page 17]
+
+RFC 4511 LDAPv3 June 2006
+
+
+ If the client sends a BindRequest with the sasl mechanism field as an
+ empty string, the server MUST return a BindResponse with the
+ resultCode set to authMethodNotSupported. This will allow the client
+ to abort a negotiation if it wishes to try again with the same SASL
+ mechanism.
+
+4.2.2. Bind Response
+
+ The Bind response is defined as follows.
+
+ BindResponse ::= [APPLICATION 1] SEQUENCE {
+ COMPONENTS OF LDAPResult,
+ serverSaslCreds [7] OCTET STRING OPTIONAL }
+
+ BindResponse consists simply of an indication from the server of the
+ status of the client's request for authentication.
+
+ A successful Bind operation is indicated by a BindResponse with a
+ resultCode set to success. Otherwise, an appropriate result code is
+ set in the BindResponse. For BindResponse, the protocolError result
+ code may be used to indicate that the version number supplied by the
+ client is unsupported.
+
+ If the client receives a BindResponse where the resultCode is set to
+ protocolError, it is to assume that the server does not support this
+ version of LDAP. While the client may be able proceed with another
+ version of this protocol (which may or may not require closing and
+ re-establishing the transport connection), how to proceed with
+ another version of this protocol is beyond the scope of this
+ document. Clients that are unable or unwilling to proceed SHOULD
+ terminate the LDAP session.
+
+ The serverSaslCreds field is used as part of a SASL-defined bind
+ mechanism to allow the client to authenticate the server to which it
+ is communicating, or to perform "challenge-response" authentication.
+ If the client bound with the simple choice, or the SASL mechanism
+ does not require the server to return information to the client, then
+ this field SHALL NOT be included in the BindResponse.
+
+4.3. Unbind Operation
+
+ The function of the Unbind operation is to terminate an LDAP session.
+ The Unbind operation is not the antithesis of the Bind operation as
+ the name implies. The naming of these operations are historical.
+ The Unbind operation should be thought of as the "quit" operation.
+
+
+
+
+
+
+Sermersheim Standards Track [Page 18]
+
+RFC 4511 LDAPv3 June 2006
+
+
+ The Unbind operation is defined as follows:
+
+ UnbindRequest ::= [APPLICATION 2] NULL
+
+ The client, upon transmission of the UnbindRequest, and the server,
+ upon receipt of the UnbindRequest, are to gracefully terminate the
+ LDAP session as described in Section 5.3. Uncompleted operations are
+ handled as specified in Section 3.1.
+
+4.4. Unsolicited Notification
+
+ An unsolicited notification is an LDAPMessage sent from the server to
+ the client that is not in response to any LDAPMessage received by the
+ server. It is used to signal an extraordinary condition in the
+ server or in the LDAP session between the client and the server. The
+ notification is of an advisory nature, and the server will not expect
+ any response to be returned from the client.
+
+ The unsolicited notification is structured as an LDAPMessage in which
+ the messageID is zero and protocolOp is set to the extendedResp
+ choice using the ExtendedResponse type (See Section 4.12). The
+ responseName field of the ExtendedResponse always contains an LDAPOID
+ that is unique for this notification.
+
+ One unsolicited notification (Notice of Disconnection) is defined in
+ this document. The specification of an unsolicited notification
+ consists of:
+
+ - the OBJECT IDENTIFIER assigned to the notification (to be specified
+ in the responseName,
+
+ - the format of the contents of the responseValue (if any),
+
+ - the circumstances which will cause the notification to be sent, and
+
+ - the semantics of the message.
+
+4.4.1. Notice of Disconnection
+
+ This notification may be used by the server to advise the client that
+ the server is about to terminate the LDAP session on its own
+ initiative. This notification is intended to assist clients in
+ distinguishing between an exceptional server condition and a
+ transient network failure. Note that this notification is not a
+ response to an Unbind requested by the client. Uncompleted
+ operations are handled as specified in Section 3.1.
+
+
+
+
+
+Sermersheim Standards Track [Page 19]
+
+RFC 4511 LDAPv3 June 2006
+
+
+ The responseName is 1.3.6.1.4.1.1466.20036, the responseValue field
+ is absent, and the resultCode is used to indicate the reason for the
+ disconnection. When the strongerAuthRequired resultCode is returned
+ with this message, it indicates that the server has detected that an
+ established security association between the client and server has
+ unexpectedly failed or been compromised.
+
+ Upon transmission of the Notice of Disconnection, the server
+ gracefully terminates the LDAP session as described in Section 5.3.
+
+4.5. Search Operation
+
+ The Search operation is used to request a server to return, subject
+ to access controls and other restrictions, a set of entries matching
+ a complex search criterion. This can be used to read attributes from
+ a single entry, from entries immediately subordinate to a particular
+ entry, or from a whole subtree of entries.
+
+4.5.1. Search Request
+
+ The Search request is defined as follows:
+
+ SearchRequest ::= [APPLICATION 3] SEQUENCE {
+ baseObject LDAPDN,
+ scope ENUMERATED {
+ baseObject (0),
+ singleLevel (1),
+ wholeSubtree (2),
+ ... },
+ derefAliases ENUMERATED {
+ neverDerefAliases (0),
+ derefInSearching (1),
+ derefFindingBaseObj (2),
+ derefAlways (3) },
+ sizeLimit INTEGER (0 .. maxInt),
+ timeLimit INTEGER (0 .. maxInt),
+ typesOnly BOOLEAN,
+ filter Filter,
+ attributes AttributeSelection }
+
+ AttributeSelection ::= SEQUENCE OF selector LDAPString
+ -- The LDAPString is constrained to
+ -- <attributeSelector> in Section 4.5.1.8
+
+ Filter ::= CHOICE {
+ and [0] SET SIZE (1..MAX) OF filter Filter,
+ or [1] SET SIZE (1..MAX) OF filter Filter,
+ not [2] Filter,
+
+
+
+Sermersheim Standards Track [Page 20]
+
+RFC 4511 LDAPv3 June 2006
+
+
+ equalityMatch [3] AttributeValueAssertion,
+ substrings [4] SubstringFilter,
+ greaterOrEqual [5] AttributeValueAssertion,
+ lessOrEqual [6] AttributeValueAssertion,
+ present [7] AttributeDescription,
+ approxMatch [8] AttributeValueAssertion,
+ extensibleMatch [9] MatchingRuleAssertion,
+ ... }
+
+ SubstringFilter ::= SEQUENCE {
+ type AttributeDescription,
+ substrings SEQUENCE SIZE (1..MAX) OF substring CHOICE {
+ initial [0] AssertionValue, -- can occur at most once
+ any [1] AssertionValue,
+ final [2] AssertionValue } -- can occur at most once
+ }
+
+ MatchingRuleAssertion ::= SEQUENCE {
+ matchingRule [1] MatchingRuleId OPTIONAL,
+ type [2] AttributeDescription OPTIONAL,
+ matchValue [3] AssertionValue,
+ dnAttributes [4] BOOLEAN DEFAULT FALSE }
+
+ Note that an X.500 "list"-like operation can be emulated by the
+ client requesting a singleLevel Search operation with a filter
+ checking for the presence of the 'objectClass' attribute, and that an
+ X.500 "read"-like operation can be emulated by a baseObject Search
+ operation with the same filter. A server that provides a gateway to
+ X.500 is not required to use the Read or List operations, although it
+ may choose to do so, and if it does, it must provide the same
+ semantics as the X.500 Search operation.
+
+4.5.1.1. SearchRequest.baseObject
+
+ The name of the base object entry (or possibly the root) relative to
+ which the Search is to be performed.
+
+4.5.1.2. SearchRequest.scope
+
+ Specifies the scope of the Search to be performed. The semantics (as
+ described in [X.511]) of the defined values of this field are:
+
+ baseObject: The scope is constrained to the entry named by
+ baseObject.
+
+ singleLevel: The scope is constrained to the immediate
+ subordinates of the entry named by baseObject.
+
+
+
+
+Sermersheim Standards Track [Page 21]
+
+RFC 4511 LDAPv3 June 2006
+
+
+ wholeSubtree: The scope is constrained to the entry named by
+ baseObject and to all its subordinates.
+
+4.5.1.3. SearchRequest.derefAliases
+
+ An indicator as to whether or not alias entries (as defined in
+ [RFC4512]) are to be dereferenced during stages of the Search
+ operation.
+
+ The act of dereferencing an alias includes recursively dereferencing
+ aliases that refer to aliases.
+
+ Servers MUST detect looping while dereferencing aliases in order to
+ prevent denial-of-service attacks of this nature.
+
+ The semantics of the defined values of this field are:
+
+ neverDerefAliases: Do not dereference aliases in searching or in
+ locating the base object of the Search.
+
+ derefInSearching: While searching subordinates of the base object,
+ dereference any alias within the search scope. Dereferenced
+ objects become the vertices of further search scopes where the
+ Search operation is also applied. If the search scope is
+ wholeSubtree, the Search continues in the subtree(s) of any
+ dereferenced object. If the search scope is singleLevel, the
+ search is applied to any dereferenced objects and is not applied
+ to their subordinates. Servers SHOULD eliminate duplicate entries
+ that arise due to alias dereferencing while searching.
+
+ derefFindingBaseObj: Dereference aliases in locating the base
+ object of the Search, but not when searching subordinates of the
+ base object.
+
+ derefAlways: Dereference aliases both in searching and in locating
+ the base object of the Search.
+
+4.5.1.4. SearchRequest.sizeLimit
+
+ A size limit that restricts the maximum number of entries to be
+ returned as a result of the Search. A value of zero in this field
+ indicates that no client-requested size limit restrictions are in
+ effect for the Search. Servers may also enforce a maximum number of
+ entries to return.
+
+
+
+
+
+
+
+Sermersheim Standards Track [Page 22]
+
+RFC 4511 LDAPv3 June 2006
+
+
+4.5.1.5. SearchRequest.timeLimit
+
+ A time limit that restricts the maximum time (in seconds) allowed for
+ a Search. A value of zero in this field indicates that no client-
+ requested time limit restrictions are in effect for the Search.
+ Servers may also enforce a maximum time limit for the Search.
+
+4.5.1.6. SearchRequest.typesOnly
+
+ An indicator as to whether Search results are to contain both
+ attribute descriptions and values, or just attribute descriptions.
+ Setting this field to TRUE causes only attribute descriptions (and
+ not values) to be returned. Setting this field to FALSE causes both
+ attribute descriptions and values to be returned.
+
+4.5.1.7. SearchRequest.filter
+
+ A filter that defines the conditions that must be fulfilled in order
+ for the Search to match a given entry.
+
+ The 'and', 'or', and 'not' choices can be used to form combinations
+ of filters. At least one filter element MUST be present in an 'and'
+ or 'or' choice. The others match against individual attribute values
+ of entries in the scope of the Search. (Implementor's note: the
+ 'not' filter is an example of a tagged choice in an implicitly-tagged
+ module. In BER this is treated as if the tag were explicit.)
+
+ A server MUST evaluate filters according to the three-valued logic of
+ [X.511] (1993), Clause 7.8.1. In summary, a filter is evaluated to
+ "TRUE", "FALSE", or "Undefined". If the filter evaluates to TRUE for
+ a particular entry, then the attributes of that entry are returned as
+ part of the Search result (subject to any applicable access control
+ restrictions). If the filter evaluates to FALSE or Undefined, then
+ the entry is ignored for the Search.
+
+ A filter of the "and" choice is TRUE if all the filters in the SET OF
+ evaluate to TRUE, FALSE if at least one filter is FALSE, and
+ Undefined otherwise. A filter of the "or" choice is FALSE if all the
+ filters in the SET OF evaluate to FALSE, TRUE if at least one filter
+ is TRUE, and Undefined otherwise. A filter of the 'not' choice is
+ TRUE if the filter being negated is FALSE, FALSE if it is TRUE, and
+ Undefined if it is Undefined.
+
+ A filter item evaluates to Undefined when the server would not be
+ able to determine whether the assertion value matches an entry.
+ Examples include:
+
+
+
+
+
+Sermersheim Standards Track [Page 23]
+
+RFC 4511 LDAPv3 June 2006
+
+
+ - An attribute description in an equalityMatch, substrings,
+ greaterOrEqual, lessOrEqual, approxMatch, or extensibleMatch filter
+ is not recognized by the server.
+
+ - The attribute type does not define the appropriate matching rule.
+
+ - A MatchingRuleId in the extensibleMatch is not recognized by the
+ server or is not valid for the attribute type.
+
+ - The type of filtering requested is not implemented.
+
+ - The assertion value is invalid.
+
+ For example, if a server did not recognize the attribute type
+ shoeSize, the filters (shoeSize=*), (shoeSize=12), (shoeSize>=12),
+ and (shoeSize<=12) would each evaluate to Undefined.
+
+ Servers MUST NOT return errors if attribute descriptions or matching
+ rule ids are not recognized, assertion values are invalid, or the
+ assertion syntax is not supported. More details of filter processing
+ are given in Clause 7.8 of [X.511].
+
+4.5.1.7.1. SearchRequest.filter.equalityMatch
+
+ The matching rule for an equalityMatch filter is defined by the
+ EQUALITY matching rule for the attribute type or subtype. The filter
+ is TRUE when the EQUALITY rule returns TRUE as applied to the
+ attribute or subtype and the asserted value.
+
+4.5.1.7.2. SearchRequest.filter.substrings
+
+ There SHALL be at most one 'initial' and at most one 'final' in the
+ 'substrings' of a SubstringFilter. If 'initial' is present, it SHALL
+ be the first element of 'substrings'. If 'final' is present, it
+ SHALL be the last element of 'substrings'.
+
+ The matching rule for an AssertionValue in a substrings filter item
+ is defined by the SUBSTR matching rule for the attribute type or
+ subtype. The filter is TRUE when the SUBSTR rule returns TRUE as
+ applied to the attribute or subtype and the asserted value.
+
+ Note that the AssertionValue in a substrings filter item conforms to
+ the assertion syntax of the EQUALITY matching rule for the attribute
+ type rather than to the assertion syntax of the SUBSTR matching rule
+ for the attribute type. Conceptually, the entire SubstringFilter is
+ converted into an assertion value of the substrings matching rule
+ prior to applying the rule.
+
+
+
+
+Sermersheim Standards Track [Page 24]
+
+RFC 4511 LDAPv3 June 2006
+
+
+4.5.1.7.3. SearchRequest.filter.greaterOrEqual
+
+ The matching rule for a greaterOrEqual filter is defined by the
+ ORDERING matching rule for the attribute type or subtype. The filter
+ is TRUE when the ORDERING rule returns FALSE as applied to the
+ attribute or subtype and the asserted value.
+
+4.5.1.7.4. SearchRequest.filter.lessOrEqual
+
+ The matching rules for a lessOrEqual filter are defined by the
+ ORDERING and EQUALITY matching rules for the attribute type or
+ subtype. The filter is TRUE when either the ORDERING or EQUALITY
+ rule returns TRUE as applied to the attribute or subtype and the
+ asserted value.
+
+4.5.1.7.5. SearchRequest.filter.present
+
+ A present filter is TRUE when there is an attribute or subtype of the
+ specified attribute description present in an entry, FALSE when no
+ attribute or subtype of the specified attribute description is
+ present in an entry, and Undefined otherwise.
+
+4.5.1.7.6. SearchRequest.filter.approxMatch
+
+ An approxMatch filter is TRUE when there is a value of the attribute
+ type or subtype for which some locally-defined approximate matching
+ algorithm (e.g., spelling variations, phonetic match, etc.) returns
+ TRUE. If a value matches for equality, it also satisfies an
+ approximate match. If approximate matching is not supported for the
+ attribute, this filter item should be treated as an equalityMatch.
+
+4.5.1.7.7. SearchRequest.filter.extensibleMatch
+
+ The fields of the extensibleMatch filter item are evaluated as
+ follows:
+
+ - If the matchingRule field is absent, the type field MUST be
+ present, and an equality match is performed for that type.
+
+ - If the type field is absent and the matchingRule is present, the
+ matchValue is compared against all attributes in an entry that
+ support that matchingRule.
+
+ - If the type field is present and the matchingRule is present, the
+ matchValue is compared against the specified attribute type and its
+ subtypes.
+
+
+
+
+
+Sermersheim Standards Track [Page 25]
+
+RFC 4511 LDAPv3 June 2006
+
+
+ - If the dnAttributes field is set to TRUE, the match is additionally
+ applied against all the AttributeValueAssertions in an entry's
+ distinguished name, and it evaluates to TRUE if there is at least
+ one attribute or subtype in the distinguished name for which the
+ filter item evaluates to TRUE. The dnAttributes field is present
+ to alleviate the need for multiple versions of generic matching
+ rules (such as word matching), where one applies to entries and
+ another applies to entries and DN attributes as well.
+
+ The matchingRule used for evaluation determines the syntax for the
+ assertion value. Once the matchingRule and attribute(s) have been
+ determined, the filter item evaluates to TRUE if it matches at least
+ one attribute type or subtype in the entry, FALSE if it does not
+ match any attribute type or subtype in the entry, and Undefined if
+ the matchingRule is not recognized, the matchingRule is unsuitable
+ for use with the specified type, or the assertionValue is invalid.
+
+4.5.1.8. SearchRequest.attributes
+
+ A selection list of the attributes to be returned from each entry
+ that matches the search filter. Attributes that are subtypes of
+ listed attributes are implicitly included. LDAPString values of this
+ field are constrained to the following Augmented Backus-Naur Form
+ (ABNF) [RFC4234]:
+
+ attributeSelector = attributedescription / selectorspecial
+
+ selectorspecial = noattrs / alluserattrs
+
+ noattrs = %x31.2E.31 ; "1.1"
+
+ alluserattrs = %x2A ; asterisk ("*")
+
+ The <attributedescription> production is defined in Section 2.5 of
+ [RFC4512].
+
+ There are three special cases that may appear in the attributes
+ selection list:
+
+ 1. An empty list with no attributes requests the return of all
+ user attributes.
+
+ 2. A list containing "*" (with zero or more attribute
+ descriptions) requests the return of all user attributes in
+ addition to other listed (operational) attributes.
+
+
+
+
+
+
+Sermersheim Standards Track [Page 26]
+
+RFC 4511 LDAPv3 June 2006
+
+
+ 3. A list containing only the OID "1.1" indicates that no
+ attributes are to be returned. If "1.1" is provided with other
+ attributeSelector values, the "1.1" attributeSelector is
+ ignored. This OID was chosen because it does not (and can not)
+ correspond to any attribute in use.
+
+ Client implementors should note that even if all user attributes are
+ requested, some attributes and/or attribute values of the entry may
+ not be included in Search results due to access controls or other
+ restrictions. Furthermore, servers will not return operational
+ attributes, such as objectClasses or attributeTypes, unless they are
+ listed by name. Operational attributes are described in [RFC4512].
+
+ Attributes are returned at most once in an entry. If an attribute
+ description is named more than once in the list, the subsequent names
+ are ignored. If an attribute description in the list is not
+ recognized, it is ignored by the server.
+
+4.5.2. Search Result
+
+ The results of the Search operation are returned as zero or more
+ SearchResultEntry and/or SearchResultReference messages, followed by
+ a single SearchResultDone message.
+
+ SearchResultEntry ::= [APPLICATION 4] SEQUENCE {
+ objectName LDAPDN,
+ attributes PartialAttributeList }
+
+ PartialAttributeList ::= SEQUENCE OF
+ partialAttribute PartialAttribute
+
+ SearchResultReference ::= [APPLICATION 19] SEQUENCE
+ SIZE (1..MAX) OF uri URI
+
+ SearchResultDone ::= [APPLICATION 5] LDAPResult
+
+ Each SearchResultEntry represents an entry found during the Search.
+ Each SearchResultReference represents an area not yet explored during
+ the Search. The SearchResultEntry and SearchResultReference messages
+ may come in any order. Following all the SearchResultReference and
+ SearchResultEntry responses, the server returns a SearchResultDone
+ response, which contains an indication of success or details any
+ errors that have occurred.
+
+ Each entry returned in a SearchResultEntry will contain all
+ appropriate attributes as specified in the attributes field of the
+ Search Request, subject to access control and other administrative
+ policy. Note that the PartialAttributeList may hold zero elements.
+
+
+
+Sermersheim Standards Track [Page 27]
+
+RFC 4511 LDAPv3 June 2006
+
+
+ This may happen when none of the attributes of an entry were
+ requested or could be returned. Note also that the partialAttribute
+ vals set may hold zero elements. This may happen when typesOnly is
+ requested, access controls prevent the return of values, or other
+ reasons.
+
+ Some attributes may be constructed by the server and appear in a
+ SearchResultEntry attribute list, although they are not stored
+ attributes of an entry. Clients SHOULD NOT assume that all
+ attributes can be modified, even if this is permitted by access
+ control.
+
+ If the server's schema defines short names [RFC4512] for an attribute
+ type, then the server SHOULD use one of those names in attribute
+ descriptions for that attribute type (in preference to using the
+ <numericoid> [RFC4512] format of the attribute type's object
+ identifier). The server SHOULD NOT use the short name if that name
+ is known by the server to be ambiguous, or if it is otherwise likely
+ to cause interoperability problems.
+
+4.5.3. Continuation References in the Search Result
+
+ If the server was able to locate the entry referred to by the
+ baseObject but was unable or unwilling to search one or more non-
+ local entries, the server may return one or more
+ SearchResultReference messages, each containing a reference to
+ another set of servers for continuing the operation. A server MUST
+ NOT return any SearchResultReference messages if it has not located
+ the baseObject and thus has not searched any entries. In this case,
+ it would return a SearchResultDone containing either a referral or
+ noSuchObject result code (depending on the server's knowledge of the
+ entry named in the baseObject).
+
+ If a server holds a copy or partial copy of the subordinate naming
+ context (Section 5 of [RFC4512]), it may use the search filter to
+ determine whether or not to return a SearchResultReference response.
+ Otherwise, SearchResultReference responses are always returned when
+ in scope.
+
+ The SearchResultReference is of the same data type as the Referral.
+
+ If the client wishes to progress the Search, it issues a new Search
+ operation for each SearchResultReference that is returned. If
+ multiple URIs are present, the client assumes that any supported URI
+ may be used to progress the operation.
+
+
+
+
+
+
+Sermersheim Standards Track [Page 28]
+
+RFC 4511 LDAPv3 June 2006
+
+
+ Clients that follow search continuation references MUST ensure that
+ they do not loop between servers. They MUST NOT repeatedly contact
+ the same server for the same request with the same parameters. Some
+ clients use a counter that is incremented each time search result
+ reference handling occurs for an operation, and these kinds of
+ clients MUST be able to handle at least ten nested referrals while
+ progressing the operation.
+
+ Note that the Abandon operation described in Section 4.11 applies
+ only to a particular operation sent at the LDAP message layer between
+ a client and server. The client must individually abandon subsequent
+ Search operations it wishes to.
+
+ A URI for a server implementing LDAP and accessible via TCP/IP (v4 or
+ v6) [RFC793][RFC791] is written as an LDAP URL according to
+ [RFC4516].
+
+ SearchResultReference values that are LDAP URLs follow these rules:
+
+ - The <dn> part of the LDAP URL MUST be present, with the new target
+ object name. The client uses this name when following the
+ reference.
+
+ - Some servers (e.g., participating in distributed indexing) may
+ provide a different filter in the LDAP URL.
+
+ - If the <filter> part of the LDAP URL is present, the client uses
+ this filter in its next request to progress this Search, and if it
+ is not present the client uses the same filter as it used for that
+ Search.
+
+ - If the originating search scope was singleLevel, the <scope> part
+ of the LDAP URL will be "base".
+
+ - It is RECOMMENDED that the <scope> part be present to avoid
+ ambiguity. In the absence of a <scope> part, the scope of the
+ original Search request is assumed.
+
+ - Other aspects of the new Search request may be the same as or
+ different from the Search request that generated the
+ SearchResultReference.
+
+ - The name of an unexplored subtree in a SearchResultReference need
+ not be subordinate to the base object.
+
+ Other kinds of URIs may be returned. The syntax and semantics of
+ such URIs is left to future specifications. Clients may ignore URIs
+ that they do not support.
+
+
+
+Sermersheim Standards Track [Page 29]
+
+RFC 4511 LDAPv3 June 2006
+
+
+ UTF-8-encoded characters appearing in the string representation of a
+ DN, search filter, or other fields of the referral value may not be
+ legal for URIs (e.g., spaces) and MUST be escaped using the % method
+ in [RFC3986].
+
+4.5.3.1. Examples
+
+ For example, suppose the contacted server (hosta) holds the entry
+ <DC=Example,DC=NET> and the entry <CN=Manager,DC=Example,DC=NET>. It
+ knows that both LDAP servers (hostb) and (hostc) hold
+ <OU=People,DC=Example,DC=NET> (one is the master and the other server
+ a shadow), and that LDAP-capable server (hostd) holds the subtree
+ <OU=Roles,DC=Example,DC=NET>. If a wholeSubtree Search of
+ <DC=Example,DC=NET> is requested to the contacted server, it may
+ return the following:
+
+ SearchResultEntry for DC=Example,DC=NET
+ SearchResultEntry for CN=Manager,DC=Example,DC=NET
+ SearchResultReference {
+ ldap://hostb/OU=People,DC=Example,DC=NET??sub
+ ldap://hostc/OU=People,DC=Example,DC=NET??sub }
+ SearchResultReference {
+ ldap://hostd/OU=Roles,DC=Example,DC=NET??sub }
+ SearchResultDone (success)
+
+ Client implementors should note that when following a
+ SearchResultReference, additional SearchResultReference may be
+ generated. Continuing the example, if the client contacted the
+ server (hostb) and issued the Search request for the subtree
+ <OU=People,DC=Example,DC=NET>, the server might respond as follows:
+
+ SearchResultEntry for OU=People,DC=Example,DC=NET
+ SearchResultReference {
+ ldap://hoste/OU=Managers,OU=People,DC=Example,DC=NET??sub }
+ SearchResultReference {
+ ldap://hostf/OU=Consultants,OU=People,DC=Example,DC=NET??sub }
+ SearchResultDone (success)
+
+ Similarly, if a singleLevel Search of <DC=Example,DC=NET> is
+ requested to the contacted server, it may return the following:
+
+ SearchResultEntry for CN=Manager,DC=Example,DC=NET
+ SearchResultReference {
+ ldap://hostb/OU=People,DC=Example,DC=NET??base
+ ldap://hostc/OU=People,DC=Example,DC=NET??base }
+ SearchResultReference {
+ ldap://hostd/OU=Roles,DC=Example,DC=NET??base }
+ SearchResultDone (success)
+
+
+
+Sermersheim Standards Track [Page 30]
+
+RFC 4511 LDAPv3 June 2006
+
+
+ If the contacted server does not hold the base object for the Search,
+ but has knowledge of its possible location, then it may return a
+ referral to the client. In this case, if the client requests a
+ subtree Search of <DC=Example,DC=ORG> to hosta, the server returns a
+ SearchResultDone containing a referral.
+
+ SearchResultDone (referral) {
+ ldap://hostg/DC=Example,DC=ORG??sub }
+
+4.6. Modify Operation
+
+ The Modify operation allows a client to request that a modification
+ of an entry be performed on its behalf by a server. The Modify
+ Request is defined as follows:
+
+ ModifyRequest ::= [APPLICATION 6] SEQUENCE {
+ object LDAPDN,
+ changes SEQUENCE OF change SEQUENCE {
+ operation ENUMERATED {
+ add (0),
+ delete (1),
+ replace (2),
+ ... },
+ modification PartialAttribute } }
+
+ Fields of the Modify Request are:
+
+ - object: The value of this field contains the name of the entry to
+ be modified. The server SHALL NOT perform any alias dereferencing
+ in determining the object to be modified.
+
+ - changes: A list of modifications to be performed on the entry. The
+ entire list of modifications MUST be performed in the order they
+ are listed as a single atomic operation. While individual
+ modifications may violate certain aspects of the directory schema
+ (such as the object class definition and Directory Information Tree
+ (DIT) content rule), the resulting entry after the entire list of
+ modifications is performed MUST conform to the requirements of the
+ directory model and controlling schema [RFC4512].
+
+ - operation: Used to specify the type of modification being
+ performed. Each operation type acts on the following
+ modification. The values of this field have the following
+ semantics, respectively:
+
+ add: add values listed to the modification attribute,
+ creating the attribute if necessary.
+
+
+
+
+Sermersheim Standards Track [Page 31]
+
+RFC 4511 LDAPv3 June 2006
+
+
+ delete: delete values listed from the modification attribute.
+ If no values are listed, or if all current values of the
+ attribute are listed, the entire attribute is removed.
+
+ replace: replace all existing values of the modification
+ attribute with the new values listed, creating the attribute
+ if it did not already exist. A replace with no value will
+ delete the entire attribute if it exists, and it is ignored
+ if the attribute does not exist.
+
+ - modification: A PartialAttribute (which may have an empty SET
+ of vals) used to hold the attribute type or attribute type and
+ values being modified.
+
+ Upon receipt of a Modify Request, the server attempts to perform the
+ necessary modifications to the DIT and returns the result in a Modify
+ Response, defined as follows:
+
+ ModifyResponse ::= [APPLICATION 7] LDAPResult
+
+ The server will return to the client a single Modify Response
+ indicating either the successful completion of the DIT modification,
+ or the reason that the modification failed. Due to the requirement
+ for atomicity in applying the list of modifications in the Modify
+ Request, the client may expect that no modifications of the DIT have
+ been performed if the Modify Response received indicates any sort of
+ error, and that all requested modifications have been performed if
+ the Modify Response indicates successful completion of the Modify
+ operation. Whether or not the modification was applied cannot be
+ determined by the client if the Modify Response was not received
+ (e.g., the LDAP session was terminated or the Modify operation was
+ abandoned).
+
+ Servers MUST ensure that entries conform to user and system schema
+ rules or other data model constraints. The Modify operation cannot
+ be used to remove from an entry any of its distinguished values,
+ i.e., those values which form the entry's relative distinguished
+ name. An attempt to do so will result in the server returning the
+ notAllowedOnRDN result code. The Modify DN operation described in
+ Section 4.9 is used to rename an entry.
+
+ For attribute types that specify no equality matching, the rules in
+ Section 2.5.1 of [RFC4512] are followed.
+
+ Note that due to the simplifications made in LDAP, there is not a
+ direct mapping of the changes in an LDAP ModifyRequest onto the
+ changes of a DAP ModifyEntry operation, and different implementations
+
+
+
+
+Sermersheim Standards Track [Page 32]
+
+RFC 4511 LDAPv3 June 2006
+
+
+ of LDAP-DAP gateways may use different means of representing the
+ change. If successful, the final effect of the operations on the
+ entry MUST be identical.
+
+4.7. Add Operation
+
+ The Add operation allows a client to request the addition of an entry
+ into the Directory. The Add Request is defined as follows:
+
+ AddRequest ::= [APPLICATION 8] SEQUENCE {
+ entry LDAPDN,
+ attributes AttributeList }
+
+ AttributeList ::= SEQUENCE OF attribute Attribute
+
+ Fields of the Add Request are:
+
+ - entry: the name of the entry to be added. The server SHALL NOT
+ dereference any aliases in locating the entry to be added.
+
+ - attributes: the list of attributes that, along with those from the
+ RDN, make up the content of the entry being added. Clients MAY or
+ MAY NOT include the RDN attribute(s) in this list. Clients MUST
+ NOT supply NO-USER-MODIFICATION attributes such as the
+ createTimestamp or creatorsName attributes, since the server
+ maintains these automatically.
+
+ Servers MUST ensure that entries conform to user and system schema
+ rules or other data model constraints. For attribute types that
+ specify no equality matching, the rules in Section 2.5.1 of [RFC4512]
+ are followed (this applies to the naming attribute in addition to any
+ multi-valued attributes being added).
+
+ The entry named in the entry field of the AddRequest MUST NOT exist
+ for the AddRequest to succeed. The immediate superior (parent) of an
+ object or alias entry to be added MUST exist. For example, if the
+ client attempted to add <CN=JS,DC=Example,DC=NET>, the
+ <DC=Example,DC=NET> entry did not exist, and the <DC=NET> entry did
+ exist, then the server would return the noSuchObject result code with
+ the matchedDN field containing <DC=NET>.
+
+ Upon receipt of an Add Request, a server will attempt to add the
+ requested entry. The result of the Add attempt will be returned to
+ the client in the Add Response, defined as follows:
+
+ AddResponse ::= [APPLICATION 9] LDAPResult
+
+
+
+
+
+Sermersheim Standards Track [Page 33]
+
+RFC 4511 LDAPv3 June 2006
+
+
+ A response of success indicates that the new entry has been added to
+ the Directory.
+
+4.8. Delete Operation
+
+ The Delete operation allows a client to request the removal of an
+ entry from the Directory. The Delete Request is defined as follows:
+
+ DelRequest ::= [APPLICATION 10] LDAPDN
+
+ The Delete Request consists of the name of the entry to be deleted.
+ The server SHALL NOT dereference aliases while resolving the name of
+ the target entry to be removed.
+
+ Only leaf entries (those with no subordinate entries) can be deleted
+ with this operation.
+
+ Upon receipt of a Delete Request, a server will attempt to perform
+ the entry removal requested and return the result in the Delete
+ Response defined as follows:
+
+ DelResponse ::= [APPLICATION 11] LDAPResult
+
+4.9. Modify DN Operation
+
+ The Modify DN operation allows a client to change the Relative
+ Distinguished Name (RDN) of an entry in the Directory and/or to move
+ a subtree of entries to a new location in the Directory. The Modify
+ DN Request is defined as follows:
+
+ ModifyDNRequest ::= [APPLICATION 12] SEQUENCE {
+ entry LDAPDN,
+ newrdn RelativeLDAPDN,
+ deleteoldrdn BOOLEAN,
+ newSuperior [0] LDAPDN OPTIONAL }
+
+ Fields of the Modify DN Request are:
+
+ - entry: the name of the entry to be changed. This entry may or may
+ not have subordinate entries.
+
+ - newrdn: the new RDN of the entry. The value of the old RDN is
+ supplied when moving the entry to a new superior without changing
+ its RDN. Attribute values of the new RDN not matching any
+ attribute value of the entry are added to the entry, and an
+ appropriate error is returned if this fails.
+
+
+
+
+
+Sermersheim Standards Track [Page 34]
+
+RFC 4511 LDAPv3 June 2006
+
+
+ - deleteoldrdn: a boolean field that controls whether the old RDN
+ attribute values are to be retained as attributes of the entry or
+ deleted from the entry.
+
+ - newSuperior: if present, this is the name of an existing object
+ entry that becomes the immediate superior (parent) of the
+ existing entry.
+
+ The server SHALL NOT dereference any aliases in locating the objects
+ named in entry or newSuperior.
+
+ Upon receipt of a ModifyDNRequest, a server will attempt to perform
+ the name change and return the result in the Modify DN Response,
+ defined as follows:
+
+ ModifyDNResponse ::= [APPLICATION 13] LDAPResult
+
+ For example, if the entry named in the entry field was <cn=John
+ Smith,c=US>, the newrdn field was <cn=John Cougar Smith>, and the
+ newSuperior field was absent, then this operation would attempt to
+ rename the entry as <cn=John Cougar Smith,c=US>. If there was
+ already an entry with that name, the operation would fail with the
+ entryAlreadyExists result code.
+
+ Servers MUST ensure that entries conform to user and system schema
+ rules or other data model constraints. For attribute types that
+ specify no equality matching, the rules in Section 2.5.1 of [RFC4512]
+ are followed (this pertains to newrdn and deleteoldrdn).
+
+ The object named in newSuperior MUST exist. For example, if the
+ client attempted to add <CN=JS,DC=Example,DC=NET>, the
+ <DC=Example,DC=NET> entry did not exist, and the <DC=NET> entry did
+ exist, then the server would return the noSuchObject result code with
+ the matchedDN field containing <DC=NET>.
+
+ If the deleteoldrdn field is TRUE, the attribute values forming the
+ old RDN (but not the new RDN) are deleted from the entry. If the
+ deleteoldrdn field is FALSE, the attribute values forming the old RDN
+ will be retained as non-distinguished attribute values of the entry.
+
+ Note that X.500 restricts the ModifyDN operation to affect only
+ entries that are contained within a single server. If the LDAP
+ server is mapped onto DAP, then this restriction will apply, and the
+ affectsMultipleDSAs result code will be returned if this error
+ occurred. In general, clients MUST NOT expect to be able to perform
+ arbitrary movements of entries and subtrees between servers or
+ between naming contexts.
+
+
+
+
+Sermersheim Standards Track [Page 35]
+
+RFC 4511 LDAPv3 June 2006
+
+
+4.10. Compare Operation
+
+ The Compare operation allows a client to compare an assertion value
+ with the values of a particular attribute in a particular entry in
+ the Directory. The Compare Request is defined as follows:
+
+ CompareRequest ::= [APPLICATION 14] SEQUENCE {
+ entry LDAPDN,
+ ava AttributeValueAssertion }
+
+ Fields of the Compare Request are:
+
+ - entry: the name of the entry to be compared. The server SHALL NOT
+ dereference any aliases in locating the entry to be compared.
+
+ - ava: holds the attribute value assertion to be compared.
+
+ Upon receipt of a Compare Request, a server will attempt to perform
+ the requested comparison and return the result in the Compare
+ Response, defined as follows:
+
+ CompareResponse ::= [APPLICATION 15] LDAPResult
+
+ The resultCode is set to compareTrue, compareFalse, or an appropriate
+ error. compareTrue indicates that the assertion value in the ava
+ field matches a value of the attribute or subtype according to the
+ attribute's EQUALITY matching rule. compareFalse indicates that the
+ assertion value in the ava field and the values of the attribute or
+ subtype did not match. Other result codes indicate either that the
+ result of the comparison was Undefined (Section 4.5.1.7), or that
+ some error occurred.
+
+ Note that some directory systems may establish access controls that
+ permit the values of certain attributes (such as userPassword) to be
+ compared but not interrogated by other means.
+
+4.11. Abandon Operation
+
+ The function of the Abandon operation is to allow a client to request
+ that the server abandon an uncompleted operation. The Abandon
+ Request is defined as follows:
+
+ AbandonRequest ::= [APPLICATION 16] MessageID
+
+ The MessageID is that of an operation that was requested earlier at
+ this LDAP message layer. The Abandon request itself has its own
+ MessageID. This is distinct from the MessageID of the earlier
+ operation being abandoned.
+
+
+
+Sermersheim Standards Track [Page 36]
+
+RFC 4511 LDAPv3 June 2006
+
+
+ There is no response defined in the Abandon operation. Upon receipt
+ of an AbandonRequest, the server MAY abandon the operation identified
+ by the MessageID. Since the client cannot tell the difference
+ between a successfully abandoned operation and an uncompleted
+ operation, the application of the Abandon operation is limited to
+ uses where the client does not require an indication of its outcome.
+
+ Abandon, Bind, Unbind, and StartTLS operations cannot be abandoned.
+
+ In the event that a server receives an Abandon Request on a Search
+ operation in the midst of transmitting responses to the Search, that
+ server MUST cease transmitting entry responses to the abandoned
+ request immediately, and it MUST NOT send the SearchResultDone. Of
+ course, the server MUST ensure that only properly encoded LDAPMessage
+ PDUs are transmitted.
+
+ The ability to abandon other (particularly update) operations is at
+ the discretion of the server.
+
+ Clients should not send Abandon requests for the same operation
+ multiple times, and they MUST also be prepared to receive results
+ from operations they have abandoned (since these might have been in
+ transit when the Abandon was requested or might not be able to be
+ abandoned).
+
+ Servers MUST discard Abandon requests for messageIDs they do not
+ recognize, for operations that cannot be abandoned, and for
+ operations that have already been abandoned.
+
+4.12. Extended Operation
+
+ The Extended operation allows additional operations to be defined for
+ services not already available in the protocol; for example, to Add
+ operations to install transport layer security (see Section 4.14).
+
+ The Extended operation allows clients to make requests and receive
+ responses with predefined syntaxes and semantics. These may be
+ defined in RFCs or be private to particular implementations.
+
+ Each Extended operation consists of an Extended request and an
+ Extended response.
+
+ ExtendedRequest ::= [APPLICATION 23] SEQUENCE {
+ requestName [0] LDAPOID,
+ requestValue [1] OCTET STRING OPTIONAL }
+
+
+
+
+
+
+Sermersheim Standards Track [Page 37]
+
+RFC 4511 LDAPv3 June 2006
+
+
+ The requestName is a dotted-decimal representation of the unique
+ OBJECT IDENTIFIER corresponding to the request. The requestValue is
+ information in a form defined by that request, encapsulated inside an
+ OCTET STRING.
+
+ The server will respond to this with an LDAPMessage containing an
+ ExtendedResponse.
+
+ ExtendedResponse ::= [APPLICATION 24] SEQUENCE {
+ COMPONENTS OF LDAPResult,
+ responseName [10] LDAPOID OPTIONAL,
+ responseValue [11] OCTET STRING OPTIONAL }
+
+ The responseName field, when present, contains an LDAPOID that is
+ unique for this extended operation or response. This field is
+ optional (even when the extension specification defines an LDAPOID
+ for use in this field). The field will be absent whenever the server
+ is unable or unwilling to determine the appropriate LDAPOID to
+ return, for instance, when the requestName cannot be parsed or its
+ value is not recognized.
+
+ Where the requestName is not recognized, the server returns
+ protocolError. (The server may return protocolError in other cases.)
+
+ The requestValue and responseValue fields contain information
+ associated with the operation. The format of these fields is defined
+ by the specification of the Extended operation. Implementations MUST
+ be prepared to handle arbitrary contents of these fields, including
+ zero bytes. Values that are defined in terms of ASN.1 and BER-
+ encoded according to Section 5.1 also follow the extensibility rules
+ in Section 4.
+
+ Servers list the requestName of Extended Requests they recognize in
+ the 'supportedExtension' attribute in the root DSE (Section 5.1 of
+ [RFC4512]).
+
+ Extended operations may be specified in other documents. The
+ specification of an Extended operation consists of:
+
+ - the OBJECT IDENTIFIER assigned to the requestName,
+
+ - the OBJECT IDENTIFIER (if any) assigned to the responseName (note
+ that the same OBJECT IDENTIFIER may be used for both the
+ requestName and responseName),
+
+
+
+
+
+
+
+Sermersheim Standards Track [Page 38]
+
+RFC 4511 LDAPv3 June 2006
+
+
+ - the format of the contents of the requestValue and responseValue
+ (if any), and
+
+ - the semantics of the operation.
+
+4.13. IntermediateResponse Message
+
+ While the Search operation provides a mechanism to return multiple
+ response messages for a single Search request, other operations, by
+ nature, do not provide for multiple response messages.
+
+ The IntermediateResponse message provides a general mechanism for
+ defining single-request/multiple-response operations in LDAP. This
+ message is intended to be used in conjunction with the Extended
+ operation to define new single-request/multiple-response operations
+ or in conjunction with a control when extending existing LDAP
+ operations in a way that requires them to return Intermediate
+ response information.
+
+ It is intended that the definitions and descriptions of Extended
+ operations and controls that make use of the IntermediateResponse
+ message will define the circumstances when an IntermediateResponse
+ message can be sent by a server and the associated meaning of an
+ IntermediateResponse message sent in a particular circumstance.
+
+ IntermediateResponse ::= [APPLICATION 25] SEQUENCE {
+ responseName [0] LDAPOID OPTIONAL,
+ responseValue [1] OCTET STRING OPTIONAL }
+
+ IntermediateResponse messages SHALL NOT be returned to the client
+ unless the client issues a request that specifically solicits their
+ return. This document defines two forms of solicitation: Extended
+ operation and request control. IntermediateResponse messages are
+ specified in documents describing the manner in which they are
+ solicited (i.e., in the Extended operation or request control
+ specification that uses them). These specifications include:
+
+ - the OBJECT IDENTIFIER (if any) assigned to the responseName,
+
+ - the format of the contents of the responseValue (if any), and
+
+ - the semantics associated with the IntermediateResponse message.
+
+ Extensions that allow the return of multiple types of
+ IntermediateResponse messages SHALL identify those types using unique
+ responseName values (note that one of these may specify no value).
+
+
+
+
+
+Sermersheim Standards Track [Page 39]
+
+RFC 4511 LDAPv3 June 2006
+
+
+ Sections 4.13.1 and 4.13.2 describe additional requirements on the
+ inclusion of responseName and responseValue in IntermediateResponse
+ messages.
+
+4.13.1. Usage with LDAP ExtendedRequest and ExtendedResponse
+
+ A single-request/multiple-response operation may be defined using a
+ single ExtendedRequest message to solicit zero or more
+ IntermediateResponse messages of one or more kinds, followed by an
+ ExtendedResponse message.
+
+4.13.2. Usage with LDAP Request Controls
+
+ A control's semantics may include the return of zero or more
+ IntermediateResponse messages prior to returning the final result
+ code for the operation. One or more kinds of IntermediateResponse
+ messages may be sent in response to a request control.
+
+ All IntermediateResponse messages associated with request controls
+ SHALL include a responseName. This requirement ensures that the
+ client can correctly identify the source of IntermediateResponse
+ messages when:
+
+ - two or more controls using IntermediateResponse messages are
+ included in a request for any LDAP operation or
+
+ - one or more controls using IntermediateResponse messages are
+ included in a request with an LDAP Extended operation that uses
+ IntermediateResponse messages.
+
+4.14. StartTLS Operation
+
+ The Start Transport Layer Security (StartTLS) operation's purpose is
+ to initiate installation of a TLS layer. The StartTLS operation is
+ defined using the Extended operation mechanism described in Section
+ 4.12.
+
+4.14.1. StartTLS Request
+
+ A client requests TLS establishment by transmitting a StartTLS
+ request message to the server. The StartTLS request is defined in
+ terms of an ExtendedRequest. The requestName is
+ "1.3.6.1.4.1.1466.20037", and the requestValue field is always
+ absent.
+
+
+
+
+
+
+
+Sermersheim Standards Track [Page 40]
+
+RFC 4511 LDAPv3 June 2006
+
+
+ The client MUST NOT send any LDAP PDUs at this LDAP message layer
+ following this request until it receives a StartTLS Extended response
+ and, in the case of a successful response, completes TLS
+ negotiations.
+
+ Detected sequencing problems (particularly those detailed in Section
+ 3.1.1 of [RFC4513]) result in the resultCode being set to
+ operationsError.
+
+ If the server does not support TLS (whether by design or by current
+ configuration), it returns with the resultCode set to protocolError
+ as described in Section 4.12.
+
+4.14.2. StartTLS Response
+
+ When a StartTLS request is received, servers supporting the operation
+ MUST return a StartTLS response message to the requestor. The
+ responseName is "1.3.6.1.4.1.1466.20037" when provided (see Section
+ 4.12). The responseValue is always absent.
+
+ If the server is willing and able to negotiate TLS, it returns the
+ StartTLS response with the resultCode set to success. Upon client
+ receipt of a successful StartTLS response, protocol peers may
+ commence with TLS negotiation as discussed in Section 3 of [RFC4513].
+
+ If the server is otherwise unwilling or unable to perform this
+ operation, the server is to return an appropriate result code
+ indicating the nature of the problem. For example, if the TLS
+ subsystem is not presently available, the server may indicate this by
+ returning with the resultCode set to unavailable. In cases where a
+ non-success result code is returned, the LDAP session is left without
+ a TLS layer.
+
+4.14.3. Removal of the TLS Layer
+
+ Either the client or server MAY remove the TLS layer and leave the
+ LDAP message layer intact by sending and receiving a TLS closure
+ alert.
+
+ The initiating protocol peer sends the TLS closure alert and MUST
+ wait until it receives a TLS closure alert from the other peer before
+ sending further LDAP PDUs.
+
+ When a protocol peer receives the initial TLS closure alert, it may
+ choose to allow the LDAP message layer to remain intact. In this
+ case, it MUST immediately transmit a TLS closure alert. Following
+ this, it MAY send and receive LDAP PDUs.
+
+
+
+
+Sermersheim Standards Track [Page 41]
+
+RFC 4511 LDAPv3 June 2006
+
+
+ Protocol peers MAY terminate the LDAP session after sending or
+ receiving a TLS closure alert.
+
+5. Protocol Encoding, Connection, and Transfer
+
+ This protocol is designed to run over connection-oriented, reliable
+ transports, where the data stream is divided into octets (8-bit
+ units), with each octet and each bit being significant.
+
+ One underlying service, LDAP over TCP, is defined in Section 5.2.
+ This service is generally applicable to applications providing or
+ consuming X.500-based directory services on the Internet. This
+ specification was generally written with the TCP mapping in mind.
+ Specifications detailing other mappings may encounter various
+ obstacles.
+
+ Implementations of LDAP over TCP MUST implement the mapping as
+ described in Section 5.2.
+
+ This table illustrates the relationship among the different layers
+ involved in an exchange between two protocol peers:
+
+ +----------------------+
+ | LDAP message layer |
+ +----------------------+ > LDAP PDUs
+ +----------------------+ < data
+ | SASL layer |
+ +----------------------+ > SASL-protected data
+ +----------------------+ < data
+ | TLS layer |
+ Application +----------------------+ > TLS-protected data
+ ------------+----------------------+ < data
+ Transport | transport connection |
+ +----------------------+
+
+5.1. Protocol Encoding
+
+ The protocol elements of LDAP SHALL be encoded for exchange using the
+ Basic Encoding Rules [BER] of [ASN.1] with the following
+ restrictions:
+
+ - Only the definite form of length encoding is used.
+
+ - OCTET STRING values are encoded in the primitive form only.
+
+ - If the value of a BOOLEAN type is true, the encoding of the value
+ octet is set to hex "FF".
+
+
+
+
+Sermersheim Standards Track [Page 42]
+
+RFC 4511 LDAPv3 June 2006
+
+
+ - If a value of a type is its default value, it is absent. Only some
+ BOOLEAN and INTEGER types have default values in this protocol
+ definition.
+
+ These restrictions are meant to ease the overhead of encoding and
+ decoding certain elements in BER.
+
+ These restrictions do not apply to ASN.1 types encapsulated inside of
+ OCTET STRING values, such as attribute values, unless otherwise
+ stated.
+
+5.2. Transmission Control Protocol (TCP)
+
+ The encoded LDAPMessage PDUs are mapped directly onto the TCP
+ [RFC793] bytestream using the BER-based encoding described in Section
+ 5.1. It is recommended that server implementations running over the
+ TCP provide a protocol listener on the Internet Assigned Numbers
+ Authority (IANA)-assigned LDAP port, 389 [PortReg]. Servers may
+ instead provide a listener on a different port number. Clients MUST
+ support contacting servers on any valid TCP port.
+
+5.3. Termination of the LDAP session
+
+ Termination of the LDAP session is typically initiated by the client
+ sending an UnbindRequest (Section 4.3), or by the server sending a
+ Notice of Disconnection (Section 4.4.1). In these cases, each
+ protocol peer gracefully terminates the LDAP session by ceasing
+ exchanges at the LDAP message layer, tearing down any SASL layer,
+ tearing down any TLS layer, and closing the transport connection.
+
+ A protocol peer may determine that the continuation of any
+ communication would be pernicious, and in this case, it may abruptly
+ terminate the session by ceasing communication and closing the
+ transport connection.
+
+ In either case, when the LDAP session is terminated, uncompleted
+ operations are handled as specified in Section 3.1.
+
+6. Security Considerations
+
+ This version of the protocol provides facilities for simple
+ authentication using a cleartext password, as well as any SASL
+ [RFC4422] mechanism. Installing SASL and/or TLS layers can provide
+ integrity and other data security services.
+
+ It is also permitted that the server can return its credentials to
+ the client, if it chooses to do so.
+
+
+
+
+Sermersheim Standards Track [Page 43]
+
+RFC 4511 LDAPv3 June 2006
+
+
+ Use of cleartext password is strongly discouraged where the
+ underlying transport service cannot guarantee confidentiality and may
+ result in disclosure of the password to unauthorized parties.
+
+ Servers are encouraged to prevent directory modifications by clients
+ that have authenticated anonymously [RFC4513].
+
+ Security considerations for authentication methods, SASL mechanisms,
+ and TLS are described in [RFC4513].
+
+ Note that SASL authentication exchanges do not provide data
+ confidentiality or integrity protection for the version or name
+ fields of the BindRequest or the resultCode, diagnosticMessage, or
+ referral fields of the BindResponse, nor for any information
+ contained in controls attached to Bind requests or responses. Thus,
+ information contained in these fields SHOULD NOT be relied on unless
+ it is otherwise protected (such as by establishing protections at the
+ transport layer).
+
+ Implementors should note that various security factors (including
+ authentication and authorization information and data security
+ services) may change during the course of the LDAP session or even
+ during the performance of a particular operation. For instance,
+ credentials could expire, authorization identities or access controls
+ could change, or the underlying security layer(s) could be replaced
+ or terminated. Implementations should be robust in the handling of
+ changing security factors.
+
+ In some cases, it may be appropriate to continue the operation even
+ in light of security factor changes. For instance, it may be
+ appropriate to continue an Abandon operation regardless of the
+ change, or to continue an operation when the change upgraded (or
+ maintained) the security factor. In other cases, it may be
+ appropriate to fail or alter the processing of the operation. For
+ instance, if confidential protections were removed, it would be
+ appropriate either to fail a request to return sensitive data or,
+ minimally, to exclude the return of sensitive data.
+
+ Implementations that cache attributes and entries obtained via LDAP
+ MUST ensure that access controls are maintained if that information
+ is to be provided to multiple clients, since servers may have access
+ control policies that prevent the return of entries or attributes in
+ Search results except to particular authenticated clients. For
+ example, caches could serve result information only to the client
+ whose request caused it to be in the cache.
+
+
+
+
+
+
+Sermersheim Standards Track [Page 44]
+
+RFC 4511 LDAPv3 June 2006
+
+
+ Servers may return referrals or Search result references that
+ redirect clients to peer servers. It is possible for a rogue
+ application to inject such referrals into the data stream in an
+ attempt to redirect a client to a rogue server. Clients are advised
+ to be aware of this and possibly reject referrals when
+ confidentiality measures are not in place. Clients are advised to
+ reject referrals from the StartTLS operation.
+
+ The matchedDN and diagnosticMessage fields, as well as some
+ resultCode values (e.g., attributeOrValueExists and
+ entryAlreadyExists), could disclose the presence or absence of
+ specific data in the directory that is subject to access and other
+ administrative controls. Server implementations should restrict
+ access to protected information equally under both normal and error
+ conditions.
+
+ Protocol peers MUST be prepared to handle invalid and arbitrary-
+ length protocol encodings. Invalid protocol encodings include: BER
+ encoding exceptions, format string and UTF-8 encoding exceptions,
+ overflow exceptions, integer value exceptions, and binary mode on/off
+ flag exceptions. The LDAPv3 PROTOS [PROTOS-LDAP] test suite provides
+ excellent examples of these exceptions and test cases used to
+ discover flaws.
+
+ In the event that a protocol peer senses an attack that in its nature
+ could cause damage due to further communication at any layer in the
+ LDAP session, the protocol peer should abruptly terminate the LDAP
+ session as described in Section 5.3.
+
+7. Acknowledgements
+
+ This document is based on RFC 2251 by Mark Wahl, Tim Howes, and Steve
+ Kille. RFC 2251 was a product of the IETF ASID Working Group.
+
+ It is also based on RFC 2830 by Jeff Hodges, RL "Bob" Morgan, and
+ Mark Wahl. RFC 2830 was a product of the IETF LDAPEXT Working Group.
+
+ It is also based on RFC 3771 by Roger Harrison and Kurt Zeilenga.
+ RFC 3771 was an individual submission to the IETF.
+
+ This document is a product of the IETF LDAPBIS Working Group.
+ Significant contributors of technical review and content include Kurt
+ Zeilenga, Steven Legg, and Hallvard Furuseth.
+
+
+
+
+
+
+
+
+Sermersheim Standards Track [Page 45]
+
+RFC 4511 LDAPv3 June 2006
+
+
+8. Normative References
+
+ [ASN.1] ITU-T Recommendation X.680 (07/2002) | ISO/IEC 8824-
+ 1:2002 "Information Technology - Abstract Syntax
+ Notation One (ASN.1): Specification of basic notation".
+
+ [BER] ITU-T Rec. X.690 (07/2002) | ISO/IEC 8825-1:2002,
+ "Information technology - ASN.1 encoding rules:
+ Specification of Basic Encoding Rules (BER), Canonical
+ Encoding Rules (CER) and Distinguished Encoding Rules
+ (DER)", 2002.
+
+ [ISO10646] Universal Multiple-Octet Coded Character Set (UCS) -
+ Architecture and Basic Multilingual Plane, ISO/IEC
+ 10646-1 : 1993.
+
+ [RFC791] Postel, J., "Internet Protocol", STD 5, RFC 791,
+ September 1981.
+
+ [RFC793] Postel, J., "Transmission Control Protocol", STD 7, RFC
+ 793, September 1981.
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [RFC3454] Hoffman P. and M. Blanchet, "Preparation of
+ Internationalized Strings ('stringprep')", RFC 3454,
+ December 2002.
+
+ [RFC3629] Yergeau, F., "UTF-8, a transformation format of ISO
+ 10646", STD 63, RFC 3629, November 2003.
+
+ [RFC3986] Berners-Lee, T., Fielding, R., and L. Masinter,
+ "Uniform Resource Identifier (URI): Generic Syntax",
+ STD 66, RFC 3986, January 2005.
+
+ [RFC4013] Zeilenga, K., "SASLprep: Stringprep Profile for User
+ Names and Passwords", RFC 4013, February 2005.
+
+ [RFC4234] Crocker, D. and P. Overell, "Augmented BNF for Syntax
+ Specifications: ABNF", RFC 4234, October 2005.
+
+ [RFC4346] Dierks, T. and E. Rescorla, "The TLS Protocol Version
+ 1.1", RFC 4346, March 2006.
+
+ [RFC4422] Melnikov, A., Ed. and K. Zeilenga, Ed., "Simple
+ Authentication and Security Layer (SASL)", RFC 4422,
+ June 2006.
+
+
+
+Sermersheim Standards Track [Page 46]
+
+RFC 4511 LDAPv3 June 2006
+
+
+ [RFC4510] Zeilenga, K., Ed., "Lightweight Directory Access
+ Protocol (LDAP): Technical Specification Road Map", RFC
+ 4510, June 2006.
+
+ [RFC4512] Zeilenga, K., Lightweight Directory Access Protocol
+ (LDAP): Directory Information Models", RFC 4512, June
+ 2006.
+
+ [RFC4513] Harrison, R., Ed., "Lightweight Directory Access
+ Protocol (LDAP): Authentication Methods and Security
+ Mechanisms", RFC 4513, June 2006.
+
+ [RFC4514] Zeilenga, K., Ed., "Lightweight Directory Access
+ Protocol (LDAP): String Representation of Distinguished
+ Names", RFC 4514, June 2006.
+
+ [RFC4516] Smith, M., Ed. and T. Howes, "Lightweight Directory
+ Access Protocol (LDAP): Uniform Resource Locator", RFC
+ 4516, June 2006.
+
+ [RFC4517] Legg, S., Ed., "Lightweight Directory Access Protocol
+ (LDAP): Syntaxes and Matching Rules", RFC 4517, June
+ 2006.
+
+ [RFC4520] Zeilenga, K., "Internet Assigned Numbers Authority
+ (IANA) Considerations for the Lightweight Directory
+ Access Protocol (LDAP)", BCP 64, RFC 4520, June 2006.
+
+ [Unicode] The Unicode Consortium, "The Unicode Standard, Version
+ 3.2.0" is defined by "The Unicode Standard, Version
+ 3.0" (Reading, MA, Addison-Wesley, 2000. ISBN 0-201-
+ 61633-5), as amended by the "Unicode Standard Annex
+ #27: Unicode 3.1"
+ (http://www.unicode.org/reports/tr27/) and by the
+ "Unicode Standard Annex #28: Unicode 3.2"
+ (http://www.unicode.org/reports/tr28/).
+
+ [X.500] ITU-T Rec. X.500, "The Directory: Overview of Concepts,
+ Models and Service", 1993.
+
+ [X.511] ITU-T Rec. X.511, "The Directory: Abstract Service
+ Definition", 1993.
+
+
+
+
+
+
+
+
+
+Sermersheim Standards Track [Page 47]
+
+RFC 4511 LDAPv3 June 2006
+
+
+9. Informative References
+
+ [CharModel] Whistler, K. and M. Davis, "Unicode Technical Report
+ #17, Character Encoding Model", UTR17,
+ <http://www.unicode.org/unicode/reports/tr17/>, August
+ 2000.
+
+ [Glossary] The Unicode Consortium, "Unicode Glossary",
+ <http://www.unicode.org/glossary/>.
+
+ [PortReg] IANA, "Port Numbers",
+ <http://www.iana.org/assignments/port-numbers>.
+
+ [PROTOS-LDAP] University of Oulu, "PROTOS Test-Suite: c06-ldapv3"
+ <http://www.ee.oulu.fi/research/ouspg/protos/testing/
+ c06/ldapv3/>.
+
+10. IANA Considerations
+
+ The Internet Assigned Numbers Authority (IANA) has updated the LDAP
+ result code registry to indicate that this document provides the
+ definitive technical specification for result codes 0-36, 48-54, 64-
+ 70, 80-90. It is also noted that one resultCode value
+ (strongAuthRequired) has been renamed (to strongerAuthRequired).
+
+ The IANA has also updated the LDAP Protocol Mechanism registry to
+ indicate that this document and [RFC4513] provides the definitive
+ technical specification for the StartTLS (1.3.6.1.4.1.1466.20037)
+ Extended operation.
+
+ IANA has assigned LDAP Object Identifier 18 [RFC4520] to identify the
+ ASN.1 module defined in this document.
+
+ Subject: Request for LDAP Object Identifier Registration
+ Person & email address to contact for further information:
+ Jim Sermersheim <jimse@novell.com>
+ Specification: RFC 4511
+ Author/Change Controller: IESG
+ Comments:
+ Identifies the LDAP ASN.1 module
+
+
+
+
+
+
+
+
+
+
+
+Sermersheim Standards Track [Page 48]
+
+RFC 4511 LDAPv3 June 2006
+
+
+Appendix A. LDAP Result Codes
+
+ This normative appendix details additional considerations regarding
+ LDAP result codes and provides a brief, general description of each
+ LDAP result code enumerated in Section 4.1.9.
+
+ Additional result codes MAY be defined for use with extensions
+ [RFC4520]. Client implementations SHALL treat any result code that
+ they do not recognize as an unknown error condition.
+
+ The descriptions provided here do not fully account for result code
+ substitutions used to prevent unauthorized disclosures (such as
+ substitution of noSuchObject for insufficientAccessRights, or
+ invalidCredentials for insufficientAccessRights).
+
+A.1. Non-Error Result Codes
+
+ These result codes (called "non-error" result codes) do not indicate
+ an error condition:
+
+ success (0),
+ compareFalse (5),
+ compareTrue (6),
+ referral (10), and
+ saslBindInProgress (14).
+
+ The success, compareTrue, and compareFalse result codes indicate
+ successful completion (and, hence, are referred to as "successful"
+ result codes).
+
+ The referral and saslBindInProgress result codes indicate the client
+ needs to take additional action to complete the operation.
+
+A.2. Result Codes
+
+ Existing LDAP result codes are described as follows:
+
+ success (0)
+ Indicates the successful completion of an operation. Note:
+ this code is not used with the Compare operation. See
+ compareFalse (5) and compareTrue (6).
+
+
+
+
+
+
+
+
+
+
+Sermersheim Standards Track [Page 49]
+
+RFC 4511 LDAPv3 June 2006
+
+
+ operationsError (1)
+ Indicates that the operation is not properly sequenced with
+ relation to other operations (of same or different type).
+
+ For example, this code is returned if the client attempts to
+ StartTLS [RFC4346] while there are other uncompleted operations
+ or if a TLS layer was already installed.
+
+ protocolError (2)
+ Indicates the server received data that is not well-formed.
+
+ For Bind operation only, this code is also used to indicate
+ that the server does not support the requested protocol
+ version.
+
+ For Extended operations only, this code is also used to
+ indicate that the server does not support (by design or
+ configuration) the Extended operation associated with the
+ requestName.
+
+ For request operations specifying multiple controls, this may
+ be used to indicate that the server cannot ignore the order
+ of the controls as specified, or that the combination of the
+ specified controls is invalid or unspecified.
+
+ timeLimitExceeded (3)
+ Indicates that the time limit specified by the client was
+ exceeded before the operation could be completed.
+
+ sizeLimitExceeded (4)
+ Indicates that the size limit specified by the client was
+ exceeded before the operation could be completed.
+
+ compareFalse (5)
+ Indicates that the Compare operation has successfully
+ completed and the assertion has evaluated to FALSE or
+ Undefined.
+
+ compareTrue (6)
+ Indicates that the Compare operation has successfully
+ completed and the assertion has evaluated to TRUE.
+
+ authMethodNotSupported (7)
+ Indicates that the authentication method or mechanism is not
+ supported.
+
+
+
+
+
+
+Sermersheim Standards Track [Page 50]
+
+RFC 4511 LDAPv3 June 2006
+
+
+ strongerAuthRequired (8)
+ Indicates the server requires strong(er) authentication in
+ order to complete the operation.
+
+ When used with the Notice of Disconnection operation, this
+ code indicates that the server has detected that an
+ established security association between the client and
+ server has unexpectedly failed or been compromised.
+
+ referral (10)
+ Indicates that a referral needs to be chased to complete the
+ operation (see Section 4.1.10).
+
+ adminLimitExceeded (11)
+ Indicates that an administrative limit has been exceeded.
+
+ unavailableCriticalExtension (12)
+ Indicates a critical control is unrecognized (see Section
+ 4.1.11).
+
+ confidentialityRequired (13)
+ Indicates that data confidentiality protections are required.
+
+ saslBindInProgress (14)
+ Indicates the server requires the client to send a new bind
+ request, with the same SASL mechanism, to continue the
+ authentication process (see Section 4.2).
+
+ noSuchAttribute (16)
+ Indicates that the named entry does not contain the specified
+ attribute or attribute value.
+
+ undefinedAttributeType (17)
+ Indicates that a request field contains an unrecognized
+ attribute description.
+
+ inappropriateMatching (18)
+ Indicates that an attempt was made (e.g., in an assertion) to
+ use a matching rule not defined for the attribute type
+ concerned.
+
+ constraintViolation (19)
+ Indicates that the client supplied an attribute value that
+ does not conform to the constraints placed upon it by the
+ data model.
+
+ For example, this code is returned when multiple values are
+ supplied to an attribute that has a SINGLE-VALUE constraint.
+
+
+
+Sermersheim Standards Track [Page 51]
+
+RFC 4511 LDAPv3 June 2006
+
+
+ attributeOrValueExists (20)
+ Indicates that the client supplied an attribute or value to
+ be added to an entry, but the attribute or value already
+ exists.
+
+ invalidAttributeSyntax (21)
+ Indicates that a purported attribute value does not conform
+ to the syntax of the attribute.
+
+ noSuchObject (32)
+ Indicates that the object does not exist in the DIT.
+
+ aliasProblem (33)
+ Indicates that an alias problem has occurred. For example,
+ the code may used to indicate an alias has been dereferenced
+ that names no object.
+
+ invalidDNSyntax (34)
+ Indicates that an LDAPDN or RelativeLDAPDN field (e.g., search
+ base, target entry, ModifyDN newrdn, etc.) of a request does
+ not conform to the required syntax or contains attribute
+ values that do not conform to the syntax of the attribute's
+ type.
+
+ aliasDereferencingProblem (36)
+ Indicates that a problem occurred while dereferencing an
+ alias. Typically, an alias was encountered in a situation
+ where it was not allowed or where access was denied.
+
+ inappropriateAuthentication (48)
+ Indicates the server requires the client that had attempted
+ to bind anonymously or without supplying credentials to
+ provide some form of credentials.
+
+ invalidCredentials (49)
+ Indicates that the provided credentials (e.g., the user's name
+ and password) are invalid.
+
+ insufficientAccessRights (50)
+ Indicates that the client does not have sufficient access
+ rights to perform the operation.
+
+ busy (51)
+ Indicates that the server is too busy to service the
+ operation.
+
+
+
+
+
+
+Sermersheim Standards Track [Page 52]
+
+RFC 4511 LDAPv3 June 2006
+
+
+ unavailable (52)
+ Indicates that the server is shutting down or a subsystem
+ necessary to complete the operation is offline.
+
+ unwillingToPerform (53)
+ Indicates that the server is unwilling to perform the
+ operation.
+
+ loopDetect (54)
+ Indicates that the server has detected an internal loop (e.g.,
+ while dereferencing aliases or chaining an operation).
+
+ namingViolation (64)
+ Indicates that the entry's name violates naming restrictions.
+
+ objectClassViolation (65)
+ Indicates that the entry violates object class restrictions.
+
+ notAllowedOnNonLeaf (66)
+ Indicates that the operation is inappropriately acting upon a
+ non-leaf entry.
+
+ notAllowedOnRDN (67)
+ Indicates that the operation is inappropriately attempting to
+ remove a value that forms the entry's relative distinguished
+ name.
+
+ entryAlreadyExists (68)
+ Indicates that the request cannot be fulfilled (added, moved,
+ or renamed) as the target entry already exists.
+
+ objectClassModsProhibited (69)
+ Indicates that an attempt to modify the object class(es) of
+ an entry's 'objectClass' attribute is prohibited.
+
+ For example, this code is returned when a client attempts to
+ modify the structural object class of an entry.
+
+ affectsMultipleDSAs (71)
+ Indicates that the operation cannot be performed as it would
+ affect multiple servers (DSAs).
+
+ other (80)
+ Indicates the server has encountered an internal error.
+
+
+
+
+
+
+
+Sermersheim Standards Track [Page 53]
+
+RFC 4511 LDAPv3 June 2006
+
+
+Appendix B. Complete ASN.1 Definition
+
+ This appendix is normative.
+
+ Lightweight-Directory-Access-Protocol-V3 {1 3 6 1 1 18}
+ -- Copyright (C) The Internet Society (2006). This version of
+ -- this ASN.1 module is part of RFC 4511; see the RFC itself
+ -- for full legal notices.
+ DEFINITIONS
+ IMPLICIT TAGS
+ EXTENSIBILITY IMPLIED ::=
+
+ BEGIN
+
+ LDAPMessage ::= SEQUENCE {
+ messageID MessageID,
+ protocolOp CHOICE {
+ bindRequest BindRequest,
+ bindResponse BindResponse,
+ unbindRequest UnbindRequest,
+ searchRequest SearchRequest,
+ searchResEntry SearchResultEntry,
+ searchResDone SearchResultDone,
+ searchResRef SearchResultReference,
+ modifyRequest ModifyRequest,
+ modifyResponse ModifyResponse,
+ addRequest AddRequest,
+ addResponse AddResponse,
+ delRequest DelRequest,
+ delResponse DelResponse,
+ modDNRequest ModifyDNRequest,
+ modDNResponse ModifyDNResponse,
+ compareRequest CompareRequest,
+ compareResponse CompareResponse,
+ abandonRequest AbandonRequest,
+ extendedReq ExtendedRequest,
+ extendedResp ExtendedResponse,
+ ...,
+ intermediateResponse IntermediateResponse },
+ controls [0] Controls OPTIONAL }
+
+ MessageID ::= INTEGER (0 .. maxInt)
+
+ maxInt INTEGER ::= 2147483647 -- (2^^31 - 1) --
+
+ LDAPString ::= OCTET STRING -- UTF-8 encoded,
+ -- [ISO10646] characters
+
+
+
+
+Sermersheim Standards Track [Page 54]
+
+RFC 4511 LDAPv3 June 2006
+
+
+ LDAPOID ::= OCTET STRING -- Constrained to <numericoid>
+ -- [RFC4512]
+
+ LDAPDN ::= LDAPString -- Constrained to <distinguishedName>
+ -- [RFC4514]
+
+ RelativeLDAPDN ::= LDAPString -- Constrained to <name-component>
+ -- [RFC4514]
+
+ AttributeDescription ::= LDAPString
+ -- Constrained to <attributedescription>
+ -- [RFC4512]
+
+ AttributeValue ::= OCTET STRING
+
+ AttributeValueAssertion ::= SEQUENCE {
+ attributeDesc AttributeDescription,
+ assertionValue AssertionValue }
+
+ AssertionValue ::= OCTET STRING
+
+ PartialAttribute ::= SEQUENCE {
+ type AttributeDescription,
+ vals SET OF value AttributeValue }
+
+ Attribute ::= PartialAttribute(WITH COMPONENTS {
+ ...,
+ vals (SIZE(1..MAX))})
+
+ MatchingRuleId ::= LDAPString
+
+ LDAPResult ::= SEQUENCE {
+ resultCode ENUMERATED {
+ success (0),
+ operationsError (1),
+ protocolError (2),
+ timeLimitExceeded (3),
+ sizeLimitExceeded (4),
+ compareFalse (5),
+ compareTrue (6),
+ authMethodNotSupported (7),
+ strongerAuthRequired (8),
+ -- 9 reserved --
+ referral (10),
+ adminLimitExceeded (11),
+ unavailableCriticalExtension (12),
+ confidentialityRequired (13),
+ saslBindInProgress (14),
+
+
+
+Sermersheim Standards Track [Page 55]
+
+RFC 4511 LDAPv3 June 2006
+
+
+ noSuchAttribute (16),
+ undefinedAttributeType (17),
+ inappropriateMatching (18),
+ constraintViolation (19),
+ attributeOrValueExists (20),
+ invalidAttributeSyntax (21),
+ -- 22-31 unused --
+ noSuchObject (32),
+ aliasProblem (33),
+ invalidDNSyntax (34),
+ -- 35 reserved for undefined isLeaf --
+ aliasDereferencingProblem (36),
+ -- 37-47 unused --
+ inappropriateAuthentication (48),
+ invalidCredentials (49),
+ insufficientAccessRights (50),
+ busy (51),
+ unavailable (52),
+ unwillingToPerform (53),
+ loopDetect (54),
+ -- 55-63 unused --
+ namingViolation (64),
+ objectClassViolation (65),
+ notAllowedOnNonLeaf (66),
+ notAllowedOnRDN (67),
+ entryAlreadyExists (68),
+ objectClassModsProhibited (69),
+ -- 70 reserved for CLDAP --
+ affectsMultipleDSAs (71),
+ -- 72-79 unused --
+ other (80),
+ ... },
+ matchedDN LDAPDN,
+ diagnosticMessage LDAPString,
+ referral [3] Referral OPTIONAL }
+
+ Referral ::= SEQUENCE SIZE (1..MAX) OF uri URI
+
+ URI ::= LDAPString -- limited to characters permitted in
+ -- URIs
+
+ Controls ::= SEQUENCE OF control Control
+
+ Control ::= SEQUENCE {
+ controlType LDAPOID,
+ criticality BOOLEAN DEFAULT FALSE,
+ controlValue OCTET STRING OPTIONAL }
+
+
+
+
+Sermersheim Standards Track [Page 56]
+
+RFC 4511 LDAPv3 June 2006
+
+
+ BindRequest ::= [APPLICATION 0] SEQUENCE {
+ version INTEGER (1 .. 127),
+ name LDAPDN,
+ authentication AuthenticationChoice }
+
+ AuthenticationChoice ::= CHOICE {
+ simple [0] OCTET STRING,
+ -- 1 and 2 reserved
+ sasl [3] SaslCredentials,
+ ... }
+
+ SaslCredentials ::= SEQUENCE {
+ mechanism LDAPString,
+ credentials OCTET STRING OPTIONAL }
+
+ BindResponse ::= [APPLICATION 1] SEQUENCE {
+ COMPONENTS OF LDAPResult,
+ serverSaslCreds [7] OCTET STRING OPTIONAL }
+
+ UnbindRequest ::= [APPLICATION 2] NULL
+
+ SearchRequest ::= [APPLICATION 3] SEQUENCE {
+ baseObject LDAPDN,
+ scope ENUMERATED {
+ baseObject (0),
+ singleLevel (1),
+ wholeSubtree (2),
+ ... },
+ derefAliases ENUMERATED {
+ neverDerefAliases (0),
+ derefInSearching (1),
+ derefFindingBaseObj (2),
+ derefAlways (3) },
+ sizeLimit INTEGER (0 .. maxInt),
+ timeLimit INTEGER (0 .. maxInt),
+ typesOnly BOOLEAN,
+ filter Filter,
+ attributes AttributeSelection }
+
+ AttributeSelection ::= SEQUENCE OF selector LDAPString
+ -- The LDAPString is constrained to
+ -- <attributeSelector> in Section 4.5.1.8
+
+ Filter ::= CHOICE {
+ and [0] SET SIZE (1..MAX) OF filter Filter,
+ or [1] SET SIZE (1..MAX) OF filter Filter,
+ not [2] Filter,
+ equalityMatch [3] AttributeValueAssertion,
+
+
+
+Sermersheim Standards Track [Page 57]
+
+RFC 4511 LDAPv3 June 2006
+
+
+ substrings [4] SubstringFilter,
+ greaterOrEqual [5] AttributeValueAssertion,
+ lessOrEqual [6] AttributeValueAssertion,
+ present [7] AttributeDescription,
+ approxMatch [8] AttributeValueAssertion,
+ extensibleMatch [9] MatchingRuleAssertion,
+ ... }
+
+ SubstringFilter ::= SEQUENCE {
+ type AttributeDescription,
+ substrings SEQUENCE SIZE (1..MAX) OF substring CHOICE {
+ initial [0] AssertionValue, -- can occur at most once
+ any [1] AssertionValue,
+ final [2] AssertionValue } -- can occur at most once
+ }
+
+ MatchingRuleAssertion ::= SEQUENCE {
+ matchingRule [1] MatchingRuleId OPTIONAL,
+ type [2] AttributeDescription OPTIONAL,
+ matchValue [3] AssertionValue,
+ dnAttributes [4] BOOLEAN DEFAULT FALSE }
+
+ SearchResultEntry ::= [APPLICATION 4] SEQUENCE {
+ objectName LDAPDN,
+ attributes PartialAttributeList }
+
+ PartialAttributeList ::= SEQUENCE OF
+ partialAttribute PartialAttribute
+
+ SearchResultReference ::= [APPLICATION 19] SEQUENCE
+ SIZE (1..MAX) OF uri URI
+
+ SearchResultDone ::= [APPLICATION 5] LDAPResult
+
+ ModifyRequest ::= [APPLICATION 6] SEQUENCE {
+ object LDAPDN,
+ changes SEQUENCE OF change SEQUENCE {
+ operation ENUMERATED {
+ add (0),
+ delete (1),
+ replace (2),
+ ... },
+ modification PartialAttribute } }
+
+ ModifyResponse ::= [APPLICATION 7] LDAPResult
+
+
+
+
+
+
+Sermersheim Standards Track [Page 58]
+
+RFC 4511 LDAPv3 June 2006
+
+
+ AddRequest ::= [APPLICATION 8] SEQUENCE {
+ entry LDAPDN,
+ attributes AttributeList }
+
+ AttributeList ::= SEQUENCE OF attribute Attribute
+
+ AddResponse ::= [APPLICATION 9] LDAPResult
+
+ DelRequest ::= [APPLICATION 10] LDAPDN
+
+ DelResponse ::= [APPLICATION 11] LDAPResult
+
+ ModifyDNRequest ::= [APPLICATION 12] SEQUENCE {
+ entry LDAPDN,
+ newrdn RelativeLDAPDN,
+ deleteoldrdn BOOLEAN,
+ newSuperior [0] LDAPDN OPTIONAL }
+
+ ModifyDNResponse ::= [APPLICATION 13] LDAPResult
+
+ CompareRequest ::= [APPLICATION 14] SEQUENCE {
+ entry LDAPDN,
+ ava AttributeValueAssertion }
+
+ CompareResponse ::= [APPLICATION 15] LDAPResult
+
+ AbandonRequest ::= [APPLICATION 16] MessageID
+
+ ExtendedRequest ::= [APPLICATION 23] SEQUENCE {
+ requestName [0] LDAPOID,
+ requestValue [1] OCTET STRING OPTIONAL }
+
+ ExtendedResponse ::= [APPLICATION 24] SEQUENCE {
+ COMPONENTS OF LDAPResult,
+ responseName [10] LDAPOID OPTIONAL,
+ responseValue [11] OCTET STRING OPTIONAL }
+
+ IntermediateResponse ::= [APPLICATION 25] SEQUENCE {
+ responseName [0] LDAPOID OPTIONAL,
+ responseValue [1] OCTET STRING OPTIONAL }
+
+ END
+
+
+
+
+
+
+
+
+
+Sermersheim Standards Track [Page 59]
+
+RFC 4511 LDAPv3 June 2006
+
+
+Appendix C. Changes
+
+ This appendix is non-normative.
+
+ This appendix summarizes substantive changes made to RFC 2251, RFC
+ 2830, and RFC 3771.
+
+C.1. Changes Made to RFC 2251
+
+ This section summarizes the substantive changes made to Sections 1,
+ 2, 3.1, and 4, and the remainder of RFC 2251. Readers should
+ consult [RFC4512] and [RFC4513] for summaries of changes to other
+ sections.
+
+C.1.1. Section 1 (Status of this Memo)
+
+ - Removed IESG note. Post publication of RFC 2251, mandatory LDAP
+ authentication mechanisms have been standardized which are
+ sufficient to remove this note. See [RFC4513] for authentication
+ mechanisms.
+
+C.1.2. Section 3.1 (Protocol Model) and others
+
+ - Removed notes giving history between LDAP v1, v2, and v3. Instead,
+ added sufficient language so that this document can stand on its
+ own.
+
+C.1.3. Section 4 (Elements of Protocol)
+
+ - Clarified where the extensibility features of ASN.1 apply to the
+ protocol. This change affected various ASN.1 types by the
+ inclusion of ellipses (...) to certain elements.
+ - Removed the requirement that servers that implement version 3 or
+ later MUST provide the 'supportedLDAPVersion' attribute. This
+ statement provided no interoperability advantages.
+
+C.1.4. Section 4.1.1 (Message Envelope)
+
+ - There was a mandatory requirement for the server to return a
+ Notice of Disconnection and drop the transport connection when a
+ PDU is malformed in a certain way. This has been updated such that
+ the server SHOULD return the Notice of Disconnection, and it MUST
+ terminate the LDAP Session.
+
+C.1.5. Section 4.1.1.1 (Message ID)
+
+ - Required that the messageID of requests MUST be non-zero as the
+ zero is reserved for Notice of Disconnection.
+
+
+
+Sermersheim Standards Track [Page 60]
+
+RFC 4511 LDAPv3 June 2006
+
+
+ - Specified when it is and isn't appropriate to return an already
+ used messageID. RFC 2251 accidentally imposed synchronous server
+ behavior in its wording of this.
+
+C.1.6. Section 4.1.2 (String Types)
+
+ - Stated that LDAPOID is constrained to <numericoid> from [RFC4512].
+
+C.1.7. Section 4.1.5.1 (Binary Option) and others
+
+ - Removed the Binary Option from the specification. There are
+ numerous interoperability problems associated with this method of
+ alternate attribute type encoding. Work to specify a suitable
+ replacement is ongoing.
+
+C.1.8. Section 4.1.8 (Attribute)
+
+ - Combined the definitions of PartialAttribute and Attribute here,
+ and defined Attribute in terms of PartialAttribute.
+
+C.1.9. Section 4.1.10 (Result Message)
+
+ - Renamed "errorMessage" to "diagnosticMessage" as it is allowed to
+ be sent for non-error results.
+ - Moved some language into Appendix A, and referred the reader there.
+ - Allowed matchedDN to be present for other result codes than those
+ listed in RFC 2251.
+ - Renamed the code "strongAuthRequired" to "strongerAuthRequired" to
+ clarify that this code may often be returned to indicate that a
+ stronger authentication is needed to perform a given operation.
+
+C.1.10. Section 4.1.11 (Referral)
+
+ - Defined referrals in terms of URIs rather than URLs.
+ - Removed the requirement that all referral URIs MUST be equally
+ capable of progressing the operation. The statement was ambiguous
+ and provided no instructions on how to carry it out.
+ - Added the requirement that clients MUST NOT loop between servers.
+ - Clarified the instructions for using LDAPURLs in referrals, and in
+ doing so added a recommendation that the scope part be present.
+ - Removed imperatives which required clients to use URLs in specific
+ ways to progress an operation. These did nothing for
+ interoperability.
+
+
+
+
+
+
+
+
+Sermersheim Standards Track [Page 61]
+
+RFC 4511 LDAPv3 June 2006
+
+
+C.1.11. Section 4.1.12 (Controls)
+
+ - Specified how control values defined in terms of ASN.1 are to be
+ encoded.
+ - Noted that the criticality field is only applied to request
+ messages (except UnbindRequest), and must be ignored when present
+ on response messages and UnbindRequest.
+ - Specified that non-critical controls may be ignored at the
+ server's discretion. There was confusion in the original wording
+ which led some to believe that recognized controls may not be
+ ignored as long as they were associated with a proper request.
+ - Added language regarding combinations of controls and the ordering
+ of controls on a message.
+ - Specified that when the semantics of the combination of controls
+ is undefined or unknown, it results in a protocolError.
+ - Changed "The server MUST be prepared" to "Implementations MUST be
+ prepared" in paragraph 8 to reflect that both client and server
+ implementations must be able to handle this (as both parse
+ controls).
+
+C.1.12. Section 4.2 (Bind Operation)
+
+ - Mandated that servers return protocolError when the version is not
+ supported.
+ - Disambiguated behavior when the simple authentication is used, the
+ name is empty, and the password is non-empty.
+ - Required servers to not dereference aliases for Bind. This was
+ added for consistency with other operations and to help ensure
+ data consistency.
+ - Required that textual passwords be transferred as UTF-8 encoded
+ Unicode, and added recommendations on string preparation. This was
+ to help ensure interoperability of passwords being sent from
+ different clients.
+
+C.1.13. Section 4.2.1 (Sequencing of the Bind Request)
+
+ - This section was largely reorganized for readability, and language
+ was added to clarify the authentication state of failed and
+ abandoned Bind operations.
+ - Removed: "If a SASL transfer encryption or integrity mechanism has
+ been negotiated, that mechanism does not support the changing of
+ credentials from one identity to another, then the client MUST
+ instead establish a new connection."
+ If there are dependencies between multiple negotiations of a
+ particular SASL mechanism, the technical specification for that
+ SASL mechanism details how applications are to deal with them.
+ LDAP should not require any special handling.
+ - Dropped MUST imperative in paragraph 3 to align with [RFC2119].
+
+
+
+Sermersheim Standards Track [Page 62]
+
+RFC 4511 LDAPv3 June 2006
+
+
+ - Mandated that clients not send non-Bind operations while a Bind is
+ in progress, and suggested that servers not process them if they
+ are received. This is needed to ensure proper sequencing of the
+ Bind in relationship to other operations.
+
+C.1.14. Section 4.2.3 (Bind Response)
+
+ - Moved most error-related text to Appendix A, and added text
+ regarding certain errors used in conjunction with the Bind
+ operation.
+ - Prohibited the server from specifying serverSaslCreds when not
+ appropriate.
+
+C.1.15. Section 4.3 (Unbind Operation)
+
+ - Specified that both peers are to cease transmission and terminate
+ the LDAP session for the Unbind operation.
+
+C.1.16. Section 4.4 (Unsolicited Notification)
+
+ - Added instructions for future specifications of Unsolicited
+ Notifications.
+
+C.1.17. Section 4.5.1 (Search Request)
+
+ - SearchRequest attributes is now defined as an AttributeSelection
+ type rather than AttributeDescriptionList, and an ABNF is
+ provided.
+ - SearchRequest attributes may contain duplicate attribute
+ descriptions. This was previously prohibited. Now servers are
+ instructed to ignore subsequent names when they are duplicated.
+ This was relaxed in order to allow different short names and also
+ OIDs to be requested for an attribute.
+ - The present search filter now evaluates to Undefined when the
+ specified attribute is not known to the server. It used to
+ evaluate to FALSE, which caused behavior inconsistent with what
+ most would expect, especially when the 'not' operator was used.
+ - The Filter choice SubstringFilter substrings type is now defined
+ with a lower bound of 1.
+ - The SubstringFilter substrings 'initial, 'any', and 'final' types
+ are now AssertionValue rather than LDAPString. Also, added
+ imperatives stating that 'initial' (if present) must be listed
+ first, and 'final' (if present) must be listed last.
+ - Disambiguated the semantics of the derefAliases choices. There was
+ question as to whether derefInSearching applied to the base object
+ in a wholeSubtree Search.
+ - Added instructions for equalityMatch, substrings, greaterOrEqual,
+ lessOrEqual, and approxMatch.
+
+
+
+Sermersheim Standards Track [Page 63]
+
+RFC 4511 LDAPv3 June 2006
+
+
+
+C.1.18. Section 4.5.2 (Search Result)
+
+ - Recommended that servers not use attribute short names when it
+ knows they are ambiguous or may cause interoperability problems.
+ - Removed all mention of ExtendedResponse due to lack of
+ implementation.
+
+C.1.19. Section 4.5.3 (Continuation References in the Search Result)
+
+ - Made changes similar to those made to Section 4.1.11.
+
+C.1.20. Section 4.5.3.1 (Example)
+
+ - Fixed examples to adhere to changes made to Section 4.5.3.
+
+C.1.21. Section 4.6 (Modify Operation)
+
+ - Replaced AttributeTypeAndValues with Attribute as they are
+ equivalent.
+ - Specified the types of modification changes that might
+ temporarily violate schema. Some readers were under the impression
+ that any temporary schema violation was allowed.
+
+C.1.22. Section 4.7 (Add Operation)
+
+ - Aligned Add operation with X.511 in that the attributes of the RDN
+ are used in conjunction with the listed attributes to create the
+ entry. Previously, Add required that the distinguished values be
+ present in the listed attributes.
+ - Removed requirement that the objectClass attribute MUST be
+ specified as some DSE types do not require this attribute.
+ Instead, generic wording was added, requiring the added entry to
+ adhere to the data model.
+ - Removed recommendation regarding placement of objects. This is
+ covered in the data model document.
+
+C.1.23. Section 4.9 (Modify DN Operation)
+
+ - Required servers to not dereference aliases for Modify DN. This
+ was added for consistency with other operations and to help ensure
+ data consistency.
+ - Allow Modify DN to fail when moving between naming contexts.
+ - Specified what happens when the attributes of the newrdn are not
+ present on the entry.
+
+
+
+
+
+
+Sermersheim Standards Track [Page 64]
+
+RFC 4511 LDAPv3 June 2006
+
+
+C.1.24. Section 4.10 (Compare Operation)
+
+ - Specified that compareFalse means that the Compare took place and
+ the result is false. There was confusion that led people to
+ believe that an Undefined match resulted in compareFalse.
+ - Required servers to not dereference aliases for Compare. This was
+ added for consistency with other operations and to help ensure
+ data consistency.
+
+C.1.25. Section 4.11 (Abandon Operation)
+
+ - Explained that since Abandon returns no response, clients should
+ not use it if they need to know the outcome.
+ - Specified that Abandon and Unbind cannot be abandoned.
+
+C.1.26. Section 4.12 (Extended Operation)
+
+ - Specified how values of Extended operations defined in terms of
+ ASN.1 are to be encoded.
+ - Added instructions on what Extended operation specifications
+ consist of.
+ - Added a recommendation that servers advertise supported Extended
+ operations.
+
+C.1.27. Section 5.2 (Transfer Protocols)
+
+ - Moved referral-specific instructions into referral-related
+ sections.
+
+C.1.28. Section 7 (Security Considerations)
+
+ - Reworded notes regarding SASL not protecting certain aspects of
+ the LDAP Bind messages.
+ - Noted that Servers are encouraged to prevent directory
+ modifications by clients that have authenticated anonymously
+ [RFC4513].
+ - Added a note regarding the possibility of changes to security
+ factors (authentication, authorization, and data confidentiality).
+ - Warned against following referrals that may have been injected in
+ the data stream.
+ - Noted that servers should protect information equally, whether in
+ an error condition or not, and mentioned matchedDN,
+ diagnosticMessage, and resultCodes specifically.
+ - Added a note regarding malformed and long encodings.
+
+
+
+
+
+
+
+Sermersheim Standards Track [Page 65]
+
+RFC 4511 LDAPv3 June 2006
+
+
+C.1.29. Appendix A (Complete ASN.1 Definition)
+
+ - Added "EXTENSIBILITY IMPLIED" to ASN.1 definition.
+ - Removed AttributeType. It is not used.
+
+C.2. Changes Made to RFC 2830
+
+ This section summarizes the substantive changes made to Sections of
+ RFC 2830. Readers should consult [RFC4513] for summaries of changes
+ to other sections.
+
+C.2.1. Section 2.3 (Response other than "success")
+
+ - Removed wording indicating that referrals can be returned from
+ StartTLS.
+ - Removed requirement that only a narrow set of result codes can be
+ returned. Some result codes are required in certain scenarios, but
+ any other may be returned if appropriate.
+ - Removed requirement that the ExtendedResponse.responseName MUST be
+ present. There are circumstances where this is impossible, and
+ requiring this is at odds with language in Section 4.12.
+
+C.2.1. Section 4 (Closing a TLS Connection)
+
+ - Reworded most of this section to align with definitions of the
+ LDAP protocol layers.
+ - Removed instructions on abrupt closure as this is covered in other
+ areas of the document (specifically, Section 5.3)
+
+C.3. Changes Made to RFC 3771
+
+ - Rewrote to fit into this document. In general, semantics were
+ preserved. Supporting and background language seen as redundant
+ due to its presence in this document was omitted.
+
+ - Specified that Intermediate responses to a request may be of
+ different types, and one of the response types may be specified to
+ have no response value.
+
+
+
+
+
+
+
+
+
+
+
+
+
+Sermersheim Standards Track [Page 66]
+
+RFC 4511 LDAPv3 June 2006
+
+
+Editor's Address
+
+ Jim Sermersheim
+ Novell, Inc.
+ 1800 South Novell Place
+ Provo, Utah 84606, USA
+
+ Phone: +1 801 861-3088
+ EMail: jimse@novell.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Sermersheim Standards Track [Page 67]
+
+RFC 4511 LDAPv3 June 2006
+
+
+Full Copyright Statement
+
+ Copyright (C) The Internet Society (2006).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
+ ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
+ INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
+ INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be
+ found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at
+ ietf-ipr@ietf.org.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is provided by the IETF
+ Administrative Support Activity (IASA).
+
+
+
+
+
+
+
+Sermersheim Standards Track [Page 68]
+
diff --git a/source4/ldap_server/devdocs/rfc4512.txt b/source4/ldap_server/devdocs/rfc4512.txt
new file mode 100644
index 0000000000..f45a3f3e73
--- /dev/null
+++ b/source4/ldap_server/devdocs/rfc4512.txt
@@ -0,0 +1,2915 @@
+
+
+
+
+
+
+Network Working Group K. Zeilenga
+Request for Comments: 4512 OpenLDAP Foundation
+Obsoletes: 2251, 2252, 2256, 3674 June 2006
+Category: Standards Track
+
+
+ Lightweight Directory Access Protocol (LDAP):
+ Directory Information Models
+
+Status of This Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2006).
+
+Abstract
+
+ The Lightweight Directory Access Protocol (LDAP) is an Internet
+ protocol for accessing distributed directory services that act in
+ accordance with X.500 data and service models. This document
+ describes the X.500 Directory Information Models, as used in LDAP.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 1]
+
+RFC 4512 LDAP Models June 2006
+
+
+Table of Contents
+
+ 1. Introduction ....................................................3
+ 1.1. Relationship to Other LDAP Specifications ..................3
+ 1.2. Relationship to X.501 ......................................4
+ 1.3. Conventions ................................................4
+ 1.4. Common ABNF Productions ....................................4
+ 2. Model of Directory User Information .............................6
+ 2.1. The Directory Information Tree .............................7
+ 2.2. Structure of an Entry ......................................7
+ 2.3. Naming of Entries ..........................................8
+ 2.4. Object Classes .............................................9
+ 2.5. Attribute Descriptions ....................................12
+ 2.6. Alias Entries .............................................16
+ 3. Directory Administrative and Operational Information ...........17
+ 3.1. Subtrees ..................................................17
+ 3.2. Subentries ................................................18
+ 3.3. The 'objectClass' attribute ...............................18
+ 3.4. Operational Attributes ....................................19
+ 4. Directory Schema ...............................................22
+ 4.1. Schema Definitions ........................................23
+ 4.2. Subschema Subentries ......................................32
+ 4.3. 'extensibleObject' object class ...........................35
+ 4.4. Subschema Discovery .......................................35
+ 5. DSA (Server) Informational Model ...............................36
+ 5.1. Server-Specific Data Requirements .........................36
+ 6. Other Considerations ...........................................40
+ 6.1. Preservation of User Information ..........................40
+ 6.2. Short Names ...............................................41
+ 6.3. Cache and Shadowing .......................................41
+ 7. Implementation Guidelines ......................................42
+ 7.1. Server Guidelines .........................................42
+ 7.2. Client Guidelines .........................................42
+ 8. Security Considerations ........................................43
+ 9. IANA Considerations ............................................43
+ 10. Acknowledgements ..............................................44
+ 11. Normative References ..........................................45
+ Appendix A. Changes ...............................................47
+ A.1. Changes to RFC 2251 .......................................47
+ A.2. Changes to RFC 2252 .......................................49
+ A.3. Changes to RFC 2256 .......................................50
+ A.4. Changes to RFC 3674 .......................................51
+
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 2]
+
+RFC 4512 LDAP Models June 2006
+
+
+1. Introduction
+
+ This document discusses the X.500 Directory Information Models
+ [X.501], as used by the Lightweight Directory Access Protocol (LDAP)
+ [RFC4510].
+
+ The Directory is "a collection of open systems cooperating to provide
+ directory services" [X.500]. The information held in the Directory
+ is collectively known as the Directory Information Base (DIB). A
+ Directory user, which may be a human or other entity, accesses the
+ Directory through a client (or Directory User Agent (DUA)). The
+ client, on behalf of the directory user, interacts with one or more
+ servers (or Directory System Agents (DSA)). A server holds a
+ fragment of the DIB.
+
+ The DIB contains two classes of information:
+
+ 1) user information (e.g., information provided and administrated
+ by users). Section 2 describes the Model of User Information.
+
+ 2) administrative and operational information (e.g., information
+ used to administer and/or operate the directory). Section 3
+ describes the model of Directory Administrative and Operational
+ Information.
+
+ These two models, referred to as the generic Directory Information
+ Models, describe how information is represented in the Directory.
+ These generic models provide a framework for other information
+ models. Section 4 discusses the subschema information model and
+ subschema discovery. Section 5 discusses the DSA (Server)
+ Informational Model.
+
+ Other X.500 information models (such as access control distribution
+ knowledge and replication knowledge information models) may be
+ adapted for use in LDAP. Specification of how these models apply to
+ LDAP is left to future documents.
+
+1.1. Relationship to Other LDAP Specifications
+
+ This document is a integral part of the LDAP technical specification
+ [RFC4510], which obsoletes the previously defined LDAP technical
+ specification, RFC 3377, in its entirety.
+
+ This document obsoletes RFC 2251, Sections 3.2 and 3.4, as well as
+ portions of Sections 4 and 6. Appendix A.1 summarizes changes to
+ these sections. The remainder of RFC 2251 is obsoleted by the
+ [RFC4511], [RFC4513], and [RFC4510] documents.
+
+
+
+
+Zeilenga Standards Track [Page 3]
+
+RFC 4512 LDAP Models June 2006
+
+
+ This document obsoletes RFC 2252, Sections 4, 5, and 7. Appendix A.2
+ summarizes changes to these sections. The remainder of RFC 2252 is
+ obsoleted by [RFC4517].
+
+ This document obsoletes RFC 2256, Sections 5.1, 5.2, 7.1, and 7.2.
+ Appendix A.3 summarizes changes to these sections. The remainder of
+ RFC 2256 is obsoleted by [RFC4519] and [RFC4517].
+
+ This document obsoletes RFC 3674 in its entirety. Appendix A.4
+ summarizes changes since RFC 3674.
+
+1.2. Relationship to X.501
+
+ This document includes material, with and without adaptation, from
+ [X.501] as necessary to describe this protocol. These adaptations
+ (and any other differences herein) apply to this protocol, and only
+ this protocol.
+
+1.3. Conventions
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in BCP 14 [RFC2119].
+
+ Schema definitions are provided using LDAP description formats (as
+ defined in Section 4.1). Definitions provided here are formatted
+ (line wrapped) for readability. Matching rules and LDAP syntaxes
+ referenced in these definitions are specified in [RFC4517].
+
+1.4. Common ABNF Productions
+
+ A number of syntaxes in this document are described using Augmented
+ Backus-Naur Form (ABNF) [RFC4234]. These syntaxes (as well as a
+ number of syntaxes defined in other documents) rely on the following
+ common productions:
+
+ keystring = leadkeychar *keychar
+ leadkeychar = ALPHA
+ keychar = ALPHA / DIGIT / HYPHEN
+ number = DIGIT / ( LDIGIT 1*DIGIT )
+
+ ALPHA = %x41-5A / %x61-7A ; "A"-"Z" / "a"-"z"
+ DIGIT = %x30 / LDIGIT ; "0"-"9"
+ LDIGIT = %x31-39 ; "1"-"9"
+ HEX = DIGIT / %x41-46 / %x61-66 ; "0"-"9" / "A"-"F" / "a"-"f"
+
+ SP = 1*SPACE ; one or more " "
+ WSP = 0*SPACE ; zero or more " "
+
+
+
+Zeilenga Standards Track [Page 4]
+
+RFC 4512 LDAP Models June 2006
+
+
+ NULL = %x00 ; null (0)
+ SPACE = %x20 ; space (" ")
+ DQUOTE = %x22 ; quote (""")
+ SHARP = %x23 ; octothorpe (or sharp sign) ("#")
+ DOLLAR = %x24 ; dollar sign ("$")
+ SQUOTE = %x27 ; single quote ("'")
+ LPAREN = %x28 ; left paren ("(")
+ RPAREN = %x29 ; right paren (")")
+ PLUS = %x2B ; plus sign ("+")
+ COMMA = %x2C ; comma (",")
+ HYPHEN = %x2D ; hyphen ("-")
+ DOT = %x2E ; period (".")
+ SEMI = %x3B ; semicolon (";")
+ LANGLE = %x3C ; left angle bracket ("<")
+ EQUALS = %x3D ; equals sign ("=")
+ RANGLE = %x3E ; right angle bracket (">")
+ ESC = %x5C ; backslash ("\")
+ USCORE = %x5F ; underscore ("_")
+ LCURLY = %x7B ; left curly brace "{"
+ RCURLY = %x7D ; right curly brace "}"
+
+ ; Any UTF-8 [RFC3629] encoded Unicode [Unicode] character
+ UTF8 = UTF1 / UTFMB
+ UTFMB = UTF2 / UTF3 / UTF4
+ UTF0 = %x80-BF
+ UTF1 = %x00-7F
+ UTF2 = %xC2-DF UTF0
+ UTF3 = %xE0 %xA0-BF UTF0 / %xE1-EC 2(UTF0) /
+ %xED %x80-9F UTF0 / %xEE-EF 2(UTF0)
+ UTF4 = %xF0 %x90-BF 2(UTF0) / %xF1-F3 3(UTF0) /
+ %xF4 %x80-8F 2(UTF0)
+
+ OCTET = %x00-FF ; Any octet (8-bit data unit)
+
+ Object identifiers (OIDs) [X.680] are represented in LDAP using a
+ dot-decimal format conforming to the ABNF:
+
+ numericoid = number 1*( DOT number )
+
+ Short names, also known as descriptors, are used as more readable
+ aliases for object identifiers. Short names are case insensitive and
+ conform to the ABNF:
+
+ descr = keystring
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 5]
+
+RFC 4512 LDAP Models June 2006
+
+
+ Where either an object identifier or a short name may be specified,
+ the following production is used:
+
+ oid = descr / numericoid
+
+ While the <descr> form is generally preferred when the usage is
+ restricted to short names referring to object identifiers that
+ identify like kinds of objects (e.g., attribute type descriptions,
+ matching rule descriptions, object class descriptions), the
+ <numericoid> form should be used when the object identifiers may
+ identify multiple kinds of objects or when an unambiguous short name
+ (descriptor) is not available.
+
+ Implementations SHOULD treat short names (descriptors) used in an
+ ambiguous manner (as discussed above) as unrecognized.
+
+ Short Names (descriptors) are discussed further in Section 6.2.
+
+2. Model of Directory User Information
+
+ As [X.501] states:
+
+ The purpose of the Directory is to hold, and provide access to,
+ information about objects of interest (objects) in some 'world'.
+ An object can be anything which is identifiable (can be named).
+
+ An object class is an identified family of objects, or conceivable
+ objects, which share certain characteristics. Every object
+ belongs to at least one class. An object class may be a subclass
+ of other object classes, in which case the members of the former
+ class, the subclass, are also considered to be members of the
+ latter classes, the superclasses. There may be subclasses of
+ subclasses, etc., to an arbitrary depth.
+
+ A directory entry, a named collection of information, is the basic
+ unit of information held in the Directory. There are multiple kinds
+ of directory entries.
+
+ An object entry represents a particular object. An alias entry
+ provides alternative naming. A subentry holds administrative and/or
+ operational information.
+
+ The set of entries representing the DIB are organized hierarchically
+ in a tree structure known as the Directory Information Tree (DIT).
+
+ Section 2.1 describes the Directory Information Tree.
+ Section 2.2 discusses the structure of entries.
+ Section 2.3 discusses naming of entries.
+
+
+
+Zeilenga Standards Track [Page 6]
+
+RFC 4512 LDAP Models June 2006
+
+
+ Section 2.4 discusses object classes.
+ Section 2.5 discusses attribute descriptions.
+ Section 2.6 discusses alias entries.
+
+2.1. The Directory Information Tree
+
+ As noted above, the DIB is composed of a set of entries organized
+ hierarchically in a tree structure known as the Directory Information
+ Tree (DIT); specifically, a tree where vertices are the entries.
+
+ The arcs between vertices define relations between entries. If an
+ arc exists from X to Y, then the entry at X is the immediate superior
+ of Y, and Y is the immediate subordinate of X. An entry's superiors
+ are the entry's immediate superior and its superiors. An entry's
+ subordinates are all of its immediate subordinates and their
+ subordinates.
+
+ Similarly, the superior/subordinate relationship between object
+ entries can be used to derive a relation between the objects they
+ represent. DIT structure rules can be used to govern relationships
+ between objects.
+
+ Note: An entry's immediate superior is also known as the entry's
+ parent, and an entry's immediate subordinate is also known as
+ the entry's child. Entries that have the same parent are known
+ as siblings.
+
+2.2. Structure of an Entry
+
+ An entry consists of a set of attributes that hold information about
+ the object that the entry represents. Some attributes represent user
+ information and are called user attributes. Other attributes
+ represent operational and/or administrative information and are
+ called operational attributes.
+
+ An attribute is an attribute description (a type and zero or more
+ options) with one or more associated values. An attribute is often
+ referred to by its attribute description. For example, the
+ 'givenName' attribute is the attribute that consists of the attribute
+ description 'givenName' (the 'givenName' attribute type [RFC4519] and
+ zero options) and one or more associated values.
+
+ The attribute type governs whether the attribute can have multiple
+ values, the syntax and matching rules used to construct and compare
+ values of that attribute, and other functions. Options indicate
+ subtypes and other functions.
+
+ Attribute values conform to the defined syntax of the attribute type.
+
+
+
+Zeilenga Standards Track [Page 7]
+
+RFC 4512 LDAP Models June 2006
+
+
+ No two values of an attribute may be equivalent. Two values are
+ considered equivalent if and only if they would match according to
+ the equality matching rule of the attribute type. Or, if the
+ attribute type is defined with no equality matching rule, two values
+ are equivalent if and only if they are identical. (See 2.5.1 for
+ other restrictions.)
+
+ For example, a 'givenName' attribute can have more than one value,
+ they must be Directory Strings, and they are case insensitive. A
+ 'givenName' attribute cannot hold both "John" and "JOHN", as these
+ are equivalent values per the equality matching rule of the attribute
+ type.
+
+ Additionally, no attribute is to have a value that is not equivalent
+ to itself. For example, the 'givenName' attribute cannot have as a
+ value a directory string that includes the REPLACEMENT CHARACTER
+ (U+FFFD) code point, as matching involving that directory string is
+ Undefined per this attribute's equality matching rule.
+
+ When an attribute is used for naming of the entry, one and only one
+ value of the attribute is used in forming the Relative Distinguished
+ Name. This value is known as a distinguished value.
+
+2.3. Naming of Entries
+
+2.3.1. Relative Distinguished Names
+
+ Each entry is named relative to its immediate superior. This
+ relative name, known as its Relative Distinguished Name (RDN)
+ [X.501], is composed of an unordered set of one or more attribute
+ value assertions (AVA) consisting of an attribute description with
+ zero options and an attribute value. These AVAs are chosen to match
+ attribute values (each a distinguished value) of the entry.
+
+ An entry's relative distinguished name must be unique among all
+ immediate subordinates of the entry's immediate superior (i.e., all
+ siblings).
+
+ The following are examples of string representations of RDNs
+ [RFC4514]:
+
+ UID=12345
+ OU=Engineering
+ CN=Kurt Zeilenga+L=Redwood Shores
+
+ The last is an example of a multi-valued RDN; that is, an RDN
+ composed of multiple AVAs.
+
+
+
+
+Zeilenga Standards Track [Page 8]
+
+RFC 4512 LDAP Models June 2006
+
+
+2.3.2. Distinguished Names
+
+ An entry's fully qualified name, known as its Distinguished Name (DN)
+ [X.501], is the concatenation of its RDN and its immediate superior's
+ DN. A Distinguished Name unambiguously refers to an entry in the
+ tree. The following are examples of string representations of DNs
+ [RFC4514]:
+
+ UID=nobody@example.com,DC=example,DC=com
+ CN=John Smith,OU=Sales,O=ACME Limited,L=Moab,ST=Utah,C=US
+
+2.3.3. Alias Names
+
+ An alias, or alias name, is "an name for an object, provided by the
+ use of alias entries" [X.501]. Alias entries are described in
+ Section 2.6.
+
+2.4. Object Classes
+
+ An object class is "an identified family of objects (or conceivable
+ objects) that share certain characteristics" [X.501].
+
+ As defined in [X.501]:
+
+ Object classes are used in the Directory for a number of purposes:
+
+ - describing and categorizing objects and the entries that
+ correspond to these objects;
+
+ - where appropriate, controlling the operation of the Directory;
+
+ - regulating, in conjunction with DIT structure rule
+ specifications, the position of entries in the DIT;
+
+ - regulating, in conjunction with DIT content rule
+ specifications, the attributes that are contained in entries;
+
+ - identifying classes of entry that are to be associated with a
+ particular policy by the appropriate administrative authority.
+
+ An object class (a subclass) may be derived from an object class
+ (its direct superclass) which is itself derived from an even more
+ generic object class. For structural object classes, this process
+ stops at the most generic object class, 'top' (defined in Section
+ 2.4.1). An ordered set of superclasses up to the most superior
+ object class of an object class is its superclass chain.
+
+
+
+
+
+Zeilenga Standards Track [Page 9]
+
+RFC 4512 LDAP Models June 2006
+
+
+ An object class may be derived from two or more direct
+ superclasses (superclasses not part of the same superclass chain).
+ This feature of subclassing is termed multiple inheritance.
+
+ Each object class identifies the set of attributes required to be
+ present in entries belonging to the class and the set of attributes
+ allowed to be present in entries belonging to the class. As an entry
+ of a class must meet the requirements of each class it belongs to, it
+ can be said that an object class inherits the sets of allowed and
+ required attributes from its superclasses. A subclass can identify
+ an attribute allowed by its superclass as being required. If an
+ attribute is a member of both sets, it is required to be present.
+
+ Each object class is defined to be one of three kinds of object
+ classes: Abstract, Structural, or Auxiliary.
+
+ Each object class is identified by an object identifier (OID) and,
+ optionally, one or more short names (descriptors).
+
+2.4.1. Abstract Object Classes
+
+ An abstract object class, as the name implies, provides a base of
+ characteristics from which other object classes can be defined to
+ inherit from. An entry cannot belong to an abstract object class
+ unless it belongs to a structural or auxiliary class that inherits
+ from that abstract class.
+
+ Abstract object classes cannot derive from structural or auxiliary
+ object classes.
+
+ All structural object classes derive (directly or indirectly) from
+ the 'top' abstract object class. Auxiliary object classes do not
+ necessarily derive from 'top'.
+
+ The following is the object class definition (see Section 4.1.1) for
+ the 'top' object class:
+
+ ( 2.5.6.0 NAME 'top' ABSTRACT MUST objectClass )
+
+ All entries belong to the 'top' abstract object class.
+
+
+
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 10]
+
+RFC 4512 LDAP Models June 2006
+
+
+2.4.2. Structural Object Classes
+
+ As stated in [X.501]:
+
+ An object class defined for use in the structural specification of
+ the DIT is termed a structural object class. Structural object
+ classes are used in the definition of the structure of the names
+ of the objects for compliant entries.
+
+ An object or alias entry is characterized by precisely one
+ structural object class superclass chain which has a single
+ structural object class as the most subordinate object class.
+ This structural object class is referred to as the structural
+ object class of the entry.
+
+ Structural object classes are related to associated entries:
+
+ - an entry conforming to a structural object class shall
+ represent the real-world object constrained by the object
+ class;
+
+ - DIT structure rules only refer to structural object classes;
+ the structural object class of an entry is used to specify the
+ position of the entry in the DIT;
+
+ - the structural object class of an entry is used, along with an
+ associated DIT content rule, to control the content of an
+ entry.
+
+ The structural object class of an entry shall not be changed.
+
+ Each structural object class is a (direct or indirect) subclass of
+ the 'top' abstract object class.
+
+ Structural object classes cannot subclass auxiliary object classes.
+
+ Each entry is said to belong to its structural object class as well
+ as all classes in its structural object class's superclass chain.
+
+2.4.3. Auxiliary Object Classes
+
+ Auxiliary object classes are used to augment the characteristics of
+ entries. They are commonly used to augment the sets of attributes
+ required and allowed to be present in an entry. They can be used to
+ describe entries or classes of entries.
+
+ Auxiliary object classes cannot subclass structural object classes.
+
+
+
+
+Zeilenga Standards Track [Page 11]
+
+RFC 4512 LDAP Models June 2006
+
+
+ An entry can belong to any subset of the set of auxiliary object
+ classes allowed by the DIT content rule associated with the
+ structural object class of the entry. If no DIT content rule is
+ associated with the structural object class of the entry, the entry
+ cannot belong to any auxiliary object class.
+
+ The set of auxiliary object classes that an entry belongs to can
+ change over time.
+
+2.5. Attribute Descriptions
+
+ An attribute description is composed of an attribute type (see
+ Section 2.5.1) and a set of zero or more attribute options (see
+ Section 2.5.2).
+
+ An attribute description is represented by the ABNF:
+
+ attributedescription = attributetype options
+ attributetype = oid
+ options = *( SEMI option )
+ option = 1*keychar
+
+ where <attributetype> identifies the attribute type and each <option>
+ identifies an attribute option. Both <attributetype> and <option>
+ productions are case insensitive. The order in which <option>s
+ appear is irrelevant. That is, any two <attributedescription>s that
+ consist of the same <attributetype> and same set of <option>s are
+ equivalent.
+
+ Examples of valid attribute descriptions:
+
+ 2.5.4.0
+ cn;lang-de;lang-en
+ owner
+
+ An attribute description with an unrecognized attribute type is to be
+ treated as unrecognized. Servers SHALL treat an attribute
+ description with an unrecognized attribute option as unrecognized.
+ Clients MAY treat an unrecognized attribute option as a tagging
+ option (see Section 2.5.2.1).
+
+ All attributes of an entry must have distinct attribute descriptions.
+
+2.5.1. Attribute Types
+
+ An attribute type governs whether the attribute can have multiple
+ values, the syntax and matching rules used to construct and compare
+ values of that attribute, and other functions.
+
+
+
+Zeilenga Standards Track [Page 12]
+
+RFC 4512 LDAP Models June 2006
+
+
+ If no equality matching is specified for the attribute type:
+
+ - the attribute (of the type) cannot be used for naming;
+ - when adding the attribute (or replacing all values), no two
+ values may be equivalent (see 2.2);
+ - individual values of a multi-valued attribute are not to be
+ independently added or deleted;
+ - attribute value assertions (such as matching in search filters
+ and comparisons) using values of such a type cannot be
+ performed.
+
+ Otherwise, the specified equality matching rule is to be used to
+ evaluate attribute value assertions concerning the attribute type.
+ The specified equality rule is to be transitive and commutative.
+
+ The attribute type indicates whether the attribute is a user
+ attribute or an operational attribute. If operational, the attribute
+ type indicates the operational usage and whether or not the attribute
+ is modifiable by users. Operational attributes are discussed in
+ Section 3.4.
+
+ An attribute type (a subtype) may derive from a more generic
+ attribute type (a direct supertype). The following restrictions
+ apply to subtyping:
+
+ - a subtype must have the same usage as its direct supertype,
+ - a subtype's syntax must be the same, or a refinement of, its
+ supertype's syntax, and
+ - a subtype must be collective [RFC3671] if its supertype is
+ collective.
+
+ An attribute description consisting of a subtype and no options is
+ said to be the direct description subtype of the attribute
+ description consisting of the subtype's direct supertype and no
+ options.
+
+ Each attribute type is identified by an object identifier (OID) and,
+ optionally, one or more short names (descriptors).
+
+2.5.2. Attribute Options
+
+ There are multiple kinds of attribute description options. The LDAP
+ technical specification details one kind: tagging options.
+
+ Not all options can be associated with attributes held in the
+ directory. Tagging options can be.
+
+
+
+
+
+Zeilenga Standards Track [Page 13]
+
+RFC 4512 LDAP Models June 2006
+
+
+ Not all options can be used in conjunction with all attribute types.
+ In such cases, the attribute description is to be treated as
+ unrecognized.
+
+ An attribute description that contains mutually exclusive options
+ shall be treated as unrecognized. That is, "cn;x-bar;x-foo", where
+ "x-foo" and "x-bar" are mutually exclusive, is to be treated as
+ unrecognized.
+
+ Other kinds of options may be specified in future documents. These
+ documents must detail how new kinds of options they define relate to
+ tagging options. In particular, these documents must detail whether
+ or not new kinds of options can be associated with attributes held in
+ the directory, how new kinds of options affect transfer of attribute
+ values, and how new kinds of options are treated in attribute
+ description hierarchies.
+
+ Options are represented as short, case-insensitive textual strings
+ conforming to the <option> production defined in Section 2.5 of this
+ document.
+
+ Procedures for registering options are detailed in BCP 64, RFC 4520
+ [RFC4520].
+
+2.5.2.1. Tagging Options
+
+ Attributes held in the directory can have attribute descriptions with
+ any number of tagging options. Tagging options are never mutually
+ exclusive.
+
+ An attribute description with N tagging options is a direct
+ (description) subtype of all attribute descriptions of the same
+ attribute type and all but one of the N options. If the attribute
+ type has a supertype, then the attribute description is also a direct
+ (description) subtype of the attribute description of the supertype
+ and the N tagging options. That is, 'cn;lang-de;lang-en' is a direct
+ (description) subtype of 'cn;lang-de', 'cn;lang-en', and
+ 'name;lang-de;lang-en' ('cn' is a subtype of 'name'; both are defined
+ in [RFC4519]).
+
+2.5.3. Attribute Description Hierarchies
+
+ An attribute description can be the direct subtype of zero or more
+ other attribute descriptions as indicated by attribute type subtyping
+ (as described in Section 2.5.1) or attribute tagging option subtyping
+ (as described in Section 2.5.2.1). These subtyping relationships are
+ used to form hierarchies of attribute descriptions and attributes.
+
+
+
+
+Zeilenga Standards Track [Page 14]
+
+RFC 4512 LDAP Models June 2006
+
+
+ As adapted from [X.501]:
+
+ Attribute hierarchies allow access to the DIB with varying degrees
+ of granularity. This is achieved by allowing the value components
+ of attributes to be accessed by using either their specific
+ attribute description (a direct reference to the attribute) or a
+ more generic attribute description (an indirect reference).
+
+ Semantically related attributes may be placed in a hierarchical
+ relationship, the more specialized being placed subordinate to the
+ more generalized. Searching for or retrieving attributes and
+ their values is made easier by quoting the more generalized
+ attribute description; a filter item so specified is evaluated for
+ the more specialized descriptions as well as for the quoted
+ description.
+
+ Where subordinate specialized descriptions are selected to be
+ returned as part of a search result these descriptions shall be
+ returned if available. Where the more general descriptions are
+ selected to be returned as part of a search result both the
+ general and the specialized descriptions shall be returned, if
+ available. An attribute value shall always be returned as a value
+ of its own attribute description.
+
+ All of the attribute descriptions in an attribute hierarchy are
+ treated as distinct and unrelated descriptions for user
+ modification of entry content.
+
+ An attribute value stored in an object or alias entry is of
+ precisely one attribute description. The description is indicated
+ when the value is originally added to the entry.
+
+ For the purpose of subschema administration of the entry, a
+ specification that an attribute is required is fulfilled if the entry
+ contains a value of an attribute description belonging to an
+ attribute hierarchy where the attribute type of that description is
+ the same as the required attribute's type. That is, a "MUST name"
+ specification is fulfilled by 'name' or 'name;x-tag-option', but is
+ not fulfilled by 'CN' or 'CN;x-tag-option' (even though 'CN' is a
+ subtype of 'name'). Likewise, an entry may contain a value of an
+ attribute description belonging to an attribute hierarchy where the
+ attribute type of that description is either explicitly included in
+ the definition of an object class to which the entry belongs or
+ allowed by the DIT content rule applicable to that entry. That is,
+ 'name' and 'name;x-tag-option' are allowed by "MAY name" (or by "MUST
+ name"), but 'CN' and 'CN;x-tag-option' are not allowed by "MAY name"
+ (or by "MUST name").
+
+
+
+
+Zeilenga Standards Track [Page 15]
+
+RFC 4512 LDAP Models June 2006
+
+
+ For the purposes of other policy administration, unless stated
+ otherwise in the specification of the particular administrative
+ model, all of the attribute descriptions in an attribute hierarchy
+ are treated as distinct and unrelated descriptions.
+
+2.6. Alias Entries
+
+ As adapted from [X.501]:
+
+ An alias, or an alias name, for an object is an alternative name
+ for an object or object entry which is provided by the use of
+ alias entries.
+
+ Each alias entry contains, within the 'aliasedObjectName'
+ attribute (known as the 'aliasedEntryName' attribute in X.500), a
+ name of some object. The distinguished name of the alias entry is
+ thus also a name for this object.
+
+ NOTE - The name within the 'aliasedObjectName' is said to be
+ pointed to by the alias. It does not have to be the
+ distinguished name of any entry.
+
+ The conversion of an alias name to an object name is termed
+ (alias) dereferencing and comprises the systematic replacement of
+ alias names, where found within a purported name, by the value of
+ the corresponding 'aliasedObjectName' attribute. The process may
+ require the examination of more than one alias entry.
+
+ Any particular entry in the DIT may have zero or more alias names.
+ It therefore follows that several alias entries may point to the
+ same entry. An alias entry may point to an entry that is not a
+ leaf entry and may point to another alias entry.
+
+ An alias entry shall have no subordinates, so that an alias entry
+ is always a leaf entry.
+
+ Every alias entry shall belong to the 'alias' object class.
+
+ An entry with the 'alias' object class must also belong to an object
+ class (or classes), or be governed by a DIT content rule, which
+ allows suitable naming attributes to be present.
+
+ Example:
+
+ dn: cn=bar,dc=example,dc=com
+ objectClass: top
+ objectClass: alias
+ objectClass: extensibleObject
+
+
+
+Zeilenga Standards Track [Page 16]
+
+RFC 4512 LDAP Models June 2006
+
+
+ cn: bar
+ aliasedObjectName: cn=foo,dc=example,dc=com
+
+2.6.1. 'alias' Object Class
+
+ Alias entries belong to the 'alias' object class.
+
+ ( 2.5.6.1 NAME 'alias'
+ SUP top STRUCTURAL
+ MUST aliasedObjectName )
+
+2.6.2. 'aliasedObjectName' Attribute Type
+
+ The 'aliasedObjectName' attribute holds the name of the entry an
+ alias points to. The 'aliasedObjectName' attribute is known as the
+ 'aliasedEntryName' attribute in X.500.
+
+ ( 2.5.4.1 NAME 'aliasedObjectName'
+ EQUALITY distinguishedNameMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
+ SINGLE-VALUE )
+
+ The 'distinguishedNameMatch' matching rule and the DistinguishedName
+ (1.3.6.1.4.1.1466.115.121.1.12) syntax are defined in [RFC4517].
+
+3. Directory Administrative and Operational Information
+
+ This section discusses select aspects of the X.500 Directory
+ Administrative and Operational Information model [X.501]. LDAP
+ implementations MAY support other aspects of this model.
+
+3.1. Subtrees
+
+ As defined in [X.501]:
+
+ A subtree is a collection of object and alias entries situated at
+ the vertices of a tree. Subtrees do not contain subentries. The
+ prefix sub, in subtree, emphasizes that the base (or root) vertex
+ of this tree is usually subordinate to the root of the DIT.
+
+ A subtree begins at some vertex and extends to some identifiable
+ lower boundary, possibly extending to leaves. A subtree is always
+ defined within a context which implicitly bounds the subtree. For
+ example, the vertex and lower boundaries of a subtree defining a
+ replicated area are bounded by a naming context.
+
+
+
+
+
+
+Zeilenga Standards Track [Page 17]
+
+RFC 4512 LDAP Models June 2006
+
+
+3.2. Subentries
+
+ A subentry is a "special sort of entry, known by the Directory, used
+ to hold information associated with a subtree or subtree refinement"
+ [X.501]. Subentries are used in Directory to hold for administrative
+ and operational purposes as defined in [X.501]. Their use in LDAP is
+ detailed in [RFC3672].
+
+ The term "(sub)entry" in this specification indicates that servers
+ implementing X.500(93) models are, in accordance with X.500(93) as
+ described in [RFC3672], to use a subentry and that other servers are
+ to use an object entry belonging to the appropriate auxiliary class
+ normally used with the subentry (e.g., 'subschema' for subschema
+ subentries) to mimic the subentry. This object entry's RDN SHALL be
+ formed from a value of the 'cn' (commonName) attribute [RFC4519] (as
+ all subentries are named with 'cn').
+
+3.3. The 'objectClass' attribute
+
+ Each entry in the DIT has an 'objectClass' attribute.
+
+ ( 2.5.4.0 NAME 'objectClass'
+ EQUALITY objectIdentifierMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )
+
+ The 'objectIdentifierMatch' matching rule and the OBJECT IDENTIFIER
+ (1.3.6.1.4.1.1466.115.121.1.38) syntax are defined in [RFC4517].
+
+ The 'objectClass' attribute specifies the object classes of an entry,
+ which (among other things) are used in conjunction with the
+ controlling schema to determine the permitted attributes of an entry.
+ Values of this attribute can be modified by clients, but the
+ 'objectClass' attribute cannot be removed.
+
+ Servers that follow X.500(93) models SHALL restrict modifications of
+ this attribute to prevent the basic structural class of the entry
+ from being changed. That is, one cannot change a 'person' into a
+ 'country'.
+
+ When creating an entry or adding an 'objectClass' value to an entry,
+ all superclasses of the named classes SHALL be implicitly added as
+ well if not already present. That is, if the auxiliary class 'x-a'
+ is a subclass of the class 'x-b', adding 'x-a' to 'objectClass'
+ causes 'x-b' to be implicitly added (if is not already present).
+
+ Servers SHALL restrict modifications of this attribute to prevent
+ superclasses of remaining 'objectClass' values from being deleted.
+ That is, if the auxiliary class 'x-a' is a subclass of the auxiliary
+
+
+
+Zeilenga Standards Track [Page 18]
+
+RFC 4512 LDAP Models June 2006
+
+
+ class 'x-b' and the 'objectClass' attribute contains 'x-a' and 'x-b',
+ an attempt to delete only 'x-b' from the 'objectClass' attribute is
+ an error.
+
+3.4. Operational Attributes
+
+ Some attributes, termed operational attributes, are used or
+ maintained by servers for administrative and operational purposes.
+ As stated in [X.501]: "There are three varieties of operational
+ attributes: Directory operational attributes, DSA-shared operational
+ attributes, and DSA-specific operational attributes".
+
+ A directory operational attribute is used to represent operational
+ and/or administrative information in the Directory Information Model.
+ This includes operational attributes maintained by the server (e.g.,
+ 'createTimestamp') as well as operational attributes that hold values
+ administrated by the user (e.g., 'ditContentRules').
+
+ A DSA-shared operational attribute is used to represent information
+ of the DSA Information Model that is shared between DSAs.
+
+ A DSA-specific operational attribute is used to represent information
+ of the DSA Information Model that is specific to the DSA (though, in
+ some cases, may be derived from information shared between DSAs;
+ e.g., 'namingContexts').
+
+ The DSA Information Model operational attributes are detailed in
+ [X.501].
+
+ Operational attributes are not normally visible. They are not
+ returned in search results unless explicitly requested by name.
+
+ Not all operational attributes are user modifiable.
+
+ Entries may contain, among others, the following operational
+ attributes:
+
+ - creatorsName: the Distinguished Name of the user who added this
+ entry to the directory,
+
+ - createTimestamp: the time this entry was added to the directory,
+
+ - modifiersName: the Distinguished Name of the user who last
+ modified this entry, and
+
+ - modifyTimestamp: the time this entry was last modified.
+
+
+
+
+
+Zeilenga Standards Track [Page 19]
+
+RFC 4512 LDAP Models June 2006
+
+
+ Servers SHOULD maintain the 'creatorsName', 'createTimestamp',
+ 'modifiersName', and 'modifyTimestamp' attributes for all entries of
+ the DIT.
+
+3.4.1. 'creatorsName'
+
+ This attribute appears in entries that were added using the protocol
+ (e.g., using the Add operation). The value is the distinguished name
+ of the creator.
+
+ ( 2.5.18.3 NAME 'creatorsName'
+ EQUALITY distinguishedNameMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
+ SINGLE-VALUE NO-USER-MODIFICATION
+ USAGE directoryOperation )
+
+ The 'distinguishedNameMatch' matching rule and the DistinguishedName
+ (1.3.6.1.4.1.1466.115.121.1.12) syntax are defined in [RFC4517].
+
+3.4.2. 'createTimestamp'
+
+ This attribute appears in entries that were added using the protocol
+ (e.g., using the Add operation). The value is the time the entry was
+ added.
+
+ ( 2.5.18.1 NAME 'createTimestamp'
+ EQUALITY generalizedTimeMatch
+ ORDERING generalizedTimeOrderingMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.24
+ SINGLE-VALUE NO-USER-MODIFICATION
+ USAGE directoryOperation )
+
+ The 'generalizedTimeMatch' and 'generalizedTimeOrderingMatch'
+ matching rules and the GeneralizedTime
+ (1.3.6.1.4.1.1466.115.121.1.24) syntax are defined in [RFC4517].
+
+3.4.3. 'modifiersName'
+
+ This attribute appears in entries that have been modified using the
+ protocol (e.g., using the Modify operation). The value is the
+ distinguished name of the last modifier.
+
+ ( 2.5.18.4 NAME 'modifiersName'
+ EQUALITY distinguishedNameMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
+ SINGLE-VALUE NO-USER-MODIFICATION
+ USAGE directoryOperation )
+
+
+
+
+Zeilenga Standards Track [Page 20]
+
+RFC 4512 LDAP Models June 2006
+
+
+ The 'distinguishedNameMatch' matching rule and the DistinguishedName
+ (1.3.6.1.4.1.1466.115.121.1.12) syntax are defined in [RFC4517].
+
+3.4.4. 'modifyTimestamp'
+
+ This attribute appears in entries that have been modified using the
+ protocol (e.g., using the Modify operation). The value is the time
+ the entry was last modified.
+
+ ( 2.5.18.2 NAME 'modifyTimestamp'
+ EQUALITY generalizedTimeMatch
+ ORDERING generalizedTimeOrderingMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.24
+ SINGLE-VALUE NO-USER-MODIFICATION
+ USAGE directoryOperation )
+
+ The 'generalizedTimeMatch' and 'generalizedTimeOrderingMatch'
+ matching rules and the GeneralizedTime
+ (1.3.6.1.4.1.1466.115.121.1.24) syntax are defined in [RFC4517].
+
+3.4.5. 'structuralObjectClass'
+
+ This attribute indicates the structural object class of the entry.
+
+ ( 2.5.21.9 NAME 'structuralObjectClass'
+ EQUALITY objectIdentifierMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.38
+ SINGLE-VALUE NO-USER-MODIFICATION
+ USAGE directoryOperation )
+
+ The 'objectIdentifierMatch' matching rule and OBJECT IDENTIFIER
+ (1.3.6.1.4.1.1466.115.121.1.38) syntax is defined in [RFC4517].
+
+3.4.6. 'governingStructureRule'
+
+ This attribute indicates the structure rule governing the entry.
+
+ ( 2.5.21.10 NAME 'governingStructureRule'
+ EQUALITY integerMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
+ SINGLE-VALUE NO-USER-MODIFICATION
+ USAGE directoryOperation )
+
+ The 'integerMatch' matching rule and INTEGER
+ (1.3.6.1.4.1.1466.115.121.1.27) syntax is defined in [RFC4517].
+
+
+
+
+
+
+Zeilenga Standards Track [Page 21]
+
+RFC 4512 LDAP Models June 2006
+
+
+4. Directory Schema
+
+ As defined in [X.501]:
+
+ The Directory Schema is a set of definitions and constraints
+ concerning the structure of the DIT, the possible ways entries are
+ named, the information that can be held in an entry, the
+ attributes used to represent that information and their
+ organization into hierarchies to facilitate search and retrieval
+ of the information and the ways in which values of attributes may
+ be matched in attribute value and matching rule assertions.
+
+ NOTE 1 - The schema enables the Directory system to, for example:
+
+ - prevent the creation of subordinate entries of the wrong
+ object-class (e.g., a country as a subordinate of a person);
+
+ - prevent the addition of attribute-types to an entry
+ inappropriate to the object-class (e.g., a serial number to a
+ person's entry);
+
+ - prevent the addition of an attribute value of a syntax not
+ matching that defined for the attribute-type (e.g., a printable
+ string to a bit string).
+
+ Formally, the Directory Schema comprises a set of:
+
+ a) Name Form definitions that define primitive naming relations
+ for structural object classes;
+
+ b) DIT Structure Rule definitions that define the names that
+ entries may have and the ways in which the entries may be
+ related to one another in the DIT;
+
+ c) DIT Content Rule definitions that extend the specification of
+ allowable attributes for entries beyond those indicated by the
+ structural object classes of the entries;
+
+ d) Object Class definitions that define the basic set of mandatory
+ and optional attributes that shall be present, and may be
+ present, respectively, in an entry of a given class, and which
+ indicate the kind of object class that is being defined;
+
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 22]
+
+RFC 4512 LDAP Models June 2006
+
+
+ e) Attribute Type definitions that identify the object identifier
+ by which an attribute is known, its syntax, associated matching
+ rules, whether it is an operational attribute and if so its
+ type, whether it is a collective attribute, whether it is
+ permitted to have multiple values and whether or not it is
+ derived from another attribute type;
+
+ f) Matching Rule definitions that define matching rules.
+
+ And in LDAP:
+
+ g) LDAP Syntax definitions that define encodings used in LDAP.
+
+4.1. Schema Definitions
+
+ Schema definitions in this section are described using ABNF and rely
+ on the common productions specified in Section 1.2 as well as these:
+
+ noidlen = numericoid [ LCURLY len RCURLY ]
+ len = number
+
+ oids = oid / ( LPAREN WSP oidlist WSP RPAREN )
+ oidlist = oid *( WSP DOLLAR WSP oid )
+
+ extensions = *( SP xstring SP qdstrings )
+ xstring = "X" HYPHEN 1*( ALPHA / HYPHEN / USCORE )
+
+ qdescrs = qdescr / ( LPAREN WSP qdescrlist WSP RPAREN )
+ qdescrlist = [ qdescr *( SP qdescr ) ]
+ qdescr = SQUOTE descr SQUOTE
+
+ qdstrings = qdstring / ( LPAREN WSP qdstringlist WSP RPAREN )
+ qdstringlist = [ qdstring *( SP qdstring ) ]
+ qdstring = SQUOTE dstring SQUOTE
+ dstring = 1*( QS / QQ / QUTF8 ) ; escaped UTF-8 string
+
+ QQ = ESC %x32 %x37 ; "\27"
+ QS = ESC %x35 ( %x43 / %x63 ) ; "\5C" / "\5c"
+
+ ; Any UTF-8 encoded Unicode character
+ ; except %x27 ("\'") and %x5C ("\")
+ QUTF8 = QUTF1 / UTFMB
+
+ ; Any ASCII character except %x27 ("\'") and %x5C ("\")
+ QUTF1 = %x00-26 / %x28-5B / %x5D-7F
+
+ Schema definitions in this section also share a number of common
+ terms.
+
+
+
+Zeilenga Standards Track [Page 23]
+
+RFC 4512 LDAP Models June 2006
+
+
+ The NAME field provides a set of short names (descriptors) that are
+ to be used as aliases for the OID.
+
+ The DESC field optionally allows a descriptive string to be provided
+ by the directory administrator and/or implementor. While
+ specifications may suggest a descriptive string, there is no
+ requirement that the suggested (or any) descriptive string be used.
+
+ The OBSOLETE field, if present, indicates the element is not active.
+
+ Implementors should note that future versions of this document may
+ expand these definitions to include additional terms. Terms whose
+ identifier begins with "X-" are reserved for private experiments and
+ are followed by <SP> and <qdstrings> tokens.
+
+4.1.1. Object Class Definitions
+
+ Object Class definitions are written according to the ABNF:
+
+ ObjectClassDescription = LPAREN WSP
+ numericoid ; object identifier
+ [ SP "NAME" SP qdescrs ] ; short names (descriptors)
+ [ SP "DESC" SP qdstring ] ; description
+ [ SP "OBSOLETE" ] ; not active
+ [ SP "SUP" SP oids ] ; superior object classes
+ [ SP kind ] ; kind of class
+ [ SP "MUST" SP oids ] ; attribute types
+ [ SP "MAY" SP oids ] ; attribute types
+ extensions WSP RPAREN
+
+ kind = "ABSTRACT" / "STRUCTURAL" / "AUXILIARY"
+
+ where:
+ <numericoid> is object identifier assigned to this object class;
+ NAME <qdescrs> are short names (descriptors) identifying this
+ object class;
+ DESC <qdstring> is a short descriptive string;
+ OBSOLETE indicates this object class is not active;
+ SUP <oids> specifies the direct superclasses of this object class;
+ the kind of object class is indicated by one of ABSTRACT,
+ STRUCTURAL, or AUXILIARY (the default is STRUCTURAL);
+ MUST and MAY specify the sets of required and allowed attribute
+ types, respectively; and
+ <extensions> describe extensions.
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 24]
+
+RFC 4512 LDAP Models June 2006
+
+
+4.1.2. Attribute Types
+
+ Attribute Type definitions are written according to the ABNF:
+
+ AttributeTypeDescription = LPAREN WSP
+ numericoid ; object identifier
+ [ SP "NAME" SP qdescrs ] ; short names (descriptors)
+ [ SP "DESC" SP qdstring ] ; description
+ [ SP "OBSOLETE" ] ; not active
+ [ SP "SUP" SP oid ] ; supertype
+ [ SP "EQUALITY" SP oid ] ; equality matching rule
+ [ SP "ORDERING" SP oid ] ; ordering matching rule
+ [ SP "SUBSTR" SP oid ] ; substrings matching rule
+ [ SP "SYNTAX" SP noidlen ] ; value syntax
+ [ SP "SINGLE-VALUE" ] ; single-value
+ [ SP "COLLECTIVE" ] ; collective
+ [ SP "NO-USER-MODIFICATION" ] ; not user modifiable
+ [ SP "USAGE" SP usage ] ; usage
+ extensions WSP RPAREN ; extensions
+
+ usage = "userApplications" / ; user
+ "directoryOperation" / ; directory operational
+ "distributedOperation" / ; DSA-shared operational
+ "dSAOperation" ; DSA-specific operational
+
+ where:
+ <numericoid> is object identifier assigned to this attribute type;
+ NAME <qdescrs> are short names (descriptors) identifying this
+ attribute type;
+ DESC <qdstring> is a short descriptive string;
+ OBSOLETE indicates this attribute type is not active;
+ SUP oid specifies the direct supertype of this type;
+ EQUALITY, ORDERING, and SUBSTR provide the oid of the equality,
+ ordering, and substrings matching rules, respectively;
+ SYNTAX identifies value syntax by object identifier and may suggest
+ a minimum upper bound;
+ SINGLE-VALUE indicates attributes of this type are restricted to a
+ single value;
+ COLLECTIVE indicates this attribute type is collective
+ [X.501][RFC3671];
+ NO-USER-MODIFICATION indicates this attribute type is not user
+ modifiable;
+ USAGE indicates the application of this attribute type; and
+ <extensions> describe extensions.
+
+ Each attribute type description must contain at least one of the SUP
+ or SYNTAX fields. If no SYNTAX field is provided, the attribute type
+ description takes its value from the supertype.
+
+
+
+Zeilenga Standards Track [Page 25]
+
+RFC 4512 LDAP Models June 2006
+
+
+ If SUP field is provided, the EQUALITY, ORDERING, and SUBSTRING
+ fields, if not specified, take their value from the supertype.
+
+ Usage of userApplications, the default, indicates that attributes of
+ this type represent user information. That is, they are user
+ attributes.
+
+ A usage of directoryOperation, distributedOperation, or dSAOperation
+ indicates that attributes of this type represent operational and/or
+ administrative information. That is, they are operational
+ attributes.
+
+ directoryOperation usage indicates that the attribute of this type is
+ a directory operational attribute. distributedOperation usage
+ indicates that the attribute of this type is a DSA-shared usage
+ operational attribute. dSAOperation usage indicates that the
+ attribute of this type is a DSA-specific operational attribute.
+
+ COLLECTIVE requires usage userApplications. Use of collective
+ attribute types in LDAP is discussed in [RFC3671].
+
+ NO-USER-MODIFICATION requires an operational usage.
+
+ Note that the <AttributeTypeDescription> does not list the matching
+ rules that can be used with that attribute type in an extensibleMatch
+ search filter [RFC4511]. This is done using the 'matchingRuleUse'
+ attribute described in Section 4.1.4.
+
+ This document refines the schema description of X.501 by requiring
+ that the SYNTAX field in an <AttributeTypeDescription> be a string
+ representation of an object identifier for the LDAP string syntax
+ definition, with an optional indication of the suggested minimum
+ bound of a value of this attribute.
+
+ A suggested minimum upper bound on the number of characters in a
+ value with a string-based syntax, or the number of bytes in a value
+ for all other syntaxes, may be indicated by appending this bound
+ count inside of curly braces following the syntax's OBJECT IDENTIFIER
+ in an Attribute Type Description. This bound is not part of the
+ syntax name itself. For instance, "1.3.6.4.1.1466.0{64}" suggests
+ that server implementations should allow a string to be 64 characters
+ long, although they may allow longer strings. Note that a single
+ character of the Directory String syntax may be encoded in more than
+ one octet since UTF-8 [RFC3629] is a variable-length encoding.
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 26]
+
+RFC 4512 LDAP Models June 2006
+
+
+4.1.3. Matching Rules
+
+ Matching rules are used in performance of attribute value assertions,
+ such as in performance of a Compare operation. They are also used in
+ evaluating search filters, determining which individual values are to
+ be added or deleted during performance of a Modify operation, and in
+ comparing distinguished names.
+
+ Each matching rule is identified by an object identifier (OID) and,
+ optionally, one or more short names (descriptors).
+
+ Matching rule definitions are written according to the ABNF:
+
+ MatchingRuleDescription = LPAREN WSP
+ numericoid ; object identifier
+ [ SP "NAME" SP qdescrs ] ; short names (descriptors)
+ [ SP "DESC" SP qdstring ] ; description
+ [ SP "OBSOLETE" ] ; not active
+ SP "SYNTAX" SP numericoid ; assertion syntax
+ extensions WSP RPAREN ; extensions
+
+ where:
+ <numericoid> is object identifier assigned to this matching rule;
+ NAME <qdescrs> are short names (descriptors) identifying this
+ matching rule;
+ DESC <qdstring> is a short descriptive string;
+ OBSOLETE indicates this matching rule is not active;
+ SYNTAX identifies the assertion syntax (the syntax of the assertion
+ value) by object identifier; and
+ <extensions> describe extensions.
+
+4.1.4. Matching Rule Uses
+
+ A matching rule use lists the attribute types that are suitable for
+ use with an extensibleMatch search filter.
+
+ Matching rule use descriptions are written according to the following
+ ABNF:
+
+ MatchingRuleUseDescription = LPAREN WSP
+ numericoid ; object identifier
+ [ SP "NAME" SP qdescrs ] ; short names (descriptors)
+ [ SP "DESC" SP qdstring ] ; description
+ [ SP "OBSOLETE" ] ; not active
+ SP "APPLIES" SP oids ; attribute types
+ extensions WSP RPAREN ; extensions
+
+
+
+
+
+Zeilenga Standards Track [Page 27]
+
+RFC 4512 LDAP Models June 2006
+
+
+ where:
+ <numericoid> is the object identifier of the matching rule
+ associated with this matching rule use description;
+ NAME <qdescrs> are short names (descriptors) identifying this
+ matching rule use;
+ DESC <qdstring> is a short descriptive string;
+ OBSOLETE indicates this matching rule use is not active;
+ APPLIES provides a list of attribute types the matching rule
+ applies to; and
+ <extensions> describe extensions.
+
+4.1.5. LDAP Syntaxes
+
+ LDAP Syntaxes of (attribute and assertion) values are described in
+ terms of ASN.1 [X.680] and, optionally, have an octet string encoding
+ known as the LDAP-specific encoding. Commonly, the LDAP-specific
+ encoding is constrained to a string of Unicode [Unicode] characters
+ in UTF-8 [RFC3629] form.
+
+ Each LDAP syntax is identified by an object identifier (OID).
+
+ LDAP syntax definitions are written according to the ABNF:
+
+ SyntaxDescription = LPAREN WSP
+ numericoid ; object identifier
+ [ SP "DESC" SP qdstring ] ; description
+ extensions WSP RPAREN ; extensions
+
+ where:
+ <numericoid> is the object identifier assigned to this LDAP syntax;
+ DESC <qdstring> is a short descriptive string; and
+ <extensions> describe extensions.
+
+4.1.6. DIT Content Rules
+
+ A DIT content rule is a "rule governing the content of entries of a
+ particular structural object class" [X.501].
+
+ For DIT entries of a particular structural object class, a DIT
+ content rule specifies which auxiliary object classes the entries are
+ allowed to belong to and which additional attributes (by type) are
+ required, allowed, or not allowed to appear in the entries.
+
+ The list of precluded attributes cannot include any attribute listed
+ as mandatory in the rule, the structural object class, or any of the
+ allowed auxiliary object classes.
+
+
+
+
+
+Zeilenga Standards Track [Page 28]
+
+RFC 4512 LDAP Models June 2006
+
+
+ Each content rule is identified by the object identifier, as well as
+ any short names (descriptors), of the structural object class it
+ applies to.
+
+ An entry may only belong to auxiliary object classes listed in the
+ governing content rule.
+
+ An entry must contain all attributes required by the object classes
+ the entry belongs to as well as all attributes required by the
+ governing content rule.
+
+ An entry may contain any non-precluded attributes allowed by the
+ object classes the entry belongs to as well as all attributes allowed
+ by the governing content rule.
+
+ An entry cannot include any attribute precluded by the governing
+ content rule.
+
+ An entry is governed by (if present and active in the subschema) the
+ DIT content rule that applies to the structural object class of the
+ entry (see Section 2.4.2). If no active rule is present for the
+ entry's structural object class, the entry's content is governed by
+ the structural object class (and possibly other aspects of user and
+ system schema). DIT content rules for superclasses of the structural
+ object class of an entry are not applicable to that entry.
+
+ DIT content rule descriptions are written according to the ABNF:
+
+ DITContentRuleDescription = LPAREN WSP
+ numericoid ; object identifier
+ [ SP "NAME" SP qdescrs ] ; short names (descriptors)
+ [ SP "DESC" SP qdstring ] ; description
+ [ SP "OBSOLETE" ] ; not active
+ [ SP "AUX" SP oids ] ; auxiliary object classes
+ [ SP "MUST" SP oids ] ; attribute types
+ [ SP "MAY" SP oids ] ; attribute types
+ [ SP "NOT" SP oids ] ; attribute types
+ extensions WSP RPAREN ; extensions
+
+ where:
+ <numericoid> is the object identifier of the structural object
+ class associated with this DIT content rule;
+ NAME <qdescrs> are short names (descriptors) identifying this DIT
+ content rule;
+ DESC <qdstring> is a short descriptive string;
+ OBSOLETE indicates this DIT content rule use is not active;
+ AUX specifies a list of auxiliary object classes that entries
+ subject to this DIT content rule may belong to;
+
+
+
+Zeilenga Standards Track [Page 29]
+
+RFC 4512 LDAP Models June 2006
+
+
+ MUST, MAY, and NOT specify lists of attribute types that are
+ required, allowed, or precluded, respectively, from appearing
+ in entries subject to this DIT content rule; and
+ <extensions> describe extensions.
+
+4.1.7. DIT Structure Rules and Name Forms
+
+ It is sometimes desirable to regulate where object and alias entries
+ can be placed in the DIT and how they can be named based upon their
+ structural object class.
+
+4.1.7.1. DIT Structure Rules
+
+ A DIT structure rule is a "rule governing the structure of the DIT by
+ specifying a permitted superior to subordinate entry relationship. A
+ structure rule relates a name form, and therefore a structural object
+ class, to superior structure rules. This permits entries of the
+ structural object class identified by the name form to exist in the
+ DIT as subordinates to entries governed by the indicated superior
+ structure rules" [X.501].
+
+ DIT structure rule descriptions are written according to the ABNF:
+
+ DITStructureRuleDescription = LPAREN WSP
+ ruleid ; rule identifier
+ [ SP "NAME" SP qdescrs ] ; short names (descriptors)
+ [ SP "DESC" SP qdstring ] ; description
+ [ SP "OBSOLETE" ] ; not active
+ SP "FORM" SP oid ; NameForm
+ [ SP "SUP" ruleids ] ; superior rules
+ extensions WSP RPAREN ; extensions
+
+ ruleids = ruleid / ( LPAREN WSP ruleidlist WSP RPAREN )
+ ruleidlist = ruleid *( SP ruleid )
+ ruleid = number
+
+ where:
+ <ruleid> is the rule identifier of this DIT structure rule;
+ NAME <qdescrs> are short names (descriptors) identifying this DIT
+ structure rule;
+ DESC <qdstring> is a short descriptive string;
+ OBSOLETE indicates this DIT structure rule use is not active;
+ FORM is specifies the name form associated with this DIT structure
+ rule;
+ SUP identifies superior rules (by rule id); and
+ <extensions> describe extensions.
+
+
+
+
+
+Zeilenga Standards Track [Page 30]
+
+RFC 4512 LDAP Models June 2006
+
+
+ If no superior rules are identified, the DIT structure rule applies
+ to an autonomous administrative point (e.g., the root vertex of the
+ subtree controlled by the subschema) [X.501].
+
+4.1.7.2. Name Forms
+
+ A name form "specifies a permissible RDN for entries of a particular
+ structural object class. A name form identifies a named object class
+ and one or more attribute types to be used for naming (i.e., for the
+ RDN). Name forms are primitive pieces of specification used in the
+ definition of DIT structure rules" [X.501].
+
+ Each name form indicates the structural object class to be named, a
+ set of required attribute types, and a set of allowed attribute
+ types. A particular attribute type cannot be in both sets.
+
+ Entries governed by the form must be named using a value from each
+ required attribute type and zero or more values from the allowed
+ attribute types.
+
+ Each name form is identified by an object identifier (OID) and,
+ optionally, one or more short names (descriptors).
+
+ Name form descriptions are written according to the ABNF:
+
+ NameFormDescription = LPAREN WSP
+ numericoid ; object identifier
+ [ SP "NAME" SP qdescrs ] ; short names (descriptors)
+ [ SP "DESC" SP qdstring ] ; description
+ [ SP "OBSOLETE" ] ; not active
+ SP "OC" SP oid ; structural object class
+ SP "MUST" SP oids ; attribute types
+ [ SP "MAY" SP oids ] ; attribute types
+ extensions WSP RPAREN ; extensions
+
+ where:
+ <numericoid> is object identifier that identifies this name form;
+ NAME <qdescrs> are short names (descriptors) identifying this name
+ form;
+ DESC <qdstring> is a short descriptive string;
+ OBSOLETE indicates this name form is not active;
+ OC identifies the structural object class this rule applies to,
+ MUST and MAY specify the sets of required and allowed,
+ respectively, naming attributes for this name form; and
+ <extensions> describe extensions.
+
+ All attribute types in the required ("MUST") and allowed ("MAY")
+ lists shall be different.
+
+
+
+Zeilenga Standards Track [Page 31]
+
+RFC 4512 LDAP Models June 2006
+
+
+4.2. Subschema Subentries
+
+ Subschema (sub)entries are used for administering information about
+ the directory schema. A single subschema (sub)entry contains all
+ schema definitions (see Section 4.1) used by entries in a particular
+ part of the directory tree.
+
+ Servers that follow X.500(93) models SHOULD implement subschema using
+ the X.500 subschema mechanisms (as detailed in Section 12 of
+ [X.501]), so these are not ordinary object entries but subentries
+ (see Section 3.2). LDAP clients SHOULD NOT assume that servers
+ implement any of the other aspects of X.500 subschema.
+
+ Servers MAY allow subschema modification. Procedures for subschema
+ modification are discussed in Section 14.5 of [X.501].
+
+ A server that masters entries and permits clients to modify these
+ entries SHALL implement and provide access to these subschema
+ (sub)entries including providing a 'subschemaSubentry' attribute in
+ each modifiable entry. This is so clients may discover the
+ attributes and object classes that are permitted to be present. It
+ is strongly RECOMMENDED that all other servers implement this as
+ well.
+
+ The value of the 'subschemaSubentry' attribute is the name of the
+ subschema (sub)entry holding the subschema controlling the entry.
+
+ ( 2.5.18.10 NAME 'subschemaSubentry'
+ EQUALITY distinguishedNameMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
+ SINGLE-VALUE NO-USER-MODIFICATION
+ USAGE directoryOperation )
+
+ The 'distinguishedNameMatch' matching rule and the DistinguishedName
+ (1.3.6.1.4.1.1466.115.121.1.12) syntax are defined in [RFC4517].
+
+ Subschema is held in (sub)entries belonging to the subschema
+ auxiliary object class.
+
+ ( 2.5.20.1 NAME 'subschema' AUXILIARY
+ MAY ( dITStructureRules $ nameForms $ ditContentRules $
+ objectClasses $ attributeTypes $ matchingRules $
+ matchingRuleUse ) )
+
+ The 'ldapSyntaxes' operational attribute may also be present in
+ subschema entries.
+
+
+
+
+
+Zeilenga Standards Track [Page 32]
+
+RFC 4512 LDAP Models June 2006
+
+
+ Servers MAY provide additional attributes (described in other
+ documents) in subschema (sub)entries.
+
+ Servers SHOULD provide the attributes 'createTimestamp' and
+ 'modifyTimestamp' in subschema (sub)entries, in order to allow
+ clients to maintain their caches of schema information.
+
+ The following subsections provide attribute type definitions for each
+ of schema definition attribute types.
+
+4.2.1. 'objectClasses'
+
+ This attribute holds definitions of object classes.
+
+ ( 2.5.21.6 NAME 'objectClasses'
+ EQUALITY objectIdentifierFirstComponentMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.37
+ USAGE directoryOperation )
+
+ The 'objectIdentifierFirstComponentMatch' matching rule and the
+ ObjectClassDescription (1.3.6.1.4.1.1466.115.121.1.37) syntax are
+ defined in [RFC4517].
+
+4.2.2. 'attributeTypes'
+
+ This attribute holds definitions of attribute types.
+
+ ( 2.5.21.5 NAME 'attributeTypes'
+ EQUALITY objectIdentifierFirstComponentMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.3
+ USAGE directoryOperation )
+
+ The 'objectIdentifierFirstComponentMatch' matching rule and the
+ AttributeTypeDescription (1.3.6.1.4.1.1466.115.121.1.3) syntax are
+ defined in [RFC4517].
+
+4.2.3. 'matchingRules'
+
+ This attribute holds definitions of matching rules.
+
+ ( 2.5.21.4 NAME 'matchingRules'
+ EQUALITY objectIdentifierFirstComponentMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.30
+ USAGE directoryOperation )
+
+ The 'objectIdentifierFirstComponentMatch' matching rule and the
+ MatchingRuleDescription (1.3.6.1.4.1.1466.115.121.1.30) syntax are
+ defined in [RFC4517].
+
+
+
+Zeilenga Standards Track [Page 33]
+
+RFC 4512 LDAP Models June 2006
+
+
+4.2.4 'matchingRuleUse'
+
+ This attribute holds definitions of matching rule uses.
+
+ ( 2.5.21.8 NAME 'matchingRuleUse'
+ EQUALITY objectIdentifierFirstComponentMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.31
+ USAGE directoryOperation )
+
+ The 'objectIdentifierFirstComponentMatch' matching rule and the
+ MatchingRuleUseDescription (1.3.6.1.4.1.1466.115.121.1.31) syntax are
+ defined in [RFC4517].
+
+4.2.5. 'ldapSyntaxes'
+
+ This attribute holds definitions of LDAP syntaxes.
+
+ ( 1.3.6.1.4.1.1466.101.120.16 NAME 'ldapSyntaxes'
+ EQUALITY objectIdentifierFirstComponentMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.54
+ USAGE directoryOperation )
+
+ The 'objectIdentifierFirstComponentMatch' matching rule and the
+ SyntaxDescription (1.3.6.1.4.1.1466.115.121.1.54) syntax are defined
+ in [RFC4517].
+
+4.2.6. 'dITContentRules'
+
+ This attribute lists DIT Content Rules that are present in the
+ subschema.
+
+ ( 2.5.21.2 NAME 'dITContentRules'
+ EQUALITY objectIdentifierFirstComponentMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.16
+ USAGE directoryOperation )
+
+ The 'objectIdentifierFirstComponentMatch' matching rule and the
+ DITContentRuleDescription (1.3.6.1.4.1.1466.115.121.1.16) syntax are
+ defined in [RFC4517].
+
+
+
+
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 34]
+
+RFC 4512 LDAP Models June 2006
+
+
+4.2.7. 'dITStructureRules'
+
+ This attribute lists DIT Structure Rules that are present in the
+ subschema.
+
+ ( 2.5.21.1 NAME 'dITStructureRules'
+ EQUALITY integerFirstComponentMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.17
+ USAGE directoryOperation )
+
+ The 'integerFirstComponentMatch' matching rule and the
+ DITStructureRuleDescription (1.3.6.1.4.1.1466.115.121.1.17) syntax
+ are defined in [RFC4517].
+
+4.2.8 'nameForms'
+
+ This attribute lists Name Forms that are in force.
+
+ ( 2.5.21.7 NAME 'nameForms'
+ EQUALITY objectIdentifierFirstComponentMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.35
+ USAGE directoryOperation )
+
+ The 'objectIdentifierFirstComponentMatch' matching rule and the
+ NameFormDescription (1.3.6.1.4.1.1466.115.121.1.35) syntax are
+ defined in [RFC4517].
+
+4.3. 'extensibleObject' object class
+
+ The 'extensibleObject' auxiliary object class allows entries that
+ belong to it to hold any user attribute. The set of allowed
+ attribute types of this object class is implicitly the set of all
+ attribute types of userApplications usage.
+
+ ( 1.3.6.1.4.1.1466.101.120.111 NAME 'extensibleObject'
+ SUP top AUXILIARY )
+
+ The mandatory attributes of the other object classes of this entry
+ are still required to be present, and any precluded attributes are
+ still not allowed to be present.
+
+4.4. Subschema Discovery
+
+ To discover the DN of the subschema (sub)entry holding the subschema
+ controlling a particular entry, a client reads that entry's
+ 'subschemaSubentry' operational attribute. To read schema attributes
+ from the subschema (sub)entry, clients MUST issue a Search operation
+ [RFC4511] where baseObject is the DN of the subschema (sub)entry,
+
+
+
+Zeilenga Standards Track [Page 35]
+
+RFC 4512 LDAP Models June 2006
+
+
+ scope is baseObject, filter is "(objectClass=subschema)" [RFC4515],
+ and the attributes field lists the names of the desired schema
+ attributes (as they are operational). Note: the
+ "(objectClass=subschema)" filter allows LDAP servers that gateway to
+ X.500 to detect that subentry information is being requested.
+
+ Clients SHOULD NOT assume that a published subschema is complete,
+ that the server supports all of the schema elements it publishes, or
+ that the server does not support an unpublished element.
+
+5. DSA (Server) Informational Model
+
+ The LDAP protocol assumes there are one or more servers that jointly
+ provide access to a Directory Information Tree (DIT). The server
+ holding the original information is called the "master" (for that
+ information). Servers that hold copies of the original information
+ are referred to as "shadowing" or "caching" servers.
+
+
+ As defined in [X.501]:
+
+ context prefix: The sequence of RDNs leading from the Root of the
+ DIT to the initial vertex of a naming context; corresponds to
+ the distinguished name of that vertex.
+
+ naming context: A subtree of entries held in a single master DSA.
+
+ That is, a naming context is the largest collection of entries,
+ starting at an entry that is mastered by a particular server, and
+ including all its subordinates and their subordinates, down to the
+ entries that are mastered by different servers. The context prefix
+ is the name of the initial entry.
+
+ The root of the DIT is a DSA-specific Entry (DSE) and not part of any
+ naming context (or any subtree); each server has different attribute
+ values in the root DSE.
+
+5.1. Server-Specific Data Requirements
+
+ An LDAP server SHALL provide information about itself and other
+ information that is specific to each server. This is represented as
+ a group of attributes located in the root DSE, which is named with
+ the DN with zero RDNs (whose [RFC4514] representation is as the
+ zero-length string).
+
+ These attributes are retrievable, subject to access control and other
+ restrictions, if a client performs a Search operation [RFC4511] with
+ an empty baseObject, scope of baseObject, the filter
+
+
+
+Zeilenga Standards Track [Page 36]
+
+RFC 4512 LDAP Models June 2006
+
+
+ "(objectClass=*)" [RFC4515], and the attributes field listing the
+ names of the desired attributes. It is noted that root DSE
+ attributes are operational and, like other operational attributes,
+ are not returned in search requests unless requested by name.
+
+ The root DSE SHALL NOT be included if the client performs a subtree
+ search starting from the root.
+
+ Servers may allow clients to modify attributes of the root DSE, where
+ appropriate.
+
+ The following attributes of the root DSE are defined below.
+ Additional attributes may be defined in other documents.
+
+ - altServer: alternative servers;
+
+ - namingContexts: naming contexts;
+
+ - supportedControl: recognized LDAP controls;
+
+ - supportedExtension: recognized LDAP extended operations;
+
+ - supportedFeatures: recognized LDAP features;
+
+ - supportedLDAPVersion: LDAP versions supported; and
+
+ - supportedSASLMechanisms: recognized Simple Authentication and
+ Security Layers (SASL) [RFC4422] mechanisms.
+
+ The values provided for these attributes may depend on session-
+ specific and other factors. For example, a server supporting the
+ SASL EXTERNAL mechanism might only list "EXTERNAL" when the client's
+ identity has been established by a lower level. See [RFC4513].
+
+ The root DSE may also include a 'subschemaSubentry' attribute. If it
+ does, the attribute refers to the subschema (sub)entry holding the
+ schema controlling the root DSE. Clients SHOULD NOT assume that this
+ subschema (sub)entry controls other entries held by the server.
+ General subschema discovery procedures are provided in Section 4.4.
+
+
+
+
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 37]
+
+RFC 4512 LDAP Models June 2006
+
+
+5.1.1. 'altServer'
+
+ The 'altServer' attribute lists URIs referring to alternative servers
+ that may be contacted when this server becomes unavailable. URIs for
+ servers implementing the LDAP are written according to [RFC4516].
+ Other kinds of URIs may be provided. If the server does not know of
+ any other servers that could be used, this attribute will be absent.
+ Clients may cache this information in case their preferred server
+ later becomes unavailable.
+
+ ( 1.3.6.1.4.1.1466.101.120.6 NAME 'altServer'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ USAGE dSAOperation )
+
+ The IA5String (1.3.6.1.4.1.1466.115.121.1.26) syntax is defined in
+ [RFC4517].
+
+5.1.2. 'namingContexts'
+
+ The 'namingContexts' attribute lists the context prefixes of the
+ naming contexts the server masters or shadows (in part or in whole).
+ If the server is a first-level DSA [X.501], it should list (in
+ addition) an empty string (indicating the root of the DIT). If the
+ server does not master or shadow any information (e.g., it is an LDAP
+ gateway to a public X.500 directory) this attribute will be absent.
+ If the server believes it masters or shadows the entire directory,
+ the attribute will have a single value, and that value will be the
+ empty string (indicating the root of the DIT).
+
+ This attribute may be used, for example, to select a suitable entry
+ name for subsequent operations with this server.
+
+ ( 1.3.6.1.4.1.1466.101.120.5 NAME 'namingContexts'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
+ USAGE dSAOperation )
+
+ The DistinguishedName (1.3.6.1.4.1.1466.115.121.1.12) syntax is
+ defined in [RFC4517].
+
+5.1.3. 'supportedControl'
+
+ The 'supportedControl' attribute lists object identifiers identifying
+ the request controls [RFC4511] the server supports. If the server
+ does not support any request controls, this attribute will be absent.
+ Object identifiers identifying response controls need not be listed.
+
+ Procedures for registering object identifiers used to discovery of
+ protocol mechanisms are detailed in BCP 64, RFC 4520 [RFC4520].
+
+
+
+Zeilenga Standards Track [Page 38]
+
+RFC 4512 LDAP Models June 2006
+
+
+ ( 1.3.6.1.4.1.1466.101.120.13 NAME 'supportedControl'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.38
+ USAGE dSAOperation )
+
+ The OBJECT IDENTIFIER (1.3.6.1.4.1.1466.115.121.1.38) syntax is
+ defined in [RFC4517].
+
+5.1.4. 'supportedExtension'
+
+ The 'supportedExtension' attribute lists object identifiers
+ identifying the extended operations [RFC4511] that the server
+ supports. If the server does not support any extended operations,
+ this attribute will be absent.
+
+ An extended operation generally consists of an extended request and
+ an extended response but may also include other protocol data units
+ (such as intermediate responses). The object identifier assigned to
+ the extended request is used to identify the extended operation.
+ Other object identifiers used in the extended operation need not be
+ listed as values of this attribute.
+
+ Procedures for registering object identifiers used to discovery of
+ protocol mechanisms are detailed in BCP 64, RFC 4520 [RFC4520].
+
+ ( 1.3.6.1.4.1.1466.101.120.7 NAME 'supportedExtension'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.38
+ USAGE dSAOperation )
+
+ The OBJECT IDENTIFIER (1.3.6.1.4.1.1466.115.121.1.38) syntax is
+ defined in [RFC4517].
+
+5.1.5. 'supportedFeatures'
+
+ The 'supportedFeatures' attribute lists object identifiers
+ identifying elective features that the server supports. If the
+ server does not support any discoverable elective features, this
+ attribute will be absent.
+
+ ( 1.3.6.1.4.1.4203.1.3.5 NAME 'supportedFeatures'
+ EQUALITY objectIdentifierMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.38
+ USAGE dSAOperation )
+
+ Procedures for registering object identifiers used to discovery of
+ protocol mechanisms are detailed in BCP 64, RFC 4520 [RFC4520].
+
+ The OBJECT IDENTIFIER (1.3.6.1.4.1.1466.115.121.1.38) syntax and
+ objectIdentifierMatch matching rule are defined in [RFC4517].
+
+
+
+Zeilenga Standards Track [Page 39]
+
+RFC 4512 LDAP Models June 2006
+
+
+5.1.6. 'supportedLDAPVersion'
+
+ The 'supportedLDAPVersion' attribute lists the versions of LDAP that
+ the server supports.
+
+ ( 1.3.6.1.4.1.1466.101.120.15 NAME 'supportedLDAPVersion'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
+ USAGE dSAOperation )
+
+ The INTEGER (1.3.6.1.4.1.1466.115.121.1.27) syntax is defined in
+ [RFC4517].
+
+5.1.7. 'supportedSASLMechanisms'
+
+ The 'supportedSASLMechanisms' attribute lists the SASL mechanisms
+ [RFC4422] that the server recognizes and/or supports [RFC4513]. The
+ contents of this attribute may depend on the current session state.
+ If the server does not support any SASL mechanisms, this attribute
+ will not be present.
+
+ ( 1.3.6.1.4.1.1466.101.120.14 NAME 'supportedSASLMechanisms'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+ USAGE dSAOperation )
+
+ The Directory String (1.3.6.1.4.1.1466.115.121.1.15) syntax is
+ defined in [RFC4517].
+
+6. Other Considerations
+
+6.1. Preservation of User Information
+
+ Syntaxes may be defined that have specific value and/or value form
+ (representation) preservation requirements. For example, a syntax
+ containing digitally signed data can mandate that the server preserve
+ both the value and form of value presented to ensure that the
+ signature is not invalidated.
+
+ Where such requirements have not been explicitly stated, servers
+ SHOULD preserve the value of user information but MAY return the
+ value in a different form. And where a server is unable (or
+ unwilling) to preserve the value of user information, the server
+ SHALL ensure that an equivalent value (per Section 2.3) is returned.
+
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 40]
+
+RFC 4512 LDAP Models June 2006
+
+
+6.2. Short Names
+
+ Short names, also known as descriptors, are used as more readable
+ aliases for object identifiers and are used to identify various
+ schema elements. However, it is not expected that LDAP
+ implementations with human user interface would display these short
+ names (or the object identifiers they refer to) to the user.
+ Instead, they would most likely be performing translations (such as
+ expressing the short name in one of the local national languages).
+ For example, the short name "st" (stateOrProvinceName) might be
+ displayed to a German-speaking user as "Land".
+
+ The same short name might have different meaning in different
+ subschemas, and, within a particular subschema, the same short name
+ might refer to different object identifiers each identifying a
+ different kind of schema element.
+
+ Implementations MUST be prepared that the same short name might be
+ used in a subschema to refer to the different kinds of schema
+ elements. That is, there might be an object class 'x-fubar' and an
+ attribute type 'x-fubar' in a subschema.
+
+ Implementations MUST be prepared that the same short name might be
+ used in the different subschemas to refer to the different schema
+ elements. That is, there might be two matching rules 'x-fubar', each
+ in different subschemas.
+
+ Procedures for registering short names (descriptors) are detailed in
+ BCP 64, RFC 4520 [RFC4520].
+
+6.3. Cache and Shadowing
+
+ Some servers may hold cache or shadow copies of entries, which can be
+ used to answer search and comparison queries, but will return
+ referrals or contact other servers if modification operations are
+ requested. Servers that perform shadowing or caching MUST ensure
+ that they do not violate any access control constraints placed on the
+ data by the originating server.
+
+
+
+
+
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 41]
+
+RFC 4512 LDAP Models June 2006
+
+
+7. Implementation Guidelines
+
+7.1. Server Guidelines
+
+ Servers MUST recognize all names of attribute types and object
+ classes defined in this document but, unless stated otherwise, need
+ not support the associated functionality. Servers SHOULD recognize
+ all the names of attribute types and object classes defined in
+ Section 3 and 4, respectively, of [RFC4519].
+
+ Servers MUST ensure that entries conform to user and system schema
+ rules or other data model constraints.
+
+ Servers MAY support DIT Content Rules. Servers MAY support DIT
+ Structure Rules and Name Forms.
+
+ Servers MAY support alias entries.
+
+ Servers MAY support the 'extensibleObject' object class.
+
+ Servers MAY support subentries. If so, they MUST do so in accordance
+ with [RFC3672]. Servers that do not support subentries SHOULD use
+ object entries to mimic subentries as detailed in Section 3.2.
+
+ Servers MAY implement additional schema elements. Servers SHOULD
+ provide definitions of all schema elements they support in subschema
+ (sub)entries.
+
+7.2. Client Guidelines
+
+ In the absence of prior agreements with servers, clients SHOULD NOT
+ assume that servers support any particular schema elements beyond
+ those referenced in Section 7.1. The client can retrieve subschema
+ information as described in Section 4.4.
+
+ Clients MUST NOT display or attempt to decode a value as ASN.1 if the
+ value's syntax is not known. Clients MUST NOT assume the LDAP-
+ specific string encoding is restricted to a UTF-8 encoded string of
+ Unicode characters or any particular subset of Unicode (such as a
+ printable subset) unless such restriction is explicitly stated.
+ Clients SHOULD NOT send attribute values in a request that are not
+ valid according to the syntax defined for the attributes.
+
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 42]
+
+RFC 4512 LDAP Models June 2006
+
+
+8. Security Considerations
+
+ Attributes of directory entries are used to provide descriptive
+ information about the real-world objects they represent, which can be
+ people, organizations, or devices. Most countries have privacy laws
+ regarding the publication of information about people.
+
+ General security considerations for accessing directory information
+ with LDAP are discussed in [RFC4511] and [RFC4513].
+
+9. IANA Considerations
+
+ The Internet Assigned Numbers Authority (IANA) has updated the LDAP
+ descriptors registry as indicated in the following template:
+
+ Subject: Request for LDAP Descriptor Registration Update
+ Descriptor (short name): see comment
+ Object Identifier: see comment
+ Person & email address to contact for further information:
+ Kurt Zeilenga <kurt@OpenLDAP.org>
+ Usage: see comment
+ Specification: RFC 4512
+ Author/Change Controller: IESG
+ Comments:
+
+ The following descriptors (short names) has been added to
+ the registry.
+
+ NAME Type OID
+ ------------------------ ---- -----------------
+ governingStructureRule A 2.5.21.10
+ structuralObjectClass A 2.5.21.9
+
+ The following descriptors (short names) have been updated to
+ refer to this RFC.
+
+ NAME Type OID
+ ------------------------ ---- -----------------
+ alias O 2.5.6.1
+ aliasedObjectName A 2.5.4.1
+ altServer A 1.3.6.1.4.1.1466.101.120.6
+ attributeTypes A 2.5.21.5
+ createTimestamp A 2.5.18.1
+ creatorsName A 2.5.18.3
+ dITContentRules A 2.5.21.2
+ dITStructureRules A 2.5.21.1
+ extensibleObject O 1.3.6.1.4.1.1466.101.120.111
+ ldapSyntaxes A 1.3.6.1.4.1.1466.101.120.16
+
+
+
+Zeilenga Standards Track [Page 43]
+
+RFC 4512 LDAP Models June 2006
+
+
+ matchingRuleUse A 2.5.21.8
+ matchingRules A 2.5.21.4
+ modifiersName A 2.5.18.4
+ modifyTimestamp A 2.5.18.2
+ nameForms A 2.5.21.7
+ namingContexts A 1.3.6.1.4.1.1466.101.120.5
+ objectClass A 2.5.4.0
+ objectClasses A 2.5.21.6
+ subschema O 2.5.20.1
+ subschemaSubentry A 2.5.18.10
+ supportedControl A 1.3.6.1.4.1.1466.101.120.13
+ supportedExtension A 1.3.6.1.4.1.1466.101.120.7
+ supportedFeatures A 1.3.6.1.4.1.4203.1.3.5
+ supportedLDAPVersion A 1.3.6.1.4.1.1466.101.120.15
+ supportedSASLMechanisms A 1.3.6.1.4.1.1466.101.120.14
+ top O 2.5.6.0
+
+10. Acknowledgements
+
+ This document is based in part on RFC 2251 by M. Wahl, T. Howes, and
+ S. Kille; RFC 2252 by M. Wahl, A. Coulbeck, T. Howes, S. Kille; and
+ RFC 2556 by M. Wahl, all products of the IETF Access, Searching and
+ Indexing of Directories (ASID) Working Group. This document is also
+ based in part on "The Directory: Models" [X.501], a product of the
+ International Telephone Union (ITU). Additional text was borrowed
+ from RFC 2253 by M. Wahl, T. Howes, and S. Kille.
+
+ This document is a product of the IETF LDAP Revision (LDAPBIS)
+ Working Group.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 44]
+
+RFC 4512 LDAP Models June 2006
+
+
+11. Normative References
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [RFC3629] Yergeau, F., "UTF-8, a transformation format of ISO
+ 10646", STD 63, RFC 3629, November 2003.
+
+ [RFC3671] Zeilenga, K., "Collective Attributes in the Lightweight
+ Directory Access Protocol (LDAP)", RFC 3671, December
+ 2003.
+
+ [RFC3672] Zeilenga, K., "Subentries in the Lightweight Directory
+ Access Protocol (LDAP)", RFC 3672, December 2003.
+
+ [RFC4234] Crocker, D. and P. Overell, "Augmented BNF for Syntax
+ Specifications: ABNF", RFC 4234, October 2005.
+
+ [RFC4422] Melnikov, A., Ed. and K. Zeilenga, Ed., "Simple
+ Authentication and Security Layer (SASL)", RFC 4422,
+ June 2006.
+
+ [RFC4510] Zeilenga, K., Ed., "Lightweight Directory Access
+ Protocol (LDAP): Technical Specification Road Map", RFC
+ 4510, June 2006.
+
+ [RFC4511] Sermersheim, J., Ed., "Lightweight Directory Access
+ Protocol (LDAP): The Protocol", RFC 4511, June 2006.
+
+ [RFC4513] Harrison, R., Ed., "Lightweight Directory Access
+ Protocol (LDAP): Authentication Methods and Security
+ Mechanisms", RFC 4513, June 2006.
+
+ [RFC4514] Zeilenga, K., Ed., "Lightweight Directory Access
+ Protocol (LDAP): String Representation of Distinguished
+ Names", RFC 4514, June 2006.
+
+ [RFC4515] Smith, M., Ed. and T. Howes, "Lightweight Directory
+ Access Protocol (LDAP): String Representation of Search
+ Filters", RFC 4515, June 2006.
+
+ [RFC4516] Smith, M., Ed. and T. Howes, "Lightweight Directory
+ Access Protocol (LDAP): Uniform Resource Locator", RFC
+ 4516, June 2006.
+
+ [RFC4517] Legg, S., Ed., "Lightweight Directory Access Protocol
+ (LDAP): Syntaxes and Matching Rules", RFC 4517, June
+ 2006.
+
+
+
+Zeilenga Standards Track [Page 45]
+
+RFC 4512 LDAP Models June 2006
+
+
+ [RFC4519] Sciberras, A., Ed., "Lightweight Directory Access
+ Protocol (LDAP): Schema for User Applications", RFC
+ 4519, June 2006.
+
+ [RFC4520] Zeilenga, K., "Internet Assigned Numbers Authority
+ (IANA) Considerations for the Lightweight Directory
+ Access Protocol (LDAP)", BCP 64, RFC 4520, June 2006.
+
+ [Unicode] The Unicode Consortium, "The Unicode Standard, Version
+ 3.2.0" is defined by "The Unicode Standard, Version
+ 3.0" (Reading, MA, Addison-Wesley, 2000. ISBN 0-201-
+ 61633-5), as amended by the "Unicode Standard Annex
+ #27: Unicode 3.1"
+ (http://www.unicode.org/reports/tr27/) and by the
+ "Unicode Standard Annex #28: Unicode 3.2"
+ (http://www.unicode.org/reports/tr28/).
+
+ [X.500] International Telecommunication Union -
+ Telecommunication Standardization Sector, "The
+ Directory -- Overview of concepts, models and
+ services," X.500(1993) (also ISO/IEC 9594-1:1994).
+
+ [X.501] International Telecommunication Union -
+ Telecommunication Standardization Sector, "The
+ Directory -- Models," X.501(1993) (also ISO/IEC 9594-
+ 2:1994).
+
+ [X.680] International Telecommunication Union -
+ Telecommunication Standardization Sector, "Abstract
+ Syntax Notation One (ASN.1) - Specification of Basic
+ Notation", X.680(2002) (also ISO/IEC 8824-1:2002).
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 46]
+
+RFC 4512 LDAP Models June 2006
+
+
+Appendix A. Changes
+
+ This appendix is non-normative.
+
+ This document amounts to nearly a complete rewrite of portions of RFC
+ 2251, RFC 2252, and RFC 2256. This rewrite was undertaken to improve
+ overall clarity of technical specification. This appendix provides a
+ summary of substantive changes made to the portions of these
+ documents incorporated into this document. Readers should consult
+ [RFC4510], [RFC4511], [RFC4517], and [RFC4519] for summaries of
+ remaining portions of these documents.
+
+A.1. Changes to RFC 2251
+
+ This document incorporates from RFC 2251, Sections 3.2 and 3.4, and
+ portions of Sections 4 and 6 as summarized below.
+
+A.1.1. Section 3.2 of RFC 2251
+
+ Section 3.2 of RFC 2251 provided a brief introduction to the X.500
+ data model, as used by LDAP. The previous specification relied on
+ [X.501] but lacked clarity in how X.500 models are adapted for use by
+ LDAP. This document describes the X.500 data models, as used by
+ LDAP, in greater detail, especially in areas where adaptation is
+ needed.
+
+ Section 3.2.1 of RFC 2251 described an attribute as "a type with one
+ or more associated values". In LDAP, an attribute is better
+ described as an attribute description, a type with zero or more
+ options, and one or more associated values.
+
+ Section 3.2.2 of RFC 2251 mandated that subschema subentries contain
+ objectClasses and attributeTypes attributes, yet X.500(93) treats
+ these attributes as optional. While generally all implementations
+ that support X.500(93) subschema mechanisms will provide both of
+ these attributes, it is not absolutely required for interoperability
+ that all servers do. The mandate was removed for consistency with
+ X.500(93). The subschema discovery mechanism was also clarified to
+ indicate that subschema controlling an entry is obtained by reading
+ the (sub)entry referred to by that entry's 'subschemaSubentry'
+ attribute.
+
+
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 47]
+
+RFC 4512 LDAP Models June 2006
+
+
+A.1.2. Section 3.4 of RFC 2251
+
+ Section 3.4 of RFC 2251 provided "Server-specific Data Requirements".
+ This material, with changes, was incorporated in Section 5.1 of this
+ document.
+
+ Changes:
+
+ - Clarify that attributes of the root DSE are subject to "other
+ restrictions" in addition to access controls.
+
+ - Clarify that only recognized extended requests need to be
+ enumerated 'supportedExtension'.
+
+ - Clarify that only recognized request controls need to be enumerated
+ 'supportedControl'.
+
+ - Clarify that root DSE attributes are operational and, like other
+ operational attributes, will not be returned in search requests
+ unless requested by name.
+
+ - Clarify that not all root DSE attributes are user modifiable.
+
+ - Remove inconsistent text regarding handling of the
+ 'subschemaSubentry' attribute within the root DSE. The previous
+ specification stated that the 'subschemaSubentry' attribute held in
+ the root DSE referred to "subschema entries (or subentries) known
+ by this server". This is inconsistent with the attribute's
+ intended use as well as its formal definition as a single valued
+ attribute [X.501]. It is also noted that a simple (possibly
+ incomplete) list of subschema (sub)entries is not terribly useful.
+ This document (in Section 5.1) specifies that the
+ 'subschemaSubentry' attribute of the root DSE refers to the
+ subschema controlling the root DSE. It is noted that the general
+ subschema discovery mechanism remains available (see Section 4.4 of
+ this document).
+
+A.1.3. Section 4 of RFC 2251
+
+ Portions of Section 4 of RFC 2251 detailing aspects of the
+ information model used by LDAP were incorporated in this document,
+ including:
+
+ - Restriction of distinguished values to attributes whose
+ descriptions have no options (from Section 4.1.3);
+
+
+
+
+
+
+Zeilenga Standards Track [Page 48]
+
+RFC 4512 LDAP Models June 2006
+
+
+ - Data model aspects of Attribute Types (from Section 4.1.4),
+ Attribute Descriptions (from 4.1.5), Attribute (from 4.1.8),
+ Matching Rule Identifier (from 4.1.9); and
+
+ - User schema requirements (from Sections 4.1.6, 4.5.1, and 4.7).
+
+ Clarifications to these portions include:
+
+ - Subtyping and AttributeDescriptions with options.
+
+A.1.4. Section 6 of RFC 2251
+
+ The Section 6.1 and the second paragraph of Section 6.2 of RFC 2251
+ where incorporated into this document.
+
+A.2. Changes to RFC 2252
+
+ This document incorporates Sections 4, 5, and 7 from RFC 2252.
+
+A.2.1. Section 4 of RFC 2252
+
+ The specification was updated to use Augmented BNF [RFC4234]. The
+ string representation of an OBJECT IDENTIFIER was tightened to
+ disallow leading zeros as described in RFC 2252.
+
+ The <descr> syntax was changed to disallow semicolon (U+003B)
+ characters in order to appear to be consistent its natural language
+ specification "descr is the syntactic representation of an object
+ descriptor, which consists of letters and digits, starting with a
+ letter". In a related change, the statement "an AttributeDescription
+ can be used as the value in a NAME part of an
+ AttributeTypeDescription" was deleted. RFC 2252 provided no
+ specification of the semantics of attribute options appearing in NAME
+ fields.
+
+ RFC 2252 stated that the <descr> form of <oid> SHOULD be preferred
+ over the <numericoid> form. However, <descr> form can be ambiguous.
+ To address this issue, the imperative was replaced with a statement
+ (in Section 1.4) that while the <descr> form is generally preferred,
+ <numericoid> should be used where an unambiguous <descr> is not
+ available. Additionally, an expanded discussion of descriptor issues
+ is in Section 6.2 ("Short Names").
+
+ The ABNF for a quoted string (qdstring) was updated to reflect
+ support for the escaping mechanism described in Section 4.3 of RFC
+ 2252.
+
+
+
+
+
+Zeilenga Standards Track [Page 49]
+
+RFC 4512 LDAP Models June 2006
+
+
+A.2.2. Section 5 of RFC 2252
+
+ Definitions of operational attributes provided in Section 5 of RFC
+ 2252 where incorporated into this document.
+
+ The 'namingContexts' description was clarified. A first-level DSA
+ should publish, in addition to other values, "" indicating the root
+ of the DIT.
+
+ The 'altServer' description was clarified. It may hold any URI.
+
+ The 'supportedExtension' description was clarified. A server need
+ only list the OBJECT IDENTIFIERs associated with the extended
+ requests of the extended operations it recognizes.
+
+ The 'supportedControl' description was clarified. A server need only
+ list the OBJECT IDENTIFIERs associated with the request controls it
+ recognizes.
+
+ Descriptions for the 'structuralObjectClass' and
+ 'governingStructureRule' operational attribute types were added.
+
+ The attribute definition of 'subschemaSubentry' was corrected to list
+ the terms SINGLE-VALUE and NO-USER-MODIFICATION in proper order.
+
+A.2.3. Section 7 of RFC 2252
+
+ Section 7 of RFC 2252 provides definitions of the 'subschema' and
+ 'extensibleObject' object classes. These definitions where
+ integrated into Section 4.2 and Section 4.3 of this document,
+ respectively. Section 7 of RFC 2252 also contained the object class
+ implementation requirement. This was incorporated into Section 7 of
+ this document.
+
+ The specification of 'extensibleObject' was clarified regarding how
+ it interacts with precluded attributes.
+
+A.3. Changes to RFC 2256
+
+ This document incorporates Sections 5.1, 5.2, 7.1, and 7.2 of RFC
+ 2256.
+
+ Section 5.1 of RFC 2256 provided the definition of the 'objectClass'
+ attribute type. This was integrated into Section 2.4.1 of this
+ document. The statement "One of the values is either 'top' or
+ 'alias'" was replaced with statement that one of the values is 'top'
+ as entries belonging to 'alias' also belong to 'top'.
+
+
+
+
+Zeilenga Standards Track [Page 50]
+
+RFC 4512 LDAP Models June 2006
+
+
+ Section 5.2 of RFC 2256 provided the definition of the
+ 'aliasedObjectName' attribute type. This was integrated into Section
+ 2.6.2 of this document.
+
+ Section 7.1 of RFC 2256 provided the definition of the 'top' object
+ class. This was integrated into Section 2.4.1 of this document.
+
+ Section 7.2 of RFC 2256 provided the definition of the 'alias' object
+ class. This was integrated into Section 2.6.1 of this document.
+
+A.4. Changes to RFC 3674
+
+ This document made no substantive change to the 'supportedFeatures'
+ technical specification provided in RFC 3674.
+
+Editor's Address
+
+ Kurt D. Zeilenga
+ OpenLDAP Foundation
+
+ EMail: Kurt@OpenLDAP.org
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 51]
+
+RFC 4512 LDAP Models June 2006
+
+
+Full Copyright Statement
+
+ Copyright (C) The Internet Society (2006).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
+ ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
+ INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
+ INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be
+ found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at
+ ietf-ipr@ietf.org.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is provided by the IETF
+ Administrative Support Activity (IASA).
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 52]
+
diff --git a/source4/ldap_server/devdocs/rfc4513.txt b/source4/ldap_server/devdocs/rfc4513.txt
new file mode 100644
index 0000000000..7e6e7eb4bd
--- /dev/null
+++ b/source4/ldap_server/devdocs/rfc4513.txt
@@ -0,0 +1,1907 @@
+
+
+
+
+
+
+Network Working Group R. Harrison, Ed.
+Request for Comments: 4513 Novell, Inc.
+Obsoletes: 2251, 2829, 2830 June 2006
+Category: Standards Track
+
+
+ Lightweight Directory Access Protocol (LDAP):
+ Authentication Methods and Security Mechanisms
+
+Status of This Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2006).
+
+Abstract
+
+ This document describes authentication methods and security
+ mechanisms of the Lightweight Directory Access Protocol (LDAP). This
+ document details establishment of Transport Layer Security (TLS)
+ using the StartTLS operation.
+
+ This document details the simple Bind authentication method including
+ anonymous, unauthenticated, and name/password mechanisms and the
+ Simple Authentication and Security Layer (SASL) Bind authentication
+ method including the EXTERNAL mechanism.
+
+ This document discusses various authentication and authorization
+ states through which a session to an LDAP server may pass and the
+ actions that trigger these state changes.
+
+ This document, together with other documents in the LDAP Technical
+ Specification (see Section 1 of the specification's road map),
+ obsoletes RFC 2251, RFC 2829, and RFC 2830.
+
+
+
+
+
+
+
+
+
+
+
+Harrison Standards Track [Page 1]
+
+RFC 4513 LDAP Authentication Methods June 2006
+
+
+Table of Contents
+
+ 1. Introduction ....................................................4
+ 1.1. Relationship to Other Documents ............................6
+ 1.2. Conventions ................................................6
+ 2. Implementation Requirements .....................................7
+ 3. StartTLS Operation ..............................................8
+ 3.1. TLS Establishment Procedures ..............................8
+ 3.1.1. StartTLS Request Sequencing .........................8
+ 3.1.2. Client Certificate ..................................9
+ 3.1.3. Server Identity Check ...............................9
+ 3.1.3.1. Comparison of DNS Names ...................10
+ 3.1.3.2. Comparison of IP Addresses ................11
+ 3.1.3.3. Comparison of Other subjectName Types .....11
+ 3.1.4. Discovery of Resultant Security Level ..............11
+ 3.1.5. Refresh of Server Capabilities Information .........11
+ 3.2. Effect of TLS on Authorization State .....................12
+ 3.3. TLS Ciphersuites ..........................................12
+ 4. Authorization State ............................................13
+ 5. Bind Operation .................................................14
+ 5.1. Simple Authentication Method ..............................14
+ 5.1.1. Anonymous Authentication Mechanism of Simple Bind ..14
+ 5.1.2. Unauthenticated Authentication Mechanism of
+ Simple Bind ........................................14
+ 5.1.3. Name/Password Authentication Mechanism of
+ Simple Bind ........................................15
+ 5.2. SASL Authentication Method ................................16
+ 5.2.1. SASL Protocol Profile ..............................16
+ 5.2.1.1. SASL Service Name for LDAP ................16
+ 5.2.1.2. SASL Authentication Initiation and
+ Protocol Exchange .........................16
+ 5.2.1.3. Optional Fields ...........................17
+ 5.2.1.4. Octet Where Negotiated Security
+ Layers Take Effect ........................18
+ 5.2.1.5. Determination of Supported SASL
+ Mechanisms ................................18
+ 5.2.1.6. Rules for Using SASL Layers ...............19
+ 5.2.1.7. Support for Multiple Authentications ......19
+ 5.2.1.8. SASL Authorization Identities .............19
+ 5.2.2. SASL Semantics within LDAP .........................20
+ 5.2.3. SASL EXTERNAL Authentication Mechanism .............20
+ 5.2.3.1. Implicit Assertion ........................21
+ 5.2.3.2. Explicit Assertion ........................21
+ 6. Security Considerations ........................................21
+ 6.1. General LDAP Security Considerations ......................21
+ 6.2. StartTLS Security Considerations ..........................22
+ 6.3. Bind Operation Security Considerations ....................23
+ 6.3.1. Unauthenticated Mechanism Security Considerations ..23
+
+
+
+Harrison Standards Track [Page 2]
+
+RFC 4513 LDAP Authentication Methods June 2006
+
+
+ 6.3.2. Name/Password Mechanism Security Considerations ....23
+ 6.3.3. Password-Related Security Considerations ...........23
+ 6.3.4. Hashed Password Security Considerations ............24
+ 6.4. SASL Security Considerations ..............................24
+ 6.5. Related Security Considerations ...........................25
+ 7. IANA Considerations ............................................25
+ 8. Acknowledgements ...............................................25
+ 9. Normative References ...........................................26
+ 10. Informative References ........................................27
+ Appendix A. Authentication and Authorization Concepts .............28
+ A.1. Access Control Policy .....................................28
+ A.2. Access Control Factors ....................................28
+ A.3. Authentication, Credentials, Identity .....................28
+ A.4. Authorization Identity ....................................29
+ Appendix B. Summary of Changes ....................................29
+ B.1. Changes Made to RFC 2251 ..................................30
+ B.1.1. Section 4.2.1 ("Sequencing of the Bind Request") ...30
+ B.1.2. Section 4.2.2 ("Authentication and Other Security
+ Services") .........................................30
+ B.2. Changes Made to RFC 2829 ..................................30
+ B.2.1. Section 4 ("Required security mechanisms") .........30
+ B.2.2. Section 5.1 ("Anonymous authentication
+ procedure") ........................................31
+ B.2.3. Section 6 ("Password-based authentication") ........31
+ B.2.4. Section 6.1 ("Digest authentication") ..............31
+ B.2.5. Section 6.2 ("'simple' authentication choice under
+ TLS encryption") ...................................31
+ B.2.6. Section 6.3 ("Other authentication choices with
+ TLS") ..............................................31
+ B.2.7. Section 7.1 ("Certificate-based authentication
+ with TLS") .........................................31
+ B.2.8. Section 8 ("Other mechanisms") .....................32
+ B.2.9. Section 9 ("Authorization Identity") ...............32
+ B.2.10. Section 10 ("TLS Ciphersuites") ...................32
+ B.3. Changes Made to RFC 2830 ..................................32
+ B.3.1. Section 3.6 ("Server Identity Check") ..............32
+ B.3.2. Section 3.7 ("Refresh of Server Capabilities
+ Information") ......................................33
+ B.3.3. Section 5 ("Effects of TLS on a Client's
+ Authorization Identity") ...........................33
+ B.3.4. Section 5.2 ("TLS Connection Closure Effects") .....33
+
+
+
+
+
+
+
+
+
+
+Harrison Standards Track [Page 3]
+
+RFC 4513 LDAP Authentication Methods June 2006
+
+
+1. Introduction
+
+ The Lightweight Directory Access Protocol (LDAP) [RFC4510] is a
+ powerful protocol for accessing directories. It offers means of
+ searching, retrieving, and manipulating directory content and ways to
+ access a rich set of security functions.
+
+ It is vital that these security functions be interoperable among all
+ LDAP clients and servers on the Internet; therefore there has to be a
+ minimum subset of security functions that is common to all
+ implementations that claim LDAP conformance.
+
+ Basic threats to an LDAP directory service include (but are not
+ limited to):
+
+ (1) Unauthorized access to directory data via data-retrieval
+ operations.
+
+ (2) Unauthorized access to directory data by monitoring access of
+ others.
+
+ (3) Unauthorized access to reusable client authentication information
+ by monitoring access of others.
+
+ (4) Unauthorized modification of directory data.
+
+ (5) Unauthorized modification of configuration information.
+
+ (6) Denial of Service: Use of resources (commonly in excess) in a
+ manner intended to deny service to others.
+
+ (7) Spoofing: Tricking a user or client into believing that
+ information came from the directory when in fact it did not,
+ either by modifying data in transit or misdirecting the client's
+ transport connection. Tricking a user or client into sending
+ privileged information to a hostile entity that appears to be the
+ directory server but is not. Tricking a directory server into
+ believing that information came from a particular client when in
+ fact it came from a hostile entity.
+
+ (8) Hijacking: An attacker seizes control of an established protocol
+ session.
+
+ Threats (1), (4), (5), (6), (7), and (8) are active attacks. Threats
+ (2) and (3) are passive attacks.
+
+
+
+
+
+
+Harrison Standards Track [Page 4]
+
+RFC 4513 LDAP Authentication Methods June 2006
+
+
+ Threats (1), (4), (5), and (6) are due to hostile clients. Threats
+ (2), (3), (7), and (8) are due to hostile agents on the path between
+ client and server or hostile agents posing as a server, e.g., IP
+ spoofing.
+
+ LDAP offers the following security mechanisms:
+
+ (1) Authentication by means of the Bind operation. The Bind
+ operation provides a simple method that supports anonymous,
+ unauthenticated, and name/password mechanisms, and the Simple
+ Authentication and Security Layer (SASL) method, which supports a
+ wide variety of authentication mechanisms.
+
+ (2) Mechanisms to support vendor-specific access control facilities
+ (LDAP does not offer a standard access control facility).
+
+ (3) Data integrity service by means of security layers in Transport
+ Layer Security (TLS) or SASL mechanisms.
+
+ (4) Data confidentiality service by means of security layers in TLS
+ or SASL mechanisms.
+
+ (5) Server resource usage limitation by means of administrative
+ limits configured on the server.
+
+ (6) Server authentication by means of the TLS protocol or SASL
+ mechanisms.
+
+ LDAP may also be protected by means outside the LDAP protocol, e.g.,
+ with IP layer security [RFC4301].
+
+ Experience has shown that simply allowing implementations to pick and
+ choose the security mechanisms that will be implemented is not a
+ strategy that leads to interoperability. In the absence of mandates,
+ clients will continue to be written that do not support any security
+ function supported by the server, or worse, they will only support
+ mechanisms that provide inadequate security for most circumstances.
+
+ It is desirable to allow clients to authenticate using a variety of
+ mechanisms including mechanisms where identities are represented as
+ distinguished names [X.501][RFC4512], in string form [RFC4514], or as
+ used in different systems (e.g., simple user names [RFC4013]).
+ Because some authentication mechanisms transmit credentials in plain
+ text form, and/or do not provide data security services and/or are
+ subject to passive attacks, it is necessary to ensure secure
+ interoperability by identifying a mandatory-to-implement mechanism
+ for establishing transport-layer security services.
+
+
+
+
+Harrison Standards Track [Page 5]
+
+RFC 4513 LDAP Authentication Methods June 2006
+
+
+ The set of security mechanisms provided in LDAP and described in this
+ document is intended to meet the security needs for a wide range of
+ deployment scenarios and still provide a high degree of
+ interoperability among various LDAP implementations and deployments.
+
+1.1. Relationship to Other Documents
+
+ This document is an integral part of the LDAP Technical Specification
+ [RFC4510].
+
+ This document, together with [RFC4510], [RFC4511], and [RFC4512],
+ obsoletes RFC 2251 in its entirety. Sections 4.2.1 (portions) and
+ 4.2.2 of RFC 2251 are obsoleted by this document. Appendix B.1
+ summarizes the substantive changes made to RFC 2251 by this document.
+
+ This document obsoletes RFC 2829 in its entirety. Appendix B.2
+ summarizes the substantive changes made to RFC 2829 by this document.
+
+ Sections 2 and 4 of RFC 2830 are obsoleted by [RFC4511]. The
+ remainder of RFC 2830 is obsoleted by this document. Appendix B.3
+ summarizes the substantive changes made to RFC 2830 by this document.
+
+1.2. Conventions
+
+ The key words "MUST", "MUST NOT", "SHALL", "SHOULD", "SHOULD NOT",
+ "MAY", and "OPTIONAL" in this document are to be interpreted as
+ described in RFC 2119 [RFC2119].
+
+ The term "user" represents any human or application entity that is
+ accessing the directory using a directory client. A directory client
+ (or client) is also known as a directory user agent (DUA).
+
+ The term "transport connection" refers to the underlying transport
+ services used to carry the protocol exchange, as well as associations
+ established by these services.
+
+ The term "TLS layer" refers to TLS services used in providing
+ security services, as well as associations established by these
+ services.
+
+ The term "SASL layer" refers to SASL services used in providing
+ security services, as well as associations established by these
+ services.
+
+ The term "LDAP message layer" refers to the LDAP Message (PDU)
+ services used in providing directory services, as well as
+ associations established by these services.
+
+
+
+
+Harrison Standards Track [Page 6]
+
+RFC 4513 LDAP Authentication Methods June 2006
+
+
+ The term "LDAP session" refers to combined services (transport
+ connection, TLS layer, SASL layer, LDAP message layer) and their
+ associations.
+
+ In general, security terms in this document are used consistently
+ with the definitions provided in [RFC2828]. In addition, several
+ terms and concepts relating to security, authentication, and
+ authorization are presented in Appendix A of this document. While
+ the formal definition of these terms and concepts is outside the
+ scope of this document, an understanding of them is prerequisite to
+ understanding much of the material in this document. Readers who are
+ unfamiliar with security-related concepts are encouraged to review
+ Appendix A before reading the remainder of this document.
+
+2. Implementation Requirements
+
+ LDAP server implementations MUST support the anonymous authentication
+ mechanism of the simple Bind method (Section 5.1.1).
+
+ LDAP implementations that support any authentication mechanism other
+ than the anonymous authentication mechanism of the simple Bind method
+ MUST support the name/password authentication mechanism of the simple
+ Bind method (Section 5.1.3) and MUST be capable of protecting this
+ name/password authentication using TLS as established by the StartTLS
+ operation (Section 3).
+
+ Implementations SHOULD disallow the use of the name/password
+ authentication mechanism by default when suitable data security
+ services are not in place, and they MAY provide other suitable data
+ security services for use with this authentication mechanism.
+
+ Implementations MAY support additional authentication mechanisms.
+ Some of these mechanisms are discussed below.
+
+ LDAP server implementations SHOULD support client assertion of
+ authorization identity via the SASL EXTERNAL mechanism (Section
+ 5.2.3).
+
+ LDAP server implementations that support no authentication mechanism
+ other than the anonymous mechanism of the simple bind method SHOULD
+ support use of TLS as established by the StartTLS operation (Section
+ 3). (Other servers MUST support TLS per the second paragraph of this
+ section.)
+
+
+
+
+
+
+
+
+Harrison Standards Track [Page 7]
+
+RFC 4513 LDAP Authentication Methods June 2006
+
+
+ Implementations supporting TLS MUST support the
+ TLS_RSA_WITH_3DES_EDE_CBC_SHA ciphersuite and SHOULD support the
+ TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA ciphersuite. Support for the
+ latter ciphersuite is recommended to encourage interoperability with
+ implementations conforming to earlier LDAP StartTLS specifications.
+
+3. StartTLS Operation
+
+ The Start Transport Layer Security (StartTLS) operation defined in
+ Section 4.14 of [RFC4511] provides the ability to establish TLS
+ [RFC4346] in an LDAP session.
+
+ The goals of using the TLS protocol with LDAP are to ensure data
+ confidentiality and integrity, and to optionally provide for
+ authentication. TLS expressly provides these capabilities, although
+ the authentication services of TLS are available to LDAP only in
+ combination with the SASL EXTERNAL authentication method (see Section
+ 5.2.3), and then only if the SASL EXTERNAL implementation chooses to
+ make use of the TLS credentials.
+
+3.1. TLS Establishment Procedures
+
+ This section describes the overall procedures clients and servers
+ must follow for TLS establishment. These procedures take into
+ consideration various aspects of the TLS layer including discovery of
+ resultant security level and assertion of the client's authorization
+ identity.
+
+3.1.1. StartTLS Request Sequencing
+
+ A client may send the StartTLS extended request at any time after
+ establishing an LDAP session, except:
+
+ - when TLS is currently established on the session,
+ - when a multi-stage SASL negotiation is in progress on the
+ session, or
+ - when there are outstanding responses for operation requests
+ previously issued on the session.
+
+ As described in [RFC4511], Section 4.14.1, a (detected) violation of
+ any of these requirements results in a return of the operationsError
+ resultCode.
+
+ Client implementers should ensure that they strictly follow these
+ operation sequencing requirements to prevent interoperability issues.
+ Operational experience has shown that violating these requirements
+
+
+
+
+
+Harrison Standards Track [Page 8]
+
+RFC 4513 LDAP Authentication Methods June 2006
+
+
+ causes interoperability issues because there are race conditions that
+ prevent servers from detecting some violations of these requirements
+ due to factors such as server hardware speed and network latencies.
+
+ There is no general requirement that the client have or have not
+ already performed a Bind operation (Section 5) before sending a
+ StartTLS operation request; however, where a client intends to
+ perform both a Bind operation and a StartTLS operation, it SHOULD
+ first perform the StartTLS operation so that the Bind request and
+ response messages are protected by the data security services
+ established by the StartTLS operation.
+
+3.1.2. Client Certificate
+
+ If an LDAP server requests or demands that a client provide a user
+ certificate during TLS negotiation and the client does not present a
+ suitable user certificate (e.g., one that can be validated), the
+ server may use a local security policy to determine whether to
+ successfully complete TLS negotiation.
+
+ If a client that has provided a suitable certificate subsequently
+ performs a Bind operation using the SASL EXTERNAL authentication
+ mechanism (Section 5.2.3), information in the certificate may be used
+ by the server to identify and authenticate the client.
+
+3.1.3. Server Identity Check
+
+ In order to prevent man-in-the-middle attacks, the client MUST verify
+ the server's identity (as presented in the server's Certificate
+ message). In this section, the client's understanding of the
+ server's identity (typically the identity used to establish the
+ transport connection) is called the "reference identity".
+
+ The client determines the type (e.g., DNS name or IP address) of the
+ reference identity and performs a comparison between the reference
+ identity and each subjectAltName value of the corresponding type
+ until a match is produced. Once a match is produced, the server's
+ identity has been verified, and the server identity check is
+ complete. Different subjectAltName types are matched in different
+ ways. Sections 3.1.3.1 - 3.1.3.3 explain how to compare values of
+ various subjectAltName types.
+
+ The client may map the reference identity to a different type prior
+ to performing a comparison. Mappings may be performed for all
+ available subjectAltName types to which the reference identity can be
+ mapped; however, the reference identity should only be mapped to
+ types for which the mapping is either inherently secure (e.g.,
+ extracting the DNS name from a URI to compare with a subjectAltName
+
+
+
+Harrison Standards Track [Page 9]
+
+RFC 4513 LDAP Authentication Methods June 2006
+
+
+ of type dNSName) or for which the mapping is performed in a secure
+ manner (e.g., using DNSSEC, or using user- or admin-configured host-
+ to-address/address-to-host lookup tables).
+
+ The server's identity may also be verified by comparing the reference
+ identity to the Common Name (CN) [RFC4519] value in the leaf Relative
+ Distinguished Name (RDN) of the subjectName field of the server's
+ certificate. This comparison is performed using the rules for
+ comparison of DNS names in Section 3.1.3.1, below, with the exception
+ that no wildcard matching is allowed. Although the use of the Common
+ Name value is existing practice, it is deprecated, and Certification
+ Authorities are encouraged to provide subjectAltName values instead.
+ Note that the TLS implementation may represent DNs in certificates
+ according to X.500 or other conventions. For example, some X.500
+ implementations order the RDNs in a DN using a left-to-right (most
+ significant to least significant) convention instead of LDAP's
+ right-to-left convention.
+
+ If the server identity check fails, user-oriented clients SHOULD
+ either notify the user (clients may give the user the opportunity to
+ continue with the LDAP session in this case) or close the transport
+ connection and indicate that the server's identity is suspect.
+ Automated clients SHOULD close the transport connection and then
+ return or log an error indicating that the server's identity is
+ suspect or both.
+
+ Beyond the server identity check described in this section, clients
+ should be prepared to do further checking to ensure that the server
+ is authorized to provide the service it is requested to provide. The
+ client may need to make use of local policy information in making
+ this determination.
+
+3.1.3.1. Comparison of DNS Names
+
+ If the reference identity is an internationalized domain name,
+ conforming implementations MUST convert it to the ASCII Compatible
+ Encoding (ACE) format as specified in Section 4 of RFC 3490 [RFC3490]
+ before comparison with subjectAltName values of type dNSName.
+ Specifically, conforming implementations MUST perform the conversion
+ operation specified in Section 4 of RFC 3490 as follows:
+
+ * in step 1, the domain name SHALL be considered a "stored
+ string";
+ * in step 3, set the flag called "UseSTD3ASCIIRules";
+ * in step 4, process each label with the "ToASCII" operation; and
+ * in step 5, change all label separators to U+002E (full stop).
+
+
+
+
+
+Harrison Standards Track [Page 10]
+
+RFC 4513 LDAP Authentication Methods June 2006
+
+
+ After performing the "to-ASCII" conversion, the DNS labels and names
+ MUST be compared for equality according to the rules specified in
+ Section 3 of RFC3490.
+
+ The '*' (ASCII 42) wildcard character is allowed in subjectAltName
+ values of type dNSName, and then only as the left-most (least
+ significant) DNS label in that value. This wildcard matches any
+ left-most DNS label in the server name. That is, the subject
+ *.example.com matches the server names a.example.com and
+ b.example.com, but does not match example.com or a.b.example.com.
+
+3.1.3.2. Comparison of IP Addresses
+
+ When the reference identity is an IP address, the identity MUST be
+ converted to the "network byte order" octet string representation
+ [RFC791][RFC2460]. For IP Version 4, as specified in RFC 791, the
+ octet string will contain exactly four octets. For IP Version 6, as
+ specified in RFC 2460, the octet string will contain exactly sixteen
+ octets. This octet string is then compared against subjectAltName
+ values of type iPAddress. A match occurs if the reference identity
+ octet string and value octet strings are identical.
+
+3.1.3.3. Comparison of Other subjectName Types
+
+ Client implementations MAY support matching against subjectAltName
+ values of other types as described in other documents.
+
+3.1.4. Discovery of Resultant Security Level
+
+ After a TLS layer is established in an LDAP session, both parties are
+ to each independently decide whether or not to continue based on
+ local policy and the security level achieved. If either party
+ decides that the security level is inadequate for it to continue, it
+ SHOULD remove the TLS layer immediately after the TLS (re)negotiation
+ has completed (see [RFC4511], Section 4.14.3, and Section 3.2 below).
+ Implementations may reevaluate the security level at any time and,
+ upon finding it inadequate, should remove the TLS layer.
+
+3.1.5. Refresh of Server Capabilities Information
+
+ After a TLS layer is established in an LDAP session, the client
+ SHOULD discard or refresh all information about the server that it
+ obtained prior to the initiation of the TLS negotiation and that it
+ did not obtain through secure mechanisms. This protects against
+ man-in-the-middle attacks that may have altered any server
+ capabilities information retrieved prior to TLS layer installation.
+
+
+
+
+
+Harrison Standards Track [Page 11]
+
+RFC 4513 LDAP Authentication Methods June 2006
+
+
+ The server may advertise different capabilities after installing a
+ TLS layer. In particular, the value of 'supportedSASLMechanisms' may
+ be different after a TLS layer has been installed (specifically, the
+ EXTERNAL and PLAIN [PLAIN] mechanisms are likely to be listed only
+ after a TLS layer has been installed).
+
+3.2. Effect of TLS on Authorization State
+
+ The establishment, change, and/or closure of TLS may cause the
+ authorization state to move to a new state. This is discussed
+ further in Section 4.
+
+3.3. TLS Ciphersuites
+
+ Several issues should be considered when selecting TLS ciphersuites
+ that are appropriate for use in a given circumstance. These issues
+ include the following:
+
+ - The ciphersuite's ability to provide adequate confidentiality
+ protection for passwords and other data sent over the transport
+ connection. Client and server implementers should recognize
+ that some TLS ciphersuites provide no confidentiality
+ protection, while other ciphersuites that do provide
+ confidentiality protection may be vulnerable to being cracked
+ using brute force methods, especially in light of ever-
+ increasing CPU speeds that reduce the time needed to
+ successfully mount such attacks.
+
+ - Client and server implementers should carefully consider the
+ value of the password or data being protected versus the level
+ of confidentiality protection provided by the ciphersuite to
+ ensure that the level of protection afforded by the ciphersuite
+ is appropriate.
+
+ - The ciphersuite's vulnerability (or lack thereof) to man-in-the-
+ middle attacks. Ciphersuites vulnerable to man-in-the-middle
+ attacks SHOULD NOT be used to protect passwords or sensitive
+ data, unless the network configuration is such that the danger
+ of a man-in-the-middle attack is negligible.
+
+ - After a TLS negotiation (either initial or subsequent) is
+ completed, both protocol peers should independently verify that
+ the security services provided by the negotiated ciphersuite are
+ adequate for the intended use of the LDAP session. If they are
+ not, the TLS layer should be closed.
+
+
+
+
+
+
+Harrison Standards Track [Page 12]
+
+RFC 4513 LDAP Authentication Methods June 2006
+
+
+4. Authorization State
+
+ Every LDAP session has an associated authorization state. This state
+ is comprised of numerous factors such as what (if any) authentication
+ state has been established, how it was established, and what security
+ services are in place. Some factors may be determined and/or
+ affected by protocol events (e.g., Bind, StartTLS, or TLS closure),
+ and some factors may be determined by external events (e.g., time of
+ day or server load).
+
+ While it is often convenient to view authorization state in
+ simplistic terms (as we often do in this technical specification)
+ such as "an anonymous state", it is noted that authorization systems
+ in LDAP implementations commonly involve many factors that
+ interrelate in complex manners.
+
+ Authorization in LDAP is a local matter. One of the key factors in
+ making authorization decisions is authorization identity. The Bind
+ operation (defined in Section 4.2 of [RFC4511] and discussed further
+ in Section 5 below) allows information to be exchanged between the
+ client and server to establish an authorization identity for the LDAP
+ session. The Bind operation may also be used to move the LDAP
+ session to an anonymous authorization state (see Section 5.1.1).
+
+ Upon initial establishment of the LDAP session, the session has an
+ anonymous authorization identity. Among other things this implies
+ that the client need not send a BindRequest in the first PDU of the
+ LDAP message layer. The client may send any operation request prior
+ to performing a Bind operation, and the server MUST treat it as if it
+ had been performed after an anonymous Bind operation (Section 5.1.1).
+
+ Upon receipt of a Bind request, the server immediately moves the
+ session to an anonymous authorization state. If the Bind request is
+ successful, the session is moved to the requested authentication
+ state with its associated authorization state. Otherwise, the
+ session remains in an anonymous state.
+
+ It is noted that other events both internal and external to LDAP may
+ result in the authentication and authorization states being moved to
+ an anonymous one. For instance, the establishment, change, or
+ closure of data security services may result in a move to an
+ anonymous state, or the user's credential information (e.g.,
+ certificate) may have expired. The former is an example of an event
+ internal to LDAP, whereas the latter is an example of an event
+ external to LDAP.
+
+
+
+
+
+
+Harrison Standards Track [Page 13]
+
+RFC 4513 LDAP Authentication Methods June 2006
+
+
+5. Bind Operation
+
+ The Bind operation ([RFC4511], Section 4.2) allows authentication
+ information to be exchanged between the client and server to
+ establish a new authorization state.
+
+ The Bind request typically specifies the desired authentication
+ identity. Some Bind mechanisms also allow the client to specify the
+ authorization identity. If the authorization identity is not
+ specified, the server derives it from the authentication identity in
+ an implementation-specific manner.
+
+ If the authorization identity is specified, the server MUST verify
+ that the client's authentication identity is permitted to assume
+ (e.g., proxy for) the asserted authorization identity. The server
+ MUST reject the Bind operation with an invalidCredentials resultCode
+ in the Bind response if the client is not so authorized.
+
+5.1. Simple Authentication Method
+
+ The simple authentication method of the Bind Operation provides three
+ authentication mechanisms:
+
+ - An anonymous authentication mechanism (Section 5.1.1).
+
+ - An unauthenticated authentication mechanism (Section 5.1.2).
+
+ - A name/password authentication mechanism using credentials
+ consisting of a name (in the form of an LDAP distinguished name
+ [RFC4514]) and a password (Section 5.1.3).
+
+5.1.1. Anonymous Authentication Mechanism of Simple Bind
+
+ An LDAP client may use the anonymous authentication mechanism of the
+ simple Bind method to explicitly establish an anonymous authorization
+ state by sending a Bind request with a name value of zero length and
+ specifying the simple authentication choice containing a password
+ value of zero length.
+
+5.1.2. Unauthenticated Authentication Mechanism of Simple Bind
+
+ An LDAP client may use the unauthenticated authentication mechanism
+ of the simple Bind method to establish an anonymous authorization
+ state by sending a Bind request with a name value (a distinguished
+ name in LDAP string form [RFC4514] of non-zero length) and specifying
+ the simple authentication choice containing a password value of zero
+ length.
+
+
+
+
+Harrison Standards Track [Page 14]
+
+RFC 4513 LDAP Authentication Methods June 2006
+
+
+ The distinguished name value provided by the client is intended to be
+ used for trace (e.g., logging) purposes only. The value is not to be
+ authenticated or otherwise validated (including verification that the
+ DN refers to an existing directory object). The value is not to be
+ used (directly or indirectly) for authorization purposes.
+
+ Unauthenticated Bind operations can have significant security issues
+ (see Section 6.3.1). In particular, users intending to perform
+ Name/Password Authentication may inadvertently provide an empty
+ password and thus cause poorly implemented clients to request
+ Unauthenticated access. Clients SHOULD be implemented to require
+ user selection of the Unauthenticated Authentication Mechanism by
+ means other than user input of an empty password. Clients SHOULD
+ disallow an empty password input to a Name/Password Authentication
+ user interface. Additionally, Servers SHOULD by default fail
+ Unauthenticated Bind requests with a resultCode of
+ unwillingToPerform.
+
+5.1.3. Name/Password Authentication Mechanism of Simple Bind
+
+ An LDAP client may use the name/password authentication mechanism of
+ the simple Bind method to establish an authenticated authorization
+ state by sending a Bind request with a name value (a distinguished
+ name in LDAP string form [RFC4514] of non-zero length) and specifying
+ the simple authentication choice containing an OCTET STRING password
+ value of non-zero length.
+
+ Servers that map the DN sent in the Bind request to a directory entry
+ with an associated set of one or more passwords used with this
+ mechanism will compare the presented password to that set of
+ passwords. The presented password is considered valid if it matches
+ any member of this set.
+
+ A resultCode of invalidDNSyntax indicates that the DN sent in the
+ name value is syntactically invalid. A resultCode of
+ invalidCredentials indicates that the DN is syntactically correct but
+ not valid for purposes of authentication, that the password is not
+ valid for the DN, or that the server otherwise considers the
+ credentials invalid. A resultCode of success indicates that the
+ credentials are valid and that the server is willing to provide
+ service to the entity these credentials identify.
+
+ Server behavior is undefined for Bind requests specifying the
+ name/password authentication mechanism with a zero-length name value
+ and a password value of non-zero length.
+
+
+
+
+
+
+Harrison Standards Track [Page 15]
+
+RFC 4513 LDAP Authentication Methods June 2006
+
+
+ The name/password authentication mechanism of the simple Bind method
+ is not suitable for authentication in environments without
+ confidentiality protection.
+
+5.2. SASL Authentication Method
+
+ The sasl authentication method of the Bind Operation provides
+ facilities for using any SASL mechanism including authentication
+ mechanisms and other services (e.g., data security services).
+
+5.2.1. SASL Protocol Profile
+
+ LDAP allows authentication via any SASL mechanism [RFC4422]. As LDAP
+ includes native anonymous and name/password (plain text)
+ authentication methods, the ANONYMOUS [RFC4505] and PLAIN [PLAIN]
+ SASL mechanisms are typically not used with LDAP.
+
+ Each protocol that utilizes SASL services is required to supply
+ certain information profiling the way they are exposed through the
+ protocol ([RFC4422], Section 4). This section explains how each of
+ these profiling requirements is met by LDAP.
+
+5.2.1.1. SASL Service Name for LDAP
+
+ The SASL service name for LDAP is "ldap", which has been registered
+ with the IANA as a SASL service name.
+
+5.2.1.2. SASL Authentication Initiation and Protocol Exchange
+
+ SASL authentication is initiated via a BindRequest message
+ ([RFC4511], Section 4.2) with the following parameters:
+
+ - The version is 3.
+ - The AuthenticationChoice is sasl.
+ - The mechanism element of the SaslCredentials sequence contains
+ the value of the desired SASL mechanism.
+ - The optional credentials field of the SaslCredentials sequence
+ MAY be used to provide an initial client response for mechanisms
+ that are defined to have the client send data first (see
+ [RFC4422], Sections 3 and 5).
+
+ In general, a SASL authentication protocol exchange consists of a
+ series of server challenges and client responses, the contents of
+ which are specific to and defined by the SASL mechanism. Thus, for
+ some SASL authentication mechanisms, it may be necessary for the
+ client to respond to one or more server challenges by sending
+ BindRequest messages multiple times. A challenge is indicated by the
+ server sending a BindResponse message with the resultCode set to
+
+
+
+Harrison Standards Track [Page 16]
+
+RFC 4513 LDAP Authentication Methods June 2006
+
+
+ saslBindInProgress. This indicates that the server requires the
+ client to send a new BindRequest message with the same SASL mechanism
+ to continue the authentication process.
+
+ To the LDAP message layer, these challenges and responses are opaque
+ binary tokens of arbitrary length. LDAP servers use the
+ serverSaslCreds field (an OCTET STRING) in a BindResponse message to
+ transmit each challenge. LDAP clients use the credentials field (an
+ OCTET STRING) in the SaslCredentials sequence of a BindRequest
+ message to transmit each response. Note that unlike some Internet
+ protocols where SASL is used, LDAP is not text based and does not
+ Base64-transform these challenge and response values.
+
+ Clients sending a BindRequest message with the sasl choice selected
+ SHOULD send a zero-length value in the name field. Servers receiving
+ a BindRequest message with the sasl choice selected SHALL ignore any
+ value in the name field.
+
+ A client may abort a SASL Bind negotiation by sending a BindRequest
+ message with a different value in the mechanism field of
+ SaslCredentials or with an AuthenticationChoice other than sasl.
+
+ If the client sends a BindRequest with the sasl mechanism field as an
+ empty string, the server MUST return a BindResponse with a resultCode
+ of authMethodNotSupported. This will allow the client to abort a
+ negotiation if it wishes to try again with the same SASL mechanism.
+
+ The server indicates completion of the SASL challenge-response
+ exchange by responding with a BindResponse in which the resultCode
+ value is not saslBindInProgress.
+
+ The serverSaslCreds field in the BindResponse can be used to include
+ an optional challenge with a success notification for mechanisms that
+ are defined to have the server send additional data along with the
+ indication of successful completion.
+
+5.2.1.3. Optional Fields
+
+ As discussed above, LDAP provides an optional field for carrying an
+ initial response in the message initiating the SASL exchange and
+ provides an optional field for carrying additional data in the
+ message indicating the outcome of the authentication exchange. As
+ the mechanism-specific content in these fields may be zero length,
+ SASL requires protocol specifications to detail how an empty field is
+ distinguished from an absent field.
+
+
+
+
+
+
+Harrison Standards Track [Page 17]
+
+RFC 4513 LDAP Authentication Methods June 2006
+
+
+ Zero-length initial response data is distinguished from no initial
+ response data in the initiating message, a BindRequest PDU, by the
+ presence of the SaslCredentials.credentials OCTET STRING (of length
+ zero) in that PDU. If the client does not intend to send an initial
+ response with the BindRequest initiating the SASL exchange, it MUST
+ omit the SaslCredentials.credentials OCTET STRING (rather than
+ include an zero-length OCTET STRING).
+
+ Zero-length additional data is distinguished from no additional
+ response data in the outcome message, a BindResponse PDU, by the
+ presence of the serverSaslCreds OCTET STRING (of length zero) in that
+ PDU. If a server does not intend to send additional data in the
+ BindResponse message indicating outcome of the exchange, the server
+ SHALL omit the serverSaslCreds OCTET STRING (rather than including a
+ zero-length OCTET STRING).
+
+5.2.1.4. Octet Where Negotiated Security Layers Take Effect
+
+ SASL layers take effect following the transmission by the server and
+ reception by the client of the final BindResponse in the SASL
+ exchange with a resultCode of success.
+
+ Once a SASL layer providing data integrity or confidentiality
+ services takes effect, the layer remains in effect until a new layer
+ is installed (i.e., at the first octet following the final
+ BindResponse of the Bind operation that caused the new layer to take
+ effect). Thus, an established SASL layer is not affected by a failed
+ or non-SASL Bind.
+
+5.2.1.5. Determination of Supported SASL Mechanisms
+
+ Clients may determine the SASL mechanisms a server supports by
+ reading the 'supportedSASLMechanisms' attribute from the root DSE
+ (DSA-Specific Entry) ([RFC4512], Section 5.1). The values of this
+ attribute, if any, list the mechanisms the server supports in the
+ current LDAP session state. LDAP servers SHOULD allow all clients --
+ even those with an anonymous authorization -- to retrieve the
+ 'supportedSASLMechanisms' attribute of the root DSE both before and
+ after the SASL authentication exchange. The purpose of the latter is
+ to allow the client to detect possible downgrade attacks (see Section
+ 6.4 and [RFC4422], Section 6.1.2).
+
+ Because SASL mechanisms provide critical security functions, clients
+ and servers should be configurable to specify what mechanisms are
+ acceptable and allow only those mechanisms to be used. Both clients
+ and servers must confirm that the negotiated security level meets
+ their requirements before proceeding to use the session.
+
+
+
+
+Harrison Standards Track [Page 18]
+
+RFC 4513 LDAP Authentication Methods June 2006
+
+
+5.2.1.6. Rules for Using SASL Layers
+
+ Upon installing a SASL layer, the client SHOULD discard or refresh
+ all information about the server that it obtained prior to the
+ initiation of the SASL negotiation and that it did not obtain through
+ secure mechanisms.
+
+ If a lower-level security layer (such as TLS) is installed, any SASL
+ layer SHALL be layered on top of such security layers regardless of
+ the order of their negotiation. In all other respects, the SASL
+ layer and other security layers act independently, e.g., if both a
+ TLS layer and a SASL layer are in effect, then removing the TLS layer
+ does not affect the continuing service of the SASL layer.
+
+5.2.1.7. Support for Multiple Authentications
+
+ LDAP supports multiple SASL authentications as defined in [RFC4422],
+ Section 4.
+
+5.2.1.8. SASL Authorization Identities
+
+ Some SASL mechanisms allow clients to request a desired authorization
+ identity for the LDAP session ([RFC4422], Section 3.4). The decision
+ to allow or disallow the current authentication identity to have
+ access to the requested authorization identity is a matter of local
+ policy. The authorization identity is a string of UTF-8 [RFC3629]
+ encoded [Unicode] characters corresponding to the following Augmented
+ Backus-Naur Form (ABNF) [RFC4234] grammar:
+
+ authzId = dnAuthzId / uAuthzId
+
+ ; distinguished-name-based authz id
+ dnAuthzId = "dn:" distinguishedName
+
+ ; unspecified authorization id, UTF-8 encoded
+ uAuthzId = "u:" userid
+ userid = *UTF8 ; syntax unspecified
+
+ where the distinguishedName rule is defined in Section 3 of [RFC4514]
+ and the UTF8 rule is defined in Section 1.4 of [RFC4512].
+
+ The dnAuthzId choice is used to assert authorization identities in
+ the form of a distinguished name to be matched in accordance with the
+ distinguishedNameMatch matching rule ([RFC4517], Section 4.2.15).
+ There is no requirement that the asserted distinguishedName value be
+ that of an entry in the directory.
+
+
+
+
+
+Harrison Standards Track [Page 19]
+
+RFC 4513 LDAP Authentication Methods June 2006
+
+
+ The uAuthzId choice allows clients to assert an authorization
+ identity that is not in distinguished name form. The format of
+ userid is defined only as a sequence of UTF-8 [RFC3629] encoded
+ [Unicode] characters, and any further interpretation is a local
+ matter. For example, the userid could identify a user of a specific
+ directory service, be a login name, or be an email address. A
+ uAuthzId SHOULD NOT be assumed to be globally unique. To compare
+ uAuthzId values, each uAuthzId value MUST be prepared as a "query"
+ string ([RFC3454], Section 7) using the SASLprep [RFC4013] algorithm,
+ and then the two values are compared octet-wise.
+
+ The above grammar is extensible. The authzId production may be
+ extended to support additional forms of identities. Each form is
+ distinguished by its unique prefix (see Section 3.12 of [RFC4520] for
+ registration requirements).
+
+5.2.2. SASL Semantics within LDAP
+
+ Implementers must take care to maintain the semantics of SASL
+ specifications when handling data that has different semantics in the
+ LDAP protocol.
+
+ For example, the SASL DIGEST-MD5 authentication mechanism
+ [DIGEST-MD5] utilizes an authentication identity and a realm that are
+ syntactically simple strings and semantically simple username
+ [RFC4013] and realm values. These values are not LDAP DNs, and there
+ is no requirement that they be represented or treated as such.
+
+5.2.3. SASL EXTERNAL Authentication Mechanism
+
+ A client can use the SASL EXTERNAL ([RFC4422], Appendix A) mechanism
+ to request the LDAP server to authenticate and establish a resulting
+ authorization identity using security credentials exchanged by a
+ lower security layer (such as by TLS authentication). If the
+ client's authentication credentials have not been established at a
+ lower security layer, the SASL EXTERNAL Bind MUST fail with a
+ resultCode of inappropriateAuthentication. Although this situation
+ has the effect of leaving the LDAP session in an anonymous state
+ (Section 4), the state of any installed security layer is unaffected.
+
+ A client may either request that its authorization identity be
+ automatically derived from its authentication credentials exchanged
+ at a lower security layer, or it may explicitly provide a desired
+ authorization identity. The former is known as an implicit
+ assertion, and the latter as an explicit assertion.
+
+
+
+
+
+
+Harrison Standards Track [Page 20]
+
+RFC 4513 LDAP Authentication Methods June 2006
+
+
+5.2.3.1. Implicit Assertion
+
+ An implicit authorization identity assertion is performed by invoking
+ a Bind request of the SASL form using the EXTERNAL mechanism name
+ that does not include the optional credentials field (found within
+ the SaslCredentials sequence in the BindRequest). The server will
+ derive the client's authorization identity from the authentication
+ identity supplied by a security layer (e.g., a public key certificate
+ used during TLS layer installation) according to local policy. The
+ underlying mechanics of how this is accomplished are implementation
+ specific.
+
+5.2.3.2. Explicit Assertion
+
+ An explicit authorization identity assertion is performed by invoking
+ a Bind request of the SASL form using the EXTERNAL mechanism name
+ that includes the credentials field (found within the SaslCredentials
+ sequence in the BindRequest). The value of the credentials field (an
+ OCTET STRING) is the asserted authorization identity and MUST be
+ constructed as documented in Section 5.2.1.8.
+
+6. Security Considerations
+
+ Security issues are discussed throughout this document. The
+ unsurprising conclusion is that security is an integral and necessary
+ part of LDAP. This section discusses a number of LDAP-related
+ security considerations.
+
+6.1. General LDAP Security Considerations
+
+ LDAP itself provides no security or protection from accessing or
+ updating the directory by means other than through the LDAP protocol,
+ e.g., from inspection of server database files by database
+ administrators.
+
+ Sensitive data may be carried in almost any LDAP message, and its
+ disclosure may be subject to privacy laws or other legal regulation
+ in many countries. Implementers should take appropriate measures to
+ protect sensitive data from disclosure to unauthorized entities.
+
+ A session on which the client has not established data integrity and
+ privacy services (e.g., via StartTLS, IPsec, or a suitable SASL
+ mechanism) is subject to man-in-the-middle attacks to view and modify
+ information in transit. Client and server implementers SHOULD take
+ measures to protect sensitive data in the LDAP session from these
+ attacks by using data protection services as discussed in this
+ document. Clients and servers should provide the ability to be
+ configured to require these protections. A resultCode of
+
+
+
+Harrison Standards Track [Page 21]
+
+RFC 4513 LDAP Authentication Methods June 2006
+
+
+ confidentialityRequired indicates that the server requires
+ establishment of (stronger) data confidentiality protection in order
+ to perform the requested operation.
+
+ Access control should always be applied when reading sensitive
+ information or updating directory information.
+
+ Various security factors, including authentication and authorization
+ information and data security services may change during the course
+ of the LDAP session, or even during the performance of a particular
+ operation. Implementations should be robust in the handling of
+ changing security factors.
+
+6.2. StartTLS Security Considerations
+
+ All security gained via use of the StartTLS operation is gained by
+ the use of TLS itself. The StartTLS operation, on its own, does not
+ provide any additional security.
+
+ The level of security provided through the use of TLS depends
+ directly on both the quality of the TLS implementation used and the
+ style of usage of that implementation. Additionally, a man-in-the-
+ middle attacker can remove the StartTLS extended operation from the
+ 'supportedExtension' attribute of the root DSE. Both parties SHOULD
+ independently ascertain and consent to the security level achieved
+ once TLS is established and before beginning use of the TLS-
+ protected session. For example, the security level of the TLS layer
+ might have been negotiated down to plaintext.
+
+ Clients MUST either warn the user when the security level achieved
+ does not provide an acceptable level of data confidentiality and/or
+ data integrity protection, or be configurable to refuse to proceed
+ without an acceptable level of security.
+
+ As stated in Section 3.1.2, a server may use a local security policy
+ to determine whether to successfully complete TLS negotiation.
+ Information in the user's certificate that is originated or verified
+ by the certification authority should be used by the policy
+ administrator when configuring the identification and authorization
+ policy.
+
+ Server implementers SHOULD allow server administrators to elect
+ whether and when data confidentiality and integrity are required, as
+ well as elect whether authentication of the client during the TLS
+ handshake is required.
+
+ Implementers should be aware of and understand TLS security
+ considerations as discussed in the TLS specification [RFC4346].
+
+
+
+Harrison Standards Track [Page 22]
+
+RFC 4513 LDAP Authentication Methods June 2006
+
+
+6.3. Bind Operation Security Considerations
+
+ This section discusses several security considerations relevant to
+ LDAP authentication via the Bind operation.
+
+6.3.1. Unauthenticated Mechanism Security Considerations
+
+ Operational experience shows that clients can (and frequently do)
+ misuse the unauthenticated authentication mechanism of the simple
+ Bind method (see Section 5.1.2). For example, a client program might
+ make a decision to grant access to non-directory information on the
+ basis of successfully completing a Bind operation. LDAP server
+ implementations may return a success response to an unauthenticated
+ Bind request. This may erroneously leave the client with the
+ impression that the server has successfully authenticated the
+ identity represented by the distinguished name when in reality, an
+ anonymous authorization state has been established. Clients that use
+ the results from a simple Bind operation to make authorization
+ decisions should actively detect unauthenticated Bind requests (by
+ verifying that the supplied password is not empty) and react
+ appropriately.
+
+6.3.2. Name/Password Mechanism Security Considerations
+
+ The name/password authentication mechanism of the simple Bind method
+ discloses the password to the server, which is an inherent security
+ risk. There are other mechanisms, such as SASL DIGEST-MD5
+ [DIGEST-MD5], that do not disclose the password to the server.
+
+6.3.3. Password-Related Security Considerations
+
+ LDAP allows multi-valued password attributes. In systems where
+ entries are expected to have one and only one password,
+ administrative controls should be provided to enforce this behavior.
+
+ The use of clear text passwords and other unprotected authentication
+ credentials is strongly discouraged over open networks when the
+ underlying transport service cannot guarantee confidentiality. LDAP
+ implementations SHOULD NOT by default support authentication methods
+ using clear text passwords and other unprotected authentication
+ credentials unless the data on the session is protected using TLS or
+ other data confidentiality and data integrity protection.
+
+ The transmission of passwords in the clear -- typically for
+ authentication or modification -- poses a significant security risk.
+ This risk can be avoided by using SASL authentication [RFC4422]
+
+
+
+
+
+Harrison Standards Track [Page 23]
+
+RFC 4513 LDAP Authentication Methods June 2006
+
+
+ mechanisms that do not transmit passwords in the clear or by
+ negotiating transport or session layer data confidentiality services
+ before transmitting password values.
+
+ To mitigate the security risks associated with the transfer of
+ passwords, a server implementation that supports any password-based
+ authentication mechanism that transmits passwords in the clear MUST
+ support a policy mechanism that at the time of authentication or
+ password modification, requires that:
+
+ A TLS layer has been successfully installed.
+
+ OR
+
+ Some other data confidentiality mechanism that protects the
+ password value from eavesdropping has been provided.
+
+ OR
+
+ The server returns a resultCode of confidentialityRequired for
+ the operation (i.e., name/password Bind with password value,
+ SASL Bind transmitting a password value in the clear, add or
+ modify including a userPassword value, etc.), even if the
+ password value is correct.
+
+ Server implementations may also want to provide policy mechanisms to
+ invalidate or otherwise protect accounts in situations where a server
+ detects that a password for an account has been transmitted in the
+ clear.
+
+6.3.4. Hashed Password Security Considerations
+
+ Some authentication mechanisms (e.g., DIGEST-MD5) transmit a hash of
+ the password value that may be vulnerable to offline dictionary
+ attacks. Implementers should take care to protect such hashed
+ password values during transmission using TLS or other
+ confidentiality mechanisms.
+
+6.4. SASL Security Considerations
+
+ Until data integrity service is installed on an LDAP session, an
+ attacker can modify the transmitted values of the
+ 'supportedSASLMechanisms' attribute response and thus downgrade the
+ list of available SASL mechanisms to include only the least secure
+ mechanism. To detect this type of attack, the client may retrieve
+ the SASL mechanisms the server makes available both before and after
+ data integrity service is installed on an LDAP session. If the
+ client finds that the integrity-protected list (the list obtained
+
+
+
+Harrison Standards Track [Page 24]
+
+RFC 4513 LDAP Authentication Methods June 2006
+
+
+ after data integrity service was installed) contains a stronger
+ mechanism than those in the previously obtained list, the client
+ should assume the previously obtained list was modified by an
+ attacker. In this circumstance it is recommended that the client
+ close the underlying transport connection and then reconnect to
+ reestablish the session.
+
+6.5. Related Security Considerations
+
+ Additional security considerations relating to the various
+ authentication methods and mechanisms discussed in this document
+ apply and can be found in [RFC4422], [RFC4013], [RFC3454], and
+ [RFC3629].
+
+7. IANA Considerations
+
+ The IANA has updated the LDAP Protocol Mechanism registry to indicate
+ that this document and [RFC4511] provide the definitive technical
+ specification for the StartTLS (1.3.6.1.4.1.1466.20037) extended
+ operation.
+
+ The IANA has updated the LDAP LDAPMessage types registry to indicate
+ that this document and [RFC4511] provide the definitive technical
+ specification for the bindRequest (0) and bindResponse (1) message
+ types.
+
+ The IANA has updated the LDAP Bind Authentication Method registry to
+ indicate that this document and [RFC4511] provide the definitive
+ technical specification for the simple (0) and sasl (3) bind
+ authentication methods.
+
+ The IANA has updated the LDAP authzid prefixes registry to indicate
+ that this document provides the definitive technical specification
+ for the dnAuthzId (dn:) and uAuthzId (u:) authzid prefixes.
+
+8. Acknowledgements
+
+ This document combines information originally contained in RFC 2251,
+ RFC 2829, and RFC 2830. RFC 2251 was a product of the Access,
+ Searching, and Indexing of Directories (ASID) Working Group. RFC
+ 2829 and RFC 2830 were products of the LDAP Extensions (LDAPEXT)
+ Working Group.
+
+ This document is a product of the IETF LDAP Revision (LDAPBIS)
+ working group.
+
+
+
+
+
+
+Harrison Standards Track [Page 25]
+
+RFC 4513 LDAP Authentication Methods June 2006
+
+
+9. Normative References
+
+ [RFC791] Postel, J., "Internet Protocol", STD 5, RFC 791,
+ September 1981.
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [RFC2460] Deering, S. and R. Hinden, "Internet Protocol, Version 6
+ (IPv6) Specification", RFC 2460, December 1998.
+
+ [RFC3454] Hoffman, P. and M. Blanchet, "Preparation of
+ Internationalized Strings ("stringprep")", RFC 3454,
+ December 2002.
+
+ [RFC3490] Faltstrom, P., Hoffman, P., and A. Costello,
+ "Internationalizing Domain Names in Applications
+ (IDNA)", RFC 3490, March 2003.
+
+ [RFC3629] Yergeau, F., "UTF-8, a transformation format of ISO
+ 10646", STD 63, RFC 3629, November 2003.
+
+ [RFC4013] Zeilenga, K., "SASLprep: Stringprep Profile for User
+ Names and Passwords", RFC 4013, February 2005.
+
+ [RFC4234] Crocker, D. and P. Overell, "Augmented BNF for Syntax
+ Specifications: ABNF", RFC 4234, October 2005.
+
+ [RFC4346] Dierks, T. and E. Rescorla, "The TLS Protocol Version
+ 1.1", RFC 4346, March 2006.
+
+ [RFC4422] Melnikov, A., Ed. and K. Zeilenga, Ed., "Simple
+ Authentication and Security Layer (SASL)", RFC 4422,
+ June 2006.
+
+ [RFC4510] Zeilenga, K., Ed., "Lightweight Directory Access
+ Protocol (LDAP): Technical Specification Road Map", RFC
+ 4510, June 2006.
+
+ [RFC4511] Sermersheim, J., Ed., "Lightweight Directory Access
+ Protocol (LDAP): The Protocol", RFC 4511, June 2006.
+
+ [RFC4512] Zeilenga, K., "Lightweight Directory Access Protocol
+ (LDAP): Directory Information Models", RFC 4512, June
+ 2006.
+
+
+
+
+
+
+Harrison Standards Track [Page 26]
+
+RFC 4513 LDAP Authentication Methods June 2006
+
+
+ [RFC4514] Zeilenga, K., Ed., "Lightweight Directory Access
+ Protocol (LDAP): String Representation of Distinguished
+ Names", RFC 4514, June 2006.
+
+ [RFC4517] Legg, S., Ed., "Lightweight Directory Access Protocol
+ (LDAP): Syntaxes and Matching Rules", RFC 4517, June
+ 2006.
+
+ [RFC4519] Sciberras, A., Ed., "Lightweight Directory Access
+ Protocol (LDAP): Schema for User Applications", RFC
+ 4519, June 2006.
+
+ [RFC4520] Zeilenga, K., "Internet Assigned Numbers Authority
+ (IANA) Considerations for the Lightweight Directory
+ Access Protocol (LDAP)", BCP 64, RFC 4520, June 2006.
+
+ [Unicode] The Unicode Consortium, "The Unicode Standard, Version
+ 3.2.0" is defined by "The Unicode Standard, Version 3.0"
+ (Reading, MA, Addison-Wesley, 2000. ISBN 0-201-61633-
+ 5), as amended by the "Unicode Standard Annex #27:
+ Unicode 3.1" (http://www.unicode.org/reports/tr27/) and
+ by the "Unicode Standard Annex #28: Unicode 3.2"
+ (http://www.unicode.org/reports/tr28/).
+
+ [X.501] ITU-T Rec. X.501, "The Directory: Models", 1993.
+
+10. Informative References
+
+ [DIGEST-MD5] Leach, P., Newman, C., and A. Melnikov, "Using Digest
+ Authentication as a SASL Mechanism", Work in Progress,
+ March 2006.
+
+ [PLAIN] Zeilenga, K., "The Plain SASL Mechanism", Work in
+ Progress, March 2005.
+
+ [RFC2828] Shirey, R., "Internet Security Glossary", FYI 36, RFC
+ 2828, May 2000.
+
+ [RFC4301] Kent, S. and K. Seo, "Security Architecture for the
+ Internet Protocol", RFC 4301, December 2005.
+
+ [RFC4505] Zeilenga, K., "The Anonymous SASL Mechanism", RFC 4505,
+ June 2006.
+
+
+
+
+
+
+
+
+Harrison Standards Track [Page 27]
+
+RFC 4513 LDAP Authentication Methods June 2006
+
+
+Appendix A. Authentication and Authorization Concepts
+
+ This appendix is non-normative.
+
+ This appendix defines basic terms, concepts, and interrelationships
+ regarding authentication, authorization, credentials, and identity.
+ These concepts are used in describing how various security approaches
+ are utilized in client authentication and authorization.
+
+A.1. Access Control Policy
+
+ An access control policy is a set of rules defining the protection of
+ resources, generally in terms of the capabilities of persons or other
+ entities accessing those resources. Security objects and mechanisms,
+ such as those described here, enable the expression of access control
+ policies and their enforcement.
+
+A.2. Access Control Factors
+
+ A request, when it is being processed by a server, may be associated
+ with a wide variety of security-related factors. The server uses
+ these factors to determine whether and how to process the request.
+ These are called access control factors (ACFs). They might include
+ source IP address, encryption strength, the type of operation being
+ requested, time of day, etc.. Some factors may be specific to the
+ request itself; others may be associated with the transport
+ connection via which the request is transmitted; and others (e.g.,
+ time of day) may be "environmental".
+
+ Access control policies are expressed in terms of access control
+ factors; for example, "a request having ACFs i,j,k can perform
+ operation Y on resource Z". The set of ACFs that a server makes
+ available for such expressions is implementation specific.
+
+A.3. Authentication, Credentials, Identity
+
+ Authentication credentials are the evidence supplied by one party to
+ another, asserting the identity of the supplying party (e.g., a user)
+ who is attempting to establish a new authorization state with the
+ other party (typically a server). Authentication is the process of
+ generating, transmitting, and verifying these credentials and thus
+ the identity they assert. An authentication identity is the name
+ presented in a credential.
+
+ There are many forms of authentication credentials. The form used
+ depends upon the particular authentication mechanism negotiated by
+ the parties. X.509 certificates, Kerberos tickets, and simple
+ identity and password pairs are all examples of authentication
+
+
+
+Harrison Standards Track [Page 28]
+
+RFC 4513 LDAP Authentication Methods June 2006
+
+
+ credential forms. Note that an authentication mechanism may
+ constrain the form of authentication identities used with it.
+
+A.4. Authorization Identity
+
+ An authorization identity is one kind of access control factor. It
+ is the name of the user or other entity that requests that operations
+ be performed. Access control policies are often expressed in terms
+ of authorization identities; for example, "entity X can perform
+ operation Y on resource Z".
+
+ The authorization identity of an LDAP session is often semantically
+ the same as the authentication identity presented by the client, but
+ it may be different. SASL allows clients to specify an authorization
+ identity distinct from the authentication identity asserted by the
+ client's credentials. This permits agents such as proxy servers to
+ authenticate using their own credentials, yet request the access
+ privileges of the identity for which they are proxying [RFC4422].
+ Also, the form of authentication identity supplied by a service like
+ TLS may not correspond to the authorization identities used to
+ express a server's access control policy, thus requiring a server-
+ specific mapping to be done. The method by which a server composes
+ and validates an authorization identity from the authentication
+ credentials supplied by a client is implementation specific.
+
+Appendix B. Summary of Changes
+
+ This appendix is non-normative.
+
+ This appendix summarizes substantive changes made to RFC 2251, RFC
+ 2829 and RFC 2830. In addition to the specific changes detailed
+ below, the reader of this document should be aware that numerous
+ general editorial changes have been made to the original content from
+ the source documents. These changes include the following:
+
+ - The material originally found in RFC 2251 Sections 4.2.1 and 4.2.2,
+ RFC 2829 (all sections except Sections 2 and 4), and RFC 2830 was
+ combined into a single document.
+
+ - The combined material was substantially reorganized and edited to
+ group related subjects, improve the document flow, and clarify
+ intent.
+
+ - Changes were made throughout the text to align with definitions of
+ LDAP protocol layers and IETF security terminology.
+
+
+
+
+
+
+Harrison Standards Track [Page 29]
+
+RFC 4513 LDAP Authentication Methods June 2006
+
+
+ - Substantial updates and additions were made to security
+ considerations from both documents based on current operational
+ experience.
+
+B.1. Changes Made to RFC 2251
+
+ This section summarizes the substantive changes made to Sections
+ 4.2.1 and 4.2.2 of RFC 2251 by this document. Additional substantive
+ changes to Section 4.2.1 of RFC 2251 are also documented in
+ [RFC4511].
+
+B.1.1. Section 4.2.1 ("Sequencing of the Bind Request")
+
+ - Paragraph 1: Removed the sentence, "If at any stage the client
+ wishes to abort the bind process it MAY unbind and then drop the
+ underlying connection". The Unbind operation still permits this
+ behavior, but it is not documented explicitly.
+
+ - Clarified that the session is moved to an anonymous state upon
+ receipt of the BindRequest PDU and that it is only moved to a non-
+ anonymous state if and when the Bind request is successful.
+
+B.1.2. Section 4.2.2 ("Authentication and Other Security Services")
+
+ - RFC 2251 states that anonymous authentication MUST be performed
+ using the simple bind method. This specification defines the
+ anonymous authentication mechanism of the simple bind method and
+ requires all conforming implementations to support it. Other
+ authentication mechanisms producing anonymous authentication and
+ authorization state may also be implemented and used by conforming
+ implementations.
+
+B.2. Changes Made to RFC 2829
+
+ This section summarizes the substantive changes made to RFC 2829.
+
+B.2.1. Section 4 ("Required security mechanisms")
+
+ - The name/password authentication mechanism (see Section B.2.5
+ below) protected by TLS replaces the SASL DIGEST-MD5 mechanism as
+ LDAP's mandatory-to-implement password-based authentication
+ mechanism. Implementations are encouraged to continue supporting
+ SASL DIGEST-MD5 [DIGEST-MD5].
+
+
+
+
+
+
+
+
+Harrison Standards Track [Page 30]
+
+RFC 4513 LDAP Authentication Methods June 2006
+
+
+B.2.2. Section 5.1 ("Anonymous authentication procedure")
+
+ - Clarified that anonymous authentication involves a name value of
+ zero length and a password value of zero length. The
+ unauthenticated authentication mechanism was added to handle simple
+ Bind requests involving a name value with a non-zero length and a
+ password value of zero length.
+
+B.2.3. Section 6 ("Password-based authentication")
+
+ - See Section B.2.1.
+
+B.2.4. Section 6.1 ("Digest authentication")
+
+ - As the SASL-DIGEST-MD5 mechanism is no longer mandatory to
+ implement, this section is now historical and was not included in
+ this document. RFC 2829, Section 6.1, continues to document the
+ SASL DIGEST-MD5 authentication mechanism.
+
+B.2.5. Section 6.2 ("'simple' authentication choice under TLS
+ encryption")
+
+ - Renamed the "simple" authentication mechanism to the name/password
+ authentication mechanism to better describe it.
+
+ - The use of TLS was generalized to align with definitions of LDAP
+ protocol layers. TLS establishment is now discussed as an
+ independent subject and is generalized for use with all
+ authentication mechanisms and other security layers.
+
+ - Removed the implication that the userPassword attribute is the sole
+ location for storage of password values to be used in
+ authentication. There is no longer any implied requirement for how
+ or where passwords are stored at the server for use in
+ authentication.
+
+B.2.6. Section 6.3 ("Other authentication choices with TLS")
+
+ - See Section B.2.5.
+
+B.2.7. Section 7.1 ("Certificate-based authentication with TLS")
+
+ - See Section B.2.5.
+
+
+
+
+
+
+
+
+Harrison Standards Track [Page 31]
+
+RFC 4513 LDAP Authentication Methods June 2006
+
+
+B.2.8. Section 8 ("Other mechanisms")
+
+ - All SASL authentication mechanisms are explicitly allowed within
+ LDAP. Specifically, this means the SASL ANONYMOUS and SASL PLAIN
+ mechanisms are no longer precluded from use within LDAP.
+
+B.2.9. Section 9 ("Authorization Identity")
+
+ - Specified matching rules for dnAuthzId and uAuthzId values. In
+ particular, the DN value in the dnAuthzId form must be matched
+ using DN matching rules, and the uAuthzId value MUST be prepared
+ using SASLprep rules before being compared octet-wise.
+
+ - Clarified that uAuthzId values should not be assumed to be globally
+ unique.
+
+B.2.10. Section 10 ("TLS Ciphersuites")
+
+ - TLS ciphersuite recommendations are no longer included in this
+ specification. Implementations must now support the
+ TLS_RSA_WITH_3DES_EDE_CBC_SHA ciphersuite and should continue to
+ support the TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA ciphersuite.
+
+ - Clarified that anonymous authentication involves a name value of
+ zero length and a password value of zero length. The
+ unauthenticated authentication mechanism was added to handle simple
+ Bind requests involving a name value with a non-zero length and a
+ password value of zero length.
+
+B.3. Changes Made to RFC 2830
+
+ This section summarizes the substantive changes made to Sections 3
+ and 5 of RFC 2830. Readers should consult [RFC4511] for summaries of
+ changes to other sections.
+
+B.3.1. Section 3.6 ("Server Identity Check")
+
+ - Substantially updated the server identity check algorithm to ensure
+ that it is complete and robust. In particular, the use of all
+ relevant values in the subjectAltName and the subjectName fields
+ are covered by the algorithm and matching rules are specified for
+ each type of value. Mapped (derived) forms of the server identity
+ may now be used when the mapping is performed in a secure fashion.
+
+
+
+
+
+
+
+
+Harrison Standards Track [Page 32]
+
+RFC 4513 LDAP Authentication Methods June 2006
+
+
+B.3.2. Section 3.7 ("Refresh of Server Capabilities Information")
+
+ - Clients are no longer required to always refresh information about
+ server capabilities following TLS establishment. This is to allow
+ for situations where this information was obtained through a secure
+ mechanism.
+
+B.3.3. Section 5 ("Effects of TLS on a Client's Authorization
+ Identity")
+
+ - Establishing a TLS layer on an LDAP session may now cause the
+ authorization state of the LDAP session to change.
+
+B.3.4. Section 5.2 ("TLS Connection Closure Effects")
+
+ - Closing a TLS layer on an LDAP session changes the authentication
+ and authorization state of the LDAP session based on local policy.
+ Specifically, this means that implementations are not required to
+ change the authentication and authorization states to anonymous
+ upon TLS closure.
+
+ - Replaced references to RFC 2401 with RFC 4301.
+
+Author's Address
+
+ Roger Harrison
+ Novell, Inc.
+ 1800 S. Novell Place
+ Provo, UT 84606
+ USA
+
+ Phone: +1 801 861 2642
+ EMail: roger_harrison@novell.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Harrison Standards Track [Page 33]
+
+RFC 4513 LDAP Authentication Methods June 2006
+
+
+Full Copyright Statement
+
+ Copyright (C) The Internet Society (2006).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
+ ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
+ INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
+ INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be
+ found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at
+ ietf-ipr@ietf.org.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is provided by the IETF
+ Administrative Support Activity (IASA).
+
+
+
+
+
+
+
+Harrison Standards Track [Page 34]
+
diff --git a/source4/ldap_server/devdocs/rfc4514.txt b/source4/ldap_server/devdocs/rfc4514.txt
new file mode 100644
index 0000000000..036c077cbf
--- /dev/null
+++ b/source4/ldap_server/devdocs/rfc4514.txt
@@ -0,0 +1,843 @@
+
+
+
+
+
+
+Network Working Group K. Zeilenga, Ed.
+Request for Comments: 4514 OpenLDAP Foundation
+Obsoletes: 2253 June 2006
+Category: Standards Track
+
+
+ Lightweight Directory Access Protocol (LDAP):
+ String Representation of Distinguished Names
+
+Status of This Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2006).
+
+Abstract
+
+ The X.500 Directory uses distinguished names (DNs) as primary keys to
+ entries in the directory. This document defines the string
+ representation used in the Lightweight Directory Access Protocol
+ (LDAP) to transfer distinguished names. The string representation is
+ designed to give a clean representation of commonly used
+ distinguished names, while being able to represent any distinguished
+ name.
+
+1. Background and Intended Usage
+
+ In X.500-based directory systems [X.500], including those accessed
+ using the Lightweight Directory Access Protocol (LDAP) [RFC4510],
+ distinguished names (DNs) are used to unambiguously refer to
+ directory entries [X.501][RFC4512].
+
+ The structure of a DN [X.501] is described in terms of ASN.1 [X.680].
+ In the X.500 Directory Access Protocol [X.511] (and other ITU-defined
+ directory protocols), DNs are encoded using the Basic Encoding Rules
+ (BER) [X.690]. In LDAP, DNs are represented in the string form
+ described in this document.
+
+ It is important to have a common format to be able to unambiguously
+ represent a distinguished name. The primary goal of this
+ specification is ease of encoding and decoding. A secondary goal is
+ to have names that are human readable. It is not expected that LDAP
+
+
+
+Zeilenga Standards Track [Page 1]
+
+RFC 4514 LDAP: Distinguished Names June 2006
+
+
+ implementations with a human user interface would display these
+ strings directly to the user, but that they would most likely be
+ performing translations (such as expressing attribute type names in
+ the local national language).
+
+ This document defines the string representation of Distinguished
+ Names used in LDAP [RFC4511][RFC4517]. Section 2 details the
+ RECOMMENDED algorithm for converting a DN from its ASN.1 structured
+ representation to a string. Section 3 details how to convert a DN
+ from a string to an ASN.1 structured representation.
+
+ While other documents may define other algorithms for converting a DN
+ from its ASN.1 structured representation to a string, all algorithms
+ MUST produce strings that adhere to the requirements of Section 3.
+
+ This document does not define a canonical string representation for
+ DNs. Comparison of DNs for equality is to be performed in accordance
+ with the distinguishedNameMatch matching rule [RFC4517].
+
+ This document is a integral part of the LDAP technical specification
+ [RFC4510], which obsoletes the previously defined LDAP technical
+ specification, RFC 3377, in its entirety. This document obsoletes
+ RFC 2253. Changes since RFC 2253 are summarized in Appendix B.
+
+ This specification assumes familiarity with X.500 [X.500] and the
+ concept of Distinguished Name [X.501][RFC4512].
+
+1.1. Conventions
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in BCP 14 [RFC2119].
+
+ Character names in this document use the notation for code points and
+ names from the Unicode Standard [Unicode]. For example, the letter
+ "a" may be represented as either <U+0061> or <LATIN SMALL LETTER A>.
+
+ Note: a glossary of terms used in Unicode can be found in [Glossary].
+ Information on the Unicode character encoding model can be found in
+ [CharModel].
+
+
+
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 2]
+
+RFC 4514 LDAP: Distinguished Names June 2006
+
+
+2. Converting DistinguishedName from ASN.1 to a String
+
+ X.501 [X.501] defines the ASN.1 [X.680] structure of distinguished
+ name. The following is a variant provided for discussion purposes.
+
+ DistinguishedName ::= RDNSequence
+
+ RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
+
+ RelativeDistinguishedName ::= SET SIZE (1..MAX) OF
+ AttributeTypeAndValue
+
+ AttributeTypeAndValue ::= SEQUENCE {
+ type AttributeType,
+ value AttributeValue }
+
+ This section defines the RECOMMENDED algorithm for converting a
+ distinguished name from an ASN.1-structured representation to a UTF-8
+ [RFC3629] encoded Unicode [Unicode] character string representation.
+ Other documents may describe other algorithms for converting a
+ distinguished name to a string, but only strings that conform to the
+ grammar defined in Section 3 SHALL be produced by LDAP
+ implementations.
+
+2.1. Converting the RDNSequence
+
+ If the RDNSequence is an empty sequence, the result is the empty or
+ zero-length string.
+
+ Otherwise, the output consists of the string encodings of each
+ RelativeDistinguishedName in the RDNSequence (according to Section
+ 2.2), starting with the last element of the sequence and moving
+ backwards toward the first.
+
+ The encodings of adjoining RelativeDistinguishedNames are separated
+ by a comma (',' U+002C) character.
+
+2.2. Converting RelativeDistinguishedName
+
+ When converting from an ASN.1 RelativeDistinguishedName to a string,
+ the output consists of the string encodings of each
+ AttributeTypeAndValue (according to Section 2.3), in any order.
+
+ Where there is a multi-valued RDN, the outputs from adjoining
+ AttributeTypeAndValues are separated by a plus sign ('+' U+002B)
+ character.
+
+
+
+
+
+Zeilenga Standards Track [Page 3]
+
+RFC 4514 LDAP: Distinguished Names June 2006
+
+
+2.3. Converting AttributeTypeAndValue
+
+ The AttributeTypeAndValue is encoded as the string representation of
+ the AttributeType, followed by an equals sign ('=' U+003D) character,
+ followed by the string representation of the AttributeValue. The
+ encoding of the AttributeValue is given in Section 2.4.
+
+ If the AttributeType is defined to have a short name (descriptor)
+ [RFC4512] and that short name is known to be registered [REGISTRY]
+ [RFC4520] as identifying the AttributeType, that short name, a
+ <descr>, is used. Otherwise the AttributeType is encoded as the
+ dotted-decimal encoding, a <numericoid>, of its OBJECT IDENTIFIER.
+ The <descr> and <numericoid> are defined in [RFC4512].
+
+ Implementations are not expected to dynamically update their
+ knowledge of registered short names. However, implementations SHOULD
+ provide a mechanism to allow their knowledge of registered short
+ names to be updated.
+
+2.4. Converting an AttributeValue from ASN.1 to a String
+
+ If the AttributeType is of the dotted-decimal form, the
+ AttributeValue is represented by an number sign ('#' U+0023)
+ character followed by the hexadecimal encoding of each of the octets
+ of the BER encoding of the X.500 AttributeValue. This form is also
+ used when the syntax of the AttributeValue does not have an LDAP-
+ specific ([RFC4517], Section 3.1) string encoding defined for it, or
+ the LDAP-specific string encoding is not restricted to UTF-8-encoded
+ Unicode characters. This form may also be used in other cases, such
+ as when a reversible string representation is desired (see Section
+ 5.2).
+
+ Otherwise, if the AttributeValue is of a syntax that has a LDAP-
+ specific string encoding, the value is converted first to a UTF-8-
+ encoded Unicode string according to its syntax specification (see
+ [RFC4517], Section 3.3, for examples). If that UTF-8-encoded Unicode
+ string does not have any of the following characters that need
+ escaping, then that string can be used as the string representation
+ of the value.
+
+ - a space (' ' U+0020) or number sign ('#' U+0023) occurring at
+ the beginning of the string;
+
+ - a space (' ' U+0020) character occurring at the end of the
+ string;
+
+
+
+
+
+
+Zeilenga Standards Track [Page 4]
+
+RFC 4514 LDAP: Distinguished Names June 2006
+
+
+ - one of the characters '"', '+', ',', ';', '<', '>', or '\'
+ (U+0022, U+002B, U+002C, U+003B, U+003C, U+003E, or U+005C,
+ respectively);
+
+ - the null (U+0000) character.
+
+ Other characters may be escaped.
+
+ Each octet of the character to be escaped is replaced by a backslash
+ and two hex digits, which form a single octet in the code of the
+ character. Alternatively, if and only if the character to be escaped
+ is one of
+
+ ' ', '"', '#', '+', ',', ';', '<', '=', '>', or '\'
+ (U+0020, U+0022, U+0023, U+002B, U+002C, U+003B,
+ U+003C, U+003D, U+003E, U+005C, respectively)
+
+ it can be prefixed by a backslash ('\' U+005C).
+
+ Examples of the escaping mechanism are shown in Section 4.
+
+3. Parsing a String Back to a Distinguished Name
+
+ The string representation of Distinguished Names is restricted to
+ UTF-8 [RFC3629] encoded Unicode [Unicode] characters. The structure
+ of this string representation is specified using the following
+ Augmented BNF [RFC4234] grammar:
+
+ distinguishedName = [ relativeDistinguishedName
+ *( COMMA relativeDistinguishedName ) ]
+ relativeDistinguishedName = attributeTypeAndValue
+ *( PLUS attributeTypeAndValue )
+ attributeTypeAndValue = attributeType EQUALS attributeValue
+ attributeType = descr / numericoid
+ attributeValue = string / hexstring
+
+ ; The following characters are to be escaped when they appear
+ ; in the value to be encoded: ESC, one of <escaped>, leading
+ ; SHARP or SPACE, trailing SPACE, and NULL.
+ string = [ ( leadchar / pair ) [ *( stringchar / pair )
+ ( trailchar / pair ) ] ]
+
+ leadchar = LUTF1 / UTFMB
+ LUTF1 = %x01-1F / %x21 / %x24-2A / %x2D-3A /
+ %x3D / %x3F-5B / %x5D-7F
+
+ trailchar = TUTF1 / UTFMB
+ TUTF1 = %x01-1F / %x21 / %x23-2A / %x2D-3A /
+
+
+
+Zeilenga Standards Track [Page 5]
+
+RFC 4514 LDAP: Distinguished Names June 2006
+
+
+ %x3D / %x3F-5B / %x5D-7F
+
+ stringchar = SUTF1 / UTFMB
+ SUTF1 = %x01-21 / %x23-2A / %x2D-3A /
+ %x3D / %x3F-5B / %x5D-7F
+
+ pair = ESC ( ESC / special / hexpair )
+ special = escaped / SPACE / SHARP / EQUALS
+ escaped = DQUOTE / PLUS / COMMA / SEMI / LANGLE / RANGLE
+ hexstring = SHARP 1*hexpair
+ hexpair = HEX HEX
+
+ where the productions <descr>, <numericoid>, <COMMA>, <DQUOTE>,
+ <EQUALS>, <ESC>, <HEX>, <LANGLE>, <NULL>, <PLUS>, <RANGLE>, <SEMI>,
+ <SPACE>, <SHARP>, and <UTFMB> are defined in [RFC4512].
+
+ Each <attributeType>, either a <descr> or a <numericoid>, refers to
+ an attribute type of an attribute value assertion (AVA). The
+ <attributeType> is followed by an <EQUALS> and an <attributeValue>.
+ The <attributeValue> is either in <string> or <hexstring> form.
+
+ If in <string> form, a LDAP string representation asserted value can
+ be obtained by replacing (left to right, non-recursively) each <pair>
+ appearing in the <string> as follows:
+
+ replace <ESC><ESC> with <ESC>;
+ replace <ESC><special> with <special>;
+ replace <ESC><hexpair> with the octet indicated by the <hexpair>.
+
+ If in <hexstring> form, a BER representation can be obtained from
+ converting each <hexpair> of the <hexstring> to the octet indicated
+ by the <hexpair>.
+
+ There is one or more attribute value assertions, separated by <PLUS>,
+ for a relative distinguished name.
+
+ There is zero or more relative distinguished names, separated by
+ <COMMA>, for a distinguished name.
+
+ Implementations MUST recognize AttributeType name strings
+ (descriptors) listed in the following table, but MAY recognize other
+ name strings.
+
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 6]
+
+RFC 4514 LDAP: Distinguished Names June 2006
+
+
+ String X.500 AttributeType
+ ------ --------------------------------------------
+ CN commonName (2.5.4.3)
+ L localityName (2.5.4.7)
+ ST stateOrProvinceName (2.5.4.8)
+ O organizationName (2.5.4.10)
+ OU organizationalUnitName (2.5.4.11)
+ C countryName (2.5.4.6)
+ STREET streetAddress (2.5.4.9)
+ DC domainComponent (0.9.2342.19200300.100.1.25)
+ UID userId (0.9.2342.19200300.100.1.1)
+
+ These attribute types are described in [RFC4519].
+
+ Implementations MAY recognize other DN string representations.
+ However, as there is no requirement that alternative DN string
+ representations be recognized (and, if so, how), implementations
+ SHOULD only generate DN strings in accordance with Section 2 of this
+ document.
+
+4. Examples
+
+ This notation is designed to be convenient for common forms of name.
+ This section gives a few examples of distinguished names written
+ using this notation. First is a name containing three relative
+ distinguished names (RDNs):
+
+ UID=jsmith,DC=example,DC=net
+
+ Here is an example of a name containing three RDNs, in which the
+ first RDN is multi-valued:
+
+ OU=Sales+CN=J. Smith,DC=example,DC=net
+
+ This example shows the method of escaping of a special characters
+ appearing in a common name:
+
+ CN=James \"Jim\" Smith\, III,DC=example,DC=net
+
+ The following shows the method for encoding a value that contains a
+ carriage return character:
+
+ CN=Before\0dAfter,DC=example,DC=net
+
+ In this RDN example, the type in the RDN is unrecognized, and the
+ value is the BER encoding of an OCTET STRING containing two octets,
+ 0x48 and 0x69.
+
+
+
+
+Zeilenga Standards Track [Page 7]
+
+RFC 4514 LDAP: Distinguished Names June 2006
+
+
+ 1.3.6.1.4.1.1466.0=#04024869
+
+ Finally, this example shows an RDN whose commonName value consists of
+ 5 letters:
+
+ Unicode Character Code UTF-8 Escaped
+ ------------------------------- ------ ------ --------
+ LATIN CAPITAL LETTER L U+004C 0x4C L
+ LATIN SMALL LETTER U U+0075 0x75 u
+ LATIN SMALL LETTER C WITH CARON U+010D 0xC48D \C4\8D
+ LATIN SMALL LETTER I U+0069 0x69 i
+ LATIN SMALL LETTER C WITH ACUTE U+0107 0xC487 \C4\87
+
+ This could be encoded in printable ASCII [ASCII] (useful for
+ debugging purposes) as:
+
+ CN=Lu\C4\8Di\C4\87
+
+5. Security Considerations
+
+ The following security considerations are specific to the handling of
+ distinguished names. LDAP security considerations are discussed in
+ [RFC4511] and other documents comprising the LDAP Technical
+ Specification [RFC4510].
+
+5.1. Disclosure
+
+ Distinguished Names typically consist of descriptive information
+ about the entries they name, which can be people, organizations,
+ devices, or other real-world objects. This frequently includes some
+ of the following kinds of information:
+
+ - the common name of the object (i.e., a person's full name)
+ - an email or TCP/IP address
+ - its physical location (country, locality, city, street address)
+ - organizational attributes (such as department name or
+ affiliation)
+
+ In some cases, such information can be considered sensitive. In many
+ countries, privacy laws exist that prohibit disclosure of certain
+ kinds of descriptive information (e.g., email addresses). Hence,
+ server implementers are encouraged to support Directory Information
+ Tree (DIT) structural rules and name forms [RFC4512], as these
+ provide a mechanism for administrators to select appropriate naming
+ attributes for entries. Administrators are encouraged to use
+ mechanisms, access controls, and other administrative controls that
+ may be available to restrict use of attributes containing sensitive
+ information in naming of entries. Additionally, use of
+
+
+
+Zeilenga Standards Track [Page 8]
+
+RFC 4514 LDAP: Distinguished Names June 2006
+
+
+ authentication and data security services in LDAP [RFC4513][RFC4511]
+ should be considered.
+
+5.2. Use of Distinguished Names in Security Applications
+
+ The transformations of an AttributeValue value from its X.501 form to
+ an LDAP string representation are not always reversible back to the
+ same BER (Basic Encoding Rules) or DER (Distinguished Encoding Rules)
+ form. An example of a situation that requires the DER form of a
+ distinguished name is the verification of an X.509 certificate.
+
+ For example, a distinguished name consisting of one RDN with one AVA,
+ in which the type is commonName and the value is of the TeletexString
+ choice with the letters 'Sam', would be represented in LDAP as the
+ string <CN=Sam>. Another distinguished name in which the value is
+ still 'Sam', but is of the PrintableString choice, would have the
+ same representation <CN=Sam>.
+
+ Applications that require the reconstruction of the DER form of the
+ value SHOULD NOT use the string representation of attribute syntaxes
+ when converting a distinguished name to the LDAP format. Instead,
+ they SHOULD use the hexadecimal form prefixed by the number sign ('#'
+ U+0023) as described in the first paragraph of Section 2.4.
+
+6. Acknowledgements
+
+ This document is an update to RFC 2253, by Mark Wahl, Tim Howes, and
+ Steve Kille. RFC 2253 was a product of the IETF ASID Working Group.
+
+ This document is a product of the IETF LDAPBIS Working Group.
+
+7. References
+
+7.1. Normative References
+
+ [REGISTRY] IANA, Object Identifier Descriptors Registry,
+ <http://www.iana.org/assignments/ldap-parameters>.
+
+ [Unicode] The Unicode Consortium, "The Unicode Standard, Version
+ 3.2.0" is defined by "The Unicode Standard, Version
+ 3.0" (Reading, MA, Addison-Wesley, 2000. ISBN 0-201-
+ 61633-5), as amended by the "Unicode Standard Annex
+ #27: Unicode 3.1"
+ (http://www.unicode.org/reports/tr27/) and by the
+ "Unicode Standard Annex #28: Unicode 3.2"
+ (http://www.unicode.org/reports/tr28/).
+
+
+
+
+
+Zeilenga Standards Track [Page 9]
+
+RFC 4514 LDAP: Distinguished Names June 2006
+
+
+ [X.501] International Telecommunication Union -
+ Telecommunication Standardization Sector, "The
+ Directory -- Models," X.501(1993) (also ISO/IEC 9594-
+ 2:1994).
+
+ [X.680] International Telecommunication Union -
+ Telecommunication Standardization Sector, "Abstract
+ Syntax Notation One (ASN.1) - Specification of Basic
+ Notation", X.680(1997) (also ISO/IEC 8824-1:1998).
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [RFC3629] Yergeau, F., "UTF-8, a transformation format of ISO
+ 10646", STD 63, RFC 3629, November 2003.
+
+ [RFC4234] Crocker, D. and P. Overell, "Augmented BNF for Syntax
+ Specifications: ABNF", RFC 4234, October 2005.
+
+ [RFC4510] Zeilenga, K., Ed., "Lightweight Directory Access
+ Protocol (LDAP): Technical Specification Road Map", RFC
+ 4510, June 2006.
+
+ [RFC4511] Sermersheim, J., Ed., "Lightweight Directory Access
+ Protocol (LDAP): The Protocol", RFC 4511, June 2006.
+
+ [RFC4512] Zeilenga, K., "Lightweight Directory Access Protocol
+ (LDAP): Directory Information Models", RFC 4512, June
+ 2006.
+
+ [RFC4513] Harrison, R., Ed., "Lightweight Directory Access
+ Protocol (LDAP): Authentication Methods and Security
+ Mechanisms", RFC 4513, June 2006.
+
+ [RFC4517] Legg, S., Ed., "Lightweight Directory Access Protocol
+ (LDAP): Syntaxes and Matching Rules", RFC 4517, June
+ 2006.
+
+ [RFC4519] Sciberras, A., Ed., "Lightweight Directory Access
+ Protocol (LDAP): Schema for User Applications", RFC
+ 4519, June 2006.
+
+ [RFC4520] Zeilenga, K., "Internet Assigned Numbers Authority
+ (IANA) Considerations for the Lightweight Directory
+ Access Protocol (LDAP)", BCP 64, RFC 4520, June 2006.
+
+
+
+
+
+
+Zeilenga Standards Track [Page 10]
+
+RFC 4514 LDAP: Distinguished Names June 2006
+
+
+7.2. Informative References
+
+ [ASCII] Coded Character Set--7-bit American Standard Code for
+ Information Interchange, ANSI X3.4-1986.
+
+ [CharModel] Whistler, K. and M. Davis, "Unicode Technical Report
+ #17, Character Encoding Model", UTR17,
+ <http://www.unicode.org/unicode/reports/tr17/>, August
+ 2000.
+
+ [Glossary] The Unicode Consortium, "Unicode Glossary",
+ <http://www.unicode.org/glossary/>.
+
+ [X.500] International Telecommunication Union -
+ Telecommunication Standardization Sector, "The
+ Directory -- Overview of concepts, models and
+ services," X.500(1993) (also ISO/IEC 9594-1:1994).
+
+ [X.511] International Telecommunication Union -
+ Telecommunication Standardization Sector, "The
+ Directory: Abstract Service Definition", X.511(1993)
+ (also ISO/IEC 9594-3:1993).
+
+ [X.690] International Telecommunication Union -
+ Telecommunication Standardization Sector,
+ "Specification of ASN.1 encoding rules: Basic Encoding
+ Rules (BER), Canonical Encoding Rules (CER), and
+ Distinguished Encoding Rules (DER)", X.690(1997) (also
+ ISO/IEC 8825-1:1998).
+
+ [RFC2849] Good, G., "The LDAP Data Interchange Format (LDIF) -
+ Technical Specification", RFC 2849, June 2000.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 11]
+
+RFC 4514 LDAP: Distinguished Names June 2006
+
+
+Appendix A. Presentation Issues
+
+ This appendix is provided for informational purposes only; it is not
+ a normative part of this specification.
+
+ The string representation described in this document is not intended
+ to be presented to humans without translation. However, at times it
+ may be desirable to present non-translated DN strings to users. This
+ section discusses presentation issues associated with non-translated
+ DN strings. Issues with presentation of translated DN strings are
+ not discussed in this appendix. Transcoding issues are also not
+ discussed in this appendix.
+
+ This appendix provides guidance for applications presenting DN
+ strings to users. This section is not comprehensive; it does not
+ discuss all presentation issues that implementers may face.
+
+ Not all user interfaces are capable of displaying the full set of
+ Unicode characters. Some Unicode characters are not displayable.
+
+ It is recommended that human interfaces use the optional hex pair
+ escaping mechanism (Section 2.3) to produce a string representation
+ suitable for display to the user. For example, an application can
+ generate a DN string for display that escapes all non-printable
+ characters appearing in the AttributeValue's string representation
+ (as demonstrated in the final example of Section 4).
+
+ When a DN string is displayed in free-form text, it is often
+ necessary to distinguish the DN string from surrounding text. While
+ this is often done with whitespace (as demonstrated in Section 4), it
+ is noted that DN strings may end with whitespace. Careful readers of
+ Section 3 will note that the characters '<' (U+003C) and '>' (U+003E)
+ may only appear in the DN string if escaped. These characters are
+ intended to be used in free-form text to distinguish a DN string from
+ surrounding text. For example, <CN=Sam\ > distinguishes the string
+ representation of the DN composed of one RDN consisting of the AVA
+ (the commonName (CN) value 'Sam ') from the surrounding text. It
+ should be noted to the user that the wrapping '<' and '>' characters
+ are not part of the DN string.
+
+ DN strings can be quite long. It is often desirable to line-wrap
+ overly long DN strings in presentations. Line wrapping should be
+ done by inserting whitespace after the RDN separator character or, if
+ necessary, after the AVA separator character. It should be noted to
+ the user that the inserted whitespace is not part of the DN string
+ and is to be removed before use in LDAP. For example, the following
+ DN string is long:
+
+
+
+
+Zeilenga Standards Track [Page 12]
+
+RFC 4514 LDAP: Distinguished Names June 2006
+
+
+ CN=Kurt D. Zeilenga,OU=Engineering,L=Redwood Shores,
+ O=OpenLDAP Foundation,ST=California,C=US
+
+ So it has been line-wrapped for readability. The extra whitespace is
+ to be removed before the DN string is used in LDAP.
+
+ Inserting whitespace is not advised because it may not be obvious to
+ the user which whitespace is part of the DN string and which
+ whitespace was added for readability.
+
+ Another alternative is to use the LDAP Data Interchange Format (LDIF)
+ [RFC2849]. For example:
+
+ # This entry has a long DN...
+ dn: CN=Kurt D. Zeilenga,OU=Engineering,L=Redwood Shores,
+ O=OpenLDAP Foundation,ST=California,C=US
+ CN: Kurt D. Zeilenga
+ SN: Zeilenga
+ objectClass: person
+
+Appendix B. Changes Made since RFC 2253
+
+ This appendix is provided for informational purposes only, it is not
+ a normative part of this specification.
+
+ The following substantive changes were made to RFC 2253:
+
+ - Removed IESG Note. The IESG Note has been addressed.
+ - Replaced all references to ISO 10646-1 with [Unicode].
+ - Clarified (in Section 1) that this document does not define a
+ canonical string representation.
+ - Clarified that Section 2 describes the RECOMMENDED encoding
+ algorithm and that alternative algorithms are allowed. Some
+ encoding options described in RFC 2253 are now treated as
+ alternative algorithms in this specification.
+ - Revised specification (in Section 2) to allow short names of any
+ registered attribute type to appear in string representations of
+ DNs instead of being restricted to a "published table". Removed
+ "as an example" language. Added statement (in Section 3)
+ allowing recognition of additional names but require recognition
+ of those names in the published table. The table now appears in
+ Section 3.
+ - Removed specification of additional requirements for LDAPv2
+ implementations which also support LDAPv3 (RFC 2253, Section 4)
+ as LDAPv2 is now Historic.
+ - Allowed recognition of alternative string representations.
+ - Updated Section 2.4 to allow hex pair escaping of all characters
+ and clarified escaping for when multiple octet UTF-8 encodings
+
+
+
+Zeilenga Standards Track [Page 13]
+
+RFC 4514 LDAP: Distinguished Names June 2006
+
+
+ are present. Indicated that null (U+0000) character is to be
+ escaped. Indicated that equals sign ('=' U+003D) character may
+ be escaped as '\='.
+ - Rewrote Section 3 to use ABNF as defined in RFC 4234.
+ - Updated the Section 3 ABNF. Changes include:
+ + allowed AttributeType short names of length 1 (e.g., 'L'),
+ + used more restrictive <oid> production in AttributeTypes,
+ + did not require escaping of equals sign ('=' U+003D)
+ characters,
+ + did not require escaping of non-leading number sign ('#'
+ U+0023) characters,
+ + allowed space (' ' U+0020) to be escaped as '\ ',
+ + required hex escaping of null (U+0000) characters, and
+ + removed LDAPv2-only constructs.
+ - Updated Section 3 to describe how to parse elements of the
+ grammar.
+ - Rewrote examples.
+ - Added reference to documentations containing general LDAP
+ security considerations.
+ - Added discussion of presentation issues (Appendix A).
+ - Added this appendix.
+
+ In addition, numerous editorial changes were made.
+
+Editor's Address
+
+ Kurt D. Zeilenga
+ OpenLDAP Foundation
+
+ EMail: Kurt@OpenLDAP.org
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 14]
+
+RFC 4514 LDAP: Distinguished Names June 2006
+
+
+Full Copyright Statement
+
+ Copyright (C) The Internet Society (2006).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
+ ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
+ INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
+ INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be
+ found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at
+ ietf-ipr@ietf.org.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is provided by the IETF
+ Administrative Support Activity (IASA).
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 15]
+
diff --git a/source4/ldap_server/devdocs/rfc4515.txt b/source4/ldap_server/devdocs/rfc4515.txt
new file mode 100644
index 0000000000..86f03ebcd3
--- /dev/null
+++ b/source4/ldap_server/devdocs/rfc4515.txt
@@ -0,0 +1,675 @@
+
+
+
+
+
+
+Network Working Group M. Smith, Ed.
+Request for Comments: 4515 Pearl Crescent, LLC
+Obsoletes: 2254 T. Howes
+Category: Standards Track Opsware, Inc.
+ June 2006
+
+
+ Lightweight Directory Access Protocol (LDAP):
+ String Representation of Search Filters
+
+Status of This Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2006).
+
+Abstract
+
+ Lightweight Directory Access Protocol (LDAP) search filters are
+ transmitted in the LDAP protocol using a binary representation that
+ is appropriate for use on the network. This document defines a
+ human-readable string representation of LDAP search filters that is
+ appropriate for use in LDAP URLs (RFC 4516) and in other
+ applications.
+
+Table of Contents
+
+ 1. Introduction ....................................................2
+ 2. LDAP Search Filter Definition ...................................2
+ 3. String Search Filter Definition .................................3
+ 4. Examples ........................................................5
+ 5. Security Considerations .........................................7
+ 6. Normative References ............................................7
+ 7. Informative References ..........................................8
+ 8. Acknowledgements ................................................8
+ Appendix A: Changes Since RFC 2254 .................................9
+ A.1. Technical Changes ..........................................9
+ A.2. Editorial Changes ..........................................9
+
+
+
+
+
+
+
+Smith and Howes Standards Track [Page 1]
+
+RFC 4515 LDAP: String Representation of Search Filters June 2006
+
+
+1. Introduction
+
+ The Lightweight Directory Access Protocol (LDAP) [RFC4510] defines a
+ network representation of a search filter transmitted to an LDAP
+ server. Some applications may find it useful to have a common way of
+ representing these search filters in a human-readable form; LDAP URLs
+ [RFC4516] are an example of one such application. This document
+ defines a human-readable string format for representing the full
+ range of possible LDAP version 3 search filters, including extended
+ match filters.
+
+ This document is a integral part of the LDAP technical specification
+ [RFC4510], which obsoletes the previously defined LDAP technical
+ specification, RFC 3377, in its entirety.
+
+ This document replaces RFC 2254. Changes to RFC 2254 are summarized
+ in Appendix A.
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in BCP 14 [RFC2119].
+
+2. LDAP Search Filter Definition
+
+ An LDAP search filter is defined in Section 4.5.1 of [RFC4511] as
+ follows:
+
+ Filter ::= CHOICE {
+ and [0] SET SIZE (1..MAX) OF filter Filter,
+ or [1] SET SIZE (1..MAX) OF filter Filter,
+ not [2] Filter,
+ equalityMatch [3] AttributeValueAssertion,
+ substrings [4] SubstringFilter,
+ greaterOrEqual [5] AttributeValueAssertion,
+ lessOrEqual [6] AttributeValueAssertion,
+ present [7] AttributeDescription,
+ approxMatch [8] AttributeValueAssertion,
+ extensibleMatch [9] MatchingRuleAssertion }
+
+ SubstringFilter ::= SEQUENCE {
+ type AttributeDescription,
+ -- initial and final can occur at most once
+ substrings SEQUENCE SIZE (1..MAX) OF substring CHOICE {
+ initial [0] AssertionValue,
+ any [1] AssertionValue,
+ final [2] AssertionValue } }
+
+
+
+
+
+Smith and Howes Standards Track [Page 2]
+
+RFC 4515 LDAP: String Representation of Search Filters June 2006
+
+
+ AttributeValueAssertion ::= SEQUENCE {
+ attributeDesc AttributeDescription,
+ assertionValue AssertionValue }
+
+ MatchingRuleAssertion ::= SEQUENCE {
+ matchingRule [1] MatchingRuleId OPTIONAL,
+ type [2] AttributeDescription OPTIONAL,
+ matchValue [3] AssertionValue,
+ dnAttributes [4] BOOLEAN DEFAULT FALSE }
+
+ AttributeDescription ::= LDAPString
+ -- Constrained to <attributedescription>
+ -- [RFC4512]
+
+ AttributeValue ::= OCTET STRING
+
+ MatchingRuleId ::= LDAPString
+
+ AssertionValue ::= OCTET STRING
+
+ LDAPString ::= OCTET STRING -- UTF-8 encoded,
+ -- [Unicode] characters
+
+ The AttributeDescription, as defined in [RFC4511], is a string
+ representation of the attribute description that is discussed in
+ [RFC4512]. The AttributeValue and AssertionValue OCTET STRING have
+ the form defined in [RFC4517]. The Filter is encoded for
+ transmission over a network using the Basic Encoding Rules (BER)
+ defined in [X.690], with simplifications described in [RFC4511].
+
+3. String Search Filter Definition
+
+ The string representation of an LDAP search filter is a string of
+ UTF-8 [RFC3629] encoded Unicode characters [Unicode] that is defined
+ by the following grammar, following the ABNF notation defined in
+ [RFC4234]. The productions used that are not defined here are
+ defined in Section 1.4 (Common ABNF Productions) of [RFC4512] unless
+ otherwise noted. The filter format uses a prefix notation.
+
+ filter = LPAREN filtercomp RPAREN
+ filtercomp = and / or / not / item
+ and = AMPERSAND filterlist
+ or = VERTBAR filterlist
+ not = EXCLAMATION filter
+ filterlist = 1*filter
+ item = simple / present / substring / extensible
+ simple = attr filtertype assertionvalue
+ filtertype = equal / approx / greaterorequal / lessorequal
+
+
+
+Smith and Howes Standards Track [Page 3]
+
+RFC 4515 LDAP: String Representation of Search Filters June 2006
+
+
+ equal = EQUALS
+ approx = TILDE EQUALS
+ greaterorequal = RANGLE EQUALS
+ lessorequal = LANGLE EQUALS
+ extensible = ( attr [dnattrs]
+ [matchingrule] COLON EQUALS assertionvalue )
+ / ( [dnattrs]
+ matchingrule COLON EQUALS assertionvalue )
+ present = attr EQUALS ASTERISK
+ substring = attr EQUALS [initial] any [final]
+ initial = assertionvalue
+ any = ASTERISK *(assertionvalue ASTERISK)
+ final = assertionvalue
+ attr = attributedescription
+ ; The attributedescription rule is defined in
+ ; Section 2.5 of [RFC4512].
+ dnattrs = COLON "dn"
+ matchingrule = COLON oid
+ assertionvalue = valueencoding
+ ; The <valueencoding> rule is used to encode an <AssertionValue>
+ ; from Section 4.1.6 of [RFC4511].
+ valueencoding = 0*(normal / escaped)
+ normal = UTF1SUBSET / UTFMB
+ escaped = ESC HEX HEX
+ UTF1SUBSET = %x01-27 / %x2B-5B / %x5D-7F
+ ; UTF1SUBSET excludes 0x00 (NUL), LPAREN,
+ ; RPAREN, ASTERISK, and ESC.
+ EXCLAMATION = %x21 ; exclamation mark ("!")
+ AMPERSAND = %x26 ; ampersand (or AND symbol) ("&")
+ ASTERISK = %x2A ; asterisk ("*")
+ COLON = %x3A ; colon (":")
+ VERTBAR = %x7C ; vertical bar (or pipe) ("|")
+ TILDE = %x7E ; tilde ("~")
+
+ Note that although both the <substring> and <present> productions in
+ the grammar above can produce the "attr=*" construct, this construct
+ is used only to denote a presence filter.
+
+ The <valueencoding> rule ensures that the entire filter string is a
+ valid UTF-8 string and provides that the octets that represent the
+ ASCII characters "*" (ASCII 0x2a), "(" (ASCII 0x28), ")" (ASCII
+ 0x29), "\" (ASCII 0x5c), and NUL (ASCII 0x00) are represented as a
+ backslash "\" (ASCII 0x5c) followed by the two hexadecimal digits
+ representing the value of the encoded octet.
+
+
+
+
+
+
+
+Smith and Howes Standards Track [Page 4]
+
+RFC 4515 LDAP: String Representation of Search Filters June 2006
+
+
+ This simple escaping mechanism eliminates filter-parsing ambiguities
+ and allows any filter that can be represented in LDAP to be
+ represented as a NUL-terminated string. Other octets that are part
+ of the <normal> set may be escaped using this mechanism, for example,
+ non-printing ASCII characters.
+
+ For AssertionValues that contain UTF-8 character data, each octet of
+ the character to be escaped is replaced by a backslash and two hex
+ digits, which form a single octet in the code of the character. For
+ example, the filter checking whether the "cn" attribute contained a
+ value with the character "*" anywhere in it would be represented as
+ "(cn=*\2a*)".
+
+ As indicated by the <valueencoding> rule, implementations MUST escape
+ all octets greater than 0x7F that are not part of a valid UTF-8
+ encoding sequence when they generate a string representation of a
+ search filter. Implementations SHOULD accept as input strings that
+ are not valid UTF-8 strings. This is necessary because RFC 2254 did
+ not clearly define the term "string representation" (and in
+ particular did not mention that the string representation of an LDAP
+ search filter is a string of UTF-8-encoded Unicode characters).
+
+4. Examples
+
+ This section gives a few examples of search filters written using
+ this notation.
+
+ (cn=Babs Jensen)
+ (!(cn=Tim Howes))
+ (&(objectClass=Person)(|(sn=Jensen)(cn=Babs J*)))
+ (o=univ*of*mich*)
+ (seeAlso=)
+
+ The following examples illustrate the use of extensible matching.
+
+ (cn:caseExactMatch:=Fred Flintstone)
+ (cn:=Betty Rubble)
+ (sn:dn:2.4.6.8.10:=Barney Rubble)
+ (o:dn:=Ace Industry)
+ (:1.2.3:=Wilma Flintstone)
+ (:DN:2.4.6.8.10:=Dino)
+
+ The first example shows use of the matching rule "caseExactMatch."
+
+ The second example demonstrates use of a MatchingRuleAssertion form
+ without a matchingRule.
+
+
+
+
+
+Smith and Howes Standards Track [Page 5]
+
+RFC 4515 LDAP: String Representation of Search Filters June 2006
+
+
+ The third example illustrates the use of the ":oid" notation to
+ indicate that the matching rule identified by the OID "2.4.6.8.10"
+ should be used when making comparisons, and that the attributes of an
+ entry's distinguished name should be considered part of the entry
+ when evaluating the match (indicated by the use of ":dn").
+
+ The fourth example denotes an equality match, except that DN
+ components should be considered part of the entry when doing the
+ match.
+
+ The fifth example is a filter that should be applied to any attribute
+ supporting the matching rule given (since the <attr> has been
+ omitted).
+
+ The sixth and final example is also a filter that should be applied
+ to any attribute supporting the matching rule given. Attributes
+ supporting the matching rule contained in the DN should also be
+ considered.
+
+ The following examples illustrate the use of the escaping mechanism.
+
+ (o=Parens R Us \28for all your parenthetical needs\29)
+ (cn=*\2A*)
+ (filename=C:\5cMyFile)
+ (bin=\00\00\00\04)
+ (sn=Lu\c4\8di\c4\87)
+ (1.3.6.1.4.1.1466.0=\04\02\48\69)
+
+ The first example shows the use of the escaping mechanism to
+ represent parenthesis characters. The second shows how to represent
+ a "*" in an assertion value, preventing it from being interpreted as
+ a substring indicator. The third illustrates the escaping of the
+ backslash character.
+
+ The fourth example shows a filter searching for the four-octet value
+ 00 00 00 04 (hex), illustrating the use of the escaping mechanism to
+ represent arbitrary data, including NUL characters.
+
+ The fifth example illustrates the use of the escaping mechanism to
+ represent various non-ASCII UTF-8 characters. Specifically, there
+ are 5 characters in the <assertionvalue> portion of this example:
+ LATIN CAPITAL LETTER L (U+004C), LATIN SMALL LETTER U (U+0075), LATIN
+ SMALL LETTER C WITH CARON (U+010D), LATIN SMALL LETTER I (U+0069),
+ and LATIN SMALL LETTER C WITH ACUTE (U+0107).
+
+ The sixth and final example demonstrates assertion of a BER-encoded
+ value.
+
+
+
+
+Smith and Howes Standards Track [Page 6]
+
+RFC 4515 LDAP: String Representation of Search Filters June 2006
+
+
+5. Security Considerations
+
+ This memo describes a string representation of LDAP search filters.
+ While the representation itself has no known security implications,
+ LDAP search filters do. They are interpreted by LDAP servers to
+ select entries from which data is retrieved. LDAP servers should
+ take care to protect the data they maintain from unauthorized access.
+
+ Please refer to the Security Considerations sections of [RFC4511] and
+ [RFC4513] for more information.
+
+6. Normative References
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [RFC3629] Yergeau, F., "UTF-8, a transformation format of ISO
+ 10646", STD 63, RFC 3629, November 2003.
+
+ [RFC4234] Crocker, D. and P. Overell, "Augmented BNF for Syntax
+ Specifications: ABNF", RFC 4234, October 2005.
+
+ [RFC4510] Zeilenga, K., Ed., "Lightweight Directory Access Protocol
+ (LDAP): Technical Specification Road Map", RFC 4510, June
+ 2006.
+
+ [RFC4511] Sermersheim, J., Ed., "Lightweight Directory Access
+ Protocol (LDAP): The Protocol", RFC 4511, June 2006.
+
+ [RFC4512] Zeilenga, K., "Lightweight Directory Access Protocol
+ (LDAP): Directory Information Models", RFC 4512, June
+ 2006.
+
+ [RFC4513] Harrison, R., Ed., "Lightweight Directory Access Protocol
+ (LDAP): Authentication Methods and Security Mechanisms",
+ RFC 4513, June 2006.
+
+ [RFC4517] Legg, S., Ed., "Lightweight Directory Access Protocol
+ (LDAP): Syntaxes and Matching Rules", RFC 4517, June
+ 2006.
+
+ [Unicode] The Unicode Consortium, "The Unicode Standard, Version
+ 3.2.0" is defined by "The Unicode Standard, Version 3.0"
+ (Reading, MA, Addison-Wesley, 2000. ISBN 0-201-61633-5),
+ as amended by the "Unicode Standard Annex #27: Unicode
+ 3.1" (http://www.unicode.org/reports/tr27/) and by the
+ "Unicode Standard Annex #28: Unicode 3.2."
+
+
+
+
+Smith and Howes Standards Track [Page 7]
+
+RFC 4515 LDAP: String Representation of Search Filters June 2006
+
+
+7. Informative References
+
+ [RFC4516] Smith, M., Ed. and T. Howes, "Lightweight Directory
+ Access Protocol (LDAP): Uniform Resource Locator", RFC
+ 4516, June 2006.
+
+ [X.690] Specification of ASN.1 encoding rules: Basic, Canonical,
+ and Distinguished Encoding Rules, ITU-T Recommendation
+ X.690, 1994.
+
+8. Acknowledgements
+
+ This document replaces RFC 2254 by Tim Howes. RFC 2254 was a product
+ of the IETF ASID Working Group.
+
+ Changes included in this revised specification are based upon
+ discussions among the authors, discussions within the LDAP (v3)
+ Revision Working Group (ldapbis), and discussions within other IETF
+ Working Groups. The contributions of individuals in these working
+ groups is gratefully acknowledged.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Smith and Howes Standards Track [Page 8]
+
+RFC 4515 LDAP: String Representation of Search Filters June 2006
+
+
+Appendix A: Changes Since RFC 2254
+
+A.1. Technical Changes
+
+ Replaced [ISO 10646] reference with [Unicode].
+
+ The following technical changes were made to the contents of the
+ "String Search Filter Definition" section:
+
+ Added statement that the string representation is a string of UTF-8-
+ encoded Unicode characters.
+
+ Revised all of the ABNF to use common productions from [RFC4512].
+
+ Replaced the "value" rule with a new "assertionvalue" rule within the
+ "simple", "extensible", and "substring" ("initial", "any", and
+ "final") rules. This matches a change made in [RFC4517].
+
+ Added "(" and ")" around the components of the <extensible>
+ subproductions for clarity.
+
+ Revised the "attr", "matchingrule", and "assertionvalue" ABNF to more
+ precisely reference productions from the [RFC4512] and [RFC4511]
+ documents.
+
+ "String Search Filter Definition" section: replaced "greater" and
+ "less" with "greaterorequal" and "lessorequal" to avoid confusion.
+
+ Introduced the "valueencoding" and associated "normal" and "escaped"
+ rules to reduce the dependence on descriptive text. The "normal"
+ production restricts filter strings to valid UTF-8 sequences.
+
+ Added a statement about expected behavior in light of RFC 2254's lack
+ of a clear definition of "string representation."
+
+A.2. Editorial Changes
+
+ Changed document title to include "LDAP:" prefix.
+
+ IESG Note: removed note about lack of satisfactory mandatory
+ authentication mechanisms.
+
+ Header and "Authors' Addresses" sections: added Mark Smith as the
+ document editor and updated affiliation and contact information.
+
+ "Table of Contents" and "Intellectual Property" sections: added.
+
+ Copyright: updated per latest IETF guidelines.
+
+
+
+Smith and Howes Standards Track [Page 9]
+
+RFC 4515 LDAP: String Representation of Search Filters June 2006
+
+
+ "Abstract" section: separated from introductory material.
+
+ "Introduction" section: new section; separated from the Abstract.
+ Updated second paragraph to indicate that RFC 2254 is replaced by
+ this document (instead of RFC 1960). Added reference to the
+ [RFC4510] document.
+
+ "LDAP Search Filter Definition" section: made corrections to the LDAP
+ search filter ABNF so it matches that used in [RFC4511].
+
+ Clarified the definition of 'value' (now 'assertionvalue') to take
+ into account the fact that it is not precisely an AttributeAssertion
+ from [RFC4511] Section 4.1.6 (special handling is required for some
+ characters). Added a note that each octet of a character to be
+ escaped is replaced by a backslash and two hex digits, which
+ represent a single octet.
+
+ "Examples" section: added four additional examples: (seeAlso=),
+ (cn:=Betty Rubble), (:1.2.3:=Wilma Flintstone), and
+ (1.3.6.1.4.1.1466.0=\04\02\48\69). Replaced one occurrence of "a
+ value" with "an assertion value". Corrected the description of this
+ example: (sn:dn:2.4.6.8.10:=Barney Rubble). Replaced the numeric OID
+ in the first extensible match example with "caseExactMatch" to
+ demonstrate use of the descriptive form. Used "DN" (uppercase) in
+ the last extensible match example to remind the reader to treat the
+ <dnattrs> production as case insensitive. Reworded the description
+ of the fourth escaping mechanism example to avoid making assumptions
+ about byte order. Added text to the fifth escaping mechanism example
+ to spell out what the non-ASCII characters are in Unicode terms.
+
+ "Security Considerations" section: added references to [RFC4511] and
+ [RFC4513].
+
+ "Normative References" section: renamed from "References" per new RFC
+ guidelines. Changed from [1] style to [RFC4511] style throughout the
+ document. Added entries for [Unicode], [RFC2119], [RFC4513],
+ [RFC4512], and [RFC4510] and updated the UTF-8 reference. Replaced
+ RFC 822 reference with a reference to RFC 4234.
+
+ "Informative References" section: (new section) moved [X.690] to this
+ section. Added a reference to [RFC4516].
+
+ "Acknowledgements" section: added.
+
+ "Appendix A: Changes Since RFC 2254" section: added.
+
+ Surrounded the names of all ABNF productions with "<" and ">" where
+ they are used in descriptive text.
+
+
+
+Smith and Howes Standards Track [Page 10]
+
+RFC 4515 LDAP: String Representation of Search Filters June 2006
+
+
+ Replaced all occurrences of "LDAPv3" with "LDAP."
+
+Authors' Addresses
+
+ Mark Smith, Editor
+ Pearl Crescent, LLC
+ 447 Marlpool Dr.
+ Saline, MI 48176
+ USA
+
+ Phone: +1 734 944-2856
+ EMail: mcs@pearlcrescent.com
+
+
+ Tim Howes
+ Opsware, Inc.
+ 599 N. Mathilda Ave.
+ Sunnyvale, CA 94085
+ USA
+
+ Phone: +1 408 744-7509
+ EMail: howes@opsware.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Smith and Howes Standards Track [Page 11]
+
+RFC 4515 LDAP: String Representation of Search Filters June 2006
+
+
+Full Copyright Statement
+
+ Copyright (C) The Internet Society (2006).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
+ ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
+ INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
+ INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be
+ found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at
+ ietf-ipr@ietf.org.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is provided by the IETF
+ Administrative Support Activity (IASA).
+
+
+
+
+
+
+
+Smith and Howes Standards Track [Page 12]
+
diff --git a/source4/ldap_server/devdocs/rfc4516.txt b/source4/ldap_server/devdocs/rfc4516.txt
new file mode 100644
index 0000000000..6de1e1d08a
--- /dev/null
+++ b/source4/ldap_server/devdocs/rfc4516.txt
@@ -0,0 +1,843 @@
+
+
+
+
+
+
+Network Working Group M. Smith, Ed.
+Request for Comments: 4516 Pearl Crescent, LLC
+Obsoletes: 2255 T. Howes
+Category: Standards Track Opsware, Inc.
+ June 2006
+
+
+ Lightweight Directory Access Protocol (LDAP):
+ Uniform Resource Locator
+
+Status of This Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2006).
+
+Abstract
+
+ This document describes a format for a Lightweight Directory Access
+ Protocol (LDAP) Uniform Resource Locator (URL). An LDAP URL
+ describes an LDAP search operation that is used to retrieve
+ information from an LDAP directory, or, in the context of an LDAP
+ referral or reference, an LDAP URL describes a service where an LDAP
+ operation may be progressed.
+
+Table of Contents
+
+ 1. Introduction ....................................................2
+ 2. URL Definition ..................................................2
+ 2.1. Percent-Encoding ...........................................4
+ 3. Defaults for Fields of the LDAP URL .............................5
+ 4. Examples ........................................................6
+ 5. Security Considerations .........................................8
+ 6. Normative References ............................................9
+ 7. Informative References .........................................10
+ 8. Acknowledgements ...............................................10
+ Appendix A: Changes Since RFC 2255 ................................11
+ A.1. Technical Changes .........................................11
+ A.2. Editorial Changes .........................................11
+
+
+
+
+
+
+Smith & Howes Standards Track [Page 1]
+
+RFC 4516 LDAP: Uniform Resource Locator June 2006
+
+
+1. Introduction
+
+ LDAP is the Lightweight Directory Access Protocol [RFC4510]. This
+ document specifies the LDAP URL format for version 3 of LDAP and
+ clarifies how LDAP URLs are resolved. This document also defines an
+ extension mechanism for LDAP URLs. This mechanism may be used to
+ provide access to new LDAP extensions.
+
+ Note that not all the parameters of the LDAP search operation
+ described in [RFC4511] can be expressed using the format defined in
+ this document. Note also that URLs may be used to represent
+ reference knowledge, including that for non-search operations.
+
+ This document is an integral part of the LDAP technical specification
+ [RFC4510], which obsoletes the previously defined LDAP technical
+ specification, RFC 3377, in its entirety.
+
+ This document replaces RFC 2255. See Appendix A for a list of
+ changes relative to RFC 2255.
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in BCP 14 [RFC2119].
+
+2. URL Definition
+
+ An LDAP URL begins with the protocol prefix "ldap" and is defined by
+ the following grammar, following the ABNF notation defined in
+ [RFC4234].
+
+ ldapurl = scheme COLON SLASH SLASH [host [COLON port]]
+ [SLASH dn [QUESTION [attributes]
+ [QUESTION [scope] [QUESTION [filter]
+ [QUESTION extensions]]]]]
+ ; <host> and <port> are defined
+ ; in Sections 3.2.2 and 3.2.3
+ ; of [RFC3986].
+ ; <filter> is from Section 3 of
+ ; [RFC4515], subject to the
+ ; provisions of the
+ ; "Percent-Encoding" section
+ ; below.
+
+ scheme = "ldap"
+
+
+
+
+
+
+
+Smith & Howes Standards Track [Page 2]
+
+RFC 4516 LDAP: Uniform Resource Locator June 2006
+
+
+ dn = distinguishedName ; From Section 3 of [RFC4514],
+ ; subject to the provisions of
+ ; the "Percent-Encoding"
+ ; section below.
+
+ attributes = attrdesc *(COMMA attrdesc)
+ attrdesc = selector *(COMMA selector)
+ selector = attributeSelector ; From Section 4.5.1 of
+ ; [RFC4511], subject to the
+ ; provisions of the
+ ; "Percent-Encoding" section
+ ; below.
+
+ scope = "base" / "one" / "sub"
+ extensions = extension *(COMMA extension)
+ extension = [EXCLAMATION] extype [EQUALS exvalue]
+ extype = oid ; From section 1.4 of [RFC4512].
+
+ exvalue = LDAPString ; From section 4.1.2 of
+ ; [RFC4511], subject to the
+ ; provisions of the
+ ; "Percent-Encoding" section
+ ; below.
+
+ EXCLAMATION = %x21 ; exclamation mark ("!")
+ SLASH = %x2F ; forward slash ("/")
+ COLON = %x3A ; colon (":")
+ QUESTION = %x3F ; question mark ("?")
+
+ The "ldap" prefix indicates an entry or entries accessible from the
+ LDAP server running on the given hostname at the given portnumber.
+ Note that the <host> may contain literal IPv6 addresses as specified
+ in Section 3.2.2 of [RFC3986].
+
+ The <dn> is an LDAP Distinguished Name using the string format
+ described in [RFC4514]. It identifies the base object of the LDAP
+ search or the target of a non-search operation.
+
+ The <attributes> construct is used to indicate which attributes
+ should be returned from the entry or entries.
+
+ The <scope> construct is used to specify the scope of the search to
+ perform in the given LDAP server. The allowable scopes are "base"
+ for a base object search, "one" for a one-level search, or "sub" for
+ a subtree search.
+
+
+
+
+
+
+Smith & Howes Standards Track [Page 3]
+
+RFC 4516 LDAP: Uniform Resource Locator June 2006
+
+
+ The <filter> is used to specify the search filter to apply to entries
+ within the specified scope during the search. It has the format
+ specified in [RFC4515].
+
+ The <extensions> construct provides the LDAP URL with an
+ extensibility mechanism, allowing the capabilities of the URL to be
+ extended in the future. Extensions are a simple comma-separated list
+ of type=value pairs, where the =value portion MAY be omitted for
+ options not requiring it. Each type=value pair is a separate
+ extension. These LDAP URL extensions are not necessarily related to
+ any of the LDAP extension mechanisms. Extensions may be supported or
+ unsupported by the client resolving the URL. An extension prefixed
+ with a '!' character (ASCII 0x21) is critical. An extension not
+ prefixed with a '!' character is non-critical.
+
+ If an LDAP URL extension is implemented (that is, if the
+ implementation understands it and is able to use it), the
+ implementation MUST make use of it. If an extension is not
+ implemented and is marked critical, the implementation MUST NOT
+ process the URL. If an extension is not implemented and is not
+ marked critical, the implementation MUST ignore the extension.
+
+ The extension type (<extype>) MAY be specified using the numeric OID
+ <numericoid> form (e.g., 1.2.3.4) or the descriptor <descr> form
+ (e.g., myLDAPURLExtension). Use of the <descr> form SHOULD be
+ restricted to registered object identifier descriptive names. See
+ [RFC4520] for registration details and usage guidelines for
+ descriptive names.
+
+ No LDAP URL extensions are defined in this document. Other documents
+ or a future version of this document MAY define one or more
+ extensions.
+
+2.1. Percent-Encoding
+
+ A generated LDAP URL MUST consist only of the restricted set of
+ characters included in one of the following three productions defined
+ in [RFC3986]:
+
+ <reserved>
+ <unreserved>
+ <pct-encoded>
+
+ Implementations SHOULD accept other valid UTF-8 strings [RFC3629] as
+ input. An octet MUST be encoded using the percent-encoding mechanism
+ described in section 2.1 of [RFC3986] in any of these situations:
+
+
+
+
+
+Smith & Howes Standards Track [Page 4]
+
+RFC 4516 LDAP: Uniform Resource Locator June 2006
+
+
+ The octet is not in the reserved set defined in section 2.2 of
+ [RFC3986] or in the unreserved set defined in section 2.3 of
+ [RFC3986].
+
+ It is the single Reserved character '?' and occurs inside a <dn>,
+ <filter>, or other element of an LDAP URL.
+
+ It is a comma character ',' that occurs inside an <exvalue>.
+
+ Note that before the percent-encoding mechanism is applied, the
+ extensions component of the LDAP URL may contain one or more null
+ (zero) bytes. No other component may.
+
+3. Defaults for Fields of the LDAP URL
+
+ Some fields of the LDAP URL are optional, as described above. In the
+ absence of any other specification, the following general defaults
+ SHOULD be used when a field is absent. Note that other documents MAY
+ specify different defaulting rules; for example, section 4.1.10 of
+ [RFC4511] specifies a different rule for determining the correct DN
+ to use when it is absent in an LDAP URL that is returned as a
+ referral.
+
+ <host>
+ If no <host> is given, the client must have some a priori
+ knowledge of an appropriate LDAP server to contact.
+
+ <port>
+ The default LDAP port is TCP port 389.
+
+ <dn>
+ If no <dn> is given, the default is the zero-length DN, "".
+
+ <attributes>
+ If the <attributes> part is omitted, all user attributes of the
+ entry or entries should be requested (e.g., by setting the
+ attributes field AttributeDescriptionList in the LDAP search
+ request to a NULL list, or by using the special <alluserattrs>
+ selector "*").
+
+ <scope>
+ If <scope> is omitted, a <scope> of "base" is assumed.
+
+ <filter>
+ If <filter> is omitted, a filter of "(objectClass=*)" is assumed.
+
+ <extensions>
+ If <extensions> is omitted, no extensions are assumed.
+
+
+
+Smith & Howes Standards Track [Page 5]
+
+RFC 4516 LDAP: Uniform Resource Locator June 2006
+
+
+4. Examples
+
+ The following are some example LDAP URLs that use the format defined
+ above. The first example is an LDAP URL referring to the University
+ of Michigan entry, available from an LDAP server of the client's
+ choosing:
+
+ ldap:///o=University%20of%20Michigan,c=US
+
+ The next example is an LDAP URL referring to the University of
+ Michigan entry in a particular ldap server:
+
+ ldap://ldap1.example.net/o=University%20of%20Michigan,c=US
+
+ Both of these URLs correspond to a base object search of the
+ "o=University of Michigan,c=US" entry using a filter of
+ "(objectclass=*)", requesting all attributes.
+
+ The next example is an LDAP URL referring to only the postalAddress
+ attribute of the University of Michigan entry:
+
+ ldap://ldap1.example.net/o=University%20of%20Michigan,
+ c=US?postalAddress
+
+ The corresponding LDAP search operation is the same as in the
+ previous example, except that only the postalAddress attribute is
+ requested.
+
+ The next example is an LDAP URL referring to the set of entries found
+ by querying the given LDAP server on port 6666 and doing a subtree
+ search of the University of Michigan for any entry with a common name
+ of "Babs Jensen", retrieving all attributes:
+
+ ldap://ldap1.example.net:6666/o=University%20of%20Michigan,
+ c=US??sub?(cn=Babs%20Jensen)
+
+ The next example is an LDAP URL referring to all children of the c=GB
+ entry:
+
+ LDAP://ldap1.example.com/c=GB?objectClass?ONE
+
+ The objectClass attribute is requested to be returned along with the
+ entries, and the default filter of "(objectclass=*)" is used.
+
+ The next example is an LDAP URL to retrieve the mail attribute for
+ the LDAP entry named "o=Question?,c=US", illustrating the use of the
+ percent-encoding mechanism on the reserved character '?'.
+
+
+
+
+Smith & Howes Standards Track [Page 6]
+
+RFC 4516 LDAP: Uniform Resource Locator June 2006
+
+
+ ldap://ldap2.example.com/o=Question%3f,c=US?mail
+
+ The next example (which is broken into two lines for readability)
+ illustrates the interaction between the LDAP string representation of
+ the filters-quoting mechanism and the URL-quoting mechanisms.
+
+ ldap://ldap3.example.com/o=Babsco,c=US
+ ???(four-octet=%5c00%5c00%5c00%5c04)
+
+ The filter in this example uses the LDAP escaping mechanism of \ to
+ encode three zero or null bytes in the value. In LDAP, the filter
+ would be written as (four-octet=\00\00\00\04). Because the \
+ character must be escaped in a URL, the \s are percent-encoded as %5c
+ (or %5C) in the URL encoding.
+
+ The next example illustrates the interaction between the LDAP string
+ representation of the DNs-quoting mechanism and URL-quoting
+ mechanisms.
+
+ ldap://ldap.example.com/o=An%20Example%5C2C%20Inc.,c=US
+
+ The DN encoded in the above URL is:
+
+ o=An Example\2C Inc.,c=US
+
+ That is, the left-most RDN value is:
+
+ An Example, Inc.
+
+ The following three URLs are equivalent, assuming that the defaulting
+ rules specified in Section 3 of this document are used:
+
+ ldap://ldap.example.net
+ ldap://ldap.example.net/
+ ldap://ldap.example.net/?
+
+ These three URLs point to the root DSE on the ldap.example.net
+ server.
+
+ The final two examples show use of a hypothetical, experimental bind
+ name extension (the value associated with the extension is an LDAP
+ DN).
+
+ ldap:///??sub??e-bindname=cn=Manager%2cdc=example%2cdc=com
+ ldap:///??sub??!e-bindname=cn=Manager%2cdc=example%2cdc=com
+
+
+
+
+
+
+Smith & Howes Standards Track [Page 7]
+
+RFC 4516 LDAP: Uniform Resource Locator June 2006
+
+
+ The two URLs are the same, except that the second one marks the
+ e-bindname extension as critical. Notice the use of the percent-
+ encoding mechanism to encode the commas within the distinguished name
+ value in the e-bindname extension.
+
+5. Security Considerations
+
+ The general URL security considerations discussed in [RFC3986] are
+ relevant for LDAP URLs.
+
+ The use of security mechanisms when processing LDAP URLs requires
+ particular care, since clients may encounter many different servers
+ via URLs, and since URLs are likely to be processed automatically,
+ without user intervention. A client SHOULD have a user-configurable
+ policy that controls which servers the client will establish LDAP
+ sessions with and with which security mechanisms, and SHOULD NOT
+ establish LDAP sessions that are inconsistent with this policy. If a
+ client chooses to reuse an existing LDAP session when resolving one
+ or more LDAP URLs, it MUST ensure that the session is compatible with
+ the URL and that no security policies are violated.
+
+ Sending authentication information, no matter the mechanism, may
+ violate a user's privacy requirements. In the absence of specific
+ policy permitting authentication information to be sent to a server,
+ a client should use an anonymous LDAP session. (Note that clients
+ conforming to previous LDAP URL specifications, where all LDAP
+ sessions are anonymous and unprotected, are consistent with this
+ specification; they simply have the default security policy.) Simply
+ opening a transport connection to another server may violate some
+ users' privacy requirements, so clients should provide the user with
+ a way to control URL processing.
+
+ Some authentication methods, in particular, reusable passwords sent
+ to the server, may reveal easily-abused information to the remote
+ server or to eavesdroppers in transit and should not be used in URL
+ processing unless they are explicitly permitted by policy.
+ Confirmation by the human user of the use of authentication
+ information is appropriate in many circumstances. Use of strong
+ authentication methods that do not reveal sensitive information is
+ much preferred. If the URL represents a referral for an update
+ operation, strong authentication methods SHOULD be used. Please
+ refer to the Security Considerations section of [RFC4513] for more
+ information.
+
+ The LDAP URL format allows the specification of an arbitrary LDAP
+ search operation to be performed when evaluating the LDAP URL.
+ Following an LDAP URL may cause unexpected results, for example, the
+ retrieval of large amounts of data or the initiation of a long-lived
+
+
+
+Smith & Howes Standards Track [Page 8]
+
+RFC 4516 LDAP: Uniform Resource Locator June 2006
+
+
+ search. The security implications of resolving an LDAP URL are the
+ same as those of resolving an LDAP search query.
+
+6. Normative References
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [RFC3629] Yergeau, F., "UTF-8, a transformation format of ISO
+ 10646", STD 63, RFC 3629, November 2003.
+
+ [RFC3986] Berners-Lee, T., Fielding, R., and L. Masinter, "Uniform
+ Resource Identifier (URI): Generic Syntax", STD 66, RFC
+ 3986, January 2005.
+
+ [RFC4234] Crocker, D. and P. Overell, "Augmented BNF for Syntax
+ Specifications: ABNF", RFC 4234, October 2005.
+
+ [RFC4510] Zeilenga, K., Ed., "Lightweight Directory Access Protocol
+ (LDAP): Technical Specification Road Map", RFC 4510, June
+ 2006.
+
+ [RFC4511] Sermersheim, J., Ed., "Lightweight Directory Access
+ Protocol (LDAP): The Protocol", RFC 4511, June 2006.
+
+ [RFC4512] Zeilenga, K., "Lightweight Directory Access Protocol
+ (LDAP): Directory Information Models", RFC 4512, June
+ 2006.
+
+ [RFC4513] Harrison, R., Ed., "Lightweight Directory Access Protocol
+ (LDAP): Authentication Methods and Security Mechanisms",
+ RFC 4513, June 2006.
+
+ [RFC4514] Zeilenga, K., Ed., "Lightweight Directory Access Protocol
+ (LDAP): String Representation of Distinguished Names", RFC
+ 4514, June 2006.
+
+ [RFC4515] Smith, M. Ed. and T. Howes, "Lightweight Directory Access
+ Protocol (LDAP): String Representation of Search Filters",
+ RFC 4515, June 2006.
+
+
+
+
+
+
+
+
+
+
+
+Smith & Howes Standards Track [Page 9]
+
+RFC 4516 LDAP: Uniform Resource Locator June 2006
+
+
+7. Informative References
+
+ [RFC2396] Berners-Lee, T., Fielding, R., and L. Masinter, "Uniform
+ Resource Identifiers (URI): Generic Syntax", RFC 2396,
+ August 1998.
+
+ [RFC4520] Zeilenga, K., "Internet Assigned Numbers Authority (IANA)
+ Considerations for the Lightweight Directory Access
+ Protocol (LDAP)", BCP 64, RFC 4520, June 2006.
+
+8. Acknowledgements
+
+ The LDAP URL format was originally defined at the University of
+ Michigan. This material is based upon work supported by the National
+ Science Foundation under Grant No. NCR-9416667. The support of both
+ the University of Michigan and the National Science Foundation is
+ gratefully acknowledged.
+
+ This document obsoletes RFC 2255 by Tim Howes and Mark Smith.
+ Changes included in this revised specification are based upon
+ discussions among the authors, discussions within the LDAP (v3)
+ Revision Working Group (ldapbis), and discussions within other IETF
+ Working Groups. The contributions of individuals in these working
+ groups is gratefully acknowledged. Several people in particular have
+ made valuable comments on this document: RL "Bob" Morgan, Mark Wahl,
+ Kurt Zeilenga, Jim Sermersheim, and Hallvard Furuseth deserve special
+ thanks for their contributions.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Smith & Howes Standards Track [Page 10]
+
+RFC 4516 LDAP: Uniform Resource Locator June 2006
+
+
+Appendix A: Changes Since RFC 2255
+
+A.1. Technical Changes
+
+ The following technical changes were made to the contents of the "URL
+ Definition" section:
+
+ Revised all of the ABNF to use common productions from [RFC4512].
+
+ Replaced references to [RFC2396] with a reference to [RFC3986] (this
+ allows literal IPv6 addresses to be used inside the <host> portion of
+ the URL, and a note was added to remind the reader of this
+ enhancement). Referencing [RFC3986] required changes to the ABNF and
+ text so that productions that are no longer defined by [RFC3986] are
+ not used. For example, <hostport> is not defined by [RFC3986] so it
+ has been replaced with host [COLON port]. Note that [RFC3986]
+ includes new definitions for the "Reserved" and "Unreserved" sets of
+ characters, and the net result is that the following two additional
+ characters should be percent-encoded when they appear anywhere in the
+ data used to construct an LDAP URL: "[" and "]" (these two characters
+ were first added to the Reserved set by RFC 2732).
+
+ Changed the definition of <attrdesc> to refer to <attributeSelector>
+ from [RFC4511]. This allows the use of "*" in the <attrdesc> part of
+ the URL. It is believed that existing implementations of RFC 2255
+ already support this.
+
+ Avoided use of <prose-val> (bracketed-string) productions in the
+ <dn>, <host>, <attrdesc>, and <exvalue> rules.
+
+ Changed the ABNF for <ldapurl> to group the <dn> component with the
+ preceding <SLASH>.
+
+ Changed the <extype> rule to be an <oid> from [RFC4512].
+
+ Changed the text about extension types so it references [RFC4520].
+ Reordered rules to more closely follow the order in which the
+ elements appear in the URL.
+
+ "Bindname Extension": removed due to lack of known implementations.
+
+A.2. Editorial Changes
+
+ Changed document title to include "LDAP:" prefix.
+
+ IESG Note: removed note about lack of satisfactory mandatory
+ authentication mechanisms.
+
+
+
+
+Smith & Howes Standards Track [Page 11]
+
+RFC 4516 LDAP: Uniform Resource Locator June 2006
+
+
+ "Status of this Memo" section: updated boilerplate to match current
+ I-D guidelines.
+
+ "Abstract" section: separated from introductory material.
+
+ "Table of Contents" and "Intellectual Property" sections: added.
+
+ "Introduction" section: new section; separated from the Abstract.
+ Changed the text indicate that RFC 2255 is replaced by this document
+ (instead of RFC 1959). Added text to indicate that LDAP URLs are
+ used for references and referrals. Fixed typo (replaced the nonsense
+ phrase "to perform to retrieve" with "used to retrieve"). Added a
+ note to let the reader know that not all of the parameters of the
+ LDAP search operation described in [RFC4511] can be expressed using
+ this format.
+
+ "URL Definition" section: removed second copy of <ldapurl> grammar
+ and following two paragraphs (editorial error in RFC 2255). Fixed
+ line break within '!' sequence. Reformatted the ABNF to improve
+ readability by aligning comments and adding some blank lines.
+ Replaced "residing in the LDAP server" with "accessible from the LDAP
+ server" in the sentence immediately following the ABNF. Removed the
+ sentence "Individual attrdesc names are as defined for
+ AttributeDescription in [RFC4511]." because [RFC4511]'s
+ <attributeSelector> is now used directly in the ABNF. Reworded last
+ paragraph to clarify which characters must be percent-encoded. Added
+ text to indicate that LDAP URLs are used for references and
+ referrals. Added text that refers to the ABNF from RFC 4234.
+ Clarified and strengthened the requirements with respect to
+ processing of URLs that contain implemented and not implemented
+ extensions (the approach now closely matches that specified in
+ [RFC4511] for LDAP controls).
+
+ "Defaults for Fields of the LDAP URL" section: added; formed by
+ moving text about defaults out of the "URL Definition" section.
+ Replaced direct reference to the attribute name "*" with a reference
+ to the special <alluserattrs> selector "*" defined in [RFC4511].
+
+ "URL Processing" section: removed.
+
+ "Examples" section: Modified examples to use example.com and
+ example.net hostnames. Added missing '?' to the LDAP URL example
+ whose filter contains three null bytes. Removed space after one
+ comma within a DN. Revised the bindname example to use e-bindname.
+ Changed the name of an attribute used in one example from "int" to
+ "four-octet" to avoid potential confusion. Added an example that
+ demonstrates the interaction between DN escaping and URL percent-
+ encoding. Added some examples to show URL equivalence with respect
+
+
+
+Smith & Howes Standards Track [Page 12]
+
+RFC 4516 LDAP: Uniform Resource Locator June 2006
+
+
+ to the <dn> portion of the URL. Used uppercase in some examples to
+ remind the reader that some tokens are case-insensitive.
+
+ "Security Considerations" section: Added a note about connection
+ reuse. Added a note about using strong authentication methods for
+ updates. Added a reference to [RFC4513]. Added note that simply
+ opening a connection may violate some users' privacy requirements.
+ Adopted the working group's revised LDAP terminology specification by
+ replacing the word "connection" with "LDAP session" or "LDAP
+ connection" as appropriate.
+
+ "Acknowledgements" section: added statement that this document
+ obsoletes RFC 2255. Added Kurt Zeilenga, Jim Sermersheim, and
+ Hallvard Furuseth.
+
+ "Normative References" section: renamed from "References" per new RFC
+ guidelines. Changed from [1] style to [RFC4511] style throughout the
+ document. Added references to RFC 4234 and RFC 3629. Updated all
+ RFC 1738 references to point to the appropriate sections within
+ [RFC3986]. Updated the LDAP references to refer to LDAPBis WG
+ documents. Removed the reference to the LDAP Attribute Syntaxes
+ document and added references to the [RFC4513], [RFC4520], and
+ [RFC4510] documents.
+
+ "Informative References" section: added.
+
+ Header and "Authors' Addresses" sections: added "editor" next to Mark
+ Smith's name. Updated affiliation and contact information.
+
+ Copyright: updated the year.
+
+ Throughout the document: surrounded the names of all ABNF productions
+ with "<" and ">" where they are used in descriptive text.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Smith & Howes Standards Track [Page 13]
+
+RFC 4516 LDAP: Uniform Resource Locator June 2006
+
+
+Authors' Addresses
+
+ Mark Smith, Editor
+ Pearl Crescent, LLC
+ 447 Marlpool Dr.
+ Saline, MI 48176
+ USA
+
+ Phone: +1 734 944-2856
+ EMail: mcs@pearlcrescent.com
+
+
+ Tim Howes
+ Opsware, Inc.
+ 599 N. Mathilda Ave.
+ Sunnyvale, CA 94085
+ USA
+
+ Phone: +1 408 744-7509
+ EMail: howes@opsware.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Smith & Howes Standards Track [Page 14]
+
+RFC 4516 LDAP: Uniform Resource Locator June 2006
+
+
+Full Copyright Statement
+
+ Copyright (C) The Internet Society (2006).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
+ ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
+ INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
+ INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be
+ found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at
+ ietf-ipr@ietf.org.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is provided by the IETF
+ Administrative Support Activity (IASA).
+
+
+
+
+
+
+
+Smith & Howes Standards Track [Page 15]
+
diff --git a/source4/ldap_server/devdocs/rfc4517.txt b/source4/ldap_server/devdocs/rfc4517.txt
new file mode 100644
index 0000000000..177e08b2ac
--- /dev/null
+++ b/source4/ldap_server/devdocs/rfc4517.txt
@@ -0,0 +1,2971 @@
+
+
+
+
+
+
+Network Working Group S. Legg, Ed.
+Request for Comments: 4517 eB2Bcom
+Obsoletes: 2252, 2256 June 2006
+Updates: 3698
+Category: Standards Track
+
+
+ Lightweight Directory Access Protocol (LDAP):
+ Syntaxes and Matching Rules
+
+
+Status of This Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2006).
+
+Abstract
+
+ Each attribute stored in a Lightweight Directory Access Protocol
+ (LDAP) directory, whose values may be transferred in the LDAP
+ protocol, has a defined syntax that constrains the structure and
+ format of its values. The comparison semantics for values of a
+ syntax are not part of the syntax definition but are instead provided
+ through separately defined matching rules. Matching rules specify an
+ argument, an assertion value, which also has a defined syntax. This
+ document defines a base set of syntaxes and matching rules for use in
+ defining attributes for LDAP directories.
+
+Table of Contents
+
+ 1. Introduction ....................................................3
+ 2. Conventions .....................................................4
+ 3. Syntaxes ........................................................4
+ 3.1. General Considerations .....................................5
+ 3.2. Common Definitions .........................................5
+ 3.3. Syntax Definitions .........................................6
+ 3.3.1. Attribute Type Description ..........................6
+ 3.3.2. Bit String ..........................................6
+ 3.3.3. Boolean .............................................7
+ 3.3.4. Country String ......................................7
+ 3.3.5. Delivery Method .....................................8
+
+
+
+Legg Standards Track [Page 1]
+
+RFC 4517 LDAP: Syntaxes and Matching Rules June 2006
+
+
+ 3.3.6. Directory String ....................................8
+ 3.3.7. DIT Content Rule Description ........................9
+ 3.3.8. DIT Structure Rule Description .....................10
+ 3.3.9. DN .................................................10
+ 3.3.10. Enhanced Guide ....................................11
+ 3.3.11. Facsimile Telephone Number ........................12
+ 3.3.12. Fax ...............................................12
+ 3.3.13. Generalized Time ..................................13
+ 3.3.14. Guide .............................................14
+ 3.3.15. IA5 String ........................................15
+ 3.3.16. Integer ...........................................15
+ 3.3.17. JPEG ..............................................15
+ 3.3.18. LDAP Syntax Description ...........................16
+ 3.3.19. Matching Rule Description .........................16
+ 3.3.20. Matching Rule Use Description .....................17
+ 3.3.21. Name and Optional UID .............................17
+ 3.3.22. Name Form Description .............................18
+ 3.3.23. Numeric String ....................................18
+ 3.3.24. Object Class Description ..........................18
+ 3.3.25. Octet String ......................................19
+ 3.3.26. OID ...............................................19
+ 3.3.27. Other Mailbox .....................................20
+ 3.3.28. Postal Address ....................................20
+ 3.3.29. Printable String ..................................21
+ 3.3.30. Substring Assertion ...............................22
+ 3.3.31. Telephone Number ..................................23
+ 3.3.32. Teletex Terminal Identifier .......................23
+ 3.3.33. Telex Number ......................................24
+ 3.3.34. UTC Time ..........................................24
+ 4. Matching Rules .................................................25
+ 4.1. General Considerations ....................................25
+ 4.2. Matching Rule Definitions .................................27
+ 4.2.1. bitStringMatch .....................................27
+ 4.2.2. booleanMatch .......................................28
+ 4.2.3. caseExactIA5Match ..................................28
+ 4.2.4. caseExactMatch .....................................29
+ 4.2.5. caseExactOrderingMatch .............................29
+ 4.2.6. caseExactSubstringsMatch ...........................30
+ 4.2.7. caseIgnoreIA5Match .................................30
+ 4.2.8. caseIgnoreIA5SubstringsMatch .......................31
+ 4.2.9. caseIgnoreListMatch ................................31
+ 4.2.10. caseIgnoreListSubstringsMatch .....................32
+ 4.2.11. caseIgnoreMatch ...................................33
+ 4.2.12. caseIgnoreOrderingMatch ...........................33
+ 4.2.13. caseIgnoreSubstringsMatch .........................34
+ 4.2.14. directoryStringFirstComponentMatch ................34
+ 4.2.15. distinguishedNameMatch ............................35
+ 4.2.16. generalizedTimeMatch ..............................36
+
+
+
+Legg Standards Track [Page 2]
+
+RFC 4517 LDAP: Syntaxes and Matching Rules June 2006
+
+
+ 4.2.17. generalizedTimeOrderingMatch ......................36
+ 4.2.18. integerFirstComponentMatch ........................36
+ 4.2.19. integerMatch ......................................37
+ 4.2.20. integerOrderingMatch ..............................37
+ 4.2.21. keywordMatch ......................................38
+ 4.2.22. numericStringMatch ................................38
+ 4.2.23. numericStringOrderingMatch ........................39
+ 4.2.24. numericStringSubstringsMatch ......................39
+ 4.2.25. objectIdentifierFirstComponentMatch ...............40
+ 4.2.26. objectIdentifierMatch .............................40
+ 4.2.27. octetStringMatch ..................................41
+ 4.2.28. octetStringOrderingMatch ..........................41
+ 4.2.29. telephoneNumberMatch ..............................42
+ 4.2.30. telephoneNumberSubstringsMatch ....................42
+ 4.2.31. uniqueMemberMatch .................................43
+ 4.2.32. wordMatch .........................................44
+ 5. Security Considerations ........................................44
+ 6. Acknowledgements ...............................................44
+ 7. IANA Considerations ............................................45
+ 8. References .....................................................46
+ 8.1. Normative References ......................................46
+ 8.2. Informative References ....................................48
+ Appendix A. Summary of Syntax Object Identifiers ..................49
+ Appendix B. Changes from RFC 2252 .................................49
+
+1. Introduction
+
+ Each attribute stored in a Lightweight Directory Access Protocol
+ (LDAP) directory [RFC4510], whose values may be transferred in the
+ LDAP protocol [RFC4511], has a defined syntax (i.e., data type) that
+ constrains the structure and format of its values. The comparison
+ semantics for values of a syntax are not part of the syntax
+ definition but are instead provided through separately defined
+ matching rules. Matching rules specify an argument, an assertion
+ value, which also has a defined syntax. This document defines a base
+ set of syntaxes and matching rules for use in defining attributes for
+ LDAP directories.
+
+ Readers are advised to familiarize themselves with the Directory
+ Information Models [RFC4512] before reading the rest of this
+ document. Section 3 provides definitions for the base set of LDAP
+ syntaxes. Section 4 provides definitions for the base set of
+ matching rules for LDAP.
+
+ This document is an integral part of the LDAP technical specification
+ [RFC4510], which obsoletes the previously defined LDAP technical
+ specification, RFC 3377, in its entirety.
+
+
+
+
+Legg Standards Track [Page 3]
+
+RFC 4517 LDAP: Syntaxes and Matching Rules June 2006
+
+
+ Sections 4, 5, and 7 of RFC 2252 are obsoleted by [RFC4512]. The
+ remainder of RFC 2252 is obsoleted by this document. Sections 6 and
+ 8 of RFC 2256 are obsoleted by this document. The remainder of RFC
+ 2256 is obsoleted by [RFC4519] and [RFC4512]. All but Section 2.11
+ of RFC 3698 is obsoleted by this document.
+
+ A number of schema elements that were included in the previous
+ revision of the LDAP technical specification are not included in this
+ revision of LDAP. Public Key Infrastructure schema elements are now
+ specified in [RFC4523]. Unless reintroduced in future technical
+ specifications, the remainder are to be considered Historic.
+
+ The changes with respect to RFC 2252 are described in Appendix B of
+ this document.
+
+2. Conventions
+
+ In this document, the key words "MUST", "MUST NOT", "REQUIRED",
+ "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY",
+ and "OPTIONAL" are to be interpreted as described in BCP 14, RFC 2119
+ [RFC2119].
+
+ Syntax definitions are written according to the <SyntaxDescription>
+ ABNF [RFC4234] rule specified in [RFC4512], and matching rule
+ definitions are written according to the <MatchingRuleDescription>
+ ABNF rule specified in [RFC4512], except that the syntax and matching
+ rule definitions provided in this document are line-wrapped for
+ readability. When such definitions are transferred as attribute
+ values in the LDAP protocol (e.g., as values of the ldapSyntaxes and
+ matchingRules attributes [RFC4512], respectively), then those values
+ would not contain line breaks.
+
+3. Syntaxes
+
+ Syntax definitions constrain the structure of attribute values stored
+ in an LDAP directory, and determine the representation of attribute
+ and assertion values transferred in the LDAP protocol.
+
+ Syntaxes that are required for directory operation, or that are in
+ common use, are specified in this section. Servers SHOULD recognize
+ all the syntaxes listed in this document, but are not required to
+ otherwise support them, and MAY recognise or support other syntaxes.
+ However, the definition of additional arbitrary syntaxes is
+ discouraged since it will hinder interoperability. Client and server
+ implementations typically do not have the ability to dynamically
+ recognize new syntaxes.
+
+
+
+
+
+Legg Standards Track [Page 4]
+
+RFC 4517 LDAP: Syntaxes and Matching Rules June 2006
+
+
+3.1. General Considerations
+
+ The description of each syntax specifies how attribute or assertion
+ values conforming to the syntax are to be represented when
+ transferred in the LDAP protocol [RFC4511]. This representation is
+ referred to as the LDAP-specific encoding to distinguish it from
+ other methods of encoding attribute values (e.g., the Basic Encoding
+ Rules (BER) encoding [BER] used by X.500 [X.500] directories).
+
+ The LDAP-specific encoding of a given attribute syntax always
+ produces octet-aligned values. To the greatest extent possible,
+ encoding rules for LDAP syntaxes should produce character strings
+ that can be displayed with little or no translation by clients
+ implementing LDAP. However, clients MUST NOT assume that the LDAP-
+ specific encoding of a value of an unrecognized syntax is a human-
+ readable character string. There are a few cases (e.g., the JPEG
+ syntax) when it is not reasonable to produce a human-readable
+ representation.
+
+ Each LDAP syntax is uniquely identified with an object identifier
+ [ASN.1] represented in the dotted-decimal format (short descriptive
+ names are not defined for syntaxes). These object identifiers are
+ not intended to be displayed to users. The object identifiers for
+ the syntaxes defined in this document are summarized in Appendix A.
+
+ A suggested minimum upper bound on the number of characters in an
+ attribute value with a string-based syntax, or the number of octets
+ in a value for all other syntaxes, MAY be indicated by appending the
+ bound inside of curly braces following the syntax's OBJECT IDENTIFIER
+ in an attribute type definition (see the <noidlen> rule in
+ [RFC4512]). Such a bound is not considered part of the syntax
+ identifier.
+
+ For example, "1.3.6.1.4.1.1466.115.121.1.15{64}" in an attribute
+ definition suggests that the directory server will allow a value of
+ the attribute to be up to 64 characters long, although it may allow
+ longer character strings. Note that a single character of the
+ Directory String syntax can be encoded in more than one octet, since
+ UTF-8 [RFC3629] is a variable-length encoding. Therefore, a 64-
+ character string may be more than 64 octets in length.
+
+3.2. Common Definitions
+
+ The following ABNF rules are used in a number of the syntax
+ definitions in Section 3.3.
+
+ PrintableCharacter = ALPHA / DIGIT / SQUOTE / LPAREN / RPAREN /
+ PLUS / COMMA / HYPHEN / DOT / EQUALS /
+
+
+
+Legg Standards Track [Page 5]
+
+RFC 4517 LDAP: Syntaxes and Matching Rules June 2006
+
+
+ SLASH / COLON / QUESTION / SPACE
+ PrintableString = 1*PrintableCharacter
+ IA5String = *(%x00-7F)
+ SLASH = %x2F ; forward slash ("/")
+ COLON = %x3A ; colon (":")
+ QUESTION = %x3F ; question mark ("?")
+
+ The <ALPHA>, <DIGIT>, <SQUOTE>, <LPAREN>, <RPAREN>, <PLUS>, <COMMA>,
+ <HYPHEN>, <DOT>, <EQUALS>, and <SPACE> rules are defined in
+ [RFC4512].
+
+3.3. Syntax Definitions
+
+3.3.1. Attribute Type Description
+
+ A value of the Attribute Type Description syntax is the definition of
+ an attribute type. The LDAP-specific encoding of a value of this
+ syntax is defined by the <AttributeTypeDescription> rule in
+ [RFC4512].
+
+ For example, the following definition of the createTimestamp
+ attribute type from [RFC4512] is also a value of the Attribute
+ Type Description syntax. (Note: Line breaks have been added for
+ readability; they are not part of the value when transferred in
+ protocol.)
+
+ ( 2.5.18.1 NAME 'createTimestamp'
+ EQUALITY generalizedTimeMatch
+ ORDERING generalizedTimeOrderingMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.24
+ SINGLE-VALUE NO-USER-MODIFICATION
+ USAGE directoryOperation )
+
+ The LDAP definition for the Attribute Type Description syntax is:
+
+ ( 1.3.6.1.4.1.1466.115.121.1.3 DESC 'Attribute Type Description' )
+
+ This syntax corresponds to the AttributeTypeDescription ASN.1 type
+ from [X.501].
+
+3.3.2. Bit String
+
+ A value of the Bit String syntax is a sequence of binary digits. The
+ LDAP-specific encoding of a value of this syntax is defined by the
+ following ABNF:
+
+ BitString = SQUOTE *binary-digit SQUOTE "B"
+ binary-digit = "0" / "1"
+
+
+
+Legg Standards Track [Page 6]
+
+RFC 4517 LDAP: Syntaxes and Matching Rules June 2006
+
+
+ The <SQUOTE> rule is defined in [RFC4512].
+
+ Example:
+ '0101111101'B
+
+ The LDAP definition for the Bit String syntax is:
+
+ ( 1.3.6.1.4.1.1466.115.121.1.6 DESC 'Bit String' )
+
+ This syntax corresponds to the BIT STRING ASN.1 type from [ASN.1].
+
+3.3.3. Boolean
+
+ A value of the Boolean syntax is one of the Boolean values, true or
+ false. The LDAP-specific encoding of a value of this syntax is
+ defined by the following ABNF:
+
+ Boolean = "TRUE" / "FALSE"
+
+ The LDAP definition for the Boolean syntax is:
+
+ ( 1.3.6.1.4.1.1466.115.121.1.7 DESC 'Boolean' )
+
+ This syntax corresponds to the BOOLEAN ASN.1 type from [ASN.1].
+
+3.3.4. Country String
+
+ A value of the Country String syntax is one of the two-character
+ codes from ISO 3166 [ISO3166] for representing a country. The LDAP-
+ specific encoding of a value of this syntax is defined by the
+ following ABNF:
+
+ CountryString = 2(PrintableCharacter)
+
+ The <PrintableCharacter> rule is defined in Section 3.2.
+
+ Examples:
+
+ US
+ AU
+
+ The LDAP definition for the Country String syntax is:
+
+ ( 1.3.6.1.4.1.1466.115.121.1.11 DESC 'Country String' )
+
+ This syntax corresponds to the following ASN.1 type from [X.520]:
+
+ PrintableString (SIZE (2)) -- ISO 3166 codes only
+
+
+
+Legg Standards Track [Page 7]
+
+RFC 4517 LDAP: Syntaxes and Matching Rules June 2006
+
+
+3.3.5. Delivery Method
+
+ A value of the Delivery Method syntax is a sequence of items that
+ indicate, in preference order, the service(s) by which an entity is
+ willing and/or capable of receiving messages. The LDAP-specific
+ encoding of a value of this syntax is defined by the following ABNF:
+
+ DeliveryMethod = pdm *( WSP DOLLAR WSP pdm )
+
+ pdm = "any" / "mhs" / "physical" / "telex" / "teletex" /
+ "g3fax" / "g4fax" / "ia5" / "videotex" / "telephone"
+
+ The <WSP> and <DOLLAR> rules are defined in [RFC4512].
+
+ Example:
+ telephone $ videotex
+
+ The LDAP definition for the Delivery Method syntax is:
+
+ ( 1.3.6.1.4.1.1466.115.121.1.14 DESC 'Delivery Method' )
+
+ This syntax corresponds to the following ASN.1 type from [X.520]:
+
+ SEQUENCE OF INTEGER {
+ any-delivery-method (0),
+ mhs-delivery (1),
+ physical-delivery (2),
+ telex-delivery (3),
+ teletex-delivery (4),
+ g3-facsimile-delivery (5),
+ g4-facsimile-delivery (6),
+ ia5-terminal-delivery (7),
+ videotex-delivery (8),
+ telephone-delivery (9) }
+
+3.3.6. Directory String
+
+ A value of the Directory String syntax is a string of one or more
+ arbitrary characters from the Universal Character Set (UCS) [UCS]. A
+ zero-length character string is not permitted. The LDAP-specific
+ encoding of a value of this syntax is the UTF-8 encoding [RFC3629] of
+ the character string. Such encodings conform to the following ABNF:
+
+ DirectoryString = 1*UTF8
+
+ The <UTF8> rule is defined in [RFC4512].
+
+
+
+
+
+Legg Standards Track [Page 8]
+
+RFC 4517 LDAP: Syntaxes and Matching Rules June 2006
+
+
+ Example:
+ This is a value of Directory String containing #!%#@.
+
+ Servers and clients MUST be prepared to receive arbitrary UCS code
+ points, including code points outside the range of printable ASCII
+ and code points not presently assigned to any character.
+
+ Attribute type definitions using the Directory String syntax should
+ not restrict the format of Directory String values, e.g., by
+ requiring that the character string conforms to specific patterns
+ described by ABNF. A new syntax should be defined in such cases.
+
+ The LDAP definition for the Directory String syntax is:
+
+ ( 1.3.6.1.4.1.1466.115.121.1.15 DESC 'Directory String' )
+
+ This syntax corresponds to the DirectoryString parameterized ASN.1
+ type from [X.520].
+
+ The DirectoryString ASN.1 type allows a choice between the
+ TeletexString, PrintableString, or UniversalString ASN.1 types from
+ [ASN.1]. However, note that the chosen alternative is not indicated
+ in the LDAP-specific encoding of a Directory String value.
+
+ Implementations that convert Directory String values from the LDAP-
+ specific encoding to the BER encoding used by X.500 must choose an
+ alternative that permits the particular characters in the string and
+ must convert the characters from the UTF-8 encoding into the
+ character encoding of the chosen alternative. When converting
+ Directory String values from the BER encoding to the LDAP-specific
+ encoding, the characters must be converted from the character
+ encoding of the chosen alternative into the UTF-8 encoding. These
+ conversions SHOULD be done in a manner consistent with the Transcode
+ step of the string preparation algorithms [RFC4518] for LDAP.
+
+3.3.7. DIT Content Rule Description
+
+ A value of the DIT Content Rule Description syntax is the definition
+ of a DIT (Directory Information Tree) content rule. The LDAP-
+ specific encoding of a value of this syntax is defined by the
+ <DITContentRuleDescription> rule in [RFC4512].
+
+ Example:
+ ( 2.5.6.4 DESC 'content rule for organization'
+ NOT ( x121Address $ telexNumber ) )
+
+ Note: A line break has been added for readability; it is not part
+ of the value.
+
+
+
+Legg Standards Track [Page 9]
+
+RFC 4517 LDAP: Syntaxes and Matching Rules June 2006
+
+
+ The LDAP definition for the DIT Content Rule Description syntax is:
+
+ ( 1.3.6.1.4.1.1466.115.121.1.16
+ DESC 'DIT Content Rule Description' )
+
+ This syntax corresponds to the DITContentRuleDescription ASN.1 type
+ from [X.501].
+
+3.3.8. DIT Structure Rule Description
+
+ A value of the DIT Structure Rule Description syntax is the
+ definition of a DIT structure rule. The LDAP-specific encoding of a
+ value of this syntax is defined by the <DITStructureRuleDescription>
+ rule in [RFC4512].
+
+ Example:
+ ( 2 DESC 'organization structure rule' FORM 2.5.15.3 )
+
+ The LDAP definition for the DIT Structure Rule Description syntax is:
+
+ ( 1.3.6.1.4.1.1466.115.121.1.17
+ DESC 'DIT Structure Rule Description' )
+
+ This syntax corresponds to the DITStructureRuleDescription ASN.1 type
+ from [X.501].
+
+3.3.9. DN
+
+ A value of the DN syntax is the (purported) distinguished name (DN)
+ of an entry [RFC4512]. The LDAP-specific encoding of a value of this
+ syntax is defined by the <distinguishedName> rule from the string
+ representation of distinguished names [RFC4514].
+
+ Examples (from [RFC4514]):
+ UID=jsmith,DC=example,DC=net
+ OU=Sales+CN=J. Smith,DC=example,DC=net
+ CN=John Smith\, III,DC=example,DC=net
+ CN=Before\0dAfter,DC=example,DC=net
+ 1.3.6.1.4.1.1466.0=#04024869,DC=example,DC=com
+ CN=Lu\C4\8Di\C4\87
+
+ The LDAP definition for the DN syntax is:
+
+ ( 1.3.6.1.4.1.1466.115.121.1.12 DESC 'DN' )
+
+ The DN syntax corresponds to the DistinguishedName ASN.1 type from
+ [X.501]. Note that a BER encoded distinguished name (as used by
+ X.500) re-encoded into the LDAP-specific encoding is not necessarily
+
+
+
+Legg Standards Track [Page 10]
+
+RFC 4517 LDAP: Syntaxes and Matching Rules June 2006
+
+
+ reversible to the original BER encoding since the chosen string type
+ in any DirectoryString components of the distinguished name is not
+ indicated in the LDAP-specific encoding of the distinguished name
+ (see Section 3.3.6).
+
+3.3.10. Enhanced Guide
+
+ A value of the Enhanced Guide syntax suggests criteria, which consist
+ of combinations of attribute types and filter operators, to be used
+ in constructing filters to search for entries of particular object
+ classes. The Enhanced Guide syntax improves upon the Guide syntax by
+ allowing the recommended depth of the search to be specified.
+
+ The LDAP-specific encoding of a value of this syntax is defined by
+ the following ABNF:
+
+ EnhancedGuide = object-class SHARP WSP criteria WSP
+ SHARP WSP subset
+ object-class = WSP oid WSP
+ subset = "baseobject" / "oneLevel" / "wholeSubtree"
+
+ criteria = and-term *( BAR and-term )
+ and-term = term *( AMPERSAND term )
+ term = EXCLAIM term /
+ attributetype DOLLAR match-type /
+ LPAREN criteria RPAREN /
+ true /
+ false
+ match-type = "EQ" / "SUBSTR" / "GE" / "LE" / "APPROX"
+ true = "?true"
+ false = "?false"
+ BAR = %x7C ; vertical bar ("|")
+ AMPERSAND = %x26 ; ampersand ("&")
+ EXCLAIM = %x21 ; exclamation mark ("!")
+
+ The <SHARP>, <WSP>, <oid>, <LPAREN>, <RPAREN>, <attributetype>, and
+ <DOLLAR> rules are defined in [RFC4512].
+
+ The LDAP definition for the Enhanced Guide syntax is:
+
+ ( 1.3.6.1.4.1.1466.115.121.1.21 DESC 'Enhanced Guide' )
+
+ Example:
+ person#(sn$EQ)#oneLevel
+
+ The Enhanced Guide syntax corresponds to the EnhancedGuide ASN.1 type
+ from [X.520]. The EnhancedGuide type references the Criteria ASN.1
+ type, also from [X.520]. The <true> rule, above, represents an empty
+
+
+
+Legg Standards Track [Page 11]
+
+RFC 4517 LDAP: Syntaxes and Matching Rules June 2006
+
+
+ "and" expression in a value of the Criteria type. The <false> rule,
+ above, represents an empty "or" expression in a value of the Criteria
+ type.
+
+3.3.11. Facsimile Telephone Number
+
+ A value of the Facsimile Telephone Number syntax is a subscriber
+ number of a facsimile device on the public switched telephone
+ network. The LDAP-specific encoding of a value of this syntax is
+ defined by the following ABNF:
+
+ fax-number = telephone-number *( DOLLAR fax-parameter )
+ telephone-number = PrintableString
+ fax-parameter = "twoDimensional" /
+ "fineResolution" /
+ "unlimitedLength" /
+ "b4Length" /
+ "a3Width" /
+ "b4Width" /
+ "uncompressed"
+
+ The <telephone-number> is a string of printable characters that
+ complies with the internationally agreed format for representing
+ international telephone numbers [E.123]. The <PrintableString> rule
+ is defined in Section 3.2. The <DOLLAR> rule is defined in
+ [RFC4512].
+
+ The LDAP definition for the Facsimile Telephone Number syntax is:
+
+ ( 1.3.6.1.4.1.1466.115.121.1.22 DESC 'Facsimile Telephone Number')
+
+ The Facsimile Telephone Number syntax corresponds to the
+ FacsimileTelephoneNumber ASN.1 type from [X.520].
+
+3.3.12. Fax
+
+ A value of the Fax syntax is an image that is produced using the
+ Group 3 facsimile process [FAX] to duplicate an object, such as a
+ memo. The LDAP-specific encoding of a value of this syntax is the
+ string of octets for a Group 3 Fax image as defined in [FAX].
+
+ The LDAP definition for the Fax syntax is:
+
+ ( 1.3.6.1.4.1.1466.115.121.1.23 DESC 'Fax' )
+
+ The ASN.1 type corresponding to the Fax syntax is defined as follows,
+ assuming EXPLICIT TAGS:
+
+
+
+
+Legg Standards Track [Page 12]
+
+RFC 4517 LDAP: Syntaxes and Matching Rules June 2006
+
+
+ Fax ::= CHOICE {
+ g3-facsimile [3] G3FacsimileBodyPart
+ }
+
+ The G3FacsimileBodyPart ASN.1 type is defined in [X.420].
+
+3.3.13. Generalized Time
+
+ A value of the Generalized Time syntax is a character string
+ representing a date and time. The LDAP-specific encoding of a value
+ of this syntax is a restriction of the format defined in [ISO8601],
+ and is described by the following ABNF:
+
+ GeneralizedTime = century year month day hour
+ [ minute [ second / leap-second ] ]
+ [ fraction ]
+ g-time-zone
+
+ century = 2(%x30-39) ; "00" to "99"
+ year = 2(%x30-39) ; "00" to "99"
+ month = ( %x30 %x31-39 ) ; "01" (January) to "09"
+ / ( %x31 %x30-32 ) ; "10" to "12"
+ day = ( %x30 %x31-39 ) ; "01" to "09"
+ / ( %x31-32 %x30-39 ) ; "10" to "29"
+ / ( %x33 %x30-31 ) ; "30" to "31"
+ hour = ( %x30-31 %x30-39 ) / ( %x32 %x30-33 ) ; "00" to "23"
+ minute = %x30-35 %x30-39 ; "00" to "59"
+
+ second = ( %x30-35 %x30-39 ) ; "00" to "59"
+ leap-second = ( %x36 %x30 ) ; "60"
+
+ fraction = ( DOT / COMMA ) 1*(%x30-39)
+ g-time-zone = %x5A ; "Z"
+ / g-differential
+ g-differential = ( MINUS / PLUS ) hour [ minute ]
+ MINUS = %x2D ; minus sign ("-")
+
+ The <DOT>, <COMMA>, and <PLUS> rules are defined in [RFC4512].
+
+ The above ABNF allows character strings that do not represent valid
+ dates (in the Gregorian calendar) and/or valid times (e.g., February
+ 31, 1994). Such character strings SHOULD be considered invalid for
+ this syntax.
+
+ The time value represents coordinated universal time (equivalent to
+ Greenwich Mean Time) if the "Z" form of <g-time-zone> is used;
+ otherwise, the value represents a local time in the time zone
+ indicated by <g-differential>. In the latter case, coordinated
+
+
+
+Legg Standards Track [Page 13]
+
+RFC 4517 LDAP: Syntaxes and Matching Rules June 2006
+
+
+ universal time can be calculated by subtracting the differential from
+ the local time. The "Z" form of <g-time-zone> SHOULD be used in
+ preference to <g-differential>.
+
+ If <minute> is omitted, then <fraction> represents a fraction of an
+ hour; otherwise, if <second> and <leap-second> are omitted, then
+ <fraction> represents a fraction of a minute; otherwise, <fraction>
+ represents a fraction of a second.
+
+ Examples:
+ 199412161032Z
+ 199412160532-0500
+
+ Both example values represent the same coordinated universal time:
+ 10:32 AM, December 16, 1994.
+
+ The LDAP definition for the Generalized Time syntax is:
+
+ ( 1.3.6.1.4.1.1466.115.121.1.24 DESC 'Generalized Time' )
+
+ This syntax corresponds to the GeneralizedTime ASN.1 type from
+ [ASN.1], with the constraint that local time without a differential
+ SHALL NOT be used.
+
+3.3.14. Guide
+
+ A value of the Guide syntax suggests criteria, which consist of
+ combinations of attribute types and filter operators, to be used in
+ constructing filters to search for entries of particular object
+ classes. The Guide syntax is obsolete and should not be used for
+ defining new attribute types.
+
+ The LDAP-specific encoding of a value of this syntax is defined by
+ the following ABNF:
+
+ Guide = [ object-class SHARP ] criteria
+
+ The <object-class> and <criteria> rules are defined in Section
+ 3.3.10. The <SHARP> rule is defined in [RFC4512].
+
+ The LDAP definition for the Guide syntax is:
+
+ ( 1.3.6.1.4.1.1466.115.121.1.25 DESC 'Guide' )
+
+ The Guide syntax corresponds to the Guide ASN.1 type from [X.520].
+
+
+
+
+
+
+Legg Standards Track [Page 14]
+
+RFC 4517 LDAP: Syntaxes and Matching Rules June 2006
+
+
+3.3.15. IA5 String
+
+ A value of the IA5 String syntax is a string of zero, one, or more
+ characters from International Alphabet 5 (IA5) [T.50], the
+ international version of the ASCII character set. The LDAP-specific
+ encoding of a value of this syntax is the unconverted string of
+ characters, which conforms to the <IA5String> rule in Section 3.2.
+
+ The LDAP definition for the IA5 String syntax is:
+
+ ( 1.3.6.1.4.1.1466.115.121.1.26 DESC 'IA5 String' )
+
+ This syntax corresponds to the IA5String ASN.1 type from [ASN.1].
+
+3.3.16. Integer
+
+ A value of the Integer syntax is a whole number of unlimited
+ magnitude. The LDAP-specific encoding of a value of this syntax is
+ the optionally signed decimal digit character string representation
+ of the number (for example, the number 1321 is represented by the
+ character string "1321"). The encoding is defined by the following
+ ABNF:
+
+ Integer = ( HYPHEN LDIGIT *DIGIT ) / number
+
+ The <HYPHEN>, <LDIGIT>, <DIGIT>, and <number> rules are defined in
+ [RFC4512].
+
+ The LDAP definition for the Integer syntax is:
+
+ ( 1.3.6.1.4.1.1466.115.121.1.27 DESC 'INTEGER' )
+
+ This syntax corresponds to the INTEGER ASN.1 type from [ASN.1].
+
+3.3.17. JPEG
+
+ A value of the JPEG syntax is an image in the JPEG File Interchange
+ Format (JFIF), as described in [JPEG]. The LDAP-specific encoding of
+ a value of this syntax is the sequence of octets of the JFIF encoding
+ of the image.
+
+ The LDAP definition for the JPEG syntax is:
+
+ ( 1.3.6.1.4.1.1466.115.121.1.28 DESC 'JPEG' )
+
+ The JPEG syntax corresponds to the following ASN.1 type:
+
+
+
+
+
+Legg Standards Track [Page 15]
+
+RFC 4517 LDAP: Syntaxes and Matching Rules June 2006
+
+
+ JPEG ::= OCTET STRING (CONSTRAINED BY
+ { -- contents octets are an image in the --
+ -- JPEG File Interchange Format -- })
+
+3.3.18. LDAP Syntax Description
+
+ A value of the LDAP Syntax Description syntax is the description of
+ an LDAP syntax. The LDAP-specific encoding of a value of this syntax
+ is defined by the <SyntaxDescription> rule in [RFC4512].
+
+ The LDAP definition for the LDAP Syntax Description syntax is:
+
+ ( 1.3.6.1.4.1.1466.115.121.1.54 DESC 'LDAP Syntax Description' )
+
+ The above LDAP definition for the LDAP Syntax Description syntax is
+ itself a legal value of the LDAP Syntax Description syntax.
+
+ The ASN.1 type corresponding to the LDAP Syntax Description syntax is
+ defined as follows, assuming EXPLICIT TAGS:
+
+ LDAPSyntaxDescription ::= SEQUENCE {
+ identifier OBJECT IDENTIFIER,
+ description DirectoryString { ub-schema } OPTIONAL }
+
+ The DirectoryString parameterized ASN.1 type is defined in [X.520].
+
+ The value of ub-schema (an integer) is implementation defined. A
+ non-normative definition appears in [X.520].
+
+3.3.19. Matching Rule Description
+
+ A value of the Matching Rule Description syntax is the definition of
+ a matching rule. The LDAP-specific encoding of a value of this
+ syntax is defined by the <MatchingRuleDescription> rule in [RFC4512].
+
+ Example:
+ ( 2.5.13.2 NAME 'caseIgnoreMatch'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+
+ Note: A line break has been added for readability; it is not part of
+ the syntax.
+
+ The LDAP definition for the Matching Rule Description syntax is:
+
+ ( 1.3.6.1.4.1.1466.115.121.1.30 DESC 'Matching Rule Description' )
+
+ This syntax corresponds to the MatchingRuleDescription ASN.1 type
+ from [X.501].
+
+
+
+Legg Standards Track [Page 16]
+
+RFC 4517 LDAP: Syntaxes and Matching Rules June 2006
+
+
+3.3.20. Matching Rule Use Description
+
+ A value of the Matching Rule Use Description syntax indicates the
+ attribute types to which a matching rule may be applied in an
+ extensibleMatch search filter [RFC4511]. The LDAP-specific encoding
+ of a value of this syntax is defined by the
+ <MatchingRuleUseDescription> rule in [RFC4512].
+
+ Example:
+ ( 2.5.13.16 APPLIES ( givenName $ surname ) )
+
+ The LDAP definition for the Matching Rule Use Description syntax is:
+
+ ( 1.3.6.1.4.1.1466.115.121.1.31
+ DESC 'Matching Rule Use Description' )
+
+ This syntax corresponds to the MatchingRuleUseDescription ASN.1 type
+ from [X.501].
+
+3.3.21. Name and Optional UID
+
+ A value of the Name and Optional UID syntax is the distinguished name
+ [RFC4512] of an entity optionally accompanied by a unique identifier
+ that serves to differentiate the entity from others with an identical
+ distinguished name.
+
+ The LDAP-specific encoding of a value of this syntax is defined by
+ the following ABNF:
+
+ NameAndOptionalUID = distinguishedName [ SHARP BitString ]
+
+ The <BitString> rule is defined in Section 3.3.2. The
+ <distinguishedName> rule is defined in [RFC4514]. The <SHARP> rule
+ is defined in [RFC4512].
+
+ Note that although the '#' character may occur in the string
+ representation of a distinguished name, no additional escaping of
+ this character is performed when a <distinguishedName> is encoded in
+ a <NameAndOptionalUID>.
+
+ Example:
+ 1.3.6.1.4.1.1466.0=#04024869,O=Test,C=GB#'0101'B
+
+ The LDAP definition for the Name and Optional UID syntax is:
+
+ ( 1.3.6.1.4.1.1466.115.121.1.34 DESC 'Name And Optional UID' )
+
+
+
+
+
+Legg Standards Track [Page 17]
+
+RFC 4517 LDAP: Syntaxes and Matching Rules June 2006
+
+
+ This syntax corresponds to the NameAndOptionalUID ASN.1 type from
+ [X.520].
+
+3.3.22. Name Form Description
+
+ A value of the Name Form Description syntax is the definition of a
+ name form, which regulates how entries may be named. The LDAP-
+ specific encoding of a value of this syntax is defined by the
+ <NameFormDescription> rule in [RFC4512].
+
+ Example:
+ ( 2.5.15.3 NAME 'orgNameForm' OC organization MUST o )
+
+ The LDAP definition for the Name Form Description syntax is:
+
+ ( 1.3.6.1.4.1.1466.115.121.1.35 DESC 'Name Form Description' )
+
+ This syntax corresponds to the NameFormDescription ASN.1 type from
+ [X.501].
+
+3.3.23. Numeric String
+
+ A value of the Numeric String syntax is a sequence of one or more
+ numerals and spaces. The LDAP-specific encoding of a value of this
+ syntax is the unconverted string of characters, which conforms to the
+ following ABNF:
+
+ NumericString = 1*(DIGIT / SPACE)
+
+ The <DIGIT> and <SPACE> rules are defined in [RFC4512].
+
+ Example:
+ 15 079 672 281
+
+ The LDAP definition for the Numeric String syntax is:
+
+ ( 1.3.6.1.4.1.1466.115.121.1.36 DESC 'Numeric String' )
+
+ This syntax corresponds to the NumericString ASN.1 type from [ASN.1].
+
+3.3.24. Object Class Description
+
+ A value of the Object Class Description syntax is the definition of
+ an object class. The LDAP-specific encoding of a value of this
+ syntax is defined by the <ObjectClassDescription> rule in [RFC4512].
+
+
+
+
+
+
+Legg Standards Track [Page 18]
+
+RFC 4517 LDAP: Syntaxes and Matching Rules June 2006
+
+
+ Example:
+ ( 2.5.6.2 NAME 'country' SUP top STRUCTURAL MUST c
+ MAY ( searchGuide $ description ) )
+
+ Note: A line break has been added for readability; it is not part of
+ the syntax.
+
+ The LDAP definition for the Object Class Description syntax is:
+
+ ( 1.3.6.1.4.1.1466.115.121.1.37 DESC 'Object Class Description' )
+
+ This syntax corresponds to the ObjectClassDescription ASN.1 type from
+ [X.501].
+
+3.3.25. Octet String
+
+ A value of the Octet String syntax is a sequence of zero, one, or
+ more arbitrary octets. The LDAP-specific encoding of a value of this
+ syntax is the unconverted sequence of octets, which conforms to the
+ following ABNF:
+
+ OctetString = *OCTET
+
+ The <OCTET> rule is defined in [RFC4512]. Values of this syntax are
+ not generally human-readable.
+
+ The LDAP definition for the Octet String syntax is:
+
+ ( 1.3.6.1.4.1.1466.115.121.1.40 DESC 'Octet String' )
+
+ This syntax corresponds to the OCTET STRING ASN.1 type from [ASN.1].
+
+3.3.26. OID
+
+ A value of the OID syntax is an object identifier: a sequence of two
+ or more non-negative integers that uniquely identify some object or
+ item of specification. Many of the object identifiers used in LDAP
+ also have IANA registered names [RFC4520].
+
+ The LDAP-specific encoding of a value of this syntax is defined by
+ the <oid> rule in [RFC4512].
+
+ Examples:
+ 1.2.3.4
+ cn
+
+ The LDAP definition for the OID syntax is:
+
+
+
+
+Legg Standards Track [Page 19]
+
+RFC 4517 LDAP: Syntaxes and Matching Rules June 2006
+
+
+ ( 1.3.6.1.4.1.1466.115.121.1.38 DESC 'OID' )
+
+ This syntax corresponds to the OBJECT IDENTIFIER ASN.1 type from
+ [ASN.1].
+
+3.3.27. Other Mailbox
+
+ A value of the Other Mailbox syntax identifies an electronic mailbox,
+ in a particular named mail system. The LDAP-specific encoding of a
+ value of this syntax is defined by the following ABNF:
+
+ OtherMailbox = mailbox-type DOLLAR mailbox
+ mailbox-type = PrintableString
+ mailbox = IA5String
+
+ The <mailbox-type> rule represents the type of mail system in which
+ the mailbox resides (for example, "MCIMail"), and <mailbox> is the
+ actual mailbox in the mail system described by <mailbox-type>. The
+ <PrintableString> and <IA5String> rules are defined in Section 3.2.
+ The <DOLLAR> rule is defined in [RFC4512].
+
+ The LDAP definition for the Other Mailbox syntax is:
+
+ ( 1.3.6.1.4.1.1466.115.121.1.39 DESC 'Other Mailbox' )
+
+ The ASN.1 type corresponding to the Other Mailbox syntax is defined
+ as follows, assuming EXPLICIT TAGS:
+
+ OtherMailbox ::= SEQUENCE {
+ mailboxType PrintableString,
+ mailbox IA5String
+ }
+
+3.3.28. Postal Address
+
+ A value of the Postal Address syntax is a sequence of strings of one
+ or more arbitrary UCS characters, which form an address in a physical
+ mail system.
+
+ The LDAP-specific encoding of a value of this syntax is defined by
+ the following ABNF:
+
+
+
+
+
+
+
+
+
+
+Legg Standards Track [Page 20]
+
+RFC 4517 LDAP: Syntaxes and Matching Rules June 2006
+
+
+ PostalAddress = line *( DOLLAR line )
+ line = 1*line-char
+ line-char = %x00-23
+ / (%x5C "24") ; escaped "$"
+ / %x25-5B
+ / (%x5C "5C") ; escaped "\"
+ / %x5D-7F
+ / UTFMB
+
+ Each character string (i.e., <line>) of a postal address value is
+ encoded as a UTF-8 [RFC3629] string, except that "\" and "$"
+ characters, if they occur in the string, are escaped by a "\"
+ character followed by the two hexadecimal digit code for the
+ character. The <DOLLAR> and <UTFMB> rules are defined in [RFC4512].
+
+ Many servers limit the postal address to no more than six lines of no
+ more than thirty characters each.
+
+ Example:
+ 1234 Main St.$Anytown, CA 12345$USA
+ \241,000,000 Sweepstakes$PO Box 1000000$Anytown, CA 12345$USA
+
+ The LDAP definition for the Postal Address syntax is:
+
+ ( 1.3.6.1.4.1.1466.115.121.1.41 DESC 'Postal Address' )
+
+ This syntax corresponds to the PostalAddress ASN.1 type from [X.520];
+ that is
+
+ PostalAddress ::= SEQUENCE SIZE(1..ub-postal-line) OF
+ DirectoryString { ub-postal-string }
+
+ The values of ub-postal-line and ub-postal-string (both integers) are
+ implementation defined. Non-normative definitions appear in [X.520].
+
+3.3.29. Printable String
+
+ A value of the Printable String syntax is a string of one or more
+ latin alphabetic, numeric, and selected punctuation characters as
+ specified by the <PrintableCharacter> rule in Section 3.2.
+
+ The LDAP-specific encoding of a value of this syntax is the
+ unconverted string of characters, which conforms to the
+ <PrintableString> rule in Section 3.2.
+
+ Example:
+ This is a PrintableString.
+
+
+
+
+Legg Standards Track [Page 21]
+
+RFC 4517 LDAP: Syntaxes and Matching Rules June 2006
+
+
+ The LDAP definition for the PrintableString syntax is:
+
+ ( 1.3.6.1.4.1.1466.115.121.1.44 DESC 'Printable String' )
+
+ This syntax corresponds to the PrintableString ASN.1 type from
+ [ASN.1].
+
+3.3.30. Substring Assertion
+
+ A value of the Substring Assertion syntax is a sequence of zero, one,
+ or more character substrings used as an argument for substring
+ extensible matching of character string attribute values; i.e., as
+ the matchValue of a MatchingRuleAssertion [RFC4511]. Each substring
+ is a string of one or more arbitrary characters from the Universal
+ Character Set (UCS) [UCS]. A zero-length substring is not permitted.
+
+ The LDAP-specific encoding of a value of this syntax is defined by
+ the following ABNF:
+
+ SubstringAssertion = [ initial ] any [ final ]
+
+ initial = substring
+ any = ASTERISK *(substring ASTERISK)
+ final = substring
+ ASTERISK = %x2A ; asterisk ("*")
+
+ substring = 1*substring-character
+ substring-character = %x00-29
+ / (%x5C "2A") ; escaped "*"
+ / %x2B-5B
+ / (%x5C "5C") ; escaped "\"
+ / %x5D-7F
+ / UTFMB
+
+ Each <substring> of a Substring Assertion value is encoded as a UTF-8
+ [RFC3629] string, except that "\" and "*" characters, if they occur
+ in the substring, are escaped by a "\" character followed by the two
+ hexadecimal digit code for the character.
+
+ The Substring Assertion syntax is used only as the syntax of
+ assertion values in the extensible match. It is not used as an
+ attribute syntax, or in the SubstringFilter [RFC4511].
+
+ The LDAP definition for the Substring Assertion syntax is:
+
+ ( 1.3.6.1.4.1.1466.115.121.1.58 DESC 'Substring Assertion' )
+
+
+
+
+
+Legg Standards Track [Page 22]
+
+RFC 4517 LDAP: Syntaxes and Matching Rules June 2006
+
+
+ This syntax corresponds to the SubstringAssertion ASN.1 type from
+ [X.520].
+
+3.3.31. Telephone Number
+
+ A value of the Telephone Number syntax is a string of printable
+ characters that complies with the internationally agreed format for
+ representing international telephone numbers [E.123].
+
+ The LDAP-specific encoding of a value of this syntax is the
+ unconverted string of characters, which conforms to the
+ <PrintableString> rule in Section 3.2.
+
+ Examples:
+ +1 512 315 0280
+ +1-512-315-0280
+ +61 3 9896 7830
+
+ The LDAP definition for the Telephone Number syntax is:
+
+ ( 1.3.6.1.4.1.1466.115.121.1.50 DESC 'Telephone Number' )
+
+ The Telephone Number syntax corresponds to the following ASN.1 type
+ from [X.520]:
+
+ PrintableString (SIZE(1..ub-telephone-number))
+
+ The value of ub-telephone-number (an integer) is implementation
+ defined. A non-normative definition appears in [X.520].
+
+3.3.32. Teletex Terminal Identifier
+
+ A value of this syntax specifies the identifier and (optionally)
+ parameters of a teletex terminal.
+
+ The LDAP-specific encoding of a value of this syntax is defined by
+ the following ABNF:
+
+ teletex-id = ttx-term *(DOLLAR ttx-param)
+ ttx-term = PrintableString ; terminal identifier
+ ttx-param = ttx-key COLON ttx-value ; parameter
+ ttx-key = "graphic" / "control" / "misc" / "page" / "private"
+ ttx-value = *ttx-value-octet
+
+ ttx-value-octet = %x00-23
+ / (%x5C "24") ; escaped "$"
+ / %x25-5B
+ / (%x5C "5C") ; escaped "\"
+
+
+
+Legg Standards Track [Page 23]
+
+RFC 4517 LDAP: Syntaxes and Matching Rules June 2006
+
+
+ / %x5D-FF
+
+ The <PrintableString> and <COLON> rules are defined in Section 3.2.
+ The <DOLLAR> rule is defined in [RFC4512].
+
+ The LDAP definition for the Teletex Terminal Identifier syntax is:
+
+ ( 1.3.6.1.4.1.1466.115.121.1.51
+ DESC 'Teletex Terminal Identifier' )
+
+ This syntax corresponds to the TeletexTerminalIdentifier ASN.1 type
+ from [X.520].
+
+3.3.33. Telex Number
+
+ A value of the Telex Number syntax specifies the telex number,
+ country code, and answerback code of a telex terminal.
+
+ The LDAP-specific encoding of a value of this syntax is defined by
+ the following ABNF:
+
+ telex-number = actual-number DOLLAR country-code
+ DOLLAR answerback
+ actual-number = PrintableString
+ country-code = PrintableString
+ answerback = PrintableString
+
+ The <PrintableString> rule is defined in Section 3.2. The <DOLLAR>
+ rule is defined in [RFC4512].
+
+ The LDAP definition for the Telex Number syntax is:
+
+ ( 1.3.6.1.4.1.1466.115.121.1.52 DESC 'Telex Number' )
+
+ This syntax corresponds to the TelexNumber ASN.1 type from [X.520].
+
+3.3.34. UTC Time
+
+ A value of the UTC Time syntax is a character string representing a
+ date and time to a precision of one minute or one second. The year
+ is given as a two-digit number. The LDAP-specific encoding of a
+ value of this syntax follows the format defined in [ASN.1] for the
+ UTCTime type and is described by the following ABNF:
+
+ UTCTime = year month day hour minute [ second ]
+ [ u-time-zone ]
+ u-time-zone = %x5A ; "Z"
+ / u-differential
+
+
+
+Legg Standards Track [Page 24]
+
+RFC 4517 LDAP: Syntaxes and Matching Rules June 2006
+
+
+ u-differential = ( MINUS / PLUS ) hour minute
+
+ The <year>, <month>, <day>, <hour>, <minute>, <second>, and <MINUS>
+ rules are defined in Section 3.3.13. The <PLUS> rule is defined in
+ [RFC4512].
+
+ The above ABNF allows character strings that do not represent valid
+ dates (in the Gregorian calendar) and/or valid times. Such character
+ strings SHOULD be considered invalid for this syntax.
+
+ The time value represents coordinated universal time if the "Z" form
+ of <u-time-zone> is used; otherwise, the value represents a local
+ time. In the latter case, if <u-differential> is provided, then
+ coordinated universal time can be calculated by subtracting the
+ differential from the local time. The <u-time-zone> SHOULD be
+ present in time values, and the "Z" form of <u-time-zone> SHOULD be
+ used in preference to <u-differential>.
+
+ The LDAP definition for the UTC Time syntax is:
+
+ ( 1.3.6.1.4.1.1466.115.121.1.53 DESC 'UTC Time' )
+
+ Note: This syntax is deprecated in favor of the Generalized Time
+ syntax.
+
+ The UTC Time syntax corresponds to the UTCTime ASN.1 type from
+ [ASN.1].
+
+4. Matching Rules
+
+ Matching rules are used by directory implementations to compare
+ attribute values against assertion values when performing Search and
+ Compare operations [RFC4511]. They are also used when comparing a
+ purported distinguished name [RFC4512] with the name of an entry.
+ When modifying entries, matching rules are used to identify values to
+ be deleted and to prevent an attribute from containing two equal
+ values.
+
+ Matching rules that are required for directory operation, or that are
+ in common use, are specified in this section.
+
+4.1. General Considerations
+
+ A matching rule is applied to attribute values through an
+ AttributeValueAssertion or MatchingRuleAssertion [RFC4511]. The
+ conditions under which an AttributeValueAssertion or
+ MatchingRuleAssertion evaluates to Undefined are specified elsewhere
+ [RFC4511]. If an assertion is not Undefined, then the result of the
+
+
+
+Legg Standards Track [Page 25]
+
+RFC 4517 LDAP: Syntaxes and Matching Rules June 2006
+
+
+ assertion is the result of applying the selected matching rule. A
+ matching rule evaluates to TRUE, and in some cases Undefined, as
+ specified in the description of the matching rule; otherwise, it
+ evaluates to FALSE.
+
+ Each assertion contains an assertion value. The definition of each
+ matching rule specifies the syntax for the assertion value. The
+ syntax of the assertion value is typically, but not necessarily, the
+ same as the syntax of the attribute values to which the matching rule
+ may be applied. Note that an AssertionValue in a SubstringFilter
+ [RFC4511] conforms to the assertion syntax of the equality matching
+ rule for the attribute type rather than to the assertion syntax of
+ the substrings matching rule for the attribute type. Conceptually,
+ the entire SubstringFilter is converted into an assertion value of
+ the substrings matching rule prior to applying the rule.
+
+ The definition of each matching rule indicates the attribute syntaxes
+ to which the rule may be applied, by specifying conditions the
+ corresponding ASN.1 type of a candidate attribute syntax must
+ satisfy. These conditions are also satisfied if the corresponding
+ ASN.1 type is a tagged or constrained derivative of the ASN.1 type
+ explicitly mentioned in the rule description (i.e., ASN.1 tags and
+ constraints are ignored in checking applicability), or is an
+ alternative reference notation for the explicitly mentioned type.
+ Each rule description lists, as examples of applicable attribute
+ syntaxes, the complete list of the syntaxes defined in this document
+ to which the matching rule applies. A matching rule may be
+ applicable to additional syntaxes defined in other documents if those
+ syntaxes satisfy the conditions on the corresponding ASN.1 type.
+
+ The description of each matching rule indicates whether the rule is
+ suitable for use as the equality matching rule (EQUALITY), ordering
+ matching rule (ORDERING), or substrings matching rule (SUBSTR) in an
+ attribute type definition [RFC4512].
+
+ Each matching rule is uniquely identified with an object identifier.
+ The definition of a matching rule should not subsequently be changed.
+ If a change is desirable, then a new matching rule with a different
+ object identifier should be defined instead.
+
+ Servers MAY implement the wordMatch and keywordMatch matching rules,
+ but they SHOULD implement the other matching rules in Section 4.2.
+ Servers MAY implement additional matching rules.
+
+ Servers that implement the extensibleMatch filter SHOULD allow the
+ matching rules listed in Section 4.2 to be used in the
+ extensibleMatch filter and SHOULD allow matching rules to be used
+ with all attribute types known to the server, where the assertion
+
+
+
+Legg Standards Track [Page 26]
+
+RFC 4517 LDAP: Syntaxes and Matching Rules June 2006
+
+
+ syntax of the matching rule is the same as the value syntax of the
+ attribute.
+
+ Servers MUST publish, in the matchingRules attribute, the definitions
+ of matching rules referenced by values of the attributeTypes and
+ matchingRuleUse attributes in the same subschema entry. Other
+ unreferenced matching rules MAY be published in the matchingRules
+ attribute.
+
+ If the server supports the extensibleMatch filter, then the server
+ MAY use the matchingRuleUse attribute to indicate the applicability
+ (in an extensibleMatch filter) of selected matching rules to
+ nominated attribute types.
+
+4.2. Matching Rule Definitions
+
+ Nominated character strings in assertion and attribute values are
+ prepared according to the string preparation algorithms [RFC4518] for
+ LDAP when evaluating the following matching rules:
+
+ numericStringMatch,
+ numericStringSubstringsMatch,
+ caseExactMatch,
+ caseExactOrderingMatch,
+ caseExactSubstringsMatch,
+ caseExactIA5Match,
+ caseIgnoreIA5Match,
+ caseIgnoreIA5SubstringsMatch,
+ caseIgnoreListMatch,
+ caseIgnoreListSubstringsMatch,
+ caseIgnoreMatch,
+ caseIgnoreOrderingMatch,
+ caseIgnoreSubstringsMatch,
+ directoryStringFirstComponentMatch,
+ telephoneNumberMatch,
+ telephoneNumberSubstringsMatch and
+ wordMatch.
+
+ The Transcode, Normalize, Prohibit, and Check bidi steps are the same
+ for each of the matching rules. However, the Map and Insignificant
+ Character Handling steps depend on the specific rule, as detailed in
+ the description of these matching rules in the sections that follow.
+
+4.2.1. bitStringMatch
+
+ The bitStringMatch rule compares an assertion value of the Bit String
+ syntax to an attribute value of a syntax (e.g., the Bit String
+ syntax) whose corresponding ASN.1 type is BIT STRING.
+
+
+
+Legg Standards Track [Page 27]
+
+RFC 4517 LDAP: Syntaxes and Matching Rules June 2006
+
+
+ If the corresponding ASN.1 type of the attribute syntax does not have
+ a named bit list [ASN.1] (which is the case for the Bit String
+ syntax), then the rule evaluates to TRUE if and only if the attribute
+ value has the same number of bits as the assertion value and the bits
+ match on a bitwise basis.
+
+ If the corresponding ASN.1 type does have a named bit list, then
+ bitStringMatch operates as above, except that trailing zero bits in
+ the attribute and assertion values are treated as absent.
+
+ The LDAP definition for the bitStringMatch rule is:
+
+ ( 2.5.13.16 NAME 'bitStringMatch'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.6 )
+
+ The bitStringMatch rule is an equality matching rule.
+
+4.2.2. booleanMatch
+
+ The booleanMatch rule compares an assertion value of the Boolean
+ syntax to an attribute value of a syntax (e.g., the Boolean syntax)
+ whose corresponding ASN.1 type is BOOLEAN.
+
+ The rule evaluates to TRUE if and only if the attribute value and the
+ assertion value are both TRUE or both FALSE.
+
+ The LDAP definition for the booleanMatch rule is:
+
+ ( 2.5.13.13 NAME 'booleanMatch'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 )
+
+ The booleanMatch rule is an equality matching rule.
+
+4.2.3. caseExactIA5Match
+
+ The caseExactIA5Match rule compares an assertion value of the IA5
+ String syntax to an attribute value of a syntax (e.g., the IA5 String
+ syntax) whose corresponding ASN.1 type is IA5String.
+
+ The rule evaluates to TRUE if and only if the prepared attribute
+ value character string and the prepared assertion value character
+ string have the same number of characters and corresponding
+ characters have the same code point.
+
+ In preparing the attribute value and assertion value for comparison,
+ characters are not case folded in the Map preparation step, and only
+ Insignificant Space Handling is applied in the Insignificant
+ Character Handling step.
+
+
+
+Legg Standards Track [Page 28]
+
+RFC 4517 LDAP: Syntaxes and Matching Rules June 2006
+
+
+ The LDAP definition for the caseExactIA5Match rule is:
+
+ ( 1.3.6.1.4.1.1466.109.114.1 NAME 'caseExactIA5Match'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+
+ The caseExactIA5Match rule is an equality matching rule.
+
+4.2.4. caseExactMatch
+
+ The caseExactMatch rule compares an assertion value of the Directory
+ String syntax to an attribute value of a syntax (e.g., the Directory
+ String, Printable String, Country String, or Telephone Number syntax)
+ whose corresponding ASN.1 type is DirectoryString or one of the
+ alternative string types of DirectoryString, such as PrintableString
+ (the other alternatives do not correspond to any syntax defined in
+ this document).
+
+ The rule evaluates to TRUE if and only if the prepared attribute
+ value character string and the prepared assertion value character
+ string have the same number of characters and corresponding
+ characters have the same code point.
+
+ In preparing the attribute value and assertion value for comparison,
+ characters are not case folded in the Map preparation step, and only
+ Insignificant Space Handling is applied in the Insignificant
+ Character Handling step.
+
+ The LDAP definition for the caseExactMatch rule is:
+
+ ( 2.5.13.5 NAME 'caseExactMatch'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+
+ The caseExactMatch rule is an equality matching rule.
+
+4.2.5. caseExactOrderingMatch
+
+ The caseExactOrderingMatch rule compares an assertion value of the
+ Directory String syntax to an attribute value of a syntax (e.g., the
+ Directory String, Printable String, Country String, or Telephone
+ Number syntax) whose corresponding ASN.1 type is DirectoryString or
+ one of its alternative string types.
+
+ The rule evaluates to TRUE if and only if, in the code point
+ collation order, the prepared attribute value character string
+ appears earlier than the prepared assertion value character string;
+ i.e., the attribute value is "less than" the assertion value.
+
+
+
+
+
+Legg Standards Track [Page 29]
+
+RFC 4517 LDAP: Syntaxes and Matching Rules June 2006
+
+
+ In preparing the attribute value and assertion value for comparison,
+ characters are not case folded in the Map preparation step, and only
+ Insignificant Space Handling is applied in the Insignificant
+ Character Handling step.
+
+ The LDAP definition for the caseExactOrderingMatch rule is:
+
+ ( 2.5.13.6 NAME 'caseExactOrderingMatch'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+
+ The caseExactOrderingMatch rule is an ordering matching rule.
+
+4.2.6. caseExactSubstringsMatch
+
+ The caseExactSubstringsMatch rule compares an assertion value of the
+ Substring Assertion syntax to an attribute value of a syntax (e.g.,
+ the Directory String, Printable String, Country String, or Telephone
+ Number syntax) whose corresponding ASN.1 type is DirectoryString or
+ one of its alternative string types.
+
+ The rule evaluates to TRUE if and only if (1) the prepared substrings
+ of the assertion value match disjoint portions of the prepared
+ attribute value character string in the order of the substrings in
+ the assertion value, (2) an <initial> substring, if present, matches
+ the beginning of the prepared attribute value character string, and
+ (3) a <final> substring, if present, matches the end of the prepared
+ attribute value character string. A prepared substring matches a
+ portion of the prepared attribute value character string if
+ corresponding characters have the same code point.
+
+ In preparing the attribute value and assertion value substrings for
+ comparison, characters are not case folded in the Map preparation
+ step, and only Insignificant Space Handling is applied in the
+ Insignificant Character Handling step.
+
+ The LDAP definition for the caseExactSubstringsMatch rule is:
+
+ ( 2.5.13.7 NAME 'caseExactSubstringsMatch'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )
+
+ The caseExactSubstringsMatch rule is a substrings matching rule.
+
+4.2.7. caseIgnoreIA5Match
+
+ The caseIgnoreIA5Match rule compares an assertion value of the IA5
+ String syntax to an attribute value of a syntax (e.g., the IA5 String
+ syntax) whose corresponding ASN.1 type is IA5String.
+
+
+
+
+Legg Standards Track [Page 30]
+
+RFC 4517 LDAP: Syntaxes and Matching Rules June 2006
+
+
+ The rule evaluates to TRUE if and only if the prepared attribute
+ value character string and the prepared assertion value character
+ string have the same number of characters and corresponding
+ characters have the same code point.
+
+ In preparing the attribute value and assertion value for comparison,
+ characters are case folded in the Map preparation step, and only
+ Insignificant Space Handling is applied in the Insignificant
+ Character Handling step.
+
+ The LDAP definition for the caseIgnoreIA5Match rule is:
+
+ ( 1.3.6.1.4.1.1466.109.114.2 NAME 'caseIgnoreIA5Match'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+
+ The caseIgnoreIA5Match rule is an equality matching rule.
+
+4.2.8. caseIgnoreIA5SubstringsMatch
+
+ The caseIgnoreIA5SubstringsMatch rule compares an assertion value of
+ the Substring Assertion syntax to an attribute value of a syntax
+ (e.g., the IA5 String syntax) whose corresponding ASN.1 type is
+ IA5String.
+
+ The rule evaluates to TRUE if and only if (1) the prepared substrings
+ of the assertion value match disjoint portions of the prepared
+ attribute value character string in the order of the substrings in
+ the assertion value, (2) an <initial> substring, if present, matches
+ the beginning of the prepared attribute value character string, and
+ (3) a <final> substring, if present, matches the end of the prepared
+ attribute value character string. A prepared substring matches a
+ portion of the prepared attribute value character string if
+ corresponding characters have the same code point.
+
+ In preparing the attribute value and assertion value substrings for
+ comparison, characters are case folded in the Map preparation step,
+ and only Insignificant Space Handling is applied in the Insignificant
+ Character Handling step.
+
+ ( 1.3.6.1.4.1.1466.109.114.3 NAME 'caseIgnoreIA5SubstringsMatch'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )
+
+ The caseIgnoreIA5SubstringsMatch rule is a substrings matching rule.
+
+4.2.9. caseIgnoreListMatch
+
+ The caseIgnoreListMatch rule compares an assertion value that is a
+ sequence of strings to an attribute value of a syntax (e.g., the
+
+
+
+Legg Standards Track [Page 31]
+
+RFC 4517 LDAP: Syntaxes and Matching Rules June 2006
+
+
+ Postal Address syntax) whose corresponding ASN.1 type is a SEQUENCE
+ OF the DirectoryString ASN.1 type.
+
+ The rule evaluates to TRUE if and only if the attribute value and the
+ assertion value have the same number of strings and corresponding
+ strings (by position) match according to the caseIgnoreMatch matching
+ rule.
+
+ In [X.520], the assertion syntax for this matching rule is defined to
+ be:
+
+ SEQUENCE OF DirectoryString {ub-match}
+
+ That is, it is different from the corresponding type for the Postal
+ Address syntax. The choice of the Postal Address syntax for the
+ assertion syntax of the caseIgnoreListMatch in LDAP should not be
+ seen as limiting the matching rule to apply only to attributes with
+ the Postal Address syntax.
+
+ The LDAP definition for the caseIgnoreListMatch rule is:
+
+ ( 2.5.13.11 NAME 'caseIgnoreListMatch'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.41 )
+
+ The caseIgnoreListMatch rule is an equality matching rule.
+
+4.2.10. caseIgnoreListSubstringsMatch
+
+ The caseIgnoreListSubstringsMatch rule compares an assertion value of
+ the Substring Assertion syntax to an attribute value of a syntax
+ (e.g., the Postal Address syntax) whose corresponding ASN.1 type is a
+ SEQUENCE OF the DirectoryString ASN.1 type.
+
+ The rule evaluates to TRUE if and only if the assertion value
+ matches, per the caseIgnoreSubstringsMatch rule, the character string
+ formed by concatenating the strings of the attribute value, except
+ that none of the <initial>, <any>, or <final> substrings of the
+ assertion value are considered to match a substring of the
+ concatenated string which spans more than one of the original strings
+ of the attribute value.
+
+ Note that, in terms of the LDAP-specific encoding of the Postal
+ Address syntax, the concatenated string omits the <DOLLAR> line
+ separator and the escaping of "\" and "$" characters.
+
+ The LDAP definition for the caseIgnoreListSubstringsMatch rule is:
+
+ ( 2.5.13.12 NAME 'caseIgnoreListSubstringsMatch'
+
+
+
+Legg Standards Track [Page 32]
+
+RFC 4517 LDAP: Syntaxes and Matching Rules June 2006
+
+
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )
+
+ The caseIgnoreListSubstringsMatch rule is a substrings matching rule.
+
+4.2.11. caseIgnoreMatch
+
+ The caseIgnoreMatch rule compares an assertion value of the Directory
+ String syntax to an attribute value of a syntax (e.g., the Directory
+ String, Printable String, Country String, or Telephone Number syntax)
+ whose corresponding ASN.1 type is DirectoryString or one of its
+ alternative string types.
+
+ The rule evaluates to TRUE if and only if the prepared attribute
+ value character string and the prepared assertion value character
+ string have the same number of characters and corresponding
+ characters have the same code point.
+
+ In preparing the attribute value and assertion value for comparison,
+ characters are case folded in the Map preparation step, and only
+ Insignificant Space Handling is applied in the Insignificant
+ Character Handling step.
+
+ The LDAP definition for the caseIgnoreMatch rule is:
+
+ ( 2.5.13.2 NAME 'caseIgnoreMatch'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+
+ The caseIgnoreMatch rule is an equality matching rule.
+
+4.2.12. caseIgnoreOrderingMatch
+
+ The caseIgnoreOrderingMatch rule compares an assertion value of the
+ Directory String syntax to an attribute value of a syntax (e.g., the
+ Directory String, Printable String, Country String, or Telephone
+ Number syntax) whose corresponding ASN.1 type is DirectoryString or
+ one of its alternative string types.
+
+ The rule evaluates to TRUE if and only if, in the code point
+ collation order, the prepared attribute value character string
+ appears earlier than the prepared assertion value character string;
+ i.e., the attribute value is "less than" the assertion value.
+
+ In preparing the attribute value and assertion value for comparison,
+ characters are case folded in the Map preparation step, and only
+ Insignificant Space Handling is applied in the Insignificant
+ Character Handling step.
+
+ The LDAP definition for the caseIgnoreOrderingMatch rule is:
+
+
+
+Legg Standards Track [Page 33]
+
+RFC 4517 LDAP: Syntaxes and Matching Rules June 2006
+
+
+ ( 2.5.13.3 NAME 'caseIgnoreOrderingMatch'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+
+ The caseIgnoreOrderingMatch rule is an ordering matching rule.
+
+4.2.13. caseIgnoreSubstringsMatch
+
+ The caseIgnoreSubstringsMatch rule compares an assertion value of the
+ Substring Assertion syntax to an attribute value of a syntax (e.g.,
+ the Directory String, Printable String, Country String, or Telephone
+ Number syntax) whose corresponding ASN.1 type is DirectoryString or
+ one of its alternative string types.
+
+ The rule evaluates to TRUE if and only if (1) the prepared substrings
+ of the assertion value match disjoint portions of the prepared
+ attribute value character string in the order of the substrings in
+ the assertion value, (2) an <initial> substring, if present, matches
+ the beginning of the prepared attribute value character string, and
+ (3) a <final> substring, if present, matches the end of the prepared
+ attribute value character string. A prepared substring matches a
+ portion of the prepared attribute value character string if
+ corresponding characters have the same code point.
+
+ In preparing the attribute value and assertion value substrings for
+ comparison, characters are case folded in the Map preparation step,
+ and only Insignificant Space Handling is applied in the Insignificant
+ Character Handling step.
+
+ The LDAP definition for the caseIgnoreSubstringsMatch rule is:
+
+ ( 2.5.13.4 NAME 'caseIgnoreSubstringsMatch'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )
+
+ The caseIgnoreSubstringsMatch rule is a substrings matching rule.
+
+4.2.14. directoryStringFirstComponentMatch
+
+ The directoryStringFirstComponentMatch rule compares an assertion
+ value of the Directory String syntax to an attribute value of a
+ syntax whose corresponding ASN.1 type is a SEQUENCE with a mandatory
+ first component of the DirectoryString ASN.1 type.
+
+ Note that the assertion syntax of this matching rule differs from the
+ attribute syntax of attributes for which this is the equality
+ matching rule.
+
+
+
+
+
+
+Legg Standards Track [Page 34]
+
+RFC 4517 LDAP: Syntaxes and Matching Rules June 2006
+
+
+ The rule evaluates to TRUE if and only if the assertion value matches
+ the first component of the attribute value using the rules of
+ caseIgnoreMatch.
+
+ The LDAP definition for the directoryStringFirstComponentMatch
+ matching rule is:
+
+ ( 2.5.13.31 NAME 'directoryStringFirstComponentMatch'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+
+ The directoryStringFirstComponentMatch rule is an equality matching
+ rule. When using directoryStringFirstComponentMatch to compare two
+ attribute values (of an applicable syntax), an assertion value must
+ first be derived from one of the attribute values. An assertion
+ value can be derived from an attribute value by taking the first
+ component of that attribute value.
+
+4.2.15. distinguishedNameMatch
+
+ The distinguishedNameMatch rule compares an assertion value of the DN
+ syntax to an attribute value of a syntax (e.g., the DN syntax) whose
+ corresponding ASN.1 type is DistinguishedName.
+
+ The rule evaluates to TRUE if and only if the attribute value and the
+ assertion value have the same number of relative distinguished names
+ and corresponding relative distinguished names (by position) are the
+ same. A relative distinguished name (RDN) of the assertion value is
+ the same as an RDN of the attribute value if and only if they have
+ the same number of attribute value assertions and each attribute
+ value assertion (AVA) of the first RDN is the same as the AVA of the
+ second RDN with the same attribute type. The order of the AVAs is
+ not significant. Also note that a particular attribute type may
+ appear in at most one AVA in an RDN. Two AVAs with the same
+ attribute type are the same if their values are equal according to
+ the equality matching rule of the attribute type. If one or more of
+ the AVA comparisons evaluate to Undefined and the remaining AVA
+ comparisons return TRUE then the distinguishedNameMatch rule
+ evaluates to Undefined.
+
+ The LDAP definition for the distinguishedNameMatch rule is:
+
+ ( 2.5.13.1 NAME 'distinguishedNameMatch'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+
+ The distinguishedNameMatch rule is an equality matching rule.
+
+
+
+
+
+
+Legg Standards Track [Page 35]
+
+RFC 4517 LDAP: Syntaxes and Matching Rules June 2006
+
+
+4.2.16. generalizedTimeMatch
+
+ The generalizedTimeMatch rule compares an assertion value of the
+ Generalized Time syntax to an attribute value of a syntax (e.g., the
+ Generalized Time syntax) whose corresponding ASN.1 type is
+ GeneralizedTime.
+
+ The rule evaluates to TRUE if and only if the attribute value
+ represents the same universal coordinated time as the assertion
+ value. If a time is specified with the minutes or seconds absent,
+ then the number of minutes or seconds (respectively) is assumed to be
+ zero.
+
+ The LDAP definition for the generalizedTimeMatch rule is:
+
+ ( 2.5.13.27 NAME 'generalizedTimeMatch'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 )
+
+ The generalizedTimeMatch rule is an equality matching rule.
+
+4.2.17. generalizedTimeOrderingMatch
+
+ The generalizedTimeOrderingMatch rule compares the time ordering of
+ an assertion value of the Generalized Time syntax to an attribute
+ value of a syntax (e.g., the Generalized Time syntax) whose
+ corresponding ASN.1 type is GeneralizedTime.
+
+ The rule evaluates to TRUE if and only if the attribute value
+ represents a universal coordinated time that is earlier than the
+ universal coordinated time represented by the assertion value.
+
+ The LDAP definition for the generalizedTimeOrderingMatch rule is:
+
+ ( 2.5.13.28 NAME 'generalizedTimeOrderingMatch'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 )
+
+ The generalizedTimeOrderingMatch rule is an ordering matching rule.
+
+4.2.18. integerFirstComponentMatch
+
+ The integerFirstComponentMatch rule compares an assertion value of
+ the Integer syntax to an attribute value of a syntax (e.g., the DIT
+ Structure Rule Description syntax) whose corresponding ASN.1 type is
+ a SEQUENCE with a mandatory first component of the INTEGER ASN.1
+ type.
+
+
+
+
+
+
+Legg Standards Track [Page 36]
+
+RFC 4517 LDAP: Syntaxes and Matching Rules June 2006
+
+
+ Note that the assertion syntax of this matching rule differs from the
+ attribute syntax of attributes for which this is the equality
+ matching rule.
+
+ The rule evaluates to TRUE if and only if the assertion value and the
+ first component of the attribute value are the same integer value.
+
+ The LDAP definition for the integerFirstComponentMatch matching rule
+ is:
+
+ ( 2.5.13.29 NAME 'integerFirstComponentMatch'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )
+
+ The integerFirstComponentMatch rule is an equality matching rule.
+ When using integerFirstComponentMatch to compare two attribute values
+ (of an applicable syntax), an assertion value must first be derived
+ from one of the attribute values. An assertion value can be derived
+ from an attribute value by taking the first component of that
+ attribute value.
+
+4.2.19. integerMatch
+
+ The integerMatch rule compares an assertion value of the Integer
+ syntax to an attribute value of a syntax (e.g., the Integer syntax)
+ whose corresponding ASN.1 type is INTEGER.
+
+ The rule evaluates to TRUE if and only if the attribute value and the
+ assertion value are the same integer value.
+
+ The LDAP definition for the integerMatch matching rule is:
+
+ ( 2.5.13.14 NAME 'integerMatch'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )
+
+ The integerMatch rule is an equality matching rule.
+
+4.2.20. integerOrderingMatch
+
+ The integerOrderingMatch rule compares an assertion value of the
+ Integer syntax to an attribute value of a syntax (e.g., the Integer
+ syntax) whose corresponding ASN.1 type is INTEGER.
+
+ The rule evaluates to TRUE if and only if the integer value of the
+ attribute value is less than the integer value of the assertion
+ value.
+
+ The LDAP definition for the integerOrderingMatch matching rule is:
+
+
+
+
+Legg Standards Track [Page 37]
+
+RFC 4517 LDAP: Syntaxes and Matching Rules June 2006
+
+
+ ( 2.5.13.15 NAME 'integerOrderingMatch'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )
+
+ The integerOrderingMatch rule is an ordering matching rule.
+
+4.2.21. keywordMatch
+
+ The keywordMatch rule compares an assertion value of the Directory
+ String syntax to an attribute value of a syntax (e.g., the Directory
+ String syntax) whose corresponding ASN.1 type is DirectoryString.
+
+ The rule evaluates to TRUE if and only if the assertion value
+ character string matches any keyword in the attribute value. The
+ identification of keywords in the attribute value and the exactness
+ of the match are both implementation specific.
+
+ The LDAP definition for the keywordMatch rule is:
+
+ ( 2.5.13.33 NAME 'keywordMatch'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+
+4.2.22. numericStringMatch
+
+ The numericStringMatch rule compares an assertion value of the
+ Numeric String syntax to an attribute value of a syntax (e.g., the
+ Numeric String syntax) whose corresponding ASN.1 type is
+ NumericString.
+
+ The rule evaluates to TRUE if and only if the prepared attribute
+ value character string and the prepared assertion value character
+ string have the same number of characters and corresponding
+ characters have the same code point.
+
+ In preparing the attribute value and assertion value for comparison,
+ characters are not case folded in the Map preparation step, and only
+ numericString Insignificant Character Handling is applied in the
+ Insignificant Character Handling step.
+
+ The LDAP definition for the numericStringMatch matching rule is:
+
+ ( 2.5.13.8 NAME 'numericStringMatch'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.36 )
+
+ The numericStringMatch rule is an equality matching rule.
+
+
+
+
+
+
+
+Legg Standards Track [Page 38]
+
+RFC 4517 LDAP: Syntaxes and Matching Rules June 2006
+
+
+4.2.23. numericStringOrderingMatch
+
+ The numericStringOrderingMatch rule compares an assertion value of
+ the Numeric String syntax to an attribute value of a syntax (e.g.,
+ the Numeric String syntax) whose corresponding ASN.1 type is
+ NumericString.
+
+ The rule evaluates to TRUE if and only if, in the code point
+ collation order, the prepared attribute value character string
+ appears earlier than the prepared assertion value character string;
+ i.e., the attribute value is "less than" the assertion value.
+
+ In preparing the attribute value and assertion value for comparison,
+ characters are not case folded in the Map preparation step, and only
+ numericString Insignificant Character Handling is applied in the
+ Insignificant Character Handling step.
+
+ The rule is identical to the caseIgnoreOrderingMatch rule except that
+ all space characters are skipped during comparison (case is
+ irrelevant as the characters are numeric).
+
+ The LDAP definition for the numericStringOrderingMatch matching rule
+ is:
+
+ ( 2.5.13.9 NAME 'numericStringOrderingMatch'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.36 )
+
+ The numericStringOrderingMatch rule is an ordering matching rule.
+
+4.2.24. numericStringSubstringsMatch
+
+ The numericStringSubstringsMatch rule compares an assertion value of
+ the Substring Assertion syntax to an attribute value of a syntax
+ (e.g., the Numeric String syntax) whose corresponding ASN.1 type is
+ NumericString.
+
+ The rule evaluates to TRUE if and only if (1) the prepared substrings
+ of the assertion value match disjoint portions of the prepared
+ attribute value character string in the order of the substrings in
+ the assertion value, (2) an <initial> substring, if present, matches
+ the beginning of the prepared attribute value character string, and
+ (3) a <final> substring, if present, matches the end of the prepared
+ attribute value character string. A prepared substring matches a
+ portion of the prepared attribute value character string if
+ corresponding characters have the same code point.
+
+ In preparing the attribute value and assertion value for comparison,
+ characters are not case folded in the Map preparation step, and only
+
+
+
+Legg Standards Track [Page 39]
+
+RFC 4517 LDAP: Syntaxes and Matching Rules June 2006
+
+
+ numericString Insignificant Character Handling is applied in the
+ Insignificant Character Handling step.
+
+ The LDAP definition for the numericStringSubstringsMatch matching
+ rule is:
+
+ ( 2.5.13.10 NAME 'numericStringSubstringsMatch'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )
+
+ The numericStringSubstringsMatch rule is a substrings matching rule.
+
+4.2.25. objectIdentifierFirstComponentMatch
+
+ The objectIdentifierFirstComponentMatch rule compares an assertion
+ value of the OID syntax to an attribute value of a syntax (e.g., the
+ Attribute Type Description, DIT Content Rule Description, LDAP Syntax
+ Description, Matching Rule Description, Matching Rule Use
+ Description, Name Form Description, or Object Class Description
+ syntax) whose corresponding ASN.1 type is a SEQUENCE with a mandatory
+ first component of the OBJECT IDENTIFIER ASN.1 type.
+
+ Note that the assertion syntax of this matching rule differs from the
+ attribute syntax of attributes for which this is the equality
+ matching rule.
+
+ The rule evaluates to TRUE if and only if the assertion value matches
+ the first component of the attribute value using the rules of
+ objectIdentifierMatch.
+
+ The LDAP definition for the objectIdentifierFirstComponentMatch
+ matching rule is:
+
+ ( 2.5.13.30 NAME 'objectIdentifierFirstComponentMatch'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )
+
+ The objectIdentifierFirstComponentMatch rule is an equality matching
+ rule. When using objectIdentifierFirstComponentMatch to compare two
+ attribute values (of an applicable syntax), an assertion value must
+ first be derived from one of the attribute values. An assertion
+ value can be derived from an attribute value by taking the first
+ component of that attribute value.
+
+4.2.26. objectIdentifierMatch
+
+ The objectIdentifierMatch rule compares an assertion value of the OID
+ syntax to an attribute value of a syntax (e.g., the OID syntax) whose
+ corresponding ASN.1 type is OBJECT IDENTIFIER.
+
+
+
+
+Legg Standards Track [Page 40]
+
+RFC 4517 LDAP: Syntaxes and Matching Rules June 2006
+
+
+ The rule evaluates to TRUE if and only if the assertion value and the
+ attribute value represent the same object identifier; that is, the
+ same sequence of integers, whether represented explicitly in the
+ <numericoid> form of <oid> or implicitly in the <descr> form (see
+ [RFC4512]).
+
+ If an LDAP client supplies an assertion value in the <descr> form and
+ the chosen descriptor is not recognized by the server, then the
+ objectIdentifierMatch rule evaluates to Undefined.
+
+ The LDAP definition for the objectIdentifierMatch matching rule is:
+
+ ( 2.5.13.0 NAME 'objectIdentifierMatch'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )
+
+ The objectIdentifierMatch rule is an equality matching rule.
+
+4.2.27. octetStringMatch
+
+ The octetStringMatch rule compares an assertion value of the Octet
+ String syntax to an attribute value of a syntax (e.g., the Octet
+ String or JPEG syntax) whose corresponding ASN.1 type is the OCTET
+ STRING ASN.1 type.
+
+ The rule evaluates to TRUE if and only if the attribute value and the
+ assertion value are the same length and corresponding octets (by
+ position) are the same.
+
+ The LDAP definition for the octetStringMatch matching rule is:
+
+ ( 2.5.13.17 NAME 'octetStringMatch'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )
+
+ The octetStringMatch rule is an equality matching rule.
+
+4.2.28. octetStringOrderingMatch
+
+ The octetStringOrderingMatch rule compares an assertion value of the
+ Octet String syntax to an attribute value of a syntax (e.g., the
+ Octet String or JPEG syntax) whose corresponding ASN.1 type is the
+ OCTET STRING ASN.1 type.
+
+ The rule evaluates to TRUE if and only if the attribute value appears
+ earlier in the collation order than the assertion value. The rule
+ compares octet strings from the first octet to the last octet, and
+ from the most significant bit to the least significant bit within the
+ octet. The first occurrence of a different bit determines the
+ ordering of the strings. A zero bit precedes a one bit. If the
+
+
+
+Legg Standards Track [Page 41]
+
+RFC 4517 LDAP: Syntaxes and Matching Rules June 2006
+
+
+ strings contain different numbers of octets but the longer string is
+ identical to the shorter string up to the length of the shorter
+ string, then the shorter string precedes the longer string.
+
+ The LDAP definition for the octetStringOrderingMatch matching rule
+ is:
+
+ ( 2.5.13.18 NAME 'octetStringOrderingMatch'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )
+
+ The octetStringOrderingMatch rule is an ordering matching rule.
+
+4.2.29. telephoneNumberMatch
+
+ The telephoneNumberMatch rule compares an assertion value of the
+ Telephone Number syntax to an attribute value of a syntax (e.g., the
+ Telephone Number syntax) whose corresponding ASN.1 type is a
+ PrintableString representing a telephone number.
+
+ The rule evaluates to TRUE if and only if the prepared attribute
+ value character string and the prepared assertion value character
+ string have the same number of characters and corresponding
+ characters have the same code point.
+
+ In preparing the attribute value and assertion value for comparison,
+ characters are case folded in the Map preparation step, and only
+ telephoneNumber Insignificant Character Handling is applied in the
+ Insignificant Character Handling step.
+
+ The LDAP definition for the telephoneNumberMatch matching rule is:
+
+ ( 2.5.13.20 NAME 'telephoneNumberMatch'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.50 )
+
+ The telephoneNumberMatch rule is an equality matching rule.
+
+4.2.30. telephoneNumberSubstringsMatch
+
+ The telephoneNumberSubstringsMatch rule compares an assertion value
+ of the Substring Assertion syntax to an attribute value of a syntax
+ (e.g., the Telephone Number syntax) whose corresponding ASN.1 type is
+ a PrintableString representing a telephone number.
+
+ The rule evaluates to TRUE if and only if (1) the prepared substrings
+ of the assertion value match disjoint portions of the prepared
+ attribute value character string in the order of the substrings in
+ the assertion value, (2) an <initial> substring, if present, matches
+ the beginning of the prepared attribute value character string, and
+
+
+
+Legg Standards Track [Page 42]
+
+RFC 4517 LDAP: Syntaxes and Matching Rules June 2006
+
+
+ (3) a <final> substring, if present, matches the end of the prepared
+ attribute value character string. A prepared substring matches a
+ portion of the prepared attribute value character string if
+ corresponding characters have the same code point.
+
+ In preparing the attribute value and assertion value substrings for
+ comparison, characters are case folded in the Map preparation step,
+ and only telephoneNumber Insignificant Character Handling is applied
+ in the Insignificant Character Handling step.
+
+ The LDAP definition for the telephoneNumberSubstringsMatch matching
+ rule is:
+
+ ( 2.5.13.21 NAME 'telephoneNumberSubstringsMatch'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )
+
+ The telephoneNumberSubstringsMatch rule is a substrings matching
+ rule.
+
+4.2.31. uniqueMemberMatch
+
+ The uniqueMemberMatch rule compares an assertion value of the Name
+ And Optional UID syntax to an attribute value of a syntax (e.g., the
+ Name And Optional UID syntax) whose corresponding ASN.1 type is
+ NameAndOptionalUID.
+
+ The rule evaluates to TRUE if and only if the <distinguishedName>
+ components of the assertion value and attribute value match according
+ to the distinguishedNameMatch rule and either, (1) the <BitString>
+ component is absent from both the attribute value and assertion
+ value, or (2) the <BitString> component is present in both the
+ attribute value and the assertion value and the <BitString> component
+ of the assertion value matches the <BitString> component of the
+ attribute value according to the bitStringMatch rule.
+
+ Note that this matching rule has been altered from its description in
+ X.520 [X.520] in order to make the matching rule commutative. Server
+ implementors should consider using the original X.520 semantics
+ (where the matching was less exact) for approximate matching of
+ attributes with uniqueMemberMatch as the equality matching rule.
+
+ The LDAP definition for the uniqueMemberMatch matching rule is:
+
+ ( 2.5.13.23 NAME 'uniqueMemberMatch'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.34 )
+
+ The uniqueMemberMatch rule is an equality matching rule.
+
+
+
+
+Legg Standards Track [Page 43]
+
+RFC 4517 LDAP: Syntaxes and Matching Rules June 2006
+
+
+4.2.32. wordMatch
+
+ The wordMatch rule compares an assertion value of the Directory
+ String syntax to an attribute value of a syntax (e.g., the Directory
+ String syntax) whose corresponding ASN.1 type is DirectoryString.
+
+ The rule evaluates to TRUE if and only if the assertion value word
+ matches, according to the semantics of caseIgnoreMatch, any word in
+ the attribute value. The precise definition of a word is
+ implementation specific.
+
+ The LDAP definition for the wordMatch rule is:
+
+ ( 2.5.13.32 NAME 'wordMatch'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+
+5. Security Considerations
+
+ In general, the LDAP-specific encodings for syntaxes defined in this
+ document do not define canonical encodings. That is, a
+ transformation from an LDAP-specific encoding into some other
+ encoding (e.g., BER) and back into the LDAP-specific encoding will
+ not necessarily reproduce exactly the original octets of the LDAP-
+ specific encoding. Therefore, an LDAP-specific encoding should not
+ be used where a canonical encoding is required.
+
+ Furthermore, the LDAP-specific encodings do not necessarily enable an
+ alternative encoding of values of the Directory String and DN
+ syntaxes to be reconstructed; e.g., a transformation from a
+ Distinguished Encoding Rules (DER) [BER] encoding to an LDAP-specific
+ encoding and back to a DER encoding may not reproduce the original
+ DER encoding. Therefore, LDAP-specific encodings should not be used
+ where reversibility to DER is needed; e.g., for the verification of
+ digital signatures. Instead, DER or a DER-reversible encoding should
+ be used.
+
+ When interpreting security-sensitive fields (in particular, fields
+ used to grant or deny access), implementations MUST ensure that any
+ matching rule comparisons are done on the underlying abstract value,
+ regardless of the particular encoding used.
+
+6. Acknowledgements
+
+ This document is primarily a revision of RFC 2252 by M. Wahl, A.
+ Coulbeck, T. Howes, and S. Kille. RFC 2252 was a product of the IETF
+ ASID Working Group.
+
+
+
+
+
+Legg Standards Track [Page 44]
+
+RFC 4517 LDAP: Syntaxes and Matching Rules June 2006
+
+
+ This document is based on input from the IETF LDAPBIS working group.
+ The author would like to thank Kathy Dally for editing the early
+ drafts of this document, and Jim Sermersheim and Kurt Zeilenga for
+ their significant contributions to this revision.
+
+7. IANA Considerations
+
+ The Internet Assigned Numbers Authority (IANA) has updated the LDAP
+ descriptors registry [BCP64] as indicated by the following templates:
+
+ Subject: Request for LDAP Descriptor Registration Update
+ Descriptor (short name): see comment
+ Object Identifier: see comment
+ Person & email address to contact for further information:
+ Steven Legg <steven.legg@eb2bcom.com>
+ Usage: see comment
+ Specification: RFC 4517
+ Author/Change Controller: IESG
+
+ NAME Type OID
+ ------------------------------------------------------------------
+ bitStringMatch M 2.5.13.16
+ booleanMatch M 2.5.13.13
+ caseExactIA5Match M 1.3.6.1.4.1.1466.109.114.1
+ caseExactMatch M 2.5.13.5
+ caseExactOrderingMatch M 2.5.13.6
+ caseExactSubstringsMatch M 2.5.13.7
+ caseIgnoreIA5Match M 1.3.6.1.4.1.1466.109.114.2
+ caseIgnoreListMatch M 2.5.13.11
+ caseIgnoreListSubstringsMatch M 2.5.13.12
+ caseIgnoreMatch M 2.5.13.2
+ caseIgnoreOrderingMatch M 2.5.13.3
+ caseIgnoreSubstringsMatch M 2.5.13.4
+ directoryStringFirstComponentMatch M 2.5.13.31
+ distinguishedNameMatch M 2.5.13.1
+ generalizedTimeMatch M 2.5.13.27
+ generalizedTimeOrderingMatch M 2.5.13.28
+ integerFirstComponentMatch M 2.5.13.29
+ integerMatch M 2.5.13.14
+ integerOrderingMatch M 2.5.13.15
+ keywordMatch M 2.5.13.33
+ numericStringMatch M 2.5.13.8
+ numericStringOrderingMatch M 2.5.13.9
+ numericStringSubstringsMatch M 2.5.13.10
+ objectIdentifierFirstComponentMatch M 2.5.13.30
+ octetStringMatch M 2.5.13.17
+ octetStringOrderingMatch M 2.5.13.18
+ telephoneNumberMatch M 2.5.13.20
+
+
+
+Legg Standards Track [Page 45]
+
+RFC 4517 LDAP: Syntaxes and Matching Rules June 2006
+
+
+ telephoneNumberSubstringsMatch M 2.5.13.21
+ uniqueMemberMatch M 2.5.13.23
+ wordMatch M 2.5.13.32
+
+ The descriptor for the object identifier 2.5.13.0 was incorrectly
+ registered as objectIdentifiersMatch (extraneous \`s') in BCP 64.
+ It has been changed to the following, with a reference to
+ RFC 4517.
+
+ NAME Type OID
+ ------------------------------------------------------------------
+ objectIdentifierMatch M 2.5.13.0
+
+ Subject: Request for LDAP Descriptor Registration
+ Descriptor (short name): caseIgnoreIA5SubstringsMatch
+ Object Identifier: 1.3.6.1.4.1.1466.109.114.3
+ Person & email address to contact for further information:
+ Steven Legg <steven.legg@eb2bcom.com>
+ Usage: other (M)
+ Specification: RFC 4517
+ Author/Change Controller: IESG
+
+8. References
+
+8.1. Normative References
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [RFC3629] Yergeau, F., "UTF-8, a transformation format of ISO
+ 10646", STD 63, RFC 3629, November 2003.
+
+ [RFC4234] Crocker, D., Ed. and P. Overell, "Augmented BNF for Syntax
+ Specifications: ABNF", RFC 4234, October 2005.
+
+ [RFC4510] Zeilenga, K., Ed., "Lightweight Directory Access Protocol
+ (LDAP): Technical Specification Road Map", RFC 4510, June
+ 2006.
+
+ [RFC4511] Sermersheim, J., Ed., "Lightweight Directory Access
+ Protocol (LDAP): The Protocol", RFC 4511, June 2006.
+
+ [RFC4512] Zeilenga, K., "Lightweight Directory Access Protocol
+ (LDAP): Directory Information Models", RFC 4512, June
+ 2006.
+
+
+
+
+
+
+Legg Standards Track [Page 46]
+
+RFC 4517 LDAP: Syntaxes and Matching Rules June 2006
+
+
+ [RFC4514] Zeilenga, K., Ed., "Lightweight Directory Access Protocol
+ (LDAP): String Representation of Distinguished Names", RFC
+ 4514, June 2006.
+
+ [RFC4518] Zeilenga, K., "Lightweight Directory Access Protocol
+ (LDAP): Internationalized String Preparation", RFC 4518,
+ June 2006.
+
+ [RFC4520] Zeilenga, K., "Internet Assigned Numbers Authority (IANA)
+ Considerations for the Lightweight Directory Access
+ Protocol (LDAP)", BCP 64, RFC 4520, June 2006.
+
+ [E.123] Notation for national and international telephone numbers,
+ ITU-T Recommendation E.123, 1988.
+
+ [FAX] Standardization of Group 3 facsimile apparatus for
+ document transmission - Terminal Equipment and Protocols
+ for Telematic Services, ITU-T Recommendation T.4, 1993
+
+ [T.50] International Reference Alphabet (IRA) (Formerly
+ International Alphabet No. 5 or IA5) Information
+ Technology - 7-Bit Coded Character Set for Information
+ Interchange, ITU-T Recommendation T.50, 1992
+
+ [X.420] ITU-T Recommendation X.420 (1996) | ISO/IEC 10021-7:1997,
+ Information Technology - Message Handling Systems (MHS):
+ Interpersonal messaging system
+
+ [X.501] ITU-T Recommendation X.501 (1993) | ISO/IEC 9594-2:1994,
+ Information Technology - Open Systems Interconnection -
+ The Directory: Models
+
+ [X.520] ITU-T Recommendation X.520 (1993) | ISO/IEC 9594-6:1994,
+ Information Technology - Open Systems Interconnection -
+ The Directory: Selected attribute types
+
+ [ASN.1] ITU-T Recommendation X.680 (07/02) | ISO/IEC 8824-1:2002,
+ Information technology - Abstract Syntax Notation One
+ (ASN.1): Specification of basic notation
+
+ [ISO3166] ISO 3166, "Codes for the representation of names of
+ countries".
+
+ [ISO8601] ISO 8601:2004, "Data elements and interchange formats --
+ Information interchange -- Representation of dates and
+ times".
+
+
+
+
+
+Legg Standards Track [Page 47]
+
+RFC 4517 LDAP: Syntaxes and Matching Rules June 2006
+
+
+ [UCS] Universal Multiple-Octet Coded Character Set (UCS) -
+ Architecture and Basic Multilingual Plane, ISO/IEC 10646-
+ 1: 1993 (with amendments).
+
+ [JPEG] JPEG File Interchange Format (Version 1.02). Eric
+ Hamilton, C-Cube Microsystems, Milpitas, CA, September 1,
+ 1992.
+
+8.2. Informative References
+
+ [RFC4519] Sciberras, A., Ed., "Lightweight Directory Access Protocol
+ (LDAP): Schema for User Applications", RFC 4519, June
+ 2006.
+
+ [RFC4523] Zeilenga, K., "Lightweight Directory Access Protocol
+ (LDAP) Schema Definitions for X.509 Certificates", RFC
+ 4523, June 2006.
+
+ [X.500] ITU-T Recommendation X.500 (1993) | ISO/IEC 9594-1:1994,
+ Information Technology - Open Systems Interconnection -
+ The Directory: Overview of concepts, models and services
+
+ [BER] ITU-T Recommendation X.690 (07/02) | ISO/IEC 8825-1:2002,
+ Information technology - ASN.1 encoding rules:
+ Specification of Basic Encoding Rules (BER), Canonical
+ Encoding Rules (CER) and Distinguished Encoding Rules
+ (DER)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Legg Standards Track [Page 48]
+
+RFC 4517 LDAP: Syntaxes and Matching Rules June 2006
+
+
+Appendix A. Summary of Syntax Object Identifiers
+
+ The following list summarizes the object identifiers assigned to the
+ syntaxes defined in this document.
+
+ Syntax OBJECT IDENTIFIER
+ ==============================================================
+ Attribute Type Description 1.3.6.1.4.1.1466.115.121.1.3
+ Bit String 1.3.6.1.4.1.1466.115.121.1.6
+ Boolean 1.3.6.1.4.1.1466.115.121.1.7
+ Country String 1.3.6.1.4.1.1466.115.121.1.11
+ Delivery Method 1.3.6.1.4.1.1466.115.121.1.14
+ Directory String 1.3.6.1.4.1.1466.115.121.1.15
+ DIT Content Rule Description 1.3.6.1.4.1.1466.115.121.1.16
+ DIT Structure Rule Description 1.3.6.1.4.1.1466.115.121.1.17
+ DN 1.3.6.1.4.1.1466.115.121.1.12
+ Enhanced Guide 1.3.6.1.4.1.1466.115.121.1.21
+ Facsimile Telephone Number 1.3.6.1.4.1.1466.115.121.1.22
+ Fax 1.3.6.1.4.1.1466.115.121.1.23
+ Generalized Time 1.3.6.1.4.1.1466.115.121.1.24
+ Guide 1.3.6.1.4.1.1466.115.121.1.25
+ IA5 String 1.3.6.1.4.1.1466.115.121.1.26
+ Integer 1.3.6.1.4.1.1466.115.121.1.27
+ JPEG 1.3.6.1.4.1.1466.115.121.1.28
+ LDAP Syntax Description 1.3.6.1.4.1.1466.115.121.1.54
+ Matching Rule Description 1.3.6.1.4.1.1466.115.121.1.30
+ Matching Rule Use Description 1.3.6.1.4.1.1466.115.121.1.31
+ Name And Optional UID 1.3.6.1.4.1.1466.115.121.1.34
+ Name Form Description 1.3.6.1.4.1.1466.115.121.1.35
+ Numeric String 1.3.6.1.4.1.1466.115.121.1.36
+ Object Class Description 1.3.6.1.4.1.1466.115.121.1.37
+ Octet String 1.3.6.1.4.1.1466.115.121.1.40
+ OID 1.3.6.1.4.1.1466.115.121.1.38
+ Other Mailbox 1.3.6.1.4.1.1466.115.121.1.39
+ Postal Address 1.3.6.1.4.1.1466.115.121.1.41
+ Printable String 1.3.6.1.4.1.1466.115.121.1.44
+ Substring Assertion 1.3.6.1.4.1.1466.115.121.1.58
+ Telephone Number 1.3.6.1.4.1.1466.115.121.1.50
+ Teletex Terminal Identifier 1.3.6.1.4.1.1466.115.121.1.51
+ Telex Number 1.3.6.1.4.1.1466.115.121.1.52
+ UTC Time 1.3.6.1.4.1.1466.115.121.1.53
+
+Appendix B. Changes from RFC 2252
+
+ This annex lists the significant differences between this
+ specification and RFC 2252.
+
+
+
+
+
+Legg Standards Track [Page 49]
+
+RFC 4517 LDAP: Syntaxes and Matching Rules June 2006
+
+
+ This annex is provided for informational purposes only. It is not a
+ normative part of this specification.
+
+ 1. The IESG Note has been removed.
+
+ 2. The major part of Sections 4, 5 and 7 has been moved to [RFC4512]
+ and revised. Changes to the parts of these sections moved to
+ [RFC4512] are detailed in [RFC4512].
+
+ 3. BNF descriptions of syntax formats have been replaced by ABNF
+ [RFC4234] specifications.
+
+ 4. The ambiguous statement in RFC 2252, Section 4.3 regarding the
+ use of a backslash quoting mechanism to escape separator symbols
+ has been removed. The escaping mechanism is now explicitly
+ represented in the ABNF for the syntaxes where this provision
+ applies.
+
+ 5. The description of each of the LDAP syntaxes has been expanded so
+ that they are less dependent on knowledge of X.500 for
+ interpretation.
+
+ 6. The relationship of LDAP syntaxes to corresponding ASN.1 type
+ definitions has been made explicit.
+
+ 7. The set of characters allowed in a <PrintableString> (formerly
+ <printablestring>) has been corrected to align with the
+ PrintableString ASN.1 type in [ASN.1]. Specifically, the double
+ quote character has been removed and the single quote character
+ and equals sign have been added.
+
+ 8. Values of the Directory String, Printable String and Telephone
+ Number syntaxes are now required to have at least one character.
+
+ 9. The <DITContentRuleDescription>, <NameFormDescription> and
+ <DITStructureRuleDescription> rules have been moved to [RFC4512].
+
+ 10. The corresponding ASN.1 type for the Other Mailbox syntax has
+ been incorporated from RFC 1274.
+
+ 11. A corresponding ASN.1 type for the LDAP Syntax Description syntax
+ has been invented.
+
+ 12. The Binary syntax has been removed because it was not adequately
+ specified, implementations with different incompatible
+ interpretations exist, and it was confused with the ;binary
+ transfer encoding.
+
+
+
+
+Legg Standards Track [Page 50]
+
+RFC 4517 LDAP: Syntaxes and Matching Rules June 2006
+
+
+ 13. All discussion of transfer options, including the ";binary"
+ option, has been removed. All imperatives regarding binary
+ transfer of values have been removed.
+
+ 14. The Delivery Method, Enhanced Guide, Guide, Octet String, Teletex
+ Terminal Identifier and Telex Number syntaxes from RFC 2256 have
+ been incorporated.
+
+ 15. The <criteria> rule for the Enhanced Guide and Guide syntaxes has
+ been extended to accommodate empty "and" and "or" expressions.
+
+ 16. An encoding for the <ttx-value> rule in the Teletex Terminal
+ Identifier syntax has been defined.
+
+ 17. The PKI-related syntaxes (Certificate, Certificate List and
+ Certificate Pair) have been removed. They are reintroduced in
+ [RFC4523] (as is the Supported Algorithm syntax from RFC 2256).
+
+ 18. The MHS OR Address syntax has been removed since its
+ specification (in RFC 2156) is not at draft standard maturity.
+
+ 19. The DL Submit Permission syntax has been removed as it depends on
+ the MHS OR Address syntax.
+
+ 20. The Presentation Address syntax has been removed since its
+ specification (in RFC 1278) is not at draft standard maturity.
+
+ 21. The ACI Item, Access Point, Audio, Data Quality, DSA Quality, DSE
+ Type, LDAP Schema Description, Master And Shadow Access Points,
+ Modify Rights, Protocol Information, Subtree Specification,
+ Supplier Information, Supplier Or Consumer and Supplier And
+ Consumer syntaxes have been removed. These syntaxes are
+ referenced in RFC 2252, but not defined.
+
+ 22. The LDAP Schema Definition syntax (defined in RFC 2927) and the
+ Mail Preference syntax have been removed on the grounds that they
+ are out of scope for the core specification.
+
+ 23. The description of each of the matching rules has been expanded
+ so that they are less dependent on knowledge of X.500 for
+ interpretation.
+
+ 24. The caseIgnoreIA5SubstringsMatch matching rule from RFC 2798 has
+ been added.
+
+ 25. The caseIgnoreListSubstringsMatch, caseIgnoreOrderingMatch and
+ caseIgnoreSubstringsMatch matching rules have been added to the
+ list of matching rules for which the provisions for handling
+
+
+
+Legg Standards Track [Page 51]
+
+RFC 4517 LDAP: Syntaxes and Matching Rules June 2006
+
+
+ leading, trailing and multiple adjoining whitespace characters
+ apply (now through string preparation). This is consistent with
+ the definitions of these matching rules in X.500. The
+ caseIgnoreIA5SubstringsMatch rule has also been added to the
+ list.
+
+ 26. The specification of the octetStringMatch matching rule from
+ RFC 2256 has been added to this document.
+
+ 27. The presentationAddressMatch matching rule has been removed as it
+ depends on an assertion syntax (Presentation Address) that is not
+ at draft standard maturity.
+
+ 28. The protocolInformationMatch matching rule has been removed as it
+ depends on an undefined assertion syntax (Protocol Information).
+
+ 29. The definitive reference for ASN.1 has been changed from X.208 to
+ X.680 since X.680 is the version of ASN.1 referred to by X.500.
+
+ 30. The specification of the caseIgnoreListSubstringsMatch matching
+ rule from RFC 2798 & X.520 has been added.
+
+ 31. String preparation algorithms have been applied to the character
+ string matching rules.
+
+ 32. The specifications of the booleanMatch, caseExactMatch,
+ caseExactOrderingMatch, caseExactSubstringsMatch,
+ directoryStringFirstComponentMatch, integerOrderingMatch,
+ keywordMatch, numericStringOrderingMatch,
+ octetStringOrderingMatch and wordMatch matching rules from
+ RFC 3698 & X.520 have been added.
+
+Author's Address
+
+ Steven Legg
+ eB2Bcom
+ Suite3, Woodhouse Corporate Centre
+ 935 Station Street
+ Box Hill North, Victoria 3129
+ AUSTRALIA
+
+ Phone: +61 3 9896 7830
+ Fax: +61 3 9896 7801
+ EMail: steven.legg@eb2bcom.com
+
+
+
+
+
+
+
+Legg Standards Track [Page 52]
+
+RFC 4517 LDAP: Syntaxes and Matching Rules June 2006
+
+
+Full Copyright Statement
+
+ Copyright (C) The Internet Society (2006).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
+ ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
+ INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
+ INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be
+ found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at
+ ietf-ipr@ietf.org.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is provided by the IETF
+ Administrative Support Activity (IASA).
+
+
+
+
+
+
+
+Legg Standards Track [Page 53]
+
diff --git a/source4/ldap_server/devdocs/rfc4518.txt b/source4/ldap_server/devdocs/rfc4518.txt
new file mode 100644
index 0000000000..f886bdfb5d
--- /dev/null
+++ b/source4/ldap_server/devdocs/rfc4518.txt
@@ -0,0 +1,787 @@
+
+
+
+
+
+
+Network Working Group K. Zeilenga
+Request for Comments: 4518 OpenLDAP Foundation
+Category: Standards Track June 2006
+
+
+ Lightweight Directory Access Protocol (LDAP):
+ Internationalized String Preparation
+
+Status of This Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2006).
+
+Abstract
+
+ The previous Lightweight Directory Access Protocol (LDAP) technical
+ specifications did not precisely define how character string matching
+ is to be performed. This led to a number of usability and
+ interoperability problems. This document defines string preparation
+ algorithms for character-based matching rules defined for use in
+ LDAP.
+
+1. Introduction
+
+1.1. Background
+
+ A Lightweight Directory Access Protocol (LDAP) [RFC4510] matching
+ rule [RFC4517] defines an algorithm for determining whether a
+ presented value matches an attribute value in accordance with the
+ criteria defined for the rule. The proposition may be evaluated to
+ True, False, or Undefined.
+
+ True - the attribute contains a matching value,
+
+ False - the attribute contains no matching value,
+
+ Undefined - it cannot be determined whether the attribute contains
+ a matching value.
+
+
+
+
+
+
+Zeilenga Standards Track [Page 1]
+
+RFC 4518 LDAP: Internationalized String Preparation June 2006
+
+
+ For instance, the caseIgnoreMatch matching rule may be used to
+ compare whether the commonName attribute contains a particular value
+ without regard for case and insignificant spaces.
+
+1.2. X.500 String Matching Rules
+
+ "X.520: Selected attribute types" [X.520] provides (among other
+ things) value syntaxes and matching rules for comparing values
+ commonly used in the directory [X.500]. These specifications are
+ inadequate for strings composed of Unicode [Unicode] characters.
+
+ The caseIgnoreMatch matching rule [X.520], for example, is simply
+ defined as being a case-insensitive comparison where insignificant
+ spaces are ignored. For printableString, there is only one space
+ character and case mapping is bijective, hence this definition is
+ sufficient. However, for Unicode string types such as
+ universalString, this is not sufficient. For example, a case-
+ insensitive matching implementation that folded lowercase characters
+ to uppercase would yield different results than an implementation
+ that used uppercase to lowercase folding. Or one implementation may
+ view space as referring to only SPACE (U+0020), a second
+ implementation may view any character with the space separator (Zs)
+ property as a space, and another implementation may view any
+ character with the whitespace (WS) category as a space.
+
+ The lack of precise specification for character string matching has
+ led to significant interoperability problems. When used in
+ certificate chain validation, security vulnerabilities can arise. To
+ address these problems, this document defines precise algorithms for
+ preparing character strings for matching.
+
+1.3. Relationship to "stringprep"
+
+ The character string preparation algorithms described in this
+ document are based upon the "stringprep" approach [RFC3454]. In
+ "stringprep", presented and stored values are first prepared for
+ comparison so that a character-by-character comparison yields the
+ "correct" result.
+
+ The approach used here is a refinement of the "stringprep" [RFC3454]
+ approach. Each algorithm involves two additional preparation steps.
+
+ a) Prior to applying the Unicode string preparation steps outlined in
+ "stringprep", the string is transcoded to Unicode.
+
+ b) After applying the Unicode string preparation steps outlined in
+ "stringprep", the string is modified to appropriately handle
+ characters insignificant to the matching rule.
+
+
+
+Zeilenga Standards Track [Page 2]
+
+RFC 4518 LDAP: Internationalized String Preparation June 2006
+
+
+ Hence, preparation of character strings for X.500 [X.500] matching
+ [X.501] involves the following steps:
+
+ 1) Transcode
+ 2) Map
+ 3) Normalize
+ 4) Prohibit
+ 5) Check Bidi (Bidirectional)
+ 6) Insignificant Character Handling
+
+ These steps are described in Section 2.
+
+ It is noted that while various tables of Unicode characters included
+ or referenced by this specification are derived from Unicode
+ [Unicode] data, these tables are to be considered definitive for the
+ purpose of implementing this specification.
+
+1.4. Relationship to the LDAP Technical Specification
+
+ This document is an integral part of the LDAP technical specification
+ [RFC4510], which obsoletes the previously defined LDAP technical
+ specification [RFC3377] in its entirety.
+
+ This document details new LDAP internationalized character string
+ preparation algorithms used by [RFC4517] and possible other technical
+ specifications defining LDAP syntaxes and/or matching rules.
+
+1.5. Relationship to X.500
+
+ LDAP is defined [RFC4510] in X.500 terms as an X.500 access
+ mechanism. As such, there is a strong desire for alignment between
+ LDAP and X.500 syntax and semantics. The character string
+ preparation algorithms described in this document are based upon
+ "Internationalized String Matching Rules for X.500" [XMATCH] proposal
+ to ITU/ISO Joint Study Group 2.
+
+1.6. Conventions and Terms
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in BCP 14 [RFC2119].
+
+ Character names in this document use the notation for code points and
+ names from the Unicode Standard [Unicode]. For example, the letter
+ "a" may be represented as either <U+0061> or <LATIN SMALL LETTER A>.
+ In the lists of mappings and the prohibited characters, the "U+" is
+
+
+
+
+
+Zeilenga Standards Track [Page 3]
+
+RFC 4518 LDAP: Internationalized String Preparation June 2006
+
+
+ left off to make the lists easier to read. The comments for
+ character ranges are shown in square brackets (such as "[CONTROL
+ CHARACTERS]") and do not come from the standard.
+
+ Note: a glossary of terms used in Unicode can be found in [Glossary].
+ Information on the Unicode character encoding model can be found in
+ [CharModel].
+
+ The term "combining mark", as used in this specification, refers to
+ any Unicode [Unicode] code point that has a mark property (Mn, Mc,
+ Me). Appendix A provides a definitive list of combining marks.
+
+2. String Preparation
+
+ The following six-step process SHALL be applied to each presented and
+ attribute value in preparation for character string matching rule
+ evaluation.
+
+ 1) Transcode
+ 2) Map
+ 3) Normalize
+ 4) Prohibit
+ 5) Check bidi
+ 6) Insignificant Character Handling
+
+ Failure in any step causes the assertion to evaluate to Undefined.
+
+ The character repertoire of this process is Unicode 3.2 [Unicode].
+
+ Note that this six-step process specification is intended to describe
+ expected matching behavior. Implementations are free to use
+ alternative processes so long as the matching rule evaluation
+ behavior provided is consistent with the behavior described by this
+ specification.
+
+2.1. Transcode
+
+ Each non-Unicode string value is transcoded to Unicode.
+
+ PrintableString [X.680] values are transcoded directly to Unicode.
+
+ UniversalString, UTF8String, and bmpString [X.680] values need not be
+ transcoded as they are Unicode-based strings (in the case of
+ bmpString, a subset of Unicode).
+
+ TeletexString [X.680] values are transcoded to Unicode. As there is
+ no standard for mapping TeletexString values to Unicode, the mapping
+ is left a local matter.
+
+
+
+Zeilenga Standards Track [Page 4]
+
+RFC 4518 LDAP: Internationalized String Preparation June 2006
+
+
+ For these and other reasons, use of TeletexString is NOT RECOMMENDED.
+
+ The output is the transcoded string.
+
+2.2. Map
+
+ SOFT HYPHEN (U+00AD) and MONGOLIAN TODO SOFT HYPHEN (U+1806) code
+ points are mapped to nothing. COMBINING GRAPHEME JOINER (U+034F) and
+ VARIATION SELECTORs (U+180B-180D, FF00-FE0F) code points are also
+ mapped to nothing. The OBJECT REPLACEMENT CHARACTER (U+FFFC) is
+ mapped to nothing.
+
+ CHARACTER TABULATION (U+0009), LINE FEED (LF) (U+000A), LINE
+ TABULATION (U+000B), FORM FEED (FF) (U+000C), CARRIAGE RETURN (CR)
+ (U+000D), and NEXT LINE (NEL) (U+0085) are mapped to SPACE (U+0020).
+
+ All other control code (e.g., Cc) points or code points with a
+ control function (e.g., Cf) are mapped to nothing. The following is
+ a complete list of these code points: U+0000-0008, 000E-001F, 007F-
+ 0084, 0086-009F, 06DD, 070F, 180E, 200C-200F, 202A-202E, 2060-2063,
+ 206A-206F, FEFF, FFF9-FFFB, 1D173-1D17A, E0001, E0020-E007F.
+
+ ZERO WIDTH SPACE (U+200B) is mapped to nothing. All other code
+ points with Separator (space, line, or paragraph) property (e.g., Zs,
+ Zl, or Zp) are mapped to SPACE (U+0020). The following is a complete
+ list of these code points: U+0020, 00A0, 1680, 2000-200A, 2028-2029,
+ 202F, 205F, 3000.
+
+ For case ignore, numeric, and stored prefix string matching rules,
+ characters are case folded per B.2 of [RFC3454].
+
+ The output is the mapped string.
+
+2.3. Normalize
+
+ The input string is to be normalized to Unicode Form KC
+ (compatibility composed) as described in [UAX15]. The output is the
+ normalized string.
+
+2.4. Prohibit
+
+ All Unassigned code points are prohibited. Unassigned code points
+ are listed in Table A.1 of [RFC3454].
+
+ Characters that, per Section 5.8 of [RFC3454], change display
+ properties or are deprecated are prohibited. These characters are
+ listed in Table C.8 of [RFC3454].
+
+
+
+
+Zeilenga Standards Track [Page 5]
+
+RFC 4518 LDAP: Internationalized String Preparation June 2006
+
+
+ Private Use code points are prohibited. These characters are listed
+ in Table C.3 of [RFC3454].
+
+ All non-character code points are prohibited. These code points are
+ listed in Table C.4 of [RFC3454].
+
+ Surrogate codes are prohibited. These characters are listed in Table
+ C.5 of [RFC3454].
+
+ The REPLACEMENT CHARACTER (U+FFFD) code point is prohibited.
+
+ The step fails if the input string contains any prohibited code
+ point. Otherwise, the output is the input string.
+
+2.5. Check bidi
+
+ Bidirectional characters are ignored.
+
+2.6. Insignificant Character Handling
+
+ In this step, the string is modified to ensure proper handling of
+ characters insignificant to the matching rule. This modification
+ differs from matching rule to matching rule.
+
+ Section 2.6.1 applies to case ignore and exact string matching.
+ Section 2.6.2 applies to numericString matching.
+ Section 2.6.3 applies to telephoneNumber matching.
+
+2.6.1. Insignificant Space Handling
+
+ For the purposes of this section, a space is defined to be the SPACE
+ (U+0020) code point followed by no combining marks.
+
+ NOTE - The previous steps ensure that the string cannot contain
+ any code points in the separator class, other than SPACE
+ (U+0020).
+
+ For input strings that are attribute values or non-substring
+ assertion values: If the input string contains no non-space
+ character, then the output is exactly two SPACEs. Otherwise (the
+ input string contains at least one non-space character), the string
+ is modified such that the string starts with exactly one space
+ character, ends with exactly one SPACE character, and any inner
+ (non-empty) sequence of space characters is replaced with exactly two
+ SPACE characters. For instance, the input strings
+ "foo<SPACE>bar<SPACE><SPACE>", result in the output
+ "<SPACE>foo<SPACE><SPACE>bar<SPACE>".
+
+
+
+
+Zeilenga Standards Track [Page 6]
+
+RFC 4518 LDAP: Internationalized String Preparation June 2006
+
+
+ For input strings that are substring assertion values: If the string
+ being prepared contains no non-space characters, then the output
+ string is exactly one SPACE. Otherwise, the following steps are
+ taken:
+
+ - If the input string is an initial substring, it is modified to
+ start with exactly one SPACE character;
+
+ - If the input string is an initial or an any substring that ends in
+ one or more space characters, it is modified to end with exactly
+ one SPACE character;
+
+ - If the input string is an any or a final substring that starts in
+ one or more space characters, it is modified to start with exactly
+ one SPACE character; and
+
+ - If the input string is a final substring, it is modified to end
+ with exactly one SPACE character.
+
+ For instance, for the input string "foo<SPACE>bar<SPACE><SPACE>" as
+ an initial substring, the output would be
+ "<SPACE>foo<SPACE><SPACE>bar<SPACE>". As an any or final substring,
+ the same input would result in "foo<SPACE>bar<SPACE>".
+
+ Appendix B discusses the rationale for the behavior.
+
+2.6.2. numericString Insignificant Character Handling
+
+ For the purposes of this section, a space is defined to be the SPACE
+ (U+0020) code point followed by no combining marks.
+
+ All spaces are regarded as insignificant and are to be removed.
+
+ For example, removal of spaces from the Form KC string:
+ "<SPACE><SPACE>123<SPACE><SPACE>456<SPACE><SPACE>"
+ would result in the output string:
+ "123456"
+ and the Form KC string:
+ "<SPACE><SPACE><SPACE>"
+ would result in the output string:
+ "" (an empty string).
+
+2.6.3. telephoneNumber Insignificant Character Handling
+
+ For the purposes of this section, a hyphen is defined to be a
+ HYPHEN-MINUS (U+002D), ARMENIAN HYPHEN (U+058A), HYPHEN (U+2010),
+ NON-BREAKING HYPHEN (U+2011), MINUS SIGN (U+2212), SMALL HYPHEN-MINUS
+ (U+FE63), or FULLWIDTH HYPHEN-MINUS (U+FF0D) code point followed by
+
+
+
+Zeilenga Standards Track [Page 7]
+
+RFC 4518 LDAP: Internationalized String Preparation June 2006
+
+
+ no combining marks and a space is defined to be the SPACE (U+0020)
+ code point followed by no combining marks.
+
+ All hyphens and spaces are considered insignificant and are to be
+ removed.
+
+ For example, removal of hyphens and spaces from the Form KC string:
+ "<SPACE><HYPHEN>123<SPACE><SPACE>456<SPACE><HYPHEN>"
+ would result in the output string:
+ "123456"
+ and the Form KC string:
+ "<HYPHEN><HYPHEN><HYPHEN>"
+ would result in the (empty) output string:
+ "".
+
+3. Security Considerations
+
+ "Preparation of Internationalized Strings ("stringprep")" [RFC3454]
+ security considerations generally apply to the algorithms described
+ here.
+
+4. Acknowledgements
+
+ The approach used in this document is based upon design principles
+ and algorithms described in "Preparation of Internationalized Strings
+ ('stringprep')" [RFC3454] by Paul Hoffman and Marc Blanchet. Some
+ additional guidance was drawn from Unicode Technical Standards,
+ Technical Reports, and Notes.
+
+ This document is a product of the IETF LDAP Revision (LDAPBIS)
+ Working Group.
+
+5. References
+
+5.1. Normative References
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [RFC3454] Hoffman, P. and M. Blanchet, "Preparation of
+ Internationalized Strings ("stringprep")", RFC 3454,
+ December 2002.
+
+ [RFC4510] Zeilenga, K., "Lightweight Directory Access Protocol
+ (LDAP): Technical Specification Road Map", RFC 4510,
+ June 2006.
+
+
+
+
+
+Zeilenga Standards Track [Page 8]
+
+RFC 4518 LDAP: Internationalized String Preparation June 2006
+
+
+ [RFC4517] Legg, S., Ed., "Lightweight Directory Access Protocol
+ (LDAP): Syntaxes and Matching Rules", RFC 4517, June
+ 2006.
+
+ [Unicode] The Unicode Consortium, "The Unicode Standard, Version
+ 3.2.0" is defined by "The Unicode Standard, Version
+ 3.0" (Reading, MA, Addison-Wesley, 2000. ISBN 0-201-
+ 61633-5), as amended by the "Unicode Standard Annex
+ #27: Unicode 3.1"
+ (http://www.unicode.org/reports/tr27/) and by the
+ "Unicode Standard Annex #28: Unicode 3.2"
+ (http://www.unicode.org/reports/tr28/).
+
+ [UAX15] Davis, M. and M. Duerst, "Unicode Standard Annex #15:
+ Unicode Normalization Forms, Version 3.2.0".
+ <http://www.unicode.org/unicode/reports/tr15/tr15-
+ 22.html>, March 2002.
+
+ [X.680] International Telecommunication Union -
+ Telecommunication Standardization Sector, "Abstract
+ Syntax Notation One (ASN.1) - Specification of Basic
+ Notation", X.680(2002) (also ISO/IEC 8824-1:2002).
+
+5.2. Informative References
+
+ [X.500] International Telecommunication Union -
+ Telecommunication Standardization Sector, "The
+ Directory -- Overview of concepts, models and
+ services," X.500(1993) (also ISO/IEC 9594-1:1994).
+
+ [X.501] International Telecommunication Union -
+ Telecommunication Standardization Sector, "The
+ Directory -- Models," X.501(1993) (also ISO/IEC 9594-
+ 2:1994).
+
+ [X.520] International Telecommunication Union -
+ Telecommunication Standardization Sector, "The
+ Directory: Selected Attribute Types", X.520(1993) (also
+ ISO/IEC 9594-6:1994).
+
+ [Glossary] The Unicode Consortium, "Unicode Glossary",
+ <http://www.unicode.org/glossary/>.
+
+ [CharModel] Whistler, K. and M. Davis, "Unicode Technical Report
+ #17, Character Encoding Model", UTR17,
+ <http://www.unicode.org/unicode/reports/tr17/>, August
+ 2000.
+
+
+
+
+Zeilenga Standards Track [Page 9]
+
+RFC 4518 LDAP: Internationalized String Preparation June 2006
+
+
+ [RFC3377] Hodges, J. and R. Morgan, "Lightweight Directory Access
+ Protocol (v3): Technical Specification", RFC 3377,
+ September 2002.
+
+ [RFC4515] Smith, M., Ed. and T. Howes, "Lightweight Directory
+ Access Protocol (LDAP): String Representation of Search
+ Filters", RFC 4515, June 2006.
+
+ [XMATCH] Zeilenga, K., "Internationalized String Matching Rules
+ for X.500", Work in Progress.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 10]
+
+RFC 4518 LDAP: Internationalized String Preparation June 2006
+
+
+Appendix A. Combining Marks
+
+ This appendix is normative.
+
+ This table was derived from Unicode [Unicode] data files; it lists
+ all code points with the Mn, Mc, or Me properties. This table is to
+ be considered definitive for the purposes of implementation of this
+ specification.
+
+ 0300-034F 0360-036F 0483-0486 0488-0489 0591-05A1
+ 05A3-05B9 05BB-05BC 05BF 05C1-05C2 05C4 064B-0655 0670
+ 06D6-06DC 06DE-06E4 06E7-06E8 06EA-06ED 0711 0730-074A
+ 07A6-07B0 0901-0903 093C 093E-094F 0951-0954 0962-0963
+ 0981-0983 09BC 09BE-09C4 09C7-09C8 09CB-09CD 09D7
+ 09E2-09E3 0A02 0A3C 0A3E-0A42 0A47-0A48 0A4B-0A4D
+ 0A70-0A71 0A81-0A83 0ABC 0ABE-0AC5 0AC7-0AC9 0ACB-0ACD
+ 0B01-0B03 0B3C 0B3E-0B43 0B47-0B48 0B4B-0B4D 0B56-0B57
+ 0B82 0BBE-0BC2 0BC6-0BC8 0BCA-0BCD 0BD7 0C01-0C03
+ 0C3E-0C44 0C46-0C48 0C4A-0C4D 0C55-0C56 0C82-0C83
+ 0CBE-0CC4 0CC6-0CC8 0CCA-0CCD 0CD5-0CD6 0D02-0D03
+ 0D3E-0D43 0D46-0D48 0D4A-0D4D 0D57 0D82-0D83 0DCA
+ 0DCF-0DD4 0DD6 0DD8-0DDF 0DF2-0DF3 0E31 0E34-0E3A
+ 0E47-0E4E 0EB1 0EB4-0EB9 0EBB-0EBC 0EC8-0ECD 0F18-0F19
+ 0F35 0F37 0F39 0F3E-0F3F 0F71-0F84 0F86-0F87 0F90-0F97
+ 0F99-0FBC 0FC6 102C-1032 1036-1039 1056-1059 1712-1714
+ 1732-1734 1752-1753 1772-1773 17B4-17D3 180B-180D 18A9
+ 20D0-20EA 302A-302F 3099-309A FB1E FE00-FE0F FE20-FE23
+ 1D165-1D169 1D16D-1D172 1D17B-1D182 1D185-1D18B
+ 1D1AA-1D1AD
+
+Appendix B. Substrings Matching
+
+ This appendix is non-normative.
+
+ In the absence of substrings matching, the insignificant space
+ handling for case ignore/exact matching could be simplified.
+ Specifically, the handling could be to require that all sequences of
+ one or more spaces be replaced with one space and, if the string
+ contains non-space characters, removal of all leading spaces and
+ trailing spaces.
+
+ In the presence of substrings matching, this simplified space
+ handling would lead to unexpected and undesirable matching behavior.
+ For instance:
+
+ 1) (CN=foo\20*\20bar) would match the CN value "foobar";
+
+
+
+
+
+Zeilenga Standards Track [Page 11]
+
+RFC 4518 LDAP: Internationalized String Preparation June 2006
+
+
+ 2) (CN=*\20foobar\20*) would match "foobar", but
+ (CN=*\20*foobar*\20*) would not.
+
+ Note to readers not familiar with LDAP substrings matching: the LDAP
+ filter [RFC4515] assertion (CN=A*B*C) says to "match any value (of
+ the attribute CN) that begins with A, contains B after A, ends with C
+ where C is also after B."
+
+ The first case illustrates that this simplified space handling would
+ cause leading and trailing spaces in substrings of the string to be
+ regarded as insignificant. However, only leading and trailing (as
+ well as multiple consecutive spaces) of the string (as a whole) are
+ insignificant.
+
+ The second case illustrates that this simplified space handling would
+ cause sub-partitioning failures. That is, if a prepared any
+ substring matches a partition of the attribute value, then an
+ assertion constructed by subdividing that substring into multiple
+ substrings should also match.
+
+ In designing an appropriate approach for space handling for
+ substrings matching, one must study key aspects of X.500 case
+ exact/ignore matching. X.520 [X.520] says:
+
+ The [substrings] rule returns TRUE if there is a partitioning of
+ the attribute value (into portions) such that:
+
+ - the specified substrings (initial, any, final) match
+ different portions of the value in the order of the strings
+ sequence;
+
+ - initial, if present, matches the first portion of the value;
+
+ - final, if present, matches the last portion of the value;
+
+ - any, if present, matches some arbitrary portion of the
+ value.
+
+ That is, the substrings assertion (CN=foo\20*\20bar) matches the
+ attribute value "foo<SPACE><SPACE>bar" as the value can be
+ partitioned into the portions "foo<SPACE>" and "<SPACE>bar" meeting
+ the above requirements.
+
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 12]
+
+RFC 4518 LDAP: Internationalized String Preparation June 2006
+
+
+ X.520 also says:
+
+ [T]he following spaces are regarded as not significant:
+
+ - leading spaces (i.e., those preceding the first character
+ that is not a space);
+
+ - trailing spaces (i.e., those following the last character
+ that is not a space);
+
+ - multiple consecutive spaces (these are taken as equivalent
+ to a single space character).
+
+ This statement applies to the assertion values and attribute values
+ as whole strings, and not individually to substrings of an assertion
+ value. In particular, the statements should be taken to mean that if
+ an assertion value and attribute value match without any
+ consideration to insignificant characters, then that assertion value
+ should also match any attribute value that differs only by inclusion
+ nor removal of insignificant characters.
+
+ Hence the assertion (CN=foo\20*\20bar) matches
+ "foo<SPACE><SPACE><SPACE>bar" and "foo<SPACE>bar" as these values
+ only differ from "foo<SPACE><SPACE>bar" by the inclusion or removal
+ of insignificant spaces.
+
+ Astute readers of this text will also note that there are special
+ cases where the specified space handling does not ignore spaces that
+ could be considered insignificant. For instance, the assertion
+ (CN=\20*\20*\20) does not match "<SPACE><SPACE><SPACE>"
+ (insignificant spaces present in value) or " " (insignificant spaces
+ not present in value). However, as these cases have no practical
+ application that cannot be met by simple assertions, e.g., (cn=\20),
+ and this minor anomaly can only be fully addressed by a preparation
+ algorithm to be used in conjunction with character-by-character
+ partitioning and matching, the anomaly is considered acceptable.
+
+Author's Address
+
+ Kurt D. Zeilenga
+ OpenLDAP Foundation
+
+ EMail: Kurt@OpenLDAP.org
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 13]
+
+RFC 4518 LDAP: Internationalized String Preparation June 2006
+
+
+Full Copyright Statement
+
+ Copyright (C) The Internet Society (2006).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
+ ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
+ INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
+ INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be
+ found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at
+ ietf-ipr@ietf.org.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is provided by the IETF
+ Administrative Support Activity (IASA).
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 14]
+
diff --git a/source4/ldap_server/devdocs/rfc4519.txt b/source4/ldap_server/devdocs/rfc4519.txt
new file mode 100644
index 0000000000..f2e9b7c4b6
--- /dev/null
+++ b/source4/ldap_server/devdocs/rfc4519.txt
@@ -0,0 +1,1963 @@
+
+
+
+
+
+
+Network Working Group A. Sciberras, Ed.
+Request for Comments: 4519 eB2Bcom
+Obsoletes: 2256 June 2006
+Updates: 2247, 2798, 2377
+Category: Standards Track
+
+
+ Lightweight Directory Access Protocol (LDAP):
+ Schema for User Applications
+
+Status of This Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2006).
+
+Abstract
+
+ This document is an integral part of the Lightweight Directory Access
+ Protocol (LDAP) technical specification. It provides a technical
+ specification of attribute types and object classes intended for use
+ by LDAP directory clients for many directory services, such as White
+ Pages. These objects are widely used as a basis for the schema in
+ many LDAP directories. This document does not cover attributes used
+ for the administration of directory servers, nor does it include
+ directory objects defined for specific uses in other documents.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Sciberras Standards Track [Page 1]
+
+RFC 4519 LDAP: Schema for User Applications June 2006
+
+
+Table of Contents
+
+ 1. Introduction ....................................................3
+ 1.1. Relationship with Other Specifications .....................3
+ 1.2. Conventions ................................................4
+ 1.3. General Issues .............................................4
+ 2. Attribute Types .................................................4
+ 2.1. 'businessCategory' .........................................5
+ 2.2. 'c' ........................................................5
+ 2.3. 'cn' .......................................................5
+ 2.4. 'dc' .......................................................6
+ 2.5. 'description' ..............................................6
+ 2.6. 'destinationIndicator' .....................................7
+ 2.7. 'distinguishedName' ........................................7
+ 2.8. 'dnQualifier' ..............................................8
+ 2.9. 'enhancedSearchGuide' ......................................8
+ 2.10. 'facsimileTelephoneNumber' ................................9
+ 2.11. 'generationQualifier' .....................................9
+ 2.12. 'givenName' ...............................................9
+ 2.13. 'houseIdentifier' .........................................9
+ 2.14. 'initials' ...............................................10
+ 2.15. 'internationalISDNNumber' ................................10
+ 2.16. 'l' ......................................................10
+ 2.17. 'member' .................................................11
+ 2.18. 'name' ...................................................11
+ 2.19. 'o' ......................................................11
+ 2.20. 'ou' .....................................................12
+ 2.21. 'owner' ..................................................12
+ 2.22. 'physicalDeliveryOfficeName' .............................12
+ 2.23. 'postalAddress' ..........................................13
+ 2.24. 'postalCode' .............................................13
+ 2.25. 'postOfficeBox' ..........................................14
+ 2.26. 'preferredDeliveryMethod' ................................14
+ 2.27. 'registeredAddress' ......................................14
+ 2.28. 'roleOccupant' ...........................................15
+ 2.29. 'searchGuide' ............................................15
+ 2.30. 'seeAlso' ................................................15
+ 2.31. 'serialNumber' ...........................................16
+ 2.32. 'sn' .....................................................16
+ 2.33. 'st' .....................................................16
+ 2.34. 'street' .................................................17
+ 2.35. 'telephoneNumber' ........................................17
+ 2.36. 'teletexTerminalIdentifier' ..............................17
+ 2.37. 'telexNumber' ............................................18
+ 2.38. 'title' ..................................................18
+ 2.39. 'uid' ....................................................18
+ 2.40. 'uniqueMember' ...........................................19
+ 2.41. 'userPassword' ...........................................19
+
+
+
+Sciberras Standards Track [Page 2]
+
+RFC 4519 LDAP: Schema for User Applications June 2006
+
+
+ 2.42. 'x121Address' ............................................20
+ 2.43. 'x500UniqueIdentifier' ...................................20
+ 3. Object Classes .................................................20
+ 3.1. 'applicationProcess' ......................................21
+ 3.2. 'country' .................................................21
+ 3.3. 'dcObject' ................................................21
+ 3.4. 'device' ..................................................21
+ 3.5. 'groupOfNames' ............................................22
+ 3.6. 'groupOfUniqueNames' ......................................22
+ 3.7. 'locality' ................................................23
+ 3.8. 'organization' ............................................23
+ 3.9. 'organizationalPerson' ....................................24
+ 3.10. 'organizationalRole' .....................................24
+ 3.11. 'organizationalUnit' .....................................24
+ 3.12. 'person' .................................................25
+ 3.13. 'residentialPerson' ......................................25
+ 3.14. 'uidObject' ..............................................26
+ 4. IANA Considerations ............................................26
+ 5. Security Considerations ........................................28
+ 6. Acknowledgements ...............................................28
+ 7. References .....................................................29
+ 7.1. Normative References ......................................29
+ 7.2. Informative References ....................................30
+ Appendix A Changes Made Since RFC 2256 ...........................32
+
+1. Introduction
+
+ This document provides an overview of attribute types and object
+ classes intended for use by Lightweight Directory Access Protocol
+ (LDAP) directory clients for many directory services, such as White
+ Pages. Originally specified in the X.500 [X.500] documents, these
+ objects are widely used as a basis for the schema in many LDAP
+ directories. This document does not cover attributes used for the
+ administration of directory servers, nor does it include directory
+ objects defined for specific uses in other documents.
+
+1.1. Relationship with Other Specifications
+
+ This document is an integral part of the LDAP technical specification
+ [RFC4510], which obsoletes the previously defined LDAP technical
+ specification, RFC 3377, in its entirety. In terms of RFC 2256,
+ Sections 6 and 8 of RFC 2256 are obsoleted by [RFC4517]. Sections
+ 5.1, 5.2, 7.1, and 7.2 of RFC 2256 are obsoleted by [RFC4512]. The
+ remainder of RFC 2256 is obsoleted by this document. The technical
+ specification for the 'dc' attribute type and 'dcObject' object class
+ found in RFC 2247 are superseded by sections 2.4 and 3.3 of this
+ document. The remainder of RFC 2247 remains in force.
+
+
+
+
+Sciberras Standards Track [Page 3]
+
+RFC 4519 LDAP: Schema for User Applications June 2006
+
+
+ This document updates RFC 2798 by replacing the informative
+ description of the 'uid' attribute type with the definitive
+ description provided in Section 2.39 of this document.
+
+ This document updates RFC 2377 by replacing the informative
+ description of the 'uidObject' object class with the definitive
+ description provided in Section 3.14 of this document.
+
+ A number of schema elements that were included in the previous
+ revision of the LDAP Technical Specification are not included in this
+ revision of LDAP. PKI-related schema elements are now specified in
+ [RFC4523]. Unless reintroduced in future technical specifications,
+ the remainder are to be considered Historic.
+
+ The descriptions in this document SHALL be considered definitive for
+ use in LDAP.
+
+1.2. Conventions
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in RFC 2119 [RFC2119].
+
+1.3. General Issues
+
+ This document references Syntaxes defined in Section 3 of [RFC4517]
+ and Matching Rules defined in Section 4 of [RFC4517].
+
+ The definitions of Attribute Types and Object Classes are written
+ using the Augmented Backus-Naur Form (ABNF) [RFC4234] of
+ AttributeTypeDescription and ObjectClassDescription given in
+ [RFC4512]. Lines have been folded for readability. When such values
+ are transferred as attribute values in the LDAP Protocol, the values
+ will not contain line breaks.
+
+2. Attribute Types
+
+ The attribute types contained in this section hold user information.
+
+ There is no requirement that servers implement the 'searchGuide' and
+ 'teletexTerminalIdentifier' attribute types. In fact, their use is
+ greatly discouraged.
+
+ An LDAP server implementation SHOULD recognize the rest of the
+ attribute types described in this section.
+
+
+
+
+
+
+Sciberras Standards Track [Page 4]
+
+RFC 4519 LDAP: Schema for User Applications June 2006
+
+
+2.1. 'businessCategory'
+
+ The 'businessCategory' attribute type describes the kinds of business
+ performed by an organization. Each kind is one value of this
+ multi-valued attribute.
+ (Source: X.520 [X.520])
+
+ ( 2.5.4.15 NAME 'businessCategory'
+ EQUALITY caseIgnoreMatch
+ SUBSTR caseIgnoreSubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+
+ 1.3.6.1.4.1.1466.115.121.1.15 refers to the Directory String syntax
+ [RFC4517].
+
+ Examples: "banking", "transportation", and "real estate".
+
+2.2. 'c'
+
+ The 'c' ('countryName' in X.500) attribute type contains a two-letter
+ ISO 3166 [ISO3166] country code.
+ (Source: X.520 [X.520])
+
+ ( 2.5.4.6 NAME 'c'
+ SUP name
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.11
+ SINGLE-VALUE )
+
+ 1.3.6.1.4.1.1466.115.121.1.11 refers to the Country String syntax
+ [RFC4517].
+
+ Examples: "DE", "AU" and "FR".
+
+2.3. 'cn'
+
+ The 'cn' ('commonName' in X.500) attribute type contains names of an
+ object. Each name is one value of this multi-valued attribute. If
+ the object corresponds to a person, it is typically the person's full
+ name.
+ (Source: X.520 [X.520])
+
+ ( 2.5.4.3 NAME 'cn'
+ SUP name )
+
+ Examples: "Martin K Smith", "Marty Smith" and "printer12".
+
+
+
+
+
+
+Sciberras Standards Track [Page 5]
+
+RFC 4519 LDAP: Schema for User Applications June 2006
+
+
+2.4. 'dc'
+
+ The 'dc' ('domainComponent' in RFC 1274) attribute type is a string
+ holding one component, a label, of a DNS domain name
+ [RFC1034][RFC2181] naming a host [RFC1123]. That is, a value of this
+ attribute is a string of ASCII characters adhering to the following
+ ABNF [RFC4234]:
+
+ label = (ALPHA / DIGIT) [*61(ALPHA / DIGIT / HYPHEN) (ALPHA / DIGIT)]
+ ALPHA = %x41-5A / %x61-7A ; "A"-"Z" / "a"-"z"
+ DIGIT = %x30-39 ; "0"-"9"
+ HYPHEN = %x2D ; hyphen ("-")
+
+ The encoding of IA5String for use in LDAP is simply the characters of
+ the ASCII label. The equality matching rule is case insensitive, as
+ is today's DNS. (Source: RFC 2247 [RFC2247] and RFC 1274 [RFC 1274])
+
+ ( 0.9.2342.19200300.100.1.25 NAME 'dc'
+ EQUALITY caseIgnoreIA5Match
+ SUBSTR caseIgnoreIA5SubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE )
+
+ 1.3.6.1.4.1.1466.115.121.1.26 refers to the IA5 String syntax
+ [RFC4517].
+
+ Examples: Valid values include "example" and "com" but not
+ "example.com". The latter is invalid as it contains multiple domain
+ components.
+
+ It is noted that the directory service will not ensure that values of
+ this attribute conform to the host label restrictions [RFC1123]
+ illustrated by the <label> production provided above. It is the
+ directory client's responsibility to ensure that the labels it stores
+ in this attribute are appropriately restricted.
+
+ Directory applications supporting International Domain Names SHALL
+ use the ToASCII method [RFC3490] to produce the domain component
+ label. The special considerations discussed in Section 4 of RFC 3490
+ [RFC3490] should be taken, depending on whether the domain component
+ is used for "stored" or "query" purposes.
+
+2.5. 'description'
+
+ The 'description' attribute type contains human-readable descriptive
+ phrases about the object. Each description is one value of this
+ multi-valued attribute.
+ (Source: X.520 [X.520])
+
+
+
+Sciberras Standards Track [Page 6]
+
+RFC 4519 LDAP: Schema for User Applications June 2006
+
+
+ ( 2.5.4.13 NAME 'description'
+ EQUALITY caseIgnoreMatch
+ SUBSTR caseIgnoreSubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+
+ 1.3.6.1.4.1.1466.115.121.1.15 refers to the Directory String syntax
+ [RFC4517].
+
+ Examples: "a color printer", "Maintenance is done every Monday, at
+ 1pm.", and "distribution list for all technical staff".
+
+2.6. 'destinationIndicator'
+
+ The 'destinationIndicator' attribute type contains country and city
+ strings associated with the object (the addressee) needed to provide
+ the Public Telegram Service. The strings are composed in accordance
+ with CCITT Recommendations F.1 [F.1] and F.31 [F.31]. Each string is
+ one value of this multi-valued attribute.
+ (Source: X.520 [X.520])
+
+ ( 2.5.4.27 NAME 'destinationIndicator'
+ EQUALITY caseIgnoreMatch
+ SUBSTR caseIgnoreSubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.44 )
+
+ 1.3.6.1.4.1.1466.115.121.1.44 refers to the Printable String syntax
+ [RFC4517].
+
+ Examples: "AASD" as a destination indicator for Sydney, Australia.
+ "GBLD" as a destination indicator for London, United
+ Kingdom.
+
+ It is noted that the directory will not ensure that values of this
+ attribute conform to the F.1 and F.31 CCITT Recommendations. It is
+ the application's responsibility to ensure destination indicators
+ that it stores in this attribute are appropriately constructed.
+
+2.7. 'distinguishedName'
+
+ The 'distinguishedName' attribute type is not used as the name of the
+ object itself, but it is instead a base type from which some user
+ attribute types with a DN syntax can inherit.
+
+ It is unlikely that values of this type itself will occur in an
+ entry. LDAP server implementations that do not support attribute
+ subtyping need not recognize this attribute in requests. Client
+ implementations MUST NOT assume that LDAP servers are capable of
+ performing attribute subtyping.
+
+
+
+Sciberras Standards Track [Page 7]
+
+RFC 4519 LDAP: Schema for User Applications June 2006
+
+
+ (Source: X.520 [X.520])
+
+ ( 2.5.4.49 NAME 'distinguishedName'
+ EQUALITY distinguishedNameMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+
+ 1.3.6.1.4.1.1466.115.121.1.12 refers to the DN syntax [RFC4517].
+
+2.8. 'dnQualifier'
+
+ The 'dnQualifier' attribute type contains disambiguating information
+ strings to add to the relative distinguished name of an entry. The
+ information is intended for use when merging data from multiple
+ sources in order to prevent conflicts between entries that would
+ otherwise have the same name. Each string is one value of this
+ multi-valued attribute. It is recommended that a value of the
+ 'dnQualifier' attribute be the same for all entries from a particular
+ source.
+ (Source: X.520 [X.520])
+
+ ( 2.5.4.46 NAME 'dnQualifier'
+ EQUALITY caseIgnoreMatch
+ ORDERING caseIgnoreOrderingMatch
+ SUBSTR caseIgnoreSubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.44 )
+
+ 1.3.6.1.4.1.1466.115.121.1.44 refers to the Printable String syntax
+ [RFC4517].
+
+ Examples: "20050322123345Z" - timestamps can be used to disambiguate
+ information.
+ "123456A" - serial numbers can be used to disambiguate
+ information.
+
+2.9. 'enhancedSearchGuide'
+
+ The 'enhancedSearchGuide' attribute type contains sets of information
+ for use by directory clients in constructing search filters. Each
+ set is one value of this multi-valued attribute.
+ (Source: X.520 [X.520])
+
+ ( 2.5.4.47 NAME 'enhancedSearchGuide'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.21 )
+
+ 1.3.6.1.4.1.1466.115.121.1.21 refers to the Enhanced Guide syntax
+ [RFC4517].
+
+
+
+
+
+Sciberras Standards Track [Page 8]
+
+RFC 4519 LDAP: Schema for User Applications June 2006
+
+
+ Examples: "person#(sn$APPROX)#wholeSubtree" and
+ "organizationalUnit#(ou$SUBSTR)#oneLevel".
+
+2.10. 'facsimileTelephoneNumber'
+
+ The 'facsimileTelephoneNumber' attribute type contains telephone
+ numbers (and, optionally, the parameters) for facsimile terminals.
+ Each telephone number is one value of this multi-valued attribute.
+ (Source: X.520 [X.520])
+
+ ( 2.5.4.23 NAME 'facsimileTelephoneNumber'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.22 )
+
+ 1.3.6.1.4.1.1466.115.121.1.22 refers to the Facsimile Telephone
+ Number syntax [RFC4517].
+
+ Examples: "+61 3 9896 7801" and "+81 3 347 7418$fineResolution".
+
+2.11. 'generationQualifier'
+
+ The 'generationQualifier' attribute type contains name strings that
+ are typically the suffix part of a person's name. Each string is one
+ value of this multi-valued attribute.
+ (Source: X.520 [X.520])
+
+ ( 2.5.4.44 NAME 'generationQualifier'
+ SUP name )
+
+ Examples: "III", "3rd", and "Jr.".
+
+2.12. 'givenName'
+
+ The 'givenName' attribute type contains name strings that are the
+ part of a person's name that is not their surname. Each string is
+ one value of this multi-valued attribute.
+ (Source: X.520 [X.520])
+
+ ( 2.5.4.42 NAME 'givenName'
+ SUP name )
+
+ Examples: "Andrew", "Charles", and "Joanne".
+
+2.13. 'houseIdentifier'
+
+ The 'houseIdentifier' attribute type contains identifiers for a
+ building within a location. Each identifier is one value of this
+ multi-valued attribute.
+ (Source: X.520 [X.520])
+
+
+
+Sciberras Standards Track [Page 9]
+
+RFC 4519 LDAP: Schema for User Applications June 2006
+
+
+ ( 2.5.4.51 NAME 'houseIdentifier'
+ EQUALITY caseIgnoreMatch
+ SUBSTR caseIgnoreSubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+
+ 1.3.6.1.4.1.1466.115.121.1.15 refers to the Directory String syntax
+ [RFC4517].
+
+ Example: "20" to represent the house number 20.
+
+2.14. 'initials'
+
+ The 'initials' attribute type contains strings of initials of some or
+ all of an individual's names, except the surname(s). Each string is
+ one value of this multi-valued attribute.
+ (Source: X.520 [X.520])
+
+ ( 2.5.4.43 NAME 'initials'
+ SUP name )
+
+ Examples: "K. A." and "K".
+
+2.15. 'internationalISDNNumber'
+
+ The 'internationalISDNNumber' attribute type contains Integrated
+ Services Digital Network (ISDN) addresses, as defined in the
+ International Telecommunication Union (ITU) Recommendation E.164
+ [E.164]. Each address is one value of this multi-valued attribute.
+ (Source: X.520 [X.520])
+
+ ( 2.5.4.25 NAME 'internationalISDNNumber'
+ EQUALITY numericStringMatch
+ SUBSTR numericStringSubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.36 )
+
+ 1.3.6.1.4.1.1466.115.121.1.36 refers to the Numeric String syntax
+ [RFC4517].
+
+ Example: "0198 333 333".
+
+2.16. 'l'
+
+ The 'l' ('localityName' in X.500) attribute type contains names of a
+ locality or place, such as a city, county, or other geographic
+ region. Each name is one value of this multi-valued attribute.
+ (Source: X.520 [X.520])
+
+
+
+
+
+Sciberras Standards Track [Page 10]
+
+RFC 4519 LDAP: Schema for User Applications June 2006
+
+
+ ( 2.5.4.7 NAME 'l'
+ SUP name )
+
+ Examples: "Geneva", "Paris", and "Edinburgh".
+
+2.17. 'member'
+
+ The 'member' attribute type contains the distinguished names of
+ objects that are on a list or in a group. Each name is one value of
+ this multi-valued attribute.
+ (Source: X.520 [X.520])
+
+ ( 2.5.4.31 NAME 'member'
+ SUP distinguishedName )
+
+ Examples: "cn=James Clarke,ou=Finance,o=Widget\, Inc." and
+ "cn=John Xerri,ou=Finance,o=Widget\, Inc." may
+ be two members of the financial team (group) at Widget,
+ Inc., in which case, both of these distinguished names
+ would be present as individual values of the member
+ attribute.
+
+2.18. 'name'
+
+ The 'name' attribute type is the attribute supertype from which user
+ attribute types with the name syntax inherit. Such attribute types
+ are typically used for naming. The attribute type is multi-valued.
+
+ It is unlikely that values of this type itself will occur in an
+ entry. LDAP server implementations that do not support attribute
+ subtyping need not recognize this attribute in requests. Client
+ implementations MUST NOT assume that LDAP servers are capable of
+ performing attribute subtyping.
+ (Source: X.520 [X.520])
+
+ ( 2.5.4.41 NAME 'name'
+ EQUALITY caseIgnoreMatch
+ SUBSTR caseIgnoreSubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+
+ 1.3.6.1.4.1.1466.115.121.1.15 refers to the Directory String syntax
+ [RFC4517].
+
+2.19. 'o'
+
+ The 'o' ('organizationName' in X.500) attribute type contains the
+ names of an organization. Each name is one value of this
+ multi-valued attribute.
+
+
+
+Sciberras Standards Track [Page 11]
+
+RFC 4519 LDAP: Schema for User Applications June 2006
+
+
+ (Source: X.520 [X.520])
+
+ ( 2.5.4.10 NAME 'o'
+ SUP name )
+
+ Examples: "Widget", "Widget, Inc.", and "Widget, Incorporated.".
+
+2.20. 'ou'
+
+ The 'ou' ('organizationalUnitName' in X.500) attribute type contains
+ the names of an organizational unit. Each name is one value of this
+ multi-valued attribute.
+ (Source: X.520 [X.520])
+
+ ( 2.5.4.11 NAME 'ou'
+ SUP name )
+
+ Examples: "Finance", "Human Resources", and "Research and
+ Development".
+
+2.21. 'owner'
+
+ The 'owner' attribute type contains the distinguished names of
+ objects that have an ownership responsibility for the object that is
+ owned. Each owner's name is one value of this multi-valued
+ attribute.
+ (Source: X.520 [X.520])
+
+ ( 2.5.4.32 NAME 'owner'
+ SUP distinguishedName )
+
+ Example: The mailing list object, whose DN is "cn=All Employees,
+ ou=Mailing List,o=Widget\, Inc.", is owned by the Human
+ Resources Director.
+
+ Therefore, the value of the 'owner' attribute within the
+ mailing list object, would be the DN of the director (role):
+ "cn=Human Resources Director,ou=employee,o=Widget\, Inc.".
+
+2.22. 'physicalDeliveryOfficeName'
+
+ The 'physicalDeliveryOfficeName' attribute type contains names that a
+ Postal Service uses to identify a post office.
+ (Source: X.520 [X.520])
+
+
+
+
+
+
+
+Sciberras Standards Track [Page 12]
+
+RFC 4519 LDAP: Schema for User Applications June 2006
+
+
+ ( 2.5.4.19 NAME 'physicalDeliveryOfficeName'
+ EQUALITY caseIgnoreMatch
+ SUBSTR caseIgnoreSubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+
+ 1.3.6.1.4.1.1466.115.121.1.15 refers to the Directory String syntax
+ [RFC4517].
+
+ Examples: "Bremerhaven, Main" and "Bremerhaven, Bonnstrasse".
+
+2.23. 'postalAddress'
+
+ The 'postalAddress' attribute type contains addresses used by a
+ Postal Service to perform services for the object. Each address is
+ one value of this multi-valued attribute.
+ (Source: X.520 [X.520])
+
+ ( 2.5.4.16 NAME 'postalAddress'
+ EQUALITY caseIgnoreListMatch
+ SUBSTR caseIgnoreListSubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.41 )
+
+ 1.3.6.1.4.1.1466.115.121.1.41 refers to the Postal Address syntax
+ [RFC4517].
+
+ Example: "15 Main St.$Ottawa$Canada".
+
+2.24. 'postalCode'
+
+ The 'postalCode' attribute type contains codes used by a Postal
+ Service to identify postal service zones. Each code is one value of
+ this multi-valued attribute.
+ (Source: X.520 [X.520])
+
+ ( 2.5.4.17 NAME 'postalCode'
+ EQUALITY caseIgnoreMatch
+ SUBSTR caseIgnoreSubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+
+ 1.3.6.1.4.1.1466.115.121.1.15 refers to the Directory String syntax
+ [RFC4517].
+
+ Example: "22180", to identify Vienna, VA, in the USA.
+
+
+
+
+
+
+
+
+Sciberras Standards Track [Page 13]
+
+RFC 4519 LDAP: Schema for User Applications June 2006
+
+
+2.25. 'postOfficeBox'
+
+ The 'postOfficeBox' attribute type contains postal box identifiers
+ that a Postal Service uses when a customer arranges to receive mail
+ at a box on the premises of the Postal Service. Each postal box
+ identifier is a single value of this multi-valued attribute.
+ (Source: X.520 [X.520])
+
+ ( 2.5.4.18 NAME 'postOfficeBox'
+ EQUALITY caseIgnoreMatch
+ SUBSTR caseIgnoreSubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+
+ 1.3.6.1.4.1.1466.115.121.1.15 refers to the Directory String syntax
+ [RFC4517].
+
+ Example: "Box 45".
+
+2.26. 'preferredDeliveryMethod'
+
+ The 'preferredDeliveryMethod' attribute type contains an indication
+ of the preferred method of getting a message to the object.
+ (Source: X.520 [X.520])
+
+ ( 2.5.4.28 NAME 'preferredDeliveryMethod'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.14
+ SINGLE-VALUE )
+
+ 1.3.6.1.4.1.1466.115.121.1.14 refers to the Delivery Method syntax
+ [RFC4517].
+
+ Example: If the mhs-delivery Delivery Method is preferred over
+ telephone-delivery, which is preferred over all other
+ methods, the value would be: "mhs $ telephone".
+
+2.27. 'registeredAddress'
+
+ The 'registeredAddress' attribute type contains postal addresses
+ suitable for reception of telegrams or expedited documents, where it
+ is necessary to have the recipient accept delivery. Each address is
+ one value of this multi-valued attribute.
+ (Source: X.520 [X.520])
+
+ ( 2.5.4.26 NAME 'registeredAddress'
+ SUP postalAddress
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.41 )
+
+
+
+
+
+Sciberras Standards Track [Page 14]
+
+RFC 4519 LDAP: Schema for User Applications June 2006
+
+
+ 1.3.6.1.4.1.1466.115.121.1.41 refers to the Postal Address syntax
+ [RFC4517].
+
+ Example: "Receptionist$Widget, Inc.$15 Main St.$Ottawa$Canada".
+
+2.28. 'roleOccupant'
+
+ The 'roleOccupant' attribute type contains the distinguished names of
+ objects (normally people) that fulfill the responsibilities of a role
+ object. Each distinguished name is one value of this multi-valued
+ attribute.
+ (Source: X.520 [X.520])
+
+ ( 2.5.4.33 NAME 'roleOccupant'
+ SUP distinguishedName )
+
+ Example: The role object, "cn=Human Resources
+ Director,ou=Position,o=Widget\, Inc.", is fulfilled by two
+ people whose object names are "cn=Mary
+ Smith,ou=employee,o=Widget\, Inc." and "cn=James
+ Brown,ou=employee,o=Widget\, Inc.". The 'roleOccupant'
+ attribute will contain both of these distinguished names,
+ since they are the occupants of this role.
+
+2.29. 'searchGuide'
+
+ The 'searchGuide' attribute type contains sets of information for use
+ by clients in constructing search filters. It is superseded by
+ 'enhancedSearchGuide', described above in Section 2.9. Each set is
+ one value of this multi-valued attribute.
+ (Source: X.520 [X.520])
+
+ ( 2.5.4.14 NAME 'searchGuide'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.25 )
+
+ 1.3.6.1.4.1.1466.115.121.1.25 refers to the Guide syntax [RFC4517].
+
+ Example: "person#sn$EQ".
+
+2.30. 'seeAlso'
+
+ The 'seeAlso' attribute type contains the distinguished names of
+ objects that are related to the subject object. Each related object
+ name is one value of this multi-valued attribute.
+ (Source: X.520 [X.520])
+
+ ( 2.5.4.34 NAME 'seeAlso'
+ SUP distinguishedName )
+
+
+
+Sciberras Standards Track [Page 15]
+
+RFC 4519 LDAP: Schema for User Applications June 2006
+
+
+ Example: The person object "cn=James Brown,ou=employee,o=Widget\,
+ Inc." is related to the role objects "cn=Football Team
+ Captain,ou=sponsored activities,o=Widget\, Inc." and
+ "cn=Chess Team,ou=sponsored activities,o=Widget\, Inc.".
+ Since the role objects are related to the person object, the
+ 'seeAlso' attribute will contain the distinguished name of
+ each role object as separate values.
+
+2.31. 'serialNumber'
+
+ The 'serialNumber' attribute type contains the serial numbers of
+ devices. Each serial number is one value of this multi-valued
+ attribute.
+ (Source: X.520 [X.520])
+
+ ( 2.5.4.5 NAME 'serialNumber'
+ EQUALITY caseIgnoreMatch
+ SUBSTR caseIgnoreSubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.44 )
+
+ 1.3.6.1.4.1.1466.115.121.1.44 refers to the Printable String syntax
+ [RFC4517].
+
+ Examples: "WI-3005" and "XF551426".
+
+2.32. 'sn'
+
+ The 'sn' ('surname' in X.500) attribute type contains name strings
+ for the family names of a person. Each string is one value of this
+ multi-valued attribute.
+ (Source: X.520 [X.520])
+
+ ( 2.5.4.4 NAME 'sn'
+ SUP name )
+
+ Example: "Smith".
+
+2.33. 'st'
+
+ The 'st' ('stateOrProvinceName' in X.500) attribute type contains the
+ full names of states or provinces. Each name is one value of this
+ multi-valued attribute.
+ (Source: X.520 [X.520])
+
+ ( 2.5.4.8 NAME 'st'
+ SUP name )
+
+ Example: "California".
+
+
+
+Sciberras Standards Track [Page 16]
+
+RFC 4519 LDAP: Schema for User Applications June 2006
+
+
+2.34. 'street'
+
+ The 'street' ('streetAddress' in X.500) attribute type contains site
+ information from a postal address (i.e., the street name, place,
+ avenue, and the house number). Each street is one value of this
+ multi-valued attribute.
+ (Source: X.520 [X.520])
+
+ ( 2.5.4.9 NAME 'street'
+ EQUALITY caseIgnoreMatch
+ SUBSTR caseIgnoreSubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+
+ 1.3.6.1.4.1.1466.115.121.1.15 refers to the Directory String syntax
+ [RFC4517].
+
+ Example: "15 Main St.".
+
+2.35. 'telephoneNumber'
+
+ The 'telephoneNumber' attribute type contains telephone numbers that
+ comply with the ITU Recommendation E.123 [E.123]. Each number is one
+ value of this multi-valued attribute.
+ (Source: X.520 [X.520])
+
+ ( 2.5.4.20 NAME 'telephoneNumber'
+ EQUALITY telephoneNumberMatch
+ SUBSTR telephoneNumberSubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.50 )
+
+ 1.3.6.1.4.1.1466.115.121.1.50 refers to the Telephone Number syntax
+ [RFC4517].
+
+ Example: "+1 234 567 8901".
+
+2.36. 'teletexTerminalIdentifier'
+
+ The withdrawal of Recommendation F.200 has resulted in the withdrawal
+ of this attribute.
+ (Source: X.520 [X.520])
+
+ ( 2.5.4.22 NAME 'teletexTerminalIdentifier'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.51 )
+
+ 1.3.6.1.4.1.1466.115.121.1.51 refers to the Teletex Terminal
+ Identifier syntax [RFC4517].
+
+
+
+
+
+Sciberras Standards Track [Page 17]
+
+RFC 4519 LDAP: Schema for User Applications June 2006
+
+
+2.37. 'telexNumber'
+
+ The 'telexNumber' attribute type contains sets of strings that are a
+ telex number, country code, and answerback code of a telex terminal.
+ Each set is one value of this multi-valued attribute.
+ (Source: X.520 [X.520])
+
+ ( 2.5.4.21 NAME 'telexNumber'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.52 )
+
+ 1.3.6.1.4.1.1466.115.121.1.52 refers to the Telex Number syntax
+ [RFC4517].
+
+ Example: "12345$023$ABCDE".
+
+2.38. 'title'
+
+ The 'title' attribute type contains the title of a person in their
+ organizational context. Each title is one value of this multi-valued
+ attribute.
+ (Source: X.520 [X.520])
+
+ ( 2.5.4.12 NAME 'title'
+ SUP name )
+ Examples: "Vice President", "Software Engineer", and "CEO".
+
+2.39. 'uid'
+
+ The 'uid' ('userid' in RFC 1274) attribute type contains computer
+ system login names associated with the object. Each name is one
+ value of this multi-valued attribute.
+ (Source: RFC 2798 [RFC2798] and RFC 1274 [RFC1274])
+
+ ( 0.9.2342.19200300.100.1.1 NAME 'uid'
+ EQUALITY caseIgnoreMatch
+ SUBSTR caseIgnoreSubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+
+ 1.3.6.1.4.1.1466.115.121.1.15 refers to the Directory String syntax
+ [RFC4517].
+
+ Examples: "s9709015", "admin", and "Administrator".
+
+
+
+
+
+
+
+
+
+Sciberras Standards Track [Page 18]
+
+RFC 4519 LDAP: Schema for User Applications June 2006
+
+
+2.40. 'uniqueMember'
+
+ The 'uniqueMember' attribute type contains the distinguished names of
+ an object that is on a list or in a group, where the relative
+ distinguished names of the object include a value that distinguishes
+ between objects when a distinguished name has been reused. Each
+ distinguished name is one value of this multi-valued attribute.
+ (Source: X.520 [X.520])
+
+ ( 2.5.4.50 NAME 'uniqueMember'
+ EQUALITY uniqueMemberMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.34 )
+
+ 1.3.6.1.4.1.1466.115.121.1.34 refers to the Name and Optional UID
+ syntax [RFC4517].
+
+ Example: If "ou=1st Battalion,o=Defense,c=US" is a battalion that was
+ disbanded, establishing a new battalion with the "same" name
+ would have a unique identifier value added, resulting in
+ "ou=1st Battalion, o=Defense,c=US#'010101'B".
+
+2.41. 'userPassword'
+
+ The 'userPassword' attribute contains octet strings that are known
+ only to the user and the system to which the user has access. Each
+ string is one value of this multi-valued attribute.
+
+ The application SHOULD prepare textual strings used as passwords by
+ transcoding them to Unicode, applying SASLprep [RFC4013], and
+ encoding as UTF-8. The determination of whether a password is
+ textual is a local client matter.
+ (Source: X.509 [X.509])
+
+ ( 2.5.4.35 NAME 'userPassword'
+ EQUALITY octetStringMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )
+
+ 1.3.6.1.4.1.1466.115.121.1.40 refers to the Octet String syntax
+ [RFC4517].
+
+ Passwords are stored using an Octet String syntax and are not
+ encrypted. Transfer of cleartext passwords is strongly discouraged
+ where the underlying transport service cannot guarantee
+ confidentiality and may result in disclosure of the password to
+ unauthorized parties.
+
+ An example of a need for multiple values in the 'userPassword'
+ attribute is an environment where every month the user is expected to
+
+
+
+Sciberras Standards Track [Page 19]
+
+RFC 4519 LDAP: Schema for User Applications June 2006
+
+
+ use a different password generated by some automated system. During
+ transitional periods, like the last and first day of the periods, it
+ may be necessary to allow two passwords for the two consecutive
+ periods to be valid in the system.
+
+2.42. 'x121Address'
+
+ The 'x121Address' attribute type contains data network addresses as
+ defined by ITU Recommendation X.121 [X.121]. Each address is one
+ value of this multi-valued attribute.
+ (Source: X.520 [X.520])
+
+ ( 2.5.4.24 NAME 'x121Address'
+ EQUALITY numericStringMatch
+ SUBSTR numericStringSubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.36 )
+
+ 1.3.6.1.4.1.1466.115.121.1.36 refers to the Numeric String syntax
+ [RFC4517].
+
+ Example: "36111222333444555".
+
+2.43. 'x500UniqueIdentifier'
+
+ The 'x500UniqueIdentifier' attribute type contains binary strings
+ that are used to distinguish between objects when a distinguished
+ name has been reused. Each string is one value of this multi-valued
+ attribute.
+
+ In X.520 [X.520], this attribute type is called 'uniqueIdentifier'.
+ This is a different attribute type from both the 'uid' and
+ 'uniqueIdentifier' LDAP attribute types. The 'uniqueIdentifier'
+ attribute type is defined in [RFC4524].
+ (Source: X.520 [X.520])
+
+ ( 2.5.4.45 NAME 'x500UniqueIdentifier'
+ EQUALITY bitStringMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.6 )
+
+ 1.3.6.1.4.1.1466.115.121.1.6 refers to the Bit String syntax
+ [RFC4517].
+
+3. Object Classes
+
+ LDAP servers SHOULD recognize all the Object Classes listed here as
+ values of the 'objectClass' attribute (see [RFC4512]).
+
+
+
+
+
+Sciberras Standards Track [Page 20]
+
+RFC 4519 LDAP: Schema for User Applications June 2006
+
+
+3.1. 'applicationProcess'
+
+ The 'applicationProcess' object class definition is the basis of an
+ entry that represents an application executing in a computer system.
+ (Source: X.521 [X.521])
+
+ ( 2.5.6.11 NAME 'applicationProcess'
+ SUP top
+ STRUCTURAL
+ MUST cn
+ MAY ( seeAlso $
+ ou $
+ l $
+ description ) )
+
+3.2. 'country'
+
+ The 'country' object class definition is the basis of an entry that
+ represents a country.
+ (Source: X.521 [X.521])
+
+ ( 2.5.6.2 NAME 'country'
+ SUP top
+ STRUCTURAL
+ MUST c
+ MAY ( searchGuide $
+ description ) )
+
+3.3. 'dcObject'
+
+ The 'dcObject' object class permits an entry to contains domain
+ component information. This object class is defined as auxiliary,
+ because it will be used in conjunction with an existing structural
+ object class.
+ (Source: RFC 2247 [RFC2247])
+
+ ( 1.3.6.1.4.1.1466.344 NAME 'dcObject'
+ SUP top
+ AUXILIARY
+ MUST dc )
+
+3.4. 'device'
+
+ The 'device' object class is the basis of an entry that represents an
+ appliance, computer, or network element.
+ (Source: X.521 [X.521])
+
+
+
+
+
+Sciberras Standards Track [Page 21]
+
+RFC 4519 LDAP: Schema for User Applications June 2006
+
+
+ ( 2.5.6.14 NAME 'device'
+ SUP top
+ STRUCTURAL
+ MUST cn
+ MAY ( serialNumber $
+ seeAlso $
+ owner $
+ ou $
+ o $
+ l $
+ description ) )
+
+3.5. 'groupOfNames'
+
+ The 'groupOfNames' object class is the basis of an entry that
+ represents a set of named objects including information related to
+ the purpose or maintenance of the set.
+ (Source: X.521 [X.521])
+
+ ( 2.5.6.9 NAME 'groupOfNames'
+ SUP top
+ STRUCTURAL
+ MUST ( member $
+ cn )
+ MAY ( businessCategory $
+ seeAlso $
+ owner $
+ ou $
+ o $
+ description ) )
+
+3.6. 'groupOfUniqueNames'
+
+ The 'groupOfUniqueNames' object class is the same as the
+ 'groupOfNames' object class except that the object names are not
+ repeated or reassigned within a set scope.
+ (Source: X.521 [X.521])
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Sciberras Standards Track [Page 22]
+
+RFC 4519 LDAP: Schema for User Applications June 2006
+
+
+ ( 2.5.6.17 NAME 'groupOfUniqueNames'
+ SUP top
+ STRUCTURAL
+ MUST ( uniqueMember $
+ cn )
+ MAY ( businessCategory $
+ seeAlso $
+ owner $
+ ou $
+ o $
+ description ) )
+
+3.7. 'locality'
+
+ The 'locality' object class is the basis of an entry that represents
+ a place in the physical world.
+ (Source: X.521 [X.521])
+
+ ( 2.5.6.3 NAME 'locality'
+ SUP top
+ STRUCTURAL
+ MAY ( street $
+ seeAlso $
+ searchGuide $
+ st $
+ l $
+ description ) )
+
+3.8. 'organization'
+
+ The 'organization' object class is the basis of an entry that
+ represents a structured group of people.
+ (Source: X.521 [X.521])
+
+ ( 2.5.6.4 NAME 'organization'
+ SUP top
+ STRUCTURAL
+ MUST o
+ MAY ( userPassword $ searchGuide $ seeAlso $
+ businessCategory $ x121Address $ registeredAddress $
+ destinationIndicator $ preferredDeliveryMethod $
+ telexNumber $ teletexTerminalIdentifier $
+ telephoneNumber $ internationalISDNNumber $
+ facsimileTelephoneNumber $ street $ postOfficeBox $
+ postalCode $ postalAddress $ physicalDeliveryOfficeName $
+ st $ l $ description ) )
+
+
+
+
+
+Sciberras Standards Track [Page 23]
+
+RFC 4519 LDAP: Schema for User Applications June 2006
+
+
+3.9. 'organizationalPerson'
+
+ The 'organizationalPerson' object class is the basis of an entry that
+ represents a person in relation to an organization.
+ (Source: X.521 [X.521])
+
+ ( 2.5.6.7 NAME 'organizationalPerson'
+ SUP person
+ STRUCTURAL
+ MAY ( title $ x121Address $ registeredAddress $
+ destinationIndicator $ preferredDeliveryMethod $
+ telexNumber $ teletexTerminalIdentifier $
+ telephoneNumber $ internationalISDNNumber $
+ facsimileTelephoneNumber $ street $ postOfficeBox $
+ postalCode $ postalAddress $ physicalDeliveryOfficeName $
+ ou $ st $ l ) )
+
+3.10. 'organizationalRole'
+
+ The 'organizationalRole' object class is the basis of an entry that
+ represents a job, function, or position in an organization.
+ (Source: X.521 [X.521])
+
+ ( 2.5.6.8 NAME 'organizationalRole'
+ SUP top
+ STRUCTURAL
+ MUST cn
+ MAY ( x121Address $ registeredAddress $ destinationIndicator $
+ preferredDeliveryMethod $ telexNumber $
+ teletexTerminalIdentifier $ telephoneNumber $
+ internationalISDNNumber $ facsimileTelephoneNumber $
+ seeAlso $ roleOccupant $ preferredDeliveryMethod $
+ street $ postOfficeBox $ postalCode $ postalAddress $
+ physicalDeliveryOfficeName $ ou $ st $ l $
+ description ) )
+
+3.11. 'organizationalUnit'
+
+ The 'organizationalUnit' object class is the basis of an entry that
+ represents a piece of an organization.
+ (Source: X.521 [X.521])
+
+
+
+
+
+
+
+
+
+
+Sciberras Standards Track [Page 24]
+
+RFC 4519 LDAP: Schema for User Applications June 2006
+
+
+ ( 2.5.6.5 NAME 'organizationalUnit'
+ SUP top
+ STRUCTURAL
+ MUST ou
+ MAY ( businessCategory $ description $ destinationIndicator $
+ facsimileTelephoneNumber $ internationalISDNNumber $ l $
+ physicalDeliveryOfficeName $ postalAddress $ postalCode $
+ postOfficeBox $ preferredDeliveryMethod $
+ registeredAddress $ searchGuide $ seeAlso $ st $ street $
+ telephoneNumber $ teletexTerminalIdentifier $
+ telexNumber $ userPassword $ x121Address ) )
+
+3.12 'person'
+
+ The 'person' object class is the basis of an entry that represents a
+ human being.
+ (Source: X.521 [X.521])
+
+ ( 2.5.6.6 NAME 'person'
+ SUP top
+ STRUCTURAL
+ MUST ( sn $
+ cn )
+ MAY ( userPassword $
+ telephoneNumber $
+ seeAlso $ description ) )
+
+3.13. 'residentialPerson'
+
+ The 'residentialPerson' object class is the basis of an entry that
+ includes a person's residence in the representation of the person.
+ (Source: X.521 [X.521])
+
+ ( 2.5.6.10 NAME 'residentialPerson'
+ SUP person
+ STRUCTURAL
+ MUST l
+ MAY ( businessCategory $ x121Address $ registeredAddress $
+ destinationIndicator $ preferredDeliveryMethod $
+ telexNumber $ teletexTerminalIdentifier $
+ telephoneNumber $ internationalISDNNumber $
+ facsimileTelephoneNumber $ preferredDeliveryMethod $
+ street $ postOfficeBox $ postalCode $ postalAddress $
+ physicalDeliveryOfficeName $ st $ l ) )
+
+
+
+
+
+
+
+Sciberras Standards Track [Page 25]
+
+RFC 4519 LDAP: Schema for User Applications June 2006
+
+
+3.14. 'uidObject'
+
+ The 'uidObject' object class permits an entry to contains user
+ identification information. This object class is defined as
+ auxiliary, because it will be used in conjunction with an existing
+ structural object class.
+ (Source: RFC 2377 [RFC2377])
+
+ ( 1.3.6.1.1.3.1 NAME 'uidObject'
+ SUP top
+ AUXILIARY
+ MUST uid )
+
+4. IANA Considerations
+
+ The Internet Assigned Numbers Authority (IANA) has updated the LDAP
+ descriptors registry as indicated in the following template:
+
+ Subject: Request for LDAP Descriptor Registration Update
+ Descriptor (short name): see comments
+ Object Identifier: see comments
+ Person & email address to contact for further information:
+ Andrew Sciberras <andrew.sciberras@eb2bcom.com>
+ Usage: (A = attribute type, O = Object Class) see comment
+ Specification: RFC 4519
+ Author/Change Controller: IESG
+
+ Comments
+
+ In the LDAP descriptors registry, the following descriptors (short
+ names) have been updated to refer to RFC 4519. Names that need to
+ be reserved, rather than assigned to an Object Identifier, will
+ contain an Object Identifier value of RESERVED.
+
+ NAME Type OID
+ ------------------------ ---- ----------------------------
+ applicationProcess O 2.5.6.11
+ businessCategory A 2.5.4.15
+ c A 2.5.4.6
+ cn A 2.5.4.3
+ commonName A 2.5.4.3
+ country O 2.5.6.2
+ countryName A 2.5.4.6
+ dc A 0.9.2342.19200300.100.1.25
+ dcObject O 1.3.6.1.4.1.1466.344
+ description A 2.5.4.13
+ destinationIndicator A 2.5.4.27
+ device O 2.5.6.14
+
+
+
+Sciberras Standards Track [Page 26]
+
+RFC 4519 LDAP: Schema for User Applications June 2006
+
+
+ NAME Type OID
+ ------------------------ ---- ----------------------------
+ distinguishedName A 2.5.4.49
+ dnQualifier A 2.5.4.46
+ domainComponent A 0.9.2342.19200300.100.1.25
+ enhancedSearchGuide A 2.5.4.47
+ facsimileTelephoneNumber A 2.5.4.23
+ generationQualifier A 2.5.4.44
+ givenName A 2.5.4.42
+ gn A RESERVED
+ groupOfNames O 2.5.6.9
+ groupOfUniqueNames O 2.5.6.17
+ houseIdentifier A 2.5.4.51
+ initials A 2.5.4.43
+ internationalISDNNumber A 2.5.4.25
+ l A 2.5.4.7
+ locality O 2.5.6.3
+ localityName A 2.5.4.7
+ member A 2.5.4.31
+ name A 2.5.4.41
+ o A 2.5.4.10
+ organization O 2.5.6.4
+ organizationName A 2.5.4.10
+ organizationalPerson O 2.5.6.7
+ organizationalRole O 2.5.6.8
+ organizationalUnit O 2.5.6.5
+ organizationalUnitName A 2.5.4.11
+ ou A 2.5.4.11
+ owner A 2.5.4.32
+ person O 2.5.6.6
+ physicalDeliveryOfficeName A 2.5.4.19
+ postalAddress A 2.5.4.16
+ postalCode A 2.5.4.17
+ postOfficeBox A 2.5.4.18
+ preferredDeliveryMethod A 2.5.4.28
+ registeredAddress A 2.5.4.26
+ residentialPerson O 2.5.6.10
+ roleOccupant A 2.5.4.33
+ searchGuide A 2.5.4.14
+ seeAlso A 2.5.4.34
+ serialNumber A 2.5.4.5
+ sn A 2.5.4.4
+ st A 2.5.4.8
+ street A 2.5.4.9
+ surname A 2.5.4.4
+ telephoneNumber A 2.5.4.20
+ teletexTerminalIdentifier A 2.5.4.22
+ telexNumber A 2.5.4.21
+
+
+
+Sciberras Standards Track [Page 27]
+
+RFC 4519 LDAP: Schema for User Applications June 2006
+
+
+ NAME Type OID
+ ------------------------ ---- ----------------------------
+ title A 2.5.4.12
+ uid A 0.9.2342.19200300.100.1.1
+ uidObject O 1.3.6.1.1.3.1
+ uniqueMember A 2.5.4.50
+ userid A 0.9.2342.19200300.100.1.1
+ userPassword A 2.5.4.35
+ x121Address A 2.5.4.24
+ x500UniqueIdentifier A 2.5.4.45
+
+5. Security Considerations
+
+ Attributes of directory entries are used to provide descriptive
+ information about the real-world objects they represent, which can be
+ people, organizations, or devices. Most countries have privacy laws
+ regarding the publication of information about people.
+
+ Transfer of cleartext passwords is strongly discouraged where the
+ underlying transport service cannot guarantee confidentiality and
+ integrity, since this may result in disclosure of the password to
+ unauthorized parties.
+
+ Multiple attribute values for the 'userPassword' attribute need to be
+ used with care. Especially reset/deletion of a password by an
+ administrator without knowing the old user password gets tricky or
+ impossible if multiple values for different applications are present.
+
+ Certainly, applications that intend to replace the 'userPassword'
+ value(s) with new value(s) should use modify/replaceValues (or
+ modify/deleteAttribute+addAttribute). In addition, server
+ implementations are encouraged to provide administrative controls
+ that, if enabled, restrict the 'userPassword' attribute to one value.
+
+ Note that when used for authentication purposes [RFC4513], the user
+ need only prove knowledge of one of the values, not all of the
+ values.
+
+6. Acknowledgements
+
+ The definitions, on which this document is based, have been developed
+ by committees for telecommunications and international standards.
+
+ This document is an update of RFC 2256 by Mark Wahl. RFC 2256 was a
+ product of the IETF ASID Working Group.
+
+
+
+
+
+
+Sciberras Standards Track [Page 28]
+
+RFC 4519 LDAP: Schema for User Applications June 2006
+
+
+ The 'dc' attribute type definition and the 'dcObject' object class
+ definition in this document supersede the specification in RFC 2247
+ by S. Kille, M. Wahl, A. Grimstad, R. Huber, and S. Sataluri.
+
+ The 'uid' attribute type definition in this document supersedes the
+ specification of the 'userid' in RFC 1274 by P. Barker and S. Kille
+ and of the uid in RFC 2798 by M. Smith.
+
+ The 'uidObject' object class definition in this document supersedes
+ the specification of the 'uidObject' in RFC 2377 by A. Grimstad, R.
+ Huber, S. Sataluri, and M. Wahl.
+
+ This document is based upon input of the IETF LDAPBIS working group.
+ The author wishes to thank S. Legg and K. Zeilenga for their
+ significant contribution to this update. The author would also like
+ to thank Kathy Dally, who edited early versions of this document.
+
+7. References
+
+7.1. Normative References
+
+ [E.123] Notation for national and international telephone numbers,
+ ITU-T Recommendation E.123, 1988
+
+ [E.164] The international public telecommunication numbering plan,
+ ITU-T Recommendation E.164, 1997
+
+ [F.1] Operational Provisions For The International Public
+ Telegram Service Transmission System, CCITT Recommendation
+ F.1, 1992
+
+ [F.31] Telegram Retransmission System, CCITT Recommendation F.31,
+ 1988
+
+ [ISO3166] ISO 3166, "Codes for the representation of names of
+ countries".
+
+ [RFC1034] Mockapetris, P., "Domain names - concepts and facilities",
+ STD 13, RFC 1034, November 1987.
+
+ [RFC1123] Braden, R., "Requirements for Internet Hosts - Application
+ and Support", STD 3, RFC 1123, October 1989.
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [RFC2181] Elz, R. and R. Bush, "Clarifications to the DNS
+ Specification", RFC 2181, July 1997.
+
+
+
+Sciberras Standards Track [Page 29]
+
+RFC 4519 LDAP: Schema for User Applications June 2006
+
+
+ [RFC3490] Faltstrom, P., Hoffman, P., and A. Costello,
+ "Internationalizing Domain Names in Applications (IDNA)",
+ RFC 3490, March 2003.
+
+ [RFC4013] Zeilenga, K., "SASLprep: Stringprep Profile for User Names
+ and Passwords", RFC 4013, February 2005.
+
+ [RFC4234] Crocker, D. and P. Overell, "Augmented BNF for Syntax
+ Specifications: ABNF", RFC 4234, October 2005.
+
+ [RFC4510] Zeilenga, K., Ed., "Lightweight Directory Access Protocol
+ (LDAP): Technical Specification Road Map", RFC 4510, June
+ 2006.
+
+ [RFC4512] Zeilenga, K., "Lightweight Directory Access Protocol
+ (LDAP): Directory Information Models", RFC 4512, June
+ 2006.
+
+ [RFC4517] Legg, S., Ed., "Lightweight Directory Access Protocol
+ (LDAP): Syntaxes and Matching Rules", RFC 4517, June 2006.
+
+ [X.121] International numbering plan for public data networks,
+ ITU-T Recommendation X.121, 1996
+
+ [X.509] The Directory: Authentication Framework, ITU-T
+ Recommendation X.509, 1993
+
+ [X.520] The Directory: Selected Attribute Types, ITU-T
+ Recommendation X.520, 1993
+
+ [X.521] The Directory: Selected Object Classes. ITU-T
+ Recommendation X.521, 1993
+
+7.2. Informative References
+
+ [RFC1274] Barker, P. and S. Kille, "The COSINE and Internet X.500
+ Schema", RFC 1274, November 1991.
+
+ [RFC2247] Kille, S., Wahl, M., Grimstad, A., Huber, R., and S.
+ Sataluri, "Using Domains in LDAP/X.500 Distinguished
+ Names", RFC 2247, January 1998.
+
+ [RFC2377] Grimstad, A., Huber, R., Sataluri, S., and M. Wahl,
+ "Naming Plan for Internet Directory-Enabled Applications",
+ RFC 2377, September 1998.
+
+ [RFC2798] Smith, M., "Definition of the inetOrgPerson LDAP Object
+ Class", RFC 2798, April 2000.
+
+
+
+Sciberras Standards Track [Page 30]
+
+RFC 4519 LDAP: Schema for User Applications June 2006
+
+
+ [RFC4513] Harrison R., Ed., "Lightweight Directory Access Protocol
+ (LDAP): Authentication Methods and Security Mechanisms",
+ RFC 4513, June 2006.
+
+ [RFC4523] Zeilenga, K., "Lightweight Directory Access Protocol
+ (LDAP) Schema Definitions for X.509 Certificates", RFC
+ 4523, June 2006.
+
+ [RFC4524] Zeilenga, E., Ed., "COSINE LDAP/X.500 Schema", RFC 4524,
+ June 2006.
+
+ [X.500] ITU-T Recommendations X.500 (1993) | ISO/IEC 9594-1:1994,
+ Information Technology - Open Systems Interconnection -
+ The Directory: Overview of concepts, models and services.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Sciberras Standards Track [Page 31]
+
+RFC 4519 LDAP: Schema for User Applications June 2006
+
+
+Appendix A. Changes Made Since RFC 2256
+
+ This appendix lists the changes that have been made from RFC 2256 to
+ RFC 4519.
+
+ This appendix is not a normative part of this specification, which
+ has been provided for informational purposes only.
+
+ 1. Replaced the document title.
+
+ 2. Removed the IESG Note.
+
+ 3. Dependencies on RFC 1274 have been eliminated.
+
+ 4. Added a Security Considerations section and an IANA
+ Considerations section.
+
+ 5. Deleted the conformance requirement for subschema object
+ classes in favor of a statement in [RFC4517].
+
+ 6. Added explanation to attribute types and to each object class.
+
+ 7. Removed Section 4, Syntaxes, and Section 6, Matching Rules,
+ (moved to [RFC4517]).
+
+ 8. Removed the certificate-related attribute types:
+ authorityRevocationList, cACertificate,
+ certificateRevocationList, crossCertificatePair,
+ deltaRevocationList, supportedAlgorithms, and userCertificate.
+
+ Removed the certificate-related Object Classes:
+ certificationAuthority, certificationAuthority-V2,
+ cRLDistributionPoint, strongAuthenticationUser, and
+ userSecurityInformation
+
+ LDAP PKI is now discussed in [RFC4523].
+
+ 9. Removed the dmdName, knowledgeInformation,
+ presentationAddress, protocolInformation, and
+ supportedApplicationContext attribute types and the dmd,
+ applicationEntity, and dSA object classes.
+
+ 10. Deleted the aliasedObjectName and objectClass attribute type
+ definitions. Deleted the alias and top object class
+ definitions. They are included in [RFC4512].
+
+
+
+
+
+
+Sciberras Standards Track [Page 32]
+
+RFC 4519 LDAP: Schema for User Applications June 2006
+
+
+ 11. Added the 'dc' attribute type from RFC 2247, making the
+ distinction between 'stored' and 'query' values when preparing
+ IDN strings.
+
+ 12. Numerous editorial changes.
+
+ 13. Removed upper bound after the SYNTAX oid in all attribute
+ definitions where it appeared.
+
+ 14. Added text about Unicode, SASLprep [RFC4013], and UTF-8 for
+ userPassword.
+
+ 15. Included definitions, comments and references for 'dcObject'
+ and 'uidObject'.
+
+ 16. Replaced PKI schema references to use RFC 4523.
+
+ 17. Spelt out and referenced ABNF on first usage.
+
+ 18. Removed Section 2.4 (Source). Replaced the source table with
+ explicit references for each definition.
+
+ 19. All references to an attribute type or object class are
+ enclosed in single quotes.
+
+ 20. The layout of attribute type definitions has been changed to
+ provide consistency throughout the document:
+ > Section Heading
+ > Description of Attribute type
+ > Multivalued description
+ > Source Information
+ > Definition
+ > Example
+ > Additional Comments
+
+ Adding this consistent output included the addition of
+ examples to some definitions.
+
+ 21. References to alternate names for attributes types are
+ provided with a reference to where they were originally
+ specified.
+
+ 22. Clarification of the description of 'distinguishedName' and
+ 'name', in regards to these attribute types being supertypes.
+
+ 23. Spelt out ISDN on first usage.
+
+
+
+
+
+Sciberras Standards Track [Page 33]
+
+RFC 4519 LDAP: Schema for User Applications June 2006
+
+
+ 24. Inserted a reference to [RFC4517] for the
+ 'teletexTerminalIdentifier' definition's SYNTAX OID.
+
+ 25. Additional names were added to the IANA Considerations. Names
+ include 'commonName', 'dcObject', 'domainComponent', 'GN',
+ 'localityName', 'organizationName', 'organizationUnitName',
+ 'surname', 'uidObject' and 'userid'.
+
+ 26. Renamed all instances of supercede to supersede.
+
+ 27. Moved [F.1], [F.31] and [RFC4013] from informative to
+ normative references.
+
+ 28. Changed the 'c' definition to be consistent with X.500.
+
+Author's Address
+
+ Andrew Sciberras
+ eB2Bcom
+ Suite 3, Woodhouse Corporate Centre,
+ 935 Station Street,
+ Box Hill North, Victoria 3129
+ AUSTRALIA
+
+ Phone: +61 3 9896 7833
+ EMail: andrew.sciberras@eb2bcom.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Sciberras Standards Track [Page 34]
+
+RFC 4519 LDAP: Schema for User Applications June 2006
+
+
+Full Copyright Statement
+
+ Copyright (C) The Internet Society (2006).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
+ ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
+ INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
+ INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be
+ found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at
+ ietf-ipr@ietf.org.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is provided by the IETF
+ Administrative Support Activity (IASA).
+
+
+
+
+
+
+
+Sciberras Standards Track [Page 35]
+
diff --git a/source4/ldap_server/devdocs/rfc4520.txt b/source4/ldap_server/devdocs/rfc4520.txt
new file mode 100644
index 0000000000..9ef5daadea
--- /dev/null
+++ b/source4/ldap_server/devdocs/rfc4520.txt
@@ -0,0 +1,1067 @@
+
+
+
+
+
+
+Network Working Group K. Zeilenga
+Request for Comments: 4520 OpenLDAP Foundation
+BCP: 64 June 2006
+Obsoletes: 3383
+Category: Best Current Practice
+
+
+ Internet Assigned Numbers Authority (IANA) Considerations for
+ the Lightweight Directory Access Protocol (LDAP)
+
+Status of This Memo
+
+ This document specifies an Internet Best Current Practices for the
+ Internet Community, and requests discussion and suggestions for
+ improvements. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2006).
+
+Abstract
+
+ This document provides procedures for registering extensible elements
+ of the Lightweight Directory Access Protocol (LDAP). The document
+ also provides guidelines to the Internet Assigned Numbers Authority
+ (IANA) describing conditions under which new values can be assigned.
+
+1. Introduction
+
+ The Lightweight Directory Access Protocol [RFC4510] (LDAP) is an
+ extensible protocol. LDAP supports:
+
+ - the addition of new operations,
+ - the extension of existing operations, and
+ - the extensible schema.
+
+ This document details procedures for registering values used to
+ unambiguously identify extensible elements of the protocol, including
+ the following:
+
+ - LDAP message types
+ - LDAP extended operations and controls
+ - LDAP result codes
+ - LDAP authentication methods
+ - LDAP attribute description options
+ - Object Identifier descriptors
+
+
+
+
+
+Zeilenga Best Current Practice [Page 1]
+
+RFC 4520 IANA Considerations for LDAP June 2006
+
+
+ These registries are maintained by the Internet Assigned Numbers
+ Authority (IANA).
+
+ In addition, this document provides guidelines to IANA describing the
+ conditions under which new values can be assigned.
+
+ This document replaces RFC 3383.
+
+2. Terminology and Conventions
+
+ This section details terms and conventions used in this document.
+
+2.1. Policy Terminology
+
+ The terms "IESG Approval", "Standards Action", "IETF Consensus",
+ "Specification Required", "First Come First Served", "Expert Review",
+ and "Private Use" are used as defined in BCP 26 [RFC2434].
+
+ The term "registration owner" (or "owner") refers to the party
+ authorized to change a value's registration.
+
+2.2. Requirement Terminology
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in BCP 14 [RFC2119]. In
+ this case, "the specification", as used by BCP 14, refers to the
+ processing of protocols being submitted to the IETF standards
+ process.
+
+2.3. Common ABNF Productions
+
+ A number of syntaxes in this document are described using ABNF
+ [RFC4234]. These syntaxes rely on the following common productions:
+
+ ALPHA = %x41-5A / %x61-7A ; "A"-"Z" / "a"-"z"
+ LDIGIT = %x31-39 ; "1"-"9"
+ DIGIT = %x30 / LDIGIT ; "0"-"9"
+ HYPHEN = %x2D ; "-"
+ DOT = %x2E ; "."
+ number = DIGIT / ( LDIGIT 1*DIGIT )
+ keychar = ALPHA / DIGIT / HYPHEN
+ leadkeychar = ALPHA
+ keystring = leadkeychar *keychar
+ keyword = keystring
+
+ Keywords are case insensitive.
+
+
+
+
+Zeilenga Best Current Practice [Page 2]
+
+RFC 4520 IANA Considerations for LDAP June 2006
+
+
+3. IANA Considerations for LDAP
+
+ This section details each kind of protocol value that can be
+ registered and provides IANA guidelines on how to assign new values.
+
+ IANA may reject obviously bogus registrations.
+
+ LDAP values specified in RFCs MUST be registered. Other LDAP values,
+ except those in private-use name spaces, SHOULD be registered. RFCs
+ SHOULD NOT reference, use, or otherwise recognize unregistered LDAP
+ values.
+
+3.1. Object Identifiers
+
+ Numerous LDAP schema and protocol elements are identified by Object
+ Identifiers (OIDs) [X.680]. Specifications that assign OIDs to
+ elements SHOULD state who delegated the OIDs for their use.
+
+ For IETF-developed elements, specifications SHOULD use OIDs under
+ "Internet Directory Numbers" (1.3.6.1.1.x). For elements developed
+ by others, any properly delegated OID can be used, including those
+ under "Internet Directory Numbers" (1.3.6.1.1.x) or "Internet Private
+ Enterprise Numbers" (1.3.6.1.4.1.x).
+
+ Internet Directory Numbers (1.3.6.1.1.x) will be assigned upon Expert
+ Review with Specification Required. Only one OID per specification
+ will be assigned. The specification MAY then assign any number of
+ OIDs within this arc without further coordination with IANA.
+
+ Internet Private Enterprise Numbers (1.3.6.1.4.1.x) are assigned by
+ IANA <http://www.iana.org/cgi-bin/enterprise.pl>. Practices for IANA
+ assignment of Internet Private Enterprise Numbers are detailed in RFC
+ 2578 [RFC2578].
+
+ To avoid interoperability problems between early implementations of a
+ "work in progress" and implementations of the published specification
+ (e.g., the RFC), experimental OIDs SHOULD be used in "works in
+ progress" and early implementations. OIDs under the Internet
+ Experimental OID arc (1.3.6.1.3.x) may be used for this purpose.
+ Practices for IANA assignment of these Internet Experimental numbers
+ are detailed in RFC 2578 [RFC2578].
+
+3.2. Protocol Mechanisms
+
+ LDAP provides a number of Root DSA-Specific Entry (DSE) attributes
+ for discovery of protocol mechanisms identified by OIDs, including
+ the supportedControl, supportedExtension, and supportedFeatures
+ attributes [RFC4512].
+
+
+
+Zeilenga Best Current Practice [Page 3]
+
+RFC 4520 IANA Considerations for LDAP June 2006
+
+
+ A registry of OIDs used for discovery of protocol mechanisms is
+ provided to allow implementors and others to locate the technical
+ specification for these protocol mechanisms. Future specifications
+ of additional Root DSE attributes holding values identifying protocol
+ mechanisms MAY extend this registry for their values.
+
+ Protocol mechanisms are registered on a First Come First Served
+ basis.
+
+3.3. LDAP Syntaxes
+
+ This registry provides a listing of LDAP syntaxes [RFC4512]. Each
+ LDAP syntax is identified by an OID. This registry is provided to
+ allow implementors and others to locate the technical specification
+ describing a particular LDAP Syntax.
+
+ LDAP Syntaxes are registered on a First Come First Served with
+ Specification Required basis.
+
+ Note: Unlike object classes, attribute types, and various other kinds
+ of schema elements, descriptors are not used in LDAP to
+ identify LDAP Syntaxes.
+
+3.4. Object Identifier Descriptors
+
+ LDAP allows short descriptive names (or descriptors) to be used
+ instead of a numeric Object Identifier to identify select protocol
+ extensions [RFC4511], schema elements [RFC4512], LDAP URL [RFC4516]
+ extensions, and other objects.
+
+ Although the protocol allows the same descriptor to refer to
+ different object identifiers in certain cases and the registry
+ supports multiple registrations of the same descriptor (each
+ indicating a different kind of schema element and different object
+ identifier), multiple registrations of the same descriptor are to be
+ avoided. All such multiple registration requests require Expert
+ Review.
+
+ Descriptors are restricted to strings of UTF-8 [RFC3629] encoded
+ Unicode characters restricted by the following ABNF:
+
+ name = keystring
+
+ Descriptors are case insensitive.
+
+ Multiple names may be assigned to a given OID. For purposes of
+ registration, an OID is to be represented in numeric OID form (e.g.,
+ 1.1.0.23.40) conforming to the following ABNF:
+
+
+
+Zeilenga Best Current Practice [Page 4]
+
+RFC 4520 IANA Considerations for LDAP June 2006
+
+
+ numericoid = number 1*( DOT number )
+
+ While the protocol places no maximum length restriction upon
+ descriptors, they should be short. Descriptors longer than 48
+ characters may be viewed as too long to register.
+
+ A value ending with a hyphen ("-") reserves all descriptors that
+ start with that value. For example, the registration of the option
+ "descrFamily-" reserves all options that start with "descrFamily-"
+ for some related purpose.
+
+ Descriptors beginning with "x-" are for Private Use and cannot be
+ registered.
+
+ Descriptors beginning with "e-" are reserved for experiments and will
+ be registered on a First Come First Served basis.
+
+ All other descriptors require Expert Review to be registered.
+
+ The registrant need not "own" the OID being named.
+
+ The OID name space is managed by the ISO/IEC Joint Technical
+ Committee 1 - Subcommittee 6.
+
+3.5. AttributeDescription Options
+
+ An AttributeDescription [RFC4512] can contain zero or more options
+ specifying additional semantics. An option SHALL be restricted to a
+ string of UTF-8 encoded Unicode characters limited by the following
+ ABNF:
+
+ option = keystring
+
+ Options are case insensitive.
+
+ While the protocol places no maximum length restriction upon option
+ strings, they should be short. Options longer than 24 characters may
+ be viewed as too long to register.
+
+ Values ending with a hyphen ("-") reserve all option names that start
+ with the name. For example, the registration of the option
+ "optionFamily-" reserves all options that start with "optionFamily-"
+ for some related purpose.
+
+ Options beginning with "x-" are for Private Use and cannot be
+ registered.
+
+
+
+
+
+Zeilenga Best Current Practice [Page 5]
+
+RFC 4520 IANA Considerations for LDAP June 2006
+
+
+ Options beginning with "e-" are reserved for experiments and will be
+ registered on a First Come First Served basis.
+
+ All other options require Standards Action or Expert Review with
+ Specification Required to be registered.
+
+3.6. LDAP Message Types
+
+ Each protocol message is encapsulated in an LDAPMessage envelope
+ [RFC4511. The protocolOp CHOICE indicates the type of message
+ encapsulated. Each message type consists of an ASN.1 identifier in
+ the form of a keyword and a non-negative choice number. The choice
+ number is combined with the class (APPLICATION) and data type
+ (CONSTRUCTED or PRIMITIVE) to construct the BER tag in the message's
+ encoding. The choice numbers for existing protocol messages are
+ implicit in the protocol's ASN.1 defined in [RFC4511].
+
+ New values will be registered upon Standards Action.
+
+ Note: LDAP provides extensible messages that reduce but do not
+ eliminate the need to add new message types.
+
+3.7. LDAP Authentication Method
+
+ The LDAP Bind operation supports multiple authentication methods
+ [RFC4511]. Each authentication choice consists of an ASN.1
+ identifier in the form of a keyword and a non-negative integer.
+
+ The registrant SHALL classify the authentication method usage using
+ one of the following terms:
+
+ COMMON - method is appropriate for common use on the
+ Internet.
+ LIMITED USE - method is appropriate for limited use.
+ OBSOLETE - method has been deprecated or otherwise found to
+ be inappropriate for any use.
+
+ Methods without publicly available specifications SHALL NOT be
+ classified as COMMON. New registrations of the class OBSOLETE cannot
+ be registered.
+
+ New authentication method integers in the range 0-1023 require
+ Standards Action to be registered. New authentication method
+ integers in the range 1024-4095 require Expert Review with
+ Specification Required. New authentication method integers in the
+ range 4096-16383 will be registered on a First Come First Served
+ basis. Keywords associated with integers in the range 0-4095 SHALL
+ NOT start with "e-" or "x-". Keywords associated with integers in
+
+
+
+Zeilenga Best Current Practice [Page 6]
+
+RFC 4520 IANA Considerations for LDAP June 2006
+
+
+ the range 4096-16383 SHALL start with "e-". Values greater than or
+ equal to 16384 and keywords starting with "x-" are for Private Use
+ and cannot be registered.
+
+ Note: LDAP supports Simple Authentication and Security Layers
+ [RFC4422] as an authentication choice. SASL is an extensible
+ authentication framework.
+
+3.8. LDAP Result Codes
+
+ LDAP result messages carry a resultCode enumerated value to indicate
+ the outcome of the operation [RFC4511]. Each result code consists of
+ an ASN.1 identifier in the form of a keyword and a non-negative
+ integer.
+
+ New resultCodes integers in the range 0-1023 require Standards Action
+ to be registered. New resultCode integers in the range 1024-4095
+ require Expert Review with Specification Required. New resultCode
+ integers in the range 4096-16383 will be registered on a First Come
+ First Served basis. Keywords associated with integers in the range
+ 0-4095 SHALL NOT start with "e-" or "x-". Keywords associated with
+ integers in the range 4096-16383 SHALL start with "e-". Values
+ greater than or equal to 16384 and keywords starting with "x-" are
+ for Private Use and cannot be registered.
+
+3.9. LDAP Search Scope
+
+ LDAP SearchRequest messages carry a scope-enumerated value to
+ indicate the extent of search within the DIT [RFC4511]. Each search
+ value consists of an ASN.1 identifier in the form of a keyword and a
+ non-negative integer.
+
+ New scope integers in the range 0-1023 require Standards Action to be
+ registered. New scope integers in the range 1024-4095 require Expert
+ Review with Specification Required. New scope integers in the range
+ 4096-16383 will be registered on a First Come First Served basis.
+ Keywords associated with integers in the range 0-4095 SHALL NOT start
+ with "e-" or "x-". Keywords associated with integers in the range
+ 4096-16383 SHALL start with "e-". Values greater than or equal to
+ 16384 and keywords starting with "x-" are for Private Use and cannot
+ be registered.
+
+3.10. LDAP Filter Choice
+
+ LDAP filters are used in making assertions against an object
+ represented in the directory [RFC4511]. The Filter CHOICE indicates
+ a type of assertion. Each Filter CHOICE consists of an ASN.1
+ identifier in the form of a keyword and a non-negative choice number.
+
+
+
+Zeilenga Best Current Practice [Page 7]
+
+RFC 4520 IANA Considerations for LDAP June 2006
+
+
+ The choice number is combined with the class (APPLICATION) and data
+ type (CONSTRUCTED or PRIMITIVE) to construct the BER tag in the
+ message's encoding.
+
+ Note: LDAP provides the extensibleMatching choice, which reduces but
+ does not eliminate the need to add new filter choices.
+
+3.11. LDAP ModifyRequest Operation Type
+
+ The LDAP ModifyRequest carries a sequence of modification operations
+ [RFC4511]. Each kind (e.g., add, delete, replace) of operation
+ consists of an ASN.1 identifier in the form of a keyword and a non-
+ negative integer.
+
+ New operation type integers in the range 0-1023 require Standards
+ Action to be registered. New operation type integers in the range
+ 1024-4095 require Expert Review with Specification Required. New
+ operation type integers in the range 4096-16383 will be registered on
+ a First Come First Served basis. Keywords associated with integers
+ in the range 0-4095 SHALL NOT start with "e-" or "x-". Keywords
+ associated with integers in the range 4096-16383 SHALL start with
+ "e-". Values greater than or equal to 16384 and keywords starting
+ with "x-" are for Private Use and cannot be registered.
+
+3.12. LDAP authzId Prefixes
+
+ Authorization Identities in LDAP are strings conforming to the
+ <authzId> production [RFC4513]. This production is extensible. Each
+ new specific authorization form is identified by a prefix string
+ conforming to the following ABNF:
+
+ prefix = keystring COLON
+ COLON = %x3A ; COLON (":" U+003A)
+
+ Prefixes are case insensitive.
+
+ While the protocol places no maximum length restriction upon prefix
+ strings, they should be short. Prefixes longer than 12 characters
+ may be viewed as too long to register.
+
+ Prefixes beginning with "x-" are for Private Use and cannot be
+ registered.
+
+ Prefixes beginning with "e-" are reserved for experiments and will be
+ registered on a First Come First Served basis.
+
+ All other prefixes require Standards Action or Expert Review with
+ Specification Required to be registered.
+
+
+
+Zeilenga Best Current Practice [Page 8]
+
+RFC 4520 IANA Considerations for LDAP June 2006
+
+
+3.13. Directory Systems Names
+
+ The IANA-maintained "Directory Systems Names" registry [IANADSN] of
+ valid keywords for well-known attributes was used in the LDAPv2
+ string representation of a distinguished name [RFC1779]. LDAPv2 is
+ now Historic [RFC3494].
+
+ Directory systems names are not known to be used in any other
+ context. LDAPv3 [RFC4514] uses Object Identifier Descriptors
+ [Section 3.2] (which have a different syntax than directory system
+ names).
+
+ New Directory System Names will no longer be accepted. For
+ historical purposes, the current list of registered names should
+ remain publicly available.
+
+4. Registration Procedure
+
+ The procedure given here MUST be used by anyone who wishes to use a
+ new value of a type described in Section 3 of this document.
+
+ The first step is for the requester to fill out the appropriate form.
+ Templates are provided in Appendix A.
+
+ If the policy is Standards Action, the completed form SHOULD be
+ provided to the IESG with the request for Standards Action. Upon
+ approval of the Standards Action, the IESG SHALL forward the request
+ (possibly revised) to IANA. The IESG SHALL be regarded as the
+ registration owner of all values requiring Standards Action.
+
+ If the policy is Expert Review, the requester SHALL post the
+ completed form to the <directory@apps.ietf.org> mailing list for
+ public review. The review period is two (2) weeks. If a revised
+ form is later submitted, the review period is restarted. Anyone may
+ subscribe to this list by sending a request to <directory-
+ request@apps.ietf.org>. During the review, objections may be raised
+ by anyone (including the Expert) on the list. After completion of
+ the review, the Expert, based on public comments, SHALL either
+ approve the request and forward it to the IANA OR deny the request.
+ In either case, the Expert SHALL promptly notify the requester of the
+ action. Actions of the Expert may be appealed [RFC2026]. The Expert
+ is appointed by Applications Area Directors. The requester is viewed
+ as the registration owner of values registered under Expert Review.
+
+ If the policy is First Come First Served, the requester SHALL submit
+ the completed form directly to the IANA: <iana@iana.org>. The
+ requester is viewed as the registration owner of values registered
+ under First Come First Served.
+
+
+
+Zeilenga Best Current Practice [Page 9]
+
+RFC 4520 IANA Considerations for LDAP June 2006
+
+
+ Neither the Expert nor IANA will take position on the claims of
+ copyright or trademark issues regarding completed forms.
+
+ Prior to submission of the Internet Draft (I-D) to the RFC Editor but
+ after IESG review and tentative approval, the document editor SHOULD
+ revise the I-D to use registered values.
+
+5. Registration Maintenance
+
+ This section discusses maintenance of registrations.
+
+5.1. Lists of Registered Values
+
+ IANA makes lists of registered values readily available to the
+ Internet community on its web site: <http://www.iana.org/>.
+
+5.2. Change Control
+
+ The registration owner MAY update the registration subject to the
+ same constraints and review as with new registrations. In cases
+ where the registration owner is unable or is unwilling to make
+ necessary updates, the IESG MAY assume ownership of the registration
+ in order to update the registration.
+
+5.3. Comments
+
+ For cases where others (anyone other than the registration owner)
+ have significant objections to the claims in a registration and the
+ registration owner does not agree to change the registration,
+ comments MAY be attached to a registration upon Expert Review. For
+ registrations owned by the IESG, the objections SHOULD be addressed
+ by initiating a request for Expert Review.
+
+ The form of these requests is ad hoc, but MUST include the specific
+ objections to be reviewed and SHOULD contain (directly or by
+ reference) materials supporting the objections.
+
+6. Security Considerations
+
+ The security considerations detailed in BCP 26 [RFC2434] are
+ generally applicable to this document. Additional security
+ considerations specific to each name space are discussed in Section
+ 3, where appropriate.
+
+ Security considerations for LDAP are discussed in documents
+ comprising the technical specification [RFC4510].
+
+
+
+
+
+Zeilenga Best Current Practice [Page 10]
+
+RFC 4520 IANA Considerations for LDAP June 2006
+
+
+7. Acknowledgement
+
+ This document is a product of the IETF LDAP Revision (LDAPBIS)
+ Working Group (WG). This document is a revision of RFC 3383, also a
+ product of the LDAPBIS WG.
+
+ This document includes text borrowed from "Guidelines for Writing an
+ IANA Considerations Section in RFCs" [RFC2434] by Thomas Narten and
+ Harald Alvestrand.
+
+8. References
+
+8.1. Normative References
+
+ [RFC2026] Bradner, S., "The Internet Standards Process -- Revision
+ 3", BCP 9, RFC 2026, October 1996.
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [RFC2434] Narten, T. and H. Alvestrand, "Guidelines for Writing an
+ IANA Considerations Section in RFCs", BCP 26, RFC 2434,
+ October 1998.
+
+ [RFC2578] McCloghrie, K., Perkins, D., and J. Schoenwaelder,
+ "Structure of Management Information Version 2 (SMIv2)",
+ STD 58, RFC 2578, April 1999.
+
+ [RFC3629] Yergeau, F., "UTF-8, a transformation format of ISO
+ 10646", STD 63, RFC 3629, November 2003.
+
+ [RFC4234] Crocker, D. and P. Overell, "Augmented BNF for Syntax
+ Specifications: ABNF", RFC 4234, October 2005.
+
+ [RFC4510] Zeilenga, K., Ed., "Lightweight Directory Access Protocol
+ (LDAP): Technical Specification Road Map", RFC 4510, June
+ 2006.
+
+ [RFC4511] Sermersheim, J., Ed., "Lightweight Directory Access
+ Protocol (LDAP): The Protocol", RFC 4511, June 2006.
+
+ [RFC4512] Zeilenga, K., "Lightweight Directory Access Protocol
+ (LDAP): Directory Information Models", RFC 4512, June
+ 2006.
+
+ [RFC4513] Harrison, R., Ed., "Lightweight Directory Access Protocol
+ (LDAP): Authentication Methods and Security Mechanisms",
+ RFC 4513, June 2006.
+
+
+
+Zeilenga Best Current Practice [Page 11]
+
+RFC 4520 IANA Considerations for LDAP June 2006
+
+
+ [RFC4516] Smith, M., Ed. and T. Howes, "Lightweight Directory Access
+ Protocol (LDAP): Uniform Resource Locator", RFC 4516, June
+ 2006.
+
+ [Unicode] The Unicode Consortium, "The Unicode Standard, Version
+ 3.2.0" is defined by "The Unicode Standard, Version 3.0"
+ (Reading, MA, Addison-Wesley, 2000. ISBN 0-201-61633-5),
+ as amended by the "Unicode Standard Annex #27: Unicode
+ 3.1" (http://www.unicode.org/reports/tr27/) and by the
+ "Unicode Standard Annex #28: Unicode 3.2"
+ (http://www.unicode.org/reports/tr28/).
+
+ [X.680] International Telecommunication Union - Telecommunication
+ Standardization Sector, "Abstract Syntax Notation One
+ (ASN.1) - Specification of Basic Notation", X.680(2002)
+ (also ISO/IEC 8824-1:2002).
+
+8.2. Informative References
+
+ [RFC1779] Kille, S., "A String Representation of Distinguished
+ Names", RFC 1779, March 1995.
+
+ [RFC3494] Zeilenga, K.,"Lightweight Directory Access Protocol
+ version 2 (LDAPv2) to Historic Status", RFC 3494, March
+ 2003.
+
+ [RFC4514] Zeilenga, K., Ed., "Lightweight Directory Access Protocol
+ (LDAP): String Representation of Distinguished Names", RFC
+ 4514, June 2006.
+
+ [RFC4422] Melnikov, A., Ed. and K. Zeilenga, Ed., "Simple
+ Authentication and Security Layer (SASL)", RFC 4422, June
+ 2006.
+
+ [IANADSN] IANA, "Directory Systems Names",
+ http://www.iana.org/assignments/directory-system-names.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Zeilenga Best Current Practice [Page 12]
+
+RFC 4520 IANA Considerations for LDAP June 2006
+
+
+Appendix A. Registration Templates
+
+ This appendix provides registration templates for registering new
+ LDAP values. Note that more than one value may be requested by
+ extending the template by listing multiple values, or through use of
+ tables.
+
+A.1. LDAP Object Identifier Registration Template
+
+ Subject: Request for LDAP OID Registration
+
+ Person & email address to contact for further information:
+
+ Specification: (I-D)
+
+ Author/Change Controller:
+
+ Comments:
+
+ (Any comments that the requester deems relevant to the request.)
+
+A.2. LDAP Protocol Mechanism Registration Template
+
+ Subject: Request for LDAP Protocol Mechanism Registration
+
+ Object Identifier:
+
+ Description:
+
+ Person & email address to contact for further information:
+
+ Usage: (One of Control or Extension or Feature or other)
+
+ Specification: (RFC, I-D, URI)
+
+ Author/Change Controller:
+
+ Comments:
+
+ (Any comments that the requester deems relevant to the request.)
+
+
+
+
+
+
+
+
+
+
+
+Zeilenga Best Current Practice [Page 13]
+
+RFC 4520 IANA Considerations for LDAP June 2006
+
+
+A.3. LDAP Syntax Registration Template
+
+ Subject: Request for LDAP Syntax Registration
+
+ Object Identifier:
+
+ Description:
+
+ Person & email address to contact for further information:
+
+ Specification: (RFC, I-D, URI)
+
+ Author/Change Controller:
+
+ Comments:
+
+ (Any comments that the requester deems relevant to the request.)
+
+A.4. LDAP Descriptor Registration Template
+
+ Subject: Request for LDAP Descriptor Registration
+
+ Descriptor (short name):
+
+ Object Identifier:
+
+ Person & email address to contact for further information:
+
+ Usage: (One of administrative role, attribute type, matching rule,
+ name form, object class, URL extension, or other)
+
+ Specification: (RFC, I-D, URI)
+
+ Author/Change Controller:
+
+ Comments:
+
+ (Any comments that the requester deems relevant to the request.)
+
+
+
+
+
+
+
+
+
+
+
+
+
+Zeilenga Best Current Practice [Page 14]
+
+RFC 4520 IANA Considerations for LDAP June 2006
+
+
+A.5. LDAP Attribute Description Option Registration Template
+
+ Subject: Request for LDAP Attribute Description Option Registration
+ Option Name:
+
+ Family of Options: (YES or NO)
+
+ Person & email address to contact for further information:
+
+ Specification: (RFC, I-D, URI)
+
+ Author/Change Controller:
+
+ Comments:
+
+ (Any comments that the requester deems relevant to the request.)
+
+A.6. LDAP Message Type Registration Template
+
+ Subject: Request for LDAP Message Type Registration
+
+ LDAP Message Name:
+
+ Person & email address to contact for further information:
+
+ Specification: (Approved I-D)
+
+ Comments:
+
+ (Any comments that the requester deems relevant to the request.)
+
+A.7. LDAP Authentication Method Registration Template
+
+ Subject: Request for LDAP Authentication Method Registration
+
+ Authentication Method Name:
+
+ Person & email address to contact for further information:
+
+ Specification: (RFC, I-D, URI)
+
+ Intended Usage: (One of COMMON, LIMITED-USE, OBSOLETE)
+
+ Author/Change Controller:
+
+ Comments:
+
+ (Any comments that the requester deems relevant to the request.)
+
+
+
+Zeilenga Best Current Practice [Page 15]
+
+RFC 4520 IANA Considerations for LDAP June 2006
+
+
+A.8. LDAP Result Code Registration Template
+
+ Subject: Request for LDAP Result Code Registration
+
+ Result Code Name:
+
+ Person & email address to contact for further information:
+
+ Specification: (RFC, I-D, URI)
+
+ Author/Change Controller:
+
+ Comments:
+
+ (Any comments that the requester deems relevant to the request.)
+
+A.8. LDAP Search Scope Registration Template
+
+ Subject: Request for LDAP Search Scope Registration
+
+ Search Scope Name:
+
+ Filter Scope String:
+
+ Person & email address to contact for further information:
+
+ Specification: (RFC, I-D, URI)
+
+ Author/Change Controller:
+
+ Comments:
+
+ (Any comments that the requester deems relevant to the request.)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Zeilenga Best Current Practice [Page 16]
+
+RFC 4520 IANA Considerations for LDAP June 2006
+
+
+A.9. LDAP Filter Choice Registration Template
+
+ Subject: Request for LDAP Filter Choice Registration
+
+ Filter Choice Name:
+
+ Person & email address to contact for further information:
+
+ Specification: (RFC, I-D, URI)
+
+ Author/Change Controller:
+
+ Comments:
+
+ (Any comments that the requester deems relevant to the request.)
+
+A.10. LDAP ModifyRequest Operation Registration Template
+
+ Subject: Request for LDAP ModifyRequest Operation Registration
+
+ ModifyRequest Operation Name:
+
+ Person & email address to contact for further information:
+
+ Specification: (RFC, I-D, URI)
+
+ Author/Change Controller:
+
+ Comments:
+
+ (Any comments that the requester deems relevant to the request.)
+
+Appendix B. Changes since RFC 3383
+
+ This informative appendix provides a summary of changes made since
+ RFC 3383.
+
+ - Object Identifier Descriptors practices were updated to require
+ all descriptors defined in RFCs to be registered and
+ recommending all other descriptors (excepting those in
+ private-use name space) be registered. Additionally, all
+ requests for multiple registrations of the same descriptor are
+ now subject to Expert Review.
+
+ - Protocol Mechanisms practices were updated to include values of
+ the 'supportedFeatures' attribute type.
+
+
+
+
+
+Zeilenga Best Current Practice [Page 17]
+
+RFC 4520 IANA Considerations for LDAP June 2006
+
+
+ - LDAP Syntax, Search Scope, Filter Choice, ModifyRequest
+ operation, and authzId prefixes registries were added.
+
+ - References to RFCs comprising the LDAP technical specifications
+ have been updated to latest revisions.
+
+ - References to ISO 10646 have been replaced with [Unicode].
+
+ - The "Assigned Values" appendix providing initial registry
+ values was removed.
+
+ - Numerous editorial changes were made.
+
+Author's Address
+
+ Kurt D. Zeilenga
+ OpenLDAP Foundation
+
+ EMail: Kurt@OpenLDAP.org
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Zeilenga Best Current Practice [Page 18]
+
+RFC 4520 IANA Considerations for LDAP June 2006
+
+
+Full Copyright Statement
+
+ Copyright (C) The Internet Society (2006).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
+ ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
+ INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
+ INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be
+ found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at
+ ietf-ipr@ietf.org.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is provided by the IETF
+ Administrative Support Activity (IASA).
+
+
+
+
+
+
+
+Zeilenga Best Current Practice [Page 19]
+
diff --git a/source4/ldap_server/devdocs/rfc4521.txt b/source4/ldap_server/devdocs/rfc4521.txt
new file mode 100644
index 0000000000..813ff1e30f
--- /dev/null
+++ b/source4/ldap_server/devdocs/rfc4521.txt
@@ -0,0 +1,899 @@
+
+
+
+
+
+
+Network Working Group K. Zeilenga
+Request for Comments: 4521 OpenLDAP Foundation
+BCP: 118 June 2006
+Category: Best Current Practice
+
+
+ Considerations for
+ Lightweight Directory Access Protocol (LDAP) Extensions
+
+Status of This Memo
+
+ This document specifies an Internet Best Current Practices for the
+ Internet Community, and requests discussion and suggestions for
+ improvements. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2006).
+
+Abstract
+
+ The Lightweight Directory Access Protocol (LDAP) is extensible. It
+ provides mechanisms for adding new operations, extending existing
+ operations, and expanding user and system schemas. This document
+ discusses considerations for designers of LDAP extensions.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Zeilenga Best Current Practice [Page 1]
+
+RFC 4521 LDAP Extensions June 2006
+
+
+Table of Contents
+
+ 1. Introduction ....................................................3
+ 1.1. Terminology ................................................3
+ 2. General Considerations ..........................................4
+ 2.1. Scope of Extension .........................................4
+ 2.2. Interaction between extensions .............................4
+ 2.3. Discovery Mechanism ........................................4
+ 2.4. Internationalization Considerations ........................5
+ 2.5. Use of the Basic Encoding Rules ............................5
+ 2.6. Use of Formal Languages ....................................5
+ 2.7. Examples ...................................................5
+ 2.8. Registration of Protocol Values ............................5
+ 3. LDAP Operation Extensions .......................................6
+ 3.1. Controls ...................................................6
+ 3.1.1. Extending Bind Operation with Controls ..............6
+ 3.1.2. Extending the Start TLS Operation with Controls .....7
+ 3.1.3. Extending the Search Operation with Controls ........7
+ 3.1.4. Extending the Update Operations with Controls .......8
+ 3.1.5. Extending the Responseless Operations with Controls..8
+ 3.2. Extended Operations ........................................8
+ 3.3. Intermediate Responses .....................................8
+ 3.4. Unsolicited Notifications ..................................9
+ 4. Extending the LDAP ASN.1 Definition .............................9
+ 4.1. Result Codes ...............................................9
+ 4.2. LDAP Message Types .........................................9
+ 4.3. Authentication Methods ....................................10
+ 4.4. General ASN.1 Extensibility ...............................10
+ 5. Schema Extensions ..............................................10
+ 5.1. LDAP Syntaxes .............................................11
+ 5.2. Matching Rules ............................................11
+ 5.3. Attribute Types ...........................................12
+ 5.4. Object Classes ............................................12
+ 6. Other Extension Mechanisms .....................................12
+ 6.1. Attribute Description Options .............................12
+ 6.2. Authorization Identities ..................................12
+ 6.3. LDAP URL Extensions .......................................12
+ 7. Security Considerations ........................................12
+ 8. Acknowledgements ...............................................13
+ 9. References .....................................................13
+ 9.1. Normative References ......................................13
+ 9.2. Informative References ....................................15
+
+
+
+
+
+
+
+
+
+Zeilenga Best Current Practice [Page 2]
+
+RFC 4521 LDAP Extensions June 2006
+
+
+1. Introduction
+
+ The Lightweight Directory Access Protocol (LDAP) [RFC4510] is an
+ extensible protocol.
+
+ LDAP allows for new operations to be added and for existing
+ operations to be enhanced [RFC4511].
+
+ LDAP allows additional schema to be defined [RFC4512][RFC4517]. This
+ can include additional object classes, attribute types, matching
+ rules, additional syntaxes, and other elements of schema. LDAP
+ provides an ability to extend attribute types with options [RFC4512].
+
+ LDAP supports a Simple Authentication and Security Layer (SASL)
+ authentication method [RFC4511][RFC4513]. SASL [RFC4422] is
+ extensible. LDAP may be extended to support additional
+ authentication methods [RFC4511].
+
+ LDAP supports establishment of Transport Layer Security (TLS)
+ [RFC4511][RFC4513]. TLS [RFC4346] is extensible.
+
+ LDAP has an extensible Uniform Resource Locator (URL) format
+ [RFC4516].
+
+ Lastly, LDAP allows for certain extensions to the protocol's Abstract
+ Syntax Notation - One (ASN.1) [X.680] definition to be made. This
+ facilitates a wide range of protocol enhancements, for example, new
+ result codes needed to support extensions to be added through
+ extension of the protocol's ASN.1 definition.
+
+ This document describes practices that engineers should consider when
+ designing extensions to LDAP.
+
+1.1. Terminology
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in BCP 14 [RFC2119]. In
+ this document, "the specification", as used by BCP 14, RFC 2119,
+ refers to the engineering of LDAP extensions.
+
+ The term "Request Control" refers to a control attached to a client-
+ generated message sent to a server. The term "Response Control"
+ refers to a control attached to a server-generated message sent to a
+ client.
+
+
+
+
+
+
+Zeilenga Best Current Practice [Page 3]
+
+RFC 4521 LDAP Extensions June 2006
+
+
+ DIT stands for Directory Information Tree.
+ DSA stands for Directory System Agent, a server.
+ DSE stands for DSA-Specific Entry.
+ DUA stands for Directory User Agent, a client.
+ DN stands for Distinguished Name.
+
+2. General Considerations
+
+2.1. Scope of Extension
+
+ Mutually agreeing peers may, within the confines of an extension,
+ agree to significant changes in protocol semantics. However,
+ designers MUST consider the impact of an extension upon protocol
+ peers that have not agreed to implement or otherwise recognize and
+ support the extension. Extensions MUST be "truly optional"
+ [RFC2119].
+
+2.2. Interaction between extensions
+
+ Designers SHOULD consider how extensions they engineer interact with
+ other extensions.
+
+ Designers SHOULD consider the extensibility of extensions they
+ specify. Extensions to LDAP SHOULD themselves be extensible.
+
+ Except where it is stated otherwise, extensibility is implied.
+
+2.3. Discovery Mechanism
+
+ Extensions SHOULD provide adequate discovery mechanisms.
+
+ As LDAP design is based upon the client-request/server-response
+ paradigm, the general discovery approach is for the client to
+ discover the capabilities of the server before utilizing a particular
+ extension. Commonly, this discovery involves querying the root DSE
+ and/or other DSEs for operational information associated with the
+ extension. LDAP provides no mechanism for a server to discover the
+ capabilities of a client.
+
+ The 'supportedControl' attribute [RFC4512] is used to advertise
+ supported controls. The 'supportedExtension' attribute [RFC4512] is
+ used to advertise supported extended operations. The
+ 'supportedFeatures' attribute [RFC4512] is used to advertise
+ features. Other root DSE attributes MAY be defined to advertise
+ other capabilities.
+
+
+
+
+
+
+Zeilenga Best Current Practice [Page 4]
+
+RFC 4521 LDAP Extensions June 2006
+
+
+2.4. Internationalization Considerations
+
+ LDAP is designed to support the full Unicode [Unicode] repertory of
+ characters. Extensions SHOULD avoid unnecessarily restricting
+ applications to subsets of Unicode (e.g., Basic Multilingual Plane,
+ ISO 8859-1, ASCII, Printable String).
+
+ LDAP Language Tag options [RFC3866] provide a mechanism for tagging
+ text (and other) values with language information. Extensions that
+ define attribute types SHOULD allow use of language tags with these
+ attributes.
+
+2.5. Use of the Basic Encoding Rules
+
+ Numerous elements of LDAP are described using ASN.1 [X.680] and are
+ encoded using a particular subset [Protocol, Section 5.2] of the
+ Basic Encoding Rules (BER) [X.690]. To allow reuse of
+ parsers/generators used in implementing the LDAP "core" technical
+ specification [RFC4510], it is RECOMMENDED that extension elements
+ (e.g., extension specific contents of controlValue, requestValue,
+ responseValue fields) described by ASN.1 and encoded using BER be
+ subjected to the restrictions of [Protocol, Section 5.2].
+
+2.6. Use of Formal Languages
+
+ Formal languages SHOULD be used in specifications in accordance with
+ IESG guidelines [FORMAL].
+
+2.7. Examples
+
+ Example DN strings SHOULD conform to the syntax defined in [RFC4518].
+ Example LDAP filter strings SHOULD conform to the syntax defined in
+ [RFC4515]. Example LDAP URLs SHOULD conform to the syntax defined in
+ [RFC4516]. Entries SHOULD be represented using LDIF [RFC2849].
+
+2.8. Registration of Protocol Values
+
+ Designers SHALL register protocol values of their LDAP extensions in
+ accordance with BCP 64, RFC 4520 [RFC4520]. Specifications that
+ create new extensible protocol elements SHALL extend existing
+ registries or establish new registries for values of these elements
+ in accordance with BCP 64, RFC 4520 [RFC4520] and BCP 26, RFC 2434
+ [RFC2434].
+
+
+
+
+
+
+
+
+Zeilenga Best Current Practice [Page 5]
+
+RFC 4521 LDAP Extensions June 2006
+
+
+3. LDAP Operation Extensions
+
+ Extensions SHOULD use controls in defining extensions that complement
+ existing operations. Where the extension to be defined does not
+ complement an existing operation, designers SHOULD consider defining
+ an extended operation instead.
+
+ For example, a subtree delete operation could be designed as either
+ an extension of the delete operation or as a new operation. As the
+ feature complements the existing delete operation, use of the control
+ mechanism to extend the delete operation is likely more appropriate.
+
+ As a counter (and contrived) example, a locate services operation (an
+ operation that would return for a DN a set of LDAP URLs to services
+ that may hold the entry named by this DN) could be designed as either
+ a search operation or a new operation. As the feature doesn't
+ complement the search operation (e.g., the operation is not contrived
+ to search for entries held in the Directory Information Tree), it is
+ likely more appropriate to define a new operation using the extended
+ operation mechanism.
+
+3.1. Controls
+
+ Controls [Protocol, Section 4.1.11] are the RECOMMENDED mechanism for
+ extending existing operations. The existing operation can be a base
+ operation defined in [RFC4511] (e.g., search, modify) , an extended
+ operation (e.g., Start TLS [RFC4511], Password Modify [RFC3062]), or
+ an operation defined as an extension to a base or extended operation.
+
+ Extensions SHOULD NOT return Response controls unless the server has
+ specific knowledge that the client can make use of the control.
+ Generally, the client requests the return of a particular response
+ control by providing a related request control.
+
+ An existing operation MAY be extended to return IntermediateResponse
+ messages [Protocol, Section 4.13].
+
+ Specifications of controls SHALL NOT attach additional semantics to
+ the criticality of controls beyond those defined in [Protocol,
+ Section 4.1.11]. A specification MAY mandate the criticality take on
+ a particular value (e.g., TRUE or FALSE), where appropriate.
+
+3.1.1. Extending Bind Operation with Controls
+
+ Controls attached to the request and response messages of a Bind
+ Operation [RFC4511] are not protected by any security layers
+ established by that Bind operation.
+
+
+
+
+Zeilenga Best Current Practice [Page 6]
+
+RFC 4521 LDAP Extensions June 2006
+
+
+ Specifications detailing controls extending the Bind operation SHALL
+ detail that the Bind negotiated security layers do not protect the
+ information contained in these controls and SHALL detail how the
+ information in these controls is protected or why the information
+ does not need protection.
+
+ It is RECOMMENDED that designers consider alternative mechanisms for
+ providing the function. For example, an extended operation issued
+ subsequent to the Bind operation (hence, protected by the security
+ layers negotiated by the Bind operation) might be used to provide the
+ desired function.
+
+ Additionally, designers of Bind control extensions MUST also consider
+ how the controls' semantics interact with individual steps of a
+ multi-step Bind operation. Note that some steps are optional and
+ thus may require special attention in the design.
+
+3.1.2. Extending the Start TLS Operation with Controls
+
+ Controls attached to the request and response messages of a Start TLS
+ Operation [RFC4511] are not protected by the security layers
+ established by the Start TLS operation.
+
+ Specifications detailing controls extending the Start TLS operation
+ SHALL detail that the Start TLS negotiated security layers do not
+ protect the information contained in these controls and SHALL detail
+ how the information in these controls is protected or why the
+ information does not need protection.
+
+ It is RECOMMENDED that designers consider alternative mechanisms for
+ providing the function. For example, an extended operation issued
+ subsequent to the Start TLS operation (hence, protected by the
+ security layers negotiated by the Start TLS operation) might be used
+ to provided the desired function.
+
+3.1.3. Extending the Search Operation with Controls
+
+ The Search operation processing has two distinct phases:
+
+ - finding the base object; and
+
+ - searching for objects at or under that base object.
+
+ Specifications of controls extending the Search Operation should
+ clearly state in which phase(s) the control's semantics apply.
+ Semantics of controls that are not specific to the Search Operation
+ SHOULD apply in the finding phase.
+
+
+
+
+Zeilenga Best Current Practice [Page 7]
+
+RFC 4521 LDAP Extensions June 2006
+
+
+3.1.4. Extending the Update Operations with Controls
+
+ Update operations have properties of atomicity, consistency,
+ isolation, and durability ([ACID]).
+
+ - atomicity: All or none of the DIT changes requested are made.
+
+ - consistency: The resulting DIT state must be conform to schema
+ and other constraints.
+
+ - isolation: Intermediate states are not exposed.
+
+ - durability: The resulting DIT state is preserved until
+ subsequently updated.
+
+ When defining a control that requests additional (or other) DIT
+ changes be made to the DIT, these additional changes SHOULD NOT be
+ treated as part of a separate transaction. The specification MUST be
+ clear as to whether the additional DIT changes are part of the same
+ or a separate transaction as the DIT changes expressed in the request
+ of the base operation.
+
+ When defining a control that requests additional (or other) DIT
+ changes be made to the DIT, the specification MUST be clear as to the
+ order in which these and the base changes are to be applied to the
+ DIT.
+
+3.1.5. Extending the Responseless Operations with Controls
+
+ The Abandon and Unbind operations do not include a response message.
+ For this reason, specifications for controls designed to be attached
+ to Abandon and Unbind requests SHOULD mandate that the control's
+ criticality be FALSE.
+
+3.2. Extended Operations
+
+ Extended Operations [Protocol, Section 4.12] are the RECOMMENDED
+ mechanism for defining new operations. An extended operation
+ consists of an ExtendedRequest message, zero or more
+ IntermediateResponse messages, and an ExtendedResponse message.
+
+3.3. Intermediate Responses
+
+ Extensions SHALL use IntermediateResponse messages instead of
+ ExtendedResponse messages to return intermediate results.
+
+
+
+
+
+
+Zeilenga Best Current Practice [Page 8]
+
+RFC 4521 LDAP Extensions June 2006
+
+
+3.4. Unsolicited Notifications
+
+ Unsolicited notifications [Protocol, Section 4.4] offer a capability
+ for the server to notify the client of events not associated with the
+ operation currently being processed.
+
+ Extensions SHOULD be designed such that unsolicited notifications are
+ not returned unless the server has specific knowledge that the client
+ can make use of the notification. Generally, the client requests the
+ return of a particular unsolicited notification by performing a
+ related extended operation.
+
+ For example, a time hack extension could be designed to return
+ unsolicited notifications at regular intervals that were enabled by
+ an extended operation (which possibly specified the desired
+ interval).
+
+4. Extending the LDAP ASN.1 Definition
+
+ LDAP allows limited extension [Protocol, Section 4] of the LDAP ASN.1
+ definition [Protocol, Appendix B] to be made.
+
+4.1. Result Codes
+
+ Extensions that specify new operations or enhance existing operations
+ often need to define new result codes. The extension SHOULD be
+ designed such that a client has a reasonably clear indication of the
+ nature of the successful or non-successful result.
+
+ Extensions SHOULD use existing result codes to indicate conditions
+ that are consistent with the intended meaning [RFC4511][X.511] of
+ these codes. Extensions MAY introduce new result codes [RFC4520]
+ where no existing result code provides an adequate indication of the
+ nature of the result.
+
+ Extensions SHALL NOT disallow or otherwise restrict the return of
+ general service result codes, especially those reporting a protocol,
+ service, or security problem, or indicating that the server is unable
+ or unwilling to complete the operation.
+
+4.2. LDAP Message Types
+
+ While extensions can specify new types of LDAP messages by extending
+ the protocolOp CHOICE of the LDAPMessage SEQUENCE, this is generally
+ unnecessary and inappropriate. Existing operation extension
+ mechanisms (e.g., extended operations, unsolicited notifications, and
+ intermediate responses) SHOULD be used instead. However, there may
+ be cases where an extension does not fit well into these mechanisms.
+
+
+
+Zeilenga Best Current Practice [Page 9]
+
+RFC 4521 LDAP Extensions June 2006
+
+
+ In such cases, a new extension mechanism SHOULD be defined that can
+ be used by multiple extensions that have similar needs.
+
+4.3. Authentication Methods
+
+ The Bind operation currently supports two authentication methods,
+ simple and SASL. SASL [RFC4422] is an extensible authentication
+ framework used by multiple application-level protocols (e.g., BEEP,
+ IMAP, SMTP). It is RECOMMENDED that new authentication processes be
+ defined as SASL mechanisms. New LDAP authentication methods MAY be
+ added to support new authentication frameworks.
+
+ The Bind operation's primary function is to establish the LDAP
+ association [RFC4513]. No other operation SHALL be defined (or
+ extended) to establish the LDAP association. However, other
+ operations MAY be defined to establish other security associations
+ (e.g., IPsec).
+
+4.4. General ASN.1 Extensibility
+
+ Section 4 of [RFC4511] states the following:
+
+ In order to support future extensions to this protocol,
+ extensibility is implied where it is allowed per ASN.1 (i.e.,
+ sequence, set, choice, and enumerated types are extensible). In
+ addition, ellipses (...) have been supplied in ASN.1 types that
+ are explicitly extensible as discussed in [RFC4520]. Because of
+ the implied extensibility, clients and servers MUST (unless
+ otherwise specified) ignore trailing SEQUENCE components whose
+ tags they do not recognize.
+
+ Designers SHOULD avoid introducing extensions that rely on
+ unsuspecting implementations to ignore trailing components of
+ SEQUENCE whose tags they do not recognize.
+
+5. Schema Extensions
+
+ Extensions defining LDAP schema elements SHALL provide schema
+ definitions conforming with syntaxes defined in [Models, Section
+ 4.1]. While provided definitions MAY be reformatted (line wrapped)
+ for readability, this SHALL be noted in the specification.
+
+ For definitions that allow a NAME field, new schema elements SHOULD
+ provide one and only one name. The name SHOULD be short.
+
+ Each schema definition allows a DESC field. The DESC field, if
+ provided, SHOULD contain a short descriptive phrase. The DESC field
+ MUST be regarded as informational. That is, the specification MUST
+
+
+
+Zeilenga Best Current Practice [Page 10]
+
+RFC 4521 LDAP Extensions June 2006
+
+
+ be written such that its interpretation is the same with and without
+ the provided DESC fields.
+
+ The extension SHALL NOT mandate that implementations provide the same
+ DESC field in the schema they publish. Implementors MAY replace or
+ remove the DESC field.
+
+ Published schema elements SHALL NOT be redefined. Replacement schema
+ elements (new OIDs, new NAMEs) SHOULD be defined as needed.
+
+ Schema designers SHOULD reuse existing schema elements, where
+ appropriate. However, any reuse MUST not alter the semantics of the
+ element.
+
+5.1. LDAP Syntaxes
+
+ Each LDAP syntax [RFC4517] is defined in terms of ASN.1 [X.680].
+ Each extension detailing an LDAP syntax MUST specify the ASN.1 data
+ definition associated with the syntax. A distinct LDAP syntax SHOULD
+ be created for each distinct ASN.1 data definition (including
+ constraints).
+
+ Each LDAP syntax SHOULD have a string encoding defined for it. It is
+ RECOMMENDED that this string encoding be restricted to UTF-8
+ [RFC3629] encoded Unicode [Unicode] characters. Use of Generic
+ String Encoding Rules (GSER) [RFC3641][RFC3642] or other generic
+ string encoding rules to provide string encodings for complex ASN.1
+ data definitions is RECOMMENDED. Otherwise, it is RECOMMENDED that
+ the string encoding be described using a formal language (e.g., ABNF
+ [RFC4234]). Formal languages SHOULD be used in specifications in
+ accordance with IESG guidelines [FORMAL].
+
+ If no string encoding is defined, the extension SHALL specify how the
+ transfer encoding is to be indicated. Generally, the extension
+ SHOULD mandate use of binary or other transfer encoding option.
+
+5.2. Matching Rules
+
+ Three basic kinds of matching rules (e.g., EQUALITY, ORDERING, and
+ SUBSTRING) may be associated with an attribute type. In addition,
+ LDAP provides an extensible matching rule mechanism.
+
+ The matching rule specification SHOULD detail which kind of matching
+ rule it is and SHOULD describe which kinds of values it can be used
+ with.
+
+ In addition to requirements stated in the LDAP technical
+ specification, equality matching rules SHOULD be commutative.
+
+
+
+Zeilenga Best Current Practice [Page 11]
+
+RFC 4521 LDAP Extensions June 2006
+
+
+5.3. Attribute Types
+
+ Designers SHOULD carefully consider how the structure of values is to
+ be restricted. Designers SHOULD consider that servers will only
+ enforce constraints of the attribute's syntax. That is, an attribute
+ intended to hold URIs, but that has directoryString syntax, is not
+ restricted to values that are URIs.
+
+ Designers SHOULD carefully consider which matching rules, if any, are
+ appropriate for the attribute type. Matching rules specified for an
+ attribute type MUST be compatible with the attribute type's syntax.
+
+ Extensions specifying operational attributes MUST detail how servers
+ are to maintain and/or utilize values of each operational attribute.
+
+5.4. Object Classes
+
+ Designers SHOULD carefully consider whether each attribute of an
+ object class is required ("MUST") or allowed ("MAY").
+
+ Extensions specifying object classes that allow (or require)
+ operational attributes MUST specify how servers are to maintain
+ and/or utilize entries belonging to these object classes.
+
+6. Other Extension Mechanisms
+
+6.1. Attribute Description Options
+
+ Each option is identified by a string of letters, numbers, and
+ hyphens. This string SHOULD be short.
+
+6.2. Authorization Identities
+
+ Extensions interacting with authorization identities SHALL support
+ the LDAP authzId format [RFC4513]. The authzId format is extensible.
+
+6.3. LDAP URL Extensions
+
+ LDAP URL extensions are identified by a short string, a descriptor.
+ Like other descriptors, the string SHOULD be short.
+
+7. Security Considerations
+
+ LDAP does not place undue restrictions on the kinds of extensions
+ that can be implemented. While this document attempts to outline
+ some specific issues that designers need to consider, it is not (and
+
+
+
+
+
+Zeilenga Best Current Practice [Page 12]
+
+RFC 4521 LDAP Extensions June 2006
+
+
+ cannot be) all encompassing. Designers MUST do their own evaluations
+ of the security considerations applicable to their extensions.
+
+ Designers MUST NOT assume that the LDAP "core" technical
+ specification [RFC4510] adequately addresses the specific concerns
+ surrounding their extensions or assume that their extensions have no
+ specific concerns.
+
+ Extension specifications, however, SHOULD note whether security
+ considerations specific to the feature they are extending, as well as
+ general LDAP security considerations, apply to the extension.
+
+8. Acknowledgements
+
+ The author thanks the IETF LDAP community for their thoughtful
+ comments.
+
+ This work builds upon "LDAP Extension Style Guide" [GUIDE] by Bruce
+ Greenblatt.
+
+9. References
+
+9.1. Normative References
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [RFC2434] Narten, T. and H. Alvestrand, "Guidelines for Writing an
+ IANA Considerations Section in RFCs", BCP 26, RFC 2434,
+ October 1998.
+
+ [RFC2849] Good, G., "The LDAP Data Interchange Format (LDIF) -
+ Technical Specification", RFC 2849, June 2000.
+
+ [RFC3629] Yergeau, F., "UTF-8, a transformation format of ISO
+ 10646", STD 63, RFC 3629, November 2003.
+
+ [RFC3641] Legg, S., "Generic String Encoding Rules (GSER) for ASN.1
+ Types", RFC 3641, October 2003.
+
+ [RFC3642] Legg, S., "Common Elements of Generic String Encoding
+ Rules (GSER) Encodings", RFC 3642, October 2003.
+
+ [RFC4512] Zeilenga, K., "Lightweight Directory Access Protocol
+ (LDAP): Directory Information Models", RFC 4512, June
+ 2006.
+
+
+
+
+
+Zeilenga Best Current Practice [Page 13]
+
+RFC 4521 LDAP Extensions June 2006
+
+
+ [RFC3866] Zeilenga, K., Ed., "Language Tags and Ranges in the
+ Lightweight Directory Access Protocol (LDAP)", RFC 3866,
+ July 2004.
+
+ [RFC4234] Crocker, D. and P. Overell, "Augmented BNF for Syntax
+ Specifications: ABNF", RFC 4234, October 2005.
+
+ [RFC4510] Zeilenga, K., Ed., "Lightweight Directory Access Protocol
+ (LDAP): Technical Specification Road Map", RFC 4510, June
+ 2006.
+
+ [RFC4511] Sermersheim, J., Ed., "Lightweight Directory Access
+ Protocol (LDAP): The Protocol", RFC 4511, June 2006.
+
+ [RFC4512] Zeilenga, K., "Lightweight Directory Access Protocol
+ (LDAP): Directory Information Models", RFC 4512, June
+ 2006.
+
+ [RFC4513] Harrison, R., Ed., "Lightweight Directory Access Protocol
+ (LDAP): Authentication Methods and Security Mechanisms",
+ RFC 4513, June 2006.
+
+ [RFC4515] Smith, M., Ed. and T. Howes, "Lightweight Directory Access
+ Protocol (LDAP): String Representation of Search Filters",
+ RFC 4515, June 2006.
+
+ [RFC4516] Smith, M., Ed. and T. Howes, "Lightweight Directory Access
+ Protocol (LDAP): Uniform Resource Locator", RFC 4516, June
+ 2006.
+
+ [RFC4517] Legg, S., Ed., "Lightweight Directory Access Protocol
+ (LDAP): Syntaxes and Matching Rules", RFC 4517, June 2006.
+
+ [RFC4518] Zeilenga, K., "Lightweight Directory Access Protocol
+ (LDAP): String Representation of Distinguished Names", RFC
+ 4518, June 2006.
+
+ [RFC4520] Zeilenga, K., "Internet Assigned Numbers Authority (IANA)
+ Considerations for the Lightweight Directory Access
+ Protocol (LDAP)", BCP 64, RFC 4520, June 2006.
+
+ [RFC4422] Melnikov, A., Ed. and K. Zeilenga, Ed., "Simple
+ Authentication and Security Layer (SASL)", RFC 4422, June
+ 2006.
+
+
+
+
+
+
+
+Zeilenga Best Current Practice [Page 14]
+
+RFC 4521 LDAP Extensions June 2006
+
+
+ [Unicode] The Unicode Consortium, "The Unicode Standard, Version
+ 3.2.0" is defined by "The Unicode Standard, Version 3.0"
+ (Reading, MA, Addison-Wesley, 2000. ISBN 0-201-61633-5),
+ as amended by the "Unicode Standard Annex #27: Unicode
+ 3.1" (http://www.unicode.org/reports/tr27/) and by the
+ "Unicode Standard Annex #28: Unicode 3.2"
+ (http://www.unicode.org/reports/tr28/).
+
+ [FORMAL] IESG, "Guidelines for the use of formal languages in IETF
+ specifications",
+ <http://www.ietf.org/IESG/STATEMENTS/pseudo-code-in-
+ specs.txt>, 2001.
+
+ [X.511] International Telecommunication Union - Telecommunication
+ Standardization Sector, "The Directory: Abstract Service
+ Definition", X.511(1993) (also ISO/IEC 9594-3:1993).
+
+ [X.680] International Telecommunication Union - Telecommunication
+ Standardization Sector, "Abstract Syntax Notation One
+ (ASN.1) - Specification of Basic Notation", X.680(2002)
+ (also ISO/IEC 8824-1:2002).
+
+ [X.690] International Telecommunication Union - Telecommunication
+ Standardization Sector, "Specification of ASN.1 encoding
+ rules: Basic Encoding Rules (BER), Canonical Encoding
+ Rules (CER), and Distinguished Encoding Rules (DER)",
+ X.690(2002) (also ISO/IEC 8825-1:2002).
+
+9.2. Informative References
+
+ [ACID] Section 4 of ISO/IEC 10026-1:1992.
+
+ [GUIDE] Greenblatt, B., "LDAP Extension Style Guide", Work in
+ Progress.
+
+ [RFC3062] Zeilenga, K., "LDAP Password Modify Extended Operation",
+ RFC 3062, February 2001.
+
+ [RFC4346] Dierks, T. and E. Rescorla, "The Transport Layer Security
+ (TLS) Protocol Version 1.1", RFC 4346, April 2006.
+
+Author's Address
+
+ Kurt D. Zeilenga
+ OpenLDAP Foundation
+
+ EMail: Kurt@OpenLDAP.org
+
+
+
+
+Zeilenga Best Current Practice [Page 15]
+
+RFC 4521 LDAP Extensions June 2006
+
+
+Full Copyright Statement
+
+ Copyright (C) The Internet Society (2006).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
+ ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
+ INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
+ INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be
+ found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at
+ ietf-ipr@ietf.org.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is provided by the IETF
+ Administrative Support Activity (IASA).
+
+
+
+
+
+
+
+Zeilenga Best Current Practice [Page 16]
+
diff --git a/source4/ldap_server/devdocs/rfc4522.txt b/source4/ldap_server/devdocs/rfc4522.txt
new file mode 100644
index 0000000000..11156a07f1
--- /dev/null
+++ b/source4/ldap_server/devdocs/rfc4522.txt
@@ -0,0 +1,451 @@
+
+
+
+
+
+
+Network Working Group S. Legg
+Request for Comments: 4522 eB2Bcom
+Category: Standards Track June 2006
+
+
+ Lightweight Directory Access Protocol (LDAP):
+ The Binary Encoding Option
+
+Status of This Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2006).
+
+Abstract
+
+ Each attribute stored in a Lightweight Directory Access Protocol
+ (LDAP) directory has a defined syntax (i.e., data type). A syntax
+ definition specifies how attribute values conforming to the syntax
+ are normally represented when transferred in LDAP operations. This
+ representation is referred to as the LDAP-specific encoding to
+ distinguish it from other methods of encoding attribute values. This
+ document defines an attribute option, the binary option, that can be
+ used to specify that the associated attribute values are instead
+ encoded according to the Basic Encoding Rules (BER) used by X.500
+ directories.
+
+Table of Contents
+
+ 1. Introduction ....................................................2
+ 2. Conventions .....................................................2
+ 3. The Binary Option ...............................................2
+ 4. Syntaxes Requiring Binary Transfer ..............................3
+ 5. Attributes Returned in a Search .................................4
+ 6. All User Attributes .............................................4
+ 7. Conflicting Requests ............................................5
+ 8. Security Considerations .........................................5
+ 9. IANA Considerations .............................................5
+ 10. References .....................................................5
+ 10.1. Normative References ......................................5
+ 10.2. Informative References ....................................6
+
+
+
+
+Legg Standards Track [Page 1]
+
+RFC 4522 LDAP: The Binary Encoding Option June 2006
+
+
+1. Introduction
+
+ Each attribute stored in a Lightweight Directory Access Protocol
+ (LDAP) directory [RFC4510] has a defined syntax (i.e., data type)
+ which constrains the structure and format of its values.
+
+ The description of each syntax [RFC4517] specifies how attribute or
+ assertion values [RFC4512] conforming to the syntax are normally
+ represented when transferred in LDAP operations [RFC4511]. This
+ representation is referred to as the LDAP-specific encoding to
+ distinguish it from other methods of encoding attribute values.
+
+ This document defines an attribute option, the binary option, which
+ can be used in an attribute description [RFC4512] in an LDAP
+ operation to specify that the associated attribute values or
+ assertion values are, or are requested to be, encoded according to
+ the Basic Encoding Rules (BER) [BER] as used by X.500 [X.500]
+ directories, instead of the usual LDAP-specific encoding.
+
+ The binary option was originally defined in RFC 2251 [RFC2251]. The
+ LDAP technical specification [RFC4510] has obsoleted the previously
+ defined LDAP technical specification [RFC3377], which included RFC
+ 2251. The binary option was not included in the revised LDAP
+ technical specification for a variety of reasons including
+ implementation inconsistencies. No attempt is made here to resolve
+ the known inconsistencies.
+
+ This document reintroduces the binary option for use with certain
+ attribute syntaxes, such as certificate syntax [RFC4523], that
+ specifically require it. No attempt has been made to address use of
+ the binary option with attributes of syntaxes that do not require its
+ use. Unless addressed in a future specification, this use is to be
+ avoided.
+
+2. Conventions
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in BCP 14, RFC 2119
+ [BCP14].
+
+3. The Binary Option
+
+ The binary option is indicated with the attribute option string
+ "binary" in an attribute description. Note that, like all attribute
+ options, the string representing the binary option is case
+ insensitive.
+
+
+
+
+Legg Standards Track [Page 2]
+
+RFC 4522 LDAP: The Binary Encoding Option June 2006
+
+
+ Where the binary option is present in an attribute description, the
+ associated attribute values or assertion values MUST be BER encoded
+ (otherwise the values are encoded according to the LDAP-specific
+ encoding [RFC4517] for the attribute's syntax). Note that it is
+ possible for a syntax to be defined such that its LDAP-specific
+ encoding is exactly the same as its BER encoding.
+
+ In terms of the protocol [RFC4511], the binary option specifies that
+ the contents octets of the associated AttributeValue or
+ AssertionValue OCTET STRING are a complete BER encoding of the
+ relevant value.
+
+ The binary option is not a tagging option [RFC4512], so the presence
+ of the binary option does not specify an attribute subtype. An
+ attribute description containing the binary option references exactly
+ the same attribute as the attribute description without the binary
+ option. The supertype/subtype relationships of attributes with
+ tagging options are not altered in any way by the presence or absence
+ of the binary option.
+
+ An attribute description SHALL be treated as unrecognized if it
+ contains the binary option and the syntax of the attribute does not
+ have an associated ASN.1 type [RFC4517], or the BER encoding of
+ values of that type is not supported.
+
+ The presence or absence of the binary option only affects the
+ transfer of attribute and assertion values in the protocol; servers
+ store any particular attribute value in a format of their choosing.
+
+4. Syntaxes Requiring Binary Transfer
+
+ The attribute values of certain attribute syntaxes are defined
+ without an LDAP-specific encoding and are required to be transferred
+ in the BER-encoded form. For the purposes of this document, these
+ syntaxes are said to have a binary transfer requirement. The
+ certificate, certificate list, certificate pair, and supported
+ algorithm syntaxes [RFC4523] are examples of syntaxes with a binary
+ transfer requirement. These syntaxes also have an additional
+ requirement that the exact BER encoding must be preserved. Note that
+ this is a property of the syntaxes themselves, and not a property of
+ the binary option. In the absence of this requirement, LDAP clients
+ would need to re-encode values using the Distinguished Encoding Rules
+ (DER).
+
+
+
+
+
+
+
+
+Legg Standards Track [Page 3]
+
+RFC 4522 LDAP: The Binary Encoding Option June 2006
+
+
+5. Attributes Returned in a Search
+
+ An LDAP search request [RFC4511] contains a list of the attributes
+ (the requested attributes list) to be returned from each entry
+ matching the search filter. An attribute description in the
+ requested attributes list also implicitly requests all subtypes of
+ the attribute type in the attribute description, whether through
+ attribute subtyping or attribute tagging option subtyping [RFC4512].
+
+ The requested attributes list MAY contain attribute descriptions with
+ the binary option, but MUST NOT contain two attribute descriptions
+ with the same attribute type and the same tagging options (even if
+ only one of them has the binary option). The binary option in an
+ attribute description in the requested attributes list implicitly
+ applies to all the subtypes of the attribute type in the attribute
+ description (however, see Section 7).
+
+ Attributes of a syntax with the binary transfer requirement, if
+ returned, SHALL be returned in the binary form (i.e., with the binary
+ option in the attribute description and the associated attribute
+ values BER encoded) regardless of whether the binary option was
+ present in the request (for the attribute or for one of its
+ supertypes).
+
+ Attributes of a syntax without the binary transfer requirement, if
+ returned, SHOULD be returned in the form explicitly requested. That
+ is, if the attribute description in the requested attributes list
+ contains the binary option, then the corresponding attribute in the
+ result SHOULD be in the binary form. If the attribute description in
+ the request does not contain the binary option, then the
+ corresponding attribute in the result SHOULD NOT be in the binary
+ form. A server MAY omit an attribute from the result if it does not
+ support the requested encoding.
+
+ Regardless of the encoding chosen, a particular attribute value is
+ returned at most once.
+
+6. All User Attributes
+
+ If the list of attributes in a search request is empty or contains
+ the special attribute description string "*", then all user
+ attributes are requested to be returned.
+
+ Attributes of a syntax with the binary transfer requirement, if
+ returned, SHALL be returned in the binary form.
+
+
+
+
+
+
+Legg Standards Track [Page 4]
+
+RFC 4522 LDAP: The Binary Encoding Option June 2006
+
+
+ Attributes of a syntax without the binary transfer requirement and
+ having a defined LDAP-specific encoding SHOULD NOT be returned in the
+ binary form.
+
+ Attributes of a syntax without the binary transfer requirement and
+ without a defined LDAP-specific encoding may be returned in the
+ binary form or omitted from the result.
+
+7. Conflicting Requests
+
+ A particular attribute could be explicitly requested by an attribute
+ description and/or implicitly requested by the attribute descriptions
+ of one or more of its supertypes, or by the special attribute
+ description string "*". If the binary option is present in at least
+ one, but not all, of these attribute descriptions then the effect of
+ the request with respect to binary transfer is implementation
+ defined.
+
+8. Security Considerations
+
+ When interpreting security-sensitive fields, and in particular fields
+ used to grant or deny access, implementations MUST ensure that any
+ matching rule comparisons are done on the underlying abstract value,
+ regardless of the particular encoding used.
+
+9. IANA Considerations
+
+ The Internet Assigned Numbers Authority (IANA) has updated the LDAP
+ attribute description option registry [BCP64] as indicated by the
+ following template:
+
+ Subject:
+ Request for LDAP Attribute Description Option Registration
+ Option Name: binary
+ Family of Options: NO
+ Person & email address to contact for further information:
+ Steven Legg <steven.legg@eb2bcom.com>
+ Specification: RFC 4522
+ Author/Change Controller: IESG
+
+10. References
+
+10.1. Normative References
+
+ [BCP14] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+
+
+
+
+Legg Standards Track [Page 5]
+
+RFC 4522 LDAP: The Binary Encoding Option June 2006
+
+
+ [BCP64] Zeilenga, K., "Internet Assigned Numbers Authority (IANA)
+ Considerations for the Lightweight Directory Access
+ Protocol (LDAP)", BCP 64, RFC 4520, June 2006.
+
+ [RFC4510] Zeilenga, K., Ed., "Lightweight Directory Access Protocol
+ (LDAP): Technical Specification Road Map", RFC RFC 4510,
+ June 2006.
+
+ [RFC4511] Sermersheim, J., "Lightweight Directory Access Protocol
+ (LDAP): The Protocol", RFC 4511, June 2006.
+
+ [RFC4512] Zeilenga, K., "Lightweight Directory Access Protocol
+ (LDAP): Directory Information Models", RFC 4512, June
+ 2006.
+
+ [RFC4517] Legg, S., Ed., "Lightweight Directory Access Protocol
+ (LDAP): Syntaxes and Matching Rules", RFC 4517, June
+ 2006.
+
+ [RFC4523] Zeilenga, K., "Lightweight Directory Access Protocol
+ (LDAP) Schema Definitions for X.509 Certificates", RFC
+ 4523, June 2006.
+
+ [BER] ITU-T Recommendation X.690 (07/02) | ISO/IEC 8825-1,
+ Information Technology - ASN.1 encoding rules:
+ Specification of Basic Encoding Rules (BER), Canonical
+ Encoding Rules (CER) and Distinguished Encoding Rules
+ (DER).
+
+10.2. Informative References
+
+ [RFC2251] Wahl, M., Howes, T., and S. Kille, "Lightweight Directory
+ Access Protocol (v3)", RFC 2251, December 1997.
+
+ [RFC3377] Hodges, J. and R. Morgan, "Lightweight Directory Access
+ Protocol (v3): Technical Specification", RFC 3377,
+ September 2002.
+
+ [X.500] ITU-T Recommendation X.500 (02/01) | ISO/IEC 9594-1:2001,
+ Information technology - Open Systems Interconnection -
+ The Directory: Overview of concepts, models and services
+
+
+
+
+
+
+
+
+
+
+Legg Standards Track [Page 6]
+
+RFC 4522 LDAP: The Binary Encoding Option June 2006
+
+
+Author's Address
+
+ Dr. Steven Legg
+ eB2Bcom
+ Suite 3, Woodhouse Corporate Centre
+ 935 Station Street
+ Box Hill North, Victoria 3129
+ AUSTRALIA
+
+ Phone: +61 3 9896 7830
+ Fax: +61 3 9896 7801
+ EMail: steven.legg@eb2bcom.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Legg Standards Track [Page 7]
+
+RFC 4522 LDAP: The Binary Encoding Option June 2006
+
+
+Full Copyright Statement
+
+ Copyright (C) The Internet Society (2006).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
+ ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
+ INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
+ INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be
+ found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at
+ ietf-ipr@ietf.org.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is provided by the IETF
+ Administrative Support Activity (IASA).
+
+
+
+
+
+
+
+Legg Standards Track [Page 8]
+
diff --git a/source4/ldap_server/devdocs/rfc4523.txt b/source4/ldap_server/devdocs/rfc4523.txt
new file mode 100644
index 0000000000..d2589811c7
--- /dev/null
+++ b/source4/ldap_server/devdocs/rfc4523.txt
@@ -0,0 +1,1347 @@
+
+
+
+
+
+
+Network Working Group K. Zeilenga
+Request for Comments: 4523 OpenLDAP Foundation
+Obsoletes: 2252, 2256, 2587 June 2006
+Category: Standards Track
+
+
+ Lightweight Directory Access Protocol (LDAP)
+ Schema Definitions for X.509 Certificates
+
+Status of This Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2006).
+
+ Abstract
+
+ This document describes schema for representing X.509 certificates,
+ X.521 security information, and related elements in directories
+ accessible using the Lightweight Directory Access Protocol (LDAP).
+ The LDAP definitions for these X.509 and X.521 schema elements
+ replace those provided in RFCs 2252 and 2256.
+
+1. Introduction
+
+ This document provides LDAP [RFC4510] schema definitions [RFC4512]
+ for a subset of elements specified in X.509 [X.509] and X.521
+ [X.521], including attribute types for certificates, cross
+ certificate pairs, and certificate revocation lists; matching rules
+ to be used with these attribute types; and related object classes.
+ LDAP syntax definitions are also provided for associated assertion
+ and attribute values.
+
+ As the semantics of these elements are as defined in X.509 and X.521,
+ knowledge of X.509 and X.521 is necessary to make use of the LDAP
+ schema definitions provided herein.
+
+ This document, together with [RFC4510], obsoletes RFCs 2252 and 2256
+ in their entirety. The changes (in this document) made since RFC
+ 2252 and RFC 2256 include:
+
+ - addition of pkiUser, pkiCA, and deltaCRL classes;
+
+
+
+Zeilenga Standards Track [Page 1]
+
+RFC 4523 LDAP X.509 Schema June 2006
+
+
+ - update of attribute types to include equality matching rules in
+ accordance with their X.500 specifications;
+
+ - addition of certificate, certificate pair, certificate list,
+ and algorithm identifier matching rules; and
+
+ - addition of LDAP syntax for assertion syntaxes for these
+ matching rules.
+
+ This document obsoletes RFC 2587. The X.509 schema descriptions for
+ LDAPv2 [RFC1777] are Historic, as is LDAPv2 [RFC3494].
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in BCP 14 [RFC2119].
+
+ Schema definitions are provided using LDAP description formats
+ [RFC4512]. Definitions provided here are formatted (line wrapped)
+ for readability.
+
+2. Syntaxes
+
+ This section describes various syntaxes used in LDAP to transfer
+ certificates and related data types.
+
+2.1. Certificate
+
+ ( 1.3.6.1.4.1.1466.115.121.1.8 DESC 'X.509 Certificate' )
+
+ A value of this syntax is an X.509 Certificate [X.509, clause 7].
+
+ Due to changes made to the definition of a Certificate through time,
+ no LDAP-specific encoding is defined for this syntax. Values of this
+ syntax SHOULD be encoded using Distinguished Encoding Rules (DER)
+ [X.690] and MUST only be transferred using the ;binary transfer
+ option [RFC4522]; that is, by requesting and returning values using
+ attribute descriptions such as "userCertificate;binary".
+
+ As values of this syntax contain digitally signed data, values of
+ this syntax and the form of each value MUST be preserved as
+ presented.
+
+2.2. CertificateList
+
+ ( 1.3.6.1.4.1.1466.115.121.1.9 DESC 'X.509 Certificate List' )
+
+ A value of this syntax is an X.509 CertificateList [X.509, clause
+ 7.3].
+
+
+
+Zeilenga Standards Track [Page 2]
+
+RFC 4523 LDAP X.509 Schema June 2006
+
+
+ Due to changes made to the definition of a CertificateList through
+ time, no LDAP-specific encoding is defined for this syntax. Values
+ of this syntax SHOULD be encoded using DER [X.690] and MUST only be
+ transferred using the ;binary transfer option [RFC4522]; that is, by
+ requesting and returning values using attribute descriptions such as
+ "certificateRevocationList;binary".
+
+ As values of this syntax contain digitally signed data, values of
+ this syntax and the form of each value MUST be preserved as
+ presented.
+
+2.3. CertificatePair
+
+ ( 1.3.6.1.4.1.1466.115.121.1.10 DESC 'X.509 Certificate Pair' )
+
+ A value of this syntax is an X.509 CertificatePair [X.509, clause
+ 11.2.3].
+
+ Due to changes made to the definition of an X.509 CertificatePair
+ through time, no LDAP-specific encoding is defined for this syntax.
+ Values of this syntax SHOULD be encoded using DER [X.690] and MUST
+ only be transferred using the ;binary transfer option [RFC4522]; that
+ is, by requesting and returning values using attribute descriptions
+ such as "crossCertificatePair;binary".
+
+ As values of this syntax contain digitally signed data, values of
+ this syntax and the form of each value MUST be preserved as
+ presented.
+
+2.4. SupportedAlgorithm
+
+ ( 1.3.6.1.4.1.1466.115.121.1.49
+ DESC 'X.509 Supported Algorithm' )
+
+ A value of this syntax is an X.509 SupportedAlgorithm [X.509, clause
+ 11.2.7].
+
+ Due to changes made to the definition of an X.509 SupportedAlgorithm
+ through time, no LDAP-specific encoding is defined for this syntax.
+ Values of this syntax SHOULD be encoded using DER [X.690] and MUST
+ only be transferred using the ;binary transfer option [RFC4522]; that
+ is, by requesting and returning values using attribute descriptions
+ such as "supportedAlgorithms;binary".
+
+ As values of this syntax contain digitally signed data, values of
+ this syntax and the form of the value MUST be preserved as presented.
+
+
+
+
+
+Zeilenga Standards Track [Page 3]
+
+RFC 4523 LDAP X.509 Schema June 2006
+
+
+2.5. CertificateExactAssertion
+
+ ( 1.3.6.1.1.15.1 DESC 'X.509 Certificate Exact Assertion' )
+
+ A value of this syntax is an X.509 CertificateExactAssertion [X.509,
+ clause 11.3.1]. Values of this syntax MUST be encoded using the
+ Generic String Encoding Rules (GSER) [RFC3641]. Appendix A.1
+ provides an equivalent Augmented Backus-Naur Form (ABNF) [RFC4234]
+ grammar for this syntax.
+
+2.6. CertificateAssertion
+
+ ( 1.3.6.1.1.15.2 DESC 'X.509 Certificate Assertion' )
+
+ A value of this syntax is an X.509 CertificateAssertion [X.509,
+ clause 11.3.2]. Values of this syntax MUST be encoded using GSER
+ [RFC3641]. Appendix A.2 provides an equivalent ABNF [RFC4234]
+ grammar for this syntax.
+
+2.7. CertificatePairExactAssertion
+
+ ( 1.3.6.1.1.15.3
+ DESC 'X.509 Certificate Pair Exact Assertion' )
+
+ A value of this syntax is an X.509 CertificatePairExactAssertion
+ [X.509, clause 11.3.3]. Values of this syntax MUST be encoded using
+ GSER [RFC3641]. Appendix A.3 provides an equivalent ABNF [RFC4234]
+ grammar for this syntax.
+
+2.8. CertificatePairAssertion
+
+ ( 1.3.6.1.1.15.4 DESC 'X.509 Certificate Pair Assertion' )
+
+ A value of this syntax is an X.509 CertificatePairAssertion [X.509,
+ clause 11.3.4]. Values of this syntax MUST be encoded using GSER
+ [RFC3641]. Appendix A.4 provides an equivalent ABNF [RFC4234]
+ grammar for this syntax.
+
+2.9. CertificateListExactAssertion
+
+ ( 1.3.6.1.1.15.5
+ DESC 'X.509 Certificate List Exact Assertion' )
+
+ A value of this syntax is an X.509 CertificateListExactAssertion
+ [X.509, clause 11.3.5]. Values of this syntax MUST be encoded using
+ GSER [RFC3641]. Appendix A.5 provides an equivalent ABNF grammar for
+ this syntax.
+
+
+
+
+Zeilenga Standards Track [Page 4]
+
+RFC 4523 LDAP X.509 Schema June 2006
+
+
+2.10. CertificateListAssertion
+
+ ( 1.3.6.1.1.15.6 DESC 'X.509 Certificate List Assertion' )
+
+ A value of this syntax is an X.509 CertificateListAssertion [X.509,
+ clause 11.3.6]. Values of this syntax MUST be encoded using GSER
+ [RFC3641]. Appendix A.6 provides an equivalent ABNF [RFC4234]
+ grammar for this syntax.
+
+2.11. AlgorithmIdentifier
+
+ ( 1.3.6.1.1.15.7 DESC 'X.509 Algorithm Identifier' )
+
+ A value of this syntax is an X.509 AlgorithmIdentifier [X.509, Clause
+ 7]. Values of this syntax MUST be encoded using GSER [RFC3641].
+
+ Appendix A.7 provides an equivalent ABNF [RFC4234] grammar for this
+ syntax.
+
+3. Matching Rules
+
+ This section introduces a set of certificate and related matching
+ rules for use in LDAP. These rules are intended to act in accordance
+ with their X.500 counterparts.
+
+3.1. certificateExactMatch
+
+ The certificateExactMatch matching rule compares the presented
+ certificate exact assertion value with an attribute value of the
+ certificate syntax as described in clause 11.3.1 of [X.509].
+
+ ( 2.5.13.34 NAME 'certificateExactMatch'
+ DESC 'X.509 Certificate Exact Match'
+ SYNTAX 1.3.6.1.1.15.1 )
+
+3.2. certificateMatch
+
+ The certificateMatch matching rule compares the presented certificate
+ assertion value with an attribute value of the certificate syntax as
+ described in clause 11.3.2 of [X.509].
+
+ ( 2.5.13.35 NAME 'certificateMatch'
+ DESC 'X.509 Certificate Match'
+ SYNTAX 1.3.6.1.1.15.2 )
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 5]
+
+RFC 4523 LDAP X.509 Schema June 2006
+
+
+3.3. certificatePairExactMatch
+
+ The certificatePairExactMatch matching rule compares the presented
+ certificate pair exact assertion value with an attribute value of the
+ certificate pair syntax as described in clause 11.3.3 of [X.509].
+
+ ( 2.5.13.36 NAME 'certificatePairExactMatch'
+ DESC 'X.509 Certificate Pair Exact Match'
+ SYNTAX 1.3.6.1.1.15.3 )
+
+3.4. certificatePairMatch
+
+ The certificatePairMatch matching rule compares the presented
+ certificate pair assertion value with an attribute value of the
+ certificate pair syntax as described in clause 11.3.4 of [X.509].
+
+ ( 2.5.13.37 NAME 'certificatePairMatch'
+ DESC 'X.509 Certificate Pair Match'
+ SYNTAX 1.3.6.1.1.15.4 )
+
+3.5. certificateListExactMatch
+
+ The certificateListExactMatch matching rule compares the presented
+ certificate list exact assertion value with an attribute value of the
+ certificate pair syntax as described in clause 11.3.5 of [X.509].
+
+ ( 2.5.13.38 NAME 'certificateListExactMatch'
+ DESC 'X.509 Certificate List Exact Match'
+ SYNTAX 1.3.6.1.1.15.5 )
+
+3.6. certificateListMatch
+
+ The certificateListMatch matching rule compares the presented
+ certificate list assertion value with an attribute value of the
+ certificate pair syntax as described in clause 11.3.6 of [X.509].
+
+ ( 2.5.13.39 NAME 'certificateListMatch'
+ DESC 'X.509 Certificate List Match'
+ SYNTAX 1.3.6.1.1.15.6 )
+
+
+
+
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 6]
+
+RFC 4523 LDAP X.509 Schema June 2006
+
+
+3.7. algorithmIdentifierMatch
+
+ The algorithmIdentifierMatch mating rule compares a presented
+ algorithm identifier with an attribute value of the supported
+ algorithm as described in clause 11.3.7 of [X.509].
+
+ ( 2.5.13.40 NAME 'algorithmIdentifier'
+ DESC 'X.509 Algorithm Identifier Match'
+ SYNTAX 1.3.6.1.1.15.7 )
+
+4. Attribute Types
+
+ This section details a set of certificate and related attribute types
+ for use in LDAP.
+
+4.1. userCertificate
+
+ The userCertificate attribute holds the X.509 certificates issued to
+ the user by one or more certificate authorities, as discussed in
+ clause 11.2.1 of [X.509].
+
+ ( 2.5.4.36 NAME 'userCertificate'
+ DESC 'X.509 user certificate'
+ EQUALITY certificateExactMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.8 )
+
+ As required by this attribute type's syntax, values of this attribute
+ are requested and transferred using the attribute description
+ "userCertificate;binary".
+
+4.2. cACertificate
+
+ The cACertificate attribute holds the X.509 certificates issued to
+ the certificate authority (CA), as discussed in clause 11.2.2 of
+ [X.509].
+
+ ( 2.5.4.37 NAME 'cACertificate'
+ DESC 'X.509 CA certificate'
+ EQUALITY certificateExactMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.8 )
+
+ As required by this attribute type's syntax, values of this attribute
+ are requested and transferred using the attribute description
+ "cACertificate;binary".
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 7]
+
+RFC 4523 LDAP X.509 Schema June 2006
+
+
+4.3. crossCertificatePair
+
+ The crossCertificatePair attribute holds an X.509 certificate pair,
+ as discussed in clause 11.2.3 of [X.509].
+
+ ( 2.5.4.40 NAME 'crossCertificatePair'
+ DESC 'X.509 cross certificate pair'
+ EQUALITY certificatePairExactMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.10 )
+
+ As required by this attribute type's syntax, values of this attribute
+ are requested and transferred using the attribute description
+ "crossCertificatePair;binary".
+
+4.4. certificateRevocationList
+
+ The certificateRevocationList attribute holds certificate lists, as
+ discussed in 11.2.4 of [X.509].
+
+ ( 2.5.4.39 NAME 'certificateRevocationList'
+ DESC 'X.509 certificate revocation list'
+ EQUALITY certificateListExactMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.9 )
+
+ As required by this attribute type's syntax, values of this attribute
+ are requested and transferred using the attribute description
+ "certificateRevocationList;binary".
+
+4.5. authorityRevocationList
+
+ The authorityRevocationList attribute holds certificate lists, as
+ discussed in 11.2.5 of [X.509].
+
+ ( 2.5.4.38 NAME 'authorityRevocationList'
+ DESC 'X.509 authority revocation list'
+ EQUALITY certificateListExactMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.9 )
+
+ As required by this attribute type's syntax, values of this attribute
+ are requested and transferred using the attribute description
+ "authorityRevocationList;binary".
+
+
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 8]
+
+RFC 4523 LDAP X.509 Schema June 2006
+
+
+4.6. deltaRevocationList
+
+ The deltaRevocationList attribute holds certificate lists, as
+ discussed in 11.2.6 of [X.509].
+
+ ( 2.5.4.53 NAME 'deltaRevocationList'
+ DESC 'X.509 delta revocation list'
+ EQUALITY certificateListExactMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.9 )
+
+ As required by this attribute type's syntax, values of this attribute
+ MUST be requested and transferred using the attribute description
+ "deltaRevocationList;binary".
+
+4.7. supportedAlgorithms
+
+ The supportedAlgorithms attribute holds supported algorithms, as
+ discussed in 11.2.7 of [X.509].
+
+ ( 2.5.4.52 NAME 'supportedAlgorithms'
+ DESC 'X.509 supported algorithms'
+ EQUALITY algorithmIdentifierMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.49 )
+
+ As required by this attribute type's syntax, values of this attribute
+ MUST be requested and transferred using the attribute description
+ "supportedAlgorithms;binary".
+
+5. Object Classes
+
+ This section details a set of certificate-related object classes for
+ use in LDAP.
+
+5.1. pkiUser
+
+ This object class is used in augment entries for objects that may be
+ subject to certificates, as defined in clause 11.1.1 of [X.509].
+
+ ( 2.5.6.21 NAME 'pkiUser'
+ DESC 'X.509 PKI User'
+ SUP top AUXILIARY
+ MAY userCertificate )
+
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 9]
+
+RFC 4523 LDAP X.509 Schema June 2006
+
+
+5.2. pkiCA
+
+ This object class is used to augment entries for objects that act as
+ certificate authorities, as defined in clause 11.1.2 of [X.509]
+
+ ( 2.5.6.22 NAME 'pkiCA'
+ DESC 'X.509 PKI Certificate Authority'
+ SUP top AUXILIARY
+ MAY ( cACertificate $ certificateRevocationList $
+ authorityRevocationList $ crossCertificatePair ) )
+
+5.3. cRLDistributionPoint
+
+ This class is used to represent objects that act as CRL distribution
+ points, as discussed in clause 11.1.3 of [X.509].
+
+ ( 2.5.6.19 NAME 'cRLDistributionPoint'
+ DESC 'X.509 CRL distribution point'
+ SUP top STRUCTURAL
+ MUST cn
+ MAY ( certificateRevocationList $
+ authorityRevocationList $ deltaRevocationList ) )
+
+5.4. deltaCRL
+
+ The deltaCRL object class is used to augment entries to hold delta
+ revocation lists, as discussed in clause 11.1.4 of [X.509].
+
+ ( 2.5.6.23 NAME 'deltaCRL'
+ DESC 'X.509 delta CRL'
+ SUP top AUXILIARY
+ MAY deltaRevocationList )
+
+5.5. strongAuthenticationUser
+
+ This object class is used to augment entries for objects
+ participating in certificate-based authentication, as defined in
+ clause 6.15 of [X.521]. This object class is deprecated in favor of
+ pkiUser.
+
+ ( 2.5.6.15 NAME 'strongAuthenticationUser'
+ DESC 'X.521 strong authentication user'
+ SUP top AUXILIARY
+ MUST userCertificate )
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 10]
+
+RFC 4523 LDAP X.509 Schema June 2006
+
+
+5.6. userSecurityInformation
+
+ This object class is used to augment entries with needed additional
+ associated security information, as defined in clause 6.16 of
+ [X.521].
+
+ ( 2.5.6.18 NAME 'userSecurityInformation'
+ DESC 'X.521 user security information'
+ SUP top AUXILIARY
+ MAY ( supportedAlgorithms ) )
+
+5.7. certificationAuthority
+
+ This object class is used to augment entries for objects that act as
+ certificate authorities, as defined in clause 6.17 of [X.521]. This
+ object class is deprecated in favor of pkiCA.
+
+ ( 2.5.6.16 NAME 'certificationAuthority'
+ DESC 'X.509 certificate authority'
+ SUP top AUXILIARY
+ MUST ( authorityRevocationList $
+ certificateRevocationList $ cACertificate )
+ MAY crossCertificatePair )
+
+5.8. certificationAuthority-V2
+
+ This object class is used to augment entries for objects that act as
+ certificate authorities, as defined in clause 6.18 of [X.521]. This
+ object class is deprecated in favor of pkiCA.
+
+ ( 2.5.6.16.2 NAME 'certificationAuthority-V2'
+ DESC 'X.509 certificate authority, version 2'
+ SUP certificationAuthority AUXILIARY
+ MAY deltaRevocationList )
+
+6. Security Considerations
+
+ General certificate considerations [RFC3280] apply to LDAP-aware
+ certificate applications. General LDAP security considerations
+ [RFC4510] apply as well.
+
+ While elements of certificate information are commonly signed, these
+ signatures only protect the integrity of the signed information. In
+ the absence of data integrity protections in LDAP (or lower layer,
+ e.g., IPsec), a server is not assured that client certificate request
+ (or other request) was unaltered in transit. Likewise, a client
+ cannot be assured that the results of the query were unaltered in
+
+
+
+
+Zeilenga Standards Track [Page 11]
+
+RFC 4523 LDAP X.509 Schema June 2006
+
+
+ transit. Hence, it is generally recommended that implementations
+ make use of authentication and data integrity services in LDAP
+ [RFC4513][RFC4511].
+
+7. IANA Considerations
+
+7.1. Object Identifier Registration
+
+ The IANA has registered an LDAP Object Identifier [RFC4520] for use
+ in this technical specification.
+
+ Subject: Request for LDAP OID Registration
+ Person & email address to contact for further information:
+ Kurt Zeilenga <kurt@OpenLDAP.org>
+ Specification: RFC 4523
+ Author/Change Controller: IESG
+ Comments:
+ Identifies the LDAP X.509 Certificate schema elements
+ introduced in this document.
+
+7.2. Descriptor Registration
+
+ The IANA has updated the LDAP
+ Descriptor registry [RFC44520] as indicated below.
+
+ Subject: Request for LDAP Descriptor Registration
+ Descriptor (short name): see table
+ Object Identifier: see table
+ Person & email address to contact for further information:
+ Kurt Zeilenga <kurt@OpenLDAP.org>
+ Usage: see table
+ Specification: RFC 4523
+ Author/Change Controller: IESG
+
+ algorithmIdentifierMatch M 2.5.13.40
+ authorityRevocationList A 2.5.4.38 *
+ cACertificate A 2.5.4.37 *
+ cRLDistributionPoint O 2.5.6.19 *
+ certificateExactMatch M 2.5.13.34
+ certificateListExactMatch M 2.5.13.38
+ certificateListMatch M 2.5.13.39
+ certificateMatch M 2.5.13.35
+ certificatePairExactMatch M 2.5.13.36
+ certificatePairMatch M 2.5.13.37
+ certificateRevocationList A 2.5.4.39 *
+ certificationAuthority O 2.5.6.16 *
+ certificationAuthority-V2 O 2.5.6.16.2 *
+ crossCertificatePair A 2.5.4.40 *
+
+
+
+Zeilenga Standards Track [Page 12]
+
+RFC 4523 LDAP X.509 Schema June 2006
+
+
+ deltaCRL O 2.5.6.23 *
+ deltaRevocationList A 2.5.4.53 *
+ pkiCA O 2.5.6.22 *
+ pkiUser O 2.5.6.21 *
+ strongAuthenticationUser O 2.5.6.15 *
+ supportedAlgorithms A 2.5.4.52 *
+ userCertificate A 2.5.4.36 *
+ userSecurityInformation O 2.5.6.18 *
+
+ * Updates previous registration
+
+8. Acknowledgements
+
+ This document is based on X.509, a product of the ITU-T. A number of
+ LDAP schema definitions were based on those found in RFCs 2252 and
+ 2256, both products of the IETF ASID WG. The ABNF productions in
+ Appendix A were provided by Steven Legg. Additional material was
+ borrowed from prior works by David Chadwick and Steven Legg to refine
+ the LDAP X.509 schema.
+
+9. References
+
+9.1. Normative References
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [RFC3641] Legg, S., "Generic String Encoding Rules (GSER) for ASN.1
+ Types", RFC 3641, October 2003.
+
+ [RFC4510] Zeilenga, K., Ed., "Lightweight Directory Access Protocol
+ (LDAP): Technical Specification Road Map", RFC 4510, June
+ 2006.
+
+ [RFC4512] Zeilenga, K., "Lightweight Directory Access Protocol
+ (LDAP): Directory Information Models", RFC 4512, June
+ 2006.
+
+ [RFC4522] Legg, S., "Lightweight Directory Access Protocol (LDAP):
+ The Binary Encoding Option", RFC 4522, June 2006.
+
+ [X.509] International Telecommunication Union - Telecommunication
+ Standardization Sector, "The Directory: Authentication
+ Framework", X.509(2000).
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 13]
+
+RFC 4523 LDAP X.509 Schema June 2006
+
+
+ [X.521] International Telecommunication Union - Telecommunication
+ Standardization Sector, "The Directory: Selected Object
+ Classes", X.521(2000).
+
+ [X.690] International Telecommunication Union - Telecommunication
+ Standardization Sector, "Specification of ASN.1 encoding
+ rules: Basic Encoding Rules (BER), Canonical Encoding
+ Rules (CER), and Distinguished Encoding Rules (DER)",
+ X.690(2002) (also ISO/IEC 8825-1:2002).
+
+9.2. Informative References
+
+ [RFC1777] Yeong, W., Howes, T., and S. Kille, "Lightweight Directory
+ Access Protocol", RFC 1777, March 1995.
+
+ [RFC2156] Kille, S., "MIXER (Mime Internet X.400 Enhanced Relay):
+ Mapping between X.400 and RFC 822/MIME", RFC 2156, January
+ 1998.
+
+ [RFC3280] Housley, R., Polk, W., Ford, W., and D. Solo, "Internet
+ X.509 Public Key Infrastructure Certificate and
+ Certificate Revocation List (CRL) Profile", RFC 3280,
+ April 2002.
+
+ [RFC3494] Zeilenga, K., "Lightweight Directory Access Protocol
+ version 2 (LDAPv2) to Historic Status", RFC 3494, March
+ 2003.
+
+ [RFC3642] Legg, S., "Common Elements of Generic String Encoding
+ Rules (GSER) Encodings", RFC 3642, October 2003.
+
+ [RFC4234] Crocker, D. and P. Overell, "Augmented BNF for Syntax
+ Specifications: ABNF", RFC 4234, October 2005.
+
+ [RFC4511] Sermersheim, J., Ed., "Lightweight Directory Access
+ Protocol (LDAP): The Protocol", RFC 4511, June 2006.
+
+ [RFC4513] Harrison, R. Ed., "Lightweight Directory Access Protocol
+ (LDAP): Authentication Methods and Security Mechanisms",
+ RFC 4513, June 2006.
+
+ [RFC4520] Zeilenga, K., "Internet Assigned Numbers Authority (IANA)
+ Considerations for the Lightweight Directory Access
+ Protocol (LDAP)", BCP 64, RFC 4520, June 2006.
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 14]
+
+RFC 4523 LDAP X.509 Schema June 2006
+
+
+Appendix A.
+
+ This appendix is informative.
+
+ This appendix provides ABNF [RFC4234] grammars for GSER-based
+ [RFC3641] LDAP-specific encodings specified in this document. These
+ grammars where produced using, and relying on, Common Elements for
+ GSER Encodings [RFC3642].
+
+A.1. CertificateExactAssertion
+
+ CertificateExactAssertion = "{" sp cea-serialNumber ","
+ sp cea-issuer sp "}"
+
+ cea-serialNumber = id-serialNumber msp CertificateSerialNumber
+ cea-issuer = id-issuer msp Name
+
+ id-serialNumber =
+ %x73.65.72.69.61.6C.4E.75.6D.62.65.72 ; 'serialNumber'
+ id-issuer = %x69.73.73.75.65.72 ; 'issuer'
+
+ Name = id-rdnSequence ":" RDNSequence
+ id-rdnSequence = %x72.64.6E.53.65.71.75.65.6E.63.65 ; 'rdnSequence'
+
+ CertificateSerialNumber = INTEGER
+
+A.2. CertificateAssertion
+
+CertificateAssertion = "{" [ sp ca-serialNumber ]
+ [ sep sp ca-issuer ]
+ [ sep sp ca-subjectKeyIdentifier ]
+ [ sep sp ca-authorityKeyIdentifier ]
+ [ sep sp ca-certificateValid ]
+ [ sep sp ca-privateKeyValid ]
+ [ sep sp ca-subjectPublicKeyAlgID ]
+ [ sep sp ca-keyUsage ]
+ [ sep sp ca-subjectAltName ]
+ [ sep sp ca-policy ]
+ [ sep sp ca-pathToName ]
+ [ sep sp ca-subject ]
+ [ sep sp ca-nameConstraints ] sp "}"
+
+ca-serialNumber = id-serialNumber msp CertificateSerialNumber
+ca-issuer = id-issuer msp Name
+ca-subjectKeyIdentifier = id-subjectKeyIdentifier msp
+ SubjectKeyIdentifier
+ca-authorityKeyIdentifier = id-authorityKeyIdentifier msp
+ AuthorityKeyIdentifier
+
+
+
+Zeilenga Standards Track [Page 15]
+
+RFC 4523 LDAP X.509 Schema June 2006
+
+
+ca-certificateValid = id-certificateValid msp Time
+ca-privateKeyValid = id-privateKeyValid msp GeneralizedTime
+ca-subjectPublicKeyAlgID = id-subjectPublicKeyAlgID msp
+ OBJECT-IDENTIFIER
+ca-keyUsage = id-keyUsage msp KeyUsage
+ca-subjectAltName = id-subjectAltName msp AltNameType
+ca-policy = id-policy msp CertPolicySet
+ca-pathToName = id-pathToName msp Name
+ca-subject = id-subject msp Name
+ca-nameConstraints = id-nameConstraints msp NameConstraintsSyntax
+
+id-subjectKeyIdentifier =
+ %x73.75.62.6A.65.63.74.4B.65.79.49.64.65.6E.74.69.66.69.65.72
+ ; 'subjectKeyIdentifier'
+id-authorityKeyIdentifier =
+ %x61.75.74.68.6F.72.69.74.79.4B.65.79.49.64.65.6E.74.69.66.69.65.72
+ ; 'authorityKeyIdentifier'
+id-certificateValid = %x63.65.72.74.69.66.69.63.61.74.65.56.61.6C.69.64
+ ; 'certificateValid'
+id-privateKeyValid = %x70.72.69.76.61.74.65.4B.65.79.56.61.6C.69.64
+ ; 'privateKeyValid'
+id-subjectPublicKeyAlgID =
+ %x73.75.62.6A.65.63.74.50.75.62.6C.69.63.4B.65.79.41.6C.67.49.44
+ ; 'subjectPublicKeyAlgID'
+id-keyUsage = %x6B.65.79.55.73.61.67.65 ; 'keyUsage'
+id-subjectAltName = %x73.75.62.6A.65.63.74.41.6C.74.4E.61.6D.65
+ ; 'subjectAltName'
+id-policy = %x70.6F.6C.69.63.79 ; 'policy'
+id-pathToName = %x70.61.74.68.54.6F.4E.61.6D.65 ; 'pathToName'
+id-subject = %x73.75.62.6A.65.63.74 ; 'subject'
+id-nameConstraints = %x6E.61.6D.65.43.6F.6E.73.74.72.61.69.6E.74.73
+ ; 'nameConstraints'
+
+SubjectKeyIdentifier = KeyIdentifier
+
+KeyIdentifier = OCTET-STRING
+
+AuthorityKeyIdentifier = "{" [ sp aki-keyIdentifier ]
+ [ sep sp aki-authorityCertIssuer ]
+ [ sep sp aki-authorityCertSerialNumber ] sp "}"
+
+aki-keyIdentifier = id-keyIdentifier msp KeyIdentifier
+aki-authorityCertIssuer = id-authorityCertIssuer msp GeneralNames
+
+GeneralNames = "{" sp GeneralName *( "," sp GeneralName ) sp "}"
+GeneralName = gn-otherName
+ / gn-rfc822Name
+ / gn-dNSName
+
+
+
+Zeilenga Standards Track [Page 16]
+
+RFC 4523 LDAP X.509 Schema June 2006
+
+
+ / gn-x400Address
+ / gn-directoryName
+ / gn-ediPartyName
+ / gn-uniformResourceIdentifier
+ / gn-iPAddress
+ / gn-registeredID
+
+gn-otherName = id-otherName ":" OtherName
+gn-rfc822Name = id-rfc822Name ":" IA5String
+gn-dNSName = id-dNSName ":" IA5String
+gn-x400Address = id-x400Address ":" ORAddress
+gn-directoryName = id-directoryName ":" Name
+gn-ediPartyName = id-ediPartyName ":" EDIPartyName
+gn-iPAddress = id-iPAddress ":" OCTET-STRING
+gn-registeredID = gn-id-registeredID ":" OBJECT-IDENTIFIER
+
+gn-uniformResourceIdentifier = id-uniformResourceIdentifier
+ ":" IA5String
+
+id-otherName = %x6F.74.68.65.72.4E.61.6D.65 ; 'otherName'
+gn-id-registeredID = %x72.65.67.69.73.74.65.72.65.64.49.44
+ ; 'registeredID'
+
+OtherName = "{" sp on-type-id "," sp on-value sp "}"
+on-type-id = id-type-id msp OBJECT-IDENTIFIER
+on-value = id-value msp Value
+ ;; <Value> as defined in Section 3 of [RFC3641]
+
+id-type-id = %x74.79.70.65.2D.69.64 ; 'type-id'
+id-value = %x76.61.6C.75.65 ; 'value'
+
+ORAddress = dquote *SafeIA5Character dquote
+SafeIA5Character = %x01-21 / %x23-7F / ; ASCII minus dquote
+ dquote dquote ; escaped double quote
+dquote = %x22 ; '"' (double quote)
+
+;; Note: The <ORAddress> rule encodes the x400Address component
+;; of a GeneralName as a character string between double quotes.
+;; The character string is first derived according to Section 4.1
+;; of [RFC2156], and then any embedded double quotes are escaped
+;; by being repeated. This resulting string is output between
+;; double quotes.
+
+EDIPartyName = "{" [ sp nameAssigner "," ] sp partyName sp "}"
+nameAssigner = id-nameAssigner msp DirectoryString
+partyName = id-partyName msp DirectoryString
+id-nameAssigner = %x6E.61.6D.65.41.73.73.69.67.6E.65.72
+ ; 'nameAssigner'
+
+
+
+Zeilenga Standards Track [Page 17]
+
+RFC 4523 LDAP X.509 Schema June 2006
+
+
+id-partyName = %x70.61.72.74.79.4E.61.6D.65 ; 'partyName'
+
+aki-authorityCertSerialNumber = id-authorityCertSerialNumber
+ msp CertificateSerialNumber
+
+id-keyIdentifier = %x6B.65.79.49.64.65.6E.74.69.66.69.65.72
+ ; 'keyIdentifier'
+id-authorityCertIssuer =
+ %x61.75.74.68.6F.72.69.74.79.43.65.72.74.49.73.73.75.65.72
+ ; 'authorityCertIssuer'
+
+id-authorityCertSerialNumber = %x61.75.74.68.6F.72.69.74.79.43
+ %x65.72.74.53.65.72.69.61.6C.4E.75.6D.62.65.72
+ ; 'authorityCertSerialNumber'
+
+Time = time-utcTime / time-generalizedTime
+time-utcTime = id-utcTime ":" UTCTime
+time-generalizedTime = id-generalizedTime ":" GeneralizedTime
+id-utcTime = %x75.74.63.54.69.6D.65 ; 'utcTime'
+id-generalizedTime = %x67.65.6E.65.72.61.6C.69.7A.65.64.54.69.6D.65
+ ; 'generalizedTime'
+
+KeyUsage = BIT-STRING / key-usage-bit-list
+key-usage-bit-list = "{" [ sp key-usage *( "," sp key-usage ) ] sp "}"
+
+;; Note: The <key-usage-bit-list> rule encodes the one bits in
+;; a KeyUsage value as a comma separated list of identifiers.
+
+key-usage = id-digitalSignature
+ / id-nonRepudiation
+ / id-keyEncipherment
+ / id-dataEncipherment
+ / id-keyAgreement
+ / id-keyCertSign
+ / id-cRLSign
+ / id-encipherOnly
+ / id-decipherOnly
+
+id-digitalSignature = %x64.69.67.69.74.61.6C.53.69.67.6E.61.74
+ %x75.72.65 ; 'digitalSignature'
+id-nonRepudiation = %x6E.6F.6E.52.65.70.75.64.69.61.74.69.6F.6E
+ ; 'nonRepudiation'
+id-keyEncipherment = %x6B.65.79.45.6E.63.69.70.68.65.72.6D.65.6E.74
+ ; 'keyEncipherment'
+id-dataEncipherment = %x64.61.74.61.45.6E.63.69.70.68.65.72.6D.65.6E
+ %x74 ; "dataEncipherment'
+id-keyAgreement = %x6B.65.79.41.67.72.65.65.6D.65.6E.74
+ ; 'keyAgreement'
+
+
+
+Zeilenga Standards Track [Page 18]
+
+RFC 4523 LDAP X.509 Schema June 2006
+
+
+id-keyCertSign = %x6B.65.79.43.65.72.74.53.69.67.6E
+ ; 'keyCertSign'
+id-cRLSign = %x63.52.4C.53.69.67.6E ; "cRLSign"
+id-encipherOnly = %x65.6E.63.69.70.68.65.72.4F.6E.6C.79
+ ; 'encipherOnly'
+id-decipherOnly = %x64.65.63.69.70.68.65.72.4F.6E.6C.79
+ ; 'decipherOnly'
+
+AltNameType = ant-builtinNameForm / ant-otherNameForm
+
+ant-builtinNameForm = id-builtinNameForm ":" BuiltinNameForm
+ant-otherNameForm = id-otherNameForm ":" OBJECT-IDENTIFIER
+
+id-builtinNameForm = %x62.75.69.6C.74.69.6E.4E.61.6D.65.46.6F.72.6D
+ ; 'builtinNameForm'
+id-otherNameForm = %x6F.74.68.65.72.4E.61.6D.65.46.6F.72.6D
+ ; 'otherNameForm'
+
+BuiltinNameForm = id-rfc822Name
+ / id-dNSName
+ / id-x400Address
+ / id-directoryName
+ / id-ediPartyName
+ / id-uniformResourceIdentifier
+ / id-iPAddress
+ / id-registeredId
+
+id-rfc822Name = %x72.66.63.38.32.32.4E.61.6D.65 ; 'rfc822Name'
+id-dNSName = %x64.4E.53.4E.61.6D.65 ; 'dNSName'
+id-x400Address = %x78.34.30.30.41.64.64.72.65.73.73 ; 'x400Address'
+id-directoryName = %x64.69.72.65.63.74.6F.72.79.4E.61.6D.65
+ ; 'directoryName'
+id-ediPartyName = %x65.64.69.50.61.72.74.79.4E.61.6D.65
+ ; 'ediPartyName'
+id-iPAddress = %x69.50.41.64.64.72.65.73.73 ; 'iPAddress'
+id-registeredId = %x72.65.67.69.73.74.65.72.65.64.49.64
+ ; 'registeredId'
+
+id-uniformResourceIdentifier = %x75.6E.69.66.6F.72.6D.52.65.73.6F.75
+ %x72.63.65.49.64.65.6E.74.69.66.69.65.72
+ ; 'uniformResourceIdentifier'
+
+CertPolicySet = "{" sp CertPolicyId *( "," sp CertPolicyId ) sp "}"
+CertPolicyId = OBJECT-IDENTIFIER
+
+NameConstraintsSyntax = "{" [ sp ncs-permittedSubtrees ]
+ [ sep sp ncs-excludedSubtrees ] sp "}"
+
+
+
+
+Zeilenga Standards Track [Page 19]
+
+RFC 4523 LDAP X.509 Schema June 2006
+
+
+ncs-permittedSubtrees = id-permittedSubtrees msp GeneralSubtrees
+ncs-excludedSubtrees = id-excludedSubtrees msp GeneralSubtrees
+
+id-permittedSubtrees =
+ %x70.65.72.6D.69.74.74.65.64.53.75.62.74.72.65.65.73
+ ; 'permittedSubtrees'
+id-excludedSubtrees =
+ %x65.78.63.6C.75.64.65.64.53.75.62.74.72.65.65.73
+ ; 'excludedSubtrees'
+
+GeneralSubtrees = "{" sp GeneralSubtree
+ *( "," sp GeneralSubtree ) sp "}"
+GeneralSubtree = "{" sp gs-base
+ [ "," sp gs-minimum ]
+ [ "," sp gs-maximum ] sp "}"
+
+gs-base = id-base msp GeneralName
+gs-minimum = id-minimum msp BaseDistance
+gs-maximum = id-maximum msp BaseDistance
+
+id-base = %x62.61.73.65 ; 'base'
+id-minimum = %x6D.69.6E.69.6D.75.6D ; 'minimum'
+id-maximum = %x6D.61.78.69.6D.75.6D ; 'maximum'
+
+BaseDistance = INTEGER-0-MAX
+
+A.3. CertificatePairExactAssertion
+
+ CertificatePairExactAssertion = "{" [ sp cpea-issuedTo ]
+ [sep sp cpea-issuedBy ] sp "}"
+ ;; At least one of <cpea-issuedTo> or <cpea-issuedBy> MUST be present.
+
+ cpea-issuedTo = id-issuedToThisCAAssertion msp
+ CertificateExactAssertion
+ cpea-issuedBy = id-issuedByThisCAAssertion msp
+ CertificateExactAssertion
+
+ id-issuedToThisCAAssertion = %x69.73.73.75.65.64.54.6F.54.68.69.73
+ %x43.41.41.73.73.65.72.74.69.6F.6E ; 'issuedToThisCAAssertion'
+ id-issuedByThisCAAssertion = %x69.73.73.75.65.64.42.79.54.68.69.73
+ %x43.41.41.73.73.65.72.74.69.6F.6E ; 'issuedByThisCAAssertion'
+
+
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 20]
+
+RFC 4523 LDAP X.509 Schema June 2006
+
+
+A.4. CertificatePairAssertion
+
+ CertificatePairAssertion = "{" [ sp cpa-issuedTo ]
+ [sep sp cpa-issuedBy ] sp "}"
+ ;; At least one of <cpa-issuedTo> and <cpa-issuedBy> MUST be present.
+
+ cpa-issuedTo = id-issuedToThisCAAssertion msp CertificateAssertion
+ cpa-issuedBy = id-issuedByThisCAAssertion msp CertificateAssertion
+
+A.5. CertificateListExactAssertion
+
+ CertificateListExactAssertion = "{" sp clea-issuer ","
+ sp clea-thisUpdate
+ [ "," sp clea-distributionPoint ] sp "}"
+
+ clea-issuer = id-issuer msp Name
+ clea-thisUpdate = id-thisUpdate msp Time
+ clea-distributionPoint = id-distributionPoint msp
+ DistributionPointName
+
+ id-thisUpdate = %x74.68.69.73.55.70.64.61.74.65 ; 'thisUpdate'
+ id-distributionPoint =
+ %x64.69.73.74.72.69.62.75.74.69.6F.6E.50.6F.69.6E.74
+ ; 'distributionPoint'
+
+ DistributionPointName = dpn-fullName / dpn-nameRelativeToCRLIssuer
+
+ dpn-fullName = id-fullName ":" GeneralNames
+ dpn-nameRelativeToCRLIssuer = id-nameRelativeToCRLIssuer ":"
+ RelativeDistinguishedName
+
+ id-fullName = %x66.75.6C.6C.4E.61.6D.65 ; 'fullName'
+ id-nameRelativeToCRLIssuer = %x6E.61.6D.65.52.65.6C.61.74.69.76.65
+ %x54.6F.43.52.4C.49.73.73.75.65.72 ; 'nameRelativeToCRLIssuer'
+
+A.6. CertificateListAssertion
+
+ CertificateListAssertion = "{" [ sp cla-issuer ]
+ [ sep sp cla-minCRLNumber ]
+ [ sep sp cla-maxCRLNumber ]
+ [ sep sp cla-reasonFlags ]
+ [ sep sp cla-dateAndTime ]
+ [ sep sp cla-distributionPoint ]
+ [ sep sp cla-authorityKeyIdentifier ] sp "}"
+
+ cla-issuer = id-issuer msp Name
+ cla-minCRLNumber = id-minCRLNumber msp CRLNumber
+ cla-maxCRLNumber = id-maxCRLNumber msp CRLNumber
+
+
+
+Zeilenga Standards Track [Page 21]
+
+RFC 4523 LDAP X.509 Schema June 2006
+
+
+ cla-reasonFlags = id-reasonFlags msp ReasonFlags
+ cla-dateAndTime = id-dateAndTime msp Time
+
+ cla-distributionPoint = id-distributionPoint msp
+ DistributionPointName
+
+ cla-authorityKeyIdentifier = id-authorityKeyIdentifier msp
+ AuthorityKeyIdentifier
+
+ id-minCRLNumber = %x6D.69.6E.43.52.4C.4E.75.6D.62.65.72
+ ; 'minCRLNumber'
+ id-maxCRLNumber = %x6D.61.78.43.52.4C.4E.75.6D.62.65.72
+ ; 'maxCRLNumber'
+ id-reasonFlags = %x72.65.61.73.6F.6E.46.6C.61.67.73 ; 'reasonFlags'
+ id-dateAndTime = %x64.61.74.65.41.6E.64.54.69.6D.65 ; 'dateAndTime'
+
+ CRLNumber = INTEGER-0-MAX
+
+ ReasonFlags = BIT-STRING
+ / "{" [ sp reason-flag *( "," sp reason-flag ) ] sp "}"
+
+ reason-flag = id-unused
+ / id-keyCompromise
+ / id-cACompromise
+ / id-affiliationChanged
+ / id-superseded
+ / id-cessationOfOperation
+ / id-certificateHold
+ / id-privilegeWithdrawn
+ / id-aACompromise
+
+ id-unused = %x75.6E.75.73.65.64 ; 'unused'
+ id-keyCompromise = %x6B.65.79.43.6F.6D.70.72.6F.6D.69.73.65
+ ; 'keyCompromise'
+ id-cACompromise = %x63.41.43.6F.6D.70.72.6F.6D.69.73.65
+ ; 'cACompromise'
+ id-affiliationChanged =
+ %x61.66.66.69.6C.69.61.74.69.6F.6E.43.68.61.6E.67.65.64
+ ; 'affiliationChanged'
+ id-superseded = %x73.75.70.65.72.73.65.64.65.64 ; 'superseded'
+ id-cessationOfOperation =
+ %x63.65.73.73.61.74.69.6F.6E.4F.66.4F.70.65.72.61.74.69.6F.6E
+ ; 'cessationOfOperation'
+ id-certificateHold = %x63.65.72.74.69.66.69.63.61.74.65.48.6F.6C.64
+ ; 'certificateHold'
+ id-privilegeWithdrawn =
+ %x70.72.69.76.69.6C.65.67.65.57.69.74.68.64.72.61.77.6E
+ ; 'privilegeWithdrawn'
+
+
+
+Zeilenga Standards Track [Page 22]
+
+RFC 4523 LDAP X.509 Schema June 2006
+
+
+ id-aACompromise = %x61.41.43.6F.6D.70.72.6F.6D.69.73.65
+ ; 'aACompromise'
+
+A.7. AlgorithmIdentifier
+
+ AlgorithmIdentifier = "{" sp ai-algorithm
+ [ "," sp ai-parameters ] sp "}"
+
+ ai-algorithm = id-algorithm msp OBJECT-IDENTIFIER
+ ai-parameters = id-parameters msp Value
+ id-algorithm = %x61.6C.67.6F.72.69.74.68.6D ; 'algorithm'
+ id-parameters = %x70.61.72.61.6D.65.74.65.72.73 ; 'parameters'
+
+Author's Address
+
+ Kurt D. Zeilenga
+ OpenLDAP Foundation
+
+ EMail: Kurt@OpenLDAP.org
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 23]
+
+RFC 4523 LDAP X.509 Schema June 2006
+
+
+Full Copyright Statement
+
+ Copyright (C) The Internet Society (2006).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
+ ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
+ INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
+ INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be
+ found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at
+ ietf-ipr@ietf.org.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is provided by the IETF
+ Administrative Support Activity (IASA).
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 24]
+
diff --git a/source4/ldap_server/devdocs/rfc4524.txt b/source4/ldap_server/devdocs/rfc4524.txt
new file mode 100644
index 0000000000..fa36be27a3
--- /dev/null
+++ b/source4/ldap_server/devdocs/rfc4524.txt
@@ -0,0 +1,1403 @@
+
+
+
+
+
+
+Network Working Group K. Zeilenga, Ed.
+Request for Comments: 4524 OpenLDAP Foundation
+Obsoletes: 1274 June 2006
+Updates: 2247, 2798
+Category: Standards Track
+
+
+ COSINE LDAP/X.500 Schema
+
+Status of This Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2006).
+
+Abstract
+
+ This document provides a collection of schema elements for use with
+ the Lightweight Directory Access Protocol (LDAP) from the COSINE and
+ Internet X.500 pilot projects.
+
+ This document obsoletes RFC 1274 and updates RFCs 2247 and 2798.
+
+Table of Contents
+
+ 1. Introduction ....................................................3
+ 1.1. Relationship to Other Documents ............................3
+ 1.2. Terminology and Conventions ................................4
+ 2. COSINE Attribute Types ..........................................4
+ 2.1. associatedDomain ...........................................4
+ 2.2. associatedName .............................................5
+ 2.3. buildingName ...............................................5
+ 2.4. co .........................................................5
+ 2.5. documentAuthor .............................................6
+ 2.6. documentIdentifier .........................................6
+ 2.7. documentLocation ...........................................6
+ 2.8. documentPublisher ..........................................7
+ 2.9. documentTitle ..............................................7
+ 2.10. documentVersion ...........................................7
+ 2.11. drink .....................................................8
+ 2.12. homePhone .................................................8
+ 2.13. homePostalAddress .........................................8
+
+
+
+Zeilenga Standards Track [Page 1]
+
+RFC 4524 COSINE LDAP/X.500 Schema June 2006
+
+
+ 2.14. host ......................................................9
+ 2.15. info ......................................................9
+ 2.16. mail ......................................................9
+ 2.17. manager ..................................................10
+ 2.18. mobile ...................................................10
+ 2.19. organizationalStatus .....................................11
+ 2.20. pager ....................................................11
+ 2.21. personalTitle ............................................11
+ 2.22. roomNumber ...............................................12
+ 2.23. secretary ................................................12
+ 2.24. uniqueIdentifier .........................................12
+ 2.25. userClass ................................................13
+ 3. COSINE Object Classes ..........................................13
+ 3.1. account ...................................................13
+ 3.2. document ..................................................14
+ 3.3. documentSeries ............................................14
+ 3.4. domain ....................................................15
+ 3.5. domainRelatedObject .......................................16
+ 3.6. friendlyCountry ...........................................16
+ 3.7. rFC822LocalPart ...........................................17
+ 3.8. room ......................................................18
+ 3.9. simpleSecurityObject ......................................18
+ 4. Security Considerations ........................................18
+ 5. IANA Considerations ............................................19
+ 6. Acknowledgements ...............................................20
+ 7. References .....................................................20
+ 7.1. Normative References ......................................20
+ 7.2. Informative References ....................................21
+ Appendix A. Changes since RFC 1274 ...............................23
+ A.1. LDAP Short Names .........................................23
+ A.2. pilotObject ..............................................23
+ A.3. pilotPerson ..............................................23
+ A.4. dNSDomain ................................................24
+ A.5. pilotDSA and qualityLabelledData .........................24
+ A.6. Attribute Syntaxes .......................................24
+ Appendix B. Changes since RFC 2247 ...............................24
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 2]
+
+RFC 4524 COSINE LDAP/X.500 Schema June 2006
+
+
+1. Introduction
+
+ In the late 1980s, X.500 Directory Services were standardized by the
+ CCITT (Commite' Consultatif International de Telegraphique et
+ Telephonique), now a part of the ITU (International Telephone Union).
+ This lead to Directory Service piloting activities in the early
+ 1990s, including the COSINE (Co-operation and Open Systems
+ Interconnection in Europe) PARADISE Project pilot [COSINEpilot] in
+ Europe. Motivated by needs for large-scale directory pilots, RFC
+ 1274 was published to standardize the directory schema and naming
+ architecture for use in the COSINE and other Internet X.500 pilots
+ [RFC1274].
+
+ In the years that followed, X.500 Directory Services have evolved to
+ incorporate new capabilities and even new protocols. In particular,
+ the Lightweight Directory Access Protocol (LDAP) [RFC4510] was
+ introduced in the early 1990s [RFC1487], with Version 3 of LDAP
+ introduced in the late 1990s [RFC2251] and subsequently revised in
+ 2005 [RFC4510].
+
+ While much of the material in RFC 1274 has been superceded by
+ subsequently published ITU-T Recommendations and IETF RFCs, many of
+ the schema elements lack standardized schema descriptions for use in
+ modern X.500 and LDAP directory services despite the fact that these
+ schema elements are in wide use today. As the old schema
+ descriptions cannot be used without adaptation, interoperability
+ issues may arise due to lack of standardized modern schema
+ descriptions.
+
+ This document addresses these issues by offering standardized schema
+ descriptions, where needed, for widely used COSINE schema elements.
+
+1.1. Relationship to Other Documents
+
+ This document, together with [RFC4519] and [RFC4517], obsoletes RFC
+ 1274 in its entirety. [RFC4519] replaces Sections 9.3.1 (Userid) and
+ 9.3.21 (Domain Component) of RFC 1274. [RFC4517] replaces Section
+ 9.4 (Generally useful syntaxes) of RFC 1274.
+
+ This document replaces the remainder of RFC 1274. Appendix A
+ discusses changes since RFC 1274, as well as why certain schema
+ elements were not brought forward in this revision of the COSINE
+ schema. All elements not brought are to be regarded as Historic.
+
+ The description of the 'domain' object class provided in this
+ document supercedes that found in RFC 2247. That is, Section 3.4 of
+ this document replaces Section 5.2 of [RFC2247].
+
+
+
+
+Zeilenga Standards Track [Page 3]
+
+RFC 4524 COSINE LDAP/X.500 Schema June 2006
+
+
+ Some of the schema elements specified here were described in RFC 2798
+ (inetOrgPerson schema). This document supersedes these descriptions.
+ This document, together with [RFC4519], replaces Section 9.1.3 of RFC
+ 2798.
+
+1.2. Terminology and Conventions
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in BCP 14 [RFC2119].
+
+ DIT stands for Directory Information Tree.
+ DN stands for Distinguished Name.
+ DSA stands for Directory System Agent, a server.
+ DSE stands for DSA-Specific Entry.
+ DUA stands for Directory User Agent, a client.
+
+ These terms are discussed in [RFC4512].
+
+ Schema definitions are provided using LDAP description formats
+ [RFC4512]. Definitions provided here are formatted (line wrapped)
+ for readability.
+
+2. COSINE Attribute Types
+
+ This section details COSINE attribute types for use in LDAP.
+
+2.1. associatedDomain
+
+ The 'associatedDomain' attribute specifies DNS [RFC1034][RFC2181]
+ host names [RFC1123] that are associated with an object. That is,
+ values of this attribute should conform to the following ABNF:
+
+ domain = root / label *( DOT label )
+ root = SPACE
+ label = LETDIG [ *61( LETDIG / HYPHEN ) LETDIG ]
+ LETDIG = %x30-39 / %x41-5A / %x61-7A ; "0" - "9" / "A"-"Z" / "a"-"z"
+ SPACE = %x20 ; space (" ")
+ HYPHEN = %x2D ; hyphen ("-")
+ DOT = %x2E ; period (".")
+
+ For example, the entry in the DIT with a DN <DC=example,DC=com> might
+ have an associated domain of "example.com".
+
+ ( 0.9.2342.19200300.100.1.37 NAME 'associatedDomain'
+ EQUALITY caseIgnoreIA5Match
+ SUBSTR caseIgnoreIA5SubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+
+
+
+Zeilenga Standards Track [Page 4]
+
+RFC 4524 COSINE LDAP/X.500 Schema June 2006
+
+
+ The IA5String (1.3.6.1.4.1.1466.115.121.1.26) syntax and the
+ 'caseIgnoreIA5Match' and 'caseIgnoreIA5SubstringsMatch' rules are
+ described in [RFC4517].
+
+ Note that the directory will not ensure that values of this attribute
+ conform to the <domain> production provided above. It is the
+ application's responsibility to ensure that domains it stores in this
+ attribute are appropriately represented.
+
+ Also note that applications supporting Internationalized Domain Names
+ SHALL use the ToASCII method [RFC3490] to produce <label> components
+ of the <domain> production.
+
+2.2. associatedName
+
+ The 'associatedName' attribute specifies names of entries in the
+ organizational DIT associated with a DNS domain [RFC1034][RFC2181].
+
+ ( 0.9.2342.19200300.100.1.38 NAME 'associatedName'
+ EQUALITY distinguishedNameMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+
+ The DistinguishedName (1.3.6.1.4.1.1466.115.121.1.12) syntax and the
+ 'distinguishedNameMatch' rule are described in [RFC4517].
+
+2.3. buildingName
+
+ The 'buildingName' attribute specifies names of the buildings where
+ an organization or organizational unit is based, for example, "The
+ White House".
+
+ ( 0.9.2342.19200300.100.1.48 NAME 'buildingName'
+ EQUALITY caseIgnoreMatch
+ SUBSTR caseIgnoreSubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
+
+ The DirectoryString (1.3.6.1.4.1.1466.115.121.1.15) syntax and the
+ 'caseIgnoreMatch' and 'caseIgnoreSubstringsMatch' rules are described
+ in [RFC4517].
+
+2.4. co
+
+ The 'co' (Friendly Country Name) attribute specifies names of
+ countries in human-readable format, for example, "Germany" and
+ "Federal Republic of Germany". It is commonly used in conjunction
+ with the 'c' (Country Name) [RFC4519] attribute (whose values are
+ restricted to the two-letter codes defined in [ISO3166]).
+
+
+
+
+Zeilenga Standards Track [Page 5]
+
+RFC 4524 COSINE LDAP/X.500 Schema June 2006
+
+
+ ( 0.9.2342.19200300.100.1.43 NAME 'co'
+ EQUALITY caseIgnoreMatch
+ SUBSTR caseIgnoreSubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+
+ The DirectoryString (1.3.6.1.4.1.1466.115.121.1.15) syntax and the
+ 'caseIgnoreMatch' and 'caseIgnoreSubstringsMatch' rules are described
+ in [RFC4517].
+
+2.5. documentAuthor
+
+ The 'documentAuthor' attribute specifies the distinguished names of
+ authors (or editors) of a document. For example,
+
+ ( 0.9.2342.19200300.100.1.14 NAME 'documentAuthor'
+ EQUALITY distinguishedNameMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+
+ The DistinguishedName (1.3.6.1.4.1.1466.115.121.1.12) syntax and the
+ 'distinguishedNameMatch' rule are described in [RFC4517].
+
+2.6. documentIdentifier
+
+ The 'documentIdentifier' attribute specifies unique identifiers for a
+ document. A document may be identified by more than one unique
+ identifier. For example, RFC 3383 and BCP 64 are unique identifiers
+ that (presently) refer to the same document.
+
+ ( 0.9.2342.19200300.100.1.11 NAME 'documentIdentifier'
+ EQUALITY caseIgnoreMatch
+ SUBSTR caseIgnoreSubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
+
+ The DirectoryString (1.3.6.1.4.1.1466.115.121.1.15) syntax and the
+ 'caseIgnoreMatch' and 'caseIgnoreSubstringsMatch' rules are described
+ in [RFC4517].
+
+2.7. documentLocation
+
+ The 'documentLocation' attribute specifies locations of the document
+ original.
+
+ ( 0.9.2342.19200300.100.1.15 NAME 'documentLocation'
+ EQUALITY caseIgnoreMatch
+ SUBSTR caseIgnoreSubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
+
+
+
+
+
+Zeilenga Standards Track [Page 6]
+
+RFC 4524 COSINE LDAP/X.500 Schema June 2006
+
+
+ The DirectoryString (1.3.6.1.4.1.1466.115.121.1.15) syntax and the
+ 'caseIgnoreMatch' and 'caseIgnoreSubstringsMatch' rules are described
+ in [RFC4517].
+
+2.8. documentPublisher
+
+ The 'documentPublisher' attribute is the persons and/or organizations
+ that published the document. Documents that are jointly published
+ have one value for each publisher.
+
+ ( 0.9.2342.19200300.100.1.56 NAME 'documentPublisher'
+ EQUALITY caseIgnoreMatch
+ SUBSTR caseIgnoreSubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+
+ The DirectoryString (1.3.6.1.4.1.1466.115.121.1.15) syntax and the
+ 'caseIgnoreMatch' and 'caseIgnoreSubstringsMatch' rules are described
+ in [RFC4517].
+
+2.9. documentTitle
+
+ The 'documentTitle' attribute specifies the titles of a document.
+ Multiple values are allowed to accommodate both long and short
+ titles, or other situations where a document has multiple titles, for
+ example, "The Lightweight Directory Access Protocol Technical
+ Specification" and "The LDAP Technical Specification".
+
+ ( 0.9.2342.19200300.100.1.12 NAME 'documentTitle'
+ EQUALITY caseIgnoreMatch
+ SUBSTR caseIgnoreSubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
+
+ The DirectoryString (1.3.6.1.4.1.1466.115.121.1.15) syntax and the
+ 'caseIgnoreMatch' and 'caseIgnoreSubstringsMatch' rules are described
+ in [RFC4517].
+
+2.10. documentVersion
+
+ The 'documentVersion' attribute specifies the version information of
+ a document.
+
+ ( 0.9.2342.19200300.100.1.13 NAME 'documentVersion'
+ EQUALITY caseIgnoreMatch
+ SUBSTR caseIgnoreSubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
+
+
+
+
+
+
+Zeilenga Standards Track [Page 7]
+
+RFC 4524 COSINE LDAP/X.500 Schema June 2006
+
+
+ The DirectoryString (1.3.6.1.4.1.1466.115.121.1.15) syntax and the
+ 'caseIgnoreMatch' and 'caseIgnoreSubstringsMatch' rules are described
+ in [RFC4517].
+
+2.11. drink
+
+ The 'drink' (favoriteDrink) attribute specifies the favorite drinks
+ of an object (or person), for instance, "cola" and "beer".
+
+ ( 0.9.2342.19200300.100.1.5 NAME 'drink'
+ EQUALITY caseIgnoreMatch
+ SUBSTR caseIgnoreSubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
+
+ The DirectoryString (1.3.6.1.4.1.1466.115.121.1.15) syntax and the
+ 'caseIgnoreMatch' and 'caseIgnoreSubstringsMatch' rules are described
+ in [RFC4517].
+
+2.12. homePhone
+
+ The 'homePhone' (Home Telephone Number) attribute specifies home
+ telephone numbers (e.g., "+1 775 555 1234") associated with a person.
+
+ ( 0.9.2342.19200300.100.1.20 NAME 'homePhone'
+ EQUALITY telephoneNumberMatch
+ SUBSTR telephoneNumberSubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.50 )
+
+ The telephoneNumber (1.3.6.1.4.1.1466.115.121.1.50) syntax and the
+ 'telephoneNumberMatch' and 'telephoneNumberSubstringsMatch' rules are
+ described in [RFC4517].
+
+2.13. homePostalAddress
+
+ The 'homePostalAddress' attribute specifies home postal addresses for
+ an object. Each value should be limited to up to 6 directory strings
+ of 30 characters each. (Note: It is not intended that the directory
+ service enforce these limits.)
+
+ ( 0.9.2342.19200300.100.1.39 NAME 'homePostalAddress'
+ EQUALITY caseIgnoreListMatch
+ SUBSTR caseIgnoreListSubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.41 )
+
+ The PostalAddress (1.3.6.1.4.1.1466.115.121.1.41) syntax and the
+ 'caseIgnoreListMatch' and 'caseIgnoreListSubstringsMatch' rules are
+ described in [RFC4517].
+
+
+
+
+Zeilenga Standards Track [Page 8]
+
+RFC 4524 COSINE LDAP/X.500 Schema June 2006
+
+
+2.14. host
+
+ The 'host' attribute specifies host computers, generally by their
+ primary fully qualified domain name (e.g., my-host.example.com).
+
+ ( 0.9.2342.19200300.100.1.9 NAME 'host'
+ EQUALITY caseIgnoreMatch
+ SUBSTR caseIgnoreSubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
+
+ The DirectoryString (1.3.6.1.4.1.1466.115.121.1.15) syntax and the
+ 'caseIgnoreMatch' and 'caseIgnoreSubstringsMatch' rules are described
+ in [RFC4517].
+
+2.15. info
+
+ The 'info' attribute specifies any general information pertinent to
+ an object. This information is not necessarily descriptive of the
+ object.
+
+ Applications should not attach specific semantics to values of this
+ attribute. The 'description' attribute [RFC4519] is available for
+ specifying descriptive information pertinent to an object.
+
+ ( 0.9.2342.19200300.100.1.4 NAME 'info'
+ EQUALITY caseIgnoreMatch
+ SUBSTR caseIgnoreSubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{2048} )
+
+ The DirectoryString (1.3.6.1.4.1.1466.115.121.1.15) syntax and the
+ 'caseIgnoreMatch' and 'caseIgnoreSubstringsMatch' rules are described
+ in [RFC4517].
+
+2.16. mail
+
+ The 'mail' (rfc822mailbox) attribute type holds Internet mail
+ addresses in Mailbox [RFC2821] form (e.g., user@example.com).
+
+ ( 0.9.2342.19200300.100.1.3 NAME 'mail'
+ EQUALITY caseIgnoreIA5Match
+ SUBSTR caseIgnoreIA5SubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} )
+
+ The IA5String (1.3.6.1.4.1.1466.115.121.1.26) syntax and the
+ 'caseIgnoreIA5Match' and 'caseIgnoreIA5SubstringsMatch' rules are
+ described in [RFC4517].
+
+
+
+
+
+Zeilenga Standards Track [Page 9]
+
+RFC 4524 COSINE LDAP/X.500 Schema June 2006
+
+
+ Note that the directory will not ensure that values of this attribute
+ conform to the <Mailbox> production [RFC2821]. It is the
+ application's responsibility to ensure that domains it stores in this
+ attribute are appropriately represented.
+
+ Additionally, the directory will compare values per the matching
+ rules named in the above attribute type description. As these rules
+ differ from rules that normally apply to <Mailbox> comparisons,
+ operational issues may arise. For example, the assertion
+ (mail=joe@example.com) will match "JOE@example.com" even though the
+ <local-parts> differ. Also, where a user has two <Mailbox>es whose
+ addresses differ only by case of the <local-part>, both cannot be
+ listed as values of the user's mail attribute (as they are considered
+ equal by the 'caseIgnoreIA5Match' rule).
+
+ Also note that applications supporting internationalized domain names
+ SHALL use the ToASCII method [RFC3490] to produce <sub-domain>
+ components of the <Mailbox> production.
+
+2.17. manager
+
+ The 'manager' attribute specifies managers, by distinguished name, of
+ the person (or entity).
+
+ ( 0.9.2342.19200300.100.1.10 NAME 'manager'
+ EQUALITY distinguishedNameMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+
+ The DistinguishedName (1.3.6.1.4.1.1466.115.121.1.12) syntax and the
+ 'distinguishedNameMatch' rule are described in [RFC4517].
+
+2.18. mobile
+
+ The 'mobile' (mobileTelephoneNumber) attribute specifies mobile
+ telephone numbers (e.g., "+1 775 555 6789") associated with a person
+ (or entity).
+
+ ( 0.9.2342.19200300.100.1.41 NAME 'mobile'
+ EQUALITY telephoneNumberMatch
+ SUBSTR telephoneNumberSubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.50 )
+
+ The telephoneNumber (1.3.6.1.4.1.1466.115.121.1.50) syntax and the
+ 'telephoneNumberMatch' and 'telephoneNumberSubstringsMatch' rules are
+ described in [RFC4517].
+
+
+
+
+
+
+Zeilenga Standards Track [Page 10]
+
+RFC 4524 COSINE LDAP/X.500 Schema June 2006
+
+
+2.19. organizationalStatus
+
+ The 'organizationalStatus' attribute specifies categories by which a
+ person is often referred to in an organization. Examples of usage in
+ academia might include "undergraduate student", "researcher",
+ "professor", and "staff". Multiple values are allowed where the
+ person is in multiple categories.
+
+ Directory administrators and application designers SHOULD consider
+ carefully the distinctions between this and the 'title' and
+ 'userClass' attributes.
+
+ ( 0.9.2342.19200300.100.1.45 NAME 'organizationalStatus'
+ EQUALITY caseIgnoreMatch
+ SUBSTR caseIgnoreSubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
+
+ The DirectoryString (1.3.6.1.4.1.1466.115.121.1.15) syntax and the
+ 'caseIgnoreMatch' and 'caseIgnoreSubstringsMatch' rules are described
+ in [RFC4517].
+
+2.20. pager
+
+ The 'pager' (pagerTelephoneNumber) attribute specifies pager
+ telephone numbers (e.g., "+1 775 555 5555") for an object.
+
+ ( 0.9.2342.19200300.100.1.42 NAME 'pager'
+ EQUALITY telephoneNumberMatch
+ SUBSTR telephoneNumberSubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.50 )
+
+ The telephoneNumber (1.3.6.1.4.1.1466.115.121.1.50) syntax and the
+ 'telephoneNumberMatch' and 'telephoneNumberSubstringsMatch' rules are
+ described in [RFC4517].
+
+2.21. personalTitle
+
+ The 'personalTitle' attribute specifies personal titles for a person.
+ Examples of personal titles are "Frau", "Dr.", "Herr", and
+ "Professor".
+
+ ( 0.9.2342.19200300.100.1.40 NAME 'personalTitle'
+ EQUALITY caseIgnoreMatch
+ SUBSTR caseIgnoreSubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
+
+
+
+
+
+
+Zeilenga Standards Track [Page 11]
+
+RFC 4524 COSINE LDAP/X.500 Schema June 2006
+
+
+ The DirectoryString (1.3.6.1.4.1.1466.115.121.1.15) syntax and the
+ 'caseIgnoreMatch' and 'caseIgnoreSubstringsMatch' rules are described
+ in [RFC4517].
+
+2.22. roomNumber
+
+ The 'roomNumber' attribute specifies the room number of an object.
+ During periods of renumbering, or in other circumstances where a room
+ has multiple valid room numbers associated with it, multiple values
+ may be provided. Note that the 'cn' (commonName) attribute type
+ SHOULD be used for naming room objects.
+
+ ( 0.9.2342.19200300.100.1.6 NAME 'roomNumber'
+ EQUALITY caseIgnoreMatch
+ SUBSTR caseIgnoreSubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
+
+ The DirectoryString (1.3.6.1.4.1.1466.115.121.1.15) syntax and the
+ 'caseIgnoreMatch' and 'caseIgnoreSubstringsMatch' rules are described
+ in [RFC4517].
+
+2.23. secretary
+
+ The 'secretary' attribute specifies secretaries and/or administrative
+ assistants, by distinguished name.
+
+ ( 0.9.2342.19200300.100.1.21 NAME 'secretary'
+ EQUALITY distinguishedNameMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+
+ The DistinguishedName (1.3.6.1.4.1.1466.115.121.1.12) syntax and the
+ 'distinguishedNameMatch' rule are described in [RFC4517].
+
+2.24. uniqueIdentifier
+
+ The 'uniqueIdentifier' attribute specifies a unique identifier for an
+ object represented in the Directory. The domain within which the
+ identifier is unique and the exact semantics of the identifier are
+ for local definition. For a person, this might be an institution-
+ wide payroll number. For an organizational unit, it might be a
+ department code.
+
+ ( 0.9.2342.19200300.100.1.44 NAME 'uniqueIdentifier'
+ EQUALITY caseIgnoreMatch
+ SUBSTR caseIgnoreSubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
+
+
+
+
+
+Zeilenga Standards Track [Page 12]
+
+RFC 4524 COSINE LDAP/X.500 Schema June 2006
+
+
+ The DirectoryString (1.3.6.1.4.1.1466.115.121.1.15) syntax and the
+ 'caseIgnoreMatch' and 'caseIgnoreSubstringsMatch' rules are described
+ in [RFC4517].
+
+ Note: X.520 also describes an attribute called 'uniqueIdentifier'
+ (2.5.4.45), which is called 'x500UniqueIdentifier' in LDAP
+ [RFC4519]. The attribute detailed here ought not be confused
+ with 'x500UniqueIdentifier'.
+
+2.25. userClass
+
+ The 'userClass' attribute specifies categories of computer or
+ application user. The semantics placed on this attribute are for
+ local interpretation. Examples of current usage of this attribute in
+ academia are "student", "staff", and "faculty". Note that the
+ 'organizationalStatus' attribute type is now often preferred, as it
+ makes no distinction between persons as opposed to users.
+
+ ( 0.9.2342.19200300.100.1.8 NAME 'userClass'
+ EQUALITY caseIgnoreMatch
+ SUBSTR caseIgnoreSubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
+
+ The DirectoryString (1.3.6.1.4.1.1466.115.121.1.15) syntax and the
+ 'caseIgnoreMatch' and 'caseIgnoreSubstringsMatch' rules are described
+ in [RFC4517].
+
+3. COSINE Object Classes
+
+ This section details COSINE object classes for use in LDAP.
+
+3.1. account
+
+ The 'account' object class is used to define entries representing
+ computer accounts. The 'uid' attribute SHOULD be used for naming
+ entries of this object class.
+
+ ( 0.9.2342.19200300.100.4.5 NAME 'account'
+ SUP top STRUCTURAL
+ MUST uid
+ MAY ( description $ seeAlso $ l $ o $ ou $ host ) )
+
+ The 'top' object class is described in [RFC4512]. The 'description',
+ 'seeAlso', 'l', 'o', 'ou', and 'uid' attribute types are described in
+ [RFC4519]. The 'host' attribute type is described in Section 2 of
+ this document.
+
+
+
+
+
+Zeilenga Standards Track [Page 13]
+
+RFC 4524 COSINE LDAP/X.500 Schema June 2006
+
+
+ 3.3. documentSeriesExample:
+
+ dn: uid=kdz,cn=Accounts,dc=Example,dc=COM
+ objectClass: account
+ uid: kdz
+ seeAlso: cn=Kurt D. Zeilenga,cn=Persons,dc=Example,dc=COM
+
+3.2. document
+
+ The 'document' object class is used to define entries that represent
+ documents.
+
+ ( 0.9.2342.19200300.100.4.6 NAME 'document'
+ SUP top STRUCTURAL
+ MUST documentIdentifier
+ MAY ( cn $ description $ seeAlso $ l $ o $ ou $
+ documentTitle $ documentVersion $ documentAuthor $
+ documentLocation $ documentPublisher ) )
+
+ The 'top' object class is described in [RFC4512]. The 'cn',
+ 'description', 'seeAlso', 'l', 'o', and 'ou' attribute types are
+ described in [RFC4519]. The 'documentIdentifier', 'documentTitle',
+ 'documentVersion', 'documentAuthor', 'documentLocation', and
+ 'documentPublisher' attribute types are described in Section 2 of
+ this document.
+
+ Example:
+
+ dn: documentIdentifier=RFC 4524,cn=RFC,dc=Example,dc=COM
+ objectClass: document
+ documentIdentifier: RFC 4524
+ documentTitle: COSINE LDAP/X.500 Schema
+ documentAuthor: cn=Kurt D. Zeilenga,cn=Persons,dc=Example,dc=COM
+ documentLocation: http://www.rfc-editor.org/rfc/rfc4524.txt
+ documentPublisher: Internet Engineering Task Force
+ description: A collection of schema elements for use in LDAP
+ description: Obsoletes RFC 1274
+ seeAlso: documentIdentifier=RFC 4510,cn=RFC,dc=Example,dc=COM
+ seeAlso: documentIdentifier=RFC 1274,cn=RFC,dc=Example,dc=COM
+
+3.3. documentSeries
+
+ The 'documentSeries' object class is used to define an entry that
+ represents a series of documents (e.g., The Request For Comments
+ memos).
+
+
+
+
+
+
+Zeilenga Standards Track [Page 14]
+
+RFC 4524 COSINE LDAP/X.500 Schema June 2006
+
+
+ ( 0.9.2342.19200300.100.4.9 NAME 'documentSeries'
+ SUP top STRUCTURAL
+ MUST cn
+ MAY ( description $ l $ o $ ou $ seeAlso $
+ telephonenumber ) )
+
+ The 'top' object class is described in [RFC4512]. The 'description',
+ 'l', 'o', 'ou', 'seeAlso', and 'telephoneNumber' attribute types are
+ described in [RFC4519].
+
+ Example:
+
+ dn: cn=RFC,dc=Example,dc=COM
+ objectClass: documentSeries
+ cn: Request for Comments
+ cn: RFC
+ description: a series of memos about the Internet
+
+3.4. domain
+
+ The 'domain' object class is used to define entries that represent
+ DNS domains for objects that are not organizations, organizational
+ units, or other kinds of objects more appropriately defined using an
+ object class specific to the kind of object being defined (e.g.,
+ 'organization', 'organizationUnit').
+
+ The 'dc' attribute should be used for naming entries of the 'domain'
+ object class.
+
+ ( 0.9.2342.19200300.100.4.13 NAME 'domain'
+ SUP top STRUCTURAL
+ MUST dc
+ MAY ( userPassword $ searchGuide $ seeAlso $ businessCategory $
+ x121Address $ registeredAddress $ destinationIndicator $
+ preferredDeliveryMethod $ telexNumber $
+ teletexTerminalIdentifier $ telephoneNumber $
+ internationaliSDNNumber $ facsimileTelephoneNumber $ street $
+ postOfficeBox $ postalCode $ postalAddress $
+ physicalDeliveryOfficeName $ st $ l $ description $ o $
+ associatedName ) )
+
+ The 'top' object class and the 'dc', 'userPassword', 'searchGuide',
+ 'seeAlso', 'businessCategory', 'x121Address', 'registeredAddress',
+ 'destinationIndicator', 'preferredDeliveryMethod', 'telexNumber',
+ 'teletexTerminalIdentifier', 'telephoneNumber',
+ 'internationaliSDNNumber', 'facsimileTelephoneNumber', 'street',
+ 'postOfficeBox', 'postalCode', 'postalAddress',
+ 'physicalDeliveryOfficeName', 'st', 'l', 'description', and 'o' types
+
+
+
+Zeilenga Standards Track [Page 15]
+
+RFC 4524 COSINE LDAP/X.500 Schema June 2006
+
+
+ are described in [RFC4519]. The 'associatedName' attribute type is
+ described in Section 2 of this document.
+
+ Example:
+
+ dn: dc=com
+ objectClass: domain
+ dc: com
+ description: the .COM TLD
+
+3.5. domainRelatedObject
+
+ The 'domainRelatedObject' object class is used to define entries that
+ represent DNS domains that are "equivalent" to an X.500 domain, e.g.,
+ an organization or organizational unit.
+
+ ( 0.9.2342.19200300.100.4.17 NAME 'domainRelatedObject'
+ SUP top AUXILIARY
+ MUST associatedDomain )
+
+ The 'top' object class is described in [RFC4512]. The
+ 'associatedDomain' attribute type is described in Section 2 of this
+ document.
+
+ Example:
+
+ dn: dc=example,dc=com
+ objectClass: organization
+ objectClass: dcObject
+ objectClass: domainRelatedObject
+ dc: example
+ associatedDomain: example.com
+ o: Example Organization
+
+ The 'organization' and 'dcObject' object classes and the 'dc' and 'o'
+ attribute types are described in [RFC4519].
+
+3.6. friendlyCountry
+
+ The 'friendlyCountry' object class is used to define entries
+ representing countries in the DIT. The object class is used to allow
+ friendlier naming of countries than that allowed by the object class
+ 'country' [RFC4519].
+
+ ( 0.9.2342.19200300.100.4.18 NAME 'friendlyCountry'
+ SUP country STRUCTURAL
+ MUST co )
+
+
+
+
+Zeilenga Standards Track [Page 16]
+
+RFC 4524 COSINE LDAP/X.500 Schema June 2006
+
+
+ The 'country' object class is described in [RFC4519]. The 'co'
+ attribute type is described in Section 2 of this document.
+
+ Example:
+
+ dn: c=DE
+ objectClass: country
+ objectClass: friendlyCountry
+ c: DE
+ co: Deutschland
+ co: Germany
+ co: Federal Republic of Germany
+ co: FRG
+
+ The 'c' attribute type is described in [RFC4519].
+
+3.7. rFC822LocalPart
+
+ The 'rFC822LocalPart' object class is used to define entries that
+ represent the local part of Internet mail addresses [RFC2822]. This
+ treats the local part of the address as a 'domain' object.
+
+ ( 0.9.2342.19200300.100.4.14 NAME 'rFC822localPart'
+ SUP domain STRUCTURAL
+ MAY ( cn $ description $ destinationIndicator $
+ facsimileTelephoneNumber $ internationaliSDNNumber $
+ physicalDeliveryOfficeName $ postalAddress $ postalCode $
+ postOfficeBox $ preferredDeliveryMethod $ registeredAddress $
+ seeAlso $ sn $ street $ telephoneNumber $
+ teletexTerminalIdentifier $ telexNumber $ x121Address ) )
+
+ The 'domain' object class is described in Section 3.4 of this
+ document. The 'cn', 'description', 'destinationIndicator',
+ 'facsimileTelephoneNumber', 'internationaliSDNNumber,
+ 'physicalDeliveryOfficeName', 'postalAddress', 'postalCode',
+ 'postOfficeBox', 'preferredDeliveryMethod', 'registeredAddress',
+ 'seeAlso', 'sn, 'street', 'telephoneNumber',
+ 'teletexTerminalIdentifier', 'telexNumber', and 'x121Address'
+ attribute types are described in [RFC4519].
+
+ Example:
+
+ dn: dc=kdz,dc=example,dc=com
+ objectClass: domain
+ objectClass: rFC822LocalPart
+ dc: kdz
+ associatedName: cn=Kurt D. Zeilenga,cn=Persons,dc=Example,dc=COM
+
+
+
+
+Zeilenga Standards Track [Page 17]
+
+RFC 4524 COSINE LDAP/X.500 Schema June 2006
+
+
+ The 'dc' attribute type is described in [RFC4519].
+
+3.8. room
+
+ The 'room' object class is used to define entries representing rooms.
+ The 'cn' (commonName) attribute SHOULD be used for naming entries of
+ this object class.
+
+ ( 0.9.2342.19200300.100.4.7 NAME 'room'
+ SUP top STRUCTURAL
+ MUST cn
+ MAY ( roomNumber $ description $ seeAlso $ telephoneNumber ) )
+
+ The 'top' object class is described in [RFC4512]. The 'cn',
+ 'description', 'seeAlso', and 'telephoneNumber' attribute types are
+ described in [RFC4519]. The 'roomNumber' attribute type is described
+ in Section 2 of this document.
+
+ dn: cn=conference room,dc=example,dc=com
+ objectClass: room
+ cn: conference room
+ telephoneNumber: +1 755 555 1111
+
+3.9. simpleSecurityObject
+
+ The 'simpleSecurityObject' object class is used to require an entry
+ to have a 'userPassword' attribute when the entry's structural object
+ class does not require (or allow) the 'userPassword attribute'.
+
+ ( 0.9.2342.19200300.100.4.19 NAME 'simpleSecurityObject'
+ SUP top AUXILIARY
+ MUST userPassword )
+
+ The 'top' object class is described in [RFC4512]. The 'userPassword'
+ attribute type is described in [RFC4519].
+
+ dn: dc=kdz,dc=Example,dc=COM
+ objectClass: account
+ objectClass: simpleSecurityObject
+ uid: kdz
+ userPassword: My Password
+ seeAlso: cn=Kurt D. Zeilenga,cn=Persons,dc=Example,dc=COM
+
+4. Security Considerations
+
+ General LDAP security considerations [RFC4510] are applicable to the
+ use of this schema. Additional considerations are noted above where
+ appropriate.
+
+
+
+Zeilenga Standards Track [Page 18]
+
+RFC 4524 COSINE LDAP/X.500 Schema June 2006
+
+
+ Directories administrators should ensure that access to sensitive
+ information be restricted to authorized entities and that appropriate
+ data security services, including data integrity and data
+ confidentiality, are used to protect against eavesdropping.
+
+ Simple authentication (e.g., plain text passwords) mechanisms should
+ only be used when adequate data security services are in place. LDAP
+ offers reasonably strong authentication and data security services
+ [RFC4513].
+
+5. IANA Considerations
+
+ The Internet Assigned Numbers Authority (IANA) has updated the LDAP
+ descriptors registry [RFC4520] as indicated in the following
+ template:
+
+ Subject: Request for LDAP Descriptor Registration Update
+ Descriptor (short name): see comment
+ Object Identifier: see comments
+ Person & email address to contact for further information:
+ Kurt Zeilenga <kurt@OpenLDAP.org>
+ Usage: see comments
+ Specification: RFC 4524
+ Author/Change Controller: IESG
+ Comments:
+
+ The following descriptors have been updated to refer to RFC 4524.
+
+ NAME Type OID
+ ------------------------ ---- --------------------------
+ account O 0.9.2342.19200300.100.4.5
+ associatedDomain A 0.9.2342.19200300.100.1.37
+ associatedName A 0.9.2342.19200300.100.1.38
+ buildingName A 0.9.2342.19200300.100.1.48
+ co A 0.9.2342.19200300.100.1.43
+ document O 0.9.2342.19200300.100.4.6
+ documentAuthor A 0.9.2342.19200300.100.1.14
+ documentIdentifier A 0.9.2342.19200300.100.1.11
+ documentLocation A 0.9.2342.19200300.100.1.15
+ documentPublisher A 0.9.2342.19200300.100.1.56
+ documentSeries O 0.9.2342.19200300.100.4.8
+ documentTitle A 0.9.2342.19200300.100.1.12
+ documentVersion A 0.9.2342.19200300.100.1.13
+ domain O 0.9.2342.19200300.100.4.13
+ domainRelatedObject O 0.9.2342.19200300.100.4.17
+ drink A 0.9.2342.19200300.100.1.5
+ favouriteDrink A* 0.9.2342.19200300.100.1.5
+ friendlyCountry O 0.9.2342.19200300.100.4.18
+
+
+
+Zeilenga Standards Track [Page 19]
+
+RFC 4524 COSINE LDAP/X.500 Schema June 2006
+
+
+ friendlyCountryName A* 0.9.2342.19200300.100.1.43
+ homePhone A 0.9.2342.19200300.100.1.20
+ homePostalAddress A 0.9.2342.19200300.100.1.39
+ homeTelephone A* 0.9.2342.19200300.100.1.20
+ host A 0.9.2342.19200300.100.1.9
+ info A 0.9.2342.19200300.100.1.4
+ mail A 0.9.2342.19200300.100.1.3
+ manager A 0.9.2342.19200300.100.1.10
+ mobile A 0.9.2342.19200300.100.1.41
+ mobileTelephoneNumber A* 0.9.2342.19200300.100.1.41
+ organizationalStatus A 0.9.2342.19200300.100.1.45
+ pager A 0.9.2342.19200300.100.1.42
+ pagerTelephoneNumber A* 0.9.2342.19200300.100.1.42
+ personalTitle A 0.9.2342.19200300.100.1.40
+ rFC822LocalPart O 0.9.2342.19200300.100.4.14
+ rfc822Mailbox A* 0.9.2342.19200300.100.1.3
+ room O 0.9.2342.19200300.100.4.7
+ roomNumber A 0.9.2342.19200300.100.1.6
+ secretary A 0.9.2342.19200300.100.1.21
+ simpleSecurityObject O 0.9.2342.19200300.100.4.19
+ singleLevelQuality A 0.9.2342.19200300.100.1.50
+ uniqueIdentifier A 0.9.2342.19200300.100.1.44
+ userClass A 0.9.2342.19200300.100.1.8
+
+ where Type A is Attribute, Type O is ObjectClass, and *
+ indicates that the registration is historic in nature.
+
+6. Acknowledgements
+
+ This document is based on RFC 1274, by Paul Barker and Steve Kille,
+ as well as on RFC 2247, by Steve Kill, Mark Wahl, Al Grimstad, Rick
+ Huber, and Sri Satulari.
+
+7. References
+
+7.1. Normative References
+
+ [RFC1034] Mockapetris, P., "Domain names - concepts and
+ facilities", STD 13, RFC 1034, November 1987.
+
+ [RFC1123] Braden, R., "Requirements for Internet Hosts -
+ Application and Support", STD 3, RFC 1123, October
+ 1989.
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+
+
+
+
+Zeilenga Standards Track [Page 20]
+
+RFC 4524 COSINE LDAP/X.500 Schema June 2006
+
+
+ [RFC2181] Elz, R. and R. Bush, "Clarifications to the DNS
+ Specification", RFC 2181, July 1997.
+
+ [RFC2247] Kille, S., Wahl, M., Grimstad, A., Huber, R., and S.
+ Sataluri, "Using Domains in LDAP/X.500 Distinguished
+ Names", RFC 2247, January 1998.
+
+ [RFC2821] Klensin, J., Ed., "Simple Mail Transfer Protocol", RFC
+ 2821, April 2001.
+
+ [RFC2822] Resnick, P., "Internet Message Format", RFC 2822, April
+ 2001.
+
+ [RFC3490] Faltstrom, P., Hoffman, P., and A. Costello,
+ "Internationalizing Domain Names in Applications
+ (IDNA)", RFC 3490, March 2003.
+
+ [RFC4510] Zeilenga, K., Ed., "Lightweight Directory Access
+ Protocol (LDAP): Technical Specification Road Map", RFC
+ 4510, June 2006.
+
+ [RFC4512] Zeilenga, K., "Lightweight Directory Access Protocol
+ (LDAP): Directory Information Models", RFC 4512, June
+ 2006.
+
+ [RFC4513] Harrison, R., "Lightweight Directory Access Protocol
+ (LDAP): Authentication Methods and Security
+ Mechanisms", RFC 4513, June 2006.
+
+ [RFC4517] Legg, S., Ed., "Lightweight Directory Access Protocol
+ (LDAP): Syntaxes and Matching Rules", RC 4517, June
+ 2006.
+
+ [RFC4519] Sciberras, A., Ed., "Lightweight Directory Access
+ Protocol (LDAP): Schema for User Applications", RFC
+ 4519, June 2006.
+
+ [X.501] International Telecommunication Union -
+ Telecommunication Standardization Sector, "The
+ Directory -- Models," X.501(1993) (also ISO/IEC 9594-
+ 2:1994).
+
+7.2. Informative References
+
+ [COSINEpilot] Goodman, D., "PARADISE" section of the March 1991
+ INTERNET MONTHLY REPORTS (p. 28-29),
+ http://www.iana.org/periodic-reports/imr-mar91.txt
+
+
+
+
+Zeilenga Standards Track [Page 21]
+
+RFC 4524 COSINE LDAP/X.500 Schema June 2006
+
+
+ [ISO3166] International Organization for Standardization, "Codes
+ for the representation of names of countries", ISO
+ 3166.
+
+ [RFC1274] Barker, P. and S. Kille, "The COSINE and Internet X.500
+ Schema", RFC 1274, November 1991.
+
+ [RFC1279] Hardcastle-Kille, S., "X.500 and Domains", RFC 1279,
+ November 1991.
+
+ [RFC1487] Yeong, W., Howes, T., and S. Kille, "X.500 Lightweight
+ Directory Access Protocol", RFC 1487, July 1993.
+
+ [RFC2251] Wahl, M., Howes, T., and S. Kille, "Lightweight
+ Directory Access Protocol (v3)", RFC 2251, December
+ 1997.
+
+ [RFC2798] Smith, M., "Definition of the inetOrgPerson LDAP Object
+ Class", RFC 2798, April 2000.
+
+ [RFC3494] Zeilenga, K., "Lightweight Directory Access Protocol
+ version 2 (LDAPv2) to Historic Status", RFC 3494, March
+ 2003.
+
+ [RFC4520] Zeilenga, K., "Internet Assigned Numbers Authority
+ (IANA) Considerations for the Lightweight Directory
+ Access Protocol (LDAP)", BCP 64, RFC 4520.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 22]
+
+RFC 4524 COSINE LDAP/X.500 Schema June 2006
+
+
+Appendix A. Changes since RFC 1274
+
+ This document represents a substantial rewrite of RFC 1274. The
+ following sections summarize the substantive changes.
+
+A.1. LDAP Short Names
+
+ A number of COSINE attribute types have short names in LDAP.
+
+ X.500 Name LDAP Short Name
+ ------------- ---------------
+ domainComponent dc
+ favoriteDrink drink
+ friendCountryName co
+ homeTelephoneNumber homePhone
+ mobileTelephoneNumber mobile
+ pagerTelephoneNumber pager
+ rfc822Mailbox mail
+ userid uid
+
+ While the LDAP short names are generally used in LDAP, some
+ implementations may (for legacy reasons [RFC3494]) recognize the
+ attribute type by its X.500 name. Hence, the X.500 names have been
+ reserved solely for this purpose.
+
+ Note: 'uid' and 'dc' are described in [RFC4519].
+
+A.2. pilotObject
+
+ The 'pilotObject' object class was not brought forward as its
+ function is largely replaced by operational attributes introduced in
+ X.500(93) [X.501] and version 3 of LDAP [RFC4512]. For instance, the
+ function of the 'lastModifiedBy' and 'lastModifiedTime' attribute
+ types is now served by the 'creatorsName', 'createTimestamp',
+ 'modifiersName', and 'modifyTimestamp' operational attributes
+ [RFC4512].
+
+A.3. pilotPerson
+
+ The 'pilotPerson' object class was not brought forward as its
+ function is largely replaced by the 'organizationalPerson' [RFC4512]
+ object class and its subclasses, such as 'inetOrgPerson' [RFC2798].
+
+ Most of the related attribute types (e.g., 'mail', 'manager') were
+ brought forward as they are used in other object classes.
+
+
+
+
+
+
+Zeilenga Standards Track [Page 23]
+
+RFC 4524 COSINE LDAP/X.500 Schema June 2006
+
+
+A.4. dNSDomain
+
+ The 'dNSDomain' object class and related attribute types were not
+ brought forward as its use is primarily experimental [RFC1279].
+
+A.5. pilotDSA and qualityLabelledData
+
+ The 'pilotDSA' and 'qualityLabelledData' object classes, as well as
+ related attribute types, were not brought forward as its use is
+ primarily experimental [QoS].
+
+A.6. Attribute Syntaxes
+
+ RFC 1274 defined and used caseIgnoreIA5StringSyntax attribute syntax.
+ This has been replaced with the IA5String syntax and appropriate
+ matching rules in 'mail' and 'associatedDomain'.
+
+ RFC 1274 restricted 'mail' to have non-zero length values. This
+ restriction is not reflected in the IA5String syntax used in the
+ definitions provided in this specification. However, as values are
+ to conform to the <Mailbox> production, the 'mail' should not contain
+ zero-length values. Unfortunately, the directory service will not
+ enforce this restriction.
+
+Appendix B. Changes since RFC 2247
+
+ The 'domainNameForm' name form was not brought forward as
+ specification of name forms used in LDAP is left to a future
+ specification.
+
+Editor's Address
+
+ Kurt D. Zeilenga
+ OpenLDAP Foundation
+
+ EMail: Kurt@OpenLDAP.org
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 24]
+
+RFC 4524 COSINE LDAP/X.500 Schema June 2006
+
+
+Full Copyright Statement
+
+ Copyright (C) The Internet Society (2006).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
+ ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
+ INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
+ INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be
+ found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at
+ ietf-ipr@ietf.org.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is provided by the IETF
+ Administrative Support Activity (IASA).
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 25]
+
diff --git a/source4/ldap_server/devdocs/rfc4525.txt b/source4/ldap_server/devdocs/rfc4525.txt
new file mode 100644
index 0000000000..6e15e4f6e9
--- /dev/null
+++ b/source4/ldap_server/devdocs/rfc4525.txt
@@ -0,0 +1,339 @@
+
+
+
+
+
+
+Network Working Group K. Zeilenga
+Request for Comments: 4525 OpenLDAP Foundation
+Category: Informational June 2006
+
+
+ Lightweight Directory Access Protocol (LDAP)
+ Modify-Increment Extension
+
+
+Status of This Memo
+
+ This memo provides information for the Internet community. It does
+ not specify an Internet standard of any kind. Distribution of this
+ memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2006).
+
+Abstract
+
+ This document describes an extension to the Lightweight Directory
+ Access Protocol (LDAP) Modify operation to support an increment
+ capability. This extension is useful in provisioning applications,
+ especially when combined with the assertion control and/or the pre-
+ read or post-read control extension.
+
+Table of Contents
+
+ 1. Background and Intended Use .....................................1
+ 2. The Modify-Increment Extension ..................................2
+ 3. LDIF Support ....................................................2
+ 4. Security Considerations .........................................3
+ 5. IANA Considerations .............................................3
+ 5.1. Object Identifier ..........................................3
+ 5.2. LDAP Protocol Mechanism ....................................3
+ 5.3. LDAP Protocol Mechanism ....................................4
+ 6. References ......................................................4
+ 6.1. Normative References .......................................4
+ 6.2. Informative References .....................................5
+
+1. Background and Intended Use
+
+ The Lightweight Directory Access Protocol (LDAP) [RFC4510] does not
+ currently provide an operation to increment values of an attribute.
+ A client must read the values of the attribute and then modify those
+ values to increment them by the desired amount. As the values may be
+ updated by other clients between this add and modify, the client must
+
+
+
+Zeilenga Informational [Page 1]
+
+RFC 4525 LDAP Modify-Increment Extension June 2006
+
+
+ be careful to construct the modify request so that it fails in this
+ case, and upon failure, to re-read the values and construct a new
+ modify request.
+
+ This document extends the LDAP Modify Operation [RFC4511] to support
+ an increment values capability. This feature is intended to be used
+ with either the LDAP pre-read or post-read control extensions
+ [RFC4527]. This feature may also be used with the LDAP assertion
+ control extension [RFC4528] to provide test-and-increment
+ functionality.
+
+ In this document key words "MUST", "MUST NOT", "REQUIRED", "SHALL",
+ "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and
+ "OPTIONAL" are to be interpreted as described in BCP 14 [RFC2119].
+
+2. The Modify-Increment Extension
+
+ This document extends the LDAP Modify request to support a increment
+ values capability. Implementations of this extension SHALL support
+ an additional ModifyRequest operation enumeration value increment
+ (3), as described herein. Implementations not supporting this
+ extension will treat this value as they would an unlisted value,
+ e.g., as a protocol error.
+
+ The increment (3) operation value specifies that an increment values
+ modification is requested. All existing values of the modification
+ attribute are to be incremented by the listed value. The
+ modification attribute must be appropriate for the request (e.g., it
+ must have INTEGER or other increment-able values), and the
+ modification must provide one and only one value. If the attribute
+ is not appropriate for the request, a constraintViolation or other
+ appropriate error is to be returned. If multiple values are
+ provided, a protocolError is to be returned.
+
+ Servers supporting this feature SHOULD publish the object identifier
+ (OID) 1.3.6.1.1.14 as a value of the 'supportedFeatures' [RFC4512]
+ attribute in the root DSE. Clients supporting this feature SHOULD
+ NOT use the feature unless they know the server supports it.
+
+3. LDIF Support
+
+ To represent Modify-Increment requests in LDAP Data Interchange
+ Format [RFC2849], the ABNF [RFC4234] production <mod-spec> is
+ extended as follows:
+
+ mod-spec =/ "increment:" FILL AttributeDescription SEP
+ attrval-spec "-" SEP
+
+
+
+
+Zeilenga Informational [Page 2]
+
+RFC 4525 LDAP Modify-Increment Extension June 2006
+
+
+ For example,
+
+ # Increment uidNumber
+ dn: cn=max-assigned uidNumber,dc=example,dc=com
+ changetype: modify
+ increment: uidNumber
+ uidNumber: 1
+ -
+
+ This LDIF fragment represents a Modify request to increment the
+ value(s) of uidNumber by 1.
+
+4. Security Considerations
+
+ General LDAP security considerations [RFC4510], as well as those
+ specific to the LDAP Modify [RFC4511], apply to this Modify-Increment
+ extension. Beyond these considerations, it is noted that
+ introduction of this extension should reduce application complexity
+ (by providing one operation for what presently requires multiple
+ operations) and, hence, it may aid in the production of correct and
+ secure implementations.
+
+5. IANA Considerations
+
+ Registration of the following values [RFC4520] have been completed.
+
+5.1. Object Identifier
+
+ The IANA has assigned an LDAP Object Identifier to identify the LDAP
+ Modify-Increment feature, as defined in this document.
+
+ Subject: Request for LDAP Object Identifier Registration
+ Person & email address to contact for further information:
+ Kurt Zeilenga <kurt@OpenLDAP.org>
+ Specification: RFC 4525
+ Author/Change Controller: Author
+ Comments:
+ Identifies the LDAP Modify-Increment feature
+
+5.2. LDAP Protocol Mechanism
+
+ The following LDAP Protocol Mechanism has been registered.
+
+ Subject: Request for LDAP Protocol Mechanism Registration
+ Object Identifier: 1.3.6.1.1.14
+ Description: Modify-Increment
+ Person & email address to contact for further information:
+ Kurt Zeilenga <kurt@openldap.org>
+
+
+
+Zeilenga Informational [Page 3]
+
+RFC 4525 LDAP Modify-Increment Extension June 2006
+
+
+ Usage: Feature
+ Specification: RFC 4525
+ Author/Change Controller: Kurt Zeilenga <kurt@openldap.org>
+ Comments: none
+
+5.3. LDAP Protocol Mechanism
+
+ The IANA has assigned an LDAP ModifyRequest Operation Type (3)
+ [RFC4520] for use in this document.
+
+ Subject: Request for LDAP Protocol Mechanism Registration
+ ModifyRequest Operation Name: increment
+ Description: Modify-Increment
+ Person & email address to contact for further information:
+ Kurt Zeilenga <kurt@openldap.org>
+ Usage: Feature
+ Specification: RFC 4525
+ Author/Change Controller: Kurt Zeilenga <kurt@openldap.org>
+ Comments: none
+
+6. References
+
+6.1. Normative References
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [RFC4234] Crocker, D. and P. Overell, "Augmented BNF for Syntax
+ Specifications: ABNF", RFC 4234, October 2005.
+
+ [RFC2849] Good, G., "The LDAP Data Interchange Format (LDIF) -
+ Technical Specification", RFC 2849, June 2000.
+
+ [RFC4510] Zeilenga, K., Ed., "Lightweight Directory Access
+ Protocol (LDAP): Technical Specification Road Map", RFC
+ 4510, June 2006.
+
+ [RFC4511] Sermersheim, J., Ed., "Lightweight Directory Access
+ Protocol (LDAP): The Protocol", RFC 4511, June 2006.
+
+ [RFC4512] Zeilenga, K., "Lightweight Directory Access Protocol
+ (LDAP): Directory Information Models", RFC 4512, June
+ 2006.
+
+
+
+
+
+
+
+
+Zeilenga Informational [Page 4]
+
+RFC 4525 LDAP Modify-Increment Extension June 2006
+
+
+6.2. Informative References
+
+ [RFC4520] Zeilenga, K., "Internet Assigned Numbers Authority
+ (IANA) Considerations for the Lightweight Directory
+ Access Protocol (LDAP)", BCP 64, RFC 4520, June 2006.
+
+ [RFC4527] Zeilenga, K., "Lightweight Directory Access Protocol
+ (LDAP) Read Entry Controls", RFC 4527, June 2006.
+
+ [RFC4528] Zeilenga, K., "Lightweight Directory Access Protocol
+ (LDAP) Assertion Control", RFC 4528, June 2006.
+
+Author's Address
+
+ Kurt D. Zeilenga
+ OpenLDAP Foundation
+
+ EMail: Kurt@OpenLDAP.org
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Zeilenga Informational [Page 5]
+
+RFC 4525 LDAP Modify-Increment Extension June 2006
+
+
+Full Copyright Statement
+
+ Copyright (C) The Internet Society (2006).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
+ ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
+ INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
+ INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be
+ found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at
+ ietf-ipr@ietf.org.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is provided by the IETF
+ Administrative Support Activity (IASA).
+
+
+
+
+
+
+
+Zeilenga Informational [Page 6]
+
diff --git a/source4/ldap_server/devdocs/rfc4526.txt b/source4/ldap_server/devdocs/rfc4526.txt
new file mode 100644
index 0000000000..9795632b99
--- /dev/null
+++ b/source4/ldap_server/devdocs/rfc4526.txt
@@ -0,0 +1,283 @@
+
+
+
+
+
+
+Network Working Group K. Zeilenga
+Request for Comments: 4526 OpenLDAP Foundation
+Category: Standards Track June 2006
+
+
+ Lightweight Directory Access Protocol (LDAP)
+ Absolute True and False Filters
+
+Status of This Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2006).
+
+Abstract
+
+ This document extends the Lightweight Directory Access Protocol
+ (LDAP) to support absolute True and False filters based upon similar
+ capabilities found in X.500 directory systems. The document also
+ extends the String Representation of LDAP Search Filters to support
+ these filters.
+
+Table of Contents
+
+ 1. Background ......................................................1
+ 2. Absolute True and False Filters .................................2
+ 3. Security Considerations .........................................2
+ 4. IANA Considerations .............................................3
+ 5. References ......................................................3
+ 5.1. Normative References .......................................3
+ 5.2. Informative References .....................................3
+
+1. Background
+
+ The X.500 Directory Access Protocol (DAP) [X.511] supports absolute
+ True and False assertions. An 'and' filter with zero elements always
+ evaluates to True. An 'or' filter with zero elements always
+ evaluates to False. These filters are commonly used when requesting
+ DSA-specific Entries (DSEs) that do not necessarily have
+ 'objectClass' attributes; that is, where "(objectClass=*)" may
+ evaluate to False.
+
+
+
+
+Zeilenga Standards Track [Page 1]
+
+RFC 4526 LDAP Absolute True and False Filters June 2006
+
+
+ Although LDAPv2 [RFC1777][RFC3494] placed no restriction on the
+ number of elements in 'and' and 'or' filter sets, the LDAPv2 string
+ representation [RFC1960][RFC3494] could not represent empty 'and' and
+ 'or' filter sets. Due to this, absolute True or False filters were
+ (unfortunately) eliminated from LDAPv3 [RFC4510].
+
+ This documents extends LDAPv3 to support absolute True and False
+ assertions by allowing empty 'and' and 'or' in Search filters
+ [RFC4511] and extends the filter string representation [RFC4515] to
+ allow empty filter lists.
+
+ It is noted that certain search operations, such as those used to
+ retrieve subschema information [RFC4512], require use of particular
+ filters. This document does not change these requirements.
+
+ This feature is intended to allow a more direct mapping between DAP
+ and LDAP (as needed to implement DAP-to-LDAP gateways).
+
+ In this document, the key words "MUST", "MUST NOT", "REQUIRED",
+ "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY",
+ and "OPTIONAL" are to be interpreted as described in BCP 14
+ [RFC2119].
+
+2. Absolute True and False Filters
+
+ Implementations of this extension SHALL allow 'and' and 'or' choices
+ with zero filter elements.
+
+ An 'and' filter consisting of an empty set of filters SHALL evaluate
+ to True. This filter is represented by the string "(&)".
+
+ An 'or' filter consisting of an empty set of filters SHALL evaluate
+ to False. This filter is represented by the string "(|)".
+
+ Servers supporting this feature SHOULD publish the Object Identifier
+ 1.3.6.1.4.1.4203.1.5.3 as a value of the 'supportedFeatures'
+ [RFC4512] attribute in the root DSE.
+
+ Clients supporting this feature SHOULD NOT use the feature unless
+ they know that the server supports it.
+
+3. Security Considerations
+
+ The (re)introduction of absolute True and False filters is not
+ believed to raise any new security considerations.
+
+ Implementors of this (or any) LDAPv3 extension should be familiar
+ with general LDAPv3 security considerations [RFC4510].
+
+
+
+Zeilenga Standards Track [Page 2]
+
+RFC 4526 LDAP Absolute True and False Filters June 2006
+
+
+4. IANA Considerations
+
+ Registration of this feature has been completed by the IANA
+ [RFC4520].
+
+ Subject: Request for LDAP Protocol Mechanism Registration Object
+ Identifier: 1.3.6.1.4.1.4203.1.5.3 Description: True/False filters
+ Person & email address to contact for further information:
+ Kurt Zeilenga <kurt@openldap.org> Usage: Feature Specification:
+ RFC 4526 Author/Change Controller: IESG Comments: none
+
+ This OID was assigned [ASSIGN] by OpenLDAP Foundation, under its
+ IANA-assigned private enterprise allocation [PRIVATE], for use in
+ this specification.
+
+5. References
+
+5.1. Normative References
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [RFC4510] Zeilenga, K., Ed, "Lightweight Directory Access
+ Protocol (LDAP): Technical Specification Road Map", RFC
+ 4510, June 2006.
+
+ [RFC4511] Sermersheim, J., Ed., "Lightweight Directory Access
+ Protocol (LDAP): The Protocol", RFC 4511, June 2006.
+
+ [RFC4512] Zeilenga, K., "Lightweight Directory Access Protocol
+ (LDAP): Directory Information Models", RFC 4512, June
+ 2006.
+
+ [RFC4515] Smith, M., Ed. and T. Howes, "Lightweight Directory
+ Access Protocol (LDAP): String Representation of Search
+ Filters", RFC 4515, June 2006.
+
+5.2. Informative References
+
+ [RFC1777] Yeong, W., Howes, T., and S. Kille, "Lightweight
+ Directory Access Protocol", RFC 1777, March 1995.
+
+ [RFC1960] Howes, T., "A String Representation of LDAP Search
+ Filters", RFC 1960, June 1996.
+
+ [RFC3494] Zeilenga, K., "Lightweight Directory Access Protocol
+ version 2 (LDAPv2) to Historic Status", RFC 3494, March
+ 2003.
+
+
+
+Zeilenga Standards Track [Page 3]
+
+RFC 4526 LDAP Absolute True and False Filters June 2006
+
+
+ [RFC4520] Zeilenga, K., "Internet Assigned Numbers Authority
+ (IANA) Considerations for the Lightweight Directory
+ Access Protocol (LDAP)", BCP 64, RFC 4520, June 2006.
+
+ [X.500] International Telecommunication Union -
+ Telecommunication Standardization Sector, "The
+ Directory -- Overview of concepts, models and
+ services," X.500(1993) (also ISO/IEC 9594-1:1994).
+
+ [X.501] International Telecommunication Union -
+ Telecommunication Standardization Sector, "The
+ Directory -- Models," X.501(1993) (also ISO/IEC 9594-
+ 2:1994).
+
+ [X.511] International Telecommunication Union -
+ Telecommunication Standardization Sector, "The
+ Directory: Abstract Service Definition", X.511(1993)
+ (also ISO/IEC 9594-3:1993).
+
+ [ASSIGN] OpenLDAP Foundation, "OpenLDAP OID Delegations",
+ http://www.openldap.org/foundation/oid-delegate.txt.
+
+ [PRIVATE] IANA, "Private Enterprise Numbers",
+ http://www.iana.org/assignments/enterprise-numbers.
+
+Author's Address
+
+ Kurt D. Zeilenga
+ OpenLDAP Foundation
+
+ EMail: Kurt@OpenLDAP.org
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 4]
+
+RFC 4526 LDAP Absolute True and False Filters June 2006
+
+
+Full Copyright Statement
+
+ Copyright (C) The Internet Society (2006).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
+ ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
+ INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
+ INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be
+ found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at
+ ietf-ipr@ietf.org.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is provided by the IETF
+ Administrative Support Activity (IASA).
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 5]
+
diff --git a/source4/ldap_server/devdocs/rfc4527.txt b/source4/ldap_server/devdocs/rfc4527.txt
new file mode 100644
index 0000000000..de6e5d0d54
--- /dev/null
+++ b/source4/ldap_server/devdocs/rfc4527.txt
@@ -0,0 +1,451 @@
+
+
+
+
+
+
+Network Working Group K. Zeilenga
+Request for Comments: 4527 OpenLDAP Foundation
+Category: Standards Track June 2006
+
+
+ Lightweight Directory Access Protocol (LDAP)
+ Read Entry Controls
+
+
+Status of This Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2006).
+
+Abstract
+
+ This document specifies an extension to the Lightweight Directory
+ Access Protocol (LDAP) to allow the client to read the target entry
+ of an update operation. The client may request to read the entry
+ before and/or after the modifications are applied. These reads are
+ done as an atomic part of the update operation.
+
+Table of Contents
+
+ 1. Background and Intent of Use ....................................2
+ 2. Terminology .....................................................2
+ 3. Read Entry Controls .............................................3
+ 3.1. The Pre-Read Controls ......................................3
+ 3.2. The Post-Read Controls .....................................3
+ 4. Interaction with Other Controls .................................4
+ 5. Security Considerations .........................................4
+ 6. IANA Considerations .............................................5
+ 6.1. Object Identifier ..........................................5
+ 6.2. LDAP Protocol Mechanisms ...................................5
+ 7. Acknowledgement .................................................5
+ 8. References ......................................................6
+ 8.1. Normative References .......................................6
+ 8.2. Informative References .....................................7
+
+
+
+
+
+
+Zeilenga Standards Track [Page 1]
+
+RFC 4527 LDAP Read Entry Controls June 2006
+
+
+1. Background and Intent of Use
+
+ This document specifies an extension to the Lightweight Directory
+ Access Protocol (LDAP) [RFC4510] to allow the client to read the
+ target entry of an update operation (e.g., Add, Delete, Modify,
+ ModifyDN). The extension utilizes controls [RFC4511] attached to
+ update requests to request and return copies of the target entry.
+ One request control, called the Pre-Read request control, indicates
+ that a copy of the entry before application of update is to be
+ returned. Another control, called the Post-Read request control,
+ indicates that a copy of the entry after application of the update is
+ to be returned. Each request control has a corresponding response
+ control used to return the entry.
+
+ To ensure proper isolation, the controls are processed as an atomic
+ part of the update operation.
+
+ The functionality offered by these controls is based upon similar
+ functionality in the X.500 Directory Access Protocol (DAP) [X.511].
+
+ The Pre-Read controls may be used to obtain replaced or deleted
+ values of modified attributes or a copy of the entry being deleted.
+
+ The Post-Read controls may be used to obtain values of operational
+ attributes, such as the 'entryUUID' [RFC4530] and 'modifyTimestamp'
+ [RFC4512] attributes, updated by the server as part of the update
+ operation.
+
+2. Terminology
+
+ Protocol elements are described using ASN.1 [X.680] with implicit
+ tags. The term "BER-encoded" means the element is to be encoded
+ using the Basic Encoding Rules [X.690] under the restrictions
+ detailed in Section 5.1 of [RFC4511].
+
+ DN stands for Distinguished Name.
+ DSA stands for Directory System Agent (i.e., a directory server).
+ DSE stands for DSA-specific Entry.
+
+ In this document, the key words "MUST", "MUST NOT", "REQUIRED",
+ "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY",
+ and "OPTIONAL" are to be interpreted as described in BCP 14
+ [RFC2119].
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 2]
+
+RFC 4527 LDAP Read Entry Controls June 2006
+
+
+3. Read Entry Controls
+
+3.1. The Pre-Read Controls
+
+ The Pre-Read request and response controls are identified by the
+ 1.3.6.1.1.13.1 object identifier. Servers implementing these
+ controls SHOULD publish 1.3.6.1.1.13.1 as a value of the
+ 'supportedControl' [RFC4512] in their root DSE.
+
+ The Pre-Read request control is a LDAP Control [RFC4511] whose
+ controlType is 1.3.6.1.1.13.1 and whose controlValue is a BER-encoded
+ AttributeSelection [RFC4511], as extended by [RFC3673]. The
+ criticality may be TRUE or FALSE. This control is appropriate for
+ the modifyRequest, delRequest, and modDNRequest LDAP messages.
+
+ The corresponding response control is a LDAP Control whose
+ controlType is 1.3.6.1.1.13.1 and whose the controlValue, an OCTET
+ STRING, contains a BER-encoded SearchResultEntry. The criticality
+ may be TRUE or FALSE. This control is appropriate for the
+ modifyResponse, delResponse, and modDNResponse LDAP messages with a
+ resultCode of success (0).
+
+ When the request control is attached to an appropriate update LDAP
+ request, the control requests the return of a copy of the target
+ entry prior to the application of the update. The AttributeSelection
+ indicates, as discussed in [RFC4511][RFC3673], which attributes are
+ requested to appear in the copy. The server is to return a
+ SearchResultEntry containing, subject to access controls and other
+ constraints, values of the requested attributes.
+
+ The normal processing of the update operation and the processing of
+ this control MUST be performed as one atomic action isolated from
+ other update operations.
+
+ If the update operation fails (in either normal or control
+ processing), no Pre-Read response control is provided.
+
+3.2. The Post-Read Controls
+
+ The Post-Read request and response controls are identified by the
+ 1.3.6.1.1.13.2 object identifier. Servers implementing these
+ controls SHOULD publish 1.3.6.1.1.13.2 as a value of the
+ 'supportedControl' [RFC4512] in their root DSE.
+
+ The Post-Read request control is a LDAP Control [RFC4511] whose
+ controlType is 1.3.6.1.1.13.2 and whose controlValue, an OCTET
+ STRING, contains a BER-encoded AttributeSelection [RFC4511], as
+ extended by [RFC3673]. The criticality may be TRUE or FALSE. This
+
+
+
+Zeilenga Standards Track [Page 3]
+
+RFC 4527 LDAP Read Entry Controls June 2006
+
+
+ control is appropriate for the addRequest, modifyRequest, and
+ modDNRequest LDAP messages.
+
+ The corresponding response control is a LDAP Control whose
+ controlType is 1.3.6.1.1.13.2 and whose controlValue is a BER-encoded
+ SearchResultEntry. The criticality may be TRUE or FALSE. This
+ control is appropriate for the addResponse, modifyResponse, and
+ modDNResponse LDAP messages with a resultCode of success (0).
+
+ When the request control is attached to an appropriate update LDAP
+ request, the control requests the return of a copy of the target
+ entry after the application of the update. The AttributeSelection
+ indicates, as discussed in [RFC4511][RFC3673], which attributes are
+ requested to appear in the copy. The server is to return a
+ SearchResultEntry containing, subject to access controls and other
+ constraints, values of the requested attributes.
+
+ The normal processing of the update operation and the processing of
+ this control MUST be performed as one atomic action isolated from
+ other update operations.
+
+ If the update operation fails (in either normal or control
+ processing), no Post-Read response control is provided.
+
+4. Interaction with Other Controls
+
+ The Pre-Read and Post-Read controls may be combined with each other
+ and/or with a variety of other controls. When combined with the
+ assertion control [RFC4528] and/or the manageDsaIT control [RFC3296],
+ the semantics of each control included in the combination applies.
+ The Pre-Read and Post-Read controls may be combined with other
+ controls as detailed in other technical specifications.
+
+5. Security Considerations
+
+ The controls defined in this document extend update operations to
+ support read capabilities. Servers MUST ensure that the client is
+ authorized for reading of the information provided in this control
+ and that the client is authorized to perform the requested directory
+ update.
+
+ Security considerations for the update operations [RFC4511] extended
+ by this control, as well as general LDAP security considerations
+ [RFC4510], generally apply to implementation and use of this
+ extension
+
+
+
+
+
+
+Zeilenga Standards Track [Page 4]
+
+RFC 4527 LDAP Read Entry Controls June 2006
+
+
+6. IANA Considerations
+
+ Registration of the following protocol values [RFC4520] have been
+ completed by the IANA.
+
+6.1. Object Identifier
+
+ The IANA has registered an LDAP Object Identifier to identify LDAP
+ protocol elements defined in this document.
+
+ Subject: Request for LDAP Object Identifier Registration
+ Person & email address to contact for further information:
+ Kurt Zeilenga <kurt@OpenLDAP.org>
+ Specification: RFC 4527
+ Author/Change Controller: IESG
+ Comments: Identifies the LDAP Read Entry Controls
+
+6.2. LDAP Protocol Mechanisms
+
+ The IANA has registered the LDAP Protocol Mechanism described in this
+ document.
+
+ Subject: Request for LDAP Protocol Mechanism Registration
+ Object Identifier: 1.3.6.1.1.13.1
+ Description: LDAP Pre-read Control
+ Person & email address to contact for further information:
+ Kurt Zeilenga <kurt@openldap.org>
+ Usage: Control
+ Specification: RFC 4527
+ Author/Change Controller: IESG
+ Comments: none
+
+ Subject: Request for LDAP Protocol Mechanism Registration
+ Object Identifier: 1.3.6.1.1.13.2
+ Description: LDAP Post-read Control
+ Person & email address to contact for further information:
+ Kurt Zeilenga <kurt@openldap.org>
+ Usage: Control
+ Specification: RFC 4527
+ Author/Change Controller: IESG
+ Comments: none
+
+7. Acknowledgement
+
+ The LDAP Pre-Read and Post-Read controls are modeled after similar
+ capabilities offered in the DAP [X.511].
+
+
+
+
+
+Zeilenga Standards Track [Page 5]
+
+RFC 4527 LDAP Read Entry Controls June 2006
+
+
+8. References
+
+8.1. Normative References
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [RFC3296] Zeilenga, K., "Named Subordinate References in
+ Lightweight Directory Access Protocol (LDAP)
+ Directories", RFC 3296, July 2002.
+
+ [RFC3673] Zeilenga, K., "Lightweight Directory Access Protocol
+ version 3 (LDAPv3): All Operational Attributes", RFC
+ 3673, December 2003.
+
+ [RFC4510] Zeilenga, K., Ed, "Lightweight Directory Access
+ Protocol (LDAP): Technical Specification Road Map", RFC
+ 4510, June 2006.
+
+ [RFC4511] Sermersheim, J., Ed., "Lightweight Directory Access
+ Protocol (LDAP): The Protocol", RFC 4511, June 2006.
+
+ [RFC4512] Zeilenga, K., "Lightweight Directory Access Protocol
+ (LDAP): Directory Information Models", RFC 4512, June
+ 2006.
+
+ [RFC4528] Zeilenga, K., "Lightweight Directory Access Protocol
+ (LDAP) Assertion Control", RFC 4528, June 2006.
+
+ [X.680] International Telecommunication Union -
+ Telecommunication Standardization Sector, "Abstract
+ Syntax Notation One (ASN.1) - Specification of Basic
+ Notation", X.680(1997) (also ISO/IEC 8824-1:1998).
+
+ [X.690] International Telecommunication Union -
+ Telecommunication Standardization Sector,
+ "Specification of ASN.1 encoding rules: Basic Encoding
+ Rules (BER), Canonical Encoding Rules (CER), and
+ Distinguished Encoding Rules (DER)", X.690(1997) (also
+ ISO/IEC 8825-1:1998).
+
+
+
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 6]
+
+RFC 4527 LDAP Read Entry Controls June 2006
+
+
+8.2. Informative References
+
+ [RFC4520] Zeilenga, K., "Internet Assigned Numbers Authority
+ (IANA) Considerations for the Lightweight Directory
+ Access Protocol (LDAP)", BCP 64, RFC 4520, June 2006.
+
+ [RFC4530] Zeilenga, K., "Lightweight Directory Access Protocol
+ (LDAP) EntryUUID Operational Attribute", RFC 4530, June
+ 2006.
+
+ [X.511] International Telecommunication Union -
+ Telecommunication Standardization Sector, "The
+ Directory: Abstract Service Definition", X.511(1993)
+ (also ISO/IEC 9594-3:1993).
+
+Author's Address
+
+ Kurt D. Zeilenga
+ OpenLDAP Foundation
+
+ EMail: Kurt@OpenLDAP.org
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 7]
+
+RFC 4527 LDAP Read Entry Controls June 2006
+
+
+Full Copyright Statement
+
+ Copyright (C) The Internet Society (2006).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
+ ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
+ INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
+ INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be
+ found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at
+ ietf-ipr@ietf.org.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is provided by the IETF
+ Administrative Support Activity (IASA).
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 8]
+
diff --git a/source4/ldap_server/devdocs/rfc4528.txt b/source4/ldap_server/devdocs/rfc4528.txt
new file mode 100644
index 0000000000..5b1fee0c1b
--- /dev/null
+++ b/source4/ldap_server/devdocs/rfc4528.txt
@@ -0,0 +1,339 @@
+
+
+
+
+
+
+Network Working Group K. Zeilenga
+Request for Comments: 4528 OpenLDAP Foundation
+Category: Standards Track June 2006
+
+
+ Lightweight Directory Access Protocol (LDAP)
+ Assertion Control
+
+
+Status of This Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2006).
+
+Abstract
+
+ This document defines the Lightweight Directory Access Protocol
+ (LDAP) Assertion Control, which allows a client to specify that a
+ directory operation should only be processed if an assertion applied
+ to the target entry of the operation is true. It can be used to
+ construct "test and set", "test and clear", and other conditional
+ operations.
+
+Table of Contents
+
+ 1. Overview ........................................................2
+ 2. Terminology .....................................................2
+ 3. The Assertion Control ...........................................2
+ 4. Security Considerations .........................................3
+ 5. IANA Considerations .............................................4
+ 5.1. Object Identifier ..........................................4
+ 5.2. LDAP Protocol Mechanism ....................................4
+ 5.3. LDAP Result Code ...........................................4
+ 6. Acknowledgements ................................................5
+ 7. References ......................................................5
+ 7.1. Normative References .......................................5
+ 7.2. Informative References .....................................5
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 1]
+
+RFC 4528 LDAP Assertion Control June 2006
+
+
+1. Overview
+
+ This document defines the Lightweight Directory Access Protocol
+ (LDAP) [RFC4510] assertion control. The assertion control allows the
+ client to specify a condition that must be true for the operation to
+ be processed normally. Otherwise, the operation is not performed.
+ For instance, the control can be used with the Modify operation
+ [RFC4511] to perform atomic "test and set" and "test and clear"
+ operations.
+
+ The control may be attached to any update operation to support
+ conditional addition, deletion, modification, and renaming of the
+ target object. The asserted condition is evaluated as an integral
+ part the operation.
+
+ The control may also be used with the search operation. Here, the
+ assertion is applied to the base object of the search before
+ searching for objects that match the search scope and filter.
+
+ The control may also be used with the compare operation. Here, it
+ extends the compare operation to allow a more complex assertion.
+
+2. Terminology
+
+ Protocol elements are described using ASN.1 [X.680] with implicit
+ tags. The term "BER-encoded" means the element is to be encoded
+ using the Basic Encoding Rules [X.690] under the restrictions
+ detailed in Section 5.1 of [RFC4511].
+
+ DSA stands for Directory System Agent (or server).
+ DSE stands for DSA-specific Entry.
+
+ In this document, the key words "MUST", "MUST NOT", "REQUIRED",
+ "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY",
+ and "OPTIONAL" are to be interpreted as described in BCP 14
+ [RFC2119].
+
+3. The Assertion Control
+
+ The assertion control is an LDAP Control [RFC4511] whose controlType
+ is 1.3.6.1.1.12 and whose controlValue is a BER-encoded Filter
+ [Protocol, Section 4.5.1]. The criticality may be TRUE or FALSE.
+ There is no corresponding response control.
+
+ The control is appropriate for both LDAP interrogation and update
+ operations [RFC4511], including Add, Compare, Delete, Modify,
+ ModifyDN (rename), and Search. It is inappropriate for Abandon,
+ Bind, Unbind, and StartTLS operations.
+
+
+
+Zeilenga Standards Track [Page 2]
+
+RFC 4528 LDAP Assertion Control June 2006
+
+
+ When the control is attached to an LDAP request, the processing of
+ the request is conditional on the evaluation of the Filter as applied
+ against the target of the operation. If the Filter evaluates to
+ TRUE, then the request is processed normally. If the Filter
+ evaluates to FALSE or Undefined, then assertionFailed (122)
+ resultCode is returned, and no further processing is performed.
+
+ For Add, Compare, and ModifyDN operations, the target is indicated by
+ the entry field in the request. For Modify operations, the target is
+ indicated by the object field. For Delete operations, the target is
+ indicated by the DelRequest type. For Compare operations and all
+ update operations, the evaluation of the assertion MUST be performed
+ as an integral part of the operation. That is, the evaluation of the
+ assertion and the normal processing of the operation SHALL be done as
+ one atomic action.
+
+ For Search operations, the target is indicated by the baseObject
+ field, and the evaluation is done after "finding" but before
+ "searching" [RFC4511]. Hence, no entries or continuations references
+ are returned if the assertion fails.
+
+ Servers implementing this technical specification SHOULD publish the
+ object identifier 1.3.6.1.1.12 as a value of the 'supportedControl'
+ attribute [RFC4512] in their root DSE. A server MAY choose to
+ advertise this extension only when the client is authorized to use
+ it.
+
+ Other documents may specify how this control applies to other LDAP
+ operations. In doing so, they must state how the target entry is
+ determined.
+
+4. Security Considerations
+
+ The filter may, like other components of the request, contain
+ sensitive information. When it does, this information should be
+ appropriately protected.
+
+ As with any general assertion mechanism, the mechanism can be used to
+ determine directory content. Hence, this mechanism SHOULD be subject
+ to appropriate access controls.
+
+ Some assertions may be very complex, requiring significant time and
+ resources to evaluate. Hence, this mechanism SHOULD be subject to
+ appropriate administrative controls.
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 3]
+
+RFC 4528 LDAP Assertion Control June 2006
+
+
+ Security considerations for the base operations [RFC4511] extended by
+ this control, as well as general LDAP security considerations
+ [RFC4510], generally apply to implementation and use of this
+ extension.
+
+5. IANA Considerations
+
+5.1. Object Identifier
+
+ The IANA has assigned an LDAP Object Identifier [RFC4520] to identify
+ the LDAP Assertion Control defined in this document.
+
+ Subject: Request for LDAP Object Identifier Registration
+ Person & email address to contact for further information:
+ Kurt Zeilenga <kurt@OpenLDAP.org>
+ Specification: RFC 4528
+ Author/Change Controller: IESG
+ Comments:
+ Identifies the LDAP Assertion Control
+
+5.2. LDAP Protocol Mechanism
+
+ Registration of this protocol mechanism [RFC4520] is requested.
+
+ Subject: Request for LDAP Protocol Mechanism Registration
+ Object Identifier: 1.3.6.1.1.12
+ Description: Assertion Control
+ Person & email address to contact for further information:
+ Kurt Zeilenga <kurt@openldap.org>
+ Usage: Control
+ Specification: RFC 4528
+ Author/Change Controller: IESG
+ Comments: none
+
+5.3. LDAP Result Code
+
+ The IANA has assigned an LDAP Result Code [RFC4520] called
+ 'assertionFailed' (122).
+
+ Subject: LDAP Result Code Registration
+ Person & email address to contact for further information:
+ Kurt Zeilenga <kurt@OpenLDAP.org>
+ Result Code Name: assertionFailed
+ Specification: RFC 4528
+ Author/Change Controller: IESG
+ Comments: none
+
+
+
+
+
+Zeilenga Standards Track [Page 4]
+
+RFC 4528 LDAP Assertion Control June 2006
+
+
+6. Acknowledgements
+
+ The assertion control concept is attributed to Morteza Ansari.
+
+7. References
+
+7.1. Normative References
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [RFC4510] Zeilenga, K., Ed., "Lightweight Directory Access
+ Protocol (LDAP): Technical Specification Road Map", RFC
+ 4510, June 2006.
+
+ [RFC4511] Sermersheim, J., Ed., "Lightweight Directory Access
+ Protocol (LDAP): The Protocol", RFC 4511, June 2006.
+
+ [RFC4512] Zeilenga, K., "Lightweight Directory Access Protocol
+ (LDAP): Directory Information Models", RFC 4512, June
+ 2006.
+
+ [X.680] International Telecommunication Union -
+ Telecommunication Standardization Sector, "Abstract
+ Syntax Notation One (ASN.1) - Specification of Basic
+ Notation", X.680(2002) (also ISO/IEC 8824-1:2002).
+
+ [X.690] International Telecommunication Union -
+ Telecommunication Standardization Sector,
+ "Specification of ASN.1 encoding rules: Basic Encoding
+ Rules (BER), Canonical Encoding Rules (CER), and
+ Distinguished Encoding Rules (DER)", X.690(2002) (also
+ ISO/IEC 8825-1:2002).
+
+7.2. Informative References
+
+ [RFC4520] Zeilenga, K., "Internet Assigned Numbers Authority
+ (IANA) Considerations for the Lightweight Directory
+ Access Protocol (LDAP)", BCP 64, RFC 4520, June 2006.
+
+Author's Address
+
+ Kurt D. Zeilenga
+ OpenLDAP Foundation
+
+ EMail: Kurt@OpenLDAP.org
+
+
+
+
+
+Zeilenga Standards Track [Page 5]
+
+RFC 4528 LDAP Assertion Control June 2006
+
+
+Full Copyright Statement
+
+ Copyright (C) The Internet Society (2006).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
+ ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
+ INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
+ INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be
+ found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at
+ ietf-ipr@ietf.org.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is provided by the IETF
+ Administrative Support Activity (IASA).
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 6]
+
diff --git a/source4/ldap_server/devdocs/rfc4529.txt b/source4/ldap_server/devdocs/rfc4529.txt
new file mode 100644
index 0000000000..47449c040f
--- /dev/null
+++ b/source4/ldap_server/devdocs/rfc4529.txt
@@ -0,0 +1,339 @@
+
+
+
+
+
+
+Network Working Group K. Zeilenga
+Request for Comments: 4529 OpenLDAP Foundation
+Category: Informational June 2006
+
+
+ Requesting Attributes by Object Class in the
+ Lightweight Directory Access Protocol (LDAP)
+
+Status of This Memo
+
+ This memo provides information for the Internet community. It does
+ not specify an Internet standard of any kind. Distribution of this
+ memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2006).
+
+Abstract
+
+ The Lightweight Directory Access Protocol (LDAP) search operation
+ provides mechanisms for clients to request all user application
+ attributes, all operational attributes, and/or attributes selected by
+ their description. This document extends LDAP to support a mechanism
+ that LDAP clients may use to request the return of all attributes of
+ an object class.
+
+Table of Contents
+
+ 1. Background and Intended Use .....................................1
+ 2. Terminology .....................................................2
+ 3. Return of all Attributes of an Object Class .....................2
+ 4. Security Considerations .........................................3
+ 5. IANA Considerations .............................................3
+ 6. References ......................................................4
+ 6.1. Normative References .......................................4
+ 6.2. Informative References .....................................4
+
+1. Background and Intended Use
+
+ In the Lightweight Directory Access Protocol (LDAP) [RFC4510], the
+ search operation [RFC4511] supports requesting the return of a set of
+ attributes. This set is determined by a list of attribute
+ descriptions. Two special descriptors are defined to request all
+ user attributes ("*") [RFC4511] and all operational attributes ("+")
+ [RFC3673]. However, there is no convenient mechanism for requesting
+ pre-defined sets of attributes such as the set of attributes used to
+ represent a particular class of object.
+
+
+
+Zeilenga Informational [Page 1]
+
+RFC 4529 Requesting Attributes by Object Class June 2006
+
+
+ This document extends LDAP to allow an object class identifier to be
+ specified in attributes lists, such as in Search requests, to request
+ the return of all attributes belonging to an object class. The
+ COMMERCIAL AT ("@", U+0040) character is used to distinguish an
+ object class identifier from an attribute descriptions.
+
+ For example, the attribute list of "@country" is equivalent to the
+ attribute list of 'c', 'searchGuide', 'description', and
+ 'objectClass'. This object class is described in [RFC4519].
+
+ This extension is intended primarily to be used where the user is in
+ direct control of the parameters of the LDAP search operation, for
+ instance when entering an LDAP URL [RFC4516] into a web browser, such
+ as <ldap:///dc=example,dc=com?@organization?base>.
+
+2. Terminology
+
+ In this document, the key words "MUST", "MUST NOT", "REQUIRED",
+ "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY",
+ and "OPTIONAL" are to be interpreted as described in BCP 14
+ [RFC2119].
+
+ DSA stands for Directory System Agent (or server).
+ DSE stands for DSA-specific Entry.
+
+3. Return of All Attributes of an Object Class
+
+ This extension allows object class identifiers to be provided in the
+ attributes field of the LDAP SearchRequest [RFC4511] or other request
+ values of the AttributeSelection data type (e.g., attributes field in
+ pre/post read controls [ReadEntry]) and/or <attributeSelector>
+ production (e.g., attributes of an LDAP URL [RFC4516]). For each
+ object class identified in the attributes field, the request is to be
+ treated as if each attribute allowed by that class (by "MUST" or
+ "MAY", directly or by "SUP"erior) [RFC4512] were itself listed.
+
+ This extension extends the <attributeSelector> [RFC4511] production
+ as indicated by the following ABNF [RFC4234]:
+
+ attributeSelector =/ objectclassdescription
+ objectclassdescription = ATSIGN oid options
+ ATSIGN = %x40 ; COMMERCIAL AT ("@" U+0040)
+
+ where <oid> and <options> productions are as defined in [RFC4512].
+
+
+
+
+
+
+
+Zeilenga Informational [Page 2]
+
+RFC 4529 Requesting Attributes by Object Class June 2006
+
+
+ The <oid> component of an <objectclassdescription> production
+ identifies the object class by short name (descr) or object
+ identifier (numericoid). If the value of the <oid> component is
+ unrecognized or does not refer to an object class, the object class
+ description is to be treated as an unrecognized attribute
+ description.
+
+ The <options> production is included in the grammar for extensibility
+ purposes. An object class description with an unrecognized or
+ inappropriate option is to be treated as unrecognized.
+
+ Although object class description options and attribute description
+ options share the same syntax, they are not semantically related.
+ This document does not define any object description option.
+
+ Servers supporting this feature SHOULD publish the object identifier
+ (OID) 1.3.6.1.4.1.4203.1.5.2 as a value of the 'supportedFeatures'
+ [RFC4512] attribute in the root DSE. Clients supporting this feature
+ SHOULD NOT use the feature unless they know that the server supports
+ it.
+
+4. Security Considerations
+
+ This extension provides a shorthand for requesting all attributes of
+ an object class. Because these attributes could have been listed
+ individually, introduction of this shorthand is not believed to raise
+ additional security considerations.
+
+ Implementors of this LDAP extension should be familiar with security
+ considerations applicable to the LDAP search operation [RFC4511], as
+ well as with general LDAP security considerations [RFC4510].
+
+5. IANA Considerations
+
+ Registration of the LDAP Protocol Mechanism [RFC4520] defined in this
+ document has been completed.
+
+ Subject: Request for LDAP Protocol Mechanism Registration
+ Object Identifier: 1.3.6.1.4.1.4203.1.5.2
+ Description: OC AD Lists
+ Person & email address to contact for further information:
+ Kurt Zeilenga <kurt@openldap.org>
+ Usage: Feature
+ Specification: RFC 4529
+ Author/Change Controller: Kurt Zeilenga <kurt@openldap.org>
+ Comments: none
+
+
+
+
+
+Zeilenga Informational [Page 3]
+
+RFC 4529 Requesting Attributes by Object Class June 2006
+
+
+ This OID was assigned [ASSIGN] by OpenLDAP Foundation, under its
+ IANA-assigned private enterprise allocation [PRIVATE], for use in
+ this specification.
+
+6. References
+
+6.1. Normative References
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [RFC4234] Crocker, D., Ed. and P. Overell, "Augmented BNF for
+ Syntax Specifications: ABNF", RFC 4234, October 2005.
+
+ [RFC4510] Zeilenga, K., Ed., "Lightweight Directory Access
+ Protocol (LDAP): Technical Specification Road Map", RFC
+ 4510, June 2006.
+
+ [RFC4511] Sermersheim, J., Ed., "Lightweight Directory Access
+ Protocol (LDAP): The Protocol", RFC 4511, June 2006.
+
+ [RFC4512] Zeilenga, K., "Lightweight Directory Access Protocol
+ (LDAP): Directory Information Models", RFC 4512, June
+ 2006.
+
+ [RFC4516] Smith, M., Ed. and T. Howes, "Lightweight Directory
+ Access Protocol (LDAP): Uniform Resource Locator", RFC
+ 4516, June 2006.
+
+ [X.680] International Telecommunication Union -
+ Telecommunication Standardization Sector, "Abstract
+ Syntax Notation One (ASN.1) - Specification of Basic
+ Notation", X.680(2002) (also ISO/IEC 8824-1:2002).
+
+6.2. Informative References
+
+ [RFC3673] Zeilenga, K., "Lightweight Directory Access Protocol
+ version 3 (LDAPv3): All Operational Attributes", RFC
+ 3673, December 2003.
+
+ [RFC4519] Sciberras, A., Ed., "Lightweight Directory Access
+ Protocol (LDAP): Schema for User Applications", RFC
+ 4519, June 2006.
+
+ [RFC4520] Zeilenga, K., "Internet Assigned Numbers Authority
+ (IANA) Considerations for the Lightweight Directory
+ Access Protocol (LDAP)", BCP 64, RFC 4520, June 2006.
+
+
+
+
+Zeilenga Informational [Page 4]
+
+RFC 4529 Requesting Attributes by Object Class June 2006
+
+
+ [ReadEntry] Zeilenga, K., "Lightweight Directory Access Protocol
+ (LDAP) Read Entry Controls", RFC 4527, June 2006.
+
+ [ASSIGN] OpenLDAP Foundation, "OpenLDAP OID Delegations",
+ http://www.openldap.org/foundation/oid-delegate.txt.
+
+ [PRIVATE] IANA, "Private Enterprise Numbers",
+ http://www.iana.org/assignments/enterprise-numbers.
+
+Author's Address
+
+ Kurt D. Zeilenga
+ OpenLDAP Foundation
+
+ EMail: Kurt@OpenLDAP.org
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Zeilenga Informational [Page 5]
+
+RFC 4529 Requesting Attributes by Object Class June 2006
+
+
+Full Copyright Statement
+
+ Copyright (C) The Internet Society (2006).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
+ ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
+ INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
+ INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be
+ found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at
+ ietf-ipr@ietf.org.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is provided by the IETF
+ Administrative Support Activity (IASA).
+
+
+
+
+
+
+
+Zeilenga Informational [Page 6]
+
diff --git a/source4/ldap_server/devdocs/rfc4530.txt b/source4/ldap_server/devdocs/rfc4530.txt
new file mode 100644
index 0000000000..219a7c2d0c
--- /dev/null
+++ b/source4/ldap_server/devdocs/rfc4530.txt
@@ -0,0 +1,451 @@
+
+
+
+
+
+
+Network Working Group K. Zeilenga
+Request for Comments: 4530 OpenLDAP Foundation
+Category: Standards Track June 2006
+
+
+ Lightweight Directory Access Protocol (LDAP)
+ entryUUID Operational Attribute
+
+
+Status of This Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2006).
+
+Abstract
+
+ This document describes the LDAP/X.500 'entryUUID' operational
+ attribute and associated matching rules and syntax. The attribute
+ holds a server-assigned Universally Unique Identifier (UUID) for the
+ object. Directory clients may use this attribute to distinguish
+ objects identified by a distinguished name or to locate an object
+ after renaming.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 1]
+
+RFC 4530 LDAP entryUUID June 2006
+
+
+Table of Contents
+
+ 1. Background and Intended Use .....................................2
+ 2. UUID Schema Elements ............................................3
+ 2.1. UUID Syntax ................................................3
+ 2.2. 'uuidMatch' Matching Rule ..................................3
+ 2.3. 'uuidOrderingMatch' Matching Rule ..........................3
+ 2.4. 'entryUUID' Attribute ......................................4
+ 3. Security Considerations .........................................4
+ 4. IANA Considerations .............................................5
+ 4.1. Object Identifier Registration .............................5
+ 4.2. UUID Syntax Registration ...................................5
+ 4.3. 'uuidMatch' Descriptor Registration ........................5
+ 4.4. 'uuidOrderingMatch' Descriptor Registration ................5
+ 4.5. 'entryUUID' Descriptor Registration ........................6
+ 5. Acknowledgements ................................................6
+ 6. References ......................................................6
+ 6.1. Normative References .......................................6
+ 6.2. Informative References .....................................7
+
+1. Background and Intended Use
+
+ In X.500 Directory Services [X.501], such as those accessible using
+ the Lightweight Directory Access Protocol (LDAP) [RFC4510], an object
+ is identified by its distinguished name (DN). However, DNs are not
+ stable identifiers. That is, a new object may be identified by a DN
+ that previously identified another (now renamed or deleted) object.
+
+ A Universally Unique Identifier (UUID) is "an identifier unique
+ across both space and time, with respect to the space of all UUIDs"
+ [RFC4122]. UUIDs are used in a wide range of systems.
+
+ This document describes the 'entryUUID' operational attribute, which
+ holds the UUID assigned to the object by the server. Clients may use
+ this attribute to distinguish objects identified by a particular
+ distinguished name or to locate a particular object after renaming.
+
+ This document defines the UUID syntax, the 'uuidMatch' and
+ 'uuidOrderingMatch' matching rules, and the 'entryUUID' attribute
+ type.
+
+ Schema definitions are provided using LDAP description formats
+ [RFC4512]. Definitions provided here are formatted (line wrapped)
+ for readability.
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 2]
+
+RFC 4530 LDAP entryUUID June 2006
+
+
+ In this document, the key words "MUST", "MUST NOT", "REQUIRED",
+ "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY",
+ and "OPTIONAL" are to be interpreted as described in BCP 14
+ [RFC2119].
+
+2. UUID Schema Elements
+
+2.1. UUID Syntax
+
+ A Universally Unique Identifier (UUID) [RFC4122] is a 16-octet (128-
+ bit) value that identifies an object. The ASN.1 [X.680] type UUID is
+ defined to represent UUIDs as follows:
+
+ UUID ::= OCTET STRING (SIZE(16))
+ -- constrained to an UUID [RFC4122]
+
+ In LDAP, UUID values are encoded using the [ASCII] character string
+ representation described in [RFC4122]. For example,
+ "597ae2f6-16a6-1027-98f4-d28b5365dc14".
+
+ The following is an LDAP syntax description suitable for publication
+ in subschema subentries.
+
+ ( 1.3.6.1.1.16.1 DESC 'UUID' )
+
+2.2. 'uuidMatch' Matching Rule
+
+ The 'uuidMatch' matching rule compares an asserted UUID with a stored
+ UUID for equality. Its semantics are the same as the
+ 'octetStringMatch' [X.520][RFC4517] matching rule. The rule differs
+ from 'octetStringMatch' in that the assertion value is encoded using
+ the UUID string representation instead of the normal OCTET STRING
+ string representation.
+
+ The following is an LDAP matching rule description suitable for
+ publication in subschema subentries.
+
+ ( 1.3.6.1.1.16.2 NAME 'uuidMatch'
+ SYNTAX 1.3.6.1.1.16.1 )
+
+2.3. 'uuidOrderingMatch' Matching Rule
+
+ The 'uuidOrderingMatch' matching rule compares an asserted UUID with
+ a stored UUID for ordering. Its semantics are the same as the
+ 'octetStringOrderingMatch' [X.520][RFC4517] matching rule. The rule
+ differs from 'octetStringOrderingMatch' in that the assertion value
+ is encoded using the UUID string representation instead of the normal
+ OCTET STRING string representation.
+
+
+
+Zeilenga Standards Track [Page 3]
+
+RFC 4530 LDAP entryUUID June 2006
+
+
+ The following is an LDAP matching rule description suitable for
+ publication in subschema subentries.
+
+ ( 1.3.6.1.1.16.3 NAME 'uuidOrderingMatch'
+ SYNTAX 1.3.6.1.1.16.1 )
+
+ Note that not all UUID variants have a defined ordering; and even
+ where it does, servers are not obligated to assign UUIDs in any
+ particular order. This matching rule is provided for completeness.
+
+2.4. 'entryUUID' Attribute
+
+ The 'entryUUID' operational attribute provides the Universally Unique
+ Identifier (UUID) assigned to the entry.
+
+ The following is an LDAP attribute type description suitable for
+ publication in subschema subentries.
+
+ ( 1.3.6.1.1.16.4 NAME 'entryUUID'
+ DESC 'UUID of the entry'
+ EQUALITY uuidMatch
+ ORDERING uuidOrderingMatch
+ SYNTAX 1.3.6.1.1.16.1
+ SINGLE-VALUE
+ NO-USER-MODIFICATION
+ USAGE directoryOperation )
+
+ Servers SHALL generate and assign a new UUID to each entry upon its
+ addition to the directory and provide that UUID as the value of the
+ 'entryUUID' operational attribute. An entry's UUID is immutable.
+
+ UUID are to be generated in accordance with Section 4 of [RFC4122].
+ In particular, servers MUST ensure that each generated UUID is unique
+ in space and time.
+
+3. Security Considerations
+
+ An entry's relative distinguish name (RDN) is composed from attribute
+ values of the entry, which are commonly descriptive of the object the
+ entry represents. Although deployers are encouraged to use naming
+ attributes whose values are widely disclosable [RFC4514], entries are
+ often named using information that cannot be disclosed to all
+ parties. As UUIDs do not contain any descriptive information of the
+ object they identify, UUIDs may be used to identify a particular
+ entry without disclosure of its contents.
+
+ General UUID security considerations [RFC4122] apply.
+
+
+
+
+Zeilenga Standards Track [Page 4]
+
+RFC 4530 LDAP entryUUID June 2006
+
+
+ General LDAP security considerations [RFC4510] apply.
+
+4. IANA Considerations
+
+ The IANA has registered the LDAP values [RFC4520] specified in this
+ document.
+
+4.1. Object Identifier Registration
+
+ Subject: Request for LDAP OID Registration
+ Person & email address to contact for further information:
+ Kurt Zeilenga <kurt@OpenLDAP.org>
+ Specification: RFC 4530
+ Author/Change Controller: IESG
+ Comments:
+ Identifies the UUID schema elements
+
+4.2. UUID Syntax Registration
+
+ Subject: Request for LDAP Syntax Registration
+ Object Identifier: 1.3.6.1.1.16.1
+ Description: UUID
+ Person & email address to contact for further information:
+ Kurt Zeilenga <kurt@OpenLDAP.org>
+ Specification: RFC 4530
+ Author/Change Controller: IESG
+ Comments:
+ Identifies the UUID syntax
+
+4.3. 'uuidMatch' Descriptor Registration
+
+ Subject: Request for LDAP Descriptor Registration
+ Descriptor (short name): uuidMatch
+ Object Identifier: 1.3.6.1.1.16.2
+ Person & email address to contact for further information:
+ Kurt Zeilenga <kurt@OpenLDAP.org>
+ Usage: Matching Rule
+ Specification: RFC 4530
+ Author/Change Controller: IESG
+
+4.4. 'uuidOrderingMatch' Descriptor Registration
+
+ Subject: Request for LDAP Descriptor Registration
+ Descriptor (short name): uuidOrderingMatch
+ Object Identifier: 1.3.6.1.1.16.3
+ Person & email address to contact for further information:
+ Kurt Zeilenga <kurt@OpenLDAP.org>
+ Usage: Matching Rule
+
+
+
+Zeilenga Standards Track [Page 5]
+
+RFC 4530 LDAP entryUUID June 2006
+
+
+ Specification: RFC 4530
+ Author/Change Controller: IESG
+
+4.5. 'entryUUID' Descriptor Registration
+
+ The IANA has registered the LDAP 'entryUUID' descriptor.
+
+ Subject: Request for LDAP Descriptor Registration
+ Descriptor (short name): entryUUID
+ Object Identifier: 1.3.6.1.1.16.4
+ Person & email address to contact for further information:
+ Kurt Zeilenga <kurt@OpenLDAP.org>
+ Usage: Attribute Type
+ Specification: RFC 4530
+ Author/Change Controller: IESG
+
+5. Acknowledgements
+
+ This document is based upon discussions in the LDAP Update and
+ Duplication Protocols (LDUP) WG. Members of the LDAP Directorate
+ provided review.
+
+6. References
+
+6.1. Normative References
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [RFC4122] Leach, P., Mealling, M., and R. Salz, "A Universally
+ Unique IDentifier (UUID) URN Namespace", RFC 4122, July
+ 2005.
+
+ [RFC4510] Zeilenga, K., Ed., "Lightweight Directory Access
+ Protocol (LDAP): Technical Specification Road Map", RFC
+ 4510, June 2006.
+
+ [RFC4512] Zeilenga, K., "Lightweight Directory Access Protocol
+ (LDAP): Directory Information Models", RFC 4512, June
+ 2006.
+
+ [RFC4517] Legg, S., Ed., "Lightweight Directory Access Protocol
+ (LDAP): Syntaxes and Matching Rules", RFC 4517, June
+ 2006.
+
+ [ASCII] Coded Character Set--7-bit American Standard Code for
+ Information Interchange, ANSI X3.4-1986.
+
+
+
+
+Zeilenga Standards Track [Page 6]
+
+RFC 4530 LDAP entryUUID June 2006
+
+
+ [X.501] International Telecommunication Union -
+ Telecommunication Standardization Sector, "The
+ Directory -- Models," X.501(1993) (also ISO/IEC 9594-
+ 2:1994).
+
+ [X.520] International Telecommunication Union -
+ Telecommunication Standardization Sector, "The
+ Directory: Selected Attribute Types", X.520(1993) (also
+ ISO/IEC 9594-6:1994).
+
+ [X.680] International Telecommunication Union -
+ Telecommunication Standardization Sector, "Abstract
+ Syntax Notation One (ASN.1) - Specification of Basic
+ Notation", X.680(2002) (also ISO/IEC 8824-1:2002).
+
+6.2. Informative References
+
+ [RFC4514] Zeilenga, K., Ed., "Lightweight Directory Access
+ Protocol (LDAP): String Representation of Distinguished
+ Names", RFC 4514, June 2006.
+
+ [RFC4520] Zeilenga, K., "Internet Assigned Numbers Authority
+ (IANA) Considerations for the Lightweight Directory
+ Access Protocol (LDAP)", BCP 64, RFC 4520, June 2006.
+
+Author's Address
+
+ Kurt D. Zeilenga
+ OpenLDAP Foundation
+
+ EMail: Kurt@OpenLDAP.org
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 7]
+
+RFC 4530 LDAP entryUUID June 2006
+
+
+Full Copyright Statement
+
+ Copyright (C) The Internet Society (2006).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
+ ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
+ INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
+ INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be
+ found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at
+ ietf-ipr@ietf.org.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is provided by the IETF
+ Administrative Support Activity (IASA).
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 8]
+
diff --git a/source4/ldap_server/devdocs/rfc4531.txt b/source4/ldap_server/devdocs/rfc4531.txt
new file mode 100644
index 0000000000..b551d441cb
--- /dev/null
+++ b/source4/ldap_server/devdocs/rfc4531.txt
@@ -0,0 +1,507 @@
+
+
+
+
+
+
+Network Working Group K. Zeilenga
+Request for Comments: 4531 OpenLDAP Foundation
+Category: Experimental June 2006
+
+
+ Lightweight Directory Access Protocol (LDAP)
+ Turn Operation
+
+
+Status of This Memo
+
+ This memo defines an Experimental Protocol for the Internet
+ community. It does not specify an Internet standard of any kind.
+ Discussion and suggestions for improvement are requested.
+ Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2006).
+
+Abstract
+
+ This specification describes a Lightweight Directory Access Protocol
+ (LDAP) extended operation to reverse (or "turn") the roles of client
+ and server for subsequent protocol exchanges in the session, or to
+ enable each peer to act as both client and server with respect to the
+ other.
+
+Table of Contents
+
+ 1. Background and Intent of Use ....................................2
+ 1.1. Terminology ................................................2
+ 2. Turn Operation ..................................................2
+ 2.1. Turn Request ...............................................3
+ 2.2. Turn Response ..............................................3
+ 3. Authentication ..................................................3
+ 3.1. Use with TLS and Simple Authentication .....................4
+ 3.2. Use with TLS and SASL EXTERNAL .............................4
+ 3.3. Use of Mutual Authentication and SASL EXTERNAL .............4
+ 4. TLS and SASL Security Layers ....................................5
+ 5. Security Considerations .........................................6
+ 6. IANA Considerations .............................................6
+ 6.1. Object Identifier ..........................................6
+ 6.2. LDAP Protocol Mechanism ....................................7
+ 7. References ......................................................7
+ 7.1. Normative References .......................................7
+ 7.2. Informative References .....................................8
+
+
+
+
+Zeilenga Experimental [Page 1]
+
+RFC 4531 LDAP Turn Operation June 2006
+
+
+1. Background and Intent of Use
+
+ The Lightweight Directory Access Protocol (LDAP) [RFC4510][RFC4511]
+ is a client-server protocol that typically operates over reliable
+ octet-stream transports, such as the Transport Control Protocol
+ (TCP). Generally, the client initiates the stream by connecting to
+ the server's listener at some well-known address.
+
+ There are cases where it is desirable for the server to initiate the
+ stream. Although it certainly is possible to write a technical
+ specification detailing how to implement server-initiated LDAP
+ sessions, this would require the design of new authentication and
+ other security mechanisms to support server-initiated LDAP sessions.
+
+ Instead, this document introduces an operation, the Turn operation,
+ which may be used to reverse the client-server roles of the protocol
+ peers. This allows the initiating protocol peer to become the server
+ (after the reversal).
+
+ As an additional feature, the Turn operation may be used to allow
+ both peers to act in both roles. This is useful where both peers are
+ directory servers that desire to request, as LDAP clients, that
+ operations be performed by the other. This may be useful in
+ replicated and/or distributed environments.
+
+ This operation is intended to be used between protocol peers that
+ have established a mutual agreement, by means outside of the
+ protocol, that requires reversal of client-server roles, or allows
+ both peers to act both as client and server.
+
+1.1. Terminology
+
+ Protocol elements are described using ASN.1 [X.680] with implicit
+ tags. The term "BER-encoded" means the element is to be encoded
+ using the Basic Encoding Rules [X.690] under the restrictions
+ detailed in Section 5.1 of [RFC4511].
+
+2. Turn Operation
+
+ The Turn operation is defined as an LDAP-Extended Operation
+ [Protocol, Section 4.12] identified by the 1.3.6.1.1.19 OID. The
+ function of the Turn Operation is to request that the client-server
+ roles be reversed, or, optionally, to request that both protocol
+ peers be able to act both as client and server in respect to the
+ other.
+
+
+
+
+
+
+Zeilenga Experimental [Page 2]
+
+RFC 4531 LDAP Turn Operation June 2006
+
+
+2.1. Turn Request
+
+ The Turn request is an ExtendedRequest where the requestName field
+ contains the 1.3.6.1.1.19 OID and the requestValue field is a BER-
+ encoded turnValue:
+
+ turnValue ::= SEQUENCE {
+ mutual BOOLEAN DEFAULT FALSE,
+ identifier LDAPString
+ }
+
+ A TRUE mutual field value indicates a request to allow both peers to
+ act both as client and server. A FALSE mutual field value indicates
+ a request to reserve the client and server roles.
+
+ The value of the identifier field is a locally defined policy
+ identifier (typically associated with a mutual agreement for which
+ this turn is be executed as part of).
+
+2.2. Turn Response
+
+ A Turn response is an ExtendedResponse where the responseName and
+ responseValue fields are absent. A resultCode of success is returned
+ if and only if the responder is willing and able to turn the session
+ as requested. Otherwise, a different resultCode is returned.
+
+3. Authentication
+
+ This extension's authentication model assumes separate authentication
+ of the peers in each of their roles. A separate Bind exchange is
+ expected between the peers in their new roles to establish identities
+ in these roles.
+
+ Upon completion of the Turn, the responding peer in its new client
+ role has an anonymous association at the initiating peer in its new
+ server role. If the turn was mutual, the authentication association
+ of the initiating peer in its pre-existing client role is left intact
+ at the responding peer in its pre-existing server role. If the turn
+ was not mutual, this association is void.
+
+ The responding peer may establish its identity in its client role by
+ requesting and successfully completing a Bind operation.
+
+ The remainder of this section discusses some authentication
+ scenarios. In the protocol exchange illustrations, A refers to the
+ initiating peer (the original client) and B refers to the responding
+ peer (the original server).
+
+
+
+
+Zeilenga Experimental [Page 3]
+
+RFC 4531 LDAP Turn Operation June 2006
+
+
+3.1. Use with TLS and Simple Authentication
+
+ A->B: StartTLS Request
+ B->A: StartTLS(success) Response
+ A->B: Bind(Simple(cn=B,dc=example,dc=net,B's secret)) Request
+ B->A: Bind(success) Response
+ A->B: Turn(TRUE,"XXYYZ") Request
+ B->A: Turn(success) Response
+ B->A: Bind(Simple(cn=A,dc=example,dc=net,A's secret)) Request
+ A->B: Bind(success) Response
+
+ In this scenario, TLS (Transport Layer Security) [RFC4346] is started
+ and the initiating peer (the original client) establishes its
+ identity with the responding peer prior to the Turn using the
+ DN/password mechanism of the Simple method of the Bind operation.
+ After the turn, the responding peer, in its new client role,
+ establishes its identity with the initiating peer in its new server
+ role.
+
+3.2. Use with TLS and SASL EXTERNAL
+
+ A->B: StartTLS Request
+ B->A: StartTLS(success) Response
+ A->B: Bind(SASL(EXTERNAL)) Request
+ B->A: Bind(success) Response
+ A->B: Turn(TRUE,"XXYYZ") Request
+ B->A: Turn(success) Response
+ B->A: Bind(SASL(EXTERNAL)) Request
+ A->B: Bind(success) Response
+
+ In this scenario, TLS is started (with each peer providing a valid
+ certificate), and the initiating peer (the original client)
+ establishes its identity through the use of the EXTERNAL mechanism of
+ the SASL (Simple Authentication and Security Layer) [RFC4422] method
+ of the Bind operation prior to the Turn. After the turn, the
+ responding peer, in its new client role, establishes its identity
+ with the initiating peer in its new server role.
+
+3.3. Use of Mutual Authentication and SASL EXTERNAL
+
+ A number of SASL mechanisms, such as GSSAPI [SASL-K5], support mutual
+ authentication. The initiating peer, in its new server role, may use
+ the identity of the responding peer, established by a prior
+ authentication exchange, as its source for "external" identity in
+ subsequent EXTERNAL exchange.
+
+ A->B: Bind(SASL(GSSAPI)) Request
+ <intermediate messages>
+
+
+
+Zeilenga Experimental [Page 4]
+
+RFC 4531 LDAP Turn Operation June 2006
+
+
+ B->A: Bind(success) Response
+ A->B: Turn(TRUE,"XXYYZ") Request
+ B->A: Turn(success) Response
+ B->A: Bind(SASL(EXTERNAL)) Request
+ A->B: Bind(success) Response
+
+ In this scenario, a GSSAPI mutual-authentication exchange is
+ completed between the initiating peer (the original client) and the
+ responding server (the original server) prior to the turn. After the
+ turn, the responding peer, in its new client role, requests that the
+ initiating peer utilize an "external" identity to establish its LDAP
+ authorization identity.
+
+4. TLS and SASL Security Layers
+
+ As described in [RFC4511], LDAP supports both Transport Layer
+ Security (TLS) [RFC4346] and Simple Authentication and Security Layer
+ (SASL) [RFC4422] security frameworks. The following table
+ illustrates the relationship between the LDAP message layer, SASL
+ layer, TLS layer, and transport connection within an LDAP session.
+
+ +----------------------+
+ | LDAP message layer |
+ +----------------------+ > LDAP PDUs
+ +----------------------+ < data
+ | SASL layer |
+ +----------------------+ > SASL-protected data
+ +----------------------+ < data
+ | TLS layer |
+ Application +----------------------+ > TLS-protected data
+ ------------+----------------------+ < data
+ Transport | transport connection |
+ +----------------------+
+
+ This extension does not alter this relationship, nor does it remove
+ the general restriction against multiple TLS layers, nor does it
+ remove the general restriction against multiple SASL layers.
+
+ As specified in [RFC4511], the StartTLS operation is used to initiate
+ negotiation of a TLS layer. If a TLS is already installed, the
+ StartTLS operation must fail. Upon establishment of the TLS layer,
+ regardless of which peer issued the request to start TLS, the peer
+ that initiated the LDAP session (the original client) performs the
+ "server identity check", as described in Section 3.1.5 of [RFC4513],
+ treating itself as the "client" and its peer as the "server".
+
+ As specified in [RFC4422], a newly negotiated SASL security layer
+ replaces the installed SASL security layer. Though the client/server
+
+
+
+Zeilenga Experimental [Page 5]
+
+RFC 4531 LDAP Turn Operation June 2006
+
+
+ roles in LDAP, and hence SASL, may be reversed in subsequent
+ exchanges, only one SASL security layer may be installed at any
+ instance.
+
+5. Security Considerations
+
+ Implementors should be aware that the reversing of client/server
+ roles and/or allowing both peers to act as client and server likely
+ introduces security considerations not foreseen by the authors of
+ this document. In particular, the security implications of the
+ design choices made in the authentication and data security models
+ for this extension (discussed in Sections 3 and 4, respectively) are
+ not fully studied. It is hoped that experimentation with this
+ extension will lead to better understanding of the security
+ implications of these models and other aspects of this extension, and
+ that appropriate considerations will be documented in a future
+ document. The following security considerations are apparent at this
+ time.
+
+ Implementors should take special care to process LDAP, SASL, TLS, and
+ other events in the appropriate roles for the peers. Note that while
+ the Turn reverses the client/server roles with LDAP, and in SASL
+ authentication exchanges, it does not reverse the roles within the
+ TLS layer or the transport connection.
+
+ The responding server (the original server) should restrict use of
+ this operation to authorized clients. Client knowledge of a valid
+ identifier should not be the sole factor in determining authorization
+ to turn.
+
+ Where the peers except to establish TLS, TLS should be started prior
+ to the Turn and any request to authenticate via the Bind operation.
+
+ LDAP security considerations [RFC4511][RFC4513] generally apply to
+ this extension.
+
+6. IANA Considerations
+
+ The following values [RFC4520] have been registered by the IANA.
+
+6.1. Object Identifier
+
+ The IANA has assigned an LDAP Object Identifier to identify the LDAP
+ Turn Operation, as defined in this document.
+
+
+
+
+
+
+
+Zeilenga Experimental [Page 6]
+
+RFC 4531 LDAP Turn Operation June 2006
+
+
+ Subject: Request for LDAP Object Identifier Registration
+ Person & email address to contact for further information:
+ Kurt Zeilenga <kurt@OpenLDAP.org>
+ Specification: RFC 4531
+ Author/Change Controller: Author
+ Comments:
+ Identifies the LDAP Turn Operation
+
+6.2. LDAP Protocol Mechanism
+
+ The IANA has registered the LDAP Protocol Mechanism described in this
+ document.
+
+ Subject: Request for LDAP Protocol Mechanism Registration
+ Object Identifier: 1.3.6.1.1.19
+ Description: LDAP Turn Operation
+ Person & email address to contact for further information:
+ Kurt Zeilenga <kurt@openldap.org>
+ Usage: Extended Operation
+ Specification: RFC 4531
+ Author/Change Controller: Author
+ Comments: none
+
+7. References
+
+7.1. Normative References
+
+ [RFC4346] Dierks, T. and, E. Rescorla, "The Transport Layer
+ Security (TLS) Protocol Version 1.1", RFC 4346, April
+ 2006.
+
+ [RFC4422] Melnikov, A., Ed. and K. Zeilenga, Ed., "Simple
+ Authentication and Security Layer (SASL)", RFC 4422,
+ June 2006.
+
+ [RFC4510] Zeilenga, K., Ed., "Lightweight Directory Access
+ Protocol (LDAP): Technical Specification Road Map", RFC
+ 4510, June 2006.
+
+ [RFC4511] Sermersheim, J., Ed., "Lightweight Directory Access
+ Protocol (LDAP): The Protocol", RFC 4511, June 2006.
+
+ [RFC4513] Harrison, R., Ed., "Lightweight Directory Access
+ Protocol (LDAP): Authentication Methods and Security
+ Mechanisms", RFC 4513, June 2006.
+
+
+
+
+
+
+Zeilenga Experimental [Page 7]
+
+RFC 4531 LDAP Turn Operation June 2006
+
+
+ [X.680] International Telecommunication Union -
+ Telecommunication Standardization Sector, "Abstract
+ Syntax Notation One (ASN.1) - Specification of Basic
+ Notation", X.680(2002) (also ISO/IEC 8824-1:2002).
+
+ [X.690] International Telecommunication Union -
+ Telecommunication Standardization Sector,
+ "Specification of ASN.1 encoding rules: Basic Encoding
+ Rules (BER), Canonical Encoding Rules (CER), and
+ Distinguished Encoding Rules (DER)", X.690(2002) (also
+ ISO/IEC 8825-1:2002).
+
+7.2. Informative References
+
+ [RFC4520] Zeilenga, K., "Internet Assigned Numbers Authority
+ (IANA) Considerations for the Lightweight Directory
+ Access Protocol (LDAP)", BCP 64, RFC 4520, June 2006.
+
+ [SASL-K5] Melnikov, A., Ed., "The Kerberos V5 ("GSSAPI") SASL
+ Mechanism", Work in Progress, May 2006.
+
+Author's Address
+
+ Kurt D. Zeilenga
+ OpenLDAP Foundation
+
+ EMail: Kurt@OpenLDAP.org
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Zeilenga Experimental [Page 8]
+
+RFC 4531 LDAP Turn Operation June 2006
+
+
+Full Copyright Statement
+
+ Copyright (C) The Internet Society (2006).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
+ ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
+ INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
+ INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be
+ found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at
+ ietf-ipr@ietf.org.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is provided by the IETF
+ Administrative Support Activity (IASA).
+
+
+
+
+
+
+
+Zeilenga Experimental [Page 9]
+
diff --git a/source4/ldap_server/devdocs/rfc4532.txt b/source4/ldap_server/devdocs/rfc4532.txt
new file mode 100644
index 0000000000..277b3b7442
--- /dev/null
+++ b/source4/ldap_server/devdocs/rfc4532.txt
@@ -0,0 +1,395 @@
+
+
+
+
+
+
+Network Working Group K. Zeilenga
+Request for Comments: 4532 OpenLDAP Foundation
+Category: Standards Track June 2006
+
+
+ Lightweight Directory Access Protocol (LDAP)
+ "Who am I?" Operation
+
+Status of This Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2006).
+
+Abstract
+
+ This specification provides a mechanism for Lightweight Directory
+ Access Protocol (LDAP) clients to obtain the authorization identity
+ the server has associated with the user or application entity. This
+ mechanism is specified as an LDAP extended operation called the LDAP
+ "Who am I?" operation.
+
+1. Background and Intent of Use
+
+ This specification describes a Lightweight Directory Access Protocol
+ (LDAP) [RFC4510] operation that clients can use to obtain the primary
+ authorization identity, in its primary form, that the server has
+ associated with the user or application entity. The operation is
+ called the "Who am I?" operation.
+
+ This specification is intended to replace the existing Authorization
+ Identity Controls [RFC3829] mechanism, which uses Bind request and
+ response controls to request and return the authorization identity.
+ Bind controls are not protected by security layers established by the
+ Bind operation that includes them. While it is possible to establish
+ security layers using StartTLS [RFC4511][RFC4513] prior to the Bind
+ operation, it is often desirable to use security layers established
+ by the Bind operation. An extended operation sent after a Bind
+ operation is protected by the security layers established by the Bind
+ operation.
+
+
+
+
+
+Zeilenga Standards Track [Page 1]
+
+RFC 4532 LDAP "Who am I?" Operation June 2006
+
+
+ There are other cases where it is desirable to request the
+ authorization identity that the server associated with the client
+ separately from the Bind operation. For example, the "Who am I?"
+ operation can be augmented with a Proxied Authorization Control
+ [RFC4370] to determine the authorization identity that the server
+ associates with the identity asserted in the Proxied Authorization
+ Control. The "Who am I?" operation can also be used prior to the
+ Bind operation.
+
+ Servers often associate multiple authorization identities with the
+ client, and each authorization identity may be represented by
+ multiple authzId [RFC4513] strings. This operation requests and
+ returns the authzId that the server considers primary. In the
+ specification, the term "the authorization identity" and "the
+ authzId" are generally to be read as "the primary authorization
+ identity" and the "the primary authzId", respectively.
+
+1.1. Conventions Used in This Document
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in BCP 14 [RFC2119].
+
+2. The "Who am I?" Operation
+
+ The "Who am I?" operation is defined as an LDAP Extended Operation
+ [RFC4511] identified by the whoamiOID Object Identifier (OID). This
+ section details the syntax of the operation's whoami request and
+ response messages.
+
+ whoamiOID ::= "1.3.6.1.4.1.4203.1.11.3"
+
+2.1. The whoami Request
+
+ The whoami request is an ExtendedRequest with a requestName field
+ containing the whoamiOID OID and an absent requestValue field. For
+ example, a whoami request could be encoded as the sequence of octets
+ (in hex):
+
+ 30 1e 02 01 02 77 19 80 17 31 2e 33 2e 36 2e 31
+ 2e 34 2e 31 2e 34 32 30 33 2e 31 2e 31 31 2e 33
+
+
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 2]
+
+RFC 4532 LDAP "Who am I?" Operation June 2006
+
+
+2.2. The whoami Response
+
+ The whoami response is an ExtendedResponse where the responseName
+ field is absent and the response field, if present, is empty or an
+ authzId [RFC4513]. For example, a whoami response returning the
+ authzId "u:xxyyz@EXAMPLE.NET" (in response to the example request)
+ would be encoded as the sequence of octets (in hex):
+
+ 30 21 02 01 02 78 1c 0a 01 00 04 00 04 00 8b 13
+ 75 3a 78 78 79 79 7a 40 45 58 41 4d 50 4c 45 2e
+ 4e 45 54
+
+3. Operational Semantics
+
+ The "Who am I?" operation provides a mechanism, a whoami Request, for
+ the client to request that the server return the authorization
+ identity it currently associates with the client. It also provides a
+ mechanism, a whoami Response, for the server to respond to that
+ request.
+
+ Servers indicate their support for this extended operation by
+ providing a whoamiOID object identifier as a value of the
+ 'supportedExtension' attribute type in their root DSE. The server
+ SHOULD advertise this extension only when the client is willing and
+ able to perform this operation.
+
+ If the server is willing and able to provide the authorization
+ identity it associates with the client, the server SHALL return a
+ whoami Response with a success resultCode. If the server is treating
+ the client as an anonymous entity, the response field is present but
+ empty. Otherwise, the server provides the authzId [RFC4513]
+ representing the authorization identity it currently associates with
+ the client in the response field.
+
+ If the server is unwilling or unable to provide the authorization
+ identity it associates with the client, the server SHALL return a
+ whoami Response with an appropriate non-success resultCode (such as
+ operationsError, protocolError, confidentialityRequired,
+ insufficientAccessRights, busy, unavailable, unwillingToPerform, or
+ other) and an absent response field.
+
+ As described in [RFC4511] and [RFC4513], an LDAP session has an
+ "anonymous" association until the client has been successfully
+ authenticated using the Bind operation. Clients MUST NOT invoke the
+ "Who am I?" operation while any Bind operation is in progress,
+ including between two Bind requests made as part of a multi-stage
+
+
+
+
+
+Zeilenga Standards Track [Page 3]
+
+RFC 4532 LDAP "Who am I?" Operation June 2006
+
+
+ Bind operation. Where a whoami Request is received in violation of
+ this absolute prohibition, the server should return a whoami Response
+ with an operationsError resultCode.
+
+4. Extending the "Who am I?" Operation with Controls
+
+ Future specifications may extend the "Who am I?" operation using the
+ control mechanism [RFC4511]. When extended by controls, the "Who am
+ I?" operation requests and returns the authorization identity the
+ server associates with the client in a particular context indicated
+ by the controls.
+
+4.1. Proxied Authorization Control
+
+ The Proxied Authorization Control [RFC4370] is used by clients to
+ request that the operation it is attached to operate under the
+ authorization of an assumed identity. The client provides the
+ identity to assume in the Proxied Authorization request control. If
+ the client is authorized to assume the requested identity, the server
+ executes the operation as if the requested identity had issued the
+ operation.
+
+ As servers often map the asserted authzId to another identity
+ [RFC4513], it is desirable to request that the server provide the
+ authzId it associates with the assumed identity.
+
+ When a Proxied Authorization Control is be attached to the "Who am
+ I?" operation, the operation requests the return of the authzId the
+ server associates with the identity asserted in the Proxied
+ Authorization Control. The authorizationDenied (123) result code is
+ used to indicate that the server does not allow the client to assume
+ the asserted identity.
+
+5. Security Considerations
+
+ Identities associated with users may be sensitive information. When
+ they are, security layers [RFC4511][RFC4513] should be established to
+ protect this information. This mechanism is specifically designed to
+ allow security layers established by a Bind operation to protect the
+ integrity and/or confidentiality of the authorization identity.
+
+ Servers may place access control or other restrictions upon the use
+ of this operation. As stated in Section 3, the server SHOULD
+ advertise this extension when it is willing and able to perform the
+ operation.
+
+ As with any other extended operations, general LDAP security
+ considerations [RFC4510] apply.
+
+
+
+Zeilenga Standards Track [Page 4]
+
+RFC 4532 LDAP "Who am I?" Operation June 2006
+
+
+6. IANA Considerations
+
+ The OID 1.3.6.1.4.1.4203.1.11.3 is used to identify the LDAP "Who am
+ I?" extended operation. This OID was assigned [ASSIGN] by the
+ OpenLDAP Foundation, under its IANA-assigned private enterprise
+ allocation [PRIVATE], for use in this specification.
+
+ Registration of this protocol mechanism [RFC4520] has been completed
+ by the IANA.
+
+ Subject: Request for LDAP Protocol Mechanism Registration
+ Object Identifier: 1.3.6.1.4.1.4203.1.11.3
+ Description: Who am I?
+ Person & email address to contact for further information:
+ Kurt Zeilenga <kurt@openldap.org>
+ Usage: Extended Operation
+ Specification: RFC 4532
+ Author/Change Controller: IESG
+ Comments: none
+
+7. Acknowledgement
+
+ This document borrows from prior work in this area, including
+ "Authentication Response Control" [RFC3829] by Rob Weltman, Mark
+ Smith, and Mark Wahl.
+
+ The LDAP "Who am I?" operation takes it's name from the UNIX
+ whoami(1) command. The whoami(1) command displays the effective user
+ ID.
+
+8. References
+
+8.1. Normative References
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [RFC4370] Weltman, R., "Lightweight Directory Access Protocol (LDAP)
+ Proxied Authorization Control", RFC 4370, February 2006.
+
+ [RFC4510] Zeilenga, K., Ed., "Lightweight Directory Access Protocol
+ (LDAP): Technical Specification Road Map", RFC 4510, June
+ 2006.
+
+ [RFC4511] Sermersheim, J., Ed., "Lightweight Directory Access
+ Protocol (LDAP): The Protocol", RFC 4511, June 2006.
+
+
+
+
+
+Zeilenga Standards Track [Page 5]
+
+RFC 4532 LDAP "Who am I?" Operation June 2006
+
+
+ [RFC4513] Harrison, R., Ed., "Lightweight Directory Access Protocol
+ (LDAP): Authentication Methods and Security Mechanisms",
+ RFC 4513, June 2006.
+
+8.2. Informative References
+
+ [RFC3829] Weltman, R., Smith, M., and M. Wahl, "Lightweight Directory
+ Access Protocol (LDAP) Authorization Identity Request and
+ Response Controls", RFC 3829, July 2004.
+
+ [RFC4520] Zeilenga, K., "Internet Assigned Numbers Authority (IANA)
+ Considerations for the Lightweight Directory Access
+ Protocol (LDAP)", BCP 64, RFC 4520, June 2006.
+
+ [ASSIGN] OpenLDAP Foundation, "OpenLDAP OID Delegations",
+ http://www.openldap.org/foundation/oid-delegate.txt.
+
+ [PRIVATE] IANA, "Private Enterprise Numbers",
+ http://www.iana.org/assignments/enterprise-numbers.
+
+Author's Address
+
+ Kurt D. Zeilenga
+ OpenLDAP Foundation
+
+ EMail: Kurt@OpenLDAP.org
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 6]
+
+RFC 4532 LDAP "Who am I?" Operation June 2006
+
+
+Full Copyright Statement
+
+ Copyright (C) The Internet Society (2006).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
+ ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
+ INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
+ INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be
+ found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at
+ ietf-ipr@ietf.org.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is provided by the IETF
+ Administrative Support Activity (IASA).
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 7]
+
diff --git a/source4/ldap_server/devdocs/rfc4533.txt b/source4/ldap_server/devdocs/rfc4533.txt
new file mode 100644
index 0000000000..5f507ceae8
--- /dev/null
+++ b/source4/ldap_server/devdocs/rfc4533.txt
@@ -0,0 +1,1795 @@
+
+
+
+
+
+
+Network Working Group K. Zeilenga
+Request for Comments: 4533 OpenLDAP Foundation
+Category: Experimental J.H. Choi
+ IBM Corporation
+ June 2006
+
+
+ The Lightweight Directory Access Protocol (LDAP)
+ Content Synchronization Operation
+
+Status of This Memo
+
+ This memo defines an Experimental Protocol for the Internet
+ community. It does not specify an Internet standard of any kind.
+ Discussion and suggestions for improvement are requested.
+ Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2006).
+
+IESG Note
+
+ The IESG notes that this work was originally discussed in the LDUP
+ working group. The group came to consensus on a different approach,
+ documented in RFC 3928; that document is on the standards track and
+ should be reviewed by those considering implementation of this
+ proposal.
+
+Abstract
+
+ This specification describes the Lightweight Directory Access
+ Protocol (LDAP) Content Synchronization Operation. The operation
+ allows a client to maintain a copy of a fragment of the Directory
+ Information Tree (DIT). It supports both polling for changes and
+ listening for changes. The operation is defined as an extension of
+ the LDAP Search Operation.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Zeilenga & Choi Experimental [Page 1]
+
+RFC 4533 LDAP Content Synchronization Operation June 2006
+
+
+Table of Contents
+
+ 1. Introduction ....................................................3
+ 1.1. Background .................................................3
+ 1.2. Intended Usage .............................................4
+ 1.3. Overview ...................................................5
+ 1.4. Conventions ................................................8
+ 2. Elements of the Sync Operation ..................................8
+ 2.1. Common ASN.1 Elements ......................................9
+ 2.2. Sync Request Control .......................................9
+ 2.3. Sync State Control ........................................10
+ 2.4. Sync Done Control .........................................10
+ 2.5. Sync Info Message .........................................11
+ 2.6. Sync Result Codes .........................................11
+ 3. Content Synchronization ........................................11
+ 3.1. Synchronization Session ...................................12
+ 3.2. Content Determination .....................................12
+ 3.3. refreshOnly Mode ..........................................13
+ 3.4. refreshAndPersist Mode ....................................16
+ 3.5. Search Request Parameters .................................17
+ 3.6. objectName ................................................18
+ 3.7. Canceling the Sync Operation ..............................19
+ 3.8. Refresh Required ..........................................19
+ 3.9. Chattiness Considerations .................................20
+ 3.10. Operation Multiplexing ...................................21
+ 4. Meta Information Considerations ................................22
+ 4.1. Entry DN ..................................................22
+ 4.2. Operational Attributes ....................................22
+ 4.3. Collective Attributes .....................................23
+ 4.4. Access and Other Administrative Controls ..................23
+ 5. Interaction with Other Controls ................................23
+ 5.1. ManageDsaIT Control .......................................24
+ 5.2. Subentries Control ........................................24
+ 6. Shadowing Considerations .......................................24
+ 7. Security Considerations ........................................25
+ 8. IANA Considerations ............................................26
+ 8.1. Object Identifier .........................................26
+ 8.2. LDAP Protocol Mechanism ...................................26
+ 8.3. LDAP Result Codes .........................................26
+ 9. Acknowledgements ...............................................26
+ 10. Normative References ..........................................27
+ 11. Informative References ........................................28
+ Appendix A. CSN-based Implementation Considerations ..............29
+
+
+
+
+
+
+
+
+Zeilenga & Choi Experimental [Page 2]
+
+RFC 4533 LDAP Content Synchronization Operation June 2006
+
+
+1. Introduction
+
+ The Lightweight Directory Access Protocol (LDAP) [RFC4510] provides a
+ mechanism, the search operation [RFC4511], that allows a client to
+ request directory content matching a complex set of assertions and to
+ request that the server return this content, subject to access
+ control and other restrictions, to the client. However, LDAP does
+ not provide (despite the introduction of numerous extensions in this
+ area) an effective and efficient mechanism for maintaining
+ synchronized copies of directory content. This document introduces a
+ new mechanism specifically designed to meet the content
+ synchronization requirements of sophisticated directory applications.
+
+ This document defines the LDAP Content Synchronization Operation, or
+ Sync Operation for short, which allows a client to maintain a
+ synchronized copy of a fragment of a Directory Information Tree
+ (DIT). The Sync Operation is defined as a set of controls and other
+ protocol elements that extend the Search Operation.
+
+1.1. Background
+
+ Over the years, a number of content synchronization approaches have
+ been suggested for use in LDAP directory services. These approaches
+ are inadequate for one or more of the following reasons:
+
+ - failure to ensure a reasonable level of convergence;
+
+ - failure to detect that convergence cannot be achieved (without
+ reload);
+
+ - require pre-arranged synchronization agreements;
+
+ - require the server to maintain histories of past changes to DIT
+ content and/or meta information;
+
+ - require the server to maintain synchronization state on a per-
+ client basis; and/or
+
+ - are overly chatty.
+
+ The Sync Operation provides eventual convergence of synchronized
+ content when possible and, when not, notification that a full reload
+ is required.
+
+ The Sync Operation does not require pre-arranged synchronization
+ agreements.
+
+
+
+
+
+Zeilenga & Choi Experimental [Page 3]
+
+RFC 4533 LDAP Content Synchronization Operation June 2006
+
+
+ The Sync Operation does not require that servers maintain or use any
+ history of past changes to the DIT or to meta information. However,
+ servers may maintain and use histories (e.g., change logs,
+ tombstones, DIT snapshots) to reduce the number of messages generated
+ and to reduce their size. As it is not always feasible to maintain
+ and use histories, the operation may be implemented using purely
+ (current) state-based approaches. The Sync Operation allows use of
+ either the state-based approach or the history-based approach on an
+ operation-by-operation basis to balance the size of history and the
+ amount of traffic. The Sync Operation also allows the combined use
+ of the state-based and the history-based approaches.
+
+ The Sync Operation does not require that servers maintain
+ synchronization state on a per-client basis. However, servers may
+ maintain and use per-client state information to reduce the number of
+ messages generated and the size of such messages.
+
+ A synchronization mechanism can be considered overly chatty when
+ synchronization traffic is not reasonably bounded. The Sync
+ Operation traffic is bounded by the size of updated (or new) entries
+ and the number of unchanged entries in the content. The operation is
+ designed to avoid full content exchanges, even when the history
+ information available to the server is insufficient to determine the
+ client's state. The operation is also designed to avoid transmission
+ of out-of-content history information, as its size is not bounded by
+ the content and it is not always feasible to transmit such history
+ information due to security reasons.
+
+ This document includes a number of non-normative appendices providing
+ additional information to server implementors.
+
+1.2. Intended Usage
+
+ The Sync Operation is intended to be used in applications requiring
+ eventually-convergent content synchronization. Upon completion of
+ each synchronization stage of the operation, all information to
+ construct a synchronized client copy of the content has been provided
+ to the client or the client has been notified that a complete content
+ reload is necessary. Except for transient inconsistencies due to
+ concurrent operation (or other) processing at the server, the client
+ copy is an accurate reflection of the content held by the server.
+ Transient inconsistencies will be resolved by subsequent
+ synchronization operations.
+
+
+
+
+
+
+
+
+Zeilenga & Choi Experimental [Page 4]
+
+RFC 4533 LDAP Content Synchronization Operation June 2006
+
+
+ Possible uses include the following:
+
+ - White page service applications may use the Sync Operation to
+ maintain a current copy of a DIT fragment, for example, a mail
+ user agent that uses the sync operation to maintain a local
+ copy of an enterprise address book.
+
+ - Meta-information engines may use the Sync Operation to maintain
+ a copy of a DIT fragment.
+
+ - Caching proxy services may use the Sync Operation to maintain a
+ coherent content cache.
+
+ - Lightweight master-slave replication between heterogeneous
+ directory servers. For example, the Sync Operation can be used
+ by a slave server to maintain a shadow copy of a DIT fragment.
+ (Note: The International Telephone Union (ITU) has defined the
+ X.500 Directory [X.500] Information Shadowing Protocol (DISP)
+ [X.525], which may be used for master-slave replication between
+ directory servers. Other experimental LDAP replication
+ protocols also exist.)
+
+ This protocol is not intended to be used in applications requiring
+ transactional data consistency.
+
+ As this protocol transfers all visible values of entries belonging to
+ the content upon change instead of change deltas, this protocol is
+ not appropriate for bandwidth-challenged applications or deployments.
+
+1.3. Overview
+
+ This section provides an overview of basic ways the Sync Operation
+ can be used to maintain a synchronized client copy of a DIT fragment.
+
+ - Polling for changes: refreshOnly mode
+
+ - Listening for changes: refreshAndPersist mode
+
+1.3.1. Polling for Changes (refreshOnly)
+
+ To obtain its initial client copy, the client issues a Sync request:
+ a search request with the Sync Request Control with mode set to
+ refreshOnly. The server, much like it would with a normal search
+ operation, returns (subject to access controls and other
+ restrictions) the content matching the search criteria (baseObject,
+ scope, filter, attributes). Additionally, with each entry returned,
+ the server provides a Sync State Control indicating state add. This
+ control contains the Universally Unique Identifier (UUID) [UUID] of
+
+
+
+Zeilenga & Choi Experimental [Page 5]
+
+RFC 4533 LDAP Content Synchronization Operation June 2006
+
+
+ the entry [RFC4530]. Unlike the Distinguished Name (DN), which may
+ change over time, an entry's UUID is stable. The initial content is
+ followed by a SearchResultDone with a Sync Done Control. The Sync
+ Done Control provides a syncCookie. The syncCookie represents
+ session state.
+
+ To poll for updates to the client copy, the client reissues the Sync
+ Operation with the syncCookie previously returned. The server, much
+ as it would with a normal search operation, determines which content
+ would be returned as if the operation were a normal search operation.
+ However, using the syncCookie as an indicator of what content the
+ client was sent previously, the server sends copies of entries that
+ have changed with a Sync State Control indicating state add. For
+ each changed entry, all (modified or unmodified) attributes belonging
+ to the content are sent.
+
+ The server may perform either or both of the two distinct
+ synchronization phases that are distinguished by how to synchronize
+ entries deleted from the content: the present and the delete phases.
+ When the server uses a single phase for the refresh stage, each phase
+ is marked as ended by a SearchResultDone with a Sync Done Control. A
+ present phase is identified by a FALSE refreshDeletes value in the
+ Sync Done Control. A delete phase is identified by a TRUE
+ refreshDeletes value. The present phase may be followed by a delete
+ phase. The two phases are delimited by a refreshPresent Sync Info
+ Message having a FALSE refreshDone value. In the case that both the
+ phases are used, the present phase is used to bring the client copy
+ up to the state at which the subsequent delete phase can begin.
+
+ In the present phase, the server sends an empty entry (i.e., no
+ attributes) with a Sync State Control indicating state present for
+ each unchanged entry.
+
+ The delete phase may be used when the server can reliably determine
+ which entries in the prior client copy are no longer present in the
+ content and the number of such entries is less than or equal to the
+ number of unchanged entries. In the delete mode, the server sends an
+ empty entry with a Sync State Control indicating state delete for
+ each entry that is no longer in the content, instead of returning an
+ empty entry with state present for each present entry.
+
+ The server may send syncIdSet Sync Info Messages containing the set
+ of UUIDs of either unchanged present entries or deleted entries,
+ instead of sending multiple individual messages. If refreshDeletes
+ of syncIdSet is set to FALSE, the UUIDs of unchanged present entries
+ are contained in the syncUUIDs set; if refreshDeletes of syncIdSet is
+ set to TRUE, the UUIDs of the entries no longer present in the
+ content are contained in the syncUUIDs set. An optional cookie can
+
+
+
+Zeilenga & Choi Experimental [Page 6]
+
+RFC 4533 LDAP Content Synchronization Operation June 2006
+
+
+ be included in the syncIdSet to represent the state of the content
+ after synchronizing the presence or the absence of the entries
+ contained in the syncUUIDs set.
+
+ The synchronized copy of the DIT fragment is constructed by the
+ client.
+
+ If refreshDeletes of syncDoneValue is FALSE, the new copy includes
+ all changed entries returned by the reissued Sync Operation, as well
+ as all unchanged entries identified as being present by the reissued
+ Sync Operation, but whose content is provided by the previous Sync
+ Operation. The unchanged entries not identified as being present are
+ deleted from the client content. They had been either deleted,
+ moved, or otherwise scoped-out from the content.
+
+ If refreshDeletes of syncDoneValue is TRUE, the new copy includes all
+ changed entries returned by the reissued Sync Operation, as well as
+ all other entries of the previous copy except for those that are
+ identified as having been deleted from the content.
+
+ The client can, at some later time, re-poll for changes to this
+ synchronized client copy.
+
+1.3.2. Listening for Changes (refreshAndPersist)
+
+ Polling for changes can be expensive in terms of server, client, and
+ network resources. The refreshAndPersist mode allows for active
+ updates of changed entries in the content.
+
+ By selecting the refreshAndPersist mode, the client requests that the
+ server send updates of entries that are changed after the initial
+ refresh content is determined. Instead of sending a SearchResultDone
+ Message as in polling, the server sends a Sync Info Message to the
+ client indicating that the refresh stage is complete and then enters
+ the persist stage. After receipt of this Sync Info Message, the
+ client will construct a synchronized copy as described in Section
+ 1.3.1.
+
+ The server may then send change notifications as the result of the
+ original Sync search request, which now remains persistent in the
+ server. For entries to be added to the returned content, the server
+ sends a SearchResultEntry (with attributes) with a Sync State Control
+ indicating state add. For entries to be deleted from the content,
+ the server sends a SearchResultEntry containing no attributes and a
+ Sync State Control indicating state delete. For entries to be
+ modified in the return content, the server sends a SearchResultEntry
+ (with attributes) with a Sync State Control indicating state modify.
+
+
+
+
+Zeilenga & Choi Experimental [Page 7]
+
+RFC 4533 LDAP Content Synchronization Operation June 2006
+
+
+ Upon modification of an entry, all (modified or unmodified)
+ attributes belonging to the content are sent.
+
+ Note that renaming an entry of the DIT may cause an add state change
+ where the entry is renamed into the content, a delete state change
+ where the entry is renamed out of the content, and a modify state
+ change where the entry remains in the content. Also note that a
+ modification of an entry of the DIT may cause an add, delete, or
+ modify state change to the content.
+
+ Upon receipt of a change notification, the client updates its copy of
+ the content.
+
+ If the server desires to update the syncCookie during the persist
+ stage, it may include the syncCookie in any Sync State Control or
+ Sync Info Message returned.
+
+ The operation persists until canceled [RFC3909] by the client or
+ terminated by the server. A Sync Done Control shall be attached to
+ SearchResultDone Message to provide a new syncCookie.
+
+1.4. Conventions
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in BCP 14 [RFC2119].
+
+ Protocol elements are described using ASN.1 [X.680] with implicit
+ tags. The term "BER-encoded" means the element is to be encoded
+ using the Basic Encoding Rules [X.690] under the restrictions
+ detailed in Section 5.1 of [RFC4511].
+
+2. Elements of the Sync Operation
+
+ The Sync Operation is defined as an extension to the LDAP Search
+ Operation [RFC4511] where the directory user agent (DUA or client)
+ submits a SearchRequest Message with a Sync Request Control and the
+ directory system agent (DSA or server) responds with zero or more
+ SearchResultEntry Messages, each with a Sync State Control; zero or
+ more SearchResultReference Messages, each with a Sync State Control;
+ zero or more Sync Info Intermediate Response Messages; and a
+ SearchResultDone Message with a Sync Done Control.
+
+ To allow clients to discover support for this operation, servers
+ implementing this operation SHOULD publish 1.3.6.1.4.1.4203.1.9.1.1
+ as a value of the 'supportedControl' attribute [RFC4512] of the root
+ DSA-specific entry (DSE). A server MAY choose to advertise this
+ extension only when the client is authorized to use it.
+
+
+
+Zeilenga & Choi Experimental [Page 8]
+
+RFC 4533 LDAP Content Synchronization Operation June 2006
+
+
+2.1. Common ASN.1 Elements
+
+2.1.1. syncUUID
+
+ The syncUUID data type is an OCTET STRING holding a 128-bit
+ (16-octet) Universally Unique Identifier (UUID) [UUID].
+
+ syncUUID ::= OCTET STRING (SIZE(16))
+ -- constrained to UUID
+
+2.1.2. syncCookie
+
+ The syncCookie is a notational convenience to indicate that, while
+ the syncCookie type is encoded as an OCTET STRING, its value is an
+ opaque value containing information about the synchronization session
+ and its state. Generally, the session information would include a
+ hash of the operation parameters that the server requires not be
+ changed and the synchronization state information would include a
+ commit (log) sequence number, a change sequence number, or a time
+ stamp. For convenience of description, the term "no cookie" refers
+ either to a null cookie or to a cookie with pre-initialized
+ synchronization state.
+
+ syncCookie ::= OCTET STRING
+
+2.2. Sync Request Control
+
+ The Sync Request Control is an LDAP Control [RFC4511] where the
+ controlType is the object identifier 1.3.6.1.4.1.4203.1.9.1.1 and the
+ controlValue, an OCTET STRING, contains a BER-encoded
+ syncRequestValue. The criticality field is either TRUE or FALSE.
+
+ syncRequestValue ::= SEQUENCE {
+ mode ENUMERATED {
+ -- 0 unused
+ refreshOnly (1),
+ -- 2 reserved
+ refreshAndPersist (3)
+ },
+ cookie syncCookie OPTIONAL,
+ reloadHint BOOLEAN DEFAULT FALSE
+ }
+
+ The Sync Request Control is only applicable to the SearchRequest
+ Message.
+
+
+
+
+
+
+Zeilenga & Choi Experimental [Page 9]
+
+RFC 4533 LDAP Content Synchronization Operation June 2006
+
+
+2.3. Sync State Control
+
+ The Sync State Control is an LDAP Control [RFC4511] where the
+ controlType is the object identifier 1.3.6.1.4.1.4203.1.9.1.2 and the
+ controlValue, an OCTET STRING, contains a BER-encoded syncStateValue.
+ The criticality is FALSE.
+
+ syncStateValue ::= SEQUENCE {
+ state ENUMERATED {
+ present (0),
+ add (1),
+ modify (2),
+ delete (3)
+ },
+ entryUUID syncUUID,
+ cookie syncCookie OPTIONAL
+ }
+
+ The Sync State Control is only applicable to SearchResultEntry and
+ SearchResultReference Messages.
+
+2.4. Sync Done Control
+
+ The Sync Done Control is an LDAP Control [RFC4511] where the
+ controlType is the object identifier 1.3.6.1.4.1.4203.1.9.1.3 and the
+ controlValue contains a BER-encoded syncDoneValue. The criticality
+ is FALSE (and hence absent).
+
+ syncDoneValue ::= SEQUENCE {
+ cookie syncCookie OPTIONAL,
+ refreshDeletes BOOLEAN DEFAULT FALSE
+ }
+
+ The Sync Done Control is only applicable to the SearchResultDone
+ Message.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Zeilenga & Choi Experimental [Page 10]
+
+RFC 4533 LDAP Content Synchronization Operation June 2006
+
+
+2.5. Sync Info Message
+
+ The Sync Info Message is an LDAP Intermediate Response Message
+ [RFC4511] where responseName is the object identifier
+ 1.3.6.1.4.1.4203.1.9.1.4 and responseValue contains a BER-encoded
+ syncInfoValue. The criticality is FALSE (and hence absent).
+
+ syncInfoValue ::= CHOICE {
+ newcookie [0] syncCookie,
+ refreshDelete [1] SEQUENCE {
+ cookie syncCookie OPTIONAL,
+ refreshDone BOOLEAN DEFAULT TRUE
+ },
+ refreshPresent [2] SEQUENCE {
+ cookie syncCookie OPTIONAL,
+ refreshDone BOOLEAN DEFAULT TRUE
+ },
+ syncIdSet [3] SEQUENCE {
+ cookie syncCookie OPTIONAL,
+ refreshDeletes BOOLEAN DEFAULT FALSE,
+ syncUUIDs SET OF syncUUID
+ }
+ }
+
+2.6. Sync Result Codes
+
+ The following LDAP resultCode [RFC4511] is defined:
+
+ e-syncRefreshRequired (4096)
+
+3. Content Synchronization
+
+ The Sync Operation is invoked when the client sends a SearchRequest
+ Message with a Sync Request Control.
+
+ The absence of a cookie or an initialized synchronization state in a
+ cookie indicates a request for initial content, while the presence of
+ a cookie representing a state of a client copy indicates a request
+ for a content update. Synchronization Sessions are discussed in
+ Section 3.1. Content Determination is discussed in Section 3.2.
+
+ The mode is either refreshOnly or refreshAndPersist. The refreshOnly
+ and refreshAndPersist modes are discussed in Sections 3.3 and 3.4,
+ respectively. The refreshOnly mode consists only of a refresh stage,
+ while the refreshAndPersist mode consists of a refresh stage and a
+ subsequent persist stage.
+
+
+
+
+
+Zeilenga & Choi Experimental [Page 11]
+
+RFC 4533 LDAP Content Synchronization Operation June 2006
+
+
+3.1. Synchronization Session
+
+ A sequence of Sync Operations where the last cookie returned by the
+ server for one operation is provided by the client in the next
+ operation is said to belong to the same Synchronization Session.
+
+ The client MUST specify the same content-controlling parameters (see
+ Section 3.5) in each Search Request of the session. The client
+ SHOULD also issue each Sync request of a session under the same
+ authentication and authorization associations with equivalent
+ integrity and protections. If the server does not recognize the
+ request cookie or the request is made under different associations or
+ non-equivalent protections, the server SHALL return the initial
+ content as if no cookie had been provided or return an empty content
+ with the e-syncRefreshRequired LDAP result code. The decision
+ between the return of the initial content and the return of the empty
+ content with the e-syncRefreshRequired result code MAY be based on
+ reloadHint in the Sync Request Control from the client. If the
+ server recognizes the request cookie as representing empty or initial
+ synchronization state of the client copy, the server SHALL return the
+ initial content.
+
+ A Synchronization Session may span multiple LDAP sessions between the
+ client and the server. The client SHOULD issue each Sync request of
+ a session to the same server. (Note: Shadowing considerations are
+ discussed in Section 6.)
+
+3.2. Content Determination
+
+ The content to be provided is determined by parameters of the Search
+ Request, as described in [RFC4511], and possibly other controls. The
+ same content parameters SHOULD be used in each Sync request of a
+ session. If different content is requested and the server is
+ unwilling or unable to process the request, the server SHALL return
+ the initial content as if no cookie had been provided or return an
+ empty content with the e-syncRefreshRequired LDAP result code. The
+ decision between the return of the initial content and the return of
+ the empty content with the e-syncRefreshRequired result code MAY be
+ based on reloadHint in the Sync Request Control from the client.
+
+ The content may not necessarily include all entries or references
+ that would be returned by a normal search operation, nor, for those
+ entries included, all attributes returned by a normal search. When
+ the server is unwilling or unable to provide synchronization for any
+ attribute for a set of entries, the server MUST treat all filter
+ components matching against these attributes as Undefined and MUST
+ NOT return these attributes in SearchResultEntry responses.
+
+
+
+
+Zeilenga & Choi Experimental [Page 12]
+
+RFC 4533 LDAP Content Synchronization Operation June 2006
+
+
+ Servers SHOULD support synchronization for all non-collective user-
+ application attributes for all entries.
+
+ The server may also return continuation references to other servers
+ or to itself. The latter is allowed as the server may partition the
+ entries it holds into separate synchronization contexts.
+
+ The client may chase all or some of these continuations, each as a
+ separate content synchronization session.
+
+3.3. refreshOnly Mode
+
+ A Sync request with mode refreshOnly and with no cookie is a poll for
+ initial content. A Sync request with mode refreshOnly and with a
+ cookie representing a synchronization state is a poll for content
+ update.
+
+3.3.1. Initial Content Poll
+
+ Upon receipt of the request, the server provides the initial content
+ using a set of zero or more SearchResultEntry and
+ SearchResultReference Messages followed by a SearchResultDone
+ Message.
+
+ Each SearchResultEntry Message SHALL include a Sync State Control of
+ state add, an entryUUID containing the entry's UUID, and no cookie.
+ Each SearchResultReference Message SHALL include a Sync State Control
+ of state add, an entryUUID containing the UUID associated with the
+ reference (normally the UUID of the associated named referral
+ [RFC3296] object), and no cookie. The SearchResultDone Message SHALL
+ include a Sync Done Control having refreshDeletes set to FALSE.
+
+ A resultCode value of success indicates that the operation
+ successfully completed. Otherwise, the result code indicates the
+ nature of the failure. The server may return e-syncRefreshRequired
+ result code on the initial content poll if it is safe to do so when
+ it is unable to perform the operation due to various reasons.
+ reloadHint is set to FALSE in the SearchRequest Message requesting
+ the initial content poll.
+
+ If the operation is successful, a cookie representing the
+ synchronization state of the current client copy SHOULD be returned
+ for use in subsequent Sync Operations.
+
+3.3.2. Content Update Poll
+
+ Upon receipt of the request, the server provides the content refresh
+ using a set of zero or more SearchResultEntry and
+
+
+
+Zeilenga & Choi Experimental [Page 13]
+
+RFC 4533 LDAP Content Synchronization Operation June 2006
+
+
+ SearchResultReference Messages followed by a SearchResultDone
+ Message.
+
+ The server is REQUIRED to:
+
+ a) provide the sequence of messages necessary for eventual
+ convergence of the client's copy of the content to the server's
+ copy,
+
+ b) treat the request as an initial content request (e.g., ignore
+ the cookie or the synchronization state represented in the
+ cookie),
+
+ c) indicate that the incremental convergence is not possible by
+ returning e-syncRefreshRequired,
+
+ d) return a resultCode other than success or e-
+ syncRefreshRequired.
+
+ A Sync Operation may consist of a single present phase, a single
+ delete phase, or a present phase followed by a delete phase.
+
+ In each phase, for each entry or reference that has been added to the
+ content or been changed since the previous Sync Operation indicated
+ by the cookie, the server returns a SearchResultEntry or
+ SearchResultReference Message, respectively, each with a Sync State
+ Control consisting of state add, an entryUUID containing the UUID of
+ the entry or reference, and no cookie. Each SearchResultEntry
+ Message represents the current state of a changed entry. Each
+ SearchResultReference Message represents the current state of a
+ changed reference.
+
+ In the present phase, for each entry that has not been changed since
+ the previous Sync Operation, an empty SearchResultEntry is returned
+ whose objectName reflects the entry's current DN, whose attributes
+ field is empty, and whose Sync State Control consists of state
+ present, an entryUUID containing the UUID of the entry, and no
+ cookie. For each reference that has not been changed since the
+ previous Sync Operation, an empty SearchResultReference containing an
+ empty SEQUENCE OF LDAPURL is returned with a Sync State Control
+ consisting of state present, an entryUUID containing the UUID of the
+ entry, and no cookie. No messages are sent for entries or references
+ that are no longer in the content.
+
+ Multiple empty entries with a Sync State Control of state present
+ SHOULD be coalesced into one or more Sync Info Messages of syncIdSet
+ value with refreshDeletes set to FALSE. syncUUIDs contain a set of
+ UUIDs of the entries and references unchanged since the last Sync
+
+
+
+Zeilenga & Choi Experimental [Page 14]
+
+RFC 4533 LDAP Content Synchronization Operation June 2006
+
+
+ Operation. syncUUIDs may be empty. The Sync Info Message of
+ syncIdSet may contain a cookie to represent the state of the content
+ after performing the synchronization of the entries in the set.
+
+ In the delete phase, for each entry no longer in the content, the
+ server returns a SearchResultEntry whose objectName reflects a past
+ DN of the entry or is empty, whose attributes field is empty, and
+ whose Sync State Control consists of state delete, an entryUUID
+ containing the UUID of the deleted entry, and no cookie. For each
+ reference no longer in the content, a SearchResultReference
+ containing an empty SEQUENCE OF LDAPURL is returned with a Sync State
+ Control consisting of state delete, an entryUUID containing the UUID
+ of the deleted reference, and no cookie.
+
+ Multiple empty entries with a Sync State Control of state delete
+ SHOULD be coalesced into one or more Sync Info Messages of syncIdSet
+ value with refreshDeletes set to TRUE. syncUUIDs contain a set of
+ UUIDs of the entries and references that have been deleted from the
+ content since the last Sync Operation. syncUUIDs may be empty. The
+ Sync Info Message of syncIdSet may contain a cookie to represent the
+ state of the content after performing the synchronization of the
+ entries in the set.
+
+ When a present phase is followed by a delete phase, the two phases
+ are delimited by a Sync Info Message containing syncInfoValue of
+ refreshPresent, which may contain a cookie representing the state
+ after completing the present phase. The refreshPresent contains
+ refreshDone, which is always FALSE in the refreshOnly mode of Sync
+ Operation because it is followed by a delete phase.
+
+ If a Sync Operation consists of a single phase, each phase and hence
+ the Sync Operation are marked as ended by a SearchResultDone Message
+ with Sync Done Control, which SHOULD contain a cookie representing
+ the state of the content after completing the Sync Operation. The
+ Sync Done Control contains refreshDeletes, which is set to FALSE for
+ the present phase and set to TRUE for the delete phase.
+
+ If a Sync Operation consists of a present phase followed by a delete
+ phase, the Sync Operation is marked as ended at the end of the delete
+ phase by a SearchResultDone Message with Sync Done Control, which
+ SHOULD contain a cookie representing the state of the content after
+ completing the Sync Operation. The Sync Done Control contains
+ refreshDeletes, which is set to TRUE.
+
+ The client can specify whether it prefers to receive an initial
+ content by supplying reloadHint of TRUE or to receive a e-
+ syncRefreshRequired resultCode by supplying reloadHint of FALSE
+ (hence absent), in the case that the server determines that it is
+
+
+
+Zeilenga & Choi Experimental [Page 15]
+
+RFC 4533 LDAP Content Synchronization Operation June 2006
+
+
+ impossible or inefficient to achieve the eventual convergence by
+ continuing the current incremental synchronization thread.
+
+ A resultCode value of success indicates that the operation is
+ successfully completed. A resultCode value of e-syncRefreshRequired
+ indicates that a full or partial refresh is needed. Otherwise, the
+ result code indicates the nature of failure. A cookie is provided in
+ the Sync Done Control for use in subsequent Sync Operations for
+ incremental synchronization.
+
+3.4. refreshAndPersist Mode
+
+ A Sync request with mode refreshAndPersist asks for initial content
+ or content update (during the refresh stage) followed by change
+ notifications (during the persist stage).
+
+3.4.1. refresh Stage
+
+ The content refresh is provided as described in Section 3.3, except
+ that the successful completion of content refresh is indicated by
+ sending a Sync Info Message of refreshDelete or refreshPresent with a
+ refreshDone value set to TRUE instead of a SearchResultDone Message
+ with resultCode success. A cookie SHOULD be returned in the Sync
+ Info Message to represent the state of the content after finishing
+ the refresh stage of the Sync Operation.
+
+3.4.2. persist Stage
+
+ Change notifications are provided during the persist stage.
+
+ As updates are made to the DIT, the server notifies the client of
+ changes to the content. DIT updates may cause entries and references
+ to be added to the content, deleted from the content, or modified
+ within the content. DIT updates may also cause references to be
+ added, deleted, or modified within the content.
+
+ Where DIT updates cause an entry to be added to the content, the
+ server provides a SearchResultEntry Message that represents the entry
+ as it appears in the content. The message SHALL include a Sync State
+ Control with state of add, an entryUUID containing the entry's UUID,
+ and an optional cookie.
+
+ Where DIT updates cause a reference to be added to the content, the
+ server provides a SearchResultReference Message that represents the
+ reference in the content. The message SHALL include a Sync State
+ Control with state of add, an entryUUID containing the UUID
+ associated with the reference, and an optional cookie.
+
+
+
+
+Zeilenga & Choi Experimental [Page 16]
+
+RFC 4533 LDAP Content Synchronization Operation June 2006
+
+
+ Where DIT updates cause an entry to be modified within the content,
+ the server provides a SearchResultEntry Message that represents the
+ entry as it appears in the content. The message SHALL include a Sync
+ State Control with state of modify, an entryUUID containing the
+ entry's UUID, and an optional cookie.
+
+ Where DIT updates cause a reference to be modified within the
+ content, the server provides a SearchResultReference Message that
+ represents the reference in the content. The message SHALL include a
+ Sync State Control with state of modify, an entryUUID containing the
+ UUID associated with the reference, and an optional cookie.
+
+ Where DIT updates cause an entry to be deleted from the content, the
+ server provides a SearchResultEntry Message with no attributes. The
+ message SHALL include a Sync State Control with state of delete, an
+ entryUUID containing the entry's UUID, and an optional cookie.
+
+ Where DIT updates cause a reference to be deleted from the content,
+ the server provides a SearchResultReference Message with an empty
+ SEQUENCE OF LDAPURL. The message SHALL include a Sync State Control
+ with state of delete, an entryUUID containing the UUID associated
+ with the reference, and an optional cookie.
+
+ Multiple empty entries with a Sync State Control of state delete
+ SHOULD be coalesced into one or more Sync Info Messages of syncIdSet
+ value with refreshDeletes set to TRUE. syncUUIDs contain a set of
+ UUIDs of the entries and references that have been deleted from the
+ content. The Sync Info Message of syncIdSet may contain a cookie to
+ represent the state of the content after performing the
+ synchronization of the entries in the set.
+
+ With each of these messages, the server may provide a new cookie to
+ be used in subsequent Sync Operations. Additionally, the server may
+ also return Sync Info Messages of choice newCookie to provide a new
+ cookie. The client SHOULD use the newest (last) cookie it received
+ from the server in subsequent Sync Operations.
+
+3.5. Search Request Parameters
+
+ As stated in Section 3.1, the client SHOULD specify the same
+ content-controlling parameters in each Search Request of the session.
+ All fields of the SearchRequest Message are considered content-
+ controlling parameters except for sizeLimit and timeLimit.
+
+
+
+
+
+
+
+
+Zeilenga & Choi Experimental [Page 17]
+
+RFC 4533 LDAP Content Synchronization Operation June 2006
+
+
+3.5.1. baseObject
+
+ As with the normal search operation, the refresh and persist stages
+ are not isolated from DIT changes. It is possible that the entry
+ referred to by the baseObject is deleted, renamed, or moved. It is
+ also possible that the alias object used in finding the entry
+ referred to by the baseObject is changed such that the baseObject
+ refers to a different entry.
+
+ If the DIT is updated during processing of the Sync Operation in a
+ manner that causes the baseObject no longer to refer to any entry or
+ in a manner that changes the entry the baseObject refers to, the
+ server SHALL return an appropriate non-success result code, such as
+ noSuchObject, aliasProblem, aliasDereferencingProblem, referral, or
+ e-syncRefreshRequired.
+
+3.5.2. derefAliases
+
+ This operation does not support alias dereferencing during searching.
+ The client SHALL specify neverDerefAliases or derefFindingBaseObj for
+ the SearchRequest derefAliases parameter. The server SHALL treat
+ other values (e.g., derefInSearching, derefAlways) as protocol
+ errors.
+
+3.5.3. sizeLimit
+
+ The sizeLimit applies only to entries (regardless of their state in
+ Sync State Control) returned during the refreshOnly operation or the
+ refresh stage of the refreshAndPersist operation.
+
+3.5.4. timeLimit
+
+ For a refreshOnly Sync Operation, the timeLimit applies to the whole
+ operation. For a refreshAndPersist operation, the timeLimit applies
+ only to the refresh stage including the generation of the Sync Info
+ Message with a refreshDone value of TRUE.
+
+3.5.5. filter
+
+ The client SHOULD avoid filter assertions that apply to the values of
+ the attributes likely to be considered by the server as ones holding
+ meta-information. See Section 4.
+
+3.6. objectName
+
+ The Sync Operation uses entryUUID values provided in the Sync State
+ Control as the primary keys to entries. The client MUST use these
+ entryUUIDs to correlate synchronization messages.
+
+
+
+Zeilenga & Choi Experimental [Page 18]
+
+RFC 4533 LDAP Content Synchronization Operation June 2006
+
+
+ In some circumstances, the DN returned may not reflect the entry's
+ current DN. In particular, when the entry is being deleted from the
+ content, the server may provide an empty DN if the server does not
+ wish to disclose the entry's current DN (or, if deleted from the DIT,
+ the entry's last DN).
+
+ Also note that the entry's DN may be viewed as meta information (see
+ Section 4.1).
+
+3.7. Canceling the Sync Operation
+
+ Servers MUST implement the LDAP Cancel [RFC3909] Operation and
+ support cancellation of outstanding Sync Operations as described
+ here.
+
+ To cancel an outstanding Sync Operation, the client issues an LDAP
+ Cancel [RFC3909] Operation.
+
+ If at any time the server becomes unwilling or unable to continue
+ processing a Sync Operation, the server SHALL return a
+ SearchResultDone with a non-success resultCode indicating the reason
+ for the termination of the operation.
+
+ Whether the client or the server initiated the termination, the
+ server may provide a cookie in the Sync Done Control for use in
+ subsequent Sync Operations.
+
+3.8. Refresh Required
+
+ In order to achieve the eventually-convergent synchronization, the
+ server may terminate the Sync Operation in the refresh or persist
+ stages by returning an e-syncRefreshRequired resultCode to the
+ client. If no cookie is provided, a full refresh is needed. If a
+ cookie representing a synchronization state is provided in this
+ response, an incremental refresh is needed.
+
+ To obtain a full refresh, the client then issues a new
+ synchronization request with no cookie. To obtain an incremental
+ reload, the client issues a new synchronization with the provided
+ cookie.
+
+ The server may choose to provide a full copy in the refresh stage
+ (e.g., ignore the cookie or the synchronization state represented in
+ the cookie) instead of providing an incremental refresh in order to
+ achieve the eventual convergence.
+
+
+
+
+
+
+Zeilenga & Choi Experimental [Page 19]
+
+RFC 4533 LDAP Content Synchronization Operation June 2006
+
+
+ The decision between the return of the initial content and the return
+ of the e-syncRefreshRequired result code may be based on reloadHint
+ in the Sync Request Control from the client.
+
+ In the case of persist stage Sync, the server returns the resultCode
+ of e-syncRefreshRequired to the client to indicate that the client
+ needs to issue a new Sync Operation in order to obtain a synchronized
+ copy of the content. If no cookie is provided, a full refresh is
+ needed. If a cookie representing a synchronization state is
+ provided, an incremental refresh is needed.
+
+ The server may also return e-syncRefreshRequired if it determines
+ that a refresh would be more efficient than sending all the messages
+ required for convergence.
+
+ Note that the client may receive one or more of SearchResultEntry,
+ SearchResultReference, and/or Sync Info Messages before it receives a
+ SearchResultDone Message with the e-syncRefreshRequired result code.
+
+3.9. Chattiness Considerations
+
+ The server MUST ensure that the number of entry messages generated to
+ refresh the client content does not exceed the number of entries
+ presently in the content. While there is no requirement for servers
+ to maintain history information, if the server has sufficient history
+ to allow it to reliably determine which entries in the prior client
+ copy are no longer present in the content and the number of such
+ entries is less than or equal to the number of unchanged entries, the
+ server SHOULD generate delete entry messages instead of present entry
+ messages (see Section 3.3.2).
+
+ When the amount of history information maintained in the server is
+ not enough for the clients to perform infrequent refreshOnly Sync
+ Operations, it is likely that the server has incomplete history
+ information (e.g., due to truncation) by the time those clients
+ connect again.
+
+ The server SHOULD NOT resort to full reload when the history
+ information is not enough to generate delete entry messages. The
+ server SHOULD generate either present entry messages only or present
+ entry messages followed by delete entry messages to bring the client
+ copy to the current state. In the latter case, the present entry
+ messages bring the client copy to a state covered by the history
+ information maintained in the server.
+
+ The server SHOULD maintain enough (current or historical) state
+ information (such as a context-wide last modify time stamp) to
+ determine if no changes were made in the context since the content
+
+
+
+Zeilenga & Choi Experimental [Page 20]
+
+RFC 4533 LDAP Content Synchronization Operation June 2006
+
+
+ refresh was provided and, when no changes were made, generate zero
+ delete entry messages instead of present messages.
+
+ The server SHOULD NOT use the history information when its use does
+ not reduce the synchronization traffic or when its use can expose
+ sensitive information not allowed to be received by the client.
+
+ The server implementor should also consider chattiness issues that
+ span multiple Sync Operations of a session. As noted in Section 3.8,
+ the server may return e-syncRefreshRequired if it determines that a
+ reload would be more efficient than continuing under the current
+ operation. If reloadHint in the Sync Request is TRUE, the server may
+ initiate a reload without directing the client to request a reload.
+
+ The server SHOULD transfer a new cookie frequently to avoid having to
+ transfer information already provided to the client. Even where DIT
+ changes do not cause content synchronization changes to be
+ transferred, it may be advantageous to provide a new cookie using a
+ Sync Info Message. However, the server SHOULD avoid overloading the
+ client or network with Sync Info Messages.
+
+ During persist mode, the server SHOULD coalesce multiple outstanding
+ messages updating the same entry. The server MAY delay generation of
+ an entry update in anticipation of subsequent changes to that entry
+ that could be coalesced. The length of the delay should be long
+ enough to allow coalescing of update requests issued back to back but
+ short enough that the transient inconsistency induced by the delay is
+ corrected in a timely manner.
+
+ The server SHOULD use the syncIdSet Sync Info Message when there are
+ multiple delete or present messages to reduce the amount of
+ synchronization traffic.
+
+ Also note that there may be many clients interested in a particular
+ directory change, and that servers attempting to service all of these
+ at once may cause congestion on the network. The congestion issues
+ are magnified when the change requires a large transfer to each
+ interested client. Implementors and deployers of servers should take
+ steps to prevent and manage network congestion.
+
+3.10. Operation Multiplexing
+
+ The LDAP protocol model [RFC4511] allows operations to be multiplexed
+ over a single LDAP session. Clients SHOULD NOT maintain multiple
+ LDAP sessions with the same server. Servers SHOULD ensure that
+ responses from concurrently processed operations are interleaved
+ fairly.
+
+
+
+
+Zeilenga & Choi Experimental [Page 21]
+
+RFC 4533 LDAP Content Synchronization Operation June 2006
+
+
+ Clients SHOULD combine Sync Operations whose result set is largely
+ overlapping. This avoids having to return multiple messages, once
+ for each overlapping session, for changes to entries in the overlap.
+
+ Clients SHOULD NOT combine Sync Operations whose result sets are
+ largely non-overlapping. This ensures that an event requiring an
+ e-syncRefreshRequired response can be limited to as few result sets
+ as possible.
+
+4. Meta Information Considerations
+
+4.1. Entry DN
+
+ As an entry's DN is constructed from its relative DN (RDN) and the
+ entry's parent's DN, it is often viewed as meta information.
+
+ While renaming or moving to a new superior causes the entry's DN to
+ change, that change SHOULD NOT, by itself, cause synchronization
+ messages to be sent for that entry. However, if the renaming or the
+ moving could cause the entry to be added or deleted from the content,
+ appropriate synchronization messages should be generated to indicate
+ this to the client.
+
+ When a server treats the entry's DN as meta information, the server
+ SHALL either
+
+ - evaluate all MatchingRuleAssertions [RFC4511] to TRUE if
+ matching a value of an attribute of the entry, otherwise
+ Undefined, or
+
+ - evaluate all MatchingRuleAssertion with dnAttributes of TRUE as
+ Undefined.
+
+ The latter choice is offered for ease of server implementation.
+
+4.2. Operational Attributes
+
+ Where values of an operational attribute are determined by values not
+ held as part of the entry it appears in, the operational attribute
+ SHOULD NOT support synchronization of that operational attribute.
+
+ For example, in servers that implement the X.501 subschema model
+ [X.501], servers should not support synchronization of the
+ subschemaSubentry attribute as its value is determined by values held
+ and administrated in subschema subentries.
+
+
+
+
+
+
+Zeilenga & Choi Experimental [Page 22]
+
+RFC 4533 LDAP Content Synchronization Operation June 2006
+
+
+ As a counter example, servers that implement aliases [RFC4512][X.501]
+ can support synchronization of the aliasedObjectName attribute as its
+ values are held and administrated as part of the alias entries.
+
+ Servers SHOULD support synchronization of the following operational
+ attributes: createTimestamp, modifyTimestamp, creatorsName,
+ modifiersName [RFC4512]. Servers MAY support synchronization of
+ other operational attributes.
+
+4.3. Collective Attributes
+
+ A collective attribute is "a user attribute whose values are the same
+ for each member of an entry collection" [X.501]. Use of collective
+ attributes in LDAP is discussed in [RFC3671].
+
+ Modification of a collective attribute generally affects the content
+ of multiple entries, which are the members of the collection. It is
+ inefficient to include values of collective attributes visible in
+ entries of the collection, as a single modification of a collective
+ attribute requires transmission of multiple SearchResultEntry (one
+ for each entry of the collection that the modification affected).
+
+ Servers SHOULD NOT synchronize collective attributes appearing in
+ entries of any collection. Servers MAY support synchronization of
+ collective attributes appearing in collective attribute subentries.
+
+4.4. Access and Other Administrative Controls
+
+ Entries are commonly subject to access and other administrative
+ Controls. While portions of the policy information governing a
+ particular entry may be held in the entry, policy information is
+ often held elsewhere (in superior entries, in subentries, in the root
+ DSE, in configuration files, etc.). Because of this, changes to
+ policy information make it difficult to ensure eventual convergence
+ during incremental synchronization.
+
+ Where it is impractical or infeasible to generate content changes
+ resulting from a change to policy information, servers may opt to
+ return e-syncRefreshRequired or to treat the Sync Operation as an
+ initial content request (e.g., ignore the cookie or the
+ synchronization state represented in the cookie).
+
+5. Interaction with Other Controls
+
+ The Sync Operation may be used with:
+
+ - ManageDsaIT Control [RFC3296]
+
+
+
+
+Zeilenga & Choi Experimental [Page 23]
+
+RFC 4533 LDAP Content Synchronization Operation June 2006
+
+
+ - Subentries Control [RFC3672]
+
+ as described below. The Sync Operation may be used with other LDAP
+ extensions as detailed in other documents.
+
+5.1. ManageDsaIT Control
+
+ The ManageDsaIT Control [RFC3296] indicates that the operation acts
+ upon the DSA Information Tree and causes referral and other special
+ entries to be treated as object entries with respect to the
+ operation.
+
+5.2. Subentries Control
+
+ The Subentries Control is used with the search operation "to control
+ the visibility of entries and subentries which are within scope"
+ [RFC3672]. When used with the Sync Operation, the subentries control
+ and other factors (search scope, filter, etc.) are used to determine
+ whether an entry or subentry appears in the content.
+
+6. Shadowing Considerations
+
+ As noted in [RFC4511], some servers may hold shadow copies of entries
+ that can be used to answer search and comparison queries. Such
+ servers may also support content synchronization requests. This
+ section discusses considerations for implementors and deployers for
+ the implementation and deployment of the Sync operation in shadowed
+ directories.
+
+ While a client may know of multiple servers that are equally capable
+ of being used to obtain particular directory content from, a client
+ SHOULD NOT assume that each of these servers is equally capable of
+ continuing a content synchronization session. As stated in Section
+ 3.1, the client SHOULD issue each Sync request of a Sync session to
+ the same server.
+
+ However, through domain naming or IP address redirection or other
+ techniques, multiple physical servers can be made to appear as one
+ logical server to a client. Only servers that are equally capable in
+ regards to their support for the Sync operation and that hold equally
+ complete copies of the entries should be made to appear as one
+ logical server. In particular, each physical server acting as one
+ logical server SHOULD be equally capable of continuing a content
+ synchronization based upon cookies provided by any of the other
+ physical servers without requiring a full reload. Because there is
+ no standard LDAP shadowing mechanism, the specification of how to
+ independently implement equally capable servers (as well as the
+ precise definition of "equally capable") is left to future documents.
+
+
+
+Zeilenga & Choi Experimental [Page 24]
+
+RFC 4533 LDAP Content Synchronization Operation June 2006
+
+
+ Note that it may be difficult for the server to reliably determine
+ what content was provided to the client by another server, especially
+ in the shadowing environments that allow shadowing events to be
+ coalesced. For these servers, the use of the delete phase discussed
+ in Section 3.3.2 may not be applicable.
+
+7. Security Considerations
+
+ In order to maintain a synchronized copy of the content, a client is
+ to delete information from its copy of the content as described
+ above. However, the client may maintain knowledge of information
+ disclosed to it by the server separate from its copy of the content
+ used for synchronization. Management of this knowledge is beyond the
+ scope of this document. Servers should be careful not to disclose
+ information for content the client is not authorized to have
+ knowledge of and/or about.
+
+ While the information provided by a series of refreshOnly Sync
+ Operations is similar to that provided by a series of Search
+ Operations, persist stage may disclose additional information. A
+ client may be able to discern information about the particular
+ sequence of update operations that caused content change.
+
+ Implementors should take precautions against malicious cookie
+ content, including malformed cookies or valid cookies used with
+ different security associations and/or protections in an attempt to
+ obtain unauthorized access to information. Servers may include a
+ digital signature in the cookie to detect tampering.
+
+ The operation may be the target of direct denial-of-service attacks.
+ Implementors should provide safeguards to ensure the operation is not
+ abused. Servers may place access control or other restrictions upon
+ the use of this operation.
+
+ Note that even small updates to the directory may cause a significant
+ amount of traffic to be generated to clients using this operation. A
+ user could abuse its update privileges to mount an indirect denial of
+ service to these clients, other clients, and/or portions of the
+ network. Servers should provide safeguards to ensure that update
+ operations are not abused.
+
+ Implementors of this (or any) LDAP extension should be familiar with
+ general LDAP security considerations [RFC4510].
+
+
+
+
+
+
+
+
+Zeilenga & Choi Experimental [Page 25]
+
+RFC 4533 LDAP Content Synchronization Operation June 2006
+
+
+8. IANA Considerations
+
+ Registration of the following values have been completed by the IANA
+ [RFC4520].
+
+8.1. Object Identifier
+
+ The OID arc 1.3.6.1.4.1.4203.1.9.1 was assigned [ASSIGN] by the
+ OpenLDAP Foundation, under its IANA-assigned private enterprise
+ allocation [PRIVATE], for use in this specification.
+
+8.2. LDAP Protocol Mechanism
+
+ The IANA has registered the LDAP Protocol Mechanism described in this
+ document.
+
+ Subject: Request for LDAP Protocol Mechanism Registration
+ Object Identifier: 1.3.6.1.4.1.4203.1.9.1.1
+ Description: LDAP Content Synchronization Control
+ Person & email address to contact for further information:
+ Kurt Zeilenga <kurt@openldap.org>
+ Usage: Control
+ Specification: RFC 4533
+ Author/Change Controller: Kurt D. Zeilenga, Jong Hyuk Choi
+ Comments: none
+
+8.3. LDAP Result Codes
+
+ The IANA has registered the LDAP Result Code described in this
+ document.
+
+ Subject: LDAP Result Code Registration
+ Person & email address to contact for further information:
+ Kurt Zeilenga <kurt@OpenLDAP.org>
+ Result Code Name: e-syncRefreshRequired (4096)
+ Specification: RFC 4533
+ Author/Change Controller: Kurt D. Zeilenga, Jong Hyuk Choi
+ Comments: none
+
+9. Acknowledgements
+
+ This document borrows significantly from the LDAP Client Update
+ Protocol [RFC3928], a product of the IETF LDUP working group. This
+ document also benefited from Persistent Search [PSEARCH], Triggered
+ Search [TSEARCH], and Directory Synchronization [DIRSYNC] works.
+ This document also borrows from "Lightweight Directory Access
+ Protocol (v3)" [RFC2251].
+
+
+
+
+Zeilenga & Choi Experimental [Page 26]
+
+RFC 4533 LDAP Content Synchronization Operation June 2006
+
+
+10. Normative References
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [RFC3296] Zeilenga, K., "Named Subordinate References in
+ Lightweight Directory Access Protocol (LDAP)
+ Directories", RFC 3296, July 2002.
+
+ [RFC3671] Zeilenga, K., "Collective Attributes in the Lightweight
+ Directory Access Protocol (LDAP)", RFC 3671, December
+ 2003.
+
+ [RFC3672] Zeilenga, K., "Subentries in the Lightweight Directory
+ Access Protocol (LDAP)", RFC 3672, December 2003.
+
+ [RFC3909] Zeilenga, K., "Lightweight Directory Access Protocol
+ (LDAP) Cancel Operation", RFC 3909, October 2004.
+
+ [RFC4510] Zeilenga, K., Ed., "Lightweight Directory Access Protocol
+ (LDAP): Technical Specification Road Map", RFC 4510, June
+ 2006.
+
+ [RFC4511] Sermersheim, J., Ed., "Lightweight Directory Access
+ Protocol (LDAP): The Protocol", RFC 4511, June 2006.
+
+ [RFC4512] Zeilenga, K., "Lightweight Directory Access Protocol
+ (LDAP): Directory Information Models", RFC 4512, June
+ 2006.
+
+ [RFC4530] Zeilenga, K., "Lightweight Directory Access Protocol
+ (LDAP) entryUUID Operational Attribute", RFC 4530, June
+ 2006.
+
+ [UUID] International Organization for Standardization (ISO),
+ "Information technology - Open Systems Interconnection -
+ Remote Procedure Call", ISO/IEC 11578:1996
+
+ [X.501] International Telecommunication Union - Telecommunication
+ Standardization Sector, "The Directory -- Models,"
+ X.501(1993) (also ISO/IEC 9594-2:1994).
+
+ [X.680] International Telecommunication Union - Telecommunication
+ Standardization Sector, "Abstract Syntax Notation One
+ (ASN.1) - Specification of Basic Notation", X.680(1997)
+ (also ISO/IEC 8824-1:1998).
+
+
+
+
+
+Zeilenga & Choi Experimental [Page 27]
+
+RFC 4533 LDAP Content Synchronization Operation June 2006
+
+
+ [X.690] International Telecommunication Union - Telecommunication
+ Standardization Sector, "Specification of ASN.1 encoding
+ rules: Basic Encoding Rules (BER), Canonical Encoding
+ Rules (CER), and Distinguished Encoding Rules (DER)",
+ X.690(1997) (also ISO/IEC 8825-1:1998).
+
+11. Informative References
+
+ [RFC2251] Wahl, M., Howes, T., and S. Kille, "Lightweight Directory
+ Access Protocol (v3)", RFC 2251, December 1997.
+
+ [RFC3928] Megginson, R., Ed., Smith, M., Natkovich, O., and J.
+ Parham, "Lightweight Directory Access Protocol (LDAP)
+ Client Update Protocol (LCUP)", RFC 3928, October 2004.
+
+ [RFC4520] Zeilenga, K., "Internet Assigned Numbers Authority (IANA)
+ Considerations for the Lightweight Directory Access
+ Protocol (LDAP)", BCP 64, RFC 4520, June 2006.
+
+ [PRIVATE] IANA, "Private Enterprise Numbers",
+ http://www.iana.org/assignments/enterprise-numbers.
+
+ [ASSIGN] OpenLDAP Foundation, "OpenLDAP OID Delegations",
+ http://www.openldap.org/foundation/oid-delegate.txt.
+
+ [X.500] International Telecommunication Union - Telecommunication
+ Standardization Sector, "The Directory -- Overview of
+ concepts, models and services," X.500(1993) (also ISO/IEC
+ 9594-1:1994).
+
+ [X.525] International Telecommunication Union - Telecommunication
+ Standardization Sector, "The Directory: Replication",
+ X.525(1993).
+
+ [DIRSYNC] Armijo, M., "Microsoft LDAP Control for Directory
+ Synchronization", Work in Progress.
+
+ [PSEARCH] Smith, M., et al., "Persistent Search: A Simple LDAP
+ Change Notification Mechanism", Work in Progress.
+
+ [TSEARCH] Wahl, M., "LDAPv3 Triggered Search Control", Work in
+ Progress.
+
+
+
+
+
+
+
+
+
+Zeilenga & Choi Experimental [Page 28]
+
+RFC 4533 LDAP Content Synchronization Operation June 2006
+
+
+Appendix A. CSN-based Implementation Considerations
+
+ This appendix is provided for informational purposes only; it is not
+ a normative part of the LDAP Content Synchronization Operation's
+ technical specification.
+
+ This appendix discusses LDAP Content Synchronization Operation server
+ implementation considerations associated with Change Sequence Number
+ based approaches.
+
+ Change Sequence Number based approaches are targeted for use in
+ servers that do not maintain history information (e.g., change logs,
+ state snapshots) about changes made to the Directory and hence, must
+ rely on current directory state and minimal synchronization state
+ information embedded in Sync Cookie. Servers that maintain history
+ information should consider other approaches that exploit the history
+ information.
+
+ A Change Sequence Number is effectively a time stamp that has
+ sufficient granularity to ensure that the precedence relationship in
+ time of two updates to the same object can be determined. Change
+ Sequence Numbers are not to be confused with Commit Sequence Numbers
+ or Commit Log Record Numbers. A Commit Sequence Number allows one to
+ determine how two commits (to the same object or different objects)
+ relate to each other in time. A Change Sequence Number associated
+ with different entries may be committed out of order. In the
+ remainder of this Appendix, the term CSN refers to a Change Sequence
+ Number.
+
+ In these approaches, the server not only maintains a CSN for each
+ directory entry (the entry CSN) but also maintains a value that we
+ will call the context CSN. The context CSN is the greatest committed
+ entry CSN that is not greater than any outstanding (uncommitted)
+ entry CSNs for all entries in a directory context. The values of
+ context CSN are used in syncCookie values as synchronization state
+ indicators.
+
+ As search operations are not isolated from individual directory
+ update operations and individual update operations cannot be assumed
+ to be serialized, one cannot assume that the returned content
+ incorporates each relevant change whose change sequence number is
+ less than or equal to the greatest entry CSN in the content. The
+ content incorporates all the relevant changes whose change sequence
+ numbers are less than or equal to context CSN before search
+ processing. The content may also incorporate any subset of the
+ changes whose change sequence number is greater than context CSN
+ before search processing but less than or equal to the context CSN
+ after search processing. The content does not incorporate any of the
+
+
+
+Zeilenga & Choi Experimental [Page 29]
+
+RFC 4533 LDAP Content Synchronization Operation June 2006
+
+
+ changes whose CSN is greater than the context CSN after search
+ processing.
+
+ A simple server implementation could use the value of the context CSN
+ before search processing to indicate state. Such an implementation
+ would embed this value into each SyncCookie returned. We'll call
+ this the cookie CSN. When a refresh was requested, the server would
+ simply generate "update" messages for all entries in the content
+ whose CSN is greater than the supplied cookie CSN and generate
+ "present" messages for all other entries in the content. However, if
+ the current context CSN is the same as the cookie CSN, the server
+ should instead generate zero "updates" and zero "delete" messages and
+ indicate a refreshDeletes of TRUE, as the directory has not changed.
+
+ The implementation should also consider the impact of changes to meta
+ information, such as access controls, that affect content
+ determination. One approach is for the server to maintain a
+ context-wide meta information CSN or meta CSN. This meta CSN would
+ be updated whenever meta information affecting content determination
+ was changed. If the value of the meta CSN is greater than the cookie
+ CSN, the server should ignore the cookie and treat the request as an
+ initial request for content.
+
+ Additionally, servers may want to consider maintaining some per-
+ session history information to reduce the number of messages needed
+ to be transferred during incremental refreshes. Specifically, a
+ server could record information about entries as they leave the scope
+ of a disconnected sync session and later use this information to
+ generate delete messages instead of present messages.
+
+ When the history information is truncated, the CSN of the latest
+ truncated history information entry may be recorded as the truncated
+ CSN of the history information. The truncated CSN may be used to
+ determine whether a client copy can be covered by the history
+ information by comparing it to the synchronization state contained in
+ the cookie supplied by the client.
+
+ When there is a large number of sessions, it may make sense to
+ maintain such history only for the selected clients. Also, servers
+ taking this approach need to consider resource consumption issues to
+ ensure reasonable server operation and to protect against abuse. It
+ may be appropriate to restrict this mode of operation by policy.
+
+
+
+
+
+
+
+
+
+Zeilenga & Choi Experimental [Page 30]
+
+RFC 4533 LDAP Content Synchronization Operation June 2006
+
+
+Authors' Addresses
+
+ Kurt D. Zeilenga
+ OpenLDAP Foundation
+
+ EMail: Kurt@OpenLDAP.org
+
+
+ Jong Hyuk Choi
+ IBM Corporation
+
+ EMail: jongchoi@us.ibm.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Zeilenga & Choi Experimental [Page 31]
+
+RFC 4533 LDAP Content Synchronization Operation June 2006
+
+
+Full Copyright Statement
+
+ Copyright (C) The Internet Society (2006).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78 and at www.rfc-editor.org/copyright.html, and
+ except as set forth therein, the authors retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
+ ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
+ INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
+ INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be
+ found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at
+ ietf-ipr@ietf.org.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is provided by the IETF
+ Administrative Support Activity (IASA).
+
+
+
+
+
+
+
+Zeilenga & Choi Experimental [Page 32]
+
diff --git a/source4/ldap_server/ldap_backend.c b/source4/ldap_server/ldap_backend.c
new file mode 100644
index 0000000000..a091c1d11e
--- /dev/null
+++ b/source4/ldap_server/ldap_backend.c
@@ -0,0 +1,779 @@
+/*
+ Unix SMB/CIFS implementation.
+ LDAP server
+ Copyright (C) Stefan Metzmacher 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "ldap_server/ldap_server.h"
+#include "../lib/util/dlinklist.h"
+#include "libcli/ldap/ldap.h"
+#include "auth/credentials/credentials.h"
+#include "auth/gensec/gensec.h"
+#include "param/param.h"
+#include "smbd/service_stream.h"
+#include "dsdb/samdb/samdb.h"
+#include "lib/ldb/include/ldb_errors.h"
+#include "lib/ldb_wrap.h"
+
+#define VALID_DN_SYNTAX(dn) do {\
+ if (!(dn)) {\
+ return NT_STATUS_NO_MEMORY;\
+ } else if ( ! ldb_dn_validate(dn)) {\
+ result = LDAP_INVALID_DN_SYNTAX;\
+ errstr = "Invalid DN format";\
+ goto reply;\
+ }\
+} while(0)
+
+static int map_ldb_error(struct ldb_context *ldb, int err, const char **errstring)
+{
+ *errstring = ldb_errstring(ldb);
+
+ /* its 1:1 for now */
+ return err;
+}
+
+/*
+ connect to the sam database
+*/
+NTSTATUS ldapsrv_backend_Init(struct ldapsrv_connection *conn)
+{
+ conn->ldb = ldb_wrap_connect(conn,
+ conn->connection->event.ctx,
+ conn->lp_ctx,
+ lp_sam_url(conn->lp_ctx),
+ conn->session_info,
+ samdb_credentials(conn, conn->connection->event.ctx, conn->lp_ctx),
+ conn->global_catalog ? LDB_FLG_RDONLY : 0, NULL);
+ if (conn->ldb == NULL) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ if (conn->server_credentials) {
+ char **sasl_mechs = NULL;
+ struct gensec_security_ops **backends = gensec_security_all();
+ struct gensec_security_ops **ops
+ = gensec_use_kerberos_mechs(conn, backends, conn->server_credentials);
+ int i, j = 0;
+ for (i = 0; ops && ops[i]; i++) {
+ if (!gensec_security_ops_enabled(ops[i], conn->lp_ctx))
+ continue;
+
+ if (ops[i]->sasl_name && ops[i]->server_start) {
+ char *sasl_name = talloc_strdup(conn, ops[i]->sasl_name);
+
+ if (!sasl_name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ sasl_mechs = talloc_realloc(conn, sasl_mechs, char *, j + 2);
+ if (!sasl_mechs) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ sasl_mechs[j] = sasl_name;
+ talloc_steal(sasl_mechs, sasl_name);
+ sasl_mechs[j+1] = NULL;
+ j++;
+ }
+ }
+ talloc_free(ops);
+ ldb_set_opaque(conn->ldb, "supportedSASLMechanims", sasl_mechs);
+ }
+
+ return NT_STATUS_OK;
+}
+
+struct ldapsrv_reply *ldapsrv_init_reply(struct ldapsrv_call *call, uint8_t type)
+{
+ struct ldapsrv_reply *reply;
+
+ reply = talloc(call, struct ldapsrv_reply);
+ if (!reply) {
+ return NULL;
+ }
+ reply->msg = talloc(reply, struct ldap_message);
+ if (reply->msg == NULL) {
+ talloc_free(reply);
+ return NULL;
+ }
+
+ reply->msg->messageid = call->request->messageid;
+ reply->msg->type = type;
+ reply->msg->controls = NULL;
+
+ return reply;
+}
+
+void ldapsrv_queue_reply(struct ldapsrv_call *call, struct ldapsrv_reply *reply)
+{
+ DLIST_ADD_END(call->replies, reply, struct ldapsrv_reply *);
+}
+
+static NTSTATUS ldapsrv_unwilling(struct ldapsrv_call *call, int error)
+{
+ struct ldapsrv_reply *reply;
+ struct ldap_ExtendedResponse *r;
+
+ DEBUG(10,("Unwilling type[%d] id[%d]\n", call->request->type, call->request->messageid));
+
+ reply = ldapsrv_init_reply(call, LDAP_TAG_ExtendedResponse);
+ if (!reply) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ r = &reply->msg->r.ExtendedResponse;
+ r->response.resultcode = error;
+ r->response.dn = NULL;
+ r->response.errormessage = NULL;
+ r->response.referral = NULL;
+ r->oid = NULL;
+ r->value = NULL;
+
+ ldapsrv_queue_reply(call, reply);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ldapsrv_SearchRequest(struct ldapsrv_call *call)
+{
+ struct ldap_SearchRequest *req = &call->request->r.SearchRequest;
+ struct ldap_SearchResEntry *ent;
+ struct ldap_Result *done;
+ struct ldapsrv_reply *ent_r, *done_r;
+ void *local_ctx;
+ struct ldb_context *samdb = talloc_get_type(call->conn->ldb, struct ldb_context);
+ struct ldb_dn *basedn;
+ struct ldb_result *res = NULL;
+ struct ldb_request *lreq;
+ struct ldb_control *search_control;
+ struct ldb_search_options_control *search_options;
+ struct ldb_control *extended_dn_control;
+ struct ldb_extended_dn_control *extended_dn_decoded = NULL;
+ enum ldb_scope scope = LDB_SCOPE_DEFAULT;
+ const char **attrs = NULL;
+ const char *scope_str, *errstr = NULL;
+ int success_limit = 1;
+ int result = -1;
+ int ldb_ret = -1;
+ int i, j;
+ int extended_type = 1;
+
+ DEBUG(10, ("SearchRequest"));
+ DEBUGADD(10, (" basedn: %s", req->basedn));
+ DEBUGADD(10, (" filter: %s\n", ldb_filter_from_tree(call, req->tree)));
+
+ local_ctx = talloc_new(call);
+ NT_STATUS_HAVE_NO_MEMORY(local_ctx);
+
+ basedn = ldb_dn_new(local_ctx, samdb, req->basedn);
+ VALID_DN_SYNTAX(basedn);
+
+ DEBUG(10, ("SearchRequest: basedn: [%s]\n", req->basedn));
+ DEBUG(10, ("SearchRequest: filter: [%s]\n", ldb_filter_from_tree(call, req->tree)));
+
+ switch (req->scope) {
+ case LDAP_SEARCH_SCOPE_BASE:
+ scope_str = "BASE";
+ scope = LDB_SCOPE_BASE;
+ success_limit = 0;
+ break;
+ case LDAP_SEARCH_SCOPE_SINGLE:
+ scope_str = "ONE";
+ scope = LDB_SCOPE_ONELEVEL;
+ success_limit = 0;
+ break;
+ case LDAP_SEARCH_SCOPE_SUB:
+ scope_str = "SUB";
+ scope = LDB_SCOPE_SUBTREE;
+ success_limit = 0;
+ break;
+ default:
+ result = LDAP_PROTOCOL_ERROR;
+ errstr = "Invalid scope";
+ goto reply;
+ }
+ DEBUG(10,("SearchRequest: scope: [%s]\n", scope_str));
+
+ if (req->num_attributes >= 1) {
+ attrs = talloc_array(local_ctx, const char *, req->num_attributes+1);
+ NT_STATUS_HAVE_NO_MEMORY(attrs);
+
+ for (i=0; i < req->num_attributes; i++) {
+ DEBUG(10,("SearchRequest: attrs: [%s]\n",req->attributes[i]));
+ attrs[i] = req->attributes[i];
+ }
+ attrs[i] = NULL;
+ }
+
+ DEBUG(5,("ldb_request %s dn=%s filter=%s\n",
+ scope_str, req->basedn, ldb_filter_from_tree(call, req->tree)));
+
+ res = talloc_zero(local_ctx, struct ldb_result);
+ NT_STATUS_HAVE_NO_MEMORY(res);
+
+ ldb_ret = ldb_build_search_req_ex(&lreq, samdb, local_ctx,
+ basedn, scope,
+ req->tree, attrs,
+ call->request->controls,
+ res, ldb_search_default_callback,
+ NULL);
+
+ if (ldb_ret != LDB_SUCCESS) {
+ goto reply;
+ }
+
+ if (call->conn->global_catalog) {
+ search_control = ldb_request_get_control(lreq, LDB_CONTROL_SEARCH_OPTIONS_OID);
+
+ search_options = NULL;
+ if (search_control) {
+ search_options = talloc_get_type(search_control->data, struct ldb_search_options_control);
+ search_options->search_options |= LDB_SEARCH_OPTION_PHANTOM_ROOT;
+ } else {
+ search_options = talloc(lreq, struct ldb_search_options_control);
+ NT_STATUS_HAVE_NO_MEMORY(search_options);
+ search_options->search_options = LDB_SEARCH_OPTION_PHANTOM_ROOT;
+ ldb_request_add_control(lreq, LDB_CONTROL_SEARCH_OPTIONS_OID, false, search_options);
+ }
+ }
+
+ extended_dn_control = ldb_request_get_control(lreq, LDB_CONTROL_EXTENDED_DN_OID);
+
+ if (extended_dn_control) {
+ if (extended_dn_control->data) {
+ extended_dn_decoded = talloc_get_type(extended_dn_control->data, struct ldb_extended_dn_control);
+ extended_type = extended_dn_decoded->type;
+ } else {
+ extended_type = 0;
+ }
+ }
+
+ ldb_set_timeout(samdb, lreq, req->timelimit);
+
+ ldb_ret = ldb_request(samdb, lreq);
+
+ if (ldb_ret != LDB_SUCCESS) {
+ goto reply;
+ }
+
+ ldb_ret = ldb_wait(lreq->handle, LDB_WAIT_ALL);
+
+ if (ldb_ret == LDB_SUCCESS) {
+ for (i = 0; i < res->count; i++) {
+ ent_r = ldapsrv_init_reply(call, LDAP_TAG_SearchResultEntry);
+ NT_STATUS_HAVE_NO_MEMORY(ent_r);
+
+ /* Better to have the whole message kept here,
+ * than to find someone further up didn't put
+ * a value in the right spot in the talloc tree */
+ talloc_steal(ent_r, res->msgs[i]);
+
+ ent = &ent_r->msg->r.SearchResultEntry;
+ ent->dn = ldb_dn_get_extended_linearized(ent_r, res->msgs[i]->dn, extended_type);
+ ent->num_attributes = 0;
+ ent->attributes = NULL;
+ if (res->msgs[i]->num_elements == 0) {
+ goto queue_reply;
+ }
+ ent->num_attributes = res->msgs[i]->num_elements;
+ ent->attributes = talloc_array(ent_r, struct ldb_message_element, ent->num_attributes);
+ NT_STATUS_HAVE_NO_MEMORY(ent->attributes);
+ for (j=0; j < ent->num_attributes; j++) {
+ ent->attributes[j].name = talloc_steal(ent->attributes, res->msgs[i]->elements[j].name);
+ ent->attributes[j].num_values = 0;
+ ent->attributes[j].values = NULL;
+ if (req->attributesonly && (res->msgs[i]->elements[j].num_values == 0)) {
+ continue;
+ }
+ ent->attributes[j].num_values = res->msgs[i]->elements[j].num_values;
+ ent->attributes[j].values = res->msgs[i]->elements[j].values;
+ talloc_steal(ent->attributes, res->msgs[i]->elements[j].values);
+ }
+queue_reply:
+ ldapsrv_queue_reply(call, ent_r);
+ }
+ }
+
+reply:
+ done_r = ldapsrv_init_reply(call, LDAP_TAG_SearchResultDone);
+ NT_STATUS_HAVE_NO_MEMORY(done_r);
+
+ done = &done_r->msg->r.SearchResultDone;
+ done->dn = NULL;
+ done->referral = NULL;
+
+ if (result != -1) {
+ } else if (ldb_ret == LDB_SUCCESS) {
+ if (res->count >= success_limit) {
+ DEBUG(10,("SearchRequest: results: [%d]\n", res->count));
+ result = LDAP_SUCCESS;
+ errstr = NULL;
+ }
+ if (res->controls) {
+ done_r->msg->controls = res->controls;
+ talloc_steal(done_r, res->controls);
+ }
+ } else {
+ DEBUG(10,("SearchRequest: error\n"));
+ result = map_ldb_error(samdb, ldb_ret, &errstr);
+ }
+
+ done->resultcode = result;
+ done->errormessage = (errstr?talloc_strdup(done_r, errstr):NULL);
+
+ talloc_free(local_ctx);
+
+ ldapsrv_queue_reply(call, done_r);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ldapsrv_ModifyRequest(struct ldapsrv_call *call)
+{
+ struct ldap_ModifyRequest *req = &call->request->r.ModifyRequest;
+ struct ldap_Result *modify_result;
+ struct ldapsrv_reply *modify_reply;
+ void *local_ctx;
+ struct ldb_context *samdb = call->conn->ldb;
+ struct ldb_message *msg = NULL;
+ struct ldb_dn *dn;
+ const char *errstr = NULL;
+ int result = LDAP_SUCCESS;
+ int ldb_ret;
+ int i,j;
+
+ DEBUG(10, ("ModifyRequest"));
+ DEBUGADD(10, (" dn: %s", req->dn));
+
+ local_ctx = talloc_named(call, 0, "ModifyRequest local memory context");
+ NT_STATUS_HAVE_NO_MEMORY(local_ctx);
+
+ dn = ldb_dn_new(local_ctx, samdb, req->dn);
+ VALID_DN_SYNTAX(dn);
+
+ DEBUG(10, ("ModifyRequest: dn: [%s]\n", req->dn));
+
+ msg = talloc(local_ctx, struct ldb_message);
+ NT_STATUS_HAVE_NO_MEMORY(msg);
+
+ msg->dn = dn;
+ msg->num_elements = 0;
+ msg->elements = NULL;
+
+ if (req->num_mods > 0) {
+ msg->num_elements = req->num_mods;
+ msg->elements = talloc_array(msg, struct ldb_message_element, req->num_mods);
+ NT_STATUS_HAVE_NO_MEMORY(msg->elements);
+
+ for (i=0; i < msg->num_elements; i++) {
+ msg->elements[i].name = discard_const_p(char, req->mods[i].attrib.name);
+ msg->elements[i].num_values = 0;
+ msg->elements[i].values = NULL;
+
+ switch (req->mods[i].type) {
+ default:
+ result = LDAP_PROTOCOL_ERROR;
+ errstr = "Invalid LDAP_MODIFY_* type";
+ goto reply;
+ case LDAP_MODIFY_ADD:
+ msg->elements[i].flags = LDB_FLAG_MOD_ADD;
+ break;
+ case LDAP_MODIFY_DELETE:
+ msg->elements[i].flags = LDB_FLAG_MOD_DELETE;
+ break;
+ case LDAP_MODIFY_REPLACE:
+ msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
+ break;
+ }
+
+ msg->elements[i].num_values = req->mods[i].attrib.num_values;
+ if (msg->elements[i].num_values > 0) {
+ msg->elements[i].values = talloc_array(msg->elements, struct ldb_val,
+ msg->elements[i].num_values);
+ NT_STATUS_HAVE_NO_MEMORY(msg->elements[i].values);
+
+ for (j=0; j < msg->elements[i].num_values; j++) {
+ if (!(req->mods[i].attrib.values[j].length > 0)) {
+ result = LDAP_OTHER;
+ errstr = "Empty attribute values are not allowed";
+ goto reply;
+ }
+ msg->elements[i].values[j].length = req->mods[i].attrib.values[j].length;
+ msg->elements[i].values[j].data = req->mods[i].attrib.values[j].data;
+ }
+ }
+ }
+ } else {
+ result = LDAP_OTHER;
+ errstr = "No mods are not allowed";
+ goto reply;
+ }
+
+reply:
+ modify_reply = ldapsrv_init_reply(call, LDAP_TAG_ModifyResponse);
+ NT_STATUS_HAVE_NO_MEMORY(modify_reply);
+
+ if (result == LDAP_SUCCESS) {
+ ldb_ret = ldb_modify(samdb, msg);
+ result = map_ldb_error(samdb, ldb_ret, &errstr);
+ }
+
+ modify_result = &modify_reply->msg->r.AddResponse;
+ modify_result->dn = NULL;
+ modify_result->resultcode = result;
+ modify_result->errormessage = (errstr?talloc_strdup(modify_reply, errstr):NULL);
+ modify_result->referral = NULL;
+
+ talloc_free(local_ctx);
+
+ ldapsrv_queue_reply(call, modify_reply);
+ return NT_STATUS_OK;
+
+}
+
+static NTSTATUS ldapsrv_AddRequest(struct ldapsrv_call *call)
+{
+ struct ldap_AddRequest *req = &call->request->r.AddRequest;
+ struct ldap_Result *add_result;
+ struct ldapsrv_reply *add_reply;
+ void *local_ctx;
+ struct ldb_context *samdb = call->conn->ldb;
+ struct ldb_message *msg = NULL;
+ struct ldb_dn *dn;
+ const char *errstr = NULL;
+ int result = LDAP_SUCCESS;
+ int ldb_ret;
+ int i,j;
+
+ DEBUG(10, ("AddRequest"));
+ DEBUGADD(10, (" dn: %s", req->dn));
+
+ local_ctx = talloc_named(call, 0, "AddRequest local memory context");
+ NT_STATUS_HAVE_NO_MEMORY(local_ctx);
+
+ dn = ldb_dn_new(local_ctx, samdb, req->dn);
+ VALID_DN_SYNTAX(dn);
+
+ DEBUG(10, ("AddRequest: dn: [%s]\n", req->dn));
+
+ msg = talloc(local_ctx, struct ldb_message);
+ NT_STATUS_HAVE_NO_MEMORY(msg);
+
+ msg->dn = dn;
+ msg->num_elements = 0;
+ msg->elements = NULL;
+
+ if (req->num_attributes > 0) {
+ msg->num_elements = req->num_attributes;
+ msg->elements = talloc_array(msg, struct ldb_message_element, msg->num_elements);
+ NT_STATUS_HAVE_NO_MEMORY(msg->elements);
+
+ for (i=0; i < msg->num_elements; i++) {
+ msg->elements[i].name = discard_const_p(char, req->attributes[i].name);
+ msg->elements[i].flags = 0;
+ msg->elements[i].num_values = 0;
+ msg->elements[i].values = NULL;
+
+ if (req->attributes[i].num_values > 0) {
+ msg->elements[i].num_values = req->attributes[i].num_values;
+ msg->elements[i].values = talloc_array(msg->elements, struct ldb_val,
+ msg->elements[i].num_values);
+ NT_STATUS_HAVE_NO_MEMORY(msg->elements[i].values);
+
+ for (j=0; j < msg->elements[i].num_values; j++) {
+ if (!(req->attributes[i].values[j].length > 0)) {
+ result = LDAP_OTHER;
+ errstr = "Empty attribute values are not allowed";
+ goto reply;
+ }
+ msg->elements[i].values[j].length = req->attributes[i].values[j].length;
+ msg->elements[i].values[j].data = req->attributes[i].values[j].data;
+ }
+ } else {
+ result = LDAP_OTHER;
+ errstr = "No attribute values are not allowed";
+ goto reply;
+ }
+ }
+ } else {
+ result = LDAP_OTHER;
+ errstr = "No attributes are not allowed";
+ goto reply;
+ }
+
+reply:
+ add_reply = ldapsrv_init_reply(call, LDAP_TAG_AddResponse);
+ NT_STATUS_HAVE_NO_MEMORY(add_reply);
+
+ if (result == LDAP_SUCCESS) {
+ ldb_ret = ldb_add(samdb, msg);
+ result = map_ldb_error(samdb, ldb_ret, &errstr);
+ }
+
+ add_result = &add_reply->msg->r.AddResponse;
+ add_result->dn = NULL;
+ add_result->resultcode = result;
+ add_result->errormessage = (errstr?talloc_strdup(add_reply,errstr):NULL);
+ add_result->referral = NULL;
+
+ talloc_free(local_ctx);
+
+ ldapsrv_queue_reply(call, add_reply);
+ return NT_STATUS_OK;
+
+}
+
+static NTSTATUS ldapsrv_DelRequest(struct ldapsrv_call *call)
+{
+ struct ldap_DelRequest *req = &call->request->r.DelRequest;
+ struct ldap_Result *del_result;
+ struct ldapsrv_reply *del_reply;
+ void *local_ctx;
+ struct ldb_context *samdb = call->conn->ldb;
+ struct ldb_dn *dn;
+ const char *errstr = NULL;
+ int result = LDAP_SUCCESS;
+ int ldb_ret;
+
+ DEBUG(10, ("DelRequest"));
+ DEBUGADD(10, (" dn: %s", req->dn));
+
+ local_ctx = talloc_named(call, 0, "DelRequest local memory context");
+ NT_STATUS_HAVE_NO_MEMORY(local_ctx);
+
+ dn = ldb_dn_new(local_ctx, samdb, req->dn);
+ VALID_DN_SYNTAX(dn);
+
+ DEBUG(10, ("DelRequest: dn: [%s]\n", req->dn));
+
+reply:
+ del_reply = ldapsrv_init_reply(call, LDAP_TAG_DelResponse);
+ NT_STATUS_HAVE_NO_MEMORY(del_reply);
+
+ if (result == LDAP_SUCCESS) {
+ ldb_ret = ldb_delete(samdb, dn);
+ result = map_ldb_error(samdb, ldb_ret, &errstr);
+ }
+
+ del_result = &del_reply->msg->r.DelResponse;
+ del_result->dn = NULL;
+ del_result->resultcode = result;
+ del_result->errormessage = (errstr?talloc_strdup(del_reply,errstr):NULL);
+ del_result->referral = NULL;
+
+ talloc_free(local_ctx);
+
+ ldapsrv_queue_reply(call, del_reply);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ldapsrv_ModifyDNRequest(struct ldapsrv_call *call)
+{
+ struct ldap_ModifyDNRequest *req = &call->request->r.ModifyDNRequest;
+ struct ldap_Result *modifydn;
+ struct ldapsrv_reply *modifydn_r;
+ void *local_ctx;
+ struct ldb_context *samdb = call->conn->ldb;
+ struct ldb_dn *olddn, *newdn=NULL, *newrdn;
+ struct ldb_dn *parentdn = NULL;
+ const char *errstr = NULL;
+ int result = LDAP_SUCCESS;
+ int ldb_ret;
+
+ DEBUG(10, ("ModifyDNRequrest"));
+ DEBUGADD(10, (" dn: %s", req->dn));
+ DEBUGADD(10, (" newrdn: %s", req->newrdn));
+
+ local_ctx = talloc_named(call, 0, "ModifyDNRequest local memory context");
+ NT_STATUS_HAVE_NO_MEMORY(local_ctx);
+
+ olddn = ldb_dn_new(local_ctx, samdb, req->dn);
+ VALID_DN_SYNTAX(olddn);
+
+ newrdn = ldb_dn_new(local_ctx, samdb, req->newrdn);
+ VALID_DN_SYNTAX(newrdn);
+
+ DEBUG(10, ("ModifyDNRequest: olddn: [%s]\n", req->dn));
+ DEBUG(10, ("ModifyDNRequest: newrdn: [%s]\n", req->newrdn));
+
+ /* we can't handle the rename if we should not remove the old dn */
+ if (!req->deleteolddn) {
+ result = LDAP_UNWILLING_TO_PERFORM;
+ errstr = "Old RDN must be deleted";
+ goto reply;
+ }
+
+ if (req->newsuperior) {
+ parentdn = ldb_dn_new(local_ctx, samdb, req->newsuperior);
+ VALID_DN_SYNTAX(parentdn);
+ DEBUG(10, ("ModifyDNRequest: newsuperior: [%s]\n", req->newsuperior));
+
+ if (ldb_dn_get_comp_num(parentdn) < 1) {
+ result = LDAP_AFFECTS_MULTIPLE_DSAS;
+ errstr = "Error new Superior DN invalid";
+ goto reply;
+ }
+ }
+
+ if (!parentdn) {
+ parentdn = ldb_dn_get_parent(local_ctx, olddn);
+ NT_STATUS_HAVE_NO_MEMORY(parentdn);
+ }
+
+ if ( ! ldb_dn_add_child_fmt(parentdn,
+ "%s=%s",
+ ldb_dn_get_rdn_name(newrdn),
+ (char *)ldb_dn_get_rdn_val(newrdn)->data)) {
+ result = LDAP_OTHER;
+ goto reply;
+ }
+ newdn = parentdn;
+
+reply:
+ modifydn_r = ldapsrv_init_reply(call, LDAP_TAG_ModifyDNResponse);
+ NT_STATUS_HAVE_NO_MEMORY(modifydn_r);
+
+ if (result == LDAP_SUCCESS) {
+ ldb_ret = ldb_rename(samdb, olddn, newdn);
+ result = map_ldb_error(samdb, ldb_ret, &errstr);
+ }
+
+ modifydn = &modifydn_r->msg->r.ModifyDNResponse;
+ modifydn->dn = NULL;
+ modifydn->resultcode = result;
+ modifydn->errormessage = (errstr?talloc_strdup(modifydn_r,errstr):NULL);
+ modifydn->referral = NULL;
+
+ talloc_free(local_ctx);
+
+ ldapsrv_queue_reply(call, modifydn_r);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ldapsrv_CompareRequest(struct ldapsrv_call *call)
+{
+ struct ldap_CompareRequest *req = &call->request->r.CompareRequest;
+ struct ldap_Result *compare;
+ struct ldapsrv_reply *compare_r;
+ void *local_ctx;
+ struct ldb_context *samdb = call->conn->ldb;
+ struct ldb_result *res = NULL;
+ struct ldb_dn *dn;
+ const char *attrs[1];
+ const char *errstr = NULL;
+ const char *filter = NULL;
+ int result = LDAP_SUCCESS;
+ int ldb_ret;
+
+ DEBUG(10, ("CompareRequest"));
+ DEBUGADD(10, (" dn: %s", req->dn));
+
+ local_ctx = talloc_named(call, 0, "CompareRequest local_memory_context");
+ NT_STATUS_HAVE_NO_MEMORY(local_ctx);
+
+ dn = ldb_dn_new(local_ctx, samdb, req->dn);
+ VALID_DN_SYNTAX(dn);
+
+ DEBUG(10, ("CompareRequest: dn: [%s]\n", req->dn));
+ filter = talloc_asprintf(local_ctx, "(%s=%*s)", req->attribute,
+ (int)req->value.length, req->value.data);
+ NT_STATUS_HAVE_NO_MEMORY(filter);
+
+ DEBUGADD(10, ("CompareRequest: attribute: [%s]\n", filter));
+
+ attrs[0] = NULL;
+
+reply:
+ compare_r = ldapsrv_init_reply(call, LDAP_TAG_CompareResponse);
+ NT_STATUS_HAVE_NO_MEMORY(compare_r);
+
+ if (result == LDAP_SUCCESS) {
+ ldb_ret = ldb_search(samdb, local_ctx, &res,
+ dn, LDB_SCOPE_BASE, attrs, "%s", filter);
+ if (ldb_ret != LDB_SUCCESS) {
+ result = map_ldb_error(samdb, ldb_ret, &errstr);
+ DEBUG(10,("CompareRequest: error: %s\n", errstr));
+ } else if (res->count == 0) {
+ DEBUG(10,("CompareRequest: doesn't matched\n"));
+ result = LDAP_COMPARE_FALSE;
+ errstr = NULL;
+ } else if (res->count == 1) {
+ DEBUG(10,("CompareRequest: matched\n"));
+ result = LDAP_COMPARE_TRUE;
+ errstr = NULL;
+ } else if (res->count > 1) {
+ result = LDAP_OTHER;
+ errstr = "too many objects match";
+ DEBUG(10,("CompareRequest: %d results: %s\n", res->count, errstr));
+ }
+ }
+
+ compare = &compare_r->msg->r.CompareResponse;
+ compare->dn = NULL;
+ compare->resultcode = result;
+ compare->errormessage = (errstr?talloc_strdup(compare_r,errstr):NULL);
+ compare->referral = NULL;
+
+ talloc_free(local_ctx);
+
+ ldapsrv_queue_reply(call, compare_r);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ldapsrv_AbandonRequest(struct ldapsrv_call *call)
+{
+/* struct ldap_AbandonRequest *req = &call->request.r.AbandonRequest;*/
+ DEBUG(10, ("AbandonRequest\n"));
+ return NT_STATUS_OK;
+}
+
+NTSTATUS ldapsrv_do_call(struct ldapsrv_call *call)
+{
+ int i;
+ struct ldap_message *msg = call->request;
+ /* Check for undecoded critical extensions */
+ for (i=0; msg->controls && msg->controls[i]; i++) {
+ if (!msg->controls_decoded[i] &&
+ msg->controls[i]->critical) {
+ DEBUG(3, ("ldapsrv_do_call: Critical extension %s is not known to this server\n",
+ msg->controls[i]->oid));
+ return ldapsrv_unwilling(call, LDAP_UNAVAILABLE_CRITICAL_EXTENSION);
+ }
+ }
+
+ switch(call->request->type) {
+ case LDAP_TAG_BindRequest:
+ return ldapsrv_BindRequest(call);
+ case LDAP_TAG_UnbindRequest:
+ return ldapsrv_UnbindRequest(call);
+ case LDAP_TAG_SearchRequest:
+ return ldapsrv_SearchRequest(call);
+ case LDAP_TAG_ModifyRequest:
+ return ldapsrv_ModifyRequest(call);
+ case LDAP_TAG_AddRequest:
+ return ldapsrv_AddRequest(call);
+ case LDAP_TAG_DelRequest:
+ return ldapsrv_DelRequest(call);
+ case LDAP_TAG_ModifyDNRequest:
+ return ldapsrv_ModifyDNRequest(call);
+ case LDAP_TAG_CompareRequest:
+ return ldapsrv_CompareRequest(call);
+ case LDAP_TAG_AbandonRequest:
+ return ldapsrv_AbandonRequest(call);
+ case LDAP_TAG_ExtendedRequest:
+ return ldapsrv_ExtendedRequest(call);
+ default:
+ return ldapsrv_unwilling(call, 2);
+ }
+}
diff --git a/source4/ldap_server/ldap_bind.c b/source4/ldap_server/ldap_bind.c
new file mode 100644
index 0000000000..9abc6115e6
--- /dev/null
+++ b/source4/ldap_server/ldap_bind.c
@@ -0,0 +1,313 @@
+/*
+ Unix SMB/CIFS implementation.
+ LDAP server
+ Copyright (C) Stefan Metzmacher 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "ldap_server/ldap_server.h"
+#include "auth/auth.h"
+#include "libcli/ldap/ldap.h"
+#include "smbd/service.h"
+#include "lib/ldb/include/ldb.h"
+#include "lib/ldb/include/ldb_errors.h"
+#include "dsdb/samdb/samdb.h"
+#include "auth/gensec/gensec.h"
+#include "param/param.h"
+
+static NTSTATUS ldapsrv_BindSimple(struct ldapsrv_call *call)
+{
+ struct ldap_BindRequest *req = &call->request->r.BindRequest;
+ struct ldapsrv_reply *reply;
+ struct ldap_BindResponse *resp;
+
+ int result;
+ const char *errstr;
+ const char *nt4_domain, *nt4_account;
+
+ struct auth_session_info *session_info;
+
+ NTSTATUS status;
+
+ DEBUG(10, ("BindSimple dn: %s\n",req->dn));
+
+ status = crack_auto_name_to_nt4_name(call, call->conn->connection->event.ctx, call->conn->lp_ctx, req->dn, &nt4_domain, &nt4_account);
+ if (NT_STATUS_IS_OK(status)) {
+ status = authenticate_username_pw(call,
+ call->conn->connection->event.ctx,
+ call->conn->connection->msg_ctx,
+ call->conn->lp_ctx,
+ nt4_domain, nt4_account,
+ req->creds.password,
+ &session_info);
+ }
+
+ reply = ldapsrv_init_reply(call, LDAP_TAG_BindResponse);
+ if (!reply) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (NT_STATUS_IS_OK(status)) {
+ result = LDAP_SUCCESS;
+ errstr = NULL;
+
+ talloc_free(call->conn->session_info);
+ call->conn->session_info = session_info;
+ talloc_steal(call->conn, session_info);
+
+ /* don't leak the old LDB */
+ talloc_free(call->conn->ldb);
+
+ status = ldapsrv_backend_Init(call->conn);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ result = LDAP_OPERATIONS_ERROR;
+ errstr = talloc_asprintf(reply, "Simple Bind: Failed to advise ldb new credentials: %s", nt_errstr(status));
+ }
+ } else {
+ status = auth_nt_status_squash(status);
+
+ result = LDAP_INVALID_CREDENTIALS;
+ errstr = talloc_asprintf(reply, "Simple Bind Failed: %s", nt_errstr(status));
+ }
+
+ resp = &reply->msg->r.BindResponse;
+ resp->response.resultcode = result;
+ resp->response.errormessage = errstr;
+ resp->response.dn = NULL;
+ resp->response.referral = NULL;
+ resp->SASL.secblob = NULL;
+
+ ldapsrv_queue_reply(call, reply);
+ return NT_STATUS_OK;
+}
+
+struct ldapsrv_sasl_context {
+ struct ldapsrv_connection *conn;
+ struct socket_context *sasl_socket;
+};
+
+static void ldapsrv_set_sasl(void *private_data)
+{
+ struct ldapsrv_sasl_context *ctx = talloc_get_type(private_data, struct ldapsrv_sasl_context);
+ talloc_steal(ctx->conn->connection, ctx->sasl_socket);
+ talloc_unlink(ctx->conn->connection, ctx->conn->connection->socket);
+
+ ctx->conn->sockets.sasl = ctx->sasl_socket;
+ ctx->conn->connection->socket = ctx->sasl_socket;
+ packet_set_socket(ctx->conn->packet, ctx->conn->connection->socket);
+}
+
+static NTSTATUS ldapsrv_BindSASL(struct ldapsrv_call *call)
+{
+ struct ldap_BindRequest *req = &call->request->r.BindRequest;
+ struct ldapsrv_reply *reply;
+ struct ldap_BindResponse *resp;
+ struct ldapsrv_connection *conn;
+ int result = 0;
+ const char *errstr=NULL;
+ NTSTATUS status = NT_STATUS_OK;
+
+ DEBUG(10, ("BindSASL dn: %s\n",req->dn));
+
+ reply = ldapsrv_init_reply(call, LDAP_TAG_BindResponse);
+ if (!reply) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ resp = &reply->msg->r.BindResponse;
+
+ conn = call->conn;
+
+ /*
+ * TODO: a SASL bind with a different mechanism
+ * should cancel an inprogress SASL bind.
+ * (see RFC 4513)
+ */
+
+ if (!conn->gensec) {
+ conn->session_info = NULL;
+
+ status = samba_server_gensec_start(conn,
+ conn->connection->event.ctx,
+ conn->connection->msg_ctx,
+ conn->lp_ctx,
+ conn->server_credentials,
+ "ldap",
+ &conn->gensec);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to start GENSEC server code: %s\n", nt_errstr(status)));
+ result = LDAP_OPERATIONS_ERROR;
+ errstr = talloc_asprintf(reply, "SASL: Failed to start authentication system: %s",
+ nt_errstr(status));
+ } else {
+
+ gensec_want_feature(conn->gensec, GENSEC_FEATURE_SIGN);
+ gensec_want_feature(conn->gensec, GENSEC_FEATURE_SEAL);
+ gensec_want_feature(conn->gensec, GENSEC_FEATURE_ASYNC_REPLIES);
+
+ status = gensec_start_mech_by_sasl_name(conn->gensec, req->creds.SASL.mechanism);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to start GENSEC SASL[%s] server code: %s\n",
+ req->creds.SASL.mechanism, nt_errstr(status)));
+ result = LDAP_OPERATIONS_ERROR;
+ errstr = talloc_asprintf(reply, "SASL:[%s]: Failed to start authentication backend: %s",
+ req->creds.SASL.mechanism, nt_errstr(status));
+ }
+ }
+ }
+
+ if (NT_STATUS_IS_OK(status)) {
+ DATA_BLOB input = data_blob(NULL, 0);
+ DATA_BLOB output = data_blob(NULL, 0);
+
+ if (req->creds.SASL.secblob) {
+ input = *req->creds.SASL.secblob;
+ }
+
+ status = gensec_update(conn->gensec, reply,
+ input, &output);
+
+ /* Windows 2000 mmc doesn't like secblob == NULL and reports a decoding error */
+ resp->SASL.secblob = talloc(reply, DATA_BLOB);
+ NT_STATUS_HAVE_NO_MEMORY(resp->SASL.secblob);
+ *resp->SASL.secblob = output;
+ } else {
+ resp->SASL.secblob = NULL;
+ }
+
+ if (NT_STATUS_EQUAL(NT_STATUS_MORE_PROCESSING_REQUIRED, status)) {
+ result = LDAP_SASL_BIND_IN_PROGRESS;
+ errstr = NULL;
+ } else if (NT_STATUS_IS_OK(status)) {
+ struct auth_session_info *old_session_info=NULL;
+ struct ldapsrv_sasl_context *ctx;
+
+ result = LDAP_SUCCESS;
+ errstr = NULL;
+
+ ctx = talloc(call, struct ldapsrv_sasl_context);
+
+ if (!ctx) {
+ status = NT_STATUS_NO_MEMORY;
+ } else {
+ ctx->conn = conn;
+ status = gensec_socket_init(conn->gensec,
+ conn->connection,
+ conn->connection->socket,
+ conn->connection->event.ctx,
+ stream_io_handler_callback,
+ conn->connection,
+ &ctx->sasl_socket);
+ }
+
+ if (!ctx || !NT_STATUS_IS_OK(status)) {
+ conn->session_info = old_session_info;
+ result = LDAP_OPERATIONS_ERROR;
+ errstr = talloc_asprintf(reply,
+ "SASL:[%s]: Failed to setup SASL socket: %s",
+ req->creds.SASL.mechanism, nt_errstr(status));
+ } else {
+
+ call->send_callback = ldapsrv_set_sasl;
+ call->send_private = ctx;
+
+ old_session_info = conn->session_info;
+ conn->session_info = NULL;
+ status = gensec_session_info(conn->gensec, &conn->session_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ conn->session_info = old_session_info;
+ result = LDAP_OPERATIONS_ERROR;
+ errstr = talloc_asprintf(reply,
+ "SASL:[%s]: Failed to get session info: %s",
+ req->creds.SASL.mechanism, nt_errstr(status));
+ } else {
+ talloc_free(old_session_info);
+ talloc_steal(conn, conn->session_info);
+
+ /* don't leak the old LDB */
+ talloc_free(conn->ldb);
+
+ status = ldapsrv_backend_Init(conn);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ result = LDAP_OPERATIONS_ERROR;
+ errstr = talloc_asprintf(reply,
+ "SASL:[%s]: Failed to advise samdb of new credentials: %s",
+ req->creds.SASL.mechanism,
+ nt_errstr(status));
+ }
+ }
+ }
+ } else {
+ status = auth_nt_status_squash(status);
+ if (result == 0) {
+ result = LDAP_INVALID_CREDENTIALS;
+ errstr = talloc_asprintf(reply, "SASL:[%s]: %s", req->creds.SASL.mechanism, nt_errstr(status));
+ }
+ }
+
+ resp->response.resultcode = result;
+ resp->response.dn = NULL;
+ resp->response.errormessage = errstr;
+ resp->response.referral = NULL;
+
+ ldapsrv_queue_reply(call, reply);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS ldapsrv_BindRequest(struct ldapsrv_call *call)
+{
+ struct ldap_BindRequest *req = &call->request->r.BindRequest;
+ struct ldapsrv_reply *reply;
+ struct ldap_BindResponse *resp;
+
+ /*
+ * TODO: we should fail the bind request
+ * if there're any pending requests.
+ *
+ * also a simple bind should cancel an
+ * inprogress SASL bind.
+ * (see RFC 4513)
+ */
+ switch (req->mechanism) {
+ case LDAP_AUTH_MECH_SIMPLE:
+ return ldapsrv_BindSimple(call);
+ case LDAP_AUTH_MECH_SASL:
+ return ldapsrv_BindSASL(call);
+ }
+
+ reply = ldapsrv_init_reply(call, LDAP_TAG_BindResponse);
+ if (!reply) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ resp = &reply->msg->r.BindResponse;
+ resp->response.resultcode = 7;
+ resp->response.dn = NULL;
+ resp->response.errormessage = talloc_asprintf(reply, "Bad AuthenticationChoice [%d]", req->mechanism);
+ resp->response.referral = NULL;
+ resp->SASL.secblob = NULL;
+
+ ldapsrv_queue_reply(call, reply);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS ldapsrv_UnbindRequest(struct ldapsrv_call *call)
+{
+ DEBUG(10, ("UnbindRequest\n"));
+ return NT_STATUS_OK;
+}
diff --git a/source4/ldap_server/ldap_extended.c b/source4/ldap_server/ldap_extended.c
new file mode 100644
index 0000000000..66ab4eea32
--- /dev/null
+++ b/source4/ldap_server/ldap_extended.c
@@ -0,0 +1,150 @@
+/*
+ Unix SMB/CIFS implementation.
+ LDAP server
+ Copyright (C) Stefan Metzmacher 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "ldap_server/ldap_server.h"
+#include "../lib/util/dlinklist.h"
+#include "libcli/ldap/ldap.h"
+#include "lib/tls/tls.h"
+#include "smbd/service_stream.h"
+
+struct ldapsrv_starttls_context {
+ struct ldapsrv_connection *conn;
+ struct socket_context *tls_socket;
+};
+
+static void ldapsrv_start_tls(void *private_data)
+{
+ struct ldapsrv_starttls_context *ctx = talloc_get_type(private_data, struct ldapsrv_starttls_context);
+ talloc_steal(ctx->conn->connection, ctx->tls_socket);
+ talloc_unlink(ctx->conn->connection, ctx->conn->connection->socket);
+
+ ctx->conn->sockets.tls = ctx->tls_socket;
+ ctx->conn->connection->socket = ctx->tls_socket;
+ packet_set_socket(ctx->conn->packet, ctx->conn->connection->socket);
+ packet_set_unreliable_select(ctx->conn->packet);
+}
+
+static NTSTATUS ldapsrv_StartTLS(struct ldapsrv_call *call,
+ struct ldapsrv_reply *reply,
+ const char **errstr)
+{
+ struct ldapsrv_starttls_context *ctx;
+
+ (*errstr) = NULL;
+
+ /*
+ * TODO: give LDAP_OPERATIONS_ERROR also when
+ * there're pending requests or there's
+ * a SASL bind in progress
+ * (see rfc4513 section 3.1.1)
+ */
+ if (call->conn->sockets.tls) {
+ (*errstr) = talloc_asprintf(reply, "START-TLS: TLS is already enabled on this LDAP session");
+ return NT_STATUS_LDAP(LDAP_OPERATIONS_ERROR);
+ }
+
+ ctx = talloc(call, struct ldapsrv_starttls_context);
+ NT_STATUS_HAVE_NO_MEMORY(ctx);
+
+ ctx->conn = call->conn;
+ ctx->tls_socket = tls_init_server(call->conn->service->tls_params,
+ call->conn->connection->socket,
+ call->conn->connection->event.fde,
+ NULL);
+ if (!ctx->tls_socket) {
+ (*errstr) = talloc_asprintf(reply, "START-TLS: Failed to setup TLS socket");
+ return NT_STATUS_LDAP(LDAP_OPERATIONS_ERROR);
+ }
+
+ call->send_callback = ldapsrv_start_tls;
+ call->send_private = ctx;
+
+ reply->msg->r.ExtendedResponse.response.resultcode = LDAP_SUCCESS;
+ reply->msg->r.ExtendedResponse.response.errormessage = NULL;
+
+ ldapsrv_queue_reply(call, reply);
+ return NT_STATUS_OK;
+}
+
+struct ldapsrv_extended_operation {
+ const char *oid;
+ NTSTATUS (*fn)(struct ldapsrv_call *call, struct ldapsrv_reply *reply, const char **errorstr);
+};
+
+static struct ldapsrv_extended_operation extended_ops[] = {
+ {
+ .oid = LDB_EXTENDED_START_TLS_OID,
+ .fn = ldapsrv_StartTLS,
+ },{
+ .oid = NULL,
+ .fn = NULL,
+ }
+};
+
+NTSTATUS ldapsrv_ExtendedRequest(struct ldapsrv_call *call)
+{
+ struct ldap_ExtendedRequest *req = &call->request->r.ExtendedRequest;
+ struct ldapsrv_reply *reply;
+ int result = LDAP_PROTOCOL_ERROR;
+ const char *error_str = NULL;
+ NTSTATUS status = NT_STATUS_OK;
+ uint32_t i;
+
+ DEBUG(10, ("Extended\n"));
+
+ reply = ldapsrv_init_reply(call, LDAP_TAG_ExtendedResponse);
+ NT_STATUS_HAVE_NO_MEMORY(reply);
+
+ ZERO_STRUCT(reply->msg->r);
+ reply->msg->r.ExtendedResponse.oid = talloc_steal(reply, req->oid);
+ reply->msg->r.ExtendedResponse.response.resultcode = LDAP_PROTOCOL_ERROR;
+ reply->msg->r.ExtendedResponse.response.errormessage = NULL;
+
+ for (i=0; extended_ops[i].oid; i++) {
+ if (strcmp(extended_ops[i].oid,req->oid) != 0) continue;
+
+ /*
+ * if the backend function returns an error we
+ * need to send the reply otherwise the reply is already
+ * send and we need to return directly
+ */
+ status = extended_ops[i].fn(call, reply, &error_str);
+ NT_STATUS_IS_OK_RETURN(status);
+
+ if (NT_STATUS_IS_LDAP(status)) {
+ result = NT_STATUS_LDAP_CODE(status);
+ } else {
+ result = LDAP_OPERATIONS_ERROR;
+ error_str = talloc_asprintf(reply, "Extended Operation(%s) failed: %s",
+ req->oid, nt_errstr(status));
+ }
+ }
+ /* if we haven't found the oid, then status is still NT_STATUS_OK */
+ if (NT_STATUS_IS_OK(status)) {
+ error_str = talloc_asprintf(reply, "Extended Operation(%s) not supported",
+ req->oid);
+ }
+
+ reply->msg->r.ExtendedResponse.response.resultcode = result;
+ reply->msg->r.ExtendedResponse.response.errormessage = error_str;
+
+ ldapsrv_queue_reply(call, reply);
+ return NT_STATUS_OK;
+}
diff --git a/source4/ldap_server/ldap_server.c b/source4/ldap_server/ldap_server.c
new file mode 100644
index 0000000000..a924024160
--- /dev/null
+++ b/source4/ldap_server/ldap_server.c
@@ -0,0 +1,592 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ LDAP server
+
+ Copyright (C) Andrew Tridgell 2005
+ Copyright (C) Volker Lendecke 2004
+ Copyright (C) Stefan Metzmacher 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/events/events.h"
+#include "auth/auth.h"
+#include "auth/credentials/credentials.h"
+#include "librpc/gen_ndr/ndr_samr.h"
+#include "../lib/util/dlinklist.h"
+#include "../lib/util/asn1.h"
+#include "ldap_server/ldap_server.h"
+#include "smbd/service_task.h"
+#include "smbd/service_stream.h"
+#include "smbd/service.h"
+#include "smbd/process_model.h"
+#include "lib/tls/tls.h"
+#include "lib/messaging/irpc.h"
+#include "lib/ldb/include/ldb.h"
+#include "lib/ldb/include/ldb_errors.h"
+#include "libcli/ldap/ldap.h"
+#include "libcli/ldap/ldap_proto.h"
+#include "system/network.h"
+#include "lib/socket/netif.h"
+#include "dsdb/samdb/samdb.h"
+#include "param/param.h"
+/*
+ close the socket and shutdown a server_context
+*/
+void ldapsrv_terminate_connection(struct ldapsrv_connection *conn,
+ const char *reason)
+{
+ stream_terminate_connection(conn->connection, reason);
+}
+
+/*
+ handle packet errors
+*/
+static void ldapsrv_error_handler(void *private_data, NTSTATUS status)
+{
+ struct ldapsrv_connection *conn = talloc_get_type(private_data,
+ struct ldapsrv_connection);
+ ldapsrv_terminate_connection(conn, nt_errstr(status));
+}
+
+/*
+ process a decoded ldap message
+*/
+static void ldapsrv_process_message(struct ldapsrv_connection *conn,
+ struct ldap_message *msg)
+{
+ struct ldapsrv_call *call;
+ NTSTATUS status;
+ DATA_BLOB blob;
+
+ call = talloc(conn, struct ldapsrv_call);
+ if (!call) {
+ ldapsrv_terminate_connection(conn, "no memory");
+ return;
+ }
+
+ call->request = talloc_steal(call, msg);
+ call->conn = conn;
+ call->replies = NULL;
+ call->send_callback = NULL;
+ call->send_private = NULL;
+
+ /* make the call */
+ status = ldapsrv_do_call(call);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(call);
+ return;
+ }
+
+ blob = data_blob(NULL, 0);
+
+ if (call->replies == NULL) {
+ talloc_free(call);
+ return;
+ }
+
+ /* build all the replies into a single blob */
+ while (call->replies) {
+ DATA_BLOB b;
+ bool ret;
+
+ msg = call->replies->msg;
+ if (!ldap_encode(msg, samba_ldap_control_handlers(), &b, call)) {
+ DEBUG(0,("Failed to encode ldap reply of type %d\n", msg->type));
+ talloc_free(call);
+ return;
+ }
+
+ ret = data_blob_append(call, &blob, b.data, b.length);
+ data_blob_free(&b);
+
+ talloc_set_name_const(blob.data, "Outgoing, encoded LDAP packet");
+
+ if (!ret) {
+ talloc_free(call);
+ return;
+ }
+
+ DLIST_REMOVE(call->replies, call->replies);
+ }
+
+ packet_send_callback(conn->packet, blob,
+ call->send_callback, call->send_private);
+ talloc_free(call);
+ return;
+}
+
+/*
+ decode/process data
+*/
+static NTSTATUS ldapsrv_decode(void *private_data, DATA_BLOB blob)
+{
+ NTSTATUS status;
+ struct ldapsrv_connection *conn = talloc_get_type(private_data,
+ struct ldapsrv_connection);
+ struct asn1_data *asn1 = asn1_init(conn);
+ struct ldap_message *msg = talloc(conn, struct ldap_message);
+
+ if (asn1 == NULL || msg == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!asn1_load(asn1, blob)) {
+ talloc_free(msg);
+ talloc_free(asn1);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = ldap_decode(asn1, samba_ldap_control_handlers(), msg);
+ if (!NT_STATUS_IS_OK(status)) {
+ asn1_free(asn1);
+ return status;
+ }
+
+ data_blob_free(&blob);
+ talloc_steal(conn, msg);
+ asn1_free(asn1);
+
+ ldapsrv_process_message(conn, msg);
+ return NT_STATUS_OK;
+}
+
+/*
+ Idle timeout handler
+*/
+static void ldapsrv_conn_idle_timeout(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval t,
+ void *private_data)
+{
+ struct ldapsrv_connection *conn = talloc_get_type(private_data, struct ldapsrv_connection);
+
+ ldapsrv_terminate_connection(conn, "Timeout. No requests after bind");
+}
+
+/*
+ called when a LDAP socket becomes readable
+*/
+void ldapsrv_recv(struct stream_connection *c, uint16_t flags)
+{
+ struct ldapsrv_connection *conn =
+ talloc_get_type(c->private_data, struct ldapsrv_connection);
+
+ if (conn->limits.ite) { /* clean initial timeout if any */
+ talloc_free(conn->limits.ite);
+ conn->limits.ite = NULL;
+ }
+
+ if (conn->limits.te) { /* clean idle timeout if any */
+ talloc_free(conn->limits.te);
+ conn->limits.te = NULL;
+ }
+
+ packet_recv(conn->packet);
+
+ /* set idle timeout */
+ conn->limits.te = event_add_timed(c->event.ctx, conn,
+ timeval_current_ofs(conn->limits.conn_idle_time, 0),
+ ldapsrv_conn_idle_timeout, conn);
+}
+
+/*
+ called when a LDAP socket becomes writable
+*/
+static void ldapsrv_send(struct stream_connection *c, uint16_t flags)
+{
+ struct ldapsrv_connection *conn =
+ talloc_get_type(c->private_data, struct ldapsrv_connection);
+
+ packet_queue_run(conn->packet);
+}
+
+static void ldapsrv_conn_init_timeout(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval t,
+ void *private_data)
+{
+ struct ldapsrv_connection *conn = talloc_get_type(private_data, struct ldapsrv_connection);
+
+ ldapsrv_terminate_connection(conn, "Timeout. No requests after initial connection");
+}
+
+static int ldapsrv_load_limits(struct ldapsrv_connection *conn)
+{
+ TALLOC_CTX *tmp_ctx;
+ const char *attrs[] = { "configurationNamingContext", NULL };
+ const char *attrs2[] = { "lDAPAdminLimits", NULL };
+ struct ldb_message_element *el;
+ struct ldb_result *res = NULL;
+ struct ldb_dn *basedn;
+ struct ldb_dn *conf_dn;
+ struct ldb_dn *policy_dn;
+ int i,ret;
+
+ /* set defaults limits in case of failure */
+ conn->limits.initial_timeout = 120;
+ conn->limits.conn_idle_time = 900;
+ conn->limits.max_page_size = 1000;
+ conn->limits.search_timeout = 120;
+
+
+ tmp_ctx = talloc_new(conn);
+ if (tmp_ctx == NULL) {
+ return -1;
+ }
+
+ basedn = ldb_dn_new(tmp_ctx, conn->ldb, NULL);
+ if ( ! ldb_dn_validate(basedn)) {
+ goto failed;
+ }
+
+ ret = ldb_search(conn->ldb, tmp_ctx, &res, basedn, LDB_SCOPE_BASE, attrs, NULL);
+ if (ret != LDB_SUCCESS) {
+ goto failed;
+ }
+
+ if (res->count != 1) {
+ goto failed;
+ }
+
+ conf_dn = ldb_msg_find_attr_as_dn(conn->ldb, tmp_ctx, res->msgs[0], "configurationNamingContext");
+ if (conf_dn == NULL) {
+ goto failed;
+ }
+
+ policy_dn = ldb_dn_copy(tmp_ctx, conf_dn);
+ ldb_dn_add_child_fmt(policy_dn, "CN=Default Query Policy,CN=Query-Policies,CN=Directory Service,CN=Windows NT,CN=Services");
+ if (policy_dn == NULL) {
+ goto failed;
+ }
+
+ ret = ldb_search(conn->ldb, tmp_ctx, &res, policy_dn, LDB_SCOPE_BASE, attrs2, NULL);
+ if (ret != LDB_SUCCESS) {
+ goto failed;
+ }
+
+ if (res->count != 1) {
+ goto failed;
+ }
+
+ el = ldb_msg_find_element(res->msgs[0], "lDAPAdminLimits");
+ if (el == NULL) {
+ goto failed;
+ }
+
+ for (i = 0; i < el->num_values; i++) {
+ char policy_name[256];
+ int policy_value, s;
+
+ s = sscanf((const char *)el->values[i].data, "%255[^=]=%d", policy_name, &policy_value);
+ if (ret != 2 || policy_value == 0)
+ continue;
+
+ if (strcasecmp("InitRecvTimeout", policy_name) == 0) {
+ conn->limits.initial_timeout = policy_value;
+ continue;
+ }
+ if (strcasecmp("MaxConnIdleTime", policy_name) == 0) {
+ conn->limits.conn_idle_time = policy_value;
+ continue;
+ }
+ if (strcasecmp("MaxPageSize", policy_name) == 0) {
+ conn->limits.max_page_size = policy_value;
+ continue;
+ }
+ if (strcasecmp("MaxQueryDuration", policy_name) == 0) {
+ conn->limits.search_timeout = policy_value;
+ continue;
+ }
+ }
+
+ return 0;
+
+failed:
+ DEBUG(0, ("Failed to load ldap server query policies\n"));
+ talloc_free(tmp_ctx);
+ return -1;
+}
+
+/*
+ initialise a server_context from a open socket and register a event handler
+ for reading from that socket
+*/
+static void ldapsrv_accept(struct stream_connection *c)
+{
+ struct ldapsrv_service *ldapsrv_service =
+ talloc_get_type(c->private_data, struct ldapsrv_service);
+ struct ldapsrv_connection *conn;
+ struct cli_credentials *server_credentials;
+ struct socket_address *socket_address;
+ NTSTATUS status;
+ int port;
+
+ conn = talloc_zero(c, struct ldapsrv_connection);
+ if (!conn) {
+ stream_terminate_connection(c, "ldapsrv_accept: out of memory");
+ return;
+ }
+
+ conn->packet = NULL;
+ conn->connection = c;
+ conn->service = ldapsrv_service;
+ conn->sockets.raw = c->socket;
+ conn->lp_ctx = ldapsrv_service->task->lp_ctx;
+
+ c->private_data = conn;
+
+ socket_address = socket_get_my_addr(c->socket, conn);
+ if (!socket_address) {
+ ldapsrv_terminate_connection(conn, "ldapsrv_accept: failed to obtain local socket address!");
+ return;
+ }
+ port = socket_address->port;
+ talloc_free(socket_address);
+
+ if (port == 636) {
+ struct socket_context *tls_socket = tls_init_server(ldapsrv_service->tls_params, c->socket,
+ c->event.fde, NULL);
+ if (!tls_socket) {
+ ldapsrv_terminate_connection(conn, "ldapsrv_accept: tls_init_server() failed");
+ return;
+ }
+ talloc_unlink(c, c->socket);
+ talloc_steal(c, tls_socket);
+ c->socket = tls_socket;
+ conn->sockets.tls = tls_socket;
+
+ } else if (port == 3268) /* Global catalog */ {
+ conn->global_catalog = true;
+ }
+ conn->packet = packet_init(conn);
+ if (conn->packet == NULL) {
+ ldapsrv_terminate_connection(conn, "out of memory");
+ return;
+ }
+
+ packet_set_private(conn->packet, conn);
+ packet_set_socket(conn->packet, c->socket);
+ packet_set_callback(conn->packet, ldapsrv_decode);
+ packet_set_full_request(conn->packet, ldap_full_packet);
+ packet_set_error_handler(conn->packet, ldapsrv_error_handler);
+ packet_set_event_context(conn->packet, c->event.ctx);
+ packet_set_fde(conn->packet, c->event.fde);
+ packet_set_serialise(conn->packet);
+
+ if (conn->sockets.tls) {
+ packet_set_unreliable_select(conn->packet);
+ }
+
+ /* Ensure we don't get packets until the database is ready below */
+ packet_recv_disable(conn->packet);
+
+ server_credentials = cli_credentials_init(conn);
+ if (!server_credentials) {
+ stream_terminate_connection(c, "Failed to init server credentials\n");
+ return;
+ }
+
+ cli_credentials_set_conf(server_credentials, conn->lp_ctx);
+ status = cli_credentials_set_machine_account(server_credentials, conn->lp_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ stream_terminate_connection(c, talloc_asprintf(conn, "Failed to obtain server credentials, perhaps a standalone server?: %s\n", nt_errstr(status)));
+ return;
+ }
+ conn->server_credentials = server_credentials;
+
+ /* Connections start out anonymous */
+ if (!NT_STATUS_IS_OK(auth_anonymous_session_info(conn, c->event.ctx, conn->lp_ctx, &conn->session_info))) {
+ ldapsrv_terminate_connection(conn, "failed to setup anonymous session info");
+ return;
+ }
+
+ if (!NT_STATUS_IS_OK(ldapsrv_backend_Init(conn))) {
+ ldapsrv_terminate_connection(conn, "backend Init failed");
+ return;
+ }
+
+ /* load limits from the conf partition */
+ ldapsrv_load_limits(conn); /* should we fail on error ? */
+
+ /* register the server */
+ irpc_add_name(c->msg_ctx, "ldap_server");
+
+ /* set connections limits */
+ conn->limits.ite = event_add_timed(c->event.ctx, conn,
+ timeval_current_ofs(conn->limits.initial_timeout, 0),
+ ldapsrv_conn_init_timeout, conn);
+
+ packet_recv_enable(conn->packet);
+
+}
+
+static const struct stream_server_ops ldap_stream_ops = {
+ .name = "ldap",
+ .accept_connection = ldapsrv_accept,
+ .recv_handler = ldapsrv_recv,
+ .send_handler = ldapsrv_send,
+};
+
+/*
+ add a socket address to the list of events, one event per port
+*/
+static NTSTATUS add_socket(struct tevent_context *event_context,
+ struct loadparm_context *lp_ctx,
+ const struct model_ops *model_ops,
+ const char *address, struct ldapsrv_service *ldap_service)
+{
+ uint16_t port = 389;
+ NTSTATUS status;
+ struct ldb_context *ldb;
+
+ status = stream_setup_socket(event_context, lp_ctx,
+ model_ops, &ldap_stream_ops,
+ "ipv4", address, &port,
+ lp_socket_options(lp_ctx),
+ ldap_service);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("ldapsrv failed to bind to %s:%u - %s\n",
+ address, port, nt_errstr(status)));
+ }
+
+ if (tls_support(ldap_service->tls_params)) {
+ /* add ldaps server */
+ port = 636;
+ status = stream_setup_socket(event_context, lp_ctx,
+ model_ops, &ldap_stream_ops,
+ "ipv4", address, &port,
+ lp_socket_options(lp_ctx),
+ ldap_service);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("ldapsrv failed to bind to %s:%u - %s\n",
+ address, port, nt_errstr(status)));
+ }
+ }
+
+ /* Load LDAP database, but only to read our settings */
+ ldb = samdb_connect(ldap_service, ldap_service->task->event_ctx,
+ lp_ctx, system_session(ldap_service, lp_ctx));
+ if (!ldb) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ if (samdb_is_gc(ldb)) {
+ port = 3268;
+ status = stream_setup_socket(event_context, lp_ctx,
+ model_ops, &ldap_stream_ops,
+ "ipv4", address, &port,
+ lp_socket_options(lp_ctx),
+ ldap_service);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("ldapsrv failed to bind to %s:%u - %s\n",
+ address, port, nt_errstr(status)));
+ }
+ }
+
+ /* And once we are bound, free the tempoary ldb, it will
+ * connect again on each incoming LDAP connection */
+ talloc_free(ldb);
+
+ return status;
+}
+
+/*
+ open the ldap server sockets
+*/
+static void ldapsrv_task_init(struct task_server *task)
+{
+ char *ldapi_path;
+ struct ldapsrv_service *ldap_service;
+ NTSTATUS status;
+ const struct model_ops *model_ops;
+
+ switch (lp_server_role(task->lp_ctx)) {
+ case ROLE_STANDALONE:
+ task_server_terminate(task, "ldap_server: no LDAP server required in standalone configuration");
+ return;
+ case ROLE_DOMAIN_MEMBER:
+ task_server_terminate(task, "ldap_server: no LDAP server required in member server configuration");
+ return;
+ case ROLE_DOMAIN_CONTROLLER:
+ /* Yes, we want an LDAP server */
+ break;
+ }
+
+ task_server_set_title(task, "task[ldapsrv]");
+
+ /* run the ldap server as a single process */
+ model_ops = process_model_startup(task->event_ctx, "single");
+ if (!model_ops) goto failed;
+
+ ldap_service = talloc_zero(task, struct ldapsrv_service);
+ if (ldap_service == NULL) goto failed;
+
+ ldap_service->task = task;
+
+ ldap_service->tls_params = tls_initialise(ldap_service, task->lp_ctx);
+ if (ldap_service->tls_params == NULL) goto failed;
+
+ if (lp_interfaces(task->lp_ctx) && lp_bind_interfaces_only(task->lp_ctx)) {
+ struct interface *ifaces;
+ int num_interfaces;
+ int i;
+
+ load_interfaces(task, lp_interfaces(task->lp_ctx), &ifaces);
+ num_interfaces = iface_count(ifaces);
+
+ /* We have been given an interfaces line, and been
+ told to only bind to those interfaces. Create a
+ socket per interface and bind to only these.
+ */
+ for(i = 0; i < num_interfaces; i++) {
+ const char *address = iface_n_ip(ifaces, i);
+ status = add_socket(task->event_ctx, task->lp_ctx, model_ops, address, ldap_service);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+ }
+ } else {
+ status = add_socket(task->event_ctx, task->lp_ctx, model_ops,
+ lp_socket_address(task->lp_ctx), ldap_service);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+ }
+
+ ldapi_path = private_path(ldap_service, task->lp_ctx, "ldapi");
+ if (!ldapi_path) {
+ goto failed;
+ }
+
+ status = stream_setup_socket(task->event_ctx, task->lp_ctx,
+ model_ops, &ldap_stream_ops,
+ "unix", ldapi_path, NULL,
+ lp_socket_options(task->lp_ctx),
+ ldap_service);
+ talloc_free(ldapi_path);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("ldapsrv failed to bind to %s - %s\n",
+ ldapi_path, nt_errstr(status)));
+ }
+
+ return;
+
+failed:
+ task_server_terminate(task, "Failed to startup ldap server task");
+}
+
+
+NTSTATUS server_service_ldap_init(void)
+{
+ return register_server_service("ldap", ldapsrv_task_init);
+}
diff --git a/source4/ldap_server/ldap_server.h b/source4/ldap_server/ldap_server.h
new file mode 100644
index 0000000000..efa802d173
--- /dev/null
+++ b/source4/ldap_server/ldap_server.h
@@ -0,0 +1,71 @@
+/*
+ Unix SMB/CIFS implementation.
+ LDAP server
+ Copyright (C) Volker Lendecke 2004
+ Copyright (C) Stefan Metzmacher 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "libcli/ldap/ldap.h"
+#include "lib/socket/socket.h"
+#include "lib/stream/packet.h"
+
+struct ldapsrv_connection {
+ struct loadparm_context *lp_ctx;
+ struct stream_connection *connection;
+ struct gensec_security *gensec;
+ struct auth_session_info *session_info;
+ struct ldapsrv_service *service;
+ struct cli_credentials *server_credentials;
+ struct ldb_context *ldb;
+
+ struct {
+ struct socket_context *raw;
+ struct socket_context *tls;
+ struct socket_context *sasl;
+ } sockets;
+
+ bool global_catalog;
+
+ struct packet_context *packet;
+
+ struct {
+ int initial_timeout;
+ int conn_idle_time;
+ int max_page_size;
+ int search_timeout;
+
+ struct tevent_timer *ite;
+ struct tevent_timer *te;
+ } limits;
+};
+
+struct ldapsrv_call {
+ struct ldapsrv_connection *conn;
+ struct ldap_message *request;
+ struct ldapsrv_reply {
+ struct ldapsrv_reply *prev, *next;
+ struct ldap_message *msg;
+ } *replies;
+ packet_send_callback_fn_t send_callback;
+ void *send_private;
+};
+
+struct ldapsrv_service {
+ struct tls_params *tls_params;
+ struct task_server *task;
+};
+
+#include "ldap_server/proto.h"
diff --git a/source4/lib/basic.mk b/source4/lib/basic.mk
new file mode 100644
index 0000000000..4b40ed41d4
--- /dev/null
+++ b/source4/lib/basic.mk
@@ -0,0 +1,26 @@
+[SUBSYSTEM::LZXPRESS]
+
+LZXPRESS_OBJ_FILES = $(libcompressionsrcdir)/lzxpress.o
+
+[SUBSYSTEM::GENCACHE]
+PRIVATE_DEPENDENCIES = TDB_WRAP
+
+GENCACHE_OBJ_FILES = $(libgencachesrcdir)/gencache.o
+
+# PUBLIC_HEADERS += $(libgencachesrcdir)/gencache.h
+
+[SUBSYSTEM::LDB_WRAP]
+PUBLIC_DEPENDENCIES = LIBLDB
+PRIVATE_DEPENDENCIES = LDBSAMBA UTIL_LDB
+
+LDB_WRAP_OBJ_FILES = $(libsrcdir)/ldb_wrap.o
+PUBLIC_HEADERS += $(libsrcdir)/ldb_wrap.h
+
+[SUBSYSTEM::TDB_WRAP]
+PUBLIC_DEPENDENCIES = LIBTDB
+
+TDB_WRAP_OBJ_FILES = $(libsrcdir)/tdb_wrap.o
+PUBLIC_HEADERS += $(libsrcdir)/tdb_wrap.h
+
+SMBREADLINE_OBJ_LIST = $(SMBREADLINE_OBJ_FILES)
+
diff --git a/source4/lib/cmdline/config.mk b/source4/lib/cmdline/config.mk
new file mode 100644
index 0000000000..4434ff3701
--- /dev/null
+++ b/source4/lib/cmdline/config.mk
@@ -0,0 +1,21 @@
+[SUBSYSTEM::LIBCMDLINE_CREDENTIALS]
+PUBLIC_DEPENDENCIES = CREDENTIALS LIBPOPT
+
+LIBCMDLINE_CREDENTIALS_OBJ_FILES = $(libcmdlinesrcdir)/credentials.o
+
+$(eval $(call proto_header_template,$(libcmdlinesrcdir)/credentials.h,$(LIBCMDLINE_CREDENTIALS_OBJ_FILES:.o=.c)))
+
+[SUBSYSTEM::POPT_SAMBA]
+PUBLIC_DEPENDENCIES = LIBPOPT
+
+POPT_SAMBA_OBJ_FILES = $(libcmdlinesrcdir)/popt_common.o
+
+PUBLIC_HEADERS += $(libcmdlinesrcdir)/popt_common.h
+
+[SUBSYSTEM::POPT_CREDENTIALS]
+PUBLIC_DEPENDENCIES = CREDENTIALS LIBCMDLINE_CREDENTIALS LIBPOPT
+PRIVATE_DEPENDENCIES = LIBSAMBA-UTIL
+
+POPT_CREDENTIALS_OBJ_FILES = $(libcmdlinesrcdir)/popt_credentials.o
+
+$(eval $(call proto_header_template,$(libcmdlinesrcdir)/popt_credentials.h,$(POPT_CREDENTIALS_OBJ_FILES:.o=.c)))
diff --git a/source4/lib/cmdline/credentials.c b/source4/lib/cmdline/credentials.c
new file mode 100644
index 0000000000..f919842e6a
--- /dev/null
+++ b/source4/lib/cmdline/credentials.c
@@ -0,0 +1,50 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "auth/credentials/credentials.h"
+#include "lib/cmdline/credentials.h"
+
+static const char *cmdline_get_userpassword(struct cli_credentials *credentials)
+{
+ char *ret;
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+
+ const char *prompt_name = cli_credentials_get_unparsed_name(credentials, mem_ctx);
+ const char *prompt;
+
+ prompt = talloc_asprintf(mem_ctx, "Password for [%s]:",
+ prompt_name);
+
+ ret = getpass(prompt);
+
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+bool cli_credentials_set_cmdline_callbacks(struct cli_credentials *cred)
+{
+ if (isatty(fileno(stdout))) {
+ cli_credentials_set_password_callback(cred, cmdline_get_userpassword);
+ return true;
+ }
+
+ return false;
+}
diff --git a/source4/lib/cmdline/popt_common.c b/source4/lib/cmdline/popt_common.c
new file mode 100644
index 0000000000..712d99996c
--- /dev/null
+++ b/source4/lib/cmdline/popt_common.c
@@ -0,0 +1,213 @@
+/*
+ Unix SMB/CIFS implementation.
+ Common popt routines
+
+ Copyright (C) Tim Potter 2001,2002
+ Copyright (C) Jelmer Vernooij 2002,2003,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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "version.h"
+#include "lib/cmdline/popt_common.h"
+#include "param/param.h"
+
+/* Handle command line options:
+ * -d,--debuglevel
+ * -s,--configfile
+ * -O,--socket-options
+ * -V,--version
+ * -l,--log-base
+ * -n,--netbios-name
+ * -W,--workgroup
+ * --realm
+ * -i,--scope
+ */
+
+enum {OPT_OPTION=1,OPT_LEAK_REPORT,OPT_LEAK_REPORT_FULL,OPT_DEBUG_STDERR};
+
+struct cli_credentials *cmdline_credentials = NULL;
+struct loadparm_context *cmdline_lp_ctx = NULL;
+
+static void popt_version_callback(poptContext con,
+ enum poptCallbackReason reason,
+ const struct poptOption *opt,
+ const char *arg, const void *data)
+{
+ switch(opt->val) {
+ case 'V':
+ printf("Version %s\n", SAMBA_VERSION_STRING );
+ exit(0);
+ }
+}
+
+static void popt_samba_callback(poptContext con,
+ enum poptCallbackReason reason,
+ const struct poptOption *opt,
+ const char *arg, const void *data)
+{
+ const char *pname;
+
+ if (reason == POPT_CALLBACK_REASON_POST) {
+ if (lp_configfile(cmdline_lp_ctx) == NULL) {
+ lp_load_default(cmdline_lp_ctx);
+ }
+ /* Hook any 'every Samba program must do this, after
+ * the smb.conf is setup' functions here */
+ return;
+ }
+
+ /* Find out basename of current program */
+ pname = strrchr_m(poptGetInvocationName(con),'/');
+
+ if (!pname)
+ pname = poptGetInvocationName(con);
+ else
+ pname++;
+
+ if (reason == POPT_CALLBACK_REASON_PRE) {
+ cmdline_lp_ctx = loadparm_init(talloc_autofree_context());
+
+ /* Hook for 'almost the first thing to do in a samba program' here */
+ /* setup for panics */
+ fault_setup(poptGetInvocationName(con));
+
+ /* and logging */
+ setup_logging(pname, DEBUG_STDOUT);
+
+ return;
+ }
+
+ switch(opt->val) {
+
+ case OPT_LEAK_REPORT:
+ talloc_enable_leak_report();
+ break;
+
+ case OPT_LEAK_REPORT_FULL:
+ talloc_enable_leak_report_full();
+ break;
+
+ case OPT_OPTION:
+ if (!lp_set_option(cmdline_lp_ctx, arg)) {
+ fprintf(stderr, "Error setting option '%s'\n", arg);
+ exit(1);
+ }
+ break;
+
+ case 'd':
+ lp_set_cmdline(cmdline_lp_ctx, "log level", arg);
+ break;
+
+ case OPT_DEBUG_STDERR:
+ setup_logging(pname, DEBUG_STDERR);
+ break;
+
+ case 's':
+ if (arg) {
+ lp_load(cmdline_lp_ctx, arg);
+ }
+ break;
+
+ case 'l':
+ if (arg) {
+ char *new_logfile = talloc_asprintf(NULL, "%s/log.%s", arg, pname);
+ lp_set_cmdline(cmdline_lp_ctx, "log file", new_logfile);
+ talloc_free(new_logfile);
+ }
+ break;
+
+
+ }
+
+}
+
+
+static void popt_common_callback(poptContext con,
+ enum poptCallbackReason reason,
+ const struct poptOption *opt,
+ const char *arg, const void *data)
+{
+ struct loadparm_context *lp_ctx = cmdline_lp_ctx;
+
+ switch(opt->val) {
+ case 'O':
+ if (arg) {
+ lp_set_cmdline(lp_ctx, "socket options", arg);
+ }
+ break;
+
+ case 'W':
+ lp_set_cmdline(lp_ctx, "workgroup", arg);
+ break;
+
+ case 'r':
+ lp_set_cmdline(lp_ctx, "realm", arg);
+ break;
+
+ case 'n':
+ lp_set_cmdline(lp_ctx, "netbios name", arg);
+ break;
+
+ case 'i':
+ lp_set_cmdline(lp_ctx, "netbios scope", arg);
+ break;
+
+ case 'm':
+ lp_set_cmdline(lp_ctx, "client max protocol", arg);
+ break;
+
+ case 'R':
+ lp_set_cmdline(lp_ctx, "name resolve order", arg);
+ break;
+
+ case 'S':
+ lp_set_cmdline(lp_ctx, "client signing", arg);
+ break;
+
+ }
+}
+
+struct poptOption popt_common_connection[] = {
+ { NULL, 0, POPT_ARG_CALLBACK, (void *)popt_common_callback },
+ { "name-resolve", 'R', POPT_ARG_STRING, NULL, 'R', "Use these name resolution services only", "NAME-RESOLVE-ORDER" },
+ { "socket-options", 'O', POPT_ARG_STRING, NULL, 'O', "socket options to use", "SOCKETOPTIONS" },
+ { "netbiosname", 'n', POPT_ARG_STRING, NULL, 'n', "Primary netbios name", "NETBIOSNAME" },
+ { "signing", 'S', POPT_ARG_STRING, NULL, 'S', "Set the client signing state", "on|off|required" },
+ { "workgroup", 'W', POPT_ARG_STRING, NULL, 'W', "Set the workgroup name", "WORKGROUP" },
+ { "realm", 0, POPT_ARG_STRING, NULL, 'r', "Set the realm name", "REALM" },
+ { "scope", 'i', POPT_ARG_STRING, NULL, 'i', "Use this Netbios scope", "SCOPE" },
+ { "maxprotocol", 'm', POPT_ARG_STRING, NULL, 'm', "Set max protocol level", "MAXPROTOCOL" },
+ { NULL }
+};
+
+struct poptOption popt_common_samba[] = {
+ { NULL, 0, POPT_ARG_CALLBACK|POPT_CBFLAG_PRE|POPT_CBFLAG_POST, (void *)popt_samba_callback },
+ { "debuglevel", 'd', POPT_ARG_STRING, NULL, 'd', "Set debug level", "DEBUGLEVEL" },
+ { "debug-stderr", 0, POPT_ARG_NONE, NULL, OPT_DEBUG_STDERR, "Send debug output to STDERR", NULL },
+ { "configfile", 's', POPT_ARG_STRING, NULL, 's', "Use alternative configuration file", "CONFIGFILE" },
+ { "option", 0, POPT_ARG_STRING, NULL, OPT_OPTION, "Set smb.conf option from command line", "name=value" },
+ { "log-basename", 'l', POPT_ARG_STRING, NULL, 'l', "Basename for log/debug files", "LOGFILEBASE" },
+ { "leak-report", 0, POPT_ARG_NONE, NULL, OPT_LEAK_REPORT, "enable talloc leak reporting on exit", NULL },
+ { "leak-report-full",0, POPT_ARG_NONE, NULL, OPT_LEAK_REPORT_FULL, "enable full talloc leak reporting on exit", NULL },
+ { NULL }
+};
+
+struct poptOption popt_common_version[] = {
+ { NULL, 0, POPT_ARG_CALLBACK, (void *)popt_version_callback },
+ { "version", 'V', POPT_ARG_NONE, NULL, 'V', "Print version" },
+ { NULL }
+};
+
diff --git a/source4/lib/cmdline/popt_common.h b/source4/lib/cmdline/popt_common.h
new file mode 100644
index 0000000000..733d12a443
--- /dev/null
+++ b/source4/lib/cmdline/popt_common.h
@@ -0,0 +1,41 @@
+/*
+ Unix SMB/CIFS implementation.
+ Common popt arguments
+ Copyright (C) Jelmer Vernooij 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _POPT_COMMON_H
+#define _POPT_COMMON_H
+
+#include <popt.h>
+
+/* Common popt structures */
+extern struct poptOption popt_common_samba[];
+extern struct poptOption popt_common_connection[];
+extern struct poptOption popt_common_version[];
+extern struct poptOption popt_common_credentials[];
+
+#define POPT_COMMON_SAMBA { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_samba, 0, "Common samba options:", NULL },
+#define POPT_COMMON_CONNECTION { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_connection, 0, "Connection options:", NULL },
+#define POPT_COMMON_VERSION { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_version, 0, "Common samba options:", NULL },
+#define POPT_COMMON_CREDENTIALS { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_credentials, 0, "Authentication options:", NULL },
+
+extern struct cli_credentials *cmdline_credentials;
+extern struct loadparm_context *cmdline_lp_ctx;
+
+void popt_common_dont_ask(void);
+
+#endif /* _POPT_COMMON_H */
diff --git a/source4/lib/cmdline/popt_credentials.c b/source4/lib/cmdline/popt_credentials.c
new file mode 100644
index 0000000000..42ecac1eaa
--- /dev/null
+++ b/source4/lib/cmdline/popt_credentials.c
@@ -0,0 +1,139 @@
+/*
+ Unix SMB/CIFS implementation.
+ Credentials popt routines
+
+ Copyright (C) Jelmer Vernooij 2002,2003,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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/cmdline/popt_common.h"
+#include "lib/cmdline/credentials.h"
+#include "auth/credentials/credentials.h"
+#include "auth/gensec/gensec.h"
+#include "param/param.h"
+
+/* Handle command line options:
+ * -U,--user
+ * -A,--authentication-file
+ * -k,--use-kerberos
+ * -N,--no-pass
+ * -S,--signing
+ * -P --machine-pass
+ * --simple-bind-dn
+ * --password
+ */
+
+
+static bool dont_ask;
+
+enum opt { OPT_SIMPLE_BIND_DN, OPT_PASSWORD, OPT_KERBEROS };
+
+/*
+ disable asking for a password
+*/
+void popt_common_dont_ask(void)
+{
+ dont_ask = true;
+}
+
+static void popt_common_credentials_callback(poptContext con,
+ enum poptCallbackReason reason,
+ const struct poptOption *opt,
+ const char *arg, const void *data)
+{
+ if (reason == POPT_CALLBACK_REASON_PRE) {
+ cmdline_credentials = cli_credentials_init(talloc_autofree_context());
+ return;
+ }
+
+ if (reason == POPT_CALLBACK_REASON_POST) {
+ cli_credentials_guess(cmdline_credentials, cmdline_lp_ctx);
+
+ if (!dont_ask) {
+ cli_credentials_set_cmdline_callbacks(cmdline_credentials);
+ }
+ return;
+ }
+
+ switch(opt->val) {
+ case 'U':
+ {
+ char *lp;
+
+ cli_credentials_parse_string(cmdline_credentials, arg, CRED_SPECIFIED);
+ /* This breaks the abstraction, including the const above */
+ if ((lp=strchr_m(arg,'%'))) {
+ lp[0]='\0';
+ lp++;
+ /* Try to prevent this showing up in ps */
+ memset(lp,0,strlen(lp));
+ }
+ }
+ break;
+
+ case OPT_PASSWORD:
+ cli_credentials_set_password(cmdline_credentials, arg, CRED_SPECIFIED);
+ /* Try to prevent this showing up in ps */
+ memset(discard_const(arg),0,strlen(arg));
+ break;
+
+ case 'A':
+ cli_credentials_parse_file(cmdline_credentials, arg, CRED_SPECIFIED);
+ break;
+
+ case 'P':
+ /* Later, after this is all over, get the machine account details from the secrets.ldb */
+ cli_credentials_set_machine_account_pending(cmdline_credentials, cmdline_lp_ctx);
+ break;
+
+ case OPT_KERBEROS:
+ {
+ bool use_kerberos = true;
+ /* Force us to only use kerberos */
+ if (arg) {
+ if (!set_boolean(arg, &use_kerberos)) {
+ fprintf(stderr, "Error parsing -k %s\n", arg);
+ exit(1);
+ break;
+ }
+ }
+
+ cli_credentials_set_kerberos_state(cmdline_credentials,
+ use_kerberos
+ ? CRED_MUST_USE_KERBEROS
+ : CRED_DONT_USE_KERBEROS);
+ break;
+ }
+
+ case OPT_SIMPLE_BIND_DN:
+ cli_credentials_set_bind_dn(cmdline_credentials, arg);
+ break;
+ }
+}
+
+
+
+struct poptOption popt_common_credentials[] = {
+ { NULL, 0, POPT_ARG_CALLBACK|POPT_CBFLAG_PRE|POPT_CBFLAG_POST, (void *)popt_common_credentials_callback },
+ { "user", 'U', POPT_ARG_STRING, NULL, 'U', "Set the network username", "[DOMAIN/]USERNAME[%PASSWORD]" },
+ { "no-pass", 'N', POPT_ARG_NONE, &dont_ask, 'N', "Don't ask for a password" },
+ { "password", 0, POPT_ARG_STRING, NULL, OPT_PASSWORD, "Password" },
+ { "authentication-file", 'A', POPT_ARG_STRING, NULL, 'A', "Get the credentials from a file", "FILE" },
+ { "machine-pass", 'P', POPT_ARG_NONE, NULL, 'P', "Use stored machine account password (implies -k)" },
+ { "simple-bind-dn", 0, POPT_ARG_STRING, NULL, OPT_SIMPLE_BIND_DN, "DN to use for a simple bind" },
+ { "kerberos", 'k', POPT_ARG_STRING, NULL, OPT_KERBEROS, "Use Kerberos" },
+ { NULL }
+};
diff --git a/source4/lib/com/README b/source4/lib/com/README
new file mode 100644
index 0000000000..361024e02c
--- /dev/null
+++ b/source4/lib/com/README
@@ -0,0 +1,9 @@
+This directory contains Samba's very simple COM implementation.
+It is by no means finished yet.
+
+The main aim of this implementation is for use by our DCOM implementation,
+which lives in the dcom subdirectory. The local version is used mostly for
+testing.
+
+More information on this effort can be found in the DCOM whitepaper in
+the lorikeet repository.
diff --git a/source4/lib/com/classes/simple.c b/source4/lib/com/classes/simple.c
new file mode 100644
index 0000000000..295f113207
--- /dev/null
+++ b/source4/lib/com/classes/simple.c
@@ -0,0 +1,122 @@
+/*
+ Unix SMB/CIFS implementation.
+ Simple class
+ Copyright (C) 2004-2005 Jelmer Vernooij <jelmer@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU 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 "lib/com/com.h"
+#include "librpc/gen_ndr/com_dcom.h"
+
+static struct IClassFactory_vtable simple_classobject_vtable;
+static struct IStream_vtable simple_IStream_vtable;
+
+static WERROR simple_IUnknown_QueryInterface (struct IUnknown *d, TALLOC_CTX *mem_ctx, struct GUID *iid, struct IUnknown **iun)
+{
+ *iun = d;
+ return WERR_OK;
+}
+
+static uint32_t simple_IUnknown_AddRef (struct IUnknown *d, TALLOC_CTX *mem_ctx)
+{
+ return 1;
+}
+
+static uint32_t simple_IUnknown_Release (struct IUnknown *d, TALLOC_CTX *mem_ctx)
+{
+ return 1;
+}
+
+static WERROR simple_IStream_Read (struct IStream *d, TALLOC_CTX *mem_ctx, uint8_t *pv, uint32_t num_requested, uint32_t *num_readx, uint32_t num_read)
+{
+ printf("%d bytes are being read\n", num_read);
+ return WERR_OK;
+}
+
+static WERROR simple_IStream_Write (struct IStream *d, TALLOC_CTX *mem_ctx, uint8_t *data, uint32_t num_requested, uint32_t num_written)
+{
+ printf("%d bytes are being written\n", num_requested);
+ return WERR_OK;
+}
+
+static WERROR simpleclass_IUnknown_QueryInterface (struct IUnknown *d, TALLOC_CTX *mem_ctx, struct GUID *iid, struct IUnknown **iun)
+{
+ /* FIXME: Return WERR_IFACE_NOT_SUPPORTED if IID != IID_IUNKNOWN and IID != IID_CLASSFACTORY */
+ *iun = d;
+ return WERR_OK;
+}
+
+static WERROR simpleclass_IClassFactory_CreateInstance (struct IClassFactory *d, TALLOC_CTX *mem_ctx, struct IUnknown *iunk, struct GUID *iid, struct IUnknown **ppv)
+{
+ struct IStream *ret;
+ /* FIXME: Check whether IID == ISTREAM_IID */
+ ret = talloc(mem_ctx, struct IStream);
+ ret->ctx = NULL;
+ ret->vtable = &simple_IStream_vtable;
+ ret->object_data = NULL;
+
+ *ppv = (struct IUnknown *)ret;
+
+ return WERR_OK;
+}
+
+static uint32_t simpleclass_IUnknown_AddRef (struct IUnknown *d, TALLOC_CTX *mem_ctx)
+{
+ return 1;
+}
+
+static uint32_t simpleclass_IUnknown_Release (struct IUnknown *d, TALLOC_CTX *mem_ctx)
+{
+ return 1;
+}
+
+/* Everything below this line should be autogenerated later on */
+static struct IClassFactory_vtable simple_classobject_vtable = {
+ { 0, 0, 0, { 0, 0 }, { 0, 0, 0, 0, 0, 0 } },
+ simpleclass_IUnknown_QueryInterface,
+ simpleclass_IUnknown_AddRef,
+ simpleclass_IUnknown_Release,
+ simpleclass_IClassFactory_CreateInstance,
+ NULL,
+ NULL,
+ NULL
+};
+
+static struct IStream_vtable simple_IStream_vtable = {
+ { 0, 0, 0, { 0, 0 }, { 0, 0, 0, 0, 0, 0 } },
+ simple_IUnknown_QueryInterface,
+ simple_IUnknown_AddRef,
+ simple_IUnknown_Release,
+ simple_IStream_Read,
+ simple_IStream_Write
+};
+
+NTSTATUS com_simple_init(void)
+{
+ struct GUID clsid;
+ struct IUnknown *class_object = talloc(talloc_autofree_context(), struct IUnknown);
+
+ class_object->ctx = NULL;
+ class_object->object_data = NULL;
+ class_object->vtable = (struct IUnknown_vtable *)&simple_classobject_vtable;
+
+ GUID_from_string(CLSID_SIMPLE, &clsid);
+ GUID_from_string(COM_ICLASSFACTORY_UUID, &simple_classobject_vtable.iid);
+ GUID_from_string(COM_ISTREAM_UUID, &simple_IStream_vtable.iid);
+
+ return com_register_running_class(&clsid, PROGID_SIMPLE, class_object);
+}
diff --git a/source4/lib/com/com.h b/source4/lib/com/com.h
new file mode 100644
index 0000000000..82d10daf4f
--- /dev/null
+++ b/source4/lib/com/com.h
@@ -0,0 +1,52 @@
+/*
+ Unix SMB/CIFS implementation.
+ Utility functions for Samba
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __SAMBA_COM_H__
+#define __SAMBA_COM_H__
+
+#include "librpc/gen_ndr/misc.h"
+
+struct com_context;
+struct tevent_context;
+
+struct com_context
+{
+ struct dcom_client_context *dcom;
+ struct tevent_context *event_ctx;
+ struct com_extension {
+ uint32_t id;
+ void *data;
+ struct com_extension *prev, *next;
+ } *extensions;
+ struct loadparm_context *lp_ctx;
+};
+
+struct IUnknown *com_class_by_clsid(struct com_context *ctx, const struct GUID *clsid);
+NTSTATUS com_register_running_class(struct GUID *clsid, const char *progid, struct IUnknown *p);
+
+struct dcom_interface_p *dcom_get_local_iface_p(struct GUID *ipid);
+
+WERROR com_init_ctx(struct com_context **ctx, struct tevent_context *event_ctx);
+WERROR com_create_object(struct com_context *ctx, struct GUID *clsid, int num_ifaces, struct GUID *iid, struct IUnknown **ip, WERROR *results);
+WERROR com_get_class_object(struct com_context *ctx, struct GUID *clsid, struct GUID *iid, struct IUnknown **ip);
+NTSTATUS com_init(void);
+
+typedef struct IUnknown *(*get_class_object_function) (const struct GUID *clsid);
+
+#endif /* __SAMBA_COM_H__ */
diff --git a/source4/lib/com/config.mk b/source4/lib/com/config.mk
new file mode 100644
index 0000000000..73836ef5f8
--- /dev/null
+++ b/source4/lib/com/config.mk
@@ -0,0 +1,22 @@
+[SUBSYSTEM::COM]
+PRIVATE_DEPENDENCIES = LIBSAMBA-UTIL LIBSAMBA-HOSTCONFIG LIBEVENTS LIBNDR
+
+COM_OBJ_FILES = $(addprefix $(comsrcdir)/, tables.o rot.o main.o)
+
+[SUBSYSTEM::DCOM]
+PUBLIC_DEPENDENCIES = COM DCOM_PROXY_DCOM RPC_NDR_REMACT \
+ RPC_NDR_OXIDRESOLVER
+
+DCOM_OBJ_FILES = $(addprefix $(comsrcdir)/dcom/, main.o tables.o)
+
+[MODULE::com_simple]
+SUBSYSTEM = COM
+INIT_FUNCTION = com_simple_init
+
+com_simple_OBJ_FILES = $(comsrcdir)/classes/simple.o
+
+[PYTHON::pycom]
+LIBRARY_REALNAME = samba/com.$(SHLIBEXT)
+PRIVATE_DEPENDENCIES = COM
+
+pycom_OBJ_FILES = $(comsrcdir)/pycom.o
diff --git a/source4/lib/com/dcom/dcom.h b/source4/lib/com/dcom/dcom.h
new file mode 100644
index 0000000000..56d6eac93c
--- /dev/null
+++ b/source4/lib/com/dcom/dcom.h
@@ -0,0 +1,85 @@
+/*
+ Unix SMB/CIFS implementation.
+ COM standard objects
+ 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 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU 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.
+*/
+
+#ifndef _DCOM_H /* _DCOM_H */
+#define _DCOM_H
+
+struct cli_credentials;
+struct dcerpc_pipe;
+
+#include "lib/com/com.h"
+#include "librpc/gen_ndr/orpc.h"
+
+struct dcom_client_context {
+ struct dcom_server_credentials {
+ const char *server;
+ struct cli_credentials *credentials;
+ struct dcom_server_credentials *prev, *next;
+ } *credentials;
+ struct dcom_object_exporter {
+ uint64_t oxid;
+ char *host;
+ struct IRemUnknown *rem_unknown;
+ struct DUALSTRINGARRAY *bindings;
+ struct dcerpc_pipe *pipe;
+ struct dcom_object_exporter *prev, *next;
+ } *object_exporters;
+};
+
+typedef enum ndr_err_code (*marshal_fn)(TALLOC_CTX *mem_ctx, struct IUnknown *pv, struct OBJREF *o);
+typedef enum ndr_err_code (*unmarshal_fn)(TALLOC_CTX *mem_ctx, struct OBJREF *o, struct IUnknown **pv);
+
+
+struct dcom_client_context *dcom_client_init(struct com_context *ctx, struct cli_credentials *credentials);
+struct dcom_object_exporter *object_exporter_by_oxid(struct com_context *ctx, uint64_t oxid);
+struct dcom_object_exporter *object_exporter_by_ip(struct com_context *ctx, struct IUnknown *ip);
+WERROR dcom_create_object(struct com_context *ctx, struct GUID *clsid, const char *server, int num_ifaces, struct GUID *iid, struct IUnknown ***ip, WERROR *results);
+WERROR dcom_get_class_object(struct com_context *ctx, struct GUID *clsid, const char *server, struct GUID *iid, struct IUnknown **ip);
+NTSTATUS dcom_get_pipe(struct IUnknown *iface, struct dcerpc_pipe **pp);
+NTSTATUS dcom_OBJREF_from_IUnknown(struct OBJREF *o, struct IUnknown *p);
+NTSTATUS dcom_IUnknown_from_OBJREF(TALLOC_CTX *mem_ctx, struct com_context *ctx, struct IUnknown **_p, struct OBJREF *o);
+uint64_t dcom_get_current_oxid(void);
+void dcom_add_server_credentials(struct com_context *ctx, const char *server, struct cli_credentials *credentials);
+WERROR dcom_query_interface(struct IUnknown *d, uint32_t cRefs, uint16_t cIids, struct GUID *iids, struct IUnknown **ip, WERROR *results);
+
+#include "librpc/gen_ndr/com_dcom.h"
+
+NTSTATUS dcom_register_proxy(struct IUnknown_vtable *proxy_vtable);
+struct IUnknown_vtable *dcom_proxy_vtable_by_iid(struct GUID *iid);
+NTSTATUS dcom_register_marshal(struct GUID *clsid, marshal_fn marshal, unmarshal_fn unmarshal);
+
+#include "libcli/composite/composite.h"
+void dcom_release_continue(struct composite_context *cr);
+#define IUnknown_ipid(d) ((d)->obj.u_objref.u_standard.std.ipid)
+struct composite_context *dcom_release_send(struct IUnknown *d, TALLOC_CTX *mem_ctx);
+marshal_fn dcom_marshal_by_clsid(struct GUID *clsid);
+unmarshal_fn dcom_unmarshal_by_clsid(struct GUID *clsid);
+
+struct dcom_proxy_async_call_state {
+ struct IUnknown *d;
+ const struct ndr_interface_table *table;
+ uint32_t opnum;
+ void (*continuation)(struct rpc_request *);
+ TALLOC_CTX *mem_ctx;
+ void *r;
+};
+
+
+#endif /* _DCOM_H */
diff --git a/source4/lib/com/dcom/main.c b/source4/lib/com/dcom/main.c
new file mode 100644
index 0000000000..695bfa7f98
--- /dev/null
+++ b/source4/lib/com/dcom/main.c
@@ -0,0 +1,710 @@
+/*
+ Unix SMB/CIFS implementation.
+ Main DCOM functionality
+ Copyright (C) 2004 Jelmer Vernooij <jelmer@samba.org>
+ Copyright (C) 2006 Andrzej Hajda <andrzej.hajda@wp.pl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU 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/epmapper.h"
+#include "librpc/gen_ndr/ndr_remact_c.h"
+#include "librpc/gen_ndr/com_dcom.h"
+#include "librpc/gen_ndr/dcom.h"
+#include "librpc/rpc/dcerpc.h"
+#include "lib/com/dcom/dcom.h"
+#include "librpc/ndr/ndr_table.h"
+#include "../lib/util/dlinklist.h"
+#include "auth/credentials/credentials.h"
+#include "libcli/composite/composite.h"
+
+#define DCOM_NEGOTIATED_PROTOCOLS { EPM_PROTOCOL_TCP, EPM_PROTOCOL_SMB, EPM_PROTOCOL_NCALRPC }
+
+static NTSTATUS dcerpc_binding_from_STRINGBINDING(TALLOC_CTX *mem_ctx, struct dcerpc_binding **b_out, struct STRINGBINDING *bd)
+{
+ char *host, *endpoint;
+ struct dcerpc_binding *b;
+
+ b = talloc_zero(mem_ctx, struct dcerpc_binding);
+ if (!b) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ b->transport = dcerpc_transport_by_endpoint_protocol(bd->wTowerId);
+
+ if (b->transport == -1) {
+ DEBUG(1, ("Can't find transport match endpoint protocol %d\n", bd->wTowerId));
+ talloc_free(b);
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ host = talloc_strdup(b, bd->NetworkAddr);
+ endpoint = strchr(host, '[');
+
+ if (endpoint) {
+ *endpoint = '\0';
+ endpoint++;
+
+ endpoint[strlen(endpoint)-1] = '\0';
+ }
+
+ b->host = host;
+ b->endpoint = talloc_strdup(b, endpoint);
+
+ *b_out = b;
+ return NT_STATUS_OK;
+}
+
+struct cli_credentials *dcom_get_server_credentials(struct com_context *ctx, const char *server)
+{
+ struct dcom_server_credentials *c;
+ struct cli_credentials *d;
+
+ d = NULL;
+ for (c = ctx->dcom->credentials; c; c = c->next) {
+ if (c->server == NULL) {
+ d = c->credentials;
+ continue;
+ }
+ if (server && !strcmp(c->server, server)) return c->credentials;
+ }
+ return d;
+}
+
+/**
+ * Register credentials for a specific server.
+ *
+ * @param ctx COM context
+ * @param server Name of server, can be NULL
+ * @param credentials Credentials object
+ */
+void dcom_add_server_credentials(struct com_context *ctx, const char *server,
+ struct cli_credentials *credentials)
+{
+ struct dcom_server_credentials *c;
+
+ /* FIXME: Don't use talloc_find_parent_bytype */
+ for (c = ctx->dcom->credentials; c; c = c->next) {
+ if ((server == NULL && c->server == NULL) ||
+ (server != NULL && c->server != NULL &&
+ !strcmp(c->server, server))) {
+ if (c->credentials && c->credentials != credentials) {
+ talloc_unlink(c, c->credentials);
+ c->credentials = credentials;
+ if (talloc_find_parent_bytype(c->credentials, struct dcom_server_credentials))
+ (void)talloc_reference(c, c->credentials);
+ else
+ talloc_steal(c, c->credentials);
+ }
+
+ return;
+ }
+ }
+
+ c = talloc(ctx->event_ctx, struct dcom_server_credentials);
+ c->server = talloc_strdup(c, server);
+ c->credentials = credentials;
+ if (talloc_find_parent_bytype(c->credentials, struct dcom_server_credentials))
+ (void)talloc_reference(c, c->credentials);
+ else
+ talloc_steal(c, c->credentials);
+
+ DLIST_ADD(ctx->dcom->credentials, c);
+}
+
+void dcom_update_credentials_for_aliases(struct com_context *ctx,
+ const char *server,
+ struct DUALSTRINGARRAY *pds)
+{
+ struct cli_credentials *cc;
+ struct dcerpc_binding *b;
+ uint32_t i;
+ NTSTATUS status;
+
+ cc = dcom_get_server_credentials(ctx, server);
+ for (i = 0; pds->stringbindings[i]; ++i) {
+ if (pds->stringbindings[i]->wTowerId != EPM_PROTOCOL_TCP)
+ continue;
+ status = dcerpc_binding_from_STRINGBINDING(ctx, &b, pds->stringbindings[i]);
+ if (!NT_STATUS_IS_OK(status))
+ continue;
+ dcom_add_server_credentials(ctx, b->host, cc);
+ talloc_free(b);
+ }
+}
+
+struct dcom_client_context *dcom_client_init(struct com_context *ctx, struct cli_credentials *credentials)
+{
+ ctx->dcom = talloc_zero(ctx, struct dcom_client_context);
+ if (!credentials) {
+ credentials = cli_credentials_init(ctx);
+ cli_credentials_set_conf(credentials, ctx->lp_ctx);
+ cli_credentials_parse_string(credentials, "%", CRED_SPECIFIED);
+ }
+ dcom_add_server_credentials(ctx, NULL, credentials);
+ return ctx->dcom;
+}
+
+static NTSTATUS dcom_connect_host(struct com_context *ctx,
+ struct dcerpc_pipe **p, const char *server)
+{
+ struct dcerpc_binding *bd;
+ const char * available_transports[] = { "ncacn_ip_tcp", "ncacn_np" };
+ int i;
+ NTSTATUS status;
+ TALLOC_CTX *loc_ctx;
+
+ if (server == NULL) {
+ return dcerpc_pipe_connect(ctx->event_ctx, p, "ncalrpc",
+ &ndr_table_IRemoteActivation,
+ dcom_get_server_credentials(ctx, NULL), ctx->event_ctx, ctx->lp_ctx);
+ }
+ loc_ctx = talloc_new(ctx);
+
+ /* Allow server name to contain a binding string */
+ if (strchr(server, ':') &&
+ NT_STATUS_IS_OK(dcerpc_parse_binding(loc_ctx, server, &bd))) {
+ if (DEBUGLVL(11))
+ bd->flags |= DCERPC_DEBUG_PRINT_BOTH;
+ status = dcerpc_pipe_connect_b(ctx->event_ctx, p, bd,
+ &ndr_table_IRemoteActivation,
+ dcom_get_server_credentials(ctx, bd->host), ctx->event_ctx, ctx->lp_ctx);
+ goto end;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(available_transports); i++)
+ {
+ char *binding = talloc_asprintf(loc_ctx, "%s:%s", available_transports[i], server);
+ if (!binding) {
+ status = NT_STATUS_NO_MEMORY;
+ goto end;
+ }
+ status = dcerpc_pipe_connect(ctx->event_ctx, p, binding,
+ &ndr_table_IRemoteActivation,
+ dcom_get_server_credentials(ctx, server),
+ ctx->event_ctx, ctx->lp_ctx);
+
+ if (NT_STATUS_IS_OK(status)) {
+ if (DEBUGLVL(11))
+ (*p)->conn->flags |= DCERPC_DEBUG_PRINT_BOTH;
+ goto end;
+ } else {
+ DEBUG(1,(__location__": dcom_connect_host : %s\n", get_friendly_nt_error_msg(status)));
+ }
+ }
+
+end:
+ talloc_free(loc_ctx);
+ return status;
+}
+
+struct dcom_object_exporter *object_exporter_by_oxid(struct com_context *ctx,
+ uint64_t oxid)
+{
+ struct dcom_object_exporter *ox;
+ for (ox = ctx->dcom->object_exporters; ox; ox = ox->next) {
+ if (ox->oxid == oxid) {
+ return ox;
+ }
+ }
+
+ return NULL;
+}
+
+struct dcom_object_exporter *object_exporter_update_oxid(struct com_context *ctx, uint64_t oxid, struct DUALSTRINGARRAY *bindings)
+{
+ struct dcom_object_exporter *ox;
+ ox = object_exporter_by_oxid(ctx, oxid);
+ if (!ox) {
+ ox = talloc_zero(ctx, struct dcom_object_exporter);
+ DLIST_ADD(ctx->dcom->object_exporters, ox);
+ ox->oxid = oxid;
+ } else {
+ talloc_free(ox->bindings);
+ }
+ ox->bindings = bindings;
+ talloc_steal(ox, bindings);
+ return ox;
+}
+
+struct dcom_object_exporter *object_exporter_by_ip(struct com_context *ctx, struct IUnknown *ip)
+{
+ return object_exporter_by_oxid(ctx, ip->obj.u_objref.u_standard.std.oxid);
+}
+
+WERROR dcom_create_object(struct com_context *ctx, struct GUID *clsid, const char *server, int num_ifaces, struct GUID *iid, struct IUnknown ***ip, WERROR *results)
+{
+ uint16_t protseq[] = DCOM_NEGOTIATED_PROTOCOLS;
+ struct dcerpc_pipe *p;
+ struct dcom_object_exporter *m;
+ NTSTATUS status;
+ struct RemoteActivation r;
+ struct DUALSTRINGARRAY *pds;
+ int i;
+ WERROR hr;
+ uint64_t oxid;
+ struct GUID ipidRemUnknown;
+ struct IUnknown *ru_template;
+ struct ORPCTHAT that;
+ uint32_t AuthnHint;
+ struct COMVERSION ServerVersion;
+ struct MInterfacePointer **ifaces;
+ TALLOC_CTX *loc_ctx;
+
+ status = dcom_connect_host(ctx, &p, server);
+ if (NT_STATUS_IS_ERR(status)) {
+ DEBUG(1, ("Unable to connect to %s - %s\n", server, get_friendly_nt_error_msg(status)));
+ return ntstatus_to_werror(status);
+ }
+ loc_ctx = talloc_new(ctx);
+
+ ifaces = talloc_array(loc_ctx, struct MInterfacePointer *, num_ifaces);
+
+ ZERO_STRUCT(r.in);
+ r.in.this.version.MajorVersion = COM_MAJOR_VERSION;
+ r.in.this.version.MinorVersion = COM_MINOR_VERSION;
+ r.in.this.cid = GUID_random();
+ r.in.Clsid = *clsid;
+ r.in.ClientImpLevel = RPC_C_IMP_LEVEL_IDENTIFY;
+ r.in.num_protseqs = ARRAY_SIZE(protseq);
+ r.in.protseq = protseq;
+ r.in.Interfaces = num_ifaces;
+ r.in.pIIDs = iid;
+ r.out.that = &that;
+ r.out.pOxid = &oxid;
+ r.out.pdsaOxidBindings = &pds;
+ r.out.ipidRemUnknown = &ipidRemUnknown;
+ r.out.AuthnHint = &AuthnHint;
+ r.out.ServerVersion = &ServerVersion;
+ r.out.hr = &hr;
+ r.out.ifaces = ifaces;
+ r.out.results = results;
+
+ status = dcerpc_RemoteActivation(p, loc_ctx, &r);
+ talloc_free(p);
+
+ if(NT_STATUS_IS_ERR(status)) {
+ DEBUG(1, ("Error while running RemoteActivation %s\n", nt_errstr(status)));
+ hr = ntstatus_to_werror(status);
+ goto end;
+ }
+
+ if(!W_ERROR_IS_OK(r.out.result)) {
+ hr = r.out.result;
+ goto end;
+ }
+
+ if(!W_ERROR_IS_OK(hr)) {
+ goto end;
+ }
+
+ m = object_exporter_update_oxid(ctx, oxid, pds);
+
+ ru_template = NULL;
+ *ip = talloc_array(ctx, struct IUnknown *, num_ifaces);
+ for (i = 0; i < num_ifaces; i++) {
+ (*ip)[i] = NULL;
+ if (W_ERROR_IS_OK(results[i])) {
+ status = dcom_IUnknown_from_OBJREF(ctx, &(*ip)[i], &r.out.ifaces[i]->obj);
+ if (!NT_STATUS_IS_OK(status)) {
+ results[i] = ntstatus_to_werror(status);
+ } else if (!ru_template)
+ ru_template = (*ip)[i];
+ }
+ }
+
+ /* TODO:avg check when exactly oxid should be updated,its lifetime etc */
+ if (m->rem_unknown && memcmp(&m->rem_unknown->obj.u_objref.u_standard.std.ipid, &ipidRemUnknown, sizeof(ipidRemUnknown))) {
+ talloc_free(m->rem_unknown);
+ m->rem_unknown = NULL;
+ }
+ if (!m->rem_unknown) {
+ if (!ru_template) {
+ DEBUG(1,("dcom_create_object: Cannot Create IRemUnknown - template interface not available\n"));
+ hr = WERR_GENERAL_FAILURE;
+ }
+ m->rem_unknown = talloc_zero(m, struct IRemUnknown);
+ memcpy(m->rem_unknown, ru_template, sizeof(struct IUnknown));
+ GUID_from_string(COM_IREMUNKNOWN_UUID, &m->rem_unknown->obj.iid);
+ m->rem_unknown->obj.u_objref.u_standard.std.ipid = ipidRemUnknown;
+ m->rem_unknown->vtable = (struct IRemUnknown_vtable *)dcom_proxy_vtable_by_iid(&m->rem_unknown->obj.iid);
+ /* TODO:avg copy stringbindigs?? */
+ }
+
+ dcom_update_credentials_for_aliases(ctx, server, pds);
+ {
+ char *c;
+ c = strchr(server, '[');
+ if (m->host) talloc_free(m->host);
+ m->host = c ? talloc_strndup(m, server, c - server) : talloc_strdup(m, server);
+ }
+ hr = WERR_OK;
+end:
+ talloc_free(loc_ctx);
+ return hr;
+}
+
+int find_similar_binding(struct STRINGBINDING **sb, const char *host)
+{
+ int i, l;
+ l = strlen(host);
+ for (i = 0; sb[i]; ++i) {
+ if ((sb[i]->wTowerId == EPM_PROTOCOL_TCP) && !strncasecmp(host, sb[i]->NetworkAddr, l) && (sb[i]->NetworkAddr[l] == '['))
+ break;
+ }
+ return i;
+}
+
+WERROR dcom_query_interface(struct IUnknown *d, uint32_t cRefs, uint16_t cIids, struct GUID *iids, struct IUnknown **ip, WERROR *results)
+{
+ struct dcom_object_exporter *ox;
+ struct REMQIRESULT *rqir;
+ WERROR result;
+ NTSTATUS status;
+ int i;
+ TALLOC_CTX *loc_ctx;
+ struct IUnknown ru;
+
+ loc_ctx = talloc_new(d);
+ ox = object_exporter_by_ip(d->ctx, d);
+
+ result = IRemUnknown_RemQueryInterface(ox->rem_unknown, loc_ctx, &IUnknown_ipid(d), cRefs, cIids, iids, &rqir);
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(1, ("dcom_query_interface failed: %08X\n", W_ERROR_V(result)));
+ talloc_free(loc_ctx);
+ return result;
+ }
+ ru = *(struct IUnknown *)ox->rem_unknown;
+ for (i = 0; i < cIids; ++i) {
+ ip[i] = NULL;
+ results[i] = rqir[i].hResult;
+ if (W_ERROR_IS_OK(results[i])) {
+ ru.obj.iid = iids[i];
+ ru.obj.u_objref.u_standard.std = rqir[i].std;
+ status = dcom_IUnknown_from_OBJREF(d->ctx, &ip[i], &ru.obj);
+ if (!NT_STATUS_IS_OK(status)) {
+ results[i] = ntstatus_to_werror(status);
+ }
+ }
+ }
+
+ talloc_free(loc_ctx);
+ return WERR_OK;
+}
+
+int is_ip_binding(const char* s)
+{
+ while (*s && (*s != '[')) {
+ if (((*s >= '0') && (*s <= '9')) || *s == '.')
+ ++s;
+ else
+ return 0;
+ }
+ return 1;
+}
+
+NTSTATUS dcom_get_pipe(struct IUnknown *iface, struct dcerpc_pipe **pp)
+{
+ struct dcerpc_binding *binding;
+ struct GUID iid;
+ uint64_t oxid;
+ NTSTATUS status;
+ int i, j, isimilar;
+ struct dcerpc_pipe *p;
+ struct dcom_object_exporter *ox;
+ const struct ndr_interface_table *table;
+
+ ox = object_exporter_by_oxid(iface->ctx, iface->obj.u_objref.u_standard.std.oxid);
+ if (!ox) {
+ DEBUG(0, ("dcom_get_pipe: OXID not found\n"));
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ p = ox->pipe;
+
+ iid = iface->vtable->iid;
+ table = ndr_table_by_uuid(&iid);
+ if (table == NULL) {
+ char *guid_str;
+ guid_str = GUID_string(NULL, &iid);
+ DEBUG(0,(__location__": dcom_get_pipe - unrecognized interface{%s}\n", guid_str));
+ talloc_free(guid_str);
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ if (p && p->last_fault_code) {
+ talloc_free(p);
+ ox->pipe = p = NULL;
+ }
+
+ if (p) {
+ if (!GUID_equal(&p->syntax.uuid, &iid)) {
+ ox->pipe->syntax.uuid = iid;
+
+ /* interface will always be present, so
+ * idl_iface_by_uuid can't return NULL */
+ /* status = dcerpc_secondary_context(p, &p2, idl_iface_by_uuid(&iid)); */
+ status = dcerpc_alter_context(p, p, &ndr_table_by_uuid(&iid)->syntax_id, &p->transfer_syntax);
+ } else
+ status = NT_STATUS_OK;
+ *pp = p;
+ return status;
+ }
+
+ status = NT_STATUS_NO_MORE_ENTRIES;
+
+ /* To avoid delays whe connecting nonroutable bindings we 1st check binding starting with hostname */
+ /* FIX:low create concurrent connections to all bindings, fastest wins - Win2k and newer does this way???? */
+ isimilar = find_similar_binding(ox->bindings->stringbindings, ox->host);
+ DEBUG(1, (__location__": dcom_get_pipe: host=%s, similar=%s\n", ox->host, ox->bindings->stringbindings[isimilar] ? ox->bindings->stringbindings[isimilar]->NetworkAddr : "None"));
+ j = isimilar - 1;
+ for (i = 0; ox->bindings->stringbindings[i]; ++i) {
+ if (!ox->bindings->stringbindings[++j]) j = 0;
+ /* FIXME:LOW Use also other transports if possible */
+ if ((j != isimilar) && (ox->bindings->stringbindings[j]->wTowerId != EPM_PROTOCOL_TCP || !is_ip_binding(ox->bindings->stringbindings[j]->NetworkAddr))) {
+ DEBUG(9, ("dcom_get_pipe: Skipping stringbinding %24.24s\n", ox->bindings->stringbindings[j]->NetworkAddr));
+ continue;
+ }
+ DEBUG(9, ("dcom_get_pipe: Trying stringbinding %s\n", ox->bindings->stringbindings[j]->NetworkAddr));
+ status = dcerpc_binding_from_STRINGBINDING(iface->ctx, &binding,
+ ox->bindings->stringbindings[j]);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Error parsing string binding"));
+ } else {
+ /* FIXME:LOW Make flags more flexible */
+ binding->flags |= DCERPC_AUTH_NTLM | DCERPC_SIGN;
+ if (DEBUGLVL(11))
+ binding->flags |= DCERPC_DEBUG_PRINT_BOTH;
+ status = dcerpc_pipe_connect_b(iface->ctx->event_ctx, &p, binding,
+ ndr_table_by_uuid(&iid),
+ dcom_get_server_credentials(iface->ctx, binding->host),
+ iface->ctx->event_ctx, iface->ctx->lp_ctx);
+ talloc_unlink(iface->ctx, binding);
+ }
+ if (NT_STATUS_IS_OK(status)) break;
+ }
+
+ if (NT_STATUS_IS_ERR(status)) {
+ DEBUG(0, ("Unable to connect to remote host - %s\n", nt_errstr(status)));
+ return status;
+ }
+
+ DEBUG(2, ("Successfully connected to OXID %llx\n", (long long)oxid));
+
+ ox->pipe = *pp = p;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS dcom_OBJREF_from_IUnknown(TALLLOC_CTX *mem_ctx, struct OBJREF *o, struct IUnknown *p)
+{
+ /* FIXME: Cache generated objref objects? */
+ ZERO_STRUCTP(o);
+
+ if (!p) {
+ o->signature = OBJREF_SIGNATURE;
+ o->flags = OBJREF_NULL;
+ } else {
+ *o = p->obj;
+ switch(o->flags) {
+ case OBJREF_CUSTOM: {
+ marshal_fn marshal;
+
+ marshal = dcom_marshal_by_clsid(&o->u_objref.u_custom.clsid);
+ if (marshal) {
+ return marshal(mem_ctx, p, o);
+ } else {
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+ }
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+enum ndr_err_code dcom_IUnknown_from_OBJREF(struct com_context *ctx, struct IUnknown **_p, struct OBJREF *o)
+{
+ struct IUnknown *p;
+ struct dcom_object_exporter *ox;
+ unmarshal_fn unmarshal;
+
+ switch(o->flags) {
+ case OBJREF_NULL:
+ *_p = NULL;
+ return NDR_ERR_SUCCESS;
+
+ case OBJREF_STANDARD:
+ p = talloc_zero(ctx, struct IUnknown);
+ p->ctx = ctx;
+ p->obj = *o;
+ p->vtable = dcom_proxy_vtable_by_iid(&o->iid);
+
+ if (!p->vtable) {
+ DEBUG(0, ("Unable to find proxy class for interface with IID %s\n", GUID_string(ctx, &o->iid)));
+ return NDR_ERR_INVALID_POINTER;
+ }
+
+ p->vtable->Release_send = dcom_release_send;
+
+ ox = object_exporter_by_oxid(ctx, o->u_objref.u_standard.std.oxid);
+ /* FIXME: Add object to list of objects to ping */
+ *_p = p;
+ return NDR_ERR_SUCCESS;
+
+ case OBJREF_HANDLER:
+ p = talloc_zero(ctx, struct IUnknown);
+ p->ctx = ctx;
+ p->obj = *o;
+ ox = object_exporter_by_oxid(ctx, o->u_objref.u_handler.std.oxid );
+ /* FIXME: Add object to list of objects to ping */
+/*FIXME p->vtable = dcom_vtable_by_clsid(&o->u_objref.u_handler.clsid);*/
+ /* FIXME: Do the custom unmarshaling call */
+
+ *_p = p;
+ return NDR_ERR_BAD_SWITCH;
+
+ case OBJREF_CUSTOM:
+ p = talloc_zero(ctx, struct IUnknown);
+ p->ctx = ctx;
+ p->vtable = NULL;
+ p->obj = *o;
+ unmarshal = dcom_unmarshal_by_clsid(&o->u_objref.u_custom.clsid);
+ *_p = p;
+ if (unmarshal) {
+ return unmarshal(ctx, o, _p);
+ } else {
+ return NDR_ERR_BAD_SWITCH;
+ }
+ }
+
+ return NDR_ERR_BAD_SWITCH;
+}
+
+uint64_t dcom_get_current_oxid(void)
+{
+ return getpid();
+}
+
+/* FIXME:Fake async dcom_get_pipe_* */
+struct composite_context *dcom_get_pipe_send(struct IUnknown *d, TALLOC_CTX *mem_ctx)
+{
+ struct composite_context *c;
+
+ c = composite_create(0, d->ctx->event_ctx);
+ if (c == NULL) return NULL;
+ c->private_data = d;
+ /* composite_done(c); bugged - callback is triggered twice by composite_continue and composite_done */
+ c->state = COMPOSITE_STATE_DONE; /* this is workaround */
+
+ return c;
+}
+
+NTSTATUS dcom_get_pipe_recv(struct composite_context *c, struct dcerpc_pipe **pp)
+{
+ NTSTATUS status;
+
+ status = dcom_get_pipe((struct IUnknown *)c->private_data, pp);
+ talloc_free(c);
+
+ return status;
+}
+
+/* FIXME:avg put IUnknown_Release_out into header */
+struct IUnknown_Release_out {
+ uint32_t result;
+};
+
+void dcom_release_continue(struct composite_context *cr)
+{
+ struct composite_context *c;
+ struct IUnknown *d;
+ struct IUnknown_Release_out *out;
+ WERROR r;
+
+ c = talloc_get_type(cr->async.private_data, struct composite_context);
+ d = c->private_data;
+ r = IRemUnknown_RemRelease_recv(cr);
+ talloc_free(d);
+ out = talloc_zero(c, struct IUnknown_Release_out);
+ out->result = W_ERROR_V(r);
+ c->private_data = out;
+ composite_done(c);
+}
+
+struct composite_context *dcom_release_send(struct IUnknown *d, TALLOC_CTX *mem_ctx)
+{
+ struct composite_context *c, *cr;
+ struct REMINTERFACEREF iref;
+ struct dcom_object_exporter *ox;
+
+ c = composite_create(d->ctx, d->ctx->event_ctx);
+ if (c == NULL) return NULL;
+ c->private_data = d;
+
+ ox = object_exporter_by_ip(d->ctx, d);
+ iref.ipid = IUnknown_ipid(d);
+ iref.cPublicRefs = 5;
+ iref.cPrivateRefs = 0;
+ cr = IRemUnknown_RemRelease_send(ox->rem_unknown, mem_ctx, 1, &iref);
+
+ composite_continue(c, cr, dcom_release_continue, c);
+ return c;
+}
+
+uint32_t dcom_release_recv(struct composite_context *c)
+{
+ NTSTATUS status;
+ WERROR r;
+
+ status = composite_wait(c);
+ if (!NT_STATUS_IS_OK(status))
+ r = ntstatus_to_werror(status);
+ else
+ W_ERROR_V(r) = ((struct IUnknown_Release_out *)c->private_data)->result;
+ talloc_free(c);
+ return W_ERROR_IS_OK(r) ? 0 : W_ERROR_V(r);
+}
+
+uint32_t dcom_release(void *interface, TALLOC_CTX *mem_ctx)
+{
+ struct composite_context *c;
+
+ c = dcom_release_send(interface, mem_ctx);
+ return dcom_release_recv(c);
+}
+
+void dcom_proxy_async_call_recv_pipe_send_rpc(struct composite_context *c_pipe)
+{
+ struct composite_context *c;
+ struct dcom_proxy_async_call_state *s;
+ struct dcerpc_pipe *p;
+ struct rpc_request *req;
+ NTSTATUS status;
+
+ c = c_pipe->async.private_data;
+ s = talloc_get_type(c->private_data, struct dcom_proxy_async_call_state);
+
+ status = dcom_get_pipe_recv(c_pipe, &p);
+ if (!NT_STATUS_IS_OK(status)) {
+ composite_error(c, NT_STATUS_RPC_NT_CALL_FAILED);
+ return;
+ }
+
+ req = dcerpc_ndr_request_send(p, &s->d->obj.u_objref.u_standard.std.ipid, s->table, s->opnum, s, s->r);
+ composite_continue_rpc(c, req, s->continuation, c);
+}
diff --git a/source4/lib/com/dcom/tables.c b/source4/lib/com/dcom/tables.c
new file mode 100644
index 0000000000..f94aa87925
--- /dev/null
+++ b/source4/lib/com/dcom/tables.c
@@ -0,0 +1,92 @@
+/*
+ Unix SMB/CIFS implementation.
+ DCOM proxy tables functionality
+ Copyright (C) 2005 Jelmer Vernooij <jelmer@samba.org>
+ Copyright (C) 2006 Andrzej Hajda <andrzej.hajda@wp.pl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU 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 "../lib/util/dlinklist.h"
+#include "librpc/gen_ndr/com_dcom.h"
+#include "lib/com/dcom/dcom.h"
+
+static struct dcom_proxy {
+ struct IUnknown_vtable *vtable;
+ struct dcom_proxy *prev, *next;
+} *proxies = NULL;
+
+NTSTATUS dcom_register_proxy(struct IUnknown_vtable *proxy_vtable)
+{
+ struct dcom_proxy *proxy = talloc(talloc_autofree_context(), struct dcom_proxy);
+
+ proxy->vtable = proxy_vtable;
+ DLIST_ADD(proxies, proxy);
+
+ return NT_STATUS_OK;
+}
+
+struct IUnknown_vtable *dcom_proxy_vtable_by_iid(struct GUID *iid)
+{
+ struct dcom_proxy *p;
+ for (p = proxies; p; p = p->next) {
+ if (GUID_equal(&p->vtable->iid, iid)) {
+ return p->vtable;
+ }
+ }
+ return NULL;
+}
+
+static struct dcom_marshal {
+ struct GUID clsid;
+ marshal_fn marshal;
+ unmarshal_fn unmarshal;
+ struct dcom_marshal *prev, *next;
+} *marshals = NULL;
+
+NTSTATUS dcom_register_marshal(struct GUID *clsid, marshal_fn marshal, unmarshal_fn unmarshal)
+{
+ struct dcom_marshal *p = talloc(talloc_autofree_context(), struct dcom_marshal);
+
+ p->clsid = *clsid;
+ p->marshal = marshal;
+ p->unmarshal = unmarshal;
+ DLIST_ADD(marshals, p);
+ return NT_STATUS_OK;
+}
+
+_PUBLIC_ marshal_fn dcom_marshal_by_clsid(struct GUID *clsid)
+{
+ struct dcom_marshal *p;
+ for (p = marshals; p; p = p->next) {
+ if (GUID_equal(&p->clsid, clsid)) {
+ return p->marshal;
+ }
+ }
+ return NULL;
+}
+
+_PUBLIC_ unmarshal_fn dcom_unmarshal_by_clsid(struct GUID *clsid)
+{
+ struct dcom_marshal *p;
+ for (p = marshals; p; p = p->next) {
+ if (GUID_equal(&p->clsid, clsid)) {
+ return p->unmarshal;
+ }
+ }
+ return NULL;
+}
+
diff --git a/source4/lib/com/main.c b/source4/lib/com/main.c
new file mode 100644
index 0000000000..062d1360ac
--- /dev/null
+++ b/source4/lib/com/main.c
@@ -0,0 +1,90 @@
+/*
+ Unix SMB/CIFS implementation.
+ Main COM functionality
+ Copyright (C) 2004 Jelmer Vernooij <jelmer@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU 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 "../lib/util/dlinklist.h"
+#include "lib/com/com.h"
+#include "lib/events/events.h"
+#include "librpc/gen_ndr/com_dcom.h"
+
+WERROR com_init_ctx(struct com_context **ctx, struct tevent_context *event_ctx)
+{
+ *ctx = talloc(NULL, struct com_context);
+ if (event_ctx == NULL) {
+ event_ctx = event_context_init(*ctx);
+ }
+ (*ctx)->event_ctx = event_ctx;
+ return WERR_OK;
+}
+
+WERROR com_create_object(struct com_context *ctx, struct GUID *clsid, int num_ifaces, struct GUID *iid, struct IUnknown **ip, WERROR *results)
+{
+ struct IUnknown *iunk = NULL;
+ struct IClassFactory *factory;
+ WERROR error;
+ int i;
+ struct GUID classfact_iid;
+
+ GUID_from_string(NDR_ICLASSFACTORY_UUID, &classfact_iid);
+
+ /* Obtain class object */
+ error = com_get_class_object(ctx, clsid, &classfact_iid, (struct IUnknown **)&factory);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(3, ("Unable to obtain class object for %s\n", GUID_string(NULL, clsid)));
+ return error;
+ }
+
+ /* Run IClassFactory::CreateInstance() */
+ error = IClassFactory_CreateInstance(factory, ctx, NULL, &classfact_iid, &iunk);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(3, ("Error while calling IClassFactory::CreateInstance : %s\n", win_errstr(error)));
+ return error;
+ }
+
+ if (!iunk) {
+ DEBUG(0, ("IClassFactory_CreateInstance returned success but result pointer is still NULL!\n"));
+ return WERR_GENERAL_FAILURE;
+ }
+
+ /* Release class object */
+ IUnknown_Release(factory, ctx);
+
+ error = WERR_OK;
+
+ /* Do one or more QueryInterface calls */
+ for (i = 0; i < num_ifaces; i++) {
+ results[i] = IUnknown_QueryInterface(iunk, ctx, &iid[i], &ip[i]);
+ if (!W_ERROR_IS_OK(results[i])) error = results[i];
+ }
+
+ return error;
+}
+
+WERROR com_get_class_object(struct com_context *ctx, struct GUID *clsid, struct GUID *iid, struct IUnknown **ip)
+{
+ struct IUnknown *iu;
+
+ iu = com_class_by_clsid(ctx, clsid);
+ if (!iu) {
+ return WERR_CLASS_NOT_REGISTERED;
+ }
+
+ return IUnknown_QueryInterface(iu, ctx, iid, ip);
+}
diff --git a/source4/lib/com/pycom.c b/source4/lib/com/pycom.c
new file mode 100644
index 0000000000..d5a07580ea
--- /dev/null
+++ b/source4/lib/com/pycom.c
@@ -0,0 +1,85 @@
+/*
+ Unix SMB/CIFS implementation.
+ Python bindings for COM library.
+ Copyright (C) Jelmer Vernooij <jelmer@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include <Python.h>
+#include "lib/com/com.h"
+#include "librpc/ndr/libndr.h"
+#include "libcli/util/pyerrors.h"
+
+#ifndef Py_RETURN_NONE
+#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
+#endif
+
+static struct com_context *py_com_ctx = NULL; /* FIXME: evil global */
+
+static PyObject *py_get_class_object(PyObject *self, PyObject *args)
+{
+ char *s_clsid, *s_iid;
+ struct GUID clsid, iid;
+ struct IUnknown *object;
+ NTSTATUS status;
+ WERROR error;
+
+ if (!PyArg_ParseTuple(args, "ss", &s_clsid, &s_iid))
+ return NULL;
+
+ status = GUID_from_string(s_clsid, &clsid);
+ if (!NT_STATUS_IS_OK(status)) {
+ PyErr_FromNTSTATUS(status);
+ return NULL;
+ }
+
+ status = GUID_from_string(s_iid, &iid);
+ if (!NT_STATUS_IS_OK(status)) {
+ PyErr_FromNTSTATUS(status);
+ return NULL;
+ }
+
+ error = com_get_class_object(py_com_ctx, &clsid, &iid, &object);
+ if (!W_ERROR_IS_OK(error)) {
+ PyErr_FromWERROR(error);
+ return NULL;
+ }
+
+ /* FIXME: Magic, integrate with stubs generated by pidl. */
+
+ Py_RETURN_NONE;
+}
+
+static struct PyMethodDef com_methods[] = {
+ { "get_class_object", (PyCFunction)py_get_class_object, METH_VARARGS, "S.get_class_object(clsid, iid) -> instance" },
+ { NULL },
+};
+
+void initcom(void)
+{
+ PyObject *m;
+ WERROR error;
+
+ error = com_init_ctx(&py_com_ctx, NULL);
+ if (!W_ERROR_IS_OK(error)) {
+ PyErr_FromWERROR(error);
+ return;
+ }
+
+ m = Py_InitModule3("com", com_methods, "Simple COM implementation");
+ if (m == NULL)
+ return;
+}
diff --git a/source4/lib/com/rot.c b/source4/lib/com/rot.c
new file mode 100644
index 0000000000..0180a92f1b
--- /dev/null
+++ b/source4/lib/com/rot.c
@@ -0,0 +1,35 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Running object table functions
+
+ 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 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU 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 "lib/com/com.h"
+
+struct dcom_interface_p *dcom_get_local_iface_p(struct GUID *ipid)
+{
+ /* FIXME: Call the local ROT and do a
+ * rot_get_interface_pointer call */
+
+ /* FIXME: Perhaps have a local (thread-local) table with
+ * local DCOM objects so that not every DCOM call requires a lookup
+ * to the ROT? */
+ return NULL;
+}
diff --git a/source4/lib/com/tables.c b/source4/lib/com/tables.c
new file mode 100644
index 0000000000..ed21da7cd8
--- /dev/null
+++ b/source4/lib/com/tables.c
@@ -0,0 +1,109 @@
+/*
+ Unix SMB/CIFS implementation.
+ COM class tables
+ Copyright (C) 2004 Jelmer Vernooij <jelmer@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU 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 "../lib/util/dlinklist.h"
+#include "lib/com/com.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+
+/* Specific implementation of one or more interfaces */
+struct com_class
+{
+ const char *progid;
+ struct GUID clsid;
+
+ struct IUnknown *class_object;
+ struct com_class *prev, *next;
+} * running_classes = NULL;
+
+static struct IUnknown *get_com_class_running(const struct GUID *clsid)
+{
+ struct com_class *c = running_classes;
+
+ while(c) {
+
+ if (GUID_equal(clsid, &c->clsid)) {
+ return c->class_object;
+ }
+
+ c = c->next;
+ }
+
+ return NULL;
+}
+
+static struct IUnknown *get_com_class_so(TALLOC_CTX *mem_ctx, const struct GUID *clsid)
+{
+ char *mod_name;
+ char *clsid_str;
+ void *mod;
+ get_class_object_function f;
+
+ clsid_str = GUID_string(mem_ctx, clsid);
+ mod_name = talloc_asprintf(mem_ctx, "%s.so", clsid_str);
+ talloc_free(clsid_str);
+
+ mod = dlopen(mod_name, 0);
+
+ if (!mod) {
+ return NULL;
+ }
+
+ f = dlsym(mod, "get_class_object");
+
+ if (!f) {
+ return NULL;
+ }
+
+ return f(clsid);
+}
+
+struct IUnknown *com_class_by_clsid(struct com_context *ctx, const struct GUID *clsid)
+{
+ struct IUnknown *c;
+
+ /* Check list of running COM classes first */
+ c = get_com_class_running(clsid);
+
+ if (c != NULL) {
+ return c;
+ }
+
+ c = get_com_class_so(ctx, clsid);
+
+ if (c != NULL) {
+ return c;
+ }
+
+ return NULL;
+}
+
+NTSTATUS com_register_running_class(struct GUID *clsid, const char *progid, struct IUnknown *p)
+{
+ struct com_class *l = talloc_zero(running_classes?running_classes:talloc_autofree_context(), struct com_class);
+
+ l->clsid = *clsid;
+ l->progid = talloc_strdup(l, progid);
+ l->class_object = p;
+
+ DLIST_ADD(running_classes, l);
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/lib/events/config.mk b/source4/lib/events/config.mk
new file mode 100644
index 0000000000..5c7e1b7210
--- /dev/null
+++ b/source4/lib/events/config.mk
@@ -0,0 +1,5 @@
+[SUBSYSTEM::LIBEVENTS]
+PUBLIC_DEPENDENCIES = LIBTEVENT
+CFLAGS = -Ilib/events
+
+LIBEVENTS_OBJ_FILES = $(addprefix $(libeventssrcdir)/, tevent_s4.o)
diff --git a/source4/lib/events/events.h b/source4/lib/events/events.h
new file mode 100644
index 0000000000..1b2dbde32b
--- /dev/null
+++ b/source4/lib/events/events.h
@@ -0,0 +1,7 @@
+#ifndef __LIB_EVENTS_H__
+#define __LIB_EVENTS_H__
+#define TEVENT_COMPAT_DEFINES 1
+#include <../lib/tevent/tevent.h>
+struct tevent_context *s4_event_context_init(TALLOC_CTX *mem_ctx);
+struct tevent_context *event_context_find(TALLOC_CTX *mem_ctx) _DEPRECATED_;
+#endif /* __LIB_EVENTS_H__ */
diff --git a/source4/lib/events/tevent_s4.c b/source4/lib/events/tevent_s4.c
new file mode 100644
index 0000000000..06bfbf61ed
--- /dev/null
+++ b/source4/lib/events/tevent_s4.c
@@ -0,0 +1,90 @@
+/*
+ Unix SMB/CIFS implementation.
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#define TEVENT_DEPRECATED 1
+#include "lib/events/events.h"
+
+/*
+ this is used to catch debug messages from events
+*/
+static void ev_wrap_debug(void *context, enum tevent_debug_level level,
+ const char *fmt, va_list ap) PRINTF_ATTRIBUTE(3,0);
+
+static void ev_wrap_debug(void *context, enum tevent_debug_level level,
+ const char *fmt, va_list ap)
+{
+ int samba_level = -1;
+ char *s = NULL;
+ switch (level) {
+ case TEVENT_DEBUG_FATAL:
+ samba_level = 0;
+ break;
+ case TEVENT_DEBUG_ERROR:
+ samba_level = 1;
+ break;
+ case TEVENT_DEBUG_WARNING:
+ samba_level = 2;
+ break;
+ case TEVENT_DEBUG_TRACE:
+ samba_level = 10;
+ break;
+
+ };
+ vasprintf(&s, fmt, ap);
+ if (!s) return;
+ DEBUG(samba_level, ("tevent: %s", s));
+ free(s);
+}
+
+/*
+ create a event_context structure. This must be the first events
+ call, and all subsequent calls pass this event_context as the first
+ element. Event handlers also receive this as their first argument.
+
+ This samba4 specific call sets the samba4 debug handler.
+*/
+struct tevent_context *s4_event_context_init(TALLOC_CTX *mem_ctx)
+{
+ struct tevent_context *ev;
+
+ ev = tevent_context_init_byname(mem_ctx, NULL);
+ if (ev) {
+ tevent_set_debug(ev, ev_wrap_debug, NULL);
+ tevent_loop_allow_nesting(ev);
+ }
+ return ev;
+}
+
+/*
+ find an event context that is a parent of the given memory context,
+ or create a new event context as a child of the given context if
+ none is found
+
+ This should be used in preference to event_context_init() in places
+ where you would prefer to use the existing event context if possible
+ (which is most situations)
+*/
+struct tevent_context *event_context_find(TALLOC_CTX *mem_ctx)
+{
+ struct tevent_context *ev = talloc_find_parent_bytype(mem_ctx, struct tevent_context);
+ if (ev == NULL) {
+ ev = tevent_context_init(mem_ctx);
+ }
+ return ev;
+}
diff --git a/source4/lib/ldb-samba/README b/source4/lib/ldb-samba/README
new file mode 100644
index 0000000000..3fa47159ca
--- /dev/null
+++ b/source4/lib/ldb-samba/README
@@ -0,0 +1,7 @@
+This directory contains Samba specific extensions to ldb. It also
+serves as example code on how to extend ldb for your own application.
+
+The main extension Samba uses is to provide ldif encode/decode
+routines for specific attributes, so users can get nice pretty
+printing of attributes in ldbedit, while the attributes are stored in
+the standard NDR format in the database.
diff --git a/source4/lib/ldb-samba/config.mk b/source4/lib/ldb-samba/config.mk
new file mode 100644
index 0000000000..ceacf277e4
--- /dev/null
+++ b/source4/lib/ldb-samba/config.mk
@@ -0,0 +1,11 @@
+################################################
+# Start SUBSYSTEM LDBSAMBA
+[SUBSYSTEM::LDBSAMBA]
+PUBLIC_DEPENDENCIES = LIBLDB
+PRIVATE_DEPENDENCIES = LIBSECURITY SAMDB_SCHEMA LIBNDR NDR_DRSBLOBS
+# End SUBSYSTEM LDBSAMBA
+################################################
+
+LDBSAMBA_OBJ_FILES = $(ldb_sambasrcdir)/ldif_handlers.o
+$(eval $(call proto_header_template,$(ldb_sambasrcdir)/ldif_handlers_proto.h,$(LDBSAMBA_OBJ_FILES:.o=.c)))
+
diff --git a/source4/lib/ldb-samba/ldif_handlers.c b/source4/lib/ldb-samba/ldif_handlers.c
new file mode 100644
index 0000000000..fc87e6ca7a
--- /dev/null
+++ b/source4/lib/ldb-samba/ldif_handlers.c
@@ -0,0 +1,771 @@
+/*
+ ldb database library - ldif handlers for Samba
+
+ Copyright (C) Andrew Tridgell 2005
+ Copyright (C) Andrew Bartlett 2006-2007
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "ldb_private.h"
+#include "ldb_handlers.h"
+#include "dsdb/samdb/samdb.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "librpc/gen_ndr/ndr_drsblobs.h"
+#include "libcli/security/security.h"
+#include "param/param.h"
+
+/*
+ convert a ldif formatted objectSid to a NDR formatted blob
+*/
+static int ldif_read_objectSid(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out)
+{
+ enum ndr_err_code ndr_err;
+ struct dom_sid *sid;
+ sid = dom_sid_parse_length(mem_ctx, in);
+ if (sid == NULL) {
+ return -1;
+ }
+ ndr_err = ndr_push_struct_blob(out, mem_ctx, NULL, sid,
+ (ndr_push_flags_fn_t)ndr_push_dom_sid);
+ talloc_free(sid);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ convert a NDR formatted blob to a ldif formatted objectSid
+*/
+static int ldif_write_objectSid(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out)
+{
+ struct dom_sid *sid;
+ enum ndr_err_code ndr_err;
+
+ sid = talloc(mem_ctx, struct dom_sid);
+ if (sid == NULL) {
+ return -1;
+ }
+ ndr_err = ndr_pull_struct_blob_all(in, sid, NULL, sid,
+ (ndr_pull_flags_fn_t)ndr_pull_dom_sid);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ talloc_free(sid);
+ return -1;
+ }
+ *out = data_blob_string_const(dom_sid_string(mem_ctx, sid));
+ talloc_free(sid);
+ if (out->data == NULL) {
+ return -1;
+ }
+ return 0;
+}
+
+static bool ldb_comparision_objectSid_isString(const struct ldb_val *v)
+{
+ if (v->length < 3) {
+ return false;
+ }
+
+ if (strncmp("S-", (const char *)v->data, 2) != 0) return false;
+
+ return true;
+}
+
+/*
+ compare two objectSids
+*/
+static int ldb_comparison_objectSid(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *v1, const struct ldb_val *v2)
+{
+ if (ldb_comparision_objectSid_isString(v1) && ldb_comparision_objectSid_isString(v2)) {
+ return ldb_comparison_binary(ldb, mem_ctx, v1, v2);
+ } else if (ldb_comparision_objectSid_isString(v1)
+ && !ldb_comparision_objectSid_isString(v2)) {
+ struct ldb_val v;
+ int ret;
+ if (ldif_read_objectSid(ldb, mem_ctx, v1, &v) != 0) {
+ /* Perhaps not a string after all */
+ return ldb_comparison_binary(ldb, mem_ctx, v1, v2);
+ }
+ ret = ldb_comparison_binary(ldb, mem_ctx, &v, v2);
+ talloc_free(v.data);
+ return ret;
+ } else if (!ldb_comparision_objectSid_isString(v1)
+ && ldb_comparision_objectSid_isString(v2)) {
+ struct ldb_val v;
+ int ret;
+ if (ldif_read_objectSid(ldb, mem_ctx, v2, &v) != 0) {
+ /* Perhaps not a string after all */
+ return ldb_comparison_binary(ldb, mem_ctx, v1, v2);
+ }
+ ret = ldb_comparison_binary(ldb, mem_ctx, v1, &v);
+ talloc_free(v.data);
+ return ret;
+ }
+ return ldb_comparison_binary(ldb, mem_ctx, v1, v2);
+}
+
+/*
+ canonicalise a objectSid
+*/
+static int ldb_canonicalise_objectSid(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out)
+{
+ if (ldb_comparision_objectSid_isString(in)) {
+ if (ldif_read_objectSid(ldb, mem_ctx, in, out) != 0) {
+ /* Perhaps not a string after all */
+ return ldb_handler_copy(ldb, mem_ctx, in, out);
+ }
+ return 0;
+ }
+ return ldb_handler_copy(ldb, mem_ctx, in, out);
+}
+
+static int extended_dn_read_SID(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out)
+{
+ struct dom_sid sid;
+ enum ndr_err_code ndr_err;
+ if (ldb_comparision_objectSid_isString(in)) {
+ if (ldif_read_objectSid(ldb, mem_ctx, in, out) == 0) {
+ return 0;
+ }
+ }
+
+ /* Perhaps not a string after all */
+ *out = data_blob_talloc(mem_ctx, NULL, in->length/2+1);
+
+ if (!out->data) {
+ return -1;
+ }
+
+ (*out).length = strhex_to_str((char *)out->data, out->length,
+ (const char *)in->data, in->length);
+
+ /* Check it looks like a SID */
+ ndr_err = ndr_pull_struct_blob_all(out, mem_ctx, NULL, &sid,
+ (ndr_pull_flags_fn_t)ndr_pull_dom_sid);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ convert a ldif formatted objectGUID to a NDR formatted blob
+*/
+static int ldif_read_objectGUID(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out)
+{
+ struct GUID guid;
+ NTSTATUS status;
+ enum ndr_err_code ndr_err;
+
+ status = GUID_from_data_blob(in, &guid);
+ if (!NT_STATUS_IS_OK(status)) {
+ return -1;
+ }
+
+ ndr_err = ndr_push_struct_blob(out, mem_ctx, NULL, &guid,
+ (ndr_push_flags_fn_t)ndr_push_GUID);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ convert a NDR formatted blob to a ldif formatted objectGUID
+*/
+static int ldif_write_objectGUID(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out)
+{
+ struct GUID guid;
+ enum ndr_err_code ndr_err;
+ ndr_err = ndr_pull_struct_blob_all(in, mem_ctx, NULL, &guid,
+ (ndr_pull_flags_fn_t)ndr_pull_GUID);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return -1;
+ }
+ out->data = (uint8_t *)GUID_string(mem_ctx, &guid);
+ if (out->data == NULL) {
+ return -1;
+ }
+ out->length = strlen((const char *)out->data);
+ return 0;
+}
+
+static bool ldb_comparision_objectGUID_isString(const struct ldb_val *v)
+{
+ if (v->length != 36 && v->length != 38) return false;
+
+ /* Might be a GUID string, can't be a binary GUID (fixed 16 bytes) */
+ return true;
+}
+
+static int extended_dn_read_GUID(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out)
+{
+ struct GUID guid;
+ enum ndr_err_code ndr_err;
+ if (in->length == 36 && ldif_read_objectGUID(ldb, mem_ctx, in, out) == 0) {
+ return 0;
+ }
+
+ /* Try as 'hex' form */
+ if (in->length != 32) {
+ return -1;
+ }
+
+ *out = data_blob_talloc(mem_ctx, NULL, in->length/2+1);
+
+ if (!out->data) {
+ return -1;
+ }
+
+ (*out).length = strhex_to_str((char *)out->data, out->length,
+ (const char *)in->data, in->length);
+
+ /* Check it looks like a GUID */
+ ndr_err = ndr_pull_struct_blob_all(out, mem_ctx, NULL, &guid,
+ (ndr_pull_flags_fn_t)ndr_pull_GUID);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ compare two objectGUIDs
+*/
+static int ldb_comparison_objectGUID(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *v1, const struct ldb_val *v2)
+{
+ if (ldb_comparision_objectGUID_isString(v1) && ldb_comparision_objectGUID_isString(v2)) {
+ return ldb_comparison_binary(ldb, mem_ctx, v1, v2);
+ } else if (ldb_comparision_objectGUID_isString(v1)
+ && !ldb_comparision_objectGUID_isString(v2)) {
+ struct ldb_val v;
+ int ret;
+ if (ldif_read_objectGUID(ldb, mem_ctx, v1, &v) != 0) {
+ /* Perhaps it wasn't a valid string after all */
+ return ldb_comparison_binary(ldb, mem_ctx, v1, v2);
+ }
+ ret = ldb_comparison_binary(ldb, mem_ctx, &v, v2);
+ talloc_free(v.data);
+ return ret;
+ } else if (!ldb_comparision_objectGUID_isString(v1)
+ && ldb_comparision_objectGUID_isString(v2)) {
+ struct ldb_val v;
+ int ret;
+ if (ldif_read_objectGUID(ldb, mem_ctx, v2, &v) != 0) {
+ /* Perhaps it wasn't a valid string after all */
+ return ldb_comparison_binary(ldb, mem_ctx, v1, v2);
+ }
+ ret = ldb_comparison_binary(ldb, mem_ctx, v1, &v);
+ talloc_free(v.data);
+ return ret;
+ }
+ return ldb_comparison_binary(ldb, mem_ctx, v1, v2);
+}
+
+/*
+ canonicalise a objectGUID
+*/
+static int ldb_canonicalise_objectGUID(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out)
+{
+ if (ldb_comparision_objectGUID_isString(in)) {
+ if (ldif_read_objectGUID(ldb, mem_ctx, in, out) != 0) {
+ /* Perhaps it wasn't a valid string after all */
+ return ldb_handler_copy(ldb, mem_ctx, in, out);
+ }
+ return 0;
+ }
+ return ldb_handler_copy(ldb, mem_ctx, in, out);
+}
+
+
+/*
+ convert a ldif (SDDL) formatted ntSecurityDescriptor to a NDR formatted blob
+*/
+static int ldif_read_ntSecurityDescriptor(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out)
+{
+ struct security_descriptor *sd;
+ enum ndr_err_code ndr_err;
+
+ sd = sddl_decode(mem_ctx, (const char *)in->data, NULL);
+ if (sd == NULL) {
+ return -1;
+ }
+ ndr_err = ndr_push_struct_blob(out, mem_ctx, NULL, sd,
+ (ndr_push_flags_fn_t)ndr_push_security_descriptor);
+ talloc_free(sd);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ convert a NDR formatted blob to a ldif formatted ntSecurityDescriptor (SDDL format)
+*/
+static int ldif_write_ntSecurityDescriptor(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out)
+{
+ struct security_descriptor *sd;
+ enum ndr_err_code ndr_err;
+
+ sd = talloc(mem_ctx, struct security_descriptor);
+ if (sd == NULL) {
+ return -1;
+ }
+ /* We can't use ndr_pull_struct_blob_all because this contains relative pointers */
+ ndr_err = ndr_pull_struct_blob(in, sd, NULL, sd,
+ (ndr_pull_flags_fn_t)ndr_pull_security_descriptor);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ talloc_free(sd);
+ return -1;
+ }
+ out->data = (uint8_t *)sddl_encode(mem_ctx, sd, NULL);
+ talloc_free(sd);
+ if (out->data == NULL) {
+ return -1;
+ }
+ out->length = strlen((const char *)out->data);
+ return 0;
+}
+
+/*
+ canonicalise an objectCategory. We use the short form as the cannoical form:
+ cn=Person,cn=Schema,cn=Configuration,<basedn> becomes 'person'
+*/
+
+static int ldif_canonicalise_objectCategory(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out)
+{
+ struct ldb_dn *dn1 = NULL;
+ const struct dsdb_schema *schema = dsdb_get_schema(ldb);
+ const struct dsdb_class *sclass;
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+ if (!tmp_ctx) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if (!schema) {
+ *out = data_blob_talloc(mem_ctx, in->data, in->length);
+ if (in->data && !out->data) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ return LDB_SUCCESS;
+ }
+ dn1 = ldb_dn_from_ldb_val(tmp_ctx, ldb, in);
+ if ( ! ldb_dn_validate(dn1)) {
+ const char *lDAPDisplayName = talloc_strndup(tmp_ctx, (char *)in->data, in->length);
+ sclass = dsdb_class_by_lDAPDisplayName(schema, lDAPDisplayName);
+ if (sclass) {
+ struct ldb_dn *dn = ldb_dn_new(mem_ctx, ldb,
+ sclass->defaultObjectCategory);
+ *out = data_blob_string_const(ldb_dn_alloc_casefold(mem_ctx, dn));
+ talloc_free(tmp_ctx);
+
+ if (!out->data) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ return LDB_SUCCESS;
+ } else {
+ *out = data_blob_talloc(mem_ctx, in->data, in->length);
+ talloc_free(tmp_ctx);
+
+ if (in->data && !out->data) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ return LDB_SUCCESS;
+ }
+ }
+ *out = data_blob_string_const(ldb_dn_alloc_casefold(mem_ctx, dn1));
+ talloc_free(tmp_ctx);
+
+ if (!out->data) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ return LDB_SUCCESS;
+}
+
+static int ldif_comparison_objectCategory(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *v1,
+ const struct ldb_val *v2)
+{
+
+ int ret, ret1, ret2;
+ struct ldb_val v1_canon, v2_canon;
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+
+ /* I could try and bail if tmp_ctx was NULL, but what return
+ * value would I use?
+ *
+ * It seems easier to continue on the NULL context
+ */
+ ret1 = ldif_canonicalise_objectCategory(ldb, tmp_ctx, v1, &v1_canon);
+ ret2 = ldif_canonicalise_objectCategory(ldb, tmp_ctx, v2, &v2_canon);
+
+ if (ret1 == LDB_SUCCESS && ret2 == LDB_SUCCESS) {
+ ret = data_blob_cmp(&v1_canon, &v2_canon);
+ } else {
+ ret = data_blob_cmp(v1, v2);
+ }
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+/*
+ convert a ldif formatted prefixMap to a NDR formatted blob
+*/
+static int ldif_read_prefixMap(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out)
+{
+ struct prefixMapBlob *blob;
+ enum ndr_err_code ndr_err;
+ char *string, *line, *p, *oid;
+
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+
+ if (tmp_ctx == NULL) {
+ return -1;
+ }
+
+ blob = talloc_zero(tmp_ctx, struct prefixMapBlob);
+ if (blob == NULL) {
+ talloc_free(blob);
+ return -1;
+ }
+
+ blob->version = PREFIX_MAP_VERSION_DSDB;
+
+ string = talloc_strndup(mem_ctx, (const char *)in->data, in->length);
+ if (string == NULL) {
+ talloc_free(blob);
+ return -1;
+ }
+
+ line = string;
+ while (line && line[0]) {
+ p=strchr(line, ';');
+ if (p) {
+ p[0] = '\0';
+ } else {
+ p=strchr(line, '\n');
+ if (p) {
+ p[0] = '\0';
+ }
+ }
+ /* allow a traling seperator */
+ if (line == p) {
+ break;
+ }
+
+ blob->ctr.dsdb.mappings = talloc_realloc(blob,
+ blob->ctr.dsdb.mappings,
+ struct drsuapi_DsReplicaOIDMapping,
+ blob->ctr.dsdb.num_mappings+1);
+ if (!blob->ctr.dsdb.mappings) {
+ talloc_free(tmp_ctx);
+ return -1;
+ }
+
+ blob->ctr.dsdb.mappings[blob->ctr.dsdb.num_mappings].id_prefix = strtoul(line, &oid, 10);
+
+ if (oid[0] != ':') {
+ talloc_free(tmp_ctx);
+ return -1;
+ }
+
+ /* we know there must be at least ":" */
+ oid++;
+
+ blob->ctr.dsdb.mappings[blob->ctr.dsdb.num_mappings].oid.oid
+ = talloc_strdup(blob->ctr.dsdb.mappings, oid);
+
+ blob->ctr.dsdb.num_mappings++;
+
+ /* Now look past the terminator we added above */
+ if (p) {
+ line = p + 1;
+ } else {
+ line = NULL;
+ }
+ }
+
+ ndr_err = ndr_push_struct_blob(out, mem_ctx,
+ lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
+ blob,
+ (ndr_push_flags_fn_t)ndr_push_prefixMapBlob);
+ talloc_free(tmp_ctx);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ convert a NDR formatted blob to a ldif formatted prefixMap
+*/
+static int ldif_write_prefixMap(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out)
+{
+ struct prefixMapBlob *blob;
+ enum ndr_err_code ndr_err;
+ char *string;
+ uint32_t i;
+
+ blob = talloc(mem_ctx, struct prefixMapBlob);
+ if (blob == NULL) {
+ return -1;
+ }
+ ndr_err = ndr_pull_struct_blob_all(in, blob,
+ lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
+ blob,
+ (ndr_pull_flags_fn_t)ndr_pull_prefixMapBlob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ talloc_free(blob);
+ return -1;
+ }
+ if (blob->version != PREFIX_MAP_VERSION_DSDB) {
+ return -1;
+ }
+ string = talloc_strdup(mem_ctx, "");
+ if (string == NULL) {
+ return -1;
+ }
+
+ for (i=0; i < blob->ctr.dsdb.num_mappings; i++) {
+ if (i > 0) {
+ string = talloc_asprintf_append(string, ";");
+ }
+ string = talloc_asprintf_append(string, "%u:%s",
+ blob->ctr.dsdb.mappings[i].id_prefix,
+ blob->ctr.dsdb.mappings[i].oid.oid);
+ if (string == NULL) {
+ return -1;
+ }
+ }
+
+ talloc_free(blob);
+ *out = data_blob_string_const(string);
+ return 0;
+}
+
+static bool ldif_comparision_prefixMap_isString(const struct ldb_val *v)
+{
+ if (v->length < 4) {
+ return true;
+ }
+
+ if (IVAL(v->data, 0) == PREFIX_MAP_VERSION_DSDB) {
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ canonicalise a prefixMap
+*/
+static int ldif_canonicalise_prefixMap(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out)
+{
+ if (ldif_comparision_prefixMap_isString(in)) {
+ return ldif_read_prefixMap(ldb, mem_ctx, in, out);
+ }
+ return ldb_handler_copy(ldb, mem_ctx, in, out);
+}
+
+static int ldif_comparison_prefixMap(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *v1,
+ const struct ldb_val *v2)
+{
+
+ int ret, ret1, ret2;
+ struct ldb_val v1_canon, v2_canon;
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+
+ /* I could try and bail if tmp_ctx was NULL, but what return
+ * value would I use?
+ *
+ * It seems easier to continue on the NULL context
+ */
+ ret1 = ldif_canonicalise_prefixMap(ldb, tmp_ctx, v1, &v1_canon);
+ ret2 = ldif_canonicalise_prefixMap(ldb, tmp_ctx, v2, &v2_canon);
+
+ if (ret1 == LDB_SUCCESS && ret2 == LDB_SUCCESS) {
+ ret = data_blob_cmp(&v1_canon, &v2_canon);
+ } else {
+ ret = data_blob_cmp(v1, v2);
+ }
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static int extended_dn_write_hex(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out)
+{
+ *out = data_blob_string_const(data_blob_hex_string(mem_ctx, in));
+ if (!out->data) {
+ return -1;
+ }
+ return 0;
+}
+
+
+#define LDB_SYNTAX_SAMBA_GUID "LDB_SYNTAX_SAMBA_GUID"
+#define LDB_SYNTAX_SAMBA_OBJECT_CATEGORY "LDB_SYNTAX_SAMBA_OBJECT_CATEGORY"
+#define LDB_SYNTAX_SAMBA_PREFIX_MAP "LDB_SYNTAX_SAMBA_PREFIX_MAP"
+
+static const struct ldb_schema_syntax samba_syntaxes[] = {
+ {
+ .name = LDB_SYNTAX_SAMBA_SID,
+ .ldif_read_fn = ldif_read_objectSid,
+ .ldif_write_fn = ldif_write_objectSid,
+ .canonicalise_fn = ldb_canonicalise_objectSid,
+ .comparison_fn = ldb_comparison_objectSid
+ },{
+ .name = LDB_SYNTAX_SAMBA_SECURITY_DESCRIPTOR,
+ .ldif_read_fn = ldif_read_ntSecurityDescriptor,
+ .ldif_write_fn = ldif_write_ntSecurityDescriptor,
+ .canonicalise_fn = ldb_handler_copy,
+ .comparison_fn = ldb_comparison_binary
+ },{
+ .name = LDB_SYNTAX_SAMBA_GUID,
+ .ldif_read_fn = ldif_read_objectGUID,
+ .ldif_write_fn = ldif_write_objectGUID,
+ .canonicalise_fn = ldb_canonicalise_objectGUID,
+ .comparison_fn = ldb_comparison_objectGUID
+ },{
+ .name = LDB_SYNTAX_SAMBA_OBJECT_CATEGORY,
+ .ldif_read_fn = ldb_handler_copy,
+ .ldif_write_fn = ldb_handler_copy,
+ .canonicalise_fn = ldif_canonicalise_objectCategory,
+ .comparison_fn = ldif_comparison_objectCategory
+ },{
+ .name = LDB_SYNTAX_SAMBA_PREFIX_MAP,
+ .ldif_read_fn = ldif_read_prefixMap,
+ .ldif_write_fn = ldif_write_prefixMap,
+ .canonicalise_fn = ldif_canonicalise_prefixMap,
+ .comparison_fn = ldif_comparison_prefixMap
+ }
+};
+
+static const struct ldb_dn_extended_syntax samba_dn_syntax[] = {
+ {
+ .name = "SID",
+ .read_fn = extended_dn_read_SID,
+ .write_clear_fn = ldif_write_objectSid,
+ .write_hex_fn = extended_dn_write_hex
+ },{
+ .name = "GUID",
+ .read_fn = extended_dn_read_GUID,
+ .write_clear_fn = ldif_write_objectGUID,
+ .write_hex_fn = extended_dn_write_hex
+ },{
+ .name = "WKGUID",
+ .read_fn = ldb_handler_copy,
+ .write_clear_fn = ldb_handler_copy,
+ .write_hex_fn = ldb_handler_copy
+ }
+};
+
+static const struct {
+ const char *name;
+ const char *syntax;
+} samba_attributes[] = {
+ { "objectSid", LDB_SYNTAX_SAMBA_SID },
+ { "securityIdentifier", LDB_SYNTAX_SAMBA_SID },
+ { "ntSecurityDescriptor", LDB_SYNTAX_SAMBA_SECURITY_DESCRIPTOR },
+ { "objectGUID", LDB_SYNTAX_SAMBA_GUID },
+ { "invocationId", LDB_SYNTAX_SAMBA_GUID },
+ { "schemaIDGUID", LDB_SYNTAX_SAMBA_GUID },
+ { "attributeSecurityGUID", LDB_SYNTAX_SAMBA_GUID },
+ { "parentGUID", LDB_SYNTAX_SAMBA_GUID },
+ { "siteGUID", LDB_SYNTAX_SAMBA_GUID },
+ { "pKTGUID", LDB_SYNTAX_SAMBA_GUID },
+ { "fRSVersionGUID", LDB_SYNTAX_SAMBA_GUID },
+ { "fRSReplicaSetGUID", LDB_SYNTAX_SAMBA_GUID },
+ { "netbootGUID", LDB_SYNTAX_SAMBA_GUID },
+ { "objectCategory", LDB_SYNTAX_SAMBA_OBJECT_CATEGORY },
+ { "prefixMap", LDB_SYNTAX_SAMBA_PREFIX_MAP }
+};
+
+const struct ldb_schema_syntax *ldb_samba_syntax_by_name(struct ldb_context *ldb, const char *name)
+{
+ uint32_t j;
+ const struct ldb_schema_syntax *s = NULL;
+
+ for (j=0; j < ARRAY_SIZE(samba_syntaxes); j++) {
+ if (strcmp(name, samba_syntaxes[j].name) == 0) {
+ s = &samba_syntaxes[j];
+ break;
+ }
+ }
+ return s;
+}
+
+
+/*
+ register the samba ldif handlers
+*/
+int ldb_register_samba_handlers(struct ldb_context *ldb)
+{
+ uint32_t i;
+
+ for (i=0; i < ARRAY_SIZE(samba_attributes); i++) {
+ int ret;
+ const struct ldb_schema_syntax *s = NULL;
+
+ s = ldb_samba_syntax_by_name(ldb, samba_attributes[i].syntax);
+
+ if (!s) {
+ s = ldb_standard_syntax_by_name(ldb, samba_attributes[i].syntax);
+ }
+
+ if (!s) {
+ return -1;
+ }
+
+ ret = ldb_schema_attribute_add_with_syntax(ldb, samba_attributes[i].name, LDB_ATTR_FLAG_FIXED, s);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+
+ for (i=0; i < ARRAY_SIZE(samba_dn_syntax); i++) {
+ int ret;
+ ret = ldb_dn_extended_add_syntax(ldb, LDB_ATTR_FLAG_FIXED, &samba_dn_syntax[i]);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+
+ }
+
+ return LDB_SUCCESS;
+}
diff --git a/source4/lib/ldb-samba/ldif_handlers.h b/source4/lib/ldb-samba/ldif_handlers.h
new file mode 100644
index 0000000000..e37c4166c8
--- /dev/null
+++ b/source4/lib/ldb-samba/ldif_handlers.h
@@ -0,0 +1,13 @@
+#ifndef __LIB_LDB_SAMBA_LDIF_HANDLERS_H__
+#define __LIB_LDB_SAMBA_LDIF_HANDLERS_H__
+
+#define LDB_SYNTAX_SAMBA_SID "LDB_SYNTAX_SAMBA_SID"
+#define LDB_SYNTAX_SAMBA_SECURITY_DESCRIPTOR "1.2.840.113556.1.4.907"
+
+#include "lib/ldb-samba/ldif_handlers_proto.h"
+
+#undef _PRINTF_ATTRIBUTE
+#define _PRINTF_ATTRIBUTE(a1, a2)
+
+#endif /* __LIB_LDB_SAMBA_LDIF_HANDLERS_H__ */
+
diff --git a/source4/lib/ldb/Doxyfile b/source4/lib/ldb/Doxyfile
new file mode 100644
index 0000000000..07b12b516a
--- /dev/null
+++ b/source4/lib/ldb/Doxyfile
@@ -0,0 +1,26 @@
+PROJECT_NAME = LDB
+OUTPUT_DIRECTORY = apidocs
+REPEAT_BRIEF = YES
+OPTIMIZE_OUTPUT_FOR_C = YES
+SORT_MEMBER_DOCS = YES
+SORT_BRIEF_DOCS = NO
+GENERATE_TODOLIST = YES
+GENERATE_BUGLIST = YES
+GENERATE_DEPRECATEDLIST= YES
+SHOW_USED_FILES = NO
+SHOW_DIRECTORIES = NO
+WARNINGS = YES
+WARN_IF_UNDOCUMENTED = YES
+WARN_IF_DOC_ERROR = YES
+WARN_NO_PARAMDOC = NO
+WARN_FORMAT = "$file:$line: $text"
+INPUT = include .
+FILE_PATTERNS = *.h *.dox
+EXCLUDE = include/config.h include/dlinklist.h \
+ include/includes.h
+EXAMPLE_PATH = examples
+GENERATE_HTML = YES
+HTML_OUTPUT = html
+GENERATE_MAN = YES
+ALWAYS_DETAILED_SEC = YES
+JAVADOC_AUTOBRIEF = YES
diff --git a/source4/lib/ldb/Makefile.in b/source4/lib/ldb/Makefile.in
new file mode 100644
index 0000000000..663dea9f80
--- /dev/null
+++ b/source4/lib/ldb/Makefile.in
@@ -0,0 +1,186 @@
+#!gmake
+#
+CC = @CC@
+GCOV = @GCOV@
+XSLTPROC = @XSLTPROC@
+DOXYGEN = @DOXYGEN@
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+datarootdir = @datarootdir@
+includedir = @includedir@
+libdir = @libdir@
+bindir = @bindir@
+mandir = @mandir@
+VPATH = @srcdir@:@libreplacedir@
+srcdir = @srcdir@
+builddir = @builddir@
+sharedbuilddir = @sharedbuilddir@
+INSTALLCMD = @INSTALL@
+SLAPD = @SLAPD@
+EXTRA_OBJ=@EXTRA_OBJ@
+TESTS=test-tdb.sh @TESTS@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PYTHON = @PYTHON@
+PYTHON_CONFIG = @PYTHON_CONFIG@
+ldbdir = $(srcdir)
+
+LDB_MODULESDIR = @LDB_MODULESDIR@
+
+TALLOC_LIBS = @TALLOC_LIBS@
+TALLOC_CFLAGS = @TALLOC_CFLAGS@
+TALLOC_OBJ = @TALLOC_OBJ@
+
+TDB_LIBS = @TDB_LIBS@
+TDB_CFLAGS = @TDB_CFLAGS@
+TDB_OBJ = @TDB_OBJ@
+
+TEVENT_LIBS = @TEVENT_LIBS@
+TEVENT_CFLAGS = @TEVENT_CFLAGS@
+TEVENT_OBJ = @TEVENT_OBJ@
+
+POPT_LIBS = @POPT_LIBS@
+POPT_CFLAGS = @POPT_CFLAGS@
+POPT_OBJ = @POPT_OBJ@
+
+LDAP_LIBS = @LDAP_LIBS@
+
+LIBDL = @LIBDL@
+
+SHLIBEXT = @SHLIBEXT@
+
+LD_EXPORT_DYNAMIC = @LD_EXPORT_DYNAMIC@
+SHLD = @SHLD@
+SHLD_FLAGS = @SHLD_FLAGS@
+
+LDFLAGS = @LDFLAGS@
+LIBS = @LIBS@
+
+PICFLAG = @PICFLAG@
+CFLAGS=-g -I$(srcdir)/include -Iinclude -I$(srcdir) -I$(srcdir)/.. \
+ $(POPT_CFLAGS) $(TALLOC_CFLAGS) $(TDB_CFLAGS) $(TEVENT_CFLAGS) \
+ -DLIBDIR=\"$(libdir)\" -DSHLIBEXT=\"$(SHLIBEXT)\" -DUSE_MMAP=1 \
+ -DLDB_MODULESDIR=\"$(LDB_MODULESDIR)\" \
+ @CFLAGS@
+
+MDLD = @MDLD@
+MDLD_FLAGS = @MDLD_FLAGS@
+
+OBJS = $(MODULES_OBJ) $(COMMON_OBJ) $(LDB_TDB_OBJ) $(TDB_OBJ) $(TEVENT_OBJ) $(TALLOC_OBJ) $(POPT_OBJ) $(LDB_MAP_OBJ) @LIBREPLACEOBJ@ $(EXTRA_OBJ)
+
+headers = $(srcdir)/include/ldb.h $(srcdir)/include/ldb_errors.h $(srcdir)/include/ldb_handlers.h $(srcdir)/include/ldb_module.h
+
+BINS = bin/ldbadd bin/ldbsearch bin/ldbdel bin/ldbmodify bin/ldbedit bin/ldbrename bin/ldbtest
+
+EXAMPLES = examples/ldbreader examples/ldifreader
+
+DIRS = lib bin common ldb_tdb ldb_ldap ldb_sqlite3 modules tools examples
+
+default: all
+
+include $(ldbdir)/rules.mk
+
+nss: nssdir all $(NSS_LIB)
+
+nssdir:
+ @mkdir -p $(NSSDIR)
+
+SONAME = libldb.$(SHLIBEXT).0
+SOLIB = libldb.$(SHLIBEXT).$(PACKAGE_VERSION)
+LIBSOLIB = lib/$(SOLIB)
+STATICLIB = lib/libldb.a
+
+lib/$(SONAME): $(LIBSOLIB)
+ ln -fs libldb.$(SHLIBEXT).$(PACKAGE_VERSION) $@
+
+lib/libldb.$(SHLIBEXT): $(LIBSOLIB) lib/$(SONAME)
+ ln -fs libldb.$(SHLIBEXT).$(PACKAGE_VERSION) $@
+
+lib/libnss_ldb.$(SHLIBEXT).2: $(NSS_OBJ) $(LIBSOLIB)
+ $(SHLD) $(SHLD_FLAGS) -o $@ $(NSS_OBJ) $(LDFLAGS) $(LIBSOLIB) @SONAMEFLAG@libnss_ldb.$(SHLIBEXT).2
+
+$(LIBSOLIB): $(OBJS)
+ $(SHLD) $(SHLD_FLAGS) -o $@ $(OBJS) $(LDFLAGS) $(LIBS) $(TALLOC_LIBS) $(TDB_LIBS) $(TEVENT_LIBS) $(LIBDL) $(LDAP_LIBS) @SONAMEFLAG@$(SONAME)
+ ln -sf libldb.$(SHLIBEXT).$(PACKAGE_VERSION) lib/libldb.$(SHLIBEXT)
+
+all: showflags dirs $(OBJS) $(STATICLIB) $(LIBSOLIB) $(BINS) $(EXAMPLES) manpages \
+ @PYTHON_BUILD_TARGET@
+
+shared-build: all
+ ${INSTALLCMD} -d $(sharedbuilddir)/lib
+ ${INSTALLCMD} -m 644 $(STATICLIB) $(sharedbuilddir)/lib
+ ${INSTALLCMD} -m 755 $(LIBSOLIB) $(sharedbuilddir)/lib
+ ln -sf $(SOLIB) $(sharedbuilddir)/lib/$(SONAME)
+ ln -sf $(SOLIB) $(sharedbuilddir)/lib/libldb.so
+ ${INSTALLCMD} -d $(sharedbuilddir)/include
+ ${INSTALLCMD} -m 644 $(srcdir)/include/ldb.h $(sharedbuilddir)/include
+ ${INSTALLCMD} -m 644 $(srcdir)/include/ldb_errors.h $(sharedbuilddir)/include
+ ${INSTALLCMD} -m 644 $(srcdir)/include/ldb_handlers.h $(sharedbuilddir)/include
+
+dirs:
+ @mkdir -p $(DIRS)
+
+manpages::
+ @$(srcdir)/docs/builddocs.sh "$(XSLTPROC)" "$(srcdir)"
+
+doxygen::
+ test -z "$(DOXYGEN)" || (cd $(srcdir) && "$(DOXYGEN)")
+
+clean::
+ rm -f *.o */*.o *.gcov */*.gc?? tdbtest.ldb*
+ rm -f $(BINS) $(TDB_OBJ) $(TALLOC_OBJ) $(STATICLIB) $(NSS_LIB) $(LIBSOLIB)
+ rm -f $(POPT_OBJ)
+ rm -f man/*.1 man/*.3 man/*.html
+ rm -f $(EXAMPLES)
+ rm -rf apidocs/
+ rm -rf tests/schema/
+
+distclean:: clean
+ rm -rf bin lib
+ rm -f config.log config.status config.cache include/config.h
+ rm -f ldb.pc
+ rm -f Makefile
+
+realdistclean:: distclean
+ rm -f configure include/config.h.in
+
+check:: test @PYTHON_CHECK_TARGET@
+
+check-soloading: sample.$(SHLIBEXT)
+ LDB_MODULES_PATH=$(builddir) $(srcdir)/tests/test-soloading.sh
+
+test:: all check-soloading
+ for t in $(TESTS); do echo STARTING $${t}; $(srcdir)/tests/$${t} || exit 1; done
+
+valgrindtest:: all
+ for t in $(TESTS); do echo STARTING $${t}; VALGRIND="valgrind -q --db-attach=yes --num-callers=30" $(srcdir)/tests/$${t} || exit 1; done
+
+installcheck:: install test
+
+install:: all installdirs installheaders installlibs installbin installdocs \
+ @PYTHON_INSTALL_TARGET@
+
+installdirs::
+ mkdir -p $(DESTDIR)$(includedir) $(DESTDIR)$(libdir) $(DESTDIR)$(bindir) $(DESTDIR)$(libdir)/pkgconfig
+
+installheaders:: installdirs
+ cp $(headers) $(DESTDIR)$(includedir)
+
+installlibs:: installdirs
+ cp $(STATICLIB) $(LIBSOLIB) $(DESTDIR)$(libdir)
+ cp ldb.pc $(DESTDIR)$(libdir)/pkgconfig
+
+installbin:: installdirs
+ cp $(BINS) $(DESTDIR)$(bindir)
+
+installdocs:: installdirs
+ $(srcdir)/docs/installdocs.sh $(DESTDIR)$(mandir)
+
+gcov::
+ $(GCOV) -po ldb_sqlite3 $(srcdir)/ldb_sqlite3/*.c 2| tee ldb_sqlite3.report.gcov
+ $(GCOV) -po ldb_ldap $(srcdir)/ldb_ldap/*.c 2| tee ldb_ldap.report.gcov
+ $(GCOV) -po ldb_tdb $(srcdir)/ldb_tdb/*.c 2| tee ldb_tdb.report.gcov
+ $(GCOV) -po common $(srcdir)/common/*.c 2| tee common.report.gcov
+ $(GCOV) -po modules $(srcdir)/modules/*.c 2| tee modules.report.gcov
+ $(GCOV) -po tools $(srcdir)/tools/*.c 2| tee tools.report.gcov
+
+include $(ldbdir)/ldb.mk
diff --git a/source4/lib/ldb/README_gcov.txt b/source4/lib/ldb/README_gcov.txt
new file mode 100644
index 0000000000..2abd9378f4
--- /dev/null
+++ b/source4/lib/ldb/README_gcov.txt
@@ -0,0 +1,29 @@
+Here is how to use gcov to test code coverage in ldb.
+
+Step 1: build ldb with gcov enabled
+
+ make clean all WITH_GCOV=1
+
+Step 3: run the test suite
+ make test-tdb
+
+Step 4: produce the gcov report
+ make gcov
+
+Step 5: read the summary reports
+ less *.report.gcov
+
+Step 6: examine the per-file reports
+ less ldb_tdb\#ldb_tdb.c.gcov
+
+You can also combine steps 2 to 4 like this:
+
+ make clean all test-tdb gcov WITH_GCOV=1
+
+Note that you should not expect 100% coverage, as some error paths
+(such as memory allocation failures) are very hard to trigger. There
+are ways of working around this, but they are quite tricky (they
+involve allocation wrappers that "fork and fail on malloc").
+
+The lines to look for in the per-file reports are the ones starting
+with "#####". Those are lines that are never executed.
diff --git a/source4/lib/ldb/aclocal.m4 b/source4/lib/ldb/aclocal.m4
new file mode 100644
index 0000000000..5605e476ba
--- /dev/null
+++ b/source4/lib/ldb/aclocal.m4
@@ -0,0 +1 @@
+m4_include(libreplace.m4)
diff --git a/source4/lib/ldb/autogen.sh b/source4/lib/ldb/autogen.sh
new file mode 100755
index 0000000000..e4d367dc1e
--- /dev/null
+++ b/source4/lib/ldb/autogen.sh
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+rm -rf autom4te.cache
+rm -f configure config.h.in
+
+IPATHS="-I libreplace -I lib/replace -I ../libreplace -I ../replace -I ../../../lib/replace"
+IPATHS="$IPATHS -I ./external"
+
+autoheader $IPATHS || exit 1
+autoconf $IPATHS || exit 1
+
+rm -rf autom4te.cache
+
+echo "Now run ./configure and then make."
+exit 0
+
diff --git a/source4/lib/ldb/build_macros.m4 b/source4/lib/ldb/build_macros.m4
new file mode 100644
index 0000000000..bb7fad8f7a
--- /dev/null
+++ b/source4/lib/ldb/build_macros.m4
@@ -0,0 +1,15 @@
+AC_DEFUN(BUILD_WITH_SHARED_BUILD_DIR,
+ [ AC_ARG_WITH([shared-build-dir],
+ [AC_HELP_STRING([--with-shared-build-dir=DIR],
+ [temporary build directory where libraries are installed [$srcdir/sharedbuild]])])
+
+ sharedbuilddir="$srcdir/sharedbuild"
+ if test x"$with_shared_build_dir" != x; then
+ sharedbuilddir=$with_shared_build_dir
+ CFLAGS="$CFLAGS -I$with_shared_build_dir/include"
+ CPPFLAGS="$CPPFLAGS -I$with_shared_build_dir/include"
+ LDFLAGS="$LDFLAGS -L$with_shared_build_dir/lib"
+ fi
+ AC_SUBST(sharedbuilddir)
+ ])
+
diff --git a/source4/lib/ldb/common/attrib_handlers.c b/source4/lib/ldb/common/attrib_handlers.c
new file mode 100644
index 0000000000..80725ec04f
--- /dev/null
+++ b/source4/lib/ldb/common/attrib_handlers.c
@@ -0,0 +1,378 @@
+/*
+ ldb database library
+
+ Copyright (C) Andrew Tridgell 2005
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+/*
+ attribute handlers for well known attribute types, selected by syntax OID
+ see rfc2252
+*/
+
+#include "ldb_private.h"
+#include "system/locale.h"
+#include "ldb_handlers.h"
+
+/*
+ default handler that just copies a ldb_val.
+*/
+int ldb_handler_copy(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out)
+{
+ *out = ldb_val_dup(mem_ctx, in);
+ if (in->length > 0 && out->data == NULL) {
+ ldb_oom(ldb);
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ a case folding copy handler, removing leading and trailing spaces and
+ multiple internal spaces
+
+ We exploit the fact that utf8 never uses the space octet except for
+ the space itself
+*/
+int ldb_handler_fold(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out)
+{
+ char *s, *t;
+ int l;
+
+ if (!in || !out || !(in->data)) {
+ return -1;
+ }
+
+ out->data = (uint8_t *)ldb_casefold(ldb, mem_ctx, (const char *)(in->data), in->length);
+ if (out->data == NULL) {
+ ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_handler_fold: unable to casefold string [%s]", in->data);
+ return -1;
+ }
+
+ s = (char *)(out->data);
+
+ /* remove trailing spaces if any */
+ l = strlen(s);
+ while (l > 0 && s[l - 1] == ' ') l--;
+ s[l] = '\0';
+
+ /* remove leading spaces if any */
+ if (*s == ' ') {
+ for (t = s; *s == ' '; s++) ;
+
+ /* remove leading spaces by moving down the string */
+ memmove(t, s, l);
+
+ s = t;
+ }
+
+ /* check middle spaces */
+ while ((t = strchr(s, ' ')) != NULL) {
+ for (s = t; *s == ' '; s++) ;
+
+ if ((s - t) > 1) {
+ l = strlen(s);
+
+ /* remove all spaces but one by moving down the string */
+ memmove(t + 1, s, l);
+ }
+ }
+
+ out->length = strlen((char *)out->data);
+ return 0;
+}
+
+
+
+/*
+ canonicalise a ldap Integer
+ rfc2252 specifies it should be in decimal form
+*/
+int ldb_canonicalise_Integer(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out)
+{
+ char *end;
+ long long i = strtoll((char *)in->data, &end, 0);
+ if (*end != 0) {
+ return -1;
+ }
+ out->data = (uint8_t *)talloc_asprintf(mem_ctx, "%lld", i);
+ if (out->data == NULL) {
+ return -1;
+ }
+ out->length = strlen((char *)out->data);
+ return 0;
+}
+
+/*
+ compare two Integers
+*/
+int ldb_comparison_Integer(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *v1, const struct ldb_val *v2)
+{
+ return strtoll((char *)v1->data, NULL, 0) - strtoll((char *)v2->data, NULL, 0);
+}
+
+/*
+ compare two binary blobs
+*/
+int ldb_comparison_binary(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *v1, const struct ldb_val *v2)
+{
+ if (v1->length != v2->length) {
+ return v1->length - v2->length;
+ }
+ return memcmp(v1->data, v2->data, v1->length);
+}
+
+/*
+ compare two case insensitive strings, ignoring multiple whitespaces
+ and leading and trailing whitespaces
+ see rfc2252 section 8.1
+
+ try to optimize for the ascii case,
+ but if we find out an utf8 codepoint revert to slower but correct function
+*/
+int ldb_comparison_fold(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *v1, const struct ldb_val *v2)
+{
+ const char *s1=(const char *)v1->data, *s2=(const char *)v2->data;
+ size_t n1 = v1->length, n2 = v2->length;
+ const char *u1, *u2;
+ char *b1, *b2;
+ int ret;
+ while (*s1 == ' ' && n1) { s1++; n1--; };
+ while (*s2 == ' ' && n2) { s2++; n2--; };
+ /* TODO: make utf8 safe, possibly with helper function from application */
+ while (*s1 && *s2 && n1 && n2) {
+ /* the first 127 (0x7F) chars are ascii and utf8 guarantes they
+ * never appear in multibyte sequences */
+ if (((unsigned char)s1[0]) & 0x80) goto utf8str;
+ if (((unsigned char)s2[0]) & 0x80) goto utf8str;
+ if (toupper((unsigned char)*s1) != toupper((unsigned char)*s2))
+ break;
+ if (*s1 == ' ') {
+ while (s1[0] == s1[1] && n1) { s1++; n1--; }
+ while (s2[0] == s2[1] && n2) { s2++; n2--; }
+ }
+ s1++; s2++;
+ n1--; n2--;
+ }
+ if (! (*s1 && *s2)) {
+ /* check for trailing spaces only if one of the pointers
+ * has reached the end of the strings otherwise we
+ * can mistakenly match.
+ * ex. "domain users" <-> "domainUpdates"
+ */
+ while (*s1 == ' ') { s1++; n1--; }
+ while (*s2 == ' ') { s2++; n2--; }
+ }
+ if (n1 != n2) {
+ return n1 - n2;
+ }
+ return (int)(toupper(*s1)) - (int)(toupper(*s2));
+
+utf8str:
+ /* no need to recheck from the start, just from the first utf8 char found */
+ b1 = ldb_casefold(ldb, mem_ctx, s1, n1);
+ b2 = ldb_casefold(ldb, mem_ctx, s2, n2);
+
+ if (b1 && b2) {
+ /* Both strings converted correctly */
+
+ u1 = b1;
+ u2 = b2;
+ } else {
+ /* One of the strings was not UTF8, so we have no options but to do a binary compare */
+
+ u1 = s1;
+ u2 = s2;
+ }
+
+ while (*u1 & *u2) {
+ if (*u1 != *u2)
+ break;
+ if (*u1 == ' ') {
+ while (u1[0] == u1[1]) u1++;
+ while (u2[0] == u2[1]) u2++;
+ }
+ u1++; u2++;
+ }
+ if (! (*u1 && *u2)) {
+ while (*u1 == ' ') u1++;
+ while (*u2 == ' ') u2++;
+ }
+ ret = (int)(*u1 - *u2);
+
+ talloc_free(b1);
+ talloc_free(b2);
+
+ return ret;
+}
+
+
+/*
+ canonicalise a attribute in DN format
+*/
+int ldb_canonicalise_dn(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out)
+{
+ struct ldb_dn *dn;
+ int ret = -1;
+
+ out->length = 0;
+ out->data = NULL;
+
+ dn = ldb_dn_from_ldb_val(ldb, mem_ctx, in);
+ if ( ! ldb_dn_validate(dn)) {
+ return LDB_ERR_INVALID_DN_SYNTAX;
+ }
+
+ out->data = (uint8_t *)ldb_dn_alloc_casefold(mem_ctx, dn);
+ if (out->data == NULL) {
+ goto done;
+ }
+ out->length = strlen((char *)out->data);
+
+ ret = 0;
+
+done:
+ talloc_free(dn);
+
+ return ret;
+}
+
+/*
+ compare two dns
+*/
+int ldb_comparison_dn(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *v1, const struct ldb_val *v2)
+{
+ struct ldb_dn *dn1 = NULL, *dn2 = NULL;
+ int ret;
+
+ dn1 = ldb_dn_from_ldb_val(ldb, mem_ctx, v1);
+ if ( ! ldb_dn_validate(dn1)) return -1;
+
+ dn2 = ldb_dn_from_ldb_val(ldb, mem_ctx, v2);
+ if ( ! ldb_dn_validate(dn2)) {
+ talloc_free(dn1);
+ return -1;
+ }
+
+ ret = ldb_dn_compare(dn1, dn2);
+
+ talloc_free(dn1);
+ talloc_free(dn2);
+ return ret;
+}
+
+/*
+ compare two utc time values. 1 second resolution
+*/
+int ldb_comparison_utctime(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *v1, const struct ldb_val *v2)
+{
+ time_t t1, t2;
+ t1 = ldb_string_to_time((char *)v1->data);
+ t2 = ldb_string_to_time((char *)v2->data);
+ return (int)t2 - (int)t1;
+}
+
+/*
+ canonicalise a utc time
+*/
+int ldb_canonicalise_utctime(struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out)
+{
+ time_t t = ldb_string_to_time((char *)in->data);
+ out->data = (uint8_t *)ldb_timestring(mem_ctx, t);
+ if (out->data == NULL) {
+ return -1;
+ }
+ out->length = strlen((char *)out->data);
+ return 0;
+}
+
+/*
+ table of standard attribute handlers
+*/
+static const struct ldb_schema_syntax ldb_standard_syntaxes[] = {
+ {
+ .name = LDB_SYNTAX_INTEGER,
+ .ldif_read_fn = ldb_handler_copy,
+ .ldif_write_fn = ldb_handler_copy,
+ .canonicalise_fn = ldb_canonicalise_Integer,
+ .comparison_fn = ldb_comparison_Integer
+ },
+ {
+ .name = LDB_SYNTAX_OCTET_STRING,
+ .ldif_read_fn = ldb_handler_copy,
+ .ldif_write_fn = ldb_handler_copy,
+ .canonicalise_fn = ldb_handler_copy,
+ .comparison_fn = ldb_comparison_binary
+ },
+ {
+ .name = LDB_SYNTAX_DIRECTORY_STRING,
+ .ldif_read_fn = ldb_handler_copy,
+ .ldif_write_fn = ldb_handler_copy,
+ .canonicalise_fn = ldb_handler_fold,
+ .comparison_fn = ldb_comparison_fold
+ },
+ {
+ .name = LDB_SYNTAX_DN,
+ .ldif_read_fn = ldb_handler_copy,
+ .ldif_write_fn = ldb_handler_copy,
+ .canonicalise_fn = ldb_canonicalise_dn,
+ .comparison_fn = ldb_comparison_dn
+ },
+ {
+ .name = LDB_SYNTAX_OBJECTCLASS,
+ .ldif_read_fn = ldb_handler_copy,
+ .ldif_write_fn = ldb_handler_copy,
+ .canonicalise_fn = ldb_handler_fold,
+ .comparison_fn = ldb_comparison_fold
+ },
+ {
+ .name = LDB_SYNTAX_UTC_TIME,
+ .ldif_read_fn = ldb_handler_copy,
+ .ldif_write_fn = ldb_handler_copy,
+ .canonicalise_fn = ldb_canonicalise_utctime,
+ .comparison_fn = ldb_comparison_utctime
+ }
+};
+
+
+/*
+ return the attribute handlers for a given syntax name
+*/
+const struct ldb_schema_syntax *ldb_standard_syntax_by_name(struct ldb_context *ldb,
+ const char *syntax)
+{
+ int i;
+ unsigned num_handlers = sizeof(ldb_standard_syntaxes)/sizeof(ldb_standard_syntaxes[0]);
+ /* TODO: should be replaced with a binary search */
+ for (i=0;i<num_handlers;i++) {
+ if (strcmp(ldb_standard_syntaxes[i].name, syntax) == 0) {
+ return &ldb_standard_syntaxes[i];
+ }
+ }
+ return NULL;
+}
diff --git a/source4/lib/ldb/common/ldb.c b/source4/lib/ldb/common/ldb.c
new file mode 100644
index 0000000000..86ce2069a5
--- /dev/null
+++ b/source4/lib/ldb/common/ldb.c
@@ -0,0 +1,1432 @@
+/*
+ ldb database library
+
+ Copyright (C) Andrew Tridgell 2004
+ Copyright (C) Simo Sorce 2005-2008
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb core API
+ *
+ * Description: core API routines interfacing to ldb backends
+ *
+ * Author: Andrew Tridgell
+ */
+
+#define TEVENT_DEPRECATED 1
+#include "ldb_private.h"
+
+static int ldb_context_destructor(void *ptr)
+{
+ struct ldb_context *ldb = talloc_get_type(ptr, struct ldb_context);
+
+ if (ldb->transaction_active) {
+ ldb_debug(ldb, LDB_DEBUG_FATAL,
+ "A transaction is still active in ldb context [%p]",
+ ldb);
+ }
+
+ return 0;
+}
+
+/*
+ this is used to catch debug messages from events
+*/
+static void ldb_tevent_debug(void *context, enum tevent_debug_level level,
+ const char *fmt, va_list ap) PRINTF_ATTRIBUTE(3,0);
+
+static void ldb_tevent_debug(void *context, enum tevent_debug_level level,
+ const char *fmt, va_list ap)
+{
+ struct ldb_context *ldb = talloc_get_type(context, struct ldb_context);
+ enum ldb_debug_level ldb_level = LDB_DEBUG_FATAL;
+ char *s = NULL;
+
+ switch (level) {
+ case TEVENT_DEBUG_FATAL:
+ ldb_level = LDB_DEBUG_FATAL;
+ break;
+ case TEVENT_DEBUG_ERROR:
+ ldb_level = LDB_DEBUG_ERROR;
+ break;
+ case TEVENT_DEBUG_WARNING:
+ ldb_level = LDB_DEBUG_WARNING;
+ break;
+ case TEVENT_DEBUG_TRACE:
+ ldb_level = LDB_DEBUG_TRACE;
+ break;
+ };
+
+ vasprintf(&s, fmt, ap);
+ if (!s) return;
+ ldb_debug(ldb, ldb_level, "tevent: %s", s);
+ free(s);
+}
+
+/*
+ initialise a ldb context
+ The mem_ctx is required
+ The event_ctx is required
+*/
+struct ldb_context *ldb_init(TALLOC_CTX *mem_ctx, struct tevent_context *ev_ctx)
+{
+ struct ldb_context *ldb;
+ int ret;
+
+ ldb = talloc_zero(mem_ctx, struct ldb_context);
+ /* FIXME: Hack a new event context so that CMD line utilities work
+ * until we have them all converted */
+ if (ev_ctx == NULL) {
+ ev_ctx = tevent_context_init(talloc_autofree_context());
+ tevent_set_debug(ev_ctx, ldb_tevent_debug, ldb);
+ tevent_loop_allow_nesting(ev_ctx);
+ }
+
+ ret = ldb_setup_wellknown_attributes(ldb);
+ if (ret != 0) {
+ talloc_free(ldb);
+ return NULL;
+ }
+
+ ldb_set_utf8_default(ldb);
+ ldb_set_create_perms(ldb, 0666);
+ ldb_set_modules_dir(ldb, LDB_MODULESDIR);
+ ldb_set_event_context(ldb, ev_ctx);
+
+ /* TODO: get timeout from options if available there */
+ ldb->default_timeout = 300; /* set default to 5 minutes */
+
+ talloc_set_destructor((TALLOC_CTX *)ldb, ldb_context_destructor);
+
+ return ldb;
+}
+
+/*
+ try to autodetect a basedn if none specified. This fixes one of my
+ pet hates about ldapsearch, which is that you have to get a long,
+ complex basedn right to make any use of it.
+*/
+void ldb_set_default_dns(struct ldb_context *ldb)
+{
+ TALLOC_CTX *tmp_ctx;
+ int ret;
+ struct ldb_result *res;
+ struct ldb_dn *tmp_dn=NULL;
+ static const char *attrs[] = {
+ "rootDomainNamingContext",
+ "configurationNamingContext",
+ "schemaNamingContext",
+ "defaultNamingContext",
+ NULL
+ };
+
+ tmp_ctx = talloc_new(ldb);
+ ret = ldb_search(ldb, tmp_ctx, &res, ldb_dn_new(tmp_ctx, ldb, NULL),
+ LDB_SCOPE_BASE, attrs, "(objectClass=*)");
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return;
+ }
+
+ if (res->count != 1) {
+ talloc_free(tmp_ctx);
+ return;
+ }
+
+ if (!ldb_get_opaque(ldb, "rootDomainNamingContext")) {
+ tmp_dn = ldb_msg_find_attr_as_dn(ldb, ldb, res->msgs[0],
+ "rootDomainNamingContext");
+ ldb_set_opaque(ldb, "rootDomainNamingContext", tmp_dn);
+ }
+
+ if (!ldb_get_opaque(ldb, "configurationNamingContext")) {
+ tmp_dn = ldb_msg_find_attr_as_dn(ldb, ldb, res->msgs[0],
+ "configurationNamingContext");
+ ldb_set_opaque(ldb, "configurationNamingContext", tmp_dn);
+ }
+
+ if (!ldb_get_opaque(ldb, "schemaNamingContext")) {
+ tmp_dn = ldb_msg_find_attr_as_dn(ldb, ldb, res->msgs[0],
+ "schemaNamingContext");
+ ldb_set_opaque(ldb, "schemaNamingContext", tmp_dn);
+ }
+
+ if (!ldb_get_opaque(ldb, "defaultNamingContext")) {
+ tmp_dn = ldb_msg_find_attr_as_dn(ldb, ldb, res->msgs[0],
+ "defaultNamingContext");
+ ldb_set_opaque(ldb, "defaultNamingContext", tmp_dn);
+ }
+
+ talloc_free(tmp_ctx);
+}
+
+struct ldb_dn *ldb_get_root_basedn(struct ldb_context *ldb)
+{
+ void *opaque = ldb_get_opaque(ldb, "rootDomainNamingContext");
+ return talloc_get_type(opaque, struct ldb_dn);
+}
+
+struct ldb_dn *ldb_get_config_basedn(struct ldb_context *ldb)
+{
+ void *opaque = ldb_get_opaque(ldb, "configurationNamingContext");
+ return talloc_get_type(opaque, struct ldb_dn);
+}
+
+struct ldb_dn *ldb_get_schema_basedn(struct ldb_context *ldb)
+{
+ void *opaque = ldb_get_opaque(ldb, "schemaNamingContext");
+ return talloc_get_type(opaque, struct ldb_dn);
+}
+
+struct ldb_dn *ldb_get_default_basedn(struct ldb_context *ldb)
+{
+ void *opaque = ldb_get_opaque(ldb, "defaultNamingContext");
+ return talloc_get_type(opaque, struct ldb_dn);
+}
+
+/*
+ connect to a database. The URL can either be one of the following forms
+ ldb://path
+ ldapi://path
+
+ flags is made up of LDB_FLG_*
+
+ the options are passed uninterpreted to the backend, and are
+ backend specific
+*/
+int ldb_connect(struct ldb_context *ldb, const char *url,
+ unsigned int flags, const char *options[])
+{
+ int ret;
+ const char *url2;
+ /* We seem to need to do this here, or else some utilities don't
+ * get ldb backends */
+
+ ldb->flags = flags;
+
+ url2 = talloc_strdup(ldb, url);
+ if (!url2) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ret = ldb_set_opaque(ldb, "ldb_url", talloc_strdup(ldb, url2));
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ ret = ldb_connect_backend(ldb, url, options, &ldb->modules);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ if (ldb_load_modules(ldb, options) != LDB_SUCCESS) {
+ ldb_debug(ldb, LDB_DEBUG_FATAL,
+ "Unable to load modules for %s: %s\n",
+ url, ldb_errstring(ldb));
+ return LDB_ERR_OTHER;
+ }
+
+ /* set the default base dn */
+ ldb_set_default_dns(ldb);
+
+ return LDB_SUCCESS;
+}
+
+void ldb_set_errstring(struct ldb_context *ldb, const char *err_string)
+{
+ if (ldb->err_string) {
+ talloc_free(ldb->err_string);
+ }
+ ldb->err_string = talloc_strdup(ldb, err_string);
+}
+
+void ldb_asprintf_errstring(struct ldb_context *ldb, const char *format, ...)
+{
+ va_list ap;
+ char *old_string = NULL;
+
+ if (ldb->err_string) {
+ old_string = ldb->err_string;
+ }
+
+ va_start(ap, format);
+ ldb->err_string = talloc_vasprintf(ldb, format, ap);
+ va_end(ap);
+ talloc_free(old_string);
+}
+
+void ldb_reset_err_string(struct ldb_context *ldb)
+{
+ if (ldb->err_string) {
+ talloc_free(ldb->err_string);
+ ldb->err_string = NULL;
+ }
+}
+
+#define FIRST_OP(ldb, op) do { \
+ module = ldb->modules; \
+ while (module && module->ops->op == NULL) module = module->next; \
+ if (module == NULL) { \
+ ldb_asprintf_errstring(ldb, "unable to find module or backend to handle operation: " #op); \
+ return LDB_ERR_OPERATIONS_ERROR; \
+ } \
+} while (0)
+
+/*
+ start a transaction
+*/
+int ldb_transaction_start(struct ldb_context *ldb)
+{
+ struct ldb_module *module;
+ int status;
+
+ ldb_debug(ldb, LDB_DEBUG_TRACE,
+ "start ldb transaction (nesting: %d)",
+ ldb->transaction_active);
+
+ /* explicit transaction active, count nested requests */
+ if (ldb->transaction_active) {
+ ldb->transaction_active++;
+ return LDB_SUCCESS;
+ }
+
+ /* start a new transaction */
+ ldb->transaction_active++;
+
+ FIRST_OP(ldb, start_transaction);
+
+ ldb_reset_err_string(ldb);
+
+ status = module->ops->start_transaction(module);
+ if (status != LDB_SUCCESS) {
+ if (ldb->err_string == NULL) {
+ /* no error string was setup by the backend */
+ ldb_asprintf_errstring(ldb,
+ "ldb transaction start: %s (%d)",
+ ldb_strerror(status),
+ status);
+ }
+ }
+ return status;
+}
+
+/*
+ commit a transaction
+*/
+int ldb_transaction_commit(struct ldb_context *ldb)
+{
+ struct ldb_module *module;
+ int status;
+
+ ldb->transaction_active--;
+
+ ldb_debug(ldb, LDB_DEBUG_TRACE,
+ "commit ldb transaction (nesting: %d)",
+ ldb->transaction_active);
+
+ /* commit only when all nested transactions are complete */
+ if (ldb->transaction_active > 0) {
+ return LDB_SUCCESS;
+ }
+
+ if (ldb->transaction_active < 0) {
+ ldb_debug(ldb, LDB_DEBUG_FATAL,
+ "commit called but no ldb transactions are active!");
+ ldb->transaction_active = 0;
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ FIRST_OP(ldb, end_transaction);
+
+ ldb_reset_err_string(ldb);
+
+ status = module->ops->end_transaction(module);
+ if (status != LDB_SUCCESS) {
+ if (ldb->err_string == NULL) {
+ /* no error string was setup by the backend */
+ ldb_asprintf_errstring(ldb,
+ "ldb transaction commit: %s (%d)",
+ ldb_strerror(status),
+ status);
+ }
+ }
+ return status;
+}
+
+/*
+ cancel a transaction
+*/
+int ldb_transaction_cancel(struct ldb_context *ldb)
+{
+ struct ldb_module *module;
+ int status;
+
+ ldb->transaction_active--;
+
+ ldb_debug(ldb, LDB_DEBUG_TRACE,
+ "cancel ldb transaction (nesting: %d)",
+ ldb->transaction_active);
+
+ /* really cancel only if all nested transactions are complete */
+ if (ldb->transaction_active > 0) {
+ return LDB_SUCCESS;
+ }
+
+ if (ldb->transaction_active < 0) {
+ ldb_debug(ldb, LDB_DEBUG_FATAL,
+ "commit called but no ldb transactions are active!");
+ ldb->transaction_active = 0;
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ FIRST_OP(ldb, del_transaction);
+
+ status = module->ops->del_transaction(module);
+ if (status != LDB_SUCCESS) {
+ if (ldb->err_string == NULL) {
+ /* no error string was setup by the backend */
+ ldb_asprintf_errstring(ldb,
+ "ldb transaction cancel: %s (%d)",
+ ldb_strerror(status),
+ status);
+ }
+ }
+ return status;
+}
+
+/* autostarts a transacion if none active */
+static int ldb_autotransaction_request(struct ldb_context *ldb,
+ struct ldb_request *req)
+{
+ int ret;
+
+ ret = ldb_transaction_start(ldb);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ ret = ldb_request(ldb, req);
+ if (ret == LDB_SUCCESS) {
+ ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+ }
+
+ if (ret == LDB_SUCCESS) {
+ return ldb_transaction_commit(ldb);
+ }
+ ldb_transaction_cancel(ldb);
+
+ if (ldb->err_string == NULL) {
+ /* no error string was setup by the backend */
+ ldb_asprintf_errstring(ldb, "%s (%d)", ldb_strerror(ret), ret);
+ }
+
+ return ret;
+}
+
+int ldb_wait(struct ldb_handle *handle, enum ldb_wait_type type)
+{
+ struct tevent_context *ev;
+ int ret;
+
+ if (!handle) {
+ return LDB_ERR_UNAVAILABLE;
+ }
+
+ if (handle->state == LDB_ASYNC_DONE) {
+ return handle->status;
+ }
+
+ ev = ldb_get_event_context(handle->ldb);
+ if (NULL == ev) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ switch (type) {
+ case LDB_WAIT_NONE:
+ ret = tevent_loop_once(ev);
+ if (ret != 0) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ if (handle->state == LDB_ASYNC_DONE ||
+ handle->status != LDB_SUCCESS) {
+ return handle->status;
+ }
+ break;
+
+ case LDB_WAIT_ALL:
+ while (handle->state != LDB_ASYNC_DONE) {
+ ret = tevent_loop_once(ev);
+ if (ret != 0) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ if (handle->status != LDB_SUCCESS) {
+ return handle->status;
+ }
+ }
+ return handle->status;
+ }
+
+ return LDB_SUCCESS;
+}
+
+/* set the specified timeout or, if timeout is 0 set the default timeout */
+int ldb_set_timeout(struct ldb_context *ldb,
+ struct ldb_request *req,
+ int timeout)
+{
+ if (req == NULL) return LDB_ERR_OPERATIONS_ERROR;
+
+ if (timeout != 0) {
+ req->timeout = timeout;
+ } else {
+ req->timeout = ldb->default_timeout;
+ }
+ req->starttime = time(NULL);
+
+ return LDB_SUCCESS;
+}
+
+/* calculates the new timeout based on the previous starttime and timeout */
+int ldb_set_timeout_from_prev_req(struct ldb_context *ldb,
+ struct ldb_request *oldreq,
+ struct ldb_request *newreq)
+{
+ if (newreq == NULL) return LDB_ERR_OPERATIONS_ERROR;
+
+ if (oldreq == NULL) {
+ return ldb_set_timeout(ldb, newreq, 0);
+ }
+
+ newreq->starttime = oldreq->starttime;
+ newreq->timeout = oldreq->timeout;
+
+ return LDB_SUCCESS;
+}
+
+
+/*
+ set the permissions for new files to be passed to open() in
+ backends that use local files
+ */
+void ldb_set_create_perms(struct ldb_context *ldb, unsigned int perms)
+{
+ ldb->create_perms = perms;
+}
+
+unsigned int ldb_get_create_perms(struct ldb_context *ldb)
+{
+ return ldb->create_perms;
+}
+
+void ldb_set_event_context(struct ldb_context *ldb, struct tevent_context *ev)
+{
+ ldb->ev_ctx = ev;
+}
+
+struct tevent_context * ldb_get_event_context(struct ldb_context *ldb)
+{
+ return ldb->ev_ctx;
+}
+
+void ldb_request_set_state(struct ldb_request *req, int state)
+{
+ req->handle->state = state;
+}
+
+int ldb_request_get_status(struct ldb_request *req)
+{
+ return req->handle->status;
+}
+
+/*
+ start an ldb request
+ NOTE: the request must be a talloc context.
+ returns LDB_ERR_* on errors.
+*/
+int ldb_request(struct ldb_context *ldb, struct ldb_request *req)
+{
+ struct ldb_module *module;
+ int ret;
+
+ if (req->callback == NULL) {
+ ldb_set_errstring(ldb, "Requests MUST define callbacks");
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+
+ ldb_reset_err_string(ldb);
+
+ /* call the first module in the chain */
+ switch (req->operation) {
+ case LDB_SEARCH:
+ FIRST_OP(ldb, search);
+ ret = module->ops->search(module, req);
+ break;
+ case LDB_ADD:
+ FIRST_OP(ldb, add);
+ ret = module->ops->add(module, req);
+ break;
+ case LDB_MODIFY:
+ FIRST_OP(ldb, modify);
+ ret = module->ops->modify(module, req);
+ break;
+ case LDB_DELETE:
+ FIRST_OP(ldb, del);
+ ret = module->ops->del(module, req);
+ break;
+ case LDB_RENAME:
+ FIRST_OP(ldb, rename);
+ ret = module->ops->rename(module, req);
+ break;
+ case LDB_EXTENDED:
+ FIRST_OP(ldb, extended);
+ ret = module->ops->extended(module, req);
+ break;
+ default:
+ FIRST_OP(ldb, request);
+ ret = module->ops->request(module, req);
+ break;
+ }
+
+ return ret;
+}
+
+int ldb_request_done(struct ldb_request *req, int status)
+{
+ req->handle->state = LDB_ASYNC_DONE;
+ req->handle->status = status;
+ return status;
+}
+
+/*
+ search the database given a LDAP-like search expression
+
+ returns an LDB error code
+
+ Use talloc_free to free the ldb_message returned in 'res', if successful
+
+*/
+int ldb_search_default_callback(struct ldb_request *req,
+ struct ldb_reply *ares)
+{
+ struct ldb_result *res;
+ int n;
+
+ res = talloc_get_type(req->context, struct ldb_result);
+
+ 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:
+ res->msgs = talloc_realloc(res, res->msgs,
+ struct ldb_message *, res->count + 2);
+ if (! res->msgs) {
+ return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ res->msgs[res->count + 1] = NULL;
+
+ res->msgs[res->count] = talloc_move(res->msgs, &ares->message);
+ res->count++;
+ break;
+
+ case LDB_REPLY_REFERRAL:
+ if (res->refs) {
+ for (n = 0; res->refs[n]; n++) /*noop*/ ;
+ } else {
+ n = 0;
+ }
+
+ res->refs = talloc_realloc(res, res->refs, char *, n + 2);
+ if (! res->refs) {
+ return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ res->refs[n] = talloc_move(res->refs, &ares->referral);
+ res->refs[n + 1] = NULL;
+ break;
+
+ case LDB_REPLY_DONE:
+ /* TODO: we should really support controls on entries
+ * and referrals too! */
+ res->controls = talloc_move(res, &ares->controls);
+
+ /* this is the last message, and means the request is done */
+ /* we have to signal and eventual ldb_wait() waiting that the
+ * async request operation was completed */
+ return ldb_request_done(req, LDB_SUCCESS);
+ }
+
+ talloc_free(ares);
+ return LDB_SUCCESS;
+}
+
+int ldb_op_default_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+ int ret;
+
+ if (!ares) {
+ return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ if (ares->error != LDB_SUCCESS) {
+ ret = ares->error;
+ talloc_free(ares);
+ return ldb_request_done(req, ret);
+ }
+
+ if (ares->type != LDB_REPLY_DONE) {
+ talloc_free(ares);
+ ldb_set_errstring(req->handle->ldb, "Invalid reply type!");
+ return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ talloc_free(ares);
+ return ldb_request_done(req, LDB_SUCCESS);
+}
+
+int ldb_build_search_req_ex(struct ldb_request **ret_req,
+ struct ldb_context *ldb,
+ void *mem_ctx,
+ struct ldb_dn *base,
+ enum ldb_scope scope,
+ struct ldb_parse_tree *tree,
+ const char * const *attrs,
+ struct ldb_control **controls,
+ void *context,
+ ldb_request_callback_t callback,
+ struct ldb_request *parent)
+{
+ struct ldb_request *req;
+
+ *ret_req = NULL;
+
+ req = talloc(mem_ctx, struct ldb_request);
+ if (req == NULL) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ req->operation = LDB_SEARCH;
+ if (base == NULL) {
+ req->op.search.base = ldb_dn_new(req, ldb, NULL);
+ } else {
+ req->op.search.base = base;
+ }
+ req->op.search.scope = scope;
+
+ req->op.search.tree = tree;
+ if (req->op.search.tree == NULL) {
+ ldb_set_errstring(ldb, "'tree' can't be NULL");
+ talloc_free(req);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ req->op.search.attrs = attrs;
+ req->controls = controls;
+ req->context = context;
+ req->callback = callback;
+
+ ldb_set_timeout_from_prev_req(ldb, parent, req);
+
+ req->handle = ldb_handle_new(req, ldb);
+ if (req->handle == NULL) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ *ret_req = req;
+ return LDB_SUCCESS;
+}
+
+int ldb_build_search_req(struct ldb_request **ret_req,
+ struct ldb_context *ldb,
+ void *mem_ctx,
+ struct ldb_dn *base,
+ enum ldb_scope scope,
+ const char *expression,
+ const char * const *attrs,
+ struct ldb_control **controls,
+ void *context,
+ ldb_request_callback_t callback,
+ struct ldb_request *parent)
+{
+ struct ldb_parse_tree *tree;
+ int ret;
+
+ tree = ldb_parse_tree(mem_ctx, expression);
+ if (tree == NULL) {
+ ldb_set_errstring(ldb, "Unable to parse search expression");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ldb_build_search_req_ex(ret_req, ldb, mem_ctx, base,
+ scope, tree, attrs, controls,
+ context, callback, parent);
+ if (ret == LDB_SUCCESS) {
+ talloc_steal(*ret_req, tree);
+ }
+ return ret;
+}
+
+int ldb_build_add_req(struct ldb_request **ret_req,
+ struct ldb_context *ldb,
+ void *mem_ctx,
+ const struct ldb_message *message,
+ struct ldb_control **controls,
+ void *context,
+ ldb_request_callback_t callback,
+ struct ldb_request *parent)
+{
+ struct ldb_request *req;
+
+ *ret_req = NULL;
+
+ req = talloc(mem_ctx, struct ldb_request);
+ if (req == NULL) {
+ ldb_set_errstring(ldb, "Out of Memory");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ req->operation = LDB_ADD;
+ req->op.add.message = message;
+ req->controls = controls;
+ req->context = context;
+ req->callback = callback;
+
+ ldb_set_timeout_from_prev_req(ldb, parent, req);
+
+ req->handle = ldb_handle_new(req, ldb);
+ if (req->handle == NULL) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ *ret_req = req;
+
+ return LDB_SUCCESS;
+}
+
+int ldb_build_mod_req(struct ldb_request **ret_req,
+ struct ldb_context *ldb,
+ void *mem_ctx,
+ const struct ldb_message *message,
+ struct ldb_control **controls,
+ void *context,
+ ldb_request_callback_t callback,
+ struct ldb_request *parent)
+{
+ struct ldb_request *req;
+
+ *ret_req = NULL;
+
+ req = talloc(mem_ctx, struct ldb_request);
+ if (req == NULL) {
+ ldb_set_errstring(ldb, "Out of Memory");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ req->operation = LDB_MODIFY;
+ req->op.mod.message = message;
+ req->controls = controls;
+ req->context = context;
+ req->callback = callback;
+
+ ldb_set_timeout_from_prev_req(ldb, parent, req);
+
+ req->handle = ldb_handle_new(req, ldb);
+ if (req->handle == NULL) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ *ret_req = req;
+
+ return LDB_SUCCESS;
+}
+
+int ldb_build_del_req(struct ldb_request **ret_req,
+ struct ldb_context *ldb,
+ void *mem_ctx,
+ struct ldb_dn *dn,
+ struct ldb_control **controls,
+ void *context,
+ ldb_request_callback_t callback,
+ struct ldb_request *parent)
+{
+ struct ldb_request *req;
+
+ *ret_req = NULL;
+
+ req = talloc(mem_ctx, struct ldb_request);
+ if (req == NULL) {
+ ldb_set_errstring(ldb, "Out of Memory");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ req->operation = LDB_DELETE;
+ req->op.del.dn = dn;
+ req->controls = controls;
+ req->context = context;
+ req->callback = callback;
+
+ ldb_set_timeout_from_prev_req(ldb, parent, req);
+
+ req->handle = ldb_handle_new(req, ldb);
+ if (req->handle == NULL) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ *ret_req = req;
+
+ return LDB_SUCCESS;
+}
+
+int ldb_build_rename_req(struct ldb_request **ret_req,
+ struct ldb_context *ldb,
+ void *mem_ctx,
+ struct ldb_dn *olddn,
+ struct ldb_dn *newdn,
+ struct ldb_control **controls,
+ void *context,
+ ldb_request_callback_t callback,
+ struct ldb_request *parent)
+{
+ struct ldb_request *req;
+
+ *ret_req = NULL;
+
+ req = talloc(mem_ctx, struct ldb_request);
+ if (req == NULL) {
+ ldb_set_errstring(ldb, "Out of Memory");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ req->operation = LDB_RENAME;
+ req->op.rename.olddn = olddn;
+ req->op.rename.newdn = newdn;
+ req->controls = controls;
+ req->context = context;
+ req->callback = callback;
+
+ ldb_set_timeout_from_prev_req(ldb, parent, req);
+
+ req->handle = ldb_handle_new(req, ldb);
+ if (req->handle == NULL) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ *ret_req = req;
+
+ return LDB_SUCCESS;
+}
+
+int ldb_extended_default_callback(struct ldb_request *req,
+ struct ldb_reply *ares)
+{
+ struct ldb_result *res;
+
+ res = talloc_get_type(req->context, struct ldb_result);
+
+ if (!ares) {
+ return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_request_done(req, ares->error);
+ }
+
+ if (ares->type == LDB_REPLY_DONE) {
+
+ /* TODO: we should really support controls on entries and referrals too! */
+ res->extended = talloc_move(res, &ares->response);
+ res->controls = talloc_move(res, &ares->controls);
+
+ talloc_free(ares);
+ return ldb_request_done(req, LDB_SUCCESS);
+ }
+
+ talloc_free(ares);
+ ldb_set_errstring(req->handle->ldb, "Invalid reply type!");
+ return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
+}
+
+int ldb_build_extended_req(struct ldb_request **ret_req,
+ struct ldb_context *ldb,
+ void *mem_ctx,
+ const char *oid,
+ void *data,
+ struct ldb_control **controls,
+ void *context,
+ ldb_request_callback_t callback,
+ struct ldb_request *parent)
+{
+ struct ldb_request *req;
+
+ *ret_req = NULL;
+
+ req = talloc(mem_ctx, struct ldb_request);
+ if (req == NULL) {
+ ldb_set_errstring(ldb, "Out of Memory");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ req->operation = LDB_EXTENDED;
+ req->op.extended.oid = oid;
+ req->op.extended.data = data;
+ req->controls = controls;
+ req->context = context;
+ req->callback = callback;
+
+ ldb_set_timeout_from_prev_req(ldb, parent, req);
+
+ req->handle = ldb_handle_new(req, ldb);
+ if (req->handle == NULL) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ *ret_req = req;
+
+ return LDB_SUCCESS;
+}
+
+int ldb_extended(struct ldb_context *ldb,
+ const char *oid,
+ void *data,
+ struct ldb_result **_res)
+{
+ struct ldb_request *req;
+ int ret;
+ struct ldb_result *res;
+
+ *_res = NULL;
+
+ res = talloc_zero(ldb, struct ldb_result);
+ if (!res) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ldb_build_extended_req(&req, ldb, ldb,
+ oid, data, NULL,
+ res, ldb_extended_default_callback,
+ NULL);
+ if (ret != LDB_SUCCESS) goto done;
+
+ ldb_set_timeout(ldb, req, 0); /* use default timeout */
+
+ ret = ldb_request(ldb, req);
+
+ if (ret == LDB_SUCCESS) {
+ ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+ }
+
+ talloc_free(req);
+
+done:
+ if (ret != LDB_SUCCESS) {
+ talloc_free(res);
+ }
+
+ *_res = res;
+ return ret;
+}
+
+/*
+ note that ldb_search() will automatically replace a NULL 'base' value
+ with the defaultNamingContext from the rootDSE if available.
+*/
+int ldb_search(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
+ struct ldb_result **result, struct ldb_dn *base,
+ enum ldb_scope scope, const char * const *attrs,
+ const char *exp_fmt, ...)
+{
+ struct ldb_request *req;
+ struct ldb_result *res;
+ char *expression;
+ va_list ap;
+ int ret;
+
+ expression = NULL;
+ *result = NULL;
+ req = NULL;
+
+ res = talloc_zero(mem_ctx, struct ldb_result);
+ if (!res) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if (exp_fmt) {
+ va_start(ap, exp_fmt);
+ expression = talloc_vasprintf(mem_ctx, exp_fmt, ap);
+ va_end(ap);
+
+ if (!expression) {
+ talloc_free(res);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ }
+
+ ret = ldb_build_search_req(&req, ldb, mem_ctx,
+ base?base:ldb_get_default_basedn(ldb),
+ scope,
+ expression,
+ attrs,
+ NULL,
+ res,
+ ldb_search_default_callback,
+ NULL);
+
+ if (ret != LDB_SUCCESS) goto done;
+
+ ret = ldb_request(ldb, req);
+
+ if (ret == LDB_SUCCESS) {
+ ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+ }
+
+done:
+ if (ret != LDB_SUCCESS) {
+ talloc_free(res);
+ res = NULL;
+ }
+
+ talloc_free(expression);
+ talloc_free(req);
+
+ *result = res;
+ return ret;
+}
+
+/*
+ add a record to the database. Will fail if a record with the given class
+ and key already exists
+*/
+int ldb_add(struct ldb_context *ldb,
+ const struct ldb_message *message)
+{
+ struct ldb_request *req;
+ int ret;
+
+ ret = ldb_msg_sanity_check(ldb, message);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ ret = ldb_build_add_req(&req, ldb, ldb,
+ message,
+ NULL,
+ NULL,
+ ldb_op_default_callback,
+ NULL);
+
+ if (ret != LDB_SUCCESS) return ret;
+
+ /* do request and autostart a transaction */
+ ret = ldb_autotransaction_request(ldb, req);
+
+ talloc_free(req);
+ return ret;
+}
+
+/*
+ modify the specified attributes of a record
+*/
+int ldb_modify(struct ldb_context *ldb,
+ const struct ldb_message *message)
+{
+ struct ldb_request *req;
+ int ret;
+
+ ret = ldb_msg_sanity_check(ldb, message);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ ret = ldb_build_mod_req(&req, ldb, ldb,
+ message,
+ NULL,
+ NULL,
+ ldb_op_default_callback,
+ NULL);
+
+ if (ret != LDB_SUCCESS) return ret;
+
+ /* do request and autostart a transaction */
+ ret = ldb_autotransaction_request(ldb, req);
+
+ talloc_free(req);
+ return ret;
+}
+
+
+/*
+ delete a record from the database
+*/
+int ldb_delete(struct ldb_context *ldb, struct ldb_dn *dn)
+{
+ struct ldb_request *req;
+ int ret;
+
+ ret = ldb_build_del_req(&req, ldb, ldb,
+ dn,
+ NULL,
+ NULL,
+ ldb_op_default_callback,
+ NULL);
+
+ if (ret != LDB_SUCCESS) return ret;
+
+ /* do request and autostart a transaction */
+ ret = ldb_autotransaction_request(ldb, req);
+
+ talloc_free(req);
+ return ret;
+}
+
+/*
+ rename a record in the database
+*/
+int ldb_rename(struct ldb_context *ldb,
+ struct ldb_dn *olddn, struct ldb_dn *newdn)
+{
+ struct ldb_request *req;
+ int ret;
+
+ ret = ldb_build_rename_req(&req, ldb, ldb,
+ olddn,
+ newdn,
+ NULL,
+ NULL,
+ ldb_op_default_callback,
+ NULL);
+
+ if (ret != LDB_SUCCESS) return ret;
+
+ /* do request and autostart a transaction */
+ ret = ldb_autotransaction_request(ldb, req);
+
+ talloc_free(req);
+ return ret;
+}
+
+
+/*
+ return the global sequence number
+*/
+int ldb_sequence_number(struct ldb_context *ldb,
+ enum ldb_sequence_type type, uint64_t *seq_num)
+{
+ struct ldb_seqnum_request *seq;
+ struct ldb_seqnum_result *seqr;
+ struct ldb_result *res;
+ TALLOC_CTX *tmp_ctx;
+ int ret;
+
+ *seq_num = 0;
+
+ tmp_ctx = talloc_zero(ldb, struct ldb_request);
+ if (tmp_ctx == NULL) {
+ ldb_set_errstring(ldb, "Out of Memory");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ seq = talloc_zero(tmp_ctx, struct ldb_seqnum_request);
+ if (seq == NULL) {
+ ldb_set_errstring(ldb, "Out of Memory");
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ goto done;
+ }
+ seq->type = type;
+
+ ret = ldb_extended(ldb, LDB_EXTENDED_SEQUENCE_NUMBER, seq, &res);
+ if (ret != LDB_SUCCESS) {
+ goto done;
+ }
+ talloc_steal(tmp_ctx, res);
+
+ if (strcmp(LDB_EXTENDED_SEQUENCE_NUMBER, res->extended->oid) != 0) {
+ ldb_set_errstring(ldb, "Invalid OID in reply");
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ goto done;
+ }
+ seqr = talloc_get_type(res->extended->data,
+ struct ldb_seqnum_result);
+ *seq_num = seqr->seq_num;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+/*
+ return extended error information
+*/
+const char *ldb_errstring(struct ldb_context *ldb)
+{
+ if (ldb->err_string) {
+ return ldb->err_string;
+ }
+
+ return NULL;
+}
+
+/*
+ return a string explaining what a ldb error constant meancs
+*/
+const char *ldb_strerror(int ldb_err)
+{
+ switch (ldb_err) {
+ case LDB_SUCCESS:
+ return "Success";
+ case LDB_ERR_OPERATIONS_ERROR:
+ return "Operations error";
+ case LDB_ERR_PROTOCOL_ERROR:
+ return "Protocol error";
+ case LDB_ERR_TIME_LIMIT_EXCEEDED:
+ return "Time limit exceeded";
+ case LDB_ERR_SIZE_LIMIT_EXCEEDED:
+ return "Size limit exceeded";
+ case LDB_ERR_COMPARE_FALSE:
+ return "Compare false";
+ case LDB_ERR_COMPARE_TRUE:
+ return "Compare true";
+ case LDB_ERR_AUTH_METHOD_NOT_SUPPORTED:
+ return "Auth method not supported";
+ case LDB_ERR_STRONG_AUTH_REQUIRED:
+ return "Strong auth required";
+/* 9 RESERVED */
+ case LDB_ERR_REFERRAL:
+ return "Referral error";
+ case LDB_ERR_ADMIN_LIMIT_EXCEEDED:
+ return "Admin limit exceeded";
+ case LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION:
+ return "Unsupported critical extension";
+ case LDB_ERR_CONFIDENTIALITY_REQUIRED:
+ return "Confidentiality required";
+ case LDB_ERR_SASL_BIND_IN_PROGRESS:
+ return "SASL bind in progress";
+ case LDB_ERR_NO_SUCH_ATTRIBUTE:
+ return "No such attribute";
+ case LDB_ERR_UNDEFINED_ATTRIBUTE_TYPE:
+ return "Undefined attribute type";
+ case LDB_ERR_INAPPROPRIATE_MATCHING:
+ return "Inappropriate matching";
+ case LDB_ERR_CONSTRAINT_VIOLATION:
+ return "Constraint violation";
+ case LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS:
+ return "Attribute or value exists";
+ case LDB_ERR_INVALID_ATTRIBUTE_SYNTAX:
+ return "Invalid attribute syntax";
+/* 22-31 unused */
+ case LDB_ERR_NO_SUCH_OBJECT:
+ return "No such object";
+ case LDB_ERR_ALIAS_PROBLEM:
+ return "Alias problem";
+ case LDB_ERR_INVALID_DN_SYNTAX:
+ return "Invalid DN syntax";
+/* 35 RESERVED */
+ case LDB_ERR_ALIAS_DEREFERENCING_PROBLEM:
+ return "Alias dereferencing problem";
+/* 37-47 unused */
+ case LDB_ERR_INAPPROPRIATE_AUTHENTICATION:
+ return "Inappropriate authentication";
+ case LDB_ERR_INVALID_CREDENTIALS:
+ return "Invalid credentials";
+ case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
+ return "insufficient access rights";
+ case LDB_ERR_BUSY:
+ return "Busy";
+ case LDB_ERR_UNAVAILABLE:
+ return "Unavailable";
+ case LDB_ERR_UNWILLING_TO_PERFORM:
+ return "Unwilling to perform";
+ case LDB_ERR_LOOP_DETECT:
+ return "Loop detect";
+/* 55-63 unused */
+ case LDB_ERR_NAMING_VIOLATION:
+ return "Naming violation";
+ case LDB_ERR_OBJECT_CLASS_VIOLATION:
+ return "Object class violation";
+ case LDB_ERR_NOT_ALLOWED_ON_NON_LEAF:
+ return "Not allowed on non-leaf";
+ case LDB_ERR_NOT_ALLOWED_ON_RDN:
+ return "Not allowed on RDN";
+ case LDB_ERR_ENTRY_ALREADY_EXISTS:
+ return "Entry already exists";
+ case LDB_ERR_OBJECT_CLASS_MODS_PROHIBITED:
+ return "Object class mods prohibited";
+/* 70 RESERVED FOR CLDAP */
+ case LDB_ERR_AFFECTS_MULTIPLE_DSAS:
+ return "Affects multiple DSAs";
+/* 72-79 unused */
+ case LDB_ERR_OTHER:
+ return "Other";
+ }
+
+ return "Unknown error";
+}
+
+/*
+ set backend specific opaque parameters
+*/
+int ldb_set_opaque(struct ldb_context *ldb, const char *name, void *value)
+{
+ struct ldb_opaque *o;
+
+ /* allow updating an existing value */
+ for (o=ldb->opaque;o;o=o->next) {
+ if (strcmp(o->name, name) == 0) {
+ o->value = value;
+ return LDB_SUCCESS;
+ }
+ }
+
+ o = talloc(ldb, struct ldb_opaque);
+ if (o == NULL) {
+ ldb_oom(ldb);
+ return LDB_ERR_OTHER;
+ }
+ o->next = ldb->opaque;
+ o->name = name;
+ o->value = value;
+ ldb->opaque = o;
+ return LDB_SUCCESS;
+}
+
+/*
+ get a previously set opaque value
+*/
+void *ldb_get_opaque(struct ldb_context *ldb, const char *name)
+{
+ struct ldb_opaque *o;
+ for (o=ldb->opaque;o;o=o->next) {
+ if (strcmp(o->name, name) == 0) {
+ return o->value;
+ }
+ }
+ return NULL;
+}
diff --git a/source4/lib/ldb/common/ldb_attributes.c b/source4/lib/ldb/common/ldb_attributes.c
new file mode 100644
index 0000000000..9fa0fb2ccd
--- /dev/null
+++ b/source4/lib/ldb/common/ldb_attributes.c
@@ -0,0 +1,275 @@
+/*
+ ldb database library
+
+ Copyright (C) Andrew Tridgell 2005
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+/*
+ register handlers for specific attributes and objectclass relationships
+
+ this allows a backend to store its schema information in any format
+ it likes (or to not have any schema information at all) while keeping the
+ message matching logic generic
+*/
+
+#include "ldb_private.h"
+#include "ldb_handlers.h"
+
+/*
+ add a attribute to the ldb_schema
+
+ if flags contains LDB_ATTR_FLAG_ALLOCATED
+ the attribute name string will be copied using
+ talloc_strdup(), otherwise it needs to be a static const
+ string at least with a lifetime longer than the ldb struct!
+
+ the ldb_schema_syntax structure should be a pointer
+ to a static const struct or at least it needs to be
+ a struct with a longer lifetime than the ldb context!
+
+*/
+int ldb_schema_attribute_add_with_syntax(struct ldb_context *ldb,
+ const char *attribute,
+ unsigned flags,
+ const struct ldb_schema_syntax *syntax)
+{
+ int i, n;
+ struct ldb_schema_attribute *a;
+
+ if (!syntax) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ n = ldb->schema.num_attributes + 1;
+
+ a = talloc_realloc(ldb, ldb->schema.attributes,
+ struct ldb_schema_attribute, n);
+ if (a == NULL) {
+ ldb_oom(ldb);
+ return -1;
+ }
+ ldb->schema.attributes = a;
+
+ for (i = 0; i < ldb->schema.num_attributes; i++) {
+ int cmp = ldb_attr_cmp(attribute, a[i].name);
+ if (cmp == 0) {
+ /* silently ignore attempts to overwrite fixed attributes */
+ if (a[i].flags & LDB_ATTR_FLAG_FIXED) {
+ return 0;
+ }
+ if (a[i].flags & LDB_ATTR_FLAG_ALLOCATED) {
+ talloc_free(discard_const_p(char, a[i].name));
+ }
+ /* To cancel out increment below */
+ ldb->schema.num_attributes--;
+ break;
+ } else if (cmp < 0) {
+ memmove(a+i+1, a+i, sizeof(*a) * (ldb->schema.num_attributes-i));
+ break;
+ }
+ }
+ ldb->schema.num_attributes++;
+
+ a[i].name = attribute;
+ a[i].flags = flags;
+ a[i].syntax = syntax;
+
+ if (a[i].flags & LDB_ATTR_FLAG_ALLOCATED) {
+ a[i].name = talloc_strdup(a, a[i].name);
+ if (a[i].name == NULL) {
+ ldb_oom(ldb);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static const struct ldb_schema_syntax ldb_syntax_default = {
+ .name = LDB_SYNTAX_OCTET_STRING,
+ .ldif_read_fn = ldb_handler_copy,
+ .ldif_write_fn = ldb_handler_copy,
+ .canonicalise_fn = ldb_handler_copy,
+ .comparison_fn = ldb_comparison_binary
+};
+
+static const struct ldb_schema_attribute ldb_attribute_default = {
+ .name = NULL,
+ .flags = 0,
+ .syntax = &ldb_syntax_default
+};
+
+/*
+ return the attribute handlers for a given attribute
+*/
+const struct ldb_schema_attribute *ldb_schema_attribute_by_name(struct ldb_context *ldb,
+ const char *name)
+{
+ int i, e, b = 0, r;
+ const struct ldb_schema_attribute *def = &ldb_attribute_default;
+
+ /* as handlers are sorted, '*' must be the first if present */
+ if (strcmp(ldb->schema.attributes[0].name, "*") == 0) {
+ def = &ldb->schema.attributes[0];
+ b = 1;
+ }
+
+ /* do a binary search on the array */
+ e = ldb->schema.num_attributes - 1;
+
+ while (b <= e) {
+
+ i = (b + e) / 2;
+
+ r = ldb_attr_cmp(name, ldb->schema.attributes[i].name);
+ if (r == 0) {
+ return &ldb->schema.attributes[i];
+ }
+ if (r < 0) {
+ e = i - 1;
+ } else {
+ b = i + 1;
+ }
+
+ }
+
+ return def;
+}
+
+
+/*
+ add to the list of ldif handlers for this ldb context
+*/
+void ldb_schema_attribute_remove(struct ldb_context *ldb, const char *name)
+{
+ const struct ldb_schema_attribute *a;
+ int i;
+
+ a = ldb_schema_attribute_by_name(ldb, name);
+ if (a == NULL || a->name == NULL) {
+ return;
+ }
+
+ /* FIXED attributes are never removed */
+ if (a->flags & LDB_ATTR_FLAG_FIXED) {
+ return;
+ }
+
+ if (a->flags & LDB_ATTR_FLAG_ALLOCATED) {
+ talloc_free(discard_const_p(char, a->name));
+ }
+
+ i = a - ldb->schema.attributes;
+ if (i < ldb->schema.num_attributes - 1) {
+ memmove(&ldb->schema.attributes[i],
+ a+1, sizeof(*a) * (ldb->schema.num_attributes-(i+1)));
+ }
+
+ ldb->schema.num_attributes--;
+}
+
+/*
+ setup a attribute handler using a standard syntax
+*/
+int ldb_schema_attribute_add(struct ldb_context *ldb,
+ const char *attribute,
+ unsigned flags,
+ const char *syntax)
+{
+ const struct ldb_schema_syntax *s = ldb_standard_syntax_by_name(ldb, syntax);
+ return ldb_schema_attribute_add_with_syntax(ldb, attribute, flags, s);
+}
+
+/*
+ setup the attribute handles for well known attributes
+*/
+int ldb_setup_wellknown_attributes(struct ldb_context *ldb)
+{
+ const struct {
+ const char *attr;
+ const char *syntax;
+ } wellknown[] = {
+ { "dn", LDB_SYNTAX_DN },
+ { "distinguishedName", LDB_SYNTAX_DN },
+ { "cn", LDB_SYNTAX_DIRECTORY_STRING },
+ { "dc", LDB_SYNTAX_DIRECTORY_STRING },
+ { "ou", LDB_SYNTAX_DIRECTORY_STRING },
+ { "objectClass", LDB_SYNTAX_OBJECTCLASS }
+ };
+ int i;
+ int ret;
+
+ for (i=0;i<ARRAY_SIZE(wellknown);i++) {
+ ret = ldb_schema_attribute_add(ldb, wellknown[i].attr, 0,
+ wellknown[i].syntax);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+
+ return LDB_SUCCESS;
+}
+
+
+/*
+ add a extended dn syntax to the ldb_schema
+*/
+int ldb_dn_extended_add_syntax(struct ldb_context *ldb,
+ unsigned flags,
+ const struct ldb_dn_extended_syntax *syntax)
+{
+ int n;
+ struct ldb_dn_extended_syntax *a;
+
+ if (!syntax) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ n = ldb->schema.num_dn_extended_syntax + 1;
+
+ a = talloc_realloc(ldb, ldb->schema.dn_extended_syntax,
+ struct ldb_dn_extended_syntax, n);
+
+ if (!a) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ a[ldb->schema.num_dn_extended_syntax] = *syntax;
+ ldb->schema.dn_extended_syntax = a;
+
+ ldb->schema.num_dn_extended_syntax = n;
+
+ return LDB_SUCCESS;
+}
+
+/*
+ return the extended dn syntax for a given name
+*/
+const struct ldb_dn_extended_syntax *ldb_dn_extended_syntax_by_name(struct ldb_context *ldb,
+ const char *name)
+{
+ int i;
+ for (i=0; i < ldb->schema.num_dn_extended_syntax; i++) {
+ if (ldb_attr_cmp(ldb->schema.dn_extended_syntax[i].name, name) == 0) {
+ return &ldb->schema.dn_extended_syntax[i];
+ }
+ }
+ return NULL;
+}
+
diff --git a/source4/lib/ldb/common/ldb_controls.c b/source4/lib/ldb/common/ldb_controls.c
new file mode 100644
index 0000000000..0c587e0905
--- /dev/null
+++ b/source4/lib/ldb/common/ldb_controls.c
@@ -0,0 +1,597 @@
+/*
+ ldb database library
+
+ Copyright (C) Simo Sorce 2005
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb_controls.c
+ *
+ * Component: ldb controls utility functions
+ *
+ * Description: helper functions for control modules
+ *
+ * Author: Simo Sorce
+ */
+
+#include "ldb_private.h"
+
+/* check if a control with the specified "oid" exist and return it */
+/* returns NULL if not found */
+struct ldb_control *ldb_request_get_control(struct ldb_request *req, const char *oid)
+{
+ int i;
+
+ /* check if there's a paged request control */
+ if (req->controls != NULL) {
+ for (i = 0; req->controls[i]; i++) {
+ if (strcmp(oid, req->controls[i]->oid) == 0) {
+ break;
+ }
+ }
+
+ return req->controls[i];
+ }
+
+ return NULL;
+}
+
+/* check if a control with the specified "oid" exist and return it */
+/* returns NULL if not found */
+struct ldb_control *ldb_reply_get_control(struct ldb_reply *rep, const char *oid)
+{
+ int i;
+
+ /* check if there's a paged request control */
+ if (rep->controls != NULL) {
+ for (i = 0; rep->controls[i]; i++) {
+ if (strcmp(oid, rep->controls[i]->oid) == 0) {
+ break;
+ }
+ }
+
+ return rep->controls[i];
+ }
+
+ return NULL;
+}
+
+/* saves the current controls list into the "saver" and replace the one in req with a new one excluding
+the "exclude" control */
+/* returns False on error */
+int save_controls(struct ldb_control *exclude, struct ldb_request *req, struct ldb_control ***saver)
+{
+ struct ldb_control **lcs;
+ int i, j;
+
+ *saver = req->controls;
+ for (i = 0; req->controls[i]; i++);
+ if (i == 1) {
+ req->controls = NULL;
+ return 1;
+ }
+
+ lcs = talloc_array(req, struct ldb_control *, i);
+ if (!lcs) {
+ return 0;
+ }
+
+ for (i = 0, j = 0; (*saver)[i]; i++) {
+ if (exclude == (*saver)[i]) continue;
+ lcs[j] = (*saver)[i];
+ j++;
+ }
+ lcs[j] = NULL;
+
+ req->controls = lcs;
+ return 1;
+}
+
+/* check if there's any control marked as critical in the list */
+/* return True if any, False if none */
+int check_critical_controls(struct ldb_control **controls)
+{
+ int i;
+
+ if (controls == NULL) {
+ return 0;
+ }
+
+ for (i = 0; controls[i]; i++) {
+ if (controls[i]->critical) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+int ldb_request_add_control(struct ldb_request *req, const char *oid, bool critical, void *data)
+{
+ unsigned n;
+ struct ldb_control **ctrls;
+ struct ldb_control *ctrl;
+
+ for (n=0; req->controls && req->controls[n];) { n++; }
+
+ ctrls = talloc_realloc(req, req->controls,
+ struct ldb_control *,
+ n + 2);
+ if (!ctrls) return LDB_ERR_OPERATIONS_ERROR;
+ req->controls = ctrls;
+ ctrls[n] = NULL;
+ ctrls[n+1] = NULL;
+
+ ctrl = talloc(ctrls, struct ldb_control);
+ if (!ctrl) return LDB_ERR_OPERATIONS_ERROR;
+
+ ctrl->oid = talloc_strdup(ctrl, oid);
+ if (!ctrl->oid) return LDB_ERR_OPERATIONS_ERROR;
+ ctrl->critical = critical;
+ ctrl->data = data;
+
+ ctrls[n] = ctrl;
+ return LDB_SUCCESS;
+}
+
+/* Parse controls from the format used on the command line and in ejs */
+
+struct ldb_control **ldb_parse_control_strings(struct ldb_context *ldb, void *mem_ctx, const char **control_strings)
+{
+ int i;
+ struct ldb_control **ctrl;
+
+ char *error_string = NULL;
+
+ if (control_strings == NULL || control_strings[0] == NULL)
+ return NULL;
+
+ for (i = 0; control_strings[i]; i++);
+
+ ctrl = talloc_array(mem_ctx, struct ldb_control *, i + 1);
+
+ for (i = 0; control_strings[i]; i++) {
+ if (strncmp(control_strings[i], "vlv:", 4) == 0) {
+ struct ldb_vlv_req_control *control;
+ const char *p;
+ char attr[1024];
+ char ctxid[1024];
+ int crit, bc, ac, os, cc, ret;
+
+ attr[0] = '\0';
+ ctxid[0] = '\0';
+ p = &(control_strings[i][4]);
+ ret = sscanf(p, "%d:%d:%d:%d:%d:%1023[^$]", &crit, &bc, &ac, &os, &cc, ctxid);
+ if (ret < 5) {
+ ret = sscanf(p, "%d:%d:%d:%1023[^:]:%1023[^$]", &crit, &bc, &ac, attr, ctxid);
+ }
+
+ if ((ret < 4) || (crit < 0) || (crit > 1)) {
+ error_string = talloc_asprintf(mem_ctx, "invalid server_sort control syntax\n");
+ error_string = talloc_asprintf_append(error_string, " syntax: crit(b):bc(n):ac(n):<os(n):cc(n)|attr(s)>[:ctxid(o)]\n");
+ error_string = talloc_asprintf_append(error_string, " note: b = boolean, n = number, s = string, o = b64 binary blob");
+ ldb_set_errstring(ldb, error_string);
+ talloc_free(error_string);
+ return NULL;
+ }
+ if (!(ctrl[i] = talloc(ctrl, struct ldb_control))) {
+ ldb_oom(ldb);
+ return NULL;
+ }
+ ctrl[i]->oid = LDB_CONTROL_VLV_REQ_OID;
+ ctrl[i]->critical = crit;
+ if (!(control = talloc(ctrl[i],
+ struct ldb_vlv_req_control))) {
+ ldb_oom(ldb);
+ return NULL;
+ }
+ control->beforeCount = bc;
+ control->afterCount = ac;
+ if (attr[0]) {
+ control->type = 1;
+ control->match.gtOrEq.value = talloc_strdup(control, attr);
+ control->match.gtOrEq.value_len = strlen(attr);
+ } else {
+ control->type = 0;
+ control->match.byOffset.offset = os;
+ control->match.byOffset.contentCount = cc;
+ }
+ if (ctxid[0]) {
+ control->ctxid_len = ldb_base64_decode(ctxid);
+ control->contextId = (char *)talloc_memdup(control, ctxid, control->ctxid_len);
+ } else {
+ control->ctxid_len = 0;
+ control->contextId = NULL;
+ }
+ ctrl[i]->data = control;
+
+ continue;
+ }
+
+ if (strncmp(control_strings[i], "dirsync:", 8) == 0) {
+ struct ldb_dirsync_control *control;
+ const char *p;
+ char cookie[1024];
+ int crit, flags, max_attrs, ret;
+
+ cookie[0] = '\0';
+ p = &(control_strings[i][8]);
+ ret = sscanf(p, "%d:%d:%d:%1023[^$]", &crit, &flags, &max_attrs, cookie);
+
+ if ((ret < 3) || (crit < 0) || (crit > 1) || (flags < 0) || (max_attrs < 0)) {
+ error_string = talloc_asprintf(mem_ctx, "invalid dirsync control syntax\n");
+ error_string = talloc_asprintf_append(error_string, " syntax: crit(b):flags(n):max_attrs(n)[:cookie(o)]\n");
+ error_string = talloc_asprintf_append(error_string, " note: b = boolean, n = number, o = b64 binary blob");
+ ldb_set_errstring(ldb, error_string);
+ talloc_free(error_string);
+ return NULL;
+ }
+
+ /* w2k3 seems to ignore the parameter,
+ * but w2k sends a wrong cookie when this value is to small
+ * this would cause looping forever, while getting
+ * the same data and same cookie forever
+ */
+ if (max_attrs == 0) max_attrs = 0x0FFFFFFF;
+
+ ctrl[i] = talloc(ctrl, struct ldb_control);
+ ctrl[i]->oid = LDB_CONTROL_DIRSYNC_OID;
+ ctrl[i]->critical = crit;
+ control = talloc(ctrl[i], struct ldb_dirsync_control);
+ control->flags = flags;
+ control->max_attributes = max_attrs;
+ if (*cookie) {
+ control->cookie_len = ldb_base64_decode(cookie);
+ control->cookie = (char *)talloc_memdup(control, cookie, control->cookie_len);
+ } else {
+ control->cookie = NULL;
+ control->cookie_len = 0;
+ }
+ ctrl[i]->data = control;
+
+ continue;
+ }
+
+ if (strncmp(control_strings[i], "asq:", 4) == 0) {
+ struct ldb_asq_control *control;
+ const char *p;
+ char attr[256];
+ int crit, ret;
+
+ attr[0] = '\0';
+ p = &(control_strings[i][4]);
+ ret = sscanf(p, "%d:%255[^$]", &crit, attr);
+ if ((ret != 2) || (crit < 0) || (crit > 1) || (attr[0] == '\0')) {
+ error_string = talloc_asprintf(mem_ctx, "invalid asq control syntax\n");
+ error_string = talloc_asprintf_append(error_string, " syntax: crit(b):attr(s)\n");
+ error_string = talloc_asprintf_append(error_string, " note: b = boolean, s = string");
+ ldb_set_errstring(ldb, error_string);
+ talloc_free(error_string);
+ return NULL;
+ }
+
+ ctrl[i] = talloc(ctrl, struct ldb_control);
+ if (!ctrl[i]) {
+ ldb_oom(ldb);
+ return NULL;
+ }
+ ctrl[i]->oid = LDB_CONTROL_ASQ_OID;
+ ctrl[i]->critical = crit;
+ control = talloc(ctrl[i], struct ldb_asq_control);
+ control->request = 1;
+ control->source_attribute = talloc_strdup(control, attr);
+ control->src_attr_len = strlen(attr);
+ ctrl[i]->data = control;
+
+ continue;
+ }
+
+ if (strncmp(control_strings[i], "extended_dn:", 12) == 0) {
+ struct ldb_extended_dn_control *control;
+ const char *p;
+ int crit, type, ret;
+
+ p = &(control_strings[i][12]);
+ ret = sscanf(p, "%d:%d", &crit, &type);
+ if ((ret != 2) || (crit < 0) || (crit > 1) || (type < 0) || (type > 1)) {
+ ret = sscanf(p, "%d", &crit);
+ if ((ret != 1) || (crit < 0) || (crit > 1)) {
+ error_string = talloc_asprintf(mem_ctx, "invalid extended_dn control syntax\n");
+ error_string = talloc_asprintf_append(error_string, " syntax: crit(b)[:type(i)]\n");
+ error_string = talloc_asprintf_append(error_string, " note: b = boolean\n");
+ error_string = talloc_asprintf_append(error_string, " i = integer\n");
+ error_string = talloc_asprintf_append(error_string, " valid values are: 0 - hexadecimal representation\n");
+ error_string = talloc_asprintf_append(error_string, " 1 - normal string representation");
+ ldb_set_errstring(ldb, error_string);
+ talloc_free(error_string);
+ return NULL;
+ }
+ control = NULL;
+ } else {
+ control = talloc(ctrl, struct ldb_extended_dn_control);
+ control->type = type;
+ }
+
+ ctrl[i] = talloc(ctrl, struct ldb_control);
+ if (!ctrl[i]) {
+ ldb_oom(ldb);
+ return NULL;
+ }
+ ctrl[i]->oid = LDB_CONTROL_EXTENDED_DN_OID;
+ ctrl[i]->critical = crit;
+ ctrl[i]->data = talloc_steal(ctrl[i], control);
+
+ continue;
+ }
+
+ if (strncmp(control_strings[i], "sd_flags:", 9) == 0) {
+ struct ldb_sd_flags_control *control;
+ const char *p;
+ int crit, ret;
+ unsigned secinfo_flags;
+
+ p = &(control_strings[i][9]);
+ ret = sscanf(p, "%d:%u", &crit, &secinfo_flags);
+ if ((ret != 2) || (crit < 0) || (crit > 1) || (secinfo_flags < 0) || (secinfo_flags > 0xF)) {
+ error_string = talloc_asprintf(mem_ctx, "invalid sd_flags control syntax\n");
+ error_string = talloc_asprintf_append(error_string, " syntax: crit(b):secinfo_flags(n)\n");
+ error_string = talloc_asprintf_append(error_string, " note: b = boolean, n = number");
+ ldb_set_errstring(ldb, error_string);
+ talloc_free(error_string);
+ return NULL;
+ }
+
+ ctrl[i] = talloc(ctrl, struct ldb_control);
+ if (!ctrl[i]) {
+ ldb_oom(ldb);
+ return NULL;
+ }
+ ctrl[i]->oid = LDB_CONTROL_SD_FLAGS_OID;
+ ctrl[i]->critical = crit;
+ control = talloc(ctrl[i], struct ldb_sd_flags_control);
+ control->secinfo_flags = secinfo_flags;
+ ctrl[i]->data = control;
+
+ continue;
+ }
+
+ if (strncmp(control_strings[i], "search_options:", 15) == 0) {
+ struct ldb_search_options_control *control;
+ const char *p;
+ int crit, ret;
+ unsigned search_options;
+
+ p = &(control_strings[i][15]);
+ ret = sscanf(p, "%d:%u", &crit, &search_options);
+ if ((ret != 2) || (crit < 0) || (crit > 1) || (search_options < 0) || (search_options > 0xF)) {
+ error_string = talloc_asprintf(mem_ctx, "invalid search_options control syntax\n");
+ error_string = talloc_asprintf_append(error_string, " syntax: crit(b):search_options(n)\n");
+ error_string = talloc_asprintf_append(error_string, " note: b = boolean, n = number");
+ ldb_set_errstring(ldb, error_string);
+ talloc_free(error_string);
+ return NULL;
+ }
+
+ ctrl[i] = talloc(ctrl, struct ldb_control);
+ if (!ctrl[i]) {
+ ldb_oom(ldb);
+ return NULL;
+ }
+ ctrl[i]->oid = LDB_CONTROL_SEARCH_OPTIONS_OID;
+ ctrl[i]->critical = crit;
+ control = talloc(ctrl[i], struct ldb_search_options_control);
+ control->search_options = search_options;
+ ctrl[i]->data = control;
+
+ continue;
+ }
+
+ if (strncmp(control_strings[i], "domain_scope:", 13) == 0) {
+ const char *p;
+ int crit, ret;
+
+ p = &(control_strings[i][13]);
+ ret = sscanf(p, "%d", &crit);
+ if ((ret != 1) || (crit < 0) || (crit > 1)) {
+ error_string = talloc_asprintf(mem_ctx, "invalid domain_scope control syntax\n");
+ error_string = talloc_asprintf_append(error_string, " syntax: crit(b)\n");
+ error_string = talloc_asprintf_append(error_string, " note: b = boolean");
+ ldb_set_errstring(ldb, error_string);
+ talloc_free(error_string);
+ return NULL;
+ }
+
+ ctrl[i] = talloc(ctrl, struct ldb_control);
+ if (!ctrl[i]) {
+ ldb_oom(ldb);
+ return NULL;
+ }
+ ctrl[i]->oid = LDB_CONTROL_DOMAIN_SCOPE_OID;
+ ctrl[i]->critical = crit;
+ ctrl[i]->data = NULL;
+
+ continue;
+ }
+
+ if (strncmp(control_strings[i], "paged_results:", 14) == 0) {
+ struct ldb_paged_control *control;
+ const char *p;
+ int crit, size, ret;
+
+ p = &(control_strings[i][14]);
+ ret = sscanf(p, "%d:%d", &crit, &size);
+
+ if ((ret != 2) || (crit < 0) || (crit > 1) || (size < 0)) {
+ error_string = talloc_asprintf(mem_ctx, "invalid paged_results control syntax\n");
+ error_string = talloc_asprintf_append(error_string, " syntax: crit(b):size(n)\n");
+ error_string = talloc_asprintf_append(error_string, " note: b = boolean, n = number");
+ ldb_set_errstring(ldb, error_string);
+ talloc_free(error_string);
+ return NULL;
+ }
+
+ ctrl[i] = talloc(ctrl, struct ldb_control);
+ if (!ctrl[i]) {
+ ldb_oom(ldb);
+ return NULL;
+ }
+ ctrl[i]->oid = LDB_CONTROL_PAGED_RESULTS_OID;
+ ctrl[i]->critical = crit;
+ control = talloc(ctrl[i], struct ldb_paged_control);
+ control->size = size;
+ control->cookie = NULL;
+ control->cookie_len = 0;
+ ctrl[i]->data = control;
+
+ continue;
+ }
+
+ if (strncmp(control_strings[i], "server_sort:", 12) == 0) {
+ struct ldb_server_sort_control **control;
+ const char *p;
+ char attr[256];
+ char rule[128];
+ int crit, rev, ret;
+
+ attr[0] = '\0';
+ rule[0] = '\0';
+ p = &(control_strings[i][12]);
+ ret = sscanf(p, "%d:%d:%255[^:]:%127[^:]", &crit, &rev, attr, rule);
+ if ((ret < 3) || (crit < 0) || (crit > 1) || (rev < 0 ) || (rev > 1) ||attr[0] == '\0') {
+ error_string = talloc_asprintf(mem_ctx, "invalid server_sort control syntax\n");
+ error_string = talloc_asprintf_append(error_string, " syntax: crit(b):rev(b):attr(s)[:rule(s)]\n");
+ error_string = talloc_asprintf_append(error_string, " note: b = boolean, s = string");
+ ldb_set_errstring(ldb, error_string);
+ talloc_free(error_string);
+ return NULL;
+ }
+ ctrl[i] = talloc(ctrl, struct ldb_control);
+ if (!ctrl[i]) {
+ ldb_oom(ldb);
+ return NULL;
+ }
+ ctrl[i]->oid = LDB_CONTROL_SERVER_SORT_OID;
+ ctrl[i]->critical = crit;
+ control = talloc_array(ctrl[i], struct ldb_server_sort_control *, 2);
+ control[0] = talloc(control, struct ldb_server_sort_control);
+ control[0]->attributeName = talloc_strdup(control, attr);
+ if (rule[0])
+ control[0]->orderingRule = talloc_strdup(control, rule);
+ else
+ control[0]->orderingRule = NULL;
+ control[0]->reverse = rev;
+ control[1] = NULL;
+ ctrl[i]->data = control;
+
+ continue;
+ }
+
+ if (strncmp(control_strings[i], "notification:", 13) == 0) {
+ const char *p;
+ int crit, ret;
+
+ p = &(control_strings[i][13]);
+ ret = sscanf(p, "%d", &crit);
+ if ((ret != 1) || (crit < 0) || (crit > 1)) {
+ error_string = talloc_asprintf(mem_ctx, "invalid notification control syntax\n");
+ error_string = talloc_asprintf_append(error_string, " syntax: crit(b)\n");
+ error_string = talloc_asprintf_append(error_string, " note: b = boolean");
+ ldb_set_errstring(ldb, error_string);
+ talloc_free(error_string);
+ return NULL;
+ }
+
+ ctrl[i] = talloc(ctrl, struct ldb_control);
+ if (!ctrl[i]) {
+ ldb_oom(ldb);
+ return NULL;
+ }
+ ctrl[i]->oid = LDB_CONTROL_NOTIFICATION_OID;
+ ctrl[i]->critical = crit;
+ ctrl[i]->data = NULL;
+
+ continue;
+ }
+
+ if (strncmp(control_strings[i], "show_deleted:", 13) == 0) {
+ const char *p;
+ int crit, ret;
+
+ p = &(control_strings[i][13]);
+ ret = sscanf(p, "%d", &crit);
+ if ((ret != 1) || (crit < 0) || (crit > 1)) {
+ error_string = talloc_asprintf(mem_ctx, "invalid show_deleted control syntax\n");
+ error_string = talloc_asprintf_append(error_string, " syntax: crit(b)\n");
+ error_string = talloc_asprintf_append(error_string, " note: b = boolean");
+ ldb_set_errstring(ldb, error_string);
+ talloc_free(error_string);
+ return NULL;
+ }
+
+ ctrl[i] = talloc(ctrl, struct ldb_control);
+ if (!ctrl[i]) {
+ ldb_oom(ldb);
+ return NULL;
+ }
+ ctrl[i]->oid = LDB_CONTROL_SHOW_DELETED_OID;
+ ctrl[i]->critical = crit;
+ ctrl[i]->data = NULL;
+
+ continue;
+ }
+
+ if (strncmp(control_strings[i], "permissive_modify:", 18) == 0) {
+ const char *p;
+ int crit, ret;
+
+ p = &(control_strings[i][18]);
+ ret = sscanf(p, "%d", &crit);
+ if ((ret != 1) || (crit < 0) || (crit > 1)) {
+ error_string = talloc_asprintf(mem_ctx, "invalid permissive_modify control syntax\n");
+ error_string = talloc_asprintf_append(error_string, " syntax: crit(b)\n");
+ error_string = talloc_asprintf_append(error_string, " note: b = boolean");
+ ldb_set_errstring(ldb, error_string);
+ talloc_free(error_string);
+ return NULL;
+ }
+
+ ctrl[i] = talloc(ctrl, struct ldb_control);
+ if (!ctrl[i]) {
+ ldb_oom(ldb);
+ return NULL;
+ }
+ ctrl[i]->oid = LDB_CONTROL_PERMISSIVE_MODIFY_OID;
+ ctrl[i]->critical = crit;
+ ctrl[i]->data = NULL;
+
+ continue;
+ }
+
+ /* no controls matched, throw an error */
+ ldb_asprintf_errstring(ldb, "Invalid control name: '%s'", control_strings[i]);
+ return NULL;
+ }
+
+ ctrl[i] = NULL;
+
+ return ctrl;
+}
+
+
diff --git a/source4/lib/ldb/common/ldb_debug.c b/source4/lib/ldb/common/ldb_debug.c
new file mode 100644
index 0000000000..f8009eb8a3
--- /dev/null
+++ b/source4/lib/ldb/common/ldb_debug.c
@@ -0,0 +1,103 @@
+/*
+ ldb database library
+
+ Copyright (C) Andrew Tridgell 2004
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb debug
+ *
+ * Description: functions for printing debug messages
+ *
+ * Author: Andrew Tridgell
+ */
+
+#include "ldb_private.h"
+
+/*
+ this allows the user to choose their own debug function
+*/
+int ldb_set_debug(struct ldb_context *ldb,
+ void (*debug)(void *context, enum ldb_debug_level level,
+ const char *fmt, va_list ap),
+ void *context)
+{
+ ldb->debug_ops.debug = debug;
+ ldb->debug_ops.context = context;
+ return 0;
+}
+
+/*
+ debug function for ldb_set_debug_stderr
+*/
+static void ldb_debug_stderr(void *context, enum ldb_debug_level level,
+ const char *fmt, va_list ap) PRINTF_ATTRIBUTE(3,0);
+static void ldb_debug_stderr(void *context, enum ldb_debug_level level,
+ const char *fmt, va_list ap)
+{
+ if (level <= LDB_DEBUG_WARNING) {
+ vfprintf(stderr, fmt, ap);
+ }
+}
+
+/*
+ convenience function to setup debug messages on stderr
+ messages of level LDB_DEBUG_WARNING and higher are printed
+*/
+int ldb_set_debug_stderr(struct ldb_context *ldb)
+{
+ return ldb_set_debug(ldb, ldb_debug_stderr, ldb);
+}
+
+/*
+ log a message
+*/
+void ldb_debug(struct ldb_context *ldb, enum ldb_debug_level level, const char *fmt, ...)
+{
+ va_list ap;
+ if (ldb->debug_ops.debug == NULL) {
+ ldb_set_debug_stderr(ldb);
+ }
+ va_start(ap, fmt);
+ ldb->debug_ops.debug(ldb->debug_ops.context, level, fmt, ap);
+ va_end(ap);
+}
+
+
+/*
+ log a message, and set the ldb error string to the same message
+*/
+void ldb_debug_set(struct ldb_context *ldb, enum ldb_debug_level level,
+ const char *fmt, ...)
+{
+ va_list ap;
+ char *msg;
+ va_start(ap, fmt);
+ msg = talloc_vasprintf(ldb, fmt, ap);
+ va_end(ap);
+ if (msg != NULL) {
+ ldb_set_errstring(ldb, msg);
+ ldb_debug(ldb, level, "%s", msg);
+ }
+ talloc_free(msg);
+}
+
diff --git a/source4/lib/ldb/common/ldb_dn.c b/source4/lib/ldb/common/ldb_dn.c
new file mode 100644
index 0000000000..402d629501
--- /dev/null
+++ b/source4/lib/ldb/common/ldb_dn.c
@@ -0,0 +1,1800 @@
+/*
+ ldb database library
+
+ Copyright (C) Simo Sorce 2005
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb dn creation and manipulation utility functions
+ *
+ * Description: - explode a dn into it's own basic elements
+ * and put them in a structure (only if necessary)
+ * - manipulate ldb_dn structures
+ *
+ * Author: Simo Sorce
+ */
+
+#include "ldb_private.h"
+#include <ctype.h>
+
+#define LDB_DN_NULL_FAILED(x) if (!(x)) goto failed
+
+#define LDB_FREE(x) do { talloc_free(x); x = NULL; } while(0)
+
+/**
+ internal ldb exploded dn structures
+*/
+struct ldb_dn_component {
+
+ char *name;
+ struct ldb_val value;
+
+ char *cf_name;
+ struct ldb_val cf_value;
+};
+
+struct ldb_dn_extended_component {
+
+ char *name;
+ struct ldb_val value;
+};
+
+struct ldb_dn {
+
+ struct ldb_context *ldb;
+
+ /* Special DNs are always linearized */
+ bool special;
+ bool invalid;
+
+ bool valid_case;
+
+ char *linearized;
+ char *extended_linearized;
+ char *casefold;
+
+ unsigned int comp_num;
+ struct ldb_dn_component *components;
+
+ unsigned int extended_comp_num;
+ struct ldb_dn_extended_component *extended_components;
+};
+
+/* strdn may be NULL */
+struct ldb_dn *ldb_dn_from_ldb_val(void *mem_ctx, struct ldb_context *ldb, const struct ldb_val *strdn)
+{
+ struct ldb_dn *dn;
+
+ if (! ldb) return NULL;
+
+ dn = talloc_zero(mem_ctx, struct ldb_dn);
+ LDB_DN_NULL_FAILED(dn);
+
+ dn->ldb = ldb;
+
+ if (strdn->data && strdn->length) {
+ if (strdn->data[0] == '@') {
+ dn->special = true;
+ }
+ dn->extended_linearized = talloc_strndup(dn, (const char *)strdn->data, strdn->length);
+ LDB_DN_NULL_FAILED(dn->extended_linearized);
+
+ if (strdn->data[0] == '<') {
+ const char *p_save, *p = dn->extended_linearized;
+ do {
+ p_save = p;
+ p = strstr(p, ">;");
+ if (p) {
+ p = p + 2;
+ }
+ } while (p);
+
+ if (p_save == dn->extended_linearized) {
+ dn->linearized = talloc_strdup(dn, "");
+ } else {
+ dn->linearized = talloc_strdup(dn, p_save);
+ }
+ LDB_DN_NULL_FAILED(dn->linearized);
+ } else {
+ dn->linearized = dn->extended_linearized;
+ dn->extended_linearized = NULL;
+ }
+ } else {
+ dn->linearized = talloc_strdup(dn, "");
+ LDB_DN_NULL_FAILED(dn->linearized);
+ }
+
+ return dn;
+
+failed:
+ talloc_free(dn);
+ return NULL;
+}
+
+/* strdn may be NULL */
+struct ldb_dn *ldb_dn_new(void *mem_ctx, struct ldb_context *ldb, const char *strdn)
+{
+ struct ldb_val blob;
+ blob.data = strdn;
+ blob.length = strdn ? strlen(strdn) : 0;
+ return ldb_dn_from_ldb_val(mem_ctx, ldb, &blob);
+}
+
+struct ldb_dn *ldb_dn_new_fmt(void *mem_ctx, struct ldb_context *ldb, const char *new_fmt, ...)
+{
+ char *strdn;
+ va_list ap;
+
+ if ( (! mem_ctx) || (! ldb)) return NULL;
+
+ va_start(ap, new_fmt);
+ strdn = talloc_vasprintf(mem_ctx, new_fmt, ap);
+ va_end(ap);
+
+ if (strdn) {
+ struct ldb_dn *dn = ldb_dn_new(mem_ctx, ldb, strdn);
+ talloc_free(strdn);
+ return dn;
+ }
+
+ return NULL;
+}
+
+static int ldb_dn_escape_internal(char *dst, const char *src, int len)
+{
+ const char *p, *s;
+ char *d;
+ int l;
+
+ p = s = src;
+ d = dst;
+
+ while (p - src < len) {
+
+ p += strcspn(p, ",=\n+<>#;\\\"");
+
+ if (p - src == len) /* found no escapable chars */
+ break;
+
+ memcpy(d, s, p - s); /* copy the part of the string before the stop */
+ d += (p - s); /* move to current position */
+
+ if (*p) { /* it is a normal escapable character */
+ *d++ = '\\';
+ *d++ = *p++;
+ } else { /* we have a zero byte in the string */
+ strncpy(d, "\00", 3); /* escape the zero */
+ d += 3;
+ p++; /* skip the zero */
+ }
+ s = p; /* move forward */
+ }
+
+ /* copy the last part (with zero) and return */
+ l = len - (s - src);
+ memcpy(d, s, l + 1);
+
+ /* return the length of the resulting string */
+ return (l + (d - dst));
+}
+
+char *ldb_dn_escape_value(void *mem_ctx, struct ldb_val value)
+{
+ char *dst;
+
+ if (!value.length)
+ return NULL;
+
+ /* allocate destination string, it will be at most 3 times the source */
+ dst = talloc_array(mem_ctx, char, value.length * 3 + 1);
+ if ( ! dst) {
+ talloc_free(dst);
+ return NULL;
+ }
+
+ ldb_dn_escape_internal(dst, (const char *)value.data, value.length);
+
+ dst = talloc_realloc(mem_ctx, dst, char, strlen(dst) + 1);
+
+ return dst;
+}
+
+/*
+ explode a DN string into a ldb_dn structure
+ based on RFC4514 except that we don't support multiple valued RDNs
+*/
+static bool ldb_dn_explode(struct ldb_dn *dn)
+{
+ char *p, *ex_name, *ex_value, *data, *d, *dt, *t;
+ bool trim = false;
+ bool in_extended = false;
+ bool in_ex_name = false;
+ bool in_ex_value = false;
+ bool in_attr = false;
+ bool in_value = false;
+ bool in_quote = false;
+ bool is_oid = false;
+ bool escape = false;
+ unsigned x;
+ int l, ret;
+ char *parse_dn;
+
+ if ( ! dn || dn->invalid) return false;
+
+ if (dn->components) {
+ return true;
+ }
+
+ if (dn->extended_linearized) {
+ parse_dn = dn->extended_linearized;
+ } else {
+ parse_dn = dn->linearized;
+ }
+
+ if ( ! parse_dn ) {
+ return false;
+ }
+
+ /* Empty DNs */
+ if (parse_dn[0] == '\0') {
+ return true;
+ }
+
+ /* Special DNs case */
+ if (dn->special) {
+ return true;
+ }
+
+ /* make sure we free this if alloced previously before replacing */
+ talloc_free(dn->components);
+
+ talloc_free(dn->extended_components);
+ dn->extended_components = NULL;
+
+ /* in the common case we have 3 or more components */
+ /* make sure all components are zeroed, other functions depend on this */
+ dn->components = talloc_zero_array(dn, struct ldb_dn_component, 3);
+ if ( ! dn->components) {
+ return false;
+ }
+ dn->comp_num = 0;
+
+ /* Components data space is allocated here once */
+ data = talloc_array(dn->components, char, strlen(parse_dn) + 1);
+ if (!data) {
+ return false;
+ }
+
+ p = parse_dn;
+ in_extended = true;
+ in_ex_name = false;
+ in_ex_value = false;
+ trim = true;
+ t = NULL;
+ d = dt = data;
+
+ while (*p) {
+ if (in_extended) {
+
+ if (!in_ex_name && !in_ex_value) {
+
+ if (p[0] == '<') {
+ p++;
+ ex_name = d;
+ in_ex_name = true;
+ continue;
+ } else if (p[0] == '\0') {
+ p++;
+ continue;
+ } else {
+ in_extended = false;
+ in_attr = true;
+ dt = d;
+
+ continue;
+ }
+ }
+
+ if (in_ex_name && *p == '=') {
+ *d++ = '\0';
+ p++;
+ ex_value = d;
+ in_ex_name = false;
+ in_ex_value = true;
+ continue;
+ }
+
+ if (in_ex_value && *p == '>') {
+ const struct ldb_dn_extended_syntax *extended_syntax;
+ struct ldb_val ex_val = {
+ .data = ex_value,
+ .length = d - ex_value
+ };
+
+ *d++ = '\0';
+ p++;
+ in_ex_value = false;
+
+ /* Process name and ex_value */
+
+ dn->extended_components = talloc_realloc(dn,
+ dn->extended_components,
+ struct ldb_dn_extended_component,
+ dn->extended_comp_num + 1);
+ if ( ! dn->extended_components) {
+ /* ouch ! */
+ goto failed;
+ }
+
+ extended_syntax = ldb_dn_extended_syntax_by_name(dn->ldb, ex_name);
+ if (!extended_syntax) {
+ /* We don't know about this type of extended DN */
+ goto failed;
+ }
+
+ dn->extended_components[dn->extended_comp_num].name = talloc_strdup(dn->extended_components, ex_name);
+ if (!dn->extended_components[dn->extended_comp_num].name) {
+ /* ouch */
+ goto failed;
+ }
+ ret = extended_syntax->read_fn(dn->ldb, dn->extended_components,
+ &ex_val, &dn->extended_components[dn->extended_comp_num].value);
+ if (ret != LDB_SUCCESS) {
+ dn->invalid = true;
+ goto failed;
+ }
+
+ dn->extended_comp_num++;
+
+ if (*p == '\0') {
+ /* We have reached the end (extended component only)! */
+ talloc_free(data);
+ return true;
+
+ } else if (*p == ';') {
+ p++;
+ continue;
+ } else {
+ dn->invalid = true;
+ goto failed;
+ }
+ }
+
+ *d++ = *p++;
+ continue;
+ }
+ if (in_attr) {
+ if (trim) {
+ if (*p == ' ') {
+ p++;
+ continue;
+ }
+
+ /* first char */
+ trim = false;
+
+ if (!isascii(*p)) {
+ /* attr names must be ascii only */
+ dn->invalid = true;
+ goto failed;
+ }
+
+ if (isdigit(*p)) {
+ is_oid = true;
+ } else
+ if ( ! isalpha(*p)) {
+ /* not a digit nor an alpha, invalid attribute name */
+ dn->invalid = true;
+ goto failed;
+ }
+
+ /* Copy this character across from parse_dn, now we have trimmed out spaces */
+ *d++ = *p++;
+ continue;
+ }
+
+ if (*p == ' ') {
+ p++;
+ /* valid only if we are at the end */
+ trim = true;
+ continue;
+ }
+
+ if (trim && (*p != '=')) {
+ /* spaces/tabs are not allowed in attribute names */
+ dn->invalid = true;
+ goto failed;
+ }
+
+ if (*p == '=') {
+ /* attribute terminated */
+ in_attr = false;
+ in_value = true;
+ trim = true;
+ l = 0;
+
+ /* Terminate this string in d (which is a copy of parse_dn with spaces trimmed) */
+ *d++ = '\0';
+ dn->components[dn->comp_num].name = talloc_strdup(dn->components, dt);
+ if ( ! dn->components[dn->comp_num].name) {
+ /* ouch */
+ goto failed;
+ }
+
+ dt = d;
+
+ p++;
+ continue;
+ }
+
+ if (!isascii(*p)) {
+ /* attr names must be ascii only */
+ dn->invalid = true;
+ goto failed;
+ }
+
+ if (is_oid && ( ! (isdigit(*p) || (*p == '.')))) {
+ /* not a digit nor a dot, invalid attribute oid */
+ dn->invalid = true;
+ goto failed;
+ } else
+ if ( ! (isalpha(*p) || isdigit(*p) || (*p == '-'))) {
+ /* not ALPHA, DIGIT or HYPHEN */
+ dn->invalid = true;
+ goto failed;
+ }
+
+ *d++ = *p++;
+ continue;
+ }
+
+ if (in_value) {
+ if (in_quote) {
+ if (*p == '\"') {
+ if (p[-1] != '\\') {
+ p++;
+ in_quote = false;
+ continue;
+ }
+ }
+ *d++ = *p++;
+ l++;
+ continue;
+ }
+
+ if (trim) {
+ if (*p == ' ') {
+ p++;
+ continue;
+ }
+
+ /* first char */
+ trim = false;
+
+ if (*p == '\"') {
+ in_quote = true;
+ p++;
+ continue;
+ }
+ }
+
+ switch (*p) {
+
+ /* TODO: support ber encoded values
+ case '#':
+ */
+
+ case ',':
+ if (escape) {
+ *d++ = *p++;
+ l++;
+ escape = false;
+ continue;
+ }
+ /* ok found value terminator */
+
+ if ( t ) {
+ /* trim back */
+ d -= (p - t);
+ l -= (p - t);
+ }
+
+ in_attr = true;
+ in_value = false;
+ trim = true;
+
+ p++;
+ *d++ = '\0';
+ dn->components[dn->comp_num].value.data = (uint8_t *)talloc_strdup(dn->components, dt);
+ dn->components[dn->comp_num].value.length = l;
+ if ( ! dn->components[dn->comp_num].value.data) {
+ /* ouch ! */
+ goto failed;
+ }
+
+ dt = d;
+
+ dn->comp_num++;
+ if (dn->comp_num > 2) {
+ dn->components = talloc_realloc(dn,
+ dn->components,
+ struct ldb_dn_component,
+ dn->comp_num + 1);
+ if ( ! dn->components) {
+ /* ouch ! */
+ goto failed;
+ }
+ /* make sure all components are zeroed, other functions depend on this */
+ memset(&dn->components[dn->comp_num], '\0', sizeof(struct ldb_dn_component));
+ }
+
+ continue;
+
+ case '=':
+ case '\n':
+ case '+':
+ case '<':
+ case '>':
+ case '#':
+ case ';':
+ case '\"':
+ /* a string with not escaped specials is invalid (tested) */
+ if ( ! escape) {
+ dn->invalid = true;
+ goto failed;
+ }
+ escape = false;
+
+ *d++ = *p++;
+ l++;
+
+ if ( t ) t = NULL;
+ break;
+
+ case '\\':
+ if ( ! escape) {
+ escape = true;
+ p++;
+ continue;
+ }
+ escape = false;
+
+ *d++ = *p++;
+ l++;
+
+ if ( t ) t = NULL;
+ break;
+
+ default:
+ if (escape) {
+ if (sscanf(p, "%02x", &x) != 1) {
+ /* invalid escaping sequence */
+ dn->invalid = true;
+ goto failed;
+ }
+ escape = false;
+
+ p += 2;
+ *d++ = (unsigned char)x;
+ l++;
+
+ if ( t ) t = NULL;
+ break;
+ }
+
+ if (*p == ' ') {
+ if ( ! t) t = p;
+ } else {
+ if ( t ) t = NULL;
+ }
+
+ *d++ = *p++;
+ l++;
+
+ break;
+ }
+
+ }
+ }
+
+ if (in_attr || in_quote) {
+ /* invalid dn */
+ dn->invalid = true;
+ goto failed;
+ }
+
+ /* save last element */
+ if ( t ) {
+ /* trim back */
+ d -= (p - t);
+ l -= (p - t);
+ }
+
+ *d++ = '\0';
+ dn->components[dn->comp_num].value.data = (uint8_t *)talloc_strdup(dn->components, dt);
+ dn->components[dn->comp_num].value.length = l;
+
+ if ( ! dn->components[dn->comp_num].value.data) {
+ /* ouch */
+ goto failed;
+ }
+
+ dn->comp_num++;
+
+ talloc_free(data);
+ return true;
+
+failed:
+ dn->comp_num = 0;
+ talloc_free(dn->components);
+ return false;
+}
+
+bool ldb_dn_validate(struct ldb_dn *dn)
+{
+ return ldb_dn_explode(dn);
+}
+
+const char *ldb_dn_get_linearized(struct ldb_dn *dn)
+{
+ int i, len;
+ char *d, *n;
+
+ if ( ! dn || ( dn->invalid)) return NULL;
+
+ if (dn->linearized) return dn->linearized;
+
+ if ( ! dn->components) {
+ dn->invalid = true;
+ return NULL;
+ }
+
+ if (dn->comp_num == 0) {
+ dn->linearized = talloc_strdup(dn, "");
+ if ( ! dn->linearized) return NULL;
+ return dn->linearized;
+ }
+
+ /* calculate maximum possible length of DN */
+ for (len = 0, i = 0; i < dn->comp_num; i++) {
+ len += strlen(dn->components[i].name); /* name len */
+ len += (dn->components[i].value.length * 3); /* max escaped data len */
+ len += 2; /* '=' and ',' */
+ }
+ dn->linearized = talloc_array(dn, char, len);
+ if ( ! dn->linearized) return NULL;
+
+ d = dn->linearized;
+
+ for (i = 0; i < dn->comp_num; i++) {
+
+ /* copy the name */
+ n = dn->components[i].name;
+ while (*n) *d++ = *n++;
+
+ *d++ = '=';
+
+ /* and the value */
+ d += ldb_dn_escape_internal( d,
+ (char *)dn->components[i].value.data,
+ dn->components[i].value.length);
+ *d++ = ',';
+ }
+
+ *(--d) = '\0';
+
+ /* don't waste more memory than necessary */
+ dn->linearized = talloc_realloc(dn, dn->linearized, char, (d - dn->linearized + 1));
+
+ return dn->linearized;
+}
+
+char *ldb_dn_get_extended_linearized(void *mem_ctx, struct ldb_dn *dn, int mode)
+{
+ const char *linearized = ldb_dn_get_linearized(dn);
+ char *p;
+ int i;
+
+ if (!linearized) {
+ return NULL;
+ }
+
+ if (!ldb_dn_has_extended(dn)) {
+ return talloc_strdup(mem_ctx, linearized);
+ }
+
+ if (!ldb_dn_validate(dn)) {
+ return NULL;
+ }
+
+ for (i=0; i < dn->extended_comp_num; i++) {
+ struct ldb_val val;
+ int ret;
+ const struct ldb_dn_extended_syntax *extended_syntax;
+ const char *name = dn->extended_components[i].name;
+
+ extended_syntax = ldb_dn_extended_syntax_by_name(dn->ldb, name);
+
+ if (mode == 1) {
+ ret = extended_syntax->write_clear_fn(dn->ldb, mem_ctx,
+ &dn->extended_components[i].value,
+ &val);
+ } else if (mode == 0) {
+ ret = extended_syntax->write_hex_fn(dn->ldb, mem_ctx,
+ &dn->extended_components[i].value,
+ &val);
+ } else {
+ ret = -1;
+ }
+
+ if (ret != LDB_SUCCESS) {
+ return NULL;
+ }
+
+ if (i == 0) {
+ p = talloc_asprintf(mem_ctx, "<%s=%s>", dn->extended_components[i].name, val.data);
+ } else {
+ p = talloc_asprintf_append(p, ";<%s=%s>", dn->extended_components[i].name, val.data);
+ }
+
+ talloc_free(val.data);
+
+ if (!p) {
+ return NULL;
+ }
+ }
+
+ if (dn->extended_comp_num && *linearized) {
+ p = talloc_asprintf_append(p, ";%s", linearized);
+ }
+
+ if (!p) {
+ return NULL;
+ }
+
+ return p;
+}
+
+
+
+char *ldb_dn_alloc_linearized(void *mem_ctx, struct ldb_dn *dn)
+{
+ return talloc_strdup(mem_ctx, ldb_dn_get_linearized(dn));
+}
+
+/*
+ casefold a dn. We need to casefold the attribute names, and canonicalize
+ attribute values of case insensitive attributes.
+*/
+
+static bool ldb_dn_casefold_internal(struct ldb_dn *dn)
+{
+ int i, ret;
+
+ if ( ! dn || dn->invalid) return false;
+
+ if (dn->valid_case) return true;
+
+ if (( ! dn->components) && ( ! ldb_dn_explode(dn))) {
+ return false;
+ }
+
+ for (i = 0; i < dn->comp_num; i++) {
+ const struct ldb_schema_attribute *a;
+
+ dn->components[i].cf_name = ldb_attr_casefold(dn->components, dn->components[i].name);
+ if (!dn->components[i].cf_name) {
+ goto failed;
+ }
+
+ a = ldb_schema_attribute_by_name(dn->ldb, dn->components[i].cf_name);
+ ret = a->syntax->canonicalise_fn(dn->ldb, dn->components,
+ &(dn->components[i].value),
+ &(dn->components[i].cf_value));
+ if (ret != 0) {
+ goto failed;
+ }
+ }
+
+ dn->valid_case = true;
+
+ return true;
+
+failed:
+ for (i = 0; i < dn->comp_num; i++) {
+ LDB_FREE(dn->components[i].cf_name);
+ LDB_FREE(dn->components[i].cf_value.data);
+ }
+ return false;
+}
+
+const char *ldb_dn_get_casefold(struct ldb_dn *dn)
+{
+ int i, len;
+ char *d, *n;
+
+ if (dn->casefold) return dn->casefold;
+
+ if (dn->special) {
+ dn->casefold = talloc_strdup(dn, dn->linearized);
+ if (!dn->casefold) return NULL;
+ dn->valid_case = true;
+ return dn->casefold;
+ }
+
+ if ( ! ldb_dn_casefold_internal(dn)) {
+ return NULL;
+ }
+
+ if (dn->comp_num == 0) {
+ if (dn->linearized && dn->linearized[0] == '\0') {
+ /* hmm a NULL dn, should we faild casefolding ? */
+ dn->casefold = talloc_strdup(dn, "");
+ return dn->casefold;
+ }
+ /* A DN must be NULL, special, or have components */
+ dn->invalid = true;
+ return NULL;
+ }
+
+ /* calculate maximum possible length of DN */
+ for (len = 0, i = 0; i < dn->comp_num; i++) {
+ len += strlen(dn->components[i].cf_name); /* name len */
+ len += (dn->components[i].cf_value.length * 3); /* max escaped data len */
+ len += 2; /* '=' and ',' */
+ }
+ dn->casefold = talloc_array(dn, char, len);
+ if ( ! dn->casefold) return NULL;
+
+ d = dn->casefold;
+
+ for (i = 0; i < dn->comp_num; i++) {
+
+ /* copy the name */
+ n = dn->components[i].cf_name;
+ while (*n) *d++ = *n++;
+
+ *d++ = '=';
+
+ /* and the value */
+ d += ldb_dn_escape_internal( d,
+ (char *)dn->components[i].cf_value.data,
+ dn->components[i].cf_value.length);
+ *d++ = ',';
+ }
+ *(--d) = '\0';
+
+ /* don't waste more memory than necessary */
+ dn->casefold = talloc_realloc(dn, dn->casefold, char, strlen(dn->casefold) + 1);
+
+ return dn->casefold;
+}
+
+char *ldb_dn_alloc_casefold(void *mem_ctx, struct ldb_dn *dn)
+{
+ return talloc_strdup(mem_ctx, ldb_dn_get_casefold(dn));
+}
+
+/* Determine if dn is below base, in the ldap tree. Used for
+ * evaluating a subtree search.
+ * 0 if they match, otherwise non-zero
+ */
+
+int ldb_dn_compare_base(struct ldb_dn *base, struct ldb_dn *dn)
+{
+ int ret;
+ int n_base, n_dn;
+
+ if ( ! base || base->invalid) return 1;
+ if ( ! dn || dn->invalid) return -1;
+
+ if (( ! base->valid_case) || ( ! dn->valid_case)) {
+ if (base->linearized && dn->linearized) {
+ /* try with a normal compare first, if we are lucky
+ * we will avoid exploding and casfolding */
+ int dif;
+ dif = strlen(dn->linearized) - strlen(base->linearized);
+ if (dif < 0) return dif;
+ if (strcmp(base->linearized, &dn->linearized[dif]) == 0) return 0;
+ }
+
+ if ( ! ldb_dn_casefold_internal(base)) {
+ return 1;
+ }
+
+ if ( ! ldb_dn_casefold_internal(dn)) {
+ return -1;
+ }
+
+ }
+
+ /* if base has more components,
+ * they don't have the same base */
+ if (base->comp_num > dn->comp_num) {
+ return (dn->comp_num - base->comp_num);
+ }
+
+ if (dn->comp_num == 0) {
+ if (dn->special && base->special) {
+ return strcmp(base->linearized, dn->linearized);
+ } else if (dn->special) {
+ return -1;
+ } else if (base->special) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+
+ n_base = base->comp_num - 1;
+ n_dn = dn->comp_num - 1;
+
+ while (n_base >= 0) {
+ /* compare attr names */
+ ret = strcmp(base->components[n_base].cf_name, dn->components[n_dn].cf_name);
+ if (ret != 0) return ret;
+
+ /* compare attr.cf_value. */
+ if (base->components[n_base].cf_value.length != dn->components[n_dn].cf_value.length) {
+ return base->components[n_base].cf_value.length - dn->components[n_dn].cf_value.length;
+ }
+ ret = strcmp((char *)base->components[n_base].cf_value.data, (char *)dn->components[n_dn].cf_value.data);
+ if (ret != 0) return ret;
+
+ n_base--;
+ n_dn--;
+ }
+
+ return 0;
+}
+
+/* compare DNs using casefolding compare functions.
+
+ If they match, then return 0
+ */
+
+int ldb_dn_compare(struct ldb_dn *dn0, struct ldb_dn *dn1)
+{
+ int i, ret;
+
+ if (( ! dn0) || dn0->invalid || ! dn1 || dn1->invalid) return -1;
+
+ if (( ! dn0->valid_case) || ( ! dn1->valid_case)) {
+ if (dn0->linearized && dn1->linearized) {
+ /* try with a normal compare first, if we are lucky
+ * we will avoid exploding and casfolding */
+ if (strcmp(dn0->linearized, dn1->linearized) == 0) return 0;
+ }
+
+ if ( ! ldb_dn_casefold_internal(dn0)) {
+ return 1;
+ }
+
+ if ( ! ldb_dn_casefold_internal(dn1)) {
+ return -1;
+ }
+
+ }
+
+ if (dn0->comp_num != dn1->comp_num) {
+ return (dn1->comp_num - dn0->comp_num);
+ }
+
+ if (dn0->comp_num == 0) {
+ if (dn0->special && dn1->special) {
+ return strcmp(dn0->linearized, dn1->linearized);
+ } else if (dn0->special) {
+ return 1;
+ } else if (dn1->special) {
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+
+ for (i = 0; i < dn0->comp_num; i++) {
+ /* compare attr names */
+ ret = strcmp(dn0->components[i].cf_name, dn1->components[i].cf_name);
+ if (ret != 0) return ret;
+
+ /* compare attr.cf_value. */
+ if (dn0->components[i].cf_value.length != dn1->components[i].cf_value.length) {
+ return dn0->components[i].cf_value.length - dn1->components[i].cf_value.length;
+ }
+ ret = strcmp((char *)dn0->components[i].cf_value.data, (char *)dn1->components[i].cf_value.data);
+ if (ret != 0) return ret;
+ }
+
+ return 0;
+}
+
+static struct ldb_dn_component ldb_dn_copy_component(void *mem_ctx, struct ldb_dn_component *src)
+{
+ struct ldb_dn_component dst;
+
+ memset(&dst, 0, sizeof(dst));
+
+ if (src == NULL) {
+ return dst;
+ }
+
+ dst.value = ldb_val_dup(mem_ctx, &(src->value));
+ if (dst.value.data == NULL) {
+ return dst;
+ }
+
+ dst.name = talloc_strdup(mem_ctx, src->name);
+ if (dst.name == NULL) {
+ LDB_FREE(dst.value.data);
+ return dst;
+ }
+
+ if (src->cf_value.data) {
+ dst.cf_value = ldb_val_dup(mem_ctx, &(src->cf_value));
+ if (dst.cf_value.data == NULL) {
+ LDB_FREE(dst.value.data);
+ LDB_FREE(dst.name);
+ return dst;
+ }
+
+ dst.cf_name = talloc_strdup(mem_ctx, src->cf_name);
+ if (dst.cf_name == NULL) {
+ LDB_FREE(dst.cf_name);
+ LDB_FREE(dst.value.data);
+ LDB_FREE(dst.name);
+ return dst;
+ }
+ } else {
+ dst.cf_value.data = NULL;
+ dst.cf_name = NULL;
+ }
+
+ return dst;
+}
+
+static struct ldb_dn_extended_component ldb_dn_extended_copy_component(void *mem_ctx, struct ldb_dn_extended_component *src)
+{
+ struct ldb_dn_extended_component dst;
+
+ memset(&dst, 0, sizeof(dst));
+
+ if (src == NULL) {
+ return dst;
+ }
+
+ dst.value = ldb_val_dup(mem_ctx, &(src->value));
+ if (dst.value.data == NULL) {
+ return dst;
+ }
+
+ dst.name = talloc_strdup(mem_ctx, src->name);
+ if (dst.name == NULL) {
+ LDB_FREE(dst.value.data);
+ return dst;
+ }
+
+ return dst;
+}
+
+struct ldb_dn *ldb_dn_copy(void *mem_ctx, struct ldb_dn *dn)
+{
+ struct ldb_dn *new_dn;
+
+ if (!dn || dn->invalid) {
+ return NULL;
+ }
+
+ new_dn = talloc_zero(mem_ctx, struct ldb_dn);
+ if ( !new_dn) {
+ return NULL;
+ }
+
+ *new_dn = *dn;
+
+ if (dn->components) {
+ int i;
+
+ new_dn->components = talloc_zero_array(new_dn, struct ldb_dn_component, dn->comp_num);
+ if ( ! new_dn->components) {
+ talloc_free(new_dn);
+ return NULL;
+ }
+
+ for (i = 0; i < dn->comp_num; i++) {
+ new_dn->components[i] = ldb_dn_copy_component(new_dn->components, &dn->components[i]);
+ if ( ! new_dn->components[i].value.data) {
+ talloc_free(new_dn);
+ return NULL;
+ }
+ }
+ }
+
+ if (dn->extended_components) {
+ int i;
+
+ new_dn->extended_components = talloc_zero_array(new_dn, struct ldb_dn_extended_component, dn->extended_comp_num);
+ if ( ! new_dn->extended_components) {
+ talloc_free(new_dn);
+ return NULL;
+ }
+
+ for (i = 0; i < dn->extended_comp_num; i++) {
+ new_dn->extended_components[i] = ldb_dn_extended_copy_component(new_dn->extended_components, &dn->extended_components[i]);
+ if ( ! new_dn->extended_components[i].value.data) {
+ talloc_free(new_dn);
+ return NULL;
+ }
+ }
+ }
+
+ if (dn->casefold) {
+ new_dn->casefold = talloc_strdup(new_dn, dn->casefold);
+ if ( ! new_dn->casefold) {
+ talloc_free(new_dn);
+ return NULL;
+ }
+ }
+
+ if (dn->linearized) {
+ new_dn->linearized = talloc_strdup(new_dn, dn->linearized);
+ if ( ! new_dn->linearized) {
+ talloc_free(new_dn);
+ return NULL;
+ }
+ }
+
+ if (dn->extended_linearized) {
+ new_dn->extended_linearized = talloc_strdup(new_dn, dn->extended_linearized);
+ if ( ! new_dn->extended_linearized) {
+ talloc_free(new_dn);
+ return NULL;
+ }
+ }
+
+ return new_dn;
+}
+
+/* modify the given dn by adding a base.
+ *
+ * return true if successful and false if not
+ * if false is returned the dn may be marked invalid
+ */
+bool ldb_dn_add_base(struct ldb_dn *dn, struct ldb_dn *base)
+{
+ const char *s;
+ char *t;
+
+ if ( !base || base->invalid || !dn || dn->invalid) {
+ return false;
+ }
+
+ if (dn->components) {
+ int i;
+
+ if ( ! ldb_dn_validate(base)) {
+ return false;
+ }
+
+ s = NULL;
+ if (dn->valid_case) {
+ if ( ! (s = ldb_dn_get_casefold(base))) {
+ return false;
+ }
+ }
+
+ dn->components = talloc_realloc(dn,
+ dn->components,
+ struct ldb_dn_component,
+ dn->comp_num + base->comp_num);
+ if ( ! dn->components) {
+ dn->invalid = true;
+ return false;
+ }
+
+ for (i = 0; i < base->comp_num; dn->comp_num++, i++) {
+ dn->components[dn->comp_num] = ldb_dn_copy_component(dn->components, &base->components[i]);
+ if (dn->components[dn->comp_num].value.data == NULL) {
+ dn->invalid = true;
+ return false;
+ }
+ }
+
+ if (dn->casefold && s) {
+ if (*dn->casefold) {
+ t = talloc_asprintf(dn, "%s,%s", dn->casefold, s);
+ } else {
+ t = talloc_strdup(dn, s);
+ }
+ LDB_FREE(dn->casefold);
+ dn->casefold = t;
+ }
+ }
+
+ if (dn->linearized) {
+
+ s = ldb_dn_get_linearized(base);
+ if ( ! s) {
+ return false;
+ }
+
+ if (*dn->linearized) {
+ t = talloc_asprintf(dn, "%s,%s", dn->linearized, s);
+ } else {
+ t = talloc_strdup(dn, s);
+ }
+ if ( ! t) {
+ dn->invalid = true;
+ return false;
+ }
+ LDB_FREE(dn->linearized);
+ dn->linearized = t;
+ }
+
+ /* Wipe the extended_linearized DN, as the GUID and SID are almost certainly no longer valid */
+ if (dn->extended_linearized) {
+ LDB_FREE(dn->extended_linearized);
+ }
+
+ LDB_FREE(dn->extended_components);
+ dn->extended_comp_num = 0;
+ return true;
+}
+
+/* modify the given dn by adding a base.
+ *
+ * return true if successful and false if not
+ * if false is returned the dn may be marked invalid
+ */
+bool ldb_dn_add_base_fmt(struct ldb_dn *dn, const char *base_fmt, ...)
+{
+ struct ldb_dn *base;
+ char *base_str;
+ va_list ap;
+ bool ret;
+
+ if ( !dn || dn->invalid) {
+ return false;
+ }
+
+ va_start(ap, base_fmt);
+ base_str = talloc_vasprintf(dn, base_fmt, ap);
+ va_end(ap);
+
+ if (base_str == NULL) {
+ return false;
+ }
+
+ base = ldb_dn_new(base_str, dn->ldb, base_str);
+
+ ret = ldb_dn_add_base(dn, base);
+
+ talloc_free(base_str);
+
+ return ret;
+}
+
+/* modify the given dn by adding children elements.
+ *
+ * return true if successful and false if not
+ * if false is returned the dn may be marked invalid
+ */
+bool ldb_dn_add_child(struct ldb_dn *dn, struct ldb_dn *child)
+{
+ const char *s;
+ char *t;
+
+ if ( !child || child->invalid || !dn || dn->invalid) {
+ return false;
+ }
+
+ if (dn->components) {
+ int n, i, j;
+
+ if ( ! ldb_dn_validate(child)) {
+ return false;
+ }
+
+ s = NULL;
+ if (dn->valid_case) {
+ if ( ! (s = ldb_dn_get_casefold(child))) {
+ return false;
+ }
+ }
+
+ n = dn->comp_num + child->comp_num;
+
+ dn->components = talloc_realloc(dn,
+ dn->components,
+ struct ldb_dn_component,
+ n);
+ if ( ! dn->components) {
+ dn->invalid = true;
+ return false;
+ }
+
+ for (i = dn->comp_num - 1, j = n - 1; i >= 0; i--, j--) {
+ dn->components[j] = dn->components[i];
+ }
+
+ for (i = 0; i < child->comp_num; i++) {
+ dn->components[i] = ldb_dn_copy_component(dn->components, &child->components[i]);
+ if (dn->components[i].value.data == NULL) {
+ dn->invalid = true;
+ return false;
+ }
+ }
+
+ dn->comp_num = n;
+
+ if (dn->casefold && s) {
+ t = talloc_asprintf(dn, "%s,%s", s, dn->casefold);
+ LDB_FREE(dn->casefold);
+ dn->casefold = t;
+ }
+ }
+
+ if (dn->linearized) {
+
+ s = ldb_dn_get_linearized(child);
+ if ( ! s) {
+ return false;
+ }
+
+ t = talloc_asprintf(dn, "%s,%s", s, dn->linearized);
+ if ( ! t) {
+ dn->invalid = true;
+ return false;
+ }
+ LDB_FREE(dn->linearized);
+ dn->linearized = t;
+ }
+
+ /* Wipe the extended_linearized DN, as the GUID and SID are almost certainly no longer valid */
+ LDB_FREE(dn->extended_linearized);
+
+ LDB_FREE(dn->extended_components);
+ dn->extended_comp_num = 0;
+
+ return true;
+}
+
+/* modify the given dn by adding children elements.
+ *
+ * return true if successful and false if not
+ * if false is returned the dn may be marked invalid
+ */
+bool ldb_dn_add_child_fmt(struct ldb_dn *dn, const char *child_fmt, ...)
+{
+ struct ldb_dn *child;
+ char *child_str;
+ va_list ap;
+ bool ret;
+
+ if ( !dn || dn->invalid) {
+ return false;
+ }
+
+ va_start(ap, child_fmt);
+ child_str = talloc_vasprintf(dn, child_fmt, ap);
+ va_end(ap);
+
+ if (child_str == NULL) {
+ return false;
+ }
+
+ child = ldb_dn_new(child_str, dn->ldb, child_str);
+
+ ret = ldb_dn_add_child(dn, child);
+
+ talloc_free(child_str);
+
+ return ret;
+}
+
+bool ldb_dn_remove_base_components(struct ldb_dn *dn, unsigned int num)
+{
+ int i;
+
+ if ( ! ldb_dn_validate(dn)) {
+ return false;
+ }
+
+ if (dn->comp_num < num) {
+ return false;
+ }
+
+ /* free components */
+ for (i = num; i > 0; i--) {
+ LDB_FREE(dn->components[dn->comp_num - i].name);
+ LDB_FREE(dn->components[dn->comp_num - i].value.data);
+ LDB_FREE(dn->components[dn->comp_num - i].cf_name);
+ LDB_FREE(dn->components[dn->comp_num - i].cf_value.data);
+ }
+
+ dn->comp_num -= num;
+
+ if (dn->valid_case) {
+ for (i = 0; i < dn->comp_num; i++) {
+ LDB_FREE(dn->components[i].cf_name);
+ LDB_FREE(dn->components[i].cf_value.data);
+ }
+ dn->valid_case = false;
+ }
+
+ LDB_FREE(dn->casefold);
+ LDB_FREE(dn->linearized);
+
+ /* Wipe the extended_linearized DN, as the GUID and SID are almost certainly no longer valid */
+ LDB_FREE(dn->extended_linearized);
+
+ LDB_FREE(dn->extended_components);
+ dn->extended_comp_num = 0;
+
+ return true;
+}
+
+bool ldb_dn_remove_child_components(struct ldb_dn *dn, unsigned int num)
+{
+ int i, j;
+
+ if ( ! ldb_dn_validate(dn)) {
+ return false;
+ }
+
+ if (dn->comp_num < num) {
+ return false;
+ }
+
+ for (i = 0, j = num; j < dn->comp_num; i++, j++) {
+ if (i < num) {
+ LDB_FREE(dn->components[i].name);
+ LDB_FREE(dn->components[i].value.data);
+ LDB_FREE(dn->components[i].cf_name);
+ LDB_FREE(dn->components[i].cf_value.data);
+ }
+ dn->components[i] = dn->components[j];
+ }
+
+ dn->comp_num -= num;
+
+ if (dn->valid_case) {
+ for (i = 0; i < dn->comp_num; i++) {
+ LDB_FREE(dn->components[i].cf_name);
+ LDB_FREE(dn->components[i].cf_value.data);
+ }
+ dn->valid_case = false;
+ }
+
+ LDB_FREE(dn->casefold);
+ LDB_FREE(dn->linearized);
+
+ /* Wipe the extended_linearized DN, as the GUID and SID are almost certainly no longer valid */
+ LDB_FREE(dn->extended_linearized);
+
+ LDB_FREE(dn->extended_components);
+ dn->extended_comp_num = 0;
+ return true;
+}
+
+struct ldb_dn *ldb_dn_get_parent(void *mem_ctx, struct ldb_dn *dn)
+{
+ struct ldb_dn *new_dn;
+
+ new_dn = ldb_dn_copy(mem_ctx, dn);
+ if ( !new_dn ) {
+ return NULL;
+ }
+
+ if ( ! ldb_dn_remove_child_components(new_dn, 1)) {
+ talloc_free(new_dn);
+ return NULL;
+ }
+
+ /* Wipe the extended_linearized DN, as the GUID and SID are almost certainly no longer valid */
+ LDB_FREE(dn->extended_linearized);
+
+ LDB_FREE(dn->extended_components);
+ dn->extended_comp_num = 0;
+ return new_dn;
+}
+
+/* Create a 'canonical name' string from a DN:
+
+ ie dc=samba,dc=org -> samba.org/
+ uid=administrator,ou=users,dc=samba,dc=org = samba.org/users/administrator
+
+ There are two formats, the EX format has the last / replaced with a newline (\n).
+
+*/
+static char *ldb_dn_canonical(void *mem_ctx, struct ldb_dn *dn, int ex_format) {
+ int i;
+ TALLOC_CTX *tmpctx;
+ char *cracked = NULL;
+ const char *format = (ex_format ? "\n" : "/" );
+
+ if ( ! ldb_dn_validate(dn)) {
+ return NULL;
+ }
+
+ tmpctx = talloc_new(mem_ctx);
+
+ /* Walk backwards down the DN, grabbing 'dc' components at first */
+ for (i = dn->comp_num - 1 ; i >= 0; i--) {
+ if (ldb_attr_cmp(dn->components[i].name, "dc") != 0) {
+ break;
+ }
+ if (cracked) {
+ cracked = talloc_asprintf(tmpctx, "%s.%s",
+ ldb_dn_escape_value(tmpctx, dn->components[i].value),
+ cracked);
+ } else {
+ cracked = ldb_dn_escape_value(tmpctx, dn->components[i].value);
+ }
+ if (!cracked) {
+ goto done;
+ }
+ }
+
+ /* Only domain components? Finish here */
+ if (i < 0) {
+ cracked = talloc_strdup_append_buffer(cracked, format);
+ talloc_steal(mem_ctx, cracked);
+ goto done;
+ }
+
+ /* Now walk backwards appending remaining components */
+ for (; i > 0; i--) {
+ cracked = talloc_asprintf_append_buffer(cracked, "/%s",
+ ldb_dn_escape_value(tmpctx, dn->components[i].value));
+ if (!cracked) {
+ goto done;
+ }
+ }
+
+ /* Last one, possibly a newline for the 'ex' format */
+ cracked = talloc_asprintf_append_buffer(cracked, "%s%s", format,
+ ldb_dn_escape_value(tmpctx, dn->components[i].value));
+
+ talloc_steal(mem_ctx, cracked);
+done:
+ talloc_free(tmpctx);
+ return cracked;
+}
+
+/* Wrapper functions for the above, for the two different string formats */
+char *ldb_dn_canonical_string(void *mem_ctx, struct ldb_dn *dn) {
+ return ldb_dn_canonical(mem_ctx, dn, 0);
+
+}
+
+char *ldb_dn_canonical_ex_string(void *mem_ctx, struct ldb_dn *dn) {
+ return ldb_dn_canonical(mem_ctx, dn, 1);
+}
+
+int ldb_dn_get_comp_num(struct ldb_dn *dn)
+{
+ if ( ! ldb_dn_validate(dn)) {
+ return -1;
+ }
+ return dn->comp_num;
+}
+
+const char *ldb_dn_get_component_name(struct ldb_dn *dn, unsigned int num)
+{
+ if ( ! ldb_dn_validate(dn)) {
+ return NULL;
+ }
+ if (num >= dn->comp_num) return NULL;
+ return dn->components[num].name;
+}
+
+const struct ldb_val *ldb_dn_get_component_val(struct ldb_dn *dn, unsigned int num)
+{
+ if ( ! ldb_dn_validate(dn)) {
+ return NULL;
+ }
+ if (num >= dn->comp_num) return NULL;
+ return &dn->components[num].value;
+}
+
+const char *ldb_dn_get_rdn_name(struct ldb_dn *dn)
+{
+ if ( ! ldb_dn_validate(dn)) {
+ return NULL;
+ }
+ if (dn->comp_num == 0) return NULL;
+ return dn->components[0].name;
+}
+
+const struct ldb_val *ldb_dn_get_rdn_val(struct ldb_dn *dn)
+{
+ if ( ! ldb_dn_validate(dn)) {
+ return NULL;
+ }
+ if (dn->comp_num == 0) return NULL;
+ return &dn->components[0].value;
+}
+
+int ldb_dn_set_component(struct ldb_dn *dn, int num, const char *name, const struct ldb_val val)
+{
+ char *n;
+ struct ldb_val v;
+
+ if ( ! ldb_dn_validate(dn)) {
+ return LDB_ERR_OTHER;
+ }
+
+ if (num >= dn->comp_num) {
+ return LDB_ERR_OTHER;
+ }
+
+ n = talloc_strdup(dn, name);
+ if ( ! n) {
+ return LDB_ERR_OTHER;
+ }
+
+ v.length = val.length;
+ v.data = (uint8_t *)talloc_memdup(dn, val.data, v.length+1);
+ if ( ! v.data) {
+ talloc_free(n);
+ return LDB_ERR_OTHER;
+ }
+
+ talloc_free(dn->components[num].name);
+ talloc_free(dn->components[num].value.data);
+ dn->components[num].name = n;
+ dn->components[num].value = v;
+
+ if (dn->valid_case) {
+ int i;
+ for (i = 0; i < dn->comp_num; i++) {
+ LDB_FREE(dn->components[i].cf_name);
+ LDB_FREE(dn->components[i].cf_value.data);
+ }
+ dn->valid_case = false;
+ }
+ LDB_FREE(dn->casefold);
+ LDB_FREE(dn->linearized);
+
+ /* Wipe the extended_linearized DN, as the GUID and SID are almost certainly no longer valid */
+ LDB_FREE(dn->extended_linearized);
+
+ dn->extended_comp_num = 0;
+ LDB_FREE(dn->extended_components);
+ return LDB_SUCCESS;
+}
+
+const struct ldb_val *ldb_dn_get_extended_component(struct ldb_dn *dn, const char *name)
+{
+ int i;
+ if ( ! ldb_dn_validate(dn)) {
+ return NULL;
+ }
+ for (i=0; i < dn->extended_comp_num; i++) {
+ if (ldb_attr_cmp(dn->extended_components[i].name, name) == 0) {
+ return &dn->extended_components[i].value;
+ }
+ }
+ return NULL;
+}
+
+int ldb_dn_set_extended_component(struct ldb_dn *dn, const char *name, const struct ldb_val *val)
+{
+ struct ldb_dn_extended_component *p;
+ int i;
+
+ if ( ! ldb_dn_validate(dn)) {
+ return LDB_ERR_OTHER;
+ }
+
+ for (i=0; i < dn->extended_comp_num; i++) {
+ if (ldb_attr_cmp(dn->extended_components[i].name, name) == 0) {
+ if (val) {
+ dn->extended_components[i].value = ldb_val_dup(dn->extended_components, val);
+
+ dn->extended_components[i].name = talloc_strdup(dn->extended_components, name);
+ if (!dn->extended_components[i].name || !dn->extended_components[i].value.data) {
+ dn->invalid = true;
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ } else {
+ if (i != (dn->extended_comp_num - 1)) {
+ memmove(&dn->extended_components[i], &dn->extended_components[i+1],
+ ((dn->extended_comp_num-1) - i)*sizeof(*dn->extended_components));
+ }
+ dn->extended_comp_num--;
+
+ dn->extended_components = talloc_realloc(dn,
+ dn->extended_components,
+ struct ldb_dn_extended_component,
+ dn->extended_comp_num);
+ if (!dn->extended_components) {
+ dn->invalid = true;
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ return LDB_SUCCESS;
+ }
+ }
+ }
+
+ p = dn->extended_components
+ = talloc_realloc(dn,
+ dn->extended_components,
+ struct ldb_dn_extended_component,
+ dn->extended_comp_num + 1);
+ if (!dn->extended_components) {
+ dn->invalid = true;
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ p[dn->extended_comp_num].value = ldb_val_dup(dn->extended_components, val);
+ p[dn->extended_comp_num].name = talloc_strdup(p, name);
+
+ if (!dn->extended_components[i].name || !dn->extended_components[i].value.data) {
+ dn->invalid = true;
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ dn->extended_components = p;
+ dn->extended_comp_num++;
+
+ return LDB_SUCCESS;
+}
+
+void ldb_dn_remove_extended_components(struct ldb_dn *dn)
+{
+ dn->extended_comp_num = 0;
+ LDB_FREE(dn->extended_components);
+}
+
+bool ldb_dn_is_valid(struct ldb_dn *dn)
+{
+ if ( ! dn) return false;
+ return ! dn->invalid;
+}
+
+bool ldb_dn_is_special(struct ldb_dn *dn)
+{
+ if ( ! dn || dn->invalid) return false;
+ return dn->special;
+}
+
+bool ldb_dn_has_extended(struct ldb_dn *dn)
+{
+ if ( ! dn || dn->invalid) return false;
+ if (dn->extended_linearized && (dn->extended_linearized[0] == '<')) return true;
+ return dn->extended_comp_num != 0;
+}
+
+bool ldb_dn_check_special(struct ldb_dn *dn, const char *check)
+{
+ if ( ! dn || dn->invalid) return false;
+ return ! strcmp(dn->linearized, check);
+}
+
+bool ldb_dn_is_null(struct ldb_dn *dn)
+{
+ if ( ! dn || dn->invalid) return false;
+ if (ldb_dn_has_extended(dn)) return false;
+ if (dn->linearized && (dn->linearized[0] == '\0')) return true;
+ return false;
+}
diff --git a/source4/lib/ldb/common/ldb_ldif.c b/source4/lib/ldb/common/ldb_ldif.c
new file mode 100644
index 0000000000..400fb352ff
--- /dev/null
+++ b/source4/lib/ldb/common/ldb_ldif.c
@@ -0,0 +1,761 @@
+/*
+ ldb database library
+
+ Copyright (C) Andrew Tridgell 2004
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldif routines
+ *
+ * Description: ldif pack/unpack routines
+ *
+ * Author: Andrew Tridgell
+ */
+
+/*
+ see RFC2849 for the LDIF format definition
+*/
+
+#include "ldb_private.h"
+#include "system/locale.h"
+
+/*
+
+*/
+static int ldb_read_data_file(void *mem_ctx, struct ldb_val *value)
+{
+ struct stat statbuf;
+ char *buf;
+ int count, size, bytes;
+ int ret;
+ int f;
+ const char *fname = (const char *)value->data;
+
+ if (strncmp(fname, "file://", 7) != 0) {
+ return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
+ }
+ fname += 7;
+
+ f = open(fname, O_RDONLY);
+ if (f == -1) {
+ return -1;
+ }
+
+ if (fstat(f, &statbuf) != 0) {
+ ret = -1;
+ goto done;
+ }
+
+ if (statbuf.st_size == 0) {
+ ret = -1;
+ goto done;
+ }
+
+ value->data = (uint8_t *)talloc_size(mem_ctx, statbuf.st_size + 1);
+ if (value->data == NULL) {
+ ret = -1;
+ goto done;
+ }
+ value->data[statbuf.st_size] = 0;
+
+ count = 0;
+ size = statbuf.st_size;
+ buf = (char *)value->data;
+ while (count < statbuf.st_size) {
+ bytes = read(f, buf, size);
+ if (bytes == -1) {
+ talloc_free(value->data);
+ ret = -1;
+ goto done;
+ }
+ count += bytes;
+ buf += bytes;
+ size -= bytes;
+ }
+
+ value->length = statbuf.st_size;
+ ret = statbuf.st_size;
+
+done:
+ close(f);
+ return ret;
+}
+
+/*
+ this base64 decoder was taken from jitterbug (written by tridge).
+ we might need to replace it with a new version
+*/
+int ldb_base64_decode(char *s)
+{
+ const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ int bit_offset=0, byte_offset, idx, i, n;
+ uint8_t *d = (uint8_t *)s;
+ char *p=NULL;
+
+ n=i=0;
+
+ while (*s && (p=strchr(b64,*s))) {
+ idx = (int)(p - b64);
+ byte_offset = (i*6)/8;
+ bit_offset = (i*6)%8;
+ d[byte_offset] &= ~((1<<(8-bit_offset))-1);
+ if (bit_offset < 3) {
+ d[byte_offset] |= (idx << (2-bit_offset));
+ n = byte_offset+1;
+ } else {
+ d[byte_offset] |= (idx >> (bit_offset-2));
+ d[byte_offset+1] = 0;
+ d[byte_offset+1] |= (idx << (8-(bit_offset-2))) & 0xFF;
+ n = byte_offset+2;
+ }
+ s++; i++;
+ }
+ if (bit_offset >= 3) {
+ n--;
+ }
+
+ if (*s && !p) {
+ /* the only termination allowed */
+ if (*s != '=') {
+ return -1;
+ }
+ }
+
+ /* null terminate */
+ d[n] = 0;
+ return n;
+}
+
+
+/*
+ encode as base64
+ caller frees
+*/
+char *ldb_base64_encode(void *mem_ctx, const char *buf, int len)
+{
+ const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ int bit_offset, byte_offset, idx, i;
+ const uint8_t *d = (const uint8_t *)buf;
+ int bytes = (len*8 + 5)/6, pad_bytes = (bytes % 4) ? 4 - (bytes % 4) : 0;
+ char *out;
+
+ out = talloc_array(mem_ctx, char, bytes+pad_bytes+1);
+ if (!out) return NULL;
+
+ for (i=0;i<bytes;i++) {
+ byte_offset = (i*6)/8;
+ bit_offset = (i*6)%8;
+ if (bit_offset < 3) {
+ idx = (d[byte_offset] >> (2-bit_offset)) & 0x3F;
+ } else {
+ idx = (d[byte_offset] << (bit_offset-2)) & 0x3F;
+ if (byte_offset+1 < len) {
+ idx |= (d[byte_offset+1] >> (8-(bit_offset-2)));
+ }
+ }
+ out[i] = b64[idx];
+ }
+
+ for (;i<bytes+pad_bytes;i++)
+ out[i] = '=';
+ out[i] = 0;
+
+ return out;
+}
+
+/*
+ see if a buffer should be base64 encoded
+*/
+int ldb_should_b64_encode(const struct ldb_val *val)
+{
+ unsigned int i;
+ uint8_t *p = val->data;
+
+ if (val->length == 0) {
+ return 0;
+ }
+
+ if (p[0] == ' ' || p[0] == ':') {
+ return 1;
+ }
+
+ for (i=0; i<val->length; i++) {
+ if (!isprint(p[i]) || p[i] == '\n') {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* this macro is used to handle the return checking on fprintf_fn() */
+#define CHECK_RET do { if (ret < 0) return ret; total += ret; } while (0)
+
+/*
+ write a line folded string onto a file
+*/
+static int fold_string(int (*fprintf_fn)(void *, const char *, ...), void *private_data,
+ const char *buf, size_t length, int start_pos)
+{
+ unsigned int i;
+ int total=0, ret;
+
+ for (i=0;i<length;i++) {
+ ret = fprintf_fn(private_data, "%c", buf[i]);
+ CHECK_RET;
+ if (i != (length-1) && (i + start_pos) % 77 == 0) {
+ ret = fprintf_fn(private_data, "\n ");
+ CHECK_RET;
+ }
+ }
+
+ return total;
+}
+
+#undef CHECK_RET
+
+/*
+ encode as base64 to a file
+*/
+static int base64_encode_f(struct ldb_context *ldb,
+ int (*fprintf_fn)(void *, const char *, ...),
+ void *private_data,
+ const char *buf, int len, int start_pos)
+{
+ char *b = ldb_base64_encode(ldb, buf, len);
+ int ret;
+
+ if (!b) {
+ return -1;
+ }
+
+ ret = fold_string(fprintf_fn, private_data, b, strlen(b), start_pos);
+
+ talloc_free(b);
+ return ret;
+}
+
+
+static const struct {
+ const char *name;
+ enum ldb_changetype changetype;
+} ldb_changetypes[] = {
+ {"add", LDB_CHANGETYPE_ADD},
+ {"delete", LDB_CHANGETYPE_DELETE},
+ {"modify", LDB_CHANGETYPE_MODIFY},
+ {NULL, 0}
+};
+
+/* this macro is used to handle the return checking on fprintf_fn() */
+#define CHECK_RET do { if (ret < 0) { talloc_free(mem_ctx); return ret; } total += ret; } while (0)
+
+/*
+ write to ldif, using a caller supplied write method
+*/
+int ldb_ldif_write(struct ldb_context *ldb,
+ int (*fprintf_fn)(void *, const char *, ...),
+ void *private_data,
+ const struct ldb_ldif *ldif)
+{
+ TALLOC_CTX *mem_ctx;
+ unsigned int i, j;
+ int total=0, ret;
+ char *p;
+ const struct ldb_message *msg;
+
+ mem_ctx = talloc_named_const(NULL, 0, "ldb_ldif_write");
+
+ msg = ldif->msg;
+ p = ldb_dn_get_extended_linearized(mem_ctx, msg->dn, 1);
+ ret = fprintf_fn(private_data, "dn: %s\n", p);
+ talloc_free(p);
+ CHECK_RET;
+
+ if (ldif->changetype != LDB_CHANGETYPE_NONE) {
+ for (i=0;ldb_changetypes[i].name;i++) {
+ if (ldb_changetypes[i].changetype == ldif->changetype) {
+ break;
+ }
+ }
+ if (!ldb_changetypes[i].name) {
+ ldb_debug(ldb, LDB_DEBUG_ERROR, "Error: Invalid ldif changetype %d\n",
+ ldif->changetype);
+ talloc_free(mem_ctx);
+ return -1;
+ }
+ ret = fprintf_fn(private_data, "changetype: %s\n", ldb_changetypes[i].name);
+ CHECK_RET;
+ }
+
+ for (i=0;i<msg->num_elements;i++) {
+ const struct ldb_schema_attribute *a;
+
+ a = ldb_schema_attribute_by_name(ldb, msg->elements[i].name);
+
+ if (ldif->changetype == LDB_CHANGETYPE_MODIFY) {
+ switch (msg->elements[i].flags & LDB_FLAG_MOD_MASK) {
+ case LDB_FLAG_MOD_ADD:
+ fprintf_fn(private_data, "add: %s\n",
+ msg->elements[i].name);
+ break;
+ case LDB_FLAG_MOD_DELETE:
+ fprintf_fn(private_data, "delete: %s\n",
+ msg->elements[i].name);
+ break;
+ case LDB_FLAG_MOD_REPLACE:
+ fprintf_fn(private_data, "replace: %s\n",
+ msg->elements[i].name);
+ break;
+ }
+ }
+
+ for (j=0;j<msg->elements[i].num_values;j++) {
+ struct ldb_val v;
+ ret = a->syntax->ldif_write_fn(ldb, mem_ctx, &msg->elements[i].values[j], &v);
+ if (ret != LDB_SUCCESS) {
+ v = msg->elements[i].values[j];
+ }
+ if (ret != LDB_SUCCESS || ldb_should_b64_encode(&v)) {
+ ret = fprintf_fn(private_data, "%s:: ",
+ msg->elements[i].name);
+ CHECK_RET;
+ ret = base64_encode_f(ldb, fprintf_fn, private_data,
+ (char *)v.data, v.length,
+ strlen(msg->elements[i].name)+3);
+ CHECK_RET;
+ ret = fprintf_fn(private_data, "\n");
+ CHECK_RET;
+ } else {
+ ret = fprintf_fn(private_data, "%s: ", msg->elements[i].name);
+ CHECK_RET;
+ ret = fold_string(fprintf_fn, private_data,
+ (char *)v.data, v.length,
+ strlen(msg->elements[i].name)+2);
+ CHECK_RET;
+ ret = fprintf_fn(private_data, "\n");
+ CHECK_RET;
+ }
+ if (v.data != msg->elements[i].values[j].data) {
+ talloc_free(v.data);
+ }
+ }
+ if (ldif->changetype == LDB_CHANGETYPE_MODIFY) {
+ fprintf_fn(private_data, "-\n");
+ }
+ }
+ ret = fprintf_fn(private_data,"\n");
+ CHECK_RET;
+
+ return total;
+}
+
+#undef CHECK_RET
+
+
+/*
+ pull a ldif chunk, which is defined as a piece of data ending in \n\n or EOF
+ this routine removes any RFC2849 continuations and comments
+
+ caller frees
+*/
+static char *next_chunk(struct ldb_context *ldb,
+ int (*fgetc_fn)(void *), void *private_data)
+{
+ size_t alloc_size=0, chunk_size = 0;
+ char *chunk = NULL;
+ int c;
+ int in_comment = 0;
+
+ while ((c = fgetc_fn(private_data)) != EOF) {
+ if (chunk_size+1 >= alloc_size) {
+ char *c2;
+ alloc_size += 1024;
+ c2 = talloc_realloc(ldb, chunk, char, alloc_size);
+ if (!c2) {
+ talloc_free(chunk);
+ errno = ENOMEM;
+ return NULL;
+ }
+ chunk = c2;
+ }
+
+ if (in_comment) {
+ if (c == '\n') {
+ in_comment = 0;
+ }
+ continue;
+ }
+
+ /* handle continuation lines - see RFC2849 */
+ if (c == ' ' && chunk_size > 1 && chunk[chunk_size-1] == '\n') {
+ chunk_size--;
+ continue;
+ }
+
+ /* chunks are terminated by a double line-feed */
+ if (c == '\n' && chunk_size > 0 && chunk[chunk_size-1] == '\n') {
+ chunk[chunk_size-1] = 0;
+ return chunk;
+ }
+
+ if (c == '#' && (chunk_size == 0 || chunk[chunk_size-1] == '\n')) {
+ in_comment = 1;
+ continue;
+ }
+
+ /* ignore leading blank lines */
+ if (chunk_size == 0 && c == '\n') {
+ continue;
+ }
+
+ chunk[chunk_size++] = c;
+ }
+
+ if (chunk) {
+ chunk[chunk_size] = 0;
+ }
+
+ return chunk;
+}
+
+
+/* simple ldif attribute parser */
+static int next_attr(void *mem_ctx, char **s, const char **attr, struct ldb_val *value)
+{
+ char *p;
+ int base64_encoded = 0;
+ int binary_file = 0;
+
+ if (strncmp(*s, "-\n", 2) == 0) {
+ value->length = 0;
+ *attr = "-";
+ *s += 2;
+ return 0;
+ }
+
+ p = strchr(*s, ':');
+ if (!p) {
+ return -1;
+ }
+
+ *p++ = 0;
+
+ if (*p == ':') {
+ base64_encoded = 1;
+ p++;
+ }
+
+ if (*p == '<') {
+ binary_file = 1;
+ p++;
+ }
+
+ *attr = *s;
+
+ while (*p == ' ' || *p == '\t') {
+ p++;
+ }
+
+ value->data = (uint8_t *)p;
+
+ p = strchr(p, '\n');
+
+ if (!p) {
+ value->length = strlen((char *)value->data);
+ *s = ((char *)value->data) + value->length;
+ } else {
+ value->length = p - (char *)value->data;
+ *s = p+1;
+ *p = 0;
+ }
+
+ if (base64_encoded) {
+ int len = ldb_base64_decode((char *)value->data);
+ if (len == -1) {
+ /* it wasn't valid base64 data */
+ return -1;
+ }
+ value->length = len;
+ }
+
+ if (binary_file) {
+ int len = ldb_read_data_file(mem_ctx, value);
+ if (len == -1) {
+ /* an error occured hile trying to retrieve the file */
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+/*
+ free a message from a ldif_read
+*/
+void ldb_ldif_read_free(struct ldb_context *ldb, struct ldb_ldif *ldif)
+{
+ talloc_free(ldif);
+}
+
+/*
+ read from a LDIF source, creating a ldb_message
+*/
+struct ldb_ldif *ldb_ldif_read(struct ldb_context *ldb,
+ int (*fgetc_fn)(void *), void *private_data)
+{
+ struct ldb_ldif *ldif;
+ struct ldb_message *msg;
+ const char *attr=NULL;
+ char *chunk=NULL, *s;
+ struct ldb_val value;
+ unsigned flags = 0;
+
+ value.data = NULL;
+
+ ldif = talloc(ldb, struct ldb_ldif);
+ if (!ldif) return NULL;
+
+ ldif->msg = talloc(ldif, struct ldb_message);
+ if (ldif->msg == NULL) {
+ talloc_free(ldif);
+ return NULL;
+ }
+
+ ldif->changetype = LDB_CHANGETYPE_NONE;
+ msg = ldif->msg;
+
+ msg->dn = NULL;
+ msg->elements = NULL;
+ msg->num_elements = 0;
+
+ chunk = next_chunk(ldb, fgetc_fn, private_data);
+ if (!chunk) {
+ goto failed;
+ }
+ talloc_steal(ldif, chunk);
+
+ s = chunk;
+
+ if (next_attr(ldif, &s, &attr, &value) != 0) {
+ goto failed;
+ }
+
+ /* first line must be a dn */
+ if (ldb_attr_cmp(attr, "dn") != 0) {
+ ldb_debug(ldb, LDB_DEBUG_ERROR, "Error: First line of ldif must be a dn not '%s'\n",
+ attr);
+ goto failed;
+ }
+
+ msg->dn = ldb_dn_from_ldb_val(msg, ldb, &value);
+
+ if ( ! ldb_dn_validate(msg->dn)) {
+ ldb_debug(ldb, LDB_DEBUG_ERROR, "Error: Unable to parse dn '%s'\n",
+ (char *)value.data);
+ goto failed;
+ }
+
+ while (next_attr(ldif, &s, &attr, &value) == 0) {
+ const struct ldb_schema_attribute *a;
+ struct ldb_message_element *el;
+ int ret, empty = 0;
+
+ if (ldb_attr_cmp(attr, "changetype") == 0) {
+ int i;
+ for (i=0;ldb_changetypes[i].name;i++) {
+ if (ldb_attr_cmp((char *)value.data, ldb_changetypes[i].name) == 0) {
+ ldif->changetype = ldb_changetypes[i].changetype;
+ break;
+ }
+ }
+ if (!ldb_changetypes[i].name) {
+ ldb_debug(ldb, LDB_DEBUG_ERROR,
+ "Error: Bad ldif changetype '%s'\n",(char *)value.data);
+ }
+ flags = 0;
+ continue;
+ }
+
+ if (ldb_attr_cmp(attr, "add") == 0) {
+ flags = LDB_FLAG_MOD_ADD;
+ empty = 1;
+ }
+ if (ldb_attr_cmp(attr, "delete") == 0) {
+ flags = LDB_FLAG_MOD_DELETE;
+ empty = 1;
+ }
+ if (ldb_attr_cmp(attr, "replace") == 0) {
+ flags = LDB_FLAG_MOD_REPLACE;
+ empty = 1;
+ }
+ if (ldb_attr_cmp(attr, "-") == 0) {
+ flags = 0;
+ continue;
+ }
+
+ if (empty) {
+ if (ldb_msg_add_empty(msg, (char *)value.data, flags, NULL) != 0) {
+ goto failed;
+ }
+ continue;
+ }
+
+ el = &msg->elements[msg->num_elements-1];
+
+ a = ldb_schema_attribute_by_name(ldb, attr);
+
+ if (msg->num_elements > 0 && ldb_attr_cmp(attr, el->name) == 0 &&
+ flags == el->flags) {
+ /* its a continuation */
+ el->values =
+ talloc_realloc(msg->elements, el->values,
+ struct ldb_val, el->num_values+1);
+ if (!el->values) {
+ goto failed;
+ }
+ ret = a->syntax->ldif_read_fn(ldb, ldif, &value, &el->values[el->num_values]);
+ if (ret != 0) {
+ goto failed;
+ }
+ if (value.length == 0) {
+ ldb_debug(ldb, LDB_DEBUG_ERROR,
+ "Error: Attribute value cannot be empty for attribute '%s'\n", el->name);
+ goto failed;
+ }
+ if (value.data != el->values[el->num_values].data) {
+ talloc_steal(el->values, el->values[el->num_values].data);
+ }
+ el->num_values++;
+ } else {
+ /* its a new attribute */
+ msg->elements = talloc_realloc(ldif, msg->elements,
+ struct ldb_message_element,
+ msg->num_elements+1);
+ if (!msg->elements) {
+ goto failed;
+ }
+ el = &msg->elements[msg->num_elements];
+ el->flags = flags;
+ el->name = talloc_strdup(msg->elements, attr);
+ el->values = talloc(msg->elements, struct ldb_val);
+ if (!el->values || !el->name) {
+ goto failed;
+ }
+ el->num_values = 1;
+ ret = a->syntax->ldif_read_fn(ldb, ldif, &value, &el->values[0]);
+ if (ret != 0) {
+ goto failed;
+ }
+ if (value.data != el->values[0].data) {
+ talloc_steal(el->values, el->values[0].data);
+ }
+ msg->num_elements++;
+ }
+ }
+
+ return ldif;
+
+failed:
+ talloc_free(ldif);
+ return NULL;
+}
+
+
+
+/*
+ a wrapper around ldif_read() for reading from FILE*
+*/
+struct ldif_read_file_state {
+ FILE *f;
+};
+
+static int fgetc_file(void *private_data)
+{
+ struct ldif_read_file_state *state =
+ (struct ldif_read_file_state *)private_data;
+ return fgetc(state->f);
+}
+
+struct ldb_ldif *ldb_ldif_read_file(struct ldb_context *ldb, FILE *f)
+{
+ struct ldif_read_file_state state;
+ state.f = f;
+ return ldb_ldif_read(ldb, fgetc_file, &state);
+}
+
+
+/*
+ a wrapper around ldif_read() for reading from const char*
+*/
+struct ldif_read_string_state {
+ const char *s;
+};
+
+static int fgetc_string(void *private_data)
+{
+ struct ldif_read_string_state *state =
+ (struct ldif_read_string_state *)private_data;
+ if (state->s[0] != 0) {
+ return *state->s++;
+ }
+ return EOF;
+}
+
+struct ldb_ldif *ldb_ldif_read_string(struct ldb_context *ldb, const char **s)
+{
+ struct ldif_read_string_state state;
+ struct ldb_ldif *ldif;
+ state.s = *s;
+ ldif = ldb_ldif_read(ldb, fgetc_string, &state);
+ *s = state.s;
+ return ldif;
+}
+
+
+/*
+ wrapper around ldif_write() for a file
+*/
+struct ldif_write_file_state {
+ FILE *f;
+};
+
+static int fprintf_file(void *private_data, const char *fmt, ...) PRINTF_ATTRIBUTE(2, 3);
+
+static int fprintf_file(void *private_data, const char *fmt, ...)
+{
+ struct ldif_write_file_state *state =
+ (struct ldif_write_file_state *)private_data;
+ int ret;
+ va_list ap;
+
+ va_start(ap, fmt);
+ ret = vfprintf(state->f, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+
+int ldb_ldif_write_file(struct ldb_context *ldb, FILE *f, const struct ldb_ldif *ldif)
+{
+ struct ldif_write_file_state state;
+ state.f = f;
+ return ldb_ldif_write(ldb, fprintf_file, &state, ldif);
+}
diff --git a/source4/lib/ldb/common/ldb_match.c b/source4/lib/ldb/common/ldb_match.c
new file mode 100644
index 0000000000..c622701d30
--- /dev/null
+++ b/source4/lib/ldb/common/ldb_match.c
@@ -0,0 +1,428 @@
+/*
+ ldb database library
+
+ Copyright (C) Andrew Tridgell 2004-2005
+ Copyright (C) Simo Sorce 2005
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb expression matching
+ *
+ * Description: ldb expression matching
+ *
+ * Author: Andrew Tridgell
+ */
+
+#include "ldb_private.h"
+
+/*
+ check if the scope matches in a search result
+*/
+static int ldb_match_scope(struct ldb_context *ldb,
+ struct ldb_dn *base,
+ struct ldb_dn *dn,
+ enum ldb_scope scope)
+{
+ int ret = 0;
+
+ if (base == NULL || dn == NULL) {
+ return 1;
+ }
+
+ switch (scope) {
+ case LDB_SCOPE_BASE:
+ if (ldb_dn_compare(base, dn) == 0) {
+ ret = 1;
+ }
+ break;
+
+ case LDB_SCOPE_ONELEVEL:
+ if (ldb_dn_get_comp_num(dn) == (ldb_dn_get_comp_num(base) + 1)) {
+ if (ldb_dn_compare_base(base, dn) == 0) {
+ ret = 1;
+ }
+ }
+ break;
+
+ case LDB_SCOPE_SUBTREE:
+ default:
+ if (ldb_dn_compare_base(base, dn) == 0) {
+ ret = 1;
+ }
+ break;
+ }
+
+ return ret;
+}
+
+
+/*
+ match if node is present
+*/
+static int ldb_match_present(struct ldb_context *ldb,
+ const struct ldb_message *msg,
+ const struct ldb_parse_tree *tree,
+ enum ldb_scope scope)
+{
+ if (ldb_attr_dn(tree->u.present.attr) == 0) {
+ return 1;
+ }
+
+ if (ldb_msg_find_element(msg, tree->u.present.attr)) {
+ return 1;
+ }
+
+ return 0;
+}
+
+static int ldb_match_comparison(struct ldb_context *ldb,
+ const struct ldb_message *msg,
+ const struct ldb_parse_tree *tree,
+ enum ldb_scope scope,
+ enum ldb_parse_op comp_op)
+{
+ unsigned int i;
+ struct ldb_message_element *el;
+ const struct ldb_schema_attribute *a;
+ int ret;
+
+ /* FIXME: APPROX comparison not handled yet */
+ if (comp_op == LDB_OP_APPROX) return 0;
+
+ el = ldb_msg_find_element(msg, tree->u.comparison.attr);
+ if (el == NULL) {
+ return 0;
+ }
+
+ a = ldb_schema_attribute_by_name(ldb, el->name);
+
+ for (i = 0; i < el->num_values; i++) {
+ ret = a->syntax->comparison_fn(ldb, ldb, &el->values[i], &tree->u.comparison.value);
+
+ if (ret == 0) {
+ return 1;
+ }
+ if (ret > 0 && comp_op == LDB_OP_GREATER) {
+ return 1;
+ }
+ if (ret < 0 && comp_op == LDB_OP_LESS) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ match a simple leaf node
+*/
+static int ldb_match_equality(struct ldb_context *ldb,
+ const struct ldb_message *msg,
+ const struct ldb_parse_tree *tree,
+ enum ldb_scope scope)
+{
+ unsigned int i;
+ struct ldb_message_element *el;
+ const struct ldb_schema_attribute *a;
+ struct ldb_dn *valuedn;
+ int ret;
+
+ if (ldb_attr_dn(tree->u.equality.attr) == 0) {
+ valuedn = ldb_dn_from_ldb_val(ldb, ldb, &tree->u.equality.value);
+ if (valuedn == NULL) {
+ return 0;
+ }
+
+ ret = ldb_dn_compare(msg->dn, valuedn);
+
+ talloc_free(valuedn);
+
+ if (ret == 0) return 1;
+ return 0;
+ }
+
+ /* TODO: handle the "*" case derived from an extended search
+ operation without the attibute type defined */
+ el = ldb_msg_find_element(msg, tree->u.equality.attr);
+ if (el == NULL) {
+ return 0;
+ }
+
+ a = ldb_schema_attribute_by_name(ldb, el->name);
+
+ for (i=0;i<el->num_values;i++) {
+ if (a->syntax->comparison_fn(ldb, ldb, &tree->u.equality.value,
+ &el->values[i]) == 0) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int ldb_wildcard_compare(struct ldb_context *ldb,
+ const struct ldb_parse_tree *tree,
+ const struct ldb_val value)
+{
+ const struct ldb_schema_attribute *a;
+ struct ldb_val val;
+ struct ldb_val cnk;
+ struct ldb_val *chunk;
+ char *p, *g;
+ uint8_t *save_p = NULL;
+ int c = 0;
+
+ a = ldb_schema_attribute_by_name(ldb, tree->u.substring.attr);
+
+ if(a->syntax->canonicalise_fn(ldb, ldb, &value, &val) != 0)
+ return -1;
+
+ save_p = val.data;
+ cnk.data = NULL;
+
+ if ( ! tree->u.substring.start_with_wildcard ) {
+
+ chunk = tree->u.substring.chunks[c];
+ if(a->syntax->canonicalise_fn(ldb, ldb, chunk, &cnk) != 0) goto failed;
+
+ /* This deals with wildcard prefix searches on binary attributes (eg objectGUID) */
+ if (cnk.length > val.length) {
+ goto failed;
+ }
+ if (memcmp((char *)val.data, (char *)cnk.data, cnk.length) != 0) goto failed;
+ val.length -= cnk.length;
+ val.data += cnk.length;
+ c++;
+ talloc_free(cnk.data);
+ cnk.data = NULL;
+ }
+
+ while (tree->u.substring.chunks[c]) {
+
+ chunk = tree->u.substring.chunks[c];
+ if(a->syntax->canonicalise_fn(ldb, ldb, chunk, &cnk) != 0) goto failed;
+
+ /* FIXME: case of embedded nulls */
+ p = strstr((char *)val.data, (char *)cnk.data);
+ if (p == NULL) goto failed;
+ if ( (! tree->u.substring.chunks[c + 1]) && (! tree->u.substring.end_with_wildcard) ) {
+ do { /* greedy */
+ g = strstr((char *)p + cnk.length, (char *)cnk.data);
+ if (g) p = g;
+ } while(g);
+ }
+ val.length = val.length - (p - (char *)(val.data)) - cnk.length;
+ val.data = (uint8_t *)(p + cnk.length);
+ c++;
+ talloc_free(cnk.data);
+ cnk.data = NULL;
+ }
+
+ if ( (! tree->u.substring.end_with_wildcard) && (*(val.data) != 0) ) goto failed; /* last chunk have not reached end of string */
+ talloc_free(save_p);
+ return 1;
+
+failed:
+ talloc_free(save_p);
+ talloc_free(cnk.data);
+ return 0;
+}
+
+/*
+ match a simple leaf node
+*/
+static int ldb_match_substring(struct ldb_context *ldb,
+ const struct ldb_message *msg,
+ const struct ldb_parse_tree *tree,
+ enum ldb_scope scope)
+{
+ unsigned int i;
+ struct ldb_message_element *el;
+
+ el = ldb_msg_find_element(msg, tree->u.substring.attr);
+ if (el == NULL) {
+ return 0;
+ }
+
+ for (i = 0; i < el->num_values; i++) {
+ if (ldb_wildcard_compare(ldb, tree, el->values[i]) == 1) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+
+/*
+ bitwise-and comparator
+*/
+static int ldb_comparator_and(const struct ldb_val *v1, const struct ldb_val *v2)
+{
+ uint64_t i1, i2;
+ i1 = strtoull((char *)v1->data, NULL, 0);
+ i2 = strtoull((char *)v2->data, NULL, 0);
+ return ((i1 & i2) == i2);
+}
+
+/*
+ bitwise-or comparator
+*/
+static int ldb_comparator_or(const struct ldb_val *v1, const struct ldb_val *v2)
+{
+ uint64_t i1, i2;
+ i1 = strtoull((char *)v1->data, NULL, 0);
+ i2 = strtoull((char *)v2->data, NULL, 0);
+ return ((i1 & i2) != 0);
+}
+
+
+/*
+ extended match, handles things like bitops
+*/
+static int ldb_match_extended(struct ldb_context *ldb,
+ const struct ldb_message *msg,
+ const struct ldb_parse_tree *tree,
+ enum ldb_scope scope)
+{
+ int i;
+ const struct {
+ const char *oid;
+ int (*comparator)(const struct ldb_val *, const struct ldb_val *);
+ } rules[] = {
+ { LDB_OID_COMPARATOR_AND, ldb_comparator_and},
+ { LDB_OID_COMPARATOR_OR, ldb_comparator_or}
+ };
+ int (*comp)(const struct ldb_val *, const struct ldb_val *) = NULL;
+ struct ldb_message_element *el;
+
+ if (tree->u.extended.dnAttributes) {
+ ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb: dnAttributes extended match not supported yet");
+ return -1;
+ }
+ if (tree->u.extended.rule_id == NULL) {
+ ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb: no-rule extended matches not supported yet");
+ return -1;
+ }
+ if (tree->u.extended.attr == NULL) {
+ ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb: no-attribute extended matches not supported yet");
+ return -1;
+ }
+
+ for (i=0;i<ARRAY_SIZE(rules);i++) {
+ if (strcmp(rules[i].oid, tree->u.extended.rule_id) == 0) {
+ comp = rules[i].comparator;
+ break;
+ }
+ }
+ if (comp == NULL) {
+ ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb: unknown extended rule_id %s\n",
+ tree->u.extended.rule_id);
+ return -1;
+ }
+
+ /* find the message element */
+ el = ldb_msg_find_element(msg, tree->u.extended.attr);
+ if (el == NULL) {
+ return 0;
+ }
+
+ for (i=0;i<el->num_values;i++) {
+ int ret = comp(&el->values[i], &tree->u.extended.value);
+ if (ret == -1 || ret == 1) return ret;
+ }
+
+ return 0;
+}
+
+/*
+ return 0 if the given parse tree matches the given message. Assumes
+ the message is in sorted order
+
+ return 1 if it matches, and 0 if it doesn't match
+
+ this is a recursive function, and does short-circuit evaluation
+ */
+static int ldb_match_message(struct ldb_context *ldb,
+ const struct ldb_message *msg,
+ const struct ldb_parse_tree *tree,
+ enum ldb_scope scope)
+{
+ unsigned int i;
+ int v;
+
+ switch (tree->operation) {
+ case LDB_OP_AND:
+ for (i=0;i<tree->u.list.num_elements;i++) {
+ v = ldb_match_message(ldb, msg, tree->u.list.elements[i], scope);
+ if (!v) return 0;
+ }
+ return 1;
+
+ case LDB_OP_OR:
+ for (i=0;i<tree->u.list.num_elements;i++) {
+ v = ldb_match_message(ldb, msg, tree->u.list.elements[i], scope);
+ if (v) return 1;
+ }
+ return 0;
+
+ case LDB_OP_NOT:
+ return ! ldb_match_message(ldb, msg, tree->u.isnot.child, scope);
+
+ case LDB_OP_EQUALITY:
+ return ldb_match_equality(ldb, msg, tree, scope);
+
+ case LDB_OP_SUBSTRING:
+ return ldb_match_substring(ldb, msg, tree, scope);
+
+ case LDB_OP_GREATER:
+ return ldb_match_comparison(ldb, msg, tree, scope, LDB_OP_GREATER);
+
+ case LDB_OP_LESS:
+ return ldb_match_comparison(ldb, msg, tree, scope, LDB_OP_LESS);
+
+ case LDB_OP_PRESENT:
+ return ldb_match_present(ldb, msg, tree, scope);
+
+ case LDB_OP_APPROX:
+ return ldb_match_comparison(ldb, msg, tree, scope, LDB_OP_APPROX);
+
+ case LDB_OP_EXTENDED:
+ return ldb_match_extended(ldb, msg, tree, scope);
+
+ }
+
+ return 0;
+}
+
+int ldb_match_msg(struct ldb_context *ldb,
+ const struct ldb_message *msg,
+ const struct ldb_parse_tree *tree,
+ struct ldb_dn *base,
+ enum ldb_scope scope)
+{
+ if ( ! ldb_match_scope(ldb, base, msg->dn, scope) ) {
+ return 0;
+ }
+
+ return ldb_match_message(ldb, msg, tree, scope);
+}
diff --git a/source4/lib/ldb/common/ldb_modules.c b/source4/lib/ldb/common/ldb_modules.c
new file mode 100644
index 0000000000..ae97ef4cce
--- /dev/null
+++ b/source4/lib/ldb/common/ldb_modules.c
@@ -0,0 +1,808 @@
+/*
+ ldb database library
+
+ Copyright (C) Simo Sorce 2004-2008
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb modules core
+ *
+ * Description: core modules routines
+ *
+ * Author: Simo Sorce
+ */
+
+#include "ldb_private.h"
+#include "dlinklist.h"
+
+#define LDB_MODULE_PREFIX "modules:"
+#define LDB_MODULE_PREFIX_LEN 8
+
+static void *ldb_dso_load_symbol(struct ldb_context *ldb, const char *name,
+ const char *symbol);
+
+void ldb_set_modules_dir(struct ldb_context *ldb, const char *path)
+{
+ talloc_free(ldb->modules_dir);
+ ldb->modules_dir = talloc_strdup(ldb, path);
+}
+
+static char *ldb_modules_strdup_no_spaces(TALLOC_CTX *mem_ctx, const char *string)
+{
+ int i, len;
+ char *trimmed;
+
+ trimmed = talloc_strdup(mem_ctx, string);
+ if (!trimmed) {
+ return NULL;
+ }
+
+ len = strlen(trimmed);
+ for (i = 0; trimmed[i] != '\0'; i++) {
+ switch (trimmed[i]) {
+ case ' ':
+ case '\t':
+ case '\n':
+ memmove(&trimmed[i], &trimmed[i + 1], len -i -1);
+ break;
+ }
+ }
+
+ return trimmed;
+}
+
+
+/* modules are called in inverse order on the stack.
+ Lets place them as an admin would think the right order is.
+ Modules order is important */
+const char **ldb_modules_list_from_string(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, const char *string)
+{
+ char **modules = NULL;
+ const char **m;
+ char *modstr, *p;
+ int i;
+
+ /* spaces not admitted */
+ modstr = ldb_modules_strdup_no_spaces(mem_ctx, string);
+ if ( ! modstr) {
+ ldb_debug(ldb, LDB_DEBUG_FATAL, "Out of Memory in ldb_modules_strdup_no_spaces()\n");
+ return NULL;
+ }
+
+ modules = talloc_realloc(mem_ctx, modules, char *, 2);
+ if ( ! modules ) {
+ ldb_debug(ldb, LDB_DEBUG_FATAL, "Out of Memory in ldb_modules_list_from_string()\n");
+ talloc_free(modstr);
+ return NULL;
+ }
+ talloc_steal(modules, modstr);
+
+ i = 0;
+ /* The str*r*chr walks backwards: This is how we get the inverse order mentioned above */
+ while ((p = strrchr(modstr, ',')) != NULL) {
+ *p = '\0';
+ p++;
+ modules[i] = p;
+
+ i++;
+ modules = talloc_realloc(mem_ctx, modules, char *, i + 2);
+ if ( ! modules ) {
+ ldb_debug(ldb, LDB_DEBUG_FATAL, "Out of Memory in ldb_modules_list_from_string()\n");
+ return NULL;
+ }
+
+ }
+ modules[i] = modstr;
+
+ modules[i + 1] = NULL;
+
+ m = (const char **)modules;
+
+ return m;
+}
+
+static struct backends_list_entry {
+ struct ldb_backend_ops *ops;
+ struct backends_list_entry *prev, *next;
+} *ldb_backends = NULL;
+
+static struct ops_list_entry {
+ const struct ldb_module_ops *ops;
+ struct ops_list_entry *next;
+} *registered_modules = NULL;
+
+static const struct ldb_builtins {
+ const struct ldb_backend_ops *backend_ops;
+ const struct ldb_module_ops *module_ops;
+} builtins[];
+
+static ldb_connect_fn ldb_find_backend(const char *url)
+{
+ struct backends_list_entry *backend;
+ int i;
+
+ for (i = 0; builtins[i].backend_ops || builtins[i].module_ops; i++) {
+ if (builtins[i].backend_ops == NULL) continue;
+
+ if (strncmp(builtins[i].backend_ops->name, url,
+ strlen(builtins[i].backend_ops->name)) == 0) {
+ return builtins[i].backend_ops->connect_fn;
+ }
+ }
+
+ for (backend = ldb_backends; backend; backend = backend->next) {
+ if (strncmp(backend->ops->name, url,
+ strlen(backend->ops->name)) == 0) {
+ return backend->ops->connect_fn;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ register a new ldb backend
+*/
+int ldb_register_backend(const char *url_prefix, ldb_connect_fn connectfn)
+{
+ struct ldb_backend_ops *backend;
+ struct backends_list_entry *entry;
+
+ backend = talloc(talloc_autofree_context(), struct ldb_backend_ops);
+ if (!backend) return LDB_ERR_OPERATIONS_ERROR;
+
+ entry = talloc(talloc_autofree_context(), struct backends_list_entry);
+ if (!entry) {
+ talloc_free(backend);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if (ldb_find_backend(url_prefix)) {
+ return LDB_SUCCESS;
+ }
+
+ /* Maybe check for duplicity here later on? */
+
+ backend->name = talloc_strdup(backend, url_prefix);
+ backend->connect_fn = connectfn;
+ entry->ops = backend;
+ DLIST_ADD(ldb_backends, entry);
+
+ return LDB_SUCCESS;
+}
+
+/*
+ Return the ldb module form of a database.
+ The URL can either be one of the following forms
+ ldb://path
+ ldapi://path
+
+ flags is made up of LDB_FLG_*
+
+ the options are passed uninterpreted to the backend, and are
+ backend specific.
+
+ This allows modules to get at only the backend module, for example where a
+ module may wish to direct certain requests at a particular backend.
+*/
+int ldb_connect_backend(struct ldb_context *ldb,
+ const char *url,
+ const char *options[],
+ struct ldb_module **backend_module)
+{
+ int ret;
+ char *backend;
+ ldb_connect_fn fn;
+
+ if (strchr(url, ':') != NULL) {
+ backend = talloc_strndup(ldb, url, strchr(url, ':')-url);
+ } else {
+ /* Default to tdb */
+ backend = talloc_strdup(ldb, "tdb");
+ }
+
+ fn = ldb_find_backend(backend);
+
+ if (fn == NULL) {
+ struct ldb_backend_ops *ops;
+ char *symbol_name = talloc_asprintf(ldb, "ldb_%s_backend_ops", backend);
+ if (symbol_name == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ops = ldb_dso_load_symbol(ldb, backend, symbol_name);
+ if (ops != NULL) {
+ fn = ops->connect_fn;
+ }
+ talloc_free(symbol_name);
+ }
+
+ talloc_free(backend);
+
+ if (fn == NULL) {
+ ldb_debug(ldb, LDB_DEBUG_FATAL,
+ "Unable to find backend for '%s'\n", url);
+ return LDB_ERR_OTHER;
+ }
+
+ ret = fn(ldb, url, ldb->flags, options, backend_module);
+
+ if (ret != LDB_SUCCESS) {
+ ldb_debug(ldb, LDB_DEBUG_ERROR,
+ "Failed to connect to '%s'\n", url);
+ return ret;
+ }
+ return ret;
+}
+
+static const struct ldb_module_ops *ldb_find_module_ops(const char *name)
+{
+ struct ops_list_entry *e;
+ int i;
+
+ for (i = 0; builtins[i].backend_ops || builtins[i].module_ops; i++) {
+ if (builtins[i].module_ops == NULL) continue;
+
+ if (strcmp(builtins[i].module_ops->name, name) == 0)
+ return builtins[i].module_ops;
+ }
+
+ for (e = registered_modules; e; e = e->next) {
+ if (strcmp(e->ops->name, name) == 0)
+ return e->ops;
+ }
+
+ return NULL;
+}
+
+
+int ldb_register_module(const struct ldb_module_ops *ops)
+{
+ struct ops_list_entry *entry = talloc(talloc_autofree_context(), struct ops_list_entry);
+
+ if (ldb_find_module_ops(ops->name) != NULL)
+ return -1;
+
+ if (entry == NULL)
+ return -1;
+
+ entry->ops = ops;
+ entry->next = registered_modules;
+ registered_modules = entry;
+
+ return 0;
+}
+
+static void *ldb_dso_load_symbol(struct ldb_context *ldb, const char *name,
+ const char *symbol)
+{
+ char *path;
+ void *handle;
+ void *sym;
+
+ if (ldb->modules_dir == NULL)
+ return NULL;
+
+ path = talloc_asprintf(ldb, "%s/%s.%s", ldb->modules_dir, name,
+ SHLIBEXT);
+
+ ldb_debug(ldb, LDB_DEBUG_TRACE, "trying to load %s from %s\n", name, path);
+
+ handle = dlopen(path, RTLD_NOW);
+ if (handle == NULL) {
+ ldb_debug(ldb, LDB_DEBUG_WARNING, "unable to load %s from %s: %s\n", name, path, dlerror());
+ return NULL;
+ }
+
+ sym = (int (*)(void))dlsym(handle, symbol);
+
+ if (sym == NULL) {
+ ldb_debug(ldb, LDB_DEBUG_ERROR, "no symbol `%s' found in %s: %s\n", symbol, path, dlerror());
+ return NULL;
+ }
+
+ talloc_free(path);
+
+ return sym;
+}
+
+int ldb_load_modules_list(struct ldb_context *ldb, const char **module_list, struct ldb_module *backend, struct ldb_module **out)
+{
+ struct ldb_module *module;
+ int i;
+
+ module = backend;
+
+ for (i = 0; module_list[i] != NULL; i++) {
+ struct ldb_module *current;
+ const struct ldb_module_ops *ops;
+
+ if (strcmp(module_list[i], "") == 0) {
+ continue;
+ }
+
+ ops = ldb_find_module_ops(module_list[i]);
+ if (ops == NULL) {
+ char *symbol_name = talloc_asprintf(ldb, "ldb_%s_module_ops",
+ module_list[i]);
+ if (symbol_name == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ops = ldb_dso_load_symbol(ldb, module_list[i], symbol_name);
+ talloc_free(symbol_name);
+ }
+
+ if (ops == NULL) {
+ ldb_debug(ldb, LDB_DEBUG_WARNING, "WARNING: Module [%s] not found\n",
+ module_list[i]);
+ continue;
+ }
+
+ current = talloc_zero(ldb, struct ldb_module);
+ if (current == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ talloc_set_name(current, "ldb_module: %s", module_list[i]);
+
+ current->ldb = ldb;
+ current->ops = ops;
+
+ DLIST_ADD(module, current);
+ }
+ *out = module;
+ return LDB_SUCCESS;
+}
+
+int ldb_init_module_chain(struct ldb_context *ldb, struct ldb_module *module)
+{
+ while (module && module->ops->init_context == NULL)
+ module = module->next;
+
+ /* init is different in that it is not an error if modules
+ * do not require initialization */
+
+ if (module) {
+ int ret = module->ops->init_context(module);
+ if (ret != LDB_SUCCESS) {
+ ldb_debug(ldb, LDB_DEBUG_FATAL, "module %s initialization failed\n", module->ops->name);
+ return ret;
+ }
+ }
+
+ return LDB_SUCCESS;
+}
+
+int ldb_load_modules(struct ldb_context *ldb, const char *options[])
+{
+ const char **modules = NULL;
+ int i;
+ int ret;
+ TALLOC_CTX *mem_ctx = talloc_new(ldb);
+ if (!mem_ctx) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* find out which modules we are requested to activate */
+
+ /* check if we have a custom module list passd as ldb option */
+ if (options) {
+ for (i = 0; options[i] != NULL; i++) {
+ if (strncmp(options[i], LDB_MODULE_PREFIX, LDB_MODULE_PREFIX_LEN) == 0) {
+ modules = ldb_modules_list_from_string(ldb, mem_ctx, &options[i][LDB_MODULE_PREFIX_LEN]);
+ }
+ }
+ }
+
+ /* if not overloaded by options and the backend is not ldap try to load the modules list from ldb */
+ if ((modules == NULL) && (strcmp("ldap", ldb->modules->ops->name) != 0)) {
+ const char * const attrs[] = { "@LIST" , NULL};
+ struct ldb_result *res = NULL;
+ struct ldb_dn *mods_dn;
+
+ mods_dn = ldb_dn_new(mem_ctx, ldb, "@MODULES");
+ if (mods_dn == NULL) {
+ talloc_free(mem_ctx);
+ return -1;
+ }
+
+ ret = ldb_search(ldb, mods_dn, &res, mods_dn, LDB_SCOPE_BASE, attrs, "@LIST=*");
+
+ if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+ ldb_debug(ldb, LDB_DEBUG_TRACE, "no modules required by the db");
+ } else if (ret != LDB_SUCCESS) {
+ ldb_debug(ldb, LDB_DEBUG_FATAL, "ldb error (%s) occurred searching for modules, bailing out\n", ldb_errstring(ldb));
+ talloc_free(mem_ctx);
+ return ret;
+ } else {
+ const char *module_list;
+ if (res->count == 0) {
+ ldb_debug(ldb, LDB_DEBUG_TRACE, "no modules required by the db");
+ } else if (res->count > 1) {
+ ldb_debug(ldb, LDB_DEBUG_FATAL, "Too many records found (%d), bailing out\n", res->count);
+ talloc_free(mem_ctx);
+ return -1;
+ } else {
+ module_list = ldb_msg_find_attr_as_string(res->msgs[0], "@LIST", NULL);
+ if (!module_list) {
+ ldb_debug(ldb, LDB_DEBUG_TRACE, "no modules required by the db");
+ }
+ modules = ldb_modules_list_from_string(ldb, mem_ctx,
+ module_list);
+ }
+ }
+
+ talloc_free(mods_dn);
+ }
+
+ if (modules != NULL) {
+ ret = ldb_load_modules_list(ldb, modules, ldb->modules, &ldb->modules);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(mem_ctx);
+ return ret;
+ }
+ } else {
+ ldb_debug(ldb, LDB_DEBUG_TRACE, "No modules specified for this database");
+ }
+
+ ret = ldb_init_module_chain(ldb, ldb->modules);
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+/*
+ by using this we allow ldb modules to only implement the functions they care about,
+ which makes writing a module simpler, and makes it more likely to keep working
+ when ldb is extended
+*/
+#define FIND_OP(module, op) do { \
+ struct ldb_context *ldb = module->ldb; \
+ module = module->next; \
+ while (module && module->ops->op == NULL) module = module->next; \
+ if (module == NULL) { \
+ ldb_asprintf_errstring(ldb, "Unable to find backend operation for " #op ); \
+ return LDB_ERR_OPERATIONS_ERROR; \
+ } \
+} while (0)
+
+
+struct ldb_module *ldb_module_new(TALLOC_CTX *memctx,
+ struct ldb_context *ldb,
+ const char *module_name,
+ const struct ldb_module_ops *ops)
+{
+ struct ldb_module *module;
+
+ module = talloc(memctx, struct ldb_module);
+ if (!module) {
+ ldb_oom(ldb);
+ return NULL;
+ }
+ talloc_set_name_const(module, module_name);
+ module->ldb = ldb;
+ module->prev = module->next = NULL;
+ module->ops = ops;
+
+ return module;
+}
+
+const char * ldb_module_get_name(struct ldb_module *module)
+{
+ return module->ops->name;
+}
+
+struct ldb_context *ldb_module_get_ctx(struct ldb_module *module)
+{
+ return module->ldb;
+}
+
+void *ldb_module_get_private(struct ldb_module *module)
+{
+ return module->private_data;
+}
+
+void ldb_module_set_private(struct ldb_module *module, void *private_data)
+{
+ module->private_data = private_data;
+}
+
+/*
+ helper functions to call the next module in chain
+*/
+
+int ldb_next_request(struct ldb_module *module, struct ldb_request *request)
+{
+ int ret;
+
+ if (request->callback == NULL) {
+ ldb_set_errstring(module->ldb, "Requests MUST define callbacks");
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+
+ switch (request->operation) {
+ case LDB_SEARCH:
+ FIND_OP(module, search);
+ ret = module->ops->search(module, request);
+ break;
+ case LDB_ADD:
+ FIND_OP(module, add);
+ ret = module->ops->add(module, request);
+ break;
+ case LDB_MODIFY:
+ FIND_OP(module, modify);
+ ret = module->ops->modify(module, request);
+ break;
+ case LDB_DELETE:
+ FIND_OP(module, del);
+ ret = module->ops->del(module, request);
+ break;
+ case LDB_RENAME:
+ FIND_OP(module, rename);
+ ret = module->ops->rename(module, request);
+ break;
+ case LDB_EXTENDED:
+ FIND_OP(module, extended);
+ ret = module->ops->extended(module, request);
+ break;
+ default:
+ FIND_OP(module, request);
+ ret = module->ops->request(module, request);
+ break;
+ }
+ if (ret == LDB_SUCCESS) {
+ return ret;
+ }
+ if (!ldb_errstring(module->ldb)) {
+ /* Set a default error string, to place the blame somewhere */
+ ldb_asprintf_errstring(module->ldb, "error in module %s: %s (%d)", module->ops->name, ldb_strerror(ret), ret);
+ }
+ return ret;
+}
+
+int ldb_next_init(struct ldb_module *module)
+{
+ module = module->next;
+
+ return ldb_init_module_chain(module->ldb, module);
+}
+
+int ldb_next_start_trans(struct ldb_module *module)
+{
+ FIND_OP(module, start_transaction);
+ return module->ops->start_transaction(module);
+}
+
+int ldb_next_end_trans(struct ldb_module *module)
+{
+ FIND_OP(module, end_transaction);
+ return module->ops->end_transaction(module);
+}
+
+int ldb_next_del_trans(struct ldb_module *module)
+{
+ FIND_OP(module, del_transaction);
+ return module->ops->del_transaction(module);
+}
+
+struct ldb_handle *ldb_handle_new(TALLOC_CTX *mem_ctx, struct ldb_context *ldb)
+{
+ struct ldb_handle *h;
+
+ h = talloc_zero(mem_ctx, struct ldb_handle);
+ if (h == NULL) {
+ ldb_set_errstring(ldb, "Out of Memory");
+ return NULL;
+ }
+
+ h->status = LDB_SUCCESS;
+ h->state = LDB_ASYNC_INIT;
+ h->ldb = ldb;
+
+ return h;
+}
+
+/* calls the request callback to send an entry
+ *
+ * params:
+ * req: the original request passed to your module
+ * msg: reply message (must be a talloc pointer, and it will be stolen
+ * on the ldb_reply that is sent to the callback)
+ * ctrls: controls to send in the reply (must be a talloc pointer, and it will be stolen
+ * on the ldb_reply that is sent to the callback)
+ */
+
+int ldb_module_send_entry(struct ldb_request *req,
+ struct ldb_message *msg,
+ struct ldb_control **ctrls)
+{
+ struct ldb_reply *ares;
+
+ ares = talloc_zero(req, struct ldb_reply);
+ if (!ares) {
+ ldb_oom(req->handle->ldb);
+ req->callback(req, NULL);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ares->type = LDB_REPLY_ENTRY;
+ ares->message = talloc_steal(ares, msg);
+ ares->controls = talloc_steal(ares, ctrls);
+ ares->error = LDB_SUCCESS;
+
+ return req->callback(req, ares);
+}
+
+/* calls the request callback to send an referrals
+ *
+ * params:
+ * req: the original request passed to your module
+ * ref: referral string (must be a talloc pointeri, steal)
+ */
+
+int ldb_module_send_referral(struct ldb_request *req,
+ char *ref)
+{
+ struct ldb_reply *ares;
+
+ ares = talloc_zero(req, struct ldb_reply);
+ if (!ares) {
+ ldb_oom(req->handle->ldb);
+ req->callback(req, NULL);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ares->type = LDB_REPLY_REFERRAL;
+ ares->referral = talloc_steal(ares, ref);
+ ares->error = LDB_SUCCESS;
+
+ return req->callback(req, ares);
+}
+
+/* calls the original request callback
+ *
+ * params:
+ * req: the original request passed to your module
+ * ctrls: controls to send in the reply (must be a talloc pointer, steal)
+ * response: results for extended request (steal)
+ * error: LDB_SUCCESS for a succesful return
+ * any other ldb error otherwise
+ */
+int ldb_module_done(struct ldb_request *req,
+ struct ldb_control **ctrls,
+ struct ldb_extended *response,
+ int error)
+{
+ struct ldb_reply *ares;
+
+ ares = talloc_zero(req, struct ldb_reply);
+ if (!ares) {
+ ldb_oom(req->handle->ldb);
+ req->callback(req, NULL);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ares->type = LDB_REPLY_DONE;
+ ares->controls = talloc_steal(ares, ctrls);
+ ares->response = talloc_steal(ares, response);
+ ares->error = error;
+
+ req->callback(req, ares);
+ return error;
+}
+
+/* to be used *only* in modules init functions.
+ * this function i synchronous and will register
+ * the requested OID in the rootdse module if present
+ * otherwise it will return an error */
+int ldb_mod_register_control(struct ldb_module *module, const char *oid)
+{
+ struct ldb_request *req;
+ int ret;
+
+ req = talloc_zero(module, struct ldb_request);
+ if (req == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ req->operation = LDB_REQ_REGISTER_CONTROL;
+ req->op.reg_control.oid = oid;
+ req->callback = ldb_op_default_callback;
+
+ ldb_set_timeout(module->ldb, req, 0);
+
+ req->handle = ldb_handle_new(req, module->ldb);
+ if (req->handle == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ldb_request(module->ldb, req);
+ if (ret == LDB_SUCCESS) {
+ ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+ }
+ talloc_free(req);
+
+ return ret;
+}
+
+#ifndef STATIC_LIBLDB_MODULES
+
+#ifdef HAVE_LDB_LDAP
+#define LDAP_BACKEND LDB_BACKEND(ldap), LDB_BACKEND(ldapi), LDB_BACKEND(ldaps),
+#else
+#define LDAP_BACKEND
+#endif
+
+#ifdef HAVE_LDB_SQLITE3
+#define SQLITE3_BACKEND LDB_BACKEND(sqlite3),
+#else
+#define SQLITE3_BACKEND
+#endif
+
+#define STATIC_LIBLDB_MODULES \
+ LDB_BACKEND(tdb), \
+ LDAP_BACKEND \
+ SQLITE3_BACKEND \
+ LDB_MODULE(operational), \
+ LDB_MODULE(rdn_name), \
+ LDB_MODULE(paged_results), \
+ LDB_MODULE(server_sort), \
+ LDB_MODULE(asq), \
+ NULL
+#endif
+
+/*
+ * this is a bit hacked, as STATIC_LIBLDB_MODULES contains ','
+ * between the elements and we want to autogenerate the
+ * extern struct declarations, so we do some hacks and let the
+ * ',' appear in an unused function prototype.
+ */
+#undef NULL
+#define NULL LDB_MODULE(NULL),
+
+#define LDB_BACKEND(name) \
+ int); \
+ extern const struct ldb_backend_ops ldb_ ## name ## _backend_ops;\
+ extern void ldb_noop ## name (int
+#define LDB_MODULE(name) \
+ int); \
+ extern const struct ldb_module_ops ldb_ ## name ## _module_ops;\
+ extern void ldb_noop ## name (int
+
+extern void ldb_start_noop(int,
+STATIC_LIBLDB_MODULES
+int);
+
+#undef NULL
+#define NULL { \
+ .backend_ops = (void *)0, \
+ .module_ops = (void *)0 \
+}
+
+#undef LDB_BACKEND
+#define LDB_BACKEND(name) { \
+ .backend_ops = &ldb_ ## name ## _backend_ops, \
+ .module_ops = (void *)0 \
+}
+#undef LDB_MODULE
+#define LDB_MODULE(name) { \
+ .backend_ops = (void *)0, \
+ .module_ops = &ldb_ ## name ## _module_ops \
+}
+
+static const struct ldb_builtins builtins[] = {
+ STATIC_LIBLDB_MODULES
+};
diff --git a/source4/lib/ldb/common/ldb_msg.c b/source4/lib/ldb/common/ldb_msg.c
new file mode 100644
index 0000000000..ad53a3d29d
--- /dev/null
+++ b/source4/lib/ldb/common/ldb_msg.c
@@ -0,0 +1,899 @@
+/*
+ ldb database library
+
+ Copyright (C) Andrew Tridgell 2004
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb message component utility functions
+ *
+ * Description: functions for manipulating ldb_message structures
+ *
+ * Author: Andrew Tridgell
+ */
+
+#include "ldb_private.h"
+
+/*
+ create a new ldb_message in a given memory context (NULL for top level)
+*/
+struct ldb_message *ldb_msg_new(void *mem_ctx)
+{
+ return talloc_zero(mem_ctx, struct ldb_message);
+}
+
+/*
+ find an element in a message by attribute name
+*/
+struct ldb_message_element *ldb_msg_find_element(const struct ldb_message *msg,
+ const char *attr_name)
+{
+ unsigned int i;
+ for (i=0;i<msg->num_elements;i++) {
+ if (ldb_attr_cmp(msg->elements[i].name, attr_name) == 0) {
+ return &msg->elements[i];
+ }
+ }
+ return NULL;
+}
+
+/*
+ see if two ldb_val structures contain exactly the same data
+ return 1 for a match, 0 for a mis-match
+*/
+int ldb_val_equal_exact(const struct ldb_val *v1, const struct ldb_val *v2)
+{
+ if (v1->length != v2->length) return 0;
+
+ if (v1->length == 0) return 1;
+
+ if (memcmp(v1->data, v2->data, v1->length) == 0) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ find a value in an element
+ assumes case sensitive comparison
+*/
+struct ldb_val *ldb_msg_find_val(const struct ldb_message_element *el,
+ struct ldb_val *val)
+{
+ unsigned int i;
+ for (i=0;i<el->num_values;i++) {
+ if (ldb_val_equal_exact(val, &el->values[i])) {
+ return &el->values[i];
+ }
+ }
+ return NULL;
+}
+
+/*
+ duplicate a ldb_val structure
+*/
+struct ldb_val ldb_val_dup(void *mem_ctx, const struct ldb_val *v)
+{
+ struct ldb_val v2;
+ v2.length = v->length;
+ if (v->data == NULL) {
+ v2.data = NULL;
+ return v2;
+ }
+
+ /* the +1 is to cope with buggy C library routines like strndup
+ that look one byte beyond */
+ v2.data = talloc_array(mem_ctx, uint8_t, v->length+1);
+ if (!v2.data) {
+ v2.length = 0;
+ return v2;
+ }
+
+ memcpy(v2.data, v->data, v->length);
+ ((char *)v2.data)[v->length] = 0;
+ return v2;
+}
+
+/*
+ add an empty element to a message
+*/
+int ldb_msg_add_empty( struct ldb_message *msg,
+ const char *attr_name,
+ int flags,
+ struct ldb_message_element **return_el)
+{
+ struct ldb_message_element *els;
+
+ els = talloc_realloc(msg, msg->elements,
+ struct ldb_message_element, msg->num_elements+1);
+ if (!els) {
+ errno = ENOMEM;
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ els[msg->num_elements].values = NULL;
+ els[msg->num_elements].num_values = 0;
+ els[msg->num_elements].flags = flags;
+ els[msg->num_elements].name = talloc_strdup(els, attr_name);
+ if (!els[msg->num_elements].name) {
+ errno = ENOMEM;
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ msg->elements = els;
+ msg->num_elements++;
+
+ if (return_el) {
+ *return_el = &els[msg->num_elements-1];
+ }
+
+ return LDB_SUCCESS;
+}
+
+/*
+ add an empty element to a message
+*/
+int ldb_msg_add(struct ldb_message *msg,
+ const struct ldb_message_element *el,
+ int flags)
+{
+ /* We have to copy this, just in case *el is a pointer into
+ * what ldb_msg_add_empty() is about to realloc() */
+ struct ldb_message_element el_copy = *el;
+ if (ldb_msg_add_empty(msg, el->name, flags, NULL) != 0) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ msg->elements[msg->num_elements-1] = el_copy;
+ msg->elements[msg->num_elements-1].flags = flags;
+
+ return LDB_SUCCESS;
+}
+
+/*
+ add a value to a message
+*/
+int ldb_msg_add_value(struct ldb_message *msg,
+ const char *attr_name,
+ const struct ldb_val *val,
+ struct ldb_message_element **return_el)
+{
+ struct ldb_message_element *el;
+ struct ldb_val *vals;
+ int ret;
+
+ el = ldb_msg_find_element(msg, attr_name);
+ if (!el) {
+ ret = ldb_msg_add_empty(msg, attr_name, 0, &el);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+
+ vals = talloc_realloc(msg, el->values, struct ldb_val, el->num_values+1);
+ if (!vals) {
+ errno = ENOMEM;
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ el->values = vals;
+ el->values[el->num_values] = *val;
+ el->num_values++;
+
+ if (return_el) {
+ *return_el = el;
+ }
+
+ return LDB_SUCCESS;
+}
+
+
+/*
+ add a value to a message, stealing it into the 'right' place
+*/
+int ldb_msg_add_steal_value(struct ldb_message *msg,
+ const char *attr_name,
+ struct ldb_val *val)
+{
+ int ret;
+ struct ldb_message_element *el;
+
+ ret = ldb_msg_add_value(msg, attr_name, val, &el);
+ if (ret == LDB_SUCCESS) {
+ talloc_steal(el->values, val->data);
+ }
+ return ret;
+}
+
+
+/*
+ add a string element to a message
+*/
+int ldb_msg_add_string(struct ldb_message *msg,
+ const char *attr_name, const char *str)
+{
+ struct ldb_val val;
+
+ val.data = discard_const_p(uint8_t, str);
+ val.length = strlen(str);
+
+ if (val.length == 0) {
+ /* allow empty strings as non-existant attributes */
+ return LDB_SUCCESS;
+ }
+
+ return ldb_msg_add_value(msg, attr_name, &val, NULL);
+}
+
+/*
+ add a string element to a message, stealing it into the 'right' place
+*/
+int ldb_msg_add_steal_string(struct ldb_message *msg,
+ const char *attr_name, char *str)
+{
+ struct ldb_val val;
+
+ val.data = (uint8_t *)str;
+ val.length = strlen(str);
+
+ return ldb_msg_add_steal_value(msg, attr_name, &val);
+}
+
+/*
+ add a printf formatted element to a message
+*/
+int ldb_msg_add_fmt(struct ldb_message *msg,
+ const char *attr_name, const char *fmt, ...)
+{
+ struct ldb_val val;
+ va_list ap;
+ char *str;
+
+ va_start(ap, fmt);
+ str = talloc_vasprintf(msg, fmt, ap);
+ va_end(ap);
+
+ if (str == NULL) return LDB_ERR_OPERATIONS_ERROR;
+
+ val.data = (uint8_t *)str;
+ val.length = strlen(str);
+
+ return ldb_msg_add_steal_value(msg, attr_name, &val);
+}
+
+/*
+ compare two ldb_message_element structures
+ assumes case senistive comparison
+*/
+int ldb_msg_element_compare(struct ldb_message_element *el1,
+ struct ldb_message_element *el2)
+{
+ unsigned int i;
+
+ if (el1->num_values != el2->num_values) {
+ return el1->num_values - el2->num_values;
+ }
+
+ for (i=0;i<el1->num_values;i++) {
+ if (!ldb_msg_find_val(el2, &el1->values[i])) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ compare two ldb_message_element structures
+ comparing by element name
+*/
+int ldb_msg_element_compare_name(struct ldb_message_element *el1,
+ struct ldb_message_element *el2)
+{
+ return ldb_attr_cmp(el1->name, el2->name);
+}
+
+/*
+ convenience functions to return common types from a message
+ these return the first value if the attribute is multi-valued
+*/
+const struct ldb_val *ldb_msg_find_ldb_val(const struct ldb_message *msg,
+ const char *attr_name)
+{
+ struct ldb_message_element *el = ldb_msg_find_element(msg, attr_name);
+ if (!el || el->num_values == 0) {
+ return NULL;
+ }
+ return &el->values[0];
+}
+
+int ldb_msg_find_attr_as_int(const struct ldb_message *msg,
+ const char *attr_name,
+ int default_value)
+{
+ const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
+ if (!v || !v->data) {
+ return default_value;
+ }
+ return strtol((const char *)v->data, NULL, 0);
+}
+
+unsigned int ldb_msg_find_attr_as_uint(const struct ldb_message *msg,
+ const char *attr_name,
+ unsigned int default_value)
+{
+ const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
+ if (!v || !v->data) {
+ return default_value;
+ }
+ return strtoul((const char *)v->data, NULL, 0);
+}
+
+int64_t ldb_msg_find_attr_as_int64(const struct ldb_message *msg,
+ const char *attr_name,
+ int64_t default_value)
+{
+ const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
+ if (!v || !v->data) {
+ return default_value;
+ }
+ return strtoll((const char *)v->data, NULL, 0);
+}
+
+uint64_t ldb_msg_find_attr_as_uint64(const struct ldb_message *msg,
+ const char *attr_name,
+ uint64_t default_value)
+{
+ const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
+ if (!v || !v->data) {
+ return default_value;
+ }
+ return strtoull((const char *)v->data, NULL, 0);
+}
+
+double ldb_msg_find_attr_as_double(const struct ldb_message *msg,
+ const char *attr_name,
+ double default_value)
+{
+ const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
+ if (!v || !v->data) {
+ return default_value;
+ }
+ return strtod((const char *)v->data, NULL);
+}
+
+int ldb_msg_find_attr_as_bool(const struct ldb_message *msg,
+ const char *attr_name,
+ int default_value)
+{
+ const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
+ if (!v || !v->data) {
+ return default_value;
+ }
+ if (v->length == 5 && strncasecmp((const char *)v->data, "FALSE", 5) == 0) {
+ return 0;
+ }
+ if (v->length == 4 && strncasecmp((const char *)v->data, "TRUE", 4) == 0) {
+ return 1;
+ }
+ return default_value;
+}
+
+const char *ldb_msg_find_attr_as_string(const struct ldb_message *msg,
+ const char *attr_name,
+ const char *default_value)
+{
+ const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
+ if (!v || !v->data) {
+ return default_value;
+ }
+ return (const char *)v->data;
+}
+
+struct ldb_dn *ldb_msg_find_attr_as_dn(struct ldb_context *ldb,
+ void *mem_ctx,
+ const struct ldb_message *msg,
+ const char *attr_name)
+{
+ struct ldb_dn *res_dn;
+ const struct ldb_val *v;
+
+ v = ldb_msg_find_ldb_val(msg, attr_name);
+ if (!v || !v->data) {
+ return NULL;
+ }
+ res_dn = ldb_dn_from_ldb_val(mem_ctx, ldb, v);
+ if ( ! ldb_dn_validate(res_dn)) {
+ talloc_free(res_dn);
+ return NULL;
+ }
+ return res_dn;
+}
+
+/*
+ sort the elements of a message by name
+*/
+void ldb_msg_sort_elements(struct ldb_message *msg)
+{
+ qsort(msg->elements, msg->num_elements, sizeof(struct ldb_message_element),
+ (comparison_fn_t)ldb_msg_element_compare_name);
+}
+
+/*
+ shallow copy a message - copying only the elements array so that the caller
+ can safely add new elements without changing the message
+*/
+struct ldb_message *ldb_msg_copy_shallow(TALLOC_CTX *mem_ctx,
+ const struct ldb_message *msg)
+{
+ struct ldb_message *msg2;
+ int i;
+
+ msg2 = talloc(mem_ctx, struct ldb_message);
+ if (msg2 == NULL) return NULL;
+
+ *msg2 = *msg;
+
+ msg2->elements = talloc_array(msg2, struct ldb_message_element,
+ msg2->num_elements);
+ if (msg2->elements == NULL) goto failed;
+
+ for (i=0;i<msg2->num_elements;i++) {
+ msg2->elements[i] = msg->elements[i];
+ }
+
+ return msg2;
+
+failed:
+ talloc_free(msg2);
+ return NULL;
+}
+
+
+/*
+ copy a message, allocating new memory for all parts
+*/
+struct ldb_message *ldb_msg_copy(TALLOC_CTX *mem_ctx,
+ const struct ldb_message *msg)
+{
+ struct ldb_message *msg2;
+ int i, j;
+
+ msg2 = ldb_msg_copy_shallow(mem_ctx, msg);
+ if (msg2 == NULL) return NULL;
+
+ msg2->dn = ldb_dn_copy(msg2, msg2->dn);
+ if (msg2->dn == NULL) goto failed;
+
+ for (i=0;i<msg2->num_elements;i++) {
+ struct ldb_message_element *el = &msg2->elements[i];
+ struct ldb_val *values = el->values;
+ el->name = talloc_strdup(msg2->elements, el->name);
+ if (el->name == NULL) goto failed;
+ el->values = talloc_array(msg2->elements, struct ldb_val, el->num_values);
+ for (j=0;j<el->num_values;j++) {
+ el->values[j] = ldb_val_dup(el->values, &values[j]);
+ if (el->values[j].data == NULL && values[j].length != 0) {
+ goto failed;
+ }
+ }
+ }
+
+ return msg2;
+
+failed:
+ talloc_free(msg2);
+ return NULL;
+}
+
+
+/*
+ canonicalise a message, merging elements of the same name
+*/
+struct ldb_message *ldb_msg_canonicalize(struct ldb_context *ldb,
+ const struct ldb_message *msg)
+{
+ int i;
+ struct ldb_message *msg2;
+
+ msg2 = ldb_msg_copy(ldb, msg);
+ if (msg2 == NULL) return NULL;
+
+ ldb_msg_sort_elements(msg2);
+
+ for (i=1;i<msg2->num_elements;i++) {
+ struct ldb_message_element *el1 = &msg2->elements[i-1];
+ struct ldb_message_element *el2 = &msg2->elements[i];
+ if (ldb_msg_element_compare_name(el1, el2) == 0) {
+ el1->values = talloc_realloc(msg2->elements, el1->values, struct ldb_val,
+ el1->num_values + el2->num_values);
+ if (el1->values == NULL) {
+ return NULL;
+ }
+ memcpy(el1->values + el1->num_values,
+ el2->values,
+ sizeof(struct ldb_val) * el2->num_values);
+ el1->num_values += el2->num_values;
+ talloc_free(discard_const_p(char, el2->name));
+ if (i+1<msg2->num_elements) {
+ memmove(el2, el2+1, sizeof(struct ldb_message_element) *
+ (msg2->num_elements - (i+1)));
+ }
+ msg2->num_elements--;
+ i--;
+ }
+ }
+
+ return msg2;
+}
+
+
+/*
+ return a ldb_message representing the differences between msg1 and msg2. If you
+ then use this in a ldb_modify() call it can be used to save edits to a message
+*/
+struct ldb_message *ldb_msg_diff(struct ldb_context *ldb,
+ struct ldb_message *msg1,
+ struct ldb_message *msg2)
+{
+ struct ldb_message *mod;
+ struct ldb_message_element *el;
+ unsigned int i;
+
+ mod = ldb_msg_new(ldb);
+
+ mod->dn = msg1->dn;
+ mod->num_elements = 0;
+ mod->elements = NULL;
+
+ msg2 = ldb_msg_canonicalize(ldb, msg2);
+ if (msg2 == NULL) {
+ return NULL;
+ }
+
+ /* look in msg2 to find elements that need to be added
+ or modified */
+ for (i=0;i<msg2->num_elements;i++) {
+ el = ldb_msg_find_element(msg1, msg2->elements[i].name);
+
+ if (el && ldb_msg_element_compare(el, &msg2->elements[i]) == 0) {
+ continue;
+ }
+
+ if (ldb_msg_add(mod,
+ &msg2->elements[i],
+ el?LDB_FLAG_MOD_REPLACE:LDB_FLAG_MOD_ADD) != 0) {
+ return NULL;
+ }
+ }
+
+ /* look in msg1 to find elements that need to be deleted */
+ for (i=0;i<msg1->num_elements;i++) {
+ el = ldb_msg_find_element(msg2, msg1->elements[i].name);
+ if (!el) {
+ if (ldb_msg_add_empty(mod,
+ msg1->elements[i].name,
+ LDB_FLAG_MOD_DELETE, NULL) != 0) {
+ return NULL;
+ }
+ }
+ }
+
+ return mod;
+}
+
+int ldb_msg_sanity_check(struct ldb_context *ldb,
+ const struct ldb_message *msg)
+{
+ int i, j;
+
+ /* basic check on DN */
+ if (msg->dn == NULL) {
+ /* TODO: return also an error string */
+ ldb_set_errstring(ldb, "ldb message lacks a DN!");
+ return LDB_ERR_INVALID_DN_SYNTAX;
+ }
+
+ /* basic syntax checks */
+ for (i = 0; i < msg->num_elements; i++) {
+ for (j = 0; j < msg->elements[i].num_values; j++) {
+ if (msg->elements[i].values[j].length == 0) {
+ TALLOC_CTX *mem_ctx = talloc_new(ldb);
+ /* an attribute cannot be empty */
+ /* TODO: return also an error string */
+ ldb_asprintf_errstring(ldb, "Element %s has empty attribute in ldb message (%s)!",
+ msg->elements[i].name,
+ ldb_dn_get_linearized(msg->dn));
+ talloc_free(mem_ctx);
+ return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
+ }
+ }
+ }
+
+ return LDB_SUCCESS;
+}
+
+
+
+
+/*
+ copy an attribute list. This only copies the array, not the elements
+ (ie. the elements are left as the same pointers)
+*/
+const char **ldb_attr_list_copy(TALLOC_CTX *mem_ctx, const char * const *attrs)
+{
+ const char **ret;
+ int i;
+ for (i=0;attrs[i];i++) /* noop */ ;
+ ret = talloc_array(mem_ctx, const char *, i+1);
+ if (ret == NULL) {
+ return NULL;
+ }
+ for (i=0;attrs[i];i++) {
+ ret[i] = attrs[i];
+ }
+ ret[i] = attrs[i];
+ return ret;
+}
+
+
+/*
+ copy an attribute list. This only copies the array, not the elements
+ (ie. the elements are left as the same pointers). The new attribute is added to the list.
+*/
+const char **ldb_attr_list_copy_add(TALLOC_CTX *mem_ctx, const char * const *attrs, const char *new_attr)
+{
+ const char **ret;
+ int i;
+ bool found = false;
+ for (i=0;attrs[i];i++) {
+ if (ldb_attr_cmp(attrs[i], new_attr) == 0) {
+ found = true;
+ }
+ }
+ if (found) {
+ return ldb_attr_list_copy(mem_ctx, attrs);
+ }
+ ret = talloc_array(mem_ctx, const char *, i+2);
+ if (ret == NULL) {
+ return NULL;
+ }
+ for (i=0;attrs[i];i++) {
+ ret[i] = attrs[i];
+ }
+ ret[i] = new_attr;
+ ret[i+1] = NULL;
+ return ret;
+}
+
+
+/*
+ return 1 if an attribute is in a list of attributes, or 0 otherwise
+*/
+int ldb_attr_in_list(const char * const *attrs, const char *attr)
+{
+ int i;
+ for (i=0;attrs && attrs[i];i++) {
+ if (ldb_attr_cmp(attrs[i], attr) == 0) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+/*
+ rename the specified attribute in a search result
+*/
+int ldb_msg_rename_attr(struct ldb_message *msg, const char *attr, const char *replace)
+{
+ struct ldb_message_element *el = ldb_msg_find_element(msg, attr);
+ if (el == NULL) {
+ return LDB_SUCCESS;
+ }
+ el->name = talloc_strdup(msg->elements, replace);
+ if (el->name == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ return LDB_SUCCESS;
+}
+
+
+/*
+ copy the specified attribute in a search result to a new attribute
+*/
+int ldb_msg_copy_attr(struct ldb_message *msg, const char *attr, const char *replace)
+{
+ struct ldb_message_element *el = ldb_msg_find_element(msg, attr);
+ if (el == NULL) {
+ return LDB_SUCCESS;
+ }
+ if (ldb_msg_add(msg, el, 0) != 0) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ return ldb_msg_rename_attr(msg, attr, replace);
+}
+
+/*
+ remove the specified element in a search result
+*/
+void ldb_msg_remove_element(struct ldb_message *msg, struct ldb_message_element *el)
+{
+ int n = (el - msg->elements);
+ if (n != msg->num_elements-1) {
+ memmove(el, el+1, ((msg->num_elements-1) - n)*sizeof(*el));
+ }
+ msg->num_elements--;
+}
+
+
+/*
+ remove the specified attribute in a search result
+*/
+void ldb_msg_remove_attr(struct ldb_message *msg, const char *attr)
+{
+ struct ldb_message_element *el = ldb_msg_find_element(msg, attr);
+ if (el) {
+ ldb_msg_remove_element(msg, el);
+ }
+}
+
+/*
+ return a LDAP formatted GeneralizedTime string
+*/
+char *ldb_timestring(TALLOC_CTX *mem_ctx, time_t t)
+{
+ struct tm *tm = gmtime(&t);
+ char *ts;
+ int r;
+
+ if (!tm) {
+ return NULL;
+ }
+
+ /* we now excatly how long this string will be */
+ ts = talloc_array(mem_ctx, char, 18);
+
+ /* formatted like: 20040408072012.0Z */
+ r = snprintf(ts, 18,
+ "%04u%02u%02u%02u%02u%02u.0Z",
+ tm->tm_year+1900, tm->tm_mon+1,
+ tm->tm_mday, tm->tm_hour, tm->tm_min,
+ tm->tm_sec);
+
+ if (r != 17) {
+ talloc_free(ts);
+ return NULL;
+ }
+
+ return ts;
+}
+
+/*
+ convert a LDAP GeneralizedTime string to a time_t. Return 0 if unable to convert
+*/
+time_t ldb_string_to_time(const char *s)
+{
+ struct tm tm;
+
+ if (s == NULL) return 0;
+
+ memset(&tm, 0, sizeof(tm));
+ if (sscanf(s, "%04u%02u%02u%02u%02u%02u",
+ &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
+ &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
+ return 0;
+ }
+ tm.tm_year -= 1900;
+ tm.tm_mon -= 1;
+
+ return timegm(&tm);
+}
+
+/*
+ return a LDAP formatted UTCTime string
+*/
+char *ldb_timestring_utc(TALLOC_CTX *mem_ctx, time_t t)
+{
+ struct tm *tm = gmtime(&t);
+ char *ts;
+ int r;
+
+ if (!tm) {
+ return NULL;
+ }
+
+ /* we now excatly how long this string will be */
+ ts = talloc_array(mem_ctx, char, 14);
+
+ /* formatted like: 20040408072012.0Z => 040408072012Z */
+ r = snprintf(ts, 14,
+ "%02u%02u%02u%02u%02u%02uZ",
+ (tm->tm_year+1900)%100, tm->tm_mon+1,
+ tm->tm_mday, tm->tm_hour, tm->tm_min,
+ tm->tm_sec);
+
+ if (r != 13) {
+ talloc_free(ts);
+ return NULL;
+ }
+
+ return ts;
+}
+
+/*
+ convert a LDAP UTCTime string to a time_t. Return 0 if unable to convert
+*/
+time_t ldb_string_utc_to_time(const char *s)
+{
+ struct tm tm;
+
+ if (s == NULL) return 0;
+
+ memset(&tm, 0, sizeof(tm));
+ if (sscanf(s, "%02u%02u%02u%02u%02u%02u",
+ &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
+ &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
+ return 0;
+ }
+ if (tm.tm_year < 50) {
+ tm.tm_year += 100;
+ }
+ tm.tm_mon -= 1;
+
+ return timegm(&tm);
+}
+
+
+/*
+ dump a set of results to a file. Useful from within gdb
+*/
+void ldb_dump_results(struct ldb_context *ldb, struct ldb_result *result, FILE *f)
+{
+ int i;
+
+ for (i = 0; i < result->count; i++) {
+ struct ldb_ldif ldif;
+ fprintf(f, "# record %d\n", i+1);
+ ldif.changetype = LDB_CHANGETYPE_NONE;
+ ldif.msg = result->msgs[i];
+ ldb_ldif_write_file(ldb, f, &ldif);
+ }
+}
+
+int ldb_msg_check_string_attribute(const struct ldb_message *msg, const char *name, const char *value)
+{
+ struct ldb_message_element *el;
+ struct ldb_val val;
+
+ el = ldb_msg_find_element(msg, name);
+ if (el == NULL)
+ return 0;
+
+ val.data = discard_const_p(uint8_t, value);
+ val.length = strlen(value);
+
+ if (ldb_msg_find_val(el, &val))
+ return 1;
+
+ return 0;
+}
diff --git a/source4/lib/ldb/common/ldb_parse.c b/source4/lib/ldb/common/ldb_parse.c
new file mode 100644
index 0000000000..654a635abf
--- /dev/null
+++ b/source4/lib/ldb/common/ldb_parse.c
@@ -0,0 +1,819 @@
+/*
+ ldb database library
+
+ Copyright (C) Andrew Tridgell 2004
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb expression parsing
+ *
+ * Description: parse LDAP-like search expressions
+ *
+ * Author: Andrew Tridgell
+ */
+
+/*
+ TODO:
+ - add RFC2254 binary string handling
+ - possibly add ~=, <= and >= handling
+ - expand the test suite
+ - add better parse error handling
+
+*/
+
+#include "ldb_private.h"
+#include "system/locale.h"
+
+/*
+a filter is defined by:
+ <filter> ::= '(' <filtercomp> ')'
+ <filtercomp> ::= <and> | <or> | <not> | <simple>
+ <and> ::= '&' <filterlist>
+ <or> ::= '|' <filterlist>
+ <not> ::= '!' <filter>
+ <filterlist> ::= <filter> | <filter> <filterlist>
+ <simple> ::= <attributetype> <filtertype> <attributevalue>
+ <filtertype> ::= '=' | '~=' | '<=' | '>='
+*/
+
+/*
+ decode a RFC2254 binary string representation of a buffer.
+ Used in LDAP filters.
+*/
+struct ldb_val ldb_binary_decode(void *mem_ctx, const char *str)
+{
+ int i, j;
+ struct ldb_val ret;
+ int slen = str?strlen(str):0;
+
+ ret.data = (uint8_t *)talloc_size(mem_ctx, slen+1);
+ ret.length = 0;
+ if (ret.data == NULL) return ret;
+
+ for (i=j=0;i<slen;i++) {
+ if (str[i] == '\\') {
+ unsigned c;
+ if (sscanf(&str[i+1], "%02X", &c) != 1) {
+ talloc_free(ret.data);
+ memset(&ret, 0, sizeof(ret));
+ return ret;
+ }
+ ((uint8_t *)ret.data)[j++] = c;
+ i += 2;
+ } else {
+ ((uint8_t *)ret.data)[j++] = str[i];
+ }
+ }
+ ret.length = j;
+ ((uint8_t *)ret.data)[j] = 0;
+
+ return ret;
+}
+
+
+/*
+ encode a blob as a RFC2254 binary string, escaping any
+ non-printable or '\' characters
+*/
+char *ldb_binary_encode(void *mem_ctx, struct ldb_val val)
+{
+ int i;
+ char *ret;
+ int len = val.length;
+ unsigned char *buf = val.data;
+
+ for (i=0;i<val.length;i++) {
+ if (!isprint(buf[i]) || strchr(" *()\\&|!\"", buf[i])) {
+ len += 2;
+ }
+ }
+ ret = talloc_array(mem_ctx, char, len+1);
+ if (ret == NULL) return NULL;
+
+ len = 0;
+ for (i=0;i<val.length;i++) {
+ if (!isprint(buf[i]) || strchr(" *()\\&|!\"", buf[i])) {
+ snprintf(ret+len, 4, "\\%02X", buf[i]);
+ len += 3;
+ } else {
+ ret[len++] = buf[i];
+ }
+ }
+
+ ret[len] = 0;
+
+ return ret;
+}
+
+/*
+ encode a string as a RFC2254 binary string, escaping any
+ non-printable or '\' characters. This routine is suitable for use
+ in escaping user data in ldap filters.
+*/
+char *ldb_binary_encode_string(void *mem_ctx, const char *string)
+{
+ struct ldb_val val;
+ val.data = discard_const_p(uint8_t, string);
+ val.length = strlen(string);
+ return ldb_binary_encode(mem_ctx, val);
+}
+
+/* find the first matching wildcard */
+static char *ldb_parse_find_wildcard(char *value)
+{
+ while (*value) {
+ value = strpbrk(value, "\\*");
+ if (value == NULL) return NULL;
+
+ if (value[0] == '\\') {
+ if (value[1] == '\0') return NULL;
+ value += 2;
+ continue;
+ }
+
+ if (value[0] == '*') return value;
+ }
+
+ return NULL;
+}
+
+/* return a NULL terminated list of binary strings representing the value
+ chunks separated by wildcards that makes the value portion of the filter
+*/
+static struct ldb_val **ldb_wildcard_decode(void *mem_ctx, const char *string)
+{
+ struct ldb_val **ret = NULL;
+ int val = 0;
+ char *wc, *str;
+
+ wc = talloc_strdup(mem_ctx, string);
+ if (wc == NULL) return NULL;
+
+ while (wc && *wc) {
+ str = wc;
+ wc = ldb_parse_find_wildcard(str);
+ if (wc && *wc) {
+ if (wc == str) {
+ wc++;
+ continue;
+ }
+ *wc = 0;
+ wc++;
+ }
+
+ ret = talloc_realloc(mem_ctx, ret, struct ldb_val *, val + 2);
+ if (ret == NULL) return NULL;
+
+ ret[val] = talloc(mem_ctx, struct ldb_val);
+ if (ret[val] == NULL) return NULL;
+
+ *(ret[val]) = ldb_binary_decode(mem_ctx, str);
+ if ((ret[val])->data == NULL) return NULL;
+
+ val++;
+ }
+
+ if (ret != NULL) {
+ ret[val] = NULL;
+ }
+
+ return ret;
+}
+
+static struct ldb_parse_tree *ldb_parse_filter(void *mem_ctx, const char **s);
+
+
+/*
+ parse an extended match
+
+ possible forms:
+ (attr:oid:=value)
+ (attr:dn:oid:=value)
+ (attr:dn:=value)
+ (:dn:oid:=value)
+
+ the ':dn' part sets the dnAttributes boolean if present
+ the oid sets the rule_id string
+
+*/
+static struct ldb_parse_tree *ldb_parse_extended(struct ldb_parse_tree *ret,
+ char *attr, char *value)
+{
+ char *p1, *p2;
+
+ ret->operation = LDB_OP_EXTENDED;
+ ret->u.extended.value = ldb_binary_decode(ret, value);
+ if (ret->u.extended.value.data == NULL) goto failed;
+
+ p1 = strchr(attr, ':');
+ if (p1 == NULL) goto failed;
+ p2 = strchr(p1+1, ':');
+
+ *p1 = 0;
+ if (p2) *p2 = 0;
+
+ ret->u.extended.attr = attr;
+ if (strcmp(p1+1, "dn") == 0) {
+ ret->u.extended.dnAttributes = 1;
+ if (p2) {
+ ret->u.extended.rule_id = talloc_strdup(ret, p2+1);
+ if (ret->u.extended.rule_id == NULL) goto failed;
+ } else {
+ ret->u.extended.rule_id = NULL;
+ }
+ } else {
+ ret->u.extended.dnAttributes = 0;
+ ret->u.extended.rule_id = talloc_strdup(ret, p1+1);
+ if (ret->u.extended.rule_id == NULL) goto failed;
+ }
+
+ return ret;
+
+failed:
+ talloc_free(ret);
+ return NULL;
+}
+
+static enum ldb_parse_op ldb_parse_filtertype(void *mem_ctx, char **type, char **value, const char **s)
+{
+ enum ldb_parse_op filter = 0;
+ char *name, *val, *k;
+ const char *p = *s;
+ const char *t, *t1;
+
+ /* retrieve attributetype name */
+ t = p;
+
+ if (*p == '@') { /* for internal attributes the first char can be @ */
+ p++;
+ }
+
+ while ((isascii(*p) && isalnum((unsigned char)*p)) || (*p == '-')) { /* attribute names can only be alphanums */
+ p++;
+ }
+
+ if (*p == ':') { /* but extended searches have : and . chars too */
+ p = strstr(p, ":=");
+ if (p == NULL) { /* malformed attribute name */
+ return 0;
+ }
+ }
+
+ t1 = p;
+
+ while (isspace((unsigned char)*p)) p++;
+
+ if (!strchr("=<>~:", *p)) {
+ return 0;
+ }
+
+ /* save name */
+ name = (char *)talloc_memdup(mem_ctx, t, t1 - t + 1);
+ if (name == NULL) return 0;
+ name[t1 - t] = '\0';
+
+ /* retrieve filtertype */
+
+ if (*p == '=') {
+ filter = LDB_OP_EQUALITY;
+ } else if (*(p + 1) == '=') {
+ switch (*p) {
+ case '<':
+ filter = LDB_OP_LESS;
+ p++;
+ break;
+ case '>':
+ filter = LDB_OP_GREATER;
+ p++;
+ break;
+ case '~':
+ filter = LDB_OP_APPROX;
+ p++;
+ break;
+ case ':':
+ filter = LDB_OP_EXTENDED;
+ p++;
+ break;
+ }
+ }
+ if (!filter) {
+ talloc_free(name);
+ return filter;
+ }
+ p++;
+
+ while (isspace((unsigned char)*p)) p++;
+
+ /* retrieve value */
+ t = p;
+
+ while (*p && ((*p != ')') || ((*p == ')') && (*(p - 1) == '\\')))) p++;
+
+ val = (char *)talloc_memdup(mem_ctx, t, p - t + 1);
+ if (val == NULL) {
+ talloc_free(name);
+ return 0;
+ }
+ val[p - t] = '\0';
+
+ k = &(val[p - t]);
+
+ /* remove trailing spaces from value */
+ while ((k > val) && (isspace((unsigned char)*(k - 1)))) k--;
+ *k = '\0';
+
+ *type = name;
+ *value = val;
+ *s = p;
+ return filter;
+}
+
+/*
+ <simple> ::= <attributetype> <filtertype> <attributevalue>
+*/
+static struct ldb_parse_tree *ldb_parse_simple(void *mem_ctx, const char **s)
+{
+ char *attr, *value;
+ struct ldb_parse_tree *ret;
+ enum ldb_parse_op filtertype;
+
+ ret = talloc(mem_ctx, struct ldb_parse_tree);
+ if (!ret) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ filtertype = ldb_parse_filtertype(ret, &attr, &value, s);
+ if (!filtertype) {
+ talloc_free(ret);
+ return NULL;
+ }
+
+ switch (filtertype) {
+
+ case LDB_OP_PRESENT:
+ ret->operation = LDB_OP_PRESENT;
+ ret->u.present.attr = attr;
+ break;
+
+ case LDB_OP_EQUALITY:
+
+ if (strcmp(value, "*") == 0) {
+ ret->operation = LDB_OP_PRESENT;
+ ret->u.present.attr = attr;
+ break;
+ }
+
+ if (ldb_parse_find_wildcard(value) != NULL) {
+ ret->operation = LDB_OP_SUBSTRING;
+ ret->u.substring.attr = attr;
+ ret->u.substring.start_with_wildcard = 0;
+ ret->u.substring.end_with_wildcard = 0;
+ ret->u.substring.chunks = ldb_wildcard_decode(ret, value);
+ if (ret->u.substring.chunks == NULL){
+ talloc_free(ret);
+ return NULL;
+ }
+ if (value[0] == '*')
+ ret->u.substring.start_with_wildcard = 1;
+ if (value[strlen(value) - 1] == '*')
+ ret->u.substring.end_with_wildcard = 1;
+ talloc_free(value);
+
+ break;
+ }
+
+ ret->operation = LDB_OP_EQUALITY;
+ ret->u.equality.attr = attr;
+ ret->u.equality.value = ldb_binary_decode(ret, value);
+ if (ret->u.equality.value.data == NULL) {
+ talloc_free(ret);
+ return NULL;
+ }
+ talloc_free(value);
+ break;
+
+ case LDB_OP_GREATER:
+ ret->operation = LDB_OP_GREATER;
+ ret->u.comparison.attr = attr;
+ ret->u.comparison.value = ldb_binary_decode(ret, value);
+ if (ret->u.comparison.value.data == NULL) {
+ talloc_free(ret);
+ return NULL;
+ }
+ talloc_free(value);
+ break;
+
+ case LDB_OP_LESS:
+ ret->operation = LDB_OP_LESS;
+ ret->u.comparison.attr = attr;
+ ret->u.comparison.value = ldb_binary_decode(ret, value);
+ if (ret->u.comparison.value.data == NULL) {
+ talloc_free(ret);
+ return NULL;
+ }
+ talloc_free(value);
+ break;
+
+ case LDB_OP_APPROX:
+ ret->operation = LDB_OP_APPROX;
+ ret->u.comparison.attr = attr;
+ ret->u.comparison.value = ldb_binary_decode(ret, value);
+ if (ret->u.comparison.value.data == NULL) {
+ talloc_free(ret);
+ return NULL;
+ }
+ talloc_free(value);
+ break;
+
+ case LDB_OP_EXTENDED:
+
+ ret = ldb_parse_extended(ret, attr, value);
+ break;
+
+ default:
+ talloc_free(ret);
+ return NULL;
+ }
+
+ return ret;
+}
+
+
+/*
+ parse a filterlist
+ <and> ::= '&' <filterlist>
+ <or> ::= '|' <filterlist>
+ <filterlist> ::= <filter> | <filter> <filterlist>
+*/
+static struct ldb_parse_tree *ldb_parse_filterlist(void *mem_ctx, const char **s)
+{
+ struct ldb_parse_tree *ret, *next;
+ enum ldb_parse_op op;
+ const char *p = *s;
+
+ switch (*p) {
+ case '&':
+ op = LDB_OP_AND;
+ break;
+ case '|':
+ op = LDB_OP_OR;
+ break;
+ default:
+ return NULL;
+ }
+ p++;
+
+ while (isspace((unsigned char)*p)) p++;
+
+ ret = talloc(mem_ctx, struct ldb_parse_tree);
+ if (!ret) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ ret->operation = op;
+ ret->u.list.num_elements = 1;
+ ret->u.list.elements = talloc(ret, struct ldb_parse_tree *);
+ if (!ret->u.list.elements) {
+ errno = ENOMEM;
+ talloc_free(ret);
+ return NULL;
+ }
+
+ ret->u.list.elements[0] = ldb_parse_filter(ret->u.list.elements, &p);
+ if (!ret->u.list.elements[0]) {
+ talloc_free(ret);
+ return NULL;
+ }
+
+ while (isspace((unsigned char)*p)) p++;
+
+ while (*p && (next = ldb_parse_filter(ret->u.list.elements, &p))) {
+ struct ldb_parse_tree **e;
+ e = talloc_realloc(ret, ret->u.list.elements,
+ struct ldb_parse_tree *,
+ ret->u.list.num_elements + 1);
+ if (!e) {
+ errno = ENOMEM;
+ talloc_free(ret);
+ return NULL;
+ }
+ ret->u.list.elements = e;
+ ret->u.list.elements[ret->u.list.num_elements] = next;
+ ret->u.list.num_elements++;
+ while (isspace((unsigned char)*p)) p++;
+ }
+
+ *s = p;
+
+ return ret;
+}
+
+
+/*
+ <not> ::= '!' <filter>
+*/
+static struct ldb_parse_tree *ldb_parse_not(void *mem_ctx, const char **s)
+{
+ struct ldb_parse_tree *ret;
+ const char *p = *s;
+
+ if (*p != '!') {
+ return NULL;
+ }
+ p++;
+
+ ret = talloc(mem_ctx, struct ldb_parse_tree);
+ if (!ret) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ ret->operation = LDB_OP_NOT;
+ ret->u.isnot.child = ldb_parse_filter(ret, &p);
+ if (!ret->u.isnot.child) {
+ talloc_free(ret);
+ return NULL;
+ }
+
+ *s = p;
+
+ return ret;
+}
+
+/*
+ parse a filtercomp
+ <filtercomp> ::= <and> | <or> | <not> | <simple>
+*/
+static struct ldb_parse_tree *ldb_parse_filtercomp(void *mem_ctx, const char **s)
+{
+ struct ldb_parse_tree *ret;
+ const char *p = *s;
+
+ while (isspace((unsigned char)*p)) p++;
+
+ switch (*p) {
+ case '&':
+ ret = ldb_parse_filterlist(mem_ctx, &p);
+ break;
+
+ case '|':
+ ret = ldb_parse_filterlist(mem_ctx, &p);
+ break;
+
+ case '!':
+ ret = ldb_parse_not(mem_ctx, &p);
+ break;
+
+ case '(':
+ case ')':
+ return NULL;
+
+ default:
+ ret = ldb_parse_simple(mem_ctx, &p);
+
+ }
+
+ *s = p;
+ return ret;
+}
+
+
+/*
+ <filter> ::= '(' <filtercomp> ')'
+*/
+static struct ldb_parse_tree *ldb_parse_filter(void *mem_ctx, const char **s)
+{
+ struct ldb_parse_tree *ret;
+ const char *p = *s;
+
+ if (*p != '(') {
+ return NULL;
+ }
+ p++;
+
+ ret = ldb_parse_filtercomp(mem_ctx, &p);
+
+ if (*p != ')') {
+ return NULL;
+ }
+ p++;
+
+ while (isspace((unsigned char)*p)) {
+ p++;
+ }
+
+ *s = p;
+
+ return ret;
+}
+
+
+/*
+ main parser entry point. Takes a search string and returns a parse tree
+
+ expression ::= <simple> | <filter>
+*/
+struct ldb_parse_tree *ldb_parse_tree(void *mem_ctx, const char *s)
+{
+ if (s == NULL || *s == 0) {
+ s = "(|(objectClass=*)(distinguishedName=*))";
+ }
+
+ while (isspace((unsigned char)*s)) s++;
+
+ if (*s == '(') {
+ return ldb_parse_filter(mem_ctx, &s);
+ }
+
+ return ldb_parse_simple(mem_ctx, &s);
+}
+
+
+/*
+ construct a ldap parse filter given a parse tree
+*/
+char *ldb_filter_from_tree(void *mem_ctx, struct ldb_parse_tree *tree)
+{
+ char *s, *s2, *ret;
+ int i;
+
+ if (tree == NULL) {
+ return NULL;
+ }
+
+ switch (tree->operation) {
+ case LDB_OP_AND:
+ case LDB_OP_OR:
+ ret = talloc_asprintf(mem_ctx, "(%c", tree->operation==LDB_OP_AND?'&':'|');
+ if (ret == NULL) return NULL;
+ for (i=0;i<tree->u.list.num_elements;i++) {
+ s = ldb_filter_from_tree(mem_ctx, tree->u.list.elements[i]);
+ if (s == NULL) {
+ talloc_free(ret);
+ return NULL;
+ }
+ s2 = talloc_asprintf_append(ret, "%s", s);
+ talloc_free(s);
+ if (s2 == NULL) {
+ talloc_free(ret);
+ return NULL;
+ }
+ ret = s2;
+ }
+ s = talloc_asprintf_append(ret, ")");
+ if (s == NULL) {
+ talloc_free(ret);
+ return NULL;
+ }
+ return s;
+ case LDB_OP_NOT:
+ s = ldb_filter_from_tree(mem_ctx, tree->u.isnot.child);
+ if (s == NULL) return NULL;
+
+ ret = talloc_asprintf(mem_ctx, "(!%s)", s);
+ talloc_free(s);
+ return ret;
+ case LDB_OP_EQUALITY:
+ s = ldb_binary_encode(mem_ctx, tree->u.equality.value);
+ if (s == NULL) return NULL;
+ ret = talloc_asprintf(mem_ctx, "(%s=%s)",
+ tree->u.equality.attr, s);
+ talloc_free(s);
+ return ret;
+ case LDB_OP_SUBSTRING:
+ ret = talloc_asprintf(mem_ctx, "(%s=%s", tree->u.substring.attr,
+ tree->u.substring.start_with_wildcard?"*":"");
+ if (ret == NULL) return NULL;
+ for (i = 0; tree->u.substring.chunks[i]; i++) {
+ s2 = ldb_binary_encode(mem_ctx, *(tree->u.substring.chunks[i]));
+ if (s2 == NULL) {
+ talloc_free(ret);
+ return NULL;
+ }
+ if (tree->u.substring.chunks[i+1] ||
+ tree->u.substring.end_with_wildcard) {
+ s = talloc_asprintf_append(ret, "%s*", s2);
+ } else {
+ s = talloc_asprintf_append(ret, "%s", s2);
+ }
+ if (s == NULL) {
+ talloc_free(ret);
+ return NULL;
+ }
+ ret = s;
+ }
+ s = talloc_asprintf_append(ret, ")");
+ if (s == NULL) {
+ talloc_free(ret);
+ return NULL;
+ }
+ ret = s;
+ return ret;
+ case LDB_OP_GREATER:
+ s = ldb_binary_encode(mem_ctx, tree->u.equality.value);
+ if (s == NULL) return NULL;
+ ret = talloc_asprintf(mem_ctx, "(%s>=%s)",
+ tree->u.equality.attr, s);
+ talloc_free(s);
+ return ret;
+ case LDB_OP_LESS:
+ s = ldb_binary_encode(mem_ctx, tree->u.equality.value);
+ if (s == NULL) return NULL;
+ ret = talloc_asprintf(mem_ctx, "(%s<=%s)",
+ tree->u.equality.attr, s);
+ talloc_free(s);
+ return ret;
+ case LDB_OP_PRESENT:
+ ret = talloc_asprintf(mem_ctx, "(%s=*)", tree->u.present.attr);
+ return ret;
+ case LDB_OP_APPROX:
+ s = ldb_binary_encode(mem_ctx, tree->u.equality.value);
+ if (s == NULL) return NULL;
+ ret = talloc_asprintf(mem_ctx, "(%s~=%s)",
+ tree->u.equality.attr, s);
+ talloc_free(s);
+ return ret;
+ case LDB_OP_EXTENDED:
+ s = ldb_binary_encode(mem_ctx, tree->u.extended.value);
+ if (s == NULL) return NULL;
+ ret = talloc_asprintf(mem_ctx, "(%s%s%s%s:=%s)",
+ tree->u.extended.attr?tree->u.extended.attr:"",
+ tree->u.extended.dnAttributes?":dn":"",
+ tree->u.extended.rule_id?":":"",
+ tree->u.extended.rule_id?tree->u.extended.rule_id:"",
+ s);
+ talloc_free(s);
+ return ret;
+ }
+
+ return NULL;
+}
+
+
+/*
+ replace any occurances of an attribute name in the parse tree with a
+ new name
+*/
+void ldb_parse_tree_attr_replace(struct ldb_parse_tree *tree,
+ const char *attr,
+ const char *replace)
+{
+ int i;
+ switch (tree->operation) {
+ case LDB_OP_AND:
+ case LDB_OP_OR:
+ for (i=0;i<tree->u.list.num_elements;i++) {
+ ldb_parse_tree_attr_replace(tree->u.list.elements[i],
+ attr, replace);
+ }
+ break;
+ case LDB_OP_NOT:
+ ldb_parse_tree_attr_replace(tree->u.isnot.child, attr, replace);
+ break;
+ case LDB_OP_EQUALITY:
+ case LDB_OP_GREATER:
+ case LDB_OP_LESS:
+ case LDB_OP_APPROX:
+ if (ldb_attr_cmp(tree->u.equality.attr, attr) == 0) {
+ tree->u.equality.attr = replace;
+ }
+ break;
+ case LDB_OP_SUBSTRING:
+ if (ldb_attr_cmp(tree->u.substring.attr, attr) == 0) {
+ tree->u.substring.attr = replace;
+ }
+ break;
+ case LDB_OP_PRESENT:
+ if (ldb_attr_cmp(tree->u.present.attr, attr) == 0) {
+ tree->u.present.attr = replace;
+ }
+ break;
+ case LDB_OP_EXTENDED:
+ if (tree->u.extended.attr &&
+ ldb_attr_cmp(tree->u.extended.attr, attr) == 0) {
+ tree->u.extended.attr = replace;
+ }
+ break;
+ }
+}
diff --git a/source4/lib/ldb/common/ldb_utf8.c b/source4/lib/ldb/common/ldb_utf8.c
new file mode 100644
index 0000000000..0a8a89ac1d
--- /dev/null
+++ b/source4/lib/ldb/common/ldb_utf8.c
@@ -0,0 +1,136 @@
+/*
+ ldb database library
+
+ Copyright (C) Andrew Tridgell 2004
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb utf8 handling
+ *
+ * Description: case folding and case comparison for UTF8 strings
+ *
+ * Author: Andrew Tridgell
+ */
+
+#include "ldb_private.h"
+#include "system/locale.h"
+
+
+/*
+ this allow the user to pass in a caseless comparison
+ function to handle utf8 caseless comparisons
+ */
+void ldb_set_utf8_fns(struct ldb_context *ldb,
+ void *context,
+ char *(*casefold)(void *, void *, const char *, size_t))
+{
+ if (context)
+ ldb->utf8_fns.context = context;
+ if (casefold)
+ ldb->utf8_fns.casefold = casefold;
+}
+
+/*
+ a simple case folding function
+ NOTE: does not handle UTF8
+*/
+char *ldb_casefold_default(void *context, void *mem_ctx, const char *s, size_t n)
+{
+ int i;
+ char *ret = talloc_strndup(mem_ctx, s, n);
+ if (!s) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ for (i=0;ret[i];i++) {
+ ret[i] = toupper((unsigned char)ret[i]);
+ }
+ return ret;
+}
+
+void ldb_set_utf8_default(struct ldb_context *ldb)
+{
+ ldb_set_utf8_fns(ldb, NULL, ldb_casefold_default);
+}
+
+char *ldb_casefold(struct ldb_context *ldb, void *mem_ctx, const char *s, size_t n)
+{
+ return ldb->utf8_fns.casefold(ldb->utf8_fns.context, mem_ctx, s, n);
+}
+
+/*
+ check the attribute name is valid according to rfc2251
+ returns 1 if the name is ok
+ */
+
+int ldb_valid_attr_name(const char *s)
+{
+ int i;
+
+ if (!s || !s[0])
+ return 0;
+
+ /* handle special ldb_tdb wildcard */
+ if (strcmp(s, "*") == 0) return 1;
+
+ for (i = 0; s[i]; i++) {
+ if (! isascii(s[i])) {
+ return 0;
+ }
+ if (i == 0) { /* first char must be an alpha (or our special '@' identifier) */
+ if (! (isalpha(s[i]) || (s[i] == '@'))) {
+ return 0;
+ }
+ } else {
+ if (! (isalnum(s[i]) || (s[i] == '-'))) {
+ return 0;
+ }
+ }
+ }
+ return 1;
+}
+
+char *ldb_attr_casefold(void *mem_ctx, const char *s)
+{
+ int i;
+ char *ret = talloc_strdup(mem_ctx, s);
+ if (!ret) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ for (i = 0; ret[i]; i++) {
+ ret[i] = toupper((unsigned char)ret[i]);
+ }
+ return ret;
+}
+
+/*
+ we accept either 'dn' or 'distinguishedName' for a distinguishedName
+*/
+int ldb_attr_dn(const char *attr)
+{
+ if (ldb_attr_cmp(attr, "dn") == 0 ||
+ ldb_attr_cmp(attr, "distinguishedName") == 0) {
+ return 0;
+ }
+ return -1;
+}
diff --git a/source4/lib/ldb/common/qsort.c b/source4/lib/ldb/common/qsort.c
new file mode 100644
index 0000000000..1a0b886b8c
--- /dev/null
+++ b/source4/lib/ldb/common/qsort.c
@@ -0,0 +1,251 @@
+/* Copyright (C) 1991,1992,1996,1997,1999,2004 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Written by Douglas C. Schmidt (schmidt@ics.uci.edu).
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see <http://www.gnu.org/licenses/>. */
+
+/* If you consider tuning this algorithm, you should consult first:
+ Engineering a sort function; Jon Bentley and M. Douglas McIlroy;
+ Software - Practice and Experience; Vol. 23 (11), 1249-1265, 1993. */
+
+/* Modified to be used in samba4 by
+ * Simo Sorce <idra@samba.org> 2005
+ */
+
+#include "ldb_private.h"
+
+/* Byte-wise swap two items of size SIZE. */
+#define SWAP(a, b, size) \
+ do \
+ { \
+ register size_t __size = (size); \
+ register char *__a = (a), *__b = (b); \
+ do \
+ { \
+ char __tmp = *__a; \
+ *__a++ = *__b; \
+ *__b++ = __tmp; \
+ } while (--__size > 0); \
+ } while (0)
+
+/* Discontinue quicksort algorithm when partition gets below this size.
+ This particular magic number was chosen to work best on a Sun 4/260. */
+#define MAX_THRESH 4
+
+/* Stack node declarations used to store unfulfilled partition obligations. */
+typedef struct
+ {
+ char *lo;
+ char *hi;
+ } stack_node;
+
+/* The next 4 #defines implement a very fast in-line stack abstraction. */
+/* The stack needs log (total_elements) entries (we could even subtract
+ log(MAX_THRESH)). Since total_elements has type size_t, we get as
+ upper bound for log (total_elements):
+ bits per byte (CHAR_BIT) * sizeof(size_t). */
+#ifndef CHAR_BIT
+#define CHAR_BIT 8
+#endif
+#define STACK_SIZE (CHAR_BIT * sizeof(size_t))
+#define PUSH(low, high) ((void) ((top->lo = (low)), (top->hi = (high)), ++top))
+#define POP(low, high) ((void) (--top, (low = top->lo), (high = top->hi)))
+#define STACK_NOT_EMPTY (stack < top)
+
+
+/* Order size using quicksort. This implementation incorporates
+ four optimizations discussed in Sedgewick:
+
+ 1. Non-recursive, using an explicit stack of pointer that store the
+ next array partition to sort. To save time, this maximum amount
+ of space required to store an array of SIZE_MAX is allocated on the
+ stack. Assuming a 32-bit (64 bit) integer for size_t, this needs
+ only 32 * sizeof(stack_node) == 256 bytes (for 64 bit: 1024 bytes).
+ Pretty cheap, actually.
+
+ 2. Chose the pivot element using a median-of-three decision tree.
+ This reduces the probability of selecting a bad pivot value and
+ eliminates certain extraneous comparisons.
+
+ 3. Only quicksorts TOTAL_ELEMS / MAX_THRESH partitions, leaving
+ insertion sort to order the MAX_THRESH items within each partition.
+ This is a big win, since insertion sort is faster for small, mostly
+ sorted array segments.
+
+ 4. The larger of the two sub-partitions is always pushed onto the
+ stack first, with the algorithm then concentrating on the
+ smaller partition. This *guarantees* no more than log (total_elems)
+ stack size is needed (actually O(1) in this case)! */
+
+void ldb_qsort (void *const pbase, size_t total_elems, size_t size,
+ void *opaque, ldb_qsort_cmp_fn_t cmp)
+{
+ register char *base_ptr = (char *) pbase;
+
+ const size_t max_thresh = MAX_THRESH * size;
+
+ if (total_elems == 0)
+ /* Avoid lossage with unsigned arithmetic below. */
+ return;
+
+ if (total_elems > MAX_THRESH)
+ {
+ char *lo = base_ptr;
+ char *hi = &lo[size * (total_elems - 1)];
+ stack_node stack[STACK_SIZE];
+ stack_node *top = stack;
+
+ PUSH (NULL, NULL);
+
+ while (STACK_NOT_EMPTY)
+ {
+ char *left_ptr;
+ char *right_ptr;
+
+ /* Select median value from among LO, MID, and HI. Rearrange
+ LO and HI so the three values are sorted. This lowers the
+ probability of picking a pathological pivot value and
+ skips a comparison for both the LEFT_PTR and RIGHT_PTR in
+ the while loops. */
+
+ char *mid = lo + size * ((hi - lo) / size >> 1);
+
+ if ((*cmp) ((void *) mid, (void *) lo, opaque) < 0)
+ SWAP (mid, lo, size);
+ if ((*cmp) ((void *) hi, (void *) mid, opaque) < 0)
+ SWAP (mid, hi, size);
+ else
+ goto jump_over;
+ if ((*cmp) ((void *) mid, (void *) lo, opaque) < 0)
+ SWAP (mid, lo, size);
+ jump_over:;
+
+ left_ptr = lo + size;
+ right_ptr = hi - size;
+
+ /* Here's the famous ``collapse the walls'' section of quicksort.
+ Gotta like those tight inner loops! They are the main reason
+ that this algorithm runs much faster than others. */
+ do
+ {
+ while ((*cmp) ((void *) left_ptr, (void *) mid, opaque) < 0)
+ left_ptr += size;
+
+ while ((*cmp) ((void *) mid, (void *) right_ptr, opaque) < 0)
+ right_ptr -= size;
+
+ if (left_ptr < right_ptr)
+ {
+ SWAP (left_ptr, right_ptr, size);
+ if (mid == left_ptr)
+ mid = right_ptr;
+ else if (mid == right_ptr)
+ mid = left_ptr;
+ left_ptr += size;
+ right_ptr -= size;
+ }
+ else if (left_ptr == right_ptr)
+ {
+ left_ptr += size;
+ right_ptr -= size;
+ break;
+ }
+ }
+ while (left_ptr <= right_ptr);
+
+ /* Set up pointers for next iteration. First determine whether
+ left and right partitions are below the threshold size. If so,
+ ignore one or both. Otherwise, push the larger partition's
+ bounds on the stack and continue sorting the smaller one. */
+
+ if ((size_t) (right_ptr - lo) <= max_thresh)
+ {
+ if ((size_t) (hi - left_ptr) <= max_thresh)
+ /* Ignore both small partitions. */
+ POP (lo, hi);
+ else
+ /* Ignore small left partition. */
+ lo = left_ptr;
+ }
+ else if ((size_t) (hi - left_ptr) <= max_thresh)
+ /* Ignore small right partition. */
+ hi = right_ptr;
+ else if ((right_ptr - lo) > (hi - left_ptr))
+ {
+ /* Push larger left partition indices. */
+ PUSH (lo, right_ptr);
+ lo = left_ptr;
+ }
+ else
+ {
+ /* Push larger right partition indices. */
+ PUSH (left_ptr, hi);
+ hi = right_ptr;
+ }
+ }
+ }
+
+ /* Once the BASE_PTR array is partially sorted by quicksort the rest
+ is completely sorted using insertion sort, since this is efficient
+ for partitions below MAX_THRESH size. BASE_PTR points to the beginning
+ of the array to sort, and END_PTR points at the very last element in
+ the array (*not* one beyond it!). */
+
+#define min(x, y) ((x) < (y) ? (x) : (y))
+
+ {
+ char *const end_ptr = &base_ptr[size * (total_elems - 1)];
+ char *tmp_ptr = base_ptr;
+ char *thresh = min(end_ptr, base_ptr + max_thresh);
+ register char *run_ptr;
+
+ /* Find smallest element in first threshold and place it at the
+ array's beginning. This is the smallest array element,
+ and the operation speeds up insertion sort's inner loop. */
+
+ for (run_ptr = tmp_ptr + size; run_ptr <= thresh; run_ptr += size)
+ if ((*cmp) ((void *) run_ptr, (void *) tmp_ptr, opaque) < 0)
+ tmp_ptr = run_ptr;
+
+ if (tmp_ptr != base_ptr)
+ SWAP (tmp_ptr, base_ptr, size);
+
+ /* Insertion sort, running from left-hand-side up to right-hand-side. */
+
+ run_ptr = base_ptr + size;
+ while ((run_ptr += size) <= end_ptr)
+ {
+ tmp_ptr = run_ptr - size;
+ while ((*cmp) ((void *) run_ptr, (void *) tmp_ptr, opaque) < 0)
+ tmp_ptr -= size;
+
+ tmp_ptr += size;
+ if (tmp_ptr != run_ptr)
+ {
+ char *trav;
+
+ trav = run_ptr + size;
+ while (--trav >= run_ptr)
+ {
+ char c = *trav;
+ char *hi, *lo;
+
+ for (hi = lo = trav; (lo -= size) >= tmp_ptr; hi = lo)
+ *hi = *lo;
+ *hi = c;
+ }
+ }
+ }
+ }
+}
diff --git a/source4/lib/ldb/config.guess b/source4/lib/ldb/config.guess
new file mode 100755
index 0000000000..354dbe175a
--- /dev/null
+++ b/source4/lib/ldb/config.guess
@@ -0,0 +1,1464 @@
+#! /bin/sh
+# Attempt to guess a canonical system name.
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+# 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+
+timestamp='2005-08-03'
+
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <http://www.gnu.org/licenses/>.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+
+# Originally written by Per Bothner <per@bothner.com>.
+# Please send patches to <config-patches@gnu.org>. Submit a context
+# diff and a properly formatted ChangeLog entry.
+#
+# This script attempts to guess a canonical system name similar to
+# config.sub. If it succeeds, it prints the system name on stdout, and
+# exits with 0. Otherwise, it exits with 1.
+#
+# The plan is that this can be called by configure scripts if you
+# don't specify an explicit build system type.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION]
+
+Output the configuration name of the system \`$me' is run on.
+
+Operation modes:
+ -h, --help print this help, then exit
+ -t, --time-stamp print date of last modification, then exit
+ -v, --version print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.guess ($timestamp)
+
+Originally written by Per Bothner.
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+ case $1 in
+ --time-stamp | --time* | -t )
+ echo "$timestamp" ; exit ;;
+ --version | -v )
+ echo "$version" ; exit ;;
+ --help | --h* | -h )
+ echo "$usage"; exit ;;
+ -- ) # Stop option processing
+ shift; break ;;
+ - ) # Use stdin as input.
+ break ;;
+ -* )
+ echo "$me: invalid option $1$help" >&2
+ exit 1 ;;
+ * )
+ break ;;
+ esac
+done
+
+if test $# != 0; then
+ echo "$me: too many arguments$help" >&2
+ exit 1
+fi
+
+trap 'exit 1' 1 2 15
+
+# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
+# compiler to aid in system detection is discouraged as it requires
+# temporary files to be created and, as you can see below, it is a
+# headache to deal with in a portable fashion.
+
+# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
+# use `HOST_CC' if defined, but it is deprecated.
+
+# Portable tmp directory creation inspired by the Autoconf team.
+
+set_cc_for_build='
+trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ;
+trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ;
+: ${TMPDIR=/tmp} ;
+ { tmp=`(umask 077 && mktemp -d -q "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
+ { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } ||
+ { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } ||
+ { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ;
+dummy=$tmp/dummy ;
+tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ;
+case $CC_FOR_BUILD,$HOST_CC,$CC in
+ ,,) echo "int x;" > $dummy.c ;
+ for c in cc gcc c89 c99 ; do
+ if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then
+ CC_FOR_BUILD="$c"; break ;
+ fi ;
+ done ;
+ if test x"$CC_FOR_BUILD" = x ; then
+ CC_FOR_BUILD=no_compiler_found ;
+ fi
+ ;;
+ ,,*) CC_FOR_BUILD=$CC ;;
+ ,*,*) CC_FOR_BUILD=$HOST_CC ;;
+esac ; set_cc_for_build= ;'
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 1994-08-24)
+if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
+ PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+# Note: order is significant - the case branches are not exclusive.
+
+case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
+ *:NetBSD:*:*)
+ # NetBSD (nbsd) targets should (where applicable) match one or
+ # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*,
+ # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently
+ # switched to ELF, *-*-netbsd* would select the old
+ # object file format. This provides both forward
+ # compatibility and a consistent mechanism for selecting the
+ # object file format.
+ #
+ # Note: NetBSD doesn't particularly care about the vendor
+ # portion of the name. We always set it to "unknown".
+ sysctl="sysctl -n hw.machine_arch"
+ UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \
+ /usr/sbin/$sysctl 2>/dev/null || echo unknown)`
+ case "${UNAME_MACHINE_ARCH}" in
+ armeb) machine=armeb-unknown ;;
+ arm*) machine=arm-unknown ;;
+ sh3el) machine=shl-unknown ;;
+ sh3eb) machine=sh-unknown ;;
+ *) machine=${UNAME_MACHINE_ARCH}-unknown ;;
+ esac
+ # The Operating System including object format, if it has switched
+ # to ELF recently, or will in the future.
+ case "${UNAME_MACHINE_ARCH}" in
+ arm*|i386|m68k|ns32k|sh3*|sparc|vax)
+ eval $set_cc_for_build
+ if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep __ELF__ >/dev/null
+ then
+ # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
+ # Return netbsd for either. FIX?
+ os=netbsd
+ else
+ os=netbsdelf
+ fi
+ ;;
+ *)
+ os=netbsd
+ ;;
+ esac
+ # The OS release
+ # Debian GNU/NetBSD machines have a different userland, and
+ # thus, need a distinct triplet. However, they do not need
+ # kernel version information, so it can be replaced with a
+ # suitable tag, in the style of linux-gnu.
+ case "${UNAME_VERSION}" in
+ Debian*)
+ release='-gnu'
+ ;;
+ *)
+ release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+ ;;
+ esac
+ # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
+ # contains redundant information, the shorter form:
+ # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+ echo "${machine}-${os}${release}"
+ exit ;;
+ *:OpenBSD:*:*)
+ UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
+ echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE}
+ exit ;;
+ *:ekkoBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE}
+ exit ;;
+ macppc:MirBSD:*:*)
+ echo powerppc-unknown-mirbsd${UNAME_RELEASE}
+ exit ;;
+ *:MirBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE}
+ exit ;;
+ alpha:OSF1:*:*)
+ case $UNAME_RELEASE in
+ *4.0)
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+ ;;
+ *5.*)
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
+ ;;
+ esac
+ # According to Compaq, /usr/sbin/psrinfo has been available on
+ # OSF/1 and Tru64 systems produced since 1995. I hope that
+ # covers most systems running today. This code pipes the CPU
+ # types through head -n 1, so we only detect the type of CPU 0.
+ ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1`
+ case "$ALPHA_CPU_TYPE" in
+ "EV4 (21064)")
+ UNAME_MACHINE="alpha" ;;
+ "EV4.5 (21064)")
+ UNAME_MACHINE="alpha" ;;
+ "LCA4 (21066/21068)")
+ UNAME_MACHINE="alpha" ;;
+ "EV5 (21164)")
+ UNAME_MACHINE="alphaev5" ;;
+ "EV5.6 (21164A)")
+ UNAME_MACHINE="alphaev56" ;;
+ "EV5.6 (21164PC)")
+ UNAME_MACHINE="alphapca56" ;;
+ "EV5.7 (21164PC)")
+ UNAME_MACHINE="alphapca57" ;;
+ "EV6 (21264)")
+ UNAME_MACHINE="alphaev6" ;;
+ "EV6.7 (21264A)")
+ UNAME_MACHINE="alphaev67" ;;
+ "EV6.8CB (21264C)")
+ UNAME_MACHINE="alphaev68" ;;
+ "EV6.8AL (21264B)")
+ UNAME_MACHINE="alphaev68" ;;
+ "EV6.8CX (21264D)")
+ UNAME_MACHINE="alphaev68" ;;
+ "EV6.9A (21264/EV69A)")
+ UNAME_MACHINE="alphaev69" ;;
+ "EV7 (21364)")
+ UNAME_MACHINE="alphaev7" ;;
+ "EV7.9 (21364A)")
+ UNAME_MACHINE="alphaev79" ;;
+ esac
+ # A Pn.n version is a patched version.
+ # A Vn.n version is a released version.
+ # A Tn.n version is a released field test version.
+ # A Xn.n version is an unreleased experimental baselevel.
+ # 1.2 uses "1.2" for uname -r.
+ echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+ exit ;;
+ Alpha\ *:Windows_NT*:*)
+ # How do we know it's Interix rather than the generic POSIX subsystem?
+ # Should we change UNAME_MACHINE based on the output of uname instead
+ # of the specific Alpha model?
+ echo alpha-pc-interix
+ exit ;;
+ 21064:Windows_NT:50:3)
+ echo alpha-dec-winnt3.5
+ exit ;;
+ Amiga*:UNIX_System_V:4.0:*)
+ echo m68k-unknown-sysv4
+ exit ;;
+ *:[Aa]miga[Oo][Ss]:*:*)
+ echo ${UNAME_MACHINE}-unknown-amigaos
+ exit ;;
+ *:[Mm]orph[Oo][Ss]:*:*)
+ echo ${UNAME_MACHINE}-unknown-morphos
+ exit ;;
+ *:OS/390:*:*)
+ echo i370-ibm-openedition
+ exit ;;
+ *:z/VM:*:*)
+ echo s390-ibm-zvmoe
+ exit ;;
+ *:OS400:*:*)
+ echo powerpc-ibm-os400
+ exit ;;
+ arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+ echo arm-acorn-riscix${UNAME_RELEASE}
+ exit ;;
+ arm:riscos:*:*|arm:RISCOS:*:*)
+ echo arm-unknown-riscos
+ exit ;;
+ SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
+ echo hppa1.1-hitachi-hiuxmpp
+ exit ;;
+ Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
+ # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+ if test "`(/bin/universe) 2>/dev/null`" = att ; then
+ echo pyramid-pyramid-sysv3
+ else
+ echo pyramid-pyramid-bsd
+ fi
+ exit ;;
+ NILE*:*:*:dcosx)
+ echo pyramid-pyramid-svr4
+ exit ;;
+ DRS?6000:unix:4.0:6*)
+ echo sparc-icl-nx6
+ exit ;;
+ DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*)
+ case `/usr/bin/uname -p` in
+ sparc) echo sparc-icl-nx7; exit ;;
+ esac ;;
+ sun4H:SunOS:5.*:*)
+ echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+ echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ i86pc:SunOS:5.*:*)
+ echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ sun4*:SunOS:6*:*)
+ # According to config.sub, this is the proper way to canonicalize
+ # SunOS6. Hard to guess exactly what SunOS6 will be like, but
+ # it's likely to be more like Solaris than SunOS4.
+ echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ sun4*:SunOS:*:*)
+ case "`/usr/bin/arch -k`" in
+ Series*|S4*)
+ UNAME_RELEASE=`uname -v`
+ ;;
+ esac
+ # Japanese Language versions have a version number like `4.1.3-JL'.
+ echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
+ exit ;;
+ sun3*:SunOS:*:*)
+ echo m68k-sun-sunos${UNAME_RELEASE}
+ exit ;;
+ sun*:*:4.2BSD:*)
+ UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+ test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
+ case "`/bin/arch`" in
+ sun3)
+ echo m68k-sun-sunos${UNAME_RELEASE}
+ ;;
+ sun4)
+ echo sparc-sun-sunos${UNAME_RELEASE}
+ ;;
+ esac
+ exit ;;
+ aushp:SunOS:*:*)
+ echo sparc-auspex-sunos${UNAME_RELEASE}
+ exit ;;
+ # The situation for MiNT is a little confusing. The machine name
+ # can be virtually everything (everything which is not
+ # "atarist" or "atariste" at least should have a processor
+ # > m68000). The system name ranges from "MiNT" over "FreeMiNT"
+ # to the lowercase version "mint" (or "freemint"). Finally
+ # the system name "TOS" denotes a system which is actually not
+ # MiNT. But MiNT is downward compatible to TOS, so this should
+ # be no problem.
+ atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit ;;
+ atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit ;;
+ *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit ;;
+ milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+ echo m68k-milan-mint${UNAME_RELEASE}
+ exit ;;
+ hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+ echo m68k-hades-mint${UNAME_RELEASE}
+ exit ;;
+ *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+ echo m68k-unknown-mint${UNAME_RELEASE}
+ exit ;;
+ m68k:machten:*:*)
+ echo m68k-apple-machten${UNAME_RELEASE}
+ exit ;;
+ powerpc:machten:*:*)
+ echo powerpc-apple-machten${UNAME_RELEASE}
+ exit ;;
+ RISC*:Mach:*:*)
+ echo mips-dec-mach_bsd4.3
+ exit ;;
+ RISC*:ULTRIX:*:*)
+ echo mips-dec-ultrix${UNAME_RELEASE}
+ exit ;;
+ VAX*:ULTRIX*:*:*)
+ echo vax-dec-ultrix${UNAME_RELEASE}
+ exit ;;
+ 2020:CLIX:*:* | 2430:CLIX:*:*)
+ echo clipper-intergraph-clix${UNAME_RELEASE}
+ exit ;;
+ mips:*:*:UMIPS | mips:*:*:RISCos)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+#ifdef __cplusplus
+#include <stdio.h> /* for printf() prototype */
+ int main (int argc, char *argv[]) {
+#else
+ int main (argc, argv) int argc; char *argv[]; {
+#endif
+ #if defined (host_mips) && defined (MIPSEB)
+ #if defined (SYSTYPE_SYSV)
+ printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_SVR4)
+ printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+ printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
+ #endif
+ #endif
+ exit (-1);
+ }
+EOF
+ $CC_FOR_BUILD -o $dummy $dummy.c &&
+ dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` &&
+ SYSTEM_NAME=`$dummy $dummyarg` &&
+ { echo "$SYSTEM_NAME"; exit; }
+ echo mips-mips-riscos${UNAME_RELEASE}
+ exit ;;
+ Motorola:PowerMAX_OS:*:*)
+ echo powerpc-motorola-powermax
+ exit ;;
+ Motorola:*:4.3:PL8-*)
+ echo powerpc-harris-powermax
+ exit ;;
+ Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
+ echo powerpc-harris-powermax
+ exit ;;
+ Night_Hawk:Power_UNIX:*:*)
+ echo powerpc-harris-powerunix
+ exit ;;
+ m88k:CX/UX:7*:*)
+ echo m88k-harris-cxux7
+ exit ;;
+ m88k:*:4*:R4*)
+ echo m88k-motorola-sysv4
+ exit ;;
+ m88k:*:3*:R3*)
+ echo m88k-motorola-sysv3
+ exit ;;
+ AViiON:dgux:*:*)
+ # DG/UX returns AViiON for all architectures
+ UNAME_PROCESSOR=`/usr/bin/uname -p`
+ if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
+ then
+ if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
+ [ ${TARGET_BINARY_INTERFACE}x = x ]
+ then
+ echo m88k-dg-dgux${UNAME_RELEASE}
+ else
+ echo m88k-dg-dguxbcs${UNAME_RELEASE}
+ fi
+ else
+ echo i586-dg-dgux${UNAME_RELEASE}
+ fi
+ exit ;;
+ M88*:DolphinOS:*:*) # DolphinOS (SVR3)
+ echo m88k-dolphin-sysv3
+ exit ;;
+ M88*:*:R3*:*)
+ # Delta 88k system running SVR3
+ echo m88k-motorola-sysv3
+ exit ;;
+ XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+ echo m88k-tektronix-sysv3
+ exit ;;
+ Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+ echo m68k-tektronix-bsd
+ exit ;;
+ *:IRIX*:*:*)
+ echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
+ exit ;;
+ ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+ echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id
+ exit ;; # Note that: echo "'`uname -s`'" gives 'AIX '
+ i*86:AIX:*:*)
+ echo i386-ibm-aix
+ exit ;;
+ ia64:AIX:*:*)
+ if [ -x /usr/bin/oslevel ] ; then
+ IBM_REV=`/usr/bin/oslevel`
+ else
+ IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+ fi
+ echo ${UNAME_MACHINE}-ibm-aix${IBM_REV}
+ exit ;;
+ *:AIX:2:3)
+ if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <sys/systemcfg.h>
+
+ main()
+ {
+ if (!__power_pc())
+ exit(1);
+ puts("powerpc-ibm-aix3.2.5");
+ exit(0);
+ }
+EOF
+ if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy`
+ then
+ echo "$SYSTEM_NAME"
+ else
+ echo rs6000-ibm-aix3.2.5
+ fi
+ elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+ echo rs6000-ibm-aix3.2.4
+ else
+ echo rs6000-ibm-aix3.2
+ fi
+ exit ;;
+ *:AIX:*:[45])
+ IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
+ if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
+ IBM_ARCH=rs6000
+ else
+ IBM_ARCH=powerpc
+ fi
+ if [ -x /usr/bin/oslevel ] ; then
+ IBM_REV=`/usr/bin/oslevel`
+ else
+ IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+ fi
+ echo ${IBM_ARCH}-ibm-aix${IBM_REV}
+ exit ;;
+ *:AIX:*:*)
+ echo rs6000-ibm-aix
+ exit ;;
+ ibmrt:4.4BSD:*|romp-ibm:BSD:*)
+ echo romp-ibm-bsd4.4
+ exit ;;
+ ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and
+ echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to
+ exit ;; # report: romp-ibm BSD 4.3
+ *:BOSX:*:*)
+ echo rs6000-bull-bosx
+ exit ;;
+ DPX/2?00:B.O.S.:*:*)
+ echo m68k-bull-sysv3
+ exit ;;
+ 9000/[34]??:4.3bsd:1.*:*)
+ echo m68k-hp-bsd
+ exit ;;
+ hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+ echo m68k-hp-bsd4.4
+ exit ;;
+ 9000/[34678]??:HP-UX:*:*)
+ HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+ case "${UNAME_MACHINE}" in
+ 9000/31? ) HP_ARCH=m68000 ;;
+ 9000/[34]?? ) HP_ARCH=m68k ;;
+ 9000/[678][0-9][0-9])
+ if [ -x /usr/bin/getconf ]; then
+ sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
+ sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
+ case "${sc_cpu_version}" in
+ 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
+ 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
+ 532) # CPU_PA_RISC2_0
+ case "${sc_kernel_bits}" in
+ 32) HP_ARCH="hppa2.0n" ;;
+ 64) HP_ARCH="hppa2.0w" ;;
+ '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20
+ esac ;;
+ esac
+ fi
+ if [ "${HP_ARCH}" = "" ]; then
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+
+ #define _HPUX_SOURCE
+ #include <stdlib.h>
+ #include <unistd.h>
+
+ int main ()
+ {
+ #if defined(_SC_KERNEL_BITS)
+ long bits = sysconf(_SC_KERNEL_BITS);
+ #endif
+ long cpu = sysconf (_SC_CPU_VERSION);
+
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+ case CPU_PA_RISC2_0:
+ #if defined(_SC_KERNEL_BITS)
+ switch (bits)
+ {
+ case 64: puts ("hppa2.0w"); break;
+ case 32: puts ("hppa2.0n"); break;
+ default: puts ("hppa2.0"); break;
+ } break;
+ #else /* !defined(_SC_KERNEL_BITS) */
+ puts ("hppa2.0"); break;
+ #endif
+ default: puts ("hppa1.0"); break;
+ }
+ exit (0);
+ }
+EOF
+ (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
+ test -z "$HP_ARCH" && HP_ARCH=hppa
+ fi ;;
+ esac
+ if [ ${HP_ARCH} = "hppa2.0w" ]
+ then
+ eval $set_cc_for_build
+
+ # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating
+ # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler
+ # generating 64-bit code. GNU and HP use different nomenclature:
+ #
+ # $ CC_FOR_BUILD=cc ./config.guess
+ # => hppa2.0w-hp-hpux11.23
+ # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess
+ # => hppa64-hp-hpux11.23
+
+ if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) |
+ grep __LP64__ >/dev/null
+ then
+ HP_ARCH="hppa2.0w"
+ else
+ HP_ARCH="hppa64"
+ fi
+ fi
+ echo ${HP_ARCH}-hp-hpux${HPUX_REV}
+ exit ;;
+ ia64:HP-UX:*:*)
+ HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+ echo ia64-hp-hpux${HPUX_REV}
+ exit ;;
+ 3050*:HI-UX:*:*)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <unistd.h>
+ int
+ main ()
+ {
+ long cpu = sysconf (_SC_CPU_VERSION);
+ /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+ true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct
+ results, however. */
+ if (CPU_IS_PA_RISC (cpu))
+ {
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+ default: puts ("hppa-hitachi-hiuxwe2"); break;
+ }
+ }
+ else if (CPU_IS_HP_MC68K (cpu))
+ puts ("m68k-hitachi-hiuxwe2");
+ else puts ("unknown-hitachi-hiuxwe2");
+ exit (0);
+ }
+EOF
+ $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` &&
+ { echo "$SYSTEM_NAME"; exit; }
+ echo unknown-hitachi-hiuxwe2
+ exit ;;
+ 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
+ echo hppa1.1-hp-bsd
+ exit ;;
+ 9000/8??:4.3bsd:*:*)
+ echo hppa1.0-hp-bsd
+ exit ;;
+ *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
+ echo hppa1.0-hp-mpeix
+ exit ;;
+ hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
+ echo hppa1.1-hp-osf
+ exit ;;
+ hp8??:OSF1:*:*)
+ echo hppa1.0-hp-osf
+ exit ;;
+ i*86:OSF1:*:*)
+ if [ -x /usr/sbin/sysversion ] ; then
+ echo ${UNAME_MACHINE}-unknown-osf1mk
+ else
+ echo ${UNAME_MACHINE}-unknown-osf1
+ fi
+ exit ;;
+ parisc*:Lites*:*:*)
+ echo hppa1.1-hp-lites
+ exit ;;
+ C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+ echo c1-convex-bsd
+ exit ;;
+ C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit ;;
+ C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+ echo c34-convex-bsd
+ exit ;;
+ C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+ echo c38-convex-bsd
+ exit ;;
+ C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+ echo c4-convex-bsd
+ exit ;;
+ CRAY*Y-MP:*:*:*)
+ echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*[A-Z]90:*:*:*)
+ echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
+ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
+ -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*TS:*:*:*)
+ echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*T3E:*:*:*)
+ echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*SV1:*:*:*)
+ echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ *:UNICOS/mp:*:*)
+ echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
+ FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+ FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+ FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
+ echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit ;;
+ 5000:UNIX_System_V:4.*:*)
+ FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+ FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'`
+ echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit ;;
+ i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
+ echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
+ exit ;;
+ sparc*:BSD/OS:*:*)
+ echo sparc-unknown-bsdi${UNAME_RELEASE}
+ exit ;;
+ *:BSD/OS:*:*)
+ echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
+ exit ;;
+ *:FreeBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+ exit ;;
+ i*:CYGWIN*:*)
+ echo ${UNAME_MACHINE}-pc-cygwin
+ exit ;;
+ i*:MINGW*:*)
+ echo ${UNAME_MACHINE}-pc-mingw32
+ exit ;;
+ i*:windows32*:*)
+ # uname -m includes "-pc" on this system.
+ echo ${UNAME_MACHINE}-mingw32
+ exit ;;
+ i*:PW*:*)
+ echo ${UNAME_MACHINE}-pc-pw32
+ exit ;;
+ x86:Interix*:[34]*)
+ echo i586-pc-interix${UNAME_RELEASE}|sed -e 's/\..*//'
+ exit ;;
+ [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)
+ echo i${UNAME_MACHINE}-pc-mks
+ exit ;;
+ i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
+ # How do we know it's Interix rather than the generic POSIX subsystem?
+ # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
+ # UNAME_MACHINE based on the output of uname instead of i386?
+ echo i586-pc-interix
+ exit ;;
+ i*:UWIN*:*)
+ echo ${UNAME_MACHINE}-pc-uwin
+ exit ;;
+ amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*)
+ echo x86_64-unknown-cygwin
+ exit ;;
+ p*:CYGWIN*:*)
+ echo powerpcle-unknown-cygwin
+ exit ;;
+ prep*:SunOS:5.*:*)
+ echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ *:GNU:*:*)
+ # the GNU system
+ echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
+ exit ;;
+ *:GNU/*:*:*)
+ # other systems with GNU libc and userland
+ echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu
+ exit ;;
+ i*86:Minix:*:*)
+ echo ${UNAME_MACHINE}-pc-minix
+ exit ;;
+ arm*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ cris:Linux:*:*)
+ echo cris-axis-linux-gnu
+ exit ;;
+ crisv32:Linux:*:*)
+ echo crisv32-axis-linux-gnu
+ exit ;;
+ frv:Linux:*:*)
+ echo frv-unknown-linux-gnu
+ exit ;;
+ ia64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ m32r*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ m68*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ mips:Linux:*:*)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #undef CPU
+ #undef mips
+ #undef mipsel
+ #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+ CPU=mipsel
+ #else
+ #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+ CPU=mips
+ #else
+ CPU=
+ #endif
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=`
+ test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; }
+ ;;
+ mips64:Linux:*:*)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #undef CPU
+ #undef mips64
+ #undef mips64el
+ #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+ CPU=mips64el
+ #else
+ #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+ CPU=mips64
+ #else
+ CPU=
+ #endif
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=`
+ test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; }
+ ;;
+ or32:Linux:*:*)
+ echo or32-unknown-linux-gnu
+ exit ;;
+ ppc:Linux:*:*)
+ echo powerpc-unknown-linux-gnu
+ exit ;;
+ ppc64:Linux:*:*)
+ echo powerpc64-unknown-linux-gnu
+ exit ;;
+ alpha:Linux:*:*)
+ case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
+ EV5) UNAME_MACHINE=alphaev5 ;;
+ EV56) UNAME_MACHINE=alphaev56 ;;
+ PCA56) UNAME_MACHINE=alphapca56 ;;
+ PCA57) UNAME_MACHINE=alphapca56 ;;
+ EV6) UNAME_MACHINE=alphaev6 ;;
+ EV67) UNAME_MACHINE=alphaev67 ;;
+ EV68*) UNAME_MACHINE=alphaev68 ;;
+ esac
+ objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null
+ if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
+ echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
+ exit ;;
+ parisc:Linux:*:* | hppa:Linux:*:*)
+ # Look for CPU level
+ case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
+ PA7*) echo hppa1.1-unknown-linux-gnu ;;
+ PA8*) echo hppa2.0-unknown-linux-gnu ;;
+ *) echo hppa-unknown-linux-gnu ;;
+ esac
+ exit ;;
+ parisc64:Linux:*:* | hppa64:Linux:*:*)
+ echo hppa64-unknown-linux-gnu
+ exit ;;
+ s390:Linux:*:* | s390x:Linux:*:*)
+ echo ${UNAME_MACHINE}-ibm-linux
+ exit ;;
+ sh64*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ sh*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ sparc:Linux:*:* | sparc64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ x86_64:Linux:*:*)
+ echo x86_64-unknown-linux-gnu
+ exit ;;
+ i*86:Linux:*:*)
+ # The BFD linker knows what the default object file format is, so
+ # first see if it will tell us. cd to the root directory to prevent
+ # problems with other programs or directories called `ld' in the path.
+ # Set LC_ALL=C to ensure ld outputs messages in English.
+ ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \
+ | sed -ne '/supported targets:/!d
+ s/[ ][ ]*/ /g
+ s/.*supported targets: *//
+ s/ .*//
+ p'`
+ case "$ld_supported_targets" in
+ elf32-i386)
+ TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu"
+ ;;
+ a.out-i386-linux)
+ echo "${UNAME_MACHINE}-pc-linux-gnuaout"
+ exit ;;
+ coff-i386)
+ echo "${UNAME_MACHINE}-pc-linux-gnucoff"
+ exit ;;
+ "")
+ # Either a pre-BFD a.out linker (linux-gnuoldld) or
+ # one that does not give us useful --help.
+ echo "${UNAME_MACHINE}-pc-linux-gnuoldld"
+ exit ;;
+ esac
+ # Determine whether the default compiler is a.out or elf
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <features.h>
+ #ifdef __ELF__
+ # ifdef __GLIBC__
+ # if __GLIBC__ >= 2
+ LIBC=gnu
+ # else
+ LIBC=gnulibc1
+ # endif
+ # else
+ LIBC=gnulibc1
+ # endif
+ #else
+ #ifdef __INTEL_COMPILER
+ LIBC=gnu
+ #else
+ LIBC=gnuaout
+ #endif
+ #endif
+ #ifdef __dietlibc__
+ LIBC=dietlibc
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^LIBC=`
+ test x"${LIBC}" != x && {
+ echo "${UNAME_MACHINE}-pc-linux-${LIBC}"
+ exit
+ }
+ test x"${TENTATIVE}" != x && { echo "${TENTATIVE}"; exit; }
+ ;;
+ i*86:DYNIX/ptx:4*:*)
+ # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
+ # earlier versions are messed up and put the nodename in both
+ # sysname and nodename.
+ echo i386-sequent-sysv4
+ exit ;;
+ i*86:UNIX_SV:4.2MP:2.*)
+ # Unixware is an offshoot of SVR4, but it has its own version
+ # number series starting with 2...
+ # I am not positive that other SVR4 systems won't match this,
+ # I just have to hope. -- rms.
+ # Use sysv4.2uw... so that sysv4* matches it.
+ echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
+ exit ;;
+ i*86:OS/2:*:*)
+ # If we were able to find `uname', then EMX Unix compatibility
+ # is probably installed.
+ echo ${UNAME_MACHINE}-pc-os2-emx
+ exit ;;
+ i*86:XTS-300:*:STOP)
+ echo ${UNAME_MACHINE}-unknown-stop
+ exit ;;
+ i*86:atheos:*:*)
+ echo ${UNAME_MACHINE}-unknown-atheos
+ exit ;;
+ i*86:syllable:*:*)
+ echo ${UNAME_MACHINE}-pc-syllable
+ exit ;;
+ i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*)
+ echo i386-unknown-lynxos${UNAME_RELEASE}
+ exit ;;
+ i*86:*DOS:*:*)
+ echo ${UNAME_MACHINE}-pc-msdosdjgpp
+ exit ;;
+ i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)
+ UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
+ if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+ echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}
+ else
+ echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
+ fi
+ exit ;;
+ i*86:*:5:[678]*)
+ # UnixWare 7.x, OpenUNIX and OpenServer 6.
+ case `/bin/uname -X | grep "^Machine"` in
+ *486*) UNAME_MACHINE=i486 ;;
+ *Pentium) UNAME_MACHINE=i586 ;;
+ *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
+ esac
+ echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
+ exit ;;
+ i*86:*:3.2:*)
+ if test -f /usr/options/cb.name; then
+ UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+ echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
+ elif /bin/uname -X 2>/dev/null >/dev/null ; then
+ UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
+ (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
+ (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
+ && UNAME_MACHINE=i586
+ (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
+ && UNAME_MACHINE=i686
+ (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
+ && UNAME_MACHINE=i686
+ echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
+ else
+ echo ${UNAME_MACHINE}-pc-sysv32
+ fi
+ exit ;;
+ pc:*:*:*)
+ # Left here for compatibility:
+ # uname -m prints for DJGPP always 'pc', but it prints nothing about
+ # the processor, so we play safe by assuming i386.
+ echo i386-pc-msdosdjgpp
+ exit ;;
+ Intel:Mach:3*:*)
+ echo i386-pc-mach3
+ exit ;;
+ paragon:*:*:*)
+ echo i860-intel-osf1
+ exit ;;
+ i860:*:4.*:*) # i860-SVR4
+ if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+ echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
+ else # Add other i860-SVR4 vendors below as they are discovered.
+ echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4
+ fi
+ exit ;;
+ mini*:CTIX:SYS*5:*)
+ # "miniframe"
+ echo m68010-convergent-sysv
+ exit ;;
+ mc68k:UNIX:SYSTEM5:3.51m)
+ echo m68k-convergent-sysv
+ exit ;;
+ M680?0:D-NIX:5.3:*)
+ echo m68k-diab-dnix
+ exit ;;
+ M68*:*:R3V[5678]*:*)
+ test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;;
+ 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0)
+ OS_REL=''
+ test -r /etc/.relid \
+ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && { echo i486-ncr-sysv4.3${OS_REL}; exit; }
+ /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
+ 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && { echo i486-ncr-sysv4; exit; } ;;
+ m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
+ echo m68k-unknown-lynxos${UNAME_RELEASE}
+ exit ;;
+ mc68030:UNIX_System_V:4.*:*)
+ echo m68k-atari-sysv4
+ exit ;;
+ TSUNAMI:LynxOS:2.*:*)
+ echo sparc-unknown-lynxos${UNAME_RELEASE}
+ exit ;;
+ rs6000:LynxOS:2.*:*)
+ echo rs6000-unknown-lynxos${UNAME_RELEASE}
+ exit ;;
+ PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*)
+ echo powerpc-unknown-lynxos${UNAME_RELEASE}
+ exit ;;
+ SM[BE]S:UNIX_SV:*:*)
+ echo mips-dde-sysv${UNAME_RELEASE}
+ exit ;;
+ RM*:ReliantUNIX-*:*:*)
+ echo mips-sni-sysv4
+ exit ;;
+ RM*:SINIX-*:*:*)
+ echo mips-sni-sysv4
+ exit ;;
+ *:SINIX-*:*:*)
+ if uname -p 2>/dev/null >/dev/null ; then
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
+ echo ${UNAME_MACHINE}-sni-sysv4
+ else
+ echo ns32k-sni-sysv
+ fi
+ exit ;;
+ PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+ # says <Richard.M.Bartel@ccMail.Census.GOV>
+ echo i586-unisys-sysv4
+ exit ;;
+ *:UNIX_System_V:4*:FTX*)
+ # From Gerald Hewes <hewes@openmarket.com>.
+ # How about differentiating between stratus architectures? -djm
+ echo hppa1.1-stratus-sysv4
+ exit ;;
+ *:*:*:FTX*)
+ # From seanf@swdc.stratus.com.
+ echo i860-stratus-sysv4
+ exit ;;
+ i*86:VOS:*:*)
+ # From Paul.Green@stratus.com.
+ echo ${UNAME_MACHINE}-stratus-vos
+ exit ;;
+ *:VOS:*:*)
+ # From Paul.Green@stratus.com.
+ echo hppa1.1-stratus-vos
+ exit ;;
+ mc68*:A/UX:*:*)
+ echo m68k-apple-aux${UNAME_RELEASE}
+ exit ;;
+ news*:NEWS-OS:6*:*)
+ echo mips-sony-newsos6
+ exit ;;
+ R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
+ if [ -d /usr/nec ]; then
+ echo mips-nec-sysv${UNAME_RELEASE}
+ else
+ echo mips-unknown-sysv${UNAME_RELEASE}
+ fi
+ exit ;;
+ BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only.
+ echo powerpc-be-beos
+ exit ;;
+ BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only.
+ echo powerpc-apple-beos
+ exit ;;
+ BePC:BeOS:*:*) # BeOS running on Intel PC compatible.
+ echo i586-pc-beos
+ exit ;;
+ SX-4:SUPER-UX:*:*)
+ echo sx4-nec-superux${UNAME_RELEASE}
+ exit ;;
+ SX-5:SUPER-UX:*:*)
+ echo sx5-nec-superux${UNAME_RELEASE}
+ exit ;;
+ SX-6:SUPER-UX:*:*)
+ echo sx6-nec-superux${UNAME_RELEASE}
+ exit ;;
+ Power*:Rhapsody:*:*)
+ echo powerpc-apple-rhapsody${UNAME_RELEASE}
+ exit ;;
+ *:Rhapsody:*:*)
+ echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
+ exit ;;
+ *:Darwin:*:*)
+ UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
+ case $UNAME_PROCESSOR in
+ *86) UNAME_PROCESSOR=i686 ;;
+ unknown) UNAME_PROCESSOR=powerpc ;;
+ esac
+ echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
+ exit ;;
+ *:procnto*:*:* | *:QNX:[0123456789]*:*)
+ UNAME_PROCESSOR=`uname -p`
+ if test "$UNAME_PROCESSOR" = "x86"; then
+ UNAME_PROCESSOR=i386
+ UNAME_MACHINE=pc
+ fi
+ echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE}
+ exit ;;
+ *:QNX:*:4*)
+ echo i386-pc-qnx
+ exit ;;
+ NSE-?:NONSTOP_KERNEL:*:*)
+ echo nse-tandem-nsk${UNAME_RELEASE}
+ exit ;;
+ NSR-?:NONSTOP_KERNEL:*:*)
+ echo nsr-tandem-nsk${UNAME_RELEASE}
+ exit ;;
+ *:NonStop-UX:*:*)
+ echo mips-compaq-nonstopux
+ exit ;;
+ BS2000:POSIX*:*:*)
+ echo bs2000-siemens-sysv
+ exit ;;
+ DS/*:UNIX_System_V:*:*)
+ echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
+ exit ;;
+ *:Plan9:*:*)
+ # "uname -m" is not consistent, so use $cputype instead. 386
+ # is converted to i386 for consistency with other x86
+ # operating systems.
+ if test "$cputype" = "386"; then
+ UNAME_MACHINE=i386
+ else
+ UNAME_MACHINE="$cputype"
+ fi
+ echo ${UNAME_MACHINE}-unknown-plan9
+ exit ;;
+ *:TOPS-10:*:*)
+ echo pdp10-unknown-tops10
+ exit ;;
+ *:TENEX:*:*)
+ echo pdp10-unknown-tenex
+ exit ;;
+ KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
+ echo pdp10-dec-tops20
+ exit ;;
+ XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
+ echo pdp10-xkl-tops20
+ exit ;;
+ *:TOPS-20:*:*)
+ echo pdp10-unknown-tops20
+ exit ;;
+ *:ITS:*:*)
+ echo pdp10-unknown-its
+ exit ;;
+ SEI:*:*:SEIUX)
+ echo mips-sei-seiux${UNAME_RELEASE}
+ exit ;;
+ *:DragonFly:*:*)
+ echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+ exit ;;
+ *:*VMS:*:*)
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
+ case "${UNAME_MACHINE}" in
+ A*) echo alpha-dec-vms ; exit ;;
+ I*) echo ia64-dec-vms ; exit ;;
+ V*) echo vax-dec-vms ; exit ;;
+ esac ;;
+ *:XENIX:*:SysV)
+ echo i386-pc-xenix
+ exit ;;
+ i*86:skyos:*:*)
+ echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//'
+ exit ;;
+esac
+
+#echo '(No uname command or uname output not recognized.)' 1>&2
+#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
+
+eval $set_cc_for_build
+cat >$dummy.c <<EOF
+#ifdef _SEQUENT_
+# include <sys/types.h>
+# include <sys/utsname.h>
+#endif
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+ /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed,
+ I don't know.... */
+ printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+ printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+ "4"
+#else
+ ""
+#endif
+ ); exit (0);
+#endif
+#endif
+
+#if defined (__arm) && defined (__acorn) && defined (__unix)
+ printf ("arm-acorn-riscix\n"); exit (0);
+#endif
+
+#if defined (hp300) && !defined (hpux)
+ printf ("m68k-hp-bsd\n"); exit (0);
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+ int version;
+ version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+ if (version < 4)
+ printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+ else
+ printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+ exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+ printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+ printf ("ns32k-encore-mach\n"); exit (0);
+#else
+ printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+ printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+ printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+ printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+ struct utsname un;
+
+ uname(&un);
+
+ if (strncmp(un.version, "V2", 2) == 0) {
+ printf ("i386-sequent-ptx2\n"); exit (0);
+ }
+ if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+ printf ("i386-sequent-ptx1\n"); exit (0);
+ }
+ printf ("i386-sequent-ptx\n"); exit (0);
+
+#endif
+
+#if defined (vax)
+# if !defined (ultrix)
+# include <sys/param.h>
+# if defined (BSD)
+# if BSD == 43
+ printf ("vax-dec-bsd4.3\n"); exit (0);
+# else
+# if BSD == 199006
+ printf ("vax-dec-bsd4.3reno\n"); exit (0);
+# else
+ printf ("vax-dec-bsd\n"); exit (0);
+# endif
+# endif
+# else
+ printf ("vax-dec-bsd\n"); exit (0);
+# endif
+# else
+ printf ("vax-dec-ultrix\n"); exit (0);
+# endif
+#endif
+
+#if defined (alliant) && defined (i860)
+ printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+ exit (1);
+}
+EOF
+
+$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` &&
+ { echo "$SYSTEM_NAME"; exit; }
+
+# Apollos put the system type in the environment.
+
+test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; }
+
+# Convex versions that predate uname can use getsysinfo(1)
+
+if [ -x /usr/convex/getsysinfo ]
+then
+ case `getsysinfo -f cpu_type` in
+ c1*)
+ echo c1-convex-bsd
+ exit ;;
+ c2*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit ;;
+ c34*)
+ echo c34-convex-bsd
+ exit ;;
+ c38*)
+ echo c38-convex-bsd
+ exit ;;
+ c4*)
+ echo c4-convex-bsd
+ exit ;;
+ esac
+fi
+
+cat >&2 <<EOF
+$0: unable to guess system type
+
+This script, last modified $timestamp, has failed to recognize
+the operating system you are using. It is advised that you
+download the most up to date version of the config scripts from
+
+ http://savannah.gnu.org/cgi-bin/viewcvs/*checkout*/config/config/config.guess
+and
+ http://savannah.gnu.org/cgi-bin/viewcvs/*checkout*/config/config/config.sub
+
+If the version you run ($0) is already up to date, please
+send the following data and any information you think might be
+pertinent to <config-patches@gnu.org> in order to provide the needed
+information to handle your system.
+
+config.guess timestamp = $timestamp
+
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
+/bin/uname -X = `(/bin/uname -X) 2>/dev/null`
+
+hostinfo = `(hostinfo) 2>/dev/null`
+/bin/universe = `(/bin/universe) 2>/dev/null`
+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null`
+/bin/arch = `(/bin/arch) 2>/dev/null`
+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
+
+UNAME_MACHINE = ${UNAME_MACHINE}
+UNAME_RELEASE = ${UNAME_RELEASE}
+UNAME_SYSTEM = ${UNAME_SYSTEM}
+UNAME_VERSION = ${UNAME_VERSION}
+EOF
+
+exit 1
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/source4/lib/ldb/config.mk b/source4/lib/ldb/config.mk
new file mode 100644
index 0000000000..6fcf3943d0
--- /dev/null
+++ b/source4/lib/ldb/config.mk
@@ -0,0 +1,151 @@
+################################################
+# Start MODULE ldb_asq
+[MODULE::ldb_asq]
+PRIVATE_DEPENDENCIES = LIBTALLOC LIBTEVENT
+CFLAGS = -I$(ldbsrcdir)/include
+INIT_FUNCTION = LDB_MODULE(asq)
+SUBSYSTEM = LIBLDB
+
+ldb_asq_OBJ_FILES = $(ldbsrcdir)/modules/asq.o
+# End MODULE ldb_asq
+################################################
+
+################################################
+# Start MODULE ldb_server_sort
+[MODULE::ldb_server_sort]
+PRIVATE_DEPENDENCIES = LIBTALLOC LIBTEVENT
+CFLAGS = -I$(ldbsrcdir)/include
+INIT_FUNCTION = LDB_MODULE(server_sort)
+SUBSYSTEM = LIBLDB
+
+# End MODULE ldb_sort
+################################################
+ldb_server_sort_OBJ_FILES = $(ldbsrcdir)/modules/sort.o
+
+################################################
+# Start MODULE ldb_paged_results
+[MODULE::ldb_paged_results]
+INIT_FUNCTION = LDB_MODULE(paged_results)
+CFLAGS = -I$(ldbsrcdir)/include
+PRIVATE_DEPENDENCIES = LIBTALLOC LIBTEVENT
+SUBSYSTEM = LIBLDB
+# End MODULE ldb_paged_results
+################################################
+
+ldb_paged_results_OBJ_FILES = $(ldbsrcdir)/modules/paged_results.o
+
+################################################
+# Start MODULE ldb_paged_results
+[MODULE::ldb_paged_searches]
+INIT_FUNCTION = LDB_MODULE(paged_searches)
+CFLAGS = -I$(ldbsrcdir)/include
+PRIVATE_DEPENDENCIES = LIBTALLOC LIBTEVENT
+SUBSYSTEM = LIBLDB
+# End MODULE ldb_paged_results
+################################################
+
+ldb_paged_searches_OBJ_FILES = $(ldbsrcdir)/modules/paged_searches.o
+
+################################################
+# Start MODULE ldb_operational
+[MODULE::ldb_operational]
+SUBSYSTEM = LIBLDB
+CFLAGS = -I$(ldbsrcdir)/include
+PRIVATE_DEPENDENCIES = LIBTALLOC LIBTEVENT
+INIT_FUNCTION = LDB_MODULE(operational)
+# End MODULE ldb_operational
+################################################
+
+ldb_operational_OBJ_FILES = $(ldbsrcdir)/modules/operational.o
+
+################################################
+# Start MODULE ldb_rdn_name
+[MODULE::ldb_rdn_name]
+SUBSYSTEM = LIBLDB
+CFLAGS = -I$(ldbsrcdir)/include
+PRIVATE_DEPENDENCIES = LIBTALLOC LIBTEVENT
+INIT_FUNCTION = LDB_MODULE(rdn_name)
+# End MODULE ldb_rdn_name
+################################################
+
+ldb_rdn_name_OBJ_FILES = $(ldbsrcdir)/modules/rdn_name.o
+
+ldb_map_OBJ_FILES = $(addprefix $(ldbsrcdir)/ldb_map/, ldb_map_inbound.o ldb_map_outbound.o ldb_map.o)
+
+$(ldb_map_OBJ_FILES): CFLAGS+=-I$(ldbsrcdir)/ldb_map
+
+################################################
+# Start MODULE ldb_skel
+[MODULE::ldb_skel]
+SUBSYSTEM = LIBLDB
+CFLAGS = -I$(ldbsrcdir)/include
+PRIVATE_DEPENDENCIES = LIBTALLOC LIBTEVENT
+INIT_FUNCTION = LDB_MODULE(skel)
+# End MODULE ldb_skel
+################################################
+
+ldb_skel_OBJ_FILES = $(ldbsrcdir)/modules/skel.o
+
+################################################
+# Start MODULE ldb_sqlite3
+[MODULE::ldb_sqlite3]
+SUBSYSTEM = LIBLDB
+CFLAGS = -I$(ldbsrcdir)/include
+PRIVATE_DEPENDENCIES = LIBTALLOC SQLITE3 LIBTEVENT
+INIT_FUNCTION = LDB_BACKEND(sqlite3)
+# End MODULE ldb_sqlite3
+################################################
+
+ldb_sqlite3_OBJ_FILES = $(ldbsrcdir)/ldb_sqlite3/ldb_sqlite3.o
+
+################################################
+# Start MODULE ldb_tdb
+[MODULE::ldb_tdb]
+SUBSYSTEM = LIBLDB
+CFLAGS = -I$(ldbsrcdir)/include -I$(ldbsrcdir)/ldb_tdb
+PRIVATE_DEPENDENCIES = \
+ LIBTDB LIBTALLOC LIBTEVENT
+INIT_FUNCTION = LDB_BACKEND(tdb)
+# End MODULE ldb_tdb
+################################################
+
+ldb_tdb_OBJ_FILES = $(addprefix $(ldbsrcdir)/ldb_tdb/, ldb_tdb.o ldb_search.o ldb_pack.o ldb_index.o ldb_cache.o ldb_tdb_wrap.o)
+
+
+################################################
+# Start SUBSYSTEM ldb
+[LIBRARY::LIBLDB]
+CFLAGS = -I$(ldbsrcdir)/include
+PUBLIC_DEPENDENCIES = \
+ LIBTALLOC LIBTEVENT
+PRIVATE_DEPENDENCIES = \
+ SOCKET_WRAPPER
+
+PC_FILES += $(ldbsrcdir)/ldb.pc
+#
+# End SUBSYSTEM ldb
+################################################
+
+LIBLDB_VERSION = 0.0.1
+LIBLDB_SOVERSION = 0
+
+LIBLDB_OBJ_FILES = $(addprefix $(ldbsrcdir)/common/, ldb.o ldb_ldif.o ldb_parse.o ldb_msg.o ldb_utf8.o ldb_debug.o ldb_modules.o ldb_match.o ldb_attributes.o attrib_handlers.o ldb_dn.o ldb_controls.o qsort.o) $(ldb_map_OBJ_FILES)
+
+$(LIBLDB_OBJ_FILES): CFLAGS+=-I$(ldbsrcdir)/include
+
+PUBLIC_HEADERS += $(ldbsrcdir)/include/ldb.h $(ldbsrcdir)/include/ldb_errors.h
+
+MANPAGES += $(ldbsrcdir)/man/ldb.3
+
+################################################
+# Start BINARY ldbtest
+[BINARY::ldbtest]
+PRIVATE_DEPENDENCIES = \
+ LIBLDB_CMDLINE
+# End BINARY ldbtest
+################################################
+
+ldbtest_OBJ_FILES = $(ldbsrcdir)/tools/ldbtest.o
+
+mkinclude tools/config.mk
+mkinclude ldb_ildap/config.mk
diff --git a/source4/lib/ldb/config.sub b/source4/lib/ldb/config.sub
new file mode 100755
index 0000000000..23cd6fd75c
--- /dev/null
+++ b/source4/lib/ldb/config.sub
@@ -0,0 +1,1577 @@
+#! /bin/sh
+# Configuration validation subroutine script.
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+# 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+
+timestamp='2005-07-08'
+
+# This file is (in principle) common to ALL GNU software.
+# The presence of a machine in this file suggests that SOME GNU software
+# can handle that machine. It does not imply ALL GNU software can.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <http://www.gnu.org/licenses/>.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+
+# Please send patches to <config-patches@gnu.org>. Submit a context
+# diff and a properly formatted ChangeLog entry.
+#
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support. The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION] CPU-MFR-OPSYS
+ $0 [OPTION] ALIAS
+
+Canonicalize a configuration name.
+
+Operation modes:
+ -h, --help print this help, then exit
+ -t, --time-stamp print date of last modification, then exit
+ -v, --version print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.sub ($timestamp)
+
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+ case $1 in
+ --time-stamp | --time* | -t )
+ echo "$timestamp" ; exit ;;
+ --version | -v )
+ echo "$version" ; exit ;;
+ --help | --h* | -h )
+ echo "$usage"; exit ;;
+ -- ) # Stop option processing
+ shift; break ;;
+ - ) # Use stdin as input.
+ break ;;
+ -* )
+ echo "$me: invalid option $1$help"
+ exit 1 ;;
+
+ *local*)
+ # First pass through any local machine types.
+ echo $1
+ exit ;;
+
+ * )
+ break ;;
+ esac
+done
+
+case $# in
+ 0) echo "$me: missing argument$help" >&2
+ exit 1;;
+ 1) ;;
+ *) echo "$me: too many arguments$help" >&2
+ exit 1;;
+esac
+
+# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
+# Here we must recognize all the valid KERNEL-OS combinations.
+maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
+case $maybe_os in
+ nto-qnx* | linux-gnu* | linux-dietlibc | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | \
+ kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | storm-chaos* | os2-emx* | rtmk-nova*)
+ os=-$maybe_os
+ basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
+ ;;
+ *)
+ basic_machine=`echo $1 | sed 's/-[^-]*$//'`
+ if [ $basic_machine != $1 ]
+ then os=`echo $1 | sed 's/.*-/-/'`
+ else os=; fi
+ ;;
+esac
+
+### Let's recognize common machines as not being operating systems so
+### that things like config.sub decstation-3100 work. We also
+### recognize some manufacturers as not being operating systems, so we
+### can provide default operating systems below.
+case $os in
+ -sun*os*)
+ # Prevent following clause from handling this invalid input.
+ ;;
+ -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
+ -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
+ -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
+ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
+ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
+ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
+ -apple | -axis | -knuth | -cray)
+ os=
+ basic_machine=$1
+ ;;
+ -sim | -cisco | -oki | -wec | -winbond)
+ os=
+ basic_machine=$1
+ ;;
+ -scout)
+ ;;
+ -wrs)
+ os=-vxworks
+ basic_machine=$1
+ ;;
+ -chorusos*)
+ os=-chorusos
+ basic_machine=$1
+ ;;
+ -chorusrdb)
+ os=-chorusrdb
+ basic_machine=$1
+ ;;
+ -hiux*)
+ os=-hiuxwe2
+ ;;
+ -sco5)
+ os=-sco3.2v5
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco4)
+ os=-sco3.2v4
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco3.2.[4-9]*)
+ os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco3.2v[4-9]*)
+ # Don't forget version if it is 3.2v4 or newer.
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco*)
+ os=-sco3.2v2
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -udk*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -isc)
+ os=-isc2.2
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -clix*)
+ basic_machine=clipper-intergraph
+ ;;
+ -isc*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -lynx*)
+ os=-lynxos
+ ;;
+ -ptx*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
+ ;;
+ -windowsnt*)
+ os=`echo $os | sed -e 's/windowsnt/winnt/'`
+ ;;
+ -psos*)
+ os=-psos
+ ;;
+ -mint | -mint[0-9]*)
+ basic_machine=m68k-atari
+ os=-mint
+ ;;
+esac
+
+# Decode aliases for certain CPU-COMPANY combinations.
+case $basic_machine in
+ # Recognize the basic CPU types without company name.
+ # Some are omitted here because they have special meanings below.
+ 1750a | 580 \
+ | a29k \
+ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
+ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
+ | am33_2.0 \
+ | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \
+ | bfin \
+ | c4x | clipper \
+ | d10v | d30v | dlx | dsp16xx \
+ | fr30 | frv \
+ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+ | i370 | i860 | i960 | ia64 \
+ | ip2k | iq2000 \
+ | m32r | m32rle | m68000 | m68k | m88k | maxq | mcore \
+ | mips | mipsbe | mipseb | mipsel | mipsle \
+ | mips16 \
+ | mips64 | mips64el \
+ | mips64vr | mips64vrel \
+ | mips64orion | mips64orionel \
+ | mips64vr4100 | mips64vr4100el \
+ | mips64vr4300 | mips64vr4300el \
+ | mips64vr5000 | mips64vr5000el \
+ | mips64vr5900 | mips64vr5900el \
+ | mipsisa32 | mipsisa32el \
+ | mipsisa32r2 | mipsisa32r2el \
+ | mipsisa64 | mipsisa64el \
+ | mipsisa64r2 | mipsisa64r2el \
+ | mipsisa64sb1 | mipsisa64sb1el \
+ | mipsisa64sr71k | mipsisa64sr71kel \
+ | mipstx39 | mipstx39el \
+ | mn10200 | mn10300 \
+ | ms1 \
+ | msp430 \
+ | ns16k | ns32k \
+ | or32 \
+ | pdp10 | pdp11 | pj | pjl \
+ | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
+ | pyramid \
+ | sh | sh[1234] | sh[24]a | sh[23]e | sh[34]eb | shbe | shle | sh[1234]le | sh3ele \
+ | sh64 | sh64le \
+ | sparc | sparc64 | sparc64b | sparc86x | sparclet | sparclite \
+ | sparcv8 | sparcv9 | sparcv9b \
+ | strongarm \
+ | tahoe | thumb | tic4x | tic80 | tron \
+ | v850 | v850e \
+ | we32k \
+ | x86 | xscale | xscalee[bl] | xstormy16 | xtensa \
+ | z8k)
+ basic_machine=$basic_machine-unknown
+ ;;
+ m32c)
+ basic_machine=$basic_machine-unknown
+ ;;
+ m6811 | m68hc11 | m6812 | m68hc12)
+ # Motorola 68HC11/12.
+ basic_machine=$basic_machine-unknown
+ os=-none
+ ;;
+ m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)
+ ;;
+
+ # We use `pc' rather than `unknown'
+ # because (1) that's what they normally are, and
+ # (2) the word "unknown" tends to confuse beginning users.
+ i*86 | x86_64)
+ basic_machine=$basic_machine-pc
+ ;;
+ # Object if more than one company name word.
+ *-*-*)
+ echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+ exit 1
+ ;;
+ # Recognize the basic CPU types with company name.
+ 580-* \
+ | a29k-* \
+ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
+ | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
+ | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \
+ | arm-* | armbe-* | armle-* | armeb-* | armv*-* \
+ | avr-* \
+ | bfin-* | bs2000-* \
+ | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \
+ | clipper-* | craynv-* | cydra-* \
+ | d10v-* | d30v-* | dlx-* \
+ | elxsi-* \
+ | f30[01]-* | f700-* | fr30-* | frv-* | fx80-* \
+ | h8300-* | h8500-* \
+ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
+ | i*86-* | i860-* | i960-* | ia64-* \
+ | ip2k-* | iq2000-* \
+ | m32r-* | m32rle-* \
+ | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
+ | m88110-* | m88k-* | maxq-* | mcore-* \
+ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
+ | mips16-* \
+ | mips64-* | mips64el-* \
+ | mips64vr-* | mips64vrel-* \
+ | mips64orion-* | mips64orionel-* \
+ | mips64vr4100-* | mips64vr4100el-* \
+ | mips64vr4300-* | mips64vr4300el-* \
+ | mips64vr5000-* | mips64vr5000el-* \
+ | mips64vr5900-* | mips64vr5900el-* \
+ | mipsisa32-* | mipsisa32el-* \
+ | mipsisa32r2-* | mipsisa32r2el-* \
+ | mipsisa64-* | mipsisa64el-* \
+ | mipsisa64r2-* | mipsisa64r2el-* \
+ | mipsisa64sb1-* | mipsisa64sb1el-* \
+ | mipsisa64sr71k-* | mipsisa64sr71kel-* \
+ | mipstx39-* | mipstx39el-* \
+ | mmix-* \
+ | ms1-* \
+ | msp430-* \
+ | none-* | np1-* | ns16k-* | ns32k-* \
+ | orion-* \
+ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
+ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
+ | pyramid-* \
+ | romp-* | rs6000-* \
+ | sh-* | sh[1234]-* | sh[24]a-* | sh[23]e-* | sh[34]eb-* | shbe-* \
+ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
+ | sparc-* | sparc64-* | sparc64b-* | sparc86x-* | sparclet-* \
+ | sparclite-* \
+ | sparcv8-* | sparcv9-* | sparcv9b-* | strongarm-* | sv1-* | sx?-* \
+ | tahoe-* | thumb-* \
+ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
+ | tron-* \
+ | v850-* | v850e-* | vax-* \
+ | we32k-* \
+ | x86-* | x86_64-* | xps100-* | xscale-* | xscalee[bl]-* \
+ | xstormy16-* | xtensa-* \
+ | ymp-* \
+ | z8k-*)
+ ;;
+ m32c-*)
+ ;;
+ # Recognize the various machine names and aliases which stand
+ # for a CPU type and a company and sometimes even an OS.
+ 386bsd)
+ basic_machine=i386-unknown
+ os=-bsd
+ ;;
+ 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+ basic_machine=m68000-att
+ ;;
+ 3b*)
+ basic_machine=we32k-att
+ ;;
+ a29khif)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ abacus)
+ basic_machine=abacus-unknown
+ ;;
+ adobe68k)
+ basic_machine=m68010-adobe
+ os=-scout
+ ;;
+ alliant | fx80)
+ basic_machine=fx80-alliant
+ ;;
+ altos | altos3068)
+ basic_machine=m68k-altos
+ ;;
+ am29k)
+ basic_machine=a29k-none
+ os=-bsd
+ ;;
+ amd64)
+ basic_machine=x86_64-pc
+ ;;
+ amd64-*)
+ basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ amdahl)
+ basic_machine=580-amdahl
+ os=-sysv
+ ;;
+ amiga | amiga-*)
+ basic_machine=m68k-unknown
+ ;;
+ amigaos | amigados)
+ basic_machine=m68k-unknown
+ os=-amigaos
+ ;;
+ amigaunix | amix)
+ basic_machine=m68k-unknown
+ os=-sysv4
+ ;;
+ apollo68)
+ basic_machine=m68k-apollo
+ os=-sysv
+ ;;
+ apollo68bsd)
+ basic_machine=m68k-apollo
+ os=-bsd
+ ;;
+ aux)
+ basic_machine=m68k-apple
+ os=-aux
+ ;;
+ balance)
+ basic_machine=ns32k-sequent
+ os=-dynix
+ ;;
+ c90)
+ basic_machine=c90-cray
+ os=-unicos
+ ;;
+ convex-c1)
+ basic_machine=c1-convex
+ os=-bsd
+ ;;
+ convex-c2)
+ basic_machine=c2-convex
+ os=-bsd
+ ;;
+ convex-c32)
+ basic_machine=c32-convex
+ os=-bsd
+ ;;
+ convex-c34)
+ basic_machine=c34-convex
+ os=-bsd
+ ;;
+ convex-c38)
+ basic_machine=c38-convex
+ os=-bsd
+ ;;
+ cray | j90)
+ basic_machine=j90-cray
+ os=-unicos
+ ;;
+ craynv)
+ basic_machine=craynv-cray
+ os=-unicosmp
+ ;;
+ cr16c)
+ basic_machine=cr16c-unknown
+ os=-elf
+ ;;
+ crds | unos)
+ basic_machine=m68k-crds
+ ;;
+ crisv32 | crisv32-* | etraxfs*)
+ basic_machine=crisv32-axis
+ ;;
+ cris | cris-* | etrax*)
+ basic_machine=cris-axis
+ ;;
+ crx)
+ basic_machine=crx-unknown
+ os=-elf
+ ;;
+ da30 | da30-*)
+ basic_machine=m68k-da30
+ ;;
+ decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
+ basic_machine=mips-dec
+ ;;
+ decsystem10* | dec10*)
+ basic_machine=pdp10-dec
+ os=-tops10
+ ;;
+ decsystem20* | dec20*)
+ basic_machine=pdp10-dec
+ os=-tops20
+ ;;
+ delta | 3300 | motorola-3300 | motorola-delta \
+ | 3300-motorola | delta-motorola)
+ basic_machine=m68k-motorola
+ ;;
+ delta88)
+ basic_machine=m88k-motorola
+ os=-sysv3
+ ;;
+ djgpp)
+ basic_machine=i586-pc
+ os=-msdosdjgpp
+ ;;
+ dpx20 | dpx20-*)
+ basic_machine=rs6000-bull
+ os=-bosx
+ ;;
+ dpx2* | dpx2*-bull)
+ basic_machine=m68k-bull
+ os=-sysv3
+ ;;
+ ebmon29k)
+ basic_machine=a29k-amd
+ os=-ebmon
+ ;;
+ elxsi)
+ basic_machine=elxsi-elxsi
+ os=-bsd
+ ;;
+ encore | umax | mmax)
+ basic_machine=ns32k-encore
+ ;;
+ es1800 | OSE68k | ose68k | ose | OSE)
+ basic_machine=m68k-ericsson
+ os=-ose
+ ;;
+ fx2800)
+ basic_machine=i860-alliant
+ ;;
+ genix)
+ basic_machine=ns32k-ns
+ ;;
+ gmicro)
+ basic_machine=tron-gmicro
+ os=-sysv
+ ;;
+ go32)
+ basic_machine=i386-pc
+ os=-go32
+ ;;
+ h3050r* | hiux*)
+ basic_machine=hppa1.1-hitachi
+ os=-hiuxwe2
+ ;;
+ h8300hms)
+ basic_machine=h8300-hitachi
+ os=-hms
+ ;;
+ h8300xray)
+ basic_machine=h8300-hitachi
+ os=-xray
+ ;;
+ h8500hms)
+ basic_machine=h8500-hitachi
+ os=-hms
+ ;;
+ harris)
+ basic_machine=m88k-harris
+ os=-sysv3
+ ;;
+ hp300-*)
+ basic_machine=m68k-hp
+ ;;
+ hp300bsd)
+ basic_machine=m68k-hp
+ os=-bsd
+ ;;
+ hp300hpux)
+ basic_machine=m68k-hp
+ os=-hpux
+ ;;
+ hp3k9[0-9][0-9] | hp9[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hp9k2[0-9][0-9] | hp9k31[0-9])
+ basic_machine=m68000-hp
+ ;;
+ hp9k3[2-9][0-9])
+ basic_machine=m68k-hp
+ ;;
+ hp9k6[0-9][0-9] | hp6[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hp9k7[0-79][0-9] | hp7[0-79][0-9])
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k78[0-9] | hp78[0-9])
+ # FIXME: really hppa2.0-hp
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
+ # FIXME: really hppa2.0-hp
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[0-9][13679] | hp8[0-9][13679])
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[0-9][0-9] | hp8[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hppa-next)
+ os=-nextstep3
+ ;;
+ hppaosf)
+ basic_machine=hppa1.1-hp
+ os=-osf
+ ;;
+ hppro)
+ basic_machine=hppa1.1-hp
+ os=-proelf
+ ;;
+ i370-ibm* | ibm*)
+ basic_machine=i370-ibm
+ ;;
+# I'm not sure what "Sysv32" means. Should this be sysv3.2?
+ i*86v32)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv32
+ ;;
+ i*86v4*)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv4
+ ;;
+ i*86v)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv
+ ;;
+ i*86sol2)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-solaris2
+ ;;
+ i386mach)
+ basic_machine=i386-mach
+ os=-mach
+ ;;
+ i386-vsta | vsta)
+ basic_machine=i386-unknown
+ os=-vsta
+ ;;
+ iris | iris4d)
+ basic_machine=mips-sgi
+ case $os in
+ -irix*)
+ ;;
+ *)
+ os=-irix4
+ ;;
+ esac
+ ;;
+ isi68 | isi)
+ basic_machine=m68k-isi
+ os=-sysv
+ ;;
+ m88k-omron*)
+ basic_machine=m88k-omron
+ ;;
+ magnum | m3230)
+ basic_machine=mips-mips
+ os=-sysv
+ ;;
+ merlin)
+ basic_machine=ns32k-utek
+ os=-sysv
+ ;;
+ mingw32)
+ basic_machine=i386-pc
+ os=-mingw32
+ ;;
+ miniframe)
+ basic_machine=m68000-convergent
+ ;;
+ *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
+ basic_machine=m68k-atari
+ os=-mint
+ ;;
+ mips3*-*)
+ basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
+ ;;
+ mips3*)
+ basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
+ ;;
+ monitor)
+ basic_machine=m68k-rom68k
+ os=-coff
+ ;;
+ morphos)
+ basic_machine=powerpc-unknown
+ os=-morphos
+ ;;
+ msdos)
+ basic_machine=i386-pc
+ os=-msdos
+ ;;
+ mvs)
+ basic_machine=i370-ibm
+ os=-mvs
+ ;;
+ ncr3000)
+ basic_machine=i486-ncr
+ os=-sysv4
+ ;;
+ netbsd386)
+ basic_machine=i386-unknown
+ os=-netbsd
+ ;;
+ netwinder)
+ basic_machine=armv4l-rebel
+ os=-linux
+ ;;
+ news | news700 | news800 | news900)
+ basic_machine=m68k-sony
+ os=-newsos
+ ;;
+ news1000)
+ basic_machine=m68030-sony
+ os=-newsos
+ ;;
+ news-3600 | risc-news)
+ basic_machine=mips-sony
+ os=-newsos
+ ;;
+ necv70)
+ basic_machine=v70-nec
+ os=-sysv
+ ;;
+ next | m*-next )
+ basic_machine=m68k-next
+ case $os in
+ -nextstep* )
+ ;;
+ -ns2*)
+ os=-nextstep2
+ ;;
+ *)
+ os=-nextstep3
+ ;;
+ esac
+ ;;
+ nh3000)
+ basic_machine=m68k-harris
+ os=-cxux
+ ;;
+ nh[45]000)
+ basic_machine=m88k-harris
+ os=-cxux
+ ;;
+ nindy960)
+ basic_machine=i960-intel
+ os=-nindy
+ ;;
+ mon960)
+ basic_machine=i960-intel
+ os=-mon960
+ ;;
+ nonstopux)
+ basic_machine=mips-compaq
+ os=-nonstopux
+ ;;
+ np1)
+ basic_machine=np1-gould
+ ;;
+ nsr-tandem)
+ basic_machine=nsr-tandem
+ ;;
+ op50n-* | op60c-*)
+ basic_machine=hppa1.1-oki
+ os=-proelf
+ ;;
+ openrisc | openrisc-*)
+ basic_machine=or32-unknown
+ ;;
+ os400)
+ basic_machine=powerpc-ibm
+ os=-os400
+ ;;
+ OSE68000 | ose68000)
+ basic_machine=m68000-ericsson
+ os=-ose
+ ;;
+ os68k)
+ basic_machine=m68k-none
+ os=-os68k
+ ;;
+ pa-hitachi)
+ basic_machine=hppa1.1-hitachi
+ os=-hiuxwe2
+ ;;
+ paragon)
+ basic_machine=i860-intel
+ os=-osf
+ ;;
+ pbd)
+ basic_machine=sparc-tti
+ ;;
+ pbb)
+ basic_machine=m68k-tti
+ ;;
+ pc532 | pc532-*)
+ basic_machine=ns32k-pc532
+ ;;
+ pentium | p5 | k5 | k6 | nexgen | viac3)
+ basic_machine=i586-pc
+ ;;
+ pentiumpro | p6 | 6x86 | athlon | athlon_*)
+ basic_machine=i686-pc
+ ;;
+ pentiumii | pentium2 | pentiumiii | pentium3)
+ basic_machine=i686-pc
+ ;;
+ pentium4)
+ basic_machine=i786-pc
+ ;;
+ pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
+ basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentiumpro-* | p6-* | 6x86-* | athlon-*)
+ basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
+ basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentium4-*)
+ basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pn)
+ basic_machine=pn-gould
+ ;;
+ power) basic_machine=power-ibm
+ ;;
+ ppc) basic_machine=powerpc-unknown
+ ;;
+ ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppcle | powerpclittle | ppc-le | powerpc-little)
+ basic_machine=powerpcle-unknown
+ ;;
+ ppcle-* | powerpclittle-*)
+ basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppc64) basic_machine=powerpc64-unknown
+ ;;
+ ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppc64le | powerpc64little | ppc64-le | powerpc64-little)
+ basic_machine=powerpc64le-unknown
+ ;;
+ ppc64le-* | powerpc64little-*)
+ basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ps2)
+ basic_machine=i386-ibm
+ ;;
+ pw32)
+ basic_machine=i586-unknown
+ os=-pw32
+ ;;
+ rom68k)
+ basic_machine=m68k-rom68k
+ os=-coff
+ ;;
+ rm[46]00)
+ basic_machine=mips-siemens
+ ;;
+ rtpc | rtpc-*)
+ basic_machine=romp-ibm
+ ;;
+ s390 | s390-*)
+ basic_machine=s390-ibm
+ ;;
+ s390x | s390x-*)
+ basic_machine=s390x-ibm
+ ;;
+ sa29200)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ sb1)
+ basic_machine=mipsisa64sb1-unknown
+ ;;
+ sb1el)
+ basic_machine=mipsisa64sb1el-unknown
+ ;;
+ sei)
+ basic_machine=mips-sei
+ os=-seiux
+ ;;
+ sequent)
+ basic_machine=i386-sequent
+ ;;
+ sh)
+ basic_machine=sh-hitachi
+ os=-hms
+ ;;
+ sh64)
+ basic_machine=sh64-unknown
+ ;;
+ sparclite-wrs | simso-wrs)
+ basic_machine=sparclite-wrs
+ os=-vxworks
+ ;;
+ sps7)
+ basic_machine=m68k-bull
+ os=-sysv2
+ ;;
+ spur)
+ basic_machine=spur-unknown
+ ;;
+ st2000)
+ basic_machine=m68k-tandem
+ ;;
+ stratus)
+ basic_machine=i860-stratus
+ os=-sysv4
+ ;;
+ sun2)
+ basic_machine=m68000-sun
+ ;;
+ sun2os3)
+ basic_machine=m68000-sun
+ os=-sunos3
+ ;;
+ sun2os4)
+ basic_machine=m68000-sun
+ os=-sunos4
+ ;;
+ sun3os3)
+ basic_machine=m68k-sun
+ os=-sunos3
+ ;;
+ sun3os4)
+ basic_machine=m68k-sun
+ os=-sunos4
+ ;;
+ sun4os3)
+ basic_machine=sparc-sun
+ os=-sunos3
+ ;;
+ sun4os4)
+ basic_machine=sparc-sun
+ os=-sunos4
+ ;;
+ sun4sol2)
+ basic_machine=sparc-sun
+ os=-solaris2
+ ;;
+ sun3 | sun3-*)
+ basic_machine=m68k-sun
+ ;;
+ sun4)
+ basic_machine=sparc-sun
+ ;;
+ sun386 | sun386i | roadrunner)
+ basic_machine=i386-sun
+ ;;
+ sv1)
+ basic_machine=sv1-cray
+ os=-unicos
+ ;;
+ symmetry)
+ basic_machine=i386-sequent
+ os=-dynix
+ ;;
+ t3e)
+ basic_machine=alphaev5-cray
+ os=-unicos
+ ;;
+ t90)
+ basic_machine=t90-cray
+ os=-unicos
+ ;;
+ tic54x | c54x*)
+ basic_machine=tic54x-unknown
+ os=-coff
+ ;;
+ tic55x | c55x*)
+ basic_machine=tic55x-unknown
+ os=-coff
+ ;;
+ tic6x | c6x*)
+ basic_machine=tic6x-unknown
+ os=-coff
+ ;;
+ tx39)
+ basic_machine=mipstx39-unknown
+ ;;
+ tx39el)
+ basic_machine=mipstx39el-unknown
+ ;;
+ toad1)
+ basic_machine=pdp10-xkl
+ os=-tops20
+ ;;
+ tower | tower-32)
+ basic_machine=m68k-ncr
+ ;;
+ tpf)
+ basic_machine=s390x-ibm
+ os=-tpf
+ ;;
+ udi29k)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ ultra3)
+ basic_machine=a29k-nyu
+ os=-sym1
+ ;;
+ v810 | necv810)
+ basic_machine=v810-nec
+ os=-none
+ ;;
+ vaxv)
+ basic_machine=vax-dec
+ os=-sysv
+ ;;
+ vms)
+ basic_machine=vax-dec
+ os=-vms
+ ;;
+ vpp*|vx|vx-*)
+ basic_machine=f301-fujitsu
+ ;;
+ vxworks960)
+ basic_machine=i960-wrs
+ os=-vxworks
+ ;;
+ vxworks68)
+ basic_machine=m68k-wrs
+ os=-vxworks
+ ;;
+ vxworks29k)
+ basic_machine=a29k-wrs
+ os=-vxworks
+ ;;
+ w65*)
+ basic_machine=w65-wdc
+ os=-none
+ ;;
+ w89k-*)
+ basic_machine=hppa1.1-winbond
+ os=-proelf
+ ;;
+ xbox)
+ basic_machine=i686-pc
+ os=-mingw32
+ ;;
+ xps | xps100)
+ basic_machine=xps100-honeywell
+ ;;
+ ymp)
+ basic_machine=ymp-cray
+ os=-unicos
+ ;;
+ z8k-*-coff)
+ basic_machine=z8k-unknown
+ os=-sim
+ ;;
+ none)
+ basic_machine=none-none
+ os=-none
+ ;;
+
+# Here we handle the default manufacturer of certain CPU types. It is in
+# some cases the only manufacturer, in others, it is the most popular.
+ w89k)
+ basic_machine=hppa1.1-winbond
+ ;;
+ op50n)
+ basic_machine=hppa1.1-oki
+ ;;
+ op60c)
+ basic_machine=hppa1.1-oki
+ ;;
+ romp)
+ basic_machine=romp-ibm
+ ;;
+ mmix)
+ basic_machine=mmix-knuth
+ ;;
+ rs6000)
+ basic_machine=rs6000-ibm
+ ;;
+ vax)
+ basic_machine=vax-dec
+ ;;
+ pdp10)
+ # there are many clones, so DEC is not a safe bet
+ basic_machine=pdp10-unknown
+ ;;
+ pdp11)
+ basic_machine=pdp11-dec
+ ;;
+ we32k)
+ basic_machine=we32k-att
+ ;;
+ sh[1234] | sh[24]a | sh[34]eb | sh[1234]le | sh[23]ele)
+ basic_machine=sh-unknown
+ ;;
+ sparc | sparcv8 | sparcv9 | sparcv9b)
+ basic_machine=sparc-sun
+ ;;
+ cydra)
+ basic_machine=cydra-cydrome
+ ;;
+ orion)
+ basic_machine=orion-highlevel
+ ;;
+ orion105)
+ basic_machine=clipper-highlevel
+ ;;
+ mac | mpw | mac-mpw)
+ basic_machine=m68k-apple
+ ;;
+ pmac | pmac-mpw)
+ basic_machine=powerpc-apple
+ ;;
+ *-unknown)
+ # Make sure to match an already-canonicalized machine name.
+ ;;
+ *)
+ echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $basic_machine in
+ *-digital*)
+ basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
+ ;;
+ *-commodore*)
+ basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
+ ;;
+ *)
+ ;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if [ x"$os" != x"" ]
+then
+case $os in
+ # First match some system type aliases
+ # that might get confused with valid system types.
+ # -solaris* is a basic system type, with this one exception.
+ -solaris1 | -solaris1.*)
+ os=`echo $os | sed -e 's|solaris1|sunos4|'`
+ ;;
+ -solaris)
+ os=-solaris2
+ ;;
+ -svr4*)
+ os=-sysv4
+ ;;
+ -unixware*)
+ os=-sysv4.2uw
+ ;;
+ -gnu/linux*)
+ os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
+ ;;
+ # First accept the basic system types.
+ # The portable systems comes first.
+ # Each alternative MUST END IN A *, to match a version number.
+ # -sysv* is not here because it comes later, after sysvr4.
+ -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
+ | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\
+ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \
+ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
+ | -aos* \
+ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
+ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
+ | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* | -openbsd* \
+ | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \
+ | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
+ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
+ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
+ | -chorusos* | -chorusrdb* \
+ | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+ | -mingw32* | -linux-gnu* | -linux-uclibc* | -uxpv* | -beos* | -mpeix* | -udk* \
+ | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
+ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
+ | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
+ | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
+ | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
+ | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
+ | -skyos* | -haiku*)
+ # Remember, each alternative MUST END IN *, to match a version number.
+ ;;
+ -qnx*)
+ case $basic_machine in
+ x86-* | i*86-*)
+ ;;
+ *)
+ os=-nto$os
+ ;;
+ esac
+ ;;
+ -nto-qnx*)
+ ;;
+ -nto*)
+ os=`echo $os | sed -e 's|nto|nto-qnx|'`
+ ;;
+ -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
+ | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \
+ | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
+ ;;
+ -mac*)
+ os=`echo $os | sed -e 's|mac|macos|'`
+ ;;
+ -linux-dietlibc)
+ os=-linux-dietlibc
+ ;;
+ -linux*)
+ os=`echo $os | sed -e 's|linux|linux-gnu|'`
+ ;;
+ -sunos5*)
+ os=`echo $os | sed -e 's|sunos5|solaris2|'`
+ ;;
+ -sunos6*)
+ os=`echo $os | sed -e 's|sunos6|solaris3|'`
+ ;;
+ -opened*)
+ os=-openedition
+ ;;
+ -os400*)
+ os=-os400
+ ;;
+ -wince*)
+ os=-wince
+ ;;
+ -osfrose*)
+ os=-osfrose
+ ;;
+ -osf*)
+ os=-osf
+ ;;
+ -utek*)
+ os=-bsd
+ ;;
+ -dynix*)
+ os=-bsd
+ ;;
+ -acis*)
+ os=-aos
+ ;;
+ -atheos*)
+ os=-atheos
+ ;;
+ -syllable*)
+ os=-syllable
+ ;;
+ -386bsd)
+ os=-bsd
+ ;;
+ -ctix* | -uts*)
+ os=-sysv
+ ;;
+ -nova*)
+ os=-rtmk-nova
+ ;;
+ -ns2 )
+ os=-nextstep2
+ ;;
+ -nsk*)
+ os=-nsk
+ ;;
+ # Preserve the version number of sinix5.
+ -sinix5.*)
+ os=`echo $os | sed -e 's|sinix|sysv|'`
+ ;;
+ -sinix*)
+ os=-sysv4
+ ;;
+ -tpf*)
+ os=-tpf
+ ;;
+ -triton*)
+ os=-sysv3
+ ;;
+ -oss*)
+ os=-sysv3
+ ;;
+ -svr4)
+ os=-sysv4
+ ;;
+ -svr3)
+ os=-sysv3
+ ;;
+ -sysvr4)
+ os=-sysv4
+ ;;
+ # This must come after -sysvr4.
+ -sysv*)
+ ;;
+ -ose*)
+ os=-ose
+ ;;
+ -es1800*)
+ os=-ose
+ ;;
+ -xenix)
+ os=-xenix
+ ;;
+ -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+ os=-mint
+ ;;
+ -aros*)
+ os=-aros
+ ;;
+ -kaos*)
+ os=-kaos
+ ;;
+ -zvmoe)
+ os=-zvmoe
+ ;;
+ -none)
+ ;;
+ *)
+ # Get rid of the `-' at the beginning of $os.
+ os=`echo $os | sed 's/[^-]*-//'`
+ echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system. Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+case $basic_machine in
+ *-acorn)
+ os=-riscix1.2
+ ;;
+ arm*-rebel)
+ os=-linux
+ ;;
+ arm*-semi)
+ os=-aout
+ ;;
+ c4x-* | tic4x-*)
+ os=-coff
+ ;;
+ # This must come before the *-dec entry.
+ pdp10-*)
+ os=-tops20
+ ;;
+ pdp11-*)
+ os=-none
+ ;;
+ *-dec | vax-*)
+ os=-ultrix4.2
+ ;;
+ m68*-apollo)
+ os=-domain
+ ;;
+ i386-sun)
+ os=-sunos4.0.2
+ ;;
+ m68000-sun)
+ os=-sunos3
+ # This also exists in the configure program, but was not the
+ # default.
+ # os=-sunos4
+ ;;
+ m68*-cisco)
+ os=-aout
+ ;;
+ mips*-cisco)
+ os=-elf
+ ;;
+ mips*-*)
+ os=-elf
+ ;;
+ or32-*)
+ os=-coff
+ ;;
+ *-tti) # must be before sparc entry or we get the wrong os.
+ os=-sysv3
+ ;;
+ sparc-* | *-sun)
+ os=-sunos4.1.1
+ ;;
+ *-be)
+ os=-beos
+ ;;
+ *-haiku)
+ os=-haiku
+ ;;
+ *-ibm)
+ os=-aix
+ ;;
+ *-knuth)
+ os=-mmixware
+ ;;
+ *-wec)
+ os=-proelf
+ ;;
+ *-winbond)
+ os=-proelf
+ ;;
+ *-oki)
+ os=-proelf
+ ;;
+ *-hp)
+ os=-hpux
+ ;;
+ *-hitachi)
+ os=-hiux
+ ;;
+ i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+ os=-sysv
+ ;;
+ *-cbm)
+ os=-amigaos
+ ;;
+ *-dg)
+ os=-dgux
+ ;;
+ *-dolphin)
+ os=-sysv3
+ ;;
+ m68k-ccur)
+ os=-rtu
+ ;;
+ m88k-omron*)
+ os=-luna
+ ;;
+ *-next )
+ os=-nextstep
+ ;;
+ *-sequent)
+ os=-ptx
+ ;;
+ *-crds)
+ os=-unos
+ ;;
+ *-ns)
+ os=-genix
+ ;;
+ i370-*)
+ os=-mvs
+ ;;
+ *-next)
+ os=-nextstep3
+ ;;
+ *-gould)
+ os=-sysv
+ ;;
+ *-highlevel)
+ os=-bsd
+ ;;
+ *-encore)
+ os=-bsd
+ ;;
+ *-sgi)
+ os=-irix
+ ;;
+ *-siemens)
+ os=-sysv4
+ ;;
+ *-masscomp)
+ os=-rtu
+ ;;
+ f30[01]-fujitsu | f700-fujitsu)
+ os=-uxpv
+ ;;
+ *-rom68k)
+ os=-coff
+ ;;
+ *-*bug)
+ os=-coff
+ ;;
+ *-apple)
+ os=-macos
+ ;;
+ *-atari*)
+ os=-mint
+ ;;
+ *)
+ os=-none
+ ;;
+esac
+fi
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer. We pick the logical manufacturer.
+vendor=unknown
+case $basic_machine in
+ *-unknown)
+ case $os in
+ -riscix*)
+ vendor=acorn
+ ;;
+ -sunos*)
+ vendor=sun
+ ;;
+ -aix*)
+ vendor=ibm
+ ;;
+ -beos*)
+ vendor=be
+ ;;
+ -hpux*)
+ vendor=hp
+ ;;
+ -mpeix*)
+ vendor=hp
+ ;;
+ -hiux*)
+ vendor=hitachi
+ ;;
+ -unos*)
+ vendor=crds
+ ;;
+ -dgux*)
+ vendor=dg
+ ;;
+ -luna*)
+ vendor=omron
+ ;;
+ -genix*)
+ vendor=ns
+ ;;
+ -mvs* | -opened*)
+ vendor=ibm
+ ;;
+ -os400*)
+ vendor=ibm
+ ;;
+ -ptx*)
+ vendor=sequent
+ ;;
+ -tpf*)
+ vendor=ibm
+ ;;
+ -vxsim* | -vxworks* | -windiss*)
+ vendor=wrs
+ ;;
+ -aux*)
+ vendor=apple
+ ;;
+ -hms*)
+ vendor=hitachi
+ ;;
+ -mpw* | -macos*)
+ vendor=apple
+ ;;
+ -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+ vendor=atari
+ ;;
+ -vos*)
+ vendor=stratus
+ ;;
+ esac
+ basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
+ ;;
+esac
+
+echo $basic_machine$os
+exit
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/source4/lib/ldb/configure.ac b/source4/lib/ldb/configure.ac
new file mode 100644
index 0000000000..d61b31afd4
--- /dev/null
+++ b/source4/lib/ldb/configure.ac
@@ -0,0 +1,102 @@
+AC_PREREQ(2.50)
+AC_DEFUN([AC_CHECK_LIB_EXT], [
+ AC_CHECK_LIB([$1],[$3],[$4],[$5],[$7])
+ ac_cv_lib_ext_$1_$3=$ac_cv_lib_$1_$3
+])
+AC_DEFUN([AC_CHECK_FUNC_EXT], [
+ AC_CHECK_FUNC([$1],[$3],[$4])
+ ac_cv_func_ext_$1=$ac_cv_func_$1
+])
+AC_DEFUN([SMB_MODULE_DEFAULT], [echo -n ""])
+AC_DEFUN([SMB_LIBRARY_ENABLE], [echo -n ""])
+AC_DEFUN([SMB_EXT_LIB], [echo -n ""])
+AC_DEFUN([SMB_ENABLE], [echo -n ""])
+AC_INIT(ldb, 0.9.3)
+AC_CONFIG_SRCDIR([common/ldb.c])
+
+AC_LIBREPLACE_ALL_CHECKS
+
+if test "$ac_cv_prog_gcc" = yes; then
+ CFLAGS="$CFLAGS -Wall -Wshadow -Wstrict-prototypes -Wpointer-arith -Wcast-qual -Wcast-align -Wwrite-strings"
+fi
+
+WITH_GCOV=0
+AC_ARG_ENABLE(gcov,
+ AS_HELP_STRING([--enable-gcov],[enable GCOV code coverage tests]),
+ [ WITH_GCOV=1])
+AC_SUBST(WITH_GCOV)
+if test x"$with_gcov_support" = x"yes"; then
+ CFLAGS="$CFLAGS -ftest-coverage -fprofile-arcs"
+ LIBS="$LIBS -lgcov"
+fi
+
+AC_PATH_PROG(XSLTPROC,xsltproc)
+AC_PATH_PROG(DOXYGEN,doxygen)
+AC_PATH_PROG(GCOV,gcov)
+AC_PATH_PROG(SLAPD,slapd)
+AC_CHECK_HEADERS(stdint.h dlfcn.h)
+AC_CONFIG_HEADER(include/config.h)
+
+LDB_MODULESDIR="${libdir}/ldb"
+AC_SUBST(LDB_MODULESDIR)
+
+TESTS=""
+EXTRA_OBJ=""
+
+m4_include(build_macros.m4)
+BUILD_WITH_SHARED_BUILD_DIR
+
+m4_include(pkg.m4)
+m4_include(libpopt.m4)
+m4_include(libtalloc.m4)
+m4_include(libtdb.m4)
+m4_include(libevents.m4)
+
+m4_include(ldap.m4)
+if test x"$with_ldap_support" = x"yes"; then
+ CFLAGS="$CFLAGS -DHAVE_LDB_LDAP=1"
+ EXTRA_OBJ="$EXTRA_OBJ ldb_ldap/ldb_ldap.o"
+ LDAP_LIBS="-llber -lldap"
+ AC_SUBST(LDAP_LIBS)
+ TESTS="$TESTS test-ldap.sh"
+fi
+
+m4_include(sqlite3.m4)
+if test x"$with_sqlite3_support" = x"yes"; then
+ LIBS="$LIBS -lsqlite3"
+ CFLAGS="$CFLAGS -DHAVE_LDB_SQLITE3=1"
+ EXTRA_OBJ="$EXTRA_OBJ ldb_sqlite3/ldb_sqlite3.o"
+ TESTS="$TESTS test-sqlite3.sh"
+fi
+
+AC_SUBST(TESTS)
+AC_SUBST(EXTRA_OBJ)
+
+AC_LD_EXPORT_DYNAMIC
+AC_LD_PICFLAG
+AC_LD_SHLIBEXT
+AC_LD_SONAMEFLAG
+AC_LIBREPLACE_SHLD
+AC_LIBREPLACE_SHLD_FLAGS
+AC_LIBREPLACE_MDLD
+AC_LIBREPLACE_MDLD_FLAGS
+AC_LIBREPLACE_RUNTIME_LIB_PATH_VAR
+
+AC_PATH_PROGS([PYTHON_CONFIG], [python2.6-config python2.5-config python2.4-config python-config])
+AC_PATH_PROGS([PYTHON], [python2.6 python2.5 python2.4 python])
+
+PYTHON_BUILD_TARGET="build-python"
+PYTHON_INSTALL_TARGET="install-python"
+PYTHON_CHECK_TARGET="check-python"
+AC_SUBST(PYTHON_BUILD_TARGET)
+AC_SUBST(PYTHON_INSTALL_TARGET)
+AC_SUBST(PYTHON_CHECK_TARGET)
+
+if test -z "$PYTHON_CONFIG"; then
+ PYTHON_BUILD_TARGET=""
+ PYTHON_CHECK_TARGET=""
+ PYTHON_INSTALL_TARGET=""
+fi
+
+m4_include(libldb.m4)
+AC_OUTPUT(Makefile ldb.pc)
diff --git a/source4/lib/ldb/docs/builddocs.sh b/source4/lib/ldb/docs/builddocs.sh
new file mode 100755
index 0000000000..449dcb2681
--- /dev/null
+++ b/source4/lib/ldb/docs/builddocs.sh
@@ -0,0 +1,52 @@
+#!/bin/sh
+# build ldb docs
+# tridge@samba.org August 2006
+
+XSLTPROC="$1"
+SRCDIR="$2"
+
+if [ -z "$XSLTPROC" ] || [ ! -x "$XSLTPROC" ]; then
+ echo "xsltproc not installed"
+ exit 0
+fi
+
+MANXSL="http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl"
+HTMLXSL="http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl"
+
+mkdir -p man
+
+for f in $SRCDIR/man/*.xml; do
+ base=`basename $f .xml`
+ out=man/"`basename $base`"
+ if [ ! -f "$out" ] || [ "$f" -nt "$out" ]; then
+ echo Processing manpage $f
+ $XSLTPROC --nonet -o "$out" "$MANXSL" $f
+ ret=$?
+ if [ "$ret" = "4" ]; then
+ echo "ignoring stylesheet error 4 for $MANXSL"
+ exit 0
+ fi
+ if [ "$ret" != "0" ]; then
+ echo "xsltproc failed with error $ret"
+ exit $ret
+ fi
+ fi
+done
+
+for f in $SRCDIR/man/*.xml; do
+ base=`basename $f .xml`
+ out=man/"`basename $base`".html
+ if [ ! -f "$out" ] || [ "$f" -nt "$out" ]; then
+ echo Processing html $f
+ $XSLTPROC --nonet -o "$out" "$HTMLXSL" $f
+ ret=$?
+ if [ "$ret" = "4" ]; then
+ echo "ignoring stylesheet error 4 for $HTMLXSL"
+ exit 0
+ fi
+ if [ "$ret" != "0" ]; then
+ echo "xsltproc failed with error $ret"
+ exit $ret
+ fi
+ fi
+done
diff --git a/source4/lib/ldb/docs/design.txt b/source4/lib/ldb/docs/design.txt
new file mode 100644
index 0000000000..0bb278b5b4
--- /dev/null
+++ b/source4/lib/ldb/docs/design.txt
@@ -0,0 +1,41 @@
+The list of indexed fields
+--------------------------
+
+dn=@INDEXLIST
+ list of field names that are indexed
+
+ contains fields of type @IDXATTR which contain attriute names
+ of indexed fields
+
+
+Data records
+------------
+
+for each user record in the db there is:
+ main record
+ key: DN=dn
+ data: packed attribute/value list
+
+ a index record for each indexed field in the record
+
+
+Index Records
+-------------
+
+The index records contain the list of dn's that contain records
+matching the index key
+
+All index records are of the form:
+ dn=@INDEX:field:value
+
+and contain fields of type @IDX which are the dns of the records
+that have that value for some attribute
+
+
+Search Expressions
+------------------
+
+Very similar to LDAP search expressions, but does not allow ~=, <= or >=
+
+ attrib0 := (field=value)
+ attrib := attrib0 | (attrib&&attrib) | (attrib||attrib) | !attrib
diff --git a/source4/lib/ldb/docs/installdocs.sh b/source4/lib/ldb/docs/installdocs.sh
new file mode 100755
index 0000000000..6cc7b74ad5
--- /dev/null
+++ b/source4/lib/ldb/docs/installdocs.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+# install ldb docs
+# tridge@samba.org August 2006
+
+MANDIR="$1"
+
+MAN1="`/bin/ls man/*.1`"
+MAN3="`/bin/ls man/*.3`"
+
+if [ -z "$MAN1" ] && [ -z "$MAN3" ]; then
+ echo "No manpages have been built"
+ exit 0
+fi
+
+mkdir -p "$MANDIR/man1" "$MANDIR/man3"
+cp $MAN1 "$MANDIR/man1/" || exit 1
+cp $MAN3 "$MANDIR/man3/" || exit 1
diff --git a/source4/lib/ldb/examples.dox b/source4/lib/ldb/examples.dox
new file mode 100644
index 0000000000..ef4b4f0a40
--- /dev/null
+++ b/source4/lib/ldb/examples.dox
@@ -0,0 +1,16 @@
+/** \example ldbreader.c
+
+The code below shows a simple LDB application.
+
+It lists / dumps the records in a LDB database to standard output.
+
+*/
+
+
+/** \example ldifreader.c
+
+The code below shows a simple LDB application.
+
+It lists / dumps the entries in an LDIF file to standard output.
+
+*/
diff --git a/source4/lib/ldb/examples/ldbreader.c b/source4/lib/ldb/examples/ldbreader.c
new file mode 100644
index 0000000000..3496baf4ce
--- /dev/null
+++ b/source4/lib/ldb/examples/ldbreader.c
@@ -0,0 +1,122 @@
+/*
+ example code for the ldb database library
+
+ Copyright (C) Brad Hards (bradh@frogmouth.net) 2005-2006
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/** \example ldbreader.c
+
+The code below shows a simple LDB application.
+
+It lists / dumps the records in a LDB database to standard output.
+
+*/
+
+#include "ldb.h"
+
+/*
+ ldb_ldif_write takes a function pointer to a custom output
+ function. This version is about as simple as the output function can
+ be. In a more complex example, you'd likely be doing something with
+ the private data function (e.g. holding a file handle).
+*/
+static int vprintf_fn(void *private_data, const char *fmt, ...)
+{
+ int retval;
+ va_list ap;
+
+ va_start(ap, fmt);
+ /* We just write to standard output */
+ retval = vprintf(fmt, ap);
+ va_end(ap);
+ /* Note that the function should return the number of
+ bytes written, or a negative error code */
+ return retval;
+}
+
+int main(int argc, const char **argv)
+{
+ struct ldb_context *ldb;
+ const char *expression = "(dn=*)";
+ struct ldb_result *resultMsg;
+ int i;
+
+ /*
+ This is the always the first thing you want to do in an LDB
+ application - initialise up the context structure.
+
+ Note that you can use the context structure as a parent
+ for talloc allocations as well
+ */
+ ldb = ldb_init(NULL, NULL);
+
+ /*
+ We now open the database. In this example we just hard code the connection path.
+
+ Also note that the database is being opened read-only. This means that the
+ call will fail unless the database already exists.
+ */
+ if (LDB_SUCCESS != ldb_connect(ldb, "tdb://tdbtest.ldb", LDB_FLG_RDONLY, NULL) ){
+ printf("Problem on connection\n");
+ exit(-1);
+ }
+
+ /*
+ At this stage we have an open database, and can start using it. It is opened
+ read-only, so a query is possible.
+
+ We construct a search that just returns all the (sensible) contents. You can do
+ quite fine grained results with the LDAP search syntax, however it is a bit
+ confusing to start with. See RFC2254.
+ */
+ if (LDB_SUCCESS != ldb_search(ldb, ldb, &resultMsg,
+ NULL, LDB_SCOPE_DEFAULT, NULL,
+ "%s", expression)) {
+ printf("Problem in search\n");
+ exit(-1);
+ }
+
+ printf("%i records returned\n", resultMsg->count);
+
+ /*
+ We can now iterate through the results, writing them out
+ (to standard output) with our custom output routine as defined
+ at the top of this file
+ */
+ for (i = 0; i < resultMsg->count; ++i) {
+ struct ldb_ldif ldifMsg;
+
+ printf("Message: %i\n", i+1);
+
+ ldifMsg.changetype = LDB_CHANGETYPE_NONE;
+ ldifMsg.msg = resultMsg->msgs[i];
+ ldb_ldif_write(ldb, vprintf_fn, NULL, &ldifMsg);
+ }
+
+ /*
+ There are two objects to clean up - the result from the
+ ldb_search() query, and the original ldb context.
+ */
+ talloc_free(resultMsg);
+
+ talloc_free(ldb);
+
+ return 0;
+}
diff --git a/source4/lib/ldb/examples/ldifreader.c b/source4/lib/ldb/examples/ldifreader.c
new file mode 100644
index 0000000000..dcd9daf812
--- /dev/null
+++ b/source4/lib/ldb/examples/ldifreader.c
@@ -0,0 +1,125 @@
+/*
+ example code for the ldb database library
+
+ Copyright (C) Brad Hards (bradh@frogmouth.net) 2005-2006
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/** \example ldifreader.c
+
+The code below shows a simple LDB application.
+
+It lists / dumps the entries in an LDIF file to standard output.
+
+*/
+
+#include "ldb.h"
+
+/*
+ ldb_ldif_write takes a function pointer to a custom output
+ function. This version is about as simple as the output function can
+ be. In a more complex example, you'd likely be doing something with
+ the private data function (e.g. holding a file handle).
+*/
+static int vprintf_fn(void *private_data, const char *fmt, ...)
+{
+ int retval;
+ va_list ap;
+
+ va_start(ap, fmt);
+ /* We just write to standard output */
+ retval = vprintf(fmt, ap);
+ va_end(ap);
+ /* Note that the function should return the number of
+ bytes written, or a negative error code */
+ return retval;
+}
+
+int main(int argc, const char **argv)
+{
+ struct ldb_context *ldb;
+ FILE *fileStream;
+ struct ldb_ldif *ldifMsg;
+
+ if (argc != 2) {
+ printf("Usage %s filename.ldif\n", argv[0]);
+ exit(1);
+ }
+
+ /*
+ This is the always the first thing you want to do in an LDB
+ application - initialise up the context structure.
+
+ Note that you can use the context structure as a parent
+ for talloc allocations as well
+ */
+ ldb = ldb_init(NULL, NULL);
+
+ fileStream = fopen(argv[1], "r");
+ if (0 == fileStream) {
+ perror(argv[1]);
+ exit(1);
+ }
+
+ /*
+ We now work through the filestream to get each entry.
+ */
+ while ( (ldifMsg = ldb_ldif_read_file(ldb, fileStream)) ) {
+ /*
+ Each message has a particular change type. For Add,
+ Modify and Delete, this will also appear in the
+ output listing (as changetype: add, changetype:
+ modify or changetype:delete, respectively).
+ */
+ switch (ldifMsg->changetype) {
+ case LDB_CHANGETYPE_NONE:
+ printf("ChangeType: None\n");
+ break;
+ case LDB_CHANGETYPE_ADD:
+ printf("ChangeType: Add\n");
+ break;
+ case LDB_CHANGETYPE_MODIFY:
+ printf("ChangeType: Modify\n");
+ break;
+ case LDB_CHANGETYPE_DELETE:
+ printf("ChangeType: Delete\n");
+ break;
+ default:
+ printf("ChangeType: Unknown\n");
+ }
+
+ /*
+ We can now write out the results, using our custom
+ output routine as defined at the top of this file.
+ */
+ ldb_ldif_write(ldb, vprintf_fn, NULL, ldifMsg);
+
+ /*
+ Clean up the message
+ */
+ ldb_ldif_read_free(ldb, ldifMsg);
+ }
+
+ /*
+ Clean up the context
+ */
+ talloc_free(ldb);
+
+ return 0;
+}
diff --git a/source4/lib/ldb/external/libevents.m4 b/source4/lib/ldb/external/libevents.m4
new file mode 100644
index 0000000000..6a0e36af8b
--- /dev/null
+++ b/source4/lib/ldb/external/libevents.m4
@@ -0,0 +1,7 @@
+AC_SUBST(TEVENT_OBJ)
+AC_SUBST(TEVENT_CFLAGS)
+AC_SUBST(TEVENT_LIBS)
+
+AC_CHECK_HEADER(tevent.h,
+ [AC_CHECK_LIB(tevent, tevent_context_init, [TEVENT_LIBS="-ltevent"]) ],
+ [PKG_CHECK_MODULES(TEVENT, tevent)])
diff --git a/source4/lib/ldb/external/libpopt.m4 b/source4/lib/ldb/external/libpopt.m4
new file mode 100644
index 0000000000..c5d12550ab
--- /dev/null
+++ b/source4/lib/ldb/external/libpopt.m4
@@ -0,0 +1,7 @@
+POPT_OBJ=""
+AC_SUBST(POPT_OBJ)
+AC_SUBST(POPT_LIBS)
+AC_SUBST(POPT_CFLAGS)
+
+AC_CHECK_HEADERS(popt.h)
+AC_CHECK_LIB(popt, poptGetContext, [ POPT_LIBS="-lpopt" ])
diff --git a/source4/lib/ldb/external/libtalloc.m4 b/source4/lib/ldb/external/libtalloc.m4
new file mode 100644
index 0000000000..a4c5b8a9d9
--- /dev/null
+++ b/source4/lib/ldb/external/libtalloc.m4
@@ -0,0 +1,7 @@
+AC_SUBST(TALLOC_OBJ)
+AC_SUBST(TALLOC_CFLAGS)
+AC_SUBST(TALLOC_LIBS)
+
+AC_CHECK_HEADER(talloc.h,
+ [AC_CHECK_LIB(talloc, talloc_init, [TALLOC_LIBS="-ltalloc"]) ],
+ [PKG_CHECK_MODULES(TALLOC, talloc)])
diff --git a/source4/lib/ldb/external/libtdb.m4 b/source4/lib/ldb/external/libtdb.m4
new file mode 100644
index 0000000000..8c2cab702f
--- /dev/null
+++ b/source4/lib/ldb/external/libtdb.m4
@@ -0,0 +1,7 @@
+AC_SUBST(TDB_OBJ)
+AC_SUBST(TDB_CFLAGS)
+AC_SUBST(TDB_LIBS)
+
+AC_CHECK_HEADER(tdb.h,
+ [AC_CHECK_LIB(tdb, tdb_open, [TDB_LIBS="-ltdb"]) ],
+ [PKG_CHECK_MODULES(TDB, tdb >= 1.1.0)])
diff --git a/source4/lib/ldb/external/pkg.m4 b/source4/lib/ldb/external/pkg.m4
new file mode 100644
index 0000000000..a8b3d06c81
--- /dev/null
+++ b/source4/lib/ldb/external/pkg.m4
@@ -0,0 +1,156 @@
+# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*-
+#
+# Copyright © 2004 Scott James Remnant <scott@netsplit.com>.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <http://www.gnu.org/licenses/>.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# PKG_PROG_PKG_CONFIG([MIN-VERSION])
+# ----------------------------------
+AC_DEFUN([PKG_PROG_PKG_CONFIG],
+[m4_pattern_forbid([^_?PKG_[A-Z_]+$])
+m4_pattern_allow([^PKG_CONFIG(_PATH)?$])
+AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])dnl
+if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
+ AC_PATH_TOOL([PKG_CONFIG], [pkg-config])
+fi
+if test -n "$PKG_CONFIG"; then
+ _pkg_min_version=m4_default([$1], [0.9.0])
+ AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version])
+ if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
+ AC_MSG_RESULT([yes])
+ else
+ AC_MSG_RESULT([no])
+ PKG_CONFIG=""
+ fi
+
+fi[]dnl
+])# PKG_PROG_PKG_CONFIG
+
+# PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
+#
+# Check to see whether a particular set of modules exists. Similar
+# to PKG_CHECK_MODULES(), but does not set variables or print errors.
+#
+#
+# Similar to PKG_CHECK_MODULES, make sure that the first instance of
+# this or PKG_CHECK_MODULES is called, or make sure to call
+# PKG_CHECK_EXISTS manually
+# --------------------------------------------------------------
+AC_DEFUN([PKG_CHECK_EXISTS],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+if test -n "$PKG_CONFIG" && \
+ AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then
+ m4_ifval([$2], [$2], [:])
+m4_ifvaln([$3], [else
+ $3])dnl
+fi])
+
+
+# _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES])
+# ---------------------------------------------
+m4_define([_PKG_CONFIG],
+[if test -n "$PKG_CONFIG"; then
+ if test -n "$$1"; then
+ pkg_cv_[]$1="$$1"
+ else
+ PKG_CHECK_EXISTS([$3],
+ [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`],
+ [pkg_failed=yes])
+ fi
+else
+ pkg_failed=untried
+fi[]dnl
+])# _PKG_CONFIG
+
+# _PKG_SHORT_ERRORS_SUPPORTED
+# -----------------------------
+AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi[]dnl
+])# _PKG_SHORT_ERRORS_SUPPORTED
+
+
+# PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
+# [ACTION-IF-NOT-FOUND])
+#
+#
+# Note that if there is a possibility the first call to
+# PKG_CHECK_MODULES might not happen, you should be sure to include an
+# explicit call to PKG_PROG_PKG_CONFIG in your configure.ac
+#
+#
+# --------------------------------------------------------------
+AC_DEFUN([PKG_CHECK_MODULES],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl
+AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl
+
+pkg_failed=no
+AC_MSG_CHECKING([for $1])
+
+_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2])
+_PKG_CONFIG([$1][_LIBS], [libs], [$2])
+
+m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS
+and $1[]_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.])
+
+if test $pkg_failed = yes; then
+ _PKG_SHORT_ERRORS_SUPPORTED
+ if test $_pkg_short_errors_supported = yes; then
+ $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --errors-to-stdout --print-errors "$2"`
+ else
+ $1[]_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "$2"`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD
+
+ ifelse([$4], , [AC_MSG_ERROR(dnl
+[Package requirements ($2) were not met:
+
+$$1_PKG_ERRORS
+
+Consider adjusting the PKG_CONFIG_PATH environment variable if you
+installed software in a non-standard prefix.
+
+_PKG_TEXT
+])],
+ [AC_MSG_RESULT([no])
+ $4])
+elif test $pkg_failed = untried; then
+ ifelse([$4], , [AC_MSG_FAILURE(dnl
+[The pkg-config script could not be found or is too old. Make sure it
+is in your PATH or set the PKG_CONFIG environment variable to the full
+path to pkg-config.
+
+_PKG_TEXT
+
+To get pkg-config, see <http://www.freedesktop.org/software/pkgconfig>.])],
+ [$4])
+else
+ $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS
+ $1[]_LIBS=$pkg_cv_[]$1[]_LIBS
+ AC_MSG_RESULT([yes])
+ ifelse([$3], , :, [$3])
+fi[]dnl
+])# PKG_CHECK_MODULES
diff --git a/source4/lib/ldb/include/dlinklist.h b/source4/lib/ldb/include/dlinklist.h
new file mode 100644
index 0000000000..acab9fa043
--- /dev/null
+++ b/source4/lib/ldb/include/dlinklist.h
@@ -0,0 +1,114 @@
+/*
+ Unix SMB/CIFS implementation.
+ some simple double linked list macros
+ Copyright (C) Andrew Tridgell 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 <http://www.gnu.org/licenses/>.
+*/
+
+/* To use these macros you must have a structure containing a next and
+ prev pointer */
+
+#ifndef _DLINKLIST_H
+#define _DLINKLIST_H
+
+/* hook into the front of the list */
+#define DLIST_ADD(list, p) \
+do { \
+ if (!(list)) { \
+ (list) = (p); \
+ (p)->next = (p)->prev = NULL; \
+ } else { \
+ (list)->prev = (p); \
+ (p)->next = (list); \
+ (p)->prev = NULL; \
+ (list) = (p); \
+ }\
+} while (0)
+
+/* remove an element from a list - element doesn't have to be in list. */
+#ifndef DLIST_REMOVE
+#define DLIST_REMOVE(list, p) \
+do { \
+ if ((p) == (list)) { \
+ (list) = (p)->next; \
+ if (list) (list)->prev = NULL; \
+ } else { \
+ if ((p)->prev) (p)->prev->next = (p)->next; \
+ if ((p)->next) (p)->next->prev = (p)->prev; \
+ } \
+ if ((p) && ((p) != (list))) (p)->next = (p)->prev = NULL; \
+} while (0)
+#endif
+
+/* promote an element to the top of the list */
+#define DLIST_PROMOTE(list, p) \
+do { \
+ DLIST_REMOVE(list, p); \
+ DLIST_ADD(list, p); \
+} while (0)
+
+/* hook into the end of the list - needs a tmp pointer */
+#define DLIST_ADD_END(list, p, type) \
+do { \
+ if (!(list)) { \
+ (list) = (p); \
+ (p)->next = (p)->prev = NULL; \
+ } else { \
+ type tmp; \
+ for (tmp = (list); tmp->next; tmp = tmp->next) ; \
+ tmp->next = (p); \
+ (p)->next = NULL; \
+ (p)->prev = tmp; \
+ } \
+} while (0)
+
+/* insert 'p' after the given element 'el' in a list. If el is NULL then
+ this is the same as a DLIST_ADD() */
+#define DLIST_ADD_AFTER(list, p, el) \
+do { \
+ if (!(list) || !(el)) { \
+ DLIST_ADD(list, p); \
+ } else { \
+ p->prev = el; \
+ p->next = el->next; \
+ el->next = p; \
+ if (p->next) p->next->prev = p; \
+ }\
+} while (0)
+
+/* demote an element to the end of the list, needs a tmp pointer */
+#define DLIST_DEMOTE(list, p, tmp) \
+do { \
+ DLIST_REMOVE(list, p); \
+ DLIST_ADD_END(list, p, tmp); \
+} while (0)
+
+/* concatenate two lists - putting all elements of the 2nd list at the
+ end of the first list */
+#define DLIST_CONCATENATE(list1, list2, type) \
+do { \
+ if (!(list1)) { \
+ (list1) = (list2); \
+ } else { \
+ type tmp; \
+ for (tmp = (list1); tmp->next; tmp = tmp->next) ; \
+ tmp->next = (list2); \
+ if (list2) { \
+ (list2)->prev = tmp; \
+ } \
+ } \
+} while (0)
+
+#endif /* _DLINKLIST_H */
diff --git a/source4/lib/ldb/include/ldb.h b/source4/lib/ldb/include/ldb.h
new file mode 100644
index 0000000000..be41151409
--- /dev/null
+++ b/source4/lib/ldb/include/ldb.h
@@ -0,0 +1,1840 @@
+/*
+ ldb database library
+
+ Copyright (C) Andrew Tridgell 2004
+ Copyright (C) Stefan Metzmacher 2004
+ Copyright (C) Simo Sorce 2005-2006
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb header
+ *
+ * Description: defines for base ldb API
+ *
+ * Author: Andrew Tridgell
+ * Author: Stefan Metzmacher
+ */
+
+/**
+ \file ldb.h Samba's ldb database
+
+ This header file provides the main API for ldb.
+*/
+
+#ifndef _LDB_H_
+
+/*! \cond DOXYGEN_IGNORE */
+#define _LDB_H_ 1
+/*! \endcond */
+
+#include <stdbool.h>
+#include "talloc.h"
+#include "tevent.h"
+#include "ldb_errors.h"
+
+/*
+ major restrictions as compared to normal LDAP:
+
+ - each record must have a unique key field
+ - the key must be representable as a NULL terminated C string and may not
+ contain a comma or braces
+
+ major restrictions as compared to tdb:
+
+ - no explicit locking calls, but we have transactions when using ldb_tdb
+
+*/
+
+#ifndef ldb_val
+/**
+ Result value
+
+ An individual lump of data in a result comes in this format. The
+ pointer will usually be to a UTF-8 string if the application is
+ sensible, but it can be to anything you like, including binary data
+ blobs of arbitrary size.
+
+ \note the data is null (0x00) terminated, but the length does not
+ include the terminator.
+*/
+struct ldb_val {
+ uint8_t *data; /*!< result data */
+ size_t length; /*!< length of data */
+};
+#endif
+
+/*! \cond DOXYGEN_IGNORE */
+#ifndef PRINTF_ATTRIBUTE
+#define PRINTF_ATTRIBUTE(a,b)
+#endif
+/*! \endcond */
+
+/* opaque ldb_dn structures, see ldb_dn.c for internals */
+struct ldb_dn_component;
+struct ldb_dn;
+
+/**
+ There are a number of flags that are used with ldap_modify() in
+ ldb_message_element.flags fields. The LDA_FLAGS_MOD_ADD,
+ LDA_FLAGS_MOD_DELETE and LDA_FLAGS_MOD_REPLACE flags are used in
+ ldap_modify() calls to specify whether attributes are being added,
+ deleted or modified respectively.
+*/
+#define LDB_FLAG_MOD_MASK 0x3
+
+/**
+ Flag value used in ldap_modify() to indicate that attributes are
+ being added.
+
+ \sa LDB_FLAG_MOD_MASK
+*/
+#define LDB_FLAG_MOD_ADD 1
+
+/**
+ Flag value used in ldap_modify() to indicate that attributes are
+ being replaced.
+
+ \sa LDB_FLAG_MOD_MASK
+*/
+#define LDB_FLAG_MOD_REPLACE 2
+
+/**
+ Flag value used in ldap_modify() to indicate that attributes are
+ being deleted.
+
+ \sa LDB_FLAG_MOD_MASK
+*/
+#define LDB_FLAG_MOD_DELETE 3
+
+/**
+ OID for logic AND comaprison.
+
+ This is the well known object ID for a logical AND comparitor.
+*/
+#define LDB_OID_COMPARATOR_AND "1.2.840.113556.1.4.803"
+
+/**
+ OID for logic OR comparison.
+
+ This is the well known object ID for a logical OR comparitor.
+*/
+#define LDB_OID_COMPARATOR_OR "1.2.840.113556.1.4.804"
+
+/**
+ results are given back as arrays of ldb_message_element
+*/
+struct ldb_message_element {
+ unsigned int flags;
+ const char *name;
+ unsigned int num_values;
+ struct ldb_val *values;
+};
+
+
+/**
+ a ldb_message represents all or part of a record. It can contain an arbitrary
+ number of elements.
+*/
+struct ldb_message {
+ struct ldb_dn *dn;
+ unsigned int num_elements;
+ struct ldb_message_element *elements;
+};
+
+enum ldb_changetype {
+ LDB_CHANGETYPE_NONE=0,
+ LDB_CHANGETYPE_ADD,
+ LDB_CHANGETYPE_DELETE,
+ LDB_CHANGETYPE_MODIFY
+};
+
+/**
+ LDIF record
+
+ This structure contains a LDIF record, as returned from ldif_read()
+ and equivalent functions.
+*/
+struct ldb_ldif {
+ enum ldb_changetype changetype; /*!< The type of change */
+ struct ldb_message *msg; /*!< The changes */
+};
+
+enum ldb_scope {LDB_SCOPE_DEFAULT=-1,
+ LDB_SCOPE_BASE=0,
+ LDB_SCOPE_ONELEVEL=1,
+ LDB_SCOPE_SUBTREE=2};
+
+struct ldb_context;
+struct tevent_context;
+
+/* debugging uses one of the following levels */
+enum ldb_debug_level {LDB_DEBUG_FATAL, LDB_DEBUG_ERROR,
+ LDB_DEBUG_WARNING, LDB_DEBUG_TRACE};
+
+/**
+ the user can optionally supply a debug function. The function
+ is based on the vfprintf() style of interface, but with the addition
+ of a severity level
+*/
+struct ldb_debug_ops {
+ void (*debug)(void *context, enum ldb_debug_level level,
+ const char *fmt, va_list ap) PRINTF_ATTRIBUTE(3,0);
+ void *context;
+};
+
+/**
+ The user can optionally supply a custom utf8 functions,
+ to handle comparisons and casefolding.
+*/
+struct ldb_utf8_fns {
+ void *context;
+ char *(*casefold)(void *context, TALLOC_CTX *mem_ctx, const char *s, size_t n);
+};
+
+/**
+ Flag value for database connection mode.
+
+ If LDB_FLG_RDONLY is used in ldb_connect, then the database will be
+ opened read-only, if possible.
+*/
+#define LDB_FLG_RDONLY 1
+
+/**
+ Flag value for database connection mode.
+
+ If LDB_FLG_NOSYNC is used in ldb_connect, then the database will be
+ opened without synchronous operations, if possible.
+*/
+#define LDB_FLG_NOSYNC 2
+
+/**
+ Flag value to specify autoreconnect mode.
+
+ If LDB_FLG_RECONNECT is used in ldb_connect, then the backend will
+ be opened in a way that makes it try to auto reconnect if the
+ connection is dropped (actually make sense only with ldap).
+*/
+#define LDB_FLG_RECONNECT 4
+
+/**
+ Flag to tell backends not to use mmap
+*/
+#define LDB_FLG_NOMMAP 8
+
+/*
+ structures for ldb_parse_tree handling code
+*/
+enum ldb_parse_op { LDB_OP_AND=1, LDB_OP_OR=2, LDB_OP_NOT=3,
+ LDB_OP_EQUALITY=4, LDB_OP_SUBSTRING=5,
+ LDB_OP_GREATER=6, LDB_OP_LESS=7, LDB_OP_PRESENT=8,
+ LDB_OP_APPROX=9, LDB_OP_EXTENDED=10 };
+
+struct ldb_parse_tree {
+ enum ldb_parse_op operation;
+ union {
+ struct {
+ struct ldb_parse_tree *child;
+ } isnot;
+ struct {
+ const char *attr;
+ struct ldb_val value;
+ } equality;
+ struct {
+ const char *attr;
+ int start_with_wildcard;
+ int end_with_wildcard;
+ struct ldb_val **chunks;
+ } substring;
+ struct {
+ const char *attr;
+ } present;
+ struct {
+ const char *attr;
+ struct ldb_val value;
+ } comparison;
+ struct {
+ const char *attr;
+ int dnAttributes;
+ char *rule_id;
+ struct ldb_val value;
+ } extended;
+ struct {
+ unsigned int num_elements;
+ struct ldb_parse_tree **elements;
+ } list;
+ } u;
+};
+
+struct ldb_parse_tree *ldb_parse_tree(TALLOC_CTX *mem_ctx, const char *s);
+char *ldb_filter_from_tree(TALLOC_CTX *mem_ctx, struct ldb_parse_tree *tree);
+
+/**
+ Encode a binary blob
+
+ This function encodes a binary blob using the encoding rules in RFC
+ 2254 (Section 4). This function also escapes any non-printable
+ characters.
+
+ \param mem_ctx the memory context to allocate the return string in.
+ \param val the (potentially) binary data to be encoded
+
+ \return the encoded data as a null terminated string
+
+ \sa <a href="http://www.ietf.org/rfc/rfc2252.txt">RFC 2252</a>.
+*/
+char *ldb_binary_encode(TALLOC_CTX *mem_ctx, struct ldb_val val);
+
+/**
+ Encode a string
+
+ This function encodes a string using the encoding rules in RFC 2254
+ (Section 4). This function also escapes any non-printable
+ characters.
+
+ \param mem_ctx the memory context to allocate the return string in.
+ \param string the string to be encoded
+
+ \return the encoded data as a null terminated string
+
+ \sa <a href="http://www.ietf.org/rfc/rfc2252.txt">RFC 2252</a>.
+*/
+char *ldb_binary_encode_string(TALLOC_CTX *mem_ctx, const char *string);
+
+/*
+ functions for controlling attribute handling
+*/
+typedef int (*ldb_attr_handler_t)(struct ldb_context *, TALLOC_CTX *mem_ctx, const struct ldb_val *, struct ldb_val *);
+typedef int (*ldb_attr_comparison_t)(struct ldb_context *, TALLOC_CTX *mem_ctx, const struct ldb_val *, const struct ldb_val *);
+
+/*
+ attribute handler structure
+
+ attr -> The attribute name
+ ldif_read_fn -> convert from ldif to binary format
+ ldif_write_fn -> convert from binary to ldif format
+ canonicalise_fn -> canonicalise a value, for use by indexing and dn construction
+ comparison_fn -> compare two values
+*/
+
+struct ldb_schema_syntax {
+ const char *name;
+ ldb_attr_handler_t ldif_read_fn;
+ ldb_attr_handler_t ldif_write_fn;
+ ldb_attr_handler_t canonicalise_fn;
+ ldb_attr_comparison_t comparison_fn;
+};
+
+struct ldb_schema_attribute {
+ const char *name;
+ unsigned flags;
+ const struct ldb_schema_syntax *syntax;
+};
+
+const struct ldb_schema_attribute *ldb_schema_attribute_by_name(struct ldb_context *ldb,
+ const char *name);
+
+struct ldb_dn_extended_syntax {
+ const char *name;
+ ldb_attr_handler_t read_fn;
+ ldb_attr_handler_t write_clear_fn;
+ ldb_attr_handler_t write_hex_fn;
+};
+
+const struct ldb_dn_extended_syntax *ldb_dn_extended_syntax_by_name(struct ldb_context *ldb,
+ const char *name);
+
+/**
+ The attribute is not returned by default
+*/
+#define LDB_ATTR_FLAG_HIDDEN (1<<0)
+
+/* the attribute handler name should be freed when released */
+#define LDB_ATTR_FLAG_ALLOCATED (1<<1)
+
+/**
+ The attribute is supplied by the application and should not be removed
+*/
+#define LDB_ATTR_FLAG_FIXED (1<<2)
+
+/**
+ LDAP attribute syntax for a DN
+
+ This is the well-known LDAP attribute syntax for a DN.
+
+ See <a href="http://www.ietf.org/rfc/rfc2252.txt">RFC 2252</a>, Section 4.3.2
+*/
+#define LDB_SYNTAX_DN "1.3.6.1.4.1.1466.115.121.1.12"
+
+/**
+ LDAP attribute syntax for a Directory String
+
+ This is the well-known LDAP attribute syntax for a Directory String.
+
+ \sa <a href="http://www.ietf.org/rfc/rfc2252.txt">RFC 2252</a>, Section 4.3.2
+*/
+#define LDB_SYNTAX_DIRECTORY_STRING "1.3.6.1.4.1.1466.115.121.1.15"
+
+/**
+ LDAP attribute syntax for an integer
+
+ This is the well-known LDAP attribute syntax for an integer.
+
+ See <a href="http://www.ietf.org/rfc/rfc2252.txt">RFC 2252</a>, Section 4.3.2
+*/
+#define LDB_SYNTAX_INTEGER "1.3.6.1.4.1.1466.115.121.1.27"
+
+/**
+ LDAP attribute syntax for an octet string
+
+ This is the well-known LDAP attribute syntax for an octet string.
+
+ See <a href="http://www.ietf.org/rfc/rfc2252.txt">RFC 2252</a>, Section 4.3.2
+*/
+#define LDB_SYNTAX_OCTET_STRING "1.3.6.1.4.1.1466.115.121.1.40"
+
+/**
+ LDAP attribute syntax for UTC time.
+
+ This is the well-known LDAP attribute syntax for a UTC time.
+
+ See <a href="http://www.ietf.org/rfc/rfc2252.txt">RFC 2252</a>, Section 4.3.2
+*/
+#define LDB_SYNTAX_UTC_TIME "1.3.6.1.4.1.1466.115.121.1.53"
+
+#define LDB_SYNTAX_OBJECTCLASS "LDB_SYNTAX_OBJECTCLASS"
+
+/* sorting helpers */
+typedef int (*ldb_qsort_cmp_fn_t) (void *v1, void *v2, void *opaque);
+
+/**
+ OID for the paged results control. This control is included in the
+ searchRequest and searchResultDone messages as part of the controls
+ field of the LDAPMessage, as defined in Section 4.1.12 of
+ LDAP v3.
+
+ \sa <a href="http://www.ietf.org/rfc/rfc2696.txt">RFC 2696</a>.
+*/
+#define LDB_CONTROL_PAGED_RESULTS_OID "1.2.840.113556.1.4.319"
+
+/**
+ OID for specifying the returned elements of the ntSecurityDescriptor
+
+ \sa <a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/ldap/ldap/ldap_server_sd_flags_oid.asp">Microsoft documentation of this OID</a>
+*/
+#define LDB_CONTROL_SD_FLAGS_OID "1.2.840.113556.1.4.801"
+
+/**
+ OID for specifying an advanced scope for the search (one partition)
+
+ \sa <a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/ldap/ldap/ldap_server_domain_scope_oid.asp">Microsoft documentation of this OID</a>
+*/
+#define LDB_CONTROL_DOMAIN_SCOPE_OID "1.2.840.113556.1.4.1339"
+
+/**
+ OID for specifying an advanced scope for a search
+
+ \sa <a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/ldap/ldap/ldap_server_search_options_oid.asp">Microsoft documentation of this OID</a>
+*/
+#define LDB_CONTROL_SEARCH_OPTIONS_OID "1.2.840.113556.1.4.1340"
+
+/**
+ OID for notification
+
+ \sa <a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/ldap/ldap/ldap_server_notification_oid.asp">Microsoft documentation of this OID</a>
+*/
+#define LDB_CONTROL_NOTIFICATION_OID "1.2.840.113556.1.4.528"
+
+/**
+ OID for getting deleted objects
+
+ \sa <a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/ldap/ldap/ldap_server_show_deleted_oid.asp">Microsoft documentation of this OID</a>
+*/
+#define LDB_CONTROL_SHOW_DELETED_OID "1.2.840.113556.1.4.417"
+
+/**
+ OID for extended DN
+
+ \sa <a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/ldap/ldap/ldap_server_extended_dn_oid.asp">Microsoft documentation of this OID</a>
+*/
+#define LDB_CONTROL_EXTENDED_DN_OID "1.2.840.113556.1.4.529"
+
+/**
+ OID for LDAP server sort result extension.
+
+ This control is included in the searchRequest message as part of
+ the controls field of the LDAPMessage, as defined in Section 4.1.12
+ of LDAP v3. The controlType is set to
+ "1.2.840.113556.1.4.473". The criticality MAY be either TRUE or
+ FALSE (where absent is also equivalent to FALSE) at the client's
+ option.
+
+ \sa <a href="http://www.ietf.org/rfc/rfc2891.txt">RFC 2891</a>.
+*/
+#define LDB_CONTROL_SERVER_SORT_OID "1.2.840.113556.1.4.473"
+
+/**
+ OID for LDAP server sort result response extension.
+
+ This control is included in the searchResultDone message as part of
+ the controls field of the LDAPMessage, as defined in Section 4.1.12 of
+ LDAP v3.
+
+ \sa <a href="http://www.ietf.org/rfc/rfc2891.txt">RFC 2891</a>.
+*/
+#define LDB_CONTROL_SORT_RESP_OID "1.2.840.113556.1.4.474"
+
+/**
+ OID for LDAP Attribute Scoped Query extension.
+
+ This control is included in SearchRequest or SearchResponse
+ messages as part of the controls field of the LDAPMessage.
+*/
+#define LDB_CONTROL_ASQ_OID "1.2.840.113556.1.4.1504"
+
+/**
+ OID for LDAP Directory Sync extension.
+
+ This control is included in SearchRequest or SearchResponse
+ messages as part of the controls field of the LDAPMessage.
+*/
+#define LDB_CONTROL_DIRSYNC_OID "1.2.840.113556.1.4.841"
+
+
+/**
+ OID for LDAP Virtual List View Request extension.
+
+ This control is included in SearchRequest messages
+ as part of the controls field of the LDAPMessage.
+*/
+#define LDB_CONTROL_VLV_REQ_OID "2.16.840.1.113730.3.4.9"
+
+/**
+ OID for LDAP Virtual List View Response extension.
+
+ This control is included in SearchResponse messages
+ as part of the controls field of the LDAPMessage.
+*/
+#define LDB_CONTROL_VLV_RESP_OID "2.16.840.1.113730.3.4.10"
+
+/**
+ OID to let modifies don't give an error when adding an existing
+ attribute with the same value or deleting an nonexisting one attribute
+
+ \sa <a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/ldap/ldap/ldap_server_permissive_modify_oid.asp">Microsoft documentation of this OID</a>
+*/
+#define LDB_CONTROL_PERMISSIVE_MODIFY_OID "1.2.840.113556.1.4.1413"
+
+/**
+ OID for LDAP Extended Operation START_TLS.
+
+ This Extended operation is used to start a new TLS
+ channel on top of a clear text channel.
+*/
+#define LDB_EXTENDED_START_TLS_OID "1.3.6.1.4.1.1466.20037"
+
+/**
+*/
+#define LDB_EXTENDED_DYNAMIC_OID "1.3.6.1.4.1.1466.101.119.1"
+
+/**
+*/
+#define LDB_EXTENDED_FAST_BIND_OID "1.2.840.113556.1.4.1781"
+
+struct ldb_sd_flags_control {
+ /*
+ * request the owner 0x00000001
+ * request the group 0x00000002
+ * request the DACL 0x00000004
+ * request the SACL 0x00000008
+ */
+ unsigned secinfo_flags;
+};
+
+/*
+ * DOMAIN_SCOPE 0x00000001
+ * this limits the search to one partition,
+ * and no referrals will be returned.
+ * (Note this doesn't limit the entries by there
+ * objectSid belonging to a domain! Builtin and Foreign Sids
+ * are still returned)
+ *
+ * PHANTOM_ROOT 0x00000002
+ * this search on the whole tree on a domain controller
+ * over multiple partitions without referrals.
+ * (This is the default behavior on the Global Catalog Port)
+ */
+
+#define LDB_SEARCH_OPTION_DOMAIN_SCOPE 0x00000001
+#define LDB_SEARCH_OPTION_PHANTOM_ROOT 0x00000002
+
+struct ldb_search_options_control {
+ unsigned search_options;
+};
+
+struct ldb_paged_control {
+ int size;
+ int cookie_len;
+ char *cookie;
+};
+
+struct ldb_extended_dn_control {
+ int type;
+};
+
+struct ldb_server_sort_control {
+ char *attributeName;
+ char *orderingRule;
+ int reverse;
+};
+
+struct ldb_sort_resp_control {
+ int result;
+ char *attr_desc;
+};
+
+struct ldb_asq_control {
+ int request;
+ char *source_attribute;
+ int src_attr_len;
+ int result;
+};
+
+struct ldb_dirsync_control {
+ int flags;
+ int max_attributes;
+ int cookie_len;
+ char *cookie;
+};
+
+struct ldb_vlv_req_control {
+ int beforeCount;
+ int afterCount;
+ int type;
+ union {
+ struct {
+ int offset;
+ int contentCount;
+ } byOffset;
+ struct {
+ int value_len;
+ char *value;
+ } gtOrEq;
+ } match;
+ int ctxid_len;
+ char *contextId;
+};
+
+struct ldb_vlv_resp_control {
+ int targetPosition;
+ int contentCount;
+ int vlv_result;
+ int ctxid_len;
+ char *contextId;
+};
+
+struct ldb_control {
+ const char *oid;
+ int critical;
+ void *data;
+};
+
+enum ldb_request_type {
+ LDB_SEARCH,
+ LDB_ADD,
+ LDB_MODIFY,
+ LDB_DELETE,
+ LDB_RENAME,
+ LDB_EXTENDED,
+ LDB_REQ_REGISTER_CONTROL,
+ LDB_REQ_REGISTER_PARTITION
+};
+
+enum ldb_reply_type {
+ LDB_REPLY_ENTRY,
+ LDB_REPLY_REFERRAL,
+ LDB_REPLY_DONE
+};
+
+enum ldb_wait_type {
+ LDB_WAIT_ALL,
+ LDB_WAIT_NONE
+};
+
+enum ldb_state {
+ LDB_ASYNC_INIT,
+ LDB_ASYNC_PENDING,
+ LDB_ASYNC_DONE
+};
+
+struct ldb_extended {
+ const char *oid;
+ void *data; /* NULL or a valid talloc pointer! talloc_get_type() will be used on it */
+};
+
+#define LDB_EXTENDED_SEQUENCE_NUMBER "1.3.6.1.4.1.7165.4.4.3"
+
+enum ldb_sequence_type {
+ LDB_SEQ_HIGHEST_SEQ,
+ LDB_SEQ_HIGHEST_TIMESTAMP,
+ LDB_SEQ_NEXT
+};
+
+#define LDB_SEQ_GLOBAL_SEQUENCE 0x01
+#define LDB_SEQ_TIMESTAMP_SEQUENCE 0x02
+
+struct ldb_seqnum_request {
+ enum ldb_sequence_type type;
+};
+
+struct ldb_seqnum_result {
+ uint64_t seq_num;
+ uint32_t flags;
+};
+
+struct ldb_result {
+ unsigned int count;
+ struct ldb_message **msgs;
+ struct ldb_extended *extended;
+ struct ldb_control **controls;
+ char **refs;
+};
+
+struct ldb_reply {
+ int error;
+ enum ldb_reply_type type;
+ struct ldb_message *message;
+ struct ldb_extended *response;
+ struct ldb_control **controls;
+ char *referral;
+};
+
+struct ldb_request;
+struct ldb_handle;
+
+struct ldb_search {
+ struct ldb_dn *base;
+ enum ldb_scope scope;
+ struct ldb_parse_tree *tree;
+ const char * const *attrs;
+ struct ldb_result *res;
+};
+
+struct ldb_add {
+ const struct ldb_message *message;
+};
+
+struct ldb_modify {
+ const struct ldb_message *message;
+};
+
+struct ldb_delete {
+ struct ldb_dn *dn;
+};
+
+struct ldb_rename {
+ struct ldb_dn *olddn;
+ struct ldb_dn *newdn;
+};
+
+struct ldb_register_control {
+ const char *oid;
+};
+
+struct ldb_register_partition {
+ struct ldb_dn *dn;
+};
+
+typedef int (*ldb_request_callback_t)(struct ldb_request *, struct ldb_reply *);
+
+struct ldb_request {
+
+ enum ldb_request_type operation;
+
+ union {
+ struct ldb_search search;
+ struct ldb_add add;
+ struct ldb_modify mod;
+ struct ldb_delete del;
+ struct ldb_rename rename;
+ struct ldb_extended extended;
+ struct ldb_register_control reg_control;
+ struct ldb_register_partition reg_partition;
+ } op;
+
+ struct ldb_control **controls;
+
+ void *context;
+ ldb_request_callback_t callback;
+
+ int timeout;
+ time_t starttime;
+ struct ldb_handle *handle;
+};
+
+int ldb_request(struct ldb_context *ldb, struct ldb_request *request);
+int ldb_request_done(struct ldb_request *req, int status);
+bool ldb_request_is_done(struct ldb_request *req);
+
+int ldb_modules_wait(struct ldb_handle *handle);
+int ldb_wait(struct ldb_handle *handle, enum ldb_wait_type type);
+
+int ldb_set_timeout(struct ldb_context *ldb, struct ldb_request *req, int timeout);
+int ldb_set_timeout_from_prev_req(struct ldb_context *ldb, struct ldb_request *oldreq, struct ldb_request *newreq);
+void ldb_set_create_perms(struct ldb_context *ldb, unsigned int perms);
+void ldb_set_modules_dir(struct ldb_context *ldb, const char *path);
+struct tevent_context;
+void ldb_set_event_context(struct ldb_context *ldb, struct tevent_context *ev);
+struct tevent_context * ldb_get_event_context(struct ldb_context *ldb);
+
+/**
+ Initialise ldbs' global information
+
+ This is required before any other LDB call
+
+ \return 0 if initialisation succeeded, -1 otherwise
+*/
+int ldb_global_init(void);
+
+/**
+ Initialise an ldb context
+
+ This is required before any other LDB call.
+
+ \param mem_ctx pointer to a talloc memory context. Pass NULL if there is
+ no suitable context available.
+
+ \return pointer to ldb_context that should be free'd (using talloc_free())
+ at the end of the program.
+*/
+struct ldb_context *ldb_init(TALLOC_CTX *mem_ctx, struct tevent_context *ev_ctx);
+
+/**
+ Connect to a database.
+
+ This is typically called soon after ldb_init(), and is required prior to
+ any search or database modification operations.
+
+ The URL can be one of the following forms:
+ - tdb://path
+ - ldapi://path
+ - ldap://host
+ - sqlite://path
+
+ \param ldb the context associated with the database (from ldb_init())
+ \param url the URL of the database to connect to, as noted above
+ \param flags a combination of LDB_FLG_* to modify the connection behaviour
+ \param options backend specific options - passed uninterpreted to the backend
+
+ \return result code (LDB_SUCCESS on success, or a failure code)
+
+ \note It is an error to connect to a database that does not exist in readonly mode
+ (that is, with LDB_FLG_RDONLY). However in read-write mode, the database will be
+ created if it does not exist.
+*/
+
+typedef void (*ldb_async_timeout_fn) (void *);
+typedef bool (*ldb_async_callback_fn) (void *);
+typedef int (*ldb_async_ctx_add_op_fn)(void *, time_t, void *, ldb_async_timeout_fn, ldb_async_callback_fn);
+typedef int (*ldb_async_ctx_wait_op_fn)(void *);
+
+void ldb_async_ctx_set_private_data(struct ldb_context *ldb,
+ void *private_data);
+void ldb_async_ctx_set_add_op(struct ldb_context *ldb,
+ ldb_async_ctx_add_op_fn add_op);
+void ldb_async_ctx_set_wait_op(struct ldb_context *ldb,
+ ldb_async_ctx_wait_op_fn wait_op);
+
+int ldb_connect(struct ldb_context *ldb, const char *url, unsigned int flags, const char *options[]);
+
+/*
+ return an automatic basedn from the rootDomainNamingContext of the rootDSE
+ This value have been set in an opaque pointer at connection time
+*/
+struct ldb_dn *ldb_get_root_basedn(struct ldb_context *ldb);
+
+/*
+ return an automatic basedn from the configurationNamingContext of the rootDSE
+ This value have been set in an opaque pointer at connection time
+*/
+struct ldb_dn *ldb_get_config_basedn(struct ldb_context *ldb);
+
+/*
+ return an automatic basedn from the schemaNamingContext of the rootDSE
+ This value have been set in an opaque pointer at connection time
+*/
+struct ldb_dn *ldb_get_schema_basedn(struct ldb_context *ldb);
+
+/*
+ return an automatic baseDN from the defaultNamingContext of the rootDSE
+ This value have been set in an opaque pointer at connection time
+*/
+struct ldb_dn *ldb_get_default_basedn(struct ldb_context *ldb);
+
+/**
+ The default async search callback function
+
+ \param req the request we are callback of
+ \param ares a single reply from the async core
+
+ \return result code (LDB_SUCCESS on success, or a failure code)
+
+ \note this function expects req->context to always be an struct ldb_result pointer
+ AND a talloc context, this function will steal on the context each message
+ from the ares reply passed on by the async core so that in the end all the
+ messages will be in the context (ldb_result) memory tree.
+ Freeing the passed context (ldb_result tree) will free all the resources
+ (the request need to be freed separately and the result doe not depend on the
+ request that can be freed as sson as the search request is finished)
+*/
+
+int ldb_search_default_callback(struct ldb_request *req, struct ldb_reply *ares);
+
+/**
+ The default async extended operation callback function
+
+ \param req the request we are callback of
+ \param ares a single reply from the async core
+
+ \return result code (LDB_SUCCESS on success, or a failure code)
+*/
+int ldb_op_default_callback(struct ldb_request *req, struct ldb_reply *ares);
+
+
+/**
+ Helper function to build a search request
+
+ \param ret_req the request structure is returned here (talloced on mem_ctx)
+ \param ldb the context associated with the database (from ldb_init())
+ \param mem_ctx a talloc memory context (used as parent of ret_req)
+ \param base the Base Distinguished Name for the query (use ldb_dn_new() for an empty one)
+ \param scope the search scope for the query
+ \param expression the search expression to use for this query
+ \param attrs the search attributes for the query (pass NULL if none required)
+ \param controls an array of controls
+ \param context the callback function context
+ \param the callback function to handle the async replies
+ \param the parent request if any
+
+ \return result code (LDB_SUCCESS on success, or a failure code)
+*/
+
+int ldb_build_search_req(struct ldb_request **ret_req,
+ struct ldb_context *ldb,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_dn *base,
+ enum ldb_scope scope,
+ const char *expression,
+ const char * const *attrs,
+ struct ldb_control **controls,
+ void *context,
+ ldb_request_callback_t callback,
+ struct ldb_request *parent);
+
+int ldb_build_search_req_ex(struct ldb_request **ret_req,
+ struct ldb_context *ldb,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_dn *base,
+ enum ldb_scope scope,
+ struct ldb_parse_tree *tree,
+ const char * const *attrs,
+ struct ldb_control **controls,
+ void *context,
+ ldb_request_callback_t callback,
+ struct ldb_request *parent);
+
+/**
+ Helper function to build an add request
+
+ \param ret_req the request structure is returned here (talloced on mem_ctx)
+ \param ldb the context associated with the database (from ldb_init())
+ \param mem_ctx a talloc memory context (used as parent of ret_req)
+ \param message contains the entry to be added
+ \param controls an array of controls
+ \param context the callback function context
+ \param the callback function to handle the async replies
+ \param the parent request if any
+
+ \return result code (LDB_SUCCESS on success, or a failure code)
+*/
+
+int ldb_build_add_req(struct ldb_request **ret_req,
+ struct ldb_context *ldb,
+ TALLOC_CTX *mem_ctx,
+ const struct ldb_message *message,
+ struct ldb_control **controls,
+ void *context,
+ ldb_request_callback_t callback,
+ struct ldb_request *parent);
+
+/**
+ Helper function to build a modify request
+
+ \param ret_req the request structure is returned here (talloced on mem_ctx)
+ \param ldb the context associated with the database (from ldb_init())
+ \param mem_ctx a talloc memory context (used as parent of ret_req)
+ \param message contains the entry to be modified
+ \param controls an array of controls
+ \param context the callback function context
+ \param the callback function to handle the async replies
+ \param the parent request if any
+
+ \return result code (LDB_SUCCESS on success, or a failure code)
+*/
+
+int ldb_build_mod_req(struct ldb_request **ret_req,
+ struct ldb_context *ldb,
+ TALLOC_CTX *mem_ctx,
+ const struct ldb_message *message,
+ struct ldb_control **controls,
+ void *context,
+ ldb_request_callback_t callback,
+ struct ldb_request *parent);
+
+/**
+ Helper function to build a delete request
+
+ \param ret_req the request structure is returned here (talloced on mem_ctx)
+ \param ldb the context associated with the database (from ldb_init())
+ \param mem_ctx a talloc memory context (used as parent of ret_req)
+ \param dn the DN to be deleted
+ \param controls an array of controls
+ \param context the callback function context
+ \param the callback function to handle the async replies
+ \param the parent request if any
+
+ \return result code (LDB_SUCCESS on success, or a failure code)
+*/
+
+int ldb_build_del_req(struct ldb_request **ret_req,
+ struct ldb_context *ldb,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_dn *dn,
+ struct ldb_control **controls,
+ void *context,
+ ldb_request_callback_t callback,
+ struct ldb_request *parent);
+
+/**
+ Helper function to build a rename request
+
+ \param ret_req the request structure is returned here (talloced on mem_ctx)
+ \param ldb the context associated with the database (from ldb_init())
+ \param mem_ctx a talloc memory context (used as parent of ret_req)
+ \param olddn the old DN
+ \param newdn the new DN
+ \param controls an array of controls
+ \param context the callback function context
+ \param the callback function to handle the async replies
+ \param the parent request if any
+
+ \return result code (LDB_SUCCESS on success, or a failure code)
+*/
+
+int ldb_build_rename_req(struct ldb_request **ret_req,
+ struct ldb_context *ldb,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_dn *olddn,
+ struct ldb_dn *newdn,
+ struct ldb_control **controls,
+ void *context,
+ ldb_request_callback_t callback,
+ struct ldb_request *parent);
+
+/**
+ Add a ldb_control to a ldb_request
+
+ \param req the request struct where to add the control
+ \param oid the object identifier of the control as string
+ \param critical whether the control should be critical or not
+ \param data a talloc pointer to the control specific data
+
+ \return result code (LDB_SUCCESS on success, or a failure code)
+*/
+int ldb_request_add_control(struct ldb_request *req, const char *oid, bool critical, void *data);
+
+/**
+ check if a control with the specified "oid" exist and return it
+ \param req the request struct where to add the control
+ \param oid the object identifier of the control as string
+
+ \return the control, NULL if not found
+*/
+struct ldb_control *ldb_request_get_control(struct ldb_request *req, const char *oid);
+
+/**
+ check if a control with the specified "oid" exist and return it
+ \param rep the reply struct where to add the control
+ \param oid the object identifier of the control as string
+
+ \return the control, NULL if not found
+*/
+struct ldb_control *ldb_reply_get_control(struct ldb_reply *rep, const char *oid);
+
+/**
+ Search the database
+
+ This function searches the database, and returns
+ records that match an LDAP-like search expression
+
+ \param ldb the context associated with the database (from ldb_init())
+ \param mem_ctx the memory context to use for the request and the results
+ \param result the return result
+ \param base the Base Distinguished Name for the query (use ldb_dn_new() for an empty one)
+ \param scope the search scope for the query
+ \param attrs the search attributes for the query (pass NULL if none required)
+ \param exp_fmt the search expression to use for this query (printf like)
+
+ \return result code (LDB_SUCCESS on success, or a failure code)
+
+ \note use talloc_free() to free the ldb_result returned
+*/
+int ldb_search(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
+ struct ldb_result **result, struct ldb_dn *base,
+ enum ldb_scope scope, const char * const *attrs,
+ const char *exp_fmt, ...) PRINTF_ATTRIBUTE(7,8);
+
+/**
+ Add a record to the database.
+
+ This function adds a record to the database. This function will fail
+ if a record with the specified class and key already exists in the
+ database.
+
+ \param ldb the context associated with the database (from
+ ldb_init())
+ \param message the message containing the record to add.
+
+ \return result code (LDB_SUCCESS if the record was added, otherwise
+ a failure code)
+*/
+int ldb_add(struct ldb_context *ldb,
+ const struct ldb_message *message);
+
+/**
+ Modify the specified attributes of a record
+
+ This function modifies a record that is in the database.
+
+ \param ldb the context associated with the database (from
+ ldb_init())
+ \param message the message containing the changes required.
+
+ \return result code (LDB_SUCCESS if the record was modified as
+ requested, otherwise a failure code)
+*/
+int ldb_modify(struct ldb_context *ldb,
+ const struct ldb_message *message);
+
+/**
+ Rename a record in the database
+
+ This function renames a record in the database.
+
+ \param ldb the context associated with the database (from
+ ldb_init())
+ \param olddn the DN for the record to be renamed.
+ \param newdn the new DN
+
+ \return result code (LDB_SUCCESS if the record was renamed as
+ requested, otherwise a failure code)
+*/
+int ldb_rename(struct ldb_context *ldb, struct ldb_dn *olddn, struct ldb_dn *newdn);
+
+/**
+ Delete a record from the database
+
+ This function deletes a record from the database.
+
+ \param ldb the context associated with the database (from
+ ldb_init())
+ \param dn the DN for the record to be deleted.
+
+ \return result code (LDB_SUCCESS if the record was deleted,
+ otherwise a failure code)
+*/
+int ldb_delete(struct ldb_context *ldb, struct ldb_dn *dn);
+
+/**
+ The default async extended operation callback function
+
+ \param req the request we are callback of
+ \param ares a single reply from the async core
+
+ \return result code (LDB_SUCCESS on success, or a failure code)
+
+ \note this function expects req->context to always be an struct ldb_result pointer
+ AND a talloc context, this function will steal on the context each message
+ from the ares reply passed on by the async core so that in the end all the
+ messages will be in the context (ldb_result) memory tree.
+ Freeing the passed context (ldb_result tree) will free all the resources
+ (the request need to be freed separately and the result doe not depend on the
+ request that can be freed as sson as the search request is finished)
+*/
+
+int ldb_extended_default_callback(struct ldb_request *req, struct ldb_reply *ares);
+
+
+/**
+ Helper function to build a extended request
+
+ \param ret_req the request structure is returned here (talloced on mem_ctx)
+ \param ldb the context associated with the database (from ldb_init())
+ \param mem_ctx a talloc memory context (used as parent of ret_req)
+ \param oid the OID of the extended operation.
+ \param data a void pointer a the extended operation specific parameters,
+ it needs to be NULL or a valid talloc pointer! talloc_get_type() will be used on it
+ \param controls an array of controls
+ \param context the callback function context
+ \param the callback function to handle the async replies
+ \param the parent request if any
+
+ \return result code (LDB_SUCCESS on success, or a failure code)
+*/
+int ldb_build_extended_req(struct ldb_request **ret_req,
+ struct ldb_context *ldb,
+ TALLOC_CTX *mem_ctx,
+ const char *oid,
+ void *data,/* NULL or a valid talloc pointer! talloc_get_type() will be used on it */
+ struct ldb_control **controls,
+ void *context,
+ ldb_request_callback_t callback,
+ struct ldb_request *parent);
+
+/**
+ call an extended operation
+
+ This function deletes a record from the database.
+
+ \param ldb the context associated with the database (from ldb_init())
+ \param oid the OID of the extended operation.
+ \param data a void pointer a the extended operation specific parameters,
+ it needs to be NULL or a valid talloc pointer! talloc_get_type() will be used on it
+ \param res the result of the extended operation
+
+ \return result code (LDB_SUCCESS if the extended operation returned fine,
+ otherwise a failure code)
+*/
+int ldb_extended(struct ldb_context *ldb,
+ const char *oid,
+ void *data,/* NULL or a valid talloc pointer! talloc_get_type() will be used on it */
+ struct ldb_result **res);
+
+/**
+ Obtain current/next database sequence number
+*/
+int ldb_sequence_number(struct ldb_context *ldb, enum ldb_sequence_type type, uint64_t *seq_num);
+
+/**
+ start a transaction
+*/
+int ldb_transaction_start(struct ldb_context *ldb);
+
+/**
+ commit a transaction
+*/
+int ldb_transaction_commit(struct ldb_context *ldb);
+
+/**
+ cancel a transaction
+*/
+int ldb_transaction_cancel(struct ldb_context *ldb);
+
+
+/**
+ return extended error information from the last call
+*/
+const char *ldb_errstring(struct ldb_context *ldb);
+
+/**
+ return a string explaining what a ldb error constant meancs
+*/
+const char *ldb_strerror(int ldb_err);
+
+/**
+ setup the default utf8 functions
+ FIXME: these functions do not yet handle utf8
+*/
+void ldb_set_utf8_default(struct ldb_context *ldb);
+
+/**
+ Casefold a string
+
+ \param ldb the ldb context
+ \param mem_ctx the memory context to allocate the result string
+ memory from.
+ \param s the string that is to be folded
+ \return a copy of the string, converted to upper case
+
+ \note The default function is not yet UTF8 aware. Provide your own
+ set of functions through ldb_set_utf8_fns()
+*/
+char *ldb_casefold(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, const char *s, size_t n);
+
+/**
+ Check the attribute name is valid according to rfc2251
+ \param s the string to check
+
+ \return 1 if the name is ok
+*/
+int ldb_valid_attr_name(const char *s);
+
+/*
+ ldif manipulation functions
+*/
+
+/**
+ Write an LDIF message
+
+ This function writes an LDIF message using a caller supplied write
+ function.
+
+ \param ldb the ldb context (from ldb_init())
+ \param fprintf_fn a function pointer for the write function. This must take
+ a private data pointer, followed by a format string, and then a variable argument
+ list.
+ \param private_data pointer that will be provided back to the write
+ function. This is useful for maintaining state or context.
+ \param ldif the message to write out
+
+ \return the total number of bytes written, or an error code as returned
+ from the write function.
+
+ \sa ldb_ldif_write_file for a more convenient way to write to a
+ file stream.
+
+ \sa ldb_ldif_read for the reader equivalent to this function.
+*/
+int ldb_ldif_write(struct ldb_context *ldb,
+ int (*fprintf_fn)(void *, const char *, ...) PRINTF_ATTRIBUTE(2,3),
+ void *private_data,
+ const struct ldb_ldif *ldif);
+
+/**
+ Clean up an LDIF message
+
+ This function cleans up a LDIF message read using ldb_ldif_read()
+ or related functions (such as ldb_ldif_read_string() and
+ ldb_ldif_read_file().
+
+ \param ldb the ldb context (from ldb_init())
+ \param msg the message to clean up and free
+
+*/
+void ldb_ldif_read_free(struct ldb_context *ldb, struct ldb_ldif *msg);
+
+/**
+ Read an LDIF message
+
+ This function creates an LDIF message using a caller supplied read
+ function.
+
+ \param ldb the ldb context (from ldb_init())
+ \param fgetc_fn a function pointer for the read function. This must
+ take a private data pointer, and must return a pointer to an
+ integer corresponding to the next byte read (or EOF if there is no
+ more data to be read).
+ \param private_data pointer that will be provided back to the read
+ function. This is udeful for maintaining state or context.
+
+ \return the LDIF message that has been read in
+
+ \note You must free the LDIF message when no longer required, using
+ ldb_ldif_read_free().
+
+ \sa ldb_ldif_read_file for a more convenient way to read from a
+ file stream.
+
+ \sa ldb_ldif_read_string for a more convenient way to read from a
+ string (char array).
+
+ \sa ldb_ldif_write for the writer equivalent to this function.
+*/
+struct ldb_ldif *ldb_ldif_read(struct ldb_context *ldb,
+ int (*fgetc_fn)(void *), void *private_data);
+
+/**
+ Read an LDIF message from a file
+
+ This function reads the next LDIF message from the contents of a
+ file stream. If you want to get all of the LDIF messages, you will
+ need to repeatedly call this function, until it returns NULL.
+
+ \param ldb the ldb context (from ldb_init())
+ \param f the file stream to read from (typically from fdopen())
+
+ \sa ldb_ldif_read_string for an equivalent function that will read
+ from a string (char array).
+
+ \sa ldb_ldif_write_file for the writer equivalent to this function.
+
+*/
+struct ldb_ldif *ldb_ldif_read_file(struct ldb_context *ldb, FILE *f);
+
+/**
+ Read an LDIF message from a string
+
+ This function reads the next LDIF message from the contents of a char
+ array. If you want to get all of the LDIF messages, you will need
+ to repeatedly call this function, until it returns NULL.
+
+ \param ldb the ldb context (from ldb_init())
+ \param s pointer to the char array to read from
+
+ \sa ldb_ldif_read_file for an equivalent function that will read
+ from a file stream.
+
+ \sa ldb_ldif_write for a more general (arbitrary read function)
+ version of this function.
+*/
+struct ldb_ldif *ldb_ldif_read_string(struct ldb_context *ldb, const char **s);
+
+/**
+ Write an LDIF message to a file
+
+ \param ldb the ldb context (from ldb_init())
+ \param f the file stream to write to (typically from fdopen())
+ \param msg the message to write out
+
+ \return the total number of bytes written, or a negative error code
+
+ \sa ldb_ldif_read_file for the reader equivalent to this function.
+*/
+int ldb_ldif_write_file(struct ldb_context *ldb, FILE *f, const struct ldb_ldif *msg);
+
+/**
+ Base64 encode a buffer
+
+ \param mem_ctx the memory context that the result is allocated
+ from.
+ \param buf pointer to the array that is to be encoded
+ \param len the number of elements in the array to be encoded
+
+ \return pointer to an array containing the encoded data
+
+ \note The caller is responsible for freeing the result
+*/
+char *ldb_base64_encode(TALLOC_CTX *mem_ctx, const char *buf, int len);
+
+/**
+ Base64 decode a buffer
+
+ This function decodes a base64 encoded string in place.
+
+ \param s the string to decode.
+
+ \return the length of the returned (decoded) string.
+
+ \note the string is null terminated, but the null terminator is not
+ included in the length.
+*/
+int ldb_base64_decode(char *s);
+
+/* The following definitions come from lib/ldb/common/ldb_dn.c */
+
+/**
+ Get the linear form of a DN (without any extended components)
+
+ \param dn The DN to linearize
+*/
+
+const char *ldb_dn_get_linearized(struct ldb_dn *dn);
+
+/**
+ Allocate a copy of the linear form of a DN (without any extended components) onto the supplied memory context
+
+ \param dn The DN to linearize
+ \param mem_ctx TALLOC context to return result on
+*/
+
+char *ldb_dn_alloc_linearized(TALLOC_CTX *mem_ctx, struct ldb_dn *dn);
+
+/**
+ Get the linear form of a DN (with any extended components)
+
+ \param mem_ctx TALLOC context to return result on
+ \param dn The DN to linearize
+ \param mode Style of extended DN to return (0 is HEX representation of binary form, 1 is a string form)
+*/
+char *ldb_dn_get_extended_linearized(void *mem_ctx, struct ldb_dn *dn, int mode);
+const struct ldb_val *ldb_dn_get_extended_component(struct ldb_dn *dn, const char *name);
+int ldb_dn_set_extended_component(struct ldb_dn *dn, const char *name, const struct ldb_val *val);
+
+void ldb_dn_remove_extended_components(struct ldb_dn *dn);
+bool ldb_dn_has_extended(struct ldb_dn *dn);
+
+int ldb_dn_extended_add_syntax(struct ldb_context *ldb,
+ unsigned flags,
+ const struct ldb_dn_extended_syntax *syntax);
+
+/**
+ Allocate a new DN from a string
+
+ \param mem_ctx TALLOC context to return resulting ldb_dn structure on
+ \param dn The new DN
+
+ \note The DN will not be parsed at this time. Use ldb_dn_validate to tell if the DN is syntacticly correct
+*/
+
+struct ldb_dn *ldb_dn_new(TALLOC_CTX *mem_ctx, struct ldb_context *ldb, const char *dn);
+/**
+ Allocate a new DN from a printf style format string and arguments
+
+ \param mem_ctx TALLOC context to return resulting ldb_dn structure on
+ \param new_fms The new DN as a format string (plus arguments)
+
+ \note The DN will not be parsed at this time. Use ldb_dn_validate to tell if the DN is syntacticly correct
+*/
+
+struct ldb_dn *ldb_dn_new_fmt(TALLOC_CTX *mem_ctx, struct ldb_context *ldb, const char *new_fmt, ...) PRINTF_ATTRIBUTE(3,4);
+/**
+ Allocate a new DN from a struct ldb_val (useful to avoid buffer overrun)
+
+ \param mem_ctx TALLOC context to return resulting ldb_dn structure on
+ \param dn The new DN
+
+ \note The DN will not be parsed at this time. Use ldb_dn_validate to tell if the DN is syntacticly correct
+*/
+
+struct ldb_dn *ldb_dn_from_ldb_val(void *mem_ctx, struct ldb_context *ldb, const struct ldb_val *strdn);
+
+/**
+ Determine if this DN is syntactically valid
+
+ \param dn The DN to validate
+*/
+
+bool ldb_dn_validate(struct ldb_dn *dn);
+
+char *ldb_dn_escape_value(TALLOC_CTX *mem_ctx, struct ldb_val value);
+const char *ldb_dn_get_casefold(struct ldb_dn *dn);
+char *ldb_dn_alloc_casefold(TALLOC_CTX *mem_ctx, struct ldb_dn *dn);
+
+int ldb_dn_compare_base(struct ldb_dn *base, struct ldb_dn *dn);
+int ldb_dn_compare(struct ldb_dn *edn0, struct ldb_dn *edn1);
+
+bool ldb_dn_add_base(struct ldb_dn *dn, struct ldb_dn *base);
+bool ldb_dn_add_base_fmt(struct ldb_dn *dn, const char *base_fmt, ...) PRINTF_ATTRIBUTE(2,3);
+bool ldb_dn_add_child(struct ldb_dn *dn, struct ldb_dn *child);
+bool ldb_dn_add_child_fmt(struct ldb_dn *dn, const char *child_fmt, ...) PRINTF_ATTRIBUTE(2,3);
+bool ldb_dn_remove_base_components(struct ldb_dn *dn, unsigned int num);
+bool ldb_dn_remove_child_components(struct ldb_dn *dn, unsigned int num);
+
+struct ldb_dn *ldb_dn_copy(TALLOC_CTX *mem_ctx, struct ldb_dn *dn);
+struct ldb_dn *ldb_dn_get_parent(TALLOC_CTX *mem_ctx, struct ldb_dn *dn);
+char *ldb_dn_canonical_string(TALLOC_CTX *mem_ctx, struct ldb_dn *dn);
+char *ldb_dn_canonical_ex_string(TALLOC_CTX *mem_ctx, struct ldb_dn *dn);
+int ldb_dn_get_comp_num(struct ldb_dn *dn);
+const char *ldb_dn_get_component_name(struct ldb_dn *dn, unsigned int num);
+const struct ldb_val *ldb_dn_get_component_val(struct ldb_dn *dn, unsigned int num);
+const char *ldb_dn_get_rdn_name(struct ldb_dn *dn);
+const struct ldb_val *ldb_dn_get_rdn_val(struct ldb_dn *dn);
+int ldb_dn_set_component(struct ldb_dn *dn, int num, const char *name, const struct ldb_val val);
+
+bool ldb_dn_is_valid(struct ldb_dn *dn);
+bool ldb_dn_is_special(struct ldb_dn *dn);
+bool ldb_dn_check_special(struct ldb_dn *dn, const char *check);
+bool ldb_dn_is_null(struct ldb_dn *dn);
+
+
+/**
+ Compare two attributes
+
+ This function compares to attribute names. Note that this is a
+ case-insensitive comparison.
+
+ \param a the first attribute name to compare
+ \param b the second attribute name to compare
+
+ \return 0 if the attribute names are the same, or only differ in
+ case; non-zero if there are any differences
+
+ attribute names are restricted by rfc2251 so using
+ strcasecmp and toupper here is ok.
+ return 0 for match
+*/
+#define ldb_attr_cmp(a, b) strcasecmp(a, b)
+char *ldb_attr_casefold(TALLOC_CTX *mem_ctx, const char *s);
+int ldb_attr_dn(const char *attr);
+
+/**
+ Create an empty message
+
+ \param mem_ctx the memory context to create in. You can pass NULL
+ to get the top level context, however the ldb context (from
+ ldb_init()) may be a better choice
+*/
+struct ldb_message *ldb_msg_new(TALLOC_CTX *mem_ctx);
+
+/**
+ Find an element within an message
+*/
+struct ldb_message_element *ldb_msg_find_element(const struct ldb_message *msg,
+ const char *attr_name);
+
+/**
+ Compare two ldb_val values
+
+ \param v1 first ldb_val structure to be tested
+ \param v2 second ldb_val structure to be tested
+
+ \return 1 for a match, 0 if there is any difference
+*/
+int ldb_val_equal_exact(const struct ldb_val *v1, const struct ldb_val *v2);
+
+/**
+ find a value within an ldb_message_element
+
+ \param el the element to search
+ \param val the value to search for
+
+ \note This search is case sensitive
+*/
+struct ldb_val *ldb_msg_find_val(const struct ldb_message_element *el,
+ struct ldb_val *val);
+
+/**
+ add a new empty element to a ldb_message
+*/
+int ldb_msg_add_empty(struct ldb_message *msg,
+ const char *attr_name,
+ int flags,
+ struct ldb_message_element **return_el);
+
+/**
+ add a element to a ldb_message
+*/
+int ldb_msg_add(struct ldb_message *msg,
+ const struct ldb_message_element *el,
+ int flags);
+int ldb_msg_add_value(struct ldb_message *msg,
+ const char *attr_name,
+ const struct ldb_val *val,
+ struct ldb_message_element **return_el);
+int ldb_msg_add_steal_value(struct ldb_message *msg,
+ const char *attr_name,
+ struct ldb_val *val);
+int ldb_msg_add_steal_string(struct ldb_message *msg,
+ const char *attr_name, char *str);
+int ldb_msg_add_string(struct ldb_message *msg,
+ const char *attr_name, const char *str);
+int ldb_msg_add_fmt(struct ldb_message *msg,
+ const char *attr_name, const char *fmt, ...) PRINTF_ATTRIBUTE(3,4);
+
+/**
+ compare two message elements - return 0 on match
+*/
+int ldb_msg_element_compare(struct ldb_message_element *el1,
+ struct ldb_message_element *el2);
+int ldb_msg_element_compare_name(struct ldb_message_element *el1,
+ struct ldb_message_element *el2);
+
+/**
+ Find elements in a message.
+
+ This function finds elements and converts to a specific type, with
+ a give default value if not found. Assumes that elements are
+ single valued.
+*/
+const struct ldb_val *ldb_msg_find_ldb_val(const struct ldb_message *msg, const char *attr_name);
+int ldb_msg_find_attr_as_int(const struct ldb_message *msg,
+ const char *attr_name,
+ int default_value);
+unsigned int ldb_msg_find_attr_as_uint(const struct ldb_message *msg,
+ const char *attr_name,
+ unsigned int default_value);
+int64_t ldb_msg_find_attr_as_int64(const struct ldb_message *msg,
+ const char *attr_name,
+ int64_t default_value);
+uint64_t ldb_msg_find_attr_as_uint64(const struct ldb_message *msg,
+ const char *attr_name,
+ uint64_t default_value);
+double ldb_msg_find_attr_as_double(const struct ldb_message *msg,
+ const char *attr_name,
+ double default_value);
+int ldb_msg_find_attr_as_bool(const struct ldb_message *msg,
+ const char *attr_name,
+ int default_value);
+const char *ldb_msg_find_attr_as_string(const struct ldb_message *msg,
+ const char *attr_name,
+ const char *default_value);
+
+struct ldb_dn *ldb_msg_find_attr_as_dn(struct ldb_context *ldb,
+ TALLOC_CTX *mem_ctx,
+ const struct ldb_message *msg,
+ const char *attr_name);
+
+void ldb_msg_sort_elements(struct ldb_message *msg);
+
+struct ldb_message *ldb_msg_copy_shallow(TALLOC_CTX *mem_ctx,
+ const struct ldb_message *msg);
+struct ldb_message *ldb_msg_copy(TALLOC_CTX *mem_ctx,
+ const struct ldb_message *msg);
+
+struct ldb_message *ldb_msg_canonicalize(struct ldb_context *ldb,
+ const struct ldb_message *msg);
+
+
+struct ldb_message *ldb_msg_diff(struct ldb_context *ldb,
+ struct ldb_message *msg1,
+ struct ldb_message *msg2);
+
+int ldb_msg_check_string_attribute(const struct ldb_message *msg,
+ const char *name,
+ const char *value);
+
+/**
+ Integrity check an ldb_message
+
+ This function performs basic sanity / integrity checks on an
+ ldb_message.
+
+ \param ldb context in which to perform the checks
+ \param msg the message to check
+
+ \return LDB_SUCCESS if the message is OK, or a non-zero error code
+ (one of LDB_ERR_INVALID_DN_SYNTAX, LDB_ERR_ENTRY_ALREADY_EXISTS or
+ LDB_ERR_INVALID_ATTRIBUTE_SYNTAX) if there is a problem with a
+ message.
+*/
+int ldb_msg_sanity_check(struct ldb_context *ldb,
+ const struct ldb_message *msg);
+
+/**
+ Duplicate an ldb_val structure
+
+ This function copies an ldb value structure.
+
+ \param mem_ctx the memory context that the duplicated value will be
+ allocated from
+ \param v the ldb_val to be duplicated.
+
+ \return the duplicated ldb_val structure.
+*/
+struct ldb_val ldb_val_dup(TALLOC_CTX *mem_ctx, const struct ldb_val *v);
+
+/**
+ this allows the user to set a debug function for error reporting
+*/
+int ldb_set_debug(struct ldb_context *ldb,
+ void (*debug)(void *context, enum ldb_debug_level level,
+ const char *fmt, va_list ap) PRINTF_ATTRIBUTE(3,0),
+ void *context);
+
+/**
+ this allows the user to set custom utf8 function for error reporting
+*/
+void ldb_set_utf8_fns(struct ldb_context *ldb,
+ void *context,
+ char *(*casefold)(void *, void *, const char *, size_t n));
+
+/**
+ this sets up debug to print messages on stderr
+*/
+int ldb_set_debug_stderr(struct ldb_context *ldb);
+
+/* control backend specific opaque values */
+int ldb_set_opaque(struct ldb_context *ldb, const char *name, void *value);
+void *ldb_get_opaque(struct ldb_context *ldb, const char *name);
+
+const char **ldb_attr_list_copy(TALLOC_CTX *mem_ctx, const char * const *attrs);
+const char **ldb_attr_list_copy_add(TALLOC_CTX *mem_ctx, const char * const *attrs, const char *new_attr);
+int ldb_attr_in_list(const char * const *attrs, const char *attr);
+
+int ldb_msg_rename_attr(struct ldb_message *msg, const char *attr, const char *replace);
+int ldb_msg_copy_attr(struct ldb_message *msg, const char *attr, const char *replace);
+void ldb_msg_remove_attr(struct ldb_message *msg, const char *attr);
+void ldb_msg_remove_element(struct ldb_message *msg, struct ldb_message_element *el);
+
+
+void ldb_parse_tree_attr_replace(struct ldb_parse_tree *tree,
+ const char *attr,
+ const char *replace);
+
+
+/**
+ Convert a time structure to a string
+
+ This function converts a time_t structure to an LDAP formatted
+ GeneralizedTime string.
+
+ \param mem_ctx the memory context to allocate the return string in
+ \param t the time structure to convert
+
+ \return the formatted string, or NULL if the time structure could
+ not be converted
+*/
+char *ldb_timestring(TALLOC_CTX *mem_ctx, time_t t);
+
+/**
+ Convert a string to a time structure
+
+ This function converts an LDAP formatted GeneralizedTime string
+ to a time_t structure.
+
+ \param s the string to convert
+
+ \return the time structure, or 0 if the string cannot be converted
+*/
+time_t ldb_string_to_time(const char *s);
+
+/**
+ Convert a time structure to a string
+
+ This function converts a time_t structure to an LDAP formatted
+ UTCTime string.
+
+ \param mem_ctx the memory context to allocate the return string in
+ \param t the time structure to convert
+
+ \return the formatted string, or NULL if the time structure could
+ not be converted
+*/
+char *ldb_timestring_utc(TALLOC_CTX *mem_ctx, time_t t);
+
+/**
+ Convert a string to a time structure
+
+ This function converts an LDAP formatted UTCTime string
+ to a time_t structure.
+
+ \param s the string to convert
+
+ \return the time structure, or 0 if the string cannot be converted
+*/
+time_t ldb_string_utc_to_time(const char *s);
+
+
+void ldb_qsort (void *const pbase, size_t total_elems, size_t size, void *opaque, ldb_qsort_cmp_fn_t cmp);
+
+
+/**
+ Convert an array of string represention of a control into an array of ldb_control structures
+
+ \param ldb LDB context
+ \param mem_ctx TALLOC context to return result on, and to allocate error_string on
+ \param control_strings Array of string-formatted controls
+
+ \return array of ldb_control elements
+*/
+struct ldb_control **ldb_parse_control_strings(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, const char **control_strings);
+
+#endif
diff --git a/source4/lib/ldb/include/ldb_errors.h b/source4/lib/ldb/include/ldb_errors.h
new file mode 100644
index 0000000000..9362233fd5
--- /dev/null
+++ b/source4/lib/ldb/include/ldb_errors.h
@@ -0,0 +1,310 @@
+/*
+ ldb database library
+
+ Copyright (C) Simo Sorce 2005
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb header
+ *
+ * Description: defines error codes following RFC 2251 ldap error codes
+ *
+ * Author: Simo Sorce
+ */
+
+#ifndef _LDB_ERRORS_H_
+
+/*! \cond DOXYGEN_IGNORE */
+#define _LDB_ERRORS_H_ 1
+/*! \endcond */
+
+/**
+ \file ldb_errors.h
+
+ This header provides a set of result codes for LDB function calls.
+
+ Many LDB function calls return an integer value (int). As shown in
+ the function documentation, those return values may indicate
+ whether the function call worked correctly (in which case it
+ returns LDB_SUCCESS) or some problem occurred (in which case some
+ other value will be returned). As a special case,
+ LDB_ERR_COMPARE_FALSE or LDB_ERR_COMPARE_TRUE may be returned,
+ which does not indicate an error.
+
+ \note Not all error codes make sense for LDB, however they are
+ based on the LDAP error codes, and are kept for reference and to
+ avoid overlap.
+
+ \note Some of this documentation is based on information in
+ the OpenLDAP documentation, as developed and maintained by the
+ <a href="http://www.openldap.org/">The OpenLDAP Project</a>.
+ */
+
+/**
+ The function call succeeded.
+
+ If a function returns LDB_SUCCESS, then that function, and the
+ underlying transactions that may have been required, completed
+ successfully.
+*/
+#define LDB_SUCCESS 0
+
+/**
+ The function call failed for some non-specific reason.
+*/
+#define LDB_ERR_OPERATIONS_ERROR 1
+
+/**
+ The function call failed because of a protocol violation.
+*/
+#define LDB_ERR_PROTOCOL_ERROR 2
+
+/**
+ The function call failed because a time limit was exceeded.
+*/
+#define LDB_ERR_TIME_LIMIT_EXCEEDED 3
+
+/**
+ The function call failed because a size limit was exceeded.
+*/
+#define LDB_ERR_SIZE_LIMIT_EXCEEDED 4
+
+/**
+ The function was for value comparison, and the comparison operation
+ returned false.
+
+ \note This is a status value, and doesn't normally indicate an
+ error.
+*/
+#define LDB_ERR_COMPARE_FALSE 5
+
+/**
+ The function was for value comparison, and the comparison operation
+ returned true.
+
+ \note This is a status value, and doesn't normally indicate an
+ error.
+*/
+#define LDB_ERR_COMPARE_TRUE 6
+
+/**
+ The function used an authentication method that is not supported by
+ the database.
+*/
+#define LDB_ERR_AUTH_METHOD_NOT_SUPPORTED 7
+
+/**
+ The function call required a underlying operation that required
+ strong authentication.
+
+ This will normally only occur if you are using LDB with a LDAP
+ backend.
+*/
+#define LDB_ERR_STRONG_AUTH_REQUIRED 8
+/* 9 RESERVED */
+
+/**
+ The function resulted in a referral to another server.
+*/
+#define LDB_ERR_REFERRAL 10
+
+/**
+ The function failed because an administrative / policy limit was
+ exceeded.
+*/
+#define LDB_ERR_ADMIN_LIMIT_EXCEEDED 11
+
+/**
+ The function required an extension or capability that the
+ database cannot provide.
+*/
+#define LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION 12
+
+/**
+ The function involved a transaction or database operation that
+ could not be performed without a secure link.
+*/
+#define LDB_ERR_CONFIDENTIALITY_REQUIRED 13
+
+/**
+ This is an intermediate result code for SASL bind operations that
+ have more than one step.
+
+ \note This is a result code that does not normally indicate an
+ error has occurred.
+*/
+#define LDB_ERR_SASL_BIND_IN_PROGRESS 14
+
+/**
+ The function referred to an attribute type that is not present in
+ the entry.
+*/
+#define LDB_ERR_NO_SUCH_ATTRIBUTE 16
+
+/**
+ The function referred to an attribute type that is invalid
+*/
+#define LDB_ERR_UNDEFINED_ATTRIBUTE_TYPE 17
+
+/**
+ The function required a filter type that is not available for the
+ specified attribute.
+*/
+#define LDB_ERR_INAPPROPRIATE_MATCHING 18
+
+/**
+ The function would have violated an attribute constraint.
+*/
+#define LDB_ERR_CONSTRAINT_VIOLATION 19
+
+/**
+ The function involved an attribute type or attribute value that
+ already exists in the entry.
+*/
+#define LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS 20
+/**
+ The function used an invalid (incorrect syntax) attribute value.
+*/
+#define LDB_ERR_INVALID_ATTRIBUTE_SYNTAX 21
+
+/* 22-31 unused */
+
+/**
+ The function referred to an object that does not exist in the
+ database.
+*/
+#define LDB_ERR_NO_SUCH_OBJECT 32
+
+/**
+ The function referred to an alias which points to a non-existant
+ object in the database.
+*/
+#define LDB_ERR_ALIAS_PROBLEM 33
+
+/**
+ The function used a DN which was invalid (incorrect syntax).
+*/
+#define LDB_ERR_INVALID_DN_SYNTAX 34
+
+/* 35 RESERVED */
+
+/**
+ The function required dereferencing of an alias, and something went
+ wrong during the dereferencing process.
+*/
+#define LDB_ERR_ALIAS_DEREFERENCING_PROBLEM 36
+
+/* 37-47 unused */
+
+/**
+ The function passed in the wrong authentication method.
+*/
+#define LDB_ERR_INAPPROPRIATE_AUTHENTICATION 48
+
+/**
+ The function passed in or referenced incorrect credentials during
+ authentication.
+*/
+#define LDB_ERR_INVALID_CREDENTIALS 49
+
+/**
+ The function required access permissions that the user does not
+ possess.
+*/
+#define LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS 50
+
+/**
+ The function required a transaction or call that the database could
+ not perform because it is busy.
+*/
+#define LDB_ERR_BUSY 51
+
+/**
+ The function required a transaction or call to a database that is
+ not available.
+*/
+#define LDB_ERR_UNAVAILABLE 52
+
+/**
+ The function required a transaction or call to a database that the
+ database declined to perform.
+*/
+#define LDB_ERR_UNWILLING_TO_PERFORM 53
+
+/**
+ The function failed because it resulted in a loop being detected.
+*/
+#define LDB_ERR_LOOP_DETECT 54
+
+/* 55-63 unused */
+
+/**
+ The function failed because it would have violated a naming rule.
+*/
+#define LDB_ERR_NAMING_VIOLATION 64
+
+/**
+ The function failed because it would have violated the schema.
+*/
+#define LDB_ERR_OBJECT_CLASS_VIOLATION 65
+
+/**
+ The function required an operation that is only allowed on leaf
+ objects, but the object is not a leaf.
+*/
+#define LDB_ERR_NOT_ALLOWED_ON_NON_LEAF 66
+
+/**
+ The function required an operation that cannot be performed on a
+ Relative DN, but the object is a Relative DN.
+*/
+#define LDB_ERR_NOT_ALLOWED_ON_RDN 67
+
+/**
+ The function failed because the entry already exists.
+*/
+#define LDB_ERR_ENTRY_ALREADY_EXISTS 68
+
+/**
+ The function failed because modifications to an object class are
+ not allowable.
+*/
+#define LDB_ERR_OBJECT_CLASS_MODS_PROHIBITED 69
+
+/* 70 RESERVED FOR CLDAP */
+
+/**
+ The function failed because it needed to be applied to multiple
+ databases.
+*/
+#define LDB_ERR_AFFECTS_MULTIPLE_DSAS 71
+
+/* 72-79 unused */
+
+/**
+ The function failed for unknown reasons.
+*/
+#define LDB_ERR_OTHER 80
+
+/* 81-90 RESERVED for APIs */
+
+#endif /* _LDB_ERRORS_H_ */
diff --git a/source4/lib/ldb/include/ldb_handlers.h b/source4/lib/ldb/include/ldb_handlers.h
new file mode 100644
index 0000000000..e1c14e679b
--- /dev/null
+++ b/source4/lib/ldb/include/ldb_handlers.h
@@ -0,0 +1,67 @@
+/*
+ ldb database library
+
+ Copyright (C) Simo Sorce 2005
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb header
+ *
+ * Description: defines attribute handlers
+ *
+ * Author: Simo Sorce
+ */
+
+
+int ldb_handler_copy( struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out);
+
+int ldb_handler_fold( struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out);
+
+int ldb_canonicalise_Integer( struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out);
+
+int ldb_comparison_Integer( struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *v1, const struct ldb_val *v2);
+
+int ldb_comparison_binary( struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *v1, const struct ldb_val *v2);
+
+int ldb_comparison_fold( struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *v1, const struct ldb_val *v2);
+
+int ldb_canonicalise_dn( struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out);
+
+int ldb_comparison_dn( struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *v1, const struct ldb_val *v2);
+
+int ldb_comparison_objectclass( struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *v1, const struct ldb_val *v2);
+
+int ldb_comparison_utctime( struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *v1, const struct ldb_val *v2);
+
+int ldb_canonicalise_utctime( struct ldb_context *ldb, void *mem_ctx,
+ const struct ldb_val *in, struct ldb_val *out);
+
diff --git a/source4/lib/ldb/include/ldb_includes.h b/source4/lib/ldb/include/ldb_includes.h
new file mode 100644
index 0000000000..602bbec32c
--- /dev/null
+++ b/source4/lib/ldb/include/ldb_includes.h
@@ -0,0 +1,18 @@
+#ifndef _LDB_PRIVATE_INCLUDES_H_
+#define _LDB_PRIVATE_INCLUDES_H_
+/*
+ a temporary includes file until I work on the ldb build system
+*/
+
+#if (_SAMBA_BUILD_ <= 3)
+/* allow forbidden string functions - should be replaced with _m functions */
+#undef strcasecmp
+#undef strncasecmp
+#define dyn_MODULESDIR dyn_LIBDIR
+#endif
+
+#include "replace.h"
+#include "system/filesys.h"
+#include "system/time.h"
+
+#endif /*_LDB_PRIVATE_INCLUDES_H_*/
diff --git a/source4/lib/ldb/include/ldb_module.h b/source4/lib/ldb/include/ldb_module.h
new file mode 100644
index 0000000000..4e1019184d
--- /dev/null
+++ b/source4/lib/ldb/include/ldb_module.h
@@ -0,0 +1,161 @@
+/*
+ ldb database library
+
+ Copyright (C) Simo Sorce 2008
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb module header
+ *
+ * Description: defines ldb modules structures and helpers
+ *
+ */
+
+#ifndef _LDB_MODULE_H_
+#define _LDB_MODULE_H_
+
+#include "ldb.h"
+
+struct ldb_context;
+struct ldb_module;
+
+/*
+ these function pointers define the operations that a ldb module can intercept
+*/
+struct ldb_module_ops {
+ const char *name;
+ int (*init_context) (struct ldb_module *);
+ int (*search)(struct ldb_module *, struct ldb_request *); /* search */
+ int (*add)(struct ldb_module *, struct ldb_request *); /* add */
+ int (*modify)(struct ldb_module *, struct ldb_request *); /* modify */
+ int (*del)(struct ldb_module *, struct ldb_request *); /* delete */
+ int (*rename)(struct ldb_module *, struct ldb_request *); /* rename */
+ int (*request)(struct ldb_module *, struct ldb_request *); /* match any other operation */
+ int (*extended)(struct ldb_module *, struct ldb_request *); /* extended operations */
+ int (*start_transaction)(struct ldb_module *);
+ int (*end_transaction)(struct ldb_module *);
+ int (*del_transaction)(struct ldb_module *);
+ int (*sequence_number)(struct ldb_module *, struct ldb_request *);
+ void *private_data;
+};
+
+
+/* The following definitions come from lib/ldb/common/ldb_debug.c */
+void ldb_debug(struct ldb_context *ldb, enum ldb_debug_level level, const char *fmt, ...) PRINTF_ATTRIBUTE(3, 4);
+void ldb_debug_set(struct ldb_context *ldb, enum ldb_debug_level level,
+ const char *fmt, ...) PRINTF_ATTRIBUTE(3, 4);
+
+#define ldb_oom(ldb) ldb_debug_set(ldb, LDB_DEBUG_FATAL, "ldb out of memory at %s:%d\n", __FILE__, __LINE__)
+
+/* The following definitions come from lib/ldb/common/ldb.c */
+
+void ldb_request_set_state(struct ldb_request *req, int state);
+int ldb_request_get_status(struct ldb_request *req);
+
+unsigned int ldb_get_create_perms(struct ldb_context *ldb);
+
+const struct ldb_schema_syntax *ldb_standard_syntax_by_name(struct ldb_context *ldb,
+ const char *syntax);
+
+/* The following definitions come from lib/ldb/common/ldb_attributes.c */
+
+int ldb_schema_attribute_add_with_syntax(struct ldb_context *ldb,
+ const char *name,
+ unsigned flags,
+ const struct ldb_schema_syntax *syntax);
+int ldb_schema_attribute_add(struct ldb_context *ldb,
+ const char *name,
+ unsigned flags,
+ const char *syntax);
+void ldb_schema_attribute_remove(struct ldb_context *ldb, const char *name);
+
+/* The following definitions come from lib/ldb/common/ldb_controls.c */
+struct ldb_control *get_control_from_list(struct ldb_control **controls, const char *oid);
+int save_controls(struct ldb_control *exclude, struct ldb_request *req, struct ldb_control ***saver);
+int check_critical_controls(struct ldb_control **controls);
+
+/* The following definitions come from lib/ldb/common/ldb_ldif.c */
+int ldb_should_b64_encode(const struct ldb_val *val);
+
+/* The following definitions come from lib/ldb/common/ldb_match.c */
+int ldb_match_msg(struct ldb_context *ldb,
+ const struct ldb_message *msg,
+ const struct ldb_parse_tree *tree,
+ struct ldb_dn *base,
+ enum ldb_scope scope);
+
+/* The following definitions come from lib/ldb/common/ldb_modules.c */
+
+struct ldb_module *ldb_module_new(TALLOC_CTX *memctx,
+ struct ldb_context *ldb,
+ const char *module_name,
+ const struct ldb_module_ops *ops);
+
+const char * ldb_module_get_name(struct ldb_module *module);
+struct ldb_context *ldb_module_get_ctx(struct ldb_module *module);
+void *ldb_module_get_private(struct ldb_module *module);
+void ldb_module_set_private(struct ldb_module *module, void *private_data);
+
+int ldb_next_request(struct ldb_module *module, struct ldb_request *request);
+int ldb_next_start_trans(struct ldb_module *module);
+int ldb_next_end_trans(struct ldb_module *module);
+int ldb_next_del_trans(struct ldb_module *module);
+int ldb_next_init(struct ldb_module *module);
+
+void ldb_set_errstring(struct ldb_context *ldb, const char *err_string);
+void ldb_asprintf_errstring(struct ldb_context *ldb, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
+void ldb_reset_err_string(struct ldb_context *ldb);
+
+const char *ldb_default_modules_dir(void);
+
+int ldb_register_module(const struct ldb_module_ops *);
+
+typedef int (*ldb_connect_fn)(struct ldb_context *ldb, const char *url,
+ unsigned int flags, const char *options[],
+ struct ldb_module **module);
+
+struct ldb_backend_ops {
+ const char *name;
+ ldb_connect_fn connect_fn;
+};
+
+const char *ldb_default_modules_dir(void);
+
+int ldb_register_backend(const char *url_prefix, ldb_connect_fn);
+
+struct ldb_handle *ldb_handle_new(TALLOC_CTX *mem_ctx, struct ldb_context *ldb);
+
+int ldb_module_send_entry(struct ldb_request *req,
+ struct ldb_message *msg,
+ struct ldb_control **ctrls);
+
+int ldb_module_send_referral(struct ldb_request *req,
+ char *ref);
+
+int ldb_module_done(struct ldb_request *req,
+ struct ldb_control **ctrls,
+ struct ldb_extended *response,
+ int error);
+
+int ldb_mod_register_control(struct ldb_module *module, const char *oid);
+
+#endif
diff --git a/source4/lib/ldb/include/ldb_private.h b/source4/lib/ldb/include/ldb_private.h
new file mode 100644
index 0000000000..2e8da9941c
--- /dev/null
+++ b/source4/lib/ldb/include/ldb_private.h
@@ -0,0 +1,165 @@
+/*
+ ldb database library
+
+ Copyright (C) Andrew Tridgell 2004
+ Copyright (C) Stefan Metzmacher 2004
+ Copyright (C) Simo Sorce 2004-2005
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb private header
+ *
+ * Description: defines internal ldb structures used by the subsystem and modules
+ *
+ * Author: Andrew Tridgell
+ * Author: Stefan Metzmacher
+ */
+
+#ifndef _LDB_PRIVATE_H_
+#define _LDB_PRIVATE_H_ 1
+
+#include "ldb_includes.h"
+#include "ldb.h"
+#include "ldb_module.h"
+
+struct ldb_context;
+
+struct ldb_module_ops;
+
+struct ldb_backend_ops;
+
+struct ldb_handle {
+ int status;
+ enum ldb_state state;
+ struct ldb_context *ldb;
+};
+
+/* basic module structure */
+struct ldb_module {
+ struct ldb_module *prev, *next;
+ struct ldb_context *ldb;
+ void *private_data;
+ const struct ldb_module_ops *ops;
+};
+
+/*
+ schema related information needed for matching rules
+*/
+struct ldb_schema {
+ /* attribute handling table */
+ unsigned num_attributes;
+ struct ldb_schema_attribute *attributes;
+
+ unsigned num_dn_extended_syntax;
+ struct ldb_dn_extended_syntax *dn_extended_syntax;
+};
+
+/*
+ every ldb connection is started by establishing a ldb_context
+*/
+struct ldb_context {
+ /* the operations provided by the backend */
+ struct ldb_module *modules;
+
+ /* debugging operations */
+ struct ldb_debug_ops debug_ops;
+
+ /* custom utf8 functions */
+ struct ldb_utf8_fns utf8_fns;
+
+ /* backend specific opaque parameters */
+ struct ldb_opaque {
+ struct ldb_opaque *next;
+ const char *name;
+ void *value;
+ } *opaque;
+
+ struct ldb_schema schema;
+
+ char *err_string;
+
+ int transaction_active;
+
+ int default_timeout;
+
+ unsigned int flags;
+
+ unsigned int create_perms;
+
+ char *modules_dir;
+
+ struct tevent_context *ev_ctx;
+};
+
+/* The following definitions come from lib/ldb/common/ldb.c */
+
+int ldb_connect_backend(struct ldb_context *ldb, const char *url, const char *options[],
+ struct ldb_module **backend_module);
+void ldb_set_default_dns(struct ldb_context *ldb);
+
+
+extern const struct ldb_module_ops ldb_objectclass_module_ops;
+extern const struct ldb_module_ops ldb_operational_module_ops;
+extern const struct ldb_module_ops ldb_paged_results_module_ops;
+extern const struct ldb_module_ops ldb_rdn_name_module_ops;
+extern const struct ldb_module_ops ldb_schema_module_ops;
+extern const struct ldb_module_ops ldb_asq_module_ops;
+extern const struct ldb_module_ops ldb_server_sort_module_ops;
+extern const struct ldb_module_ops ldb_ldap_module_ops;
+extern const struct ldb_module_ops ldb_ildap_module_ops;
+extern const struct ldb_module_ops ldb_paged_searches_module_ops;
+extern const struct ldb_module_ops ldb_tdb_module_ops;
+extern const struct ldb_module_ops ldb_skel_module_ops;
+extern const struct ldb_module_ops ldb_subtree_rename_module_ops;
+extern const struct ldb_module_ops ldb_subtree_delete_module_ops;
+extern const struct ldb_module_ops ldb_sqlite3_module_ops;
+extern const struct ldb_module_ops ldb_wins_ldb_module_ops;
+extern const struct ldb_module_ops ldb_ranged_results_module_ops;
+
+extern const struct ldb_backend_ops ldb_tdb_backend_ops;
+extern const struct ldb_backend_ops ldb_sqlite3_backend_ops;
+extern const struct ldb_backend_ops ldb_ldap_backend_ops;
+extern const struct ldb_backend_ops ldb_ldapi_backend_ops;
+extern const struct ldb_backend_ops ldb_ldaps_backend_ops;
+
+int ldb_setup_wellknown_attributes(struct ldb_context *ldb);
+
+const char **ldb_subclass_list(struct ldb_context *ldb, const char *classname);
+void ldb_subclass_remove(struct ldb_context *ldb, const char *classname);
+int ldb_subclass_add(struct ldb_context *ldb, const char *classname, const char *subclass);
+
+/* The following definitions come from lib/ldb/common/ldb_utf8.c */
+char *ldb_casefold_default(void *context, void *mem_ctx, const char *s, size_t n);
+
+void ldb_dump_results(struct ldb_context *ldb, struct ldb_result *result, FILE *f);
+
+
+/* The following definitions come from lib/ldb/common/ldb_modules.c */
+
+const char **ldb_modules_list_from_string(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, const char *string);
+int ldb_load_modules_list(struct ldb_context *ldb, const char **module_list, struct ldb_module *backend, struct ldb_module **out);
+int ldb_load_modules(struct ldb_context *ldb, const char *options[]);
+int ldb_init_module_chain(struct ldb_context *ldb, struct ldb_module *module);
+
+struct ldb_val ldb_binary_decode(void *mem_ctx, const char *str);
+
+#endif
diff --git a/source4/lib/ldb/install-sh b/source4/lib/ldb/install-sh
new file mode 100755
index 0000000000..58719246f0
--- /dev/null
+++ b/source4/lib/ldb/install-sh
@@ -0,0 +1,238 @@
+#! /bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.
+#
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+transformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+ case $1 in
+ -c) instcmd="$cpprog"
+ shift
+ continue;;
+
+ -d) dir_arg=true
+ shift
+ continue;;
+
+ -m) chmodcmd="$chmodprog $2"
+ shift
+ shift
+ continue;;
+
+ -o) chowncmd="$chownprog $2"
+ shift
+ shift
+ continue;;
+
+ -g) chgrpcmd="$chgrpprog $2"
+ shift
+ shift
+ continue;;
+
+ -s) stripcmd="$stripprog"
+ shift
+ continue;;
+
+ -t=*) transformarg=`echo $1 | sed 's/-t=//'`
+ shift
+ continue;;
+
+ -b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+ shift
+ continue;;
+
+ *) if [ x"$src" = x ]
+ then
+ src=$1
+ else
+ # this colon is to work around a 386BSD /bin/sh bug
+ :
+ dst=$1
+ fi
+ shift
+ continue;;
+ esac
+done
+
+if [ x"$src" = x ]
+then
+ echo "install: no input file specified"
+ exit 1
+else
+ true
+fi
+
+if [ x"$dir_arg" != x ]; then
+ dst=$src
+ src=""
+
+ if [ -d $dst ]; then
+ instcmd=:
+ else
+ instcmd=mkdir
+ fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad
+# if $src (and thus $dsttmp) contains '*'.
+
+ if [ -f $src -o -d $src ]
+ then
+ true
+ else
+ echo "install: $src does not exist"
+ exit 1
+ fi
+
+ if [ x"$dst" = x ]
+ then
+ echo "install: no destination specified"
+ exit 1
+ else
+ true
+ fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+ if [ -d $dst ]
+ then
+ dst="$dst"/`basename $src`
+ else
+ true
+ fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+# this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+ pathcomp="${pathcomp}${1}"
+ shift
+
+ if [ ! -d "${pathcomp}" ] ;
+ then
+ $mkdirprog "${pathcomp}"
+ else
+ true
+ fi
+
+ pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+ $doit $instcmd $dst &&
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+ if [ x"$transformarg" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ dstfile=`basename $dst $transformbasename |
+ sed $transformarg`$transformbasename
+ fi
+
+# don't allow the sed command to completely eliminate the filename
+
+ if [ x"$dstfile" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ true
+ fi
+
+# Make a temp file name in the proper directory.
+
+ dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+ $doit $instcmd $src $dsttmp &&
+
+ trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing. If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+ $doit $rmcmd -f $dstdir/$dstfile &&
+ $doit $mvcmd $dsttmp $dstdir/$dstfile
+
+fi &&
+
+
+exit 0
diff --git a/source4/lib/ldb/ldap.m4 b/source4/lib/ldb/ldap.m4
new file mode 100644
index 0000000000..417083ed61
--- /dev/null
+++ b/source4/lib/ldb/ldap.m4
@@ -0,0 +1,90 @@
+########################################################
+# Compile with LDAP support?
+
+LDAP_LIBS=""
+with_ldap_support=auto
+AC_MSG_CHECKING([for LDAP support])
+
+AC_ARG_WITH(ldap,
+AS_HELP_STRING([--with-ldap],[LDAP backend support (default=yes)]),
+[ case "$withval" in
+ yes|no)
+ with_ldap_support=$withval
+ ;;
+ esac ])
+
+AC_MSG_RESULT($with_ldap_support)
+
+if test x"$with_ldap_support" != x"no"; then
+
+ ##################################################################
+ # first test for ldap.h and lber.h
+ # (ldap.h is required for this test)
+ AC_CHECK_HEADERS(ldap.h lber.h)
+
+ if test x"$ac_cv_header_ldap_h" != x"yes"; then
+ if test x"$with_ldap_support" = x"yes"; then
+ AC_MSG_ERROR(ldap.h is needed for LDAP support)
+ else
+ AC_MSG_WARN(ldap.h is needed for LDAP support)
+ fi
+
+ with_ldap_support=no
+ fi
+fi
+
+if test x"$with_ldap_support" != x"no"; then
+ ac_save_LIBS=$LIBS
+
+ ##################################################################
+ # we might need the lber lib on some systems. To avoid link errors
+ # this test must be before the libldap test
+ AC_CHECK_LIB_EXT(lber, LDAP_LIBS, ber_scanf)
+
+ ########################################################
+ # now see if we can find the ldap libs in standard paths
+ AC_CHECK_LIB_EXT(ldap, LDAP_LIBS, ldap_init)
+
+ AC_CHECK_FUNC_EXT(ldap_domain2hostlist,$LDAP_LIBS)
+
+ ########################################################
+ # If we have LDAP, does it's rebind procedure take 2 or 3 arguments?
+ # Check found in pam_ldap 145.
+ AC_CHECK_FUNC_EXT(ldap_set_rebind_proc,$LDAP_LIBS)
+
+ LIBS="$LIBS $LDAP_LIBS"
+ AC_CACHE_CHECK(whether ldap_set_rebind_proc takes 3 arguments, smb_ldap_cv_ldap_set_rebind_proc, [
+ AC_TRY_COMPILE([
+ #include <lber.h>
+ #include <ldap.h>],
+ [ldap_set_rebind_proc(0, 0, 0);],
+ [smb_ldap_cv_ldap_set_rebind_proc=3],
+ [smb_ldap_cv_ldap_set_rebind_proc=2]
+ )
+ ])
+
+ AC_DEFINE_UNQUOTED(LDAP_SET_REBIND_PROC_ARGS, $smb_ldap_cv_ldap_set_rebind_proc, [Number of arguments to ldap_set_rebind_proc])
+
+ AC_CHECK_FUNC_EXT(ldap_initialize,$LDAP_LIBS)
+
+ if test x"$ac_cv_lib_ext_ldap_ldap_init" = x"yes" -a x"$ac_cv_func_ext_ldap_domain2hostlist" = x"yes"; then
+ AC_DEFINE(HAVE_LDAP,1,[Whether ldap is available])
+ AC_DEFINE(HAVE_LDB_LDAP,1,[Whether ldb_ldap is available])
+ with_ldap_support=yes
+ AC_MSG_CHECKING(whether LDAP support is used)
+ AC_MSG_RESULT(yes)
+ SMB_ENABLE(LDAP,YES)
+ else
+ if test x"$with_ldap_support" = x"yes"; then
+ AC_MSG_ERROR(libldap is needed for LDAP support)
+ else
+ AC_MSG_WARN(libldap is needed for LDAP support)
+ fi
+
+ LDAP_LIBS=""
+ with_ldap_support=no
+ fi
+ LIBS=$ac_save_LIBS
+fi
+
+SMB_EXT_LIB(LDAP,[${LDAP_LIBS}],[${LDAP_CFLAGS}],[${LDAP_CPPFLAGS}],[${LDAP_LDFLAGS}])
diff --git a/source4/lib/ldb/ldb.mk b/source4/lib/ldb/ldb.mk
new file mode 100644
index 0000000000..ff8c1f3baf
--- /dev/null
+++ b/source4/lib/ldb/ldb.mk
@@ -0,0 +1,81 @@
+LDB_LIB = -Llib -lldb
+
+LIB_FLAGS=$(LDFLAGS) $(LIBS) $(LDB_LIB) $(POPT_LIBS) $(TALLOC_LIBS) \
+ $(TDB_LIBS) $(TEVENT_LIBS) $(LDAP_LIBS) $(LIBDL)
+
+LDB_TDB_DIR=ldb_tdb
+LDB_TDB_OBJ=$(LDB_TDB_DIR)/ldb_tdb.o \
+ $(LDB_TDB_DIR)/ldb_pack.o $(LDB_TDB_DIR)/ldb_search.o $(LDB_TDB_DIR)/ldb_index.o \
+ $(LDB_TDB_DIR)/ldb_cache.o $(LDB_TDB_DIR)/ldb_tdb_wrap.o
+
+LDB_MAP_DIR=ldb_map
+LDB_MAP_OBJ=$(LDB_MAP_DIR)/ldb_map.o $(LDB_MAP_DIR)/ldb_map_inbound.o \
+ $(LDB_MAP_DIR)/ldb_map_outbound.o
+
+COMDIR=common
+COMMON_OBJ=$(COMDIR)/ldb.o $(COMDIR)/ldb_ldif.o \
+ $(COMDIR)/ldb_parse.o $(COMDIR)/ldb_msg.o $(COMDIR)/ldb_utf8.o \
+ $(COMDIR)/ldb_debug.o $(COMDIR)/ldb_modules.o \
+ $(COMDIR)/ldb_dn.o $(COMDIR)/ldb_match.o $(COMDIR)/ldb_attributes.o \
+ $(COMDIR)/attrib_handlers.o $(COMDIR)/ldb_controls.o $(COMDIR)/qsort.o
+
+MODDIR=modules
+MODULES_OBJ=$(MODDIR)/operational.o $(MODDIR)/rdn_name.o \
+ $(MODDIR)/paged_results.o $(MODDIR)/sort.o $(MODDIR)/asq.o
+
+NSSDIR=nssldb
+NSS_OBJ= $(NSSDIR)/ldb-nss.o $(NSSDIR)/ldb-pwd.o $(NSSDIR)/ldb-grp.o
+NSS_LIB = lib/libnss_ldb.$(SHLIBEXT).2
+
+lib/libldb.a: $(OBJS)
+ ar -rv $@ $(OBJS)
+ @-ranlib $@
+
+sample.$(SHLIBEXT): tests/sample_module.o
+ $(MDLD) $(MDLD_FLAGS) -o $@ tests/sample_module.o
+
+bin/ldbadd: tools/ldbadd.o tools/cmdline.o
+ $(CC) -o bin/ldbadd tools/ldbadd.o tools/cmdline.o $(LIB_FLAGS) $(LD_EXPORT_DYNAMIC)
+
+bin/ldbsearch: tools/ldbsearch.o tools/cmdline.o
+ $(CC) -o bin/ldbsearch tools/ldbsearch.o tools/cmdline.o $(LIB_FLAGS) $(LD_EXPORT_DYNAMIC)
+
+bin/ldbdel: tools/ldbdel.o tools/cmdline.o
+ $(CC) -o bin/ldbdel tools/ldbdel.o tools/cmdline.o $(LIB_FLAGS) $(LD_EXPORT_DYNAMIC)
+
+bin/ldbmodify: tools/ldbmodify.o tools/cmdline.o
+ $(CC) -o bin/ldbmodify tools/ldbmodify.o tools/cmdline.o $(LIB_FLAGS) $(LD_EXPORT_DYNAMIC)
+
+bin/ldbedit: tools/ldbedit.o tools/cmdline.o
+ $(CC) -o bin/ldbedit tools/ldbedit.o tools/cmdline.o $(LIB_FLAGS) $(LD_EXPORT_DYNAMIC)
+
+bin/ldbrename: tools/ldbrename.o tools/cmdline.o
+ $(CC) -o bin/ldbrename tools/ldbrename.o tools/cmdline.o $(LIB_FLAGS) $(LD_EXPORT_DYNAMIC)
+
+bin/ldbtest: tools/ldbtest.o tools/cmdline.o
+ $(CC) -o bin/ldbtest tools/ldbtest.o tools/cmdline.o $(LIB_FLAGS) $(LD_EXPORT_DYNAMIC)
+
+examples/ldbreader: examples/ldbreader.o
+ $(CC) -o examples/ldbreader examples/ldbreader.o $(LIB_FLAGS)
+
+examples/ldifreader: examples/ldifreader.o
+ $(CC) -o examples/ldifreader examples/ldifreader.o $(LIB_FLAGS)
+
+# Python bindings
+build-python:: ldb.$(SHLIBEXT)
+
+pyldb.o: $(ldbdir)/pyldb.c
+ $(CC) $(PICFLAG) -c $(ldbdir)/pyldb.c $(CFLAGS) `$(PYTHON_CONFIG) --cflags`
+
+ldb.$(SHLIBEXT): pyldb.o
+ $(SHLD) $(SHLD_FLAGS) -o ldb.$(SHLIBEXT) pyldb.o $(LIB_FLAGS) `$(PYTHON_CONFIG) --ldflags`
+
+install-python:: build-python
+ mkdir -p $(DESTDIR)`$(PYTHON) -c "import distutils.sysconfig; print distutils.sysconfig.get_python_lib(1, prefix='$(prefix)')"`
+ cp ldb.$(SHLIBEXT) $(DESTDIR)`$(PYTHON) -c "import distutils.sysconfig; print distutils.sysconfig.get_python_lib(1, prefix='$(prefix)')"`
+
+check-python:: build-python
+ LD_LIBRARY_PATH=lib PYTHONPATH=.:$(ldbdir) $(PYTHON) $(ldbdir)/tests/python/api.py
+
+clean::
+ rm -f ldb.$(SHLIBEXT)
diff --git a/source4/lib/ldb/ldb.pc.in b/source4/lib/ldb/ldb.pc.in
new file mode 100644
index 0000000000..b7e4c85844
--- /dev/null
+++ b/source4/lib/ldb/ldb.pc.in
@@ -0,0 +1,16 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+modulesdir=@modulesdir@
+
+Name: ldb
+Description: An LDAP-like embedded database
+Version: @PACKAGE_VERSION@
+Requires.private: tdb
+Requires: talloc
+Libs: -L${libdir} -lldb
+Libs.private: @LDAP_LIBS@
+Cflags: -I${includedir}
+Modulesdir: ${modulesdir}
+URL: http://ldb.samba.org/
diff --git a/source4/lib/ldb/ldb_ildap/config.mk b/source4/lib/ldb/ldb_ildap/config.mk
new file mode 100644
index 0000000000..6a1ef8164c
--- /dev/null
+++ b/source4/lib/ldb/ldb_ildap/config.mk
@@ -0,0 +1,13 @@
+################################################
+# Start MODULE ldb_ildap
+[MODULE::ldb_ildap]
+SUBSYSTEM = LIBLDB
+CFLAGS = -I$(ldbsrcdir)/include
+PRIVATE_DEPENDENCIES = LIBTALLOC LIBCLI_LDAP CREDENTIALS
+INIT_FUNCTION = LDB_BACKEND(ldapi),LDB_BACKEND(ldaps),LDB_BACKEND(ldap)
+ALIASES = ldapi ldaps ldap
+# End MODULE ldb_ildap
+################################################
+
+ldb_ildap_OBJ_FILES = $(ldbsrcdir)/ldb_ildap/ldb_ildap.o
+
diff --git a/source4/lib/ldb/ldb_ildap/ldb_ildap.c b/source4/lib/ldb/ldb_ildap/ldb_ildap.c
new file mode 100644
index 0000000000..4447d0e09a
--- /dev/null
+++ b/source4/lib/ldb/ldb_ildap/ldb_ildap.c
@@ -0,0 +1,849 @@
+/*
+ ldb database library - ildap backend
+
+ Copyright (C) Andrew Tridgell 2005
+ Copyright (C) Simo Sorce 2008
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb_ildap
+ *
+ * Component: ldb ildap backend
+ *
+ * Description: This is a ldb backend for the internal ldap
+ * client library in Samba4. By using this backend we are
+ * independent of a system ldap library
+ *
+ * Author: Andrew Tridgell
+ *
+ * Modifications:
+ *
+ * - description: make the module use asyncronous calls
+ * date: Feb 2006
+ * author: Simo Sorce
+ */
+
+#include "includes.h"
+#include "ldb_module.h"
+#include "dlinklist.h"
+
+#include "libcli/ldap/ldap.h"
+#include "libcli/ldap/ldap_client.h"
+#include "auth/auth.h"
+#include "auth/credentials/credentials.h"
+
+struct ildb_private {
+ struct ldap_connection *ldap;
+ struct tevent_context *event_ctx;
+};
+
+struct ildb_context {
+ struct ldb_module *module;
+ struct ldb_request *req;
+
+ struct ildb_private *ildb;
+ struct ldap_request *ireq;
+
+ bool done;
+
+ struct ildb_destructor_ctx *dc;
+};
+
+static void ildb_request_done(struct ildb_context *ctx,
+ struct ldb_control **ctrls, int error)
+{
+ struct ldb_context *ldb;
+ struct ldb_reply *ares;
+
+ ldb = ldb_module_get_ctx(ctx->module);
+
+ ctx->done = true;
+
+ if (ctx->req == NULL) {
+ /* if the req has been freed already just return */
+ return;
+ }
+
+ ares = talloc_zero(ctx->req, struct ldb_reply);
+ if (!ares) {
+ ldb_oom(ldb);
+ ctx->req->callback(ctx->req, NULL);
+ return;
+ }
+ ares->type = LDB_REPLY_DONE;
+ ares->controls = talloc_steal(ares, ctrls);
+ ares->error = error;
+
+ ctx->req->callback(ctx->req, ares);
+}
+
+static void ildb_auto_done_callback(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval t,
+ void *private_data)
+{
+ struct ildb_context *ac;
+
+ ac = talloc_get_type(private_data, struct ildb_context);
+ ildb_request_done(ac, NULL, LDB_SUCCESS);
+}
+
+/*
+ convert a ldb_message structure to a list of ldap_mod structures
+ ready for ildap_add() or ildap_modify()
+*/
+static struct ldap_mod **ildb_msg_to_mods(void *mem_ctx, int *num_mods,
+ const struct ldb_message *msg,
+ int use_flags)
+{
+ struct ldap_mod **mods;
+ unsigned int i;
+ int n = 0;
+
+ /* allocate maximum number of elements needed */
+ mods = talloc_array(mem_ctx, struct ldap_mod *, msg->num_elements+1);
+ if (!mods) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ mods[0] = NULL;
+
+ for (i = 0; i < msg->num_elements; i++) {
+ const struct ldb_message_element *el = &msg->elements[i];
+
+ mods[n] = talloc(mods, struct ldap_mod);
+ if (!mods[n]) {
+ goto failed;
+ }
+ mods[n + 1] = NULL;
+ mods[n]->type = 0;
+ mods[n]->attrib = *el;
+ if (use_flags) {
+ switch (el->flags & LDB_FLAG_MOD_MASK) {
+ case LDB_FLAG_MOD_ADD:
+ mods[n]->type = LDAP_MODIFY_ADD;
+ break;
+ case LDB_FLAG_MOD_DELETE:
+ mods[n]->type = LDAP_MODIFY_DELETE;
+ break;
+ case LDB_FLAG_MOD_REPLACE:
+ mods[n]->type = LDAP_MODIFY_REPLACE;
+ break;
+ }
+ }
+ n++;
+ }
+
+ *num_mods = n;
+ return mods;
+
+failed:
+ talloc_free(mods);
+ return NULL;
+}
+
+
+/*
+ map an ildap NTSTATUS to a ldb error code
+*/
+static int ildb_map_error(struct ldb_module *module, NTSTATUS status)
+{
+ struct ildb_private *ildb;
+ struct ldb_context *ldb;
+ TALLOC_CTX *mem_ctx;
+
+ ildb = talloc_get_type(ldb_module_get_private(module), struct ildb_private);
+ ldb = ldb_module_get_ctx(module);
+
+ if (NT_STATUS_IS_OK(status)) {
+ return LDB_SUCCESS;
+ }
+
+ mem_ctx = talloc_new(ildb);
+ if (!mem_ctx) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ldb_set_errstring(ldb,
+ ldap_errstr(ildb->ldap, mem_ctx, status));
+ talloc_free(mem_ctx);
+ if (NT_STATUS_IS_LDAP(status)) {
+ return NT_STATUS_LDAP_CODE(status);
+ }
+ return LDB_ERR_OPERATIONS_ERROR;
+}
+
+static void ildb_request_timeout(struct tevent_context *ev, struct tevent_timer *te,
+ struct timeval t, void *private_data)
+{
+ struct ildb_context *ac = talloc_get_type(private_data, struct ildb_context);
+
+ if (ac->ireq->state == LDAP_REQUEST_PENDING) {
+ DLIST_REMOVE(ac->ireq->conn->pending, ac->ireq);
+ }
+
+ ildb_request_done(ac, NULL, LDB_ERR_TIME_LIMIT_EXCEEDED);
+}
+
+static void ildb_callback(struct ldap_request *req)
+{
+ struct ldb_context *ldb;
+ struct ildb_context *ac;
+ NTSTATUS status;
+ struct ldap_SearchResEntry *search;
+ struct ldap_message *msg;
+ struct ldb_control **controls;
+ struct ldb_message *ldbmsg;
+ char *referral;
+ bool callback_failed;
+ bool request_done;
+ int ret;
+ int i;
+
+ ac = talloc_get_type(req->async.private_data, struct ildb_context);
+ ldb = ldb_module_get_ctx(ac->module);
+ callback_failed = false;
+ request_done = false;
+ controls = NULL;
+
+ if (!NT_STATUS_IS_OK(req->status)) {
+ ret = ildb_map_error(ac->module, req->status);
+ ildb_request_done(ac, NULL, ret);
+ return;
+ }
+
+ if (req->num_replies < 1) {
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ ildb_request_done(ac, NULL, ret);
+ return;
+ }
+
+ switch (req->type) {
+
+ case LDAP_TAG_ModifyRequest:
+ if (req->replies[0]->type != LDAP_TAG_ModifyResponse) {
+ ret = LDB_ERR_PROTOCOL_ERROR;
+ break;
+ }
+ status = ldap_check_response(ac->ireq->conn, &req->replies[0]->r.GeneralResult);
+ ret = ildb_map_error(ac->module, status);
+ request_done = true;
+ break;
+
+ case LDAP_TAG_AddRequest:
+ if (req->replies[0]->type != LDAP_TAG_AddResponse) {
+ ret = LDB_ERR_PROTOCOL_ERROR;
+ return;
+ }
+ status = ldap_check_response(ac->ireq->conn, &req->replies[0]->r.GeneralResult);
+ ret = ildb_map_error(ac->module, status);
+ request_done = true;
+ break;
+
+ case LDAP_TAG_DelRequest:
+ if (req->replies[0]->type != LDAP_TAG_DelResponse) {
+ ret = LDB_ERR_PROTOCOL_ERROR;
+ return;
+ }
+ status = ldap_check_response(ac->ireq->conn, &req->replies[0]->r.GeneralResult);
+ ret = ildb_map_error(ac->module, status);
+ request_done = true;
+ break;
+
+ case LDAP_TAG_ModifyDNRequest:
+ if (req->replies[0]->type != LDAP_TAG_ModifyDNResponse) {
+ ret = LDB_ERR_PROTOCOL_ERROR;
+ return;
+ }
+ status = ldap_check_response(ac->ireq->conn, &req->replies[0]->r.GeneralResult);
+ ret = ildb_map_error(ac->module, status);
+ request_done = true;
+ break;
+
+ case LDAP_TAG_SearchRequest:
+ /* loop over all messages */
+ for (i = 0; i < req->num_replies; i++) {
+
+ msg = req->replies[i];
+ switch (msg->type) {
+
+ case LDAP_TAG_SearchResultDone:
+
+ status = ldap_check_response(ac->ireq->conn, &msg->r.GeneralResult);
+ if (!NT_STATUS_IS_OK(status)) {
+ ret = ildb_map_error(ac->module, status);
+ break;
+ }
+
+ controls = talloc_steal(ac, msg->controls);
+ if (msg->r.SearchResultDone.resultcode) {
+ if (msg->r.SearchResultDone.errormessage) {
+ ldb_set_errstring(ldb, msg->r.SearchResultDone.errormessage);
+ }
+ }
+
+ ret = msg->r.SearchResultDone.resultcode;
+ request_done = true;
+ break;
+
+ case LDAP_TAG_SearchResultEntry:
+
+ ldbmsg = ldb_msg_new(ac);
+ if (!ldbmsg) {
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ break;
+ }
+
+ search = &(msg->r.SearchResultEntry);
+
+ ldbmsg->dn = ldb_dn_new(ldbmsg, ldb, search->dn);
+ if ( ! ldb_dn_validate(ldbmsg->dn)) {
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ break;
+ }
+ ldbmsg->num_elements = search->num_attributes;
+ ldbmsg->elements = talloc_move(ldbmsg, &search->attributes);
+
+ controls = talloc_steal(ac, msg->controls);
+
+ ret = ldb_module_send_entry(ac->req, ldbmsg, controls);
+ if (ret != LDB_SUCCESS) {
+ callback_failed = true;
+ }
+ break;
+
+ case LDAP_TAG_SearchResultReference:
+
+ referral = talloc_strdup(ac, msg->r.SearchResultReference.referral);
+
+ ret = ldb_module_send_referral(ac->req, referral);
+ if (ret != LDB_SUCCESS) {
+ callback_failed = true;
+ }
+ break;
+
+ default:
+ /* TAG not handled, fail ! */
+ ret = LDB_ERR_PROTOCOL_ERROR;
+ break;
+ }
+
+ if (ret != LDB_SUCCESS) {
+ break;
+ }
+ }
+
+ talloc_free(req->replies);
+ req->replies = NULL;
+ req->num_replies = 0;
+
+ break;
+
+ default:
+ ret = LDB_ERR_PROTOCOL_ERROR;
+ break;
+ }
+
+ if (ret != LDB_SUCCESS) {
+
+ /* if the callback failed the caller will have freed the
+ * request. Just return and don't try to use it */
+ if ( ! callback_failed) {
+ request_done = true;
+ }
+ }
+
+ if (request_done) {
+ ildb_request_done(ac, controls, ret);
+ }
+ return;
+}
+
+static int ildb_request_send(struct ildb_context *ac, struct ldap_message *msg)
+{
+ struct ldb_context *ldb;
+ struct ldap_request *req;
+
+ if (!ac) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ldb = ldb_module_get_ctx(ac->module);
+
+ req = ldap_request_send(ac->ildb->ldap, msg);
+ if (req == NULL) {
+ ldb_set_errstring(ldb, "async send request failed");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ac->ireq = talloc_steal(ac, req);
+
+ if (!ac->ireq->conn) {
+ ldb_set_errstring(ldb, "connection to remote LDAP server dropped?");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ talloc_free(req->time_event);
+ req->time_event = NULL;
+ if (ac->req->timeout) {
+ req->time_event = tevent_add_timer(ac->ildb->event_ctx, ac,
+ timeval_current_ofs(ac->req->timeout, 0),
+ ildb_request_timeout, ac);
+ }
+
+ req->async.fn = ildb_callback;
+ req->async.private_data = ac;
+
+ return LDB_SUCCESS;
+}
+
+/*
+ search for matching records using an asynchronous function
+ */
+static int ildb_search(struct ildb_context *ac)
+{
+ struct ldb_context *ldb;
+ struct ldb_request *req = ac->req;
+ struct ldap_message *msg;
+ int n;
+
+ ldb = ldb_module_get_ctx(ac->module);
+
+ if (!req->callback || !req->context) {
+ ldb_set_errstring(ldb, "Async interface called with NULL callback function or NULL context");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if (req->op.search.tree == NULL) {
+ ldb_set_errstring(ldb, "Invalid expression parse tree");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ msg = new_ldap_message(req);
+ if (msg == NULL) {
+ ldb_set_errstring(ldb, "Out of Memory");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ msg->type = LDAP_TAG_SearchRequest;
+
+ if (req->op.search.base == NULL) {
+ msg->r.SearchRequest.basedn = talloc_strdup(msg, "");
+ } else {
+ msg->r.SearchRequest.basedn = ldb_dn_get_extended_linearized(msg, req->op.search.base, 0);
+ }
+ if (msg->r.SearchRequest.basedn == NULL) {
+ ldb_set_errstring(ldb, "Unable to determine baseDN");
+ talloc_free(msg);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if (req->op.search.scope == LDB_SCOPE_DEFAULT) {
+ msg->r.SearchRequest.scope = LDB_SCOPE_SUBTREE;
+ } else {
+ msg->r.SearchRequest.scope = req->op.search.scope;
+ }
+
+ msg->r.SearchRequest.deref = LDAP_DEREFERENCE_NEVER;
+ msg->r.SearchRequest.timelimit = 0;
+ msg->r.SearchRequest.sizelimit = 0;
+ msg->r.SearchRequest.attributesonly = 0;
+ msg->r.SearchRequest.tree = discard_const(req->op.search.tree);
+
+ for (n = 0; req->op.search.attrs && req->op.search.attrs[n]; n++) /* noop */ ;
+ msg->r.SearchRequest.num_attributes = n;
+ msg->r.SearchRequest.attributes = req->op.search.attrs;
+ msg->controls = req->controls;
+
+ return ildb_request_send(ac, msg);
+}
+
+/*
+ add a record
+*/
+static int ildb_add(struct ildb_context *ac)
+{
+ struct ldb_request *req = ac->req;
+ struct ldap_message *msg;
+ struct ldap_mod **mods;
+ int i,n;
+
+ msg = new_ldap_message(req);
+ if (msg == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ msg->type = LDAP_TAG_AddRequest;
+
+ msg->r.AddRequest.dn = ldb_dn_get_extended_linearized(msg, req->op.add.message->dn, 0);
+ if (msg->r.AddRequest.dn == NULL) {
+ talloc_free(msg);
+ return LDB_ERR_INVALID_DN_SYNTAX;
+ }
+
+ mods = ildb_msg_to_mods(msg, &n, req->op.add.message, 0);
+ if (mods == NULL) {
+ talloc_free(msg);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ msg->r.AddRequest.num_attributes = n;
+ msg->r.AddRequest.attributes = talloc_array(msg, struct ldb_message_element, n);
+ if (msg->r.AddRequest.attributes == NULL) {
+ talloc_free(msg);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ for (i = 0; i < n; i++) {
+ msg->r.AddRequest.attributes[i] = mods[i]->attrib;
+ }
+
+ return ildb_request_send(ac, msg);
+}
+
+/*
+ modify a record
+*/
+static int ildb_modify(struct ildb_context *ac)
+{
+ struct ldb_request *req = ac->req;
+ struct ldap_message *msg;
+ struct ldap_mod **mods;
+ int i,n;
+
+ msg = new_ldap_message(req);
+ if (msg == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ msg->type = LDAP_TAG_ModifyRequest;
+
+ msg->r.ModifyRequest.dn = ldb_dn_get_extended_linearized(msg, req->op.mod.message->dn, 0);
+ if (msg->r.ModifyRequest.dn == NULL) {
+ talloc_free(msg);
+ return LDB_ERR_INVALID_DN_SYNTAX;
+ }
+
+ mods = ildb_msg_to_mods(msg, &n, req->op.mod.message, 1);
+ if (mods == NULL) {
+ talloc_free(msg);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ msg->r.ModifyRequest.num_mods = n;
+ msg->r.ModifyRequest.mods = talloc_array(msg, struct ldap_mod, n);
+ if (msg->r.ModifyRequest.mods == NULL) {
+ talloc_free(msg);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ for (i = 0; i < n; i++) {
+ msg->r.ModifyRequest.mods[i] = *mods[i];
+ }
+
+ return ildb_request_send(ac, msg);
+}
+
+/*
+ delete a record
+*/
+static int ildb_delete(struct ildb_context *ac)
+{
+ struct ldb_request *req = ac->req;
+ struct ldap_message *msg;
+
+ msg = new_ldap_message(req);
+ if (msg == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ msg->type = LDAP_TAG_DelRequest;
+
+ msg->r.DelRequest.dn = ldb_dn_get_extended_linearized(msg, req->op.del.dn, 0);
+ if (msg->r.DelRequest.dn == NULL) {
+ talloc_free(msg);
+ return LDB_ERR_INVALID_DN_SYNTAX;
+ }
+
+ return ildb_request_send(ac, msg);
+}
+
+/*
+ rename a record
+*/
+static int ildb_rename(struct ildb_context *ac)
+{
+ struct ldb_request *req = ac->req;
+ struct ldap_message *msg;
+
+ msg = new_ldap_message(req);
+ if (msg == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ msg->type = LDAP_TAG_ModifyDNRequest;
+ msg->r.ModifyDNRequest.dn = ldb_dn_get_extended_linearized(msg, req->op.rename.olddn, 0);
+ if (msg->r.ModifyDNRequest.dn == NULL) {
+ talloc_free(msg);
+ return LDB_ERR_INVALID_DN_SYNTAX;
+ }
+
+ msg->r.ModifyDNRequest.newrdn =
+ talloc_asprintf(msg, "%s=%s",
+ ldb_dn_get_rdn_name(req->op.rename.newdn),
+ ldb_dn_escape_value(msg, *ldb_dn_get_rdn_val(req->op.rename.newdn)));
+ if (msg->r.ModifyDNRequest.newrdn == NULL) {
+ talloc_free(msg);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ msg->r.ModifyDNRequest.newsuperior =
+ ldb_dn_alloc_linearized(msg, ldb_dn_get_parent(msg, req->op.rename.newdn));
+ if (msg->r.ModifyDNRequest.newsuperior == NULL) {
+ talloc_free(msg);
+ return LDB_ERR_INVALID_DN_SYNTAX;
+ }
+
+ msg->r.ModifyDNRequest.deleteolddn = true;
+
+ return ildb_request_send(ac, msg);
+}
+
+static int ildb_start_trans(struct ldb_module *module)
+{
+ /* TODO implement a local locking mechanism here */
+
+ return LDB_SUCCESS;
+}
+
+static int ildb_end_trans(struct ldb_module *module)
+{
+ /* TODO implement a local transaction mechanism here */
+
+ return LDB_SUCCESS;
+}
+
+static int ildb_del_trans(struct ldb_module *module)
+{
+ /* TODO implement a local locking mechanism here */
+
+ return LDB_SUCCESS;
+}
+
+static bool ildb_dn_is_special(struct ldb_request *req)
+{
+ struct ldb_dn *dn = NULL;
+
+ switch (req->operation) {
+ case LDB_ADD:
+ dn = req->op.add.message->dn;
+ break;
+ case LDB_MODIFY:
+ dn = req->op.mod.message->dn;
+ break;
+ case LDB_DELETE:
+ dn = req->op.del.dn;
+ break;
+ case LDB_RENAME:
+ dn = req->op.rename.olddn;
+ break;
+ default:
+ break;
+ }
+
+ if (dn && ldb_dn_is_special(dn)) {
+ return true;
+ }
+ return false;
+}
+
+static int ildb_handle_request(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct ildb_private *ildb;
+ struct ildb_context *ac;
+ struct tevent_timer *te;
+ int ret;
+
+ ildb = talloc_get_type(ldb_module_get_private(module), struct ildb_private);
+ ldb = ldb_module_get_ctx(module);
+
+ if (req->starttime == 0 || req->timeout == 0) {
+ ldb_set_errstring(ldb, "Invalid timeout settings");
+ return LDB_ERR_TIME_LIMIT_EXCEEDED;
+ }
+
+ ac = talloc_zero(req, struct ildb_context);
+ if (ac == NULL) {
+ ldb_set_errstring(ldb, "Out of Memory");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->module = module;
+ ac->req = req;
+ ac->ildb = ildb;
+
+ if (ildb_dn_is_special(req)) {
+
+ te = tevent_add_timer(ac->ildb->event_ctx,
+ ac, timeval_zero(),
+ ildb_auto_done_callback, ac);
+ if (NULL == te) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ return LDB_SUCCESS;
+ }
+
+ switch (ac->req->operation) {
+ case LDB_SEARCH:
+ ret = ildb_search(ac);
+ break;
+ case LDB_ADD:
+ ret = ildb_add(ac);
+ break;
+ case LDB_MODIFY:
+ ret = ildb_modify(ac);
+ break;
+ case LDB_DELETE:
+ ret = ildb_delete(ac);
+ break;
+ case LDB_RENAME:
+ ret = ildb_rename(ac);
+ break;
+ default:
+ /* no other op supported */
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ break;
+ }
+
+ return ret;
+}
+
+static const struct ldb_module_ops ildb_ops = {
+ .name = "ldap",
+ .search = ildb_handle_request,
+ .add = ildb_handle_request,
+ .modify = ildb_handle_request,
+ .del = ildb_handle_request,
+ .rename = ildb_handle_request,
+/* .request = ildb_handle_request, */
+ .start_transaction = ildb_start_trans,
+ .end_transaction = ildb_end_trans,
+ .del_transaction = ildb_del_trans,
+};
+
+/*
+ connect to the database
+*/
+static int ildb_connect(struct ldb_context *ldb, const char *url,
+ unsigned int flags, const char *options[],
+ struct ldb_module **_module)
+{
+ struct ldb_module *module;
+ struct ildb_private *ildb;
+ NTSTATUS status;
+ struct cli_credentials *creds;
+ struct loadparm_context *lp_ctx;
+
+ module = ldb_module_new(ldb, ldb, "ldb_ildap backend", &ildb_ops);
+ if (!module) return -1;
+
+ ildb = talloc(module, struct ildb_private);
+ if (!ildb) {
+ ldb_oom(ldb);
+ goto failed;
+ }
+ ldb_module_set_private(module, ildb);
+
+ ildb->event_ctx = ldb_get_event_context(ldb);
+
+ lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
+ struct loadparm_context);
+
+ ildb->ldap = ldap4_new_connection(ildb, lp_ctx,
+ ildb->event_ctx);
+ if (!ildb->ldap) {
+ ldb_oom(ldb);
+ goto failed;
+ }
+
+ if (flags & LDB_FLG_RECONNECT) {
+ ldap_set_reconn_params(ildb->ldap, 10);
+ }
+
+ status = ldap_connect(ildb->ldap, url);
+ if (!NT_STATUS_IS_OK(status)) {
+ ldb_debug(ldb, LDB_DEBUG_ERROR, "Failed to connect to ldap URL '%s' - %s\n",
+ url, ldap_errstr(ildb->ldap, module, status));
+ goto failed;
+ }
+
+ /* caller can optionally setup credentials using the opaque token 'credentials' */
+ creds = talloc_get_type(ldb_get_opaque(ldb, "credentials"), struct cli_credentials);
+ if (creds == NULL) {
+ struct auth_session_info *session_info = talloc_get_type(ldb_get_opaque(ldb, "sessionInfo"), struct auth_session_info);
+ if (session_info) {
+ creds = session_info->credentials;
+ }
+ }
+
+ if (creds != NULL && cli_credentials_authentication_requested(creds)) {
+ const char *bind_dn = cli_credentials_get_bind_dn(creds);
+ if (bind_dn) {
+ const char *password = cli_credentials_get_password(creds);
+ status = ldap_bind_simple(ildb->ldap, bind_dn, password);
+ if (!NT_STATUS_IS_OK(status)) {
+ ldb_debug(ldb, LDB_DEBUG_ERROR, "Failed to bind - %s\n",
+ ldap_errstr(ildb->ldap, module, status));
+ goto failed;
+ }
+ } else {
+ status = ldap_bind_sasl(ildb->ldap, creds, lp_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ ldb_debug(ldb, LDB_DEBUG_ERROR, "Failed to bind - %s\n",
+ ldap_errstr(ildb->ldap, module, status));
+ goto failed;
+ }
+ }
+ }
+
+ *_module = module;
+ return 0;
+
+failed:
+ talloc_free(module);
+ return -1;
+}
+
+_PUBLIC_ const struct ldb_backend_ops ldb_ldap_backend_ops = {
+ .name = "ldap",
+ .connect_fn = ildb_connect
+};
+
+_PUBLIC_ const struct ldb_backend_ops ldb_ldapi_backend_ops = {
+ .name = "ldapi",
+ .connect_fn = ildb_connect
+};
+
+_PUBLIC_ const struct ldb_backend_ops ldb_ldaps_backend_ops = {
+ .name = "ldaps",
+ .connect_fn = ildb_connect
+};
+
diff --git a/source4/lib/ldb/ldb_ldap/ldb_ldap.c b/source4/lib/ldb/ldb_ldap/ldb_ldap.c
new file mode 100644
index 0000000000..43a01f75a7
--- /dev/null
+++ b/source4/lib/ldb/ldb_ldap/ldb_ldap.c
@@ -0,0 +1,909 @@
+/*
+ ldb database library
+
+ Copyright (C) Andrew Tridgell 2004
+ Copyright (C) Simo Sorce 2006
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb_ldap
+ *
+ * Component: ldb ldap backend
+ *
+ * Description: core files for LDAP backend
+ *
+ * Author: Andrew Tridgell
+ *
+ * Modifications:
+ *
+ * - description: make the module use asyncronous calls
+ * date: Feb 2006
+ * author: Simo Sorce
+ */
+
+#include "ldb_includes.h"
+#include "ldb_module.h"
+
+#define LDAP_DEPRECATED 1
+#include <ldap.h>
+
+struct lldb_private {
+ LDAP *ldap;
+};
+
+struct lldb_context {
+ struct ldb_module *module;
+ struct ldb_request *req;
+
+ struct lldb_private *lldb;
+
+ struct ldb_control **controls;
+ int msgid;
+};
+
+static int lldb_ldap_to_ldb(int err) {
+ /* Ldap errors and ldb errors are defined to the same values */
+ return err;
+}
+
+/*
+ convert a ldb_message structure to a list of LDAPMod structures
+ ready for ldap_add() or ldap_modify()
+*/
+static LDAPMod **lldb_msg_to_mods(void *mem_ctx, const struct ldb_message *msg, int use_flags)
+{
+ LDAPMod **mods;
+ unsigned int i, j;
+ int num_mods = 0;
+
+ /* allocate maximum number of elements needed */
+ mods = talloc_array(mem_ctx, LDAPMod *, msg->num_elements+1);
+ if (!mods) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ mods[0] = NULL;
+
+ for (i=0;i<msg->num_elements;i++) {
+ const struct ldb_message_element *el = &msg->elements[i];
+
+ mods[num_mods] = talloc(mods, LDAPMod);
+ if (!mods[num_mods]) {
+ goto failed;
+ }
+ mods[num_mods+1] = NULL;
+ mods[num_mods]->mod_op = LDAP_MOD_BVALUES;
+ if (use_flags) {
+ switch (el->flags & LDB_FLAG_MOD_MASK) {
+ case LDB_FLAG_MOD_ADD:
+ mods[num_mods]->mod_op |= LDAP_MOD_ADD;
+ break;
+ case LDB_FLAG_MOD_DELETE:
+ mods[num_mods]->mod_op |= LDAP_MOD_DELETE;
+ break;
+ case LDB_FLAG_MOD_REPLACE:
+ mods[num_mods]->mod_op |= LDAP_MOD_REPLACE;
+ break;
+ }
+ }
+ mods[num_mods]->mod_type = discard_const_p(char, el->name);
+ mods[num_mods]->mod_vals.modv_bvals = talloc_array(mods[num_mods],
+ struct berval *,
+ 1+el->num_values);
+ if (!mods[num_mods]->mod_vals.modv_bvals) {
+ goto failed;
+ }
+
+ for (j=0;j<el->num_values;j++) {
+ mods[num_mods]->mod_vals.modv_bvals[j] = talloc(mods[num_mods]->mod_vals.modv_bvals,
+ struct berval);
+ if (!mods[num_mods]->mod_vals.modv_bvals[j]) {
+ goto failed;
+ }
+ mods[num_mods]->mod_vals.modv_bvals[j]->bv_val = el->values[j].data;
+ mods[num_mods]->mod_vals.modv_bvals[j]->bv_len = el->values[j].length;
+ }
+ mods[num_mods]->mod_vals.modv_bvals[j] = NULL;
+ num_mods++;
+ }
+
+ return mods;
+
+failed:
+ talloc_free(mods);
+ return NULL;
+}
+
+/*
+ add a single set of ldap message values to a ldb_message
+*/
+static int lldb_add_msg_attr(struct ldb_context *ldb,
+ struct ldb_message *msg,
+ const char *attr, struct berval **bval)
+{
+ int count, i;
+ struct ldb_message_element *el;
+
+ count = ldap_count_values_len(bval);
+
+ if (count <= 0) {
+ return -1;
+ }
+
+ el = talloc_realloc(msg, msg->elements, struct ldb_message_element,
+ msg->num_elements + 1);
+ if (!el) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ msg->elements = el;
+
+ el = &msg->elements[msg->num_elements];
+
+ el->name = talloc_strdup(msg->elements, attr);
+ if (!el->name) {
+ errno = ENOMEM;
+ return -1;
+ }
+ el->flags = 0;
+
+ el->num_values = 0;
+ el->values = talloc_array(msg->elements, struct ldb_val, count);
+ if (!el->values) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ for (i=0;i<count;i++) {
+ /* we have to ensure this is null terminated so that
+ ldb_msg_find_attr_as_string() can work */
+ el->values[i].data = talloc_size(el->values, bval[i]->bv_len+1);
+ if (!el->values[i].data) {
+ errno = ENOMEM;
+ return -1;
+ }
+ memcpy(el->values[i].data, bval[i]->bv_val, bval[i]->bv_len);
+ el->values[i].data[bval[i]->bv_len] = 0;
+ el->values[i].length = bval[i]->bv_len;
+ el->num_values++;
+ }
+
+ msg->num_elements++;
+
+ return 0;
+}
+
+/*
+ search for matching records
+*/
+static int lldb_search(struct lldb_context *lldb_ac)
+{
+ struct ldb_context *ldb;
+ struct lldb_private *lldb = lldb_ac->lldb;
+ struct ldb_module *module = lldb_ac->module;
+ struct ldb_request *req = lldb_ac->req;
+ struct timeval tv;
+ int ldap_scope;
+ char *search_base;
+ char *expression;
+ int ret;
+
+ ldb = ldb_module_get_ctx(module);
+
+ if (!req->callback || !req->context) {
+ ldb_set_errstring(ldb, "Async interface called with NULL callback function or NULL context");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if (req->op.search.tree == NULL) {
+ ldb_set_errstring(ldb, "Invalid expression parse tree");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if (req->controls != NULL) {
+ ldb_debug(ldb, LDB_DEBUG_WARNING, "Controls are not yet supported by ldb_ldap backend!\n");
+ }
+
+ ldb_request_set_state(req, LDB_ASYNC_PENDING);
+
+ search_base = ldb_dn_alloc_linearized(lldb_ac, req->op.search.base);
+ if (req->op.search.base == NULL) {
+ search_base = talloc_strdup(lldb_ac, "");
+ }
+ if (search_base == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ expression = ldb_filter_from_tree(lldb_ac, req->op.search.tree);
+ if (expression == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ switch (req->op.search.scope) {
+ case LDB_SCOPE_BASE:
+ ldap_scope = LDAP_SCOPE_BASE;
+ break;
+ case LDB_SCOPE_ONELEVEL:
+ ldap_scope = LDAP_SCOPE_ONELEVEL;
+ break;
+ default:
+ ldap_scope = LDAP_SCOPE_SUBTREE;
+ break;
+ }
+
+ tv.tv_sec = req->timeout;
+ tv.tv_usec = 0;
+
+ ret = ldap_search_ext(lldb->ldap, search_base, ldap_scope,
+ expression,
+ discard_const_p(char *, req->op.search.attrs),
+ 0,
+ NULL,
+ NULL,
+ &tv,
+ LDAP_NO_LIMIT,
+ &lldb_ac->msgid);
+
+ if (ret != LDAP_SUCCESS) {
+ ldb_set_errstring(ldb, ldap_err2string(ret));
+ }
+
+ return lldb_ldap_to_ldb(ret);
+}
+
+/*
+ add a record
+*/
+static int lldb_add(struct lldb_context *lldb_ac)
+{
+ struct ldb_context *ldb;
+ struct lldb_private *lldb = lldb_ac->lldb;
+ struct ldb_module *module = lldb_ac->module;
+ struct ldb_request *req = lldb_ac->req;
+ LDAPMod **mods;
+ char *dn;
+ int ret;
+
+ ldb_module_get_ctx(module);
+
+ ldb_request_set_state(req, LDB_ASYNC_PENDING);
+
+ mods = lldb_msg_to_mods(lldb_ac, req->op.add.message, 0);
+ if (mods == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ dn = ldb_dn_alloc_linearized(lldb_ac, req->op.add.message->dn);
+ if (dn == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ldap_add_ext(lldb->ldap, dn, mods,
+ NULL,
+ NULL,
+ &lldb_ac->msgid);
+
+ if (ret != LDAP_SUCCESS) {
+ ldb_set_errstring(ldb, ldap_err2string(ret));
+ }
+
+ return lldb_ldap_to_ldb(ret);
+}
+
+/*
+ modify a record
+*/
+static int lldb_modify(struct lldb_context *lldb_ac)
+{
+ struct ldb_context *ldb;
+ struct lldb_private *lldb = lldb_ac->lldb;
+ struct ldb_module *module = lldb_ac->module;
+ struct ldb_request *req = lldb_ac->req;
+ LDAPMod **mods;
+ char *dn;
+ int ret;
+
+ ldb_module_get_ctx(module);
+
+ ldb_request_set_state(req, LDB_ASYNC_PENDING);
+
+ mods = lldb_msg_to_mods(lldb_ac, req->op.mod.message, 1);
+ if (mods == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ dn = ldb_dn_alloc_linearized(lldb_ac, req->op.mod.message->dn);
+ if (dn == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ldap_modify_ext(lldb->ldap, dn, mods,
+ NULL,
+ NULL,
+ &lldb_ac->msgid);
+
+ if (ret != LDAP_SUCCESS) {
+ ldb_set_errstring(ldb, ldap_err2string(ret));
+ }
+
+ return lldb_ldap_to_ldb(ret);
+}
+
+/*
+ delete a record
+*/
+static int lldb_delete(struct lldb_context *lldb_ac)
+{
+ struct ldb_context *ldb;
+ struct lldb_private *lldb = lldb_ac->lldb;
+ struct ldb_module *module = lldb_ac->module;
+ struct ldb_request *req = lldb_ac->req;
+ char *dnstr;
+ int ret;
+
+ ldb_module_get_ctx(module);
+
+ ldb_request_set_state(req, LDB_ASYNC_PENDING);
+
+ dnstr = ldb_dn_alloc_linearized(lldb_ac, req->op.del.dn);
+
+ ret = ldap_delete_ext(lldb->ldap, dnstr,
+ NULL,
+ NULL,
+ &lldb_ac->msgid);
+
+ if (ret != LDAP_SUCCESS) {
+ ldb_set_errstring(ldb, ldap_err2string(ret));
+ }
+
+ return lldb_ldap_to_ldb(ret);
+}
+
+/*
+ rename a record
+*/
+static int lldb_rename(struct lldb_context *lldb_ac)
+{
+ struct ldb_context *ldb;
+ struct lldb_private *lldb = lldb_ac->lldb;
+ struct ldb_module *module = lldb_ac->module;
+ struct ldb_request *req = lldb_ac->req;
+ char *old_dn;
+ char *newrdn;
+ char *parentdn;
+ int ret;
+
+ ldb_module_get_ctx(module);
+
+ ldb_request_set_state(req, LDB_ASYNC_PENDING);
+
+ old_dn = ldb_dn_alloc_linearized(lldb_ac, req->op.rename.olddn);
+ if (old_dn == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ newrdn = talloc_asprintf(lldb_ac, "%s=%s",
+ ldb_dn_get_rdn_name(req->op.rename.newdn),
+ ldb_dn_escape_value(lldb, *(ldb_dn_get_rdn_val(req->op.rename.newdn))));
+ if (!newrdn) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ parentdn = ldb_dn_alloc_linearized(lldb_ac, ldb_dn_get_parent(lldb_ac, req->op.rename.newdn));
+ if (!parentdn) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ldap_rename(lldb->ldap, old_dn, newrdn, parentdn,
+ 1, NULL, NULL,
+ &lldb_ac->msgid);
+
+ if (ret != LDAP_SUCCESS) {
+ ldb_set_errstring(ldb, ldap_err2string(ret));
+ }
+
+ return lldb_ldap_to_ldb(ret);
+}
+
+static int lldb_start_trans(struct ldb_module *module)
+{
+ /* TODO implement a local transaction mechanism here */
+
+ return LDB_SUCCESS;
+}
+
+static int lldb_end_trans(struct ldb_module *module)
+{
+ /* TODO implement a local transaction mechanism here */
+
+ return LDB_SUCCESS;
+}
+
+static int lldb_del_trans(struct ldb_module *module)
+{
+ /* TODO implement a local transaction mechanism here */
+
+ return LDB_SUCCESS;
+}
+
+void lldb_request_done(struct lldb_context *ac,
+ struct ldb_control **ctrls, int error)
+{
+ struct ldb_request *req;
+ struct ldb_reply *ares;
+
+ req = ac->req;
+
+ ares = talloc_zero(req, struct ldb_reply);
+ if (!ares) {
+ ldb_oom(ldb_module_get_ctx(ac->module));
+ req->callback(req, NULL);
+ return;
+ }
+ ares->type = LDB_REPLY_DONE;
+ ares->controls = talloc_steal(ares, ctrls);
+ ares->error = error;
+
+ req->callback(req, ares);
+}
+
+/* return false if the request is still in progress
+ * return true if the request is completed
+ */
+static bool lldb_parse_result(struct lldb_context *ac, LDAPMessage *result)
+{
+ struct ldb_context *ldb;
+ struct lldb_private *lldb = ac->lldb;
+ LDAPControl **serverctrlsp = NULL;
+ char **referralsp = NULL;
+ char *matcheddnp = NULL;
+ char *errmsgp = NULL;
+ LDAPMessage *msg;
+ int type;
+ struct ldb_message *ldbmsg;
+ char *referral;
+ bool callback_failed;
+ bool request_done;
+ bool lret;
+ int ret;
+ int i;
+
+ ldb = ldb_module_get_ctx(ac->module);
+
+ type = ldap_msgtype(result);
+ callback_failed = false;
+ request_done = false;
+
+ switch (type) {
+ case LDAP_RES_SEARCH_ENTRY:
+
+ msg = ldap_first_entry(lldb->ldap, result);
+ if (msg != NULL) {
+ BerElement *berptr = NULL;
+ char *attr, *dn;
+
+ ldbmsg = ldb_msg_new(ac);
+ if (!ldbmsg) {
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ break;
+ }
+
+ dn = ldap_get_dn(lldb->ldap, msg);
+ if (!dn) {
+ talloc_free(ldbmsg);
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ break;
+ }
+ ldbmsg->dn = ldb_dn_new(ldbmsg, ldb, dn);
+ if ( ! ldb_dn_validate(ldbmsg->dn)) {
+ talloc_free(ldbmsg);
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ break;
+ }
+ ldap_memfree(dn);
+
+ ldbmsg->num_elements = 0;
+ ldbmsg->elements = NULL;
+
+ /* loop over all attributes */
+ for (attr=ldap_first_attribute(lldb->ldap, msg, &berptr);
+ attr;
+ attr=ldap_next_attribute(lldb->ldap, msg, berptr)) {
+ struct berval **bval;
+ bval = ldap_get_values_len(lldb->ldap, msg, attr);
+
+ if (bval) {
+ lldb_add_msg_attr(ldb, ldbmsg, attr, bval);
+ ldap_value_free_len(bval);
+ }
+ }
+ if (berptr) ber_free(berptr, 0);
+
+ ret = ldb_module_send_entry(ac->req, ldbmsg, NULL /* controls not yet supported */);
+ if (ret != LDB_SUCCESS) {
+
+ callback_failed = true;
+ }
+ } else {
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ }
+ break;
+
+ case LDAP_RES_SEARCH_REFERENCE:
+
+ if (ldap_parse_result(lldb->ldap, result, &ret,
+ &matcheddnp, &errmsgp,
+ &referralsp, &serverctrlsp, 0) != LDAP_SUCCESS) {
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ }
+ if (ret != LDB_SUCCESS) {
+ break;
+ }
+ if (referralsp == NULL) {
+ ret = LDB_ERR_PROTOCOL_ERROR;
+ break;
+ }
+
+ for (i = 0; referralsp[i]; i++) {
+ referral = talloc_strdup(ac, referralsp[i]);
+
+ ret = ldb_module_send_referral(ac->req, referral);
+ if (ret != LDB_SUCCESS) {
+ callback_failed = true;
+ break;
+ }
+ }
+ break;
+
+ case LDAP_RES_SEARCH_RESULT:
+ case LDAP_RES_MODIFY:
+ case LDAP_RES_ADD:
+ case LDAP_RES_DELETE:
+ case LDAP_RES_MODDN:
+
+ if (ldap_parse_result(lldb->ldap, result, &ret,
+ &matcheddnp, &errmsgp,
+ &referralsp, &serverctrlsp, 0) != LDAP_SUCCESS) {
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ }
+ if (ret != LDB_SUCCESS) {
+ break;
+ }
+
+ if (serverctrlsp != NULL) {
+ /* FIXME: transform the LDAPControl list into an ldb_control one */
+ ac->controls = NULL;
+ }
+
+ request_done = true;
+ break;
+
+ default:
+ ret = LDB_ERR_PROTOCOL_ERROR;
+ break;
+ }
+
+ if (ret != LDB_SUCCESS) {
+
+ /* if the callback failed the caller will have freed the
+ * request. Just return and don't try to use it */
+ if (callback_failed) {
+
+ /* tell lldb_wait to remove the request from the
+ * queue */
+ lret = true;
+ goto free_and_return;
+ }
+
+ request_done = true;
+ }
+
+ if (request_done) {
+ lldb_request_done(ac, ac->controls, ret);
+ lret = true;
+ goto free_and_return;
+ }
+
+ lret = false;
+
+free_and_return:
+
+ if (matcheddnp) ldap_memfree(matcheddnp);
+ if (errmsgp && *errmsgp) {
+ ldb_set_errstring(ldb, errmsgp);
+ }
+ if (errmsgp) {
+ ldap_memfree(errmsgp);
+ }
+ if (referralsp) ldap_value_free(referralsp);
+ if (serverctrlsp) ldap_controls_free(serverctrlsp);
+
+ ldap_msgfree(result);
+
+ return lret;
+}
+
+static void lldb_timeout(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval t,
+ void *private_data)
+{
+ struct lldb_context *ac;
+ ac = talloc_get_type(private_data, struct lldb_context);
+
+ lldb_request_done(ac, NULL, LDB_ERR_TIME_LIMIT_EXCEEDED);
+}
+
+static void lldb_callback(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval t,
+ void *private_data)
+{
+ struct lldb_context *ac;
+ struct tevent_timer *lte;
+ struct timeval tv;
+ LDAPMessage *result;
+ int lret;
+
+ ac = talloc_get_type(private_data, struct lldb_context);
+
+ if (!ac->msgid) {
+ lldb_request_done(ac, NULL, LDB_ERR_OPERATIONS_ERROR);
+ return;
+ }
+
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ lret = ldap_result(ac->lldb->ldap, ac->msgid, 0, &tv, &result);
+ if (lret == 0) {
+ goto respin;
+ }
+ if (lret == -1) {
+ lldb_request_done(ac, NULL, LDB_ERR_OPERATIONS_ERROR);
+ return;
+ }
+
+ if ( ! lldb_parse_result(ac, result)) {
+ goto respin;
+ }
+
+ return;
+
+respin:
+ tv.tv_sec = 0;
+ tv.tv_usec = 100;
+ lte = tevent_add_timer(ev, ac, tv, lldb_callback, ac);
+ if (NULL == lte) {
+ lldb_request_done(ac, NULL, LDB_ERR_OPERATIONS_ERROR);
+ }
+}
+
+static bool lldb_dn_is_special(struct ldb_request *req)
+{
+ struct ldb_dn *dn = NULL;
+
+ switch (req->operation) {
+ case LDB_ADD:
+ dn = req->op.add.message->dn;
+ break;
+ case LDB_MODIFY:
+ dn = req->op.mod.message->dn;
+ break;
+ case LDB_DELETE:
+ dn = req->op.del.dn;
+ break;
+ case LDB_RENAME:
+ dn = req->op.rename.olddn;
+ break;
+ default:
+ break;
+ }
+
+ if (dn && ldb_dn_is_special(dn)) {
+ return true;
+ }
+ return false;
+}
+
+static void lldb_auto_done_callback(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval t,
+ void *private_data)
+{
+ struct lldb_context *ac;
+
+ ac = talloc_get_type(private_data, struct lldb_context);
+ lldb_request_done(ac, NULL, LDB_SUCCESS);
+}
+
+static int lldb_handle_request(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct lldb_private *lldb;
+ struct lldb_context *ac;
+ struct tevent_context *ev;
+ struct tevent_timer *te;
+ struct timeval tv;
+ int ret;
+
+ lldb = talloc_get_type(ldb_module_get_private(module), struct lldb_private);
+ ldb = ldb_module_get_ctx(module);
+
+ if (req->starttime == 0 || req->timeout == 0) {
+ ldb_set_errstring(ldb, "Invalid timeout settings");
+ return LDB_ERR_TIME_LIMIT_EXCEEDED;
+ }
+
+ ev = ldb_get_event_context(ldb);
+ if (NULL == ev) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac = talloc_zero(ldb, struct lldb_context);
+ if (ac == NULL) {
+ ldb_set_errstring(ldb, "Out of Memory");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->module = module;
+ ac->req = req;
+ ac->lldb = lldb;
+ ac->msgid = 0;
+
+ if (lldb_dn_is_special(req)) {
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ te = tevent_add_timer(ev, ac, tv,
+ lldb_auto_done_callback, ac);
+ if (NULL == te) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ return LDB_SUCCESS;
+ }
+
+ switch (ac->req->operation) {
+ case LDB_SEARCH:
+ ret = lldb_search(ac);
+ break;
+ case LDB_ADD:
+ ret = lldb_add(ac);
+ break;
+ case LDB_MODIFY:
+ ret = lldb_modify(ac);
+ break;
+ case LDB_DELETE:
+ ret = lldb_delete(ac);
+ break;
+ case LDB_RENAME:
+ ret = lldb_rename(ac);
+ break;
+ default:
+ /* no other op supported */
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ break;
+ }
+
+ if (ret != LDB_SUCCESS) {
+ lldb_request_done(ac, NULL, ret);
+ return ret;
+ }
+
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ te = tevent_add_timer(ev, ac, tv, lldb_callback, ac);
+ if (NULL == te) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+
+ tv.tv_sec = req->starttime + req->timeout;
+ tv.tv_usec = 0;
+ te = tevent_add_timer(ev, ac, tv, lldb_timeout, ac);
+ if (NULL == te) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ return LDB_SUCCESS;
+}
+
+static const struct ldb_module_ops lldb_ops = {
+ .name = "ldap",
+ .search = lldb_handle_request,
+ .add = lldb_handle_request,
+ .modify = lldb_handle_request,
+ .del = lldb_handle_request,
+ .rename = lldb_handle_request,
+ .request = lldb_handle_request,
+ .start_transaction = lldb_start_trans,
+ .end_transaction = lldb_end_trans,
+ .del_transaction = lldb_del_trans,
+};
+
+
+static int lldb_destructor(struct lldb_private *lldb)
+{
+ ldap_unbind(lldb->ldap);
+ return 0;
+}
+
+/*
+ connect to the database
+*/
+static int lldb_connect(struct ldb_context *ldb,
+ const char *url,
+ unsigned int flags,
+ const char *options[],
+ struct ldb_module **_module)
+{
+ struct ldb_module *module;
+ struct lldb_private *lldb;
+ int version = 3;
+ int ret;
+
+ module = ldb_module_new(ldb, ldb, "ldb_ldap backend", &lldb_ops);
+ if (!module) return -1;
+
+ lldb = talloc_zero(module, struct lldb_private);
+ if (!lldb) {
+ ldb_oom(ldb);
+ goto failed;
+ }
+ ldb_module_set_private(module, lldb);
+
+ ret = ldap_initialize(&lldb->ldap, url);
+ if (ret != LDAP_SUCCESS) {
+ ldb_debug(ldb, LDB_DEBUG_FATAL, "ldap_initialize failed for URL '%s' - %s\n",
+ url, ldap_err2string(ret));
+ goto failed;
+ }
+
+ talloc_set_destructor(lldb, lldb_destructor);
+
+ ret = ldap_set_option(lldb->ldap, LDAP_OPT_PROTOCOL_VERSION, &version);
+ if (ret != LDAP_SUCCESS) {
+ ldb_debug(ldb, LDB_DEBUG_FATAL, "ldap_set_option failed - %s\n",
+ ldap_err2string(ret));
+ goto failed;
+ }
+
+ *_module = module;
+ return 0;
+
+failed:
+ talloc_free(module);
+ return -1;
+}
+
+const struct ldb_backend_ops ldb_ldap_backend_ops = {
+ .name = "ldap",
+ .connect_fn = lldb_connect
+};
+
+const struct ldb_backend_ops ldb_ldapi_backend_ops = {
+ .name = "ldapi",
+ .connect_fn = lldb_connect
+};
+
+const struct ldb_backend_ops ldb_ldaps_backend_ops = {
+ .name = "ldaps",
+ .connect_fn = lldb_connect
+};
diff --git a/source4/lib/ldb/ldb_map/ldb_map.c b/source4/lib/ldb/ldb_map/ldb_map.c
new file mode 100644
index 0000000000..5b4ea7910a
--- /dev/null
+++ b/source4/lib/ldb/ldb_map/ldb_map.c
@@ -0,0 +1,1135 @@
+/*
+ ldb database mapping module
+
+ Copyright (C) Jelmer Vernooij 2005
+ Copyright (C) Martin Kuehl <mkhl@samba.org> 2006
+ Copyright (C) Simo Sorce 2008
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb ldb_map module
+ *
+ * Description: Map portions of data into a different format on a
+ * remote partition.
+ *
+ * Author: Jelmer Vernooij, Martin Kuehl
+ */
+
+#include "ldb_includes.h"
+#include "ldb_map.h"
+#include "ldb_map_private.h"
+
+#ifndef _PUBLIC_
+#define _PUBLIC_
+#endif
+
+/* Description of the provided ldb requests:
+ - special attribute 'isMapped'
+
+ - search:
+ - if parse tree can be split
+ - search remote records w/ remote attrs and parse tree
+ - otherwise
+ - enumerate all remote records
+ - for each remote result
+ - map remote result to local message
+ - search local result
+ - is present
+ - merge local into remote result
+ - run callback on merged result
+ - otherwise
+ - run callback on remote result
+
+ - add:
+ - split message into local and remote part
+ - if local message is not empty
+ - add isMapped to local message
+ - add local message
+ - add remote message
+
+ - modify:
+ - split message into local and remote part
+ - if local message is not empty
+ - add isMapped to local message
+ - search for local record
+ - if present
+ - modify local record
+ - otherwise
+ - add local message
+ - modify remote record
+
+ - delete:
+ - search for local record
+ - if present
+ - delete local record
+ - delete remote record
+
+ - rename:
+ - search for local record
+ - if present
+ - rename local record
+ - modify local isMapped
+ - rename remote record
+*/
+
+
+
+/* Private data structures
+ * ======================= */
+
+/* Global private data */
+/* Extract mappings from private data. */
+const struct ldb_map_context *map_get_context(struct ldb_module *module)
+{
+ const struct map_private *data = talloc_get_type(ldb_module_get_private(module), struct map_private);
+ return data->context;
+}
+
+/* Create a generic request context. */
+struct map_context *map_init_context(struct ldb_module *module,
+ struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct map_context *ac;
+
+ ldb = ldb_module_get_ctx(module);
+
+ ac = talloc_zero(req, struct map_context);
+ if (ac == NULL) {
+ ldb_set_errstring(ldb, "Out of Memory");
+ return NULL;
+ }
+
+ ac->module = module;
+ ac->req = req;
+
+ return ac;
+}
+
+/* Dealing with DNs for different partitions
+ * ========================================= */
+
+/* Check whether any data should be stored in the local partition. */
+bool map_check_local_db(struct ldb_module *module)
+{
+ const struct ldb_map_context *data = map_get_context(module);
+
+ if (!data->remote_base_dn || !data->local_base_dn) {
+ return false;
+ }
+
+ return true;
+}
+
+/* Copy a DN with the base DN of the local partition. */
+static struct ldb_dn *ldb_dn_rebase_local(void *mem_ctx, const struct ldb_map_context *data, struct ldb_dn *dn)
+{
+ struct ldb_dn *new_dn;
+
+ new_dn = ldb_dn_copy(mem_ctx, dn);
+ if ( ! ldb_dn_validate(new_dn)) {
+ talloc_free(new_dn);
+ return NULL;
+ }
+
+ /* may be we don't need to rebase at all */
+ if ( ! data->remote_base_dn || ! data->local_base_dn) {
+ return new_dn;
+ }
+
+ if ( ! ldb_dn_remove_base_components(new_dn, ldb_dn_get_comp_num(data->remote_base_dn))) {
+ talloc_free(new_dn);
+ return NULL;
+ }
+
+ if ( ! ldb_dn_add_base(new_dn, data->local_base_dn)) {
+ talloc_free(new_dn);
+ return NULL;
+ }
+
+ return new_dn;
+}
+
+/* Copy a DN with the base DN of the remote partition. */
+static struct ldb_dn *ldb_dn_rebase_remote(void *mem_ctx, const struct ldb_map_context *data, struct ldb_dn *dn)
+{
+ struct ldb_dn *new_dn;
+
+ new_dn = ldb_dn_copy(mem_ctx, dn);
+ if ( ! ldb_dn_validate(new_dn)) {
+ talloc_free(new_dn);
+ return NULL;
+ }
+
+ /* may be we don't need to rebase at all */
+ if ( ! data->remote_base_dn || ! data->local_base_dn) {
+ return new_dn;
+ }
+
+ if ( ! ldb_dn_remove_base_components(new_dn, ldb_dn_get_comp_num(data->local_base_dn))) {
+ talloc_free(new_dn);
+ return NULL;
+ }
+
+ if ( ! ldb_dn_add_base(new_dn, data->remote_base_dn)) {
+ talloc_free(new_dn);
+ return NULL;
+ }
+
+ return new_dn;
+}
+
+/* Run a request and make sure it targets the remote partition. */
+/* TODO: free old DNs and messages? */
+int ldb_next_remote_request(struct ldb_module *module, struct ldb_request *request)
+{
+ const struct ldb_map_context *data = map_get_context(module);
+ struct ldb_context *ldb;
+ struct ldb_message *msg;
+
+ ldb = ldb_module_get_ctx(module);
+
+ switch (request->operation) {
+ case LDB_SEARCH:
+ if (request->op.search.base) {
+ request->op.search.base = ldb_dn_rebase_remote(request, data, request->op.search.base);
+ } else {
+ request->op.search.base = data->remote_base_dn;
+ /* TODO: adjust scope? */
+ }
+ break;
+
+ case LDB_ADD:
+ msg = ldb_msg_copy_shallow(request, request->op.add.message);
+ msg->dn = ldb_dn_rebase_remote(msg, data, msg->dn);
+ request->op.add.message = msg;
+ break;
+
+ case LDB_MODIFY:
+ msg = ldb_msg_copy_shallow(request, request->op.mod.message);
+ msg->dn = ldb_dn_rebase_remote(msg, data, msg->dn);
+ request->op.mod.message = msg;
+ break;
+
+ case LDB_DELETE:
+ request->op.del.dn = ldb_dn_rebase_remote(request, data, request->op.del.dn);
+ break;
+
+ case LDB_RENAME:
+ request->op.rename.olddn = ldb_dn_rebase_remote(request, data, request->op.rename.olddn);
+ request->op.rename.newdn = ldb_dn_rebase_remote(request, data, request->op.rename.newdn);
+ break;
+
+ default:
+ ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_map: "
+ "Invalid remote request!\n");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ return ldb_next_request(module, request);
+}
+
+
+/* Finding mappings for attributes and objectClasses
+ * ================================================= */
+
+/* Find an objectClass mapping by the local name. */
+static const struct ldb_map_objectclass *map_objectclass_find_local(const struct ldb_map_context *data, const char *name)
+{
+ int i;
+
+ for (i = 0; data->objectclass_maps && data->objectclass_maps[i].local_name; i++) {
+ if (ldb_attr_cmp(data->objectclass_maps[i].local_name, name) == 0) {
+ return &data->objectclass_maps[i];
+ }
+ }
+
+ return NULL;
+}
+
+/* Find an objectClass mapping by the remote name. */
+static const struct ldb_map_objectclass *map_objectclass_find_remote(const struct ldb_map_context *data, const char *name)
+{
+ int i;
+
+ for (i = 0; data->objectclass_maps && data->objectclass_maps[i].remote_name; i++) {
+ if (ldb_attr_cmp(data->objectclass_maps[i].remote_name, name) == 0) {
+ return &data->objectclass_maps[i];
+ }
+ }
+
+ return NULL;
+}
+
+/* Find an attribute mapping by the local name. */
+const struct ldb_map_attribute *map_attr_find_local(const struct ldb_map_context *data, const char *name)
+{
+ int i;
+
+ for (i = 0; data->attribute_maps[i].local_name; i++) {
+ if (ldb_attr_cmp(data->attribute_maps[i].local_name, name) == 0) {
+ return &data->attribute_maps[i];
+ }
+ }
+ for (i = 0; data->attribute_maps[i].local_name; i++) {
+ if (ldb_attr_cmp(data->attribute_maps[i].local_name, "*") == 0) {
+ return &data->attribute_maps[i];
+ }
+ }
+
+ return NULL;
+}
+
+/* Find an attribute mapping by the remote name. */
+const struct ldb_map_attribute *map_attr_find_remote(const struct ldb_map_context *data, const char *name)
+{
+ const struct ldb_map_attribute *map;
+ const struct ldb_map_attribute *wildcard = NULL;
+ int i, j;
+
+ for (i = 0; data->attribute_maps[i].local_name; i++) {
+ map = &data->attribute_maps[i];
+ if (ldb_attr_cmp(map->local_name, "*") == 0) {
+ wildcard = &data->attribute_maps[i];
+ }
+
+ switch (map->type) {
+ case MAP_IGNORE:
+ break;
+
+ case MAP_KEEP:
+ if (ldb_attr_cmp(map->local_name, name) == 0) {
+ return map;
+ }
+ break;
+
+ case MAP_RENAME:
+ case MAP_CONVERT:
+ if (ldb_attr_cmp(map->u.rename.remote_name, name) == 0) {
+ return map;
+ }
+ break;
+
+ case MAP_GENERATE:
+ for (j = 0; map->u.generate.remote_names && map->u.generate.remote_names[j]; j++) {
+ if (ldb_attr_cmp(map->u.generate.remote_names[j], name) == 0) {
+ return map;
+ }
+ }
+ break;
+ }
+ }
+
+ /* We didn't find it, so return the wildcard record if one was configured */
+ return wildcard;
+}
+
+
+/* Mapping attributes
+ * ================== */
+
+/* Check whether an attribute will be mapped into the remote partition. */
+bool map_attr_check_remote(const struct ldb_map_context *data, const char *attr)
+{
+ const struct ldb_map_attribute *map = map_attr_find_local(data, attr);
+
+ if (map == NULL) {
+ return false;
+ }
+ if (map->type == MAP_IGNORE) {
+ return false;
+ }
+
+ return true;
+}
+
+/* Map an attribute name into the remote partition. */
+const char *map_attr_map_local(void *mem_ctx, const struct ldb_map_attribute *map, const char *attr)
+{
+ if (map == NULL) {
+ return talloc_strdup(mem_ctx, attr);
+ }
+
+ switch (map->type) {
+ case MAP_KEEP:
+ return talloc_strdup(mem_ctx, attr);
+
+ case MAP_RENAME:
+ case MAP_CONVERT:
+ return talloc_strdup(mem_ctx, map->u.rename.remote_name);
+
+ default:
+ return NULL;
+ }
+}
+
+/* Map an attribute name back into the local partition. */
+const char *map_attr_map_remote(void *mem_ctx, const struct ldb_map_attribute *map, const char *attr)
+{
+ if (map == NULL) {
+ return talloc_strdup(mem_ctx, attr);
+ }
+
+ if (map->type == MAP_KEEP) {
+ return talloc_strdup(mem_ctx, attr);
+ }
+
+ return talloc_strdup(mem_ctx, map->local_name);
+}
+
+
+/* Merge two lists of attributes into a single one. */
+int map_attrs_merge(struct ldb_module *module, void *mem_ctx,
+ const char ***attrs, const char * const *more_attrs)
+{
+ int i, j, k;
+
+ for (i = 0; *attrs && (*attrs)[i]; i++) /* noop */ ;
+ for (j = 0; more_attrs && more_attrs[j]; j++) /* noop */ ;
+
+ *attrs = talloc_realloc(mem_ctx, *attrs, const char *, i+j+1);
+ if (*attrs == NULL) {
+ map_oom(module);
+ return -1;
+ }
+
+ for (k = 0; k < j; k++) {
+ (*attrs)[i + k] = more_attrs[k];
+ }
+
+ (*attrs)[i+k] = NULL;
+
+ return 0;
+}
+
+/* Mapping ldb values
+ * ================== */
+
+/* Map an ldb value into the remote partition. */
+struct ldb_val ldb_val_map_local(struct ldb_module *module, void *mem_ctx,
+ const struct ldb_map_attribute *map, const struct ldb_val *val)
+{
+ if (map && (map->type == MAP_CONVERT) && (map->u.convert.convert_local)) {
+ return map->u.convert.convert_local(module, mem_ctx, val);
+ }
+
+ return ldb_val_dup(mem_ctx, val);
+}
+
+/* Map an ldb value back into the local partition. */
+struct ldb_val ldb_val_map_remote(struct ldb_module *module, void *mem_ctx,
+ const struct ldb_map_attribute *map, const struct ldb_val *val)
+{
+ if (map && (map->type == MAP_CONVERT) && (map->u.convert.convert_remote)) {
+ return map->u.convert.convert_remote(module, mem_ctx, val);
+ }
+
+ return ldb_val_dup(mem_ctx, val);
+}
+
+
+/* Mapping DNs
+ * =========== */
+
+/* Check whether a DN is below the local baseDN. */
+bool ldb_dn_check_local(struct ldb_module *module, struct ldb_dn *dn)
+{
+ const struct ldb_map_context *data = map_get_context(module);
+
+ if (!data->local_base_dn) {
+ return true;
+ }
+
+ return ldb_dn_compare_base(data->local_base_dn, dn) == 0;
+}
+
+/* Map a DN into the remote partition. */
+struct ldb_dn *ldb_dn_map_local(struct ldb_module *module, void *mem_ctx, struct ldb_dn *dn)
+{
+ const struct ldb_map_context *data = map_get_context(module);
+ struct ldb_context *ldb;
+ struct ldb_dn *newdn;
+ const struct ldb_map_attribute *map;
+ enum ldb_map_attr_type map_type;
+ const char *name;
+ struct ldb_val value;
+ int i, ret;
+
+ if (dn == NULL) {
+ return NULL;
+ }
+
+ ldb = ldb_module_get_ctx(module);
+
+ newdn = ldb_dn_copy(mem_ctx, dn);
+ if (newdn == NULL) {
+ map_oom(module);
+ return NULL;
+ }
+
+ /* For each RDN, map the component name and possibly the value */
+ for (i = 0; i < ldb_dn_get_comp_num(newdn); i++) {
+ map = map_attr_find_local(data, ldb_dn_get_component_name(dn, i));
+
+ /* Unknown attribute - leave this RDN as is and hope the best... */
+ if (map == NULL) {
+ map_type = MAP_KEEP;
+ } else {
+ map_type = map->type;
+ }
+
+ switch (map_type) {
+ case MAP_IGNORE:
+ case MAP_GENERATE:
+ ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_map: "
+ "MAP_IGNORE/MAP_GENERATE attribute '%s' "
+ "used in DN!\n", ldb_dn_get_component_name(dn, i));
+ goto failed;
+
+ case MAP_CONVERT:
+ if (map->u.convert.convert_local == NULL) {
+ ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_map: "
+ "'convert_local' not set for attribute '%s' "
+ "used in DN!\n", ldb_dn_get_component_name(dn, i));
+ goto failed;
+ }
+ /* fall through */
+ case MAP_KEEP:
+ case MAP_RENAME:
+ name = map_attr_map_local(newdn, map, ldb_dn_get_component_name(dn, i));
+ if (name == NULL) goto failed;
+
+ value = ldb_val_map_local(module, newdn, map, ldb_dn_get_component_val(dn, i));
+ if (value.data == NULL) goto failed;
+
+ ret = ldb_dn_set_component(newdn, i, name, value);
+ if (ret != LDB_SUCCESS) {
+ goto failed;
+ }
+
+ break;
+ }
+ }
+
+ return newdn;
+
+failed:
+ talloc_free(newdn);
+ return NULL;
+}
+
+/* Map a DN into the local partition. */
+struct ldb_dn *ldb_dn_map_remote(struct ldb_module *module, void *mem_ctx, struct ldb_dn *dn)
+{
+ const struct ldb_map_context *data = map_get_context(module);
+ struct ldb_context *ldb;
+ struct ldb_dn *newdn;
+ const struct ldb_map_attribute *map;
+ enum ldb_map_attr_type map_type;
+ const char *name;
+ struct ldb_val value;
+ int i, ret;
+
+ if (dn == NULL) {
+ return NULL;
+ }
+
+ ldb = ldb_module_get_ctx(module);
+
+ newdn = ldb_dn_copy(mem_ctx, dn);
+ if (newdn == NULL) {
+ map_oom(module);
+ return NULL;
+ }
+
+ /* For each RDN, map the component name and possibly the value */
+ for (i = 0; i < ldb_dn_get_comp_num(newdn); i++) {
+ map = map_attr_find_remote(data, ldb_dn_get_component_name(dn, i));
+
+ /* Unknown attribute - leave this RDN as is and hope the best... */
+ if (map == NULL) {
+ map_type = MAP_KEEP;
+ } else {
+ map_type = map->type;
+ }
+
+ switch (map_type) {
+ case MAP_IGNORE:
+ case MAP_GENERATE:
+ ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_map: "
+ "MAP_IGNORE/MAP_GENERATE attribute '%s' "
+ "used in DN!\n", ldb_dn_get_component_name(dn, i));
+ goto failed;
+
+ case MAP_CONVERT:
+ if (map->u.convert.convert_remote == NULL) {
+ ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_map: "
+ "'convert_remote' not set for attribute '%s' "
+ "used in DN!\n", ldb_dn_get_component_name(dn, i));
+ goto failed;
+ }
+ /* fall through */
+ case MAP_KEEP:
+ case MAP_RENAME:
+ name = map_attr_map_remote(newdn, map, ldb_dn_get_component_name(dn, i));
+ if (name == NULL) goto failed;
+
+ value = ldb_val_map_remote(module, newdn, map, ldb_dn_get_component_val(dn, i));
+ if (value.data == NULL) goto failed;
+
+ ret = ldb_dn_set_component(newdn, i, name, value);
+ if (ret != LDB_SUCCESS) {
+ goto failed;
+ }
+
+ break;
+ }
+ }
+
+ return newdn;
+
+failed:
+ talloc_free(newdn);
+ return NULL;
+}
+
+/* Map a DN and its base into the local partition. */
+/* TODO: This should not be required with GUIDs. */
+struct ldb_dn *ldb_dn_map_rebase_remote(struct ldb_module *module, void *mem_ctx, struct ldb_dn *dn)
+{
+ const struct ldb_map_context *data = map_get_context(module);
+ struct ldb_dn *dn1, *dn2;
+
+ dn1 = ldb_dn_rebase_local(mem_ctx, data, dn);
+ dn2 = ldb_dn_map_remote(module, mem_ctx, dn1);
+
+ talloc_free(dn1);
+ return dn2;
+}
+
+
+/* Converting DNs and objectClasses (as ldb values)
+ * ================================================ */
+
+/* Map a DN contained in an ldb value into the remote partition. */
+static struct ldb_val ldb_dn_convert_local(struct ldb_module *module, void *mem_ctx, const struct ldb_val *val)
+{
+ struct ldb_context *ldb;
+ struct ldb_dn *dn, *newdn;
+ struct ldb_val newval;
+
+ ldb = ldb_module_get_ctx(module);
+
+ dn = ldb_dn_from_ldb_val(mem_ctx, ldb, val);
+ if (! ldb_dn_validate(dn)) {
+ newval.length = 0;
+ newval.data = NULL;
+ talloc_free(dn);
+ return newval;
+ }
+ newdn = ldb_dn_map_local(module, mem_ctx, dn);
+ talloc_free(dn);
+
+ newval.length = 0;
+ newval.data = (uint8_t *)ldb_dn_alloc_linearized(mem_ctx, newdn);
+ if (newval.data) {
+ newval.length = strlen((char *)newval.data);
+ }
+ talloc_free(newdn);
+
+ return newval;
+}
+
+/* Map a DN contained in an ldb value into the local partition. */
+static struct ldb_val ldb_dn_convert_remote(struct ldb_module *module, void *mem_ctx, const struct ldb_val *val)
+{
+ struct ldb_context *ldb;
+ struct ldb_dn *dn, *newdn;
+ struct ldb_val newval;
+
+ ldb = ldb_module_get_ctx(module);
+
+ dn = ldb_dn_from_ldb_val(mem_ctx, ldb, val);
+ if (! ldb_dn_validate(dn)) {
+ newval.length = 0;
+ newval.data = NULL;
+ talloc_free(dn);
+ return newval;
+ }
+ newdn = ldb_dn_map_remote(module, mem_ctx, dn);
+ talloc_free(dn);
+
+ newval.length = 0;
+ newval.data = (uint8_t *)ldb_dn_alloc_linearized(mem_ctx, newdn);
+ if (newval.data) {
+ newval.length = strlen((char *)newval.data);
+ }
+ talloc_free(newdn);
+
+ return newval;
+}
+
+/* Map an objectClass into the remote partition. */
+static struct ldb_val map_objectclass_convert_local(struct ldb_module *module, void *mem_ctx, const struct ldb_val *val)
+{
+ const struct ldb_map_context *data = map_get_context(module);
+ const char *name = (char *)val->data;
+ const struct ldb_map_objectclass *map = map_objectclass_find_local(data, name);
+ struct ldb_val newval;
+
+ if (map) {
+ newval.data = (uint8_t*)talloc_strdup(mem_ctx, map->remote_name);
+ newval.length = strlen((char *)newval.data);
+ return newval;
+ }
+
+ return ldb_val_dup(mem_ctx, val);
+}
+
+/* Generate a remote message with a mapped objectClass. */
+static void map_objectclass_generate_remote(struct ldb_module *module, const char *local_attr, const struct ldb_message *old, struct ldb_message *remote, struct ldb_message *local)
+{
+ const struct ldb_map_context *data = map_get_context(module);
+ struct ldb_context *ldb;
+ struct ldb_message_element *el, *oc;
+ struct ldb_val val;
+ bool found_extensibleObject = false;
+ int i;
+
+ ldb = ldb_module_get_ctx(module);
+
+ /* Find old local objectClass */
+ oc = ldb_msg_find_element(old, "objectClass");
+ if (oc == NULL) {
+ return;
+ }
+
+ /* Prepare new element */
+ el = talloc_zero(remote, struct ldb_message_element);
+ if (el == NULL) {
+ ldb_oom(ldb);
+ return; /* TODO: fail? */
+ }
+
+ /* Copy local objectClass element, reverse space for an extra value */
+ el->num_values = oc->num_values + 1;
+ el->values = talloc_array(el, struct ldb_val, el->num_values);
+ if (el->values == NULL) {
+ talloc_free(el);
+ ldb_oom(ldb);
+ return; /* TODO: fail? */
+ }
+
+ /* Copy local element name "objectClass" */
+ el->name = talloc_strdup(el, local_attr);
+
+ /* Convert all local objectClasses */
+ for (i = 0; i < el->num_values - 1; i++) {
+ el->values[i] = map_objectclass_convert_local(module, el->values, &oc->values[i]);
+ if (ldb_attr_cmp((char *)el->values[i].data, data->add_objectclass) == 0) {
+ found_extensibleObject = true;
+ }
+ }
+
+ if (!found_extensibleObject) {
+ val.data = (uint8_t *)talloc_strdup(el->values, data->add_objectclass);
+ val.length = strlen((char *)val.data);
+
+ /* Append additional objectClass data->add_objectclass */
+ el->values[i] = val;
+ } else {
+ el->num_values--;
+ }
+
+ /* Add new objectClass to remote message */
+ ldb_msg_add(remote, el, 0);
+}
+
+/* Map an objectClass into the local partition. */
+static struct ldb_val map_objectclass_convert_remote(struct ldb_module *module, void *mem_ctx, const struct ldb_val *val)
+{
+ const struct ldb_map_context *data = map_get_context(module);
+ const char *name = (char *)val->data;
+ const struct ldb_map_objectclass *map = map_objectclass_find_remote(data, name);
+ struct ldb_val newval;
+
+ if (map) {
+ newval.data = (uint8_t*)talloc_strdup(mem_ctx, map->local_name);
+ newval.length = strlen((char *)newval.data);
+ return newval;
+ }
+
+ return ldb_val_dup(mem_ctx, val);
+}
+
+/* Generate a local message with a mapped objectClass. */
+static struct ldb_message_element *map_objectclass_generate_local(struct ldb_module *module, void *mem_ctx, const char *local_attr, const struct ldb_message *remote)
+{
+ const struct ldb_map_context *data = map_get_context(module);
+ struct ldb_context *ldb;
+ struct ldb_message_element *el, *oc;
+ struct ldb_val val;
+ int i;
+
+ ldb = ldb_module_get_ctx(module);
+
+ /* Find old remote objectClass */
+ oc = ldb_msg_find_element(remote, "objectClass");
+ if (oc == NULL) {
+ return NULL;
+ }
+
+ /* Prepare new element */
+ el = talloc_zero(mem_ctx, struct ldb_message_element);
+ if (el == NULL) {
+ ldb_oom(ldb);
+ return NULL;
+ }
+
+ /* Copy remote objectClass element */
+ el->num_values = oc->num_values;
+ el->values = talloc_array(el, struct ldb_val, el->num_values);
+ if (el->values == NULL) {
+ talloc_free(el);
+ ldb_oom(ldb);
+ return NULL;
+ }
+
+ /* Copy remote element name "objectClass" */
+ el->name = talloc_strdup(el, local_attr);
+
+ /* Convert all remote objectClasses */
+ for (i = 0; i < el->num_values; i++) {
+ el->values[i] = map_objectclass_convert_remote(module, el->values, &oc->values[i]);
+ }
+
+ val.data = (uint8_t *)talloc_strdup(el->values, data->add_objectclass);
+ val.length = strlen((char *)val.data);
+
+ /* Remove last value if it was the string in data->add_objectclass (eg samba4top, extensibleObject) */
+ if (ldb_val_equal_exact(&val, &el->values[i-1])) {
+ el->num_values--;
+ el->values = talloc_realloc(el, el->values, struct ldb_val, el->num_values);
+ if (el->values == NULL) {
+ talloc_free(el);
+ ldb_oom(ldb);
+ return NULL;
+ }
+ }
+
+ return el;
+}
+
+static const struct ldb_map_attribute objectclass_convert_map = {
+ .local_name = "objectClass",
+ .type = MAP_CONVERT,
+ .u = {
+ .convert = {
+ .remote_name = "objectClass",
+ .convert_local = map_objectclass_convert_local,
+ .convert_remote = map_objectclass_convert_remote,
+ },
+ },
+};
+
+
+/* Mappings for searches on objectClass= assuming a one-to-one
+ * mapping. Needed because this is a generate operator for the
+ * add/modify code */
+static int map_objectclass_convert_operator(struct ldb_module *module, void *mem_ctx,
+ struct ldb_parse_tree **new, const struct ldb_parse_tree *tree)
+{
+
+ return map_subtree_collect_remote_simple(module, mem_ctx, new, tree, &objectclass_convert_map);
+}
+
+/* Auxiliary request construction
+ * ============================== */
+
+/* Build a request to search a record by its DN. */
+struct ldb_request *map_search_base_req(struct map_context *ac, struct ldb_dn *dn, const char * const *attrs, const struct ldb_parse_tree *tree, void *context, ldb_map_callback_t callback)
+{
+ const struct ldb_parse_tree *search_tree;
+ struct ldb_context *ldb;
+ struct ldb_request *req;
+ int ret;
+
+ ldb = ldb_module_get_ctx(ac->module);
+
+ if (tree) {
+ search_tree = tree;
+ } else {
+ search_tree = ldb_parse_tree(ac, NULL);
+ if (search_tree == NULL) {
+ return NULL;
+ }
+ }
+
+ ret = ldb_build_search_req_ex(&req, ldb, ac,
+ dn, LDB_SCOPE_BASE,
+ search_tree, attrs,
+ NULL,
+ context, callback,
+ ac->req);
+ if (ret != LDB_SUCCESS) {
+ return NULL;
+ }
+
+ return req;
+}
+
+/* Build a request to update the 'IS_MAPPED' attribute */
+struct ldb_request *map_build_fixup_req(struct map_context *ac,
+ struct ldb_dn *olddn,
+ struct ldb_dn *newdn,
+ void *context,
+ ldb_map_callback_t callback)
+{
+ struct ldb_context *ldb;
+ struct ldb_request *req;
+ struct ldb_message *msg;
+ const char *dn;
+ int ret;
+
+ ldb = ldb_module_get_ctx(ac->module);
+
+ /* Prepare message */
+ msg = ldb_msg_new(ac);
+ if (msg == NULL) {
+ map_oom(ac->module);
+ return NULL;
+ }
+
+ /* Update local 'IS_MAPPED' to the new remote DN */
+ msg->dn = ldb_dn_copy(msg, olddn);
+ dn = ldb_dn_alloc_linearized(msg, newdn);
+ if ( ! dn || ! ldb_dn_validate(msg->dn)) {
+ goto failed;
+ }
+ if (ldb_msg_add_empty(msg, IS_MAPPED, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
+ goto failed;
+ }
+ if (ldb_msg_add_string(msg, IS_MAPPED, dn) != 0) {
+ goto failed;
+ }
+
+ /* Prepare request */
+ ret = ldb_build_mod_req(&req, ldb,
+ ac, msg, NULL,
+ context, callback,
+ ac->req);
+ if (ret != LDB_SUCCESS) {
+ goto failed;
+ }
+ talloc_steal(req, msg);
+
+ return req;
+failed:
+ talloc_free(msg);
+ return NULL;
+}
+
+/* Module initialization
+ * ===================== */
+
+
+/* Builtin mappings for DNs and objectClasses */
+static const struct ldb_map_attribute builtin_attribute_maps[] = {
+ {
+ .local_name = "dn",
+ .type = MAP_CONVERT,
+ .u = {
+ .convert = {
+ .remote_name = "dn",
+ .convert_local = ldb_dn_convert_local,
+ .convert_remote = ldb_dn_convert_remote,
+ },
+ },
+ },
+ {
+ .local_name = NULL,
+ }
+};
+
+static const struct ldb_map_attribute objectclass_attribute_map = {
+ .local_name = "objectClass",
+ .type = MAP_GENERATE,
+ .convert_operator = map_objectclass_convert_operator,
+ .u = {
+ .generate = {
+ .remote_names = { "objectClass", NULL },
+ .generate_local = map_objectclass_generate_local,
+ .generate_remote = map_objectclass_generate_remote,
+ },
+ },
+};
+
+
+/* Find the special 'MAP_DN_NAME' record and store local and remote
+ * base DNs in private data. */
+static int map_init_dns(struct ldb_module *module, struct ldb_map_context *data, const char *name)
+{
+ static const char * const attrs[] = { MAP_DN_FROM, MAP_DN_TO, NULL };
+ struct ldb_context *ldb;
+ struct ldb_dn *dn;
+ struct ldb_message *msg;
+ struct ldb_result *res;
+ int ret;
+
+ if (!name) {
+ data->local_base_dn = NULL;
+ data->remote_base_dn = NULL;
+ return LDB_SUCCESS;
+ }
+
+ ldb = ldb_module_get_ctx(module);
+
+ dn = ldb_dn_new_fmt(data, ldb, "%s=%s", MAP_DN_NAME, name);
+ if ( ! ldb_dn_validate(dn)) {
+ ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_map: "
+ "Failed to construct '%s' DN!\n", MAP_DN_NAME);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ldb_search(ldb, data, &res, dn, LDB_SCOPE_BASE, attrs, NULL);
+ talloc_free(dn);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ if (res->count == 0) {
+ ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_map: "
+ "No results for '%s=%s'!\n", MAP_DN_NAME, name);
+ talloc_free(res);
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+ if (res->count > 1) {
+ ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_map: "
+ "Too many results for '%s=%s'!\n", MAP_DN_NAME, name);
+ talloc_free(res);
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+
+ msg = res->msgs[0];
+ data->local_base_dn = ldb_msg_find_attr_as_dn(ldb, data, msg, MAP_DN_FROM);
+ data->remote_base_dn = ldb_msg_find_attr_as_dn(ldb, data, msg, MAP_DN_TO);
+ talloc_free(res);
+
+ return LDB_SUCCESS;
+}
+
+/* Store attribute maps and objectClass maps in private data. */
+static int map_init_maps(struct ldb_module *module, struct ldb_map_context *data,
+ const struct ldb_map_attribute *attrs,
+ const struct ldb_map_objectclass *ocls,
+ const char * const *wildcard_attributes)
+{
+ int i, j, last;
+ last = 0;
+
+ /* Count specified attribute maps */
+ for (i = 0; attrs[i].local_name; i++) /* noop */ ;
+ /* Count built-in attribute maps */
+ for (j = 0; builtin_attribute_maps[j].local_name; j++) /* noop */ ;
+
+ /* Store list of attribute maps */
+ data->attribute_maps = talloc_array(data, struct ldb_map_attribute, i+j+2);
+ if (data->attribute_maps == NULL) {
+ map_oom(module);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* Specified ones go first */
+ for (i = 0; attrs[i].local_name; i++) {
+ data->attribute_maps[last] = attrs[i];
+ last++;
+ }
+
+ /* Built-in ones go last */
+ for (i = 0; builtin_attribute_maps[i].local_name; i++) {
+ data->attribute_maps[last] = builtin_attribute_maps[i];
+ last++;
+ }
+
+ if (data->add_objectclass) {
+ /* ObjectClass one is very last, if required */
+ data->attribute_maps[last] = objectclass_attribute_map;
+ last++;
+ } else if (ocls) {
+ data->attribute_maps[last] = objectclass_convert_map;
+ last++;
+ }
+
+ /* Ensure 'local_name == NULL' for the last entry */
+ memset(&data->attribute_maps[last], 0, sizeof(struct ldb_map_attribute));
+
+ /* Store list of objectClass maps */
+ data->objectclass_maps = ocls;
+
+ data->wildcard_attributes = wildcard_attributes;
+
+ return LDB_SUCCESS;
+}
+
+/* Initialize global private data. */
+_PUBLIC_ int ldb_map_init(struct ldb_module *module, const struct ldb_map_attribute *attrs,
+ const struct ldb_map_objectclass *ocls,
+ const char * const *wildcard_attributes,
+ const char *add_objectclass,
+ const char *name)
+{
+ struct map_private *data;
+ int ret;
+
+ /* Prepare private data */
+ data = talloc_zero(module, struct map_private);
+ if (data == NULL) {
+ map_oom(module);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ldb_module_set_private(module, data);
+
+ data->context = talloc_zero(data, struct ldb_map_context);
+ if (!data->context) {
+ map_oom(module);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* Store local and remote baseDNs */
+ ret = map_init_dns(module, data->context, name);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(data);
+ return ret;
+ }
+
+ data->context->add_objectclass = add_objectclass;
+
+ /* Store list of attribute and objectClass maps */
+ ret = map_init_maps(module, data->context, attrs, ocls, wildcard_attributes);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(data);
+ return ret;
+ }
+
+ return LDB_SUCCESS;
+}
diff --git a/source4/lib/ldb/ldb_map/ldb_map.h b/source4/lib/ldb/ldb_map/ldb_map.h
new file mode 100644
index 0000000000..3c1fe80895
--- /dev/null
+++ b/source4/lib/ldb/ldb_map/ldb_map.h
@@ -0,0 +1,173 @@
+/*
+ ldb database mapping module
+
+ Copyright (C) Jelmer Vernooij 2005
+ Copyright (C) Martin Kuehl <mkhl@samba.org> 2006
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#ifndef __LDB_MAP_H__
+#define __LDB_MAP_H__
+
+#include "ldb_module.h"
+
+/* ldb_map is a skeleton LDB module that can be used for any other modules
+ * that need to map attributes.
+ *
+ * The term 'remote' in this header refers to the connection where the
+ * original schema is used on while 'local' means the local connection
+ * that any upper layers will use.
+ *
+ * All local attributes will have to have a definition. Not all remote
+ * attributes need a definition as LDB is a lot less strict than LDAP
+ * (in other words, sending unknown attributes to an LDAP server hurts us,
+ * while returning too many attributes in ldb_search() doesn't)
+ */
+
+
+/* Name of the internal attribute pointing from the local to the
+ * remote part of a record */
+#define IS_MAPPED "isMapped"
+
+
+struct ldb_map_context;
+
+/* convert a local ldb_val to a remote ldb_val */
+typedef struct ldb_val (*ldb_map_convert_func) (struct ldb_module *module, void *mem_ctx, const struct ldb_val *val);
+
+#define LDB_MAP_MAX_REMOTE_NAMES 10
+
+/* map from local to remote attribute */
+struct ldb_map_attribute {
+ const char *local_name; /* local name */
+
+ enum ldb_map_attr_type {
+ MAP_IGNORE, /* Ignore this local attribute. Doesn't exist remotely. */
+ MAP_KEEP, /* Keep as is. Same name locally and remotely. */
+ MAP_RENAME, /* Simply rename the attribute. Name changes, data is the same */
+ MAP_CONVERT, /* Rename + convert data */
+ MAP_GENERATE /* Use generate function for generating new name/data.
+ Used for generating attributes based on
+ multiple remote attributes. */
+ } type;
+
+ /* if set, will be called for search expressions that contain this attribute */
+ int (*convert_operator)(struct ldb_module *, TALLOC_CTX *ctx, struct ldb_parse_tree **ntree, const struct ldb_parse_tree *otree);
+
+ union {
+ struct {
+ const char *remote_name;
+ } rename;
+
+ struct {
+ const char *remote_name;
+
+ /* Convert local to remote data */
+ ldb_map_convert_func convert_local;
+
+ /* Convert remote to local data */
+ /* an entry can have convert_remote set to NULL, as long as there as an entry with the same local_name
+ * that is non-NULL before it. */
+ ldb_map_convert_func convert_remote;
+ } convert;
+
+ struct {
+ /* Generate the local attribute from remote message */
+ struct ldb_message_element *(*generate_local)(struct ldb_module *, TALLOC_CTX *mem_ctx, const char *remote_attr, const struct ldb_message *remote);
+
+ /* Update remote message with information from local message */
+ void (*generate_remote)(struct ldb_module *, const char *local_attr, const struct ldb_message *old, struct ldb_message *remote, struct ldb_message *local);
+
+ /* Name(s) for this attribute on the remote server. This is an array since
+ * one local attribute's data can be split up into several attributes
+ * remotely */
+ const char *remote_names[LDB_MAP_MAX_REMOTE_NAMES];
+
+ /* Names of additional remote attributes
+ * required for the generation. NULL
+ * indicates that `local_attr' suffices. */
+ /*
+#define LDB_MAP_MAX_SELF_ATTRIBUTES 10
+ const char *self_attrs[LDB_MAP_MAX_SELF_ATTRIBUTES];
+ */
+ } generate;
+ } u;
+};
+
+
+#define LDB_MAP_MAX_SUBCLASSES 10
+#define LDB_MAP_MAX_MUSTS 10
+#define LDB_MAP_MAX_MAYS 50
+
+/* map from local to remote objectClass */
+struct ldb_map_objectclass {
+ const char *local_name;
+ const char *remote_name;
+ const char *base_classes[LDB_MAP_MAX_SUBCLASSES];
+ const char *musts[LDB_MAP_MAX_MUSTS];
+ const char *mays[LDB_MAP_MAX_MAYS];
+};
+
+
+/* private context data */
+struct ldb_map_context {
+ struct ldb_map_attribute *attribute_maps;
+ /* NOTE: Always declare base classes first here */
+ const struct ldb_map_objectclass *objectclass_maps;
+
+ /* Remote (often operational) attributes that should be added
+ * to any wildcard search */
+ const char * const *wildcard_attributes;
+
+ /* ObjectClass (if any) to be added to remote attributes on add */
+ const char *add_objectclass;
+
+ /* struct ldb_context *mapped_ldb; */
+ struct ldb_dn *local_base_dn;
+ struct ldb_dn *remote_base_dn;
+};
+
+/* Global private data */
+struct map_private {
+ void *caller_private;
+ struct ldb_map_context *context;
+};
+
+/* Initialize global private data. */
+int ldb_map_init(struct ldb_module *module, const struct ldb_map_attribute *attrs,
+ const struct ldb_map_objectclass *ocls,
+ const char * const *wildcard_attributes,
+ const char *add_objectclass,
+ const char *name);
+
+int map_add(struct ldb_module *module, struct ldb_request *req);
+int map_search(struct ldb_module *module, struct ldb_request *req);
+int map_rename(struct ldb_module *module, struct ldb_request *req);
+int map_delete(struct ldb_module *module, struct ldb_request *req);
+int map_modify(struct ldb_module *module, struct ldb_request *req);
+
+#define LDB_MAP_OPS \
+ .add = map_add, \
+ .modify = map_modify, \
+ .del = map_delete, \
+ .rename = map_rename, \
+ .search = map_search,
+
+#endif /* __LDB_MAP_H__ */
diff --git a/source4/lib/ldb/ldb_map/ldb_map_inbound.c b/source4/lib/ldb/ldb_map/ldb_map_inbound.c
new file mode 100644
index 0000000000..455740ce59
--- /dev/null
+++ b/source4/lib/ldb/ldb_map/ldb_map_inbound.c
@@ -0,0 +1,826 @@
+/*
+ ldb database mapping module
+
+ Copyright (C) Jelmer Vernooij 2005
+ Copyright (C) Martin Kuehl <mkhl@samba.org> 2006
+ Copyright (C) Simo Sorce <idra@samba.org> 2008
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "ldb_includes.h"
+#include "ldb_map.h"
+#include "ldb_map_private.h"
+
+
+/* Mapping message elements
+ * ======================== */
+
+/* Map a message element into the remote partition. */
+static struct ldb_message_element *ldb_msg_el_map_local(struct ldb_module *module, void *mem_ctx, const struct ldb_map_attribute *map, const struct ldb_message_element *old)
+{
+ struct ldb_message_element *el;
+ int i;
+
+ el = talloc_zero(mem_ctx, struct ldb_message_element);
+ if (el == NULL) {
+ map_oom(module);
+ return NULL;
+ }
+
+ el->num_values = old->num_values;
+ el->values = talloc_array(el, struct ldb_val, el->num_values);
+ if (el->values == NULL) {
+ talloc_free(el);
+ map_oom(module);
+ return NULL;
+ }
+
+ el->name = map_attr_map_local(el, map, old->name);
+
+ for (i = 0; i < el->num_values; i++) {
+ el->values[i] = ldb_val_map_local(module, el->values, map, &old->values[i]);
+ }
+
+ return el;
+}
+
+/* Add a message element either to a local or to a remote message,
+ * depending on whether it goes into the local or remote partition. */
+static int ldb_msg_el_partition(struct ldb_module *module, struct ldb_message *local, struct ldb_message *remote, const struct ldb_message *msg, const char *attr_name, /* const char * const names[], */ const struct ldb_message_element *old)
+{
+ const struct ldb_map_context *data = map_get_context(module);
+ const struct ldb_map_attribute *map = map_attr_find_local(data, attr_name);
+ struct ldb_message_element *el=NULL;
+ struct ldb_context *ldb = ldb_module_get_ctx(module);
+
+ /* Unknown attribute: ignore */
+ if (map == NULL) {
+ ldb_debug(ldb, LDB_DEBUG_WARNING, "ldb_map: "
+ "Not mapping attribute '%s': no mapping found\n",
+ old->name);
+ goto local;
+ }
+
+ switch (map->type) {
+ case MAP_IGNORE:
+ goto local;
+
+ case MAP_CONVERT:
+ if (map->u.convert.convert_local == NULL) {
+ ldb_debug(ldb, LDB_DEBUG_WARNING, "ldb_map: "
+ "Not mapping attribute '%s': "
+ "'convert_local' not set\n",
+ map->local_name);
+ goto local;
+ }
+ /* fall through */
+ case MAP_KEEP:
+ case MAP_RENAME:
+ el = ldb_msg_el_map_local(module, remote, map, old);
+ break;
+
+ case MAP_GENERATE:
+ if (map->u.generate.generate_remote == NULL) {
+ ldb_debug(ldb, LDB_DEBUG_WARNING, "ldb_map: "
+ "Not mapping attribute '%s': "
+ "'generate_remote' not set\n",
+ map->local_name);
+ goto local;
+ }
+
+ /* TODO: if this attr requires context:
+ * make sure all context attrs are mappable (in 'names')
+ * make sure all context attrs have already been mapped?
+ * maybe postpone generation until they have been mapped?
+ */
+
+ map->u.generate.generate_remote(module, map->local_name, msg, remote, local);
+ return 0;
+ }
+
+ if (el == NULL) {
+ return -1;
+ }
+
+ return ldb_msg_add(remote, el, old->flags);
+
+local:
+ el = talloc(local, struct ldb_message_element);
+ if (el == NULL) {
+ map_oom(module);
+ return -1;
+ }
+
+ *el = *old; /* copy the old element */
+
+ return ldb_msg_add(local, el, old->flags);
+}
+
+/* Mapping messages
+ * ================ */
+
+/* Check whether a message will be (partially) mapped into the remote partition. */
+static bool ldb_msg_check_remote(struct ldb_module *module, const struct ldb_message *msg)
+{
+ const struct ldb_map_context *data = map_get_context(module);
+ bool ret;
+ int i;
+
+ for (i = 0; i < msg->num_elements; i++) {
+ ret = map_attr_check_remote(data, msg->elements[i].name);
+ if (ret) {
+ return ret;
+ }
+ }
+
+ return false;
+}
+
+/* Split message elements that stay in the local partition from those
+ * that are mapped into the remote partition. */
+static int ldb_msg_partition(struct ldb_module *module, struct ldb_message *local, struct ldb_message *remote, const struct ldb_message *msg)
+{
+ /* const char * const names[]; */
+ struct ldb_context *ldb;
+ int i, ret;
+
+ ldb = ldb_module_get_ctx(module);
+
+ for (i = 0; i < msg->num_elements; i++) {
+ /* Skip 'IS_MAPPED' */
+ if (ldb_attr_cmp(msg->elements[i].name, IS_MAPPED) == 0) {
+ ldb_debug(ldb, LDB_DEBUG_WARNING, "ldb_map: "
+ "Skipping attribute '%s'\n",
+ msg->elements[i].name);
+ continue;
+ }
+
+ ret = ldb_msg_el_partition(module, local, remote, msg, msg->elements[i].name, &msg->elements[i]);
+ if (ret) {
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+
+static int map_add_do_local(struct map_context *ac);
+static int map_modify_do_local(struct map_context *ac);
+static int map_delete_do_local(struct map_context *ac);
+static int map_rename_do_local(struct map_context *ac);
+static int map_rename_do_fixup(struct map_context *ac);
+static int map_rename_local_callback(struct ldb_request *req,
+ struct ldb_reply *ares);
+
+
+/*****************************************************************************
+ * COMMON INBOUND functions
+*****************************************************************************/
+
+/* Store the DN of a single search result in context. */
+static int map_search_self_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+ struct ldb_context *ldb;
+ struct map_context *ac;
+ int ret;
+
+ ac = talloc_get_type(req->context, struct map_context);
+ ldb = ldb_module_get_ctx(ac->module);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ /* We are interested only in the single reply */
+ switch(ares->type) {
+ case LDB_REPLY_ENTRY:
+ /* We have already found a remote DN */
+ if (ac->local_dn) {
+ ldb_set_errstring(ldb,
+ "Too many results!");
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ /* Store local DN */
+ ac->local_dn = talloc_steal(ac, ares->message->dn);
+ break;
+
+ case LDB_REPLY_DONE:
+
+ switch (ac->req->operation) {
+ case LDB_MODIFY:
+ ret = map_modify_do_local(ac);
+ break;
+ case LDB_DELETE:
+ ret = map_delete_do_local(ac);
+ break;
+ case LDB_RENAME:
+ ret = map_rename_do_local(ac);
+ break;
+ default:
+ /* if we get here we have definitely a problem */
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ }
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ default:
+ /* ignore referrals */
+ break;
+ }
+
+ talloc_free(ares);
+ return LDB_SUCCESS;
+}
+
+/* Build a request to search the local record by its DN. */
+static int map_search_self_req(struct ldb_request **req,
+ struct map_context *ac,
+ struct ldb_dn *dn)
+{
+ /* attrs[] is returned from this function in
+ * ac->search_req->op.search.attrs, so it must be static, as
+ * otherwise the compiler can put it on the stack */
+ static const char * const attrs[] = { IS_MAPPED, NULL };
+ struct ldb_parse_tree *tree;
+
+ /* Limit search to records with 'IS_MAPPED' present */
+ tree = ldb_parse_tree(ac, "(" IS_MAPPED "=*)");
+ if (tree == NULL) {
+ map_oom(ac->module);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ *req = map_search_base_req(ac, dn, attrs, tree,
+ ac, map_search_self_callback);
+ if (*req == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ return LDB_SUCCESS;
+}
+
+static int map_op_local_callback(struct ldb_request *req,
+ struct ldb_reply *ares)
+{
+ struct ldb_context *ldb;
+ struct map_context *ac;
+ int ret;
+
+ ac = talloc_get_type(req->context, struct map_context);
+ ldb = ldb_module_get_ctx(ac->module);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ if (ares->type != LDB_REPLY_DONE) {
+ ldb_set_errstring(ldb, "Invalid reply type!");
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ /* Do the remote request. */
+ ret = ldb_next_remote_request(ac->module, ac->remote_req);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ return LDB_SUCCESS;
+}
+
+static int map_op_remote_callback(struct ldb_request *req,
+ struct ldb_reply *ares)
+{
+ struct ldb_context *ldb;
+ struct map_context *ac;
+
+ ac = talloc_get_type(req->context, struct map_context);
+ ldb = ldb_module_get_ctx(ac->module);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ if (ares->type != LDB_REPLY_DONE) {
+ ldb_set_errstring(ldb, "Invalid reply type!");
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+}
+
+
+/*****************************************************************************
+ * ADD operations
+*****************************************************************************/
+
+
+/* Add a record. */
+int map_add(struct ldb_module *module, struct ldb_request *req)
+{
+ const struct ldb_message *msg = req->op.add.message;
+ struct ldb_context *ldb;
+ struct map_context *ac;
+ struct ldb_message *remote_msg;
+ const char *dn;
+ int ret;
+
+ ldb = ldb_module_get_ctx(module);
+
+ /* Do not manipulate our control entries */
+ if (ldb_dn_is_special(msg->dn)) {
+ return ldb_next_request(module, req);
+ }
+
+ /* No mapping requested (perhaps no DN mapping specified), skip to next module */
+ if (!ldb_dn_check_local(module, msg->dn)) {
+ return ldb_next_request(module, req);
+ }
+
+ /* No mapping needed, fail */
+ if (!ldb_msg_check_remote(module, msg)) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* Prepare context and handle */
+ ac = map_init_context(module, req);
+ if (ac == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+
+ /* Prepare the local message */
+ ac->local_msg = ldb_msg_new(ac);
+ if (ac->local_msg == NULL) {
+ map_oom(module);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ac->local_msg->dn = msg->dn;
+
+ /* Prepare the remote message */
+ remote_msg = ldb_msg_new(ac);
+ if (remote_msg == NULL) {
+ map_oom(module);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ remote_msg->dn = ldb_dn_map_local(ac->module, remote_msg, msg->dn);
+
+ /* Split local from remote message */
+ ldb_msg_partition(module, ac->local_msg, remote_msg, msg);
+
+ /* Prepare the remote operation */
+ ret = ldb_build_add_req(&ac->remote_req, ldb,
+ ac, remote_msg,
+ req->controls,
+ ac, map_op_remote_callback,
+ req);
+ if (ret != LDB_SUCCESS) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if ((ac->local_msg->num_elements == 0) ||
+ ( ! map_check_local_db(ac->module))) {
+ /* No local data or db, just run the remote request */
+ return ldb_next_remote_request(ac->module, ac->remote_req);
+ }
+
+ /* Store remote DN in 'IS_MAPPED' */
+ /* TODO: use GUIDs here instead */
+ dn = ldb_dn_alloc_linearized(ac->local_msg, remote_msg->dn);
+ if (ldb_msg_add_string(ac->local_msg, IS_MAPPED, dn) != 0) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ return map_add_do_local(ac);
+}
+
+/* Add the local record. */
+static int map_add_do_local(struct map_context *ac)
+{
+ struct ldb_request *local_req;
+ struct ldb_context *ldb;
+ int ret;
+
+ ldb = ldb_module_get_ctx(ac->module);
+
+ /* Prepare the local operation */
+ ret = ldb_build_add_req(&local_req, ldb, ac,
+ ac->local_msg,
+ ac->req->controls,
+ ac,
+ map_op_local_callback,
+ ac->req);
+ if (ret != LDB_SUCCESS) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ return ldb_next_request(ac->module, local_req);
+}
+
+/*****************************************************************************
+ * MODIFY operations
+*****************************************************************************/
+
+/* Modify a record. */
+int map_modify(struct ldb_module *module, struct ldb_request *req)
+{
+ const struct ldb_message *msg = req->op.mod.message;
+ struct ldb_request *search_req;
+ struct ldb_message *remote_msg;
+ struct ldb_context *ldb;
+ struct map_context *ac;
+ int ret;
+
+ ldb = ldb_module_get_ctx(module);
+
+ /* Do not manipulate our control entries */
+ if (ldb_dn_is_special(msg->dn)) {
+ return ldb_next_request(module, req);
+ }
+
+ /* No mapping requested (perhaps no DN mapping specified), skip to next module */
+ if (!ldb_dn_check_local(module, msg->dn)) {
+ return ldb_next_request(module, req);
+ }
+
+ /* No mapping needed, skip to next module */
+ /* TODO: What if the remote part exists, the local doesn't,
+ * and this request wants to modify local data and thus
+ * add the local record? */
+ if (!ldb_msg_check_remote(module, msg)) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* Prepare context and handle */
+ ac = map_init_context(module, req);
+ if (ac == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* Prepare the local message */
+ ac->local_msg = ldb_msg_new(ac);
+ if (ac->local_msg == NULL) {
+ map_oom(module);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ac->local_msg->dn = msg->dn;
+
+ /* Prepare the remote message */
+ remote_msg = ldb_msg_new(ac->remote_req);
+ if (remote_msg == NULL) {
+ map_oom(module);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ remote_msg->dn = ldb_dn_map_local(ac->module, remote_msg, msg->dn);
+
+ /* Split local from remote message */
+ ldb_msg_partition(module, ac->local_msg, remote_msg, msg);
+
+ /* Prepare the remote operation */
+ ret = ldb_build_mod_req(&ac->remote_req, ldb,
+ ac, remote_msg,
+ req->controls,
+ ac, map_op_remote_callback,
+ req);
+ if (ret != LDB_SUCCESS) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if ((ac->local_msg->num_elements == 0) ||
+ ( ! map_check_local_db(ac->module))) {
+ /* No local data or db, just run the remote request */
+ return ldb_next_remote_request(ac->module, ac->remote_req);
+ }
+
+ /* prepare the search operation */
+ ret = map_search_self_req(&search_req, ac, msg->dn);
+ if (ret != LDB_SUCCESS) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ return ldb_next_request(module, search_req);
+}
+
+/* Modify the local record. */
+static int map_modify_do_local(struct map_context *ac)
+{
+ struct ldb_request *local_req;
+ struct ldb_context *ldb;
+ char *dn;
+ int ret;
+
+ ldb = ldb_module_get_ctx(ac->module);
+
+ if (ac->local_dn == NULL) {
+ /* No local record present, add it instead */
+ /* Add local 'IS_MAPPED' */
+ /* TODO: use GUIDs here instead */
+ if (ldb_msg_add_empty(ac->local_msg, IS_MAPPED,
+ LDB_FLAG_MOD_ADD, NULL) != 0) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ dn = ldb_dn_alloc_linearized(ac->local_msg,
+ ac->remote_req->op.mod.message->dn);
+ if (ldb_msg_add_string(ac->local_msg, IS_MAPPED, dn) != 0) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* Prepare the local operation */
+ ret = ldb_build_add_req(&local_req, ldb, ac,
+ ac->local_msg,
+ ac->req->controls,
+ ac,
+ map_op_local_callback,
+ ac->req);
+ if (ret != LDB_SUCCESS) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ } else {
+ /* Prepare the local operation */
+ ret = ldb_build_mod_req(&local_req, ldb, ac,
+ ac->local_msg,
+ ac->req->controls,
+ ac,
+ map_op_local_callback,
+ ac->req);
+ if (ret != LDB_SUCCESS) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ }
+
+ return ldb_next_request(ac->module, local_req);
+}
+
+/*****************************************************************************
+ * DELETE operations
+*****************************************************************************/
+
+/* Delete a record. */
+int map_delete(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_request *search_req;
+ struct ldb_context *ldb;
+ struct map_context *ac;
+ int ret;
+
+ ldb = ldb_module_get_ctx(module);
+
+ /* Do not manipulate our control entries */
+ if (ldb_dn_is_special(req->op.del.dn)) {
+ return ldb_next_request(module, req);
+ }
+
+ /* No mapping requested (perhaps no DN mapping specified).
+ * Skip to next module */
+ if (!ldb_dn_check_local(module, req->op.del.dn)) {
+ return ldb_next_request(module, req);
+ }
+
+ /* Prepare context and handle */
+ ac = map_init_context(module, req);
+ if (ac == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* Prepare the remote operation */
+ ret = ldb_build_del_req(&ac->remote_req, ldb, ac,
+ ldb_dn_map_local(module, ac, req->op.del.dn),
+ req->controls,
+ ac,
+ map_op_remote_callback,
+ req);
+ if (ret != LDB_SUCCESS) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* No local db, just run the remote request */
+ if (!map_check_local_db(ac->module)) {
+ /* Do the remote request. */
+ return ldb_next_remote_request(ac->module, ac->remote_req);
+ }
+
+ /* Prepare the search operation */
+ ret = map_search_self_req(&search_req, ac, req->op.del.dn);
+ if (ret != LDB_SUCCESS) {
+ map_oom(module);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ return ldb_next_request(module, search_req);
+}
+
+/* Delete the local record. */
+static int map_delete_do_local(struct map_context *ac)
+{
+ struct ldb_request *local_req;
+ struct ldb_context *ldb;
+ int ret;
+
+ ldb = ldb_module_get_ctx(ac->module);
+
+ /* No local record, continue remotely */
+ if (ac->local_dn == NULL) {
+ /* Do the remote request. */
+ return ldb_next_remote_request(ac->module, ac->remote_req);
+ }
+
+ /* Prepare the local operation */
+ ret = ldb_build_del_req(&local_req, ldb, ac,
+ ac->req->op.del.dn,
+ ac->req->controls,
+ ac,
+ map_op_local_callback,
+ ac->req);
+ if (ret != LDB_SUCCESS) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ return ldb_next_request(ac->module, local_req);
+}
+
+/*****************************************************************************
+ * RENAME operations
+*****************************************************************************/
+
+/* Rename a record. */
+int map_rename(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_request *search_req;
+ struct ldb_context *ldb;
+ struct map_context *ac;
+ int ret;
+
+ ldb = ldb_module_get_ctx(module);
+
+ /* Do not manipulate our control entries */
+ if (ldb_dn_is_special(req->op.rename.olddn)) {
+ return ldb_next_request(module, req);
+ }
+
+ /* No mapping requested (perhaps no DN mapping specified).
+ * Skip to next module */
+ if ((!ldb_dn_check_local(module, req->op.rename.olddn)) &&
+ (!ldb_dn_check_local(module, req->op.rename.newdn))) {
+ return ldb_next_request(module, req);
+ }
+
+ /* Rename into/out of the mapped partition requested, bail out */
+ if (!ldb_dn_check_local(module, req->op.rename.olddn) ||
+ !ldb_dn_check_local(module, req->op.rename.newdn)) {
+ return LDB_ERR_AFFECTS_MULTIPLE_DSAS;
+ }
+
+ /* Prepare context and handle */
+ ac = map_init_context(module, req);
+ if (ac == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* Prepare the remote operation */
+ ret = ldb_build_rename_req(&ac->remote_req, ldb, ac,
+ ldb_dn_map_local(module, ac, req->op.rename.olddn),
+ ldb_dn_map_local(module, ac, req->op.rename.newdn),
+ req->controls,
+ ac, map_op_remote_callback,
+ req);
+ if (ret != LDB_SUCCESS) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* No local db, just run the remote request */
+ if (!map_check_local_db(ac->module)) {
+ /* Do the remote request. */
+ return ldb_next_remote_request(ac->module, ac->remote_req);
+ }
+
+ /* Prepare the search operation */
+ ret = map_search_self_req(&search_req, ac, req->op.rename.olddn);
+ if (ret != LDB_SUCCESS) {
+ map_oom(module);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ return ldb_next_request(module, search_req);
+}
+
+/* Rename the local record. */
+static int map_rename_do_local(struct map_context *ac)
+{
+ struct ldb_request *local_req;
+ struct ldb_context *ldb;
+ int ret;
+
+ ldb = ldb_module_get_ctx(ac->module);
+
+ /* No local record, continue remotely */
+ if (ac->local_dn == NULL) {
+ /* Do the remote request. */
+ return ldb_next_remote_request(ac->module, ac->remote_req);
+ }
+
+ /* Prepare the local operation */
+ ret = ldb_build_rename_req(&local_req, ldb, ac,
+ ac->req->op.rename.olddn,
+ ac->req->op.rename.newdn,
+ ac->req->controls,
+ ac,
+ map_rename_local_callback,
+ ac->req);
+ if (ret != LDB_SUCCESS) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ return ldb_next_request(ac->module, local_req);
+}
+
+static int map_rename_local_callback(struct ldb_request *req,
+ struct ldb_reply *ares)
+{
+ struct ldb_context *ldb;
+ struct map_context *ac;
+ int ret;
+
+ ac = talloc_get_type(req->context, struct map_context);
+ ldb = ldb_module_get_ctx(ac->module);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ if (ares->type != LDB_REPLY_DONE) {
+ ldb_set_errstring(ldb, "Invalid reply type!");
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ /* proceed with next step */
+ ret = map_rename_do_fixup(ac);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ return LDB_SUCCESS;
+}
+
+/* Update the local 'IS_MAPPED' attribute. */
+static int map_rename_do_fixup(struct map_context *ac)
+{
+ struct ldb_request *local_req;
+
+ /* Prepare the fixup operation */
+ /* TODO: use GUIDs here instead -- or skip it when GUIDs are used. */
+ local_req = map_build_fixup_req(ac,
+ ac->req->op.rename.newdn,
+ ac->remote_req->op.rename.newdn,
+ ac,
+ map_op_local_callback);
+ if (local_req == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ return ldb_next_request(ac->module, local_req);
+}
diff --git a/source4/lib/ldb/ldb_map/ldb_map_outbound.c b/source4/lib/ldb/ldb_map/ldb_map_outbound.c
new file mode 100644
index 0000000000..ffcefad6be
--- /dev/null
+++ b/source4/lib/ldb/ldb_map/ldb_map_outbound.c
@@ -0,0 +1,1378 @@
+/*
+ ldb database mapping module
+
+ Copyright (C) Jelmer Vernooij 2005
+ Copyright (C) Martin Kuehl <mkhl@samba.org> 2006
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2006
+ Copyright (C) Simo Sorce <idra@samba.org> 2008
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "ldb_includes.h"
+#include "ldb_map.h"
+#include "ldb_map_private.h"
+
+
+/* Mapping attributes
+ * ================== */
+
+/* Select attributes that stay in the local partition. */
+static const char **map_attrs_select_local(struct ldb_module *module, void *mem_ctx, const char * const *attrs)
+{
+ const struct ldb_map_context *data = map_get_context(module);
+ const char **result;
+ int i, last;
+
+ if (attrs == NULL)
+ return NULL;
+
+ last = 0;
+ result = talloc_array(mem_ctx, const char *, 1);
+ if (result == NULL) {
+ goto failed;
+ }
+ result[0] = NULL;
+
+ for (i = 0; attrs[i]; i++) {
+ /* Wildcards and ignored attributes are kept locally */
+ if ((ldb_attr_cmp(attrs[i], "*") == 0) ||
+ (!map_attr_check_remote(data, attrs[i]))) {
+ result = talloc_realloc(mem_ctx, result, const char *, last+2);
+ if (result == NULL) {
+ goto failed;
+ }
+
+ result[last] = talloc_strdup(result, attrs[i]);
+ result[last+1] = NULL;
+ last++;
+ }
+ }
+
+ return result;
+
+failed:
+ talloc_free(result);
+ map_oom(module);
+ return NULL;
+}
+
+/* Collect attributes that are mapped into the remote partition. */
+static const char **map_attrs_collect_remote(struct ldb_module *module, void *mem_ctx,
+ const char * const *attrs)
+{
+ const struct ldb_map_context *data = map_get_context(module);
+ const char **result;
+ const struct ldb_map_attribute *map;
+ const char *name=NULL;
+ int i, j, last;
+ int ret;
+
+ last = 0;
+ result = talloc_array(mem_ctx, const char *, 1);
+ if (result == NULL) {
+ goto failed;
+ }
+ result[0] = NULL;
+
+ for (i = 0; attrs[i]; i++) {
+ /* Wildcards are kept remotely, too */
+ if (ldb_attr_cmp(attrs[i], "*") == 0) {
+ const char **new_attrs = NULL;
+ ret = map_attrs_merge(module, mem_ctx, &new_attrs, attrs);
+ if (ret != LDB_SUCCESS) {
+ goto failed;
+ }
+ ret = map_attrs_merge(module, mem_ctx, &new_attrs, data->wildcard_attributes);
+ if (ret != LDB_SUCCESS) {
+ goto failed;
+ }
+
+ attrs = new_attrs;
+ break;
+ }
+ }
+
+ for (i = 0; attrs[i]; i++) {
+ /* Wildcards are kept remotely, too */
+ if (ldb_attr_cmp(attrs[i], "*") == 0) {
+ /* Add all 'include in wildcard' attributes */
+ name = attrs[i];
+ goto named;
+ }
+
+ /* Add remote names of mapped attrs */
+ map = map_attr_find_local(data, attrs[i]);
+ if (map == NULL) {
+ continue;
+ }
+
+ switch (map->type) {
+ case MAP_IGNORE:
+ continue;
+
+ case MAP_KEEP:
+ name = attrs[i];
+ goto named;
+
+ case MAP_RENAME:
+ case MAP_CONVERT:
+ name = map->u.rename.remote_name;
+ goto named;
+
+ case MAP_GENERATE:
+ /* Add all remote names of "generate" attrs */
+ for (j = 0; map->u.generate.remote_names[j]; j++) {
+ result = talloc_realloc(mem_ctx, result, const char *, last+2);
+ if (result == NULL) {
+ goto failed;
+ }
+
+ result[last] = talloc_strdup(result, map->u.generate.remote_names[j]);
+ result[last+1] = NULL;
+ last++;
+ }
+ continue;
+ }
+
+ named: /* We found a single remote name, add that */
+ result = talloc_realloc(mem_ctx, result, const char *, last+2);
+ if (result == NULL) {
+ goto failed;
+ }
+
+ result[last] = talloc_strdup(result, name);
+ result[last+1] = NULL;
+ last++;
+ }
+
+ return result;
+
+failed:
+ talloc_free(result);
+ map_oom(module);
+ return NULL;
+}
+
+/* Split attributes that stay in the local partition from those that
+ * are mapped into the remote partition. */
+static int map_attrs_partition(struct ldb_module *module, void *mem_ctx, const char ***local_attrs, const char ***remote_attrs, const char * const *attrs)
+{
+ *local_attrs = map_attrs_select_local(module, mem_ctx, attrs);
+ *remote_attrs = map_attrs_collect_remote(module, mem_ctx, attrs);
+
+ return 0;
+}
+
+/* Mapping message elements
+ * ======================== */
+
+/* Add an element to a message, overwriting any old identically named elements. */
+static int ldb_msg_replace(struct ldb_message *msg, const struct ldb_message_element *el)
+{
+ struct ldb_message_element *old;
+
+ old = ldb_msg_find_element(msg, el->name);
+
+ /* no local result, add as new element */
+ if (old == NULL) {
+ if (ldb_msg_add_empty(msg, el->name, 0, &old) != 0) {
+ return -1;
+ }
+ talloc_free(discard_const_p(char, old->name));
+ }
+
+ /* copy new element */
+ *old = *el;
+
+ /* and make sure we reference the contents */
+ if (!talloc_reference(msg->elements, el->name)) {
+ return -1;
+ }
+ if (!talloc_reference(msg->elements, el->values)) {
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Map a message element back into the local partition. */
+static struct ldb_message_element *ldb_msg_el_map_remote(struct ldb_module *module,
+ void *mem_ctx,
+ const struct ldb_map_attribute *map,
+ const char *attr_name,
+ const struct ldb_message_element *old)
+{
+ struct ldb_message_element *el;
+ int i;
+
+ el = talloc_zero(mem_ctx, struct ldb_message_element);
+ if (el == NULL) {
+ map_oom(module);
+ return NULL;
+ }
+
+ el->values = talloc_array(el, struct ldb_val, old->num_values);
+ if (el->values == NULL) {
+ talloc_free(el);
+ map_oom(module);
+ return NULL;
+ }
+
+ el->name = talloc_strdup(el, attr_name);
+ if (el->name == NULL) {
+ talloc_free(el);
+ map_oom(module);
+ return NULL;
+ }
+
+ for (i = 0; i < old->num_values; i++) {
+ el->values[i] = ldb_val_map_remote(module, el->values, map, &old->values[i]);
+ /* Conversions might fail, in which case bail */
+ if (!el->values[i].data) {
+ talloc_free(el);
+ return NULL;
+ }
+ el->num_values++;
+ }
+
+ return el;
+}
+
+/* Merge a remote message element into a local message. */
+static int ldb_msg_el_merge(struct ldb_module *module, struct ldb_message *local,
+ struct ldb_message *remote, const char *attr_name)
+{
+ const struct ldb_map_context *data = map_get_context(module);
+ const struct ldb_map_attribute *map;
+ struct ldb_message_element *old, *el=NULL;
+ const char *remote_name = NULL;
+ struct ldb_context *ldb;
+
+ ldb = ldb_module_get_ctx(module);
+
+ /* We handle wildcards in ldb_msg_el_merge_wildcard */
+ if (ldb_attr_cmp(attr_name, "*") == 0) {
+ return LDB_SUCCESS;
+ }
+
+ map = map_attr_find_local(data, attr_name);
+
+ /* Unknown attribute in remote message:
+ * skip, attribute was probably auto-generated */
+ if (map == NULL) {
+ return LDB_SUCCESS;
+ }
+
+ switch (map->type) {
+ case MAP_IGNORE:
+ break;
+ case MAP_CONVERT:
+ remote_name = map->u.convert.remote_name;
+ break;
+ case MAP_KEEP:
+ remote_name = attr_name;
+ break;
+ case MAP_RENAME:
+ remote_name = map->u.rename.remote_name;
+ break;
+ case MAP_GENERATE:
+ break;
+ }
+
+ switch (map->type) {
+ case MAP_IGNORE:
+ return LDB_SUCCESS;
+
+ case MAP_CONVERT:
+ if (map->u.convert.convert_remote == NULL) {
+ ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_map: "
+ "Skipping attribute '%s': "
+ "'convert_remote' not set\n",
+ attr_name);
+ return LDB_SUCCESS;
+ }
+ /* fall through */
+ case MAP_KEEP:
+ case MAP_RENAME:
+ old = ldb_msg_find_element(remote, remote_name);
+ if (old) {
+ el = ldb_msg_el_map_remote(module, local, map, attr_name, old);
+ } else {
+ return LDB_ERR_NO_SUCH_ATTRIBUTE;
+ }
+ break;
+
+ case MAP_GENERATE:
+ if (map->u.generate.generate_local == NULL) {
+ ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_map: "
+ "Skipping attribute '%s': "
+ "'generate_local' not set\n",
+ attr_name);
+ return LDB_SUCCESS;
+ }
+
+ el = map->u.generate.generate_local(module, local, attr_name, remote);
+ if (!el) {
+ /* Generation failure is probably due to lack of source attributes */
+ return LDB_ERR_NO_SUCH_ATTRIBUTE;
+ }
+ break;
+ }
+
+ if (el == NULL) {
+ return LDB_ERR_NO_SUCH_ATTRIBUTE;
+ }
+
+ return ldb_msg_replace(local, el);
+}
+
+/* Handle wildcard parts of merging a remote message element into a local message. */
+static int ldb_msg_el_merge_wildcard(struct ldb_module *module, struct ldb_message *local,
+ struct ldb_message *remote)
+{
+ const struct ldb_map_context *data = map_get_context(module);
+ const struct ldb_map_attribute *map = map_attr_find_local(data, "*");
+ struct ldb_message_element *el=NULL;
+ int i, ret;
+
+ /* Perhaps we have a mapping for "*" */
+ if (map && map->type == MAP_KEEP) {
+ /* We copy everything over, and hope that anything with a
+ more specific rule is overwritten */
+ for (i = 0; i < remote->num_elements; i++) {
+ el = ldb_msg_el_map_remote(module, local, map, remote->elements[i].name,
+ &remote->elements[i]);
+ if (el == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ldb_msg_replace(local, el);
+ if (ret) {
+ return ret;
+ }
+ }
+ }
+
+ /* Now walk the list of possible mappings, and apply each */
+ for (i = 0; data->attribute_maps[i].local_name; i++) {
+ ret = ldb_msg_el_merge(module, local, remote,
+ data->attribute_maps[i].local_name);
+ if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE) {
+ continue;
+ } else if (ret) {
+ return ret;
+ } else {
+ continue;
+ }
+ }
+
+ return LDB_SUCCESS;
+}
+
+/* Mapping messages
+ * ================ */
+
+/* Merge two local messages into a single one. */
+static int ldb_msg_merge_local(struct ldb_module *module, struct ldb_message *msg1, struct ldb_message *msg2)
+{
+ int i, ret;
+
+ for (i = 0; i < msg2->num_elements; i++) {
+ ret = ldb_msg_replace(msg1, &msg2->elements[i]);
+ if (ret) {
+ return ret;
+ }
+ }
+
+ return LDB_SUCCESS;
+}
+
+/* Merge a local and a remote message into a single local one. */
+static int ldb_msg_merge_remote(struct map_context *ac, struct ldb_message *local,
+ struct ldb_message *remote)
+{
+ int i, ret;
+ const char * const *attrs = ac->all_attrs;
+ if (!attrs) {
+ ret = ldb_msg_el_merge_wildcard(ac->module, local, remote);
+ if (ret) {
+ return ret;
+ }
+ }
+
+ for (i = 0; attrs && attrs[i]; i++) {
+ if (ldb_attr_cmp(attrs[i], "*") == 0) {
+ ret = ldb_msg_el_merge_wildcard(ac->module, local, remote);
+ if (ret) {
+ return ret;
+ }
+ break;
+ }
+ }
+
+ /* Try to map each attribute back;
+ * Add to local message is possible,
+ * Overwrite old local attribute if necessary */
+ for (i = 0; attrs && attrs[i]; i++) {
+ ret = ldb_msg_el_merge(ac->module, local, remote,
+ attrs[i]);
+ if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE) {
+ } else if (ret) {
+ return ret;
+ }
+ }
+
+ return LDB_SUCCESS;
+}
+
+/* Mapping search results
+ * ====================== */
+
+/* Map a search result back into the local partition. */
+static int map_reply_remote(struct map_context *ac, struct ldb_reply *ares)
+{
+ struct ldb_message *msg;
+ struct ldb_dn *dn;
+ int ret;
+
+ /* There is no result message, skip */
+ if (ares->type != LDB_REPLY_ENTRY) {
+ return 0;
+ }
+
+ /* Create a new result message */
+ msg = ldb_msg_new(ares);
+ if (msg == NULL) {
+ map_oom(ac->module);
+ return -1;
+ }
+
+ /* Merge remote message into new message */
+ ret = ldb_msg_merge_remote(ac, msg, ares->message);
+ if (ret) {
+ talloc_free(msg);
+ return ret;
+ }
+
+ /* Create corresponding local DN */
+ dn = ldb_dn_map_rebase_remote(ac->module, msg, ares->message->dn);
+ if (dn == NULL) {
+ talloc_free(msg);
+ return -1;
+ }
+ msg->dn = dn;
+
+ /* Store new message with new DN as the result */
+ talloc_free(ares->message);
+ ares->message = msg;
+
+ return 0;
+}
+
+/* Mapping parse trees
+ * =================== */
+
+/* Check whether a parse tree can safely be split in two. */
+static bool ldb_parse_tree_check_splittable(const struct ldb_parse_tree *tree)
+{
+ const struct ldb_parse_tree *subtree = tree;
+ bool negate = false;
+
+ while (subtree) {
+ switch (subtree->operation) {
+ case LDB_OP_NOT:
+ negate = !negate;
+ subtree = subtree->u.isnot.child;
+ continue;
+
+ case LDB_OP_AND:
+ return !negate; /* if negate: False */
+
+ case LDB_OP_OR:
+ return negate; /* if negate: True */
+
+ default:
+ return true; /* simple parse tree */
+ }
+ }
+
+ return true; /* no parse tree */
+}
+
+/* Collect a list of attributes required to match a given parse tree. */
+static int ldb_parse_tree_collect_attrs(struct ldb_module *module, void *mem_ctx, const char ***attrs, const struct ldb_parse_tree *tree)
+{
+ const char **new_attrs;
+ int i, ret;
+
+ if (tree == NULL) {
+ return 0;
+ }
+
+ switch (tree->operation) {
+ case LDB_OP_OR:
+ case LDB_OP_AND: /* attributes stored in list of subtrees */
+ for (i = 0; i < tree->u.list.num_elements; i++) {
+ ret = ldb_parse_tree_collect_attrs(module, mem_ctx,
+ attrs, tree->u.list.elements[i]);
+ if (ret) {
+ return ret;
+ }
+ }
+ return 0;
+
+ case LDB_OP_NOT: /* attributes stored in single subtree */
+ return ldb_parse_tree_collect_attrs(module, mem_ctx, attrs, tree->u.isnot.child);
+
+ default: /* single attribute in tree */
+ new_attrs = ldb_attr_list_copy_add(mem_ctx, *attrs, tree->u.equality.attr);
+ talloc_free(*attrs);
+ *attrs = new_attrs;
+ return 0;
+ }
+
+ return -1;
+}
+
+static int map_subtree_select_local(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree);
+
+/* Select a negated subtree that queries attributes in the local partition */
+static int map_subtree_select_local_not(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree)
+{
+ struct ldb_parse_tree *child;
+ int ret;
+
+ /* Prepare new tree */
+ *new = talloc_memdup(mem_ctx, tree, sizeof(struct ldb_parse_tree));
+ if (*new == NULL) {
+ map_oom(module);
+ return -1;
+ }
+
+ /* Generate new subtree */
+ ret = map_subtree_select_local(module, *new, &child, tree->u.isnot.child);
+ if (ret) {
+ talloc_free(*new);
+ return ret;
+ }
+
+ /* Prune tree without subtree */
+ if (child == NULL) {
+ talloc_free(*new);
+ *new = NULL;
+ return 0;
+ }
+
+ (*new)->u.isnot.child = child;
+
+ return ret;
+}
+
+/* Select a list of subtrees that query attributes in the local partition */
+static int map_subtree_select_local_list(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree)
+{
+ int i, j, ret=0;
+
+ /* Prepare new tree */
+ *new = talloc_memdup(mem_ctx, tree, sizeof(struct ldb_parse_tree));
+ if (*new == NULL) {
+ map_oom(module);
+ return -1;
+ }
+
+ /* Prepare list of subtrees */
+ (*new)->u.list.num_elements = 0;
+ (*new)->u.list.elements = talloc_array(*new, struct ldb_parse_tree *, tree->u.list.num_elements);
+ if ((*new)->u.list.elements == NULL) {
+ map_oom(module);
+ talloc_free(*new);
+ return -1;
+ }
+
+ /* Generate new list of subtrees */
+ j = 0;
+ for (i = 0; i < tree->u.list.num_elements; i++) {
+ struct ldb_parse_tree *child;
+ ret = map_subtree_select_local(module, *new, &child, tree->u.list.elements[i]);
+ if (ret) {
+ talloc_free(*new);
+ return ret;
+ }
+
+ if (child) {
+ (*new)->u.list.elements[j] = child;
+ j++;
+ }
+ }
+
+ /* Prune tree without subtrees */
+ if (j == 0) {
+ talloc_free(*new);
+ *new = NULL;
+ return 0;
+ }
+
+ /* Fix subtree list size */
+ (*new)->u.list.num_elements = j;
+ (*new)->u.list.elements = talloc_realloc(*new, (*new)->u.list.elements, struct ldb_parse_tree *, (*new)->u.list.num_elements);
+
+ return ret;
+}
+
+/* Select a simple subtree that queries attributes in the local partition */
+static int map_subtree_select_local_simple(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree)
+{
+ /* Prepare new tree */
+ *new = talloc_memdup(mem_ctx, tree, sizeof(struct ldb_parse_tree));
+ if (*new == NULL) {
+ map_oom(module);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Select subtrees that query attributes in the local partition */
+static int map_subtree_select_local(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree)
+{
+ const struct ldb_map_context *data = map_get_context(module);
+
+ if (tree == NULL) {
+ return 0;
+ }
+
+ if (tree->operation == LDB_OP_NOT) {
+ return map_subtree_select_local_not(module, mem_ctx, new, tree);
+ }
+
+ if (tree->operation == LDB_OP_AND || tree->operation == LDB_OP_OR) {
+ return map_subtree_select_local_list(module, mem_ctx, new, tree);
+ }
+
+ if (map_attr_check_remote(data, tree->u.equality.attr)) {
+ *new = NULL;
+ return 0;
+ }
+
+ return map_subtree_select_local_simple(module, mem_ctx, new, tree);
+}
+
+static int map_subtree_collect_remote(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree);
+
+/* Collect a negated subtree that queries attributes in the remote partition */
+static int map_subtree_collect_remote_not(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree)
+{
+ struct ldb_parse_tree *child;
+ int ret;
+
+ /* Prepare new tree */
+ *new = talloc_memdup(mem_ctx, tree, sizeof(struct ldb_parse_tree));
+ if (*new == NULL) {
+ map_oom(module);
+ return -1;
+ }
+
+ /* Generate new subtree */
+ ret = map_subtree_collect_remote(module, *new, &child, tree->u.isnot.child);
+ if (ret) {
+ talloc_free(*new);
+ return ret;
+ }
+
+ /* Prune tree without subtree */
+ if (child == NULL) {
+ talloc_free(*new);
+ *new = NULL;
+ return 0;
+ }
+
+ (*new)->u.isnot.child = child;
+
+ return ret;
+}
+
+/* Collect a list of subtrees that query attributes in the remote partition */
+static int map_subtree_collect_remote_list(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree)
+{
+ int i, j, ret=0;
+
+ /* Prepare new tree */
+ *new = talloc_memdup(mem_ctx, tree, sizeof(struct ldb_parse_tree));
+ if (*new == NULL) {
+ map_oom(module);
+ return -1;
+ }
+
+ /* Prepare list of subtrees */
+ (*new)->u.list.num_elements = 0;
+ (*new)->u.list.elements = talloc_array(*new, struct ldb_parse_tree *, tree->u.list.num_elements);
+ if ((*new)->u.list.elements == NULL) {
+ map_oom(module);
+ talloc_free(*new);
+ return -1;
+ }
+
+ /* Generate new list of subtrees */
+ j = 0;
+ for (i = 0; i < tree->u.list.num_elements; i++) {
+ struct ldb_parse_tree *child;
+ ret = map_subtree_collect_remote(module, *new, &child, tree->u.list.elements[i]);
+ if (ret) {
+ talloc_free(*new);
+ return ret;
+ }
+
+ if (child) {
+ (*new)->u.list.elements[j] = child;
+ j++;
+ }
+ }
+
+ /* Prune tree without subtrees */
+ if (j == 0) {
+ talloc_free(*new);
+ *new = NULL;
+ return 0;
+ }
+
+ /* Fix subtree list size */
+ (*new)->u.list.num_elements = j;
+ (*new)->u.list.elements = talloc_realloc(*new, (*new)->u.list.elements, struct ldb_parse_tree *, (*new)->u.list.num_elements);
+
+ return ret;
+}
+
+/* Collect a simple subtree that queries attributes in the remote partition */
+int map_subtree_collect_remote_simple(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree, const struct ldb_map_attribute *map)
+{
+ const char *attr;
+
+ /* Prepare new tree */
+ *new = talloc(mem_ctx, struct ldb_parse_tree);
+ if (*new == NULL) {
+ map_oom(module);
+ return -1;
+ }
+ **new = *tree;
+
+ if (map->type == MAP_KEEP) {
+ /* Nothing to do here */
+ return 0;
+ }
+
+ /* Store attribute and value in new tree */
+ switch (tree->operation) {
+ case LDB_OP_PRESENT:
+ attr = map_attr_map_local(*new, map, tree->u.present.attr);
+ (*new)->u.present.attr = attr;
+ break;
+ case LDB_OP_SUBSTRING:
+ {
+ attr = map_attr_map_local(*new, map, tree->u.substring.attr);
+ (*new)->u.substring.attr = attr;
+ break;
+ }
+ case LDB_OP_EQUALITY:
+ attr = map_attr_map_local(*new, map, tree->u.equality.attr);
+ (*new)->u.equality.attr = attr;
+ break;
+ case LDB_OP_LESS:
+ case LDB_OP_GREATER:
+ case LDB_OP_APPROX:
+ attr = map_attr_map_local(*new, map, tree->u.comparison.attr);
+ (*new)->u.comparison.attr = attr;
+ break;
+ case LDB_OP_EXTENDED:
+ attr = map_attr_map_local(*new, map, tree->u.extended.attr);
+ (*new)->u.extended.attr = attr;
+ break;
+ default: /* unknown kind of simple subtree */
+ talloc_free(*new);
+ return -1;
+ }
+
+ if (attr == NULL) {
+ talloc_free(*new);
+ *new = NULL;
+ return 0;
+ }
+
+ if (map->type == MAP_RENAME) {
+ /* Nothing more to do here, the attribute has been renamed */
+ return 0;
+ }
+
+ /* Store attribute and value in new tree */
+ switch (tree->operation) {
+ case LDB_OP_PRESENT:
+ break;
+ case LDB_OP_SUBSTRING:
+ {
+ int i;
+ /* Map value */
+ (*new)->u.substring.chunks = NULL;
+ for (i=0; tree->u.substring.chunks[i]; i++) {
+ (*new)->u.substring.chunks = talloc_realloc(*new, (*new)->u.substring.chunks, struct ldb_val *, i+2);
+ if (!(*new)->u.substring.chunks) {
+ talloc_free(*new);
+ *new = NULL;
+ return 0;
+ }
+ (*new)->u.substring.chunks[i] = talloc(*new, struct ldb_val);
+ if (!(*new)->u.substring.chunks[i]) {
+ talloc_free(*new);
+ *new = NULL;
+ return 0;
+ }
+ *(*new)->u.substring.chunks[i] = ldb_val_map_local(module, *new, map, tree->u.substring.chunks[i]);
+ (*new)->u.substring.chunks[i+1] = NULL;
+ }
+ break;
+ }
+ case LDB_OP_EQUALITY:
+ (*new)->u.equality.value = ldb_val_map_local(module, *new, map, &tree->u.equality.value);
+ break;
+ case LDB_OP_LESS:
+ case LDB_OP_GREATER:
+ case LDB_OP_APPROX:
+ (*new)->u.comparison.value = ldb_val_map_local(module, *new, map, &tree->u.comparison.value);
+ break;
+ case LDB_OP_EXTENDED:
+ (*new)->u.extended.value = ldb_val_map_local(module, *new, map, &tree->u.extended.value);
+ (*new)->u.extended.rule_id = talloc_strdup(*new, tree->u.extended.rule_id);
+ break;
+ default: /* unknown kind of simple subtree */
+ talloc_free(*new);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Collect subtrees that query attributes in the remote partition */
+static int map_subtree_collect_remote(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree)
+{
+ const struct ldb_map_context *data = map_get_context(module);
+ const struct ldb_map_attribute *map;
+ struct ldb_context *ldb;
+
+ ldb = ldb_module_get_ctx(module);
+
+ if (tree == NULL) {
+ return 0;
+ }
+
+ if (tree->operation == LDB_OP_NOT) {
+ return map_subtree_collect_remote_not(module, mem_ctx, new, tree);
+ }
+
+ if ((tree->operation == LDB_OP_AND) || (tree->operation == LDB_OP_OR)) {
+ return map_subtree_collect_remote_list(module, mem_ctx, new, tree);
+ }
+
+ if (!map_attr_check_remote(data, tree->u.equality.attr)) {
+ *new = NULL;
+ return 0;
+ }
+
+ map = map_attr_find_local(data, tree->u.equality.attr);
+ if (map->convert_operator) {
+ return map->convert_operator(module, mem_ctx, new, tree);
+ }
+
+ if (map->type == MAP_GENERATE) {
+ ldb_debug(ldb, LDB_DEBUG_WARNING, "ldb_map: "
+ "Skipping attribute '%s': "
+ "'convert_operator' not set\n",
+ tree->u.equality.attr);
+ *new = NULL;
+ return 0;
+ }
+
+ return map_subtree_collect_remote_simple(module, mem_ctx, new, tree, map);
+}
+
+/* Split subtrees that query attributes in the local partition from
+ * those that query the remote partition. */
+static int ldb_parse_tree_partition(struct ldb_module *module,
+ void *mem_ctx,
+ struct ldb_parse_tree **local_tree,
+ struct ldb_parse_tree **remote_tree,
+ const struct ldb_parse_tree *tree)
+{
+ int ret;
+
+ *local_tree = NULL;
+ *remote_tree = NULL;
+
+ /* No original tree */
+ if (tree == NULL) {
+ return 0;
+ }
+
+ /* Generate local tree */
+ ret = map_subtree_select_local(module, mem_ctx, local_tree, tree);
+ if (ret) {
+ return ret;
+ }
+
+ /* Generate remote tree */
+ ret = map_subtree_collect_remote(module, mem_ctx, remote_tree, tree);
+ if (ret) {
+ talloc_free(*local_tree);
+ return ret;
+ }
+
+ return 0;
+}
+
+/* Collect a list of attributes required either explicitly from a
+ * given list or implicitly from a given parse tree; split the
+ * collected list into local and remote parts. */
+static int map_attrs_collect_and_partition(struct ldb_module *module, struct map_context *ac,
+ const char * const *search_attrs,
+ const struct ldb_parse_tree *tree)
+{
+ void *tmp_ctx;
+ const char **tree_attrs;
+ const char **remote_attrs;
+ const char **local_attrs;
+ int ret;
+
+ /* There is no tree, just partition the searched attributes */
+ if (tree == NULL) {
+ ret = map_attrs_partition(module, ac,
+ &local_attrs, &remote_attrs, search_attrs);
+ if (ret == 0) {
+ ac->local_attrs = local_attrs;
+ ac->remote_attrs = remote_attrs;
+ ac->all_attrs = search_attrs;
+ }
+ return ret;
+ }
+
+ /* Create context for temporary memory */
+ tmp_ctx = talloc_new(ac);
+ if (tmp_ctx == NULL) {
+ goto oom;
+ }
+
+ /* Prepare list of attributes from tree */
+ tree_attrs = talloc_array(tmp_ctx, const char *, 1);
+ if (tree_attrs == NULL) {
+ talloc_free(tmp_ctx);
+ goto oom;
+ }
+ tree_attrs[0] = NULL;
+
+ /* Collect attributes from tree */
+ ret = ldb_parse_tree_collect_attrs(module, tmp_ctx, &tree_attrs, tree);
+ if (ret) {
+ goto done;
+ }
+
+ /* Merge attributes from search operation */
+ ret = map_attrs_merge(module, tmp_ctx, &tree_attrs, search_attrs);
+ if (ret) {
+ goto done;
+ }
+
+ /* Split local from remote attributes */
+ ret = map_attrs_partition(module, ac, &local_attrs,
+ &remote_attrs, tree_attrs);
+
+ if (ret == 0) {
+ ac->local_attrs = local_attrs;
+ ac->remote_attrs = remote_attrs;
+ talloc_steal(ac, tree_attrs);
+ ac->all_attrs = tree_attrs;
+ }
+done:
+ /* Free temporary memory */
+ talloc_free(tmp_ctx);
+ return ret;
+
+oom:
+ map_oom(module);
+ return -1;
+}
+
+
+/* Outbound requests: search
+ * ========================= */
+
+static int map_remote_search_callback(struct ldb_request *req,
+ struct ldb_reply *ares);
+static int map_local_merge_callback(struct ldb_request *req,
+ struct ldb_reply *ares);
+static int map_search_local(struct map_context *ac);
+
+static int map_save_entry(struct map_context *ac, struct ldb_reply *ares)
+{
+ struct map_reply *mr;
+
+ mr = talloc_zero(ac, struct map_reply);
+ if (mr == NULL) {
+ map_oom(ac->module);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ mr->remote = talloc_steal(mr, ares);
+ if (ac->r_current) {
+ ac->r_current->next = mr;
+ } else {
+ /* first entry */
+ ac->r_list = mr;
+ }
+ ac->r_current = mr;
+
+ return LDB_SUCCESS;
+}
+
+/* Pass a merged search result up the callback chain. */
+int map_return_entry(struct map_context *ac, struct ldb_reply *ares)
+{
+ struct ldb_message_element *el;
+ const char * const *attrs;
+ struct ldb_context *ldb;
+ int i;
+
+ ldb = ldb_module_get_ctx(ac->module);
+
+ /* Merged result doesn't match original query, skip */
+ if (!ldb_match_msg(ldb, ares->message,
+ ac->req->op.search.tree,
+ ac->req->op.search.base,
+ ac->req->op.search.scope)) {
+ ldb_debug(ldb, LDB_DEBUG_TRACE, "ldb_map: "
+ "Skipping record '%s': "
+ "doesn't match original search\n",
+ ldb_dn_get_linearized(ares->message->dn));
+ return LDB_SUCCESS;
+ }
+
+ /* Limit result to requested attrs */
+ if (ac->req->op.search.attrs &&
+ (! ldb_attr_in_list(ac->req->op.search.attrs, "*"))) {
+
+ attrs = ac->req->op.search.attrs;
+ i = 0;
+
+ while (i < ares->message->num_elements) {
+
+ el = &ares->message->elements[i];
+ if ( ! ldb_attr_in_list(attrs, el->name)) {
+ ldb_msg_remove_element(ares->message, el);
+ } else {
+ i++;
+ }
+ }
+ }
+
+ return ldb_module_send_entry(ac->req, ares->message, ares->controls);
+}
+
+/* Search a record. */
+int map_search(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_parse_tree *remote_tree;
+ struct ldb_parse_tree *local_tree;
+ struct ldb_request *remote_req;
+ struct ldb_context *ldb;
+ struct map_context *ac;
+ int ret;
+
+ const char *wildcard[] = { "*", NULL };
+ const char * const *attrs;
+
+ ldb = ldb_module_get_ctx(module);
+
+ /* if we're not yet initialized, go to the next module */
+ if (!ldb_module_get_private(module))
+ return ldb_next_request(module, req);
+
+ /* Do not manipulate our control entries */
+ if (ldb_dn_is_special(req->op.search.base)) {
+ return ldb_next_request(module, req);
+ }
+
+ /* No mapping requested, skip to next module */
+ if ((req->op.search.base) && (!ldb_dn_check_local(module, req->op.search.base))) {
+ return ldb_next_request(module, req);
+ }
+
+ /* TODO: How can we be sure about which partition we are
+ * targetting when there is no search base? */
+
+ /* Prepare context and handle */
+ ac = map_init_context(module, req);
+ if (ac == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* It is easier to deal with the two different ways of
+ * expressing the wildcard in the same codepath */
+ attrs = req->op.search.attrs;
+ if (attrs == NULL) {
+ attrs = wildcard;
+ }
+
+ /* Split local from remote attrs */
+ ret = map_attrs_collect_and_partition(module, ac,
+ attrs, req->op.search.tree);
+ if (ret) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* Split local from remote tree */
+ ret = ldb_parse_tree_partition(module, ac,
+ &local_tree, &remote_tree,
+ req->op.search.tree);
+ if (ret) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if (((local_tree != NULL) && (remote_tree != NULL)) &&
+ (!ldb_parse_tree_check_splittable(req->op.search.tree))) {
+ /* The query can't safely be split, enumerate the remote partition */
+ local_tree = NULL;
+ remote_tree = NULL;
+ }
+
+ if (local_tree == NULL) {
+ /* Construct default local parse tree */
+ local_tree = talloc_zero(ac, struct ldb_parse_tree);
+ if (local_tree == NULL) {
+ map_oom(ac->module);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ local_tree->operation = LDB_OP_PRESENT;
+ local_tree->u.present.attr = talloc_strdup(local_tree, IS_MAPPED);
+ }
+ if (remote_tree == NULL) {
+ /* Construct default remote parse tree */
+ remote_tree = ldb_parse_tree(ac, NULL);
+ if (remote_tree == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ }
+
+ ac->local_tree = local_tree;
+
+ /* Prepare the remote operation */
+ ret = ldb_build_search_req_ex(&remote_req, ldb, ac,
+ req->op.search.base,
+ req->op.search.scope,
+ remote_tree,
+ ac->remote_attrs,
+ req->controls,
+ ac, map_remote_search_callback,
+ req);
+ if (ret != LDB_SUCCESS) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ return ldb_next_remote_request(module, remote_req);
+}
+
+/* Now, search the local part of a remote search result. */
+static int map_remote_search_callback(struct ldb_request *req,
+ struct ldb_reply *ares)
+{
+ struct map_context *ac;
+ int ret;
+
+ ac = talloc_get_type(req->context, struct map_context);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ switch (ares->type) {
+ case LDB_REPLY_REFERRAL:
+
+ /* ignore referrals */
+ talloc_free(ares);
+ return LDB_SUCCESS;
+
+ case LDB_REPLY_ENTRY:
+
+ /* Map result record into a local message */
+ ret = map_reply_remote(ac, ares);
+ if (ret) {
+ talloc_free(ares);
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ /* if we have no local db, then we can just return the reply to
+ * the upper layer, otherwise we must save it and process it
+ * when all replies ahve been gathered */
+ if ( ! map_check_local_db(ac->module)) {
+ ret = map_return_entry(ac, ares);
+ } else {
+ ret = map_save_entry(ac,ares);
+ }
+
+ if (ret != LDB_SUCCESS) {
+ talloc_free(ares);
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ break;
+
+ case LDB_REPLY_DONE:
+
+ if ( ! map_check_local_db(ac->module)) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, LDB_SUCCESS);
+ }
+
+ talloc_free(ares);
+
+ /* reset the pointer to the start of the list */
+ ac->r_current = ac->r_list;
+
+ /* no entry just return */
+ if (ac->r_current == NULL) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, LDB_SUCCESS);
+ }
+
+ ret = map_search_local(ac);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+ }
+
+ return LDB_SUCCESS;
+}
+
+static int map_search_local(struct map_context *ac)
+{
+ struct ldb_request *search_req;
+
+ if (ac->r_current == NULL || ac->r_current->remote == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* Prepare local search request */
+ /* TODO: use GUIDs here instead? */
+ search_req = map_search_base_req(ac,
+ ac->r_current->remote->message->dn,
+ NULL, NULL,
+ ac, map_local_merge_callback);
+ if (search_req == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ return ldb_next_request(ac->module, search_req);
+}
+
+/* Merge the remote and local parts of a search result. */
+int map_local_merge_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+ struct ldb_context *ldb;
+ struct map_context *ac;
+ int ret;
+
+ ac = talloc_get_type(req->context, struct map_context);
+ ldb = ldb_module_get_ctx(ac->module);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+ /* We have already found a local record */
+ if (ac->r_current->local) {
+ talloc_free(ares);
+ ldb_set_errstring(ldb, "ldb_map: Too many results!");
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ /* Store local result */
+ ac->r_current->local = talloc_steal(ac->r_current, ares);
+
+ break;
+
+ case LDB_REPLY_REFERRAL:
+ /* ignore referrals */
+ talloc_free(ares);
+ break;
+
+ case LDB_REPLY_DONE:
+ talloc_free(ares);
+
+ /* No local record found, map and send remote record */
+ if (ac->r_current->local != NULL) {
+ /* Merge remote into local message */
+ ret = ldb_msg_merge_local(ac->module,
+ ac->r_current->local->message,
+ ac->r_current->remote->message);
+ if (ret == LDB_SUCCESS) {
+ ret = map_return_entry(ac, ac->r_current->local);
+ }
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ } else {
+ ret = map_return_entry(ac, ac->r_current->remote);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req,
+ NULL, NULL, ret);
+ }
+ }
+
+ if (ac->r_current->next != NULL) {
+ ac->r_current = ac->r_current->next;
+ if (ac->r_current->remote->type == LDB_REPLY_ENTRY) {
+ ret = map_search_local(ac);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req,
+ NULL, NULL, ret);
+ }
+ break;
+ }
+ }
+
+ /* ok we are done with all search, finally it is time to
+ * finish operations for this module */
+ return ldb_module_done(ac->req,
+ ac->r_current->remote->controls,
+ ac->r_current->remote->response,
+ ac->r_current->remote->error);
+ }
+
+ return LDB_SUCCESS;
+}
diff --git a/source4/lib/ldb/ldb_map/ldb_map_private.h b/source4/lib/ldb/ldb_map/ldb_map_private.h
new file mode 100644
index 0000000000..612d215ae9
--- /dev/null
+++ b/source4/lib/ldb/ldb_map/ldb_map_private.h
@@ -0,0 +1,91 @@
+#include "ldb_includes.h"
+
+/* A handy macro to report Out of Memory conditions */
+#define map_oom(module) ldb_set_errstring(ldb_module_get_ctx(module), talloc_asprintf(module, "Out of Memory"));
+
+/* The type of search callback functions */
+typedef int (*ldb_map_callback_t)(struct ldb_request *, struct ldb_reply *);
+
+/* The special DN from which the local and remote base DNs are fetched */
+#define MAP_DN_NAME "@MAP"
+#define MAP_DN_FROM "@FROM"
+#define MAP_DN_TO "@TO"
+
+/* Private data structures
+ * ======================= */
+
+struct map_reply {
+ struct map_reply *next;
+ struct ldb_reply *remote;
+ struct ldb_reply *local;
+};
+
+/* Context data for mapped requests */
+struct map_context {
+
+ struct ldb_module *module;
+ struct ldb_request *req;
+
+ struct ldb_dn *local_dn;
+ const struct ldb_parse_tree *local_tree;
+ const char * const *local_attrs;
+ const char * const *remote_attrs;
+ const char * const *all_attrs;
+
+ struct ldb_message *local_msg;
+ struct ldb_request *remote_req;
+
+ struct map_reply *r_list;
+ struct map_reply *r_current;
+};
+
+/* Common operations
+ * ================= */
+
+/* The following definitions come from lib/ldb/modules/ldb_map.c */
+const struct ldb_map_context *map_get_context(struct ldb_module *module);
+struct map_context *map_init_context(struct ldb_module *module,
+ struct ldb_request *req);
+
+int ldb_next_remote_request(struct ldb_module *module, struct ldb_request *request);
+
+bool map_check_local_db(struct ldb_module *module);
+bool map_attr_check_remote(const struct ldb_map_context *data, const char *attr);
+bool ldb_dn_check_local(struct ldb_module *module, struct ldb_dn *dn);
+
+const struct ldb_map_attribute *map_attr_find_local(const struct ldb_map_context *data, const char *name);
+const struct ldb_map_attribute *map_attr_find_remote(const struct ldb_map_context *data, const char *name);
+
+const char *map_attr_map_local(void *mem_ctx, const struct ldb_map_attribute *map, const char *attr);
+const char *map_attr_map_remote(void *mem_ctx, const struct ldb_map_attribute *map, const char *attr);
+int map_attrs_merge(struct ldb_module *module, void *mem_ctx, const char ***attrs, const char * const *more_attrs);
+
+struct ldb_val ldb_val_map_local(struct ldb_module *module, void *mem_ctx, const struct ldb_map_attribute *map, const struct ldb_val *val);
+struct ldb_val ldb_val_map_remote(struct ldb_module *module, void *mem_ctx, const struct ldb_map_attribute *map, const struct ldb_val *val);
+
+struct ldb_dn *ldb_dn_map_local(struct ldb_module *module, void *mem_ctx, struct ldb_dn *dn);
+struct ldb_dn *ldb_dn_map_remote(struct ldb_module *module, void *mem_ctx, struct ldb_dn *dn);
+struct ldb_dn *ldb_dn_map_rebase_remote(struct ldb_module *module, void *mem_ctx, struct ldb_dn *dn);
+
+struct ldb_request *map_search_base_req(struct map_context *ac,
+ struct ldb_dn *dn,
+ const char * const *attrs,
+ const struct ldb_parse_tree *tree,
+ void *context,
+ ldb_map_callback_t callback);
+struct ldb_request *map_build_fixup_req(struct map_context *ac,
+ struct ldb_dn *olddn,
+ struct ldb_dn *newdn,
+ void *context,
+ ldb_map_callback_t callback);
+int map_subtree_collect_remote_simple(struct ldb_module *module, void *mem_ctx,
+ struct ldb_parse_tree **new,
+ const struct ldb_parse_tree *tree,
+ const struct ldb_map_attribute *map);
+int map_return_fatal_error(struct ldb_request *req,
+ struct ldb_reply *ares);
+int map_return_normal_error(struct ldb_request *req,
+ struct ldb_reply *ares,
+ int error);
+
+int map_return_entry(struct map_context *ac, struct ldb_reply *ares);
diff --git a/source4/lib/ldb/ldb_sqlite3/README b/source4/lib/ldb/ldb_sqlite3/README
new file mode 100644
index 0000000000..6cda0a7759
--- /dev/null
+++ b/source4/lib/ldb/ldb_sqlite3/README
@@ -0,0 +1,7 @@
+trees.ps contains an explanation of the Genealogical Representation of Trees
+in Databases which is being used in ldb_sqlite3. Note that we use fgID
+representation with 4 bytes per level, so we can represent 6.5E+08 subclasses
+of any object class. This should be adequate for our purposes. :-)
+
+The following document is the primary basis for the schema currently being
+used here: http://www.research.ibm.com/journal/sj/392/shi.html
diff --git a/source4/lib/ldb/ldb_sqlite3/base160.c b/source4/lib/ldb/ldb_sqlite3/base160.c
new file mode 100644
index 0000000000..423e2b6841
--- /dev/null
+++ b/source4/lib/ldb/ldb_sqlite3/base160.c
@@ -0,0 +1,154 @@
+/*
+ base160 code used by ldb_sqlite3
+
+ Copyright (C) 2004 Derrell Lipman
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+/*
+ * ldb_sqlite3_base160()
+ *
+ * Convert an integer value to a string containing the base 160 representation
+ * of the integer. We always convert to a string representation that is 4
+ * bytes in length, and we always null terminate.
+ *
+ * Parameters:
+ * val --
+ * The value to be converted
+ *
+ * result --
+ * Buffer in which the result is to be placed
+ *
+ * Returns:
+ * nothing
+ */
+static unsigned char base160tab[161] =
+{
+ 48 , 49 , 50 , 51 , 52 , 53 , 54 , 55 , 56 , 57 , /* 0-9 */
+ 58 , 59 , 65 , 66 , 67 , 68 , 69 , 70 , 71 , 72 , /* : ; A-H */
+ 73 , 74 , 75 , 76 , 77 , 78 , 79 , 80 , 81 , 82 , /* I-R */
+ 83 , 84 , 85 , 86 , 87 , 88 , 89 , 90 , 97 , 98 , /* S-Z , a-b */
+ 99 , 100, 101, 102, 103, 104, 105, 106, 107, 108, /* c-l */
+ 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, /* m-v */
+ 119, 120, 121, 122, 160, 161, 162, 163, 164, 165, /* w-z, latin1 */
+ 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, /* latin1 */
+ 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, /* latin1 */
+ 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, /* latin1 */
+ 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, /* latin1 */
+ 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, /* latin1 */
+ 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, /* latin1 */
+ 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, /* latin1 */
+ 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, /* latin1 */
+ 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, /* latin1 */
+ '\0'
+};
+
+
+/*
+ * lsqlite3_base160()
+ *
+ * Convert an unsigned long integer into a base160 representation of the
+ * number.
+ *
+ * Parameters:
+ * val --
+ * value to be converted
+ *
+ * result --
+ * character array, 5 bytes long, into which the base160 representation
+ * will be placed. The result will be a four-digit representation of the
+ * number (with leading zeros prepended as necessary), and null
+ * terminated.
+ *
+ * Returns:
+ * Nothing
+ */
+void
+lsqlite3_base160(unsigned long val,
+ unsigned char result[5])
+{
+ int i;
+
+ for (i = 3; i >= 0; i--) {
+
+ result[i] = base160tab[val % 160];
+ val /= 160;
+ }
+
+ result[4] = '\0';
+}
+
+
+/*
+ * lsqlite3_base160Next()
+ *
+ * Retrieve the next-greater number in the base160 sequence for the terminal
+ * tree node (the last four digits). Only one tree level (four digits) are
+ * operated on.
+ *
+ * Parameters:
+ * base160 -- a character array containing either an empty string (in which
+ * case no operation is performed), or a string of base160 digits
+ * with a length of a multiple of four digits.
+ *
+ * Upon return, the trailing four digits (one tree level) will
+ * have been incremented by 1.
+ *
+ * Returns:
+ * base160 -- the modified array
+ */
+char *
+lsqlite3_base160Next(char base160[])
+{
+ int i;
+ int len;
+ unsigned char * pTab;
+ char * pBase160 = base160;
+
+ /*
+ * We need a minimum of four digits, and we will always get a multiple of
+ * four digits.
+ */
+ if (len = strlen(pBase160)) >= 4)
+ {
+ pBase160 += strlen(pBase160) - 1;
+
+ /* We only carry through four digits: one level in the tree */
+ for (i = 0; i < 4; i++) {
+
+ /* What base160 value does this digit have? */
+ pTab = strchr(base160tab, *pBase160);
+
+ /* Is there a carry? */
+ if (pTab < base160tab + sizeof(base160tab) - 1) {
+
+ /* Nope. Just increment this value and we're done. */
+ *pBase160 = *++pTab;
+ break;
+ } else {
+
+ /*
+ * There's a carry. This value gets base160tab[0], we
+ * decrement the buffer pointer to get the next higher-order
+ * digit, and continue in the loop.
+ */
+ *pBase160-- = base160tab[0];
+ }
+ }
+ }
+
+ return base160;
+}
diff --git a/source4/lib/ldb/ldb_sqlite3/ldb_sqlite3.c b/source4/lib/ldb/ldb_sqlite3/ldb_sqlite3.c
new file mode 100644
index 0000000000..8acbac4cc3
--- /dev/null
+++ b/source4/lib/ldb/ldb_sqlite3/ldb_sqlite3.c
@@ -0,0 +1,1907 @@
+/*
+ ldb database library
+
+ Copyright (C) Derrell Lipman 2005
+ Copyright (C) Simo Sorce 2005-2009
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb sqlite3 backend
+ *
+ * Description: core files for SQLITE3 backend
+ *
+ * Author: Derrell Lipman (based on Andrew Tridgell's LDAP backend)
+ */
+
+#include "ldb_module.h"
+
+#include <sqlite3.h>
+
+struct lsqlite3_private {
+ int trans_count;
+ char **options;
+ sqlite3 *sqlite;
+};
+
+struct lsql_context {
+ struct ldb_module *module;
+ struct ldb_request *req;
+
+ /* search stuff */
+ long long current_eid;
+ const char * const * attrs;
+ struct ldb_reply *ares;
+
+ bool callback_failed;
+ struct tevent_timer *timeout_event;
+};
+
+/*
+ * Macros used throughout
+ */
+
+#ifndef FALSE
+# define FALSE (0)
+# define TRUE (! FALSE)
+#endif
+
+#define RESULT_ATTR_TABLE "temp_result_attrs"
+
+
+/* for testing, define to nothing, (create non-temporary table) */
+#define TEMPTAB "TEMPORARY"
+
+/*
+ * Static variables
+ */
+sqlite3_stmt * stmtGetEID = NULL;
+
+static char *lsqlite3_tprintf(TALLOC_CTX *mem_ctx, const char *fmt, ...)
+{
+ char *str, *ret;
+ va_list ap;
+
+ va_start(ap, fmt);
+ str = sqlite3_vmprintf(fmt, ap);
+ va_end(ap);
+
+ if (str == NULL) return NULL;
+
+ ret = talloc_strdup(mem_ctx, str);
+ if (ret == NULL) {
+ sqlite3_free(str);
+ return NULL;
+ }
+
+ sqlite3_free(str);
+ return ret;
+}
+
+static char base160tab[161] = {
+ 48 ,49 ,50 ,51 ,52 ,53 ,54 ,55 ,56 ,57 , /* 0-9 */
+ 58 ,59 ,65 ,66 ,67 ,68 ,69 ,70 ,71 ,72 , /* : ; A-H */
+ 73 ,74 ,75 ,76 ,77 ,78 ,79 ,80 ,81 ,82 , /* I-R */
+ 83 ,84 ,85 ,86 ,87 ,88 ,89 ,90 ,97 ,98 , /* S-Z , a-b */
+ 99 ,100,101,102,103,104,105,106,107,108, /* c-l */
+ 109,110,111,112,113,114,115,116,117,118, /* m-v */
+ 119,120,121,122,160,161,162,163,164,165, /* w-z, latin1 */
+ 166,167,168,169,170,171,172,173,174,175, /* latin1 */
+ 176,177,178,179,180,181,182,183,184,185, /* latin1 */
+ 186,187,188,189,190,191,192,193,194,195, /* latin1 */
+ 196,197,198,199,200,201,202,203,204,205, /* latin1 */
+ 206,207,208,209,210,211,212,213,214,215, /* latin1 */
+ 216,217,218,219,220,221,222,223,224,225, /* latin1 */
+ 226,227,228,229,230,231,232,233,234,235, /* latin1 */
+ 236,237,238,239,240,241,242,243,244,245, /* latin1 */
+ 246,247,248,249,250,251,252,253,254,255, /* latin1 */
+ '\0'
+};
+
+
+/*
+ * base160()
+ *
+ * Convert an unsigned long integer into a base160 representation of the
+ * number.
+ *
+ * Parameters:
+ * val --
+ * value to be converted
+ *
+ * result --
+ * character array, 5 bytes long, into which the base160 representation
+ * will be placed. The result will be a four-digit representation of the
+ * number (with leading zeros prepended as necessary), and null
+ * terminated.
+ *
+ * Returns:
+ * Nothing
+ */
+static void
+base160_sql(sqlite3_context * hContext,
+ int argc,
+ sqlite3_value ** argv)
+{
+ int i;
+ long long val;
+ char result[5];
+
+ val = sqlite3_value_int64(argv[0]);
+
+ for (i = 3; i >= 0; i--) {
+
+ result[i] = base160tab[val % 160];
+ val /= 160;
+ }
+
+ result[4] = '\0';
+
+ sqlite3_result_text(hContext, result, -1, SQLITE_TRANSIENT);
+}
+
+
+/*
+ * base160next_sql()
+ *
+ * This function enhances sqlite by adding a "base160_next()" function which is
+ * accessible via queries.
+ *
+ * Retrieve the next-greater number in the base160 sequence for the terminal
+ * tree node (the last four digits). Only one tree level (four digits) is
+ * operated on.
+ *
+ * Input:
+ * A character string: either an empty string (in which case no operation is
+ * performed), or a string of base160 digits with a length of a multiple of
+ * four digits.
+ *
+ * Output:
+ * Upon return, the trailing four digits (one tree level) will have been
+ * incremented by 1.
+ */
+static void
+base160next_sql(sqlite3_context * hContext,
+ int argc,
+ sqlite3_value ** argv)
+{
+ int i;
+ int len;
+ char * pTab;
+ char * pBase160 = strdup((const char *)sqlite3_value_text(argv[0]));
+ char * pStart = pBase160;
+
+ /*
+ * We need a minimum of four digits, and we will always get a multiple
+ * of four digits.
+ */
+ if (pBase160 != NULL &&
+ (len = strlen(pBase160)) >= 4 &&
+ len % 4 == 0) {
+
+ if (pBase160 == NULL) {
+
+ sqlite3_result_null(hContext);
+ return;
+ }
+
+ pBase160 += strlen(pBase160) - 1;
+
+ /* We only carry through four digits: one level in the tree */
+ for (i = 0; i < 4; i++) {
+
+ /* What base160 value does this digit have? */
+ pTab = strchr(base160tab, *pBase160);
+
+ /* Is there a carry? */
+ if (pTab < base160tab + sizeof(base160tab) - 1) {
+
+ /*
+ * Nope. Just increment this value and we're
+ * done.
+ */
+ *pBase160 = *++pTab;
+ break;
+ } else {
+
+ /*
+ * There's a carry. This value gets
+ * base160tab[0], we decrement the buffer
+ * pointer to get the next higher-order digit,
+ * and continue in the loop.
+ */
+ *pBase160-- = base160tab[0];
+ }
+ }
+
+ sqlite3_result_text(hContext,
+ pStart,
+ strlen(pStart),
+ free);
+ } else {
+ sqlite3_result_value(hContext, argv[0]);
+ if (pBase160 != NULL) {
+ free(pBase160);
+ }
+ }
+}
+
+static char *parsetree_to_sql(struct ldb_module *module,
+ void *mem_ctx,
+ const struct ldb_parse_tree *t)
+{
+ struct ldb_context *ldb;
+ const struct ldb_schema_attribute *a;
+ struct ldb_val value, subval;
+ char *wild_card_string;
+ char *child, *tmp;
+ char *ret = NULL;
+ char *attr;
+ int i;
+
+ ldb = ldb_module_get_ctx(module);
+
+ switch(t->operation) {
+ case LDB_OP_AND:
+
+ tmp = parsetree_to_sql(module, mem_ctx, t->u.list.elements[0]);
+ if (tmp == NULL) return NULL;
+
+ for (i = 1; i < t->u.list.num_elements; i++) {
+
+ child = parsetree_to_sql(module, mem_ctx, t->u.list.elements[i]);
+ if (child == NULL) return NULL;
+
+ tmp = talloc_asprintf_append(tmp, " INTERSECT %s ", child);
+ if (tmp == NULL) return NULL;
+ }
+
+ ret = talloc_asprintf(mem_ctx, "SELECT * FROM ( %s )\n", tmp);
+
+ return ret;
+
+ case LDB_OP_OR:
+
+ tmp = parsetree_to_sql(module, mem_ctx, t->u.list.elements[0]);
+ if (tmp == NULL) return NULL;
+
+ for (i = 1; i < t->u.list.num_elements; i++) {
+
+ child = parsetree_to_sql(module, mem_ctx, t->u.list.elements[i]);
+ if (child == NULL) return NULL;
+
+ tmp = talloc_asprintf_append(tmp, " UNION %s ", child);
+ if (tmp == NULL) return NULL;
+ }
+
+ return talloc_asprintf(mem_ctx, "SELECT * FROM ( %s ) ", tmp);
+
+ case LDB_OP_NOT:
+
+ child = parsetree_to_sql(module, mem_ctx, t->u.isnot.child);
+ if (child == NULL) return NULL;
+
+ return talloc_asprintf(mem_ctx,
+ "SELECT eid FROM ldb_entry "
+ "WHERE eid NOT IN ( %s ) ", child);
+
+ case LDB_OP_EQUALITY:
+ /*
+ * For simple searches, we want to retrieve the list of EIDs that
+ * match the criteria.
+ */
+ attr = ldb_attr_casefold(mem_ctx, t->u.equality.attr);
+ if (attr == NULL) return NULL;
+ a = ldb_schema_attribute_by_name(ldb, attr);
+
+ /* Get a canonicalised copy of the data */
+ a->syntax->canonicalise_fn(ldb, mem_ctx, &(t->u.equality.value), &value);
+ if (value.data == NULL) {
+ return NULL;
+ }
+
+ if (strcasecmp(t->u.equality.attr, "dn") == 0) {
+ /* DN query is a special ldb case */
+ const char *cdn = ldb_dn_get_casefold(
+ ldb_dn_new(mem_ctx, ldb,
+ (const char *)value.data));
+
+ return lsqlite3_tprintf(mem_ctx,
+ "SELECT eid FROM ldb_entry "
+ "WHERE norm_dn = '%q'", cdn);
+
+ } else {
+ /* A normal query. */
+ return lsqlite3_tprintf(mem_ctx,
+ "SELECT eid FROM ldb_attribute_values "
+ "WHERE norm_attr_name = '%q' "
+ "AND norm_attr_value = '%q'",
+ attr,
+ value.data);
+
+ }
+
+ case LDB_OP_SUBSTRING:
+
+ wild_card_string = talloc_strdup(mem_ctx,
+ (t->u.substring.start_with_wildcard)?"*":"");
+ if (wild_card_string == NULL) return NULL;
+
+ for (i = 0; t->u.substring.chunks[i]; i++) {
+ wild_card_string = talloc_asprintf_append(wild_card_string, "%s*",
+ t->u.substring.chunks[i]->data);
+ if (wild_card_string == NULL) return NULL;
+ }
+
+ if ( ! t->u.substring.end_with_wildcard ) {
+ /* remove last wildcard */
+ wild_card_string[strlen(wild_card_string) - 1] = '\0';
+ }
+
+ attr = ldb_attr_casefold(mem_ctx, t->u.substring.attr);
+ if (attr == NULL) return NULL;
+ a = ldb_schema_attribute_by_name(ldb, attr);
+
+ subval.data = (void *)wild_card_string;
+ subval.length = strlen(wild_card_string) + 1;
+
+ /* Get a canonicalised copy of the data */
+ a->syntax->canonicalise_fn(ldb, mem_ctx, &(subval), &value);
+ if (value.data == NULL) {
+ return NULL;
+ }
+
+ return lsqlite3_tprintf(mem_ctx,
+ "SELECT eid FROM ldb_attribute_values "
+ "WHERE norm_attr_name = '%q' "
+ "AND norm_attr_value GLOB '%q'",
+ attr,
+ value.data);
+
+ case LDB_OP_GREATER:
+ attr = ldb_attr_casefold(mem_ctx, t->u.equality.attr);
+ if (attr == NULL) return NULL;
+ a = ldb_schema_attribute_by_name(ldb, attr);
+
+ /* Get a canonicalised copy of the data */
+ a->syntax->canonicalise_fn(ldb, mem_ctx, &(t->u.equality.value), &value);
+ if (value.data == NULL) {
+ return NULL;
+ }
+
+ return lsqlite3_tprintf(mem_ctx,
+ "SELECT eid FROM ldb_attribute_values "
+ "WHERE norm_attr_name = '%q' "
+ "AND ldap_compare(norm_attr_value, '>=', '%q', '%q') ",
+ attr,
+ value.data,
+ attr);
+
+ case LDB_OP_LESS:
+ attr = ldb_attr_casefold(mem_ctx, t->u.equality.attr);
+ if (attr == NULL) return NULL;
+ a = ldb_schema_attribute_by_name(ldb, attr);
+
+ /* Get a canonicalised copy of the data */
+ a->syntax->canonicalise_fn(ldb, mem_ctx, &(t->u.equality.value), &value);
+ if (value.data == NULL) {
+ return NULL;
+ }
+
+ return lsqlite3_tprintf(mem_ctx,
+ "SELECT eid FROM ldb_attribute_values "
+ "WHERE norm_attr_name = '%q' "
+ "AND ldap_compare(norm_attr_value, '<=', '%q', '%q') ",
+ attr,
+ value.data,
+ attr);
+
+ case LDB_OP_PRESENT:
+ if (strcasecmp(t->u.present.attr, "dn") == 0) {
+ return talloc_strdup(mem_ctx, "SELECT eid FROM ldb_entry");
+ }
+
+ attr = ldb_attr_casefold(mem_ctx, t->u.present.attr);
+ if (attr == NULL) return NULL;
+
+ return lsqlite3_tprintf(mem_ctx,
+ "SELECT eid FROM ldb_attribute_values "
+ "WHERE norm_attr_name = '%q' ",
+ attr);
+
+ case LDB_OP_APPROX:
+ attr = ldb_attr_casefold(mem_ctx, t->u.equality.attr);
+ if (attr == NULL) return NULL;
+ a = ldb_schema_attribute_by_name(ldb, attr);
+
+ /* Get a canonicalised copy of the data */
+ a->syntax->canonicalise_fn(ldb, mem_ctx, &(t->u.equality.value), &value);
+ if (value.data == NULL) {
+ return NULL;
+ }
+
+ return lsqlite3_tprintf(mem_ctx,
+ "SELECT eid FROM ldb_attribute_values "
+ "WHERE norm_attr_name = '%q' "
+ "AND ldap_compare(norm_attr_value, '~%', 'q', '%q') ",
+ attr,
+ value.data,
+ attr);
+
+ case LDB_OP_EXTENDED:
+#warning "work out how to handle bitops"
+ return NULL;
+
+ default:
+ break;
+ };
+
+ /* should never occur */
+ abort();
+ return NULL;
+}
+
+/*
+ * query_int()
+ *
+ * This function is used for the common case of queries that return a single
+ * integer value.
+ *
+ * NOTE: If more than one value is returned by the query, all but the first
+ * one will be ignored.
+ */
+static int
+query_int(const struct lsqlite3_private * lsqlite3,
+ long long * pRet,
+ const char * pSql,
+ ...)
+{
+ int ret;
+ int bLoop;
+ char * p;
+ sqlite3_stmt * pStmt;
+ va_list args;
+
+ /* Begin access to variable argument list */
+ va_start(args, pSql);
+
+ /* Format the query */
+ if ((p = sqlite3_vmprintf(pSql, args)) == NULL) {
+ return SQLITE_NOMEM;
+ }
+
+ /*
+ * Prepare and execute the SQL statement. Loop allows retrying on
+ * certain errors, e.g. SQLITE_SCHEMA occurs if the schema changes,
+ * requiring retrying the operation.
+ */
+ for (bLoop = TRUE; bLoop; ) {
+
+ /* Compile the SQL statement into sqlite virtual machine */
+ if ((ret = sqlite3_prepare(lsqlite3->sqlite,
+ p,
+ -1,
+ &pStmt,
+ NULL)) == SQLITE_SCHEMA) {
+ if (stmtGetEID != NULL) {
+ sqlite3_finalize(stmtGetEID);
+ stmtGetEID = NULL;
+ }
+ continue;
+ } else if (ret != SQLITE_OK) {
+ break;
+ }
+
+ /* One row expected */
+ if ((ret = sqlite3_step(pStmt)) == SQLITE_SCHEMA) {
+ if (stmtGetEID != NULL) {
+ sqlite3_finalize(stmtGetEID);
+ stmtGetEID = NULL;
+ }
+ (void) sqlite3_finalize(pStmt);
+ continue;
+ } else if (ret != SQLITE_ROW) {
+ (void) sqlite3_finalize(pStmt);
+ break;
+ }
+
+ /* Get the value to be returned */
+ *pRet = sqlite3_column_int64(pStmt, 0);
+
+ /* Free the virtual machine */
+ if ((ret = sqlite3_finalize(pStmt)) == SQLITE_SCHEMA) {
+ if (stmtGetEID != NULL) {
+ sqlite3_finalize(stmtGetEID);
+ stmtGetEID = NULL;
+ }
+ continue;
+ } else if (ret != SQLITE_OK) {
+ (void) sqlite3_finalize(pStmt);
+ break;
+ }
+
+ /*
+ * Normal condition is only one time through loop. Loop is
+ * rerun in error conditions, via "continue", above.
+ */
+ bLoop = FALSE;
+ }
+
+ /* All done with variable argument list */
+ va_end(args);
+
+
+ /* Free the memory we allocated for our query string */
+ sqlite3_free(p);
+
+ return ret;
+}
+
+/*
+ * This is a bad hack to support ldap style comparisons whithin sqlite.
+ * val is the attribute in the row currently under test
+ * func is the desired test "<=" ">=" "~" ":"
+ * cmp is the value to compare against (eg: "test")
+ * attr is the attribute name the value of which we want to test
+ */
+
+static void lsqlite3_compare(sqlite3_context *ctx, int argc,
+ sqlite3_value **argv)
+{
+ struct ldb_context *ldb = (struct ldb_context *)sqlite3_user_data(ctx);
+ const char *val = (const char *)sqlite3_value_text(argv[0]);
+ const char *func = (const char *)sqlite3_value_text(argv[1]);
+ const char *cmp = (const char *)sqlite3_value_text(argv[2]);
+ const char *attr = (const char *)sqlite3_value_text(argv[3]);
+ const struct ldb_schema_attribute *a;
+ struct ldb_val valX;
+ struct ldb_val valY;
+ int ret;
+
+ switch (func[0]) {
+ /* greater */
+ case '>': /* >= */
+ a = ldb_schema_attribute_by_name(ldb, attr);
+ valX.data = (uint8_t *)cmp;
+ valX.length = strlen(cmp);
+ valY.data = (uint8_t *)val;
+ valY.length = strlen(val);
+ ret = a->syntax->comparison_fn(ldb, ldb, &valY, &valX);
+ if (ret >= 0)
+ sqlite3_result_int(ctx, 1);
+ else
+ sqlite3_result_int(ctx, 0);
+ return;
+
+ /* lesser */
+ case '<': /* <= */
+ a = ldb_schema_attribute_by_name(ldb, attr);
+ valX.data = (uint8_t *)cmp;
+ valX.length = strlen(cmp);
+ valY.data = (uint8_t *)val;
+ valY.length = strlen(val);
+ ret = a->syntax->comparison_fn(ldb, ldb, &valY, &valX);
+ if (ret <= 0)
+ sqlite3_result_int(ctx, 1);
+ else
+ sqlite3_result_int(ctx, 0);
+ return;
+
+ /* approx */
+ case '~':
+ /* TODO */
+ sqlite3_result_int(ctx, 0);
+ return;
+
+ /* bitops */
+ case ':':
+ /* TODO */
+ sqlite3_result_int(ctx, 0);
+ return;
+
+ default:
+ break;
+ }
+
+ sqlite3_result_error(ctx, "Value must start with a special operation char (<>~:)!", -1);
+ return;
+}
+
+
+/* rename a record */
+static int lsqlite3_safe_rollback(sqlite3 *sqlite)
+{
+ char *errmsg;
+ int ret;
+
+ /* execute */
+ ret = sqlite3_exec(sqlite, "ROLLBACK;", NULL, NULL, &errmsg);
+ if (ret != SQLITE_OK) {
+ if (errmsg) {
+ printf("lsqlite3_safe_rollback: Error: %s\n", errmsg);
+ free(errmsg);
+ }
+ return -1;
+ }
+
+ return 0;
+}
+
+/* return an eid as result */
+static int lsqlite3_eid_callback(void *result, int col_num, char **cols, char **names)
+{
+ long long *eid = (long long *)result;
+
+ if (col_num != 1) return SQLITE_ABORT;
+ if (strcasecmp(names[0], "eid") != 0) return SQLITE_ABORT;
+
+ *eid = atoll(cols[0]);
+ return SQLITE_OK;
+}
+
+/*
+ * add a single set of ldap message values to a ldb_message
+ */
+static int lsqlite3_search_callback(void *result, int col_num, char **cols, char **names)
+{
+ struct ldb_context *ldb;
+ struct lsql_context *ac;
+ struct ldb_message *msg;
+ long long eid;
+ int i, ret;
+
+ ac = talloc_get_type(result, struct lsql_context);
+ ldb = ldb_module_get_ctx(ac->module);
+
+ /* eid, dn, attr_name, attr_value */
+ if (col_num != 4) return SQLITE_ABORT;
+
+ eid = atoll(cols[0]);
+
+ if (ac->ares) {
+ msg = ac->ares->message;
+ }
+
+ if (eid != ac->current_eid) { /* here begin a new entry */
+
+ /* call the async callback for the last entry
+ * except the first time */
+ if (ac->current_eid != 0) {
+ msg = ldb_msg_canonicalize(ldb, msg);
+ if (!msg) return SQLITE_ABORT;
+
+ ret = ldb_module_send_entry(ac->req, msg, NULL);
+ if (ret != LDB_SUCCESS) {
+ ac->callback_failed = true;
+ return SQLITE_ABORT;
+ }
+ }
+
+ /* start over */
+ ac->ares = talloc_zero(ac, struct ldb_reply);
+ if (!ac->ares) return SQLITE_ABORT;
+
+ msg = ldb_msg_new(ac->ares);
+ if (!msg) return SQLITE_ABORT;
+
+ ac->ares->type = LDB_REPLY_ENTRY;
+ ac->current_eid = eid;
+ }
+
+ if (msg->dn == NULL) {
+ msg->dn = ldb_dn_new(msg, ldb, cols[1]);
+ if (msg->dn == NULL)
+ return SQLITE_ABORT;
+ }
+
+ if (ac->attrs) {
+ int found = 0;
+ for (i = 0; ac->attrs[i]; i++) {
+ if (strcasecmp(cols[2], ac->attrs[i]) == 0) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) goto done;
+ }
+
+ if (ldb_msg_add_string(msg, cols[2], cols[3]) != 0) {
+ return SQLITE_ABORT;
+ }
+
+done:
+ ac->ares->message = msg;
+ return SQLITE_OK;
+}
+
+
+/*
+ * lsqlite3_get_eid()
+ * lsqlite3_get_eid_ndn()
+ *
+ * These functions are used for the very common case of retrieving an EID value
+ * given a (normalized) DN.
+ */
+
+static long long lsqlite3_get_eid_ndn(sqlite3 *sqlite, void *mem_ctx, const char *norm_dn)
+{
+ char *errmsg;
+ char *query;
+ long long eid = -1;
+ long long ret;
+
+ /* get object eid */
+ query = lsqlite3_tprintf(mem_ctx, "SELECT eid "
+ "FROM ldb_entry "
+ "WHERE norm_dn = '%q';", norm_dn);
+ if (query == NULL) return -1;
+
+ ret = sqlite3_exec(sqlite, query, lsqlite3_eid_callback, &eid, &errmsg);
+ if (ret != SQLITE_OK) {
+ if (errmsg) {
+ printf("lsqlite3_get_eid: Fatal Error: %s\n", errmsg);
+ free(errmsg);
+ }
+ return -1;
+ }
+
+ return eid;
+}
+
+static long long lsqlite3_get_eid(struct lsqlite3_private *lsqlite3,
+ struct ldb_dn *dn)
+{
+ TALLOC_CTX *local_ctx;
+ long long eid = -1;
+ char *cdn;
+
+ /* ignore ltdb specials */
+ if (ldb_dn_is_special(dn)) {
+ return -1;
+ }
+
+ /* create a local ctx */
+ local_ctx = talloc_named(lsqlite3, 0, "lsqlite3_get_eid local context");
+ if (local_ctx == NULL) {
+ return -1;
+ }
+
+ cdn = ldb_dn_alloc_casefold(local_ctx, dn);
+ if (!cdn) goto done;
+
+ eid = lsqlite3_get_eid_ndn(lsqlite3->sqlite, local_ctx, cdn);
+
+done:
+ talloc_free(local_ctx);
+ return eid;
+}
+
+/*
+ * Interface functions referenced by lsqlite3_ops
+ */
+
+/* search for matching records, by tree */
+int lsql_search(struct lsql_context *ctx)
+{
+ struct ldb_module *module = ctx->module;
+ struct ldb_request *req = ctx->req;
+ struct lsqlite3_private *lsqlite3;
+ struct ldb_context *ldb;
+ char *norm_basedn;
+ char *sqlfilter;
+ char *errmsg;
+ char *query = NULL;
+ int ret;
+
+ ldb = ldb_module_get_ctx(module);
+ lsqlite3 = talloc_get_type(ldb_module_get_private(module),
+ struct lsqlite3_private);
+
+ if ((( ! ldb_dn_is_valid(req->op.search.base)) ||
+ ldb_dn_is_null(req->op.search.base)) &&
+ (req->op.search.scope == LDB_SCOPE_BASE ||
+ req->op.search.scope == LDB_SCOPE_ONELEVEL)) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if (req->op.search.base) {
+ norm_basedn = ldb_dn_alloc_casefold(ctx, req->op.search.base);
+ if (norm_basedn == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ } else norm_basedn = talloc_strdup(ctx, "");
+
+ /* Convert filter into a series of SQL conditions (constraints) */
+ sqlfilter = parsetree_to_sql(module, ctx, req->op.search.tree);
+
+ switch(req->op.search.scope) {
+ case LDB_SCOPE_DEFAULT:
+ case LDB_SCOPE_SUBTREE:
+ if (*norm_basedn != '\0') {
+ query = lsqlite3_tprintf(ctx,
+ "SELECT entry.eid,\n"
+ " entry.dn,\n"
+ " av.attr_name,\n"
+ " av.attr_value\n"
+ " FROM ldb_entry AS entry\n"
+
+ " LEFT OUTER JOIN ldb_attribute_values AS av\n"
+ " ON av.eid = entry.eid\n"
+
+ " WHERE entry.eid IN\n"
+ " (SELECT DISTINCT ldb_entry.eid\n"
+ " FROM ldb_entry\n"
+ " WHERE (ldb_entry.norm_dn GLOB('*,%q')\n"
+ " OR ldb_entry.norm_dn = '%q')\n"
+ " AND ldb_entry.eid IN\n"
+ " (%s)\n"
+ " )\n"
+
+ " ORDER BY entry.eid ASC;",
+ norm_basedn,
+ norm_basedn,
+ sqlfilter);
+ } else {
+ query = lsqlite3_tprintf(ctx,
+ "SELECT entry.eid,\n"
+ " entry.dn,\n"
+ " av.attr_name,\n"
+ " av.attr_value\n"
+ " FROM ldb_entry AS entry\n"
+
+ " LEFT OUTER JOIN ldb_attribute_values AS av\n"
+ " ON av.eid = entry.eid\n"
+
+ " WHERE entry.eid IN\n"
+ " (SELECT DISTINCT ldb_entry.eid\n"
+ " FROM ldb_entry\n"
+ " WHERE ldb_entry.eid IN\n"
+ " (%s)\n"
+ " )\n"
+
+ " ORDER BY entry.eid ASC;",
+ sqlfilter);
+ }
+
+ break;
+
+ case LDB_SCOPE_BASE:
+ query = lsqlite3_tprintf(ctx,
+ "SELECT entry.eid,\n"
+ " entry.dn,\n"
+ " av.attr_name,\n"
+ " av.attr_value\n"
+ " FROM ldb_entry AS entry\n"
+
+ " LEFT OUTER JOIN ldb_attribute_values AS av\n"
+ " ON av.eid = entry.eid\n"
+
+ " WHERE entry.eid IN\n"
+ " (SELECT DISTINCT ldb_entry.eid\n"
+ " FROM ldb_entry\n"
+ " WHERE ldb_entry.norm_dn = '%q'\n"
+ " AND ldb_entry.eid IN\n"
+ " (%s)\n"
+ " )\n"
+
+ " ORDER BY entry.eid ASC;",
+ norm_basedn,
+ sqlfilter);
+ break;
+
+ case LDB_SCOPE_ONELEVEL:
+ query = lsqlite3_tprintf(ctx,
+ "SELECT entry.eid,\n"
+ " entry.dn,\n"
+ " av.attr_name,\n"
+ " av.attr_value\n"
+ " FROM ldb_entry AS entry\n"
+
+ " LEFT OUTER JOIN ldb_attribute_values AS av\n"
+ " ON av.eid = entry.eid\n"
+
+ " WHERE entry.eid IN\n"
+ " (SELECT DISTINCT ldb_entry.eid\n"
+ " FROM ldb_entry\n"
+ " WHERE norm_dn GLOB('*,%q')\n"
+ " AND NOT norm_dn GLOB('*,*,%q')\n"
+ " AND ldb_entry.eid IN\n(%s)\n"
+ " )\n"
+
+ " ORDER BY entry.eid ASC;",
+ norm_basedn,
+ norm_basedn,
+ sqlfilter);
+ break;
+ }
+
+ if (query == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* * /
+ printf ("%s\n", query);
+ / * */
+
+ ctx->current_eid = 0;
+ ctx->attrs = req->op.search.attrs;
+ ctx->ares = NULL;
+
+ ldb_request_set_state(req, LDB_ASYNC_PENDING);
+
+ ret = sqlite3_exec(lsqlite3->sqlite, query, lsqlite3_search_callback, ctx, &errmsg);
+ if (ret != SQLITE_OK) {
+ if (errmsg) {
+ ldb_set_errstring(ldb, errmsg);
+ free(errmsg);
+ }
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* complete the last message if any */
+ if (ctx->ares) {
+ ctx->ares->message = ldb_msg_canonicalize(ldb, ctx->ares->message);
+ if (ctx->ares->message == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ldb_module_send_entry(req, ctx->ares->message, NULL);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+
+
+ return LDB_SUCCESS;
+}
+
+/* add a record */
+static int lsql_add(struct lsql_context *ctx)
+{
+ struct ldb_module *module = ctx->module;
+ struct ldb_request *req = ctx->req;
+ struct lsqlite3_private *lsqlite3;
+ struct ldb_context *ldb;
+ struct ldb_message *msg = req->op.add.message;
+ long long eid;
+ char *dn, *ndn;
+ char *errmsg;
+ char *query;
+ int i;
+ int ret;
+
+ ldb = ldb_module_get_ctx(module);
+ lsqlite3 = talloc_get_type(ldb_module_get_private(module),
+ struct lsqlite3_private);
+
+ /* See if this is an ltdb special */
+ if (ldb_dn_is_special(msg->dn)) {
+/*
+ struct ldb_dn *c;
+ c = ldb_dn_new(local_ctx, ldb, "@INDEXLIST");
+ if (ldb_dn_compare(ldb, msg->dn, c) == 0) {
+#warning "should we handle indexes somehow ?"
+ ret = LDB_ERR_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+*/
+ /* Others return an error */
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+
+ /* create linearized and normalized dns */
+ dn = ldb_dn_alloc_linearized(ctx, msg->dn);
+ ndn = ldb_dn_alloc_casefold(ctx, msg->dn);
+ if (dn == NULL || ndn == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ query = lsqlite3_tprintf(ctx,
+ /* Add new entry */
+ "INSERT OR ABORT INTO ldb_entry "
+ "('dn', 'norm_dn') "
+ "VALUES ('%q', '%q');",
+ dn, ndn);
+ if (query == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = sqlite3_exec(lsqlite3->sqlite, query, NULL, NULL, &errmsg);
+ if (ret != SQLITE_OK) {
+ if (errmsg) {
+ ldb_set_errstring(ldb, errmsg);
+ free(errmsg);
+ }
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ eid = lsqlite3_get_eid_ndn(lsqlite3->sqlite, ctx, ndn);
+ if (eid == -1) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ for (i = 0; i < msg->num_elements; i++) {
+ const struct ldb_message_element *el = &msg->elements[i];
+ const struct ldb_schema_attribute *a;
+ char *attr;
+ int j;
+
+ /* Get a case-folded copy of the attribute name */
+ attr = ldb_attr_casefold(ctx, el->name);
+ if (attr == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ a = ldb_schema_attribute_by_name(ldb, el->name);
+
+ /* For each value of the specified attribute name... */
+ for (j = 0; j < el->num_values; j++) {
+ struct ldb_val value;
+ char *insert;
+
+ /* Get a canonicalised copy of the data */
+ a->syntax->canonicalise_fn(ldb, ctx, &(el->values[j]), &value);
+ if (value.data == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ insert = lsqlite3_tprintf(ctx,
+ "INSERT OR ROLLBACK INTO ldb_attribute_values "
+ "('eid', 'attr_name', 'norm_attr_name',"
+ " 'attr_value', 'norm_attr_value') "
+ "VALUES ('%lld', '%q', '%q', '%q', '%q');",
+ eid, el->name, attr,
+ el->values[j].data, value.data);
+ if (insert == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = sqlite3_exec(lsqlite3->sqlite, insert, NULL, NULL, &errmsg);
+ if (ret != SQLITE_OK) {
+ if (errmsg) {
+ ldb_set_errstring(ldb, errmsg);
+ free(errmsg);
+ }
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ }
+ }
+
+ return LDB_SUCCESS;
+}
+
+/* modify a record */
+static int lsql_modify(struct lsql_context *ctx)
+{
+ struct ldb_module *module = ctx->module;
+ struct ldb_request *req = ctx->req;
+ struct lsqlite3_private *lsqlite3;
+ struct ldb_context *ldb;
+ struct ldb_message *msg = req->op.mod.message;
+ long long eid;
+ char *errmsg;
+ int i;
+ int ret;
+
+ ldb = ldb_module_get_ctx(module);
+ lsqlite3 = talloc_get_type(ldb_module_get_private(module),
+ struct lsqlite3_private);
+
+ /* See if this is an ltdb special */
+ if (ldb_dn_is_special(msg->dn)) {
+ /* Others return an error */
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+
+ eid = lsqlite3_get_eid(lsqlite3, msg->dn);
+ if (eid == -1) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ for (i = 0; i < msg->num_elements; i++) {
+ const struct ldb_message_element *el = &msg->elements[i];
+ const struct ldb_schema_attribute *a;
+ int flags = el->flags & LDB_FLAG_MOD_MASK;
+ char *attr;
+ char *mod;
+ int j;
+
+ /* Get a case-folded copy of the attribute name */
+ attr = ldb_attr_casefold(ctx, el->name);
+ if (attr == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ a = ldb_schema_attribute_by_name(ldb, el->name);
+
+ switch (flags) {
+
+ case LDB_FLAG_MOD_REPLACE:
+
+ /* remove all attributes before adding the replacements */
+ mod = lsqlite3_tprintf(ctx,
+ "DELETE FROM ldb_attribute_values "
+ "WHERE eid = '%lld' "
+ "AND norm_attr_name = '%q';",
+ eid, attr);
+ if (mod == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = sqlite3_exec(lsqlite3->sqlite, mod, NULL, NULL, &errmsg);
+ if (ret != SQLITE_OK) {
+ if (errmsg) {
+ ldb_set_errstring(ldb, errmsg);
+ free(errmsg);
+ }
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* MISSING break is INTENTIONAL */
+
+ case LDB_FLAG_MOD_ADD:
+#warning "We should throw an error if no value is provided!"
+ /* For each value of the specified attribute name... */
+ for (j = 0; j < el->num_values; j++) {
+ struct ldb_val value;
+
+ /* Get a canonicalised copy of the data */
+ a->syntax->canonicalise_fn(ldb, ctx, &(el->values[j]), &value);
+ if (value.data == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ mod = lsqlite3_tprintf(ctx,
+ "INSERT OR ROLLBACK INTO ldb_attribute_values "
+ "('eid', 'attr_name', 'norm_attr_name',"
+ " 'attr_value', 'norm_attr_value') "
+ "VALUES ('%lld', '%q', '%q', '%q', '%q');",
+ eid, el->name, attr,
+ el->values[j].data, value.data);
+
+ if (mod == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = sqlite3_exec(lsqlite3->sqlite, mod, NULL, NULL, &errmsg);
+ if (ret != SQLITE_OK) {
+ if (errmsg) {
+ ldb_set_errstring(ldb, errmsg);
+ free(errmsg);
+ }
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ }
+
+ break;
+
+ case LDB_FLAG_MOD_DELETE:
+#warning "We should throw an error if the attribute we are trying to delete does not exist!"
+ if (el->num_values == 0) {
+ mod = lsqlite3_tprintf(ctx,
+ "DELETE FROM ldb_attribute_values "
+ "WHERE eid = '%lld' "
+ "AND norm_attr_name = '%q';",
+ eid, attr);
+ if (mod == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = sqlite3_exec(lsqlite3->sqlite, mod, NULL, NULL, &errmsg);
+ if (ret != SQLITE_OK) {
+ if (errmsg) {
+ ldb_set_errstring(ldb, errmsg);
+ free(errmsg);
+ }
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ }
+
+ /* For each value of the specified attribute name... */
+ for (j = 0; j < el->num_values; j++) {
+ struct ldb_val value;
+
+ /* Get a canonicalised copy of the data */
+ a->syntax->canonicalise_fn(ldb, ctx, &(el->values[j]), &value);
+ if (value.data == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ mod = lsqlite3_tprintf(ctx,
+ "DELETE FROM ldb_attribute_values "
+ "WHERE eid = '%lld' "
+ "AND norm_attr_name = '%q' "
+ "AND norm_attr_value = '%q';",
+ eid, attr, value.data);
+
+ if (mod == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = sqlite3_exec(lsqlite3->sqlite, mod, NULL, NULL, &errmsg);
+ if (ret != SQLITE_OK) {
+ if (errmsg) {
+ ldb_set_errstring(ldb, errmsg);
+ free(errmsg);
+ }
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ }
+
+ break;
+ }
+ }
+
+ return LDB_SUCCESS;
+}
+
+/* delete a record */
+static int lsql_delete(struct lsql_context *ctx)
+{
+ struct ldb_module *module = ctx->module;
+ struct ldb_request *req = ctx->req;
+ struct lsqlite3_private *lsqlite3;
+ struct ldb_context *ldb;
+ long long eid;
+ char *errmsg;
+ char *query;
+ int ret;
+
+ ldb = ldb_module_get_ctx(module);
+ lsqlite3 = talloc_get_type(ldb_module_get_private(module),
+ struct lsqlite3_private);
+
+ eid = lsqlite3_get_eid(lsqlite3, req->op.del.dn);
+ if (eid == -1) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ query = lsqlite3_tprintf(ctx,
+ /* Delete entry */
+ "DELETE FROM ldb_entry WHERE eid = %lld; "
+ /* Delete attributes */
+ "DELETE FROM ldb_attribute_values WHERE eid = %lld; ",
+ eid, eid);
+ if (query == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = sqlite3_exec(lsqlite3->sqlite, query, NULL, NULL, &errmsg);
+ if (ret != SQLITE_OK) {
+ if (errmsg) {
+ ldb_set_errstring(ldb, errmsg);
+ free(errmsg);
+ }
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ return LDB_SUCCESS;
+}
+
+/* rename a record */
+static int lsql_rename(struct lsql_context *ctx)
+{
+ struct ldb_module *module = ctx->module;
+ struct ldb_request *req = ctx->req;
+ struct lsqlite3_private *lsqlite3;
+ struct ldb_context *ldb;
+ char *new_dn, *new_cdn, *old_cdn;
+ char *errmsg;
+ char *query;
+ int ret;
+
+ ldb = ldb_module_get_ctx(module);
+ lsqlite3 = talloc_get_type(ldb_module_get_private(module),
+ struct lsqlite3_private);
+
+ /* create linearized and normalized dns */
+ old_cdn = ldb_dn_alloc_casefold(ctx, req->op.rename.olddn);
+ new_cdn = ldb_dn_alloc_casefold(ctx, req->op.rename.newdn);
+ new_dn = ldb_dn_alloc_linearized(ctx, req->op.rename.newdn);
+ if (old_cdn == NULL || new_cdn == NULL || new_dn == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* build the SQL query */
+ query = lsqlite3_tprintf(ctx,
+ "UPDATE ldb_entry SET dn = '%q', norm_dn = '%q' "
+ "WHERE norm_dn = '%q';",
+ new_dn, new_cdn, old_cdn);
+ if (query == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* execute */
+ ret = sqlite3_exec(lsqlite3->sqlite, query, NULL, NULL, &errmsg);
+ if (ret != SQLITE_OK) {
+ if (errmsg) {
+ ldb_set_errstring(ldb, errmsg);
+ free(errmsg);
+ }
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ return LDB_SUCCESS;
+}
+
+static int lsql_start_trans(struct ldb_module * module)
+{
+ int ret;
+ char *errmsg;
+ struct lsqlite3_private *lsqlite3;
+
+ lsqlite3 = talloc_get_type(ldb_module_get_private(module),
+ struct lsqlite3_private);
+
+ if (lsqlite3->trans_count == 0) {
+ ret = sqlite3_exec(lsqlite3->sqlite, "BEGIN IMMEDIATE;", NULL, NULL, &errmsg);
+ if (ret != SQLITE_OK) {
+ if (errmsg) {
+ printf("lsqlite3_start_trans: error: %s\n", errmsg);
+ free(errmsg);
+ }
+ return -1;
+ }
+ };
+
+ lsqlite3->trans_count++;
+
+ return 0;
+}
+
+static int lsql_end_trans(struct ldb_module *module)
+{
+ int ret;
+ char *errmsg;
+ struct lsqlite3_private *lsqlite3;
+
+ lsqlite3 = talloc_get_type(ldb_module_get_private(module),
+ struct lsqlite3_private);
+
+ if (lsqlite3->trans_count > 0) {
+ lsqlite3->trans_count--;
+ } else return -1;
+
+ if (lsqlite3->trans_count == 0) {
+ ret = sqlite3_exec(lsqlite3->sqlite, "COMMIT;", NULL, NULL, &errmsg);
+ if (ret != SQLITE_OK) {
+ if (errmsg) {
+ printf("lsqlite3_end_trans: error: %s\n", errmsg);
+ free(errmsg);
+ }
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int lsql_del_trans(struct ldb_module *module)
+{
+ struct lsqlite3_private *lsqlite3;
+
+ lsqlite3 = talloc_get_type(ldb_module_get_private(module),
+ struct lsqlite3_private);
+
+ if (lsqlite3->trans_count > 0) {
+ lsqlite3->trans_count--;
+ } else return -1;
+
+ if (lsqlite3->trans_count == 0) {
+ return lsqlite3_safe_rollback(lsqlite3->sqlite);
+ }
+
+ return -1;
+}
+
+static int destructor(struct lsqlite3_private *lsqlite3)
+{
+ if (lsqlite3->sqlite) {
+ sqlite3_close(lsqlite3->sqlite);
+ }
+ return 0;
+}
+
+static void lsql_request_done(struct lsql_context *ctx, int error)
+{
+ struct ldb_context *ldb;
+ struct ldb_request *req;
+ struct ldb_reply *ares;
+
+ ldb = ldb_module_get_ctx(ctx->module);
+ req = ctx->req;
+
+ /* if we already returned an error just return */
+ if (ldb_request_get_status(req) != LDB_SUCCESS) {
+ return;
+ }
+
+ ares = talloc_zero(req, struct ldb_reply);
+ if (!ares) {
+ ldb_oom(ldb);
+ req->callback(req, NULL);
+ return;
+ }
+ ares->type = LDB_REPLY_DONE;
+ ares->error = error;
+
+ req->callback(req, ares);
+}
+
+static void lsql_timeout(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval t,
+ void *private_data)
+{
+ struct lsql_context *ctx;
+ ctx = talloc_get_type(private_data, struct lsql_context);
+
+ lsql_request_done(ctx, LDB_ERR_TIME_LIMIT_EXCEEDED);
+}
+
+static void lsql_callback(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval t,
+ void *private_data)
+{
+ struct lsql_context *ctx;
+ int ret;
+
+ ctx = talloc_get_type(private_data, struct lsql_context);
+
+ switch (ctx->req->operation) {
+ case LDB_SEARCH:
+ ret = lsql_search(ctx);
+ break;
+ case LDB_ADD:
+ ret = lsql_add(ctx);
+ break;
+ case LDB_MODIFY:
+ ret = lsql_modify(ctx);
+ break;
+ case LDB_DELETE:
+ ret = lsql_delete(ctx);
+ break;
+ case LDB_RENAME:
+ ret = lsql_rename(ctx);
+ break;
+/* TODO:
+ case LDB_EXTENDED:
+ ret = lsql_extended(ctx);
+ break;
+ */
+ default:
+ /* no other op supported */
+ ret = LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+
+ if (!ctx->callback_failed) {
+ /* Once we are done, we do not need timeout events */
+ talloc_free(ctx->timeout_event);
+ lsql_request_done(ctx, ret);
+ }
+}
+
+static int lsql_handle_request(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct tevent_context *ev;
+ struct lsql_context *ac;
+ struct tevent_timer *te;
+ struct timeval tv;
+
+ if (check_critical_controls(req->controls)) {
+ return LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION;
+ }
+
+ if (req->starttime == 0 || req->timeout == 0) {
+ ldb_set_errstring(ldb, "Invalid timeout settings");
+ return LDB_ERR_TIME_LIMIT_EXCEEDED;
+ }
+
+ ldb = ldb_module_get_ctx(module);
+ ev = ldb_get_event_context(ldb);
+
+ ac = talloc_zero(req, struct lsql_context);
+ if (ac == NULL) {
+ ldb_set_errstring(ldb, "Out of Memory");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->module = module;
+ ac->req = req;
+
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ te = tevent_add_timer(ev, ac, tv, lsql_callback, ac);
+ if (NULL == te) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ tv.tv_sec = req->starttime + req->timeout;
+ ac->timeout_event = tevent_add_timer(ev, ac, tv, lsql_timeout, ac);
+ if (NULL == ac->timeout_event) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ return LDB_SUCCESS;
+}
+
+/*
+ * Table of operations for the sqlite3 backend
+ */
+static const struct ldb_module_ops lsqlite3_ops = {
+ .name = "sqlite",
+ .search = lsql_handle_request,
+ .add = lsql_handle_request,
+ .modify = lsql_handle_request,
+ .del = lsql_handle_request,
+ .rename = lsql_handle_request,
+ .extended = lsql_handle_request,
+ .start_transaction = lsql_start_trans,
+ .end_transaction = lsql_end_trans,
+ .del_transaction = lsql_del_trans,
+};
+
+/*
+ * Static functions
+ */
+
+static int initialize(struct lsqlite3_private *lsqlite3,
+ struct ldb_context *ldb, const char *url, int flags)
+{
+ TALLOC_CTX *local_ctx;
+ long long queryInt;
+ int rollback = 0;
+ char *errmsg;
+ char *schema;
+ int ret;
+
+ /* create a local ctx */
+ local_ctx = talloc_named(lsqlite3, 0, "lsqlite3_rename local context");
+ if (local_ctx == NULL) {
+ return -1;
+ }
+
+ schema = lsqlite3_tprintf(local_ctx,
+
+
+ "CREATE TABLE ldb_info AS "
+ " SELECT 'LDB' AS database_type,"
+ " '1.0' AS version;"
+
+ /*
+ * The entry table holds the information about an entry.
+ * This table is used to obtain the EID of the entry and to
+ * support scope=one and scope=base. The parent and child
+ * table is included in the entry table since all the other
+ * attributes are dependent on EID.
+ */
+ "CREATE TABLE ldb_entry "
+ "("
+ " eid INTEGER PRIMARY KEY AUTOINCREMENT,"
+ " dn TEXT UNIQUE NOT NULL,"
+ " norm_dn TEXT UNIQUE NOT NULL"
+ ");"
+
+
+ "CREATE TABLE ldb_object_classes"
+ "("
+ " class_name TEXT PRIMARY KEY,"
+ " parent_class_name TEXT,"
+ " tree_key TEXT UNIQUE,"
+ " max_child_num INTEGER DEFAULT 0"
+ ");"
+
+ /*
+ * We keep a full listing of attribute/value pairs here
+ */
+ "CREATE TABLE ldb_attribute_values"
+ "("
+ " eid INTEGER REFERENCES ldb_entry,"
+ " attr_name TEXT,"
+ " norm_attr_name TEXT,"
+ " attr_value TEXT,"
+ " norm_attr_value TEXT "
+ ");"
+
+
+ /*
+ * Indexes
+ */
+ "CREATE INDEX ldb_attribute_values_eid_idx "
+ " ON ldb_attribute_values (eid);"
+
+ "CREATE INDEX ldb_attribute_values_name_value_idx "
+ " ON ldb_attribute_values (attr_name, norm_attr_value);"
+
+
+
+ /*
+ * Triggers
+ */
+
+ "CREATE TRIGGER ldb_object_classes_insert_tr"
+ " AFTER INSERT"
+ " ON ldb_object_classes"
+ " FOR EACH ROW"
+ " BEGIN"
+ " UPDATE ldb_object_classes"
+ " SET tree_key = COALESCE(tree_key, "
+ " ("
+ " SELECT tree_key || "
+ " (SELECT base160(max_child_num + 1)"
+ " FROM ldb_object_classes"
+ " WHERE class_name = "
+ " new.parent_class_name)"
+ " FROM ldb_object_classes "
+ " WHERE class_name = new.parent_class_name "
+ " ));"
+ " UPDATE ldb_object_classes "
+ " SET max_child_num = max_child_num + 1"
+ " WHERE class_name = new.parent_class_name;"
+ " END;"
+
+ /*
+ * Table initialization
+ */
+
+ "INSERT INTO ldb_object_classes "
+ " (class_name, tree_key) "
+ " VALUES "
+ " ('TOP', '0001');");
+
+ /* Skip protocol indicator of url */
+ if (strncmp(url, "sqlite3://", 10) != 0) {
+ return SQLITE_MISUSE;
+ }
+
+ /* Update pointer to just after the protocol indicator */
+ url += 10;
+
+ /* Try to open the (possibly empty/non-existent) database */
+ if ((ret = sqlite3_open(url, &lsqlite3->sqlite)) != SQLITE_OK) {
+ return ret;
+ }
+
+ /* In case this is a new database, enable auto_vacuum */
+ ret = sqlite3_exec(lsqlite3->sqlite, "PRAGMA auto_vacuum = 1;", NULL, NULL, &errmsg);
+ if (ret != SQLITE_OK) {
+ if (errmsg) {
+ printf("lsqlite3 initializaion error: %s\n", errmsg);
+ free(errmsg);
+ }
+ goto failed;
+ }
+
+ if (flags & LDB_FLG_NOSYNC) {
+ /* DANGEROUS */
+ ret = sqlite3_exec(lsqlite3->sqlite, "PRAGMA synchronous = OFF;", NULL, NULL, &errmsg);
+ if (ret != SQLITE_OK) {
+ if (errmsg) {
+ printf("lsqlite3 initializaion error: %s\n", errmsg);
+ free(errmsg);
+ }
+ goto failed;
+ }
+ }
+
+ /* */
+
+ /* Establish a busy timeout of 30 seconds */
+ if ((ret = sqlite3_busy_timeout(lsqlite3->sqlite,
+ 30000)) != SQLITE_OK) {
+ return ret;
+ }
+
+ /* Create a function, callable from sql, to increment a tree_key */
+ if ((ret =
+ sqlite3_create_function(lsqlite3->sqlite,/* handle */
+ "base160_next", /* function name */
+ 1, /* number of args */
+ SQLITE_ANY, /* preferred text type */
+ NULL, /* user data */
+ base160next_sql, /* called func */
+ NULL, /* step func */
+ NULL /* final func */
+ )) != SQLITE_OK) {
+ return ret;
+ }
+
+ /* Create a function, callable from sql, to convert int to base160 */
+ if ((ret =
+ sqlite3_create_function(lsqlite3->sqlite,/* handle */
+ "base160", /* function name */
+ 1, /* number of args */
+ SQLITE_ANY, /* preferred text type */
+ NULL, /* user data */
+ base160_sql, /* called func */
+ NULL, /* step func */
+ NULL /* final func */
+ )) != SQLITE_OK) {
+ return ret;
+ }
+
+ /* Create a function, callable from sql, to perform various comparisons */
+ if ((ret =
+ sqlite3_create_function(lsqlite3->sqlite, /* handle */
+ "ldap_compare", /* function name */
+ 4, /* number of args */
+ SQLITE_ANY, /* preferred text type */
+ ldb , /* user data */
+ lsqlite3_compare, /* called func */
+ NULL, /* step func */
+ NULL /* final func */
+ )) != SQLITE_OK) {
+ return ret;
+ }
+
+ /* Begin a transaction */
+ ret = sqlite3_exec(lsqlite3->sqlite, "BEGIN EXCLUSIVE;", NULL, NULL, &errmsg);
+ if (ret != SQLITE_OK) {
+ if (errmsg) {
+ printf("lsqlite3: initialization error: %s\n", errmsg);
+ free(errmsg);
+ }
+ goto failed;
+ }
+ rollback = 1;
+
+ /* Determine if this is a new database. No tables means it is. */
+ if (query_int(lsqlite3,
+ &queryInt,
+ "SELECT COUNT(*)\n"
+ " FROM sqlite_master\n"
+ " WHERE type = 'table';") != 0) {
+ goto failed;
+ }
+
+ if (queryInt == 0) {
+ /*
+ * Create the database schema
+ */
+ ret = sqlite3_exec(lsqlite3->sqlite, schema, NULL, NULL, &errmsg);
+ if (ret != SQLITE_OK) {
+ if (errmsg) {
+ printf("lsqlite3 initializaion error: %s\n", errmsg);
+ free(errmsg);
+ }
+ goto failed;
+ }
+ } else {
+ /*
+ * Ensure that the database we opened is one of ours
+ */
+ if (query_int(lsqlite3,
+ &queryInt,
+ "SELECT "
+ " (SELECT COUNT(*) = 2"
+ " FROM sqlite_master "
+ " WHERE type = 'table' "
+ " AND name IN "
+ " ("
+ " 'ldb_entry', "
+ " 'ldb_object_classes' "
+ " ) "
+ " ) "
+ " AND "
+ " (SELECT 1 "
+ " FROM ldb_info "
+ " WHERE database_type = 'LDB' "
+ " AND version = '1.0'"
+ " );") != 0 ||
+ queryInt != 1) {
+
+ /* It's not one that we created. See ya! */
+ goto failed;
+ }
+ }
+
+ /* Commit the transaction */
+ ret = sqlite3_exec(lsqlite3->sqlite, "COMMIT;", NULL, NULL, &errmsg);
+ if (ret != SQLITE_OK) {
+ if (errmsg) {
+ printf("lsqlite3: iniialization error: %s\n", errmsg);
+ free(errmsg);
+ }
+ goto failed;
+ }
+
+ return SQLITE_OK;
+
+failed:
+ if (rollback) lsqlite3_safe_rollback(lsqlite3->sqlite);
+ sqlite3_close(lsqlite3->sqlite);
+ return -1;
+}
+
+/*
+ * connect to the database
+ */
+static int lsqlite3_connect(struct ldb_context *ldb,
+ const char *url,
+ unsigned int flags,
+ const char *options[],
+ struct ldb_module **_module)
+{
+ struct ldb_module *module;
+ struct lsqlite3_private *lsqlite3;
+ int i, ret;
+
+ module = ldb_module_new(ldb, ldb, "ldb_sqlite3 backend", &lsqlite3_ops);
+ if (!module) return -1;
+
+ lsqlite3 = talloc(module, struct lsqlite3_private);
+ if (!lsqlite3) {
+ goto failed;
+ }
+
+ lsqlite3->sqlite = NULL;
+ lsqlite3->options = NULL;
+ lsqlite3->trans_count = 0;
+
+ ret = initialize(lsqlite3, ldb, url, flags);
+ if (ret != SQLITE_OK) {
+ goto failed;
+ }
+
+ talloc_set_destructor(lsqlite3, destructor);
+
+ ldb_module_set_private(module, lsqlite3);
+
+ if (options) {
+ /*
+ * take a copy of the options array, so we don't have to rely
+ * on the caller keeping it around (it might be dynamic)
+ */
+ for (i=0;options[i];i++) ;
+
+ lsqlite3->options = talloc_array(lsqlite3, char *, i+1);
+ if (!lsqlite3->options) {
+ goto failed;
+ }
+
+ for (i=0;options[i];i++) {
+
+ lsqlite3->options[i+1] = NULL;
+ lsqlite3->options[i] =
+ talloc_strdup(lsqlite3->options, options[i]);
+ if (!lsqlite3->options[i]) {
+ goto failed;
+ }
+ }
+ }
+
+ *_module = module;
+ return 0;
+
+failed:
+ if (lsqlite3 && lsqlite3->sqlite != NULL) {
+ (void) sqlite3_close(lsqlite3->sqlite);
+ }
+ talloc_free(lsqlite3);
+ return -1;
+}
+
+const struct ldb_backend_ops ldb_sqlite3_backend_ops = {
+ .name = "sqlite3",
+ .connect_fn = lsqlite3_connect
+};
diff --git a/source4/lib/ldb/ldb_sqlite3/schema b/source4/lib/ldb/ldb_sqlite3/schema
new file mode 100644
index 0000000000..ab7c5cc406
--- /dev/null
+++ b/source4/lib/ldb/ldb_sqlite3/schema
@@ -0,0 +1,328 @@
+ -- ------------------------------------------------------
+
+ PRAGMA auto_vacuum=1;
+
+ -- ------------------------------------------------------
+
+ BEGIN EXCLUSIVE;
+
+ -- ------------------------------------------------------
+
+ CREATE TABLE ldb_info AS
+ SELECT 'LDB' AS database_type,
+ '1.0' AS version;
+
+ /*
+ * Get the next USN value with:
+ * BEGIN EXCLUSIVE;
+ * UPDATE usn SET value = value + 1;
+ * SELECT value FROM usn;
+ * COMMIT;
+ */
+ CREATE TABLE usn
+ (
+ value INTEGER
+ );
+
+ CREATE TABLE ldb_object
+ (
+ /* tree_key is auto-generated by the insert trigger */
+ tree_key TEXT PRIMARY KEY,
+
+ parent_tree_key TEXT,
+ dn TEXT,
+
+ attr_name TEXT REFERENCES ldb_attributes,
+ attr_value TEXT,
+
+ /*
+ * object_type can take on these values (to date):
+ * 1: object is a node of a DN
+ * 2: object is an attribute/value pair of its parent DN
+ */
+ object_type INTEGER,
+
+ /*
+ * if object_type is 1, the node can have children.
+ * this tracks the maximum previously assigned child
+ * number so we can generate a new unique tree key for
+ * a new child object. note that this is always incremented,
+ * so if children are deleted, this will not represent
+ * the _number_ of children.
+ */
+ max_child_num INTEGER,
+
+ /*
+ * Automatically maintained meta-data (a gift for metze)
+ */
+ object_guid TEXT UNIQUE,
+ timestamp INTEGER, -- originating_time
+ invoke_id TEXT, -- GUID: originating_invocation_id
+ usn INTEGER, -- hyper: originating_usn
+
+ /* do not allow duplicate name/value pairs */
+ UNIQUE (parent_tree_key, attr_name, attr_value, object_type)
+ );
+
+ CREATE TABLE ldb_attributes
+ (
+ attr_name TEXT PRIMARY KEY,
+ parent_tree_key TEXT,
+
+ objectclass_p BOOLEAN DEFAULT 0,
+
+ case_insensitive_p BOOLEAN DEFAULT 0,
+ wildcard_p BOOLEAN DEFAULT 0,
+ hidden_p BOOLEAN DEFAULT 0,
+ integer_p BOOLEAN DEFAULT 0,
+
+ /* tree_key is auto-generated by the insert trigger */
+ tree_key TEXT, -- null if not a object/sub class
+ -- level 1 if an objectclass
+ -- level 1-n if a subclass
+ max_child_num INTEGER
+ );
+
+ -- ------------------------------------------------------
+
+ CREATE INDEX ldb_object_dn_idx
+ ON ldb_object (dn);
+
+ CREATE INDEX ldb_attributes_tree_key_ids
+ ON ldb_attributes (tree_key);
+
+ -- ------------------------------------------------------
+
+ /* Gifts for metze. Automatically updated meta-data */
+ CREATE TRIGGER ldb_object_insert_tr
+ AFTER INSERT
+ ON ldb_object
+ FOR EACH ROW
+ BEGIN
+ UPDATE ldb_object
+ SET max_child_num = max_child_num + 1
+ WHERE tree_key = new.parent_tree_key;
+ UPDATE usn SET value = value + 1;
+ UPDATE ldb_object
+ SET tree_key =
+ (SELECT
+ new.tree_key ||
+ base160(SELECT max_child_num
+ FROM ldb_object
+ WHERE tree_key =
+ new.parent_tree_key));
+ max_child_num = 0,
+ object_guid = random_guid(),
+ timestamp = strftime('%s', 'now'),
+ usn = (SELECT value FROM usn);
+ WHERE tree_key = new.tree_key;
+ END;
+
+ CREATE TRIGGER ldb_object_update_tr
+ AFTER UPDATE
+ ON ldb_object
+ FOR EACH ROW
+ BEGIN
+ UPDATE usn SET value = value + 1;
+ UPDATE ldb_object
+ SET timestamp = strftime('%s', 'now'),
+ usn = (SELECT value FROM usn);
+ WHERE tree_key = new.tree_key;
+ END;
+
+ CREATE TRIGGER ldb_attributes_insert_tr
+ AFTER INSERT
+ ON ldb_attributes
+ FOR EACH ROW
+ BEGIN
+ UPDATE ldb_attributes
+ SET max_child_num = max_child_num + 1
+ WHERE tree_key = new.parent_tree_key;
+ UPDATE ldb_attributes
+ SET tree_key =
+ (SELECT
+ new.tree_key ||
+ base160(SELECT max_child_num
+ FROM ldb_attributes
+ WHERE tree_key =
+ new.parent_tree_key));
+ max_child_num = 0
+ WHERE tree_key = new.tree_key;
+ END;
+
+
+ -- ------------------------------------------------------
+
+ /* Initialize usn */
+ INSERT INTO usn (value) VALUES (0);
+
+ /* Create root object */
+ INSERT INTO ldb_object
+ (tree_key, parent_tree_key,
+ dn,
+ object_type, max_child_num)
+ VALUES ('', NULL,
+ '',
+ 1, 0);
+
+ /* We need an implicit "top" level object class */
+ INSERT INTO ldb_attributes (attr_name,
+ parent_tree_key)
+ SELECT 'top', '';
+
+ -- ------------------------------------------------------
+
+ COMMIT;
+
+ -- ------------------------------------------------------
+
+/*
+ * dn: o=University of Michigan,c=US
+ * objectclass: organization
+ * objectclass: domainRelatedObject
+ */
+-- newDN
+BEGIN;
+
+INSERT OR IGNORE INTO ldb_object
+ (parent_tree_key
+ dn,
+ attr_name, attr_value, object_type, max_child_num)
+ VALUES ('',
+ 'c=US',
+ 'c', 'US', 1, 0);
+
+INSERT INTO ldb_object
+ (parent_tree_key,
+ dn,
+ attr_name, attr_value, object_type, max_child_num)
+ VALUES ('0001',
+ 'o=University of Michigan,c=US',
+ 'o', 'University of Michigan', 1, 0);
+
+-- newObjectClass
+INSERT OR IGNORE INTO ldb_attributes
+ (attr_name, parent_tree_key, objectclass_p)
+ VALUES
+ ('objectclass', '', 1);
+
+INSERT INTO ldb_object
+ (parent_tree_key,
+ dn,
+ attr_name, attr_value, object_type, max_child_num)
+ VALUES ('00010001',
+ NULL,
+ 'objectclass', 'organization', 2, 0);
+
+INSERT OR IGNORE INTO ldb_attributes
+ (attr_name, parent_tree_key, objectclass_p)
+ VALUES
+ ('objectclass', '', 1);
+
+INSERT INTO ldb_object
+ (parent_tree_key,
+ dn,
+ attr_name, attr_value, object_type, max_child_num)
+ VALUES ('00010001',
+ NULL,
+ 'objectclass', 'domainRelatedObject', 2, 0);
+
+COMMIT;
+
+
+/*
+ * dn: o=University of Michigan,c=US
+ * l: Ann Arbor, Michigan
+ * st: Michigan
+ * o: University of Michigan
+ * o: UMICH
+ * seeAlso:
+ * telephonenumber: +1 313 764-1817
+ */
+-- addAttrValuePair
+BEGIN;
+
+INSERT INTO ldb_object
+ (parent_tree_key, dn,
+ attr_name, attr_value, object_type, max_child_num)
+ VALUES ('00010001', NULL,
+ 'l', 'Ann Arbor, Michigan', 2, 0);
+
+INSERT INTO ldb_object
+ (parent_tree_key, dn,
+ attr_name, attr_value, object_type, max_child_num)
+ VALUES ('00010001', NULL,
+ 'st', 'Michigan', 2, 0);
+
+INSERT INTO ldb_object
+ (parent_tree_key, dn,
+ attr_name, attr_value, object_type, max_child_num)
+ VALUES ('00010001', NULL,
+ 'o', 'University of Michigan', 2, 0);
+
+INSERT INTO ldb_object
+ (parent_tree_key, dn,
+ attr_name, attr_value, object_type, max_child_num)
+ VALUES ('00010001', NULL,
+ 'o', 'UMICH', 2, 0);
+
+INSERT INTO ldb_object
+ (parent_tree_key, dn,
+ attr_name, attr_value, object_type, max_child_num)
+ VALUES ('00010001', NULL,
+ 'seeAlso', '', 2, 0);
+
+INSERT INTO ldb_object
+ (parent_tree_key, dn,
+ attr_name, attr_value, object_type, max_child_num)
+ VALUES ('00010001', NULL,
+ 'telephonenumber', '+1 313 764-1817', 2, 0);
+
+COMMIT;
+
+-- ----------------------------------------------------------------------
+
+/*
+ * dn: @ATTRIBUTES
+ * uid: CASE_INSENSITIVE WILDCARD
+ * cn: CASE_INSENSITIVE
+ * ou: CASE_INSENSITIVE
+ * dn: CASE_INSENSITIVE
+ */
+-- newAttribute
+
+BEGIN;
+
+INSERT OR IGNORE INTO ldb_attributes
+ (attr_name, parent_tree_key, objectclass_p)
+ VALUES
+ ('uid', '', 0);
+
+UPDATE ldb_attributes
+ SET case_insensitive_p = 1,
+ wildcard_p = 1,
+ hidden_p = 0,
+ integer_p = 0
+ WHERE attr_name = 'uid'
+
+UPDATE ldb_attributes
+ SET case_insensitive_p = 1,
+ wildcard_p = 0,
+ hidden_p = 0,
+ integer_p = 0
+ WHERE attr_name = 'cn'
+
+UPDATE ldb_attributes
+ SET case_insensitive_p = 1,
+ wildcard_p = 0,
+ hidden_p = 0,
+ integer_p = 0
+ WHERE attr_name = 'ou'
+
+UPDATE ldb_attributes
+ SET case_insensitive_p = 1,
+ wildcard_p = 0,
+ hidden_p = 0,
+ integer_p = 0
+ WHERE attr_name = 'dn'
+
diff --git a/source4/lib/ldb/ldb_sqlite3/trees.ps b/source4/lib/ldb/ldb_sqlite3/trees.ps
new file mode 100644
index 0000000000..433a064816
--- /dev/null
+++ b/source4/lib/ldb/ldb_sqlite3/trees.ps
@@ -0,0 +1,1760 @@
+%!PS-Adobe-2.0
+%%Creator: dvips(k) 5.86 Copyright 1999 Radical Eye Software
+%%Title: trees.dvi
+%%Pages: 7
+%%PageOrder: Ascend
+%%BoundingBox: 0 0 596 842
+%%EndComments
+%DVIPSWebPage: (www.radicaleye.com)
+%DVIPSCommandLine: dvips -f trees.dvi
+%DVIPSParameters: dpi=600, compressed
+%DVIPSSource: TeX output 2000.05.06:2055
+%%BeginProcSet: texc.pro
+%!
+/TeXDict 300 dict def TeXDict begin/N{def}def/B{bind def}N/S{exch}N/X{S
+N}B/A{dup}B/TR{translate}N/isls false N/vsize 11 72 mul N/hsize 8.5 72
+mul N/landplus90{false}def/@rigin{isls{[0 landplus90{1 -1}{-1 1}ifelse 0
+0 0]concat}if 72 Resolution div 72 VResolution div neg scale isls{
+landplus90{VResolution 72 div vsize mul 0 exch}{Resolution -72 div hsize
+mul 0}ifelse TR}if Resolution VResolution vsize -72 div 1 add mul TR[
+matrix currentmatrix{A A round sub abs 0.00001 lt{round}if}forall round
+exch round exch]setmatrix}N/@landscape{/isls true N}B/@manualfeed{
+statusdict/manualfeed true put}B/@copies{/#copies X}B/FMat[1 0 0 -1 0 0]
+N/FBB[0 0 0 0]N/nn 0 N/IEn 0 N/ctr 0 N/df-tail{/nn 8 dict N nn begin
+/FontType 3 N/FontMatrix fntrx N/FontBBox FBB N string/base X array
+/BitMaps X/BuildChar{CharBuilder}N/Encoding IEn N end A{/foo setfont}2
+array copy cvx N load 0 nn put/ctr 0 N[}B/sf 0 N/df{/sf 1 N/fntrx FMat N
+df-tail}B/dfs{div/sf X/fntrx[sf 0 0 sf neg 0 0]N df-tail}B/E{pop nn A
+definefont setfont}B/Cw{Cd A length 5 sub get}B/Ch{Cd A length 4 sub get
+}B/Cx{128 Cd A length 3 sub get sub}B/Cy{Cd A length 2 sub get 127 sub}
+B/Cdx{Cd A length 1 sub get}B/Ci{Cd A type/stringtype ne{ctr get/ctr ctr
+1 add N}if}B/id 0 N/rw 0 N/rc 0 N/gp 0 N/cp 0 N/G 0 N/CharBuilder{save 3
+1 roll S A/base get 2 index get S/BitMaps get S get/Cd X pop/ctr 0 N Cdx
+0 Cx Cy Ch sub Cx Cw add Cy setcachedevice Cw Ch true[1 0 0 -1 -.1 Cx
+sub Cy .1 sub]/id Ci N/rw Cw 7 add 8 idiv string N/rc 0 N/gp 0 N/cp 0 N{
+rc 0 ne{rc 1 sub/rc X rw}{G}ifelse}imagemask restore}B/G{{id gp get/gp
+gp 1 add N A 18 mod S 18 idiv pl S get exec}loop}B/adv{cp add/cp X}B
+/chg{rw cp id gp 4 index getinterval putinterval A gp add/gp X adv}B/nd{
+/cp 0 N rw exit}B/lsh{rw cp 2 copy get A 0 eq{pop 1}{A 255 eq{pop 254}{
+A A add 255 and S 1 and or}ifelse}ifelse put 1 adv}B/rsh{rw cp 2 copy
+get A 0 eq{pop 128}{A 255 eq{pop 127}{A 2 idiv S 128 and or}ifelse}
+ifelse put 1 adv}B/clr{rw cp 2 index string putinterval adv}B/set{rw cp
+fillstr 0 4 index getinterval putinterval adv}B/fillstr 18 string 0 1 17
+{2 copy 255 put pop}for N/pl[{adv 1 chg}{adv 1 chg nd}{1 add chg}{1 add
+chg nd}{adv lsh}{adv lsh nd}{adv rsh}{adv rsh nd}{1 add adv}{/rc X nd}{
+1 add set}{1 add clr}{adv 2 chg}{adv 2 chg nd}{pop nd}]A{bind pop}
+forall N/D{/cc X A type/stringtype ne{]}if nn/base get cc ctr put nn
+/BitMaps get S ctr S sf 1 ne{A A length 1 sub A 2 index S get sf div put
+}if put/ctr ctr 1 add N}B/I{cc 1 add D}B/bop{userdict/bop-hook known{
+bop-hook}if/SI save N @rigin 0 0 moveto/V matrix currentmatrix A 1 get A
+mul exch 0 get A mul add .99 lt{/QV}{/RV}ifelse load def pop pop}N/eop{
+SI restore userdict/eop-hook known{eop-hook}if showpage}N/@start{
+userdict/start-hook known{start-hook}if pop/VResolution X/Resolution X
+1000 div/DVImag X/IEn 256 array N 2 string 0 1 255{IEn S A 360 add 36 4
+index cvrs cvn put}for pop 65781.76 div/vsize X 65781.76 div/hsize X}N
+/p{show}N/RMat[1 0 0 -1 0 0]N/BDot 260 string N/Rx 0 N/Ry 0 N/V{}B/RV/v{
+/Ry X/Rx X V}B statusdict begin/product where{pop false[(Display)(NeXT)
+(LaserWriter 16/600)]{A length product length le{A length product exch 0
+exch getinterval eq{pop true exit}if}{pop}ifelse}forall}{false}ifelse
+end{{gsave TR -.1 .1 TR 1 1 scale Rx Ry false RMat{BDot}imagemask
+grestore}}{{gsave TR -.1 .1 TR Rx Ry scale 1 1 false RMat{BDot}
+imagemask grestore}}ifelse B/QV{gsave newpath transform round exch round
+exch itransform moveto Rx 0 rlineto 0 Ry neg rlineto Rx neg 0 rlineto
+fill grestore}B/a{moveto}B/delta 0 N/tail{A/delta X 0 rmoveto}B/M{S p
+delta add tail}B/b{S p tail}B/c{-4 M}B/d{-3 M}B/e{-2 M}B/f{-1 M}B/g{0 M}
+B/h{1 M}B/i{2 M}B/j{3 M}B/k{4 M}B/w{0 rmoveto}B/l{p -4 w}B/m{p -3 w}B/n{
+p -2 w}B/o{p -1 w}B/q{p 1 w}B/r{p 2 w}B/s{p 3 w}B/t{p 4 w}B/x{0 S
+rmoveto}B/y{3 2 roll p a}B/bos{/SS save N}B/eos{SS restore}B end
+
+%%EndProcSet
+TeXDict begin 39158280 55380996 1000 600 600 (trees.dvi)
+@start
+%DVIPSBitmapFont: Fa cmr10 10 6
+/Fa 6 55 df<146014E0EB01C0EB0380EB0700130E131E5B5BA25B485AA2485AA212075B
+120F90C7FCA25A121EA2123EA35AA65AB2127CA67EA3121EA2121F7EA27F12077F1203A2
+6C7EA26C7E1378A27F7F130E7FEB0380EB01C0EB00E01460135278BD20>40
+D<12C07E12707E7E7E120F6C7E6C7EA26C7E6C7EA21378A2137C133C133E131EA2131F7F
+A21480A3EB07C0A6EB03E0B2EB07C0A6EB0F80A31400A25B131EA2133E133C137C1378A2
+5BA2485A485AA2485A48C7FC120E5A5A5A5A5A13527CBD20>I<15301578B3A6007FB812
+F8B912FCA26C17F8C80078C8FCB3A6153036367BAF41>43 D<EB03F8EB1FFF90387E0FC0
+9038F803E03901E000F0484813780007147C48487FA248C77EA2481580A3007EEC0FC0A6
+00FE15E0B3007E15C0A4007F141F6C1580A36C15006D5B000F143EA26C6C5B6C6C5B6C6C
+485A6C6C485A90387E0FC0D91FFFC7FCEB03F8233A7DB72A>48 D<EB01C013031307131F
+13FFB5FCA2131F1200B3B3A8497E007FB512F0A31C3879B72A>I<EC3FC0903801FFF001
+0713FC90380FE03E90383F800790387E001F49EB3F804848137F485AA2485A000FEC3F00
+49131E001F91C7FCA2485AA3127F90C9FCEB01FC903807FF8039FF1E07E090383801F049
+6C7E01607F01E0137E497FA249148016C0151FA290C713E0A57EA56C7E16C0A2121FED3F
+807F000F15006C6C5B15FE6C6C5B6C6C485A3900FE07F090383FFFC06D90C7FCEB03FC23
+3A7DB72A>54 D E
+%EndDVIPSBitmapFont
+%DVIPSBitmapFont: Fb cmr7 7 3
+/Fb 3 55 df<EB3F803801FFF03803E0F83807803C48487E001E7F003E1480A2003C1307
+007C14C0A400FC14E0AE007C14C0A36CEB0F80A36CEB1F006C131E6C6C5A3803E0F86CB4
+5A38003F801B277EA521>48 D<13381378EA01F8121F12FE12E01200B3AB487EB512F8A2
+15267BA521>I<EB0FE0EB3FF8EBF81C3801E0063803C01F48485AEA0F005A121E003E13
+1E91C7FC5AA21304EB3FC038FCFFF038FDC078EB003CB4133E48131E141FA2481480A412
+7CA4003C1400123E001E131E143E6C133C6C6C5A3803C1F03801FFC06C6CC7FC19277DA5
+21>54 D E
+%EndDVIPSBitmapFont
+%DVIPSBitmapFont: Fc cmmi10 10 1
+/Fc 1 69 df<0103B7FC4916E018F8903B0007F80007FE4BEB00FFF03F80020FED1FC018
+0F4B15E0F007F0021F1503A24B15F81801143F19FC5DA2147FA292C8FCA25C18035CA213
+0119F84A1507A2130319F04A150FA2010717E0181F4A16C0A2010FEE3F80A24AED7F0018
+7E011F16FE4D5A4A5D4D5A013F4B5A4D5A4A4A5A057FC7FC017F15FEEE03FC91C7EA0FF0
+49EC7FC0B8C8FC16FC16C03E397DB845>68 D E
+%EndDVIPSBitmapFont
+%DVIPSBitmapFont: Fd ectt1000 10 73
+/Fd 73 126 df<D807801307D81FE0EB0F80151F487E486C133F1600007C5CD8FCFC137E
+EAF87C15FE5D14015DA21403D8FCFC5BEA7CF8007F13075D383FF00FD81FE05BA2380780
+1FC75B143F92C7FCA25C147E14FE5CA213015CA213035C13075CA2130F5C131FEC800FED
+3FC0013FEB7FE0140049EBFFF0017E13F9A2D9FE0113F801FC13F0A2120113F8120313F0
+15F90007010013F05B000F14FF49EB7FE0A20007EC3FC06C48EB0F0025417DB92C>37
+D<EA0F80EA1FE0EA3FF0127F13F8A213FCA2123F121F120FEA007CA313FC13F8A2120113
+F01203EA07E0A2EA0FC0EA3F80127FEAFF005A12F812700E1D71B22C>39
+D<143814FC13011303EB07F8EB0FF0EB1FC0EB3F80EB7F0013FE485A485A5B12075B120F
+5B485AA2123F90C7FCA25A127EA312FE5AAC7E127EA3127F7EA27F121FA26C7E7F12077F
+12037F6C7E6C7E137FEB3F80EB1FC0EB0FF0EB07F8EB03FC130113001438164272B92C>
+I<127012FC7E7E6C7E6C7EEA0FE06C7E6C7E6C7E6C7E137F7F1480131F14C0130FEB07E0
+A214F01303A214F81301A314FC1300AC130114F8A3130314F0A2130714E0A2EB0FC0131F
+1480133F14005B13FE485A485A485A485AEA3FC0485A48C7FC5A5A1270164279B92C>I<
+EB0380497EA60020140800F8143E00FE14FE00FF13C1EBC7C7EBE7CF003FB512F8000F14
+E0000314806C140038007FFCA248B5FC481480000F14E0003F14F839FFE7CFFEEBC7C7EB
+07C100FE13C000F8143E0020140800001400A66D5A1F247AAA2C>I<147014F8AF003FB6
+12E0B712F8A4C700F8C7FCB0147025267DAB2C>I<EA0F80EA1FE0EA3FF0EA7FF8A213FC
+A3123F121F120F120013F8A21201EA03F01207EA1FE0EA7FC0EAFF80130012FC12700E17
+718A2C>I<121FEA3F80EA7FC0EAFFE0A5EA7FC0EA3F80EA1F000B0B708A2C>46
+D<1507ED0F80A2151F16005D153E157E157CA215FC5D14015D14035D14075D140F5D141F
+92C7FC5C143EA2147E147C14FC5C13015C13035C13075C130F5C131F91C8FC5B133EA213
+7E137C13FC5B12015B12035B12075B120F5B121F90C9FCA25A123E127E127C12FC5AA212
+7021417BB92C>I<EB03F8EB0FFE90383FFF80497F90B57E3901FE0FF03903F803F84848
+6C7EEBE0004848137EA248487FA248C7EA1F80A2003E140F007E15C0A3007C140700FC15
+E0AC6C140F007E15C0A46CEC1F80A36C6CEB3F00A26C6C137E6D13FE00075CEBF0016C6C
+485A3901FE0FF06CB55A6D5B6D5BD90FFEC7FCEB03F823357CB32C>I<1307497EA2131F
+A2133F137F13FF5A1207127FB5FC13DF139FEA7C1F1200B3AE007FB512E0B612F0A36C14
+E01C3477B32C>I<EB0FF890387FFF8048B512E00007804814FC391FF80FFE393FE001FF
+903880007F48C7EA3F80007E141F00FE15C0150F6C15E01507A3127E123CC8FCA2150F16
+C0151F1680153F16005D15FE4A5A14034A5A4A5A4A5A4A5AECFF804948C7FC495A495A49
+5AEB3FE0EB7F8049C8FC485A4848EB03C04848EB07E0EA1FE0485A48B6FCB7FCA36C15C0
+23347CB32C>I<000FB512FE4880A35D0180C8FCADEB83FE90389FFF8090B512E015F881
+9038FE03FE9038F000FF01C07F49EB3F8090C7121F6C15C0C8120FA2ED07E0A4123C127E
+B4FC150F16C0A248141F007EEC3F80007FEC7F006C6C5B6D485A391FF80FFC6CB55A6C5C
+000114C06C6C90C7FCEB0FF823347CB22C>53 D<EC3FC0903801FFF801077F011F7F497F
+90387FE07F9039FF003F804848137FEA03F8485A5B000FEC3F004848131E4990C7FC123F
+90C9FCA25A127EEB03FE90381FFF80D8FC7F13E000FDB57EB67E9038FE07FC9038F001FE
+9038C0007F49EB3F8090C7121F16C048140F16E01507A3127EA47E150F6D14C0001F141F
+6D1480000F143F6DEB7F003907F801FE3903FE07FC6CB55A6C5C6D5B011F1380D907FCC7
+FC23357CB32C>I<1278B712C016E0A316C000FCC7EA3F80ED7F0015FE00785CC712014A
+5A4A5A5D140F5D4A5A143F92C7FC5C147E14FE5C13015CA2495AA213075CA3495AA4495A
+A5133F91C8FCAA131E23357CB32C>I<EA0F80EA1FC0EA3FE0EA7FF0A5EA3FE0EA1FC0EA
+0F80C7FCAEEA0F80EA1FE0EA3FF0EA7FF8A213FCA3123F121F120F120013F8A21201EA03
+F01207EA1FE0EA7FC0EAFF80130012FC12700E3071A32C>59 D<1502ED0F80151F157F15
+FF913803FE00EC0FFCEC1FF0EC7FE0ECFF80D903FEC7FC495AEB1FF0495AEBFF80000390
+C8FCEA07FCEA1FF8EA3FE0EAFF8090C9FCA27FEA3FE0EA1FF8EA07FC6CB4FCC67FEB3FE0
+6D7EEB07FC6D7E903800FF80EC7FE0EC1FF0EC0FFCEC03FE913800FF80157F151F150FED
+0200212A7BAD2C>I<007FB612F0B712F8A36C15F0CAFCA8007FB612F0B712F8A36C15F0
+25127DA12C>I<122012F87EB4FC7FEA3FE0EA1FF8EA07FC6CB4FCC67FEB3FE06D7EEB07
+FC6D7E903800FF80EC7FE0EC1FF0EC0FFCEC03FE913800FF80157FA215FF913803FE00EC
+0FFCEC1FF0EC7FE0ECFF80D903FEC7FC495AEB1FF0495AEBFF80000390C8FCEA07FCEA1F
+F8EA3FE0EAFF8090C9FC12FC5A1220212A7BAD2C>I<14FE497EA4497FA214EFA2130781
+A214C7A2010F7FA314C390381F83F0A590383F01F8A490387E00FCA549137E90B512FEA3
+4880A29038F8003FA34848EB1F80A4000715C049130FD87FFEEBFFFC6D5AB514FE6C15FC
+497E27347EB32C>65 D<007FB512E015F8B612FE6C8016C03903F0003FED0FE0ED07F015
+03A2ED01F8A6ED03F0A21507ED0FE0ED1FC0EDFF8090B612005D5D15FF16C09039F0001F
+E0ED07F0ED03F81501ED00FCA216FE167EA616FE16FC1501ED03F8150FED3FF0007FB612
+E016C0B712806CECFE0015F027337FB22C>I<02FF13700107EBE0F84913F9013F13FD49
+13FFEBFF813901FE007F4848131FD807F0130F1507485A491303485A150148C7FCA25A00
+7EEC00F01600A212FE5AAB7E127EA3007F15F06CEC01F8A26C7EA26C6C13036D14F06C6C
+130716E0D803FC131F6C6CEB3FC03A00FF81FF806DB512006D5B010F5B6D13F001001380
+25357DB32C>I<007FB5FCB612C015F0816C803907E003FEEC00FFED7F80153FED1FC0ED
+0FE0A2150716F0150316F81501A4ED00FCACED01F8A3150316F0A2150716E0150FED1FC0
+153FED7F80EDFF00EC03FE007FB55AB65A5D15C06C91C7FC26337EB22C>I<007FB612F0
+B712F8A37E3903F00001A7ED00F01600A4EC01E04A7EA490B5FCA5EBF003A46E5A91C8FC
+A5163C167EA8007FB612FEB7FCA36C15FC27337EB22C>I<007FB612F8B712FCA37ED803
+F0C7FCA716781600A515F04A7EA490B5FCA5EBF001A46E5A92C7FCAD387FFFE0B5FC805C
+7E26337EB22C>I<903901FC038090390FFF87C04913EF017F13FF90B6FC4813073803FC
+01497E4848137F4848133F49131F121F5B003F140F90C7FCA2127EED078092C7FCA212FE
+5AA8913803FFF84A13FCA27E007E6D13F89138000FC0A36C141FA27F121F6D133F120F6D
+137F6C7E6C6C13FF6D5A3801FF076C90B5FC6D13EF011F13CF6DEB0780D901FCC7FC2635
+7DB32C>I<D87FFEEBFFFCB54813FEA36C486C13FCD807E0EB0FC0B190B6FCA59038E000
+0FB3D87FFEEBFFFCB54813FEA36C486C13FC27337EB22C>I<007FB512F8B612FCA36C14
+F839000FC000B3B3A5007FB512F8B612FCA36C14F81E3379B22C>I<D87FFCEB7FF8486C
+EBFFFCA36C48EB7FF8D807C0EB1F80153FED7F00157E5D4A5A14034A5A5D4A5A4A5A143F
+4AC7FC147E5CEBC1F813C3EBC7FCA2EBCFFEEBDFBEEBFFBF141F01FE7F496C7E13F86E7E
+EBF00301E07FEBC001816E7EA2157E153E153F811680ED0FC0A2ED07E0D87FFCEB1FFC48
+6CEB3FFEA36C48EB1FFC27337EB22C>75 D<387FFFE0B57EA36C5BD803F0C8FCB3AE16F0
+ED01F8A8007FB6FCB7FCA36C15F025337DB22C>I<D87FE0EB0FFC486CEB1FFEA26D133F
+007F15FC000F15E001BC137BA4019E13F3A3EB9F01A2018F13E3A21483A2018713C314C7
+A201831383A214EFA201811303A214FFEB80FEA3147C14381400ACD87FF0EB1FFC486CEB
+3FFEA36C48EB1FFC27337EB22C>I<D87FF0EB7FFC486CEBFFFEA27F007FEC7FFCD807FE
+EB07C013DEA213DF13CFA2148013C714C0A213C314E0A213C114F0A213C014F8A2147CA3
+143EA2141E141FA2140F1587A2140715C7A2140315E71401A215F71400A215FFD87FFC13
+7F487E153FA26C48EB1F8027337EB22C>I<EB7FFF0003B512E0000F14F848804880EBE0
+03EB800048C7127FA2007E80A300FE158048141FB3A86C143FA2007E1500A3007F5CA26C
+6C13FEEBF00790B5FC6C5C6C5C000314E0C66C90C7FC21357BB32C>I<007FB512C0B612
+F88115FF6C15802603F00013C0153FED0FE0ED07F0A2150316F81501A6150316F01507A2
+ED0FE0ED3FC015FF90B61280160015FC5D15C001F0C8FCB0387FFF80B57EA36C5B25337E
+B22C>I<EB7FFF0003B512E0000F14F848804880EBF007EB800048C7127FA2007E80A300
+FE158048141FB3A7EB01F0EB03F800FE143F267E01FC1300A2EB00FE007F5C147FD83F80
+13FEEBF03F90B5FC6C5C6C5C000314E0C67E90380007F0A26E7EA26E7EA26E7EA2157FA2
+153E21407BB32C>I<387FFFFCB67E15E015F86C803907E007FE1401EC007F6F7E151FA2
+6F7EA64B5AA2153F4BC7FCEC01FE140790B55A5D15E081819038E007FCEC01FE1400157F
+81A8160FEE1F80A5D87FFEEB1FBFB5ECFF00815E6C486D5AC8EA01F029347EB22C>I<90
+381FF80790B5EA0F804814CF000714FF5A381FF01F383FC003497E48C7FC007E147F00FE
+143F5A151FA46CEC0F00007E91C7FC127F7FEA3FE0EA1FFCEBFFC06C13FC0003EBFFC06C
+14F06C6C7F01077F9038007FFEEC07FF02001380153FED1FC0A2ED0FE0A20078140712FC
+A56CEC0FC0A26CEC1F806D133F01E0EB7F009038FE01FF90B55A5D00F914F0D8F83F13C0
+D8700790C7FC23357CB32C>I<007FB612FCB712FEA43AFC007E007EA70078153CC71400
+B3AF90383FFFFCA2497F6D5BA227337EB22C>I<3B7FFF803FFFC0B56C4813E0A36C496C
+13C03B03F00001F800B3AF6D130300015DA26D130700005D6D130F017F495A6D6C485AEC
+E0FF6DB5C7FC6D5B010313F86D5B9038003F802B3480B22C>I<D87FFCEB7FFC486CEBFF
+FEA36C48EB7FFCD80FC0EB07E06D130F000715C0A36D131F00031580A36D133F00011500
+A36D5B0000147EA4017E5BA46D485AA490381F83F0A4010F5B14C7A301075BA214EFA201
+035BA214FFA26D90C7FCA46D5A27347EB22C>I<D87FF0EB07FF486C491380A36C486D13
+00001FC8127CA46C6C5CA76C6C495AA4143E147FA33A03E0FF83E0A214F7A201E113C3A3
+000101E35BA201F113C701F313E7A314C1A200005DA201F713F71480A301FF13FF017F91
+C7FC4A7EA4013E133E29347FB22C>I<3A3FFF03FFE0484913F0148714076C6D13E03A01
+F800FE007F0000495A13FE017E5BEB7F03013F5B1487011F5B14CF010F5B14FF6D5BA26D
+90C7FCA26D5AA26D5AA2497EA2497EA2497F81EB0FCF81EB1FC7EC87F0EB3F83EC03F8EB
+7F01017E7FEBFE00497F0001147E49137F000380491480151FD87FFEEBFFFC6D5AB514FE
+6C15FC497E27337EB22C>I<D87FFCEB7FFC486CEBFFFEA36C48EB7FFCD807F0EB0FC015
+1F000315806D133F12016DEB7F0012006D137E017E13FE017F5BEB3F01EC81F8131FEC83
+F0EB0FC314C7903807E7E0A201035B14EF6DB45AA292C7FC7F5C147EB0903807FFE0497F
+A36D5B27337EB22C>I<387FFFFCB512FEA314FC00FCC7FCB3B3B3B512FC14FEA36C13FC
+17416FB92C>91 D<127012F8A27E127C127E123E123F7EA27F120F7F12077F12037F1201
+7F12007F137C137E133EA2133F7F80130F80130780130380130180130080147C147E143E
+A2143F8081140F81140781140381140181140081157CA2157E153E153F811680150FA2ED
+070021417BB92C>I<387FFFFCB512FEA37EC7127EB3B3B3387FFFFEB5FCA36C13FC1741
+7DB92C>I<EB07C0EB1FF0EB7FFC48B5FC000714C0001F14F0397FFC7FFC39FFF01FFEEB
+C007EB0001007CEB007C003014181F0C7AAE2C>I<007FB6FCB71280A46C150021067B7D
+2C>I<1338137CEA01FC1203EA07F813F0EA0FC0EA1F80A2EA3F00123E127E127CA212FC
+5AA3EAFFC013E013F013F8A2127FA2123F13F0EA1FE0EA07C00E1D72B82C>I<3801FFF0
+000713FE001F6D7E15E048809038C01FF81407EC01FC381F80000006C77EC8127EA3ECFF
+FE131F90B5FC1203120F48EB807E383FF800EA7FC090C7FC12FE5AA47E007F14FEEB8003
+383FE01F6CB612FC6C15FE6C14BF0001EBFE1F3A003FF007FC27247CA32C>I<EA7FF048
+7EA3127F1201AAEC1FE0ECFFF801FB13FE90B6FC16809138F07FC09138801FE091380007
+F049EB03F85BED01FC491300A216FE167EA816FE6D14FCA2ED01F86D13036DEB07F0150F
+9138801FE09138E07FC091B51280160001FB5B01F813F83900F03FC027337FB22C>I<90
+3803FFE0011F13F8017F13FE48B5FC48804848C6FCEA0FF0485A49137E4848131890C9FC
+5A127EA25AA8127EA2127F6C140F6DEB1F806C7E6D133F6C6CEB7F003907FE03FF6CB55A
+6C5C6C6C5B011F13E0010390C7FC21247AA32C>I<EC0FFE4A7EA380EC003FAAEB07F8EB
+3FFE90B512BF4814FF5A3807FC0F380FF00348487E497E48487F90C7FC007E80A212FE5A
+A87E007E5CA2007F5C6C7E5C6C6C5A380FF0073807FC1F6CB612FC6CECBFFE6C143FEB3F
+FC90390FF01FFC27337DB22C>I<EB03FE90381FFFC0017F13F048B57E48803907FE03FE
+390FF800FFD81FE0EB3F805B4848EB1FC090C7120F5A007E15E015075AB7FCA416C000FC
+C9FC7E127EA2127F6CEC03C06DEB07E06C7ED80FF0130F6C6CEB3FC001FF13FF000190B5
+12806C1500013F13FC010F13F00101138023247CA32C>I<ED03F8903907F80FFC90391F
+FE3FFE017FB6FC48B7FC48ECFE7F9038FC0FF82607F003133E3A0FE001FC1CD9C0001300
+001F8049137EA66D13FE000F5CEBE0016C6C485A3903FC0FF048B5FC5D481480D99FFEC7
+FCEB87F80180C8FCA37F6C7E90B512F06C14FE48ECFF804815E04815F03A3FC0001FF848
+C7EA03FC007E1400007C157C00FC157E48153EA46C157E007E15FCD87F801303D83FE0EB
+0FF8D81FFCEB7FF06CB612E0000315806C1500D8003F13F8010713C028387EA42C>103
+D<EA7FF0487EA3127F1201AAEC1FE0EC7FFC9038F9FFFE01FB7F90B6FC9138F03F80ECC0
+1F02807FEC000F5B5BA25BB3267FFFE0B5FCB500F11480A36C01E0140029337FB22C>I<
+1307EB1FC0A2497EA36D5AA20107C7FC90C8FCA7387FFFC080B5FC7EA2EA0007B3A8007F
+B512FCB612FEA36C14FC1F3479B32C>I<EA7FE0487EA3127F1201AA91381FFFF04A13F8
+A36E13F0913800FE004A5A4A5A4A5A4A5A4A5A4A5A4AC7FC14FEEBF1FC13F3EBF7FE90B5
+FCA2EC9F80EC0FC001FE7FEBFC07496C7E496C7E811400157E811680151F3A7FFFC0FFFC
+B500E113FEA36C01C013FC27337EB22C>107 D<387FFFE0B57EA37EEA0003B3B3A5007F
+B61280B712C0A36C158022337BB22C>I<3A7F83F007E09039CFFC1FF83AFFDFFE3FFCD8
+7FFF13FF91B57E3A07FE1FFC3E01FCEBF83F496C487E01F013E001E013C0A301C01380B3
+3B7FFC3FF87FF0027F13FFD8FFFE6D13F8D87FFC4913F0023F137F2D2481A32C>I<397F
+F01FE039FFF87FFC9038F9FFFE01FB7F6CB6FC00019038F03F80ECC01F02807FEC000F5B
+5BA25BB3267FFFE0B5FCB500F11480A36C01E0140029247FA32C>I<EB07FCEB1FFF017F
+13C048B512F048803907FC07FC390FF001FE48486C7E0180133F003F158090C7121F007E
+EC0FC0A348EC07E0A76C140F007E15C0A2007F141F6C15806D133F6C6CEB7F006D5B6C6C
+485A3907FC07FC6CB55A6C5C6C6C13C0011F90C7FCEB07FC23247CA32C>I<397FF01FE0
+39FFF8FFF801FB13FE90B6FC6C158000019038F07FC09138801FE091380007F049EB03F8
+5BED01FC491300A216FE167EA816FE6D14FCA2ED01F86D13036DEB07F0150F9138801FE0
+9138E07FC091B51280160001FB5B01F813F8EC3FC091C8FCAD387FFFE0B57EA36C5B2736
+7FA32C>I<903903FC078090391FFF0FC0017F13CF48B512EF4814FF3807FE07380FF001
+48487E49137F4848133F90C7FC48141F127E150F5AA87E007E141FA26C143F7F6C6C137F
+6D13FF380FF0033807FC0F6CB6FC6C14EF6C6C138F6D130FEB07F890C7FCAD0203B5FC4A
+1480A36E140029367DA32C>I<D87FFEEB3FC0B53801FFF0020713F8021F13FC6C5B3900
+3F7FE1ECFF019138FC00F84A13704A13005CA25C5CA391C8FCAF007FB512E0B67EA36C5C
+26247EA32C>I<90387FF8700003B512F8120F5A5A387FC00F387E00034813015AA36CEB
+00F0007F140013F0383FFFC06C13FE6CEBFF80000314E0C66C13F8010113FCEB0007EC00
+FE0078147F00FC143F151F7EA26C143F6D133E6D13FE9038F007FC90B5FC15F815E000F8
+148039701FFC0020247AA32C>I<131E133FA9007FB6FCB71280A36C1500D8003FC8FCB1
+ED03C0ED07E0A5EC800F011FEB1FC0ECE07F6DB51280160001035B6D13F89038003FE023
+2E7EAD2C>I<3A7FF003FF80486C487FA3007F7F0001EB000FB3A3151FA2153F6D137F39
+00FE03FF90B7FC6D15807F6D13CF902603FE07130029247FA32C>I<3A3FFF03FFF04801
+8713F8A36C010313F03A00FC007E005D90387E01F8013F5BEB1F83EC87E090380FCFC090
+3807EF80EB03FF6D90C7FC5C6D5A147C14FE130180903803EF80903807CFC0EB0FC7EC83
+E090381F01F0013F7FEB7E00017C137C49137E0001803A7FFF01FFFC1483B514FE6C15FC
+140127247EA32C>120 D<3A7FFF01FFFCB5008113FE148314816C010113FC3A03E0000F
+806C7E151F6D140012005D6D133E137C017E137E013E137CA2013F13FC6D5BA2EB0F815D
+A2EB07C1ECC3E0A2EB03E3ECE7C0130114F75DEB00FFA292C7FC80A2143EA2147E147CA2
+14FC5CA2EA0C01003F5BEA7F83EB87E0EA7E0F495A387FFF806C90C8FC6C5A6C5AEA07E0
+27367EA32C>I<15FF02071380141F147F91B512004913C04AC7FCEB03F85CB31307EB1F
+E013FF007F5BB55A49C8FC6D7E6C7FC67F131FEB07F01303B380EB01FEECFFC06D13FF6E
+1380141F14070200130021417BB92C>123 D<127812FCB3B3B3A9127806416DB92C>I<EA
+7FC0EAFFF813FE6D7E6C7FC67F131FEB07F01303B380EB01FEECFFC06D13FF6E1380141F
+147F91B512004913C04AC7FCEB03F85CB31307EB1FE013FF007F5BB55A49C8FC13F8EA7F
+C021417BB92C>I E
+%EndDVIPSBitmapFont
+%DVIPSBitmapFont: Fe ecti1000 10 33
+/Fe 33 122 df<EE3FFC4BB51280923907E007C092391F8001E0DB3F0013F0037E13034B
+1307A24A5A18E04A48EB038094C7FCA314075DA4140F5DA3010FB7FCA25F903A001F8000
+7EA217FE023F5C92C7FCA216015F5C147E16035FA214FE4A13075FA30101140F5F4AECC1
+C0A2161F1783010316805CA2EF870013074A5CEE0F8EEE079EEE03FC010FEC00F04A91C7
+FCA35C131FA2001C90CAFC127E5BEAFE3E133C137CEAF878EA78F0EA3FE0EA0F80344C82
+BA2F>28 D<150C151C153815F0EC01E0EC03C0EC0780EC0F00141E5C147C5C5C495A1303
+495A5C130F49C7FCA2133EA25BA25BA2485AA212035B12075BA2120F5BA2121FA290C8FC
+A25AA2123EA2127EA2127CA412FC5AAD1278A57EA3121C121EA2120E7EA26C7E6C7EA212
+001E5274BD22>40 D<140C140E80EC0380A2EC01C015E0A2140015F0A21578A4157C153C
+AB157CA715FCA215F8A21401A215F0A21403A215E0A21407A215C0140F1580A2141F1500
+A2143EA25CA25CA2495AA2495A5C1307495A91C7FC5B133E133C5B5B485A12035B48C8FC
+120E5A12785A12C01E527FBD22>I<4B7EA3150393C8FCA35D1506A3150E150CA3151C15
+18A315381530A31570B912E0A2C80060C8FC15E05DA314015DA3140392C9FCA35C1406A3
+140E140CA3141C1418A2333275AD40>43 D<EA03C0EA07F0120F121F13F8A313F0EA07B0
+EA003013701360A213E013C01201EA038013005A120E5A5A5A5A5A0D197A8819>I<120E
+EA3F80127F12FFA31300127E123C0909778819>46 D<0103B612FEEFFFC018F0903B0007
+F8000FF84BEB03FCEF00FE020F157FF03F804B141F19C0021F150F19E05D1807143F19F0
+5DA2147FA292C8FCA25C180F5CA2130119E04A151FA2130319C04A153FA201071780187F
+4A1600A2010F16FEA24A4A5A60011F15034D5A4A5D4D5A013F4B5A173F4A4AC7FC17FC01
+7FEC03F84C5A91C7EA1FC04949B45A007F90B548C8FCB712F016803C397CB83F>68
+D<0103B512F8A390390007F8005DA2140FA25DA2141FA25DA2143FA25DA2147FA292C7FC
+A25CA25CA21301A25CA21303A25CA21307A25CA2130FA25CA2131FA25CA2133FA25CA213
+7FA291C8FC497EB6FCA25C25397CB820>73 D<0107B512FCA25E9026000FF8C7FC5D5D14
+1FA25DA2143FA25DA2147FA292C8FCA25CA25CA21301A25CA21303A25CA21307A25CA213
+0F170C4A141CA2011F153C17384A1478A2013F157017F04A14E01601017F140317C091C7
+1207160F49EC1F80163F4914FF000102071300B8FCA25E2E397BB834>76
+D<ED03FE92383FFFC09238FC07F0913903E001F891390F80007C023FC77E027E8002F815
+804948EC0FC0EB07E04948EC07E0131F4A15F049C81203137E01FE16F8485AA2485AA248
+5AA2120F5B001F16075B123FA34848ED0FF0A448C9EA1FE0A3EF3FC0A21880177F18005F
+5F16015F6C4B5A4C5AA24C5A6C4B5A6D4A5A001F93C7FC6D147E000F5D6C6CEB03F06C6C
+495A6C6CEB0F806C6C013FC8FC90383F01FC90381FFFE0010190C9FC353D74BA40>79
+D<ED03FE92383FFFC09238FC07F0913903E001F891390FC0007C023FC77E027E804A1580
+D901F0EC0FC013074948EC07E0495A4A15F049C8FC49150301FE16F8485AA2485AA2485A
+A2120F491507121FA2485AA34848ED0FF0A448C9EA1FE0A3EF3FC0A21880177F4817005F
+5F16015F007F4B5A5F91380F800791393FE00FE06C903970601FC0902680E0305B261F81
+C049C7FC913880187ED80FC35C3A07E30019F00003EC1FE0D801FB14806CB46C48C8FC90
+263F81FC13186DB45A01010138133890C7003C1330177017F05FED3E03ED3F07EEFFC05F
+A294C7FC5E6F5A6F5AED07E0354B74BA40>81 D<92383FC00E913901FFF01C020713FC91
+391FC07E3C91393F001F7C027CEB0FF84A130749481303495A4948EB01F0A2495AA2011F
+15E091C7FCA34915C0A36E90C7FCA2806D7E14FCECFF806D13F015FE6D6D7E6D14E00100
+80023F7F14079138007FFC150F15031501A21500A2167C120EA3001E15FC5EA3003E4A5A
+A24B5AA2007F4A5A4B5A6D49C7FC6D133ED8F9F013FC39F8FC03F839F07FFFE0D8E01F13
+8026C003FCC8FC2F3D7ABA2F>83 D<0007B812E0A25AD9F800EB001F01C049EB07C0485A
+D900011403121E001C5C003C17801403123800785C00701607140700F01700485CA2140F
+C792C7FC5DA2141FA25DA2143FA25DA2147FA292C9FCA25CA25CA21301A25CA21303A25C
+A21307A25CA2130FA25CEB3FF0007FB512F8B6FCA2333971B83B>I<14F8EB07FE90381F
+871C90383E03FE137CEBF801120148486C5A485A120FEBC001001F5CA2EA3F801403007F
+5C1300A21407485C5AA2140F5D48ECC1C0A2141F15831680143F1587007C017F1300ECFF
+076C485B9038038F8E391F0F079E3907FE03FC3901F000F0222677A42A>97
+D<133FEA1FFFA3C67E137EA313FE5BA312015BA312035BA31207EBE0F8EBE7FE9038EF0F
+80390FFC07C013F89038F003E013E0D81FC013F0A21380A2123F1300A214075A127EA214
+0F12FE4814E0A2141F15C05AEC3F80A215005C147E5C387801F8007C5B383C03E0383E07
+C0381E1F80D80FFEC7FCEA01F01C3B77B926>I<147F903803FFC090380FC1E090381F00
+70017E13784913383901F801F83803F003120713E0120FD81FC013F091C7FC485AA2127F
+90C8FCA35A5AA45AA3153015381578007C14F0007EEB01E0003EEB03C0EC0F806CEB3E00
+380F81F83803FFE0C690C7FC1D2677A426>I<ED01F815FFA3150316F0A21507A216E0A2
+150FA216C0A2151FA21680A2153FA202F81300EB07FE90381F877F90383E03FF017C5BEB
+F80112013803F00048485B120FEBC001121F5DEA3F801403127F01005BA214075A485CA2
+140FA248ECC1C0A2141F15C3ED8380143F1587007C017F1300ECFF076C485B9038038F8E
+391F0F079E3907FE03FC3901F000F0253B77B92A>I<147F903803FFC090380FC1E09038
+3F00F0017E13785B485A485A485A120F4913F8001F14F0383F8001EC07E0EC1F80397F81
+FF00EBFFF8148090C8FC5A5AA55AA21530007C14381578007E14F0003EEB01E0EC03C06C
+EB0F806CEB3E00380781F83803FFE0C690C7FC1D2677A426>I<ED07C0ED1FF0ED3E38ED
+7C3CEDF8FC15F9140115F1020313F8EDF0F0160014075DA4140F5DA4141F5D010FB512C0
+5B16809039003F800092C7FCA45C147EA414FE5CA413015CA413035CA413075CA4130F5C
+A3131F5CA391C8FC5B121CEA7E3EA2EAFE3C137C1378EAF8F01278EA3FC0EA0F80264C82
+BA19>I<EC07C0EC3FF09138FC38E0903901F01FF0EB03E0903807C00FEB0F80011F1307
+D93F0013E05B017E130F13FE4914C01201151F1203491480A2153F1207491400A25DA249
+137EA215FEA25D00031301140314076C6C485A0000131FEB787BEB3FF390380FC3F0EB00
+031407A25DA2140F5D121C007E131F5D00FE49C7FC147E5C387801F8387C07E0381FFF80
+D803FEC8FC24367CA426>I<EB03F0EA01FFA3EA00075CA3130F5CA3131F5CA3133F91C8
+FCA35B90387E07F0EC1FFCEC783E9038FFE01F02C01380EC800F1400485A16C05B49EB1F
+8012035BA2153F000715005BA25D000F147E5B15FE5D121FD98001131C15F8163C003F01
+031338010013F0A216704814E0007E15F016E0EDE1C000FE903801E38048903800FF0000
+38143C263B7BB92A>I<EB01C0EB07E014F0130F14E01307EB038090C7FCAB13F0EA03FC
+EA071EEA0E1F121CA212385B1270A25BEAF07E12E013FEC65AA212015B1203A25B12075B
+A2000F13E013C013C1001F13C01381A2EB83801303EB0700A2130E6C5AEA07F8EA01E014
+3879B619>I<EB0FC0EA07FFA3EA001F1480A2133FA21400A25BA2137EA213FEA25BA212
+01A25BA21203A25BA21207A25BA2120FA25BA2121FA25BA2123FA290C7FCA25AA2EA7E0E
+A212FE131EEAFC1CA2133C133812F81378EA7870EA7CE0121FEA0F80123B79B915>108
+D<D801E001FEEB07F03C07F803FF801FFC3C0E3C0F07C0783E3C1E3E3C03E1E01F261C1F
+78D9F3C013803C383FF001F7800F02E01400007801C013FE007018C002805B4A4848EB1F
+80EAF07FD8E07E5CA200000207143F01FE1700495CA2030F5C0001177E495C18FE031F5C
+120349DA8001131C18F8033F153C00070403133849020013F0A24B1570000F17E049017E
+15F019E003FEECE1C0001FEE01E34949903800FF000007C70038143C3E2679A444>I<D8
+01E013FE3A07F803FF803A0E3C0F07C03A1E3E3C03E0261C1F787F39383FF00114E00078
+13C000708114804A485AEAF07FEAE07EA20000140701FE5C5BA2150F00015D5B151F5E12
+034990383F8380160316070007027F130049137EA2160E000F147C49141E161C5E001FEC
+3C7849EB1FE00007C7EA0780292679A42F>I<147F903803FFC090380FC1F090381F00F8
+017E137C5B4848137E4848133E0007143F5B120F485AA2485A157F127F90C7FCA215FF5A
+4814FEA2140115FC5AEC03F8A2EC07F015E0140F007C14C0007EEB1F80003EEB3F00147E
+6C13F8380F83F03803FFC0C648C7FC202677A42A>I<9039078007C090391FE03FF09039
+3CF0787C903938F8E03E9038787FC00170497EECFF00D9F0FE148013E05CEA01E113C15C
+A2D80003143FA25CA20107147FA24A1400A2010F5C5E5C4B5A131F5EEC80035E013F495A
+6E485A5E6E48C7FC017F133EEC70FC90387E3FF0EC0F8001FEC9FCA25BA21201A25BA212
+03A25B1207B512C0A3293580A42A>I<3903C003F0390FF01FFC391E783C0F381C7C703A
+3C3EE03F8038383FC0EB7F800078150000701300151CD8F07E90C7FCEAE0FE5BA2120012
+015BA312035BA312075BA3120F5BA3121F5BA3123F90C9FC120E212679A423>114
+D<14FE903807FF8090380F83C090383E00E04913F00178137001F813F00001130313F0A2
+15E00003EB01C06DC7FC7FEBFFC06C13F814FE6C7F6D13807F010F13C01300143F141F14
+0F123E127E00FE1480A348EB1F0012E06C133E00705B6C5B381E03E06CB45AD801FEC7FC
+1C267AA422>I<EB0380EB07C0130FA4131F1480A3133F1400A35B137E007FB5FCA2B6FC
+3800FC00A312015BA312035BA312075BA3120F5BA3121FEB801CA2143C003F1338EB0078
+147014F014E0EB01C0EA3E03381F0780380F0F00EA07FCEA01F0183579B31C>I<01F013
+0ED803FC133FD8071EEB7F80EA0E1F121C123C0038143F49131F0070140FA25BD8F07E14
+0000E08013FEC6485B150E12015B151E0003141C5BA2153C000714385B5DA35DA24A5A14
+0300035C6D48C7FC0001130E3800F83CEB7FF8EB0FC0212679A426>118
+D<903907E007C090391FF81FF89039787C383C9038F03E703A01E01EE0FE3803C01F0180
+13C0D8070014FC481480000E1570023F1300001E91C7FC121CA2C75AA2147EA214FEA25C
+A21301A24A1370A2010314F016E0001C5B007E1401010714C000FEEC0380010F1307010E
+EB0F0039781CF81E9038387C3C393FF03FF03907C00FC027267CA427>120
+D<13F0D803FCEB01C0D8071EEB03E0D80E1F1307121C123C0038140F4914C01270A24913
+1FD8F07E148012E013FEC648133F160012015B5D0003147E5BA215FE00075C5BA214015D
+A314035D14070003130FEBF01F3901F87FE038007FF7EB1FC7EB000F5DA2141F003F5C48
+133F92C7FC147E147C007E13FC387001F8EB03E06C485A383C1F80D80FFEC8FCEA03F023
+3679A428>I E
+%EndDVIPSBitmapFont
+%DVIPSBitmapFont: Ff cmsy10 10 1
+/Ff 1 16 df<EB1FF0EBFFFE487F000714C04814E04814F04814F8A24814FCA3B612FEA9
+6C14FCA36C14F8A26C14F06C14E06C14C0000114006C5BEB1FF01F1F7BA42A>15
+D E
+%EndDVIPSBitmapFont
+%DVIPSBitmapFont: Fg ecbx1000 10 36
+/Fg 36 119 df<913803FFC0027F13F00103B512FC010FEB00FED93FF8133FD97FE0EBFF
+8049485A5A1480484A13C04A6C1380A36F1300167E93C7FCA592383FFFC0B8FCA4000390
+C7FCB3ABB5D8FC3F13FFA4303A7EB935>28 D<B61280A819087F9620>45
+D<EA0F80EA3FE0EA7FF0A2EAFFF8A5EA7FF0A2EA3FE0EA0F800D0D798C1B>I<141E143E
+14FE1307137FB5FCA3138FEA000FB3B3A5007FB61280A4213679B530>49
+D<EB0FFE90387FFFC048B512F0000714FC390FE03FFF261F800F1380263F000313C0D87F
+8014E0EBE00100FF6D13F07FA2ED7FF8A46C5A6C5A0006C7FCC8FCEDFFF0A216E05C16C0
+4A138016004A5A4A5AEC1FF05D4A5A4AC7FC14FE495AD903F01378495A495A495A49C712
+F8017C14F05B49130148B6FC5A5A5A5A5A4815E0B7FCA425367BB530>I<EC0FF8ECFFFE
+0103EBFF8090390FF80FC090393FE003E090397F8001F09038FF000F48EC1FF84848133F
+485A120F5B121FA2003FEC1FF0ED0FE0484890C7FCA31408EC7FF039FFF1FFFC01F313FF
+D9F78013809039FF007FC049EB3FE04914F0ED1FF85B16FCA34914FEA4127FA5123F16FC
+A26C7E16F8000F143F6D14F0000715E06C6CEB7FC03A01FF81FF806C90B51200013F13FC
+010F13F00101138027377CB530>54 D<EA0F80EA3FE0EA7FF0A2EAFFF8A5EA7FF0A2EA3F
+E0EA0F80C7FCABEA0F80EA3FE0EA7FF0A2EAFFF8A5EA7FF0A2EA3FE0EA0F800D2579A41B
+>58 D<B812C017FC17FF18C028007FF000037F04007F717E717E171F84A2717EA74D5AA2
+60173F4D5A4D5A4C13C0040F5B91B600FCC7FCA2EFFF8002F0C713F0EF3FF8717E717E71
+7E19807113C0A319E0A719C0A25F4D138019005FEF7FFE4C485AB912F018C095C7FC17F0
+3B397DB844>66 D<DB3FFCEB01C00203B5EAC003021FECF00791B6EAFC0F01039039FC00
+FF3F4901C0EB1FFFD91FFEC77E49481403D97FF080494880485B48177F4849153F4890C9
+FC181F485A180F123F5B1807127FA24993C7FC12FFAD127F7FF003C0123FA27F001F1707
+A26C6C1780180F6C6D16006C6D5D6C173E6C6D157ED97FF85D6D6C4A5A6DB44A5A010701
+C0EB0FE06D01FCEBFF80010090B548C7FC021F14F8020314E09126003FFEC8FC3A3B7BB9
+45>I<B87E17F817FF18C028007FF8000713F09338007FF8EF1FFE717E050313807113C0
+A27113E0F07FF0A2F03FF8A219FC181FA219FEA419FFAC19FEA419FC183FA219F8187F19
+F0F0FFE0A24D13C04D13804D1300EF1FFEEF7FFC933807FFF0B912C095C7FC17FC178040
+397DB849>I<B612FCA439007FF800B3B3ADB612FCA41E397DB824>73
+D<B7FCA426007FF8C9FCB3ACEF0780A5170F1800A35FA25FA25F5F5E5EEE0FFE167FB8FC
+A431397DB839>76 D<B500F80403B512F06E5EA26E5ED8007FF1E000A2D97BFF161EA201
+796D5DA201786D5DA26E6C5DA36E6C4A5AA26E6C4A5AA26E6C4A5AA26E6C4A5AA26E6C14
+1EA36E6D5BA26E6D5BA26F6C5BA26F6C485AA36F6C485AA26F6C485AA26F6C48C7FCA292
+3803FF1EA36F13BCA26F13F8A2705AA2705AA213FCB500FC6D4848B612F0A2EE0F80EE07
+0054397DB85B>I<B500FC0203B512F0A28080C66C6D90390003F0006F6E5A81017B7F13
+798101787F6E7E6E7E6E7F6E7FA26E7F6E7F6E7F6E7F6F7E153F826F13806F13C06F13E0
+6F13F06F13F88117FCEE7FFEEE3FFF7013817013C17013E18218F17013F97013FDEF7FFF
+8383A28383838383187FA2183F181F01FC160FB500FC150718031801A244397DB84B>I<
+EDFFF8020FEBFF80027F14F0903A01FFC01FFC010790380007FFD91FFC010113C0D93FF0
+6D6C7E49486E7E49486E7E48496E7E48834890C86C7EA248486F1380A248486F13C0A200
+3F18E0A348486F13F0A400FF18F8AC007F18F06D5DA3003F18E0A26D5D001F18C0A26C6C
+4B13806C18006E5C6C6D4A5A6C5F6C6D4A5A6D6C4A5AD93FFC49485A6DB401075B0107D9
+C01F90C7FC010190B512FC6D6C14F0020F1480020001F8C8FC3D3B7BB948>I<B8FC17F0
+17FEEFFF8028007FF8000F13C0040113E07013F0EF7FF8EF3FFCA2EF1FFEA218FFA818FE
+A2EF3FFCA2EF7FF8EFFFF04C13E0040F13C091B7120017FC17E002F8C9FCB3A4B612FCA4
+38397DB841>I<EDFFF8020FEBFF80027F14F0903A01FFE03FFC010790380007FFD91FFC
+010113C049486D7FD97FE0EC3FF049486E7E488348496E7E4890C86C7EA248486F1380A2
+001F18C04981003F18E0A3007F18F04981A300FF18F8AC007F18F0A36D5D003F18E0A36C
+6C4B13C0A2000FDA1FC014806C6C90267FF0071300EDFFF86C903A81F07C0FFE6C903AC3
+C01E1FFC6CDA800F5BD97FE3ECBFF0D93FF36DB45AD91FFF5D010701C091C7FC01019038
+F01FFC6D6CB500F01308020F6E131C0200EBF9FC92260001FE133C9438FF80FC18FF8219
+F8A28319F0A27113E0A27113C0711380711300EF01FC3E4A7BB948>I<D907FF130E013F
+EBE01E90B5EAF83E0003ECFE7E3A07FC01FFFE390FF0001F4848130F4848130349130100
+7F140090C8FC167E5A163EA27F161E7F7F6D91C7FC13FC387FFFE014FEECFFF06C14FE6F
+7E6C816C15F06C816C81C681133F010F801301D9000F1480EC007F030F13C01503818100
+F0157FA3163FA27E17807E167F6C16007E6D14FE01E0495A01F813039039FF801FF800FC
+90B512E0D8F83F5CD8F00749C7FC39E0007FF02A3B7BB935>83 D<B600FC011FB512C0A4
+26007FF8C8381FC000725AB3B3181F013F94C7FC8060011F163E6D6C157E187C6D6C15FC
+6D6D495A6D6DEB07F06D01F0EB1FE0DA7FFEEBFFC0021FB6C8FC02075C020014F0030F13
+80423A7DB849>85 D<B600F00103B512E0A4C601F0C83807F0006E5E017F5F6E150FA201
+3F5F6E151F011F94C7FC6E5D6D163E6F147E6D167CA26F14FC6D5E6F13016D5E6F13036D
+5E811707027F5D6F130F023F5D6F131F021F92C8FC815F6E143EEE807E6E147CEEC0FC6E
+5C16E016E16E5C16F36E5C16FF6F5BA36F5BA26F90C9FCA26F5AA36F5AA26F5AA26F5A43
+3A7EB848>I<B6D8E01FB500FC90383FFFFCA4000101F0C7D83FFCC8EA7E006C71153C17
+1F6E197C017F701578836E7014F8013F6F5E6E1801011F4B6D5CA26E18036D4B6D5CA26D
+6D496D495A173C6F170F6D037C6D91C7FCEF787F6F5F6D4B6C6C131E816D02016E5BEFE0
+1F03F8177C027F01036E13784D7E03FCEE80F8023F49486C5C15FE021F010FEDC1E04D7E
+03FF16C36E49EDE3C0041E7F049E15F76E01BC6D5C04FC15FF6E95C8FC4C80A26E5F4C14
+3F6E5F4C141FA2037F5E4C140FA26F486E5AA2031F5E93C812036F5E5E3A7EB863>I<13
+FFB5FCA412077EAF4AB47E020F13F0023F13FC9138FE03FFDAF00013804AEB7FC00280EB
+3FE091C713F0EE1FF8A217FC160FA217FEAA17FCA3EE1FF8A217F06E133F6EEB7FE06E14
+C0903AFDF001FF80903AF8FC07FE009039F03FFFF8D9E00F13E0D9C00390C7FC2F3A7EB9
+35>98 D<EE7F80ED7FFFA4150381AF903801FF81010F13F1013F13FD9038FFC07F0003EB
+001FD807FC1307000F8048487F5B123FA2485AA312FFAA127FA27F123FA26C6C5B000F5C
+6C6C5B6C6C4913C02701FF80FD13FE39007FFFF9011F13E1010113012F3A7DB935>100
+D<903803FF80011F13F0017F13FC3901FF83FE3A03FE007F804848133F484814C0001FEC
+1FE05B003FEC0FF0A2485A16F8150712FFA290B6FCA301E0C8FCA4127FA36C7E1678121F
+6C6C14F86D14F000071403D801FFEB0FE06C9038C07FC06DB51200010F13FC010113E025
+257DA42C>I<EC1FF0903801FFFC010713FF90391FF87F8090383FE0FFD9FFC113C0A248
+1381A24813016E1380A2ED3E0092C7FCA8B6FCA4000390C8FCB3ABB512FEA4223A7DB91D
+>I<161FD907FEEBFFC090387FFFE348B6EAEFE02607FE07138F260FF801131F48486C13
+8F003F15CF4990387FC7C0EEC000007F81A6003F5DA26D13FF001F5D6C6C4890C7FC3907
+FE07FE48B512F86D13E0261E07FEC8FC90CAFCA2123E123F7F6C7E90B512F8EDFF8016E0
+6C15F86C816C815A001F81393FC0000F48C8138048157F5A163FA36C157F6C16006D5C6C
+6C495AD81FF0EB07FCD807FEEB3FF00001B612C06C6C91C7FC010713F02B377DA530>I<
+EA01F0EA07FC487EA2487EA56C5AA26C5AEA01F0C8FCA913FF127FA412077EB3A9B512F8
+A4153B7DBA1B>105 D<13FFB5FCA412077EAF92380FFFE0A4923803FC0016F0ED0FE0ED
+1F804BC7FC157E5DEC03F8EC07E04A5A141FEC7FE04A7E8181A2ECCFFEEC0FFF496C7F80
+6E7F6E7F82157F6F7E6F7E82150F82B5D8F83F13F8A42D3A7EB932>107
+D<13FFB5FCA412077EB3B3ACB512FCA4163A7DB91B>I<01FED97FE0EB0FFC00FF902601
+FFFC90383FFF80020701FF90B512E0DA1F81903983F03FF0DA3C00903887801F000749DA
+CF007F00034914DE6D48D97FFC6D7E4A5CA24A5CA291C75BB3A3B5D8FC1FB50083B512F0
+A44C257DA451>I<01FEEB7FC000FF903803FFF8020F13FE91381F03FFDA3C0113800007
+13780003497E6D4814C05CA25CA291C7FCB3A3B5D8FC3F13FFA430257DA435>I<903801
+FFC0010F13F8017F13FFD9FF807F3A03FE003FE048486D7E48486D7E48486D7EA2003F81
+491303007F81A300FF1680A9007F1600A3003F5D6D1307001F5DA26C6C495A6C6C495A6C
+6C495A6C6C6CB45A6C6CB5C7FC011F13FC010113C029257DA430>I<9038FE03F000FFEB
+0FFEEC3FFF91387C7F809138F8FFC000075B6C6C5A5CA29138807F80ED3F00150C92C7FC
+91C8FCB3A2B512FEA422257EA427>114 D<90383FF0383903FFFEF8000F13FF381FC00F
+383F0003007E1301007C130012FC15787E7E6D130013FCEBFFE06C13FCECFF806C14C06C
+14F06C14F81203C614FC131F9038007FFE140700F0130114007E157E7E157C6C14FC6C14
+F8EB80019038F007F090B512C000F8140038E01FF81F257DA426>I<130FA55BA45BA25B
+5BA25A1207001FEBFFE0B6FCA3000390C7FCB21578A815F86CEB80F014816CEBC3E09038
+3FFFC06D1380903803FE001D357EB425>I<B539F001FFF8A4000390C7EA1F00161E6E13
+3E6C153C6E137C6C15786E13F8017F5CECF001013F5C14F8011F495AA2ECFC07010F5CEC
+FE0F010791C7FC6E5A6D131E15BE6D13BC15FC6D5BA36E5AA26E5AA26E5AA26E5AA22D25
+7EA432>118 D E
+%EndDVIPSBitmapFont
+%DVIPSBitmapFont: Fh ecrm1000 10 89
+/Fh 89 126 df<486C1360000314E039070001C0000EEB038048EB070000181306003813
+0E0030130C0070131C00601318A200E01338481330A400CEEB338039FF803FE001C013F0
+A3007F131FA2393F800FE0390E0003801C1981B91C>16 D<001C1307007FEB1FC039FF80
+3FE0A201C013F0A3007F131F001CEB073000001300A400011470491360A2000314E090C7
+12C048130100061480000E130348EB070048130E485B006013181C1980B91C>I<B81280
+A2290280962A>21 D<DA0FF813FC91397FFF07FF903B01F807DF83C0903A07E001FF0F90
+3B1F8007FE1FE090393F000FFC137E16F85B9338F007804848010790C7FC1503ACB812F8
+A32801F80003F0C7FCB3AB486C497E267FFFE0B512F0A3333B7FBA30>27
+D<EC0FF8EC7FFE903901F80780903907E001C090391F8000E090383F0007017E497EA25B
+A2485A6F5AED018092C8FCA9ED03F0B7FCA33901F8000F1503B3AA486C497E267FFFE0B5
+12C0A32A3B7FBA2E>I<DA0FF0EB1FF0DA7FFEEBFFFC903B01F80F83F00F903C07E001CF
+C00380903C1F8000FF0001C090273F0007FE130F017E4948497EA2495CA248485C03076E
+5A03030203C7FC95C8FCA9F007E0BAFCA33C01F80003F0001F1807B3AA486C496C497E26
+7FFFE0B500C1B51280A3413B7FBA45>30 D<EB0380A3EB0FF0EB7FFE48B512803903F38F
+C03907C381E0390F8380F0D81F031338123E003C141C007C140C150E0078143E00F814FE
+1481A400FCEB80FC157800FE140012FF127F13C313E3EA3FFF6C7F14F86C13FE6CEBFF80
+000114C06C14E0013F13F01303ECBFF8148FEC83FC1481A2EC80FE157E123C12FF153EA4
+12FE00F8143C00E0147C12600070147815F8003814F0003C1381001EEB83E0000FEB87C0
+3907E39F803901FFFE006C5BEB1FE0EB0380A41F437BBD2A>36 D<141FEC7FC0903801F0
+E0903803C0600107137090380F803090381F00381518A25BA2133E133F15381530A21570
+5D5D140190381F838092CAFC1487148E02DC49B51280EB0FF85C4A9039003FF8000107ED
+0FC06E5D71C7FC6E140E010F150CD91DFC141C01391518D970FE143801E015302601C07F
+1470D803805D00076D6C5BD80F00EBC00148011F5C4890380FE003003E6E48C8FC007E90
+3807F8060203130E00FE6E5A6E6C5A1400ED7F706C4B13036F5A6F7E6C6C6D6C5B701306
+6C6C496C130E6DD979FE5B281FF001F07F133C3C07F80FE03FC0F86CB539800FFFF0C690
+26FE000313C0D91FF0D9007FC7FC393E7DBB41>38 D<121C127FEAFF80A213C0A3127F12
+1C1200A412011380A2120313005A1206120E5A5A5A12600A1979B917>I<146014E0EB01
+C0EB0380EB0700130E131E5B5BA25B485AA2485AA212075B120F90C7FCA25A121EA2123E
+A35AA65AB2127CA67EA3121EA2121F7EA27F12077F1203A26C7EA26C7E1378A27F7F130E
+7FEB0380EB01C0EB00E01460135278BD20>I<12C07E12707E7E7E120F6C7E6C7EA26C7E
+6C7EA21378A2137C133C133E131EA2131F7FA21480A3EB07C0A6EB03E0B2EB07C0A6EB0F
+80A31400A25B131EA2133E133C137C1378A25BA2485A485AA2485A48C7FC120E5A5A5A5A
+5A13527CBD20>I<1530B3A8B912FCA2C80030C8FCB3A836367BAF41>43
+D<121C127FEAFF80A213C0A3127F121C1200A412011380A2120313005A1206120E5A5A5A
+12600A19798817>I<B512FCA516057F941C>I<121C127FEAFF80A5EA7F00121C09097988
+17>I<1506A2150E150CA2151C151815381530A215701560A215E015C0A214011580A214
+0315005C1406A2140E140CA2141C1418A214381430A21470146014E05CA213015CA21303
+91C7FCA25B1306A2130E130C131C1318A213381330A213701360A213E05BA212015B1203
+90C8FCA25A1206A2120E120CA2121C1218A21238123012701260A212E05AA21F537BBD2A
+>I<EB03F8EB1FFF90387E0FC09038F803E03901E000F0484813780007147C48487FA248
+C77EA2481580A3007EEC0FC0A500FE15E0B3007E15C0A4007F141F6C1580A36C1500A26C
+6C133EA26C6C5B6C6C5BEBF0013900F803E090387E0FC0D91FFFC7FCEB03F823397DB62A
+>I<EB01C013031307131F13FFB5FCA2131F1200B3B3A7497E007FB512F0A31C3779B62A>
+I<EB0FF0EB7FFE48B57E3903E03FE0390F000FF0001E6D7E001C6D7E486D7E5A6E7E1260
+12FE6CEC7F807FA56CC7FC121CC8FCEDFF00A25D14015D14035D4A5A4A5A5D4A5A4AC7FC
+147E5C495A14E0495A495A49C8FC011EEB01805B5B49130348481400485A485A90C75A48
+B6FC5A5A485CB6FCA321377CB62A>I<EB07F8EB3FFF90B512C03901F80FF03903C007F8
+48486C7E390E0001FEEA0F80391FE000FF7FA56C5A6C5AC7485AA25D14035D4A5A5DEC0F
+80027FC7FCEB1FFCECFF809038000FE06E7EEC01FC816E7EED7F80A216C0A2153F16E0A2
+121EEA7F80A2487EA316C0157F491480007EC7FC0070ECFF006C495A121E390F8003F839
+07F00FF00001B512C06C6C90C7FCEB0FF823397DB62A>I<1538A2157815F8A214011403
+1407A2140F141F141B14331473146314C313011483EB030313071306130C131C13181330
+1370136013C01201EA038013005A120E120C5A123812305A12E0B712F8A3C73803F800AA
+4A7E0103B512F8A325387EB72A>I<0006140CD80780133C9038F003F890B5FC5D5D1580
+92C7FC14FC38067FE090C9FCAAEB07F8EB1FFE9038780F809038E007E03907C003F0496C
+7E130000066D7E81C8FC8181A21680A4121C127F5A7FA390C713005D12FC00605C12704A
+5A6C5C6C1303001E495A6C6C485A3907E03F800001B5C7FC38007FFCEB1FE021397CB62A
+>I<EC3FC0903801FFF0010713FC90380FE03E90383F800790387E001F49EB3F80484813
+7F485A12075B000FEC3F0049131E001F91C7FC5B123FA3127F90C9FCEB01FC903807FF80
+39FF1E07E090383801F0496C7E01607F01E0137E497F16805BED1FC0A390C713E0A57EA4
+7F123F16C0A2001FEC3F807F000F15006D5B000714FE6C6C5B6C6C485A3900FE07F09038
+7FFFC0011F90C7FCEB03FC23397DB62A>I<12301238123E003FB612E0A316C05A168016
+000070C712060060140E5D5D00E014304814705D5DC712014A5A4AC7FC1406140E5CA25C
+1478147014F05C1301A213035C1307A2130FA3131F5CA2133FA5137FA96DC8FC131E233A
+7BB72A>I<EB03F8EB1FFF017F13C09038FC07F03901E001F83903C0007C4848133C90C7
+123E48141E000E141F001E80A3121FA26D5B6D131E7FD80FF85B6D137C01FF13786C6D5A
+6CEBE3E0ECF780C601FFC7FC6D5A6D6C7E010F13E0013F7F01F97F3901E07FFE48486C7E
+380F800F48486C1380001E010113C0487F007C143F0078EC1FE0150F00F81407481403A2
+1501A36C15C0A200781403007C15806C14076CEC0F006C6C131ED807E0137C3903F803F0
+C6B55A013F1380D907FCC7FC23397DB62A>I<EB03F8EB1FFF017F13C03901FC07E04848
+6C7E3907E001F8000F6D7E4848137E5B003F80A248C71380A25AED1FC0A516E0A56C143F
+A36C7E157F121F6C6C13FF6C6C13DF000313013901F0039F3900FC0F1FD93FFC13C0EB07
+F090C7FCA2153F1680A216005D120F486C137E486C5BA24A5A4A5A49485A381F000F001C
+EB1F80260F807FC7FC3807FFFE000113F838003FC023397DB62A>I<121C127FEAFF80A5
+EA7F00121CC7FCB2121C127FEAFF80A5EA7F00121C092479A317>I<121C127FEAFF80A5
+EA7F00121CC7FCB2121C127FEAFF80A213C0A3127F121C1200A412011380A2120313005A
+1206120E5A5A5A12600A3479A317>I<EF01C0EF0780EF1E0017F8EE03E0040FC7FC163C
+16F0ED03C0030FC8FC153CEC01F0EC07C0021EC9FC1478EB01E0EB0780011ECAFC13F8EA
+03E0000FCBFC123C12F0A2123C120FEA03E0EA00F8131EEB0780EB01E0EB0078141EEC07
+C0EC01F0EC003C150FED03C0ED00F0163C160FEE03E0EE00F8171EEF0780EF01C0322E79
+AB41>I<007FB812F8B912FCCCFCB0B912FC6C17F836147B9E41>I<12E01278121EEA07C0
+EA01F0EA003C130FEB03C0EB00F0143C140FEC03E0EC00F8151EED0780ED01E0ED007816
+1EEE07C0EE01F0EE003C170FEF03C0A2EF0F00173CEE01F0EE07C0041EC7FC1678ED01E0
+ED0780031EC8FC15F8EC03E0020FC9FC143C14F0EB03C0010FCAFC133CEA01F0EA07C000
+1ECBFC127812E0322E79AB41>I<EB3FE03801FFFE3907C03F80390E000FC0003CEB07F0
+00301303007014F8007C130100FE14FC7EA4127E003CEB03F8C7FCEC07F0A2EC0FE0EC1F
+80EC3F00147E147C5C495A5C495A5CA249C7FCA31306AA90C8FCA8130EEB3F80497EA56D
+5A010EC7FC1E3B7CBA27>I<1538A3157CA315FEA34A7EA34A6C7EA202077FEC063FA202
+0E7FEC0C1FA2021C7FEC180FA202387FEC3007A202707FEC6003A202C07F1501A2D90180
+7F81A249C77F167FA20106810107B6FCA24981010CC7121FA2496E7EA3496E7EA3496E7E
+A213E0707E1201486C81D80FFC02071380B56C90B512FEA3373C7DBB3E>65
+D<B712E016FC16FF0001903980007FC06C90C7EA1FE0707E707E707EA2707EA283A75F16
+035F4C5A4C5A4C5A4C5AEEFF8091B500FCC7FCA291C7EA7F80EE1FE0EE07F0707E707E83
+707EA21880177F18C0A7188017FFA24C13005F16034C5AEE1FF8486DEB7FF0B812C094C7
+FC16F832397DB83B>I<913A01FF800180020FEBE003027F13F8903A01FF807E07903A03
+FC000F0FD90FF0EB039F4948EB01DFD93F80EB00FF49C8127F01FE153F12014848151F48
+48150FA248481507A2485A1703123F5B007F1601A35B00FF93C7FCAD127F6DED0180A312
+3F7F001F160318006C7E5F6C7E17066C6C150E6C6C5D00001618017F15386D6C5CD91FE0
+5C6D6CEB03C0D903FCEB0F80902701FF803FC7FC9039007FFFFC020F13F002011380313D
+7BBA3C>I<B712C016F816FE000190398001FF806C90C7EA3FE0EE0FF0EE03F8707E707E
+177FA2EF3F8018C0171F18E0170F18F0A3EF07F8A418FCAC18F8A4EF0FF0A218E0A2171F
+18C0EF3F80A2EF7F0017FE4C5A4C5AEE0FF0EE3FE0486DEBFF80B8C7FC16F816C036397D
+B83F>I<B812FEA3000190388000076C90C8FC173F838383A383A31880170116C0A394C7
+FCA31501A21503150F91B5FCA3EC000F15031501A21500A21860A318E093C712C0A41701
+A3EF0380A21707A2170F173F177F486D903807FF00B9FCA333397EB838>I<B812F8A300
+01903880001F6C90C71201EE00FC177C173C171CA2170CA4170E1706A2ED0180A21700A4
+1503A21507151F91B5FCA3EC001F15071503A21501A692C8FCAD4813C0B612C0A32F397D
+B836>I<DBFF8013C0020FEBF001023F13FC9139FF803F03903A03FC000787D90FF0EB03
+CF4948EB00EF4948147F4948143F49C8121F485A4848150F48481507A248481503A2485A
+1701123F5B007F1600A448481600AB93B6FCA26C7E9338007FE0EF3FC0A2123F7F121FA2
+6C7EA26C7EA26C7E6C7E6C6C157F6D7E6D6C14FF6D6C14EFD90FF8EB03C7D903FEEB0783
+903A00FFC03F0191393FFFFC00020F01F0130002001380383D7CBA41>I<B648B512FEA3
+0001902680000313006C90C76C5AB3A491B6FCA391C71201B3A6486D497EB648B512FEA3
+37397DB83E>I<B612C0A3C6EBC0006D5AB3B3AD497EB612C0A31A397EB81E>I<013FB512
+E0A39039001FFC00EC07F8B3B3A3123FEA7F80EAFFC0A44A5A1380D87F005B0070131F6C
+5C6C495A6C49C7FC380781FC3801FFF038007F80233B7DB82B>I<B649B5FCA300010180
+9038007FF06C90C8EA3F80053EC7FC173C17385F5F4C5A4C5A4CC8FC160E5E5E5E5E4B5A
+ED0780030EC9FC5D153E157E15FF5C4A7F4A6C7E140E4A6C7E4A6C7E14704A6C7E4A6C7E
+14804A6C7E6F7EA26F7F707EA2707E707EA2707EA2707E707EA2707E707F8484486D497F
+B6011FEBFF80A339397DB841>I<B612E0A3000101C0C8FC6C90C9FCB3AD1718A5173817
+30A31770A317F0A216011603160FEE1FE0486D13FFB8FCA32D397DB834>I<B5933807FF
+F86E5DA20001F0FC002600DFC0ED1BF8A2D9CFE01533A3D9C7F01563A3D9C3F815C3A2D9
+C1FCEC0183A3D9C0FEEC0303A2027F1406A36E6C130CA36E6C1318A26E6C1330A36E6C13
+60A26E6C13C0A3913901FC0180A3913900FE0300A2ED7F06A3ED3F8CA2ED1FD8A3ED0FF0
+A3486C6D5A487ED80FFC6D48497EB500C00203B512F8A2ED018045397DB84C>I<B59138
+07FFFE8080C69238007FE06EEC1F80D9DFF0EC0F001706EBCFF8EBC7FCA2EBC3FEEBC1FF
+A201C07F6E7EA26E7E6E7E81140F6E7E8114036E7E168080ED7FC016E0153FED1FF0ED0F
+F8A2ED07FCED03FEA2ED01FF6F1386A2EE7FC6EE3FE6A2EE1FF6EE0FFEA216071603A216
+011600A2177E486C153E487ED80FFC151EB500C0140EA2170637397DB83E>I<EC03FF02
+1F13E09138FE01FC903901F8007ED907E0EB1F8049486D7ED93F80EB07F049C76C7E01FE
+6E7E48486E7E49157E0003167F4848ED3F80A24848ED1FC0A2001F17E049150F003F17F0
+A3007F17F8491507A300FF17FCAC007F17F86D150FA3003F17F0A26C6CED1FE0A36C6CED
+3FC0000717806D157F000317006C6C15FEA26C6C4A5A017F4A5A6D6C495A6D6C495AD907
+E0EB1F80D903F8017FC7FC903900FE01FC91381FFFE0020390C8FC363D7BBA41>I<B712
+C016FC16FF0001D9800013C06C90C7EA1FE0707EEE03F883707EA2707EA21880A71800A2
+4C5AA24C5A5FEE0FF04C5AEEFF8091B548C7FC16F091CAFCB3A5487FB6FCA331397EB838
+>I<EC03FF021F13E09138FE01FC903901F8007ED907E0EB1F8049486D7ED93F80EB07F0
+49C76C7E01FE6E7E48486E7EA24848157F0007178049153F000F17C049151F001F17E0A2
+4848ED0FF0A3007F17F8A2491507A200FF17FCAC007F17F8A26D150FA2003F17F0A26C6C
+ED1FE0A36C6CED3FC00007027C14804AB4FC3C03F80383807F003B01FC0701C0FEEC0E00
+2600FE0CEBE1FC017FEC63F8D93F8CEB77F0D91FCCEB3FE0D907EE14806DB449C7FC0100
+D981FC130CEC1FFF0203131C91C7001E131C161F183CEF807CEFC0F8EE0FFFA318F08218
+E07013C07013809338007E00364B7BBA41>I<B612FEEDFFE016F8000190388007FE6C90
+C76C7EEE3FC0707E707E707EA2707EA283A65FA24C5AA24C5A4C5AEE3F8004FFC8FCED07
+FC91B512E05E9138000FF0ED03F8ED00FE82707E707EA2161F83A583A6F00180A217F816
+0F1803486D01071400B66D6C5A04011306933800FE0ECAEA3FFCEF07F0393B7DB83D>I<
+D90FF813C090383FFE0190B512813903F807E33907E000F74848137F4848133F48C7121F
+003E140F007E1407A2007C140312FC1501A36C1400A37E6D14006C7E7F13F86CB47E6C13
+F8ECFF806C14E06C14F86C14FEC680013F1480010714C0EB007F020713E0EC007FED3FF0
+151F150FED07F8A200C01403A21501A37EA216F07E15036C15E06C14076C15C06C140F6D
+EB1F80D8FBF0EB3F00D8F0FE13FE39E03FFFF8010F13E0D8C00190C7FC253D7CBA2E>I<
+003FB812E0A3D9C003EB001F273E0001FE130348EE01F00078160000701770A300601730
+A400E01738481718A4C71600B3B0913807FF80011FB612E0A335397DB83C>I<B6903807
+FFFEA3000101809038007FE06C90C8EA1F80EF0F001706B3B2170E6D150C80171C133F17
+186D6C14385F6D6C14F06D6C5C6D6C495A6D6CEB07806D6C49C7FC91387F807E91381FFF
+F8020713E09138007F80373B7DB83E>I<B500FC91387FFF80A30003018091380FFC006C
+90C8EA07E0715A6C705A6E1403017F93C7FCA280013F1506A26E140E011F150C80010F5D
+A28001075DA26E147001031560A26D6C5CA2806D4A5AA2ED8003027F91C8FCA291383FC0
+06A215E0021F5BA2EDF01C020F1318A26E6C5AA215FC02035BA2EDFEE002015BA26E6C5A
+A36FC9FCA3153EA2151CA3393B7EB83E>I<B5D8FC07B5D8F001B5FCA30007902780001F
+FEC7EA1FF86C48C7D80FF8EC07E000010307ED03C01B807F6C6F6C1500A26E5F017F6E6C
+1406A280013F4A6C5CA280011F4A6D5BEE067FA26D6C010E6D5BEE0C3FA26D6C011C6D5B
+EE181FA26D6C6F5BEE300FA26D6C6F485AEE6007A26D6C4CC7FC9338C003FCA203805D91
+3B7F818001FE06A203C1150EDA3FC3C7EAFF0CA203E3151CDA1FE6EC7F98A215F6DA0FFC
+EC3FF0A302075E4B141FA202035E4B140FA202015E4B1407A2020093C8FC4B80503B7EB8
+55>I<B500FE91383FFFE0A3000301E0913807FE00C649EC03F0017F6F5A606D6C5D6D6C
+140395C7FC6D6C1406A26D6C5C6D6C141C17186D6C143817306D6D5B6E6C13E05F91383F
+E0015F91381FF003DA0FF890C8FC1606913807FC0E160C913803FE1C913801FF185E6E13
+B016E0157F6F5AB3A24B7E023FB512C0A33B397FB83E>89 D<003FB7FCA39039FC0001FE
+01C0130349495A003EC7FC003C4A5A5E0038141F00784A5A12704B5A5E006014FF4A90C7
+FCA24A5A5DC712074A5AA24A5A5D143F4A5AA24A5A92C8FC5B495AA2495A5C130F4948EB
+0180A2495A5C137F495A16034890C7FC5B1203485AEE0700485A495C001F5D48485C5E48
+48495A49130FB8FCA329397BB833>I<EAFFFCA2EAFC00B3B3B3B3A7EAFFFCA20E5379BD
+17>I<EAFFFCA21200B3B3B3B3A712FFA20E537FBD17>93 D<007FB81280B912C0A26C17
+803204797041>95 D<EB1FE0EBFFFC3803E03F3907000F80390F8007E0486C6C7E13E06E
+7EA26E7E6C5A6C5AC8FCA4147FEB07FFEB3FE0EBFE00EA03F8EA0FF0EA1FC0123F485A90
+C7FC160C12FEA31401A26C13036CEB077C903980063E18383FC01E3A0FE0781FF03A03FF
+F00FE03A007F8007C026277DA52A>97 D<EA03F012FFA3120F1203B0EC1FE0EC7FF89038
+F1E03E9039F3801F809039F7000FC001FEEB07E049EB03F049EB01F85BED00FCA216FEA2
+167E167FAA167E16FEA216FC15016D14F8ED03F07F01EEEB07E001C6EB0FC09039C7801F
+00903881E07E903800FFF8C7EA1FC0283B7EB92E>I<EB03FC90381FFF8090387E03E039
+01F80070484813F83907E001FC380FC003A2EA1F80123F90380001F848EB00F01500A212
+7E12FEAA127E127FA26C14067F001F140E6D130C000F141C6C6C13386C6C13706C6C13E0
+39007C07C090381FFF00EB07F81F277DA525>I<ED0FC0EC03FFA3EC003F150FB0EB03F8
+EB1FFF90387E078F9038F801EF3903F0007F4848133F4848131FA24848130F123F90C7FC
+5AA2127E12FEAA127E127FA27EA26C6C131FA26C6C133F6C6C137F6C6CEBEFF03A01F801
+CFFF39007C078F90381FFE0FD907F813C0283B7DB92E>I<EB07F8EB1FFF90387C0FC039
+01F803E03903F001F0D807E013F8380FC0004848137CA248C7127E153E5A153F127E12FE
+A3B7FCA248C8FCA5127EA2127FA26C14037F001F14076C6C13060007140E6D131CD801F0
+13386C6C137090387E03E090381FFF80903803FC0020277EA525>I<147E903803FF8090
+380FC1E0EB1F8790383F0FF0137EA213FCA23901F803C091C7FCADB512FCA3D801F8C7FC
+B3AB487E387FFFF8A31C3B7FBA19>I<ED03F090390FF00FF890393FFC3C3C9039F81F70
+7C3901F00FE03903E007C03A07C003E010000FECF000A248486C7EA86C6C485AA200075C
+6C6C485A6D485A6D48C7FC38073FFC38060FF0000EC9FCA4120FA213C06CB512C015F86C
+14FE6CECFF804815C03A0F80007FE048C7EA0FF0003E140348140116F8481400A56C1401
+007C15F06CEC03E0003F1407D80F80EB0F80D807E0EB3F003901FC01FC39007FFFF00107
+90C7FC26387EA52A>I<EA03F012FFA3120F1203B0EC0FF0EC3FFCECF03F9039F1C01F80
+9039F3800FC0EBF70013FE496D7EA25BA35BB3A3486C497EB500C1B51280A3293A7EB92E
+>I<EA0380EA0FE0487EA56C5AEA0380C8FCAAEA03F012FFA312071203B3AA487EB512C0
+A312387EB717>I<EB01C0EB07F0EB0FF8A5EB07F0EB01C090C7FCAAEB01F813FFA31307
+1301B3B3A2123C127E00FF13F01303A214E038FE07C0127C383C0F00EA0FFEEA03F81549
+84B719>I<EA03F012FFA3120F1203B1913801FFFCA39138007FC01600157C15705D4A5A
+4A5A4AC7FC141E1438147814FC13F1EBF3FEEBF73F01FE7FEBF81F496C7E8114076E7E6E
+7E811400157E157F811680ED1FC0486CEB3FF0B500C0B5FCA3283A7EB92C>I<EA03F012
+FFA3120F1203B3B3AD487EB512C0A3123A7EB917>I<2703F00FF0EB1FE000FFD93FFCEB
+7FF8913AF03F01E07E903BF1C01F83803F3D0FF3800FC7001F802603F70013CE01FE14DC
+49D907F8EB0FC0A2495CA3495CB3A3486C496CEB1FE0B500C1B50083B5FCA340257EA445
+>I<3903F00FF000FFEB3FFCECF03F9039F1C01F803A0FF3800FC03803F70013FE496D7E
+A25BA35BB3A3486C497EB500C1B51280A329257EA42E>I<EB03FE90380FFF8090383E03
+E09038F800F84848137C48487F48487F4848EB0F80001F15C090C712074815E0A2007EEC
+03F0A400FE15F8A9007E15F0A2007F14076C15E0A26C6CEB0FC0000F15806D131F6C6CEB
+3F006C6C137EC66C13F890387E03F090381FFFC0D903FEC7FC25277EA52A>I<3903F01F
+E000FFEB7FF89038F1E07E9039F3801F803A07F7000FC0D803FEEB07E049EB03F04914F8
+49130116FC150016FEA3167FAA16FEA3ED01FCA26DEB03F816F06D13076DEB0FE001F614
+C09039F7803F009038F1E07E9038F0FFF8EC1FC091C8FCAB487EB512C0A328357EA42E>
+I<D903F813C090381FFE0190387E07819038FC01C33903F000E300071477484813374913
+3F001F141F485A150F48C7FCA312FEAA127FA37E6D131F121F6D133F120F6C6C137F6C6C
+13EF3901F801CF39007E078F90381FFE0FEB07F890C7FCABED1FE00203B5FCA328357DA4
+2C>I<3807E01F00FFEB7FC09038E1E3E09038E387F0380FE707EA03E613EE9038EC03E0
+9038FC0080491300A45BB3A2487EB512F0A31C257EA421>I<EBFF03000313E7380F80FF
+381E003F487F487F00707F12F0A2807EA27EB490C7FCEA7FE013FF6C13E06C13F86C7F00
+037FC67F01071380EB007F141F00C0EB0FC01407A26C1303A37E15806C13077EEC0F00B4
+131E38F3C07C38E1FFF038C03F801A277DA521>I<1318A51338A31378A313F812011203
+1207001FB5FCB6FCA2D801F8C7FCB215C0A93800FC011580EB7C03017E13006D5AEB0FFE
+EB01F81A347FB220>I<D803F0EB07E000FFEB01FFA3000FEB001F00031407B3A4150FA3
+151F12016D133F0000EC77F86D9038E7FF8090383F03C790381FFF87903A03FC07E00029
+267EA42E>I<B538803FFEA33A0FF8000FF06C48EB07E00003EC03C06D148000011500A2
+6C6C1306A26D130E017E130CA26D5BA2EC8038011F1330A26D6C5AA214E001075BA29038
+03F180A3D901FBC7FCA214FF6D5AA2147CA31438A227257EA32C>I<B53A1FFFE03FFEA3
+260FF8009038000FF86C48017EEB03E018C00003023EEB0180A26C6C013FEB0300A36C6C
+EC8006156FA2017E9038EFC00C15C7171CD93F01EBE01815830281EBF038D91F83143015
+0102C3EBF87090260FC6001360A2D907E66D5A02EC137CA2D903FCEB7F804A133FA20101
+92C7FC4A7FA20100141E4A130E0260130C37257EA33C>I<B538807FFFA33A03FE003FF0
+0001EC1F80000092C7FC017E131C6D13186D6C5AECC070010F5B6D6C5AECF180EB03FB6D
+B4C8FC6D5AA2147F804A7E8114CF903801C7E090380383F090380703F8EB0601496C7E01
+1C137E49137F01787F496D7E486C80000FEC3FF0D8FFFE90B51280A329247FA32C>I<B5
+38803FFEA33A0FF8000FF06C48EB07C00003EC03806C7E16007F00001406A2017E5BA213
+7F6D5BA26D6C5AA2ECC070010F1360A26D6C5AA214F101035BA2D901FBC7FCA214FF6D5A
+A2147CA31438A21430A214701460A25CA2EA7C0100FE5B130391C8FC1306EAFC0EEA701C
+6C5AEA1FF0EA0FC027357EA32C>I<003FB512FCA2EB8003D83E0013F8003CEB07F00038
+EB0FE012300070EB1FC0EC3F800060137F150014FE495AA2C6485A495AA2495A495A495A
+A290387F000613FEA2485A485A0007140E5B4848130C4848131CA24848133C48C7127C48
+EB03FC90B5FCA21F247EA325>I<EC01F8140FEC3F80ECFC00495A495A495AA2130F5CB3
+A7131F5C133F49C7FC13FEEA03F8EA7FE048C8FCEA7FE0EA03F8EA00FE137F6D7E131F80
+130FB3A7801307A26D7E6D7E6D7EEC3F80EC0FF814011D537ABD2A>I<126012F0B3B3B3
+B3A91260045377BD17>I<12FCEAFFC0EA07F0EA01FCEA007E7F80131F80130FB3A78013
+07806D7E6D7EEB007EEC1FF0EC07F8EC1FF0EC7E00495A495A495A5C130F5CB3A7131F5C
+133F91C7FC137E485AEA07F0EAFFC000FCC8FC1D537ABD2A>I E
+%EndDVIPSBitmapFont
+%DVIPSBitmapFont: Fi ecbx1440 14.4 34
+/Fi 34 118 df<EE7FFC031FB57E4AB612E0020715F8023F9038C00FFC913AFFFC0001FE
+4901F0EB007F010701C0EB03FF4949497F4990C75A5B5C495A4D7F01FF6F5B5CA27190C7
+FC715AEF00F895C8FCAA0407B512C0BAFCA5C601F8C7120F83B3B3A6B6D8F807B612C0A5
+42547DD349>28 D<B712E0AB230B7F9F2C>45 D<151E153E15FE1403140F147FEB07FF00
+03B5FCB6FCA3EBF87FEAFC00C7FCB3B3B3A6007FB712FCA52E4E76CD42>49
+D<EC1FFE49B512F0010F14FC013FECFF804915E02701FF803F7F2703FC000713FCD807F0
+01017F48486D7FD81F806E138048C87E7013C0D87FE016E001F8806D16F000FF817F7013
+F8A56C5AA26C5A6C5AEA0380C914F05EA218E05E18C05E18804C13005F4C5A4C5A5F4B5B
+4B5B4B5B94C7FCED0FFC4B5A4B5AED7FC04B5A4A90C8FCEC03FC4A5A4A4814F84A5A4A5A
+4AC8FC02FEEC01F0495A495A495A5CD90F80140349C8FC013E1507017FB7FC90B812E05A
+5A5A5A5A5A5AB9FC18C0A4354E7ACD42>I<913807FFC0027F13FC0103B67E010F15E090
+261FF80313F890267FC0007F01FEC7EA3FFE48488148486E138013FE486C6C6D13C08048
+17E080A66C5B18C06C5B6C90C75AD80038168090C8FC4C1300A24C5A5F4C5A4B5B4B13C0
+030F5BDB7FFEC7FC91387FFFF816C016FCEEFF80DA000313E09238007FF8EE3FFE707E70
+138018C07013E018F07013F8A218FC82A218FEA3EA03C0EA0FF0EA3FFC487EA2B5FCA218
+FCA25E18F8A26C4816F0495C4916E0D83FE04A13C06C485CD80FF04A1380D807FE91387F
+FE003B03FFE003FFFC6C90B65A6C6C15E0010F92C7FC010114FCD9001F1380374F7BCD42
+>I<17FC1601A216031607160FA2161F163F167FA216FF5D5DA25D5D5D167F153E157E15
+FC15F8EC01F01403EC07E015C0EC0F80141FEC3F00143E5C14FC495A5C495A1307495A5C
+49C7FC5B137E137C5B1201485A5B485A120F485A90C8FC123E127E5ABA1280A5C901FCC7
+FCAF021FB71280A5394F7CCE42>I<486C150601F0153E01FEEC01FED9FFF0133F91B65A
+5F5F5F5F5F94C7FC16FC5E16E093C8FC15FC01F0138091CAFCAC913807FF80023F13F891
+B512FE01F36E7E9026FFFC0113E09139E0007FF891C76C7E496E7E01F86E7E5B70138049
+16C0C9FC18E08218F0A418F8A31203EA0FE0EA3FF8487EA212FF7FA218F0A25B5E6C4816
+E05B01C016C06CC85A18806C6C4A13007FD80FF04A5A6C6CECFFFCD803FE4913F02701FF
+E00F5B6C6CB612806D92C7FC010F14F8010114C09026003FFCC8FC354F7ACD42>I<EA07
+E0EA1FF8EA3FFCEA7FFEA2B5FCA6EA7FFEA2EA3FFCEA1FF8EA07E0C7FCB3A3EA07E0EA1F
+F8EA3FFCEA7FFEA2B5FCA6EA7FFEA2EA3FFCEA1FF8EA07E0103576B425>58
+D<932603FFF01407047F01FF5C0307B600E05B033F03F85B92B700FE5B02039126C003FF
+5B020F01F8C7EA3FC1023F01C0EC0FE391B5C80003B5FC4901FC814949814901E082011F
+498249498292CA7E4948834948835A4A83485B4885A2484984A2485B87A2485B87A25AA2
+98C8FC91CFFCA2B5FCAE7E067FB7128080A37E95C76C90C7FC807EA36C7FA26C7FA26C7F
+7E806C7F137F6D7E816D6D93B5FC01077F6D01F85D6D7F6D01FF5D023F01E0EC0FEF020F
+01FCEC3FE30203903AFFE001FF81020091B6C6FC033F03FC133F030703F0130FDB007F02
+801303040301F8CAFC595479D267>71 D<B81280A5D8000701F0C7FCB3B3B3B2B81280A5
+29527DD130>73 D<B812E0A5D8000F01E0CAFCB3B3A91AF8A419011AF0A51903A31907A2
+190F1AE0191FA2193F197F19FF60180760187F0503B5FCBB12C0A545527CD14F>76
+D<B600F84BB612FC818181A2D800076E91C7383FE00070EE0F80828214DF02CF7F02C77F
+8202C37F14C102C0806F7F836F7F816F7F6F7F83816F7F6F80707F8482707F707F707F84
+82707F7080717F8583717F717F85717F83717F7114801AC07213E0847213F07213F81AFC
+7213FE847213FF72148F1BCF7313EF857313FF85A285858585A286868686A286868686EB
+1FF0B600FE177F1B3F1B1F1B0FA25E527CD167>78 D<B912FCF0FFE019FE737E1AE0D800
+0F01E0C7003F7F060313FC06007F737E7313807313C07313E0851BF0A21BF885A21BFCA9
+1BF8A3611BF0A21BE04F13C0614F13804F13004F5A060713F8063F5B92B812C097C7FC19
+F8198003E0CBFCB3AEB712FEA54E527CD15A>80 D<93381FFF800303B512FC033FECFFC0
+92B712F00207D9F80113FE021F903AC0003FFF804A48C700077FDAFFF8020113F049496E
+7F49496F7E49496F7E49496F7E4990C96C7F4948707F4948707F01FF854849707F4A8248
+86A24849717E48864A83A2481B80A248497113C0A4481BE0A291CB7EA3B51AF0AF6C1BE0
+A36E5FA26C1BC0A36C1B806E5FA26C1B006E5F6C62A26C6DD903FC4A5A6CDB0FFF5D6E49
+EBC0016C4B01E05C6D6C90277E07F0035B6E9039F801F807902A3FFF01F000780F5B6D04
+7C5C6DD981E06D4890C7FC6D01E191381F7FFE010101F1EDFFF86DD9F9F06D5BDA3FFF16
+C06E6D013F5B02079027FE01FFFEC8FC020190B612F8DA003F4B141003071838DB001FEB
+83F893C7EA03FC1C7885726C14F8F2C003F2F01F97B512F084A31CE085A27314C01C8085
+1C00735B735B735B735B9638003FC0556A79D263>I<B912E018FF19F019FE737ED80007
+01F0C714E0060F7F060313FC06007F737E737F8587737FA28785A287A863A26163636163
+4F90C8FC4F5A4F5A06035B060F13E095B5128092B748C9FC19F019C019F09226F0000713
+FC050013FF063F7F727F727F727F727FA2727FA28486A886A71D707513F8A2851C017301
+C013F0A273EBE003B86C6D9038F007E0739038FC1FC0070190B51280736C1400080F5BCE
+13F85D537CD162>I<DA0FFE141C91B500F0133C010702FC137C011F02FF13FC017F15C1
+9026FFF00113E148903980001FFB4890C7EA07FFD807FC14014848804848153F171F4848
+150FA2007F1607491503A2170112FFA217007FA26D167CA27F7F6D93C7FC6C7E14C014F8
+ECFF806C14F8EDFFC06C15FC6CEDFF8017F06C16FC6C826C707E6C836D82011F82010782
+13016D6C81020781EC007F030380ED003F040314801600173F837113C0838312F883A383
+7EA319807EA26C5E19007F6D4B5A7F6D4B5A01FC4B5A6D151FD9FFC04A5AD97FF8ECFFE0
+28FE1FFF80075B010790B6C7FCD8FC0115FC486C6C14F048010F14C0489026007FFCC8FC
+3A5479D249>I<003FBB12FCA59126C0007FEB000301FCC7ED003FD87FF0F00FFE491807
+49180349180190C81600A2007E1A7EA3007C1A3EA500FC1A3F481A1FA6C91700B3B3AC49
+B912C0A550517BD05B>I<EC3FFE0107B512E0011F14FC017F14FF2701FFC00F13C02703
+FE00037F486C01007F6E6D7E486D80707EA2707EA3707F6C5B6C90C7FC6C5AC9FCA60307
+B5FC0203B6FC147F0103B7FC011FEBF00F017F1300EBFFFC000313F04813C0485B4890C7
+FC5A5B485AF081F012FF5BA35EA26D5C127F6D5C003F03F713C36DD901E314E06CD9C007
+14FF00079026F01F8114C06C90B5C61480C602FC6D1300011F01F0EB3FFC01010180EB07
+F03C387CB642>97 D<913803FFE0023F13FE91B67E010315E0010F9038003FF8D93FFCEB
+07FC4948497E4948131F4849497E485B485BA24890C7FC5A5B003F6F5A705A705A007F92
+C8FC5BA312FFAD127F7FA3123F7F6CEE0F80A26C6D141F18006C6D5C6C6D143E6C6D147E
+6C6D5C6D6C495A6DB4EB07F0010F9038C01FE06D90B5128001014AC7FCD9003F13F80203
+138031387CB63A>99 D<943803FF80040FB5FCA5EE003F170FB3A4913803FF80023F13F8
+49B512FE0107ECFF8F011F9038C03FEF90273FFE0007B5FCD97FF8130149487F48498048
+4980484980488291C8FC5A5B123FA2127F5BA312FFAD127FA37F123FA3121F7F6C5E6C6D
+5C5F6C6D91B5FC6C6D5B6C6D4914E0D97FFCD90FEFEBFF80D91FFFEB7F8F010790B5120F
+010114FC6D6C13E00207010049C7FC41547CD249>I<913807FF80027F13F849B512FE01
+076E7E011F010313E0903A3FFC007FF0D97FF06D7E49486D7E4849130F48496D7E488248
+90C77E1880485A82003F17C0A3485A18E082A212FFA290B8FCA401FCCAFCA6127FA37F12
+3FA2EF03E06C7E17076C17C06C6D140F18806C6D141F6C6DEC3F006C6D147ED97FFC495A
+D91FFFEB07F86D9038E03FF0010390B512C001005D023F01FCC7FC020113E033387CB63C
+>I<ED1FF8913803FFFE020FEBFF80023F14C09139FFF83FE001039038E0FFF049138049
+010113F85BEB3FFEA2EB7FFCA26F13F0495AEE7FE0EE1F8093C7FCAEB712C0A5C601F8C8
+FCB3B3A7B612FEA52D547CD328>I<DA1FFE14FE49B539E007FF80010FDAFC1F13C0013F
+DAFF7F13E090267FF807EBFF072701FFE001EBF07F48497E484990387FF83F91C7003F14
+C048EEFC1F489338FE070049021F90C7FCA2003F82A9001F5EA26D143F6C5E6C5E6E137F
+6C6D495A6C6D485B6CD9F80713804890B6C8FCD803EF14FC01C114E02707C01FFEC9FC49
+CBFCA2487EA37FA27F13FC90B612FE6CEDFFF017FCEFFF806C8318F06C836C837F48B87E
+1207D80FFCC700037F4848EC003F4848150F48486F138083485A83A56D5D007F18006D5D
+003F5F6C6C4B5A01FE153FD807FFED7FF06C01C049485AC601FC011F1380013FB648C7FC
+010F15F8010115C0D9000F01F8C8FC3B4F7CB542>I<EB3FF8B5FCA51203C6FCB3A4EE1F
+FC93B57E030314E0030F14F892391FC07FFC92397E003FFE03F86D7EECF9F04B6D7FECFB
+C0ECFF8092C76C7FA25CA25CA45CB3ACB6D8F807B612C0A542537CD249>I<133FEBFFC0
+487F487FA2487FA66C5BA26C5B6C5B013FC7FC90C8FCAEEB1FF8B5FCA512017EB3B3A6B6
+12F0A51C547CD324>I<EB3FF8B5FCA51203C6FCB3B3B3B1B612F8A51D537CD224>108
+D<D93FF0D91FF84AB47EB591B56C010F13F8030302E0013F13FE030F6E90B6FCDB3F8090
+27F803F80F7F922A7E007FFC07E0077F000302F890283FFE0F80037FC6D9F1F0011F4948
+7EDAF3E0DAFF3E814B153CDAF7805D92C76C496D7F14FF4A5EA24A5EA34A5EB3ADB6D8F8
+0FB66CB612F8A565367BB56E>I<D93FF0EB1FFCB591B57E030314E0030F14F892391FC0
+7FFC92397E003FFE000302F86D7EC6EBF1F04B6D7FECF3C0ECF78092C76C7F14FF5CA25C
+A45CB3ACB6D8F807B612C0A542367CB549>I<913801FFC0023F13FE91B67E010315E001
+0F018013F8903A3FFC001FFED97FF0EB07FF49486D7F48496D7F48496D7F91C8127F4883
+488349153F001F83A2003F8349151FA2007F83A400FF1880AC007F1800A3003F5F6D153F
+A2001F5FA26C6C4B5AA26C6D4A5A6C5F6C6D495B6C6D495B6D6C4990C7FCD93FFCEB1FFE
+6DB46CB45A010790B512F0010115C0D9003F49C8FC020313E039387CB642>I<D93FF8EB
+7FF0B50107B5FC031F14C0037F14F09126F9FF0013FCDAFFF8EB3FFF000302E0010F7FC6
+02806D7F92C76C7F4A824A804A6E7F85187F85A2183F85A4721380AD4E1300A44E5AA261
+18FF616E5C616E4A5B6E4A5B6F495B03E04990C7FC6FEB7FFE913AF9FE01FFF802F8B65A
+033F14C0030749C8FC030013E093CAFCB1B612F8A5414D7DB549>I<90393FF001FCB590
+380FFF804B13E0037F13F09238FE1FF89138F1F83F00019138F07FFC6CEBF3E015C0ECF7
+80A2ECFF00EE3FF84AEB1FF0EE0FE093C7FC5CA45CB3ABB612FEA52E367DB535>114
+D<903903FFC00E011FEBFC1E90B6127E000315FE3907FE003FD80FF0130F484813034848
+1301491300127F90C8127EA248153EA27FA27F01F091C7FC13FCEBFF806C13FEECFFF06C
+14FE6F7E6C15E06C816C15FC6C81C681133F010F15801301D9000F14C0EC003F030713E0
+150100F880167F6C153FA2161F7EA217C07E6D143F17807F6DEC7F0001F85C6DEB03FE90
+39FF801FFC486CB512F0D8F81F14C0D8F00791C7FC39E0007FF02B387CB634>I<147CA6
+14FCA41301A31303A21307A2130F131F133F137F13FF1203000F90B512FEB7FCA426007F
+FCC8FCB3A9EE0F80ABEE1F006D7EA2011F143E806D6D5A6DEBC1F86DEBFFF001005C023F
+1380DA03FEC7FC294D7ECB33>I<D93FF8913801FFC0B50207B5FCA50003ED001FC61607
+B3AE5FA35FA25F137F5F6D6C14F7DC01E713F06D6CD907C7EBFFC0903A0FFF801F876D90
+B51207010114FC6D6C13F0020701C091C7FC42377CB549>I E
+%EndDVIPSBitmapFont
+%DVIPSBitmapFont: Fj ecrm0900 9 5
+/Fj 5 109 df<123C127E12FFA4127E123C08087A8715>46 D<EB7F803803FFF0380F80
+FC381C003E003F133F6D6C7E6E7EA26E7EEA1F00C7FCA4EB01FF131FEBFF873803FC07EA
+0FF0EA1FC0EA3F80127F13004815C05AA3140FA26C131F6C133B3A3F8071F180391FC1E1
+FF2607FFC013003900FE003C22237DA126>97 D<EA03F012FFA312071203AEEC3F80ECFF
+E09038F3C0F89038F7007E01FE7F49EB1F8049EB0FC05BED07E016F0A2150316F8AA16F0
+150716E0A2ED0FC07F6DEB1F8001ECEB3F0001CF137C90388381F8903801FFE0C76CC7FC
+25357EB32B>I<EA03F012FFA312071203AEEC1FC0EC7FF09038F1E0FC9038F3807C9038
+F7007E13FE497FA25BA25BB3486CEB7F80B538C7FFFCA326347EB32B>104
+D<EA07E012FFA3120F1207B3B3A7EA0FF0B5FCA310347EB315>108
+D E
+%EndDVIPSBitmapFont
+%DVIPSBitmapFont: Fk ecbx0900 9 7
+/Fk 7 117 df<ED1F80A24B7EA24B7EA34B7EA24A7FA34A7FA24A7F15CFA2020F7F1587
+021F801503023F80EC3E01A2027E80EC7C0002FC804A137FA20101814A133F0103814A13
+1FA249B67EA24981A290271F8000077F91C77EA24982013E80017E82017C80A201FC8249
+157FB500F0013FB512F0A43C347DB343>65 D<EB7FFE0003B512E04814F8390FF00FFC39
+1FF803FF806E138016C0157F6C5A6C5AEA0180C8FCEC7FFF010FB5FC90B6FC0003EBF07F
+000F1300EA1FF8485A485A485A5BA315FF7F007F5B6D4813E03A3FF80FBFFF000FB5121F
+0003EBFC0F39007FE00728217EA02B>97 D<EA01FC12FFA4120F1207ADEC0FF8EC7FFF01
+FDB512C09039FFF01FF09138800FF84A6C7E496D7E496D7EA2178081A217C0A91780A25D
+1700A26D495A6D495A6E485A9039F7E03FF001E1B512C0D9C07F90C7FC9038801FF02A34
+7DB331>I<903807FF80013F13F090B512FC3903FE01FE4848487EEA0FF8EA1FF0EA3FE0
+A2007F6D5A496C5A153000FF91C7FCA9127F7FA2003FEC07807F6C6C130F000FEC1F00D8
+07FE133E3903FF80FCC6EBFFF8013F13E0010790C7FC21217DA027>I<3901F81F8000FF
+EB7FF0ECFFF89038F9E3FC9038FBC7FE380FFF876C1307A213FEEC03FCEC01F8EC006049
+1300B1B512F0A41F217EA024>114 D<9038FFE1C0000713FF5A383F803F387E000F1407
+5A14037EA26C6CC7FC13FCEBFFE06C13FC806CEBFF80000F14C06C14E0C6FC010F13F0EB
+007F140F00F0130714037EA26C14E06C13076CEB0FC09038C01F8090B5120000F913FC38
+E03FE01C217DA023>I<133CA5137CA313FCA21201A212031207001FB51280B6FCA3D807
+FCC7FCB0EC03C0A79038FE078012033901FF0F006C13FEEB3FFCEB0FF01A2F7EAE22>I
+E
+%EndDVIPSBitmapFont
+%DVIPSBitmapFont: Fl ecrm1200 12 25
+/Fl 25 122 df<121EEA7F8012FF13C0A213E0A3127FEA1E601200A413E013C0A3120113
+80120313005A1206120E5A5A5A12600B1D78891B>44 D<14FF010713E090381F81F89038
+3E007C01FC133F4848EB1F8049130F4848EB07C04848EB03E0A2000F15F0491301001F15
+F8A2003F15FCA390C8FC4815FEA54815FFB3A46C15FEA56D1301003F15FCA3001F15F8A2
+6C6CEB03F0A36C6CEB07E0000315C06D130F6C6CEB1F806C6CEB3F00013E137C90381F81
+F8903807FFE0010090C7FC28447CC131>48 D<EB03FE90381FFFC0017F13F03901F80FFC
+3903C001FE48486C7E000EC7EA7F8048EC3FC0ED1FE04815F00030140F007015F8006014
+07126CB415FC7F7F1503A46C4813076CC7FCC8FC16F8A2150F16F0151F16E0A2ED3FC0ED
+7F8016005D5D4A5A4A5A4A5A5D4A5A4A5A4AC7FC147C5C5C495A495A495A49C7120C131E
+5B013814185B5B485A4848143848C81230000E1570001FB612F0A25A5AB712E0A326427B
+C131>50 D<EC07FCEC3FFF91B512C0903903FC03E0903907E000F0D91FC0133849C71258
+017EEB01FC01FE1303491307485A485AA24848EB03F8000FEC01F092C7FC485AA3485AA3
+127FA29038007F80903801FFF090380780FC39FF0E003E49EB1F8049EB0FC049EB07E013
+6001E0EB03F04914F8150116FC5BED00FEA390C812FFA47EA57F123FA216FE121F15016D
+14FC120FED03F86C7EED07F06C6C14E06C6CEB0FC06C6CEB1F80017EEB3F0090383F80FE
+90380FFFF8010313E00100138028447CC131>54 D<16C04B7EA34B7EA34B7EA34B7EA3ED
+19FEA3ED30FFA203707FED607FA203E07FEDC03FA2020180ED801FA2DA03007F160FA202
+06801607A24A6D7EA34A6D7EA34A6D7EA20270810260147FA202E08191B7FCA249820280
+C7121FA249C87F170FA20106821707A2496F7EA3496F7EA3496F7EA201788313F8486C83
+D80FFF03037FB500E0027FEBFFC0A342477DC649>65 D<B8FC17E017FC00019039C00003
+FF6C6C4801007FEF3FC0717E717E717E84170384170184A760A21703601707604D5A4D5A
+EF7FC04DC7FCEE03FEEE3FF091B65A17FC0280C7B47EEF1FC0EF0FF0717E717E717E717E
+1980187F19C0A2183F19E0A8F07FC0A2198018FF4D1300A24D5AEF0FFC4D5AEF7FE04848
+6C903803FFC0B9C7FC17FC17C03B447CC345>I<B712FEEEFFE017F800019039C00007FE
+6C6C48903800FF80EF3FC0EF0FF0717E717EEF00FE8484F03F80F01FC0A2F00FE019F018
+0719F8A2180319FCA3F001FEA419FFAD19FEA3180319FCA319F8180719F0180F19E0A2F0
+1FC0F03F80A2F07F0018FE4D5A4D5AEF0FF0EF3FE0EFFF8048486C010790C7FCB812FC17
+E04CC8FC40447CC34A>68 D<B56C933807FFFC6E5EA20001F1FE0026006FE0EE1BF8A3D9
+67F01633A2D963F81663A3D961FC16C3A3D960FEED0183A2027FED0303A36E6C1406A36E
+6C140CA26E6C1418A36E6C1430A36E6C1460A26E6C14C0A36E6CEB0180A3037FEB0300A2
+92383F8006A36F6C5AA36F6C5AA26F6C5AA36F6C5AA36F6C5AA26FB45AA370C7FC13F0A2
+486C143ED80FFFEF0FFEB500F0011C0107B512FCA34E447BC359>77
+D<003FB912F8A3903BF0001FF8001F01806D481303003EC7150048187C0078183CA20070
+181CA30060180CA5481806A5C81600B3B3A54B7EED7FFE49B77EA33F447DC346>84
+D<B600C0010FB5FCA3000101E0C813F026007F80ED1F80F00F00A21806B3B3A7180E6D6C
+150CA2181C131F6E1518010F163818306D6C1570606D6C14016D6C5D6D6CEC0780027F4A
+C7FC6E6C131EDA1FE0137C913907FC03F00201B55A6E6C1380DB07FCC8FC40467CC349>
+I<EB07FC90383FFF809038F80FE03903C003F048C66C7E000E6D7ED80FC0137E486C137F
+6D6D7EA36F7EA26C5AEA0380C8FCA4EC0FFF49B5FC90380FFE1FEB3FC0EBFF00EA03FC48
+5A485A485A485A127F5B176048C7FCA3153FA36D137F007F14EF6D9038C7E0C0003F1301
+3A1FE00783F13B07F81E03FF802701FFFC0113003A001FE0007C2B2E7CAC31>97
+D<EC7F80903803FFF090380FC07C90383F000F01FCEB03804848EB01C00003140F4848EB
+1FE049133F120F485AA2485AED1FC0007FEC070092C7FCA290C9FC5AAB7E7FA2123F1630
+7F001F15706C6C146016E06C6C14C06C6C13010001EC03806C6CEB0700013F131E90381F
+C078903807FFF001001380242E7DAC2B>99 D<167FED3FFFA315018182B3EC7F80903803
+FFF090380FC07C90383F000E017E1307496D5AD803F87F48487F5B000F81485AA2485AA2
+127FA290C8FC5AAB7E7FA2123FA26C7EA2000F5D7F6C6C5B00035C6C6C9038077F806C6C
+010E13C0013F011C13FE90380FC0F8903803FFE09026007F0013002F467DC436>I<EB01
+FE903807FFC090381F03F090387E00FC49137E48487F485A4848EB1F80000F15C049130F
+121F484814E01507A2007F15F090C7FCA25AA390B6FCA290C9FCA67EA27FA2123F16306C
+7E1670000F15606D14E06C6C14C0000314016C6CEB03806C6CEB0700013E131E90381F80
+F8903803FFE0010090C7FC242E7DAC2B>I<EE0F80D901FCEB7FE0903A0FFF81F0F09039
+3F07E3819039FC01FF033A01F800FE014848017E13E00007027FC7FC497F000F8149131F
+001F81A9000F5D6D133F000792C7FC6D5B0003147E6C6C5B6D485A3903BF07E090380FFF
+80260701FCC8FC90CAFCA25AA37F6C7E7F90B512F86C14FF16E06C15F86C6C8048B67E3A
+07C0000FFF48481300003FC8EA3F80003E151F48ED0FC0A2481507A56C150F007C168000
+7E151F003E16006C153E6C6C5CD807E0495AD801F8EB07E0D8007FEB3F8090261FFFFEC7
+FC010113E02C427DAC31>103 D<EA01E0EA07F8A2487EA46C5AA2EA01E0C8FCADEA01FC
+12FFA3120712031201B3B0487EB512F8A315437DC21C>105 D<EA01FC12FFA312071203
+1201B3B3B3A5487EB512F8A315457DC41C>108 D<3901FC01FE00FF903807FFC091381E
+07F091383801F8000701707F0003EBE0002601FDC07F5C01FF147F91C7FCA25BA35BB3A8
+486CECFF80B5D8F83F13FEA32F2C7DAB36>110 D<EC7F80903803FFF090380FC0FC9038
+3E001F496D7E496D7E48486D7E48486D7E48486D7E000F81A24848147E003F157FA290C8
+7E481680A44816C0AA6C1680A26D147F003F1600A2001F157E6D14FE000F5D6D13010007
+5D6C6C495A6C6C495A6C6C495A013E49C7FC90381FC0FE903807FFF89038007F802A2E7D
+AC31>I<3903F803F000FFEB1FFCEC3C3EEC707F0007EBE0FF3803F9C000015B13FBEC00
+7E153C01FF13005BA45BB3A748B4FCB512FEA3202C7DAB26>114
+D<90383FE0183901FFFC383907E01F78390F0003F8001E1301481300007C1478127800F8
+1438A21518A27EA27E6C6C13006C7E13FC383FFFE06C13FC6C13FF6C14C06C14E0C614F0
+011F13F81300EC0FFC140300C0EB01FE1400157E7E153EA27EA36C143C6C147C15786C14
+F86CEB01F039F38003E039F1F00F8039E07FFE0038C00FF01F2E7DAC26>I<1306A5130E
+A4131EA3133E137EA213FE12011207001FB512F0B6FCA2C648C7FCB3A4150CAA017E131C
+017F1318A26D133890381F8030ECC070903807E0E0903801FFC09038007F001E3E7EBC26
+>I<D801FC147F00FFEC3FFFA300071401000380000181B3A85EA35DA212006D5B017E90
+38077F80017F010E13C06D011C13FE90380FC078903803FFF09026007F8013002F2D7DAB
+36>I<B539F001FFFCA3000790C7EA7FE06C48EC1F8000011600160E1200160C017F5CA2
+80013F5CA26E1370011F146080010F5CA2ECF00101075CA26D6C48C7FCA26E5A01011306
+A26D6C5AA214FF6E5AA215B8EC3FB015F06E5AA36E5AA26E5AA36EC8FC2E2C7EAA33>I<
+B539F001FFFCA3000790C7EA7FE06C48EC1F8000011600160E0000150C6D141C6D1418A2
+6E1338013F1430A26D6C5BA26E13E0010F5CA26D6C485AA2ECF803010391C7FCA2903801
+FC06A2ECFE0E0100130CA2EC7F18A215B8EC3FB0A2EC1FE0A36E5AA26E5AA36EC8FCA214
+06A35CA25CA2123C007E5BB4FC5CA25CEAFE01387C0380D87007C9FCEA3C1EEA0FFCEA03
+F02E3F7EAA33>121 D E
+%EndDVIPSBitmapFont
+%DVIPSBitmapFont: Fm ecbx1200 12 47
+/Fm 47 123 df<0118140C017C143E01FC147E48485C4848495A495C4848495A4848495A
+001F140F90C75B003E4AC7FCA2003C141E007C143E0078143CA200F8147CA2481478D8F1
+F014F8D8F7FCEB7BFEB46CEB7FFF6D1580028014C0A36C80A36C806C496C13806C486D13
+006C486D5AD801F0EB00F82A2283C427>16 D<D807C0EB03E0D81FF0EB0FF8486C497E48
+6C497E486C497E6D1580A3028014C0A36C806C80D81FF7EB0FFBD807C7EB03E3D80007EB
+0003010F1407A291C71380A249140F011E1500013E5CA249143E01FC147E49147C48485C
+4848495A000714034848495A4848495A90C75B000C0206C7FC2A2281C427>I<ED0FFF4A
+B512C0020F14F0027F80903A01FFF803FC499038C000FE010FEB00034948497E49485B5C
+495A4C138001FF6E13005CA3705AEE01F893C8FCA74BB51280B9FCA5C69038E00003B3B0
+007FD9FFC1B6FCA538467EC53E>28 D<EA07C0EA1FF0EA3FF8EA7FFCEAFFFEA7EA7FFCEA
+3FF8EA1FF0EA07C00F0F788E1F>46 D<EC3FF849B5FC010F14E0013F14F890397FF01FFC
+9039FFC007FE4890380001FF48486D1380000716C049147F000F16E049143F001F16F0A2
+003F16F8A249141F007F16FCA600FF16FEB3A3007F16FCA56C6CEC3FF8A3001F16F0A200
+0F16E06D147F000716C06D14FF6C6C4913806C6D4813006C6D485A90397FF01FFC6DB55A
+010F14E0010314809026003FF8C7FC2F427CC038>48 D<EC03C01407141F147FEB03FF13
+3FB6FCA413C3EA0003B3B3ADB712FCA5264177C038>I<ECFFE0010F13FE013F6D7E90B6
+12E0000315F82607FC0313FE3A0FE0007FFFD81F806D138048C7000F13C0488001C015E0
+01F07F00FF6E13F07F17F881A46C5A6C5A6C5AC9FC17F05DA217E05D17C04B13804B1300
+A2ED1FFC4B5A5E4B5A4B5A4A90C7FC4A5A4A5AEC0FF04A5AEC3F804AC7127814FE495A49
+4814F8D907E014F0495A495A49C8FC017C140149140348B7FC4816E05A5A5A5A5AB8FC17
+C0A42D417BC038>I<ECFFF0010713FF011F14C0017F14F049C66C7ED803F8EB3FFED807
+E06D7E81D80FF86D138013FE001F16C07FA66C5A6C4815806C485BC814005D5E4B5A4B5A
+4B5A4A5B020F1380902607FFFEC7FC15F815FF16C090C713F0ED3FFCED0FFEEEFF80816F
+13C017E0A26F13F0A217F8A3EA0FC0EA3FF0487EA2487EA217F0A25D17E06C5A494913C0
+5BD83F80491380D81FF0491300D80FFEEBFFFE6CB612F800015D6C6C14C0011F49C7FC01
+0113E02D427BC038>I<163FA25E5E5D5DA25D5D5D5DA25D92B5FCEC01F7EC03E7140715
+C7EC0F87EC1F07143E147E147C14F8EB01F0EB03E0130714C0EB0F80EB1F00133E5BA25B
+485A485A485A120F5B48C7FC123E5A12FCB91280A5C8000F90C7FCAC027FB61280A53141
+7DC038>I<0007150301E0143F01FFEB07FF91B6FC5E5E5E5E5E16804BC7FC5D15E092C8
+FC01C0C9FCAAEC3FF001C1B5FC01C714C001DF14F09039FFE03FFC9138000FFE01FC6D7E
+01F06D13804915C0497F6C4815E0C8FC6F13F0A317F8A4EA0F80EA3FE0487E12FF7FA317
+F05B5D6C4815E05B007EC74813C0123E003F4A1380D81FC0491300D80FF0495AD807FEEB
+FFFC6CB612F0C65D013F1480010F01FCC7FC010113C02D427BC038>I<4AB47E021F13F0
+027F13FC49B6FC01079038807F8090390FFC001FD93FF014C04948137F4948EBFFE04849
+5A5A1400485A120FA248486D13C0EE7F80EE1E00003F92C7FCA25B127FA2EC07FC91381F
+FF8000FF017F13E091B512F89039F9F01FFC9039FBC007FE9039FF8003FF17804A6C13C0
+5B6F13E0A24915F0A317F85BA4127FA5123FA217F07F121FA2000F4A13E0A26C6C15C06D
+4913806C018014006C6D485A6C9038E01FFC6DB55A011F5C010714C0010191C7FC903800
+3FF02D427BC038>I<121E121F13FC90B712FEA45A17FC17F817F017E017C0A248168000
+7EC8EA3F00007C157E5E00785D15014B5A00F84A5A484A5A5E151FC848C7FC157E5DA24A
+5A14035D14074A5AA2141F5D143FA2147F5D14FFA25BA35B92C8FCA35BA55BAA6D5A6D5A
+6D5A2F447AC238>I<EA07C0EA1FF0EA3FF8EA7FFCEAFFFEA7EA7FFCEA3FF8EA1FF0EA07
+C0C7FCAEEA07C0EA1FF0EA3FF8EA7FFCEAFFFEA7EA7FFCEA3FF8EA1FF0EA07C00F2C78AB
+1F>58 D<1A60F101F01907191FF17FC0953801FF00F007FCF01FF0F07FC04D48C7FCEF07
+FCEF3FF0EFFFC0040390C8FCEE0FFCEE3FE0EEFF80DB03FEC9FCED0FF8ED3FE0EDFF80DA
+07FECAFCEC1FF8EC7FE0903801FF80D907FCCBFCEB1FF0EB7FC04848CCFCEA07FCEA1FF0
+EA7FC048CDFCA2EA7FC0EA1FF0EA07FCEA01FF38007FC0EB1FF0EB07FC903801FF809038
+007FE0EC1FF8EC07FE913800FF80ED3FE0ED0FF8ED03FE923800FF80EE3FE0EE0FFCEE03
+FF040013C0EF3FF0EF07FCEF01FF9438007FC0F01FF0F007FCF001FF9538007FC0F11FF0
+19071901F10060444277B957>60 D<126012F812FE6C7EEA3FE0EA0FF8EA03FEC66C7EEB
+3FE0EB0FF8EB03FE903800FFC0EC3FF0EC0FFCEC03FF9138007FC0ED1FF0ED07FCED01FF
+9238007FC0EE1FF0EE07FE933801FF809338007FE0EF1FF8EF03FE943800FF80F03FE0F0
+0FF8F003FE953800FF80F13FE0F10FF0A2F13FE0F1FF80953803FE00F00FF8F03FE0F0FF
+80DD03FEC7FCEF1FF8EF7FE0933801FF80DC07FEC8FCEE1FF0EE7FC04B48C9FCED07FCED
+1FF0ED7FC0DA03FFCAFCEC0FFCEC3FF0ECFFC0D903FECBFCEB0FF8EB3FE0EBFF80D803FE
+CCFCEA0FF8EA3FE0EAFF8048CDFC12F81260444277B957>62 D<923803FFF0037FEBFF80
+0203B612F0020F15FC913A3FFC000FFFDAFFC0010013C0D903FEC8EA1FF0D907F0ED03F8
+D91FC0ED00FE4948167F017ECAEA1F8049717E4848717E49DAFF8013034848010F01F06D
+7E4848013F01FC6D7E92B6FC4848489026C07F80137C49489026001FC0133C484948D907
+E0133E001E49486D6C131E003E49480101141F023F913800FFE0003C4A82007C017F1880
+007819074A5AA300F81AC04848491603AB6C6C7F12781B801A076E7E127C003C133F003E
+6E1700021F4A5C001E6D6C5B001F6D6C49EBF01E6C6D6C011F143E6D6CD9C07F6D5A6C6C
+6C90B5383FFFF8033FD9FC0F5B6C6C010FD9F0035B6C6C0100903980007F806D91CBFC6C
+7E137E6D7E6D6CEF7FC0D907F0EE03FFD903FE043F1300902600FFC0913803FFF8DA3FFC
+49B512C0020FB748C7FC020316E0DA007F02FCC8FC030349C9FC4A477AC557>64
+D<EE1F80A24C7EA24C7EA34C7EA24B7FA34B7FA24B7FA34B7F169F031F80161F82033F80
+ED3E07037E80157C8203FC804B7E02018115F0820203814B137F0207815D173F020F814B
+7F021F8292C77EA24A82023E80027E82027FB7FCA291B87EA2498302F0C8FCA20103834A
+157F0107834A153FA249488284011F8491C97E4984133E017E82B6020FB612F0A54C457C
+C455>I<B9FC18F018FE727E19E026003FFCC700077F05017F716C7E727E727EA2721380
+A37213C0A74E1380A24E1300A24E5A4E5A4E5A4D5B05075B94B5128091B700FCC7FC18F0
+18FF19E002FCC7000113F8716C7EF01FFE727E7213801AC07213E0A27213F0A31AF8A71A
+F0A2601AE0604E13C0604E138095B5120005075BBA12F86119C04EC7FC18E045447CC350
+>I<DCFFF01470031F01FF14F04AB6EAE0010207EDF803023FEDFE0791B539E001FF0F49
+49C7EA3F9F010701F0EC0FFF4901C0804990C87E4948814948814948167F4849163F4849
+161F5A4A160F485B19074890CAFC19035A5BA2007F1801A34994C7FC12FFAE127F7F1AF0
+A2123FA27F6C18011AE06C7F19036C6D17C06E16077E6C6DEE0F806C6DEE1F006D6C5E6D
+6C167E6D6C6C5D6D6D4A5A6D01F0EC07F0010101FEEC1FE06D903AFFF001FF80023F90B6
+C7FC020715FC020115F0DA001F1480030001F8C8FC44467AC451>I<B9FC18F018FE727E
+19E026003FFEC7001F13F805017F9438003FFF060F7F727F727F727F84737E737EA2737E
+A2737EA21B80A2851BC0A51BE0AD1BC0A51B8061A21B006162193F624F5A19FF624E5B06
+075B4E5B063F90C7FC4DB45A050F13F8BA5A19C04EC8FC18F095C9FC4B447CC356>I<B7
+12E0A5D8001F90C7FCB3B3B3A4B712E0A523447DC32A>73 D<B500FE067FB512806E95B6
+FCA26F5EA2D8003F50C7FC013D6DEE03DFA2013C6DEE079FA26E6CEE0F1FA26E6C161EA2
+6E6C163CA36E6C1678A26E6C16F0A26E6DEC01E0A26E6DEC03C0A36E6DEC0780A26F6CEC
+0F00A26F6C141EA26F6C5CA36F6C5CA26F6C5CA26F6D485AA26F6D485AA26F6D485AA370
+6C48C7FCA293383FF81EA2706C5AA2706C5AA3706C5AA2705BA2705BA2705BA2B6057FB6
+128071C7FCA2173E171C61447CC36A>77 D<923807FFC092B512FE0207ECFFC0021F15F0
+91267FFE0013FC902601FFF0EB1FFF01070180010313C04990C76C7FD91FFC6E6C7E4948
+6F7E49486F7E01FF8348496F7E48496F1380A248496F13C0A24890C96C13E0A24819F049
+82003F19F8A3007F19FC49177FA400FF19FEAD007F19FC6D17FFA3003F19F8A26D5E6C19
+F0A26E5D6C19E0A26C6D4B13C06C19806E5D6C6D4B13006C6D4B5A6D6C4B5A6D6C4B5A6D
+6C4A5B6D01C001075B6D01F0011F5B010101FE90B5C7FC6D90B65A023F15F8020715C002
+004AC8FC030713C047467AC454>79 D<DAFFE0131C010701FE133C013F9038FF807C90B6
+EAE0FC4815F9489038801FFF3907FC00014848EB007F4848143F4848140F491407007F15
+035B1601160012FF177CA27FA26D153C7F7F6D92C7FC6C7EEBFFE014FE6CEBFFF015FF6C
+15E016FC6C816C6F7E6C826C826C6C81011F810107811300020F80140003077FED007F82
+040F1380828212F082A282A27EA218007EA26C5D6C5E6D14036D5D6D140701F84A5A01FF
+EC3FF002F8EBFFE0486CB65AD8FC1F92C7FCD8F80714FC48C614F0480107138031467AC4
+3E>83 D<007FBA12E0BB12F0A46C19E04406776757>95 D<903801FFE0011F13FE017F6D
+7E48B612E03A03FE007FF84848EB1FFC6D6D7E486C6D7EA26F7FA36F7F6C5A6C5AEA00F0
+90C7FCA40203B5FC91B6FC1307013F13F19038FFFC01000313E0481380381FFE00485A5B
+127F5B12FF5BA35DA26D5B6C6C5B4B13F0D83FFE013EEBFFC03A1FFF80FC7F0007EBFFF8
+6CECE01FC66CEB8007D90FFCC9FC322F7DAD36>97 D<EB7FC0B5FCA512037EB1ED0FF892
+B57E02C314E002CF14F89139DFC03FFC9139FF000FFE02FCEB03FF4A6D13804A15C04A6D
+13E05CEF7FF0A218F8173FA318FCAC18F8A2177F18F0A3EFFFE06E15C06E5B6E49138002
+7C491300496C495A903AFC1FC07FFC496CB512F0D9F00314C049C691C7FCC8EA1FF03646
+7DC43E>I<EC3FFC49B512C0010F14F0013F14FC90397FF003FE9039FFC001FF0003495A
+48494813805B120F485AA2485A6F1300007F6E5AED00784991C7FCA212FFAC6C7EA3123F
+6DEC03C0A26C6C1407000F16806D140F6C6DEB1F006C6D133E6C01F05B3A007FFC03F86D
+B55A010F14C0010391C7FC9038003FF82A2F7CAD32>I<EE03FEED07FFA5ED001F160FB1
+EC3FE0903803FFFC010FEBFF8F013F14CF9039FFF807FF48EBC00148903880007F4890C7
+123F4848141F49140F121F485AA3127F5BA212FFAC127FA37F123FA26C6C141FA26C6C14
+3F0007157F6C6C91B5FC6CD9C00314FC6C9038F01FEF6DB5128F011FEBFE0F010713F890
+26007FC0EBF80036467CC43E>I<EC3FF80103B57E010F14E0013F8090397FF83FF89039
+FFC007FC48496C7E48496C7E48486D1380485A001FED7FC05B003FED3FE0A2127F5B17F0
+161F12FFA290B7FCA401F0C9FCA5127FA27FA2123F17F06C7E16016C6C15E06C6C14036C
+6DEB07C06C6DEB0F806C01F0EB3F0090397FFE01FE011FB55A010714F0010114C0902600
+1FFEC7FC2C2F7DAD33>I<EDFF80020F13E0027F13F049B512F849EB8FFC90390FFE0FFE
+90381FFC1F14F8133FEB7FF0A2ED0FFCEBFFE0ED03F0ED00C01600ABB612F8A5C601E0C7
+FCB3B0007FEBFFE0A527467DC522>I<DAFFE0137E010F9039FE03FF80013FEBFF8F90B8
+12C048D9C07F133F489038001FF84848EB0FFC4848903907FE1F80001F9238FF0F00496D
+90C7FCA2003F82A8001F93C7FCA26D5B000F5D6C6C495A6C6C495A6C9038C07FF04890B5
+5A1680D8078F49C8FC018013E0000F90CAFCA47F7F7F90B612C016FC6CEDFF8017E06C82
+6C16FC7E000382000F82D81FF0C77ED83FC014074848020113808248C9FC177FA46D15FF
+007F17006D5C6C6C4A5A6C6C4A5AD80FFEEC3FF83B07FFC001FFF0000190B612C06C6C92
+C7FC010F14F8D9007F90C8FC32427DAC38>I<EB7FC0B5FCA512037EB1ED07FE92383FFF
+8092B512E002C114F89139C7F03FFC9138CF801F9139DF000FFE14DE14FC4A6D7E5CA25C
+A35CB3A7B60083B512FEA537457CC43E>I<137C48B4FC4813804813C0A24813E0A56C13
+C0A26C13806C1300EA007C90C7FCAAEB7FC0EA7FFFA512037EB3AFB6FCA518467CC520>
+I<EB7FC0B5FCA512037EB3B3B3A3B61280A519457CC420>108 D<90277F8007FEEC0FFC
+B590263FFFC090387FFF8092B5D8F001B512E002816E4880913D87F01FFC0FE03FF8913D
+8FC00FFE1F801FFC0003D99F009026FF3E007F6C019E6D013C130F02BC5D02F86D496D7E
+A24A5D4A5DA34A5DB3A7B60081B60003B512FEA5572D7CAC5E>I<90397F8007FEB59038
+3FFF8092B512E0028114F8913987F03FFC91388F801F000390399F000FFE6C139E14BC02
+F86D7E5CA25CA35CB3A7B60083B512FEA5372D7CAC3E>I<EC1FFC49B512C0010714F001
+1F14FC90397FF80FFF9026FFC0017F48496C7F4848C7EA3FE000078248486E7E49140F00
+1F82A2003F82491407007F82A400FF1780AA007F1700A46C6C4A5AA2001F5E6D141F000F
+5E6C6C4A5AA26C6C6CEBFFE06C6D485B27007FF80F90C7FC6DB55A010F14F8010114C090
+26001FFCC8FC312F7DAD38>I<90397FC00FF8B590B57E02C314E002CF14F89139DFC03F
+FC9139FF001FFE000301FCEB07FF6C496D13804A15C04A6D13E05C7013F0A2EF7FF8A4EF
+3FFCACEF7FF8A318F017FFA24C13E06E15C06E5B6E4913806E4913006E495A9139DFC07F
+FC02CFB512F002C314C002C091C7FCED1FF092C9FCADB67EA536407DAC3E>I<DA3FE013
+1E902603FFFC133E010F01FF137E013F1480903AFFF80FE0FE489038E003F148EBC00148
+90388000FB4890C7127F49143F001F151F485A160F5B127FA3485AAC6C7EA46C7EA26C6C
+141F163F6C6C147F6C15FF6C6D5A6C9038E003EF6C9038F01FCF6DB5128F011FEBFE0F01
+0313F89038007FC091C7FCAD0307B512FCA536407CAC3B>I<90387F807FB53881FFE002
+8313F0028F13F8ED8FFC91389F1FFE000313BE6C13BC14F8A214F0ED0FFC9138E007F8ED
+01E092C7FCA35CB3A5B612E0A5272D7DAC2E>I<90391FFC038090B51287000314FF120F
+381FF003383FC00049133F48C7121F127E00FE140FA215077EA27F01E090C7FC13FE387F
+FFF014FF6C14C015F06C14FC6C800003806C15806C7E010F14C0EB003F020313E0140000
+F0143FA26C141F150FA27EA26C15C06C141FA26DEB3F8001E0EB7F009038F803FE90B55A
+00FC5CD8F03F13E026E007FEC7FC232F7CAD2C>I<EB01E0A51303A41307A2130FA2131F
+A2133F137F13FF1203000F90B51280B7FCA4C601E0C7FCB3A3ED01E0A9150302F013C013
+7F150790393FF80F8090391FFC1F006DB5FC6D13FC01015B9038003FE023407EBE2C>I<
+D97FC049B4FCB50103B5FCA50003EC000F6C81B3A85EA25EA25E7E6E491380017FD901F7
+13FE9138F807E76DB512C7010F1407010313FE9026007FF0EBFC00372E7CAC3E>I<B690
+3803FFFCA5000101E09038003E006C163C80017F5D8017F8013F5D6E1301011F5D6E1303
+010F5D6E13076D5DED800F6D92C7FC15C05E6DEBE01E163E6D143CEDF07C027F1378EDF8
+F8023F5B15FD021F5B15FF6E5BA36E5BA26E90C8FCA26E5AA26E5AA21578362C7EAB3B>
+I<B500FE90383FFFF0A5C601F0903803E0006D6C495A6D6C495A011F4AC7FC6E5B6D6C13
+7E6DEB807C6D6D5A6DEBC1F0EDE3E06DEBF7C06EB45A806E90C8FC5D6E7E6E7F6E7FA24A
+7F4A7F8291381F3FFCEC3E1F027C7F4A6C7E49486C7F01036D7F49487E02C08049486C7F
+49C76C7E013E6E7E017E141FB500E090B512FCA5362C7EAB3B>120
+D<001FB71280A49026FC001F130001E0495A5B49495A90C7485A48495B123E4A5B4A5B00
+3C495BA24A90C7FC4A5A4A5AC7FC4A5A495B495BA2495B499038800780491300A2495A49
+48130F49481400A2485B48495B485BA248495B4890C75A48485C15034848EB1FFEB7FCA4
+292C7DAB32>122 D E
+%EndDVIPSBitmapFont
+%DVIPSBitmapFont: Fn ecrm1728 17.28 8
+/Fn 8 117 df<B912F018FF19E019F8C601FCC8EA7FFED93FF892380FFF80011F04017F
+9538007FF0F11FF8737EF103FE737E737F747E747E747E1A0F87747E1A0387747EA27413
+80A2F37FC0A21CE01B3FA21CF0A21B1F1CF8A31CFCA21B0FA41CFEAF1CFCA51B1F1CF8A4
+F33FF0A21CE0A21B7F1CC01BFF1C80A2501300A2505A505AA2505A505A505A505A1AFF4F
+5B4F90C7FCF107FCF11FF8F17FF0953801FFC0013F04075BD9FFFCDB7FFEC8FCBA12F819
+E096C9FC18F0576278E167>68 D<BB12FCA4C601FCC8120FD93FF89238007FFE011F171F
+190719031900A21A7E1A3EA21A1EA21A1F86A486A6F20380A318E0A297C7FCA61701A417
+031707170F171F17FF91B7FCA402F8C7FC171F170F170717031701A41700A895C9FCB3A5
+80133F90B57EB712E0A4496278E158>70 D<EC3FE0903803FFFE010F6D7E90393FC03FE0
+90397C000FF801F0EB03FC48486D7E48486D7E48486E7E48C86C7E7F01F06E7E487E6D6E
+7EA3707EA36C5AEA03E0C9FCA6167FED7FFF020FB5FC91387FF807903801FF80903807FC
+00EB1FF0EB7FC0495AD803FEC7FC485A120F5B485A485AA24848EE01C0A312FF5BA2160F
+A3161F6D141B007F153B16736D17806C6C9138E1FC03001FEC03C16C6C903A0780FE0700
+D807FE49486C5A2701FF807CEB7FFE6C6CB4486D5A011F01E06D5A010390C7EA07E03A41
+79BF43>97 D<ED1FE0EDFFF8020313FE91380FF03F91391FC01F8091383F807F91397F00
+FFC014FE1301495A5C0107EC7F80A24948EB1E0093C7FCA2495AB3A5B712E0A426001FE0
+C8FCB3B3B0497EEB7FFC003FB512FEA42A657DE429>102 D<1378EA01FE487E487FA66C
+90C7FC6C5AEA007890C8FCB3A2EB0780EA0FFFB5FCA41203C6FCA2137FB3B3AC497E487F
+B61280A4195F7BDE25>105 D<010FEB07F8D80FFFEB1FFEB590387FFF809238F81FC091
+3801E03F913903C07FE00003EB0780C6EB0F00140E6D5A0218EB3FC00238EB1F800230EB
+0600027090C7FCA2146014E0A25CA55CB3B0497E4813F0B612F8A42B3F7BBE34>114
+D<9138FFC003010FEBF807017FEBFE0F3A01FF003F9FD803F0EB07DF48486DB4FCD80F80
+1300001F8148C8FC003E81007E81127C00FC81A4827EA27E7F6C7E6D91C7FC13F8EA3FFE
+381FFFE06C13FF15F0000314FE6C6E7E6C6C14E0011F14F801078001008002077FDA003F
+13801507030113C0ED007F00E0ED3FE0161F17F06C150F1607A36C1503A37EA26C16E016
+077E17C06D140F6D15806D141FD8FDF0EC3F00D8F8F8147E017C495A3AF01F801FF06DB5
+12C0D8E00391C7FC39C0007FF02C417CBF35>I<1470A714F0A51301A31303A21307A213
+0FA2131F133F137F13FF1203000F90B6FCB8FCA326000FF0C8FCB3AEEE01C0AE6D6CEB03
+80A316076D6C14005E6D6C130E6D6C131E6E6C5A91383FE0F86EB45A020713C0020090C7
+FC2A597ED734>I E
+%EndDVIPSBitmapFont
+%DVIPSBitmapFont: Fo ecbx1728 17.28 18
+/Fo 18 117 df<BB7E1AFCF2FFC01BF01BFED8000191C8001F6D7E070014E0081F7F0807
+13FC08017F747F093F7F757F757F757F757F757F757FA2767E1E80881EC0881EE0881EF0
+A2881EF8A31EFC88A31EFEA61EFFB01EFEA61EFCA2641EF8A31EF064A21EE0641EC0641E
+80521300A2525A515B515BA2515B515B093F5B515B504848C7FC08075B081F5B97B512E0
+070F1480BC48C8FC1BF81BC008FCC9FC1A8068627BE177>68 D<942603FFF8151C94B66C
+143C040F03F0147C047F03FC14FC0303B81301030FDAC00113C0033F01F8C7381FF00392
+B500C0913807F807020349C83801FE0F020F01F89238007F1F4A01E0EE3FBF4A49EE0FFF
+91B5CA7E494983494983494983495B4949187F4B183F491A1F495B90B5CC120FA2484919
+075A4A19035A4A19015AA24A19005AA348491A7CA35A9AC8FCA35CA2B5FCB07EA26E043F
+B81280A47E96C7000701FCC7FCA26C7FA37E80A27E807E807E6C7FA26D7F6D7F7F816D7F
+6D6D5F6D7F6D6D5F6D6D7E023F6D5E6E01F05E6E6DEEFE7F020301FF923801FC3F020002
+C0913807F80F033F01FC91381FF007030F903BFFE001FFC001030391B6EA8000DB007F4B
+C7123C040F03F8140C040003C091C8FC050301F8CBFC696677E37A>71
+D<BA12E0F1FF801AF81AFF1BC0D8000191C7000114F0DE000F13FC070313FF070080083F
+7F747F747F747F747FA2747F88A28986A389A865A35091C8FCA26462646462505B505B50
+138097B5C9FC070313FC070F5B4EB512C093B8CAFC1AF81AC01AF893C7000713FE06006D
+7E073F7F7313F007077F737F87737F85888688A2747FAA88A91F707614F8A286A2746D13
+011FF086746D13037614E0B800FE6EED07C0746CEBC00F759038F07F80090F90B5120009
+035CCF6C13F80A0313E06D647BE173>82 D<001FBD12F0A59126F8000191C7123F4801C0
+060713F849C71700491A7F01F01A1F491A0F491A07A2491A03A290C81801A2007EF300FC
+A4007C1C7CA7481C3EA5C91900B3B3B3A5023FB912F8A55F617AE06C>84
+D<913803FFF0027F13FF0103B612E0010F15F890263FFC0013FED97FC090381FFF8049C7
+6C7F4801C06D7F486D6D7F6E6D7F48836E7F84177F84A36C496E7FA26C5B6C5B013FC8FC
+90C9FCA75F0307B6FC4AB7FC141F91B5EAF03F0103EBFE00010F13F0013F1380D9FFFEC7
+FC485B485B485B485B485B485BA24890C8FC1A7CA2485AA35FA394B5FC7F6C5D6EEB03DF
+6CDB07CFEBC0F86C6DEB0F8F6C6DD91F07EBF3F06C01F8017E14FF6C9027FE01FC0314E0
+C690B5D8F00114C0013F9126C0007F1380010791C7383FFE009026003FF8EC07F846437B
+C14D>97 D<903807FF80B6FCA5C6FC7F7FB3A9933801FFE0041F13FE047FEBFFC00381B6
+12F0922687FC0113FC923A9FE0003FFEDBBF8090380FFF8003FEC76C7F4B6E7F4B6E7F4B
+6E7F4B824B157F4B82737EA21B80851BC0A31BE085A41BF0AE1BE0A44F13C0A31B80A24F
+1300A262197F6F5E6F4B5A4E5B6F4A5BDAFCF84A5BDAF87E4A5B4A6C4A90C7FC9126E01F
+C0EB7FFC913BC00FF803FFF8DA8003B612E091C71580013E023F01FCC8FC90C800031380
+4C657CE356>I<ED1FFF4AB512F8020F14FF027F15C0902701FFF80013F04901E0EB0FF8
+010F0180EB03FC4990C7EA0FFE49484A7E49485C4948168048495C5A5C5A485BA2487013
+005C48705A715AEF03F04893C8FC91CBFCA4B5FCAE7EA280A27EA36C7FF003E07E6E1507
+6C18C06E150F6C18806C6D151F6C6DED3F006D6C157E6D6C15FE6D6D495A6D6D495A6D01
+F0EB0FE0010101FEEB7FC06D6CB6C7FC021F14FC020314E09126001FFEC8FC3B437BC145
+>I<ED3FFE0203B512E0021F14FC027F14FF902701FFF80F13C00107D9C0037F4990C77F
+49486E7E49486E7E49486E7E49486E7E5A48496E13805A4A16C0488219E0485B834818F0
+A34890C8FCA27113F8A3B5FCA391B8FCA491CBFCA67EA4807EA27E19F8806C17016C18F0
+806C17036C6DED07E06E16C06C170F6D6CED1F806D6CED3F006D6C6C14FE01076DEB03FC
+6D01F8EB0FF8010001FFEB7FE0023F90B51280020F4AC7FC020114F8DA000F13803D437C
+C146>101 D<EEFFE0031F13FC92B6FC02031580020F9038E03FC04A903800FFE091267F
+FE0113F04A485A49494813F84913F04913E0A25B15C05B7013F04913807013E09338007F
+80EF1E0094C7FCB1B8FCA5D8003F0180C8FCB3B3B2B712F8A535657CE42F>I<F13F8091
+2601FFF0903801FFE0021F01FF010713F091B6D8E01F13F801039238F87FCF010F903BC0
+7FFEFC0FFC903B1FFE000FFFF0D97FFC6DEBE01F49486D140F48496D13F01AF848496DEB
+F807489438FC01E096C7FC48496E7EA44883A96C5FA46C6D4A5AA26C5F6C6D495BA26C6D
+495B6D6C495B6D6C4990C8FC903A7FFFC07FFE017B90B512F801F015E0021F91C9FC0001
+010113F049CCFC1203A27FA37FA27F7F6D7E91B612FCEFFFE06C17FCF0FF806C18E0856D
+17FC6D836D837F017F188048BA12C000070180C712074848C96C13E04848160F48481603
+4982007F19F084485A197FA56D17FF007F19E0A26D5E6C6C4C13C0001F19806D5E6C6C4C
+1300000301C0ED3FFC6C01F0EDFFF826007FFC020313E090261FFFE0017F1380010790B6
+48C7FC010116F8D9001F1580DA007F01E0C8FC46607CC14D>I<EB0FE0EB3FF8497E497E
+487FA24880A76C91C7FCA26C5B6D5A6D5AEB0FE090C9FCB1903807FF80007FB5FCA5C6FC
+7F7FB3B3B0B712C0A522657CE42A>105 D<903807FF80B6FCA5C6FC7F7FB3B3B3B3AFB7
+12E0A523647CE32A>108 D<D90FFFEC7FF8B60103B5FC040F14E0043F80DC7F0113FC92
+2601F8007FC6DA03E06D7E6D49487F6D49488193C77E031E825D153803788003708215F0
+5DA35DA35DB3B3A2B7D8E03FB612F8A54D417BC056>110 D<92381FFF804AB512F8020F
+14FF023F15C09126FFFC0313F001039039E0007FFC490180EB1FFED91FFEC73807FF8049
+486E7F49486E7F49486E7F48496F7EA248496F7E4884A248496F7EA2481980A24819C091
+C97EA24819E0A5B518F0AD6C19E0A46C6D4B13C0A36C1980A26C6D4B1300A26C606E157F
+6C606C6D4B5A6C606D6C4A5B6D6C4A5B6D6C4A5B6D6C6C011F90C7FC010301E0EB7FFC6D
+9039FC03FFF86D6CB612E0020F92C8FC020114F8DA001F138044437CC14D>I<903B07FF
+8001FFE0B6011F13FE047FEBFFC00381B612F0922687FC0313FC923A9FE0007FFEC6DABF
+806D6C7E6D01FEC7000F7F6D496E7F4B824B6E7F4B6E7F4B804B82737EA21B80851BC0A2
+851BE0A4851BF0AE4F13E0A41BC061A21B80A24F1300A24F5AA26F4A5B6F4A5B626F4A5B
+6F4A5B03FE4A5B03BF027F90C7FCDB9FC0EBFFFC92268FF8075B0383B612E00380158004
+3F01FCC8FC0403138093CBFCB3A4B712E0A54C5D7CC056>I<D90FFFEB07F8B6EB3FFF4C
+13804BB512E0923903F83FF0923907E07FF8C691380F80FF6D020113FC6D131E153E153C
+1578A21570DBF00013F8EF7FF04BEB3FE0EF0F8094C7FC5DA65DB3B1B712F8A536417DC0
+3E>114 D<DA7FFC131C0107B5EAC03C011FECF0FC90B612FD489038C003FFD807FEC712
+7FD80FF8143F49140F4848140748481403A248481401A2160012FFA26D157CA27F7F7F6D
+92C7FCEBFF806C13F0ECFFC015FE6CECFFC016F86C15FE6C6F7E6C826C826C826C82013F
+81010F81010181EB003F02011580EC000F1500041F13C000F88182826C8182A26C167FA3
+7E18807F17FF6D16007F6D4A5A7F6D4A5A6DEC0FF86D6C495A3BFE1FF001FFE0486CB612
+80D8F8034AC7FC48C614F048010F90C8FC32437BC13D>I<EC07C0A6140FA5141FA3143F
+A2147FA214FF5BA25B5B5B5B137F48B5FC000F91B512F8B8FCA4D8001F01C0C7FCB3B017
+1FAD6D153E81A26D157C816D15F86D7F6D9038FC01F091397FFF07E06EEBFFC0020F1480
+0203EBFE009138003FF8305C7DDA3C>I E
+%EndDVIPSBitmapFont
+end
+%%EndProlog
+%%BeginSetup
+%%Feature: *Resolution 600dpi
+TeXDict begin
+%%PaperSize: A4
+
+%%EndSetup
+%%Page: 1 1
+1 0 bop 290 639 a Fo(Genealogical)56 b(Represen)l(tation)e(of)f(T)-13
+b(rees)52 b(in)g(Databases)1686 822 y Fn(First)46 b(Draft)1247
+1063 y Fm(Miguel)36 b(Sofer)i(<mig@utdt.edu>)1359 1179
+y Fl(Univ)m(ersidad)33 b(T)-8 b(orcuato)33 b(Di)f(T)-8
+b(ella)1728 1295 y(Buenos)33 b(Aires)1797 1411 y(Argen)m(tina)1746
+1606 y(Ma)m(y)h(6,)e(2000)1839 1905 y Fk(Abstract)441
+2035 y Fj(blah)25 b(blah)h(.)13 b(.)g(.)118 2310 y Fi(1)131
+b(In)l(tro)t(duction)118 2491 y Fh(T)-7 b(rees)28 b(are)h(a)g(v)n(ery)f
+(frequen)n(t)h(data)f(structure.)41 b(They)30 b(are)e(the)h(natural)g
+(represen)n(tation)e(for)i(instance)g(for)f(organiza-)118
+2591 y(tional)f(c)n(harts,)g(threaded)g(discussion)g(groups,)f(some)h
+(bills)g(of)h(materials,)e(.)14 b(.)g(.)243 2691 y(A)n(t)28
+b(least)f(t)n(w)n(o)f(alternativ)n(e)h(represen)n(tations)e(for)i
+(trees)g(in)h(RDBMs)g(are)e(kno)n(wn)h(and)h(used:)220
+2857 y(1.)41 b Fg(P)m(oin)m(ters:)k Fh(a)31 b(\034eld)h(in)h(the)f(c)n
+(hild)g(record)e(references)h(the)h(paren)n(t)f(no)r(de.)50
+b(This)32 b(seems)g(to)f(b)r(e)i(the)f(canonical)326
+2956 y(represen)n(tation.)38 b(Some)29 b(DB)g(engines)f(pro)n(vide)g
+(sp)r(ecial)g(SQL)g(extensions)g(to)h(simplify)g(tree)g(searc)n(hes;)e
+(Oracle)326 3056 y(tree)d(extensions)g(are)g(an)h(example)f(\(see)h
+(for)f(instance)g([1]\);)i(DB2's)f(WITH)g(can)f(b)r(e)i(used)e(for)h
+(this)g(purp)r(ose)f(to)r(o)326 3156 y(\(see)j([3],)g(pp)h(139-162\).)
+220 3322 y(2.)41 b Fg(Nested)35 b(Sets:)43 b Fh(t)n(w)n(o)30
+b(n)n(umeric)h(\034elds)g(in)g(ev)n(ery)f(no)r(de)h(record)f(co)r(de)h
+(the)g(tree)g(structure.)47 b(I)31 b(can't)g(pro)n(vide)f(a)326
+3421 y(b)r(etter)e(or)e(briefer)h(description)g(of)h(this)g(metho)r(d)g
+(than)f(the)h(four)f(articles)g([2].)118 3587 y(These)g(t)n(w)n(o)g
+(metho)r(ds)h(o\033er)f(di\033eren)n(t)h(adv)-5 b(an)n(tages)25
+b(and)j(disadv)-5 b(an)n(tages:)243 3753 y Ff(\017)41
+b Fh(P)n(oin)n(ters)30 b(are)g(extremely)g(e\036cien)n(t)h(for)f(no)r
+(de)h(insertion)f(and/or)g(deletion,)h(but)h(require)e(recursiv)n(e)f
+(table)i(ac-)326 3853 y(cesses)e(to)h(searc)n(h)f(the)h(tree)g(\(I)h
+(do)f(not)g(kno)n(w)f(the)i(implemen)n(tation)f(details)g(of)g(the)h
+(Oracle)e(tree)g(extensions,)326 3953 y(whic)n(h)e(as)g(far)g(as)g(I)g
+(kno)n(w)g(ma)n(y)g(solv)n(e)f(this)i(problem)f(in)n(ternally;)g(they)g
+(de\034nitely)h(solv)n(e)f(it)g(for)g(the)h(end)g(user\).)243
+4119 y Ff(\017)41 b Fh(Nested)30 b(sets)g(are)f(v)n(ery)f(e\036cien)n
+(t)i(for)g(tree)f(searc)n(hes,)g(but)i(are)e(rather)f(exp)r(ensiv)n(e)i
+(for)f(no)r(de)h(insertion)f(and/or)326 4218 y(deletion:)37
+b(they)27 b(require)g(up)r(dating)g(p)r(oten)n(tially)h(man)n(y)f(no)r
+(des.)243 4384 y(W)-7 b(e)30 b(prop)r(ose)f(here)h(a)g(di\033eren)n(t)h
+(represen)n(tation,)e(based)g(on)i(no)r(de)f(iden)n(ti\034ers)g(whic)n
+(h)g(are)f(\020genealogical)f(iden)n(ti-)118 4484 y(\034ers\021:)44
+b(they)32 b(con)n(tain)f(the)h(complete)f(genealogy)f(of)h(the)h(no)r
+(de,)h(i.e.,)g(the)f(list)g(of)g(ancestors)d(up)j(to)g(the)g(ro)r(ot)f
+(of)g(the)118 4584 y(tree.)243 4683 y(This)j(allo)n(ws)f(to)i(replace)e
+(man)n(y)h(searc)n(hes)f(in)h(database)g(tables)g(with)h(string)f(op)r
+(erations)f(on)h(the)h(index.)58 b(The)118 4783 y(result,)24
+b(as)f(explained)h(in)g(Section)g(3)f(is)h(that)g(tree)f(searc)n(hes)f
+(pro)r(ceed)h(at)h(\020nested)f(sets\021)30 b(sp)r(eed,)25
+b(while)f(no)r(de)g(insertions)118 4882 y(and)k(deletions)f(are)f(as)h
+(fast)h(as)f(with)h(p)r(oin)n(ters.)243 4982 y(The)i(ob)n(vious)f(do)n
+(wnside)h(of)h(the)g(metho)r(d)g(is)f(that)h(the)g(primary)f(k)n(ey)f
+(in)i(the)g(tree)f(needs)h(to)f(b)r(e)h(a)g(v)-5 b(ariable)29
+b(size)118 5082 y(text)j(\034eld,)h(and)f(that)g(the)g(iden)n
+(ti\034ers)f(ma)n(y)g(b)r(e)i(extremelly)e(long)g(for)g(deep)h(trees.)
+49 b(W)-7 b(e)32 b(will)g(pro)n(vide)e(estimates)i(of)118
+5181 y(the)c(size)f(required)g(as)g(a)g(function)h(of)g(the)f
+(magnitude)h(of)f(the)h(tree.)1987 5653 y(1)p eop
+%%Page: 2 2
+2 1 bop 118 291 a Fi(2)131 b(Genealogical)45 b(iden)l(ti\034ers)g(for)f
+(trees)118 489 y Fm(2.1)112 b(De\034nition)118 642 y
+Fh(W)-7 b(e)28 b(de\034ne)g Fe(gene)l(alo)l(gic)l(al)k(identi\034ers)j
+Fh(recursiv)n(ely)25 b(as)i(follo)n(ws:)326 808 y Fg(De\034nition:)59
+b Fe(The)42 b(gene)l(alo)l(gic)l(al)h(identi\034er)f(\(gID\))e(of)i(a)f
+(no)l(de)h(is)f(obtaine)l(d)h(by)g(app)l(ending)g(a)f(child)326
+908 y(identi\034er)30 b(to)g(the)g(gene)l(alo)l(gic)l(al)h
+(identi\034er)g(of)f(the)g(p)l(ar)l(ent)f(no)l(de.)243
+1074 y Fh(Remark)40 b(that)h(genealogical)e(iden)n(ti\034ers)i(are)f
+(rather)g(w)n(ell)h(kno)n(wn)f(and)h(used;)48 b(common)41
+b(examples)f(are)g(the)118 1174 y(\020path+\034le-name\021)33
+b(in)28 b(a)f(computer)g(\034le)h(system)f(and)h(the)f(URLs)h(within)g
+(a)f(WWW.)243 1273 y(The)d(name)g(\020genealogical)e(iden)n
+(ti\034er\021)30 b(is)24 b(suggested)g(b)n(y)g(the)g(fact)h(that)f(the)
+h(v)-5 b(alue)24 b(of)g(the)h(iden)n(ti\034er)f(con)n(tains)f(the)118
+1373 y(complete)30 b(genealogy)d(of)j(the)g(no)r(de:)41
+b(it)30 b(con)n(tains)e(as)h(a)h(substring)f(the)h(gID)f(of)h(its)g
+(father,)g(whic)n(h)f(in)h(turn)g(con)n(tains)118 1472
+y(as)d(a)g(substring)g(the)h(gID)g(of)f(the)h(grandfather,)e(.)14
+b(.)g(.)243 1572 y(The)27 b(ro)r(ot)g(no)r(de)h(of)f(the)h(tree)f(has)g
+(a)h(gID)f(with)h(v)-5 b(alue)28 b(\021)34 b(\(the)28
+b(empt)n(y)g(string\),)f(as)g(it)h(has)f(no)g(paren)n(t.)118
+1804 y Fm(2.2)112 b(Child)36 b(iden)m(ti\034ers)118 1958
+y Fh(The)26 b(ob)n(vious)e(c)n(hild)i(iden)n(ti\034er)g(is)f(a)h
+(zero-based)d(coun)n(ter:)35 b(iden)n(tify)26 b(the)h(c)n(hild)e(b)n(y)
+h(the)g(n)n(um)n(b)r(er)f(of)h(older)f(brethren)g(it)118
+2057 y(has.)243 2157 y(W)-7 b(e)25 b(could)f(represen)n(t)g(the)h(coun)
+n(ter)f(in)h(base)f(10;)h(this)g(ho)n(w)n(ev)n(er)e(is)i(extremely)f(w)
+n(asteful)g(of)h(resources.)34 b(It)25 b(is)g(m)n(uc)n(h)118
+2257 y(b)r(etter)33 b(to)f(represen)n(t)f(the)h(coun)n(ter)g(in)g(as)g
+(large)e(a)i(base)g(as)f(p)r(ossible:)46 b(in)n(terpret)32
+b(as)f(n)n(um)n(b)r(ers)h(a)g(set)g(of)g(c)n(haracters)118
+2356 y(larger)26 b(than)h({0,1,.)14 b(.)g(.)g(9}.)243
+2456 y(As)26 b(tree)f(op)r(erations)f(will)i(in)n(v)n(olv)n(e)f(string)
+g(op)r(erations)f(on)i(the)g(indices,)g(in)g(order)f(to)g(a)n(v)n(oid)g
+(a)g(\020quoting)g(hell\021)33 b(it)26 b(is)118 2555
+y(desirable)d(to)h(a)n(v)n(oid)e(using)h(an)n(y)g(c)n(haracter)f(with)i
+(a)g(sp)r(ecial)f(meaning)h(in)g(LIKE)g(expressions)e(or)g(regular)g
+(expressions;)118 2655 y(i.e.,)28 b(w)n(e)f(will)h(not)f(use)h(an)n(y)f
+(of)g(the)h(sym)n(b)r(ols)70 b Fd(.)44 b(*)f(^)g(\\)g([)g(])g({)h(})f
+(\()g(\))g(<)g(>)71 b Fh(?)37 b(|)28 b(&)f($)243 2755
+y(W)-7 b(e)28 b(prop)r(ose)e(to)h(reserv)n(e)f(also)g(/)i(as)f(a)g
+(separator)e(\(see)i(\020V)-7 b(ariable)27 b(Sized)g(gID\021)34
+b(b)r(elo)n(w\).)243 2854 y(If)g(w)n(e)f(limit)i(ourselv)n(es)d(to)i
+(ascii)f(c)n(haracters,)g(and)h(a)n(v)n(oid)e(to)i(b)r(e)g(safe)f(a)h
+(lot)g(of)g(other)f(c)n(haracters,)g(w)n(e)g(can)h(use)118
+2954 y(n)n(um)n(b)r(ers)27 b(in)h(base)f(64)g(b)n(y)g(represen)n(ting)
+243 3120 y Ff(\017)41 b Fh(0-9)26 b(with)i('0'-'9')f(\(dec)g(ascii)g
+(co)r(de)h(48-57\))243 3286 y Ff(\017)41 b Fh(10)26 b(with)i(':')37
+b(\(dec)28 b(ascii)f(co)r(de)h(58\))243 3452 y Ff(\017)41
+b Fh(11)26 b(with)i(';')g(\(dec)g(ascii)f(co)r(de)g(59\))243
+3618 y Ff(\017)41 b Fh(12-37)25 b(with)j('A'-'Z')g(\(dec)f(ascii)g(co)r
+(de)h(65-90\))243 3784 y Ff(\017)41 b Fh(38-63)25 b(with)j('a'-'z')f
+(\(dec)h(ascii)f(co)r(de)g(97-122\))118 3950 y(By)g(using)g(base)f(64,)
+h(up)g(to)h(4096)d(c)n(hildren)i(can)f(b)r(e)i(represen)n(ted)e(using)h
+(t)n(w)n(o)f(suc)n(h)h(digits,)g(up)h(to)f(262144)d(with)k(three)118
+4050 y(digits,)g(and)f(up)h(to)f(16777216)d(with)k(four)f(digits.)243
+4149 y(If)37 b(the)g(RDBMs)g(supp)r(orts)f(in)n(ternational)g(c)n
+(haracters,)h(it)g(is)g(p)r(ossible)f(to)h(further)f(increase)g(the)h
+(base;)k(as)36 b(an)118 4249 y(example,)30 b(b)n(y)f(using)g(the)h(95)f
+(additional)g(c)n(haracters)e(of)i(the)h(latin-1)f(c)n(haracter)e(set,)
+k(w)n(e)e(could)g(co)r(de)g(n)n(um)n(b)r(ers)g(in)h(a)118
+4349 y(base)f(up)g(to)g(160)f(\025)g(remark)g(that)h(ev)n(ery)f(single)
+h(digit)g(is)g(still)h(one)e(b)n(yte)h(in)h(this)f(represen)n(tation.)
+40 b(This)29 b(means)f(that)118 4448 y(w)n(e)f(expand)h(the)f(sym)n(b)r
+(ols)g(ab)r(o)n(v)n(e)f(b)n(y)i(represen)n(ting)243 4614
+y Ff(\017)41 b Fh(64-159)25 b(with)j(dec)f(latin1)g(co)r(de)h(160-255)
+243 4780 y(In)23 b(base)g(160,)g(up)g(to)h(25600)d(c)n(hildren)i(can)f
+(b)r(e)i(represen)n(ted)e(using)h(t)n(w)n(o)g(digits,)h(up)g(to)f
+(4096000)d(with)k(three)f(digits,)118 4880 y(and)28 b(up)f(to)h
+(6.5E+08)e(with)i(four)f(digits.)243 4980 y(Remark)g(that)h(base)f(con)
+n(v)n(ersions)f(only)h(need)i(to)e(b)r(e)i(p)r(erformed)e(at)h
+(insertion)g(time,)g(when)h(the)f(index)g(of)g(a)g(new)118
+5079 y(no)r(de)g(is)f(computed.)37 b(They)28 b(will)f(therefore)g(only)
+g(ha)n(v)n(e)f(an)i(impact)f(on)h(insertion)f(timings.)1987
+5653 y(2)p eop
+%%Page: 3 3
+3 2 bop 118 291 a Fm(2.3)112 b(Coun)m(ters:)50 b(\020delimited\021)44
+b(vs.)51 b(\020\034xed)38 b(size\021)118 444 y Fh(The)33
+b(standard)g(represen)n(tation)e(of)i(gID)h(uses)e(a)h(v)-5
+b(ariable)32 b(size)h(c)n(hild)h(iden)n(ti\034er,)g(and)f(delimiters)g
+(to)h(separate)d(the)118 543 y(gID)f(of)g(the)h(c)n(hild)f(no)r(de)g
+(from)f(the)i(gID)f(of)g(its)g(paren)n(t.)43 b(F)-7 b(or)30
+b(example,)g(w)n(e)g(can)f(represen)n(t)g(the)i(\034fth)g(c)n(hild)f
+(of)g(no)r(de)118 643 y('/23/27/1')24 b(as)j('/23/27/1/4'.)32
+b(Let)c(us)f(call)g(this)h(a)f Fg(vgID)h Fh(represen)n(tation)e(\(V)-7
+b(ariable)27 b(Size)h(Genealogical)d(ID\).)243 743 y(This)30
+b(represen)n(tation)f(allo)n(ws)f(for)i(an)n(y)g(n)n(um)n(b)r(er)g(of)g
+(c)n(hildren)g(of)h(a)f(no)r(de,)h(sub)5 b(ject)30 b(only)g(to)g(the)h
+(limitations)f(the)118 842 y(RDBMS)e(ma)n(y)f(ha)n(v)n(e)f(as)h(to)h
+(the)g(length)f(of)h(a)f(v)-5 b(ariable)27 b(sized)g(string.)243
+942 y(Alternativ)n(ely)-7 b(,)24 b(w)n(e)f(could)h(c)n(ho)r(ose)f(to)h
+(limit)g(from)g(the)g(outset)g(the)g(quan)n(tit)n(y)g(of)f(c)n(hildren)
+h(that)g(a)g(no)r(de)g(ma)n(y)f(ha)n(v)n(e;)118 1042
+y(this)28 b(limit)g(w)n(ould)f(dep)r(end)i(of)e(course)f(on)i(the)g
+(application.)36 b(Let)27 b(us)h(call)f(this)h(a)f Fg(fgID)h
+Fh(represen)n(tation.)243 1141 y(F)-7 b(or)25 b(example,)h(if)g(no)g
+(no)r(de)f(is)h(allo)n(w)n(ed)f(to)g(ha)n(v)n(e)g(more)g(than)h(25600)d
+(c)n(hildren,)j(w)n(e)g(could)f(represen)n(t)g(the)h(coun)n(ters)118
+1241 y(alw)n(a)n(ys)36 b(with)i(2)f(digits.)67 b(The)38
+b(no)r(de)f(whic)n(h)h(w)n(as)f(previously)f('/23/27/1/4')d(is)k(no)n
+(w)g('23270104'.)64 b(If)38 b(w)n(e)f(require)118 1340
+y(a)g(three)g(digit)h(represen)n(tation)d(of)i(no)r(des)g(\(up)h(to)f
+(ab)r(out)h(4)f(million)g(c)n(hildren\),)j(then)d(it)h(will)g(b)r(e)f
+(represen)n(ted)f(as)118 1440 y('023027001004'.)118 1672
+y Fm(2.4)112 b(Ordering)37 b(of)h(no)s(des)118 1825 y
+Fh(F)-7 b(or)35 b(some)g(applications)g(it)h(is)f(necessary)f(to)i
+(obtain)f(subtrees)g(ordered)f(according)g(to)i(some)f(sp)r(ecial)g
+(rules.)60 b(F)-7 b(or)118 1925 y(instance:)220 2090
+y(1.)41 b(the)34 b(complete)g(subtree)f(starting)g(at)h(a)f(no)r(de)h
+(is)g(listed)g(immediately)g(after)f(the)i(no)r(de)f(in)g(question)f
+(\(\020depth)326 2189 y(\034rst\021\))220 2354 y(2.)41
+b(no)r(des)27 b(with)h(a)f(common)g(paren)n(t)g(are)g(listed)g(c)n
+(hronologically)243 2519 y(F)-7 b(or)39 b(instance,)k(the)d(displa)n(y)
+f(of)h(an)f(organization)f(c)n(hart)h(is)g(usually)h(required)e(to)i
+(satisfy)g(at)f(least)h(the)g(\034rst)118 2619 y(condition.)h(In)29
+b(a)g(threaded)f(discussion)h(group)e(one)i(wishes)g(to)f(satisfy)h(b)r
+(oth)h(conditions)e(to)h(displa)n(y)f(the)h(messages)118
+2718 y(in)20 b(a)g(thread)g(\025)f(the)i(threads)e(themselv)n(es)h
+(\(i.e.,)i(c)n(hildren)e(of)g(the)g(ro)r(ot)f(no)r(de\))i(are)e
+(usually)g(listed)i(in)f(in)n(v)n(erse)f(c)n(hronolical)118
+2818 y(order.)243 2917 y(T)-7 b(o)35 b(mak)n(e)f(a)h(particular)f
+(ordering)g(e\036cien)n(t,)j(it)f(w)n(ould)f(b)r(e)h(a)f(nice)g
+(feature)g(if)h(it)g(could)f(b)r(e)h(made)f(to)g(coincide)118
+3017 y(with)28 b(a)f(lexicographic)f(ordering)f(of)j(the)g(indices)f
+(\025i.e.,)g(as)g(pro)r(duced)g(b)n(y)h(an)f(\020ORDER)h(BY)f(id)h
+(ASC\021)35 b(in)27 b(SQL.)h(The)118 3117 y(lexicographic)d(ordering)h
+(of)h(fgID)h(satis\034es)e(b)r(oth)i(conditions.)36 b(The)27
+b(lexicographic)f(ordering)f(of)i(vgID)g(as)g(describ)r(ed)118
+3216 y(ab)r(o)n(v)n(e)34 b(satis\034es)g(the)h(\034rst)g(requisite)f
+(if)i(the)f(separator)d(has)j(the)g(minimal)g(binary)g(represen)n
+(tation)e(of)i(all)f(allo)n(w)n(ed)118 3316 y(sym)n(b)r(ols)c(in)h(an)f
+(index)h(\025)f(this)h(is)g(wh)n(y)f(w)n(e)g(reserv)n(ed)f(/)h(for)g
+(the)i(separator.)43 b(But)31 b(the)g(second)f(prop)r(ert)n(y)g(is)g
+(missing:)118 3416 y(for)d(instance,)g(the)h(index)g('/1/10')d(is)j
+(lexicographically)d(b)r(efore)i('/1/2'.)243 3515 y(If)c(the)h(second)e
+(prop)r(ert)n(y)g(is)i(also)e(required)g(for)h(vgID,)g(w)n(e)f(can)h
+(sp)r(ecify)h(the)f(c)n(hild)h(iden)n(ti\034ers)e(with)i(coun)n(ters)e
+(built)118 3615 y(in)28 b(the)g(follo)n(wing)e(w)n(a)n(y:)36
+b(represen)n(t)26 b(a)h(n)n(um)n(b)r(er)h(b)n(y)f(a)g(string)g(of)g
+(digits,)h(where)243 3779 y Ff(\017)41 b Fh(the)25 b(\034rst)g(digit)h
+Fc(D)896 3791 y Fb(0)958 3779 y Fh(represen)n(ts)e(the)i(length)f(in)h
+(digits)f(of)g(the)h(decimal)f(expansion)f(of)i(the)f(n)n(um)n(b)r(er,)
+h(min)n(us)f(one)243 3945 y Ff(\017)41 b Fh(the)28 b(follo)n(wing)e
+Fa(\()p Fc(D)920 3957 y Fb(0)976 3945 y Fa(+)18 b(1\))27
+b Fh(digits)h(are)e(the)i(decimal)g(expansion)e(of)i(the)g(n)n(um)n(b)r
+(er)118 4109 y(Let)g(us)f(call)h(these)f(iden)n(ti\034ers)g
+Fg(m-vgID)p Fh(,)g(\020m\021)34 b(for)27 b(mo)r(di\034ed.)243
+4209 y(As)e(an)f(example,)h(the)g(no)r(de)g(whic)n(h)g(w)n(as)f
+(previously)f(represen)n(ted)h(b)n(y)g(/15/3/182)d(will,)k(after)g
+(this)g(mo)r(di\034cation,)118 4309 y(ha)n(v)n(e)h(the)i(index)g
+(/115/03/2182.)243 4408 y(The)37 b(lexicographic)f(ordering)g(of)i
+(m-vgID)f(is)h(the)g(desired)f(ordering)f(of)h(the)h(tree)g(no)r(des.)
+67 b(The)38 b(cost)f(of)g(this)118 4508 y(prop)r(ert)n(y)31
+b(is)i(that)f(\(a\))h(the)g(ID)f(are)g(no)n(w)g(longer,)g(\(b\))h(no)f
+(no)r(de)g(can)g(ha)n(v)n(e)g(more)f(than)i Fa(160)3106
+4478 y Fb(160)3240 4508 y Fh(c)n(hildren)f(\(actually)-7
+b(,)118 4607 y(this)32 b(is)g(a)f(non-issue\),)h(and)f(\(c\))h(the)g
+(index)g(structure)f(is)h(redundan)n(t,)g(some)f(formally)f(correct)h
+(indices)g(are)g(in)n(v)-5 b(alid)118 4707 y(\025e.g.,)24
+b(/316/013/11.)30 b(The)24 b(third)g(issue)g(can)g(b)r(e)g(addressed)f
+(b)n(y)g(k)n(eeping)g(a)h(strict)g(con)n(trol)e(on)i(the)g(generation)f
+(of)h(new)118 4807 y(indices)k(to)f(insure)g(that)h(all)f(indices)h
+(are)e(formally)h(correct.)243 4906 y(The)32 b(issue)f(of)h(the)g(rev)n
+(erse)e(c)n(hronological)f(indexing)j(of)f(threads)h(in)g(threaded)f
+(discussion)g(groups)g(can)g(b)r(e)h(ad-)118 5006 y(dressed)d(easily)f
+(enough)h(in)h(fgID:)f(coun)n(t)g(\020do)n(wn\021)36
+b(instead)29 b(of)g(\020up\021)36 b(the)30 b(c)n(hildren)f(of)g(the)h
+(ro)r(ot)e(no)r(de)i(\025)f(this)h(implies)118 5106 y(only)e(an)g
+(inconsequen)n(tial)f(mo)r(di\034cation)h(of)g(the)g(no)r(de)h
+(insertion)e(routine,)h(as)g(sho)n(wn)f(b)r(elo)n(w.)38
+b(The)29 b(problem)e(is)h(less)118 5205 y(trivial)i(with)g(vgID;)h(in)f
+(this)h(case,)f(ma)n(yb)r(e)f(a)h(thread)g(iden)n(ti\034er)g(should)g
+(b)r(e)h(k)n(ept)f(in)g(a)g(di\033eren)n(t)g(\034eld)h(-)f(i.e.,)h
+(repre-)118 5305 y(sen)n(ting)h(the)h(structure)f(as)g(a)h(forest)f
+(rather)f(than)i(a)f(tree,)i(where)e(the)h(thread_id)f(\034eld)h
+(selects)f(the)h(\020tree\021)38 b(in)33 b(the)118 5404
+y(forest.)1987 5653 y(3)p eop
+%%Page: 4 4
+4 3 bop 118 291 a Fi(3)131 b(T)-11 b(ree)45 b(op)t(erations)e(using)h
+(genealogical)g(indices)118 472 y Fh(In)32 b(this)f(section)g(w)n(e)g
+(sho)n(w)g(ho)n(w)g(to)g(implemen)n(t)h(v)-5 b(arious)30
+b(tree)h(op)r(erations)f(using)h(gID)g(as)g(the)h(primary)e(k)n(ey)h
+(in)g(the)118 572 y(no)r(de)d(table.)243 672 y(Some)h(implemen)n
+(tation)h(issues)g(are)f(relev)-5 b(an)n(t)29 b(here,)h(esp)r(ecially)f
+(concerning)g(the)h(utilisation)g(of)g(indices)g(b)n(y)f(the)118
+771 y(DB)f(engine.)243 871 y(W)-7 b(e)28 b(discuss)f(a)g(tree)g
+(represen)n(ted)f(in)i(a)f(table)h(of)f(the)h(form)326
+1034 y Fd(CREATE)41 b(TABLE)g(tree)h(\()456 1134 y(gid)304
+b(text)42 b(PRIMARY)f(KEY,)456 1234 y(nchildren)f(integer)h(DEFAULT)f
+(0,)456 1333 y(\\ldots)h(the)i(actual)e(node)h(data)326
+1433 y(\);)118 1597 y Fh(The)26 b(\034eld)g(\020nc)n(hildren\021)32
+b(is)26 b(a)f(coun)n(ter)g(for)g(the)i(n)n(um)n(b)r(er)e(of)h(c)n
+(hildren)f(that)h(the)h(no)r(de)f(has)f Fe(ever)35 b
+Fh(had;)27 b(w)n(e)e(assume)g(here)118 1696 y(it)j(is)g(not)f(up)r
+(dated)h(when)g(no)r(des)f(or)g(subtrees)g(are)f(deleted.)243
+1796 y(Section)h(4)g(pro)n(vides)f(a)i(complete)f(implemen)n(tation)h
+(of)f(these)h(op)r(erations)e(for)h(fgID)h(in)g(P)n(ostgreSQL.)118
+2028 y Fm(3.1)112 b(Computing)37 b(the)g(lev)m(el)f(of)h(a)h(no)s(de)
+118 2181 y Fg(Cost:)f Fe(string)30 b(op)l(er)l(ations)g(\(no)g(table)g
+(ac)l(c)l(ess\))243 2280 y Fh(This)d(is)h(a)f(pure)g(string)g(op)r
+(eration,)f(no)i(table)f(access)g(is)g(required.)243
+2460 y Ff(\017)41 b Fg(vgID:)27 b Fh(coun)n(t)h(the)g(n)n(um)n(b)r(er)f
+(of)g(separators)e(\('/'\))j(in)g(the)g(PK)243 2625 y
+Ff(\017)41 b Fg(fgID:)27 b Fh(coun)n(t)g(the)h(n)n(um)n(b)r(er)g(of)f
+(c)n(haracters)e(in)j(the)g(PK,)g(divide)g(b)n(y)f(the)h(\034xed)f
+(size)h(of)f(the)h(coun)n(ters.)118 2857 y Fm(3.2)112
+b(Selecting)36 b(or)h(deleting)f(a)i(subtree)118 3010
+y Fg(Cost:)f Fe(index)30 b(sc)l(an)g(of)g(the)g(tr)l(e)l(e)243
+3173 y Ff(\017)41 b Fg(vgID:)27 b Fh(The)h(subtree)f(ro)r(oted)g(at)g
+(/26/5/7)e(is)i(selected)g(b)n(y)508 3338 y Fd(...)43
+b(WHERE)e(id)i(LIKE)f('/26/5/7\045')d(AND)j(id)h(<)g('/26/5/70')243
+3503 y Ff(\017)e Fg(m-vgID:)26 b Fh(The)h(subtree)h(ro)r(oted)e(at)i
+(/126/05/07)22 b(is)28 b(selected)f(b)n(y)508 3668 y
+Fd(...)43 b(WHERE)e(id)i(LIKE)f('/126/06/07\045')243
+3833 y Ff(\017)f Fg(fgID:)27 b Fh(The)h(subtree)f(ro)r(oted)g(at)g
+(260507)e(is)i(selected)h(b)n(y)508 3997 y Fd(...)43
+b(WHERE)e(id)i(LIKE)f('260507\045')118 4229 y Fm(3.3)112
+b(Selecting)36 b(the)h(direct)f(c)m(hildren)g(of)i(a)g(no)s(de)118
+4382 y Fg(Cost:)f Fe(index)30 b(sc)l(an)g(of)g(the)g(tr)l(e)l(e)243
+4562 y Ff(\017)41 b Fg(vgID:)27 b Fh(The)h(direct)f(c)n(hildren)g(of)h
+(/26/5/7)c(are)j(selected)g(b)n(y)508 4727 y Fd(...)43
+b(WHERE)e(id)i(LIKE)f('/26/5/7/\045')d(AND)j(id)h(NOT)f(LIKE)g
+('26/5/7/\045/\045')243 4892 y Ff(\017)f Fg(m-vgID:)26
+b Fh(The)h(direct)h(c)n(hildren)f(of)g(/26/5/7)e(are)h(selected)i(b)n
+(y)508 5056 y Fd(...)43 b(WHERE)e(id)i(LIKE)f('/126/06/07/\045')37
+b(AND)43 b(id)f(NOT)h(LIKE)f('/126/05/07/\045/\045)o(')243
+5221 y Ff(\017)f Fg(fgID:)27 b Fh(The)h(direct)f(c)n(hildren)g(of)h
+(260507)c(are)j(selected)g(b)n(y)508 5386 y Fd(...)43
+b(WHERE)e(id)i(LIKE)f('260507\045')d(AND)k(char_length\(id\))37
+b(=)43 b(\(char_length\('26)o(05)o(07')o(\)+)o(2\))1987
+5653 y Fh(4)p eop
+%%Page: 5 5
+5 4 bop 118 291 a Fm(3.4)112 b(Inserting)37 b(a)h(no)s(de)g(or)f(a)h
+(subtree)118 444 y Fg(Cost:)f Fe(index)30 b(sc)l(an)g(of)g(the)g(tr)l
+(e)l(e)f(+)h(string)f(and)h(math)g(op)l(er)l(ations)243
+543 y Fh(Insertion)f(is)g(a)h(pro)r(cedural)e(op)r(eration.)42
+b(As)30 b(eac)n(h)f(RDBMS)h(has)f(a)h(di\033eren)n(t)f(w)n(a)n(y)g(of)g
+(de\034ning)h(pro)r(cedures,)f(w)n(e)118 643 y(will)f(just)g(describ)r
+(e)f(here)g(the)h(necessary)e(steps.)37 b(Examples)27
+b(for)g(P)n(ostgreSQL)f(are)h(pro)n(vided)f(in)i(4.)243
+743 y(In)22 b(order)f(to)h(insert)g(a)g(new)g(c)n(hild)h(of)f
+(\020daddy\021)28 b(\(either)23 b(one)f(of)g(/26/5/7,)e(/126/05/07)d
+(or)22 b(260507)d(in)k(the)f(examples)118 842 y(ab)r(o)n(v)n(e\))27
+b(y)n(ou)f(ha)n(v)n(e)h(to)220 1008 y(1.)41 b(add)27
+b(one)g(to)h(the)g(n)n(um)n(b)r(er)f(of)g(c)n(hildren)h(of)f
+(\020daddy\021)508 1174 y Fd(UPDATE)41 b(tree)h(SET)h(nchildren)c(=)k
+(\(nchildren)d(+)j(1\))g(WHERE)e(ID)i(=)g(``daddy'';)220
+1340 y Fh(2.)e(enco)r(de)27 b(the)h(n)n(um)n(b)r(er)f(of)g(c)n(hildren)
+g(of)h(\020daddy\021)33 b(in)28 b(base)f(160,)f(bring)h(it)h(to)f(the)h
+(correct)e(format)h(dep)r(ending)h(on)326 1440 y(the)c(v)-5
+b(arian)n(t)23 b(of)h(gID)g(\(pad)g(with)h(0)e(or)g(not,)i(prep)r(end)f
+(a)g(digit)g(coun)n(ter)f(or)g(not,)i(prep)r(end)f(/)g(or)f(not,)i
+(coun)n(t)e(do)n(wn)326 1540 y(or)j(up,)i(.)14 b(.)g(.)g(\))37
+b(and)28 b(app)r(end)f(it)h(to)g(daddy's)f(gID)g(to)h(obtain)f(the)h
+(new)g(no)r(de's)f(gID.)220 1706 y(3.)41 b(insert)27
+b(the)h(new)f(no)r(de)243 1872 y(When)35 b(inserting)g(a)f(subtree,)j
+(the)e(index)g(of)g(the)h(ro)r(ot)e(of)h(the)g(subtree)g(has)f(to)h(b)r
+(e)h(computed)f(as)f(ab)r(o)n(v)n(e,)i(and)118 1971 y(prep)r(ended)28
+b(to)f(the)h(index)g(of)f(eac)n(h)g(no)r(de)h(of)f(the)h(subtree)f(b)r
+(efore)h(insertion.)243 2071 y(Remark)e(that)i(only)f(the)h(paren)n(t)f
+(no)r(de)h(has)f(to)g(b)r(e)h(up)r(dated)g(on)f(insertion.)118
+2303 y Fm(3.5)112 b(Selecting)36 b(the)h(ancestors)h(of)g(a)g(no)s(de)
+118 2457 y Fg(Cost:)f Fe(index)30 b(sc)l(an)g(of)g(the)g(tr)l(e)l(e)243
+2556 y Fh(Y)-7 b(ou)27 b(can)g(sp)r(ecify)h(all)g(ancestors)d(of)j(a)f
+(no)r(de)h(in)f(a)h(single)f(SQL)g(statemen)n(t;)g(for)g(instance)h
+(for)f(vgID)326 2722 y Fd(...)42 b(WHERE)f('/25/6/7')f(LIKE)i(\(id)g
+(||)h('/\045'\))f(AND)g(id)h(<)g('/25/6/7')118 2888 y
+Fh(The)31 b(second)e(part)h(of)h(the)g(clause,)f(while)h(logically)e
+(redundan)n(t,)h(is)h(a)f(\020hin)n(t\021)37 b(to)30
+b(the)h(optimizer.)45 b(A)n(t)31 b(least)f(in)g(P)n(ost-)118
+2988 y(greSQL,)c(without)i(it)g(the)g(optimizer)f(will)h(c)n(ho)r(ose)e
+(a)i(sequen)n(tial)e(scan)h(of)h(the)g(table)f(and)h(disregard)d(the)j
+(index.)118 3220 y Fm(3.6)112 b(Selecting)36 b(all)g(lea)m(v)m(es)118
+3374 y Fg(Cost:)h Fe(sc)l(an)30 b(of)g(the)g(tr)l(e)l(e)243
+3473 y Fh(A)e(leaf)f(is)g(a)h(no)r(de)f(without)h(descendan)n(ts:)36
+b(it)28 b(has)f(0)g(c)n(hildren.)37 b(Hence)326 3639
+y Fd(...)42 b(WHERE)f(nchildren)f(=)j(0)118 3805 y Fh(If)28
+b(this)g(t)n(yp)r(e)g(of)f(query)g(is)h(often)f(necessary)-7
+b(,)26 b(y)n(ou)h(ma)n(y)g(b)r(e)h(w)n(ell)f(advised)g(to)g(k)n(eep)g
+(an)h(index)f(on)h(tree\(nc)n(hildren\).)118 4038 y Fm(3.7)112
+b(Determining)35 b(if)i(A)g(is)g(a)h(descendan)m(t)g(of)g(B)118
+4191 y Fg(Cost:)f Fe(string)30 b(op)l(er)l(ations,)h(no)f(table)g(ac)l
+(c)l(ess)243 4291 y Fh(This)d(is)h(a)f(pure)g(string)g(op)r(eration)f
+(on)i(the)g(indices,)f(no)g(table)h(access)e(is)i(necessary)-7
+b(.)118 4565 y Fi(4)131 b(Putting)45 b(it)f(all)h(together:)57
+b(a)44 b(P)l(ostgreSQL)f(implemen)l(tation)118 4747 y
+Fh(h)n(ttp://www.p)r(ostgresql.org/mhonarc/pgsq)o(l-sql/)o(20)o(00)o
+(-0)o(4/)o(msg0)o(02)o(67)o(.h)n(tml)243 4847 y(W)-7
+b(e)30 b(describ)r(e)g(here)g(a)g(small)f(pac)n(k)-5
+b(age)29 b(that)i(can)e(b)r(e)i(used)f(for)g(implemen)n(ting)g(gID)g
+(on)g(P)n(ostgreSQL.)f(It)i(can)e(b)r(e)118 4946 y(found)f(at)f(<h)n
+(ttp://...>)243 5046 y(The)21 b(pac)n(k)-5 b(age)21 b(uses)g(the)h(pro)
+r(cedural)e(language)h(PL/PGsql.)35 b(A)22 b(b)r(etter)g(implemen)n
+(tation)g(w)n(ould)f(probably)g(de\034ne)118 5145 y(the)28
+b(gID)g(as)f(new)g(P)n(ostgres)f(t)n(yp)r(es,)i(and)f(co)r(de)g(all)h
+(this)g(in)f(C.)243 5245 y(The)g(\034les)h(should)f(b)r(e)h(loaded)f
+(in)h(alphab)r(etical)f(order.)1987 5653 y(5)p eop
+%%Page: 6 6
+6 5 bop 118 291 a Fm(4.1)112 b(tree0_enco)s(ding.sql)118
+444 y Fh(This)28 b(\034le)f(de\034nes)h(and)f(p)r(opulates)h(the)f
+(table)h(_b160_digits)d(of)j(\020digits\021)33 b(in)28
+b(base)f(160,)326 604 y Fd(CREATE)41 b(TABLE)g(\\_b160\\_digits)d
+(\(deci)j(integer,)f(code)i(char\);)118 764 y Fh(and)28
+b(the)f(t)n(w)n(o)g(functions)326 924 y Fd(CREATE)41
+b(FUNCTION)f(\\_b160\\_encode\(i)o(nt)o(eg)o(er\))d(RETURNS)j(string)
+413 1024 y(AS)j('....')e(LANGUAGE)f('plpgsql';)326 1124
+y(CREATE)h(FUNCTION)f(\\_b160\\_encode\(i)o(nt)o(eg)o(er,)o(in)o(te)o
+(ger)o(\))d(RETURNS)k(string)413 1223 y(AS)i('....')e(LANGUAGE)f
+('plpgsql';)118 1384 y Fh(The)22 b(\034rst)h(function)f(returns)g(a)g
+(v)-5 b(ariable)21 b(size)h(enco)r(ding;)i(the)f(second)e(a)h(\034xed)h
+(size)f(enco)r(ding)g(\(the)h(second)e(parameter)118
+1483 y(is)g(the)h(size\),)g(and)f(raises)e(an)i(exception)g(if)h(the)f
+(n)n(um)n(b)r(er)g(is)g(to)r(o)g(large)e(to)i(b)r(e)h(represen)n(ted)e
+(with)h(the)h(requested)e(n)n(um)n(b)r(er)118 1583 y(of)28
+b(digits.)118 1814 y Fm(4.2)112 b(tree1_de\034ne.sql)118
+1967 y Fh(This)28 b(\034le)f(pro)n(vides)f(a)i(function)326
+2127 y Fd(CREATE)41 b(FUNCTION)f(_tree_create\(tex)o(t,)o(in)o(teg)o
+(er)o(,t)o(ext)o(,t)o(ex)o(t\))d(RETURNS)k(bpchar)413
+2227 y(AS)i('....')e(LANGUAGE)f('plpgsql';)118 2387 y
+Fh(that)e(creates)f(a)h(tree)f(infrastructure)g(of)h(either)g(fgID)g
+(or)f(vgID.)h(Assuming)f(y)n(ou)g(ha)n(v)n(e)g(a)h(table)f(\020m)n
+(ytable\021)44 b(with)118 2487 y(primary)26 b(k)n(ey)h(\020m)n
+(yid\021,)g(then)h(calling)326 2647 y Fd(SELECT)41 b(_tree_create\('m)o
+(yt)o(ree)o(',)o(2,')o(my)o(ta)o(ble)o(',)o('m)o(yid)o('\))o(;)118
+2807 y Fh(will)28 b(cause:)220 2967 y(1.)41 b(the)28
+b(creation)e(of)i(a)f(table)508 3131 y Fd(CREATE)41 b(TABLE)h
+(mytree_bkg\()683 3230 y(gid)g(text)g(PRIMARY)e(KEY,)683
+3330 y(nchildren)f(int,)683 3429 y(sid)j(integer)f(REFERENCES)e
+(mytable\(myid\))508 3529 y(\);)508 3629 y(CREATE)i(UNIQUE)g(INDEX)h
+(mytree_bkg_sid)37 b(ON)43 b(mytree_bkg\(sid\);)326 3792
+y Fh(for)27 b(the)h(tree)f(structure.)220 3955 y(2.)41
+b(the)28 b(creation)e(of)i(a)f(view)508 4118 y Fd(CREATE)41
+b(VIEW)h(mytree)f(AS)639 4218 y(SELECT)g(t.gid,n.*)900
+4317 y(FROM)h(mytable)f(n,)i(mytree_bkg)c(t)900 4417
+y(WHERE)j(t.sid=n.myid;)326 4580 y Fh(with:)35 b(a)23
+b(trigger)e(on)i(UPD)n(A)-7 b(TE)25 b(that)e(blo)r(c)n(ks)g(up)r
+(dating)g(the)h(gid)f(and)g(allo)n(ws)f(up)r(dating)h(the)g(no)r(de)h
+(data,)f(a)g(rule)326 4680 y(on)k(DELETE)i(that)f(deletes)f(the)h
+(corresp)r(onding)e(en)n(try)h(b)r(oth)h(in)g(m)n(ytree_bkg)d(and)j(m)n
+(ytable,)f(and)g(a)g(trigger)326 4779 y(ON)h(INSER)-7
+b(T)30 b(that)f(raises)e(an)h(exception)g(and)g(informs)h(the)f(user)g
+(to)h(use)f(the)h(insertion)f(function)h(describ)r(ed)326
+4879 y(b)r(elo)n(w.)220 5042 y(3.)41 b(t)n(w)n(o)26 b(insertion)h
+(functions)h(that)g(compute)g(automatically)e(the)i(gID)g(of)f(the)h
+(new)g(no)r(de:)425 5205 y Ff(\017)41 b Fh(a)27 b(function)i(m)n
+(ytree_insert\(text,text,in)n(teger,text\))d(for)h(insertion)g(sim)n
+(ultaneosly)f(in)i(b)r(oth)g(tables:)508 5305 y(m)n
+(ytree_insert\('2201','hello',0,'not)15 b(m)n(uc)n(h'\))j(inserts)g(a)g
+(new)g(c)n(hild)h(of)f(2201)f(with)h(data1='hello',)h(data2=0)508
+5404 y(and)28 b(data3='not)e(m)n(uc)n(h')1987 5653 y(6)p
+eop
+%%Page: 7 7
+7 6 bop 425 291 a Ff(\017)41 b Fh(a)27 b(function)i(m)n
+(ytree_insert_no)r(de\(text,in)n(teger\))c(for)i(insertion)g(in)h(m)n
+(ytree_bkg)508 390 y(m)n(ytree_insert\('2201',25\))c(inserts)j(in)h(m)n
+(ytree_bkg)e(a)h(new)h(c)n(hild)f(of)h(2201)d(with)j(sid=25)220
+556 y(4.)41 b(a)27 b(function)h(m)n(ytree_mo)n(v)n(e\(text,text\))e
+(that)i(mo)n(v)n(es)e(subtrees:)326 656 y(m)n(ytree_mo)n(v)n
+(e\('2201','23'\))d(mo)n(v)n(es)j(the)i(subtree)f(ro)r(oted)g(at)g
+(2201)f(to)h(a)h(place)f(b)r(elo)n(w)g(23)f(\(ma)n(yb)r(e)i(2307\))220
+822 y(5.)41 b(a)c(function)g(m)n(ytree_len\(\))g(that)h(returns)e(the)i
+(length)f(of)g(the)h(enco)r(dings)f(used)g(in)h(the)f(gID)g(\(2)h
+(here;)j(0)c(if)326 922 y(v)-5 b(ariable)26 b(size\).)118
+1196 y Fi(5)131 b(Non-tree)44 b(hierarc)l(hies)118 1378
+y Fh(sequence)22 b(as)f(id,)j(table)e(with)h(\(id,g-index\))f(with)g(p)
+r(ossibly)g(man)n(y)g(g-indices)f(for)h(eac)n(h)f(id)h(\(if)h(TOO)f
+(man)n(y)-7 b(,)23 b(bad)f(mo)r(del:)118 1478 y(list)28
+b(all)f(genealogies,)f(i.e.,)h(paths)h(from)f(the)h(ro)r(ot\))118
+1752 y Fi(References)160 1934 y Fh([1])41 b(Philip)28
+b(Greenspun,)g Fe(T)-6 b(r)l(e)l(es)29 b(in)h(Or)l(acle)g(SQL)p
+Fh(,)d(in)h Fg(SQL)k(for)g(W)-8 b(eb)31 b(Nerds)289 2033
+y Fh(<h)n(ttp://photo.net/sql/trees.h)n(tml>)160 2200
+y([2])41 b(Jo)r(e)27 b(Celk)n(o,)f Fe(SQL)j(for)i(Smarties)p
+Fh(,)d(in)g Fg(DBMS)j(Online)p Fh(,)26 b(Marc)n(h)h(to)g(June)h(1996)
+289 2299 y(<h)n(ttp://www.dbmsmag.com/9603d06.h)n(tml>)289
+2399 y(<h)n(ttp://www.dbmsmag.com/9604d06.h)n(tml>)289
+2498 y(<h)n(ttp://www.dbmsmag.com/9605d06.h)n(tml>)289
+2598 y(<h)n(ttp://www.dbmsmag.com/9606d06.h)n(tml>)160
+2764 y([3])41 b(Graeme)26 b(Birc)n(hall,)h Fg(DB2)32
+b(UDB)g(V6.1)f(SQL)h(Co)s(okb)s(o)s(ok)p Fh(,)289 2864
+y(<h)n(ttp://ourw)n(orld.compuserv)n(e.com/homepag)o(es/)o(Gra)o
+(eme_Bir)o(c)n(ha)o(ll/HTM_CO)o(OK)o(.HTM>)1987 5653
+y(7)p eop
+%%Trailer
+end
+userdict /end-hook known{end-hook}if
+%%EOF
diff --git a/source4/lib/ldb/ldb_tdb/ldb_cache.c b/source4/lib/ldb/ldb_tdb/ldb_cache.c
new file mode 100644
index 0000000000..43b965f239
--- /dev/null
+++ b/source4/lib/ldb/ldb_tdb/ldb_cache.c
@@ -0,0 +1,477 @@
+/*
+ ldb database library
+
+ Copyright (C) Andrew Tridgell 2004
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb tdb cache functions
+ *
+ * Description: cache special records in a ldb/tdb
+ *
+ * Author: Andrew Tridgell
+ */
+
+#include "ldb_tdb.h"
+
+#define LTDB_FLAG_CASE_INSENSITIVE (1<<0)
+#define LTDB_FLAG_INTEGER (1<<1)
+#define LTDB_FLAG_HIDDEN (1<<2)
+
+/* valid attribute flags */
+static const struct {
+ const char *name;
+ int value;
+} ltdb_valid_attr_flags[] = {
+ { "CASE_INSENSITIVE", LTDB_FLAG_CASE_INSENSITIVE },
+ { "INTEGER", LTDB_FLAG_INTEGER },
+ { "HIDDEN", LTDB_FLAG_HIDDEN },
+ { "NONE", 0 },
+ { NULL, 0 }
+};
+
+
+/*
+ de-register any special handlers for @ATTRIBUTES
+*/
+static void ltdb_attributes_unload(struct ldb_module *module)
+{
+ struct ldb_context *ldb;
+ void *data = ldb_module_get_private(module);
+ struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private);
+ struct ldb_message *msg;
+ int i;
+
+ ldb = ldb_module_get_ctx(module);
+
+ if (ltdb->cache->attributes == NULL) {
+ /* no previously loaded attributes */
+ return;
+ }
+
+ msg = ltdb->cache->attributes;
+ for (i=0;i<msg->num_elements;i++) {
+ ldb_schema_attribute_remove(ldb, msg->elements[i].name);
+ }
+
+ talloc_free(ltdb->cache->attributes);
+ ltdb->cache->attributes = NULL;
+}
+
+/*
+ add up the attrib flags for a @ATTRIBUTES element
+*/
+static int ltdb_attributes_flags(struct ldb_message_element *el, unsigned *v)
+{
+ int i;
+ unsigned value = 0;
+ for (i=0;i<el->num_values;i++) {
+ int j;
+ for (j=0;ltdb_valid_attr_flags[j].name;j++) {
+ if (strcmp(ltdb_valid_attr_flags[j].name,
+ (char *)el->values[i].data) == 0) {
+ value |= ltdb_valid_attr_flags[j].value;
+ break;
+ }
+ }
+ if (ltdb_valid_attr_flags[j].name == NULL) {
+ return -1;
+ }
+ }
+ *v = value;
+ return 0;
+}
+
+/*
+ register any special handlers from @ATTRIBUTES
+*/
+static int ltdb_attributes_load(struct ldb_module *module)
+{
+ struct ldb_context *ldb;
+ void *data = ldb_module_get_private(module);
+ struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private);
+ struct ldb_message *msg = ltdb->cache->attributes;
+ struct ldb_dn *dn;
+ int i, r;
+
+ ldb = ldb_module_get_ctx(module);
+
+ dn = ldb_dn_new(module, ldb, LTDB_ATTRIBUTES);
+ if (dn == NULL) goto failed;
+
+ r = ltdb_search_dn1(module, dn, msg);
+ talloc_free(dn);
+ if (r != LDB_SUCCESS && r != LDB_ERR_NO_SUCH_OBJECT) {
+ goto failed;
+ }
+ if (r == LDB_ERR_NO_SUCH_OBJECT) {
+ return 0;
+ }
+ /* mapping these flags onto ldap 'syntaxes' isn't strictly correct,
+ but its close enough for now */
+ for (i=0;i<msg->num_elements;i++) {
+ unsigned flags;
+ const char *syntax;
+ const struct ldb_schema_syntax *s;
+
+ if (ltdb_attributes_flags(&msg->elements[i], &flags) != 0) {
+ ldb_debug(ldb, LDB_DEBUG_ERROR, "Invalid @ATTRIBUTES element for '%s'\n", msg->elements[i].name);
+ goto failed;
+ }
+ switch (flags & ~LTDB_FLAG_HIDDEN) {
+ case 0:
+ syntax = LDB_SYNTAX_OCTET_STRING;
+ break;
+ case LTDB_FLAG_CASE_INSENSITIVE:
+ syntax = LDB_SYNTAX_DIRECTORY_STRING;
+ break;
+ case LTDB_FLAG_INTEGER:
+ syntax = LDB_SYNTAX_INTEGER;
+ break;
+ default:
+ ldb_debug(ldb, LDB_DEBUG_ERROR,
+ "Invalid flag combination 0x%x for '%s' in @ATTRIBUTES\n",
+ flags, msg->elements[i].name);
+ goto failed;
+ }
+
+ s = ldb_standard_syntax_by_name(ldb, syntax);
+ if (s == NULL) {
+ ldb_debug(ldb, LDB_DEBUG_ERROR,
+ "Invalid attribute syntax '%s' for '%s' in @ATTRIBUTES\n",
+ syntax, msg->elements[i].name);
+ goto failed;
+ }
+
+ flags |= LDB_ATTR_FLAG_ALLOCATED;
+ if (ldb_schema_attribute_add_with_syntax(ldb, msg->elements[i].name, flags, s) != 0) {
+ goto failed;
+ }
+ }
+
+ return 0;
+failed:
+ return -1;
+}
+
+
+/*
+ initialise the baseinfo record
+*/
+static int ltdb_baseinfo_init(struct ldb_module *module)
+{
+ struct ldb_context *ldb;
+ void *data = ldb_module_get_private(module);
+ struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private);
+ struct ldb_message *msg;
+ struct ldb_message_element el;
+ struct ldb_val val;
+ int ret;
+ /* the initial sequence number must be different from the one
+ set in ltdb_cache_free(). Thanks to Jon for pointing this
+ out. */
+ const char *initial_sequence_number = "1";
+
+ ldb = ldb_module_get_ctx(module);
+
+ ltdb->sequence_number = atof(initial_sequence_number);
+
+ msg = talloc(ltdb, struct ldb_message);
+ if (msg == NULL) {
+ goto failed;
+ }
+
+ msg->num_elements = 1;
+ msg->elements = &el;
+ msg->dn = ldb_dn_new(msg, ldb, LTDB_BASEINFO);
+ if (!msg->dn) {
+ goto failed;
+ }
+ el.name = talloc_strdup(msg, LTDB_SEQUENCE_NUMBER);
+ if (!el.name) {
+ goto failed;
+ }
+ el.values = &val;
+ el.num_values = 1;
+ el.flags = 0;
+ val.data = (uint8_t *)talloc_strdup(msg, initial_sequence_number);
+ if (!val.data) {
+ goto failed;
+ }
+ val.length = 1;
+
+ ret = ltdb_store(module, msg, TDB_INSERT);
+
+ talloc_free(msg);
+
+ return ret;
+
+failed:
+ talloc_free(msg);
+ errno = ENOMEM;
+ return LDB_ERR_OPERATIONS_ERROR;
+}
+
+/*
+ free any cache records
+ */
+static void ltdb_cache_free(struct ldb_module *module)
+{
+ void *data = ldb_module_get_private(module);
+ struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private);
+
+ ltdb->sequence_number = 0;
+ talloc_free(ltdb->cache);
+ ltdb->cache = NULL;
+}
+
+/*
+ force a cache reload
+*/
+int ltdb_cache_reload(struct ldb_module *module)
+{
+ ltdb_attributes_unload(module);
+ ltdb_cache_free(module);
+ return ltdb_cache_load(module);
+}
+
+/*
+ load the cache records
+*/
+int ltdb_cache_load(struct ldb_module *module)
+{
+ struct ldb_context *ldb;
+ void *data = ldb_module_get_private(module);
+ struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private);
+ struct ldb_dn *baseinfo_dn = NULL, *options_dn = NULL;
+ struct ldb_dn *indexlist_dn = NULL;
+ uint64_t seq;
+ struct ldb_message *baseinfo = NULL, *options = NULL;
+ int r;
+
+ ldb = ldb_module_get_ctx(module);
+
+ /* a very fast check to avoid extra database reads */
+ if (ltdb->cache != NULL &&
+ tdb_get_seqnum(ltdb->tdb) == ltdb->tdb_seqnum) {
+ return 0;
+ }
+
+ if (ltdb->cache == NULL) {
+ ltdb->cache = talloc_zero(ltdb, struct ltdb_cache);
+ if (ltdb->cache == NULL) goto failed;
+ ltdb->cache->indexlist = talloc_zero(ltdb->cache, struct ldb_message);
+ ltdb->cache->attributes = talloc_zero(ltdb->cache, struct ldb_message);
+ if (ltdb->cache->indexlist == NULL ||
+ ltdb->cache->attributes == NULL) {
+ goto failed;
+ }
+ }
+
+ baseinfo = talloc(ltdb->cache, struct ldb_message);
+ if (baseinfo == NULL) goto failed;
+
+ baseinfo_dn = ldb_dn_new(module, ldb, LTDB_BASEINFO);
+ if (baseinfo_dn == NULL) goto failed;
+
+ r= ltdb_search_dn1(module, baseinfo_dn, baseinfo);
+ if (r != LDB_SUCCESS && r != LDB_ERR_NO_SUCH_OBJECT) {
+ goto failed;
+ }
+
+ /* possibly initialise the baseinfo */
+ if (r == LDB_ERR_NO_SUCH_OBJECT) {
+ if (ltdb_baseinfo_init(module) != LDB_SUCCESS) {
+ goto failed;
+ }
+ if (ltdb_search_dn1(module, baseinfo_dn, baseinfo) != LDB_SUCCESS) {
+ goto failed;
+ }
+ }
+
+ ltdb->tdb_seqnum = tdb_get_seqnum(ltdb->tdb);
+
+ /* if the current internal sequence number is the same as the one
+ in the database then assume the rest of the cache is OK */
+ seq = ldb_msg_find_attr_as_uint64(baseinfo, LTDB_SEQUENCE_NUMBER, 0);
+ if (seq == ltdb->sequence_number) {
+ goto done;
+ }
+ ltdb->sequence_number = seq;
+
+ /* Read an interpret database options */
+ options = talloc(ltdb->cache, struct ldb_message);
+ if (options == NULL) goto failed;
+
+ options_dn = ldb_dn_new(options, ldb, LTDB_OPTIONS);
+ if (options_dn == NULL) goto failed;
+
+ r= ltdb_search_dn1(module, options_dn, options);
+ if (r != LDB_SUCCESS && r != LDB_ERR_NO_SUCH_OBJECT) {
+ goto failed;
+ }
+
+ /* set flag for checking base DN on searches */
+ if (r == LDB_SUCCESS) {
+ ltdb->check_base = ldb_msg_find_attr_as_bool(options, LTDB_CHECK_BASE, false);
+ } else {
+ ltdb->check_base = false;
+ }
+
+ talloc_free(ltdb->cache->last_attribute.name);
+ memset(&ltdb->cache->last_attribute, 0, sizeof(ltdb->cache->last_attribute));
+
+ ltdb_attributes_unload(module);
+
+ talloc_free(ltdb->cache->indexlist);
+
+ ltdb->cache->indexlist = talloc_zero(ltdb->cache, struct ldb_message);
+ ltdb->cache->attributes = talloc_zero(ltdb->cache, struct ldb_message);
+ if (ltdb->cache->indexlist == NULL ||
+ ltdb->cache->attributes == NULL) {
+ goto failed;
+ }
+
+ indexlist_dn = ldb_dn_new(module, ldb, LTDB_INDEXLIST);
+ if (indexlist_dn == NULL) goto failed;
+
+ r = ltdb_search_dn1(module, indexlist_dn, ltdb->cache->indexlist);
+ if (r != LDB_SUCCESS && r != LDB_ERR_NO_SUCH_OBJECT) {
+ goto failed;
+ }
+
+ if (ltdb_attributes_load(module) == -1) {
+ goto failed;
+ }
+
+done:
+ talloc_free(options);
+ talloc_free(baseinfo);
+ talloc_free(baseinfo_dn);
+ talloc_free(indexlist_dn);
+ return 0;
+
+failed:
+ talloc_free(options);
+ talloc_free(baseinfo);
+ talloc_free(baseinfo_dn);
+ talloc_free(indexlist_dn);
+ return -1;
+}
+
+
+/*
+ increase the sequence number to indicate a database change
+*/
+int ltdb_increase_sequence_number(struct ldb_module *module)
+{
+ struct ldb_context *ldb;
+ void *data = ldb_module_get_private(module);
+ struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private);
+ struct ldb_message *msg;
+ struct ldb_message_element el[2];
+ struct ldb_val val;
+ struct ldb_val val_time;
+ time_t t = time(NULL);
+ char *s = NULL;
+ int ret;
+
+ ldb = ldb_module_get_ctx(module);
+
+ msg = talloc(ltdb, struct ldb_message);
+ if (msg == NULL) {
+ errno = ENOMEM;
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ s = talloc_asprintf(msg, "%llu", ltdb->sequence_number+1);
+ if (!s) {
+ errno = ENOMEM;
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ msg->num_elements = ARRAY_SIZE(el);
+ msg->elements = el;
+ msg->dn = ldb_dn_new(msg, ldb, LTDB_BASEINFO);
+ if (msg->dn == NULL) {
+ talloc_free(msg);
+ errno = ENOMEM;
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ el[0].name = talloc_strdup(msg, LTDB_SEQUENCE_NUMBER);
+ if (el[0].name == NULL) {
+ talloc_free(msg);
+ errno = ENOMEM;
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ el[0].values = &val;
+ el[0].num_values = 1;
+ el[0].flags = LDB_FLAG_MOD_REPLACE;
+ val.data = (uint8_t *)s;
+ val.length = strlen(s);
+
+ el[1].name = talloc_strdup(msg, LTDB_MOD_TIMESTAMP);
+ if (el[1].name == NULL) {
+ talloc_free(msg);
+ errno = ENOMEM;
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ el[1].values = &val_time;
+ el[1].num_values = 1;
+ el[1].flags = LDB_FLAG_MOD_REPLACE;
+
+ s = ldb_timestring(msg, t);
+ if (s == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ val_time.data = (uint8_t *)s;
+ val_time.length = strlen(s);
+
+ ret = ltdb_modify_internal(module, msg);
+
+ talloc_free(msg);
+
+ if (ret == LDB_SUCCESS) {
+ ltdb->sequence_number += 1;
+ }
+
+ /* updating the tdb_seqnum here avoids us reloading the cache
+ records due to our own modification */
+ ltdb->tdb_seqnum = tdb_get_seqnum(ltdb->tdb);
+
+ return ret;
+}
+
+int ltdb_check_at_attributes_values(const struct ldb_val *value)
+{
+ int i;
+
+ for (i = 0; ltdb_valid_attr_flags[i].name != NULL; i++) {
+ if ((strcmp(ltdb_valid_attr_flags[i].name, (char *)value->data) == 0)) {
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
diff --git a/source4/lib/ldb/ldb_tdb/ldb_index.c b/source4/lib/ldb/ldb_tdb/ldb_index.c
new file mode 100644
index 0000000000..c99c2936d8
--- /dev/null
+++ b/source4/lib/ldb/ldb_tdb/ldb_index.c
@@ -0,0 +1,1617 @@
+/*
+ ldb database library
+
+ Copyright (C) Andrew Tridgell 2004
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb tdb backend - indexing
+ *
+ * Description: indexing routines for ldb tdb backend
+ *
+ * Author: Andrew Tridgell
+ */
+
+#include "ldb_tdb.h"
+#include "dlinklist.h"
+
+/*
+ the idxptr code is a bit unusual. The way it works is to replace
+ @IDX elements in records during a transaction with @IDXPTR
+ elements. The @IDXPTR elements don't contain the actual index entry
+ values, but contain a pointer to a linked list of values.
+
+ This means we are storing pointers in a database, which is normally
+ not allowed, but in this case we are storing them only for the
+ duration of a transaction, and re-writing them into the normal @IDX
+ format at the end of the transaction. That means no other processes
+ are ever exposed to the @IDXPTR values.
+
+ The advantage is that the linked list doesn't cause huge
+ fragmentation during a transaction. Without the @IDXPTR method we
+ often ended up with a ldb that was between 10x and 100x larger then
+ it needs to be due to massive fragmentation caused by re-writing
+ @INDEX records many times during indexing.
+ */
+struct ldb_index_pointer {
+ struct ldb_index_pointer *next, *prev;
+ struct ldb_val value;
+};
+
+struct ltdb_idxptr {
+ int num_dns;
+ const char **dn_list;
+ bool repack;
+};
+
+/*
+ add to the list of DNs that need to be fixed on transaction end
+ */
+static int ltdb_idxptr_add(struct ldb_module *module, const struct ldb_message *msg)
+{
+ void *data = ldb_module_get_private(module);
+ struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private);
+ ltdb->idxptr->dn_list = talloc_realloc(ltdb->idxptr, ltdb->idxptr->dn_list,
+ const char *, ltdb->idxptr->num_dns+1);
+ if (ltdb->idxptr->dn_list == NULL) {
+ ltdb->idxptr->num_dns = 0;
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ltdb->idxptr->dn_list[ltdb->idxptr->num_dns] =
+ talloc_strdup(ltdb->idxptr->dn_list, ldb_dn_get_linearized(msg->dn));
+ if (ltdb->idxptr->dn_list[ltdb->idxptr->num_dns] == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ltdb->idxptr->num_dns++;
+ return LDB_SUCCESS;
+}
+
+/* free an idxptr record */
+static int ltdb_free_idxptr(struct ldb_module *module, struct ldb_message_element *el)
+{
+ struct ldb_val val;
+ struct ldb_index_pointer *ptr;
+
+ if (el->num_values != 1) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ val = el->values[0];
+ if (val.length != sizeof(void *)) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ptr = *(struct ldb_index_pointer **)val.data;
+ if (talloc_get_type(ptr, struct ldb_index_pointer) != ptr) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ while (ptr) {
+ struct ldb_index_pointer *tmp = ptr;
+ DLIST_REMOVE(ptr, ptr);
+ talloc_free(tmp);
+ }
+
+ return LDB_SUCCESS;
+}
+
+
+/* convert from the IDXPTR format to a ldb_message_element format */
+static int ltdb_convert_from_idxptr(struct ldb_module *module, struct ldb_message_element *el)
+{
+ struct ldb_val val;
+ struct ldb_index_pointer *ptr, *tmp;
+ int i;
+ struct ldb_val *val2;
+
+ if (el->num_values != 1) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ val = el->values[0];
+ if (val.length != sizeof(void *)) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ptr = *(struct ldb_index_pointer **)val.data;
+ if (talloc_get_type(ptr, struct ldb_index_pointer) != ptr) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* count the length of the list */
+ for (i=0, tmp = ptr; tmp; tmp=tmp->next) {
+ i++;
+ }
+
+ /* allocate the new values array */
+ val2 = talloc_realloc(NULL, el->values, struct ldb_val, i);
+ if (val2 == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ el->values = val2;
+ el->num_values = i;
+
+ /* populate the values array */
+ for (i=0, tmp = ptr; tmp; tmp=tmp->next, i++) {
+ el->values[i].length = tmp->value.length;
+ /* we need to over-allocate here as there are still some places
+ in ldb that rely on null termination. */
+ el->values[i].data = talloc_size(el->values, tmp->value.length+1);
+ if (el->values[i].data == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ memcpy(el->values[i].data, tmp->value.data, tmp->value.length);
+ el->values[i].data[tmp->value.length] = 0;
+ }
+
+ /* update the name */
+ el->name = LTDB_IDX;
+
+ return LDB_SUCCESS;
+}
+
+
+/* convert to the IDXPTR format from a ldb_message_element format */
+static int ltdb_convert_to_idxptr(struct ldb_module *module, struct ldb_message_element *el)
+{
+ struct ldb_index_pointer *ptr, *tmp;
+ int i;
+ struct ldb_val *val2;
+ void *data = ldb_module_get_private(module);
+ struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private);
+
+ ptr = NULL;
+
+ for (i=0;i<el->num_values;i++) {
+ tmp = talloc(ltdb->idxptr, struct ldb_index_pointer);
+ if (tmp == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ tmp->value = el->values[i];
+ tmp->value.data = talloc_memdup(tmp, tmp->value.data, tmp->value.length);
+ if (tmp->value.data == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ DLIST_ADD(ptr, tmp);
+ }
+
+ /* allocate the new values array */
+ val2 = talloc_realloc(NULL, el->values, struct ldb_val, 1);
+ if (val2 == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ el->values = val2;
+ el->num_values = 1;
+
+ el->values[0].data = talloc_memdup(el->values, &ptr, sizeof(ptr));
+ el->values[0].length = sizeof(ptr);
+
+ /* update the name */
+ el->name = LTDB_IDXPTR;
+
+ return LDB_SUCCESS;
+}
+
+
+/* enable the idxptr mode when transactions start */
+int ltdb_index_transaction_start(struct ldb_module *module)
+{
+ void *data = ldb_module_get_private(module);
+ struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private);
+ ltdb->idxptr = talloc_zero(module, struct ltdb_idxptr);
+ return LDB_SUCCESS;
+}
+
+/*
+ a wrapper around ltdb_search_dn1() which translates pointer based index records
+ and maps them into normal ldb message structures
+ */
+static int ltdb_search_dn1_index(struct ldb_module *module,
+ struct ldb_dn *dn, struct ldb_message *msg)
+{
+ int ret, i;
+ ret = ltdb_search_dn1(module, dn, msg);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ /* if this isn't a @INDEX record then don't munge it */
+ if (strncmp(ldb_dn_get_linearized(msg->dn), LTDB_INDEX ":", strlen(LTDB_INDEX) + 1) != 0) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ for (i=0;i<msg->num_elements;i++) {
+ struct ldb_message_element *el = &msg->elements[i];
+ if (strcmp(el->name, LTDB_IDXPTR) == 0) {
+ ret = ltdb_convert_from_idxptr(module, el);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+ }
+
+ return ret;
+}
+
+
+
+/*
+ fixup the idxptr for one DN
+ */
+static int ltdb_idxptr_fix_dn(struct ldb_module *module, const char *strdn)
+{
+ struct ldb_context *ldb;
+ struct ldb_dn *dn;
+ struct ldb_message *msg = ldb_msg_new(module);
+ int ret;
+
+ ldb = ldb_module_get_ctx(module);
+
+ dn = ldb_dn_new(msg, ldb, strdn);
+ if (ltdb_search_dn1_index(module, dn, msg) == LDB_SUCCESS) {
+ ret = ltdb_store(module, msg, TDB_REPLACE);
+ }
+ talloc_free(msg);
+ return ret;
+}
+
+/* cleanup the idxptr mode when transaction commits */
+int ltdb_index_transaction_commit(struct ldb_module *module)
+{
+ int i;
+ void *data = ldb_module_get_private(module);
+ struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private);
+
+ /* fix all the DNs that we have modified */
+ if (ltdb->idxptr) {
+ for (i=0;i<ltdb->idxptr->num_dns;i++) {
+ ltdb_idxptr_fix_dn(module, ltdb->idxptr->dn_list[i]);
+ }
+
+ if (ltdb->idxptr->repack) {
+ tdb_repack(ltdb->tdb);
+ }
+ }
+
+ talloc_free(ltdb->idxptr);
+ ltdb->idxptr = NULL;
+ return LDB_SUCCESS;
+}
+
+/* cleanup the idxptr mode when transaction cancels */
+int ltdb_index_transaction_cancel(struct ldb_module *module)
+{
+ void *data = ldb_module_get_private(module);
+ struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private);
+ talloc_free(ltdb->idxptr);
+ ltdb->idxptr = NULL;
+ return LDB_SUCCESS;
+}
+
+
+
+/* a wrapper around ltdb_store() for the index code which
+ stores in IDXPTR format when idxptr mode is enabled
+
+ WARNING: This modifies the msg which is passed in
+*/
+int ltdb_store_idxptr(struct ldb_module *module, const struct ldb_message *msg, int flgs)
+{
+ void *data = ldb_module_get_private(module);
+ struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private);
+ int ret;
+
+ if (ltdb->idxptr) {
+ int i;
+ struct ldb_message *msg2 = ldb_msg_new(module);
+
+ /* free any old pointer */
+ ret = ltdb_search_dn1(module, msg->dn, msg2);
+ if (ret == 0) {
+ for (i=0;i<msg2->num_elements;i++) {
+ struct ldb_message_element *el = &msg2->elements[i];
+ if (strcmp(el->name, LTDB_IDXPTR) == 0) {
+ ret = ltdb_free_idxptr(module, el);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+ }
+ }
+ talloc_free(msg2);
+
+ for (i=0;i<msg->num_elements;i++) {
+ struct ldb_message_element *el = &msg->elements[i];
+ if (strcmp(el->name, LTDB_IDX) == 0) {
+ ret = ltdb_convert_to_idxptr(module, el);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+ }
+
+ if (ltdb_idxptr_add(module, msg) != 0) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ }
+
+ ret = ltdb_store(module, msg, flgs);
+ return ret;
+}
+
+
+/*
+ find an element in a list, using the given comparison function and
+ assuming that the list is already sorted using comp_fn
+
+ return -1 if not found, or the index of the first occurance of needle if found
+*/
+static int ldb_list_find(const void *needle,
+ const void *base, size_t nmemb, size_t size,
+ comparison_fn_t comp_fn)
+{
+ const char *base_p = (const char *)base;
+ size_t min_i, max_i, test_i;
+
+ if (nmemb == 0) {
+ return -1;
+ }
+
+ min_i = 0;
+ max_i = nmemb-1;
+
+ while (min_i < max_i) {
+ int r;
+
+ test_i = (min_i + max_i) / 2;
+ /* the following cast looks strange, but is
+ correct. The key to understanding it is that base_p
+ is a pointer to an array of pointers, so we have to
+ dereference it after casting to void **. The strange
+ const in the middle gives us the right type of pointer
+ after the dereference (tridge) */
+ r = comp_fn(needle, *(void * const *)(base_p + (size * test_i)));
+ if (r == 0) {
+ /* scan back for first element */
+ while (test_i > 0 &&
+ comp_fn(needle, *(void * const *)(base_p + (size * (test_i-1)))) == 0) {
+ test_i--;
+ }
+ return test_i;
+ }
+ if (r < 0) {
+ if (test_i == 0) {
+ return -1;
+ }
+ max_i = test_i - 1;
+ }
+ if (r > 0) {
+ min_i = test_i + 1;
+ }
+ }
+
+ if (comp_fn(needle, *(void * const *)(base_p + (size * min_i))) == 0) {
+ return min_i;
+ }
+
+ return -1;
+}
+
+struct dn_list {
+ unsigned int count;
+ char **dn;
+};
+
+/*
+ return the dn key to be used for an index
+ caller frees
+*/
+static struct ldb_dn *ltdb_index_key(struct ldb_context *ldb,
+ const char *attr, const struct ldb_val *value)
+{
+ struct ldb_dn *ret;
+ struct ldb_val v;
+ const struct ldb_schema_attribute *a;
+ char *attr_folded;
+ int r;
+
+ attr_folded = ldb_attr_casefold(ldb, attr);
+ if (!attr_folded) {
+ return NULL;
+ }
+
+ a = ldb_schema_attribute_by_name(ldb, attr);
+ r = a->syntax->canonicalise_fn(ldb, ldb, value, &v);
+ if (r != LDB_SUCCESS) {
+ const char *errstr = ldb_errstring(ldb);
+ /* canonicalisation can be refused. For example,
+ a attribute that takes wildcards will refuse to canonicalise
+ if the value contains a wildcard */
+ ldb_asprintf_errstring(ldb, "Failed to create index key for attribute '%s':%s%s%s",
+ attr, ldb_strerror(r), (errstr?":":""), (errstr?errstr:""));
+ talloc_free(attr_folded);
+ return NULL;
+ }
+ if (ldb_should_b64_encode(&v)) {
+ char *vstr = ldb_base64_encode(ldb, (char *)v.data, v.length);
+ if (!vstr) return NULL;
+ ret = ldb_dn_new_fmt(ldb, ldb, "%s:%s::%s", LTDB_INDEX, attr_folded, vstr);
+ talloc_free(vstr);
+ } else {
+ ret = ldb_dn_new_fmt(ldb, ldb, "%s:%s:%.*s", LTDB_INDEX, attr_folded, (int)v.length, (char *)v.data);
+ }
+
+ if (v.data != value->data) {
+ talloc_free(v.data);
+ }
+ talloc_free(attr_folded);
+
+ return ret;
+}
+
+/*
+ see if a attribute value is in the list of indexed attributes
+*/
+static int ldb_msg_find_idx(const struct ldb_message *msg, const char *attr,
+ unsigned int *v_idx, const char *key)
+{
+ unsigned int i, j;
+ for (i=0;i<msg->num_elements;i++) {
+ if (ldb_attr_cmp(msg->elements[i].name, key) == 0) {
+ const struct ldb_message_element *el = &msg->elements[i];
+
+ if (attr == NULL) {
+ /* in this case we are just looking to see if key is present,
+ we are not spearching for a specific index */
+ return 0;
+ }
+
+ for (j=0;j<el->num_values;j++) {
+ if (ldb_attr_cmp((char *)el->values[j].data, attr) == 0) {
+ if (v_idx) {
+ *v_idx = j;
+ }
+ return i;
+ }
+ }
+ }
+ }
+ return -1;
+}
+
+/* used in sorting dn lists */
+static int list_cmp(const char **s1, const char **s2)
+{
+ return strcmp(*s1, *s2);
+}
+
+/*
+ return a list of dn's that might match a simple indexed search or
+ */
+static int ltdb_index_dn_simple(struct ldb_module *module,
+ const struct ldb_parse_tree *tree,
+ const struct ldb_message *index_list,
+ struct dn_list *list)
+{
+ struct ldb_context *ldb;
+ struct ldb_dn *dn;
+ int ret;
+ unsigned int i, j;
+ struct ldb_message *msg;
+
+ ldb = ldb_module_get_ctx(module);
+
+ list->count = 0;
+ list->dn = NULL;
+
+ /* if the attribute isn't in the list of indexed attributes then
+ this node needs a full search */
+ if (ldb_msg_find_idx(index_list, tree->u.equality.attr, NULL, LTDB_IDXATTR) == -1) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* the attribute is indexed. Pull the list of DNs that match the
+ search criterion */
+ dn = ltdb_index_key(ldb, tree->u.equality.attr, &tree->u.equality.value);
+ if (!dn) return LDB_ERR_OPERATIONS_ERROR;
+
+ msg = talloc(list, struct ldb_message);
+ if (msg == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ltdb_search_dn1_index(module, dn, msg);
+ talloc_free(dn);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ for (i=0;i<msg->num_elements;i++) {
+ struct ldb_message_element *el;
+
+ if (strcmp(msg->elements[i].name, LTDB_IDX) != 0) {
+ continue;
+ }
+
+ el = &msg->elements[i];
+
+ list->dn = talloc_array(list, char *, el->num_values);
+ if (!list->dn) {
+ talloc_free(msg);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ for (j=0;j<el->num_values;j++) {
+ list->dn[list->count] =
+ talloc_strdup(list->dn, (char *)el->values[j].data);
+ if (!list->dn[list->count]) {
+ talloc_free(msg);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ list->count++;
+ }
+ }
+
+ talloc_free(msg);
+
+ if (list->count > 1) {
+ qsort(list->dn, list->count, sizeof(char *), (comparison_fn_t) list_cmp);
+ }
+
+ return LDB_SUCCESS;
+}
+
+
+static int list_union(struct ldb_context *, struct dn_list *, const struct dn_list *);
+
+/*
+ return a list of dn's that might match a leaf indexed search
+ */
+static int ltdb_index_dn_leaf(struct ldb_module *module,
+ const struct ldb_parse_tree *tree,
+ const struct ldb_message *index_list,
+ struct dn_list *list)
+{
+ struct ldb_context *ldb;
+ ldb = ldb_module_get_ctx(module);
+
+ if (ldb_attr_dn(tree->u.equality.attr) == 0) {
+ list->dn = talloc_array(list, char *, 1);
+ if (list->dn == NULL) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ list->dn[0] = talloc_strdup(list->dn, (char *)tree->u.equality.value.data);
+ if (list->dn[0] == NULL) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ list->count = 1;
+ return LDB_SUCCESS;
+ }
+ return ltdb_index_dn_simple(module, tree, index_list, list);
+}
+
+
+/*
+ list intersection
+ list = list & list2
+ relies on the lists being sorted
+*/
+static int list_intersect(struct ldb_context *ldb,
+ struct dn_list *list, const struct dn_list *list2)
+{
+ struct dn_list *list3;
+ unsigned int i;
+
+ if (list->count == 0 || list2->count == 0) {
+ /* 0 & X == 0 */
+ return LDB_ERR_NO_SUCH_OBJECT;
+ }
+
+ list3 = talloc(ldb, struct dn_list);
+ if (list3 == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ list3->dn = talloc_array(list3, char *, list->count);
+ if (!list3->dn) {
+ talloc_free(list3);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ list3->count = 0;
+
+ for (i=0;i<list->count;i++) {
+ if (ldb_list_find(list->dn[i], list2->dn, list2->count,
+ sizeof(char *), (comparison_fn_t)strcmp) != -1) {
+ list3->dn[list3->count] = talloc_move(list3->dn, &list->dn[i]);
+ list3->count++;
+ } else {
+ talloc_free(list->dn[i]);
+ }
+ }
+
+ talloc_free(list->dn);
+ list->dn = talloc_move(list, &list3->dn);
+ list->count = list3->count;
+ talloc_free(list3);
+
+ return LDB_ERR_NO_SUCH_OBJECT;
+}
+
+
+/*
+ list union
+ list = list | list2
+ relies on the lists being sorted
+*/
+static int list_union(struct ldb_context *ldb,
+ struct dn_list *list, const struct dn_list *list2)
+{
+ unsigned int i;
+ char **d;
+ unsigned int count = list->count;
+
+ if (list->count == 0 && list2->count == 0) {
+ /* 0 | 0 == 0 */
+ return LDB_ERR_NO_SUCH_OBJECT;
+ }
+
+ d = talloc_realloc(list, list->dn, char *, list->count + list2->count);
+ if (!d) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ list->dn = d;
+
+ for (i=0;i<list2->count;i++) {
+ if (ldb_list_find(list2->dn[i], list->dn, count,
+ sizeof(char *), (comparison_fn_t)strcmp) == -1) {
+ list->dn[list->count] = talloc_strdup(list->dn, list2->dn[i]);
+ if (!list->dn[list->count]) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ list->count++;
+ }
+ }
+
+ if (list->count != count) {
+ qsort(list->dn, list->count, sizeof(char *), (comparison_fn_t)list_cmp);
+ }
+
+ return LDB_ERR_NO_SUCH_OBJECT;
+}
+
+static int ltdb_index_dn(struct ldb_module *module,
+ const struct ldb_parse_tree *tree,
+ const struct ldb_message *index_list,
+ struct dn_list *list);
+
+
+/*
+ OR two index results
+ */
+static int ltdb_index_dn_or(struct ldb_module *module,
+ const struct ldb_parse_tree *tree,
+ const struct ldb_message *index_list,
+ struct dn_list *list)
+{
+ struct ldb_context *ldb;
+ unsigned int i;
+ int ret;
+
+ ldb = ldb_module_get_ctx(module);
+
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ list->dn = NULL;
+ list->count = 0;
+
+ for (i=0;i<tree->u.list.num_elements;i++) {
+ struct dn_list *list2;
+ int v;
+
+ list2 = talloc(module, struct dn_list);
+ if (list2 == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ v = ltdb_index_dn(module, tree->u.list.elements[i], index_list, list2);
+
+ if (v == LDB_ERR_NO_SUCH_OBJECT) {
+ /* 0 || X == X */
+ if (ret != LDB_SUCCESS && ret != LDB_ERR_NO_SUCH_OBJECT) {
+ ret = v;
+ }
+ talloc_free(list2);
+ continue;
+ }
+
+ if (v != LDB_SUCCESS && v != LDB_ERR_NO_SUCH_OBJECT) {
+ /* 1 || X == 1 */
+ talloc_free(list->dn);
+ talloc_free(list2);
+ return v;
+ }
+
+ if (ret != LDB_SUCCESS && ret != LDB_ERR_NO_SUCH_OBJECT) {
+ ret = LDB_SUCCESS;
+ list->dn = talloc_move(list, &list2->dn);
+ list->count = list2->count;
+ } else {
+ if (list_union(ldb, list, list2) == -1) {
+ talloc_free(list2);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ret = LDB_SUCCESS;
+ }
+ talloc_free(list2);
+ }
+
+ if (list->count == 0) {
+ return LDB_ERR_NO_SUCH_OBJECT;
+ }
+
+ return ret;
+}
+
+
+/*
+ NOT an index results
+ */
+static int ltdb_index_dn_not(struct ldb_module *module,
+ const struct ldb_parse_tree *tree,
+ const struct ldb_message *index_list,
+ struct dn_list *list)
+{
+ /* the only way to do an indexed not would be if we could
+ negate the not via another not or if we knew the total
+ number of database elements so we could know that the
+ existing expression covered the whole database.
+
+ instead, we just give up, and rely on a full index scan
+ (unless an outer & manages to reduce the list)
+ */
+ return LDB_ERR_OPERATIONS_ERROR;
+}
+
+/*
+ AND two index results
+ */
+static int ltdb_index_dn_and(struct ldb_module *module,
+ const struct ldb_parse_tree *tree,
+ const struct ldb_message *index_list,
+ struct dn_list *list)
+{
+ struct ldb_context *ldb;
+ unsigned int i;
+ int ret;
+
+ ldb = ldb_module_get_ctx(module);
+
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ list->dn = NULL;
+ list->count = 0;
+
+ for (i=0;i<tree->u.list.num_elements;i++) {
+ struct dn_list *list2;
+ int v;
+
+ list2 = talloc(module, struct dn_list);
+ if (list2 == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ v = ltdb_index_dn(module, tree->u.list.elements[i], index_list, list2);
+
+ if (v == LDB_ERR_NO_SUCH_OBJECT) {
+ /* 0 && X == 0 */
+ talloc_free(list->dn);
+ talloc_free(list2);
+ return LDB_ERR_NO_SUCH_OBJECT;
+ }
+
+ if (v != LDB_SUCCESS && v != LDB_ERR_NO_SUCH_OBJECT) {
+ talloc_free(list2);
+ continue;
+ }
+
+ if (ret != LDB_SUCCESS && ret != LDB_ERR_NO_SUCH_OBJECT) {
+ ret = LDB_SUCCESS;
+ talloc_free(list->dn);
+ list->dn = talloc_move(list, &list2->dn);
+ list->count = list2->count;
+ } else {
+ if (list_intersect(ldb, list, list2) == -1) {
+ talloc_free(list2);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ }
+
+ talloc_free(list2);
+
+ if (list->count == 0) {
+ talloc_free(list->dn);
+ return LDB_ERR_NO_SUCH_OBJECT;
+ }
+ }
+
+ return ret;
+}
+
+/*
+ AND index results and ONE level special index
+ */
+static int ltdb_index_dn_one(struct ldb_module *module,
+ struct ldb_dn *parent_dn,
+ struct dn_list *list)
+{
+ struct ldb_context *ldb;
+ struct dn_list *list2;
+ struct ldb_message *msg;
+ struct ldb_dn *key;
+ struct ldb_val val;
+ unsigned int i, j;
+ int ret;
+
+ ldb = ldb_module_get_ctx(module);
+
+ list2 = talloc_zero(module, struct dn_list);
+ if (list2 == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* the attribute is indexed. Pull the list of DNs that match the
+ search criterion */
+ val.data = (uint8_t *)((uintptr_t)ldb_dn_get_casefold(parent_dn));
+ val.length = strlen((char *)val.data);
+ key = ltdb_index_key(ldb, LTDB_IDXONE, &val);
+ if (!key) {
+ talloc_free(list2);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ msg = talloc(list2, struct ldb_message);
+ if (msg == NULL) {
+ talloc_free(list2);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ltdb_search_dn1_index(module, key, msg);
+ talloc_free(key);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ for (i = 0; i < msg->num_elements; i++) {
+ struct ldb_message_element *el;
+
+ if (strcmp(msg->elements[i].name, LTDB_IDX) != 0) {
+ continue;
+ }
+
+ el = &msg->elements[i];
+
+ list2->dn = talloc_array(list2, char *, el->num_values);
+ if (!list2->dn) {
+ talloc_free(list2);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ for (j = 0; j < el->num_values; j++) {
+ list2->dn[list2->count] = talloc_strdup(list2->dn, (char *)el->values[j].data);
+ if (!list2->dn[list2->count]) {
+ talloc_free(list2);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ list2->count++;
+ }
+ }
+
+ if (list2->count == 0) {
+ talloc_free(list2);
+ return LDB_ERR_NO_SUCH_OBJECT;
+ }
+
+ if (list2->count > 1) {
+ qsort(list2->dn, list2->count, sizeof(char *), (comparison_fn_t) list_cmp);
+ }
+
+ if (list->count > 0) {
+ if (list_intersect(ldb, list, list2) == -1) {
+ talloc_free(list2);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if (list->count == 0) {
+ talloc_free(list->dn);
+ talloc_free(list2);
+ return LDB_ERR_NO_SUCH_OBJECT;
+ }
+ } else {
+ list->dn = talloc_move(list, &list2->dn);
+ list->count = list2->count;
+ }
+
+ talloc_free(list2);
+
+ return LDB_SUCCESS;
+}
+
+/*
+ return a list of dn's that might match a indexed search or
+ an error. return LDB_ERR_NO_SUCH_OBJECT for no matches, or LDB_SUCCESS for matches
+ */
+static int ltdb_index_dn(struct ldb_module *module,
+ const struct ldb_parse_tree *tree,
+ const struct ldb_message *index_list,
+ struct dn_list *list)
+{
+ int ret = LDB_ERR_OPERATIONS_ERROR;
+
+ switch (tree->operation) {
+ case LDB_OP_AND:
+ ret = ltdb_index_dn_and(module, tree, index_list, list);
+ break;
+
+ case LDB_OP_OR:
+ ret = ltdb_index_dn_or(module, tree, index_list, list);
+ break;
+
+ case LDB_OP_NOT:
+ ret = ltdb_index_dn_not(module, tree, index_list, list);
+ break;
+
+ case LDB_OP_EQUALITY:
+ ret = ltdb_index_dn_leaf(module, tree, index_list, list);
+ break;
+
+ case LDB_OP_SUBSTRING:
+ case LDB_OP_GREATER:
+ case LDB_OP_LESS:
+ case LDB_OP_PRESENT:
+ case LDB_OP_APPROX:
+ case LDB_OP_EXTENDED:
+ /* we can't index with fancy bitops yet */
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ break;
+ }
+
+ return ret;
+}
+
+/*
+ filter a candidate dn_list from an indexed search into a set of results
+ extracting just the given attributes
+*/
+static int ltdb_index_filter(const struct dn_list *dn_list,
+ struct ltdb_context *ac)
+{
+ struct ldb_context *ldb;
+ struct ldb_message *msg;
+ unsigned int i;
+
+ ldb = ldb_module_get_ctx(ac->module);
+
+ for (i = 0; i < dn_list->count; i++) {
+ struct ldb_dn *dn;
+ int ret;
+
+ msg = ldb_msg_new(ac);
+ if (!msg) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ dn = ldb_dn_new(msg, ldb, dn_list->dn[i]);
+ if (dn == NULL) {
+ talloc_free(msg);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ltdb_search_dn1(ac->module, dn, msg);
+ talloc_free(dn);
+ if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+ /* the record has disappeared? yes, this can happen */
+ talloc_free(msg);
+ continue;
+ }
+
+ if (ret != LDB_SUCCESS && ret != LDB_ERR_NO_SUCH_OBJECT) {
+ /* an internal error */
+ talloc_free(msg);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if (!ldb_match_msg(ldb, msg,
+ ac->tree, ac->base, ac->scope)) {
+ talloc_free(msg);
+ continue;
+ }
+
+ /* filter the attributes that the user wants */
+ ret = ltdb_filter_attrs(msg, ac->attrs);
+
+ if (ret == -1) {
+ talloc_free(msg);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ldb_module_send_entry(ac->req, msg, NULL);
+ if (ret != LDB_SUCCESS) {
+ ac->request_terminated = true;
+ return ret;
+ }
+ }
+
+ return LDB_SUCCESS;
+}
+
+/*
+ search the database with a LDAP-like expression using indexes
+ returns -1 if an indexed search is not possible, in which
+ case the caller should call ltdb_search_full()
+*/
+int ltdb_search_indexed(struct ltdb_context *ac)
+{
+ struct ldb_context *ldb;
+ void *data = ldb_module_get_private(ac->module);
+ struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private);
+ struct dn_list *dn_list;
+ int ret, idxattr, idxone;
+
+ ldb = ldb_module_get_ctx(ac->module);
+
+ idxattr = idxone = 0;
+ ret = ldb_msg_find_idx(ltdb->cache->indexlist, NULL, NULL, LTDB_IDXATTR);
+ if (ret == 0 ) {
+ idxattr = 1;
+ }
+
+ /* We do one level indexing only if requested */
+ ret = ldb_msg_find_idx(ltdb->cache->indexlist, NULL, NULL, LTDB_IDXONE);
+ if (ret == 0 ) {
+ idxone = 1;
+ }
+
+ if ((ac->scope == LDB_SCOPE_ONELEVEL && (idxattr+idxone == 0)) ||
+ (ac->scope == LDB_SCOPE_SUBTREE && idxattr == 0)) {
+ /* no indexes? must do full search */
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = LDB_ERR_OPERATIONS_ERROR;
+
+ dn_list = talloc_zero(ac, struct dn_list);
+ if (dn_list == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if (ac->scope == LDB_SCOPE_BASE) {
+ /* with BASE searches only one DN can match */
+ dn_list->dn = talloc_array(dn_list, char *, 1);
+ if (dn_list->dn == NULL) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ dn_list->dn[0] = ldb_dn_alloc_linearized(dn_list, ac->base);
+ if (dn_list->dn[0] == NULL) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ dn_list->count = 1;
+ ret = LDB_SUCCESS;
+ }
+
+ if (ac->scope != LDB_SCOPE_BASE && idxattr == 1) {
+ ret = ltdb_index_dn(ac->module, ac->tree, ltdb->cache->indexlist, dn_list);
+
+ if (ret != LDB_SUCCESS && ret != LDB_ERR_NO_SUCH_OBJECT) {
+ talloc_free(dn_list);
+ return ret;
+ }
+ }
+
+ if (ac->scope == LDB_SCOPE_ONELEVEL && idxone == 1) {
+ ret = ltdb_index_dn_one(ac->module, ac->base, dn_list);
+ }
+
+ if (ret == LDB_SUCCESS) {
+ /* we've got a candidate list - now filter by the full tree
+ and extract the needed attributes */
+ ret = ltdb_index_filter(dn_list, ac);
+ }
+
+ talloc_free(dn_list);
+
+ return ret;
+}
+
+/*
+ add a index element where this is the first indexed DN for this value
+*/
+static int ltdb_index_add1_new(struct ldb_context *ldb,
+ struct ldb_message *msg,
+ const char *dn)
+{
+ struct ldb_message_element *el;
+
+ /* add another entry */
+ el = talloc_realloc(msg, msg->elements,
+ struct ldb_message_element, msg->num_elements+1);
+ if (!el) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ msg->elements = el;
+ msg->elements[msg->num_elements].name = talloc_strdup(msg->elements, LTDB_IDX);
+ if (!msg->elements[msg->num_elements].name) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ msg->elements[msg->num_elements].num_values = 0;
+ msg->elements[msg->num_elements].values = talloc(msg->elements, struct ldb_val);
+ if (!msg->elements[msg->num_elements].values) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ msg->elements[msg->num_elements].values[0].length = strlen(dn);
+ msg->elements[msg->num_elements].values[0].data = discard_const_p(uint8_t, dn);
+ msg->elements[msg->num_elements].num_values = 1;
+ msg->num_elements++;
+
+ return LDB_SUCCESS;
+}
+
+
+/*
+ add a index element where this is not the first indexed DN for this
+ value
+*/
+static int ltdb_index_add1_add(struct ldb_context *ldb,
+ struct ldb_message *msg,
+ int idx,
+ const char *dn)
+{
+ struct ldb_val *v2;
+ unsigned int i;
+
+ /* for multi-valued attributes we can end up with repeats */
+ for (i=0;i<msg->elements[idx].num_values;i++) {
+ if (strcmp(dn, (char *)msg->elements[idx].values[i].data) == 0) {
+ return LDB_SUCCESS;
+ }
+ }
+
+ v2 = talloc_realloc(msg->elements, msg->elements[idx].values,
+ struct ldb_val,
+ msg->elements[idx].num_values+1);
+ if (!v2) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ msg->elements[idx].values = v2;
+
+ msg->elements[idx].values[msg->elements[idx].num_values].length = strlen(dn);
+ msg->elements[idx].values[msg->elements[idx].num_values].data = discard_const_p(uint8_t, dn);
+ msg->elements[idx].num_values++;
+
+ return LDB_SUCCESS;
+}
+
+/*
+ add an index entry for one message element
+*/
+static int ltdb_index_add1(struct ldb_module *module, const char *dn,
+ struct ldb_message_element *el, int v_idx)
+{
+ struct ldb_context *ldb;
+ struct ldb_message *msg;
+ struct ldb_dn *dn_key;
+ int ret;
+ unsigned int i;
+
+ ldb = ldb_module_get_ctx(module);
+
+ msg = talloc(module, struct ldb_message);
+ if (msg == NULL) {
+ errno = ENOMEM;
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ dn_key = ltdb_index_key(ldb, el->name, &el->values[v_idx]);
+ if (!dn_key) {
+ talloc_free(msg);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ talloc_steal(msg, dn_key);
+
+ ret = ltdb_search_dn1_index(module, dn_key, msg);
+ if (ret != LDB_SUCCESS && ret != LDB_ERR_NO_SUCH_OBJECT) {
+ talloc_free(msg);
+ return ret;
+ }
+
+ if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+ msg->dn = dn_key;
+ msg->num_elements = 0;
+ msg->elements = NULL;
+ }
+
+ for (i=0;i<msg->num_elements;i++) {
+ if (strcmp(LTDB_IDX, msg->elements[i].name) == 0) {
+ break;
+ }
+ }
+
+ if (i == msg->num_elements) {
+ ret = ltdb_index_add1_new(ldb, msg, dn);
+ } else {
+ ret = ltdb_index_add1_add(ldb, msg, i, dn);
+ }
+
+ if (ret == LDB_SUCCESS) {
+ ret = ltdb_store_idxptr(module, msg, TDB_REPLACE);
+ }
+
+ talloc_free(msg);
+
+ return ret;
+}
+
+static int ltdb_index_add0(struct ldb_module *module, const char *dn,
+ struct ldb_message_element *elements, int num_el)
+{
+ void *data = ldb_module_get_private(module);
+ struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private);
+ int ret;
+ unsigned int i, j;
+
+ if (dn[0] == '@') {
+ return LDB_SUCCESS;
+ }
+
+ if (ltdb->cache->indexlist->num_elements == 0) {
+ /* no indexed fields */
+ return LDB_SUCCESS;
+ }
+
+ for (i = 0; i < num_el; i++) {
+ ret = ldb_msg_find_idx(ltdb->cache->indexlist, elements[i].name,
+ NULL, LTDB_IDXATTR);
+ if (ret == -1) {
+ continue;
+ }
+ for (j = 0; j < elements[i].num_values; j++) {
+ ret = ltdb_index_add1(module, dn, &elements[i], j);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+ }
+
+ return LDB_SUCCESS;
+}
+
+/*
+ add the index entries for a new record
+*/
+int ltdb_index_add(struct ldb_module *module, const struct ldb_message *msg)
+{
+ const char *dn;
+ int ret;
+
+ dn = ldb_dn_get_linearized(msg->dn);
+ if (dn == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ltdb_index_add0(module, dn, msg->elements, msg->num_elements);
+
+ return ret;
+}
+
+
+/*
+ delete an index entry for one message element
+*/
+int ltdb_index_del_value(struct ldb_module *module, const char *dn,
+ struct ldb_message_element *el, int v_idx)
+{
+ struct ldb_context *ldb;
+ struct ldb_message *msg;
+ struct ldb_dn *dn_key;
+ int ret, i;
+ unsigned int j;
+
+ ldb = ldb_module_get_ctx(module);
+
+ if (dn[0] == '@') {
+ return LDB_SUCCESS;
+ }
+
+ dn_key = ltdb_index_key(ldb, el->name, &el->values[v_idx]);
+ if (!dn_key) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ msg = talloc(dn_key, struct ldb_message);
+ if (msg == NULL) {
+ talloc_free(dn_key);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ltdb_search_dn1_index(module, dn_key, msg);
+ if (ret != LDB_SUCCESS && ret != LDB_ERR_NO_SUCH_OBJECT) {
+ talloc_free(dn_key);
+ return ret;
+ }
+
+ if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+ /* it wasn't indexed. Did we have an earlier error? If we did then
+ its gone now */
+ talloc_free(dn_key);
+ return LDB_SUCCESS;
+ }
+
+ i = ldb_msg_find_idx(msg, dn, &j, LTDB_IDX);
+ if (i == -1) {
+ struct ldb_ldif ldif;
+
+ ldb_debug(ldb, LDB_DEBUG_ERROR,
+ "ERROR: dn %s not found in %s\n", dn,
+ ldb_dn_get_linearized(dn_key));
+ ldif.changetype = LDB_CHANGETYPE_NONE;
+ ldif.msg = msg;
+ ldb_ldif_write_file(ldb, stdout, &ldif);
+ sleep(100);
+ /* it ain't there. hmmm */
+ talloc_free(dn_key);
+ return LDB_SUCCESS;
+ }
+
+ if (j != msg->elements[i].num_values - 1) {
+ memmove(&msg->elements[i].values[j],
+ &msg->elements[i].values[j+1],
+ (msg->elements[i].num_values-(j+1)) *
+ sizeof(msg->elements[i].values[0]));
+ }
+ msg->elements[i].num_values--;
+
+ if (msg->elements[i].num_values == 0) {
+ ret = ltdb_delete_noindex(module, dn_key);
+ } else {
+ ret = ltdb_store_idxptr(module, msg, TDB_REPLACE);
+ }
+
+ talloc_free(dn_key);
+
+ return ret;
+}
+
+/*
+ delete the index entries for a record
+ return -1 on failure
+*/
+int ltdb_index_del(struct ldb_module *module, const struct ldb_message *msg)
+{
+ void *data = ldb_module_get_private(module);
+ struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private);
+ int ret;
+ const char *dn;
+ unsigned int i, j;
+
+ /* find the list of indexed fields */
+ if (ltdb->cache->indexlist->num_elements == 0) {
+ /* no indexed fields */
+ return LDB_SUCCESS;
+ }
+
+ if (ldb_dn_is_special(msg->dn)) {
+ return LDB_SUCCESS;
+ }
+
+ dn = ldb_dn_get_linearized(msg->dn);
+ if (dn == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ for (i = 0; i < msg->num_elements; i++) {
+ ret = ldb_msg_find_idx(ltdb->cache->indexlist, msg->elements[i].name,
+ NULL, LTDB_IDXATTR);
+ if (ret == -1) {
+ continue;
+ }
+ for (j = 0; j < msg->elements[i].num_values; j++) {
+ ret = ltdb_index_del_value(module, dn, &msg->elements[i], j);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+ }
+
+ return LDB_SUCCESS;
+}
+
+/*
+ handle special index for one level searches
+*/
+int ltdb_index_one(struct ldb_module *module, const struct ldb_message *msg, int add)
+{
+ void *data = ldb_module_get_private(module);
+ struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private);
+ struct ldb_message_element el;
+ struct ldb_val val;
+ struct ldb_dn *pdn;
+ const char *dn;
+ int ret;
+
+ /* We index for ONE Level only if requested */
+ ret = ldb_msg_find_idx(ltdb->cache->indexlist, NULL, NULL, LTDB_IDXONE);
+ if (ret != 0) {
+ return LDB_SUCCESS;
+ }
+
+ pdn = ldb_dn_get_parent(module, msg->dn);
+ if (pdn == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ dn = ldb_dn_get_linearized(msg->dn);
+ if (dn == NULL) {
+ talloc_free(pdn);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ val.data = (uint8_t *)((uintptr_t)ldb_dn_get_casefold(pdn));
+ if (val.data == NULL) {
+ talloc_free(pdn);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ val.length = strlen((char *)val.data);
+ el.name = LTDB_IDXONE;
+ el.values = &val;
+ el.num_values = 1;
+
+ if (add) {
+ ret = ltdb_index_add1(module, dn, &el, 0);
+ } else { /* delete */
+ ret = ltdb_index_del_value(module, dn, &el, 0);
+ }
+
+ talloc_free(pdn);
+
+ return ret;
+}
+
+
+/*
+ traversal function that deletes all @INDEX records
+*/
+static int delete_index(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data, void *state)
+{
+ const char *dn = "DN=" LTDB_INDEX ":";
+ if (strncmp((char *)key.dptr, dn, strlen(dn)) == 0) {
+ return tdb_delete(tdb, key);
+ }
+ return 0;
+}
+
+/*
+ traversal function that adds @INDEX records during a re index
+*/
+static int re_index(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data, void *state)
+{
+ struct ldb_context *ldb;
+ struct ldb_module *module = (struct ldb_module *)state;
+ struct ldb_message *msg;
+ const char *dn = NULL;
+ int ret;
+ TDB_DATA key2;
+
+ ldb = ldb_module_get_ctx(module);
+
+ if (strncmp((char *)key.dptr, "DN=@", 4) == 0 ||
+ strncmp((char *)key.dptr, "DN=", 3) != 0) {
+ return 0;
+ }
+
+ msg = talloc(module, struct ldb_message);
+ if (msg == NULL) {
+ return -1;
+ }
+
+ ret = ltdb_unpack_data(module, &data, msg);
+ if (ret != 0) {
+ talloc_free(msg);
+ return -1;
+ }
+
+ /* check if the DN key has changed, perhaps due to the
+ case insensitivity of an element changing */
+ key2 = ltdb_key(module, msg->dn);
+ if (key2.dptr == NULL) {
+ /* probably a corrupt record ... darn */
+ ldb_debug(ldb, LDB_DEBUG_ERROR, "Invalid DN in re_index: %s\n",
+ ldb_dn_get_linearized(msg->dn));
+ talloc_free(msg);
+ return 0;
+ }
+ if (strcmp((char *)key2.dptr, (char *)key.dptr) != 0) {
+ tdb_delete(tdb, key);
+ tdb_store(tdb, key2, data, 0);
+ }
+ talloc_free(key2.dptr);
+
+ if (msg->dn == NULL) {
+ dn = (char *)key.dptr + 3;
+ } else {
+ dn = ldb_dn_get_linearized(msg->dn);
+ }
+
+ ret = ltdb_index_one(module, msg, 1);
+ if (ret == LDB_SUCCESS) {
+ ret = ltdb_index_add0(module, dn, msg->elements, msg->num_elements);
+ } else {
+ ldb_debug(ldb, LDB_DEBUG_ERROR,
+ "Adding special ONE LEVEL index failed (%s)!\n",
+ ldb_dn_get_linearized(msg->dn));
+ }
+
+ talloc_free(msg);
+
+ if (ret != LDB_SUCCESS) return -1;
+
+ return 0;
+}
+
+/*
+ force a complete reindex of the database
+*/
+int ltdb_reindex(struct ldb_module *module)
+{
+ void *data = ldb_module_get_private(module);
+ struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private);
+ int ret;
+
+ if (ltdb_cache_reload(module) != 0) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* first traverse the database deleting any @INDEX records */
+ ret = tdb_traverse(ltdb->tdb, delete_index, NULL);
+ if (ret == -1) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* if we don't have indexes we have nothing todo */
+ if (ltdb->cache->indexlist->num_elements == 0) {
+ return LDB_SUCCESS;
+ }
+
+ /* now traverse adding any indexes for normal LDB records */
+ ret = tdb_traverse(ltdb->tdb, re_index, module);
+ if (ret == -1) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if (ltdb->idxptr) {
+ ltdb->idxptr->repack = true;
+ }
+
+ return LDB_SUCCESS;
+}
diff --git a/source4/lib/ldb/ldb_tdb/ldb_pack.c b/source4/lib/ldb/ldb_tdb/ldb_pack.c
new file mode 100644
index 0000000000..1995606f88
--- /dev/null
+++ b/source4/lib/ldb/ldb_tdb/ldb_pack.c
@@ -0,0 +1,291 @@
+/*
+ ldb database library
+
+ Copyright (C) Andrew Tridgell 2004
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb pack/unpack
+ *
+ * Description: pack/unpack routines for ldb messages as key/value blobs
+ *
+ * Author: Andrew Tridgell
+ */
+
+#include "ldb_tdb.h"
+
+/* change this if the data format ever changes */
+#define LTDB_PACKING_FORMAT 0x26011967
+
+/* old packing formats */
+#define LTDB_PACKING_FORMAT_NODN 0x26011966
+
+/* use a portable integer format */
+static void put_uint32(uint8_t *p, int ofs, unsigned int val)
+{
+ p += ofs;
+ p[0] = val&0xFF;
+ p[1] = (val>>8) & 0xFF;
+ p[2] = (val>>16) & 0xFF;
+ p[3] = (val>>24) & 0xFF;
+}
+
+static unsigned int pull_uint32(uint8_t *p, int ofs)
+{
+ p += ofs;
+ return p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24);
+}
+
+static int attribute_storable_values(const struct ldb_message_element *el)
+{
+ if (el->num_values == 0) return 0;
+
+ if (ldb_attr_cmp(el->name, "dn") == 0) return 0;
+
+ if (ldb_attr_cmp(el->name, "distinguishedName") == 0) return 0;
+
+ return el->num_values;
+}
+
+/*
+ pack a ldb message into a linear buffer in a TDB_DATA
+
+ note that this routine avoids saving elements with zero values,
+ as these are equivalent to having no element
+
+ caller frees the data buffer after use
+*/
+int ltdb_pack_data(struct ldb_module *module,
+ const struct ldb_message *message,
+ struct TDB_DATA *data)
+{
+ struct ldb_context *ldb;
+ unsigned int i, j, real_elements=0;
+ size_t size;
+ const char *dn;
+ uint8_t *p;
+ size_t len;
+
+ ldb = ldb_module_get_ctx(module);
+
+ dn = ldb_dn_get_linearized(message->dn);
+ if (dn == NULL) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ /* work out how big it needs to be */
+ size = 8;
+
+ size += 1 + strlen(dn);
+
+ for (i=0;i<message->num_elements;i++) {
+ if (attribute_storable_values(&message->elements[i]) == 0) {
+ continue;
+ }
+
+ real_elements++;
+
+ size += 1 + strlen(message->elements[i].name) + 4;
+ for (j=0;j<message->elements[i].num_values;j++) {
+ size += 4 + message->elements[i].values[j].length + 1;
+ }
+ }
+
+ /* allocate it */
+ data->dptr = talloc_array(ldb, uint8_t, size);
+ if (!data->dptr) {
+ errno = ENOMEM;
+ return -1;
+ }
+ data->dsize = size;
+
+ p = data->dptr;
+ put_uint32(p, 0, LTDB_PACKING_FORMAT);
+ put_uint32(p, 4, real_elements);
+ p += 8;
+
+ /* the dn needs to be packed so we can be case preserving
+ while hashing on a case folded dn */
+ len = strlen(dn);
+ memcpy(p, dn, len+1);
+ p += len + 1;
+
+ for (i=0;i<message->num_elements;i++) {
+ if (attribute_storable_values(&message->elements[i]) == 0) {
+ continue;
+ }
+ len = strlen(message->elements[i].name);
+ memcpy(p, message->elements[i].name, len+1);
+ p += len + 1;
+ put_uint32(p, 0, message->elements[i].num_values);
+ p += 4;
+ for (j=0;j<message->elements[i].num_values;j++) {
+ put_uint32(p, 0, message->elements[i].values[j].length);
+ memcpy(p+4, message->elements[i].values[j].data,
+ message->elements[i].values[j].length);
+ p[4+message->elements[i].values[j].length] = 0;
+ p += 4 + message->elements[i].values[j].length + 1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ unpack a ldb message from a linear buffer in TDB_DATA
+
+ Free with ltdb_unpack_data_free()
+*/
+int ltdb_unpack_data(struct ldb_module *module,
+ const struct TDB_DATA *data,
+ struct ldb_message *message)
+{
+ struct ldb_context *ldb;
+ uint8_t *p;
+ unsigned int remaining;
+ unsigned int i, j;
+ unsigned format;
+ size_t len;
+
+ ldb = ldb_module_get_ctx(module);
+ message->elements = NULL;
+
+ p = data->dptr;
+ if (data->dsize < 8) {
+ errno = EIO;
+ goto failed;
+ }
+
+ format = pull_uint32(p, 0);
+ message->num_elements = pull_uint32(p, 4);
+ p += 8;
+
+ remaining = data->dsize - 8;
+
+ switch (format) {
+ case LTDB_PACKING_FORMAT_NODN:
+ message->dn = NULL;
+ break;
+
+ case LTDB_PACKING_FORMAT:
+ len = strnlen((char *)p, remaining);
+ if (len == remaining) {
+ errno = EIO;
+ goto failed;
+ }
+ message->dn = ldb_dn_new(message, ldb, (char *)p);
+ if (message->dn == NULL) {
+ errno = ENOMEM;
+ goto failed;
+ }
+ remaining -= len + 1;
+ p += len + 1;
+ break;
+
+ default:
+ errno = EIO;
+ goto failed;
+ }
+
+ if (message->num_elements == 0) {
+ message->elements = NULL;
+ return 0;
+ }
+
+ if (message->num_elements > remaining / 6) {
+ errno = EIO;
+ goto failed;
+ }
+
+ message->elements = talloc_array(message, struct ldb_message_element, message->num_elements);
+ if (!message->elements) {
+ errno = ENOMEM;
+ goto failed;
+ }
+
+ memset(message->elements, 0,
+ message->num_elements * sizeof(struct ldb_message_element));
+
+ for (i=0;i<message->num_elements;i++) {
+ if (remaining < 10) {
+ errno = EIO;
+ goto failed;
+ }
+ len = strnlen((char *)p, remaining-6);
+ if (len == remaining-6) {
+ errno = EIO;
+ goto failed;
+ }
+ message->elements[i].flags = 0;
+ message->elements[i].name = talloc_strndup(message->elements, (char *)p, len);
+ if (message->elements[i].name == NULL) {
+ errno = ENOMEM;
+ goto failed;
+ }
+ remaining -= len + 1;
+ p += len + 1;
+ message->elements[i].num_values = pull_uint32(p, 0);
+ message->elements[i].values = NULL;
+ if (message->elements[i].num_values != 0) {
+ message->elements[i].values = talloc_array(message->elements,
+ struct ldb_val,
+ message->elements[i].num_values);
+ if (!message->elements[i].values) {
+ errno = ENOMEM;
+ goto failed;
+ }
+ }
+ p += 4;
+ remaining -= 4;
+ for (j=0;j<message->elements[i].num_values;j++) {
+ len = pull_uint32(p, 0);
+ if (len > remaining-5) {
+ errno = EIO;
+ goto failed;
+ }
+
+ message->elements[i].values[j].length = len;
+ message->elements[i].values[j].data = talloc_size(message->elements[i].values, len+1);
+ if (message->elements[i].values[j].data == NULL) {
+ errno = ENOMEM;
+ goto failed;
+ }
+ memcpy(message->elements[i].values[j].data, p+4, len);
+ message->elements[i].values[j].data[len] = 0;
+
+ remaining -= len+4+1;
+ p += len+4+1;
+ }
+ }
+
+ if (remaining != 0) {
+ ldb_debug(ldb, LDB_DEBUG_ERROR,
+ "Error: %d bytes unread in ltdb_unpack_data\n", remaining);
+ }
+
+ return 0;
+
+failed:
+ talloc_free(message->elements);
+ return -1;
+}
diff --git a/source4/lib/ldb/ldb_tdb/ldb_search.c b/source4/lib/ldb/ldb_tdb/ldb_search.c
new file mode 100644
index 0000000000..d395c28f28
--- /dev/null
+++ b/source4/lib/ldb/ldb_tdb/ldb_search.c
@@ -0,0 +1,560 @@
+/*
+ ldb database library
+
+ Copyright (C) Andrew Tridgell 2004
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb search functions
+ *
+ * Description: functions to search ldb+tdb databases
+ *
+ * Author: Andrew Tridgell
+ */
+
+#include "ldb_tdb.h"
+
+/*
+ add one element to a message
+*/
+static int msg_add_element(struct ldb_message *ret,
+ const struct ldb_message_element *el,
+ int check_duplicates)
+{
+ unsigned int i;
+ struct ldb_message_element *e2, *elnew;
+
+ if (check_duplicates && ldb_msg_find_element(ret, el->name)) {
+ /* its already there */
+ return 0;
+ }
+
+ e2 = talloc_realloc(ret, ret->elements, struct ldb_message_element, ret->num_elements+1);
+ if (!e2) {
+ return -1;
+ }
+ ret->elements = e2;
+
+ elnew = &e2[ret->num_elements];
+
+ elnew->name = talloc_strdup(ret->elements, el->name);
+ if (!elnew->name) {
+ return -1;
+ }
+
+ if (el->num_values) {
+ elnew->values = talloc_array(ret->elements, struct ldb_val, el->num_values);
+ if (!elnew->values) {
+ return -1;
+ }
+ } else {
+ elnew->values = NULL;
+ }
+
+ for (i=0;i<el->num_values;i++) {
+ elnew->values[i] = ldb_val_dup(elnew->values, &el->values[i]);
+ if (elnew->values[i].length != el->values[i].length) {
+ return -1;
+ }
+ }
+
+ elnew->num_values = el->num_values;
+
+ ret->num_elements++;
+
+ return 0;
+}
+
+/*
+ add the special distinguishedName element
+*/
+static int msg_add_distinguished_name(struct ldb_message *msg)
+{
+ struct ldb_message_element el;
+ struct ldb_val val;
+ int ret;
+
+ el.flags = 0;
+ el.name = "distinguishedName";
+ el.num_values = 1;
+ el.values = &val;
+ val.data = (uint8_t *)ldb_dn_alloc_linearized(msg, msg->dn);
+ val.length = strlen((char *)val.data);
+
+ ret = msg_add_element(msg, &el, 1);
+ return ret;
+}
+
+/*
+ add all elements from one message into another
+ */
+static int msg_add_all_elements(struct ldb_module *module, struct ldb_message *ret,
+ const struct ldb_message *msg)
+{
+ struct ldb_context *ldb;
+ unsigned int i;
+ int check_duplicates = (ret->num_elements != 0);
+
+ ldb = ldb_module_get_ctx(module);
+
+ if (msg_add_distinguished_name(ret) != 0) {
+ return -1;
+ }
+
+ for (i=0;i<msg->num_elements;i++) {
+ const struct ldb_schema_attribute *a;
+ a = ldb_schema_attribute_by_name(ldb, msg->elements[i].name);
+ if (a->flags & LDB_ATTR_FLAG_HIDDEN) {
+ continue;
+ }
+ if (msg_add_element(ret, &msg->elements[i],
+ check_duplicates) != 0) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+/*
+ pull the specified list of attributes from a message
+ */
+static struct ldb_message *ltdb_pull_attrs(struct ldb_module *module,
+ TALLOC_CTX *mem_ctx,
+ const struct ldb_message *msg,
+ const char * const *attrs)
+{
+ struct ldb_message *ret;
+ int i;
+
+ ret = talloc(mem_ctx, struct ldb_message);
+ if (!ret) {
+ return NULL;
+ }
+
+ ret->dn = ldb_dn_copy(ret, msg->dn);
+ if (!ret->dn) {
+ talloc_free(ret);
+ return NULL;
+ }
+
+ ret->num_elements = 0;
+ ret->elements = NULL;
+
+ if (!attrs) {
+ if (msg_add_all_elements(module, ret, msg) != 0) {
+ talloc_free(ret);
+ return NULL;
+ }
+ return ret;
+ }
+
+ for (i=0;attrs[i];i++) {
+ struct ldb_message_element *el;
+
+ if (strcmp(attrs[i], "*") == 0) {
+ if (msg_add_all_elements(module, ret, msg) != 0) {
+ talloc_free(ret);
+ return NULL;
+ }
+ continue;
+ }
+
+ if (ldb_attr_cmp(attrs[i], "distinguishedName") == 0) {
+ if (msg_add_distinguished_name(ret) != 0) {
+ return NULL;
+ }
+ continue;
+ }
+
+ el = ldb_msg_find_element(msg, attrs[i]);
+ if (!el) {
+ continue;
+ }
+ if (msg_add_element(ret, el, 1) != 0) {
+ talloc_free(ret);
+ return NULL;
+ }
+ }
+
+ return ret;
+}
+
+/*
+ search the database for a single simple dn.
+ return LDB_ERR_NO_SUCH_OBJECT on record-not-found
+ and LDB_SUCCESS on success
+*/
+static int ltdb_search_base(struct ldb_module *module, struct ldb_dn *dn)
+{
+ void *data = ldb_module_get_private(module);
+ struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private);
+ TDB_DATA tdb_key, tdb_data;
+
+ if (ldb_dn_is_null(dn)) {
+ return LDB_ERR_NO_SUCH_OBJECT;
+ }
+
+ /* form the key */
+ tdb_key = ltdb_key(module, dn);
+ if (!tdb_key.dptr) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ tdb_data = tdb_fetch(ltdb->tdb, tdb_key);
+ talloc_free(tdb_key.dptr);
+ if (!tdb_data.dptr) {
+ return LDB_ERR_NO_SUCH_OBJECT;
+ }
+
+ free(tdb_data.dptr);
+ return LDB_SUCCESS;
+}
+
+/*
+ search the database for a single simple dn, returning all attributes
+ in a single message
+
+ return LDB_ERR_NO_SUCH_OBJECT on record-not-found
+ and LDB_SUCCESS on success
+*/
+int ltdb_search_dn1(struct ldb_module *module, struct ldb_dn *dn, struct ldb_message *msg)
+{
+ void *data = ldb_module_get_private(module);
+ struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private);
+ int ret;
+ TDB_DATA tdb_key, tdb_data;
+
+ memset(msg, 0, sizeof(*msg));
+
+ /* form the key */
+ tdb_key = ltdb_key(module, dn);
+ if (!tdb_key.dptr) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ tdb_data = tdb_fetch(ltdb->tdb, tdb_key);
+ talloc_free(tdb_key.dptr);
+ if (!tdb_data.dptr) {
+ return LDB_ERR_NO_SUCH_OBJECT;
+ }
+
+ msg->num_elements = 0;
+ msg->elements = NULL;
+
+ ret = ltdb_unpack_data(module, &tdb_data, msg);
+ free(tdb_data.dptr);
+ if (ret == -1) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if (!msg->dn) {
+ msg->dn = ldb_dn_copy(msg, dn);
+ }
+ if (!msg->dn) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ return LDB_SUCCESS;
+}
+
+/*
+ add a set of attributes from a record to a set of results
+ return 0 on success, -1 on failure
+*/
+int ltdb_add_attr_results(struct ldb_module *module,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message *msg,
+ const char * const attrs[],
+ unsigned int *count,
+ struct ldb_message ***res)
+{
+ struct ldb_message *msg2;
+ struct ldb_message **res2;
+
+ /* pull the attributes that the user wants */
+ msg2 = ltdb_pull_attrs(module, mem_ctx, msg, attrs);
+ if (!msg2) {
+ return -1;
+ }
+
+ /* add to the results list */
+ res2 = talloc_realloc(mem_ctx, *res, struct ldb_message *, (*count)+2);
+ if (!res2) {
+ talloc_free(msg2);
+ return -1;
+ }
+
+ (*res) = res2;
+
+ (*res)[*count] = talloc_move(*res, &msg2);
+ (*res)[(*count)+1] = NULL;
+ (*count)++;
+
+ return 0;
+}
+
+
+
+/*
+ filter the specified list of attributes from a message
+ removing not requested attrs.
+ */
+int ltdb_filter_attrs(struct ldb_message *msg, const char * const *attrs)
+{
+ int i, keep_all = 0;
+
+ if (attrs) {
+ /* check for special attrs */
+ for (i = 0; attrs[i]; i++) {
+ if (strcmp(attrs[i], "*") == 0) {
+ keep_all = 1;
+ break;
+ }
+
+ if (ldb_attr_cmp(attrs[i], "distinguishedName") == 0) {
+ if (msg_add_distinguished_name(msg) != 0) {
+ return -1;
+ }
+ }
+ }
+ } else {
+ keep_all = 1;
+ }
+
+ if (keep_all) {
+ if (msg_add_distinguished_name(msg) != 0) {
+ return -1;
+ }
+ return 0;
+ }
+
+ for (i = 0; i < msg->num_elements; i++) {
+ int j, found;
+
+ for (j = 0, found = 0; attrs[j]; j++) {
+ if (ldb_attr_cmp(msg->elements[i].name, attrs[j]) == 0) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ ldb_msg_remove_attr(msg, msg->elements[i].name);
+ i--;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ search function for a non-indexed search
+ */
+static int search_func(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data, void *state)
+{
+ struct ldb_context *ldb;
+ struct ltdb_context *ac;
+ struct ldb_message *msg;
+ int ret;
+
+ ac = talloc_get_type(state, struct ltdb_context);
+ ldb = ldb_module_get_ctx(ac->module);
+
+ if (key.dsize < 4 ||
+ strncmp((char *)key.dptr, "DN=", 3) != 0) {
+ return 0;
+ }
+
+ msg = ldb_msg_new(ac);
+ if (!msg) {
+ return -1;
+ }
+
+ /* unpack the record */
+ ret = ltdb_unpack_data(ac->module, &data, msg);
+ if (ret == -1) {
+ talloc_free(msg);
+ return -1;
+ }
+
+ if (!msg->dn) {
+ msg->dn = ldb_dn_new(msg, ldb,
+ (char *)key.dptr + 3);
+ if (msg->dn == NULL) {
+ talloc_free(msg);
+ return -1;
+ }
+ }
+
+ /* see if it matches the given expression */
+ if (!ldb_match_msg(ldb, msg,
+ ac->tree, ac->base, ac->scope)) {
+ talloc_free(msg);
+ return 0;
+ }
+
+ /* filter the attributes that the user wants */
+ ret = ltdb_filter_attrs(msg, ac->attrs);
+
+ if (ret == -1) {
+ talloc_free(msg);
+ return -1;
+ }
+
+ ret = ldb_module_send_entry(ac->req, msg, NULL);
+ if (ret != LDB_SUCCESS) {
+ ac->request_terminated = true;
+ /* the callback failed, abort the operation */
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*
+ search the database with a LDAP-like expression.
+ this is the "full search" non-indexed variant
+*/
+static int ltdb_search_full(struct ltdb_context *ctx)
+{
+ void *data = ldb_module_get_private(ctx->module);
+ struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private);
+ int ret;
+
+ if (ltdb->in_transaction != 0) {
+ ret = tdb_traverse(ltdb->tdb, search_func, ctx);
+ } else {
+ ret = tdb_traverse_read(ltdb->tdb, search_func, ctx);
+ }
+
+ if (ret == -1) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ return LDB_SUCCESS;
+}
+
+/*
+ search the database with a LDAP-like expression.
+ choses a search method
+*/
+int ltdb_search(struct ltdb_context *ctx)
+{
+ struct ldb_context *ldb;
+ struct ldb_module *module = ctx->module;
+ struct ldb_request *req = ctx->req;
+ void *data = ldb_module_get_private(module);
+ struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private);
+ int ret;
+
+ ldb = ldb_module_get_ctx(module);
+
+ ldb_request_set_state(req, LDB_ASYNC_PENDING);
+
+ if (ltdb_lock_read(module) != 0) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if (ltdb_cache_load(module) != 0) {
+ ltdb_unlock_read(module);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if (req->op.search.tree == NULL) {
+ ltdb_unlock_read(module);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if ((req->op.search.base == NULL) || (ldb_dn_is_null(req->op.search.base) == true)) {
+
+ /* Check what we should do with a NULL dn */
+ switch (req->op.search.scope) {
+ case LDB_SCOPE_BASE:
+ ldb_asprintf_errstring(ldb,
+ "NULL Base DN invalid for a base search");
+ ret = LDB_ERR_INVALID_DN_SYNTAX;
+ break;
+ case LDB_SCOPE_ONELEVEL:
+ ldb_asprintf_errstring(ldb,
+ "NULL Base DN invalid for a one-level search");
+ ret = LDB_ERR_INVALID_DN_SYNTAX;
+ break;
+ case LDB_SCOPE_SUBTREE:
+ default:
+ /* We accept subtree searches from a NULL base DN, ie over the whole DB */
+ ret = LDB_SUCCESS;
+ }
+ } else if (ldb_dn_is_valid(req->op.search.base) == false) {
+
+ /* We don't want invalid base DNs here */
+ ldb_asprintf_errstring(ldb,
+ "Invalid Base DN: %s",
+ ldb_dn_get_linearized(req->op.search.base));
+ ret = LDB_ERR_INVALID_DN_SYNTAX;
+
+ } else if (ltdb->check_base) {
+ /* This database has been marked as 'checkBaseOnSearch', so do a spot check of the base dn */
+ ret = ltdb_search_base(module, req->op.search.base);
+
+ if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+ ldb_asprintf_errstring(ldb,
+ "No such Base DN: %s",
+ ldb_dn_get_linearized(req->op.search.base));
+ }
+
+ } else {
+ /* If we are not checking the base DN life is easy */
+ ret = LDB_SUCCESS;
+ }
+
+ ctx->tree = req->op.search.tree;
+ ctx->scope = req->op.search.scope;
+ ctx->base = req->op.search.base;
+ ctx->attrs = req->op.search.attrs;
+
+ if (ret == LDB_SUCCESS) {
+ ret = ltdb_search_indexed(ctx);
+ if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+ /* Not in the index, therefore OK! */
+ ret = LDB_SUCCESS;
+
+ }
+ /* Check if we got just a normal error.
+ * In that case proceed to a full search unless we got a
+ * callback error */
+ if ( ! ctx->request_terminated && ret != LDB_SUCCESS) {
+ /* Not indexed, so we need to do a full scan */
+ ret = ltdb_search_full(ctx);
+ if (ret != LDB_SUCCESS) {
+ ldb_set_errstring(ldb, "Indexed and full searches both failed!\n");
+ }
+ }
+ }
+
+ ltdb_unlock_read(module);
+
+ return ret;
+}
+
diff --git a/source4/lib/ldb/ldb_tdb/ldb_tdb.c b/source4/lib/ldb/ldb_tdb/ldb_tdb.c
new file mode 100644
index 0000000000..9df62be936
--- /dev/null
+++ b/source4/lib/ldb/ldb_tdb/ldb_tdb.c
@@ -0,0 +1,1296 @@
+/*
+ ldb database library
+
+ Copyright (C) Andrew Tridgell 2004
+ Copyright (C) Stefan Metzmacher 2004
+ Copyright (C) Simo Sorce 2006-2008
+
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb_tdb
+ *
+ * Component: ldb tdb backend
+ *
+ * Description: core functions for tdb backend
+ *
+ * Author: Andrew Tridgell
+ * Author: Stefan Metzmacher
+ *
+ * Modifications:
+ *
+ * - description: make the module use asyncronous calls
+ * date: Feb 2006
+ * Author: Simo Sorce
+ *
+ * - description: make it possible to use event contexts
+ * date: Jan 2008
+ * Author: Simo Sorce
+ */
+
+#include "ldb_tdb.h"
+
+
+/*
+ map a tdb error code to a ldb error code
+*/
+static int ltdb_err_map(enum TDB_ERROR tdb_code)
+{
+ switch (tdb_code) {
+ case TDB_SUCCESS:
+ return LDB_SUCCESS;
+ case TDB_ERR_CORRUPT:
+ case TDB_ERR_OOM:
+ case TDB_ERR_EINVAL:
+ return LDB_ERR_OPERATIONS_ERROR;
+ case TDB_ERR_IO:
+ return LDB_ERR_PROTOCOL_ERROR;
+ case TDB_ERR_LOCK:
+ case TDB_ERR_NOLOCK:
+ return LDB_ERR_BUSY;
+ case TDB_ERR_LOCK_TIMEOUT:
+ return LDB_ERR_TIME_LIMIT_EXCEEDED;
+ case TDB_ERR_EXISTS:
+ return LDB_ERR_ENTRY_ALREADY_EXISTS;
+ case TDB_ERR_NOEXIST:
+ return LDB_ERR_NO_SUCH_OBJECT;
+ case TDB_ERR_RDONLY:
+ return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
+ }
+ return LDB_ERR_OTHER;
+}
+
+/*
+ lock the database for read - use by ltdb_search and ltdb_sequence_number
+*/
+int ltdb_lock_read(struct ldb_module *module)
+{
+ void *data = ldb_module_get_private(module);
+ struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private);
+ if (ltdb->in_transaction == 0) {
+ return tdb_lockall_read(ltdb->tdb);
+ }
+ return 0;
+}
+
+/*
+ unlock the database after a ltdb_lock_read()
+*/
+int ltdb_unlock_read(struct ldb_module *module)
+{
+ void *data = ldb_module_get_private(module);
+ struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private);
+ if (ltdb->in_transaction == 0) {
+ return tdb_unlockall_read(ltdb->tdb);
+ }
+ return 0;
+}
+
+
+/*
+ form a TDB_DATA for a record key
+ caller frees
+
+ note that the key for a record can depend on whether the
+ dn refers to a case sensitive index record or not
+*/
+struct TDB_DATA ltdb_key(struct ldb_module *module, struct ldb_dn *dn)
+{
+ struct ldb_context *ldb = ldb_module_get_ctx(module);
+ TDB_DATA key;
+ char *key_str = NULL;
+ const char *dn_folded = NULL;
+
+ /*
+ most DNs are case insensitive. The exception is index DNs for
+ case sensitive attributes
+
+ there are 3 cases dealt with in this code:
+
+ 1) if the dn doesn't start with @ then uppercase the attribute
+ names and the attributes values of case insensitive attributes
+ 2) if the dn starts with @ then leave it alone -
+ the indexing code handles the rest
+ */
+
+ dn_folded = ldb_dn_get_casefold(dn);
+ if (!dn_folded) {
+ goto failed;
+ }
+
+ key_str = talloc_strdup(ldb, "DN=");
+ if (!key_str) {
+ goto failed;
+ }
+
+ key_str = talloc_strdup_append_buffer(key_str, dn_folded);
+ if (!key_str) {
+ goto failed;
+ }
+
+ key.dptr = (uint8_t *)key_str;
+ key.dsize = strlen(key_str) + 1;
+
+ return key;
+
+failed:
+ errno = ENOMEM;
+ key.dptr = NULL;
+ key.dsize = 0;
+ return key;
+}
+
+/*
+ check special dn's have valid attributes
+ currently only @ATTRIBUTES is checked
+*/
+static int ltdb_check_special_dn(struct ldb_module *module,
+ const struct ldb_message *msg)
+{
+ struct ldb_context *ldb = ldb_module_get_ctx(module);
+ int i, j;
+
+ if (! ldb_dn_is_special(msg->dn) ||
+ ! ldb_dn_check_special(msg->dn, LTDB_ATTRIBUTES)) {
+ return 0;
+ }
+
+ /* we have @ATTRIBUTES, let's check attributes are fine */
+ /* should we check that we deny multivalued attributes ? */
+ for (i = 0; i < msg->num_elements; i++) {
+ for (j = 0; j < msg->elements[i].num_values; j++) {
+ if (ltdb_check_at_attributes_values(&msg->elements[i].values[j]) != 0) {
+ ldb_set_errstring(ldb, "Invalid attribute value in an @ATTRIBUTES entry");
+ return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+/*
+ we've made a modification to a dn - possibly reindex and
+ update sequence number
+*/
+static int ltdb_modified(struct ldb_module *module, struct ldb_dn *dn)
+{
+ int ret = LDB_SUCCESS;
+
+ if (ldb_dn_is_special(dn) &&
+ (ldb_dn_check_special(dn, LTDB_INDEXLIST) ||
+ ldb_dn_check_special(dn, LTDB_ATTRIBUTES)) ) {
+ ret = ltdb_reindex(module);
+ }
+
+ if (ret == LDB_SUCCESS &&
+ !(ldb_dn_is_special(dn) &&
+ ldb_dn_check_special(dn, LTDB_BASEINFO)) ) {
+ ret = ltdb_increase_sequence_number(module);
+ }
+
+ return ret;
+}
+
+/*
+ store a record into the db
+*/
+int ltdb_store(struct ldb_module *module, const struct ldb_message *msg, int flgs)
+{
+ void *data = ldb_module_get_private(module);
+ struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private);
+ TDB_DATA tdb_key, tdb_data;
+ int ret;
+
+ tdb_key = ltdb_key(module, msg->dn);
+ if (!tdb_key.dptr) {
+ return LDB_ERR_OTHER;
+ }
+
+ ret = ltdb_pack_data(module, msg, &tdb_data);
+ if (ret == -1) {
+ talloc_free(tdb_key.dptr);
+ return LDB_ERR_OTHER;
+ }
+
+ ret = tdb_store(ltdb->tdb, tdb_key, tdb_data, flgs);
+ if (ret == -1) {
+ ret = ltdb_err_map(tdb_error(ltdb->tdb));
+ goto done;
+ }
+
+ ret = ltdb_index_add(module, msg);
+ if (ret != LDB_SUCCESS) {
+ tdb_delete(ltdb->tdb, tdb_key);
+ }
+
+done:
+ talloc_free(tdb_key.dptr);
+ talloc_free(tdb_data.dptr);
+
+ return ret;
+}
+
+
+static int ltdb_add_internal(struct ldb_module *module,
+ const struct ldb_message *msg)
+{
+ struct ldb_context *ldb = ldb_module_get_ctx(module);
+ int ret;
+
+ ret = ltdb_check_special_dn(module, msg);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ if (ltdb_cache_load(module) != 0) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ltdb_store(module, msg, TDB_INSERT);
+
+ if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) {
+ ldb_asprintf_errstring(ldb,
+ "Entry %s already exists",
+ ldb_dn_get_linearized(msg->dn));
+ return ret;
+ }
+
+ if (ret == LDB_SUCCESS) {
+ ret = ltdb_index_one(module, msg, 1);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ ret = ltdb_modified(module, msg->dn);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+/*
+ add a record to the database
+*/
+static int ltdb_add(struct ltdb_context *ctx)
+{
+ struct ldb_module *module = ctx->module;
+ struct ldb_request *req = ctx->req;
+ int tret;
+
+ ldb_request_set_state(req, LDB_ASYNC_PENDING);
+
+ tret = ltdb_add_internal(module, req->op.add.message);
+ if (tret != LDB_SUCCESS) {
+ return tret;
+ }
+
+ return LDB_SUCCESS;
+}
+
+/*
+ delete a record from the database, not updating indexes (used for deleting
+ index records)
+*/
+int ltdb_delete_noindex(struct ldb_module *module, struct ldb_dn *dn)
+{
+ void *data = ldb_module_get_private(module);
+ struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private);
+ TDB_DATA tdb_key;
+ int ret;
+
+ tdb_key = ltdb_key(module, dn);
+ if (!tdb_key.dptr) {
+ return LDB_ERR_OTHER;
+ }
+
+ ret = tdb_delete(ltdb->tdb, tdb_key);
+ talloc_free(tdb_key.dptr);
+
+ if (ret != 0) {
+ ret = ltdb_err_map(tdb_error(ltdb->tdb));
+ }
+
+ return ret;
+}
+
+static int ltdb_delete_internal(struct ldb_module *module, struct ldb_dn *dn)
+{
+ struct ldb_message *msg;
+ int ret;
+
+ msg = talloc(module, struct ldb_message);
+ if (msg == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* in case any attribute of the message was indexed, we need
+ to fetch the old record */
+ ret = ltdb_search_dn1(module, dn, msg);
+ if (ret != LDB_SUCCESS) {
+ /* not finding the old record is an error */
+ goto done;
+ }
+
+ ret = ltdb_delete_noindex(module, dn);
+ if (ret != LDB_SUCCESS) {
+ goto done;
+ }
+
+ /* remove one level attribute */
+ ret = ltdb_index_one(module, msg, 0);
+ if (ret != LDB_SUCCESS) {
+ goto done;
+ }
+
+ /* remove any indexed attributes */
+ ret = ltdb_index_del(module, msg);
+ if (ret != LDB_SUCCESS) {
+ goto done;
+ }
+
+ ret = ltdb_modified(module, dn);
+ if (ret != LDB_SUCCESS) {
+ goto done;
+ }
+
+done:
+ talloc_free(msg);
+ return ret;
+}
+
+/*
+ delete a record from the database
+*/
+static int ltdb_delete(struct ltdb_context *ctx)
+{
+ struct ldb_module *module = ctx->module;
+ struct ldb_request *req = ctx->req;
+ int tret;
+
+ ldb_request_set_state(req, LDB_ASYNC_PENDING);
+
+ if (ltdb_cache_load(module) != 0) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ tret = ltdb_delete_internal(module, req->op.del.dn);
+ if (tret != LDB_SUCCESS) {
+ return tret;
+ }
+
+ return LDB_SUCCESS;
+}
+
+/*
+ find an element by attribute name. At the moment this does a linear search,
+ it should be re-coded to use a binary search once all places that modify
+ records guarantee sorted order
+
+ return the index of the first matching element if found, otherwise -1
+*/
+static int find_element(const struct ldb_message *msg, const char *name)
+{
+ unsigned int i;
+ for (i=0;i<msg->num_elements;i++) {
+ if (ldb_attr_cmp(msg->elements[i].name, name) == 0) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+
+/*
+ add an element to an existing record. Assumes a elements array that we
+ can call re-alloc on, and assumed that we can re-use the data pointers from
+ the passed in additional values. Use with care!
+
+ returns 0 on success, -1 on failure (and sets errno)
+*/
+static int msg_add_element(struct ldb_context *ldb,
+ struct ldb_message *msg,
+ struct ldb_message_element *el)
+{
+ struct ldb_message_element *e2;
+ unsigned int i;
+
+ e2 = talloc_realloc(msg, msg->elements, struct ldb_message_element,
+ msg->num_elements+1);
+ if (!e2) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ msg->elements = e2;
+
+ e2 = &msg->elements[msg->num_elements];
+
+ e2->name = el->name;
+ e2->flags = el->flags;
+ e2->values = NULL;
+ if (el->num_values != 0) {
+ e2->values = talloc_array(msg->elements,
+ struct ldb_val, el->num_values);
+ if (!e2->values) {
+ errno = ENOMEM;
+ return -1;
+ }
+ }
+ for (i=0;i<el->num_values;i++) {
+ e2->values[i] = el->values[i];
+ }
+ e2->num_values = el->num_values;
+
+ msg->num_elements++;
+
+ return 0;
+}
+
+/*
+ delete all elements having a specified attribute name
+*/
+static int msg_delete_attribute(struct ldb_module *module,
+ struct ldb_context *ldb,
+ struct ldb_message *msg, const char *name)
+{
+ const char *dn;
+ unsigned int i, j;
+
+ dn = ldb_dn_get_linearized(msg->dn);
+ if (dn == NULL) {
+ return -1;
+ }
+
+ for (i=0;i<msg->num_elements;i++) {
+ if (ldb_attr_cmp(msg->elements[i].name, name) == 0) {
+ for (j=0;j<msg->elements[i].num_values;j++) {
+ ltdb_index_del_value(module, dn,
+ &msg->elements[i], j);
+ }
+ talloc_free(msg->elements[i].values);
+ if (msg->num_elements > (i+1)) {
+ memmove(&msg->elements[i],
+ &msg->elements[i+1],
+ sizeof(struct ldb_message_element)*
+ (msg->num_elements - (i+1)));
+ }
+ msg->num_elements--;
+ i--;
+ msg->elements = talloc_realloc(msg, msg->elements,
+ struct ldb_message_element,
+ msg->num_elements);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ delete all elements matching an attribute name/value
+
+ return 0 on success, -1 on failure
+*/
+static int msg_delete_element(struct ldb_module *module,
+ struct ldb_message *msg,
+ const char *name,
+ const struct ldb_val *val)
+{
+ struct ldb_context *ldb = ldb_module_get_ctx(module);
+ unsigned int i;
+ int found;
+ struct ldb_message_element *el;
+ const struct ldb_schema_attribute *a;
+
+ found = find_element(msg, name);
+ if (found == -1) {
+ return -1;
+ }
+
+ el = &msg->elements[found];
+
+ a = ldb_schema_attribute_by_name(ldb, el->name);
+
+ for (i=0;i<el->num_values;i++) {
+ if (a->syntax->comparison_fn(ldb, ldb,
+ &el->values[i], val) == 0) {
+ if (i<el->num_values-1) {
+ memmove(&el->values[i], &el->values[i+1],
+ sizeof(el->values[i])*
+ (el->num_values-(i+1)));
+ }
+ el->num_values--;
+ if (el->num_values == 0) {
+ return msg_delete_attribute(module, ldb,
+ msg, name);
+ }
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+
+/*
+ modify a record - internal interface
+
+ yuck - this is O(n^2). Luckily n is usually small so we probably
+ get away with it, but if we ever have really large attribute lists
+ then we'll need to look at this again
+*/
+int ltdb_modify_internal(struct ldb_module *module,
+ const struct ldb_message *msg)
+{
+ struct ldb_context *ldb = ldb_module_get_ctx(module);
+ void *data = ldb_module_get_private(module);
+ struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private);
+ TDB_DATA tdb_key, tdb_data;
+ struct ldb_message *msg2;
+ unsigned i, j;
+ int ret, idx;
+
+ tdb_key = ltdb_key(module, msg->dn);
+ if (!tdb_key.dptr) {
+ return LDB_ERR_OTHER;
+ }
+
+ tdb_data = tdb_fetch(ltdb->tdb, tdb_key);
+ if (!tdb_data.dptr) {
+ talloc_free(tdb_key.dptr);
+ return ltdb_err_map(tdb_error(ltdb->tdb));
+ }
+
+ msg2 = talloc(tdb_key.dptr, struct ldb_message);
+ if (msg2 == NULL) {
+ talloc_free(tdb_key.dptr);
+ return LDB_ERR_OTHER;
+ }
+
+ ret = ltdb_unpack_data(module, &tdb_data, msg2);
+ if (ret == -1) {
+ ret = LDB_ERR_OTHER;
+ goto failed;
+ }
+
+ if (!msg2->dn) {
+ msg2->dn = msg->dn;
+ }
+
+ for (i=0;i<msg->num_elements;i++) {
+ struct ldb_message_element *el = &msg->elements[i];
+ struct ldb_message_element *el2;
+ struct ldb_val *vals;
+ const char *dn;
+
+ switch (msg->elements[i].flags & LDB_FLAG_MOD_MASK) {
+
+ case LDB_FLAG_MOD_ADD:
+ /* add this element to the message. fail if it
+ already exists */
+ idx = find_element(msg2, el->name);
+
+ if (idx == -1) {
+ if (msg_add_element(ldb, msg2, el) != 0) {
+ ret = LDB_ERR_OTHER;
+ goto failed;
+ }
+ continue;
+ }
+
+ el2 = &msg2->elements[idx];
+
+ /* An attribute with this name already exists,
+ * add all values if they don't already exist
+ * (check both the other elements to be added,
+ * and those already in the db). */
+
+ for (j=0;j<el->num_values;j++) {
+ if (ldb_msg_find_val(el2, &el->values[j])) {
+ ldb_asprintf_errstring(ldb, "%s: value #%d already exists", el->name, j);
+ ret = LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
+ goto failed;
+ }
+ if (ldb_msg_find_val(el, &el->values[j]) != &el->values[j]) {
+ ldb_asprintf_errstring(ldb, "%s: value #%d provided more than once", el->name, j);
+ ret = LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
+ goto failed;
+ }
+ }
+
+ vals = talloc_realloc(msg2->elements, el2->values, struct ldb_val,
+ el2->num_values + el->num_values);
+
+ if (vals == NULL) {
+ ret = LDB_ERR_OTHER;
+ goto failed;
+ }
+
+ for (j=0;j<el->num_values;j++) {
+ vals[el2->num_values + j] =
+ ldb_val_dup(vals, &el->values[j]);
+ }
+
+ el2->values = vals;
+ el2->num_values += el->num_values;
+
+ break;
+
+ case LDB_FLAG_MOD_REPLACE:
+ /* replace all elements of this attribute name with the elements
+ listed. The attribute not existing is not an error */
+ msg_delete_attribute(module, ldb, msg2, el->name);
+
+ for (j=0;j<el->num_values;j++) {
+ if (ldb_msg_find_val(el, &el->values[j]) != &el->values[j]) {
+ ldb_asprintf_errstring(ldb, "%s: value #%d provided more than once", el->name, j);
+ ret = LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
+ goto failed;
+ }
+ }
+
+ /* add the replacement element, if not empty */
+ if (el->num_values != 0 &&
+ msg_add_element(ldb, msg2, el) != 0) {
+ ret = LDB_ERR_OTHER;
+ goto failed;
+ }
+ break;
+
+ case LDB_FLAG_MOD_DELETE:
+
+ dn = ldb_dn_get_linearized(msg->dn);
+ if (dn == NULL) {
+ ret = LDB_ERR_OTHER;
+ goto failed;
+ }
+
+ /* we could be being asked to delete all
+ values or just some values */
+ if (msg->elements[i].num_values == 0) {
+ if (msg_delete_attribute(module, ldb, msg2,
+ msg->elements[i].name) != 0) {
+ ldb_asprintf_errstring(ldb, "No such attribute: %s for delete on %s", msg->elements[i].name, dn);
+ ret = LDB_ERR_NO_SUCH_ATTRIBUTE;
+ goto failed;
+ }
+ break;
+ }
+ for (j=0;j<msg->elements[i].num_values;j++) {
+ if (msg_delete_element(module,
+ msg2,
+ msg->elements[i].name,
+ &msg->elements[i].values[j]) != 0) {
+ ldb_asprintf_errstring(ldb, "No matching attribute value when deleting attribute: %s on %s", msg->elements[i].name, dn);
+ ret = LDB_ERR_NO_SUCH_ATTRIBUTE;
+ goto failed;
+ }
+ ret = ltdb_index_del_value(module, dn, &msg->elements[i], j);
+ if (ret != LDB_SUCCESS) {
+ goto failed;
+ }
+ }
+ break;
+ default:
+ ldb_asprintf_errstring(ldb,
+ "Invalid ldb_modify flags on %s: 0x%x",
+ msg->elements[i].name,
+ msg->elements[i].flags & LDB_FLAG_MOD_MASK);
+ ret = LDB_ERR_PROTOCOL_ERROR;
+ goto failed;
+ }
+ }
+
+ /* we've made all the mods
+ * save the modified record back into the database */
+ ret = ltdb_store(module, msg2, TDB_MODIFY);
+ if (ret != LDB_SUCCESS) {
+ goto failed;
+ }
+
+ ret = ltdb_modified(module, msg->dn);
+ if (ret != LDB_SUCCESS) {
+ goto failed;
+ }
+
+ talloc_free(tdb_key.dptr);
+ free(tdb_data.dptr);
+ return ret;
+
+failed:
+ talloc_free(tdb_key.dptr);
+ free(tdb_data.dptr);
+ return ret;
+}
+
+/*
+ modify a record
+*/
+static int ltdb_modify(struct ltdb_context *ctx)
+{
+ struct ldb_module *module = ctx->module;
+ struct ldb_request *req = ctx->req;
+ int tret;
+
+ ldb_request_set_state(req, LDB_ASYNC_PENDING);
+
+ tret = ltdb_check_special_dn(module, req->op.mod.message);
+ if (tret != LDB_SUCCESS) {
+ return tret;
+ }
+
+ if (ltdb_cache_load(module) != 0) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ tret = ltdb_modify_internal(module, req->op.mod.message);
+ if (tret != LDB_SUCCESS) {
+ return tret;
+ }
+
+ return LDB_SUCCESS;
+}
+
+/*
+ rename a record
+*/
+static int ltdb_rename(struct ltdb_context *ctx)
+{
+ struct ldb_module *module = ctx->module;
+ struct ldb_request *req = ctx->req;
+ struct ldb_message *msg;
+ int tret;
+
+ ldb_request_set_state(req, LDB_ASYNC_PENDING);
+
+ if (ltdb_cache_load(ctx->module) != 0) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ msg = talloc(ctx, struct ldb_message);
+ if (msg == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* in case any attribute of the message was indexed, we need
+ to fetch the old record */
+ tret = ltdb_search_dn1(module, req->op.rename.olddn, msg);
+ if (tret != LDB_SUCCESS) {
+ /* not finding the old record is an error */
+ return tret;
+ }
+
+ msg->dn = ldb_dn_copy(msg, req->op.rename.newdn);
+ if (!msg->dn) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if (ldb_dn_compare(req->op.rename.olddn, req->op.rename.newdn) == 0) {
+ /* The rename operation is apparently only changing case -
+ the DNs are the same. Delete the old DN before adding
+ the new one to avoid a TDB_ERR_EXISTS error.
+
+ The only drawback to this is that if the delete
+ succeeds but the add fails, we rely on the
+ transaction to roll this all back. */
+ tret = ltdb_delete_internal(module, req->op.rename.olddn);
+ if (tret != LDB_SUCCESS) {
+ return tret;
+ }
+
+ tret = ltdb_add_internal(module, msg);
+ if (tret != LDB_SUCCESS) {
+ return tret;
+ }
+ } else {
+ /* The rename operation is changing DNs. Try to add the new
+ DN first to avoid clobbering another DN not related to
+ this rename operation. */
+ tret = ltdb_add_internal(module, msg);
+ if (tret != LDB_SUCCESS) {
+ return tret;
+ }
+
+ tret = ltdb_delete_internal(module, req->op.rename.olddn);
+ if (tret != LDB_SUCCESS) {
+ ltdb_delete_internal(module, req->op.rename.newdn);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ }
+
+ return LDB_SUCCESS;
+}
+
+static int ltdb_start_trans(struct ldb_module *module)
+{
+ void *data = ldb_module_get_private(module);
+ struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private);
+
+ if (tdb_transaction_start(ltdb->tdb) != 0) {
+ return ltdb_err_map(tdb_error(ltdb->tdb));
+ }
+
+ ltdb->in_transaction++;
+
+ ltdb_index_transaction_start(module);
+
+ return LDB_SUCCESS;
+}
+
+static int ltdb_end_trans(struct ldb_module *module)
+{
+ void *data = ldb_module_get_private(module);
+ struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private);
+
+ ltdb->in_transaction--;
+
+ if (ltdb_index_transaction_commit(module) != 0) {
+ tdb_transaction_cancel(ltdb->tdb);
+ return ltdb_err_map(tdb_error(ltdb->tdb));
+ }
+
+ if (tdb_transaction_commit(ltdb->tdb) != 0) {
+ return ltdb_err_map(tdb_error(ltdb->tdb));
+ }
+
+ return LDB_SUCCESS;
+}
+
+static int ltdb_del_trans(struct ldb_module *module)
+{
+ void *data = ldb_module_get_private(module);
+ struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private);
+
+ ltdb->in_transaction--;
+
+ if (ltdb_index_transaction_cancel(module) != 0) {
+ tdb_transaction_cancel(ltdb->tdb);
+ return ltdb_err_map(tdb_error(ltdb->tdb));
+ }
+
+ if (tdb_transaction_cancel(ltdb->tdb) != 0) {
+ return ltdb_err_map(tdb_error(ltdb->tdb));
+ }
+
+ return LDB_SUCCESS;
+}
+
+/*
+ return sequenceNumber from @BASEINFO
+*/
+static int ltdb_sequence_number(struct ltdb_context *ctx,
+ struct ldb_extended **ext)
+{
+ struct ldb_context *ldb;
+ struct ldb_module *module = ctx->module;
+ struct ldb_request *req = ctx->req;
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_seqnum_request *seq;
+ struct ldb_seqnum_result *res;
+ struct ldb_message *msg = NULL;
+ struct ldb_dn *dn;
+ const char *date;
+ int ret;
+
+ ldb = ldb_module_get_ctx(module);
+
+ seq = talloc_get_type(req->op.extended.data,
+ struct ldb_seqnum_request);
+ if (seq == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ldb_request_set_state(req, LDB_ASYNC_PENDING);
+
+ if (ltdb_lock_read(module) != 0) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ res = talloc_zero(req, struct ldb_seqnum_result);
+ if (res == NULL) {
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ goto done;
+ }
+ tmp_ctx = talloc_new(req);
+ if (tmp_ctx == NULL) {
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ goto done;
+ }
+
+ dn = ldb_dn_new(tmp_ctx, ldb, LTDB_BASEINFO);
+
+ msg = talloc(tmp_ctx, struct ldb_message);
+ if (msg == NULL) {
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ goto done;
+ }
+
+ ret = ltdb_search_dn1(module, dn, msg);
+ if (ret != LDB_SUCCESS) {
+ goto done;
+ }
+
+ switch (seq->type) {
+ case LDB_SEQ_HIGHEST_SEQ:
+ res->seq_num = ldb_msg_find_attr_as_uint64(msg, LTDB_SEQUENCE_NUMBER, 0);
+ break;
+ case LDB_SEQ_NEXT:
+ res->seq_num = ldb_msg_find_attr_as_uint64(msg, LTDB_SEQUENCE_NUMBER, 0);
+ res->seq_num++;
+ break;
+ case LDB_SEQ_HIGHEST_TIMESTAMP:
+ date = ldb_msg_find_attr_as_string(msg, LTDB_MOD_TIMESTAMP, NULL);
+ if (date) {
+ res->seq_num = ldb_string_to_time(date);
+ } else {
+ res->seq_num = 0;
+ /* zero is as good as anything when we don't know */
+ }
+ break;
+ }
+
+ *ext = talloc_zero(req, struct ldb_extended);
+ if (*ext == NULL) {
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ goto done;
+ }
+ (*ext)->oid = LDB_EXTENDED_SEQUENCE_NUMBER;
+ (*ext)->data = talloc_steal(*ext, res);
+
+ ret = LDB_SUCCESS;
+
+done:
+ talloc_free(tmp_ctx);
+ ltdb_unlock_read(module);
+ return ret;
+}
+
+static void ltdb_request_done(struct ltdb_context *ctx, int error)
+{
+ struct ldb_context *ldb;
+ struct ldb_request *req;
+ struct ldb_reply *ares;
+
+ ldb = ldb_module_get_ctx(ctx->module);
+ req = ctx->req;
+
+ /* if we already returned an error just return */
+ if (ldb_request_get_status(req) != LDB_SUCCESS) {
+ return;
+ }
+
+ ares = talloc_zero(req, struct ldb_reply);
+ if (!ares) {
+ ldb_oom(ldb);
+ req->callback(req, NULL);
+ return;
+ }
+ ares->type = LDB_REPLY_DONE;
+ ares->error = error;
+
+ req->callback(req, ares);
+}
+
+static void ltdb_timeout(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval t,
+ void *private_data)
+{
+ struct ltdb_context *ctx;
+ ctx = talloc_get_type(private_data, struct ltdb_context);
+
+ if (!ctx->request_terminated) {
+ /* request is done now */
+ ltdb_request_done(ctx, LDB_ERR_TIME_LIMIT_EXCEEDED);
+ }
+
+ if (!ctx->request_terminated) {
+ /* neutralize the spy */
+ ctx->spy->ctx = NULL;
+ }
+ talloc_free(ctx);
+}
+
+static void ltdb_request_extended_done(struct ltdb_context *ctx,
+ struct ldb_extended *ext,
+ int error)
+{
+ struct ldb_context *ldb;
+ struct ldb_request *req;
+ struct ldb_reply *ares;
+
+ ldb = ldb_module_get_ctx(ctx->module);
+ req = ctx->req;
+
+ /* if we already returned an error just return */
+ if (ldb_request_get_status(req) != LDB_SUCCESS) {
+ return;
+ }
+
+ ares = talloc_zero(req, struct ldb_reply);
+ if (!ares) {
+ ldb_oom(ldb);
+ req->callback(req, NULL);
+ return;
+ }
+ ares->type = LDB_REPLY_DONE;
+ ares->response = ext;
+ ares->error = error;
+
+ req->callback(req, ares);
+}
+
+static void ltdb_handle_extended(struct ltdb_context *ctx)
+{
+ struct ldb_extended *ext = NULL;
+ int ret;
+
+ if (strcmp(ctx->req->op.extended.oid,
+ LDB_EXTENDED_SEQUENCE_NUMBER) == 0) {
+ /* get sequence number */
+ ret = ltdb_sequence_number(ctx, &ext);
+ } else {
+ /* not recognized */
+ ret = LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION;
+ }
+
+ ltdb_request_extended_done(ctx, ext, ret);
+}
+
+static void ltdb_callback(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval t,
+ void *private_data)
+{
+ struct ltdb_context *ctx;
+ int ret;
+
+ ctx = talloc_get_type(private_data, struct ltdb_context);
+
+ if (ctx->request_terminated) {
+ goto done;
+ }
+
+ switch (ctx->req->operation) {
+ case LDB_SEARCH:
+ ret = ltdb_search(ctx);
+ break;
+ case LDB_ADD:
+ ret = ltdb_add(ctx);
+ break;
+ case LDB_MODIFY:
+ ret = ltdb_modify(ctx);
+ break;
+ case LDB_DELETE:
+ ret = ltdb_delete(ctx);
+ break;
+ case LDB_RENAME:
+ ret = ltdb_rename(ctx);
+ break;
+ case LDB_EXTENDED:
+ ltdb_handle_extended(ctx);
+ goto done;
+ default:
+ /* no other op supported */
+ ret = LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+
+ if (!ctx->request_terminated) {
+ /* request is done now */
+ ltdb_request_done(ctx, ret);
+ }
+
+done:
+ if (!ctx->request_terminated) {
+ /* neutralize the spy */
+ ctx->spy->ctx = NULL;
+ }
+ talloc_free(ctx);
+}
+
+static int ltdb_request_destructor(void *ptr)
+{
+ struct ltdb_req_spy *spy = talloc_get_type(ptr, struct ltdb_req_spy);
+
+ if (spy->ctx != NULL) {
+ spy->ctx->request_terminated = true;
+ }
+
+ return 0;
+}
+
+static int ltdb_handle_request(struct ldb_module *module,
+ struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct tevent_context *ev;
+ struct ltdb_context *ac;
+ struct tevent_timer *te;
+ struct timeval tv;
+
+ if (check_critical_controls(req->controls)) {
+ return LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION;
+ }
+
+ ldb = ldb_module_get_ctx(module);
+
+ if (req->starttime == 0 || req->timeout == 0) {
+ ldb_set_errstring(ldb, "Invalid timeout settings");
+ return LDB_ERR_TIME_LIMIT_EXCEEDED;
+ }
+
+ ev = ldb_get_event_context(ldb);
+
+ ac = talloc_zero(ldb, struct ltdb_context);
+ if (ac == NULL) {
+ ldb_set_errstring(ldb, "Out of Memory");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->module = module;
+ ac->req = req;
+
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ te = tevent_add_timer(ev, ac, tv, ltdb_callback, ac);
+ if (NULL == te) {
+ talloc_free(ac);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ tv.tv_sec = req->starttime + req->timeout;
+ ac->timeout_event = tevent_add_timer(ev, ac, tv, ltdb_timeout, ac);
+ if (NULL == ac->timeout_event) {
+ talloc_free(ac);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* set a spy so that we do not try to use the request context
+ * if it is freed before ltdb_callback fires */
+ ac->spy = talloc(req, struct ltdb_req_spy);
+ if (NULL == ac->spy) {
+ talloc_free(ac);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ac->spy->ctx = ac;
+
+ talloc_set_destructor((TALLOC_CTX *)ac->spy, ltdb_request_destructor);
+
+ return LDB_SUCCESS;
+}
+
+static const struct ldb_module_ops ltdb_ops = {
+ .name = "tdb",
+ .search = ltdb_handle_request,
+ .add = ltdb_handle_request,
+ .modify = ltdb_handle_request,
+ .del = ltdb_handle_request,
+ .rename = ltdb_handle_request,
+ .extended = ltdb_handle_request,
+ .start_transaction = ltdb_start_trans,
+ .end_transaction = ltdb_end_trans,
+ .del_transaction = ltdb_del_trans,
+};
+
+/*
+ connect to the database
+*/
+static int ltdb_connect(struct ldb_context *ldb, const char *url,
+ unsigned int flags, const char *options[],
+ struct ldb_module **_module)
+{
+ struct ldb_module *module;
+ const char *path;
+ int tdb_flags, open_flags;
+ struct ltdb_private *ltdb;
+
+ /* parse the url */
+ if (strchr(url, ':')) {
+ if (strncmp(url, "tdb://", 6) != 0) {
+ ldb_debug(ldb, LDB_DEBUG_ERROR,
+ "Invalid tdb URL '%s'", url);
+ return -1;
+ }
+ path = url+6;
+ } else {
+ path = url;
+ }
+
+ tdb_flags = TDB_DEFAULT | TDB_SEQNUM;
+
+ /* check for the 'nosync' option */
+ if (flags & LDB_FLG_NOSYNC) {
+ tdb_flags |= TDB_NOSYNC;
+ }
+
+ /* and nommap option */
+ if (flags & LDB_FLG_NOMMAP) {
+ tdb_flags |= TDB_NOMMAP;
+ }
+
+ if (flags & LDB_FLG_RDONLY) {
+ open_flags = O_RDONLY;
+ } else {
+ open_flags = O_CREAT | O_RDWR;
+ }
+
+ ltdb = talloc_zero(ldb, struct ltdb_private);
+ if (!ltdb) {
+ ldb_oom(ldb);
+ return -1;
+ }
+
+ /* note that we use quite a large default hash size */
+ ltdb->tdb = ltdb_wrap_open(ltdb, path, 10000,
+ tdb_flags, open_flags,
+ ldb_get_create_perms(ldb), ldb);
+ if (!ltdb->tdb) {
+ ldb_debug(ldb, LDB_DEBUG_ERROR,
+ "Unable to open tdb '%s'\n", path);
+ talloc_free(ltdb);
+ return -1;
+ }
+
+ ltdb->sequence_number = 0;
+
+ module = ldb_module_new(ldb, ldb, "ldb_tdb backend", &ltdb_ops);
+ if (!module) {
+ talloc_free(ltdb);
+ return -1;
+ }
+ ldb_module_set_private(module, ltdb);
+
+ if (ltdb_cache_load(module) != 0) {
+ talloc_free(module);
+ talloc_free(ltdb);
+ return -1;
+ }
+
+ *_module = module;
+ return 0;
+}
+
+const struct ldb_backend_ops ldb_tdb_backend_ops = {
+ .name = "tdb",
+ .connect_fn = ltdb_connect
+};
diff --git a/source4/lib/ldb/ldb_tdb/ldb_tdb.h b/source4/lib/ldb/ldb_tdb/ldb_tdb.h
new file mode 100644
index 0000000000..5a1c8fee2d
--- /dev/null
+++ b/source4/lib/ldb/ldb_tdb/ldb_tdb.h
@@ -0,0 +1,135 @@
+#include "ldb_includes.h"
+#include "tdb.h"
+#include "ldb_module.h"
+
+/* this private structure is used by the ltdb backend in the
+ ldb_context */
+struct ltdb_private {
+ TDB_CONTEXT *tdb;
+ unsigned int connect_flags;
+
+ /* a double is used for portability and ease of string
+ handling. It has plenty of digits of precision */
+ unsigned long long sequence_number;
+
+ /* the low level tdb seqnum - used to avoid loading BASEINFO when
+ possible */
+ int tdb_seqnum;
+
+ struct ltdb_cache {
+ struct ldb_message *indexlist;
+ struct ldb_message *attributes;
+
+ struct {
+ char *name;
+ int flags;
+ } last_attribute;
+ } *cache;
+
+ int in_transaction;
+
+ bool check_base;
+ struct ltdb_idxptr *idxptr;
+};
+
+/*
+ the async local context
+ holds also internal search state during a full db search
+*/
+struct ltdb_req_spy {
+ struct ltdb_context *ctx;
+};
+
+struct ltdb_context {
+ struct ldb_module *module;
+ struct ldb_request *req;
+
+ bool request_terminated;
+ struct ltdb_req_spy *spy;
+
+ /* search stuff */
+ const struct ldb_parse_tree *tree;
+ struct ldb_dn *base;
+ enum ldb_scope scope;
+ const char * const *attrs;
+ struct tevent_timer *timeout_event;
+};
+
+/* special record types */
+#define LTDB_INDEX "@INDEX"
+#define LTDB_INDEXLIST "@INDEXLIST"
+#define LTDB_IDX "@IDX"
+#define LTDB_IDXPTR "@IDXPTR"
+#define LTDB_IDXATTR "@IDXATTR"
+#define LTDB_IDXONE "@IDXONE"
+#define LTDB_BASEINFO "@BASEINFO"
+#define LTDB_OPTIONS "@OPTIONS"
+#define LTDB_ATTRIBUTES "@ATTRIBUTES"
+
+/* special attribute types */
+#define LTDB_SEQUENCE_NUMBER "sequenceNumber"
+#define LTDB_CHECK_BASE "checkBaseOnSearch"
+#define LTDB_MOD_TIMESTAMP "whenChanged"
+#define LTDB_OBJECTCLASS "objectClass"
+
+/* The following definitions come from lib/ldb/ldb_tdb/ldb_cache.c */
+
+int ltdb_cache_reload(struct ldb_module *module);
+int ltdb_cache_load(struct ldb_module *module);
+int ltdb_increase_sequence_number(struct ldb_module *module);
+int ltdb_check_at_attributes_values(const struct ldb_val *value);
+
+/* The following definitions come from lib/ldb/ldb_tdb/ldb_index.c */
+
+struct ldb_parse_tree;
+
+int ltdb_search_indexed(struct ltdb_context *ctx);
+int ltdb_index_add(struct ldb_module *module, const struct ldb_message *msg);
+int ltdb_index_del(struct ldb_module *module, const struct ldb_message *msg);
+int ltdb_index_one(struct ldb_module *module, const struct ldb_message *msg, int add);
+int ltdb_reindex(struct ldb_module *module);
+int ltdb_index_transaction_start(struct ldb_module *module);
+int ltdb_index_transaction_commit(struct ldb_module *module);
+int ltdb_index_transaction_cancel(struct ldb_module *module);
+
+/* The following definitions come from lib/ldb/ldb_tdb/ldb_pack.c */
+
+int ltdb_pack_data(struct ldb_module *module,
+ const struct ldb_message *message,
+ struct TDB_DATA *data);
+void ltdb_unpack_data_free(struct ldb_module *module,
+ struct ldb_message *message);
+int ltdb_unpack_data(struct ldb_module *module,
+ const struct TDB_DATA *data,
+ struct ldb_message *message);
+
+/* The following definitions come from lib/ldb/ldb_tdb/ldb_search.c */
+
+int ltdb_has_wildcard(struct ldb_module *module, const char *attr_name,
+ const struct ldb_val *val);
+void ltdb_search_dn1_free(struct ldb_module *module, struct ldb_message *msg);
+int ltdb_search_dn1(struct ldb_module *module, struct ldb_dn *dn, struct ldb_message *msg);
+int ltdb_add_attr_results(struct ldb_module *module,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message *msg,
+ const char * const attrs[],
+ unsigned int *count,
+ struct ldb_message ***res);
+int ltdb_filter_attrs(struct ldb_message *msg, const char * const *attrs);
+int ltdb_search(struct ltdb_context *ctx);
+
+/* The following definitions come from lib/ldb/ldb_tdb/ldb_tdb.c */
+int ltdb_lock_read(struct ldb_module *module);
+int ltdb_unlock_read(struct ldb_module *module);
+struct TDB_DATA ltdb_key(struct ldb_module *module, struct ldb_dn *dn);
+int ltdb_store(struct ldb_module *module, const struct ldb_message *msg, int flgs);
+int ltdb_delete_noindex(struct ldb_module *module, struct ldb_dn *dn);
+int ltdb_modify_internal(struct ldb_module *module, const struct ldb_message *msg);
+
+int ltdb_index_del_value(struct ldb_module *module, const char *dn,
+ struct ldb_message_element *el, int v_idx);
+
+struct tdb_context *ltdb_wrap_open(TALLOC_CTX *mem_ctx,
+ const char *path, int hash_size, int tdb_flags,
+ int open_flags, mode_t mode,
+ struct ldb_context *ldb);
diff --git a/source4/lib/ldb/ldb_tdb/ldb_tdb_wrap.c b/source4/lib/ldb/ldb_tdb/ldb_tdb_wrap.c
new file mode 100644
index 0000000000..6ee8417e25
--- /dev/null
+++ b/source4/lib/ldb/ldb_tdb/ldb_tdb_wrap.c
@@ -0,0 +1,155 @@
+/*
+ ldb database library
+
+ Copyright (C) Andrew Tridgell 2005
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "ldb_tdb.h"
+
+/*
+ the purpose of this code is to work around the braindead posix locking
+ rules, to allow us to have a ldb open more than once while allowing
+ locking to work
+*/
+
+struct ltdb_wrap {
+ struct ltdb_wrap *next, *prev;
+ struct tdb_context *tdb;
+ dev_t device;
+ ino_t inode;
+};
+
+static struct ltdb_wrap *tdb_list;
+
+/* destroy the last connection to a tdb */
+static int ltdb_wrap_destructor(struct ltdb_wrap *w)
+{
+ tdb_close(w->tdb);
+ if (w->next) {
+ w->next->prev = w->prev;
+ }
+ if (w->prev) {
+ w->prev->next = w->next;
+ }
+ if (w == tdb_list) {
+ tdb_list = w->next;
+ }
+ return 0;
+}
+
+static void ltdb_log_fn(struct tdb_context *tdb, enum tdb_debug_level level, const char *fmt, ...) PRINTF_ATTRIBUTE(3, 4);
+static void ltdb_log_fn(struct tdb_context *tdb, enum tdb_debug_level level, const char *fmt, ...)
+{
+ va_list ap;
+ const char *name = tdb_name(tdb);
+ struct ldb_context *ldb = talloc_get_type(tdb_get_logging_private(tdb), struct ldb_context);
+ enum ldb_debug_level ldb_level;
+ char *message;
+
+ if (ldb == NULL)
+ return;
+
+ va_start(ap, fmt);
+ message = talloc_vasprintf(ldb, fmt, ap);
+ va_end(ap);
+
+ switch (level) {
+ case TDB_DEBUG_FATAL:
+ ldb_level = LDB_DEBUG_FATAL;
+ break;
+ case TDB_DEBUG_ERROR:
+ ldb_level = LDB_DEBUG_ERROR;
+ break;
+ case TDB_DEBUG_WARNING:
+ ldb_level = LDB_DEBUG_WARNING;
+ break;
+ case TDB_DEBUG_TRACE:
+ ldb_level = LDB_DEBUG_TRACE;
+ break;
+ default:
+ ldb_level = LDB_DEBUG_FATAL;
+ }
+
+ ldb_debug(ldb, ldb_level, "ltdb: tdb(%s): %s", name, message);
+ talloc_free(message);
+}
+
+/*
+ wrapped connection to a tdb database. The caller should _not_ free
+ this as it is not a talloc structure (as tdb does not use talloc
+ yet). It will auto-close when the caller frees the mem_ctx that is
+ passed to this call
+ */
+struct tdb_context *ltdb_wrap_open(TALLOC_CTX *mem_ctx,
+ const char *path, int hash_size,
+ int tdb_flags,
+ int open_flags, mode_t mode,
+ struct ldb_context *ldb)
+{
+ struct ltdb_wrap *w;
+ struct stat st;
+ struct tdb_logging_context log_ctx;
+
+ log_ctx.log_fn = ltdb_log_fn;
+ log_ctx.log_private = ldb;
+
+ if (stat(path, &st) == 0) {
+ for (w=tdb_list;w;w=w->next) {
+ if (st.st_dev == w->device && st.st_ino == w->inode) {
+ if (!talloc_reference(mem_ctx, w)) {
+ return NULL;
+ }
+ return w->tdb;
+ }
+ }
+ }
+
+ w = talloc(mem_ctx, struct ltdb_wrap);
+ if (w == NULL) {
+ return NULL;
+ }
+
+ w->tdb = tdb_open_ex(path, hash_size, tdb_flags, open_flags, mode, &log_ctx, NULL);
+ if (w->tdb == NULL) {
+ talloc_free(w);
+ return NULL;
+ }
+
+ if (fstat(tdb_fd(w->tdb), &st) != 0) {
+ tdb_close(w->tdb);
+ talloc_free(w);
+ return NULL;
+ }
+
+ w->device = st.st_dev;
+ w->inode = st.st_ino;
+
+ talloc_set_destructor(w, ltdb_wrap_destructor);
+
+ w->next = tdb_list;
+ w->prev = NULL;
+ if (tdb_list) {
+ tdb_list->prev = w;
+ }
+ tdb_list = w;
+
+ return w->tdb;
+}
+
diff --git a/source4/lib/ldb/libldb.m4 b/source4/lib/ldb/libldb.m4
new file mode 100644
index 0000000000..5653794a88
--- /dev/null
+++ b/source4/lib/ldb/libldb.m4
@@ -0,0 +1,7 @@
+
+# disable ldb_sqlite3 by default
+SMB_ENABLE(ldb_sqlite3, NO)
+
+#if test x"$with_sqlite3_support" = x"yes"; then
+# SMB_ENABLE(ldb_sqlite3, YES)
+#fi
diff --git a/source4/lib/ldb/mainpage.dox b/source4/lib/ldb/mainpage.dox
new file mode 100644
index 0000000000..bbd8d9c502
--- /dev/null
+++ b/source4/lib/ldb/mainpage.dox
@@ -0,0 +1,80 @@
+/**
+
+\mainpage ldb
+
+\section Overview
+
+ldb is a LDAP-like embedded database. It is not at all LDAP standards
+compliant, so if you want a standards compliant database then please
+see the excellent <a href="http://www.openldap.org/">OpenLDAP</a>
+project.<p>
+
+What ldb does is provide a fast database with an LDAP-like API
+designed to be used within an application. In some ways it can be seen
+as a intermediate solution between key-value pair databases and a real
+LDAP database.<p>
+
+ldb is the database engine used in Samba4.
+
+\section Features
+
+The main features that separate ldb from other solutions are:
+ - Safe multi-reader, multi-writer, using byte range locking
+ - LDAP-like API
+ - fast operation
+ - choice of local tdb, local sqlite3 or remote LDAP backends
+ - integration with <a href="http://talloc.samba.org">talloc</a>
+ - schema-less operation, for trivial setup
+ - modules for extensions (such as schema support)
+ - easy setup of indexes and attribute properties
+ - ldbedit tool for database editing (reminiscent of 'vipw')
+ - ldif for import/export
+
+\section Documentation
+
+ldb has limited programmer and administrator documentation:
+ - a list of <a href="globals_func.html">functions</a>
+ - a list of <a href="examples.html">examples</a>
+ - a list of <a href="annotated.html">data structures</a>
+ - a list of <a href="globals_defs.html">constants</a>
+
+If you need more information than is presented in this document, you
+may wish to look at the source code, especially the source code in the
+<a href="http://samba.org/ftp/unpacked/samba4/source/lib/ldb/tools/">tools directory</a>.
+
+ldb makes use of the LDAP Data Interchange Format (LDIF), which is
+documented in <a href="http://www.ietf.org/rfc/rfc2849.txt">RFC
+2849</a>.
+
+\section Support
+
+ldb does not currently have its own mailing list or bug tracking
+system. For now, please use the <a
+href="https://lists.samba.org/mailman/listinfo/samba-technical">samba-technical</a>
+mailing list, and the <a href="http://bugzilla.samba.org/">Samba
+bugzilla</a> bug tracking system.
+
+\section Download
+
+You can download the latest release either via rsync or anonymous
+svn. To fetch via svn use the following commands:
+
+\verbatim
+ svn co svn://svnanon.samba.org/samba/branches/SAMBA_4_0/source/lib/ldb ldb
+ svn co svn://svnanon.samba.org/samba/branches/SAMBA_4_0/source/lib/tdb tdb
+ svn co svn://svnanon.samba.org/samba/branches/SAMBA_4_0/source/lib/talloc talloc
+\endverbatim
+
+To fetch via rsync use these commands:
+
+\verbatim
+ rsync -Pavz samba.org::ftp/unpacked/samba4/source/lib/ldb .
+ rsync -Pavz samba.org::ftp/unpacked/samba4/source/lib/tdb .
+ rsync -Pavz samba.org::ftp/unpacked/samba4/source/lib/talloc .
+\endverbatim
+
+\section Credits
+
+ldb is another product of the prolific <a href="http://samba.org/~tridge/">Andrew Tridgell</a>.
+
+*/
diff --git a/source4/lib/ldb/man/ad2oLschema.1.xml b/source4/lib/ldb/man/ad2oLschema.1.xml
new file mode 100644
index 0000000000..6ae8996477
--- /dev/null
+++ b/source4/lib/ldb/man/ad2oLschema.1.xml
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<refentry id="ad2oLschema.1">
+
+<refmeta>
+ <refentrytitle>ad2oLschema</refentrytitle>
+ <manvolnum>1</manvolnum>
+</refmeta>
+
+
+<refnamediv>
+ <refname>ad2oLschema</refname>
+ <refpurpose>Converts AC-like LDAP schemas to OpenLDAP
+ compatible schema files</refpurpose>
+</refnamediv>
+
+<refsynopsisdiv>
+ <cmdsynopsis>
+ <command>ad2oLschema</command>
+ <arg choice="opt">-I INPUT-FILE</arg>
+ <arg choice="opt">-O OUTPUT-FILE</arg>
+ </cmdsynopsis>
+</refsynopsisdiv>
+
+<refsect1>
+ <title>DESCRIPTION</title>
+
+ <para>ad2oLschema is a simple tool that converts AD-like LDIF
+ schema files into OpenLDAP schema files.</para>
+</refsect1>
+
+
+<refsect1>
+ <title>OPTIONS</title>
+
+ <variablelist>
+ <varlistentry>
+ <term>-H url</term>
+ <listitem><para>URL to an LDB or LDAP server with an AD schema to read. </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-I input-file</term> <listitem><para>AD schema
+ to read. If neither this nor -H is specified, the
+ schema file will be read from standard input.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-O output-file</term>
+ <listitem><para>File to write OpenLDAP version of schema to.
+ </para></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect1>
+
+<refsect1>
+ <title>VERSION</title>
+
+ <para>This man page is correct for version 4.0 of the Samba suite.</para>
+</refsect1>
+
+<refsect1>
+ <title>SEE ALSO</title>
+
+ <para>ldb(7), ldbmodify, ldbdel, ldif(5)</para>
+
+</refsect1>
+
+<refsect1>
+ <title>AUTHOR</title>
+
+ <para> ldb was written by
+ <ulink url="http://samba.org/~tridge/">Andrew Tridgell</ulink>.
+ ad2oLschema was written by <ulink
+ url="http://samba.org/~abartlet/">Andrew Bartlett</ulink>.
+ </para>
+
+ <para>
+If you wish to report a problem or make a suggestion then please see
+the <ulink url="http://ldb.samba.org/"/> web site for
+current contact and maintainer information.
+ </para>
+
+</refsect1>
+
+</refentry>
diff --git a/source4/lib/ldb/man/ldb.3.xml b/source4/lib/ldb/man/ldb.3.xml
new file mode 100644
index 0000000000..19d9a89e10
--- /dev/null
+++ b/source4/lib/ldb/man/ldb.3.xml
@@ -0,0 +1,262 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<refentry id="ldb.3">
+
+<refmeta>
+ <refentrytitle>ldb</refentrytitle>
+ <manvolnum>3</manvolnum>
+</refmeta>
+
+<refnamediv>
+ <refname>ldb</refname>
+ <refclass>The Samba Project</refclass>
+ <refpurpose>A light-weight database library</refpurpose>
+</refnamediv>
+
+<refsynopsisdiv>
+ <synopsis>#include &lt;ldb.h&gt;</synopsis>
+</refsynopsisdiv>
+
+<refsect1>
+ <title>description</title>
+
+ <para>
+ldb is a light weight embedded database library and API. With a
+programming interface that is very similar to LDAP, ldb can store its
+data either in a tdb(3) database or in a real LDAP database.
+ </para>
+
+ <para>
+When used with the tdb backend ldb does not require any database
+daemon. Instead, ldb function calls are processed immediately by the
+ldb library, which does IO directly on the database, while allowing
+multiple readers/writers using operating system byte range locks. This
+leads to an API with very low overheads, often resulting in speeds of
+more than 10x what can be achieved with a more traditional LDAP
+architecture.
+ </para>
+
+ <para>
+In a taxonomy of databases ldb would sit half way between key/value
+pair databases (such as berkley db or tdb) and a full LDAP
+database. With a structured attribute oriented API like LDAP and good
+indexing capabilities, ldb can be used for quite sophisticated
+applications that need a light weight database, without the
+administrative overhead of a full LDAP installation.
+ </para>
+
+ <para>
+Included with ldb are a number of useful command line tools for
+manipulating a ldb database. These tools are similar in style to the
+equivalent ldap command line tools.
+ </para>
+
+ <para>
+In its default mode of operation with a tdb backend, ldb can also be
+seen as a "schema-less LDAP". By default ldb does not require a
+schema, which greatly reduces the complexity of getting started with
+ldb databases. As the complexity of you application grows you can take
+advantage of some of the optional schema-like attributes that ldb
+offers, or you can migrate to using the full LDAP api while keeping
+your exiting ldb code.
+ </para>
+
+ <para>
+If you are new to ldb, then I suggest starting with the manual pages
+for ldbsearch(1) and ldbedit(1), and experimenting with a local
+database. Then I suggest you look at the ldb_connect(3) and
+ldb_search(3) manual pages.
+ </para>
+</refsect1>
+
+<refsect1>
+ <title>TOOLS</title>
+
+ <itemizedlist>
+ <listitem><para>
+ <application>ldbsearch(1)</application>
+ - command line ldb search utility
+ </para></listitem>
+
+ <listitem><para>
+ <application>ldbedit(1)</application>
+ - edit all or part of a ldb database using your favourite editor
+ </para></listitem>
+
+ <listitem><para>
+ <application>ldbadd(1)</application>
+ - add records to a ldb database using LDIF formatted input
+ </para></listitem>
+
+ <listitem><para>
+ <application>ldbdel(1)</application>
+ - delete records from a ldb database
+ </para></listitem>
+
+ <listitem><para>
+ <application>ldbmodify(1)</application>
+ - modify records in a ldb database using LDIF formatted input
+ </para></listitem>
+ </itemizedlist>
+</refsect1>
+
+<refsect1>
+ <title>FUNCTIONS</title>
+
+ <itemizedlist>
+ <listitem><para>
+ <function>ldb_connect(3)</function>
+ - connect to a ldb backend
+ </para></listitem>
+
+ <listitem><para>
+ <function>ldb_search(3)</function>
+ - perform a database search
+ </para></listitem>
+
+ <listitem><para>
+ <function>ldb_add(3)</function>
+ - add a record to the database
+ </para></listitem>
+
+ <listitem><para>
+ <function>ldb_delete(3)</function>
+ - delete a record from the database
+ </para></listitem>
+
+ <listitem><para>
+ <function>ldb_modify(3)</function>
+ - modify a record in the database
+ </para></listitem>
+
+ <listitem><para>
+ <function>ldb_errstring(3)</function>
+ - retrieve extended error information from the last operation
+ </para></listitem>
+
+ <listitem><para>
+ <function>ldb_ldif_write(3)</function>
+ - write a LDIF formatted message
+ </para></listitem>
+
+ <listitem><para>
+ <function>ldb_ldif_write_file(3)</function>
+ - write a LDIF formatted message to a file
+ </para></listitem>
+
+ <listitem><para>
+ <function>ldb_ldif_read(3)</function>
+ - read a LDIF formatted message
+ </para></listitem>
+
+ <listitem><para>
+ <function>ldb_ldif_read_free(3)</function>
+ - free the result of a ldb_ldif_read()
+ </para></listitem>
+
+ <listitem><para>
+ <function>ldb_ldif_read_file(3)</function>
+ - read a LDIF message from a file
+ </para></listitem>
+
+ <listitem><para>
+ <function>ldb_ldif_read_string(3)</function>
+ - read a LDIF message from a string
+ </para></listitem>
+
+ <listitem><para>
+ <function>ldb_msg_find_element(3)</function>
+ - find an element in a ldb_message
+ </para></listitem>
+
+ <listitem><para>
+ <function>ldb_val_equal_exact(3)</function>
+ - compare two ldb_val structures
+ </para></listitem>
+
+ <listitem><para>
+ <function>ldb_msg_find_val(3)</function>
+ - find an element by value
+ </para></listitem>
+
+ <listitem><para>
+ <function>ldb_msg_add_empty(3)</function>
+ - add an empty message element to a ldb_message
+ </para></listitem>
+
+
+ <listitem><para>
+ <function>ldb_msg_add(3)</function>
+ - add a non-empty message element to a ldb_message
+ </para></listitem>
+
+
+ <listitem><para>
+ <function>ldb_msg_element_compare(3)</function>
+ - compare two ldb_message_element structures
+ </para></listitem>
+
+
+ <listitem><para>
+ <function>ldb_msg_find_int(3)</function>
+ - return an integer value from a ldb_message
+ </para></listitem>
+
+
+ <listitem><para>
+ <function>ldb_msg_find_uint(3)</function>
+ - return an unsigned integer value from a ldb_message
+ </para></listitem>
+
+
+ <listitem><para>
+ <function>ldb_msg_find_double(3)</function>
+ - return a double value from a ldb_message
+ </para></listitem>
+
+
+ <listitem><para>
+ <function>ldb_msg_find_string(3)</function>
+ - return a string value from a ldb_message
+ </para></listitem>
+
+
+ <listitem><para>
+ <function>ldb_set_alloc(3)</function>
+ - set the memory allocation function to be used by ldb
+ </para></listitem>
+
+
+ <listitem><para>
+ <function>ldb_set_debug(3)</function>
+ - set a debug handler to be used by ldb
+ </para></listitem>
+
+
+ <listitem><para>
+ <function>ldb_set_debug_stderr(3)</function>
+ - set a debug handler for stderr output
+ </para></listitem>
+ </itemizedlist>
+</refsect1>
+
+<refsect1>
+ <title>Author</title>
+
+ <para>
+ ldb was written by
+ <ulink url="http://samba.org/~tridge/">Andrew Tridgell</ulink>.
+ </para>
+
+ <para>
+If you wish to report a problem or make a suggestion then please see
+the <ulink url="http://ldb.samba.org/"/> web site for
+current contact and maintainer information.
+ </para>
+
+ <para>
+ldb is released under the GNU Lesser General Public License version 2
+or later. Please see the file COPYING for license details.
+ </para>
+</refsect1>
+</refentry>
diff --git a/source4/lib/ldb/man/ldbadd.1.xml b/source4/lib/ldb/man/ldbadd.1.xml
new file mode 100644
index 0000000000..7ad0f835d0
--- /dev/null
+++ b/source4/lib/ldb/man/ldbadd.1.xml
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<refentry id="ldbadd.1">
+
+<refmeta>
+ <refentrytitle>ldbadd</refentrytitle>
+ <manvolnum>1</manvolnum>
+</refmeta>
+
+
+<refnamediv>
+ <refname>ldbadd</refname>
+ <refpurpose>Command-line utility for adding records to an LDB</refpurpose>
+</refnamediv>
+
+<refsynopsisdiv>
+ <cmdsynopsis>
+ <command>ldbadd</command>
+ <arg choice="opt">-h</arg>
+ <arg choice="opt">-H LDB-URL</arg>
+ <arg choice="opt">ldif-file1</arg>
+ <arg choice="opt">ldif-file2</arg>
+ <arg choice="opt">...</arg>
+ </cmdsynopsis>
+</refsynopsisdiv>
+
+<refsect1>
+ <title>DESCRIPTION</title>
+
+ <para>ldbadd adds records to an ldb(7) database. It reads
+ the ldif(5) files specified on the command line and adds
+ the records from these files to the LDB database, which is specified
+ by the -H option or the LDB_URL environment variable.
+ </para>
+
+ <para>If - is specified as a ldb file, the ldif input is read from
+ standard input.</para>
+
+</refsect1>
+
+
+<refsect1>
+ <title>OPTIONS</title>
+
+ <variablelist>
+ <varlistentry>
+ <term>-h</term>
+ <listitem><para>
+ Show list of available options.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-H &lt;ldb-url&gt;</term>
+ <listitem><para>
+ LDB URL to connect to. See ldb(7) for details.
+ </para></listitem>
+ </varlistentry>
+
+ </variablelist>
+
+</refsect1>
+
+<refsect1>
+ <title>ENVIRONMENT</title>
+
+ <variablelist>
+ <varlistentry><term>LDB_URL</term>
+ <listitem><para>LDB URL to connect to (can be overrided by using the
+ -H command-line option.)</para></listitem>
+ </varlistentry>
+ </variablelist>
+
+</refsect1>
+
+<refsect1>
+ <title>VERSION</title>
+
+ <para>This man page is correct for version 4.0 of the Samba suite.</para>
+</refsect1>
+
+<refsect1>
+ <title>SEE ALSO</title>
+
+ <para>ldb(7), ldbmodify, ldbdel, ldif(5)</para>
+
+</refsect1>
+
+<refsect1>
+ <title>AUTHOR</title>
+
+ <para> ldb was written by
+ <ulink url="http://samba.org/~tridge/">Andrew Tridgell</ulink>.
+ </para>
+
+ <para>
+If you wish to report a problem or make a suggestion then please see
+the <ulink url="http://ldb.samba.org/"/> web site for
+current contact and maintainer information.
+ </para>
+
+ <para>This manpage was written by Jelmer Vernooij.</para>
+
+</refsect1>
+
+</refentry>
diff --git a/source4/lib/ldb/man/ldbdel.1.xml b/source4/lib/ldb/man/ldbdel.1.xml
new file mode 100644
index 0000000000..7dfc7366f6
--- /dev/null
+++ b/source4/lib/ldb/man/ldbdel.1.xml
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<refentry id="ldbdel.1">
+
+<refmeta>
+ <refentrytitle>ldbdel</refentrytitle>
+ <manvolnum>1</manvolnum>
+</refmeta>
+
+
+<refnamediv>
+ <refname>ldbdel</refname>
+ <refpurpose>Command-line program for deleting LDB records</refpurpose>
+</refnamediv>
+
+<refsynopsisdiv>
+ <cmdsynopsis>
+ <command>ldbdel</command>
+ <arg choice="opt">-h</arg>
+ <arg choice="opt">-H LDB-URL</arg>
+ <arg choice="opt">dn</arg>
+ <arg choice="opt">...</arg>
+ </cmdsynopsis>
+</refsynopsisdiv>
+
+<refsect1>
+ <title>DESCRIPTION</title>
+
+ <para>ldbdel deletes records from an ldb(7) database.
+ It deletes the records identified by the dn's specified
+ on the command-line. </para>
+
+ <para>ldbdel uses either the database that is specified with
+ the -H option or the database specified by the LDB_URL environment
+ variable.</para>
+
+</refsect1>
+
+
+<refsect1>
+ <title>OPTIONS</title>
+
+ <variablelist>
+ <varlistentry>
+ <term>-h</term>
+ <listitem><para>
+ Show list of available options.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-H &lt;ldb-url&gt;</term>
+ <listitem><para>
+ LDB URL to connect to. See ldb(7) for details.
+ </para></listitem>
+ </varlistentry>
+
+ </variablelist>
+
+</refsect1>
+
+<refsect1>
+ <title>ENVIRONMENT</title>
+
+ <variablelist>
+ <varlistentry><term>LDB_URL</term>
+ <listitem><para>LDB URL to connect to (can be overrided by using the
+ -H command-line option.)</para></listitem>
+ </varlistentry>
+ </variablelist>
+
+</refsect1>
+
+<refsect1>
+ <title>VERSION</title>
+
+ <para>This man page is correct for version 4.0 of the Samba suite.</para>
+</refsect1>
+
+<refsect1>
+ <title>SEE ALSO</title>
+
+ <para>ldb(7), ldbmodify, ldbadd, ldif(5)</para>
+
+</refsect1>
+
+<refsect1>
+ <title>AUTHOR</title>
+
+ <para> ldb was written by
+ <ulink url="http://samba.org/~tridge/">Andrew Tridgell</ulink>.
+ </para>
+
+ <para>
+If you wish to report a problem or make a suggestion then please see
+the <ulink url="http://ldb.samba.org/"/> web site for
+current contact and maintainer information.
+ </para>
+
+ <para>ldbdel was written by Andrew Tridgell.</para>
+
+ <para>This manpage was written by Jelmer Vernooij.</para>
+
+</refsect1>
+
+</refentry>
diff --git a/source4/lib/ldb/man/ldbedit.1.xml b/source4/lib/ldb/man/ldbedit.1.xml
new file mode 100644
index 0000000000..15c69b1b25
--- /dev/null
+++ b/source4/lib/ldb/man/ldbedit.1.xml
@@ -0,0 +1,200 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<refentry id="ldbedit.1">
+
+ <refmeta>
+ <refentrytitle>ldbedit</refentrytitle>
+ <manvolnum>1</manvolnum>
+ </refmeta>
+
+
+ <refnamediv>
+ <refname>ldbedit</refname>
+ <refpurpose>Edit LDB databases using your preferred editor</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <cmdsynopsis>
+ <command>ldbedit</command>
+ <arg choice="opt">-?</arg>
+ <arg choice="opt">--usage</arg>
+ <arg choice="opt">-s base|one|sub</arg>
+ <arg choice="opt">-b basedn</arg>
+ <arg choice="opt">-a</arg>
+ <arg choice="opt">-e editor</arg>
+ <arg choice="opt">-H LDB-URL</arg>
+ <arg choice="opt">expression</arg>
+ <arg rep="repeat" choice="opt">attributes</arg>
+ </cmdsynopsis>
+</refsynopsisdiv>
+
+<refsect1>
+ <title>DESCRIPTION</title>
+
+ <para>ldbedit is a utility that allows you to edit LDB entries (in
+ tdb files, sqlite files or LDAP servers) using your preferred editor.
+ ldbedit generates an LDIF file based on your query, allows you to edit
+ the LDIF, and then merges that LDIF back into the LDB backend.
+ </para>
+
+</refsect1>
+
+
+ <refsect1>
+ <title>OPTIONS</title>
+
+ <variablelist>
+ <varlistentry>
+ <term>-?</term>
+ <term>--help</term>
+ <listitem>
+ <para>
+ Show list of available options, and a phrase describing what that option
+ does.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>--usage</term>
+ <listitem>
+ <para>
+ Show list of available options. This is similar to the help option,
+ however it does not provide any description, and is hence shorter.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-H &lt;ldb-url&gt;</term>
+ <listitem>
+ <para>
+ LDB URL to connect to. For a tdb database,
+ this will be of the form
+ tdb://<replaceable>filename</replaceable>.
+ For a LDAP connection over unix domain
+ sockets, this will be of the form
+ ldapi://<replaceable>socket</replaceable>. For
+ a (potentially remote) LDAP connection over
+ TCP, this will be of the form
+ ldap://<replaceable>hostname</replaceable>. For
+ an SQLite database, this will be of the form
+ sqlite://<replaceable>filename</replaceable>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-s one|sub|base</term>
+ <listitem><para>Search scope to use. One-level, subtree or base.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-a</term>
+ <term>-all</term>
+ <listitem>
+ <para>Edit all records. This allows you to
+ apply the same change to a number of records
+ at once. You probably want to combine this
+ with an expression of the form
+ "objectclass=*".
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-e editor</term>
+ <term>--editor editor</term>
+ <listitem>
+ <para>Specify the editor that should be used (overrides
+ the VISUAL and EDITOR environment
+ variables). If this option is not used, and
+ neither VISUAL nor EDITOR environment variables
+ are set, then the vi editor will be used.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-b basedn</term>
+ <listitem><para>Specify Base Distinguished Name to use.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-v</term>
+ <term>--verbose</term>
+ <listitem>
+ <para>Make ldbedit more verbose about the
+ operations that are being performed. Without
+ this option, ldbedit will only provide a
+ summary change line.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+
+ </refsect1>
+
+ <refsect1>
+ <title>ENVIRONMENT</title>
+
+ <variablelist>
+ <varlistentry>
+ <term>LDB_URL</term>
+ <listitem>
+ <para>LDB URL to connect to. This can be
+ overridden by using the -H command-line option.)
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>VISUAL and EDITOR</term>
+ <listitem>
+ <para>
+ Environment variables used to determine what
+ editor to use. VISUAL takes precedence over
+ EDITOR, and both are overridden by the
+ -e command-line option.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ </refsect1>
+
+ <refsect1>
+ <title>VERSION</title>
+
+ <para>This man page is correct for version 4.0 of the Samba suite.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>SEE ALSO</title>
+
+ <para>ldb(7), ldbmodify(1), ldbdel(1), ldif(5), vi(1)</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>AUTHOR</title>
+
+ <para>
+ ldb was written by
+ <ulink url="http://samba.org/~tridge/">Andrew Tridgell</ulink>.
+ </para>
+
+ <para>
+ If you wish to report a problem or make a suggestion then please see
+ the <ulink url="http://ldb.samba.org/"/> web site for
+ current contact and maintainer information.
+ </para>
+
+ <para>
+ This manpage was written by Jelmer Vernooij and updated
+ by Brad Hards.
+ </para>
+
+ </refsect1>
+
+</refentry>
diff --git a/source4/lib/ldb/man/ldbmodify.1.xml b/source4/lib/ldb/man/ldbmodify.1.xml
new file mode 100644
index 0000000000..bc19647785
--- /dev/null
+++ b/source4/lib/ldb/man/ldbmodify.1.xml
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<refentry id="ldbmodify.1">
+
+<refmeta>
+ <refentrytitle>ldbmodify</refentrytitle>
+ <manvolnum>1</manvolnum>
+</refmeta>
+
+
+<refnamediv>
+ <refname>ldbmodify</refname>
+ <refpurpose>Modify records in a LDB database</refpurpose>
+</refnamediv>
+
+<refsynopsisdiv>
+ <cmdsynopsis>
+ <command>ldbmodify</command>
+ <arg choice="opt">-H LDB-URL</arg>
+ <arg choice="opt">ldif-file</arg>
+ </cmdsynopsis>
+</refsynopsisdiv>
+
+<refsect1>
+ <title>DESCRIPTION</title>
+
+ <para>
+ ldbmodify changes, adds and deletes records in a LDB database.
+ The changes that should be made to the LDB database are read from
+ the specified LDIF-file. If - is specified as the filename, input is read from stdin.
+ </para>
+
+ <para>For now, see ldapmodify(1) for details on the LDIF file format.</para>
+
+</refsect1>
+
+
+<refsect1>
+ <title>OPTIONS</title>
+
+ <variablelist>
+ <varlistentry>
+ <term>-H &lt;ldb-url&gt;</term>
+ <listitem><para>
+ LDB URL to connect to. See ldb(7) for details.
+ </para></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect1>
+
+<refsect1>
+ <title>ENVIRONMENT</title>
+
+ <variablelist>
+ <varlistentry><term>LDB_URL</term>
+ <listitem><para>LDB URL to connect to (can be overrided by using the
+ -H command-line option.)</para></listitem>
+ </varlistentry>
+ </variablelist>
+
+</refsect1>
+
+<refsect1>
+ <title>VERSION</title>
+
+ <para>This man page is correct for version 4.0 of the Samba suite.</para>
+</refsect1>
+
+<refsect1>
+ <title>SEE ALSO</title>
+
+ <para>ldb(7), ldbedit</para>
+
+</refsect1>
+
+<refsect1>
+ <title>AUTHOR</title>
+
+ <para> ldb was written by
+ <ulink url="http://samba.org/~tridge/">Andrew Tridgell</ulink>.
+ </para>
+
+ <para>
+If you wish to report a problem or make a suggestion then please see
+the <ulink url="http://ldb.samba.org/"/> web site for
+current contact and maintainer information.
+ </para>
+
+ <para>This manpage was written by Jelmer Vernooij.</para>
+
+</refsect1>
+
+</refentry>
diff --git a/source4/lib/ldb/man/ldbrename.1.xml b/source4/lib/ldb/man/ldbrename.1.xml
new file mode 100644
index 0000000000..391ec84ccc
--- /dev/null
+++ b/source4/lib/ldb/man/ldbrename.1.xml
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<refentry id="ldbrename.1">
+
+<refmeta>
+ <refentrytitle>ldbrename</refentrytitle>
+ <manvolnum>1</manvolnum>
+</refmeta>
+
+
+<refnamediv>
+ <refname>ldbrename</refname>
+ <refpurpose>Edit LDB databases using your favorite editor</refpurpose>
+</refnamediv>
+
+<refsynopsisdiv>
+ <cmdsynopsis>
+ <command>ldbrename</command>
+ <arg choice="opt">-h</arg>
+ <arg choice="opt">-o options</arg>
+ <arg choice="req">olddn</arg>
+ <arg choice="req">newdb</arg>
+ </cmdsynopsis>
+</refsynopsisdiv>
+
+<refsect1>
+ <title>DESCRIPTION</title>
+
+ <para>ldbrename is a utility that allows you to rename trees in
+ an LDB database based by DN. This utility takes
+ two arguments: the original
+ DN name of the top element and the DN to change it to.
+ </para>
+
+</refsect1>
+
+
+<refsect1>
+ <title>OPTIONS</title>
+
+ <variablelist>
+ <varlistentry>
+ <term>-h</term>
+ <listitem><para>
+ Show list of available options.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-H &lt;ldb-url&gt;</term>
+ <listitem><para>
+ LDB URL to connect to. See ldb(7) for details.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-o options</term>
+ <listitem><para>Extra ldb options, such as
+ modules.</para></listitem>
+ </varlistentry>
+
+ </variablelist>
+
+</refsect1>
+
+<refsect1>
+ <title>ENVIRONMENT</title>
+
+ <variablelist>
+ <varlistentry><term>LDB_URL</term>
+ <listitem><para>LDB URL to connect to (can be overrided by using the
+ -H command-line option.)</para></listitem>
+ </varlistentry>
+ </variablelist>
+
+</refsect1>
+
+<refsect1>
+ <title>VERSION</title>
+
+ <para>This man page is correct for version 4.0 of the Samba suite.</para>
+</refsect1>
+
+<refsect1>
+ <title>SEE ALSO</title>
+
+ <para>ldb(7), ldbmodify, ldbdel, ldif(5)</para>
+
+</refsect1>
+
+<refsect1>
+ <title>AUTHOR</title>
+
+ <para> ldb was written by
+ <ulink url="http://samba.org/~tridge/">Andrew Tridgell</ulink>.
+ </para>
+
+ <para>
+If you wish to report a problem or make a suggestion then please see
+the <ulink url="http://ldb.samba.org/"/> web site for
+current contact and maintainer information.
+ </para>
+
+ <para>This manpage was written by Jelmer Vernooij.</para>
+
+</refsect1>
+
+</refentry>
diff --git a/source4/lib/ldb/man/ldbsearch.1.xml b/source4/lib/ldb/man/ldbsearch.1.xml
new file mode 100644
index 0000000000..ed3749b920
--- /dev/null
+++ b/source4/lib/ldb/man/ldbsearch.1.xml
@@ -0,0 +1,119 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<refentry id="ldbsearch.1">
+
+<refmeta>
+ <refentrytitle>ldbsearch</refentrytitle>
+ <manvolnum>1</manvolnum>
+</refmeta>
+
+
+<refnamediv>
+ <refname>ldbsearch</refname>
+ <refpurpose>Search for records in a LDB database</refpurpose>
+</refnamediv>
+
+<refsynopsisdiv>
+ <cmdsynopsis>
+ <command>ldbsearch</command>
+ <arg choice="opt">-h</arg>
+ <arg choice="opt">-s base|one|sub</arg>
+ <arg choice="opt">-b basedn</arg>
+ <arg chioce="opt">-i</arg>
+ <arg choice="opt">-H LDB-URL</arg>
+ <arg choice="opt">expression</arg>
+ <arg choice="opt">attributes</arg>
+ </cmdsynopsis>
+</refsynopsisdiv>
+
+<refsect1>
+ <title>DESCRIPTION</title>
+
+ <para>ldbsearch searches a LDB database for records matching the
+ specified expression (see the ldapsearch(1) manpage for
+ a description of the expression format). For each
+ record, the specified attributes are printed.
+ </para>
+
+</refsect1>
+
+
+<refsect1>
+ <title>OPTIONS</title>
+
+ <variablelist>
+ <varlistentry>
+ <term>-h</term>
+ <listitem><para>
+ Show list of available options.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-H &lt;ldb-url&gt;</term>
+ <listitem><para>
+ LDB URL to connect to. See ldb(7) for details.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-s one|sub|base</term>
+ <listitem><para>Search scope to use. One-level, subtree or base.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-i</term>
+ <listitem><para>Read search expressions from stdin. </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-b basedn</term>
+ <listitem><para>Specify Base DN to use.</para></listitem>
+ </varlistentry>
+
+ </variablelist>
+
+</refsect1>
+
+<refsect1>
+ <title>ENVIRONMENT</title>
+
+ <variablelist>
+ <varlistentry><term>LDB_URL</term>
+ <listitem><para>LDB URL to connect to (can be overrided by using the
+ -H command-line option.)</para></listitem>
+ </varlistentry>
+ </variablelist>
+
+</refsect1>
+
+<refsect1>
+ <title>VERSION</title>
+
+ <para>This man page is correct for version 4.0 of the Samba suite.</para>
+</refsect1>
+
+<refsect1>
+ <title>SEE ALSO</title>
+
+ <para>ldb(7), ldbedit(1)</para>
+
+</refsect1>
+
+<refsect1>
+ <title>AUTHOR</title>
+
+ <para> ldb was written by
+ <ulink url="http://samba.org/~tridge/">Andrew Tridgell</ulink>.
+ </para>
+
+ <para>
+If you wish to report a problem or make a suggestion then please see
+the <ulink url="http://ldb.samba.org/"/> web site for
+current contact and maintainer information.
+ </para>
+
+ <para>This manpage was written by Jelmer Vernooij.</para>
+
+</refsect1>
+
+</refentry>
diff --git a/source4/lib/ldb/man/oLschema2ldif.1.xml b/source4/lib/ldb/man/oLschema2ldif.1.xml
new file mode 100644
index 0000000000..b1e681be4e
--- /dev/null
+++ b/source4/lib/ldb/man/oLschema2ldif.1.xml
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<refentry id="oLschema2ldif.1">
+
+<refmeta>
+ <refentrytitle>oLschema2ldif</refentrytitle>
+ <manvolnum>1</manvolnum>
+</refmeta>
+
+
+<refnamediv>
+ <refname>oLschema2ldif</refname>
+ <refpurpose>Converts LDAP schema's to LDB-compatible LDIF</refpurpose>
+</refnamediv>
+
+<refsynopsisdiv>
+ <cmdsynopsis>
+ <command>oLschema2ldif</command>
+ <arg choice="opt">-I INPUT-FILE</arg>
+ <arg choice="opt">-O OUTPUT-FILE</arg>
+ </cmdsynopsis>
+</refsynopsisdiv>
+
+<refsect1>
+ <title>DESCRIPTION</title>
+
+ <para>oLschema2ldif is a simple tool that converts standard OpenLDAP schema files to a LDIF format that is understood by LDB.</para>
+</refsect1>
+
+
+<refsect1>
+ <title>OPTIONS</title>
+
+ <variablelist>
+ <varlistentry>
+ <term>-I input-file</term>
+ <listitem><para>OpenLDAP schema to read. If none are specified,
+the schema file will be read from standard input.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-O output-file</term>
+ <listitem><para>File to write ldif version of schema to.
+ </para></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect1>
+
+<refsect1>
+ <title>VERSION</title>
+
+ <para>This man page is correct for version 4.0 of the Samba suite.</para>
+</refsect1>
+
+<refsect1>
+ <title>SEE ALSO</title>
+
+ <para>ldb(7), ldbmodify, ldbdel, ldif(5)</para>
+
+</refsect1>
+
+<refsect1>
+ <title>AUTHOR</title>
+
+ <para> ldb was written by
+ <ulink url="http://samba.org/~tridge/">Andrew Tridgell</ulink>.
+ oLschema2ldif was written by <ulink url="mailto:idra@samba.org">Simo Sorce</ulink>.
+ </para>
+
+ <para>
+If you wish to report a problem or make a suggestion then please see
+the <ulink url="http://ldb.samba.org/"/> web site for
+current contact and maintainer information.
+ </para>
+
+</refsect1>
+
+</refentry>
diff --git a/source4/lib/ldb/modules/asq.c b/source4/lib/ldb/modules/asq.c
new file mode 100644
index 0000000000..475b609e41
--- /dev/null
+++ b/source4/lib/ldb/modules/asq.c
@@ -0,0 +1,406 @@
+/*
+ ldb database library
+
+ Copyright (C) Simo Sorce 2005-2008
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb attribute scoped query control module
+ *
+ * Description: this module searches all the the objects pointed
+ * by the DNs contained in the references attribute
+ *
+ * Author: Simo Sorce
+ */
+
+#include "ldb_module.h"
+
+struct asq_context {
+
+ enum {ASQ_SEARCH_BASE, ASQ_SEARCH_MULTI} step;
+
+ struct ldb_module *module;
+ struct ldb_request *req;
+
+ struct ldb_asq_control *asq_ctrl;
+
+ const char * const *req_attrs;
+ char *req_attribute;
+ enum {
+ ASQ_CTRL_SUCCESS = 0,
+ ASQ_CTRL_INVALID_ATTRIBUTE_SYNTAX = 21,
+ ASQ_CTRL_UNWILLING_TO_PERFORM = 53,
+ ASQ_CTRL_AFFECTS_MULTIPLE_DSA = 71
+ } asq_ret;
+
+ struct ldb_reply *base_res;
+
+ struct ldb_request **reqs;
+ int num_reqs;
+ int cur_req;
+
+ struct ldb_control **controls;
+};
+
+static struct asq_context *asq_context_init(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct asq_context *ac;
+
+ ldb = ldb_module_get_ctx(module);
+
+ ac = talloc_zero(req, struct asq_context);
+ if (ac == NULL) {
+ ldb_oom(ldb);
+ return NULL;
+ }
+
+ ac->module = module;
+ ac->req = req;
+
+ return ac;
+}
+
+static int asq_search_continue(struct asq_context *ac);
+
+static int asq_search_terminate(struct asq_context *ac)
+{
+ struct ldb_asq_control *asq;
+ int i;
+
+ if (ac->controls) {
+ for (i = 0; ac->controls[i]; i++) /* count em */ ;
+ } else {
+ i = 0;
+ }
+
+ ac->controls = talloc_realloc(ac, ac->controls, struct ldb_control *, i + 2);
+
+ if (ac->controls == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->controls[i] = talloc(ac->controls, struct ldb_control);
+ if (ac->controls[i] == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->controls[i]->oid = LDB_CONTROL_ASQ_OID;
+ ac->controls[i]->critical = 0;
+
+ asq = talloc_zero(ac->controls[i], struct ldb_asq_control);
+ if (asq == NULL)
+ return LDB_ERR_OPERATIONS_ERROR;
+
+ asq->result = ac->asq_ret;
+
+ ac->controls[i]->data = asq;
+
+ ac->controls[i + 1] = NULL;
+
+ return ldb_module_done(ac->req, ac->controls, NULL, LDB_SUCCESS);
+}
+
+static int asq_base_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+ struct asq_context *ac;
+ int ret;
+
+ ac = talloc_get_type(req->context, struct asq_context);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+ ac->base_res = talloc_move(ac, &ares);
+ break;
+
+ case LDB_REPLY_REFERRAL:
+ /* ignore referrals */
+ talloc_free(ares);
+ break;
+
+ case LDB_REPLY_DONE:
+
+ talloc_free(ares);
+
+ /* next step */
+ ret = asq_search_continue(ac);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+ break;
+
+ }
+ return LDB_SUCCESS;
+}
+
+static int asq_reqs_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+ struct asq_context *ac;
+ int ret;
+
+ ac = talloc_get_type(req->context, struct asq_context);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+ /* pass the message up to the original callback as we
+ * do not have to elaborate on it any further */
+ ret = ldb_module_send_entry(ac->req, ares->message, ares->controls);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+ talloc_free(ares);
+ break;
+
+ case LDB_REPLY_REFERRAL:
+ /* ignore referrals */
+ talloc_free(ares);
+ break;
+
+ case LDB_REPLY_DONE:
+
+ talloc_free(ares);
+
+ ret = asq_search_continue(ac);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+ break;
+ }
+
+ return LDB_SUCCESS;
+}
+
+static int asq_build_first_request(struct asq_context *ac, struct ldb_request **base_req)
+{
+ struct ldb_context *ldb;
+ const char **base_attrs;
+ int ret;
+
+ ldb = ldb_module_get_ctx(ac->module);
+
+ ac->req_attrs = ac->req->op.search.attrs;
+ ac->req_attribute = talloc_strdup(ac, ac->asq_ctrl->source_attribute);
+ if (ac->req_attribute == NULL)
+ return LDB_ERR_OPERATIONS_ERROR;
+
+ base_attrs = talloc_array(ac, const char *, 2);
+ if (base_attrs == NULL) return LDB_ERR_OPERATIONS_ERROR;
+
+ base_attrs[0] = talloc_strdup(base_attrs, ac->asq_ctrl->source_attribute);
+ if (base_attrs[0] == NULL) return LDB_ERR_OPERATIONS_ERROR;
+
+ base_attrs[1] = NULL;
+
+ ret = ldb_build_search_req(base_req, ldb, ac,
+ ac->req->op.search.base,
+ LDB_SCOPE_BASE,
+ NULL,
+ (const char * const *)base_attrs,
+ NULL,
+ ac, asq_base_callback,
+ ac->req);
+ if (ret != LDB_SUCCESS) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ return LDB_SUCCESS;
+}
+
+static int asq_build_multiple_requests(struct asq_context *ac, bool *terminated)
+{
+ struct ldb_context *ldb;
+ struct ldb_control **saved_controls;
+ struct ldb_control *control;
+ struct ldb_dn *dn;
+ struct ldb_message_element *el;
+ int ret, i;
+
+ if (ac->base_res == NULL) {
+ return LDB_ERR_NO_SUCH_OBJECT;
+ }
+
+ ldb = ldb_module_get_ctx(ac->module);
+
+ el = ldb_msg_find_element(ac->base_res->message, ac->req_attribute);
+ /* no values found */
+ if (el == NULL) {
+ ac->asq_ret = ASQ_CTRL_SUCCESS;
+ *terminated = true;
+ return asq_search_terminate(ac);
+ }
+
+ ac->num_reqs = el->num_values;
+ ac->cur_req = 0;
+ ac->reqs = talloc_array(ac, struct ldb_request *, ac->num_reqs);
+ if (ac->reqs == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ for (i = 0; i < el->num_values; i++) {
+
+ dn = ldb_dn_new(ac, ldb,
+ (const char *)el->values[i].data);
+ if ( ! ldb_dn_validate(dn)) {
+ ac->asq_ret = ASQ_CTRL_INVALID_ATTRIBUTE_SYNTAX;
+ *terminated = true;
+ return asq_search_terminate(ac);
+ }
+
+ ret = ldb_build_search_req_ex(&ac->reqs[i],
+ ldb, ac,
+ dn, LDB_SCOPE_BASE,
+ ac->req->op.search.tree,
+ ac->req_attrs,
+ ac->req->controls,
+ ac, asq_reqs_callback,
+ ac->req);
+ if (ret != LDB_SUCCESS) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* remove the ASQ control itself */
+ control = ldb_request_get_control(ac->req, LDB_CONTROL_ASQ_OID);
+ if (!save_controls(control, ac->reqs[i], &saved_controls)) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ }
+
+ return LDB_SUCCESS;
+}
+
+static int asq_search_continue(struct asq_context *ac)
+{
+ struct ldb_context *ldb;
+ bool terminated = false;
+ int ret;
+
+ ldb = ldb_module_get_ctx(ac->module);
+
+ switch (ac->step) {
+ case ASQ_SEARCH_BASE:
+
+ /* build up the requests call chain */
+ ret = asq_build_multiple_requests(ac, &terminated);
+ if (ret != LDB_SUCCESS || terminated) {
+ return ret;
+ }
+
+ ac->step = ASQ_SEARCH_MULTI;
+
+ return ldb_request(ldb, ac->reqs[ac->cur_req]);
+
+ case ASQ_SEARCH_MULTI:
+
+ ac->cur_req++;
+
+ if (ac->cur_req == ac->num_reqs) {
+ /* done */
+ return asq_search_terminate(ac);
+ }
+
+ return ldb_request(ldb, ac->reqs[ac->cur_req]);
+ }
+
+ return LDB_ERR_OPERATIONS_ERROR;
+}
+
+static int asq_search(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct ldb_request *base_req;
+ struct ldb_control *control;
+ struct asq_context *ac;
+ int ret;
+
+ ldb = ldb_module_get_ctx(module);
+
+ /* check if there's a paged request control */
+ control = ldb_request_get_control(req, LDB_CONTROL_ASQ_OID);
+ if (control == NULL) {
+ /* not found go on */
+ return ldb_next_request(module, req);
+ }
+
+ ac = asq_context_init(module, req);
+ if (!ac) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* check the search is well formed */
+ if (req->op.search.scope != LDB_SCOPE_BASE) {
+ ac->asq_ret = ASQ_CTRL_UNWILLING_TO_PERFORM;
+ return asq_search_terminate(ac);
+ }
+
+ ac->asq_ctrl = talloc_get_type(control->data, struct ldb_asq_control);
+ if (!ac->asq_ctrl) {
+ return LDB_ERR_PROTOCOL_ERROR;
+ }
+
+ ret = asq_build_first_request(ac, &base_req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ ac->step = ASQ_SEARCH_BASE;
+
+ return ldb_request(ldb, base_req);
+}
+
+static int asq_init(struct ldb_module *module)
+{
+ struct ldb_context *ldb;
+ int ret;
+
+ ldb = ldb_module_get_ctx(module);
+
+ ret = ldb_mod_register_control(module, LDB_CONTROL_ASQ_OID);
+ if (ret != LDB_SUCCESS) {
+ ldb_debug(ldb, LDB_DEBUG_WARNING, "asq: Unable to register control with rootdse!\n");
+ }
+
+ return ldb_next_init(module);
+}
+
+const struct ldb_module_ops ldb_asq_module_ops = {
+ .name = "asq",
+ .search = asq_search,
+ .init_context = asq_init
+};
diff --git a/source4/lib/ldb/modules/operational.c b/source4/lib/ldb/modules/operational.c
new file mode 100644
index 0000000000..43b223b52e
--- /dev/null
+++ b/source4/lib/ldb/modules/operational.c
@@ -0,0 +1,314 @@
+/*
+ ldb database library
+
+ Copyright (C) Andrew Tridgell 2005
+ Copyright (C) Simo Sorce 2006-2008
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+/*
+ handle operational attributes
+ */
+
+/*
+ createTimestamp: HIDDEN, searchable, ldaptime, alias for whenCreated
+ modifyTimestamp: HIDDEN, searchable, ldaptime, alias for whenChanged
+
+ for the above two, we do the search as normal, and if
+ createTimestamp or modifyTimestamp is asked for, then do
+ additional searches for whenCreated and whenChanged and fill in
+ the resulting values
+
+ we also need to replace these with the whenCreated/whenChanged
+ equivalent in the search expression trees
+
+ whenCreated: not-HIDDEN, CONSTRUCTED, SEARCHABLE
+ whenChanged: not-HIDDEN, CONSTRUCTED, SEARCHABLE
+
+ on init we need to setup attribute handlers for these so
+ comparisons are done correctly. The resolution is 1 second.
+
+ on add we need to add both the above, for current time
+
+ on modify we need to change whenChanged
+
+
+ subschemaSubentry: HIDDEN, not-searchable,
+ points at DN CN=Aggregate,$SCHEMADN
+
+ for this one we do the search as normal, then add the static
+ value if requested. How do we work out the $BASEDN from inside a
+ module?
+
+
+ structuralObjectClass: HIDDEN, CONSTRUCTED, not-searchable. always same as objectclass?
+
+ for this one we do the search as normal, then if requested ask
+ for objectclass, change the attribute name, and add it
+
+ allowedAttributesEffective: HIDDEN, CONSTRUCTED, not-searchable,
+ list of attributes that can be modified - requires schema lookup
+
+
+ attributeTypes: in schema only
+ objectClasses: in schema only
+ matchingRules: in schema only
+ matchingRuleUse: in schema only
+ creatorsName: not supported by w2k3?
+ modifiersName: not supported by w2k3?
+*/
+
+#include "ldb_includes.h"
+#include "ldb_module.h"
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
+#endif
+
+/*
+ construct a canonical name from a message
+*/
+static int construct_canonical_name(struct ldb_module *module, struct ldb_message *msg)
+{
+ char *canonicalName;
+ canonicalName = ldb_dn_canonical_string(msg, msg->dn);
+ if (canonicalName == NULL) {
+ return -1;
+ }
+ return ldb_msg_add_steal_string(msg, "canonicalName", canonicalName);
+}
+
+/*
+ a list of attribute names that should be substituted in the parse
+ tree before the search is done
+*/
+static const struct {
+ const char *attr;
+ const char *replace;
+} parse_tree_sub[] = {
+ { "createTimestamp", "whenCreated" },
+ { "modifyTimestamp", "whenChanged" }
+};
+
+
+/*
+ a list of attribute names that are hidden, but can be searched for
+ using another (non-hidden) name to produce the correct result
+*/
+static const struct {
+ const char *attr;
+ const char *replace;
+ int (*constructor)(struct ldb_module *, struct ldb_message *);
+} search_sub[] = {
+ { "createTimestamp", "whenCreated", NULL },
+ { "modifyTimestamp", "whenChanged", NULL },
+ { "structuralObjectClass", "objectClass", NULL },
+ { "canonicalName", "distinguishedName", construct_canonical_name }
+};
+
+/*
+ post process a search result record. For any search_sub[] attributes that were
+ asked for, we need to call the appropriate copy routine to copy the result
+ into the message, then remove any attributes that we added to the search but were
+ not asked for by the user
+*/
+static int operational_search_post_process(struct ldb_module *module,
+ struct ldb_message *msg,
+ const char * const *attrs)
+{
+ struct ldb_context *ldb;
+ int i, a=0;
+
+ ldb = ldb_module_get_ctx(module);
+
+ for (a=0;attrs && attrs[a];a++) {
+ for (i=0;i<ARRAY_SIZE(search_sub);i++) {
+ if (ldb_attr_cmp(attrs[a], search_sub[i].attr) != 0) {
+ continue;
+ }
+
+ /* construct the new attribute, using either a supplied
+ constructor or a simple copy */
+ if (search_sub[i].constructor) {
+ if (search_sub[i].constructor(module, msg) != 0) {
+ goto failed;
+ }
+ } else if (ldb_msg_copy_attr(msg,
+ search_sub[i].replace,
+ search_sub[i].attr) != 0) {
+ goto failed;
+ }
+
+ /* remove the added search attribute, unless it was asked for
+ by the user */
+ if (search_sub[i].replace == NULL ||
+ ldb_attr_in_list(attrs, search_sub[i].replace) ||
+ ldb_attr_in_list(attrs, "*")) {
+ continue;
+ }
+
+ ldb_msg_remove_attr(msg, search_sub[i].replace);
+ }
+ }
+
+ return 0;
+
+failed:
+ ldb_debug_set(ldb, LDB_DEBUG_WARNING,
+ "operational_search_post_process failed for attribute '%s'\n",
+ attrs[a]);
+ return -1;
+}
+
+
+/*
+ hook search operations
+*/
+
+struct operational_context {
+ struct ldb_module *module;
+ struct ldb_request *req;
+
+ const char * const *attrs;
+};
+
+static int operational_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+ struct operational_context *ac;
+ int ret;
+
+ ac = talloc_get_type(req->context, struct operational_context);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+ /* for each record returned post-process to add any derived
+ attributes that have been asked for */
+ ret = operational_search_post_process(ac->module,
+ ares->message,
+ ac->attrs);
+ if (ret != 0) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ return ldb_module_send_entry(ac->req, ares->message, ares->controls);
+
+ case LDB_REPLY_REFERRAL:
+ /* ignore referrals */
+ break;
+
+ case LDB_REPLY_DONE:
+
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, LDB_SUCCESS);
+ }
+
+ talloc_free(ares);
+ return LDB_SUCCESS;
+}
+
+static int operational_search(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct operational_context *ac;
+ struct ldb_request *down_req;
+ const char **search_attrs = NULL;
+ int i, a;
+ int ret;
+
+ ldb = ldb_module_get_ctx(module);
+
+ ac = talloc(req, struct operational_context);
+ if (ac == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->module = module;
+ ac->req = req;
+ ac->attrs = req->op.search.attrs;
+
+ /* FIXME: We must copy the tree and keep the original
+ * unmodified. SSS */
+ /* replace any attributes in the parse tree that are
+ searchable, but are stored using a different name in the
+ backend */
+ for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
+ ldb_parse_tree_attr_replace(req->op.search.tree,
+ parse_tree_sub[i].attr,
+ parse_tree_sub[i].replace);
+ }
+
+ /* in the list of attributes we are looking for, rename any
+ attributes to the alias for any hidden attributes that can
+ be fetched directly using non-hidden names */
+ for (a=0;ac->attrs && ac->attrs[a];a++) {
+ for (i=0;i<ARRAY_SIZE(search_sub);i++) {
+ if (ldb_attr_cmp(ac->attrs[a], search_sub[i].attr) == 0 &&
+ search_sub[i].replace) {
+ if (!search_attrs) {
+ search_attrs = ldb_attr_list_copy(req, ac->attrs);
+ if (search_attrs == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ }
+ search_attrs[a] = search_sub[i].replace;
+ }
+ }
+ }
+
+ ret = ldb_build_search_req_ex(&down_req, ldb, ac,
+ req->op.search.base,
+ req->op.search.scope,
+ req->op.search.tree,
+ /* use new set of attrs if any */
+ search_attrs == NULL?req->op.search.attrs:search_attrs,
+ req->controls,
+ ac, operational_callback,
+ req);
+ if (ret != LDB_SUCCESS) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* perform the search */
+ return ldb_next_request(module, down_req);
+}
+
+static int operational_init(struct ldb_module *ctx)
+{
+ int ret = 0;
+
+ if (ret != 0) {
+ return ret;
+ }
+
+ return ldb_next_init(ctx);
+}
+
+const struct ldb_module_ops ldb_operational_module_ops = {
+ .name = "operational",
+ .search = operational_search,
+ .init_context = operational_init
+};
diff --git a/source4/lib/ldb/modules/paged_results.c b/source4/lib/ldb/modules/paged_results.c
new file mode 100644
index 0000000000..2a06c5e6c5
--- /dev/null
+++ b/source4/lib/ldb/modules/paged_results.c
@@ -0,0 +1,422 @@
+/*
+ ldb database library
+
+ Copyright (C) Simo Sorce 2005-2008
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: paged_result
+ *
+ * Component: ldb paged results control module
+ *
+ * Description: this module caches a complete search and sends back
+ * results in chunks as asked by the client
+ *
+ * Author: Simo Sorce
+ */
+
+#include "ldb_includes.h"
+#include "ldb_module.h"
+
+struct message_store {
+ /* keep the whole ldb_reply as an optimization
+ * instead of freeing and talloc-ing the container
+ * on each result */
+ struct ldb_reply *r;
+ struct message_store *next;
+};
+
+struct private_data;
+
+struct results_store {
+
+ struct private_data *priv;
+
+ char *cookie;
+ time_t timestamp;
+
+ struct results_store *next;
+
+ struct message_store *first;
+ struct message_store *last;
+ int num_entries;
+
+ struct message_store *first_ref;
+ struct message_store *last_ref;
+
+ struct ldb_control **controls;
+};
+
+struct private_data {
+
+ int next_free_id;
+ struct results_store *store;
+
+};
+
+static int store_destructor(struct results_store *del)
+{
+ struct private_data *priv = del->priv;
+ struct results_store *loop;
+
+ if (priv->store == del) {
+ priv->store = del->next;
+ return 0;
+ }
+
+ for (loop = priv->store; loop; loop = loop->next) {
+ if (loop->next == del) {
+ loop->next = del->next;
+ return 0;
+ }
+ }
+
+ /* is not in list ? */
+ return -1;
+}
+
+static struct results_store *new_store(struct private_data *priv)
+{
+ struct results_store *newr;
+ int new_id = priv->next_free_id++;
+
+ /* TODO: we should have a limit on the number of
+ * outstanding paged searches
+ */
+
+ newr = talloc(priv, struct results_store);
+ if (!newr) return NULL;
+
+ newr->priv = priv;
+
+ newr->cookie = talloc_asprintf(newr, "%d", new_id);
+ if (!newr->cookie) {
+ talloc_free(newr);
+ return NULL;
+ }
+
+ newr->timestamp = time(NULL);
+
+ newr->first = NULL;
+ newr->num_entries = 0;
+ newr->first_ref = NULL;
+ newr->controls = NULL;
+
+ newr->next = priv->store;
+ priv->store = newr;
+
+ talloc_set_destructor(newr, store_destructor);
+
+ return newr;
+}
+
+struct paged_context {
+ struct ldb_module *module;
+ struct ldb_request *req;
+
+ struct results_store *store;
+ int size;
+ struct ldb_control **controls;
+};
+
+static int paged_results(struct paged_context *ac)
+{
+ struct ldb_paged_control *paged;
+ struct message_store *msg;
+ int i, num_ctrls, ret;
+
+ if (ac->store == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ while (ac->store->num_entries > 0 && ac->size > 0) {
+ msg = ac->store->first;
+ ret = ldb_module_send_entry(ac->req, msg->r->message, msg->r->controls);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ ac->store->first = msg->next;
+ talloc_free(msg);
+ ac->store->num_entries--;
+ ac->size--;
+ }
+
+ while (ac->store->first_ref != NULL) {
+ msg = ac->store->first_ref;
+ ret = ldb_module_send_referral(ac->req, msg->r->referral);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ ac->store->first_ref = msg->next;
+ talloc_free(msg);
+ }
+
+ /* return result done */
+ num_ctrls = 1;
+ i = 0;
+
+ if (ac->store->controls != NULL) {
+ while (ac->store->controls[i]) i++; /* counting */
+
+ num_ctrls += i;
+ }
+
+ ac->controls = talloc_array(ac, struct ldb_control *, num_ctrls +1);
+ if (ac->controls == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ac->controls[num_ctrls] = NULL;
+
+ for (i = 0; i < (num_ctrls -1); i++) {
+ ac->controls[i] = talloc_reference(ac->controls, ac->store->controls[i]);
+ }
+
+ ac->controls[i] = talloc(ac->controls, struct ldb_control);
+ if (ac->controls[i] == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->controls[i]->oid = talloc_strdup(ac->controls[i],
+ LDB_CONTROL_PAGED_RESULTS_OID);
+ if (ac->controls[i]->oid == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->controls[i]->critical = 0;
+
+ paged = talloc(ac->controls[i], struct ldb_paged_control);
+ if (paged == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->controls[i]->data = paged;
+
+ if (ac->size > 0) {
+ paged->size = 0;
+ paged->cookie = NULL;
+ paged->cookie_len = 0;
+ } else {
+ paged->size = ac->store->num_entries;
+ paged->cookie = talloc_strdup(paged, ac->store->cookie);
+ paged->cookie_len = strlen(paged->cookie) + 1;
+ }
+
+ return LDB_SUCCESS;
+}
+
+static int paged_search_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+ struct paged_context *ac ;
+ struct message_store *msg_store;
+ int ret;
+
+ ac = talloc_get_type(req->context, struct paged_context);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+ msg_store = talloc(ac->store, struct message_store);
+ if (msg_store == NULL) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ msg_store->next = NULL;
+ msg_store->r = talloc_steal(msg_store, ares);
+
+ if (ac->store->first == NULL) {
+ ac->store->first = msg_store;
+ } else {
+ ac->store->last->next = msg_store;
+ }
+ ac->store->last = msg_store;
+
+ ac->store->num_entries++;
+
+ break;
+
+ case LDB_REPLY_REFERRAL:
+ msg_store = talloc(ac->store, struct message_store);
+ if (msg_store == NULL) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ msg_store->next = NULL;
+ msg_store->r = talloc_steal(msg_store, ares);
+
+ if (ac->store->first_ref == NULL) {
+ ac->store->first_ref = msg_store;
+ } else {
+ ac->store->last_ref->next = msg_store;
+ }
+ ac->store->last_ref = msg_store;
+
+ break;
+
+ case LDB_REPLY_DONE:
+ ac->store->controls = talloc_move(ac->store, &ares->controls);
+ ret = paged_results(ac);
+ return ldb_module_done(ac->req, ac->controls,
+ ares->response, ret);
+ }
+
+ return LDB_SUCCESS;
+}
+
+static int paged_search(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct ldb_control *control;
+ struct private_data *private_data;
+ struct ldb_paged_control *paged_ctrl;
+ struct ldb_control **saved_controls;
+ struct ldb_request *search_req;
+ struct paged_context *ac;
+ int ret;
+
+ ldb = ldb_module_get_ctx(module);
+
+ /* check if there's a paged request control */
+ control = ldb_request_get_control(req, LDB_CONTROL_PAGED_RESULTS_OID);
+ if (control == NULL) {
+ /* not found go on */
+ return ldb_next_request(module, req);
+ }
+
+ paged_ctrl = talloc_get_type(control->data, struct ldb_paged_control);
+ if (!paged_ctrl) {
+ return LDB_ERR_PROTOCOL_ERROR;
+ }
+
+ private_data = talloc_get_type(ldb_module_get_private(module),
+ struct private_data);
+
+ ac = talloc_zero(req, struct paged_context);
+ if (ac == NULL) {
+ ldb_set_errstring(ldb, "Out of Memory");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->module = module;
+ ac->req = req;
+ ac->size = paged_ctrl->size;
+
+ /* check if it is a continuation search the store */
+ if (paged_ctrl->cookie_len == 0) {
+ if (paged_ctrl->size == 0) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->store = new_store(private_data);
+ if (ac->store == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ldb_build_search_req_ex(&search_req, ldb, ac,
+ req->op.search.base,
+ req->op.search.scope,
+ req->op.search.tree,
+ req->op.search.attrs,
+ req->controls,
+ ac,
+ paged_search_callback,
+ req);
+
+ /* save it locally and remove it from the list */
+ /* we do not need to replace them later as we
+ * are keeping the original req intact */
+ if (!save_controls(control, search_req, &saved_controls)) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ return ldb_next_request(module, search_req);
+
+ } else {
+ struct results_store *current = NULL;
+
+ /* TODO: age out old outstanding requests */
+ for (current = private_data->store; current; current = current->next) {
+ if (strcmp(current->cookie, paged_ctrl->cookie) == 0) {
+ current->timestamp = time(NULL);
+ break;
+ }
+ }
+ if (current == NULL) {
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+
+ ac->store = current;
+
+ /* check if it is an abandon */
+ if (ac->size == 0) {
+ return ldb_module_done(req, NULL, NULL,
+ LDB_SUCCESS);
+ }
+
+ ret = paged_results(ac);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(req, NULL, NULL, ret);
+ }
+ return ldb_module_done(req, ac->controls, NULL,
+ LDB_SUCCESS);
+ }
+}
+
+static int paged_request_init(struct ldb_module *module)
+{
+ struct ldb_context *ldb;
+ struct private_data *data;
+ int ret;
+
+ ldb = ldb_module_get_ctx(module);
+
+ data = talloc(module, struct private_data);
+ if (data == NULL) {
+ return LDB_ERR_OTHER;
+ }
+
+ data->next_free_id = 1;
+ data->store = NULL;
+ ldb_module_set_private(module, data);
+
+ ret = ldb_mod_register_control(module, LDB_CONTROL_PAGED_RESULTS_OID);
+ if (ret != LDB_SUCCESS) {
+ ldb_debug(ldb, LDB_DEBUG_WARNING,
+ "paged_request:"
+ "Unable to register control with rootdse!\n");
+ }
+
+ return ldb_next_init(module);
+}
+
+const struct ldb_module_ops ldb_paged_results_module_ops = {
+ .name = "paged_results",
+ .search = paged_search,
+ .init_context = paged_request_init
+};
diff --git a/source4/lib/ldb/modules/paged_searches.c b/source4/lib/ldb/modules/paged_searches.c
new file mode 100644
index 0000000000..01e77cb22c
--- /dev/null
+++ b/source4/lib/ldb/modules/paged_searches.c
@@ -0,0 +1,383 @@
+/*
+ ldb database library
+
+ Copyright (C) Simo Sorce 2005-2008
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: paged_searches
+ *
+ * Component: ldb paged searches module
+ *
+ * Description: this module detects if the remote ldap server supports
+ * paged results and use them to transparently access all objects
+ *
+ * Author: Simo Sorce
+ */
+
+#include "includes.h"
+#include "ldb_module.h"
+
+#define PS_DEFAULT_PAGE_SIZE 500
+/* 500 objects per query seem to be a decent compromise
+ * the default AD limit per request is 1000 entries */
+
+struct private_data {
+
+ bool paged_supported;
+};
+
+struct ps_context {
+ struct ldb_module *module;
+ struct ldb_request *req;
+
+ bool pending;
+
+ char **saved_referrals;
+ int num_referrals;
+};
+
+static int check_ps_continuation(struct ldb_request *req, struct ldb_reply *ares)
+{
+ struct ps_context *ac;
+ struct ldb_paged_control *rep_control, *req_control;
+
+ ac = talloc_get_type(req->context, struct ps_context);
+
+ /* look up our paged control */
+ if (!ares->controls || strcmp(LDB_CONTROL_PAGED_RESULTS_OID, ares->controls[0]->oid) != 0) {
+ /* something wrong here */
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ rep_control = talloc_get_type(ares->controls[0]->data, struct ldb_paged_control);
+ if (rep_control->cookie_len == 0) {
+ /* we are done */
+ ac->pending = false;
+ return LDB_SUCCESS;
+ }
+
+ /* more processing required */
+ /* let's fill in the request control with the new cookie */
+ /* if there's a reply control we must find a request
+ * control matching it */
+
+ if (strcmp(LDB_CONTROL_PAGED_RESULTS_OID, req->controls[0]->oid) != 0) {
+ /* something wrong here */
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ req_control = talloc_get_type(req->controls[0]->data, struct ldb_paged_control);
+
+ if (req_control->cookie) {
+ talloc_free(req_control->cookie);
+ }
+
+ req_control->cookie = talloc_memdup(req_control,
+ rep_control->cookie,
+ rep_control->cookie_len);
+ req_control->cookie_len = rep_control->cookie_len;
+
+ ac->pending = true;
+ return LDB_SUCCESS;
+}
+
+static int store_referral(struct ps_context *ac, char *referral)
+{
+ ac->saved_referrals = talloc_realloc(ac, ac->saved_referrals, char *, ac->num_referrals + 2);
+ if (!ac->saved_referrals) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->saved_referrals[ac->num_referrals] = talloc_strdup(ac->saved_referrals, referral);
+ if (!ac->saved_referrals[ac->num_referrals]) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->num_referrals++;
+ ac->saved_referrals[ac->num_referrals] = NULL;
+
+ return LDB_SUCCESS;
+}
+
+static int send_referrals(struct ps_context *ac)
+{
+ struct ldb_reply *ares;
+ int ret;
+ int i;
+
+ for (i = 0; i < ac->num_referrals; i++) {
+ ares = talloc_zero(ac->req, struct ldb_reply);
+ if (!ares) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ares->type = LDB_REPLY_REFERRAL;
+ ares->referral = ac->saved_referrals[i];
+
+ ret = ldb_module_send_referral(ac->req, ares->referral);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+
+ return LDB_SUCCESS;
+}
+
+static int ps_next_request(struct ps_context *ac);
+
+static int ps_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+ struct ps_context *ac;
+ int ret;
+
+ ac = talloc_get_type(req->context, struct ps_context);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+ ret = ldb_module_send_entry(ac->req, ares->message, ares->controls);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+ break;
+
+ case LDB_REPLY_REFERRAL:
+ ret = store_referral(ac, ares->referral);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+ break;
+
+ case LDB_REPLY_DONE:
+
+ ret = check_ps_continuation(req, ares);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+
+ if (ac->pending) {
+
+ ret = ps_next_request(ac);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req,
+ NULL, NULL, ret);
+ }
+
+ } else {
+
+ /* send referrals */
+ ret = send_referrals(ac);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req,
+ NULL, NULL, ret);
+ }
+
+ /* send REPLY_DONE */
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, LDB_SUCCESS);
+ }
+ break;
+ }
+
+ talloc_free(ares);
+ return LDB_SUCCESS;
+}
+
+static int ps_search(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct private_data *private_data;
+ struct ps_context *ac;
+
+ private_data = talloc_get_type(ldb_module_get_private(module), struct private_data);
+ ldb = ldb_module_get_ctx(module);
+
+ /* check if paging is supported and if there is a any control */
+ if (!private_data || !private_data->paged_supported || req->controls) {
+ /* do not touch this request paged controls not
+ * supported or explicit controls have been set or we
+ * are just not setup yet */
+ return ldb_next_request(module, req);
+ }
+
+ ac = talloc_zero(req, struct ps_context);
+ if (ac == NULL) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->module = module;
+ ac->req = req;
+ ac->pending = false;
+ ac->saved_referrals = NULL;
+ ac->num_referrals = 0;
+
+ return ps_next_request(ac);
+}
+
+static int ps_next_request(struct ps_context *ac) {
+
+ struct ldb_context *ldb;
+ struct ldb_paged_control *control;
+ struct ldb_control **controls;
+ struct ldb_request *new_req;
+ int ret;
+
+ ldb = ldb_module_get_ctx(ac->module);
+
+ controls = talloc_array(ac, struct ldb_control *, 2);
+ if (!controls) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ controls[0] = talloc(controls, struct ldb_control);
+ if (!controls[0]) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ control = talloc(controls[0], struct ldb_paged_control);
+ if (!control) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ control->size = PS_DEFAULT_PAGE_SIZE;
+ control->cookie = NULL;
+ control->cookie_len = 0;
+
+ controls[0]->oid = LDB_CONTROL_PAGED_RESULTS_OID;
+ controls[0]->critical = 1;
+ controls[0]->data = control;
+ controls[1] = NULL;
+
+ ret = ldb_build_search_req_ex(&new_req, ldb, ac,
+ ac->req->op.search.base,
+ ac->req->op.search.scope,
+ ac->req->op.search.tree,
+ ac->req->op.search.attrs,
+ controls,
+ ac,
+ ps_callback,
+ ac->req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ talloc_steal(new_req, controls);
+
+ return ldb_next_request(ac->module, new_req);
+}
+
+static int check_supported_paged(struct ldb_request *req,
+ struct ldb_reply *ares)
+{
+ struct private_data *data;
+
+ data = talloc_get_type(req->context, struct private_data);
+
+ if (!ares) {
+ return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+ if (ldb_msg_check_string_attribute(ares->message,
+ "supportedControl",
+ LDB_CONTROL_PAGED_RESULTS_OID)) {
+ data->paged_supported = true;
+ }
+ break;
+
+ case LDB_REPLY_REFERRAL:
+ /* ignore */
+ break;
+
+ case LDB_REPLY_DONE:
+ return ldb_request_done(req, LDB_SUCCESS);
+ }
+
+ talloc_free(ares);
+ return LDB_SUCCESS;
+}
+
+static int ps_init(struct ldb_module *module)
+{
+ struct ldb_context *ldb;
+ static const char *attrs[] = { "supportedControl", NULL };
+ struct private_data *data;
+ struct ldb_dn *base;
+ int ret;
+ struct ldb_request *req;
+
+ ldb = ldb_module_get_ctx(module);
+
+ data = talloc(module, struct private_data);
+ if (data == NULL) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ data->paged_supported = false;
+
+ ldb_module_set_private(module, data);
+
+ base = ldb_dn_new(module, ldb, "");
+ if (base == NULL) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ret = ldb_build_search_req(&req, ldb, module,
+ base, LDB_SCOPE_BASE,
+ "(objectClass=*)",
+ attrs, NULL,
+ data, check_supported_paged,
+ NULL);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ ret = ldb_next_request(module, req);
+ if (ret == LDB_SUCCESS) {
+ ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+ }
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ talloc_free(base);
+ talloc_free(req);
+
+ return ldb_next_init(module);
+}
+
+_PUBLIC_ const struct ldb_module_ops ldb_paged_searches_module_ops = {
+ .name = "paged_searches",
+ .search = ps_search,
+ .init_context = ps_init
+};
diff --git a/source4/lib/ldb/modules/rdn_name.c b/source4/lib/ldb/modules/rdn_name.c
new file mode 100644
index 0000000000..880678d89d
--- /dev/null
+++ b/source4/lib/ldb/modules/rdn_name.c
@@ -0,0 +1,328 @@
+/*
+ ldb database library
+
+ Copyright (C) Andrew Bartlet 2005
+ Copyright (C) Simo Sorce 2006-2008
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: rdb_name
+ *
+ * Component: ldb rdn name module
+ *
+ * Description: keep a consistent name attribute on objects manpulations
+ *
+ * Author: Andrew Bartlet
+ *
+ * Modifications:
+ * - made the module async
+ * Simo Sorce Mar 2006
+ */
+
+#include "ldb_includes.h"
+#include "ldb_module.h"
+
+struct rename_context {
+
+ struct ldb_module *module;
+ struct ldb_request *req;
+
+ struct ldb_reply *ares;
+};
+
+static struct ldb_message_element *rdn_name_find_attribute(const struct ldb_message *msg, const char *name)
+{
+ int i;
+
+ for (i = 0; i < msg->num_elements; i++) {
+ if (ldb_attr_cmp(name, msg->elements[i].name) == 0) {
+ return &msg->elements[i];
+ }
+ }
+
+ return NULL;
+}
+
+static int rdn_name_add_callback(struct ldb_request *req,
+ struct ldb_reply *ares)
+{
+ struct rename_context *ac;
+
+ ac = talloc_get_type(req->context, struct rename_context);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ if (ares->type != LDB_REPLY_DONE) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, LDB_SUCCESS);
+}
+
+static int rdn_name_add(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct ldb_request *down_req;
+ struct rename_context *ac;
+ struct ldb_message *msg;
+ struct ldb_message_element *attribute;
+ const struct ldb_schema_attribute *a;
+ const char *rdn_name;
+ struct ldb_val rdn_val;
+ int i, ret;
+
+ ldb = ldb_module_get_ctx(module);
+ ldb_debug(ldb, LDB_DEBUG_TRACE, "rdn_name_add_record\n");
+
+ /* do not manipulate our control entries */
+ if (ldb_dn_is_special(req->op.add.message->dn)) {
+ return ldb_next_request(module, req);
+ }
+
+ ac = talloc_zero(req, struct rename_context);
+ if (ac == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->module = module;
+ ac->req = req;
+
+ msg = ldb_msg_copy_shallow(req, req->op.add.message);
+ if (msg == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ rdn_name = ldb_dn_get_rdn_name(msg->dn);
+ if (rdn_name == NULL) {
+ talloc_free(ac);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ rdn_val = ldb_val_dup(msg, ldb_dn_get_rdn_val(msg->dn));
+
+ /* Perhaps someone above us tried to set this? */
+ if ((attribute = rdn_name_find_attribute(msg, "name")) != NULL ) {
+ attribute->num_values = 0;
+ }
+
+ if (ldb_msg_add_value(msg, "name", &rdn_val, NULL) != 0) {
+ talloc_free(ac);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ attribute = rdn_name_find_attribute(msg, rdn_name);
+
+ if (!attribute) {
+ if (ldb_msg_add_value(msg, rdn_name, &rdn_val, NULL) != 0) {
+ talloc_free(ac);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ } else {
+ a = ldb_schema_attribute_by_name(ldb, rdn_name);
+
+ for (i = 0; i < attribute->num_values; i++) {
+ ret = a->syntax->comparison_fn(ldb, msg,
+ &rdn_val, &attribute->values[i]);
+ if (ret == 0) {
+ /* overwrite so it matches in case */
+ attribute->values[i] = rdn_val;
+ break;
+ }
+ }
+ if (i == attribute->num_values) {
+ ldb_debug_set(ldb, LDB_DEBUG_FATAL,
+ "RDN mismatch on %s: %s (%s)",
+ ldb_dn_get_linearized(msg->dn), rdn_name, rdn_val.data);
+ talloc_free(ac);
+ /* Match AD's error here */
+ return LDB_ERR_INVALID_DN_SYNTAX;
+ }
+ }
+
+ ret = ldb_build_add_req(&down_req, ldb, req,
+ msg,
+ req->controls,
+ ac, rdn_name_add_callback,
+ req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ talloc_steal(down_req, msg);
+
+ /* go on with the call chain */
+ return ldb_next_request(module, down_req);
+}
+
+static int rdn_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+ struct rename_context *ac;
+
+ ac = talloc_get_type(req->context, struct rename_context);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ /* the only supported reply right now is a LDB_REPLY_DONE */
+ if (ares->type != LDB_REPLY_DONE) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ /* send saved controls eventually */
+ return ldb_module_done(ac->req, ac->ares->controls,
+ ac->ares->response, LDB_SUCCESS);
+}
+
+static int rdn_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+ struct ldb_context *ldb;
+ struct rename_context *ac;
+ struct ldb_request *mod_req;
+ const char *rdn_name;
+ struct ldb_val rdn_val;
+ struct ldb_message *msg;
+ int ret;
+
+ ac = talloc_get_type(req->context, struct rename_context);
+ ldb = ldb_module_get_ctx(ac->module);
+
+ if (!ares) {
+ goto error;
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ /* the only supported reply right now is a LDB_REPLY_DONE */
+ if (ares->type != LDB_REPLY_DONE) {
+ goto error;
+ }
+
+ /* save reply for caller */
+ ac->ares = talloc_steal(ac, ares);
+
+ msg = ldb_msg_new(ac);
+ if (msg == NULL) {
+ goto error;
+ }
+ msg->dn = ldb_dn_copy(msg, ac->req->op.rename.newdn);
+ if (msg->dn == NULL) {
+ goto error;
+ }
+ rdn_name = ldb_dn_get_rdn_name(ac->req->op.rename.newdn);
+ if (rdn_name == NULL) {
+ goto error;
+ }
+
+ rdn_val = ldb_val_dup(msg, ldb_dn_get_rdn_val(ac->req->op.rename.newdn));
+
+ if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
+ goto error;
+ }
+ if (ldb_msg_add_value(msg, rdn_name, &rdn_val, NULL) != 0) {
+ goto error;
+ }
+ if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
+ goto error;
+ }
+ if (ldb_msg_add_value(msg, "name", &rdn_val, NULL) != 0) {
+ goto error;
+ }
+
+ ret = ldb_build_mod_req(&mod_req, ldb,
+ ac, msg, NULL,
+ ac, rdn_modify_callback,
+ req);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+ talloc_steal(mod_req, msg);
+
+ /* do the mod call */
+ return ldb_request(ldb, mod_req);
+
+error:
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+}
+
+static int rdn_name_rename(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct rename_context *ac;
+ struct ldb_request *down_req;
+ int ret;
+
+ ldb = ldb_module_get_ctx(module);
+ ldb_debug(ldb, LDB_DEBUG_TRACE, "rdn_name_rename\n");
+
+ /* do not manipulate our control entries */
+ if (ldb_dn_is_special(req->op.rename.newdn)) {
+ return ldb_next_request(module, req);
+ }
+
+ ac = talloc_zero(req, struct rename_context);
+ if (ac == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->module = module;
+ ac->req = req;
+
+ ret = ldb_build_rename_req(&down_req,
+ ldb,
+ ac,
+ req->op.rename.olddn,
+ req->op.rename.newdn,
+ req->controls,
+ ac,
+ rdn_rename_callback,
+ req);
+
+ if (ret != LDB_SUCCESS) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* rename first, modify "name" if rename is ok */
+ return ldb_next_request(module, down_req);
+}
+
+const struct ldb_module_ops ldb_rdn_name_module_ops = {
+ .name = "rdn_name",
+ .add = rdn_name_add,
+ .rename = rdn_name_rename,
+};
diff --git a/source4/lib/ldb/modules/skel.c b/source4/lib/ldb/modules/skel.c
new file mode 100644
index 0000000000..248f9b346b
--- /dev/null
+++ b/source4/lib/ldb/modules/skel.c
@@ -0,0 +1,138 @@
+/*
+ ldb database library
+
+ Copyright (C) Simo Sorce 2004
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb skel module
+ *
+ * Description: example module
+ *
+ * Author: Simo Sorce
+ */
+
+#include "ldb_module.h"
+
+struct private_data {
+
+ char *some_private_data;
+};
+
+/* search */
+static int skel_search(struct ldb_module *module, struct ldb_request *req)
+{
+ return ldb_next_request(module, req);
+}
+
+/* add */
+static int skel_add(struct ldb_module *module, struct ldb_request *req){
+ return ldb_next_request(module, req);
+}
+
+/* modify */
+static int skel_modify(struct ldb_module *module, struct ldb_request *req)
+{
+ return ldb_next_request(module, req);
+}
+
+/* delete */
+static int skel_delete(struct ldb_module *module, struct ldb_request *req)
+{
+ return ldb_next_request(module, req);
+}
+
+/* rename */
+static int skel_rename(struct ldb_module *module, struct ldb_request *req)
+{
+ return ldb_next_request(module, req);
+}
+
+/* start a transaction */
+static int skel_start_trans(struct ldb_module *module)
+{
+ return ldb_next_start_trans(module);
+}
+
+/* end a transaction */
+static int skel_end_trans(struct ldb_module *module)
+{
+ return ldb_next_end_trans(module);
+}
+
+/* delete a transaction */
+static int skel_del_trans(struct ldb_module *module)
+{
+ return ldb_next_del_trans(module);
+}
+
+static int skel_destructor(struct ldb_module *ctx)
+{
+ struct private_data *data;
+
+ data = talloc_get_type(ldb_module_get_private(ctx), struct private_data);
+
+ /* put your clean-up functions here */
+ if (data->some_private_data) talloc_free(data->some_private_data);
+
+ return 0;
+}
+
+static int skel_request(struct ldb_module *module, struct ldb_request *req)
+{
+ return ldb_next_request(module, req);
+}
+
+static int skel_init(struct ldb_module *module)
+{
+ struct ldb_context *ldb;
+ struct private_data *data;
+
+ ldb = ldb_module_get_ctx(module);
+
+ data = talloc(module, struct private_data);
+ if (data == NULL) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ data->some_private_data = NULL;
+ ldb_module_set_private(module, data);
+
+ talloc_set_destructor (module, skel_destructor);
+
+ return ldb_next_init(module);
+}
+
+const struct ldb_module_ops ldb_skel_module_ops = {
+ .name = "skel",
+ .init_context = skel_init,
+ .search = skel_search,
+ .add = skel_add,
+ .modify = skel_modify,
+ .del = skel_delete,
+ .rename = skel_rename,
+ .request = skel_request,
+ .start_transaction = skel_start_trans,
+ .end_transaction = skel_end_trans,
+ .del_transaction = skel_del_trans,
+};
diff --git a/source4/lib/ldb/modules/sort.c b/source4/lib/ldb/modules/sort.c
new file mode 100644
index 0000000000..309101c32b
--- /dev/null
+++ b/source4/lib/ldb/modules/sort.c
@@ -0,0 +1,349 @@
+/*
+ ldb database library
+
+ Copyright (C) Simo Sorce 2005-2008
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb server side sort control module
+ *
+ * Description: this module sorts the results of a search
+ *
+ * Author: Simo Sorce
+ */
+
+#include "ldb_module.h"
+
+struct opaque {
+ struct ldb_context *ldb;
+ const struct ldb_attrib_handler *h;
+ const char *attribute;
+ int reverse;
+ int result;
+};
+
+struct sort_context {
+ struct ldb_module *module;
+
+ char *attributeName;
+ char *orderingRule;
+ int reverse;
+
+ struct ldb_request *req;
+ struct ldb_message **msgs;
+ char **referrals;
+ int num_msgs;
+ int num_refs;
+
+ const struct ldb_schema_attribute *a;
+ int sort_result;
+};
+
+static int build_response(void *mem_ctx, struct ldb_control ***ctrls, int result, const char *desc)
+{
+ struct ldb_control **controls;
+ struct ldb_sort_resp_control *resp;
+ int i;
+
+ if (*ctrls) {
+ controls = *ctrls;
+ for (i = 0; controls[i]; i++);
+ controls = talloc_realloc(mem_ctx, controls, struct ldb_control *, i + 2);
+ } else {
+ i = 0;
+ controls = talloc_array(mem_ctx, struct ldb_control *, 2);
+ }
+ if (! controls )
+ return LDB_ERR_OPERATIONS_ERROR;
+
+ *ctrls = controls;
+
+ controls[i+1] = NULL;
+ controls[i] = talloc(controls, struct ldb_control);
+ if (! controls[i] )
+ return LDB_ERR_OPERATIONS_ERROR;
+
+ controls[i]->oid = LDB_CONTROL_SORT_RESP_OID;
+ controls[i]->critical = 0;
+
+ resp = talloc(controls[i], struct ldb_sort_resp_control);
+ if (! resp )
+ return LDB_ERR_OPERATIONS_ERROR;
+
+ resp->result = result;
+ resp->attr_desc = talloc_strdup(resp, desc);
+
+ if (! resp->attr_desc )
+ return LDB_ERR_OPERATIONS_ERROR;
+
+ controls[i]->data = resp;
+
+ return LDB_SUCCESS;
+}
+
+static int sort_compare(struct ldb_message **msg1, struct ldb_message **msg2, void *opaque)
+{
+ struct sort_context *ac = talloc_get_type(opaque, struct sort_context);
+ struct ldb_message_element *el1, *el2;
+ struct ldb_context *ldb;
+
+ ldb = ldb_module_get_ctx(ac->module);
+
+ if (ac->sort_result != 0) {
+ /* an error occurred previously,
+ * let's exit the sorting by returning always 0 */
+ return 0;
+ }
+
+ el1 = ldb_msg_find_element(*msg1, ac->attributeName);
+ el2 = ldb_msg_find_element(*msg2, ac->attributeName);
+
+ if (!el1 || !el2) {
+ /* the attribute was not found return and
+ * set an error */
+ ac->sort_result = LDB_ERR_UNWILLING_TO_PERFORM;
+ return 0;
+ }
+
+ if (ac->reverse)
+ return ac->a->syntax->comparison_fn(ldb, ac, &el2->values[0], &el1->values[0]);
+
+ return ac->a->syntax->comparison_fn(ldb, ac, &el1->values[0], &el2->values[0]);
+}
+
+static int server_sort_results(struct sort_context *ac)
+{
+ struct ldb_context *ldb;
+ struct ldb_reply *ares;
+ int i, ret;
+
+ ldb = ldb_module_get_ctx(ac->module);
+
+ ac->a = ldb_schema_attribute_by_name(ldb, ac->attributeName);
+ ac->sort_result = 0;
+
+ ldb_qsort(ac->msgs, ac->num_msgs,
+ sizeof(struct ldb_message *),
+ ac, (ldb_qsort_cmp_fn_t)sort_compare);
+
+ if (ac->sort_result != LDB_SUCCESS) {
+ return ac->sort_result;
+ }
+
+ for (i = 0; i < ac->num_msgs; i++) {
+ ares = talloc_zero(ac, struct ldb_reply);
+ if (!ares) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ares->type = LDB_REPLY_ENTRY;
+ ares->message = talloc_move(ares, &ac->msgs[i]);
+
+ ret = ldb_module_send_entry(ac->req, ares->message, ares->controls);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+
+ for (i = 0; i < ac->num_refs; i++) {
+ ares = talloc_zero(ac, struct ldb_reply);
+ if (!ares) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ares->type = LDB_REPLY_REFERRAL;
+ ares->referral = talloc_move(ares, &ac->referrals[i]);
+
+ ret = ldb_module_send_referral(ac->req, ares->referral);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+
+ return LDB_SUCCESS;
+}
+
+static int server_sort_search_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+ struct sort_context *ac;
+ struct ldb_context *ldb;
+ int ret;
+
+ ac = talloc_get_type(req->context, struct sort_context);
+ ldb = ldb_module_get_ctx(ac->module);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+ ac->msgs = talloc_realloc(ac, ac->msgs, struct ldb_message *, ac->num_msgs + 2);
+ if (! ac->msgs) {
+ talloc_free(ares);
+ ldb_oom(ldb);
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ ac->msgs[ac->num_msgs] = talloc_steal(ac->msgs, ares->message);
+ ac->num_msgs++;
+ ac->msgs[ac->num_msgs] = NULL;
+
+ break;
+
+ case LDB_REPLY_REFERRAL:
+ ac->referrals = talloc_realloc(ac, ac->referrals, char *, ac->num_refs + 2);
+ if (! ac->referrals) {
+ talloc_free(ares);
+ ldb_oom(ldb);
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ ac->referrals[ac->num_refs] = talloc_steal(ac->referrals, ares->referral);
+ ac->num_refs++;
+ ac->referrals[ac->num_refs] = NULL;
+
+ break;
+
+ case LDB_REPLY_DONE:
+
+ ret = server_sort_results(ac);
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ret);
+ }
+
+ talloc_free(ares);
+ return LDB_SUCCESS;
+}
+
+static int server_sort_search(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_control *control;
+ struct ldb_server_sort_control **sort_ctrls;
+ struct ldb_control **saved_controls;
+ struct ldb_control **controls;
+ struct ldb_request *down_req;
+ struct sort_context *ac;
+ struct ldb_context *ldb;
+ int ret;
+
+ ldb = ldb_module_get_ctx(module);
+
+ /* check if there's a paged request control */
+ control = ldb_request_get_control(req, LDB_CONTROL_SERVER_SORT_OID);
+ if (control == NULL) {
+ /* not found go on */
+ return ldb_next_request(module, req);
+ }
+
+ ac = talloc_zero(req, struct sort_context);
+ if (ac == NULL) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->module = module;
+ ac->req = req;
+
+ sort_ctrls = talloc_get_type(control->data, struct ldb_server_sort_control *);
+ if (!sort_ctrls) {
+ return LDB_ERR_PROTOCOL_ERROR;
+ }
+
+ /* FIXME: we do not support more than one attribute for sorting right now */
+ /* FIXME: we need to check if the attribute type exist or return an error */
+
+ if (sort_ctrls[1] != NULL) {
+ if (control->critical) {
+
+ /* callback immediately */
+ ret = build_response(req, &controls,
+ LDB_ERR_UNWILLING_TO_PERFORM,
+ "sort control is not complete yet");
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ return ldb_module_done(req, controls, NULL, ret);
+ } else {
+ /* just pass the call down and don't do any sorting */
+ return ldb_next_request(module, req);
+ }
+ }
+
+ ac->attributeName = sort_ctrls[0]->attributeName;
+ ac->orderingRule = sort_ctrls[0]->orderingRule;
+ ac->reverse = sort_ctrls[0]->reverse;
+
+ ret = ldb_build_search_req_ex(&down_req, ldb, ac,
+ req->op.search.base,
+ req->op.search.scope,
+ req->op.search.tree,
+ req->op.search.attrs,
+ req->controls,
+ ac,
+ server_sort_search_callback,
+ req);
+ if (ret != LDB_SUCCESS) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* save it locally and remove it from the list */
+ /* we do not need to replace them later as we
+ * are keeping the original req intact */
+ if (!save_controls(control, down_req, &saved_controls)) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ return ldb_next_request(module, down_req);
+}
+
+static int server_sort_init(struct ldb_module *module)
+{
+ struct ldb_context *ldb;
+ int ret;
+
+ ldb = ldb_module_get_ctx(module);
+
+ ret = ldb_mod_register_control(module, LDB_CONTROL_SERVER_SORT_OID);
+ if (ret != LDB_SUCCESS) {
+ ldb_debug(ldb, LDB_DEBUG_WARNING,
+ "server_sort:"
+ "Unable to register control with rootdse!\n");
+ }
+
+ return ldb_next_init(module);
+}
+
+const struct ldb_module_ops ldb_server_sort_module_ops = {
+ .name = "server_sort",
+ .search = server_sort_search,
+ .init_context = server_sort_init
+};
diff --git a/source4/lib/ldb/nssldb/README.txt b/source4/lib/ldb/nssldb/README.txt
new file mode 100644
index 0000000000..ddba62b380
--- /dev/null
+++ b/source4/lib/ldb/nssldb/README.txt
@@ -0,0 +1,34 @@
+
+This test code requires a tdb that is configured for to use the asq module.
+You can do that adding the following record to a tdb:
+
+dn: @MODULES
+@LIST: asq
+
+Other modules can be used as well (like rdn_name for example)
+
+The uidNumber 0 and the gidNumber 0 are considered invalid.
+
+The user records should contain the followin attributes:
+uid (required) the user name
+userPassword (optional) the user password (if not present "LDB" is
+ returned in the password field)
+uidNumber (required) the user uid
+gidNumber (required) the user primary gid
+gecos (optional) the GECOS
+homeDirectory (required) the home directory
+loginShell (required) the login shell
+memberOf (required) all the groups the user is member of should
+ be reported here using their DNs. The
+ primary group as well.
+
+The group accounts should contain the following attributes:
+cn (required) the group name
+uesrPassword (optional) the group password (if not present "LDB" is
+ returned in the password field)
+gidNumber (required) the group gid
+member (optional) the DNs of the member users, also the ones
+ that have this group as primary
+
+
+SSS
diff --git a/source4/lib/ldb/nssldb/ldb-grp.c b/source4/lib/ldb/nssldb/ldb-grp.c
new file mode 100644
index 0000000000..5e7556dc73
--- /dev/null
+++ b/source4/lib/ldb/nssldb/ldb-grp.c
@@ -0,0 +1,429 @@
+/*
+ LDB nsswitch module
+
+ Copyright (C) Simo Sorce 2006
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "ldb-nss.h"
+
+extern struct _ldb_nss_context *_ldb_nss_ctx;
+
+const char *_ldb_nss_gr_attrs[] = {
+ "cn",
+ "userPassword",
+ "gidNumber",
+ NULL
+};
+
+const char *_ldb_nss_mem_attrs[] = {
+ "uid",
+ NULL
+};
+
+#define _NSS_LDB_ENOMEM(amem) \
+ do { \
+ if ( ! amem) { \
+ errno = ENOMEM; \
+ talloc_free(memctx); \
+ return NSS_STATUS_UNAVAIL; \
+ } \
+ } while(0)
+
+/* This setgrent, getgrent, endgrent is not very efficient */
+
+NSS_STATUS _nss_ldb_setgrent(void)
+{
+ int ret;
+
+ ret = _ldb_nss_init();
+ if (ret != NSS_STATUS_SUCCESS) {
+ return ret;
+ }
+
+ _ldb_nss_ctx->gr_cur = 0;
+ if (_ldb_nss_ctx->gr_res != NULL) {
+ talloc_free(_ldb_nss_ctx->gr_res);
+ _ldb_nss_ctx->gr_res = NULL;
+ }
+
+ ret = ldb_search(_ldb_nss_ctx->ldb,
+ _ldb_nss_ctx->ldb,
+ &_ldb_nss_ctx->gr_res,
+ _ldb_nss_ctx->base,
+ LDB_SCOPE_SUBTREE,
+ _ldb_nss_gr_attrs,
+ _LDB_NSS_GRENT_FILTER);
+ if (ret != LDB_SUCCESS) {
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ return NSS_STATUS_SUCCESS;
+}
+
+NSS_STATUS _nss_ldb_endgrent(void)
+{
+ int ret;
+
+ ret = _ldb_nss_init();
+ if (ret != NSS_STATUS_SUCCESS) {
+ return ret;
+ }
+
+ _ldb_nss_ctx->gr_cur = 0;
+ if (_ldb_nss_ctx->gr_res) {
+ talloc_free(_ldb_nss_ctx->gr_res);
+ _ldb_nss_ctx->gr_res = NULL;
+ }
+
+ return NSS_STATUS_SUCCESS;
+}
+
+NSS_STATUS _nss_ldb_getgrent_r(struct group *result_buf, char *buffer, size_t buflen, int *errnop)
+{
+ int ret;
+ struct ldb_result *res;
+
+ ret = _ldb_nss_init();
+ if (ret != NSS_STATUS_SUCCESS) {
+ return ret;
+ }
+
+ *errnop = 0;
+
+ if (_ldb_nss_ctx->gr_cur >= _ldb_nss_ctx->gr_res->count) {
+ /* already returned all entries */
+ return NSS_STATUS_NOTFOUND;
+ }
+
+ res = talloc_zero(_ldb_nss_ctx->gr_res, struct ldb_result);
+ if ( ! res) {
+ errno = *errnop = ENOMEM;
+ _ldb_nss_ctx->gr_cur++; /* skip this entry */
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ ret = _ldb_nss_group_request(&res,
+ _ldb_nss_ctx->gr_res->msgs[_ldb_nss_ctx->gr_cur]->dn,
+ _ldb_nss_mem_attrs,
+ "member");
+
+ if (ret != NSS_STATUS_SUCCESS) {
+ *errnop = errno;
+ talloc_free(res);
+ _ldb_nss_ctx->gr_cur++; /* skip this entry */
+ return ret;
+ }
+
+ ret = _ldb_nss_fill_group(result_buf,
+ buffer,
+ buflen,
+ errnop,
+ _ldb_nss_ctx->gr_res->msgs[_ldb_nss_ctx->gr_cur],
+ res);
+
+ talloc_free(res);
+
+ if (ret != NSS_STATUS_SUCCESS) {
+ if (ret != NSS_STATUS_TRYAGAIN) {
+ _ldb_nss_ctx->gr_cur++; /* skip this entry */
+ }
+ return ret;
+ }
+
+ /* this entry is ok, increment counter to nex entry */
+ _ldb_nss_ctx->gr_cur++;
+
+ return NSS_STATUS_SUCCESS;
+}
+
+NSS_STATUS _nss_ldb_getgrnam_r(const char *name, struct group *result_buf, char *buffer, size_t buflen, int *errnop)
+{
+ int ret;
+ char *filter;
+ TALLOC_CTX *ctx;
+ struct ldb_result *gr_res;
+ struct ldb_result *mem_res;
+
+ ret = _ldb_nss_init();
+ if (ret != NSS_STATUS_SUCCESS) {
+ return ret;
+ }
+
+ ctx = talloc_new(_ldb_nss_ctx->ldb);
+ if ( ! ctx) {
+ *errnop = errno = ENOMEM;
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ /* build the filter for this uid */
+ filter = talloc_asprintf(ctx, _LDB_NSS_GRNAM_FILTER, name);
+ if (filter == NULL) {
+ /* this is a fatal error */
+ *errnop = errno = ENOMEM;
+ ret = NSS_STATUS_UNAVAIL;
+ goto done;
+ }
+
+ /* search the entry */
+ ret = ldb_search(_ldb_nss_ctx->ldb,
+ _ldb_nss_ctx->ldb,
+ &gr_res,
+ _ldb_nss_ctx->base,
+ LDB_SCOPE_SUBTREE,
+ _ldb_nss_gr_attrs,
+ filter);
+ if (ret != LDB_SUCCESS) {
+ /* this is a fatal error */
+ *errnop = errno = ENOENT;
+ ret = NSS_STATUS_UNAVAIL;
+ goto done;
+ }
+
+ talloc_steal(ctx, gr_res);
+
+ /* if none found return */
+ if (gr_res->count == 0) {
+ *errnop = errno = ENOENT;
+ ret = NSS_STATUS_NOTFOUND;
+ goto done;
+ }
+
+ if (gr_res->count != 1) {
+ /* this is a fatal error */
+ *errnop = errno = ENOENT;
+ ret = NSS_STATUS_UNAVAIL;
+ goto done;
+ }
+
+ mem_res = talloc_zero(ctx, struct ldb_result);
+ if ( ! mem_res) {
+ errno = *errnop = ENOMEM;
+ ret = NSS_STATUS_UNAVAIL;
+ goto done;
+ }
+
+ ret = _ldb_nss_group_request(&mem_res,
+ gr_res->msgs[0]->dn,
+ _ldb_nss_mem_attrs,
+ "member");
+
+ if (ret != NSS_STATUS_SUCCESS) {
+ *errnop = errno;
+ goto done;
+ }
+
+ ret = _ldb_nss_fill_group(result_buf,
+ buffer,
+ buflen,
+ errnop,
+ gr_res->msgs[0],
+ mem_res);
+
+ if (ret != NSS_STATUS_SUCCESS) {
+ goto done;
+ }
+
+ ret = NSS_STATUS_SUCCESS;
+done:
+ talloc_free(ctx);
+ return ret;
+}
+
+NSS_STATUS _nss_ldb_getgrgid_r(gid_t gid, struct group *result_buf, char *buffer, size_t buflen, int *errnop)
+{
+ int ret;
+ char *filter;
+ TALLOC_CTX *ctx;
+ struct ldb_result *gr_res;
+ struct ldb_result *mem_res;
+
+ if (gid == 0) { /* we don't serve root gid by policy */
+ *errnop = errno = ENOENT;
+ return NSS_STATUS_NOTFOUND;
+ }
+
+ ret = _ldb_nss_init();
+ if (ret != NSS_STATUS_SUCCESS) {
+ return ret;
+ }
+
+ ctx = talloc_new(_ldb_nss_ctx->ldb);
+ if ( ! ctx) {
+ *errnop = errno = ENOMEM;
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ /* build the filter for this uid */
+ filter = talloc_asprintf(ctx, _LDB_NSS_GRGID_FILTER, gid);
+ if (filter == NULL) {
+ /* this is a fatal error */
+ *errnop = errno = ENOMEM;
+ ret = NSS_STATUS_UNAVAIL;
+ goto done;
+ }
+
+ /* search the entry */
+ ret = ldb_search(_ldb_nss_ctx->ldb,
+ _ldb_nss_ctx->ldb,
+ &gr_res,
+ _ldb_nss_ctx->base,
+ LDB_SCOPE_SUBTREE,
+ _ldb_nss_gr_attrs,
+ filter);
+ if (ret != LDB_SUCCESS) {
+ /* this is a fatal error */
+ *errnop = errno = ENOENT;
+ ret = NSS_STATUS_UNAVAIL;
+ goto done;
+ }
+
+ talloc_steal(ctx, gr_res);
+
+ /* if none found return */
+ if (gr_res->count == 0) {
+ *errnop = errno = ENOENT;
+ ret = NSS_STATUS_NOTFOUND;
+ goto done;
+ }
+
+ if (gr_res->count != 1) {
+ /* this is a fatal error */
+ *errnop = errno = ENOENT;
+ ret = NSS_STATUS_UNAVAIL;
+ goto done;
+ }
+
+ mem_res = talloc_zero(ctx, struct ldb_result);
+ if ( ! mem_res) {
+ errno = *errnop = ENOMEM;
+ ret = NSS_STATUS_UNAVAIL;
+ goto done;
+ }
+
+ ret = _ldb_nss_group_request(&mem_res,
+ gr_res->msgs[0]->dn,
+ _ldb_nss_mem_attrs,
+ "member");
+
+ if (ret != NSS_STATUS_SUCCESS) {
+ *errnop = errno;
+ goto done;
+ }
+
+ ret = _ldb_nss_fill_group(result_buf,
+ buffer,
+ buflen,
+ errnop,
+ gr_res->msgs[0],
+ mem_res);
+
+ if (ret != NSS_STATUS_SUCCESS) {
+ goto done;
+ }
+
+ ret = NSS_STATUS_SUCCESS;
+done:
+ talloc_free(ctx);
+ return ret;
+}
+
+NSS_STATUS _nss_ldb_initgroups_dyn(const char *user, gid_t group, long int *start, long int *size, gid_t **groups, long int limit, int *errnop)
+{
+ int ret;
+ char *filter;
+ const char * attrs[] = { "uidNumber", "gidNumber", NULL };
+ struct ldb_result *uid_res;
+ struct ldb_result *mem_res;
+
+ ret = _ldb_nss_init();
+ if (ret != NSS_STATUS_SUCCESS) {
+ return ret;
+ }
+
+ mem_res = talloc_zero(_ldb_nss_ctx, struct ldb_result);
+ if ( ! mem_res) {
+ errno = *errnop = ENOMEM;
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ /* build the filter for this name */
+ filter = talloc_asprintf(mem_res, _LDB_NSS_PWNAM_FILTER, user);
+ if (filter == NULL) {
+ /* this is a fatal error */
+ *errnop = errno = ENOENT;
+ ret = NSS_STATUS_UNAVAIL;
+ goto done;
+ }
+
+ /* search the entry */
+ ret = ldb_search(_ldb_nss_ctx->ldb,
+ _ldb_nss_ctx->ldb,
+ &uid_res,
+ _ldb_nss_ctx->base,
+ LDB_SCOPE_SUBTREE,
+ attrs,
+ filter);
+ if (ret != LDB_SUCCESS) {
+ /* this is a fatal error */
+ *errnop = errno = ENOENT;
+ ret = NSS_STATUS_UNAVAIL;
+ goto done;
+ }
+
+ talloc_steal(mem_res, uid_res);
+
+ /* if none found return */
+ if (uid_res->count == 0) {
+ *errnop = errno = ENOENT;
+ ret = NSS_STATUS_NOTFOUND;
+ goto done;
+ }
+
+ if (uid_res->count != 1) {
+ /* this is a fatal error */
+ *errnop = errno = ENOENT;
+ ret = NSS_STATUS_UNAVAIL;
+ goto done;
+ }
+
+ ret = _ldb_nss_group_request(&mem_res,
+ uid_res->msgs[0]->dn,
+ attrs,
+ "memberOf");
+
+ if (ret != NSS_STATUS_SUCCESS) {
+ *errnop = errno;
+ goto done;
+ }
+
+ ret = _ldb_nss_fill_initgr(group,
+ limit,
+ start,
+ size,
+ groups,
+ errnop,
+ mem_res);
+
+ if (ret != NSS_STATUS_SUCCESS) {
+ goto done;
+ }
+
+ ret = NSS_STATUS_SUCCESS;
+
+done:
+ talloc_free(mem_res);
+ return ret;
+}
diff --git a/source4/lib/ldb/nssldb/ldb-nss.c b/source4/lib/ldb/nssldb/ldb-nss.c
new file mode 100644
index 0000000000..8f7321031b
--- /dev/null
+++ b/source4/lib/ldb/nssldb/ldb-nss.c
@@ -0,0 +1,395 @@
+/*
+ LDB nsswitch module
+
+ Copyright (C) Simo Sorce 2006
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "ldb-nss.h"
+
+struct _ldb_nss_context *_ldb_nss_ctx = NULL;
+
+NSS_STATUS _ldb_nss_init(void)
+{
+ int ret;
+
+ pid_t mypid = getpid();
+
+ if (_ldb_nss_ctx != NULL) {
+ if (_ldb_nss_ctx->pid == mypid) {
+ /* already initialized */
+ return NSS_STATUS_SUCCESS;
+ } else {
+ /* we are in a forked child now, reinitialize */
+ talloc_free(_ldb_nss_ctx);
+ _ldb_nss_ctx = NULL;
+ }
+ }
+
+ _ldb_nss_ctx = talloc_named(NULL, 0, "_ldb_nss_ctx(%u)", mypid);
+ if (_ldb_nss_ctx == NULL) {
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ _ldb_nss_ctx->pid = mypid;
+
+ _ldb_nss_ctx->ldb = ldb_init(_ldb_nss_ctx, NULL);
+ if (_ldb_nss_ctx->ldb == NULL) {
+ goto failed;
+ }
+
+ ret = ldb_connect(_ldb_nss_ctx->ldb, _LDB_NSS_URL, LDB_FLG_RDONLY, NULL);
+ if (ret != LDB_SUCCESS) {
+ goto failed;
+ }
+
+ _ldb_nss_ctx->base = ldb_dn_new(_ldb_nss_ctx, _ldb_nss_ctx->ldb, _LDB_NSS_BASEDN);
+ if ( ! ldb_dn_validate(_ldb_nss_ctx->base)) {
+ goto failed;
+ }
+
+ _ldb_nss_ctx->pw_cur = 0;
+ _ldb_nss_ctx->pw_res = NULL;
+ _ldb_nss_ctx->gr_cur = 0;
+ _ldb_nss_ctx->gr_res = NULL;
+
+ return NSS_STATUS_SUCCESS;
+
+failed:
+ /* talloc_free(_ldb_nss_ctx); */
+ _ldb_nss_ctx = NULL;
+ return NSS_STATUS_UNAVAIL;
+}
+
+NSS_STATUS _ldb_nss_fill_passwd(struct passwd *result,
+ char *buffer,
+ int buflen,
+ int *errnop,
+ struct ldb_message *msg)
+{
+ int len;
+ int bufpos;
+ const char *tmp;
+
+ bufpos = 0;
+
+ /* get username */
+ tmp = ldb_msg_find_attr_as_string(msg, "uid", NULL);
+ if (tmp == NULL) {
+ /* this is a fatal error */
+ *errnop = errno = ENOENT;
+ return NSS_STATUS_UNAVAIL;
+ }
+ len = strlen(tmp)+1;
+ if (bufpos + len > buflen) {
+ /* buffer too small */
+ *errnop = errno = EAGAIN;
+ return NSS_STATUS_TRYAGAIN;
+ }
+ memcpy(&buffer[bufpos], tmp, len);
+ result->pw_name = &buffer[bufpos];
+ bufpos += len;
+
+ /* get userPassword */
+ tmp = ldb_msg_find_attr_as_string(msg, "userPassword", NULL);
+ if (tmp == NULL) {
+ tmp = "LDB";
+ }
+ len = strlen(tmp)+1;
+ if (bufpos + len > buflen) {
+ /* buffer too small */
+ *errnop = errno = EAGAIN;
+ return NSS_STATUS_TRYAGAIN;
+ }
+ memcpy(&buffer[bufpos], tmp, len);
+ result->pw_passwd = &buffer[bufpos];
+ bufpos += len;
+
+ /* this backend never serves an uid 0 user */
+ result->pw_uid = ldb_msg_find_attr_as_int(msg, "uidNumber", 0);
+ if (result->pw_uid == 0) {
+ /* this is a fatal error */
+ *errnop = errno = ENOENT;
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ result->pw_gid = ldb_msg_find_attr_as_int(msg, "gidNumber", 0);
+ if (result->pw_gid == 0) {
+ /* this is a fatal error */
+ *errnop = errno = ENOENT;
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ /* get gecos */
+ tmp = ldb_msg_find_attr_as_string(msg, "gecos", NULL);
+ if (tmp == NULL) {
+ tmp = "";
+ }
+ len = strlen(tmp)+1;
+ if (bufpos + len > buflen) {
+ /* buffer too small */
+ *errnop = errno = EAGAIN;
+ return NSS_STATUS_TRYAGAIN;
+ }
+ memcpy(&buffer[bufpos], tmp, len);
+ result->pw_gecos = &buffer[bufpos];
+ bufpos += len;
+
+ /* get homeDirectory */
+ tmp = ldb_msg_find_attr_as_string(msg, "homeDirectory", NULL);
+ if (tmp == NULL) {
+ tmp = "";
+ }
+ len = strlen(tmp)+1;
+ if (bufpos + len > buflen) {
+ /* buffer too small */
+ *errnop = errno = EAGAIN;
+ return NSS_STATUS_TRYAGAIN;
+ }
+ memcpy(&buffer[bufpos], tmp, len);
+ result->pw_dir = &buffer[bufpos];
+ bufpos += len;
+
+ /* get shell */
+ tmp = ldb_msg_find_attr_as_string(msg, "loginShell", NULL);
+ if (tmp == NULL) {
+ tmp = "";
+ }
+ len = strlen(tmp)+1;
+ if (bufpos + len > buflen) {
+ /* buffer too small */
+ *errnop = errno = EAGAIN;
+ return NSS_STATUS_TRYAGAIN;
+ }
+ memcpy(&buffer[bufpos], tmp, len);
+ result->pw_shell = &buffer[bufpos];
+ bufpos += len;
+
+ return NSS_STATUS_SUCCESS;
+}
+
+NSS_STATUS _ldb_nss_fill_group(struct group *result,
+ char *buffer,
+ int buflen,
+ int *errnop,
+ struct ldb_message *group,
+ struct ldb_result *members)
+{
+ const char *tmp;
+ size_t len;
+ size_t bufpos;
+ size_t lsize;
+ int i;
+
+ bufpos = 0;
+
+ /* get group name */
+ tmp = ldb_msg_find_attr_as_string(group, "cn", NULL);
+ if (tmp == NULL) {
+ /* this is a fatal error */
+ *errnop = errno = ENOENT;
+ return NSS_STATUS_UNAVAIL;
+ }
+ len = strlen(tmp)+1;
+ if (bufpos + len > buflen) {
+ /* buffer too small */
+ *errnop = errno = EAGAIN;
+ return NSS_STATUS_TRYAGAIN;
+ }
+ memcpy(&buffer[bufpos], tmp, len);
+ result->gr_name = &buffer[bufpos];
+ bufpos += len;
+
+ /* get userPassword */
+ tmp = ldb_msg_find_attr_as_string(group, "userPassword", NULL);
+ if (tmp == NULL) {
+ tmp = "LDB";
+ }
+ len = strlen(tmp)+1;
+ if (bufpos + len > buflen) {
+ /* buffer too small */
+ *errnop = errno = EAGAIN;
+ return NSS_STATUS_TRYAGAIN;
+ }
+ memcpy(&buffer[bufpos], tmp, len);
+ result->gr_passwd = &buffer[bufpos];
+ bufpos += len;
+
+ result->gr_gid = ldb_msg_find_attr_as_int(group, "gidNumber", 0);
+ if (result->gr_gid == 0) {
+ /* this is a fatal error */
+ *errnop = errno = ENOENT;
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ /* check if there is enough memory for the list of pointers */
+ lsize = (members->count + 1) * sizeof(char *);
+
+ /* align buffer on pointer boundary */
+ bufpos += (sizeof(char*) - ((unsigned long)(buffer) % sizeof(char*)));
+ if ((buflen - bufpos) < lsize) {
+ /* buffer too small */
+ *errnop = errno = EAGAIN;
+ return NSS_STATUS_TRYAGAIN;
+ }
+
+ result->gr_mem = (char **)&buffer[bufpos];
+ bufpos += lsize;
+
+ for (i = 0; i < members->count; i++) {
+ tmp = ldb_msg_find_attr_as_string(members->msgs[i], "uid", NULL);
+ if (tmp == NULL) {
+ /* this is a fatal error */
+ *errnop = errno = ENOENT;
+ return NSS_STATUS_UNAVAIL;
+ }
+ len = strlen(tmp)+1;
+ if (bufpos + len > buflen) {
+ /* buffer too small */
+ *errnop = errno = EAGAIN;
+ return NSS_STATUS_TRYAGAIN;
+ }
+ memcpy(&buffer[bufpos], tmp, len);
+ result->gr_mem[i] = &buffer[bufpos];
+ bufpos += len;
+ }
+
+ result->gr_mem[i] = NULL;
+
+ return NSS_STATUS_SUCCESS;
+}
+
+NSS_STATUS _ldb_nss_fill_initgr(gid_t group,
+ long int limit,
+ long int *start,
+ long int *size,
+ gid_t **groups,
+ int *errnop,
+ struct ldb_result *grlist)
+{
+ NSS_STATUS ret;
+ int i;
+
+ for (i = 0; i < grlist->count; i++) {
+
+ if (limit && (*start > limit)) {
+ /* TODO: warn no all groups were reported */
+ *errnop = 0;
+ ret = NSS_STATUS_SUCCESS;
+ goto done;
+ }
+
+ if (*start == *size) {
+ /* buffer full, enlarge it */
+ long int gs;
+ gid_t *gm;
+
+ gs = (*size) + 32;
+ if (limit && (gs > limit)) {
+ gs = limit;
+ }
+
+ gm = (gid_t *)realloc((*groups), gs * sizeof(gid_t));
+ if ( ! gm) {
+ *errnop = ENOMEM;
+ ret = NSS_STATUS_UNAVAIL;
+ goto done;
+ }
+
+ *groups = gm;
+ *size = gs;
+ }
+
+ (*groups)[*start] = ldb_msg_find_attr_as_int(grlist->msgs[i], "gidNumber", 0);
+ if ((*groups)[*start] == 0 || (*groups)[*start] == group) {
+ /* skip root group or primary group */
+ continue;
+ }
+ (*start)++;
+
+ }
+
+ *errnop = 0;
+ ret = NSS_STATUS_SUCCESS;
+done:
+ return ret;
+}
+
+#define _LDB_NSS_ALLOC_CHECK(mem) do { if (!mem) { errno = ENOMEM; return NSS_STATUS_UNAVAIL; } } while(0)
+
+NSS_STATUS _ldb_nss_group_request(struct ldb_result **_res,
+ struct ldb_dn *group_dn,
+ const char * const *attrs,
+ const char *mattr)
+{
+ struct ldb_control **ctrls;
+ struct ldb_control *ctrl;
+ struct ldb_asq_control *asqc;
+ struct ldb_request *req;
+ int ret;
+ struct ldb_result *res = *_res;
+
+ ctrls = talloc_array(res, struct ldb_control *, 2);
+ _LDB_NSS_ALLOC_CHECK(ctrls);
+
+ ctrl = talloc(ctrls, struct ldb_control);
+ _LDB_NSS_ALLOC_CHECK(ctrl);
+
+ asqc = talloc(ctrl, struct ldb_asq_control);
+ _LDB_NSS_ALLOC_CHECK(asqc);
+
+ asqc->source_attribute = talloc_strdup(asqc, mattr);
+ _LDB_NSS_ALLOC_CHECK(asqc->source_attribute);
+
+ asqc->request = 1;
+ asqc->src_attr_len = strlen(asqc->source_attribute);
+ ctrl->oid = LDB_CONTROL_ASQ_OID;
+ ctrl->critical = 1;
+ ctrl->data = asqc;
+ ctrls[0] = ctrl;
+ ctrls[1] = NULL;
+
+ ret = ldb_build_search_req(
+ &req,
+ _ldb_nss_ctx->ldb,
+ res,
+ group_dn,
+ LDB_SCOPE_BASE,
+ "(objectClass=*)",
+ attrs,
+ ctrls,
+ res,
+ ldb_search_default_callback);
+
+ if (ret != LDB_SUCCESS) {
+ errno = ENOENT;
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ ldb_set_timeout(_ldb_nss_ctx->ldb, req, 0);
+
+ ret = ldb_request(_ldb_nss_ctx->ldb, req);
+
+ if (ret == LDB_SUCCESS) {
+ ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+ } else {
+ talloc_free(req);
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ talloc_free(req);
+ return NSS_STATUS_SUCCESS;
+}
+
diff --git a/source4/lib/ldb/nssldb/ldb-nss.h b/source4/lib/ldb/nssldb/ldb-nss.h
new file mode 100644
index 0000000000..583876fea6
--- /dev/null
+++ b/source4/lib/ldb/nssldb/ldb-nss.h
@@ -0,0 +1,84 @@
+/*
+ LDB nsswitch module
+
+ Copyright (C) Simo Sorce 2006
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _LDB_NSS
+#define _LDB_NSS
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+
+#include <nss.h>
+#include <pwd.h>
+#include <grp.h>
+
+#define _LDB_NSS_URL "etc/users.ldb"
+#define _LDB_NSS_BASEDN "CN=Users,CN=System"
+#define _LDB_NSS_PWENT_FILTER "(&(objectClass=posixAccount)(!(uidNumber=0))(!(gidNumber=0)))"
+#define _LDB_NSS_PWUID_FILTER "(&(objectClass=posixAccount)(uidNumber=%d)(!(gidNumber=0)))"
+#define _LDB_NSS_PWNAM_FILTER "(&(objectClass=posixAccount)(uid=%s)(!(uidNumber=0))(!(gidNumber=0)))"
+
+#define _LDB_NSS_GRENT_FILTER "(&(objectClass=posixGroup)(!(gidNumber=0)))"
+#define _LDB_NSS_GRGID_FILTER "(&(objectClass=posixGroup)(gidNumber=%d)))"
+#define _LDB_NSS_GRNAM_FILTER "(&(objectClass=posixGroup)(cn=%s)(!(gidNumber=0)))"
+
+typedef enum nss_status NSS_STATUS;
+
+struct _ldb_nss_context {
+
+ pid_t pid;
+
+ struct ldb_context *ldb;
+ struct ldb_dn *base;
+
+ int pw_cur;
+ struct ldb_result *pw_res;
+
+ int gr_cur;
+ struct ldb_result *gr_res;
+};
+
+NSS_STATUS _ldb_nss_init(void);
+
+NSS_STATUS _ldb_nss_fill_passwd(struct passwd *result,
+ char *buffer,
+ int buflen,
+ int *errnop,
+ struct ldb_message *msg);
+
+NSS_STATUS _ldb_nss_fill_group(struct group *result,
+ char *buffer,
+ int buflen,
+ int *errnop,
+ struct ldb_message *group,
+ struct ldb_result *members);
+
+NSS_STATUS _ldb_nss_fill_initgr(gid_t group,
+ long int limit,
+ long int *start,
+ long int *size,
+ gid_t **groups,
+ int *errnop,
+ struct ldb_result *grlist);
+
+NSS_STATUS _ldb_nss_group_request(struct ldb_result **res,
+ struct ldb_dn *group_dn,
+ const char * const *attrs,
+ const char *mattr);
+
+#endif /* _LDB_NSS */
diff --git a/source4/lib/ldb/nssldb/ldb-pwd.c b/source4/lib/ldb/nssldb/ldb-pwd.c
new file mode 100644
index 0000000000..6ab103a6fe
--- /dev/null
+++ b/source4/lib/ldb/nssldb/ldb-pwd.c
@@ -0,0 +1,242 @@
+/*
+ LDB nsswitch module
+
+ Copyright (C) Simo Sorce 2006
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "ldb-nss.h"
+
+extern struct _ldb_nss_context *_ldb_nss_ctx;
+
+const char *_ldb_nss_pw_attrs[] = {
+ "uid",
+ "userPassword",
+ "uidNumber",
+ "gidNumber",
+ "gecos",
+ "homeDirectory",
+ "loginShell",
+ NULL
+};
+
+NSS_STATUS _nss_ldb_setpwent(void)
+{
+ int ret;
+ ret = _ldb_nss_init();
+ if (ret != NSS_STATUS_SUCCESS) {
+ return ret;
+ }
+
+ _ldb_nss_ctx->pw_cur = 0;
+ if (_ldb_nss_ctx->pw_res != NULL) {
+ talloc_free(_ldb_nss_ctx->pw_res);
+ _ldb_nss_ctx->pw_res = NULL;
+ }
+
+ ret = ldb_search(_ldb_nss_ctx->ldb,
+ _ldb_nss_ctx->ldb,
+ &_ldb_nss_ctx->pw_res,
+ _ldb_nss_ctx->base,
+ LDB_SCOPE_SUBTREE,
+ _ldb_nss_pw_attrs,
+ _LDB_NSS_PWENT_FILTER);
+ if (ret != LDB_SUCCESS) {
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ return NSS_STATUS_SUCCESS;
+}
+
+NSS_STATUS _nss_ldb_endpwent(void)
+{
+ int ret;
+
+ ret = _ldb_nss_init();
+ if (ret != NSS_STATUS_SUCCESS) {
+ return ret;
+ }
+
+ _ldb_nss_ctx->pw_cur = 0;
+ if (_ldb_nss_ctx->pw_res) {
+ talloc_free(_ldb_nss_ctx->pw_res);
+ _ldb_nss_ctx->pw_res = NULL;
+ }
+
+ return NSS_STATUS_SUCCESS;
+}
+
+NSS_STATUS _nss_ldb_getpwent_r(struct passwd *result_buf,
+ char *buffer,
+ int buflen,
+ int *errnop)
+{
+ int ret;
+
+ ret = _ldb_nss_init();
+ if (ret != NSS_STATUS_SUCCESS) {
+ return ret;
+ }
+
+ *errnop = 0;
+
+ if (_ldb_nss_ctx->pw_cur >= _ldb_nss_ctx->pw_res->count) {
+ /* already returned all entries */
+ return NSS_STATUS_NOTFOUND;
+ }
+
+ ret = _ldb_nss_fill_passwd(result_buf,
+ buffer,
+ buflen,
+ errnop,
+ _ldb_nss_ctx->pw_res->msgs[_ldb_nss_ctx->pw_cur]);
+ if (ret != NSS_STATUS_SUCCESS) {
+ return ret;
+ }
+
+ _ldb_nss_ctx->pw_cur++;
+
+ return NSS_STATUS_SUCCESS;
+}
+
+NSS_STATUS _nss_ldb_getpwuid_r(uid_t uid, struct passwd *result_buf, char *buffer, size_t buflen, int *errnop)
+{
+ int ret;
+ char *filter;
+ struct ldb_result *res;
+
+ if (uid == 0) { /* we don't serve root uid by policy */
+ *errnop = errno = ENOENT;
+ return NSS_STATUS_NOTFOUND;
+ }
+
+ ret = _ldb_nss_init();
+ if (ret != NSS_STATUS_SUCCESS) {
+ return ret;
+ }
+
+ /* build the filter for this uid */
+ filter = talloc_asprintf(_ldb_nss_ctx, _LDB_NSS_PWUID_FILTER, uid);
+ if (filter == NULL) {
+ /* this is a fatal error */
+ *errnop = errno = ENOMEM;
+ ret = NSS_STATUS_UNAVAIL;
+ goto done;
+ }
+
+ /* search the entry */
+ ret = ldb_search(_ldb_nss_ctx->ldb,
+ _ldb_nss_ctx->ldb,
+ &res,
+ _ldb_nss_ctx->base,
+ LDB_SCOPE_SUBTREE,
+ _ldb_nss_pw_attrs,
+ filter);
+ if (ret != LDB_SUCCESS) {
+ /* this is a fatal error */
+ *errnop = errno = ENOENT;
+ ret = NSS_STATUS_UNAVAIL;
+ goto done;
+ }
+
+ /* if none found return */
+ if (res->count == 0) {
+ *errnop = errno = ENOENT;
+ ret = NSS_STATUS_NOTFOUND;
+ goto done;
+ }
+
+ if (res->count != 1) {
+ /* this is a fatal error */
+ *errnop = errno = ENOENT;
+ ret = NSS_STATUS_UNAVAIL;
+ goto done;
+ }
+
+ /* fill in the passwd struct */
+ ret = _ldb_nss_fill_passwd(result_buf,
+ buffer,
+ buflen,
+ errnop,
+ res->msgs[0]);
+
+done:
+ talloc_free(filter);
+ talloc_free(res);
+ return ret;
+}
+
+NSS_STATUS _nss_ldb_getpwnam_r(const char *name, struct passwd *result_buf, char *buffer, size_t buflen, int *errnop)
+{
+ int ret;
+ char *filter;
+ struct ldb_result *res;
+
+ ret = _ldb_nss_init();
+ if (ret != NSS_STATUS_SUCCESS) {
+ return ret;
+ }
+
+ /* build the filter for this name */
+ filter = talloc_asprintf(_ldb_nss_ctx, _LDB_NSS_PWNAM_FILTER, name);
+ if (filter == NULL) {
+ /* this is a fatal error */
+ *errnop = errno = ENOENT;
+ ret = NSS_STATUS_UNAVAIL;
+ goto done;
+ }
+
+ /* search the entry */
+ ret = ldb_search(_ldb_nss_ctx->ldb,
+ _ldb_nss_ctx->ldb,
+ &res,
+ _ldb_nss_ctx->base,
+ LDB_SCOPE_SUBTREE,
+ _ldb_nss_pw_attrs,
+ filter);
+ if (ret != LDB_SUCCESS) {
+ /* this is a fatal error */
+ *errnop = errno = ENOENT;
+ ret = NSS_STATUS_UNAVAIL;
+ goto done;
+ }
+
+ /* if none found return */
+ if (res->count == 0) {
+ *errnop = errno = ENOENT;
+ ret = NSS_STATUS_NOTFOUND;
+ goto done;
+ }
+
+ if (res->count != 1) {
+ /* this is a fatal error */
+ *errnop = errno = ENOENT;
+ ret = NSS_STATUS_UNAVAIL;
+ goto done;
+ }
+
+ /* fill in the passwd struct */
+ ret = _ldb_nss_fill_passwd(result_buf,
+ buffer,
+ buflen,
+ errnop,
+ res->msgs[0]);
+
+done:
+ talloc_free(filter);
+ talloc_free(res);
+ return ret;
+}
+
diff --git a/source4/lib/ldb/pyldb.c b/source4/lib/ldb/pyldb.c
new file mode 100644
index 0000000000..81b960979f
--- /dev/null
+++ b/source4/lib/ldb/pyldb.c
@@ -0,0 +1,2175 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Swig interface to ldb.
+
+ Copyright (C) 2005,2006 Tim Potter <tpot@samba.org>
+ Copyright (C) 2006 Simo Sorce <idra@samba.org>
+ Copyright (C) 2007-2008 Jelmer Vernooij <jelmer@samba.org>
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "replace.h"
+#include "ldb_private.h"
+#include <Python.h>
+#include "pyldb.h"
+
+/* There's no Py_ssize_t in 2.4, apparently */
+#if PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION < 5
+typedef int Py_ssize_t;
+typedef inquiry lenfunc;
+typedef intargfunc ssizeargfunc;
+#endif
+
+#ifndef Py_RETURN_NONE
+#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
+#endif
+
+/* Picked out of thin air. To do this properly, we should probably have some part of the
+ * errors in LDB be allocated to bindings ? */
+#define LDB_ERR_PYTHON_EXCEPTION 142
+
+static PyObject *PyExc_LdbError;
+
+void PyErr_SetLdbError(int ret, struct ldb_context *ldb_ctx)
+{
+ if (ret == LDB_ERR_PYTHON_EXCEPTION)
+ return; /* Python exception should already be set, just keep that */
+ PyErr_SetObject(PyExc_LdbError, Py_BuildValue(discard_const_p(char, "(i,s)"),
+ ret, ldb_ctx == NULL?ldb_strerror(ret):ldb_errstring(ldb_ctx)));
+}
+
+static PyObject *PyObject_FromLdbValue(struct ldb_context *ldb_ctx,
+ struct ldb_message_element *el,
+ struct ldb_val *val)
+{
+ const struct ldb_schema_attribute *a;
+ struct ldb_val new_val;
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+ PyObject *ret;
+
+ new_val = *val;
+
+ if (ldb_ctx != NULL) {
+ a = ldb_schema_attribute_by_name(ldb_ctx, el->name);
+
+ if (a != NULL) {
+ if (a->syntax->ldif_write_fn(ldb_ctx, mem_ctx, val, &new_val) != 0) {
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+ }
+ }
+
+ ret = PyString_FromStringAndSize((const char *)new_val.data, new_val.length);
+
+ talloc_free(mem_ctx);
+
+ return ret;
+}
+
+bool PyObject_AsDn(TALLOC_CTX *mem_ctx, PyObject *object,
+ struct ldb_context *ldb_ctx, struct ldb_dn **dn)
+{
+ struct ldb_dn *odn;
+
+ if (ldb_ctx != NULL && PyString_Check(object)) {
+ odn = ldb_dn_new(mem_ctx, ldb_ctx, PyString_AsString(object));
+ *dn = odn;
+ return true;
+ }
+
+ if (PyLdbDn_Check(object)) {
+ *dn = PyLdbDn_AsDn(object);
+ return true;
+ }
+
+ PyErr_SetString(PyExc_TypeError, "Expected DN");
+ return false;
+}
+
+static PyObject *PyLdbResult_FromResult(struct ldb_result *result)
+{
+ PyObject *ret;
+ int i;
+ if (result == NULL) {
+ Py_RETURN_NONE;
+ }
+ ret = PyList_New(result->count);
+ for (i = 0; i < result->count; i++) {
+ PyList_SetItem(ret, i, PyLdbMessage_FromMessage(result->msgs[i])
+ );
+ }
+ return ret;
+}
+
+static struct ldb_result *PyLdbResult_AsResult(TALLOC_CTX *mem_ctx, PyObject *obj)
+{
+ struct ldb_result *res;
+ int i;
+
+ if (obj == Py_None)
+ return NULL;
+
+ res = talloc_zero(mem_ctx, struct ldb_result);
+ res->count = PyList_Size(obj);
+ res->msgs = talloc_array(res, struct ldb_message *, res->count);
+ for (i = 0; i < res->count; i++) {
+ PyObject *item = PyList_GetItem(obj, i);
+ res->msgs[i] = PyLdbMessage_AsMessage(item);
+ }
+ return res;
+}
+
+static PyObject *py_ldb_dn_validate(PyLdbDnObject *self)
+{
+ return PyBool_FromLong(ldb_dn_validate(self->dn));
+}
+
+static PyObject *py_ldb_dn_is_valid(PyLdbDnObject *self)
+{
+ return PyBool_FromLong(ldb_dn_is_valid(self->dn));
+}
+
+static PyObject *py_ldb_dn_is_special(PyLdbDnObject *self)
+{
+ return PyBool_FromLong(ldb_dn_is_special(self->dn));
+}
+
+static PyObject *py_ldb_dn_is_null(PyLdbDnObject *self)
+{
+ return PyBool_FromLong(ldb_dn_is_null(self->dn));
+}
+
+static PyObject *py_ldb_dn_get_casefold(PyLdbDnObject *self)
+{
+ return PyString_FromString(ldb_dn_get_casefold(self->dn));
+}
+
+static PyObject *py_ldb_dn_get_linearized(PyLdbDnObject *self)
+{
+ return PyString_FromString(ldb_dn_get_linearized(self->dn));
+}
+
+static PyObject *py_ldb_dn_canonical_str(PyLdbDnObject *self)
+{
+ return PyString_FromString(ldb_dn_canonical_string(self->dn, self->dn));
+}
+
+static PyObject *py_ldb_dn_canonical_ex_str(PyLdbDnObject *self)
+{
+ return PyString_FromString(ldb_dn_canonical_ex_string(self->dn, self->dn));
+}
+
+static PyObject *py_ldb_dn_repr(PyLdbDnObject *self)
+{
+ return PyString_FromFormat("Dn(%s)", PyObject_REPR(PyString_FromString(ldb_dn_get_linearized(self->dn))));
+}
+
+static PyObject *py_ldb_dn_check_special(PyLdbDnObject *self, PyObject *args)
+{
+ char *name;
+
+ if (!PyArg_ParseTuple(args, "s", &name))
+ return NULL;
+
+ return ldb_dn_check_special(self->dn, name)?Py_True:Py_False;
+}
+
+static int py_ldb_dn_compare(PyLdbDnObject *dn1, PyLdbDnObject *dn2)
+{
+ return ldb_dn_compare(dn1->dn, dn2->dn);
+}
+
+static PyObject *py_ldb_dn_get_parent(PyLdbDnObject *self)
+{
+ struct ldb_dn *dn = PyLdbDn_AsDn((PyObject *)self);
+ return PyLdbDn_FromDn(ldb_dn_get_parent(NULL, dn));
+}
+
+#define dn_ldb_ctx(dn) ((struct ldb_context *)dn)
+
+static PyObject *py_ldb_dn_add_child(PyLdbDnObject *self, PyObject *args)
+{
+ PyObject *py_other;
+ struct ldb_dn *dn, *other;
+ if (!PyArg_ParseTuple(args, "O", &py_other))
+ return NULL;
+
+ dn = PyLdbDn_AsDn((PyObject *)self);
+
+ if (!PyObject_AsDn(NULL, py_other, dn_ldb_ctx(dn), &other))
+ return NULL;
+
+ return ldb_dn_add_child(dn, other)?Py_True:Py_False;
+}
+
+static PyObject *py_ldb_dn_add_base(PyLdbDnObject *self, PyObject *args)
+{
+ PyObject *py_other;
+ struct ldb_dn *other, *dn;
+ if (!PyArg_ParseTuple(args, "O", &py_other))
+ return NULL;
+
+ dn = PyLdbDn_AsDn((PyObject *)self);
+
+ if (!PyObject_AsDn(NULL, py_other, dn_ldb_ctx(dn), &other))
+ return NULL;
+
+ return ldb_dn_add_base(dn, other)?Py_True:Py_False;
+}
+
+static PyMethodDef py_ldb_dn_methods[] = {
+ { "validate", (PyCFunction)py_ldb_dn_validate, METH_NOARGS,
+ "S.validate() -> bool\n"
+ "Validate DN is correct." },
+ { "is_valid", (PyCFunction)py_ldb_dn_is_valid, METH_NOARGS,
+ "S.is_valid() -> bool\n" },
+ { "is_special", (PyCFunction)py_ldb_dn_is_special, METH_NOARGS,
+ "S.is_special() -> bool\n"
+ "Check whether this is a special LDB DN." },
+ { "is_null", (PyCFunction)py_ldb_dn_is_null, METH_NOARGS,
+ "Check whether this is a null DN." },
+ { "get_casefold", (PyCFunction)py_ldb_dn_get_casefold, METH_NOARGS,
+ NULL },
+ { "get_linearized", (PyCFunction)py_ldb_dn_get_linearized, METH_NOARGS,
+ NULL },
+ { "canonical_str", (PyCFunction)py_ldb_dn_canonical_str, METH_NOARGS,
+ "S.canonical_str() -> string\n"
+ "Canonical version of this DN (like a posix path)." },
+ { "canonical_ex_str", (PyCFunction)py_ldb_dn_canonical_ex_str, METH_NOARGS,
+ "S.canonical_ex_str() -> string\n"
+ "Canonical version of this DN (like a posix path, with terminating newline)." },
+ { "check_special", (PyCFunction)py_ldb_dn_is_special, METH_VARARGS,
+ NULL },
+ { "parent", (PyCFunction)py_ldb_dn_get_parent, METH_NOARGS,
+ "S.parent() -> dn\n"
+ "Get the parent for this DN." },
+ { "add_child", (PyCFunction)py_ldb_dn_add_child, METH_VARARGS,
+ "S.add_child(dn) -> None\n"
+ "Add a child DN to this DN." },
+ { "add_base", (PyCFunction)py_ldb_dn_add_base, METH_VARARGS,
+ "S.add_base(dn) -> None\n"
+ "Add a base DN to this DN." },
+ { "check_special", (PyCFunction)py_ldb_dn_check_special, METH_VARARGS,
+ NULL },
+ { NULL }
+};
+
+static Py_ssize_t py_ldb_dn_len(PyLdbDnObject *self)
+{
+ return ldb_dn_get_comp_num(PyLdbDn_AsDn((PyObject *)self));
+}
+
+static PyObject *py_ldb_dn_concat(PyLdbDnObject *self, PyObject *py_other)
+{
+ struct ldb_dn *dn = PyLdbDn_AsDn((PyObject *)self),
+ *other;
+ struct ldb_dn *ret = ldb_dn_copy(NULL, dn);
+ if (!PyObject_AsDn(NULL, py_other, NULL, &other))
+ return NULL;
+ ldb_dn_add_child(ret, other);
+ return PyLdbDn_FromDn(ret);
+}
+
+static PySequenceMethods py_ldb_dn_seq = {
+ .sq_length = (lenfunc)py_ldb_dn_len,
+ .sq_concat = (binaryfunc)py_ldb_dn_concat,
+};
+
+static PyObject *py_ldb_dn_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
+{
+ struct ldb_dn *ret;
+ char *str;
+ PyObject *py_ldb;
+ struct ldb_context *ldb_ctx;
+ PyLdbDnObject *py_ret;
+ const char * const kwnames[] = { "ldb", "dn", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "Os",
+ discard_const_p(char *, kwnames),
+ &py_ldb, &str))
+ return NULL;
+
+ ldb_ctx = PyLdb_AsLdbContext(py_ldb);
+
+ ret = ldb_dn_new(ldb_ctx, ldb_ctx, str);
+ /* ldb_dn_new() doesn't accept NULL as memory context, so
+ we do it this way... */
+ talloc_steal(NULL, ret);
+
+ if (ret == NULL || !ldb_dn_validate(ret)) {
+ PyErr_SetString(PyExc_ValueError, "unable to parse dn string");
+ return NULL;
+ }
+
+ py_ret = (PyLdbDnObject *)type->tp_alloc(type, 0);
+ if (ret == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+ py_ret->dn = ret;
+ return (PyObject *)py_ret;
+}
+
+PyObject *PyLdbDn_FromDn(struct ldb_dn *dn)
+{
+ PyLdbDnObject *py_ret;
+ py_ret = (PyLdbDnObject *)PyLdbDn.tp_alloc(&PyLdbDn, 0);
+ if (py_ret == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+ py_ret->mem_ctx = talloc_new(NULL);
+ py_ret->dn = talloc_reference(py_ret->mem_ctx, dn);
+ return (PyObject *)py_ret;
+}
+
+static void py_ldb_dn_dealloc(PyLdbDnObject *self)
+{
+ talloc_free(self->mem_ctx);
+ self->ob_type->tp_free(self);
+}
+
+PyTypeObject PyLdbDn = {
+ .tp_name = "Dn",
+ .tp_methods = py_ldb_dn_methods,
+ .tp_str = (reprfunc)py_ldb_dn_get_linearized,
+ .tp_repr = (reprfunc)py_ldb_dn_repr,
+ .tp_compare = (cmpfunc)py_ldb_dn_compare,
+ .tp_as_sequence = &py_ldb_dn_seq,
+ .tp_doc = "A LDB distinguished name.",
+ .tp_new = py_ldb_dn_new,
+ .tp_dealloc = (destructor)py_ldb_dn_dealloc,
+ .tp_basicsize = sizeof(PyLdbObject),
+ .tp_flags = Py_TPFLAGS_DEFAULT,
+};
+
+/* Debug */
+static void py_ldb_debug(void *context, enum ldb_debug_level level, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(3, 0);
+static void py_ldb_debug(void *context, enum ldb_debug_level level, const char *fmt, va_list ap)
+{
+ PyObject *fn = (PyObject *)context;
+ PyObject_CallFunction(fn, discard_const_p(char, "(i,O)"), level, PyString_FromFormatV(fmt, ap));
+}
+
+static PyObject *py_ldb_set_debug(PyLdbObject *self, PyObject *args)
+{
+ PyObject *cb;
+
+ if (!PyArg_ParseTuple(args, "O", &cb))
+ return NULL;
+
+ Py_INCREF(cb);
+ /* FIXME: Where do we DECREF cb ? */
+ PyErr_LDB_ERROR_IS_ERR_RAISE(ldb_set_debug(self->ldb_ctx, py_ldb_debug, cb), PyLdb_AsLdbContext(self));
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_ldb_set_create_perms(PyTypeObject *self, PyObject *args)
+{
+ unsigned int perms;
+ if (!PyArg_ParseTuple(args, "I", &perms))
+ return NULL;
+
+ ldb_set_create_perms(PyLdb_AsLdbContext(self), perms);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_ldb_set_modules_dir(PyTypeObject *self, PyObject *args)
+{
+ char *modules_dir;
+ if (!PyArg_ParseTuple(args, "s", &modules_dir))
+ return NULL;
+
+ ldb_set_modules_dir(PyLdb_AsLdbContext(self), modules_dir);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_ldb_transaction_start(PyLdbObject *self)
+{
+ PyErr_LDB_ERROR_IS_ERR_RAISE(ldb_transaction_start(PyLdb_AsLdbContext(self)), PyLdb_AsLdbContext(self));
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_ldb_transaction_commit(PyLdbObject *self)
+{
+ PyErr_LDB_ERROR_IS_ERR_RAISE(ldb_transaction_commit(PyLdb_AsLdbContext(self)), PyLdb_AsLdbContext(self));
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_ldb_transaction_cancel(PyLdbObject *self)
+{
+ PyErr_LDB_ERROR_IS_ERR_RAISE(ldb_transaction_cancel(PyLdb_AsLdbContext(self)), PyLdb_AsLdbContext(self));
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_ldb_setup_wellknown_attributes(PyLdbObject *self)
+{
+ PyErr_LDB_ERROR_IS_ERR_RAISE(ldb_setup_wellknown_attributes(PyLdb_AsLdbContext(self)), PyLdb_AsLdbContext(self));
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_ldb_repr(PyLdbObject *self)
+{
+ return PyString_FromFormat("<ldb connection>");
+}
+
+static PyObject *py_ldb_get_root_basedn(PyLdbObject *self)
+{
+ struct ldb_dn *dn = ldb_get_root_basedn(PyLdb_AsLdbContext(self));
+ if (dn == NULL)
+ Py_RETURN_NONE;
+ return PyLdbDn_FromDn(dn);
+}
+
+
+static PyObject *py_ldb_get_schema_basedn(PyLdbObject *self)
+{
+ struct ldb_dn *dn = ldb_get_schema_basedn(PyLdb_AsLdbContext(self));
+ if (dn == NULL)
+ Py_RETURN_NONE;
+ return PyLdbDn_FromDn(dn);
+}
+
+
+static PyObject *py_ldb_get_config_basedn(PyLdbObject *self)
+{
+ struct ldb_dn *dn = ldb_get_config_basedn(PyLdb_AsLdbContext(self));
+ if (dn == NULL)
+ Py_RETURN_NONE;
+ return PyLdbDn_FromDn(dn);
+}
+
+
+static PyObject *py_ldb_get_default_basedn(PyLdbObject *self)
+{
+ struct ldb_dn *dn = ldb_get_default_basedn(PyLdb_AsLdbContext(self));
+ if (dn == NULL)
+ Py_RETURN_NONE;
+ return PyLdbDn_FromDn(dn);
+}
+
+static const char **PyList_AsStringList(TALLOC_CTX *mem_ctx, PyObject *list)
+{
+ const char **ret;
+ int i;
+ if (!PyList_Check(list)) {
+ PyErr_SetString(PyExc_TypeError, "options is not a list");
+ return NULL;
+ }
+ ret = talloc_array(NULL, const char *, PyList_Size(list)+1);
+ for (i = 0; i < PyList_Size(list); i++) {
+ PyObject *item = PyList_GetItem(list, i);
+ if (!PyString_Check(item)) {
+ PyErr_SetString(PyExc_TypeError, "options should be strings");
+ return NULL;
+ }
+ ret[i] = PyString_AsString(item);
+ }
+ ret[i] = NULL;
+ return ret;
+}
+
+static int py_ldb_init(PyLdbObject *self, PyObject *args, PyObject *kwargs)
+{
+ const char * const kwnames[] = { "url", "flags", "options", NULL };
+ char *url = NULL;
+ PyObject *py_options = Py_None;
+ const char **options;
+ int flags = 0;
+ int ret;
+ struct ldb_context *ldb;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ziO:Ldb.__init__",
+ discard_const_p(char *, kwnames),
+ &url, &flags, &py_options))
+ return -1;
+
+ ldb = PyLdb_AsLdbContext(self);
+
+ if (py_options == Py_None) {
+ options = NULL;
+ } else {
+ options = PyList_AsStringList(ldb, py_options);
+ if (options == NULL)
+ return -1;
+ }
+
+ if (url != NULL) {
+ ret = ldb_connect(ldb, url, flags, options);
+ if (ret != LDB_SUCCESS) {
+ PyErr_SetLdbError(ret, ldb);
+ return -1;
+ }
+ }
+
+ talloc_free(options);
+ return 0;
+}
+
+static PyObject *py_ldb_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
+{
+ PyLdbObject *ret;
+ struct ldb_context *ldb;
+ ldb = ldb_init(NULL, NULL);
+ if (ldb == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ ret = (PyLdbObject *)type->tp_alloc(type, 0);
+ if (ret == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+ ret->ldb_ctx = ldb;
+ return (PyObject *)ret;
+}
+
+static PyObject *py_ldb_connect(PyLdbObject *self, PyObject *args, PyObject *kwargs)
+{
+ char *url;
+ int flags = 0;
+ PyObject *py_options = Py_None;
+ int ret;
+ const char **options;
+ const char * const kwnames[] = { "url", "flags", "options", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|iO",
+ discard_const_p(char *, kwnames),
+ &url, &flags, &py_options))
+ return NULL;
+
+ if (py_options == Py_None) {
+ options = NULL;
+ } else {
+ options = PyList_AsStringList(NULL, py_options);
+ if (options == NULL)
+ return NULL;
+ }
+
+ ret = ldb_connect(PyLdb_AsLdbContext(self), url, flags, options);
+ talloc_free(options);
+
+ PyErr_LDB_ERROR_IS_ERR_RAISE(ret, PyLdb_AsLdbContext(self));
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_ldb_modify(PyLdbObject *self, PyObject *args)
+{
+ PyObject *py_msg;
+ int ret;
+ if (!PyArg_ParseTuple(args, "O", &py_msg))
+ return NULL;
+
+ if (!PyLdbMessage_Check(py_msg)) {
+ PyErr_SetString(PyExc_TypeError, "Expected Ldb Message");
+ return NULL;
+ }
+
+ ret = ldb_modify(PyLdb_AsLdbContext(self), PyLdbMessage_AsMessage(py_msg));
+ PyErr_LDB_ERROR_IS_ERR_RAISE(ret, PyLdb_AsLdbContext(self));
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_ldb_add(PyLdbObject *self, PyObject *args)
+{
+ PyObject *py_msg;
+ int ret;
+ Py_ssize_t dict_pos, msg_pos;
+ struct ldb_message_element *msgel;
+ struct ldb_message *msg;
+ PyObject *key, *value;
+
+ if (!PyArg_ParseTuple(args, "O", &py_msg))
+ return NULL;
+
+ if (PyDict_Check(py_msg)) {
+ PyObject *dn_value = PyDict_GetItemString(py_msg, "dn");
+ msg = ldb_msg_new(NULL);
+ msg->elements = talloc_zero_array(msg, struct ldb_message_element, PyDict_Size(py_msg));
+ msg_pos = dict_pos = 0;
+ if (dn_value) {
+ if (!PyObject_AsDn(msg, dn_value, PyLdb_AsLdbContext(self), &msg->dn)) {
+ PyErr_SetString(PyExc_TypeError, "unable to import dn object");
+ return NULL;
+ }
+ if (msg->dn == NULL) {
+ PyErr_SetString(PyExc_TypeError, "dn set but not found");
+ return NULL;
+ }
+ }
+
+ while (PyDict_Next(py_msg, &dict_pos, &key, &value)) {
+ char *key_str = PyString_AsString(key);
+ if (strcmp(key_str, "dn") != 0) {
+ msgel = PyObject_AsMessageElement(msg->elements, value, 0, key_str);
+ if (msgel == NULL) {
+ PyErr_SetString(PyExc_TypeError, "unable to import element");
+ return NULL;
+ }
+ memcpy(&msg->elements[msg_pos], msgel, sizeof(*msgel));
+ msg_pos++;
+ }
+ }
+
+ if (msg->dn == NULL) {
+ PyErr_SetString(PyExc_TypeError, "no dn set");
+ return NULL;
+ }
+
+ msg->num_elements = msg_pos;
+ } else {
+ msg = PyLdbMessage_AsMessage(py_msg);
+ }
+
+ ret = ldb_add(PyLdb_AsLdbContext(self), msg);
+ PyErr_LDB_ERROR_IS_ERR_RAISE(ret, PyLdb_AsLdbContext(self));
+
+ Py_RETURN_NONE;
+}
+
+
+
+static PyObject *py_ldb_delete(PyLdbObject *self, PyObject *args)
+{
+ PyObject *py_dn;
+ struct ldb_dn *dn;
+ int ret;
+ struct ldb_context *ldb;
+ if (!PyArg_ParseTuple(args, "O", &py_dn))
+ return NULL;
+
+ ldb = PyLdb_AsLdbContext(self);
+
+ if (!PyObject_AsDn(NULL, py_dn, ldb, &dn))
+ return NULL;
+
+ ret = ldb_delete(ldb, dn);
+ PyErr_LDB_ERROR_IS_ERR_RAISE(ret, ldb);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_ldb_rename(PyLdbObject *self, PyObject *args)
+{
+ PyObject *py_dn1, *py_dn2;
+ struct ldb_dn *dn1, *dn2;
+ int ret;
+ struct ldb_context *ldb;
+ if (!PyArg_ParseTuple(args, "OO", &py_dn1, &py_dn2))
+ return NULL;
+
+ ldb = PyLdb_AsLdbContext(self);
+ if (!PyObject_AsDn(NULL, py_dn1, ldb, &dn1))
+ return NULL;
+
+ if (!PyObject_AsDn(NULL, py_dn2, ldb, &dn2))
+ return NULL;
+
+ ret = ldb_rename(ldb, dn1, dn2);
+ PyErr_LDB_ERROR_IS_ERR_RAISE(ret, ldb);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_ldb_schema_attribute_remove(PyLdbObject *self, PyObject *args)
+{
+ char *name;
+ if (!PyArg_ParseTuple(args, "s", &name))
+ return NULL;
+
+ ldb_schema_attribute_remove(PyLdb_AsLdbContext(self), name);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_ldb_schema_attribute_add(PyLdbObject *self, PyObject *args)
+{
+ char *attribute, *syntax;
+ unsigned int flags;
+ int ret;
+ if (!PyArg_ParseTuple(args, "sIs", &attribute, &flags, &syntax))
+ return NULL;
+
+ ret = ldb_schema_attribute_add(PyLdb_AsLdbContext(self), attribute, flags, syntax);
+
+ PyErr_LDB_ERROR_IS_ERR_RAISE(ret, PyLdb_AsLdbContext(self));
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *ldb_ldif_to_pyobject(struct ldb_ldif *ldif)
+{
+ if (ldif == NULL) {
+ Py_RETURN_NONE;
+ } else {
+ /* We don't want this attached to the 'ldb' any more */
+ talloc_steal(NULL, ldif);
+ return Py_BuildValue(discard_const_p(char, "(iO)"),
+ ldif->changetype,
+ PyLdbMessage_FromMessage(ldif->msg));
+ }
+}
+
+
+static PyObject *py_ldb_parse_ldif(PyLdbObject *self, PyObject *args)
+{
+ PyObject *list;
+ struct ldb_ldif *ldif;
+ const char *s;
+
+ if (!PyArg_ParseTuple(args, "s", &s))
+ return NULL;
+
+ list = PyList_New(0);
+ while ((ldif = ldb_ldif_read_string(self->ldb_ctx, &s)) != NULL) {
+ PyList_Append(list, ldb_ldif_to_pyobject(ldif));
+ }
+ return PyObject_GetIter(list);
+}
+
+static PyObject *py_ldb_schema_format_value(PyLdbObject *self, PyObject *args)
+{
+ const struct ldb_schema_attribute *a;
+ struct ldb_val old_val;
+ struct ldb_val new_val;
+ TALLOC_CTX *mem_ctx;
+ PyObject *ret;
+ char *element_name;
+ PyObject *val;
+
+ if (!PyArg_ParseTuple(args, "sO", &element_name, &val))
+ return NULL;
+
+ mem_ctx = talloc_new(NULL);
+
+ old_val.data = (uint8_t *)PyString_AsString(val);
+ old_val.length = PyString_Size(val);
+
+ a = ldb_schema_attribute_by_name(PyLdb_AsLdbContext(self), element_name);
+
+ if (a == NULL) {
+ Py_RETURN_NONE;
+ }
+
+ if (a->syntax->ldif_write_fn(PyLdb_AsLdbContext(self), mem_ctx, &old_val, &new_val) != 0) {
+ talloc_free(mem_ctx);
+ Py_RETURN_NONE;
+ }
+
+ ret = PyString_FromStringAndSize((const char *)new_val.data, new_val.length);
+
+ talloc_free(mem_ctx);
+
+ return ret;
+}
+
+static PyObject *py_ldb_search(PyLdbObject *self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *py_base = Py_None;
+ enum ldb_scope scope = LDB_SCOPE_DEFAULT;
+ char *expr = NULL;
+ PyObject *py_attrs = Py_None;
+ PyObject *py_controls = Py_None;
+ const char * const kwnames[] = { "base", "scope", "expression", "attrs", "controls", NULL };
+ int ret;
+ struct ldb_result *res;
+ struct ldb_request *req;
+ const char **attrs;
+ struct ldb_context *ldb_ctx;
+ struct ldb_control **parsed_controls;
+ struct ldb_dn *base;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OizOO",
+ discard_const_p(char *, kwnames),
+ &py_base, &scope, &expr, &py_attrs, &py_controls))
+ return NULL;
+
+ ldb_ctx = PyLdb_AsLdbContext(self);
+
+ if (py_attrs == Py_None) {
+ attrs = NULL;
+ } else {
+ attrs = PyList_AsStringList(ldb_ctx, py_attrs);
+ if (attrs == NULL)
+ return NULL;
+ }
+
+ if (py_base == Py_None) {
+ base = ldb_get_default_basedn(ldb_ctx);
+ } else {
+ if (!PyObject_AsDn(ldb_ctx, py_base, ldb_ctx, &base))
+ return NULL;
+ }
+
+ if (py_controls == Py_None) {
+ parsed_controls = NULL;
+ } else {
+ const char **controls = PyList_AsStringList(ldb_ctx, py_controls);
+ parsed_controls = ldb_parse_control_strings(ldb_ctx, ldb_ctx, controls);
+ talloc_free(controls);
+ }
+
+ res = talloc_zero(ldb_ctx, struct ldb_result);
+ if (res == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ ret = ldb_build_search_req(&req, ldb_ctx, ldb_ctx,
+ base,
+ scope,
+ expr,
+ attrs,
+ parsed_controls,
+ res,
+ ldb_search_default_callback,
+ NULL);
+
+ if (ret != LDB_SUCCESS) {
+ talloc_free(res);
+ PyErr_LDB_ERROR_IS_ERR_RAISE(ret, ldb_ctx);
+ return NULL;
+ }
+
+ ret = ldb_request(ldb_ctx, req);
+
+ if (ret == LDB_SUCCESS) {
+ ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+ }
+
+ talloc_free(req);
+
+ if (ret != LDB_SUCCESS) {
+ talloc_free(res);
+ PyErr_LDB_ERROR_IS_ERR_RAISE(ret, ldb_ctx);
+ return NULL;
+ }
+
+ return PyLdbResult_FromResult(res);
+}
+
+static PyObject *py_ldb_get_opaque(PyLdbObject *self, PyObject *args)
+{
+ char *name;
+ void *data;
+
+ if (!PyArg_ParseTuple(args, "s", &name))
+ return NULL;
+
+ data = ldb_get_opaque(PyLdb_AsLdbContext(self), name);
+
+ if (data == NULL)
+ Py_RETURN_NONE;
+
+ /* FIXME: More interpretation */
+
+ return Py_True;
+}
+
+static PyObject *py_ldb_set_opaque(PyLdbObject *self, PyObject *args)
+{
+ char *name;
+ PyObject *data;
+
+ if (!PyArg_ParseTuple(args, "sO", &name, &data))
+ return NULL;
+
+ /* FIXME: More interpretation */
+
+ ldb_set_opaque(PyLdb_AsLdbContext(self), name, data);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_ldb_modules(PyLdbObject *self)
+{
+ struct ldb_context *ldb = PyLdb_AsLdbContext(self);
+ PyObject *ret = PyList_New(0);
+ struct ldb_module *mod;
+
+ for (mod = ldb->modules; mod; mod = mod->next) {
+ PyList_Append(ret, PyLdbModule_FromModule(mod));
+ }
+
+ return ret;
+}
+
+static PyMethodDef py_ldb_methods[] = {
+ { "set_debug", (PyCFunction)py_ldb_set_debug, METH_VARARGS,
+ "S.set_debug(callback) -> None\n"
+ "Set callback for LDB debug messages.\n"
+ "The callback should accept a debug level and debug text." },
+ { "set_create_perms", (PyCFunction)py_ldb_set_create_perms, METH_VARARGS,
+ "S.set_create_perms(mode) -> None\n"
+ "Set mode to use when creating new LDB files." },
+ { "set_modules_dir", (PyCFunction)py_ldb_set_modules_dir, METH_VARARGS,
+ "S.set_modules_dir(path) -> None\n"
+ "Set path LDB should search for modules" },
+ { "transaction_start", (PyCFunction)py_ldb_transaction_start, METH_NOARGS,
+ "S.transaction_start() -> None\n"
+ "Start a new transaction." },
+ { "transaction_commit", (PyCFunction)py_ldb_transaction_commit, METH_NOARGS,
+ "S.transaction_commit() -> None\n"
+ "commit a new transaction." },
+ { "transaction_cancel", (PyCFunction)py_ldb_transaction_cancel, METH_NOARGS,
+ "S.transaction_cancel() -> None\n"
+ "cancel a new transaction." },
+ { "setup_wellknown_attributes", (PyCFunction)py_ldb_setup_wellknown_attributes, METH_NOARGS,
+ NULL },
+ { "get_root_basedn", (PyCFunction)py_ldb_get_root_basedn, METH_NOARGS,
+ NULL },
+ { "get_schema_basedn", (PyCFunction)py_ldb_get_schema_basedn, METH_NOARGS,
+ NULL },
+ { "get_default_basedn", (PyCFunction)py_ldb_get_default_basedn, METH_NOARGS,
+ NULL },
+ { "get_config_basedn", (PyCFunction)py_ldb_get_config_basedn, METH_NOARGS,
+ NULL },
+ { "connect", (PyCFunction)py_ldb_connect, METH_VARARGS|METH_KEYWORDS,
+ "S.connect(url, flags=0, options=None) -> None\n"
+ "Connect to a LDB URL." },
+ { "modify", (PyCFunction)py_ldb_modify, METH_VARARGS,
+ "S.modify(message) -> None\n"
+ "Modify an entry." },
+ { "add", (PyCFunction)py_ldb_add, METH_VARARGS,
+ "S.add(message) -> None\n"
+ "Add an entry." },
+ { "delete", (PyCFunction)py_ldb_delete, METH_VARARGS,
+ "S.delete(dn) -> None\n"
+ "Remove an entry." },
+ { "rename", (PyCFunction)py_ldb_rename, METH_VARARGS,
+ "S.rename(old_dn, new_dn) -> None\n"
+ "Rename an entry." },
+ { "search", (PyCFunction)py_ldb_search, METH_VARARGS|METH_KEYWORDS,
+ "S.search(base=None, scope=None, expression=None, attrs=None, controls=None) -> msgs\n"
+ "Search in a database.\n"
+ "\n"
+ ":param base: Optional base DN to search\n"
+ ":param scope: Search scope (SCOPE_BASE, SCOPE_ONELEVEL or SCOPE_SUBTREE)\n"
+ ":param expression: Optional search expression\n"
+ ":param attrs: Attributes to return (defaults to all)\n"
+ ":param controls: Optional list of controls\n"
+ ":return: Iterator over Message objects\n"
+ },
+ { "schema_attribute_remove", (PyCFunction)py_ldb_schema_attribute_remove, METH_VARARGS,
+ NULL },
+ { "schema_attribute_add", (PyCFunction)py_ldb_schema_attribute_add, METH_VARARGS,
+ NULL },
+ { "schema_format_value", (PyCFunction)py_ldb_schema_format_value, METH_VARARGS,
+ NULL },
+ { "parse_ldif", (PyCFunction)py_ldb_parse_ldif, METH_VARARGS,
+ "S.parse_ldif(ldif) -> iter(messages)\n"
+ "Parse a string formatted using LDIF." },
+ { "get_opaque", (PyCFunction)py_ldb_get_opaque, METH_VARARGS,
+ "S.get_opaque(name) -> value\n"
+ "Get an opaque value set on this LDB connection. \n"
+ ":note: The returned value may not be useful in Python."
+ },
+ { "set_opaque", (PyCFunction)py_ldb_set_opaque, METH_VARARGS,
+ "S.set_opaque(name, value) -> None\n"
+ "Set an opaque value on this LDB connection. \n"
+ ":note: Passing incorrect values may cause crashes." },
+ { "modules", (PyCFunction)py_ldb_modules, METH_NOARGS,
+ "S.modules() -> list\n"
+ "Return the list of modules on this LDB connection " },
+ { NULL },
+};
+
+PyObject *PyLdbModule_FromModule(struct ldb_module *mod)
+{
+ PyLdbModuleObject *ret;
+
+ ret = (PyLdbModuleObject *)PyLdbModule.tp_alloc(&PyLdbModule, 0);
+ if (ret == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+ ret->mem_ctx = talloc_new(NULL);
+ ret->mod = talloc_reference(ret->mem_ctx, mod);
+ return (PyObject *)ret;
+}
+
+static PyObject *py_ldb_get_firstmodule(PyLdbObject *self, void *closure)
+{
+ return PyLdbModule_FromModule(PyLdb_AsLdbContext(self)->modules);
+}
+
+static PyGetSetDef py_ldb_getset[] = {
+ { discard_const_p(char, "firstmodule"), (getter)py_ldb_get_firstmodule, NULL, NULL },
+ { NULL }
+};
+
+static int py_ldb_contains(PyLdbObject *self, PyObject *obj)
+{
+ struct ldb_context *ldb_ctx = PyLdb_AsLdbContext(self);
+ struct ldb_dn *dn;
+ struct ldb_result *result;
+ int ret;
+ int count;
+
+ if (!PyObject_AsDn(ldb_ctx, obj, ldb_ctx, &dn))
+ return -1;
+
+ ret = ldb_search(ldb_ctx, ldb_ctx, &result, dn, LDB_SCOPE_BASE, NULL, NULL);
+ if (ret != LDB_SUCCESS) {
+ PyErr_SetLdbError(ret, ldb_ctx);
+ return -1;
+ }
+
+ count = result->count;
+
+ talloc_free(result);
+
+ return count;
+}
+
+static PySequenceMethods py_ldb_seq = {
+ .sq_contains = (objobjproc)py_ldb_contains,
+};
+
+PyObject *PyLdb_FromLdbContext(struct ldb_context *ldb_ctx)
+{
+ PyLdbObject *ret;
+
+ ret = (PyLdbObject *)PyLdb.tp_alloc(&PyLdb, 0);
+ if (ret == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+ ret->mem_ctx = talloc_new(NULL);
+ ret->ldb_ctx = talloc_reference(ret->mem_ctx, ldb_ctx);
+ return (PyObject *)ret;
+}
+
+static void py_ldb_dealloc(PyLdbObject *self)
+{
+ talloc_free(self->mem_ctx);
+ self->ob_type->tp_free(self);
+}
+
+PyTypeObject PyLdb = {
+ .tp_name = "Ldb",
+ .tp_methods = py_ldb_methods,
+ .tp_repr = (reprfunc)py_ldb_repr,
+ .tp_new = py_ldb_new,
+ .tp_init = (initproc)py_ldb_init,
+ .tp_dealloc = (destructor)py_ldb_dealloc,
+ .tp_getset = py_ldb_getset,
+ .tp_getattro = PyObject_GenericGetAttr,
+ .tp_basicsize = sizeof(PyLdbObject),
+ .tp_doc = "Connection to a LDB database.",
+ .tp_as_sequence = &py_ldb_seq,
+ .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,
+};
+
+static PyObject *py_ldb_module_repr(PyLdbModuleObject *self)
+{
+ return PyString_FromFormat("<ldb module '%s'>", PyLdbModule_AsModule(self)->ops->name);
+}
+
+static PyObject *py_ldb_module_str(PyLdbModuleObject *self)
+{
+ return PyString_FromString(PyLdbModule_AsModule(self)->ops->name);
+}
+
+static PyObject *py_ldb_module_start_transaction(PyLdbModuleObject *self)
+{
+ PyLdbModule_AsModule(self)->ops->start_transaction(PyLdbModule_AsModule(self));
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_ldb_module_end_transaction(PyLdbModuleObject *self)
+{
+ PyLdbModule_AsModule(self)->ops->end_transaction(PyLdbModule_AsModule(self));
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_ldb_module_del_transaction(PyLdbModuleObject *self)
+{
+ PyLdbModule_AsModule(self)->ops->del_transaction(PyLdbModule_AsModule(self));
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_ldb_module_search(PyLdbModuleObject *self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *py_base, *py_tree, *py_attrs;
+ int ret, scope;
+ struct ldb_request *req;
+ const char * const kwnames[] = { "base", "scope", "tree", "attrs", NULL };
+ struct ldb_module *mod;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OiOO",
+ discard_const_p(char *, kwnames),
+ &py_base, &scope, &py_tree, &py_attrs))
+ return NULL;
+
+ mod = self->mod;
+
+ ret = ldb_build_search_req(&req, mod->ldb, NULL, PyLdbDn_AsDn(py_base),
+ scope, NULL /* expr */, py_attrs == Py_None?NULL:PyList_AsStringList(req, py_attrs),
+ NULL /* controls */, NULL, NULL, NULL);
+ PyErr_LDB_ERROR_IS_ERR_RAISE(ret, mod->ldb);
+
+ ret = mod->ops->search(mod, req);
+ talloc_free(req);
+
+ PyErr_LDB_ERROR_IS_ERR_RAISE(ret, mod->ldb);
+
+ return PyLdbResult_FromResult(req->op.search.res);
+}
+
+
+static PyObject *py_ldb_module_add(PyLdbModuleObject *self, PyObject *args)
+{
+ struct ldb_request *req;
+ PyObject *py_message;
+ int ret;
+ struct ldb_module *mod;
+
+ if (!PyArg_ParseTuple(args, "O", &py_message))
+ return NULL;
+
+ req = talloc_zero(NULL, struct ldb_request);
+ req->operation = LDB_ADD;
+ req->op.add.message = PyLdbMessage_AsMessage(py_message);
+
+ mod = PyLdbModule_AsModule(self);
+ ret = mod->ops->add(mod, req);
+
+ PyErr_LDB_ERROR_IS_ERR_RAISE(ret, mod->ldb);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_ldb_module_modify(PyLdbModuleObject *self, PyObject *args)
+{
+ int ret;
+ struct ldb_request *req;
+ PyObject *py_message;
+ struct ldb_module *mod;
+
+ if (!PyArg_ParseTuple(args, "O", &py_message))
+ return NULL;
+
+ req = talloc_zero(NULL, struct ldb_request);
+ req->operation = LDB_MODIFY;
+ req->op.mod.message = PyLdbMessage_AsMessage(py_message);
+
+ mod = PyLdbModule_AsModule(self);
+ ret = mod->ops->modify(mod, req);
+
+ PyErr_LDB_ERROR_IS_ERR_RAISE(ret, mod->ldb);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_ldb_module_delete(PyLdbModuleObject *self, PyObject *args)
+{
+ int ret;
+ struct ldb_request *req;
+ PyObject *py_dn;
+
+ if (!PyArg_ParseTuple(args, "O", &py_dn))
+ return NULL;
+
+ req = talloc_zero(NULL, struct ldb_request);
+ req->operation = LDB_DELETE;
+ req->op.del.dn = PyLdbDn_AsDn(py_dn);
+
+ ret = PyLdbModule_AsModule(self)->ops->del(PyLdbModule_AsModule(self), req);
+
+ PyErr_LDB_ERROR_IS_ERR_RAISE(ret, NULL);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_ldb_module_rename(PyLdbModuleObject *self, PyObject *args)
+{
+ int ret;
+ struct ldb_request *req;
+ PyObject *py_dn1, *py_dn2;
+
+ if (!PyArg_ParseTuple(args, "OO", &py_dn1, &py_dn2))
+ return NULL;
+
+ req = talloc_zero(NULL, struct ldb_request);
+
+ req->operation = LDB_RENAME;
+ req->op.rename.olddn = PyLdbDn_AsDn(py_dn1);
+ req->op.rename.newdn = PyLdbDn_AsDn(py_dn2);
+
+ ret = PyLdbModule_AsModule(self)->ops->rename(PyLdbModule_AsModule(self), req);
+
+ PyErr_LDB_ERROR_IS_ERR_RAISE(ret, NULL);
+
+ Py_RETURN_NONE;
+}
+
+static PyMethodDef py_ldb_module_methods[] = {
+ { "search", (PyCFunction)py_ldb_module_search, METH_VARARGS|METH_KEYWORDS, NULL },
+ { "add", (PyCFunction)py_ldb_module_add, METH_VARARGS, NULL },
+ { "modify", (PyCFunction)py_ldb_module_modify, METH_VARARGS, NULL },
+ { "rename", (PyCFunction)py_ldb_module_rename, METH_VARARGS, NULL },
+ { "delete", (PyCFunction)py_ldb_module_delete, METH_VARARGS, NULL },
+ { "start_transaction", (PyCFunction)py_ldb_module_start_transaction, METH_NOARGS, NULL },
+ { "end_transaction", (PyCFunction)py_ldb_module_end_transaction, METH_NOARGS, NULL },
+ { "del_transaction", (PyCFunction)py_ldb_module_del_transaction, METH_NOARGS, NULL },
+ { NULL },
+};
+
+static void py_ldb_module_dealloc(PyLdbModuleObject *self)
+{
+ talloc_free(self->mem_ctx);
+ self->ob_type->tp_free(self);
+}
+
+PyTypeObject PyLdbModule = {
+ .tp_name = "LdbModule",
+ .tp_methods = py_ldb_module_methods,
+ .tp_repr = (reprfunc)py_ldb_module_repr,
+ .tp_str = (reprfunc)py_ldb_module_str,
+ .tp_basicsize = sizeof(PyLdbModuleObject),
+ .tp_dealloc = (destructor)py_ldb_module_dealloc,
+ .tp_flags = Py_TPFLAGS_DEFAULT,
+};
+
+struct ldb_message_element *PyObject_AsMessageElement(TALLOC_CTX *mem_ctx,
+ PyObject *set_obj, int flags,
+ const char *attr_name)
+{
+ struct ldb_message_element *me;
+
+ if (PyLdbMessageElement_Check(set_obj))
+ return PyLdbMessageElement_AsMessageElement(set_obj);
+
+ me = talloc(mem_ctx, struct ldb_message_element);
+
+ me->name = attr_name;
+ me->flags = flags;
+ if (PyString_Check(set_obj)) {
+ me->num_values = 1;
+ me->values = talloc_array(me, struct ldb_val, me->num_values);
+ me->values[0].length = PyString_Size(set_obj);
+ me->values[0].data = (uint8_t *)talloc_strndup(me->values,
+ PyString_AsString(set_obj),
+ me->values[0].length);
+ } else if (PySequence_Check(set_obj)) {
+ int i;
+ me->num_values = PySequence_Size(set_obj);
+ me->values = talloc_array(me, struct ldb_val, me->num_values);
+ for (i = 0; i < me->num_values; i++) {
+ PyObject *obj = PySequence_GetItem(set_obj, i);
+
+ me->values[i].length = PyString_Size(obj);
+ me->values[i].data = (uint8_t *)PyString_AsString(obj);
+ }
+ } else {
+ talloc_free(me);
+ me = NULL;
+ }
+
+ return me;
+}
+
+
+static PyObject *ldb_msg_element_to_set(struct ldb_context *ldb_ctx,
+ struct ldb_message_element *me)
+{
+ int i;
+ PyObject *result;
+
+ /* Python << 2.5 doesn't have PySet_New and PySet_Add. */
+ result = PyList_New(me->num_values);
+
+ for (i = 0; i < me->num_values; i++) {
+ PyList_SetItem(result, i,
+ PyObject_FromLdbValue(ldb_ctx, me, &me->values[i]));
+ }
+
+ return result;
+}
+
+static PyObject *py_ldb_msg_element_get(PyLdbMessageElementObject *self, PyObject *args)
+{
+ int i;
+ if (!PyArg_ParseTuple(args, "i", &i))
+ return NULL;
+ if (i < 0 || i >= PyLdbMessageElement_AsMessageElement(self)->num_values)
+ Py_RETURN_NONE;
+
+ return PyObject_FromLdbValue(NULL, PyLdbMessageElement_AsMessageElement(self),
+ &(PyLdbMessageElement_AsMessageElement(self)->values[i]));
+}
+
+static PyMethodDef py_ldb_msg_element_methods[] = {
+ { "get", (PyCFunction)py_ldb_msg_element_get, METH_VARARGS, NULL },
+ { NULL },
+};
+
+static Py_ssize_t py_ldb_msg_element_len(PyLdbMessageElementObject *self)
+{
+ return PyLdbMessageElement_AsMessageElement(self)->num_values;
+}
+
+static PyObject *py_ldb_msg_element_find(PyLdbMessageElementObject *self, Py_ssize_t idx)
+{
+ struct ldb_message_element *el = PyLdbMessageElement_AsMessageElement(self);
+ if (idx < 0 || idx >= el->num_values) {
+ PyErr_SetString(PyExc_IndexError, "Out of range");
+ return NULL;
+ }
+ return PyString_FromStringAndSize((char *)el->values[idx].data, el->values[idx].length);
+}
+
+static PySequenceMethods py_ldb_msg_element_seq = {
+ .sq_length = (lenfunc)py_ldb_msg_element_len,
+ .sq_item = (ssizeargfunc)py_ldb_msg_element_find,
+};
+
+static int py_ldb_msg_element_cmp(PyLdbMessageElementObject *self, PyLdbMessageElementObject *other)
+{
+ return ldb_msg_element_compare(PyLdbMessageElement_AsMessageElement(self),
+ PyLdbMessageElement_AsMessageElement(other));
+}
+
+static PyObject *py_ldb_msg_element_iter(PyLdbMessageElementObject *self)
+{
+ return PyObject_GetIter(ldb_msg_element_to_set(NULL, PyLdbMessageElement_AsMessageElement(self)));
+}
+
+PyObject *PyLdbMessageElement_FromMessageElement(struct ldb_message_element *el, TALLOC_CTX *mem_ctx)
+{
+ PyLdbMessageElementObject *ret;
+ ret = (PyLdbMessageElementObject *)PyLdbMessageElement.tp_alloc(&PyLdbMessageElement, 0);
+ if (ret == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+ ret->mem_ctx = talloc_new(NULL);
+ if (talloc_reference(ret->mem_ctx, mem_ctx) == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+ ret->el = el;
+ return (PyObject *)ret;
+}
+
+static PyObject *py_ldb_msg_element_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
+{
+ PyObject *py_elements = NULL;
+ struct ldb_message_element *el;
+ int flags = 0;
+ char *name = NULL;
+ const char * const kwnames[] = { "elements", "flags", "name", NULL };
+ PyLdbMessageElementObject *ret;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|Ois",
+ discard_const_p(char *, kwnames),
+ &py_elements, &flags, &name))
+ return NULL;
+
+ el = talloc_zero(NULL, struct ldb_message_element);
+
+ if (py_elements != NULL) {
+ int i;
+ if (PyString_Check(py_elements)) {
+ el->num_values = 1;
+ el->values = talloc_array(el, struct ldb_val, 1);
+ el->values[0].data = (uint8_t *)PyString_AsString(py_elements);
+ el->values[0].length = PyString_Size(py_elements);
+ } else if (PySequence_Check(py_elements)) {
+ el->num_values = PySequence_Size(py_elements);
+ el->values = talloc_array(el, struct ldb_val, el->num_values);
+ for (i = 0; i < el->num_values; i++) {
+ PyObject *item = PySequence_GetItem(py_elements, i);
+ el->values[i].data = (uint8_t *)PyString_AsString(item);
+ el->values[i].length = PyString_Size(item);
+ }
+ } else {
+ PyErr_SetString(PyExc_TypeError,
+ "Expected string or list");
+ talloc_free(el);
+ return NULL;
+ }
+ }
+
+ el->flags = flags;
+ el->name = talloc_strdup(el, name);
+
+ ret = (PyLdbMessageElementObject *)PyLdbMessageElement.tp_alloc(&PyLdbMessageElement, 0);
+ if (ret == NULL) {
+ PyErr_NoMemory();
+ talloc_free(el);
+ return NULL;
+ }
+
+ ret->mem_ctx = talloc_new(NULL);
+ ret->el = talloc_reference(ret->mem_ctx, el);
+ return (PyObject *)ret;
+}
+
+static PyObject *py_ldb_msg_element_repr(PyLdbMessageElementObject *self)
+{
+ char *element_str = NULL;
+ int i;
+ struct ldb_message_element *el = PyLdbMessageElement_AsMessageElement(self);
+ PyObject *ret;
+
+ for (i = 0; i < el->num_values; i++) {
+ PyObject *o = py_ldb_msg_element_find(self, i);
+ if (element_str == NULL)
+ element_str = talloc_strdup(NULL, PyObject_REPR(o));
+ else
+ element_str = talloc_asprintf_append(element_str, ",%s", PyObject_REPR(o));
+ }
+
+ ret = PyString_FromFormat("MessageElement([%s])", element_str);
+
+ talloc_free(element_str);
+
+ return ret;
+}
+
+static PyObject *py_ldb_msg_element_str(PyLdbMessageElementObject *self)
+{
+ struct ldb_message_element *el = PyLdbMessageElement_AsMessageElement(self);
+
+ if (el->num_values == 1)
+ return PyString_FromStringAndSize((char *)el->values[0].data, el->values[0].length);
+ else
+ Py_RETURN_NONE;
+}
+
+static void py_ldb_msg_element_dealloc(PyLdbMessageElementObject *self)
+{
+ talloc_free(self->mem_ctx);
+ self->ob_type->tp_free(self);
+}
+
+PyTypeObject PyLdbMessageElement = {
+ .tp_name = "MessageElement",
+ .tp_basicsize = sizeof(PyLdbMessageElementObject),
+ .tp_dealloc = (destructor)py_ldb_msg_element_dealloc,
+ .tp_repr = (reprfunc)py_ldb_msg_element_repr,
+ .tp_str = (reprfunc)py_ldb_msg_element_str,
+ .tp_methods = py_ldb_msg_element_methods,
+ .tp_compare = (cmpfunc)py_ldb_msg_element_cmp,
+ .tp_iter = (getiterfunc)py_ldb_msg_element_iter,
+ .tp_as_sequence = &py_ldb_msg_element_seq,
+ .tp_new = py_ldb_msg_element_new,
+ .tp_flags = Py_TPFLAGS_DEFAULT,
+};
+
+static PyObject *py_ldb_msg_remove_attr(PyLdbMessageObject *self, PyObject *args)
+{
+ char *name;
+ if (!PyArg_ParseTuple(args, "s", &name))
+ return NULL;
+
+ ldb_msg_remove_attr(self->msg, name);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_ldb_msg_keys(PyLdbMessageObject *self)
+{
+ struct ldb_message *msg = PyLdbMessage_AsMessage(self);
+ int i, j = 0;
+ PyObject *obj = PyList_New(msg->num_elements+(msg->dn != NULL?1:0));
+ if (msg->dn != NULL) {
+ PyList_SetItem(obj, j, PyString_FromString("dn"));
+ j++;
+ }
+ for (i = 0; i < msg->num_elements; i++) {
+ PyList_SetItem(obj, j, PyString_FromString(msg->elements[i].name));
+ j++;
+ }
+ return obj;
+}
+
+static PyObject *py_ldb_msg_getitem_helper(PyLdbMessageObject *self, PyObject *py_name)
+{
+ struct ldb_message_element *el;
+ char *name = PyString_AsString(py_name);
+ struct ldb_message *msg = PyLdbMessage_AsMessage(self);
+ if (!strcmp(name, "dn"))
+ return PyLdbDn_FromDn(msg->dn);
+ el = ldb_msg_find_element(msg, name);
+ if (el == NULL) {
+ return NULL;
+ }
+ return (PyObject *)PyLdbMessageElement_FromMessageElement(el, msg);
+}
+
+static PyObject *py_ldb_msg_getitem(PyLdbMessageObject *self, PyObject *py_name)
+{
+ PyObject *ret = py_ldb_msg_getitem_helper(self, py_name);
+ if (ret == NULL) {
+ PyErr_SetString(PyExc_KeyError, "No such element");
+ return NULL;
+ }
+ return ret;
+}
+
+static PyObject *py_ldb_msg_get(PyLdbMessageObject *self, PyObject *args)
+{
+ PyObject *name, *ret;
+ if (!PyArg_ParseTuple(args, "O", &name))
+ return NULL;
+
+ ret = py_ldb_msg_getitem_helper(self, name);
+ if (ret == NULL)
+ Py_RETURN_NONE;
+ return ret;
+}
+
+static PyObject *py_ldb_msg_items(PyLdbMessageObject *self)
+{
+ struct ldb_message *msg = PyLdbMessage_AsMessage(self);
+ int i, j;
+ PyObject *l = PyList_New(msg->num_elements + (msg->dn == NULL?0:1));
+ j = 0;
+ if (msg->dn != NULL) {
+ PyList_SetItem(l, 0, Py_BuildValue("(sO)", "dn", PyLdbDn_FromDn(msg->dn)));
+ j++;
+ }
+ for (i = 0; i < msg->num_elements; i++, j++) {
+ PyList_SetItem(l, j, Py_BuildValue("(sO)", msg->elements[i].name, PyLdbMessageElement_FromMessageElement(&msg->elements[i], self->msg)));
+ }
+ return l;
+}
+
+static PyMethodDef py_ldb_msg_methods[] = {
+ { "keys", (PyCFunction)py_ldb_msg_keys, METH_NOARGS, NULL },
+ { "remove", (PyCFunction)py_ldb_msg_remove_attr, METH_VARARGS, NULL },
+ { "get", (PyCFunction)py_ldb_msg_get, METH_VARARGS, NULL },
+ { "items", (PyCFunction)py_ldb_msg_items, METH_NOARGS, NULL },
+ { NULL },
+};
+
+static PyObject *py_ldb_msg_iter(PyLdbMessageObject *self)
+{
+ PyObject *list, *iter;
+
+ list = py_ldb_msg_keys(self);
+ iter = PyObject_GetIter(list);
+ Py_DECREF(list);
+ return iter;
+}
+
+static int py_ldb_msg_setitem(PyLdbMessageObject *self, PyObject *name, PyObject *value)
+{
+ char *attr_name = PyString_AsString(name);
+ if (value == NULL) {
+ ldb_msg_remove_attr(self->msg, attr_name);
+ } else {
+ struct ldb_message_element *el = PyObject_AsMessageElement(NULL,
+ value, 0, attr_name);
+ if (el == NULL)
+ return -1;
+ talloc_steal(self->msg, el);
+ ldb_msg_remove_attr(PyLdbMessage_AsMessage(self), attr_name);
+ ldb_msg_add(PyLdbMessage_AsMessage(self), el, el->flags);
+ }
+ return 0;
+}
+
+static Py_ssize_t py_ldb_msg_length(PyLdbMessageObject *self)
+{
+ return PyLdbMessage_AsMessage(self)->num_elements;
+}
+
+static PyMappingMethods py_ldb_msg_mapping = {
+ .mp_length = (lenfunc)py_ldb_msg_length,
+ .mp_subscript = (binaryfunc)py_ldb_msg_getitem,
+ .mp_ass_subscript = (objobjargproc)py_ldb_msg_setitem,
+};
+
+static PyObject *py_ldb_msg_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
+{
+ const char * const kwnames[] = { "dn", NULL };
+ struct ldb_message *ret;
+ PyObject *pydn = NULL;
+ PyLdbMessageObject *py_ret;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O",
+ discard_const_p(char *, kwnames),
+ &pydn))
+ return NULL;
+
+ ret = ldb_msg_new(NULL);
+ if (ret == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ if (pydn != NULL)
+ if (!PyObject_AsDn(NULL, pydn, NULL, &ret->dn))
+ return NULL;
+
+ py_ret = (PyLdbMessageObject *)type->tp_alloc(type, 0);
+ if (py_ret == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ py_ret->mem_ctx = talloc_new(NULL);
+ py_ret->msg = talloc_reference(py_ret->mem_ctx, ret);
+ return (PyObject *)py_ret;
+}
+
+PyObject *PyLdbMessage_FromMessage(struct ldb_message *msg)
+{
+ PyLdbMessageObject *ret;
+
+ ret = (PyLdbMessageObject *)PyLdbMessage.tp_alloc(&PyLdbMessage, 0);
+ if (ret == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+ ret->mem_ctx = talloc_new(NULL);
+ ret->msg = talloc_reference(ret->mem_ctx, msg);
+ return (PyObject *)ret;
+}
+
+static PyObject *py_ldb_msg_get_dn(PyLdbMessageObject *self, void *closure)
+{
+ return PyLdbDn_FromDn(PyLdbMessage_AsMessage(self)->dn);
+}
+
+static int py_ldb_msg_set_dn(PyLdbMessageObject *self, PyObject *value, void *closure)
+{
+ PyLdbMessage_AsMessage(self)->dn = PyLdbDn_AsDn(value);
+ return 0;
+}
+
+static PyGetSetDef py_ldb_msg_getset[] = {
+ { discard_const_p(char, "dn"), (getter)py_ldb_msg_get_dn, (setter)py_ldb_msg_set_dn, NULL },
+ { NULL }
+};
+
+static PyObject *py_ldb_msg_repr(PyLdbMessageObject *self)
+{
+ PyObject *dict = PyDict_New(), *ret;
+ if (PyDict_Update(dict, (PyObject *)self) != 0)
+ return NULL;
+ ret = PyString_FromFormat("Message(%s)", PyObject_REPR(dict));
+ Py_DECREF(dict);
+ return ret;
+}
+
+static void py_ldb_msg_dealloc(PyLdbMessageObject *self)
+{
+ talloc_free(self->mem_ctx);
+ self->ob_type->tp_free(self);
+}
+
+PyTypeObject PyLdbMessage = {
+ .tp_name = "Message",
+ .tp_methods = py_ldb_msg_methods,
+ .tp_getset = py_ldb_msg_getset,
+ .tp_as_mapping = &py_ldb_msg_mapping,
+ .tp_basicsize = sizeof(PyLdbMessageObject),
+ .tp_dealloc = (destructor)py_ldb_msg_dealloc,
+ .tp_new = py_ldb_msg_new,
+ .tp_repr = (reprfunc)py_ldb_msg_repr,
+ .tp_flags = Py_TPFLAGS_DEFAULT,
+ .tp_iter = (getiterfunc)py_ldb_msg_iter,
+};
+
+PyObject *PyLdbTree_FromTree(struct ldb_parse_tree *tree)
+{
+ PyLdbTreeObject *ret;
+
+ ret = (PyLdbTreeObject *)PyLdbTree.tp_alloc(&PyLdbTree, 0);
+ if (ret == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ ret->mem_ctx = talloc_new(NULL);
+ ret->tree = talloc_reference(ret->mem_ctx, tree);
+ return (PyObject *)ret;
+}
+
+static void py_ldb_tree_dealloc(PyLdbTreeObject *self)
+{
+ talloc_free(self->mem_ctx);
+ self->ob_type->tp_free(self);
+}
+
+PyTypeObject PyLdbTree = {
+ .tp_name = "Tree",
+ .tp_basicsize = sizeof(PyLdbTreeObject),
+ .tp_dealloc = (destructor)py_ldb_tree_dealloc,
+ .tp_flags = Py_TPFLAGS_DEFAULT,
+};
+
+/* Ldb_module */
+static int py_module_search(struct ldb_module *mod, struct ldb_request *req)
+{
+ PyObject *py_ldb = (PyObject *)mod->private_data;
+ PyObject *py_result, *py_base, *py_attrs, *py_tree;
+
+ py_base = PyLdbDn_FromDn(req->op.search.base);
+
+ if (py_base == NULL)
+ return LDB_ERR_OPERATIONS_ERROR;
+
+ py_tree = PyLdbTree_FromTree(req->op.search.tree);
+
+ if (py_tree == NULL)
+ return LDB_ERR_OPERATIONS_ERROR;
+
+ if (req->op.search.attrs == NULL) {
+ py_attrs = Py_None;
+ } else {
+ int i, len;
+ for (len = 0; req->op.search.attrs[len]; len++);
+ py_attrs = PyList_New(len);
+ for (i = 0; i < len; i++)
+ PyList_SetItem(py_attrs, i, PyString_FromString(req->op.search.attrs[i]));
+ }
+
+ py_result = PyObject_CallMethod(py_ldb, discard_const_p(char, "search"),
+ discard_const_p(char, "OiOO"),
+ py_base, req->op.search.scope, py_tree, py_attrs);
+
+ Py_DECREF(py_attrs);
+ Py_DECREF(py_tree);
+ Py_DECREF(py_base);
+
+ if (py_result == NULL) {
+ return LDB_ERR_PYTHON_EXCEPTION;
+ }
+
+ req->op.search.res = PyLdbResult_AsResult(NULL, py_result);
+ if (req->op.search.res == NULL) {
+ return LDB_ERR_PYTHON_EXCEPTION;
+ }
+
+ Py_DECREF(py_result);
+
+ return LDB_SUCCESS;
+}
+
+static int py_module_add(struct ldb_module *mod, struct ldb_request *req)
+{
+ PyObject *py_ldb = (PyObject *)mod->private_data;
+ PyObject *py_result, *py_msg;
+
+ py_msg = PyLdbMessage_FromMessage(discard_const_p(struct ldb_message, req->op.add.message));
+
+ if (py_msg == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ py_result = PyObject_CallMethod(py_ldb, discard_const_p(char, "add"),
+ discard_const_p(char, "O"),
+ py_msg);
+
+ Py_DECREF(py_msg);
+
+ if (py_result == NULL) {
+ return LDB_ERR_PYTHON_EXCEPTION;
+ }
+
+ Py_DECREF(py_result);
+
+ return LDB_SUCCESS;
+}
+
+static int py_module_modify(struct ldb_module *mod, struct ldb_request *req)
+{
+ PyObject *py_ldb = (PyObject *)mod->private_data;
+ PyObject *py_result, *py_msg;
+
+ py_msg = PyLdbMessage_FromMessage(discard_const_p(struct ldb_message, req->op.mod.message));
+
+ if (py_msg == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ py_result = PyObject_CallMethod(py_ldb, discard_const_p(char, "modify"),
+ discard_const_p(char, "O"),
+ py_msg);
+
+ Py_DECREF(py_msg);
+
+ if (py_result == NULL) {
+ return LDB_ERR_PYTHON_EXCEPTION;
+ }
+
+ Py_DECREF(py_result);
+
+ return LDB_SUCCESS;
+}
+
+static int py_module_del(struct ldb_module *mod, struct ldb_request *req)
+{
+ PyObject *py_ldb = (PyObject *)mod->private_data;
+ PyObject *py_result, *py_dn;
+
+ py_dn = PyLdbDn_FromDn(req->op.del.dn);
+
+ if (py_dn == NULL)
+ return LDB_ERR_OPERATIONS_ERROR;
+
+ py_result = PyObject_CallMethod(py_ldb, discard_const_p(char, "delete"),
+ discard_const_p(char, "O"),
+ py_dn);
+
+ if (py_result == NULL) {
+ return LDB_ERR_PYTHON_EXCEPTION;
+ }
+
+ Py_DECREF(py_result);
+
+ return LDB_SUCCESS;
+}
+
+static int py_module_rename(struct ldb_module *mod, struct ldb_request *req)
+{
+ PyObject *py_ldb = (PyObject *)mod->private_data;
+ PyObject *py_result, *py_olddn, *py_newdn;
+
+ py_olddn = PyLdbDn_FromDn(req->op.rename.olddn);
+
+ if (py_olddn == NULL)
+ return LDB_ERR_OPERATIONS_ERROR;
+
+ py_newdn = PyLdbDn_FromDn(req->op.rename.newdn);
+
+ if (py_newdn == NULL)
+ return LDB_ERR_OPERATIONS_ERROR;
+
+ py_result = PyObject_CallMethod(py_ldb, discard_const_p(char, "rename"),
+ discard_const_p(char, "OO"),
+ py_olddn, py_newdn);
+
+ Py_DECREF(py_olddn);
+ Py_DECREF(py_newdn);
+
+ if (py_result == NULL) {
+ return LDB_ERR_PYTHON_EXCEPTION;
+ }
+
+ Py_DECREF(py_result);
+
+ return LDB_SUCCESS;
+}
+
+static int py_module_request(struct ldb_module *mod, struct ldb_request *req)
+{
+ PyObject *py_ldb = (PyObject *)mod->private_data;
+ PyObject *py_result;
+
+ py_result = PyObject_CallMethod(py_ldb, discard_const_p(char, "request"),
+ discard_const_p(char, ""));
+
+ return LDB_ERR_OPERATIONS_ERROR;
+}
+
+static int py_module_extended(struct ldb_module *mod, struct ldb_request *req)
+{
+ PyObject *py_ldb = (PyObject *)mod->private_data;
+ PyObject *py_result;
+
+ py_result = PyObject_CallMethod(py_ldb, discard_const_p(char, "extended"),
+ discard_const_p(char, ""));
+
+ return LDB_ERR_OPERATIONS_ERROR;
+}
+
+static int py_module_start_transaction(struct ldb_module *mod)
+{
+ PyObject *py_ldb = (PyObject *)mod->private_data;
+ PyObject *py_result;
+
+ py_result = PyObject_CallMethod(py_ldb, discard_const_p(char, "start_transaction"),
+ discard_const_p(char, ""));
+
+ if (py_result == NULL) {
+ return LDB_ERR_PYTHON_EXCEPTION;
+ }
+
+ Py_DECREF(py_result);
+
+ return LDB_SUCCESS;
+}
+
+static int py_module_end_transaction(struct ldb_module *mod)
+{
+ PyObject *py_ldb = (PyObject *)mod->private_data;
+ PyObject *py_result;
+
+ py_result = PyObject_CallMethod(py_ldb, discard_const_p(char, "end_transaction"),
+ discard_const_p(char, ""));
+
+ if (py_result == NULL) {
+ return LDB_ERR_PYTHON_EXCEPTION;
+ }
+
+ Py_DECREF(py_result);
+
+ return LDB_SUCCESS;
+}
+
+static int py_module_del_transaction(struct ldb_module *mod)
+{
+ PyObject *py_ldb = (PyObject *)mod->private_data;
+ PyObject *py_result;
+
+ py_result = PyObject_CallMethod(py_ldb, discard_const_p(char, "del_transaction"),
+ discard_const_p(char, ""));
+
+ if (py_result == NULL) {
+ return LDB_ERR_PYTHON_EXCEPTION;
+ }
+
+ Py_DECREF(py_result);
+
+ return LDB_SUCCESS;
+}
+
+static int py_module_destructor(struct ldb_module *mod)
+{
+ Py_DECREF((PyObject *)mod->private_data);
+ return 0;
+}
+
+static int py_module_init(struct ldb_module *mod)
+{
+ PyObject *py_class = (PyObject *)mod->ops->private_data;
+ PyObject *py_result, *py_next, *py_ldb;
+
+ py_ldb = PyLdb_FromLdbContext(mod->ldb);
+
+ if (py_ldb == NULL)
+ return LDB_ERR_OPERATIONS_ERROR;
+
+ py_next = PyLdbModule_FromModule(mod->next);
+
+ if (py_next == NULL)
+ return LDB_ERR_OPERATIONS_ERROR;
+
+ py_result = PyObject_CallFunction(py_class, discard_const_p(char, "OO"),
+ py_ldb, py_next);
+
+ if (py_result == NULL) {
+ return LDB_ERR_PYTHON_EXCEPTION;
+ }
+
+ mod->private_data = py_result;
+
+ talloc_set_destructor(mod, py_module_destructor);
+
+ return ldb_next_init(mod);
+}
+
+static PyObject *py_register_module(PyObject *module, PyObject *args)
+{
+ int ret;
+ struct ldb_module_ops *ops;
+ PyObject *input;
+
+ if (!PyArg_ParseTuple(args, "O", &input))
+ return NULL;
+
+ ops = talloc_zero(talloc_autofree_context(), struct ldb_module_ops);
+ if (ops == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ ops->name = talloc_strdup(ops, PyString_AsString(PyObject_GetAttrString(input, discard_const_p(char, "name"))));
+
+ Py_INCREF(input);
+ ops->private_data = input;
+ ops->init_context = py_module_init;
+ ops->search = py_module_search;
+ ops->add = py_module_add;
+ ops->modify = py_module_modify;
+ ops->del = py_module_del;
+ ops->rename = py_module_rename;
+ ops->request = py_module_request;
+ ops->extended = py_module_extended;
+ ops->start_transaction = py_module_start_transaction;
+ ops->end_transaction = py_module_end_transaction;
+ ops->del_transaction = py_module_del_transaction;
+
+ ret = ldb_register_module(ops);
+
+ PyErr_LDB_ERROR_IS_ERR_RAISE(ret, NULL);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_timestring(PyObject *module, PyObject *args)
+{
+ time_t t;
+ char *tresult;
+ PyObject *ret;
+ if (!PyArg_ParseTuple(args, "L", &t))
+ return NULL;
+ tresult = ldb_timestring(NULL, t);
+ ret = PyString_FromString(tresult);
+ talloc_free(tresult);
+ return ret;
+}
+
+static PyObject *py_string_to_time(PyObject *module, PyObject *args)
+{
+ char *str;
+ if (!PyArg_ParseTuple(args, "s", &str))
+ return NULL;
+
+ return PyInt_FromLong(ldb_string_to_time(str));
+}
+
+static PyObject *py_valid_attr_name(PyObject *self, PyObject *args)
+{
+ char *name;
+ if (!PyArg_ParseTuple(args, "s", &name))
+ return NULL;
+ return PyBool_FromLong(ldb_valid_attr_name(name));
+}
+
+static PyMethodDef py_ldb_global_methods[] = {
+ { "register_module", py_register_module, METH_VARARGS,
+ "S.register_module(module) -> None\n"
+ "Register a LDB module."},
+ { "timestring", py_timestring, METH_VARARGS,
+ "S.timestring(int) -> string\n"
+ "Generate a LDAP time string from a UNIX timestamp" },
+ { "string_to_time", py_string_to_time, METH_VARARGS,
+ "S.string_to_time(string) -> int\n"
+ "Parse a LDAP time string into a UNIX timestamp." },
+ { "valid_attr_name", py_valid_attr_name, METH_VARARGS,
+ "S.valid_attr_name(name) -> bool\n"
+ "Check whether the supplied name is a valid attribute name." },
+ { "open", (PyCFunction)py_ldb_new, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { NULL }
+};
+
+void initldb(void)
+{
+ PyObject *m;
+
+ if (PyType_Ready(&PyLdbDn) < 0)
+ return;
+
+ if (PyType_Ready(&PyLdbMessage) < 0)
+ return;
+
+ if (PyType_Ready(&PyLdbMessageElement) < 0)
+ return;
+
+ if (PyType_Ready(&PyLdb) < 0)
+ return;
+
+ if (PyType_Ready(&PyLdbModule) < 0)
+ return;
+
+ if (PyType_Ready(&PyLdbTree) < 0)
+ return;
+
+ m = Py_InitModule3("ldb", py_ldb_global_methods,
+ "An interface to LDB, a LDAP-like API that can either to talk an embedded database (TDB-based) or a standards-compliant LDAP server.");
+ if (m == NULL)
+ return;
+
+ PyModule_AddObject(m, "SCOPE_DEFAULT", PyInt_FromLong(LDB_SCOPE_DEFAULT));
+ PyModule_AddObject(m, "SCOPE_BASE", PyInt_FromLong(LDB_SCOPE_BASE));
+ PyModule_AddObject(m, "SCOPE_ONELEVEL", PyInt_FromLong(LDB_SCOPE_ONELEVEL));
+ PyModule_AddObject(m, "SCOPE_SUBTREE", PyInt_FromLong(LDB_SCOPE_SUBTREE));
+
+ PyModule_AddObject(m, "CHANGETYPE_NONE", PyInt_FromLong(LDB_CHANGETYPE_NONE));
+ PyModule_AddObject(m, "CHANGETYPE_ADD", PyInt_FromLong(LDB_CHANGETYPE_ADD));
+ PyModule_AddObject(m, "CHANGETYPE_DELETE", PyInt_FromLong(LDB_CHANGETYPE_DELETE));
+ PyModule_AddObject(m, "CHANGETYPE_MODIFY", PyInt_FromLong(LDB_CHANGETYPE_MODIFY));
+
+ PyModule_AddObject(m, "SUCCESS", PyInt_FromLong(LDB_SUCCESS));
+ PyModule_AddObject(m, "ERR_OPERATIONS_ERROR", PyInt_FromLong(LDB_ERR_OPERATIONS_ERROR));
+ PyModule_AddObject(m, "ERR_PROTOCOL_ERROR", PyInt_FromLong(LDB_ERR_PROTOCOL_ERROR));
+ PyModule_AddObject(m, "ERR_TIME_LIMIT_EXCEEDED", PyInt_FromLong(LDB_ERR_TIME_LIMIT_EXCEEDED));
+ PyModule_AddObject(m, "ERR_SIZE_LIMIT_EXCEEDED", PyInt_FromLong(LDB_ERR_SIZE_LIMIT_EXCEEDED));
+ PyModule_AddObject(m, "ERR_COMPARE_FALSE", PyInt_FromLong(LDB_ERR_COMPARE_FALSE));
+ PyModule_AddObject(m, "ERR_COMPARE_TRUE", PyInt_FromLong(LDB_ERR_COMPARE_TRUE));
+ PyModule_AddObject(m, "ERR_AUTH_METHOD_NOT_SUPPORTED", PyInt_FromLong(LDB_ERR_AUTH_METHOD_NOT_SUPPORTED));
+ PyModule_AddObject(m, "ERR_STRONG_AUTH_REQUIRED", PyInt_FromLong(LDB_ERR_STRONG_AUTH_REQUIRED));
+ PyModule_AddObject(m, "ERR_REFERRAL", PyInt_FromLong(LDB_ERR_REFERRAL));
+ PyModule_AddObject(m, "ERR_ADMIN_LIMIT_EXCEEDED", PyInt_FromLong(LDB_ERR_ADMIN_LIMIT_EXCEEDED));
+ PyModule_AddObject(m, "ERR_UNSUPPORTED_CRITICAL_EXTENSION", PyInt_FromLong(LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION));
+ PyModule_AddObject(m, "ERR_CONFIDENTIALITY_REQUIRED", PyInt_FromLong(LDB_ERR_CONFIDENTIALITY_REQUIRED));
+ PyModule_AddObject(m, "ERR_SASL_BIND_IN_PROGRESS", PyInt_FromLong(LDB_ERR_SASL_BIND_IN_PROGRESS));
+ PyModule_AddObject(m, "ERR_NO_SUCH_ATTRIBUTE", PyInt_FromLong(LDB_ERR_NO_SUCH_ATTRIBUTE));
+ PyModule_AddObject(m, "ERR_UNDEFINED_ATTRIBUTE_TYPE", PyInt_FromLong(LDB_ERR_UNDEFINED_ATTRIBUTE_TYPE));
+ PyModule_AddObject(m, "ERR_INAPPROPRIATE_MATCHING", PyInt_FromLong(LDB_ERR_INAPPROPRIATE_MATCHING));
+ PyModule_AddObject(m, "ERR_CONSTRAINT_VIOLATION", PyInt_FromLong(LDB_ERR_CONSTRAINT_VIOLATION));
+ PyModule_AddObject(m, "ERR_ATTRIBUTE_OR_VALUE_EXISTS", PyInt_FromLong(LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS));
+ PyModule_AddObject(m, "ERR_INVALID_ATTRIBUTE_SYNTAX", PyInt_FromLong(LDB_ERR_INVALID_ATTRIBUTE_SYNTAX));
+ PyModule_AddObject(m, "ERR_NO_SUCH_OBJECT", PyInt_FromLong(LDB_ERR_NO_SUCH_OBJECT));
+ PyModule_AddObject(m, "ERR_ALIAS_PROBLEM", PyInt_FromLong(LDB_ERR_ALIAS_PROBLEM));
+ PyModule_AddObject(m, "ERR_INVALID_DN_SYNTAX", PyInt_FromLong(LDB_ERR_INVALID_DN_SYNTAX));
+ PyModule_AddObject(m, "ERR_ALIAS_DEREFERINCING_PROBLEM", PyInt_FromLong(LDB_ERR_ALIAS_DEREFERENCING_PROBLEM));
+ PyModule_AddObject(m, "ERR_INAPPROPRIATE_AUTHENTICATION", PyInt_FromLong(LDB_ERR_INAPPROPRIATE_AUTHENTICATION));
+ PyModule_AddObject(m, "ERR_INVALID_CREDENTIALS", PyInt_FromLong(LDB_ERR_INVALID_CREDENTIALS));
+ PyModule_AddObject(m, "ERR_INSUFFICIENT_ACCESS_RIGHTS", PyInt_FromLong(LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS));
+ PyModule_AddObject(m, "ERR_BUSY", PyInt_FromLong(LDB_ERR_BUSY));
+ PyModule_AddObject(m, "ERR_UNAVAILABLE", PyInt_FromLong(LDB_ERR_UNAVAILABLE));
+ PyModule_AddObject(m, "ERR_UNWILLING_TO_PERFORM", PyInt_FromLong(LDB_ERR_UNWILLING_TO_PERFORM));
+ PyModule_AddObject(m, "ERR_LOOP_DETECT", PyInt_FromLong(LDB_ERR_LOOP_DETECT));
+ PyModule_AddObject(m, "ERR_NAMING_VIOLATION", PyInt_FromLong(LDB_ERR_NAMING_VIOLATION));
+ PyModule_AddObject(m, "ERR_OBJECT_CLASS_VIOLATION", PyInt_FromLong(LDB_ERR_OBJECT_CLASS_VIOLATION));
+ PyModule_AddObject(m, "ERR_NOT_ALLOWED_ON_NON_LEAF", PyInt_FromLong(LDB_ERR_NOT_ALLOWED_ON_NON_LEAF));
+ PyModule_AddObject(m, "ERR_NOT_ALLOWED_ON_RDN", PyInt_FromLong(LDB_ERR_NOT_ALLOWED_ON_RDN));
+ PyModule_AddObject(m, "ERR_ENTRY_ALREADY_EXISTS", PyInt_FromLong(LDB_ERR_ENTRY_ALREADY_EXISTS));
+ PyModule_AddObject(m, "ERR_OBJECT_CLASS_MODS_PROHIBITED", PyInt_FromLong(LDB_ERR_OBJECT_CLASS_MODS_PROHIBITED));
+ PyModule_AddObject(m, "ERR_AFFECTS_MULTIPLE_DSAS", PyInt_FromLong(LDB_ERR_AFFECTS_MULTIPLE_DSAS));
+
+ PyModule_AddObject(m, "ERR_OTHER", PyInt_FromLong(LDB_ERR_OTHER));
+
+ PyModule_AddObject(m, "__docformat__", PyString_FromString("restructuredText"));
+
+ PyExc_LdbError = PyErr_NewException(discard_const_p(char, "_ldb.LdbError"), NULL, NULL);
+ PyModule_AddObject(m, "LdbError", PyExc_LdbError);
+
+ Py_INCREF(&PyLdb);
+ Py_INCREF(&PyLdbDn);
+ Py_INCREF(&PyLdbModule);
+ Py_INCREF(&PyLdbMessage);
+ Py_INCREF(&PyLdbMessageElement);
+ Py_INCREF(&PyLdbTree);
+
+ PyModule_AddObject(m, "Ldb", (PyObject *)&PyLdb);
+ PyModule_AddObject(m, "Dn", (PyObject *)&PyLdbDn);
+ PyModule_AddObject(m, "Message", (PyObject *)&PyLdbMessage);
+ PyModule_AddObject(m, "MessageElement", (PyObject *)&PyLdbMessageElement);
+ PyModule_AddObject(m, "Module", (PyObject *)&PyLdbModule);
+ PyModule_AddObject(m, "Tree", (PyObject *)&PyLdbTree);
+}
diff --git a/source4/lib/ldb/pyldb.h b/source4/lib/ldb/pyldb.h
new file mode 100644
index 0000000000..731911a387
--- /dev/null
+++ b/source4/lib/ldb/pyldb.h
@@ -0,0 +1,101 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Swig interface to ldb.
+
+ Copyright (C) 2007-2008 Jelmer Vernooij <jelmer@samba.org>
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _PYLDB_H_
+#define _PYLDB_H_
+
+#include <Python.h>
+
+typedef struct {
+ PyObject_HEAD
+ struct ldb_context *ldb_ctx;
+ TALLOC_CTX *mem_ctx;
+} PyLdbObject;
+
+PyAPI_DATA(PyTypeObject) PyLdb;
+PyObject *PyLdb_FromLdbContext(struct ldb_context *ldb_ctx);
+#define PyLdb_AsLdbContext(pyobj) ((PyLdbObject *)pyobj)->ldb_ctx
+#define PyLdb_Check(ob) PyObject_TypeCheck(ob, &PyLdb)
+
+typedef struct {
+ PyObject_HEAD
+ struct ldb_dn *dn;
+ TALLOC_CTX *mem_ctx;
+} PyLdbDnObject;
+
+PyAPI_DATA(PyTypeObject) PyLdbDn;
+PyObject *PyLdbDn_FromDn(struct ldb_dn *);
+bool PyObject_AsDn(TALLOC_CTX *mem_ctx, PyObject *object, struct ldb_context *ldb_ctx, struct ldb_dn **dn);
+#define PyLdbDn_AsDn(pyobj) ((PyLdbDnObject *)pyobj)->dn
+#define PyLdbDn_Check(ob) PyObject_TypeCheck(ob, &PyLdbDn)
+
+typedef struct {
+ PyObject_HEAD
+ struct ldb_message *msg;
+ TALLOC_CTX *mem_ctx;
+} PyLdbMessageObject;
+PyAPI_DATA(PyTypeObject) PyLdbMessage;
+PyObject *PyLdbMessage_FromMessage(struct ldb_message *message);
+#define PyLdbMessage_Check(ob) PyObject_TypeCheck(ob, &PyLdbMessage)
+#define PyLdbMessage_AsMessage(pyobj) ((PyLdbMessageObject *)pyobj)->msg
+
+typedef struct {
+ PyObject_HEAD
+ struct ldb_module *mod;
+ TALLOC_CTX *mem_ctx;
+} PyLdbModuleObject;
+PyAPI_DATA(PyTypeObject) PyLdbModule;
+PyObject *PyLdbModule_FromModule(struct ldb_module *mod);
+#define PyLdbModule_AsModule(pyobj) ((PyLdbModuleObject *)pyobj)->mod
+
+typedef struct {
+ PyObject_HEAD
+ struct ldb_message_element *el;
+ TALLOC_CTX *mem_ctx;
+} PyLdbMessageElementObject;
+PyAPI_DATA(PyTypeObject) PyLdbMessageElement;
+struct ldb_message_element *PyObject_AsMessageElement(TALLOC_CTX *mem_ctx, PyObject *obj, int flags, const char *name);
+PyObject *PyLdbMessageElement_FromMessageElement(struct ldb_message_element *, TALLOC_CTX *mem_ctx);
+#define PyLdbMessageElement_AsMessageElement(pyobj) ((PyLdbMessageElementObject *)pyobj)->el
+#define PyLdbMessageElement_Check(ob) PyObject_TypeCheck(ob, &PyLdbMessageElement)
+
+typedef struct {
+ PyObject_HEAD
+ struct ldb_parse_tree *tree;
+ TALLOC_CTX *mem_ctx;
+} PyLdbTreeObject;
+PyAPI_DATA(PyTypeObject) PyLdbTree;
+PyObject *PyLdbTree_FromTree(struct ldb_parse_tree *);
+#define PyLdbTree_AsTree(pyobj) ((PyLdbTreeObject *)pyobj)->tree
+
+void PyErr_SetLdbError(int ret, struct ldb_context *ldb_ctx);
+#define PyErr_LDB_ERROR_IS_ERR_RAISE(ret,ldb) \
+ if (ret != LDB_SUCCESS) { \
+ PyErr_SetLdbError(ret, ldb); \
+ return NULL; \
+ }
+
+
+#endif /* _PYLDB_H_ */
diff --git a/source4/lib/ldb/python.mk b/source4/lib/ldb/python.mk
new file mode 100644
index 0000000000..6cc6d2e90e
--- /dev/null
+++ b/source4/lib/ldb/python.mk
@@ -0,0 +1,6 @@
+[PYTHON::pyldb]
+LIBRARY_REALNAME = ldb.$(SHLIBEXT)
+PUBLIC_DEPENDENCIES = LIBLDB PYTALLOC
+
+pyldb_OBJ_FILES = $(ldbsrcdir)/pyldb.o
+$(pyldb_OBJ_FILES): CFLAGS+=-I$(ldbsrcdir)/include
diff --git a/source4/lib/ldb/rules.mk b/source4/lib/ldb/rules.mk
new file mode 100644
index 0000000000..639271b76d
--- /dev/null
+++ b/source4/lib/ldb/rules.mk
@@ -0,0 +1,25 @@
+etags:
+ etags `find $(srcdir) -name "*.[ch]"`
+
+ctags:
+ ctags `find $(srcdir) -name "*.[ch]"`
+
+.SUFFIXES: .1 .1.xml .3 .3.xml .xml .html .c .o
+
+.c.o:
+ @echo Compiling $*.c
+ @mkdir -p `dirname $@`
+ @$(CC) $(CFLAGS) $(PICFLAG) -c $< -o $@
+
+.c.po:
+ @echo Compiling $*.c
+ @mkdir -p `dirname $@`
+ @$(CC) -fPIC $(CFLAGS) -c $< -o $@
+
+showflags::
+ @echo 'ldb will be compiled with flags:'
+ @echo ' CFLAGS = $(CFLAGS)'
+ @echo ' LIBS = $(LIBS)'
+
+distclean::
+ rm -f *~ */*~
diff --git a/source4/lib/ldb/sqlite3.m4 b/source4/lib/ldb/sqlite3.m4
new file mode 100644
index 0000000000..d0a74ee53c
--- /dev/null
+++ b/source4/lib/ldb/sqlite3.m4
@@ -0,0 +1,62 @@
+########################################################
+# Compile with SQLITE3 support?
+
+SQLITE3_LIBS=""
+with_sqlite3_support=no
+AC_MSG_CHECKING([for SQLITE3 support])
+
+AC_ARG_WITH(sqlite3,
+AS_HELP_STRING([--with-sqlite3],[SQLITE3 backend support (default=no)]),
+[ case "$withval" in
+ yes|no|auto)
+ with_sqlite3_support=$withval
+ ;;
+ esac ])
+
+AC_MSG_RESULT($with_sqlite3_support)
+
+if test x"$with_sqlite3_support" != x"no"; then
+ ##################################################################
+ # first test for sqlite3.h
+ AC_CHECK_HEADERS(sqlite3.h)
+
+ if test x"$ac_cv_header_sqlite3_h" != x"yes"; then
+ if test x"$with_sqlite3_support" = x"yes"; then
+ AC_MSG_ERROR(sqlite3.h is needed for SQLITE3 support)
+ else
+ AC_MSG_WARN(sqlite3.h is needed for SQLITE3 support)
+ fi
+
+ with_sqlite3_support=no
+ fi
+fi
+
+if test x"$with_sqlite3_support" != x"no"; then
+ ac_save_LIBS=$LIBS
+
+ ########################################################
+ # now see if we can find the sqlite3 libs in standard paths
+ AC_CHECK_LIB_EXT(sqlite3, SQLITE3_LIBS, sqlite3_open)
+
+ if test x"$ac_cv_lib_ext_sqlite3_sqlite3_open" = x"yes"; then
+ AC_DEFINE(HAVE_SQLITE3,1,[Whether sqlite3 is available])
+ AC_DEFINE(HAVE_LDB_SQLITE3,1,[Whether ldb_sqlite3 is available])
+ AC_MSG_CHECKING(whether SQLITE3 support is used)
+ AC_MSG_RESULT(yes)
+ with_sqlite3_support=yes
+ SMB_ENABLE(SQLITE3,YES)
+ else
+ if test x"$with_sqlite3_support" = x"yes"; then
+ AC_MSG_ERROR(libsqlite3 is needed for SQLITE3 support)
+ else
+ AC_MSG_WARN(libsqlite3 is needed for SQLITE3 support)
+ fi
+
+ SQLITE3_LIBS=""
+ with_sqlite3_support=no
+ fi
+
+ LIBS=$ac_save_LIBS;
+fi
+
+SMB_EXT_LIB(SQLITE3,[${SQLITE3_LIBS}],[${SQLITE3_CFLAGS}],[${SQLITE3_CPPFLAGS}],[${SQLITE3_LDFLAGS}])
diff --git a/source4/lib/ldb/standalone.sh b/source4/lib/ldb/standalone.sh
new file mode 100755
index 0000000000..8ab081e0f3
--- /dev/null
+++ b/source4/lib/ldb/standalone.sh
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+cd ../replace
+make clean
+
+cd ../talloc
+make clean
+
+cd ../tdb
+make clean
+
+cd ../events
+make clean
+
+cd ../ldb
+make clean
+
+./autogen.sh
+
+rm -fr build
+mkdir build
+cd build
+
+../configure $*
+make dirs
+make all
+
+cd ..
diff --git a/source4/lib/ldb/tests/init.ldif b/source4/lib/ldb/tests/init.ldif
new file mode 100644
index 0000000000..2e0b83c769
--- /dev/null
+++ b/source4/lib/ldb/tests/init.ldif
@@ -0,0 +1,40 @@
+dn: o=University of Michigan,c=TEST
+objectclass: organization
+objectclass: domainRelatedObject
+l: Ann Arbor, Michigan
+st: Michigan
+o: University of Michigan
+o: UMICH
+o: UM
+o: U-M
+o: U of M
+description: The University of Michigan at Ann Arbor
+seeAlso:
+postaladdress: University of Michigan $ 535 W. William St. $ Ann Arbor, MI 481
+ 09 $ US
+telephonenumber: +1 313 764-1817
+associateddomain: example.com
+
+dn: ou=People,o=University of Michigan,c=TEST
+objectclass: organizationalUnit
+objectclass: extensibleObject
+ou: People
+uidNumber: 0
+gidNumber: 0
+
+dn: ou=Ldb Test,ou=People,o=University of Michigan,c=TEST
+objectclass: organizationalUnit
+objectclass: extensibleObject
+ou: People
+ou: Ldb Test
+uidNumber: 0
+gidNumber: 0
+
+dn: ou=LdbTspace,ou=People,o=University of Michigan,c=TEST
+objectclass: organizationalUnit
+objectclass: extensibleObject
+ou: People
+ou: LdbTspace
+description: test white space removal in comparisons
+uidNumber: 0
+gidNumber: 0
diff --git a/source4/lib/ldb/tests/init_slapd.sh b/source4/lib/ldb/tests/init_slapd.sh
new file mode 100755
index 0000000000..cf06acd08b
--- /dev/null
+++ b/source4/lib/ldb/tests/init_slapd.sh
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+if [ -z "$LDBDIR" ]; then
+ LDBDIR=`dirname $0`/..
+ export LDBDIR
+fi
+
+rm -rf tests/tmp/db
+mkdir -p tests/tmp/db
+
+if [ -f tests/tmp/slapd.pid ]; then
+ kill `cat tests/tmp/slapd.pid`
+ sleep 1
+fi
+if [ -f tests/tmp/slapd.pid ]; then
+ kill -9 `cat tests/tmp/slapd.pid`
+ rm -f tests/tmp/slapd.pid
+fi
+
+# we don't consider a slapadd failure as a test suite failure, as it
+# has nothing to do with ldb
+
+MODCONF=tests/tmp/modules.conf
+rm -f $MODCONF
+touch $MODCONF || exit 1
+
+slaptest -u -f $LDBDIR/tests/slapd.conf > /dev/null 2>&1 || {
+ echo "enabling sladp modules"
+cat > $MODCONF <<EOF
+modulepath /usr/lib/ldap
+moduleload back_bdb
+EOF
+}
+
+slaptest -u -f $LDBDIR/tests/slapd.conf || {
+ echo "slaptest failed - skipping ldap tests"
+ exit 0
+}
+
+slapadd -f $LDBDIR/tests/slapd.conf < $LDBDIR/tests/init.ldif || exit 0
+
diff --git a/source4/lib/ldb/tests/kill_slapd.sh b/source4/lib/ldb/tests/kill_slapd.sh
new file mode 100755
index 0000000000..91beb10814
--- /dev/null
+++ b/source4/lib/ldb/tests/kill_slapd.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+if [ -z "$LDBDIR" ]; then
+ LDBDIR=`dirname $0`/..
+ export LDBDIR
+fi
+
+if [ -f tests/tmp/slapd.pid ]; then
+ echo "killing slapd process `cat tests/tmp/slapd.pid`"
+ kill -9 `cat tests/tmp/slapd.pid`
+ rm -f tests/tmp/slapd.pid
+fi
diff --git a/source4/lib/ldb/tests/ldapi_url.sh b/source4/lib/ldb/tests/ldapi_url.sh
new file mode 100755
index 0000000000..fef6c35f2b
--- /dev/null
+++ b/source4/lib/ldb/tests/ldapi_url.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+# aargh, did LDAP ever have to expose this crap to users ...
+
+BASE=`pwd`
+
+TMPDIR=$BASE/tests/tmp
+
+LDAPI_ESCAPE=`echo $TMPDIR/ldapi | sed 's|/|%2F|g'`
+
+echo "ldapi://$LDAPI_ESCAPE"
diff --git a/source4/lib/ldb/tests/photo.ldif b/source4/lib/ldb/tests/photo.ldif
new file mode 100644
index 0000000000..28981b1f24
--- /dev/null
+++ b/source4/lib/ldb/tests/photo.ldif
@@ -0,0 +1,5 @@
+dn: cn=Hampster Ursula,ou=Alumni Association,ou=People,o=University of Michigan,c=TEST
+changetype: modify
+add: jpegPhoto
+jpegPhoto:< file://tests/tmp/samba4.png
+
diff --git a/source4/lib/ldb/tests/python/api.py b/source4/lib/ldb/tests/python/api.py
new file mode 100755
index 0000000000..c372b8fa71
--- /dev/null
+++ b/source4/lib/ldb/tests/python/api.py
@@ -0,0 +1,497 @@
+#!/usr/bin/python
+# Simple tests for the ldb python bindings.
+# Copyright (C) 2007 Jelmer Vernooij <jelmer@samba.org>
+
+import os, sys
+import unittest
+
+# Required for the standalone LDB build
+sys.path.append("build/lib.linux-i686-2.4")
+
+import ldb
+
+def filename():
+ return os.tempnam()
+
+class NoContextTests(unittest.TestCase):
+ def test_valid_attr_name(self):
+ self.assertTrue(ldb.valid_attr_name("foo"))
+ self.assertFalse(ldb.valid_attr_name("24foo"))
+
+ def test_timestring(self):
+ self.assertEquals("19700101000000.0Z", ldb.timestring(0))
+ self.assertEquals("20071119191012.0Z", ldb.timestring(1195499412))
+
+ def test_string_to_time(self):
+ self.assertEquals(0, ldb.string_to_time("19700101000000.0Z"))
+ self.assertEquals(1195499412, ldb.string_to_time("20071119191012.0Z"))
+
+
+class SimpleLdb(unittest.TestCase):
+ def test_connect(self):
+ ldb.Ldb(filename())
+
+ def test_connect_none(self):
+ ldb.Ldb()
+
+ def test_connect_later(self):
+ x = ldb.Ldb()
+ x.connect(filename())
+
+ def test_repr(self):
+ x = ldb.Ldb()
+ self.assertTrue(repr(x).startswith("<ldb connection"))
+
+ def test_set_create_perms(self):
+ x = ldb.Ldb()
+ x.set_create_perms(0600)
+
+ def test_set_modules_dir(self):
+ x = ldb.Ldb()
+ x.set_modules_dir("/tmp")
+
+ def test_modules_none(self):
+ x = ldb.Ldb()
+ self.assertEquals([], x.modules())
+
+ def test_modules_tdb(self):
+ x = ldb.Ldb("bar.ldb")
+ self.assertEquals("[<ldb module 'tdb'>]", repr(x.modules()))
+
+ def test_search(self):
+ l = ldb.Ldb(filename())
+ self.assertEquals(len(l.search()), 1)
+
+ def test_search_controls(self):
+ l = ldb.Ldb(filename())
+ self.assertEquals(len(l.search(controls=["paged_results:0:5"])), 1)
+
+ def test_search_attrs(self):
+ l = ldb.Ldb(filename())
+ self.assertEquals(len(l.search(ldb.Dn(l, ""), ldb.SCOPE_SUBTREE, "(dc=*)", ["dc"])), 0)
+
+ def test_search_string_dn(self):
+ l = ldb.Ldb(filename())
+ self.assertEquals(len(l.search("", ldb.SCOPE_SUBTREE, "(dc=*)", ["dc"])), 0)
+
+ def test_search_attr_string(self):
+ l = ldb.Ldb("foo.tdb")
+ self.assertRaises(TypeError, l.search, attrs="dc")
+
+ def test_opaque(self):
+ l = ldb.Ldb(filename())
+ l.set_opaque("my_opaque", l)
+ self.assertTrue(l.get_opaque("my_opaque") is not None)
+ self.assertEquals(None, l.get_opaque("unknown"))
+
+ def test_search_scope_base(self):
+ l = ldb.Ldb(filename())
+ self.assertEquals(len(l.search(ldb.Dn(l, "dc=foo"),
+ ldb.SCOPE_ONELEVEL)), 0)
+
+ def test_delete(self):
+ l = ldb.Ldb(filename())
+ self.assertRaises(ldb.LdbError, lambda: l.delete(ldb.Dn(l, "dc=foo")))
+
+ def test_contains(self):
+ l = ldb.Ldb(filename())
+ self.assertFalse(ldb.Dn(l, "dc=foo") in l)
+ l = ldb.Ldb(filename())
+ m = ldb.Message()
+ m.dn = ldb.Dn(l, "dc=foo")
+ m["b"] = ["a"]
+ l.add(m)
+ try:
+ self.assertTrue(ldb.Dn(l, "dc=foo") in l)
+ finally:
+ l.delete(m.dn)
+
+ def test_get_config_basedn(self):
+ l = ldb.Ldb(filename())
+ self.assertEquals(None, l.get_config_basedn())
+
+ def test_get_root_basedn(self):
+ l = ldb.Ldb(filename())
+ self.assertEquals(None, l.get_root_basedn())
+
+ def test_get_schema_basedn(self):
+ l = ldb.Ldb(filename())
+ self.assertEquals(None, l.get_schema_basedn())
+
+ def test_get_default_basedn(self):
+ l = ldb.Ldb(filename())
+ self.assertEquals(None, l.get_default_basedn())
+
+ def test_add(self):
+ l = ldb.Ldb(filename())
+ m = ldb.Message()
+ m.dn = ldb.Dn(l, "dc=foo")
+ m["bla"] = "bla"
+ self.assertEquals(len(l.search()), 1)
+ l.add(m)
+ try:
+ self.assertEquals(len(l.search()), 2)
+ finally:
+ l.delete(ldb.Dn(l, "dc=foo"))
+
+ def test_add_dict(self):
+ l = ldb.Ldb(filename())
+ m = {"dn": ldb.Dn(l, "dc=foo"),
+ "bla": "bla"}
+ self.assertEquals(len(l.search()), 1)
+ l.add(m)
+ try:
+ self.assertEquals(len(l.search()), 2)
+ finally:
+ l.delete(ldb.Dn(l, "dc=foo"))
+
+ def test_add_dict_string_dn(self):
+ l = ldb.Ldb(filename())
+ m = {"dn": "dc=foo", "bla": "bla"}
+ self.assertEquals(len(l.search()), 1)
+ l.add(m)
+ try:
+ self.assertEquals(len(l.search()), 2)
+ finally:
+ l.delete(ldb.Dn(l, "dc=foo"))
+
+ def test_rename(self):
+ l = ldb.Ldb(filename())
+ m = ldb.Message()
+ m.dn = ldb.Dn(l, "dc=foo")
+ m["bla"] = "bla"
+ self.assertEquals(len(l.search()), 1)
+ l.add(m)
+ try:
+ l.rename(ldb.Dn(l, "dc=foo"), ldb.Dn(l, "dc=bar"))
+ self.assertEquals(len(l.search()), 2)
+ finally:
+ l.delete(ldb.Dn(l, "dc=bar"))
+
+ def test_rename_string_dns(self):
+ l = ldb.Ldb(filename())
+ m = ldb.Message()
+ m.dn = ldb.Dn(l, "dc=foo")
+ m["bla"] = "bla"
+ self.assertEquals(len(l.search()), 1)
+ l.add(m)
+ self.assertEquals(len(l.search()), 2)
+ try:
+ l.rename("dc=foo", "dc=bar")
+ self.assertEquals(len(l.search()), 2)
+ finally:
+ l.delete(ldb.Dn(l, "dc=bar"))
+
+ def test_modify_delete(self):
+ l = ldb.Ldb(filename())
+ m = ldb.Message()
+ m.dn = ldb.Dn(l, "dc=modifydelete")
+ m["bla"] = ["1234"]
+ l.add(m)
+ rm = l.search(m.dn)[0]
+ self.assertEquals(["1234"], list(rm["bla"]))
+ try:
+ m = ldb.Message()
+ m.dn = ldb.Dn(l, "dc=modifydelete")
+ m["bla"] = ldb.MessageElement([], ldb.CHANGETYPE_DELETE, "bla")
+ l.modify(m)
+ rm = l.search(m.dn)[0]
+ self.assertEquals(1, len(rm))
+ finally:
+ l.delete(ldb.Dn(l, "dc=modifydelete"))
+
+ def test_modify_add(self):
+ l = ldb.Ldb(filename())
+ m = ldb.Message()
+ m.dn = ldb.Dn(l, "dc=add")
+ m["bla"] = ["1234"]
+ l.add(m)
+ try:
+ m = ldb.Message()
+ m.dn = ldb.Dn(l, "dc=add")
+ m["bla"] = ldb.MessageElement(["456"], ldb.CHANGETYPE_ADD, "bla")
+ l.modify(m)
+ rm = l.search(m.dn)[0]
+ self.assertEquals(2, len(rm))
+ self.assertEquals(["1234", "456"], list(rm["bla"]))
+ finally:
+ l.delete(ldb.Dn(l, "dc=add"))
+
+ def test_modify_modify(self):
+ l = ldb.Ldb(filename())
+ m = ldb.Message()
+ m.dn = ldb.Dn(l, "dc=modify2")
+ m["bla"] = ["1234", "456"]
+ l.add(m)
+ try:
+ m = ldb.Message()
+ m.dn = ldb.Dn(l, "dc=modify2")
+ m["bla"] = ldb.MessageElement(["456"], ldb.CHANGETYPE_MODIFY, "bla")
+ l.modify(m)
+ rm = l.search(m.dn)[0]
+ self.assertEquals(2, len(rm))
+ self.assertEquals(["1234"], list(rm["bla"]))
+ finally:
+ l.delete(ldb.Dn(l, "dc=modify2"))
+
+ def test_transaction_commit(self):
+ l = ldb.Ldb(filename())
+ l.transaction_start()
+ m = ldb.Message(ldb.Dn(l, "dc=foo"))
+ m["foo"] = ["bar"]
+ l.add(m)
+ l.transaction_commit()
+ l.delete(m.dn)
+
+ def test_transaction_cancel(self):
+ l = ldb.Ldb(filename())
+ l.transaction_start()
+ m = ldb.Message(ldb.Dn(l, "dc=foo"))
+ m["foo"] = ["bar"]
+ l.add(m)
+ l.transaction_cancel()
+ self.assertEquals(0, len(l.search(ldb.Dn(l, "dc=foo"))))
+
+ def test_set_debug(self):
+ def my_report_fn(level, text):
+ pass
+ l = ldb.Ldb(filename())
+ l.set_debug(my_report_fn)
+
+
+class DnTests(unittest.TestCase):
+ def setUp(self):
+ self.ldb = ldb.Ldb(filename())
+
+ def test_eq(self):
+ x = ldb.Dn(self.ldb, "dc=foo,bar=bloe")
+ y = ldb.Dn(self.ldb, "dc=foo,bar=bloe")
+ self.assertEquals(x, y)
+
+ def test_str(self):
+ x = ldb.Dn(self.ldb, "dc=foo,bar=bloe")
+ self.assertEquals(x.__str__(), "dc=foo,bar=bloe")
+
+ def test_repr(self):
+ x = ldb.Dn(self.ldb, "dc=foo,bla=blie")
+ self.assertEquals(x.__repr__(), "Dn('dc=foo,bla=blie')")
+
+ def test_get_casefold(self):
+ x = ldb.Dn(self.ldb, "dc=foo,bar=bloe")
+ self.assertEquals(x.get_casefold(), "DC=FOO,BAR=bloe")
+
+ def test_validate(self):
+ x = ldb.Dn(self.ldb, "dc=foo,bar=bloe")
+ self.assertTrue(x.validate())
+
+ def test_parent(self):
+ x = ldb.Dn(self.ldb, "dc=foo,bar=bloe")
+ self.assertEquals("bar=bloe", x.parent().__str__())
+
+ def test_compare(self):
+ x = ldb.Dn(self.ldb, "dc=foo,bar=bloe")
+ y = ldb.Dn(self.ldb, "dc=foo,bar=bloe")
+ self.assertEquals(x, y)
+ z = ldb.Dn(self.ldb, "dc=foo,bar=blie")
+ self.assertNotEquals(z, y)
+
+ def test_is_valid(self):
+ x = ldb.Dn(self.ldb, "dc=foo,dc=bloe")
+ self.assertTrue(x.is_valid())
+ x = ldb.Dn(self.ldb, "")
+ # is_valid()'s return values appears to be a side effect of
+ # some other ldb functions. yuck.
+ # self.assertFalse(x.is_valid())
+
+ def test_is_special(self):
+ x = ldb.Dn(self.ldb, "dc=foo,bar=bloe")
+ self.assertFalse(x.is_special())
+ x = ldb.Dn(self.ldb, "@FOOBAR")
+ self.assertTrue(x.is_special())
+
+ def test_check_special(self):
+ x = ldb.Dn(self.ldb, "dc=foo,bar=bloe")
+ self.assertFalse(x.check_special("FOOBAR"))
+ x = ldb.Dn(self.ldb, "@FOOBAR")
+ self.assertTrue(x.check_special("@FOOBAR"))
+
+ def test_len(self):
+ x = ldb.Dn(self.ldb, "dc=foo,bar=bloe")
+ self.assertEquals(2, len(x))
+ x = ldb.Dn(self.ldb, "dc=foo")
+ self.assertEquals(1, len(x))
+
+ def test_add_child(self):
+ x = ldb.Dn(self.ldb, "dc=foo,bar=bloe")
+ self.assertTrue(x.add_child(ldb.Dn(self.ldb, "bla=bloe")))
+ self.assertEquals("bla=bloe,dc=foo,bar=bloe", x.__str__())
+
+ def test_add_base(self):
+ x = ldb.Dn(self.ldb, "dc=foo,bar=bloe")
+ base = ldb.Dn(self.ldb, "bla=bloe")
+ self.assertTrue(x.add_base(base))
+ self.assertEquals("dc=foo,bar=bloe,bla=bloe", x.__str__())
+
+ def test_add(self):
+ x = ldb.Dn(self.ldb, "dc=foo")
+ y = ldb.Dn(self.ldb, "bar=bla")
+ self.assertEquals("dc=foo,bar=bla", str(y + x))
+
+ def test_parse_ldif(self):
+ msgs = self.ldb.parse_ldif("dn: foo=bar\n")
+ msg = msgs.next()
+ self.assertEquals("foo=bar", str(msg[1].dn))
+ self.assertTrue(isinstance(msg[1], ldb.Message))
+
+ def test_parse_ldif_more(self):
+ msgs = self.ldb.parse_ldif("dn: foo=bar\n\n\ndn: bar=bar")
+ msg = msgs.next()
+ self.assertEquals("foo=bar", str(msg[1].dn))
+ msg = msgs.next()
+ self.assertEquals("bar=bar", str(msg[1].dn))
+
+ def test_canonical_string(self):
+ x = ldb.Dn(self.ldb, "dc=foo,bar=bloe")
+ self.assertEquals("/bloe/foo", x.canonical_str())
+
+ def test_canonical_ex_string(self):
+ x = ldb.Dn(self.ldb, "dc=foo,bar=bloe")
+ self.assertEquals("/bloe\nfoo", x.canonical_ex_str())
+
+
+class LdbMsgTests(unittest.TestCase):
+ def setUp(self):
+ self.msg = ldb.Message()
+
+ def test_init_dn(self):
+ self.msg = ldb.Message(ldb.Dn(ldb.Ldb(), "dc=foo"))
+ self.assertEquals("dc=foo", str(self.msg.dn))
+
+ def test_iter_items(self):
+ self.assertEquals(0, len(self.msg.items()))
+ self.msg.dn = ldb.Dn(ldb.Ldb("foo.tdb"), "dc=foo")
+ self.assertEquals(1, len(self.msg.items()))
+
+ def test_repr(self):
+ self.msg.dn = ldb.Dn(ldb.Ldb("foo.tdb"), "dc=foo")
+ self.msg["dc"] = "foo"
+ self.assertEquals("Message({'dn': Dn('dc=foo'), 'dc': MessageElement(['foo'])})", repr(self.msg))
+
+ def test_len(self):
+ self.assertEquals(0, len(self.msg))
+
+ def test_notpresent(self):
+ self.assertRaises(KeyError, lambda: self.msg["foo"])
+
+ def test_del(self):
+ del self.msg["foo"]
+
+ def test_add_value(self):
+ self.assertEquals(0, len(self.msg))
+ self.msg["foo"] = ["foo"]
+ self.assertEquals(1, len(self.msg))
+
+ def test_add_value_multiple(self):
+ self.assertEquals(0, len(self.msg))
+ self.msg["foo"] = ["foo", "bla"]
+ self.assertEquals(1, len(self.msg))
+ self.assertEquals(["foo", "bla"], list(self.msg["foo"]))
+
+ def test_set_value(self):
+ self.msg["foo"] = ["fool"]
+ self.assertEquals(["fool"], list(self.msg["foo"]))
+ self.msg["foo"] = ["bar"]
+ self.assertEquals(["bar"], list(self.msg["foo"]))
+
+ def test_keys(self):
+ self.msg.dn = ldb.Dn(ldb.Ldb("foo.tdb"), "@BASEINFO")
+ self.msg["foo"] = ["bla"]
+ self.msg["bar"] = ["bla"]
+ self.assertEquals(["dn", "foo", "bar"], self.msg.keys())
+
+ def test_dn(self):
+ self.msg.dn = ldb.Dn(ldb.Ldb(filename()), "@BASEINFO")
+ self.assertEquals("@BASEINFO", self.msg.dn.__str__())
+
+ def test_get_dn(self):
+ self.msg.dn = ldb.Dn(ldb.Ldb("foo.tdb"), "@BASEINFO")
+ self.assertEquals("@BASEINFO", self.msg.get("dn").__str__())
+
+ def test_get_other(self):
+ self.msg["foo"] = ["bar"]
+ self.assertEquals("bar", self.msg.get("foo")[0])
+
+ def test_get_unknown(self):
+ self.assertEquals(None, self.msg.get("lalalala"))
+
+
+class MessageElementTests(unittest.TestCase):
+ def test_cmp_element(self):
+ x = ldb.MessageElement(["foo"])
+ y = ldb.MessageElement(["foo"])
+ z = ldb.MessageElement(["bzr"])
+ self.assertEquals(x, y)
+ self.assertNotEquals(x, z)
+
+ def test_create_iterable(self):
+ x = ldb.MessageElement(["foo"])
+ self.assertEquals(["foo"], list(x))
+
+ def test_repr(self):
+ x = ldb.MessageElement(["foo"])
+ self.assertEquals("MessageElement(['foo'])", repr(x))
+ x = ldb.MessageElement(["foo", "bla"])
+ self.assertEquals(2, len(x))
+ self.assertEquals("MessageElement(['foo','bla'])", repr(x))
+
+ def test_get_item(self):
+ x = ldb.MessageElement(["foo", "bar"])
+ self.assertEquals("foo", x[0])
+ self.assertEquals("bar", x[1])
+ self.assertEquals("bar", x[-1])
+ self.assertRaises(IndexError, lambda: x[45])
+
+ def test_len(self):
+ x = ldb.MessageElement(["foo", "bar"])
+ self.assertEquals(2, len(x))
+
+ def test_eq(self):
+ x = ldb.MessageElement(["foo", "bar"])
+ y = ldb.MessageElement(["foo", "bar"])
+ self.assertEquals(y, x)
+ x = ldb.MessageElement(["foo"])
+ self.assertNotEquals(y, x)
+ y = ldb.MessageElement(["foo"])
+ self.assertEquals(y, x)
+
+
+class ModuleTests(unittest.TestCase):
+ def test_register_module(self):
+ class ExampleModule:
+ name = "example"
+ ldb.register_module(ExampleModule)
+
+ def test_use_module(self):
+ ops = []
+ class ExampleModule:
+ name = "bla"
+
+ def __init__(self, ldb, next):
+ ops.append("init")
+ self.next = next
+
+ def search(self, *args, **kwargs):
+ return self.next.search(*args, **kwargs)
+
+ ldb.register_module(ExampleModule)
+ if os.path.exists("usemodule.ldb"):
+ os.unlink("usemodule.ldb")
+ l = ldb.Ldb("usemodule.ldb")
+ l.add({"dn": "@MODULES", "@LIST": "bla"})
+ self.assertEquals([], ops)
+ l = ldb.Ldb("usemodule.ldb")
+ self.assertEquals(["init"], ops)
+
+if __name__ == '__main__':
+ import unittest
+ unittest.TestProgram()
diff --git a/source4/lib/ldb/tests/python/ldap.py b/source4/lib/ldb/tests/python/ldap.py
new file mode 100755
index 0000000000..a30273fc66
--- /dev/null
+++ b/source4/lib/ldb/tests/python/ldap.py
@@ -0,0 +1,1058 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# This is a port of the original in testprogs/ejs/ldap.js
+
+import getopt
+import optparse
+import sys
+import time
+
+sys.path.append("bin/python")
+sys.path.append("../lib/subunit/python")
+
+import samba.getopt as options
+
+from samba.auth import system_session
+from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError
+from ldb import ERR_NO_SUCH_OBJECT, ERR_ATTRIBUTE_OR_VALUE_EXISTS
+from ldb import ERR_ENTRY_ALREADY_EXISTS, ERR_UNWILLING_TO_PERFORM
+from ldb import ERR_NOT_ALLOWED_ON_NON_LEAF, ERR_OTHER, ERR_INVALID_DN_SYNTAX
+from samba import Ldb
+from subunit import SubunitTestRunner
+from samba import param
+import unittest
+
+parser = optparse.OptionParser("ldap [options] <host>")
+sambaopts = options.SambaOptions(parser)
+parser.add_option_group(sambaopts)
+parser.add_option_group(options.VersionOptions(parser))
+# use command line creds if available
+credopts = options.CredentialsOptions(parser)
+parser.add_option_group(credopts)
+opts, args = parser.parse_args()
+
+if len(args) < 1:
+ parser.print_usage()
+ sys.exit(1)
+
+host = args[0]
+
+lp = sambaopts.get_loadparm()
+creds = credopts.get_credentials(lp)
+
+class BasicTests(unittest.TestCase):
+ def delete_force(self, ldb, dn):
+ try:
+ ldb.delete(dn)
+ except LdbError, (num, _):
+ self.assertEquals(num, ERR_NO_SUCH_OBJECT)
+
+ def find_basedn(self, ldb):
+ res = ldb.search(base="", expression="", scope=SCOPE_BASE,
+ attrs=["defaultNamingContext"])
+ self.assertEquals(len(res), 1)
+ return res[0]["defaultNamingContext"][0]
+
+ def find_configurationdn(self, ldb):
+ res = ldb.search(base="", expression="", scope=SCOPE_BASE, attrs=["configurationNamingContext"])
+ self.assertEquals(len(res), 1)
+ return res[0]["configurationNamingContext"][0]
+
+ def find_schemadn(self, ldb):
+ res = ldb.search(base="", expression="", scope=SCOPE_BASE, attrs=["schemaNamingContext"])
+ self.assertEquals(len(res), 1)
+ return res[0]["schemaNamingContext"][0]
+
+ def setUp(self):
+ self.ldb = ldb
+ self.gc_ldb = gc_ldb
+ self.base_dn = self.find_basedn(ldb)
+ self.configuration_dn = self.find_configurationdn(ldb)
+ self.schema_dn = self.find_schemadn(ldb)
+
+ print "baseDN: %s\n" % self.base_dn
+
+ self.delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
+ self.delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
+ self.delete_force(self.ldb, "cn=ldaptestuser2,cn=users," + self.base_dn)
+ self.delete_force(self.ldb, "cn=ldaptestgroup2,cn=users," + self.base_dn)
+ self.delete_force(self.ldb, "cn=ldaptestutf8user èùéìòà ,cn=users," + self.base_dn)
+ self.delete_force(self.ldb, "cn=ldaptestutf8user2 èùéìòà ,cn=users," + self.base_dn)
+
+ def test_group_add_invalid_member(self):
+ """Testing group add with invalid member"""
+ try:
+ self.ldb.add({
+ "dn": "cn=ldaptestgroup,cn=uSers," + self.base_dn,
+ "objectclass": "group",
+ "member": "cn=ldaptestuser,cn=useRs," + self.base_dn})
+ self.fail()
+ except LdbError, (num, _):
+ self.assertEquals(num, ERR_NO_SUCH_OBJECT)
+
+ def test_all(self):
+ """Basic tests"""
+
+ self.delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
+
+ print "Testing user add"
+ ldb.add({
+ "dn": "cn=ldaptestuser,cn=uSers," + self.base_dn,
+ "objectclass": ["user", "person"],
+ "cN": "LDAPtestUSER",
+ "givenname": "ldap",
+ "sn": "testy"})
+
+ ldb.add({
+ "dn": "cn=ldaptestgroup,cn=uSers," + self.base_dn,
+ "objectclass": "group",
+ "member": "cn=ldaptestuser,cn=useRs," + self.base_dn})
+
+ self.delete_force(ldb, "cn=ldaptestcomputer,cn=computers," + self.base_dn)
+ ldb.add({
+ "dn": "cn=ldaptestcomputer,cn=computers," + self.base_dn,
+ "objectclass": "computer",
+ "cN": "LDAPtestCOMPUTER"})
+
+ self.delete_force(self.ldb, "cn=ldaptest2computer,cn=computers," + self.base_dn)
+ ldb.add({"dn": "cn=ldaptest2computer,cn=computers," + self.base_dn,
+ "objectClass": "computer",
+ "cn": "LDAPtest2COMPUTER",
+ "userAccountControl": "4096",
+ "displayname": "ldap testy"})
+
+ self.delete_force(self.ldb, "cn=ldaptestcomputer3,cn=computers," + self.base_dn)
+ try:
+ ldb.add({"dn": "cn=ldaptestcomputer3,cn=computers," + self.base_dn,
+ "objectClass": "computer",
+ "cn": "LDAPtest2COMPUTER"
+ })
+ self.fail()
+ except LdbError, (num, _):
+ self.assertEquals(num, ERR_INVALID_DN_SYNTAX)
+
+ self.delete_force(self.ldb, "cn=ldaptestcomputer3,cn=computers," + self.base_dn)
+ try:
+ ldb.add({"dn": "cn=ldaptestcomputer3,cn=computers," + self.base_dn,
+ "objectClass": "computer",
+ "cn": "ldaptestcomputer3",
+ "sAMAccountType": "805306368"
+ })
+ self.fail()
+ except LdbError, (num, _):
+ self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
+
+ self.delete_force(self.ldb, "cn=ldaptestcomputer3,cn=computers," + self.base_dn)
+ try:
+ ldb.add({"dn": "cn=ldaptestcomputer3,cn=computers," + self.base_dn,
+ "objectClass": "computer",
+ "cn": "ldaptestcomputer3",
+ "userAccountControl": "0"
+ })
+ self.fail()
+ except LdbError, (num, _):
+ self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
+
+ self.delete_force(self.ldb, "cn=ldaptestuser7,cn=users," + self.base_dn)
+ try:
+ ldb.add({"dn": "cn=ldaptestuser7,cn=users," + self.base_dn,
+ "objectClass": "user",
+ "cn": "LDAPtestuser7",
+ "userAccountControl": "0"
+ })
+ self.fail()
+ except LdbError, (num, _):
+ self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
+
+ self.delete_force(self.ldb, "cn=ldaptestuser7,cn=users," + self.base_dn)
+
+ ldb.add({"dn": "cn=ldaptestuser7,cn=users," + self.base_dn,
+ "objectClass": "user",
+ "cn": "LDAPtestuser7",
+ "userAccountControl": "2"
+ })
+
+ self.delete_force(self.ldb, "cn=ldaptestuser7,cn=users," + self.base_dn)
+
+ self.delete_force(self.ldb, "cn=ldaptestcomputer3,cn=computers," + self.base_dn)
+ ldb.add({"dn": "cn=ldaptestcomputer3,cn=computers," + self.base_dn,
+ "objectClass": "computer",
+ "cn": "LDAPtestCOMPUTER3"
+ })
+
+ print "Testing ldb.search for (&(cn=ldaptestcomputer3)(objectClass=user))";
+ res = ldb.search(self.base_dn, expression="(&(cn=ldaptestcomputer3)(objectClass=user))");
+ self.assertEquals(len(res), 1, "Found only %d for (&(cn=ldaptestcomputer3)(objectClass=user))" % len(res))
+
+ self.assertEquals(str(res[0].dn), ("CN=ldaptestcomputer3,CN=Computers," + self.base_dn));
+ self.assertEquals(res[0]["cn"][0], "ldaptestcomputer3");
+ self.assertEquals(res[0]["name"][0], "ldaptestcomputer3");
+ self.assertEquals(res[0]["objectClass"][0], "top");
+ self.assertEquals(res[0]["objectClass"][1], "person");
+ self.assertEquals(res[0]["objectClass"][2], "organizationalPerson");
+ self.assertEquals(res[0]["objectClass"][3], "user");
+ self.assertEquals(res[0]["objectClass"][4], "computer");
+ self.assertTrue("objectGUID" in res[0])
+ self.assertTrue("whenCreated" in res[0])
+ self.assertEquals(res[0]["objectCategory"][0], ("CN=Computer,CN=Schema,CN=Configuration," + self.base_dn));
+ self.assertEquals(int(res[0]["primaryGroupID"][0]), 513);
+ self.assertEquals(int(res[0]["sAMAccountType"][0]), 805306368);
+ self.assertEquals(int(res[0]["userAccountControl"][0]), 546);
+
+ self.delete_force(self.ldb, "cn=ldaptestcomputer3,cn=computers," + self.base_dn)
+
+ print "Testing attribute or value exists behaviour"
+ try:
+ ldb.modify_ldif("""
+dn: cn=ldaptest2computer,cn=computers,""" + self.base_dn + """
+changetype: modify
+replace: servicePrincipalName
+servicePrincipalName: host/ldaptest2computer
+servicePrincipalName: host/ldaptest2computer
+servicePrincipalName: cifs/ldaptest2computer
+""")
+ self.fail()
+ except LdbError, (num, msg):
+ self.assertEquals(num, ERR_ATTRIBUTE_OR_VALUE_EXISTS)
+
+ ldb.modify_ldif("""
+dn: cn=ldaptest2computer,cn=computers,""" + self.base_dn + """
+changetype: modify
+replace: servicePrincipalName
+servicePrincipalName: host/ldaptest2computer
+servicePrincipalName: cifs/ldaptest2computer
+""")
+ try:
+ ldb.modify_ldif("""
+dn: cn=ldaptest2computer,cn=computers,""" + self.base_dn + """
+changetype: modify
+add: servicePrincipalName
+servicePrincipalName: host/ldaptest2computer
+""")
+ self.fail()
+ except LdbError, (num, msg):
+ self.assertEquals(num, ERR_ATTRIBUTE_OR_VALUE_EXISTS)
+
+ print "Testing ranged results"
+ ldb.modify_ldif("""
+dn: cn=ldaptest2computer,cn=computers,""" + self.base_dn + """
+changetype: modify
+replace: servicePrincipalName
+""")
+
+ ldb.modify_ldif("""
+dn: cn=ldaptest2computer,cn=computers,""" + self.base_dn + """
+changetype: modify
+add: servicePrincipalName
+servicePrincipalName: host/ldaptest2computer0
+servicePrincipalName: host/ldaptest2computer1
+servicePrincipalName: host/ldaptest2computer2
+servicePrincipalName: host/ldaptest2computer3
+servicePrincipalName: host/ldaptest2computer4
+servicePrincipalName: host/ldaptest2computer5
+servicePrincipalName: host/ldaptest2computer6
+servicePrincipalName: host/ldaptest2computer7
+servicePrincipalName: host/ldaptest2computer8
+servicePrincipalName: host/ldaptest2computer9
+servicePrincipalName: host/ldaptest2computer10
+servicePrincipalName: host/ldaptest2computer11
+servicePrincipalName: host/ldaptest2computer12
+servicePrincipalName: host/ldaptest2computer13
+servicePrincipalName: host/ldaptest2computer14
+servicePrincipalName: host/ldaptest2computer15
+servicePrincipalName: host/ldaptest2computer16
+servicePrincipalName: host/ldaptest2computer17
+servicePrincipalName: host/ldaptest2computer18
+servicePrincipalName: host/ldaptest2computer19
+servicePrincipalName: host/ldaptest2computer20
+servicePrincipalName: host/ldaptest2computer21
+servicePrincipalName: host/ldaptest2computer22
+servicePrincipalName: host/ldaptest2computer23
+servicePrincipalName: host/ldaptest2computer24
+servicePrincipalName: host/ldaptest2computer25
+servicePrincipalName: host/ldaptest2computer26
+servicePrincipalName: host/ldaptest2computer27
+servicePrincipalName: host/ldaptest2computer28
+servicePrincipalName: host/ldaptest2computer29
+""")
+
+ res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE,
+ attrs=["servicePrincipalName;range=0-*"])
+ self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)")
+ #print len(res[0]["servicePrincipalName;range=0-*"])
+ self.assertEquals(len(res[0]["servicePrincipalName;range=0-*"]), 30)
+
+ res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, attrs=["servicePrincipalName;range=0-19"])
+ self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)")
+ # print res[0]["servicePrincipalName;range=0-19"].length
+ self.assertEquals(len(res[0]["servicePrincipalName;range=0-19"]), 20)
+
+
+ res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, attrs=["servicePrincipalName;range=0-30"])
+ self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)")
+ self.assertEquals(len(res[0]["servicePrincipalName;range=0-*"]), 30)
+
+ res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, attrs=["servicePrincipalName;range=0-40"])
+ self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)")
+ self.assertEquals(len(res[0]["servicePrincipalName;range=0-*"]), 30)
+
+ res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, attrs=["servicePrincipalName;range=30-40"])
+ self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)")
+ self.assertEquals(len(res[0]["servicePrincipalName;range=30-*"]), 0)
+
+
+ res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, attrs=["servicePrincipalName;range=10-40"])
+ self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)")
+ self.assertEquals(len(res[0]["servicePrincipalName;range=10-*"]), 20)
+ # pos_11 = res[0]["servicePrincipalName;range=10-*"][18]
+
+ res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, attrs=["servicePrincipalName;range=11-40"])
+ self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)")
+ self.assertEquals(len(res[0]["servicePrincipalName;range=11-*"]), 19)
+ # print res[0]["servicePrincipalName;range=11-*"][18]
+ # print pos_11
+ # self.assertEquals((res[0]["servicePrincipalName;range=11-*"][18]), pos_11)
+
+ res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, attrs=["servicePrincipalName;range=11-15"])
+ self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)")
+ self.assertEquals(len(res[0]["servicePrincipalName;range=11-15"]), 5)
+ # self.assertEquals(res[0]["servicePrincipalName;range=11-15"][4], pos_11)
+
+ res = ldb.search(self.base_dn, expression="(cn=ldaptest2computer))", scope=SCOPE_SUBTREE, attrs=["servicePrincipalName"])
+ self.assertEquals(len(res), 1, "Could not find (cn=ldaptest2computer)")
+ # print res[0]["servicePrincipalName"][18]
+ # print pos_11
+ self.assertEquals(len(res[0]["servicePrincipalName"]), 30)
+ # self.assertEquals(res[0]["servicePrincipalName"][18], pos_11)
+
+ self.delete_force(self.ldb, "cn=ldaptestuser2,cn=users," + self.base_dn)
+ ldb.add({
+ "dn": "cn=ldaptestuser2,cn=useRs," + self.base_dn,
+ "objectClass": ["person", "user"],
+ "cn": "LDAPtestUSER2",
+ "givenname": "testy",
+ "sn": "ldap user2"})
+
+ print "Testing Ambigious Name Resolution"
+ # Testing ldb.search for (&(anr=ldap testy)(objectClass=user))
+ res = ldb.search(expression="(&(anr=ldap testy)(objectClass=user))")
+ self.assertEquals(len(res), 3, "Found only %d of 3 for (&(anr=ldap testy)(objectClass=user))" % len(res))
+
+ # Testing ldb.search for (&(anr=testy ldap)(objectClass=user))
+ res = ldb.search(expression="(&(anr=testy ldap)(objectClass=user))")
+ self.assertEquals(len(res), 2, "Found only %d of 2 for (&(anr=testy ldap)(objectClass=user))" % len(res))
+
+ # Testing ldb.search for (&(anr=ldap)(objectClass=user))
+ res = ldb.search(expression="(&(anr=ldap)(objectClass=user))")
+ self.assertEquals(len(res), 4, "Found only %d of 4 for (&(anr=ldap)(objectClass=user))" % len(res))
+
+ # Testing ldb.search for (&(anr==ldap)(objectClass=user))
+ res = ldb.search(expression="(&(anr==ldap)(objectClass=user))")
+ self.assertEquals(len(res), 1, "Could not find (&(anr==ldap)(objectClass=user)). Found only %d for (&(anr=ldap)(objectClass=user))" % len(res))
+
+ self.assertEquals(str(res[0].dn), ("CN=ldaptestuser,CN=Users," + self.base_dn))
+ self.assertEquals(res[0]["cn"][0], "ldaptestuser")
+ self.assertEquals(str(res[0]["name"]), "ldaptestuser")
+
+ # Testing ldb.search for (&(anr=testy)(objectClass=user))
+ res = ldb.search(expression="(&(anr=testy)(objectClass=user))")
+ self.assertEquals(len(res), 2, "Found only %d for (&(anr=testy)(objectClass=user))" % len(res))
+
+ # Testing ldb.search for (&(anr=testy ldap)(objectClass=user))
+ res = ldb.search(expression="(&(anr=testy ldap)(objectClass=user))")
+ self.assertEquals(len(res), 2, "Found only %d for (&(anr=testy ldap)(objectClass=user))" % len(res))
+
+ # Testing ldb.search for (&(anr==testy ldap)(objectClass=user))
+# this test disabled for the moment, as anr with == tests are not understood
+# res = ldb.search(expression="(&(anr==testy ldap)(objectClass=user))")
+# self.assertEquals(len(res), 1, "Found only %d for (&(anr==testy ldap)(objectClass=user))" % len(res))
+
+ self.assertEquals(str(res[0].dn), ("CN=ldaptestuser,CN=Users," + self.base_dn))
+ self.assertEquals(res[0]["cn"][0], "ldaptestuser")
+ self.assertEquals(res[0]["name"][0], "ldaptestuser")
+
+ # Testing ldb.search for (&(anr==testy ldap)(objectClass=user))
+# res = ldb.search(expression="(&(anr==testy ldap)(objectClass=user))")
+# self.assertEquals(len(res), 1, "Could not find (&(anr==testy ldap)(objectClass=user))")
+
+ self.assertEquals(str(res[0].dn), ("CN=ldaptestuser,CN=Users," + self.base_dn))
+ self.assertEquals(res[0]["cn"][0], "ldaptestuser")
+ self.assertEquals(res[0]["name"][0], "ldaptestuser")
+
+ # Testing ldb.search for (&(anr=testy ldap user)(objectClass=user))
+ res = ldb.search(expression="(&(anr=testy ldap user)(objectClass=user))")
+ self.assertEquals(len(res), 1, "Could not find (&(anr=testy ldap user)(objectClass=user))")
+
+ self.assertEquals(str(res[0].dn), ("CN=ldaptestuser2,CN=Users," + self.base_dn))
+ self.assertEquals(str(res[0]["cn"]), "ldaptestuser2")
+ self.assertEquals(str(res[0]["name"]), "ldaptestuser2")
+
+ # Testing ldb.search for (&(anr==testy ldap user2)(objectClass=user))
+# res = ldb.search(expression="(&(anr==testy ldap user2)(objectClass=user))")
+# self.assertEquals(len(res), 1, "Could not find (&(anr==testy ldap user2)(objectClass=user))")
+
+ self.assertEquals(str(res[0].dn), ("CN=ldaptestuser2,CN=Users," + self.base_dn))
+ self.assertEquals(str(res[0]["cn"]), "ldaptestuser2")
+ self.assertEquals(str(res[0]["name"]), "ldaptestuser2")
+
+ # Testing ldb.search for (&(anr==ldap user2)(objectClass=user))
+# res = ldb.search(expression="(&(anr==ldap user2)(objectClass=user))")
+# self.assertEquals(len(res), 1, "Could not find (&(anr==ldap user2)(objectClass=user))")
+
+ self.assertEquals(str(res[0].dn), ("CN=ldaptestuser2,CN=Users," + self.base_dn))
+ self.assertEquals(str(res[0]["cn"]), "ldaptestuser2")
+ self.assertEquals(str(res[0]["name"]), "ldaptestuser2")
+
+ # Testing ldb.search for (&(anr==not ldap user2)(objectClass=user))
+# res = ldb.search(expression="(&(anr==not ldap user2)(objectClass=user))")
+# self.assertEquals(len(res), 0, "Must not find (&(anr==not ldap user2)(objectClass=user))")
+
+ # Testing ldb.search for (&(anr=not ldap user2)(objectClass=user))
+ res = ldb.search(expression="(&(anr=not ldap user2)(objectClass=user))")
+ self.assertEquals(len(res), 0, "Must not find (&(anr=not ldap user2)(objectClass=user))")
+
+ # Testing ldb.search for (&(anr="testy ldap")(objectClass=user)) (ie, with quotes)
+# res = ldb.search(expression="(&(anr==\"testy ldap\")(objectClass=user))")
+# self.assertEquals(len(res), 0, "Found (&(anr==\"testy ldap\")(objectClass=user))")
+
+ print "Testing Group Modifies"
+ ldb.modify_ldif("""
+dn: cn=ldaptestgroup,cn=users,""" + self.base_dn + """
+changetype: modify
+add: member
+member: cn=ldaptestuser2,cn=users,""" + self.base_dn + """
+member: cn=ldaptestcomputer,cn=computers,""" + self.base_dn + """
+""")
+
+ self.delete_force(ldb, "cn=ldaptestuser3,cn=users," + self.base_dn)
+
+ print "Testing adding non-existent user to a group"
+ try:
+ ldb.modify_ldif("""
+dn: cn=ldaptestgroup,cn=users,""" + self.base_dn + """
+changetype: modify
+add: member
+member: cn=ldaptestuser3,cn=users,""" + self.base_dn + """
+""")
+ self.fail()
+ except LdbError, (num, _):
+ self.assertEquals(num, ERR_NO_SUCH_OBJECT)
+
+ print "Testing Renames"
+
+ attrs = ["objectGUID", "objectSid"]
+ print "Testing ldb.search for (&(cn=ldaptestUSer2)(objectClass=user))"
+ res_user = ldb.search(self.base_dn, expression="(&(cn=ldaptestUSer2)(objectClass=user))", scope=SCOPE_SUBTREE, attrs=attrs)
+ self.assertEquals(len(res_user), 1, "Could not find (&(cn=ldaptestUSer2)(objectClass=user))")
+
+ #Check rename works with extended/alternate DN forms
+ ldb.rename("<SID=" + ldb.schema_format_value("objectSID", res_user[0]["objectSID"][0]) + ">" , "cn=ldaptestuser3,cn=users," + self.base_dn)
+
+ ldb.rename("cn=ldaptestuser3,cn=users," + self.base_dn, "cn=ldaptestuser3,cn=users," + self.base_dn)
+
+ ldb.rename("cn=ldaptestuser3,cn=users," + self.base_dn, "cn=ldaptestUSER3,cn=users," + self.base_dn)
+
+ print "Testing ldb.search for (&(cn=ldaptestuser3)(objectClass=user))"
+ res = ldb.search(expression="(&(cn=ldaptestuser3)(objectClass=user))")
+ self.assertEquals(len(res), 1, "Could not find (&(cn=ldaptestuser3)(objectClass=user))")
+
+ self.assertEquals(str(res[0].dn), ("CN=ldaptestUSER3,CN=Users," + self.base_dn))
+ self.assertEquals(str(res[0]["cn"]), "ldaptestUSER3")
+ self.assertEquals(str(res[0]["name"]), "ldaptestUSER3")
+
+ #"Testing ldb.search for (&(&(cn=ldaptestuser3)(userAccountControl=*))(objectClass=user))"
+ res = ldb.search(expression="(&(&(cn=ldaptestuser3)(userAccountControl=*))(objectClass=user))")
+ self.assertEquals(len(res), 1, "(&(&(cn=ldaptestuser3)(userAccountControl=*))(objectClass=user))")
+
+ self.assertEquals(str(res[0].dn), ("CN=ldaptestUSER3,CN=Users," + self.base_dn))
+ self.assertEquals(str(res[0]["cn"]), "ldaptestUSER3")
+ self.assertEquals(str(res[0]["name"]), "ldaptestUSER3")
+
+ #"Testing ldb.search for (&(&(cn=ldaptestuser3)(userAccountControl=546))(objectClass=user))"
+ res = ldb.search(expression="(&(&(cn=ldaptestuser3)(userAccountControl=546))(objectClass=user))")
+ self.assertEquals(len(res), 1, "(&(&(cn=ldaptestuser3)(userAccountControl=546))(objectClass=user))")
+
+ self.assertEquals(str(res[0].dn), ("CN=ldaptestUSER3,CN=Users," + self.base_dn))
+ self.assertEquals(str(res[0]["cn"]), "ldaptestUSER3")
+ self.assertEquals(str(res[0]["name"]), "ldaptestUSER3")
+
+ #"Testing ldb.search for (&(&(cn=ldaptestuser3)(userAccountControl=547))(objectClass=user))"
+ res = ldb.search(expression="(&(&(cn=ldaptestuser3)(userAccountControl=547))(objectClass=user))")
+ self.assertEquals(len(res), 0, "(&(&(cn=ldaptestuser3)(userAccountControl=547))(objectClass=user))")
+
+ # This is a Samba special, and does not exist in real AD
+ # print "Testing ldb.search for (dn=CN=ldaptestUSER3,CN=Users," + self.base_dn + ")"
+ # res = ldb.search("(dn=CN=ldaptestUSER3,CN=Users," + self.base_dn + ")")
+ # if (res.error != 0 || len(res) != 1) {
+ # print "Could not find (dn=CN=ldaptestUSER3,CN=Users," + self.base_dn + ")"
+ # self.assertEquals(len(res), 1)
+ # }
+ # self.assertEquals(res[0].dn, ("CN=ldaptestUSER3,CN=Users," + self.base_dn))
+ # self.assertEquals(res[0].cn, "ldaptestUSER3")
+ # self.assertEquals(res[0].name, "ldaptestUSER3")
+
+ print "Testing ldb.search for (distinguishedName=CN=ldaptestUSER3,CN=Users," + self.base_dn + ")"
+ res = ldb.search(expression="(distinguishedName=CN=ldaptestUSER3,CN=Users," + self.base_dn + ")")
+ self.assertEquals(len(res), 1, "Could not find (dn=CN=ldaptestUSER3,CN=Users," + self.base_dn + ")")
+ self.assertEquals(str(res[0].dn), ("CN=ldaptestUSER3,CN=Users," + self.base_dn))
+ self.assertEquals(str(res[0]["cn"]), "ldaptestUSER3")
+ self.assertEquals(str(res[0]["name"]), "ldaptestUSER3")
+
+ # ensure we cannot add it again
+ try:
+ ldb.add({"dn": "cn=ldaptestuser3,cn=userS," + self.base_dn,
+ "objectClass": ["person", "user"],
+ "cn": "LDAPtestUSER3"})
+ self.fail()
+ except LdbError, (num, _):
+ self.assertEquals(num, ERR_ENTRY_ALREADY_EXISTS)
+
+ # rename back
+ ldb.rename("cn=ldaptestuser3,cn=users," + self.base_dn, "cn=ldaptestuser2,cn=users," + self.base_dn)
+
+ # ensure we cannnot rename it twice
+ try:
+ ldb.rename("cn=ldaptestuser3,cn=users," + self.base_dn,
+ "cn=ldaptestuser2,cn=users," + self.base_dn)
+ self.fail()
+ except LdbError, (num, _):
+ self.assertEquals(num, ERR_NO_SUCH_OBJECT)
+
+ # ensure can now use that name
+ ldb.add({"dn": "cn=ldaptestuser3,cn=users," + self.base_dn,
+ "objectClass": ["person", "user"],
+ "cn": "LDAPtestUSER3"})
+
+ # ensure we now cannnot rename
+ try:
+ ldb.rename("cn=ldaptestuser2,cn=users," + self.base_dn, "cn=ldaptestuser3,cn=users," + self.base_dn)
+ self.fail()
+ except LdbError, (num, _):
+ self.assertEquals(num, ERR_ENTRY_ALREADY_EXISTS)
+ try:
+ ldb.rename("cn=ldaptestuser3,cn=users," + self.base_dn, "cn=ldaptestuser3,cn=configuration," + self.base_dn)
+ self.fail()
+ except LdbError, (num, _):
+ self.assertTrue(num in (71, 64))
+
+ ldb.rename("cn=ldaptestuser3,cn=users," + self.base_dn, "cn=ldaptestuser5,cn=users," + self.base_dn)
+
+ ldb.delete("cn=ldaptestuser5,cn=users," + self.base_dn)
+
+ self.delete_force(ldb, "cn=ldaptestgroup2,cn=users," + self.base_dn)
+
+ ldb.rename("cn=ldaptestgroup,cn=users," + self.base_dn, "cn=ldaptestgroup2,cn=users," + self.base_dn)
+
+ print "Testing subtree Renames"
+
+ ldb.add({"dn": "cn=ldaptestcontainer," + self.base_dn,
+ "objectClass": "container"})
+
+ self.delete_force(self.ldb, "cn=ldaptestuser4,cn=ldaptestcontainer," + self.base_dn)
+ ldb.add({"dn": "CN=ldaptestuser4,CN=ldaptestcontainer," + self.base_dn,
+ "objectClass": ["person", "user"],
+ "cn": "LDAPtestUSER4"})
+
+ ldb.modify_ldif("""
+dn: cn=ldaptestgroup2,cn=users,""" + self.base_dn + """
+changetype: modify
+add: member
+member: cn=ldaptestuser4,cn=ldaptestcontainer,""" + self.base_dn + """
+""")
+
+ print "Testing ldb.rename of cn=ldaptestcontainer," + self.base_dn + " to cn=ldaptestcontainer2," + self.base_dn
+ ldb.rename("CN=ldaptestcontainer," + self.base_dn, "CN=ldaptestcontainer2," + self.base_dn)
+
+ print "Testing ldb.search for (&(cn=ldaptestuser4)(objectClass=user))"
+ res = ldb.search(expression="(&(cn=ldaptestuser4)(objectClass=user))")
+ self.assertEquals(len(res), 1, "Could not find (&(cn=ldaptestuser4)(objectClass=user))")
+
+ print "Testing subtree ldb.search for (&(cn=ldaptestuser4)(objectClass=user)) in (just renamed from) cn=ldaptestcontainer," + self.base_dn
+ try:
+ res = ldb.search("cn=ldaptestcontainer," + self.base_dn,
+ expression="(&(cn=ldaptestuser4)(objectClass=user))",
+ scope=SCOPE_SUBTREE)
+ self.fail(res)
+ except LdbError, (num, _):
+ self.assertEquals(num, ERR_NO_SUCH_OBJECT)
+
+ print "Testing one-level ldb.search for (&(cn=ldaptestuser4)(objectClass=user)) in (just renamed from) cn=ldaptestcontainer," + self.base_dn
+ try:
+ res = ldb.search("cn=ldaptestcontainer," + self.base_dn,
+ expression="(&(cn=ldaptestuser4)(objectClass=user))", scope=SCOPE_ONELEVEL)
+ self.fail()
+ except LdbError, (num, _):
+ self.assertEquals(num, ERR_NO_SUCH_OBJECT)
+
+ print "Testing ldb.search for (&(cn=ldaptestuser4)(objectClass=user)) in renamed container"
+ res = ldb.search("cn=ldaptestcontainer2," + self.base_dn, expression="(&(cn=ldaptestuser4)(objectClass=user))", scope=SCOPE_SUBTREE)
+ self.assertEquals(len(res), 1, "Could not find (&(cn=ldaptestuser4)(objectClass=user)) under cn=ldaptestcontainer2," + self.base_dn)
+
+ self.assertEquals(str(res[0].dn), ("CN=ldaptestuser4,CN=ldaptestcontainer2," + self.base_dn))
+ self.assertEquals(res[0]["memberOf"][0].upper(), ("CN=ldaptestgroup2,CN=Users," + self.base_dn).upper())
+
+ time.sleep(4)
+
+ print "Testing ldb.search for (&(member=CN=ldaptestuser4,CN=ldaptestcontainer2," + self.base_dn + ")(objectclass=group)) to check subtree renames and linked attributes"
+ res = ldb.search(self.base_dn, expression="(&(member=CN=ldaptestuser4,CN=ldaptestcontainer2," + self.base_dn + ")(objectclass=group))", scope=SCOPE_SUBTREE)
+ self.assertEquals(len(res), 1, "Could not find (&(member=CN=ldaptestuser4,CN=ldaptestcontainer2," + self.base_dn + ")(objectclass=group)), perhaps linked attributes are not consistant with subtree renames?")
+
+ print "Testing ldb.rename (into itself) of cn=ldaptestcontainer2," + self.base_dn + " to cn=ldaptestcontainer,cn=ldaptestcontainer2," + self.base_dn
+ try:
+ ldb.rename("cn=ldaptestcontainer2," + self.base_dn, "cn=ldaptestcontainer,cn=ldaptestcontainer2," + self.base_dn)
+ self.fail()
+ except LdbError, (num, _):
+ self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
+
+ print "Testing ldb.rename (into non-existent container) of cn=ldaptestcontainer2," + self.base_dn + " to cn=ldaptestcontainer,cn=ldaptestcontainer3," + self.base_dn
+ try:
+ ldb.rename("cn=ldaptestcontainer2," + self.base_dn, "cn=ldaptestcontainer,cn=ldaptestcontainer3," + self.base_dn)
+ self.fail()
+ except LdbError, (num, _):
+ self.assertTrue(num in (ERR_UNWILLING_TO_PERFORM, ERR_OTHER))
+
+ print "Testing delete (should fail, not a leaf node) of renamed cn=ldaptestcontainer2," + self.base_dn
+ try:
+ ldb.delete("cn=ldaptestcontainer2," + self.base_dn)
+ self.fail()
+ except LdbError, (num, _):
+ self.assertEquals(num, ERR_NOT_ALLOWED_ON_NON_LEAF)
+
+ print "Testing base ldb.search for CN=ldaptestuser4,CN=ldaptestcontainer2," + self.base_dn
+ res = ldb.search(expression="(objectclass=*)", base=("CN=ldaptestuser4,CN=ldaptestcontainer2," + self.base_dn), scope=SCOPE_BASE)
+ self.assertEquals(len(res), 1)
+ res = ldb.search(expression="(cn=ldaptestuser40)", base=("CN=ldaptestuser4,CN=ldaptestcontainer2," + self.base_dn), scope=SCOPE_BASE)
+ self.assertEquals(len(res), 0)
+
+ print "Testing one-level ldb.search for (&(cn=ldaptestuser4)(objectClass=user)) in cn=ldaptestcontainer2," + self.base_dn
+ res = ldb.search(expression="(&(cn=ldaptestuser4)(objectClass=user))", base=("cn=ldaptestcontainer2," + self.base_dn), scope=SCOPE_ONELEVEL)
+ # FIXME: self.assertEquals(len(res), 0)
+
+ print "Testing one-level ldb.search for (&(cn=ldaptestuser4)(objectClass=user)) in cn=ldaptestcontainer2," + self.base_dn
+ res = ldb.search(expression="(&(cn=ldaptestuser4)(objectClass=user))", base=("cn=ldaptestcontainer2," + self.base_dn), scope=SCOPE_SUBTREE)
+ # FIXME: self.assertEquals(len(res), 0)
+
+ print "Testing delete of subtree renamed "+("CN=ldaptestuser4,CN=ldaptestcontainer2," + self.base_dn)
+ ldb.delete(("CN=ldaptestuser4,CN=ldaptestcontainer2," + self.base_dn))
+ print "Testing delete of renamed cn=ldaptestcontainer2," + self.base_dn
+ ldb.delete("cn=ldaptestcontainer2," + self.base_dn)
+
+ self.delete_force(self.ldb, "cn=ldaptestutf8user èùéìòà ,cn=users," + self.base_dn)
+ ldb.add({"dn": "cn=ldaptestutf8user èùéìòà ,cn=users," + self.base_dn, "objectClass": "user"})
+
+ self.delete_force(self.ldb, "cn=ldaptestutf8user2 èùéìòà ,cn=users," + self.base_dn)
+ ldb.add({"dn": "cn=ldaptestutf8user2 èùéìòà ,cn=users," + self.base_dn, "objectClass": "user"})
+
+ print "Testing ldb.search for (&(cn=ldaptestuser)(objectClass=user))"
+ res = ldb.search(expression="(&(cn=ldaptestuser)(objectClass=user))")
+ self.assertEquals(len(res), 1, "Could not find (&(cn=ldaptestuser)(objectClass=user))")
+
+ self.assertEquals(str(res[0].dn), ("CN=ldaptestuser,CN=Users," + self.base_dn))
+ self.assertEquals(str(res[0]["cn"]), "ldaptestuser")
+ self.assertEquals(str(res[0]["name"]), "ldaptestuser")
+ self.assertEquals(set(res[0]["objectClass"]), set(["top", "person", "organizationalPerson", "user"]))
+ self.assertTrue("objectGUID" in res[0])
+ self.assertTrue("whenCreated" in res[0])
+ self.assertEquals(str(res[0]["objectCategory"]), ("CN=Person,CN=Schema,CN=Configuration," + self.base_dn))
+ self.assertEquals(int(res[0]["sAMAccountType"][0]), 805306368)
+ self.assertEquals(int(res[0]["userAccountControl"][0]), 546)
+ self.assertEquals(res[0]["memberOf"][0].upper(), ("CN=ldaptestgroup2,CN=Users," + self.base_dn).upper())
+ self.assertEquals(len(res[0]["memberOf"]), 1)
+
+ print "Testing ldb.search for (&(cn=ldaptestuser)(objectCategory=cn=person,cn=schema,cn=configuration," + self.base_dn + "))"
+ res2 = ldb.search(expression="(&(cn=ldaptestuser)(objectCategory=cn=person,cn=schema,cn=configuration," + self.base_dn + "))")
+ self.assertEquals(len(res2), 1, "Could not find (&(cn=ldaptestuser)(objectCategory=cn=person,cn=schema,cn=configuration," + self.base_dn + "))")
+
+ self.assertEquals(res[0].dn, res2[0].dn)
+
+ print "Testing ldb.search for (&(cn=ldaptestuser)(objectCategory=PerSon))"
+ res3 = ldb.search(expression="(&(cn=ldaptestuser)(objectCategory=PerSon))")
+ self.assertEquals(len(res3), 1, "Could not find (&(cn=ldaptestuser)(objectCategory=PerSon)): matched %d" % len(res3))
+
+ self.assertEquals(res[0].dn, res3[0].dn)
+
+ if gc_ldb is not None:
+ print "Testing ldb.search for (&(cn=ldaptestuser)(objectCategory=PerSon)) in Global Catalog"
+ res3gc = gc_ldb.search(expression="(&(cn=ldaptestuser)(objectCategory=PerSon))")
+ self.assertEquals(len(res3gc), 1)
+
+ self.assertEquals(res[0].dn, res3gc[0].dn)
+
+ print "Testing ldb.search for (&(cn=ldaptestuser)(objectCategory=PerSon)) in with 'phantom root' control"
+
+ res3control = gc_ldb.search(self.base_dn, expression="(&(cn=ldaptestuser)(objectCategory=PerSon))", scope=SCOPE_SUBTREE, attrs=["cn"], controls=["search_options:1:2"])
+ self.assertEquals(len(res3control), 1, "Could not find (&(cn=ldaptestuser)(objectCategory=PerSon)) in Global Catalog")
+
+ self.assertEquals(res[0].dn, res3control[0].dn)
+
+ ldb.delete(res[0].dn)
+
+ print "Testing ldb.search for (&(cn=ldaptestcomputer)(objectClass=user))"
+ res = ldb.search(expression="(&(cn=ldaptestcomputer)(objectClass=user))")
+ self.assertEquals(len(res), 1, "Could not find (&(cn=ldaptestuser)(objectClass=user))")
+
+ self.assertEquals(str(res[0].dn), ("CN=ldaptestcomputer,CN=Computers," + self.base_dn))
+ self.assertEquals(str(res[0]["cn"]), "ldaptestcomputer")
+ self.assertEquals(str(res[0]["name"]), "ldaptestcomputer")
+ self.assertEquals(set(res[0]["objectClass"]), set(["top", "person", "organizationalPerson", "user", "computer"]))
+ self.assertTrue("objectGUID" in res[0])
+ self.assertTrue("whenCreated" in res[0])
+ self.assertEquals(str(res[0]["objectCategory"]), ("CN=Computer,CN=Schema,CN=Configuration," + self.base_dn))
+ self.assertEquals(int(res[0]["primaryGroupID"][0]), 513)
+ self.assertEquals(int(res[0]["sAMAccountType"][0]), 805306368)
+ self.assertEquals(int(res[0]["userAccountControl"][0]), 546)
+ self.assertEquals(res[0]["memberOf"][0].upper(), ("CN=ldaptestgroup2,CN=Users," + self.base_dn).upper())
+ self.assertEquals(len(res[0]["memberOf"]), 1)
+
+ print "Testing ldb.search for (&(cn=ldaptestcomputer)(objectCategory=cn=computer,cn=schema,cn=configuration," + self.base_dn + "))"
+ res2 = ldb.search(expression="(&(cn=ldaptestcomputer)(objectCategory=cn=computer,cn=schema,cn=configuration," + self.base_dn + "))")
+ self.assertEquals(len(res2), 1, "Could not find (&(cn=ldaptestcomputer)(objectCategory=cn=computer,cn=schema,cn=configuration," + self.base_dn + "))")
+
+ self.assertEquals(res[0].dn, res2[0].dn)
+
+ if gc_ldb is not None:
+ print "Testing ldb.search for (&(cn=ldaptestcomputer)(objectCategory=cn=computer,cn=schema,cn=configuration," + self.base_dn + ")) in Global Catlog"
+ res2gc = gc_ldb.search(expression="(&(cn=ldaptestcomputer)(objectCategory=cn=computer,cn=schema,cn=configuration," + self.base_dn + "))")
+ self.assertEquals(len(res2gc), 1, "Could not find (&(cn=ldaptestcomputer)(objectCategory=cn=computer,cn=schema,cn=configuration," + self.base_dn + ")) in Global Catlog")
+
+ self.assertEquals(res[0].dn, res2gc[0].dn)
+
+ print "Testing ldb.search for (&(cn=ldaptestcomputer)(objectCategory=compuTER))"
+ res3 = ldb.search(expression="(&(cn=ldaptestcomputer)(objectCategory=compuTER))")
+ self.assertEquals(len(res3), 1, "Could not find (&(cn=ldaptestcomputer)(objectCategory=compuTER))")
+
+ self.assertEquals(res[0].dn, res3[0].dn)
+
+ if gc_ldb is not None:
+ print "Testing ldb.search for (&(cn=ldaptestcomputer)(objectCategory=compuTER)) in Global Catalog"
+ res3gc = gc_ldb.search(expression="(&(cn=ldaptestcomputer)(objectCategory=compuTER))")
+ self.assertEquals(len(res3gc), 1, "Could not find (&(cn=ldaptestcomputer)(objectCategory=compuTER)) in Global Catalog")
+
+ self.assertEquals(res[0].dn, res3gc[0].dn)
+
+ print "Testing ldb.search for (&(cn=ldaptestcomp*r)(objectCategory=compuTER))"
+ res4 = ldb.search(expression="(&(cn=ldaptestcomp*r)(objectCategory=compuTER))")
+ self.assertEquals(len(res4), 1, "Could not find (&(cn=ldaptestcomp*r)(objectCategory=compuTER))")
+
+ self.assertEquals(res[0].dn, res4[0].dn)
+
+ print "Testing ldb.search for (&(cn=ldaptestcomput*)(objectCategory=compuTER))"
+ res5 = ldb.search(expression="(&(cn=ldaptestcomput*)(objectCategory=compuTER))")
+ self.assertEquals(len(res5), 1, "Could not find (&(cn=ldaptestcomput*)(objectCategory=compuTER))")
+
+ self.assertEquals(res[0].dn, res5[0].dn)
+
+ print "Testing ldb.search for (&(cn=*daptestcomputer)(objectCategory=compuTER))"
+ res6 = ldb.search(expression="(&(cn=*daptestcomputer)(objectCategory=compuTER))")
+ self.assertEquals(len(res6), 1, "Could not find (&(cn=*daptestcomputer)(objectCategory=compuTER))")
+
+ self.assertEquals(res[0].dn, res6[0].dn)
+
+ ldb.delete("<GUID=" + ldb.schema_format_value("objectGUID", res[0]["objectGUID"][0]) + ">")
+
+ print "Testing ldb.search for (&(cn=ldaptest2computer)(objectClass=user))"
+ res = ldb.search(expression="(&(cn=ldaptest2computer)(objectClass=user))")
+ self.assertEquals(len(res), 1, "Could not find (&(cn=ldaptest2computer)(objectClass=user))")
+
+ self.assertEquals(str(res[0].dn), "CN=ldaptest2computer,CN=Computers," + self.base_dn)
+ self.assertEquals(str(res[0]["cn"]), "ldaptest2computer")
+ self.assertEquals(str(res[0]["name"]), "ldaptest2computer")
+ self.assertEquals(list(res[0]["objectClass"]), ["top", "person", "organizationalPerson", "user", "computer"])
+ self.assertTrue("objectGUID" in res[0])
+ self.assertTrue("whenCreated" in res[0])
+ self.assertEquals(res[0]["objectCategory"][0], "CN=Computer,CN=Schema,CN=Configuration," + self.base_dn)
+ self.assertEquals(int(res[0]["sAMAccountType"][0]), 805306369)
+ self.assertEquals(int(res[0]["userAccountControl"][0]), 4096)
+
+ ldb.delete("<SID=" + ldb.schema_format_value("objectSID", res[0]["objectSID"][0]) + ">")
+
+ attrs = ["cn", "name", "objectClass", "objectGUID", "objectSID", "whenCreated", "nTSecurityDescriptor", "memberOf", "allowedAttributes", "allowedAttributesEffective"]
+ print "Testing ldb.search for (&(cn=ldaptestUSer2)(objectClass=user))"
+ res_user = ldb.search(self.base_dn, expression="(&(cn=ldaptestUSer2)(objectClass=user))", scope=SCOPE_SUBTREE, attrs=attrs)
+ self.assertEquals(len(res_user), 1, "Could not find (&(cn=ldaptestUSer2)(objectClass=user))")
+
+ self.assertEquals(str(res_user[0].dn), ("CN=ldaptestuser2,CN=Users," + self.base_dn))
+ self.assertEquals(str(res_user[0]["cn"]), "ldaptestuser2")
+ self.assertEquals(str(res_user[0]["name"]), "ldaptestuser2")
+ self.assertEquals(list(res_user[0]["objectClass"]), ["top", "person", "organizationalPerson", "user"])
+ self.assertTrue("objectSid" in res_user[0])
+ self.assertTrue("objectGUID" in res_user[0])
+ self.assertTrue("whenCreated" in res_user[0])
+ self.assertTrue("nTSecurityDescriptor" in res_user[0])
+ self.assertTrue("allowedAttributes" in res_user[0])
+ self.assertTrue("allowedAttributesEffective" in res_user[0])
+ self.assertEquals(res_user[0]["memberOf"][0].upper(), ("CN=ldaptestgroup2,CN=Users," + self.base_dn).upper())
+
+ ldaptestuser2_sid = res_user[0]["objectSid"][0]
+ ldaptestuser2_guid = res_user[0]["objectGUID"][0]
+
+ attrs = ["cn", "name", "objectClass", "objectGUID", "objectSID", "whenCreated", "nTSecurityDescriptor", "member", "allowedAttributes", "allowedAttributesEffective"]
+ print "Testing ldb.search for (&(cn=ldaptestgroup2)(objectClass=group))"
+ res = ldb.search(self.base_dn, expression="(&(cn=ldaptestgroup2)(objectClass=group))", scope=SCOPE_SUBTREE, attrs=attrs)
+ self.assertEquals(len(res), 1, "Could not find (&(cn=ldaptestgroup2)(objectClass=group))")
+
+ self.assertEquals(str(res[0].dn), ("CN=ldaptestgroup2,CN=Users," + self.base_dn))
+ self.assertEquals(str(res[0]["cn"]), "ldaptestgroup2")
+ self.assertEquals(str(res[0]["name"]), "ldaptestgroup2")
+ self.assertEquals(list(res[0]["objectClass"]), ["top", "group"])
+ self.assertTrue("objectGUID" in res[0])
+ self.assertTrue("objectSid" in res[0])
+ self.assertTrue("whenCreated" in res[0])
+ self.assertTrue("nTSecurityDescriptor" in res[0])
+ self.assertTrue("allowedAttributes" in res[0])
+ self.assertTrue("allowedAttributesEffective" in res[0])
+ memberUP = []
+ for m in res[0]["member"]:
+ memberUP.append(m.upper())
+ self.assertTrue(("CN=ldaptestuser2,CN=Users," + self.base_dn).upper() in memberUP)
+
+ res = ldb.search(self.base_dn, expression="(&(cn=ldaptestgroup2)(objectClass=group))", scope=SCOPE_SUBTREE, attrs=attrs, controls=["extended_dn:1:1"])
+ self.assertEquals(len(res), 1, "Could not find (&(cn=ldaptestgroup2)(objectClass=group))")
+
+ print res[0]["member"]
+ memberUP = []
+ for m in res[0]["member"]:
+ memberUP.append(m.upper())
+ print ("<GUID=" + ldb.schema_format_value("objectGUID", ldaptestuser2_guid) + ">;<SID=" + ldb.schema_format_value("objectSid", ldaptestuser2_sid) + ">;CN=ldaptestuser2,CN=Users," + self.base_dn).upper()
+
+ self.assertTrue(("<GUID=" + ldb.schema_format_value("objectGUID", ldaptestuser2_guid) + ">;<SID=" + ldb.schema_format_value("objectSid", ldaptestuser2_sid) + ">;CN=ldaptestuser2,CN=Users," + self.base_dn).upper() in memberUP)
+
+ print "Testing Linked attribute behaviours"
+ ldb.modify_ldif("""
+dn: cn=ldaptestgroup2,cn=users,""" + self.base_dn + """
+changetype: modify
+replace: member
+member: CN=ldaptestuser2,CN=Users,""" + self.base_dn + """
+member: CN=ldaptestutf8user èùéìòà,CN=Users,""" + self.base_dn + """
+""")
+
+ ldb.modify_ldif("""
+dn: <GUID=""" + ldb.schema_format_value("objectGUID", res[0]["objectGUID"][0]) + """>
+changetype: modify
+replace: member
+member: CN=ldaptestutf8user èùéìòà,CN=Users,""" + self.base_dn + """
+""")
+
+ ldb.modify_ldif("""
+dn: <SID=""" + ldb.schema_format_value("objectSid", res[0]["objectSid"][0]) + """>
+changetype: modify
+delete: member
+""")
+
+ ldb.modify_ldif("""
+dn: cn=ldaptestgroup2,cn=users,""" + self.base_dn + """
+changetype: modify
+add: member
+member: <GUID=""" + ldb.schema_format_value("objectGUID", res[0]["objectGUID"][0]) + """>
+member: CN=ldaptestutf8user èùéìòà,CN=Users,""" + self.base_dn + """
+""")
+
+ ldb.modify_ldif("""
+dn: cn=ldaptestgroup2,cn=users,""" + self.base_dn + """
+changetype: modify
+replace: member
+""")
+
+ ldb.modify_ldif("""
+dn: cn=ldaptestgroup2,cn=users,""" + self.base_dn + """
+changetype: modify
+add: member
+member: <SID=""" + ldb.schema_format_value("objectSid", res_user[0]["objectSid"][0]) + """>
+member: CN=ldaptestutf8user èùéìòà,CN=Users,""" + self.base_dn + """
+""")
+
+ ldb.modify_ldif("""
+dn: cn=ldaptestgroup2,cn=users,""" + self.base_dn + """
+changetype: modify
+delete: member
+member: CN=ldaptestutf8user èùéìòà,CN=Users,""" + self.base_dn + """
+""")
+
+ res = ldb.search(self.base_dn, expression="(&(cn=ldaptestgroup2)(objectClass=group))", scope=SCOPE_SUBTREE, attrs=attrs)
+ self.assertEquals(len(res), 1, "Could not find (&(cn=ldaptestgroup2)(objectClass=group))")
+
+ self.assertEquals(str(res[0].dn), ("CN=ldaptestgroup2,CN=Users," + self.base_dn))
+ self.assertEquals(res[0]["member"][0], ("CN=ldaptestuser2,CN=Users," + self.base_dn))
+ self.assertEquals(len(res[0]["member"]), 1)
+
+ ldb.delete(("CN=ldaptestuser2,CN=Users," + self.base_dn))
+
+ time.sleep(4)
+
+ attrs = ["cn", "name", "objectClass", "objectGUID", "whenCreated", "nTSecurityDescriptor", "member"]
+ print "Testing ldb.search for (&(cn=ldaptestgroup2)(objectClass=group)) to check linked delete"
+ res = ldb.search(self.base_dn, expression="(&(cn=ldaptestgroup2)(objectClass=group))", scope=SCOPE_SUBTREE, attrs=attrs)
+ self.assertEquals(len(res), 1, "Could not find (&(cn=ldaptestgroup2)(objectClass=group)) to check linked delete")
+
+ self.assertEquals(str(res[0].dn), ("CN=ldaptestgroup2,CN=Users," + self.base_dn))
+ self.assertTrue("member" not in res[0])
+
+ print "Testing ldb.search for (&(cn=ldaptestutf8user ÈÙÉÌÒÀ)(objectClass=user))"
+ res = ldb.search(expression="(&(cn=ldaptestutf8user ÈÙÉÌÒÀ)(objectClass=user))")
+ self.assertEquals(len(res), 1, "Could not find (&(cn=ldaptestutf8user ÈÙÉÌÒÀ)(objectClass=user))")
+
+ self.assertEquals(str(res[0].dn), ("CN=ldaptestutf8user èùéìòà,CN=Users," + self.base_dn))
+ self.assertEquals(str(res[0]["cn"]), "ldaptestutf8user èùéìòà")
+ self.assertEquals(str(res[0]["name"]), "ldaptestutf8user èùéìòà")
+ self.assertEquals(list(res[0]["objectClass"]), ["top", "person", "organizationalPerson", "user"])
+ self.assertTrue("objectGUID" in res[0])
+ self.assertTrue("whenCreated" in res[0])
+
+ ldb.delete(res[0].dn)
+
+ print "Testing ldb.search for (&(cn=ldaptestutf8user2*)(objectClass=user))"
+ res = ldb.search(expression="(&(cn=ldaptestutf8user2*)(objectClass=user))")
+ self.assertEquals(len(res), 1, "Could not find (&(cn=ldaptestutf8user2*)(objectClass=user))")
+
+ ldb.delete(res[0].dn)
+
+ ldb.delete(("CN=ldaptestgroup2,CN=Users," + self.base_dn))
+
+ print "Testing ldb.search for (&(cn=ldaptestutf8user2 ÈÙÉÌÒÀ)(objectClass=user))"
+ res = ldb.search(expression="(&(cn=ldaptestutf8user ÈÙÉÌÒÀ)(objectClass=user))")
+
+ #FIXME: self.assert len(res) == 1, "Could not find (expect space collapse, win2k3 fails) (&(cn=ldaptestutf8user2 ÈÙÉÌÒÀ)(objectClass=user))"
+
+ print "Testing that we can't get at the configuration DN from the main search base"
+ res = ldb.search(self.base_dn, expression="objectClass=crossRef", scope=SCOPE_SUBTREE, attrs=["cn"])
+ self.assertEquals(len(res), 0)
+
+ print "Testing that we can get at the configuration DN from the main search base on the LDAP port with the 'phantom root' search_options control"
+ res = ldb.search(self.base_dn, expression="objectClass=crossRef", scope=SCOPE_SUBTREE, attrs=["cn"], controls=["search_options:1:2"])
+ self.assertTrue(len(res) > 0)
+
+ if gc_ldb is not None:
+ print "Testing that we can get at the configuration DN from the main search base on the GC port with the search_options control == 0"
+
+ res = gc_ldb.search(self.base_dn, expression="objectClass=crossRef", scope=SCOPE_SUBTREE, attrs=["cn"], controls=["search_options:1:0"])
+ self.assertTrue(len(res) > 0)
+
+ print "Testing that we do find configuration elements in the global catlog"
+ res = gc_ldb.search(self.base_dn, expression="objectClass=crossRef", scope=SCOPE_SUBTREE, attrs=["cn"])
+ self.assertTrue(len(res) > 0)
+
+ print "Testing that we do find configuration elements and user elements at the same time"
+ res = gc_ldb.search(self.base_dn, expression="(|(objectClass=crossRef)(objectClass=person))", scope=SCOPE_SUBTREE, attrs=["cn"])
+ self.assertTrue(len(res) > 0)
+
+ print "Testing that we do find configuration elements in the global catlog, with the configuration basedn"
+ res = gc_ldb.search(self.configuration_dn, expression="objectClass=crossRef", scope=SCOPE_SUBTREE, attrs=["cn"])
+ self.assertTrue(len(res) > 0)
+
+ print "Testing that we can get at the configuration DN on the main LDAP port"
+ res = ldb.search(self.configuration_dn, expression="objectClass=crossRef", scope=SCOPE_SUBTREE, attrs=["cn"])
+ self.assertTrue(len(res) > 0)
+
+ print "Testing objectCategory canonacolisation"
+ res = ldb.search(self.configuration_dn, expression="objectCategory=ntDsDSA", scope=SCOPE_SUBTREE, attrs=["cn"])
+ self.assertTrue(len(res) > 0, "Didn't find any records with objectCategory=ntDsDSA")
+ self.assertTrue(len(res) != 0)
+
+ res = ldb.search(self.configuration_dn, expression="objectCategory=CN=ntDs-DSA," + self.schema_dn, scope=SCOPE_SUBTREE, attrs=["cn"])
+ self.assertTrue(len(res) > 0, "Didn't find any records with objectCategory=CN=ntDs-DSA," + self.schema_dn)
+ self.assertTrue(len(res) != 0)
+
+ print "Testing objectClass attribute order on "+ self.base_dn
+ res = ldb.search(expression="objectClass=domain", base=self.base_dn,
+ scope=SCOPE_BASE, attrs=["objectClass"])
+ self.assertEquals(len(res), 1)
+
+ self.assertEquals(list(res[0]["objectClass"]), ["top", "domain", "domainDNS"])
+
+ # check enumeration
+
+ print "Testing ldb.search for objectCategory=person"
+ res = ldb.search(self.base_dn, expression="objectCategory=person", scope=SCOPE_SUBTREE, attrs=["cn"])
+ self.assertTrue(len(res) > 0)
+
+ print "Testing ldb.search for objectCategory=person with domain scope control"
+ res = ldb.search(self.base_dn, expression="objectCategory=person", scope=SCOPE_SUBTREE, attrs=["cn"], controls=["domain_scope:1"])
+ self.assertTrue(len(res) > 0)
+
+ print "Testing ldb.search for objectCategory=user"
+ res = ldb.search(self.base_dn, expression="objectCategory=user", scope=SCOPE_SUBTREE, attrs=["cn"])
+ self.assertTrue(len(res) > 0)
+
+ print "Testing ldb.search for objectCategory=user with domain scope control"
+ res = ldb.search(self.base_dn, expression="objectCategory=user", scope=SCOPE_SUBTREE, attrs=["cn"], controls=["domain_scope:1"])
+ self.assertTrue(len(res) > 0)
+
+ print "Testing ldb.search for objectCategory=group"
+ res = ldb.search(self.base_dn, expression="objectCategory=group", scope=SCOPE_SUBTREE, attrs=["cn"])
+ self.assertTrue(len(res) > 0)
+
+ print "Testing ldb.search for objectCategory=group with domain scope control"
+ res = ldb.search(self.base_dn, expression="objectCategory=group", scope=SCOPE_SUBTREE, attrs=["cn"], controls=["domain_scope:1"])
+ self.assertTrue(len(res) > 0)
+
+
+class BaseDnTests(unittest.TestCase):
+ def setUp(self):
+ self.ldb = ldb
+
+ def test_rootdse_attrs(self):
+ """Testing for all rootDSE attributes"""
+ res = self.ldb.search(scope=SCOPE_BASE, attrs=[])
+ self.assertEquals(len(res), 1)
+
+ def test_highestcommittedusn(self):
+ """Testing for highestCommittedUSN"""
+ res = self.ldb.search("", scope=SCOPE_BASE, attrs=["highestCommittedUSN"])
+ self.assertEquals(len(res), 1)
+ self.assertTrue(int(res[0]["highestCommittedUSN"][0]) != 0)
+
+ def test_netlogon(self):
+ """Testing for netlogon via LDAP"""
+ res = self.ldb.search("", scope=SCOPE_BASE, attrs=["netlogon"])
+ self.assertEquals(len(res), 0)
+
+ def test_netlogon_highestcommitted_usn(self):
+ """Testing for netlogon and highestCommittedUSN via LDAP"""
+ res = self.ldb.search("", scope=SCOPE_BASE,
+ attrs=["netlogon", "highestCommittedUSN"])
+ self.assertEquals(len(res), 0)
+
+class SchemaTests(unittest.TestCase):
+ def find_schemadn(self, ldb):
+ res = ldb.search(base="", expression="", scope=SCOPE_BASE, attrs=["schemaNamingContext"])
+ self.assertEquals(len(res), 1)
+ return res[0]["schemaNamingContext"][0]
+
+ def setUp(self):
+ self.ldb = ldb
+ self.schema_dn = self.find_schemadn(ldb)
+
+ def test_generated_schema(self):
+ """Testing we can read the generated schema via LDAP"""
+ res = self.ldb.search("cn=aggregate,"+self.schema_dn, scope=SCOPE_BASE,
+ attrs=["objectClasses", "attributeTypes", "dITContentRules"])
+ self.assertEquals(len(res), 1)
+ self.assertTrue("dITContentRules" in res[0])
+ self.assertTrue("objectClasses" in res[0])
+ self.assertTrue("attributeTypes" in res[0])
+
+ def test_generated_schema_is_operational(self):
+ """Testing we don't get the generated schema via LDAP by default"""
+ res = self.ldb.search("cn=aggregate,"+self.schema_dn, scope=SCOPE_BASE,
+ attrs=["*"])
+ self.assertEquals(len(res), 1)
+ self.assertFalse("dITContentRules" in res[0])
+ self.assertFalse("objectClasses" in res[0])
+ self.assertFalse("attributeTypes" in res[0])
+
+if not "://" in host:
+ host = "ldap://%s" % host
+
+ldb = Ldb(host, credentials=creds, session_info=system_session(), lp=lp)
+gc_ldb = Ldb("%s:3268" % host, credentials=creds,
+ session_info=system_session(), lp=lp)
+
+runner = SubunitTestRunner()
+rc = 0
+if not runner.run(unittest.makeSuite(BaseDnTests)).wasSuccessful():
+ rc = 1
+if not runner.run(unittest.makeSuite(BasicTests)).wasSuccessful():
+ rc = 1
+if not runner.run(unittest.makeSuite(SchemaTests)).wasSuccessful():
+ rc = 1
+sys.exit(rc)
diff --git a/source4/lib/ldb/tests/samba4.png b/source4/lib/ldb/tests/samba4.png
new file mode 100644
index 0000000000..c8096889a6
--- /dev/null
+++ b/source4/lib/ldb/tests/samba4.png
Binary files differ
diff --git a/source4/lib/ldb/tests/sample_module.c b/source4/lib/ldb/tests/sample_module.c
new file mode 100644
index 0000000000..bbe4419b59
--- /dev/null
+++ b/source4/lib/ldb/tests/sample_module.c
@@ -0,0 +1,36 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "ldb_module.h"
+
+int sample_add(struct ldb_module *mod, struct ldb_request *req)
+{
+ ldb_msg_add_fmt(req->op.add.message, "touchedBy", "sample");
+
+ return ldb_next_request(mod, req);
+}
+
+const struct ldb_module_ops ldb_sample_module_ops = {
+ .name = "sample",
+ .add = sample_add,
+};
diff --git a/source4/lib/ldb/tests/schema-tests/schema-add-test.ldif b/source4/lib/ldb/tests/schema-tests/schema-add-test.ldif
new file mode 100644
index 0000000000..472ab48fac
--- /dev/null
+++ b/source4/lib/ldb/tests/schema-tests/schema-add-test.ldif
@@ -0,0 +1,66 @@
+dn: CN=Users,DC=schema,DC=test
+objectClass: top
+objectClass: container
+cn: Users
+description: Default container for upgraded user accounts
+instanceType: 4
+whenCreated: 20050116175504.0Z
+whenChanged: 20050116175504.0Z
+uSNCreated: 1
+uSNChanged: 1
+showInAdvancedViewOnly: FALSE
+name: Users
+objectGUID: b847056a-9934-d87b-8a1a-99fabe0863c8
+systemFlags: 0x8c000000
+objectCategory: CN=Container,CN=Schema,CN=Configuration,DC=schema,DC=test
+isCriticalSystemObject: TRUE
+nTSecurityDescriptor: foo
+
+dn: CN=Administrator,CN=Users,DC=schema,DC=test
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: user
+cn: Administrator
+description: Built-in account for administering the computer/domain
+instanceType: 4
+whenCreated: 20050116175504.0Z
+whenChanged: 20050116175504.0Z
+uSNCreated: 1
+memberOf: CN=Group Policy Creator Owners,CN=Users,DC=schema,DC=test
+memberOf: CN=Domain Admins,CN=Users,DC=schema,DC=test
+memberOf: CN=Enterprise Admins,CN=Users,DC=schema,DC=test
+memberOf: CN=Schema Admins,CN=Users,DC=schema,DC=test
+memberOf: CN=Administrators,CN=Builtin,DC=schema,DC=test
+uSNChanged: 1
+name: Administrator
+objectGUID: 6c02f98c-46c6-aa38-5f13-a510cac04e6c
+userAccountControl: 0x10200
+badPwdCount: 0
+codePage: 0
+countryCode: 0
+badPasswordTime: 0
+lastLogoff: 0
+lastLogon: 0
+pwdLastSet: 0
+primaryGroupID: 513
+objectSid: S-1-5-21-43662522-77495566-38969261-500
+adminCount: 1
+accountExpires: 9223372036854775807
+logonCount: 0
+sAMAccountName: Administrator
+sAMAccountType: 0x30000000
+objectCategory: CN=Person,CN=Schema,CN=Configuration,DC=schema,DC=test
+isCriticalSystemObject: TRUE
+unicodePwd: samba
+nTSecurityDescriptor: foo
+
+dn: CN=Test,CN=Users,DC=schema,DC=test
+objectClass: top
+objectClass: test
+cn: Test
+description: This is a test
+objectCategory: CN=Test,CN=Schema,CN=Configuration,DC=schema,DC=test
+nTSecurityDescriptor: foo
+instanceType: 4
+
diff --git a/source4/lib/ldb/tests/schema-tests/schema-mod-test-1.ldif b/source4/lib/ldb/tests/schema-tests/schema-mod-test-1.ldif
new file mode 100644
index 0000000000..b976724485
--- /dev/null
+++ b/source4/lib/ldb/tests/schema-tests/schema-mod-test-1.ldif
@@ -0,0 +1,5 @@
+dn: CN=Test,CN=Users,DC=schema,DC=test
+changetype: modify
+replace: description
+description: this test must not fail
+
diff --git a/source4/lib/ldb/tests/schema-tests/schema-mod-test-2.ldif b/source4/lib/ldb/tests/schema-tests/schema-mod-test-2.ldif
new file mode 100644
index 0000000000..fa193af683
--- /dev/null
+++ b/source4/lib/ldb/tests/schema-tests/schema-mod-test-2.ldif
@@ -0,0 +1,5 @@
+dn: CN=Test,CN=Users,DC=schema,DC=test
+changetype: modify
+delete: description
+# this test must not fail
+
diff --git a/source4/lib/ldb/tests/schema-tests/schema-mod-test-3.ldif b/source4/lib/ldb/tests/schema-tests/schema-mod-test-3.ldif
new file mode 100644
index 0000000000..8ab7798f9c
--- /dev/null
+++ b/source4/lib/ldb/tests/schema-tests/schema-mod-test-3.ldif
@@ -0,0 +1,5 @@
+dn: CN=Test,CN=Users,DC=schema,DC=test
+changetype: modify
+add: description
+description: this test must not fail
+
diff --git a/source4/lib/ldb/tests/schema-tests/schema-mod-test-4.ldif b/source4/lib/ldb/tests/schema-tests/schema-mod-test-4.ldif
new file mode 100644
index 0000000000..cbf0e60bbe
--- /dev/null
+++ b/source4/lib/ldb/tests/schema-tests/schema-mod-test-4.ldif
@@ -0,0 +1,5 @@
+dn: CN=Test,CN=Users,DC=schema,DC=test
+changetype: modify
+add: foo
+foo: this test must fail
+
diff --git a/source4/lib/ldb/tests/schema-tests/schema-mod-test-5.ldif b/source4/lib/ldb/tests/schema-tests/schema-mod-test-5.ldif
new file mode 100644
index 0000000000..bc64e9edb6
--- /dev/null
+++ b/source4/lib/ldb/tests/schema-tests/schema-mod-test-5.ldif
@@ -0,0 +1,5 @@
+dn: CN=Test,CN=Users,DC=schema,DC=test
+changetype: modify
+delete: nTSecurityDescriptor
+# this test must fail
+
diff --git a/source4/lib/ldb/tests/schema-tests/schema.ldif b/source4/lib/ldb/tests/schema-tests/schema.ldif
new file mode 100644
index 0000000000..4ab1932839
--- /dev/null
+++ b/source4/lib/ldb/tests/schema-tests/schema.ldif
@@ -0,0 +1,100 @@
+dn: @INDEXLIST
+@IDXATTR: name
+@IDXATTR: sAMAccountName
+@IDXATTR: objectSid
+@IDXATTR: objectClass
+@IDXATTR: member
+@IDXATTR: uidNumber
+@IDXATTR: gidNumber
+@IDXATTR: unixName
+@IDXATTR: privilege
+@IDXATTR: lDAPDisplayName
+
+dn: @ATTRIBUTES
+realm: CASE_INSENSITIVE
+userPrincipalName: CASE_INSENSITIVE
+servicePrincipalName: CASE_INSENSITIVE
+name: CASE_INSENSITIVE
+dn: CASE_INSENSITIVE
+sAMAccountName: CASE_INSENSITIVE
+objectClass: CASE_INSENSITIVE
+unicodePwd: HIDDEN
+ntPwdHash: HIDDEN
+ntPwdHistory: HIDDEN
+lmPwdHash: HIDDEN
+lmPwdHistory: HIDDEN
+createTimestamp: HIDDEN
+modifyTimestamp: HIDDEN
+
+dn: @MODULES
+@LIST: timestamps,schema
+
+dn: CN=Top,CN=Schema,CN=Configuration,DC=schema,DC=test
+objectClass: top
+objectClass: classSchema
+lDAPDisplayName: top
+cn: Top
+uSNCreated: 1
+uSNChanged: 1
+subClassOf: top
+systemMustContain: objectClass
+systemMayContain: structuralObjectClass
+systemMayContain: createTimeStamp
+systemMayContain: modifyTimeStamp
+systemMayContain: creatorsName
+systemMayContain: modifiersName
+systemMayContain: hasSubordinates
+systemMayContain: subschemaSubentry
+systemMayContain: collectiveSubentry
+systemMayContain: entryUUID
+systemMayContain: entryCSN
+systemMayContain: namingCSN
+systemMayContain: superiorUUID
+systemMayContain: contextCSN
+systemMayContain: whenCreated
+systemMayContain: whenChanged
+systemMayContain: uSNCreated
+systemMayContain: uSNChanged
+systemMayContain: distinguishedName
+systemMayContain: name
+systemMayContain: cn
+systemMayContain: userPassword
+systemMayContain: labeledURI
+
+dn: CN=Class-Schema,CN=Schema,CN=Configuration,DC=schema,DC=test
+objectClass: top
+objectClass: classSchema
+lDAPDisplayName: classSchema
+cn: Class-Schema
+uSNCreated: 2
+uSNChanged: 2
+lDAPDisplayName: classSchema
+subClassOf: top
+systemMustContain: cn
+systemMustContain: subClassOf
+systemMayContain: systemPossSuperiors
+systemMayContain: systemOnly
+systemMayContain: systemMustContain
+systemMayContain: systemMayContain
+systemMayContain: systemAuxiliaryClass
+systemMayContain: possSuperiors
+systemMayContain: mustContain
+systemMayContain: mayContain
+systemMayContain: lDAPDisplayName
+systemMayContain: auxiliaryClass
+
+dn: CN=Attribute-Schema,CN=Schema,CN=Configuration,DC=schema,DC=test
+objectClass: top
+objectClass: classSchema
+cn: Attribute-Schema
+uSNCreated: 3
+uSNChanged: 3
+lDAPDisplayName: attributeSchema
+subClassOf: top
+systemMustContain: oMSyntax
+systemMustContain: lDAPDisplayName
+systemMustContain: isSingleValued
+systemMustContain: cn
+systemMustContain: attributeSyntax
+systemMustContain: attributeID
+
diff --git a/source4/lib/ldb/tests/slapd.conf b/source4/lib/ldb/tests/slapd.conf
new file mode 100644
index 0000000000..fa2789d8c1
--- /dev/null
+++ b/source4/lib/ldb/tests/slapd.conf
@@ -0,0 +1,26 @@
+loglevel 0
+
+include tests/schema/core.schema
+include tests/schema/cosine.schema
+include tests/schema/inetorgperson.schema
+include tests/schema/openldap.schema
+include tests/schema/nis.schema
+
+
+pidfile tests/tmp/slapd.pid
+argsfile tests/tmp/slapd.args
+
+access to * by * write
+
+allow update_anon bind_anon_dn
+
+include tests/tmp/modules.conf
+
+defaultsearchbase "o=University of Michigan,c=TEST"
+
+backend bdb
+database bdb
+suffix "o=University of Michigan,c=TEST"
+directory tests/tmp/db
+index objectClass eq
+index uid eq
diff --git a/source4/lib/ldb/tests/start_slapd.sh b/source4/lib/ldb/tests/start_slapd.sh
new file mode 100755
index 0000000000..11679d47a3
--- /dev/null
+++ b/source4/lib/ldb/tests/start_slapd.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+if [ -z "$LDBDIR" ]; then
+ LDBDIR=`dirname $0`/..
+ export LDBDIR
+fi
+
+mkdir -p $LDBDIR/tests/tmp/db
+
+# running slapd in the background (with &) means it stays in the same process group, so it can be
+# killed by timelimit
+slapd -d0 -f $LDBDIR/tests/slapd.conf -h "`$LDBDIR/tests/ldapi_url.sh`" $* &
+
+sleep 2
diff --git a/source4/lib/ldb/tests/test-attribs.ldif b/source4/lib/ldb/tests/test-attribs.ldif
new file mode 100644
index 0000000000..79508c4b7b
--- /dev/null
+++ b/source4/lib/ldb/tests/test-attribs.ldif
@@ -0,0 +1,6 @@
+dn: @ATTRIBUTES
+uid: CASE_INSENSITIVE
+cn: CASE_INSENSITIVE
+ou: CASE_INSENSITIVE
+dn: CASE_INSENSITIVE
+
diff --git a/source4/lib/ldb/tests/test-config.ldif b/source4/lib/ldb/tests/test-config.ldif
new file mode 100644
index 0000000000..7926a9e3c5
--- /dev/null
+++ b/source4/lib/ldb/tests/test-config.ldif
@@ -0,0 +1,67 @@
+##############################
+# global configuration options
+dn: cn=Global,cn=Config,cn=Samba
+objectclass: globalconfig
+LocalConfigCn: cn=%U,cn=Config,cn=Samba
+LocalConfigCn;1: cn=%U,cn=Config,cn=Samba
+LocalConfigCn;2: cn=%I,cn=Config,cn=Samba
+LocalConfigCn;3: cn=%M,cn=Config,cn=Samba
+
+#############
+dn: cn=Protocol,cn=Global,cn=Config,cn=Samba
+maxXmit: 7000
+
+################################
+dn: cn=Volker,cn=Config,cn=Samba
+Workgroup: VNET3
+UnixCharset: UTF8
+Security: user
+Interfaces: vmnet* eth*
+NetbiosName: blu
+GuestAccount: tridge
+
+#################################
+dn: cn=Volker,cn=Config,cn=Samba
+Workgroup: VNET3
+UnixCharset: UTF8
+Security: user
+Interfaces: vmnet* eth*
+NetbiosName: blu
+GuestAccount: tridge
+Include: cn=%U,cn=MyConfig,cn=Config,cn=Samba
+
+#### ((objectClass=fileshare)(cn=test))
+##############################
+# [test] share
+dn: cn=test,cn=Shares,cn=Config,cn=Samba
+objectclass: fileshare
+cn: test
+Comment: a test share
+Path: /home/tridge/samba4/prefix/test
+ReadOnly: no
+
+#####################################
+# [msdn] a remote proxy share, stored
+# on \\msdn\test
+dn: cn=msdn,cn=Shares,cn=Config,cn=Samba
+objectclass: fileshare
+cn: msdn
+NtvfsHandler: cifs
+ReadOnly: no
+_CifsServer: msdn
+_CifsUser: administrator
+_CifsPassword: penguin
+_CifsDomain: winxp
+_CifsShare: test
+
+
+##############################
+# [VisualC] share
+dn: cn=visualc,cn=Shares,cn=Config,cn=Samba
+objectclass: fileshare
+cn: VisualC
+Comment: VisualC development
+Path: /home/tridge/VisualC
+ReadOnly: no
+NtvfsHandler: simple
+
diff --git a/source4/lib/ldb/tests/test-default-config.ldif b/source4/lib/ldb/tests/test-default-config.ldif
new file mode 100644
index 0000000000..87b7bcd3cc
--- /dev/null
+++ b/source4/lib/ldb/tests/test-default-config.ldif
@@ -0,0 +1,17 @@
+##############################
+# global configuration options
+dn: cn=Global,cn=DefaultConfig,cn=Samba
+objectclass: globalconfig
+Workgroup: WORKGROUP
+UnixCharset: UTF8
+Security: user
+NetbiosName: blu
+GuestAccount: nobody
+
+##############################
+# [_default_] share
+dn: cn=_default_,cn=Shares,cn=DefaultConfig,cn=Samba
+objectclass: fileshare
+cn: _default_
+Path: /tmp
+ReadOnly: yes
diff --git a/source4/lib/ldb/tests/test-extended.sh b/source4/lib/ldb/tests/test-extended.sh
new file mode 100755
index 0000000000..14b988e3f9
--- /dev/null
+++ b/source4/lib/ldb/tests/test-extended.sh
@@ -0,0 +1,69 @@
+#!/bin/sh
+
+echo "Running extended search tests"
+
+mv $LDB_URL $LDB_URL.1
+
+cat <<EOF | $VALGRIND ldbadd$EXEEXT || exit 1
+dn: cn=testrec1,cn=TEST
+i1: 1
+i2: 0
+i3: 1234
+i4: 0x7003004
+
+dn: cn=testrec2,cn=TEST
+i1: 0x800000
+
+dn: cn=testrec3,cn=TEST
+i1: 0x101010101
+i1: 7
+
+dn: cn=auser1,cn=TEST
+groupType: 2147483648
+samAccountType: 805306368
+
+dn: cn=auser2,cn=TEST
+groupType: 2147483648
+samAccountType: 805306369
+
+dn: cn=auser3,cn=TEST
+groupType: 2147483649
+samAccountType: 805306370
+
+dn: cn=auser4,cn=TEST
+groupType: 2147483649
+samAccountType: 805306369
+EOF
+
+checkcount() {
+ count=$1
+ expression="$2"
+ n=`$VALGRIND ldbsearch$EXEEXT "$expression" | grep '^dn' | wc -l`
+ if [ $n != $count ]; then
+ echo "Got $n but expected $count for $expression"
+ $VALGRIND ldbsearch$EXEEXT "$expression"
+ exit 1
+ fi
+ echo "OK: $count $expression"
+}
+
+checkcount 1 '(i3=1234)'
+checkcount 0 '(i3=12345)'
+
+checkcount 2 '(i1:1.2.840.113556.1.4.803:=1)'
+checkcount 1 '(i1:1.2.840.113556.1.4.803:=3)'
+checkcount 1 '(i1:1.2.840.113556.1.4.803:=7)'
+checkcount 0 '(i1:1.2.840.113556.1.4.803:=15)'
+checkcount 1 '(i1:1.2.840.113556.1.4.803:=0x800000)'
+checkcount 1 '(i1:1.2.840.113556.1.4.803:=8388608)'
+
+checkcount 2 '(i1:1.2.840.113556.1.4.804:=1)'
+checkcount 2 '(i1:1.2.840.113556.1.4.804:=3)'
+checkcount 2 '(i1:1.2.840.113556.1.4.804:=7)'
+checkcount 2 '(i1:1.2.840.113556.1.4.804:=15)'
+checkcount 1 '(i1:1.2.840.113556.1.4.804:=0x800000)'
+checkcount 1 '(i1:1.2.840.113556.1.4.804:=8388608)'
+
+# this is one that w2k gives
+checkcount 3 '(|(|(&(!(groupType:1.2.840.113556.1.4.803:=1))(groupType:1.2.840.113556.1.4.803:=2147483648)(groupType:1.2.840.113556.1.4.804:=10))(samAccountType=805306368))(samAccountType=805306369))'
+
diff --git a/source4/lib/ldb/tests/test-generic.sh b/source4/lib/ldb/tests/test-generic.sh
new file mode 100755
index 0000000000..fec4b5b078
--- /dev/null
+++ b/source4/lib/ldb/tests/test-generic.sh
@@ -0,0 +1,128 @@
+#!/bin/sh
+
+if [ -z "$LDB_SPECIALS" ]; then
+ LDB_SPECIALS=1
+ export LDB_SPECIALS
+fi
+
+echo "LDB_URL: $LDB_URL"
+
+echo "Adding base elements"
+$VALGRIND ldbadd$EXEEXT $LDBDIR/tests/test.ldif || exit 1
+
+echo "Adding again - should fail"
+$VALGRIND ldbadd$EXEEXT $LDBDIR/tests/test.ldif 2> /dev/null && {
+ echo "Should have failed to add again - gave $?"
+ exit 1
+}
+
+echo "Modifying elements"
+$VALGRIND ldbmodify$EXEEXT $LDBDIR/tests/test-modify.ldif || exit 1
+
+echo "Showing modified record"
+$VALGRIND ldbsearch$EXEEXT '(uid=uham)' || exit 1
+
+echo "Rename entry"
+OLDDN="cn=Ursula Hampster,ou=Alumni Association,ou=People,o=University of Michigan,c=TEST"
+NEWDN="cn=Hampster Ursula,ou=Alumni Association,ou=People,o=University of Michigan,c=TEST"
+$VALGRIND ldbrename$EXEEXT "$OLDDN" "$NEWDN" || exit 1
+
+echo "Showing renamed record"
+$VALGRIND ldbsearch$EXEEXT '(uid=uham)' || exit 1
+
+echo "Starting ldbtest"
+$VALGRIND ldbtest$EXEEXT --num-records 100 --num-searches 10 || exit 1
+
+if [ $LDB_SPECIALS = 1 ]; then
+ echo "Adding index"
+ $VALGRIND ldbadd$EXEEXT $LDBDIR/tests/test-index.ldif || exit 1
+fi
+
+echo "Adding bad attributes - should fail"
+$VALGRIND ldbadd$EXEEXT $LDBDIR/tests/test-wrong_attributes.ldif && {
+ echo "Should fhave failed - gave $?"
+ exit 1
+}
+
+echo "testing indexed search"
+$VALGRIND ldbsearch$EXEEXT '(uid=uham)' || exit 1
+$VALGRIND ldbsearch$EXEEXT '(&(objectclass=person)(objectclass=person)(objectclass=top))' || exit 1
+$VALGRIND ldbsearch$EXEEXT '(&(uid=uham)(uid=uham))' || exit 1
+$VALGRIND ldbsearch$EXEEXT '(|(uid=uham)(uid=uham))' || exit 1
+$VALGRIND ldbsearch$EXEEXT '(|(uid=uham)(uid=uham)(objectclass=OpenLDAPperson))' || exit 1
+$VALGRIND ldbsearch$EXEEXT '(&(uid=uham)(uid=uham)(!(objectclass=xxx)))' || exit 1
+$VALGRIND ldbsearch$EXEEXT '(&(objectclass=person)(uid=uham)(!(uid=uhamxx)))' uid \* \+ dn || exit 1
+$VALGRIND ldbsearch$EXEEXT '(&(uid=uham)(uid=uha*)(title=*))' uid || exit 1
+
+# note that the "((" is treated as an attribute not an expression
+# this matches the openldap ldapsearch behaviour of looking for a '='
+# to see if the first argument is an expression or not
+$VALGRIND ldbsearch$EXEEXT '((' uid || exit 1
+$VALGRIND ldbsearch$EXEEXT '(objectclass=)' uid || exit 1
+$VALGRIND ldbsearch$EXEEXT -b 'cn=Hampster Ursula,ou=Alumni Association,ou=People,o=University of Michigan,c=TEST' -s base "" sn || exit 1
+
+echo "Test wildcard match"
+$VALGRIND ldbadd$EXEEXT $LDBDIR/tests/test-wildcard.ldif || exit 1
+$VALGRIND ldbsearch$EXEEXT '(cn=test*multi)' || exit 1
+$VALGRIND ldbsearch$EXEEXT '(cn=*test*multi*)' || exit 1
+$VALGRIND ldbsearch$EXEEXT '(cn=*test_multi)' || exit 1
+$VALGRIND ldbsearch$EXEEXT '(cn=test_multi*)' || exit 1
+$VALGRIND ldbsearch$EXEEXT '(cn=test*multi*test*multi)' || exit 1
+$VALGRIND ldbsearch$EXEEXT '(cn=test*multi*test*multi*multi_*)' || exit 1
+
+echo "Starting ldbtest indexed"
+$VALGRIND ldbtest$EXEEXT --num-records 100 --num-searches 500 || exit 1
+
+echo "Testing one level search"
+count=`$VALGRIND ldbsearch$EXEEXT -b 'ou=Groups,o=University of Michigan,c=TEST' -s one 'objectclass=*' none |grep '^dn' | wc -l`
+if [ $count != 3 ]; then
+ echo returned $count records - expected 3
+ exit 1
+fi
+
+echo "Testing binary file attribute value"
+mkdir -p tests/tmp
+cp $LDBDIR/tests/samba4.png tests/tmp/samba4.png
+$VALGRIND ldbmodify$EXEEXT $LDBDIR/tests/photo.ldif || exit 1
+count=`$VALGRIND ldbsearch$EXEEXT '(cn=Hampster Ursula)' jpegPhoto | grep '^dn' | wc -l`
+if [ $count != 1 ]; then
+ echo returned $count records - expected 1
+ exit 1
+fi
+rm -f tests/tmp/samba4.png
+
+echo "*TODO* Testing UTF8 upper lower case searches !!"
+
+echo "Testing compare"
+count=`$VALGRIND ldbsearch$EXEEXT '(cn>=t)' cn | grep '^dn' | wc -l`
+if [ $count != 2 ]; then
+ echo returned $count records - expected 2
+ echo "this fails on openLdap ..."
+fi
+
+count=`$VALGRIND ldbsearch$EXEEXT '(cn<=t)' cn | grep '^dn' | wc -l`
+if [ $count != 13 ]; then
+ echo returned $count records - expected 13
+ echo "this fails on openLdap ..."
+fi
+
+checkcount() {
+ count=$1
+ scope=$2
+ basedn=$3
+ expression="$4"
+ n=`$VALGRIND ldbsearch$EXEEXT -s "$scope" -b "$basedn" "$expression" | grep '^dn' | wc -l`
+ if [ $n != $count ]; then
+ echo "Got $n but expected $count for $expression"
+ bin/ldbsearch "$expression"
+ exit 1
+ fi
+ echo "OK: $count $expression"
+}
+
+checkcount 0 'base' '' '(uid=uham)'
+checkcount 0 'one' '' '(uid=uham)'
+
+checkcount 1 'base' 'cn=Hampster Ursula,ou=Alumni Association,ou=People,o=University of Michigan,c=TEST' '(uid=uham)'
+checkcount 1 'one' 'ou=Alumni Association,ou=People,o=University of Michigan,c=TEST' '(uid=uham)'
+checkcount 1 'one' 'ou=People,o=University of Michigan,c=TEST' '(ou=ldb test)'
diff --git a/source4/lib/ldb/tests/test-index.ldif b/source4/lib/ldb/tests/test-index.ldif
new file mode 100644
index 0000000000..268173641d
--- /dev/null
+++ b/source4/lib/ldb/tests/test-index.ldif
@@ -0,0 +1,7 @@
+dn: @INDEXLIST
+@IDXATTR: uid
+@IDXATTR: objectclass
+
+dn: @ATTRIBUTES
+uid: CASE_INSENSITIVE
+
diff --git a/source4/lib/ldb/tests/test-ldap.sh b/source4/lib/ldb/tests/test-ldap.sh
new file mode 100755
index 0000000000..14cfb5f979
--- /dev/null
+++ b/source4/lib/ldb/tests/test-ldap.sh
@@ -0,0 +1,54 @@
+#!/bin/sh
+
+PATH=/usr/local/sbin:/usr/sbin:/sbin:$PATH
+export PATH
+SCHEMA_NEEDED="core nis cosine inetorgperson openldap"
+
+# setup needed schema files
+for f in $SCHEMA_NEEDED; do
+ if [ ! -r tests/schema/$f.schema ]; then
+ mkdir -p tests/schema
+ if [ -r /etc/ldap/schema/$f.schema ]; then
+ ln -s /etc/ldap/schema/$f.schema tests/schema/$f.schema
+ continue;
+ fi
+ if [ -r /etc/openldap/schema/$f.schema ]; then
+ ln -s /etc/openldap/schema/$f.schema tests/schema/$f.schema
+ continue;
+ fi
+
+ echo "SKIPPING TESTS: you need the following OpenLDAP schema files"
+ for f in $SCHEMA_NEEDED; do
+ echo " $f.schema"
+ done
+ exit 0
+ fi
+done
+
+if [ -z "$LDBDIR" ]; then
+ LDBDIR=`dirname $0`/..
+ export LDBDIR
+fi
+
+LDB_URL=`$LDBDIR/tests/ldapi_url.sh`
+export LDB_URL
+
+PATH=bin:$PATH
+export PATH
+
+LDB_SPECIALS=0
+export LDB_SPECIALS
+
+if $LDBDIR/tests/init_slapd.sh &&
+ $LDBDIR/tests/start_slapd.sh &&
+ $LDBDIR/tests/test-generic.sh; then
+ echo "ldap tests passed";
+ ret=0
+else
+ echo "ldap tests failed";
+ ret=$?
+fi
+
+#$LDBDIR/tests/kill_slapd.sh
+
+exit $ret
diff --git a/source4/lib/ldb/tests/test-modify.ldif b/source4/lib/ldb/tests/test-modify.ldif
new file mode 100644
index 0000000000..e5b9ca4086
--- /dev/null
+++ b/source4/lib/ldb/tests/test-modify.ldif
@@ -0,0 +1,23 @@
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,o=University of Michiga
+ n,c=TEST
+changetype: modify
+add: drink
+drink: mango lassi
+-
+add: drink
+drink: lemonade
+-
+delete: pager
+-
+replace: telephonenumber
+telephonenumber: +61 2 6260 6012
+telephonenumber: +61 412 666 929
+-
+delete: telephonenumber
+telephonenumber: +61 2 6260 6012
+-
+delete: telephonenumber
+telephonenumber: +61 412 666 929
+-
+add: telephonenumber
+telephonenumber: +61 412 666 929
diff --git a/source4/lib/ldb/tests/test-schema.sh b/source4/lib/ldb/tests/test-schema.sh
new file mode 100755
index 0000000000..2f10fb45e2
--- /dev/null
+++ b/source4/lib/ldb/tests/test-schema.sh
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+LDB_URL="tdb://schema.ldb"
+export LDB_URL
+
+rm -f schema.ldb
+
+echo "LDB_URL: $LDB_URL"
+
+echo "Adding schema"
+$VALGRIND bin/ldbadd $LDBDIR/tests/schema-tests/schema.ldif || exit 1
+
+echo "Adding few test elements (no failure expected here)"
+$VALGRIND bin/ldbadd $LDBDIR/tests/schema-tests/schema-add-test.ldif || exit 1
+
+echo "Modifying elements (2 failures expected here)"
+
+$VALGRIND bin/ldbmodify $LDBDIR/tests/schema-tests/schema-mod-test-1.ldif || exit 1
+$VALGRIND bin/ldbmodify $LDBDIR/tests/schema-tests/schema-mod-test-2.ldif || exit 1
+$VALGRIND bin/ldbmodify $LDBDIR/tests/schema-tests/schema-mod-test-3.ldif || exit 1
+$VALGRIND bin/ldbmodify $LDBDIR/tests/schema-tests/schema-mod-test-4.ldif
+if [ "$?" == "0" ]; then
+ echo "test failed!"
+ exit 1
+fi
+$VALGRIND bin/ldbmodify $LDBDIR/tests/schema-tests/schema-mod-test-5.ldif
+if [ "$?" == "0" ]; then
+ echo "test failed!"
+ exit 1
+fi
+
+echo "Showing modified record"
+$VALGRIND bin/ldbsearch '(cn=Test)' || exit 1
+
diff --git a/source4/lib/ldb/tests/test-soloading.sh b/source4/lib/ldb/tests/test-soloading.sh
new file mode 100755
index 0000000000..da6d57541e
--- /dev/null
+++ b/source4/lib/ldb/tests/test-soloading.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+if [ -n "$TEST_DATA_PREFIX" ]; then
+ LDB_URL="$TEST_DATA_PREFIX/tdbtest.ldb"
+else
+ LDB_URL="tdbtest.ldb"
+fi
+export LDB_URL
+
+PATH=bin:$PATH
+export PATH
+
+rm -f $LDB_URL*
+
+if [ -z "$LDBDIR" ]; then
+ LDBDIR=`dirname $0`/..
+ export LDBDIR
+fi
+
+cat <<EOF | $VALGRIND ldbadd || exit 1
+dn: @MODULES
+@LIST: sample
+EOF
+
+cat <<EOF | $VALGRIND ldbadd || exit 1
+dn: dc=bar
+dc: bar
+someThing: someThingElse
+EOF
+
+$VALGRIND ldbsearch "(touchedBy=sample)" | grep "touchedBy: sample" || exit 1
+
diff --git a/source4/lib/ldb/tests/test-sqlite3.sh b/source4/lib/ldb/tests/test-sqlite3.sh
new file mode 100755
index 0000000000..0cef318d98
--- /dev/null
+++ b/source4/lib/ldb/tests/test-sqlite3.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+
+LDB_URL="sqlite3://sqltest.ldb"
+export LDB_URL
+
+rm -f sqltest.ldb
+
+if [ -z "$LDBDIR" ]; then
+ LDBDIR=`dirname $0`/..
+ export LDBDIR
+fi
+
+PATH=bin:$PATH
+export PATH
+
+LDB_SPECIALS=0
+export LDB_SPECIALS
+
+$LDBDIR/tests/test-generic.sh
+
+#. $LDBDIR/tests/test-extended.sh
+
+#. $LDBDIR/tests/test-tdb-features.sh
+
diff --git a/source4/lib/ldb/tests/test-tdb-features.sh b/source4/lib/ldb/tests/test-tdb-features.sh
new file mode 100644
index 0000000000..d4248366ca
--- /dev/null
+++ b/source4/lib/ldb/tests/test-tdb-features.sh
@@ -0,0 +1,160 @@
+#!/bin/sh
+
+echo "Running tdb feature tests"
+
+mv $LDB_URL $LDB_URL.2
+
+checkcount() {
+ count=$1
+ expression="$2"
+ n=`$VALGRIND ldbsearch$EXEEXT "$expression" | grep '^dn' | wc -l`
+ if [ $n != $count ]; then
+ echo "Got $n but expected $count for $expression"
+ $VALGRIND ldbsearch$EXEEXT "$expression"
+ exit 1
+ fi
+ echo "OK: $count $expression"
+}
+
+echo "Testing case sensitive search"
+cat <<EOF | $VALGRIND ldbadd$EXEEXT || exit 1
+dn: cn=t1,cn=TEST
+objectClass: testclass
+test: foo
+EOF
+checkcount 1 '(test=foo)'
+checkcount 0 '(test=FOO)'
+checkcount 0 '(test=FO*)'
+
+echo "Making case insensitive"
+cat <<EOF | $VALGRIND ldbmodify$EXEEXT || exit 1
+dn: @ATTRIBUTES
+changetype: add
+add: test
+test: CASE_INSENSITIVE
+EOF
+
+echo $ldif | $VALGRIND ldbmodify$EXEEXT || exit 1
+checkcount 1 '(test=foo)'
+checkcount 1 '(test=FOO)'
+checkcount 1 '(test=fo*)'
+
+echo "adding i"
+cat <<EOF | $VALGRIND ldbmodify$EXEEXT || exit 1
+dn: cn=t1,cn=TEST
+changetype: modify
+add: i
+i: 0x100
+EOF
+checkcount 1 '(i=0x100)'
+checkcount 0 '(i=256)'
+
+echo "marking i as INTEGER"
+cat <<EOF | $VALGRIND ldbmodify$EXEEXT || exit 1
+dn: @ATTRIBUTES
+changetype: modify
+add: i
+i: INTEGER
+EOF
+checkcount 1 '(i=0x100)'
+checkcount 1 '(i=256)'
+
+echo "adding j"
+cat <<EOF | $VALGRIND ldbmodify$EXEEXT || exit 1
+dn: cn=t1,cn=TEST
+changetype: modify
+add: j
+j: 0x100
+EOF
+checkcount 1 '(j=0x100)'
+checkcount 0 '(j=256)'
+
+echo "Adding wildcard attribute"
+cat <<EOF | $VALGRIND ldbmodify$EXEEXT || exit 1
+dn: @ATTRIBUTES
+changetype: modify
+add: *
+*: INTEGER
+EOF
+checkcount 1 '(j=0x100)'
+checkcount 1 '(j=256)'
+
+echo "Testing class search"
+checkcount 0 '(objectClass=otherclass)'
+checkcount 1 '(objectClass=testclass)'
+
+echo "Adding index"
+cat <<EOF | $VALGRIND ldbadd$EXEEXT || exit 1
+dn: @INDEXLIST
+@IDXATTR: i
+@IDXATTR: test
+EOF
+checkcount 1 '(i=0x100)'
+checkcount 1 '(i=256)'
+checkcount 0 '(i=-256)'
+checkcount 1 '(test=foo)'
+checkcount 1 '(test=FOO)'
+checkcount 1 '(test=*f*o)'
+
+echo "making test case sensitive"
+cat <<EOF | $VALGRIND ldbmodify$EXEEXT || exit 1
+dn: @ATTRIBUTES
+changetype: modify
+replace: test
+test: NONE
+EOF
+checkcount 1 '(test=foo)'
+checkcount 0 '(test=FOO)'
+checkcount 1 '(test=f*o*)'
+
+checkone() {
+ count=$1
+ base="$2"
+ expression="$3"
+ n=`$VALGRIND ldbsearch$EXEEXT -s one -b "$base" "$expression" | grep '^dn' | wc -l`
+ if [ $n != $count ]; then
+ echo "Got $n but expected $count for $expression"
+ $VALGRIND ldbsearch$EXEEXT -s one -b "$base" "$expression"
+ exit 1
+ fi
+ echo "OK: $count $expression"
+}
+
+echo "Removing wildcard attribute"
+cat <<EOF | $VALGRIND ldbmodify$EXEEXT || exit 1
+dn: @ATTRIBUTES
+changetype: modify
+delete: *
+*: INTEGER
+EOF
+
+echo "Adding one level indexes"
+cat <<EOF | $VALGRIND ldbmodify$EXEEXT || exit 1
+dn: @INDEXLIST
+changetype: modify
+add: @IDXONE
+@IDXONE: 1
+EOF
+
+echo "Testing one level indexed search"
+cat <<EOF | $VALGRIND ldbadd$EXEEXT || exit 1
+dn: cn=one,cn=t1,cn=TEST
+objectClass: oneclass
+cn: one
+test: one
+EOF
+checkone 1 "cn=t1,cn=TEST" '(test=one)'
+cat <<EOF | $VALGRIND ldbadd$EXEEXT || exit 1
+dn: cn=two,cn=t1,cn=TEST
+objectClass: oneclass
+cn: two
+test: one
+
+dn: cn=three,cn=t1,cn=TEST
+objectClass: oneclass
+cn: three
+test: one
+EOF
+checkone 3 "cn=t1,cn=TEST" '(test=one)'
+checkone 1 "cn=t1,cn=TEST" '(cn=two)'
+
diff --git a/source4/lib/ldb/tests/test-tdb.sh b/source4/lib/ldb/tests/test-tdb.sh
new file mode 100755
index 0000000000..1c35451962
--- /dev/null
+++ b/source4/lib/ldb/tests/test-tdb.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+if [ -n "$TEST_DATA_PREFIX" ]; then
+ LDB_URL="$TEST_DATA_PREFIX/tdbtest.ldb"
+else
+ LDB_URL="tdbtest.ldb"
+fi
+export LDB_URL
+
+PATH=bin:$PATH
+export PATH
+
+rm -f $LDB_URL*
+
+if [ -z "$LDBDIR" ]; then
+ LDBDIR=`dirname $0`/..
+ export LDBDIR
+fi
+
+cat <<EOF | $VALGRIND ldbadd$EXEEXT || exit 1
+dn: @MODULES
+@LIST: rdn_name
+EOF
+
+$VALGRIND ldbadd$EXEEXT $LDBDIR/tests/init.ldif || exit 1
+
+. $LDBDIR/tests/test-generic.sh
+
+. $LDBDIR/tests/test-extended.sh
+
+. $LDBDIR/tests/test-tdb-features.sh
diff --git a/source4/lib/ldb/tests/test-wildcard.ldif b/source4/lib/ldb/tests/test-wildcard.ldif
new file mode 100644
index 0000000000..222512eeab
--- /dev/null
+++ b/source4/lib/ldb/tests/test-wildcard.ldif
@@ -0,0 +1,5 @@
+dn: cn=test_multi_test_multi_test_multi,o=University of Michigan,c=TEST
+objectclass: person
+cn: test_multi_test_multi_test_multi
+sn: multi_test
+description: test multi wildcards matching
diff --git a/source4/lib/ldb/tests/test-wrong_attributes.ldif b/source4/lib/ldb/tests/test-wrong_attributes.ldif
new file mode 100644
index 0000000000..27f45f0e56
--- /dev/null
+++ b/source4/lib/ldb/tests/test-wrong_attributes.ldif
@@ -0,0 +1,3 @@
+dn: @ATTRIBUTES
+uid: CASE_INTENSIVE
+
diff --git a/source4/lib/ldb/tests/test.ldif b/source4/lib/ldb/tests/test.ldif
new file mode 100644
index 0000000000..e53fadc700
--- /dev/null
+++ b/source4/lib/ldb/tests/test.ldif
@@ -0,0 +1,411 @@
+dn: ou=Groups,o=University of Michigan,c=TEST
+objectclass: organizationalUnit
+ou: Groups
+
+dn: ou=Information Technology Division,ou=People,o=University of Michigan,c=TEST
+objectclass: organizationalUnit
+ou: Information Technology Division
+description:: aMODwoPDgsKCw4PCgsOCwotFVlZQw4PCg8OCwoPDg8KCw4LCv0zDg8KDw4LCgsOD
+ woLDgsKKT8ODwoPDgsKDw4PCgsOCwqs6w4PCg8OCwoLDg8KCw4LCjUQkw4PCg8OCwoLDg8KCw4LCi
+ 01QUcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4
+ LCgsODwoLDgsKLRCQoZitEJMODwoPDgsKCw4PCgsOCwrfDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoP
+ Dg8KCw4LCgcODwoPDgsKDw4PCgsOCwqHDg8KDw4LCgsODwoLDgsKLRCQkZitEJMODwoPDgsKCw4PC
+ gsOCwrfDg8KDw4LCg8ODwoLDgsKQw4PCg8OCwoPDg8KCw4LCisODwoPDgsKCw4PCgsOCwotFUVZqU
+ MODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKAw4PCg8OCwoLDg8KCw4LCik85dCTDg8KDw4
+ LCgsODwoLDgsKFQ8ODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4L
+ Cvzl0JMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPD
+ gsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKLRCTDg8KDw4LCgsODwoLDgsKDw4PCg8OCwoLDg8KCw
+ 4LCuMODwoPDgsKDw4PCgsOCwoR0Q8ODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LChMODwo
+ PDgsKDw4PCgsOCwoFOdTrDg8KDw4LCg8ODwoLDgsKHw4PCg8OCwoPDg8KCw4LChMODwoPDgsKDw4P
+ CgsOCwoFOw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwrtHw4PCg8OCwoLDg8KCw4LChcOD
+ woPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsK4dMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODw
+ oLDgsKtR8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwo
+ PDgsKDw4PCgsOCwr9SfGrDg8KDw4LCgsODwoLDgsKLQGgxw4PCg8OCwoPDg8KCw4LCoWhQw4PCg8O
+ CwoPDg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKT8ODwoPDgsKCw4PCgsOC
+ wotEJDDDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHTDg8KDw4LCgsODwoLDgsKDw4PCg
+ 8OCwoPDg8KCw4LCuHXDg8KDw4LCgsODwoLDgsKLRCRqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4
+ PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpPDg8K
+ Dw4LCg8ODwoLDgsKQXV9eW8ODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoPD
+ g8KCw4LCgsODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODw
+ oPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgs
+ OCwoxWV8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKxw4PCg8OCwoLDg8KCw4LCi3wkw4P
+ Cg8OCwoLDg8KCw4LCjcODwoPDgsKCw4PCgsOCwofDg8KDw4LCg8ODwoLDgsKof8ODwoPDgsKDw4PC
+ gsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCg8ODwoPDgsKDw4PCgsOCwrh5w4PCg
+ 8OCwoLDg8KCw4LChzQzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PCgsOCworDg8KDw4LCgsODwo
+ LDgsKIw4PCg8OCwoLDg8KCw4LCuDFBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNdDF
+ Bw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPD
+ gsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw
+ 4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgs
+ KCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKAdcODwoPDgsKDw4PCgsOCwqhtw4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCsMODwoPDgsKC
+ w4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCt
+ sODwoPDgsKDw4PCgsOCwq7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4
+ PCgsOCwoPDg8KDw4LCg8ODwoLDgsKoZsODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4w4P
+ Cg8OCwoLDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwpUzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PC
+ gsOCworDg8KDw4LCgsODwoLDgsKISDJBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNN
+ DJBw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwo
+ PDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8O
+ DwoPDgsKDw4PCgsOCwojDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCnEzDg8KDw4LCgsOD
+ woLDgsKLSEBmw4PCg8OCwoLDg8KCw4LCg3lwdSTDg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw
+ 4LCv8ODwoPDgsKCw4PCgsOCwobDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwp/Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwoj
+ Dg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCwpPDg8KDw4LCgsOD
+ woLDgsKBw4PCg8OCwoPDg8KCw4LCv1rDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODw
+ oPDgsKCw4PCgsOCwodqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwoBqaMODwoPDgsKCw4
+ PCgsOCwpBQw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDIMODwoPDgsKCw4PCgsOCwopPw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKOacODwoPDgsKCw4PCgsOCwrhf
+ XsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCw
+ oLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKGw4PCg8OCwoLDg8KCw4LCgM
+ ODwoPDgsKCw4PCgsOCwoRJw4PCg8OCwoLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsO
+ DwoLDgsKIw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQ9w4PCg8OCwoLDg8KCw4LCgcOD
+ woPDgsKDw4PCgsOCwr9aw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQxw4PCg8OCwoLDg
+ 8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LCm0
+ 7Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsK
+ Cw4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODw
+ oPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgs
+ OCwo7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCkMODwoPDgsKDw4PCgsOCwojDg8KDw4L
+ CgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsK+
+ S8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKww4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDw
+ 4PCgsOCwoTDg8KDw4LCgsODwoLDgsKKT1DDg8KDw4LCg8ODwoLDgsKoRsODwoPDgsKCw4PCgsOCwo
+ vDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwrZ0Y8ODwoPDgsK
+ Cw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/dF/Dg8KDw4LCgsODwoLDgsKhdHpPw4PCg8OCwoLDg8KC
+ w4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PCg8OCwoPDg8KCw4LCqC1Jw4PCg8OCwoLDg8KCw4LChcODw
+ oPDgsKDw4PCgsOCwoB1RMODwoPDgsKCw4PCgsOCwqFwek/Dg8KDw4LCgsODwoLDgsKLw4PCg8OCwo
+ PDg8KCw4LCj1DDg8KDw4LCg8ODwoLDgsKoScODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK
+ AdTPDg8KDw4LCgsODwoLDgsKhbHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PC
+ g8OCwoPDg8KCw4LCqEnDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHXDg8KDw4LCgsODw
+ oLDgsKhaHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo9Qw4PCg8OCwoPDg8KCw4LCqM
+ ODwoPDgsKDw4PCgsOCwrpIw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoB1M8ODwoPDgsK
+ Dw4PCgsOCwoBfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgjPDg8KDw4LCg8ODwoLDgsKAX17Dg
+ 8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCg8ODwo
+ LDgsKoJ8ODwoPDgsKDw4PCgsOCwq3Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoP
+ DgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4aHU5w4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PC
+ gsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw
+ 4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgs
+ KIw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpLDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoB0IcODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKA
+ w4PCg8OCwoPDg8KCw4LCtMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKAdGbDg8KDw4LCg
+ sODwoLDgsKLQGY9dGY9dTPDg8KDw4LCg8ODwoLDgsKAX17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwo
+ LDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPDgsKDw4PCgsO
+ CwoIzw4PCg8OCwoPDg8KCw4LCgF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwovDg8KD
+ w4LCg8ODwoLDgsK/Ri9BUC9BRi9BWi9BZC9BWzBBZC9BZTBBZC9BZC9BbzBBZC9BeTBBw4PCg8OCw
+ oLDg8KCw4LCgzBBMUFhMUFrMUE=
+description:: UF7Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOC
+ wozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg
+ 8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCqFDDg8KDw4LCg8ODwoLDgsKpRsODwoPDgsKDw4PCgsOCwo
+ zDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8O
+ DwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKCw4PCgsOCwotEJCDDg8KDw4LCgsODwoLDgsKD
+ w4PCg8OCwoPDg8KCw4LCrMODwoPDgsKCw4PCgsOCwotUJCRTw4PCg8OCwoLDg8KCw4LCi1wkJFbDg
+ 8KDw4LCgsODwoLDgsKJTCRXVVBSU8ODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsKdT8ODwo
+ PDgsKCw4PCgsOCwoN8JDB1w4PCg8OCwoPDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8O
+ DwoLDgsKBTsODwoPDgsKDw4PCgsOCwqktw4PCg8OCwoLDg8KCw4LCg3wkMHTDg8KDw4LCgsODwoLD
+ gsKDfCQww4PCg8OCwoLDg8KCw4LChTPDg8KDw4LCg8ODwoLDgsK2OTXDg8KDw4LCg8ODwoLDgsKAw
+ 4PCg8OCwoPDg8KCw4LCgU7Dg8KDw4LCgsODwoLDgsKEIMODwoPDgsKCw4PCgsOCwqFIw4PCg8OCwo
+ PDg8KCw4LChU7Dg8KDw4LCgsODwoLDgsKJNcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8ODwoLDgsK
+ BTsODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKD
+ w4PCgsOCwr9TXMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw
+ 4LChMODwoPDgsKCw4PCgsOCwpHDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLIEjDg8
+ KDw4LCg8ODwoLDgsKFTlDDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ngw4PCg8OCwoL
+ Dg8KCw4LCi8ODwoPDgsKDw4PCgsOCwpjDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCm3Rx
+ w4PCg8OCwoLDg8KCw4LCizvDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi8ODwoPDgsKDw
+ 4PCgsOCwr9XaMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGdGLDg8KDw4LCgsODwo
+ LDgsKLf2zDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi1D
+ Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8OD
+ woLDgsKow4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwq10SmgoT03Dg8KDw4LCgsODwoLDg
+ sKLw4PCg8OCwoPDg8KCw4LCjcODwoPDgsKDw4PCgsOCwqggTMODwoPDgsKCw4PCgsOCwoXDg8KDw4
+ LCg8ODwoLDgsKAdDrDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLTSBQUcODwoPDgsK
+ Dw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKL
+ RCQoZitEJCDDg8KDw4LCgsODwoLDgsK3w4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwoHDg
+ 8KDw4LCg8ODwoLDgsKhw4PCg8OCwoLDg8KCw4LCi0QkJGYrRCTDg8KDw4LCgsODwoLDgsK3w4PCg8
+ OCwoPDg8KCw4LCkMODwoPDgsKDw4PCgsOCworDg8KDw4LCgsODwoLDgsKLRSBRVmpQw4PCg8OCwoP
+ Dg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKTzl0JHXDg8KDw4LCgsODwoLD
+ gsKhOXQkw4PCg8OCwoLDg8KCw4LChW/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODw
+ oPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKhRMODwoPDgsKDw4PCgsOCwoVOw4PCg8OCwoLDg8
+ KCw4LCi8ODwoPDgsKDw4PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ncw4P
+ Cg8OCwoLDg8KCw4LCiUQkw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsOD
+ woLDgsKEw4PCg8OCwoPDg8KCw4LCtjPDg8KDw4LCg8ODwoLDgsK2w4PCg8OCwoLDg8KCw4LCjUQkw
+ 4PCg8OCwoLDg8KCw4LCiyBEw4PCg8OCwoPDg8KCw4LChU5Qw4PCg8OCwoLDg8KCw4LCi8ODwoPDgs
+ KDw4PCgsOCwr9TYMODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCkMODwoPDgsKC
+ w4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCj8ODwoPDgsKDw4PCgsOCwr9Ta
+ MODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwr3Dg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4L
+ Cj1DDg8KDw4LCg8ODwoLDgsK/U2zDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCqMODwoPD
+ gsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKtw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCw
+ p9oMMODwoPDgsKDw4PCgsOCwolMw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4
+ LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCq0vDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4L
+ CgMODwoPDgsKCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi0QkOcODwoPD
+ gsKCw4PCgsOCwrDDg8KDw4LCg8ODwoLDgsKEdEU5w4PCg8OCwoLDg8KCw4LCtTR0PcODwoPDgsKCw
+ 4PCgsOCwovDg8KDw4LCg8ODwoLDgsKNw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwo5Lw4
+ PCg8OCwoLDg8KCw4LCi0AgUMODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKsw4PCg8OCwoL
+ Dg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHUow4PCg8OCwoLDg8KCw4LC
+ i8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCgsODwoLDgsKJw4PCg8OCwoLDg8KCw4LCtTTDg8KDw4LCg
+ 8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKDw4PCgsOCwrtWw4PCg8OCwoLDg8KCw4LCi8
+ ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCnw==
+
+#LEAD COMMENT
+
+# another comment
+dn: CN=All Staff,ou=Groups,o=University of Michigan,c=TEST
+#EMBEDDED COMMENT
+member: cn=Manager,o=University of Michigan,c=TEST
+member: cn=Barbara Jensen,ou=Information Technology Division,ou=People,o=Unive
+ rsity of Michigan,c=TEST
+member: cn=Jane Doe,ou=Alumni Association,ou=People,o=University of Michigan,c
+ =US
+member: cn=John Doe,ou=Information Technology Division,ou=People,o=University
+ of Michigan,c=TEST
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,o=University of Michiga
+ n,c=TEST
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,o=University of Mic
+ higan,c=TEST
+member: cn=James A Jones 2,ou=Information Technology Division,ou=People,o=Univ
+ ersity of Michigan,c=TEST
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,o=University of Mich
+ igan,c=TEST
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,o=University of Mic
+ higan,c=TEST
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,o=University of Mic
+ higan,c=TEST
+member: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,o=Univers
+ ity of Michigan,c=TEST
+owner: cn=Manager,o=University of Michigan,c=TEST
+cn: All Staff
+description: Everyone in the sample data
+objectclass: groupofnames
+
+dn: cn=Alumni Assoc Staff,ou=Groups,o=University of Michigan,c=TEST
+member: cn=Manager,o=University of Michigan,c=TEST
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,o=University of Mic
+ higan,c=TEST
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,o=University of Mic
+ higan,c=TEST
+member: cn=Jane Doe,ou=Alumni Association,ou=People,o=University of Michigan,c
+ =US
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,o=University of Mich
+ igan,c=TEST
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,o=University of Michiga
+ n,c=TEST
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,o=University of Mic
+ higan,c=TEST
+owner: cn=Manager,o=University of Michigan,c=TEST
+description: All Alumni Assoc Staff
+cn: Alumni Assoc Staff
+objectclass: groupofnames
+
+dn: ou=Alumni Association,ou=People,o=University of Michigan,c=TEST
+objectclass: organizationalUnit
+ou: Alumni Association
+
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,o=Universit
+ y of Michigan,c=TEST
+objectclass: OpenLDAPperson
+cn: Barbara Jensen
+cn: Babs Jensen
+sn:: IEplbnNlbiA=
+uid:: YmplCW5zZW4
+title: Mythical Manager, Research Systems
+postaladdress: ITD Prod Dev & Deployment $ 535 W. William St. Room 4212 $ Ann
+ Arbor, MI 48103-4943
+seealso: cn=All Staff,ou=Groups,o=University of Michigan,c=TEST
+userpassword:: YmplbnNlbg==
+mail: bjensen@mailgw.example.com
+homepostaladdress: 123 Wesley $ Ann Arbor, MI 48103
+description: Mythical manager of the rsdd unix project
+drink: water
+homephone: +1 313 555 2333
+pager: +1 313 555 3233
+facsimiletelephonenumber: +1 313 555 2274
+telephonenumber: +1 313 555 9022
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,o=University
+ of Michigan,c=TEST
+objectclass: OpenLDAPperson
+cn: Bjorn Jensen
+cn: Biiff Jensen
+sn: Jensen
+uid: bjorn
+seealso: cn=All Staff,ou=Groups,o=University of Michigan,c=TEST
+userpassword:: Ympvcm4=
+homepostaladdress: 19923 Seven Mile Rd. $ South Lyon, MI 49999
+drink: Iced Tea
+description: Hiker, biker
+title: Director, Embedded Systems
+postaladdress: Info Tech Division $ 535 W. William St. $ Ann Arbor, MI 48103
+mail: bjorn@mailgw.example.com
+homephone: +1 313 555 5444
+pager: +1 313 555 4474
+facsimiletelephonenumber: +1 313 555 2177
+telephonenumber: +1 313 555 0355
+
+dn: cn=Dorothy Stevens,ou=Alumni Association,ou=People,o=University of Michiga
+ n,c=TEST
+objectclass: OpenLDAPperson
+cn: Dorothy Stevens
+cn: Dot Stevens
+sn: Stevens
+uid: dots
+title: Secretary, UM Alumni Association
+postaladdress: Alumni Association $ 111 Maple St $ Ann Arbor, MI 48109
+seealso: cn=All Staff,ou=Groups,o=University of Michigan,c=TEST
+drink: Lemonade
+homepostaladdress: 377 White St. Apt. 3 $ Ann Arbor, MI 48104
+description: Very tall
+facsimiletelephonenumber: +1 313 555 3223
+telephonenumber: +1 313 555 3664
+mail: dots@mail.alumni.example.com
+homephone: +1 313 555 0454
+
+dn: cn=ITD Staff,ou=Groups,o=University of Michigan,c=TEST
+owner: cn=Manager,o=University of Michigan,c=TEST
+description: All ITD Staff
+cn: ITD Staff
+objectclass: groupofuniquenames
+uniquemember: cn=Manager,o=University of Michigan,c=TEST
+uniquemember: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,o=U
+ niversity of Michigan,c=TEST
+uniquemember: cn=James A Jones 2,ou=Information Technology Division,ou=People,
+ o=University of Michigan,c=TEST
+uniquemember: cn=John Doe,ou=Information Technology Division,ou=People,o=Unive
+ rsity of Michigan,c=TEST
+
+dn: cn=James A Jones 1,ou=Alumni Association,ou=People,o=University of Michiga
+ n,c=TEST
+objectclass: OpenLDAPperson
+cn: James A Jones 1
+cn: James Jones
+cn: Jim Jones
+sn: Jones
+uid: jaj
+postaladdress: Alumni Association $ 111 Maple St $ Ann Arbor, MI 48109
+seealso: cn=All Staff,ou=Groups,o=University of Michigan,c=TEST
+userpassword:: amFq
+homepostaladdress: 3882 Beverly Rd. $ Ann Arbor, MI 48105
+homephone: +1 313 555 4772
+description: Outstanding
+title: Mad Cow Researcher, UM Alumni Association
+pager: +1 313 555 3923
+mail: jaj@mail.alumni.example.com
+facsimiletelephonenumber: +1 313 555 4332
+telephonenumber: +1 313 555 0895
+
+dn: cn=James A Jones 2,ou=Information Technology Division,ou=People,o=Universi
+ ty of Michigan,c=TEST
+objectclass: OpenLDAPperson
+cn: James A Jones 2
+cn: James Jones
+cn: Jim Jones
+sn: Doe
+uid: jjones
+seealso: cn=All Staff,ou=Groups,o=University of Michigan,c=TEST
+homepostaladdress: 933 Brooks $ Ann Arbor, MI 48104
+homephone: +1 313 555 8838
+title: Senior Manager, Information Technology Division
+description: Not around very much
+mail: jjones@mailgw.example.com
+postaladdress: Info Tech Division $ 535 W William $ Ann Arbor, MI 48103
+pager: +1 313 555 2833
+facsimiletelephonenumber: +1 313 555 8688
+telephonenumber: +1 313 555 7334
+
+dn: cn=Jane Doe,ou=Alumni Association,ou=People,o=University of Michigan,c=TEST
+objectclass: OpenLDAPperson
+cn: Jane Doe
+cn: Jane Alverson
+sn: Doe
+uid: jdoe
+title: Programmer Analyst, UM Alumni Association
+postaladdress: Alumni Association $ 111 Maple St $ Ann Arbor, MI 48109
+seealso: cn=All Staff,ou=Groups,o=University of Michigan,c=TEST
+homepostaladdress: 123 Anystreet $ Ann Arbor, MI 48104
+drink: diet coke
+description: Enthusiastic
+mail: jdoe@woof.net
+homephone: +1 313 555 5445
+pager: +1 313 555 1220
+facsimiletelephonenumber: +1 313 555 2311
+telephonenumber: +1 313 555 4774
+
+dn: cn=Jennifer Smith,ou=Alumni Association,ou=People,o=University of Michigan
+ ,c=TEST
+objectclass: OpenLDAPperson
+cn: Jennifer Smith
+cn: Jen Smith
+sn: Smith
+uid: jen
+postaladdress: Alumni Association $ 111 Maple St $ Ann Arbor, MI 48109
+seealso: cn=All Staff,ou=Groups,o=University of Michigan,c=TEST
+drink: Sam Adams
+homepostaladdress: 1000 Maple #44 $ Ann Arbor, MI 48103
+title: Telemarketer, UM Alumni Association
+mail: jen@mail.alumni.example.com
+homephone: +1 313 555 2333
+pager: +1 313 555 6442
+facsimiletelephonenumber: +1 313 555 2756
+telephonenumber: +1 313 555 8232
+
+dn: cn=John Doe,ou=Information Technology Division,ou=People,o=University of M
+ ichigan,c=TEST
+objectclass: OpenLDAPperson
+cn: John Doe
+cn: Jonathon Doe
+sn: Doe
+uid: johnd
+postaladdress: ITD $ 535 W. William $ Ann Arbor, MI 48109
+seealso: cn=All Staff,ou=Groups,o=University of Michigan,c=TEST
+homepostaladdress: 912 East Bllvd $ Ann Arbor, MI 48104
+title: System Administrator, Information Technology Division
+description: overworked!
+mail: johnd@mailgw.example.com
+homephone: +1 313 555 3774
+pager: +1 313 555 6573
+facsimiletelephonenumber: +1 313 555 4544
+telephonenumber: +1 313 555 9394
+
+dn: cn=Manager,o=University of Michigan,c=TEST
+objectclass: person
+cn: Manager
+cn: Directory Manager
+cn: Dir Man
+sn: Manager
+description: Manager of the directory
+userpassword:: c2VjcmV0
+
+dn: cn=Mark Elliot,ou=Alumni Association,ou=People,o=University of Michigan,c=
+ TEST
+objectclass: OpenLDAPperson
+cn: Mark Elliot
+cn: Mark A Elliot
+sn: Elliot
+uid: melliot
+postaladdress: Alumni Association $ 111 Maple St $ Ann Arbor, MI 48109
+seealso: cn=All Staff,ou=Groups,o=University of Michigan,c=TEST
+homepostaladdress: 199 Outer Drive $ Ypsilanti, MI 48198
+homephone: +1 313 555 0388
+drink: Gasoline
+title: Director, UM Alumni Association
+mail: melliot@mail.alumni.example.com
+pager: +1 313 555 7671
+facsimiletelephonenumber: +1 313 555 7762
+telephonenumber: +1 313 555 4177
+
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,o=University of Michiga
+ n,c=TEST
+objectclass: OpenLDAPperson
+cn: Ursula Hampster
+sn: Hampster
+uid: uham
+title: Secretary, UM Alumni Association
+postaladdress: Alumni Association $ 111 Maple St $ Ann Arbor, MI 48109
+seealso: cn=All Staff,ou=Groups,o=University of Michigan,c=TEST
+homepostaladdress: 123 Anystreet $ Ann Arbor, MI 48104
+mail: uham@mail.alumni.example.com
+description: a long attribute name, longer than 128 bytes so that we
+ trigger sign extension problems in tdb_pack, no thats not long enough
+ yet, maybe this is. I'll just keep going till it triggers the error
+homephone: +1 313 555 8421
+pager: +1 313 555 2844
+facsimiletelephonenumber: +1 313 555 9700
+telephonenumber: +1 313 555 5331
diff --git a/source4/lib/ldb/tests/testdata.txt b/source4/lib/ldb/tests/testdata.txt
new file mode 100644
index 0000000000..dadb9f0f98
--- /dev/null
+++ b/source4/lib/ldb/tests/testdata.txt
@@ -0,0 +1,8 @@
+foo=bar5
+(&(|(a=b)(c=d))(e=f))
+(&(|(a=b)(c=d)(g=h))(e=f))
+name=firstname lastname
+(&(sid=S-1-2-3)(name = fred bloggs))
+(&(|(a=b)(c=d))(g=f))
+(&(sid=S-1-2-3)(!(name = fred bloggs)))
+(&(!(|(a=b)(c=d))(g=f)))
diff --git a/source4/lib/ldb/tests/testsearch.txt b/source4/lib/ldb/tests/testsearch.txt
new file mode 100644
index 0000000000..c5738639b7
--- /dev/null
+++ b/source4/lib/ldb/tests/testsearch.txt
@@ -0,0 +1,5 @@
+(blah=foo)
+(objectclass=person)
+(dn=*)
+(&(objectclass=person)(objectclass=person))
+(&(objectclass=person)(objectclass=personx))
diff --git a/source4/lib/ldb/tools/cmdline.c b/source4/lib/ldb/tools/cmdline.c
new file mode 100644
index 0000000000..2701de5a48
--- /dev/null
+++ b/source4/lib/ldb/tools/cmdline.c
@@ -0,0 +1,398 @@
+/*
+ ldb database library - command line handling for ldb tools
+
+ Copyright (C) Andrew Tridgell 2005
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "ldb_includes.h"
+#include "ldb.h"
+#include "tools/cmdline.h"
+
+#if (_SAMBA_BUILD_ >= 4)
+#include "includes.h"
+#include "lib/cmdline/popt_common.h"
+#include "lib/ldb-samba/ldif_handlers.h"
+#include "auth/gensec/gensec.h"
+#include "auth/auth.h"
+#include "ldb_wrap.h"
+#include "param/param.h"
+#endif
+
+
+
+/**
+ process command line options
+*/
+struct ldb_cmdline *ldb_cmdline_process(struct ldb_context *ldb,
+ int argc, const char **argv,
+ void (*usage)(void))
+{
+ static struct ldb_cmdline options; /* needs to be static for older compilers */
+ struct ldb_cmdline *ret=NULL;
+ poptContext pc;
+#if (_SAMBA_BUILD_ >= 4)
+ int r;
+#endif
+ int num_options = 0;
+ int opt;
+ int flags = 0;
+
+ struct poptOption popt_options[] = {
+ POPT_AUTOHELP
+ { "url", 'H', POPT_ARG_STRING, &options.url, 0, "database URL", "URL" },
+ { "basedn", 'b', POPT_ARG_STRING, &options.basedn, 0, "base DN", "DN" },
+ { "editor", 'e', POPT_ARG_STRING, &options.editor, 0, "external editor", "PROGRAM" },
+ { "scope", 's', POPT_ARG_STRING, NULL, 's', "search scope", "SCOPE" },
+ { "verbose", 'v', POPT_ARG_NONE, NULL, 'v', "increase verbosity", NULL },
+ { "interactive", 'i', POPT_ARG_NONE, &options.interactive, 0, "input from stdin", NULL },
+ { "recursive", 'r', POPT_ARG_NONE, &options.recursive, 0, "recursive delete", NULL },
+ { "modules-path", 0, POPT_ARG_STRING, &options.modules_path, 0, "modules path", "PATH" },
+ { "num-searches", 0, POPT_ARG_INT, &options.num_searches, 0, "number of test searches", NULL },
+ { "num-records", 0, POPT_ARG_INT, &options.num_records, 0, "number of test records", NULL },
+ { "all", 'a', POPT_ARG_NONE, &options.all_records, 0, "(|(objectClass=*)(distinguishedName=*))", NULL },
+ { "nosync", 0, POPT_ARG_NONE, &options.nosync, 0, "non-synchronous transactions", NULL },
+ { "sorted", 'S', POPT_ARG_NONE, &options.sorted, 0, "sort attributes", NULL },
+ { "input", 'I', POPT_ARG_STRING, &options.input, 0, "Input File", "Input" },
+ { "output", 'O', POPT_ARG_STRING, &options.output, 0, "Output File", "Output" },
+ { NULL, 'o', POPT_ARG_STRING, NULL, 'o', "ldb_connect option", "OPTION" },
+ { "controls", 0, POPT_ARG_STRING, NULL, 'c', "controls", NULL },
+#if (_SAMBA_BUILD_ >= 4)
+ POPT_COMMON_SAMBA
+ POPT_COMMON_CREDENTIALS
+ POPT_COMMON_CONNECTION
+ POPT_COMMON_VERSION
+#endif
+ { NULL }
+ };
+
+#if (_SAMBA_BUILD_ >= 4)
+ r = ldb_register_samba_handlers(ldb);
+ if (r != 0) {
+ goto failed;
+ }
+
+#endif
+
+ ret = talloc_zero(ldb, struct ldb_cmdline);
+ if (ret == NULL) {
+ fprintf(stderr, "Out of memory!\n");
+ goto failed;
+ }
+
+ options = *ret;
+
+ /* pull in URL */
+ options.url = getenv("LDB_URL");
+
+ /* and editor (used by ldbedit) */
+ options.editor = getenv("VISUAL");
+ if (!options.editor) {
+ options.editor = getenv("EDITOR");
+ }
+ if (!options.editor) {
+ options.editor = "vi";
+ }
+
+ options.scope = LDB_SCOPE_DEFAULT;
+
+ pc = poptGetContext(argv[0], argc, argv, popt_options,
+ POPT_CONTEXT_KEEP_FIRST);
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case 's': {
+ const char *arg = poptGetOptArg(pc);
+ if (strcmp(arg, "base") == 0) {
+ options.scope = LDB_SCOPE_BASE;
+ } else if (strcmp(arg, "sub") == 0) {
+ options.scope = LDB_SCOPE_SUBTREE;
+ } else if (strcmp(arg, "one") == 0) {
+ options.scope = LDB_SCOPE_ONELEVEL;
+ } else {
+ fprintf(stderr, "Invalid scope '%s'\n", arg);
+ goto failed;
+ }
+ break;
+ }
+
+ case 'v':
+ options.verbose++;
+ break;
+
+ case 'o':
+ options.options = talloc_realloc(ret, options.options,
+ const char *, num_options+3);
+ if (options.options == NULL) {
+ fprintf(stderr, "Out of memory!\n");
+ goto failed;
+ }
+ options.options[num_options] = poptGetOptArg(pc);
+ options.options[num_options+1] = NULL;
+ num_options++;
+ break;
+
+ case 'c': {
+ const char *cs = poptGetOptArg(pc);
+ const char *p, *q;
+ int cc;
+
+ for (p = cs, cc = 1; (q = strchr(p, ',')); cc++, p = q + 1) ;
+
+ options.controls = talloc_array(ret, char *, cc + 1);
+ if (options.controls == NULL) {
+ fprintf(stderr, "Out of memory!\n");
+ goto failed;
+ }
+ for (p = cs, cc = 0; p != NULL; cc++) {
+ const char *t;
+
+ t = strchr(p, ',');
+ if (t == NULL) {
+ options.controls[cc] = talloc_strdup(options.controls, p);
+ p = NULL;
+ } else {
+ options.controls[cc] = talloc_strndup(options.controls, p, t-p);
+ p = t + 1;
+ }
+ }
+ options.controls[cc] = NULL;
+
+ break;
+ }
+ default:
+ fprintf(stderr, "Invalid option %s: %s\n",
+ poptBadOption(pc, 0), poptStrerror(opt));
+ if (usage) usage();
+ goto failed;
+ }
+ }
+
+ /* setup the remaining options for the main program to use */
+ options.argv = poptGetArgs(pc);
+ if (options.argv) {
+ options.argv++;
+ while (options.argv[options.argc]) options.argc++;
+ }
+
+ *ret = options;
+
+ /* all utils need some option */
+ if (ret->url == NULL) {
+ fprintf(stderr, "You must supply a url with -H or with $LDB_URL\n");
+ if (usage) usage();
+ goto failed;
+ }
+
+ if (strcmp(ret->url, "NONE") == 0) {
+ return ret;
+ }
+
+ if (options.nosync) {
+ flags |= LDB_FLG_NOSYNC;
+ }
+
+#if (_SAMBA_BUILD_ >= 4)
+ /* Must be after we have processed command line options */
+ gensec_init(cmdline_lp_ctx);
+
+ if (ldb_set_opaque(ldb, "sessionInfo", system_session(ldb, cmdline_lp_ctx))) {
+ goto failed;
+ }
+ if (ldb_set_opaque(ldb, "credentials", cmdline_credentials)) {
+ goto failed;
+ }
+ if (ldb_set_opaque(ldb, "loadparm", cmdline_lp_ctx)) {
+ goto failed;
+ }
+
+ ldb_set_utf8_fns(ldb, NULL, wrap_casefold);
+#endif
+
+ if (options.modules_path != NULL) {
+ ldb_set_modules_dir(ldb, options.modules_path);
+ } else if (getenv("LDB_MODULES_PATH") != NULL) {
+ ldb_set_modules_dir(ldb, getenv("LDB_MODULES_PATH"));
+ }
+
+ /* now connect to the ldb */
+ if (ldb_connect(ldb, ret->url, flags, ret->options) != 0) {
+ fprintf(stderr, "Failed to connect to %s - %s\n",
+ ret->url, ldb_errstring(ldb));
+ goto failed;
+ }
+
+ return ret;
+
+failed:
+ talloc_free(ret);
+ exit(1);
+ return NULL;
+}
+
+/* this function check controls reply and determines if more
+ * processing is needed setting up the request controls correctly
+ *
+ * returns:
+ * -1 error
+ * 0 all ok
+ * 1 all ok, more processing required
+ */
+int handle_controls_reply(struct ldb_control **reply, struct ldb_control **request)
+{
+ int i, j;
+ int ret = 0;
+
+ if (reply == NULL || request == NULL) return -1;
+
+ for (i = 0; reply[i]; i++) {
+ if (strcmp(LDB_CONTROL_VLV_RESP_OID, reply[i]->oid) == 0) {
+ struct ldb_vlv_resp_control *rep_control;
+
+ rep_control = talloc_get_type(reply[i]->data, struct ldb_vlv_resp_control);
+
+ /* check we have a matching control in the request */
+ for (j = 0; request[j]; j++) {
+ if (strcmp(LDB_CONTROL_VLV_REQ_OID, request[j]->oid) == 0)
+ break;
+ }
+ if (! request[j]) {
+ fprintf(stderr, "Warning VLV reply received but no request have been made\n");
+ continue;
+ }
+
+ /* check the result */
+ if (rep_control->vlv_result != 0) {
+ fprintf(stderr, "Warning: VLV not performed with error: %d\n", rep_control->vlv_result);
+ } else {
+ fprintf(stderr, "VLV Info: target position = %d, content count = %d\n", rep_control->targetPosition, rep_control->contentCount);
+ }
+
+ continue;
+ }
+
+ if (strcmp(LDB_CONTROL_ASQ_OID, reply[i]->oid) == 0) {
+ struct ldb_asq_control *rep_control;
+
+ rep_control = talloc_get_type(reply[i]->data, struct ldb_asq_control);
+
+ /* check the result */
+ if (rep_control->result != 0) {
+ fprintf(stderr, "Warning: ASQ not performed with error: %d\n", rep_control->result);
+ }
+
+ continue;
+ }
+
+ if (strcmp(LDB_CONTROL_PAGED_RESULTS_OID, reply[i]->oid) == 0) {
+ struct ldb_paged_control *rep_control, *req_control;
+
+ rep_control = talloc_get_type(reply[i]->data, struct ldb_paged_control);
+ if (rep_control->cookie_len == 0) /* we are done */
+ break;
+
+ /* more processing required */
+ /* let's fill in the request control with the new cookie */
+
+ for (j = 0; request[j]; j++) {
+ if (strcmp(LDB_CONTROL_PAGED_RESULTS_OID, request[j]->oid) == 0)
+ break;
+ }
+ /* if there's a reply control we must find a request
+ * control matching it */
+ if (! request[j]) return -1;
+
+ req_control = talloc_get_type(request[j]->data, struct ldb_paged_control);
+
+ if (req_control->cookie)
+ talloc_free(req_control->cookie);
+ req_control->cookie = (char *)talloc_memdup(
+ req_control, rep_control->cookie,
+ rep_control->cookie_len);
+ req_control->cookie_len = rep_control->cookie_len;
+
+ ret = 1;
+
+ continue;
+ }
+
+ if (strcmp(LDB_CONTROL_SORT_RESP_OID, reply[i]->oid) == 0) {
+ struct ldb_sort_resp_control *rep_control;
+
+ rep_control = talloc_get_type(reply[i]->data, struct ldb_sort_resp_control);
+
+ /* check we have a matching control in the request */
+ for (j = 0; request[j]; j++) {
+ if (strcmp(LDB_CONTROL_SERVER_SORT_OID, request[j]->oid) == 0)
+ break;
+ }
+ if (! request[j]) {
+ fprintf(stderr, "Warning Server Sort reply received but no request found\n");
+ continue;
+ }
+
+ /* check the result */
+ if (rep_control->result != 0) {
+ fprintf(stderr, "Warning: Sorting not performed with error: %d\n", rep_control->result);
+ }
+
+ continue;
+ }
+
+ if (strcmp(LDB_CONTROL_DIRSYNC_OID, reply[i]->oid) == 0) {
+ struct ldb_dirsync_control *rep_control, *req_control;
+ char *cookie;
+
+ rep_control = talloc_get_type(reply[i]->data, struct ldb_dirsync_control);
+ if (rep_control->cookie_len == 0) /* we are done */
+ break;
+
+ /* more processing required */
+ /* let's fill in the request control with the new cookie */
+
+ for (j = 0; request[j]; j++) {
+ if (strcmp(LDB_CONTROL_DIRSYNC_OID, request[j]->oid) == 0)
+ break;
+ }
+ /* if there's a reply control we must find a request
+ * control matching it */
+ if (! request[j]) return -1;
+
+ req_control = talloc_get_type(request[j]->data, struct ldb_dirsync_control);
+
+ if (req_control->cookie)
+ talloc_free(req_control->cookie);
+ req_control->cookie = (char *)talloc_memdup(
+ req_control, rep_control->cookie,
+ rep_control->cookie_len);
+ req_control->cookie_len = rep_control->cookie_len;
+
+ cookie = ldb_base64_encode(req_control, rep_control->cookie, rep_control->cookie_len);
+ printf("# DIRSYNC cookie returned was:\n# %s\n", cookie);
+
+ continue;
+ }
+
+ /* no controls matched, throw a warning */
+ fprintf(stderr, "Unknown reply control oid: %s\n", reply[i]->oid);
+ }
+
+ return ret;
+}
+
diff --git a/source4/lib/ldb/tools/cmdline.h b/source4/lib/ldb/tools/cmdline.h
new file mode 100644
index 0000000000..45619ce496
--- /dev/null
+++ b/source4/lib/ldb/tools/cmdline.h
@@ -0,0 +1,53 @@
+/*
+ ldb database library - command line handling for ldb tools
+
+ Copyright (C) Andrew Tridgell 2005
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <popt.h>
+
+struct ldb_cmdline {
+ const char *url;
+ enum ldb_scope scope;
+ const char *basedn;
+ const char *modules_path;
+ int interactive;
+ int sorted;
+ const char *editor;
+ int verbose;
+ int recursive;
+ int all_records;
+ int nosync;
+ const char **options;
+ int argc;
+ const char **argv;
+ int num_records;
+ int num_searches;
+ const char *sasl_mechanism;
+ const char *input;
+ const char *output;
+ char **controls;
+};
+
+struct ldb_cmdline *ldb_cmdline_process(struct ldb_context *ldb, int argc, const char **argv,
+ void (*usage)(void));
+
+
+int handle_controls_reply(struct ldb_control **reply, struct ldb_control **request);
diff --git a/source4/lib/ldb/tools/config.mk b/source4/lib/ldb/tools/config.mk
new file mode 100644
index 0000000000..6b57929df0
--- /dev/null
+++ b/source4/lib/ldb/tools/config.mk
@@ -0,0 +1,90 @@
+################################################
+# Start SUBSYSTEM LIBLDB_CMDLINE
+[SUBSYSTEM::LIBLDB_CMDLINE]
+CFLAGS = -I$(ldbsrcdir) -I$(ldbsrcdir)/include
+PUBLIC_DEPENDENCIES = LIBLDB LIBPOPT
+PRIVATE_DEPENDENCIES = LIBSAMBA-UTIL POPT_SAMBA POPT_CREDENTIALS gensec
+# End SUBSYSTEM LIBLDB_CMDLINE
+################################################
+
+LIBLDB_CMDLINE_OBJ_FILES = $(ldbsrcdir)/tools/cmdline.o
+
+################################################
+# Start BINARY ldbadd
+[BINARY::ldbadd]
+INSTALLDIR = BINDIR
+PRIVATE_DEPENDENCIES = \
+ LIBLDB_CMDLINE LIBCLI_RESOLVE
+# End BINARY ldbadd
+################################################
+
+
+ldbadd_OBJ_FILES = $(ldbsrcdir)/tools/ldbadd.o
+
+MANPAGES += $(ldbsrcdir)/man/ldbadd.1
+
+################################################
+# Start BINARY ldbdel
+[BINARY::ldbdel]
+INSTALLDIR = BINDIR
+PRIVATE_DEPENDENCIES = \
+ LIBLDB_CMDLINE
+# End BINARY ldbdel
+################################################
+
+ldbdel_OBJ_FILES = $(ldbsrcdir)/tools/ldbdel.o
+
+MANPAGES += $(ldbsrcdir)/man/ldbdel.1
+
+################################################
+# Start BINARY ldbmodify
+[BINARY::ldbmodify]
+INSTALLDIR = BINDIR
+PRIVATE_DEPENDENCIES = \
+ LIBLDB_CMDLINE
+# End BINARY ldbmodify
+################################################
+
+ldbmodify_OBJ_FILES = $(ldbsrcdir)/tools/ldbmodify.o
+MANPAGES += $(ldbsrcdir)/man/ldbmodify.1
+
+################################################
+# Start BINARY ldbsearch
+[BINARY::ldbsearch]
+INSTALLDIR = BINDIR
+PRIVATE_DEPENDENCIES = \
+ LIBLDB_CMDLINE
+# End BINARY ldbsearch
+################################################
+
+ldbsearch_OBJ_FILES = $(ldbsrcdir)/tools/ldbsearch.o
+
+MANPAGES += $(ldbsrcdir)/man/ldbsearch.1
+
+################################################
+# Start BINARY ldbedit
+[BINARY::ldbedit]
+INSTALLDIR = BINDIR
+PRIVATE_DEPENDENCIES = \
+ LIBLDB_CMDLINE
+# End BINARY ldbedit
+################################################
+
+ldbedit_OBJ_FILES = $(ldbsrcdir)/tools/ldbedit.o
+
+MANPAGES += $(ldbsrcdir)/man/ldbedit.1
+
+################################################
+# Start BINARY ldbrename
+[BINARY::ldbrename]
+INSTALLDIR = BINDIR
+PRIVATE_DEPENDENCIES = \
+ LIBLDB_CMDLINE
+# End BINARY ldbrename
+################################################
+
+ldbrename_OBJ_FILES = $(ldbsrcdir)/tools/ldbrename.o
+
+MANPAGES += $(ldbsrcdir)/man/ldbrename.1
+
+
diff --git a/source4/lib/ldb/tools/ldbadd.c b/source4/lib/ldb/tools/ldbadd.c
new file mode 100644
index 0000000000..be02334797
--- /dev/null
+++ b/source4/lib/ldb/tools/ldbadd.c
@@ -0,0 +1,126 @@
+/*
+ ldb database library
+
+ Copyright (C) Andrew Tridgell 2004
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldbadd
+ *
+ * Description: utility to add records - modelled on ldapadd
+ *
+ * Author: Andrew Tridgell
+ */
+
+#include "ldb.h"
+#include "tools/cmdline.h"
+
+static int failures;
+
+static void usage(void)
+{
+ printf("Usage: ldbadd <options> <ldif...>\n");
+ printf("Options:\n");
+ printf(" -H ldb_url choose the database (or $LDB_URL)\n");
+ printf(" -o options pass options like modules to activate\n");
+ printf(" e.g: -o modules:timestamps\n");
+ printf("\n");
+ printf("Adds records to a ldb, reading ldif the specified list of files\n\n");
+ exit(1);
+}
+
+
+/*
+ add records from an opened file
+*/
+static int process_file(struct ldb_context *ldb, FILE *f, int *count)
+{
+ struct ldb_ldif *ldif;
+ int ret = LDB_SUCCESS;
+
+ while ((ldif = ldb_ldif_read_file(ldb, f))) {
+ if (ldif->changetype != LDB_CHANGETYPE_ADD &&
+ ldif->changetype != LDB_CHANGETYPE_NONE) {
+ fprintf(stderr, "Only CHANGETYPE_ADD records allowed\n");
+ break;
+ }
+
+ ldif->msg = ldb_msg_canonicalize(ldb, ldif->msg);
+
+ ret = ldb_add(ldb, ldif->msg);
+ if (ret != LDB_SUCCESS) {
+ fprintf(stderr, "ERR: \"%s\" on DN %s\n",
+ ldb_errstring(ldb), ldb_dn_get_linearized(ldif->msg->dn));
+ failures++;
+ } else {
+ (*count)++;
+ }
+ ldb_ldif_read_free(ldb, ldif);
+ }
+
+ return ret;
+}
+
+
+
+int main(int argc, const char **argv)
+{
+ struct ldb_context *ldb;
+ int i, ret=0, count=0;
+ struct ldb_cmdline *options;
+
+ ldb = ldb_init(NULL, NULL);
+
+ options = ldb_cmdline_process(ldb, argc, argv, usage);
+
+ if (ldb_transaction_start(ldb) != 0) {
+ printf("Failed to start transaction: %s\n", ldb_errstring(ldb));
+ exit(1);
+ }
+
+ if (options->argc == 0) {
+ ret = process_file(ldb, stdin, &count);
+ } else {
+ for (i=0;i<options->argc;i++) {
+ const char *fname = options->argv[i];
+ FILE *f;
+ f = fopen(fname, "r");
+ if (!f) {
+ perror(fname);
+ exit(1);
+ }
+ ret = process_file(ldb, f, &count);
+ fclose(f);
+ }
+ }
+
+ if (count != 0 && ldb_transaction_commit(ldb) != 0) {
+ printf("Failed to commit transaction: %s\n", ldb_errstring(ldb));
+ exit(1);
+ }
+
+ talloc_free(ldb);
+
+ printf("Added %d records with %d failures\n", count, failures);
+
+ return ret;
+}
diff --git a/source4/lib/ldb/tools/ldbdel.c b/source4/lib/ldb/tools/ldbdel.c
new file mode 100644
index 0000000000..232f51681a
--- /dev/null
+++ b/source4/lib/ldb/tools/ldbdel.c
@@ -0,0 +1,115 @@
+/*
+ ldb database library
+
+ Copyright (C) Andrew Tridgell 2004
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldbdel
+ *
+ * Description: utility to delete records - modelled on ldapdelete
+ *
+ * Author: Andrew Tridgell
+ */
+
+#include "ldb.h"
+#include "tools/cmdline.h"
+
+static int ldb_delete_recursive(struct ldb_context *ldb, struct ldb_dn *dn)
+{
+ int ret, i, total=0;
+ const char *attrs[] = { NULL };
+ struct ldb_result *res;
+
+ ret = ldb_search(ldb, ldb, &res, dn, LDB_SCOPE_SUBTREE, attrs, "distinguishedName=*");
+ if (ret != LDB_SUCCESS) return -1;
+
+ for (i = 0; i < res->count; i++) {
+ if (ldb_delete(ldb, res->msgs[i]->dn) == 0) {
+ total++;
+ }
+ }
+
+ talloc_free(res);
+
+ if (total == 0) {
+ return -1;
+ }
+ printf("Deleted %d records\n", total);
+ return 0;
+}
+
+static void usage(void)
+{
+ printf("Usage: ldbdel <options> <DN...>\n");
+ printf("Options:\n");
+ printf(" -r recursively delete the given subtree\n");
+ printf(" -H ldb_url choose the database (or $LDB_URL)\n");
+ printf(" -o options pass options like modules to activate\n");
+ printf(" e.g: -o modules:timestamps\n");
+ printf("\n");
+ printf("Deletes records from a ldb\n\n");
+ exit(1);
+}
+
+int main(int argc, const char **argv)
+{
+ struct ldb_context *ldb;
+ int ret = 0, i;
+ struct ldb_cmdline *options;
+
+ ldb = ldb_init(NULL, NULL);
+
+ options = ldb_cmdline_process(ldb, argc, argv, usage);
+
+ if (options->argc < 1) {
+ usage();
+ exit(1);
+ }
+
+ for (i=0;i<options->argc;i++) {
+ struct ldb_dn *dn;
+
+ dn = ldb_dn_new(ldb, ldb, options->argv[i]);
+ if ( ! ldb_dn_validate(dn)) {
+ printf("Invalid DN format\n");
+ exit(1);
+ }
+ if (options->recursive) {
+ ret = ldb_delete_recursive(ldb, dn);
+ } else {
+ ret = ldb_delete(ldb, dn);
+ if (ret == 0) {
+ printf("Deleted 1 record\n");
+ }
+ }
+ if (ret != 0) {
+ printf("delete of '%s' failed - %s\n",
+ ldb_dn_get_linearized(dn),
+ ldb_errstring(ldb));
+ }
+ }
+
+ talloc_free(ldb);
+
+ return ret;
+}
diff --git a/source4/lib/ldb/tools/ldbedit.c b/source4/lib/ldb/tools/ldbedit.c
new file mode 100644
index 0000000000..9d3bd27983
--- /dev/null
+++ b/source4/lib/ldb/tools/ldbedit.c
@@ -0,0 +1,339 @@
+/*
+ ldb database library
+
+ Copyright (C) Andrew Tridgell 2004
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldbedit
+ *
+ * Description: utility for ldb database editing
+ *
+ * Author: Andrew Tridgell
+ */
+#include "ldb_includes.h"
+#include "ldb.h"
+#include "tools/cmdline.h"
+
+static struct ldb_cmdline *options;
+
+/*
+ debug routine
+*/
+static void ldif_write_msg(struct ldb_context *ldb,
+ FILE *f,
+ enum ldb_changetype changetype,
+ struct ldb_message *msg)
+{
+ struct ldb_ldif ldif;
+ ldif.changetype = changetype;
+ ldif.msg = msg;
+ ldb_ldif_write_file(ldb, f, &ldif);
+}
+
+/*
+ modify a database record so msg1 becomes msg2
+ returns the number of modified elements
+*/
+static int modify_record(struct ldb_context *ldb,
+ struct ldb_message *msg1,
+ struct ldb_message *msg2)
+{
+ struct ldb_message *mod;
+
+ mod = ldb_msg_diff(ldb, msg1, msg2);
+ if (mod == NULL) {
+ fprintf(stderr, "Failed to calculate message differences\n");
+ return -1;
+ }
+
+ if (mod->num_elements == 0) {
+ return 0;
+ }
+
+ if (options->verbose > 0) {
+ ldif_write_msg(ldb, stdout, LDB_CHANGETYPE_MODIFY, mod);
+ }
+
+ if (ldb_modify(ldb, mod) != 0) {
+ fprintf(stderr, "failed to modify %s - %s\n",
+ ldb_dn_get_linearized(msg1->dn), ldb_errstring(ldb));
+ return -1;
+ }
+
+ return mod->num_elements;
+}
+
+/*
+ find dn in msgs[]
+*/
+static struct ldb_message *msg_find(struct ldb_context *ldb,
+ struct ldb_message **msgs,
+ int count,
+ struct ldb_dn *dn)
+{
+ int i;
+ for (i=0;i<count;i++) {
+ if (ldb_dn_compare(dn, msgs[i]->dn) == 0) {
+ return msgs[i];
+ }
+ }
+ return NULL;
+}
+
+/*
+ merge the changes in msgs2 into the messages from msgs1
+*/
+static int merge_edits(struct ldb_context *ldb,
+ struct ldb_message **msgs1, int count1,
+ struct ldb_message **msgs2, int count2)
+{
+ int i;
+ struct ldb_message *msg;
+ int ret = 0;
+ int adds=0, modifies=0, deletes=0;
+
+ if (ldb_transaction_start(ldb) != 0) {
+ fprintf(stderr, "Failed to start transaction: %s\n", ldb_errstring(ldb));
+ return -1;
+ }
+
+ /* do the adds and modifies */
+ for (i=0;i<count2;i++) {
+ msg = msg_find(ldb, msgs1, count1, msgs2[i]->dn);
+ if (!msg) {
+ if (options->verbose > 0) {
+ ldif_write_msg(ldb, stdout, LDB_CHANGETYPE_ADD, msgs2[i]);
+ }
+ if (ldb_add(ldb, msgs2[i]) != 0) {
+ fprintf(stderr, "failed to add %s - %s\n",
+ ldb_dn_get_linearized(msgs2[i]->dn),
+ ldb_errstring(ldb));
+ return -1;
+ }
+ adds++;
+ } else {
+ if (modify_record(ldb, msg, msgs2[i]) > 0) {
+ modifies++;
+ }
+ }
+ }
+
+ /* do the deletes */
+ for (i=0;i<count1;i++) {
+ msg = msg_find(ldb, msgs2, count2, msgs1[i]->dn);
+ if (!msg) {
+ if (options->verbose > 0) {
+ ldif_write_msg(ldb, stdout, LDB_CHANGETYPE_DELETE, msgs1[i]);
+ }
+ if (ldb_delete(ldb, msgs1[i]->dn) != 0) {
+ fprintf(stderr, "failed to delete %s - %s\n",
+ ldb_dn_get_linearized(msgs1[i]->dn),
+ ldb_errstring(ldb));
+ return -1;
+ }
+ deletes++;
+ }
+ }
+
+ if (ldb_transaction_commit(ldb) != 0) {
+ fprintf(stderr, "Failed to commit transaction: %s\n", ldb_errstring(ldb));
+ return -1;
+ }
+
+ printf("# %d adds %d modifies %d deletes\n", adds, modifies, deletes);
+
+ return ret;
+}
+
+/*
+ save a set of messages as ldif to a file
+*/
+static int save_ldif(struct ldb_context *ldb,
+ FILE *f, struct ldb_message **msgs, int count)
+{
+ int i;
+
+ fprintf(f, "# editing %d records\n", count);
+
+ for (i=0;i<count;i++) {
+ struct ldb_ldif ldif;
+ fprintf(f, "# record %d\n", i+1);
+
+ ldif.changetype = LDB_CHANGETYPE_NONE;
+ ldif.msg = msgs[i];
+
+ ldb_ldif_write_file(ldb, f, &ldif);
+ }
+
+ return 0;
+}
+
+
+/*
+ edit the ldb search results in msgs using the user selected editor
+*/
+static int do_edit(struct ldb_context *ldb, struct ldb_message **msgs1, int count1,
+ const char *editor)
+{
+ int fd, ret;
+ FILE *f;
+ char file_template[] = "/tmp/ldbedit.XXXXXX";
+ char *cmd;
+ struct ldb_ldif *ldif;
+ struct ldb_message **msgs2 = NULL;
+ int count2 = 0;
+
+ /* write out the original set of messages to a temporary
+ file */
+ fd = mkstemp(file_template);
+
+ if (fd == -1) {
+ perror(file_template);
+ return -1;
+ }
+
+ f = fdopen(fd, "r+");
+
+ if (!f) {
+ perror("fopen");
+ close(fd);
+ unlink(file_template);
+ return -1;
+ }
+
+ if (save_ldif(ldb, f, msgs1, count1) != 0) {
+ return -1;
+ }
+
+ fclose(f);
+
+ cmd = talloc_asprintf(ldb, "%s %s", editor, file_template);
+
+ if (!cmd) {
+ unlink(file_template);
+ fprintf(stderr, "out of memory\n");
+ return -1;
+ }
+
+ /* run the editor */
+ ret = system(cmd);
+ talloc_free(cmd);
+
+ if (ret != 0) {
+ unlink(file_template);
+ fprintf(stderr, "edit with %s failed\n", editor);
+ return -1;
+ }
+
+ /* read the resulting ldif into msgs2 */
+ f = fopen(file_template, "r");
+ if (!f) {
+ perror(file_template);
+ return -1;
+ }
+
+ while ((ldif = ldb_ldif_read_file(ldb, f))) {
+ msgs2 = talloc_realloc(ldb, msgs2, struct ldb_message *, count2+1);
+ if (!msgs2) {
+ fprintf(stderr, "out of memory");
+ return -1;
+ }
+ msgs2[count2++] = ldif->msg;
+ }
+
+ fclose(f);
+ unlink(file_template);
+
+ return merge_edits(ldb, msgs1, count1, msgs2, count2);
+}
+
+static void usage(void)
+{
+ printf("Usage: ldbedit <options> <expression> <attributes ...>\n");
+ printf("Options:\n");
+ printf(" -H ldb_url choose the database (or $LDB_URL)\n");
+ printf(" -s base|sub|one choose search scope\n");
+ printf(" -b basedn choose baseDN\n");
+ printf(" -a edit all records (expression 'objectclass=*')\n");
+ printf(" -e editor choose editor (or $VISUAL or $EDITOR)\n");
+ printf(" -v verbose mode\n");
+ exit(1);
+}
+
+int main(int argc, const char **argv)
+{
+ struct ldb_context *ldb;
+ struct ldb_result *result = NULL;
+ struct ldb_dn *basedn = NULL;
+ int ret;
+ const char *expression = "(|(objectClass=*)(distinguishedName=*))";
+ const char * const * attrs = NULL;
+
+ ldb = ldb_init(NULL, NULL);
+
+ options = ldb_cmdline_process(ldb, argc, argv, usage);
+
+ /* the check for '=' is for compatibility with ldapsearch */
+ if (options->argc > 0 &&
+ strchr(options->argv[0], '=')) {
+ expression = options->argv[0];
+ options->argv++;
+ options->argc--;
+ }
+
+ if (options->argc > 0) {
+ attrs = (const char * const *)(options->argv);
+ }
+
+ if (options->basedn != NULL) {
+ basedn = ldb_dn_new(ldb, ldb, options->basedn);
+ if ( ! ldb_dn_validate(basedn)) {
+ printf("Invalid Base DN format\n");
+ exit(1);
+ }
+ }
+
+ ret = ldb_search(ldb, ldb, &result, basedn, options->scope, attrs, "%s", expression);
+ if (ret != LDB_SUCCESS) {
+ printf("search failed - %s\n", ldb_errstring(ldb));
+ exit(1);
+ }
+
+ if (result->count == 0) {
+ printf("no matching records - cannot edit\n");
+ return 0;
+ }
+
+ do_edit(ldb, result->msgs, result->count, options->editor);
+
+ if (result) {
+ ret = talloc_free(result);
+ if (ret == -1) {
+ fprintf(stderr, "talloc_free failed\n");
+ exit(1);
+ }
+ }
+
+ talloc_free(ldb);
+ return 0;
+}
diff --git a/source4/lib/ldb/tools/ldbmodify.c b/source4/lib/ldb/tools/ldbmodify.c
new file mode 100644
index 0000000000..c3f55c6096
--- /dev/null
+++ b/source4/lib/ldb/tools/ldbmodify.c
@@ -0,0 +1,116 @@
+/*
+ ldb database library
+
+ Copyright (C) Andrew Tridgell 2004
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldbmodify
+ *
+ * Description: utility to modify records - modelled on ldapmodify
+ *
+ * Author: Andrew Tridgell
+ */
+
+#include "ldb.h"
+#include "tools/cmdline.h"
+
+static int failures;
+
+static void usage(void)
+{
+ printf("Usage: ldbmodify <options> <ldif...>\n");
+ printf("Options:\n");
+ printf(" -H ldb_url choose the database (or $LDB_URL)\n");
+ printf(" -o options pass options like modules to activate\n");
+ printf(" e.g: -o modules:timestamps\n");
+ printf("\n");
+ printf("Modifies a ldb based upon ldif change records\n\n");
+ exit(1);
+}
+
+/*
+ process modifies for one file
+*/
+static int process_file(struct ldb_context *ldb, FILE *f, int *count)
+{
+ struct ldb_ldif *ldif;
+ int ret = LDB_SUCCESS;
+
+ while ((ldif = ldb_ldif_read_file(ldb, f))) {
+ switch (ldif->changetype) {
+ case LDB_CHANGETYPE_NONE:
+ case LDB_CHANGETYPE_ADD:
+ ret = ldb_add(ldb, ldif->msg);
+ break;
+ case LDB_CHANGETYPE_DELETE:
+ ret = ldb_delete(ldb, ldif->msg->dn);
+ break;
+ case LDB_CHANGETYPE_MODIFY:
+ ret = ldb_modify(ldb, ldif->msg);
+ break;
+ }
+ if (ret != LDB_SUCCESS) {
+ fprintf(stderr, "ERR: \"%s\" on DN %s\n",
+ ldb_errstring(ldb), ldb_dn_get_linearized(ldif->msg->dn));
+ failures++;
+ } else {
+ (*count)++;
+ }
+ ldb_ldif_read_free(ldb, ldif);
+ }
+
+ return ret;
+}
+
+int main(int argc, const char **argv)
+{
+ struct ldb_context *ldb;
+ int count=0;
+ int i, ret=LDB_SUCCESS;
+ struct ldb_cmdline *options;
+
+ ldb = ldb_init(NULL, NULL);
+
+ options = ldb_cmdline_process(ldb, argc, argv, usage);
+
+ if (options->argc == 0) {
+ ret = process_file(ldb, stdin, &count);
+ } else {
+ for (i=0;i<options->argc;i++) {
+ const char *fname = options->argv[i];
+ FILE *f;
+ f = fopen(fname, "r");
+ if (!f) {
+ perror(fname);
+ exit(1);
+ }
+ ret = process_file(ldb, f, &count);
+ }
+ }
+
+ talloc_free(ldb);
+
+ printf("Modified %d records with %d failures\n", count, failures);
+
+ return ret;
+}
diff --git a/source4/lib/ldb/tools/ldbrename.c b/source4/lib/ldb/tools/ldbrename.c
new file mode 100644
index 0000000000..01ed3d9835
--- /dev/null
+++ b/source4/lib/ldb/tools/ldbrename.c
@@ -0,0 +1,90 @@
+/*
+ ldb database library
+
+ Copyright (C) Andrew Tridgell 2004
+ Copyright (C) Stefan Metzmacher 2004
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldbrename
+ *
+ * Description: utility to rename records - modelled on ldapmodrdn
+ *
+ * Author: Andrew Tridgell
+ * Author: Stefan Metzmacher
+ */
+
+#include "ldb.h"
+#include "tools/cmdline.h"
+
+static void usage(void)
+{
+ printf("Usage: ldbrename [<options>] <olddn> <newdn>\n");
+ printf("Options:\n");
+ printf(" -H ldb_url choose the database (or $LDB_URL)\n");
+ printf(" -o options pass options like modules to activate\n");
+ printf(" e.g: -o modules:timestamps\n");
+ printf("\n");
+ printf("Renames records in a ldb\n\n");
+ exit(1);
+}
+
+
+int main(int argc, const char **argv)
+{
+ struct ldb_context *ldb;
+ int ret;
+ struct ldb_cmdline *options;
+ struct ldb_dn *dn1, *dn2;
+
+ ldb = ldb_init(NULL, NULL);
+
+ options = ldb_cmdline_process(ldb, argc, argv, usage);
+
+ if (options->argc < 2) {
+ usage();
+ }
+
+ dn1 = ldb_dn_new(ldb, ldb, options->argv[0]);
+ dn2 = ldb_dn_new(ldb, ldb, options->argv[1]);
+
+ if ( ! ldb_dn_validate(dn1)) {
+ printf("Invalid DN1: %s\n", options->argv[0]);
+ return -1;
+ }
+ if ( ! ldb_dn_validate(dn2)) {
+ printf("Invalid DN2: %s\n", options->argv[1]);
+ return -1;
+ }
+
+ ret = ldb_rename(ldb, dn1, dn2);
+ if (ret == 0) {
+ printf("Renamed 1 record\n");
+ } else {
+ printf("rename of '%s' to '%s' failed - %s\n",
+ options->argv[0], options->argv[1], ldb_errstring(ldb));
+ }
+
+ talloc_free(ldb);
+
+ return ret;
+}
diff --git a/source4/lib/ldb/tools/ldbsearch.c b/source4/lib/ldb/tools/ldbsearch.c
new file mode 100644
index 0000000000..ba0a2a8927
--- /dev/null
+++ b/source4/lib/ldb/tools/ldbsearch.c
@@ -0,0 +1,329 @@
+/*
+ ldb database library
+
+ Copyright (C) Andrew Tridgell 2004
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldbsearch
+ *
+ * Description: utility for ldb search - modelled on ldapsearch
+ *
+ * Author: Andrew Tridgell
+ */
+
+#include "ldb_includes.h"
+#include "ldb.h"
+#include "tools/cmdline.h"
+
+static void usage(void)
+{
+ printf("Usage: ldbsearch <options> <expression> <attrs...>\n");
+ printf("Options:\n");
+ printf(" -H ldb_url choose the database (or $LDB_URL)\n");
+ printf(" -s base|sub|one choose search scope\n");
+ printf(" -b basedn choose baseDN\n");
+ printf(" -i read search expressions from stdin\n");
+ printf(" -S sort returned attributes\n");
+ printf(" -o options pass options like modules to activate\n");
+ printf(" e.g: -o modules:timestamps\n");
+ exit(1);
+}
+
+static int do_compare_msg(struct ldb_message **el1,
+ struct ldb_message **el2,
+ void *opaque)
+{
+ return ldb_dn_compare((*el1)->dn, (*el2)->dn);
+}
+
+struct search_context {
+ struct ldb_context *ldb;
+ struct ldb_control **req_ctrls;
+
+ int sort;
+ int num_stored;
+ struct ldb_message **store;
+ int refs_stored;
+ char **refs_store;
+
+ int entries;
+ int refs;
+
+ int pending;
+ int status;
+};
+
+static int store_message(struct ldb_message *msg, struct search_context *sctx) {
+
+ sctx->store = talloc_realloc(sctx, sctx->store, struct ldb_message *, sctx->num_stored + 2);
+ if (!sctx->store) {
+ fprintf(stderr, "talloc_realloc failed while storing messages\n");
+ return -1;
+ }
+
+ sctx->store[sctx->num_stored] = talloc_move(sctx->store, &msg);
+ sctx->num_stored++;
+ sctx->store[sctx->num_stored] = NULL;
+
+ return 0;
+}
+
+static int store_referral(char *referral, struct search_context *sctx) {
+
+ sctx->refs_store = talloc_realloc(sctx, sctx->refs_store, char *, sctx->refs_stored + 2);
+ if (!sctx->refs_store) {
+ fprintf(stderr, "talloc_realloc failed while storing referrals\n");
+ return -1;
+ }
+
+ sctx->refs_store[sctx->refs_stored] = talloc_move(sctx->refs_store, &referral);
+ sctx->refs_stored++;
+ sctx->refs_store[sctx->refs_stored] = NULL;
+
+ return 0;
+}
+
+static int display_message(struct ldb_message *msg, struct search_context *sctx) {
+ struct ldb_ldif ldif;
+
+ sctx->entries++;
+ printf("# record %d\n", sctx->entries);
+
+ ldif.changetype = LDB_CHANGETYPE_NONE;
+ ldif.msg = msg;
+
+ if (sctx->sort) {
+ /*
+ * Ensure attributes are always returned in the same
+ * order. For testing, this makes comparison of old
+ * vs. new much easier.
+ */
+ ldb_msg_sort_elements(ldif.msg);
+ }
+
+ ldb_ldif_write_file(sctx->ldb, stdout, &ldif);
+
+ return 0;
+}
+
+static int display_referral(char *referral, struct search_context *sctx)
+{
+
+ sctx->refs++;
+ printf("# Referral\nref: %s\n\n", referral);
+
+ return 0;
+}
+
+static int search_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+ struct search_context *sctx;
+ int ret;
+
+ sctx = talloc_get_type(req->context, struct search_context);
+
+ 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:
+ if (sctx->sort) {
+ ret = store_message(ares->message, sctx);
+ } else {
+ ret = display_message(ares->message, sctx);
+ }
+ break;
+
+ case LDB_REPLY_REFERRAL:
+ if (sctx->sort) {
+ ret = store_referral(ares->referral, sctx);
+ } else {
+ ret = display_referral(ares->referral, sctx);
+ }
+ if (ret) {
+ return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
+ }
+ break;
+
+ case LDB_REPLY_DONE:
+ if (ares->controls) {
+ if (handle_controls_reply(ares->controls, sctx->req_ctrls) == 1)
+ sctx->pending = 1;
+ }
+ talloc_free(ares);
+ return ldb_request_done(req, LDB_SUCCESS);
+ }
+
+ talloc_free(ares);
+ if (ret) {
+ return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ return LDB_SUCCESS;
+}
+
+static int do_search(struct ldb_context *ldb,
+ struct ldb_dn *basedn,
+ struct ldb_cmdline *options,
+ const char *expression,
+ const char * const *attrs)
+{
+ struct ldb_request *req;
+ struct search_context *sctx;
+ int ret;
+
+ req = NULL;
+
+ sctx = talloc(ldb, struct search_context);
+ if (!sctx) return -1;
+
+ sctx->ldb = ldb;
+ sctx->sort = options->sorted;
+ sctx->num_stored = 0;
+ sctx->refs_stored = 0;
+ sctx->store = NULL;
+ sctx->req_ctrls = ldb_parse_control_strings(ldb, sctx, (const char **)options->controls);
+ if (options->controls != NULL && sctx->req_ctrls== NULL) {
+ printf("parsing controls failed: %s\n", ldb_errstring(ldb));
+ return -1;
+ }
+ sctx->entries = 0;
+ sctx->refs = 0;
+
+ if (basedn == NULL) {
+ basedn = ldb_get_default_basedn(ldb);
+ }
+
+again:
+ /* free any previous requests */
+ if (req) talloc_free(req);
+
+ ret = ldb_build_search_req(&req, ldb, ldb,
+ basedn, options->scope,
+ expression, attrs,
+ sctx->req_ctrls,
+ sctx, search_callback,
+ NULL);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(sctx);
+ printf("allocating request failed: %s\n", ldb_errstring(ldb));
+ return -1;
+ }
+
+ sctx->pending = 0;
+
+ ret = ldb_request(ldb, req);
+ if (ret != LDB_SUCCESS) {
+ printf("search failed - %s\n", ldb_errstring(ldb));
+ return -1;
+ }
+
+ ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+ if (ret != LDB_SUCCESS) {
+ printf("search error - %s\n", ldb_errstring(ldb));
+ return -1;
+ }
+
+ if (sctx->pending)
+ goto again;
+
+ if (sctx->sort && (sctx->num_stored != 0 || sctx->refs != 0)) {
+ int i;
+
+ if (sctx->num_stored) {
+ ldb_qsort(sctx->store, sctx->num_stored, sizeof(struct ldb_message *),
+ ldb, (ldb_qsort_cmp_fn_t)do_compare_msg);
+ }
+ for (i = 0; i < sctx->num_stored; i++) {
+ display_message(sctx->store[i], sctx);
+ }
+
+ for (i = 0; i < sctx->refs_stored; i++) {
+ display_referral(sctx->refs_store[i], sctx);
+ }
+ }
+
+ printf("# returned %d records\n# %d entries\n# %d referrals\n",
+ sctx->entries + sctx->refs, sctx->entries, sctx->refs);
+
+ talloc_free(sctx);
+ talloc_free(req);
+
+ return 0;
+}
+
+int main(int argc, const char **argv)
+{
+ struct ldb_context *ldb;
+ struct ldb_dn *basedn = NULL;
+ const char * const * attrs = NULL;
+ struct ldb_cmdline *options;
+ int ret = -1;
+ const char *expression = "(|(objectClass=*)(distinguishedName=*))";
+
+ ldb = ldb_init(NULL, NULL);
+ if (ldb == NULL) {
+ return -1;
+ }
+
+ options = ldb_cmdline_process(ldb, argc, argv, usage);
+
+ /* the check for '=' is for compatibility with ldapsearch */
+ if (!options->interactive &&
+ options->argc > 0 &&
+ strchr(options->argv[0], '=')) {
+ expression = options->argv[0];
+ options->argv++;
+ options->argc--;
+ }
+
+ if (options->argc > 0) {
+ attrs = (const char * const *)(options->argv);
+ }
+
+ if (options->basedn != NULL) {
+ basedn = ldb_dn_new(ldb, ldb, options->basedn);
+ if ( ! ldb_dn_validate(basedn)) {
+ fprintf(stderr, "Invalid Base DN format\n");
+ exit(1);
+ }
+ }
+
+ if (options->interactive) {
+ char line[1024];
+ while (fgets(line, sizeof(line), stdin)) {
+ if (do_search(ldb, basedn, options, line, attrs) == -1) {
+ ret = -1;
+ }
+ }
+ } else {
+ ret = do_search(ldb, basedn, options, expression, attrs);
+ }
+
+ talloc_free(ldb);
+ return ret;
+}
diff --git a/source4/lib/ldb/tools/ldbtest.c b/source4/lib/ldb/tools/ldbtest.c
new file mode 100644
index 0000000000..6af0ee9336
--- /dev/null
+++ b/source4/lib/ldb/tools/ldbtest.c
@@ -0,0 +1,419 @@
+/*
+ ldb database library
+
+ Copyright (C) Andrew Tridgell 2004
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldbtest
+ *
+ * Description: utility to test ldb
+ *
+ * Author: Andrew Tridgell
+ */
+
+#include "ldb_includes.h"
+#include "ldb.h"
+#include "tools/cmdline.h"
+
+static struct timeval tp1,tp2;
+static struct ldb_cmdline *options;
+
+static void _start_timer(void)
+{
+ gettimeofday(&tp1,NULL);
+}
+
+static double _end_timer(void)
+{
+ gettimeofday(&tp2,NULL);
+ return((tp2.tv_sec - tp1.tv_sec) +
+ (tp2.tv_usec - tp1.tv_usec)*1.0e-6);
+}
+
+static void add_records(struct ldb_context *ldb,
+ struct ldb_dn *basedn,
+ int count)
+{
+ struct ldb_message msg;
+ int i;
+
+#if 0
+ if (ldb_lock(ldb, "transaction") != 0) {
+ printf("transaction lock failed\n");
+ exit(1);
+ }
+#endif
+ for (i=0;i<count;i++) {
+ struct ldb_message_element el[6];
+ struct ldb_val vals[6][1];
+ char *name;
+ TALLOC_CTX *tmp_ctx = talloc_new(ldb);
+
+ name = talloc_asprintf(tmp_ctx, "Test%d", i);
+
+ msg.dn = ldb_dn_copy(tmp_ctx, basedn);
+ ldb_dn_add_child_fmt(msg.dn, "cn=%s", name);
+ msg.num_elements = 6;
+ msg.elements = el;
+
+ el[0].flags = 0;
+ el[0].name = talloc_strdup(tmp_ctx, "cn");
+ el[0].num_values = 1;
+ el[0].values = vals[0];
+ vals[0][0].data = (uint8_t *)name;
+ vals[0][0].length = strlen(name);
+
+ el[1].flags = 0;
+ el[1].name = "title";
+ el[1].num_values = 1;
+ el[1].values = vals[1];
+ vals[1][0].data = (uint8_t *)talloc_asprintf(tmp_ctx, "The title of %s", name);
+ vals[1][0].length = strlen((char *)vals[1][0].data);
+
+ el[2].flags = 0;
+ el[2].name = talloc_strdup(tmp_ctx, "uid");
+ el[2].num_values = 1;
+ el[2].values = vals[2];
+ vals[2][0].data = (uint8_t *)ldb_casefold(ldb, tmp_ctx, name, strlen(name));
+ vals[2][0].length = strlen((char *)vals[2][0].data);
+
+ el[3].flags = 0;
+ el[3].name = talloc_strdup(tmp_ctx, "mail");
+ el[3].num_values = 1;
+ el[3].values = vals[3];
+ vals[3][0].data = (uint8_t *)talloc_asprintf(tmp_ctx, "%s@example.com", name);
+ vals[3][0].length = strlen((char *)vals[3][0].data);
+
+ el[4].flags = 0;
+ el[4].name = talloc_strdup(tmp_ctx, "objectClass");
+ el[4].num_values = 1;
+ el[4].values = vals[4];
+ vals[4][0].data = (uint8_t *)talloc_strdup(tmp_ctx, "OpenLDAPperson");
+ vals[4][0].length = strlen((char *)vals[4][0].data);
+
+ el[5].flags = 0;
+ el[5].name = talloc_strdup(tmp_ctx, "sn");
+ el[5].num_values = 1;
+ el[5].values = vals[5];
+ vals[5][0].data = (uint8_t *)name;
+ vals[5][0].length = strlen((char *)vals[5][0].data);
+
+ ldb_delete(ldb, msg.dn);
+
+ if (ldb_add(ldb, &msg) != 0) {
+ printf("Add of %s failed - %s\n", name, ldb_errstring(ldb));
+ exit(1);
+ }
+
+ printf("adding uid %s\r", name);
+ fflush(stdout);
+
+ talloc_free(tmp_ctx);
+ }
+#if 0
+ if (ldb_unlock(ldb, "transaction") != 0) {
+ printf("transaction unlock failed\n");
+ exit(1);
+ }
+#endif
+ printf("\n");
+}
+
+static void modify_records(struct ldb_context *ldb,
+ struct ldb_dn *basedn,
+ int count)
+{
+ struct ldb_message msg;
+ int i;
+
+ for (i=0;i<count;i++) {
+ struct ldb_message_element el[3];
+ struct ldb_val vals[3];
+ char *name;
+ TALLOC_CTX *tmp_ctx = talloc_new(ldb);
+
+ name = talloc_asprintf(tmp_ctx, "Test%d", i);
+ msg.dn = ldb_dn_copy(tmp_ctx, basedn);
+ ldb_dn_add_child_fmt(msg.dn, "cn=%s", name);
+
+ msg.num_elements = 3;
+ msg.elements = el;
+
+ el[0].flags = LDB_FLAG_MOD_DELETE;
+ el[0].name = talloc_strdup(tmp_ctx, "mail");
+ el[0].num_values = 0;
+
+ el[1].flags = LDB_FLAG_MOD_ADD;
+ el[1].name = talloc_strdup(tmp_ctx, "mail");
+ el[1].num_values = 1;
+ el[1].values = &vals[1];
+ vals[1].data = (uint8_t *)talloc_asprintf(tmp_ctx, "%s@other.example.com", name);
+ vals[1].length = strlen((char *)vals[1].data);
+
+ el[2].flags = LDB_FLAG_MOD_REPLACE;
+ el[2].name = talloc_strdup(tmp_ctx, "mail");
+ el[2].num_values = 1;
+ el[2].values = &vals[2];
+ vals[2].data = (uint8_t *)talloc_asprintf(tmp_ctx, "%s@other2.example.com", name);
+ vals[2].length = strlen((char *)vals[2].data);
+
+ if (ldb_modify(ldb, &msg) != 0) {
+ printf("Modify of %s failed - %s\n", name, ldb_errstring(ldb));
+ exit(1);
+ }
+
+ printf("Modifying uid %s\r", name);
+ fflush(stdout);
+
+ talloc_free(tmp_ctx);
+ }
+
+ printf("\n");
+}
+
+
+static void delete_records(struct ldb_context *ldb,
+ struct ldb_dn *basedn,
+ int count)
+{
+ int i;
+
+ for (i=0;i<count;i++) {
+ struct ldb_dn *dn;
+ char *name = talloc_asprintf(ldb, "Test%d", i);
+ dn = ldb_dn_copy(name, basedn);
+ ldb_dn_add_child_fmt(dn, "cn=%s", name);
+
+ printf("Deleting uid Test%d\r", i);
+ fflush(stdout);
+
+ if (ldb_delete(ldb, dn) != 0) {
+ printf("Delete of %s failed - %s\n", ldb_dn_get_linearized(dn), ldb_errstring(ldb));
+ exit(1);
+ }
+ talloc_free(name);
+ }
+
+ printf("\n");
+}
+
+static void search_uid(struct ldb_context *ldb, struct ldb_dn *basedn, int nrecords, int nsearches)
+{
+ int i;
+
+ for (i=0;i<nsearches;i++) {
+ int uid = (i * 700 + 17) % (nrecords * 2);
+ char *expr;
+ struct ldb_result *res = NULL;
+ int ret;
+
+ expr = talloc_asprintf(ldb, "(uid=TEST%d)", uid);
+ ret = ldb_search(ldb, ldb, &res, basedn, LDB_SCOPE_SUBTREE, NULL, "%s", expr);
+
+ if (ret != LDB_SUCCESS || (uid < nrecords && res->count != 1)) {
+ printf("Failed to find %s - %s\n", expr, ldb_errstring(ldb));
+ exit(1);
+ }
+
+ if (uid >= nrecords && res->count > 0) {
+ printf("Found %s !? - %d\n", expr, ret);
+ exit(1);
+ }
+
+ printf("testing uid %d/%d - %d \r", i, uid, res->count);
+ fflush(stdout);
+
+ talloc_free(res);
+ talloc_free(expr);
+ }
+
+ printf("\n");
+}
+
+static void start_test(struct ldb_context *ldb, int nrecords, int nsearches)
+{
+ struct ldb_dn *basedn;
+
+ basedn = ldb_dn_new(ldb, ldb, options->basedn);
+ if ( ! ldb_dn_validate(basedn)) {
+ printf("Invalid base DN\n");
+ exit(1);
+ }
+
+ printf("Adding %d records\n", nrecords);
+ add_records(ldb, basedn, nrecords);
+
+ printf("Starting search on uid\n");
+ _start_timer();
+ search_uid(ldb, basedn, nrecords, nsearches);
+ printf("uid search took %.2f seconds\n", _end_timer());
+
+ printf("Modifying records\n");
+ modify_records(ldb, basedn, nrecords);
+
+ printf("Deleting records\n");
+ delete_records(ldb, basedn, nrecords);
+}
+
+
+/*
+ 2) Store an @indexlist record
+
+ 3) Store a record that contains fields that should be index according
+to @index
+
+ 4) disconnection from database
+
+ 5) connect to same database
+
+ 6) search for record added in step 3 using a search key that should
+be indexed
+*/
+static void start_test_index(struct ldb_context **ldb)
+{
+ struct ldb_message *msg;
+ struct ldb_result *res = NULL;
+ struct ldb_dn *indexlist;
+ struct ldb_dn *basedn;
+ int ret;
+ int flags = 0;
+ const char *specials;
+
+ specials = getenv("LDB_SPECIALS");
+ if (specials && atoi(specials) == 0) {
+ printf("LDB_SPECIALS disabled - skipping index test\n");
+ return;
+ }
+
+ if (options->nosync) {
+ flags |= LDB_FLG_NOSYNC;
+ }
+
+ printf("Starting index test\n");
+
+ indexlist = ldb_dn_new(*ldb, *ldb, "@INDEXLIST");
+
+ ldb_delete(*ldb, indexlist);
+
+ msg = ldb_msg_new(NULL);
+
+ msg->dn = indexlist;
+ ldb_msg_add_string(msg, "@IDXATTR", strdup("uid"));
+
+ if (ldb_add(*ldb, msg) != 0) {
+ printf("Add of %s failed - %s\n", ldb_dn_get_linearized(msg->dn), ldb_errstring(*ldb));
+ exit(1);
+ }
+
+ basedn = ldb_dn_new(*ldb, *ldb, options->basedn);
+
+ memset(msg, 0, sizeof(*msg));
+ msg->dn = ldb_dn_copy(msg, basedn);
+ ldb_dn_add_child_fmt(msg->dn, "cn=test");
+ ldb_msg_add_string(msg, "cn", strdup("test"));
+ ldb_msg_add_string(msg, "sn", strdup("test"));
+ ldb_msg_add_string(msg, "uid", strdup("test"));
+ ldb_msg_add_string(msg, "objectClass", strdup("OpenLDAPperson"));
+
+ if (ldb_add(*ldb, msg) != 0) {
+ printf("Add of %s failed - %s\n", ldb_dn_get_linearized(msg->dn), ldb_errstring(*ldb));
+ exit(1);
+ }
+
+ if (talloc_free(*ldb) != 0) {
+ printf("failed to free/close ldb database");
+ exit(1);
+ }
+
+ (*ldb) = ldb_init(options, NULL);
+
+ ret = ldb_connect(*ldb, options->url, flags, NULL);
+ if (ret != 0) {
+ printf("failed to connect to %s\n", options->url);
+ exit(1);
+ }
+
+ basedn = ldb_dn_new(*ldb, *ldb, options->basedn);
+
+ ret = ldb_search(*ldb, *ldb, &res, basedn, LDB_SCOPE_SUBTREE, NULL, "uid=test");
+ if (ret != LDB_SUCCESS) {
+ printf("Search with (uid=test) filter failed!\n");
+ exit(1);
+ }
+ if(res->count != 1) {
+ printf("Should have found 1 record - found %d\n", res->count);
+ exit(1);
+ }
+
+ indexlist = ldb_dn_new(*ldb, *ldb, "@INDEXLIST");
+
+ if (ldb_delete(*ldb, msg->dn) != 0 ||
+ ldb_delete(*ldb, indexlist) != 0) {
+ printf("cleanup failed - %s\n", ldb_errstring(*ldb));
+ exit(1);
+ }
+
+ printf("Finished index test\n");
+}
+
+
+static void usage(void)
+{
+ printf("Usage: ldbtest <options>\n");
+ printf("Options:\n");
+ printf(" -H ldb_url choose the database (or $LDB_URL)\n");
+ printf(" --num-records nrecords database size to use\n");
+ printf(" --num-searches nsearches number of searches to do\n");
+ printf("\n");
+ printf("tests ldb API\n\n");
+ exit(1);
+}
+
+int main(int argc, const char **argv)
+{
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+ struct ldb_context *ldb;
+
+ ldb = ldb_init(mem_ctx, NULL);
+
+ options = ldb_cmdline_process(ldb, argc, argv, usage);
+
+ talloc_steal(mem_ctx, options);
+
+ if (options->basedn == NULL) {
+ options->basedn = "ou=Ldb Test,ou=People,o=University of Michigan,c=TEST";
+ }
+
+ srandom(1);
+
+ printf("Testing with num-records=%d and num-searches=%d\n",
+ options->num_records, options->num_searches);
+
+ start_test(ldb, options->num_records, options->num_searches);
+
+ start_test_index(&ldb);
+
+ talloc_free(mem_ctx);
+
+ return 0;
+}
diff --git a/source4/lib/ldb/web/index.html b/source4/lib/ldb/web/index.html
new file mode 100644
index 0000000000..26dd344527
--- /dev/null
+++ b/source4/lib/ldb/web/index.html
@@ -0,0 +1,86 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<HTML>
+<HEAD>
+<TITLE>ldb</TITLE>
+</HEAD>
+<BODY BGCOLOR="#ffffff" TEXT="#000000" VLINK="#292555" LINK="#292555" ALINK="#cc0033">
+
+<h1>ldb</h1>
+
+ldb is a LDAP-like embedded database. It is not at all
+<a href="http://en.wikipedia.org/wiki/Lightweight_Directory_Access_Protocol">LDAP</a>
+standards compliant, so if you want a standards compliant database then please
+see the excellent <a href="http://www.openldap.org/">OpenLDAP</a>
+project.<p>
+
+What ldb does is provide a fast database with an LDAP-like API
+designed to be used within an application. In some ways it can be seen
+as a intermediate solution between key-value pair databases and a real
+LDAP database.<p>
+
+ldb is the database engine used in Samba4.
+
+<h2>Features</h2>
+
+The main features that separate ldb from other solutions are:
+
+<ul>
+<li>Safe multi-reader, multi-writer, using byte range locking
+<li><a href="http://en.wikipedia.org/wiki/LDAP_Application_Program_Interface">LDAP-like API</a>
+<li>fast operation
+<li>choice of local tdb or remote LDAP backends
+<li>integration with <a href="http://talloc.samba.org">talloc</a>
+<li>schema-less operation, for trivial setup
+<li>modules for extensions (such as schema support)
+<li>easy setup of indexes and attribute properties
+<li><a href="http://en.wikipedia.org/wiki/LDAP_Data_Interchange_Format">LDIF</a> for import/export
+<li>ldbedit tool for database (via LDIF) editing (reminiscent of 'vipw')
+</ul>
+
+<h2>Documentation</h2>
+
+Currently ldb is completely lacking in programmer or user
+documentation. This is your opportunity to make a contribution! Start
+with the public functions declared in <a
+href="http://samba.org/ftp/unpacked/ldb/include/ldb.h">ldb.h</a>
+and the example code in the <a
+href="http://samba.org/ftp/unpacked/ldb/tools/">tools
+directory</a>. Documentation in the same docbook format used by Samba
+would be preferred.
+
+<h2>Discussion and bug reports</h2>
+
+ldb does not currently have its own mailing list or bug tracking
+system. For now, please use the <a
+href="https://lists.samba.org/mailman/listinfo/samba-technical">samba-technical</a>
+mailing list or the <a href="https://lists.samba.org/mailman/listinfo/ldb">ldb</a>
+mailing list, and the <a href="http://bugzilla.samba.org/">Samba bugzilla</a> bug tracking system.
+
+<h2>Download</h2>
+
+You can download the latest release either via rsync or thtough git.<br>
+<br>
+To fetch via git see the following guide:<br>
+<a href="http://wiki.samba.org/index.php/Using_Git_for_Samba_Development">Using Git for Samba Development</a><br>
+Once you have cloned the tree switch to the v4-0-test branch and cd into the source/lib/ldb directory.<br>
+<br>
+To fetch via rsync use these commands:
+
+<pre>
+ rsync -Pavz samba.org::ftp/unpacked/ldb .
+ rsync -Pavz samba.org::ftp/unpacked/tdb .
+ rsync -Pavz samba.org::ftp/unpacked/talloc .
+ rsync -Pavz samba.org::ftp/unpacked/libreplace .
+</pre>
+
+and build in ldb. It will find the other libraries in the directory
+above automatically.
+
+<hr>
+<tiny>
+<a href="http://samba.org/~tridge/">Andrew Tridgell</a><br>
+ldb AT tridgell.net
+</tiny>
+
+</BODY>
+</HTML>
diff --git a/source4/lib/ldb_wrap.c b/source4/lib/ldb_wrap.c
new file mode 100644
index 0000000000..15cf11f942
--- /dev/null
+++ b/source4/lib/ldb_wrap.c
@@ -0,0 +1,192 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ LDB wrap functions
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ the stupidity of the unix fcntl locking design forces us to never
+ allow a database file to be opened twice in the same process. These
+ wrappers provide convenient access to a tdb or ldb, taking advantage
+ of talloc destructors to ensure that only a single open is done
+*/
+
+#include "includes.h"
+#include "lib/events/events.h"
+#include "lib/ldb/include/ldb.h"
+#include "lib/ldb/include/ldb_errors.h"
+#include "lib/ldb-samba/ldif_handlers.h"
+#include "ldb_wrap.h"
+#include "dsdb/samdb/samdb.h"
+#include "param/param.h"
+
+/*
+ this is used to catch debug messages from ldb
+*/
+static void ldb_wrap_debug(void *context, enum ldb_debug_level level,
+ const char *fmt, va_list ap) PRINTF_ATTRIBUTE(3,0);
+
+static void ldb_wrap_debug(void *context, enum ldb_debug_level level,
+ const char *fmt, va_list ap)
+{
+ int samba_level = -1;
+ char *s = NULL;
+ switch (level) {
+ case LDB_DEBUG_FATAL:
+ samba_level = 0;
+ break;
+ case LDB_DEBUG_ERROR:
+ samba_level = 1;
+ break;
+ case LDB_DEBUG_WARNING:
+ samba_level = 2;
+ break;
+ case LDB_DEBUG_TRACE:
+ samba_level = 5;
+ break;
+
+ };
+ vasprintf(&s, fmt, ap);
+ if (!s) return;
+ DEBUG(samba_level, ("ldb: %s\n", s));
+ free(s);
+}
+
+/* check for memory leaks on the ldb context */
+static int ldb_wrap_destructor(struct ldb_context *ldb)
+{
+ size_t *startup_blocks = (size_t *)ldb_get_opaque(ldb, "startup_blocks");
+
+ if (startup_blocks &&
+ talloc_total_blocks(ldb) > *startup_blocks + 400) {
+ DEBUG(0,("WARNING: probable memory leak in ldb %s - %lu blocks (startup %lu) %lu bytes\n",
+ (char *)ldb_get_opaque(ldb, "wrap_url"),
+ (unsigned long)talloc_total_blocks(ldb),
+ (unsigned long)*startup_blocks,
+ (unsigned long)talloc_total_size(ldb)));
+#if 0
+ talloc_report_full(ldb, stdout);
+ call_backtrace();
+ smb_panic("probable memory leak in ldb");
+#endif
+ }
+ return 0;
+}
+
+/*
+ wrapped connection to a ldb database
+ to close just talloc_free() the returned ldb_context
+
+ TODO: We need an error_string parameter
+ */
+struct ldb_context *ldb_wrap_connect(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct loadparm_context *lp_ctx,
+ const char *url,
+ struct auth_session_info *session_info,
+ struct cli_credentials *credentials,
+ unsigned int flags,
+ const char *options[])
+{
+ struct ldb_context *ldb;
+ int ret;
+ char *real_url = NULL;
+ size_t *startup_blocks;
+
+ /* we want to use the existing event context if possible. This
+ relies on the fact that in smbd, everything is a child of
+ the main event_context */
+ if (ev == NULL) {
+ return NULL;
+ }
+
+ ldb = ldb_init(mem_ctx, ev);
+ if (ldb == NULL) {
+ return NULL;
+ }
+
+ ldb_set_modules_dir(ldb,
+ talloc_asprintf(ldb,
+ "%s/ldb",
+ lp_modulesdir(lp_ctx)));
+
+ if (ldb_set_opaque(ldb, "sessionInfo", session_info)) {
+ talloc_free(ldb);
+ return NULL;
+ }
+
+ if (ldb_set_opaque(ldb, "credentials", credentials)) {
+ talloc_free(ldb);
+ return NULL;
+ }
+
+ if (ldb_set_opaque(ldb, "loadparm", lp_ctx)) {
+ talloc_free(ldb);
+ return NULL;
+ }
+
+ /* This must be done before we load the schema, as these
+ * handlers for objectSid and objectGUID etc must take
+ * precedence over the 'binary attribute' declaration in the
+ * schema */
+ ret = ldb_register_samba_handlers(ldb);
+ if (ret == -1) {
+ talloc_free(ldb);
+ return NULL;
+ }
+
+ if (lp_ctx != NULL && strcmp(lp_sam_url(lp_ctx), url) == 0) {
+ dsdb_set_global_schema(ldb);
+ }
+
+ ldb_set_debug(ldb, ldb_wrap_debug, NULL);
+
+ ldb_set_utf8_fns(ldb, NULL, wrap_casefold);
+
+ real_url = private_path(ldb, lp_ctx, url);
+ if (real_url == NULL) {
+ talloc_free(ldb);
+ return NULL;
+ }
+
+ /* allow admins to force non-sync ldb for all databases */
+ if (lp_parm_bool(lp_ctx, NULL, "ldb", "nosync", false)) {
+ flags |= LDB_FLG_NOSYNC;
+ }
+
+ /* we usually want Samba databases to be private. If we later
+ find we need one public, we will need to add a parameter to
+ ldb_wrap_connect() */
+ ldb_set_create_perms(ldb, 0600);
+
+ ret = ldb_connect(ldb, real_url, flags, options);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(ldb);
+ return NULL;
+ }
+
+ /* setup for leak detection */
+ ldb_set_opaque(ldb, "wrap_url", real_url);
+ startup_blocks = talloc(ldb, size_t);
+ *startup_blocks = talloc_total_blocks(ldb);
+ ldb_set_opaque(ldb, "startup_blocks", startup_blocks);
+
+ talloc_set_destructor(ldb, ldb_wrap_destructor);
+
+ return ldb;
+}
diff --git a/source4/lib/ldb_wrap.h b/source4/lib/ldb_wrap.h
new file mode 100644
index 0000000000..f44ff8c599
--- /dev/null
+++ b/source4/lib/ldb_wrap.h
@@ -0,0 +1,43 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ database wrap headers
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _LDB_WRAP_H_
+#define _LDB_WRAP_H_
+
+struct auth_session_info;
+struct ldb_message;
+struct ldb_dn;
+struct cli_credentials;
+struct loadparm_context;
+struct tevent_context;
+
+char *wrap_casefold(void *context, void *mem_ctx, const char *s, size_t n);
+
+struct ldb_context *ldb_wrap_connect(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct loadparm_context *lp_ctx,
+ const char *url,
+ struct auth_session_info *session_info,
+ struct cli_credentials *credentials,
+ unsigned int flags,
+ const char *options[]);
+
+#endif /* _LDB_WRAP_H_ */
diff --git a/source4/lib/messaging/config.mk b/source4/lib/messaging/config.mk
new file mode 100644
index 0000000000..b5b1e6d1e3
--- /dev/null
+++ b/source4/lib/messaging/config.mk
@@ -0,0 +1,18 @@
+[SUBSYSTEM::MESSAGING]
+PUBLIC_DEPENDENCIES = \
+ LIBSAMBA-UTIL \
+ TDB_WRAP \
+ NDR_IRPC \
+ UNIX_PRIVS \
+ UTIL_TDB \
+ CLUSTER \
+ LIBNDR \
+ samba_socket
+
+MESSAGING_OBJ_FILES = $(libmessagingsrcdir)/messaging.o
+
+[PYTHON::python_messaging]
+LIBRARY_REALNAME = samba/messaging.$(SHLIBEXT)
+PRIVATE_DEPENDENCIES = MESSAGING LIBEVENTS python_irpc
+
+python_messaging_OBJ_FILES = $(libmessagingsrcdir)/pymessaging.o
diff --git a/source4/lib/messaging/irpc.h b/source4/lib/messaging/irpc.h
new file mode 100644
index 0000000000..3c518828ab
--- /dev/null
+++ b/source4/lib/messaging/irpc.h
@@ -0,0 +1,132 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Samba internal rpc code - header
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef IRPC_H
+#define IRPC_H
+
+#include "librpc/gen_ndr/irpc.h"
+#include "librpc/gen_ndr/server_id.h"
+
+/*
+ an incoming irpc message
+*/
+struct irpc_message {
+ struct server_id from;
+ void *private_data;
+ struct irpc_header header;
+ struct ndr_pull *ndr;
+ bool defer_reply;
+ struct messaging_context *msg_ctx;
+ struct irpc_list *irpc;
+ void *data;
+ struct tevent_context *ev;
+};
+
+/* don't allow calls to take too long */
+#define IRPC_CALL_TIMEOUT 10
+
+
+/* the server function type */
+typedef NTSTATUS (*irpc_function_t)(struct irpc_message *, void *r);
+
+/* register a server function with the irpc messaging system */
+#define IRPC_REGISTER(msg_ctx, pipename, funcname, function, private_data) \
+ irpc_register(msg_ctx, &ndr_table_ ## pipename, \
+ NDR_ ## funcname, \
+ (irpc_function_t)function, private_data)
+
+/* make a irpc call */
+#define IRPC_CALL(msg_ctx, server_id, pipename, funcname, ptr, ctx) \
+ irpc_call(msg_ctx, server_id, &ndr_table_ ## pipename, NDR_ ## funcname, ptr, ctx)
+
+#define IRPC_CALL_SEND(msg_ctx, server_id, pipename, funcname, ptr, ctx) \
+ irpc_call_send(msg_ctx, server_id, &ndr_table_ ## pipename, NDR_ ## funcname, ptr, ctx)
+
+
+/*
+ a pending irpc call
+*/
+struct irpc_request {
+ struct messaging_context *msg_ctx;
+ const struct ndr_interface_table *table;
+ int callnum;
+ int callid;
+ void *r;
+ NTSTATUS status;
+ bool done;
+ bool reject_free;
+ TALLOC_CTX *mem_ctx;
+ struct {
+ void (*fn)(struct irpc_request *);
+ void *private_data;
+ } async;
+};
+
+struct loadparm_context;
+
+typedef void (*msg_callback_t)(struct messaging_context *msg, void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id, DATA_BLOB *data);
+
+NTSTATUS messaging_send(struct messaging_context *msg, struct server_id server,
+ uint32_t msg_type, DATA_BLOB *data);
+NTSTATUS messaging_register(struct messaging_context *msg, void *private_data,
+ uint32_t msg_type,
+ msg_callback_t fn);
+NTSTATUS messaging_register_tmp(struct messaging_context *msg, void *private_data,
+ msg_callback_t fn, uint32_t *msg_type);
+struct messaging_context *messaging_init(TALLOC_CTX *mem_ctx,
+ const char *dir,
+ struct server_id server_id,
+ struct smb_iconv_convenience *iconv_convenience,
+ struct tevent_context *ev);
+struct messaging_context *messaging_client_init(TALLOC_CTX *mem_ctx,
+ const char *dir,
+ struct smb_iconv_convenience *iconv_convenience,
+ struct tevent_context *ev);
+NTSTATUS messaging_send_ptr(struct messaging_context *msg, struct server_id server,
+ uint32_t msg_type, void *ptr);
+void messaging_deregister(struct messaging_context *msg, uint32_t msg_type, void *private_data);
+
+
+
+
+NTSTATUS irpc_register(struct messaging_context *msg_ctx,
+ const struct ndr_interface_table *table,
+ int call, irpc_function_t fn, void *private_data);
+struct irpc_request *irpc_call_send(struct messaging_context *msg_ctx,
+ struct server_id server_id,
+ const struct ndr_interface_table *table,
+ int callnum, void *r, TALLOC_CTX *ctx);
+NTSTATUS irpc_call_recv(struct irpc_request *irpc);
+NTSTATUS irpc_call(struct messaging_context *msg_ctx,
+ struct server_id server_id,
+ const struct ndr_interface_table *table,
+ int callnum, void *r, TALLOC_CTX *ctx);
+
+NTSTATUS irpc_add_name(struct messaging_context *msg_ctx, const char *name);
+struct server_id *irpc_servers_byname(struct messaging_context *msg_ctx, TALLOC_CTX *mem_ctx, const char *name);
+void irpc_remove_name(struct messaging_context *msg_ctx, const char *name);
+NTSTATUS irpc_send_reply(struct irpc_message *m, NTSTATUS status);
+struct server_id messaging_get_server_id(struct messaging_context *msg_ctx);
+
+#endif
+
diff --git a/source4/lib/messaging/messaging.c b/source4/lib/messaging/messaging.c
new file mode 100644
index 0000000000..cfceeffac7
--- /dev/null
+++ b/source4/lib/messaging/messaging.c
@@ -0,0 +1,1118 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Samba internal messaging functions
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/events/events.h"
+#include "system/filesys.h"
+#include "messaging/messaging.h"
+#include "../lib/util/dlinklist.h"
+#include "lib/socket/socket.h"
+#include "librpc/gen_ndr/ndr_irpc.h"
+#include "lib/messaging/irpc.h"
+#include "tdb_wrap.h"
+#include "../lib/util/unix_privs.h"
+#include "librpc/rpc/dcerpc.h"
+#include "../tdb/include/tdb.h"
+#include "../lib/util/util_tdb.h"
+#include "cluster/cluster.h"
+
+/* change the message version with any incompatible changes in the protocol */
+#define MESSAGING_VERSION 1
+
+struct messaging_context {
+ struct server_id server_id;
+ struct socket_context *sock;
+ const char *base_path;
+ const char *path;
+ struct dispatch_fn **dispatch;
+ uint32_t num_types;
+ struct idr_context *dispatch_tree;
+ struct messaging_rec *pending;
+ struct messaging_rec *retry_queue;
+ struct smb_iconv_convenience *iconv_convenience;
+ struct irpc_list *irpc;
+ struct idr_context *idr;
+ const char **names;
+ struct timeval start_time;
+ struct tevent_timer *retry_te;
+ struct {
+ struct tevent_context *ev;
+ struct tevent_fd *fde;
+ } event;
+};
+
+/* we have a linked list of dispatch handlers for each msg_type that
+ this messaging server can deal with */
+struct dispatch_fn {
+ struct dispatch_fn *next, *prev;
+ uint32_t msg_type;
+ void *private_data;
+ msg_callback_t fn;
+};
+
+/* an individual message */
+struct messaging_rec {
+ struct messaging_rec *next, *prev;
+ struct messaging_context *msg;
+ const char *path;
+
+ struct messaging_header {
+ uint32_t version;
+ uint32_t msg_type;
+ struct server_id from;
+ struct server_id to;
+ uint32_t length;
+ } *header;
+
+ DATA_BLOB packet;
+ uint32_t retries;
+};
+
+
+static void irpc_handler(struct messaging_context *, void *,
+ uint32_t, struct server_id, DATA_BLOB *);
+
+
+/*
+ A useful function for testing the message system.
+*/
+static void ping_message(struct messaging_context *msg, void *private_data,
+ uint32_t msg_type, struct server_id src, DATA_BLOB *data)
+{
+ DEBUG(1,("INFO: Received PING message from server %u.%u [%.*s]\n",
+ (uint_t)src.node, (uint_t)src.id, (int)data->length,
+ data->data?(const char *)data->data:""));
+ messaging_send(msg, src, MSG_PONG, data);
+}
+
+/*
+ return uptime of messaging server via irpc
+*/
+static NTSTATUS irpc_uptime(struct irpc_message *msg,
+ struct irpc_uptime *r)
+{
+ struct messaging_context *ctx = talloc_get_type(msg->private_data, struct messaging_context);
+ *r->out.start_time = timeval_to_nttime(&ctx->start_time);
+ return NT_STATUS_OK;
+}
+
+/*
+ return the path to a messaging socket
+*/
+static char *messaging_path(struct messaging_context *msg, struct server_id server_id)
+{
+ return talloc_asprintf(msg, "%s/msg.%s", msg->base_path,
+ cluster_id_string(msg, server_id));
+}
+
+/*
+ dispatch a fully received message
+
+ note that this deliberately can match more than one message handler
+ per message. That allows a single messasging context to register
+ (for example) a debug handler for more than one piece of code
+*/
+static void messaging_dispatch(struct messaging_context *msg, struct messaging_rec *rec)
+{
+ struct dispatch_fn *d, *next;
+
+ /* temporary IDs use an idtree, the rest use a array of pointers */
+ if (rec->header->msg_type >= MSG_TMP_BASE) {
+ d = (struct dispatch_fn *)idr_find(msg->dispatch_tree,
+ rec->header->msg_type);
+ } else if (rec->header->msg_type < msg->num_types) {
+ d = msg->dispatch[rec->header->msg_type];
+ } else {
+ d = NULL;
+ }
+
+ for (; d; d = next) {
+ DATA_BLOB data;
+ next = d->next;
+ data.data = rec->packet.data + sizeof(*rec->header);
+ data.length = rec->header->length;
+ d->fn(msg, d->private_data, d->msg_type, rec->header->from, &data);
+ }
+ rec->header->length = 0;
+}
+
+/*
+ handler for messages that arrive from other nodes in the cluster
+*/
+static void cluster_message_handler(struct messaging_context *msg, DATA_BLOB packet)
+{
+ struct messaging_rec *rec;
+
+ rec = talloc(msg, struct messaging_rec);
+ if (rec == NULL) {
+ smb_panic("Unable to allocate messaging_rec");
+ }
+
+ rec->msg = msg;
+ rec->path = msg->path;
+ rec->header = (struct messaging_header *)packet.data;
+ rec->packet = packet;
+ rec->retries = 0;
+
+ if (packet.length != sizeof(*rec->header) + rec->header->length) {
+ DEBUG(0,("messaging: bad message header size %d should be %d\n",
+ rec->header->length, (int)(packet.length - sizeof(*rec->header))));
+ talloc_free(rec);
+ return;
+ }
+
+ messaging_dispatch(msg, rec);
+ talloc_free(rec);
+}
+
+
+
+/*
+ try to send the message
+*/
+static NTSTATUS try_send(struct messaging_rec *rec)
+{
+ struct messaging_context *msg = rec->msg;
+ size_t nsent;
+ void *priv;
+ NTSTATUS status;
+ struct socket_address *path;
+
+ /* rec->path is the path of the *other* socket, where we want
+ * this to end up */
+ path = socket_address_from_strings(msg, msg->sock->backend_name,
+ rec->path, 0);
+ if (!path) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* we send with privileges so messages work from any context */
+ priv = root_privileges();
+ status = socket_sendto(msg->sock, &rec->packet, &nsent, path);
+ talloc_free(path);
+ talloc_free(priv);
+
+ return status;
+}
+
+/*
+ retry backed off messages
+*/
+static void msg_retry_timer(struct tevent_context *ev, struct tevent_timer *te,
+ struct timeval t, void *private_data)
+{
+ struct messaging_context *msg = talloc_get_type(private_data,
+ struct messaging_context);
+ msg->retry_te = NULL;
+
+ /* put the messages back on the main queue */
+ while (msg->retry_queue) {
+ struct messaging_rec *rec = msg->retry_queue;
+ DLIST_REMOVE(msg->retry_queue, rec);
+ DLIST_ADD_END(msg->pending, rec, struct messaging_rec *);
+ }
+
+ EVENT_FD_WRITEABLE(msg->event.fde);
+}
+
+/*
+ handle a socket write event
+*/
+static void messaging_send_handler(struct messaging_context *msg)
+{
+ while (msg->pending) {
+ struct messaging_rec *rec = msg->pending;
+ NTSTATUS status;
+ status = try_send(rec);
+ if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
+ rec->retries++;
+ if (rec->retries > 3) {
+ /* we're getting continuous write errors -
+ backoff this record */
+ DLIST_REMOVE(msg->pending, rec);
+ DLIST_ADD_END(msg->retry_queue, rec,
+ struct messaging_rec *);
+ if (msg->retry_te == NULL) {
+ msg->retry_te =
+ event_add_timed(msg->event.ev, msg,
+ timeval_current_ofs(1, 0),
+ msg_retry_timer, msg);
+ }
+ }
+ break;
+ }
+ rec->retries = 0;
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1,("messaging: Lost message from %s to %s of type %u - %s\n",
+ cluster_id_string(debug_ctx(), rec->header->from),
+ cluster_id_string(debug_ctx(), rec->header->to),
+ rec->header->msg_type,
+ nt_errstr(status)));
+ }
+ DLIST_REMOVE(msg->pending, rec);
+ talloc_free(rec);
+ }
+ if (msg->pending == NULL) {
+ EVENT_FD_NOT_WRITEABLE(msg->event.fde);
+ }
+}
+
+/*
+ handle a new incoming packet
+*/
+static void messaging_recv_handler(struct messaging_context *msg)
+{
+ struct messaging_rec *rec;
+ NTSTATUS status;
+ DATA_BLOB packet;
+ size_t msize;
+
+ /* see how many bytes are in the next packet */
+ status = socket_pending(msg->sock, &msize);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("socket_pending failed in messaging - %s\n",
+ nt_errstr(status)));
+ return;
+ }
+
+ packet = data_blob_talloc(msg, NULL, msize);
+ if (packet.data == NULL) {
+ /* assume this is temporary and retry */
+ return;
+ }
+
+ status = socket_recv(msg->sock, packet.data, msize, &msize);
+ if (!NT_STATUS_IS_OK(status)) {
+ data_blob_free(&packet);
+ return;
+ }
+
+ if (msize < sizeof(*rec->header)) {
+ DEBUG(0,("messaging: bad message of size %d\n", (int)msize));
+ data_blob_free(&packet);
+ return;
+ }
+
+ rec = talloc(msg, struct messaging_rec);
+ if (rec == NULL) {
+ smb_panic("Unable to allocate messaging_rec");
+ }
+
+ talloc_steal(rec, packet.data);
+ rec->msg = msg;
+ rec->path = msg->path;
+ rec->header = (struct messaging_header *)packet.data;
+ rec->packet = packet;
+ rec->retries = 0;
+
+ if (msize != sizeof(*rec->header) + rec->header->length) {
+ DEBUG(0,("messaging: bad message header size %d should be %d\n",
+ rec->header->length, (int)(msize - sizeof(*rec->header))));
+ talloc_free(rec);
+ return;
+ }
+
+ messaging_dispatch(msg, rec);
+ talloc_free(rec);
+}
+
+
+/*
+ handle a socket event
+*/
+static void messaging_handler(struct tevent_context *ev, struct tevent_fd *fde,
+ uint16_t flags, void *private_data)
+{
+ struct messaging_context *msg = talloc_get_type(private_data,
+ struct messaging_context);
+ if (flags & EVENT_FD_WRITE) {
+ messaging_send_handler(msg);
+ }
+ if (flags & EVENT_FD_READ) {
+ messaging_recv_handler(msg);
+ }
+}
+
+
+/*
+ Register a dispatch function for a particular message type.
+*/
+NTSTATUS messaging_register(struct messaging_context *msg, void *private_data,
+ uint32_t msg_type, msg_callback_t fn)
+{
+ struct dispatch_fn *d;
+
+ /* possibly expand dispatch array */
+ if (msg_type >= msg->num_types) {
+ struct dispatch_fn **dp;
+ int i;
+ dp = talloc_realloc(msg, msg->dispatch, struct dispatch_fn *, msg_type+1);
+ NT_STATUS_HAVE_NO_MEMORY(dp);
+ msg->dispatch = dp;
+ for (i=msg->num_types;i<=msg_type;i++) {
+ msg->dispatch[i] = NULL;
+ }
+ msg->num_types = msg_type+1;
+ }
+
+ d = talloc_zero(msg->dispatch, struct dispatch_fn);
+ NT_STATUS_HAVE_NO_MEMORY(d);
+ d->msg_type = msg_type;
+ d->private_data = private_data;
+ d->fn = fn;
+
+ DLIST_ADD(msg->dispatch[msg_type], d);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ register a temporary message handler. The msg_type is allocated
+ above MSG_TMP_BASE
+*/
+NTSTATUS messaging_register_tmp(struct messaging_context *msg, void *private_data,
+ msg_callback_t fn, uint32_t *msg_type)
+{
+ struct dispatch_fn *d;
+ int id;
+
+ d = talloc_zero(msg->dispatch, struct dispatch_fn);
+ NT_STATUS_HAVE_NO_MEMORY(d);
+ d->private_data = private_data;
+ d->fn = fn;
+
+ id = idr_get_new_above(msg->dispatch_tree, d, MSG_TMP_BASE, UINT16_MAX);
+ if (id == -1) {
+ talloc_free(d);
+ return NT_STATUS_TOO_MANY_CONTEXT_IDS;
+ }
+
+ d->msg_type = (uint32_t)id;
+ (*msg_type) = d->msg_type;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ De-register the function for a particular message type.
+*/
+void messaging_deregister(struct messaging_context *msg, uint32_t msg_type, void *private_data)
+{
+ struct dispatch_fn *d, *next;
+
+ if (msg_type >= msg->num_types) {
+ d = (struct dispatch_fn *)idr_find(msg->dispatch_tree,
+ msg_type);
+ if (!d) return;
+ idr_remove(msg->dispatch_tree, msg_type);
+ talloc_free(d);
+ return;
+ }
+
+ for (d = msg->dispatch[msg_type]; d; d = next) {
+ next = d->next;
+ if (d->private_data == private_data) {
+ DLIST_REMOVE(msg->dispatch[msg_type], d);
+ talloc_free(d);
+ }
+ }
+}
+
+/*
+ Send a message to a particular server
+*/
+NTSTATUS messaging_send(struct messaging_context *msg, struct server_id server,
+ uint32_t msg_type, DATA_BLOB *data)
+{
+ struct messaging_rec *rec;
+ NTSTATUS status;
+ size_t dlength = data?data->length:0;
+
+ rec = talloc(msg, struct messaging_rec);
+ if (rec == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rec->packet = data_blob_talloc(rec, NULL, sizeof(*rec->header) + dlength);
+ if (rec->packet.data == NULL) {
+ talloc_free(rec);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rec->retries = 0;
+ rec->msg = msg;
+ rec->header = (struct messaging_header *)rec->packet.data;
+ /* zero padding */
+ ZERO_STRUCTP(rec->header);
+ rec->header->version = MESSAGING_VERSION;
+ rec->header->msg_type = msg_type;
+ rec->header->from = msg->server_id;
+ rec->header->to = server;
+ rec->header->length = dlength;
+ if (dlength != 0) {
+ memcpy(rec->packet.data + sizeof(*rec->header),
+ data->data, dlength);
+ }
+
+ if (!cluster_node_equal(&msg->server_id, &server)) {
+ /* the destination is on another node - dispatch via
+ the cluster layer */
+ status = cluster_message_send(server, &rec->packet);
+ talloc_free(rec);
+ return status;
+ }
+
+ rec->path = messaging_path(msg, server);
+ talloc_steal(rec, rec->path);
+
+ if (msg->pending != NULL) {
+ status = STATUS_MORE_ENTRIES;
+ } else {
+ status = try_send(rec);
+ }
+
+ if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
+ if (msg->pending == NULL) {
+ EVENT_FD_WRITEABLE(msg->event.fde);
+ }
+ DLIST_ADD_END(msg->pending, rec, struct messaging_rec *);
+ return NT_STATUS_OK;
+ }
+
+ talloc_free(rec);
+
+ return status;
+}
+
+/*
+ Send a message to a particular server, with the message containing a single pointer
+*/
+NTSTATUS messaging_send_ptr(struct messaging_context *msg, struct server_id server,
+ uint32_t msg_type, void *ptr)
+{
+ DATA_BLOB blob;
+
+ blob.data = (uint8_t *)&ptr;
+ blob.length = sizeof(void *);
+
+ return messaging_send(msg, server, msg_type, &blob);
+}
+
+
+/*
+ destroy the messaging context
+*/
+static int messaging_destructor(struct messaging_context *msg)
+{
+ unlink(msg->path);
+ while (msg->names && msg->names[0]) {
+ irpc_remove_name(msg, msg->names[0]);
+ }
+ return 0;
+}
+
+/*
+ create the listening socket and setup the dispatcher
+*/
+struct messaging_context *messaging_init(TALLOC_CTX *mem_ctx,
+ const char *dir,
+ struct server_id server_id,
+ struct smb_iconv_convenience *iconv_convenience,
+ struct tevent_context *ev)
+{
+ struct messaging_context *msg;
+ NTSTATUS status;
+ struct socket_address *path;
+
+ if (ev == NULL) {
+ return NULL;
+ }
+
+ msg = talloc_zero(mem_ctx, struct messaging_context);
+ if (msg == NULL) {
+ return NULL;
+ }
+
+ /* setup a handler for messages from other cluster nodes, if appropriate */
+ status = cluster_message_init(msg, server_id, cluster_message_handler);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(msg);
+ return NULL;
+ }
+
+ /* create the messaging directory if needed */
+ mkdir(dir, 0700);
+
+ msg->base_path = talloc_reference(msg, dir);
+ msg->path = messaging_path(msg, server_id);
+ msg->server_id = server_id;
+ msg->iconv_convenience = iconv_convenience;
+ msg->idr = idr_init(msg);
+ msg->dispatch_tree = idr_init(msg);
+ msg->start_time = timeval_current();
+
+ status = socket_create("unix", SOCKET_TYPE_DGRAM, &msg->sock, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(msg);
+ return NULL;
+ }
+
+ /* by stealing here we ensure that the socket is cleaned up (and even
+ deleted) on exit */
+ talloc_steal(msg, msg->sock);
+
+ path = socket_address_from_strings(msg, msg->sock->backend_name,
+ msg->path, 0);
+ if (!path) {
+ talloc_free(msg);
+ return NULL;
+ }
+
+ status = socket_listen(msg->sock, path, 50, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Unable to setup messaging listener for '%s':%s\n", msg->path, nt_errstr(status)));
+ talloc_free(msg);
+ return NULL;
+ }
+
+ /* it needs to be non blocking for sends */
+ set_blocking(socket_get_fd(msg->sock), false);
+
+ msg->event.ev = talloc_reference(msg, ev);
+ msg->event.fde = event_add_fd(ev, msg, socket_get_fd(msg->sock),
+ EVENT_FD_READ, messaging_handler, msg);
+
+ talloc_set_destructor(msg, messaging_destructor);
+
+ messaging_register(msg, NULL, MSG_PING, ping_message);
+ messaging_register(msg, NULL, MSG_IRPC, irpc_handler);
+ IRPC_REGISTER(msg, irpc, IRPC_UPTIME, irpc_uptime, msg);
+
+ return msg;
+}
+
+/*
+ A hack, for the short term until we get 'client only' messaging in place
+*/
+struct messaging_context *messaging_client_init(TALLOC_CTX *mem_ctx,
+ const char *dir,
+ struct smb_iconv_convenience *iconv_convenience,
+ struct tevent_context *ev)
+{
+ struct server_id id;
+ ZERO_STRUCT(id);
+ id.id = random() % 0x10000000;
+ return messaging_init(mem_ctx, dir, id, iconv_convenience, ev);
+}
+/*
+ a list of registered irpc server functions
+*/
+struct irpc_list {
+ struct irpc_list *next, *prev;
+ struct GUID uuid;
+ const struct ndr_interface_table *table;
+ int callnum;
+ irpc_function_t fn;
+ void *private_data;
+};
+
+
+/*
+ register a irpc server function
+*/
+NTSTATUS irpc_register(struct messaging_context *msg_ctx,
+ const struct ndr_interface_table *table,
+ int callnum, irpc_function_t fn, void *private_data)
+{
+ struct irpc_list *irpc;
+
+ /* override an existing handler, if any */
+ for (irpc=msg_ctx->irpc; irpc; irpc=irpc->next) {
+ if (irpc->table == table && irpc->callnum == callnum) {
+ break;
+ }
+ }
+ if (irpc == NULL) {
+ irpc = talloc(msg_ctx, struct irpc_list);
+ NT_STATUS_HAVE_NO_MEMORY(irpc);
+ DLIST_ADD(msg_ctx->irpc, irpc);
+ }
+
+ irpc->table = table;
+ irpc->callnum = callnum;
+ irpc->fn = fn;
+ irpc->private_data = private_data;
+ irpc->uuid = irpc->table->syntax_id.uuid;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ handle an incoming irpc reply message
+*/
+static void irpc_handler_reply(struct messaging_context *msg_ctx, struct irpc_message *m)
+{
+ struct irpc_request *irpc;
+ enum ndr_err_code ndr_err;
+
+ irpc = (struct irpc_request *)idr_find(msg_ctx->idr, m->header.callid);
+ if (irpc == NULL) return;
+
+ /* parse the reply data */
+ ndr_err = irpc->table->calls[irpc->callnum].ndr_pull(m->ndr, NDR_OUT, irpc->r);
+ if (NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ irpc->status = m->header.status;
+ talloc_steal(irpc->mem_ctx, m);
+ } else {
+ irpc->status = ndr_map_error2ntstatus(ndr_err);
+ talloc_steal(irpc, m);
+ }
+ irpc->done = true;
+ if (irpc->async.fn) {
+ irpc->async.fn(irpc);
+ }
+}
+
+/*
+ send a irpc reply
+*/
+NTSTATUS irpc_send_reply(struct irpc_message *m, NTSTATUS status)
+{
+ struct ndr_push *push;
+ DATA_BLOB packet;
+ enum ndr_err_code ndr_err;
+
+ m->header.status = status;
+
+ /* setup the reply */
+ push = ndr_push_init_ctx(m->ndr, m->msg_ctx->iconv_convenience);
+ if (push == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto failed;
+ }
+
+ m->header.flags |= IRPC_FLAG_REPLY;
+
+ /* construct the packet */
+ ndr_err = ndr_push_irpc_header(push, NDR_SCALARS|NDR_BUFFERS, &m->header);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ goto failed;
+ }
+
+ ndr_err = m->irpc->table->calls[m->irpc->callnum].ndr_push(push, NDR_OUT, m->data);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ goto failed;
+ }
+
+ /* send the reply message */
+ packet = ndr_push_blob(push);
+ status = messaging_send(m->msg_ctx, m->from, MSG_IRPC, &packet);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+
+failed:
+ talloc_free(m);
+ return status;
+}
+
+/*
+ handle an incoming irpc request message
+*/
+static void irpc_handler_request(struct messaging_context *msg_ctx,
+ struct irpc_message *m)
+{
+ struct irpc_list *i;
+ void *r;
+ enum ndr_err_code ndr_err;
+
+ for (i=msg_ctx->irpc; i; i=i->next) {
+ if (GUID_equal(&i->uuid, &m->header.uuid) &&
+ i->table->syntax_id.if_version == m->header.if_version &&
+ i->callnum == m->header.callnum) {
+ break;
+ }
+ }
+
+ if (i == NULL) {
+ /* no registered handler for this message */
+ talloc_free(m);
+ return;
+ }
+
+ /* allocate space for the structure */
+ r = talloc_zero_size(m->ndr, i->table->calls[m->header.callnum].struct_size);
+ if (r == NULL) goto failed;
+
+ /* parse the request data */
+ ndr_err = i->table->calls[i->callnum].ndr_pull(m->ndr, NDR_IN, r);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) goto failed;
+
+ /* make the call */
+ m->private_data= i->private_data;
+ m->defer_reply = false;
+ m->msg_ctx = msg_ctx;
+ m->irpc = i;
+ m->data = r;
+ m->ev = msg_ctx->event.ev;
+
+ m->header.status = i->fn(m, r);
+
+ if (m->defer_reply) {
+ /* the server function has asked to defer the reply to later */
+ talloc_steal(msg_ctx, m);
+ return;
+ }
+
+ irpc_send_reply(m, m->header.status);
+ return;
+
+failed:
+ talloc_free(m);
+}
+
+/*
+ handle an incoming irpc message
+*/
+static void irpc_handler(struct messaging_context *msg_ctx, void *private_data,
+ uint32_t msg_type, struct server_id src, DATA_BLOB *packet)
+{
+ struct irpc_message *m;
+ enum ndr_err_code ndr_err;
+
+ m = talloc(msg_ctx, struct irpc_message);
+ if (m == NULL) goto failed;
+
+ m->from = src;
+
+ m->ndr = ndr_pull_init_blob(packet, m, msg_ctx->iconv_convenience);
+ if (m->ndr == NULL) goto failed;
+
+ m->ndr->flags |= LIBNDR_FLAG_REF_ALLOC;
+
+ ndr_err = ndr_pull_irpc_header(m->ndr, NDR_BUFFERS|NDR_SCALARS, &m->header);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) goto failed;
+
+ if (m->header.flags & IRPC_FLAG_REPLY) {
+ irpc_handler_reply(msg_ctx, m);
+ } else {
+ irpc_handler_request(msg_ctx, m);
+ }
+ return;
+
+failed:
+ talloc_free(m);
+}
+
+
+/*
+ destroy a irpc request
+*/
+static int irpc_destructor(struct irpc_request *irpc)
+{
+ if (irpc->callid != -1) {
+ idr_remove(irpc->msg_ctx->idr, irpc->callid);
+ irpc->callid = -1;
+ }
+
+ if (irpc->reject_free) {
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ timeout a irpc request
+*/
+static void irpc_timeout(struct tevent_context *ev, struct tevent_timer *te,
+ struct timeval t, void *private_data)
+{
+ struct irpc_request *irpc = talloc_get_type(private_data, struct irpc_request);
+ irpc->status = NT_STATUS_IO_TIMEOUT;
+ irpc->done = true;
+ if (irpc->async.fn) {
+ irpc->async.fn(irpc);
+ }
+}
+
+
+/*
+ make a irpc call - async send
+*/
+struct irpc_request *irpc_call_send(struct messaging_context *msg_ctx,
+ struct server_id server_id,
+ const struct ndr_interface_table *table,
+ int callnum, void *r, TALLOC_CTX *ctx)
+{
+ struct irpc_header header;
+ struct ndr_push *ndr;
+ NTSTATUS status;
+ DATA_BLOB packet;
+ struct irpc_request *irpc;
+ enum ndr_err_code ndr_err;
+
+ irpc = talloc(msg_ctx, struct irpc_request);
+ if (irpc == NULL) goto failed;
+
+ irpc->msg_ctx = msg_ctx;
+ irpc->table = table;
+ irpc->callnum = callnum;
+ irpc->callid = idr_get_new(msg_ctx->idr, irpc, UINT16_MAX);
+ if (irpc->callid == -1) goto failed;
+ irpc->r = r;
+ irpc->done = false;
+ irpc->async.fn = NULL;
+ irpc->mem_ctx = ctx;
+ irpc->reject_free = false;
+
+ talloc_set_destructor(irpc, irpc_destructor);
+
+ /* setup the header */
+ header.uuid = table->syntax_id.uuid;
+
+ header.if_version = table->syntax_id.if_version;
+ header.callid = irpc->callid;
+ header.callnum = callnum;
+ header.flags = 0;
+ header.status = NT_STATUS_OK;
+
+ /* construct the irpc packet */
+ ndr = ndr_push_init_ctx(irpc, msg_ctx->iconv_convenience);
+ if (ndr == NULL) goto failed;
+
+ ndr_err = ndr_push_irpc_header(ndr, NDR_SCALARS|NDR_BUFFERS, &header);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) goto failed;
+
+ ndr_err = table->calls[callnum].ndr_push(ndr, NDR_IN, r);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) goto failed;
+
+ /* and send it */
+ packet = ndr_push_blob(ndr);
+ status = messaging_send(msg_ctx, server_id, MSG_IRPC, &packet);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+
+ event_add_timed(msg_ctx->event.ev, irpc,
+ timeval_current_ofs(IRPC_CALL_TIMEOUT, 0),
+ irpc_timeout, irpc);
+
+ talloc_free(ndr);
+ return irpc;
+
+failed:
+ talloc_free(irpc);
+ return NULL;
+}
+
+/*
+ wait for a irpc reply
+*/
+NTSTATUS irpc_call_recv(struct irpc_request *irpc)
+{
+ NTSTATUS status;
+
+ NT_STATUS_HAVE_NO_MEMORY(irpc);
+
+ irpc->reject_free = true;
+
+ while (!irpc->done) {
+ if (event_loop_once(irpc->msg_ctx->event.ev) != 0) {
+ return NT_STATUS_CONNECTION_DISCONNECTED;
+ }
+ }
+
+ irpc->reject_free = false;
+
+ status = irpc->status;
+ talloc_free(irpc);
+ return status;
+}
+
+/*
+ perform a synchronous irpc request
+*/
+NTSTATUS irpc_call(struct messaging_context *msg_ctx,
+ struct server_id server_id,
+ const struct ndr_interface_table *table,
+ int callnum, void *r,
+ TALLOC_CTX *mem_ctx)
+{
+ struct irpc_request *irpc = irpc_call_send(msg_ctx, server_id,
+ table, callnum, r, mem_ctx);
+ return irpc_call_recv(irpc);
+}
+
+/*
+ open the naming database
+*/
+static struct tdb_wrap *irpc_namedb_open(struct messaging_context *msg_ctx)
+{
+ struct tdb_wrap *t;
+ char *path = talloc_asprintf(msg_ctx, "%s/names.tdb", msg_ctx->base_path);
+ if (path == NULL) {
+ return NULL;
+ }
+ t = tdb_wrap_open(msg_ctx, path, 0, 0, O_RDWR|O_CREAT, 0660);
+ talloc_free(path);
+ return t;
+}
+
+
+/*
+ add a string name that this irpc server can be called on
+*/
+NTSTATUS irpc_add_name(struct messaging_context *msg_ctx, const char *name)
+{
+ struct tdb_wrap *t;
+ TDB_DATA rec;
+ int count;
+ NTSTATUS status = NT_STATUS_OK;
+
+ t = irpc_namedb_open(msg_ctx);
+ NT_STATUS_HAVE_NO_MEMORY(t);
+
+ if (tdb_lock_bystring(t->tdb, name) != 0) {
+ talloc_free(t);
+ return NT_STATUS_LOCK_NOT_GRANTED;
+ }
+ rec = tdb_fetch_bystring(t->tdb, name);
+ count = rec.dsize / sizeof(struct server_id);
+ rec.dptr = (unsigned char *)realloc_p(rec.dptr, struct server_id, count+1);
+ rec.dsize += sizeof(struct server_id);
+ if (rec.dptr == NULL) {
+ tdb_unlock_bystring(t->tdb, name);
+ talloc_free(t);
+ return NT_STATUS_NO_MEMORY;
+ }
+ ((struct server_id *)rec.dptr)[count] = msg_ctx->server_id;
+ if (tdb_store_bystring(t->tdb, name, rec, 0) != 0) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ }
+ free(rec.dptr);
+ tdb_unlock_bystring(t->tdb, name);
+ talloc_free(t);
+
+ msg_ctx->names = str_list_add(msg_ctx->names, name);
+ talloc_steal(msg_ctx, msg_ctx->names);
+
+ return status;
+}
+
+/*
+ return a list of server ids for a server name
+*/
+struct server_id *irpc_servers_byname(struct messaging_context *msg_ctx,
+ TALLOC_CTX *mem_ctx,
+ const char *name)
+{
+ struct tdb_wrap *t;
+ TDB_DATA rec;
+ int count, i;
+ struct server_id *ret;
+
+ t = irpc_namedb_open(msg_ctx);
+ if (t == NULL) {
+ return NULL;
+ }
+
+ if (tdb_lock_bystring(t->tdb, name) != 0) {
+ talloc_free(t);
+ return NULL;
+ }
+ rec = tdb_fetch_bystring(t->tdb, name);
+ if (rec.dptr == NULL) {
+ tdb_unlock_bystring(t->tdb, name);
+ talloc_free(t);
+ return NULL;
+ }
+ count = rec.dsize / sizeof(struct server_id);
+ ret = talloc_array(mem_ctx, struct server_id, count+1);
+ if (ret == NULL) {
+ tdb_unlock_bystring(t->tdb, name);
+ talloc_free(t);
+ return NULL;
+ }
+ for (i=0;i<count;i++) {
+ ret[i] = ((struct server_id *)rec.dptr)[i];
+ }
+ ret[i] = cluster_id(0, 0);
+ free(rec.dptr);
+ tdb_unlock_bystring(t->tdb, name);
+ talloc_free(t);
+
+ return ret;
+}
+
+/*
+ remove a name from a messaging context
+*/
+void irpc_remove_name(struct messaging_context *msg_ctx, const char *name)
+{
+ struct tdb_wrap *t;
+ TDB_DATA rec;
+ int count, i;
+ struct server_id *ids;
+
+ str_list_remove(msg_ctx->names, name);
+
+ t = irpc_namedb_open(msg_ctx);
+ if (t == NULL) {
+ return;
+ }
+
+ if (tdb_lock_bystring(t->tdb, name) != 0) {
+ talloc_free(t);
+ return;
+ }
+ rec = tdb_fetch_bystring(t->tdb, name);
+ if (rec.dptr == NULL) {
+ tdb_unlock_bystring(t->tdb, name);
+ talloc_free(t);
+ return;
+ }
+ count = rec.dsize / sizeof(struct server_id);
+ if (count == 0) {
+ free(rec.dptr);
+ tdb_unlock_bystring(t->tdb, name);
+ talloc_free(t);
+ return;
+ }
+ ids = (struct server_id *)rec.dptr;
+ for (i=0;i<count;i++) {
+ if (cluster_id_equal(&ids[i], &msg_ctx->server_id)) {
+ if (i < count-1) {
+ memmove(ids+i, ids+i+1,
+ sizeof(struct server_id) * (count-(i+1)));
+ }
+ rec.dsize -= sizeof(struct server_id);
+ break;
+ }
+ }
+ tdb_store_bystring(t->tdb, name, rec, 0);
+ free(rec.dptr);
+ tdb_unlock_bystring(t->tdb, name);
+ talloc_free(t);
+}
+
+struct server_id messaging_get_server_id(struct messaging_context *msg_ctx)
+{
+ return msg_ctx->server_id;
+}
diff --git a/source4/lib/messaging/messaging.h b/source4/lib/messaging/messaging.h
new file mode 100644
index 0000000000..c91a31d285
--- /dev/null
+++ b/source4/lib/messaging/messaging.h
@@ -0,0 +1,39 @@
+/*
+ Unix SMB/CIFS implementation.
+ messages.c header
+ Copyright (C) Andrew Tridgell 2000
+ Copyright (C) 2001, 2002 by Martin Pool
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _MESSAGES_H_
+#define _MESSAGES_H_
+
+struct messaging_context;
+
+/* general messages */
+#define MSG_DEBUG 1
+#define MSG_PING 2
+#define MSG_PONG 3
+#define MSG_BRL_RETRY 4
+#define MSG_PVFS_RETRY_OPEN 5
+#define MSG_IRPC 6
+#define MSG_PVFS_NOTIFY 7
+#define MSG_NTVFS_OPLOCK_BREAK 8
+
+/* temporary messaging endpoints are allocated above this line */
+#define MSG_TMP_BASE 1000
+
+#endif
diff --git a/source4/lib/messaging/pymessaging.c b/source4/lib/messaging/pymessaging.c
new file mode 100644
index 0000000000..96981895b6
--- /dev/null
+++ b/source4/lib/messaging/pymessaging.c
@@ -0,0 +1,578 @@
+/*
+ Unix SMB/CIFS implementation.
+ Copyright © Jelmer Vernooij <jelmer@samba.org> 2008
+
+ Based on the equivalent for EJS:
+ Copyright © Andrew Tridgell <tridge@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include <Python.h>
+#include "scripting/python/modules.h"
+#include "libcli/util/pyerrors.h"
+#include "librpc/rpc/pyrpc.h"
+#include "lib/messaging/irpc.h"
+#include "lib/messaging/messaging.h"
+#include "lib/events/events.h"
+#include "cluster/cluster.h"
+#include "param/param.h"
+
+#ifndef Py_RETURN_NONE
+#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
+#endif
+
+PyAPI_DATA(PyTypeObject) messaging_Type;
+PyAPI_DATA(PyTypeObject) irpc_ClientConnectionType;
+
+/* FIXME: This prototype should be in param/pyparam.h */
+struct loadparm_context *py_default_loadparm_context(TALLOC_CTX *mem_ctx);
+
+/* FIXME: This prototype should be in py_irpc.h, or shared otherwise */
+extern const struct PyNdrRpcMethodDef py_ndr_irpc_methods[];
+
+static bool server_id_from_py(PyObject *object, struct server_id *server_id)
+{
+ if (!PyTuple_Check(object)) {
+ PyErr_SetString(PyExc_ValueError, "Expected tuple");
+ return false;
+ }
+
+ if (PyTuple_Size(object) == 3) {
+ return PyArg_ParseTuple(object, "iii", &server_id->id, &server_id->id2, &server_id->node);
+ } else {
+ int id, id2;
+ if (!PyArg_ParseTuple(object, "ii", &id, &id2))
+ return false;
+ *server_id = cluster_id(id, id2);
+ return true;
+ }
+}
+
+typedef struct {
+ PyObject_HEAD
+ TALLOC_CTX *mem_ctx;
+ struct messaging_context *msg_ctx;
+} messaging_Object;
+
+PyObject *py_messaging_connect(PyTypeObject *self, PyObject *args, PyObject *kwargs)
+{
+ struct tevent_context *ev;
+ const char *kwnames[] = { "own_id", "messaging_path", NULL };
+ PyObject *own_id = Py_None;
+ const char *messaging_path = NULL;
+ messaging_Object *ret;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|Oz:connect",
+ discard_const_p(char *, kwnames), &own_id, &messaging_path)) {
+ return NULL;
+ }
+
+ ret = PyObject_New(messaging_Object, &messaging_Type);
+ if (ret == NULL)
+ return NULL;
+
+ ret->mem_ctx = talloc_new(NULL);
+
+ ev = s4_event_context_init(ret->mem_ctx);
+
+ if (messaging_path == NULL) {
+ messaging_path = lp_messaging_path(ret->mem_ctx,
+ py_default_loadparm_context(ret->mem_ctx));
+ } else {
+ messaging_path = talloc_strdup(ret->mem_ctx, messaging_path);
+ }
+
+ if (own_id != Py_None) {
+ struct server_id server_id;
+
+ if (!server_id_from_py(own_id, &server_id))
+ return NULL;
+
+ ret->msg_ctx = messaging_init(ret->mem_ctx,
+ messaging_path,
+ server_id,
+ py_iconv_convenience(ret->mem_ctx),
+ ev);
+ } else {
+ ret->msg_ctx = messaging_client_init(ret->mem_ctx,
+ messaging_path,
+ py_iconv_convenience(ret->mem_ctx),
+ ev);
+ }
+
+ if (ret->msg_ctx == NULL) {
+ PyErr_SetString(PyExc_RuntimeError, "messaging_connect unable to create a messaging context");
+ talloc_free(ret->mem_ctx);
+ return NULL;
+ }
+
+ return (PyObject *)ret;
+}
+
+static void py_messaging_dealloc(PyObject *self)
+{
+ messaging_Object *iface = (messaging_Object *)self;
+ talloc_free(iface->msg_ctx);
+ PyObject_Del(self);
+}
+
+static PyObject *py_messaging_send(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ messaging_Object *iface = (messaging_Object *)self;
+ uint32_t msg_type;
+ DATA_BLOB data;
+ PyObject *target;
+ NTSTATUS status;
+ struct server_id server;
+ const char *kwnames[] = { "target", "msg_type", "data", NULL };
+ int length;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "Ois#|:send",
+ discard_const_p(char *, kwnames), &target, &msg_type, &data.data, &length)) {
+ return NULL;
+ }
+
+ data.length = length;
+
+ if (!server_id_from_py(target, &server))
+ return NULL;
+
+ status = messaging_send(iface->msg_ctx, server, msg_type, &data);
+ if (NT_STATUS_IS_ERR(status)) {
+ PyErr_SetNTSTATUS(status);
+ return NULL;
+ }
+
+ Py_RETURN_NONE;
+}
+
+static void py_msg_callback_wrapper(struct messaging_context *msg, void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id, DATA_BLOB *data)
+{
+ PyObject *callback = (PyObject *)private_data;
+
+ PyObject_CallFunction(callback, discard_const_p(char, "i(iii)s#"), msg_type,
+ server_id.id, server_id.id2, server_id.node,
+ data->data, data->length);
+}
+
+static PyObject *py_messaging_register(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ messaging_Object *iface = (messaging_Object *)self;
+ int msg_type = -1;
+ PyObject *callback;
+ NTSTATUS status;
+ const char *kwnames[] = { "callback", "msg_type", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|i:send",
+ discard_const_p(char *, kwnames), &callback, &msg_type)) {
+ return NULL;
+ }
+
+ Py_INCREF(callback);
+
+ if (msg_type == -1) {
+ uint32_t msg_type32 = msg_type;
+ status = messaging_register_tmp(iface->msg_ctx, callback,
+ py_msg_callback_wrapper, &msg_type32);
+ msg_type = msg_type32;
+ } else {
+ status = messaging_register(iface->msg_ctx, callback,
+ msg_type, py_msg_callback_wrapper);
+ }
+ if (NT_STATUS_IS_ERR(status)) {
+ PyErr_SetNTSTATUS(status);
+ return NULL;
+ }
+
+ return PyLong_FromLong(msg_type);
+}
+
+static PyObject *py_messaging_deregister(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ messaging_Object *iface = (messaging_Object *)self;
+ int msg_type = -1;
+ PyObject *callback;
+ const char *kwnames[] = { "callback", "msg_type", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|i:send",
+ discard_const_p(char *, kwnames), &callback, &msg_type)) {
+ return NULL;
+ }
+
+ messaging_deregister(iface->msg_ctx, msg_type, callback);
+
+ Py_DECREF(callback);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_messaging_add_name(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ messaging_Object *iface = (messaging_Object *)self;
+ NTSTATUS status;
+ char *name;
+ const char *kwnames[] = { "name", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|:send",
+ discard_const_p(char *, kwnames), &name)) {
+ return NULL;
+ }
+
+ status = irpc_add_name(iface->msg_ctx, name);
+ if (NT_STATUS_IS_ERR(status)) {
+ PyErr_SetNTSTATUS(status);
+ return NULL;
+ }
+
+ Py_RETURN_NONE;
+}
+
+
+static PyObject *py_messaging_remove_name(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ messaging_Object *iface = (messaging_Object *)self;
+ char *name;
+ const char *kwnames[] = { "name", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|:send",
+ discard_const_p(char *, kwnames), &name)) {
+ return NULL;
+ }
+
+ irpc_remove_name(iface->msg_ctx, name);
+
+ Py_RETURN_NONE;
+}
+
+static PyMethodDef py_messaging_methods[] = {
+ { "send", (PyCFunction)py_messaging_send, METH_VARARGS|METH_KEYWORDS,
+ "S.send(target, msg_type, data) -> None\nSend a message" },
+ { "register", (PyCFunction)py_messaging_register, METH_VARARGS|METH_KEYWORDS,
+ "S.register(callback, msg_type=None) -> msg_type\nRegister a message handler" },
+ { "deregister", (PyCFunction)py_messaging_deregister, METH_VARARGS|METH_KEYWORDS,
+ "S.deregister(callback, msg_type) -> None\nDeregister a message handler" },
+ { "add_name", (PyCFunction)py_messaging_add_name, METH_VARARGS|METH_KEYWORDS, "S.add_name(name) -> None\nListen on another name" },
+ { "remove_name", (PyCFunction)py_messaging_remove_name, METH_VARARGS|METH_KEYWORDS, "S.remove_name(name) -> None\nStop listening on a name" },
+ { NULL, NULL, 0, NULL }
+};
+
+static PyObject *py_messaging_server_id(PyObject *obj, void *closure)
+{
+ messaging_Object *iface = (messaging_Object *)obj;
+ struct server_id server_id = messaging_get_server_id(iface->msg_ctx);
+
+ return Py_BuildValue("(iii)", server_id.id, server_id.id2,
+ server_id.node);
+}
+
+static PyGetSetDef py_messaging_getset[] = {
+ { discard_const_p(char, "server_id"), py_messaging_server_id, NULL,
+ discard_const_p(char, "local server id") },
+ { NULL },
+};
+
+
+PyTypeObject messaging_Type = {
+ PyObject_HEAD_INIT(NULL) 0,
+ .tp_name = "irpc.Messaging",
+ .tp_basicsize = sizeof(messaging_Object),
+ .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,
+ .tp_new = py_messaging_connect,
+ .tp_dealloc = py_messaging_dealloc,
+ .tp_methods = py_messaging_methods,
+ .tp_getset = py_messaging_getset,
+ .tp_doc = "Messaging(own_id=None, messaging_path=None)\n" \
+ "Create a new object that can be used to communicate with the peers in the specified messaging path.\n" \
+ "If no path is specified, the default path from smb.conf will be used."
+};
+
+
+/*
+ state of a irpc 'connection'
+*/
+typedef struct {
+ PyObject_HEAD
+ const char *server_name;
+ struct server_id *dest_ids;
+ struct messaging_context *msg_ctx;
+ TALLOC_CTX *mem_ctx;
+} irpc_ClientConnectionObject;
+
+/*
+ setup a context for talking to a irpc server
+ example:
+ status = irpc.connect("smb_server");
+*/
+
+PyObject *py_irpc_connect(PyTypeObject *self, PyObject *args, PyObject *kwargs)
+{
+ struct tevent_context *ev;
+ const char *kwnames[] = { "server", "own_id", "messaging_path", NULL };
+ char *server;
+ const char *messaging_path = NULL;
+ PyObject *own_id = Py_None;
+ irpc_ClientConnectionObject *ret;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|Oz:connect",
+ discard_const_p(char *, kwnames), &server, &own_id, &messaging_path)) {
+ return NULL;
+ }
+
+ ret = PyObject_New(irpc_ClientConnectionObject, &irpc_ClientConnectionType);
+ if (ret == NULL)
+ return NULL;
+
+ ret->mem_ctx = talloc_new(NULL);
+
+ ret->server_name = server;
+
+ ev = s4_event_context_init(ret->mem_ctx);
+
+ if (messaging_path == NULL) {
+ messaging_path = lp_messaging_path(ret->mem_ctx,
+ py_default_loadparm_context(ret->mem_ctx));
+ } else {
+ messaging_path = talloc_strdup(ret->mem_ctx, messaging_path);
+ }
+
+ if (own_id != Py_None) {
+ struct server_id server_id;
+
+ if (!server_id_from_py(own_id, &server_id))
+ return NULL;
+
+ ret->msg_ctx = messaging_init(ret->mem_ctx,
+ messaging_path,
+ server_id,
+ py_iconv_convenience(ret->mem_ctx),
+ ev);
+ } else {
+ ret->msg_ctx = messaging_client_init(ret->mem_ctx,
+ messaging_path,
+ py_iconv_convenience(ret->mem_ctx),
+ ev);
+ }
+
+ if (ret->msg_ctx == NULL) {
+ PyErr_SetString(PyExc_RuntimeError, "irpc_connect unable to create a messaging context");
+ talloc_free(ret->mem_ctx);
+ return NULL;
+ }
+
+ ret->dest_ids = irpc_servers_byname(ret->msg_ctx, ret->mem_ctx, ret->server_name);
+ if (ret->dest_ids == NULL || ret->dest_ids[0].id == 0) {
+ talloc_free(ret->mem_ctx);
+ PyErr_SetNTSTATUS(NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ return NULL;
+ } else {
+ return (PyObject *)ret;
+ }
+}
+
+typedef struct {
+ PyObject_HEAD
+ struct irpc_request **reqs;
+ int count;
+ int current;
+ TALLOC_CTX *mem_ctx;
+ py_data_unpack_fn unpack_fn;
+} irpc_ResultObject;
+
+
+static PyObject *irpc_result_next(irpc_ResultObject *iterator)
+{
+ NTSTATUS status;
+
+ if (iterator->current >= iterator->count) {
+ PyErr_SetString(PyExc_StopIteration, "No more results");
+ return NULL;
+ }
+
+ status = irpc_call_recv(iterator->reqs[iterator->current]);
+ iterator->current++;
+ if (!NT_STATUS_IS_OK(status)) {
+ PyErr_SetNTSTATUS(status);
+ return NULL;
+ }
+
+ return iterator->unpack_fn(iterator->reqs[iterator->current-1]->r);
+}
+
+static PyObject *irpc_result_len(irpc_ResultObject *self)
+{
+ return PyLong_FromLong(self->count);
+}
+
+static PyMethodDef irpc_result_methods[] = {
+ { "__len__", (PyCFunction)irpc_result_len, METH_NOARGS,
+ "Number of elements returned"},
+ { NULL }
+};
+
+static void irpc_result_dealloc(PyObject *self)
+{
+ talloc_free(((irpc_ResultObject *)self)->mem_ctx);
+ PyObject_Del(self);
+}
+
+PyTypeObject irpc_ResultIteratorType = {
+ PyObject_HEAD_INIT(NULL) 0,
+ .tp_name = "irpc.ResultIterator",
+ .tp_basicsize = sizeof(irpc_ResultObject),
+ .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,
+ .tp_iternext = (iternextfunc)irpc_result_next,
+ .tp_iter = PyObject_SelfIter,
+ .tp_methods = irpc_result_methods,
+ .tp_dealloc = irpc_result_dealloc,
+};
+
+static PyObject *py_irpc_call(irpc_ClientConnectionObject *p, struct PyNdrRpcMethodDef *method_def, PyObject *args, PyObject *kwargs)
+{
+ void *ptr;
+ struct irpc_request **reqs;
+ int i, count;
+ NTSTATUS status;
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+ irpc_ResultObject *ret;
+
+ /* allocate the C structure */
+ ptr = talloc_zero_size(mem_ctx, method_def->table->calls[method_def->opnum].struct_size);
+ if (ptr == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ /* convert the mpr object into a C structure */
+ if (!method_def->pack_in_data(args, kwargs, ptr)) {
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ for (count=0;p->dest_ids[count].id;count++) /* noop */ ;
+
+ /* we need to make a call per server */
+ reqs = talloc_array(mem_ctx, struct irpc_request *, count);
+ if (reqs == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ /* make the actual calls */
+ for (i=0;i<count;i++) {
+ reqs[i] = irpc_call_send(p->msg_ctx, p->dest_ids[i],
+ method_def->table, method_def->opnum, ptr, ptr);
+ if (reqs[i] == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ talloc_steal(reqs, reqs[i]);
+ }
+
+ ret = PyObject_New(irpc_ResultObject, &irpc_ResultIteratorType);
+ ret->mem_ctx = mem_ctx;
+ ret->reqs = reqs;
+ ret->count = count;
+ ret->current = 0;
+ ret->unpack_fn = method_def->unpack_out_data;
+
+ return (PyObject *)ret;
+done:
+ talloc_free(mem_ctx);
+ PyErr_SetNTSTATUS(status);
+ return NULL;
+}
+
+static PyObject *py_irpc_call_wrapper(PyObject *self, PyObject *args, void *wrapped, PyObject *kwargs)
+{
+ irpc_ClientConnectionObject *iface = (irpc_ClientConnectionObject *)self;
+ struct PyNdrRpcMethodDef *md = wrapped;
+
+ return py_irpc_call(iface, md, args, kwargs);
+}
+
+static void py_irpc_dealloc(PyObject *self)
+{
+ irpc_ClientConnectionObject *iface = (irpc_ClientConnectionObject *)self;
+ talloc_free(iface->mem_ctx);
+ PyObject_Del(self);
+}
+
+PyTypeObject irpc_ClientConnectionType = {
+ PyObject_HEAD_INIT(NULL) 0,
+ .tp_name = "irpc.ClientConnection",
+ .tp_basicsize = sizeof(irpc_ClientConnectionObject),
+ .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,
+ .tp_new = py_irpc_connect,
+ .tp_dealloc = py_irpc_dealloc,
+ .tp_doc = "ClientConnection(server, own_id=None, messaging_path=None)\n" \
+ "Create a new IRPC client connection to communicate with the servers in the specified path.\n" \
+ "If no path is specified, the default path from smb.conf will be used."
+};
+
+static bool irpc_AddNdrRpcMethods(PyTypeObject *ifacetype, const struct PyNdrRpcMethodDef *mds)
+{
+ int i;
+ for (i = 0; mds[i].name; i++) {
+ PyObject *ret;
+ struct wrapperbase *wb = calloc(sizeof(struct wrapperbase), 1);
+
+ wb->name = discard_const_p(char, mds[i].name);
+ wb->flags = PyWrapperFlag_KEYWORDS;
+ wb->wrapper = (wrapperfunc)py_irpc_call_wrapper;
+ wb->doc = discard_const_p(char, mds[i].doc);
+
+ ret = PyDescr_NewWrapper(ifacetype, wb, discard_const_p(void, &mds[i]));
+
+ PyDict_SetItemString(ifacetype->tp_dict, mds[i].name,
+ (PyObject *)ret);
+ }
+
+ return true;
+}
+
+void initmessaging(void)
+{
+ PyObject *mod;
+ PyObject *dep_irpc;
+
+ dep_irpc = PyImport_ImportModule("samba.dcerpc.irpc");
+ if (dep_irpc == NULL)
+ return;
+
+ if (PyType_Ready(&irpc_ClientConnectionType) < 0)
+ return;
+
+ if (PyType_Ready(&messaging_Type) < 0)
+ return;
+
+ if (PyType_Ready(&irpc_ResultIteratorType) < 0)
+ return;
+
+ if (!irpc_AddNdrRpcMethods(&irpc_ClientConnectionType, py_ndr_irpc_methods))
+ return;
+
+ mod = Py_InitModule3("messaging", NULL, "Internal RPC");
+ if (mod == NULL)
+ return;
+
+ Py_INCREF((PyObject *)&irpc_ClientConnectionType);
+ PyModule_AddObject(mod, "ClientConnection", (PyObject *)&irpc_ClientConnectionType);
+
+ Py_INCREF((PyObject *)&messaging_Type);
+ PyModule_AddObject(mod, "Messaging", (PyObject *)&messaging_Type);
+}
diff --git a/source4/lib/messaging/tests/bindings.py b/source4/lib/messaging/tests/bindings.py
new file mode 100644
index 0000000000..c89538ddfa
--- /dev/null
+++ b/source4/lib/messaging/tests/bindings.py
@@ -0,0 +1,57 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Unix SMB/CIFS implementation.
+# Copyright © Jelmer Vernooij <jelmer@samba.org> 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 <http://www.gnu.org/licenses/>.
+#
+
+from samba.messaging import Messaging
+from unittest import TestCase
+
+class MessagingTests(TestCase):
+ def get_context(self, *args, **kwargs):
+ kwargs["messaging_path"] = "."
+ return Messaging(*args, **kwargs)
+
+ def test_register(self):
+ x = self.get_context()
+ def callback():
+ pass
+ msg_type = x.register(callback)
+ x.deregister(callback, msg_type)
+
+ def test_assign_server_id(self):
+ x = self.get_context()
+ self.assertTrue(isinstance(x.server_id, tuple))
+ self.assertEquals(3, len(x.server_id))
+
+ def test_ping_speed(self):
+ server_ctx = self.get_context((0, 1))
+ def ping_callback(src, data):
+ server_ctx.send(src, data)
+ def exit_callback():
+ print "received exit"
+ msg_ping = server_ctx.register(ping_callback)
+ msg_exit = server_ctx.register(exit_callback)
+
+ def pong_callback():
+ print "received pong"
+ client_ctx = self.get_context((0, 2))
+ msg_pong = client_ctx.register(pong_callback)
+
+ client_ctx.send((0,1), msg_ping, "testing")
+ client_ctx.send((0,1), msg_ping, "")
+
diff --git a/source4/lib/messaging/tests/irpc.c b/source4/lib/messaging/tests/irpc.c
new file mode 100644
index 0000000000..3eb23e0f7d
--- /dev/null
+++ b/source4/lib/messaging/tests/irpc.c
@@ -0,0 +1,271 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ local test for irpc code
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/events/events.h"
+#include "lib/messaging/irpc.h"
+#include "librpc/gen_ndr/ndr_echo.h"
+#include "torture/torture.h"
+#include "cluster/cluster.h"
+#include "param/param.h"
+
+const uint32_t MSG_ID1 = 1, MSG_ID2 = 2;
+
+static bool test_debug;
+
+struct irpc_test_data
+{
+ struct messaging_context *msg_ctx1, *msg_ctx2;
+ struct tevent_context *ev;
+};
+
+/*
+ serve up AddOne over the irpc system
+*/
+static NTSTATUS irpc_AddOne(struct irpc_message *irpc, struct echo_AddOne *r)
+{
+ *r->out.out_data = r->in.in_data + 1;
+ if (test_debug) {
+ printf("irpc_AddOne: in=%u in+1=%u out=%u\n",
+ r->in.in_data, r->in.in_data+1, *r->out.out_data);
+ }
+ return NT_STATUS_OK;
+}
+
+/*
+ a deferred reply to echodata
+*/
+static void deferred_echodata(struct tevent_context *ev, struct tevent_timer *te,
+ struct timeval t, void *private_data)
+{
+ struct irpc_message *irpc = talloc_get_type(private_data, struct irpc_message);
+ struct echo_EchoData *r = irpc->data;
+ r->out.out_data = talloc_memdup(r, r->in.in_data, r->in.len);
+ if (r->out.out_data == NULL) {
+ irpc_send_reply(irpc, NT_STATUS_NO_MEMORY);
+ }
+ printf("sending deferred reply\n");
+ irpc_send_reply(irpc, NT_STATUS_OK);
+}
+
+
+/*
+ serve up EchoData over the irpc system
+*/
+static NTSTATUS irpc_EchoData(struct irpc_message *irpc, struct echo_EchoData *r)
+{
+ irpc->defer_reply = true;
+ event_add_timed(irpc->ev, irpc, timeval_zero(), deferred_echodata, irpc);
+ return NT_STATUS_OK;
+}
+
+
+/*
+ test a addone call over the internal messaging system
+*/
+static bool test_addone(struct torture_context *test, const void *_data,
+ const void *_value)
+{
+ struct echo_AddOne r;
+ NTSTATUS status;
+ const struct irpc_test_data *data = (const struct irpc_test_data *)_data;
+ uint32_t value = *(const uint32_t *)_value;
+
+ /* make the call */
+ r.in.in_data = value;
+
+ test_debug = true;
+ status = IRPC_CALL(data->msg_ctx1, cluster_id(0, MSG_ID2),
+ rpcecho, ECHO_ADDONE, &r, test);
+ test_debug = false;
+ torture_assert_ntstatus_ok(test, status, "AddOne failed");
+
+ /* check the answer */
+ torture_assert(test, *r.out.out_data == r.in.in_data + 1,
+ "AddOne wrong answer");
+
+ torture_comment(test, "%u + 1 = %u\n", r.in.in_data, *r.out.out_data);
+ return true;
+}
+
+/*
+ test a echodata call over the internal messaging system
+*/
+static bool test_echodata(struct torture_context *tctx,
+ const void *tcase_data,
+ const void *test_data)
+{
+ struct echo_EchoData r;
+ NTSTATUS status;
+ const struct irpc_test_data *data = (const struct irpc_test_data *)tcase_data;
+ TALLOC_CTX *mem_ctx = tctx;
+
+ /* make the call */
+ r.in.in_data = (unsigned char *)talloc_strdup(mem_ctx, "0123456789");
+ r.in.len = strlen((char *)r.in.in_data);
+
+ status = IRPC_CALL(data->msg_ctx1, cluster_id(0, MSG_ID2),
+ rpcecho, ECHO_ECHODATA, &r,
+ mem_ctx);
+ torture_assert_ntstatus_ok(tctx, status, "EchoData failed");
+
+ /* check the answer */
+ if (memcmp(r.out.out_data, r.in.in_data, r.in.len) != 0) {
+ NDR_PRINT_OUT_DEBUG(echo_EchoData, &r);
+ torture_fail(tctx, "EchoData wrong answer");
+ }
+
+ torture_comment(tctx, "Echo '%*.*s' -> '%*.*s'\n",
+ r.in.len, r.in.len,
+ r.in.in_data,
+ r.in.len, r.in.len,
+ r.out.out_data);
+ return true;
+}
+
+
+static void irpc_callback(struct irpc_request *irpc)
+{
+ struct echo_AddOne *r = (struct echo_AddOne *)irpc->r;
+ int *pong_count = (int *)irpc->async.private_data;
+ NTSTATUS status = irpc_call_recv(irpc);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("irpc call failed - %s\n", nt_errstr(status));
+ }
+ if (*r->out.out_data != r->in.in_data + 1) {
+ printf("AddOne wrong answer - %u + 1 = %u should be %u\n",
+ r->in.in_data, *r->out.out_data, r->in.in_data+1);
+ }
+ (*pong_count)++;
+}
+
+/*
+ test echo speed
+*/
+static bool test_speed(struct torture_context *tctx,
+ const void *tcase_data,
+ const void *test_data)
+{
+ int ping_count = 0;
+ int pong_count = 0;
+ const struct irpc_test_data *data = (const struct irpc_test_data *)tcase_data;
+ struct timeval tv;
+ struct echo_AddOne r;
+ TALLOC_CTX *mem_ctx = tctx;
+ int timelimit = torture_setting_int(tctx, "timelimit", 10);
+
+ tv = timeval_current();
+
+ r.in.in_data = 0;
+
+ torture_comment(tctx, "Sending echo for %d seconds\n", timelimit);
+ while (timeval_elapsed(&tv) < timelimit) {
+ struct irpc_request *irpc;
+
+ irpc = IRPC_CALL_SEND(data->msg_ctx1, cluster_id(0, MSG_ID2),
+ rpcecho, ECHO_ADDONE,
+ &r, mem_ctx);
+ torture_assert(tctx, irpc != NULL, "AddOne send failed");
+
+ irpc->async.fn = irpc_callback;
+ irpc->async.private_data = &pong_count;
+
+ ping_count++;
+
+ while (ping_count > pong_count + 20) {
+ event_loop_once(data->ev);
+ }
+ }
+
+ torture_comment(tctx, "waiting for %d remaining replies (done %d)\n",
+ ping_count - pong_count, pong_count);
+ while (timeval_elapsed(&tv) < 30 && pong_count < ping_count) {
+ event_loop_once(data->ev);
+ }
+
+ torture_assert_int_equal(tctx, ping_count, pong_count, "ping test failed");
+
+ torture_comment(tctx, "echo rate of %.0f messages/sec\n",
+ (ping_count+pong_count)/timeval_elapsed(&tv));
+ return true;
+}
+
+
+static bool irpc_setup(struct torture_context *tctx, void **_data)
+{
+ struct irpc_test_data *data;
+
+ *_data = data = talloc(tctx, struct irpc_test_data);
+
+ lp_set_cmdline(tctx->lp_ctx, "pid directory", "piddir.tmp");
+
+ data->ev = tctx->ev;
+ torture_assert(tctx, data->msg_ctx1 =
+ messaging_init(tctx,
+ lp_messaging_path(tctx, tctx->lp_ctx),
+ cluster_id(0, MSG_ID1),
+ lp_iconv_convenience(tctx->lp_ctx),
+ data->ev),
+ "Failed to init first messaging context");
+
+ torture_assert(tctx, data->msg_ctx2 =
+ messaging_init(tctx,
+ lp_messaging_path(tctx, tctx->lp_ctx),
+ cluster_id(0, MSG_ID2),
+ lp_iconv_convenience(tctx->lp_ctx),
+ data->ev),
+ "Failed to init second messaging context");
+
+ /* register the server side function */
+ IRPC_REGISTER(data->msg_ctx1, rpcecho, ECHO_ADDONE, irpc_AddOne, NULL);
+ IRPC_REGISTER(data->msg_ctx2, rpcecho, ECHO_ADDONE, irpc_AddOne, NULL);
+
+ IRPC_REGISTER(data->msg_ctx1, rpcecho, ECHO_ECHODATA, irpc_EchoData, NULL);
+ IRPC_REGISTER(data->msg_ctx2, rpcecho, ECHO_ECHODATA, irpc_EchoData, NULL);
+
+ return true;
+}
+
+struct torture_suite *torture_local_irpc(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "IRPC");
+ struct torture_tcase *tcase = torture_suite_add_tcase(suite, "irpc");
+ int i;
+ uint32_t *values = talloc_array(tcase, uint32_t, 5);
+
+ values[0] = 0;
+ values[1] = 0x7FFFFFFE;
+ values[2] = 0xFFFFFFFE;
+ values[3] = 0xFFFFFFFF;
+ values[4] = random() & 0xFFFFFFFF;
+
+ tcase->setup = irpc_setup;
+
+ for (i = 0; i < 5; i++) {
+ torture_tcase_add_test_const(tcase, "addone", test_addone,
+ (void *)&values[i]);
+ }
+
+ torture_tcase_add_test_const(tcase, "echodata", test_echodata, NULL);
+ torture_tcase_add_test_const(tcase, "speed", test_speed, NULL);
+
+ return suite;
+}
diff --git a/source4/lib/messaging/tests/messaging.c b/source4/lib/messaging/tests/messaging.c
new file mode 100644
index 0000000000..f61132caac
--- /dev/null
+++ b/source4/lib/messaging/tests/messaging.c
@@ -0,0 +1,145 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ local test for messaging code
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/events/events.h"
+#include "lib/messaging/irpc.h"
+#include "torture/torture.h"
+#include "cluster/cluster.h"
+#include "param/param.h"
+
+
+static uint32_t msg_pong;
+
+static void ping_message(struct messaging_context *msg, void *private_data,
+ uint32_t msg_type, struct server_id src, DATA_BLOB *data)
+{
+ NTSTATUS status;
+ status = messaging_send(msg, src, msg_pong, data);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("pong failed - %s\n", nt_errstr(status));
+ }
+}
+
+static void pong_message(struct messaging_context *msg, void *private_data,
+ uint32_t msg_type, struct server_id src, DATA_BLOB *data)
+{
+ int *count = (int *)private_data;
+ (*count)++;
+}
+
+static void exit_message(struct messaging_context *msg, void *private_data,
+ uint32_t msg_type, struct server_id src, DATA_BLOB *data)
+{
+ talloc_free(private_data);
+ exit(0);
+}
+
+/*
+ test ping speed
+*/
+static bool test_ping_speed(struct torture_context *tctx)
+{
+ struct tevent_context *ev;
+ struct messaging_context *msg_client_ctx;
+ struct messaging_context *msg_server_ctx;
+ int ping_count = 0;
+ int pong_count = 0;
+ struct timeval tv;
+ int timelimit = torture_setting_int(tctx, "timelimit", 10);
+ uint32_t msg_ping, msg_exit;
+
+ lp_set_cmdline(tctx->lp_ctx, "pid directory", "piddir.tmp");
+
+ ev = tctx->ev;
+
+ msg_server_ctx = messaging_init(tctx,
+ lp_messaging_path(tctx, tctx->lp_ctx),
+ cluster_id(0, 1),
+ lp_iconv_convenience(tctx->lp_ctx),
+ ev);
+
+ torture_assert(tctx, msg_server_ctx != NULL, "Failed to init ping messaging context");
+
+ messaging_register_tmp(msg_server_ctx, NULL, ping_message, &msg_ping);
+ messaging_register_tmp(msg_server_ctx, tctx, exit_message, &msg_exit);
+
+ msg_client_ctx = messaging_init(tctx,
+ lp_messaging_path(tctx, tctx->lp_ctx),
+ cluster_id(0, 2),
+ lp_iconv_convenience(tctx->lp_ctx),
+ ev);
+
+ torture_assert(tctx, msg_client_ctx != NULL,
+ "msg_client_ctx messaging_init() failed");
+
+ messaging_register_tmp(msg_client_ctx, &pong_count, pong_message, &msg_pong);
+
+ tv = timeval_current();
+
+ torture_comment(tctx, "Sending pings for %d seconds\n", timelimit);
+ while (timeval_elapsed(&tv) < timelimit) {
+ DATA_BLOB data;
+ NTSTATUS status1, status2;
+
+ data.data = discard_const_p(uint8_t, "testing");
+ data.length = strlen((const char *)data.data);
+
+ status1 = messaging_send(msg_client_ctx, cluster_id(0, 1), msg_ping, &data);
+ status2 = messaging_send(msg_client_ctx, cluster_id(0, 1), msg_ping, NULL);
+
+ torture_assert_ntstatus_ok(tctx, status1, "msg1 failed");
+ ping_count++;
+
+ torture_assert_ntstatus_ok(tctx, status2, "msg2 failed");
+ ping_count++;
+
+ while (ping_count > pong_count + 20) {
+ event_loop_once(ev);
+ }
+ }
+
+ torture_comment(tctx, "waiting for %d remaining replies (done %d)\n",
+ ping_count - pong_count, pong_count);
+ while (timeval_elapsed(&tv) < 30 && pong_count < ping_count) {
+ event_loop_once(ev);
+ }
+
+ torture_comment(tctx, "sending exit\n");
+ messaging_send(msg_client_ctx, cluster_id(0, 1), msg_exit, NULL);
+
+ torture_assert_int_equal(tctx, ping_count, pong_count, "ping test failed");
+
+ torture_comment(tctx, "ping rate of %.0f messages/sec\n",
+ (ping_count+pong_count)/timeval_elapsed(&tv));
+
+ talloc_free(msg_client_ctx);
+ talloc_free(msg_server_ctx);
+
+ return true;
+}
+
+struct torture_suite *torture_local_messaging(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *s = torture_suite_create(mem_ctx, "MESSAGING");
+ torture_suite_add_simple_test(s, "ping_speed", test_ping_speed);
+ return s;
+}
diff --git a/source4/lib/registry/Doxyfile b/source4/lib/registry/Doxyfile
new file mode 100644
index 0000000000..efc01cd355
--- /dev/null
+++ b/source4/lib/registry/Doxyfile
@@ -0,0 +1,24 @@
+PROJECT_NAME = REGISTRY
+OUTPUT_DIRECTORY = apidocs
+BRIEF_MEMBER_DESC = YES
+REPEAT_BRIEF = YES
+OPTIMIZE_OUTPUT_FOR_C = YES
+SORT_MEMBER_DOCS = YES
+SORT_BRIEF_DOCS = NO
+GENERATE_TODOLIST = YES
+GENERATE_BUGLIST = YES
+GENERATE_DEPRECATEDLIST= YES
+SHOW_USED_FILES = NO
+SHOW_DIRECTORIES = NO
+WARNINGS = YES
+WARN_IF_UNDOCUMENTED = YES
+WARN_IF_DOC_ERROR = YES
+WARN_NO_PARAMDOC = NO
+WARN_FORMAT = "$file:$line: $text"
+INPUT = .
+FILE_PATTERNS = *.c *.h *.dox
+GENERATE_HTML = YES
+HTML_OUTPUT = html
+GENERATE_MAN = YES
+ALWAYS_DETAILED_SEC = YES
+JAVADOC_AUTOBRIEF = YES
diff --git a/source4/lib/registry/README b/source4/lib/registry/README
new file mode 100644
index 0000000000..07b2c01684
--- /dev/null
+++ b/source4/lib/registry/README
@@ -0,0 +1,42 @@
+This is the registry library. The registry is basically a bunch of
+hives, each of which is loaded from a file. When using a local registry,
+it is possible to specify where hives should be loaded from, etc.
+
+There are separate APIs for accessing the data in a hive and the
+data in the registry itself. Each supports different backends.
+
+The following "full registry" backends are currently provided:
+
+ * Remote (over DCE/RPC)
+ * Local (allows "mounting" hives)
+ * Wine (uses the wine plain-text file)
+
+The following hive backends are supported:
+
+ - ldb
+ - regf (NTUSER.DAT-style files)
+ - rpc (Remote individual hives)
+ - directory
+
+reg_open_samba() loads a set of hives based on smb.conf settings.
+Lines in smb.conf should have the following syntax:
+
+registry:<hivename> = <backend>:<location>
+
+So an example usage could be:
+
+registry:HKEY_CURRENT_USER = regf:NTUSER.DAT
+registry:HKEY_LOCAL_MACHINE = ldb:tdb://registry.tdb
+
+WERR_NOT_SUPPORTED will be returned for all hives that haven't been set.
+
+On Windows the various registry hives are loaded from:
+
+HKEY_CURRENT_CONFIG: %SystemRoot%\System32\Config\System
+HKEY_CURRENT_USER: %Profile%\NTUser.dat
+HKEY_LOCAL_MACHINE\SAM: %SystemRoot%\System32\Config\Sam
+HKEY_LOCAL_MACHINE\Security: %SystemRoot%\System32\Config\Security
+HKEY_LOCAL_MACHINE\Software: %SystemRoot%\System32\Config\Software
+HKEY_LOCAL_MACHINE\System: %SystemRoot%\System32\Config\System
+HKEY_USERS\.DEFAULT: %SystemRoot%\System32\Config\Default
+HKEY_LOCAL_MACHINE\HARDWARE: is autogenerated
diff --git a/source4/lib/registry/TODO b/source4/lib/registry/TODO
new file mode 100644
index 0000000000..b5809b84ed
--- /dev/null
+++ b/source4/lib/registry/TODO
@@ -0,0 +1,5 @@
+- ..\..\, \bla\blie support in regshell
+- finish rpc_server
+
+regshell:
+ - support for security descriptors
diff --git a/source4/lib/registry/config.mk b/source4/lib/registry/config.mk
new file mode 100644
index 0000000000..a566042cf2
--- /dev/null
+++ b/source4/lib/registry/config.mk
@@ -0,0 +1,110 @@
+[SUBSYSTEM::TDR_REGF]
+PUBLIC_DEPENDENCIES = TDR
+
+TDR_REGF_OBJ_FILES = $(libregistrysrcdir)/tdr_regf.o
+
+# Special support for external builddirs
+$(libregistrysrcdir)/regf.c: $(libregistrysrcdir)/tdr_regf.c
+$(libregistrysrcdir)/tdr_regf.h: $(libregistrysrcdir)/tdr_regf.c
+$(libregistrysrcdir)/tdr_regf.c: $(libregistrysrcdir)/regf.idl
+ @CPP="$(CPP)" $(PERL) $(pidldir)/pidl $(PIDL_ARGS) \
+ --header --outputdir=$(libregistrysrcdir) \
+ --tdr-parser -- $(libregistrysrcdir)/regf.idl
+
+clean::
+ @-rm -f $(libregistrysrcdir)/regf.h $(libregistrysrcdir)/tdr_regf*
+
+################################################
+# Start SUBSYSTEM registry
+[LIBRARY::registry]
+PUBLIC_DEPENDENCIES = \
+ LIBSAMBA-UTIL CHARSET TDR_REGF LIBLDB \
+ RPC_NDR_WINREG LDB_WRAP
+# End MODULE registry_ldb
+################################################
+
+PC_FILES += $(libregistrysrcdir)/registry.pc
+
+registry_VERSION = 0.0.1
+registry_SOVERSION = 0
+
+registry_OBJ_FILES = $(addprefix $(libregistrysrcdir)/, interface.o util.o samba.o \
+ patchfile_dotreg.o patchfile_preg.o patchfile.o regf.o \
+ hive.o local.o ldb.o dir.o rpc.o)
+
+PUBLIC_HEADERS += $(libregistrysrcdir)/registry.h
+
+[SUBSYSTEM::registry_common]
+PUBLIC_DEPENDENCIES = registry
+
+registry_common_OBJ_FILES = $(libregistrysrcdir)/tools/common.o
+
+$(eval $(call proto_header_template,$(libregistrysrcdir)/tools/common.h,$(registry_common_OBJ_FILES:.o=.c)))
+
+################################################
+# Start BINARY regdiff
+[BINARY::regdiff]
+INSTALLDIR = BINDIR
+PRIVATE_DEPENDENCIES = \
+ LIBSAMBA-HOSTCONFIG registry LIBPOPT POPT_SAMBA POPT_CREDENTIALS
+# End BINARY regdiff
+################################################
+
+regdiff_OBJ_FILES = $(libregistrysrcdir)/tools/regdiff.o
+
+MANPAGES += $(libregistrysrcdir)/man/regdiff.1
+
+################################################
+# Start BINARY regpatch
+[BINARY::regpatch]
+INSTALLDIR = BINDIR
+PRIVATE_DEPENDENCIES = \
+ LIBSAMBA-HOSTCONFIG registry LIBPOPT POPT_SAMBA POPT_CREDENTIALS \
+ registry_common
+# End BINARY regpatch
+################################################
+
+regpatch_OBJ_FILES = $(libregistrysrcdir)/tools/regpatch.o
+
+MANPAGES += $(libregistrysrcdir)/man/regpatch.1
+
+################################################
+# Start BINARY regshell
+[BINARY::regshell]
+INSTALLDIR = BINDIR
+PRIVATE_DEPENDENCIES = \
+ LIBSAMBA-HOSTCONFIG LIBPOPT registry POPT_SAMBA POPT_CREDENTIALS \
+ SMBREADLINE registry_common
+# End BINARY regshell
+################################################
+
+regshell_OBJ_FILES = $(libregistrysrcdir)/tools/regshell.o
+
+MANPAGES += $(libregistrysrcdir)/man/regshell.1
+
+################################################
+# Start BINARY regtree
+[BINARY::regtree]
+INSTALLDIR = BINDIR
+PRIVATE_DEPENDENCIES = \
+ LIBSAMBA-HOSTCONFIG LIBPOPT registry POPT_SAMBA POPT_CREDENTIALS \
+ registry_common
+# End BINARY regtree
+################################################
+
+regtree_OBJ_FILES = $(libregistrysrcdir)/tools/regtree.o
+
+MANPAGES += $(libregistrysrcdir)/man/regtree.1
+
+[SUBSYSTEM::torture_registry]
+PRIVATE_DEPENDENCIES = torture registry
+
+torture_registry_OBJ_FILES = $(addprefix $(libregistrysrcdir)/tests/, generic.o hive.o diff.o registry.o)
+
+$(eval $(call proto_header_template,$(libregistrysrcdir)/tests/proto.h,$(torture_registry_OBJ_FILES:.o=.c)))
+
+[PYTHON::py_registry]
+LIBRARY_REALNAME = samba/registry.$(SHLIBEXT)
+PUBLIC_DEPENDENCIES = registry PYTALLOC pycredentials param
+
+py_registry_OBJ_FILES = $(libregistrysrcdir)/pyregistry.o
diff --git a/source4/lib/registry/dir.c b/source4/lib/registry/dir.c
new file mode 100644
index 0000000000..42946bceec
--- /dev/null
+++ b/source4/lib/registry/dir.c
@@ -0,0 +1,409 @@
+/*
+ Unix SMB/CIFS implementation.
+ Registry interface
+ Copyright (C) Jelmer Vernooij 2004-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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "registry.h"
+#include "system/dir.h"
+#include "system/filesys.h"
+
+struct dir_key {
+ struct hive_key key;
+ const char *path;
+};
+
+static struct hive_operations reg_backend_dir;
+
+static WERROR reg_dir_add_key(TALLOC_CTX *mem_ctx,
+ const struct hive_key *parent,
+ const char *name, const char *classname,
+ struct security_descriptor *desc,
+ struct hive_key **result)
+{
+ struct dir_key *dk = talloc_get_type(parent, struct dir_key);
+ char *path;
+ int ret;
+
+ path = talloc_asprintf(mem_ctx, "%s/%s", dk->path, name);
+ ret = mkdir(path, 0700);
+ if (ret == 0) {
+ struct dir_key *key = talloc(mem_ctx, struct dir_key);
+ key->key.ops = &reg_backend_dir;
+ key->path = talloc_steal(key, path);
+ *result = (struct hive_key *)key;
+ return WERR_OK;
+ }
+
+ if (errno == EEXIST)
+ return WERR_ALREADY_EXISTS;
+ printf("FAILED %s BECAUSE: %s\n", path, strerror(errno));
+ return WERR_GENERAL_FAILURE;
+}
+
+static WERROR reg_dir_delete_recursive(const char *name)
+{
+ DIR *d;
+ struct dirent *e;
+ WERROR werr;
+
+ d = opendir(name);
+ if (d == NULL) {
+ DEBUG(3,("Unable to open '%s': %s\n", name,
+ strerror(errno)));
+ return WERR_BADFILE;
+ }
+
+ while((e = readdir(d))) {
+ char *path;
+ struct stat stbuf;
+
+ if (ISDOT(e->d_name) || ISDOTDOT(e->d_name))
+ continue;
+
+ path = talloc_asprintf(name, "%s/%s", name, e->d_name);
+ if (!path)
+ return WERR_NOMEM;
+
+ stat(path, &stbuf);
+
+ if (!S_ISDIR(stbuf.st_mode)) {
+ if (unlink(path) < 0) {
+ talloc_free(path);
+ closedir(d);
+ return WERR_GENERAL_FAILURE;
+ }
+ } else {
+ werr = reg_dir_delete_recursive(path);
+ if (!W_ERROR_IS_OK(werr)) {
+ talloc_free(path);
+ closedir(d);
+ return werr;
+ }
+ }
+
+ talloc_free(path);
+ }
+ closedir(d);
+
+ if (rmdir(name) == 0)
+ return WERR_OK;
+ else if (errno == ENOENT)
+ return WERR_BADFILE;
+ else
+ return WERR_GENERAL_FAILURE;
+}
+
+static WERROR reg_dir_del_key(const struct hive_key *k, const char *name)
+{
+ struct dir_key *dk = talloc_get_type(k, struct dir_key);
+ char *child = talloc_asprintf(NULL, "%s/%s", dk->path, name);
+ WERROR ret;
+
+ ret = reg_dir_delete_recursive(child);
+
+ talloc_free(child);
+
+ return ret;
+}
+
+static WERROR reg_dir_open_key(TALLOC_CTX *mem_ctx,
+ const struct hive_key *parent,
+ const char *name, struct hive_key **subkey)
+{
+ DIR *d;
+ char *fullpath;
+ const struct dir_key *p = talloc_get_type(parent, struct dir_key);
+ struct dir_key *ret;
+
+ if (name == NULL) {
+ DEBUG(0, ("NULL pointer passed as directory name!"));
+ return WERR_INVALID_PARAM;
+ }
+
+ fullpath = talloc_asprintf(mem_ctx, "%s/%s", p->path, name);
+
+ d = opendir(fullpath);
+ if (d == NULL) {
+ DEBUG(3,("Unable to open '%s': %s\n", fullpath,
+ strerror(errno)));
+ return WERR_BADFILE;
+ }
+ closedir(d);
+ ret = talloc(mem_ctx, struct dir_key);
+ ret->key.ops = &reg_backend_dir;
+ ret->path = talloc_steal(ret, fullpath);
+ *subkey = (struct hive_key *)ret;
+ return WERR_OK;
+}
+
+static WERROR reg_dir_key_by_index(TALLOC_CTX *mem_ctx,
+ const struct hive_key *k, uint32_t idx,
+ const char **name,
+ const char **classname,
+ NTTIME *last_mod_time)
+{
+ struct dirent *e;
+ const struct dir_key *dk = talloc_get_type(k, struct dir_key);
+ int i = 0;
+ DIR *d;
+
+ d = opendir(dk->path);
+
+ if (d == NULL)
+ return WERR_INVALID_PARAM;
+
+ while((e = readdir(d))) {
+ if(!ISDOT(e->d_name) && !ISDOTDOT(e->d_name)) {
+ struct stat stbuf;
+ char *thispath;
+
+ /* Check if file is a directory */
+ asprintf(&thispath, "%s/%s", dk->path, e->d_name);
+ stat(thispath, &stbuf);
+
+ if (!S_ISDIR(stbuf.st_mode)) {
+ SAFE_FREE(thispath);
+ continue;
+ }
+
+ if (i == idx) {
+ struct stat st;
+ *name = talloc_strdup(mem_ctx, e->d_name);
+ *classname = NULL;
+ stat(thispath, &st);
+ unix_to_nt_time(last_mod_time, st.st_mtime);
+ SAFE_FREE(thispath);
+ closedir(d);
+ return WERR_OK;
+ }
+ i++;
+
+ SAFE_FREE(thispath);
+ }
+ }
+
+ closedir(d);
+
+ return WERR_NO_MORE_ITEMS;
+}
+
+WERROR reg_open_directory(TALLOC_CTX *parent_ctx,
+ const char *location, struct hive_key **key)
+{
+ struct dir_key *dk;
+
+ if (location == NULL)
+ return WERR_INVALID_PARAM;
+
+ dk = talloc(parent_ctx, struct dir_key);
+ dk->key.ops = &reg_backend_dir;
+ dk->path = talloc_strdup(dk, location);
+ *key = (struct hive_key *)dk;
+ return WERR_OK;
+}
+
+WERROR reg_create_directory(TALLOC_CTX *parent_ctx,
+ const char *location, struct hive_key **key)
+{
+ if (mkdir(location, 0700) != 0) {
+ *key = NULL;
+ return WERR_GENERAL_FAILURE;
+ }
+
+ return reg_open_directory(parent_ctx, location, key);
+}
+
+static WERROR reg_dir_get_info(TALLOC_CTX *ctx, const struct hive_key *key,
+ const char **classname,
+ uint32_t *num_subkeys,
+ uint32_t *num_values,
+ NTTIME *lastmod,
+ uint32_t *max_subkeynamelen,
+ uint32_t *max_valnamelen,
+ uint32_t *max_valbufsize)
+{
+ DIR *d;
+ const struct dir_key *dk = talloc_get_type(key, struct dir_key);
+ struct dirent *e;
+ struct stat st;
+
+ SMB_ASSERT(key != NULL);
+
+ if (classname != NULL)
+ *classname = NULL;
+
+ d = opendir(dk->path);
+ if (d == NULL)
+ return WERR_INVALID_PARAM;
+
+ if (num_subkeys != NULL)
+ *num_subkeys = 0;
+
+ if (num_values != NULL)
+ *num_values = 0;
+
+ if (max_subkeynamelen != NULL)
+ *max_subkeynamelen = 0;
+
+ if (max_valnamelen != NULL)
+ *max_valnamelen = 0;
+
+ if (max_valbufsize != NULL)
+ *max_valbufsize = 0;
+
+ while((e = readdir(d))) {
+ if(!ISDOT(e->d_name) && !ISDOTDOT(e->d_name)) {
+ char *path = talloc_asprintf(ctx, "%s/%s",
+ dk->path, e->d_name);
+
+ if (stat(path, &st) < 0) {
+ DEBUG(0, ("Error statting %s: %s\n", path,
+ strerror(errno)));
+ continue;
+ }
+
+ if (S_ISDIR(st.st_mode)) {
+ if (num_subkeys != NULL)
+ (*num_subkeys)++;
+ if (max_subkeynamelen != NULL)
+ *max_subkeynamelen = MAX(*max_subkeynamelen, strlen(e->d_name));
+ }
+
+ if (!S_ISDIR(st.st_mode)) {
+ if (num_values != NULL)
+ (*num_values)++;
+ if (max_valnamelen != NULL)
+ *max_valnamelen = MAX(*max_valnamelen, strlen(e->d_name));
+ if (max_valbufsize != NULL)
+ *max_valbufsize = MAX(*max_valbufsize, st.st_size);
+ }
+
+ talloc_free(path);
+ }
+ }
+
+ closedir(d);
+
+ if (lastmod != NULL)
+ *lastmod = 0;
+ return WERR_OK;
+}
+
+static WERROR reg_dir_set_value(struct hive_key *key, const char *name,
+ uint32_t type, const DATA_BLOB data)
+{
+ const struct dir_key *dk = talloc_get_type(key, struct dir_key);
+ char *path = talloc_asprintf(dk, "%s/%s", dk->path, name);
+
+ if (!file_save(path, data.data, data.length))
+ return WERR_GENERAL_FAILURE;
+
+ /* FIXME: Type */
+
+ return WERR_OK;
+}
+
+static WERROR reg_dir_get_value(TALLOC_CTX *mem_ctx,
+ struct hive_key *key, const char *name,
+ uint32_t *type, DATA_BLOB *data)
+{
+ const struct dir_key *dk = talloc_get_type(key, struct dir_key);
+ char *path = talloc_asprintf(mem_ctx, "%s/%s", dk->path, name);
+ size_t size;
+ char *contents;
+
+ contents = file_load(path, &size, 0, mem_ctx);
+ talloc_free(path);
+ if (contents == NULL)
+ return WERR_BADFILE;
+
+ if (type != NULL)
+ *type = 4; /* FIXME */
+
+ data->data = (uint8_t *)contents;
+ data->length = size;
+
+ return WERR_OK;
+}
+
+static WERROR reg_dir_enum_value(TALLOC_CTX *mem_ctx,
+ struct hive_key *key, int idx,
+ const char **name,
+ uint32_t *type, DATA_BLOB *data)
+{
+ const struct dir_key *dk = talloc_get_type(key, struct dir_key);
+ DIR *d;
+ struct dirent *e;
+ int i;
+
+ d = opendir(dk->path);
+ if (d == NULL) {
+ DEBUG(3,("Unable to open '%s': %s\n", dk->path,
+ strerror(errno)));
+ return WERR_BADFILE;
+ }
+
+ i = 0;
+ while((e = readdir(d))) {
+ if (ISDOT(e->d_name) || ISDOTDOT(e->d_name))
+ continue;
+
+ if (i == idx) {
+ if (name != NULL)
+ *name = talloc_strdup(mem_ctx, e->d_name);
+ W_ERROR_NOT_OK_RETURN(reg_dir_get_value(mem_ctx, key,
+ *name, type,
+ data));
+ return WERR_OK;
+ }
+
+ i++;
+ }
+ closedir(d);
+
+ return WERR_NO_MORE_ITEMS;
+}
+
+
+static WERROR reg_dir_del_value (struct hive_key *key, const char *name)
+{
+ const struct dir_key *dk = talloc_get_type(key, struct dir_key);
+ char *path = talloc_asprintf(key, "%s/%s", dk->path, name);
+ if (unlink(path) < 0) {
+ talloc_free(path);
+ if (errno == ENOENT)
+ return WERR_BADFILE;
+ return WERR_GENERAL_FAILURE;
+ }
+ talloc_free(path);
+
+ return WERR_OK;
+}
+
+static struct hive_operations reg_backend_dir = {
+ .name = "dir",
+ .get_key_by_name = reg_dir_open_key,
+ .get_key_info = reg_dir_get_info,
+ .add_key = reg_dir_add_key,
+ .del_key = reg_dir_del_key,
+ .enum_key = reg_dir_key_by_index,
+ .set_value = reg_dir_set_value,
+ .get_value_by_name = reg_dir_get_value,
+ .enum_value = reg_dir_enum_value,
+ .delete_value = reg_dir_del_value,
+};
diff --git a/source4/lib/registry/hive.c b/source4/lib/registry/hive.c
new file mode 100644
index 0000000000..8bf7c9f8d1
--- /dev/null
+++ b/source4/lib/registry/hive.c
@@ -0,0 +1,180 @@
+
+/*
+ Unix SMB/CIFS implementation.
+ Registry hive interface
+ Copyright (C) Jelmer Vernooij 2003-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 "registry.h"
+#include "system/filesys.h"
+#include "param/param.h"
+
+/** Open a registry file/host/etc */
+_PUBLIC_ WERROR reg_open_hive(TALLOC_CTX *parent_ctx, const char *location,
+ struct auth_session_info *session_info,
+ struct cli_credentials *credentials,
+ struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx,
+ struct hive_key **root)
+{
+ int fd, num;
+ char peek[20];
+
+ /* Check for directory */
+ if (directory_exist(location)) {
+ return reg_open_directory(parent_ctx, location, root);
+ }
+
+ fd = open(location, O_RDWR);
+ if (fd == -1) {
+ if (errno == ENOENT)
+ return WERR_BADFILE;
+ return WERR_BADFILE;
+ }
+
+ num = read(fd, peek, 20);
+ if (num == -1) {
+ return WERR_BADFILE;
+ }
+
+ if (!strncmp(peek, "regf", 4)) {
+ close(fd);
+ return reg_open_regf_file(parent_ctx, location, lp_iconv_convenience(lp_ctx), root);
+ } else if (!strncmp(peek, "TDB file", 8)) {
+ close(fd);
+ return reg_open_ldb_file(parent_ctx, location, session_info,
+ credentials, ev_ctx, lp_ctx, root);
+ }
+
+ return WERR_BADFILE;
+}
+
+_PUBLIC_ WERROR hive_key_get_info(TALLOC_CTX *mem_ctx,
+ const struct hive_key *key,
+ const char **classname, uint32_t *num_subkeys,
+ uint32_t *num_values,
+ NTTIME *last_change_time,
+ uint32_t *max_subkeynamelen,
+ uint32_t *max_valnamelen,
+ uint32_t *max_valbufsize)
+{
+ return key->ops->get_key_info(mem_ctx, key, classname, num_subkeys,
+ num_values, last_change_time,
+ max_subkeynamelen,
+ max_valnamelen, max_valbufsize);
+}
+
+_PUBLIC_ WERROR hive_key_add_name(TALLOC_CTX *ctx,
+ const struct hive_key *parent_key,
+ const char *name, const char *classname,
+ struct security_descriptor *desc,
+ struct hive_key **key)
+{
+ SMB_ASSERT(strchr(name, '\\') == NULL);
+
+ return parent_key->ops->add_key(ctx, parent_key, name, classname,
+ desc, key);
+}
+
+_PUBLIC_ WERROR hive_key_del(const struct hive_key *key, const char *name)
+{
+ return key->ops->del_key(key, name);
+}
+
+_PUBLIC_ WERROR hive_get_key_by_name(TALLOC_CTX *mem_ctx,
+ const struct hive_key *key,
+ const char *name,
+ struct hive_key **subkey)
+{
+ return key->ops->get_key_by_name(mem_ctx, key, name, subkey);
+}
+
+WERROR hive_enum_key(TALLOC_CTX *mem_ctx,
+ const struct hive_key *key, uint32_t idx,
+ const char **name,
+ const char **classname,
+ NTTIME *last_mod_time)
+{
+ return key->ops->enum_key(mem_ctx, key, idx, name, classname,
+ last_mod_time);
+}
+
+WERROR hive_key_set_value(struct hive_key *key, const char *name, uint32_t type,
+ const DATA_BLOB data)
+{
+ if (key->ops->set_value == NULL)
+ return WERR_NOT_SUPPORTED;
+
+ return key->ops->set_value(key, name, type, data);
+}
+
+WERROR hive_get_value(TALLOC_CTX *mem_ctx,
+ struct hive_key *key, const char *name,
+ uint32_t *type, DATA_BLOB *data)
+{
+ if (key->ops->get_value_by_name == NULL)
+ return WERR_NOT_SUPPORTED;
+
+ return key->ops->get_value_by_name(mem_ctx, key, name, type, data);
+}
+
+WERROR hive_get_value_by_index(TALLOC_CTX *mem_ctx,
+ struct hive_key *key, uint32_t idx,
+ const char **name,
+ uint32_t *type, DATA_BLOB *data)
+{
+ if (key->ops->enum_value == NULL)
+ return WERR_NOT_SUPPORTED;
+
+ return key->ops->enum_value(mem_ctx, key, idx, name, type, data);
+}
+
+WERROR hive_get_sec_desc(TALLOC_CTX *mem_ctx,
+ struct hive_key *key,
+ struct security_descriptor **security)
+{
+ if (key->ops->get_sec_desc == NULL)
+ return WERR_NOT_SUPPORTED;
+
+ return key->ops->get_sec_desc(mem_ctx, key, security);
+}
+
+WERROR hive_set_sec_desc(struct hive_key *key,
+ const struct security_descriptor *security)
+{
+ if (key->ops->set_sec_desc == NULL)
+ return WERR_NOT_SUPPORTED;
+
+ return key->ops->set_sec_desc(key, security);
+}
+
+WERROR hive_key_del_value(struct hive_key *key, const char *name)
+{
+ if (key->ops->delete_value == NULL)
+ return WERR_NOT_SUPPORTED;
+
+ return key->ops->delete_value(key, name);
+}
+
+WERROR hive_key_flush(struct hive_key *key)
+{
+ if (key->ops->flush_key == NULL)
+ return WERR_OK;
+
+ return key->ops->flush_key(key);
+}
diff --git a/source4/lib/registry/interface.c b/source4/lib/registry/interface.c
new file mode 100644
index 0000000000..81ca2c39bc
--- /dev/null
+++ b/source4/lib/registry/interface.c
@@ -0,0 +1,295 @@
+/*
+ Unix SMB/CIFS implementation.
+ Transparent registry backend handling
+ Copyright (C) Jelmer Vernooij 2003-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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "../lib/util/dlinklist.h"
+#include "lib/registry/registry.h"
+#include "system/filesys.h"
+
+
+/**
+ * @file
+ * @brief Main registry functions
+ */
+
+const struct reg_predefined_key reg_predefined_keys[] = {
+ {HKEY_CLASSES_ROOT,"HKEY_CLASSES_ROOT" },
+ {HKEY_CURRENT_USER,"HKEY_CURRENT_USER" },
+ {HKEY_LOCAL_MACHINE, "HKEY_LOCAL_MACHINE" },
+ {HKEY_PERFORMANCE_DATA, "HKEY_PERFORMANCE_DATA" },
+ {HKEY_USERS, "HKEY_USERS" },
+ {HKEY_CURRENT_CONFIG, "HKEY_CURRENT_CONFIG" },
+ {HKEY_DYN_DATA, "HKEY_DYN_DATA" },
+ {HKEY_PERFORMANCE_TEXT, "HKEY_PERFORMANCE_TEXT" },
+ {HKEY_PERFORMANCE_NLSTEXT, "HKEY_PERFORMANCE_NLSTEXT" },
+ { 0, NULL }
+};
+
+/** Obtain name of specific hkey. */
+_PUBLIC_ const char *reg_get_predef_name(uint32_t hkey)
+{
+ int i;
+ for (i = 0; reg_predefined_keys[i].name; i++) {
+ if (reg_predefined_keys[i].handle == hkey)
+ return reg_predefined_keys[i].name;
+ }
+
+ return NULL;
+}
+
+/** Get predefined key by name. */
+_PUBLIC_ WERROR reg_get_predefined_key_by_name(struct registry_context *ctx,
+ const char *name,
+ struct registry_key **key)
+{
+ int i;
+
+ for (i = 0; reg_predefined_keys[i].name; i++) {
+ if (!strcasecmp(reg_predefined_keys[i].name, name))
+ return reg_get_predefined_key(ctx,
+ reg_predefined_keys[i].handle,
+ key);
+ }
+
+ DEBUG(1, ("No predefined key with name '%s'\n", name));
+
+ return WERR_BADFILE;
+}
+
+/** Get predefined key by id. */
+_PUBLIC_ WERROR reg_get_predefined_key(struct registry_context *ctx,
+ uint32_t hkey, struct registry_key **key)
+{
+ return ctx->ops->get_predefined_key(ctx, hkey, key);
+}
+
+/**
+ * Open a key
+ * First tries to use the open_key function from the backend
+ * then falls back to get_subkey_by_name and later get_subkey_by_index
+ */
+_PUBLIC_ WERROR reg_open_key(TALLOC_CTX *mem_ctx, struct registry_key *parent,
+ const char *name, struct registry_key **result)
+{
+ if (parent == NULL) {
+ DEBUG(0, ("Invalid parent key specified for open of '%s'\n",
+ name));
+ return WERR_INVALID_PARAM;
+ }
+
+ if (parent->context->ops->open_key == NULL) {
+ DEBUG(0, ("Registry backend doesn't have open_key!\n"));
+ return WERR_NOT_SUPPORTED;
+ }
+
+ return parent->context->ops->open_key(mem_ctx, parent, name, result);
+}
+
+/**
+ * Get value by index
+ */
+_PUBLIC_ WERROR reg_key_get_value_by_index(TALLOC_CTX *mem_ctx,
+ const struct registry_key *key,
+ uint32_t idx, const char **name,
+ uint32_t *type, DATA_BLOB *data)
+{
+ if (key == NULL)
+ return WERR_INVALID_PARAM;
+
+ if (key->context->ops->enum_value == NULL)
+ return WERR_NOT_SUPPORTED;
+
+ return key->context->ops->enum_value(mem_ctx, key, idx, name,
+ type, data);
+}
+
+/**
+ * Get the number of subkeys.
+ */
+_PUBLIC_ WERROR reg_key_get_info(TALLOC_CTX *mem_ctx,
+ const struct registry_key *key,
+ const char **classname,
+ uint32_t *num_subkeys,
+ uint32_t *num_values,
+ NTTIME *last_change_time,
+ uint32_t *max_subkeynamelen,
+ uint32_t *max_valnamelen,
+ uint32_t *max_valbufsize)
+{
+ if (key == NULL)
+ return WERR_INVALID_PARAM;
+
+ if (key->context->ops->get_key_info == NULL)
+ return WERR_NOT_SUPPORTED;
+
+ return key->context->ops->get_key_info(mem_ctx,
+ key, classname, num_subkeys,
+ num_values, last_change_time,
+ max_subkeynamelen,
+ max_valnamelen, max_valbufsize);
+}
+
+/**
+ * Get subkey by index.
+ */
+_PUBLIC_ WERROR reg_key_get_subkey_by_index(TALLOC_CTX *mem_ctx,
+ const struct registry_key *key,
+ int idx, const char **name,
+ const char **keyclass,
+ NTTIME *last_changed_time)
+{
+ if (key == NULL)
+ return WERR_INVALID_PARAM;
+
+ if (key->context->ops->enum_key == NULL)
+ return WERR_NOT_SUPPORTED;
+
+ return key->context->ops->enum_key(mem_ctx, key, idx, name,
+ keyclass, last_changed_time);
+}
+
+/**
+ * Get value by name.
+ */
+_PUBLIC_ WERROR reg_key_get_value_by_name(TALLOC_CTX *mem_ctx,
+ const struct registry_key *key,
+ const char *name,
+ uint32_t *type,
+ DATA_BLOB *data)
+{
+ if (key == NULL)
+ return WERR_INVALID_PARAM;
+
+ if (key->context->ops->get_value == NULL)
+ return WERR_NOT_SUPPORTED;
+
+ return key->context->ops->get_value(mem_ctx, key, name, type, data);
+}
+
+/**
+ * Delete a key.
+ */
+_PUBLIC_ WERROR reg_key_del(struct registry_key *parent, const char *name)
+{
+ if (parent == NULL)
+ return WERR_INVALID_PARAM;
+
+ if (parent->context->ops->delete_key == NULL)
+ return WERR_NOT_SUPPORTED;
+
+ return parent->context->ops->delete_key(parent, name);
+}
+
+/**
+ * Add a key.
+ */
+_PUBLIC_ WERROR reg_key_add_name(TALLOC_CTX *mem_ctx,
+ struct registry_key *parent,
+ const char *name, const char *key_class,
+ struct security_descriptor *desc,
+ struct registry_key **newkey)
+{
+ if (parent == NULL)
+ return WERR_INVALID_PARAM;
+
+ if (parent->context->ops->create_key == NULL) {
+ DEBUG(1, ("Backend '%s' doesn't support method add_key\n",
+ parent->context->ops->name));
+ return WERR_NOT_SUPPORTED;
+ }
+
+ return parent->context->ops->create_key(mem_ctx, parent, name,
+ key_class, desc, newkey);
+}
+
+/**
+ * Set a value.
+ */
+_PUBLIC_ WERROR reg_val_set(struct registry_key *key, const char *value,
+ uint32_t type, const DATA_BLOB data)
+{
+ if (key == NULL)
+ return WERR_INVALID_PARAM;
+
+ /* A 'real' set function has preference */
+ if (key->context->ops->set_value == NULL) {
+ DEBUG(1, ("Backend '%s' doesn't support method set_value\n",
+ key->context->ops->name));
+ return WERR_NOT_SUPPORTED;
+ }
+
+ return key->context->ops->set_value(key, value, type, data);
+}
+
+/**
+ * Get the security descriptor on a key.
+ */
+_PUBLIC_ WERROR reg_get_sec_desc(TALLOC_CTX *ctx,
+ const struct registry_key *key,
+ struct security_descriptor **secdesc)
+{
+ if (key == NULL)
+ return WERR_INVALID_PARAM;
+
+ /* A 'real' set function has preference */
+ if (key->context->ops->get_sec_desc == NULL)
+ return WERR_NOT_SUPPORTED;
+
+ return key->context->ops->get_sec_desc(ctx, key, secdesc);
+}
+
+/**
+ * Delete a value.
+ */
+_PUBLIC_ WERROR reg_del_value(struct registry_key *key, const char *valname)
+{
+ if (key == NULL)
+ return WERR_INVALID_PARAM;
+
+ if (key->context->ops->delete_value == NULL)
+ return WERR_NOT_SUPPORTED;
+
+ return key->context->ops->delete_value(key, valname);
+}
+
+/**
+ * Flush a key to disk.
+ */
+_PUBLIC_ WERROR reg_key_flush(struct registry_key *key)
+{
+ if (key == NULL)
+ return WERR_INVALID_PARAM;
+
+ if (key->context->ops->flush_key == NULL)
+ return WERR_NOT_SUPPORTED;
+
+ return key->context->ops->flush_key(key);
+}
+
+_PUBLIC_ WERROR reg_set_sec_desc(struct registry_key *key,
+ const struct security_descriptor *security)
+{
+ if (key == NULL)
+ return WERR_INVALID_PARAM;
+
+ if (key->context->ops->set_sec_desc == NULL)
+ return WERR_NOT_SUPPORTED;
+
+ return key->context->ops->set_sec_desc(key, security);
+}
diff --git a/source4/lib/registry/ldb.c b/source4/lib/registry/ldb.c
new file mode 100644
index 0000000000..c558805e04
--- /dev/null
+++ b/source4/lib/registry/ldb.c
@@ -0,0 +1,834 @@
+/*
+ Unix SMB/CIFS implementation.
+ Registry interface
+ Copyright (C) 2004-2007, Jelmer Vernooij, jelmer@samba.org
+ Copyright (C) 2008 Matthias Dieter Wallnöfer, mwallnoefer@yahoo.de
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "registry.h"
+#include "lib/ldb/include/ldb.h"
+#include "lib/ldb/include/ldb_errors.h"
+#include "ldb_wrap.h"
+#include "librpc/gen_ndr/winreg.h"
+#include "param/param.h"
+
+static struct hive_operations reg_backend_ldb;
+
+struct ldb_key_data
+{
+ struct hive_key key;
+ struct ldb_context *ldb;
+ struct ldb_dn *dn;
+ struct ldb_message **subkeys, **values;
+ int subkey_count, value_count;
+};
+
+static void reg_ldb_unpack_value(TALLOC_CTX *mem_ctx,
+ struct ldb_message *msg,
+ const char **name, uint32_t *type,
+ DATA_BLOB *data)
+{
+ const struct ldb_val *val;
+ uint32_t value_type;
+
+ if (name != NULL)
+ *name = talloc_strdup(mem_ctx,
+ ldb_msg_find_attr_as_string(msg, "value",
+ NULL));
+
+ value_type = ldb_msg_find_attr_as_uint(msg, "type", 0);
+ *type = value_type;
+
+ val = ldb_msg_find_ldb_val(msg, "data");
+
+ switch (value_type)
+ {
+ case REG_SZ:
+ case REG_EXPAND_SZ:
+ if (val != NULL)
+ convert_string_talloc(mem_ctx, CH_UTF8, CH_UTF16,
+ val->data, val->length,
+ (void **)&data->data, &data->length, false);
+ else {
+ data->data = NULL;
+ data->length = 0;
+ }
+ break;
+
+ case REG_BINARY:
+ if (val != NULL)
+ *data = data_blob_talloc(mem_ctx, val->data, val->length);
+ else {
+ data->data = NULL;
+ data->length = 0;
+ }
+ break;
+
+ case REG_DWORD: {
+ uint32_t tmp = strtoul((char *)val->data, NULL, 0);
+ *data = data_blob_talloc(mem_ctx, &tmp, 4);
+ }
+ break;
+
+ default:
+ *data = data_blob_talloc(mem_ctx, val->data, val->length);
+ break;
+ }
+}
+
+static struct ldb_message *reg_ldb_pack_value(struct ldb_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ const char *name,
+ uint32_t type, DATA_BLOB data)
+{
+ struct ldb_val val;
+ struct ldb_message *msg = talloc_zero(mem_ctx, struct ldb_message);
+ char *type_s;
+
+ ldb_msg_add_string(msg, "value", talloc_strdup(mem_ctx, name));
+
+ switch (type) {
+ case REG_SZ:
+ case REG_EXPAND_SZ:
+ if (data.data[0] != '\0') {
+ convert_string_talloc(mem_ctx, CH_UTF16, CH_UTF8,
+ (void *)data.data,
+ data.length,
+ (void **)&val.data, &val.length, false);
+ ldb_msg_add_value(msg, "data", &val, NULL);
+ } else {
+ ldb_msg_add_empty(msg, "data", LDB_FLAG_MOD_DELETE, NULL);
+ }
+ break;
+
+ case REG_BINARY:
+ if (data.length > 0)
+ ldb_msg_add_value(msg, "data", &data, NULL);
+ else
+ ldb_msg_add_empty(msg, "data", LDB_FLAG_MOD_DELETE, NULL);
+ break;
+
+ case REG_DWORD:
+ ldb_msg_add_string(msg, "data",
+ talloc_asprintf(mem_ctx, "0x%x",
+ IVAL(data.data, 0)));
+ break;
+ default:
+ ldb_msg_add_value(msg, "data", &data, NULL);
+ }
+
+
+ type_s = talloc_asprintf(mem_ctx, "%u", type);
+ ldb_msg_add_string(msg, "type", type_s);
+
+ return msg;
+}
+
+static char *reg_ldb_escape(TALLOC_CTX *mem_ctx, const char *value)
+{
+ struct ldb_val val;
+
+ val.data = discard_const_p(uint8_t, value);
+ val.length = strlen(value);
+
+ return ldb_dn_escape_value(mem_ctx, val);
+}
+
+static int reg_close_ldb_key(struct ldb_key_data *key)
+{
+ if (key->subkeys != NULL) {
+ talloc_free(key->subkeys);
+ key->subkeys = NULL;
+ }
+
+ if (key->values != NULL) {
+ talloc_free(key->values);
+ key->values = NULL;
+ }
+ return 0;
+}
+
+static struct ldb_dn *reg_path_to_ldb(TALLOC_CTX *mem_ctx,
+ const struct hive_key *from,
+ const char *path, const char *add)
+{
+ TALLOC_CTX *local_ctx;
+ struct ldb_dn *ret;
+ char *mypath = talloc_strdup(mem_ctx, path);
+ char *begin;
+ struct ldb_key_data *kd = talloc_get_type(from, struct ldb_key_data);
+ struct ldb_context *ldb = kd->ldb;
+
+ local_ctx = talloc_new(mem_ctx);
+
+ if (add) {
+ ret = ldb_dn_new(mem_ctx, ldb, add);
+ } else {
+ ret = ldb_dn_new(mem_ctx, ldb, NULL);
+ }
+ if (!ldb_dn_validate(ret)) {
+ talloc_free(ret);
+ talloc_free(local_ctx);
+ return NULL;
+ }
+
+ while (mypath) {
+ char *keyname;
+
+ begin = strrchr(mypath, '\\');
+
+ if (begin) keyname = begin + 1;
+ else keyname = mypath;
+
+ if(strlen(keyname)) {
+ if (!ldb_dn_add_base_fmt(ret, "key=%s",
+ reg_ldb_escape(local_ctx,
+ keyname)))
+ {
+ talloc_free(local_ctx);
+ return NULL;
+ }
+ }
+
+ if(begin) {
+ *begin = '\0';
+ } else {
+ break;
+ }
+ }
+
+ ldb_dn_add_base(ret, kd->dn);
+
+ talloc_free(local_ctx);
+
+ return ret;
+}
+
+static WERROR cache_subkeys(struct ldb_key_data *kd)
+{
+ struct ldb_context *c = kd->ldb;
+ struct ldb_result *res;
+ int ret;
+
+ ret = ldb_search(c, c, &res, kd->dn, LDB_SCOPE_ONELEVEL, NULL, "(key=*)");
+
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0, ("Error getting subkeys for '%s': %s\n",
+ ldb_dn_get_linearized(kd->dn), ldb_errstring(c)));
+ return WERR_FOOBAR;
+ }
+
+ kd->subkey_count = res->count;
+ kd->subkeys = talloc_steal(kd, res->msgs);
+ talloc_free(res);
+
+ return WERR_OK;
+}
+
+static WERROR cache_values(struct ldb_key_data *kd)
+{
+ struct ldb_context *c = kd->ldb;
+ struct ldb_result *res;
+ int ret;
+
+ ret = ldb_search(c, c, &res, kd->dn, LDB_SCOPE_ONELEVEL,
+ NULL, "(value=*)");
+
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0, ("Error getting values for '%s': %s\n",
+ ldb_dn_get_linearized(kd->dn), ldb_errstring(c)));
+ return WERR_FOOBAR;
+ }
+
+ kd->value_count = res->count;
+ kd->values = talloc_steal(kd, res->msgs);
+ talloc_free(res);
+
+ return WERR_OK;
+}
+
+
+static WERROR ldb_get_subkey_by_id(TALLOC_CTX *mem_ctx,
+ const struct hive_key *k, uint32_t idx,
+ const char **name,
+ const char **classname,
+ NTTIME *last_mod_time)
+{
+ struct ldb_message_element *el;
+ struct ldb_key_data *kd = talloc_get_type(k, struct ldb_key_data);
+
+ /* Initialization */
+ if (name != NULL)
+ *name = NULL;
+ if (classname != NULL)
+ *classname = NULL; /* TODO: Store properly */
+ if (last_mod_time != NULL)
+ *last_mod_time = 0; /* TODO: we need to add this to the
+ ldb backend properly */
+
+ /* Do a search if necessary */
+ if (kd->subkeys == NULL) {
+ W_ERROR_NOT_OK_RETURN(cache_subkeys(kd));
+ }
+
+ if (idx >= kd->subkey_count)
+ return WERR_NO_MORE_ITEMS;
+
+ el = ldb_msg_find_element(kd->subkeys[idx], "key");
+ SMB_ASSERT(el != NULL);
+ SMB_ASSERT(el->num_values != 0);
+
+ if (name != NULL)
+ *name = talloc_strdup(mem_ctx, (char *)el->values[0].data);
+
+ return WERR_OK;
+}
+
+static WERROR ldb_get_default_value(TALLOC_CTX *mem_ctx, struct hive_key *k,
+ const char **name, uint32_t *data_type,
+ DATA_BLOB *data)
+{
+ struct ldb_key_data *kd = talloc_get_type(k, struct ldb_key_data);
+ struct ldb_context *c = kd->ldb;
+ const char* attrs[] = { "data", "type", NULL };
+ struct ldb_result *res;
+ int ret;
+
+ ret = ldb_search(c, mem_ctx, &res, kd->dn, LDB_SCOPE_BASE, attrs, "%s", "");
+
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0, ("Error getting default value for '%s': %s\n",
+ ldb_dn_get_linearized(kd->dn), ldb_errstring(c)));
+ return WERR_FOOBAR;
+ }
+
+ if (res->count == 0 || res->msgs[0]->num_elements == 0)
+ return WERR_BADFILE;
+
+ reg_ldb_unpack_value(mem_ctx,
+ res->msgs[0], name, data_type, data);
+
+ talloc_free(res);
+
+ return WERR_OK;
+}
+
+static WERROR ldb_get_value_by_id(TALLOC_CTX *mem_ctx, struct hive_key *k,
+ int idx, const char **name,
+ uint32_t *data_type, DATA_BLOB *data)
+{
+ struct ldb_key_data *kd = talloc_get_type(k, struct ldb_key_data);
+
+ /* if default value exists, give it back */
+ if (W_ERROR_IS_OK(ldb_get_default_value(mem_ctx, k, name, data_type,
+ data))) {
+ if (idx == 0)
+ return WERR_OK;
+ else
+ --idx;
+ }
+
+ /* Do the search if necessary */
+ if (kd->values == NULL) {
+ W_ERROR_NOT_OK_RETURN(cache_values(kd));
+ }
+
+ if (idx >= kd->value_count)
+ return WERR_NO_MORE_ITEMS;
+
+ reg_ldb_unpack_value(mem_ctx, kd->values[idx], name, data_type, data);
+
+ return WERR_OK;
+}
+
+static WERROR ldb_get_value(TALLOC_CTX *mem_ctx, struct hive_key *k,
+ const char *name, uint32_t *data_type,
+ DATA_BLOB *data)
+{
+ struct ldb_key_data *kd = talloc_get_type(k, struct ldb_key_data);
+ struct ldb_context *c = kd->ldb;
+ struct ldb_result *res;
+ int ret;
+ char *query;
+
+ if (strlen(name) == 0) {
+ /* default value */
+ return ldb_get_default_value(mem_ctx, k, NULL, data_type, data);
+ } else {
+ /* normal value */
+ query = talloc_asprintf(mem_ctx, "(value=%s)", name);
+ ret = ldb_search(c, mem_ctx, &res, kd->dn, LDB_SCOPE_ONELEVEL, NULL, "%s", query);
+ talloc_free(query);
+
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0, ("Error getting values for '%s': %s\n",
+ ldb_dn_get_linearized(kd->dn), ldb_errstring(c)));
+ return WERR_FOOBAR;
+ }
+
+ if (res->count == 0)
+ return WERR_BADFILE;
+
+ reg_ldb_unpack_value(mem_ctx, res->msgs[0], NULL, data_type, data);
+
+ talloc_free(res);
+ }
+
+ return WERR_OK;
+}
+
+static WERROR ldb_open_key(TALLOC_CTX *mem_ctx, const struct hive_key *h,
+ const char *name, struct hive_key **key)
+{
+ struct ldb_result *res;
+ struct ldb_dn *ldap_path;
+ int ret;
+ struct ldb_key_data *newkd;
+ struct ldb_key_data *kd = talloc_get_type(h, struct ldb_key_data);
+ struct ldb_context *c = kd->ldb;
+
+ ldap_path = reg_path_to_ldb(mem_ctx, h, name, NULL);
+
+ ret = ldb_search(c, mem_ctx, &res, ldap_path, LDB_SCOPE_BASE, NULL, "(key=*)");
+
+ if (ret != LDB_SUCCESS) {
+ DEBUG(3, ("Error opening key '%s': %s\n",
+ ldb_dn_get_linearized(ldap_path), ldb_errstring(c)));
+ return WERR_FOOBAR;
+ } else if (res->count == 0) {
+ DEBUG(3, ("Key '%s' not found\n",
+ ldb_dn_get_linearized(ldap_path)));
+ talloc_free(res);
+ return WERR_BADFILE;
+ }
+
+ newkd = talloc_zero(mem_ctx, struct ldb_key_data);
+ newkd->key.ops = &reg_backend_ldb;
+ newkd->ldb = talloc_reference(newkd, kd->ldb);
+ newkd->dn = ldb_dn_copy(mem_ctx, res->msgs[0]->dn);
+
+ *key = (struct hive_key *)newkd;
+
+ return WERR_OK;
+}
+
+WERROR reg_open_ldb_file(TALLOC_CTX *parent_ctx, const char *location,
+ struct auth_session_info *session_info,
+ struct cli_credentials *credentials,
+ struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx,
+ struct hive_key **k)
+{
+ struct ldb_key_data *kd;
+ struct ldb_context *wrap;
+ struct ldb_message *attrs_msg;
+
+ if (location == NULL)
+ return WERR_INVALID_PARAM;
+
+ wrap = ldb_wrap_connect(parent_ctx, ev_ctx, lp_ctx,
+ location, session_info, credentials, 0, NULL);
+
+ if (wrap == NULL) {
+ DEBUG(1, (__FILE__": unable to connect\n"));
+ return WERR_FOOBAR;
+ }
+
+ attrs_msg = ldb_msg_new(wrap);
+ W_ERROR_HAVE_NO_MEMORY(attrs_msg);
+ attrs_msg->dn = ldb_dn_new(attrs_msg, wrap, "@ATTRIBUTES");
+ W_ERROR_HAVE_NO_MEMORY(attrs_msg->dn);
+ ldb_msg_add_string(attrs_msg, "key", "CASE_INSENSITIVE");
+ ldb_msg_add_string(attrs_msg, "value", "CASE_INSENSITIVE");
+
+ ldb_add(wrap, attrs_msg);
+
+ ldb_set_debug_stderr(wrap);
+
+ kd = talloc_zero(parent_ctx, struct ldb_key_data);
+ kd->key.ops = &reg_backend_ldb;
+ kd->ldb = talloc_reference(kd, wrap);
+ talloc_set_destructor (kd, reg_close_ldb_key);
+ kd->dn = ldb_dn_new(kd, wrap, "hive=NONE");
+
+ *k = (struct hive_key *)kd;
+
+ return WERR_OK;
+}
+
+static WERROR ldb_add_key(TALLOC_CTX *mem_ctx, const struct hive_key *parent,
+ const char *name, const char *classname,
+ struct security_descriptor *sd,
+ struct hive_key **newkey)
+{
+ struct ldb_key_data *parentkd = discard_const_p(struct ldb_key_data, parent);
+ struct ldb_message *msg;
+ struct ldb_key_data *newkd;
+ int ret;
+
+ msg = ldb_msg_new(mem_ctx);
+
+ msg->dn = reg_path_to_ldb(msg, parent, name, NULL);
+
+ ldb_msg_add_string(msg, "key", talloc_strdup(mem_ctx, name));
+ if (classname != NULL)
+ ldb_msg_add_string(msg, "classname",
+ talloc_strdup(mem_ctx, classname));
+
+ ret = ldb_add(parentkd->ldb, msg);
+ if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) {
+ return WERR_ALREADY_EXISTS;
+ }
+
+ if (ret != LDB_SUCCESS) {
+ DEBUG(1, ("ldb_add: %s\n", ldb_errstring(parentkd->ldb)));
+ return WERR_FOOBAR;
+ }
+
+ DEBUG(2, ("key added: %s\n", ldb_dn_get_linearized(msg->dn)));
+
+ newkd = talloc_zero(mem_ctx, struct ldb_key_data);
+ newkd->ldb = talloc_reference(newkd, parentkd->ldb);
+ newkd->key.ops = &reg_backend_ldb;
+ newkd->dn = talloc_steal(newkd, msg->dn);
+
+ *newkey = (struct hive_key *)newkd;
+
+ /* reset cache */
+ talloc_free(parentkd->subkeys);
+ parentkd->subkeys = NULL;
+
+ return WERR_OK;
+}
+
+static WERROR ldb_del_value (struct hive_key *key, const char *child)
+{
+ int ret;
+ struct ldb_key_data *kd = talloc_get_type(key, struct ldb_key_data);
+ TALLOC_CTX *mem_ctx;
+ struct ldb_message *msg;
+ struct ldb_dn *childdn;
+
+ if (strlen(child) == 0) {
+ /* default value */
+ mem_ctx = talloc_init("ldb_del_value");
+
+ msg = talloc_zero(mem_ctx, struct ldb_message);
+ msg->dn = ldb_dn_copy(msg, kd->dn);
+ ldb_msg_add_empty(msg, "data", LDB_FLAG_MOD_DELETE, NULL);
+ ldb_msg_add_empty(msg, "type", LDB_FLAG_MOD_DELETE, NULL);
+
+ ret = ldb_modify(kd->ldb, msg);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(1, ("ldb_del_value: %s\n", ldb_errstring(kd->ldb)));
+ talloc_free(mem_ctx);
+ return WERR_FOOBAR;
+ }
+
+ talloc_free(mem_ctx);
+ } else {
+ /* normal value */
+ childdn = ldb_dn_copy(kd->ldb, kd->dn);
+ if (!ldb_dn_add_child_fmt(childdn, "value=%s",
+ reg_ldb_escape(childdn, child)))
+ {
+ talloc_free(childdn);
+ return WERR_FOOBAR;
+ }
+
+ ret = ldb_delete(kd->ldb, childdn);
+
+ talloc_free(childdn);
+
+ if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+ return WERR_BADFILE;
+ } else if (ret != LDB_SUCCESS) {
+ DEBUG(1, ("ldb_del_value: %s\n", ldb_errstring(kd->ldb)));
+ return WERR_FOOBAR;
+ }
+ }
+
+ /* reset cache */
+ talloc_free(kd->values);
+ kd->values = NULL;
+
+ return WERR_OK;
+}
+
+static WERROR ldb_del_key(const struct hive_key *key, const char *name)
+{
+ int i, ret;
+ struct ldb_key_data *parentkd = talloc_get_type(key, struct ldb_key_data);
+ struct ldb_dn *ldap_path;
+ TALLOC_CTX *mem_ctx = talloc_init("ldb_del_key");
+ struct ldb_context *c = parentkd->ldb;
+ struct ldb_result *res_keys;
+ struct ldb_result *res_vals;
+ WERROR werr;
+ struct hive_key *hk;
+
+ /* Verify key exists by opening it */
+ werr = ldb_open_key(mem_ctx, key, name, &hk);
+ if (!W_ERROR_IS_OK(werr)) {
+ talloc_free(mem_ctx);
+ return werr;
+ }
+
+ ldap_path = reg_path_to_ldb(mem_ctx, key, name, NULL);
+ if (!ldap_path) {
+ talloc_free(mem_ctx);
+ return WERR_FOOBAR;
+ }
+
+ /* Search for subkeys */
+ ret = ldb_search(c, mem_ctx, &res_keys, ldap_path, LDB_SCOPE_ONELEVEL,
+ NULL, "(key=*)");
+
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0, ("Error getting subkeys for '%s': %s\n",
+ ldb_dn_get_linearized(ldap_path), ldb_errstring(c)));
+ talloc_free(mem_ctx);
+ return WERR_FOOBAR;
+ }
+
+ /* Search for values */
+ ret = ldb_search(c, mem_ctx, &res_vals, ldap_path, LDB_SCOPE_ONELEVEL,
+ NULL, "(value=*)");
+
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0, ("Error getting values for '%s': %s\n",
+ ldb_dn_get_linearized(ldap_path), ldb_errstring(c)));
+ talloc_free(mem_ctx);
+ return WERR_FOOBAR;
+ }
+
+ /* Start an explicit transaction */
+ ret = ldb_transaction_start(c);
+
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0, ("ldb_transaction_start: %s\n", ldb_errstring(c)));
+ talloc_free(mem_ctx);
+ return WERR_FOOBAR;
+ }
+
+ if (res_keys->count || res_vals->count)
+ {
+ /* Delete any subkeys */
+ for (i = 0; i < res_keys->count; i++)
+ {
+ werr = ldb_del_key(hk, ldb_msg_find_attr_as_string(
+ res_keys->msgs[i],
+ "key", NULL));
+ if (!W_ERROR_IS_OK(werr)) {
+ ret = ldb_transaction_cancel(c);
+ talloc_free(mem_ctx);
+ return werr;
+ }
+ }
+
+ /* Delete any values */
+ for (i = 0; i < res_vals->count; i++)
+ {
+ werr = ldb_del_value(hk, ldb_msg_find_attr_as_string(
+ res_vals->msgs[i],
+ "value", NULL));
+ if (!W_ERROR_IS_OK(werr)) {
+ ret = ldb_transaction_cancel(c);
+ talloc_free(mem_ctx);
+ return werr;
+ }
+ }
+ }
+
+ /* Delete the key itself */
+ ret = ldb_delete(c, ldap_path);
+
+ if (ret != LDB_SUCCESS)
+ {
+ DEBUG(1, ("ldb_del_key: %s\n", ldb_errstring(c)));
+ ret = ldb_transaction_cancel(c);
+ talloc_free(mem_ctx);
+ return WERR_FOOBAR;
+ }
+
+ /* Commit the transaction */
+ ret = ldb_transaction_commit(c);
+
+ if (ret != LDB_SUCCESS)
+ {
+ DEBUG(0, ("ldb_transaction_commit: %s\n", ldb_errstring(c)));
+ ret = ldb_transaction_cancel(c);
+ talloc_free(mem_ctx);
+ return WERR_FOOBAR;
+ }
+
+ talloc_free(mem_ctx);
+
+ /* reset cache */
+ talloc_free(parentkd->subkeys);
+ parentkd->subkeys = NULL;
+
+ return WERR_OK;
+}
+
+static WERROR ldb_set_value(struct hive_key *parent,
+ const char *name, uint32_t type,
+ const DATA_BLOB data)
+{
+ struct ldb_message *msg;
+ struct ldb_key_data *kd = talloc_get_type(parent, struct ldb_key_data);
+ int ret;
+ TALLOC_CTX *mem_ctx = talloc_init("ldb_set_value");
+
+ msg = reg_ldb_pack_value(kd->ldb, mem_ctx, name, type, data);
+ msg->dn = ldb_dn_copy(msg, kd->dn);
+
+ if (strlen(name) > 0) {
+ /* For a default value, we add/overwrite the attributes to/of the hive.
+ For a normal value, we create new childs. */
+ if (!ldb_dn_add_child_fmt(msg->dn, "value=%s",
+ reg_ldb_escape(mem_ctx, name)))
+ {
+ talloc_free(mem_ctx);
+ return WERR_FOOBAR;
+ }
+ }
+
+ ret = ldb_add(kd->ldb, msg);
+ if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) {
+ int i;
+ for (i = 0; i < msg->num_elements; i++) {
+ if (msg->elements[i].flags != LDB_FLAG_MOD_DELETE)
+ msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
+ }
+ ret = ldb_modify(kd->ldb, msg);
+ }
+
+ if (ret != LDB_SUCCESS) {
+ DEBUG(1, ("ldb_set_value: %s\n", ldb_errstring(kd->ldb)));
+ talloc_free(mem_ctx);
+ return WERR_FOOBAR;
+ }
+
+ /* reset cache */
+ talloc_free(kd->values);
+ kd->values = NULL;
+
+ talloc_free(mem_ctx);
+ return WERR_OK;
+}
+
+static WERROR ldb_get_key_info(TALLOC_CTX *mem_ctx,
+ const struct hive_key *key,
+ const char **classname,
+ uint32_t *num_subkeys,
+ uint32_t *num_values,
+ NTTIME *last_change_time,
+ uint32_t *max_subkeynamelen,
+ uint32_t *max_valnamelen,
+ uint32_t *max_valbufsize)
+{
+ struct ldb_key_data *kd = talloc_get_type(key, struct ldb_key_data);
+
+ /* Initialization */
+ if (classname != NULL)
+ *classname = NULL;
+ if (num_subkeys != NULL)
+ *num_subkeys = 0;
+ if (num_values != NULL)
+ *num_values = 0;
+ if (last_change_time != NULL)
+ *last_change_time = 0;
+ if (max_subkeynamelen != NULL)
+ *max_subkeynamelen = 0;
+ if (max_valnamelen != NULL)
+ *max_valnamelen = 0;
+ if (max_valbufsize != NULL)
+ *max_valbufsize = 0;
+
+ if (kd->subkeys == NULL) {
+ W_ERROR_NOT_OK_RETURN(cache_subkeys(kd));
+ }
+
+ if (kd->values == NULL) {
+ W_ERROR_NOT_OK_RETURN(cache_values(kd));
+ }
+
+ if (num_subkeys != NULL) {
+ *num_subkeys = kd->subkey_count;
+ }
+ if (num_values != NULL) {
+ *num_values = kd->value_count;
+ }
+
+
+ if (max_subkeynamelen != NULL) {
+ int i;
+ struct ldb_message_element *el;
+
+ *max_subkeynamelen = 0;
+
+ for (i = 0; i < kd->subkey_count; i++) {
+ el = ldb_msg_find_element(kd->subkeys[i], "key");
+ *max_subkeynamelen = MAX(*max_subkeynamelen, el->values[0].length);
+ }
+ }
+
+ if (max_valnamelen != NULL || max_valbufsize != NULL) {
+ int i;
+ struct ldb_message_element *el;
+ W_ERROR_NOT_OK_RETURN(cache_values(kd));
+
+ if (max_valbufsize != NULL)
+ *max_valbufsize = 0;
+
+ if (max_valnamelen != NULL)
+ *max_valnamelen = 0;
+
+ for (i = 0; i < kd->value_count; i++) {
+ if (max_valnamelen != NULL) {
+ el = ldb_msg_find_element(kd->values[i], "value");
+ *max_valnamelen = MAX(*max_valnamelen, el->values[0].length);
+ }
+
+ if (max_valbufsize != NULL) {
+ uint32_t data_type;
+ DATA_BLOB data;
+ reg_ldb_unpack_value(mem_ctx,
+ kd->values[i], NULL,
+ &data_type, &data);
+ *max_valbufsize = MAX(*max_valbufsize, data.length);
+ talloc_free(data.data);
+ }
+ }
+ }
+
+ return WERR_OK;
+}
+
+static struct hive_operations reg_backend_ldb = {
+ .name = "ldb",
+ .add_key = ldb_add_key,
+ .del_key = ldb_del_key,
+ .get_key_by_name = ldb_open_key,
+ .enum_value = ldb_get_value_by_id,
+ .enum_key = ldb_get_subkey_by_id,
+ .set_value = ldb_set_value,
+ .get_value_by_name = ldb_get_value,
+ .delete_value = ldb_del_value,
+ .get_key_info = ldb_get_key_info,
+};
diff --git a/source4/lib/registry/local.c b/source4/lib/registry/local.c
new file mode 100644
index 0000000000..a3aee06b13
--- /dev/null
+++ b/source4/lib/registry/local.c
@@ -0,0 +1,351 @@
+/*
+ Unix SMB/CIFS implementation.
+ Transparent registry backend handling
+ Copyright (C) Jelmer Vernooij 2003-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 "../lib/util/dlinklist.h"
+#include "lib/registry/registry.h"
+#include "system/filesys.h"
+
+struct reg_key_path {
+ uint32_t predefined_key;
+ const char **elements;
+};
+
+struct registry_local {
+ const struct registry_operations *ops;
+
+ struct mountpoint {
+ struct reg_key_path path;
+ struct hive_key *key;
+ struct mountpoint *prev, *next;
+ } *mountpoints;
+};
+
+struct local_key {
+ struct registry_key global;
+ struct reg_key_path path;
+ struct hive_key *hive_key;
+};
+
+
+struct registry_key *reg_import_hive_key(struct registry_context *ctx,
+ struct hive_key *hive,
+ uint32_t predefined_key,
+ const char **elements)
+{
+ struct local_key *local_key;
+ struct reg_key_path parent_path;
+
+ parent_path.predefined_key = predefined_key;
+ parent_path.elements = elements;
+
+ local_key = talloc(ctx, struct local_key);
+ local_key->hive_key = talloc_steal(local_key, hive);
+ local_key->global.context = talloc_reference(local_key, ctx);
+ local_key->path = parent_path;
+
+ return (struct registry_key *)local_key;
+}
+
+
+static WERROR local_open_key(TALLOC_CTX *mem_ctx,
+ struct registry_key *parent,
+ const char *path,
+ struct registry_key **result)
+{
+ char *orig = talloc_strdup(mem_ctx, path),
+ *curbegin = orig,
+ *curend = strchr(orig, '\\');
+ struct local_key *local_parent = talloc_get_type(parent,
+ struct local_key);
+ struct hive_key *curkey = local_parent->hive_key;
+ WERROR error;
+ const char **elements = NULL;
+ int el;
+
+ if (local_parent->path.elements != NULL) {
+ elements = talloc_array(mem_ctx, const char *,
+ str_list_length(local_parent->path.elements) + 1);
+ for (el = 0; local_parent->path.elements[el] != NULL; el++) {
+ elements[el] = talloc_reference(elements,
+ local_parent->path.elements[el]);
+ }
+ elements[el] = NULL;
+ } else {
+ elements = NULL;
+ el = 0;
+ }
+
+ while (curbegin != NULL && *curbegin) {
+ if (curend != NULL)
+ *curend = '\0';
+ elements = talloc_realloc(mem_ctx, elements, const char *, el+2);
+ elements[el] = talloc_strdup(elements, curbegin);
+ el++;
+ elements[el] = NULL;
+ error = hive_get_key_by_name(mem_ctx, curkey,
+ curbegin, &curkey);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(2, ("Opening key %s failed: %s\n", curbegin,
+ win_errstr(error)));
+ talloc_free(orig);
+ return error;
+ }
+ if (curend == NULL)
+ break;
+ curbegin = curend + 1;
+ curend = strchr(curbegin, '\\');
+ }
+ talloc_free(orig);
+
+ *result = reg_import_hive_key(local_parent->global.context, curkey,
+ local_parent->path.predefined_key,
+ talloc_steal(curkey, elements));
+
+ return WERR_OK;
+}
+
+WERROR local_get_predefined_key(struct registry_context *ctx,
+ uint32_t key_id, struct registry_key **key)
+{
+ struct registry_local *rctx = talloc_get_type(ctx,
+ struct registry_local);
+ struct mountpoint *mp;
+
+ for (mp = rctx->mountpoints; mp != NULL; mp = mp->next) {
+ if (mp->path.predefined_key == key_id &&
+ mp->path.elements == NULL)
+ break;
+ }
+
+ if (mp == NULL)
+ return WERR_BADFILE;
+
+ *key = reg_import_hive_key(ctx, mp->key,
+ mp->path.predefined_key,
+ mp->path.elements);
+
+ return WERR_OK;
+}
+
+static WERROR local_enum_key(TALLOC_CTX *mem_ctx,
+ const struct registry_key *key, uint32_t idx,
+ const char **name,
+ const char **keyclass,
+ NTTIME *last_changed_time)
+{
+ const struct local_key *local = (const struct local_key *)key;
+
+ return hive_enum_key(mem_ctx, local->hive_key, idx, name, keyclass,
+ last_changed_time);
+}
+
+static WERROR local_create_key(TALLOC_CTX *mem_ctx,
+ struct registry_key *parent_key,
+ const char *name,
+ const char *key_class,
+ struct security_descriptor *security,
+ struct registry_key **key)
+{
+ struct local_key *local_parent;
+ struct hive_key *hivekey;
+ const char **elements;
+ int i;
+ const char *last_part;
+
+ last_part = strrchr(name, '\\');
+ if (last_part == NULL) {
+ last_part = name;
+ local_parent = (struct local_key *)parent_key;
+ } else {
+ W_ERROR_NOT_OK_RETURN(reg_open_key(mem_ctx, parent_key,
+ talloc_strndup(mem_ctx, name, last_part-name),
+ (struct registry_key **)&local_parent));
+ last_part++;
+ }
+
+ W_ERROR_NOT_OK_RETURN(hive_key_add_name(mem_ctx, local_parent->hive_key,
+ last_part, key_class, security,
+ &hivekey));
+
+ if (local_parent->path.elements != NULL) {
+ elements = talloc_array(hivekey, const char *,
+ str_list_length(local_parent->path.elements)+2);
+ for (i = 0; local_parent->path.elements[i] != NULL; i++) {
+ elements[i] = talloc_reference(elements,
+ local_parent->path.elements[i]);
+ }
+ } else {
+ elements = talloc_array(hivekey, const char *, 2);
+ i = 0;
+ }
+
+ elements[i] = talloc_strdup(elements, name);
+ elements[i+1] = NULL;
+
+ *key = reg_import_hive_key(local_parent->global.context, hivekey,
+ local_parent->path.predefined_key,
+ elements);
+
+ return WERR_OK;
+}
+
+static WERROR local_set_value(struct registry_key *key, const char *name,
+ uint32_t type, const DATA_BLOB data)
+{
+ struct local_key *local = (struct local_key *)key;
+
+ return hive_key_set_value(local->hive_key, name, type, data);
+}
+
+static WERROR local_get_value(TALLOC_CTX *mem_ctx,
+ const struct registry_key *key,
+ const char *name, uint32_t *type, DATA_BLOB *data)
+{
+ const struct local_key *local = (const struct local_key *)key;
+
+ return hive_get_value(mem_ctx, local->hive_key, name, type, data);
+}
+
+static WERROR local_enum_value(TALLOC_CTX *mem_ctx,
+ const struct registry_key *key, uint32_t idx,
+ const char **name,
+ uint32_t *type,
+ DATA_BLOB *data)
+{
+ const struct local_key *local = (const struct local_key *)key;
+
+ return hive_get_value_by_index(mem_ctx, local->hive_key, idx,
+ name, type, data);
+}
+
+static WERROR local_delete_key(struct registry_key *key, const char *name)
+{
+ const struct local_key *local = (const struct local_key *)key;
+
+ return hive_key_del(local->hive_key, name);
+}
+
+static WERROR local_delete_value(struct registry_key *key, const char *name)
+{
+ const struct local_key *local = (const struct local_key *)key;
+
+ return hive_key_del_value(local->hive_key, name);
+}
+
+static WERROR local_flush_key(struct registry_key *key)
+{
+ const struct local_key *local = (const struct local_key *)key;
+
+ return hive_key_flush(local->hive_key);
+}
+
+static WERROR local_get_key_info(TALLOC_CTX *mem_ctx,
+ const struct registry_key *key,
+ const char **classname,
+ uint32_t *num_subkeys,
+ uint32_t *num_values,
+ NTTIME *last_change_time,
+ uint32_t *max_subkeynamelen,
+ uint32_t *max_valnamelen,
+ uint32_t *max_valbufsize)
+{
+ const struct local_key *local = (const struct local_key *)key;
+
+ return hive_key_get_info(mem_ctx, local->hive_key,
+ classname, num_subkeys, num_values,
+ last_change_time, max_subkeynamelen,
+ max_valnamelen, max_valbufsize);
+}
+static WERROR local_get_sec_desc(TALLOC_CTX *mem_ctx,
+ const struct registry_key *key,
+ struct security_descriptor **security)
+{
+ const struct local_key *local = (const struct local_key *)key;
+
+ return hive_get_sec_desc(mem_ctx, local->hive_key, security);
+}
+static WERROR local_set_sec_desc(struct registry_key *key,
+ const struct security_descriptor *security)
+{
+ const struct local_key *local = (const struct local_key *)key;
+
+ return hive_set_sec_desc(local->hive_key, security);
+}
+const static struct registry_operations local_ops = {
+ .name = "local",
+ .open_key = local_open_key,
+ .get_predefined_key = local_get_predefined_key,
+ .enum_key = local_enum_key,
+ .create_key = local_create_key,
+ .set_value = local_set_value,
+ .get_value = local_get_value,
+ .enum_value = local_enum_value,
+ .delete_key = local_delete_key,
+ .delete_value = local_delete_value,
+ .flush_key = local_flush_key,
+ .get_key_info = local_get_key_info,
+ .get_sec_desc = local_get_sec_desc,
+ .set_sec_desc = local_set_sec_desc,
+};
+
+WERROR reg_open_local(TALLOC_CTX *mem_ctx, struct registry_context **ctx)
+{
+ struct registry_local *ret = talloc_zero(mem_ctx,
+ struct registry_local);
+
+ W_ERROR_HAVE_NO_MEMORY(ret);
+
+ ret->ops = &local_ops;
+
+ *ctx = (struct registry_context *)ret;
+
+ return WERR_OK;
+}
+
+WERROR reg_mount_hive(struct registry_context *rctx,
+ struct hive_key *hive_key,
+ uint32_t key_id,
+ const char **elements)
+{
+ struct registry_local *reg_local = talloc_get_type(rctx,
+ struct registry_local);
+ struct mountpoint *mp = talloc(rctx, struct mountpoint);
+ int i = 0;
+
+ mp->path.predefined_key = key_id;
+ mp->prev = mp->next = NULL;
+ mp->key = hive_key;
+ if (elements != NULL && str_list_length(elements) != 0) {
+ mp->path.elements = talloc_array(mp, const char *,
+ str_list_length(elements));
+ for (i = 0; elements[i] != NULL; i++) {
+ mp->path.elements[i] = talloc_reference(mp->path.elements,
+ elements[i]);
+ }
+ mp->path.elements[i] = NULL;
+ } else {
+ mp->path.elements = NULL;
+ }
+
+ DLIST_ADD(reg_local->mountpoints, mp);
+
+ return WERR_OK;
+}
diff --git a/source4/lib/registry/man/regdiff.1.xml b/source4/lib/registry/man/regdiff.1.xml
new file mode 100644
index 0000000000..7bcaa1502c
--- /dev/null
+++ b/source4/lib/registry/man/regdiff.1.xml
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE refentry PUBLIC "-//Samba-Team//DTD DocBook V4.2-Based Variant V1.0//EN" "http://www.samba.org/samba/DTD/samba-doc">
+<refentry id="regdiff.1">
+
+<refmeta>
+ <refentrytitle>regdiff</refentrytitle>
+ <manvolnum>1</manvolnum>
+</refmeta>
+
+
+<refnamediv>
+ <refname>regdiff</refname>
+ <refpurpose>Diff program for Windows registry files</refpurpose>
+</refnamediv>
+
+<refsynopsisdiv>
+ <cmdsynopsis>
+ <command>regdiff</command>
+ <arg choice="opt">--help</arg>
+ <arg choice="opt">--backend=BACKEND</arg>
+ <arg choice="opt">--backend=BACKEND</arg>
+ <arg choice="opt">--credentials=CREDENTIALS</arg>
+ <arg choice="opt">--credentials=CREDENTIALS</arg>
+ <arg choice="opt">location</arg>
+ <arg choice="opt">location</arg>
+ </cmdsynopsis>
+</refsynopsisdiv>
+
+<refsect1>
+ <title>DESCRIPTION</title>
+
+ <para>regdiff compares two Windows registry files key by key
+ and value by value and generates a text file that contains the
+ differences between the two files.</para>
+
+ <para>A file generated by regdiff can later be applied to a
+ registry file by the regpatch utility. </para>
+
+ <para>regdiff and regpatch use the same file format as
+ the regedit32.exe utility from Windows.</para>
+
+</refsect1>
+
+<refsect1>
+ <title>OPTIONS</title>
+
+ <variablelist>
+ <varlistentry>
+ <term>--help</term>
+ <listitem><para>
+ Show list of available options.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>--backend BACKEND</term>
+ <listitem><para>Name of backend to load. Possible values are:
+ creg, regf, dir and rpc. The default is <emphasis>dir</emphasis>.
+ </para>
+ <para>
+ This argument can be specified twice: once for the first
+ registry file and once for the second.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>--credentials=CREDENTIALS</term>
+ <listitem><para>
+ Credentials to use, if any. Password should be separated from user name by a percent sign.
+ </para>
+ <para>
+ This argument can be specified twice: once for the first
+ registry file and once for the second.
+ </para></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect1>
+
+<refsect1>
+ <title>VERSION</title>
+
+ <para>This man page is correct for version 4.0 of the Samba suite.</para>
+</refsect1>
+
+<refsect1>
+ <title>SEE ALSO</title>
+
+ <para>gregedit, regshell, regpatch, regtree, samba, patch, diff</para>
+
+</refsect1>
+
+<refsect1>
+ <title>AUTHOR</title>
+
+ <para>This utility is part of the <ulink url="http://www.samba.org/">Samba</ulink> suite, which is developed by the global <ulink url="http://www.samba.org/samba/team/">Samba Team</ulink>.</para>
+
+ <para>This manpage and regdiff were written by Jelmer Vernooij. </para>
+
+</refsect1>
+
+</refentry>
diff --git a/source4/lib/registry/man/regpatch.1.xml b/source4/lib/registry/man/regpatch.1.xml
new file mode 100644
index 0000000000..d9dcdcbf80
--- /dev/null
+++ b/source4/lib/registry/man/regpatch.1.xml
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE refentry PUBLIC "-//Samba-Team//DTD DocBook V4.2-Based Variant V1.0//EN" "http://www.samba.org/samba/DTD/samba-doc">
+<refentry id="regpatch.1">
+
+<refmeta>
+ <refentrytitle>regpatch</refentrytitle>
+ <manvolnum>1</manvolnum>
+</refmeta>
+
+
+<refnamediv>
+ <refname>regpatch</refname>
+ <refpurpose>Applies registry patches to registry files</refpurpose>
+</refnamediv>
+
+<refsynopsisdiv>
+ <cmdsynopsis>
+ <command>regpatch</command>
+ <arg choice="opt">--help</arg>
+ <arg choice="opt">--backend=BACKEND</arg>
+ <arg choice="opt">--credentials=CREDENTIALS</arg>
+ <arg choice="opt">location</arg>
+ <arg choice="opt">patch-file</arg>
+ </cmdsynopsis>
+</refsynopsisdiv>
+
+<refsect1>
+ <title>DESCRIPTION</title>
+
+ <para>The regpatch utility applies registry patches to Windows registry
+ files. The patch files should have the same format as is being used
+ by the regdiff utility and regedit32.exe from Windows.</para>
+
+ <para>If no patch file is specified on the command line,
+ regpatch attempts to read it from standard input.</para>
+</refsect1>
+
+
+<refsect1>
+ <title>OPTIONS</title>
+
+ <variablelist>
+ <varlistentry>
+ <term>--help</term>
+ <listitem><para>
+ Show list of available options.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>--backend BACKEND</term>
+ <listitem><para>Name of backend to load. Possible values are:
+ creg, regf, dir and rpc. The default is <emphasis>dir</emphasis>.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>--credentials=CREDENTIALS</term>
+ <listitem><para>
+ Credentials to use, if any. Password should be separated from user name by a percent sign.</para></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect1>
+
+<refsect1>
+ <title>VERSION</title>
+
+ <para>This man page is correct for version 4.0 of the Samba suite.</para>
+</refsect1>
+
+<refsect1>
+ <title>SEE ALSO</title>
+
+ <para>regdiff, regtree, regshell, gregedit, samba, diff, patch</para>
+
+</refsect1>
+
+<refsect1>
+ <title>AUTHOR</title>
+
+ <para>This utility is part of the <ulink url="http://www.samba.org/">Samba</ulink> suite, which is developed by the global <ulink url="http://www.samba.org/samba/team/">Samba Team</ulink>.</para>
+
+ <para>This manpage and regpatch were written by Jelmer Vernooij. </para>
+
+</refsect1>
+
+</refentry>
diff --git a/source4/lib/registry/man/regshell.1.xml b/source4/lib/registry/man/regshell.1.xml
new file mode 100644
index 0000000000..9f16d8cc24
--- /dev/null
+++ b/source4/lib/registry/man/regshell.1.xml
@@ -0,0 +1,186 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE refentry PUBLIC "-//Samba-Team//DTD DocBook V4.2-Based Variant V1.0//EN" "http://www.samba.org/samba/DTD/samba-doc">
+<refentry id="regshell.1">
+
+<refmeta>
+ <refentrytitle>regshell</refentrytitle>
+ <manvolnum>1</manvolnum>
+</refmeta>
+
+
+<refnamediv>
+ <refname>regshell</refname>
+ <refpurpose>Windows registry file browser using readline</refpurpose>
+</refnamediv>
+
+<refsynopsisdiv>
+ <cmdsynopsis>
+ <command>regshell</command>
+ <arg choice="opt">--help</arg>
+ <arg choice="opt">--backend=BACKEND</arg>
+ <arg choice="opt">--credentials=CREDENTIALS</arg>
+ <arg choice="opt">location</arg>
+ </cmdsynopsis>
+</refsynopsisdiv>
+
+<refsect1>
+ <title>DESCRIPTION</title>
+
+ <para>
+ regshell is a utility that lets you browse thru a Windows registry
+ file as if you were using a regular unix shell to browse thru a
+ file system.
+ </para>
+
+</refsect1>
+
+
+<refsect1>
+ <title>OPTIONS</title>
+
+ <variablelist>
+ <varlistentry>
+ <term>--help</term>
+ <listitem><para>
+ Show list of available options.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>--backend BACKEND</term>
+ <listitem><para>Name of backend to load. Possible values are:
+ creg, regf, dir and rpc. The default is <emphasis>dir</emphasis>.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>--credentials=CREDENTIALS</term>
+ <listitem><para>
+ Credentials to use, if any. Password should be separated from user name by a percent sign.</para></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect1>
+
+<refsect1>
+ <title>COMMANDS</title>
+
+ <variablelist>
+ <varlistentry>
+ <term>ck|cd &lt;keyname&gt;</term>
+ <listitem><para>
+ Go to the specified subkey.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>ch|predef [predefined-key-name]</term>
+ <listitem><para>
+ Go to the specified predefined key.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>list|ls</term>
+ <listitem><para>
+ List subkeys and values of the current key.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>mkkey|mkdir &lt;keyname&gt;</term>
+ <listitem><para>
+ Create a key with the specified <replaceable>keyname</replaceable> as a subkey of the current key.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>rmval|rm &lt;valname&gt;</term>
+ <listitem><para>
+ Delete the specified value.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>rmkey|rmdir &lt;keyname&gt;</term>
+ <listitem><para>
+ Delete the specified subkey recursively.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>pwd|pwk</term>
+ <listitem><para>Print the full name of the current key.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>set|update</term>
+ <listitem><para>Update the value of a key value. Not implemented at the moment.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>help|?</term>
+ <listitem><para>Print a list of available commands.</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>exit|quit</term>
+ <listitem><para>Leave regshell.</para></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect1>
+
+<refsect1>
+ <title>EXAMPLES</title>
+
+ <para>Browsing thru a nt4 registry file</para>
+ <screen>
+<userinput>regshell -b nt4 NTUSER.DAT</userinput>
+$$$PROTO.HIV> <userinput>ls</userinput>
+K AppEvents
+K Console
+K Control Panel
+K Environment
+K Identities
+K Keyboard Layout
+K Network
+K Printers
+K Software
+K UNICODE Program Groups
+K Windows 3.1 Migration Status
+$$$PROTO.HIV> <userinput>exit</userinput>
+</screen>
+
+<para>Listing the subkeys of HKEY_CURRENT_USER\AppEvents on a remote computer:</para>
+<screen>
+<userinput>regshell --remote=ncacn_np:aurelia -c "jelmer%secret"</userinput>
+HKEY_CURRENT_MACHINE> <userinput>predef HKEY_CURRENT_USER</userinput>
+HKEY_CURRENT_USER> <userinput>cd AppEvents</userinput>
+Current path is: HKEY_CURRENT_USER\AppEvents
+HKEY_CURRENT_USER\AppEvents> <userinput>ls</userinput>
+K EventLabels
+K Schemes
+HKEY_CURRENT_USER\AppEvents> <userinput>exit</userinput>
+</screen>
+</refsect1>
+
+<refsect1>
+ <title>VERSION</title>
+
+ <para>This man page is correct for version 4.0 of the Samba suite.</para>
+</refsect1>
+
+<refsect1>
+ <title>SEE ALSO</title>
+
+ <para>regtree, regdiff, regpatch, gregedit, samba</para>
+
+</refsect1>
+
+<refsect1>
+ <title>AUTHOR</title>
+
+ <para>This utility is part of the <ulink url="http://www.samba.org/">Samba</ulink> suite, which is developed by the global <ulink url="http://www.samba.org/samba/team/">Samba Team</ulink>.</para>
+
+ <para>This manpage and regshell were written by Jelmer Vernooij. </para>
+
+</refsect1>
+
+</refentry>
diff --git a/source4/lib/registry/man/regtree.1.xml b/source4/lib/registry/man/regtree.1.xml
new file mode 100644
index 0000000000..93f15e1fb2
--- /dev/null
+++ b/source4/lib/registry/man/regtree.1.xml
@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE refentry PUBLIC "-//Samba-Team//DTD DocBook V4.2-Based Variant V1.0//EN" "http://www.samba.org/samba/DTD/samba-doc">
+<refentry id="regtree.1">
+
+<refmeta>
+ <refentrytitle>regtree</refentrytitle>
+ <manvolnum>1</manvolnum>
+</refmeta>
+
+
+<refnamediv>
+ <refname>regtree</refname>
+ <refpurpose>Text-mode registry viewer</refpurpose>
+</refnamediv>
+
+<refsynopsisdiv>
+ <cmdsynopsis>
+ <command>regtree</command>
+ <arg choice="opt">--help</arg>
+ <arg choice="opt">--backend=BACKEND</arg>
+ <arg choice="opt">--fullpath</arg>
+ <arg choice="opt">--no-values</arg>
+ <arg choice="opt">--credentials=CREDENTIALS</arg>
+ <arg choice="opt">location</arg>
+ </cmdsynopsis>
+</refsynopsisdiv>
+
+<refsect1>
+ <title>DESCRIPTION</title>
+
+ <para>The regtree utility prints out all the contents of a
+ Windows registry file. Subkeys are printed with one level
+ more indentation than their parents. </para>
+
+</refsect1>
+
+
+<refsect1>
+ <title>OPTIONS</title>
+
+ <variablelist>
+ <varlistentry>
+ <term>--help</term>
+ <listitem><para>
+ Show list of available options.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>--backend BACKEND</term>
+ <listitem><para>Name of backend to load. Possible values are:
+ creg, regf, dir and rpc. The default is <emphasis>dir</emphasis>.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>--credentials=CREDENTIALS</term>
+ <listitem><para>
+ Credentials to use, if any. Password should be separated from user name by a percent sign.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>--fullpath</term>
+ <listitem><para>
+ Print the full path to each key instead of only its name.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry><term>--no-values</term>
+ <listitem><para>Don't print values, just keys.</para></listitem>
+ </varlistentry>
+ </variablelist>
+
+</refsect1>
+
+<refsect1>
+ <title>VERSION</title>
+
+ <para>This man page is correct for version 4.0 of the Samba suite.</para>
+</refsect1>
+
+<refsect1>
+ <title>SEE ALSO</title>
+
+ <para>gregedit, regshell, regdiff, regpatch, samba</para>
+
+</refsect1>
+
+<refsect1>
+ <title>AUTHOR</title>
+
+ <para>This utility is part of the <ulink url="http://www.samba.org/">Samba</ulink> suite, which is developed by the global <ulink url="http://www.samba.org/samba/team/">Samba Team</ulink>.</para>
+
+ <para>This manpage and regtree were written by Jelmer Vernooij. </para>
+
+</refsect1>
+
+</refentry>
diff --git a/source4/lib/registry/patchfile.c b/source4/lib/registry/patchfile.c
new file mode 100644
index 0000000000..925806985e
--- /dev/null
+++ b/source4/lib/registry/patchfile.c
@@ -0,0 +1,496 @@
+/*
+ Unix SMB/CIFS implementation.
+ Reading registry patch files
+
+ Copyright (C) Jelmer Vernooij 2004-2007
+ Copyright (C) Wilco Baan Hofman 2006
+ Copyright (C) Matthias Dieter Wallnöfer 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/registry/registry.h"
+#include "system/filesys.h"
+
+
+_PUBLIC_ WERROR reg_preg_diff_load(int fd,
+ struct smb_iconv_convenience *iconv_convenience,
+ const struct reg_diff_callbacks *callbacks,
+ void *callback_data);
+
+_PUBLIC_ WERROR reg_dotreg_diff_load(int fd,
+ struct smb_iconv_convenience *iconv_convenience,
+ const struct reg_diff_callbacks *callbacks,
+ void *callback_data);
+
+/*
+ * Generate difference between two keys
+ */
+WERROR reg_generate_diff_key(struct registry_key *oldkey,
+ struct registry_key *newkey,
+ const char *path,
+ const struct reg_diff_callbacks *callbacks,
+ void *callback_data)
+{
+ int i;
+ struct registry_key *t1 = NULL, *t2 = NULL;
+ char *tmppath;
+ const char *keyname1;
+ WERROR error, error1, error2;
+ TALLOC_CTX *mem_ctx = talloc_init("writediff");
+ uint32_t old_num_subkeys, old_num_values,
+ new_num_subkeys, new_num_values;
+
+ if (oldkey != NULL) {
+ error = reg_key_get_info(mem_ctx, oldkey, NULL,
+ &old_num_subkeys, &old_num_values,
+ NULL, NULL, NULL, NULL);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Error occurred while getting key info: %s\n",
+ win_errstr(error)));
+ talloc_free(mem_ctx);
+ return error;
+ }
+ } else {
+ old_num_subkeys = 0;
+ old_num_values = 0;
+ }
+
+ /* Subkeys that were changed or deleted */
+ for (i = 0; i < old_num_subkeys; i++) {
+ error1 = reg_key_get_subkey_by_index(mem_ctx, oldkey, i,
+ &keyname1, NULL, NULL);
+ if (!W_ERROR_IS_OK(error1)) {
+ DEBUG(0, ("Error occurred while getting subkey by index: %s\n",
+ win_errstr(error1)));
+ continue;
+ }
+
+ if (newkey != NULL) {
+ error2 = reg_open_key(mem_ctx, newkey, keyname1, &t2);
+ } else {
+ error2 = WERR_BADFILE;
+ t2 = NULL;
+ }
+
+ if (!W_ERROR_IS_OK(error2) && !W_ERROR_EQUAL(error2, WERR_BADFILE)) {
+ DEBUG(0, ("Error occured while getting subkey by name: %s\n",
+ win_errstr(error2)));
+ talloc_free(mem_ctx);
+ return error2;
+ }
+
+ /* if "error2" is going to be "WERR_BADFILE", then newkey */
+ /* didn't have such a subkey and therefore add a del diff */
+ tmppath = talloc_asprintf(mem_ctx, "%s\\%s", path, keyname1);
+ if (!W_ERROR_IS_OK(error2))
+ callbacks->del_key(callback_data, tmppath);
+
+ /* perform here also the recursive invocation */
+ error1 = reg_open_key(mem_ctx, oldkey, keyname1, &t1);
+ if (!W_ERROR_IS_OK(error1)) {
+ DEBUG(0, ("Error occured while getting subkey by name: %s\n",
+ win_errstr(error1)));
+ talloc_free(mem_ctx);
+ return error1;
+ }
+ reg_generate_diff_key(t1, t2, tmppath, callbacks, callback_data);
+
+ talloc_free(tmppath);
+ }
+
+ if (newkey != NULL) {
+ error = reg_key_get_info(mem_ctx, newkey, NULL,
+ &new_num_subkeys, &new_num_values,
+ NULL, NULL, NULL, NULL);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Error occurred while getting key info: %s\n",
+ win_errstr(error)));
+ talloc_free(mem_ctx);
+ return error;
+ }
+ } else {
+ new_num_subkeys = 0;
+ new_num_values = 0;
+ }
+
+ /* Subkeys that were added */
+ for(i = 0; i < new_num_subkeys; i++) {
+ error1 = reg_key_get_subkey_by_index(mem_ctx, newkey, i,
+ &keyname1, NULL, NULL);
+ if (!W_ERROR_IS_OK(error1)) {
+ DEBUG(0, ("Error occurred while getting subkey by index: %s\n",
+ win_errstr(error1)));
+ talloc_free(mem_ctx);
+ return error1;
+ }
+
+ if (oldkey != NULL) {
+ error2 = reg_open_key(mem_ctx, oldkey, keyname1, &t1);
+
+ if (W_ERROR_IS_OK(error2))
+ continue;
+ } else {
+ error2 = WERR_BADFILE;
+ t1 = NULL;
+ }
+
+ if (!W_ERROR_EQUAL(error2, WERR_BADFILE)) {
+ DEBUG(0, ("Error occurred while getting subkey by name: %s\n",
+ win_errstr(error2)));
+ talloc_free(mem_ctx);
+ return error2;
+ }
+
+ /* oldkey didn't have such a subkey, add add diff */
+ tmppath = talloc_asprintf(mem_ctx, "%s\\%s", path, keyname1);
+ callbacks->add_key(callback_data, tmppath);
+
+ /* perform here also the recursive invocation */
+ error1 = reg_open_key(mem_ctx, newkey, keyname1, &t2);
+ if (!W_ERROR_IS_OK(error1)) {
+ DEBUG(0, ("Error occured while getting subkey by name: %s\n",
+ win_errstr(error1)));
+ talloc_free(mem_ctx);
+ return error1;
+ }
+ reg_generate_diff_key(t1, t2, tmppath, callbacks, callback_data);
+
+ talloc_free(tmppath);
+ }
+
+ /* Values that were added or changed */
+ for(i = 0; i < new_num_values; i++) {
+ const char *name;
+ uint32_t type1, type2;
+ DATA_BLOB contents1, contents2;
+
+ error1 = reg_key_get_value_by_index(mem_ctx, newkey, i,
+ &name, &type1, &contents1);
+ if (!W_ERROR_IS_OK(error1)) {
+ DEBUG(0, ("Unable to get value by index: %s\n",
+ win_errstr(error1)));
+ talloc_free(mem_ctx);
+ return error1;
+ }
+
+ if (oldkey != NULL) {
+ error2 = reg_key_get_value_by_name(mem_ctx, oldkey,
+ name, &type2,
+ &contents2);
+ } else
+ error2 = WERR_BADFILE;
+
+ if (!W_ERROR_IS_OK(error2)
+ && !W_ERROR_EQUAL(error2, WERR_BADFILE)) {
+ DEBUG(0, ("Error occurred while getting value by name: %s\n",
+ win_errstr(error2)));
+ talloc_free(mem_ctx);
+ return error2;
+ }
+
+ if (W_ERROR_IS_OK(error2)
+ && (data_blob_cmp(&contents1, &contents2) == 0)
+ && (type1 == type2))
+ continue;
+
+ callbacks->set_value(callback_data, path, name,
+ type1, contents1);
+ }
+
+ /* Values that were deleted */
+ for (i = 0; i < old_num_values; i++) {
+ const char *name;
+ uint32_t type;
+ DATA_BLOB contents;
+
+ error1 = reg_key_get_value_by_index(mem_ctx, oldkey, i, &name,
+ &type, &contents);
+ if (!W_ERROR_IS_OK(error1)) {
+ DEBUG(0, ("Unable to get value by index: %s\n",
+ win_errstr(error1)));
+ talloc_free(mem_ctx);
+ return error1;
+ }
+
+ if (newkey != NULL)
+ error2 = reg_key_get_value_by_name(mem_ctx, newkey,
+ name, &type, &contents);
+ else
+ error2 = WERR_BADFILE;
+
+ if (W_ERROR_IS_OK(error2))
+ continue;
+
+ if (!W_ERROR_EQUAL(error2, WERR_BADFILE)) {
+ DEBUG(0, ("Error occurred while getting value by name: %s\n",
+ win_errstr(error2)));
+ talloc_free(mem_ctx);
+ return error2;
+ }
+
+ callbacks->del_value(callback_data, path, name);
+ }
+
+ talloc_free(mem_ctx);
+ return WERR_OK;
+}
+
+/**
+ * Generate diff between two registry contexts
+ */
+_PUBLIC_ WERROR reg_generate_diff(struct registry_context *ctx1,
+ struct registry_context *ctx2,
+ const struct reg_diff_callbacks *callbacks,
+ void *callback_data)
+{
+ int i;
+ WERROR error;
+
+ for (i = 0; reg_predefined_keys[i].name; i++) {
+ struct registry_key *r1 = NULL, *r2 = NULL;
+
+ error = reg_get_predefined_key(ctx1,
+ reg_predefined_keys[i].handle, &r1);
+ if (!W_ERROR_IS_OK(error) &&
+ !W_ERROR_EQUAL(error, WERR_BADFILE)) {
+ DEBUG(0, ("Unable to open hive %s for backend 1\n",
+ reg_predefined_keys[i].name));
+ continue;
+ }
+
+ error = reg_get_predefined_key(ctx2,
+ reg_predefined_keys[i].handle, &r2);
+ if (!W_ERROR_IS_OK(error) &&
+ !W_ERROR_EQUAL(error, WERR_BADFILE)) {
+ DEBUG(0, ("Unable to open hive %s for backend 2\n",
+ reg_predefined_keys[i].name));
+ continue;
+ }
+
+ error = reg_generate_diff_key(r1, r2,
+ reg_predefined_keys[i].name, callbacks,
+ callback_data);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Unable to determine diff: %s\n",
+ win_errstr(error)));
+ return error;
+ }
+ }
+ if (callbacks->done != NULL) {
+ callbacks->done(callback_data);
+ }
+ return WERR_OK;
+}
+
+/**
+ * Load diff file
+ */
+_PUBLIC_ WERROR reg_diff_load(const char *filename,
+ struct smb_iconv_convenience *iconv_convenience,
+ const struct reg_diff_callbacks *callbacks,
+ void *callback_data)
+{
+ int fd;
+ char hdr[4];
+
+ fd = open(filename, O_RDONLY, 0);
+ if (fd == -1) {
+ DEBUG(0, ("Error opening registry patch file `%s'\n",
+ filename));
+ return WERR_GENERAL_FAILURE;
+ }
+
+ if (read(fd, &hdr, 4) != 4) {
+ DEBUG(0, ("Error reading registry patch file `%s'\n",
+ filename));
+ return WERR_GENERAL_FAILURE;
+ }
+
+ /* Reset position in file */
+ lseek(fd, 0, SEEK_SET);
+#if 0 /* These backends are not supported yet. */
+ if (strncmp(hdr, "CREG", 4) == 0) {
+ /* Must be a W9x CREG Config.pol file */
+ return reg_creg_diff_load(diff, fd);
+ } else if (strncmp(hdr, "regf", 4) == 0) {
+ /* Must be a REGF NTConfig.pol file */
+ return reg_regf_diff_load(diff, fd);
+ } else
+#endif
+ if (strncmp(hdr, "PReg", 4) == 0) {
+ /* Must be a GPO Registry.pol file */
+ return reg_preg_diff_load(fd, iconv_convenience, callbacks, callback_data);
+ } else {
+ /* Must be a normal .REG file */
+ return reg_dotreg_diff_load(fd, iconv_convenience, callbacks, callback_data);
+ }
+}
+
+/**
+ * The reg_diff_apply functions
+ */
+static WERROR reg_diff_apply_add_key(void *_ctx, const char *key_name)
+{
+ struct registry_context *ctx = (struct registry_context *)_ctx;
+ struct registry_key *tmp;
+ char *buf, *buf_ptr;
+ WERROR error;
+
+ /* Recursively create the path */
+ buf = talloc_strdup(ctx, key_name);
+ buf_ptr = buf;
+
+ while (*buf_ptr++ != '\0' ) {
+ if (*buf_ptr == '\\') {
+ *buf_ptr = '\0';
+ error = reg_key_add_abs(ctx, ctx, buf, 0, NULL, &tmp);
+
+ if (!W_ERROR_EQUAL(error, WERR_ALREADY_EXISTS) &&
+ !W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Error adding new key '%s': %s\n",
+ key_name, win_errstr(error)));
+ return error;
+ }
+ *buf_ptr++ = '\\';
+ }
+ }
+
+ /* Add the key */
+ error = reg_key_add_abs(ctx, ctx, key_name, 0, NULL, &tmp);
+
+ if (!W_ERROR_EQUAL(error, WERR_ALREADY_EXISTS) &&
+ !W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Error adding new key '%s': %s\n",
+ key_name, win_errstr(error)));
+ return error;
+ }
+ return WERR_OK;
+}
+
+static WERROR reg_diff_apply_del_key(void *_ctx, const char *key_name)
+{
+ struct registry_context *ctx = (struct registry_context *)_ctx;
+
+ /* We can't proof here for success, because a common superkey could */
+ /* have been deleted before the subkey's (diff order). This removed */
+ /* therefore all childs recursively and the "WERR_BADFILE" result is */
+ /* expected. */
+
+ reg_key_del_abs(ctx, key_name);
+
+ return WERR_OK;
+}
+
+static WERROR reg_diff_apply_set_value(void *_ctx, const char *path,
+ const char *value_name,
+ uint32_t value_type, DATA_BLOB value)
+{
+ struct registry_context *ctx = (struct registry_context *)_ctx;
+ struct registry_key *tmp;
+ WERROR error;
+
+ /* Open key */
+ error = reg_open_key_abs(ctx, ctx, path, &tmp);
+
+ if (W_ERROR_EQUAL(error, WERR_BADFILE)) {
+ DEBUG(0, ("Error opening key '%s'\n", path));
+ return error;
+ }
+
+ /* Set value */
+ error = reg_val_set(tmp, value_name,
+ value_type, value);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Error setting value '%s'\n", value_name));
+ return error;
+ }
+
+ return WERR_OK;
+}
+
+static WERROR reg_diff_apply_del_value(void *_ctx, const char *key_name,
+ const char *value_name)
+{
+ struct registry_context *ctx = (struct registry_context *)_ctx;
+ struct registry_key *tmp;
+ WERROR error;
+
+ /* Open key */
+ error = reg_open_key_abs(ctx, ctx, key_name, &tmp);
+
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Error opening key '%s'\n", key_name));
+ return error;
+ }
+
+ error = reg_del_value(tmp, value_name);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Error deleting value '%s'\n", value_name));
+ return error;
+ }
+
+
+ return WERR_OK;
+}
+
+static WERROR reg_diff_apply_del_all_values(void *_ctx, const char *key_name)
+{
+ struct registry_context *ctx = (struct registry_context *)_ctx;
+ struct registry_key *key;
+ WERROR error;
+ const char* value_name;
+
+ error = reg_open_key_abs(ctx, ctx, key_name, &key);
+
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Error opening key '%s'\n", key_name));
+ return error;
+ }
+
+ W_ERROR_NOT_OK_RETURN(reg_key_get_info(ctx, key, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL));
+
+ while (W_ERROR_IS_OK(reg_key_get_value_by_index(
+ ctx, key, 0, &value_name, NULL, NULL))) {
+ error = reg_del_value(key, value_name);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Error deleting value '%s'\n", value_name));
+ return error;
+ }
+ }
+
+ return WERR_OK;
+}
+
+/**
+ * Apply diff to a registry context
+ */
+_PUBLIC_ WERROR reg_diff_apply(struct registry_context *ctx,
+ struct smb_iconv_convenience *iconv_convenience,
+ const char *filename)
+{
+ struct reg_diff_callbacks callbacks;
+
+ callbacks.add_key = reg_diff_apply_add_key;
+ callbacks.del_key = reg_diff_apply_del_key;
+ callbacks.set_value = reg_diff_apply_set_value;
+ callbacks.del_value = reg_diff_apply_del_value;
+ callbacks.del_all_values = reg_diff_apply_del_all_values;
+ callbacks.done = NULL;
+
+ return reg_diff_load(filename, iconv_convenience,
+ &callbacks, ctx);
+}
diff --git a/source4/lib/registry/patchfile_dotreg.c b/source4/lib/registry/patchfile_dotreg.c
new file mode 100644
index 0000000000..3b5708978d
--- /dev/null
+++ b/source4/lib/registry/patchfile_dotreg.c
@@ -0,0 +1,265 @@
+/*
+ Unix SMB/CIFS implementation.
+ Reading .REG files
+
+ Copyright (C) Jelmer Vernooij 2004-2007
+ Copyright (C) Wilco Baan Hofman 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* FIXME Newer .REG files, created by Windows XP and above use unicode UCS-2 */
+
+#include "includes.h"
+#include "lib/registry/registry.h"
+#include "system/filesys.h"
+
+/**
+ * @file
+ * @brief Registry patch files
+ */
+
+#define HEADER_STRING "REGEDIT4"
+
+struct dotreg_data {
+ int fd;
+ struct smb_iconv_convenience *iconv_convenience;
+};
+
+static WERROR reg_dotreg_diff_add_key(void *_data, const char *key_name)
+{
+ struct dotreg_data *data = (struct dotreg_data *)_data;
+
+ fdprintf(data->fd, "\n[%s]\n", key_name);
+
+ return WERR_OK;
+}
+
+static WERROR reg_dotreg_diff_del_key(void *_data, const char *key_name)
+{
+ struct dotreg_data *data = (struct dotreg_data *)_data;
+
+ fdprintf(data->fd, "\n[-%s]\n", key_name);
+
+ return WERR_OK;
+}
+
+static WERROR reg_dotreg_diff_set_value(void *_data, const char *path,
+ const char *value_name,
+ uint32_t value_type, DATA_BLOB value)
+{
+ struct dotreg_data *data = (struct dotreg_data *)_data;
+
+ fdprintf(data->fd, "\"%s\"=%s:%s\n",
+ value_name, str_regtype(value_type),
+ reg_val_data_string(NULL, data->iconv_convenience, value_type, value));
+
+ return WERR_OK;
+}
+
+static WERROR reg_dotreg_diff_del_value(void *_data, const char *path,
+ const char *value_name)
+{
+ struct dotreg_data *data = (struct dotreg_data *)_data;
+
+ fdprintf(data->fd, "\"%s\"=-\n", value_name);
+
+ return WERR_OK;
+}
+
+static WERROR reg_dotreg_diff_done(void *_data)
+{
+ struct dotreg_data *data = (struct dotreg_data *)_data;
+
+ close(data->fd);
+ talloc_free(data);
+
+ return WERR_OK;
+}
+
+static WERROR reg_dotreg_diff_del_all_values(void *callback_data,
+ const char *key_name)
+{
+ return WERR_NOT_SUPPORTED;
+}
+
+/**
+ * Save registry diff
+ */
+_PUBLIC_ WERROR reg_dotreg_diff_save(TALLOC_CTX *ctx, const char *filename,
+ struct smb_iconv_convenience *iconv_convenience,
+ struct reg_diff_callbacks **callbacks,
+ void **callback_data)
+{
+ struct dotreg_data *data;
+
+ data = talloc_zero(ctx, struct dotreg_data);
+ *callback_data = data;
+
+ data->iconv_convenience = iconv_convenience;
+
+ if (filename) {
+ data->fd = open(filename, O_CREAT|O_WRONLY, 0755);
+ if (data->fd < 0) {
+ DEBUG(0, ("Unable to open %s\n", filename));
+ return WERR_BADFILE;
+ }
+ } else {
+ data->fd = STDOUT_FILENO;
+ }
+
+ fdprintf(data->fd, "%s\n\n", HEADER_STRING);
+
+ *callbacks = talloc(ctx, struct reg_diff_callbacks);
+
+ (*callbacks)->add_key = reg_dotreg_diff_add_key;
+ (*callbacks)->del_key = reg_dotreg_diff_del_key;
+ (*callbacks)->set_value = reg_dotreg_diff_set_value;
+ (*callbacks)->del_value = reg_dotreg_diff_del_value;
+ (*callbacks)->del_all_values = reg_dotreg_diff_del_all_values;
+ (*callbacks)->done = reg_dotreg_diff_done;
+
+ return WERR_OK;
+}
+
+/**
+ * Load diff file
+ */
+_PUBLIC_ WERROR reg_dotreg_diff_load(int fd,
+ struct smb_iconv_convenience *iconv_convenience,
+ const struct reg_diff_callbacks *callbacks,
+ void *callback_data)
+{
+ char *line, *p, *q;
+ char *curkey = NULL;
+ TALLOC_CTX *mem_ctx = talloc_init("reg_dotreg_diff_load");
+ WERROR error;
+ uint32_t value_type;
+ DATA_BLOB value;
+
+ line = afdgets(fd, mem_ctx, 0);
+ if (!line) {
+ DEBUG(0, ("Can't read from file.\n"));
+ talloc_free(mem_ctx);
+ close(fd);
+ return WERR_GENERAL_FAILURE;
+ }
+
+ while ((line = afdgets(fd, mem_ctx, 0))) {
+ /* Ignore comments and empty lines */
+ if (strlen(line) == 0 || line[0] == ';') {
+ talloc_free(line);
+
+ if (curkey) {
+ talloc_free(curkey);
+ }
+ curkey = NULL;
+ continue;
+ }
+
+ /* Start of key */
+ if (line[0] == '[') {
+ p = strchr_m(line, ']');
+ if (p[strlen(p)-1] != ']') {
+ DEBUG(0, ("Missing ']'\n"));
+ return WERR_GENERAL_FAILURE;
+ }
+ /* Deleting key */
+ if (line[1] == '-') {
+ curkey = talloc_strndup(line, line+2, strlen(line)-3);
+
+ error = callbacks->del_key(callback_data,
+ curkey);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(0,("Error deleting key %s\n",
+ curkey));
+ talloc_free(mem_ctx);
+ return error;
+ }
+
+ talloc_free(line);
+ curkey = NULL;
+ continue;
+ }
+ curkey = talloc_strndup(mem_ctx, line+1, strlen(line)-2);
+
+ error = callbacks->add_key(callback_data, curkey);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(0,("Error adding key %s\n", curkey));
+ talloc_free(mem_ctx);
+ return error;
+ }
+
+ talloc_free(line);
+ continue;
+ }
+
+ /* Deleting/Changing value */
+ p = strchr_m(line, '=');
+ if (p == NULL) {
+ DEBUG(0, ("Malformed line\n"));
+ talloc_free(line);
+ continue;
+ }
+
+ *p = '\0'; p++;
+
+ if (curkey == NULL) {
+ DEBUG(0, ("Value change without key\n"));
+ talloc_free(line);
+ continue;
+ }
+
+ /* Delete value */
+ if (strcmp(p, "-") == 0) {
+ error = callbacks->del_value(callback_data,
+ curkey, line);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Error deleting value %s in key %s\n",
+ line, curkey));
+ talloc_free(mem_ctx);
+ return error;
+ }
+
+ talloc_free(line);
+ continue;
+ }
+
+ q = strchr_m(p, ':');
+ if (q) {
+ *q = '\0';
+ q++;
+ }
+
+ reg_string_to_val(line, iconv_convenience,
+ q?p:"REG_SZ", q?q:p,
+ &value_type, &value);
+
+ error = callbacks->set_value(callback_data, curkey, line,
+ value_type, value);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Error setting value for %s in %s\n",
+ line, curkey));
+ talloc_free(mem_ctx);
+ return error;
+ }
+
+ talloc_free(line);
+ }
+
+ close(fd);
+
+ return WERR_OK;
+}
diff --git a/source4/lib/registry/patchfile_preg.c b/source4/lib/registry/patchfile_preg.c
new file mode 100644
index 0000000000..e9801bb425
--- /dev/null
+++ b/source4/lib/registry/patchfile_preg.c
@@ -0,0 +1,358 @@
+/*
+ Unix SMB/CIFS implementation.
+ Reading Registry.pol PReg registry files
+
+ Copyright (C) Wilco Baan Hofman 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "lib/registry/registry.h"
+#include "system/filesys.h"
+#include "librpc/gen_ndr/winreg.h"
+
+struct preg_data {
+ int fd;
+ TALLOC_CTX *ctx;
+ struct smb_iconv_convenience *ic;
+};
+
+static WERROR preg_read_utf16(struct smb_iconv_convenience *ic, int fd, char *c)
+{
+ uint16_t v;
+
+ if (read(fd, &v, 2) < 2) {
+ return WERR_GENERAL_FAILURE;
+ }
+ push_codepoint(ic, c, v);
+ return WERR_OK;
+}
+static WERROR preg_write_utf16(struct smb_iconv_convenience *ic, int fd, const char *string)
+{
+ codepoint_t v;
+ uint16_t i;
+ size_t size;
+
+ for (i = 0; i < strlen(string); i+=size) {
+ v = next_codepoint_convenience(ic, &string[i], &size);
+ if (write(fd, &v, 2) < 2) {
+ return WERR_GENERAL_FAILURE;
+ }
+ }
+ return WERR_OK;
+}
+/* PReg does not support adding keys. */
+static WERROR reg_preg_diff_add_key(void *_data, const char *key_name)
+{
+ return WERR_OK;
+}
+
+static WERROR reg_preg_diff_set_value(void *_data, const char *key_name,
+ const char *value_name,
+ uint32_t value_type, DATA_BLOB value_data)
+{
+ struct preg_data *data = (struct preg_data *)_data;
+ uint32_t buf;
+
+ preg_write_utf16(data->ic, data->fd, "[");
+ preg_write_utf16(data->ic, data->fd, key_name);
+ preg_write_utf16(data->ic, data->fd, ";");
+ preg_write_utf16(data->ic, data->fd, value_name);
+ preg_write_utf16(data->ic, data->fd, ";");
+ SIVAL(&buf, 0, value_type);
+ write(data->fd, &buf, sizeof(uint32_t));
+ preg_write_utf16(data->ic, data->fd, ";");
+ SIVAL(&buf, 0, value_data.length);
+ write(data->fd, &buf, sizeof(uint32_t));
+ preg_write_utf16(data->ic, data->fd, ";");
+ write(data->fd, value_data.data, value_data.length);
+ preg_write_utf16(data->ic, data->fd, "]");
+
+ return WERR_OK;
+}
+
+static WERROR reg_preg_diff_del_key(void *_data, const char *key_name)
+{
+ struct preg_data *data = (struct preg_data *)_data;
+ char *parent_name;
+ DATA_BLOB blob;
+
+ parent_name = talloc_strndup(data->ctx, key_name, strrchr(key_name, '\\')-key_name);
+ blob.data = (uint8_t *)talloc_strndup(data->ctx, key_name+(strrchr(key_name, '\\')-key_name)+1,
+ strlen(key_name)-(strrchr(key_name, '\\')-key_name));
+ blob.length = strlen((char *)blob.data)+1;
+
+
+ /* FIXME: These values should be accumulated to be written at done(). */
+ return reg_preg_diff_set_value(data, parent_name, "**DeleteKeys", REG_SZ, blob);
+}
+
+static WERROR reg_preg_diff_del_value(void *_data, const char *key_name,
+ const char *value_name)
+{
+ struct preg_data *data = (struct preg_data *)_data;
+ char *val;
+ DATA_BLOB blob;
+
+ val = talloc_asprintf(data->ctx, "**Del.%s", value_name);
+
+ blob.data = (uint8_t *)talloc(data->ctx, uint32_t);
+ *(uint32_t *)blob.data = 0;
+ blob.length = 4;
+ return reg_preg_diff_set_value(data, key_name, val, REG_DWORD, blob);
+}
+
+static WERROR reg_preg_diff_del_all_values(void *_data, const char *key_name)
+{
+ struct preg_data *data = (struct preg_data *)_data;
+ DATA_BLOB blob;
+
+ blob.data = (uint8_t *)talloc(data->ctx, uint32_t);
+ *(uint32_t *)blob.data = 0;
+ blob.length = 4;
+
+ return reg_preg_diff_set_value(data, key_name, "**DelVals.", REG_DWORD, blob);
+}
+
+static WERROR reg_preg_diff_done(void *_data)
+{
+ struct preg_data *data = (struct preg_data *)_data;
+
+ close(data->fd);
+ talloc_free(data);
+ return WERR_OK;
+}
+
+/**
+ * Save registry diff
+ */
+_PUBLIC_ WERROR reg_preg_diff_save(TALLOC_CTX *ctx, const char *filename,
+ struct smb_iconv_convenience *ic,
+ struct reg_diff_callbacks **callbacks,
+ void **callback_data)
+{
+ struct preg_data *data;
+ struct {
+ char hdr[4];
+ uint32_t version;
+ } preg_header;
+
+
+ data = talloc_zero(ctx, struct preg_data);
+ *callback_data = data;
+
+ if (filename) {
+ data->fd = open(filename, O_CREAT|O_WRONLY, 0755);
+ if (data->fd < 0) {
+ DEBUG(0, ("Unable to open %s\n", filename));
+ return WERR_BADFILE;
+ }
+ } else {
+ data->fd = STDOUT_FILENO;
+ }
+
+ strncpy(preg_header.hdr, "PReg", 4);
+ SIVAL(&preg_header, 4, 1);
+ write(data->fd, (uint8_t *)&preg_header,8);
+
+ data->ctx = ctx;
+ data->ic = ic;
+
+ *callbacks = talloc(ctx, struct reg_diff_callbacks);
+
+ (*callbacks)->add_key = reg_preg_diff_add_key;
+ (*callbacks)->del_key = reg_preg_diff_del_key;
+ (*callbacks)->set_value = reg_preg_diff_set_value;
+ (*callbacks)->del_value = reg_preg_diff_del_value;
+ (*callbacks)->del_all_values = reg_preg_diff_del_all_values;
+ (*callbacks)->done = reg_preg_diff_done;
+
+ return WERR_OK;
+}
+/**
+ * Load diff file
+ */
+_PUBLIC_ WERROR reg_preg_diff_load(int fd,
+ struct smb_iconv_convenience *iconv_convenience,
+ const struct reg_diff_callbacks *callbacks,
+ void *callback_data)
+{
+ struct {
+ char hdr[4];
+ uint32_t version;
+ } preg_header;
+ char *buf;
+ size_t buf_size = 1024;
+ char *buf_ptr;
+ TALLOC_CTX *mem_ctx = talloc_init("reg_preg_diff_load");
+ WERROR ret = WERR_OK;
+ DATA_BLOB data = {NULL, 0};
+ char *key = NULL;
+ char *value_name = NULL;
+
+ buf = talloc_array(mem_ctx, char, buf_size);
+ buf_ptr = buf;
+
+ /* Read first 8 bytes (the header) */
+ if (read(fd, &preg_header, 8) != 8) {
+ DEBUG(0, ("Could not read PReg file: %s\n",
+ strerror(errno)));
+ ret = WERR_GENERAL_FAILURE;
+ goto cleanup;
+ }
+ preg_header.version = IVAL(&preg_header.version, 0);
+
+ if (strncmp(preg_header.hdr, "PReg", 4) != 0) {
+ DEBUG(0, ("This file is not a valid preg registry file\n"));
+ ret = WERR_GENERAL_FAILURE;
+ goto cleanup;
+ }
+ if (preg_header.version > 1) {
+ DEBUG(0, ("Warning: file format version is higher than expected.\n"));
+ }
+
+ /* Read the entries */
+ while(1) {
+ uint32_t value_type, length;
+
+ if (!W_ERROR_IS_OK(preg_read_utf16(iconv_convenience, fd, buf_ptr))) {
+ break;
+ }
+ if (*buf_ptr != '[') {
+ DEBUG(0, ("Error in PReg file.\n"));
+ ret = WERR_GENERAL_FAILURE;
+ goto cleanup;
+ }
+
+ /* Get the path */
+ buf_ptr = buf;
+ while (W_ERROR_IS_OK(preg_read_utf16(iconv_convenience, fd, buf_ptr)) &&
+ *buf_ptr != ';' && buf_ptr-buf < buf_size) {
+ buf_ptr++;
+ }
+ buf[buf_ptr-buf] = '\0';
+ key = talloc_strdup(mem_ctx, buf);
+
+ /* Get the name */
+ buf_ptr = buf;
+ while (W_ERROR_IS_OK(preg_read_utf16(iconv_convenience, fd, buf_ptr)) &&
+ *buf_ptr != ';' && buf_ptr-buf < buf_size) {
+ buf_ptr++;
+ }
+ buf[buf_ptr-buf] = '\0';
+ value_name = talloc_strdup(mem_ctx, buf);
+
+ /* Get the type */
+ if (read(fd, &value_type, 4) < 4) {
+ DEBUG(0, ("Error while reading PReg\n"));
+ ret = WERR_GENERAL_FAILURE;
+ goto cleanup;
+ }
+ value_type = IVAL(&value_type, 0);
+
+ /* Read past delimiter */
+ buf_ptr = buf;
+ if (!(W_ERROR_IS_OK(preg_read_utf16(iconv_convenience, fd, buf_ptr)) &&
+ *buf_ptr == ';') && buf_ptr-buf < buf_size) {
+ DEBUG(0, ("Error in PReg file.\n"));
+ ret = WERR_GENERAL_FAILURE;
+ goto cleanup;
+ }
+ /* Get data length */
+ if (read(fd, &length, 4) < 4) {
+ DEBUG(0, ("Error while reading PReg\n"));
+ ret = WERR_GENERAL_FAILURE;
+ goto cleanup;
+ }
+ /* Read past delimiter */
+ buf_ptr = buf;
+ if (!(W_ERROR_IS_OK(preg_read_utf16(iconv_convenience, fd, buf_ptr)) &&
+ *buf_ptr == ';') && buf_ptr-buf < buf_size) {
+ DEBUG(0, ("Error in PReg file.\n"));
+ ret = WERR_GENERAL_FAILURE;
+ goto cleanup;
+ }
+ /* Get the data */
+ buf_ptr = buf;
+ if (length < buf_size &&
+ read(fd, buf_ptr, length) != length) {
+ DEBUG(0, ("Error while reading PReg\n"));
+ ret = WERR_GENERAL_FAILURE;
+ goto cleanup;
+ }
+ data = data_blob_talloc(mem_ctx, buf, length);
+
+ /* Check if delimiter is in place (whine if it isn't) */
+ buf_ptr = buf;
+ if (!(W_ERROR_IS_OK(preg_read_utf16(iconv_convenience, fd, buf_ptr)) &&
+ *buf_ptr == ']') && buf_ptr-buf < buf_size) {
+ DEBUG(0, ("Warning: Missing ']' in PReg file, expected ']', got '%c' 0x%x.\n",
+ *buf_ptr, *buf_ptr));
+ }
+
+ if (strcasecmp(value_name, "**DelVals") == 0) {
+ callbacks->del_all_values(callback_data, key);
+ } else if (strncasecmp(value_name, "**Del.",6) == 0) {
+ char *p = value_name+6;
+
+ callbacks->del_value(callback_data, key, p);
+ } else if (strcasecmp(value_name, "**DeleteValues") == 0) {
+ char *p, *q;
+
+ p = (char *) data.data;
+
+ while ((q = strchr_m(p, ';'))) {
+ *q = '\0';
+ q++;
+
+ callbacks->del_value(callback_data, key, p);
+
+ p = q;
+ }
+ callbacks->del_value(callback_data, key, p);
+ } else if (strcasecmp(value_name, "**DeleteKeys") == 0) {
+ char *p, *q, *full_key;
+
+ p = (char *) data.data;
+
+ while ((q = strchr_m(p, ';'))) {
+ *q = '\0';
+ q++;
+
+ full_key = talloc_asprintf(mem_ctx, "%s\\%s",
+ key, p);
+ callbacks->del_key(callback_data, full_key);
+ talloc_free(full_key);
+
+ p = q;
+ }
+ full_key = talloc_asprintf(mem_ctx, "%s\\%s", key, p);
+ callbacks->del_key(callback_data, full_key);
+ talloc_free(full_key);
+ } else {
+ callbacks->add_key(callback_data, key);
+ callbacks->set_value(callback_data, key, value_name,
+ value_type, data);
+ }
+ }
+cleanup:
+ close(fd);
+ talloc_free(data.data);
+ talloc_free(key);
+ talloc_free(value_name);
+ talloc_free(buf);
+ return ret;
+}
diff --git a/source4/lib/registry/pyregistry.c b/source4/lib/registry/pyregistry.c
new file mode 100644
index 0000000000..30becbb1bb
--- /dev/null
+++ b/source4/lib/registry/pyregistry.c
@@ -0,0 +1,443 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Jelmer Vernooij <jelmer@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include <tevent.h>
+#include <Python.h>
+#include "libcli/util/pyerrors.h"
+#include "lib/registry/registry.h"
+#include "scripting/python/modules.h" /* for py_iconv_convenience() */
+#include <pytalloc.h>
+#include "auth/credentials/pycredentials.h"
+#include "param/pyparam.h"
+
+#ifndef Py_RETURN_NONE
+#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
+#endif
+
+PyAPI_DATA(PyTypeObject) PyRegistryKey;
+PyAPI_DATA(PyTypeObject) PyRegistry;
+PyAPI_DATA(PyTypeObject) PyHiveKey;
+
+/*#define PyRegistryKey_AsRegistryKey(obj) py_talloc_get_type(obj, struct registry_key)*/
+#define PyRegistry_AsRegistryContext(obj) ((struct registry_context *)py_talloc_get_ptr(obj))
+#define PyHiveKey_AsHiveKey(obj) ((struct hive_key*)py_talloc_get_ptr(obj))
+
+
+static PyObject *py_get_predefined_key_by_name(PyObject *self, PyObject *args)
+{
+ char *name;
+ WERROR result;
+ struct registry_context *ctx = PyRegistry_AsRegistryContext(self);
+ struct registry_key *key;
+
+ if (!PyArg_ParseTuple(args, "s", &name))
+ return NULL;
+
+ result = reg_get_predefined_key_by_name(ctx, name, &key);
+ PyErr_WERROR_IS_ERR_RAISE(result);
+
+ return py_talloc_import(&PyRegistryKey, key);
+}
+
+static PyObject *py_key_del_abs(PyObject *self, PyObject *args)
+{
+ char *path;
+ WERROR result;
+ struct registry_context *ctx = PyRegistry_AsRegistryContext(self);
+
+ if (!PyArg_ParseTuple(args, "s", &path))
+ return NULL;
+
+ result = reg_key_del_abs(ctx, path);
+ PyErr_WERROR_IS_ERR_RAISE(result);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_get_predefined_key(PyObject *self, PyObject *args)
+{
+ uint32_t hkey;
+ struct registry_context *ctx = PyRegistry_AsRegistryContext(self);
+ WERROR result;
+ struct registry_key *key;
+
+ if (!PyArg_ParseTuple(args, "I", &hkey))
+ return NULL;
+
+ result = reg_get_predefined_key(ctx, hkey, &key);
+ PyErr_WERROR_IS_ERR_RAISE(result);
+
+ return py_talloc_import(&PyRegistryKey, key);
+}
+
+static PyObject *py_diff_apply(PyObject *self, PyObject *args)
+{
+ char *filename;
+ WERROR result;
+ struct registry_context *ctx = PyRegistry_AsRegistryContext(self);
+ if (!PyArg_ParseTuple(args, "s", &filename))
+ return NULL;
+
+ result = reg_diff_apply(ctx, py_iconv_convenience(NULL), filename);
+ PyErr_WERROR_IS_ERR_RAISE(result);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_mount_hive(PyObject *self, PyObject *args)
+{
+ struct registry_context *ctx = PyRegistry_AsRegistryContext(self);
+ uint32_t hkey;
+ PyObject *py_hivekey, *py_elements = Py_None;
+ const char **elements;
+ WERROR result;
+
+ if (!PyArg_ParseTuple(args, "OI|O", &py_hivekey, &hkey, &py_elements))
+ return NULL;
+
+ if (!PyList_Check(py_elements) && py_elements != Py_None) {
+ PyErr_SetString(PyExc_TypeError, "Expected list of elements");
+ return NULL;
+ }
+
+ if (py_elements == Py_None) {
+ elements = NULL;
+ } else {
+ int i;
+ elements = talloc_array(NULL, const char *, PyList_Size(py_elements));
+ for (i = 0; i < PyList_Size(py_elements); i++)
+ elements[i] = PyString_AsString(PyList_GetItem(py_elements, i));
+ }
+
+ SMB_ASSERT(ctx != NULL);
+
+ result = reg_mount_hive(ctx, PyHiveKey_AsHiveKey(py_hivekey), hkey, elements);
+ PyErr_WERROR_IS_ERR_RAISE(result);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *registry_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
+{
+ WERROR result;
+ struct registry_context *ctx;
+ result = reg_open_local(NULL, &ctx);
+ PyErr_WERROR_IS_ERR_RAISE(result);
+ return py_talloc_import(&PyRegistry, ctx);
+}
+
+static PyMethodDef registry_methods[] = {
+ { "get_predefined_key_by_name", py_get_predefined_key_by_name, METH_VARARGS,
+ "S.get_predefined_key_by_name(name) -> key\n"
+ "Find a predefined key by name" },
+ { "key_del_abs", py_key_del_abs, METH_VARARGS, "S.key_del_abs(name) -> None\n"
+ "Delete a key by absolute path." },
+ { "get_predefined_key", py_get_predefined_key, METH_VARARGS, "S.get_predefined_key(hkey_id) -> key\n"
+ "Find a predefined key by id" },
+ { "diff_apply", py_diff_apply, METH_VARARGS, "S.diff_apply(filename) -> None\n"
+ "Apply the diff from the specified file" },
+ { "mount_hive", py_mount_hive, METH_VARARGS, "S.mount_hive(key, key_id, elements=None) -> None\n"
+ "Mount the specified key at the specified path." },
+ { NULL }
+};
+
+PyTypeObject PyRegistry = {
+ .tp_name = "Registry",
+ .tp_methods = registry_methods,
+ .tp_new = registry_new,
+ .tp_basicsize = sizeof(py_talloc_Object),
+ .tp_dealloc = py_talloc_dealloc,
+ .tp_flags = Py_TPFLAGS_DEFAULT,
+};
+
+static PyObject *py_hive_key_del(PyObject *self, PyObject *args)
+{
+ char *name;
+ struct hive_key *key = PyHiveKey_AsHiveKey(self);
+ WERROR result;
+
+ if (!PyArg_ParseTuple(args, "s", &name))
+ return NULL;
+
+ result = hive_key_del(key, name);
+
+ PyErr_WERROR_IS_ERR_RAISE(result);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_hive_key_flush(PyObject *self)
+{
+ WERROR result;
+ struct hive_key *key = PyHiveKey_AsHiveKey(self);
+
+ result = hive_key_flush(key);
+ PyErr_WERROR_IS_ERR_RAISE(result);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_hive_key_del_value(PyObject *self, PyObject *args)
+{
+ char *name;
+ WERROR result;
+ struct hive_key *key = PyHiveKey_AsHiveKey(self);
+
+ if (!PyArg_ParseTuple(args, "s", &name))
+ return NULL;
+
+ result = hive_key_del_value(key, name);
+
+ PyErr_WERROR_IS_ERR_RAISE(result);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_hive_key_set_value(PyObject *self, PyObject *args)
+{
+ char *name;
+ uint32_t type;
+ DATA_BLOB value;
+ WERROR result;
+ struct hive_key *key = PyHiveKey_AsHiveKey(self);
+
+ if (!PyArg_ParseTuple(args, "siz#", &name, &type, &value.data, &value.length))
+ return NULL;
+
+ if (value.data != NULL)
+ result = hive_key_set_value(key, name, type, value);
+ else
+ result = hive_key_del_value(key, name);
+
+ PyErr_WERROR_IS_ERR_RAISE(result);
+
+ Py_RETURN_NONE;
+}
+
+static PyMethodDef hive_key_methods[] = {
+ { "del", py_hive_key_del, METH_VARARGS, "S.del(name) -> None\n"
+ "Delete a subkey" },
+ { "flush", (PyCFunction)py_hive_key_flush, METH_NOARGS, "S.flush() -> None\n"
+ "Flush this key to disk" },
+ { "del_value", py_hive_key_del_value, METH_VARARGS, "S.del_value(name) -> None\n"
+ "Delete a value" },
+ { "set_value", py_hive_key_set_value, METH_VARARGS, "S.set_value(name, type, data) -> None\n"
+ "Set a value" },
+ { NULL }
+};
+
+static PyObject *hive_open(PyTypeObject *type, PyObject *args, PyObject *kwargs)
+{
+ /* reg_open_hive */
+ Py_RETURN_NONE;
+}
+
+PyTypeObject PyHiveKey = {
+ .tp_name = "HiveKey",
+ .tp_methods = hive_key_methods,
+ .tp_new = hive_open,
+ .tp_basicsize = sizeof(py_talloc_Object),
+ .tp_dealloc = py_talloc_dealloc,
+ .tp_flags = Py_TPFLAGS_DEFAULT,
+};
+
+PyTypeObject PyRegistryKey = {
+ .tp_name = "RegistryKey",
+ .tp_basicsize = sizeof(py_talloc_Object),
+ .tp_dealloc = py_talloc_dealloc,
+ .tp_flags = Py_TPFLAGS_DEFAULT,
+};
+
+static PyObject *py_open_samba(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ const char *kwnames[] = { "lp_ctx", "session_info", NULL };
+ struct registry_context *reg_ctx;
+ WERROR result;
+ struct loadparm_context *lp_ctx;
+ PyObject *py_lp_ctx, *py_session_info, *py_credentials;
+ struct auth_session_info *session_info;
+ struct cli_credentials *credentials;
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOO", discard_const_p(char *, kwnames),
+ &py_lp_ctx, &py_session_info, &py_credentials))
+ return NULL;
+
+ lp_ctx = lp_from_py_object(py_lp_ctx);
+ if (lp_ctx == NULL) {
+ PyErr_SetString(PyExc_TypeError, "Expected loadparm context");
+ return NULL;
+ }
+
+ credentials = cli_credentials_from_py_object(py_credentials);
+ if (credentials == NULL) {
+ PyErr_SetString(PyExc_TypeError, "Expected credentials");
+ return NULL;
+ }
+
+ session_info = NULL; /* FIXME */
+
+ result = reg_open_samba(NULL, &reg_ctx, NULL,
+ lp_ctx, session_info, credentials);
+ if (!W_ERROR_IS_OK(result)) {
+ PyErr_SetWERROR(result);
+ return NULL;
+ }
+
+ return py_talloc_import(&PyRegistry, reg_ctx);
+}
+
+static PyObject *py_open_directory(PyObject *self, PyObject *args)
+{
+ char *location;
+ WERROR result;
+ struct hive_key *key;
+
+ if (!PyArg_ParseTuple(args, "s", &location))
+ return NULL;
+
+ result = reg_open_directory(NULL, location, &key);
+ PyErr_WERROR_IS_ERR_RAISE(result);
+
+ return py_talloc_import(&PyHiveKey, key);
+}
+
+static PyObject *py_create_directory(PyObject *self, PyObject *args)
+{
+ char *location;
+ WERROR result;
+ struct hive_key *key;
+
+ if (!PyArg_ParseTuple(args, "s", &location))
+ return NULL;
+
+ result = reg_create_directory(NULL, location, &key);
+ PyErr_WERROR_IS_ERR_RAISE(result);
+
+ return py_talloc_import(&PyHiveKey, key);
+}
+
+static PyObject *py_open_ldb_file(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ const char *kwnames[] = { "location", "session_info", "credentials", "lp_ctx", NULL };
+ PyObject *py_session_info = Py_None, *py_credentials = Py_None, *py_lp_ctx = Py_None;
+ WERROR result;
+ char *location;
+ struct loadparm_context *lp_ctx;
+ struct cli_credentials *credentials;
+ struct hive_key *key;
+ struct auth_session_info *session_info;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|OOO",
+ discard_const_p(char *, kwnames),
+ &location,
+ &py_session_info, &py_credentials,
+ &py_lp_ctx))
+ return NULL;
+
+ lp_ctx = lp_from_py_object(py_lp_ctx);
+ if (lp_ctx == NULL) {
+ PyErr_SetString(PyExc_TypeError, "Expected loadparm context");
+ return NULL;
+ }
+
+ credentials = cli_credentials_from_py_object(py_credentials);
+ if (credentials == NULL) {
+ PyErr_SetString(PyExc_TypeError, "Expected credentials");
+ return NULL;
+ }
+
+ session_info = NULL; /* FIXME */
+
+ result = reg_open_ldb_file(NULL, location, session_info, credentials,
+ tevent_context_init(NULL), lp_ctx, &key);
+ PyErr_WERROR_IS_ERR_RAISE(result);
+
+ return py_talloc_import(&PyHiveKey, key);
+}
+
+static PyObject *py_str_regtype(PyObject *self, PyObject *args)
+{
+ int regtype;
+
+ if (!PyArg_ParseTuple(args, "i", &regtype))
+ return NULL;
+
+ return PyString_FromString(str_regtype(regtype));
+}
+
+static PyObject *py_get_predef_name(PyObject *self, PyObject *args)
+{
+ uint32_t hkey;
+ const char *str;
+
+ if (!PyArg_ParseTuple(args, "I", &hkey))
+ return NULL;
+
+ str = reg_get_predef_name(hkey);
+ if (str == NULL)
+ Py_RETURN_NONE;
+ return PyString_FromString(str);
+}
+
+static PyMethodDef py_registry_methods[] = {
+ { "open_samba", (PyCFunction)py_open_samba, METH_VARARGS|METH_KEYWORDS, "open_samba() -> reg" },
+ { "open_directory", py_open_directory, METH_VARARGS, "open_dir(location) -> key" },
+ { "create_directory", py_create_directory, METH_VARARGS, "create_dir(location) -> key" },
+ { "open_ldb", (PyCFunction)py_open_ldb_file, METH_VARARGS|METH_KEYWORDS, "open_ldb(location, session_info=None, credentials=None, loadparm_context=None) -> key" },
+ { "str_regtype", py_str_regtype, METH_VARARGS, "str_regtype(int) -> str" },
+ { "get_predef_name", py_get_predef_name, METH_VARARGS, "get_predef_name(hkey) -> str" },
+ { NULL }
+};
+
+void initregistry(void)
+{
+ PyObject *m;
+
+ if (PyType_Ready(&PyHiveKey) < 0)
+ return;
+
+ if (PyType_Ready(&PyRegistry) < 0)
+ return;
+
+ if (PyType_Ready(&PyRegistryKey) < 0)
+ return;
+
+ m = Py_InitModule3("registry", py_registry_methods, "Registry");
+ if (m == NULL)
+ return;
+
+ PyModule_AddObject(m, "HKEY_CLASSES_ROOT", PyInt_FromLong(HKEY_CLASSES_ROOT));
+ PyModule_AddObject(m, "HKEY_CURRENT_USER", PyInt_FromLong(HKEY_CURRENT_USER));
+ PyModule_AddObject(m, "HKEY_LOCAL_MACHINE", PyInt_FromLong(HKEY_LOCAL_MACHINE));
+ PyModule_AddObject(m, "HKEY_USERS", PyInt_FromLong(HKEY_USERS));
+ PyModule_AddObject(m, "HKEY_PERFORMANCE_DATA", PyInt_FromLong(HKEY_PERFORMANCE_DATA));
+ PyModule_AddObject(m, "HKEY_CURRENT_CONFIG", PyInt_FromLong(HKEY_CURRENT_CONFIG));
+ PyModule_AddObject(m, "HKEY_DYN_DATA", PyInt_FromLong(HKEY_DYN_DATA));
+ PyModule_AddObject(m, "HKEY_PERFORMANCE_TEXT", PyInt_FromLong(HKEY_PERFORMANCE_TEXT));
+ PyModule_AddObject(m, "HKEY_PERFORMANCE_NLSTEXT", PyInt_FromLong(HKEY_PERFORMANCE_NLSTEXT));
+
+ Py_INCREF(&PyRegistry);
+ PyModule_AddObject(m, "Registry", (PyObject *)&PyRegistry);
+
+ Py_INCREF(&PyHiveKey);
+ PyModule_AddObject(m, "HiveKey", (PyObject *)&PyHiveKey);
+
+ Py_INCREF(&PyRegistryKey);
+ PyModule_AddObject(m, "RegistryKey", (PyObject *)&PyRegistryKey);
+}
diff --git a/source4/lib/registry/regf.c b/source4/lib/registry/regf.c
new file mode 100644
index 0000000000..fbb9cd9de9
--- /dev/null
+++ b/source4/lib/registry/regf.c
@@ -0,0 +1,2164 @@
+/*
+ Samba CIFS implementation
+ Registry backend for REGF files
+ Copyright (C) 2005-2007 Jelmer Vernooij, jelmer@samba.org
+ Copyright (C) 2006 Wilco Baan Hofman, wilco@baanhofman.nl
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "system/time.h"
+#include "lib/registry/tdr_regf.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "librpc/gen_ndr/winreg.h"
+#include "lib/registry/registry.h"
+#include "libcli/security/security.h"
+
+
+static struct hive_operations reg_backend_regf;
+
+/**
+ * There are several places on the web where the REGF format is explained;
+ *
+ * TODO: Links
+ */
+
+/* TODO:
+ * - Return error codes that make more sense
+ * - Locking
+ * - do more things in-memory
+ */
+
+/*
+ * Read HBIN blocks into memory
+ */
+
+struct regf_data {
+ int fd;
+ struct hbin_block **hbins;
+ struct regf_hdr *header;
+ struct smb_iconv_convenience *iconv_convenience;
+};
+
+static WERROR regf_save_hbin(struct regf_data *data);
+
+struct regf_key_data {
+ struct hive_key key;
+ struct regf_data *hive;
+ uint32_t offset;
+ struct nk_block *nk;
+};
+
+static struct hbin_block *hbin_by_offset(const struct regf_data *data,
+ uint32_t offset, uint32_t *rel_offset)
+{
+ int i;
+
+ for (i = 0; data->hbins[i]; i++) {
+ if (offset >= data->hbins[i]->offset_from_first &&
+ offset < data->hbins[i]->offset_from_first+
+ data->hbins[i]->offset_to_next) {
+ if (rel_offset != NULL)
+ *rel_offset = offset - data->hbins[i]->offset_from_first - 0x20;
+ return data->hbins[i];
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * Validate a regf header
+ * For now, do nothing, but we should check the checksum
+ */
+static uint32_t regf_hdr_checksum(const uint8_t *buffer)
+{
+ uint32_t checksum = 0, x;
+ int i;
+
+ for (i = 0; i < 0x01FB; i+= 4) {
+ x = IVAL(buffer, i);
+ checksum ^= x;
+ }
+
+ return checksum;
+}
+
+/**
+ * Obtain the contents of a HBIN block
+ */
+static DATA_BLOB hbin_get(const struct regf_data *data, uint32_t offset)
+{
+ DATA_BLOB ret;
+ struct hbin_block *hbin;
+ uint32_t rel_offset;
+
+ ret.data = NULL;
+ ret.length = 0;
+
+ hbin = hbin_by_offset(data, offset, &rel_offset);
+
+ if (hbin == NULL) {
+ DEBUG(1, ("Can't find HBIN containing 0x%04x\n", offset));
+ return ret;
+ }
+
+ ret.length = IVAL(hbin->data, rel_offset);
+ if (!(ret.length & 0x80000000)) {
+ DEBUG(0, ("Trying to use dirty block at 0x%04x\n", offset));
+ return ret;
+ }
+
+ /* remove high bit */
+ ret.length = (ret.length ^ 0xffffffff) + 1;
+
+ ret.length -= 4; /* 4 bytes for the length... */
+ ret.data = hbin->data +
+ (offset - hbin->offset_from_first - 0x20) + 4;
+
+ return ret;
+}
+
+static bool hbin_get_tdr(struct regf_data *regf, uint32_t offset,
+ TALLOC_CTX *ctx, tdr_pull_fn_t pull_fn, void *p)
+{
+ struct tdr_pull *pull = tdr_pull_init(regf, regf->iconv_convenience);
+
+ pull->data = hbin_get(regf, offset);
+ if (!pull->data.data) {
+ DEBUG(1, ("Unable to get data at 0x%04x\n", offset));
+ talloc_free(pull);
+ return false;
+ }
+
+ if (NT_STATUS_IS_ERR(pull_fn(pull, ctx, p))) {
+ DEBUG(1, ("Error parsing record at 0x%04x using tdr\n",
+ offset));
+ talloc_free(pull);
+ return false;
+ }
+ talloc_free(pull);
+
+ return true;
+}
+
+/* Allocate some new data */
+static DATA_BLOB hbin_alloc(struct regf_data *data, uint32_t size,
+ uint32_t *offset)
+{
+ DATA_BLOB ret;
+ uint32_t rel_offset = -1; /* Relative offset ! */
+ struct hbin_block *hbin = NULL;
+ int i;
+
+ *offset = 0;
+
+ if (size == 0)
+ return data_blob(NULL, 0);
+
+ size += 4; /* Need to include int32 for the length */
+
+ /* Allocate as a multiple of 8 */
+ size = (size + 7) & ~7;
+
+ ret.data = NULL;
+ ret.length = 0;
+
+ for (i = 0; (hbin = data->hbins[i]); i++) {
+ int j;
+ int32_t my_size;
+ for (j = 0; j < hbin->offset_to_next-0x20; j+= my_size) {
+ my_size = IVALS(hbin->data, j);
+
+ if (my_size == 0x0) {
+ DEBUG(0, ("Invalid zero-length block! File is corrupt.\n"));
+ return ret;
+ }
+
+ if (my_size % 8 != 0) {
+ DEBUG(0, ("Encountered non-aligned block!\n"));
+ }
+
+ if (my_size < 0) { /* Used... */
+ my_size = -my_size;
+ } else if (my_size == size) { /* exact match */
+ rel_offset = j;
+ DEBUG(4, ("Found free block of exact size %d in middle of HBIN\n",
+ size));
+ break;
+ } else if (my_size > size) { /* data will remain */
+ rel_offset = j;
+ /* Split this block and mark the next block as free */
+ SIVAL(hbin->data, rel_offset+size, my_size-size);
+ DEBUG(4, ("Found free block of size %d (needing %d) in middle of HBIN\n",
+ my_size, size));
+ break;
+ }
+ }
+
+ if (rel_offset != -1)
+ break;
+ }
+
+ /* No space available in previous hbins,
+ * allocate new one */
+ if (data->hbins[i] == NULL) {
+ DEBUG(4, ("No space available in other HBINs for block of size %d, allocating new HBIN\n",
+ size));
+ data->hbins = talloc_realloc(data, data->hbins,
+ struct hbin_block *, i+2);
+ hbin = talloc(data->hbins, struct hbin_block);
+ SMB_ASSERT(hbin != NULL);
+
+ data->hbins[i] = hbin;
+ data->hbins[i+1] = NULL;
+
+ hbin->HBIN_ID = talloc_strdup(hbin, "hbin");
+ hbin->offset_from_first = (i == 0?0:data->hbins[i-1]->offset_from_first+data->hbins[i-1]->offset_to_next);
+ hbin->offset_to_next = 0x1000;
+ hbin->unknown[0] = 0;
+ hbin->unknown[0] = 0;
+ unix_to_nt_time(&hbin->last_change, time(NULL));
+ hbin->block_size = hbin->offset_to_next;
+ hbin->data = talloc_zero_array(hbin, uint8_t, hbin->block_size - 0x20);
+
+ rel_offset = 0x0;
+ SIVAL(hbin->data, size, hbin->block_size - size - 0x20);
+ }
+
+ /* Set size and mark as used */
+ SIVAL(hbin->data, rel_offset, -size);
+
+ ret.data = hbin->data + rel_offset + 0x4; /* Skip past length */
+ ret.length = size - 0x4;
+ if (offset) {
+ uint32_t new_rel_offset;
+ *offset = hbin->offset_from_first + rel_offset + 0x20;
+ SMB_ASSERT(hbin_by_offset(data, *offset, &new_rel_offset) == hbin);
+ SMB_ASSERT(new_rel_offset == rel_offset);
+ }
+
+ return ret;
+}
+
+/* Store a data blob. Return the offset at which it was stored */
+static uint32_t hbin_store (struct regf_data *data, DATA_BLOB blob)
+{
+ uint32_t ret;
+ DATA_BLOB dest = hbin_alloc(data, blob.length, &ret);
+
+ memcpy(dest.data, blob.data, blob.length);
+
+ return ret;
+}
+
+static uint32_t hbin_store_tdr(struct regf_data *data,
+ tdr_push_fn_t push_fn, void *p)
+{
+ struct tdr_push *push = tdr_push_init(data, data->iconv_convenience);
+ uint32_t ret;
+
+ if (NT_STATUS_IS_ERR(push_fn(push, p))) {
+ DEBUG(0, ("Error during push\n"));
+ return -1;
+ }
+
+ ret = hbin_store(data, push->data);
+
+ talloc_free(push);
+
+ return ret;
+}
+
+
+/* Free existing data */
+static void hbin_free (struct regf_data *data, uint32_t offset)
+{
+ int32_t size;
+ uint32_t rel_offset;
+ int32_t next_size;
+ struct hbin_block *hbin;
+
+ SMB_ASSERT (offset > 0);
+
+ hbin = hbin_by_offset(data, offset, &rel_offset);
+
+ if (hbin == NULL)
+ return;
+
+ /* Get original size */
+ size = IVALS(hbin->data, rel_offset);
+
+ if (size > 0) {
+ DEBUG(1, ("Trying to free already freed block at 0x%04x\n",
+ offset));
+ return;
+ }
+ /* Mark as unused */
+ size = -size;
+
+ /* If the next block is free, merge into big free block */
+ if (rel_offset + size < hbin->offset_to_next) {
+ next_size = IVALS(hbin->data, rel_offset+size);
+ if (next_size > 0) {
+ size += next_size;
+ }
+ }
+
+ /* Write block size */
+ SIVALS(hbin->data, rel_offset, size);
+}
+
+/**
+ * Store a data blob data was already stored, but has changed in size
+ * Will try to save it at the current location if possible, otherwise
+ * does a free + store */
+static uint32_t hbin_store_resize(struct regf_data *data,
+ uint32_t orig_offset, DATA_BLOB blob)
+{
+ uint32_t rel_offset;
+ struct hbin_block *hbin = hbin_by_offset(data, orig_offset,
+ &rel_offset);
+ int32_t my_size;
+ int32_t orig_size;
+ int32_t needed_size;
+ int32_t possible_size;
+ int i;
+
+ SMB_ASSERT(orig_offset > 0);
+
+ if (!hbin)
+ return hbin_store(data, blob);
+
+ /* Get original size */
+ orig_size = -IVALS(hbin->data, rel_offset);
+
+ needed_size = blob.length + 4; /* Add int32 containing length */
+ needed_size = (needed_size + 7) & ~7; /* Align */
+
+ /* Fits into current allocated block */
+ if (orig_size >= needed_size) {
+ memcpy(hbin->data + rel_offset + 0x4, blob.data, blob.length);
+ /* If the difference in size is greater than 0x4, split the block
+ * and free/merge it */
+ if (orig_size - needed_size > 0x4) {
+ SIVALS(hbin->data, rel_offset, -needed_size);
+ SIVALS(hbin->data, rel_offset + needed_size,
+ needed_size-orig_size);
+ hbin_free(data, orig_offset + needed_size);
+ }
+ return orig_offset;
+ }
+
+ possible_size = orig_size;
+
+ /* Check if it can be combined with the next few free records */
+ for (i = rel_offset; i < hbin->offset_to_next - 0x20; i += my_size) {
+ if (IVALS(hbin->data, i) < 0) /* Used */
+ break;
+
+ my_size = IVALS(hbin->data, i);
+
+ if (my_size == 0x0) {
+ DEBUG(0, ("Invalid zero-length block! File is corrupt.\n"));
+ break;
+ } else {
+ possible_size += my_size;
+ }
+
+ if (possible_size >= blob.length) {
+ SIVAL(hbin->data, rel_offset, -possible_size);
+ memcpy(hbin->data + rel_offset + 0x4,
+ blob.data, blob.length);
+ return orig_offset;
+ }
+ }
+
+ hbin_free(data, orig_offset);
+ return hbin_store(data, blob);
+}
+
+static uint32_t hbin_store_tdr_resize(struct regf_data *regf,
+ tdr_push_fn_t push_fn,
+ uint32_t orig_offset, void *p)
+{
+ struct tdr_push *push = tdr_push_init(regf, regf->iconv_convenience);
+ uint32_t ret;
+
+ if (NT_STATUS_IS_ERR(push_fn(push, p))) {
+ DEBUG(0, ("Error during push\n"));
+ return -1;
+ }
+
+ ret = hbin_store_resize(regf, orig_offset, push->data);
+
+ talloc_free(push);
+
+ return ret;
+}
+
+static uint32_t regf_create_lh_hash(const char *name)
+{
+ char *hash_name;
+ uint32_t ret = 0;
+ uint16_t i;
+
+ hash_name = strupper_talloc(NULL, name);
+ for (i = 0; *(hash_name + i) != 0; i++) {
+ ret *= 37;
+ ret += *(hash_name + i);
+ }
+ talloc_free(hash_name);
+ return ret;
+}
+
+static WERROR regf_get_info(TALLOC_CTX *mem_ctx,
+ const struct hive_key *key,
+ const char **classname,
+ uint32_t *num_subkeys,
+ uint32_t *num_values,
+ NTTIME *last_mod_time,
+ uint32_t *max_subkeynamelen,
+ uint32_t *max_valnamelen,
+ uint32_t *max_valbufsize)
+{
+ const struct regf_key_data *private_data =
+ (const struct regf_key_data *)key;
+
+ if (num_subkeys != NULL)
+ *num_subkeys = private_data->nk->num_subkeys;
+
+ if (num_values != NULL)
+ *num_values = private_data->nk->num_values;
+
+ if (classname != NULL) {
+ if (private_data->nk->clsname_offset != -1) {
+ DATA_BLOB data = hbin_get(private_data->hive,
+ private_data->nk->clsname_offset);
+ *classname = talloc_strndup(mem_ctx,
+ (char*)data.data,
+ private_data->nk->clsname_length);
+ } else
+ *classname = NULL;
+ }
+
+ /* TODO: Last mod time */
+
+ /* TODO: max valnamelen */
+
+ /* TODO: max valbufsize */
+
+ /* TODO: max subkeynamelen */
+
+ return WERR_OK;
+}
+
+static struct regf_key_data *regf_get_key(TALLOC_CTX *ctx,
+ struct regf_data *regf,
+ uint32_t offset)
+{
+ struct nk_block *nk;
+ struct regf_key_data *ret;
+
+ ret = talloc_zero(ctx, struct regf_key_data);
+ ret->key.ops = &reg_backend_regf;
+ ret->hive = talloc_reference(ret, regf);
+ ret->offset = offset;
+ nk = talloc(ret, struct nk_block);
+ if (nk == NULL)
+ return NULL;
+
+ ret->nk = nk;
+
+ if (!hbin_get_tdr(regf, offset, nk,
+ (tdr_pull_fn_t)tdr_pull_nk_block, nk)) {
+ DEBUG(0, ("Unable to find HBIN data for offset %d\n", offset));
+ return NULL;
+ }
+
+ if (strcmp(nk->header, "nk") != 0) {
+ DEBUG(0, ("Expected nk record, got %s\n", nk->header));
+ talloc_free(ret);
+ return NULL;
+ }
+
+ return ret;
+}
+
+
+static WERROR regf_get_value(TALLOC_CTX *ctx, struct hive_key *key,
+ int idx, const char **name,
+ uint32_t *data_type, DATA_BLOB *data)
+{
+ const struct regf_key_data *private_data =
+ (const struct regf_key_data *)key;
+ struct vk_block *vk;
+ struct regf_data *regf = private_data->hive;
+ uint32_t vk_offset;
+ DATA_BLOB tmp;
+
+ if (idx >= private_data->nk->num_values)
+ return WERR_NO_MORE_ITEMS;
+
+ tmp = hbin_get(regf, private_data->nk->values_offset);
+ if (!tmp.data) {
+ DEBUG(0, ("Unable to find value list\n"));
+ return WERR_GENERAL_FAILURE;
+ }
+
+ if (tmp.length < private_data->nk->num_values * 4) {
+ DEBUG(1, ("Value counts mismatch\n"));
+ }
+
+ vk_offset = IVAL(tmp.data, idx * 4);
+
+ vk = talloc(NULL, struct vk_block);
+ W_ERROR_HAVE_NO_MEMORY(vk);
+
+ if (!hbin_get_tdr(regf, vk_offset, vk,
+ (tdr_pull_fn_t)tdr_pull_vk_block, vk)) {
+ DEBUG(0, ("Unable to get VK block at %d\n", vk_offset));
+ talloc_free(vk);
+ return WERR_GENERAL_FAILURE;
+ }
+
+ /* FIXME: name character set ?*/
+ if (name != NULL)
+ *name = talloc_strndup(ctx, vk->data_name, vk->name_length);
+
+ if (data_type != NULL)
+ *data_type = vk->data_type;
+
+ if (vk->data_length & 0x80000000) {
+ vk->data_length &=~0x80000000;
+ data->data = (uint8_t *)talloc_memdup(ctx, (uint8_t *)&vk->data_offset, vk->data_length);
+ data->length = vk->data_length;
+ } else {
+ *data = hbin_get(regf, vk->data_offset);
+ }
+
+ if (data->length < vk->data_length) {
+ DEBUG(1, ("Read data less than indicated data length!\n"));
+ }
+
+ talloc_free(vk);
+
+ return WERR_OK;
+}
+
+static WERROR regf_get_value_by_name(TALLOC_CTX *mem_ctx,
+ struct hive_key *key, const char *name,
+ uint32_t *type, DATA_BLOB *data)
+{
+ int i;
+ const char *vname;
+ WERROR error;
+
+ /* FIXME: Do binary search? Is this list sorted at all? */
+
+ for (i = 0; W_ERROR_IS_OK(error = regf_get_value(mem_ctx, key, i,
+ &vname, type, data));
+ i++) {
+ if (!strcmp(vname, name))
+ return WERR_OK;
+ }
+
+ if (W_ERROR_EQUAL(error, WERR_NO_MORE_ITEMS))
+ return WERR_BADFILE;
+
+ return error;
+}
+
+
+static WERROR regf_get_subkey_by_index(TALLOC_CTX *ctx,
+ const struct hive_key *key,
+ uint32_t idx, const char **name,
+ const char **classname,
+ NTTIME *last_mod_time)
+{
+ DATA_BLOB data;
+ struct regf_key_data *ret;
+ const struct regf_key_data *private_data = (const struct regf_key_data *)key;
+ struct nk_block *nk = private_data->nk;
+ uint32_t key_off=0;
+
+ if (idx >= nk->num_subkeys)
+ return WERR_NO_MORE_ITEMS;
+
+ data = hbin_get(private_data->hive, nk->subkeys_offset);
+ if (!data.data) {
+ DEBUG(0, ("Unable to find subkey list\n"));
+ return WERR_GENERAL_FAILURE;
+ }
+
+ if (!strncmp((char *)data.data, "li", 2)) {
+ struct li_block li;
+ struct tdr_pull *pull = tdr_pull_init(private_data->hive, private_data->hive->iconv_convenience);
+
+ DEBUG(10, ("Subkeys in LI list\n"));
+ pull->data = data;
+
+ if (NT_STATUS_IS_ERR(tdr_pull_li_block(pull, nk, &li))) {
+ DEBUG(0, ("Error parsing LI list\n"));
+ talloc_free(pull);
+ return WERR_GENERAL_FAILURE;
+ }
+ talloc_free(pull);
+ SMB_ASSERT(!strncmp(li.header, "li", 2));
+
+ if (li.key_count != nk->num_subkeys) {
+ DEBUG(0, ("Subkey counts don't match\n"));
+ return WERR_GENERAL_FAILURE;
+ }
+ key_off = li.nk_offset[idx];
+
+ } else if (!strncmp((char *)data.data, "lf", 2)) {
+ struct lf_block lf;
+ struct tdr_pull *pull = tdr_pull_init(private_data->hive, private_data->hive->iconv_convenience);
+
+ DEBUG(10, ("Subkeys in LF list\n"));
+ pull->data = data;
+
+ if (NT_STATUS_IS_ERR(tdr_pull_lf_block(pull, nk, &lf))) {
+ DEBUG(0, ("Error parsing LF list\n"));
+ talloc_free(pull);
+ return WERR_GENERAL_FAILURE;
+ }
+ talloc_free(pull);
+ SMB_ASSERT(!strncmp(lf.header, "lf", 2));
+
+ if (lf.key_count != nk->num_subkeys) {
+ DEBUG(0, ("Subkey counts don't match\n"));
+ return WERR_GENERAL_FAILURE;
+ }
+
+ key_off = lf.hr[idx].nk_offset;
+ } else if (!strncmp((char *)data.data, "lh", 2)) {
+ struct lh_block lh;
+ struct tdr_pull *pull = tdr_pull_init(private_data->hive, private_data->hive->iconv_convenience);
+
+ DEBUG(10, ("Subkeys in LH list\n"));
+ pull->data = data;
+
+ if (NT_STATUS_IS_ERR(tdr_pull_lh_block(pull, nk, &lh))) {
+ DEBUG(0, ("Error parsing LH list\n"));
+ talloc_free(pull);
+ return WERR_GENERAL_FAILURE;
+ }
+ talloc_free(pull);
+ SMB_ASSERT(!strncmp(lh.header, "lh", 2));
+
+ if (lh.key_count != nk->num_subkeys) {
+ DEBUG(0, ("Subkey counts don't match\n"));
+ return WERR_GENERAL_FAILURE;
+ }
+ key_off = lh.hr[idx].nk_offset;
+ } else if (!strncmp((char *)data.data, "ri", 2)) {
+ struct ri_block ri;
+ struct tdr_pull *pull = tdr_pull_init(ctx, private_data->hive->iconv_convenience);
+ uint16_t i;
+ uint16_t sublist_count = 0;
+
+ DEBUG(10, ("Subkeys in RI list\n"));
+ pull->data = data;
+
+ if (NT_STATUS_IS_ERR(tdr_pull_ri_block(pull, nk, &ri))) {
+ DEBUG(0, ("Error parsing RI list\n"));
+ talloc_free(pull);
+ return WERR_GENERAL_FAILURE;
+ }
+ SMB_ASSERT(!strncmp(ri.header, "ri", 2));
+
+ for (i = 0; i < ri.key_count; i++) {
+ DATA_BLOB list_data;
+
+ /* Get sublist data blob */
+ list_data = hbin_get(private_data->hive, ri.offset[i]);
+ if (!list_data.data) {
+ DEBUG(0, ("Error getting RI list."));
+ talloc_free(pull);
+ return WERR_GENERAL_FAILURE;
+ }
+
+ pull->data = list_data;
+
+ if (!strncmp((char *)list_data.data, "li", 2)) {
+ struct li_block li;
+
+ DEBUG(10, ("Subkeys in RI->LI list\n"));
+
+ if (NT_STATUS_IS_ERR(tdr_pull_li_block(pull,
+ nk,
+ &li))) {
+ DEBUG(0, ("Error parsing LI list from RI\n"));
+ talloc_free(pull);
+ return WERR_GENERAL_FAILURE;
+ }
+ SMB_ASSERT(!strncmp(li.header, "li", 2));
+
+ /* Advance to next sublist if necessary */
+ if (idx >= sublist_count + li.key_count) {
+ sublist_count += li.key_count;
+ continue;
+ }
+ key_off = li.nk_offset[idx - sublist_count];
+ sublist_count += li.key_count;
+ break;
+ } else if (!strncmp((char *)list_data.data, "lh", 2)) {
+ struct lh_block lh;
+
+ DEBUG(10, ("Subkeys in RI->LH list\n"));
+
+ if (NT_STATUS_IS_ERR(tdr_pull_lh_block(pull,
+ nk,
+ &lh))) {
+ DEBUG(0, ("Error parsing LH list from RI\n"));
+ talloc_free(pull);
+ return WERR_GENERAL_FAILURE;
+ }
+ SMB_ASSERT(!strncmp(lh.header, "lh", 2));
+
+ /* Advance to next sublist if necessary */
+ if (idx >= sublist_count + lh.key_count) {
+ sublist_count += lh.key_count;
+ continue;
+ }
+ key_off = lh.hr[idx - sublist_count].nk_offset;
+ sublist_count += lh.key_count;
+ break;
+ } else {
+ DEBUG(0,("Unknown sublist in ri block\n"));
+ talloc_free(pull);
+
+ return WERR_GENERAL_FAILURE;
+ }
+
+ }
+ talloc_free(pull);
+
+
+ if (idx > sublist_count) {
+ return WERR_NO_MORE_ITEMS;
+ }
+
+ } else {
+ DEBUG(0, ("Unknown type for subkey list (0x%04x): %c%c\n",
+ nk->subkeys_offset, data.data[0], data.data[1]));
+ return WERR_GENERAL_FAILURE;
+ }
+
+ ret = regf_get_key (ctx, private_data->hive, key_off);
+
+ if (classname != NULL) {
+ if (ret->nk->clsname_offset != -1) {
+ DATA_BLOB db = hbin_get(ret->hive,
+ ret->nk->clsname_offset);
+ *classname = talloc_strndup(ctx,
+ (char*)db.data,
+ ret->nk->clsname_length);
+ } else
+ *classname = NULL;
+ }
+
+ if (last_mod_time != NULL)
+ *last_mod_time = ret->nk->last_change;
+
+ if (name != NULL)
+ *name = talloc_steal(ctx, ret->nk->key_name);
+
+ talloc_free(ret);
+
+ return WERR_OK;
+}
+
+static WERROR regf_match_subkey_by_name(TALLOC_CTX *ctx,
+ const struct hive_key *key,
+ uint32_t offset,
+ const char *name, uint32_t *ret)
+{
+ DATA_BLOB subkey_data;
+ struct nk_block subkey;
+ struct tdr_pull *pull;
+ const struct regf_key_data *private_data =
+ (const struct regf_key_data *)key;
+
+ subkey_data = hbin_get(private_data->hive, offset);
+ if (!subkey_data.data) {
+ DEBUG(0, ("Unable to retrieve subkey HBIN\n"));
+ return WERR_GENERAL_FAILURE;
+ }
+
+ pull = tdr_pull_init(ctx, private_data->hive->iconv_convenience);
+
+ pull->data = subkey_data;
+
+ if (NT_STATUS_IS_ERR(tdr_pull_nk_block(pull, ctx, &subkey))) {
+ DEBUG(0, ("Error parsing NK structure.\n"));
+ talloc_free(pull);
+ return WERR_GENERAL_FAILURE;
+ }
+ talloc_free(pull);
+
+ if (strncmp(subkey.header, "nk", 2)) {
+ DEBUG(0, ("Not an NK structure.\n"));
+ return WERR_GENERAL_FAILURE;
+ }
+
+ if (!strcasecmp(subkey.key_name, name)) {
+ *ret = offset;
+ } else {
+ *ret = 0;
+ }
+ return WERR_OK;
+}
+
+static WERROR regf_get_subkey_by_name(TALLOC_CTX *ctx,
+ const struct hive_key *key,
+ const char *name,
+ struct hive_key **ret)
+{
+ DATA_BLOB data;
+ const struct regf_key_data *private_data =
+ (const struct regf_key_data *)key;
+ struct nk_block *nk = private_data->nk;
+ uint32_t key_off = 0;
+
+ data = hbin_get(private_data->hive, nk->subkeys_offset);
+ if (!data.data) {
+ DEBUG(0, ("Unable to find subkey list\n"));
+ return WERR_GENERAL_FAILURE;
+ }
+
+ if (!strncmp((char *)data.data, "li", 2)) {
+ struct li_block li;
+ struct tdr_pull *pull = tdr_pull_init(ctx, private_data->hive->iconv_convenience);
+ uint16_t i;
+
+ DEBUG(10, ("Subkeys in LI list\n"));
+ pull->data = data;
+
+ if (NT_STATUS_IS_ERR(tdr_pull_li_block(pull, nk, &li))) {
+ DEBUG(0, ("Error parsing LI list\n"));
+ talloc_free(pull);
+ return WERR_GENERAL_FAILURE;
+ }
+ talloc_free(pull);
+ SMB_ASSERT(!strncmp(li.header, "li", 2));
+
+ if (li.key_count != nk->num_subkeys) {
+ DEBUG(0, ("Subkey counts don't match\n"));
+ return WERR_GENERAL_FAILURE;
+ }
+
+ for (i = 0; i < li.key_count; i++) {
+ W_ERROR_NOT_OK_RETURN(regf_match_subkey_by_name(nk, key,
+ li.nk_offset[i],
+ name,
+ &key_off));
+ if (key_off != 0)
+ break;
+ }
+ if (key_off == 0)
+ return WERR_BADFILE;
+ } else if (!strncmp((char *)data.data, "lf", 2)) {
+ struct lf_block lf;
+ struct tdr_pull *pull = tdr_pull_init(ctx, private_data->hive->iconv_convenience);
+ uint16_t i;
+
+ DEBUG(10, ("Subkeys in LF list\n"));
+ pull->data = data;
+
+ if (NT_STATUS_IS_ERR(tdr_pull_lf_block(pull, nk, &lf))) {
+ DEBUG(0, ("Error parsing LF list\n"));
+ talloc_free(pull);
+ return WERR_GENERAL_FAILURE;
+ }
+ talloc_free(pull);
+ SMB_ASSERT(!strncmp(lf.header, "lf", 2));
+
+ if (lf.key_count != nk->num_subkeys) {
+ DEBUG(0, ("Subkey counts don't match\n"));
+ return WERR_GENERAL_FAILURE;
+ }
+
+ for (i = 0; i < lf.key_count; i++) {
+ if (strncmp(lf.hr[i].hash, name, 4)) {
+ continue;
+ }
+ W_ERROR_NOT_OK_RETURN(regf_match_subkey_by_name(nk,
+ key,
+ lf.hr[i].nk_offset,
+ name,
+ &key_off));
+ if (key_off != 0)
+ break;
+ }
+ if (key_off == 0)
+ return WERR_BADFILE;
+ } else if (!strncmp((char *)data.data, "lh", 2)) {
+ struct lh_block lh;
+ struct tdr_pull *pull = tdr_pull_init(ctx, private_data->hive->iconv_convenience);
+ uint16_t i;
+ uint32_t hash;
+
+ DEBUG(10, ("Subkeys in LH list\n"));
+ pull->data = data;
+
+ if (NT_STATUS_IS_ERR(tdr_pull_lh_block(pull, nk, &lh))) {
+ DEBUG(0, ("Error parsing LH list\n"));
+ talloc_free(pull);
+ return WERR_GENERAL_FAILURE;
+ }
+ talloc_free(pull);
+ SMB_ASSERT(!strncmp(lh.header, "lh", 2));
+
+ if (lh.key_count != nk->num_subkeys) {
+ DEBUG(0, ("Subkey counts don't match\n"));
+ return WERR_GENERAL_FAILURE;
+ }
+
+ hash = regf_create_lh_hash(name);
+ for (i = 0; i < lh.key_count; i++) {
+ if (lh.hr[i].base37 != hash) {
+ continue;
+ }
+ W_ERROR_NOT_OK_RETURN(regf_match_subkey_by_name(nk,
+ key,
+ lh.hr[i].nk_offset,
+ name,
+ &key_off));
+ if (key_off != 0)
+ break;
+ }
+ if (key_off == 0)
+ return WERR_BADFILE;
+ } else if (!strncmp((char *)data.data, "ri", 2)) {
+ struct ri_block ri;
+ struct tdr_pull *pull = tdr_pull_init(ctx, private_data->hive->iconv_convenience);
+ uint16_t i, j;
+
+ DEBUG(10, ("Subkeys in RI list\n"));
+ pull->data = data;
+
+ if (NT_STATUS_IS_ERR(tdr_pull_ri_block(pull, nk, &ri))) {
+ DEBUG(0, ("Error parsing RI list\n"));
+ talloc_free(pull);
+ return WERR_GENERAL_FAILURE;
+ }
+ SMB_ASSERT(!strncmp(ri.header, "ri", 2));
+
+ for (i = 0; i < ri.key_count; i++) {
+ DATA_BLOB list_data;
+
+ /* Get sublist data blob */
+ list_data = hbin_get(private_data->hive, ri.offset[i]);
+ if (list_data.data == NULL) {
+ DEBUG(0, ("Error getting RI list."));
+ talloc_free(pull);
+ return WERR_GENERAL_FAILURE;
+ }
+
+ pull->data = list_data;
+
+ if (!strncmp((char *)list_data.data, "li", 2)) {
+ struct li_block li;
+
+ if (NT_STATUS_IS_ERR(tdr_pull_li_block(pull,
+ nk,
+ &li))) {
+ DEBUG(0, ("Error parsing LI list from RI\n"));
+ talloc_free(pull);
+ return WERR_GENERAL_FAILURE;
+ }
+ SMB_ASSERT(!strncmp(li.header, "li", 2));
+
+ for (j = 0; j < li.key_count; j++) {
+ W_ERROR_NOT_OK_RETURN(regf_match_subkey_by_name(nk, key,
+ li.nk_offset[j],
+ name,
+ &key_off));
+ if (key_off)
+ break;
+ }
+ } else if (!strncmp((char *)list_data.data, "lh", 2)) {
+ struct lh_block lh;
+ uint32_t hash;
+
+ if (NT_STATUS_IS_ERR(tdr_pull_lh_block(pull,
+ nk,
+ &lh))) {
+ DEBUG(0, ("Error parsing LH list from RI\n"));
+ talloc_free(pull);
+ return WERR_GENERAL_FAILURE;
+ }
+ SMB_ASSERT(!strncmp(lh.header, "lh", 2));
+
+ hash = regf_create_lh_hash(name);
+ for (j = 0; j < lh.key_count; j++) {
+ if (lh.hr[j].base37 != hash) {
+ continue;
+ }
+ W_ERROR_NOT_OK_RETURN(regf_match_subkey_by_name(nk, key,
+ lh.hr[j].nk_offset,
+ name,
+ &key_off));
+ if (key_off)
+ break;
+ }
+ }
+ if (key_off)
+ break;
+ }
+ talloc_free(pull);
+ if (!key_off)
+ return WERR_BADFILE;
+ } else {
+ DEBUG(0, ("Unknown subkey list type.\n"));
+ return WERR_GENERAL_FAILURE;
+ }
+
+ *ret = (struct hive_key *)regf_get_key(ctx, private_data->hive,
+ key_off);
+ return WERR_OK;
+}
+
+static WERROR regf_set_sec_desc(struct hive_key *key,
+ const struct security_descriptor *sec_desc)
+{
+ const struct regf_key_data *private_data =
+ (const struct regf_key_data *)key;
+ struct sk_block cur_sk, sk, new_sk;
+ struct regf_data *regf = private_data->hive;
+ struct nk_block root;
+ DATA_BLOB data;
+ uint32_t sk_offset, cur_sk_offset;
+ bool update_cur_sk = false;
+
+ /* Get the root nk */
+ hbin_get_tdr(regf, regf->header->data_offset, regf,
+ (tdr_pull_fn_t) tdr_pull_nk_block, &root);
+
+ /* Push the security descriptor to a blob */
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_push_struct_blob(&data, regf, NULL,
+ sec_desc, (ndr_push_flags_fn_t)ndr_push_security_descriptor))) {
+ DEBUG(0, ("Unable to push security descriptor\n"));
+ return WERR_GENERAL_FAILURE;
+ }
+
+ /* Get the current security descriptor for the key */
+ if (!hbin_get_tdr(regf, private_data->nk->sk_offset, regf,
+ (tdr_pull_fn_t) tdr_pull_sk_block, &cur_sk)) {
+ DEBUG(0, ("Unable to find security descriptor for current key\n"));
+ return WERR_BADFILE;
+ }
+ /* If there's no change, change nothing. */
+ if (memcmp(data.data, cur_sk.sec_desc,
+ MIN(data.length, cur_sk.rec_size)) == 0) {
+ return WERR_OK;
+ }
+
+ /* Delete the current sk if only this key is using it */
+ if (cur_sk.ref_cnt == 1) {
+ /* Get the previous security descriptor for the key */
+ if (!hbin_get_tdr(regf, cur_sk.prev_offset, regf,
+ (tdr_pull_fn_t) tdr_pull_sk_block, &sk)) {
+ DEBUG(0, ("Unable to find prev security descriptor for current key\n"));
+ return WERR_BADFILE;
+ }
+ /* Change and store the previous security descriptor */
+ sk.next_offset = cur_sk.next_offset;
+ hbin_store_tdr_resize(regf, (tdr_push_fn_t) tdr_push_sk_block,
+ cur_sk.prev_offset, &sk);
+
+ /* Get the next security descriptor for the key */
+ if (!hbin_get_tdr(regf, cur_sk.next_offset, regf,
+ (tdr_pull_fn_t) tdr_pull_sk_block, &sk)) {
+ DEBUG(0, ("Unable to find next security descriptor for current key\n"));
+ return WERR_BADFILE;
+ }
+ /* Change and store the next security descriptor */
+ sk.prev_offset = cur_sk.prev_offset;
+ hbin_store_tdr_resize(regf, (tdr_push_fn_t) tdr_push_sk_block,
+ cur_sk.next_offset, &sk);
+
+ hbin_free(regf, private_data->nk->sk_offset);
+ } else {
+ /* This key will no longer be referring to this sk */
+ cur_sk.ref_cnt--;
+ update_cur_sk = true;
+ }
+
+ sk_offset = root.sk_offset;
+
+ do {
+ cur_sk_offset = sk_offset;
+ if (!hbin_get_tdr(regf, sk_offset, regf,
+ (tdr_pull_fn_t) tdr_pull_sk_block, &sk)) {
+ DEBUG(0, ("Unable to find security descriptor\n"));
+ return WERR_BADFILE;
+ }
+ if (memcmp(data.data, sk.sec_desc, MIN(data.length, sk.rec_size)) == 0) {
+ private_data->nk->sk_offset = sk_offset;
+ sk.ref_cnt++;
+ hbin_store_tdr_resize(regf,
+ (tdr_push_fn_t) tdr_push_sk_block,
+ sk_offset, &sk);
+ hbin_store_tdr_resize(regf,
+ (tdr_push_fn_t) tdr_push_nk_block,
+ private_data->offset,
+ private_data->nk);
+ return WERR_OK;
+ }
+ sk_offset = sk.next_offset;
+ } while (sk_offset != root.sk_offset);
+
+ ZERO_STRUCT(new_sk);
+ new_sk.header = "sk";
+ new_sk.prev_offset = cur_sk_offset;
+ new_sk.next_offset = root.sk_offset;
+ new_sk.ref_cnt = 1;
+ new_sk.rec_size = data.length;
+ new_sk.sec_desc = data.data;
+
+ sk_offset = hbin_store_tdr(regf,
+ (tdr_push_fn_t) tdr_push_sk_block,
+ &new_sk);
+ if (sk_offset == -1) {
+ DEBUG(0, ("Error storing sk block\n"));
+ return WERR_GENERAL_FAILURE;
+ }
+ private_data->nk->sk_offset = sk_offset;
+
+ if (update_cur_sk) {
+ hbin_store_tdr_resize(regf,
+ (tdr_push_fn_t) tdr_push_sk_block,
+ private_data->nk->sk_offset, &cur_sk);
+ }
+
+ /* Get the previous security descriptor for the key */
+ if (!hbin_get_tdr(regf, new_sk.prev_offset, regf,
+ (tdr_pull_fn_t) tdr_pull_sk_block, &sk)) {
+ DEBUG(0, ("Unable to find security descriptor for previous key\n"));
+ return WERR_BADFILE;
+ }
+ /* Change and store the previous security descriptor */
+ sk.next_offset = sk_offset;
+ hbin_store_tdr_resize(regf,
+ (tdr_push_fn_t) tdr_push_sk_block,
+ cur_sk.prev_offset, &sk);
+
+ /* Get the next security descriptor for the key (always root, as we append) */
+ if (!hbin_get_tdr(regf, new_sk.next_offset, regf,
+ (tdr_pull_fn_t) tdr_pull_sk_block, &sk)) {
+ DEBUG(0, ("Unable to find security descriptor for current key\n"));
+ return WERR_BADFILE;
+ }
+ /* Change and store the next security descriptor (always root, as we append) */
+ sk.prev_offset = sk_offset;
+ hbin_store_tdr_resize(regf,
+ (tdr_push_fn_t) tdr_push_sk_block,
+ root.sk_offset, &sk);
+
+
+ /* Store the nk. */
+ hbin_store_tdr_resize(regf,
+ (tdr_push_fn_t) tdr_push_sk_block,
+ private_data->offset, private_data->nk);
+ return WERR_OK;
+}
+
+static WERROR regf_get_sec_desc(TALLOC_CTX *ctx, const struct hive_key *key,
+ struct security_descriptor **sd)
+{
+ const struct regf_key_data *private_data =
+ (const struct regf_key_data *)key;
+ struct sk_block sk;
+ struct regf_data *regf = private_data->hive;
+ DATA_BLOB data;
+
+ if (!hbin_get_tdr(regf, private_data->nk->sk_offset, ctx,
+ (tdr_pull_fn_t) tdr_pull_sk_block, &sk)) {
+ DEBUG(0, ("Unable to find security descriptor\n"));
+ return WERR_GENERAL_FAILURE;
+ }
+
+ if (strcmp(sk.header, "sk") != 0) {
+ DEBUG(0, ("Expected 'sk', got '%s'\n", sk.header));
+ return WERR_GENERAL_FAILURE;
+ }
+
+ *sd = talloc(ctx, struct security_descriptor);
+ W_ERROR_HAVE_NO_MEMORY(*sd);
+
+ data.data = sk.sec_desc;
+ data.length = sk.rec_size;
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_pull_struct_blob(&data, ctx, NULL, *sd,
+ (ndr_pull_flags_fn_t)ndr_pull_security_descriptor))) {
+ DEBUG(0, ("Error parsing security descriptor\n"));
+ return WERR_GENERAL_FAILURE;
+ }
+
+ return WERR_OK;
+}
+
+static WERROR regf_sl_add_entry(struct regf_data *regf, uint32_t list_offset,
+ const char *name,
+ uint32_t key_offset, uint32_t *ret)
+{
+ DATA_BLOB data;
+
+ /* Create a new key if necessary */
+ if (list_offset == -1) {
+ if (regf->header->version.major != 1) {
+ DEBUG(0, ("Can't store keys in unknown registry format\n"));
+ return WERR_NOT_SUPPORTED;
+ }
+ if (regf->header->version.minor < 3) {
+ /* Store LI */
+ struct li_block li;
+ ZERO_STRUCT(li);
+ li.header = "li";
+ li.key_count = 1;
+
+ li.nk_offset = talloc_array(regf, uint32_t, 1);
+ W_ERROR_HAVE_NO_MEMORY(li.nk_offset);
+ li.nk_offset[0] = key_offset;
+
+ *ret = hbin_store_tdr(regf,
+ (tdr_push_fn_t) tdr_push_li_block,
+ &li);
+
+ talloc_free(li.nk_offset);
+ } else if (regf->header->version.minor == 3 ||
+ regf->header->version.minor == 4) {
+ /* Store LF */
+ struct lf_block lf;
+ ZERO_STRUCT(lf);
+ lf.header = "lf";
+ lf.key_count = 1;
+
+ lf.hr = talloc_array(regf, struct hash_record, 1);
+ W_ERROR_HAVE_NO_MEMORY(lf.hr);
+ lf.hr[0].nk_offset = key_offset;
+ lf.hr[0].hash = talloc_strndup(lf.hr, name, 4);
+ W_ERROR_HAVE_NO_MEMORY(lf.hr[0].hash);
+
+ *ret = hbin_store_tdr(regf,
+ (tdr_push_fn_t) tdr_push_lf_block,
+ &lf);
+
+ talloc_free(lf.hr);
+ } else if (regf->header->version.minor == 5) {
+ /* Store LH */
+ struct lh_block lh;
+ ZERO_STRUCT(lh);
+ lh.header = "lh";
+ lh.key_count = 1;
+
+ lh.hr = talloc_array(regf, struct lh_hash, 1);
+ W_ERROR_HAVE_NO_MEMORY(lh.hr);
+ lh.hr[0].nk_offset = key_offset;
+ lh.hr[0].base37 = regf_create_lh_hash(name);
+
+ *ret = hbin_store_tdr(regf,
+ (tdr_push_fn_t) tdr_push_lh_block,
+ &lh);
+
+ talloc_free(lh.hr);
+ }
+ return WERR_OK;
+ }
+
+ data = hbin_get(regf, list_offset);
+ if (!data.data) {
+ DEBUG(0, ("Unable to find subkey list\n"));
+ return WERR_BADFILE;
+ }
+
+ if (!strncmp((char *)data.data, "li", 2)) {
+ struct tdr_pull *pull = tdr_pull_init(regf, regf->iconv_convenience);
+ struct li_block li;
+
+ pull->data = data;
+
+ if (NT_STATUS_IS_ERR(tdr_pull_li_block(pull, regf, &li))) {
+ DEBUG(0, ("Error parsing LI list\n"));
+ talloc_free(pull);
+ return WERR_BADFILE;
+ }
+ talloc_free(pull);
+
+ if (strncmp(li.header, "li", 2) != 0) {
+ abort();
+ DEBUG(0, ("LI header corrupt\n"));
+ return WERR_BADFILE;
+ }
+
+ li.nk_offset = talloc_realloc(regf, li.nk_offset,
+ uint32_t, li.key_count+1);
+ W_ERROR_HAVE_NO_MEMORY(li.nk_offset);
+ li.nk_offset[li.key_count] = key_offset;
+ li.key_count++;
+ *ret = hbin_store_tdr_resize(regf,
+ (tdr_push_fn_t)tdr_push_li_block,
+ list_offset, &li);
+
+ talloc_free(li.nk_offset);
+ } else if (!strncmp((char *)data.data, "lf", 2)) {
+ struct tdr_pull *pull = tdr_pull_init(regf, regf->iconv_convenience);
+ struct lf_block lf;
+
+ pull->data = data;
+
+ if (NT_STATUS_IS_ERR(tdr_pull_lf_block(pull, regf, &lf))) {
+ DEBUG(0, ("Error parsing LF list\n"));
+ talloc_free(pull);
+ return WERR_BADFILE;
+ }
+ talloc_free(pull);
+ SMB_ASSERT(!strncmp(lf.header, "lf", 2));
+
+ lf.hr = talloc_realloc(regf, lf.hr, struct hash_record,
+ lf.key_count+1);
+ W_ERROR_HAVE_NO_MEMORY(lf.hr);
+ lf.hr[lf.key_count].nk_offset = key_offset;
+ lf.hr[lf.key_count].hash = talloc_strndup(lf.hr, name, 4);
+ W_ERROR_HAVE_NO_MEMORY(lf.hr[lf.key_count].hash);
+ lf.key_count++;
+ *ret = hbin_store_tdr_resize(regf,
+ (tdr_push_fn_t)tdr_push_lf_block,
+ list_offset, &lf);
+
+ talloc_free(lf.hr);
+ } else if (!strncmp((char *)data.data, "lh", 2)) {
+ struct tdr_pull *pull = tdr_pull_init(regf, regf->iconv_convenience);
+ struct lh_block lh;
+
+ pull->data = data;
+
+ if (NT_STATUS_IS_ERR(tdr_pull_lh_block(pull, regf, &lh))) {
+ DEBUG(0, ("Error parsing LH list\n"));
+ talloc_free(pull);
+ return WERR_BADFILE;
+ }
+ talloc_free(pull);
+ SMB_ASSERT(!strncmp(lh.header, "lh", 2));
+
+ lh.hr = talloc_realloc(regf, lh.hr, struct lh_hash,
+ lh.key_count+1);
+ W_ERROR_HAVE_NO_MEMORY(lh.hr);
+ lh.hr[lh.key_count].nk_offset = key_offset;
+ lh.hr[lh.key_count].base37 = regf_create_lh_hash(name);
+ lh.key_count++;
+ *ret = hbin_store_tdr_resize(regf,
+ (tdr_push_fn_t)tdr_push_lh_block,
+ list_offset, &lh);
+
+ talloc_free(lh.hr);
+ } else if (!strncmp((char *)data.data, "ri", 2)) {
+ /* FIXME */
+ DEBUG(0, ("Adding to 'ri' subkey list is not supported yet.\n"));
+ return WERR_NOT_SUPPORTED;
+ } else {
+ DEBUG(0, ("Cannot add to unknown subkey list\n"));
+ return WERR_BADFILE;
+ }
+
+ return WERR_OK;
+}
+
+static WERROR regf_sl_del_entry(struct regf_data *regf, uint32_t list_offset,
+ uint32_t key_offset, uint32_t *ret)
+{
+ DATA_BLOB data;
+
+ data = hbin_get(regf, list_offset);
+ if (!data.data) {
+ DEBUG(0, ("Unable to find subkey list\n"));
+ return WERR_BADFILE;
+ }
+
+ if (strncmp((char *)data.data, "li", 2) == 0) {
+ struct li_block li;
+ struct tdr_pull *pull = tdr_pull_init(regf, regf->iconv_convenience);
+ uint16_t i;
+ bool found_offset = false;
+
+ DEBUG(10, ("Subkeys in LI list\n"));
+
+ pull->data = data;
+
+ if (NT_STATUS_IS_ERR(tdr_pull_li_block(pull, regf, &li))) {
+ DEBUG(0, ("Error parsing LI list\n"));
+ talloc_free(pull);
+ return WERR_BADFILE;
+ }
+ talloc_free(pull);
+
+ SMB_ASSERT(!strncmp(li.header, "li", 2));
+
+ for (i = 0; i < li.key_count; i++) {
+ if (found_offset) {
+ li.nk_offset[i-1] = li.nk_offset[i];
+ }
+ if (li.nk_offset[i] == key_offset) {
+ found_offset = true;
+ continue;
+ }
+ }
+ if (!found_offset) {
+ DEBUG(2, ("Subkey not found\n"));
+ return WERR_BADFILE;
+ }
+ li.key_count--;
+
+ /* If the there are no entries left, free the subkey list */
+ if (li.key_count == 0) {
+ hbin_free(regf, list_offset);
+ *ret = -1;
+ }
+
+ /* Store li block */
+ *ret = hbin_store_tdr_resize(regf,
+ (tdr_push_fn_t) tdr_push_li_block,
+ list_offset, &li);
+ } else if (strncmp((char *)data.data, "lf", 2) == 0) {
+ struct lf_block lf;
+ struct tdr_pull *pull = tdr_pull_init(regf, regf->iconv_convenience);
+ uint16_t i;
+ bool found_offset = false;
+
+ DEBUG(10, ("Subkeys in LF list\n"));
+
+ pull->data = data;
+
+ if (NT_STATUS_IS_ERR(tdr_pull_lf_block(pull, regf, &lf))) {
+ DEBUG(0, ("Error parsing LF list\n"));
+ talloc_free(pull);
+ return WERR_BADFILE;
+ }
+ talloc_free(pull);
+
+ SMB_ASSERT(!strncmp(lf.header, "lf", 2));
+
+ for (i = 0; i < lf.key_count; i++) {
+ if (found_offset) {
+ lf.hr[i-1] = lf.hr[i];
+ continue;
+ }
+ if (lf.hr[i].nk_offset == key_offset) {
+ found_offset = 1;
+ continue;
+ }
+ }
+ if (!found_offset) {
+ DEBUG(2, ("Subkey not found\n"));
+ return WERR_BADFILE;
+ }
+ lf.key_count--;
+
+ /* If the there are no entries left, free the subkey list */
+ if (lf.key_count == 0) {
+ hbin_free(regf, list_offset);
+ *ret = -1;
+ return WERR_OK;
+ }
+
+ /* Store lf block */
+ *ret = hbin_store_tdr_resize(regf,
+ (tdr_push_fn_t) tdr_push_lf_block,
+ list_offset, &lf);
+ } else if (strncmp((char *)data.data, "lh", 2) == 0) {
+ struct lh_block lh;
+ struct tdr_pull *pull = tdr_pull_init(regf, regf->iconv_convenience);
+ uint16_t i;
+ bool found_offset = false;
+
+ DEBUG(10, ("Subkeys in LH list\n"));
+
+ pull->data = data;
+
+ if (NT_STATUS_IS_ERR(tdr_pull_lh_block(pull, regf, &lh))) {
+ DEBUG(0, ("Error parsing LF list\n"));
+ talloc_free(pull);
+ return WERR_BADFILE;
+ }
+ talloc_free(pull);
+
+ SMB_ASSERT(!strncmp(lh.header, "lh", 2));
+
+ for (i = 0; i < lh.key_count; i++) {
+ if (found_offset) {
+ lh.hr[i-1] = lh.hr[i];
+ continue;
+ }
+ if (lh.hr[i].nk_offset == key_offset) {
+ found_offset = 1;
+ continue;
+ }
+ }
+ if (!found_offset) {
+ DEBUG(0, ("Subkey not found\n"));
+ return WERR_BADFILE;
+ }
+ lh.key_count--;
+
+ /* If the there are no entries left, free the subkey list */
+ if (lh.key_count == 0) {
+ hbin_free(regf, list_offset);
+ *ret = -1;
+ return WERR_OK;
+ }
+
+ /* Store lh block */
+ *ret = hbin_store_tdr_resize(regf,
+ (tdr_push_fn_t) tdr_push_lh_block,
+ list_offset, &lh);
+ } else if (strncmp((char *)data.data, "ri", 2) == 0) {
+ /* FIXME */
+ DEBUG(0, ("Sorry, deletion from ri block is not supported yet.\n"));
+ return WERR_NOT_SUPPORTED;
+ } else {
+ DEBUG (0, ("Unknown header found in subkey list.\n"));
+ return WERR_BADFILE;
+ }
+ return WERR_OK;
+}
+
+static WERROR regf_del_value (struct hive_key *key, const char *name)
+{
+ struct regf_key_data *private_data = (struct regf_key_data *)key;
+ struct regf_data *regf = private_data->hive;
+ struct nk_block *nk = private_data->nk;
+ struct vk_block vk;
+ uint32_t vk_offset;
+ bool found_offset = false;
+ DATA_BLOB values;
+ uint32_t i;
+
+ if (nk->values_offset == -1) {
+ return WERR_BADFILE;
+ }
+
+ values = hbin_get(regf, nk->values_offset);
+
+ for (i = 0; i < nk->num_values; i++) {
+ if (found_offset) {
+ ((uint32_t *)values.data)[i-1] = ((uint32_t *) values.data)[i];
+ } else {
+ vk_offset = IVAL(values.data, i * 4);
+ if (!hbin_get_tdr(regf, vk_offset, private_data,
+ (tdr_pull_fn_t)tdr_pull_vk_block,
+ &vk)) {
+ DEBUG(0, ("Unable to get VK block at %d\n",
+ vk_offset));
+ return WERR_BADFILE;
+ }
+ if (strcmp(vk.data_name, name) == 0) {
+ hbin_free(regf, vk_offset);
+ found_offset = true;
+ }
+ }
+ }
+ if (!found_offset) {
+ return WERR_BADFILE;
+ } else {
+ nk->num_values--;
+ values.length = (nk->num_values)*4;
+ }
+
+ /* Store values list and nk */
+ if (nk->num_values == 0) {
+ hbin_free(regf, nk->values_offset);
+ nk->values_offset = -1;
+ } else {
+ nk->values_offset = hbin_store_resize(regf,
+ nk->values_offset,
+ values);
+ }
+ hbin_store_tdr_resize(regf, (tdr_push_fn_t) tdr_push_nk_block,
+ private_data->offset, nk);
+
+ return regf_save_hbin(private_data->hive);
+}
+
+
+static WERROR regf_del_key(const struct hive_key *parent, const char *name)
+{
+ const struct regf_key_data *private_data =
+ (const struct regf_key_data *)parent;
+ struct regf_key_data *key;
+ struct nk_block *parent_nk;
+ WERROR error;
+
+ SMB_ASSERT(private_data);
+
+ parent_nk = private_data->nk;
+
+ if (parent_nk->subkeys_offset == -1) {
+ DEBUG(4, ("Subkey list is empty, this key cannot contain subkeys.\n"));
+ return WERR_BADFILE;
+ }
+
+ /* Find the key */
+ if (!W_ERROR_IS_OK(regf_get_subkey_by_name(parent_nk, parent, name,
+ (struct hive_key **)&key))) {
+ DEBUG(2, ("Key '%s' not found\n", name));
+ return WERR_BADFILE;
+ }
+
+ if (key->nk->subkeys_offset != -1) {
+ char *sk_name;
+ struct hive_key *sk = (struct hive_key *)key;
+ int i = key->nk->num_subkeys;
+ while (i--) {
+ /* Get subkey information. */
+ error = regf_get_subkey_by_index(parent_nk, sk, 0,
+ (const char **)&sk_name,
+ NULL, NULL);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Can't retrieve subkey by index.\n"));
+ return error;
+ }
+
+ /* Delete subkey. */
+ error = regf_del_key(sk, sk_name);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Can't delete key '%s'.\n", sk_name));
+ return error;
+ }
+
+ talloc_free(sk_name);
+ }
+ }
+
+ if (key->nk->values_offset != -1) {
+ char *val_name;
+ struct hive_key *sk = (struct hive_key *)key;
+ DATA_BLOB data;
+ int i = key->nk->num_values;
+ while (i--) {
+ /* Get value information. */
+ error = regf_get_value(parent_nk, sk, 0,
+ (const char **)&val_name,
+ NULL, &data);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Can't retrieve value by index.\n"));
+ return error;
+ }
+
+ /* Delete value. */
+ error = regf_del_value(sk, val_name);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Can't delete value '%s'.\n", val_name));
+ return error;
+ }
+
+ talloc_free(val_name);
+ }
+ }
+
+ /* Delete it from the subkey list. */
+ error = regf_sl_del_entry(private_data->hive, parent_nk->subkeys_offset,
+ key->offset, &parent_nk->subkeys_offset);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Can't store new subkey list for parent key. Won't delete.\n"));
+ return error;
+ }
+
+ /* Re-store parent key */
+ parent_nk->num_subkeys--;
+ hbin_store_tdr_resize(private_data->hive,
+ (tdr_push_fn_t) tdr_push_nk_block,
+ private_data->offset, parent_nk);
+
+ if (key->nk->clsname_offset != -1) {
+ hbin_free(private_data->hive, key->nk->clsname_offset);
+ }
+ hbin_free(private_data->hive, key->offset);
+
+ return regf_save_hbin(private_data->hive);
+}
+
+static WERROR regf_add_key(TALLOC_CTX *ctx, const struct hive_key *parent,
+ const char *name, const char *classname,
+ struct security_descriptor *sec_desc,
+ struct hive_key **ret)
+{
+ const struct regf_key_data *private_data =
+ (const struct regf_key_data *)parent;
+ struct nk_block *parent_nk = private_data->nk, nk;
+ struct nk_block *root;
+ struct regf_data *regf = private_data->hive;
+ uint32_t offset;
+ WERROR error;
+
+ nk.header = "nk";
+ nk.type = REG_SUB_KEY;
+ unix_to_nt_time(&nk.last_change, time(NULL));
+ nk.uk1 = 0;
+ nk.parent_offset = private_data->offset;
+ nk.num_subkeys = 0;
+ nk.uk2 = 0;
+ nk.subkeys_offset = -1;
+ nk.unknown_offset = -1;
+ nk.num_values = 0;
+ nk.values_offset = -1;
+ memset(nk.unk3, 0, 5);
+ nk.clsname_offset = -1; /* FIXME: fill in */
+ nk.clsname_length = 0;
+ nk.key_name = name;
+
+ /* Get the security descriptor of the root key */
+ root = talloc_zero(ctx, struct nk_block);
+ W_ERROR_HAVE_NO_MEMORY(root);
+
+ if (!hbin_get_tdr(regf, regf->header->data_offset, root,
+ (tdr_pull_fn_t)tdr_pull_nk_block, root)) {
+ DEBUG(0, ("Unable to find HBIN data for offset %d\n", offset));
+ return WERR_GENERAL_FAILURE;
+ }
+ nk.sk_offset = root->sk_offset;
+ talloc_free(root);
+
+ /* Store the new nk key */
+ offset = hbin_store_tdr(regf, (tdr_push_fn_t) tdr_push_nk_block, &nk);
+
+ error = regf_sl_add_entry(regf, parent_nk->subkeys_offset, name, offset,
+ &parent_nk->subkeys_offset);
+ if (!W_ERROR_IS_OK(error)) {
+ hbin_free(regf, offset);
+ return error;
+ }
+
+ parent_nk->num_subkeys++;
+
+ /* Since the subkey offset of the parent can change, store it again */
+ hbin_store_tdr_resize(regf, (tdr_push_fn_t) tdr_push_nk_block,
+ nk.parent_offset, parent_nk);
+
+ *ret = (struct hive_key *)regf_get_key(ctx, regf, offset);
+
+ return regf_save_hbin(private_data->hive);
+}
+
+static WERROR regf_set_value(struct hive_key *key, const char *name,
+ uint32_t type, const DATA_BLOB data)
+{
+ struct regf_key_data *private_data = (struct regf_key_data *)key;
+ struct regf_data *regf = private_data->hive;
+ struct nk_block *nk = private_data->nk;
+ struct vk_block vk;
+ uint32_t i;
+ uint32_t tmp_vk_offset, vk_offset, old_vk_offset = -1;
+ DATA_BLOB values;
+
+ ZERO_STRUCT(vk);
+
+ /* find the value offset, if it exists */
+ if (nk->values_offset != -1) {
+ values = hbin_get(regf, nk->values_offset);
+
+ for (i = 0; i < nk->num_values; i++) {
+ tmp_vk_offset = IVAL(values.data, i * 4);
+ if (!hbin_get_tdr(regf, tmp_vk_offset, private_data,
+ (tdr_pull_fn_t)tdr_pull_vk_block,
+ &vk)) {
+ DEBUG(0, ("Unable to get VK block at %d\n",
+ tmp_vk_offset));
+ return WERR_GENERAL_FAILURE;
+ }
+ if (strcmp(vk.data_name, name) == 0) {
+ old_vk_offset = tmp_vk_offset;
+ break;
+ }
+ }
+ /* Free data, if any */
+ if (!(vk.data_length & 0x80000000)) {
+ hbin_free(regf, vk.data_offset);
+ }
+ }
+ if (old_vk_offset == -1) {
+ vk.header = "vk";
+ vk.name_length = strlen(name);
+ if (name != NULL && name[0] != 0) {
+ vk.flag = 1;
+ vk.data_name = name;
+ } else {
+ vk.data_name = NULL;
+ vk.flag = 0;
+ }
+ }
+ /* Set the type and data */
+ vk.data_length = data.length;
+ vk.data_type = type;
+ if (type == REG_DWORD) {
+ vk.data_length |= 0x80000000;
+ vk.data_offset = *(uint32_t *)data.data;
+ } else {
+ /* Store data somewhere */
+ vk.data_offset = hbin_store(regf, data);
+ }
+ if (old_vk_offset == -1) {
+ /* Store new vk */
+ vk_offset = hbin_store_tdr(regf,
+ (tdr_push_fn_t) tdr_push_vk_block,
+ &vk);
+ } else {
+ /* Store vk at offset */
+ vk_offset = hbin_store_tdr_resize(regf,
+ (tdr_push_fn_t) tdr_push_vk_block,
+ old_vk_offset ,&vk);
+ }
+
+ /* Re-allocate the value list */
+ if (nk->values_offset == -1) {
+ nk->values_offset = hbin_store_tdr(regf,
+ (tdr_push_fn_t) tdr_push_uint32,
+ &vk_offset);
+ nk->num_values = 1;
+ } else {
+
+ /* Change if we're changing, otherwise we're adding the value */
+ if (old_vk_offset != -1) {
+ /* Find and overwrite the offset. */
+ for (i = 0; i < nk->num_values; i++) {
+ if (IVAL(values.data, i * 4) == old_vk_offset) {
+ SIVAL(values.data, i * 4, vk_offset);
+ break;
+ }
+ }
+ } else {
+ /* Create a new value list */
+ DATA_BLOB value_list;
+
+ value_list.length = (nk->num_values+1)*4;
+ value_list.data = (uint8_t *)talloc_array(private_data,
+ uint32_t,
+ nk->num_values+1);
+ W_ERROR_HAVE_NO_MEMORY(value_list.data);
+ memcpy(value_list.data, values.data, nk->num_values * 4);
+
+ SIVAL(value_list.data, nk->num_values * 4, vk_offset);
+ nk->num_values++;
+ nk->values_offset = hbin_store_resize(regf,
+ nk->values_offset,
+ value_list);
+ }
+
+ }
+ hbin_store_tdr_resize(regf,
+ (tdr_push_fn_t) tdr_push_nk_block,
+ private_data->offset, nk);
+ return regf_save_hbin(private_data->hive);
+}
+
+static WERROR regf_save_hbin(struct regf_data *regf)
+{
+ struct tdr_push *push = tdr_push_init(regf, regf->iconv_convenience);
+ int i;
+
+ W_ERROR_HAVE_NO_MEMORY(push);
+
+ if (lseek(regf->fd, 0, SEEK_SET) == -1) {
+ DEBUG(0, ("Error lseeking in regf file\n"));
+ return WERR_GENERAL_FAILURE;
+ }
+
+ /* Recompute checksum */
+ if (NT_STATUS_IS_ERR(tdr_push_regf_hdr(push, regf->header))) {
+ DEBUG(0, ("Failed to push regf header\n"));
+ return WERR_GENERAL_FAILURE;
+ }
+ regf->header->chksum = regf_hdr_checksum(push->data.data);
+ talloc_free(push);
+
+ if (NT_STATUS_IS_ERR(tdr_push_to_fd(regf->fd, regf->iconv_convenience,
+ (tdr_push_fn_t)tdr_push_regf_hdr,
+ regf->header))) {
+ DEBUG(0, ("Error writing registry file header\n"));
+ return WERR_GENERAL_FAILURE;
+ }
+
+ if (lseek(regf->fd, 0x1000, SEEK_SET) == -1) {
+ DEBUG(0, ("Error lseeking to 0x1000 in regf file\n"));
+ return WERR_GENERAL_FAILURE;
+ }
+
+ for (i = 0; regf->hbins[i]; i++) {
+ if (NT_STATUS_IS_ERR(tdr_push_to_fd(regf->fd, regf->iconv_convenience,
+ (tdr_push_fn_t)tdr_push_hbin_block,
+ regf->hbins[i]))) {
+ DEBUG(0, ("Error writing HBIN block\n"));
+ return WERR_GENERAL_FAILURE;
+ }
+ }
+
+ return WERR_OK;
+}
+
+WERROR reg_create_regf_file(TALLOC_CTX *parent_ctx,
+ struct smb_iconv_convenience *iconv_convenience,
+ const char *location,
+ int minor_version, struct hive_key **key)
+{
+ struct regf_data *regf;
+ struct regf_hdr *regf_hdr;
+ struct nk_block nk;
+ struct sk_block sk;
+ WERROR error;
+ DATA_BLOB data;
+ struct security_descriptor *sd;
+ uint32_t sk_offset;
+
+ regf = (struct regf_data *)talloc_zero(NULL, struct regf_data);
+
+ regf->iconv_convenience = iconv_convenience;
+
+ W_ERROR_HAVE_NO_MEMORY(regf);
+
+ DEBUG(5, ("Attempting to create registry file\n"));
+
+ /* Get the header */
+ regf->fd = creat(location, 0644);
+
+ if (regf->fd == -1) {
+ DEBUG(0,("Could not create file: %s, %s\n", location,
+ strerror(errno)));
+ talloc_free(regf);
+ return WERR_GENERAL_FAILURE;
+ }
+
+ regf_hdr = talloc_zero(regf, struct regf_hdr);
+ W_ERROR_HAVE_NO_MEMORY(regf_hdr);
+ regf_hdr->REGF_ID = "regf";
+ unix_to_nt_time(&regf_hdr->modtime, time(NULL));
+ regf_hdr->version.major = 1;
+ regf_hdr->version.minor = minor_version;
+ regf_hdr->last_block = 0x1000; /* Block size */
+ regf_hdr->description = talloc_strdup(regf_hdr,
+ "Registry created by Samba 4");
+ W_ERROR_HAVE_NO_MEMORY(regf_hdr->description);
+ regf_hdr->chksum = 0;
+
+ regf->header = regf_hdr;
+
+ /* Create all hbin blocks */
+ regf->hbins = talloc_array(regf, struct hbin_block *, 1);
+ W_ERROR_HAVE_NO_MEMORY(regf->hbins);
+ regf->hbins[0] = NULL;
+
+ nk.header = "nk";
+ nk.type = REG_SUB_KEY;
+ unix_to_nt_time(&nk.last_change, time(NULL));
+ nk.uk1 = 0;
+ nk.parent_offset = -1;
+ nk.num_subkeys = 0;
+ nk.uk2 = 0;
+ nk.subkeys_offset = -1;
+ nk.unknown_offset = -1;
+ nk.num_values = 0;
+ nk.values_offset = -1;
+ memset(nk.unk3, 0, 5);
+ nk.clsname_offset = -1;
+ nk.clsname_length = 0;
+ nk.sk_offset = 0x80;
+ nk.key_name = "SambaRootKey";
+
+ /*
+ * It should be noted that changing the key_name to something shorter
+ * creates a shorter nk block, which makes the position of the sk block
+ * change. All Windows registries I've seen have the sk at 0x80.
+ * I therefore recommend that our regf files share that offset -- Wilco
+ */
+
+ /* Create a security descriptor. */
+ sd = security_descriptor_dacl_create(regf,
+ 0,
+ NULL, NULL,
+ SID_NT_AUTHENTICATED_USERS,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ SEC_GENERIC_ALL,
+ SEC_ACE_FLAG_OBJECT_INHERIT,
+ NULL);
+
+ /* Push the security descriptor to a blob */
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_push_struct_blob(&data, regf, NULL,
+ sd, (ndr_push_flags_fn_t)ndr_push_security_descriptor))) {
+ DEBUG(0, ("Unable to push security descriptor\n"));
+ return WERR_GENERAL_FAILURE;
+ }
+
+ ZERO_STRUCT(sk);
+ sk.header = "sk";
+ sk.prev_offset = 0x80;
+ sk.next_offset = 0x80;
+ sk.ref_cnt = 1;
+ sk.rec_size = data.length;
+ sk.sec_desc = data.data;
+
+ /* Store the new nk key */
+ regf->header->data_offset = hbin_store_tdr(regf,
+ (tdr_push_fn_t)tdr_push_nk_block,
+ &nk);
+ /* Store the sk block */
+ sk_offset = hbin_store_tdr(regf,
+ (tdr_push_fn_t) tdr_push_sk_block,
+ &sk);
+ if (sk_offset != 0x80) {
+ DEBUG(0, ("Error storing sk block, should be at 0x80, stored at 0x%x\n", nk.sk_offset));
+ return WERR_GENERAL_FAILURE;
+ }
+
+
+ *key = (struct hive_key *)regf_get_key(parent_ctx, regf,
+ regf->header->data_offset);
+
+ error = regf_save_hbin(regf);
+ if (!W_ERROR_IS_OK(error)) {
+ return error;
+ }
+
+ /* We can drop our own reference now that *key will have created one */
+ talloc_free(regf);
+
+ return WERR_OK;
+}
+
+WERROR reg_open_regf_file(TALLOC_CTX *parent_ctx, const char *location,
+ struct smb_iconv_convenience *iconv_convenience, struct hive_key **key)
+{
+ struct regf_data *regf;
+ struct regf_hdr *regf_hdr;
+ struct tdr_pull *pull;
+ int i;
+
+ regf = (struct regf_data *)talloc_zero(parent_ctx, struct regf_data);
+
+ regf->iconv_convenience = iconv_convenience;
+
+ W_ERROR_HAVE_NO_MEMORY(regf);
+
+ DEBUG(5, ("Attempting to load registry file\n"));
+
+ /* Get the header */
+ regf->fd = open(location, O_RDWR);
+
+ if (regf->fd == -1) {
+ DEBUG(0,("Could not load file: %s, %s\n", location,
+ strerror(errno)));
+ talloc_free(regf);
+ return WERR_GENERAL_FAILURE;
+ }
+
+ pull = tdr_pull_init(regf, regf->iconv_convenience);
+
+ pull->data.data = (uint8_t*)fd_load(regf->fd, &pull->data.length, 0, regf);
+
+ if (pull->data.data == NULL) {
+ DEBUG(0, ("Error reading data\n"));
+ talloc_free(regf);
+ return WERR_GENERAL_FAILURE;
+ }
+
+ regf_hdr = talloc(regf, struct regf_hdr);
+ W_ERROR_HAVE_NO_MEMORY(regf_hdr);
+
+ if (NT_STATUS_IS_ERR(tdr_pull_regf_hdr(pull, regf_hdr, regf_hdr))) {
+ talloc_free(regf);
+ return WERR_GENERAL_FAILURE;
+ }
+
+ regf->header = regf_hdr;
+
+ if (strcmp(regf_hdr->REGF_ID, "regf") != 0) {
+ DEBUG(0, ("Unrecognized NT registry header id: %s, %s\n",
+ regf_hdr->REGF_ID, location));
+ talloc_free(regf);
+ return WERR_GENERAL_FAILURE;
+ }
+
+ /* Validate the header ... */
+ if (regf_hdr_checksum(pull->data.data) != regf_hdr->chksum) {
+ DEBUG(0, ("Registry file checksum error: %s: %d,%d\n",
+ location, regf_hdr->chksum,
+ regf_hdr_checksum(pull->data.data)));
+ talloc_free(regf);
+ return WERR_GENERAL_FAILURE;
+ }
+
+ pull->offset = 0x1000;
+
+ i = 0;
+ /* Read in all hbin blocks */
+ regf->hbins = talloc_array(regf, struct hbin_block *, 1);
+ W_ERROR_HAVE_NO_MEMORY(regf->hbins);
+
+ regf->hbins[0] = NULL;
+
+ while (pull->offset < pull->data.length &&
+ pull->offset <= regf->header->last_block) {
+ struct hbin_block *hbin = talloc(regf->hbins,
+ struct hbin_block);
+
+ W_ERROR_HAVE_NO_MEMORY(hbin);
+
+ if (NT_STATUS_IS_ERR(tdr_pull_hbin_block(pull, hbin, hbin))) {
+ DEBUG(0, ("[%d] Error parsing HBIN block\n", i));
+ talloc_free(regf);
+ return WERR_FOOBAR;
+ }
+
+ if (strcmp(hbin->HBIN_ID, "hbin") != 0) {
+ DEBUG(0, ("[%d] Expected 'hbin', got '%s'\n",
+ i, hbin->HBIN_ID));
+ talloc_free(regf);
+ return WERR_FOOBAR;
+ }
+
+ regf->hbins[i] = hbin;
+ i++;
+ regf->hbins = talloc_realloc(regf, regf->hbins,
+ struct hbin_block *, i+2);
+ regf->hbins[i] = NULL;
+ }
+
+ talloc_free(pull);
+
+ DEBUG(1, ("%d HBIN blocks read\n", i));
+
+ *key = (struct hive_key *)regf_get_key(parent_ctx, regf,
+ regf->header->data_offset);
+
+ /* We can drop our own reference now that *key will have created one */
+ talloc_free(regf);
+
+ return WERR_OK;
+}
+
+static struct hive_operations reg_backend_regf = {
+ .name = "regf",
+ .get_key_info = regf_get_info,
+ .enum_key = regf_get_subkey_by_index,
+ .get_key_by_name = regf_get_subkey_by_name,
+ .get_value_by_name = regf_get_value_by_name,
+ .enum_value = regf_get_value,
+ .get_sec_desc = regf_get_sec_desc,
+ .set_sec_desc = regf_set_sec_desc,
+ .add_key = regf_add_key,
+ .set_value = regf_set_value,
+ .del_key = regf_del_key,
+ .delete_value = regf_del_value,
+};
diff --git a/source4/lib/registry/regf.idl b/source4/lib/registry/regf.idl
new file mode 100644
index 0000000000..ffd7072b7a
--- /dev/null
+++ b/source4/lib/registry/regf.idl
@@ -0,0 +1,167 @@
+/*
+ Definitions for the REGF registry file format as used by
+ Windows NT4 and above.
+
+ Copyright (C) 2005 Jelmer Vernooij, jelmer@samba.org
+ Copyright (C) 2006 Wilco Baan Hofman, wilco@baanhofman.nl
+
+ Based on two files from Samba 3:
+ regedit.c by Richard Sharpe
+ regfio.c by Jerry Carter
+
+*/
+
+interface regf
+{
+ const int REGF_OFFSET_NONE = 0xffffffff;
+
+ /*
+ * Registry version number
+ * 1.2.0.1 for WinNT 3.51
+ * 1.3.0.1 for WinNT 4
+ * 1.5.0.1 for WinXP
+ */
+
+ [noprint] struct regf_version {
+ [value(1)] uint32 major;
+ [value(3)] uint32 minor;
+ [value(0)] uint32 release;
+ [value(1)] uint32 build;
+ };
+
+ /*
+ "regf" is obviously the abbreviation for "Registry file". "regf" is the
+ signature of the header-block which is always 4kb in size, although only
+ the first 64 bytes seem to be used and a checksum is calculated over
+ the first 0x200 bytes only!
+ */
+
+ [public,noprint] struct regf_hdr {
+ [charset(DOS)] uint8 REGF_ID[4]; /* 'regf' */
+ uint32 update_counter1;
+ uint32 update_counter2;
+ NTTIME modtime;
+ regf_version version;
+ uint32 data_offset;
+ uint32 last_block;
+ [value(1)] uint32 uk7; /* 1 */
+ [charset(UTF16)] uint16 description[0x20];
+ uint32 padding[99]; /* Padding */
+ /* Checksum of first 0x200 bytes XOR-ed */
+ uint32 chksum;
+ };
+
+ /*
+ hbin probably means hive-bin (i.e. hive-container)
+ This block is always a multiple
+ of 4kb in size.
+ */
+ [public,noprint] struct hbin_block {
+ [charset(DOS)] uint8 HBIN_ID[4]; /* hbin */
+ uint32 offset_from_first; /* Offset from 1st hbin-Block */
+ uint32 offset_to_next; /* Offset to the next hbin-Block */
+ uint32 unknown[2];
+ NTTIME last_change;
+ uint32 block_size; /* Block size (including the header!) */
+ uint8 data[offset_to_next-0x20];
+ /* data is filled with:
+ uint32 length;
+ Negative if in use, positive otherwise
+ Always a multiple of 8
+ uint8_t data[length];
+ Free space marker if 0xffffffff
+ */
+ };
+
+ [noprint] enum reg_key_type {
+ REG_ROOT_KEY = 0x20,
+ REG_SUB_KEY = 0x2C,
+ REG_SYM_LINK = 0x10
+ };
+
+ /*
+ The nk-record can be treated as a combination of tree-record and
+ key-record of the win 95 registry.
+ */
+ [public,noprint] struct nk_block {
+ [charset(DOS)] uint8 header[2];
+ reg_key_type type;
+ NTTIME last_change;
+ uint32 uk1;
+ uint32 parent_offset;
+ uint32 num_subkeys;
+ uint32 uk2;
+ uint32 subkeys_offset;
+ uint32 unknown_offset;
+ uint32 num_values;
+ uint32 values_offset; /* Points to a list of offsets of vk-records */
+ uint32 sk_offset;
+ uint32 clsname_offset;
+ uint32 unk3[5];
+ [value(strlen(key_name))] uint16 name_length;
+ uint16 clsname_length;
+ [charset(DOS)] uint8 key_name[name_length];
+ };
+
+ /* sk (? Security Key ?) is the ACL of the registry. */
+ [noprint,public] struct sk_block {
+ [charset(DOS)] uint8 header[2];
+ uint16 tag;
+ uint32 prev_offset;
+ uint32 next_offset;
+ uint32 ref_cnt;
+ uint32 rec_size;
+ uint8 sec_desc[rec_size];
+ };
+
+ [noprint] struct lh_hash {
+ uint32 nk_offset;
+ uint32 base37; /* base37 of key name */
+ };
+
+ /* Subkey listing with hash of first 4 characters */
+ [public,noprint] struct lh_block {
+ [charset(DOS)] uint8 header[2];
+ uint16 key_count;
+ lh_hash hr[key_count];
+ };
+
+ [public,noprint] struct li_block {
+ [charset(DOS)] uint8 header[2];
+ uint16 key_count;
+ uint32 nk_offset[key_count];
+ };
+
+ [public,noprint] struct ri_block {
+ [charset(DOS)] uint8 header[2];
+ uint16 key_count;
+ uint32 offset[key_count]; /* li/lh offset */
+ };
+
+ /* The vk-record consists information to a single value (value key). */
+ [public,noprint] struct vk_block {
+ [charset(DOS)] uint8 header[2];
+ [value(strlen(data_name))] uint16 name_length;
+ uint32 data_length; /* If top-bit set, offset contains the data */
+ uint32 data_offset;
+ uint32 data_type;
+ uint16 flag; /* =1, has name, else no name (=Default). */
+ uint16 unk1;
+ [charset(DOS)] uint8 data_name[name_length];
+ };
+
+ [noprint] struct hash_record {
+ uint32 nk_offset;
+ [charset(DOS)] uint8 hash[4];
+ };
+
+ /*
+ The lf-record is the counterpart to the RGKN-record (the
+ hash-function)
+ */
+ [public,noprint] struct lf_block {
+ [charset(DOS)] uint8 header[2];
+ uint16 key_count;
+ hash_record hr[key_count]; /* Array of hash records, depending on key_count */
+ };
+}
diff --git a/source4/lib/registry/registry.h b/source4/lib/registry/registry.h
new file mode 100644
index 0000000000..a97d9f6184
--- /dev/null
+++ b/source4/lib/registry/registry.h
@@ -0,0 +1,526 @@
+/*
+ Unix SMB/CIFS implementation.
+ Registry interface
+ Copyright (C) Gerald Carter 2002.
+ Copyright (C) Jelmer Vernooij 2003-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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _REGISTRY_H /* _REGISTRY_H */
+#define _REGISTRY_H
+
+struct registry_context;
+struct loadparm_context;
+struct smb_iconv_convenience;
+
+#include <talloc.h>
+#include "libcli/util/werror.h"
+#include "librpc/gen_ndr/security.h"
+#include "libcli/util/ntstatus.h"
+#include "../lib/util/time.h"
+#include "../lib/util/data_blob.h"
+
+/**
+ * The hive API. This API is generally used for
+ * reading a specific file that contains just one hive.
+ *
+ * Good examples are .DAT (NTUSER.DAT) files.
+ *
+ * This API does not have any notification support (that
+ * should be provided by the registry implementation), nor
+ * does it understand what predefined keys are.
+ */
+
+struct hive_key {
+ const struct hive_operations *ops;
+};
+
+struct hive_operations {
+ const char *name;
+
+ /**
+ * Open a specific subkey
+ */
+ WERROR (*enum_key) (TALLOC_CTX *mem_ctx,
+ const struct hive_key *key, uint32_t idx,
+ const char **name,
+ const char **classname,
+ NTTIME *last_mod_time);
+
+ /**
+ * Open a subkey by name
+ */
+ WERROR (*get_key_by_name) (TALLOC_CTX *mem_ctx,
+ const struct hive_key *key, const char *name,
+ struct hive_key **subkey);
+
+ /**
+ * Add a new key.
+ */
+ WERROR (*add_key) (TALLOC_CTX *ctx,
+ const struct hive_key *parent_key, const char *name,
+ const char *classname,
+ struct security_descriptor *desc,
+ struct hive_key **key);
+ /**
+ * Remove an existing key.
+ */
+ WERROR (*del_key) (const struct hive_key *key, const char *name);
+
+ /**
+ * Force write of a key to disk.
+ */
+ WERROR (*flush_key) (struct hive_key *key);
+
+ /**
+ * Retrieve a registry value with a specific index.
+ */
+ WERROR (*enum_value) (TALLOC_CTX *mem_ctx,
+ struct hive_key *key, int idx,
+ const char **name, uint32_t *type,
+ DATA_BLOB *data);
+
+ /**
+ * Retrieve a registry value with the specified name
+ */
+ WERROR (*get_value_by_name) (TALLOC_CTX *mem_ctx,
+ struct hive_key *key, const char *name,
+ uint32_t *type, DATA_BLOB *data);
+
+ /**
+ * Set a value on the specified registry key.
+ */
+ WERROR (*set_value) (struct hive_key *key, const char *name,
+ uint32_t type, const DATA_BLOB data);
+
+ /**
+ * Remove a value.
+ */
+ WERROR (*delete_value) (struct hive_key *key, const char *name);
+
+ /* Security Descriptors */
+
+ /**
+ * Change the security descriptor on a registry key.
+ *
+ * This should return WERR_NOT_SUPPORTED if the underlying
+ * format does not have a mechanism for storing
+ * security descriptors.
+ */
+ WERROR (*set_sec_desc) (struct hive_key *key,
+ const struct security_descriptor *desc);
+
+ /**
+ * Retrieve the security descriptor on a registry key.
+ *
+ * This should return WERR_NOT_SUPPORTED if the underlying
+ * format does not have a mechanism for storing
+ * security descriptors.
+ */
+ WERROR (*get_sec_desc) (TALLOC_CTX *ctx,
+ const struct hive_key *key,
+ struct security_descriptor **desc);
+
+ /**
+ * Retrieve general information about a key.
+ */
+ WERROR (*get_key_info) (TALLOC_CTX *mem_ctx,
+ const struct hive_key *key,
+ const char **classname,
+ uint32_t *num_subkeys,
+ uint32_t *num_values,
+ NTTIME *last_change_time,
+ uint32_t *max_subkeynamelen,
+ uint32_t *max_valnamelen,
+ uint32_t *max_valbufsize);
+};
+
+struct cli_credentials;
+struct auth_session_info;
+struct tevent_context;
+
+WERROR reg_open_hive(TALLOC_CTX *parent_ctx, const char *location,
+ struct auth_session_info *session_info,
+ struct cli_credentials *credentials,
+ struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx,
+ struct hive_key **root);
+WERROR hive_key_get_info(TALLOC_CTX *mem_ctx, const struct hive_key *key,
+ const char **classname, uint32_t *num_subkeys,
+ uint32_t *num_values, NTTIME *last_change_time,
+ uint32_t *max_subkeynamelen,
+ uint32_t *max_valnamelen, uint32_t *max_valbufsize);
+WERROR hive_key_add_name(TALLOC_CTX *ctx, const struct hive_key *parent_key,
+ const char *name, const char *classname,
+ struct security_descriptor *desc,
+ struct hive_key **key);
+WERROR hive_key_del(const struct hive_key *key, const char *name);
+WERROR hive_get_key_by_name(TALLOC_CTX *mem_ctx,
+ const struct hive_key *key, const char *name,
+ struct hive_key **subkey);
+WERROR hive_enum_key(TALLOC_CTX *mem_ctx,
+ const struct hive_key *key, uint32_t idx,
+ const char **name,
+ const char **classname,
+ NTTIME *last_mod_time);
+
+WERROR hive_key_set_value(struct hive_key *key, const char *name,
+ uint32_t type, const DATA_BLOB data);
+
+WERROR hive_get_value(TALLOC_CTX *mem_ctx,
+ struct hive_key *key, const char *name,
+ uint32_t *type, DATA_BLOB *data);
+WERROR hive_get_value_by_index(TALLOC_CTX *mem_ctx,
+ struct hive_key *key, uint32_t idx,
+ const char **name,
+ uint32_t *type, DATA_BLOB *data);
+WERROR hive_get_sec_desc(TALLOC_CTX *mem_ctx,
+ struct hive_key *key,
+ struct security_descriptor **security);
+
+WERROR hive_set_sec_desc(struct hive_key *key,
+ const struct security_descriptor *security);
+
+WERROR hive_key_del_value(struct hive_key *key, const char *name);
+
+WERROR hive_key_flush(struct hive_key *key);
+
+
+/* Individual backends */
+WERROR reg_open_directory(TALLOC_CTX *parent_ctx,
+ const char *location, struct hive_key **key);
+WERROR reg_open_regf_file(TALLOC_CTX *parent_ctx,
+ const char *location, struct smb_iconv_convenience *iconv_convenience,
+ struct hive_key **key);
+WERROR reg_open_ldb_file(TALLOC_CTX *parent_ctx, const char *location,
+ struct auth_session_info *session_info,
+ struct cli_credentials *credentials,
+ struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx,
+ struct hive_key **k);
+
+
+WERROR reg_create_directory(TALLOC_CTX *parent_ctx,
+ const char *location, struct hive_key **key);
+WERROR reg_create_regf_file(TALLOC_CTX *parent_ctx,
+ struct smb_iconv_convenience *iconv_convenience,
+ const char *location,
+ int major_version,
+ struct hive_key **key);
+
+
+
+/* Handles for the predefined keys */
+#define HKEY_CLASSES_ROOT 0x80000000
+#define HKEY_CURRENT_USER 0x80000001
+#define HKEY_LOCAL_MACHINE 0x80000002
+#define HKEY_USERS 0x80000003
+#define HKEY_PERFORMANCE_DATA 0x80000004
+#define HKEY_CURRENT_CONFIG 0x80000005
+#define HKEY_DYN_DATA 0x80000006
+#define HKEY_PERFORMANCE_TEXT 0x80000050
+#define HKEY_PERFORMANCE_NLSTEXT 0x80000060
+
+#define HKEY_FIRST HKEY_CLASSES_ROOT
+#define HKEY_LAST HKEY_PERFORMANCE_NLSTEXT
+
+struct reg_predefined_key {
+ uint32_t handle;
+ const char *name;
+};
+
+extern const struct reg_predefined_key reg_predefined_keys[];
+
+#define REG_DELETE -1
+
+/*
+ * The general idea here is that every backend provides a 'hive'. Combining
+ * various hives gives you a complete registry like windows has
+ */
+
+#define REGISTRY_INTERFACE_VERSION 1
+
+struct reg_key_operations;
+
+/* structure to store the registry handles */
+struct registry_key
+{
+ struct registry_context *context;
+};
+
+struct registry_value
+{
+ const char *name;
+ unsigned int data_type;
+ DATA_BLOB data;
+};
+
+/* FIXME */
+typedef void (*reg_key_notification_function) (void);
+typedef void (*reg_value_notification_function) (void);
+
+struct cli_credentials;
+
+struct registry_operations {
+ const char *name;
+
+ WERROR (*get_key_info) (TALLOC_CTX *mem_ctx,
+ const struct registry_key *key,
+ const char **classname,
+ uint32_t *numsubkeys,
+ uint32_t *numvalues,
+ NTTIME *last_change_time,
+ uint32_t *max_subkeynamelen,
+ uint32_t *max_valnamelen,
+ uint32_t *max_valbufsize);
+
+ WERROR (*flush_key) (struct registry_key *key);
+
+ WERROR (*get_predefined_key) (struct registry_context *ctx,
+ uint32_t key_id,
+ struct registry_key **key);
+
+ WERROR (*open_key) (TALLOC_CTX *mem_ctx,
+ struct registry_key *parent,
+ const char *path,
+ struct registry_key **key);
+
+ WERROR (*create_key) (TALLOC_CTX *mem_ctx,
+ struct registry_key *parent,
+ const char *name,
+ const char *key_class,
+ struct security_descriptor *security,
+ struct registry_key **key);
+
+ WERROR (*delete_key) (struct registry_key *key, const char *name);
+
+ WERROR (*delete_value) (struct registry_key *key, const char *name);
+
+ WERROR (*enum_key) (TALLOC_CTX *mem_ctx,
+ const struct registry_key *key, uint32_t idx,
+ const char **name,
+ const char **keyclass,
+ NTTIME *last_changed_time);
+
+ WERROR (*enum_value) (TALLOC_CTX *mem_ctx,
+ const struct registry_key *key, uint32_t idx,
+ const char **name,
+ uint32_t *type,
+ DATA_BLOB *data);
+
+ WERROR (*get_sec_desc) (TALLOC_CTX *mem_ctx,
+ const struct registry_key *key,
+ struct security_descriptor **security);
+
+ WERROR (*set_sec_desc) (struct registry_key *key,
+ const struct security_descriptor *security);
+
+ WERROR (*load_key) (struct registry_key *key,
+ const char *key_name,
+ const char *path);
+
+ WERROR (*unload_key) (struct registry_key *key, const char *name);
+
+ WERROR (*notify_value_change) (struct registry_key *key,
+ reg_value_notification_function fn);
+
+ WERROR (*get_value) (TALLOC_CTX *mem_ctx,
+ const struct registry_key *key,
+ const char *name,
+ uint32_t *type,
+ DATA_BLOB *data);
+
+ WERROR (*set_value) (struct registry_key *key,
+ const char *name,
+ uint32_t type,
+ const DATA_BLOB data);
+};
+
+/**
+ * Handle to a full registry
+ * contains zero or more hives
+ */
+struct registry_context {
+ const struct registry_operations *ops;
+};
+
+struct auth_session_info;
+struct tevent_context;
+struct loadparm_context;
+
+/**
+ * Open the locally defined registry.
+ */
+WERROR reg_open_local(TALLOC_CTX *mem_ctx,
+ struct registry_context **ctx);
+
+WERROR reg_open_samba(TALLOC_CTX *mem_ctx,
+ struct registry_context **ctx,
+ struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx,
+ struct auth_session_info *session_info,
+ struct cli_credentials *credentials);
+
+/**
+ * Open the registry on a remote machine.
+ */
+WERROR reg_open_remote(struct registry_context **ctx,
+ struct auth_session_info *session_info,
+ struct cli_credentials *credentials,
+ struct loadparm_context *lp_ctx,
+ const char *location, struct tevent_context *ev);
+
+WERROR reg_open_wine(struct registry_context **ctx, const char *path);
+
+const char *reg_get_predef_name(uint32_t hkey);
+WERROR reg_get_predefined_key_by_name(struct registry_context *ctx,
+ const char *name,
+ struct registry_key **key);
+WERROR reg_get_predefined_key(struct registry_context *ctx,
+ uint32_t hkey,
+ struct registry_key **key);
+
+WERROR reg_open_key(TALLOC_CTX *mem_ctx, struct registry_key *parent,
+ const char *name, struct registry_key **result);
+
+WERROR reg_key_get_value_by_index(TALLOC_CTX *mem_ctx,
+ const struct registry_key *key, uint32_t idx,
+ const char **name,
+ uint32_t *type,
+ DATA_BLOB *data);
+WERROR reg_key_get_info(TALLOC_CTX *mem_ctx,
+ const struct registry_key *key,
+ const char **class_name,
+ uint32_t *num_subkeys,
+ uint32_t *num_values,
+ NTTIME *last_change_time,
+ uint32_t *max_subkeynamelen,
+ uint32_t *max_valnamelen,
+ uint32_t *max_valbufsize);
+WERROR reg_key_get_subkey_by_index(TALLOC_CTX *mem_ctx,
+ const struct registry_key *key,
+ int idx,
+ const char **name,
+ const char **classname,
+ NTTIME *last_mod_time);
+WERROR reg_key_get_subkey_by_name(TALLOC_CTX *mem_ctx,
+ const struct registry_key *key,
+ const char *name,
+ struct registry_key **subkey);
+WERROR reg_key_get_value_by_name(TALLOC_CTX *mem_ctx,
+ const struct registry_key *key,
+ const char *name,
+ uint32_t *type,
+ DATA_BLOB *data);
+WERROR reg_key_del(struct registry_key *parent, const char *name);
+WERROR reg_key_add_name(TALLOC_CTX *mem_ctx,
+ struct registry_key *parent, const char *name,
+ const char *classname,
+ struct security_descriptor *desc,
+ struct registry_key **newkey);
+WERROR reg_val_set(struct registry_key *key, const char *value,
+ uint32_t type, DATA_BLOB data);
+WERROR reg_get_sec_desc(TALLOC_CTX *ctx, const struct registry_key *key,
+ struct security_descriptor **secdesc);
+WERROR reg_del_value(struct registry_key *key, const char *valname);
+WERROR reg_key_flush(struct registry_key *key);
+WERROR reg_create_key(TALLOC_CTX *mem_ctx,
+ struct registry_key *parent,
+ const char *name,
+ const char *key_class,
+ struct security_descriptor *security,
+ struct registry_key **key);
+
+/* Utility functions */
+const char *str_regtype(int type);
+char *reg_val_data_string(TALLOC_CTX *mem_ctx, struct smb_iconv_convenience *iconv_convenience, uint32_t type, const DATA_BLOB data);
+char *reg_val_description(TALLOC_CTX *mem_ctx, struct smb_iconv_convenience *iconv_convenience, const char *name,
+ uint32_t type, const DATA_BLOB data);
+bool reg_string_to_val(TALLOC_CTX *mem_ctx, struct smb_iconv_convenience *iconv_convenience, const char *type_str,
+ const char *data_str, uint32_t *type, DATA_BLOB *data);
+WERROR reg_open_key_abs(TALLOC_CTX *mem_ctx, struct registry_context *handle,
+ const char *name, struct registry_key **result);
+WERROR reg_key_del_abs(struct registry_context *ctx, const char *path);
+WERROR reg_key_add_abs(TALLOC_CTX *mem_ctx, struct registry_context *ctx,
+ const char *path, uint32_t access_mask,
+ struct security_descriptor *sec_desc,
+ struct registry_key **result);
+WERROR reg_load_key(struct registry_context *ctx, struct registry_key *key,
+ const char *name, const char *filename);
+
+WERROR reg_mount_hive(struct registry_context *rctx,
+ struct hive_key *hive_key,
+ uint32_t key_id,
+ const char **elements);
+
+struct registry_key *reg_import_hive_key(struct registry_context *ctx,
+ struct hive_key *hive,
+ uint32_t predef_key,
+ const char **elements);
+WERROR reg_set_sec_desc(struct registry_key *key,
+ const struct security_descriptor *security);
+
+struct reg_diff_callbacks {
+ WERROR (*add_key) (void *callback_data, const char *key_name);
+ WERROR (*set_value) (void *callback_data, const char *key_name,
+ const char *value_name, uint32_t value_type,
+ DATA_BLOB value);
+ WERROR (*del_value) (void *callback_data, const char *key_name,
+ const char *value_name);
+ WERROR (*del_key) (void *callback_data, const char *key_name);
+ WERROR (*del_all_values) (void *callback_data, const char *key_name);
+ WERROR (*done) (void *callback_data);
+};
+
+WERROR reg_diff_apply(struct registry_context *ctx,
+ struct smb_iconv_convenience *ic, const char *filename);
+
+WERROR reg_generate_diff(struct registry_context *ctx1,
+ struct registry_context *ctx2,
+ const struct reg_diff_callbacks *callbacks,
+ void *callback_data);
+WERROR reg_dotreg_diff_save(TALLOC_CTX *ctx, const char *filename,
+ struct smb_iconv_convenience *iconv_convenience,
+ struct reg_diff_callbacks **callbacks,
+ void **callback_data);
+WERROR reg_preg_diff_save(TALLOC_CTX *ctx, const char *filename,
+ struct smb_iconv_convenience *ic,
+ struct reg_diff_callbacks **callbacks,
+ void **callback_data);
+WERROR reg_generate_diff_key(struct registry_key *oldkey,
+ struct registry_key *newkey,
+ const char *path,
+ const struct reg_diff_callbacks *callbacks,
+ void *callback_data);
+WERROR reg_diff_load(const char *filename,
+ struct smb_iconv_convenience *iconv_convenience,
+ const struct reg_diff_callbacks *callbacks,
+ void *callback_data);
+
+WERROR reg_dotreg_diff_load(int fd,
+ struct smb_iconv_convenience *iconv_convenience,
+ const struct reg_diff_callbacks *callbacks,
+ void *callback_data);
+
+WERROR reg_preg_diff_load(int fd,
+ struct smb_iconv_convenience *iconv_convenience,
+ const struct reg_diff_callbacks *callbacks,
+ void *callback_data);
+
+WERROR local_get_predefined_key(struct registry_context *ctx,
+ uint32_t key_id, struct registry_key **key);
+
+
+#endif /* _REGISTRY_H */
diff --git a/source4/lib/registry/registry.pc.in b/source4/lib/registry/registry.pc.in
new file mode 100644
index 0000000000..d981a45b2c
--- /dev/null
+++ b/source4/lib/registry/registry.pc.in
@@ -0,0 +1,12 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: registry
+Description: Windows-style registry library
+Requires: talloc
+Requires.private: ldb
+Version: 0.0.1
+Libs: -L${libdir} -lregistry
+Cflags: -I${includedir} -DHAVE_IMMEDIATE_STRUCTURES=1
diff --git a/source4/lib/registry/rpc.c b/source4/lib/registry/rpc.c
new file mode 100644
index 0000000000..0cb6b118a5
--- /dev/null
+++ b/source4/lib/registry/rpc.c
@@ -0,0 +1,516 @@
+/*
+ Samba Unix/Linux SMB implementation
+ RPC backend for the registry library
+ Copyright (C) 2003-2007 Jelmer Vernooij, jelmer@samba.org
+ Copyright (C) 2008 Matthias Dieter Wallnöfer, mwallnoefer@yahoo.de
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "includes.h"
+#include "registry.h"
+#include "librpc/gen_ndr/ndr_winreg_c.h"
+
+#define MAX_NAMESIZE 512
+#define MAX_VALSIZE 32768
+
+struct rpc_key {
+ struct registry_key key;
+ struct policy_handle pol;
+ struct dcerpc_pipe *pipe;
+
+ const char* classname;
+ uint32_t num_subkeys;
+ uint32_t max_subkeylen;
+ uint32_t max_classlen;
+ uint32_t num_values;
+ uint32_t max_valnamelen;
+ uint32_t max_valbufsize;
+ uint32_t secdescsize;
+ NTTIME last_changed_time;
+};
+
+struct rpc_registry_context {
+ struct registry_context context;
+ struct dcerpc_pipe *pipe;
+};
+
+static struct registry_operations reg_backend_rpc;
+
+/**
+ * This is the RPC backend for the registry library.
+ */
+
+#define openhive(u) static WERROR open_ ## u(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, struct policy_handle *hnd) \
+{ \
+ struct winreg_Open ## u r; \
+ NTSTATUS status; \
+\
+ ZERO_STRUCT(r); \
+ r.in.system_name = NULL; \
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; \
+ r.out.handle = hnd;\
+\
+ status = dcerpc_winreg_Open ## u(p, mem_ctx, &r); \
+\
+ if (!NT_STATUS_IS_OK(status)) { \
+ DEBUG(1, ("OpenHive failed - %s\n", nt_errstr(status))); \
+ return ntstatus_to_werror(status); \
+ } \
+\
+ return r.out.result;\
+}
+
+openhive(HKLM)
+openhive(HKCU)
+openhive(HKPD)
+openhive(HKU)
+openhive(HKCR)
+openhive(HKDD)
+openhive(HKCC)
+
+static struct {
+ uint32_t hkey;
+ WERROR (*open) (struct dcerpc_pipe *p, TALLOC_CTX *,
+ struct policy_handle *h);
+} known_hives[] = {
+ { HKEY_LOCAL_MACHINE, open_HKLM },
+ { HKEY_CURRENT_USER, open_HKCU },
+ { HKEY_CLASSES_ROOT, open_HKCR },
+ { HKEY_PERFORMANCE_DATA, open_HKPD },
+ { HKEY_USERS, open_HKU },
+ { HKEY_DYN_DATA, open_HKDD },
+ { HKEY_CURRENT_CONFIG, open_HKCC },
+ { 0, NULL }
+};
+
+static WERROR rpc_query_key(TALLOC_CTX *mem_ctx, const struct registry_key *k);
+
+static WERROR rpc_get_predefined_key(struct registry_context *ctx,
+ uint32_t hkey_type,
+ struct registry_key **k)
+{
+ int n;
+ struct rpc_key *mykeydata;
+ struct rpc_registry_context *rctx = talloc_get_type(ctx, struct rpc_registry_context);
+
+ *k = NULL;
+
+ for(n = 0; known_hives[n].hkey; n++) {
+ if(known_hives[n].hkey == hkey_type)
+ break;
+ }
+
+ if (known_hives[n].open == NULL) {
+ DEBUG(1, ("No such hive %d\n", hkey_type));
+ return WERR_NO_MORE_ITEMS;
+ }
+
+ mykeydata = talloc_zero(ctx, struct rpc_key);
+ mykeydata->key.context = ctx;
+ mykeydata->pipe = talloc_reference(mykeydata, rctx->pipe);
+ mykeydata->num_values = -1;
+ mykeydata->num_subkeys = -1;
+ *k = (struct registry_key *)mykeydata;
+ return known_hives[n].open(mykeydata->pipe, mykeydata, &(mykeydata->pol));
+}
+
+#if 0
+static WERROR rpc_key_put_rpc_data(TALLOC_CTX *mem_ctx, struct registry_key *k)
+{
+ struct winreg_OpenKey r;
+ struct rpc_key_data *mykeydata;
+
+ k->backend_data = mykeydata = talloc_zero(mem_ctx, struct rpc_key_data);
+ mykeydata->num_values = -1;
+ mykeydata->num_subkeys = -1;
+
+ /* Then, open the handle using the hive */
+
+ ZERO_STRUCT(r);
+ r.in.handle = &(((struct rpc_key_data *)k->hive->root->backend_data)->pol);
+ r.in.keyname.name = k->path;
+ r.in.unknown = 0x00000000;
+ r.in.access_mask = 0x02000000;
+ r.out.handle = &mykeydata->pol;
+
+ dcerpc_winreg_OpenKey((struct dcerpc_pipe *)k->hive->backend_data,
+ mem_ctx, &r);
+
+ return r.out.result;
+}
+#endif
+
+static WERROR rpc_open_key(TALLOC_CTX *mem_ctx, struct registry_key *h,
+ const char *name, struct registry_key **key)
+{
+ struct rpc_key *parentkeydata = talloc_get_type(h, struct rpc_key),
+ *mykeydata;
+ struct winreg_OpenKey r;
+ NTSTATUS status;
+
+ mykeydata = talloc_zero(mem_ctx, struct rpc_key);
+ mykeydata->key.context = parentkeydata->key.context;
+ mykeydata->pipe = talloc_reference(mykeydata, parentkeydata->pipe);
+ mykeydata->num_values = -1;
+ mykeydata->num_subkeys = -1;
+ *key = (struct registry_key *)mykeydata;
+
+ /* Then, open the handle using the hive */
+ ZERO_STRUCT(r);
+ r.in.parent_handle = &parentkeydata->pol;
+ r.in.keyname.name = name;
+ r.in.unknown = 0x00000000;
+ r.in.access_mask = 0x02000000;
+ r.out.handle = &mykeydata->pol;
+
+ status = dcerpc_winreg_OpenKey(mykeydata->pipe, mem_ctx, &r);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("OpenKey failed - %s\n", nt_errstr(status)));
+ return ntstatus_to_werror(status);
+ }
+
+ return r.out.result;
+}
+
+static WERROR rpc_get_value_by_index(TALLOC_CTX *mem_ctx,
+ const struct registry_key *parent,
+ uint32_t n,
+ const char **value_name,
+ uint32_t *type,
+ DATA_BLOB *data)
+{
+ struct rpc_key *mykeydata = talloc_get_type(parent, struct rpc_key);
+ struct winreg_EnumValue r;
+ struct winreg_ValNameBuf name;
+ uint8_t value;
+ uint32_t val_size = MAX_VALSIZE;
+ uint32_t zero = 0;
+ WERROR error;
+ NTSTATUS status;
+
+ if (mykeydata->num_values == -1) {
+ error = rpc_query_key(mem_ctx, parent);
+ if(!W_ERROR_IS_OK(error)) return error;
+ }
+
+ name.name = "";
+ name.size = MAX_NAMESIZE;
+
+ ZERO_STRUCT(r);
+ r.in.handle = &mykeydata->pol;
+ r.in.enum_index = n;
+ r.in.name = &name;
+ r.in.type = type;
+ r.in.value = &value;
+ r.in.size = &val_size;
+ r.in.length = &zero;
+ r.out.name = &name;
+ r.out.type = type;
+ r.out.value = &value;
+ r.out.size = &val_size;
+ r.out.length = &zero;
+
+ status = dcerpc_winreg_EnumValue(mykeydata->pipe, mem_ctx, &r);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("EnumValue failed - %s\n", nt_errstr(status)));
+ return ntstatus_to_werror(status);
+ }
+
+ *value_name = talloc_reference(mem_ctx, r.out.name->name);
+ *type = *(r.out.type);
+ *data = data_blob_talloc(mem_ctx, r.out.value, *r.out.length);
+
+ return r.out.result;
+}
+
+static WERROR rpc_get_value_by_name(TALLOC_CTX *mem_ctx,
+ const struct registry_key *parent,
+ const char *value_name,
+ uint32_t *type,
+ DATA_BLOB *data)
+{
+ struct rpc_key *mykeydata = talloc_get_type(parent, struct rpc_key);
+ struct winreg_QueryValue r;
+ struct winreg_String name;
+ uint8_t value;
+ uint32_t val_size = MAX_VALSIZE;
+ uint32_t zero = 0;
+ WERROR error;
+ NTSTATUS status;
+
+ if (mykeydata->num_values == -1) {
+ error = rpc_query_key(mem_ctx, parent);
+ if(!W_ERROR_IS_OK(error)) return error;
+ }
+
+ name.name = value_name;
+
+ ZERO_STRUCT(r);
+ r.in.handle = &mykeydata->pol;
+ r.in.value_name = &name;
+ r.in.type = type;
+ r.in.data = &value;
+ r.in.data_size = &val_size;
+ r.in.data_length = &zero;
+ r.out.type = type;
+ r.out.data = &value;
+ r.out.data_size = &val_size;
+ r.out.data_length = &zero;
+
+ status = dcerpc_winreg_QueryValue(mykeydata->pipe, mem_ctx, &r);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("QueryValue failed - %s\n", nt_errstr(status)));
+ return ntstatus_to_werror(status);
+ }
+
+ *type = *(r.out.type);
+ *data = data_blob_talloc(mem_ctx, r.out.data, *r.out.data_length);
+
+ return r.out.result;
+}
+
+static WERROR rpc_get_subkey_by_index(TALLOC_CTX *mem_ctx,
+ const struct registry_key *parent,
+ uint32_t n,
+ const char **name,
+ const char **keyclass,
+ NTTIME *last_changed_time)
+{
+ struct winreg_EnumKey r;
+ struct rpc_key *mykeydata = talloc_get_type(parent, struct rpc_key);
+ struct winreg_StringBuf namebuf, classbuf;
+ NTTIME change_time = 0;
+ NTSTATUS status;
+
+ namebuf.name = "";
+ namebuf.size = MAX_NAMESIZE;
+ classbuf.name = NULL;
+ classbuf.size = 0;
+
+ ZERO_STRUCT(r);
+ r.in.handle = &mykeydata->pol;
+ r.in.enum_index = n;
+ r.in.name = &namebuf;
+ r.in.keyclass = &classbuf;
+ r.in.last_changed_time = &change_time;
+ r.out.name = &namebuf;
+ r.out.keyclass = &classbuf;
+ r.out.last_changed_time = &change_time;
+
+ status = dcerpc_winreg_EnumKey(mykeydata->pipe, mem_ctx, &r);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("EnumKey failed - %s\n", nt_errstr(status)));
+ return ntstatus_to_werror(status);
+ }
+
+ if (name != NULL)
+ *name = talloc_reference(mem_ctx, r.out.name->name);
+ if (keyclass != NULL)
+ *keyclass = talloc_reference(mem_ctx, r.out.keyclass->name);
+ if (last_changed_time != NULL)
+ *last_changed_time = *(r.out.last_changed_time);
+
+ return r.out.result;
+}
+
+static WERROR rpc_add_key(TALLOC_CTX *mem_ctx,
+ struct registry_key *parent, const char *name,
+ const char *key_class,
+ struct security_descriptor *sec,
+ struct registry_key **key)
+{
+ struct winreg_CreateKey r;
+ struct rpc_key *parentkd = talloc_get_type(parent, struct rpc_key);
+ struct rpc_key *rpck = talloc(mem_ctx, struct rpc_key);
+
+ NTSTATUS status;
+
+ ZERO_STRUCT(r);
+ r.in.handle = &parentkd->pol;
+ r.in.name.name = name;
+ r.in.keyclass.name = NULL;
+ r.in.options = 0;
+ r.in.access_mask = 0x02000000;
+ r.in.secdesc = NULL;
+ r.in.action_taken = NULL;
+ r.out.new_handle = &rpck->pol;
+ r.out.action_taken = NULL;
+
+ status = dcerpc_winreg_CreateKey(parentkd->pipe, mem_ctx, &r);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(rpck);
+ DEBUG(1, ("CreateKey failed - %s\n", nt_errstr(status)));
+ return ntstatus_to_werror(status);
+ }
+
+ rpck->pipe = talloc_reference(rpck, parentkd->pipe);
+ *key = (struct registry_key *)rpck;
+
+ return r.out.result;
+}
+
+static WERROR rpc_query_key(TALLOC_CTX *mem_ctx, const struct registry_key *k)
+{
+ struct winreg_QueryInfoKey r;
+ struct rpc_key *mykeydata = talloc_get_type(k, struct rpc_key);
+ struct winreg_String classname;
+ NTSTATUS status;
+
+ classname.name = NULL;
+
+ ZERO_STRUCT(r);
+ r.in.handle = &mykeydata->pol;
+ r.in.classname = &classname;
+ r.out.classname = &classname;
+ r.out.num_subkeys = &mykeydata->num_subkeys;
+ r.out.max_subkeylen = &mykeydata->max_subkeylen;
+ r.out.max_classlen = &mykeydata->max_classlen;
+ r.out.num_values = &mykeydata->num_values;
+ r.out.max_valnamelen = &mykeydata->max_valnamelen;
+ r.out.max_valbufsize = &mykeydata->max_valbufsize;
+ r.out.secdescsize = &mykeydata->secdescsize;
+ r.out.last_changed_time = &mykeydata->last_changed_time;
+
+ status = dcerpc_winreg_QueryInfoKey(mykeydata->pipe, mem_ctx, &r);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("QueryInfoKey failed - %s\n", nt_errstr(status)));
+ return ntstatus_to_werror(status);
+ }
+
+ mykeydata->classname = talloc_reference(mem_ctx, r.out.classname->name);
+
+ return r.out.result;
+}
+
+static WERROR rpc_del_key(struct registry_key *parent, const char *name)
+{
+ NTSTATUS status;
+ struct rpc_key *mykeydata = talloc_get_type(parent, struct rpc_key);
+ struct winreg_DeleteKey r;
+ TALLOC_CTX *mem_ctx = talloc_init("del_key");
+
+ ZERO_STRUCT(r);
+ r.in.handle = &mykeydata->pol;
+ r.in.key.name = name;
+
+ status = dcerpc_winreg_DeleteKey(mykeydata->pipe, mem_ctx, &r);
+
+ talloc_free(mem_ctx);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("DeleteKey failed - %s\n", nt_errstr(status)));
+ return ntstatus_to_werror(status);
+ }
+
+ return r.out.result;
+}
+
+static WERROR rpc_get_info(TALLOC_CTX *mem_ctx, const struct registry_key *key,
+ const char **classname,
+ uint32_t *num_subkeys,
+ uint32_t *num_values,
+ NTTIME *last_changed_time,
+ uint32_t *max_subkeylen,
+ uint32_t *max_valnamelen,
+ uint32_t *max_valbufsize)
+{
+ struct rpc_key *mykeydata = talloc_get_type(key, struct rpc_key);
+ WERROR error;
+
+ if (mykeydata->num_values == -1) {
+ error = rpc_query_key(mem_ctx, key);
+ if(!W_ERROR_IS_OK(error)) return error;
+ }
+
+ if (classname != NULL)
+ *classname = mykeydata->classname;
+
+ if (num_subkeys != NULL)
+ *num_subkeys = mykeydata->num_subkeys;
+
+ if (num_values != NULL)
+ *num_values = mykeydata->num_values;
+
+ if (last_changed_time != NULL)
+ *last_changed_time = mykeydata->last_changed_time;
+
+ if (max_subkeylen != NULL)
+ *max_subkeylen = mykeydata->max_subkeylen;
+
+ if (max_valnamelen != NULL)
+ *max_valnamelen = mykeydata->max_valnamelen;
+
+ if (max_valbufsize != NULL)
+ *max_valbufsize = mykeydata->max_valbufsize;
+
+ return WERR_OK;
+}
+
+static struct registry_operations reg_backend_rpc = {
+ .name = "rpc",
+ .open_key = rpc_open_key,
+ .get_predefined_key = rpc_get_predefined_key,
+ .enum_key = rpc_get_subkey_by_index,
+ .enum_value = rpc_get_value_by_index,
+ .get_value = rpc_get_value_by_name,
+ .create_key = rpc_add_key,
+ .delete_key = rpc_del_key,
+ .get_key_info = rpc_get_info,
+ .get_predefined_key = rpc_get_predefined_key,
+};
+
+_PUBLIC_ WERROR reg_open_remote(struct registry_context **ctx,
+ struct auth_session_info *session_info,
+ struct cli_credentials *credentials,
+ struct loadparm_context *lp_ctx,
+ const char *location, struct tevent_context *ev)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p;
+ struct rpc_registry_context *rctx;
+
+ dcerpc_init(lp_ctx);
+
+ rctx = talloc(NULL, struct rpc_registry_context);
+
+ /* Default to local smbd if no connection is specified */
+ if (!location) {
+ location = talloc_strdup(rctx, "ncalrpc:");
+ }
+
+ status = dcerpc_pipe_connect(rctx /* TALLOC_CTX */,
+ &p, location,
+ &ndr_table_winreg,
+ credentials, ev, lp_ctx);
+ rctx->pipe = p;
+
+ if(NT_STATUS_IS_ERR(status)) {
+ DEBUG(1, ("Unable to open '%s': %s\n", location,
+ nt_errstr(status)));
+ talloc_free(rctx);
+ *ctx = NULL;
+ return ntstatus_to_werror(status);
+ }
+
+ *ctx = (struct registry_context *)rctx;
+ (*ctx)->ops = &reg_backend_rpc;
+
+ return WERR_OK;
+}
diff --git a/source4/lib/registry/samba.c b/source4/lib/registry/samba.c
new file mode 100644
index 0000000000..fc59bdfdfa
--- /dev/null
+++ b/source4/lib/registry/samba.c
@@ -0,0 +1,97 @@
+/*
+ Unix SMB/CIFS implementation.
+ Copyright (C) Jelmer Vernooij 2004-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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "registry.h"
+#include "param/param.h"
+
+/**
+ * @file
+ * @brief Samba-specific registry functions
+ */
+
+static WERROR mount_samba_hive(struct registry_context *ctx,
+ struct tevent_context *event_ctx,
+ struct loadparm_context *lp_ctx,
+ struct auth_session_info *auth_info,
+ struct cli_credentials *creds,
+ const char *name,
+ uint32_t hive_id)
+{
+ WERROR error;
+ struct hive_key *hive;
+ const char *location;
+
+ location = talloc_asprintf(ctx, "%s/%s.ldb",
+ lp_private_dir(lp_ctx),
+ name);
+
+ error = reg_open_hive(ctx, location, auth_info, creds, event_ctx, lp_ctx, &hive);
+
+ if (W_ERROR_EQUAL(error, WERR_BADFILE))
+ error = reg_open_ldb_file(ctx, location, auth_info,
+ creds, event_ctx, lp_ctx, &hive);
+
+ if (!W_ERROR_IS_OK(error))
+ return error;
+
+ return reg_mount_hive(ctx, hive, hive_id, NULL);
+}
+
+
+_PUBLIC_ WERROR reg_open_samba(TALLOC_CTX *mem_ctx,
+ struct registry_context **ctx,
+ struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx,
+ struct auth_session_info *session_info,
+ struct cli_credentials *credentials)
+{
+ WERROR result;
+
+ result = reg_open_local(mem_ctx, ctx);
+ if (!W_ERROR_IS_OK(result)) {
+ return result;
+ }
+
+ mount_samba_hive(*ctx, ev_ctx, lp_ctx, session_info, credentials,
+ "hklm", HKEY_LOCAL_MACHINE);
+
+ mount_samba_hive(*ctx, ev_ctx, lp_ctx, session_info, credentials,
+ "hkcr", HKEY_CLASSES_ROOT);
+
+ /* FIXME: Should be mounted from NTUSER.DAT in the home directory of the
+ * current user */
+ mount_samba_hive(*ctx, ev_ctx, lp_ctx, session_info, credentials,
+ "hkcu", HKEY_CURRENT_USER);
+
+ mount_samba_hive(*ctx, ev_ctx, lp_ctx, session_info, credentials,
+ "hku", HKEY_USERS);
+
+ /* FIXME: Different hive backend for HKEY_CLASSES_ROOT: merged view of HKEY_LOCAL_MACHINE\Software\Classes
+ * and HKEY_CURRENT_USER\Software\Classes */
+
+ /* FIXME: HKEY_CURRENT_CONFIG is an alias for HKEY_LOCAL_MACHINE\System\CurrentControlSet\Hardware Profiles\Current */
+
+ /* FIXME: HKEY_PERFORMANCE_DATA is dynamically generated */
+
+ /* FIXME: HKEY_LOCAL_MACHINE\Hardware is autogenerated */
+
+ /* FIXME: HKEY_LOCAL_MACHINE\Security\SAM is an alias for HKEY_LOCAL_MACHINE\SAM */
+
+ return WERR_OK;
+}
diff --git a/source4/lib/registry/tests/bindings.py b/source4/lib/registry/tests/bindings.py
new file mode 100644
index 0000000000..8c3233ef1e
--- /dev/null
+++ b/source4/lib/registry/tests/bindings.py
@@ -0,0 +1,57 @@
+#!/usr/bin/python
+
+# Unix SMB/CIFS implementation.
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 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 <http://www.gnu.org/licenses/>.
+#
+
+import os
+import unittest
+from samba import registry
+import samba.tests
+
+class HelperTests(unittest.TestCase):
+ def test_predef_to_name(self):
+ self.assertEquals("HKEY_LOCAL_MACHINE",
+ registry.get_predef_name(0x80000002))
+
+ def test_str_regtype(self):
+ self.assertEquals("REG_DWORD", registry.str_regtype(4))
+
+
+
+class HiveTests(samba.tests.TestCaseInTempDir):
+ def setUp(self):
+ super(HiveTests, self).setUp()
+ self.hive_path = os.path.join(self.tempdir, "ldb_new.ldb")
+ self.hive = registry.open_ldb(self.hive_path)
+
+ def tearDown(self):
+ del self.hive
+ os.unlink(self.hive_path)
+
+ def test_ldb_new(self):
+ self.assertTrue(self.hive is not None)
+
+ #def test_flush(self):
+ # self.hive.flush()
+
+ #def test_del_value(self):
+ # self.hive.del_value("FOO")
+
+
+class RegistryTests(unittest.TestCase):
+ def test_new(self):
+ self.registry = registry.Registry()
diff --git a/source4/lib/registry/tests/diff.c b/source4/lib/registry/tests/diff.c
new file mode 100644
index 0000000000..3dba7293bc
--- /dev/null
+++ b/source4/lib/registry/tests/diff.c
@@ -0,0 +1,296 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ local testing of registry diff functionality
+
+ Copyright (C) Jelmer Vernooij 2007
+ Copyright (C) Wilco Baan Hofman 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/registry/registry.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/winreg.h"
+#include "param/param.h"
+
+struct diff_tcase_data {
+ struct registry_context *r1_ctx;
+ struct registry_context *r2_ctx;
+ struct reg_diff_callbacks *callbacks;
+ void *callback_data;
+ char *tempdir;
+ char *filename;
+};
+
+static bool test_generate_diff(struct torture_context *tctx, void *tcase_data)
+{
+ WERROR error;
+ struct diff_tcase_data *td = tcase_data;
+
+ error = reg_generate_diff(td->r1_ctx, td->r2_ctx,
+ td->callbacks,
+ td->callback_data);
+ torture_assert_werr_ok(tctx, error, "reg_generate_diff");
+
+ return true;
+}
+
+#if 0
+static bool test_diff_load(struct torture_context *tctx, void *tcase_data)
+{
+ struct diff_tcase_data *td = tcase_data;
+ struct smb_iconv_convenience *ic;
+ struct reg_diff_callbacks *callbacks;
+ void *data;
+ WERROR error;
+
+ ic = lp_iconv_convenience(tctx->lp_ctx);
+
+ error = reg_diff_load(td->filename, iconv_convenience, callbacks, data);
+ torture_assert_werr_ok(tctx, error, "reg_diff_load");
+
+ return true;
+}
+#endif
+static bool test_diff_apply(struct torture_context *tctx, void *tcase_data)
+{
+ struct diff_tcase_data *td = tcase_data;
+ struct registry_key *key;
+ WERROR error;
+
+ error = reg_diff_apply(td->r1_ctx, lp_iconv_convenience(tctx->lp_ctx), td->filename);
+ torture_assert_werr_ok(tctx, error, "reg_diff_apply");
+
+ error = td->r1_ctx->ops->get_predefined_key(td->r1_ctx, HKEY_LOCAL_MACHINE, &key);
+ torture_assert_werr_ok(tctx, error, "Opening HKEY_LOCAL_MACHINE failed");
+
+ /* If this generates an error it could be that the apply doesn't work,
+ * but also that the reg_generate_diff didn't work. */
+ error = td->r1_ctx->ops->open_key(td->r1_ctx, key, "Software", &key);
+ torture_assert_werr_ok(tctx, error, "Opening HKLM\\Software failed");
+ error = td->r1_ctx->ops->open_key(td->r1_ctx, key, "Microsoft", &key);
+ torture_assert_werr_ok(tctx, error, "Opening HKLM\\Software\\Microsoft failed");
+ error = td->r1_ctx->ops->open_key(td->r1_ctx, key, "Windows", &key);
+ torture_assert_werr_ok(tctx, error, "Opening HKLM\\..\\Microsoft\\Windows failed");
+ error = td->r1_ctx->ops->open_key(td->r1_ctx, key, "CurrentVersion", &key);
+ torture_assert_werr_ok(tctx, error, "Opening HKLM\\..\\Windows\\CurrentVersion failed");
+ error = td->r1_ctx->ops->open_key(td->r1_ctx, key, "Policies", &key);
+ torture_assert_werr_ok(tctx, error, "Opening HKLM\\..\\CurrentVersion\\Policies failed");
+ error = td->r1_ctx->ops->open_key(td->r1_ctx, key, "Explorer", &key);
+ torture_assert_werr_ok(tctx, error, "Opening HKLM\\..\\Policies\\Explorer failed");
+
+ return true;
+}
+
+static const char *added_key = NULL;
+
+static WERROR test_add_key(void *callback_data, const char *key_name)
+{
+ added_key = talloc_strdup(callback_data, key_name);
+
+ return WERR_OK;
+}
+
+static bool test_generate_diff_key_add(struct torture_context *tctx, void *tcase_data)
+{
+ struct reg_diff_callbacks cb;
+ struct registry_key rk;
+
+ return true;
+
+ ZERO_STRUCT(cb);
+
+ cb.add_key = test_add_key;
+
+ if (W_ERROR_IS_OK(reg_generate_diff_key(&rk, NULL, "bla", &cb, tctx)))
+ return false;
+
+ torture_assert_str_equal(tctx, added_key, "bla", "key added");
+
+ return true;
+}
+
+static bool test_generate_diff_key_null(struct torture_context *tctx, void *tcase_data)
+{
+ struct reg_diff_callbacks cb;
+
+ ZERO_STRUCT(cb);
+
+ if (!W_ERROR_IS_OK(reg_generate_diff_key(NULL, NULL, "", &cb, NULL)))
+ return false;
+
+ return true;
+}
+
+static void tcase_add_tests (struct torture_tcase *tcase)
+{
+ torture_tcase_add_simple_test(tcase, "test_generate_diff_key_add",
+ test_generate_diff_key_add);
+ torture_tcase_add_simple_test(tcase, "test_generate_diff_key_null",
+ test_generate_diff_key_null);
+ torture_tcase_add_simple_test(tcase, "test_generate_diff",
+ test_generate_diff);
+ torture_tcase_add_simple_test(tcase, "test_diff_apply",
+ test_diff_apply);
+/* torture_tcase_add_simple_test(tcase, "test_diff_load",
+ test_diff_load);
+*/
+}
+
+static bool diff_setup_tcase(struct torture_context *tctx, void **data)
+{
+ struct registry_context *r1_ctx, *r2_ctx;
+ WERROR error;
+ NTSTATUS status;
+ struct hive_key *r1_hklm, *r1_hkcu;
+ struct hive_key *r2_hklm, *r2_hkcu;
+ const char *filename;
+ struct diff_tcase_data *td;
+ struct registry_key *key, *newkey;
+ DATA_BLOB blob;
+
+ td = talloc(tctx, struct diff_tcase_data);
+
+ /* Create two registry contexts */
+ error = reg_open_local(tctx, &r1_ctx);
+ torture_assert_werr_ok(tctx, error, "Opening registry 1 for patch tests failed");
+
+ error = reg_open_local(tctx, &r2_ctx);
+ torture_assert_werr_ok(tctx, error, "Opening registry 2 for patch tests failed");
+
+ /* Create temp directory */
+ status = torture_temp_dir(tctx, "patchfile", &td->tempdir);
+ torture_assert_ntstatus_ok(tctx, status, "Creating temp dir failed");
+
+ /* Create and mount HKLM and HKCU hives for registry 1 */
+ filename = talloc_asprintf(tctx, "%s/r1_local_machine.ldb", td->tempdir);
+ error = reg_open_ldb_file(tctx, filename, NULL, NULL, tctx->ev, tctx->lp_ctx, &r1_hklm);
+ torture_assert_werr_ok(tctx, error, "Opening local machine file failed");
+
+ error = reg_mount_hive(r1_ctx, r1_hklm, HKEY_LOCAL_MACHINE, NULL);
+ torture_assert_werr_ok(tctx, error, "Mounting hive failed");
+
+ filename = talloc_asprintf(tctx, "%s/r1_current_user.ldb", td->tempdir);
+ error = reg_open_ldb_file(tctx, filename, NULL, NULL, tctx->ev, tctx->lp_ctx, &r1_hkcu);
+ torture_assert_werr_ok(tctx, error, "Opening current user file failed");
+
+ error = reg_mount_hive(r1_ctx, r1_hkcu, HKEY_CURRENT_USER, NULL);
+ torture_assert_werr_ok(tctx, error, "Mounting hive failed");
+
+ /* Create and mount HKLM and HKCU hives for registry 2 */
+ filename = talloc_asprintf(tctx, "%s/r2_local_machine.ldb", td->tempdir);
+ error = reg_open_ldb_file(tctx, filename, NULL, NULL, tctx->ev, tctx->lp_ctx, &r2_hklm);
+ torture_assert_werr_ok(tctx, error, "Opening local machine file failed");
+
+ error = reg_mount_hive(r2_ctx, r2_hklm, HKEY_LOCAL_MACHINE, NULL);
+ torture_assert_werr_ok(tctx, error, "Mounting hive failed");
+
+ filename = talloc_asprintf(tctx, "%s/r2_current_user.ldb", td->tempdir);
+ error = reg_open_ldb_file(tctx, filename, NULL, NULL, tctx->ev, tctx->lp_ctx, &r2_hkcu);
+ torture_assert_werr_ok(tctx, error, "Opening current user file failed");
+
+ error = reg_mount_hive(r2_ctx, r2_hkcu, HKEY_CURRENT_USER, NULL);
+ torture_assert_werr_ok(tctx, error, "Mounting hive failed");
+
+ error = r1_ctx->ops->get_predefined_key(r1_ctx, HKEY_CURRENT_USER, &key);
+ torture_assert_werr_ok(tctx, error, "Opening HKEY_CURRENT_USER failed");
+ error = r1_ctx->ops->create_key(r1_ctx, key, "Network", NULL, NULL, &newkey);
+ torture_assert_werr_ok(tctx, error, "Opening HKCU\\Network failed");
+ error = r1_ctx->ops->create_key(r1_ctx, newkey, "L", NULL, NULL, &newkey);
+ torture_assert_werr_ok(tctx, error, "Opening HKCU\\Network\\L failed");
+
+ error = r2_ctx->ops->get_predefined_key(r2_ctx, HKEY_LOCAL_MACHINE, &key);
+ torture_assert_werr_ok(tctx, error, "Opening HKEY_LOCAL_MACHINE failed");
+ error = r2_ctx->ops->create_key(r2_ctx, key, "Software", NULL, NULL, &newkey);
+ torture_assert_werr_ok(tctx, error, "Creating HKLM\\Sofware failed");
+ error = r2_ctx->ops->create_key(r2_ctx, newkey, "Microsoft", NULL, NULL, &newkey);
+ torture_assert_werr_ok(tctx, error, "Creating HKLM\\Software\\Microsoft failed");
+ error = r2_ctx->ops->create_key(r2_ctx, newkey, "Windows", NULL, NULL, &newkey);
+ torture_assert_werr_ok(tctx, error, "Creating HKLM\\Software\\Microsoft\\Windows failed");
+ error = r2_ctx->ops->create_key(r2_ctx, newkey, "CurrentVersion", NULL, NULL, &newkey);
+ torture_assert_werr_ok(tctx, error, "Creating HKLM\\..\\Windows\\CurrentVersion failed");
+ error = r2_ctx->ops->create_key(r2_ctx, newkey, "Policies", NULL, NULL, &newkey);
+ torture_assert_werr_ok(tctx, error, "Creating HKLM\\..\\CurrentVersion\\Policies failed");
+ error = r2_ctx->ops->create_key(r2_ctx, newkey, "Explorer", NULL, NULL, &newkey);
+ torture_assert_werr_ok(tctx, error, "Creating HKLM\\..\\Policies\\Explorer failed");
+
+
+ blob.data = (void *)talloc(r2_ctx, uint32_t);
+ SIVAL(blob.data, 0, 0x03ffffff);
+ blob.length = sizeof(uint32_t);
+
+ r1_ctx->ops->set_value(newkey, "NoDrives", REG_DWORD, blob);
+
+ /* Set test case data */
+ td->r1_ctx = r1_ctx;
+ td->r2_ctx = r2_ctx;
+
+ *data = td;
+
+ return true;
+}
+
+static bool diff_setup_preg_tcase (struct torture_context *tctx, void **data)
+{
+ struct diff_tcase_data *td;
+ struct smb_iconv_convenience *ic;
+ WERROR error;
+
+ diff_setup_tcase(tctx, data);
+ td = *data;
+
+ ic = lp_iconv_convenience(tctx->lp_ctx);
+
+ td->filename = talloc_asprintf(tctx, "%s/test.pol", td->tempdir);
+ error = reg_preg_diff_save(tctx, td->filename, ic, &td->callbacks, &td->callback_data);
+ torture_assert_werr_ok(tctx, error, "reg_preg_diff_save");
+
+ return true;
+}
+
+static bool diff_setup_dotreg_tcase (struct torture_context *tctx, void **data)
+{
+ struct diff_tcase_data *td;
+ struct smb_iconv_convenience *ic;
+ WERROR error;
+
+ diff_setup_tcase(tctx, data);
+ td = *data;
+
+ ic = lp_iconv_convenience(tctx->lp_ctx);
+
+ td->filename = talloc_asprintf(tctx, "%s/test.reg", td->tempdir);
+ error = reg_dotreg_diff_save(tctx, td->filename, ic, &td->callbacks, &td->callback_data);
+ torture_assert_werr_ok(tctx, error, "reg_dotreg_diff_save");
+
+ return true;
+}
+
+struct torture_suite *torture_registry_diff(TALLOC_CTX *mem_ctx)
+{
+ struct torture_tcase *tcase;
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "DIFF");
+
+ tcase = torture_suite_add_tcase(suite, "PReg");
+ torture_tcase_set_fixture(tcase, diff_setup_preg_tcase, NULL);
+ tcase_add_tests(tcase);
+
+ tcase = torture_suite_add_tcase(suite, "dotreg");
+ torture_tcase_set_fixture(tcase, diff_setup_dotreg_tcase, NULL);
+ tcase_add_tests(tcase);
+
+ return suite;
+}
diff --git a/source4/lib/registry/tests/generic.c b/source4/lib/registry/tests/generic.c
new file mode 100644
index 0000000000..3de7602e53
--- /dev/null
+++ b/source4/lib/registry/tests/generic.c
@@ -0,0 +1,138 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ local testing of registry library
+
+ Copyright (C) Jelmer Vernooij 2005-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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/registry/registry.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/winreg.h"
+#include "param/param.h"
+
+struct torture_suite *torture_registry_hive(TALLOC_CTX *mem_ctx);
+struct torture_suite *torture_registry_registry(TALLOC_CTX *mem_ctx);
+struct torture_suite *torture_registry_diff(TALLOC_CTX *mem_ctx);
+
+static bool test_str_regtype(struct torture_context *ctx)
+{
+ torture_assert_str_equal(ctx, str_regtype(1),
+ "REG_SZ", "REG_SZ failed");
+ torture_assert_str_equal(ctx, str_regtype(4),
+ "REG_DWORD", "REG_DWORD failed");
+
+ return true;
+}
+
+
+static bool test_reg_val_data_string_dword(struct torture_context *ctx)
+{
+ uint32_t d = 0x20;
+ DATA_BLOB db = { (uint8_t *)&d, sizeof(d) };
+ torture_assert_str_equal(ctx, "0x20",
+ reg_val_data_string(ctx, lp_iconv_convenience(ctx->lp_ctx), REG_DWORD, db),
+ "dword failed");
+ return true;
+}
+
+static bool test_reg_val_data_string_sz(struct torture_context *ctx)
+{
+ DATA_BLOB db;
+ convert_string_talloc_convenience(ctx, lp_iconv_convenience(ctx->lp_ctx), CH_UTF8, CH_UTF16,
+ "bla", 3, (void **)&db.data, &db.length, false);
+ torture_assert_str_equal(ctx, "bla",
+ reg_val_data_string(ctx, lp_iconv_convenience(ctx->lp_ctx), REG_SZ, db),
+ "sz failed");
+ db.length = 4;
+ torture_assert_str_equal(ctx, "bl",
+ reg_val_data_string(ctx, lp_iconv_convenience(ctx->lp_ctx), REG_SZ, db),
+ "sz failed");
+ return true;
+}
+
+static bool test_reg_val_data_string_binary(struct torture_context *ctx)
+{
+ uint8_t x[] = { 0x1, 0x2, 0x3, 0x4 };
+ DATA_BLOB db = { x, 4 };
+ torture_assert_str_equal(ctx, "01020304",
+ reg_val_data_string(ctx, lp_iconv_convenience(ctx->lp_ctx), REG_BINARY, db),
+ "binary failed");
+ return true;
+}
+
+
+static bool test_reg_val_data_string_empty(struct torture_context *ctx)
+{
+ DATA_BLOB db = { NULL, 0 };
+ torture_assert_str_equal(ctx, "",
+ reg_val_data_string(ctx, lp_iconv_convenience(ctx->lp_ctx), REG_BINARY, db),
+ "empty failed");
+ return true;
+}
+
+static bool test_reg_val_description(struct torture_context *ctx)
+{
+ DATA_BLOB data;
+ convert_string_talloc_convenience(ctx, lp_iconv_convenience(ctx->lp_ctx), CH_UTF8, CH_UTF16,
+ "stationary traveller",
+ strlen("stationary traveller"),
+ (void **)&data.data, &data.length, false);
+ torture_assert_str_equal(ctx, "camel = REG_SZ : stationary traveller",
+ reg_val_description(ctx, lp_iconv_convenience(ctx->lp_ctx), "camel", REG_SZ, data),
+ "reg_val_description failed");
+ return true;
+}
+
+
+static bool test_reg_val_description_nullname(struct torture_context *ctx)
+{
+ DATA_BLOB data;
+ convert_string_talloc_convenience(ctx, lp_iconv_convenience(ctx->lp_ctx), CH_UTF8, CH_UTF16,
+ "west berlin",
+ strlen("west berlin"),
+ (void **)&data.data, &data.length, false);
+ torture_assert_str_equal(ctx, "<No Name> = REG_SZ : west berlin",
+ reg_val_description(ctx, lp_iconv_convenience(ctx->lp_ctx), NULL, REG_SZ, data),
+ "description with null name failed");
+ return true;
+}
+
+struct torture_suite *torture_registry(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "REGISTRY");
+ torture_suite_add_simple_test(suite, "str_regtype",
+ test_str_regtype);
+ torture_suite_add_simple_test(suite, "reg_val_data_string dword",
+ test_reg_val_data_string_dword);
+ torture_suite_add_simple_test(suite, "reg_val_data_string sz",
+ test_reg_val_data_string_sz);
+ torture_suite_add_simple_test(suite, "reg_val_data_string binary",
+ test_reg_val_data_string_binary);
+ torture_suite_add_simple_test(suite, "reg_val_data_string empty",
+ test_reg_val_data_string_empty);
+ torture_suite_add_simple_test(suite, "reg_val_description",
+ test_reg_val_description);
+ torture_suite_add_simple_test(suite, "reg_val_description null",
+ test_reg_val_description_nullname);
+
+ torture_suite_add_suite(suite, torture_registry_hive(suite));
+ torture_suite_add_suite(suite, torture_registry_registry(suite));
+ torture_suite_add_suite(suite, torture_registry_diff(suite));
+
+ return suite;
+}
diff --git a/source4/lib/registry/tests/hive.c b/source4/lib/registry/tests/hive.c
new file mode 100644
index 0000000000..edc97c2468
--- /dev/null
+++ b/source4/lib/registry/tests/hive.c
@@ -0,0 +1,493 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ local testing of registry library - hives
+
+ Copyright (C) Jelmer Vernooij 2005-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 "lib/registry/registry.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/winreg.h"
+#include "system/filesys.h"
+#include "param/param.h"
+#include "libcli/security/security.h"
+
+static bool test_del_nonexistant_key(struct torture_context *tctx,
+ const void *test_data)
+{
+ const struct hive_key *root = (const struct hive_key *)test_data;
+ WERROR error = hive_key_del(root, "bla");
+ torture_assert_werr_equal(tctx, error, WERR_BADFILE,
+ "invalid return code");
+
+ return true;
+}
+
+static bool test_keyinfo_root(struct torture_context *tctx,
+ const void *test_data)
+{
+ uint32_t num_subkeys, num_values;
+ const struct hive_key *root = (const struct hive_key *)test_data;
+ WERROR error;
+
+ /* This is a new backend. There should be no subkeys and no
+ * values */
+ error = hive_key_get_info(tctx, root, NULL, &num_subkeys, &num_values,
+ NULL, NULL, NULL, NULL);
+ torture_assert_werr_ok(tctx, error, "reg_key_num_subkeys()");
+
+ torture_assert_int_equal(tctx, num_subkeys, 0,
+ "New key has non-zero subkey count");
+
+ torture_assert_werr_ok(tctx, error, "reg_key_num_values");
+
+ torture_assert_int_equal(tctx, num_values, 0,
+ "New key has non-zero value count");
+
+ return true;
+}
+
+static bool test_keyinfo_nums(struct torture_context *tctx, void *test_data)
+{
+ uint32_t num_subkeys, num_values;
+ struct hive_key *root = (struct hive_key *)test_data;
+ WERROR error;
+ struct hive_key *subkey;
+ char data[4];
+ SIVAL(data, 0, 42);
+
+ error = hive_key_add_name(tctx, root, "Nested Keyll", NULL,
+ NULL, &subkey);
+ torture_assert_werr_ok(tctx, error, "hive_key_add_name");
+
+ error = hive_key_set_value(root, "Answer", REG_DWORD,
+ data_blob_talloc(tctx, data, sizeof(data)));
+ torture_assert_werr_ok(tctx, error, "hive_key_set_value");
+
+ /* This is a new backend. There should be no subkeys and no
+ * values */
+ error = hive_key_get_info(tctx, root, NULL, &num_subkeys, &num_values,
+ NULL, NULL, NULL, NULL);
+ torture_assert_werr_ok(tctx, error, "reg_key_num_subkeys()");
+
+ torture_assert_int_equal(tctx, num_subkeys, 1, "subkey count");
+
+ torture_assert_werr_ok(tctx, error, "reg_key_num_values");
+
+ torture_assert_int_equal(tctx, num_values, 1, "value count");
+
+ return true;
+}
+
+static bool test_add_subkey(struct torture_context *tctx,
+ const void *test_data)
+{
+ WERROR error;
+ struct hive_key *subkey;
+ const struct hive_key *root = (const struct hive_key *)test_data;
+ TALLOC_CTX *mem_ctx = tctx;
+
+ error = hive_key_add_name(mem_ctx, root, "Nested Key", NULL,
+ NULL, &subkey);
+ torture_assert_werr_ok(tctx, error, "hive_key_add_name");
+
+ error = hive_key_del(root, "Nested Key");
+ torture_assert_werr_ok(tctx, error, "reg_key_del");
+
+ return true;
+}
+
+static bool test_del_recursive(struct torture_context *tctx,
+ const void *test_data)
+{
+ WERROR error;
+ struct hive_key *subkey;
+ struct hive_key *subkey2;
+ const struct hive_key *root = (const struct hive_key *)test_data;
+ TALLOC_CTX *mem_ctx = tctx;
+ char data[4];
+ SIVAL(data, 0, 42);
+
+ /* Create a new key under the root */
+ error = hive_key_add_name(mem_ctx, root, "Parent Key", NULL,
+ NULL, &subkey);
+ torture_assert_werr_ok(tctx, error, "hive_key_add_name");
+
+ /* Create a new key under "Parent Key" */
+ error = hive_key_add_name(mem_ctx, subkey, "Child Key", NULL,
+ NULL, &subkey2);
+ torture_assert_werr_ok(tctx, error, "hive_key_add_name");
+
+ /* Create a new value under "Child Key" */
+ error = hive_key_set_value(subkey2, "Answer Recursive", REG_DWORD,
+ data_blob_talloc(mem_ctx, data, sizeof(data)));
+ torture_assert_werr_ok(tctx, error, "hive_key_set_value");
+
+ /* Deleting "Parent Key" will also delete "Child Key" and the value. */
+ error = hive_key_del(root, "Parent Key");
+ torture_assert_werr_ok(tctx, error, "hive_key_del");
+
+ return true;
+}
+
+static bool test_flush_key(struct torture_context *tctx, void *test_data)
+{
+ struct hive_key *root = (struct hive_key *)test_data;
+
+ torture_assert_werr_ok(tctx, hive_key_flush(root), "flush key");
+
+ return true;
+}
+
+static bool test_del_key(struct torture_context *tctx, const void *test_data)
+{
+ WERROR error;
+ struct hive_key *subkey;
+ const struct hive_key *root = (const struct hive_key *)test_data;
+ TALLOC_CTX *mem_ctx = tctx;
+
+ error = hive_key_add_name(mem_ctx, root, "Nested Key", NULL,
+ NULL, &subkey);
+ torture_assert_werr_ok(tctx, error, "hive_key_add_name");
+
+ error = hive_key_del(root, "Nested Key");
+ torture_assert_werr_ok(tctx, error, "reg_key_del");
+
+ error = hive_key_del(root, "Nested Key");
+ torture_assert_werr_equal(tctx, error, WERR_BADFILE, "reg_key_del");
+
+ return true;
+}
+
+static bool test_set_value(struct torture_context *tctx,
+ const void *test_data)
+{
+ WERROR error;
+ struct hive_key *subkey;
+ const struct hive_key *root = (const struct hive_key *)test_data;
+ TALLOC_CTX *mem_ctx = tctx;
+ char data[4];
+ SIVAL(data, 0, 42);
+
+ error = hive_key_add_name(mem_ctx, root, "YA Nested Key", NULL,
+ NULL, &subkey);
+ torture_assert_werr_ok(tctx, error, "hive_key_add_name");
+
+ error = hive_key_set_value(subkey, "Answer", REG_DWORD,
+ data_blob_talloc(mem_ctx, data, sizeof(data)));
+ torture_assert_werr_ok(tctx, error, "hive_key_set_value");
+
+ return true;
+}
+
+static bool test_get_value(struct torture_context *tctx, const void *test_data)
+{
+ WERROR error;
+ struct hive_key *subkey;
+ const struct hive_key *root = (const struct hive_key *)test_data;
+ TALLOC_CTX *mem_ctx = tctx;
+ char data[4];
+ uint32_t type;
+ DATA_BLOB value;
+
+ SIVAL(data, 0, 42);
+
+ error = hive_key_add_name(mem_ctx, root, "EYA Nested Key", NULL,
+ NULL, &subkey);
+ torture_assert_werr_ok(tctx, error, "hive_key_add_name");
+
+ error = hive_get_value(mem_ctx, subkey, "Answer", &type, &value);
+ torture_assert_werr_equal(tctx, error, WERR_BADFILE,
+ "getting missing value");
+
+ error = hive_key_set_value(subkey, "Answer", REG_DWORD,
+ data_blob_talloc(mem_ctx, data, sizeof(data)));
+ torture_assert_werr_ok(tctx, error, "hive_key_set_value");
+
+ error = hive_get_value(mem_ctx, subkey, "Answer", &type, &value);
+ torture_assert_werr_ok(tctx, error, "getting value");
+
+ torture_assert_int_equal(tctx, value.length, 4, "value length");
+ torture_assert_int_equal(tctx, type, REG_DWORD, "value type");
+
+ torture_assert_mem_equal(tctx, &data, value.data, sizeof(uint32_t),
+ "value data");
+
+ return true;
+}
+
+static bool test_del_value(struct torture_context *tctx, const void *test_data)
+{
+ WERROR error;
+ struct hive_key *subkey;
+ const struct hive_key *root = (const struct hive_key *)test_data;
+ TALLOC_CTX *mem_ctx = tctx;
+ char data[4];
+ uint32_t type;
+ DATA_BLOB value;
+
+ SIVAL(data, 0, 42);
+
+ error = hive_key_add_name(mem_ctx, root, "EEYA Nested Key", NULL,
+ NULL, &subkey);
+ torture_assert_werr_ok(tctx, error, "hive_key_add_name");
+
+ error = hive_key_set_value(subkey, "Answer", REG_DWORD,
+ data_blob_talloc(mem_ctx, data, sizeof(data)));
+ torture_assert_werr_ok(tctx, error, "hive_key_set_value");
+
+ error = hive_key_del_value(subkey, "Answer");
+ torture_assert_werr_ok(tctx, error, "deleting value");
+
+ error = hive_get_value(mem_ctx, subkey, "Answer", &type, &value);
+ torture_assert_werr_equal(tctx, error, WERR_BADFILE, "getting value");
+
+ error = hive_key_del_value(subkey, "Answer");
+ torture_assert_werr_equal(tctx, error, WERR_BADFILE,
+ "deleting value");
+
+ return true;
+}
+
+static bool test_list_values(struct torture_context *tctx,
+ const void *test_data)
+{
+ WERROR error;
+ struct hive_key *subkey;
+ const struct hive_key *root = (const struct hive_key *)test_data;
+ TALLOC_CTX *mem_ctx = tctx;
+ char data[4];
+ uint32_t type;
+ DATA_BLOB value;
+ const char *name;
+ int data_val = 42;
+ SIVAL(data, 0, data_val);
+
+ error = hive_key_add_name(mem_ctx, root, "AYAYA Nested Key", NULL,
+ NULL, &subkey);
+ torture_assert_werr_ok(tctx, error, "hive_key_add_name");
+
+ error = hive_key_set_value(subkey, "Answer", REG_DWORD,
+ data_blob_talloc(mem_ctx, data, sizeof(data)));
+ torture_assert_werr_ok(tctx, error, "hive_key_set_value");
+
+ error = hive_get_value_by_index(mem_ctx, subkey, 0, &name,
+ &type, &value);
+ torture_assert_werr_ok(tctx, error, "getting value");
+
+ torture_assert_str_equal(tctx, name, "Answer", "value name");
+
+ torture_assert_int_equal(tctx, value.length, 4, "value length");
+ torture_assert_int_equal(tctx, type, REG_DWORD, "value type");
+
+
+ torture_assert_int_equal(tctx, data_val, IVAL(value.data, 0), "value data");
+
+ error = hive_get_value_by_index(mem_ctx, subkey, 1, &name,
+ &type, &value);
+ torture_assert_werr_equal(tctx, error, WERR_NO_MORE_ITEMS,
+ "getting missing value");
+
+ return true;
+}
+
+static bool test_hive_security(struct torture_context *tctx, const void *_data)
+{
+ struct hive_key *subkey = NULL;
+ const struct hive_key *root = _data;
+ WERROR error;
+ struct security_descriptor *osd, *nsd;
+
+ osd = 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,
+ NULL);
+
+
+ error = hive_key_add_name(tctx, root, "SecurityKey", NULL,
+ osd, &subkey);
+ torture_assert_werr_ok(tctx, error, "hive_key_add_name");
+
+ error = hive_get_sec_desc(tctx, subkey, &nsd);
+ torture_assert_werr_ok (tctx, error, "getting security descriptor");
+
+ torture_assert(tctx, security_descriptor_equal(osd, nsd),
+ "security descriptor changed!");
+
+ /* Create a fresh security descriptor */
+ talloc_free(osd);
+ osd = 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,
+ NULL);
+
+ error = hive_set_sec_desc(subkey, osd);
+ torture_assert_werr_ok(tctx, error, "setting security descriptor");
+
+ error = hive_get_sec_desc(tctx, subkey, &nsd);
+ torture_assert_werr_ok (tctx, error, "getting security descriptor");
+
+ torture_assert(tctx, security_descriptor_equal(osd, nsd),
+ "security descriptor changed!");
+
+ return true;
+}
+
+static void tcase_add_tests(struct torture_tcase *tcase)
+{
+ torture_tcase_add_simple_test_const(tcase, "del_nonexistant_key",
+ test_del_nonexistant_key);
+ torture_tcase_add_simple_test_const(tcase, "add_subkey",
+ test_add_subkey);
+ torture_tcase_add_simple_test(tcase, "flush_key",
+ test_flush_key);
+ /* test_del_recursive() test must run before test_keyinfo_root().
+ test_keyinfo_root() checks the number of subkeys, which verifies
+ the recursive delete worked properly. */
+ torture_tcase_add_simple_test_const(tcase, "del_recursive",
+ test_del_recursive);
+ torture_tcase_add_simple_test_const(tcase, "get_info",
+ test_keyinfo_root);
+ torture_tcase_add_simple_test(tcase, "get_info_nums",
+ test_keyinfo_nums);
+ torture_tcase_add_simple_test_const(tcase, "set_value",
+ test_set_value);
+ torture_tcase_add_simple_test_const(tcase, "get_value",
+ test_get_value);
+ torture_tcase_add_simple_test_const(tcase, "list_values",
+ test_list_values);
+ torture_tcase_add_simple_test_const(tcase, "del_key",
+ test_del_key);
+ torture_tcase_add_simple_test_const(tcase, "del_value",
+ test_del_value);
+ torture_tcase_add_simple_test_const(tcase, "check hive security",
+ test_hive_security);
+}
+
+static bool hive_setup_dir(struct torture_context *tctx, void **data)
+{
+ struct hive_key *key;
+ WERROR error;
+ char *dirname;
+ NTSTATUS status;
+
+ status = torture_temp_dir(tctx, "hive-dir", &dirname);
+ if (!NT_STATUS_IS_OK(status))
+ return false;
+
+ rmdir(dirname);
+
+ error = reg_create_directory(tctx, dirname, &key);
+ if (!W_ERROR_IS_OK(error)) {
+ fprintf(stderr, "Unable to initialize dir hive\n");
+ return false;
+ }
+
+ *data = key;
+
+ return true;
+}
+
+static bool hive_setup_ldb(struct torture_context *tctx, void **data)
+{
+ struct hive_key *key;
+ WERROR error;
+ char *dirname;
+ NTSTATUS status;
+
+ status = torture_temp_dir(tctx, "hive-ldb", &dirname);
+ if (!NT_STATUS_IS_OK(status))
+ return false;
+
+ rmdir(dirname);
+
+ error = reg_open_ldb_file(tctx, dirname, NULL, NULL, tctx->ev, tctx->lp_ctx, &key);
+ if (!W_ERROR_IS_OK(error)) {
+ fprintf(stderr, "Unable to initialize ldb hive\n");
+ return false;
+ }
+
+ *data = key;
+
+ return true;
+}
+
+static bool hive_setup_regf(struct torture_context *tctx, void **data)
+{
+ struct hive_key *key;
+ WERROR error;
+ char *dirname;
+ NTSTATUS status;
+
+ status = torture_temp_dir(tctx, "hive-regf", &dirname);
+ if (!NT_STATUS_IS_OK(status))
+ return false;
+
+ rmdir(dirname);
+
+ error = reg_create_regf_file(tctx, lp_iconv_convenience(tctx->lp_ctx),
+ dirname, 5, &key);
+ if (!W_ERROR_IS_OK(error)) {
+ fprintf(stderr, "Unable to create new regf file\n");
+ return false;
+ }
+
+ *data = key;
+
+ return true;
+}
+
+static bool test_dir_refuses_null_location(struct torture_context *tctx)
+{
+ torture_assert_werr_equal(tctx, WERR_INVALID_PARAM,
+ reg_open_directory(NULL, NULL, NULL),
+ "reg_open_directory accepts NULL location");
+ return true;
+}
+
+struct torture_suite *torture_registry_hive(TALLOC_CTX *mem_ctx)
+{
+ struct torture_tcase *tcase;
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "HIVE");
+
+ torture_suite_add_simple_test(suite, "dir-refuses-null-location",
+ test_dir_refuses_null_location);
+
+ tcase = torture_suite_add_tcase(suite, "dir");
+ torture_tcase_set_fixture(tcase, hive_setup_dir, NULL);
+ tcase_add_tests(tcase);
+
+ tcase = torture_suite_add_tcase(suite, "ldb");
+ torture_tcase_set_fixture(tcase, hive_setup_ldb, NULL);
+ tcase_add_tests(tcase);
+
+ tcase = torture_suite_add_tcase(suite, "regf");
+ torture_tcase_set_fixture(tcase, hive_setup_regf, NULL);
+ tcase_add_tests(tcase);
+
+ return suite;
+}
diff --git a/source4/lib/registry/tests/registry.c b/source4/lib/registry/tests/registry.c
new file mode 100644
index 0000000000..7274bf009d
--- /dev/null
+++ b/source4/lib/registry/tests/registry.c
@@ -0,0 +1,594 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ local testing of registry library - registry backend
+
+ Copyright (C) Jelmer Vernooij 2005-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 "lib/registry/registry.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/winreg.h"
+#include "libcli/security/security.h"
+#include "system/filesys.h"
+
+/**
+ * Test obtaining a predefined key.
+ */
+static bool test_get_predefined(struct torture_context *tctx, void *_data)
+{
+ struct registry_context *rctx = (struct registry_context *)_data;
+ struct registry_key *root;
+ WERROR error;
+
+ error = reg_get_predefined_key(rctx, HKEY_CLASSES_ROOT, &root);
+ torture_assert_werr_ok(tctx, error,
+ "getting predefined key failed");
+ return true;
+}
+
+/**
+ * Test obtaining a predefined key.
+ */
+static bool test_get_predefined_unknown(struct torture_context *tctx,
+ void *_data)
+{
+ struct registry_context *rctx = _data;
+ struct registry_key *root;
+ WERROR error;
+
+ error = reg_get_predefined_key(rctx, 1337, &root);
+ torture_assert_werr_equal(tctx, error, WERR_BADFILE,
+ "getting predefined key failed");
+ return true;
+}
+
+static bool test_predef_key_by_name(struct torture_context *tctx, void *_data)
+{
+ struct registry_context *rctx = (struct registry_context *)_data;
+ struct registry_key *root;
+ WERROR error;
+
+ error = reg_get_predefined_key_by_name(rctx, "HKEY_CLASSES_ROOT",
+ &root);
+ torture_assert_werr_ok(tctx, error,
+ "getting predefined key failed");
+
+ error = reg_get_predefined_key_by_name(rctx, "HKEY_classes_ROOT",
+ &root);
+ torture_assert_werr_ok(tctx, error,
+ "getting predefined key case insensitively failed");
+
+ return true;
+}
+
+static bool test_predef_key_by_name_invalid(struct torture_context *tctx,
+ void *_data)
+{
+ struct registry_context *rctx = (struct registry_context *)_data;
+ struct registry_key *root;
+ WERROR error;
+
+ error = reg_get_predefined_key_by_name(rctx, "BLA", &root);
+ torture_assert_werr_equal(tctx, error, WERR_BADFILE,
+ "getting predefined key failed");
+ return true;
+}
+
+/**
+ * Test creating a new subkey
+ */
+static bool test_create_subkey(struct torture_context *tctx, void *_data)
+{
+ struct registry_context *rctx = (struct registry_context *)_data;
+ struct registry_key *root, *newkey;
+ WERROR error;
+
+ error = reg_get_predefined_key(rctx, HKEY_CLASSES_ROOT, &root);
+ torture_assert_werr_ok(tctx, error,
+ "getting predefined key failed");
+
+ error = reg_key_add_name(rctx, root, "Bad Bentheim", NULL, NULL,
+ &newkey);
+ torture_assert_werr_ok(tctx, error, "Creating key return code");
+ torture_assert(tctx, newkey != NULL, "Creating new key");
+
+ return true;
+}
+
+/**
+ * Test creating a new nested subkey
+ */
+static bool test_create_nested_subkey(struct torture_context *tctx, void *_data)
+{
+ struct registry_context *rctx = (struct registry_context *)_data;
+ struct registry_key *root, *newkey1, *newkey2;
+ WERROR error;
+
+ error = reg_get_predefined_key(rctx, HKEY_CLASSES_ROOT, &root);
+ torture_assert_werr_ok(tctx, error,
+ "getting predefined key failed");
+
+ error = reg_key_add_name(rctx, root, "Hamburg", NULL, NULL,
+ &newkey1);
+ torture_assert_werr_ok(tctx, error, "Creating key return code");
+ torture_assert(tctx, newkey1 != NULL, "Creating new key");
+
+ error = reg_key_add_name(rctx, root, "Hamburg\\Hamburg", NULL, NULL,
+ &newkey2);
+ torture_assert_werr_ok(tctx, error, "Creating key return code");
+ torture_assert(tctx, newkey2 != NULL, "Creating new key");
+
+ return true;
+}
+
+/**
+ * Test creating a new subkey
+ */
+static bool test_key_add_abs_top(struct torture_context *tctx, void *_data)
+{
+ struct registry_context *rctx = (struct registry_context *)_data;
+ struct registry_key *root;
+ WERROR error;
+
+ error = reg_key_add_abs(tctx, rctx, "HKEY_CLASSES_ROOT", 0, NULL,
+ &root);
+ torture_assert_werr_equal(tctx, error, WERR_ALREADY_EXISTS,
+ "create top level");
+
+ return true;
+}
+
+/**
+ * Test creating a new subkey
+ */
+static bool test_key_add_abs(struct torture_context *tctx, void *_data)
+{
+ WERROR error;
+ struct registry_context *rctx = (struct registry_context *)_data;
+ struct registry_key *root, *result1, *result2;
+
+ error = reg_key_add_abs(tctx, rctx, "HKEY_CLASSES_ROOT\\bloe", 0, NULL,
+ &result1);
+ torture_assert_werr_ok(tctx, error, "create lowest");
+
+ error = reg_key_add_abs(tctx, rctx, "HKEY_CLASSES_ROOT\\bloe\\bla", 0,
+ NULL, &result1);
+ torture_assert_werr_ok(tctx, error, "create nested");
+
+ error = reg_get_predefined_key(rctx, HKEY_CLASSES_ROOT, &root);
+ torture_assert_werr_ok(tctx, error,
+ "getting predefined key failed");
+
+ error = reg_open_key(tctx, root, "bloe", &result2);
+ torture_assert_werr_ok(tctx, error, "opening key");
+
+ error = reg_open_key(tctx, root, "bloe\\bla", &result2);
+ torture_assert_werr_ok(tctx, error, "opening key");
+
+ return true;
+}
+
+
+static bool test_del_key(struct torture_context *tctx, void *_data)
+{
+ struct registry_context *rctx = (struct registry_context *)_data;
+ struct registry_key *root, *newkey;
+ WERROR error;
+
+ error = reg_get_predefined_key(rctx, HKEY_CLASSES_ROOT, &root);
+ torture_assert_werr_ok(tctx, error,
+ "getting predefined key failed");
+
+ error = reg_key_add_name(rctx, root, "Polen", NULL, NULL, &newkey);
+
+ torture_assert_werr_ok(tctx, error, "Creating key return code");
+ torture_assert(tctx, newkey != NULL, "Creating new key");
+
+ error = reg_key_del(root, "Polen");
+ torture_assert_werr_ok(tctx, error, "Delete key");
+
+ error = reg_key_del(root, "Polen");
+ torture_assert_werr_equal(tctx, error, WERR_BADFILE,
+ "Delete missing key");
+
+ return true;
+}
+
+/**
+ * Convenience function for opening the HKEY_CLASSES_ROOT hive and
+ * creating a single key for testing purposes.
+ */
+static bool create_test_key(struct torture_context *tctx,
+ struct registry_context *rctx,
+ const char *name,
+ struct registry_key **root,
+ struct registry_key **subkey)
+{
+ WERROR error;
+
+ error = reg_get_predefined_key(rctx, HKEY_CLASSES_ROOT, root);
+ torture_assert_werr_ok(tctx, error,
+ "getting predefined key failed");
+
+ error = reg_key_add_name(rctx, *root, name, NULL, NULL, subkey);
+ torture_assert_werr_ok(tctx, error, "Creating key return code");
+
+ return true;
+}
+
+
+static bool test_flush_key(struct torture_context *tctx, void *_data)
+{
+ struct registry_context *rctx = (struct registry_context *)_data;
+ struct registry_key *root, *subkey;
+ WERROR error;
+
+ if (!create_test_key(tctx, rctx, "Bremen", &root, &subkey))
+ return false;
+
+ error = reg_key_flush(subkey);
+ torture_assert_werr_ok(tctx, error, "flush key");
+
+ torture_assert_werr_equal(tctx, reg_key_flush(NULL),
+ WERR_INVALID_PARAM, "flush key");
+
+ return true;
+}
+
+static bool test_query_key(struct torture_context *tctx, void *_data)
+{
+ struct registry_context *rctx = (struct registry_context *)_data;
+ struct registry_key *root, *subkey;
+ WERROR error;
+ NTTIME last_changed_time;
+ uint32_t num_subkeys, num_values;
+ const char *classname;
+
+ if (!create_test_key(tctx, rctx, "Munchen", &root, &subkey))
+ return false;
+
+ error = reg_key_get_info(tctx, subkey, &classname,
+ &num_subkeys, &num_values,
+ &last_changed_time, NULL, NULL, NULL);
+
+ torture_assert_werr_ok(tctx, error, "get info key");
+ torture_assert(tctx, classname == NULL, "classname");
+ torture_assert_int_equal(tctx, num_subkeys, 0, "num subkeys");
+ torture_assert_int_equal(tctx, num_values, 0, "num values");
+
+ return true;
+}
+
+static bool test_query_key_nums(struct torture_context *tctx, void *_data)
+{
+ struct registry_context *rctx = (struct registry_context *)_data;
+ struct registry_key *root, *subkey1, *subkey2;
+ WERROR error;
+ uint32_t num_subkeys, num_values;
+ char data[4];
+ SIVAL(data, 0, 42);
+
+ if (!create_test_key(tctx, rctx, "Berlin", &root, &subkey1))
+ return false;
+
+ error = reg_key_add_name(rctx, subkey1, "Bentheim", NULL, NULL,
+ &subkey2);
+ torture_assert_werr_ok(tctx, error, "Creating key return code");
+
+ error = reg_val_set(subkey1, "Answer", REG_DWORD,
+ data_blob_talloc(tctx, &data, sizeof(data)));
+ torture_assert_werr_ok(tctx, error, "set value");
+
+ error = reg_key_get_info(tctx, subkey1, NULL, &num_subkeys,
+ &num_values, NULL, NULL, NULL, NULL);
+
+ torture_assert_werr_ok(tctx, error, "get info key");
+ torture_assert_int_equal(tctx, num_subkeys, 1, "num subkeys");
+ torture_assert_int_equal(tctx, num_values, 1, "num values");
+
+ return true;
+}
+
+/**
+ * Test that the subkeys of a key can be enumerated, that
+ * the returned parameters for get_subkey_by_index are optional and
+ * that enumerating the parents of a non-top-level node works.
+ */
+static bool test_list_subkeys(struct torture_context *tctx, void *_data)
+{
+ struct registry_context *rctx = (struct registry_context *)_data;
+ struct registry_key *subkey = NULL, *root;
+ WERROR error;
+ NTTIME last_mod_time;
+ const char *classname, *name;
+
+ if (!create_test_key(tctx, rctx, "Goettingen", &root, &subkey))
+ return false;
+
+ error = reg_key_get_subkey_by_index(tctx, root, 0, &name, &classname,
+ &last_mod_time);
+
+ torture_assert_werr_ok(tctx, error, "Enum keys return code");
+ torture_assert_str_equal(tctx, name, "Goettingen", "Enum keys data");
+
+
+ error = reg_key_get_subkey_by_index(tctx, root, 0, NULL, NULL, NULL);
+
+ torture_assert_werr_ok(tctx, error,
+ "Enum keys with NULL arguments return code");
+
+ error = reg_key_get_subkey_by_index(tctx, root, 1, NULL, NULL, NULL);
+
+ torture_assert_werr_equal(tctx, error, WERR_NO_MORE_ITEMS,
+ "Invalid error for no more items");
+
+ error = reg_key_get_subkey_by_index(tctx, subkey, 0, NULL, NULL, NULL);
+
+ torture_assert_werr_equal(tctx, error, WERR_NO_MORE_ITEMS,
+ "Invalid error for no more items");
+
+ return true;
+}
+
+/**
+ * Test setting a value
+ */
+static bool test_set_value(struct torture_context *tctx, void *_data)
+{
+ struct registry_context *rctx = (struct registry_context *)_data;
+ struct registry_key *subkey = NULL, *root;
+ WERROR error;
+ char data[4];
+
+ SIVAL(data, 0, 42);
+
+ if (!create_test_key(tctx, rctx, "Dusseldorf", &root, &subkey))
+ return false;
+
+ error = reg_val_set(subkey, "Answer", REG_DWORD,
+ data_blob_talloc(tctx, data, sizeof(data)));
+ torture_assert_werr_ok (tctx, error, "setting value");
+
+ return true;
+}
+
+/**
+ * Test getting/setting security descriptors
+ */
+static bool test_security(struct torture_context *tctx, void *_data)
+{
+ struct registry_context *rctx = (struct registry_context *)_data;
+ struct registry_key *subkey = NULL, *root;
+ WERROR error;
+ struct security_descriptor *osd, *nsd;
+
+ if (!create_test_key(tctx, rctx, "Düsseldorf", &root, &subkey))
+ return false;
+
+ osd = 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,
+ NULL);
+
+ error = reg_set_sec_desc(subkey, osd);
+ torture_assert_werr_ok(tctx, error, "setting security descriptor");
+
+ error = reg_get_sec_desc(tctx, subkey, &nsd);
+ torture_assert_werr_ok (tctx, error, "getting security descriptor");
+
+ torture_assert(tctx, security_descriptor_equal(osd, nsd),
+ "security descriptor changed!");
+
+ return true;
+}
+
+/**
+ * Test getting a value
+ */
+static bool test_get_value(struct torture_context *tctx, void *_data)
+{
+ struct registry_context *rctx = (struct registry_context *)_data;
+ struct registry_key *subkey = NULL, *root;
+ WERROR error;
+ DATA_BLOB data;
+ char value[4];
+ uint32_t type;
+ SIVAL(value, 0, 42);
+
+ if (!create_test_key(tctx, rctx, "Duisburg", &root, &subkey))
+ return false;
+
+ error = reg_key_get_value_by_name(tctx, subkey, __FUNCTION__, &type,
+ &data);
+ torture_assert_werr_equal(tctx, error, WERR_BADFILE,
+ "getting missing value");
+
+ error = reg_val_set(subkey, __FUNCTION__, REG_DWORD,
+ data_blob_talloc(tctx, value, sizeof(value)));
+ torture_assert_werr_ok(tctx, error, "setting value");
+
+ error = reg_key_get_value_by_name(tctx, subkey, __FUNCTION__, &type,
+ &data);
+ torture_assert_werr_ok(tctx, error, "getting value");
+
+ torture_assert_int_equal(tctx, sizeof(value), data.length, "value length ok");
+ torture_assert_mem_equal(tctx, data.data, value, sizeof(value),
+ "value content ok");
+ torture_assert_int_equal(tctx, REG_DWORD, type, "value type");
+
+ return true;
+}
+
+/**
+ * Test unsetting a value
+ */
+static bool test_del_value(struct torture_context *tctx, void *_data)
+{
+ struct registry_context *rctx =(struct registry_context *)_data;
+ struct registry_key *subkey = NULL, *root;
+ WERROR error;
+ DATA_BLOB data;
+ uint32_t type;
+ char value[4];
+ SIVAL(value, 0, 42);
+
+ if (!create_test_key(tctx, rctx, "Warschau", &root, &subkey))
+ return false;
+
+ error = reg_key_get_value_by_name(tctx, subkey, __FUNCTION__, &type,
+ &data);
+ torture_assert_werr_equal(tctx, error, WERR_BADFILE,
+ "getting missing value");
+
+ error = reg_val_set(subkey, __FUNCTION__, REG_DWORD,
+ data_blob_talloc(tctx, value, sizeof(value)));
+ torture_assert_werr_ok (tctx, error, "setting value");
+
+ error = reg_del_value(subkey, __FUNCTION__);
+ torture_assert_werr_ok (tctx, error, "unsetting value");
+
+ error = reg_key_get_value_by_name(tctx, subkey, __FUNCTION__,
+ &type, &data);
+ torture_assert_werr_equal(tctx, error, WERR_BADFILE,
+ "getting missing value");
+
+ return true;
+}
+
+/**
+ * Test listing values
+ */
+static bool test_list_values(struct torture_context *tctx, void *_data)
+{
+ struct registry_context *rctx = (struct registry_context *)_data;
+ struct registry_key *subkey = NULL, *root;
+ WERROR error;
+ DATA_BLOB data;
+ uint32_t type;
+ const char *name;
+ char value[4];
+ SIVAL(value, 0, 42);
+
+ if (!create_test_key(tctx, rctx, "Bonn", &root, &subkey))
+ return false;
+
+ error = reg_val_set(subkey, "bar", REG_DWORD,
+ data_blob_talloc(tctx, value, sizeof(value)));
+ torture_assert_werr_ok (tctx, error, "setting value");
+
+ error = reg_key_get_value_by_index(tctx, subkey, 0, &name,
+ &type, &data);
+ torture_assert_werr_ok(tctx, error, "getting value");
+
+ torture_assert_str_equal(tctx, name, "bar", "value name");
+ torture_assert_int_equal(tctx, sizeof(value), data.length, "value length");
+ torture_assert_mem_equal(tctx, data.data, value, sizeof(value),
+ "value content");
+ torture_assert_int_equal(tctx, REG_DWORD, type, "value type");
+
+ error = reg_key_get_value_by_index(tctx, subkey, 1, &name,
+ &type, &data);
+ torture_assert_werr_equal(tctx, error, WERR_NO_MORE_ITEMS,
+ "getting missing value");
+
+ return true;
+}
+
+static bool setup_local_registry(struct torture_context *tctx, void **data)
+{
+ struct registry_context *rctx;
+ WERROR error;
+ char *tempdir;
+ NTSTATUS status;
+ struct hive_key *hive_key;
+ const char *filename;
+
+ error = reg_open_local(tctx, &rctx);
+ torture_assert_werr_ok(tctx, error, "Opening local registry failed");
+
+ status = torture_temp_dir(tctx, "registry-local", &tempdir);
+ torture_assert_ntstatus_ok(tctx, status, "Creating temp dir failed");
+
+ filename = talloc_asprintf(tctx, "%s/classes_root.ldb", tempdir);
+ error = reg_open_ldb_file(tctx, filename, NULL, NULL, tctx->ev, tctx->lp_ctx, &hive_key);
+ torture_assert_werr_ok(tctx, error, "Opening classes_root file failed");
+
+ error = reg_mount_hive(rctx, hive_key, HKEY_CLASSES_ROOT, NULL);
+ torture_assert_werr_ok(tctx, error, "Mounting hive failed");
+
+ *data = rctx;
+
+ return true;
+}
+
+static void tcase_add_tests(struct torture_tcase *tcase)
+{
+ torture_tcase_add_simple_test(tcase, "list_subkeys",
+ test_list_subkeys);
+ torture_tcase_add_simple_test(tcase, "get_predefined_key",
+ test_get_predefined);
+ torture_tcase_add_simple_test(tcase, "get_predefined_key",
+ test_get_predefined_unknown);
+ torture_tcase_add_simple_test(tcase, "create_key",
+ test_create_subkey);
+ torture_tcase_add_simple_test(tcase, "create_key",
+ test_create_nested_subkey);
+ torture_tcase_add_simple_test(tcase, "key_add_abs",
+ test_key_add_abs);
+ torture_tcase_add_simple_test(tcase, "key_add_abs_top",
+ test_key_add_abs_top);
+ torture_tcase_add_simple_test(tcase, "set_value",
+ test_set_value);
+ torture_tcase_add_simple_test(tcase, "get_value",
+ test_get_value);
+ torture_tcase_add_simple_test(tcase, "list_values",
+ test_list_values);
+ torture_tcase_add_simple_test(tcase, "del_key",
+ test_del_key);
+ torture_tcase_add_simple_test(tcase, "del_value",
+ test_del_value);
+ torture_tcase_add_simple_test(tcase, "flush_key",
+ test_flush_key);
+ torture_tcase_add_simple_test(tcase, "query_key",
+ test_query_key);
+ torture_tcase_add_simple_test(tcase, "query_key_nums",
+ test_query_key_nums);
+ torture_tcase_add_simple_test(tcase, "test_predef_key_by_name",
+ test_predef_key_by_name);
+ torture_tcase_add_simple_test(tcase, "security",
+ test_security);
+ torture_tcase_add_simple_test(tcase,"test_predef_key_by_name_invalid",
+ test_predef_key_by_name_invalid);
+}
+
+struct torture_suite *torture_registry_registry(TALLOC_CTX *mem_ctx)
+{
+ struct torture_tcase *tcase;
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "REGISTRY");
+
+ tcase = torture_suite_add_tcase(suite, "local");
+ torture_tcase_set_fixture(tcase, setup_local_registry, NULL);
+ tcase_add_tests(tcase);
+
+ return suite;
+}
diff --git a/source4/lib/registry/tools/common.c b/source4/lib/registry/tools/common.c
new file mode 100644
index 0000000000..d997cb0fde
--- /dev/null
+++ b/source4/lib/registry/tools/common.c
@@ -0,0 +1,88 @@
+/*
+ Unix SMB/CIFS implementation.
+ Popt routines specifically for registry
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "auth/credentials/credentials.h"
+#include "lib/registry/registry.h"
+#include "lib/registry/tools/common.h"
+
+struct registry_context *reg_common_open_remote(const char *remote,
+ struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx,
+ struct cli_credentials *creds)
+{
+ struct registry_context *h = NULL;
+ WERROR error;
+
+ error = reg_open_remote(&h, NULL, creds, lp_ctx, remote, ev_ctx);
+
+ if (!W_ERROR_IS_OK(error)) {
+ fprintf(stderr, "Unable to open remote registry at %s:%s \n",
+ remote, win_errstr(error));
+ return NULL;
+ }
+
+ return h;
+}
+
+struct registry_key *reg_common_open_file(const char *path,
+ struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx,
+ struct cli_credentials *creds)
+{
+ struct hive_key *hive_root;
+ struct registry_context *h = NULL;
+ WERROR error;
+
+ error = reg_open_hive(ev_ctx, path, NULL, creds, ev_ctx, lp_ctx, &hive_root);
+
+ if(!W_ERROR_IS_OK(error)) {
+ fprintf(stderr, "Unable to open '%s': %s \n",
+ path, win_errstr(error));
+ return NULL;
+ }
+
+ error = reg_open_local(NULL, &h);
+ if (!W_ERROR_IS_OK(error)) {
+ fprintf(stderr, "Unable to initialize local registry: %s\n",
+ win_errstr(error));
+ return NULL;
+ }
+
+ return reg_import_hive_key(h, hive_root, -1, NULL);
+}
+
+struct registry_context *reg_common_open_local(struct cli_credentials *creds,
+ struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx)
+{
+ WERROR error;
+ struct registry_context *h = NULL;
+
+ error = reg_open_samba(NULL, &h, ev_ctx, lp_ctx, NULL, creds);
+
+ if(!W_ERROR_IS_OK(error)) {
+ fprintf(stderr, "Unable to open local registry:%s \n",
+ win_errstr(error));
+ return NULL;
+ }
+
+ return h;
+}
diff --git a/source4/lib/registry/tools/regdiff.c b/source4/lib/registry/tools/regdiff.c
new file mode 100644
index 0000000000..945b472903
--- /dev/null
+++ b/source4/lib/registry/tools/regdiff.c
@@ -0,0 +1,149 @@
+/*
+ Unix SMB/CIFS implementation.
+ simple registry frontend
+
+ Copyright (C) Jelmer Vernooij 2004-2007
+ Copyright (C) Wilco Baan Hofman 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/registry/registry.h"
+#include "lib/events/events.h"
+#include "lib/cmdline/popt_common.h"
+#include "lib/registry/tools/common.h"
+#include "param/param.h"
+
+enum reg_backend { REG_UNKNOWN, REG_LOCAL, REG_REMOTE, REG_NULL };
+
+static struct registry_context *open_backend(poptContext pc,
+ struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx,
+ enum reg_backend backend,
+ const char *remote_host)
+{
+ WERROR error;
+ struct registry_context *ctx;
+
+ switch (backend) {
+ case REG_UNKNOWN:
+ poptPrintUsage(pc, stderr, 0);
+ return NULL;
+ case REG_LOCAL:
+ error = reg_open_samba(NULL, &ctx, ev_ctx, lp_ctx, NULL, cmdline_credentials);
+ break;
+ case REG_REMOTE:
+ error = reg_open_remote(&ctx, NULL, cmdline_credentials, lp_ctx,
+ remote_host, ev_ctx);
+ break;
+ case REG_NULL:
+ error = reg_open_local(NULL, &ctx);
+ break;
+ }
+
+ if (!W_ERROR_IS_OK(error)) {
+ fprintf(stderr, "Error: %s\n", win_errstr(error));
+ return NULL;
+ }
+
+ return ctx;
+}
+
+int main(int argc, const char **argv)
+{
+ int opt;
+ poptContext pc;
+ char *outputfile = NULL;
+ enum reg_backend backend1 = REG_UNKNOWN, backend2 = REG_UNKNOWN;
+ const char *remote1 = NULL, *remote2 = NULL;
+ struct registry_context *h1 = NULL, *h2 = NULL;
+ WERROR error;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {"output", 'o', POPT_ARG_STRING, &outputfile, 0, "output file to use", NULL },
+ {"null", 'n', POPT_ARG_NONE, NULL, 'n', "Diff from NULL", NULL },
+ {"remote", 'R', POPT_ARG_STRING, NULL, 'R', "Connect to remote server" , NULL },
+ {"local", 'L', POPT_ARG_NONE, NULL, 'L', "Open local registry", NULL },
+ POPT_COMMON_SAMBA
+ POPT_COMMON_CREDENTIALS
+ POPT_COMMON_VERSION
+ { NULL }
+ };
+ TALLOC_CTX *ctx;
+ void *callback_data;
+ struct tevent_context *ev_ctx;
+ struct reg_diff_callbacks *callbacks;
+
+ ctx = talloc_init("regdiff");
+
+ pc = poptGetContext(argv[0], argc, argv, long_options,0);
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ error = WERR_OK;
+ switch(opt) {
+ case 'L':
+ if (backend1 == REG_UNKNOWN)
+ backend1 = REG_LOCAL;
+ else if (backend2 == REG_UNKNOWN)
+ backend2 = REG_LOCAL;
+ break;
+ case 'n':
+ if (backend1 == REG_UNKNOWN)
+ backend1 = REG_NULL;
+ else if (backend2 == REG_UNKNOWN)
+ backend2 = REG_NULL;
+ break;
+ case 'R':
+ if (backend1 == REG_UNKNOWN) {
+ backend1 = REG_REMOTE;
+ remote1 = poptGetOptArg(pc);
+ } else if (backend2 == REG_UNKNOWN) {
+ backend2 = REG_REMOTE;
+ remote2 = poptGetOptArg(pc);
+ }
+ break;
+ }
+
+ }
+
+ ev_ctx = s4_event_context_init(NULL);
+
+ h1 = open_backend(pc, ev_ctx, cmdline_lp_ctx, backend1, remote1);
+ if (h1 == NULL)
+ return 1;
+
+ h2 = open_backend(pc, ev_ctx, cmdline_lp_ctx, backend2, remote2);
+ if (h2 == NULL)
+ return 1;
+
+ poptFreeContext(pc);
+
+ error = reg_dotreg_diff_save(ctx, outputfile, lp_iconv_convenience(cmdline_lp_ctx), &callbacks,
+ &callback_data);
+ if (!W_ERROR_IS_OK(error)) {
+ fprintf(stderr, "Problem saving registry diff to '%s': %s\n",
+ outputfile, win_errstr(error));
+ return -1;
+ }
+
+ error = reg_generate_diff(h1, h2, callbacks, callback_data);
+ if (!W_ERROR_IS_OK(error)) {
+ fprintf(stderr, "Unable to generate diff between keys: %s\n",
+ win_errstr(error));
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/source4/lib/registry/tools/regpatch.c b/source4/lib/registry/tools/regpatch.c
new file mode 100644
index 0000000000..3f550e517e
--- /dev/null
+++ b/source4/lib/registry/tools/regpatch.c
@@ -0,0 +1,74 @@
+/*
+ Unix SMB/CIFS implementation.
+ simple registry frontend
+
+ Copyright (C) 2004-2007 Jelmer Vernooij, jelmer@samba.org
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/events/events.h"
+#include "lib/registry/registry.h"
+#include "lib/cmdline/popt_common.h"
+#include "lib/registry/tools/common.h"
+#include "param/param.h"
+#include "events/events.h"
+
+int main(int argc, char **argv)
+{
+ int opt;
+ poptContext pc;
+ const char *patch;
+ struct registry_context *h;
+ const char *file = NULL;
+ const char *remote = NULL;
+ struct tevent_context *ev_ctx;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {"remote", 'R', POPT_ARG_STRING, &remote, 0, "connect to specified remote server", NULL},
+ {"file", 'F', POPT_ARG_STRING, &file, 0, "file path", NULL },
+ POPT_COMMON_SAMBA
+ POPT_COMMON_CREDENTIALS
+ { NULL }
+ };
+
+ pc = poptGetContext(argv[0], argc, (const char **) argv, long_options,0);
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ }
+
+ ev_ctx = s4_event_context_init(NULL);
+
+ if (remote) {
+ h = reg_common_open_remote (remote, ev_ctx, cmdline_lp_ctx, cmdline_credentials);
+ } else {
+ h = reg_common_open_local (cmdline_credentials, ev_ctx, cmdline_lp_ctx);
+ }
+
+ if (h == NULL)
+ return 1;
+
+ patch = poptGetArg(pc);
+ if (patch == NULL) {
+ poptPrintUsage(pc, stderr, 0);
+ return 1;
+ }
+
+ poptFreeContext(pc);
+
+ reg_diff_apply(h, lp_iconv_convenience(cmdline_lp_ctx), patch);
+
+ return 0;
+}
diff --git a/source4/lib/registry/tools/regshell.c b/source4/lib/registry/tools/regshell.c
new file mode 100644
index 0000000000..0fc06d1219
--- /dev/null
+++ b/source4/lib/registry/tools/regshell.c
@@ -0,0 +1,582 @@
+/*
+ Unix SMB/CIFS implementation.
+ simple registry frontend
+
+ Copyright (C) Jelmer Vernooij 2004-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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/registry/registry.h"
+#include "lib/cmdline/popt_common.h"
+#include "lib/events/events.h"
+#include "system/time.h"
+#include "lib/smbreadline/smbreadline.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "lib/registry/tools/common.h"
+#include "param/param.h"
+
+struct regshell_context {
+ struct registry_context *registry;
+ const char *path;
+ struct registry_key *current;
+};
+
+/* *
+ * ck/cd - change key
+ * ls - list values/keys
+ * rmval/rm - remove value
+ * rmkey/rmdir - remove key
+ * mkkey/mkdir - make key
+ * ch - change hive
+ * info - show key info
+ * save - save hive
+ * print - print value
+ * help
+ * exit
+ */
+
+static WERROR cmd_info(struct regshell_context *ctx, int argc, char **argv)
+{
+ struct security_descriptor *sec_desc = NULL;
+ time_t last_mod;
+ WERROR error;
+ const char *classname = NULL;
+ NTTIME last_change;
+ uint32_t max_subkeynamelen;
+ uint32_t max_valnamelen;
+ uint32_t max_valbufsize;
+ uint32_t num_subkeys;
+ uint32_t num_values;
+
+ error = reg_key_get_info(ctx, ctx->current, &classname, &num_subkeys, &num_values,
+ &last_change, &max_subkeynamelen, &max_valnamelen, &max_valbufsize);
+ if (!W_ERROR_IS_OK(error)) {
+ printf("Error getting key info: %s\n", win_errstr(error));
+ return error;
+ }
+
+
+ printf("Name: %s\n", strchr(ctx->path, '\\')?strrchr(ctx->path, '\\')+1:
+ ctx->path);
+ printf("Full path: %s\n", ctx->path);
+ if (classname != NULL)
+ printf("Key Class: %s\n", classname);
+ last_mod = nt_time_to_unix(last_change);
+ printf("Time Last Modified: %s\n", ctime(&last_mod));
+ printf("Number of subkeys: %d\n", num_subkeys);
+ printf("Number of values: %d\n", num_values);
+
+ if (max_valnamelen > 0)
+ printf("Maximum value name length: %d\n", max_valnamelen);
+
+ if (max_valbufsize > 0)
+ printf("Maximum value data length: %d\n", max_valbufsize);
+
+ if (max_subkeynamelen > 0)
+ printf("Maximum sub key name length: %d\n", max_subkeynamelen);
+
+ error = reg_get_sec_desc(ctx, ctx->current, &sec_desc);
+ if (!W_ERROR_IS_OK(error)) {
+ printf("Error getting security descriptor\n");
+ return error;
+ }
+ ndr_print_debug((ndr_print_fn_t)ndr_print_security_descriptor,
+ "Security", sec_desc);
+ talloc_free(sec_desc);
+
+ return WERR_OK;
+}
+
+static WERROR cmd_predef(struct regshell_context *ctx, int argc, char **argv)
+{
+ struct registry_key *ret = NULL;
+ if (argc < 2) {
+ fprintf(stderr, "Usage: predef predefined-key-name\n");
+ } else if (!ctx) {
+ fprintf(stderr, "No full registry loaded, no predefined keys defined\n");
+ } else {
+ WERROR error = reg_get_predefined_key_by_name(ctx->registry,
+ argv[1], &ret);
+
+ if (!W_ERROR_IS_OK(error)) {
+ fprintf(stderr, "Error opening predefined key %s: %s\n",
+ argv[1], win_errstr(error));
+ return error;
+ }
+
+ ctx->path = strupper_talloc(ctx, argv[1]);
+ ctx->current = ret;
+ }
+
+ return WERR_OK;
+}
+
+static WERROR cmd_pwd(struct regshell_context *ctx,
+ int argc, char **argv)
+{
+ printf("%s\n", ctx->path);
+ return WERR_OK;
+}
+
+static WERROR cmd_set(struct regshell_context *ctx, int argc, char **argv)
+{
+ struct registry_value val;
+ WERROR error;
+
+ if (argc < 4) {
+ fprintf(stderr, "Usage: set value-name type value\n");
+ return WERR_INVALID_PARAM;
+ }
+
+ if (!reg_string_to_val(ctx, lp_iconv_convenience(cmdline_lp_ctx),
+ argv[2], argv[3], &val.data_type,
+ &val.data)) {
+ fprintf(stderr, "Unable to interpret data\n");
+ return WERR_INVALID_PARAM;
+ }
+
+ error = reg_val_set(ctx->current, argv[1], val.data_type, val.data);
+ if (!W_ERROR_IS_OK(error)) {
+ fprintf(stderr, "Error setting value: %s\n", win_errstr(error));
+ return error;
+ }
+
+ return WERR_OK;
+}
+
+static WERROR cmd_ck(struct regshell_context *ctx, int argc, char **argv)
+{
+ struct registry_key *nkey = NULL;
+ WERROR error;
+
+ if(argc == 2) {
+ error = reg_open_key(ctx->registry, ctx->current, argv[1],
+ &nkey);
+ if(!W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Error opening specified key: %s\n",
+ win_errstr(error)));
+ return error;
+ }
+
+ ctx->path = talloc_asprintf(ctx, "%s\\%s", ctx->path, argv[1]);
+ ctx->current = nkey;
+ }
+ printf("New path is: %s\n", ctx->path);
+
+ return WERR_OK;
+}
+
+static WERROR cmd_print(struct regshell_context *ctx, int argc, char **argv)
+{
+ uint32_t value_type;
+ DATA_BLOB value_data;
+ WERROR error;
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage: print <valuename>\n");
+ return WERR_INVALID_PARAM;
+ }
+
+ error = reg_key_get_value_by_name(ctx, ctx->current, argv[1],
+ &value_type, &value_data);
+ if (!W_ERROR_IS_OK(error)) {
+ fprintf(stderr, "No such value '%s'\n", argv[1]);
+ return error;
+ }
+
+ printf("%s\n%s\n", str_regtype(value_type),
+ reg_val_data_string(ctx, lp_iconv_convenience(cmdline_lp_ctx), value_type, value_data));
+
+ return WERR_OK;
+}
+
+static WERROR cmd_ls(struct regshell_context *ctx, int argc, char **argv)
+{
+ int i;
+ WERROR error;
+ uint32_t valuetype;
+ DATA_BLOB valuedata;
+ const char *name = NULL;
+
+ for (i = 0; W_ERROR_IS_OK(error = reg_key_get_subkey_by_index(ctx,
+ ctx->current,
+ i,
+ &name,
+ NULL,
+ NULL)); i++) {
+ printf("K %s\n", name);
+ }
+
+ if (!W_ERROR_EQUAL(error, WERR_NO_MORE_ITEMS)) {
+ fprintf(stderr, "Error occured while browsing thru keys: %s\n",
+ win_errstr(error));
+ return error;
+ }
+
+ for (i = 0; W_ERROR_IS_OK(error = reg_key_get_value_by_index(ctx,
+ ctx->current, i, &name, &valuetype, &valuedata)); i++)
+ printf("V \"%s\" %s %s\n", name, str_regtype(valuetype),
+ reg_val_data_string(ctx, lp_iconv_convenience(cmdline_lp_ctx), valuetype, valuedata));
+
+ return WERR_OK;
+}
+static WERROR cmd_mkkey(struct regshell_context *ctx, int argc, char **argv)
+{
+ struct registry_key *tmp;
+ WERROR error;
+
+ if(argc < 2) {
+ fprintf(stderr, "Usage: mkkey <keyname>\n");
+ return WERR_INVALID_PARAM;
+ }
+
+ error = reg_key_add_name(ctx, ctx->current, argv[1], 0, NULL, &tmp);
+
+ if (!W_ERROR_IS_OK(error)) {
+ fprintf(stderr, "Error adding new subkey '%s': %s\n", argv[1],
+ win_errstr(error));
+ return error;
+ }
+
+ return WERR_OK;
+}
+
+static WERROR cmd_rmkey(struct regshell_context *ctx,
+ int argc, char **argv)
+{
+ WERROR error;
+
+ if(argc < 2) {
+ fprintf(stderr, "Usage: rmkey <name>\n");
+ return WERR_INVALID_PARAM;
+ }
+
+ error = reg_key_del(ctx->current, argv[1]);
+ if(!W_ERROR_IS_OK(error)) {
+ fprintf(stderr, "Error deleting '%s'\n", argv[1]);
+ return error;
+ } else {
+ fprintf(stderr, "Successfully deleted '%s'\n", argv[1]);
+ }
+
+ return WERR_OK;
+}
+
+static WERROR cmd_rmval(struct regshell_context *ctx, int argc, char **argv)
+{
+ WERROR error;
+
+ if(argc < 2) {
+ fprintf(stderr, "Usage: rmval <valuename>\n");
+ return WERR_INVALID_PARAM;
+ }
+
+ error = reg_del_value(ctx->current, argv[1]);
+ if(!W_ERROR_IS_OK(error)) {
+ fprintf(stderr, "Error deleting value '%s'\n", argv[1]);
+ return error;
+ } else {
+ fprintf(stderr, "Successfully deleted value '%s'\n", argv[1]);
+ }
+
+ return WERR_OK;
+}
+
+_NORETURN_ static WERROR cmd_exit(struct regshell_context *ctx,
+ int argc, char **argv)
+{
+ exit(0);
+ return WERR_OK;
+}
+
+static WERROR cmd_help(struct regshell_context *ctx, int, char **);
+
+static struct {
+ const char *name;
+ const char *alias;
+ const char *help;
+ WERROR (*handle)(struct regshell_context *ctx, int argc, char **argv);
+} regshell_cmds[] = {
+ {"ck", "cd", "Change current key", cmd_ck },
+ {"info", "i", "Show detailed information of a key", cmd_info },
+ {"list", "ls", "List values/keys in current key", cmd_ls },
+ {"print", "p", "Print value", cmd_print },
+ {"mkkey", "mkdir", "Make new key", cmd_mkkey },
+ {"rmval", "rm", "Remove value", cmd_rmval },
+ {"rmkey", "rmdir", "Remove key", cmd_rmkey },
+ {"pwd", "pwk", "Printing current key", cmd_pwd },
+ {"set", "update", "Update value", cmd_set },
+ {"help", "?", "Help", cmd_help },
+ {"exit", "quit", "Exit", cmd_exit },
+ {"predef", "predefined", "Go to predefined key", cmd_predef },
+ {NULL }
+};
+
+static WERROR cmd_help(struct regshell_context *ctx,
+ int argc, char **argv)
+{
+ int i;
+ printf("Available commands:\n");
+ for(i = 0; regshell_cmds[i].name; i++) {
+ printf("%s - %s\n", regshell_cmds[i].name,
+ regshell_cmds[i].help);
+ }
+ return WERR_OK;
+}
+
+static WERROR process_cmd(struct regshell_context *ctx,
+ char *line)
+{
+ int argc;
+ char **argv = NULL;
+ int ret, i;
+
+ if ((ret = poptParseArgvString(line, &argc, (const char ***) &argv)) != 0) {
+ fprintf(stderr, "regshell: %s\n", poptStrerror(ret));
+ return WERR_INVALID_PARAM;
+ }
+
+ for(i = 0; regshell_cmds[i].name; i++) {
+ if(!strcmp(regshell_cmds[i].name, argv[0]) ||
+ (regshell_cmds[i].alias && !strcmp(regshell_cmds[i].alias, argv[0]))) {
+ return regshell_cmds[i].handle(ctx, argc, argv);
+ }
+ }
+
+ fprintf(stderr, "No such command '%s'\n", argv[0]);
+
+ return WERR_INVALID_PARAM;
+}
+
+#define MAX_COMPLETIONS 100
+
+static struct registry_key *current_key = NULL;
+
+static char **reg_complete_command(const char *text, int start, int end)
+{
+ /* Complete command */
+ char **matches;
+ int i, len, samelen=0, count=1;
+
+ matches = malloc_array_p(char *, MAX_COMPLETIONS);
+ if (!matches) return NULL;
+ matches[0] = NULL;
+
+ len = strlen(text);
+ for (i=0;regshell_cmds[i].handle && count < MAX_COMPLETIONS-1;i++) {
+ if (strncmp(text, regshell_cmds[i].name, len) == 0) {
+ matches[count] = strdup(regshell_cmds[i].name);
+ if (!matches[count])
+ goto cleanup;
+ if (count == 1)
+ samelen = strlen(matches[count]);
+ else
+ while (strncmp(matches[count], matches[count-1], samelen) != 0)
+ samelen--;
+ count++;
+ }
+ }
+
+ switch (count) {
+ case 0: /* should never happen */
+ case 1:
+ goto cleanup;
+ case 2:
+ matches[0] = strdup(matches[1]);
+ break;
+ default:
+ matches[0] = strndup(matches[1], samelen);
+ }
+ matches[count] = NULL;
+ return matches;
+
+cleanup:
+ count--;
+ while (count >= 0) {
+ free(matches[count]);
+ count--;
+ }
+ free(matches);
+ return NULL;
+}
+
+static char **reg_complete_key(const char *text, int start, int end)
+{
+ struct registry_key *base;
+ const char *subkeyname;
+ int i, j = 1;
+ int samelen = 0;
+ int len;
+ char **matches;
+ const char *base_n = "";
+ TALLOC_CTX *mem_ctx;
+ WERROR status;
+
+ matches = malloc_array_p(char *, MAX_COMPLETIONS);
+ if (!matches) return NULL;
+ matches[0] = NULL;
+ mem_ctx = talloc_init("completion");
+
+ base = current_key;
+
+ len = strlen(text);
+ for(i = 0; j < MAX_COMPLETIONS-1; i++) {
+ status = reg_key_get_subkey_by_index(mem_ctx, base, i,
+ &subkeyname, NULL, NULL);
+ if(W_ERROR_IS_OK(status)) {
+ if(!strncmp(text, subkeyname, len)) {
+ matches[j] = strdup(subkeyname);
+ j++;
+
+ if (j == 1)
+ samelen = strlen(matches[j]);
+ else
+ while (strncmp(matches[j], matches[j-1], samelen) != 0)
+ samelen--;
+ }
+ } else if(W_ERROR_EQUAL(status, WERR_NO_MORE_ITEMS)) {
+ break;
+ } else {
+ printf("Error creating completion list: %s\n",
+ win_errstr(status));
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+ }
+
+ if (j == 1) { /* No matches at all */
+ SAFE_FREE(matches);
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ if (j == 2) { /* Exact match */
+ asprintf(&matches[0], "%s%s", base_n, matches[1]);
+ } else {
+ asprintf(&matches[0], "%s%s", base_n,
+ talloc_strndup(mem_ctx, matches[1], samelen));
+ }
+ talloc_free(mem_ctx);
+
+ matches[j] = NULL;
+ return matches;
+}
+
+static char **reg_completion(const char *text, int start, int end)
+{
+ smb_readline_ca_char(' ');
+
+ if (start == 0) {
+ return reg_complete_command(text, start, end);
+ } else {
+ return reg_complete_key(text, start, end);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ int opt;
+ const char *file = NULL;
+ poptContext pc;
+ const char *remote = NULL;
+ struct regshell_context *ctx;
+ struct tevent_context *ev_ctx;
+ bool ret = true;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {"file", 'F', POPT_ARG_STRING, &file, 0, "open hive file", NULL },
+ {"remote", 'R', POPT_ARG_STRING, &remote, 0, "connect to specified remote server", NULL},
+ POPT_COMMON_SAMBA
+ POPT_COMMON_CREDENTIALS
+ POPT_COMMON_VERSION
+ { NULL }
+ };
+
+ pc = poptGetContext(argv[0], argc, (const char **) argv, long_options,0);
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ }
+
+ ctx = talloc_zero(NULL, struct regshell_context);
+
+ ev_ctx = s4_event_context_init(ctx);
+
+ if (remote != NULL) {
+ ctx->registry = reg_common_open_remote(remote, ev_ctx,
+ cmdline_lp_ctx, cmdline_credentials);
+ } else if (file != NULL) {
+ ctx->current = reg_common_open_file(file, ev_ctx, cmdline_lp_ctx, cmdline_credentials);
+ if (ctx->current == NULL)
+ return 1;
+ ctx->registry = ctx->current->context;
+ ctx->path = talloc_strdup(ctx, "");
+ } else {
+ ctx->registry = reg_common_open_local(cmdline_credentials, ev_ctx, cmdline_lp_ctx);
+ }
+
+ if (ctx->registry == NULL)
+ return 1;
+
+ if (ctx->current == NULL) {
+ int i;
+
+ for (i = 0; (reg_predefined_keys[i].handle != 0) &&
+ (ctx->current == NULL); i++) {
+ WERROR err;
+ err = reg_get_predefined_key(ctx->registry,
+ reg_predefined_keys[i].handle,
+ &ctx->current);
+ if (W_ERROR_IS_OK(err)) {
+ ctx->path = talloc_strdup(ctx,
+ reg_predefined_keys[i].name);
+ break;
+ } else {
+ ctx->current = NULL;
+ }
+ }
+ }
+
+ if (ctx->current == NULL) {
+ fprintf(stderr, "Unable to access any of the predefined keys\n");
+ return -1;
+ }
+
+ poptFreeContext(pc);
+
+ while (true) {
+ char *line, *prompt;
+
+ asprintf(&prompt, "%s> ", ctx->path);
+
+ current_key = ctx->current; /* No way to pass a void * pointer
+ via readline :-( */
+ line = smb_readline(prompt, NULL, reg_completion);
+
+ if (line == NULL) {
+ free(prompt);
+ break;
+ }
+
+ if (line[0] != '\n') {
+ ret = W_ERROR_IS_OK(process_cmd(ctx, line));
+ }
+ free(line);
+ free(prompt);
+ }
+ talloc_free(ctx);
+
+ return (ret?0:1);
+}
diff --git a/source4/lib/registry/tools/regtree.c b/source4/lib/registry/tools/regtree.c
new file mode 100644
index 0000000000..948ed49312
--- /dev/null
+++ b/source4/lib/registry/tools/regtree.c
@@ -0,0 +1,167 @@
+/*
+ Unix SMB/CIFS implementation.
+ simple registry frontend
+
+ Copyright (C) Jelmer Vernooij 2004-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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/registry/registry.h"
+#include "lib/registry/tools/common.h"
+#include "lib/events/events.h"
+#include "lib/cmdline/popt_common.h"
+#include "param/param.h"
+
+/**
+ * Print a registry key recursively
+ *
+ * @param level Level at which to print
+ * @param p Key to print
+ * @param fullpath Whether the full pat hshould be printed or just the last bit
+ * @param novals Whether values should not be printed
+ */
+static void print_tree(int level, struct registry_key *p,
+ const char *name,
+ bool fullpath, bool novals)
+{
+ struct registry_key *subkey;
+ const char *valuename, *keyname;
+ uint32_t valuetype;
+ DATA_BLOB valuedata;
+ struct security_descriptor *sec_desc;
+ WERROR error;
+ int i;
+ TALLOC_CTX *mem_ctx;
+
+ for(i = 0; i < level; i++) putchar(' '); puts(name);
+
+ mem_ctx = talloc_init("print_tree");
+ for (i = 0; W_ERROR_IS_OK(error = reg_key_get_subkey_by_index(mem_ctx,
+ p,
+ i,
+ &keyname,
+ NULL,
+ NULL)); i++) {
+
+ SMB_ASSERT(strlen(keyname) > 0);
+ if (!W_ERROR_IS_OK(reg_open_key(mem_ctx, p, keyname, &subkey)))
+ continue;
+
+ print_tree(level+1, subkey, (fullpath && strlen(name))?
+ talloc_asprintf(mem_ctx, "%s\\%s",
+ name, keyname):
+ keyname, fullpath, novals);
+ talloc_free(subkey);
+ }
+ talloc_free(mem_ctx);
+
+ if(!W_ERROR_EQUAL(error, WERR_NO_MORE_ITEMS)) {
+ DEBUG(0, ("Error occured while fetching subkeys for '%s': %s\n",
+ name, win_errstr(error)));
+ }
+
+ if (!novals) {
+ mem_ctx = talloc_init("print_tree");
+ for(i = 0; W_ERROR_IS_OK(error = reg_key_get_value_by_index(
+ mem_ctx, p, i, &valuename, &valuetype, &valuedata));
+ i++) {
+ int j;
+ for(j = 0; j < level+1; j++) putchar(' ');
+ printf("%s\n", reg_val_description(mem_ctx,
+ lp_iconv_convenience(cmdline_lp_ctx), valuename,
+ valuetype, valuedata));
+ }
+ talloc_free(mem_ctx);
+
+ if(!W_ERROR_EQUAL(error, WERR_NO_MORE_ITEMS)) {
+ DEBUG(0, ("Error occured while fetching values for '%s': %s\n",
+ name, win_errstr(error)));
+ }
+ }
+
+ mem_ctx = talloc_init("sec_desc");
+ if (NT_STATUS_IS_ERR(reg_get_sec_desc(mem_ctx, p, &sec_desc))) {
+ DEBUG(0, ("Error getting security descriptor\n"));
+ }
+ talloc_free(mem_ctx);
+}
+
+int main(int argc, char **argv)
+{
+ int opt, i;
+ const char *file = NULL;
+ const char *remote = NULL;
+ poptContext pc;
+ struct registry_context *h = NULL;
+ struct registry_key *start_key = NULL;
+ struct tevent_context *ev_ctx;
+ WERROR error;
+ bool fullpath = false, no_values = false;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {"file", 'F', POPT_ARG_STRING, &file, 0, "file path", NULL },
+ {"remote", 'R', POPT_ARG_STRING, &remote, 0, "connect to specified remote server", NULL },
+ {"fullpath", 'f', POPT_ARG_NONE, &fullpath, 0, "show full paths", NULL},
+ {"no-values", 'V', POPT_ARG_NONE, &no_values, 0, "don't show values", NULL},
+ POPT_COMMON_SAMBA
+ POPT_COMMON_CREDENTIALS
+ POPT_COMMON_VERSION
+ { NULL }
+ };
+
+ pc = poptGetContext(argv[0], argc, (const char **) argv, long_options,0);
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ }
+
+ ev_ctx = s4_event_context_init(NULL);
+
+ if (remote != NULL) {
+ h = reg_common_open_remote(remote, ev_ctx, cmdline_lp_ctx, cmdline_credentials);
+ } else if (file != NULL) {
+ start_key = reg_common_open_file(file, ev_ctx, cmdline_lp_ctx, cmdline_credentials);
+ } else {
+ h = reg_common_open_local(cmdline_credentials, ev_ctx, cmdline_lp_ctx);
+ }
+
+ if (h == NULL && start_key == NULL)
+ return 1;
+
+ poptFreeContext(pc);
+
+ error = WERR_OK;
+
+ if (start_key != NULL) {
+ print_tree(0, start_key, "", fullpath, no_values);
+ } else {
+ for(i = 0; reg_predefined_keys[i].handle; i++) {
+ error = reg_get_predefined_key(h,
+ reg_predefined_keys[i].handle,
+ &start_key);
+ if (!W_ERROR_IS_OK(error)) {
+ fprintf(stderr, "Skipping %s: %s\n",
+ reg_predefined_keys[i].name,
+ win_errstr(error));
+ continue;
+ }
+ SMB_ASSERT(start_key != NULL);
+ print_tree(0, start_key, reg_predefined_keys[i].name,
+ fullpath, no_values);
+ }
+ }
+
+ return 0;
+}
diff --git a/source4/lib/registry/util.c b/source4/lib/registry/util.c
new file mode 100644
index 0000000000..a1897eff2e
--- /dev/null
+++ b/source4/lib/registry/util.c
@@ -0,0 +1,250 @@
+/*
+ Unix SMB/CIFS implementation.
+ Transparent registry backend handling
+ Copyright (C) Jelmer Vernooij 2003-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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/registry/registry.h"
+#include "librpc/gen_ndr/winreg.h"
+
+/**
+ * @file
+ * @brief Registry utility functions
+ */
+
+static const struct {
+ uint32_t id;
+ const char *name;
+} reg_value_types[] = {
+ { REG_SZ, "REG_SZ" },
+ { REG_DWORD, "REG_DWORD" },
+ { REG_BINARY, "REG_BINARY" },
+ { REG_EXPAND_SZ, "REG_EXPAND_SZ" },
+ { REG_NONE, "REG_NONE" },
+ { 0, NULL }
+};
+
+/** Return string description of registry value type */
+_PUBLIC_ const char *str_regtype(int type)
+{
+ int i;
+ for (i = 0; reg_value_types[i].name; i++) {
+ if (reg_value_types[i].id == type)
+ return reg_value_types[i].name;
+ }
+
+ return "Unknown";
+}
+
+_PUBLIC_ char *reg_val_data_string(TALLOC_CTX *mem_ctx,
+ struct smb_iconv_convenience *iconv_convenience,
+ uint32_t type,
+ const DATA_BLOB data)
+{
+ char *ret = NULL;
+
+ if (data.length == 0)
+ return talloc_strdup(mem_ctx, "");
+
+ switch (type) {
+ case REG_EXPAND_SZ:
+ case REG_SZ:
+ convert_string_talloc_convenience(mem_ctx, iconv_convenience, CH_UTF16, CH_UNIX,
+ data.data, data.length,
+ (void **)&ret, NULL, false);
+ return ret;
+ case REG_BINARY:
+ ret = data_blob_hex_string(mem_ctx, &data);
+ return ret;
+ case REG_DWORD:
+ if (*(int *)data.data == 0)
+ return talloc_strdup(mem_ctx, "0");
+ return talloc_asprintf(mem_ctx, "0x%x",
+ *(int *)data.data);
+ case REG_MULTI_SZ:
+ /* FIXME */
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+/** Generate a string that describes a registry value */
+_PUBLIC_ char *reg_val_description(TALLOC_CTX *mem_ctx,
+ struct smb_iconv_convenience *iconv_convenience,
+ const char *name,
+ uint32_t data_type,
+ const DATA_BLOB data)
+{
+ return talloc_asprintf(mem_ctx, "%s = %s : %s", name?name:"<No Name>",
+ str_regtype(data_type),
+ reg_val_data_string(mem_ctx, iconv_convenience, data_type, data));
+}
+
+_PUBLIC_ bool reg_string_to_val(TALLOC_CTX *mem_ctx,
+ struct smb_iconv_convenience *iconv_convenience,
+ const char *type_str,
+ const char *data_str, uint32_t *type,
+ DATA_BLOB *data)
+{
+ int i;
+ *type = -1;
+
+ /* Find the correct type */
+ for (i = 0; reg_value_types[i].name; i++) {
+ if (!strcmp(reg_value_types[i].name, type_str)) {
+ *type = reg_value_types[i].id;
+ break;
+ }
+ }
+
+ if (*type == -1)
+ return false;
+
+ /* Convert data appropriately */
+
+ switch (*type)
+ {
+ case REG_SZ:
+ case REG_EXPAND_SZ:
+ convert_string_talloc_convenience(mem_ctx, iconv_convenience, CH_UNIX, CH_UTF16,
+ data_str, strlen(data_str),
+ (void **)&data->data, &data->length, false);
+ break;
+
+ case REG_DWORD: {
+ uint32_t tmp = strtol(data_str, NULL, 0);
+ *data = data_blob_talloc(mem_ctx, &tmp, 4);
+ }
+ break;
+
+ case REG_NONE:
+ ZERO_STRUCTP(data);
+ break;
+
+ case REG_BINARY:
+ *data = strhex_to_data_blob(mem_ctx, data_str);
+ break;
+
+ default:
+ /* FIXME */
+ return false;
+ }
+ return true;
+}
+
+/** Open a key by name (including the predefined key name!) */
+WERROR reg_open_key_abs(TALLOC_CTX *mem_ctx, struct registry_context *handle,
+ const char *name, struct registry_key **result)
+{
+ struct registry_key *predef;
+ WERROR error;
+ int predeflength;
+ char *predefname;
+
+ if (strchr(name, '\\') != NULL)
+ predeflength = strchr(name, '\\')-name;
+ else
+ predeflength = strlen(name);
+
+ predefname = talloc_strndup(mem_ctx, name, predeflength);
+ error = reg_get_predefined_key_by_name(handle, predefname, &predef);
+ talloc_free(predefname);
+
+ if (!W_ERROR_IS_OK(error)) {
+ return error;
+ }
+
+ if (strchr(name, '\\')) {
+ return reg_open_key(mem_ctx, predef, strchr(name, '\\')+1,
+ result);
+ } else {
+ *result = predef;
+ return WERR_OK;
+ }
+}
+
+static WERROR get_abs_parent(TALLOC_CTX *mem_ctx, struct registry_context *ctx,
+ const char *path, struct registry_key **parent,
+ const char **name)
+{
+ char *parent_name;
+ WERROR error;
+
+ if (strchr(path, '\\') == NULL) {
+ return WERR_FOOBAR;
+ }
+
+ parent_name = talloc_strndup(mem_ctx, path, strrchr(path, '\\')-path);
+
+ error = reg_open_key_abs(mem_ctx, ctx, parent_name, parent);
+ if (!W_ERROR_IS_OK(error)) {
+ return error;
+ }
+
+ *name = talloc_strdup(mem_ctx, strrchr(path, '\\')+1);
+
+ return WERR_OK;
+}
+
+WERROR reg_key_del_abs(struct registry_context *ctx, const char *path)
+{
+ struct registry_key *parent;
+ const char *n;
+ TALLOC_CTX *mem_ctx = talloc_init("reg_key_del_abs");
+ WERROR error;
+
+ if (!strchr(path, '\\')) {
+ return WERR_FOOBAR;
+ }
+
+ error = get_abs_parent(mem_ctx, ctx, path, &parent, &n);
+ if (W_ERROR_IS_OK(error)) {
+ error = reg_key_del(parent, n);
+ }
+
+ talloc_free(mem_ctx);
+
+ return error;
+}
+
+WERROR reg_key_add_abs(TALLOC_CTX *mem_ctx, struct registry_context *ctx,
+ const char *path, uint32_t access_mask,
+ struct security_descriptor *sec_desc,
+ struct registry_key **result)
+{
+ struct registry_key *parent;
+ const char *n;
+ WERROR error;
+
+ if (!strchr(path, '\\')) {
+ return WERR_ALREADY_EXISTS;
+ }
+
+ error = get_abs_parent(mem_ctx, ctx, path, &parent, &n);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(2, ("Opening parent of %s failed with %s\n", path,
+ win_errstr(error)));
+ return error;
+ }
+
+ error = reg_key_add_name(mem_ctx, parent, n, NULL, sec_desc, result);
+
+ return error;
+}
diff --git a/source4/lib/registry/wine.c b/source4/lib/registry/wine.c
new file mode 100644
index 0000000000..77d2ce66e9
--- /dev/null
+++ b/source4/lib/registry/wine.c
@@ -0,0 +1,45 @@
+/*
+ Unix SMB/CIFS implementation.
+ Registry interface
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/registry/common/registry.h"
+#include "windows/registry.h"
+
+static WERROR wine_open_reg (struct registry_hive *h, struct registry_key **key)
+{
+ /* FIXME: Open h->location and mmap it */
+}
+
+static REG_OPS reg_backend_wine = {
+ .name = "wine",
+ .open_hive = wine_open_reg,
+
+};
+
+NTSTATUS registry_wine_init(void)
+{
+ register_backend("registry", &reg_backend_wine);
+ return NT_STATUS_OK;
+}
+
+WERROR reg_open_wine(struct registry_key **ctx)
+{
+ /* FIXME: Open ~/.wine/system.reg, etc */
+ return WERR_NOT_SUPPORTED;
+}
diff --git a/source4/lib/samba3/README b/source4/lib/samba3/README
new file mode 100644
index 0000000000..83520f673d
--- /dev/null
+++ b/source4/lib/samba3/README
@@ -0,0 +1,8 @@
+This directory contains various files and functions for the purpose of
+Samba3 import, migration and compatibility.
+
+For example, the first file in this directory (smbpasswd.c) handles
+portions of the smbpasswd file format.
+
+The other files in this directory support reading the various
+TDB databases from Samba3.
diff --git a/source4/lib/samba3/STATUS b/source4/lib/samba3/STATUS
new file mode 100644
index 0000000000..e4644526df
--- /dev/null
+++ b/source4/lib/samba3/STATUS
@@ -0,0 +1,68 @@
+--- Samba3 -> Samba4 Upgrade ---
+(C) 2005 Jelmer Vernooij <jelmer@samba.org>
+Published under the GNU GPL
+
+Sponsored by the Google Summer of Code program (http://code.google.com/summerofcode.html)
+Mentored by Andrew Bartlett <abartlet@samba.org>
+Thanks!
+
+Done:
+ - Reading wins.dat
+ - Reading registry.tdb
+ - Reading passdb.tdb
+ - Reading account_policy.tdb
+ - Reading group_mappings.tdb
+ - Reading winbindd_idmap.tdb
+ - Reading share_info.tdb
+ - Reading secrets.tdb
+ - Reading smbpasswd
+ - Reading + writing (generic) smb.conf files
+ - Testsuite for read support mentioned above
+ - Console utility for dumping Samba information
+ - Import user accounts in Samba4
+ - Import groups in Samba4
+ - Import secrets in Samba4
+ - Import WINS data in Samba4
+ - Dump idmap data to LDB
+ - Import registry keys/values in Samba4
+ - Import account policies in Samba4
+ - Testsuite for upgrade
+ - Console utility from upgrading from Samba3 -> Samba4
+ - SWAT (Web interface) support for upgrading from Samba3 -> Samba4
+ - LDB generic mapping module
+ - (Experimental) Samba4 LDB <-> Samba3 LDAP mapping module based on LDB generic mapping module
+ - Testsuite for Samba4 LDB <-> Samba3 LDAP mapping module
+
+Source files:
+source/lib/ldb/modules/ldb_map.c
+source/lib/ldb/modules/ldb_map.h
+source/lib/samba3/group.c
+source/lib/samba3/idmap.c
+source/lib/samba3/policy.c
+source/lib/samba3/registry.c
+source/lib/samba3/samba3.c
+source/lib/samba3/secrets.c
+source/lib/samba3/share_info.c
+source/lib/samba3/smbpasswd.c
+source/lib/samba3/tdbsam.c
+source/lib/samba3/winsdb.c
+source/lib/samba3/samba3.h
+source/scripting/libjs/upgrade.js
+source/scripting/ejs/smbcalls_param.c
+source/scripting/ejs/smbcalls_samba3.c
+source/param/generic.c
+source/param/generic.h
+testdata/samba3/verify
+testprogs/ejs/samba3sam
+source/setup/upgrade
+source/scripting/bin/samba3dump
+source/dsdb/samdb/ldb_modules/samba3sam.c
+source/script/tests/test_s3upgrade.sh
+swat/install/samba3.esp
+
+Known remaining issues:
+ - [upgrade] Conversion from the smbpasswd/TDB passwords to ntPwdHash / lmPwdHash is broken. Couldn't find out why.
+ - [ldb_map] Conversion of attribute names in DN's is still a bit dodgy
+ - [ldb_map] mapped objectClass names may be mentioned multiple times in returned records
+ - [ldb_map] add/modify support not tested very well with LDAP yet (only LDB+TDB)
+ - [ldb_map] group membership is not yet mapped (only primaryGroupID / sambaPrimaryGroupSID)
diff --git a/source4/lib/samba3/config.mk b/source4/lib/samba3/config.mk
new file mode 100644
index 0000000000..365347fe21
--- /dev/null
+++ b/source4/lib/samba3/config.mk
@@ -0,0 +1,8 @@
+################################################
+# Start SUBSYSTEM LIBSAMBA3
+[SUBSYSTEM::SMBPASSWD]
+PRIVATE_DEPENDENCIES = CHARSET LIBSAMBA-UTIL
+# End SUBSYSTEM LIBSAMBA3
+################################################
+
+SMBPASSWD_OBJ_FILES = $(libsrcdir)/samba3/smbpasswd.o
diff --git a/source4/lib/samba3/samba3.h b/source4/lib/samba3/samba3.h
new file mode 100644
index 0000000000..1a0ce04143
--- /dev/null
+++ b/source4/lib/samba3/samba3.h
@@ -0,0 +1,32 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba3 interfaces
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _SAMBA3_H /* _SAMBA3_H */
+#define _SAMBA3_H
+
+#include "librpc/gen_ndr/security.h"
+#include "librpc/gen_ndr/samr.h"
+#include "param/param.h"
+
+struct samr_Password *smbpasswd_gethexpwd(TALLOC_CTX *mem_ctx, const char *p);
+char *smbpasswd_sethexpwd(TALLOC_CTX *mem_ctx, struct samr_Password *pwd, uint16_t acb_info);
+uint16_t smbpasswd_decode_acb_info(const char *p);
+char *smbpasswd_encode_acb_info(TALLOC_CTX *mem_ctx, uint16_t acb_info);
+
+#endif /* _SAMBA3_H */
diff --git a/source4/lib/samba3/smbpasswd.c b/source4/lib/samba3/smbpasswd.c
new file mode 100644
index 0000000000..502f13febc
--- /dev/null
+++ b/source4/lib/samba3/smbpasswd.c
@@ -0,0 +1,203 @@
+/*
+ Unix SMB/CIFS implementation.
+ smbpasswd file format routines
+
+ Copyright (C) Andrew Tridgell 1992-1998
+ Modified by Jeremy Allison 1995.
+ Modified by Gerald (Jerry) Carter 2000-2001
+ Copyright (C) Tim Potter 2001
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
+ Copyright (C) Jelmer Vernooij <jelmer@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+/*! \file lib/smbpasswd.c
+
+ The smbpasswd file is used to store encrypted passwords in a similar
+ fashion to the /etc/passwd file. The format is colon separated fields
+ with one user per line like so:
+
+ <username>:<uid>:<lanman hash>:<nt hash>:<acb info>:<last change time>
+
+ The username and uid must correspond to an entry in the /etc/passwd
+ file. The lanman and nt password hashes are 32 hex digits corresponding
+ to the 16-byte lanman and nt hashes respectively.
+
+ The password last change time is stored as a string of the format
+ LCD-<change time> where the change time is expressed as an
+
+ 'N' No password
+ 'D' Disabled
+ 'H' Homedir required
+ 'T' Temp account.
+ 'U' User account (normal)
+ 'M' MNS logon user account - what is this ?
+ 'W' Workstation account
+ 'S' Server account
+ 'L' Locked account
+ 'X' No Xpiry on password
+ 'I' Interdomain trust account
+
+*/
+
+#include "includes.h"
+#include "system/locale.h"
+#include "lib/samba3/samba3.h"
+
+/*! Convert 32 hex characters into a 16 byte array. */
+
+struct samr_Password *smbpasswd_gethexpwd(TALLOC_CTX *mem_ctx, const char *p)
+{
+ int i;
+ unsigned char lonybble, hinybble;
+ const char *hexchars = "0123456789ABCDEF";
+ const char *p1, *p2;
+ struct samr_Password *pwd = talloc(mem_ctx, struct samr_Password);
+
+ if (!p) return NULL;
+
+ for (i = 0; i < (sizeof(pwd->hash) * 2); i += 2)
+ {
+ hinybble = toupper(p[i]);
+ lonybble = toupper(p[i + 1]);
+
+ p1 = strchr_m(hexchars, hinybble);
+ p2 = strchr_m(hexchars, lonybble);
+
+ if (!p1 || !p2) {
+ return NULL;
+ }
+
+ hinybble = PTR_DIFF(p1, hexchars);
+ lonybble = PTR_DIFF(p2, hexchars);
+
+ pwd->hash[i / 2] = (hinybble << 4) | lonybble;
+ }
+ return pwd;
+}
+
+/*! Convert a 16-byte array into 32 hex characters. */
+char *smbpasswd_sethexpwd(TALLOC_CTX *mem_ctx, struct samr_Password *pwd, uint16_t acb_info)
+{
+ char *p;
+ if (pwd != NULL) {
+ int i;
+ p = talloc_array(mem_ctx, char, 33);
+ if (!p) {
+ return NULL;
+ }
+
+ for (i = 0; i < sizeof(pwd->hash); i++)
+ slprintf(&p[i*2], 3, "%02X", pwd->hash[i]);
+ } else {
+ if (acb_info & ACB_PWNOTREQ)
+ p = talloc_strdup(mem_ctx, "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX");
+ else
+ p = talloc_strdup(mem_ctx, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
+ }
+ return p;
+}
+
+/*! Decode the account control bits (ACB) info from a string. */
+
+uint16_t smbpasswd_decode_acb_info(const char *p)
+{
+ uint16_t acb_info = 0;
+ bool finished = false;
+
+ /*
+ * Check if the account type bits have been encoded after the
+ * NT password (in the form [NDHTUWSLXI]).
+ */
+
+ if (*p != '[') return 0;
+
+ for (p++; *p && !finished; p++)
+ {
+ switch (*p) {
+ case 'N': /* 'N'o password. */
+ acb_info |= ACB_PWNOTREQ;
+ break;
+ case 'D': /* 'D'isabled. */
+ acb_info |= ACB_DISABLED;
+ break;
+ case 'H': /* 'H'omedir required. */
+ acb_info |= ACB_HOMDIRREQ;
+ break;
+ case 'T': /* 'T'emp account. */
+ acb_info |= ACB_TEMPDUP;
+ break;
+ case 'U': /* 'U'ser account (normal). */
+ acb_info |= ACB_NORMAL;
+ break;
+ case 'M': /* 'M'NS logon user account. What is this ? */
+ acb_info |= ACB_MNS;
+ break;
+ case 'W': /* 'W'orkstation account. */
+ acb_info |= ACB_WSTRUST;
+ break;
+ case 'S': /* 'S'erver account. */
+ acb_info |= ACB_SVRTRUST;
+ break;
+ case 'L': /* 'L'ocked account. */
+ acb_info |= ACB_AUTOLOCK;
+ break;
+ case 'X': /* No 'X'piry on password */
+ acb_info |= ACB_PWNOEXP;
+ break;
+ case 'I': /* 'I'nterdomain trust account. */
+ acb_info |= ACB_DOMTRUST;
+ break;
+
+ case ' ':
+ break;
+ case ':':
+ case '\n':
+ case ']':
+ default:
+ finished = true;
+ break;
+ }
+ }
+
+ return acb_info;
+}
+
+/*! Encode account control bits (ACBs) into a string. */
+
+char *smbpasswd_encode_acb_info(TALLOC_CTX *mem_ctx, uint16_t acb_info)
+{
+ char *acct_str = talloc_array(mem_ctx, char, 35);
+ size_t i = 0;
+
+ acct_str[i++] = '[';
+
+ if (acb_info & ACB_PWNOTREQ ) acct_str[i++] = 'N';
+ if (acb_info & ACB_DISABLED ) acct_str[i++] = 'D';
+ if (acb_info & ACB_HOMDIRREQ) acct_str[i++] = 'H';
+ if (acb_info & ACB_TEMPDUP ) acct_str[i++] = 'T';
+ if (acb_info & ACB_NORMAL ) acct_str[i++] = 'U';
+ if (acb_info & ACB_MNS ) acct_str[i++] = 'M';
+ if (acb_info & ACB_WSTRUST ) acct_str[i++] = 'W';
+ if (acb_info & ACB_SVRTRUST ) acct_str[i++] = 'S';
+ if (acb_info & ACB_AUTOLOCK ) acct_str[i++] = 'L';
+ if (acb_info & ACB_PWNOEXP ) acct_str[i++] = 'X';
+ if (acb_info & ACB_DOMTRUST ) acct_str[i++] = 'I';
+
+ acct_str[i++] = ']';
+ acct_str[i++] = '\0';
+
+ return acct_str;
+}
diff --git a/source4/lib/smbreadline/readline.m4 b/source4/lib/smbreadline/readline.m4
new file mode 100644
index 0000000000..86e2fb19d6
--- /dev/null
+++ b/source4/lib/smbreadline/readline.m4
@@ -0,0 +1,88 @@
+###############################################
+# Readline included by default unless explicitly asked not to
+test "${with_readline+set}" != "set" && with_readline=yes
+
+EXTERNAL_READLINE=no
+# test for where we get readline() from
+AC_MSG_CHECKING(whether to use readline)
+AC_ARG_WITH(readline,
+[AS_HELP_STRING([--with-readline[=DIR]], [Look for readline include/libs in DIR (default=auto)])],
+[ case "$with_readline" in
+ yes)
+ AC_MSG_RESULT(yes)
+
+ AC_CHECK_HEADERS(readline.h history.h readline/readline.h)
+ AC_CHECK_HEADERS(readline/history.h)
+
+ AC_CHECK_HEADERS(readline.h readline/readline.h,[
+ for termlib in ncurses curses termcap terminfo termlib tinfo; do
+ AC_CHECK_LIB(${termlib}, tgetent, [TERMLIBS="-l${termlib}"; break])
+ done
+ AC_CHECK_LIB(readline, rl_callback_handler_install,
+ [TERMLIBS="-lreadline $TERMLIBS"
+ EXTERNAL_READLINE=yes
+ break], [TERMLIBS=], $TERMLIBS)])
+ ;;
+ no)
+ AC_MSG_RESULT(no)
+ ;;
+ *)
+ AC_MSG_RESULT(yes)
+
+ # Needed for AC_CHECK_HEADERS and AC_CHECK_LIB to look at
+ # alternate readline path
+ _ldflags=${LDFLAGS}
+ _cppflags=${CPPFLAGS}
+
+ # Add additional search path
+ LDFLAGS="-L$with_readline/lib $LDFLAGS"
+ CPPFLAGS="-I$with_readline/include $CPPFLAGS"
+
+ AC_CHECK_HEADERS(readline.h history.h readline/readline.h)
+ AC_CHECK_HEADERS(readline/history.h)
+
+ AC_CHECK_HEADERS(readline.h readline/readline.h,[
+ for termlib in ncurses curses termcap terminfo termlib; do
+ AC_CHECK_LIB(${termlib}, tgetent, [TERMLIBS="-l${termlib}"; break])
+ done
+ AC_CHECK_LIB(readline, rl_callback_handler_install,
+ [TERMLDFLAGS="-L$with_readline/lib"
+ TERMCPPFLAGS="-I$with_readline/include"
+ LDFLAGS="-L$with_readline/lib $LDFLAGS"
+ CPPFLAGS="-I$with_readline/include $CPPFLAGS"
+ TERMLIBS="-lreadline $TERMLIBS"
+ EXTERNAL_READLINE=yes
+ break], [TERMLIBS= CPPFLAGS=$_cppflags], $TERMLIBS)])
+
+ ;;
+ esac],
+ AC_MSG_RESULT(no)
+)
+
+# The readline API changed slightly from readline3 to readline4, so
+# code will generate warnings on one of them unless we have a few
+# special cases.
+AC_CHECK_LIB(readline, rl_completion_matches,
+ [AC_DEFINE(HAVE_NEW_LIBREADLINE, 1,
+ [Do we have rl_completion_matches?])],
+ [],
+ [$TERMLIBS])
+
+# not all readline libs have rl_event_hook or history_list
+AC_CHECK_DECLS(rl_event_hook, [], [], [#include <readline/readline.h>])
+AC_CHECK_LIB(readline, history_list,
+ [AC_DEFINE(HAVE_HISTORY_LIST, 1, [Do we have history_list?])],
+ [],
+ [$TERMLIBS])
+
+AC_MSG_CHECKING(whether to use extern readline)
+if test x"$EXTERNAL_READLINE" = x"yes"; then
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(HAVE_LIBREADLINE,1,[Whether the system has readline])
+ SMB_SUBSYSTEM(SMBREADLINE, [\$(smbreadlinesrcdir)/smbreadline.o], [READLINE])
+ SMB_EXT_LIB(READLINE, [${TERMLIBS}])
+ SMB_ENABLE(READLINE,YES)
+else
+ SMB_SUBSYSTEM(SMBREADLINE, [\$(smbreadlinesrcdir)/smbreadline.o], [])
+ AC_MSG_RESULT(no)
+fi
diff --git a/source4/lib/smbreadline/smbreadline.c b/source4/lib/smbreadline/smbreadline.c
new file mode 100644
index 0000000000..5fb3bf4fae
--- /dev/null
+++ b/source4/lib/smbreadline/smbreadline.c
@@ -0,0 +1,170 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba readline wrapper implementation
+ Copyright (C) Simo Sorce 2001
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "system/select.h"
+#include "system/readline.h"
+#include "lib/smbreadline/smbreadline.h"
+
+/*******************************************************************
+ Similar to sys_select() but catch EINTR and continue.
+ This is what sys_select() used to do in Samba.
+********************************************************************/
+
+static int sys_select_intr(int maxfd, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *tval)
+{
+ int ret;
+ fd_set *readfds2, readfds_buf, *writefds2, writefds_buf, *errorfds2, errorfds_buf;
+ struct timeval tval2, *ptval;
+
+ readfds2 = (readfds ? &readfds_buf : NULL);
+ writefds2 = (writefds ? &writefds_buf : NULL);
+ errorfds2 = (errorfds ? &errorfds_buf : NULL);
+ ptval = (tval ? &tval2 : NULL);
+
+ do {
+ if (readfds)
+ readfds_buf = *readfds;
+ if (writefds)
+ writefds_buf = *writefds;
+ if (errorfds)
+ errorfds_buf = *errorfds;
+ if (tval)
+ tval2 = *tval;
+
+ /* We must use select and not sys_select here. If we use
+ sys_select we'd lose the fact a signal occurred when sys_select
+ read a byte from the pipe. Fix from Mark Weaver
+ <mark-clist@npsl.co.uk>
+ */
+
+ ret = select(maxfd, readfds2, writefds2, errorfds2, ptval);
+ } while (ret == -1 && errno == EINTR);
+
+ if (readfds)
+ *readfds = readfds_buf;
+ if (writefds)
+ *writefds = writefds_buf;
+ if (errorfds)
+ *errorfds = errorfds_buf;
+
+ return ret;
+}
+
+/****************************************************************************
+ Display the prompt and wait for input. Call callback() regularly
+****************************************************************************/
+
+static char *smb_readline_replacement(const char *prompt, void (*callback)(void),
+ char **(completion_fn)(const char *text, int start, int end))
+{
+ fd_set fds;
+ char *line;
+ struct timeval timeout;
+ int fd = STDIN_FILENO;
+ char *ret;
+
+ printf("%s", prompt);
+
+ line = (char *)malloc(BUFSIZ);
+ if (!line) {
+ return NULL;
+ }
+
+ while (1) {
+ timeout.tv_sec = 5;
+ timeout.tv_usec = 0;
+
+ FD_ZERO(&fds);
+ FD_SET(fd,&fds);
+
+ if (sys_select_intr(fd+1,&fds,NULL,NULL,&timeout) == 1) {
+ ret = x_fgets(line, BUFSIZ, x_stdin);
+ return ret;
+ }
+ if (callback)
+ callback();
+ }
+}
+
+/****************************************************************************
+ Display the prompt and wait for input. Call callback() regularly.
+****************************************************************************/
+
+char *smb_readline(const char *prompt, void (*callback)(void),
+ char **(completion_fn)(const char *text, int start, int end))
+{
+#if HAVE_LIBREADLINE
+ if (isatty(STDIN_FILENO)) {
+ char *ret;
+
+ /* Aargh! Readline does bizzare things with the terminal width
+ that mucks up expect(1). Set CLI_NO_READLINE in the environment
+ to force readline not to be used. */
+
+ if (getenv("CLI_NO_READLINE"))
+ return smb_readline_replacement(prompt, callback, completion_fn);
+
+ if (completion_fn) {
+ /* The callback prototype has changed slightly between
+ different versions of Readline, so the same function
+ works in all of them to date, but we get compiler
+ warnings in some. */
+ rl_attempted_completion_function = RL_COMPLETION_CAST completion_fn;
+ }
+
+#if HAVE_DECL_RL_EVENT_HOOK
+ if (callback)
+ rl_event_hook = (Function *)callback;
+#endif
+ ret = readline(prompt);
+ if (ret && *ret)
+ add_history(ret);
+ return ret;
+ } else
+#endif
+ return smb_readline_replacement(prompt, callback, completion_fn);
+}
+
+/****************************************************************************
+ * return line buffer text
+ ****************************************************************************/
+const char *smb_readline_get_line_buffer(void)
+{
+#if defined(HAVE_LIBREADLINE)
+ return rl_line_buffer;
+#else
+ return NULL;
+#endif
+}
+
+/****************************************************************************
+ * set completion append character
+ ***************************************************************************/
+void smb_readline_ca_char(char c)
+{
+#if defined(HAVE_LIBREADLINE)
+ rl_completion_append_character = c;
+#endif
+}
+
+
+
diff --git a/source4/lib/smbreadline/smbreadline.h b/source4/lib/smbreadline/smbreadline.h
new file mode 100644
index 0000000000..cde2b47a24
--- /dev/null
+++ b/source4/lib/smbreadline/smbreadline.h
@@ -0,0 +1,9 @@
+#ifndef __SMBREADLINE_H__
+#define __SMBREADLINE_H__
+
+char *smb_readline(const char *prompt, void (*callback)(void),
+ char **(completion_fn)(const char *text, int start, int end));
+const char *smb_readline_get_line_buffer(void);
+void smb_readline_ca_char(char c);
+
+#endif /* __SMBREADLINE_H__ */
diff --git a/source4/lib/socket/access.c b/source4/lib/socket/access.c
new file mode 100644
index 0000000000..42c42db365
--- /dev/null
+++ b/source4/lib/socket/access.c
@@ -0,0 +1,361 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ check access rules for socket connections
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+
+/*
+ This module is an adaption of code from the tcpd-1.4 package written
+ by Wietse Venema, Eindhoven University of Technology, The Netherlands.
+
+ The code is used here with permission.
+
+ The code has been considerably changed from the original. Bug reports
+ should be sent to samba@samba.org
+*/
+
+#include "includes.h"
+#include "system/network.h"
+#include "lib/socket/socket.h"
+#include "system/locale.h"
+
+#define FAIL (-1)
+#define ALLONES ((uint32_t)0xFFFFFFFF)
+
+/* masked_match - match address against netnumber/netmask */
+static bool masked_match(TALLOC_CTX *mem_ctx, const char *tok, const char *slash, const char *s)
+{
+ uint32_t net;
+ uint32_t mask;
+ uint32_t addr;
+ char *tok_cpy;
+
+ if ((addr = interpret_addr(s)) == INADDR_NONE)
+ return false;
+
+ tok_cpy = talloc_strdup(mem_ctx, tok);
+ tok_cpy[PTR_DIFF(slash,tok)] = '\0';
+ net = interpret_addr(tok_cpy);
+ talloc_free(tok_cpy);
+
+ if (strlen(slash + 1) > 2) {
+ mask = interpret_addr(slash + 1);
+ } else {
+ mask = (uint32_t)((ALLONES >> atoi(slash + 1)) ^ ALLONES);
+ /* convert to network byte order */
+ mask = htonl(mask);
+ }
+
+ if (net == INADDR_NONE || mask == INADDR_NONE) {
+ DEBUG(0,("access: bad net/mask access control: %s\n", tok));
+ return false;
+ }
+
+ return (addr & mask) == (net & mask);
+}
+
+/* string_match - match string against token */
+static bool string_match(TALLOC_CTX *mem_ctx, const char *tok,const char *s, char *invalid_char)
+{
+ size_t tok_len;
+ size_t str_len;
+ const char *cut;
+
+ *invalid_char = '\0';
+
+ /* Return true if a token has the magic value "ALL". Return
+ * FAIL if the token is "FAIL". If the token starts with a "."
+ * (domain name), return true if it matches the last fields of
+ * the string. If the token has the magic value "LOCAL",
+ * return true if the string does not contain a "."
+ * character. If the token ends on a "." (network number),
+ * return true if it matches the first fields of the
+ * string. If the token begins with a "@" (netgroup name),
+ * return true if the string is a (host) member of the
+ * netgroup. Return true if the token fully matches the
+ * string. If the token is a netnumber/netmask pair, return
+ * true if the address is a member of the specified subnet.
+ */
+
+ if (tok[0] == '.') { /* domain: match last fields */
+ if ((str_len = strlen(s)) > (tok_len = strlen(tok))
+ && strcasecmp(tok, s + str_len - tok_len)==0) {
+ return true;
+ }
+ } else if (tok[0] == '@') { /* netgroup: look it up */
+ DEBUG(0,("access: netgroup support is not available\n"));
+ return false;
+ } else if (strcmp(tok, "ALL")==0) { /* all: match any */
+ return true;
+ } else if (strcmp(tok, "FAIL")==0) { /* fail: match any */
+ return FAIL;
+ } else if (strcmp(tok, "LOCAL")==0) { /* local: no dots */
+ if (strchr(s, '.') == 0 && strcasecmp(s, "unknown") != 0) {
+ return true;
+ }
+ } else if (strcasecmp(tok, s)==0) { /* match host name or address */
+ return true;
+ } else if (tok[(tok_len = strlen(tok)) - 1] == '.') { /* network */
+ if (strncmp(tok, s, tok_len) == 0)
+ return true;
+ } else if ((cut = strchr(tok, '/')) != 0) { /* netnumber/netmask */
+ if (isdigit((int)s[0]) && masked_match(mem_ctx, tok, cut, s))
+ return true;
+ } else if (strchr(tok, '*') != 0) {
+ *invalid_char = '*';
+ } else if (strchr(tok, '?') != 0) {
+ *invalid_char = '?';
+ }
+ return false;
+}
+
+struct client_addr {
+ const char *cname;
+ const char *caddr;
+};
+
+/* client_match - match host name and address against token */
+static bool client_match(TALLOC_CTX *mem_ctx, const char *tok, struct client_addr *client)
+{
+ bool match;
+ char invalid_char = '\0';
+
+ /*
+ * Try to match the address first. If that fails, try to match the host
+ * name if available.
+ */
+
+ if ((match = string_match(mem_ctx, tok, client->caddr, &invalid_char)) == 0) {
+ if(invalid_char)
+ DEBUG(0,("client_match: address match failing due to invalid character '%c' found in \
+token '%s' in an allow/deny hosts line.\n", invalid_char, tok ));
+
+ if (client->cname[0] != 0)
+ match = string_match(mem_ctx, tok, client->cname, &invalid_char);
+
+ if(invalid_char)
+ DEBUG(0,("client_match: address match failing due to invalid character '%c' found in \
+token '%s' in an allow/deny hosts line.\n", invalid_char, tok ));
+ }
+
+ return (match);
+}
+
+/* list_match - match an item against a list of tokens with exceptions */
+static bool list_match(TALLOC_CTX *mem_ctx, const char **list, struct client_addr *client)
+{
+ bool match = false;
+
+ if (!list)
+ return false;
+
+ /*
+ * Process tokens one at a time. We have exhausted all possible matches
+ * when we reach an "EXCEPT" token or the end of the list. If we do find
+ * a match, look for an "EXCEPT" list and recurse to determine whether
+ * the match is affected by any exceptions.
+ */
+
+ for (; *list ; list++) {
+ if (strcmp(*list, "EXCEPT")==0) /* EXCEPT: give up */
+ break;
+ if ((match = client_match(mem_ctx, *list, client))) /* true or FAIL */
+ break;
+ }
+
+ /* Process exceptions to true or FAIL matches. */
+ if (match != false) {
+ while (*list && strcmp(*list, "EXCEPT")!=0)
+ list++;
+
+ for (; *list; list++) {
+ if (client_match(mem_ctx, *list, client)) /* Exception Found */
+ return false;
+ }
+ }
+
+ return match;
+}
+
+/* return true if access should be allowed */
+static bool allow_access_internal(TALLOC_CTX *mem_ctx,
+ const char **deny_list,const char **allow_list,
+ const char *cname, const char *caddr)
+{
+ struct client_addr client;
+
+ client.cname = cname;
+ client.caddr = caddr;
+
+ /* if it is loopback then always allow unless specifically denied */
+ if (strcmp(caddr, "127.0.0.1") == 0) {
+ /*
+ * If 127.0.0.1 matches both allow and deny then allow.
+ * Patch from Steve Langasek vorlon@netexpress.net.
+ */
+ if (deny_list &&
+ list_match(mem_ctx, deny_list, &client) &&
+ (!allow_list ||
+ !list_match(mem_ctx, allow_list, &client))) {
+ return false;
+ }
+ return true;
+ }
+
+ /* if theres no deny list and no allow list then allow access */
+ if ((!deny_list || *deny_list == 0) &&
+ (!allow_list || *allow_list == 0)) {
+ return true;
+ }
+
+ /* if there is an allow list but no deny list then allow only hosts
+ on the allow list */
+ if (!deny_list || *deny_list == 0)
+ return list_match(mem_ctx, allow_list, &client);
+
+ /* if theres a deny list but no allow list then allow
+ all hosts not on the deny list */
+ if (!allow_list || *allow_list == 0)
+ return !list_match(mem_ctx, deny_list, &client);
+
+ /* if there are both types of list then allow all hosts on the
+ allow list */
+ if (list_match(mem_ctx, allow_list, &client))
+ return true;
+
+ /* if there are both types of list and it's not on the allow then
+ allow it if its not on the deny */
+ if (list_match(mem_ctx, deny_list, &client))
+ return false;
+
+ return true;
+}
+
+/* return true if access should be allowed */
+bool allow_access(TALLOC_CTX *mem_ctx,
+ const char **deny_list, const char **allow_list,
+ const char *cname, const char *caddr)
+{
+ bool ret;
+ char *nc_cname = talloc_strdup(mem_ctx, cname);
+ char *nc_caddr = talloc_strdup(mem_ctx, caddr);
+
+ if (!nc_cname || !nc_caddr) {
+ return false;
+ }
+
+ ret = allow_access_internal(mem_ctx, deny_list, allow_list, nc_cname, nc_caddr);
+
+ talloc_free(nc_cname);
+ talloc_free(nc_caddr);
+
+ return ret;
+}
+
+/* return true if the char* contains ip addrs only. Used to avoid
+gethostbyaddr() calls */
+
+static bool only_ipaddrs_in_list(const char** list)
+{
+ bool only_ip = true;
+
+ if (!list)
+ return true;
+
+ for (; *list ; list++) {
+ /* factor out the special strings */
+ if (strcmp(*list, "ALL")==0 ||
+ strcmp(*list, "FAIL")==0 ||
+ strcmp(*list, "EXCEPT")==0) {
+ continue;
+ }
+
+ if (!is_ipaddress(*list)) {
+ /*
+ * if we failed, make sure that it was not because the token
+ * was a network/netmask pair. Only network/netmask pairs
+ * have a '/' in them
+ */
+ if ((strchr(*list, '/')) == NULL) {
+ only_ip = false;
+ DEBUG(3,("only_ipaddrs_in_list: list has non-ip address (%s)\n", *list));
+ break;
+ }
+ }
+ }
+
+ return only_ip;
+}
+
+/* return true if access should be allowed to a service for a socket */
+bool socket_check_access(struct socket_context *sock,
+ const char *service_name,
+ const char **allow_list, const char **deny_list)
+{
+ bool ret;
+ const char *name="";
+ struct socket_address *addr;
+ TALLOC_CTX *mem_ctx;
+
+ if ((!deny_list || *deny_list==0) &&
+ (!allow_list || *allow_list==0)) {
+ return true;
+ }
+
+ mem_ctx = talloc_init("socket_check_access");
+ if (!mem_ctx) {
+ return false;
+ }
+
+ addr = socket_get_peer_addr(sock, mem_ctx);
+ if (!addr) {
+ DEBUG(0,("socket_check_access: Denied connection from unknown host: could not get peer address from kernel\n"));
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ /* bypass gethostbyaddr() calls if the lists only contain IP addrs */
+ if (!only_ipaddrs_in_list(allow_list) ||
+ !only_ipaddrs_in_list(deny_list)) {
+ name = socket_get_peer_name(sock, mem_ctx);
+ if (!name) {
+ name = addr->addr;
+ }
+ }
+
+ if (!addr) {
+ DEBUG(0,("socket_check_access: Denied connection from unknown host\n"));
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ ret = allow_access(mem_ctx, deny_list, allow_list, name, addr->addr);
+
+ if (ret) {
+ DEBUG(2,("socket_check_access: Allowed connection to '%s' from %s (%s)\n",
+ service_name, name, addr->addr));
+ } else {
+ DEBUG(0,("socket_check_access: Denied connection to '%s' from %s (%s)\n",
+ service_name, name, addr->addr));
+ }
+
+ talloc_free(mem_ctx);
+
+ return ret;
+}
diff --git a/source4/lib/socket/config.m4 b/source4/lib/socket/config.m4
new file mode 100644
index 0000000000..fa987a1f46
--- /dev/null
+++ b/source4/lib/socket/config.m4
@@ -0,0 +1,18 @@
+AC_CHECK_FUNCS(writev)
+AC_CHECK_FUNCS(readv)
+
+############################################
+# check for unix domain sockets
+# done by AC_LIBREPLACE_NETWORK_CHECKS
+SMB_ENABLE(socket_unix, NO)
+if test x"$libreplace_cv_HAVE_UNIXSOCKET" = x"yes"; then
+ SMB_ENABLE(socket_unix, YES)
+fi
+
+############################################
+# check for ipv6
+# done by AC_LIBREPLACE_NETWORK_CHECKS
+SMB_ENABLE(socket_ipv6, NO)
+if test x"$libreplace_cv_HAVE_IPV6" = x"yes"; then
+ SMB_ENABLE(socket_ipv6, YES)
+fi
diff --git a/source4/lib/socket/config.mk b/source4/lib/socket/config.mk
new file mode 100644
index 0000000000..ac515c8f6d
--- /dev/null
+++ b/source4/lib/socket/config.mk
@@ -0,0 +1,43 @@
+##############################
+# Start SUBSYSTEM LIBNETIF
+[SUBSYSTEM::LIBNETIF]
+PRIVATE_DEPENDENCIES = LIBSAMBA-UTIL LIBREPLACE_NETWORK
+# End SUBSYSTEM LIBNETIF
+##############################
+
+LIBNETIF_OBJ_FILES = $(addprefix $(libsocketsrcdir)/, interface.o netif.o)
+
+$(eval $(call proto_header_template,$(libsocketsrcdir)/netif_proto.h,$(LIBNETIF_OBJ_FILES:.o=.c)))
+
+################################################
+# Start MODULE socket_ip
+[MODULE::socket_ip]
+SUBSYSTEM = samba_socket
+OUTPUT_TYPE = MERGED_OBJ
+PRIVATE_DEPENDENCIES = LIBSAMBA-ERRORS LIBREPLACE_NETWORK
+# End MODULE socket_ip
+################################################
+
+socket_ip_OBJ_FILES = $(libsocketsrcdir)/socket_ip.o
+
+################################################
+# Start MODULE socket_unix
+[MODULE::socket_unix]
+SUBSYSTEM = samba_socket
+OUTPUT_TYPE = MERGED_OBJ
+PRIVATE_DEPENDENCIES = LIBREPLACE_NETWORK
+# End MODULE socket_unix
+################################################
+
+socket_unix_OBJ_FILES = $(libsocketsrcdir)/socket_unix.o
+
+################################################
+# Start SUBSYSTEM SOCKET
+[SUBSYSTEM::samba_socket]
+PUBLIC_DEPENDENCIES = LIBTALLOC
+PRIVATE_DEPENDENCIES = SOCKET_WRAPPER LIBCLI_COMPOSITE LIBCLI_RESOLVE
+# End SUBSYSTEM SOCKET
+################################################
+
+samba_socket_OBJ_FILES = $(addprefix $(libsocketsrcdir)/, socket.o access.o connect_multi.o connect.o)
+
diff --git a/source4/lib/socket/connect.c b/source4/lib/socket/connect.c
new file mode 100644
index 0000000000..b943de8c14
--- /dev/null
+++ b/source4/lib/socket/connect.c
@@ -0,0 +1,158 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ implements a non-blocking connect operation that is aware of the samba4
+ events system
+
+ Copyright (C) Andrew Tridgell 2005
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/socket/socket.h"
+#include "lib/events/events.h"
+#include "libcli/composite/composite.h"
+
+
+struct connect_state {
+ struct socket_context *sock;
+ const struct socket_address *my_address;
+ const struct socket_address *server_address;
+ uint32_t flags;
+};
+
+static void socket_connect_handler(struct tevent_context *ev,
+ struct tevent_fd *fde,
+ uint16_t flags, void *private_data);
+
+/*
+ call the real socket_connect() call, and setup event handler
+*/
+static void socket_send_connect(struct composite_context *result)
+{
+ struct tevent_fd *fde;
+ struct connect_state *state = talloc_get_type(result->private_data,
+ struct connect_state);
+
+ result->status = socket_connect(state->sock,
+ state->my_address,
+ state->server_address,
+ state->flags);
+ if (NT_STATUS_IS_ERR(result->status) &&
+ !NT_STATUS_EQUAL(result->status,
+ NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ composite_error(result, result->status);
+ return;
+ }
+
+ fde = event_add_fd(result->event_ctx, result,
+ socket_get_fd(state->sock),
+ EVENT_FD_READ|EVENT_FD_WRITE,
+ socket_connect_handler, result);
+ composite_nomem(fde, result);
+}
+
+
+/*
+ send a socket connect, potentially doing some name resolution first
+*/
+struct composite_context *socket_connect_send(struct socket_context *sock,
+ struct socket_address *my_address,
+ struct socket_address *server_address,
+ uint32_t flags,
+ struct tevent_context *event_ctx)
+{
+ struct composite_context *result;
+ struct connect_state *state;
+
+ result = composite_create(sock, event_ctx);
+ if (result == NULL) return NULL;
+
+ state = talloc_zero(result, struct connect_state);
+ if (composite_nomem(state, result)) return result;
+ result->private_data = state;
+
+ state->sock = talloc_reference(state, sock);
+ if (composite_nomem(state->sock, result)) return result;
+
+ if (my_address) {
+ void *ref = talloc_reference(state, my_address);
+ if (composite_nomem(ref, result)) {
+ return result;
+ }
+ state->my_address = my_address;
+ }
+
+ {
+ void *ref = talloc_reference(state, server_address);
+ if (composite_nomem(ref, result)) {
+ return result;
+ }
+ state->server_address = server_address;
+ }
+
+ state->flags = flags;
+
+ set_blocking(socket_get_fd(sock), false);
+
+ socket_send_connect(result);
+
+ return result;
+}
+
+/*
+ handle write events on connect completion
+*/
+static void socket_connect_handler(struct tevent_context *ev,
+ struct tevent_fd *fde,
+ uint16_t flags, void *private_data)
+{
+ struct composite_context *result =
+ talloc_get_type(private_data, struct composite_context);
+ struct connect_state *state = talloc_get_type(result->private_data,
+ struct connect_state);
+
+ result->status = socket_connect_complete(state->sock, state->flags);
+ if (!composite_is_ok(result)) return;
+
+ composite_done(result);
+}
+
+/*
+ wait for a socket_connect_send() to finish
+*/
+NTSTATUS socket_connect_recv(struct composite_context *result)
+{
+ NTSTATUS status = composite_wait(result);
+ talloc_free(result);
+ return status;
+}
+
+
+/*
+ like socket_connect() but takes an event context, doing a semi-async connect
+*/
+NTSTATUS socket_connect_ev(struct socket_context *sock,
+ struct socket_address *my_address,
+ struct socket_address *server_address,
+ uint32_t flags,
+ struct tevent_context *ev)
+{
+ struct composite_context *ctx;
+ ctx = socket_connect_send(sock, my_address,
+ server_address, flags, ev);
+ return socket_connect_recv(ctx);
+}
diff --git a/source4/lib/socket/connect_multi.c b/source4/lib/socket/connect_multi.c
new file mode 100644
index 0000000000..8fcfc0a8ea
--- /dev/null
+++ b/source4/lib/socket/connect_multi.c
@@ -0,0 +1,278 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Fire connect requests to a host and a number of ports, with a timeout
+ between the connect request. Return if the first connect comes back
+ successfully or return the last error.
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/socket/socket.h"
+#include "lib/events/events.h"
+#include "libcli/composite/composite.h"
+#include "libcli/resolve/resolve.h"
+
+#define MULTI_PORT_DELAY 2000 /* microseconds */
+
+/*
+ overall state
+*/
+struct connect_multi_state {
+ const char *server_address;
+ int num_ports;
+ uint16_t *ports;
+
+ struct socket_context *sock;
+ uint16_t result_port;
+
+ int num_connects_sent, num_connects_recv;
+};
+
+/*
+ state of an individual socket_connect_send() call
+*/
+struct connect_one_state {
+ struct composite_context *result;
+ struct socket_context *sock;
+ struct socket_address *addr;
+};
+
+static void continue_resolve_name(struct composite_context *creq);
+static void connect_multi_timer(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv, void *p);
+static void connect_multi_next_socket(struct composite_context *result);
+static void continue_one(struct composite_context *creq);
+
+/*
+ setup an async socket_connect, with multiple ports
+*/
+_PUBLIC_ struct composite_context *socket_connect_multi_send(
+ TALLOC_CTX *mem_ctx,
+ const char *server_address,
+ int num_server_ports,
+ uint16_t *server_ports,
+ struct resolve_context *resolve_ctx,
+ struct tevent_context *event_ctx)
+{
+ struct composite_context *result;
+ struct connect_multi_state *multi;
+ int i;
+
+ result = talloc_zero(mem_ctx, struct composite_context);
+ if (result == NULL) return NULL;
+ result->state = COMPOSITE_STATE_IN_PROGRESS;
+ result->event_ctx = event_ctx;
+
+ multi = talloc_zero(result, struct connect_multi_state);
+ if (composite_nomem(multi, result)) goto failed;
+ result->private_data = multi;
+
+ multi->server_address = talloc_strdup(multi, server_address);
+ if (composite_nomem(multi->server_address, result)) goto failed;
+
+ multi->num_ports = num_server_ports;
+ multi->ports = talloc_array(multi, uint16_t, multi->num_ports);
+ if (composite_nomem(multi->ports, result)) goto failed;
+
+ for (i=0; i<multi->num_ports; i++) {
+ multi->ports[i] = server_ports[i];
+ }
+
+ if (!is_ipaddress(server_address)) {
+ /*
+ we don't want to do the name resolution separately
+ for each port, so start it now, then only start on
+ the real sockets once we have an IP
+ */
+ struct nbt_name name;
+ struct composite_context *creq;
+ make_nbt_name_server(&name, server_address);
+ creq = resolve_name_send(resolve_ctx, &name, result->event_ctx);
+ if (composite_nomem(creq, result)) goto failed;
+ composite_continue(result, creq, continue_resolve_name, result);
+ return result;
+ }
+
+ /* now we've setup the state we can process the first socket */
+ connect_multi_next_socket(result);
+
+ if (!NT_STATUS_IS_OK(result->status)) {
+ goto failed;
+ }
+
+ return result;
+
+ failed:
+ composite_error(result, result->status);
+ return result;
+}
+
+/*
+ start connecting to the next socket/port in the list
+*/
+static void connect_multi_next_socket(struct composite_context *result)
+{
+ struct connect_multi_state *multi = talloc_get_type(result->private_data,
+ struct connect_multi_state);
+ struct connect_one_state *state;
+ struct composite_context *creq;
+ int next = multi->num_connects_sent;
+
+ if (next == multi->num_ports) {
+ /* don't do anything, just wait for the existing ones to finish */
+ return;
+ }
+
+ multi->num_connects_sent += 1;
+
+ state = talloc(multi, struct connect_one_state);
+ if (composite_nomem(state, result)) return;
+
+ state->result = result;
+ result->status = socket_create("ipv4", SOCKET_TYPE_STREAM, &state->sock, 0);
+ if (!composite_is_ok(result)) return;
+
+ /* Form up the particular address we are interested in */
+ state->addr = socket_address_from_strings(state, state->sock->backend_name,
+ multi->server_address, multi->ports[next]);
+ if (composite_nomem(state->addr, result)) return;
+
+ talloc_steal(state, state->sock);
+
+ creq = socket_connect_send(state->sock, NULL,
+ state->addr, 0,
+ result->event_ctx);
+ if (composite_nomem(creq, result)) return;
+ talloc_steal(state, creq);
+
+ composite_continue(result, creq, continue_one, state);
+
+ /* if there are more ports to go then setup a timer to fire when we have waited
+ for a couple of milli-seconds, when that goes off we try the next port regardless
+ of whether this port has completed */
+ if (multi->num_ports > multi->num_connects_sent) {
+ /* note that this timer is a child of the single
+ connect attempt state, so it will go away when this
+ request completes */
+ event_add_timed(result->event_ctx, state,
+ timeval_current_ofs(0, MULTI_PORT_DELAY),
+ connect_multi_timer, result);
+ }
+}
+
+/*
+ a timer has gone off telling us that we should try the next port
+*/
+static void connect_multi_timer(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv, void *p)
+{
+ struct composite_context *result = talloc_get_type(p, struct composite_context);
+ connect_multi_next_socket(result);
+}
+
+
+/*
+ recv name resolution reply then send the next connect
+*/
+static void continue_resolve_name(struct composite_context *creq)
+{
+ struct composite_context *result = talloc_get_type(creq->async.private_data,
+ struct composite_context);
+ struct connect_multi_state *multi = talloc_get_type(result->private_data,
+ struct connect_multi_state);
+ const char *addr;
+
+ result->status = resolve_name_recv(creq, multi, &addr);
+ if (!composite_is_ok(result)) return;
+
+ multi->server_address = addr;
+
+ connect_multi_next_socket(result);
+}
+
+/*
+ one of our socket_connect_send() calls hash finished. If it got a
+ connection or there are none left then we are done
+*/
+static void continue_one(struct composite_context *creq)
+{
+ struct connect_one_state *state = talloc_get_type(creq->async.private_data,
+ struct connect_one_state);
+ struct composite_context *result = state->result;
+ struct connect_multi_state *multi = talloc_get_type(result->private_data,
+ struct connect_multi_state);
+ NTSTATUS status;
+ multi->num_connects_recv++;
+
+ status = socket_connect_recv(creq);
+
+ if (NT_STATUS_IS_OK(status)) {
+ multi->sock = talloc_steal(multi, state->sock);
+ multi->result_port = state->addr->port;
+ }
+
+ talloc_free(state);
+
+ if (NT_STATUS_IS_OK(status) ||
+ multi->num_connects_recv == multi->num_ports) {
+ result->status = status;
+ composite_done(result);
+ return;
+ }
+
+ /* try the next port */
+ connect_multi_next_socket(result);
+}
+
+/*
+ async recv routine for socket_connect_multi()
+ */
+_PUBLIC_ NTSTATUS socket_connect_multi_recv(struct composite_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct socket_context **sock,
+ uint16_t *port)
+{
+ NTSTATUS status = composite_wait(ctx);
+ if (NT_STATUS_IS_OK(status)) {
+ struct connect_multi_state *multi =
+ talloc_get_type(ctx->private_data,
+ struct connect_multi_state);
+ *sock = talloc_steal(mem_ctx, multi->sock);
+ *port = multi->result_port;
+ }
+ talloc_free(ctx);
+ return status;
+}
+
+NTSTATUS socket_connect_multi(TALLOC_CTX *mem_ctx,
+ const char *server_address,
+ int num_server_ports, uint16_t *server_ports,
+ struct resolve_context *resolve_ctx,
+ struct tevent_context *event_ctx,
+ struct socket_context **result,
+ uint16_t *result_port)
+{
+ struct composite_context *ctx =
+ socket_connect_multi_send(mem_ctx, server_address,
+ num_server_ports, server_ports,
+ resolve_ctx,
+ event_ctx);
+ return socket_connect_multi_recv(ctx, mem_ctx, result, result_port);
+}
diff --git a/source4/lib/socket/interface.c b/source4/lib/socket/interface.c
new file mode 100644
index 0000000000..af81804911
--- /dev/null
+++ b/source4/lib/socket/interface.c
@@ -0,0 +1,334 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ multiple interface handling
+
+ Copyright (C) Andrew Tridgell 1992-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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/network.h"
+#include "lib/socket/netif.h"
+#include "../lib/util/dlinklist.h"
+
+/** used for network interfaces */
+struct interface {
+ struct interface *next, *prev;
+ struct in_addr ip;
+ struct in_addr nmask;
+ const char *ip_s;
+ const char *bcast_s;
+ const char *nmask_s;
+};
+
+#define ALLONES ((uint32_t)0xFFFFFFFF)
+/*
+ address construction based on a patch from fred@datalync.com
+*/
+#define MKBCADDR(_IP, _NM) ((_IP & _NM) | (_NM ^ ALLONES))
+#define MKNETADDR(_IP, _NM) (_IP & _NM)
+
+/****************************************************************************
+Try and find an interface that matches an ip. If we cannot, return NULL
+ **************************************************************************/
+static struct interface *iface_find(struct interface *interfaces,
+ struct in_addr ip, bool CheckMask)
+{
+ struct interface *i;
+ if (is_zero_ip_v4(ip)) return interfaces;
+
+ for (i=interfaces;i;i=i->next)
+ if (CheckMask) {
+ if (same_net_v4(i->ip,ip,i->nmask)) return i;
+ } else if (i->ip.s_addr == ip.s_addr) return i;
+
+ return NULL;
+}
+
+
+/****************************************************************************
+add an interface to the linked list of interfaces
+****************************************************************************/
+static void add_interface(TALLOC_CTX *mem_ctx, struct in_addr ip, struct in_addr nmask, struct interface **interfaces)
+{
+ struct interface *iface;
+ struct in_addr bcast;
+
+ if (iface_find(*interfaces, ip, false)) {
+ DEBUG(3,("not adding duplicate interface %s\n",inet_ntoa(ip)));
+ return;
+ }
+
+ iface = talloc(*interfaces == NULL ? mem_ctx : *interfaces, struct interface);
+ if (iface == NULL)
+ return;
+
+ ZERO_STRUCTPN(iface);
+
+ iface->ip = ip;
+ iface->nmask = nmask;
+ bcast.s_addr = MKBCADDR(iface->ip.s_addr, iface->nmask.s_addr);
+
+ /* keep string versions too, to avoid people tripping over the implied
+ static in inet_ntoa() */
+ iface->ip_s = talloc_strdup(iface, inet_ntoa(iface->ip));
+ iface->nmask_s = talloc_strdup(iface, inet_ntoa(iface->nmask));
+
+ if (nmask.s_addr != ~0) {
+ iface->bcast_s = talloc_strdup(iface, inet_ntoa(bcast));
+ }
+
+ DLIST_ADD_END(*interfaces, iface, struct interface *);
+
+ DEBUG(2,("added interface ip=%s nmask=%s\n", iface->ip_s, iface->nmask_s));
+}
+
+
+
+/**
+interpret a single element from a interfaces= config line
+
+This handles the following different forms:
+
+1) wildcard interface name
+2) DNS name
+3) IP/masklen
+4) ip/mask
+5) bcast/mask
+**/
+static void interpret_interface(TALLOC_CTX *mem_ctx,
+ const char *token,
+ struct iface_struct *probed_ifaces,
+ int total_probed,
+ struct interface **local_interfaces)
+{
+ struct in_addr ip, nmask;
+ char *p;
+ char *address;
+ int i, added=0;
+
+ ip.s_addr = 0;
+ nmask.s_addr = 0;
+
+ /* first check if it is an interface name */
+ for (i=0;i<total_probed;i++) {
+ if (gen_fnmatch(token, probed_ifaces[i].name) == 0) {
+ add_interface(mem_ctx, probed_ifaces[i].ip,
+ probed_ifaces[i].netmask,
+ local_interfaces);
+ added = 1;
+ }
+ }
+ if (added) return;
+
+ /* maybe it is a DNS name */
+ p = strchr_m(token,'/');
+ if (!p) {
+ /* don't try to do dns lookups on wildcard names */
+ if (strpbrk(token, "*?") != NULL) {
+ return;
+ }
+ ip.s_addr = interpret_addr2(token).s_addr;
+ for (i=0;i<total_probed;i++) {
+ if (ip.s_addr == probed_ifaces[i].ip.s_addr) {
+ add_interface(mem_ctx, probed_ifaces[i].ip,
+ probed_ifaces[i].netmask,
+ local_interfaces);
+ return;
+ }
+ }
+ DEBUG(2,("can't determine netmask for %s\n", token));
+ return;
+ }
+
+ address = talloc_strdup(mem_ctx, token);
+ p = strchr_m(address,'/');
+
+ /* parse it into an IP address/netmasklength pair */
+ *p++ = 0;
+
+ ip.s_addr = interpret_addr2(address).s_addr;
+
+ if (strlen(p) > 2) {
+ nmask.s_addr = interpret_addr2(p).s_addr;
+ } else {
+ nmask.s_addr = htonl(((ALLONES >> atoi(p)) ^ ALLONES));
+ }
+
+ /* maybe the first component was a broadcast address */
+ if (ip.s_addr == MKBCADDR(ip.s_addr, nmask.s_addr) ||
+ ip.s_addr == MKNETADDR(ip.s_addr, nmask.s_addr)) {
+ for (i=0;i<total_probed;i++) {
+ if (same_net_v4(ip, probed_ifaces[i].ip, nmask)) {
+ add_interface(mem_ctx, probed_ifaces[i].ip, nmask,
+ local_interfaces);
+ talloc_free(address);
+ return;
+ }
+ }
+ DEBUG(2,("Can't determine ip for broadcast address %s\n", address));
+ talloc_free(address);
+ return;
+ }
+
+ add_interface(mem_ctx, ip, nmask, local_interfaces);
+ talloc_free(address);
+}
+
+
+/**
+load the list of network interfaces
+**/
+void load_interfaces(TALLOC_CTX *mem_ctx, const char **interfaces, struct interface **local_interfaces)
+{
+ const char **ptr = interfaces;
+ int i;
+ struct iface_struct ifaces[MAX_INTERFACES];
+ struct in_addr loopback_ip;
+ int total_probed;
+
+ *local_interfaces = NULL;
+
+ loopback_ip = interpret_addr2("127.0.0.1");
+
+ /* probe the kernel for interfaces */
+ total_probed = get_interfaces(ifaces, MAX_INTERFACES);
+
+ /* if we don't have a interfaces line then use all interfaces
+ except loopback */
+ if (!ptr || !*ptr || !**ptr) {
+ if (total_probed <= 0) {
+ DEBUG(0,("ERROR: Could not determine network interfaces, you must use a interfaces config line\n"));
+ }
+ for (i=0;i<total_probed;i++) {
+ if (ifaces[i].ip.s_addr != loopback_ip.s_addr) {
+ add_interface(mem_ctx, ifaces[i].ip,
+ ifaces[i].netmask, local_interfaces);
+ }
+ }
+ }
+
+ while (ptr && *ptr) {
+ interpret_interface(mem_ctx, *ptr, ifaces, total_probed, local_interfaces);
+ ptr++;
+ }
+
+ if (!*local_interfaces) {
+ DEBUG(0,("WARNING: no network interfaces found\n"));
+ }
+}
+
+/**
+ how many interfaces do we have
+ **/
+int iface_count(struct interface *ifaces)
+{
+ int ret = 0;
+ struct interface *i;
+
+ for (i=ifaces;i;i=i->next)
+ ret++;
+ return ret;
+}
+
+/**
+ return IP of the Nth interface
+ **/
+const char *iface_n_ip(struct interface *ifaces, int n)
+{
+ struct interface *i;
+
+ for (i=ifaces;i && n;i=i->next)
+ n--;
+
+ if (i) {
+ return i->ip_s;
+ }
+ return NULL;
+}
+
+/**
+ return bcast of the Nth interface
+ **/
+const char *iface_n_bcast(struct interface *ifaces, int n)
+{
+ struct interface *i;
+
+ for (i=ifaces;i && n;i=i->next)
+ n--;
+
+ if (i) {
+ return i->bcast_s;
+ }
+ return NULL;
+}
+
+/**
+ return netmask of the Nth interface
+ **/
+const char *iface_n_netmask(struct interface *ifaces, int n)
+{
+ struct interface *i;
+
+ for (i=ifaces;i && n;i=i->next)
+ n--;
+
+ if (i) {
+ return i->nmask_s;
+ }
+ return NULL;
+}
+
+/**
+ return the local IP address that best matches a destination IP, or
+ our first interface if none match
+*/
+const char *iface_best_ip(struct interface *ifaces, const char *dest)
+{
+ struct interface *iface;
+ struct in_addr ip;
+
+ ip.s_addr = interpret_addr(dest);
+ iface = iface_find(ifaces, ip, true);
+ if (iface) {
+ return iface->ip_s;
+ }
+ return iface_n_ip(ifaces, 0);
+}
+
+/**
+ return true if an IP is one one of our local networks
+*/
+bool iface_is_local(struct interface *ifaces, const char *dest)
+{
+ struct in_addr ip;
+
+ ip.s_addr = interpret_addr(dest);
+ if (iface_find(ifaces, ip, true)) {
+ return true;
+ }
+ return false;
+}
+
+/**
+ return true if a IP matches a IP/netmask pair
+*/
+bool iface_same_net(const char *ip1, const char *ip2, const char *netmask)
+{
+ return same_net_v4(interpret_addr2(ip1),
+ interpret_addr2(ip2),
+ interpret_addr2(netmask));
+}
diff --git a/source4/lib/socket/netif.c b/source4/lib/socket/netif.c
new file mode 100644
index 0000000000..bf410af441
--- /dev/null
+++ b/source4/lib/socket/netif.c
@@ -0,0 +1,126 @@
+/*
+ Unix SMB/CIFS implementation.
+ return a list of network interfaces
+ Copyright (C) Andrew Tridgell 1998
+ Copyright (C) Jeremy Allison 2007
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+
+/* working out the interfaces for a OS is an incredibly non-portable
+ thing. We have several possible implementations below, and autoconf
+ tries each of them to see what works
+
+ Note that this file does _not_ include includes.h. That is so this code
+ can be called directly from the autoconf tests. That also means
+ this code cannot use any of the normal Samba debug stuff or defines.
+ This is standalone code.
+
+*/
+
+#include "includes.h"
+#include "system/network.h"
+#include "netif.h"
+
+/****************************************************************************
+ Try the "standard" getifaddrs/freeifaddrs interfaces.
+ Also gets IPv6 interfaces.
+****************************************************************************/
+
+/****************************************************************************
+ Get the netmask address for a local interface.
+****************************************************************************/
+
+static int _get_interfaces(struct iface_struct *ifaces, int max_interfaces)
+{
+ struct ifaddrs *iflist = NULL;
+ struct ifaddrs *ifptr = NULL;
+ int total = 0;
+
+ if (getifaddrs(&iflist) < 0) {
+ return -1;
+ }
+
+ /* Loop through interfaces, looking for given IP address */
+ for (ifptr = iflist, total = 0;
+ ifptr != NULL && total < max_interfaces;
+ ifptr = ifptr->ifa_next) {
+
+ memset(&ifaces[total], '\0', sizeof(ifaces[total]));
+
+ if (!ifptr->ifa_addr || !ifptr->ifa_netmask) {
+ continue;
+ }
+
+ /* Check the interface is up. */
+ if (!(ifptr->ifa_flags & IFF_UP)) {
+ continue;
+ }
+
+ /* We don't support IPv6 *yet* */
+ if (ifptr->ifa_addr->sa_family != AF_INET) {
+ continue;
+ }
+
+ ifaces[total].ip = ((struct sockaddr_in *)ifptr->ifa_addr)->sin_addr;
+ ifaces[total].netmask = ((struct sockaddr_in *)ifptr->ifa_netmask)->sin_addr;
+
+ strlcpy(ifaces[total].name, ifptr->ifa_name,
+ sizeof(ifaces[total].name));
+ total++;
+ }
+
+ freeifaddrs(iflist);
+
+ return total;
+}
+
+static int iface_comp(struct iface_struct *i1, struct iface_struct *i2)
+{
+ int r;
+ r = strcmp(i1->name, i2->name);
+ if (r) return r;
+ r = ntohl(i1->ip.s_addr) - ntohl(i2->ip.s_addr);
+ if (r) return r;
+ r = ntohl(i1->netmask.s_addr) - ntohl(i2->netmask.s_addr);
+ return r;
+}
+
+/* this wrapper is used to remove duplicates from the interface list generated
+ above */
+int get_interfaces(struct iface_struct *ifaces, int max_interfaces)
+{
+ int total, i, j;
+
+ total = _get_interfaces(ifaces, max_interfaces);
+ if (total <= 0) return total;
+
+ /* now we need to remove duplicates */
+ qsort(ifaces, total, sizeof(ifaces[0]), QSORT_CAST iface_comp);
+
+ for (i=1;i<total;) {
+ if (iface_comp(&ifaces[i-1], &ifaces[i]) == 0) {
+ for (j=i-1;j<total-1;j++) {
+ ifaces[j] = ifaces[j+1];
+ }
+ total--;
+ } else {
+ i++;
+ }
+ }
+
+ return total;
+}
diff --git a/source4/lib/socket/netif.h b/source4/lib/socket/netif.h
new file mode 100644
index 0000000000..417c6e074f
--- /dev/null
+++ b/source4/lib/socket/netif.h
@@ -0,0 +1,36 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ structures for lib/netif/
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "system/network.h"
+
+struct iface_struct {
+ char name[16];
+ struct in_addr ip;
+ struct in_addr netmask;
+};
+
+struct interface;
+
+#define MAX_INTERFACES 128
+
+#ifndef AUTOCONF_TEST
+#include "lib/socket/netif_proto.h"
+#endif
diff --git a/source4/lib/socket/socket.c b/source4/lib/socket/socket.c
new file mode 100644
index 0000000000..8e2f1683f2
--- /dev/null
+++ b/source4/lib/socket/socket.c
@@ -0,0 +1,596 @@
+/*
+ Unix SMB/CIFS implementation.
+ Socket functions
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Tim Potter 2000-2001
+ Copyright (C) Stefan Metzmacher 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/socket/socket.h"
+#include "system/filesys.h"
+#include "system/network.h"
+#include "param/param.h"
+
+/*
+ auto-close sockets on free
+*/
+static int socket_destructor(struct socket_context *sock)
+{
+ if (sock->ops->fn_close &&
+ !(sock->flags & SOCKET_FLAG_NOCLOSE)) {
+ sock->ops->fn_close(sock);
+ }
+ return 0;
+}
+
+_PUBLIC_ void socket_tevent_fd_close_fn(struct tevent_context *ev,
+ struct tevent_fd *fde,
+ int fd,
+ void *private_data)
+{
+ /* this might be the socket_wrapper swrap_close() */
+ close(fd);
+}
+
+_PUBLIC_ NTSTATUS socket_create_with_ops(TALLOC_CTX *mem_ctx, const struct socket_ops *ops,
+ struct socket_context **new_sock,
+ enum socket_type type, uint32_t flags)
+{
+ NTSTATUS status;
+
+ (*new_sock) = talloc(mem_ctx, struct socket_context);
+ if (!(*new_sock)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*new_sock)->type = type;
+ (*new_sock)->state = SOCKET_STATE_UNDEFINED;
+ (*new_sock)->flags = flags;
+
+ (*new_sock)->fd = -1;
+
+ (*new_sock)->private_data = NULL;
+ (*new_sock)->ops = ops;
+ (*new_sock)->backend_name = NULL;
+
+ status = (*new_sock)->ops->fn_init((*new_sock));
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(*new_sock);
+ return status;
+ }
+
+ /* by enabling "testnonblock" mode, all socket receive and
+ send calls on non-blocking sockets will randomly recv/send
+ less data than requested */
+
+ if (!(flags & SOCKET_FLAG_BLOCK) &&
+ type == SOCKET_TYPE_STREAM &&
+ getenv("SOCKET_TESTNONBLOCK") != NULL) {
+ (*new_sock)->flags |= SOCKET_FLAG_TESTNONBLOCK;
+ }
+
+ /* we don't do a connect() on dgram sockets, so need to set
+ non-blocking at socket create time */
+ if (!(flags & SOCKET_FLAG_BLOCK) && type == SOCKET_TYPE_DGRAM) {
+ set_blocking(socket_get_fd(*new_sock), false);
+ }
+
+ talloc_set_destructor(*new_sock, socket_destructor);
+
+ return NT_STATUS_OK;
+}
+
+_PUBLIC_ NTSTATUS socket_create(const char *name, enum socket_type type,
+ struct socket_context **new_sock, uint32_t flags)
+{
+ const struct socket_ops *ops;
+
+ ops = socket_getops_byname(name, type);
+ if (!ops) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ return socket_create_with_ops(NULL, ops, new_sock, type, flags);
+}
+
+_PUBLIC_ NTSTATUS socket_connect(struct socket_context *sock,
+ const struct socket_address *my_address,
+ const struct socket_address *server_address,
+ uint32_t flags)
+{
+ if (sock == NULL) {
+ return NT_STATUS_CONNECTION_DISCONNECTED;
+ }
+ if (sock->state != SOCKET_STATE_UNDEFINED) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!sock->ops->fn_connect) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ return sock->ops->fn_connect(sock, my_address, server_address, flags);
+}
+
+_PUBLIC_ NTSTATUS socket_connect_complete(struct socket_context *sock, uint32_t flags)
+{
+ if (!sock->ops->fn_connect_complete) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return sock->ops->fn_connect_complete(sock, flags);
+}
+
+_PUBLIC_ NTSTATUS socket_listen(struct socket_context *sock,
+ const struct socket_address *my_address,
+ int queue_size, uint32_t flags)
+{
+ if (sock == NULL) {
+ return NT_STATUS_CONNECTION_DISCONNECTED;
+ }
+ if (sock->state != SOCKET_STATE_UNDEFINED) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!sock->ops->fn_listen) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ return sock->ops->fn_listen(sock, my_address, queue_size, flags);
+}
+
+_PUBLIC_ NTSTATUS socket_accept(struct socket_context *sock, struct socket_context **new_sock)
+{
+ NTSTATUS status;
+
+ if (sock == NULL) {
+ return NT_STATUS_CONNECTION_DISCONNECTED;
+ }
+ if (sock->type != SOCKET_TYPE_STREAM) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (sock->state != SOCKET_STATE_SERVER_LISTEN) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!sock->ops->fn_accept) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ status = sock->ops->fn_accept(sock, new_sock);
+
+ if (NT_STATUS_IS_OK(status)) {
+ talloc_set_destructor(*new_sock, socket_destructor);
+ (*new_sock)->flags = 0;
+ }
+
+ return status;
+}
+
+_PUBLIC_ NTSTATUS socket_recv(struct socket_context *sock, void *buf,
+ size_t wantlen, size_t *nread)
+{
+ if (sock == NULL) {
+ return NT_STATUS_CONNECTION_DISCONNECTED;
+ }
+ if (sock->state != SOCKET_STATE_CLIENT_CONNECTED &&
+ sock->state != SOCKET_STATE_SERVER_CONNECTED &&
+ sock->type != SOCKET_TYPE_DGRAM) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!sock->ops->fn_recv) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if ((sock->flags & SOCKET_FLAG_TESTNONBLOCK)
+ && wantlen > 1) {
+
+ if (random() % 10 == 0) {
+ *nread = 0;
+ return STATUS_MORE_ENTRIES;
+ }
+ return sock->ops->fn_recv(sock, buf, 1+(random() % wantlen), nread);
+ }
+ return sock->ops->fn_recv(sock, buf, wantlen, nread);
+}
+
+_PUBLIC_ NTSTATUS socket_recvfrom(struct socket_context *sock, void *buf,
+ size_t wantlen, size_t *nread,
+ TALLOC_CTX *mem_ctx, struct socket_address **src_addr)
+{
+ if (sock == NULL) {
+ return NT_STATUS_CONNECTION_DISCONNECTED;
+ }
+ if (sock->type != SOCKET_TYPE_DGRAM) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!sock->ops->fn_recvfrom) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ return sock->ops->fn_recvfrom(sock, buf, wantlen, nread,
+ mem_ctx, src_addr);
+}
+
+_PUBLIC_ NTSTATUS socket_send(struct socket_context *sock,
+ const DATA_BLOB *blob, size_t *sendlen)
+{
+ if (sock == NULL) {
+ return NT_STATUS_CONNECTION_DISCONNECTED;
+ }
+ if (sock->state != SOCKET_STATE_CLIENT_CONNECTED &&
+ sock->state != SOCKET_STATE_SERVER_CONNECTED) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!sock->ops->fn_send) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if ((sock->flags & SOCKET_FLAG_TESTNONBLOCK)
+ && blob->length > 1) {
+ DATA_BLOB blob2 = *blob;
+ if (random() % 10 == 0) {
+ *sendlen = 0;
+ return STATUS_MORE_ENTRIES;
+ }
+ /* The random size sends are incompatible with TLS and SASL
+ * sockets, which require re-sends to be consistant */
+ if (!(sock->flags & SOCKET_FLAG_ENCRYPT)) {
+ blob2.length = 1+(random() % blob2.length);
+ } else {
+ /* This is particularly stressful on buggy
+ * LDAP clients, that don't expect on LDAP
+ * packet in many SASL packets */
+ blob2.length = 1 + blob2.length/2;
+ }
+ return sock->ops->fn_send(sock, &blob2, sendlen);
+ }
+ return sock->ops->fn_send(sock, blob, sendlen);
+}
+
+
+_PUBLIC_ NTSTATUS socket_sendto(struct socket_context *sock,
+ const DATA_BLOB *blob, size_t *sendlen,
+ const struct socket_address *dest_addr)
+{
+ if (sock == NULL) {
+ return NT_STATUS_CONNECTION_DISCONNECTED;
+ }
+ if (sock->type != SOCKET_TYPE_DGRAM) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (sock->state == SOCKET_STATE_CLIENT_CONNECTED ||
+ sock->state == SOCKET_STATE_SERVER_CONNECTED) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!sock->ops->fn_sendto) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ return sock->ops->fn_sendto(sock, blob, sendlen, dest_addr);
+}
+
+
+/*
+ ask for the number of bytes in a pending incoming packet
+*/
+_PUBLIC_ NTSTATUS socket_pending(struct socket_context *sock, size_t *npending)
+{
+ if (sock == NULL) {
+ return NT_STATUS_CONNECTION_DISCONNECTED;
+ }
+ if (!sock->ops->fn_pending) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return sock->ops->fn_pending(sock, npending);
+}
+
+
+_PUBLIC_ NTSTATUS socket_set_option(struct socket_context *sock, const char *option, const char *val)
+{
+ if (sock == NULL) {
+ return NT_STATUS_CONNECTION_DISCONNECTED;
+ }
+ if (!sock->ops->fn_set_option) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ return sock->ops->fn_set_option(sock, option, val);
+}
+
+_PUBLIC_ char *socket_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx)
+{
+ if (!sock->ops->fn_get_peer_name) {
+ return NULL;
+ }
+
+ return sock->ops->fn_get_peer_name(sock, mem_ctx);
+}
+
+_PUBLIC_ struct socket_address *socket_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
+{
+ if (!sock->ops->fn_get_peer_addr) {
+ return NULL;
+ }
+
+ return sock->ops->fn_get_peer_addr(sock, mem_ctx);
+}
+
+_PUBLIC_ struct socket_address *socket_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
+{
+ if (!sock->ops->fn_get_my_addr) {
+ return NULL;
+ }
+
+ return sock->ops->fn_get_my_addr(sock, mem_ctx);
+}
+
+_PUBLIC_ int socket_get_fd(struct socket_context *sock)
+{
+ if (!sock->ops->fn_get_fd) {
+ return -1;
+ }
+
+ return sock->ops->fn_get_fd(sock);
+}
+
+/*
+ call dup() on a socket, and close the old fd. This is used to change
+ the fd to the lowest available number, to make select() more
+ efficient (select speed depends on the maxiumum fd number passed to
+ it)
+*/
+_PUBLIC_ NTSTATUS socket_dup(struct socket_context *sock)
+{
+ int fd;
+ if (sock->fd == -1) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+ fd = dup(sock->fd);
+ if (fd == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+ close(sock->fd);
+ sock->fd = fd;
+ return NT_STATUS_OK;
+
+}
+
+/* Create a new socket_address. The type must match the socket type.
+ * The host parameter may be an IP or a hostname
+ */
+
+_PUBLIC_ struct socket_address *socket_address_from_strings(TALLOC_CTX *mem_ctx,
+ const char *family,
+ const char *host,
+ int port)
+{
+ struct socket_address *addr = talloc(mem_ctx, struct socket_address);
+ if (!addr) {
+ return NULL;
+ }
+
+ addr->family = family;
+ addr->addr = talloc_strdup(addr, host);
+ if (!addr->addr) {
+ talloc_free(addr);
+ return NULL;
+ }
+ addr->port = port;
+ addr->sockaddr = NULL;
+ addr->sockaddrlen = 0;
+
+ return addr;
+}
+
+/* Create a new socket_address. Copy the struct sockaddr into the new
+ * structure. Used for hooks in the kerberos libraries, where they
+ * supply only a struct sockaddr */
+
+_PUBLIC_ struct socket_address *socket_address_from_sockaddr(TALLOC_CTX *mem_ctx,
+ struct sockaddr *sockaddr,
+ size_t sockaddrlen)
+{
+ struct socket_address *addr = talloc(mem_ctx, struct socket_address);
+ if (!addr) {
+ return NULL;
+ }
+ addr->family = NULL;
+ addr->addr = NULL;
+ addr->port = 0;
+ addr->sockaddr = (struct sockaddr *)talloc_memdup(addr, sockaddr, sockaddrlen);
+ if (!addr->sockaddr) {
+ talloc_free(addr);
+ return NULL;
+ }
+ addr->sockaddrlen = sockaddrlen;
+ return addr;
+}
+
+/* Copy a socket_address structure */
+struct socket_address *socket_address_copy(TALLOC_CTX *mem_ctx,
+ const struct socket_address *oaddr)
+{
+ struct socket_address *addr = talloc_zero(mem_ctx, struct socket_address);
+ if (!addr) {
+ return NULL;
+ }
+ addr->family = oaddr->family;
+ if (oaddr->addr) {
+ addr->addr = talloc_strdup(addr, oaddr->addr);
+ if (!addr->addr) {
+ goto nomem;
+ }
+ }
+ addr->port = oaddr->port;
+ if (oaddr->sockaddr) {
+ addr->sockaddr = (struct sockaddr *)talloc_memdup(addr,
+ oaddr->sockaddr,
+ oaddr->sockaddrlen);
+ if (!addr->sockaddr) {
+ goto nomem;
+ }
+ addr->sockaddrlen = oaddr->sockaddrlen;
+ }
+
+ return addr;
+
+nomem:
+ talloc_free(addr);
+ return NULL;
+}
+
+_PUBLIC_ const struct socket_ops *socket_getops_byname(const char *family, enum socket_type type)
+{
+ extern const struct socket_ops *socket_ipv4_ops(enum socket_type);
+ extern const struct socket_ops *socket_ipv6_ops(enum socket_type);
+ extern const struct socket_ops *socket_unixdom_ops(enum socket_type);
+
+ if (strcmp("ip", family) == 0 ||
+ strcmp("ipv4", family) == 0) {
+ return socket_ipv4_ops(type);
+ }
+
+#if HAVE_IPV6
+ if (strcmp("ipv6", family) == 0) {
+ return socket_ipv6_ops(type);
+ }
+#endif
+
+ if (strcmp("unix", family) == 0) {
+ return socket_unixdom_ops(type);
+ }
+
+ return NULL;
+}
+
+enum SOCK_OPT_TYPES {OPT_BOOL,OPT_INT,OPT_ON};
+
+static const struct {
+ const char *name;
+ int level;
+ int option;
+ int value;
+ int opttype;
+} socket_options[] = {
+ {"SO_KEEPALIVE", SOL_SOCKET, SO_KEEPALIVE, 0, OPT_BOOL},
+ {"SO_REUSEADDR", SOL_SOCKET, SO_REUSEADDR, 0, OPT_BOOL},
+ {"SO_BROADCAST", SOL_SOCKET, SO_BROADCAST, 0, OPT_BOOL},
+#ifdef TCP_NODELAY
+ {"TCP_NODELAY", IPPROTO_TCP, TCP_NODELAY, 0, OPT_BOOL},
+#endif
+#ifdef IPTOS_LOWDELAY
+ {"IPTOS_LOWDELAY", IPPROTO_IP, IP_TOS, IPTOS_LOWDELAY, OPT_ON},
+#endif
+#ifdef IPTOS_THROUGHPUT
+ {"IPTOS_THROUGHPUT", IPPROTO_IP, IP_TOS, IPTOS_THROUGHPUT, OPT_ON},
+#endif
+#ifdef SO_REUSEPORT
+ {"SO_REUSEPORT", SOL_SOCKET, SO_REUSEPORT, 0, OPT_BOOL},
+#endif
+#ifdef SO_SNDBUF
+ {"SO_SNDBUF", SOL_SOCKET, SO_SNDBUF, 0, OPT_INT},
+#endif
+#ifdef SO_RCVBUF
+ {"SO_RCVBUF", SOL_SOCKET, SO_RCVBUF, 0, OPT_INT},
+#endif
+#ifdef SO_SNDLOWAT
+ {"SO_SNDLOWAT", SOL_SOCKET, SO_SNDLOWAT, 0, OPT_INT},
+#endif
+#ifdef SO_RCVLOWAT
+ {"SO_RCVLOWAT", SOL_SOCKET, SO_RCVLOWAT, 0, OPT_INT},
+#endif
+#ifdef SO_SNDTIMEO
+ {"SO_SNDTIMEO", SOL_SOCKET, SO_SNDTIMEO, 0, OPT_INT},
+#endif
+#ifdef SO_RCVTIMEO
+ {"SO_RCVTIMEO", SOL_SOCKET, SO_RCVTIMEO, 0, OPT_INT},
+#endif
+ {NULL,0,0,0,0}};
+
+
+/**
+ Set user socket options.
+**/
+_PUBLIC_ void set_socket_options(int fd, const char *options)
+{
+ const char **options_list = (const char **)str_list_make(NULL, options, " \t,");
+ int j;
+
+ if (!options_list)
+ return;
+
+ for (j = 0; options_list[j]; j++) {
+ const char *tok = options_list[j];
+ int ret=0,i;
+ int value = 1;
+ char *p;
+ bool got_value = false;
+
+ if ((p = strchr(tok,'='))) {
+ *p = 0;
+ value = atoi(p+1);
+ got_value = true;
+ }
+
+ for (i=0;socket_options[i].name;i++)
+ if (strequal(socket_options[i].name,tok))
+ break;
+
+ if (!socket_options[i].name) {
+ DEBUG(0,("Unknown socket option %s\n",tok));
+ continue;
+ }
+
+ switch (socket_options[i].opttype) {
+ case OPT_BOOL:
+ case OPT_INT:
+ ret = setsockopt(fd,socket_options[i].level,
+ socket_options[i].option,(char *)&value,sizeof(int));
+ break;
+
+ case OPT_ON:
+ if (got_value)
+ DEBUG(0,("syntax error - %s does not take a value\n",tok));
+
+ {
+ int on = socket_options[i].value;
+ ret = setsockopt(fd,socket_options[i].level,
+ socket_options[i].option,(char *)&on,sizeof(int));
+ }
+ break;
+ }
+
+ if (ret != 0)
+ DEBUG(0,("Failed to set socket option %s (Error %s)\n",tok, strerror(errno) ));
+ }
+
+ talloc_free(options_list);
+}
+
+/*
+ set some flags on a socket
+ */
+void socket_set_flags(struct socket_context *sock, unsigned flags)
+{
+ sock->flags |= flags;
+}
diff --git a/source4/lib/socket/socket.h b/source4/lib/socket/socket.h
new file mode 100644
index 0000000000..0f469e5ceb
--- /dev/null
+++ b/source4/lib/socket/socket.h
@@ -0,0 +1,218 @@
+/*
+ Unix SMB/CIFS implementation.
+ Socket functions
+ Copyright (C) Stefan Metzmacher 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _SAMBA_SOCKET_H
+#define _SAMBA_SOCKET_H
+
+struct tevent_context;
+struct tevent_fd;
+struct socket_context;
+
+enum socket_type {
+ SOCKET_TYPE_STREAM,
+ SOCKET_TYPE_DGRAM
+};
+
+struct socket_address {
+ const char *family;
+ char *addr;
+ int port;
+ struct sockaddr *sockaddr;
+ size_t sockaddrlen;
+};
+
+struct socket_ops {
+ const char *name;
+
+ NTSTATUS (*fn_init)(struct socket_context *sock);
+
+ /* client ops */
+ NTSTATUS (*fn_connect)(struct socket_context *sock,
+ const struct socket_address *my_address,
+ const struct socket_address *server_address,
+ uint32_t flags);
+
+ /* complete a non-blocking connect */
+ NTSTATUS (*fn_connect_complete)(struct socket_context *sock,
+ uint32_t flags);
+
+ /* server ops */
+ NTSTATUS (*fn_listen)(struct socket_context *sock,
+ const struct socket_address *my_address,
+ int queue_size, uint32_t flags);
+ NTSTATUS (*fn_accept)(struct socket_context *sock,
+ struct socket_context **new_sock);
+
+ /* general ops */
+ NTSTATUS (*fn_recv)(struct socket_context *sock, void *buf,
+ size_t wantlen, size_t *nread);
+ NTSTATUS (*fn_send)(struct socket_context *sock,
+ const DATA_BLOB *blob, size_t *sendlen);
+
+ NTSTATUS (*fn_sendto)(struct socket_context *sock,
+ const DATA_BLOB *blob, size_t *sendlen,
+ const struct socket_address *dest_addr);
+ NTSTATUS (*fn_recvfrom)(struct socket_context *sock,
+ void *buf, size_t wantlen, size_t *nread,
+ TALLOC_CTX *addr_ctx, struct socket_address **src_addr);
+ NTSTATUS (*fn_pending)(struct socket_context *sock, size_t *npending);
+
+ void (*fn_close)(struct socket_context *sock);
+
+ NTSTATUS (*fn_set_option)(struct socket_context *sock, const char *option, const char *val);
+
+ char *(*fn_get_peer_name)(struct socket_context *sock, TALLOC_CTX *mem_ctx);
+ struct socket_address *(*fn_get_peer_addr)(struct socket_context *sock, TALLOC_CTX *mem_ctx);
+ struct socket_address *(*fn_get_my_addr)(struct socket_context *sock, TALLOC_CTX *mem_ctx);
+
+ int (*fn_get_fd)(struct socket_context *sock);
+};
+
+enum socket_state {
+ SOCKET_STATE_UNDEFINED,
+
+ SOCKET_STATE_CLIENT_START,
+ SOCKET_STATE_CLIENT_CONNECTED,
+ SOCKET_STATE_CLIENT_STARTTLS,
+ SOCKET_STATE_CLIENT_ERROR,
+
+ SOCKET_STATE_SERVER_LISTEN,
+ SOCKET_STATE_SERVER_CONNECTED,
+ SOCKET_STATE_SERVER_STARTTLS,
+ SOCKET_STATE_SERVER_ERROR
+};
+
+#define SOCKET_FLAG_BLOCK 0x00000001
+#define SOCKET_FLAG_PEEK 0x00000002
+#define SOCKET_FLAG_TESTNONBLOCK 0x00000004
+#define SOCKET_FLAG_ENCRYPT 0x00000008 /* This socket
+ * implementation requires
+ * that re-sends be
+ * consistant, because it
+ * is encrypting data.
+ * This modifies the
+ * TESTNONBLOCK case */
+#define SOCKET_FLAG_NOCLOSE 0x00000010 /* don't auto-close on free */
+
+
+struct socket_context {
+ enum socket_type type;
+ enum socket_state state;
+ uint32_t flags;
+
+ int fd;
+
+ void *private_data;
+ const struct socket_ops *ops;
+ const char *backend_name;
+
+ /* specific to the ip backend */
+ int family;
+};
+
+struct resolve_context;
+
+/* prototypes */
+NTSTATUS socket_create_with_ops(TALLOC_CTX *mem_ctx, const struct socket_ops *ops,
+ struct socket_context **new_sock,
+ enum socket_type type, uint32_t flags);
+NTSTATUS socket_create(const char *name, enum socket_type type,
+ struct socket_context **new_sock, uint32_t flags);
+NTSTATUS socket_connect(struct socket_context *sock,
+ const struct socket_address *my_address,
+ const struct socket_address *server_address,
+ uint32_t flags);
+NTSTATUS socket_connect_complete(struct socket_context *sock, uint32_t flags);
+NTSTATUS socket_listen(struct socket_context *sock,
+ const struct socket_address *my_address,
+ int queue_size, uint32_t flags);
+NTSTATUS socket_accept(struct socket_context *sock, struct socket_context **new_sock);
+NTSTATUS socket_recv(struct socket_context *sock, void *buf,
+ size_t wantlen, size_t *nread);
+NTSTATUS socket_recvfrom(struct socket_context *sock, void *buf,
+ size_t wantlen, size_t *nread,
+ TALLOC_CTX *addr_ctx, struct socket_address **src_addr);
+NTSTATUS socket_send(struct socket_context *sock,
+ const DATA_BLOB *blob, size_t *sendlen);
+NTSTATUS socket_sendto(struct socket_context *sock,
+ const DATA_BLOB *blob, size_t *sendlen,
+ const struct socket_address *dest_addr);
+NTSTATUS socket_pending(struct socket_context *sock, size_t *npending);
+NTSTATUS socket_set_option(struct socket_context *sock, const char *option, const char *val);
+char *socket_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx);
+struct socket_address *socket_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx);
+struct socket_address *socket_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx);
+int socket_get_fd(struct socket_context *sock);
+NTSTATUS socket_dup(struct socket_context *sock);
+struct socket_address *socket_address_from_strings(TALLOC_CTX *mem_ctx,
+ const char *type,
+ const char *host,
+ int port);
+struct socket_address *socket_address_from_sockaddr(TALLOC_CTX *mem_ctx,
+ struct sockaddr *sockaddr,
+ size_t addrlen);
+struct socket_address *socket_address_copy(TALLOC_CTX *mem_ctx,
+ const struct socket_address *oaddr);
+const struct socket_ops *socket_getops_byname(const char *name, enum socket_type type);
+bool allow_access(TALLOC_CTX *mem_ctx,
+ const char **deny_list, const char **allow_list,
+ const char *cname, const char *caddr);
+bool socket_check_access(struct socket_context *sock,
+ const char *service_name,
+ const char **allow_list, const char **deny_list);
+
+struct composite_context *socket_connect_send(struct socket_context *sock,
+ struct socket_address *my_address,
+ struct socket_address *server_address,
+ uint32_t flags,
+ struct tevent_context *event_ctx);
+NTSTATUS socket_connect_recv(struct composite_context *ctx);
+NTSTATUS socket_connect_ev(struct socket_context *sock,
+ struct socket_address *my_address,
+ struct socket_address *server_address,
+ uint32_t flags,
+ struct tevent_context *ev);
+
+struct composite_context *socket_connect_multi_send(TALLOC_CTX *mem_ctx,
+ const char *server_address,
+ int num_server_ports,
+ uint16_t *server_ports,
+ struct resolve_context *resolve_ctx,
+ struct tevent_context *event_ctx);
+NTSTATUS socket_connect_multi_recv(struct composite_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct socket_context **result,
+ uint16_t *port);
+NTSTATUS socket_connect_multi(TALLOC_CTX *mem_ctx, const char *server_address,
+ int num_server_ports, uint16_t *server_ports,
+ struct resolve_context *resolve_ctx,
+ struct tevent_context *event_ctx,
+ struct socket_context **result,
+ uint16_t *port);
+void set_socket_options(int fd, const char *options);
+void socket_set_flags(struct socket_context *socket, unsigned flags);
+
+void socket_tevent_fd_close_fn(struct tevent_context *ev,
+ struct tevent_fd *fde,
+ int fd,
+ void *private_data);
+
+extern bool testnonblock;
+
+#endif /* _SAMBA_SOCKET_H */
diff --git a/source4/lib/socket/socket_ip.c b/source4/lib/socket/socket_ip.c
new file mode 100644
index 0000000000..89b310a23a
--- /dev/null
+++ b/source4/lib/socket/socket_ip.c
@@ -0,0 +1,1004 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Socket IPv4/IPv6 functions
+
+ Copyright (C) Stefan Metzmacher 2004
+ Copyright (C) Andrew Tridgell 2004-2005
+ Copyright (C) Jelmer Vernooij 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "lib/socket/socket.h"
+#include "system/network.h"
+
+static NTSTATUS ipv4_init(struct socket_context *sock)
+{
+ int type;
+
+ switch (sock->type) {
+ case SOCKET_TYPE_STREAM:
+ type = SOCK_STREAM;
+ break;
+ case SOCKET_TYPE_DGRAM:
+ type = SOCK_DGRAM;
+ break;
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ sock->fd = socket(PF_INET, type, 0);
+ if (sock->fd == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ sock->backend_name = "ipv4";
+ sock->family = AF_INET;
+
+ return NT_STATUS_OK;
+}
+
+static void ip_close(struct socket_context *sock)
+{
+ close(sock->fd);
+}
+
+static NTSTATUS ip_connect_complete(struct socket_context *sock, uint32_t flags)
+{
+ int error=0, ret;
+ socklen_t len = sizeof(error);
+
+ /* check for any errors that may have occurred - this is needed
+ for non-blocking connect */
+ ret = getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, &error, &len);
+ if (ret == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+ if (error != 0) {
+ return map_nt_error_from_unix(error);
+ }
+
+ if (!(flags & SOCKET_FLAG_BLOCK)) {
+ ret = set_blocking(sock->fd, false);
+ if (ret == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+ }
+
+ sock->state = SOCKET_STATE_CLIENT_CONNECTED;
+
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS ipv4_connect(struct socket_context *sock,
+ const struct socket_address *my_address,
+ const struct socket_address *srv_address,
+ uint32_t flags)
+{
+ struct sockaddr_in srv_addr;
+ struct in_addr my_ip;
+ struct in_addr srv_ip;
+ int ret;
+
+ if (my_address && my_address->sockaddr) {
+ ret = bind(sock->fd, my_address->sockaddr, my_address->sockaddrlen);
+ if (ret == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+ } else if (my_address) {
+ my_ip = interpret_addr2(my_address->addr);
+
+ if (my_ip.s_addr != 0 || my_address->port != 0) {
+ struct sockaddr_in my_addr;
+ ZERO_STRUCT(my_addr);
+#ifdef HAVE_SOCK_SIN_LEN
+ my_addr.sin_len = sizeof(my_addr);
+#endif
+ my_addr.sin_addr.s_addr = my_ip.s_addr;
+ my_addr.sin_port = htons(my_address->port);
+ my_addr.sin_family = PF_INET;
+
+ ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
+ if (ret == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+ }
+ }
+
+ if (srv_address->sockaddr) {
+ ret = connect(sock->fd, srv_address->sockaddr, srv_address->sockaddrlen);
+ if (ret == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+ } else {
+ srv_ip = interpret_addr2(srv_address->addr);
+ if (!srv_ip.s_addr) {
+ return NT_STATUS_BAD_NETWORK_NAME;
+ }
+
+ SMB_ASSERT(srv_address->port != 0);
+
+ ZERO_STRUCT(srv_addr);
+#ifdef HAVE_SOCK_SIN_LEN
+ srv_addr.sin_len = sizeof(srv_addr);
+#endif
+ srv_addr.sin_addr.s_addr= srv_ip.s_addr;
+ srv_addr.sin_port = htons(srv_address->port);
+ srv_addr.sin_family = PF_INET;
+
+ ret = connect(sock->fd, (const struct sockaddr *)&srv_addr, sizeof(srv_addr));
+ if (ret == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+ }
+
+ return ip_connect_complete(sock, flags);
+}
+
+
+/*
+ note that for simplicity of the API, socket_listen() is also
+ use for DGRAM sockets, but in reality only a bind() is done
+*/
+static NTSTATUS ipv4_listen(struct socket_context *sock,
+ const struct socket_address *my_address,
+ int queue_size, uint32_t flags)
+{
+ struct sockaddr_in my_addr;
+ struct in_addr ip_addr;
+ int ret;
+
+ socket_set_option(sock, "SO_REUSEADDR=1", NULL);
+
+ if (my_address->sockaddr) {
+ ret = bind(sock->fd, my_address->sockaddr, my_address->sockaddrlen);
+ } else {
+ ip_addr = interpret_addr2(my_address->addr);
+
+ ZERO_STRUCT(my_addr);
+#ifdef HAVE_SOCK_SIN_LEN
+ my_addr.sin_len = sizeof(my_addr);
+#endif
+ my_addr.sin_addr.s_addr = ip_addr.s_addr;
+ my_addr.sin_port = htons(my_address->port);
+ my_addr.sin_family = PF_INET;
+
+ ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
+ }
+
+ if (ret == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ if (sock->type == SOCKET_TYPE_STREAM) {
+ ret = listen(sock->fd, queue_size);
+ if (ret == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+ }
+
+ if (!(flags & SOCKET_FLAG_BLOCK)) {
+ ret = set_blocking(sock->fd, false);
+ if (ret == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+ }
+
+ sock->state= SOCKET_STATE_SERVER_LISTEN;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ipv4_accept(struct socket_context *sock, struct socket_context **new_sock)
+{
+ struct sockaddr_in cli_addr;
+ socklen_t cli_addr_len = sizeof(cli_addr);
+ int new_fd;
+
+ if (sock->type != SOCKET_TYPE_STREAM) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ new_fd = accept(sock->fd, (struct sockaddr *)&cli_addr, &cli_addr_len);
+ if (new_fd == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ if (!(sock->flags & SOCKET_FLAG_BLOCK)) {
+ int ret = set_blocking(new_fd, false);
+ if (ret == -1) {
+ close(new_fd);
+ return map_nt_error_from_unix(errno);
+ }
+ }
+
+ /* TODO: we could add a 'accept_check' hook here
+ * which get the black/white lists via socket_set_accept_filter()
+ * or something like that
+ * --metze
+ */
+
+ (*new_sock) = talloc(NULL, struct socket_context);
+ if (!(*new_sock)) {
+ close(new_fd);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* copy the socket_context */
+ (*new_sock)->type = sock->type;
+ (*new_sock)->state = SOCKET_STATE_SERVER_CONNECTED;
+ (*new_sock)->flags = sock->flags;
+
+ (*new_sock)->fd = new_fd;
+
+ (*new_sock)->private_data = NULL;
+ (*new_sock)->ops = sock->ops;
+ (*new_sock)->backend_name = sock->backend_name;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ip_recv(struct socket_context *sock, void *buf,
+ size_t wantlen, size_t *nread)
+{
+ ssize_t gotlen;
+
+ *nread = 0;
+
+ gotlen = recv(sock->fd, buf, wantlen, 0);
+ if (gotlen == 0) {
+ return NT_STATUS_END_OF_FILE;
+ } else if (gotlen == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ *nread = gotlen;
+
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS ipv4_recvfrom(struct socket_context *sock, void *buf,
+ size_t wantlen, size_t *nread,
+ TALLOC_CTX *addr_ctx, struct socket_address **_src)
+{
+ ssize_t gotlen;
+ struct sockaddr_in *from_addr;
+ socklen_t from_len = sizeof(*from_addr);
+ struct socket_address *src;
+ char addrstring[INET_ADDRSTRLEN];
+
+ src = talloc(addr_ctx, struct socket_address);
+ if (!src) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ src->family = sock->backend_name;
+
+ from_addr = talloc(src, struct sockaddr_in);
+ if (!from_addr) {
+ talloc_free(src);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ src->sockaddr = (struct sockaddr *)from_addr;
+
+ *nread = 0;
+
+ gotlen = recvfrom(sock->fd, buf, wantlen, 0,
+ src->sockaddr, &from_len);
+ if (gotlen == 0) {
+ talloc_free(src);
+ return NT_STATUS_END_OF_FILE;
+ } else if (gotlen == -1) {
+ talloc_free(src);
+ return map_nt_error_from_unix(errno);
+ }
+
+ src->sockaddrlen = from_len;
+
+ if (inet_ntop(AF_INET, &from_addr->sin_addr, addrstring,
+ sizeof(addrstring)) == NULL) {
+ talloc_free(src);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ src->addr = talloc_strdup(src, addrstring);
+ if (src->addr == NULL) {
+ talloc_free(src);
+ return NT_STATUS_NO_MEMORY;
+ }
+ src->port = ntohs(from_addr->sin_port);
+
+ *nread = gotlen;
+ *_src = src;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ip_send(struct socket_context *sock,
+ const DATA_BLOB *blob, size_t *sendlen)
+{
+ ssize_t len;
+
+ *sendlen = 0;
+
+ len = send(sock->fd, blob->data, blob->length, 0);
+ if (len == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ *sendlen = len;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ipv4_sendto(struct socket_context *sock,
+ const DATA_BLOB *blob, size_t *sendlen,
+ const struct socket_address *dest_addr)
+{
+ ssize_t len;
+
+ if (dest_addr->sockaddr) {
+ len = sendto(sock->fd, blob->data, blob->length, 0,
+ dest_addr->sockaddr, dest_addr->sockaddrlen);
+ } else {
+ struct sockaddr_in srv_addr;
+ struct in_addr addr;
+
+ SMB_ASSERT(dest_addr->port != 0);
+
+ ZERO_STRUCT(srv_addr);
+#ifdef HAVE_SOCK_SIN_LEN
+ srv_addr.sin_len = sizeof(srv_addr);
+#endif
+ addr = interpret_addr2(dest_addr->addr);
+ if (addr.s_addr == 0) {
+ return NT_STATUS_HOST_UNREACHABLE;
+ }
+ srv_addr.sin_addr.s_addr = addr.s_addr;
+ srv_addr.sin_port = htons(dest_addr->port);
+ srv_addr.sin_family = PF_INET;
+
+ *sendlen = 0;
+
+ len = sendto(sock->fd, blob->data, blob->length, 0,
+ (struct sockaddr *)&srv_addr, sizeof(srv_addr));
+ }
+ if (len == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ *sendlen = len;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ipv4_set_option(struct socket_context *sock, const char *option, const char *val)
+{
+ set_socket_options(sock->fd, option);
+ return NT_STATUS_OK;
+}
+
+static char *ipv4_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx)
+{
+ struct sockaddr_in peer_addr;
+ socklen_t len = sizeof(peer_addr);
+ struct hostent *he;
+ int ret;
+
+ ret = getpeername(sock->fd, (struct sockaddr *)&peer_addr, &len);
+ if (ret == -1) {
+ return NULL;
+ }
+
+ he = gethostbyaddr((char *)&peer_addr.sin_addr, sizeof(peer_addr.sin_addr), AF_INET);
+ if (he == NULL) {
+ return NULL;
+ }
+
+ return talloc_strdup(mem_ctx, he->h_name);
+}
+
+static struct socket_address *ipv4_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
+{
+ struct sockaddr_in *peer_addr;
+ socklen_t len = sizeof(*peer_addr);
+ struct socket_address *peer;
+ char addrstring[INET_ADDRSTRLEN];
+ int ret;
+
+ peer = talloc(mem_ctx, struct socket_address);
+ if (!peer) {
+ return NULL;
+ }
+
+ peer->family = sock->backend_name;
+ peer_addr = talloc(peer, struct sockaddr_in);
+ if (!peer_addr) {
+ talloc_free(peer);
+ return NULL;
+ }
+
+ peer->sockaddr = (struct sockaddr *)peer_addr;
+
+ ret = getpeername(sock->fd, peer->sockaddr, &len);
+ if (ret == -1) {
+ talloc_free(peer);
+ return NULL;
+ }
+
+ peer->sockaddrlen = len;
+
+ if (inet_ntop(AF_INET, &peer_addr->sin_addr, addrstring,
+ sizeof(addrstring)) == NULL) {
+ talloc_free(peer);
+ return NULL;
+ }
+ peer->addr = talloc_strdup(peer, addrstring);
+ if (!peer->addr) {
+ talloc_free(peer);
+ return NULL;
+ }
+ peer->port = ntohs(peer_addr->sin_port);
+
+ return peer;
+}
+
+static struct socket_address *ipv4_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
+{
+ struct sockaddr_in *local_addr;
+ socklen_t len = sizeof(*local_addr);
+ struct socket_address *local;
+ char addrstring[INET_ADDRSTRLEN];
+ int ret;
+
+ local = talloc(mem_ctx, struct socket_address);
+ if (!local) {
+ return NULL;
+ }
+
+ local->family = sock->backend_name;
+ local_addr = talloc(local, struct sockaddr_in);
+ if (!local_addr) {
+ talloc_free(local);
+ return NULL;
+ }
+
+ local->sockaddr = (struct sockaddr *)local_addr;
+
+ ret = getsockname(sock->fd, local->sockaddr, &len);
+ if (ret == -1) {
+ talloc_free(local);
+ return NULL;
+ }
+
+ local->sockaddrlen = len;
+
+ if (inet_ntop(AF_INET, &local_addr->sin_addr, addrstring,
+ sizeof(addrstring)) == NULL) {
+ talloc_free(local);
+ return NULL;
+ }
+ local->addr = talloc_strdup(local, addrstring);
+ if (!local->addr) {
+ talloc_free(local);
+ return NULL;
+ }
+ local->port = ntohs(local_addr->sin_port);
+
+ return local;
+}
+static int ip_get_fd(struct socket_context *sock)
+{
+ return sock->fd;
+}
+
+static NTSTATUS ip_pending(struct socket_context *sock, size_t *npending)
+{
+ int value = 0;
+ if (ioctl(sock->fd, FIONREAD, &value) == 0) {
+ *npending = value;
+ return NT_STATUS_OK;
+ }
+ return map_nt_error_from_unix(errno);
+}
+
+static const struct socket_ops ipv4_ops = {
+ .name = "ipv4",
+ .fn_init = ipv4_init,
+ .fn_connect = ipv4_connect,
+ .fn_connect_complete = ip_connect_complete,
+ .fn_listen = ipv4_listen,
+ .fn_accept = ipv4_accept,
+ .fn_recv = ip_recv,
+ .fn_recvfrom = ipv4_recvfrom,
+ .fn_send = ip_send,
+ .fn_sendto = ipv4_sendto,
+ .fn_pending = ip_pending,
+ .fn_close = ip_close,
+
+ .fn_set_option = ipv4_set_option,
+
+ .fn_get_peer_name = ipv4_get_peer_name,
+ .fn_get_peer_addr = ipv4_get_peer_addr,
+ .fn_get_my_addr = ipv4_get_my_addr,
+
+ .fn_get_fd = ip_get_fd
+};
+
+_PUBLIC_ const struct socket_ops *socket_ipv4_ops(enum socket_type type)
+{
+ return &ipv4_ops;
+}
+
+#if HAVE_IPV6
+
+static struct in6_addr interpret_addr6(const char *name)
+{
+ char addr[INET6_ADDRSTRLEN];
+ struct in6_addr dest6;
+ const char *sp = name;
+ char *p;
+ int ret;
+
+ if (sp == NULL) return in6addr_any;
+
+ p = strchr_m(sp, '%');
+
+ if (strcasecmp(sp, "localhost") == 0) {
+ sp = "::1";
+ }
+
+ /*
+ * Cope with link-local.
+ * This is IP:v6:addr%ifname.
+ */
+
+ if (p && (p > sp) && (if_nametoindex(p+1) != 0)) {
+ strlcpy(addr, sp,
+ MIN(PTR_DIFF(p,sp)+1,
+ sizeof(addr)));
+ sp = addr;
+ }
+
+ ret = inet_pton(AF_INET6, sp, &dest6);
+ if (ret > 0) {
+ return dest6;
+ }
+
+ return in6addr_any;
+}
+
+static NTSTATUS ipv6_init(struct socket_context *sock)
+{
+ int type;
+
+ switch (sock->type) {
+ case SOCKET_TYPE_STREAM:
+ type = SOCK_STREAM;
+ break;
+ case SOCKET_TYPE_DGRAM:
+ type = SOCK_DGRAM;
+ break;
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ sock->fd = socket(PF_INET6, type, 0);
+ if (sock->fd == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ sock->backend_name = "ipv6";
+ sock->family = AF_INET6;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ipv6_tcp_connect(struct socket_context *sock,
+ const struct socket_address *my_address,
+ const struct socket_address *srv_address,
+ uint32_t flags)
+{
+ int ret;
+
+ if (my_address && my_address->sockaddr) {
+ ret = bind(sock->fd, my_address->sockaddr, my_address->sockaddrlen);
+ if (ret == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+ } else if (my_address) {
+ struct in6_addr my_ip;
+ my_ip = interpret_addr6(my_address->addr);
+
+ if (memcmp(&my_ip, &in6addr_any, sizeof(my_ip)) || my_address->port != 0) {
+ struct sockaddr_in6 my_addr;
+ ZERO_STRUCT(my_addr);
+ my_addr.sin6_addr = my_ip;
+ my_addr.sin6_port = htons(my_address->port);
+ my_addr.sin6_family = PF_INET6;
+
+ ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
+ if (ret == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+ }
+ }
+
+ if (srv_address->sockaddr) {
+ ret = connect(sock->fd, srv_address->sockaddr, srv_address->sockaddrlen);
+ } else {
+ struct in6_addr srv_ip;
+ struct sockaddr_in6 srv_addr;
+ srv_ip = interpret_addr6(srv_address->addr);
+ if (memcmp(&srv_ip, &in6addr_any, sizeof(srv_ip)) == 0) {
+ return NT_STATUS_BAD_NETWORK_NAME;
+ }
+
+ ZERO_STRUCT(srv_addr);
+ srv_addr.sin6_addr = srv_ip;
+ srv_addr.sin6_port = htons(srv_address->port);
+ srv_addr.sin6_family = PF_INET6;
+
+ ret = connect(sock->fd, (const struct sockaddr *)&srv_addr, sizeof(srv_addr));
+ }
+ if (ret == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ return ip_connect_complete(sock, flags);
+}
+
+static NTSTATUS ipv6_listen(struct socket_context *sock,
+ const struct socket_address *my_address,
+ int queue_size, uint32_t flags)
+{
+ struct sockaddr_in6 my_addr;
+ struct in6_addr ip_addr;
+ int ret;
+
+ socket_set_option(sock, "SO_REUSEADDR=1", NULL);
+
+ if (my_address->sockaddr) {
+ ret = bind(sock->fd, my_address->sockaddr, my_address->sockaddrlen);
+ } else {
+ ip_addr = interpret_addr6(my_address->addr);
+
+ ZERO_STRUCT(my_addr);
+ my_addr.sin6_addr = ip_addr;
+ my_addr.sin6_port = htons(my_address->port);
+ my_addr.sin6_family = PF_INET6;
+
+ ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
+ }
+
+ if (ret == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ if (sock->type == SOCKET_TYPE_STREAM) {
+ ret = listen(sock->fd, queue_size);
+ if (ret == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+ }
+
+ if (!(flags & SOCKET_FLAG_BLOCK)) {
+ ret = set_blocking(sock->fd, false);
+ if (ret == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+ }
+
+ sock->state= SOCKET_STATE_SERVER_LISTEN;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ipv6_tcp_accept(struct socket_context *sock, struct socket_context **new_sock)
+{
+ struct sockaddr_in cli_addr;
+ socklen_t cli_addr_len = sizeof(cli_addr);
+ int new_fd;
+
+ if (sock->type != SOCKET_TYPE_STREAM) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ new_fd = accept(sock->fd, (struct sockaddr *)&cli_addr, &cli_addr_len);
+ if (new_fd == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ if (!(sock->flags & SOCKET_FLAG_BLOCK)) {
+ int ret = set_blocking(new_fd, false);
+ if (ret == -1) {
+ close(new_fd);
+ return map_nt_error_from_unix(errno);
+ }
+ }
+
+ /* TODO: we could add a 'accept_check' hook here
+ * which get the black/white lists via socket_set_accept_filter()
+ * or something like that
+ * --metze
+ */
+
+ (*new_sock) = talloc(NULL, struct socket_context);
+ if (!(*new_sock)) {
+ close(new_fd);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* copy the socket_context */
+ (*new_sock)->type = sock->type;
+ (*new_sock)->state = SOCKET_STATE_SERVER_CONNECTED;
+ (*new_sock)->flags = sock->flags;
+
+ (*new_sock)->fd = new_fd;
+
+ (*new_sock)->private_data = NULL;
+ (*new_sock)->ops = sock->ops;
+ (*new_sock)->backend_name = sock->backend_name;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ipv6_recvfrom(struct socket_context *sock, void *buf,
+ size_t wantlen, size_t *nread,
+ TALLOC_CTX *addr_ctx, struct socket_address **_src)
+{
+ ssize_t gotlen;
+ struct sockaddr_in6 *from_addr;
+ socklen_t from_len = sizeof(*from_addr);
+ struct socket_address *src;
+ char addrstring[INET6_ADDRSTRLEN];
+
+ src = talloc(addr_ctx, struct socket_address);
+ if (!src) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ src->family = sock->backend_name;
+
+ from_addr = talloc(src, struct sockaddr_in6);
+ if (!from_addr) {
+ talloc_free(src);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ src->sockaddr = (struct sockaddr *)from_addr;
+
+ *nread = 0;
+
+ gotlen = recvfrom(sock->fd, buf, wantlen, 0,
+ src->sockaddr, &from_len);
+ if (gotlen == 0) {
+ talloc_free(src);
+ return NT_STATUS_END_OF_FILE;
+ } else if (gotlen == -1) {
+ talloc_free(src);
+ return map_nt_error_from_unix(errno);
+ }
+
+ src->sockaddrlen = from_len;
+
+ if (inet_ntop(AF_INET6, &from_addr->sin6_addr, addrstring, sizeof(addrstring)) == NULL) {
+ DEBUG(0, ("Unable to convert address to string: %s\n", strerror(errno)));
+ talloc_free(src);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ src->addr = talloc_strdup(src, addrstring);
+ if (src->addr == NULL) {
+ talloc_free(src);
+ return NT_STATUS_NO_MEMORY;
+ }
+ src->port = ntohs(from_addr->sin6_port);
+
+ *nread = gotlen;
+ *_src = src;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ipv6_sendto(struct socket_context *sock,
+ const DATA_BLOB *blob, size_t *sendlen,
+ const struct socket_address *dest_addr)
+{
+ ssize_t len;
+
+ if (dest_addr->sockaddr) {
+ len = sendto(sock->fd, blob->data, blob->length, 0,
+ dest_addr->sockaddr, dest_addr->sockaddrlen);
+ } else {
+ struct sockaddr_in6 srv_addr;
+ struct in6_addr addr;
+
+ ZERO_STRUCT(srv_addr);
+ addr = interpret_addr6(dest_addr->addr);
+ if (addr.s6_addr == 0) {
+ return NT_STATUS_HOST_UNREACHABLE;
+ }
+ srv_addr.sin6_addr = addr;
+ srv_addr.sin6_port = htons(dest_addr->port);
+ srv_addr.sin6_family = PF_INET6;
+
+ *sendlen = 0;
+
+ len = sendto(sock->fd, blob->data, blob->length, 0,
+ (struct sockaddr *)&srv_addr, sizeof(srv_addr));
+ }
+ if (len == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ *sendlen = len;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ipv6_set_option(struct socket_context *sock, const char *option, const char *val)
+{
+ set_socket_options(sock->fd, option);
+ return NT_STATUS_OK;
+}
+
+static char *ipv6_tcp_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx)
+{
+ struct sockaddr_in6 peer_addr;
+ socklen_t len = sizeof(peer_addr);
+ struct hostent *he;
+ int ret;
+
+ ret = getpeername(sock->fd, (struct sockaddr *)&peer_addr, &len);
+ if (ret == -1) {
+ return NULL;
+ }
+
+ he = gethostbyaddr((char *)&peer_addr.sin6_addr, sizeof(peer_addr.sin6_addr), AF_INET6);
+ if (he == NULL) {
+ return NULL;
+ }
+
+ return talloc_strdup(mem_ctx, he->h_name);
+}
+
+static struct socket_address *ipv6_tcp_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
+{
+ struct sockaddr_in6 *peer_addr;
+ socklen_t len = sizeof(*peer_addr);
+ struct socket_address *peer;
+ int ret;
+ char addr[128];
+ const char *addr_ret;
+
+ peer = talloc(mem_ctx, struct socket_address);
+ if (!peer) {
+ return NULL;
+ }
+
+ peer->family = sock->backend_name;
+ peer_addr = talloc(peer, struct sockaddr_in6);
+ if (!peer_addr) {
+ talloc_free(peer);
+ return NULL;
+ }
+
+ peer->sockaddr = (struct sockaddr *)peer_addr;
+
+ ret = getpeername(sock->fd, peer->sockaddr, &len);
+ if (ret == -1) {
+ talloc_free(peer);
+ return NULL;
+ }
+
+ peer->sockaddrlen = len;
+
+ addr_ret = inet_ntop(AF_INET6, &peer_addr->sin6_addr, addr, sizeof(addr));
+ if (addr_ret == NULL) {
+ talloc_free(peer);
+ return NULL;
+ }
+
+ peer->addr = talloc_strdup(peer, addr_ret);
+ if (peer->addr == NULL) {
+ talloc_free(peer);
+ return NULL;
+ }
+
+ peer->port = ntohs(peer_addr->sin6_port);
+
+ return peer;
+}
+
+static struct socket_address *ipv6_tcp_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
+{
+ struct sockaddr_in6 *local_addr;
+ socklen_t len = sizeof(*local_addr);
+ struct socket_address *local;
+ int ret;
+ char addrstring[INET6_ADDRSTRLEN];
+
+ local = talloc(mem_ctx, struct socket_address);
+ if (!local) {
+ return NULL;
+ }
+
+ local->family = sock->backend_name;
+ local_addr = talloc(local, struct sockaddr_in6);
+ if (!local_addr) {
+ talloc_free(local);
+ return NULL;
+ }
+
+ local->sockaddr = (struct sockaddr *)local_addr;
+
+ ret = getsockname(sock->fd, local->sockaddr, &len);
+ if (ret == -1) {
+ talloc_free(local);
+ return NULL;
+ }
+
+ local->sockaddrlen = len;
+
+ if (inet_ntop(AF_INET6, &local_addr->sin6_addr, addrstring,
+ sizeof(addrstring)) == NULL) {
+ DEBUG(0, ("Unable to convert address to string: %s\n",
+ strerror(errno)));
+ talloc_free(local);
+ return NULL;
+ }
+
+ local->addr = talloc_strdup(mem_ctx, addrstring);
+ if (!local->addr) {
+ talloc_free(local);
+ return NULL;
+ }
+ local->port = ntohs(local_addr->sin6_port);
+
+ return local;
+}
+
+static const struct socket_ops ipv6_tcp_ops = {
+ .name = "ipv6",
+ .fn_init = ipv6_init,
+ .fn_connect = ipv6_tcp_connect,
+ .fn_connect_complete = ip_connect_complete,
+ .fn_listen = ipv6_listen,
+ .fn_accept = ipv6_tcp_accept,
+ .fn_recv = ip_recv,
+ .fn_recvfrom = ipv6_recvfrom,
+ .fn_send = ip_send,
+ .fn_sendto = ipv6_sendto,
+ .fn_pending = ip_pending,
+ .fn_close = ip_close,
+
+ .fn_set_option = ipv6_set_option,
+
+ .fn_get_peer_name = ipv6_tcp_get_peer_name,
+ .fn_get_peer_addr = ipv6_tcp_get_peer_addr,
+ .fn_get_my_addr = ipv6_tcp_get_my_addr,
+
+ .fn_get_fd = ip_get_fd
+};
+
+_PUBLIC_ const struct socket_ops *socket_ipv6_ops(enum socket_type type)
+{
+ return &ipv6_tcp_ops;
+}
+
+#endif
diff --git a/source4/lib/socket/socket_unix.c b/source4/lib/socket/socket_unix.c
new file mode 100644
index 0000000000..af7d2bb79f
--- /dev/null
+++ b/source4/lib/socket/socket_unix.c
@@ -0,0 +1,420 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ unix domain socket functions
+
+ Copyright (C) Stefan Metzmacher 2004
+ Copyright (C) Andrew Tridgell 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/socket/socket.h"
+#include "system/network.h"
+#include "system/filesys.h"
+
+
+
+/*
+ approximate errno mapping
+*/
+static NTSTATUS unixdom_error(int ernum)
+{
+ return map_nt_error_from_unix(ernum);
+}
+
+static NTSTATUS unixdom_init(struct socket_context *sock)
+{
+ int type;
+
+ switch (sock->type) {
+ case SOCKET_TYPE_STREAM:
+ type = SOCK_STREAM;
+ break;
+ case SOCKET_TYPE_DGRAM:
+ type = SOCK_DGRAM;
+ break;
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ sock->fd = socket(PF_UNIX, type, 0);
+ if (sock->fd == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+ sock->private_data = NULL;
+
+ sock->backend_name = "unix";
+
+ return NT_STATUS_OK;
+}
+
+static void unixdom_close(struct socket_context *sock)
+{
+ close(sock->fd);
+}
+
+static NTSTATUS unixdom_connect_complete(struct socket_context *sock, uint32_t flags)
+{
+ int error=0, ret;
+ socklen_t len = sizeof(error);
+
+ /* check for any errors that may have occurred - this is needed
+ for non-blocking connect */
+ ret = getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, &error, &len);
+ if (ret == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+ if (error != 0) {
+ return map_nt_error_from_unix(error);
+ }
+
+ if (!(flags & SOCKET_FLAG_BLOCK)) {
+ ret = set_blocking(sock->fd, false);
+ if (ret == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+ }
+
+ sock->state = SOCKET_STATE_CLIENT_CONNECTED;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS unixdom_connect(struct socket_context *sock,
+ const struct socket_address *my_address,
+ const struct socket_address *srv_address,
+ uint32_t flags)
+{
+ int ret;
+
+ if (srv_address->sockaddr) {
+ ret = connect(sock->fd, srv_address->sockaddr, srv_address->sockaddrlen);
+ } else {
+ struct sockaddr_un srv_addr;
+ if (strlen(srv_address->addr)+1 > sizeof(srv_addr.sun_path)) {
+ return NT_STATUS_OBJECT_PATH_INVALID;
+ }
+
+ ZERO_STRUCT(srv_addr);
+ srv_addr.sun_family = AF_UNIX;
+ strncpy(srv_addr.sun_path, srv_address->addr, sizeof(srv_addr.sun_path));
+
+ ret = connect(sock->fd, (const struct sockaddr *)&srv_addr, sizeof(srv_addr));
+ }
+ if (ret == -1) {
+ return unixdom_error(errno);
+ }
+
+ return unixdom_connect_complete(sock, flags);
+}
+
+static NTSTATUS unixdom_listen(struct socket_context *sock,
+ const struct socket_address *my_address,
+ int queue_size, uint32_t flags)
+{
+ struct sockaddr_un my_addr;
+ int ret;
+
+ /* delete if it already exists */
+ if (my_address->addr) {
+ unlink(my_address->addr);
+ }
+
+ if (my_address->sockaddr) {
+ ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
+ } else if (my_address->addr == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ } else {
+ if (strlen(my_address->addr)+1 > sizeof(my_addr.sun_path)) {
+ return NT_STATUS_OBJECT_PATH_INVALID;
+ }
+
+
+ ZERO_STRUCT(my_addr);
+ my_addr.sun_family = AF_UNIX;
+ strncpy(my_addr.sun_path, my_address->addr, sizeof(my_addr.sun_path));
+
+ ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
+ }
+ if (ret == -1) {
+ return unixdom_error(errno);
+ }
+
+ if (sock->type == SOCKET_TYPE_STREAM) {
+ ret = listen(sock->fd, queue_size);
+ if (ret == -1) {
+ return unixdom_error(errno);
+ }
+ }
+
+ if (!(flags & SOCKET_FLAG_BLOCK)) {
+ ret = set_blocking(sock->fd, false);
+ if (ret == -1) {
+ return unixdom_error(errno);
+ }
+ }
+
+ sock->state = SOCKET_STATE_SERVER_LISTEN;
+ sock->private_data = (void *)talloc_strdup(sock, my_address->addr);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS unixdom_accept(struct socket_context *sock,
+ struct socket_context **new_sock)
+{
+ struct sockaddr_un cli_addr;
+ socklen_t cli_addr_len = sizeof(cli_addr);
+ int new_fd;
+
+ if (sock->type != SOCKET_TYPE_STREAM) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ new_fd = accept(sock->fd, (struct sockaddr *)&cli_addr, &cli_addr_len);
+ if (new_fd == -1) {
+ return unixdom_error(errno);
+ }
+
+ if (!(sock->flags & SOCKET_FLAG_BLOCK)) {
+ int ret = set_blocking(new_fd, false);
+ if (ret == -1) {
+ close(new_fd);
+ return map_nt_error_from_unix(errno);
+ }
+ }
+
+ (*new_sock) = talloc(NULL, struct socket_context);
+ if (!(*new_sock)) {
+ close(new_fd);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* copy the socket_context */
+ (*new_sock)->type = sock->type;
+ (*new_sock)->state = SOCKET_STATE_SERVER_CONNECTED;
+ (*new_sock)->flags = sock->flags;
+
+ (*new_sock)->fd = new_fd;
+
+ (*new_sock)->private_data = NULL;
+ (*new_sock)->ops = sock->ops;
+ (*new_sock)->backend_name = sock->backend_name;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS unixdom_recv(struct socket_context *sock, void *buf,
+ size_t wantlen, size_t *nread)
+{
+ ssize_t gotlen;
+
+ *nread = 0;
+
+ gotlen = recv(sock->fd, buf, wantlen, 0);
+ if (gotlen == 0) {
+ return NT_STATUS_END_OF_FILE;
+ } else if (gotlen == -1) {
+ return unixdom_error(errno);
+ }
+
+ *nread = gotlen;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS unixdom_send(struct socket_context *sock,
+ const DATA_BLOB *blob, size_t *sendlen)
+{
+ ssize_t len;
+
+ *sendlen = 0;
+
+ len = send(sock->fd, blob->data, blob->length, 0);
+ if (len == -1) {
+ return unixdom_error(errno);
+ }
+
+ *sendlen = len;
+
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS unixdom_sendto(struct socket_context *sock,
+ const DATA_BLOB *blob, size_t *sendlen,
+ const struct socket_address *dest)
+{
+ ssize_t len;
+ *sendlen = 0;
+
+ if (dest->sockaddr) {
+ len = sendto(sock->fd, blob->data, blob->length, 0,
+ dest->sockaddr, dest->sockaddrlen);
+ } else {
+ struct sockaddr_un srv_addr;
+
+ if (strlen(dest->addr)+1 > sizeof(srv_addr.sun_path)) {
+ return NT_STATUS_OBJECT_PATH_INVALID;
+ }
+
+ ZERO_STRUCT(srv_addr);
+ srv_addr.sun_family = AF_UNIX;
+ strncpy(srv_addr.sun_path, dest->addr, sizeof(srv_addr.sun_path));
+
+ len = sendto(sock->fd, blob->data, blob->length, 0,
+ (struct sockaddr *)&srv_addr, sizeof(srv_addr));
+ }
+ if (len == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ *sendlen = len;
+
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS unixdom_set_option(struct socket_context *sock,
+ const char *option, const char *val)
+{
+ return NT_STATUS_OK;
+}
+
+static char *unixdom_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx)
+{
+ return talloc_strdup(mem_ctx, "LOCAL/unixdom");
+}
+
+static struct socket_address *unixdom_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
+{
+ struct sockaddr_in *peer_addr;
+ socklen_t len = sizeof(*peer_addr);
+ struct socket_address *peer;
+ int ret;
+
+ peer = talloc(mem_ctx, struct socket_address);
+ if (!peer) {
+ return NULL;
+ }
+
+ peer->family = sock->backend_name;
+ peer_addr = talloc(peer, struct sockaddr_in);
+ if (!peer_addr) {
+ talloc_free(peer);
+ return NULL;
+ }
+
+ peer->sockaddr = (struct sockaddr *)peer_addr;
+
+ ret = getpeername(sock->fd, peer->sockaddr, &len);
+ if (ret == -1) {
+ talloc_free(peer);
+ return NULL;
+ }
+
+ peer->sockaddrlen = len;
+
+ peer->port = 0;
+ peer->addr = talloc_strdup(peer, "LOCAL/unixdom");
+ if (!peer->addr) {
+ talloc_free(peer);
+ return NULL;
+ }
+
+ return peer;
+}
+
+static struct socket_address *unixdom_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
+{
+ struct sockaddr_in *local_addr;
+ socklen_t len = sizeof(*local_addr);
+ struct socket_address *local;
+ int ret;
+
+ local = talloc(mem_ctx, struct socket_address);
+ if (!local) {
+ return NULL;
+ }
+
+ local->family = sock->backend_name;
+ local_addr = talloc(local, struct sockaddr_in);
+ if (!local_addr) {
+ talloc_free(local);
+ return NULL;
+ }
+
+ local->sockaddr = (struct sockaddr *)local_addr;
+
+ ret = getsockname(sock->fd, local->sockaddr, &len);
+ if (ret == -1) {
+ talloc_free(local);
+ return NULL;
+ }
+
+ local->sockaddrlen = len;
+
+ local->port = 0;
+ local->addr = talloc_strdup(local, "LOCAL/unixdom");
+ if (!local->addr) {
+ talloc_free(local);
+ return NULL;
+ }
+
+ return local;
+}
+
+static int unixdom_get_fd(struct socket_context *sock)
+{
+ return sock->fd;
+}
+
+static NTSTATUS unixdom_pending(struct socket_context *sock, size_t *npending)
+{
+ int value = 0;
+ if (ioctl(sock->fd, FIONREAD, &value) == 0) {
+ *npending = value;
+ return NT_STATUS_OK;
+ }
+ return map_nt_error_from_unix(errno);
+}
+
+static const struct socket_ops unixdom_ops = {
+ .name = "unix",
+ .fn_init = unixdom_init,
+ .fn_connect = unixdom_connect,
+ .fn_connect_complete = unixdom_connect_complete,
+ .fn_listen = unixdom_listen,
+ .fn_accept = unixdom_accept,
+ .fn_recv = unixdom_recv,
+ .fn_send = unixdom_send,
+ .fn_sendto = unixdom_sendto,
+ .fn_close = unixdom_close,
+ .fn_pending = unixdom_pending,
+
+ .fn_set_option = unixdom_set_option,
+
+ .fn_get_peer_name = unixdom_get_peer_name,
+ .fn_get_peer_addr = unixdom_get_peer_addr,
+ .fn_get_my_addr = unixdom_get_my_addr,
+
+ .fn_get_fd = unixdom_get_fd
+};
+
+_PUBLIC_ const struct socket_ops *socket_unixdom_ops(enum socket_type type)
+{
+ return &unixdom_ops;
+}
diff --git a/source4/lib/socket/testsuite.c b/source4/lib/socket/testsuite.c
new file mode 100644
index 0000000000..cba283f558
--- /dev/null
+++ b/source4/lib/socket/testsuite.c
@@ -0,0 +1,198 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ local testing of socket routines.
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/socket/socket.h"
+#include "lib/events/events.h"
+#include "system/network.h"
+#include "lib/socket/netif.h"
+#include "torture/torture.h"
+#include "param/param.h"
+#include "libcli/resolve/resolve.h"
+
+/**
+ basic testing of udp routines
+*/
+static bool test_udp(struct torture_context *tctx)
+{
+ struct socket_context *sock1, *sock2;
+ NTSTATUS status;
+ struct socket_address *srv_addr, *from_addr, *localhost;
+ size_t size = 100 + (random() % 100);
+ DATA_BLOB blob, blob2;
+ size_t sent, nread;
+ TALLOC_CTX *mem_ctx = tctx;
+ struct interface *ifaces;
+
+ load_interfaces(tctx, lp_interfaces(tctx->lp_ctx), &ifaces);
+
+ status = socket_create("ip", SOCKET_TYPE_DGRAM, &sock1, 0);
+ torture_assert_ntstatus_ok(tctx, status, "creating DGRAM IP socket 1");
+ talloc_steal(mem_ctx, sock1);
+
+ status = socket_create("ip", SOCKET_TYPE_DGRAM, &sock2, 0);
+ torture_assert_ntstatus_ok(tctx, status, "creating DGRAM IP socket 1");
+ talloc_steal(mem_ctx, sock2);
+
+ localhost = socket_address_from_strings(sock1, sock1->backend_name,
+ iface_best_ip(ifaces, "127.0.0.1"), 0);
+
+ torture_assert(tctx, localhost, "Localhost not found");
+
+ status = socket_listen(sock1, localhost, 0, 0);
+ torture_assert_ntstatus_ok(tctx, status, "listen on socket 1");
+
+ srv_addr = socket_get_my_addr(sock1, mem_ctx);
+ torture_assert(tctx, srv_addr != NULL &&
+ strcmp(srv_addr->addr, iface_best_ip(ifaces, "127.0.0.1")) == 0,
+ talloc_asprintf(tctx,
+ "Expected server address of %s but got %s",
+ iface_best_ip(ifaces, "127.0.0.1"), srv_addr ? srv_addr->addr : NULL));
+
+ torture_comment(tctx, "server port is %d\n", srv_addr->port);
+
+ blob = data_blob_talloc(mem_ctx, NULL, size);
+ blob2 = data_blob_talloc(mem_ctx, NULL, size);
+ generate_random_buffer(blob.data, blob.length);
+
+ sent = size;
+ status = socket_sendto(sock2, &blob, &sent, srv_addr);
+ torture_assert_ntstatus_ok(tctx, status, "sendto() on socket 2");
+
+ status = socket_recvfrom(sock1, blob2.data, size, &nread,
+ sock1, &from_addr);
+ torture_assert_ntstatus_ok(tctx, status, "recvfrom() on socket 1");
+
+ torture_assert_str_equal(tctx, from_addr->addr, srv_addr->addr,
+ "different address");
+
+ torture_assert_int_equal(tctx, nread, size, "Unexpected recvfrom size");
+
+ torture_assert_mem_equal(tctx, blob2.data, blob.data, size,
+ "Bad data in recvfrom");
+
+ generate_random_buffer(blob.data, blob.length);
+ status = socket_sendto(sock1, &blob, &sent, from_addr);
+ torture_assert_ntstatus_ok(tctx, status, "sendto() on socket 1");
+
+ status = socket_recvfrom(sock2, blob2.data, size, &nread,
+ sock2, &from_addr);
+ torture_assert_ntstatus_ok(tctx, status, "recvfrom() on socket 2");
+ torture_assert_str_equal(tctx, from_addr->addr, srv_addr->addr,
+ "Unexpected recvfrom addr");
+
+ torture_assert_int_equal(tctx, nread, size, "Unexpected recvfrom size");
+
+ torture_assert_int_equal(tctx, from_addr->port, srv_addr->port,
+ "Unexpected recvfrom port");
+
+ torture_assert_mem_equal(tctx, blob2.data, blob.data, size,
+ "Bad data in recvfrom");
+
+ talloc_free(sock1);
+ talloc_free(sock2);
+ return true;
+}
+
+/*
+ basic testing of tcp routines
+*/
+static bool test_tcp(struct torture_context *tctx)
+{
+ struct socket_context *sock1, *sock2, *sock3;
+ NTSTATUS status;
+ struct socket_address *srv_addr, *from_addr, *localhost;
+ size_t size = 100 + (random() % 100);
+ DATA_BLOB blob, blob2;
+ size_t sent, nread;
+ TALLOC_CTX *mem_ctx = tctx;
+ struct tevent_context *ev = tctx->ev;
+ struct interface *ifaces;
+
+ status = socket_create("ip", SOCKET_TYPE_STREAM, &sock1, 0);
+ torture_assert_ntstatus_ok(tctx, status, "creating IP stream socket 1");
+ talloc_steal(mem_ctx, sock1);
+
+ status = socket_create("ip", SOCKET_TYPE_STREAM, &sock2, 0);
+ torture_assert_ntstatus_ok(tctx, status, "creating IP stream socket 1");
+ talloc_steal(mem_ctx, sock2);
+
+ load_interfaces(tctx, lp_interfaces(tctx->lp_ctx), &ifaces);
+ localhost = socket_address_from_strings(sock1, sock1->backend_name,
+ iface_best_ip(ifaces, "127.0.0.1"), 0);
+ torture_assert(tctx, localhost, "Localhost not found");
+
+ status = socket_listen(sock1, localhost, 0, 0);
+ torture_assert_ntstatus_ok(tctx, status, "listen on socket 1");
+
+ srv_addr = socket_get_my_addr(sock1, mem_ctx);
+ torture_assert(tctx, srv_addr && srv_addr->addr,
+ "Unexpected socket_get_my_addr NULL\n");
+
+ torture_assert_str_equal(tctx, srv_addr->addr, iface_best_ip(ifaces, "127.0.0.1"),
+ "Unexpected server address");
+
+ torture_comment(tctx, "server port is %d\n", srv_addr->port);
+
+ status = socket_connect_ev(sock2, NULL, srv_addr, 0, ev);
+ torture_assert_ntstatus_ok(tctx, status, "connect() on socket 2");
+
+ status = socket_accept(sock1, &sock3);
+ torture_assert_ntstatus_ok(tctx, status, "accept() on socket 1");
+ talloc_steal(mem_ctx, sock3);
+ talloc_free(sock1);
+
+ blob = data_blob_talloc(mem_ctx, NULL, size);
+ blob2 = data_blob_talloc(mem_ctx, NULL, size);
+ generate_random_buffer(blob.data, blob.length);
+
+ sent = size;
+ status = socket_send(sock2, &blob, &sent);
+ torture_assert_ntstatus_ok(tctx, status, "send() on socket 2");
+
+ status = socket_recv(sock3, blob2.data, size, &nread);
+ torture_assert_ntstatus_ok(tctx, status, "recv() on socket 3");
+
+ from_addr = socket_get_peer_addr(sock3, mem_ctx);
+
+ torture_assert(tctx, from_addr && from_addr->addr,
+ "Unexpected recvfrom addr NULL");
+
+ torture_assert_str_equal(tctx, from_addr->addr, srv_addr->addr,
+ "Unexpected recvfrom addr");
+
+ torture_assert_int_equal(tctx, nread, size, "Unexpected recvfrom size");
+
+ torture_assert_mem_equal(tctx, blob2.data, blob.data, size,
+ "Bad data in recv");
+ return true;
+}
+
+struct torture_suite *torture_local_socket(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx,
+ "SOCKET");
+
+ torture_suite_add_simple_test(suite, "udp", test_udp);
+ torture_suite_add_simple_test(suite, "tcp", test_tcp);
+
+ return suite;
+}
diff --git a/source4/lib/stream/config.mk b/source4/lib/stream/config.mk
new file mode 100644
index 0000000000..56d117e7bd
--- /dev/null
+++ b/source4/lib/stream/config.mk
@@ -0,0 +1,4 @@
+[SUBSYSTEM::LIBPACKET]
+PRIVATE_DEPENDENCIES = LIBTLS
+
+LIBPACKET_OBJ_FILES = $(libstreamsrcdir)/packet.o
diff --git a/source4/lib/stream/packet.c b/source4/lib/stream/packet.c
new file mode 100644
index 0000000000..f5e2b843cd
--- /dev/null
+++ b/source4/lib/stream/packet.c
@@ -0,0 +1,604 @@
+/*
+ Unix SMB/CIFS mplementation.
+
+ helper layer for breaking up streams into discrete requests
+
+ 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 <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "includes.h"
+#include "../lib/util/dlinklist.h"
+#include "lib/events/events.h"
+#include "lib/socket/socket.h"
+#include "lib/stream/packet.h"
+#include "libcli/raw/smb.h"
+
+struct packet_context {
+ packet_callback_fn_t callback;
+ packet_full_request_fn_t full_request;
+ packet_error_handler_fn_t error_handler;
+ DATA_BLOB partial;
+ uint32_t num_read;
+ uint32_t initial_read;
+ struct socket_context *sock;
+ struct tevent_context *ev;
+ size_t packet_size;
+ void *private_data;
+ struct tevent_fd *fde;
+ bool serialise;
+ int processing;
+ bool recv_disable;
+ bool nofree;
+
+ bool busy;
+ bool destructor_called;
+
+ bool unreliable_select;
+
+ struct send_element {
+ struct send_element *next, *prev;
+ DATA_BLOB blob;
+ size_t nsent;
+ packet_send_callback_fn_t send_callback;
+ void *send_callback_private;
+ } *send_queue;
+};
+
+/*
+ a destructor used when we are processing packets to prevent freeing of this
+ context while it is being used
+*/
+static int packet_destructor(struct packet_context *pc)
+{
+ if (pc->busy) {
+ pc->destructor_called = true;
+ /* now we refuse the talloc_free() request. The free will
+ happen again in the packet_recv() code */
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*
+ initialise a packet receiver
+*/
+_PUBLIC_ struct packet_context *packet_init(TALLOC_CTX *mem_ctx)
+{
+ struct packet_context *pc = talloc_zero(mem_ctx, struct packet_context);
+ if (pc != NULL) {
+ talloc_set_destructor(pc, packet_destructor);
+ }
+ return pc;
+}
+
+
+/*
+ set the request callback, called when a full request is ready
+*/
+_PUBLIC_ void packet_set_callback(struct packet_context *pc, packet_callback_fn_t callback)
+{
+ pc->callback = callback;
+}
+
+/*
+ set the error handler
+*/
+_PUBLIC_ void packet_set_error_handler(struct packet_context *pc, packet_error_handler_fn_t handler)
+{
+ pc->error_handler = handler;
+}
+
+/*
+ set the private pointer passed to the callback functions
+*/
+_PUBLIC_ void packet_set_private(struct packet_context *pc, void *private_data)
+{
+ pc->private_data = private_data;
+}
+
+/*
+ set the full request callback. Should return as follows:
+ NT_STATUS_OK == blob is a full request.
+ STATUS_MORE_ENTRIES == blob is not complete yet
+ any error == blob is not a valid
+*/
+_PUBLIC_ void packet_set_full_request(struct packet_context *pc, packet_full_request_fn_t callback)
+{
+ pc->full_request = callback;
+}
+
+/*
+ set a socket context to use. You must set a socket_context
+*/
+_PUBLIC_ void packet_set_socket(struct packet_context *pc, struct socket_context *sock)
+{
+ pc->sock = sock;
+}
+
+/*
+ set an event context. If this is set then the code will ensure that
+ packets arrive with separate events, by creating a immediate event
+ for any secondary packets when more than one packet is read at one
+ time on a socket. This can matter for code that relies on not
+ getting more than one packet per event
+*/
+_PUBLIC_ void packet_set_event_context(struct packet_context *pc, struct tevent_context *ev)
+{
+ pc->ev = ev;
+}
+
+/*
+ tell the packet layer the fde for the socket
+*/
+_PUBLIC_ void packet_set_fde(struct packet_context *pc, struct tevent_fd *fde)
+{
+ pc->fde = fde;
+}
+
+/*
+ tell the packet layer to serialise requests, so we don't process two
+ requests at once on one connection. You must have set the
+ event_context and fde
+*/
+_PUBLIC_ void packet_set_serialise(struct packet_context *pc)
+{
+ pc->serialise = true;
+}
+
+/*
+ tell the packet layer how much to read when starting a new packet
+ this ensures it doesn't overread
+*/
+_PUBLIC_ void packet_set_initial_read(struct packet_context *pc, uint32_t initial_read)
+{
+ pc->initial_read = initial_read;
+}
+
+/*
+ tell the packet system not to steal/free blobs given to packet_send()
+*/
+_PUBLIC_ void packet_set_nofree(struct packet_context *pc)
+{
+ pc->nofree = true;
+}
+
+/*
+ tell the packet system that select/poll/epoll on the underlying
+ socket may not be a reliable way to determine if data is available
+ for receive. This happens with underlying socket systems such as the
+ one implemented on top of GNUTLS, where there may be data in
+ encryption/compression buffers that could be received by
+ socket_recv(), while there is no data waiting at the real socket
+ level as seen by select/poll/epoll. The GNUTLS library is supposed
+ to cope with this by always leaving some data sitting in the socket
+ buffer, but it does not seem to be reliable.
+ */
+_PUBLIC_ void packet_set_unreliable_select(struct packet_context *pc)
+{
+ pc->unreliable_select = true;
+}
+
+/*
+ tell the caller we have an error
+*/
+static void packet_error(struct packet_context *pc, NTSTATUS status)
+{
+ pc->sock = NULL;
+ if (pc->error_handler) {
+ pc->error_handler(pc->private_data, status);
+ return;
+ }
+ /* default error handler is to free the callers private pointer */
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE)) {
+ DEBUG(0,("packet_error on %s - %s\n",
+ talloc_get_name(pc->private_data), nt_errstr(status)));
+ }
+ talloc_free(pc->private_data);
+ return;
+}
+
+
+/*
+ tell the caller we have EOF
+*/
+static void packet_eof(struct packet_context *pc)
+{
+ packet_error(pc, NT_STATUS_END_OF_FILE);
+}
+
+
+/*
+ used to put packets on event boundaries
+*/
+static void packet_next_event(struct tevent_context *ev, struct tevent_timer *te,
+ struct timeval t, void *private_data)
+{
+ struct packet_context *pc = talloc_get_type(private_data, struct packet_context);
+ if (pc->num_read != 0 && pc->packet_size != 0 &&
+ pc->packet_size <= pc->num_read) {
+ packet_recv(pc);
+ }
+}
+
+
+/*
+ call this when the socket becomes readable to kick off the whole
+ stream parsing process
+*/
+_PUBLIC_ void packet_recv(struct packet_context *pc)
+{
+ size_t npending;
+ NTSTATUS status;
+ size_t nread = 0;
+ DATA_BLOB blob;
+ bool recv_retry = false;
+
+ if (pc->processing) {
+ EVENT_FD_NOT_READABLE(pc->fde);
+ pc->processing++;
+ return;
+ }
+
+ if (pc->recv_disable) {
+ EVENT_FD_NOT_READABLE(pc->fde);
+ return;
+ }
+
+ if (pc->packet_size != 0 && pc->num_read >= pc->packet_size) {
+ goto next_partial;
+ }
+
+ if (pc->packet_size != 0) {
+ /* we've already worked out how long this next packet is, so skip the
+ socket_pending() call */
+ npending = pc->packet_size - pc->num_read;
+ } else if (pc->initial_read != 0) {
+ npending = pc->initial_read - pc->num_read;
+ } else {
+ if (pc->sock) {
+ status = socket_pending(pc->sock, &npending);
+ } else {
+ status = NT_STATUS_CONNECTION_DISCONNECTED;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ packet_error(pc, status);
+ return;
+ }
+ }
+
+ if (npending == 0) {
+ packet_eof(pc);
+ return;
+ }
+
+again:
+
+ if (npending + pc->num_read < npending) {
+ packet_error(pc, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ if (npending + pc->num_read < pc->num_read) {
+ packet_error(pc, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ /* possibly expand the partial packet buffer */
+ if (npending + pc->num_read > pc->partial.length) {
+ if (!data_blob_realloc(pc, &pc->partial, npending+pc->num_read)) {
+ packet_error(pc, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ }
+
+ if (pc->partial.length < pc->num_read + npending) {
+ packet_error(pc, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ if ((uint8_t *)pc->partial.data + pc->num_read < (uint8_t *)pc->partial.data) {
+ packet_error(pc, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+ if ((uint8_t *)pc->partial.data + pc->num_read + npending < (uint8_t *)pc->partial.data) {
+ packet_error(pc, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ status = socket_recv(pc->sock, pc->partial.data + pc->num_read,
+ npending, &nread);
+
+ if (NT_STATUS_IS_ERR(status)) {
+ packet_error(pc, status);
+ return;
+ }
+ if (recv_retry && NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
+ nread = 0;
+ status = NT_STATUS_OK;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return;
+ }
+
+ if (nread == 0 && !recv_retry) {
+ packet_eof(pc);
+ return;
+ }
+
+ pc->num_read += nread;
+
+ if (pc->unreliable_select && nread != 0) {
+ recv_retry = true;
+ status = socket_pending(pc->sock, &npending);
+ if (!NT_STATUS_IS_OK(status)) {
+ packet_error(pc, status);
+ return;
+ }
+ if (npending != 0) {
+ goto again;
+ }
+ }
+
+next_partial:
+ if (pc->partial.length != pc->num_read) {
+ if (!data_blob_realloc(pc, &pc->partial, pc->num_read)) {
+ packet_error(pc, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ }
+
+ /* see if its a full request */
+ blob = pc->partial;
+ blob.length = pc->num_read;
+ status = pc->full_request(pc->private_data, blob, &pc->packet_size);
+ if (NT_STATUS_IS_ERR(status)) {
+ packet_error(pc, status);
+ return;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return;
+ }
+
+ if (pc->packet_size > pc->num_read) {
+ /* the caller made an error */
+ DEBUG(0,("Invalid packet_size %lu greater than num_read %lu\n",
+ (long)pc->packet_size, (long)pc->num_read));
+ packet_error(pc, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ /* it is a full request - give it to the caller */
+ blob = pc->partial;
+ blob.length = pc->num_read;
+
+ if (pc->packet_size < pc->num_read) {
+ pc->partial = data_blob_talloc(pc, blob.data + pc->packet_size,
+ pc->num_read - pc->packet_size);
+ if (pc->partial.data == NULL) {
+ packet_error(pc, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ /* Trunate the blob sent to the caller to only the packet length */
+ if (!data_blob_realloc(pc, &blob, pc->packet_size)) {
+ packet_error(pc, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ } else {
+ pc->partial = data_blob(NULL, 0);
+ }
+ pc->num_read -= pc->packet_size;
+ pc->packet_size = 0;
+
+ if (pc->serialise) {
+ pc->processing = 1;
+ }
+
+ pc->busy = true;
+
+ status = pc->callback(pc->private_data, blob);
+
+ pc->busy = false;
+
+ if (pc->destructor_called) {
+ talloc_free(pc);
+ return;
+ }
+
+ if (pc->processing) {
+ if (pc->processing > 1) {
+ EVENT_FD_READABLE(pc->fde);
+ }
+ pc->processing = 0;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ packet_error(pc, status);
+ return;
+ }
+
+ /* Have we consumed the whole buffer yet? */
+ if (pc->partial.length == 0) {
+ return;
+ }
+
+ /* we got multiple packets in one tcp read */
+ if (pc->ev == NULL) {
+ goto next_partial;
+ }
+
+ blob = pc->partial;
+ blob.length = pc->num_read;
+
+ status = pc->full_request(pc->private_data, blob, &pc->packet_size);
+ if (NT_STATUS_IS_ERR(status)) {
+ packet_error(pc, status);
+ return;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return;
+ }
+
+ event_add_timed(pc->ev, pc, timeval_zero(), packet_next_event, pc);
+}
+
+
+/*
+ temporarily disable receiving
+*/
+_PUBLIC_ void packet_recv_disable(struct packet_context *pc)
+{
+ EVENT_FD_NOT_READABLE(pc->fde);
+ pc->recv_disable = true;
+}
+
+/*
+ re-enable receiving
+*/
+_PUBLIC_ void packet_recv_enable(struct packet_context *pc)
+{
+ EVENT_FD_READABLE(pc->fde);
+ pc->recv_disable = false;
+ if (pc->num_read != 0 && pc->packet_size >= pc->num_read) {
+ event_add_timed(pc->ev, pc, timeval_zero(), packet_next_event, pc);
+ }
+}
+
+/*
+ trigger a run of the send queue
+*/
+_PUBLIC_ void packet_queue_run(struct packet_context *pc)
+{
+ while (pc->send_queue) {
+ struct send_element *el = pc->send_queue;
+ NTSTATUS status;
+ size_t nwritten;
+ DATA_BLOB blob = data_blob_const(el->blob.data + el->nsent,
+ el->blob.length - el->nsent);
+
+ status = socket_send(pc->sock, &blob, &nwritten);
+
+ if (NT_STATUS_IS_ERR(status)) {
+ packet_error(pc, status);
+ return;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return;
+ }
+ el->nsent += nwritten;
+ if (el->nsent == el->blob.length) {
+ DLIST_REMOVE(pc->send_queue, el);
+ if (el->send_callback) {
+ pc->busy = true;
+ el->send_callback(el->send_callback_private);
+ pc->busy = false;
+ if (pc->destructor_called) {
+ talloc_free(pc);
+ return;
+ }
+ }
+ talloc_free(el);
+ }
+ }
+
+ /* we're out of requests to send, so don't wait for write
+ events any more */
+ EVENT_FD_NOT_WRITEABLE(pc->fde);
+}
+
+/*
+ put a packet in the send queue. When the packet is actually sent,
+ call send_callback.
+
+ Useful for operations that must occour after sending a message, such
+ as the switch to SASL encryption after as sucessful LDAP bind relpy.
+*/
+_PUBLIC_ NTSTATUS packet_send_callback(struct packet_context *pc, DATA_BLOB blob,
+ packet_send_callback_fn_t send_callback,
+ void *private_data)
+{
+ struct send_element *el;
+ el = talloc(pc, struct send_element);
+ NT_STATUS_HAVE_NO_MEMORY(el);
+
+ DLIST_ADD_END(pc->send_queue, el, struct send_element *);
+ el->blob = blob;
+ el->nsent = 0;
+ el->send_callback = send_callback;
+ el->send_callback_private = private_data;
+
+ /* if we aren't going to free the packet then we must reference it
+ to ensure it doesn't disappear before going out */
+ if (pc->nofree) {
+ if (!talloc_reference(el, blob.data)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ } else {
+ talloc_steal(el, blob.data);
+ }
+
+ if (private_data && !talloc_reference(el, private_data)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ EVENT_FD_WRITEABLE(pc->fde);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ put a packet in the send queue
+*/
+_PUBLIC_ NTSTATUS packet_send(struct packet_context *pc, DATA_BLOB blob)
+{
+ return packet_send_callback(pc, blob, NULL, NULL);
+}
+
+
+/*
+ a full request checker for NBT formatted packets (first 3 bytes are length)
+*/
+_PUBLIC_ NTSTATUS packet_full_request_nbt(void *private_data, DATA_BLOB blob, size_t *size)
+{
+ if (blob.length < 4) {
+ return STATUS_MORE_ENTRIES;
+ }
+ *size = 4 + smb_len(blob.data);
+ if (*size > blob.length) {
+ return STATUS_MORE_ENTRIES;
+ }
+ return NT_STATUS_OK;
+}
+
+
+/*
+ work out if a packet is complete for protocols that use a 32 bit network byte
+ order length
+*/
+_PUBLIC_ NTSTATUS packet_full_request_u32(void *private_data, DATA_BLOB blob, size_t *size)
+{
+ if (blob.length < 4) {
+ return STATUS_MORE_ENTRIES;
+ }
+ *size = 4 + RIVAL(blob.data, 0);
+ if (*size > blob.length) {
+ return STATUS_MORE_ENTRIES;
+ }
+ return NT_STATUS_OK;
+}
diff --git a/source4/lib/stream/packet.h b/source4/lib/stream/packet.h
new file mode 100644
index 0000000000..85f0f26265
--- /dev/null
+++ b/source4/lib/stream/packet.h
@@ -0,0 +1,64 @@
+/*
+ Unix SMB/CIFS mplementation.
+
+ helper layer for breaking up streams into discrete requests
+
+ 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 <http://www.gnu.org/licenses/>.
+
+*/
+
+struct packet_context;
+struct tevent_context;
+struct tevent_fd;
+
+typedef NTSTATUS (*packet_full_request_fn_t)(void *private_data,
+ DATA_BLOB blob, size_t *packet_size);
+typedef NTSTATUS (*packet_callback_fn_t)(void *private_data, DATA_BLOB blob);
+
+/* Used to notify that a packet has been sent, and is on the wire */
+typedef void (*packet_send_callback_fn_t)(void *private_data);
+typedef void (*packet_error_handler_fn_t)(void *private_data, NTSTATUS status);
+
+
+
+struct packet_context *packet_init(TALLOC_CTX *mem_ctx);
+void packet_set_callback(struct packet_context *pc, packet_callback_fn_t callback);
+void packet_set_error_handler(struct packet_context *pc, packet_error_handler_fn_t handler);
+void packet_set_private(struct packet_context *pc, void *private_data);
+void packet_set_full_request(struct packet_context *pc, packet_full_request_fn_t callback);
+void packet_set_socket(struct packet_context *pc, struct socket_context *sock);
+void packet_set_event_context(struct packet_context *pc, struct tevent_context *ev);
+void packet_set_fde(struct packet_context *pc, struct tevent_fd *fde);
+void packet_set_serialise(struct packet_context *pc);
+void packet_set_initial_read(struct packet_context *pc, uint32_t initial_read);
+void packet_set_nofree(struct packet_context *pc);
+void packet_recv(struct packet_context *pc);
+void packet_recv_disable(struct packet_context *pc);
+void packet_recv_enable(struct packet_context *pc);
+void packet_set_unreliable_select(struct packet_context *pc);
+NTSTATUS packet_send(struct packet_context *pc, DATA_BLOB blob);
+NTSTATUS packet_send_callback(struct packet_context *pc, DATA_BLOB blob,
+ packet_send_callback_fn_t send_callback,
+ void *private_data);
+void packet_queue_run(struct packet_context *pc);
+
+/*
+ pre-canned handlers
+*/
+NTSTATUS packet_full_request_nbt(void *private_data, DATA_BLOB blob, size_t *size);
+NTSTATUS packet_full_request_u32(void *private_data, DATA_BLOB blob, size_t *size);
+
+
diff --git a/source4/lib/tdb_wrap.c b/source4/lib/tdb_wrap.c
new file mode 100644
index 0000000000..da27803b06
--- /dev/null
+++ b/source4/lib/tdb_wrap.c
@@ -0,0 +1,117 @@
+/*
+ Unix SMB/CIFS implementation.
+ TDB wrap functions
+
+ Copyright (C) Andrew Tridgell 2004
+ Copyright (C) Jelmer Vernooij <jelmer@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "../tdb/include/tdb.h"
+#include "../lib/util/dlinklist.h"
+#include "tdb_wrap.h"
+#include "tdb.h"
+
+static struct tdb_wrap *tdb_list;
+
+/* destroy the last connection to a tdb */
+static int tdb_wrap_destructor(struct tdb_wrap *w)
+{
+ tdb_close(w->tdb);
+ DLIST_REMOVE(tdb_list, w);
+ return 0;
+}
+
+/*
+ Log tdb messages via DEBUG().
+*/
+static void tdb_wrap_log(TDB_CONTEXT *tdb, enum tdb_debug_level level,
+ const char *format, ...) PRINTF_ATTRIBUTE(3,4);
+
+static void tdb_wrap_log(TDB_CONTEXT *tdb, enum tdb_debug_level level,
+ const char *format, ...)
+{
+ va_list ap;
+ char *ptr = NULL;
+ int dl;
+
+ va_start(ap, format);
+ vasprintf(&ptr, format, ap);
+ va_end(ap);
+
+ switch (level) {
+ case TDB_DEBUG_FATAL:
+ dl = 0;
+ break;
+ case TDB_DEBUG_ERROR:
+ dl = 1;
+ break;
+ case TDB_DEBUG_WARNING:
+ dl = 2;
+ break;
+ case TDB_DEBUG_TRACE:
+ dl = 5;
+ break;
+ default:
+ dl = 0;
+ }
+
+ if (ptr != NULL) {
+ const char *name = tdb_name(tdb);
+ DEBUG(dl, ("tdb(%s): %s", name ? name : "unnamed", ptr));
+ free(ptr);
+ }
+}
+
+
+/*
+ wrapped connection to a tdb database
+ to close just talloc_free() the tdb_wrap pointer
+ */
+struct tdb_wrap *tdb_wrap_open(TALLOC_CTX *mem_ctx,
+ const char *name, int hash_size, int tdb_flags,
+ int open_flags, mode_t mode)
+{
+ struct tdb_wrap *w;
+ struct tdb_logging_context log_ctx;
+ log_ctx.log_fn = tdb_wrap_log;
+
+ for (w=tdb_list;w;w=w->next) {
+ if (strcmp(name, w->name) == 0) {
+ return talloc_reference(mem_ctx, w);
+ }
+ }
+
+ w = talloc(mem_ctx, struct tdb_wrap);
+ if (w == NULL) {
+ return NULL;
+ }
+
+ w->name = talloc_strdup(w, name);
+
+ w->tdb = tdb_open_ex(name, hash_size, tdb_flags,
+ open_flags, mode, &log_ctx, NULL);
+ if (w->tdb == NULL) {
+ talloc_free(w);
+ return NULL;
+ }
+
+ talloc_set_destructor(w, tdb_wrap_destructor);
+
+ DLIST_ADD(tdb_list, w);
+
+ return w;
+}
diff --git a/source4/lib/tdb_wrap.h b/source4/lib/tdb_wrap.h
new file mode 100644
index 0000000000..eb0191fb31
--- /dev/null
+++ b/source4/lib/tdb_wrap.h
@@ -0,0 +1,38 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ database wrap headers
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _TDB_WRAP_H_
+#define _TDB_WRAP_H_
+
+#include "../lib/tdb/include/tdb.h"
+
+struct tdb_wrap {
+ struct tdb_context *tdb;
+
+ const char *name;
+ struct tdb_wrap *next, *prev;
+};
+
+struct tdb_wrap *tdb_wrap_open(TALLOC_CTX *mem_ctx,
+ const char *name, int hash_size, int tdb_flags,
+ int open_flags, mode_t mode);
+
+#endif /* _TDB_WRAP_H_ */
diff --git a/source4/lib/tdr/TODO b/source4/lib/tdr/TODO
new file mode 100644
index 0000000000..5093afd438
--- /dev/null
+++ b/source4/lib/tdr/TODO
@@ -0,0 +1 @@
+- Support read/write (to fd) as well as push/pull (to DATA_BLOB)
diff --git a/source4/lib/tdr/config.mk b/source4/lib/tdr/config.mk
new file mode 100644
index 0000000000..07506ec647
--- /dev/null
+++ b/source4/lib/tdr/config.mk
@@ -0,0 +1,9 @@
+[SUBSYSTEM::TDR]
+CFLAGS = -Ilib/tdr
+PUBLIC_DEPENDENCIES = LIBTALLOC LIBSAMBA-UTIL
+
+TDR_OBJ_FILES = $(libtdrsrcdir)/tdr.o
+
+$(eval $(call proto_header_template,$(libtdrsrcdir)/tdr_proto.h,$(TDR_OBJ_FILES:.o=.c)))
+
+PUBLIC_HEADERS += $(libtdrsrcdir)/tdr.h
diff --git a/source4/lib/tdr/tdr.c b/source4/lib/tdr/tdr.c
new file mode 100644
index 0000000000..8b62ea0c2b
--- /dev/null
+++ b/source4/lib/tdr/tdr.c
@@ -0,0 +1,397 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ TDR (Trivial Data Representation) helper functions
+ Based loosely on ndr.c by Andrew Tridgell.
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "system/network.h"
+#include "tdr/tdr.h"
+
+#define TDR_BASE_MARSHALL_SIZE 1024
+
+#define TDR_PUSH_NEED_BYTES(tdr, n) TDR_CHECK(tdr_push_expand(tdr, tdr->data.length+(n)))
+
+#define TDR_PULL_NEED_BYTES(tdr, n) do { \
+ if ((n) > tdr->data.length || tdr->offset + (n) > tdr->data.length) { \
+ return NT_STATUS_BUFFER_TOO_SMALL; \
+ } \
+} while(0)
+
+#define TDR_BE(tdr) ((tdr)->flags & TDR_BIG_ENDIAN)
+
+#define TDR_CVAL(tdr, ofs) CVAL(tdr->data.data,ofs)
+#define TDR_SVAL(tdr, ofs) (TDR_BE(tdr)?RSVAL(tdr->data.data,ofs):SVAL(tdr->data.data,ofs))
+#define TDR_IVAL(tdr, ofs) (TDR_BE(tdr)?RIVAL(tdr->data.data,ofs):IVAL(tdr->data.data,ofs))
+#define TDR_SCVAL(tdr, ofs, v) SCVAL(tdr->data.data,ofs,v)
+#define TDR_SSVAL(tdr, ofs, v) do { if (TDR_BE(tdr)) { RSSVAL(tdr->data.data,ofs,v); } else SSVAL(tdr->data.data,ofs,v); } while (0)
+#define TDR_SIVAL(tdr, ofs, v) do { if (TDR_BE(tdr)) { RSIVAL(tdr->data.data,ofs,v); } else SIVAL(tdr->data.data,ofs,v); } while (0)
+
+/**
+ expand the available space in the buffer to 'size'
+*/
+NTSTATUS tdr_push_expand(struct tdr_push *tdr, uint32_t size)
+{
+ if (talloc_get_size(tdr->data.data) >= size) {
+ return NT_STATUS_OK;
+ }
+
+ tdr->data.data = talloc_realloc(tdr, tdr->data.data, uint8_t, tdr->data.length + TDR_BASE_MARSHALL_SIZE);
+
+ if (tdr->data.data == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ return NT_STATUS_OK;
+}
+
+
+NTSTATUS tdr_pull_uint8(struct tdr_pull *tdr, TALLOC_CTX *ctx, uint8_t *v)
+{
+ TDR_PULL_NEED_BYTES(tdr, 1);
+ *v = TDR_CVAL(tdr, tdr->offset);
+ tdr->offset += 1;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS tdr_push_uint8(struct tdr_push *tdr, const uint8_t *v)
+{
+ TDR_PUSH_NEED_BYTES(tdr, 1);
+ TDR_SCVAL(tdr, tdr->data.length, *v);
+ tdr->data.length += 1;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS tdr_print_uint8(struct tdr_print *tdr, const char *name, uint8_t *v)
+{
+ tdr->print(tdr, "%-25s: 0x%02x (%u)", name, *v, *v);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS tdr_pull_uint16(struct tdr_pull *tdr, TALLOC_CTX *ctx, uint16_t *v)
+{
+ TDR_PULL_NEED_BYTES(tdr, 2);
+ *v = TDR_SVAL(tdr, tdr->offset);
+ tdr->offset += 2;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS tdr_push_uint16(struct tdr_push *tdr, const uint16_t *v)
+{
+ TDR_PUSH_NEED_BYTES(tdr, 2);
+ TDR_SSVAL(tdr, tdr->data.length, *v);
+ tdr->data.length += 2;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS tdr_print_uint16(struct tdr_print *tdr, const char *name, uint16_t *v)
+{
+ tdr->print(tdr, "%-25s: 0x%02x (%u)", name, *v, *v);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS tdr_pull_uint32(struct tdr_pull *tdr, TALLOC_CTX *ctx, uint32_t *v)
+{
+ TDR_PULL_NEED_BYTES(tdr, 4);
+ *v = TDR_IVAL(tdr, tdr->offset);
+ tdr->offset += 4;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS tdr_push_uint32(struct tdr_push *tdr, const uint32_t *v)
+{
+ TDR_PUSH_NEED_BYTES(tdr, 4);
+ TDR_SIVAL(tdr, tdr->data.length, *v);
+ tdr->data.length += 4;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS tdr_print_uint32(struct tdr_print *tdr, const char *name, uint32_t *v)
+{
+ tdr->print(tdr, "%-25s: 0x%02x (%u)", name, *v, *v);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS tdr_pull_charset(struct tdr_pull *tdr, TALLOC_CTX *ctx, const char **v, uint32_t length, uint32_t el_size, charset_t chset)
+{
+ size_t ret;
+
+ if (length == -1) {
+ switch (chset) {
+ case CH_DOS:
+ length = ascii_len_n((const char*)tdr->data.data+tdr->offset, tdr->data.length-tdr->offset);
+ break;
+ case CH_UTF16:
+ length = utf16_len_n(tdr->data.data+tdr->offset, tdr->data.length-tdr->offset);
+ break;
+
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ }
+
+ if (length == 0) {
+ *v = talloc_strdup(ctx, "");
+ return NT_STATUS_OK;
+ }
+
+ TDR_PULL_NEED_BYTES(tdr, el_size*length);
+
+ if (!convert_string_talloc_convenience(ctx, tdr->iconv_convenience, chset, CH_UNIX, tdr->data.data+tdr->offset, el_size*length, discard_const_p(void *, v), &ret, false)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ tdr->offset += length * el_size;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS tdr_push_charset(struct tdr_push *tdr, const char **v, uint32_t length, uint32_t el_size, charset_t chset)
+{
+ size_t ret, required;
+
+ if (length == -1) {
+ length = strlen(*v) + 1; /* Extra element for null character */
+ }
+
+ required = el_size * length;
+ TDR_PUSH_NEED_BYTES(tdr, required);
+
+ if (!convert_string_convenience(tdr->iconv_convenience, CH_UNIX, chset, *v, strlen(*v), tdr->data.data+tdr->data.length, required, &ret, false)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* Make sure the remaining part of the string is filled with zeroes */
+ if (ret < required) {
+ memset(tdr->data.data+tdr->data.length+ret, 0, required-ret);
+ }
+
+ tdr->data.length += required;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS tdr_print_charset(struct tdr_print *tdr, const char *name, const char **v, uint32_t length, uint32_t el_size, charset_t chset)
+{
+ tdr->print(tdr, "%-25s: %s", name, *v);
+ return NT_STATUS_OK;
+}
+
+/**
+ parse a hyper
+*/
+NTSTATUS tdr_pull_hyper(struct tdr_pull *tdr, TALLOC_CTX *ctx, uint64_t *v)
+{
+ TDR_PULL_NEED_BYTES(tdr, 8);
+ *v = TDR_IVAL(tdr, tdr->offset);
+ *v |= (uint64_t)(TDR_IVAL(tdr, tdr->offset+4)) << 32;
+ tdr->offset += 8;
+ return NT_STATUS_OK;
+}
+
+/**
+ push a hyper
+*/
+NTSTATUS tdr_push_hyper(struct tdr_push *tdr, uint64_t *v)
+{
+ TDR_PUSH_NEED_BYTES(tdr, 8);
+ TDR_SIVAL(tdr, tdr->data.length, ((*v) & 0xFFFFFFFF));
+ TDR_SIVAL(tdr, tdr->data.length+4, ((*v)>>32));
+ tdr->data.length += 8;
+ return NT_STATUS_OK;
+}
+
+/**
+ push a NTTIME
+*/
+NTSTATUS tdr_push_NTTIME(struct tdr_push *tdr, NTTIME *t)
+{
+ TDR_CHECK(tdr_push_hyper(tdr, t));
+ return NT_STATUS_OK;
+}
+
+/**
+ pull a NTTIME
+*/
+NTSTATUS tdr_pull_NTTIME(struct tdr_pull *tdr, TALLOC_CTX *ctx, NTTIME *t)
+{
+ TDR_CHECK(tdr_pull_hyper(tdr, ctx, t));
+ return NT_STATUS_OK;
+}
+
+/**
+ push a time_t
+*/
+NTSTATUS tdr_push_time_t(struct tdr_push *tdr, time_t *t)
+{
+ return tdr_push_uint32(tdr, (uint32_t *)t);
+}
+
+/**
+ pull a time_t
+*/
+NTSTATUS tdr_pull_time_t(struct tdr_pull *tdr, TALLOC_CTX *ctx, time_t *t)
+{
+ uint32_t tt;
+ TDR_CHECK(tdr_pull_uint32(tdr, ctx, &tt));
+ *t = tt;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS tdr_print_time_t(struct tdr_print *tdr, const char *name, time_t *t)
+{
+ if (*t == (time_t)-1 || *t == 0) {
+ tdr->print(tdr, "%-25s: (time_t)%d", name, (int)*t);
+ } else {
+ tdr->print(tdr, "%-25s: %s", name, timestring(tdr, *t));
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS tdr_print_NTTIME(struct tdr_print *tdr, const char *name, NTTIME *t)
+{
+ tdr->print(tdr, "%-25s: %s", name, nt_time_string(tdr, *t));
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS tdr_print_DATA_BLOB(struct tdr_print *tdr, const char *name, DATA_BLOB *r)
+{
+ tdr->print(tdr, "%-25s: DATA_BLOB length=%u", name, r->length);
+ if (r->length) {
+ dump_data(10, r->data, r->length);
+ }
+
+ return NT_STATUS_OK;
+}
+
+#define TDR_ALIGN(l,n) (((l) & ((n)-1)) == 0?0:((n)-((l)&((n)-1))))
+
+/*
+ push a DATA_BLOB onto the wire.
+*/
+NTSTATUS tdr_push_DATA_BLOB(struct tdr_push *tdr, DATA_BLOB *blob)
+{
+ if (tdr->flags & TDR_ALIGN2) {
+ blob->length = TDR_ALIGN(tdr->data.length, 2);
+ } else if (tdr->flags & TDR_ALIGN4) {
+ blob->length = TDR_ALIGN(tdr->data.length, 4);
+ } else if (tdr->flags & TDR_ALIGN8) {
+ blob->length = TDR_ALIGN(tdr->data.length, 8);
+ }
+
+ TDR_PUSH_NEED_BYTES(tdr, blob->length);
+
+ memcpy(tdr->data.data+tdr->data.length, blob->data, blob->length);
+ return NT_STATUS_OK;
+}
+
+/*
+ pull a DATA_BLOB from the wire.
+*/
+NTSTATUS tdr_pull_DATA_BLOB(struct tdr_pull *tdr, TALLOC_CTX *ctx, DATA_BLOB *blob)
+{
+ uint32_t length;
+
+ if (tdr->flags & TDR_ALIGN2) {
+ length = TDR_ALIGN(tdr->offset, 2);
+ } else if (tdr->flags & TDR_ALIGN4) {
+ length = TDR_ALIGN(tdr->offset, 4);
+ } else if (tdr->flags & TDR_ALIGN8) {
+ length = TDR_ALIGN(tdr->offset, 8);
+ } else if (tdr->flags & TDR_REMAINING) {
+ length = tdr->data.length - tdr->offset;
+ } else {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (tdr->data.length - tdr->offset < length) {
+ length = tdr->data.length - tdr->offset;
+ }
+
+ TDR_PULL_NEED_BYTES(tdr, length);
+
+ *blob = data_blob_talloc(tdr, tdr->data.data+tdr->offset, length);
+ tdr->offset += length;
+ return NT_STATUS_OK;
+}
+
+struct tdr_push *tdr_push_init(TALLOC_CTX *mem_ctx, struct smb_iconv_convenience *ic)
+{
+ struct tdr_push *push = talloc_zero(mem_ctx, struct tdr_push);
+
+ if (push == NULL)
+ return NULL;
+
+ push->iconv_convenience = talloc_reference(push, ic);
+
+ return push;
+}
+
+struct tdr_pull *tdr_pull_init(TALLOC_CTX *mem_ctx, struct smb_iconv_convenience *ic)
+{
+ struct tdr_pull *pull = talloc_zero(mem_ctx, struct tdr_pull);
+
+ if (pull == NULL)
+ return NULL;
+
+ pull->iconv_convenience = talloc_reference(pull, ic);
+
+ return pull;
+}
+
+NTSTATUS tdr_push_to_fd(int fd, struct smb_iconv_convenience *iconv_convenience, tdr_push_fn_t push_fn, const void *p)
+{
+ struct tdr_push *push = tdr_push_init(NULL, iconv_convenience);
+
+ if (push == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ if (NT_STATUS_IS_ERR(push_fn(push, p))) {
+ DEBUG(1, ("Error pushing data\n"));
+ talloc_free(push);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (write(fd, push->data.data, push->data.length) < push->data.length) {
+ DEBUG(1, ("Error writing all data\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ talloc_free(push);
+
+ return NT_STATUS_OK;
+}
+
+void tdr_print_debug_helper(struct tdr_print *tdr, const char *format, ...) _PRINTF_ATTRIBUTE(2,3)
+{
+ va_list ap;
+ char *s = NULL;
+ int i;
+
+ va_start(ap, format);
+ vasprintf(&s, format, ap);
+ va_end(ap);
+
+ for (i=0;i<tdr->level;i++) { DEBUG(0,(" ")); }
+
+ DEBUG(0,("%s\n", s));
+ free(s);
+}
diff --git a/source4/lib/tdr/tdr.h b/source4/lib/tdr/tdr.h
new file mode 100644
index 0000000000..c983cd35c1
--- /dev/null
+++ b/source4/lib/tdr/tdr.h
@@ -0,0 +1,67 @@
+/*
+ Unix SMB/CIFS implementation.
+ TDR definitions
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __TDR_H__
+#define __TDR_H__
+
+#include <talloc.h>
+#include "../lib/util/charset/charset.h"
+
+#define TDR_BIG_ENDIAN 0x01
+#define TDR_ALIGN2 0x02
+#define TDR_ALIGN4 0x04
+#define TDR_ALIGN8 0x08
+#define TDR_REMAINING 0x10
+
+struct tdr_pull {
+ DATA_BLOB data;
+ uint32_t offset;
+ int flags;
+ struct smb_iconv_convenience *iconv_convenience;
+};
+
+struct tdr_push {
+ DATA_BLOB data;
+ int flags;
+ struct smb_iconv_convenience *iconv_convenience;
+};
+
+struct tdr_print {
+ int level;
+ void (*print)(struct tdr_print *, const char *, ...);
+ int flags;
+};
+
+#define TDR_CHECK(call) do { NTSTATUS _status; \
+ _status = call; \
+ if (!NT_STATUS_IS_OK(_status)) \
+ return _status; \
+ } while (0)
+
+#define TDR_ALLOC(ctx, s, n) do { \
+ (s) = talloc_array_size(ctx, sizeof(*(s)), n); \
+ if ((n) && !(s)) return NT_STATUS_NO_MEMORY; \
+ } while (0)
+
+typedef NTSTATUS (*tdr_push_fn_t) (struct tdr_push *, const void *);
+typedef NTSTATUS (*tdr_pull_fn_t) (struct tdr_pull *, TALLOC_CTX *, void *);
+
+#include "lib/tdr/tdr_proto.h"
+
+#endif /* __TDR_H__ */
diff --git a/source4/lib/tdr/testsuite.c b/source4/lib/tdr/testsuite.c
new file mode 100644
index 0000000000..44c5810f90
--- /dev/null
+++ b/source4/lib/tdr/testsuite.c
@@ -0,0 +1,186 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for basic tdr functions
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "lib/tdr/tdr.h"
+#include "param/param.h"
+
+static bool test_push_uint8(struct torture_context *tctx)
+{
+ uint8_t v = 4;
+ struct tdr_push *tdr = tdr_push_init(tctx, lp_iconv_convenience(tctx->lp_ctx));
+
+ torture_assert_ntstatus_ok(tctx, tdr_push_uint8(tdr, &v), "push failed");
+ torture_assert_int_equal(tctx, tdr->data.length, 1, "length incorrect");
+ torture_assert_int_equal(tctx, tdr->data.data[0], 4, "data incorrect");
+ return true;
+}
+
+static bool test_pull_uint8(struct torture_context *tctx)
+{
+ uint8_t d = 2;
+ uint8_t l;
+ struct tdr_pull *tdr = tdr_pull_init(tctx, lp_iconv_convenience(tctx->lp_ctx));
+ tdr->data.data = &d;
+ tdr->data.length = 1;
+ tdr->offset = 0;
+ tdr->flags = 0;
+ torture_assert_ntstatus_ok(tctx, tdr_pull_uint8(tdr, tctx, &l),
+ "pull failed");
+ torture_assert_int_equal(tctx, 1, tdr->offset,
+ "offset invalid");
+ return true;
+}
+
+static bool test_push_uint16(struct torture_context *tctx)
+{
+ uint16_t v = 0xF32;
+ struct tdr_push *tdr = tdr_push_init(tctx, lp_iconv_convenience(tctx->lp_ctx));
+
+ torture_assert_ntstatus_ok(tctx, tdr_push_uint16(tdr, &v), "push failed");
+ torture_assert_int_equal(tctx, tdr->data.length, 2, "length incorrect");
+ torture_assert_int_equal(tctx, tdr->data.data[0], 0x32, "data incorrect");
+ torture_assert_int_equal(tctx, tdr->data.data[1], 0x0F, "data incorrect");
+ return true;
+}
+
+static bool test_pull_uint16(struct torture_context *tctx)
+{
+ uint8_t d[2] = { 782 & 0xFF, (782 & 0xFF00) / 0x100 };
+ uint16_t l;
+ struct tdr_pull *tdr = tdr_pull_init(tctx, lp_iconv_convenience(tctx->lp_ctx));
+ tdr->data.data = d;
+ tdr->data.length = 2;
+ tdr->offset = 0;
+ tdr->flags = 0;
+ torture_assert_ntstatus_ok(tctx, tdr_pull_uint16(tdr, tctx, &l),
+ "pull failed");
+ torture_assert_int_equal(tctx, 2, tdr->offset, "offset invalid");
+ torture_assert_int_equal(tctx, 782, l, "right int read");
+ return true;
+}
+
+static bool test_push_uint32(struct torture_context *tctx)
+{
+ uint32_t v = 0x100F32;
+ struct tdr_push *tdr = tdr_push_init(tctx, lp_iconv_convenience(tctx->lp_ctx));
+
+ torture_assert_ntstatus_ok(tctx, tdr_push_uint32(tdr, &v), "push failed");
+ torture_assert_int_equal(tctx, tdr->data.length, 4, "length incorrect");
+ torture_assert_int_equal(tctx, tdr->data.data[0], 0x32, "data incorrect");
+ torture_assert_int_equal(tctx, tdr->data.data[1], 0x0F, "data incorrect");
+ torture_assert_int_equal(tctx, tdr->data.data[2], 0x10, "data incorrect");
+ torture_assert_int_equal(tctx, tdr->data.data[3], 0x00, "data incorrect");
+ return true;
+}
+
+static bool test_pull_uint32(struct torture_context *tctx)
+{
+ uint8_t d[4] = { 782 & 0xFF, (782 & 0xFF00) / 0x100, 0, 0 };
+ uint32_t l;
+ struct tdr_pull *tdr = tdr_pull_init(tctx, lp_iconv_convenience(tctx->lp_ctx));
+ tdr->data.data = d;
+ tdr->data.length = 4;
+ tdr->offset = 0;
+ tdr->flags = 0;
+ torture_assert_ntstatus_ok(tctx, tdr_pull_uint32(tdr, tctx, &l),
+ "pull failed");
+ torture_assert_int_equal(tctx, 4, tdr->offset, "offset invalid");
+ torture_assert_int_equal(tctx, 782, l, "right int read");
+ return true;
+}
+
+static bool test_pull_charset(struct torture_context *tctx)
+{
+ struct tdr_pull *tdr = tdr_pull_init(tctx, lp_iconv_convenience(tctx->lp_ctx));
+ const char *l = NULL;
+ tdr->data.data = (uint8_t *)talloc_strdup(tctx, "bla");
+ tdr->data.length = 4;
+ tdr->offset = 0;
+ tdr->flags = 0;
+ torture_assert_ntstatus_ok(tctx, tdr_pull_charset(tdr, tctx, &l, -1, 1, CH_DOS),
+ "pull failed");
+ torture_assert_int_equal(tctx, 4, tdr->offset, "offset invalid");
+ torture_assert_str_equal(tctx, "bla", l, "right int read");
+
+ tdr->offset = 0;
+ torture_assert_ntstatus_ok(tctx, tdr_pull_charset(tdr, tctx, &l, 2, 1, CH_UNIX),
+ "pull failed");
+ torture_assert_int_equal(tctx, 2, tdr->offset, "offset invalid");
+ torture_assert_str_equal(tctx, "bl", l, "right int read");
+
+ return true;
+}
+
+static bool test_pull_charset_empty(struct torture_context *tctx)
+{
+ struct tdr_pull *tdr = tdr_pull_init(tctx, lp_iconv_convenience(tctx->lp_ctx));
+ const char *l = NULL;
+ tdr->data.data = (uint8_t *)talloc_strdup(tctx, "bla");
+ tdr->data.length = 4;
+ tdr->offset = 0;
+ tdr->flags = 0;
+ torture_assert_ntstatus_ok(tctx, tdr_pull_charset(tdr, tctx, &l, 0, 1, CH_DOS),
+ "pull failed");
+ torture_assert_int_equal(tctx, 0, tdr->offset, "offset invalid");
+ torture_assert_str_equal(tctx, "", l, "right string read");
+
+ return true;
+}
+
+
+
+static bool test_push_charset(struct torture_context *tctx)
+{
+ const char *l = "bloe";
+ struct tdr_push *tdr = tdr_push_init(tctx, lp_iconv_convenience(tctx->lp_ctx));
+ torture_assert_ntstatus_ok(tctx, tdr_push_charset(tdr, &l, 4, 1, CH_UTF8),
+ "push failed");
+ torture_assert_int_equal(tctx, 4, tdr->data.length, "offset invalid");
+ torture_assert(tctx, strcmp("bloe", (const char *)tdr->data.data) == 0, "right string push");
+
+ torture_assert_ntstatus_ok(tctx, tdr_push_charset(tdr, &l, -1, 1, CH_UTF8),
+ "push failed");
+ torture_assert_int_equal(tctx, 9, tdr->data.length, "offset invalid");
+ torture_assert_str_equal(tctx, "bloe", (const char *)tdr->data.data+4, "right string read");
+
+ return true;
+}
+
+struct torture_suite *torture_local_tdr(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "TDR");
+
+ torture_suite_add_simple_test(suite, "pull_uint8", test_pull_uint8);
+ torture_suite_add_simple_test(suite, "push_uint8", test_push_uint8);
+
+ torture_suite_add_simple_test(suite, "pull_uint16", test_pull_uint16);
+ torture_suite_add_simple_test(suite, "push_uint16", test_push_uint16);
+
+ torture_suite_add_simple_test(suite, "pull_uint32", test_pull_uint32);
+ torture_suite_add_simple_test(suite, "push_uint32", test_push_uint32);
+
+ torture_suite_add_simple_test(suite, "pull_charset", test_pull_charset);
+ torture_suite_add_simple_test(suite, "pull_charset", test_pull_charset_empty);
+ torture_suite_add_simple_test(suite, "push_charset", test_push_charset);
+
+ return suite;
+}
diff --git a/source4/lib/tls/config.m4 b/source4/lib/tls/config.m4
new file mode 100644
index 0000000000..c46a009e3d
--- /dev/null
+++ b/source4/lib/tls/config.m4
@@ -0,0 +1,45 @@
+###############################
+# start SMB_EXT_LIB_GNUTLS
+# check for gnutls/gnutls.h and -lgnutls
+
+use_gnutls=auto
+AC_ARG_ENABLE(gnutls,
+AS_HELP_STRING([--enable-gnutls],[Turn on gnutls support (default=yes)]),
+ [if test x$enable_gnutls = xno; then
+ use_gnutls=no
+ fi])
+
+
+if test x$use_gnutls = xauto && pkg-config --exists gnutls; then
+ SMB_EXT_LIB_FROM_PKGCONFIG(GNUTLS, gnutls < 2.6.0,
+ [use_gnutls=yes],
+ [use_gnutls=no])
+fi
+
+if test x$use_gnutls = xauto; then
+ AC_CHECK_HEADERS(gnutls/gnutls.h)
+ AC_CHECK_LIB_EXT(gnutls, GNUTLS_LIBS, gnutls_global_init)
+ AC_CHECK_DECL(gnutls_x509_crt_set_version,
+ [AC_DEFINE(HAVE_GNUTLS_X509_CRT_SET_VERSION,1,gnutls set_version)], [], [
+ #include <gnutls/gnutls.h>
+ #include <gnutls/x509.h>
+ ])
+ if test x"$ac_cv_header_gnutls_gnutls_h" = x"yes" -a x"$ac_cv_lib_ext_gnutls_gnutls_global_init" = x"yes" -a x"$ac_cv_have_decl_gnutls_x509_crt_set_version" = x"yes";then
+ SMB_ENABLE(GNUTLS,YES)
+ AC_CHECK_DECL(gnutls_x509_crt_set_subject_key_id,
+ [AC_DEFINE(HAVE_GNUTLS_X509_CRT_SET_SUBJECT_KEY_ID,1,gnutls subject_key)], [], [
+ #include <gnutls/gnutls.h>
+ #include <gnutls/x509.h>
+ ])
+ fi
+ SMB_EXT_LIB(GNUTLS, $GNUTLS_LIBS)
+fi
+if test x$use_gnutls = xyes; then
+ #Some older versions have a different type name
+ AC_CHECK_TYPES([gnutls_datum],,,[#include "gnutls/gnutls.h"])
+ AC_CHECK_TYPES([gnutls_datum_t],,,[#include "gnutls/gnutls.h"])
+ AC_DEFINE(ENABLE_GNUTLS,1,[Whether we have gnutls support (SSL)])
+ AC_CHECK_HEADERS(gcrypt.h)
+ AC_CHECK_LIB_EXT(gcrypt, GCRYPT_LIBS, gcry_control)
+ SMB_EXT_LIB(GCRYPT, $GCRYPT_LIBS)
+fi
diff --git a/source4/lib/tls/config.mk b/source4/lib/tls/config.mk
new file mode 100644
index 0000000000..0e1978cc1b
--- /dev/null
+++ b/source4/lib/tls/config.mk
@@ -0,0 +1,5 @@
+[SUBSYSTEM::LIBTLS]
+PUBLIC_DEPENDENCIES = \
+ LIBTALLOC GNUTLS GCRYPT LIBSAMBA-HOSTCONFIG samba_socket
+
+LIBTLS_OBJ_FILES = $(addprefix $(libtlssrcdir)/, tls.o tlscert.o)
diff --git a/source4/lib/tls/tls.c b/source4/lib/tls/tls.c
new file mode 100644
index 0000000000..1014ab07a8
--- /dev/null
+++ b/source4/lib/tls/tls.c
@@ -0,0 +1,695 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ transport layer security handling code
+
+ Copyright (C) Andrew Tridgell 2004-2005
+ Copyright (C) Stefan Metzmacher 2004
+ Copyright (C) Andrew Bartlett 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/events/events.h"
+#include "lib/socket/socket.h"
+#include "lib/tls/tls.h"
+#include "param/param.h"
+
+#if ENABLE_GNUTLS
+#include "gnutls/gnutls.h"
+
+#define DH_BITS 1024
+
+#if defined(HAVE_GNUTLS_DATUM) && !defined(HAVE_GNUTLS_DATUM_T)
+typedef gnutls_datum gnutls_datum_t;
+#endif
+
+/* hold persistent tls data */
+struct tls_params {
+ gnutls_certificate_credentials x509_cred;
+ gnutls_dh_params dh_params;
+ bool tls_enabled;
+};
+#endif
+
+/* hold per connection tls data */
+struct tls_context {
+ struct socket_context *socket;
+ struct tevent_fd *fde;
+ bool tls_enabled;
+#if ENABLE_GNUTLS
+ gnutls_session session;
+ bool done_handshake;
+ bool have_first_byte;
+ uint8_t first_byte;
+ bool tls_detect;
+ const char *plain_chars;
+ bool output_pending;
+ gnutls_certificate_credentials xcred;
+ bool interrupted;
+#endif
+};
+
+bool tls_enabled(struct socket_context *sock)
+{
+ struct tls_context *tls;
+ if (!sock) {
+ return false;
+ }
+ if (strcmp(sock->backend_name, "tls") != 0) {
+ return false;
+ }
+ tls = talloc_get_type(sock->private_data, struct tls_context);
+ if (!tls) {
+ return false;
+ }
+ return tls->tls_enabled;
+}
+
+
+#if ENABLE_GNUTLS
+
+static const struct socket_ops tls_socket_ops;
+
+static NTSTATUS tls_socket_init(struct socket_context *sock)
+{
+ switch (sock->type) {
+ case SOCKET_TYPE_STREAM:
+ break;
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ sock->backend_name = "tls";
+
+ return NT_STATUS_OK;
+}
+
+#define TLSCHECK(call) do { \
+ ret = call; \
+ if (ret < 0) { \
+ DEBUG(0,("TLS %s - %s\n", #call, gnutls_strerror(ret))); \
+ goto failed; \
+ } \
+} while (0)
+
+
+/*
+ callback for reading from a socket
+*/
+static ssize_t tls_pull(gnutls_transport_ptr ptr, void *buf, size_t size)
+{
+ struct tls_context *tls = talloc_get_type(ptr, struct tls_context);
+ NTSTATUS status;
+ size_t nread;
+
+ if (tls->have_first_byte) {
+ *(uint8_t *)buf = tls->first_byte;
+ tls->have_first_byte = false;
+ return 1;
+ }
+
+ status = socket_recv(tls->socket, buf, size, &nread);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE)) {
+ return 0;
+ }
+ if (NT_STATUS_IS_ERR(status)) {
+ EVENT_FD_NOT_READABLE(tls->fde);
+ EVENT_FD_NOT_WRITEABLE(tls->fde);
+ errno = EBADF;
+ return -1;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ EVENT_FD_READABLE(tls->fde);
+ errno = EAGAIN;
+ return -1;
+ }
+ if (tls->output_pending) {
+ EVENT_FD_WRITEABLE(tls->fde);
+ }
+ if (size != nread) {
+ EVENT_FD_READABLE(tls->fde);
+ }
+ return nread;
+}
+
+/*
+ callback for writing to a socket
+*/
+static ssize_t tls_push(gnutls_transport_ptr ptr, const void *buf, size_t size)
+{
+ struct tls_context *tls = talloc_get_type(ptr, struct tls_context);
+ NTSTATUS status;
+ size_t nwritten;
+ DATA_BLOB b;
+
+ if (!tls->tls_enabled) {
+ return size;
+ }
+
+ b.data = discard_const(buf);
+ b.length = size;
+
+ status = socket_send(tls->socket, &b, &nwritten);
+ if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
+ errno = EAGAIN;
+ return -1;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ EVENT_FD_WRITEABLE(tls->fde);
+ return -1;
+ }
+ if (size != nwritten) {
+ EVENT_FD_WRITEABLE(tls->fde);
+ }
+ return nwritten;
+}
+
+/*
+ destroy a tls session
+ */
+static int tls_destructor(struct tls_context *tls)
+{
+ int ret;
+ ret = gnutls_bye(tls->session, GNUTLS_SHUT_WR);
+ if (ret < 0) {
+ DEBUG(0,("TLS gnutls_bye failed - %s\n", gnutls_strerror(ret)));
+ }
+ return 0;
+}
+
+
+/*
+ possibly continue the handshake process
+*/
+static NTSTATUS tls_handshake(struct tls_context *tls)
+{
+ int ret;
+
+ if (tls->done_handshake) {
+ return NT_STATUS_OK;
+ }
+
+ ret = gnutls_handshake(tls->session);
+ if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) {
+ if (gnutls_record_get_direction(tls->session) == 1) {
+ EVENT_FD_WRITEABLE(tls->fde);
+ }
+ return STATUS_MORE_ENTRIES;
+ }
+ if (ret < 0) {
+ DEBUG(0,("TLS gnutls_handshake failed - %s\n", gnutls_strerror(ret)));
+ return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+ tls->done_handshake = true;
+ return NT_STATUS_OK;
+}
+
+/*
+ possibly continue an interrupted operation
+*/
+static NTSTATUS tls_interrupted(struct tls_context *tls)
+{
+ int ret;
+
+ if (!tls->interrupted) {
+ return NT_STATUS_OK;
+ }
+ if (gnutls_record_get_direction(tls->session) == 1) {
+ ret = gnutls_record_send(tls->session, NULL, 0);
+ } else {
+ ret = gnutls_record_recv(tls->session, NULL, 0);
+ }
+ if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) {
+ return STATUS_MORE_ENTRIES;
+ }
+ tls->interrupted = false;
+ return NT_STATUS_OK;
+}
+
+/*
+ see how many bytes are pending on the connection
+*/
+static NTSTATUS tls_socket_pending(struct socket_context *sock, size_t *npending)
+{
+ struct tls_context *tls = talloc_get_type(sock->private_data, struct tls_context);
+ if (!tls->tls_enabled || tls->tls_detect) {
+ return socket_pending(tls->socket, npending);
+ }
+ *npending = gnutls_record_check_pending(tls->session);
+ if (*npending == 0) {
+ NTSTATUS status = socket_pending(tls->socket, npending);
+ if (*npending == 0) {
+ /* seems to be a gnutls bug */
+ (*npending) = 100;
+ }
+ return status;
+ }
+ return NT_STATUS_OK;
+}
+
+/*
+ receive data either by tls or normal socket_recv
+*/
+static NTSTATUS tls_socket_recv(struct socket_context *sock, void *buf,
+ size_t wantlen, size_t *nread)
+{
+ int ret;
+ NTSTATUS status;
+ struct tls_context *tls = talloc_get_type(sock->private_data, struct tls_context);
+
+ if (tls->tls_enabled && tls->tls_detect) {
+ status = socket_recv(tls->socket, &tls->first_byte, 1, nread);
+ NT_STATUS_NOT_OK_RETURN(status);
+ if (*nread == 0) return NT_STATUS_OK;
+ tls->tls_detect = false;
+ /* look for the first byte of a valid HTTP operation */
+ if (strchr(tls->plain_chars, tls->first_byte)) {
+ /* not a tls link */
+ tls->tls_enabled = false;
+ *(uint8_t *)buf = tls->first_byte;
+ return NT_STATUS_OK;
+ }
+ tls->have_first_byte = true;
+ }
+
+ if (!tls->tls_enabled) {
+ return socket_recv(tls->socket, buf, wantlen, nread);
+ }
+
+ status = tls_handshake(tls);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ status = tls_interrupted(tls);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ ret = gnutls_record_recv(tls->session, buf, wantlen);
+ if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) {
+ if (gnutls_record_get_direction(tls->session) == 1) {
+ EVENT_FD_WRITEABLE(tls->fde);
+ }
+ tls->interrupted = true;
+ return STATUS_MORE_ENTRIES;
+ }
+ if (ret < 0) {
+ return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+ *nread = ret;
+ return NT_STATUS_OK;
+}
+
+
+/*
+ send data either by tls or normal socket_recv
+*/
+static NTSTATUS tls_socket_send(struct socket_context *sock,
+ const DATA_BLOB *blob, size_t *sendlen)
+{
+ NTSTATUS status;
+ int ret;
+ struct tls_context *tls = talloc_get_type(sock->private_data, struct tls_context);
+
+ if (!tls->tls_enabled) {
+ return socket_send(tls->socket, blob, sendlen);
+ }
+
+ status = tls_handshake(tls);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ status = tls_interrupted(tls);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ ret = gnutls_record_send(tls->session, blob->data, blob->length);
+ if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) {
+ if (gnutls_record_get_direction(tls->session) == 1) {
+ EVENT_FD_WRITEABLE(tls->fde);
+ }
+ tls->interrupted = true;
+ return STATUS_MORE_ENTRIES;
+ }
+ if (ret < 0) {
+ DEBUG(0,("gnutls_record_send of %d failed - %s\n", (int)blob->length, gnutls_strerror(ret)));
+ return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+ *sendlen = ret;
+ tls->output_pending = (ret < blob->length);
+ return NT_STATUS_OK;
+}
+
+
+/*
+ initialise global tls state
+*/
+struct tls_params *tls_initialise(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx)
+{
+ struct tls_params *params;
+ int ret;
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+ const char *keyfile = lp_tls_keyfile(tmp_ctx, lp_ctx);
+ const char *certfile = lp_tls_certfile(tmp_ctx, lp_ctx);
+ const char *cafile = lp_tls_cafile(tmp_ctx, lp_ctx);
+ const char *crlfile = lp_tls_crlfile(tmp_ctx, lp_ctx);
+ const char *dhpfile = lp_tls_dhpfile(tmp_ctx, lp_ctx);
+ void tls_cert_generate(TALLOC_CTX *, const char *, const char *, const char *, const char *);
+ params = talloc(mem_ctx, struct tls_params);
+ if (params == NULL) {
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+
+ if (!lp_tls_enabled(lp_ctx) || keyfile == NULL || *keyfile == 0) {
+ params->tls_enabled = false;
+ talloc_free(tmp_ctx);
+ return params;
+ }
+
+ if (!file_exist(cafile)) {
+ char *hostname = talloc_asprintf(mem_ctx, "%s.%s",
+ lp_netbios_name(lp_ctx), lp_realm(lp_ctx));
+ if (hostname == NULL) {
+ goto init_failed;
+ }
+ tls_cert_generate(params, hostname, keyfile, certfile, cafile);
+ talloc_free(hostname);
+ }
+
+ ret = gnutls_global_init();
+ if (ret < 0) goto init_failed;
+
+ gnutls_certificate_allocate_credentials(&params->x509_cred);
+ if (ret < 0) goto init_failed;
+
+ if (cafile && *cafile) {
+ ret = gnutls_certificate_set_x509_trust_file(params->x509_cred, cafile,
+ GNUTLS_X509_FMT_PEM);
+ if (ret < 0) {
+ DEBUG(0,("TLS failed to initialise cafile %s\n", cafile));
+ goto init_failed;
+ }
+ }
+
+ if (crlfile && *crlfile) {
+ ret = gnutls_certificate_set_x509_crl_file(params->x509_cred,
+ crlfile,
+ GNUTLS_X509_FMT_PEM);
+ if (ret < 0) {
+ DEBUG(0,("TLS failed to initialise crlfile %s\n", crlfile));
+ goto init_failed;
+ }
+ }
+
+ ret = gnutls_certificate_set_x509_key_file(params->x509_cred,
+ certfile, keyfile,
+ GNUTLS_X509_FMT_PEM);
+ if (ret < 0) {
+ DEBUG(0,("TLS failed to initialise certfile %s and keyfile %s\n",
+ certfile, keyfile));
+ goto init_failed;
+ }
+
+
+ ret = gnutls_dh_params_init(&params->dh_params);
+ if (ret < 0) goto init_failed;
+
+ if (dhpfile && *dhpfile) {
+ gnutls_datum_t dhparms;
+ size_t size;
+ dhparms.data = (uint8_t *)file_load(dhpfile, &size, 0, mem_ctx);
+
+ if (!dhparms.data) {
+ DEBUG(0,("Failed to read DH Parms from %s\n", dhpfile));
+ goto init_failed;
+ }
+ dhparms.size = size;
+
+ ret = gnutls_dh_params_import_pkcs3(params->dh_params, &dhparms, GNUTLS_X509_FMT_PEM);
+ if (ret < 0) goto init_failed;
+ } else {
+ ret = gnutls_dh_params_generate2(params->dh_params, DH_BITS);
+ if (ret < 0) goto init_failed;
+ }
+
+ gnutls_certificate_set_dh_params(params->x509_cred, params->dh_params);
+
+ params->tls_enabled = true;
+
+ talloc_free(tmp_ctx);
+ return params;
+
+init_failed:
+ DEBUG(0,("GNUTLS failed to initialise - %s\n", gnutls_strerror(ret)));
+ params->tls_enabled = false;
+ talloc_free(tmp_ctx);
+ return params;
+}
+
+
+/*
+ setup for a new connection
+*/
+struct socket_context *tls_init_server(struct tls_params *params,
+ struct socket_context *socket_ctx,
+ struct tevent_fd *fde,
+ const char *plain_chars)
+{
+ struct tls_context *tls;
+ int ret;
+ struct socket_context *new_sock;
+ NTSTATUS nt_status;
+
+ nt_status = socket_create_with_ops(socket_ctx, &tls_socket_ops, &new_sock,
+ SOCKET_TYPE_STREAM,
+ socket_ctx->flags | SOCKET_FLAG_ENCRYPT);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return NULL;
+ }
+
+ tls = talloc(new_sock, struct tls_context);
+ if (tls == NULL) {
+ return NULL;
+ }
+
+ tls->socket = socket_ctx;
+ tls->fde = fde;
+ if (talloc_reference(tls, fde) == NULL) {
+ talloc_free(new_sock);
+ return NULL;
+ }
+ if (talloc_reference(tls, socket_ctx) == NULL) {
+ talloc_free(new_sock);
+ return NULL;
+ }
+
+ new_sock->private_data = tls;
+
+ if (!params->tls_enabled) {
+ talloc_free(new_sock);
+ return NULL;
+ }
+
+ TLSCHECK(gnutls_init(&tls->session, GNUTLS_SERVER));
+
+ talloc_set_destructor(tls, tls_destructor);
+
+ TLSCHECK(gnutls_set_default_priority(tls->session));
+ TLSCHECK(gnutls_credentials_set(tls->session, GNUTLS_CRD_CERTIFICATE,
+ params->x509_cred));
+ gnutls_certificate_server_set_request(tls->session, GNUTLS_CERT_REQUEST);
+ gnutls_dh_set_prime_bits(tls->session, DH_BITS);
+ gnutls_transport_set_ptr(tls->session, (gnutls_transport_ptr)tls);
+ gnutls_transport_set_pull_function(tls->session, (gnutls_pull_func)tls_pull);
+ gnutls_transport_set_push_function(tls->session, (gnutls_push_func)tls_push);
+ gnutls_transport_set_lowat(tls->session, 0);
+
+ tls->plain_chars = plain_chars;
+ if (plain_chars) {
+ tls->tls_detect = true;
+ } else {
+ tls->tls_detect = false;
+ }
+
+ tls->output_pending = false;
+ tls->done_handshake = false;
+ tls->have_first_byte = false;
+ tls->tls_enabled = true;
+ tls->interrupted = false;
+
+ new_sock->state = SOCKET_STATE_SERVER_CONNECTED;
+
+ return new_sock;
+
+failed:
+ DEBUG(0,("TLS init connection failed - %s\n", gnutls_strerror(ret)));
+ talloc_free(new_sock);
+ return NULL;
+}
+
+
+/*
+ setup for a new client connection
+*/
+struct socket_context *tls_init_client(struct socket_context *socket_ctx,
+ struct tevent_fd *fde,
+ const char *ca_path)
+{
+ struct tls_context *tls;
+ int ret = 0;
+ const int cert_type_priority[] = { GNUTLS_CRT_X509, GNUTLS_CRT_OPENPGP, 0 };
+ char *cafile;
+ struct socket_context *new_sock;
+ NTSTATUS nt_status;
+
+ nt_status = socket_create_with_ops(socket_ctx, &tls_socket_ops, &new_sock,
+ SOCKET_TYPE_STREAM,
+ socket_ctx->flags | SOCKET_FLAG_ENCRYPT);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return NULL;
+ }
+
+ tls = talloc(new_sock, struct tls_context);
+ if (tls == NULL) return NULL;
+
+ tls->socket = socket_ctx;
+ tls->fde = fde;
+ if (talloc_reference(tls, fde) == NULL) {
+ return NULL;
+ }
+ if (talloc_reference(tls, socket_ctx) == NULL) {
+ return NULL;
+ }
+ new_sock->private_data = tls;
+
+ gnutls_global_init();
+
+ gnutls_certificate_allocate_credentials(&tls->xcred);
+ gnutls_certificate_set_x509_trust_file(tls->xcred, cafile, GNUTLS_X509_FMT_PEM);
+ TLSCHECK(gnutls_init(&tls->session, GNUTLS_CLIENT));
+ TLSCHECK(gnutls_set_default_priority(tls->session));
+ gnutls_certificate_type_set_priority(tls->session, cert_type_priority);
+ TLSCHECK(gnutls_credentials_set(tls->session, GNUTLS_CRD_CERTIFICATE, tls->xcred));
+
+ talloc_set_destructor(tls, tls_destructor);
+
+ gnutls_transport_set_ptr(tls->session, (gnutls_transport_ptr)tls);
+ gnutls_transport_set_pull_function(tls->session, (gnutls_pull_func)tls_pull);
+ gnutls_transport_set_push_function(tls->session, (gnutls_push_func)tls_push);
+ gnutls_transport_set_lowat(tls->session, 0);
+ tls->tls_detect = false;
+
+ tls->output_pending = false;
+ tls->done_handshake = false;
+ tls->have_first_byte = false;
+ tls->tls_enabled = true;
+ tls->interrupted = false;
+
+ new_sock->state = SOCKET_STATE_CLIENT_CONNECTED;
+
+ return new_sock;
+
+failed:
+ DEBUG(0,("TLS init connection failed - %s\n", gnutls_strerror(ret)));
+ tls->tls_enabled = false;
+ return new_sock;
+}
+
+static NTSTATUS tls_socket_set_option(struct socket_context *sock, const char *option, const char *val)
+{
+ set_socket_options(socket_get_fd(sock), option);
+ return NT_STATUS_OK;
+}
+
+static char *tls_socket_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx)
+{
+ struct tls_context *tls = talloc_get_type(sock->private_data, struct tls_context);
+ return socket_get_peer_name(tls->socket, mem_ctx);
+}
+
+static struct socket_address *tls_socket_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
+{
+ struct tls_context *tls = talloc_get_type(sock->private_data, struct tls_context);
+ return socket_get_peer_addr(tls->socket, mem_ctx);
+}
+
+static struct socket_address *tls_socket_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
+{
+ struct tls_context *tls = talloc_get_type(sock->private_data, struct tls_context);
+ return socket_get_my_addr(tls->socket, mem_ctx);
+}
+
+static int tls_socket_get_fd(struct socket_context *sock)
+{
+ struct tls_context *tls = talloc_get_type(sock->private_data, struct tls_context);
+ return socket_get_fd(tls->socket);
+}
+
+static const struct socket_ops tls_socket_ops = {
+ .name = "tls",
+ .fn_init = tls_socket_init,
+ .fn_recv = tls_socket_recv,
+ .fn_send = tls_socket_send,
+ .fn_pending = tls_socket_pending,
+
+ .fn_set_option = tls_socket_set_option,
+
+ .fn_get_peer_name = tls_socket_get_peer_name,
+ .fn_get_peer_addr = tls_socket_get_peer_addr,
+ .fn_get_my_addr = tls_socket_get_my_addr,
+ .fn_get_fd = tls_socket_get_fd
+};
+
+bool tls_support(struct tls_params *params)
+{
+ return params->tls_enabled;
+}
+
+#else
+
+/* for systems without tls we just fail the operations, and the caller
+ * will retain the original socket */
+
+struct tls_params *tls_initialise(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx)
+{
+ return talloc_new(mem_ctx);
+}
+
+/*
+ setup for a new connection
+*/
+struct socket_context *tls_init_server(struct tls_params *params,
+ struct socket_context *socket,
+ struct tevent_fd *fde,
+ const char *plain_chars)
+{
+ return NULL;
+}
+
+
+/*
+ setup for a new client connection
+*/
+struct socket_context *tls_init_client(struct socket_context *socket,
+ struct tevent_fd *fde,
+ const char *ca_path)
+{
+ return NULL;
+}
+
+bool tls_support(struct tls_params *params)
+{
+ return false;
+}
+
+#endif
+
diff --git a/source4/lib/tls/tls.h b/source4/lib/tls/tls.h
new file mode 100644
index 0000000000..c5bd5c87cc
--- /dev/null
+++ b/source4/lib/tls/tls.h
@@ -0,0 +1,68 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ transport layer security handling code
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _TLS_H_
+#define _TLS_H_
+
+#include "lib/socket/socket.h"
+
+struct loadparm_context;
+
+/*
+ call tls_initialise() once per task to startup the tls subsystem
+*/
+struct tls_params *tls_initialise(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx);
+
+/*
+ call tls_init_server() on each new server connection
+
+ the 'plain_chars' parameter is a list of chars that when they occur
+ as the first character from the client on the connection tell the
+ tls code that this is a non-tls connection. This can be used to have
+ tls and non-tls servers on the same port. If this is NULL then only
+ tls connections will be allowed
+*/
+struct socket_context *tls_init_server(struct tls_params *parms,
+ struct socket_context *sock,
+ struct tevent_fd *fde,
+ const char *plain_chars);
+
+/*
+ call tls_init_client() on each new client connection
+*/
+struct socket_context *tls_init_client(struct socket_context *sock,
+ struct tevent_fd *fde,
+ const char *cafile);
+
+/*
+ return True if a connection used tls
+*/
+bool tls_enabled(struct socket_context *tls);
+
+
+/*
+ true if tls support is compiled in
+*/
+bool tls_support(struct tls_params *parms);
+
+const struct socket_ops *socket_tls_ops(enum socket_type type);
+
+#endif
diff --git a/source4/lib/tls/tlscert.c b/source4/lib/tls/tlscert.c
new file mode 100644
index 0000000000..62e7a72240
--- /dev/null
+++ b/source4/lib/tls/tlscert.c
@@ -0,0 +1,166 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ auto-generate self signed TLS certificates
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+#if ENABLE_GNUTLS
+#include "gnutls/gnutls.h"
+#include "gnutls/x509.h"
+#if HAVE_GCRYPT_H
+#include <gcrypt.h>
+#endif
+
+#define ORGANISATION_NAME "Samba Administration"
+#define UNIT_NAME "Samba - temporary autogenerated certificate"
+#define LIFETIME 700*24*60*60
+#define DH_BITS 1024
+
+/*
+ auto-generate a set of self signed certificates
+*/
+void tls_cert_generate(TALLOC_CTX *mem_ctx,
+ const char *hostname,
+ const char *keyfile, const char *certfile,
+ const char *cafile)
+{
+ gnutls_x509_crt cacrt, crt;
+ gnutls_x509_privkey key, cakey;
+ uint32_t serial = (uint32_t)time(NULL);
+ unsigned char keyid[100];
+ char buf[4096];
+ size_t bufsize;
+ size_t keyidsize = sizeof(keyid);
+ time_t activation = time(NULL), expiry = activation + LIFETIME;
+ int ret;
+
+ if (file_exist(keyfile) || file_exist(certfile) || file_exist(cafile)) {
+ DEBUG(0,("TLS autogeneration skipped - some TLS files already exist\n"));
+ return;
+ }
+
+#define TLSCHECK(call) do { \
+ ret = call; \
+ if (ret < 0) { \
+ DEBUG(0,("TLS %s - %s\n", #call, gnutls_strerror(ret))); \
+ goto failed; \
+ } \
+} while (0)
+
+ TLSCHECK(gnutls_global_init());
+
+ DEBUG(0,("Attempting to autogenerate TLS self-signed keys for https for hostname '%s'\n",
+ hostname));
+
+#ifdef HAVE_GCRYPT_H
+ DEBUG(3,("Enabling QUICK mode in gcrypt\n"));
+ gcry_control(GCRYCTL_ENABLE_QUICK_RANDOM, 0);
+#endif
+
+ DEBUG(3,("Generating private key\n"));
+ TLSCHECK(gnutls_x509_privkey_init(&key));
+ TLSCHECK(gnutls_x509_privkey_generate(key, GNUTLS_PK_RSA, DH_BITS, 0));
+
+ DEBUG(3,("Generating CA private key\n"));
+ TLSCHECK(gnutls_x509_privkey_init(&cakey));
+ TLSCHECK(gnutls_x509_privkey_generate(cakey, GNUTLS_PK_RSA, DH_BITS, 0));
+
+ DEBUG(3,("Generating CA certificate\n"));
+ TLSCHECK(gnutls_x509_crt_init(&cacrt));
+ TLSCHECK(gnutls_x509_crt_set_dn_by_oid(cacrt,
+ GNUTLS_OID_X520_ORGANIZATION_NAME, 0,
+ ORGANISATION_NAME, strlen(ORGANISATION_NAME)));
+ TLSCHECK(gnutls_x509_crt_set_dn_by_oid(cacrt,
+ GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0,
+ UNIT_NAME, strlen(UNIT_NAME)));
+ TLSCHECK(gnutls_x509_crt_set_dn_by_oid(cacrt,
+ GNUTLS_OID_X520_COMMON_NAME, 0,
+ hostname, strlen(hostname)));
+ TLSCHECK(gnutls_x509_crt_set_key(cacrt, cakey));
+ TLSCHECK(gnutls_x509_crt_set_serial(cacrt, &serial, sizeof(serial)));
+ TLSCHECK(gnutls_x509_crt_set_activation_time(cacrt, activation));
+ TLSCHECK(gnutls_x509_crt_set_expiration_time(cacrt, expiry));
+ TLSCHECK(gnutls_x509_crt_set_ca_status(cacrt, 0));
+#ifdef GNUTLS_KP_TLS_WWW_SERVER
+ TLSCHECK(gnutls_x509_crt_set_key_purpose_oid(cacrt, GNUTLS_KP_TLS_WWW_SERVER, 0));
+#endif
+ TLSCHECK(gnutls_x509_crt_set_version(cacrt, 3));
+ TLSCHECK(gnutls_x509_crt_get_key_id(cacrt, 0, keyid, &keyidsize));
+#if HAVE_GNUTLS_X509_CRT_SET_SUBJECT_KEY_ID
+ TLSCHECK(gnutls_x509_crt_set_subject_key_id(cacrt, keyid, keyidsize));
+#endif
+ TLSCHECK(gnutls_x509_crt_sign(cacrt, cacrt, cakey));
+
+ DEBUG(3,("Generating TLS certificate\n"));
+ TLSCHECK(gnutls_x509_crt_init(&crt));
+ TLSCHECK(gnutls_x509_crt_set_dn_by_oid(crt,
+ GNUTLS_OID_X520_ORGANIZATION_NAME, 0,
+ ORGANISATION_NAME, strlen(ORGANISATION_NAME)));
+ TLSCHECK(gnutls_x509_crt_set_dn_by_oid(crt,
+ GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0,
+ UNIT_NAME, strlen(UNIT_NAME)));
+ TLSCHECK(gnutls_x509_crt_set_dn_by_oid(crt,
+ GNUTLS_OID_X520_COMMON_NAME, 0,
+ hostname, strlen(hostname)));
+ TLSCHECK(gnutls_x509_crt_set_key(crt, key));
+ TLSCHECK(gnutls_x509_crt_set_serial(crt, &serial, sizeof(serial)));
+ TLSCHECK(gnutls_x509_crt_set_activation_time(crt, activation));
+ TLSCHECK(gnutls_x509_crt_set_expiration_time(crt, expiry));
+ TLSCHECK(gnutls_x509_crt_set_ca_status(crt, 0));
+#ifdef GNUTLS_KP_TLS_WWW_SERVER
+ TLSCHECK(gnutls_x509_crt_set_key_purpose_oid(crt, GNUTLS_KP_TLS_WWW_SERVER, 0));
+#endif
+ TLSCHECK(gnutls_x509_crt_set_version(crt, 3));
+ TLSCHECK(gnutls_x509_crt_get_key_id(crt, 0, keyid, &keyidsize));
+#if HAVE_GNUTLS_X509_CRT_SET_SUBJECT_KEY_ID
+ TLSCHECK(gnutls_x509_crt_set_subject_key_id(crt, keyid, keyidsize));
+#endif
+ TLSCHECK(gnutls_x509_crt_sign(crt, crt, key));
+
+ DEBUG(3,("Exporting TLS keys\n"));
+
+ bufsize = sizeof(buf);
+ TLSCHECK(gnutls_x509_crt_export(crt, GNUTLS_X509_FMT_PEM, buf, &bufsize));
+ file_save(certfile, buf, bufsize);
+
+ bufsize = sizeof(buf);
+ TLSCHECK(gnutls_x509_crt_export(cacrt, GNUTLS_X509_FMT_PEM, buf, &bufsize));
+ file_save(cafile, buf, bufsize);
+
+ bufsize = sizeof(buf);
+ TLSCHECK(gnutls_x509_privkey_export(key, GNUTLS_X509_FMT_PEM, buf, &bufsize));
+ file_save(keyfile, buf, bufsize);
+
+ gnutls_x509_privkey_deinit(key);
+ gnutls_x509_privkey_deinit(cakey);
+ gnutls_x509_crt_deinit(cacrt);
+ gnutls_x509_crt_deinit(crt);
+ gnutls_global_deinit();
+
+ DEBUG(0,("TLS self-signed keys generated OK\n"));
+ return;
+
+failed:
+ DEBUG(0,("TLS certificate generation failed\n"));
+}
+
+#else
+void tls_cert_dummy(void) {}
+#endif
diff --git a/source4/lib/wmi/config.mk b/source4/lib/wmi/config.mk
new file mode 100644
index 0000000000..3bb1690c7b
--- /dev/null
+++ b/source4/lib/wmi/config.mk
@@ -0,0 +1,69 @@
+[SUBSYSTEM::WMI]
+PUBLIC_DEPENDENCIES = RPC_NDR_OXIDRESOLVER \
+ NDR_DCOM \
+ RPC_NDR_REMACT \
+ NDR_TABLE \
+ DCOM_PROXY_DCOM \
+ DCOM
+
+WMI_OBJ_FILES = $(addprefix $(wmisrcdir)/, wmicore.o wbemdata.o ../../librpc/gen_ndr/dcom_p.o)
+
+#################################
+# Start BINARY wmic
+[BINARY::wmic]
+INSTALLDIR = BINDIR
+PRIVATE_DEPENDENCIES = \
+ POPT_SAMBA \
+ POPT_CREDENTIALS \
+ LIBPOPT \
+ WMI
+
+wmic_OBJ_FILES = $(wmisrcdir)/tools/wmic.o
+# End BINARY wmic
+#################################
+
+#################################
+# Start BINARY wmis
+[BINARY::wmis]
+INSTALLDIR = BINDIR
+PRIVATE_DEPENDENCIES = \
+ POPT_SAMBA \
+ POPT_CREDENTIALS \
+ LIBPOPT \
+ WMI
+
+wmis_OBJ_FILES = \
+ $(wmisrcdir)/tools/wmis.o
+
+# End BINARY wmis
+#################################
+
+librpc/gen_ndr/dcom_p.c: idl
+
+#######################
+# Start LIBRARY swig_dcerpc
+[PYTHON::pywmi]
+PUBLIC_DEPENDENCIES = LIBCLI_SMB LIBNDR LIBSAMBA-UTIL LIBSAMBA-CONFIG WMI
+
+$(eval $(call python_py_module_template,wmi.py,$(wmisrcdir)/wmi.py))
+
+pywmi_OBJ_FILES = $(wmisrcdir)/wmi_wrap.o
+$(pywmi_OBJ_FILES): CFLAGS+=$(CFLAG_NO_UNUSED_MACROS) $(CFLAG_NO_CAST_QUAL)
+
+# End LIBRARY swig_dcerpc
+#######################
+
+#################################
+# Start BINARY pdhc
+#[BINARY::pdhc]
+#INSTALLDIR = BINDIR
+#OBJ_FILES = \
+# pdhc.o
+#PRIVATE_DEPENDENCIES = \
+# POPT_SAMBA \
+# POPT_CREDENTIALS \
+# LIBPOPT \
+# NDR_TABLE \
+# RPC_NDR_WINREG
+# End BINARY pdhc
+#################################
diff --git a/source4/lib/wmi/tools/wmic.c b/source4/lib/wmi/tools/wmic.c
new file mode 100644
index 0000000000..bbfe5ed334
--- /dev/null
+++ b/source4/lib/wmi/tools/wmic.c
@@ -0,0 +1,221 @@
+/*
+ WMI Sample client
+ Copyright (C) 2006 Andrzej Hajda <andrzej.hajda@wp.pl>
+ Copyright (C) 2008 Jelmer Vernooij <jelmer@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU 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 "lib/cmdline/popt_common.h"
+#include "librpc/rpc/dcerpc.h"
+#include "librpc/gen_ndr/ndr_oxidresolver.h"
+#include "librpc/gen_ndr/ndr_oxidresolver_c.h"
+#include "librpc/gen_ndr/ndr_dcom.h"
+#include "librpc/gen_ndr/ndr_dcom_c.h"
+#include "librpc/gen_ndr/ndr_remact_c.h"
+#include "librpc/gen_ndr/ndr_epmapper_c.h"
+#include "librpc/gen_ndr/com_dcom.h"
+
+#include "lib/com/dcom/dcom.h"
+#include "lib/com/com.h"
+
+#include "lib/wmi/wmi.h"
+
+struct program_args {
+ char *hostname;
+ char *query;
+ char *ns;
+};
+
+static void parse_args(int argc, char *argv[], struct program_args *pmyargs)
+{
+ poptContext pc;
+ int opt, i;
+
+ int argc_new;
+ char **argv_new;
+
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ POPT_COMMON_SAMBA
+ POPT_COMMON_CONNECTION
+ POPT_COMMON_CREDENTIALS
+ POPT_COMMON_VERSION
+ {"namespace", 0, POPT_ARG_STRING, &pmyargs->ns, 0,
+ "WMI namespace, default to root\\cimv2", 0},
+ POPT_TABLEEND
+ };
+
+ pc = poptGetContext("wmi", argc, (const char **) argv,
+ long_options, POPT_CONTEXT_KEEP_FIRST);
+
+ poptSetOtherOptionHelp(pc, "//host query\n\nExample: wmic -U [domain/]adminuser%password //host \"select * from Win32_ComputerSystem\"");
+
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ poptPrintUsage(pc, stdout, 0);
+ poptFreeContext(pc);
+ exit(1);
+ }
+
+ argv_new = discard_const_p(char *, poptGetArgs(pc));
+
+ argc_new = argc;
+ for (i = 0; i < argc; i++) {
+ if (argv_new[i] == NULL) {
+ argc_new = i;
+ break;
+ }
+ }
+
+ if (argc_new != 3 || argv_new[1][0] != '/'
+ || argv_new[1][1] != '/') {
+ poptPrintUsage(pc, stdout, 0);
+ poptFreeContext(pc);
+ exit(1);
+ }
+
+ pmyargs->hostname = argv_new[1] + 2;
+ pmyargs->query = argv_new[2];
+ poptFreeContext(pc);
+}
+
+#define WERR_CHECK(msg) if (!W_ERROR_IS_OK(result)) { \
+ DEBUG(0, ("ERROR: %s\n", msg)); \
+ goto error; \
+ } else { \
+ DEBUG(1, ("OK : %s\n", msg)); \
+ }
+
+#define RETURN_CVAR_ARRAY_STR(fmt, arr) {\
+ uint32_t i;\
+ char *r;\
+\
+ if (!arr) {\
+ return talloc_strdup(mem_ctx, "NULL");\
+ }\
+ r = talloc_strdup(mem_ctx, "(");\
+ for (i = 0; i < arr->count; ++i) {\
+ r = talloc_asprintf_append(r, fmt "%s", arr->item[i], (i+1 == arr->count)?"":",");\
+ }\
+ return talloc_asprintf_append(r, ")");\
+}
+
+char *string_CIMVAR(TALLOC_CTX *mem_ctx, union CIMVAR *v, enum CIMTYPE_ENUMERATION cimtype)
+{
+ switch (cimtype) {
+ case CIM_SINT8: return talloc_asprintf(mem_ctx, "%d", v->v_sint8);
+ case CIM_UINT8: return talloc_asprintf(mem_ctx, "%u", v->v_uint8);
+ case CIM_SINT16: return talloc_asprintf(mem_ctx, "%d", v->v_sint16);
+ case CIM_UINT16: return talloc_asprintf(mem_ctx, "%u", v->v_uint16);
+ case CIM_SINT32: return talloc_asprintf(mem_ctx, "%d", v->v_sint32);
+ case CIM_UINT32: return talloc_asprintf(mem_ctx, "%u", v->v_uint32);
+ case CIM_SINT64: return talloc_asprintf(mem_ctx, "%lld", v->v_sint64);
+ case CIM_UINT64: return talloc_asprintf(mem_ctx, "%llu", v->v_sint64);
+ case CIM_REAL32: return talloc_asprintf(mem_ctx, "%f", (double)v->v_uint32);
+ case CIM_REAL64: return talloc_asprintf(mem_ctx, "%f", (double)v->v_uint64);
+ case CIM_BOOLEAN: return talloc_asprintf(mem_ctx, "%s", v->v_boolean?"True":"False");
+ case CIM_STRING:
+ case CIM_DATETIME:
+ case CIM_REFERENCE: return talloc_asprintf(mem_ctx, "%s", v->v_string);
+ case CIM_CHAR16: return talloc_asprintf(mem_ctx, "Unsupported");
+ case CIM_OBJECT: return talloc_asprintf(mem_ctx, "Unsupported");
+ case CIM_ARR_SINT8: RETURN_CVAR_ARRAY_STR("%d", v->a_sint8);
+ case CIM_ARR_UINT8: RETURN_CVAR_ARRAY_STR("%u", v->a_uint8);
+ case CIM_ARR_SINT16: RETURN_CVAR_ARRAY_STR("%d", v->a_sint16);
+ case CIM_ARR_UINT16: RETURN_CVAR_ARRAY_STR("%u", v->a_uint16);
+ case CIM_ARR_SINT32: RETURN_CVAR_ARRAY_STR("%d", v->a_sint32);
+ case CIM_ARR_UINT32: RETURN_CVAR_ARRAY_STR("%u", v->a_uint32);
+ case CIM_ARR_SINT64: RETURN_CVAR_ARRAY_STR("%lld", v->a_sint64);
+ case CIM_ARR_UINT64: RETURN_CVAR_ARRAY_STR("%llu", v->a_uint64);
+ case CIM_ARR_REAL32: RETURN_CVAR_ARRAY_STR("%f", v->a_real32);
+ case CIM_ARR_REAL64: RETURN_CVAR_ARRAY_STR("%f", v->a_real64);
+ case CIM_ARR_BOOLEAN: RETURN_CVAR_ARRAY_STR("%d", v->a_boolean);
+ case CIM_ARR_STRING: RETURN_CVAR_ARRAY_STR("%s", v->a_string);
+ case CIM_ARR_DATETIME: RETURN_CVAR_ARRAY_STR("%s", v->a_datetime);
+ case CIM_ARR_REFERENCE: RETURN_CVAR_ARRAY_STR("%s", v->a_reference);
+ default: return talloc_asprintf(mem_ctx, "Unsupported");
+ }
+}
+
+#undef RETURN_CVAR_ARRAY_STR
+
+int main(int argc, char **argv)
+{
+ struct program_args args = {};
+ uint32_t cnt = 5, ret;
+ char *class_name = NULL;
+ WERROR result;
+ NTSTATUS status;
+ struct IWbemServices *pWS = NULL;
+ struct BSTR queryLanguage, query;
+ struct IEnumWbemClassObject *pEnum = NULL;
+ struct com_context *ctx = NULL;
+
+ parse_args(argc, argv, &args);
+
+ wmi_init(&ctx, cmdline_credentials);
+
+ if (!args.ns)
+ args.ns = "root\\cimv2";
+ result = WBEM_ConnectServer(ctx, args.hostname, args.ns, 0, 0, 0, 0, 0, 0, &pWS);
+ WERR_CHECK("Login to remote object.");
+
+ queryLanguage.data = "WQL";
+ query.data = args.query;
+ result = IWbemServices_ExecQuery(pWS, ctx, queryLanguage, query, WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_ENSURE_LOCATABLE, NULL, &pEnum);
+ WERR_CHECK("WMI query execute.");
+
+ IEnumWbemClassObject_Reset(pEnum, ctx);
+ WERR_CHECK("Reset result of WMI query.");
+
+ do {
+ uint32_t i, j;
+ struct WbemClassObject *co[cnt];
+
+ result = IEnumWbemClassObject_SmartNext(pEnum, ctx, 0xFFFFFFFF, cnt, co, &ret);
+ /* WERR_BADFUNC is OK, it means only that there is less returned objects than requested */
+ if (!W_ERROR_EQUAL(result, WERR_BADFUNC)) {
+ WERR_CHECK("Retrieve result data.");
+ } else {
+ DEBUG(1, ("OK : Retrieved less objects than requested (it is normal).\n"));
+ }
+ if (!ret) break;
+
+ for (i = 0; i < ret; ++i) {
+ if (!class_name || strcmp(co[i]->obj_class->__CLASS, class_name)) {
+ if (class_name) talloc_free(class_name);
+ class_name = talloc_strdup(ctx, co[i]->obj_class->__CLASS);
+ printf("CLASS: %s\n", class_name);
+ for (j = 0; j < co[i]->obj_class->__PROPERTY_COUNT; ++j)
+ printf("%s%s", j?"|":"", co[i]->obj_class->properties[j].property.name);
+ printf("\n");
+ }
+ for (j = 0; j < co[i]->obj_class->__PROPERTY_COUNT; ++j) {
+ char *s;
+ s = string_CIMVAR(ctx, &co[i]->instance->data[j], co[i]->obj_class->properties[j].property.desc->cimtype & CIM_TYPEMASK);
+ printf("%s%s", j?"|":"", s);
+ }
+ printf("\n");
+ }
+ } while (ret == cnt);
+ talloc_free(ctx);
+ return 0;
+error:
+ status = werror_to_ntstatus(result);
+ fprintf(stderr, "NTSTATUS: %s - %s\n", nt_errstr(status), get_friendly_nt_error_msg(status));
+ talloc_free(ctx);
+ return 1;
+}
diff --git a/source4/lib/wmi/tools/wmis.c b/source4/lib/wmi/tools/wmis.c
new file mode 100644
index 0000000000..314fdd1cd7
--- /dev/null
+++ b/source4/lib/wmi/tools/wmis.c
@@ -0,0 +1,221 @@
+/*
+ WMI Sample client
+ Copyright (C) 2006 Andrzej Hajda <andrzej.hajda@wp.pl>
+ Copyright (C) 2008 Jelmer Vernooij <jelmer@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU 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 "lib/cmdline/popt_common.h"
+#include "auth/credentials/credentials.h"
+#include "librpc/rpc/dcerpc.h"
+#include "librpc/gen_ndr/ndr_oxidresolver.h"
+#include "librpc/gen_ndr/ndr_oxidresolver_c.h"
+#include "librpc/gen_ndr/dcom.h"
+#include "librpc/gen_ndr/ndr_dcom.h"
+#include "librpc/gen_ndr/ndr_dcom_c.h"
+#include "librpc/gen_ndr/ndr_remact_c.h"
+#include "librpc/gen_ndr/ndr_epmapper_c.h"
+#include "librpc/gen_ndr/com_dcom.h"
+
+#include "lib/com/dcom/dcom.h"
+#include "librpc/gen_ndr/com_wmi.h"
+#include "librpc/ndr/ndr_table.h"
+
+#include "lib/wmi/wmi.h"
+
+struct program_args {
+ char *hostname;
+ char *query;
+};
+
+static void parse_args(int argc, char *argv[], struct program_args *pmyargs)
+{
+ poptContext pc;
+ int opt, i;
+
+ int argc_new;
+ char **argv_new;
+
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ POPT_COMMON_SAMBA
+ POPT_COMMON_CONNECTION
+ POPT_COMMON_CREDENTIALS
+ POPT_COMMON_VERSION
+ POPT_TABLEEND
+ };
+
+ pc = poptGetContext("wmi", argc, (const char **) argv,
+ long_options, POPT_CONTEXT_KEEP_FIRST);
+
+ poptSetOtherOptionHelp(pc, "//host\n\nExample: wmis -U [domain/]adminuser%password //host");
+
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ poptPrintUsage(pc, stdout, 0);
+ poptFreeContext(pc);
+ exit(1);
+ }
+
+ argv_new = discard_const_p(char *, poptGetArgs(pc));
+
+ argc_new = argc;
+ for (i = 0; i < argc; i++) {
+ if (argv_new[i] == NULL) {
+ argc_new = i;
+ break;
+ }
+ }
+
+ if (argc_new < 2 || argv_new[1][0] != '/'
+ || argv_new[1][1] != '/') {
+ poptPrintUsage(pc, stdout, 0);
+ poptFreeContext(pc);
+ exit(1);
+ }
+
+ pmyargs->hostname = argv_new[1] + 2;
+ poptFreeContext(pc);
+}
+
+#define WERR_CHECK(msg) if (!W_ERROR_IS_OK(result)) { \
+ DEBUG(0, ("ERROR: %s\n", msg)); \
+ goto error; \
+ } else { \
+ DEBUG(1, ("OK : %s\n", msg)); \
+ }
+/*
+WERROR WBEM_ConnectServer(struct com_context *ctx, const char *server, const char *nspace, const char *user, const char *password, const char *locale, uint32_t flags, const char *authority, struct IWbemContext* wbem_ctx, struct IWbemServices** services)
+{
+ struct GUID clsid;
+ struct GUID iid;
+ WERROR result, coresult;
+ struct IUnknown **mqi;
+ struct IWbemLevel1Login *pL;
+
+ if (user) {
+ char *cred;
+ struct cli_credentials *cc;
+
+ cred = talloc_asprintf(NULL, "%s%%%s", user, password);
+ cc = cli_credentials_init(ctx);
+ cli_credentials_set_conf(cc);
+ cli_credentials_parse_string(cc, cred, CRED_SPECIFIED);
+ dcom_set_server_credentials(ctx, server, cc);
+ talloc_free(cred);
+ }
+
+ GUID_from_string(CLSID_WBEMLEVEL1LOGIN, &clsid);
+ GUID_from_string(COM_IWBEMLEVEL1LOGIN_UUID, &iid);
+ result = dcom_create_object(ctx, &clsid, server, 1, &iid, &mqi, &coresult);
+ WERR_CHECK("dcom_create_object.");
+ result = coresult;
+ WERR_CHECK("Create remote WMI object.");
+ pL = (struct IWbemLevel1Login *)mqi[0];
+ talloc_free(mqi);
+
+ result = IWbemLevel1Login_NTLMLogin(pL, ctx, nspace, locale, flags, wbem_ctx, services);
+ WERR_CHECK("Login to remote object.");
+error:
+ return result;
+}
+*/
+WERROR WBEM_RemoteExecute(struct IWbemServices *pWS, const char *cmdline, uint32_t *ret_code)
+{
+ struct IWbemClassObject *wco = NULL;
+ struct IWbemClassObject *inc, *outc, *in;
+ struct IWbemClassObject *out = NULL;
+ WERROR result;
+ union CIMVAR v;
+ TALLOC_CTX *ctx;
+ struct BSTR objectPath, methodName;
+
+ ctx = talloc_new(0);
+
+ objectPath.data = "Win32_Process";
+
+ result = IWbemServices_GetObject(pWS, ctx, objectPath,
+ WBEM_FLAG_RETURN_WBEM_COMPLETE, NULL, &wco, NULL);
+ WERR_CHECK("GetObject.");
+
+ result = IWbemClassObject_GetMethod(wco, ctx, "Create", 0, &inc, &outc);
+ WERR_CHECK("IWbemClassObject_GetMethod.");
+
+ result = IWbemClassObject_SpawnInstance(inc, ctx, 0, &in);
+ WERR_CHECK("IWbemClassObject_SpawnInstance.");
+
+ v.v_string = cmdline;
+ result = IWbemClassObject_Put(in, ctx, "CommandLine", 0, &v, 0);
+ WERR_CHECK("IWbemClassObject_Put(CommandLine).");
+
+ methodName.data = "Create";
+ result = IWbemServices_ExecMethod(pWS, ctx, objectPath, methodName, 0, NULL, in, &out,
+ NULL);
+ WERR_CHECK("IWbemServices_ExecMethod.");
+
+ if (ret_code) {
+ result = WbemClassObject_Get(out->object_data, ctx, "ReturnValue", 0, &v, 0, 0);
+ WERR_CHECK("IWbemClassObject_Put(CommandLine).");
+ *ret_code = v.v_uint32;
+ }
+error:
+ talloc_free(ctx);
+ return result;
+}
+
+int main(int argc, char **argv)
+{
+ struct program_args args = {};
+ struct com_context *ctx = NULL;
+ WERROR result;
+ NTSTATUS status;
+ struct IWbemServices *pWS = NULL;
+ struct IEnumWbemClassObject *pEnum = NULL;
+ uint32_t cnt;
+ struct BSTR queryLanguage;
+ struct BSTR query;
+
+ parse_args(argc, argv, &args);
+
+ wmi_init(&ctx, cmdline_credentials);
+ result = WBEM_ConnectServer(ctx, args.hostname, "root\\cimv2", 0, 0, 0, 0, 0, 0, &pWS);
+ WERR_CHECK("WBEM_ConnectServer.");
+
+ printf("1: Creating directory C:\\wmi_test_dir_tmp using method Win32_Process.Create\n");
+ WBEM_RemoteExecute(pWS, "cmd.exe /C mkdir C:\\wmi_test_dir_tmp", &cnt);
+ WERR_CHECK("WBEM_RemoteExecute.");
+ printf("2: ReturnCode: %d\n", cnt);
+
+ printf("3: Monitoring directory C:\\wmi_test_dir_tmp. Please create/delete files in that directory to see notifications, after 4 events program quits.\n");
+ query.data = "SELECT * FROM __InstanceOperationEvent WITHIN 1 WHERE Targetinstance ISA 'CIM_DirectoryContainsFile' and TargetInstance.GroupComponent= 'Win32_Directory.Name=\"C:\\\\\\\\wmi_test_dir_tmp\"'";
+ queryLanguage.data = "WQL";
+ result = IWbemServices_ExecNotificationQuery(pWS, ctx, queryLanguage,
+ query, WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD_ONLY, NULL, &pEnum);
+ WERR_CHECK("WMI query execute.");
+ for (cnt = 0; cnt < 4; ++cnt) {
+ struct WbemClassObject *co;
+ uint32_t ret;
+ result = IEnumWbemClassObject_SmartNext(pEnum, ctx, 0xFFFFFFFF, 1, &co, &ret);
+ WERR_CHECK("IEnumWbemClassObject_Next.");
+ printf("%s\n", co->obj_class->__CLASS);
+ }
+
+error:
+ status = werror_to_ntstatus(result);
+ fprintf(stderr, "NTSTATUS: %s - %s\n", nt_errstr(status), get_friendly_nt_error_msg(status));
+ talloc_free(ctx);
+ return 1;
+}
diff --git a/source4/lib/wmi/wbemdata.c b/source4/lib/wmi/wbemdata.c
new file mode 100644
index 0000000000..57e4022e7c
--- /dev/null
+++ b/source4/lib/wmi/wbemdata.c
@@ -0,0 +1,451 @@
+/*
+ WMI Implementation
+ Copyright (C) 2006 Andrzej Hajda <andrzej.hajda@wp.pl>
+ Copyright (C) 2008 Jelmer Vernooij <jelmer@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU 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 "librpc/gen_ndr/dcom.h"
+#include "librpc/gen_ndr/com_dcom.h"
+#include "librpc/ndr/libndr.h"
+#include "librpc/ndr/libndr_proto.h"
+#include "lib/com/com.h"
+#include "lib/com/dcom/dcom.h"
+#include "lib/util/dlinklist.h"
+#include "librpc/ndr/libndr.h"
+#include "librpc/gen_ndr/ndr_dcom.h"
+#include "librpc/rpc/dcerpc.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "lib/talloc/talloc.h"
+#include "libcli/composite/composite.h"
+#include "lib/wmi/wmi.h"
+#include "librpc/gen_ndr/ndr_wmi.h"
+
+enum {
+ DATATYPE_CLASSOBJECT = 2,
+ DATATYPE_OBJECT = 3,
+ COFLAG_IS_CLASS = 4,
+};
+
+static enum ndr_err_code marshal(TALLOC_CTX *mem_ctx, struct IUnknown *pv, struct OBJREF *o)
+{
+ struct ndr_push *ndr;
+ struct IWbemClassObject *wco;
+ struct MInterfacePointer *mp;
+
+ mp = (struct MInterfacePointer *)((char *)o - offsetof(struct MInterfacePointer, obj)); /* FIXME:high remove this Mumbo Jumbo */
+ wco = pv->object_data;
+ ndr = talloc_zero(mem_ctx, struct ndr_push);
+ ndr->flags = 0;
+ ndr->alloc_size = 1024;
+ ndr->data = talloc_array(mp, uint8_t, ndr->alloc_size);
+
+ if (wco) {
+ uint32_t ofs;
+ NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, 0x12345678));
+ NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, 0));
+ NDR_CHECK(ndr_push_IWbemClassObject(ndr, NDR_SCALARS | NDR_BUFFERS, wco));
+ ofs = ndr->offset;
+ ndr->offset = 4;
+ NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, ofs - 8));
+ ndr->offset = ofs;
+ } else {
+ NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, 0));
+ }
+ o->u_objref.u_custom.pData = talloc_realloc(mp, ndr->data, uint8_t, ndr->offset);
+ o->u_objref.u_custom.size = ndr->offset;
+ mp->size = sizeof(struct OBJREF) - sizeof(union OBJREF_Types) + sizeof(struct u_custom) + o->u_objref.u_custom.size - 4;
+ if (DEBUGLVL(9)) {
+ NDR_PRINT_DEBUG(IWbemClassObject, wco);
+ }
+ return NDR_ERR_SUCCESS;
+}
+
+static enum ndr_err_code unmarshal(TALLOC_CTX *mem_ctx, struct OBJREF *o, struct IUnknown **pv)
+{
+ struct ndr_pull *ndr;
+ struct IWbemClassObject *wco;
+ enum ndr_err_code ndr_err;
+ uint32_t u;
+
+ mem_ctx = talloc_new(0);
+ ndr = talloc_zero(mem_ctx, struct ndr_pull);
+ ndr->current_mem_ctx = mem_ctx;
+ ndr->data = o->u_objref.u_custom.pData;
+ ndr->data_size = o->u_objref.u_custom.size;
+
+ NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &u));
+ if (!u) {
+ talloc_free(*pv);
+ *pv = NULL;
+ return NDR_ERR_SUCCESS;
+ }
+ NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &u));
+ if (u + 8 > ndr->data_size) {
+ DEBUG(1, ("unmarshall_IWbemClassObject: Incorrect data_size"));
+ return NDR_ERR_BUFSIZE;
+ }
+ wco = talloc_zero(*pv, struct IWbemClassObject);
+ ndr->current_mem_ctx = wco;
+ ndr_err = ndr_pull_IWbemClassObject(ndr, NDR_SCALARS | NDR_BUFFERS, wco);
+
+ if (NDR_ERR_CODE_IS_SUCCESS(ndr_err) && (DEBUGLVL(9))) {
+ NDR_PRINT_DEBUG(IWbemClassObject, wco);
+ }
+
+ if (NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ (*pv)->object_data = wco;
+ } else {
+ talloc_free(wco);
+ }
+ return NDR_ERR_SUCCESS;
+}
+
+WERROR dcom_IWbemClassObject_from_WbemClassObject(struct com_context *ctx, struct IWbemClassObject **_p, struct IWbemClassObject *wco)
+{
+ struct IWbemClassObject *p;
+
+ p = talloc_zero(ctx, struct IWbemClassObject);
+ p->ctx = ctx;
+ p->obj.signature = 0x574f454d;
+ p->obj.flags = OBJREF_CUSTOM;
+ GUID_from_string("dc12a681-737f-11cf-884d-00aa004b2e24", &p->obj.iid);
+ GUID_from_string("4590f812-1d3a-11d0-891f-00aa004b2e24", &p->obj.u_objref.u_custom.clsid);
+ p->object_data = (void *)wco;
+ talloc_steal(p, p->object_data);
+ *_p = p;
+ return WERR_OK;
+}
+
+WERROR IWbemClassObject_GetMethod(struct IWbemClassObject *d, TALLOC_CTX *mem_ctx, const char *name, uint32_t flags, struct IWbemClassObject **in, struct IWbemClassObject **out)
+{
+ uint32_t i;
+ struct IWbemClassObject *wco;
+
+ wco = (struct IWbemClassObject *)d->object_data;
+ for (i = 0; i < wco->obj_methods->count; ++i)
+ if (!strcmp(wco->obj_methods->method[i].name, name)) {
+ if (in) dcom_IWbemClassObject_from_WbemClassObject(d->ctx, in, wco->obj_methods->method[i].in);
+ if (out) dcom_IWbemClassObject_from_WbemClassObject(d->ctx, out, wco->obj_methods->method[i].out);
+ return WERR_OK;
+ }
+ return WERR_NOT_FOUND;
+}
+
+void IWbemClassObject_CreateInstance(struct IWbemClassObject *wco)
+{
+ uint32_t i;
+
+ wco->instance = talloc_zero(wco, struct WbemInstance);
+ wco->instance->default_flags = talloc_array(wco->instance, uint8_t, wco->obj_class->__PROPERTY_COUNT);
+ wco->instance->data = talloc_array(wco->instance, union CIMVAR, wco->obj_class->__PROPERTY_COUNT);
+ memset(wco->instance->data, 0, sizeof(union CIMVAR) * wco->obj_class->__PROPERTY_COUNT);
+ for (i = 0; i < wco->obj_class->__PROPERTY_COUNT; ++i) {
+ wco->instance->default_flags[i] = 1; /* FIXME:high resolve this magic */
+ }
+ wco->instance->__CLASS = wco->obj_class->__CLASS;
+ wco->instance->u2_4 = 4;
+ wco->instance->u3_1 = 1;
+}
+
+WERROR IWbemClassObject_Clone(struct IWbemClassObject *d, TALLOC_CTX *mem_ctx, struct IWbemClassObject **copy)
+{
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR IWbemClassObject_SpawnInstance(struct IWbemClassObject *d, TALLOC_CTX *mem_ctx, uint32_t flags, struct IWbemClassObject **instance)
+{
+ struct IWbemClassObject *wco, *nwco;
+
+ wco = (struct IWbemClassObject *)d->object_data;
+ nwco = talloc_zero(mem_ctx, struct IWbemClassObject);
+ nwco->flags = WCF_INSTANCE;
+ nwco->obj_class = wco->obj_class;
+ IWbemClassObject_CreateInstance(nwco);
+ dcom_IWbemClassObject_from_WbemClassObject(d->ctx, instance, nwco);
+ return WERR_OK;
+}
+
+WERROR IWbemClassObject_Get(struct IWbemClassObject *d, TALLOC_CTX *mem_ctx, const char *name, uint32_t flags, union CIMVAR *val, enum CIMTYPE_ENUMERATION *cimtype, uint32_t *flavor)
+{
+ uint32_t i;
+ for (i = 0; i < d->obj_class->__PROPERTY_COUNT; ++i) {
+ if (!strcmp(d->obj_class->properties[i].property.name, name)) {
+ duplicate_CIMVAR(mem_ctx, &d->instance->data[i], val, d->obj_class->properties[i].property.desc->cimtype);
+ if (cimtype != NULL)
+ *cimtype = d->obj_class->properties[i].property.desc->cimtype;
+ if (flavor != NULL)
+ *flavor = 0; /* FIXME:avg implement flavor */
+ return WERR_OK;
+ }
+ }
+ return WERR_NOT_FOUND;
+}
+
+WERROR IWbemClassObject_Put(struct IWbemClassObject *d, TALLOC_CTX *mem_ctx, const char *name, uint32_t flags, union CIMVAR *val, enum CIMTYPE_ENUMERATION cimtype)
+{
+ struct IWbemClassObject *wco;
+ uint32_t i;
+
+ wco = (struct IWbemClassObject *)d->object_data;
+ for (i = 0; i < wco->obj_class->__PROPERTY_COUNT; ++i) {
+ if (!strcmp(wco->obj_class->properties[i].property.name, name)) {
+ if (cimtype && cimtype != wco->obj_class->properties[i].property.desc->cimtype) return WERR_INVALID_PARAM;
+ wco->instance->default_flags[i] = 0;
+ duplicate_CIMVAR(wco->instance, val, &wco->instance->data[i], wco->obj_class->properties[i].property.desc->cimtype);
+ return WERR_OK;
+ }
+ }
+ return WERR_NOT_FOUND;
+}
+
+#define WERR_CHECK(msg) if (!W_ERROR_IS_OK(result)) { \
+ DEBUG(1, ("ERROR: %s - %s\n", msg, wmi_errstr(result))); \
+ return result; \
+ } else { \
+ DEBUG(1, ("OK : %s\n", msg)); \
+ }
+
+struct pair_guid_ptr {
+ struct GUID guid;
+ void *ptr;
+ struct pair_guid_ptr *next, *prev;
+};
+
+static void *get_ptr_by_guid(struct pair_guid_ptr *list, struct GUID *uuid)
+{
+ for (; list; list = list->next) {
+ if (GUID_equal(&list->guid, uuid))
+ return list->ptr;
+ }
+ return NULL;
+}
+
+static void add_pair_guid_ptr(TALLOC_CTX *mem_ctx, struct pair_guid_ptr **list, struct GUID *uuid, void *ptr)
+{
+ struct pair_guid_ptr *e;
+
+ e = talloc(mem_ctx, struct pair_guid_ptr);
+ e->guid = *uuid;
+ e->ptr = ptr;
+ talloc_steal(e, ptr);
+ DLIST_ADD(*list, e);
+}
+
+struct IEnumWbemClassObject_data {
+ struct GUID guid;
+ struct IWbemFetchSmartEnum *pFSE;
+ struct IWbemWCOSmartEnum *pSE;
+ struct pair_guid_ptr *cache;
+};
+#define NDR_CHECK_EXPR(expr) do { if (!(expr)) {\
+ DEBUG(0, ("%s(%d): WBEMDATA_ERR(0x%08X): Error parsing(%s)\n", __FILE__, __LINE__, ndr->offset, #expr)); \
+ return NDR_ERR_VALIDATE; \
+ } \
+ } while(0)
+
+#define NDR_CHECK_CONST(val, exp) NDR_CHECK_EXPR((val) == (exp))
+
+
+static enum ndr_err_code WBEMDATA_Parse(TALLOC_CTX *mem_ctx, uint8_t *data, uint32_t size, struct IEnumWbemClassObject *d, uint32_t uCount, struct IWbemClassObject **apObjects)
+{
+ struct ndr_pull *ndr;
+ uint32_t u, i, ofs_next;
+ uint8_t u8, datatype;
+ struct GUID guid;
+ struct IEnumWbemClassObject_data *ecod;
+
+ if (!uCount)
+ return NDR_ERR_BAD_SWITCH;
+
+ ecod = d->object_data;
+
+ ndr = talloc_zero(mem_ctx, struct ndr_pull);
+ ndr->current_mem_ctx = d->ctx;
+ ndr->data = data;
+ ndr->data_size = size;
+ ndr_set_flags(&ndr->flags, LIBNDR_FLAG_NOALIGN);
+
+ NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &u));
+ NDR_CHECK_CONST(u, 0x0);
+ NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &u));
+ NDR_CHECK_CONST(u, *(const uint32_t *)"WBEM");
+ NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &u));
+ NDR_CHECK_CONST(u, *(const uint32_t *)"DATA");
+ NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &u));
+ NDR_CHECK_CONST(u, 0x1A); /* Length of header */
+ NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &u));
+ NDR_PULL_NEED_BYTES(ndr, u + 6);
+ NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &u));
+ NDR_CHECK_CONST(u, 0x0);
+ NDR_CHECK(ndr_pull_uint8(ndr, NDR_SCALARS, &u8));
+ NDR_CHECK_CONST(u8, 0x01); /* Major Version */
+ NDR_CHECK(ndr_pull_uint8(ndr, NDR_SCALARS, &u8));
+ NDR_CHECK_EXPR(u8 <= 1); /* Minor Version 0 - Win2000, 1 - XP/2003 */
+ NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &u));
+ NDR_CHECK_CONST(u, 0x8); /* Length of header */
+ NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &u));
+ NDR_PULL_NEED_BYTES(ndr, u);
+ NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &u));
+ NDR_CHECK_CONST(u, 0xC); /* Length of header */
+ NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &u));
+ NDR_PULL_NEED_BYTES(ndr, u + 4);
+ NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &u));
+ NDR_CHECK_CONST(u, uCount);
+ for (i = 0; i < uCount; ++i) {
+ NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &u));
+ NDR_CHECK_CONST(u, 0x9); /* Length of header */
+ NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &u));
+ NDR_PULL_NEED_BYTES(ndr, u + 1);
+ NDR_CHECK(ndr_pull_uint8(ndr, NDR_SCALARS, &datatype));
+ ofs_next = ndr->offset + u;
+ NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &u));
+ NDR_CHECK_CONST(u, 0x18); /* Length of header */
+ NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &u));
+ NDR_PULL_NEED_BYTES(ndr, u + 16);
+ NDR_CHECK(ndr_pull_GUID(ndr, NDR_SCALARS, &guid));
+ switch (datatype) {
+ case DATATYPE_CLASSOBJECT:
+ apObjects[i] = talloc_zero(d->ctx, struct IWbemClassObject);
+ ndr->current_mem_ctx = apObjects[i];
+ NDR_CHECK(ndr_pull_WbemClassObject(ndr, NDR_SCALARS|NDR_BUFFERS, apObjects[i]));
+ ndr->current_mem_ctx = d->ctx;
+ add_pair_guid_ptr(ecod, &ecod->cache, &guid, apObjects[i]->obj_class);
+ break;
+ case DATATYPE_OBJECT:
+ apObjects[i] = talloc_zero(d->ctx, struct IWbemClassObject);
+ apObjects[i]->obj_class = get_ptr_by_guid(ecod->cache, &guid);
+ (void)talloc_reference(apObjects[i], apObjects[i]->obj_class);
+ ndr->current_mem_ctx = apObjects[i];
+ NDR_CHECK(ndr_pull_WbemClassObject_Object(ndr, NDR_SCALARS|NDR_BUFFERS, apObjects[i]));
+ ndr->current_mem_ctx = d->ctx;
+ break;
+ default:
+ DEBUG(0, ("WBEMDATA_Parse: Data type %d not supported\n", datatype));
+ return NDR_ERR_BAD_SWITCH;
+ }
+ ndr->offset = ofs_next;
+ if (DEBUGLVL(9)) {
+ NDR_PRINT_DEBUG(IWbemClassObject, apObjects[i]);
+ }
+ }
+ return NDR_ERR_SUCCESS;
+}
+
+WERROR IEnumWbemClassObject_SmartNext(struct IEnumWbemClassObject *d, TALLOC_CTX *mem_ctx, int32_t lTimeout, uint32_t uCount, struct IWbemClassObject **apObjects, uint32_t *puReturned)
+{
+ WERROR result;
+ NTSTATUS status;
+ struct IEnumWbemClassObject_data *ecod;
+ TALLOC_CTX *loc_ctx;
+ uint32_t size;
+ uint8_t *data;
+
+ loc_ctx = talloc_new(0);
+ ecod = d->object_data;
+ if (!ecod) {
+ struct GUID iid;
+ WERROR coresult;
+
+ d->object_data = ecod = talloc_zero(d, struct IEnumWbemClassObject_data);
+ GUID_from_string(COM_IWBEMFETCHSMARTENUM_UUID, &iid);
+ result = dcom_query_interface((struct IUnknown *)d, 5, 1, &iid, (struct IUnknown **)&ecod->pFSE, &coresult);
+ WERR_CHECK("dcom_query_interface.");
+ result = coresult;
+ WERR_CHECK("Retrieve enumerator of result(IWbemFetchSmartEnum).");
+
+ result = IWbemFetchSmartEnum_Fetch(ecod->pFSE, mem_ctx, &ecod->pSE);
+ WERR_CHECK("Retrieve enumerator of result(IWbemWCOSmartEnum).");
+
+ ecod->guid = GUID_random();
+ d->vtable->Release_send = dcom_proxy_IEnumWbemClassObject_Release_send;
+ }
+
+ result = IWbemWCOSmartEnum_Next(ecod->pSE, loc_ctx, &ecod->guid, lTimeout, uCount, puReturned, &size, &data);
+ if (!W_ERROR_EQUAL(result, WERR_BADFUNC)) {
+ WERR_CHECK("IWbemWCOSmartEnum_Next.");
+ }
+
+ if (data) {
+ NDR_CHECK(WBEMDATA_Parse(mem_ctx, data, size, d, *puReturned, apObjects));
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ status = werror_to_ntstatus(result);
+ DEBUG(9, ("dcom_proxy_IEnumWbemClassObject_Next: %s - %s\n", nt_errstr(status), get_friendly_nt_error_msg(status)));
+ }
+ talloc_free(loc_ctx);
+ return result;
+}
+
+struct composite_context *dcom_proxy_IEnumWbemClassObject_Release_send(struct IUnknown *d, TALLOC_CTX *mem_ctx)
+{
+ struct composite_context *c, *cr;
+ struct REMINTERFACEREF iref[3];
+ struct dcom_object_exporter *ox;
+ struct IEnumWbemClassObject_data *ecod;
+ int n;
+
+ c = composite_create(d->ctx, d->ctx->event_ctx);
+ if (c == NULL) return NULL;
+ c->private_data = d;
+
+ ox = object_exporter_by_ip(d->ctx, d);
+ iref[0].ipid = IUnknown_ipid(d);
+ iref[0].cPublicRefs = 5;
+ iref[0].cPrivateRefs = 0;
+ n = 1;
+
+ ecod = d->object_data;
+ if (ecod) {
+ if (ecod->pFSE) {
+ talloc_steal(d, ecod->pFSE);
+ iref[n].ipid = IUnknown_ipid(ecod->pFSE);
+ iref[n].cPublicRefs = 5;
+ iref[n].cPrivateRefs = 0;
+ ++n;
+ }
+ if (ecod->pSE) {
+ talloc_steal(d, ecod->pSE);
+ iref[n].ipid = IUnknown_ipid(ecod->pSE);
+ iref[n].cPublicRefs = 5;
+ iref[n].cPrivateRefs = 0;
+ ++n;
+ }
+ }
+ cr = IRemUnknown_RemRelease_send(ox->rem_unknown, mem_ctx, n, iref);
+
+ composite_continue(c, cr, dcom_release_continue, c);
+ return c;
+}
+
+NTSTATUS dcom_proxy_IWbemClassObject_init(void)
+{
+ struct GUID clsid;
+ GUID_from_string("4590f812-1d3a-11d0-891f-00aa004b2e24", &clsid);
+ dcom_register_marshal(&clsid, marshal, unmarshal);
+
+#if 0
+ struct IEnumWbemClassObject_vtable *proxy_vtable;
+ proxy_vtable = (struct IEnumWbemClassObject_vtable *)dcom_proxy_vtable_by_iid((struct GUID *)&dcerpc_table_IEnumWbemClassObject.syntax_id.uuid);
+ if (proxy_vtable)
+ proxy_vtable->Release_send = dcom_proxy_IEnumWbemClassObject_Release_send;
+ else
+ DEBUG(0, ("WARNING: IEnumWbemClassObject should be initialized before IWbemClassObject."));
+#endif
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/lib/wmi/wmi.h b/source4/lib/wmi/wmi.h
new file mode 100644
index 0000000000..46c9b70cdb
--- /dev/null
+++ b/source4/lib/wmi/wmi.h
@@ -0,0 +1,48 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Jelmer Vernooij <jelmer@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _WMI_H_
+#define _WMI_H_
+
+#include "librpc/gen_ndr/com_wmi.h"
+
+/* The following definitions come from lib/wmi/wmicore.c */
+
+
+/** FIXME: Use credentials struct rather than user/password here */
+WERROR WBEM_ConnectServer(struct com_context *ctx, const char *server, const char *nspace,
+ struct cli_credentials *credentials,
+ const char *locale, uint32_t flags, const char *authority,
+ struct IWbemContext* wbem_ctx, struct IWbemServices** services);
+const char *wmi_errstr(WERROR werror);
+
+/* The following definitions come from lib/wmi/wbemdata.c */
+
+WERROR IWbemClassObject_GetMethod(struct IWbemClassObject *d, TALLOC_CTX *mem_ctx, const char *name, uint32_t flags, struct IWbemClassObject **in, struct IWbemClassObject **out);
+void WbemClassObject_CreateInstance(struct IWbemClassObject *wco);
+WERROR IWbemClassObject_Clone(struct IWbemClassObject *d, TALLOC_CTX *mem_ctx, struct IWbemClassObject **copy);
+WERROR IWbemClassObject_SpawnInstance(struct IWbemClassObject *d, TALLOC_CTX *mem_ctx, uint32_t flags, struct IWbemClassObject **instance);
+WERROR IWbemClassObject_Get(struct IWbemClassObject *d, TALLOC_CTX *mem_ctx, const char *name, uint32_t flags, union CIMVAR *val, enum CIMTYPE_ENUMERATION *cimtype, uint32_t *flavor);
+WERROR IWbemClassObject_Put(struct IWbemClassObject *d, TALLOC_CTX *mem_ctx, const char *name, uint32_t flags, union CIMVAR *val, enum CIMTYPE_ENUMERATION cimtype);
+WERROR IEnumWbemClassObject_SmartNext(struct IEnumWbemClassObject *d, TALLOC_CTX *mem_ctx, int32_t lTimeout, uint32_t uCount, struct IWbemClassObject **apObjects, uint32_t *puReturned);
+struct composite_context *dcom_proxy_IEnumWbemClassObject_Release_send(struct IUnknown *d, TALLOC_CTX *mem_ctx);
+
+void wmi_init(struct com_context **ctx, struct cli_credentials *credentials);
+
+#endif
diff --git a/source4/lib/wmi/wmi.i b/source4/lib/wmi/wmi.i
new file mode 100644
index 0000000000..2c8bbe517e
--- /dev/null
+++ b/source4/lib/wmi/wmi.i
@@ -0,0 +1,352 @@
+/*
+ WMI Implementation
+ Copyright (C) 2006 Andrzej Hajda <andrzej.hajda@wp.pl>
+ Copyright (C) 2008 Jelmer Vernooij <jelmer@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU 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.
+*/
+
+%module wmi
+
+%include "typemaps.i"
+%include "libcli/util/errors.i"
+%import "stdint.i"
+%import "lib/talloc/talloc.i"
+
+%runtime %{
+void push_object(PyObject **stack, PyObject *o)
+{
+ if ((!*stack) || (*stack == Py_None)) {
+ *stack = o;
+ } else {
+ PyObject *o2, *o3;
+ if (!PyTuple_Check(*stack)) {
+ o2 = *stack;
+ *stack = PyTuple_New(1);
+ PyTuple_SetItem(*stack,0,o2);
+ }
+ o3 = PyTuple_New(1);
+ PyTuple_SetItem(o3,0,o);
+ o2 = *stack;
+ *stack = PySequence_Concat(o2,o3);
+ Py_DECREF(o2);
+ Py_DECREF(o3);
+ }
+}
+%}
+
+%{
+#include "includes.h"
+#include "librpc/gen_ndr/misc.h"
+#include "librpc/rpc/dcerpc.h"
+#include "lib/com/dcom/dcom.h"
+#include "librpc/gen_ndr/com_dcom.h"
+#include "lib/wmi/wmi.h"
+
+
+WERROR WBEM_ConnectServer(struct com_context *ctx, const char *server, const char *nspace, const char *user, const char *password,
+ const char *locale, uint32_t flags, const char *authority, struct IWbemContext* wbem_ctx, struct IWbemServices** services);
+WERROR IEnumWbemClassObject_SmartNext(struct IEnumWbemClassObject *d, TALLOC_CTX *mem_ctx, int32_t lTimeout,uint32_t uCount,
+ struct WbemClassObject **apObjects, uint32_t *puReturned);
+
+static PyObject *PyObject_FromCVAR(uint32_t cimtype, union CIMVAR *cvar);
+static PyObject *PySWbemObject_FromWbemClassObject(struct WbemClassObject *wco);
+
+static struct com_context *com_ctx;
+static PyObject *ComError;
+static PyObject *mod_win32_client;
+static PyObject *mod_pywintypes;
+
+typedef struct IUnknown IUnknown;
+typedef struct IWbemServices IWbemServices;
+typedef struct IWbemClassObject IWbemClassObject;
+typedef struct IEnumWbemClassObject IEnumWbemClassObject;
+%}
+
+%wrapper %{
+
+#define RETURN_CVAR_ARRAY(fmt, arr) {\
+ PyObject *l, *o;\
+ uint32_t i;\
+\
+ if (!arr) {\
+ Py_INCREF(Py_None);\
+ return Py_None;\
+ }\
+ l = PyList_New(arr->count);\
+ if (!l) return NULL;\
+ for (i = 0; i < arr->count; ++i) {\
+ o = _Py_BuildValue(fmt, arr->item[i]);\
+ if (!o) {\
+ Py_DECREF(l);\
+ return NULL;\
+ }\
+ PyList_SET_ITEM(l, i, o);\
+ }\
+ return l;\
+}
+
+static PyObject *_Py_BuildValue(char *str, ...)
+{
+ PyObject * result = NULL;
+ va_list lst;
+ va_start(lst, str);
+ if (str && *str == 'I') {
+ uint32_t value = va_arg(lst, uint32_t);
+ if (value & 0x80000000) {
+ result = Py_BuildValue("L", (long)value);
+ } else {
+ result = Py_BuildValue("i", value);
+ }
+ } else {
+ result = Py_VaBuildValue(str, lst);
+ }
+ va_end(lst);
+ return result;
+}
+
+
+static PyObject *PyObject_FromCVAR(uint32_t cimtype, union CIMVAR *cvar)
+{
+ switch (cimtype) {
+ case CIM_SINT8: return Py_BuildValue("b", cvar->v_sint8);
+ case CIM_UINT8: return Py_BuildValue("B", cvar->v_uint8);
+ case CIM_SINT16: return Py_BuildValue("h", cvar->v_sint16);
+ case CIM_UINT16: return Py_BuildValue("H", cvar->v_uint16);
+ case CIM_SINT32: return Py_BuildValue("i", cvar->v_sint32);
+ case CIM_UINT32: return _Py_BuildValue("I", cvar->v_uint32);
+ case CIM_SINT64: return Py_BuildValue("L", cvar->v_sint64);
+ case CIM_UINT64: return Py_BuildValue("K", cvar->v_uint64);
+ case CIM_REAL32: return Py_BuildValue("f", cvar->v_real32);
+ case CIM_REAL64: return Py_BuildValue("d", cvar->v_real64);
+ case CIM_BOOLEAN: return Py_BuildValue("h", cvar->v_boolean);
+ case CIM_STRING: return Py_BuildValue("s", cvar->v_string);
+ case CIM_DATETIME: return Py_BuildValue("s", cvar->v_datetime);
+ case CIM_REFERENCE: return Py_BuildValue("s", cvar->v_reference);
+ case CIM_OBJECT: return PySWbemObject_FromWbemClassObject(cvar->v_object);
+ case CIM_ARR_SINT8: RETURN_CVAR_ARRAY("b", cvar->a_sint8);
+ case CIM_ARR_UINT8: RETURN_CVAR_ARRAY("B", cvar->a_uint8);
+ case CIM_ARR_SINT16: RETURN_CVAR_ARRAY("h", cvar->a_sint16);
+ case CIM_ARR_UINT16: RETURN_CVAR_ARRAY("H", cvar->a_uint16);
+ case CIM_ARR_SINT32: RETURN_CVAR_ARRAY("i", cvar->a_sint32);
+ case CIM_ARR_UINT32: RETURN_CVAR_ARRAY("I", cvar->a_uint32);
+ case CIM_ARR_SINT64: RETURN_CVAR_ARRAY("L", cvar->a_sint64);
+ case CIM_ARR_UINT64: RETURN_CVAR_ARRAY("K", cvar->a_uint64);
+ case CIM_ARR_REAL32: RETURN_CVAR_ARRAY("f", cvar->a_real32);
+ case CIM_ARR_REAL64: RETURN_CVAR_ARRAY("d", cvar->a_real64);
+ case CIM_ARR_BOOLEAN: RETURN_CVAR_ARRAY("h", cvar->a_boolean);
+ case CIM_ARR_STRING: RETURN_CVAR_ARRAY("s", cvar->a_string);
+ case CIM_ARR_DATETIME: RETURN_CVAR_ARRAY("s", cvar->a_datetime);
+ case CIM_ARR_REFERENCE: RETURN_CVAR_ARRAY("s", cvar->a_reference);
+ default:
+ {
+ char *str;
+ str = talloc_asprintf(NULL, "Unsupported CIMTYPE(0x%04X)", cimtype);
+ PyErr_SetString(PyExc_RuntimeError, str);
+ talloc_free(str);
+ return NULL;
+ }
+ }
+}
+
+#undef RETURN_CVAR_ARRAY
+
+PyObject *PySWbemObject_InitProperites(PyObject *o, struct WbemClassObject *wco)
+{
+ PyObject *properties;
+ PyObject *addProp;
+ uint32_t i;
+ int32_t r;
+ PyObject *result;
+
+ result = NULL;
+ properties = PyObject_GetAttrString(o, "Properties_");
+ if (!properties) return NULL;
+ addProp = PyObject_GetAttrString(properties, "Add");
+ if (!addProp) {
+ Py_DECREF(properties);
+ return NULL;
+ }
+
+ for (i = 0; i < wco->obj_class->__PROPERTY_COUNT; ++i) {
+ PyObject *args, *property;
+
+ args = Py_BuildValue("(si)", wco->obj_class->properties[i].property.name, wco->obj_class->properties[i].property.desc->cimtype & CIM_TYPEMASK);
+ if (!args) goto finish;
+ property = PyObject_CallObject(addProp, args);
+ Py_DECREF(args);
+ if (!property) goto finish;
+ if (wco->flags & WCF_INSTANCE) {
+ PyObject *value;
+
+ if (wco->instance->default_flags[i] & 1) {
+ value = Py_None;
+ Py_INCREF(Py_None);
+ } else
+ value = PyObject_FromCVAR(wco->obj_class->properties[i].property.desc->cimtype & CIM_TYPEMASK, &wco->instance->data[i]);
+ if (!value) {
+ Py_DECREF(property);
+ goto finish;
+ }
+ r = PyObject_SetAttrString(property, "Value", value);
+ Py_DECREF(value);
+ if (r == -1) {
+ PyErr_SetString(PyExc_RuntimeError, "Error setting value of property");
+ goto finish;
+ }
+ }
+ Py_DECREF(property);
+ }
+
+ Py_INCREF(Py_None);
+ result = Py_None;
+finish:
+ Py_DECREF(addProp);
+ Py_DECREF(properties);
+ return result;
+}
+
+static PyObject *PySWbemObject_FromWbemClassObject(struct WbemClassObject *wco)
+{
+ PyObject *swo_class, *swo, *args, *result;
+
+ swo_class = PyObject_GetAttrString(mod_win32_client, "SWbemObject");
+ if (!swo_class) return NULL;
+ args = PyTuple_New(0);
+ if (!args) {
+ Py_DECREF(swo_class);
+ return NULL;
+ }
+ swo = PyObject_CallObject(swo_class, args);
+ Py_DECREF(args);
+ Py_DECREF(swo_class);
+ if (!swo) return NULL;
+
+ result = PySWbemObject_InitProperites(swo, wco);
+ if (!result) {
+ Py_DECREF(swo);
+ return NULL;
+ }
+ Py_DECREF(result);
+
+ return swo;
+}
+
+%}
+
+%typemap(in, numinputs=0) struct com_context *ctx {
+ $1 = com_ctx;
+}
+
+%typemap(in, numinputs=0) struct IWbemServices **services (struct IWbemServices *temp) {
+ $1 = &temp;
+}
+
+%typemap(argout) struct IWbemServices **services {
+ PyObject *o;
+ o = SWIG_NewPointerObj(*$1, SWIGTYPE_p_IWbemServices, 0);
+ push_object(&$result, o);
+}
+
+WERROR WBEM_ConnectServer(struct com_context *ctx, const char *server, const char *nspace, const char *user, const char *password,
+ const char *locale, uint32_t flags, const char *authority, struct IWbemContext* wbem_ctx, struct IWbemServices** services);
+
+%typemap(in, numinputs=0) struct IEnumWbemClassObject **ppEnum (struct IEnumWbemClassObject *temp) {
+ $1 = &temp;
+}
+
+%typemap(argout) struct IEnumWbemClassObject **ppEnum {
+ PyObject *o;
+ o = SWIG_NewPointerObj(*$1, SWIGTYPE_p_IEnumWbemClassObject, 0);
+ push_object(&$result, o);
+}
+
+typedef struct IUnknown {
+ %extend {
+ uint32_t Release(TALLOC_CTX *mem_ctx);
+ }
+} IUnknown;
+
+%typemap(in) struct BSTR {
+ $1.data = PyString_AsString($input);
+}
+
+
+typedef struct IWbemServices {
+ %extend {
+ WERROR ExecQuery(TALLOC_CTX *mem_ctx, struct BSTR strQueryLanguage, struct BSTR strQuery, int32_t lFlags, struct IWbemContext *pCtx, struct IEnumWbemClassObject **ppEnum);
+ WERROR ExecNotificationQuery(TALLOC_CTX *mem_ctx, struct BSTR strQueryLanguage, struct BSTR strQuery, int32_t lFlags, struct IWbemContext *pCtx, struct IEnumWbemClassObject **ppEnum);
+ WERROR CreateInstanceEnum(TALLOC_CTX *mem_ctx, struct BSTR strClass,
+ int32_t lFlags, struct IWbemContext *pCtx, struct IEnumWbemClassObject **ppEnum);
+ }
+} IWbemServices;
+
+typedef struct IEnumWbemClassObject {
+ %extend {
+ WERROR Reset(TALLOC_CTX *mem_ctx);
+ }
+} IEnumWbemClassObject;
+
+%typemap(in, numinputs=1) (uint32_t uCount, struct WbemClassObject **apObjects, uint32_t *puReturned) (uint32_t uReturned) {
+ if (PyLong_Check($input))
+ $1 = PyLong_AsUnsignedLong($input);
+ else if (PyInt_Check($input))
+ $1 = PyInt_AsLong($input);
+ else {
+ PyErr_SetString(PyExc_TypeError,"Expected a long or an int");
+ return NULL;
+ }
+ $2 = talloc_array(NULL, struct WbemClassObject *, $1);
+ $3 = &uReturned;
+}
+
+%typemap(argout) (struct WbemClassObject **apObjects, uint32_t *puReturned) {
+ uint32_t i;
+ PyObject *o;
+ int32_t error;
+
+ error = 0;
+
+ $result = PyTuple_New(*$2);
+ for (i = 0; i < *$2; ++i) {
+ if (!error) {
+ o = PySWbemObject_FromWbemClassObject($1[i]);
+ if (!o)
+ --error;
+ else
+ error = PyTuple_SetItem($result, i, o);
+ }
+ talloc_free($1[i]);
+ }
+ talloc_free($1);
+ if (error) return NULL;
+}
+
+WERROR IEnumWbemClassObject_SmartNext(struct IEnumWbemClassObject *d, TALLOC_CTX *mem_ctx, int32_t lTimeout, uint32_t uCount,
+ struct WbemClassObject **apObjects, uint32_t *puReturned);
+
+%init %{
+
+ mod_win32_client = PyImport_ImportModule("win32com.client");
+ mod_pywintypes = PyImport_ImportModule("pywintypes");
+ ComError = PyObject_GetAttrString(mod_pywintypes, "com_error");
+
+ wmi_init(&com_ctx, NULL);
+ {
+ PyObject *pModule;
+
+ pModule = PyImport_ImportModule( "win32com.client" );
+ }
+%}
diff --git a/source4/lib/wmi/wmi.py b/source4/lib/wmi/wmi.py
new file mode 100644
index 0000000000..4bc59cfe41
--- /dev/null
+++ b/source4/lib/wmi/wmi.py
@@ -0,0 +1,95 @@
+# This file was automatically generated by SWIG (http://www.swig.org).
+# Version 1.3.36
+#
+# Don't modify this file, modify the SWIG interface instead.
+
+import _wmi
+import new
+new_instancemethod = new.instancemethod
+try:
+ _swig_property = property
+except NameError:
+ pass # Python < 2.2 doesn't have 'property'.
+def _swig_setattr_nondynamic(self,class_type,name,value,static=1):
+ if (name == "thisown"): return self.this.own(value)
+ if (name == "this"):
+ if type(value).__name__ == 'PySwigObject':
+ self.__dict__[name] = value
+ return
+ method = class_type.__swig_setmethods__.get(name,None)
+ if method: return method(self,value)
+ if (not static) or hasattr(self,name):
+ self.__dict__[name] = value
+ else:
+ raise AttributeError("You cannot add attributes to %s" % self)
+
+def _swig_setattr(self,class_type,name,value):
+ return _swig_setattr_nondynamic(self,class_type,name,value,0)
+
+def _swig_getattr(self,class_type,name):
+ if (name == "thisown"): return self.this.own()
+ method = class_type.__swig_getmethods__.get(name,None)
+ if method: return method(self)
+ raise AttributeError,name
+
+def _swig_repr(self):
+ try: strthis = "proxy of " + self.this.__repr__()
+ except: strthis = ""
+ return "<%s.%s; %s >" % (self.__class__.__module__, self.__class__.__name__, strthis,)
+
+import types
+try:
+ _object = types.ObjectType
+ _newclass = 1
+except AttributeError:
+ class _object : pass
+ _newclass = 0
+del types
+
+
+def _swig_setattr_nondynamic_method(set):
+ def set_attr(self,name,value):
+ if (name == "thisown"): return self.this.own(value)
+ if hasattr(self,name) or (name == "this"):
+ set(self,name,value)
+ else:
+ raise AttributeError("You cannot add attributes to %s" % self)
+ return set_attr
+
+
+WBEM_ConnectServer = _wmi.WBEM_ConnectServer
+class IUnknown(object):
+ thisown = _swig_property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc='The membership flag')
+ __repr__ = _swig_repr
+ def __init__(self, *args, **kwargs):
+ _wmi.IUnknown_swiginit(self,_wmi.new_IUnknown(*args, **kwargs))
+ __swig_destroy__ = _wmi.delete_IUnknown
+IUnknown.Release = new_instancemethod(_wmi.IUnknown_Release,None,IUnknown)
+IUnknown_swigregister = _wmi.IUnknown_swigregister
+IUnknown_swigregister(IUnknown)
+
+class IWbemServices(object):
+ thisown = _swig_property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc='The membership flag')
+ __repr__ = _swig_repr
+ def __init__(self, *args, **kwargs):
+ _wmi.IWbemServices_swiginit(self,_wmi.new_IWbemServices(*args, **kwargs))
+ __swig_destroy__ = _wmi.delete_IWbemServices
+IWbemServices.ExecQuery = new_instancemethod(_wmi.IWbemServices_ExecQuery,None,IWbemServices)
+IWbemServices.ExecNotificationQuery = new_instancemethod(_wmi.IWbemServices_ExecNotificationQuery,None,IWbemServices)
+IWbemServices.CreateInstanceEnum = new_instancemethod(_wmi.IWbemServices_CreateInstanceEnum,None,IWbemServices)
+IWbemServices_swigregister = _wmi.IWbemServices_swigregister
+IWbemServices_swigregister(IWbemServices)
+
+class IEnumWbemClassObject(object):
+ thisown = _swig_property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc='The membership flag')
+ __repr__ = _swig_repr
+ def __init__(self, *args, **kwargs):
+ _wmi.IEnumWbemClassObject_swiginit(self,_wmi.new_IEnumWbemClassObject(*args, **kwargs))
+ __swig_destroy__ = _wmi.delete_IEnumWbemClassObject
+IEnumWbemClassObject.Reset = new_instancemethod(_wmi.IEnumWbemClassObject_Reset,None,IEnumWbemClassObject)
+IEnumWbemClassObject_swigregister = _wmi.IEnumWbemClassObject_swigregister
+IEnumWbemClassObject_swigregister(IEnumWbemClassObject)
+
+IEnumWbemClassObject_SmartNext = _wmi.IEnumWbemClassObject_SmartNext
+
+
diff --git a/source4/lib/wmi/wmi_wrap.c b/source4/lib/wmi/wmi_wrap.c
new file mode 100644
index 0000000000..ee5a8fcb0f
--- /dev/null
+++ b/source4/lib/wmi/wmi_wrap.c
@@ -0,0 +1,4310 @@
+/* ----------------------------------------------------------------------------
+ * This file was automatically generated by SWIG (http://www.swig.org).
+ * Version 1.3.36
+ *
+ * This file is not intended to be easily readable and contains a number of
+ * coding conventions designed to improve portability and efficiency. Do not make
+ * changes to this file unless you know what you are doing--modify the SWIG
+ * interface file instead.
+ * ----------------------------------------------------------------------------- */
+
+#define SWIGPYTHON
+#define SWIG_PYTHON_NO_BUILD_NONE
+/* -----------------------------------------------------------------------------
+ * This section contains generic SWIG labels for method/variable
+ * declarations/attributes, and other compiler dependent labels.
+ * ----------------------------------------------------------------------------- */
+
+/* template workaround for compilers that cannot correctly implement the C++ standard */
+#ifndef SWIGTEMPLATEDISAMBIGUATOR
+# if defined(__SUNPRO_CC) && (__SUNPRO_CC <= 0x560)
+# define SWIGTEMPLATEDISAMBIGUATOR template
+# elif defined(__HP_aCC)
+/* Needed even with `aCC -AA' when `aCC -V' reports HP ANSI C++ B3910B A.03.55 */
+/* If we find a maximum version that requires this, the test would be __HP_aCC <= 35500 for A.03.55 */
+# define SWIGTEMPLATEDISAMBIGUATOR template
+# else
+# define SWIGTEMPLATEDISAMBIGUATOR
+# endif
+#endif
+
+/* inline attribute */
+#ifndef SWIGINLINE
+# if defined(__cplusplus) || (defined(__GNUC__) && !defined(__STRICT_ANSI__))
+# define SWIGINLINE inline
+# else
+# define SWIGINLINE
+# endif
+#endif
+
+/* attribute recognised by some compilers to avoid 'unused' warnings */
+#ifndef SWIGUNUSED
+# if defined(__GNUC__)
+# if !(defined(__cplusplus)) || (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4))
+# define SWIGUNUSED __attribute__ ((__unused__))
+# else
+# define SWIGUNUSED
+# endif
+# elif defined(__ICC)
+# define SWIGUNUSED __attribute__ ((__unused__))
+# else
+# define SWIGUNUSED
+# endif
+#endif
+
+#ifndef SWIG_MSC_UNSUPPRESS_4505
+# if defined(_MSC_VER)
+# pragma warning(disable : 4505) /* unreferenced local function has been removed */
+# endif
+#endif
+
+#ifndef SWIGUNUSEDPARM
+# ifdef __cplusplus
+# define SWIGUNUSEDPARM(p)
+# else
+# define SWIGUNUSEDPARM(p) p SWIGUNUSED
+# endif
+#endif
+
+/* internal SWIG method */
+#ifndef SWIGINTERN
+# define SWIGINTERN static SWIGUNUSED
+#endif
+
+/* internal inline SWIG method */
+#ifndef SWIGINTERNINLINE
+# define SWIGINTERNINLINE SWIGINTERN SWIGINLINE
+#endif
+
+/* exporting methods */
+#if (__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+# ifndef GCC_HASCLASSVISIBILITY
+# define GCC_HASCLASSVISIBILITY
+# endif
+#endif
+
+#ifndef SWIGEXPORT
+# if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__)
+# if defined(STATIC_LINKED)
+# define SWIGEXPORT
+# else
+# define SWIGEXPORT __declspec(dllexport)
+# endif
+# else
+# if defined(__GNUC__) && defined(GCC_HASCLASSVISIBILITY)
+# define SWIGEXPORT __attribute__ ((visibility("default")))
+# else
+# define SWIGEXPORT
+# endif
+# endif
+#endif
+
+/* calling conventions for Windows */
+#ifndef SWIGSTDCALL
+# if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__)
+# define SWIGSTDCALL __stdcall
+# else
+# define SWIGSTDCALL
+# endif
+#endif
+
+/* Deal with Microsoft's attempt at deprecating C standard runtime functions */
+#if !defined(SWIG_NO_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_CRT_SECURE_NO_DEPRECATE)
+# define _CRT_SECURE_NO_DEPRECATE
+#endif
+
+/* Deal with Microsoft's attempt at deprecating methods in the standard C++ library */
+#if !defined(SWIG_NO_SCL_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_SCL_SECURE_NO_DEPRECATE)
+# define _SCL_SECURE_NO_DEPRECATE
+#endif
+
+
+
+/* Python.h has to appear first */
+#include <Python.h>
+
+/* -----------------------------------------------------------------------------
+ * swigrun.swg
+ *
+ * This file contains generic CAPI SWIG runtime support for pointer
+ * type checking.
+ * ----------------------------------------------------------------------------- */
+
+/* This should only be incremented when either the layout of swig_type_info changes,
+ or for whatever reason, the runtime changes incompatibly */
+#define SWIG_RUNTIME_VERSION "4"
+
+/* define SWIG_TYPE_TABLE_NAME as "SWIG_TYPE_TABLE" */
+#ifdef SWIG_TYPE_TABLE
+# define SWIG_QUOTE_STRING(x) #x
+# define SWIG_EXPAND_AND_QUOTE_STRING(x) SWIG_QUOTE_STRING(x)
+# define SWIG_TYPE_TABLE_NAME SWIG_EXPAND_AND_QUOTE_STRING(SWIG_TYPE_TABLE)
+#else
+# define SWIG_TYPE_TABLE_NAME
+#endif
+
+/*
+ You can use the SWIGRUNTIME and SWIGRUNTIMEINLINE macros for
+ creating a static or dynamic library from the swig runtime code.
+ In 99.9% of the cases, swig just needs to declare them as 'static'.
+
+ But only do this if is strictly necessary, ie, if you have problems
+ with your compiler or so.
+*/
+
+#ifndef SWIGRUNTIME
+# define SWIGRUNTIME SWIGINTERN
+#endif
+
+#ifndef SWIGRUNTIMEINLINE
+# define SWIGRUNTIMEINLINE SWIGRUNTIME SWIGINLINE
+#endif
+
+/* Generic buffer size */
+#ifndef SWIG_BUFFER_SIZE
+# define SWIG_BUFFER_SIZE 1024
+#endif
+
+/* Flags for pointer conversions */
+#define SWIG_POINTER_DISOWN 0x1
+#define SWIG_CAST_NEW_MEMORY 0x2
+
+/* Flags for new pointer objects */
+#define SWIG_POINTER_OWN 0x1
+
+
+/*
+ Flags/methods for returning states.
+
+ The swig conversion methods, as ConvertPtr, return and integer
+ that tells if the conversion was successful or not. And if not,
+ an error code can be returned (see swigerrors.swg for the codes).
+
+ Use the following macros/flags to set or process the returning
+ states.
+
+ In old swig versions, you usually write code as:
+
+ if (SWIG_ConvertPtr(obj,vptr,ty.flags) != -1) {
+ // success code
+ } else {
+ //fail code
+ }
+
+ Now you can be more explicit as:
+
+ int res = SWIG_ConvertPtr(obj,vptr,ty.flags);
+ if (SWIG_IsOK(res)) {
+ // success code
+ } else {
+ // fail code
+ }
+
+ that seems to be the same, but now you can also do
+
+ Type *ptr;
+ int res = SWIG_ConvertPtr(obj,(void **)(&ptr),ty.flags);
+ if (SWIG_IsOK(res)) {
+ // success code
+ if (SWIG_IsNewObj(res) {
+ ...
+ delete *ptr;
+ } else {
+ ...
+ }
+ } else {
+ // fail code
+ }
+
+ I.e., now SWIG_ConvertPtr can return new objects and you can
+ identify the case and take care of the deallocation. Of course that
+ requires also to SWIG_ConvertPtr to return new result values, as
+
+ int SWIG_ConvertPtr(obj, ptr,...) {
+ if (<obj is ok>) {
+ if (<need new object>) {
+ *ptr = <ptr to new allocated object>;
+ return SWIG_NEWOBJ;
+ } else {
+ *ptr = <ptr to old object>;
+ return SWIG_OLDOBJ;
+ }
+ } else {
+ return SWIG_BADOBJ;
+ }
+ }
+
+ Of course, returning the plain '0(success)/-1(fail)' still works, but you can be
+ more explicit by returning SWIG_BADOBJ, SWIG_ERROR or any of the
+ swig errors code.
+
+ Finally, if the SWIG_CASTRANK_MODE is enabled, the result code
+ allows to return the 'cast rank', for example, if you have this
+
+ int food(double)
+ int fooi(int);
+
+ and you call
+
+ food(1) // cast rank '1' (1 -> 1.0)
+ fooi(1) // cast rank '0'
+
+ just use the SWIG_AddCast()/SWIG_CheckState()
+
+
+ */
+#define SWIG_OK (0)
+#define SWIG_ERROR (-1)
+#define SWIG_IsOK(r) (r >= 0)
+#define SWIG_ArgError(r) ((r != SWIG_ERROR) ? r : SWIG_TypeError)
+
+/* The CastRankLimit says how many bits are used for the cast rank */
+#define SWIG_CASTRANKLIMIT (1 << 8)
+/* The NewMask denotes the object was created (using new/malloc) */
+#define SWIG_NEWOBJMASK (SWIG_CASTRANKLIMIT << 1)
+/* The TmpMask is for in/out typemaps that use temporal objects */
+#define SWIG_TMPOBJMASK (SWIG_NEWOBJMASK << 1)
+/* Simple returning values */
+#define SWIG_BADOBJ (SWIG_ERROR)
+#define SWIG_OLDOBJ (SWIG_OK)
+#define SWIG_NEWOBJ (SWIG_OK | SWIG_NEWOBJMASK)
+#define SWIG_TMPOBJ (SWIG_OK | SWIG_TMPOBJMASK)
+/* Check, add and del mask methods */
+#define SWIG_AddNewMask(r) (SWIG_IsOK(r) ? (r | SWIG_NEWOBJMASK) : r)
+#define SWIG_DelNewMask(r) (SWIG_IsOK(r) ? (r & ~SWIG_NEWOBJMASK) : r)
+#define SWIG_IsNewObj(r) (SWIG_IsOK(r) && (r & SWIG_NEWOBJMASK))
+#define SWIG_AddTmpMask(r) (SWIG_IsOK(r) ? (r | SWIG_TMPOBJMASK) : r)
+#define SWIG_DelTmpMask(r) (SWIG_IsOK(r) ? (r & ~SWIG_TMPOBJMASK) : r)
+#define SWIG_IsTmpObj(r) (SWIG_IsOK(r) && (r & SWIG_TMPOBJMASK))
+
+
+/* Cast-Rank Mode */
+#if defined(SWIG_CASTRANK_MODE)
+# ifndef SWIG_TypeRank
+# define SWIG_TypeRank unsigned long
+# endif
+# ifndef SWIG_MAXCASTRANK /* Default cast allowed */
+# define SWIG_MAXCASTRANK (2)
+# endif
+# define SWIG_CASTRANKMASK ((SWIG_CASTRANKLIMIT) -1)
+# define SWIG_CastRank(r) (r & SWIG_CASTRANKMASK)
+SWIGINTERNINLINE int SWIG_AddCast(int r) {
+ return SWIG_IsOK(r) ? ((SWIG_CastRank(r) < SWIG_MAXCASTRANK) ? (r + 1) : SWIG_ERROR) : r;
+}
+SWIGINTERNINLINE int SWIG_CheckState(int r) {
+ return SWIG_IsOK(r) ? SWIG_CastRank(r) + 1 : 0;
+}
+#else /* no cast-rank mode */
+# define SWIG_AddCast
+# define SWIG_CheckState(r) (SWIG_IsOK(r) ? 1 : 0)
+#endif
+
+
+
+
+#include <string.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef void *(*swig_converter_func)(void *, int *);
+typedef struct swig_type_info *(*swig_dycast_func)(void **);
+
+/* Structure to store information on one type */
+typedef struct swig_type_info {
+ const char *name; /* mangled name of this type */
+ const char *str; /* human readable name of this type */
+ swig_dycast_func dcast; /* dynamic cast function down a hierarchy */
+ struct swig_cast_info *cast; /* linked list of types that can cast into this type */
+ void *clientdata; /* language specific type data */
+ int owndata; /* flag if the structure owns the clientdata */
+} swig_type_info;
+
+/* Structure to store a type and conversion function used for casting */
+typedef struct swig_cast_info {
+ swig_type_info *type; /* pointer to type that is equivalent to this type */
+ swig_converter_func converter; /* function to cast the void pointers */
+ struct swig_cast_info *next; /* pointer to next cast in linked list */
+ struct swig_cast_info *prev; /* pointer to the previous cast */
+} swig_cast_info;
+
+/* Structure used to store module information
+ * Each module generates one structure like this, and the runtime collects
+ * all of these structures and stores them in a circularly linked list.*/
+typedef struct swig_module_info {
+ swig_type_info **types; /* Array of pointers to swig_type_info structures that are in this module */
+ size_t size; /* Number of types in this module */
+ struct swig_module_info *next; /* Pointer to next element in circularly linked list */
+ swig_type_info **type_initial; /* Array of initially generated type structures */
+ swig_cast_info **cast_initial; /* Array of initially generated casting structures */
+ void *clientdata; /* Language specific module data */
+} swig_module_info;
+
+/*
+ Compare two type names skipping the space characters, therefore
+ "char*" == "char *" and "Class<int>" == "Class<int >", etc.
+
+ Return 0 when the two name types are equivalent, as in
+ strncmp, but skipping ' '.
+*/
+SWIGRUNTIME int
+SWIG_TypeNameComp(const char *f1, const char *l1,
+ const char *f2, const char *l2) {
+ for (;(f1 != l1) && (f2 != l2); ++f1, ++f2) {
+ while ((*f1 == ' ') && (f1 != l1)) ++f1;
+ while ((*f2 == ' ') && (f2 != l2)) ++f2;
+ if (*f1 != *f2) return (*f1 > *f2) ? 1 : -1;
+ }
+ return (int)((l1 - f1) - (l2 - f2));
+}
+
+/*
+ Check type equivalence in a name list like <name1>|<name2>|...
+ Return 0 if not equal, 1 if equal
+*/
+SWIGRUNTIME int
+SWIG_TypeEquiv(const char *nb, const char *tb) {
+ int equiv = 0;
+ const char* te = tb + strlen(tb);
+ const char* ne = nb;
+ while (!equiv && *ne) {
+ for (nb = ne; *ne; ++ne) {
+ if (*ne == '|') break;
+ }
+ equiv = (SWIG_TypeNameComp(nb, ne, tb, te) == 0) ? 1 : 0;
+ if (*ne) ++ne;
+ }
+ return equiv;
+}
+
+/*
+ Check type equivalence in a name list like <name1>|<name2>|...
+ Return 0 if equal, -1 if nb < tb, 1 if nb > tb
+*/
+SWIGRUNTIME int
+SWIG_TypeCompare(const char *nb, const char *tb) {
+ int equiv = 0;
+ const char* te = tb + strlen(tb);
+ const char* ne = nb;
+ while (!equiv && *ne) {
+ for (nb = ne; *ne; ++ne) {
+ if (*ne == '|') break;
+ }
+ equiv = (SWIG_TypeNameComp(nb, ne, tb, te) == 0) ? 1 : 0;
+ if (*ne) ++ne;
+ }
+ return equiv;
+}
+
+
+/* think of this as a c++ template<> or a scheme macro */
+#define SWIG_TypeCheck_Template(comparison, ty) \
+ if (ty) { \
+ swig_cast_info *iter = ty->cast; \
+ while (iter) { \
+ if (comparison) { \
+ if (iter == ty->cast) return iter; \
+ /* Move iter to the top of the linked list */ \
+ iter->prev->next = iter->next; \
+ if (iter->next) \
+ iter->next->prev = iter->prev; \
+ iter->next = ty->cast; \
+ iter->prev = 0; \
+ if (ty->cast) ty->cast->prev = iter; \
+ ty->cast = iter; \
+ return iter; \
+ } \
+ iter = iter->next; \
+ } \
+ } \
+ return 0
+
+/*
+ Check the typename
+*/
+SWIGRUNTIME swig_cast_info *
+SWIG_TypeCheck(const char *c, swig_type_info *ty) {
+ SWIG_TypeCheck_Template(strcmp(iter->type->name, c) == 0, ty);
+}
+
+/* Same as previous function, except strcmp is replaced with a pointer comparison */
+SWIGRUNTIME swig_cast_info *
+SWIG_TypeCheckStruct(swig_type_info *from, swig_type_info *into) {
+ SWIG_TypeCheck_Template(iter->type == from, into);
+}
+
+/*
+ Cast a pointer up an inheritance hierarchy
+*/
+SWIGRUNTIMEINLINE void *
+SWIG_TypeCast(swig_cast_info *ty, void *ptr, int *newmemory) {
+ return ((!ty) || (!ty->converter)) ? ptr : (*ty->converter)(ptr, newmemory);
+}
+
+/*
+ Dynamic pointer casting. Down an inheritance hierarchy
+*/
+SWIGRUNTIME swig_type_info *
+SWIG_TypeDynamicCast(swig_type_info *ty, void **ptr) {
+ swig_type_info *lastty = ty;
+ if (!ty || !ty->dcast) return ty;
+ while (ty && (ty->dcast)) {
+ ty = (*ty->dcast)(ptr);
+ if (ty) lastty = ty;
+ }
+ return lastty;
+}
+
+/*
+ Return the name associated with this type
+*/
+SWIGRUNTIMEINLINE const char *
+SWIG_TypeName(const swig_type_info *ty) {
+ return ty->name;
+}
+
+/*
+ Return the pretty name associated with this type,
+ that is an unmangled type name in a form presentable to the user.
+*/
+SWIGRUNTIME const char *
+SWIG_TypePrettyName(const swig_type_info *type) {
+ /* The "str" field contains the equivalent pretty names of the
+ type, separated by vertical-bar characters. We choose
+ to print the last name, as it is often (?) the most
+ specific. */
+ if (!type) return NULL;
+ if (type->str != NULL) {
+ const char *last_name = type->str;
+ const char *s;
+ for (s = type->str; *s; s++)
+ if (*s == '|') last_name = s+1;
+ return last_name;
+ }
+ else
+ return type->name;
+}
+
+/*
+ Set the clientdata field for a type
+*/
+SWIGRUNTIME void
+SWIG_TypeClientData(swig_type_info *ti, void *clientdata) {
+ swig_cast_info *cast = ti->cast;
+ /* if (ti->clientdata == clientdata) return; */
+ ti->clientdata = clientdata;
+
+ while (cast) {
+ if (!cast->converter) {
+ swig_type_info *tc = cast->type;
+ if (!tc->clientdata) {
+ SWIG_TypeClientData(tc, clientdata);
+ }
+ }
+ cast = cast->next;
+ }
+}
+SWIGRUNTIME void
+SWIG_TypeNewClientData(swig_type_info *ti, void *clientdata) {
+ SWIG_TypeClientData(ti, clientdata);
+ ti->owndata = 1;
+}
+
+/*
+ Search for a swig_type_info structure only by mangled name
+ Search is a O(log #types)
+
+ We start searching at module start, and finish searching when start == end.
+ Note: if start == end at the beginning of the function, we go all the way around
+ the circular list.
+*/
+SWIGRUNTIME swig_type_info *
+SWIG_MangledTypeQueryModule(swig_module_info *start,
+ swig_module_info *end,
+ const char *name) {
+ swig_module_info *iter = start;
+ do {
+ if (iter->size) {
+ register size_t l = 0;
+ register size_t r = iter->size - 1;
+ do {
+ /* since l+r >= 0, we can (>> 1) instead (/ 2) */
+ register size_t i = (l + r) >> 1;
+ const char *iname = iter->types[i]->name;
+ if (iname) {
+ register int compare = strcmp(name, iname);
+ if (compare == 0) {
+ return iter->types[i];
+ } else if (compare < 0) {
+ if (i) {
+ r = i - 1;
+ } else {
+ break;
+ }
+ } else if (compare > 0) {
+ l = i + 1;
+ }
+ } else {
+ break; /* should never happen */
+ }
+ } while (l <= r);
+ }
+ iter = iter->next;
+ } while (iter != end);
+ return 0;
+}
+
+/*
+ Search for a swig_type_info structure for either a mangled name or a human readable name.
+ It first searches the mangled names of the types, which is a O(log #types)
+ If a type is not found it then searches the human readable names, which is O(#types).
+
+ We start searching at module start, and finish searching when start == end.
+ Note: if start == end at the beginning of the function, we go all the way around
+ the circular list.
+*/
+SWIGRUNTIME swig_type_info *
+SWIG_TypeQueryModule(swig_module_info *start,
+ swig_module_info *end,
+ const char *name) {
+ /* STEP 1: Search the name field using binary search */
+ swig_type_info *ret = SWIG_MangledTypeQueryModule(start, end, name);
+ if (ret) {
+ return ret;
+ } else {
+ /* STEP 2: If the type hasn't been found, do a complete search
+ of the str field (the human readable name) */
+ swig_module_info *iter = start;
+ do {
+ register size_t i = 0;
+ for (; i < iter->size; ++i) {
+ if (iter->types[i]->str && (SWIG_TypeEquiv(iter->types[i]->str, name)))
+ return iter->types[i];
+ }
+ iter = iter->next;
+ } while (iter != end);
+ }
+
+ /* neither found a match */
+ return 0;
+}
+
+/*
+ Pack binary data into a string
+*/
+SWIGRUNTIME char *
+SWIG_PackData(char *c, void *ptr, size_t sz) {
+ static const char hex[17] = "0123456789abcdef";
+ register const unsigned char *u = (unsigned char *) ptr;
+ register const unsigned char *eu = u + sz;
+ for (; u != eu; ++u) {
+ register unsigned char uu = *u;
+ *(c++) = hex[(uu & 0xf0) >> 4];
+ *(c++) = hex[uu & 0xf];
+ }
+ return c;
+}
+
+/*
+ Unpack binary data from a string
+*/
+SWIGRUNTIME const char *
+SWIG_UnpackData(const char *c, void *ptr, size_t sz) {
+ register unsigned char *u = (unsigned char *) ptr;
+ register const unsigned char *eu = u + sz;
+ for (; u != eu; ++u) {
+ register char d = *(c++);
+ register unsigned char uu;
+ if ((d >= '0') && (d <= '9'))
+ uu = ((d - '0') << 4);
+ else if ((d >= 'a') && (d <= 'f'))
+ uu = ((d - ('a'-10)) << 4);
+ else
+ return (char *) 0;
+ d = *(c++);
+ if ((d >= '0') && (d <= '9'))
+ uu |= (d - '0');
+ else if ((d >= 'a') && (d <= 'f'))
+ uu |= (d - ('a'-10));
+ else
+ return (char *) 0;
+ *u = uu;
+ }
+ return c;
+}
+
+/*
+ Pack 'void *' into a string buffer.
+*/
+SWIGRUNTIME char *
+SWIG_PackVoidPtr(char *buff, void *ptr, const char *name, size_t bsz) {
+ char *r = buff;
+ if ((2*sizeof(void *) + 2) > bsz) return 0;
+ *(r++) = '_';
+ r = SWIG_PackData(r,&ptr,sizeof(void *));
+ if (strlen(name) + 1 > (bsz - (r - buff))) return 0;
+ strcpy(r,name);
+ return buff;
+}
+
+SWIGRUNTIME const char *
+SWIG_UnpackVoidPtr(const char *c, void **ptr, const char *name) {
+ if (*c != '_') {
+ if (strcmp(c,"NULL") == 0) {
+ *ptr = (void *) 0;
+ return name;
+ } else {
+ return 0;
+ }
+ }
+ return SWIG_UnpackData(++c,ptr,sizeof(void *));
+}
+
+SWIGRUNTIME char *
+SWIG_PackDataName(char *buff, void *ptr, size_t sz, const char *name, size_t bsz) {
+ char *r = buff;
+ size_t lname = (name ? strlen(name) : 0);
+ if ((2*sz + 2 + lname) > bsz) return 0;
+ *(r++) = '_';
+ r = SWIG_PackData(r,ptr,sz);
+ if (lname) {
+ strncpy(r,name,lname+1);
+ } else {
+ *r = 0;
+ }
+ return buff;
+}
+
+SWIGRUNTIME const char *
+SWIG_UnpackDataName(const char *c, void *ptr, size_t sz, const char *name) {
+ if (*c != '_') {
+ if (strcmp(c,"NULL") == 0) {
+ memset(ptr,0,sz);
+ return name;
+ } else {
+ return 0;
+ }
+ }
+ return SWIG_UnpackData(++c,ptr,sz);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+/* Errors in SWIG */
+#define SWIG_UnknownError -1
+#define SWIG_IOError -2
+#define SWIG_RuntimeError -3
+#define SWIG_IndexError -4
+#define SWIG_TypeError -5
+#define SWIG_DivisionByZero -6
+#define SWIG_OverflowError -7
+#define SWIG_SyntaxError -8
+#define SWIG_ValueError -9
+#define SWIG_SystemError -10
+#define SWIG_AttributeError -11
+#define SWIG_MemoryError -12
+#define SWIG_NullReferenceError -13
+
+
+
+
+/* Add PyOS_snprintf for old Pythons */
+#if PY_VERSION_HEX < 0x02020000
+# if defined(_MSC_VER) || defined(__BORLANDC__) || defined(_WATCOM)
+# define PyOS_snprintf _snprintf
+# else
+# define PyOS_snprintf snprintf
+# endif
+#endif
+
+/* A crude PyString_FromFormat implementation for old Pythons */
+#if PY_VERSION_HEX < 0x02020000
+
+#ifndef SWIG_PYBUFFER_SIZE
+# define SWIG_PYBUFFER_SIZE 1024
+#endif
+
+static PyObject *
+PyString_FromFormat(const char *fmt, ...) {
+ va_list ap;
+ char buf[SWIG_PYBUFFER_SIZE * 2];
+ int res;
+ va_start(ap, fmt);
+ res = vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ return (res < 0 || res >= (int)sizeof(buf)) ? 0 : PyString_FromString(buf);
+}
+#endif
+
+/* Add PyObject_Del for old Pythons */
+#if PY_VERSION_HEX < 0x01060000
+# define PyObject_Del(op) PyMem_DEL((op))
+#endif
+#ifndef PyObject_DEL
+# define PyObject_DEL PyObject_Del
+#endif
+
+/* A crude PyExc_StopIteration exception for old Pythons */
+#if PY_VERSION_HEX < 0x02020000
+# ifndef PyExc_StopIteration
+# define PyExc_StopIteration PyExc_RuntimeError
+# endif
+# ifndef PyObject_GenericGetAttr
+# define PyObject_GenericGetAttr 0
+# endif
+#endif
+/* Py_NotImplemented is defined in 2.1 and up. */
+#if PY_VERSION_HEX < 0x02010000
+# ifndef Py_NotImplemented
+# define Py_NotImplemented PyExc_RuntimeError
+# endif
+#endif
+
+
+/* A crude PyString_AsStringAndSize implementation for old Pythons */
+#if PY_VERSION_HEX < 0x02010000
+# ifndef PyString_AsStringAndSize
+# define PyString_AsStringAndSize(obj, s, len) {*s = PyString_AsString(obj); *len = *s ? strlen(*s) : 0;}
+# endif
+#endif
+
+/* PySequence_Size for old Pythons */
+#if PY_VERSION_HEX < 0x02000000
+# ifndef PySequence_Size
+# define PySequence_Size PySequence_Length
+# endif
+#endif
+
+
+/* PyBool_FromLong for old Pythons */
+#if PY_VERSION_HEX < 0x02030000
+static
+PyObject *PyBool_FromLong(long ok)
+{
+ PyObject *result = ok ? Py_True : Py_False;
+ Py_INCREF(result);
+ return result;
+}
+#endif
+
+/* Py_ssize_t for old Pythons */
+/* This code is as recommended by: */
+/* http://www.python.org/dev/peps/pep-0353/#conversion-guidelines */
+#if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
+typedef int Py_ssize_t;
+# define PY_SSIZE_T_MAX INT_MAX
+# define PY_SSIZE_T_MIN INT_MIN
+#endif
+
+/* -----------------------------------------------------------------------------
+ * error manipulation
+ * ----------------------------------------------------------------------------- */
+
+SWIGRUNTIME PyObject*
+SWIG_Python_ErrorType(int code) {
+ PyObject* type = 0;
+ switch(code) {
+ case SWIG_MemoryError:
+ type = PyExc_MemoryError;
+ break;
+ case SWIG_IOError:
+ type = PyExc_IOError;
+ break;
+ case SWIG_RuntimeError:
+ type = PyExc_RuntimeError;
+ break;
+ case SWIG_IndexError:
+ type = PyExc_IndexError;
+ break;
+ case SWIG_TypeError:
+ type = PyExc_TypeError;
+ break;
+ case SWIG_DivisionByZero:
+ type = PyExc_ZeroDivisionError;
+ break;
+ case SWIG_OverflowError:
+ type = PyExc_OverflowError;
+ break;
+ case SWIG_SyntaxError:
+ type = PyExc_SyntaxError;
+ break;
+ case SWIG_ValueError:
+ type = PyExc_ValueError;
+ break;
+ case SWIG_SystemError:
+ type = PyExc_SystemError;
+ break;
+ case SWIG_AttributeError:
+ type = PyExc_AttributeError;
+ break;
+ default:
+ type = PyExc_RuntimeError;
+ }
+ return type;
+}
+
+
+SWIGRUNTIME void
+SWIG_Python_AddErrorMsg(const char* mesg)
+{
+ PyObject *type = 0;
+ PyObject *value = 0;
+ PyObject *traceback = 0;
+
+ if (PyErr_Occurred()) PyErr_Fetch(&type, &value, &traceback);
+ if (value) {
+ PyObject *old_str = PyObject_Str(value);
+ PyErr_Clear();
+ Py_XINCREF(type);
+ PyErr_Format(type, "%s %s", PyString_AsString(old_str), mesg);
+ Py_DECREF(old_str);
+ Py_DECREF(value);
+ } else {
+ PyErr_SetString(PyExc_RuntimeError, mesg);
+ }
+}
+
+
+
+#if defined(SWIG_PYTHON_NO_THREADS)
+# if defined(SWIG_PYTHON_THREADS)
+# undef SWIG_PYTHON_THREADS
+# endif
+#endif
+#if defined(SWIG_PYTHON_THREADS) /* Threading support is enabled */
+# if !defined(SWIG_PYTHON_USE_GIL) && !defined(SWIG_PYTHON_NO_USE_GIL)
+# if (PY_VERSION_HEX >= 0x02030000) /* For 2.3 or later, use the PyGILState calls */
+# define SWIG_PYTHON_USE_GIL
+# endif
+# endif
+# if defined(SWIG_PYTHON_USE_GIL) /* Use PyGILState threads calls */
+# ifndef SWIG_PYTHON_INITIALIZE_THREADS
+# define SWIG_PYTHON_INITIALIZE_THREADS PyEval_InitThreads()
+# endif
+# ifdef __cplusplus /* C++ code */
+ class SWIG_Python_Thread_Block {
+ bool status;
+ PyGILState_STATE state;
+ public:
+ void end() { if (status) { PyGILState_Release(state); status = false;} }
+ SWIG_Python_Thread_Block() : status(true), state(PyGILState_Ensure()) {}
+ ~SWIG_Python_Thread_Block() { end(); }
+ };
+ class SWIG_Python_Thread_Allow {
+ bool status;
+ PyThreadState *save;
+ public:
+ void end() { if (status) { PyEval_RestoreThread(save); status = false; }}
+ SWIG_Python_Thread_Allow() : status(true), save(PyEval_SaveThread()) {}
+ ~SWIG_Python_Thread_Allow() { end(); }
+ };
+# define SWIG_PYTHON_THREAD_BEGIN_BLOCK SWIG_Python_Thread_Block _swig_thread_block
+# define SWIG_PYTHON_THREAD_END_BLOCK _swig_thread_block.end()
+# define SWIG_PYTHON_THREAD_BEGIN_ALLOW SWIG_Python_Thread_Allow _swig_thread_allow
+# define SWIG_PYTHON_THREAD_END_ALLOW _swig_thread_allow.end()
+# else /* C code */
+# define SWIG_PYTHON_THREAD_BEGIN_BLOCK PyGILState_STATE _swig_thread_block = PyGILState_Ensure()
+# define SWIG_PYTHON_THREAD_END_BLOCK PyGILState_Release(_swig_thread_block)
+# define SWIG_PYTHON_THREAD_BEGIN_ALLOW PyThreadState *_swig_thread_allow = PyEval_SaveThread()
+# define SWIG_PYTHON_THREAD_END_ALLOW PyEval_RestoreThread(_swig_thread_allow)
+# endif
+# else /* Old thread way, not implemented, user must provide it */
+# if !defined(SWIG_PYTHON_INITIALIZE_THREADS)
+# define SWIG_PYTHON_INITIALIZE_THREADS
+# endif
+# if !defined(SWIG_PYTHON_THREAD_BEGIN_BLOCK)
+# define SWIG_PYTHON_THREAD_BEGIN_BLOCK
+# endif
+# if !defined(SWIG_PYTHON_THREAD_END_BLOCK)
+# define SWIG_PYTHON_THREAD_END_BLOCK
+# endif
+# if !defined(SWIG_PYTHON_THREAD_BEGIN_ALLOW)
+# define SWIG_PYTHON_THREAD_BEGIN_ALLOW
+# endif
+# if !defined(SWIG_PYTHON_THREAD_END_ALLOW)
+# define SWIG_PYTHON_THREAD_END_ALLOW
+# endif
+# endif
+#else /* No thread support */
+# define SWIG_PYTHON_INITIALIZE_THREADS
+# define SWIG_PYTHON_THREAD_BEGIN_BLOCK
+# define SWIG_PYTHON_THREAD_END_BLOCK
+# define SWIG_PYTHON_THREAD_BEGIN_ALLOW
+# define SWIG_PYTHON_THREAD_END_ALLOW
+#endif
+
+/* -----------------------------------------------------------------------------
+ * Python API portion that goes into the runtime
+ * ----------------------------------------------------------------------------- */
+
+#ifdef __cplusplus
+extern "C" {
+#if 0
+} /* cc-mode */
+#endif
+#endif
+
+/* -----------------------------------------------------------------------------
+ * Constant declarations
+ * ----------------------------------------------------------------------------- */
+
+/* Constant Types */
+#define SWIG_PY_POINTER 4
+#define SWIG_PY_BINARY 5
+
+/* Constant information structure */
+typedef struct swig_const_info {
+ int type;
+ char *name;
+ long lvalue;
+ double dvalue;
+ void *pvalue;
+ swig_type_info **ptype;
+} swig_const_info;
+
+#ifdef __cplusplus
+#if 0
+{ /* cc-mode */
+#endif
+}
+#endif
+
+
+/* -----------------------------------------------------------------------------
+ * See the LICENSE file for information on copyright, usage and redistribution
+ * of SWIG, and the README file for authors - http://www.swig.org/release.html.
+ *
+ * pyrun.swg
+ *
+ * This file contains the runtime support for Python modules
+ * and includes code for managing global variables and pointer
+ * type checking.
+ *
+ * ----------------------------------------------------------------------------- */
+
+/* Common SWIG API */
+
+/* for raw pointers */
+#define SWIG_Python_ConvertPtr(obj, pptr, type, flags) SWIG_Python_ConvertPtrAndOwn(obj, pptr, type, flags, 0)
+#define SWIG_ConvertPtr(obj, pptr, type, flags) SWIG_Python_ConvertPtr(obj, pptr, type, flags)
+#define SWIG_ConvertPtrAndOwn(obj,pptr,type,flags,own) SWIG_Python_ConvertPtrAndOwn(obj, pptr, type, flags, own)
+#define SWIG_NewPointerObj(ptr, type, flags) SWIG_Python_NewPointerObj(ptr, type, flags)
+#define SWIG_CheckImplicit(ty) SWIG_Python_CheckImplicit(ty)
+#define SWIG_AcquirePtr(ptr, src) SWIG_Python_AcquirePtr(ptr, src)
+#define swig_owntype int
+
+/* for raw packed data */
+#define SWIG_ConvertPacked(obj, ptr, sz, ty) SWIG_Python_ConvertPacked(obj, ptr, sz, ty)
+#define SWIG_NewPackedObj(ptr, sz, type) SWIG_Python_NewPackedObj(ptr, sz, type)
+
+/* for class or struct pointers */
+#define SWIG_ConvertInstance(obj, pptr, type, flags) SWIG_ConvertPtr(obj, pptr, type, flags)
+#define SWIG_NewInstanceObj(ptr, type, flags) SWIG_NewPointerObj(ptr, type, flags)
+
+/* for C or C++ function pointers */
+#define SWIG_ConvertFunctionPtr(obj, pptr, type) SWIG_Python_ConvertFunctionPtr(obj, pptr, type)
+#define SWIG_NewFunctionPtrObj(ptr, type) SWIG_Python_NewPointerObj(ptr, type, 0)
+
+/* for C++ member pointers, ie, member methods */
+#define SWIG_ConvertMember(obj, ptr, sz, ty) SWIG_Python_ConvertPacked(obj, ptr, sz, ty)
+#define SWIG_NewMemberObj(ptr, sz, type) SWIG_Python_NewPackedObj(ptr, sz, type)
+
+
+/* Runtime API */
+
+#define SWIG_GetModule(clientdata) SWIG_Python_GetModule()
+#define SWIG_SetModule(clientdata, pointer) SWIG_Python_SetModule(pointer)
+#define SWIG_NewClientData(obj) PySwigClientData_New(obj)
+
+#define SWIG_SetErrorObj SWIG_Python_SetErrorObj
+#define SWIG_SetErrorMsg SWIG_Python_SetErrorMsg
+#define SWIG_ErrorType(code) SWIG_Python_ErrorType(code)
+#define SWIG_Error(code, msg) SWIG_Python_SetErrorMsg(SWIG_ErrorType(code), msg)
+#define SWIG_fail goto fail
+
+
+/* Runtime API implementation */
+
+/* Error manipulation */
+
+SWIGINTERN void
+SWIG_Python_SetErrorObj(PyObject *errtype, PyObject *obj) {
+ SWIG_PYTHON_THREAD_BEGIN_BLOCK;
+ PyErr_SetObject(errtype, obj);
+ Py_DECREF(obj);
+ SWIG_PYTHON_THREAD_END_BLOCK;
+}
+
+SWIGINTERN void
+SWIG_Python_SetErrorMsg(PyObject *errtype, const char *msg) {
+ SWIG_PYTHON_THREAD_BEGIN_BLOCK;
+ PyErr_SetString(errtype, (char *) msg);
+ SWIG_PYTHON_THREAD_END_BLOCK;
+}
+
+#define SWIG_Python_Raise(obj, type, desc) SWIG_Python_SetErrorObj(SWIG_Python_ExceptionType(desc), obj)
+
+/* Set a constant value */
+
+SWIGINTERN void
+SWIG_Python_SetConstant(PyObject *d, const char *name, PyObject *obj) {
+ PyDict_SetItemString(d, (char*) name, obj);
+ Py_DECREF(obj);
+}
+
+/* Append a value to the result obj */
+
+SWIGINTERN PyObject*
+SWIG_Python_AppendOutput(PyObject* result, PyObject* obj) {
+#if !defined(SWIG_PYTHON_OUTPUT_TUPLE)
+ if (!result) {
+ result = obj;
+ } else if (result == Py_None) {
+ Py_DECREF(result);
+ result = obj;
+ } else {
+ if (!PyList_Check(result)) {
+ PyObject *o2 = result;
+ result = PyList_New(1);
+ PyList_SetItem(result, 0, o2);
+ }
+ PyList_Append(result,obj);
+ Py_DECREF(obj);
+ }
+ return result;
+#else
+ PyObject* o2;
+ PyObject* o3;
+ if (!result) {
+ result = obj;
+ } else if (result == Py_None) {
+ Py_DECREF(result);
+ result = obj;
+ } else {
+ if (!PyTuple_Check(result)) {
+ o2 = result;
+ result = PyTuple_New(1);
+ PyTuple_SET_ITEM(result, 0, o2);
+ }
+ o3 = PyTuple_New(1);
+ PyTuple_SET_ITEM(o3, 0, obj);
+ o2 = result;
+ result = PySequence_Concat(o2, o3);
+ Py_DECREF(o2);
+ Py_DECREF(o3);
+ }
+ return result;
+#endif
+}
+
+/* Unpack the argument tuple */
+
+SWIGINTERN int
+SWIG_Python_UnpackTuple(PyObject *args, const char *name, Py_ssize_t min, Py_ssize_t max, PyObject **objs)
+{
+ if (!args) {
+ if (!min && !max) {
+ return 1;
+ } else {
+ PyErr_Format(PyExc_TypeError, "%s expected %s%d arguments, got none",
+ name, (min == max ? "" : "at least "), (int)min);
+ return 0;
+ }
+ }
+ if (!PyTuple_Check(args)) {
+ PyErr_SetString(PyExc_SystemError, "UnpackTuple() argument list is not a tuple");
+ return 0;
+ } else {
+ register Py_ssize_t l = PyTuple_GET_SIZE(args);
+ if (l < min) {
+ PyErr_Format(PyExc_TypeError, "%s expected %s%d arguments, got %d",
+ name, (min == max ? "" : "at least "), (int)min, (int)l);
+ return 0;
+ } else if (l > max) {
+ PyErr_Format(PyExc_TypeError, "%s expected %s%d arguments, got %d",
+ name, (min == max ? "" : "at most "), (int)max, (int)l);
+ return 0;
+ } else {
+ register int i;
+ for (i = 0; i < l; ++i) {
+ objs[i] = PyTuple_GET_ITEM(args, i);
+ }
+ for (; l < max; ++l) {
+ objs[l] = 0;
+ }
+ return i + 1;
+ }
+ }
+}
+
+/* A functor is a function object with one single object argument */
+#if PY_VERSION_HEX >= 0x02020000
+#define SWIG_Python_CallFunctor(functor, obj) PyObject_CallFunctionObjArgs(functor, obj, NULL);
+#else
+#define SWIG_Python_CallFunctor(functor, obj) PyObject_CallFunction(functor, "O", obj);
+#endif
+
+/*
+ Helper for static pointer initialization for both C and C++ code, for example
+ static PyObject *SWIG_STATIC_POINTER(MyVar) = NewSomething(...);
+*/
+#ifdef __cplusplus
+#define SWIG_STATIC_POINTER(var) var
+#else
+#define SWIG_STATIC_POINTER(var) var = 0; if (!var) var
+#endif
+
+/* -----------------------------------------------------------------------------
+ * Pointer declarations
+ * ----------------------------------------------------------------------------- */
+
+/* Flags for new pointer objects */
+#define SWIG_POINTER_NOSHADOW (SWIG_POINTER_OWN << 1)
+#define SWIG_POINTER_NEW (SWIG_POINTER_NOSHADOW | SWIG_POINTER_OWN)
+
+#define SWIG_POINTER_IMPLICIT_CONV (SWIG_POINTER_DISOWN << 1)
+
+#ifdef __cplusplus
+extern "C" {
+#if 0
+} /* cc-mode */
+#endif
+#endif
+
+/* How to access Py_None */
+#if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__)
+# ifndef SWIG_PYTHON_NO_BUILD_NONE
+# ifndef SWIG_PYTHON_BUILD_NONE
+# define SWIG_PYTHON_BUILD_NONE
+# endif
+# endif
+#endif
+
+#ifdef SWIG_PYTHON_BUILD_NONE
+# ifdef Py_None
+# undef Py_None
+# define Py_None SWIG_Py_None()
+# endif
+SWIGRUNTIMEINLINE PyObject *
+_SWIG_Py_None(void)
+{
+ PyObject *none = Py_BuildValue((char*)"");
+ Py_DECREF(none);
+ return none;
+}
+SWIGRUNTIME PyObject *
+SWIG_Py_None(void)
+{
+ static PyObject *SWIG_STATIC_POINTER(none) = _SWIG_Py_None();
+ return none;
+}
+#endif
+
+/* The python void return value */
+
+SWIGRUNTIMEINLINE PyObject *
+SWIG_Py_Void(void)
+{
+ PyObject *none = Py_None;
+ Py_INCREF(none);
+ return none;
+}
+
+/* PySwigClientData */
+
+typedef struct {
+ PyObject *klass;
+ PyObject *newraw;
+ PyObject *newargs;
+ PyObject *destroy;
+ int delargs;
+ int implicitconv;
+} PySwigClientData;
+
+SWIGRUNTIMEINLINE int
+SWIG_Python_CheckImplicit(swig_type_info *ty)
+{
+ PySwigClientData *data = (PySwigClientData *)ty->clientdata;
+ return data ? data->implicitconv : 0;
+}
+
+SWIGRUNTIMEINLINE PyObject *
+SWIG_Python_ExceptionType(swig_type_info *desc) {
+ PySwigClientData *data = desc ? (PySwigClientData *) desc->clientdata : 0;
+ PyObject *klass = data ? data->klass : 0;
+ return (klass ? klass : PyExc_RuntimeError);
+}
+
+
+SWIGRUNTIME PySwigClientData *
+PySwigClientData_New(PyObject* obj)
+{
+ if (!obj) {
+ return 0;
+ } else {
+ PySwigClientData *data = (PySwigClientData *)malloc(sizeof(PySwigClientData));
+ /* the klass element */
+ data->klass = obj;
+ Py_INCREF(data->klass);
+ /* the newraw method and newargs arguments used to create a new raw instance */
+ if (PyClass_Check(obj)) {
+ data->newraw = 0;
+ data->newargs = obj;
+ Py_INCREF(obj);
+ } else {
+#if (PY_VERSION_HEX < 0x02020000)
+ data->newraw = 0;
+#else
+ data->newraw = PyObject_GetAttrString(data->klass, (char *)"__new__");
+#endif
+ if (data->newraw) {
+ Py_INCREF(data->newraw);
+ data->newargs = PyTuple_New(1);
+ PyTuple_SetItem(data->newargs, 0, obj);
+ } else {
+ data->newargs = obj;
+ }
+ Py_INCREF(data->newargs);
+ }
+ /* the destroy method, aka as the C++ delete method */
+ data->destroy = PyObject_GetAttrString(data->klass, (char *)"__swig_destroy__");
+ if (PyErr_Occurred()) {
+ PyErr_Clear();
+ data->destroy = 0;
+ }
+ if (data->destroy) {
+ int flags;
+ Py_INCREF(data->destroy);
+ flags = PyCFunction_GET_FLAGS(data->destroy);
+#ifdef METH_O
+ data->delargs = !(flags & (METH_O));
+#else
+ data->delargs = 0;
+#endif
+ } else {
+ data->delargs = 0;
+ }
+ data->implicitconv = 0;
+ return data;
+ }
+}
+
+SWIGRUNTIME void
+PySwigClientData_Del(PySwigClientData* data)
+{
+ Py_XDECREF(data->newraw);
+ Py_XDECREF(data->newargs);
+ Py_XDECREF(data->destroy);
+}
+
+/* =============== PySwigObject =====================*/
+
+typedef struct {
+ PyObject_HEAD
+ void *ptr;
+ swig_type_info *ty;
+ int own;
+ PyObject *next;
+} PySwigObject;
+
+SWIGRUNTIME PyObject *
+PySwigObject_long(PySwigObject *v)
+{
+ return PyLong_FromVoidPtr(v->ptr);
+}
+
+SWIGRUNTIME PyObject *
+PySwigObject_format(const char* fmt, PySwigObject *v)
+{
+ PyObject *res = NULL;
+ PyObject *args = PyTuple_New(1);
+ if (args) {
+ if (PyTuple_SetItem(args, 0, PySwigObject_long(v)) == 0) {
+ PyObject *ofmt = PyString_FromString(fmt);
+ if (ofmt) {
+ res = PyString_Format(ofmt,args);
+ Py_DECREF(ofmt);
+ }
+ Py_DECREF(args);
+ }
+ }
+ return res;
+}
+
+SWIGRUNTIME PyObject *
+PySwigObject_oct(PySwigObject *v)
+{
+ return PySwigObject_format("%o",v);
+}
+
+SWIGRUNTIME PyObject *
+PySwigObject_hex(PySwigObject *v)
+{
+ return PySwigObject_format("%x",v);
+}
+
+SWIGRUNTIME PyObject *
+#ifdef METH_NOARGS
+PySwigObject_repr(PySwigObject *v)
+#else
+PySwigObject_repr(PySwigObject *v, PyObject *args)
+#endif
+{
+ const char *name = SWIG_TypePrettyName(v->ty);
+ PyObject *hex = PySwigObject_hex(v);
+ PyObject *repr = PyString_FromFormat("<Swig Object of type '%s' at 0x%s>", name, PyString_AsString(hex));
+ Py_DECREF(hex);
+ if (v->next) {
+#ifdef METH_NOARGS
+ PyObject *nrep = PySwigObject_repr((PySwigObject *)v->next);
+#else
+ PyObject *nrep = PySwigObject_repr((PySwigObject *)v->next, args);
+#endif
+ PyString_ConcatAndDel(&repr,nrep);
+ }
+ return repr;
+}
+
+SWIGRUNTIME int
+PySwigObject_print(PySwigObject *v, FILE *fp, int SWIGUNUSEDPARM(flags))
+{
+#ifdef METH_NOARGS
+ PyObject *repr = PySwigObject_repr(v);
+#else
+ PyObject *repr = PySwigObject_repr(v, NULL);
+#endif
+ if (repr) {
+ fputs(PyString_AsString(repr), fp);
+ Py_DECREF(repr);
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+SWIGRUNTIME PyObject *
+PySwigObject_str(PySwigObject *v)
+{
+ char result[SWIG_BUFFER_SIZE];
+ return SWIG_PackVoidPtr(result, v->ptr, v->ty->name, sizeof(result)) ?
+ PyString_FromString(result) : 0;
+}
+
+SWIGRUNTIME int
+PySwigObject_compare(PySwigObject *v, PySwigObject *w)
+{
+ void *i = v->ptr;
+ void *j = w->ptr;
+ return (i < j) ? -1 : ((i > j) ? 1 : 0);
+}
+
+SWIGRUNTIME PyTypeObject* _PySwigObject_type(void);
+
+SWIGRUNTIME PyTypeObject*
+PySwigObject_type(void) {
+ static PyTypeObject *SWIG_STATIC_POINTER(type) = _PySwigObject_type();
+ return type;
+}
+
+SWIGRUNTIMEINLINE int
+PySwigObject_Check(PyObject *op) {
+ return ((op)->ob_type == PySwigObject_type())
+ || (strcmp((op)->ob_type->tp_name,"PySwigObject") == 0);
+}
+
+SWIGRUNTIME PyObject *
+PySwigObject_New(void *ptr, swig_type_info *ty, int own);
+
+SWIGRUNTIME void
+PySwigObject_dealloc(PyObject *v)
+{
+ PySwigObject *sobj = (PySwigObject *) v;
+ PyObject *next = sobj->next;
+ if (sobj->own == SWIG_POINTER_OWN) {
+ swig_type_info *ty = sobj->ty;
+ PySwigClientData *data = ty ? (PySwigClientData *) ty->clientdata : 0;
+ PyObject *destroy = data ? data->destroy : 0;
+ if (destroy) {
+ /* destroy is always a VARARGS method */
+ PyObject *res;
+ if (data->delargs) {
+ /* we need to create a temporal object to carry the destroy operation */
+ PyObject *tmp = PySwigObject_New(sobj->ptr, ty, 0);
+ res = SWIG_Python_CallFunctor(destroy, tmp);
+ Py_DECREF(tmp);
+ } else {
+ PyCFunction meth = PyCFunction_GET_FUNCTION(destroy);
+ PyObject *mself = PyCFunction_GET_SELF(destroy);
+ res = ((*meth)(mself, v));
+ }
+ Py_XDECREF(res);
+ }
+#if !defined(SWIG_PYTHON_SILENT_MEMLEAK)
+ else {
+ const char *name = SWIG_TypePrettyName(ty);
+ printf("swig/python detected a memory leak of type '%s', no destructor found.\n", (name ? name : "unknown"));
+ }
+#endif
+ }
+ Py_XDECREF(next);
+ PyObject_DEL(v);
+}
+
+SWIGRUNTIME PyObject*
+PySwigObject_append(PyObject* v, PyObject* next)
+{
+ PySwigObject *sobj = (PySwigObject *) v;
+#ifndef METH_O
+ PyObject *tmp = 0;
+ if (!PyArg_ParseTuple(next,(char *)"O:append", &tmp)) return NULL;
+ next = tmp;
+#endif
+ if (!PySwigObject_Check(next)) {
+ return NULL;
+ }
+ sobj->next = next;
+ Py_INCREF(next);
+ return SWIG_Py_Void();
+}
+
+SWIGRUNTIME PyObject*
+#ifdef METH_NOARGS
+PySwigObject_next(PyObject* v)
+#else
+PySwigObject_next(PyObject* v, PyObject *SWIGUNUSEDPARM(args))
+#endif
+{
+ PySwigObject *sobj = (PySwigObject *) v;
+ if (sobj->next) {
+ Py_INCREF(sobj->next);
+ return sobj->next;
+ } else {
+ return SWIG_Py_Void();
+ }
+}
+
+SWIGINTERN PyObject*
+#ifdef METH_NOARGS
+PySwigObject_disown(PyObject *v)
+#else
+PySwigObject_disown(PyObject* v, PyObject *SWIGUNUSEDPARM(args))
+#endif
+{
+ PySwigObject *sobj = (PySwigObject *)v;
+ sobj->own = 0;
+ return SWIG_Py_Void();
+}
+
+SWIGINTERN PyObject*
+#ifdef METH_NOARGS
+PySwigObject_acquire(PyObject *v)
+#else
+PySwigObject_acquire(PyObject* v, PyObject *SWIGUNUSEDPARM(args))
+#endif
+{
+ PySwigObject *sobj = (PySwigObject *)v;
+ sobj->own = SWIG_POINTER_OWN;
+ return SWIG_Py_Void();
+}
+
+SWIGINTERN PyObject*
+PySwigObject_own(PyObject *v, PyObject *args)
+{
+ PyObject *val = 0;
+#if (PY_VERSION_HEX < 0x02020000)
+ if (!PyArg_ParseTuple(args,(char *)"|O:own",&val))
+#else
+ if (!PyArg_UnpackTuple(args, (char *)"own", 0, 1, &val))
+#endif
+ {
+ return NULL;
+ }
+ else
+ {
+ PySwigObject *sobj = (PySwigObject *)v;
+ PyObject *obj = PyBool_FromLong(sobj->own);
+ if (val) {
+#ifdef METH_NOARGS
+ if (PyObject_IsTrue(val)) {
+ PySwigObject_acquire(v);
+ } else {
+ PySwigObject_disown(v);
+ }
+#else
+ if (PyObject_IsTrue(val)) {
+ PySwigObject_acquire(v,args);
+ } else {
+ PySwigObject_disown(v,args);
+ }
+#endif
+ }
+ return obj;
+ }
+}
+
+#ifdef METH_O
+static PyMethodDef
+swigobject_methods[] = {
+ {(char *)"disown", (PyCFunction)PySwigObject_disown, METH_NOARGS, (char *)"releases ownership of the pointer"},
+ {(char *)"acquire", (PyCFunction)PySwigObject_acquire, METH_NOARGS, (char *)"aquires ownership of the pointer"},
+ {(char *)"own", (PyCFunction)PySwigObject_own, METH_VARARGS, (char *)"returns/sets ownership of the pointer"},
+ {(char *)"append", (PyCFunction)PySwigObject_append, METH_O, (char *)"appends another 'this' object"},
+ {(char *)"next", (PyCFunction)PySwigObject_next, METH_NOARGS, (char *)"returns the next 'this' object"},
+ {(char *)"__repr__",(PyCFunction)PySwigObject_repr, METH_NOARGS, (char *)"returns object representation"},
+ {0, 0, 0, 0}
+};
+#else
+static PyMethodDef
+swigobject_methods[] = {
+ {(char *)"disown", (PyCFunction)PySwigObject_disown, METH_VARARGS, (char *)"releases ownership of the pointer"},
+ {(char *)"acquire", (PyCFunction)PySwigObject_acquire, METH_VARARGS, (char *)"aquires ownership of the pointer"},
+ {(char *)"own", (PyCFunction)PySwigObject_own, METH_VARARGS, (char *)"returns/sets ownership of the pointer"},
+ {(char *)"append", (PyCFunction)PySwigObject_append, METH_VARARGS, (char *)"appends another 'this' object"},
+ {(char *)"next", (PyCFunction)PySwigObject_next, METH_VARARGS, (char *)"returns the next 'this' object"},
+ {(char *)"__repr__",(PyCFunction)PySwigObject_repr, METH_VARARGS, (char *)"returns object representation"},
+ {0, 0, 0, 0}
+};
+#endif
+
+#if PY_VERSION_HEX < 0x02020000
+SWIGINTERN PyObject *
+PySwigObject_getattr(PySwigObject *sobj,char *name)
+{
+ return Py_FindMethod(swigobject_methods, (PyObject *)sobj, name);
+}
+#endif
+
+SWIGRUNTIME PyTypeObject*
+_PySwigObject_type(void) {
+ static char swigobject_doc[] = "Swig object carries a C/C++ instance pointer";
+
+ static PyNumberMethods PySwigObject_as_number = {
+ (binaryfunc)0, /*nb_add*/
+ (binaryfunc)0, /*nb_subtract*/
+ (binaryfunc)0, /*nb_multiply*/
+ (binaryfunc)0, /*nb_divide*/
+ (binaryfunc)0, /*nb_remainder*/
+ (binaryfunc)0, /*nb_divmod*/
+ (ternaryfunc)0,/*nb_power*/
+ (unaryfunc)0, /*nb_negative*/
+ (unaryfunc)0, /*nb_positive*/
+ (unaryfunc)0, /*nb_absolute*/
+ (inquiry)0, /*nb_nonzero*/
+ 0, /*nb_invert*/
+ 0, /*nb_lshift*/
+ 0, /*nb_rshift*/
+ 0, /*nb_and*/
+ 0, /*nb_xor*/
+ 0, /*nb_or*/
+ (coercion)0, /*nb_coerce*/
+ (unaryfunc)PySwigObject_long, /*nb_int*/
+ (unaryfunc)PySwigObject_long, /*nb_long*/
+ (unaryfunc)0, /*nb_float*/
+ (unaryfunc)PySwigObject_oct, /*nb_oct*/
+ (unaryfunc)PySwigObject_hex, /*nb_hex*/
+#if PY_VERSION_HEX >= 0x02050000 /* 2.5.0 */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* nb_inplace_add -> nb_index */
+#elif PY_VERSION_HEX >= 0x02020000 /* 2.2.0 */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* nb_inplace_add -> nb_inplace_true_divide */
+#elif PY_VERSION_HEX >= 0x02000000 /* 2.0.0 */
+ 0,0,0,0,0,0,0,0,0,0,0 /* nb_inplace_add -> nb_inplace_or */
+#endif
+ };
+
+ static PyTypeObject pyswigobject_type;
+ static int type_init = 0;
+ if (!type_init) {
+ const PyTypeObject tmp
+ = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ (char *)"PySwigObject", /* tp_name */
+ sizeof(PySwigObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ (destructor)PySwigObject_dealloc, /* tp_dealloc */
+ (printfunc)PySwigObject_print, /* tp_print */
+#if PY_VERSION_HEX < 0x02020000
+ (getattrfunc)PySwigObject_getattr, /* tp_getattr */
+#else
+ (getattrfunc)0, /* tp_getattr */
+#endif
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)PySwigObject_compare, /* tp_compare */
+ (reprfunc)PySwigObject_repr, /* tp_repr */
+ &PySwigObject_as_number, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)PySwigObject_str, /* tp_str */
+ PyObject_GenericGetAttr, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ swigobject_doc, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+#if PY_VERSION_HEX >= 0x02020000
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ swigobject_methods, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+ 0, /* tp_free */
+ 0, /* tp_is_gc */
+ 0, /* tp_bases */
+ 0, /* tp_mro */
+ 0, /* tp_cache */
+ 0, /* tp_subclasses */
+ 0, /* tp_weaklist */
+#endif
+#if PY_VERSION_HEX >= 0x02030000
+ 0, /* tp_del */
+#endif
+#ifdef COUNT_ALLOCS
+ 0,0,0,0 /* tp_alloc -> tp_next */
+#endif
+ };
+ pyswigobject_type = tmp;
+ pyswigobject_type.ob_type = &PyType_Type;
+ type_init = 1;
+ }
+ return &pyswigobject_type;
+}
+
+SWIGRUNTIME PyObject *
+PySwigObject_New(void *ptr, swig_type_info *ty, int own)
+{
+ PySwigObject *sobj = PyObject_NEW(PySwigObject, PySwigObject_type());
+ if (sobj) {
+ sobj->ptr = ptr;
+ sobj->ty = ty;
+ sobj->own = own;
+ sobj->next = 0;
+ }
+ return (PyObject *)sobj;
+}
+
+/* -----------------------------------------------------------------------------
+ * Implements a simple Swig Packed type, and use it instead of string
+ * ----------------------------------------------------------------------------- */
+
+typedef struct {
+ PyObject_HEAD
+ void *pack;
+ swig_type_info *ty;
+ size_t size;
+} PySwigPacked;
+
+SWIGRUNTIME int
+PySwigPacked_print(PySwigPacked *v, FILE *fp, int SWIGUNUSEDPARM(flags))
+{
+ char result[SWIG_BUFFER_SIZE];
+ fputs("<Swig Packed ", fp);
+ if (SWIG_PackDataName(result, v->pack, v->size, 0, sizeof(result))) {
+ fputs("at ", fp);
+ fputs(result, fp);
+ }
+ fputs(v->ty->name,fp);
+ fputs(">", fp);
+ return 0;
+}
+
+SWIGRUNTIME PyObject *
+PySwigPacked_repr(PySwigPacked *v)
+{
+ char result[SWIG_BUFFER_SIZE];
+ if (SWIG_PackDataName(result, v->pack, v->size, 0, sizeof(result))) {
+ return PyString_FromFormat("<Swig Packed at %s%s>", result, v->ty->name);
+ } else {
+ return PyString_FromFormat("<Swig Packed %s>", v->ty->name);
+ }
+}
+
+SWIGRUNTIME PyObject *
+PySwigPacked_str(PySwigPacked *v)
+{
+ char result[SWIG_BUFFER_SIZE];
+ if (SWIG_PackDataName(result, v->pack, v->size, 0, sizeof(result))){
+ return PyString_FromFormat("%s%s", result, v->ty->name);
+ } else {
+ return PyString_FromString(v->ty->name);
+ }
+}
+
+SWIGRUNTIME int
+PySwigPacked_compare(PySwigPacked *v, PySwigPacked *w)
+{
+ size_t i = v->size;
+ size_t j = w->size;
+ int s = (i < j) ? -1 : ((i > j) ? 1 : 0);
+ return s ? s : strncmp((char *)v->pack, (char *)w->pack, 2*v->size);
+}
+
+SWIGRUNTIME PyTypeObject* _PySwigPacked_type(void);
+
+SWIGRUNTIME PyTypeObject*
+PySwigPacked_type(void) {
+ static PyTypeObject *SWIG_STATIC_POINTER(type) = _PySwigPacked_type();
+ return type;
+}
+
+SWIGRUNTIMEINLINE int
+PySwigPacked_Check(PyObject *op) {
+ return ((op)->ob_type == _PySwigPacked_type())
+ || (strcmp((op)->ob_type->tp_name,"PySwigPacked") == 0);
+}
+
+SWIGRUNTIME void
+PySwigPacked_dealloc(PyObject *v)
+{
+ if (PySwigPacked_Check(v)) {
+ PySwigPacked *sobj = (PySwigPacked *) v;
+ free(sobj->pack);
+ }
+ PyObject_DEL(v);
+}
+
+SWIGRUNTIME PyTypeObject*
+_PySwigPacked_type(void) {
+ static char swigpacked_doc[] = "Swig object carries a C/C++ instance pointer";
+ static PyTypeObject pyswigpacked_type;
+ static int type_init = 0;
+ if (!type_init) {
+ const PyTypeObject tmp
+ = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ (char *)"PySwigPacked", /* tp_name */
+ sizeof(PySwigPacked), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ (destructor)PySwigPacked_dealloc, /* tp_dealloc */
+ (printfunc)PySwigPacked_print, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)PySwigPacked_compare, /* tp_compare */
+ (reprfunc)PySwigPacked_repr, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)PySwigPacked_str, /* tp_str */
+ PyObject_GenericGetAttr, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ swigpacked_doc, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+#if PY_VERSION_HEX >= 0x02020000
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+ 0, /* tp_free */
+ 0, /* tp_is_gc */
+ 0, /* tp_bases */
+ 0, /* tp_mro */
+ 0, /* tp_cache */
+ 0, /* tp_subclasses */
+ 0, /* tp_weaklist */
+#endif
+#if PY_VERSION_HEX >= 0x02030000
+ 0, /* tp_del */
+#endif
+#ifdef COUNT_ALLOCS
+ 0,0,0,0 /* tp_alloc -> tp_next */
+#endif
+ };
+ pyswigpacked_type = tmp;
+ pyswigpacked_type.ob_type = &PyType_Type;
+ type_init = 1;
+ }
+ return &pyswigpacked_type;
+}
+
+SWIGRUNTIME PyObject *
+PySwigPacked_New(void *ptr, size_t size, swig_type_info *ty)
+{
+ PySwigPacked *sobj = PyObject_NEW(PySwigPacked, PySwigPacked_type());
+ if (sobj) {
+ void *pack = malloc(size);
+ if (pack) {
+ memcpy(pack, ptr, size);
+ sobj->pack = pack;
+ sobj->ty = ty;
+ sobj->size = size;
+ } else {
+ PyObject_DEL((PyObject *) sobj);
+ sobj = 0;
+ }
+ }
+ return (PyObject *) sobj;
+}
+
+SWIGRUNTIME swig_type_info *
+PySwigPacked_UnpackData(PyObject *obj, void *ptr, size_t size)
+{
+ if (PySwigPacked_Check(obj)) {
+ PySwigPacked *sobj = (PySwigPacked *)obj;
+ if (sobj->size != size) return 0;
+ memcpy(ptr, sobj->pack, size);
+ return sobj->ty;
+ } else {
+ return 0;
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * pointers/data manipulation
+ * ----------------------------------------------------------------------------- */
+
+SWIGRUNTIMEINLINE PyObject *
+_SWIG_This(void)
+{
+ return PyString_FromString("this");
+}
+
+SWIGRUNTIME PyObject *
+SWIG_This(void)
+{
+ static PyObject *SWIG_STATIC_POINTER(swig_this) = _SWIG_This();
+ return swig_this;
+}
+
+/* #define SWIG_PYTHON_SLOW_GETSET_THIS */
+
+SWIGRUNTIME PySwigObject *
+SWIG_Python_GetSwigThis(PyObject *pyobj)
+{
+ if (PySwigObject_Check(pyobj)) {
+ return (PySwigObject *) pyobj;
+ } else {
+ PyObject *obj = 0;
+#if (!defined(SWIG_PYTHON_SLOW_GETSET_THIS) && (PY_VERSION_HEX >= 0x02030000))
+ if (PyInstance_Check(pyobj)) {
+ obj = _PyInstance_Lookup(pyobj, SWIG_This());
+ } else {
+ PyObject **dictptr = _PyObject_GetDictPtr(pyobj);
+ if (dictptr != NULL) {
+ PyObject *dict = *dictptr;
+ obj = dict ? PyDict_GetItem(dict, SWIG_This()) : 0;
+ } else {
+#ifdef PyWeakref_CheckProxy
+ if (PyWeakref_CheckProxy(pyobj)) {
+ PyObject *wobj = PyWeakref_GET_OBJECT(pyobj);
+ return wobj ? SWIG_Python_GetSwigThis(wobj) : 0;
+ }
+#endif
+ obj = PyObject_GetAttr(pyobj,SWIG_This());
+ if (obj) {
+ Py_DECREF(obj);
+ } else {
+ if (PyErr_Occurred()) PyErr_Clear();
+ return 0;
+ }
+ }
+ }
+#else
+ obj = PyObject_GetAttr(pyobj,SWIG_This());
+ if (obj) {
+ Py_DECREF(obj);
+ } else {
+ if (PyErr_Occurred()) PyErr_Clear();
+ return 0;
+ }
+#endif
+ if (obj && !PySwigObject_Check(obj)) {
+ /* a PyObject is called 'this', try to get the 'real this'
+ PySwigObject from it */
+ return SWIG_Python_GetSwigThis(obj);
+ }
+ return (PySwigObject *)obj;
+ }
+}
+
+/* Acquire a pointer value */
+
+SWIGRUNTIME int
+SWIG_Python_AcquirePtr(PyObject *obj, int own) {
+ if (own == SWIG_POINTER_OWN) {
+ PySwigObject *sobj = SWIG_Python_GetSwigThis(obj);
+ if (sobj) {
+ int oldown = sobj->own;
+ sobj->own = own;
+ return oldown;
+ }
+ }
+ return 0;
+}
+
+/* Convert a pointer value */
+
+SWIGRUNTIME int
+SWIG_Python_ConvertPtrAndOwn(PyObject *obj, void **ptr, swig_type_info *ty, int flags, int *own) {
+ if (!obj) return SWIG_ERROR;
+ if (obj == Py_None) {
+ if (ptr) *ptr = 0;
+ return SWIG_OK;
+ } else {
+ PySwigObject *sobj = SWIG_Python_GetSwigThis(obj);
+ if (own)
+ *own = 0;
+ while (sobj) {
+ void *vptr = sobj->ptr;
+ if (ty) {
+ swig_type_info *to = sobj->ty;
+ if (to == ty) {
+ /* no type cast needed */
+ if (ptr) *ptr = vptr;
+ break;
+ } else {
+ swig_cast_info *tc = SWIG_TypeCheck(to->name,ty);
+ if (!tc) {
+ sobj = (PySwigObject *)sobj->next;
+ } else {
+ if (ptr) {
+ int newmemory = 0;
+ *ptr = SWIG_TypeCast(tc,vptr,&newmemory);
+ if (newmemory == SWIG_CAST_NEW_MEMORY) {
+ assert(own);
+ if (own)
+ *own = *own | SWIG_CAST_NEW_MEMORY;
+ }
+ }
+ break;
+ }
+ }
+ } else {
+ if (ptr) *ptr = vptr;
+ break;
+ }
+ }
+ if (sobj) {
+ if (own)
+ *own = *own | sobj->own;
+ if (flags & SWIG_POINTER_DISOWN) {
+ sobj->own = 0;
+ }
+ return SWIG_OK;
+ } else {
+ int res = SWIG_ERROR;
+ if (flags & SWIG_POINTER_IMPLICIT_CONV) {
+ PySwigClientData *data = ty ? (PySwigClientData *) ty->clientdata : 0;
+ if (data && !data->implicitconv) {
+ PyObject *klass = data->klass;
+ if (klass) {
+ PyObject *impconv;
+ data->implicitconv = 1; /* avoid recursion and call 'explicit' constructors*/
+ impconv = SWIG_Python_CallFunctor(klass, obj);
+ data->implicitconv = 0;
+ if (PyErr_Occurred()) {
+ PyErr_Clear();
+ impconv = 0;
+ }
+ if (impconv) {
+ PySwigObject *iobj = SWIG_Python_GetSwigThis(impconv);
+ if (iobj) {
+ void *vptr;
+ res = SWIG_Python_ConvertPtrAndOwn((PyObject*)iobj, &vptr, ty, 0, 0);
+ if (SWIG_IsOK(res)) {
+ if (ptr) {
+ *ptr = vptr;
+ /* transfer the ownership to 'ptr' */
+ iobj->own = 0;
+ res = SWIG_AddCast(res);
+ res = SWIG_AddNewMask(res);
+ } else {
+ res = SWIG_AddCast(res);
+ }
+ }
+ }
+ Py_DECREF(impconv);
+ }
+ }
+ }
+ }
+ return res;
+ }
+ }
+}
+
+/* Convert a function ptr value */
+
+SWIGRUNTIME int
+SWIG_Python_ConvertFunctionPtr(PyObject *obj, void **ptr, swig_type_info *ty) {
+ if (!PyCFunction_Check(obj)) {
+ return SWIG_ConvertPtr(obj, ptr, ty, 0);
+ } else {
+ void *vptr = 0;
+
+ /* here we get the method pointer for callbacks */
+ const char *doc = (((PyCFunctionObject *)obj) -> m_ml -> ml_doc);
+ const char *desc = doc ? strstr(doc, "swig_ptr: ") : 0;
+ if (desc) {
+ desc = ty ? SWIG_UnpackVoidPtr(desc + 10, &vptr, ty->name) : 0;
+ if (!desc) return SWIG_ERROR;
+ }
+ if (ty) {
+ swig_cast_info *tc = SWIG_TypeCheck(desc,ty);
+ if (tc) {
+ int newmemory = 0;
+ *ptr = SWIG_TypeCast(tc,vptr,&newmemory);
+ assert(!newmemory); /* newmemory handling not yet implemented */
+ } else {
+ return SWIG_ERROR;
+ }
+ } else {
+ *ptr = vptr;
+ }
+ return SWIG_OK;
+ }
+}
+
+/* Convert a packed value value */
+
+SWIGRUNTIME int
+SWIG_Python_ConvertPacked(PyObject *obj, void *ptr, size_t sz, swig_type_info *ty) {
+ swig_type_info *to = PySwigPacked_UnpackData(obj, ptr, sz);
+ if (!to) return SWIG_ERROR;
+ if (ty) {
+ if (to != ty) {
+ /* check type cast? */
+ swig_cast_info *tc = SWIG_TypeCheck(to->name,ty);
+ if (!tc) return SWIG_ERROR;
+ }
+ }
+ return SWIG_OK;
+}
+
+/* -----------------------------------------------------------------------------
+ * Create a new pointer object
+ * ----------------------------------------------------------------------------- */
+
+/*
+ Create a new instance object, whitout calling __init__, and set the
+ 'this' attribute.
+*/
+
+SWIGRUNTIME PyObject*
+SWIG_Python_NewShadowInstance(PySwigClientData *data, PyObject *swig_this)
+{
+#if (PY_VERSION_HEX >= 0x02020000)
+ PyObject *inst = 0;
+ PyObject *newraw = data->newraw;
+ if (newraw) {
+ inst = PyObject_Call(newraw, data->newargs, NULL);
+ if (inst) {
+#if !defined(SWIG_PYTHON_SLOW_GETSET_THIS)
+ PyObject **dictptr = _PyObject_GetDictPtr(inst);
+ if (dictptr != NULL) {
+ PyObject *dict = *dictptr;
+ if (dict == NULL) {
+ dict = PyDict_New();
+ *dictptr = dict;
+ PyDict_SetItem(dict, SWIG_This(), swig_this);
+ }
+ }
+#else
+ PyObject *key = SWIG_This();
+ PyObject_SetAttr(inst, key, swig_this);
+#endif
+ }
+ } else {
+ PyObject *dict = PyDict_New();
+ PyDict_SetItem(dict, SWIG_This(), swig_this);
+ inst = PyInstance_NewRaw(data->newargs, dict);
+ Py_DECREF(dict);
+ }
+ return inst;
+#else
+#if (PY_VERSION_HEX >= 0x02010000)
+ PyObject *inst;
+ PyObject *dict = PyDict_New();
+ PyDict_SetItem(dict, SWIG_This(), swig_this);
+ inst = PyInstance_NewRaw(data->newargs, dict);
+ Py_DECREF(dict);
+ return (PyObject *) inst;
+#else
+ PyInstanceObject *inst = PyObject_NEW(PyInstanceObject, &PyInstance_Type);
+ if (inst == NULL) {
+ return NULL;
+ }
+ inst->in_class = (PyClassObject *)data->newargs;
+ Py_INCREF(inst->in_class);
+ inst->in_dict = PyDict_New();
+ if (inst->in_dict == NULL) {
+ Py_DECREF(inst);
+ return NULL;
+ }
+#ifdef Py_TPFLAGS_HAVE_WEAKREFS
+ inst->in_weakreflist = NULL;
+#endif
+#ifdef Py_TPFLAGS_GC
+ PyObject_GC_Init(inst);
+#endif
+ PyDict_SetItem(inst->in_dict, SWIG_This(), swig_this);
+ return (PyObject *) inst;
+#endif
+#endif
+}
+
+SWIGRUNTIME void
+SWIG_Python_SetSwigThis(PyObject *inst, PyObject *swig_this)
+{
+ PyObject *dict;
+#if (PY_VERSION_HEX >= 0x02020000) && !defined(SWIG_PYTHON_SLOW_GETSET_THIS)
+ PyObject **dictptr = _PyObject_GetDictPtr(inst);
+ if (dictptr != NULL) {
+ dict = *dictptr;
+ if (dict == NULL) {
+ dict = PyDict_New();
+ *dictptr = dict;
+ }
+ PyDict_SetItem(dict, SWIG_This(), swig_this);
+ return;
+ }
+#endif
+ dict = PyObject_GetAttrString(inst, (char*)"__dict__");
+ PyDict_SetItem(dict, SWIG_This(), swig_this);
+ Py_DECREF(dict);
+}
+
+
+SWIGINTERN PyObject *
+SWIG_Python_InitShadowInstance(PyObject *args) {
+ PyObject *obj[2];
+ if (!SWIG_Python_UnpackTuple(args,(char*)"swiginit", 2, 2, obj)) {
+ return NULL;
+ } else {
+ PySwigObject *sthis = SWIG_Python_GetSwigThis(obj[0]);
+ if (sthis) {
+ PySwigObject_append((PyObject*) sthis, obj[1]);
+ } else {
+ SWIG_Python_SetSwigThis(obj[0], obj[1]);
+ }
+ return SWIG_Py_Void();
+ }
+}
+
+/* Create a new pointer object */
+
+SWIGRUNTIME PyObject *
+SWIG_Python_NewPointerObj(void *ptr, swig_type_info *type, int flags) {
+ if (!ptr) {
+ return SWIG_Py_Void();
+ } else {
+ int own = (flags & SWIG_POINTER_OWN) ? SWIG_POINTER_OWN : 0;
+ PyObject *robj = PySwigObject_New(ptr, type, own);
+ PySwigClientData *clientdata = type ? (PySwigClientData *)(type->clientdata) : 0;
+ if (clientdata && !(flags & SWIG_POINTER_NOSHADOW)) {
+ PyObject *inst = SWIG_Python_NewShadowInstance(clientdata, robj);
+ if (inst) {
+ Py_DECREF(robj);
+ robj = inst;
+ }
+ }
+ return robj;
+ }
+}
+
+/* Create a new packed object */
+
+SWIGRUNTIMEINLINE PyObject *
+SWIG_Python_NewPackedObj(void *ptr, size_t sz, swig_type_info *type) {
+ return ptr ? PySwigPacked_New((void *) ptr, sz, type) : SWIG_Py_Void();
+}
+
+/* -----------------------------------------------------------------------------*
+ * Get type list
+ * -----------------------------------------------------------------------------*/
+
+#ifdef SWIG_LINK_RUNTIME
+void *SWIG_ReturnGlobalTypeList(void *);
+#endif
+
+SWIGRUNTIME swig_module_info *
+SWIG_Python_GetModule(void) {
+ static void *type_pointer = (void *)0;
+ /* first check if module already created */
+ if (!type_pointer) {
+#ifdef SWIG_LINK_RUNTIME
+ type_pointer = SWIG_ReturnGlobalTypeList((void *)0);
+#else
+ type_pointer = PyCObject_Import((char*)"swig_runtime_data" SWIG_RUNTIME_VERSION,
+ (char*)"type_pointer" SWIG_TYPE_TABLE_NAME);
+ if (PyErr_Occurred()) {
+ PyErr_Clear();
+ type_pointer = (void *)0;
+ }
+#endif
+ }
+ return (swig_module_info *) type_pointer;
+}
+
+#if PY_MAJOR_VERSION < 2
+/* PyModule_AddObject function was introduced in Python 2.0. The following function
+ is copied out of Python/modsupport.c in python version 2.3.4 */
+SWIGINTERN int
+PyModule_AddObject(PyObject *m, char *name, PyObject *o)
+{
+ PyObject *dict;
+ if (!PyModule_Check(m)) {
+ PyErr_SetString(PyExc_TypeError,
+ "PyModule_AddObject() needs module as first arg");
+ return SWIG_ERROR;
+ }
+ if (!o) {
+ PyErr_SetString(PyExc_TypeError,
+ "PyModule_AddObject() needs non-NULL value");
+ return SWIG_ERROR;
+ }
+
+ dict = PyModule_GetDict(m);
+ if (dict == NULL) {
+ /* Internal error -- modules must have a dict! */
+ PyErr_Format(PyExc_SystemError, "module '%s' has no __dict__",
+ PyModule_GetName(m));
+ return SWIG_ERROR;
+ }
+ if (PyDict_SetItemString(dict, name, o))
+ return SWIG_ERROR;
+ Py_DECREF(o);
+ return SWIG_OK;
+}
+#endif
+
+SWIGRUNTIME void
+SWIG_Python_DestroyModule(void *vptr)
+{
+ swig_module_info *swig_module = (swig_module_info *) vptr;
+ swig_type_info **types = swig_module->types;
+ size_t i;
+ for (i =0; i < swig_module->size; ++i) {
+ swig_type_info *ty = types[i];
+ if (ty->owndata) {
+ PySwigClientData *data = (PySwigClientData *) ty->clientdata;
+ if (data) PySwigClientData_Del(data);
+ }
+ }
+ Py_DECREF(SWIG_This());
+}
+
+SWIGRUNTIME void
+SWIG_Python_SetModule(swig_module_info *swig_module) {
+ static PyMethodDef swig_empty_runtime_method_table[] = { {NULL, NULL, 0, NULL} };/* Sentinel */
+
+ PyObject *module = Py_InitModule((char*)"swig_runtime_data" SWIG_RUNTIME_VERSION,
+ swig_empty_runtime_method_table);
+ PyObject *pointer = PyCObject_FromVoidPtr((void *) swig_module, SWIG_Python_DestroyModule);
+ if (pointer && module) {
+ PyModule_AddObject(module, (char*)"type_pointer" SWIG_TYPE_TABLE_NAME, pointer);
+ } else {
+ Py_XDECREF(pointer);
+ }
+}
+
+/* The python cached type query */
+SWIGRUNTIME PyObject *
+SWIG_Python_TypeCache(void) {
+ static PyObject *SWIG_STATIC_POINTER(cache) = PyDict_New();
+ return cache;
+}
+
+SWIGRUNTIME swig_type_info *
+SWIG_Python_TypeQuery(const char *type)
+{
+ PyObject *cache = SWIG_Python_TypeCache();
+ PyObject *key = PyString_FromString(type);
+ PyObject *obj = PyDict_GetItem(cache, key);
+ swig_type_info *descriptor;
+ if (obj) {
+ descriptor = (swig_type_info *) PyCObject_AsVoidPtr(obj);
+ } else {
+ swig_module_info *swig_module = SWIG_Python_GetModule();
+ descriptor = SWIG_TypeQueryModule(swig_module, swig_module, type);
+ if (descriptor) {
+ obj = PyCObject_FromVoidPtr(descriptor, NULL);
+ PyDict_SetItem(cache, key, obj);
+ Py_DECREF(obj);
+ }
+ }
+ Py_DECREF(key);
+ return descriptor;
+}
+
+/*
+ For backward compatibility only
+*/
+#define SWIG_POINTER_EXCEPTION 0
+#define SWIG_arg_fail(arg) SWIG_Python_ArgFail(arg)
+#define SWIG_MustGetPtr(p, type, argnum, flags) SWIG_Python_MustGetPtr(p, type, argnum, flags)
+
+SWIGRUNTIME int
+SWIG_Python_AddErrMesg(const char* mesg, int infront)
+{
+ if (PyErr_Occurred()) {
+ PyObject *type = 0;
+ PyObject *value = 0;
+ PyObject *traceback = 0;
+ PyErr_Fetch(&type, &value, &traceback);
+ if (value) {
+ PyObject *old_str = PyObject_Str(value);
+ Py_XINCREF(type);
+ PyErr_Clear();
+ if (infront) {
+ PyErr_Format(type, "%s %s", mesg, PyString_AsString(old_str));
+ } else {
+ PyErr_Format(type, "%s %s", PyString_AsString(old_str), mesg);
+ }
+ Py_DECREF(old_str);
+ }
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+SWIGRUNTIME int
+SWIG_Python_ArgFail(int argnum)
+{
+ if (PyErr_Occurred()) {
+ /* add information about failing argument */
+ char mesg[256];
+ PyOS_snprintf(mesg, sizeof(mesg), "argument number %d:", argnum);
+ return SWIG_Python_AddErrMesg(mesg, 1);
+ } else {
+ return 0;
+ }
+}
+
+SWIGRUNTIMEINLINE const char *
+PySwigObject_GetDesc(PyObject *self)
+{
+ PySwigObject *v = (PySwigObject *)self;
+ swig_type_info *ty = v ? v->ty : 0;
+ return ty ? ty->str : (char*)"";
+}
+
+SWIGRUNTIME void
+SWIG_Python_TypeError(const char *type, PyObject *obj)
+{
+ if (type) {
+#if defined(SWIG_COBJECT_TYPES)
+ if (obj && PySwigObject_Check(obj)) {
+ const char *otype = (const char *) PySwigObject_GetDesc(obj);
+ if (otype) {
+ PyErr_Format(PyExc_TypeError, "a '%s' is expected, 'PySwigObject(%s)' is received",
+ type, otype);
+ return;
+ }
+ } else
+#endif
+ {
+ const char *otype = (obj ? obj->ob_type->tp_name : 0);
+ if (otype) {
+ PyObject *str = PyObject_Str(obj);
+ const char *cstr = str ? PyString_AsString(str) : 0;
+ if (cstr) {
+ PyErr_Format(PyExc_TypeError, "a '%s' is expected, '%s(%s)' is received",
+ type, otype, cstr);
+ } else {
+ PyErr_Format(PyExc_TypeError, "a '%s' is expected, '%s' is received",
+ type, otype);
+ }
+ Py_XDECREF(str);
+ return;
+ }
+ }
+ PyErr_Format(PyExc_TypeError, "a '%s' is expected", type);
+ } else {
+ PyErr_Format(PyExc_TypeError, "unexpected type is received");
+ }
+}
+
+
+/* Convert a pointer value, signal an exception on a type mismatch */
+SWIGRUNTIME void *
+SWIG_Python_MustGetPtr(PyObject *obj, swig_type_info *ty, int argnum, int flags) {
+ void *result;
+ if (SWIG_Python_ConvertPtr(obj, &result, ty, flags) == -1) {
+ PyErr_Clear();
+ if (flags & SWIG_POINTER_EXCEPTION) {
+ SWIG_Python_TypeError(SWIG_TypePrettyName(ty), obj);
+ SWIG_Python_ArgFail(argnum);
+ }
+ }
+ return result;
+}
+
+
+#ifdef __cplusplus
+#if 0
+{ /* cc-mode */
+#endif
+}
+#endif
+
+
+
+#define SWIG_exception_fail(code, msg) do { SWIG_Error(code, msg); SWIG_fail; } while(0)
+
+#define SWIG_contract_assert(expr, msg) if (!(expr)) { SWIG_Error(SWIG_RuntimeError, msg); SWIG_fail; } else
+
+
+
+void push_object(PyObject **stack, PyObject *o)
+{
+ if ((!*stack) || (*stack == Py_None)) {
+ *stack = o;
+ } else {
+ PyObject *o2, *o3;
+ if (!PyTuple_Check(*stack)) {
+ o2 = *stack;
+ *stack = PyTuple_New(1);
+ PyTuple_SetItem(*stack,0,o2);
+ }
+ o3 = PyTuple_New(1);
+ PyTuple_SetItem(o3,0,o);
+ o2 = *stack;
+ *stack = PySequence_Concat(o2,o3);
+ Py_DECREF(o2);
+ Py_DECREF(o3);
+ }
+}
+
+
+/* -------- TYPES TABLE (BEGIN) -------- */
+
+#define SWIGTYPE_p_BSTR swig_types[0]
+#define SWIGTYPE_p_IEnumWbemClassObject swig_types[1]
+#define SWIGTYPE_p_IUnknown swig_types[2]
+#define SWIGTYPE_p_IWbemContext swig_types[3]
+#define SWIGTYPE_p_IWbemServices swig_types[4]
+#define SWIGTYPE_p_TALLOC_CTX swig_types[5]
+#define SWIGTYPE_p_char swig_types[6]
+#define SWIGTYPE_p_com_context swig_types[7]
+#define SWIGTYPE_p_int swig_types[8]
+#define SWIGTYPE_p_long_long swig_types[9]
+#define SWIGTYPE_p_p_IEnumWbemClassObject swig_types[10]
+#define SWIGTYPE_p_p_IWbemServices swig_types[11]
+#define SWIGTYPE_p_p_WbemClassObject swig_types[12]
+#define SWIGTYPE_p_short swig_types[13]
+#define SWIGTYPE_p_signed_char swig_types[14]
+#define SWIGTYPE_p_unsigned_char swig_types[15]
+#define SWIGTYPE_p_unsigned_int swig_types[16]
+#define SWIGTYPE_p_unsigned_long_long swig_types[17]
+#define SWIGTYPE_p_unsigned_short swig_types[18]
+static swig_type_info *swig_types[20];
+static swig_module_info swig_module = {swig_types, 19, 0, 0, 0, 0};
+#define SWIG_TypeQuery(name) SWIG_TypeQueryModule(&swig_module, &swig_module, name)
+#define SWIG_MangledTypeQuery(name) SWIG_MangledTypeQueryModule(&swig_module, &swig_module, name)
+
+/* -------- TYPES TABLE (END) -------- */
+
+#if (PY_VERSION_HEX <= 0x02000000)
+# if !defined(SWIG_PYTHON_CLASSIC)
+# error "This python version requires swig to be run with the '-classic' option"
+# endif
+#endif
+#if (PY_VERSION_HEX <= 0x02020000)
+# error "This python version requires swig to be run with the '-nomodern' option"
+#endif
+#if (PY_VERSION_HEX <= 0x02020000)
+# error "This python version requires swig to be run with the '-nomodernargs' option"
+#endif
+#ifndef METH_O
+# error "This python version requires swig to be run with the '-nofastunpack' option"
+#endif
+#ifdef SWIG_TypeQuery
+# undef SWIG_TypeQuery
+#endif
+#define SWIG_TypeQuery SWIG_Python_TypeQuery
+
+/*-----------------------------------------------
+ @(target):= _wmi.so
+ ------------------------------------------------*/
+#define SWIG_init init_wmi
+
+#define SWIG_name "_wmi"
+
+#define SWIGVERSION 0x010336
+#define SWIG_VERSION SWIGVERSION
+
+
+#define SWIG_as_voidptr(a) (void *)((const void *)(a))
+#define SWIG_as_voidptrptr(a) ((void)SWIG_as_voidptr(*a),(void**)(a))
+
+
+#include "libcli/util/pyerrors.h"
+
+
+#include "includes.h"
+#include "librpc/gen_ndr/misc.h"
+#include "librpc/rpc/dcerpc.h"
+#include "lib/com/dcom/dcom.h"
+#include "librpc/gen_ndr/com_dcom.h"
+#include "lib/wmi/wmi.h"
+
+
+WERROR WBEM_ConnectServer(struct com_context *ctx, const char *server, const char *nspace, const char *user, const char *password,
+ const char *locale, uint32_t flags, const char *authority, struct IWbemContext* wbem_ctx, struct IWbemServices** services);
+WERROR IEnumWbemClassObject_SmartNext(struct IEnumWbemClassObject *d, TALLOC_CTX *mem_ctx, int32_t lTimeout,uint32_t uCount,
+ struct WbemClassObject **apObjects, uint32_t *puReturned);
+
+static PyObject *PyObject_FromCVAR(uint32_t cimtype, union CIMVAR *cvar);
+static PyObject *PySWbemObject_FromWbemClassObject(struct WbemClassObject *wco);
+
+static struct com_context *com_ctx;
+static PyObject *ComError;
+static PyObject *mod_win32_client;
+static PyObject *mod_pywintypes;
+
+typedef struct IUnknown IUnknown;
+typedef struct IWbemServices IWbemServices;
+typedef struct IWbemClassObject IWbemClassObject;
+typedef struct IEnumWbemClassObject IEnumWbemClassObject;
+
+
+SWIGINTERN swig_type_info*
+SWIG_pchar_descriptor(void)
+{
+ static int init = 0;
+ static swig_type_info* info = 0;
+ if (!init) {
+ info = SWIG_TypeQuery("_p_char");
+ init = 1;
+ }
+ return info;
+}
+
+
+SWIGINTERN int
+SWIG_AsCharPtrAndSize(PyObject *obj, char** cptr, size_t* psize, int *alloc)
+{
+ if (PyString_Check(obj)) {
+ char *cstr; Py_ssize_t len;
+ PyString_AsStringAndSize(obj, &cstr, &len);
+ if (cptr) {
+ if (alloc) {
+ /*
+ In python the user should not be able to modify the inner
+ string representation. To warranty that, if you define
+ SWIG_PYTHON_SAFE_CSTRINGS, a new/copy of the python string
+ buffer is always returned.
+
+ The default behavior is just to return the pointer value,
+ so, be careful.
+ */
+#if defined(SWIG_PYTHON_SAFE_CSTRINGS)
+ if (*alloc != SWIG_OLDOBJ)
+#else
+ if (*alloc == SWIG_NEWOBJ)
+#endif
+ {
+ *cptr = (char *)memcpy((char *)malloc((len + 1)*sizeof(char)), cstr, sizeof(char)*(len + 1));
+ *alloc = SWIG_NEWOBJ;
+ }
+ else {
+ *cptr = cstr;
+ *alloc = SWIG_OLDOBJ;
+ }
+ } else {
+ *cptr = PyString_AsString(obj);
+ }
+ }
+ if (psize) *psize = len + 1;
+ return SWIG_OK;
+ } else {
+ swig_type_info* pchar_descriptor = SWIG_pchar_descriptor();
+ if (pchar_descriptor) {
+ void* vptr = 0;
+ if (SWIG_ConvertPtr(obj, &vptr, pchar_descriptor, 0) == SWIG_OK) {
+ if (cptr) *cptr = (char *) vptr;
+ if (psize) *psize = vptr ? (strlen((char *)vptr) + 1) : 0;
+ if (alloc) *alloc = SWIG_OLDOBJ;
+ return SWIG_OK;
+ }
+ }
+ }
+ return SWIG_TypeError;
+}
+
+
+
+
+
+#include <limits.h>
+#if !defined(SWIG_NO_LLONG_MAX)
+# if !defined(LLONG_MAX) && defined(__GNUC__) && defined (__LONG_LONG_MAX__)
+# define LLONG_MAX __LONG_LONG_MAX__
+# define LLONG_MIN (-LLONG_MAX - 1LL)
+# define ULLONG_MAX (LLONG_MAX * 2ULL + 1ULL)
+# endif
+#endif
+
+
+SWIGINTERN int
+SWIG_AsVal_double (PyObject *obj, double *val)
+{
+ int res = SWIG_TypeError;
+ if (PyFloat_Check(obj)) {
+ if (val) *val = PyFloat_AsDouble(obj);
+ return SWIG_OK;
+ } else if (PyInt_Check(obj)) {
+ if (val) *val = PyInt_AsLong(obj);
+ return SWIG_OK;
+ } else if (PyLong_Check(obj)) {
+ double v = PyLong_AsDouble(obj);
+ if (!PyErr_Occurred()) {
+ if (val) *val = v;
+ return SWIG_OK;
+ } else {
+ PyErr_Clear();
+ }
+ }
+#ifdef SWIG_PYTHON_CAST_MODE
+ {
+ int dispatch = 0;
+ double d = PyFloat_AsDouble(obj);
+ if (!PyErr_Occurred()) {
+ if (val) *val = d;
+ return SWIG_AddCast(SWIG_OK);
+ } else {
+ PyErr_Clear();
+ }
+ if (!dispatch) {
+ long v = PyLong_AsLong(obj);
+ if (!PyErr_Occurred()) {
+ if (val) *val = v;
+ return SWIG_AddCast(SWIG_AddCast(SWIG_OK));
+ } else {
+ PyErr_Clear();
+ }
+ }
+ }
+#endif
+ return res;
+}
+
+
+#include <float.h>
+
+
+#include <math.h>
+
+
+SWIGINTERNINLINE int
+SWIG_CanCastAsInteger(double *d, double min, double max) {
+ double x = *d;
+ if ((min <= x && x <= max)) {
+ double fx = floor(x);
+ double cx = ceil(x);
+ double rd = ((x - fx) < 0.5) ? fx : cx; /* simple rint */
+ if ((errno == EDOM) || (errno == ERANGE)) {
+ errno = 0;
+ } else {
+ double summ, reps, diff;
+ if (rd < x) {
+ diff = x - rd;
+ } else if (rd > x) {
+ diff = rd - x;
+ } else {
+ return 1;
+ }
+ summ = rd + x;
+ reps = diff/summ;
+ if (reps < 8*DBL_EPSILON) {
+ *d = rd;
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+
+SWIGINTERN int
+SWIG_AsVal_unsigned_SS_long (PyObject *obj, unsigned long *val)
+{
+ if (PyInt_Check(obj)) {
+ long v = PyInt_AsLong(obj);
+ if (v >= 0) {
+ if (val) *val = v;
+ return SWIG_OK;
+ } else {
+ return SWIG_OverflowError;
+ }
+ } else if (PyLong_Check(obj)) {
+ unsigned long v = PyLong_AsUnsignedLong(obj);
+ if (!PyErr_Occurred()) {
+ if (val) *val = v;
+ return SWIG_OK;
+ } else {
+ PyErr_Clear();
+ }
+ }
+#ifdef SWIG_PYTHON_CAST_MODE
+ {
+ int dispatch = 0;
+ unsigned long v = PyLong_AsUnsignedLong(obj);
+ if (!PyErr_Occurred()) {
+ if (val) *val = v;
+ return SWIG_AddCast(SWIG_OK);
+ } else {
+ PyErr_Clear();
+ }
+ if (!dispatch) {
+ double d;
+ int res = SWIG_AddCast(SWIG_AsVal_double (obj,&d));
+ if (SWIG_IsOK(res) && SWIG_CanCastAsInteger(&d, 0, ULONG_MAX)) {
+ if (val) *val = (unsigned long)(d);
+ return res;
+ }
+ }
+ }
+#endif
+ return SWIG_TypeError;
+}
+
+
+SWIGINTERN int
+SWIG_AsVal_unsigned_SS_int (PyObject * obj, unsigned int *val)
+{
+ unsigned long v;
+ int res = SWIG_AsVal_unsigned_SS_long (obj, &v);
+ if (SWIG_IsOK(res)) {
+ if ((v > UINT_MAX)) {
+ return SWIG_OverflowError;
+ } else {
+ if (val) *val = (unsigned int)(v);
+ }
+ }
+ return res;
+}
+
+
+ #define SWIG_From_long PyInt_FromLong
+
+
+SWIGINTERNINLINE PyObject*
+SWIG_From_unsigned_SS_long (unsigned long value)
+{
+ return (value > LONG_MAX) ?
+ PyLong_FromUnsignedLong(value) : PyInt_FromLong((long)(value));
+}
+
+
+SWIGINTERNINLINE PyObject *
+SWIG_From_unsigned_SS_int (unsigned int value)
+{
+ return SWIG_From_unsigned_SS_long (value);
+}
+
+
+SWIGINTERN int
+SWIG_AsVal_long (PyObject *obj, long* val)
+{
+ if (PyInt_Check(obj)) {
+ if (val) *val = PyInt_AsLong(obj);
+ return SWIG_OK;
+ } else if (PyLong_Check(obj)) {
+ long v = PyLong_AsLong(obj);
+ if (!PyErr_Occurred()) {
+ if (val) *val = v;
+ return SWIG_OK;
+ } else {
+ PyErr_Clear();
+ }
+ }
+#ifdef SWIG_PYTHON_CAST_MODE
+ {
+ int dispatch = 0;
+ long v = PyInt_AsLong(obj);
+ if (!PyErr_Occurred()) {
+ if (val) *val = v;
+ return SWIG_AddCast(SWIG_OK);
+ } else {
+ PyErr_Clear();
+ }
+ if (!dispatch) {
+ double d;
+ int res = SWIG_AddCast(SWIG_AsVal_double (obj,&d));
+ if (SWIG_IsOK(res) && SWIG_CanCastAsInteger(&d, LONG_MIN, LONG_MAX)) {
+ if (val) *val = (long)(d);
+ return res;
+ }
+ }
+ }
+#endif
+ return SWIG_TypeError;
+}
+
+
+SWIGINTERN int
+SWIG_AsVal_int (PyObject * obj, int *val)
+{
+ long v;
+ int res = SWIG_AsVal_long (obj, &v);
+ if (SWIG_IsOK(res)) {
+ if ((v < INT_MIN || v > INT_MAX)) {
+ return SWIG_OverflowError;
+ } else {
+ if (val) *val = (int)(v);
+ }
+ }
+ return res;
+}
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define RETURN_CVAR_ARRAY(fmt, arr) {\
+ PyObject *l, *o;\
+ uint32_t i;\
+\
+ if (!arr) {\
+ Py_INCREF(Py_None);\
+ return Py_None;\
+ }\
+ l = PyList_New(arr->count);\
+ if (!l) return NULL;\
+ for (i = 0; i < arr->count; ++i) {\
+ o = _Py_BuildValue(fmt, arr->item[i]);\
+ if (!o) {\
+ Py_DECREF(l);\
+ return NULL;\
+ }\
+ PyList_SET_ITEM(l, i, o);\
+ }\
+ return l;\
+}
+
+static PyObject *_Py_BuildValue(char *str, ...)
+{
+ PyObject * result = NULL;
+ va_list lst;
+ va_start(lst, str);
+ if (str && *str == 'I') {
+ uint32_t value = va_arg(lst, uint32_t);
+ if (value & 0x80000000) {
+ result = Py_BuildValue("L", (long)value);
+ } else {
+ result = Py_BuildValue("i", value);
+ }
+ } else {
+ result = Py_VaBuildValue(str, lst);
+ }
+ va_end(lst);
+ return result;
+}
+
+
+static PyObject *PyObject_FromCVAR(uint32_t cimtype, union CIMVAR *cvar)
+{
+ switch (cimtype) {
+ case CIM_SINT8: return Py_BuildValue("b", cvar->v_sint8);
+ case CIM_UINT8: return Py_BuildValue("B", cvar->v_uint8);
+ case CIM_SINT16: return Py_BuildValue("h", cvar->v_sint16);
+ case CIM_UINT16: return Py_BuildValue("H", cvar->v_uint16);
+ case CIM_SINT32: return Py_BuildValue("i", cvar->v_sint32);
+ case CIM_UINT32: return _Py_BuildValue("I", cvar->v_uint32);
+ case CIM_SINT64: return Py_BuildValue("L", cvar->v_sint64);
+ case CIM_UINT64: return Py_BuildValue("K", cvar->v_uint64);
+ case CIM_REAL32: return Py_BuildValue("f", cvar->v_real32);
+ case CIM_REAL64: return Py_BuildValue("d", cvar->v_real64);
+ case CIM_BOOLEAN: return Py_BuildValue("h", cvar->v_boolean);
+ case CIM_STRING: return Py_BuildValue("s", cvar->v_string);
+ case CIM_DATETIME: return Py_BuildValue("s", cvar->v_datetime);
+ case CIM_REFERENCE: return Py_BuildValue("s", cvar->v_reference);
+ case CIM_OBJECT: return PySWbemObject_FromWbemClassObject(cvar->v_object);
+ case CIM_ARR_SINT8: RETURN_CVAR_ARRAY("b", cvar->a_sint8);
+ case CIM_ARR_UINT8: RETURN_CVAR_ARRAY("B", cvar->a_uint8);
+ case CIM_ARR_SINT16: RETURN_CVAR_ARRAY("h", cvar->a_sint16);
+ case CIM_ARR_UINT16: RETURN_CVAR_ARRAY("H", cvar->a_uint16);
+ case CIM_ARR_SINT32: RETURN_CVAR_ARRAY("i", cvar->a_sint32);
+ case CIM_ARR_UINT32: RETURN_CVAR_ARRAY("I", cvar->a_uint32);
+ case CIM_ARR_SINT64: RETURN_CVAR_ARRAY("L", cvar->a_sint64);
+ case CIM_ARR_UINT64: RETURN_CVAR_ARRAY("K", cvar->a_uint64);
+ case CIM_ARR_REAL32: RETURN_CVAR_ARRAY("f", cvar->a_real32);
+ case CIM_ARR_REAL64: RETURN_CVAR_ARRAY("d", cvar->a_real64);
+ case CIM_ARR_BOOLEAN: RETURN_CVAR_ARRAY("h", cvar->a_boolean);
+ case CIM_ARR_STRING: RETURN_CVAR_ARRAY("s", cvar->a_string);
+ case CIM_ARR_DATETIME: RETURN_CVAR_ARRAY("s", cvar->a_datetime);
+ case CIM_ARR_REFERENCE: RETURN_CVAR_ARRAY("s", cvar->a_reference);
+ default:
+ {
+ char *str;
+ str = talloc_asprintf(NULL, "Unsupported CIMTYPE(0x%04X)", cimtype);
+ PyErr_SetString(PyExc_RuntimeError, str);
+ talloc_free(str);
+ return NULL;
+ }
+ }
+}
+
+#undef RETURN_CVAR_ARRAY
+
+PyObject *PySWbemObject_InitProperites(PyObject *o, struct WbemClassObject *wco)
+{
+ PyObject *properties;
+ PyObject *addProp;
+ uint32_t i;
+ int32_t r;
+ PyObject *result;
+
+ result = NULL;
+ properties = PyObject_GetAttrString(o, "Properties_");
+ if (!properties) return NULL;
+ addProp = PyObject_GetAttrString(properties, "Add");
+ if (!addProp) {
+ Py_DECREF(properties);
+ return NULL;
+ }
+
+ for (i = 0; i < wco->obj_class->__PROPERTY_COUNT; ++i) {
+ PyObject *args, *property;
+
+ args = Py_BuildValue("(si)", wco->obj_class->properties[i].property.name, wco->obj_class->properties[i].property.desc->cimtype & CIM_TYPEMASK);
+ if (!args) goto finish;
+ property = PyObject_CallObject(addProp, args);
+ Py_DECREF(args);
+ if (!property) goto finish;
+ if (wco->flags & WCF_INSTANCE) {
+ PyObject *value;
+
+ if (wco->instance->default_flags[i] & 1) {
+ value = Py_None;
+ Py_INCREF(Py_None);
+ } else
+ value = PyObject_FromCVAR(wco->obj_class->properties[i].property.desc->cimtype & CIM_TYPEMASK, &wco->instance->data[i]);
+ if (!value) {
+ Py_DECREF(property);
+ goto finish;
+ }
+ r = PyObject_SetAttrString(property, "Value", value);
+ Py_DECREF(value);
+ if (r == -1) {
+ PyErr_SetString(PyExc_RuntimeError, "Error setting value of property");
+ goto finish;
+ }
+ }
+ Py_DECREF(property);
+ }
+
+ Py_INCREF(Py_None);
+ result = Py_None;
+finish:
+ Py_DECREF(addProp);
+ Py_DECREF(properties);
+ return result;
+}
+
+static PyObject *PySWbemObject_FromWbemClassObject(struct WbemClassObject *wco)
+{
+ PyObject *swo_class, *swo, *args, *result;
+
+ swo_class = PyObject_GetAttrString(mod_win32_client, "SWbemObject");
+ if (!swo_class) return NULL;
+ args = PyTuple_New(0);
+ if (!args) {
+ Py_DECREF(swo_class);
+ return NULL;
+ }
+ swo = PyObject_CallObject(swo_class, args);
+ Py_DECREF(args);
+ Py_DECREF(swo_class);
+ if (!swo) return NULL;
+
+ result = PySWbemObject_InitProperites(swo, wco);
+ if (!result) {
+ Py_DECREF(swo);
+ return NULL;
+ }
+ Py_DECREF(result);
+
+ return swo;
+}
+
+
+SWIGINTERN PyObject *_wrap_WBEM_ConnectServer(PyObject *SWIGUNUSEDPARM(self), PyObject *args, PyObject *kwargs) {
+ PyObject *resultobj = 0;
+ struct com_context *arg1 = (struct com_context *) 0 ;
+ char *arg2 = (char *) 0 ;
+ char *arg3 = (char *) 0 ;
+ char *arg4 = (char *) 0 ;
+ char *arg5 = (char *) 0 ;
+ char *arg6 = (char *) 0 ;
+ uint32_t arg7 ;
+ char *arg8 = (char *) 0 ;
+ struct IWbemContext *arg9 = (struct IWbemContext *) 0 ;
+ struct IWbemServices **arg10 = (struct IWbemServices **) 0 ;
+ int res2 ;
+ char *buf2 = 0 ;
+ int alloc2 = 0 ;
+ int res3 ;
+ char *buf3 = 0 ;
+ int alloc3 = 0 ;
+ int res4 ;
+ char *buf4 = 0 ;
+ int alloc4 = 0 ;
+ int res5 ;
+ char *buf5 = 0 ;
+ int alloc5 = 0 ;
+ int res6 ;
+ char *buf6 = 0 ;
+ int alloc6 = 0 ;
+ unsigned int val7 ;
+ int ecode7 = 0 ;
+ int res8 ;
+ char *buf8 = 0 ;
+ int alloc8 = 0 ;
+ void *argp9 = 0 ;
+ int res9 = 0 ;
+ struct IWbemServices *temp10 ;
+ PyObject * obj0 = 0 ;
+ PyObject * obj1 = 0 ;
+ PyObject * obj2 = 0 ;
+ PyObject * obj3 = 0 ;
+ PyObject * obj4 = 0 ;
+ PyObject * obj5 = 0 ;
+ PyObject * obj6 = 0 ;
+ PyObject * obj7 = 0 ;
+ char * kwnames[] = {
+ (char *) "server",(char *) "nspace",(char *) "user",(char *) "password",(char *) "locale",(char *) "flags",(char *) "authority",(char *) "wbem_ctx", NULL
+ };
+ WERROR result;
+
+ {
+ arg1 = com_ctx;
+ }
+ {
+ arg10 = &temp10;
+ }
+ if (!PyArg_ParseTupleAndKeywords(args,kwargs,(char *)"OOOOOOOO:WBEM_ConnectServer",kwnames,&obj0,&obj1,&obj2,&obj3,&obj4,&obj5,&obj6,&obj7)) SWIG_fail;
+ res2 = SWIG_AsCharPtrAndSize(obj0, &buf2, NULL, &alloc2);
+ if (!SWIG_IsOK(res2)) {
+ SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "WBEM_ConnectServer" "', argument " "2"" of type '" "char const *""'");
+ }
+ arg2 = (char *)(buf2);
+ res3 = SWIG_AsCharPtrAndSize(obj1, &buf3, NULL, &alloc3);
+ if (!SWIG_IsOK(res3)) {
+ SWIG_exception_fail(SWIG_ArgError(res3), "in method '" "WBEM_ConnectServer" "', argument " "3"" of type '" "char const *""'");
+ }
+ arg3 = (char *)(buf3);
+ res4 = SWIG_AsCharPtrAndSize(obj2, &buf4, NULL, &alloc4);
+ if (!SWIG_IsOK(res4)) {
+ SWIG_exception_fail(SWIG_ArgError(res4), "in method '" "WBEM_ConnectServer" "', argument " "4"" of type '" "char const *""'");
+ }
+ arg4 = (char *)(buf4);
+ res5 = SWIG_AsCharPtrAndSize(obj3, &buf5, NULL, &alloc5);
+ if (!SWIG_IsOK(res5)) {
+ SWIG_exception_fail(SWIG_ArgError(res5), "in method '" "WBEM_ConnectServer" "', argument " "5"" of type '" "char const *""'");
+ }
+ arg5 = (char *)(buf5);
+ res6 = SWIG_AsCharPtrAndSize(obj4, &buf6, NULL, &alloc6);
+ if (!SWIG_IsOK(res6)) {
+ SWIG_exception_fail(SWIG_ArgError(res6), "in method '" "WBEM_ConnectServer" "', argument " "6"" of type '" "char const *""'");
+ }
+ arg6 = (char *)(buf6);
+ ecode7 = SWIG_AsVal_unsigned_SS_int(obj5, &val7);
+ if (!SWIG_IsOK(ecode7)) {
+ SWIG_exception_fail(SWIG_ArgError(ecode7), "in method '" "WBEM_ConnectServer" "', argument " "7"" of type '" "uint32_t""'");
+ }
+ arg7 = (uint32_t)(val7);
+ res8 = SWIG_AsCharPtrAndSize(obj6, &buf8, NULL, &alloc8);
+ if (!SWIG_IsOK(res8)) {
+ SWIG_exception_fail(SWIG_ArgError(res8), "in method '" "WBEM_ConnectServer" "', argument " "8"" of type '" "char const *""'");
+ }
+ arg8 = (char *)(buf8);
+ res9 = SWIG_ConvertPtr(obj7, &argp9,SWIGTYPE_p_IWbemContext, 0 | 0 );
+ if (!SWIG_IsOK(res9)) {
+ SWIG_exception_fail(SWIG_ArgError(res9), "in method '" "WBEM_ConnectServer" "', argument " "9"" of type '" "struct IWbemContext *""'");
+ }
+ arg9 = (struct IWbemContext *)(argp9);
+ result = WBEM_ConnectServer(arg1,(char const *)arg2,(char const *)arg3,(char const *)arg4,(char const *)arg5,(char const *)arg6,arg7,(char const *)arg8,arg9,arg10);
+ if (!W_ERROR_IS_OK(result)) {
+ PyErr_SetWERROR(result);
+ SWIG_fail;
+ } else if (resultobj == NULL) {
+ resultobj = Py_None;
+ }
+ {
+ PyObject *o;
+ o = SWIG_NewPointerObj(*arg10, SWIGTYPE_p_IWbemServices, 0);
+ push_object(&resultobj, o);
+ }
+ if (alloc2 == SWIG_NEWOBJ) free((char*)buf2);
+ if (alloc3 == SWIG_NEWOBJ) free((char*)buf3);
+ if (alloc4 == SWIG_NEWOBJ) free((char*)buf4);
+ if (alloc5 == SWIG_NEWOBJ) free((char*)buf5);
+ if (alloc6 == SWIG_NEWOBJ) free((char*)buf6);
+ if (alloc8 == SWIG_NEWOBJ) free((char*)buf8);
+ return resultobj;
+fail:
+ if (alloc2 == SWIG_NEWOBJ) free((char*)buf2);
+ if (alloc3 == SWIG_NEWOBJ) free((char*)buf3);
+ if (alloc4 == SWIG_NEWOBJ) free((char*)buf4);
+ if (alloc5 == SWIG_NEWOBJ) free((char*)buf5);
+ if (alloc6 == SWIG_NEWOBJ) free((char*)buf6);
+ if (alloc8 == SWIG_NEWOBJ) free((char*)buf8);
+ return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_IUnknown_Release(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+ PyObject *resultobj = 0;
+ IUnknown *arg1 = (IUnknown *) 0 ;
+ TALLOC_CTX *arg2 = (TALLOC_CTX *) 0 ;
+ void *argp1 = 0 ;
+ int res1 = 0 ;
+ PyObject *swig_obj[1] ;
+ uint32_t result;
+
+ arg2 = NULL;
+ if (!args) SWIG_fail;
+ swig_obj[0] = args;
+ res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_IUnknown, 0 | 0 );
+ if (!SWIG_IsOK(res1)) {
+ SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "IUnknown_Release" "', argument " "1"" of type '" "IUnknown *""'");
+ }
+ arg1 = (IUnknown *)(argp1);
+ result = (uint32_t)IUnknown_Release(arg1,arg2);
+ resultobj = SWIG_From_unsigned_SS_int((unsigned int)(result));
+ return resultobj;
+fail:
+ return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_new_IUnknown(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+ PyObject *resultobj = 0;
+ IUnknown *result = 0 ;
+
+ if (!SWIG_Python_UnpackTuple(args,"new_IUnknown",0,0,0)) SWIG_fail;
+ result = (IUnknown *)calloc(1, sizeof(IUnknown));
+ resultobj = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_IUnknown, SWIG_POINTER_NEW | 0 );
+ return resultobj;
+fail:
+ return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_delete_IUnknown(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+ PyObject *resultobj = 0;
+ IUnknown *arg1 = (IUnknown *) 0 ;
+ void *argp1 = 0 ;
+ int res1 = 0 ;
+ PyObject *swig_obj[1] ;
+
+ if (!args) SWIG_fail;
+ swig_obj[0] = args;
+ res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_IUnknown, SWIG_POINTER_DISOWN | 0 );
+ if (!SWIG_IsOK(res1)) {
+ SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "delete_IUnknown" "', argument " "1"" of type '" "IUnknown *""'");
+ }
+ arg1 = (IUnknown *)(argp1);
+ free((char *) arg1);
+ resultobj = SWIG_Py_Void();
+ return resultobj;
+fail:
+ return NULL;
+}
+
+
+SWIGINTERN PyObject *IUnknown_swigregister(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+ PyObject *obj;
+ if (!SWIG_Python_UnpackTuple(args,(char*)"swigregister", 1, 1,&obj)) return NULL;
+ SWIG_TypeNewClientData(SWIGTYPE_p_IUnknown, SWIG_NewClientData(obj));
+ return SWIG_Py_Void();
+}
+
+SWIGINTERN PyObject *IUnknown_swiginit(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+ return SWIG_Python_InitShadowInstance(args);
+}
+
+SWIGINTERN PyObject *_wrap_IWbemServices_ExecQuery(PyObject *SWIGUNUSEDPARM(self), PyObject *args, PyObject *kwargs) {
+ PyObject *resultobj = 0;
+ IWbemServices *arg1 = (IWbemServices *) 0 ;
+ TALLOC_CTX *arg2 = (TALLOC_CTX *) 0 ;
+ struct BSTR arg3 ;
+ struct BSTR arg4 ;
+ int32_t arg5 ;
+ struct IWbemContext *arg6 = (struct IWbemContext *) 0 ;
+ struct IEnumWbemClassObject **arg7 = (struct IEnumWbemClassObject **) 0 ;
+ void *argp1 = 0 ;
+ int res1 = 0 ;
+ int val5 ;
+ int ecode5 = 0 ;
+ void *argp6 = 0 ;
+ int res6 = 0 ;
+ struct IEnumWbemClassObject *temp7 ;
+ PyObject * obj0 = 0 ;
+ PyObject * obj1 = 0 ;
+ PyObject * obj2 = 0 ;
+ PyObject * obj3 = 0 ;
+ PyObject * obj4 = 0 ;
+ char * kwnames[] = {
+ (char *) "self",(char *) "strQueryLanguage",(char *) "strQuery",(char *) "lFlags",(char *) "pCtx", NULL
+ };
+ WERROR result;
+
+ arg2 = NULL;
+ {
+ arg7 = &temp7;
+ }
+ if (!PyArg_ParseTupleAndKeywords(args,kwargs,(char *)"OOOOO:IWbemServices_ExecQuery",kwnames,&obj0,&obj1,&obj2,&obj3,&obj4)) SWIG_fail;
+ res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_IWbemServices, 0 | 0 );
+ if (!SWIG_IsOK(res1)) {
+ SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "IWbemServices_ExecQuery" "', argument " "1"" of type '" "IWbemServices *""'");
+ }
+ arg1 = (IWbemServices *)(argp1);
+ {
+ (&arg3)->data = PyString_AsString(obj1);
+ }
+ {
+ (&arg4)->data = PyString_AsString(obj2);
+ }
+ ecode5 = SWIG_AsVal_int(obj3, &val5);
+ if (!SWIG_IsOK(ecode5)) {
+ SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "IWbemServices_ExecQuery" "', argument " "5"" of type '" "int32_t""'");
+ }
+ arg5 = (int32_t)(val5);
+ res6 = SWIG_ConvertPtr(obj4, &argp6,SWIGTYPE_p_IWbemContext, 0 | 0 );
+ if (!SWIG_IsOK(res6)) {
+ SWIG_exception_fail(SWIG_ArgError(res6), "in method '" "IWbemServices_ExecQuery" "', argument " "6"" of type '" "struct IWbemContext *""'");
+ }
+ arg6 = (struct IWbemContext *)(argp6);
+ result = IWbemServices_ExecQuery(arg1,arg2,arg3,arg4,arg5,arg6,arg7);
+ if (!W_ERROR_IS_OK(result)) {
+ PyErr_SetWERROR(result);
+ SWIG_fail;
+ } else if (resultobj == NULL) {
+ resultobj = Py_None;
+ }
+ {
+ PyObject *o;
+ o = SWIG_NewPointerObj(*arg7, SWIGTYPE_p_IEnumWbemClassObject, 0);
+ push_object(&resultobj, o);
+ }
+ return resultobj;
+fail:
+ return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_IWbemServices_ExecNotificationQuery(PyObject *SWIGUNUSEDPARM(self), PyObject *args, PyObject *kwargs) {
+ PyObject *resultobj = 0;
+ IWbemServices *arg1 = (IWbemServices *) 0 ;
+ TALLOC_CTX *arg2 = (TALLOC_CTX *) 0 ;
+ struct BSTR arg3 ;
+ struct BSTR arg4 ;
+ int32_t arg5 ;
+ struct IWbemContext *arg6 = (struct IWbemContext *) 0 ;
+ struct IEnumWbemClassObject **arg7 = (struct IEnumWbemClassObject **) 0 ;
+ void *argp1 = 0 ;
+ int res1 = 0 ;
+ int val5 ;
+ int ecode5 = 0 ;
+ void *argp6 = 0 ;
+ int res6 = 0 ;
+ struct IEnumWbemClassObject *temp7 ;
+ PyObject * obj0 = 0 ;
+ PyObject * obj1 = 0 ;
+ PyObject * obj2 = 0 ;
+ PyObject * obj3 = 0 ;
+ PyObject * obj4 = 0 ;
+ char * kwnames[] = {
+ (char *) "self",(char *) "strQueryLanguage",(char *) "strQuery",(char *) "lFlags",(char *) "pCtx", NULL
+ };
+ WERROR result;
+
+ arg2 = NULL;
+ {
+ arg7 = &temp7;
+ }
+ if (!PyArg_ParseTupleAndKeywords(args,kwargs,(char *)"OOOOO:IWbemServices_ExecNotificationQuery",kwnames,&obj0,&obj1,&obj2,&obj3,&obj4)) SWIG_fail;
+ res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_IWbemServices, 0 | 0 );
+ if (!SWIG_IsOK(res1)) {
+ SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "IWbemServices_ExecNotificationQuery" "', argument " "1"" of type '" "IWbemServices *""'");
+ }
+ arg1 = (IWbemServices *)(argp1);
+ {
+ (&arg3)->data = PyString_AsString(obj1);
+ }
+ {
+ (&arg4)->data = PyString_AsString(obj2);
+ }
+ ecode5 = SWIG_AsVal_int(obj3, &val5);
+ if (!SWIG_IsOK(ecode5)) {
+ SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "IWbemServices_ExecNotificationQuery" "', argument " "5"" of type '" "int32_t""'");
+ }
+ arg5 = (int32_t)(val5);
+ res6 = SWIG_ConvertPtr(obj4, &argp6,SWIGTYPE_p_IWbemContext, 0 | 0 );
+ if (!SWIG_IsOK(res6)) {
+ SWIG_exception_fail(SWIG_ArgError(res6), "in method '" "IWbemServices_ExecNotificationQuery" "', argument " "6"" of type '" "struct IWbemContext *""'");
+ }
+ arg6 = (struct IWbemContext *)(argp6);
+ result = IWbemServices_ExecNotificationQuery(arg1,arg2,arg3,arg4,arg5,arg6,arg7);
+ if (!W_ERROR_IS_OK(result)) {
+ PyErr_SetWERROR(result);
+ SWIG_fail;
+ } else if (resultobj == NULL) {
+ resultobj = Py_None;
+ }
+ {
+ PyObject *o;
+ o = SWIG_NewPointerObj(*arg7, SWIGTYPE_p_IEnumWbemClassObject, 0);
+ push_object(&resultobj, o);
+ }
+ return resultobj;
+fail:
+ return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_IWbemServices_CreateInstanceEnum(PyObject *SWIGUNUSEDPARM(self), PyObject *args, PyObject *kwargs) {
+ PyObject *resultobj = 0;
+ IWbemServices *arg1 = (IWbemServices *) 0 ;
+ TALLOC_CTX *arg2 = (TALLOC_CTX *) 0 ;
+ struct BSTR arg3 ;
+ int32_t arg4 ;
+ struct IWbemContext *arg5 = (struct IWbemContext *) 0 ;
+ struct IEnumWbemClassObject **arg6 = (struct IEnumWbemClassObject **) 0 ;
+ void *argp1 = 0 ;
+ int res1 = 0 ;
+ int val4 ;
+ int ecode4 = 0 ;
+ void *argp5 = 0 ;
+ int res5 = 0 ;
+ struct IEnumWbemClassObject *temp6 ;
+ PyObject * obj0 = 0 ;
+ PyObject * obj1 = 0 ;
+ PyObject * obj2 = 0 ;
+ PyObject * obj3 = 0 ;
+ char * kwnames[] = {
+ (char *) "self",(char *) "strClass",(char *) "lFlags",(char *) "pCtx", NULL
+ };
+ WERROR result;
+
+ arg2 = NULL;
+ {
+ arg6 = &temp6;
+ }
+ if (!PyArg_ParseTupleAndKeywords(args,kwargs,(char *)"OOOO:IWbemServices_CreateInstanceEnum",kwnames,&obj0,&obj1,&obj2,&obj3)) SWIG_fail;
+ res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_IWbemServices, 0 | 0 );
+ if (!SWIG_IsOK(res1)) {
+ SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "IWbemServices_CreateInstanceEnum" "', argument " "1"" of type '" "IWbemServices *""'");
+ }
+ arg1 = (IWbemServices *)(argp1);
+ {
+ (&arg3)->data = PyString_AsString(obj1);
+ }
+ ecode4 = SWIG_AsVal_int(obj2, &val4);
+ if (!SWIG_IsOK(ecode4)) {
+ SWIG_exception_fail(SWIG_ArgError(ecode4), "in method '" "IWbemServices_CreateInstanceEnum" "', argument " "4"" of type '" "int32_t""'");
+ }
+ arg4 = (int32_t)(val4);
+ res5 = SWIG_ConvertPtr(obj3, &argp5,SWIGTYPE_p_IWbemContext, 0 | 0 );
+ if (!SWIG_IsOK(res5)) {
+ SWIG_exception_fail(SWIG_ArgError(res5), "in method '" "IWbemServices_CreateInstanceEnum" "', argument " "5"" of type '" "struct IWbemContext *""'");
+ }
+ arg5 = (struct IWbemContext *)(argp5);
+ result = IWbemServices_CreateInstanceEnum(arg1,arg2,arg3,arg4,arg5,arg6);
+ if (!W_ERROR_IS_OK(result)) {
+ PyErr_SetWERROR(result);
+ SWIG_fail;
+ } else if (resultobj == NULL) {
+ resultobj = Py_None;
+ }
+ {
+ PyObject *o;
+ o = SWIG_NewPointerObj(*arg6, SWIGTYPE_p_IEnumWbemClassObject, 0);
+ push_object(&resultobj, o);
+ }
+ return resultobj;
+fail:
+ return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_new_IWbemServices(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+ PyObject *resultobj = 0;
+ IWbemServices *result = 0 ;
+
+ if (!SWIG_Python_UnpackTuple(args,"new_IWbemServices",0,0,0)) SWIG_fail;
+ result = (IWbemServices *)calloc(1, sizeof(IWbemServices));
+ resultobj = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_IWbemServices, SWIG_POINTER_NEW | 0 );
+ return resultobj;
+fail:
+ return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_delete_IWbemServices(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+ PyObject *resultobj = 0;
+ IWbemServices *arg1 = (IWbemServices *) 0 ;
+ void *argp1 = 0 ;
+ int res1 = 0 ;
+ PyObject *swig_obj[1] ;
+
+ if (!args) SWIG_fail;
+ swig_obj[0] = args;
+ res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_IWbemServices, SWIG_POINTER_DISOWN | 0 );
+ if (!SWIG_IsOK(res1)) {
+ SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "delete_IWbemServices" "', argument " "1"" of type '" "IWbemServices *""'");
+ }
+ arg1 = (IWbemServices *)(argp1);
+ free((char *) arg1);
+ resultobj = SWIG_Py_Void();
+ return resultobj;
+fail:
+ return NULL;
+}
+
+
+SWIGINTERN PyObject *IWbemServices_swigregister(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+ PyObject *obj;
+ if (!SWIG_Python_UnpackTuple(args,(char*)"swigregister", 1, 1,&obj)) return NULL;
+ SWIG_TypeNewClientData(SWIGTYPE_p_IWbemServices, SWIG_NewClientData(obj));
+ return SWIG_Py_Void();
+}
+
+SWIGINTERN PyObject *IWbemServices_swiginit(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+ return SWIG_Python_InitShadowInstance(args);
+}
+
+SWIGINTERN PyObject *_wrap_IEnumWbemClassObject_Reset(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+ PyObject *resultobj = 0;
+ IEnumWbemClassObject *arg1 = (IEnumWbemClassObject *) 0 ;
+ TALLOC_CTX *arg2 = (TALLOC_CTX *) 0 ;
+ void *argp1 = 0 ;
+ int res1 = 0 ;
+ PyObject *swig_obj[1] ;
+ WERROR result;
+
+ arg2 = NULL;
+ if (!args) SWIG_fail;
+ swig_obj[0] = args;
+ res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_IEnumWbemClassObject, 0 | 0 );
+ if (!SWIG_IsOK(res1)) {
+ SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "IEnumWbemClassObject_Reset" "', argument " "1"" of type '" "IEnumWbemClassObject *""'");
+ }
+ arg1 = (IEnumWbemClassObject *)(argp1);
+ result = IEnumWbemClassObject_Reset(arg1,arg2);
+ if (!W_ERROR_IS_OK(result)) {
+ PyErr_SetWERROR(result);
+ SWIG_fail;
+ } else if (resultobj == NULL) {
+ resultobj = Py_None;
+ }
+ return resultobj;
+fail:
+ return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_new_IEnumWbemClassObject(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+ PyObject *resultobj = 0;
+ IEnumWbemClassObject *result = 0 ;
+
+ if (!SWIG_Python_UnpackTuple(args,"new_IEnumWbemClassObject",0,0,0)) SWIG_fail;
+ result = (IEnumWbemClassObject *)calloc(1, sizeof(IEnumWbemClassObject));
+ resultobj = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_IEnumWbemClassObject, SWIG_POINTER_NEW | 0 );
+ return resultobj;
+fail:
+ return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_delete_IEnumWbemClassObject(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+ PyObject *resultobj = 0;
+ IEnumWbemClassObject *arg1 = (IEnumWbemClassObject *) 0 ;
+ void *argp1 = 0 ;
+ int res1 = 0 ;
+ PyObject *swig_obj[1] ;
+
+ if (!args) SWIG_fail;
+ swig_obj[0] = args;
+ res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_IEnumWbemClassObject, SWIG_POINTER_DISOWN | 0 );
+ if (!SWIG_IsOK(res1)) {
+ SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "delete_IEnumWbemClassObject" "', argument " "1"" of type '" "IEnumWbemClassObject *""'");
+ }
+ arg1 = (IEnumWbemClassObject *)(argp1);
+ free((char *) arg1);
+ resultobj = SWIG_Py_Void();
+ return resultobj;
+fail:
+ return NULL;
+}
+
+
+SWIGINTERN PyObject *IEnumWbemClassObject_swigregister(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+ PyObject *obj;
+ if (!SWIG_Python_UnpackTuple(args,(char*)"swigregister", 1, 1,&obj)) return NULL;
+ SWIG_TypeNewClientData(SWIGTYPE_p_IEnumWbemClassObject, SWIG_NewClientData(obj));
+ return SWIG_Py_Void();
+}
+
+SWIGINTERN PyObject *IEnumWbemClassObject_swiginit(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+ return SWIG_Python_InitShadowInstance(args);
+}
+
+SWIGINTERN PyObject *_wrap_IEnumWbemClassObject_SmartNext(PyObject *SWIGUNUSEDPARM(self), PyObject *args, PyObject *kwargs) {
+ PyObject *resultobj = 0;
+ struct IEnumWbemClassObject *arg1 = (struct IEnumWbemClassObject *) 0 ;
+ TALLOC_CTX *arg2 = (TALLOC_CTX *) 0 ;
+ int32_t arg3 ;
+ uint32_t arg4 ;
+ struct WbemClassObject **arg5 = (struct WbemClassObject **) 0 ;
+ uint32_t *arg6 = (uint32_t *) 0 ;
+ void *argp1 = 0 ;
+ int res1 = 0 ;
+ int val3 ;
+ int ecode3 = 0 ;
+ uint32_t uReturned4 ;
+ PyObject * obj0 = 0 ;
+ PyObject * obj1 = 0 ;
+ PyObject * obj2 = 0 ;
+ char * kwnames[] = {
+ (char *) "d",(char *) "lTimeout",(char *) "uCount", NULL
+ };
+ WERROR result;
+
+ arg2 = NULL;
+ if (!PyArg_ParseTupleAndKeywords(args,kwargs,(char *)"OOO:IEnumWbemClassObject_SmartNext",kwnames,&obj0,&obj1,&obj2)) SWIG_fail;
+ res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_IEnumWbemClassObject, 0 | 0 );
+ if (!SWIG_IsOK(res1)) {
+ SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "IEnumWbemClassObject_SmartNext" "', argument " "1"" of type '" "struct IEnumWbemClassObject *""'");
+ }
+ arg1 = (struct IEnumWbemClassObject *)(argp1);
+ ecode3 = SWIG_AsVal_int(obj1, &val3);
+ if (!SWIG_IsOK(ecode3)) {
+ SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "IEnumWbemClassObject_SmartNext" "', argument " "3"" of type '" "int32_t""'");
+ }
+ arg3 = (int32_t)(val3);
+ {
+ if (PyLong_Check(obj2))
+ arg4 = PyLong_AsUnsignedLong(obj2);
+ else if (PyInt_Check(obj2))
+ arg4 = PyInt_AsLong(obj2);
+ else {
+ PyErr_SetString(PyExc_TypeError,"Expected a long or an int");
+ return NULL;
+ }
+ arg5 = talloc_array(NULL, struct WbemClassObject *, arg4);
+ arg6 = &uReturned4;
+ }
+ result = IEnumWbemClassObject_SmartNext(arg1,arg2,arg3,arg4,arg5,arg6);
+ if (!W_ERROR_IS_OK(result)) {
+ PyErr_SetWERROR(result);
+ SWIG_fail;
+ } else if (resultobj == NULL) {
+ resultobj = Py_None;
+ }
+ {
+ uint32_t i;
+ PyObject *o;
+ int32_t error;
+
+ error = 0;
+
+ resultobj = PyTuple_New(*arg6);
+ for (i = 0; i < *arg6; ++i) {
+ if (!error) {
+ o = PySWbemObject_FromWbemClassObject(arg5[i]);
+ if (!o)
+ --error;
+ else
+ error = PyTuple_SetItem(resultobj, i, o);
+ }
+ talloc_free(arg5[i]);
+ }
+ talloc_free(arg5);
+ if (error) return NULL;
+ }
+ return resultobj;
+fail:
+ return NULL;
+}
+
+
+static PyMethodDef SwigMethods[] = {
+ { (char *)"WBEM_ConnectServer", (PyCFunction) _wrap_WBEM_ConnectServer, METH_VARARGS | METH_KEYWORDS, NULL},
+ { (char *)"IUnknown_Release", (PyCFunction)_wrap_IUnknown_Release, METH_O, NULL},
+ { (char *)"new_IUnknown", (PyCFunction)_wrap_new_IUnknown, METH_NOARGS, NULL},
+ { (char *)"delete_IUnknown", (PyCFunction)_wrap_delete_IUnknown, METH_O, NULL},
+ { (char *)"IUnknown_swigregister", IUnknown_swigregister, METH_VARARGS, NULL},
+ { (char *)"IUnknown_swiginit", IUnknown_swiginit, METH_VARARGS, NULL},
+ { (char *)"IWbemServices_ExecQuery", (PyCFunction) _wrap_IWbemServices_ExecQuery, METH_VARARGS | METH_KEYWORDS, NULL},
+ { (char *)"IWbemServices_ExecNotificationQuery", (PyCFunction) _wrap_IWbemServices_ExecNotificationQuery, METH_VARARGS | METH_KEYWORDS, NULL},
+ { (char *)"IWbemServices_CreateInstanceEnum", (PyCFunction) _wrap_IWbemServices_CreateInstanceEnum, METH_VARARGS | METH_KEYWORDS, NULL},
+ { (char *)"new_IWbemServices", (PyCFunction)_wrap_new_IWbemServices, METH_NOARGS, NULL},
+ { (char *)"delete_IWbemServices", (PyCFunction)_wrap_delete_IWbemServices, METH_O, NULL},
+ { (char *)"IWbemServices_swigregister", IWbemServices_swigregister, METH_VARARGS, NULL},
+ { (char *)"IWbemServices_swiginit", IWbemServices_swiginit, METH_VARARGS, NULL},
+ { (char *)"IEnumWbemClassObject_Reset", (PyCFunction)_wrap_IEnumWbemClassObject_Reset, METH_O, NULL},
+ { (char *)"new_IEnumWbemClassObject", (PyCFunction)_wrap_new_IEnumWbemClassObject, METH_NOARGS, NULL},
+ { (char *)"delete_IEnumWbemClassObject", (PyCFunction)_wrap_delete_IEnumWbemClassObject, METH_O, NULL},
+ { (char *)"IEnumWbemClassObject_swigregister", IEnumWbemClassObject_swigregister, METH_VARARGS, NULL},
+ { (char *)"IEnumWbemClassObject_swiginit", IEnumWbemClassObject_swiginit, METH_VARARGS, NULL},
+ { (char *)"IEnumWbemClassObject_SmartNext", (PyCFunction) _wrap_IEnumWbemClassObject_SmartNext, METH_VARARGS | METH_KEYWORDS, NULL},
+ { NULL, NULL, 0, NULL }
+};
+
+
+/* -------- TYPE CONVERSION AND EQUIVALENCE RULES (BEGIN) -------- */
+
+static swig_type_info _swigt__p_BSTR = {"_p_BSTR", "struct BSTR *", 0, 0, (void*)0, 0};
+static swig_type_info _swigt__p_IEnumWbemClassObject = {"_p_IEnumWbemClassObject", "struct IEnumWbemClassObject *|IEnumWbemClassObject *", 0, 0, (void*)0, 0};
+static swig_type_info _swigt__p_IUnknown = {"_p_IUnknown", "struct IUnknown *|IUnknown *", 0, 0, (void*)0, 0};
+static swig_type_info _swigt__p_IWbemContext = {"_p_IWbemContext", "struct IWbemContext *", 0, 0, (void*)0, 0};
+static swig_type_info _swigt__p_IWbemServices = {"_p_IWbemServices", "struct IWbemServices *|IWbemServices *", 0, 0, (void*)0, 0};
+static swig_type_info _swigt__p_TALLOC_CTX = {"_p_TALLOC_CTX", "TALLOC_CTX *", 0, 0, (void*)0, 0};
+static swig_type_info _swigt__p_char = {"_p_char", "char *", 0, 0, (void*)0, 0};
+static swig_type_info _swigt__p_com_context = {"_p_com_context", "struct com_context *", 0, 0, (void*)0, 0};
+static swig_type_info _swigt__p_int = {"_p_int", "intptr_t *|int *|int_least32_t *|int_fast32_t *|int32_t *|int_fast16_t *", 0, 0, (void*)0, 0};
+static swig_type_info _swigt__p_long_long = {"_p_long_long", "int_least64_t *|int_fast64_t *|int64_t *|long long *|intmax_t *", 0, 0, (void*)0, 0};
+static swig_type_info _swigt__p_p_IEnumWbemClassObject = {"_p_p_IEnumWbemClassObject", "struct IEnumWbemClassObject **", 0, 0, (void*)0, 0};
+static swig_type_info _swigt__p_p_IWbemServices = {"_p_p_IWbemServices", "struct IWbemServices **", 0, 0, (void*)0, 0};
+static swig_type_info _swigt__p_p_WbemClassObject = {"_p_p_WbemClassObject", "struct WbemClassObject **", 0, 0, (void*)0, 0};
+static swig_type_info _swigt__p_short = {"_p_short", "short *|int_least16_t *|int16_t *", 0, 0, (void*)0, 0};
+static swig_type_info _swigt__p_signed_char = {"_p_signed_char", "signed char *|int_least8_t *|int_fast8_t *|int8_t *", 0, 0, (void*)0, 0};
+static swig_type_info _swigt__p_unsigned_char = {"_p_unsigned_char", "unsigned char *|uint_least8_t *|uint_fast8_t *|uint8_t *", 0, 0, (void*)0, 0};
+static swig_type_info _swigt__p_unsigned_int = {"_p_unsigned_int", "uintptr_t *|uint_least32_t *|uint_fast32_t *|uint32_t *|unsigned int *|uint_fast16_t *", 0, 0, (void*)0, 0};
+static swig_type_info _swigt__p_unsigned_long_long = {"_p_unsigned_long_long", "uint_least64_t *|uint_fast64_t *|uint64_t *|unsigned long long *|uintmax_t *", 0, 0, (void*)0, 0};
+static swig_type_info _swigt__p_unsigned_short = {"_p_unsigned_short", "unsigned short *|uint_least16_t *|uint16_t *", 0, 0, (void*)0, 0};
+
+static swig_type_info *swig_type_initial[] = {
+ &_swigt__p_BSTR,
+ &_swigt__p_IEnumWbemClassObject,
+ &_swigt__p_IUnknown,
+ &_swigt__p_IWbemContext,
+ &_swigt__p_IWbemServices,
+ &_swigt__p_TALLOC_CTX,
+ &_swigt__p_char,
+ &_swigt__p_com_context,
+ &_swigt__p_int,
+ &_swigt__p_long_long,
+ &_swigt__p_p_IEnumWbemClassObject,
+ &_swigt__p_p_IWbemServices,
+ &_swigt__p_p_WbemClassObject,
+ &_swigt__p_short,
+ &_swigt__p_signed_char,
+ &_swigt__p_unsigned_char,
+ &_swigt__p_unsigned_int,
+ &_swigt__p_unsigned_long_long,
+ &_swigt__p_unsigned_short,
+};
+
+static swig_cast_info _swigc__p_BSTR[] = { {&_swigt__p_BSTR, 0, 0, 0},{0, 0, 0, 0}};
+static swig_cast_info _swigc__p_IEnumWbemClassObject[] = { {&_swigt__p_IEnumWbemClassObject, 0, 0, 0},{0, 0, 0, 0}};
+static swig_cast_info _swigc__p_IUnknown[] = { {&_swigt__p_IUnknown, 0, 0, 0},{0, 0, 0, 0}};
+static swig_cast_info _swigc__p_IWbemContext[] = { {&_swigt__p_IWbemContext, 0, 0, 0},{0, 0, 0, 0}};
+static swig_cast_info _swigc__p_IWbemServices[] = { {&_swigt__p_IWbemServices, 0, 0, 0},{0, 0, 0, 0}};
+static swig_cast_info _swigc__p_TALLOC_CTX[] = { {&_swigt__p_TALLOC_CTX, 0, 0, 0},{0, 0, 0, 0}};
+static swig_cast_info _swigc__p_char[] = { {&_swigt__p_char, 0, 0, 0},{0, 0, 0, 0}};
+static swig_cast_info _swigc__p_com_context[] = { {&_swigt__p_com_context, 0, 0, 0},{0, 0, 0, 0}};
+static swig_cast_info _swigc__p_int[] = { {&_swigt__p_int, 0, 0, 0},{0, 0, 0, 0}};
+static swig_cast_info _swigc__p_long_long[] = { {&_swigt__p_long_long, 0, 0, 0},{0, 0, 0, 0}};
+static swig_cast_info _swigc__p_p_IEnumWbemClassObject[] = { {&_swigt__p_p_IEnumWbemClassObject, 0, 0, 0},{0, 0, 0, 0}};
+static swig_cast_info _swigc__p_p_IWbemServices[] = { {&_swigt__p_p_IWbemServices, 0, 0, 0},{0, 0, 0, 0}};
+static swig_cast_info _swigc__p_p_WbemClassObject[] = { {&_swigt__p_p_WbemClassObject, 0, 0, 0},{0, 0, 0, 0}};
+static swig_cast_info _swigc__p_short[] = { {&_swigt__p_short, 0, 0, 0},{0, 0, 0, 0}};
+static swig_cast_info _swigc__p_signed_char[] = { {&_swigt__p_signed_char, 0, 0, 0},{0, 0, 0, 0}};
+static swig_cast_info _swigc__p_unsigned_char[] = { {&_swigt__p_unsigned_char, 0, 0, 0},{0, 0, 0, 0}};
+static swig_cast_info _swigc__p_unsigned_int[] = { {&_swigt__p_unsigned_int, 0, 0, 0},{0, 0, 0, 0}};
+static swig_cast_info _swigc__p_unsigned_long_long[] = { {&_swigt__p_unsigned_long_long, 0, 0, 0},{0, 0, 0, 0}};
+static swig_cast_info _swigc__p_unsigned_short[] = { {&_swigt__p_unsigned_short, 0, 0, 0},{0, 0, 0, 0}};
+
+static swig_cast_info *swig_cast_initial[] = {
+ _swigc__p_BSTR,
+ _swigc__p_IEnumWbemClassObject,
+ _swigc__p_IUnknown,
+ _swigc__p_IWbemContext,
+ _swigc__p_IWbemServices,
+ _swigc__p_TALLOC_CTX,
+ _swigc__p_char,
+ _swigc__p_com_context,
+ _swigc__p_int,
+ _swigc__p_long_long,
+ _swigc__p_p_IEnumWbemClassObject,
+ _swigc__p_p_IWbemServices,
+ _swigc__p_p_WbemClassObject,
+ _swigc__p_short,
+ _swigc__p_signed_char,
+ _swigc__p_unsigned_char,
+ _swigc__p_unsigned_int,
+ _swigc__p_unsigned_long_long,
+ _swigc__p_unsigned_short,
+};
+
+
+/* -------- TYPE CONVERSION AND EQUIVALENCE RULES (END) -------- */
+
+static swig_const_info swig_const_table[] = {
+{0, 0, 0, 0.0, 0, 0}};
+
+#ifdef __cplusplus
+}
+#endif
+/* -----------------------------------------------------------------------------
+ * Type initialization:
+ * This problem is tough by the requirement that no dynamic
+ * memory is used. Also, since swig_type_info structures store pointers to
+ * swig_cast_info structures and swig_cast_info structures store pointers back
+ * to swig_type_info structures, we need some lookup code at initialization.
+ * The idea is that swig generates all the structures that are needed.
+ * The runtime then collects these partially filled structures.
+ * The SWIG_InitializeModule function takes these initial arrays out of
+ * swig_module, and does all the lookup, filling in the swig_module.types
+ * array with the correct data and linking the correct swig_cast_info
+ * structures together.
+ *
+ * The generated swig_type_info structures are assigned staticly to an initial
+ * array. We just loop through that array, and handle each type individually.
+ * First we lookup if this type has been already loaded, and if so, use the
+ * loaded structure instead of the generated one. Then we have to fill in the
+ * cast linked list. The cast data is initially stored in something like a
+ * two-dimensional array. Each row corresponds to a type (there are the same
+ * number of rows as there are in the swig_type_initial array). Each entry in
+ * a column is one of the swig_cast_info structures for that type.
+ * The cast_initial array is actually an array of arrays, because each row has
+ * a variable number of columns. So to actually build the cast linked list,
+ * we find the array of casts associated with the type, and loop through it
+ * adding the casts to the list. The one last trick we need to do is making
+ * sure the type pointer in the swig_cast_info struct is correct.
+ *
+ * First off, we lookup the cast->type name to see if it is already loaded.
+ * There are three cases to handle:
+ * 1) If the cast->type has already been loaded AND the type we are adding
+ * casting info to has not been loaded (it is in this module), THEN we
+ * replace the cast->type pointer with the type pointer that has already
+ * been loaded.
+ * 2) If BOTH types (the one we are adding casting info to, and the
+ * cast->type) are loaded, THEN the cast info has already been loaded by
+ * the previous module so we just ignore it.
+ * 3) Finally, if cast->type has not already been loaded, then we add that
+ * swig_cast_info to the linked list (because the cast->type) pointer will
+ * be correct.
+ * ----------------------------------------------------------------------------- */
+
+#ifdef __cplusplus
+extern "C" {
+#if 0
+} /* c-mode */
+#endif
+#endif
+
+#if 0
+#define SWIGRUNTIME_DEBUG
+#endif
+
+
+SWIGRUNTIME void
+SWIG_InitializeModule(void *clientdata) {
+ size_t i;
+ swig_module_info *module_head, *iter;
+ int found, init;
+
+ clientdata = clientdata;
+
+ /* check to see if the circular list has been setup, if not, set it up */
+ if (swig_module.next==0) {
+ /* Initialize the swig_module */
+ swig_module.type_initial = swig_type_initial;
+ swig_module.cast_initial = swig_cast_initial;
+ swig_module.next = &swig_module;
+ init = 1;
+ } else {
+ init = 0;
+ }
+
+ /* Try and load any already created modules */
+ module_head = SWIG_GetModule(clientdata);
+ if (!module_head) {
+ /* This is the first module loaded for this interpreter */
+ /* so set the swig module into the interpreter */
+ SWIG_SetModule(clientdata, &swig_module);
+ module_head = &swig_module;
+ } else {
+ /* the interpreter has loaded a SWIG module, but has it loaded this one? */
+ found=0;
+ iter=module_head;
+ do {
+ if (iter==&swig_module) {
+ found=1;
+ break;
+ }
+ iter=iter->next;
+ } while (iter!= module_head);
+
+ /* if the is found in the list, then all is done and we may leave */
+ if (found) return;
+ /* otherwise we must add out module into the list */
+ swig_module.next = module_head->next;
+ module_head->next = &swig_module;
+ }
+
+ /* When multiple interpeters are used, a module could have already been initialized in
+ a different interpreter, but not yet have a pointer in this interpreter.
+ In this case, we do not want to continue adding types... everything should be
+ set up already */
+ if (init == 0) return;
+
+ /* Now work on filling in swig_module.types */
+#ifdef SWIGRUNTIME_DEBUG
+ printf("SWIG_InitializeModule: size %d\n", swig_module.size);
+#endif
+ for (i = 0; i < swig_module.size; ++i) {
+ swig_type_info *type = 0;
+ swig_type_info *ret;
+ swig_cast_info *cast;
+
+#ifdef SWIGRUNTIME_DEBUG
+ printf("SWIG_InitializeModule: type %d %s\n", i, swig_module.type_initial[i]->name);
+#endif
+
+ /* if there is another module already loaded */
+ if (swig_module.next != &swig_module) {
+ type = SWIG_MangledTypeQueryModule(swig_module.next, &swig_module, swig_module.type_initial[i]->name);
+ }
+ if (type) {
+ /* Overwrite clientdata field */
+#ifdef SWIGRUNTIME_DEBUG
+ printf("SWIG_InitializeModule: found type %s\n", type->name);
+#endif
+ if (swig_module.type_initial[i]->clientdata) {
+ type->clientdata = swig_module.type_initial[i]->clientdata;
+#ifdef SWIGRUNTIME_DEBUG
+ printf("SWIG_InitializeModule: found and overwrite type %s \n", type->name);
+#endif
+ }
+ } else {
+ type = swig_module.type_initial[i];
+ }
+
+ /* Insert casting types */
+ cast = swig_module.cast_initial[i];
+ while (cast->type) {
+ /* Don't need to add information already in the list */
+ ret = 0;
+#ifdef SWIGRUNTIME_DEBUG
+ printf("SWIG_InitializeModule: look cast %s\n", cast->type->name);
+#endif
+ if (swig_module.next != &swig_module) {
+ ret = SWIG_MangledTypeQueryModule(swig_module.next, &swig_module, cast->type->name);
+#ifdef SWIGRUNTIME_DEBUG
+ if (ret) printf("SWIG_InitializeModule: found cast %s\n", ret->name);
+#endif
+ }
+ if (ret) {
+ if (type == swig_module.type_initial[i]) {
+#ifdef SWIGRUNTIME_DEBUG
+ printf("SWIG_InitializeModule: skip old type %s\n", ret->name);
+#endif
+ cast->type = ret;
+ ret = 0;
+ } else {
+ /* Check for casting already in the list */
+ swig_cast_info *ocast = SWIG_TypeCheck(ret->name, type);
+#ifdef SWIGRUNTIME_DEBUG
+ if (ocast) printf("SWIG_InitializeModule: skip old cast %s\n", ret->name);
+#endif
+ if (!ocast) ret = 0;
+ }
+ }
+
+ if (!ret) {
+#ifdef SWIGRUNTIME_DEBUG
+ printf("SWIG_InitializeModule: adding cast %s\n", cast->type->name);
+#endif
+ if (type->cast) {
+ type->cast->prev = cast;
+ cast->next = type->cast;
+ }
+ type->cast = cast;
+ }
+ cast++;
+ }
+ /* Set entry in modules->types array equal to the type */
+ swig_module.types[i] = type;
+ }
+ swig_module.types[i] = 0;
+
+#ifdef SWIGRUNTIME_DEBUG
+ printf("**** SWIG_InitializeModule: Cast List ******\n");
+ for (i = 0; i < swig_module.size; ++i) {
+ int j = 0;
+ swig_cast_info *cast = swig_module.cast_initial[i];
+ printf("SWIG_InitializeModule: type %d %s\n", i, swig_module.type_initial[i]->name);
+ while (cast->type) {
+ printf("SWIG_InitializeModule: cast type %s\n", cast->type->name);
+ cast++;
+ ++j;
+ }
+ printf("---- Total casts: %d\n",j);
+ }
+ printf("**** SWIG_InitializeModule: Cast List ******\n");
+#endif
+}
+
+/* This function will propagate the clientdata field of type to
+* any new swig_type_info structures that have been added into the list
+* of equivalent types. It is like calling
+* SWIG_TypeClientData(type, clientdata) a second time.
+*/
+SWIGRUNTIME void
+SWIG_PropagateClientData(void) {
+ size_t i;
+ swig_cast_info *equiv;
+ static int init_run = 0;
+
+ if (init_run) return;
+ init_run = 1;
+
+ for (i = 0; i < swig_module.size; i++) {
+ if (swig_module.types[i]->clientdata) {
+ equiv = swig_module.types[i]->cast;
+ while (equiv) {
+ if (!equiv->converter) {
+ if (equiv->type && !equiv->type->clientdata)
+ SWIG_TypeClientData(equiv->type, swig_module.types[i]->clientdata);
+ }
+ equiv = equiv->next;
+ }
+ }
+ }
+}
+
+#ifdef __cplusplus
+#if 0
+{
+ /* c-mode */
+#endif
+}
+#endif
+
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /* Python-specific SWIG API */
+#define SWIG_newvarlink() SWIG_Python_newvarlink()
+#define SWIG_addvarlink(p, name, get_attr, set_attr) SWIG_Python_addvarlink(p, name, get_attr, set_attr)
+#define SWIG_InstallConstants(d, constants) SWIG_Python_InstallConstants(d, constants)
+
+ /* -----------------------------------------------------------------------------
+ * global variable support code.
+ * ----------------------------------------------------------------------------- */
+
+ typedef struct swig_globalvar {
+ char *name; /* Name of global variable */
+ PyObject *(*get_attr)(void); /* Return the current value */
+ int (*set_attr)(PyObject *); /* Set the value */
+ struct swig_globalvar *next;
+ } swig_globalvar;
+
+ typedef struct swig_varlinkobject {
+ PyObject_HEAD
+ swig_globalvar *vars;
+ } swig_varlinkobject;
+
+ SWIGINTERN PyObject *
+ swig_varlink_repr(swig_varlinkobject *SWIGUNUSEDPARM(v)) {
+ return PyString_FromString("<Swig global variables>");
+ }
+
+ SWIGINTERN PyObject *
+ swig_varlink_str(swig_varlinkobject *v) {
+ PyObject *str = PyString_FromString("(");
+ swig_globalvar *var;
+ for (var = v->vars; var; var=var->next) {
+ PyString_ConcatAndDel(&str,PyString_FromString(var->name));
+ if (var->next) PyString_ConcatAndDel(&str,PyString_FromString(", "));
+ }
+ PyString_ConcatAndDel(&str,PyString_FromString(")"));
+ return str;
+ }
+
+ SWIGINTERN int
+ swig_varlink_print(swig_varlinkobject *v, FILE *fp, int SWIGUNUSEDPARM(flags)) {
+ PyObject *str = swig_varlink_str(v);
+ fprintf(fp,"Swig global variables ");
+ fprintf(fp,"%s\n", PyString_AsString(str));
+ Py_DECREF(str);
+ return 0;
+ }
+
+ SWIGINTERN void
+ swig_varlink_dealloc(swig_varlinkobject *v) {
+ swig_globalvar *var = v->vars;
+ while (var) {
+ swig_globalvar *n = var->next;
+ free(var->name);
+ free(var);
+ var = n;
+ }
+ }
+
+ SWIGINTERN PyObject *
+ swig_varlink_getattr(swig_varlinkobject *v, char *n) {
+ PyObject *res = NULL;
+ swig_globalvar *var = v->vars;
+ while (var) {
+ if (strcmp(var->name,n) == 0) {
+ res = (*var->get_attr)();
+ break;
+ }
+ var = var->next;
+ }
+ if (res == NULL && !PyErr_Occurred()) {
+ PyErr_SetString(PyExc_NameError,"Unknown C global variable");
+ }
+ return res;
+ }
+
+ SWIGINTERN int
+ swig_varlink_setattr(swig_varlinkobject *v, char *n, PyObject *p) {
+ int res = 1;
+ swig_globalvar *var = v->vars;
+ while (var) {
+ if (strcmp(var->name,n) == 0) {
+ res = (*var->set_attr)(p);
+ break;
+ }
+ var = var->next;
+ }
+ if (res == 1 && !PyErr_Occurred()) {
+ PyErr_SetString(PyExc_NameError,"Unknown C global variable");
+ }
+ return res;
+ }
+
+ SWIGINTERN PyTypeObject*
+ swig_varlink_type(void) {
+ static char varlink__doc__[] = "Swig var link object";
+ static PyTypeObject varlink_type;
+ static int type_init = 0;
+ if (!type_init) {
+ const PyTypeObject tmp
+ = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* Number of items in variable part (ob_size) */
+ (char *)"swigvarlink", /* Type name (tp_name) */
+ sizeof(swig_varlinkobject), /* Basic size (tp_basicsize) */
+ 0, /* Itemsize (tp_itemsize) */
+ (destructor) swig_varlink_dealloc, /* Deallocator (tp_dealloc) */
+ (printfunc) swig_varlink_print, /* Print (tp_print) */
+ (getattrfunc) swig_varlink_getattr, /* get attr (tp_getattr) */
+ (setattrfunc) swig_varlink_setattr, /* Set attr (tp_setattr) */
+ 0, /* tp_compare */
+ (reprfunc) swig_varlink_repr, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ (reprfunc)swig_varlink_str, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ 0, /* tp_flags */
+ varlink__doc__, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+#if PY_VERSION_HEX >= 0x02020000
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* tp_iter -> tp_weaklist */
+#endif
+#if PY_VERSION_HEX >= 0x02030000
+ 0, /* tp_del */
+#endif
+#ifdef COUNT_ALLOCS
+ 0,0,0,0 /* tp_alloc -> tp_next */
+#endif
+ };
+ varlink_type = tmp;
+ varlink_type.ob_type = &PyType_Type;
+ type_init = 1;
+ }
+ return &varlink_type;
+ }
+
+ /* Create a variable linking object for use later */
+ SWIGINTERN PyObject *
+ SWIG_Python_newvarlink(void) {
+ swig_varlinkobject *result = PyObject_NEW(swig_varlinkobject, swig_varlink_type());
+ if (result) {
+ result->vars = 0;
+ }
+ return ((PyObject*) result);
+ }
+
+ SWIGINTERN void
+ SWIG_Python_addvarlink(PyObject *p, char *name, PyObject *(*get_attr)(void), int (*set_attr)(PyObject *p)) {
+ swig_varlinkobject *v = (swig_varlinkobject *) p;
+ swig_globalvar *gv = (swig_globalvar *) malloc(sizeof(swig_globalvar));
+ if (gv) {
+ size_t size = strlen(name)+1;
+ gv->name = (char *)malloc(size);
+ if (gv->name) {
+ strncpy(gv->name,name,size);
+ gv->get_attr = get_attr;
+ gv->set_attr = set_attr;
+ gv->next = v->vars;
+ }
+ }
+ v->vars = gv;
+ }
+
+ SWIGINTERN PyObject *
+ SWIG_globals(void) {
+ static PyObject *_SWIG_globals = 0;
+ if (!_SWIG_globals) _SWIG_globals = SWIG_newvarlink();
+ return _SWIG_globals;
+ }
+
+ /* -----------------------------------------------------------------------------
+ * constants/methods manipulation
+ * ----------------------------------------------------------------------------- */
+
+ /* Install Constants */
+ SWIGINTERN void
+ SWIG_Python_InstallConstants(PyObject *d, swig_const_info constants[]) {
+ PyObject *obj = 0;
+ size_t i;
+ for (i = 0; constants[i].type; ++i) {
+ switch(constants[i].type) {
+ case SWIG_PY_POINTER:
+ obj = SWIG_NewPointerObj(constants[i].pvalue, *(constants[i]).ptype,0);
+ break;
+ case SWIG_PY_BINARY:
+ obj = SWIG_NewPackedObj(constants[i].pvalue, constants[i].lvalue, *(constants[i].ptype));
+ break;
+ default:
+ obj = 0;
+ break;
+ }
+ if (obj) {
+ PyDict_SetItemString(d, constants[i].name, obj);
+ Py_DECREF(obj);
+ }
+ }
+ }
+
+ /* -----------------------------------------------------------------------------*/
+ /* Fix SwigMethods to carry the callback ptrs when needed */
+ /* -----------------------------------------------------------------------------*/
+
+ SWIGINTERN void
+ SWIG_Python_FixMethods(PyMethodDef *methods,
+ swig_const_info *const_table,
+ swig_type_info **types,
+ swig_type_info **types_initial) {
+ size_t i;
+ for (i = 0; methods[i].ml_name; ++i) {
+ const char *c = methods[i].ml_doc;
+ if (c && (c = strstr(c, "swig_ptr: "))) {
+ int j;
+ swig_const_info *ci = 0;
+ const char *name = c + 10;
+ for (j = 0; const_table[j].type; ++j) {
+ if (strncmp(const_table[j].name, name,
+ strlen(const_table[j].name)) == 0) {
+ ci = &(const_table[j]);
+ break;
+ }
+ }
+ if (ci) {
+ size_t shift = (ci->ptype) - types;
+ swig_type_info *ty = types_initial[shift];
+ size_t ldoc = (c - methods[i].ml_doc);
+ size_t lptr = strlen(ty->name)+2*sizeof(void*)+2;
+ char *ndoc = (char*)malloc(ldoc + lptr + 10);
+ if (ndoc) {
+ char *buff = ndoc;
+ void *ptr = (ci->type == SWIG_PY_POINTER) ? ci->pvalue : 0;
+ if (ptr) {
+ strncpy(buff, methods[i].ml_doc, ldoc);
+ buff += ldoc;
+ strncpy(buff, "swig_ptr: ", 10);
+ buff += 10;
+ SWIG_PackVoidPtr(buff, ptr, ty->name, lptr);
+ methods[i].ml_doc = ndoc;
+ }
+ }
+ }
+ }
+ }
+ }
+
+#ifdef __cplusplus
+}
+#endif
+
+/* -----------------------------------------------------------------------------*
+ * Partial Init method
+ * -----------------------------------------------------------------------------*/
+
+#ifdef __cplusplus
+extern "C"
+#endif
+SWIGEXPORT void SWIG_init(void) {
+ PyObject *m, *d;
+
+ /* Fix SwigMethods to carry the callback ptrs when needed */
+ SWIG_Python_FixMethods(SwigMethods, swig_const_table, swig_types, swig_type_initial);
+
+ m = Py_InitModule((char *) SWIG_name, SwigMethods);
+ d = PyModule_GetDict(m);
+
+ SWIG_InitializeModule(0);
+ SWIG_InstallConstants(d,swig_const_table);
+
+
+
+
+ mod_win32_client = PyImport_ImportModule("win32com.client");
+ mod_pywintypes = PyImport_ImportModule("pywintypes");
+ ComError = PyObject_GetAttrString(mod_pywintypes, "com_error");
+
+ wmi_init(&com_ctx, NULL);
+ {
+ PyObject *pModule;
+
+ pModule = PyImport_ImportModule( "win32com.client" );
+ }
+
+}
+
diff --git a/source4/lib/wmi/wmicore.c b/source4/lib/wmi/wmicore.c
new file mode 100644
index 0000000000..7624946536
--- /dev/null
+++ b/source4/lib/wmi/wmicore.c
@@ -0,0 +1,252 @@
+/*
+ WMI Sample client
+ Copyright (C) 2006 Andrzej Hajda <andrzej.hajda@wp.pl>
+ Copyright (C) 2008 Jelmer Vernooij <jelmer@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU 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 "auth/credentials/credentials.h"
+#include "librpc/gen_ndr/com_dcom.h"
+#include "lib/com/dcom/dcom.h"
+#include "librpc/gen_ndr/wmi.h"
+#include "librpc/gen_ndr/com_wmi.h"
+#include "librpc/rpc/dcerpc.h"
+#include "librpc/ndr/ndr_table.h"
+
+struct IWbemServices;
+struct IWbemContext;
+
+#define WERR_CHECK(msg) if (!W_ERROR_IS_OK(result)) { \
+ DEBUG(0, ("ERROR: %s\n", msg)); \
+ goto end; \
+ } else { \
+ DEBUG(1, ("OK : %s\n", msg)); \
+ }
+
+void wmi_init(struct com_context **ctx, struct cli_credentials *credentials,
+ struct loadparm_context *lp_ctx)
+{
+ dcerpc_init(lp_ctx);
+ ndr_table_init();
+
+ /* FIXME: Register DCOM proxies? */
+
+ com_init_ctx(ctx, NULL);
+ dcom_client_init(*ctx, credentials);
+}
+
+/** FIXME: Use credentials struct rather than user/password here */
+WERROR WBEM_ConnectServer(struct com_context *ctx, const char *server, const uint16_t *nspace,
+ struct cli_credentials *credentials,
+ const char *locale, uint32_t flags, const char *authority,
+ struct IWbemContext* wbem_ctx, struct IWbemServices** services)
+{
+ struct GUID clsid;
+ struct GUID iid;
+ WERROR result, coresult;
+ struct IUnknown **mqi;
+ struct IWbemLevel1Login *pL;
+
+ GUID_from_string(CLSID_WBEMLEVEL1LOGIN, &clsid);
+ GUID_from_string(COM_IWBEMLEVEL1LOGIN_UUID, &iid);
+ result = dcom_create_object(ctx, &clsid, server, 1, &iid, &mqi, &coresult);
+ WERR_CHECK("dcom_create_object.");
+ result = coresult;
+ WERR_CHECK("Create remote WMI object.");
+ pL = (struct IWbemLevel1Login *)mqi[0];
+ talloc_free(mqi);
+
+ result = IWbemLevel1Login_NTLMLogin(pL, ctx, nspace, locale, flags, wbem_ctx, services);
+ WERR_CHECK("Login to remote object.");
+
+ IUnknown_Release((struct IUnknown *)pL, ctx);
+end:
+ return result;
+}
+
+struct werror_code_struct {
+ const char *dos_errstr;
+ WERROR werror;
+};
+
+static const struct werror_code_struct wmi_errs[] =
+{
+ { "RPC_S_CALL_FAILED", W_ERROR(RPC_S_CALL_FAILED) },
+
+ { "WBEM_NO_ERROR", W_ERROR(WBEM_NO_ERROR) },
+ { "WBEM_S_NO_ERROR", W_ERROR(WBEM_S_NO_ERROR) },
+ { "WBEM_S_SAME", W_ERROR(WBEM_S_SAME) },
+ { "WBEM_S_FALSE", W_ERROR(WBEM_S_FALSE) },
+ { "WBEM_S_ALREADY_EXISTS", W_ERROR(WBEM_S_ALREADY_EXISTS) },
+ { "WBEM_S_RESET_TO_DEFAULT", W_ERROR(WBEM_S_RESET_TO_DEFAULT) },
+ { "WBEM_S_DIFFERENT", W_ERROR(WBEM_S_DIFFERENT) },
+ { "WBEM_S_TIMEDOUT", W_ERROR(WBEM_S_TIMEDOUT) },
+ { "WBEM_S_NO_MORE_DATA", W_ERROR(WBEM_S_NO_MORE_DATA) },
+ { "WBEM_S_OPERATION_CANCELLED", W_ERROR(WBEM_S_OPERATION_CANCELLED) },
+ { "WBEM_S_PENDING", W_ERROR(WBEM_S_PENDING) },
+ { "WBEM_S_DUPLICATE_OBJECTS", W_ERROR(WBEM_S_DUPLICATE_OBJECTS) },
+ { "WBEM_S_ACCESS_DENIED", W_ERROR(WBEM_S_ACCESS_DENIED) },
+ { "WBEM_S_PARTIAL_RESULTS", W_ERROR(WBEM_S_PARTIAL_RESULTS) },
+ { "WBEM_S_NO_POSTHOOK", W_ERROR(WBEM_S_NO_POSTHOOK) },
+ { "WBEM_S_POSTHOOK_WITH_BOTH", W_ERROR(WBEM_S_POSTHOOK_WITH_BOTH) },
+ { "WBEM_S_POSTHOOK_WITH_NEW", W_ERROR(WBEM_S_POSTHOOK_WITH_NEW) },
+ { "WBEM_S_POSTHOOK_WITH_STATUS", W_ERROR(WBEM_S_POSTHOOK_WITH_STATUS) },
+ { "WBEM_S_POSTHOOK_WITH_OLD", W_ERROR(WBEM_S_POSTHOOK_WITH_OLD) },
+ { "WBEM_S_REDO_PREHOOK_WITH_ORIGINAL_OBJECT", W_ERROR(WBEM_S_REDO_PREHOOK_WITH_ORIGINAL_OBJECT) },
+ { "WBEM_S_SOURCE_NOT_AVAILABLE", W_ERROR(WBEM_S_SOURCE_NOT_AVAILABLE) },
+ { "WBEM_E_FAILED", W_ERROR(WBEM_E_FAILED) },
+ { "WBEM_E_NOT_FOUND", W_ERROR(WBEM_E_NOT_FOUND) },
+ { "WBEM_E_ACCESS_DENIED", W_ERROR(WBEM_E_ACCESS_DENIED) },
+ { "WBEM_E_PROVIDER_FAILURE", W_ERROR(WBEM_E_PROVIDER_FAILURE) },
+ { "WBEM_E_TYPE_MISMATCH", W_ERROR(WBEM_E_TYPE_MISMATCH) },
+ { "WBEM_E_OUT_OF_MEMORY", W_ERROR(WBEM_E_OUT_OF_MEMORY) },
+ { "WBEM_E_INVALID_CONTEXT", W_ERROR(WBEM_E_INVALID_CONTEXT) },
+ { "WBEM_E_INVALID_PARAMETER", W_ERROR(WBEM_E_INVALID_PARAMETER) },
+ { "WBEM_E_NOT_AVAILABLE", W_ERROR(WBEM_E_NOT_AVAILABLE) },
+ { "WBEM_E_CRITICAL_ERROR", W_ERROR(WBEM_E_CRITICAL_ERROR) },
+ { "WBEM_E_INVALID_STREAM", W_ERROR(WBEM_E_INVALID_STREAM) },
+ { "WBEM_E_NOT_SUPPORTED", W_ERROR(WBEM_E_NOT_SUPPORTED) },
+ { "WBEM_E_INVALID_SUPERCLASS", W_ERROR(WBEM_E_INVALID_SUPERCLASS) },
+ { "WBEM_E_INVALID_NAMESPACE", W_ERROR(WBEM_E_INVALID_NAMESPACE) },
+ { "WBEM_E_INVALID_OBJECT", W_ERROR(WBEM_E_INVALID_OBJECT) },
+ { "WBEM_E_INVALID_CLASS", W_ERROR(WBEM_E_INVALID_CLASS) },
+ { "WBEM_E_PROVIDER_NOT_FOUND", W_ERROR(WBEM_E_PROVIDER_NOT_FOUND) },
+ { "WBEM_E_INVALID_PROVIDER_REGISTRATION", W_ERROR(WBEM_E_INVALID_PROVIDER_REGISTRATION) },
+ { "WBEM_E_PROVIDER_LOAD_FAILURE", W_ERROR(WBEM_E_PROVIDER_LOAD_FAILURE) },
+ { "WBEM_E_INITIALIZATION_FAILURE", W_ERROR(WBEM_E_INITIALIZATION_FAILURE) },
+ { "WBEM_E_TRANSPORT_FAILURE", W_ERROR(WBEM_E_TRANSPORT_FAILURE) },
+ { "WBEM_E_INVALID_OPERATION", W_ERROR(WBEM_E_INVALID_OPERATION) },
+ { "WBEM_E_INVALID_QUERY", W_ERROR(WBEM_E_INVALID_QUERY) },
+ { "WBEM_E_INVALID_QUERY_TYPE", W_ERROR(WBEM_E_INVALID_QUERY_TYPE) },
+ { "WBEM_E_ALREADY_EXISTS", W_ERROR(WBEM_E_ALREADY_EXISTS) },
+ { "WBEM_E_OVERRIDE_NOT_ALLOWED", W_ERROR(WBEM_E_OVERRIDE_NOT_ALLOWED) },
+ { "WBEM_E_PROPAGATED_QUALIFIER", W_ERROR(WBEM_E_PROPAGATED_QUALIFIER) },
+ { "WBEM_E_PROPAGATED_PROPERTY", W_ERROR(WBEM_E_PROPAGATED_PROPERTY) },
+ { "WBEM_E_UNEXPECTED", W_ERROR(WBEM_E_UNEXPECTED) },
+ { "WBEM_E_ILLEGAL_OPERATION", W_ERROR(WBEM_E_ILLEGAL_OPERATION) },
+ { "WBEM_E_CANNOT_BE_KEY", W_ERROR(WBEM_E_CANNOT_BE_KEY) },
+ { "WBEM_E_INCOMPLETE_CLASS", W_ERROR(WBEM_E_INCOMPLETE_CLASS) },
+ { "WBEM_E_INVALID_SYNTAX", W_ERROR(WBEM_E_INVALID_SYNTAX) },
+ { "WBEM_E_NONDECORATED_OBJECT", W_ERROR(WBEM_E_NONDECORATED_OBJECT) },
+ { "WBEM_E_READ_ONLY", W_ERROR(WBEM_E_READ_ONLY) },
+ { "WBEM_E_PROVIDER_NOT_CAPABLE", W_ERROR(WBEM_E_PROVIDER_NOT_CAPABLE) },
+ { "WBEM_E_CLASS_HAS_CHILDREN", W_ERROR(WBEM_E_CLASS_HAS_CHILDREN) },
+ { "WBEM_E_CLASS_HAS_INSTANCES", W_ERROR(WBEM_E_CLASS_HAS_INSTANCES) },
+ { "WBEM_E_QUERY_NOT_IMPLEMENTED", W_ERROR(WBEM_E_QUERY_NOT_IMPLEMENTED) },
+ { "WBEM_E_ILLEGAL_NULL", W_ERROR(WBEM_E_ILLEGAL_NULL) },
+ { "WBEM_E_INVALID_QUALIFIER_TYPE", W_ERROR(WBEM_E_INVALID_QUALIFIER_TYPE) },
+ { "WBEM_E_INVALID_PROPERTY_TYPE", W_ERROR(WBEM_E_INVALID_PROPERTY_TYPE) },
+ { "WBEM_E_VALUE_OUT_OF_RANGE", W_ERROR(WBEM_E_VALUE_OUT_OF_RANGE) },
+ { "WBEM_E_CANNOT_BE_SINGLETON", W_ERROR(WBEM_E_CANNOT_BE_SINGLETON) },
+ { "WBEM_E_INVALID_CIM_TYPE", W_ERROR(WBEM_E_INVALID_CIM_TYPE) },
+ { "WBEM_E_INVALID_METHOD", W_ERROR(WBEM_E_INVALID_METHOD) },
+ { "WBEM_E_INVALID_METHOD_PARAMETERS", W_ERROR(WBEM_E_INVALID_METHOD_PARAMETERS) },
+ { "WBEM_E_SYSTEM_PROPERTY", W_ERROR(WBEM_E_SYSTEM_PROPERTY) },
+ { "WBEM_E_INVALID_PROPERTY", W_ERROR(WBEM_E_INVALID_PROPERTY) },
+ { "WBEM_E_CALL_CANCELLED", W_ERROR(WBEM_E_CALL_CANCELLED) },
+ { "WBEM_E_SHUTTING_DOWN", W_ERROR(WBEM_E_SHUTTING_DOWN) },
+ { "WBEM_E_PROPAGATED_METHOD", W_ERROR(WBEM_E_PROPAGATED_METHOD) },
+ { "WBEM_E_UNSUPPORTED_PARAMETER", W_ERROR(WBEM_E_UNSUPPORTED_PARAMETER) },
+ { "WBEM_E_MISSING_PARAMETER_ID", W_ERROR(WBEM_E_MISSING_PARAMETER_ID) },
+ { "WBEM_E_INVALID_PARAMETER_ID", W_ERROR(WBEM_E_INVALID_PARAMETER_ID) },
+ { "WBEM_E_NONCONSECUTIVE_PARAMETER_IDS", W_ERROR(WBEM_E_NONCONSECUTIVE_PARAMETER_IDS) },
+ { "WBEM_E_PARAMETER_ID_ON_RETVAL", W_ERROR(WBEM_E_PARAMETER_ID_ON_RETVAL) },
+ { "WBEM_E_INVALID_OBJECT_PATH", W_ERROR(WBEM_E_INVALID_OBJECT_PATH) },
+ { "WBEM_E_OUT_OF_DISK_SPACE", W_ERROR(WBEM_E_OUT_OF_DISK_SPACE) },
+ { "WBEM_E_BUFFER_TOO_SMALL", W_ERROR(WBEM_E_BUFFER_TOO_SMALL) },
+ { "WBEM_E_UNSUPPORTED_PUT_EXTENSION", W_ERROR(WBEM_E_UNSUPPORTED_PUT_EXTENSION) },
+ { "WBEM_E_UNKNOWN_OBJECT_TYPE", W_ERROR(WBEM_E_UNKNOWN_OBJECT_TYPE) },
+ { "WBEM_E_UNKNOWN_PACKET_TYPE", W_ERROR(WBEM_E_UNKNOWN_PACKET_TYPE) },
+ { "WBEM_E_MARSHAL_VERSION_MISMATCH", W_ERROR(WBEM_E_MARSHAL_VERSION_MISMATCH) },
+ { "WBEM_E_MARSHAL_INVALID_SIGNATURE", W_ERROR(WBEM_E_MARSHAL_INVALID_SIGNATURE) },
+ { "WBEM_E_INVALID_QUALIFIER", W_ERROR(WBEM_E_INVALID_QUALIFIER) },
+ { "WBEM_E_INVALID_DUPLICATE_PARAMETER", W_ERROR(WBEM_E_INVALID_DUPLICATE_PARAMETER) },
+ { "WBEM_E_TOO_MUCH_DATA", W_ERROR(WBEM_E_TOO_MUCH_DATA) },
+ { "WBEM_E_SERVER_TOO_BUSY", W_ERROR(WBEM_E_SERVER_TOO_BUSY) },
+ { "WBEM_E_INVALID_FLAVOR", W_ERROR(WBEM_E_INVALID_FLAVOR) },
+ { "WBEM_E_CIRCULAR_REFERENCE", W_ERROR(WBEM_E_CIRCULAR_REFERENCE) },
+ { "WBEM_E_UNSUPPORTED_CLASS_UPDATE", W_ERROR(WBEM_E_UNSUPPORTED_CLASS_UPDATE) },
+ { "WBEM_E_CANNOT_CHANGE_KEY_INHERITANCE", W_ERROR(WBEM_E_CANNOT_CHANGE_KEY_INHERITANCE) },
+ { "WBEM_E_CANNOT_CHANGE_INDEX_INHERITANCE", W_ERROR(WBEM_E_CANNOT_CHANGE_INDEX_INHERITANCE) },
+ { "WBEM_E_TOO_MANY_PROPERTIES", W_ERROR(WBEM_E_TOO_MANY_PROPERTIES) },
+ { "WBEM_E_UPDATE_TYPE_MISMATCH", W_ERROR(WBEM_E_UPDATE_TYPE_MISMATCH) },
+ { "WBEM_E_UPDATE_OVERRIDE_NOT_ALLOWED", W_ERROR(WBEM_E_UPDATE_OVERRIDE_NOT_ALLOWED) },
+ { "WBEM_E_UPDATE_PROPAGATED_METHOD", W_ERROR(WBEM_E_UPDATE_PROPAGATED_METHOD) },
+ { "WBEM_E_METHOD_NOT_IMPLEMENTED", W_ERROR(WBEM_E_METHOD_NOT_IMPLEMENTED) },
+ { "WBEM_E_METHOD_DISABLED", W_ERROR(WBEM_E_METHOD_DISABLED) },
+ { "WBEM_E_REFRESHER_BUSY", W_ERROR(WBEM_E_REFRESHER_BUSY) },
+ { "WBEM_E_UNPARSABLE_QUERY", W_ERROR(WBEM_E_UNPARSABLE_QUERY) },
+ { "WBEM_E_NOT_EVENT_CLASS", W_ERROR(WBEM_E_NOT_EVENT_CLASS) },
+ { "WBEM_E_MISSING_GROUP_WITHIN", W_ERROR(WBEM_E_MISSING_GROUP_WITHIN) },
+ { "WBEM_E_MISSING_AGGREGATION_LIST", W_ERROR(WBEM_E_MISSING_AGGREGATION_LIST) },
+ { "WBEM_E_PROPERTY_NOT_AN_OBJECT", W_ERROR(WBEM_E_PROPERTY_NOT_AN_OBJECT) },
+ { "WBEM_E_AGGREGATING_BY_OBJECT", W_ERROR(WBEM_E_AGGREGATING_BY_OBJECT) },
+ { "WBEM_E_UNINTERPRETABLE_PROVIDER_QUERY", W_ERROR(WBEM_E_UNINTERPRETABLE_PROVIDER_QUERY) },
+ { "WBEM_E_BACKUP_RESTORE_WINMGMT_RUNNING", W_ERROR(WBEM_E_BACKUP_RESTORE_WINMGMT_RUNNING) },
+ { "WBEM_E_QUEUE_OVERFLOW", W_ERROR(WBEM_E_QUEUE_OVERFLOW) },
+ { "WBEM_E_PRIVILEGE_NOT_HELD", W_ERROR(WBEM_E_PRIVILEGE_NOT_HELD) },
+ { "WBEM_E_INVALID_OPERATOR", W_ERROR(WBEM_E_INVALID_OPERATOR) },
+ { "WBEM_E_LOCAL_CREDENTIALS", W_ERROR(WBEM_E_LOCAL_CREDENTIALS) },
+ { "WBEM_E_CANNOT_BE_ABSTRACT", W_ERROR(WBEM_E_CANNOT_BE_ABSTRACT) },
+ { "WBEM_E_AMENDED_OBJECT", W_ERROR(WBEM_E_AMENDED_OBJECT) },
+ { "WBEM_E_CLIENT_TOO_SLOW", W_ERROR(WBEM_E_CLIENT_TOO_SLOW) },
+ { "WBEM_E_NULL_SECURITY_DESCRIPTOR", W_ERROR(WBEM_E_NULL_SECURITY_DESCRIPTOR) },
+ { "WBEM_E_TIMED_OUT", W_ERROR(WBEM_E_TIMED_OUT) },
+ { "WBEM_E_INVALID_ASSOCIATION", W_ERROR(WBEM_E_INVALID_ASSOCIATION) },
+ { "WBEM_E_AMBIGUOUS_OPERATION", W_ERROR(WBEM_E_AMBIGUOUS_OPERATION) },
+ { "WBEM_E_QUOTA_VIOLATION", W_ERROR(WBEM_E_QUOTA_VIOLATION) },
+ { "WBEM_E_RESERVED_001", W_ERROR(WBEM_E_RESERVED_001) },
+ { "WBEM_E_RESERVED_002", W_ERROR(WBEM_E_RESERVED_002) },
+ { "WBEM_E_UNSUPPORTED_LOCALE", W_ERROR(WBEM_E_UNSUPPORTED_LOCALE) },
+ { "WBEM_E_HANDLE_OUT_OF_DATE", W_ERROR(WBEM_E_HANDLE_OUT_OF_DATE) },
+ { "WBEM_E_CONNECTION_FAILED", W_ERROR(WBEM_E_CONNECTION_FAILED) },
+ { "WBEM_E_INVALID_HANDLE_REQUEST", W_ERROR(WBEM_E_INVALID_HANDLE_REQUEST) },
+ { "WBEM_E_PROPERTY_NAME_TOO_WIDE", W_ERROR(WBEM_E_PROPERTY_NAME_TOO_WIDE) },
+ { "WBEM_E_CLASS_NAME_TOO_WIDE", W_ERROR(WBEM_E_CLASS_NAME_TOO_WIDE) },
+ { "WBEM_E_METHOD_NAME_TOO_WIDE", W_ERROR(WBEM_E_METHOD_NAME_TOO_WIDE) },
+ { "WBEM_E_QUALIFIER_NAME_TOO_WIDE", W_ERROR(WBEM_E_QUALIFIER_NAME_TOO_WIDE) },
+ { "WBEM_E_RERUN_COMMAND", W_ERROR(WBEM_E_RERUN_COMMAND) },
+ { "WBEM_E_DATABASE_VER_MISMATCH", W_ERROR(WBEM_E_DATABASE_VER_MISMATCH) },
+ { "WBEM_E_VETO_DELETE", W_ERROR(WBEM_E_VETO_DELETE) },
+ { "WBEM_E_VETO_PUT", W_ERROR(WBEM_E_VETO_PUT) },
+ { "WBEM_E_INVALID_LOCALE", W_ERROR(WBEM_E_INVALID_LOCALE) },
+ { "WBEM_E_PROVIDER_SUSPENDED", W_ERROR(WBEM_E_PROVIDER_SUSPENDED) },
+ { "WBEM_E_SYNCHRONIZATION_REQUIRED", W_ERROR(WBEM_E_SYNCHRONIZATION_REQUIRED) },
+ { "WBEM_E_NO_SCHEMA", W_ERROR(WBEM_E_NO_SCHEMA) },
+ { "WBEM_E_PROVIDER_ALREADY_REGISTERED", W_ERROR(WBEM_E_PROVIDER_ALREADY_REGISTERED) },
+ { "WBEM_E_PROVIDER_NOT_REGISTERED", W_ERROR(WBEM_E_PROVIDER_NOT_REGISTERED) },
+ { "WBEM_E_FATAL_TRANSPORT_ERROR", W_ERROR(WBEM_E_FATAL_TRANSPORT_ERROR) },
+ { "WBEM_E_ENCRYPTED_CONNECTION_REQUIRED", W_ERROR(WBEM_E_ENCRYPTED_CONNECTION_REQUIRED) },
+ { "WBEM_E_PROVIDER_TIMED_OUT", W_ERROR(WBEM_E_PROVIDER_TIMED_OUT) },
+ { "WBEM_E_NO_KEY", W_ERROR(WBEM_E_NO_KEY) },
+ { "WBEM_E_PROVIDER_DISABLED", W_ERROR(WBEM_E_PROVIDER_DISABLED) },
+ { NULL, W_ERROR(0) }
+};
+
+const char *wmi_errstr(WERROR werror)
+{
+ int idx = 0;
+
+ while (wmi_errs[idx].dos_errstr != NULL) {
+ if (W_ERROR_V(wmi_errs[idx].werror) ==
+ W_ERROR_V(werror))
+ return wmi_errs[idx].dos_errstr;
+ idx++;
+ }
+
+ return win_errstr(werror);
+}
diff --git a/source4/lib/zlib.mk b/source4/lib/zlib.mk
new file mode 100644
index 0000000000..5c5e6e69ba
--- /dev/null
+++ b/source4/lib/zlib.mk
@@ -0,0 +1,16 @@
+[SUBSYSTEM::ZLIB]
+CFLAGS = -I$(zlibsrcdir)
+
+ZLIB_OBJ_FILES = \
+ $(zlibsrcdir)/adler32.o \
+ $(zlibsrcdir)/compress.o \
+ $(zlibsrcdir)/crc32.o \
+ $(zlibsrcdir)/gzio.o \
+ $(zlibsrcdir)/uncompr.o \
+ $(zlibsrcdir)/deflate.o \
+ $(zlibsrcdir)/trees.o \
+ $(zlibsrcdir)/zutil.o \
+ $(zlibsrcdir)/inflate.o \
+ $(zlibsrcdir)/infback.o \
+ $(zlibsrcdir)/inftrees.o \
+ $(zlibsrcdir)/inffast.o
diff --git a/source4/libcli/auth/config.mk b/source4/libcli/auth/config.mk
new file mode 100644
index 0000000000..498c2af258
--- /dev/null
+++ b/source4/libcli/auth/config.mk
@@ -0,0 +1,17 @@
+#################################
+# Start SUBSYSTEM LIBCLI_AUTH
+[SUBSYSTEM::LIBCLI_AUTH]
+PUBLIC_DEPENDENCIES = \
+ MSRPC_PARSE \
+ LIBSAMBA-HOSTCONFIG
+# End SUBSYSTEM LIBCLI_AUTH
+#################################
+
+LIBCLI_AUTH_OBJ_FILES = $(addprefix $(libclisrcdir)/auth/, \
+ credentials.o \
+ session.o \
+ smbencrypt.o \
+ smbdes.o)
+
+PUBLIC_HEADERS += $(libclisrcdir)/auth/credentials.h
+$(eval $(call proto_header_template,$(libclisrcdir)/auth/proto.h,$(LIBCLI_AUTH_OBJ_FILES:.o=.c)))
diff --git a/source4/libcli/auth/credentials.c b/source4/libcli/auth/credentials.c
new file mode 100644
index 0000000000..3c77b0836d
--- /dev/null
+++ b/source4/libcli/auth/credentials.c
@@ -0,0 +1,375 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ code to manipulate domain credentials
+
+ Copyright (C) Andrew Tridgell 1997-2003
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/time.h"
+#include "auth/auth.h"
+#include "../lib/crypto/crypto.h"
+#include "libcli/auth/libcli_auth.h"
+
+/*
+ initialise the credentials state for old-style 64 bit session keys
+
+ this call is made after the netr_ServerReqChallenge call
+*/
+static void creds_init_64bit(struct creds_CredentialState *creds,
+ const struct netr_Credential *client_challenge,
+ const struct netr_Credential *server_challenge,
+ const struct samr_Password *machine_password)
+{
+ uint32_t sum[2];
+ uint8_t sum2[8];
+
+ sum[0] = IVAL(client_challenge->data, 0) + IVAL(server_challenge->data, 0);
+ sum[1] = IVAL(client_challenge->data, 4) + IVAL(server_challenge->data, 4);
+
+ SIVAL(sum2,0,sum[0]);
+ SIVAL(sum2,4,sum[1]);
+
+ ZERO_STRUCT(creds->session_key);
+
+ des_crypt128(creds->session_key, sum2, machine_password->hash);
+
+ des_crypt112(creds->client.data, client_challenge->data, creds->session_key, 1);
+ des_crypt112(creds->server.data, server_challenge->data, creds->session_key, 1);
+
+ creds->seed = creds->client;
+}
+
+/*
+ initialise the credentials state for ADS-style 128 bit session keys
+
+ this call is made after the netr_ServerReqChallenge call
+*/
+static void creds_init_128bit(struct creds_CredentialState *creds,
+ const struct netr_Credential *client_challenge,
+ const struct netr_Credential *server_challenge,
+ const struct samr_Password *machine_password)
+{
+ unsigned char zero[4], tmp[16];
+ HMACMD5Context ctx;
+ struct MD5Context md5;
+
+ ZERO_STRUCT(creds->session_key);
+
+ memset(zero, 0, sizeof(zero));
+
+ hmac_md5_init_rfc2104(machine_password->hash, sizeof(machine_password->hash), &ctx);
+ MD5Init(&md5);
+ MD5Update(&md5, zero, sizeof(zero));
+ MD5Update(&md5, client_challenge->data, 8);
+ MD5Update(&md5, server_challenge->data, 8);
+ MD5Final(tmp, &md5);
+ hmac_md5_update(tmp, sizeof(tmp), &ctx);
+ hmac_md5_final(creds->session_key, &ctx);
+
+ creds->client = *client_challenge;
+ creds->server = *server_challenge;
+
+ des_crypt112(creds->client.data, client_challenge->data, creds->session_key, 1);
+ des_crypt112(creds->server.data, server_challenge->data, creds->session_key, 1);
+
+ creds->seed = creds->client;
+}
+
+
+/*
+ step the credentials to the next element in the chain, updating the
+ current client and server credentials and the seed
+*/
+static void creds_step(struct creds_CredentialState *creds)
+{
+ struct netr_Credential time_cred;
+
+ DEBUG(5,("\tseed %08x:%08x\n",
+ IVAL(creds->seed.data, 0), IVAL(creds->seed.data, 4)));
+
+ SIVAL(time_cred.data, 0, IVAL(creds->seed.data, 0) + creds->sequence);
+ SIVAL(time_cred.data, 4, IVAL(creds->seed.data, 4));
+
+ DEBUG(5,("\tseed+time %08x:%08x\n", IVAL(time_cred.data, 0), IVAL(time_cred.data, 4)));
+
+ des_crypt112(creds->client.data, time_cred.data, creds->session_key, 1);
+
+ DEBUG(5,("\tCLIENT %08x:%08x\n",
+ IVAL(creds->client.data, 0), IVAL(creds->client.data, 4)));
+
+ SIVAL(time_cred.data, 0, IVAL(creds->seed.data, 0) + creds->sequence + 1);
+ SIVAL(time_cred.data, 4, IVAL(creds->seed.data, 4));
+
+ DEBUG(5,("\tseed+time+1 %08x:%08x\n",
+ IVAL(time_cred.data, 0), IVAL(time_cred.data, 4)));
+
+ des_crypt112(creds->server.data, time_cred.data, creds->session_key, 1);
+
+ DEBUG(5,("\tSERVER %08x:%08x\n",
+ IVAL(creds->server.data, 0), IVAL(creds->server.data, 4)));
+
+ creds->seed = time_cred;
+}
+
+
+/*
+ DES encrypt a 8 byte LMSessionKey buffer using the Netlogon session key
+*/
+void creds_des_encrypt_LMKey(struct creds_CredentialState *creds, struct netr_LMSessionKey *key)
+{
+ struct netr_LMSessionKey tmp;
+ des_crypt56(tmp.key, key->key, creds->session_key, 1);
+ *key = tmp;
+}
+
+/*
+ DES decrypt a 8 byte LMSessionKey buffer using the Netlogon session key
+*/
+void creds_des_decrypt_LMKey(struct creds_CredentialState *creds, struct netr_LMSessionKey *key)
+{
+ struct netr_LMSessionKey tmp;
+ des_crypt56(tmp.key, key->key, creds->session_key, 0);
+ *key = tmp;
+}
+
+/*
+ DES encrypt a 16 byte password buffer using the session key
+*/
+void creds_des_encrypt(struct creds_CredentialState *creds, struct samr_Password *pass)
+{
+ struct samr_Password tmp;
+ des_crypt112_16(tmp.hash, pass->hash, creds->session_key, 1);
+ *pass = tmp;
+}
+
+/*
+ DES decrypt a 16 byte password buffer using the session key
+*/
+void creds_des_decrypt(struct creds_CredentialState *creds, struct samr_Password *pass)
+{
+ struct samr_Password tmp;
+ des_crypt112_16(tmp.hash, pass->hash, creds->session_key, 0);
+ *pass = tmp;
+}
+
+/*
+ ARCFOUR encrypt/decrypt a password buffer using the session key
+*/
+void creds_arcfour_crypt(struct creds_CredentialState *creds, uint8_t *data, size_t len)
+{
+ DATA_BLOB session_key = data_blob(creds->session_key, 16);
+
+ arcfour_crypt_blob(data, len, &session_key);
+
+ data_blob_free(&session_key);
+}
+
+/*****************************************************************
+The above functions are common to the client and server interface
+next comes the client specific functions
+******************************************************************/
+
+/*
+ initialise the credentials chain and return the first client
+ credentials
+*/
+void creds_client_init(struct creds_CredentialState *creds,
+ const struct netr_Credential *client_challenge,
+ const struct netr_Credential *server_challenge,
+ const struct samr_Password *machine_password,
+ struct netr_Credential *initial_credential,
+ uint32_t negotiate_flags)
+{
+ creds->sequence = time(NULL);
+ creds->negotiate_flags = negotiate_flags;
+
+ dump_data_pw("Client chall", client_challenge->data, sizeof(client_challenge->data));
+ dump_data_pw("Server chall", server_challenge->data, sizeof(server_challenge->data));
+ dump_data_pw("Machine Pass", machine_password->hash, sizeof(machine_password->hash));
+
+ if (negotiate_flags & NETLOGON_NEG_128BIT) {
+ creds_init_128bit(creds, client_challenge, server_challenge, machine_password);
+ } else {
+ creds_init_64bit(creds, client_challenge, server_challenge, machine_password);
+ }
+
+ dump_data_pw("Session key", creds->session_key, 16);
+ dump_data_pw("Credential ", creds->client.data, 8);
+
+ *initial_credential = creds->client;
+}
+
+/*
+ step the credentials to the next element in the chain, updating the
+ current client and server credentials and the seed
+
+ produce the next authenticator in the sequence ready to send to
+ the server
+*/
+void creds_client_authenticator(struct creds_CredentialState *creds,
+ struct netr_Authenticator *next)
+{
+ creds->sequence += 2;
+ creds_step(creds);
+
+ next->cred = creds->client;
+ next->timestamp = creds->sequence;
+}
+
+/*
+ check that a credentials reply from a server is correct
+*/
+bool creds_client_check(struct creds_CredentialState *creds,
+ const struct netr_Credential *received_credentials)
+{
+ if (!received_credentials ||
+ memcmp(received_credentials->data, creds->server.data, 8) != 0) {
+ DEBUG(2,("credentials check failed\n"));
+ return false;
+ }
+ return true;
+}
+
+
+/*****************************************************************
+The above functions are common to the client and server interface
+next comes the server specific functions
+******************************************************************/
+
+/*
+ initialise the credentials chain and return the first server
+ credentials
+*/
+void creds_server_init(struct creds_CredentialState *creds,
+ const struct netr_Credential *client_challenge,
+ const struct netr_Credential *server_challenge,
+ const struct samr_Password *machine_password,
+ struct netr_Credential *initial_credential,
+ uint32_t negotiate_flags)
+{
+ if (negotiate_flags & NETLOGON_NEG_128BIT) {
+ creds_init_128bit(creds, client_challenge, server_challenge,
+ machine_password);
+ } else {
+ creds_init_64bit(creds, client_challenge, server_challenge,
+ machine_password);
+ }
+
+ *initial_credential = creds->server;
+ creds->negotiate_flags = negotiate_flags;
+}
+
+/*
+ check that a credentials reply from a server is correct
+*/
+bool creds_server_check(const struct creds_CredentialState *creds,
+ const struct netr_Credential *received_credentials)
+{
+ if (memcmp(received_credentials->data, creds->client.data, 8) != 0) {
+ DEBUG(2,("credentials check failed\n"));
+ dump_data_pw("client creds", creds->client.data, 8);
+ dump_data_pw("calc creds", received_credentials->data, 8);
+ return false;
+ }
+ return true;
+}
+
+NTSTATUS creds_server_step_check(struct creds_CredentialState *creds,
+ struct netr_Authenticator *received_authenticator,
+ struct netr_Authenticator *return_authenticator)
+{
+ if (!received_authenticator || !return_authenticator) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!creds) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* TODO: this may allow the a replay attack on a non-signed
+ connection. Should we check that this is increasing? */
+ creds->sequence = received_authenticator->timestamp;
+ creds_step(creds);
+ if (creds_server_check(creds, &received_authenticator->cred)) {
+ return_authenticator->cred = creds->server;
+ return_authenticator->timestamp = creds->sequence;
+ return NT_STATUS_OK;
+ } else {
+ ZERO_STRUCTP(return_authenticator);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+}
+
+void creds_decrypt_samlogon(struct creds_CredentialState *creds,
+ uint16_t validation_level,
+ union netr_Validation *validation)
+{
+ static const char zeros[16];
+
+ struct netr_SamBaseInfo *base = NULL;
+ switch (validation_level) {
+ case 2:
+ if (validation->sam2) {
+ base = &validation->sam2->base;
+ }
+ break;
+ case 3:
+ if (validation->sam3) {
+ base = &validation->sam3->base;
+ }
+ break;
+ case 6:
+ if (validation->sam6) {
+ base = &validation->sam6->base;
+ }
+ break;
+ default:
+ /* If we can't find it, we can't very well decrypt it */
+ return;
+ }
+
+ if (!base) {
+ return;
+ }
+
+ /* find and decyrpt the session keys, return in parameters above */
+ if (validation_level == 6) {
+ /* they aren't encrypted! */
+ } else if (creds->negotiate_flags & NETLOGON_NEG_ARCFOUR) {
+ if (memcmp(base->key.key, zeros,
+ sizeof(base->key.key)) != 0) {
+ creds_arcfour_crypt(creds,
+ base->key.key,
+ sizeof(base->key.key));
+ }
+
+ if (memcmp(base->LMSessKey.key, zeros,
+ sizeof(base->LMSessKey.key)) != 0) {
+ creds_arcfour_crypt(creds,
+ base->LMSessKey.key,
+ sizeof(base->LMSessKey.key));
+ }
+ } else {
+ if (memcmp(base->LMSessKey.key, zeros,
+ sizeof(base->LMSessKey.key)) != 0) {
+ creds_des_decrypt_LMKey(creds,
+ &base->LMSessKey);
+ }
+ }
+}
diff --git a/source4/libcli/auth/credentials.h b/source4/libcli/auth/credentials.h
new file mode 100644
index 0000000000..4e11cb090f
--- /dev/null
+++ b/source4/libcli/auth/credentials.h
@@ -0,0 +1,46 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ code to manipulate domain credentials
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "librpc/gen_ndr/netlogon.h"
+
+struct creds_CredentialState {
+ uint32_t negotiate_flags;
+ uint8_t session_key[16];
+ uint32_t sequence;
+ struct netr_Credential seed;
+ struct netr_Credential client;
+ struct netr_Credential server;
+ uint16_t secure_channel_type;
+ const char *domain;
+ const char *computer_name;
+ const char *account_name;
+ struct dom_sid *sid;
+};
+
+/* for the timebeing, use the same neg flags as Samba3. */
+/* The 7 here seems to be required to get Win2k not to downgrade us
+ to NT4. Actually, anything other than 1ff would seem to do... */
+#define NETLOGON_NEG_AUTH2_FLAGS 0x000701ff
+
+/* these are the flags that ADS clients use */
+#define NETLOGON_NEG_AUTH2_ADS_FLAGS (0x200fbffb | NETLOGON_NEG_ARCFOUR | NETLOGON_NEG_128BIT | NETLOGON_NEG_SCHANNEL)
+
+
diff --git a/source4/libcli/auth/libcli_auth.h b/source4/libcli/auth/libcli_auth.h
new file mode 100644
index 0000000000..ec1c1e7d98
--- /dev/null
+++ b/source4/libcli/auth/libcli_auth.h
@@ -0,0 +1,24 @@
+/*
+ samba -- Unix SMB/CIFS implementation.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef __LIBCLI_AUTH_H__
+#define __LIBCLI_AUTH_H__
+
+#include "librpc/gen_ndr/netlogon.h"
+#include "libcli/auth/credentials.h"
+#include "libcli/auth/proto.h"
+
+#endif /* __LIBCLI_AUTH_H__ */
diff --git a/source4/libcli/auth/session.c b/source4/libcli/auth/session.c
new file mode 100644
index 0000000000..10c728662d
--- /dev/null
+++ b/source4/libcli/auth/session.c
@@ -0,0 +1,218 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ code to encrypt/decrypt data using the user session key
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/auth/libcli_auth.h"
+
+/*
+ encrypt or decrypt a blob of data using the user session key
+ as used in lsa_SetSecret
+
+ before calling, the out blob must be initialised to be the same size
+ as the in blob
+*/
+void sess_crypt_blob(DATA_BLOB *out, const DATA_BLOB *in, const DATA_BLOB *session_key,
+ bool forward)
+{
+ int i, k;
+
+ for (i=0,k=0;
+ i<in->length;
+ i += 8, k += 7) {
+ uint8_t bin[8], bout[8], key[7];
+
+ memset(bin, 0, 8);
+ memcpy(bin, &in->data[i], MIN(8, in->length-i));
+
+ if (k + 7 > session_key->length) {
+ k = (session_key->length - k);
+ }
+ memcpy(key, &session_key->data[k], 7);
+
+ des_crypt56(bout, bin, key, forward?1:0);
+
+ memcpy(&out->data[i], bout, MIN(8, in->length-i));
+ }
+}
+
+
+/*
+ a convenient wrapper around sess_crypt_blob() for strings, using the LSA convention
+
+ note that we round the length to a multiple of 8. This seems to be needed for
+ compatibility with windows
+
+ caller should free using data_blob_free()
+*/
+DATA_BLOB sess_encrypt_string(const char *str, const DATA_BLOB *session_key)
+{
+ DATA_BLOB ret, src;
+ int slen = strlen(str);
+ int dlen = (slen+7) & ~7;
+
+ src = data_blob(NULL, 8+dlen);
+ if (!src.data) {
+ return data_blob(NULL, 0);
+ }
+
+ ret = data_blob(NULL, 8+dlen);
+ if (!ret.data) {
+ data_blob_free(&src);
+ return data_blob(NULL, 0);
+ }
+
+ SIVAL(src.data, 0, slen);
+ SIVAL(src.data, 4, 1);
+ memset(src.data+8, 0, dlen);
+ memcpy(src.data+8, str, slen);
+
+ sess_crypt_blob(&ret, &src, session_key, true);
+
+ data_blob_free(&src);
+
+ return ret;
+}
+
+/*
+ a convenient wrapper around sess_crypt_blob() for strings, using the LSA convention
+
+ caller should free the returned string
+*/
+char *sess_decrypt_string(TALLOC_CTX *mem_ctx,
+ DATA_BLOB *blob, const DATA_BLOB *session_key)
+{
+ DATA_BLOB out;
+ int slen;
+ char *ret;
+
+ if (blob->length < 8) {
+ return NULL;
+ }
+
+ out = data_blob_talloc(mem_ctx, NULL, blob->length);
+ if (!out.data) {
+ return NULL;
+ }
+
+ sess_crypt_blob(&out, blob, session_key, false);
+
+ if (IVAL(out.data, 4) != 1) {
+ DEBUG(0,("Unexpected revision number %d in session crypted string\n",
+ IVAL(out.data, 4)));
+ data_blob_free(&out);
+ return NULL;
+ }
+
+ slen = IVAL(out.data, 0);
+ if (slen > blob->length - 8) {
+ DEBUG(0,("Invalid crypt length %d\n", slen));
+ data_blob_free(&out);
+ return NULL;
+ }
+
+ ret = talloc_strndup(mem_ctx, (const char *)(out.data+8), slen);
+
+ data_blob_free(&out);
+
+ DEBUG(0,("decrypted string '%s' of length %d\n", ret, slen));
+
+ return ret;
+}
+
+/*
+ a convenient wrapper around sess_crypt_blob() for DATA_BLOBs, using the LSA convention
+
+ note that we round the length to a multiple of 8. This seems to be needed for
+ compatibility with windows
+
+ caller should free using data_blob_free()
+*/
+DATA_BLOB sess_encrypt_blob(TALLOC_CTX *mem_ctx, DATA_BLOB *blob_in, const DATA_BLOB *session_key)
+{
+ DATA_BLOB ret, src;
+ int dlen = (blob_in->length+7) & ~7;
+
+ src = data_blob_talloc(mem_ctx, NULL, 8+dlen);
+ if (!src.data) {
+ return data_blob(NULL, 0);
+ }
+
+ ret = data_blob_talloc(mem_ctx, NULL, 8+dlen);
+ if (!ret.data) {
+ data_blob_free(&src);
+ return data_blob(NULL, 0);
+ }
+
+ SIVAL(src.data, 0, blob_in->length);
+ SIVAL(src.data, 4, 1);
+ memset(src.data+8, 0, dlen);
+ memcpy(src.data+8, blob_in->data, blob_in->length);
+
+ sess_crypt_blob(&ret, &src, session_key, true);
+
+ data_blob_free(&src);
+
+ return ret;
+}
+
+/*
+ Decrypt a DATA_BLOB using the LSA convention
+*/
+NTSTATUS sess_decrypt_blob(TALLOC_CTX *mem_ctx, const DATA_BLOB *blob, const DATA_BLOB *session_key,
+ DATA_BLOB *ret)
+{
+ DATA_BLOB out;
+ int slen;
+
+ if (blob->length < 8) {
+ DEBUG(0, ("Unexpected length %d in session crypted secret (BLOB)\n",
+ (int)blob->length));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ out = data_blob_talloc(mem_ctx, NULL, blob->length);
+ if (!out.data) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ sess_crypt_blob(&out, blob, session_key, false);
+
+ if (IVAL(out.data, 4) != 1) {
+ DEBUG(2,("Unexpected revision number %d in session crypted secret (BLOB)\n",
+ IVAL(out.data, 4)));
+ return NT_STATUS_UNKNOWN_REVISION;
+ }
+
+ slen = IVAL(out.data, 0);
+ if (slen > blob->length - 8) {
+ DEBUG(0,("Invalid crypt length %d in session crypted secret (BLOB)\n", slen));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ *ret = data_blob_talloc(mem_ctx, out.data+8, slen);
+ if (slen && !ret->data) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ data_blob_free(&out);
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/libcli/auth/smbdes.c b/source4/libcli/auth/smbdes.c
new file mode 100644
index 0000000000..32e65e779d
--- /dev/null
+++ b/source4/libcli/auth/smbdes.c
@@ -0,0 +1,381 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ a partial implementation of DES designed for use in the
+ SMB authentication protocol
+
+ Copyright (C) Andrew Tridgell 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/auth/libcli_auth.h"
+
+/* NOTES:
+
+ This code makes no attempt to be fast! In fact, it is a very
+ slow implementation
+
+ This code is NOT a complete DES implementation. It implements only
+ the minimum necessary for SMB authentication, as used by all SMB
+ products (including every copy of Microsoft Windows95 ever sold)
+
+ In particular, it can only do a unchained forward DES pass. This
+ means it is not possible to use this code for encryption/decryption
+ of data, instead it is only useful as a "hash" algorithm.
+
+ There is no entry point into this code that allows normal DES operation.
+
+ I believe this means that this code does not come under ITAR
+ regulations but this is NOT a legal opinion. If you are concerned
+ about the applicability of ITAR regulations to this code then you
+ should confirm it for yourself (and maybe let me know if you come
+ up with a different answer to the one above)
+*/
+
+
+static const uint8_t perm1[56] = {57, 49, 41, 33, 25, 17, 9,
+ 1, 58, 50, 42, 34, 26, 18,
+ 10, 2, 59, 51, 43, 35, 27,
+ 19, 11, 3, 60, 52, 44, 36,
+ 63, 55, 47, 39, 31, 23, 15,
+ 7, 62, 54, 46, 38, 30, 22,
+ 14, 6, 61, 53, 45, 37, 29,
+ 21, 13, 5, 28, 20, 12, 4};
+
+static const uint8_t perm2[48] = {14, 17, 11, 24, 1, 5,
+ 3, 28, 15, 6, 21, 10,
+ 23, 19, 12, 4, 26, 8,
+ 16, 7, 27, 20, 13, 2,
+ 41, 52, 31, 37, 47, 55,
+ 30, 40, 51, 45, 33, 48,
+ 44, 49, 39, 56, 34, 53,
+ 46, 42, 50, 36, 29, 32};
+
+static const uint8_t perm3[64] = {58, 50, 42, 34, 26, 18, 10, 2,
+ 60, 52, 44, 36, 28, 20, 12, 4,
+ 62, 54, 46, 38, 30, 22, 14, 6,
+ 64, 56, 48, 40, 32, 24, 16, 8,
+ 57, 49, 41, 33, 25, 17, 9, 1,
+ 59, 51, 43, 35, 27, 19, 11, 3,
+ 61, 53, 45, 37, 29, 21, 13, 5,
+ 63, 55, 47, 39, 31, 23, 15, 7};
+
+static const uint8_t perm4[48] = { 32, 1, 2, 3, 4, 5,
+ 4, 5, 6, 7, 8, 9,
+ 8, 9, 10, 11, 12, 13,
+ 12, 13, 14, 15, 16, 17,
+ 16, 17, 18, 19, 20, 21,
+ 20, 21, 22, 23, 24, 25,
+ 24, 25, 26, 27, 28, 29,
+ 28, 29, 30, 31, 32, 1};
+
+static const uint8_t perm5[32] = { 16, 7, 20, 21,
+ 29, 12, 28, 17,
+ 1, 15, 23, 26,
+ 5, 18, 31, 10,
+ 2, 8, 24, 14,
+ 32, 27, 3, 9,
+ 19, 13, 30, 6,
+ 22, 11, 4, 25};
+
+
+static const uint8_t perm6[64] ={ 40, 8, 48, 16, 56, 24, 64, 32,
+ 39, 7, 47, 15, 55, 23, 63, 31,
+ 38, 6, 46, 14, 54, 22, 62, 30,
+ 37, 5, 45, 13, 53, 21, 61, 29,
+ 36, 4, 44, 12, 52, 20, 60, 28,
+ 35, 3, 43, 11, 51, 19, 59, 27,
+ 34, 2, 42, 10, 50, 18, 58, 26,
+ 33, 1, 41, 9, 49, 17, 57, 25};
+
+
+static const uint8_t sc[16] = {1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1};
+
+static const uint8_t sbox[8][4][16] = {
+ {{14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7},
+ {0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8},
+ {4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0},
+ {15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13}},
+
+ {{15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10},
+ {3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5},
+ {0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15},
+ {13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9}},
+
+ {{10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8},
+ {13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1},
+ {13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7},
+ {1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12}},
+
+ {{7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15},
+ {13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9},
+ {10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4},
+ {3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14}},
+
+ {{2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9},
+ {14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6},
+ {4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14},
+ {11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3}},
+
+ {{12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11},
+ {10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8},
+ {9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6},
+ {4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13}},
+
+ {{4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1},
+ {13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6},
+ {1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2},
+ {6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12}},
+
+ {{13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7},
+ {1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2},
+ {7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8},
+ {2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11}}};
+
+static void permute(char *out, const char *in, const uint8_t *p, int n)
+{
+ int i;
+ for (i=0;i<n;i++)
+ out[i] = in[p[i]-1];
+}
+
+static void lshift(char *d, int count, int n)
+{
+ char out[64];
+ int i;
+ for (i=0;i<n;i++)
+ out[i] = d[(i+count)%n];
+ for (i=0;i<n;i++)
+ d[i] = out[i];
+}
+
+static void concat(char *out, char *in1, char *in2, int l1, int l2)
+{
+ while (l1--)
+ *out++ = *in1++;
+ while (l2--)
+ *out++ = *in2++;
+}
+
+static void xor(char *out, char *in1, char *in2, int n)
+{
+ int i;
+ for (i=0;i<n;i++)
+ out[i] = in1[i] ^ in2[i];
+}
+
+static void dohash(char *out, char *in, char *key, int forw)
+{
+ int i, j, k;
+ char pk1[56];
+ char c[28];
+ char d[28];
+ char cd[56];
+ char ki[16][48];
+ char pd1[64];
+ char l[32], r[32];
+ char rl[64];
+
+ permute(pk1, key, perm1, 56);
+
+ for (i=0;i<28;i++)
+ c[i] = pk1[i];
+ for (i=0;i<28;i++)
+ d[i] = pk1[i+28];
+
+ for (i=0;i<16;i++) {
+ lshift(c, sc[i], 28);
+ lshift(d, sc[i], 28);
+
+ concat(cd, c, d, 28, 28);
+ permute(ki[i], cd, perm2, 48);
+ }
+
+ permute(pd1, in, perm3, 64);
+
+ for (j=0;j<32;j++) {
+ l[j] = pd1[j];
+ r[j] = pd1[j+32];
+ }
+
+ for (i=0;i<16;i++) {
+ char er[48];
+ char erk[48];
+ char b[8][6];
+ char cb[32];
+ char pcb[32];
+ char r2[32];
+
+ permute(er, r, perm4, 48);
+
+ xor(erk, er, ki[forw ? i : 15 - i], 48);
+
+ for (j=0;j<8;j++)
+ for (k=0;k<6;k++)
+ b[j][k] = erk[j*6 + k];
+
+ for (j=0;j<8;j++) {
+ int m, n;
+ m = (b[j][0]<<1) | b[j][5];
+
+ n = (b[j][1]<<3) | (b[j][2]<<2) | (b[j][3]<<1) | b[j][4];
+
+ for (k=0;k<4;k++)
+ b[j][k] = (sbox[j][m][n] & (1<<(3-k)))?1:0;
+ }
+
+ for (j=0;j<8;j++)
+ for (k=0;k<4;k++)
+ cb[j*4+k] = b[j][k];
+ permute(pcb, cb, perm5, 32);
+
+ xor(r2, l, pcb, 32);
+
+ for (j=0;j<32;j++)
+ l[j] = r[j];
+
+ for (j=0;j<32;j++)
+ r[j] = r2[j];
+ }
+
+ concat(rl, r, l, 32, 32);
+
+ permute(out, rl, perm6, 64);
+}
+
+static void str_to_key(const uint8_t *str,uint8_t *key)
+{
+ int i;
+
+ key[0] = str[0]>>1;
+ key[1] = ((str[0]&0x01)<<6) | (str[1]>>2);
+ key[2] = ((str[1]&0x03)<<5) | (str[2]>>3);
+ key[3] = ((str[2]&0x07)<<4) | (str[3]>>4);
+ key[4] = ((str[3]&0x0F)<<3) | (str[4]>>5);
+ key[5] = ((str[4]&0x1F)<<2) | (str[5]>>6);
+ key[6] = ((str[5]&0x3F)<<1) | (str[6]>>7);
+ key[7] = str[6]&0x7F;
+ for (i=0;i<8;i++) {
+ key[i] = (key[i]<<1);
+ }
+}
+
+/*
+ basic des crypt using a 56 bit (7 byte) key
+*/
+void des_crypt56(uint8_t out[8], const uint8_t in[8], const uint8_t key[7], int forw)
+{
+ int i;
+ char outb[64];
+ char inb[64];
+ char keyb[64];
+ uint8_t key2[8];
+
+ str_to_key(key, key2);
+
+ for (i=0;i<64;i++) {
+ inb[i] = (in[i/8] & (1<<(7-(i%8)))) ? 1 : 0;
+ keyb[i] = (key2[i/8] & (1<<(7-(i%8)))) ? 1 : 0;
+ outb[i] = 0;
+ }
+
+ dohash(outb, inb, keyb, forw);
+
+ for (i=0;i<8;i++) {
+ out[i] = 0;
+ }
+
+ for (i=0;i<64;i++) {
+ if (outb[i])
+ out[i/8] |= (1<<(7-(i%8)));
+ }
+}
+
+void E_P16(const uint8_t *p14,uint8_t *p16)
+{
+ const uint8_t sp8[8] = {0x4b, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25};
+ des_crypt56(p16, sp8, p14, 1);
+ des_crypt56(p16+8, sp8, p14+7, 1);
+}
+
+void E_P24(const uint8_t *p21, const uint8_t *c8, uint8_t *p24)
+{
+ des_crypt56(p24, c8, p21, 1);
+ des_crypt56(p24+8, c8, p21+7, 1);
+ des_crypt56(p24+16, c8, p21+14, 1);
+}
+
+void D_P16(const uint8_t *p14, const uint8_t *in, uint8_t *out)
+{
+ des_crypt56(out, in, p14, 0);
+ des_crypt56(out+8, in+8, p14+7, 0);
+}
+
+void E_old_pw_hash( uint8_t *p14, const uint8_t *in, uint8_t *out)
+{
+ des_crypt56(out, in, p14, 1);
+ des_crypt56(out+8, in+8, p14+7, 1);
+}
+
+/* des encryption with a 128 bit key */
+void des_crypt128(uint8_t out[8], const uint8_t in[8], const uint8_t key[16])
+{
+ uint8_t buf[8];
+ des_crypt56(buf, in, key, 1);
+ des_crypt56(out, buf, key+9, 1);
+}
+
+/* des encryption with a 64 bit key */
+void des_crypt64(uint8_t out[8], const uint8_t in[8], const uint8_t key[8], int forw)
+{
+ uint8_t buf[8];
+ uint8_t key2[8];
+ ZERO_STRUCT(key2);
+ des_crypt56(buf, in, key, forw);
+ key2[0] = key[7];
+ des_crypt56(out, buf, key2, forw);
+}
+
+/* des encryption with a 112 bit (14 byte) key */
+void des_crypt112(uint8_t out[8], const uint8_t in[8], const uint8_t key[14], int forw)
+{
+ uint8_t buf[8];
+ des_crypt56(buf, in, key, forw);
+ des_crypt56(out, buf, key+7, forw);
+}
+
+/* des encryption of a 16 byte lump of data with a 112 bit key */
+void des_crypt112_16(uint8_t out[16], uint8_t in[16], const uint8_t key[14], int forw)
+{
+ des_crypt56(out, in, key, forw);
+ des_crypt56(out + 8, in + 8, key+7, forw);
+}
+
+/* Decode a sam password hash into a password. The password hash is the
+ same method used to store passwords in the NT registry. The DES key
+ used is based on the RID of the user. */
+void sam_rid_crypt(uint_t rid, const uint8_t *in, uint8_t *out, int forw)
+{
+ uint8_t s[14];
+
+ s[0] = s[4] = s[8] = s[12] = (uint8_t)(rid & 0xFF);
+ s[1] = s[5] = s[9] = s[13] = (uint8_t)((rid >> 8) & 0xFF);
+ s[2] = s[6] = s[10] = (uint8_t)((rid >> 16) & 0xFF);
+ s[3] = s[7] = s[11] = (uint8_t)((rid >> 24) & 0xFF);
+
+ des_crypt56(out, in, s, forw);
+ des_crypt56(out+8, in+8, s+7, forw);
+}
diff --git a/source4/libcli/auth/smbencrypt.c b/source4/libcli/auth/smbencrypt.c
new file mode 100644
index 0000000000..c6118c6568
--- /dev/null
+++ b/source4/libcli/auth/smbencrypt.c
@@ -0,0 +1,595 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB parameters and setup
+ Copyright (C) Andrew Tridgell 1992-1998
+ Modified by Jeremy Allison 1995.
+ Copyright (C) Jeremy Allison 1995-2000.
+ Copyright (C) Luke Kennethc Casson Leighton 1996-2000.
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2002-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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/time.h"
+#include "auth/ntlmssp/ntlmssp.h"
+#include "auth/ntlmssp/msrpc_parse.h"
+#include "../lib/crypto/crypto.h"
+#include "libcli/auth/libcli_auth.h"
+
+/*
+ This implements the X/Open SMB password encryption
+ It takes a password ('unix' string), a 8 byte "crypt key"
+ and puts 24 bytes of encrypted password into p24
+
+ Returns false if password must have been truncated to create LM hash
+*/
+bool SMBencrypt(const char *passwd, const uint8_t *c8, uint8_t p24[24])
+{
+ bool ret;
+ uint8_t p21[21];
+
+ memset(p21,'\0',21);
+ ret = E_deshash(passwd, p21);
+
+ SMBOWFencrypt(p21, c8, p24);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("SMBencrypt: lm#, challenge, response\n"));
+ dump_data(100, p21, 16);
+ dump_data(100, c8, 8);
+ dump_data(100, p24, 24);
+#endif
+
+ return ret;
+}
+
+/**
+ * Creates the MD4 Hash of the users password in NT UNICODE.
+ * @param passwd password in 'unix' charset.
+ * @param p16 return password hashed with md4, caller allocated 16 byte buffer
+ */
+
+bool E_md4hash(const char *passwd, uint8_t p16[16])
+{
+ size_t len;
+ smb_ucs2_t *wpwd;
+ bool ret;
+
+ ret = push_ucs2_talloc(NULL, &wpwd, passwd, &len);
+ if (!ret || len < 2) {
+ /* We don't want to return fixed data, as most callers
+ * don't check */
+ mdfour(p16, (const uint8_t *)passwd, strlen(passwd));
+ return false;
+ }
+
+ len -= 2;
+ mdfour(p16, (const uint8_t *)wpwd, len);
+
+ talloc_free(wpwd);
+ return true;
+}
+
+/**
+ * Creates the DES forward-only Hash of the users password in DOS ASCII charset
+ * @param passwd password in 'unix' charset.
+ * @param p16 return password hashed with DES, caller allocated 16 byte buffer
+ * @return false if password was > 14 characters, and therefore may be incorrect, otherwise true
+ * @note p16 is filled in regardless
+ */
+
+bool E_deshash(const char *passwd, uint8_t p16[16])
+{
+ bool ret = true;
+ char dospwd[256];
+ ZERO_STRUCT(dospwd);
+
+ /* Password must be converted to DOS charset - null terminated, uppercase. */
+ push_string(dospwd, passwd, sizeof(dospwd), STR_ASCII|STR_UPPER|STR_TERMINATE);
+
+ /* Only the first 14 chars are considered, password need not be null terminated. */
+ E_P16((const uint8_t *)dospwd, p16);
+
+ if (strlen(dospwd) > 14) {
+ ret = false;
+ }
+
+ ZERO_STRUCT(dospwd);
+
+ return ret;
+}
+
+/* Does both the NTLMv2 owfs of a user's password */
+bool ntv2_owf_gen(const uint8_t owf[16],
+ const char *user_in, const char *domain_in,
+ bool upper_case_domain, /* Transform the domain into UPPER case */
+ uint8_t kr_buf[16])
+{
+ smb_ucs2_t *user;
+ smb_ucs2_t *domain;
+ size_t user_byte_len;
+ size_t domain_byte_len;
+ bool ret;
+
+ HMACMD5Context ctx;
+ TALLOC_CTX *mem_ctx = talloc_init("ntv2_owf_gen for %s\\%s", domain_in, user_in);
+
+ if (!mem_ctx) {
+ return false;
+ }
+
+ if (!user_in) {
+ user_in = "";
+ }
+
+ if (!domain_in) {
+ domain_in = "";
+ }
+
+ user_in = strupper_talloc(mem_ctx, user_in);
+ if (user_in == NULL) {
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ if (upper_case_domain) {
+ domain_in = strupper_talloc(mem_ctx, domain_in);
+ if (domain_in == NULL) {
+ talloc_free(mem_ctx);
+ return false;
+ }
+ }
+
+ ret = push_ucs2_talloc(mem_ctx, &user, user_in, &user_byte_len );
+ if (!ret) {
+ DEBUG(0, ("push_uss2_talloc() for user returned -1 (probably talloc() failure)\n"));
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ ret = push_ucs2_talloc(mem_ctx, &domain, domain_in, &domain_byte_len);
+ if (!ret) {
+ DEBUG(0, ("push_ucs2_talloc() for domain returned -1 (probably talloc() failure)\n"));
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ SMB_ASSERT(user_byte_len >= 2);
+ SMB_ASSERT(domain_byte_len >= 2);
+
+ /* We don't want null termination */
+ user_byte_len = user_byte_len - 2;
+ domain_byte_len = domain_byte_len - 2;
+
+ hmac_md5_init_limK_to_64(owf, 16, &ctx);
+ hmac_md5_update((const void *)user, user_byte_len, &ctx);
+ hmac_md5_update((const void *)domain, domain_byte_len, &ctx);
+ hmac_md5_final(kr_buf, &ctx);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100, ("ntv2_owf_gen: user, domain, owfkey, kr\n"));
+ dump_data(100, (const void *)user, user_byte_len);
+ dump_data(100, (const void *)domain, domain_byte_len);
+ dump_data(100, owf, 16);
+ dump_data(100, kr_buf, 16);
+#endif
+
+ talloc_free(mem_ctx);
+ return true;
+}
+
+/* Does the des encryption from the NT or LM MD4 hash. */
+void SMBOWFencrypt(const uint8_t passwd[16], const uint8_t *c8, uint8_t p24[24])
+{
+ uint8_t p21[21];
+
+ ZERO_STRUCT(p21);
+
+ memcpy(p21, passwd, 16);
+ E_P24(p21, c8, p24);
+}
+
+/* Does the NT MD4 hash then des encryption. */
+
+void SMBNTencrypt(const char *passwd, uint8_t *c8, uint8_t *p24)
+{
+ uint8_t p21[21];
+
+ memset(p21,'\0',21);
+
+ E_md4hash(passwd, p21);
+ SMBOWFencrypt(p21, c8, p24);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("SMBNTencrypt: nt#, challenge, response\n"));
+ dump_data(100, p21, 16);
+ dump_data(100, c8, 8);
+ dump_data(100, p24, 24);
+#endif
+}
+
+/* Does the md5 encryption from the Key Response for NTLMv2. */
+void SMBOWFencrypt_ntv2(const uint8_t kr[16],
+ const DATA_BLOB *srv_chal,
+ const DATA_BLOB *smbcli_chal,
+ uint8_t resp_buf[16])
+{
+ HMACMD5Context ctx;
+
+ hmac_md5_init_limK_to_64(kr, 16, &ctx);
+ hmac_md5_update(srv_chal->data, srv_chal->length, &ctx);
+ hmac_md5_update(smbcli_chal->data, smbcli_chal->length, &ctx);
+ hmac_md5_final(resp_buf, &ctx);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100, ("SMBOWFencrypt_ntv2: srv_chal, smbcli_chal, resp_buf\n"));
+ dump_data(100, srv_chal->data, srv_chal->length);
+ dump_data(100, smbcli_chal->data, smbcli_chal->length);
+ dump_data(100, resp_buf, 16);
+#endif
+}
+
+void SMBsesskeygen_ntv2(const uint8_t kr[16],
+ const uint8_t * nt_resp, uint8_t sess_key[16])
+{
+ /* a very nice, 128 bit, variable session key */
+
+ HMACMD5Context ctx;
+
+ hmac_md5_init_limK_to_64(kr, 16, &ctx);
+ hmac_md5_update(nt_resp, 16, &ctx);
+ hmac_md5_final((uint8_t *)sess_key, &ctx);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100, ("SMBsesskeygen_ntv2:\n"));
+ dump_data(100, sess_key, 16);
+#endif
+}
+
+void SMBsesskeygen_ntv1(const uint8_t kr[16], uint8_t sess_key[16])
+{
+ /* yes, this session key does not change - yes, this
+ is a problem - but it is 128 bits */
+
+ mdfour((uint8_t *)sess_key, kr, 16);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100, ("SMBsesskeygen_ntv1:\n"));
+ dump_data(100, sess_key, 16);
+#endif
+}
+
+void SMBsesskeygen_lm_sess_key(const uint8_t lm_hash[16],
+ const uint8_t lm_resp[24], /* only uses 8 */
+ uint8_t sess_key[16])
+{
+ /* Calculate the LM session key (effective length 40 bits,
+ but changes with each session) */
+ uint8_t p24[24];
+ uint8_t partial_lm_hash[14];
+
+ memcpy(partial_lm_hash, lm_hash, 8);
+ memset(partial_lm_hash + 8, 0xbd, 6);
+
+ des_crypt56(p24, lm_resp, partial_lm_hash, 1);
+ des_crypt56(p24+8, lm_resp, partial_lm_hash + 7, 1);
+
+ memcpy(sess_key, p24, 16);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100, ("SMBsesskeygen_lm_sess_key: \n"));
+ dump_data(100, sess_key, 16);
+#endif
+}
+
+DATA_BLOB NTLMv2_generate_names_blob(TALLOC_CTX *mem_ctx,
+ const char *hostname,
+ const char *domain)
+{
+ DATA_BLOB names_blob = data_blob_talloc(mem_ctx, NULL, 0);
+
+ msrpc_gen(mem_ctx, &names_blob,
+ "aaa",
+ NTLMSSP_NAME_TYPE_DOMAIN, domain,
+ NTLMSSP_NAME_TYPE_SERVER, hostname,
+ 0, "");
+ return names_blob;
+}
+
+static DATA_BLOB NTLMv2_generate_client_data(TALLOC_CTX *mem_ctx, const DATA_BLOB *names_blob)
+{
+ uint8_t client_chal[8];
+ DATA_BLOB response = data_blob(NULL, 0);
+ uint8_t long_date[8];
+ NTTIME nttime;
+
+ unix_to_nt_time(&nttime, time(NULL));
+
+ generate_random_buffer(client_chal, sizeof(client_chal));
+
+ push_nttime(long_date, 0, nttime);
+
+ /* See http://www.ubiqx.org/cifs/SMB.html#SMB.8.5 */
+
+ msrpc_gen(mem_ctx, &response, "ddbbdb",
+ 0x00000101, /* Header */
+ 0, /* 'Reserved' */
+ long_date, 8, /* Timestamp */
+ client_chal, 8, /* client challenge */
+ 0, /* Unknown */
+ names_blob->data, names_blob->length); /* End of name list */
+
+ return response;
+}
+
+static DATA_BLOB NTLMv2_generate_response(TALLOC_CTX *out_mem_ctx,
+ const uint8_t ntlm_v2_hash[16],
+ const DATA_BLOB *server_chal,
+ const DATA_BLOB *names_blob)
+{
+ uint8_t ntlmv2_response[16];
+ DATA_BLOB ntlmv2_client_data;
+ DATA_BLOB final_response;
+
+ TALLOC_CTX *mem_ctx = talloc_named(out_mem_ctx, 0,
+ "NTLMv2_generate_response internal context");
+
+ if (!mem_ctx) {
+ return data_blob(NULL, 0);
+ }
+
+ /* NTLMv2 */
+ /* generate some data to pass into the response function - including
+ the hostname and domain name of the server */
+ ntlmv2_client_data = NTLMv2_generate_client_data(mem_ctx, names_blob);
+
+ /* Given that data, and the challenge from the server, generate a response */
+ SMBOWFencrypt_ntv2(ntlm_v2_hash, server_chal, &ntlmv2_client_data, ntlmv2_response);
+
+ final_response = data_blob_talloc(out_mem_ctx, NULL, sizeof(ntlmv2_response) + ntlmv2_client_data.length);
+
+ memcpy(final_response.data, ntlmv2_response, sizeof(ntlmv2_response));
+
+ memcpy(final_response.data+sizeof(ntlmv2_response),
+ ntlmv2_client_data.data, ntlmv2_client_data.length);
+
+ talloc_free(mem_ctx);
+
+ return final_response;
+}
+
+static DATA_BLOB LMv2_generate_response(TALLOC_CTX *mem_ctx,
+ const uint8_t ntlm_v2_hash[16],
+ const DATA_BLOB *server_chal)
+{
+ uint8_t lmv2_response[16];
+ DATA_BLOB lmv2_client_data = data_blob_talloc(mem_ctx, NULL, 8);
+ DATA_BLOB final_response = data_blob_talloc(mem_ctx, NULL,24);
+
+ /* LMv2 */
+ /* client-supplied random data */
+ generate_random_buffer(lmv2_client_data.data, lmv2_client_data.length);
+
+ /* Given that data, and the challenge from the server, generate a response */
+ SMBOWFencrypt_ntv2(ntlm_v2_hash, server_chal, &lmv2_client_data, lmv2_response);
+ memcpy(final_response.data, lmv2_response, sizeof(lmv2_response));
+
+ /* after the first 16 bytes is the random data we generated above,
+ so the server can verify us with it */
+ memcpy(final_response.data+sizeof(lmv2_response),
+ lmv2_client_data.data, lmv2_client_data.length);
+
+ data_blob_free(&lmv2_client_data);
+
+ return final_response;
+}
+
+bool SMBNTLMv2encrypt_hash(TALLOC_CTX *mem_ctx,
+ const char *user, const char *domain, const uint8_t nt_hash[16],
+ const DATA_BLOB *server_chal,
+ const DATA_BLOB *names_blob,
+ DATA_BLOB *lm_response, DATA_BLOB *nt_response,
+ DATA_BLOB *lm_session_key, DATA_BLOB *user_session_key)
+{
+ uint8_t ntlm_v2_hash[16];
+
+ /* We don't use the NT# directly. Instead we use it mashed up with
+ the username and domain.
+ This prevents username swapping during the auth exchange
+ */
+ if (!ntv2_owf_gen(nt_hash, user, domain, true, ntlm_v2_hash)) {
+ return false;
+ }
+
+ if (nt_response) {
+ *nt_response = NTLMv2_generate_response(mem_ctx,
+ ntlm_v2_hash, server_chal,
+ names_blob);
+ if (user_session_key) {
+ *user_session_key = data_blob_talloc(mem_ctx, NULL, 16);
+
+ /* The NTLMv2 calculations also provide a session key, for signing etc later */
+ /* use only the first 16 bytes of nt_response for session key */
+ SMBsesskeygen_ntv2(ntlm_v2_hash, nt_response->data, user_session_key->data);
+ }
+ }
+
+ /* LMv2 */
+
+ if (lm_response) {
+ *lm_response = LMv2_generate_response(mem_ctx,
+ ntlm_v2_hash, server_chal);
+ if (lm_session_key) {
+ *lm_session_key = data_blob_talloc(mem_ctx, NULL, 16);
+
+ /* The NTLMv2 calculations also provide a session key, for signing etc later */
+ /* use only the first 16 bytes of lm_response for session key */
+ SMBsesskeygen_ntv2(ntlm_v2_hash, lm_response->data, lm_session_key->data);
+ }
+ }
+
+ return true;
+}
+
+bool SMBNTLMv2encrypt(TALLOC_CTX *mem_ctx,
+ const char *user, const char *domain,
+ const char *password,
+ const DATA_BLOB *server_chal,
+ const DATA_BLOB *names_blob,
+ DATA_BLOB *lm_response, DATA_BLOB *nt_response,
+ DATA_BLOB *lm_session_key, DATA_BLOB *user_session_key)
+{
+ uint8_t nt_hash[16];
+ E_md4hash(password, nt_hash);
+
+ return SMBNTLMv2encrypt_hash(mem_ctx,
+ user, domain, nt_hash, server_chal, names_blob,
+ lm_response, nt_response, lm_session_key, user_session_key);
+}
+
+/***********************************************************
+ encode a password buffer with a unicode password. The buffer
+ is filled with random data to make it harder to attack.
+************************************************************/
+bool encode_pw_buffer(uint8_t buffer[516], const char *password, int string_flags)
+{
+ uint8_t new_pw[512];
+ size_t new_pw_len;
+
+ /* the incoming buffer can be any alignment. */
+ string_flags |= STR_NOALIGN;
+
+ new_pw_len = push_string(new_pw,
+ password,
+ sizeof(new_pw), string_flags);
+
+ memcpy(&buffer[512 - new_pw_len], new_pw, new_pw_len);
+
+ generate_random_buffer(buffer, 512 - new_pw_len);
+
+ /*
+ * The length of the new password is in the last 4 bytes of
+ * the data buffer.
+ */
+ SIVAL(buffer, 512, new_pw_len);
+ ZERO_STRUCT(new_pw);
+ return true;
+}
+
+
+/***********************************************************
+ decode a password buffer
+ *new_pw_len is the length in bytes of the possibly mulitbyte
+ returned password including termination.
+************************************************************/
+bool decode_pw_buffer(uint8_t in_buffer[516], char *new_pwrd,
+ int new_pwrd_size, int string_flags)
+{
+ int byte_len=0;
+ ssize_t converted_pw_len;
+
+ /* the incoming buffer can be any alignment. */
+ string_flags |= STR_NOALIGN;
+
+ /*
+ Warning !!! : This function is called from some rpc call.
+ The password IN the buffer may be a UNICODE string.
+ The password IN new_pwrd is an ASCII string
+ If you reuse that code somewhere else check first.
+ */
+
+ /* The length of the new password is in the last 4 bytes of the data buffer. */
+
+ byte_len = IVAL(in_buffer, 512);
+
+#ifdef DEBUG_PASSWORD
+ dump_data(100, in_buffer, 516);
+#endif
+
+ /* Password cannot be longer than the size of the password buffer */
+ if ( (byte_len < 0) || (byte_len > 512)) {
+ return false;
+ }
+
+ /* decode into the return buffer. Buffer length supplied */
+ converted_pw_len = pull_string(new_pwrd, &in_buffer[512 - byte_len], new_pwrd_size,
+ byte_len, string_flags);
+
+ if (converted_pw_len == -1) {
+ return false;
+ }
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("decode_pw_buffer: new_pwrd: "));
+ dump_data(100, (const uint8_t *)new_pwrd, converted_pw_len);
+ DEBUG(100,("multibyte len:%d\n", (int)converted_pw_len));
+ DEBUG(100,("original char len:%d\n", byte_len/2));
+#endif
+
+ return true;
+}
+
+/***********************************************************
+ encode a password buffer with an already unicode password. The
+ rest of the buffer is filled with random data to make it harder to attack.
+************************************************************/
+bool set_pw_in_buffer(uint8_t buffer[516], DATA_BLOB *password)
+{
+ if (password->length > 512) {
+ return false;
+ }
+
+ memcpy(&buffer[512 - password->length], password->data, password->length);
+
+ generate_random_buffer(buffer, 512 - password->length);
+
+ /*
+ * The length of the new password is in the last 4 bytes of
+ * the data buffer.
+ */
+ SIVAL(buffer, 512, password->length);
+ return true;
+}
+
+/***********************************************************
+ decode a password buffer
+ *new_pw_size is the length in bytes of the extracted unicode password
+************************************************************/
+bool extract_pw_from_buffer(TALLOC_CTX *mem_ctx,
+ uint8_t in_buffer[516], DATA_BLOB *new_pass)
+{
+ int byte_len=0;
+
+ /* The length of the new password is in the last 4 bytes of the data buffer. */
+
+ byte_len = IVAL(in_buffer, 512);
+
+#ifdef DEBUG_PASSWORD
+ dump_data(100, in_buffer, 516);
+#endif
+
+ /* Password cannot be longer than the size of the password buffer */
+ if ( (byte_len < 0) || (byte_len > 512)) {
+ return false;
+ }
+
+ *new_pass = data_blob_talloc(mem_ctx, &in_buffer[512 - byte_len], byte_len);
+
+ if (!new_pass->data) {
+ return false;
+ }
+
+ return true;
+}
diff --git a/source4/libcli/cldap/cldap.c b/source4/libcli/cldap/cldap.c
new file mode 100644
index 0000000000..b18ba12b1f
--- /dev/null
+++ b/source4/libcli/cldap/cldap.c
@@ -0,0 +1,738 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ cldap client library
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ see RFC1798 for details of CLDAP
+
+ basic properties
+ - carried over UDP on port 389
+ - request and response matched by message ID
+ - request consists of only a single searchRequest element
+ - response can be in one of two forms
+ - a single searchResponse, followed by a searchResult
+ - a single searchResult
+*/
+
+#include "includes.h"
+#include "lib/events/events.h"
+#include "../lib/util/dlinklist.h"
+#include "libcli/ldap/ldap.h"
+#include "libcli/ldap/ldap_ndr.h"
+#include "libcli/cldap/cldap.h"
+#include "lib/socket/socket.h"
+#include "libcli/security/security.h"
+#include "librpc/gen_ndr/ndr_nbt.h"
+
+/*
+ destroy a pending request
+*/
+static int cldap_request_destructor(struct cldap_request *req)
+{
+ if (req->state == CLDAP_REQUEST_SEND) {
+ DLIST_REMOVE(req->cldap->send_queue, req);
+ }
+ if (!req->is_reply && req->message_id != 0) {
+ idr_remove(req->cldap->idr, req->message_id);
+ req->message_id = 0;
+ }
+ return 0;
+}
+
+/*
+ handle recv events on a cldap socket
+*/
+static void cldap_socket_recv(struct cldap_socket *cldap)
+{
+ TALLOC_CTX *tmp_ctx = talloc_new(cldap);
+ NTSTATUS status;
+ struct socket_address *src;
+ DATA_BLOB blob;
+ size_t nread, dsize;
+ struct asn1_data *asn1 = asn1_init(tmp_ctx);
+ struct ldap_message *ldap_msg;
+ struct cldap_request *req;
+
+ if (!asn1) return;
+
+ status = socket_pending(cldap->sock, &dsize);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ return;
+ }
+
+ blob = data_blob_talloc(tmp_ctx, NULL, dsize);
+ if (blob.data == NULL) {
+ talloc_free(tmp_ctx);
+ return;
+ }
+
+ status = socket_recvfrom(cldap->sock, blob.data, blob.length, &nread,
+ tmp_ctx, &src);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ return;
+ }
+ blob.length = nread;
+
+ DEBUG(2,("Received cldap packet of length %d from %s:%d\n",
+ (int)blob.length, src->addr, src->port));
+
+ if (!asn1_load(asn1, blob)) {
+ DEBUG(2,("Failed to setup for asn.1 decode\n"));
+ talloc_free(tmp_ctx);
+ return;
+ }
+
+ ldap_msg = talloc(tmp_ctx, struct ldap_message);
+ if (ldap_msg == NULL) {
+ talloc_free(tmp_ctx);
+ return;
+ }
+
+ /* this initial decode is used to find the message id */
+ status = ldap_decode(asn1, NULL, ldap_msg);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(2,("Failed to decode ldap message: %s\n", nt_errstr(status)));
+ talloc_free(tmp_ctx);
+ return;
+ }
+
+ /* find the pending request */
+ req = idr_find(cldap->idr, ldap_msg->messageid);
+ if (req == NULL) {
+ if (cldap->incoming.handler) {
+ cldap->incoming.handler(cldap, ldap_msg, src);
+ } else {
+ DEBUG(2,("Mismatched cldap reply %u from %s:%d\n",
+ ldap_msg->messageid, src->addr, src->port));
+ }
+ talloc_free(tmp_ctx);
+ return;
+ }
+
+ req->asn1 = talloc_steal(req, asn1);
+ req->asn1->ofs = 0;
+
+ req->state = CLDAP_REQUEST_DONE;
+ talloc_free(req->te);
+
+ talloc_free(tmp_ctx);
+
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+}
+
+/*
+ handle request timeouts
+*/
+static void cldap_request_timeout(struct tevent_context *event_ctx,
+ struct tevent_timer *te, struct timeval t,
+ void *private_data)
+{
+ struct cldap_request *req = talloc_get_type(private_data, struct cldap_request);
+
+ /* possibly try again */
+ if (req->num_retries != 0) {
+ size_t len = req->encoded.length;
+
+ req->num_retries--;
+
+ socket_sendto(req->cldap->sock, &req->encoded, &len,
+ req->dest);
+
+ req->te = event_add_timed(req->cldap->event_ctx, req,
+ timeval_current_ofs(req->timeout, 0),
+ cldap_request_timeout, req);
+ return;
+ }
+
+ req->state = CLDAP_REQUEST_ERROR;
+ req->status = NT_STATUS_IO_TIMEOUT;
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+}
+
+/*
+ handle send events on a cldap socket
+*/
+static void cldap_socket_send(struct cldap_socket *cldap)
+{
+ struct cldap_request *req;
+ NTSTATUS status;
+
+ while ((req = cldap->send_queue)) {
+ size_t len;
+
+ len = req->encoded.length;
+ status = socket_sendto(cldap->sock, &req->encoded, &len,
+ req->dest);
+ if (NT_STATUS_IS_ERR(status)) {
+ DEBUG(0,("Failed to send cldap request of length %u to %s:%d\n",
+ (unsigned)req->encoded.length, req->dest->addr, req->dest->port));
+ DLIST_REMOVE(cldap->send_queue, req);
+ req->state = CLDAP_REQUEST_ERROR;
+ req->status = status;
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+ continue;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) return;
+
+ DLIST_REMOVE(cldap->send_queue, req);
+
+ if (req->is_reply) {
+ talloc_free(req);
+ } else {
+ req->state = CLDAP_REQUEST_WAIT;
+
+ req->te = event_add_timed(cldap->event_ctx, req,
+ timeval_current_ofs(req->timeout, 0),
+ cldap_request_timeout, req);
+
+ EVENT_FD_READABLE(cldap->fde);
+ }
+ }
+
+ EVENT_FD_NOT_WRITEABLE(cldap->fde);
+ return;
+}
+
+
+/*
+ handle fd events on a cldap_socket
+*/
+static void cldap_socket_handler(struct tevent_context *ev, struct tevent_fd *fde,
+ uint16_t flags, void *private_data)
+{
+ struct cldap_socket *cldap = talloc_get_type(private_data, struct cldap_socket);
+ if (flags & EVENT_FD_WRITE) {
+ cldap_socket_send(cldap);
+ }
+ if (flags & EVENT_FD_READ) {
+ cldap_socket_recv(cldap);
+ }
+}
+
+/*
+ initialise a cldap_socket. The event_ctx is optional, if provided
+ then operations will use that event context
+*/
+struct cldap_socket *cldap_socket_init(TALLOC_CTX *mem_ctx,
+ struct tevent_context *event_ctx,
+ struct smb_iconv_convenience *iconv_convenience)
+{
+ struct cldap_socket *cldap;
+ NTSTATUS status;
+
+ cldap = talloc(mem_ctx, struct cldap_socket);
+ if (cldap == NULL) goto failed;
+
+ cldap->event_ctx = talloc_reference(cldap, event_ctx);
+ if (cldap->event_ctx == NULL) goto failed;
+
+ cldap->idr = idr_init(cldap);
+ if (cldap->idr == NULL) goto failed;
+
+ status = socket_create("ip", SOCKET_TYPE_DGRAM, &cldap->sock, 0);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+
+ talloc_steal(cldap, cldap->sock);
+
+ cldap->fde = event_add_fd(cldap->event_ctx, cldap,
+ socket_get_fd(cldap->sock), 0,
+ cldap_socket_handler, cldap);
+
+ cldap->send_queue = NULL;
+ cldap->incoming.handler = NULL;
+ cldap->iconv_convenience = iconv_convenience;
+
+ return cldap;
+
+failed:
+ talloc_free(cldap);
+ return NULL;
+}
+
+
+/*
+ setup a handler for incoming requests
+*/
+NTSTATUS cldap_set_incoming_handler(struct cldap_socket *cldap,
+ void (*handler)(struct cldap_socket *, struct ldap_message *,
+ struct socket_address *),
+ void *private_data)
+{
+ cldap->incoming.handler = handler;
+ cldap->incoming.private_data = private_data;
+ EVENT_FD_READABLE(cldap->fde);
+ return NT_STATUS_OK;
+}
+
+/*
+ queue a cldap request for send
+*/
+struct cldap_request *cldap_search_send(struct cldap_socket *cldap,
+ struct cldap_search *io)
+{
+ struct ldap_message *msg;
+ struct cldap_request *req;
+ struct ldap_SearchRequest *search;
+
+ req = talloc_zero(cldap, struct cldap_request);
+ if (req == NULL) goto failed;
+
+ req->cldap = cldap;
+ req->state = CLDAP_REQUEST_SEND;
+ req->timeout = io->in.timeout;
+ req->num_retries = io->in.retries;
+ req->is_reply = false;
+ req->asn1 = asn1_init(req);
+ if (!req->asn1) {
+ goto failed;
+ }
+
+ req->dest = socket_address_from_strings(req, cldap->sock->backend_name,
+ io->in.dest_address,
+ io->in.dest_port);
+ if (!req->dest) goto failed;
+
+ req->message_id = idr_get_new_random(cldap->idr, req, UINT16_MAX);
+ if (req->message_id == -1) goto failed;
+
+ talloc_set_destructor(req, cldap_request_destructor);
+
+ msg = talloc(req, struct ldap_message);
+ if (msg == NULL) goto failed;
+ msg->messageid = req->message_id;
+ msg->type = LDAP_TAG_SearchRequest;
+ msg->controls = NULL;
+ search = &msg->r.SearchRequest;
+
+ search->basedn = "";
+ search->scope = LDAP_SEARCH_SCOPE_BASE;
+ search->deref = LDAP_DEREFERENCE_NEVER;
+ search->timelimit = 0;
+ search->sizelimit = 0;
+ search->attributesonly = false;
+ search->num_attributes = str_list_length(io->in.attributes);
+ search->attributes = io->in.attributes;
+ search->tree = ldb_parse_tree(req, io->in.filter);
+ if (search->tree == NULL) {
+ goto failed;
+ }
+
+ if (!ldap_encode(msg, NULL, &req->encoded, req)) {
+ DEBUG(0,("Failed to encode cldap message to %s:%d\n",
+ req->dest->addr, req->dest->port));
+ goto failed;
+ }
+
+ DLIST_ADD_END(cldap->send_queue, req, struct cldap_request *);
+
+ EVENT_FD_WRITEABLE(cldap->fde);
+
+ return req;
+
+failed:
+ talloc_free(req);
+ return NULL;
+}
+
+
+/*
+ queue a cldap reply for send
+*/
+NTSTATUS cldap_reply_send(struct cldap_socket *cldap, struct cldap_reply *io)
+{
+ struct ldap_message *msg;
+ struct cldap_request *req;
+ DATA_BLOB blob1, blob2;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ req = talloc_zero(cldap, struct cldap_request);
+ if (req == NULL) goto failed;
+
+ req->cldap = cldap;
+ req->state = CLDAP_REQUEST_SEND;
+ req->is_reply = true;
+ req->asn1 = asn1_init(req);
+ if (!req->asn1) {
+ goto failed;
+ }
+
+ req->dest = io->dest;
+ if (talloc_reference(req, io->dest) == NULL) goto failed;
+
+ talloc_set_destructor(req, cldap_request_destructor);
+
+ msg = talloc(req, struct ldap_message);
+ if (msg == NULL) goto failed;
+ msg->messageid = io->messageid;
+ msg->controls = NULL;
+
+ if (io->response) {
+ msg->type = LDAP_TAG_SearchResultEntry;
+ msg->r.SearchResultEntry = *io->response;
+
+ if (!ldap_encode(msg, NULL, &blob1, req)) {
+ DEBUG(0,("Failed to encode cldap message to %s:%d\n",
+ req->dest->addr, req->dest->port));
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto failed;
+ }
+ } else {
+ blob1 = data_blob(NULL, 0);
+ }
+
+ msg->type = LDAP_TAG_SearchResultDone;
+ msg->r.SearchResultDone = *io->result;
+
+ if (!ldap_encode(msg, NULL, &blob2, req)) {
+ DEBUG(0,("Failed to encode cldap message to %s:%d\n",
+ req->dest->addr, req->dest->port));
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto failed;
+ }
+
+ req->encoded = data_blob_talloc(req, NULL, blob1.length + blob2.length);
+ if (req->encoded.data == NULL) goto failed;
+
+ memcpy(req->encoded.data, blob1.data, blob1.length);
+ memcpy(req->encoded.data+blob1.length, blob2.data, blob2.length);
+
+ DLIST_ADD_END(cldap->send_queue, req, struct cldap_request *);
+
+ EVENT_FD_WRITEABLE(cldap->fde);
+
+ return NT_STATUS_OK;
+
+failed:
+ talloc_free(req);
+ return status;
+}
+
+/*
+ receive a cldap reply
+*/
+NTSTATUS cldap_search_recv(struct cldap_request *req,
+ TALLOC_CTX *mem_ctx,
+ struct cldap_search *io)
+{
+ struct ldap_message *ldap_msg;
+ NTSTATUS status;
+
+ if (req == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ while (req->state < CLDAP_REQUEST_DONE) {
+ if (event_loop_once(req->cldap->event_ctx) != 0) {
+ talloc_free(req);
+ return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+ }
+
+ if (req->state == CLDAP_REQUEST_ERROR) {
+ status = req->status;
+ talloc_free(req);
+ return status;
+ }
+
+ ldap_msg = talloc(mem_ctx, struct ldap_message);
+ NT_STATUS_HAVE_NO_MEMORY(ldap_msg);
+
+ status = ldap_decode(req->asn1, NULL, ldap_msg);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(2,("Failed to decode cldap search reply: %s\n", nt_errstr(status)));
+ talloc_free(req);
+ return status;
+ }
+
+ ZERO_STRUCT(io->out);
+
+ /* the first possible form has a search result in first place */
+ if (ldap_msg->type == LDAP_TAG_SearchResultEntry) {
+ io->out.response = talloc(mem_ctx, struct ldap_SearchResEntry);
+ NT_STATUS_HAVE_NO_MEMORY(io->out.response);
+ *io->out.response = ldap_msg->r.SearchResultEntry;
+
+ /* decode the 2nd part */
+ status = ldap_decode(req->asn1, NULL, ldap_msg);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(2,("Failed to decode cldap search result entry: %s\n", nt_errstr(status)));
+ talloc_free(req);
+ return status;
+ }
+ }
+
+ if (ldap_msg->type != LDAP_TAG_SearchResultDone) {
+ talloc_free(req);
+ return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
+ }
+
+ io->out.result = talloc(mem_ctx, struct ldap_Result);
+ NT_STATUS_HAVE_NO_MEMORY(io->out.result);
+ *io->out.result = ldap_msg->r.SearchResultDone;
+
+ talloc_free(req);
+
+ if (io->out.result->resultcode != LDAP_SUCCESS) {
+ return NT_STATUS_LDAP(io->out.result->resultcode);
+ }
+ return NT_STATUS_OK;
+}
+
+
+/*
+ synchronous cldap search
+*/
+NTSTATUS cldap_search(struct cldap_socket *cldap,
+ TALLOC_CTX *mem_ctx,
+ struct cldap_search *io)
+{
+ struct cldap_request *req = cldap_search_send(cldap, io);
+ return cldap_search_recv(req, mem_ctx, io);
+}
+
+
+
+/*
+ queue a cldap netlogon for send
+*/
+struct cldap_request *cldap_netlogon_send(struct cldap_socket *cldap,
+ struct cldap_netlogon *io)
+{
+ struct cldap_search search;
+ char *filter;
+ struct cldap_request *req;
+ const char *attr[] = { "NetLogon", NULL };
+ TALLOC_CTX *tmp_ctx = talloc_new(cldap);
+
+ filter = talloc_asprintf(tmp_ctx, "(&(NtVer=%s)",
+ ldap_encode_ndr_uint32(tmp_ctx, io->in.version));
+ if (filter == NULL) goto failed;
+ if (io->in.user) {
+ filter = talloc_asprintf_append_buffer(filter, "(User=%s)", io->in.user);
+ if (filter == NULL) goto failed;
+ }
+ if (io->in.host) {
+ filter = talloc_asprintf_append_buffer(filter, "(Host=%s)", io->in.host);
+ if (filter == NULL) goto failed;
+ }
+ if (io->in.realm) {
+ filter = talloc_asprintf_append_buffer(filter, "(DnsDomain=%s)", io->in.realm);
+ if (filter == NULL) goto failed;
+ }
+ if (io->in.acct_control != -1) {
+ filter = talloc_asprintf_append_buffer(filter, "(AAC=%s)",
+ ldap_encode_ndr_uint32(tmp_ctx, io->in.acct_control));
+ if (filter == NULL) goto failed;
+ }
+ if (io->in.domain_sid) {
+ struct dom_sid *sid = dom_sid_parse_talloc(tmp_ctx, io->in.domain_sid);
+ if (sid == NULL) goto failed;
+ filter = talloc_asprintf_append_buffer(filter, "(domainSid=%s)",
+ ldap_encode_ndr_dom_sid(tmp_ctx, sid));
+ if (filter == NULL) goto failed;
+ }
+ if (io->in.domain_guid) {
+ struct GUID guid;
+ NTSTATUS status;
+ status = GUID_from_string(io->in.domain_guid, &guid);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+ filter = talloc_asprintf_append_buffer(filter, "(DomainGuid=%s)",
+ ldap_encode_ndr_GUID(tmp_ctx, &guid));
+ if (filter == NULL) goto failed;
+ }
+ filter = talloc_asprintf_append_buffer(filter, ")");
+ if (filter == NULL) goto failed;
+
+ search.in.dest_address = io->in.dest_address;
+ search.in.dest_port = io->in.dest_port;
+ search.in.filter = filter;
+ search.in.attributes = attr;
+ search.in.timeout = 2;
+ search.in.retries = 2;
+
+ req = cldap_search_send(cldap, &search);
+
+ talloc_free(tmp_ctx);
+ return req;
+failed:
+ talloc_free(tmp_ctx);
+ return NULL;
+}
+
+
+/*
+ receive a cldap netlogon reply
+*/
+NTSTATUS cldap_netlogon_recv(struct cldap_request *req,
+ TALLOC_CTX *mem_ctx,
+ struct cldap_netlogon *io)
+{
+ NTSTATUS status;
+ struct cldap_search search;
+ struct cldap_socket *cldap;
+ DATA_BLOB *data;
+
+ cldap = req->cldap;
+
+ status = cldap_search_recv(req, mem_ctx, &search);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (search.out.response == NULL) {
+ return NT_STATUS_NOT_FOUND;
+ }
+
+ if (search.out.response->num_attributes != 1 ||
+ strcasecmp(search.out.response->attributes[0].name, "netlogon") != 0 ||
+ search.out.response->attributes[0].num_values != 1 ||
+ search.out.response->attributes[0].values->length < 2) {
+ return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+ data = search.out.response->attributes[0].values;
+
+ status = pull_netlogon_samlogon_response(data, mem_ctx, req->cldap->iconv_convenience,
+ &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;
+}
+
+/*
+ sync cldap netlogon search
+*/
+NTSTATUS cldap_netlogon(struct cldap_socket *cldap,
+ TALLOC_CTX *mem_ctx, struct cldap_netlogon *io)
+{
+ struct cldap_request *req = cldap_netlogon_send(cldap, io);
+ return cldap_netlogon_recv(req, mem_ctx, io);
+}
+
+
+/*
+ send an empty reply (used on any error, so the client doesn't keep waiting
+ or send the bad request again)
+*/
+NTSTATUS cldap_empty_reply(struct cldap_socket *cldap,
+ uint32_t message_id,
+ struct socket_address *src)
+{
+ NTSTATUS status;
+ struct cldap_reply reply;
+ struct ldap_Result result;
+
+ reply.messageid = message_id;
+ reply.dest = src;
+ reply.response = NULL;
+ reply.result = &result;
+
+ ZERO_STRUCT(result);
+
+ status = cldap_reply_send(cldap, &reply);
+
+ return status;
+}
+
+/*
+ send an error reply (used on any error, so the client doesn't keep waiting
+ or send the bad request again)
+*/
+NTSTATUS cldap_error_reply(struct cldap_socket *cldap,
+ uint32_t message_id,
+ struct socket_address *src,
+ int resultcode,
+ const char *errormessage)
+{
+ NTSTATUS status;
+ struct cldap_reply reply;
+ struct ldap_Result result;
+
+ reply.messageid = message_id;
+ reply.dest = src;
+ reply.response = NULL;
+ reply.result = &result;
+
+ ZERO_STRUCT(result);
+ result.resultcode = resultcode;
+ result.errormessage = errormessage;
+
+ status = cldap_reply_send(cldap, &reply);
+
+ return status;
+}
+
+
+/*
+ send a netlogon reply
+*/
+NTSTATUS cldap_netlogon_reply(struct cldap_socket *cldap,
+ uint32_t message_id,
+ struct socket_address *src,
+ uint32_t version,
+ struct netlogon_samlogon_response *netlogon)
+{
+ NTSTATUS status;
+ struct cldap_reply reply;
+ struct ldap_SearchResEntry response;
+ struct ldap_Result result;
+ TALLOC_CTX *tmp_ctx = talloc_new(cldap);
+ DATA_BLOB blob;
+
+ status = push_netlogon_samlogon_response(&blob, tmp_ctx, cldap->iconv_convenience,
+ netlogon);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ reply.messageid = message_id;
+ reply.dest = src;
+ reply.response = &response;
+ reply.result = &result;
+
+ ZERO_STRUCT(result);
+
+ response.dn = "";
+ response.num_attributes = 1;
+ response.attributes = talloc(tmp_ctx, struct ldb_message_element);
+ NT_STATUS_HAVE_NO_MEMORY(response.attributes);
+ response.attributes->name = "netlogon";
+ response.attributes->num_values = 1;
+ response.attributes->values = &blob;
+
+ status = cldap_reply_send(cldap, &reply);
+
+ talloc_free(tmp_ctx);
+
+ return status;
+}
+
+
diff --git a/source4/libcli/cldap/cldap.h b/source4/libcli/cldap/cldap.h
new file mode 100644
index 0000000000..8951daa775
--- /dev/null
+++ b/source4/libcli/cldap/cldap.h
@@ -0,0 +1,182 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ a async CLDAP library
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "../lib/util/asn1.h"
+#include "../libcli/netlogon.h"
+
+struct ldap_message;
+
+enum cldap_request_state {CLDAP_REQUEST_SEND,
+ CLDAP_REQUEST_WAIT,
+ CLDAP_REQUEST_DONE,
+ CLDAP_REQUEST_ERROR};
+
+/*
+ a cldap request packet
+*/
+struct cldap_request {
+ struct cldap_request *next, *prev;
+
+ struct cldap_socket *cldap;
+
+ enum cldap_request_state state;
+ NTSTATUS status;
+
+ /* where to send the request */
+ struct socket_address *dest;
+
+ /* timeout between retries (seconds) */
+ int timeout;
+ int num_retries;
+
+ bool is_reply;
+
+ /* the ldap message_id */
+ int message_id;
+
+ struct tevent_timer *te;
+
+ /* the encoded request */
+ DATA_BLOB encoded;
+
+ /* the reply data */
+ struct asn1_data *asn1;
+
+ /* information on what to do on completion */
+ struct {
+ void (*fn)(struct cldap_request *);
+ void *private_data;
+ } async;
+};
+
+/*
+ context structure for operations on cldap packets
+*/
+struct cldap_socket {
+ struct socket_context *sock;
+ struct tevent_context *event_ctx;
+ struct smb_iconv_convenience *iconv_convenience;
+
+ /* the fd event */
+ struct tevent_fd *fde;
+
+ /* a queue of outgoing requests */
+ struct cldap_request *send_queue;
+
+ /* mapping from message_id to pending request */
+ struct idr_context *idr;
+
+ /* what to do with incoming request packets */
+ struct {
+ void (*handler)(struct cldap_socket *, struct ldap_message *,
+ struct socket_address *);
+ void *private_data;
+ } incoming;
+};
+
+
+/*
+ a general cldap search request
+*/
+struct cldap_search {
+ struct {
+ const char *dest_address;
+ uint16_t dest_port;
+ const char *filter;
+ const char **attributes;
+ int timeout;
+ int retries;
+ } in;
+ struct {
+ struct ldap_SearchResEntry *response;
+ struct ldap_Result *result;
+ } out;
+};
+
+struct cldap_socket *cldap_socket_init(TALLOC_CTX *mem_ctx,
+ struct tevent_context *event_ctx,
+ struct smb_iconv_convenience *iconv_convenience);
+NTSTATUS cldap_set_incoming_handler(struct cldap_socket *cldap,
+ void (*handler)(struct cldap_socket *, struct ldap_message *,
+ struct socket_address *),
+ void *private_data);
+struct cldap_request *cldap_search_send(struct cldap_socket *cldap,
+ struct cldap_search *io);
+NTSTATUS cldap_search_recv(struct cldap_request *req, TALLOC_CTX *mem_ctx,
+ struct cldap_search *io);
+NTSTATUS cldap_search(struct cldap_socket *cldap, TALLOC_CTX *mem_ctx,
+ struct cldap_search *io);
+
+
+/*
+ a general cldap reply
+*/
+struct cldap_reply {
+ uint32_t messageid;
+ struct socket_address *dest;
+ struct ldap_SearchResEntry *response;
+ struct ldap_Result *result;
+};
+
+NTSTATUS cldap_reply_send(struct cldap_socket *cldap, struct cldap_reply *io);
+
+NTSTATUS cldap_empty_reply(struct cldap_socket *cldap,
+ uint32_t message_id,
+ struct socket_address *src);
+NTSTATUS cldap_error_reply(struct cldap_socket *cldap,
+ uint32_t message_id,
+ struct socket_address *src,
+ int resultcode,
+ const char *errormessage);
+
+/*
+ a netlogon cldap request
+*/
+struct cldap_netlogon {
+ struct {
+ const char *dest_address;
+ uint16_t dest_port;
+ const char *realm;
+ const char *host;
+ const char *user;
+ const char *domain_guid;
+ const char *domain_sid;
+ int acct_control;
+ uint32_t version;
+ bool map_response;
+ } in;
+ struct {
+ struct netlogon_samlogon_response netlogon;
+ } out;
+};
+
+struct cldap_request *cldap_netlogon_send(struct cldap_socket *cldap,
+ struct cldap_netlogon *io);
+NTSTATUS cldap_netlogon_recv(struct cldap_request *req,
+ TALLOC_CTX *mem_ctx,
+ struct cldap_netlogon *io);
+NTSTATUS cldap_netlogon(struct cldap_socket *cldap,
+ TALLOC_CTX *mem_ctx, struct cldap_netlogon *io);
+NTSTATUS cldap_netlogon_reply(struct cldap_socket *cldap,
+ uint32_t message_id,
+ struct socket_address *src,
+ uint32_t version,
+ struct netlogon_samlogon_response *netlogon);
diff --git a/source4/libcli/cliconnect.c b/source4/libcli/cliconnect.c
new file mode 100644
index 0000000000..14935dadba
--- /dev/null
+++ b/source4/libcli/cliconnect.c
@@ -0,0 +1,267 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ client connect/disconnect routines
+
+ Copyright (C) Andrew Tridgell 2003-2005
+ Copyright (C) James Peach 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/libcli.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/auth/libcli_auth.h"
+#include "libcli/smb_composite/smb_composite.h"
+
+/*
+ wrapper around smbcli_sock_connect()
+*/
+bool smbcli_socket_connect(struct smbcli_state *cli, const char *server,
+ const char **ports,
+ struct tevent_context *ev_ctx,
+ struct resolve_context *resolve_ctx,
+ struct smbcli_options *options,
+ struct smb_iconv_convenience *iconv_convenience,
+ const char *socket_options)
+{
+ struct smbcli_socket *sock;
+
+ sock = smbcli_sock_connect_byname(server, ports, NULL,
+ resolve_ctx, ev_ctx,
+ socket_options);
+
+ if (sock == NULL) return false;
+
+ cli->transport = smbcli_transport_init(sock, cli, true, options,
+ iconv_convenience);
+ if (!cli->transport) {
+ return false;
+ }
+
+ return true;
+}
+
+/* wrapper around smbcli_transport_connect() */
+bool smbcli_transport_establish(struct smbcli_state *cli,
+ struct nbt_name *calling,
+ struct nbt_name *called)
+{
+ return smbcli_transport_connect(cli->transport, calling, called);
+}
+
+/* wrapper around smb_raw_negotiate() */
+NTSTATUS smbcli_negprot(struct smbcli_state *cli, bool unicode, int maxprotocol)
+{
+ return smb_raw_negotiate(cli->transport, unicode, maxprotocol);
+}
+
+/* wrapper around smb_raw_sesssetup() */
+NTSTATUS smbcli_session_setup(struct smbcli_state *cli,
+ struct cli_credentials *credentials,
+ const char *workgroup,
+ struct smbcli_session_options options,
+ struct gensec_settings *gensec_settings)
+{
+ struct smb_composite_sesssetup setup;
+ NTSTATUS status;
+
+ cli->session = smbcli_session_init(cli->transport, cli, true,
+ options);
+ if (!cli->session) return NT_STATUS_UNSUCCESSFUL;
+
+ setup.in.sesskey = cli->transport->negotiate.sesskey;
+ setup.in.capabilities = cli->transport->negotiate.capabilities;
+ setup.in.credentials = credentials;
+ setup.in.workgroup = workgroup;
+ setup.in.gensec_settings = gensec_settings;
+
+ status = smb_composite_sesssetup(cli->session, &setup);
+
+ cli->session->vuid = setup.out.vuid;
+
+ return status;
+}
+
+/* wrapper around smb_raw_tcon() */
+NTSTATUS smbcli_tconX(struct smbcli_state *cli, const char *sharename,
+ const char *devtype, const char *password)
+{
+ union smb_tcon tcon;
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+
+ cli->tree = smbcli_tree_init(cli->session, cli, true);
+ if (!cli->tree) return NT_STATUS_UNSUCCESSFUL;
+
+ mem_ctx = talloc_init("tcon");
+ if (!mem_ctx) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* setup a tree connect */
+ tcon.generic.level = RAW_TCON_TCONX;
+ tcon.tconx.in.flags = 0;
+ if (cli->transport->negotiate.sec_mode & NEGOTIATE_SECURITY_USER_LEVEL) {
+ tcon.tconx.in.password = data_blob(NULL, 0);
+ } else if (cli->transport->negotiate.sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) {
+ tcon.tconx.in.password = data_blob_talloc(mem_ctx, NULL, 24);
+ if (cli->transport->negotiate.secblob.length < 8) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ SMBencrypt(password, cli->transport->negotiate.secblob.data, tcon.tconx.in.password.data);
+ } else {
+ tcon.tconx.in.password = data_blob_talloc(mem_ctx, password, strlen(password)+1);
+ }
+ tcon.tconx.in.path = sharename;
+ tcon.tconx.in.device = devtype;
+
+ status = smb_raw_tcon(cli->tree, mem_ctx, &tcon);
+
+ cli->tree->tid = tcon.tconx.out.tid;
+
+ talloc_free(mem_ctx);
+
+ return status;
+}
+
+
+/*
+ easy way to get to a fully connected smbcli_state in one call
+*/
+NTSTATUS smbcli_full_connection(TALLOC_CTX *parent_ctx,
+ struct smbcli_state **ret_cli,
+ const char *host,
+ const char **ports,
+ const char *sharename,
+ const char *devtype,
+ const char *socket_options,
+ struct cli_credentials *credentials,
+ struct resolve_context *resolve_ctx,
+ struct tevent_context *ev,
+ struct smbcli_options *options,
+ struct smbcli_session_options *session_options,
+ struct smb_iconv_convenience *iconv_convenience,
+ struct gensec_settings *gensec_settings)
+{
+ struct smbcli_tree *tree;
+ NTSTATUS status;
+
+ *ret_cli = NULL;
+
+ status = smbcli_tree_full_connection(parent_ctx,
+ &tree, host, ports,
+ sharename, devtype,
+ socket_options,
+ credentials, resolve_ctx, ev,
+ options,
+ session_options,
+ iconv_convenience,
+ gensec_settings);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ (*ret_cli) = smbcli_state_init(parent_ctx);
+
+ (*ret_cli)->tree = tree;
+ (*ret_cli)->session = tree->session;
+ (*ret_cli)->transport = tree->session->transport;
+
+ talloc_steal(*ret_cli, tree);
+
+done:
+ return status;
+}
+
+
+/*
+ disconnect the tree
+*/
+NTSTATUS smbcli_tdis(struct smbcli_state *cli)
+{
+ return smb_tree_disconnect(cli->tree);
+}
+
+/****************************************************************************
+ Initialise a client state structure.
+****************************************************************************/
+struct smbcli_state *smbcli_state_init(TALLOC_CTX *mem_ctx)
+{
+ return talloc_zero(mem_ctx, struct smbcli_state);
+}
+
+/* Insert a NULL at the first separator of the given path and return a pointer
+ * to the remainder of the string.
+ */
+static char *
+terminate_path_at_separator(char * path)
+{
+ char * p;
+
+ if (!path) {
+ return NULL;
+ }
+
+ if ((p = strchr_m(path, '/'))) {
+ *p = '\0';
+ return p + 1;
+ }
+
+ if ((p = strchr_m(path, '\\'))) {
+ *p = '\0';
+ return p + 1;
+ }
+
+ /* No separator. */
+ return NULL;
+}
+
+/*
+ parse a //server/share type UNC name
+*/
+bool smbcli_parse_unc(const char *unc_name, TALLOC_CTX *mem_ctx,
+ char **hostname, char **sharename)
+{
+ char *p;
+
+ *hostname = *sharename = NULL;
+
+ if (strncmp(unc_name, "\\\\", 2) &&
+ strncmp(unc_name, "//", 2)) {
+ return false;
+ }
+
+ *hostname = talloc_strdup(mem_ctx, &unc_name[2]);
+ p = terminate_path_at_separator(*hostname);
+
+ if (p != NULL && *p) {
+ *sharename = talloc_strdup(mem_ctx, p);
+ terminate_path_at_separator(*sharename);
+ }
+
+ if (*hostname && *sharename) {
+ return true;
+ }
+
+ talloc_free(*hostname);
+ talloc_free(*sharename);
+ *hostname = *sharename = NULL;
+ return false;
+}
+
+
+
diff --git a/source4/libcli/clideltree.c b/source4/libcli/clideltree.c
new file mode 100644
index 0000000000..d947ac3547
--- /dev/null
+++ b/source4/libcli/clideltree.c
@@ -0,0 +1,137 @@
+/*
+ Unix SMB/CIFS implementation.
+ useful function for deleting a whole directory tree
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/libcli.h"
+#include "system/dir.h"
+
+struct delete_state {
+ struct smbcli_tree *tree;
+ int total_deleted;
+ bool failed;
+};
+
+/*
+ callback function for torture_deltree()
+*/
+static void delete_fn(struct clilist_file_info *finfo, const char *name, void *state)
+{
+ struct delete_state *dstate = (struct delete_state *)state;
+ char *s, *n;
+ if (ISDOT(finfo->name) || ISDOTDOT(finfo->name)) {
+ return;
+ }
+
+ n = strdup(name);
+ n[strlen(n)-1] = 0;
+ asprintf(&s, "%s%s", n, finfo->name);
+
+ if (finfo->attrib & FILE_ATTRIBUTE_READONLY) {
+ if (NT_STATUS_IS_ERR(smbcli_setatr(dstate->tree, s, 0, 0))) {
+ DEBUG(2,("Failed to remove READONLY on %s - %s\n",
+ s, smbcli_errstr(dstate->tree)));
+ }
+ }
+
+ if (finfo->attrib & FILE_ATTRIBUTE_DIRECTORY) {
+ char *s2;
+ asprintf(&s2, "%s\\*", s);
+ smbcli_unlink(dstate->tree, s2);
+ smbcli_list(dstate->tree, s2,
+ FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM,
+ delete_fn, state);
+ free(s2);
+ if (NT_STATUS_IS_ERR(smbcli_rmdir(dstate->tree, s))) {
+ DEBUG(2,("Failed to delete %s - %s\n",
+ s, smbcli_errstr(dstate->tree)));
+ dstate->failed = true;
+ }
+ dstate->total_deleted++;
+ } else {
+ if (NT_STATUS_IS_ERR(smbcli_unlink(dstate->tree, s))) {
+ DEBUG(2,("Failed to delete %s - %s\n",
+ s, smbcli_errstr(dstate->tree)));
+ dstate->failed = true;
+ }
+ dstate->total_deleted++;
+ }
+ free(s);
+ free(n);
+}
+
+/*
+ recursively descend a tree deleting all files
+ returns the number of files deleted, or -1 on error
+*/
+int smbcli_deltree(struct smbcli_tree *tree, const char *dname)
+{
+ char *mask;
+ struct delete_state dstate;
+ NTSTATUS status;
+
+ dstate.tree = tree;
+ dstate.total_deleted = 0;
+ dstate.failed = false;
+
+ /* it might be a file */
+ status = smbcli_unlink(tree, dname);
+ if (NT_STATUS_IS_OK(smbcli_unlink(tree, dname))) {
+ return 1;
+ }
+ if (NT_STATUS_EQUAL(smbcli_nt_error(tree), NT_STATUS_OBJECT_NAME_NOT_FOUND) ||
+ NT_STATUS_EQUAL(smbcli_nt_error(tree), NT_STATUS_OBJECT_PATH_NOT_FOUND) ||
+ NT_STATUS_EQUAL(smbcli_nt_error(tree), NT_STATUS_NO_SUCH_FILE) ||
+ NT_STATUS_EQUAL(smbcli_nt_error(tree), NT_STATUS_DOS(ERRDOS, ERRbadfile))) {
+ return 0;
+ }
+ if (NT_STATUS_EQUAL(status, NT_STATUS_CANNOT_DELETE)) {
+ /* it could be read-only */
+ status = smbcli_setatr(tree, dname, FILE_ATTRIBUTE_NORMAL, 0);
+ if (NT_STATUS_IS_OK(smbcli_unlink(tree, dname))) {
+ return 1;
+ }
+ }
+
+ asprintf(&mask, "%s\\*", dname);
+ smbcli_unlink(dstate.tree, mask);
+ smbcli_list(dstate.tree, mask,
+ FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM,
+ delete_fn, &dstate);
+ free(mask);
+
+ status = smbcli_rmdir(dstate.tree, dname);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_CANNOT_DELETE)) {
+ /* it could be read-only */
+ status = smbcli_setatr(dstate.tree, dname, FILE_ATTRIBUTE_NORMAL, 0);
+ status = smbcli_rmdir(dstate.tree, dname);
+ }
+ if (NT_STATUS_IS_ERR(status)) {
+ DEBUG(2,("Failed to delete %s - %s\n",
+ dname, smbcli_errstr(dstate.tree)));
+ return -1;
+ }
+ dstate.total_deleted++;
+
+ if (dstate.failed) {
+ return -1;
+ }
+
+ return dstate.total_deleted;
+}
diff --git a/source4/libcli/clifile.c b/source4/libcli/clifile.c
new file mode 100644
index 0000000000..2cf174060b
--- /dev/null
+++ b/source4/libcli/clifile.c
@@ -0,0 +1,702 @@
+/*
+ Unix SMB/CIFS implementation.
+ client file operations
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Jeremy Allison 2001-2002
+ Copyright (C) James 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/libcli.h"
+
+/****************************************************************************
+ Hard/Symlink a file (UNIX extensions).
+****************************************************************************/
+
+static NTSTATUS smbcli_link_internal(struct smbcli_tree *tree,
+ const char *fname_src,
+ const char *fname_dst, bool hard_link)
+{
+ union smb_setfileinfo parms;
+ NTSTATUS status;
+
+ if (hard_link) {
+ parms.generic.level = RAW_SFILEINFO_UNIX_HLINK;
+ parms.unix_hlink.in.file.path = fname_src;
+ parms.unix_hlink.in.link_dest = fname_dst;
+ } else {
+ parms.generic.level = RAW_SFILEINFO_UNIX_LINK;
+ parms.unix_link.in.file.path = fname_src;
+ parms.unix_link.in.link_dest = fname_dst;
+ }
+
+ status = smb_raw_setpathinfo(tree, &parms);
+
+ return status;
+}
+
+/****************************************************************************
+ Map standard UNIX permissions onto wire representations.
+****************************************************************************/
+uint32_t unix_perms_to_wire(mode_t perms)
+{
+ uint_t ret = 0;
+
+ ret |= ((perms & S_IXOTH) ? UNIX_X_OTH : 0);
+ ret |= ((perms & S_IWOTH) ? UNIX_W_OTH : 0);
+ ret |= ((perms & S_IROTH) ? UNIX_R_OTH : 0);
+ ret |= ((perms & S_IXGRP) ? UNIX_X_GRP : 0);
+ ret |= ((perms & S_IWGRP) ? UNIX_W_GRP : 0);
+ ret |= ((perms & S_IRGRP) ? UNIX_R_GRP : 0);
+ ret |= ((perms & S_IXUSR) ? UNIX_X_USR : 0);
+ ret |= ((perms & S_IWUSR) ? UNIX_W_USR : 0);
+ ret |= ((perms & S_IRUSR) ? UNIX_R_USR : 0);
+#ifdef S_ISVTX
+ ret |= ((perms & S_ISVTX) ? UNIX_STICKY : 0);
+#endif
+#ifdef S_ISGID
+ ret |= ((perms & S_ISGID) ? UNIX_SET_GID : 0);
+#endif
+#ifdef S_ISUID
+ ret |= ((perms & S_ISUID) ? UNIX_SET_UID : 0);
+#endif
+ return ret;
+}
+
+/****************************************************************************
+ Symlink a file (UNIX extensions).
+****************************************************************************/
+NTSTATUS smbcli_unix_symlink(struct smbcli_tree *tree, const char *fname_src,
+ const char *fname_dst)
+{
+ return smbcli_link_internal(tree, fname_src, fname_dst, false);
+}
+
+/****************************************************************************
+ Hard a file (UNIX extensions).
+****************************************************************************/
+NTSTATUS smbcli_unix_hardlink(struct smbcli_tree *tree, const char *fname_src,
+ const char *fname_dst)
+{
+ return smbcli_link_internal(tree, fname_src, fname_dst, true);
+}
+
+
+/****************************************************************************
+ Chmod or chown a file internal (UNIX extensions).
+****************************************************************************/
+static NTSTATUS smbcli_unix_chmod_chown_internal(struct smbcli_tree *tree,
+ const char *fname,
+ uint32_t mode, uint32_t uid,
+ uint32_t gid)
+{
+ union smb_setfileinfo parms;
+ NTSTATUS status;
+
+ parms.generic.level = SMB_SFILEINFO_UNIX_BASIC;
+ parms.unix_basic.in.file.path = fname;
+ parms.unix_basic.in.uid = uid;
+ parms.unix_basic.in.gid = gid;
+ parms.unix_basic.in.mode = mode;
+
+ status = smb_raw_setpathinfo(tree, &parms);
+
+ return status;
+}
+
+/****************************************************************************
+ chmod a file (UNIX extensions).
+****************************************************************************/
+
+NTSTATUS smbcli_unix_chmod(struct smbcli_tree *tree, const char *fname, mode_t mode)
+{
+ return smbcli_unix_chmod_chown_internal(tree, fname,
+ unix_perms_to_wire(mode),
+ SMB_UID_NO_CHANGE,
+ SMB_GID_NO_CHANGE);
+}
+
+/****************************************************************************
+ chown a file (UNIX extensions).
+****************************************************************************/
+NTSTATUS smbcli_unix_chown(struct smbcli_tree *tree, const char *fname, uid_t uid,
+ gid_t gid)
+{
+ return smbcli_unix_chmod_chown_internal(tree, fname, SMB_MODE_NO_CHANGE,
+ (uint32_t)uid, (uint32_t)gid);
+}
+
+
+/****************************************************************************
+ Rename a file.
+****************************************************************************/
+NTSTATUS smbcli_rename(struct smbcli_tree *tree, const char *fname_src,
+ const char *fname_dst)
+{
+ union smb_rename parms;
+
+ parms.generic.level = RAW_RENAME_RENAME;
+ parms.rename.in.attrib = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_DIRECTORY;
+ parms.rename.in.pattern1 = fname_src;
+ parms.rename.in.pattern2 = fname_dst;
+
+ return smb_raw_rename(tree, &parms);
+}
+
+
+/****************************************************************************
+ Delete a file.
+****************************************************************************/
+NTSTATUS smbcli_unlink(struct smbcli_tree *tree, const char *fname)
+{
+ union smb_unlink parms;
+
+ parms.unlink.in.pattern = fname;
+ if (strchr(fname, '*')) {
+ parms.unlink.in.attrib = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN;
+ } else {
+ parms.unlink.in.attrib = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_DIRECTORY;
+ }
+
+ return smb_raw_unlink(tree, &parms);
+}
+
+/****************************************************************************
+ Create a directory.
+****************************************************************************/
+NTSTATUS smbcli_mkdir(struct smbcli_tree *tree, const char *dname)
+{
+ union smb_mkdir parms;
+
+ parms.mkdir.level = RAW_MKDIR_MKDIR;
+ parms.mkdir.in.path = dname;
+
+ return smb_raw_mkdir(tree, &parms);
+}
+
+
+/****************************************************************************
+ Remove a directory.
+****************************************************************************/
+NTSTATUS smbcli_rmdir(struct smbcli_tree *tree, const char *dname)
+{
+ struct smb_rmdir parms;
+
+ parms.in.path = dname;
+
+ return smb_raw_rmdir(tree, &parms);
+}
+
+
+/****************************************************************************
+ Set or clear the delete on close flag.
+****************************************************************************/
+NTSTATUS smbcli_nt_delete_on_close(struct smbcli_tree *tree, int fnum,
+ bool flag)
+{
+ union smb_setfileinfo parms;
+ NTSTATUS status;
+
+ parms.disposition_info.level = RAW_SFILEINFO_DISPOSITION_INFO;
+ parms.disposition_info.in.file.fnum = fnum;
+ parms.disposition_info.in.delete_on_close = flag;
+
+ status = smb_raw_setfileinfo(tree, &parms);
+
+ return status;
+}
+
+
+/****************************************************************************
+ Create/open a file - exposing the full horror of the NT API :-).
+ Used in CIFS-on-CIFS NTVFS.
+****************************************************************************/
+int smbcli_nt_create_full(struct smbcli_tree *tree, const char *fname,
+ uint32_t CreatFlags, uint32_t DesiredAccess,
+ uint32_t FileAttributes, uint32_t ShareAccess,
+ uint32_t CreateDisposition, uint32_t CreateOptions,
+ uint8_t SecurityFlags)
+{
+ union smb_open open_parms;
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+
+ mem_ctx = talloc_init("raw_open");
+ if (!mem_ctx) return -1;
+
+ open_parms.ntcreatex.level = RAW_OPEN_NTCREATEX;
+ open_parms.ntcreatex.in.flags = CreatFlags;
+ open_parms.ntcreatex.in.root_fid = 0;
+ open_parms.ntcreatex.in.access_mask = DesiredAccess;
+ open_parms.ntcreatex.in.file_attr = FileAttributes;
+ open_parms.ntcreatex.in.alloc_size = 0;
+ open_parms.ntcreatex.in.share_access = ShareAccess;
+ open_parms.ntcreatex.in.open_disposition = CreateDisposition;
+ open_parms.ntcreatex.in.create_options = CreateOptions;
+ open_parms.ntcreatex.in.impersonation = 0;
+ open_parms.ntcreatex.in.security_flags = SecurityFlags;
+ open_parms.ntcreatex.in.fname = fname;
+
+ status = smb_raw_open(tree, mem_ctx, &open_parms);
+ talloc_free(mem_ctx);
+
+ if (NT_STATUS_IS_OK(status)) {
+ return open_parms.ntcreatex.out.file.fnum;
+ }
+
+ return -1;
+}
+
+
+/****************************************************************************
+ Open a file (using SMBopenx)
+ WARNING: if you open with O_WRONLY then getattrE won't work!
+****************************************************************************/
+int smbcli_open(struct smbcli_tree *tree, const char *fname, int flags,
+ int share_mode)
+{
+ union smb_open open_parms;
+ uint_t openfn=0;
+ uint_t accessmode=0;
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+
+ mem_ctx = talloc_init("raw_open");
+ if (!mem_ctx) return -1;
+
+ 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<<OPENX_MODE_DENY_SHIFT);
+
+ if ((flags & O_ACCMODE) == O_RDWR) {
+ accessmode |= OPENX_MODE_ACCESS_RDWR;
+ } else if ((flags & O_ACCMODE) == O_WRONLY) {
+ accessmode |= OPENX_MODE_ACCESS_WRITE;
+ }
+
+#if defined(O_SYNC)
+ if ((flags & O_SYNC) == O_SYNC) {
+ accessmode |= OPENX_MODE_WRITE_THRU;
+ }
+#endif
+
+ if (share_mode == DENY_FCB) {
+ accessmode = OPENX_MODE_ACCESS_FCB | OPENX_MODE_DENY_FCB;
+ }
+
+ open_parms.openx.level = RAW_OPEN_OPENX;
+ open_parms.openx.in.flags = 0;
+ open_parms.openx.in.open_mode = accessmode;
+ 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 = openfn;
+ open_parms.openx.in.size = 0;
+ open_parms.openx.in.timeout = 0;
+ open_parms.openx.in.fname = fname;
+
+ status = smb_raw_open(tree, mem_ctx, &open_parms);
+ talloc_free(mem_ctx);
+
+ if (NT_STATUS_IS_OK(status)) {
+ return open_parms.openx.out.file.fnum;
+ }
+
+ return -1;
+}
+
+
+/****************************************************************************
+ Close a file.
+****************************************************************************/
+NTSTATUS smbcli_close(struct smbcli_tree *tree, int fnum)
+{
+ union smb_close close_parms;
+ NTSTATUS status;
+
+ close_parms.close.level = RAW_CLOSE_CLOSE;
+ close_parms.close.in.file.fnum = fnum;
+ close_parms.close.in.write_time = 0;
+ status = smb_raw_close(tree, &close_parms);
+ return status;
+}
+
+/****************************************************************************
+ send a lock with a specified locktype
+ this is used for testing LOCKING_ANDX_CANCEL_LOCK
+****************************************************************************/
+NTSTATUS smbcli_locktype(struct smbcli_tree *tree, int fnum,
+ uint32_t offset, uint32_t len, int timeout,
+ uint8_t locktype)
+{
+ union smb_lock parms;
+ struct smb_lock_entry lock[1];
+ NTSTATUS status;
+
+ parms.lockx.level = RAW_LOCK_LOCKX;
+ parms.lockx.in.file.fnum = fnum;
+ parms.lockx.in.mode = locktype;
+ parms.lockx.in.timeout = timeout;
+ parms.lockx.in.ulock_cnt = 0;
+ parms.lockx.in.lock_cnt = 1;
+ lock[0].pid = tree->session->pid;
+ lock[0].offset = offset;
+ lock[0].count = len;
+ parms.lockx.in.locks = &lock[0];
+
+ status = smb_raw_lock(tree, &parms);
+
+ return status;
+}
+
+
+/****************************************************************************
+ Lock a file.
+****************************************************************************/
+NTSTATUS smbcli_lock(struct smbcli_tree *tree, int fnum,
+ uint32_t offset, uint32_t len, int timeout,
+ enum brl_type lock_type)
+{
+ union smb_lock parms;
+ struct smb_lock_entry lock[1];
+ NTSTATUS status;
+
+ parms.lockx.level = RAW_LOCK_LOCKX;
+ parms.lockx.in.file.fnum = fnum;
+ parms.lockx.in.mode = (lock_type == READ_LOCK? 1 : 0);
+ parms.lockx.in.timeout = timeout;
+ parms.lockx.in.ulock_cnt = 0;
+ parms.lockx.in.lock_cnt = 1;
+ lock[0].pid = tree->session->pid;
+ lock[0].offset = offset;
+ lock[0].count = len;
+ parms.lockx.in.locks = &lock[0];
+
+ status = smb_raw_lock(tree, &parms);
+
+ return status;
+}
+
+
+/****************************************************************************
+ Unlock a file.
+****************************************************************************/
+NTSTATUS smbcli_unlock(struct smbcli_tree *tree, int fnum, uint32_t offset, uint32_t len)
+{
+ union smb_lock parms;
+ struct smb_lock_entry lock[1];
+ NTSTATUS status;
+
+ parms.lockx.level = RAW_LOCK_LOCKX;
+ parms.lockx.in.file.fnum = fnum;
+ parms.lockx.in.mode = 0;
+ parms.lockx.in.timeout = 0;
+ parms.lockx.in.ulock_cnt = 1;
+ parms.lockx.in.lock_cnt = 0;
+ lock[0].pid = tree->session->pid;
+ lock[0].offset = offset;
+ lock[0].count = len;
+ parms.lockx.in.locks = &lock[0];
+
+ status = smb_raw_lock(tree, &parms);
+ return status;
+}
+
+
+/****************************************************************************
+ Lock a file with 64 bit offsets.
+****************************************************************************/
+NTSTATUS smbcli_lock64(struct smbcli_tree *tree, int fnum,
+ off_t offset, off_t len, int timeout,
+ enum brl_type lock_type)
+{
+ union smb_lock parms;
+ int ltype;
+ struct smb_lock_entry lock[1];
+ NTSTATUS status;
+
+ if (!(tree->session->transport->negotiate.capabilities & CAP_LARGE_FILES)) {
+ return smbcli_lock(tree, fnum, offset, len, timeout, lock_type);
+ }
+
+ parms.lockx.level = RAW_LOCK_LOCKX;
+ parms.lockx.in.file.fnum = fnum;
+
+ ltype = (lock_type == READ_LOCK? 1 : 0);
+ ltype |= LOCKING_ANDX_LARGE_FILES;
+ parms.lockx.in.mode = ltype;
+ parms.lockx.in.timeout = timeout;
+ parms.lockx.in.ulock_cnt = 0;
+ parms.lockx.in.lock_cnt = 1;
+ lock[0].pid = tree->session->pid;
+ lock[0].offset = offset;
+ lock[0].count = len;
+ parms.lockx.in.locks = &lock[0];
+
+ status = smb_raw_lock(tree, &parms);
+
+ return status;
+}
+
+
+/****************************************************************************
+ Unlock a file with 64 bit offsets.
+****************************************************************************/
+NTSTATUS smbcli_unlock64(struct smbcli_tree *tree, int fnum, off_t offset,
+ off_t len)
+{
+ union smb_lock parms;
+ struct smb_lock_entry lock[1];
+ NTSTATUS status;
+
+ if (!(tree->session->transport->negotiate.capabilities & CAP_LARGE_FILES)) {
+ return smbcli_unlock(tree, fnum, offset, len);
+ }
+
+ parms.lockx.level = RAW_LOCK_LOCKX;
+ parms.lockx.in.file.fnum = fnum;
+ 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 = tree->session->pid;
+ lock[0].offset = offset;
+ lock[0].count = len;
+ parms.lockx.in.locks = &lock[0];
+
+ status = smb_raw_lock(tree, &parms);
+
+ return status;
+}
+
+
+/****************************************************************************
+ Do a SMBgetattrE call.
+****************************************************************************/
+NTSTATUS smbcli_getattrE(struct smbcli_tree *tree, int fnum,
+ uint16_t *attr, size_t *size,
+ time_t *c_time, time_t *a_time, time_t *m_time)
+{
+ union smb_fileinfo parms;
+ NTSTATUS status;
+
+ parms.getattre.level = RAW_FILEINFO_GETATTRE;
+ parms.getattre.in.file.fnum = fnum;
+
+ status = smb_raw_fileinfo(tree, NULL, &parms);
+
+ if (!NT_STATUS_IS_OK(status))
+ return status;
+
+ if (size) {
+ *size = parms.getattre.out.size;
+ }
+
+ if (attr) {
+ *attr = parms.getattre.out.attrib;
+ }
+
+ if (c_time) {
+ *c_time = parms.getattre.out.create_time;
+ }
+
+ if (a_time) {
+ *a_time = parms.getattre.out.access_time;
+ }
+
+ if (m_time) {
+ *m_time = parms.getattre.out.write_time;
+ }
+
+ return status;
+}
+
+/****************************************************************************
+ Do a SMBgetatr call
+****************************************************************************/
+NTSTATUS smbcli_getatr(struct smbcli_tree *tree, const char *fname,
+ uint16_t *attr, size_t *size, time_t *t)
+{
+ union smb_fileinfo parms;
+ NTSTATUS status;
+
+ parms.getattr.level = RAW_FILEINFO_GETATTR;
+ parms.getattr.in.file.path = fname;
+
+ status = smb_raw_pathinfo(tree, NULL, &parms);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (size) {
+ *size = parms.getattr.out.size;
+ }
+
+ if (t) {
+ *t = parms.getattr.out.write_time;
+ }
+
+ if (attr) {
+ *attr = parms.getattr.out.attrib;
+ }
+
+ return status;
+}
+
+
+/****************************************************************************
+ Do a SMBsetatr call.
+****************************************************************************/
+NTSTATUS smbcli_setatr(struct smbcli_tree *tree, const char *fname, uint16_t mode,
+ time_t t)
+{
+ union smb_setfileinfo parms;
+
+ parms.setattr.level = RAW_SFILEINFO_SETATTR;
+ parms.setattr.in.file.path = fname;
+ parms.setattr.in.attrib = mode;
+ parms.setattr.in.write_time = t;
+
+ return smb_raw_setpathinfo(tree, &parms);
+}
+
+/****************************************************************************
+ Do a setfileinfo basic_info call.
+****************************************************************************/
+NTSTATUS smbcli_fsetatr(struct smbcli_tree *tree, int fnum, uint16_t mode,
+ NTTIME create_time, NTTIME access_time,
+ NTTIME write_time, NTTIME change_time)
+{
+ union smb_setfileinfo parms;
+
+ parms.basic_info.level = RAW_SFILEINFO_BASIC_INFO;
+ parms.basic_info.in.file.fnum = fnum;
+ parms.basic_info.in.attrib = mode;
+ parms.basic_info.in.create_time = create_time;
+ parms.basic_info.in.access_time = access_time;
+ parms.basic_info.in.write_time = write_time;
+ parms.basic_info.in.change_time = change_time;
+
+ return smb_raw_setfileinfo(tree, &parms);
+}
+
+
+/****************************************************************************
+ truncate a file to a given size
+****************************************************************************/
+NTSTATUS smbcli_ftruncate(struct smbcli_tree *tree, int fnum, uint64_t size)
+{
+ union smb_setfileinfo parms;
+
+ parms.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFO;
+ parms.end_of_file_info.in.file.fnum = fnum;
+ parms.end_of_file_info.in.size = size;
+
+ return smb_raw_setfileinfo(tree, &parms);
+}
+
+
+/****************************************************************************
+ Check for existence of a dir.
+****************************************************************************/
+NTSTATUS smbcli_chkpath(struct smbcli_tree *tree, const char *path)
+{
+ union smb_chkpath parms;
+ char *path2;
+ NTSTATUS status;
+
+ path2 = strdup(path);
+ trim_string(path2,NULL,"\\");
+ if (!*path2) {
+ free(path2);
+ path2 = strdup("\\");
+ }
+
+ parms.chkpath.in.path = path2;
+
+ status = smb_raw_chkpath(tree, &parms);
+
+ free(path2);
+
+ return status;
+}
+
+
+/****************************************************************************
+ Query disk space.
+****************************************************************************/
+NTSTATUS smbcli_dskattr(struct smbcli_tree *tree, uint32_t *bsize,
+ uint64_t *total, uint64_t *avail)
+{
+ union smb_fsinfo fsinfo_parms;
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+
+ mem_ctx = talloc_init("smbcli_dskattr");
+
+ fsinfo_parms.dskattr.level = RAW_QFS_SIZE_INFO;
+ status = smb_raw_fsinfo(tree, mem_ctx, &fsinfo_parms);
+ if (NT_STATUS_IS_OK(status)) {
+ *bsize = fsinfo_parms.size_info.out.bytes_per_sector * fsinfo_parms.size_info.out.sectors_per_unit;
+ *total = fsinfo_parms.size_info.out.total_alloc_units;
+ *avail = fsinfo_parms.size_info.out.avail_alloc_units;
+ }
+
+ talloc_free(mem_ctx);
+
+ return status;
+}
+
+
+/****************************************************************************
+ Create and open a temporary file.
+****************************************************************************/
+int smbcli_ctemp(struct smbcli_tree *tree, const char *path, char **tmp_path)
+{
+ union smb_open open_parms;
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+
+ mem_ctx = talloc_init("raw_open");
+ if (!mem_ctx) return -1;
+
+ open_parms.openx.level = RAW_OPEN_CTEMP;
+ open_parms.ctemp.in.attrib = 0;
+ open_parms.ctemp.in.directory = path;
+ open_parms.ctemp.in.write_time = 0;
+
+ status = smb_raw_open(tree, mem_ctx, &open_parms);
+ if (tmp_path) {
+ *tmp_path = strdup(open_parms.ctemp.out.name);
+ }
+ talloc_free(mem_ctx);
+ if (NT_STATUS_IS_OK(status)) {
+ return open_parms.ctemp.out.file.fnum;
+ }
+ return -1;
+}
diff --git a/source4/libcli/clilist.c b/source4/libcli/clilist.c
new file mode 100644
index 0000000000..65ee0a8303
--- /dev/null
+++ b/source4/libcli/clilist.c
@@ -0,0 +1,356 @@
+/*
+ Unix SMB/CIFS implementation.
+ client directory list routines
+ Copyright (C) Andrew Tridgell 1994-2003
+ Copyright (C) James Myers 2003 <myersjj@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/libcli.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+
+struct search_private {
+ struct clilist_file_info *dirlist;
+ TALLOC_CTX *mem_ctx;
+ int dirlist_len;
+ int ff_searchcount; /* total received in 1 server trip */
+ int total_received; /* total received all together */
+ enum smb_search_data_level data_level;
+ const char *last_name; /* used to continue trans2 search */
+ struct smb_search_id id; /* used for old-style search */
+};
+
+
+/****************************************************************************
+ Interpret a long filename structure.
+****************************************************************************/
+static bool interpret_long_filename(enum smb_search_data_level level,
+ const union smb_search_data *info,
+ struct clilist_file_info *finfo)
+{
+ struct clilist_file_info finfo2;
+
+ if (!finfo) finfo = &finfo2;
+ ZERO_STRUCTP(finfo);
+
+ switch (level) {
+ case RAW_SEARCH_DATA_STANDARD:
+ finfo->size = info->standard.size;
+ finfo->mtime = info->standard.write_time;
+ finfo->attrib = info->standard.attrib;
+ finfo->name = info->standard.name.s;
+ finfo->short_name = info->standard.name.s;
+ break;
+
+ case RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO:
+ finfo->size = info->both_directory_info.size;
+ finfo->mtime = nt_time_to_unix(info->both_directory_info.write_time);
+ finfo->attrib = info->both_directory_info.attrib;
+ finfo->short_name = info->both_directory_info.short_name.s;
+ finfo->name = info->both_directory_info.name.s;
+ break;
+
+ default:
+ DEBUG(0,("Unhandled level %d in interpret_long_filename\n", (int)level));
+ return false;
+ }
+
+ return true;
+}
+
+/* callback function used for trans2 search */
+static bool smbcli_list_new_callback(void *private_data, const union smb_search_data *file)
+{
+ struct search_private *state = (struct search_private*) private_data;
+ struct clilist_file_info *tdl;
+
+ /* add file info to the dirlist pool */
+ tdl = talloc_realloc(state,
+ state->dirlist,
+ struct clilist_file_info,
+ state->dirlist_len + 1);
+ if (!tdl) {
+ return false;
+ }
+ state->dirlist = tdl;
+ state->dirlist_len++;
+
+ interpret_long_filename(state->data_level, file, &state->dirlist[state->total_received]);
+
+ state->last_name = state->dirlist[state->total_received].name;
+ state->total_received++;
+ state->ff_searchcount++;
+
+ return true;
+}
+
+int smbcli_list_new(struct smbcli_tree *tree, const char *Mask, uint16_t attribute,
+ enum smb_search_data_level level,
+ void (*fn)(struct clilist_file_info *, const char *, void *),
+ void *caller_state)
+{
+ union smb_search_first first_parms;
+ union smb_search_next next_parms;
+ struct search_private state; /* for callbacks */
+ int received = 0;
+ bool first = true;
+ int num_received = 0;
+ int max_matches = 512;
+ char *mask;
+ int ff_eos = 0, i, ff_searchcount;
+ int ff_dir_handle=0;
+
+ /* initialize state for search */
+ state.mem_ctx = talloc_init("smbcli_list_new");
+ state.dirlist_len = 0;
+ state.total_received = 0;
+
+ state.dirlist = talloc_array(state.mem_ctx,
+ struct clilist_file_info, 0);
+ mask = talloc_strdup(state.mem_ctx, Mask);
+
+ if (level == RAW_SEARCH_DATA_GENERIC) {
+ if (tree->session->transport->negotiate.capabilities & CAP_NT_SMBS) {
+ level = RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO;
+ } else {
+ level = RAW_SEARCH_DATA_STANDARD;
+ }
+ }
+ state.data_level = level;
+
+ while (1) {
+ state.ff_searchcount = 0;
+ if (first) {
+ NTSTATUS status;
+
+ first_parms.t2ffirst.level = RAW_SEARCH_TRANS2;
+ first_parms.t2ffirst.data_level = state.data_level;
+ first_parms.t2ffirst.in.max_count = max_matches;
+ first_parms.t2ffirst.in.search_attrib = attribute;
+ first_parms.t2ffirst.in.pattern = mask;
+ first_parms.t2ffirst.in.flags = FLAG_TRANS2_FIND_CLOSE_IF_END;
+ first_parms.t2ffirst.in.storage_type = 0;
+
+ status = smb_raw_search_first(tree,
+ state.mem_ctx, &first_parms,
+ (void*)&state, smbcli_list_new_callback);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(state.mem_ctx);
+ return -1;
+ }
+
+ ff_dir_handle = first_parms.t2ffirst.out.handle;
+ ff_searchcount = first_parms.t2ffirst.out.count;
+ ff_eos = first_parms.t2ffirst.out.end_of_search;
+
+ received = first_parms.t2ffirst.out.count;
+ if (received <= 0) break;
+ if (ff_eos) break;
+ first = false;
+ } else {
+ NTSTATUS status;
+
+ next_parms.t2fnext.level = RAW_SEARCH_TRANS2;
+ next_parms.t2fnext.data_level = state.data_level;
+ next_parms.t2fnext.in.max_count = max_matches;
+ next_parms.t2fnext.in.last_name = state.last_name;
+ next_parms.t2fnext.in.handle = ff_dir_handle;
+ next_parms.t2fnext.in.resume_key = 0;
+ next_parms.t2fnext.in.flags = FLAG_TRANS2_FIND_CLOSE_IF_END;
+
+ status = smb_raw_search_next(tree,
+ state.mem_ctx,
+ &next_parms,
+ (void*)&state,
+ smbcli_list_new_callback);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return -1;
+ }
+ ff_searchcount = next_parms.t2fnext.out.count;
+ ff_eos = next_parms.t2fnext.out.end_of_search;
+ received = next_parms.t2fnext.out.count;
+ if (received <= 0) break;
+ if (ff_eos) break;
+ }
+
+ num_received += received;
+ }
+
+ for (i=0;i<state.total_received;i++) {
+ fn(&state.dirlist[i], Mask, caller_state);
+ }
+
+ talloc_free(state.mem_ctx);
+
+ return state.total_received;
+}
+
+/****************************************************************************
+ Interpret a short filename structure.
+ The length of the structure is returned.
+****************************************************************************/
+static bool interpret_short_filename(enum smb_search_data_level level,
+ const union smb_search_data *info,
+ struct clilist_file_info *finfo)
+{
+ struct clilist_file_info finfo2;
+
+ if (!finfo) finfo = &finfo2;
+ ZERO_STRUCTP(finfo);
+
+ switch (level) {
+ case RAW_SEARCH_DATA_SEARCH:
+ finfo->mtime = info->search.write_time;
+ finfo->size = info->search.size;
+ finfo->attrib = info->search.attrib;
+ finfo->name = info->search.name;
+ finfo->short_name = info->search.name;
+ break;
+
+ default:
+ DEBUG(0,("Unhandled level %d in interpret_short_filename\n", (int)level));
+ return false;
+ }
+
+ return true;
+}
+
+/* callback function used for smb_search */
+static bool smbcli_list_old_callback(void *private_data, const union smb_search_data *file)
+{
+ struct search_private *state = (struct search_private*) private_data;
+ struct clilist_file_info *tdl;
+
+ /* add file info to the dirlist pool */
+ tdl = talloc_realloc(state,
+ state->dirlist,
+ struct clilist_file_info,
+ state->dirlist_len + 1);
+
+ if (!tdl) {
+ return false;
+ }
+ state->dirlist = tdl;
+ state->dirlist_len++;
+
+ interpret_short_filename(state->data_level, file, &state->dirlist[state->total_received]);
+
+ state->total_received++;
+ state->ff_searchcount++;
+ state->id = file->search.id; /* return resume info */
+
+ return true;
+}
+
+int smbcli_list_old(struct smbcli_tree *tree, const char *Mask, uint16_t attribute,
+ void (*fn)(struct clilist_file_info *, const char *, void *),
+ void *caller_state)
+{
+ union smb_search_first first_parms;
+ union smb_search_next next_parms;
+ struct search_private state; /* for callbacks */
+ const int num_asked = 500;
+ int received = 0;
+ bool first = true;
+ int num_received = 0;
+ char *mask;
+ int i;
+
+ /* initialize state for search */
+ state.mem_ctx = talloc_init("smbcli_list_old");
+ state.dirlist_len = 0;
+ state.total_received = 0;
+ state.data_level = RAW_SEARCH_DATA_SEARCH;
+
+ state.dirlist = talloc_array(state.mem_ctx, struct clilist_file_info,
+ 0);
+ mask = talloc_strdup(state.mem_ctx, Mask);
+
+ while (1) {
+ state.ff_searchcount = 0;
+ if (first) {
+ NTSTATUS status;
+
+ first_parms.search_first.level = RAW_SEARCH_SEARCH;
+ first_parms.search_first.data_level = RAW_SEARCH_DATA_SEARCH;
+ first_parms.search_first.in.max_count = num_asked;
+ first_parms.search_first.in.search_attrib = attribute;
+ first_parms.search_first.in.pattern = mask;
+
+ status = smb_raw_search_first(tree, state.mem_ctx,
+ &first_parms,
+ (void*)&state,
+ smbcli_list_old_callback);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(state.mem_ctx);
+ return -1;
+ }
+
+ received = first_parms.search_first.out.count;
+ if (received <= 0) break;
+ first = false;
+ } else {
+ NTSTATUS status;
+
+ next_parms.search_next.level = RAW_SEARCH_SEARCH;
+ next_parms.search_next.data_level = RAW_SEARCH_DATA_SEARCH;
+ next_parms.search_next.in.max_count = num_asked;
+ next_parms.search_next.in.search_attrib = attribute;
+ next_parms.search_next.in.id = state.id;
+
+ status = smb_raw_search_next(tree, state.mem_ctx,
+ &next_parms,
+ (void*)&state,
+ smbcli_list_old_callback);
+
+ if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
+ break;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(state.mem_ctx);
+ return -1;
+ }
+ received = next_parms.search_next.out.count;
+ if (received <= 0) break;
+ }
+
+ num_received += received;
+ }
+
+ for (i=0;i<state.total_received;i++) {
+ fn(&state.dirlist[i], Mask, caller_state);
+ }
+
+ talloc_free(state.mem_ctx);
+
+ return state.total_received;
+}
+
+/****************************************************************************
+ Do a directory listing, calling fn on each file found.
+ This auto-switches between old and new style.
+****************************************************************************/
+
+int smbcli_list(struct smbcli_tree *tree, const char *Mask,uint16_t attribute,
+ void (*fn)(struct clilist_file_info *, const char *, void *), void *state)
+{
+ if (tree->session->transport->negotiate.protocol <= PROTOCOL_LANMAN1)
+ return smbcli_list_old(tree, Mask, attribute, fn, state);
+ return smbcli_list_new(tree, Mask, attribute, RAW_SEARCH_DATA_GENERIC, fn, state);
+}
diff --git a/source4/libcli/climessage.c b/source4/libcli/climessage.c
new file mode 100644
index 0000000000..5ed0e8e3cd
--- /dev/null
+++ b/source4/libcli/climessage.c
@@ -0,0 +1,95 @@
+/*
+ Unix SMB/CIFS implementation.
+ client message handling routines
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) James J Myers 2003 <myersjj@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/libcli.h"
+
+
+/****************************************************************************
+start a message sequence
+****************************************************************************/
+bool smbcli_message_start(struct smbcli_tree *tree, const char *host, const char *username,
+ int *grp)
+{
+ struct smbcli_request *req;
+
+ req = smbcli_request_setup(tree, SMBsendstrt, 0, 0);
+ smbcli_req_append_string(req, username, STR_TERMINATE);
+ smbcli_req_append_string(req, host, STR_TERMINATE);
+ if (!smbcli_request_send(req) ||
+ !smbcli_request_receive(req) ||
+ smbcli_is_error(tree)) {
+ smbcli_request_destroy(req);
+ return false;
+ }
+
+ *grp = SVAL(req->in.vwv, VWV(0));
+ smbcli_request_destroy(req);
+
+ return true;
+}
+
+
+/****************************************************************************
+send a message
+****************************************************************************/
+bool smbcli_message_text(struct smbcli_tree *tree, char *msg, int len, int grp)
+{
+ struct smbcli_request *req;
+
+ req = smbcli_request_setup(tree, SMBsendtxt, 1, 0);
+ SSVAL(req->out.vwv, VWV(0), grp);
+
+ smbcli_req_append_bytes(req, (const uint8_t *)msg, len);
+
+ if (!smbcli_request_send(req) ||
+ !smbcli_request_receive(req) ||
+ smbcli_is_error(tree)) {
+ smbcli_request_destroy(req);
+ return false;
+ }
+
+ smbcli_request_destroy(req);
+ return true;
+}
+
+/****************************************************************************
+end a message
+****************************************************************************/
+bool smbcli_message_end(struct smbcli_tree *tree, int grp)
+{
+ struct smbcli_request *req;
+
+ req = smbcli_request_setup(tree, SMBsendend, 1, 0);
+ SSVAL(req->out.vwv, VWV(0), grp);
+
+ if (!smbcli_request_send(req) ||
+ !smbcli_request_receive(req) ||
+ smbcli_is_error(tree)) {
+ smbcli_request_destroy(req);
+ return false;
+ }
+
+ smbcli_request_destroy(req);
+ return true;
+}
+
diff --git a/source4/libcli/clireadwrite.c b/source4/libcli/clireadwrite.c
new file mode 100644
index 0000000000..ae2367918c
--- /dev/null
+++ b/source4/libcli/clireadwrite.c
@@ -0,0 +1,167 @@
+/*
+ Unix SMB/CIFS implementation.
+ client file read/write routines
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) James 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/libcli.h"
+
+/****************************************************************************
+ Read size bytes at offset offset using SMBreadX.
+****************************************************************************/
+ssize_t smbcli_read(struct smbcli_tree *tree, int fnum, void *_buf, off_t offset,
+ size_t size)
+{
+ uint8_t *buf = (uint8_t *)_buf;
+ union smb_read parms;
+ int readsize;
+ ssize_t total = 0;
+
+ if (size == 0) {
+ return 0;
+ }
+
+ parms.readx.level = RAW_READ_READX;
+ parms.readx.in.file.fnum = fnum;
+
+ /*
+ * Set readsize to the maximum size we can handle in one readX,
+ * rounded down to a multiple of 1024.
+ */
+ readsize = (tree->session->transport->negotiate.max_xmit - (MIN_SMB_SIZE+32));
+ if (readsize > 0xFFFF) readsize = 0xFFFF;
+
+ while (total < size) {
+ NTSTATUS status;
+
+ readsize = MIN(readsize, size-total);
+
+ parms.readx.in.offset = offset;
+ parms.readx.in.mincnt = readsize;
+ parms.readx.in.maxcnt = readsize;
+ parms.readx.in.remaining = size - total;
+ parms.readx.in.read_for_execute = false;
+ parms.readx.out.data = buf + total;
+
+ status = smb_raw_read(tree, &parms);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return -1;
+ }
+
+ total += parms.readx.out.nread;
+ offset += parms.readx.out.nread;
+
+ /* If the server returned less than we asked for we're at EOF */
+ if (parms.readx.out.nread < readsize)
+ break;
+ }
+
+ return total;
+}
+
+
+/****************************************************************************
+ write to a file
+ write_mode: 0x0001 disallow write cacheing
+ 0x0002 return bytes remaining
+ 0x0004 use raw named pipe protocol
+ 0x0008 start of message mode named pipe protocol
+****************************************************************************/
+ssize_t smbcli_write(struct smbcli_tree *tree,
+ int fnum, uint16_t write_mode,
+ const void *_buf, off_t offset, size_t size)
+{
+ const uint8_t *buf = (const uint8_t *)_buf;
+ union smb_write parms;
+ int block = (tree->session->transport->negotiate.max_xmit - (MIN_SMB_SIZE+32));
+ ssize_t total = 0;
+
+ if (size == 0) {
+ return 0;
+ }
+
+ if (block > 0xFFFF) block = 0xFFFF;
+
+
+ parms.writex.level = RAW_WRITE_WRITEX;
+ parms.writex.in.file.fnum = fnum;
+ parms.writex.in.wmode = write_mode;
+ parms.writex.in.remaining = 0;
+
+ while (total < size) {
+ NTSTATUS status;
+
+ block = MIN(block, size - total);
+
+ parms.writex.in.offset = offset;
+ parms.writex.in.count = block;
+ parms.writex.in.data = buf;
+
+ status = smb_raw_write(tree, &parms);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return -1;
+ }
+
+ offset += parms.writex.out.nwritten;
+ total += parms.writex.out.nwritten;
+ buf += parms.writex.out.nwritten;
+ }
+
+ return total;
+}
+
+/****************************************************************************
+ write to a file using a SMBwrite and not bypassing 0 byte writes
+****************************************************************************/
+ssize_t smbcli_smbwrite(struct smbcli_tree *tree,
+ int fnum, const void *_buf, off_t offset, size_t size1)
+{
+ const uint8_t *buf = (const uint8_t *)_buf;
+ union smb_write parms;
+ ssize_t total = 0;
+
+ parms.write.level = RAW_WRITE_WRITE;
+ parms.write.in.remaining = 0;
+
+ do {
+ size_t size = MIN(size1, tree->session->transport->negotiate.max_xmit - 48);
+ if (size > 0xFFFF) size = 0xFFFF;
+
+ parms.write.in.file.fnum = fnum;
+ parms.write.in.offset = offset;
+ parms.write.in.count = size;
+ parms.write.in.data = buf + total;
+
+ if (NT_STATUS_IS_ERR(smb_raw_write(tree, &parms)))
+ return -1;
+
+ size = parms.write.out.nwritten;
+ if (size == 0)
+ break;
+
+ size1 -= size;
+ total += size;
+ offset += size;
+ } while (size1);
+
+ return total;
+}
diff --git a/source4/libcli/clitrans2.c b/source4/libcli/clitrans2.c
new file mode 100644
index 0000000000..5c5ba6585f
--- /dev/null
+++ b/source4/libcli/clitrans2.c
@@ -0,0 +1,224 @@
+/*
+ Unix SMB/CIFS implementation.
+ client trans2 calls
+ Copyright (C) James J Myers 2003 <myersjj@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/libcli.h"
+
+/****************************************************************************
+send a qpathinfo call
+****************************************************************************/
+NTSTATUS smbcli_qpathinfo(struct smbcli_tree *tree, const char *fname,
+ time_t *c_time, time_t *a_time, time_t *m_time,
+ size_t *size, uint16_t *mode)
+{
+ union smb_fileinfo parms;
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+
+ mem_ctx = talloc_init("smbcli_qpathinfo");
+ if (!mem_ctx) return NT_STATUS_NO_MEMORY;
+
+ parms.standard.level = RAW_FILEINFO_STANDARD;
+ parms.standard.in.file.path = fname;
+
+ status = smb_raw_pathinfo(tree, mem_ctx, &parms);
+ talloc_free(mem_ctx);
+ if (!NT_STATUS_IS_OK(status))
+ return status;
+
+ if (c_time) {
+ *c_time = parms.standard.out.create_time;
+ }
+ if (a_time) {
+ *a_time = parms.standard.out.access_time;
+ }
+ if (m_time) {
+ *m_time = parms.standard.out.write_time;
+ }
+ if (size) {
+ *size = parms.standard.out.size;
+ }
+ if (mode) {
+ *mode = parms.standard.out.attrib;
+ }
+
+ return status;
+}
+
+/****************************************************************************
+send a qpathinfo call with the SMB_QUERY_FILE_ALL_INFO info level
+****************************************************************************/
+NTSTATUS smbcli_qpathinfo2(struct smbcli_tree *tree, const char *fname,
+ time_t *c_time, time_t *a_time, time_t *m_time,
+ time_t *w_time, size_t *size, uint16_t *mode,
+ ino_t *ino)
+{
+ union smb_fileinfo parms;
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+
+ mem_ctx = talloc_init("smbcli_qfilename");
+ if (!mem_ctx) return NT_STATUS_NO_MEMORY;
+
+ parms.all_info.level = RAW_FILEINFO_ALL_INFO;
+ parms.all_info.in.file.path = fname;
+
+ status = smb_raw_pathinfo(tree, mem_ctx, &parms);
+ talloc_free(mem_ctx);
+ if (!NT_STATUS_IS_OK(status))
+ return status;
+
+ if (c_time) {
+ *c_time = nt_time_to_unix(parms.all_info.out.create_time);
+ }
+ if (a_time) {
+ *a_time = nt_time_to_unix(parms.all_info.out.access_time);
+ }
+ if (m_time) {
+ *m_time = nt_time_to_unix(parms.all_info.out.change_time);
+ }
+ if (w_time) {
+ *w_time = nt_time_to_unix(parms.all_info.out.write_time);
+ }
+ if (size) {
+ *size = parms.all_info.out.size;
+ }
+ if (mode) {
+ *mode = parms.all_info.out.attrib;
+ }
+
+ return status;
+}
+
+
+/****************************************************************************
+send a qfileinfo QUERY_FILE_NAME_INFO call
+****************************************************************************/
+NTSTATUS smbcli_qfilename(struct smbcli_tree *tree, int fnum, const char **name)
+{
+ union smb_fileinfo parms;
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+
+ mem_ctx = talloc_init("smbcli_qfilename");
+ if (!mem_ctx) return NT_STATUS_NO_MEMORY;
+
+ parms.name_info.level = RAW_FILEINFO_NAME_INFO;
+ parms.name_info.in.file.fnum = fnum;
+
+ status = smb_raw_fileinfo(tree, mem_ctx, &parms);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(mem_ctx);
+ *name = NULL;
+ return status;
+ }
+
+ *name = strdup(parms.name_info.out.fname.s);
+
+ talloc_free(mem_ctx);
+
+ return status;
+}
+
+
+/****************************************************************************
+send a qfileinfo call
+****************************************************************************/
+NTSTATUS smbcli_qfileinfo(struct smbcli_tree *tree, int fnum,
+ uint16_t *mode, size_t *size,
+ time_t *c_time, time_t *a_time, time_t *m_time,
+ time_t *w_time, ino_t *ino)
+{
+ union smb_fileinfo parms;
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+
+ mem_ctx = talloc_init("smbcli_qfileinfo");
+ if (!mem_ctx)
+ return NT_STATUS_NO_MEMORY;
+
+ parms.all_info.level = RAW_FILEINFO_ALL_INFO;
+ parms.all_info.in.file.fnum = fnum;
+
+ status = smb_raw_fileinfo(tree, mem_ctx, &parms);
+ talloc_free(mem_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (c_time) {
+ *c_time = nt_time_to_unix(parms.all_info.out.create_time);
+ }
+ if (a_time) {
+ *a_time = nt_time_to_unix(parms.all_info.out.access_time);
+ }
+ if (m_time) {
+ *m_time = nt_time_to_unix(parms.all_info.out.change_time);
+ }
+ if (w_time) {
+ *w_time = nt_time_to_unix(parms.all_info.out.write_time);
+ }
+ if (mode) {
+ *mode = parms.all_info.out.attrib;
+ }
+ if (size) {
+ *size = (size_t)parms.all_info.out.size;
+ }
+ if (ino) {
+ *ino = 0;
+ }
+
+ return status;
+}
+
+
+/****************************************************************************
+send a qpathinfo SMB_QUERY_FILE_ALT_NAME_INFO call
+****************************************************************************/
+NTSTATUS smbcli_qpathinfo_alt_name(struct smbcli_tree *tree, const char *fname,
+ const char **alt_name)
+{
+ union smb_fileinfo parms;
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+
+ parms.alt_name_info.level = RAW_FILEINFO_ALT_NAME_INFO;
+ parms.alt_name_info.in.file.path = fname;
+
+ mem_ctx = talloc_init("smbcli_qpathinfo_alt_name");
+ if (!mem_ctx) return NT_STATUS_NO_MEMORY;
+
+ status = smb_raw_pathinfo(tree, mem_ctx, &parms);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(mem_ctx);
+ *alt_name = NULL;
+ return smbcli_nt_error(tree);
+ }
+
+ if (!parms.alt_name_info.out.fname.s) {
+ *alt_name = strdup("");
+ } else {
+ *alt_name = strdup(parms.alt_name_info.out.fname.s);
+ }
+
+ talloc_free(mem_ctx);
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/libcli/composite/composite.c b/source4/libcli/composite/composite.c
new file mode 100644
index 0000000000..ab32175d00
--- /dev/null
+++ b/source4/libcli/composite/composite.c
@@ -0,0 +1,218 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Volker Lendecke 2005
+ 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 <http://www.gnu.org/licenses/>.
+*/
+/*
+ composite API helper functions
+*/
+
+#include "includes.h"
+#include "lib/events/events.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/composite/composite.h"
+#include "lib/messaging/irpc.h"
+#include "librpc/rpc/dcerpc.h"
+#include "../libcli/nbt/libnbt.h"
+
+/*
+ create a new composite_context structure
+ and initialize it
+*/
+_PUBLIC_ struct composite_context *composite_create(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev)
+{
+ struct composite_context *c;
+
+ c = talloc_zero(mem_ctx, struct composite_context);
+ if (!c) return NULL;
+ c->state = COMPOSITE_STATE_IN_PROGRESS;
+ c->event_ctx = talloc_reference(c, ev);
+ if (!c->event_ctx) {
+ talloc_free(c);
+ return NULL;
+ }
+
+ return c;
+}
+
+/*
+ block until a composite function has completed, then return the status
+*/
+_PUBLIC_ NTSTATUS composite_wait(struct composite_context *c)
+{
+ if (c == NULL) return NT_STATUS_NO_MEMORY;
+
+ c->used_wait = true;
+
+ while (c->state < COMPOSITE_STATE_DONE) {
+ if (event_loop_once(c->event_ctx) != 0) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ return c->status;
+}
+
+/*
+ block until a composite function has completed, then return the status.
+ Free the composite context before returning
+*/
+_PUBLIC_ NTSTATUS composite_wait_free(struct composite_context *c)
+{
+ NTSTATUS status = composite_wait(c);
+ talloc_free(c);
+ return status;
+}
+
+/*
+ callback from composite_done() and composite_error()
+
+ this is used to allow for a composite function to complete without
+ going through any state transitions. When that happens the caller
+ has had no opportunity to fill in the async callback fields
+ (ctx->async.fn and ctx->async.private_data) which means the usual way of
+ dealing with composite functions doesn't work. To cope with this,
+ we trigger a timer event that will happen then the event loop is
+ re-entered. This gives the caller a chance to setup the callback,
+ and allows the caller to ignore the fact that the composite
+ function completed early
+*/
+static void composite_trigger(struct tevent_context *ev, struct tevent_timer *te,
+ struct timeval t, void *ptr)
+{
+ struct composite_context *c = talloc_get_type(ptr, struct composite_context);
+ if (c->async.fn) {
+ c->async.fn(c);
+ }
+}
+
+
+_PUBLIC_ void composite_error(struct composite_context *ctx, NTSTATUS status)
+{
+ /* you are allowed to pass NT_STATUS_OK to composite_error(), in which
+ case it is equivalent to composite_done() */
+ if (NT_STATUS_IS_OK(status)) {
+ composite_done(ctx);
+ return;
+ }
+ if (!ctx->used_wait && !ctx->async.fn) {
+ event_add_timed(ctx->event_ctx, ctx, timeval_zero(), composite_trigger, ctx);
+ }
+ ctx->status = status;
+ ctx->state = COMPOSITE_STATE_ERROR;
+ if (ctx->async.fn != NULL) {
+ ctx->async.fn(ctx);
+ }
+}
+
+_PUBLIC_ bool composite_nomem(const void *p, struct composite_context *ctx)
+{
+ if (p != NULL) {
+ return false;
+ }
+ composite_error(ctx, NT_STATUS_NO_MEMORY);
+ return true;
+}
+
+_PUBLIC_ bool composite_is_ok(struct composite_context *ctx)
+{
+ if (NT_STATUS_IS_OK(ctx->status)) {
+ return true;
+ }
+ composite_error(ctx, ctx->status);
+ return false;
+}
+
+_PUBLIC_ void composite_done(struct composite_context *ctx)
+{
+ if (!ctx->used_wait && !ctx->async.fn) {
+ event_add_timed(ctx->event_ctx, ctx, timeval_zero(), composite_trigger, ctx);
+ }
+ ctx->state = COMPOSITE_STATE_DONE;
+ if (ctx->async.fn != NULL) {
+ ctx->async.fn(ctx);
+ }
+}
+
+_PUBLIC_ void composite_continue(struct composite_context *ctx,
+ struct composite_context *new_ctx,
+ void (*continuation)(struct composite_context *),
+ void *private_data)
+{
+ if (composite_nomem(new_ctx, ctx)) return;
+ new_ctx->async.fn = continuation;
+ new_ctx->async.private_data = private_data;
+
+ /* if we are setting up a continuation, and the context has
+ already finished, then we should run the callback with an
+ immediate event, otherwise we can be stuck forever */
+ if (new_ctx->state >= COMPOSITE_STATE_DONE && continuation) {
+ event_add_timed(new_ctx->event_ctx, new_ctx, timeval_zero(), composite_trigger, new_ctx);
+ }
+}
+
+_PUBLIC_ void composite_continue_rpc(struct composite_context *ctx,
+ struct rpc_request *new_req,
+ void (*continuation)(struct rpc_request *),
+ void *private_data)
+{
+ if (composite_nomem(new_req, ctx)) return;
+ new_req->async.callback = continuation;
+ new_req->async.private_data = private_data;
+}
+
+_PUBLIC_ void composite_continue_irpc(struct composite_context *ctx,
+ struct irpc_request *new_req,
+ void (*continuation)(struct irpc_request *),
+ void *private_data)
+{
+ if (composite_nomem(new_req, ctx)) return;
+ new_req->async.fn = continuation;
+ new_req->async.private_data = private_data;
+}
+
+_PUBLIC_ void composite_continue_smb(struct composite_context *ctx,
+ struct smbcli_request *new_req,
+ void (*continuation)(struct smbcli_request *),
+ void *private_data)
+{
+ if (composite_nomem(new_req, ctx)) return;
+ new_req->async.fn = continuation;
+ new_req->async.private_data = private_data;
+}
+
+_PUBLIC_ void composite_continue_smb2(struct composite_context *ctx,
+ struct smb2_request *new_req,
+ void (*continuation)(struct smb2_request *),
+ void *private_data)
+{
+ if (composite_nomem(new_req, ctx)) return;
+ new_req->async.fn = continuation;
+ new_req->async.private_data = private_data;
+}
+
+_PUBLIC_ void composite_continue_nbt(struct composite_context *ctx,
+ struct nbt_name_request *new_req,
+ void (*continuation)(struct nbt_name_request *),
+ void *private_data)
+{
+ if (composite_nomem(new_req, ctx)) return;
+ new_req->async.fn = continuation;
+ new_req->async.private_data = private_data;
+}
diff --git a/source4/libcli/composite/composite.h b/source4/libcli/composite/composite.h
new file mode 100644
index 0000000000..a99a762c5f
--- /dev/null
+++ b/source4/libcli/composite/composite.h
@@ -0,0 +1,109 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ composite request interfaces
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __COMPOSITE_H__
+#define __COMPOSITE_H__
+
+#include "libcli/raw/interfaces.h"
+
+struct tevent_context;
+
+/*
+ this defines the structures associated with "composite"
+ requests. Composite requests are libcli requests that are internally
+ implemented as multiple async calls, but can be treated as a
+ single call via these composite calls. The composite calls are
+ particularly designed to be used in async applications.
+ you can also stack multiple level of composite call
+*/
+
+/*
+ a composite call moves between the following 3 states.
+*/
+enum composite_state { COMPOSITE_STATE_INIT, /* we are creating the request */
+ COMPOSITE_STATE_IN_PROGRESS, /* the request is in the outgoing socket Q */
+ COMPOSITE_STATE_DONE, /* the request is received by the caller finished */
+ COMPOSITE_STATE_ERROR }; /* a packet or transport level error has occurred */
+
+/* the context of one "composite" call */
+struct composite_context {
+ /* the external state - will be queried by the caller */
+ enum composite_state state;
+
+ /* a private pointer for use by the composite function
+ implementation */
+ void *private_data;
+
+ /* status code when finished */
+ NTSTATUS status;
+
+ /* the event context we are using */
+ struct tevent_context *event_ctx;
+
+ /* information on what to do on completion */
+ struct {
+ void (*fn)(struct composite_context *);
+ void *private_data;
+ } async;
+
+ bool used_wait;
+};
+
+struct irpc_request;
+struct smbcli_request;
+struct smb2_request;
+struct rpc_request;
+struct nbt_name_request;
+
+struct composite_context *composite_create(TALLOC_CTX *mem_ctx, struct tevent_context *ev);
+bool composite_nomem(const void *p, struct composite_context *ctx);
+void composite_continue(struct composite_context *ctx,
+ struct composite_context *new_ctx,
+ void (*continuation)(struct composite_context *),
+ void *private_data);
+void composite_continue_rpc(struct composite_context *ctx,
+ struct rpc_request *new_req,
+ void (*continuation)(struct rpc_request *),
+ void *private_data);
+void composite_continue_irpc(struct composite_context *ctx,
+ struct irpc_request *new_req,
+ void (*continuation)(struct irpc_request *),
+ void *private_data);
+void composite_continue_smb(struct composite_context *ctx,
+ struct smbcli_request *new_req,
+ void (*continuation)(struct smbcli_request *),
+ void *private_data);
+void composite_continue_smb2(struct composite_context *ctx,
+ struct smb2_request *new_req,
+ void (*continuation)(struct smb2_request *),
+ void *private_data);
+void composite_continue_nbt(struct composite_context *ctx,
+ struct nbt_name_request *new_req,
+ void (*continuation)(struct nbt_name_request *),
+ void *private_data);
+bool composite_is_ok(struct composite_context *ctx);
+void composite_done(struct composite_context *ctx);
+void composite_error(struct composite_context *ctx, NTSTATUS status);
+NTSTATUS composite_wait(struct composite_context *c);
+NTSTATUS composite_wait_free(struct composite_context *c);
+
+
+#endif /* __COMPOSITE_H__ */
diff --git a/source4/libcli/config.mk b/source4/libcli/config.mk
new file mode 100644
index 0000000000..dc3431ab9f
--- /dev/null
+++ b/source4/libcli/config.mk
@@ -0,0 +1,169 @@
+mkinclude auth/config.mk
+mkinclude ldap/config.mk
+mkinclude security/config.mk
+mkinclude wbclient/config.mk
+
+[SUBSYSTEM::LIBSAMBA-ERRORS]
+
+LIBSAMBA-ERRORS_OBJ_FILES = $(addprefix ../libcli/util/, doserr.o ) $(libclisrcdir)/util/errormap.o $(libclisrcdir)/util/nterr.o
+
+PUBLIC_HEADERS += $(addprefix ../libcli/util/, error.h ntstatus.h doserr.h werror.h)
+
+[SUBSYSTEM::LIBCLI_LSA]
+PUBLIC_DEPENDENCIES = RPC_NDR_LSA
+PRIVATE_DEPENDENCIES = LIBSECURITY
+
+LIBCLI_LSA_OBJ_FILES = $(libclisrcdir)/util/clilsa.o
+
+$(eval $(call proto_header_template,$(libclisrcdir)/util/clilsa.h,$(LIBCLI_LSA_OBJ_FILES:.o=.c)))
+
+[SUBSYSTEM::LIBCLI_COMPOSITE]
+PUBLIC_DEPENDENCIES = LIBEVENTS
+
+LIBCLI_COMPOSITE_OBJ_FILES = $(libclisrcdir)/composite/composite.o
+$(eval $(call proto_header_template,$(libclisrcdir)/composite/proto.h,$(LIBCLI_COMPOSITE_OBJ_FILES:.o=.c)))
+
+[SUBSYSTEM::LIBCLI_SMB_COMPOSITE]
+PUBLIC_DEPENDENCIES = LIBCLI_COMPOSITE CREDENTIALS gensec LIBCLI_RESOLVE
+
+LIBCLI_SMB_COMPOSITE_OBJ_FILES = $(addprefix $(libclisrcdir)/smb_composite/, \
+ loadfile.o \
+ savefile.o \
+ connect.o \
+ sesssetup.o \
+ fetchfile.o \
+ appendacl.o \
+ fsinfo.o \
+ smb2.o)
+
+$(eval $(call proto_header_template,$(libclisrcdir)/smb_composite/proto.h,$(LIBCLI_SMB_COMPOSITE_OBJ_FILES:.o=.c)))
+
+[SUBSYSTEM::NDR_NBT_BUF]
+
+NDR_NBT_BUF_OBJ_FILES = $(libclinbtsrcdir)/nbtname.o
+
+$(eval $(call proto_header_template,$(libclinbtsrcdir)/nbtname.h,$(NDR_NBT_BUF_OBJ_FILES:.o=.c)))
+
+[SUBSYSTEM::LIBCLI_NBT]
+PUBLIC_DEPENDENCIES = LIBNDR NDR_NBT LIBCLI_COMPOSITE LIBEVENTS \
+ NDR_SECURITY samba_socket LIBSAMBA-UTIL
+
+LIBCLI_NBT_OBJ_FILES = $(addprefix $(libclinbtsrcdir)/, \
+ nbtsocket.o \
+ namequery.o \
+ nameregister.o \
+ namerefresh.o \
+ namerelease.o)
+
+[BINARY::nmblookup]
+INSTALLDIR = BINDIR
+PRIVATE_DEPENDENCIES = \
+ LIBSAMBA-HOSTCONFIG \
+ LIBSAMBA-UTIL \
+ LIBCLI_NBT \
+ LIBPOPT \
+ POPT_SAMBA \
+ LIBNETIF \
+ LIBCLI_RESOLVE
+
+nmblookup_OBJ_FILES = $(libclinbtsrcdir)/tools/nmblookup.o
+MANPAGES += $(libclinbtsrcdir)/man/nmblookup.1
+
+[SUBSYSTEM::LIBCLI_NDR_NETLOGON]
+PUBLIC_DEPENDENCIES = LIBNDR \
+ NDR_SECURITY
+
+LIBCLI_NDR_NETLOGON_OBJ_FILES = $(addprefix $(libclinbtsrcdir)/../, ndr_netlogon.o)
+
+[SUBSYSTEM::LIBCLI_NETLOGON]
+PUBLIC_DEPENDENCIES = LIBSAMBA-UTIL LIBCLI_NDR_NETLOGON
+
+LIBCLI_NETLOGON_OBJ_FILES = $(addprefix $(libclinbtsrcdir)/, \
+ ../netlogon.o)
+
+[PYTHON::python_netbios]
+LIBRARY_REALNAME = samba/netbios.$(SHLIBEXT)
+PUBLIC_DEPENDENCIES = LIBCLI_NBT DYNCONFIG LIBSAMBA-HOSTCONFIG
+
+python_netbios_OBJ_FILES = $(libclinbtsrcdir)/pynbt.o
+
+[SUBSYSTEM::LIBCLI_DGRAM]
+PUBLIC_DEPENDENCIES = LIBCLI_NBT LIBNDR LIBCLI_RESOLVE LIBCLI_NETLOGON
+
+LIBCLI_DGRAM_OBJ_FILES = $(addprefix $(libclisrcdir)/dgram/, \
+ dgramsocket.o \
+ mailslot.o \
+ netlogon.o \
+ browse.o)
+
+[SUBSYSTEM::LIBCLI_CLDAP]
+PUBLIC_DEPENDENCIES = LIBCLI_LDAP
+PRIVATE_DEPENDENCIES = LIBSAMBA-UTIL LIBLDB LIBCLI_NETLOGON
+
+LIBCLI_CLDAP_OBJ_FILES = $(libclisrcdir)/cldap/cldap.o
+# PUBLIC_HEADERS += $(libclisrcdir)/cldap/cldap.h
+
+[SUBSYSTEM::LIBCLI_WREPL]
+PUBLIC_DEPENDENCIES = NDR_WINSREPL samba_socket LIBEVENTS LIBPACKET
+
+LIBCLI_WREPL_OBJ_FILES = $(libclisrcdir)/wrepl/winsrepl.o
+
+$(eval $(call proto_header_template,$(libclisrcdir)/wrepl/winsrepl_proto.h,$(LIBCLI_WREPL_OBJ_FILES:.o=.c)))
+
+[SUBSYSTEM::LIBCLI_RESOLVE]
+PUBLIC_DEPENDENCIES = NDR_NBT
+
+LIBCLI_RESOLVE_OBJ_FILES = $(libclisrcdir)/resolve/resolve.o
+
+$(eval $(call proto_header_template,$(libclisrcdir)/resolve/proto.h,$(LIBCLI_RESOLVE_OBJ_FILES:.o=.c)))
+
+[SUBSYSTEM::LP_RESOLVE]
+PRIVATE_DEPENDENCIES = LIBCLI_NBT LIBSAMBA-HOSTCONFIG LIBNETIF
+
+LP_RESOLVE_OBJ_FILES = $(addprefix $(libclisrcdir)/resolve/, \
+ bcast.o nbtlist.o wins.o \
+ dns_ex.o \
+ host.o resolve_lp.o)
+
+$(eval $(call proto_header_template,$(libclisrcdir)/resolve/lp_proto.h,$(LP_RESOLVE_OBJ_FILES:.o=.c)))
+
+[SUBSYSTEM::LIBCLI_FINDDCS]
+PUBLIC_DEPENDENCIES = LIBCLI_NBT MESSAGING
+
+LIBCLI_FINDDCS_OBJ_FILES = $(libclisrcdir)/finddcs.o
+
+$(eval $(call proto_header_template,$(libclisrcdir)/finddcs.h,$(LIBCLI_FINDDCS_OBJ_FILES:.o=.c)))
+
+[SUBSYSTEM::LIBCLI_SMB]
+PUBLIC_DEPENDENCIES = LIBCLI_RAW LIBSAMBA-ERRORS LIBCLI_AUTH \
+ LIBCLI_SMB_COMPOSITE LIBCLI_NBT LIBSECURITY LIBCLI_RESOLVE \
+ LIBCLI_DGRAM LIBCLI_SMB2 LIBCLI_FINDDCS samba_socket
+
+LIBCLI_SMB_OBJ_FILES = $(addprefix $(libclisrcdir)/, \
+ clireadwrite.o \
+ cliconnect.o \
+ clifile.o \
+ clilist.o \
+ clitrans2.o \
+ climessage.o \
+ clideltree.o)
+
+$(eval $(call proto_header_template,$(libclisrcdir)/libcli_proto.h,$(LIBCLI_SMB_OBJ_FILES:.o=.c)))
+
+# PUBLIC_HEADERS += $(libclisrcdir)/libcli.h
+
+[SUBSYSTEM::LIBCLI_RAW]
+PRIVATE_DEPENDENCIES = LIBCLI_COMPOSITE LP_RESOLVE gensec LIBCLI_RESOLVE LIBSECURITY LIBNDR
+#LDFLAGS = $(LIBCLI_SMB_COMPOSITE_OUTPUT)
+PUBLIC_DEPENDENCIES = samba_socket LIBPACKET gensec LIBCRYPTO CREDENTIALS
+
+LIBCLI_RAW_OBJ_FILES = $(addprefix $(libclisrcdir)/raw/, rawfile.o smb_signing.o clisocket.o \
+ clitransport.o clisession.o clitree.o clierror.o rawrequest.o \
+ rawreadwrite.o rawsearch.o rawsetfileinfo.o raweas.o rawtrans.o \
+ clioplock.o rawnegotiate.o rawfsinfo.o rawfileinfo.o rawnotify.o \
+ rawioctl.o rawacl.o rawdate.o rawlpq.o rawshadow.o)
+
+
+$(eval $(call proto_header_template,$(libclisrcdir)/raw/raw_proto.h,$(LIBCLI_RAW_OBJ_FILES:.o=.c)))
+
+mkinclude smb2/config.mk
diff --git a/source4/libcli/dgram/browse.c b/source4/libcli/dgram/browse.c
new file mode 100644
index 0000000000..14d8278635
--- /dev/null
+++ b/source4/libcli/dgram/browse.c
@@ -0,0 +1,114 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ handling for browsing dgram requests
+
+ Copyright (C) Jelmer Vernooij 2005
+ Heavily based on ntlogon.c
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/dgram/libdgram.h"
+#include "lib/socket/socket.h"
+#include "libcli/resolve/resolve.h"
+#include "librpc/gen_ndr/ndr_nbt.h"
+#include "param/param.h"
+
+NTSTATUS dgram_mailslot_browse_send(struct nbt_dgram_socket *dgmsock,
+ struct nbt_name *dest_name,
+ struct socket_address *dest,
+ struct nbt_name *src_name,
+ struct nbt_browse_packet *request)
+{
+ NTSTATUS status;
+ enum ndr_err_code ndr_err;
+ DATA_BLOB blob;
+ TALLOC_CTX *tmp_ctx = talloc_new(dgmsock);
+
+ ndr_err = ndr_push_struct_blob(&blob, tmp_ctx, dgmsock->iconv_convenience, request,
+ (ndr_push_flags_fn_t)ndr_push_nbt_browse_packet);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ talloc_free(tmp_ctx);
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ status = dgram_mailslot_send(dgmsock, DGRAM_DIRECT_UNIQUE,
+ NBT_MAILSLOT_BROWSE,
+ dest_name, dest,
+ src_name, &blob);
+ talloc_free(tmp_ctx);
+ return status;
+}
+
+NTSTATUS dgram_mailslot_browse_reply(struct nbt_dgram_socket *dgmsock,
+ struct nbt_dgram_packet *request,
+ const char *mailslot_name,
+ const char *my_netbios_name,
+ struct nbt_browse_packet *reply)
+{
+ NTSTATUS status;
+ enum ndr_err_code ndr_err;
+ DATA_BLOB blob;
+ TALLOC_CTX *tmp_ctx = talloc_new(dgmsock);
+ struct nbt_name myname;
+ struct socket_address *dest;
+
+ ndr_err = ndr_push_struct_blob(&blob, tmp_ctx, dgmsock->iconv_convenience, reply,
+ (ndr_push_flags_fn_t)ndr_push_nbt_browse_packet);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ talloc_free(tmp_ctx);
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ make_nbt_name_client(&myname, my_netbios_name);
+
+ dest = socket_address_from_strings(tmp_ctx, dgmsock->sock->backend_name,
+ request->src_addr, request->src_port);
+ if (!dest) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = dgram_mailslot_send(dgmsock, DGRAM_DIRECT_UNIQUE,
+ mailslot_name,
+ &request->data.msg.source_name,
+ dest,
+ &myname, &blob);
+ talloc_free(tmp_ctx);
+ return status;
+}
+
+NTSTATUS dgram_mailslot_browse_parse(struct dgram_mailslot_handler *dgmslot,
+ TALLOC_CTX *mem_ctx,
+ struct nbt_dgram_packet *dgram,
+ struct nbt_browse_packet *pkt)
+{
+ DATA_BLOB data = dgram_mailslot_data(dgram);
+ enum ndr_err_code ndr_err;
+
+ ndr_err = ndr_pull_struct_blob(&data, mem_ctx, dgmslot->dgmsock->iconv_convenience, pkt,
+ (ndr_pull_flags_fn_t)ndr_pull_nbt_browse_packet);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
+ DEBUG(0,("Failed to parse browse packet of length %d: %s\n",
+ (int)data.length, nt_errstr(status)));
+ if (DEBUGLVL(10)) {
+ file_save("browse.dat", data.data, data.length);
+ }
+ return status;
+ }
+ return NT_STATUS_OK;
+}
diff --git a/source4/libcli/dgram/dgramsocket.c b/source4/libcli/dgram/dgramsocket.c
new file mode 100644
index 0000000000..751706d2c5
--- /dev/null
+++ b/source4/libcli/dgram/dgramsocket.c
@@ -0,0 +1,245 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ low level socket handling for nbt dgram requests (UDP138)
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/events/events.h"
+#include "../lib/util/dlinklist.h"
+#include "libcli/dgram/libdgram.h"
+#include "lib/socket/socket.h"
+#include "librpc/gen_ndr/ndr_nbt.h"
+
+
+/*
+ handle recv events on a nbt dgram socket
+*/
+static void dgm_socket_recv(struct nbt_dgram_socket *dgmsock)
+{
+ TALLOC_CTX *tmp_ctx = talloc_new(dgmsock);
+ NTSTATUS status;
+ struct socket_address *src;
+ DATA_BLOB blob;
+ size_t nread, dsize;
+ struct nbt_dgram_packet *packet;
+ const char *mailslot_name;
+ enum ndr_err_code ndr_err;
+
+ status = socket_pending(dgmsock->sock, &dsize);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ return;
+ }
+
+ blob = data_blob_talloc(tmp_ctx, NULL, dsize);
+ if (blob.data == NULL) {
+ talloc_free(tmp_ctx);
+ return;
+ }
+
+ status = socket_recvfrom(dgmsock->sock, blob.data, blob.length, &nread,
+ tmp_ctx, &src);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ return;
+ }
+ blob.length = nread;
+
+ DEBUG(2,("Received dgram packet of length %d from %s:%d\n",
+ (int)blob.length, src->addr, src->port));
+
+ packet = talloc(tmp_ctx, struct nbt_dgram_packet);
+ if (packet == NULL) {
+ talloc_free(tmp_ctx);
+ return;
+ }
+
+ /* parse the request */
+ ndr_err = ndr_pull_struct_blob(&blob, packet, dgmsock->iconv_convenience, packet,
+ (ndr_pull_flags_fn_t)ndr_pull_nbt_dgram_packet);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ DEBUG(2,("Failed to parse incoming NBT DGRAM packet - %s\n",
+ nt_errstr(status)));
+ talloc_free(tmp_ctx);
+ return;
+ }
+
+ /* if this is a mailslot message, then see if we can dispatch it to a handler */
+ mailslot_name = dgram_mailslot_name(packet);
+ if (mailslot_name) {
+ struct dgram_mailslot_handler *dgmslot;
+ dgmslot = dgram_mailslot_find(dgmsock, mailslot_name);
+ if (dgmslot) {
+ dgmslot->handler(dgmslot, packet, src);
+ } else {
+ DEBUG(2,("No mailslot handler for '%s'\n", mailslot_name));
+ }
+ } else {
+ /* dispatch if there is a general handler */
+ if (dgmsock->incoming.handler) {
+ dgmsock->incoming.handler(dgmsock, packet, src);
+ }
+ }
+
+ talloc_free(tmp_ctx);
+}
+
+
+/*
+ handle send events on a nbt dgram socket
+*/
+static void dgm_socket_send(struct nbt_dgram_socket *dgmsock)
+{
+ struct nbt_dgram_request *req;
+ NTSTATUS status;
+
+ while ((req = dgmsock->send_queue)) {
+ size_t len;
+
+ len = req->encoded.length;
+ status = socket_sendto(dgmsock->sock, &req->encoded, &len,
+ req->dest);
+ if (NT_STATUS_IS_ERR(status)) {
+ DEBUG(3,("Failed to send datagram of length %u to %s:%d: %s\n",
+ (unsigned)req->encoded.length, req->dest->addr, req->dest->port,
+ nt_errstr(status)));
+ DLIST_REMOVE(dgmsock->send_queue, req);
+ talloc_free(req);
+ continue;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) return;
+
+ DLIST_REMOVE(dgmsock->send_queue, req);
+ talloc_free(req);
+ }
+
+ EVENT_FD_NOT_WRITEABLE(dgmsock->fde);
+ return;
+}
+
+
+/*
+ handle fd events on a nbt_dgram_socket
+*/
+static void dgm_socket_handler(struct tevent_context *ev, struct tevent_fd *fde,
+ uint16_t flags, void *private_data)
+{
+ struct nbt_dgram_socket *dgmsock = talloc_get_type(private_data,
+ struct nbt_dgram_socket);
+ if (flags & EVENT_FD_WRITE) {
+ dgm_socket_send(dgmsock);
+ }
+ if (flags & EVENT_FD_READ) {
+ dgm_socket_recv(dgmsock);
+ }
+}
+
+/*
+ initialise a nbt_dgram_socket. The event_ctx is optional, if provided
+ then operations will use that event context
+*/
+struct nbt_dgram_socket *nbt_dgram_socket_init(TALLOC_CTX *mem_ctx,
+ struct tevent_context *event_ctx,
+ struct smb_iconv_convenience *iconv_convenience)
+{
+ struct nbt_dgram_socket *dgmsock;
+ NTSTATUS status;
+
+ dgmsock = talloc(mem_ctx, struct nbt_dgram_socket);
+ if (dgmsock == NULL) goto failed;
+
+ dgmsock->event_ctx = talloc_reference(dgmsock, event_ctx);
+ if (dgmsock->event_ctx == NULL) goto failed;
+
+ status = socket_create("ip", SOCKET_TYPE_DGRAM, &dgmsock->sock, 0);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+
+ socket_set_option(dgmsock->sock, "SO_BROADCAST", "1");
+
+ talloc_steal(dgmsock, dgmsock->sock);
+
+ dgmsock->fde = event_add_fd(dgmsock->event_ctx, dgmsock,
+ socket_get_fd(dgmsock->sock), 0,
+ dgm_socket_handler, dgmsock);
+
+ dgmsock->send_queue = NULL;
+ dgmsock->incoming.handler = NULL;
+ dgmsock->mailslot_handlers = NULL;
+ dgmsock->iconv_convenience = iconv_convenience;
+
+ return dgmsock;
+
+failed:
+ talloc_free(dgmsock);
+ return NULL;
+}
+
+
+/*
+ setup a handler for generic incoming requests
+*/
+NTSTATUS dgram_set_incoming_handler(struct nbt_dgram_socket *dgmsock,
+ void (*handler)(struct nbt_dgram_socket *,
+ struct nbt_dgram_packet *,
+ struct socket_address *),
+ void *private_data)
+{
+ dgmsock->incoming.handler = handler;
+ dgmsock->incoming.private_data = private_data;
+ EVENT_FD_READABLE(dgmsock->fde);
+ return NT_STATUS_OK;
+}
+
+
+/*
+ queue a datagram for send
+*/
+NTSTATUS nbt_dgram_send(struct nbt_dgram_socket *dgmsock,
+ struct nbt_dgram_packet *packet,
+ struct socket_address *dest)
+{
+ struct nbt_dgram_request *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+ enum ndr_err_code ndr_err;
+
+ req = talloc(dgmsock, struct nbt_dgram_request);
+ if (req == NULL) goto failed;
+
+ req->dest = dest;
+ if (talloc_reference(req, dest) == NULL) goto failed;
+
+ ndr_err = ndr_push_struct_blob(&req->encoded, req, dgmsock->iconv_convenience, packet,
+ (ndr_push_flags_fn_t)ndr_push_nbt_dgram_packet);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ goto failed;
+ }
+
+ DLIST_ADD_END(dgmsock->send_queue, req, struct nbt_dgram_request *);
+
+ EVENT_FD_WRITEABLE(dgmsock->fde);
+
+ return NT_STATUS_OK;
+
+failed:
+ talloc_free(req);
+ return status;
+}
diff --git a/source4/libcli/dgram/libdgram.h b/source4/libcli/dgram/libdgram.h
new file mode 100644
index 0000000000..a17a6042d9
--- /dev/null
+++ b/source4/libcli/dgram/libdgram.h
@@ -0,0 +1,157 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ a raw async NBT DGRAM library
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "../libcli/netlogon.h"
+
+/*
+ a datagram name request
+*/
+struct nbt_dgram_request {
+ struct nbt_dgram_request *next, *prev;
+
+ /* where to send the request */
+ struct socket_address *dest;
+
+ /* the encoded request */
+ DATA_BLOB encoded;
+};
+
+/*
+ context structure for operations on dgram packets
+*/
+struct nbt_dgram_socket {
+ struct socket_context *sock;
+ struct tevent_context *event_ctx;
+ struct smb_iconv_convenience *iconv_convenience;
+
+ /* the fd event */
+ struct tevent_fd *fde;
+
+ /* a queue of outgoing requests */
+ struct nbt_dgram_request *send_queue;
+
+ /* a list of mailslot handlers */
+ struct dgram_mailslot_handler *mailslot_handlers;
+
+ /* what to do with incoming request packets */
+ struct {
+ void (*handler)(struct nbt_dgram_socket *, struct nbt_dgram_packet *,
+ struct socket_address *src);
+ void *private_data;
+ } incoming;
+};
+
+
+/*
+ the mailslot code keeps a list of mailslot handlers. A mailslot
+ handler is a function that receives incoming packets for a specific
+ mailslot name. When a caller needs to send a mailslot and wants to
+ get a reply then it needs to register itself as listening for
+ incoming packets on the reply mailslot
+*/
+
+typedef void (*dgram_mailslot_handler_t)(struct dgram_mailslot_handler *,
+ struct nbt_dgram_packet *,
+ struct socket_address *src);
+
+struct dgram_mailslot_handler {
+ struct dgram_mailslot_handler *next, *prev;
+
+ struct nbt_dgram_socket *dgmsock;
+ const char *mailslot_name;
+
+ dgram_mailslot_handler_t handler;
+ void *private_data;
+};
+
+
+/* prototypes */
+NTSTATUS nbt_dgram_send(struct nbt_dgram_socket *dgmsock,
+ struct nbt_dgram_packet *packet,
+ struct socket_address *dest);
+NTSTATUS dgram_set_incoming_handler(struct nbt_dgram_socket *dgmsock,
+ void (*handler)(struct nbt_dgram_socket *,
+ struct nbt_dgram_packet *,
+ struct socket_address *),
+ void *private_data);
+struct nbt_dgram_socket *nbt_dgram_socket_init(TALLOC_CTX *mem_ctx,
+ struct tevent_context *event_ctx,
+ struct smb_iconv_convenience *);
+
+const char *dgram_mailslot_name(struct nbt_dgram_packet *packet);
+struct dgram_mailslot_handler *dgram_mailslot_find(struct nbt_dgram_socket *dgmsock,
+ const char *mailslot_name);
+struct dgram_mailslot_handler *dgram_mailslot_listen(struct nbt_dgram_socket *dgmsock,
+ const char *mailslot_name,
+ dgram_mailslot_handler_t handler,
+ void *private_data);
+struct dgram_mailslot_handler *dgram_mailslot_temp(struct nbt_dgram_socket *dgmsock,
+ const char *mailslot_name,
+ dgram_mailslot_handler_t handler,
+ void *private_data);
+DATA_BLOB dgram_mailslot_data(struct nbt_dgram_packet *dgram);
+
+
+NTSTATUS dgram_mailslot_send(struct nbt_dgram_socket *dgmsock,
+ enum dgram_msg_type msg_type,
+ const char *mailslot_name,
+ struct nbt_name *dest_name,
+ struct socket_address *dest,
+ struct nbt_name *src_name,
+ DATA_BLOB *request);
+
+NTSTATUS dgram_mailslot_netlogon_send(struct nbt_dgram_socket *dgmsock,
+ struct nbt_name *dest_name,
+ struct socket_address *dest,
+ const char *mailslot_name,
+ struct nbt_name *src_name,
+ struct nbt_netlogon_packet *request);
+NTSTATUS dgram_mailslot_netlogon_reply(struct nbt_dgram_socket *dgmsock,
+ struct nbt_dgram_packet *request,
+ const char *my_netbios_name,
+ const char *mailslot_name,
+ struct nbt_netlogon_response *reply);
+NTSTATUS dgram_mailslot_netlogon_parse_request(struct dgram_mailslot_handler *dgmslot,
+ TALLOC_CTX *mem_ctx,
+ struct nbt_dgram_packet *dgram,
+ struct nbt_netlogon_packet *netlogon);
+
+NTSTATUS dgram_mailslot_netlogon_parse_response(struct dgram_mailslot_handler *dgmslot,
+ TALLOC_CTX *mem_ctx,
+ struct nbt_dgram_packet *dgram,
+ struct nbt_netlogon_response *netlogon);
+
+NTSTATUS dgram_mailslot_browse_send(struct nbt_dgram_socket *dgmsock,
+ struct nbt_name *dest_name,
+ struct socket_address *dest,
+ struct nbt_name *src_name,
+ struct nbt_browse_packet *request);
+
+NTSTATUS dgram_mailslot_browse_reply(struct nbt_dgram_socket *dgmsock,
+ struct nbt_dgram_packet *request,
+ const char *mailslot_name,
+ const char *my_netbios_name,
+ struct nbt_browse_packet *reply);
+
+NTSTATUS dgram_mailslot_browse_parse(struct dgram_mailslot_handler *dgmslot,
+ TALLOC_CTX *mem_ctx,
+ struct nbt_dgram_packet *dgram,
+ struct nbt_browse_packet *pkt);
diff --git a/source4/libcli/dgram/mailslot.c b/source4/libcli/dgram/mailslot.c
new file mode 100644
index 0000000000..261946e458
--- /dev/null
+++ b/source4/libcli/dgram/mailslot.c
@@ -0,0 +1,226 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ packet handling for mailslot requests.
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ This implements "Class 2 mailslots", i.e. the communication mechanism
+ used for all mailslot packets smaller than 425 bytes.
+
+ "Class 1 mailslots" (which use SMB) are used for messages larger
+ than 426 bytes and are supported on some systems. These are not implemented
+ in Samba4 yet, as there don't appear to be any core services that use
+ them.
+
+ 425 and 426-byte sized messages are not supported at all.
+*/
+
+#include "includes.h"
+#include "lib/events/events.h"
+#include "../lib/util/dlinklist.h"
+#include "libcli/dgram/libdgram.h"
+#include "lib/socket/socket.h"
+
+/*
+ destroy a mailslot handler
+*/
+static int dgram_mailslot_destructor(struct dgram_mailslot_handler *dgmslot)
+{
+ DLIST_REMOVE(dgmslot->dgmsock->mailslot_handlers, dgmslot);
+ return 0;
+}
+
+/*
+ start listening on a mailslot. talloc_free() the handle to stop listening
+*/
+struct dgram_mailslot_handler *dgram_mailslot_listen(struct nbt_dgram_socket *dgmsock,
+ const char *mailslot_name,
+ dgram_mailslot_handler_t handler,
+ void *private_data)
+{
+ struct dgram_mailslot_handler *dgmslot;
+
+ dgmslot = talloc(dgmsock, struct dgram_mailslot_handler);
+ if (dgmslot == NULL) return NULL;
+
+ dgmslot->dgmsock = dgmsock;
+ dgmslot->mailslot_name = talloc_strdup(dgmslot, mailslot_name);
+ if (dgmslot->mailslot_name == NULL) {
+ talloc_free(dgmslot);
+ return NULL;
+ }
+ dgmslot->handler = handler;
+ dgmslot->private_data = private_data;
+
+ DLIST_ADD(dgmsock->mailslot_handlers, dgmslot);
+ talloc_set_destructor(dgmslot, dgram_mailslot_destructor);
+
+ EVENT_FD_READABLE(dgmsock->fde);
+
+ return dgmslot;
+}
+
+/*
+ find the handler for a specific mailslot name
+*/
+struct dgram_mailslot_handler *dgram_mailslot_find(struct nbt_dgram_socket *dgmsock,
+ const char *mailslot_name)
+{
+ struct dgram_mailslot_handler *h;
+ for (h=dgmsock->mailslot_handlers;h;h=h->next) {
+ if (strcasecmp(h->mailslot_name, mailslot_name) == 0) {
+ return h;
+ }
+ }
+ return NULL;
+}
+
+/*
+ check that a datagram packet is a valid mailslot request, and return the
+ mailslot name if it is, otherwise return NULL
+*/
+const char *dgram_mailslot_name(struct nbt_dgram_packet *packet)
+{
+ if (packet->msg_type != DGRAM_DIRECT_UNIQUE &&
+ packet->msg_type != DGRAM_DIRECT_GROUP &&
+ packet->msg_type != DGRAM_BCAST) {
+ return NULL;
+ }
+ if (packet->data.msg.dgram_body_type != DGRAM_SMB) return NULL;
+ if (packet->data.msg.body.smb.smb_command != SMB_TRANSACTION) return NULL;
+ return packet->data.msg.body.smb.body.trans.mailslot_name;
+}
+
+
+/*
+ create a temporary mailslot handler for a reply mailslot, allocating
+ a new mailslot name using the given base name and a random integer extension
+*/
+struct dgram_mailslot_handler *dgram_mailslot_temp(struct nbt_dgram_socket *dgmsock,
+ const char *mailslot_name,
+ dgram_mailslot_handler_t handler,
+ void *private_data)
+{
+ char *name;
+ int i;
+ struct dgram_mailslot_handler *dgmslot;
+
+ /* try a 100 times at most */
+ for (i=0;i<100;i++) {
+ name = talloc_asprintf(dgmsock, "%s%03u",
+ mailslot_name,
+ generate_random() % 1000);
+ if (name == NULL) return NULL;
+ if (dgram_mailslot_find(dgmsock, name)) {
+ talloc_free(name);
+ return NULL;
+ }
+ dgmslot = dgram_mailslot_listen(dgmsock, name, handler, private_data);
+ talloc_free(name);
+ if (dgmslot != NULL) {
+ return dgmslot;
+ }
+ }
+ DEBUG(2,("Unable to create temporary mailslot from %s\n", mailslot_name));
+ return NULL;
+}
+
+
+/*
+ send a mailslot request
+*/
+NTSTATUS dgram_mailslot_send(struct nbt_dgram_socket *dgmsock,
+ enum dgram_msg_type msg_type,
+ const char *mailslot_name,
+ struct nbt_name *dest_name,
+ struct socket_address *dest,
+ struct nbt_name *src_name,
+ DATA_BLOB *request)
+{
+ TALLOC_CTX *tmp_ctx = talloc_new(dgmsock);
+ struct nbt_dgram_packet packet;
+ struct dgram_message *msg;
+ struct dgram_smb_packet *smb;
+ struct smb_trans_body *trans;
+ struct socket_address *src;
+ NTSTATUS status;
+
+ if (dest->port == 0) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ ZERO_STRUCT(packet);
+ packet.msg_type = msg_type;
+ packet.flags = DGRAM_FLAG_FIRST | DGRAM_NODE_NBDD;
+ packet.dgram_id = generate_random() % UINT16_MAX;
+ src = socket_get_my_addr(dgmsock->sock, tmp_ctx);
+ if (!src) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ packet.src_addr = src->addr;
+ packet.src_port = src->port;
+
+ msg = &packet.data.msg;
+ /* this length calculation is very crude - it should be based on gensize
+ calls */
+ msg->length = 138 + strlen(mailslot_name) + request->length;
+ msg->offset = 0;
+
+ msg->source_name = *src_name;
+ msg->dest_name = *dest_name;
+ msg->dgram_body_type = DGRAM_SMB;
+
+ smb = &msg->body.smb;
+ smb->smb_command = SMB_TRANSACTION;
+
+ trans = &smb->body.trans;
+ trans->total_data_count = request->length;
+ trans->timeout = 1000;
+ trans->data_count = request->length;
+ trans->data_offset = 70 + strlen(mailslot_name);
+ trans->opcode = 1; /* write mail slot */
+ trans->priority = 1;
+ trans->_class = 2;
+ trans->mailslot_name = mailslot_name;
+ trans->data = *request;
+
+ status = nbt_dgram_send(dgmsock, &packet, dest);
+
+ talloc_free(tmp_ctx);
+
+ return status;
+}
+
+/*
+ return the mailslot data portion from a mailslot packet
+*/
+DATA_BLOB dgram_mailslot_data(struct nbt_dgram_packet *dgram)
+{
+ struct smb_trans_body *trans = &dgram->data.msg.body.smb.body.trans;
+ DATA_BLOB ret = trans->data;
+ int pad = trans->data_offset - (70 + strlen(trans->mailslot_name));
+
+ if (pad < 0 || pad > ret.length) {
+ DEBUG(2,("Badly formatted data in mailslot - pad = %d\n", pad));
+ return data_blob(NULL, 0);
+ }
+ ret.data += pad;
+ ret.length -= pad;
+ return ret;
+}
diff --git a/source4/libcli/dgram/netlogon.c b/source4/libcli/dgram/netlogon.c
new file mode 100644
index 0000000000..26b00bdafd
--- /dev/null
+++ b/source4/libcli/dgram/netlogon.c
@@ -0,0 +1,145 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ handling for netlogon dgram requests
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/dgram/libdgram.h"
+#include "lib/socket/socket.h"
+#include "libcli/resolve/resolve.h"
+#include "librpc/gen_ndr/ndr_nbt.h"
+
+/*
+ send a netlogon mailslot request
+*/
+NTSTATUS dgram_mailslot_netlogon_send(struct nbt_dgram_socket *dgmsock,
+ struct nbt_name *dest_name,
+ struct socket_address *dest,
+ const char *mailslot,
+ struct nbt_name *src_name,
+ struct nbt_netlogon_packet *request)
+{
+ NTSTATUS status;
+ enum ndr_err_code ndr_err;
+ DATA_BLOB blob;
+ TALLOC_CTX *tmp_ctx = talloc_new(dgmsock);
+
+ ndr_err = ndr_push_struct_blob(&blob, tmp_ctx,
+ dgmsock->iconv_convenience,
+ request,
+ (ndr_push_flags_fn_t)ndr_push_nbt_netlogon_packet);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ talloc_free(tmp_ctx);
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+
+ status = dgram_mailslot_send(dgmsock, DGRAM_DIRECT_UNIQUE,
+ mailslot,
+ dest_name, dest,
+ src_name, &blob);
+ talloc_free(tmp_ctx);
+ return status;
+}
+
+
+/*
+ send a netlogon mailslot reply
+*/
+NTSTATUS dgram_mailslot_netlogon_reply(struct nbt_dgram_socket *dgmsock,
+ struct nbt_dgram_packet *request,
+ const char *my_netbios_name,
+ const char *mailslot_name,
+ struct nbt_netlogon_response *reply)
+{
+ NTSTATUS status;
+ DATA_BLOB blob;
+ TALLOC_CTX *tmp_ctx = talloc_new(dgmsock);
+ struct nbt_name myname;
+ struct socket_address *dest;
+
+ status = push_nbt_netlogon_response(&blob, tmp_ctx, dgmsock->iconv_convenience,
+ reply);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ make_nbt_name_client(&myname, my_netbios_name);
+
+ dest = socket_address_from_strings(tmp_ctx, dgmsock->sock->backend_name,
+ request->src_addr, request->src_port);
+ if (!dest) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = dgram_mailslot_send(dgmsock, DGRAM_DIRECT_UNIQUE,
+ mailslot_name,
+ &request->data.msg.source_name,
+ dest,
+ &myname, &blob);
+ talloc_free(tmp_ctx);
+ return status;
+}
+
+
+/*
+ parse a netlogon response. The packet must be a valid mailslot packet
+*/
+NTSTATUS dgram_mailslot_netlogon_parse_request(struct dgram_mailslot_handler *dgmslot,
+ TALLOC_CTX *mem_ctx,
+ struct nbt_dgram_packet *dgram,
+ struct nbt_netlogon_packet *netlogon)
+{
+ DATA_BLOB data = dgram_mailslot_data(dgram);
+ enum ndr_err_code ndr_err;
+
+ ndr_err = ndr_pull_struct_blob(&data, mem_ctx, dgmslot->dgmsock->iconv_convenience, netlogon,
+ (ndr_pull_flags_fn_t)ndr_pull_nbt_netlogon_packet);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
+ DEBUG(0,("Failed to parse netlogon packet of length %d: %s\n",
+ (int)data.length, nt_errstr(status)));
+ if (DEBUGLVL(10)) {
+ file_save("netlogon.dat", data.data, data.length);
+ }
+ return status;
+ }
+ return NT_STATUS_OK;
+}
+
+/*
+ parse a netlogon response. The packet must be a valid mailslot packet
+*/
+NTSTATUS dgram_mailslot_netlogon_parse_response(struct dgram_mailslot_handler *dgmslot,
+ TALLOC_CTX *mem_ctx,
+ struct nbt_dgram_packet *dgram,
+ struct nbt_netlogon_response *netlogon)
+{
+ NTSTATUS status;
+ DATA_BLOB data = dgram_mailslot_data(dgram);
+
+ status = pull_nbt_netlogon_response(&data, mem_ctx, dgmslot->dgmsock->iconv_convenience, netlogon);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
diff --git a/source4/libcli/finddcs.c b/source4/libcli/finddcs.c
new file mode 100644
index 0000000000..2e4fad9332
--- /dev/null
+++ b/source4/libcli/finddcs.c
@@ -0,0 +1,276 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ a composite API for finding a DC and its name
+
+ Copyright (C) Volker Lendecke 2005
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "include/includes.h"
+#include "lib/messaging/irpc.h"
+#include "librpc/gen_ndr/ndr_irpc.h"
+#include "librpc/gen_ndr/samr.h"
+#include "libcli/composite/composite.h"
+#include "libcli/libcli.h"
+#include "libcli/resolve/resolve.h"
+#include "libcli/finddcs.h"
+
+struct finddcs_state {
+ struct composite_context *ctx;
+ struct messaging_context *msg_ctx;
+
+ const char *my_netbios_name;
+ const char *domain_name;
+ struct dom_sid *domain_sid;
+
+ struct nbtd_getdcname r;
+ struct nbt_name_status node_status;
+
+ struct smb_iconv_convenience *iconv_convenience;
+
+ int num_dcs;
+ struct nbt_dc_name *dcs;
+ uint16_t nbt_port;
+};
+
+static void finddcs_name_resolved(struct composite_context *ctx);
+static void finddcs_getdc_replied(struct irpc_request *ireq);
+static void fallback_node_status(struct finddcs_state *state);
+static void fallback_node_status_replied(struct nbt_name_request *name_req);
+
+/*
+ * Setup and send off the a normal name resolution for the target name.
+ *
+ * The domain_sid parameter is optional, and is used in the subsequent getdc request.
+ *
+ * This will try a GetDC request, but this may not work. It will try
+ * a node status as a fallback, then return no name (but still include
+ * the IP)
+ */
+
+struct composite_context *finddcs_send(TALLOC_CTX *mem_ctx,
+ const char *my_netbios_name,
+ uint16_t nbt_port,
+ const char *domain_name,
+ int name_type,
+ struct dom_sid *domain_sid,
+ struct smb_iconv_convenience *iconv_convenience,
+ struct resolve_context *resolve_ctx,
+ struct tevent_context *event_ctx,
+ struct messaging_context *msg_ctx)
+{
+ struct composite_context *c, *creq;
+ struct finddcs_state *state;
+ struct nbt_name name;
+
+ c = composite_create(mem_ctx, event_ctx);
+ if (c == NULL) return NULL;
+
+ state = talloc(c, struct finddcs_state);
+ if (composite_nomem(state, c)) return c;
+ c->private_data = state;
+
+ state->ctx = c;
+
+ state->nbt_port = nbt_port;
+ state->my_netbios_name = talloc_strdup(state, my_netbios_name);
+ state->domain_name = talloc_strdup(state, domain_name);
+ state->iconv_convenience = iconv_convenience;
+ if (composite_nomem(state->domain_name, c)) return c;
+
+ if (domain_sid) {
+ state->domain_sid = talloc_reference(state, domain_sid);
+ if (composite_nomem(state->domain_sid, c)) return c;
+ } else {
+ state->domain_sid = NULL;
+ }
+
+ state->msg_ctx = msg_ctx;
+
+ make_nbt_name(&name, state->domain_name, name_type);
+ creq = resolve_name_send(resolve_ctx, &name, event_ctx);
+ composite_continue(c, creq, finddcs_name_resolved, state);
+ return c;
+}
+
+/* Having got an name query answer, fire off a GetDC request, so we
+ * can find the target's all-important name. (Kerberos and some
+ * netlogon operations are quite picky about names)
+ *
+ * The name is a courtesy, if we don't find it, don't completely fail.
+ *
+ * However, if the nbt server is down, fall back to a node status
+ * request
+ */
+static void finddcs_name_resolved(struct composite_context *ctx)
+{
+ struct finddcs_state *state =
+ talloc_get_type(ctx->async.private_data, struct finddcs_state);
+ struct irpc_request *ireq;
+ struct server_id *nbt_servers;
+ const char *address;
+
+ state->ctx->status = resolve_name_recv(ctx, state, &address);
+ if (!composite_is_ok(state->ctx)) return;
+
+ /* TODO: This should try and find all the DCs, and give the
+ * caller them in the order they responded */
+
+ state->num_dcs = 1;
+ state->dcs = talloc_array(state, struct nbt_dc_name, state->num_dcs);
+ if (composite_nomem(state->dcs, state->ctx)) return;
+
+ state->dcs[0].address = talloc_steal(state->dcs, address);
+
+ /* Try and find the nbt server. Fallback to a node status
+ * request if we can't make this happen The nbt server just
+ * might not be running, or we may not have a messaging
+ * context (not root etc) */
+ if (!state->msg_ctx) {
+ fallback_node_status(state);
+ return;
+ }
+
+ nbt_servers = irpc_servers_byname(state->msg_ctx, state, "nbt_server");
+ if ((nbt_servers == NULL) || (nbt_servers[0].id == 0)) {
+ fallback_node_status(state);
+ return;
+ }
+
+ state->r.in.domainname = state->domain_name;
+ state->r.in.ip_address = state->dcs[0].address;
+ state->r.in.my_computername = state->my_netbios_name;
+ state->r.in.my_accountname = talloc_asprintf(state, "%s$", state->my_netbios_name);
+ if (composite_nomem(state->r.in.my_accountname, state->ctx)) return;
+ state->r.in.account_control = ACB_WSTRUST;
+ state->r.in.domain_sid = state->domain_sid;
+
+ ireq = irpc_call_send(state->msg_ctx, nbt_servers[0],
+ &ndr_table_irpc, NDR_NBTD_GETDCNAME,
+ &state->r, state);
+ if (!ireq) {
+ fallback_node_status(state);
+ return;
+ }
+
+ composite_continue_irpc(state->ctx, ireq, finddcs_getdc_replied, state);
+}
+
+/* Called when the GetDC request returns */
+static void finddcs_getdc_replied(struct irpc_request *ireq)
+{
+ struct finddcs_state *state =
+ talloc_get_type(ireq->async.private_data, struct finddcs_state);
+
+ state->ctx->status = irpc_call_recv(ireq);
+ if (!composite_is_ok(state->ctx)) return;
+
+ state->dcs[0].name = talloc_steal(state->dcs, state->r.out.dcname);
+ composite_done(state->ctx);
+}
+
+/* The GetDC request might not be available (such as occours when the
+ * NBT server is down). Fallback to a node status. It is the best
+ * hope we have... */
+static void fallback_node_status(struct finddcs_state *state)
+{
+ struct nbt_name_socket *nbtsock;
+ struct nbt_name_request *name_req;
+
+ state->node_status.in.name.name = "*";
+ state->node_status.in.name.type = NBT_NAME_CLIENT;
+ state->node_status.in.name.scope = NULL;
+ state->node_status.in.dest_addr = state->dcs[0].address;
+ state->node_status.in.dest_port = state->nbt_port;
+ state->node_status.in.timeout = 1;
+ state->node_status.in.retries = 2;
+
+ nbtsock = nbt_name_socket_init(state, state->ctx->event_ctx,
+ state->iconv_convenience);
+ if (composite_nomem(nbtsock, state->ctx)) return;
+
+ name_req = nbt_name_status_send(nbtsock, &state->node_status);
+ if (composite_nomem(name_req, state->ctx)) return;
+
+ composite_continue_nbt(state->ctx,
+ name_req,
+ fallback_node_status_replied,
+ state);
+}
+
+/* We have a node status reply (or perhaps a timeout) */
+static void fallback_node_status_replied(struct nbt_name_request *name_req)
+{
+ int i;
+ struct finddcs_state *state = talloc_get_type(name_req->async.private_data, struct finddcs_state);
+ state->ctx->status = nbt_name_status_recv(name_req, state, &state->node_status);
+ if (!composite_is_ok(state->ctx)) return;
+
+ for (i=0; i < state->node_status.out.status.num_names; i++) {
+ int j;
+ if (state->node_status.out.status.names[i].type == NBT_NAME_SERVER) {
+ char *name = talloc_strndup(state->dcs, state->node_status.out.status.names[0].name, 15);
+ /* Strip space padding */
+ if (name) {
+ j = MIN(strlen(name), 15);
+ for (; j > 0 && name[j - 1] == ' '; j--) {
+ name[j - 1] = '\0';
+ }
+ }
+ state->dcs[0].name = name;
+ composite_done(state->ctx);
+ return;
+ }
+ }
+ composite_error(state->ctx, NT_STATUS_NO_LOGON_SERVERS);
+}
+
+NTSTATUS finddcs_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
+ int *num_dcs, struct nbt_dc_name **dcs)
+{
+ NTSTATUS status = composite_wait(c);
+ if (NT_STATUS_IS_OK(status)) {
+ struct finddcs_state *state =
+ talloc_get_type(c->private_data, struct finddcs_state);
+ *num_dcs = state->num_dcs;
+ *dcs = talloc_steal(mem_ctx, state->dcs);
+ }
+ talloc_free(c);
+ return status;
+}
+
+NTSTATUS finddcs(TALLOC_CTX *mem_ctx,
+ const char *my_netbios_name,
+ uint16_t nbt_port,
+ const char *domain_name, int name_type,
+ struct dom_sid *domain_sid,
+ struct smb_iconv_convenience *iconv_convenience,
+ struct resolve_context *resolve_ctx,
+ struct tevent_context *event_ctx,
+ struct messaging_context *msg_ctx,
+ int *num_dcs, struct nbt_dc_name **dcs)
+{
+ struct composite_context *c = finddcs_send(mem_ctx,
+ my_netbios_name,
+ nbt_port,
+ domain_name, name_type,
+ domain_sid,
+ iconv_convenience,
+ resolve_ctx,
+ event_ctx, msg_ctx);
+ return finddcs_recv(c, mem_ctx, num_dcs, dcs);
+}
diff --git a/source4/libcli/ldap/config.mk b/source4/libcli/ldap/config.mk
new file mode 100644
index 0000000000..f0c0f5295d
--- /dev/null
+++ b/source4/libcli/ldap/config.mk
@@ -0,0 +1,12 @@
+[SUBSYSTEM::LIBCLI_LDAP]
+PUBLIC_DEPENDENCIES = LIBSAMBA-ERRORS LIBTEVENT LIBPACKET
+PRIVATE_DEPENDENCIES = LIBCLI_COMPOSITE samba_socket NDR_SAMR LIBTLS \
+ LIBCLI_LDAP_NDR LIBNDR LP_RESOLVE gensec LIBCLI_LDAP_MESSAGE
+
+LIBCLI_LDAP_OBJ_FILES = $(addprefix $(libclisrcdir)/ldap/, \
+ ldap_client.o ldap_bind.o \
+ ldap_ildap.o ldap_controls.o)
+PUBLIC_HEADERS += $(libclisrcdir)/ldap/ldap.h
+
+$(eval $(call proto_header_template,$(libclisrcdir)/ldap/ldap_proto.h,$(LIBCLI_LDAP_OBJ_FILES:.o=.c)))
+
diff --git a/source4/libcli/ldap/ldap.h b/source4/libcli/ldap/ldap.h
new file mode 100644
index 0000000000..79cfef2128
--- /dev/null
+++ b/source4/libcli/ldap/ldap.h
@@ -0,0 +1,31 @@
+/*
+ Unix SMB/CIFS Implementation.
+ LDAP protocol helper functions for SAMBA
+ Copyright (C) Volker Lendecke 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 <http://www.gnu.org/licenses/>.
+
+*/
+
+#ifndef _SMB_LDAP_H_
+#define _SMB_LDAP_H_
+
+#include "../libcli/ldap/ldap_message.h"
+#include "librpc/gen_ndr/misc.h"
+
+struct tevent_context;
+struct cli_credentials;
+struct dom_sid;
+
+#endif
diff --git a/source4/libcli/ldap/ldap_bind.c b/source4/libcli/ldap/ldap_bind.c
new file mode 100644
index 0000000000..5e6a5faafa
--- /dev/null
+++ b/source4/libcli/ldap/ldap_bind.c
@@ -0,0 +1,413 @@
+/*
+ Unix SMB/CIFS mplementation.
+
+ LDAP bind calls
+
+ Copyright (C) Andrew Tridgell 2005
+ Copyright (C) Volker Lendecke 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 <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "includes.h"
+#include "libcli/ldap/ldap.h"
+#include "libcli/ldap/ldap_proto.h"
+#include "libcli/ldap/ldap_client.h"
+#include "lib/tls/tls.h"
+#include "auth/gensec/gensec.h"
+#include "auth/credentials/credentials.h"
+#include "lib/stream/packet.h"
+#include "param/param.h"
+
+struct ldap_simple_creds {
+ const char *dn;
+ const char *pw;
+};
+
+_PUBLIC_ NTSTATUS ldap_rebind(struct ldap_connection *conn)
+{
+ NTSTATUS status;
+ struct ldap_simple_creds *creds;
+
+ switch (conn->bind.type) {
+ case LDAP_BIND_SASL:
+ status = ldap_bind_sasl(conn, (struct cli_credentials *)conn->bind.creds,
+ conn->lp_ctx);
+ break;
+
+ case LDAP_BIND_SIMPLE:
+ creds = (struct ldap_simple_creds *)conn->bind.creds;
+
+ if (creds == NULL) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ status = ldap_bind_simple(conn, creds->dn, creds->pw);
+ break;
+
+ default:
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return status;
+}
+
+
+static struct ldap_message *new_ldap_simple_bind_msg(struct ldap_connection *conn,
+ const char *dn, const char *pw)
+{
+ struct ldap_message *res;
+
+ res = new_ldap_message(conn);
+ if (!res) {
+ return NULL;
+ }
+
+ res->type = LDAP_TAG_BindRequest;
+ res->r.BindRequest.version = 3;
+ res->r.BindRequest.dn = talloc_strdup(res, dn);
+ res->r.BindRequest.mechanism = LDAP_AUTH_MECH_SIMPLE;
+ res->r.BindRequest.creds.password = talloc_strdup(res, pw);
+ res->controls = NULL;
+
+ return res;
+}
+
+
+/*
+ perform a simple username/password bind
+*/
+_PUBLIC_ NTSTATUS ldap_bind_simple(struct ldap_connection *conn,
+ const char *userdn, const char *password)
+{
+ struct ldap_request *req;
+ struct ldap_message *msg;
+ const char *dn, *pw;
+ NTSTATUS status;
+
+ if (conn == NULL) {
+ return NT_STATUS_INVALID_CONNECTION;
+ }
+
+ if (userdn) {
+ dn = userdn;
+ } else {
+ if (conn->auth_dn) {
+ dn = conn->auth_dn;
+ } else {
+ dn = "";
+ }
+ }
+
+ if (password) {
+ pw = password;
+ } else {
+ if (conn->simple_pw) {
+ pw = conn->simple_pw;
+ } else {
+ pw = "";
+ }
+ }
+
+ msg = new_ldap_simple_bind_msg(conn, dn, pw);
+ NT_STATUS_HAVE_NO_MEMORY(msg);
+
+ /* send the request */
+ req = ldap_request_send(conn, msg);
+ talloc_free(msg);
+ NT_STATUS_HAVE_NO_MEMORY(req);
+
+ /* wait for replies */
+ status = ldap_request_wait(req);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return status;
+ }
+
+ /* check its a valid reply */
+ msg = req->replies[0];
+ if (msg->type != LDAP_TAG_BindResponse) {
+ talloc_free(req);
+ return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+
+ status = ldap_check_response(conn, &msg->r.BindResponse.response);
+
+ talloc_free(req);
+
+ if (NT_STATUS_IS_OK(status)) {
+ struct ldap_simple_creds *creds = talloc(conn, struct ldap_simple_creds);
+ if (creds == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ creds->dn = talloc_strdup(creds, dn);
+ creds->pw = talloc_strdup(creds, pw);
+ if (creds->dn == NULL || creds->pw == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ conn->bind.type = LDAP_BIND_SIMPLE;
+ conn->bind.creds = creds;
+ }
+
+ return status;
+}
+
+
+static struct ldap_message *new_ldap_sasl_bind_msg(struct ldap_connection *conn,
+ const char *sasl_mechanism,
+ DATA_BLOB *secblob)
+{
+ struct ldap_message *res;
+
+ res = new_ldap_message(conn);
+ if (!res) {
+ return NULL;
+ }
+
+ res->type = LDAP_TAG_BindRequest;
+ res->r.BindRequest.version = 3;
+ res->r.BindRequest.dn = "";
+ res->r.BindRequest.mechanism = LDAP_AUTH_MECH_SASL;
+ res->r.BindRequest.creds.SASL.mechanism = talloc_strdup(res, sasl_mechanism);
+ if (secblob) {
+ res->r.BindRequest.creds.SASL.secblob = talloc(res, DATA_BLOB);
+ if (!res->r.BindRequest.creds.SASL.secblob) {
+ talloc_free(res);
+ return NULL;
+ }
+ *res->r.BindRequest.creds.SASL.secblob = *secblob;
+ } else {
+ res->r.BindRequest.creds.SASL.secblob = NULL;
+ }
+ res->controls = NULL;
+
+ return res;
+}
+
+
+/*
+ perform a sasl bind using the given credentials
+*/
+_PUBLIC_ NTSTATUS ldap_bind_sasl(struct ldap_connection *conn,
+ struct cli_credentials *creds,
+ struct loadparm_context *lp_ctx)
+{
+ NTSTATUS status;
+ TALLOC_CTX *tmp_ctx = NULL;
+
+ DATA_BLOB input = data_blob(NULL, 0);
+ DATA_BLOB output = data_blob(NULL, 0);
+
+ struct ldap_message **sasl_mechs_msgs;
+ struct ldap_SearchResEntry *search;
+ int count, i;
+
+ const char **sasl_names;
+ uint32_t old_gensec_features;
+ static const char *supported_sasl_mech_attrs[] = {
+ "supportedSASLMechanisms",
+ NULL
+ };
+
+ gensec_init(lp_ctx);
+
+ status = gensec_client_start(conn, &conn->gensec,
+ conn->event.event_ctx,
+ lp_gensec_settings(conn, lp_ctx));
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Failed to start GENSEC engine (%s)\n", nt_errstr(status)));
+ goto failed;
+ }
+
+ /* require Kerberos SIGN/SEAL only if we don't use SSL
+ * Windows seem not to like double encryption */
+ old_gensec_features = cli_credentials_get_gensec_features(creds);
+ if (tls_enabled(conn->sock)) {
+ cli_credentials_set_gensec_features(creds, old_gensec_features & ~(GENSEC_FEATURE_SIGN|GENSEC_FEATURE_SEAL));
+ }
+
+ /* this call also sets the gensec_want_features */
+ status = gensec_set_credentials(conn->gensec, creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to set GENSEC creds: %s\n",
+ nt_errstr(status)));
+ goto failed;
+ }
+
+ /* reset the original gensec_features (on the credentials
+ * context, so we don't tatoo it ) */
+ cli_credentials_set_gensec_features(creds, old_gensec_features);
+
+ if (conn->host) {
+ status = gensec_set_target_hostname(conn->gensec, conn->host);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to set GENSEC target hostname: %s\n",
+ nt_errstr(status)));
+ goto failed;
+ }
+ }
+
+ status = gensec_set_target_service(conn->gensec, "ldap");
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to set GENSEC target service: %s\n",
+ nt_errstr(status)));
+ goto failed;
+ }
+
+ status = ildap_search(conn, "", LDAP_SEARCH_SCOPE_BASE, "", supported_sasl_mech_attrs,
+ false, NULL, NULL, &sasl_mechs_msgs);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to inquire of target's available sasl mechs in rootdse search: %s\n",
+ nt_errstr(status)));
+ goto failed;
+ }
+
+ count = ildap_count_entries(conn, sasl_mechs_msgs);
+ if (count != 1) {
+ DEBUG(1, ("Failed to inquire of target's available sasl mechs in rootdse search: wrong number of replies: %d\n",
+ count));
+ goto failed;
+ }
+
+ tmp_ctx = talloc_new(conn);
+ if (tmp_ctx == NULL) goto failed;
+
+ search = &sasl_mechs_msgs[0]->r.SearchResultEntry;
+ if (search->num_attributes != 1) {
+ DEBUG(1, ("Failed to inquire of target's available sasl mechs in rootdse search: wrong number of attributes: %d != 1\n",
+ search->num_attributes));
+ goto failed;
+ }
+
+ sasl_names = talloc_array(tmp_ctx, const char *, search->attributes[0].num_values + 1);
+ if (!sasl_names) {
+ DEBUG(1, ("talloc_arry(char *, %d) failed\n",
+ count));
+ goto failed;
+ }
+
+ for (i=0; i<search->attributes[0].num_values; i++) {
+ sasl_names[i] = (const char *)search->attributes[0].values[i].data;
+ }
+ sasl_names[i] = NULL;
+
+ status = gensec_start_mech_by_sasl_list(conn->gensec, sasl_names);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("None of the %d proposed SASL mechs were acceptable: %s\n",
+ count, nt_errstr(status)));
+ goto failed;
+ }
+
+ while (1) {
+ NTSTATUS gensec_status;
+ struct ldap_message *response;
+ struct ldap_message *msg;
+ struct ldap_request *req;
+ int result = LDAP_OTHER;
+
+ status = gensec_update(conn->gensec, tmp_ctx,
+ input,
+ &output);
+ /* The status value here, from GENSEC is vital to the security
+ * of the system. Even if the other end accepts, if GENSEC
+ * claims 'MORE_PROCESSING_REQUIRED' then you must keep
+ * feeding it blobs, or else the remote host/attacker might
+ * avoid mutal authentication requirements.
+ *
+ * Likewise, you must not feed GENSEC too much (after the OK),
+ * it doesn't like that either
+ */
+
+ gensec_status = status;
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) &&
+ !NT_STATUS_IS_OK(status)) {
+ break;
+ }
+ if (NT_STATUS_IS_OK(status) && output.length == 0) {
+ break;
+ }
+
+ /* Perhaps we should make gensec_start_mech_by_sasl_list() return the name we got? */
+ msg = new_ldap_sasl_bind_msg(tmp_ctx, conn->gensec->ops->sasl_name, (output.data?&output:NULL));
+ if (msg == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto failed;
+ }
+
+ req = ldap_request_send(conn, msg);
+ if (req == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto failed;
+ }
+ talloc_steal(tmp_ctx, req);
+
+ status = ldap_result_n(req, 0, &response);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+
+ if (response->type != LDAP_TAG_BindResponse) {
+ status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ goto failed;
+ }
+
+ result = response->r.BindResponse.response.resultcode;
+
+ if (result != LDAP_SUCCESS && result != LDAP_SASL_BIND_IN_PROGRESS) {
+ status = ldap_check_response(conn,
+ &response->r.BindResponse.response);
+ break;
+ }
+
+ /* This is where we check if GENSEC wanted to be fed more data */
+ if (!NT_STATUS_EQUAL(gensec_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ break;
+ }
+ if (response->r.BindResponse.SASL.secblob) {
+ input = *response->r.BindResponse.SASL.secblob;
+ } else {
+ input = data_blob(NULL, 0);
+ }
+ }
+
+ talloc_free(tmp_ctx);
+
+ if (NT_STATUS_IS_OK(status)) {
+ struct socket_context *sasl_socket;
+ status = gensec_socket_init(conn->gensec,
+ conn,
+ conn->sock,
+ conn->event.event_ctx,
+ ldap_read_io_handler,
+ conn,
+ &sasl_socket);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+
+ conn->sock = sasl_socket;
+ packet_set_socket(conn->packet, conn->sock);
+
+ conn->bind.type = LDAP_BIND_SASL;
+ conn->bind.creds = creds;
+ }
+
+ return status;
+
+failed:
+ talloc_free(tmp_ctx);
+ talloc_free(conn->gensec);
+ conn->gensec = NULL;
+ return status;
+}
diff --git a/source4/libcli/ldap/ldap_client.c b/source4/libcli/ldap/ldap_client.c
new file mode 100644
index 0000000000..304a2e1253
--- /dev/null
+++ b/source4/libcli/ldap/ldap_client.c
@@ -0,0 +1,832 @@
+/*
+ Unix SMB/CIFS mplementation.
+ LDAP protocol helper functions for SAMBA
+
+ Copyright (C) Andrew Tridgell 2004
+ Copyright (C) Volker Lendecke 2004
+ 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 <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "includes.h"
+#include <tevent.h>
+#include "lib/socket/socket.h"
+#include "../lib/util/asn1.h"
+#include "../lib/util/dlinklist.h"
+#include "libcli/ldap/ldap.h"
+#include "libcli/ldap/ldap_proto.h"
+#include "libcli/ldap/ldap_client.h"
+#include "libcli/composite/composite.h"
+#include "lib/stream/packet.h"
+#include "lib/tls/tls.h"
+#include "auth/gensec/gensec.h"
+#include "system/time.h"
+#include "param/param.h"
+#include "libcli/resolve/resolve.h"
+
+/**
+ create a new ldap_connection stucture. The event context is optional
+*/
+_PUBLIC_ struct ldap_connection *ldap4_new_connection(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ struct tevent_context *ev)
+{
+ struct ldap_connection *conn;
+
+ if (ev == NULL) {
+ return NULL;
+ }
+
+ conn = talloc_zero(mem_ctx, struct ldap_connection);
+ if (conn == NULL) {
+ return NULL;
+ }
+
+ conn->next_messageid = 1;
+ conn->event.event_ctx = ev;
+
+ conn->lp_ctx = lp_ctx;
+
+ /* set a reasonable request timeout */
+ conn->timeout = 60;
+
+ /* explicitly avoid reconnections by default */
+ conn->reconnect.max_retries = 0;
+
+ return conn;
+}
+
+/*
+ the connection is dead
+*/
+static void ldap_connection_dead(struct ldap_connection *conn)
+{
+ struct ldap_request *req;
+
+ talloc_free(conn->sock); /* this will also free event.fde */
+ talloc_free(conn->packet);
+ conn->sock = NULL;
+ conn->event.fde = NULL;
+ conn->packet = NULL;
+
+ /* return an error for any pending request ... */
+ while (conn->pending) {
+ req = conn->pending;
+ DLIST_REMOVE(req->conn->pending, req);
+ req->state = LDAP_REQUEST_DONE;
+ req->status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+ }
+}
+
+static void ldap_reconnect(struct ldap_connection *conn);
+
+/*
+ handle packet errors
+*/
+static void ldap_error_handler(void *private_data, NTSTATUS status)
+{
+ struct ldap_connection *conn = talloc_get_type(private_data,
+ struct ldap_connection);
+ ldap_connection_dead(conn);
+
+ /* but try to reconnect so that the ldb client can go on */
+ ldap_reconnect(conn);
+}
+
+
+/*
+ match up with a pending message, adding to the replies list
+*/
+static void ldap_match_message(struct ldap_connection *conn, struct ldap_message *msg)
+{
+ struct ldap_request *req;
+ int i;
+
+ for (req=conn->pending; req; req=req->next) {
+ if (req->messageid == msg->messageid) break;
+ }
+ /* match a zero message id to the last request sent.
+ It seems that servers send 0 if unable to parse */
+ if (req == NULL && msg->messageid == 0) {
+ req = conn->pending;
+ }
+ if (req == NULL) {
+ DEBUG(0,("ldap: no matching message id for %u\n",
+ msg->messageid));
+ talloc_free(msg);
+ return;
+ }
+
+ /* Check for undecoded critical extensions */
+ for (i=0; msg->controls && msg->controls[i]; i++) {
+ if (!msg->controls_decoded[i] &&
+ msg->controls[i]->critical) {
+ req->status = NT_STATUS_LDAP(LDAP_UNAVAILABLE_CRITICAL_EXTENSION);
+ req->state = LDAP_REQUEST_DONE;
+ DLIST_REMOVE(conn->pending, req);
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+ return;
+ }
+ }
+
+ /* add to the list of replies received */
+ talloc_steal(req, msg);
+ req->replies = talloc_realloc(req, req->replies,
+ struct ldap_message *, req->num_replies+1);
+ if (req->replies == NULL) {
+ req->status = NT_STATUS_NO_MEMORY;
+ req->state = LDAP_REQUEST_DONE;
+ DLIST_REMOVE(conn->pending, req);
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+ return;
+ }
+
+ req->replies[req->num_replies] = talloc_steal(req->replies, msg);
+ req->num_replies++;
+
+ if (msg->type != LDAP_TAG_SearchResultEntry &&
+ msg->type != LDAP_TAG_SearchResultReference) {
+ /* currently only search results expect multiple
+ replies */
+ req->state = LDAP_REQUEST_DONE;
+ DLIST_REMOVE(conn->pending, req);
+ }
+
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+}
+
+
+/*
+ decode/process LDAP data
+*/
+static NTSTATUS ldap_recv_handler(void *private_data, DATA_BLOB blob)
+{
+ NTSTATUS status;
+ struct ldap_connection *conn = talloc_get_type(private_data,
+ struct ldap_connection);
+ struct ldap_message *msg = talloc(conn, struct ldap_message);
+ struct asn1_data *asn1 = asn1_init(conn);
+
+ if (asn1 == NULL || msg == NULL) {
+ return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
+ }
+
+ if (!asn1_load(asn1, blob)) {
+ talloc_free(msg);
+ talloc_free(asn1);
+ return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
+ }
+
+ status = ldap_decode(asn1, samba_ldap_control_handlers(), msg);
+ if (!NT_STATUS_IS_OK(status)) {
+ asn1_free(asn1);
+ return status;
+ }
+
+ ldap_match_message(conn, msg);
+
+ data_blob_free(&blob);
+ asn1_free(asn1);
+ return NT_STATUS_OK;
+}
+
+/* Handle read events, from the GENSEC socket callback, or real events */
+void ldap_read_io_handler(void *private_data, uint16_t flags)
+{
+ struct ldap_connection *conn = talloc_get_type(private_data,
+ struct ldap_connection);
+ packet_recv(conn->packet);
+}
+
+/*
+ handle ldap socket events
+*/
+static void ldap_io_handler(struct tevent_context *ev, struct tevent_fd *fde,
+ uint16_t flags, void *private_data)
+{
+ struct ldap_connection *conn = talloc_get_type(private_data,
+ struct ldap_connection);
+ if (flags & TEVENT_FD_WRITE) {
+ packet_queue_run(conn->packet);
+ if (!tls_enabled(conn->sock)) return;
+ }
+ if (flags & TEVENT_FD_READ) {
+ ldap_read_io_handler(private_data, flags);
+ }
+}
+
+/*
+ parse a ldap URL
+*/
+static NTSTATUS ldap_parse_basic_url(TALLOC_CTX *mem_ctx, const char *url,
+ char **host, uint16_t *port, bool *ldaps)
+{
+ int tmp_port = 0;
+ char protocol[11];
+ char tmp_host[1025];
+ int ret;
+
+ /* Paranoia check */
+ SMB_ASSERT(sizeof(protocol)>10 && sizeof(tmp_host)>254);
+
+ ret = sscanf(url, "%10[^:]://%254[^:/]:%d", protocol, tmp_host, &tmp_port);
+ if (ret < 2) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (strequal(protocol, "ldap")) {
+ *port = 389;
+ *ldaps = false;
+ } else if (strequal(protocol, "ldaps")) {
+ *port = 636;
+ *ldaps = true;
+ } else {
+ DEBUG(0, ("unrecognised ldap protocol (%s)!\n", protocol));
+ return NT_STATUS_PROTOCOL_UNREACHABLE;
+ }
+
+ if (tmp_port != 0)
+ *port = tmp_port;
+
+ *host = talloc_strdup(mem_ctx, tmp_host);
+ NT_STATUS_HAVE_NO_MEMORY(*host);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ connect to a ldap server
+*/
+
+struct ldap_connect_state {
+ struct composite_context *ctx;
+ struct ldap_connection *conn;
+};
+
+static void ldap_connect_recv_unix_conn(struct composite_context *ctx);
+static void ldap_connect_recv_tcp_conn(struct composite_context *ctx);
+
+_PUBLIC_ struct composite_context *ldap_connect_send(struct ldap_connection *conn,
+ const char *url)
+{
+ struct composite_context *result, *ctx;
+ struct ldap_connect_state *state;
+ char protocol[11];
+ int ret;
+
+ result = talloc_zero(conn, struct composite_context);
+ if (result == NULL) goto failed;
+ result->state = COMPOSITE_STATE_IN_PROGRESS;
+ result->async.fn = NULL;
+ result->event_ctx = conn->event.event_ctx;
+
+ state = talloc(result, struct ldap_connect_state);
+ if (state == NULL) goto failed;
+ state->ctx = result;
+ result->private_data = state;
+
+ state->conn = conn;
+
+ if (conn->reconnect.url == NULL) {
+ conn->reconnect.url = talloc_strdup(conn, url);
+ if (conn->reconnect.url == NULL) goto failed;
+ }
+
+ /* Paranoia check */
+ SMB_ASSERT(sizeof(protocol)>10);
+
+ ret = sscanf(url, "%10[^:]://", protocol);
+ if (ret < 1) {
+ return NULL;
+ }
+
+ if (strequal(protocol, "ldapi")) {
+ struct socket_address *unix_addr;
+ char path[1025];
+
+ NTSTATUS status = socket_create("unix", SOCKET_TYPE_STREAM, &conn->sock, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NULL;
+ }
+ talloc_steal(conn, conn->sock);
+ SMB_ASSERT(sizeof(protocol)>10);
+ SMB_ASSERT(sizeof(path)>1024);
+
+ /* LDAPI connections are to localhost, so give the local host name as the target for gensec */
+ conn->host = talloc_asprintf(conn, "%s.%s", lp_netbios_name(conn->lp_ctx), lp_realm(conn->lp_ctx));
+ if (composite_nomem(conn->host, state->ctx)) {
+ return result;
+ }
+
+ /* The %c specifier doesn't null terminate :-( */
+ ZERO_STRUCT(path);
+ ret = sscanf(url, "%10[^:]://%1025c", protocol, path);
+ if (ret < 2) {
+ composite_error(state->ctx, NT_STATUS_INVALID_PARAMETER);
+ return result;
+ }
+
+ rfc1738_unescape(path);
+
+ unix_addr = socket_address_from_strings(conn, conn->sock->backend_name,
+ path, 0);
+ if (!unix_addr) {
+ return NULL;
+ }
+
+ ctx = socket_connect_send(conn->sock, NULL, unix_addr,
+ 0, conn->event.event_ctx);
+ ctx->async.fn = ldap_connect_recv_unix_conn;
+ ctx->async.private_data = state;
+ return result;
+ } else {
+ NTSTATUS status = ldap_parse_basic_url(conn, url, &conn->host,
+ &conn->port, &conn->ldaps);
+ if (!NT_STATUS_IS_OK(state->ctx->status)) {
+ composite_error(state->ctx, status);
+ return result;
+ }
+
+ ctx = socket_connect_multi_send(state, conn->host, 1, &conn->port,
+ lp_resolve_context(conn->lp_ctx), conn->event.event_ctx);
+ if (ctx == NULL) goto failed;
+
+ ctx->async.fn = ldap_connect_recv_tcp_conn;
+ ctx->async.private_data = state;
+ return result;
+ }
+ failed:
+ talloc_free(result);
+ return NULL;
+}
+
+static void ldap_connect_got_sock(struct composite_context *ctx,
+ struct ldap_connection *conn)
+{
+ /* setup a handler for events on this socket */
+ conn->event.fde = tevent_add_fd(conn->event.event_ctx, conn->sock,
+ socket_get_fd(conn->sock),
+ TEVENT_FD_READ, ldap_io_handler, conn);
+ if (conn->event.fde == NULL) {
+ composite_error(ctx, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
+ tevent_fd_set_close_fn(conn->event.fde, socket_tevent_fd_close_fn);
+ socket_set_flags(conn->sock, SOCKET_FLAG_NOCLOSE);
+
+ talloc_steal(conn, conn->sock);
+ if (conn->ldaps) {
+ struct socket_context *tls_socket;
+ struct socket_context *tmp_socket;
+ char *cafile = lp_tls_cafile(conn->sock, conn->lp_ctx);
+
+ if (!cafile || !*cafile) {
+ talloc_free(conn->sock);
+ return;
+ }
+
+ tls_socket = tls_init_client(conn->sock, conn->event.fde, cafile);
+ talloc_free(cafile);
+
+ if (tls_socket == NULL) {
+ talloc_free(conn->sock);
+ return;
+ }
+
+ /* the original socket, must become a child of the tls socket */
+ tmp_socket = conn->sock;
+ conn->sock = talloc_steal(conn, tls_socket);
+ talloc_steal(conn->sock, tmp_socket);
+ }
+
+ conn->packet = packet_init(conn);
+ if (conn->packet == NULL) {
+ talloc_free(conn->sock);
+ return;
+ }
+
+ packet_set_private(conn->packet, conn);
+ packet_set_socket(conn->packet, conn->sock);
+ packet_set_callback(conn->packet, ldap_recv_handler);
+ packet_set_full_request(conn->packet, ldap_full_packet);
+ packet_set_error_handler(conn->packet, ldap_error_handler);
+ packet_set_event_context(conn->packet, conn->event.event_ctx);
+ packet_set_fde(conn->packet, conn->event.fde);
+/* packet_set_serialise(conn->packet); */
+
+ if (conn->ldaps) {
+ packet_set_unreliable_select(conn->packet);
+ }
+
+ composite_done(ctx);
+}
+
+static void ldap_connect_recv_tcp_conn(struct composite_context *ctx)
+{
+ struct ldap_connect_state *state =
+ talloc_get_type(ctx->async.private_data,
+ struct ldap_connect_state);
+ struct ldap_connection *conn = state->conn;
+ uint16_t port;
+ NTSTATUS status = socket_connect_multi_recv(ctx, state, &conn->sock,
+ &port);
+ if (!NT_STATUS_IS_OK(status)) {
+ composite_error(state->ctx, status);
+ return;
+ }
+
+ ldap_connect_got_sock(state->ctx, conn);
+}
+
+static void ldap_connect_recv_unix_conn(struct composite_context *ctx)
+{
+ struct ldap_connect_state *state =
+ talloc_get_type(ctx->async.private_data,
+ struct ldap_connect_state);
+ struct ldap_connection *conn = state->conn;
+
+ NTSTATUS status = socket_connect_recv(ctx);
+
+ if (!NT_STATUS_IS_OK(state->ctx->status)) {
+ composite_error(state->ctx, status);
+ return;
+ }
+
+ ldap_connect_got_sock(state->ctx, conn);
+}
+
+_PUBLIC_ NTSTATUS ldap_connect_recv(struct composite_context *ctx)
+{
+ NTSTATUS status = composite_wait(ctx);
+ talloc_free(ctx);
+ return status;
+}
+
+_PUBLIC_ NTSTATUS ldap_connect(struct ldap_connection *conn, const char *url)
+{
+ struct composite_context *ctx = ldap_connect_send(conn, url);
+ return ldap_connect_recv(ctx);
+}
+
+/* set reconnect parameters */
+
+_PUBLIC_ void ldap_set_reconn_params(struct ldap_connection *conn, int max_retries)
+{
+ if (conn) {
+ conn->reconnect.max_retries = max_retries;
+ conn->reconnect.retries = 0;
+ conn->reconnect.previous = time(NULL);
+ }
+}
+
+/* Actually this function is NOT ASYNC safe, FIXME? */
+static void ldap_reconnect(struct ldap_connection *conn)
+{
+ NTSTATUS status;
+ time_t now = time(NULL);
+
+ /* do we have set up reconnect ? */
+ if (conn->reconnect.max_retries == 0) return;
+
+ /* is the retry time expired ? */
+ if (now > conn->reconnect.previous + 30) {
+ conn->reconnect.retries = 0;
+ conn->reconnect.previous = now;
+ }
+
+ /* are we reconnectind too often and too fast? */
+ if (conn->reconnect.retries > conn->reconnect.max_retries) return;
+
+ /* keep track of the number of reconnections */
+ conn->reconnect.retries++;
+
+ /* reconnect */
+ status = ldap_connect(conn, conn->reconnect.url);
+ if ( ! NT_STATUS_IS_OK(status)) {
+ return;
+ }
+
+ /* rebind */
+ status = ldap_rebind(conn);
+ if ( ! NT_STATUS_IS_OK(status)) {
+ ldap_connection_dead(conn);
+ }
+}
+
+/* destroy an open ldap request */
+static int ldap_request_destructor(struct ldap_request *req)
+{
+ if (req->state == LDAP_REQUEST_PENDING) {
+ DLIST_REMOVE(req->conn->pending, req);
+ }
+ return 0;
+}
+
+/*
+ called on timeout of a ldap request
+*/
+static void ldap_request_timeout(struct tevent_context *ev, struct tevent_timer *te,
+ struct timeval t, void *private_data)
+{
+ struct ldap_request *req = talloc_get_type(private_data, struct ldap_request);
+ req->status = NT_STATUS_IO_TIMEOUT;
+ if (req->state == LDAP_REQUEST_PENDING) {
+ DLIST_REMOVE(req->conn->pending, req);
+ }
+ req->state = LDAP_REQUEST_DONE;
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+}
+
+
+/*
+ called on completion of a one-way ldap request
+*/
+static void ldap_request_complete(struct tevent_context *ev, struct tevent_timer *te,
+ struct timeval t, void *private_data)
+{
+ struct ldap_request *req = talloc_get_type(private_data, struct ldap_request);
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+}
+
+/*
+ send a ldap message - async interface
+*/
+_PUBLIC_ struct ldap_request *ldap_request_send(struct ldap_connection *conn,
+ struct ldap_message *msg)
+{
+ struct ldap_request *req;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+
+ req = talloc_zero(conn, struct ldap_request);
+ if (req == NULL) return NULL;
+
+ if (conn->sock == NULL) {
+ status = NT_STATUS_INVALID_CONNECTION;
+ goto failed;
+ }
+
+ req->state = LDAP_REQUEST_SEND;
+ req->conn = conn;
+ req->messageid = conn->next_messageid++;
+ if (conn->next_messageid == 0) {
+ conn->next_messageid = 1;
+ }
+ req->type = msg->type;
+ if (req->messageid == -1) {
+ goto failed;
+ }
+
+ talloc_set_destructor(req, ldap_request_destructor);
+
+ msg->messageid = req->messageid;
+
+ if (!ldap_encode(msg, samba_ldap_control_handlers(), &req->data, req)) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ goto failed;
+ }
+
+ status = packet_send(conn->packet, req->data);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+
+ /* some requests don't expect a reply, so don't add those to the
+ pending queue */
+ if (req->type == LDAP_TAG_AbandonRequest ||
+ req->type == LDAP_TAG_UnbindRequest) {
+ req->status = NT_STATUS_OK;
+ req->state = LDAP_REQUEST_DONE;
+ /* we can't call the async callback now, as it isn't setup, so
+ call it as next event */
+ tevent_add_timer(conn->event.event_ctx, req, timeval_zero(),
+ ldap_request_complete, req);
+ return req;
+ }
+
+ req->state = LDAP_REQUEST_PENDING;
+ DLIST_ADD(conn->pending, req);
+
+ /* put a timeout on the request */
+ req->time_event = tevent_add_timer(conn->event.event_ctx, req,
+ timeval_current_ofs(conn->timeout, 0),
+ ldap_request_timeout, req);
+
+ return req;
+
+failed:
+ req->status = status;
+ req->state = LDAP_REQUEST_ERROR;
+ tevent_add_timer(conn->event.event_ctx, req, timeval_zero(),
+ ldap_request_complete, req);
+
+ return req;
+}
+
+
+/*
+ wait for a request to complete
+ note that this does not destroy the request
+*/
+_PUBLIC_ NTSTATUS ldap_request_wait(struct ldap_request *req)
+{
+ while (req->state < LDAP_REQUEST_DONE) {
+ if (tevent_loop_once(req->conn->event.event_ctx) != 0) {
+ req->status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ break;
+ }
+ }
+ return req->status;
+}
+
+
+/*
+ a mapping of ldap response code to strings
+*/
+static const struct {
+ enum ldap_result_code code;
+ const char *str;
+} ldap_code_map[] = {
+#define _LDAP_MAP_CODE(c) { c, #c }
+ _LDAP_MAP_CODE(LDAP_SUCCESS),
+ _LDAP_MAP_CODE(LDAP_OPERATIONS_ERROR),
+ _LDAP_MAP_CODE(LDAP_PROTOCOL_ERROR),
+ _LDAP_MAP_CODE(LDAP_TIME_LIMIT_EXCEEDED),
+ _LDAP_MAP_CODE(LDAP_SIZE_LIMIT_EXCEEDED),
+ _LDAP_MAP_CODE(LDAP_COMPARE_FALSE),
+ _LDAP_MAP_CODE(LDAP_COMPARE_TRUE),
+ _LDAP_MAP_CODE(LDAP_AUTH_METHOD_NOT_SUPPORTED),
+ _LDAP_MAP_CODE(LDAP_STRONG_AUTH_REQUIRED),
+ _LDAP_MAP_CODE(LDAP_REFERRAL),
+ _LDAP_MAP_CODE(LDAP_ADMIN_LIMIT_EXCEEDED),
+ _LDAP_MAP_CODE(LDAP_UNAVAILABLE_CRITICAL_EXTENSION),
+ _LDAP_MAP_CODE(LDAP_CONFIDENTIALITY_REQUIRED),
+ _LDAP_MAP_CODE(LDAP_SASL_BIND_IN_PROGRESS),
+ _LDAP_MAP_CODE(LDAP_NO_SUCH_ATTRIBUTE),
+ _LDAP_MAP_CODE(LDAP_UNDEFINED_ATTRIBUTE_TYPE),
+ _LDAP_MAP_CODE(LDAP_INAPPROPRIATE_MATCHING),
+ _LDAP_MAP_CODE(LDAP_CONSTRAINT_VIOLATION),
+ _LDAP_MAP_CODE(LDAP_ATTRIBUTE_OR_VALUE_EXISTS),
+ _LDAP_MAP_CODE(LDAP_INVALID_ATTRIBUTE_SYNTAX),
+ _LDAP_MAP_CODE(LDAP_NO_SUCH_OBJECT),
+ _LDAP_MAP_CODE(LDAP_ALIAS_PROBLEM),
+ _LDAP_MAP_CODE(LDAP_INVALID_DN_SYNTAX),
+ _LDAP_MAP_CODE(LDAP_ALIAS_DEREFERENCING_PROBLEM),
+ _LDAP_MAP_CODE(LDAP_INAPPROPRIATE_AUTHENTICATION),
+ _LDAP_MAP_CODE(LDAP_INVALID_CREDENTIALS),
+ _LDAP_MAP_CODE(LDAP_INSUFFICIENT_ACCESS_RIGHTS),
+ _LDAP_MAP_CODE(LDAP_BUSY),
+ _LDAP_MAP_CODE(LDAP_UNAVAILABLE),
+ _LDAP_MAP_CODE(LDAP_UNWILLING_TO_PERFORM),
+ _LDAP_MAP_CODE(LDAP_LOOP_DETECT),
+ _LDAP_MAP_CODE(LDAP_NAMING_VIOLATION),
+ _LDAP_MAP_CODE(LDAP_OBJECT_CLASS_VIOLATION),
+ _LDAP_MAP_CODE(LDAP_NOT_ALLOWED_ON_NON_LEAF),
+ _LDAP_MAP_CODE(LDAP_NOT_ALLOWED_ON_RDN),
+ _LDAP_MAP_CODE(LDAP_ENTRY_ALREADY_EXISTS),
+ _LDAP_MAP_CODE(LDAP_OBJECT_CLASS_MODS_PROHIBITED),
+ _LDAP_MAP_CODE(LDAP_AFFECTS_MULTIPLE_DSAS),
+ _LDAP_MAP_CODE(LDAP_OTHER)
+};
+
+/*
+ used to setup the status code from a ldap response
+*/
+_PUBLIC_ NTSTATUS ldap_check_response(struct ldap_connection *conn, struct ldap_Result *r)
+{
+ int i;
+ const char *codename = "unknown";
+
+ if (r->resultcode == LDAP_SUCCESS) {
+ return NT_STATUS_OK;
+ }
+
+ if (conn->last_error) {
+ talloc_free(conn->last_error);
+ }
+
+ for (i=0;i<ARRAY_SIZE(ldap_code_map);i++) {
+ if (r->resultcode == ldap_code_map[i].code) {
+ codename = ldap_code_map[i].str;
+ break;
+ }
+ }
+
+ conn->last_error = talloc_asprintf(conn, "LDAP error %u %s - %s <%s> <%s>",
+ r->resultcode,
+ codename,
+ r->dn?r->dn:"(NULL)",
+ r->errormessage?r->errormessage:"",
+ r->referral?r->referral:"");
+
+ return NT_STATUS_LDAP(r->resultcode);
+}
+
+/*
+ return error string representing the last error
+*/
+_PUBLIC_ const char *ldap_errstr(struct ldap_connection *conn,
+ TALLOC_CTX *mem_ctx,
+ NTSTATUS status)
+{
+ if (NT_STATUS_IS_LDAP(status) && conn->last_error != NULL) {
+ return talloc_strdup(mem_ctx, conn->last_error);
+ }
+ return talloc_asprintf(mem_ctx, "LDAP client internal error: %s", nt_errstr(status));
+}
+
+
+/*
+ return the Nth result message, waiting if necessary
+*/
+_PUBLIC_ NTSTATUS ldap_result_n(struct ldap_request *req, int n, struct ldap_message **msg)
+{
+ *msg = NULL;
+
+ NT_STATUS_HAVE_NO_MEMORY(req);
+
+ while (req->state < LDAP_REQUEST_DONE && n >= req->num_replies) {
+ if (tevent_loop_once(req->conn->event.event_ctx) != 0) {
+ return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+ }
+
+ if (n < req->num_replies) {
+ *msg = req->replies[n];
+ return NT_STATUS_OK;
+ }
+
+ if (!NT_STATUS_IS_OK(req->status)) {
+ return req->status;
+ }
+
+ return NT_STATUS_NO_MORE_ENTRIES;
+}
+
+
+/*
+ return a single result message, checking if it is of the expected LDAP type
+*/
+_PUBLIC_ NTSTATUS ldap_result_one(struct ldap_request *req, struct ldap_message **msg, int type)
+{
+ NTSTATUS status;
+ status = ldap_result_n(req, 0, msg);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if ((*msg)->type != type) {
+ *msg = NULL;
+ return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+ return status;
+}
+
+/*
+ a simple ldap transaction, for single result requests that only need a status code
+ this relies on single valued requests having the response type == request type + 1
+*/
+_PUBLIC_ NTSTATUS ldap_transaction(struct ldap_connection *conn, struct ldap_message *msg)
+{
+ struct ldap_request *req = ldap_request_send(conn, msg);
+ struct ldap_message *res;
+ NTSTATUS status;
+ status = ldap_result_n(req, 0, &res);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return status;
+ }
+ if (res->type != msg->type + 1) {
+ talloc_free(req);
+ return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
+ }
+ status = ldap_check_response(conn, &res->r.GeneralResult);
+ talloc_free(req);
+ return status;
+}
diff --git a/source4/libcli/ldap/ldap_client.h b/source4/libcli/ldap/ldap_client.h
new file mode 100644
index 0000000000..084de2e6dc
--- /dev/null
+++ b/source4/libcli/ldap/ldap_client.h
@@ -0,0 +1,140 @@
+/*
+ Unix SMB/CIFS Implementation.
+
+ ldap client side header
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+
+#include "libcli/ldap/ldap.h"
+
+enum ldap_request_state { LDAP_REQUEST_SEND=1, LDAP_REQUEST_PENDING=2, LDAP_REQUEST_DONE=3, LDAP_REQUEST_ERROR=4 };
+
+/* this is the handle that the caller gets when an async ldap message
+ is sent */
+struct ldap_request {
+ struct ldap_request *next, *prev;
+ struct ldap_connection *conn;
+
+ enum ldap_request_tag type;
+ int messageid;
+ enum ldap_request_state state;
+
+ int num_replies;
+ struct ldap_message **replies;
+
+ NTSTATUS status;
+ DATA_BLOB data;
+ struct {
+ void (*fn)(struct ldap_request *);
+ void *private_data;
+ } async;
+
+ struct tevent_timer *time_event;
+};
+
+
+/* main context for a ldap client connection */
+struct ldap_connection {
+ struct socket_context *sock;
+ struct loadparm_context *lp_ctx;
+
+ char *host;
+ uint16_t port;
+ bool ldaps;
+
+ const char *auth_dn;
+ const char *simple_pw;
+
+ struct {
+ char *url;
+ int max_retries;
+ int retries;
+ time_t previous;
+ } reconnect;
+
+ struct {
+ enum { LDAP_BIND_SIMPLE, LDAP_BIND_SASL } type;
+ void *creds;
+ } bind;
+
+ /* next message id to assign */
+ unsigned next_messageid;
+
+ /* Outstanding LDAP requests that have not yet been replied to */
+ struct ldap_request *pending;
+
+ /* Let's support SASL */
+ struct gensec_security *gensec;
+
+ /* the default timeout for messages */
+ int timeout;
+
+ /* last error message */
+ char *last_error;
+
+ struct {
+ struct tevent_context *event_ctx;
+ struct tevent_fd *fde;
+ } event;
+
+ struct packet_context *packet;
+};
+
+struct ldap_connection *ldap4_new_connection(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ struct tevent_context *ev);
+
+NTSTATUS ldap_connect(struct ldap_connection *conn, const char *url);
+struct composite_context *ldap_connect_send(struct ldap_connection *conn,
+ const char *url);
+
+NTSTATUS ldap_rebind(struct ldap_connection *conn);
+NTSTATUS ldap_bind_simple(struct ldap_connection *conn,
+ const char *userdn, const char *password);
+NTSTATUS ldap_bind_sasl(struct ldap_connection *conn,
+ struct cli_credentials *creds,
+ struct loadparm_context *lp_ctx);
+struct ldap_request *ldap_request_send(struct ldap_connection *conn,
+ struct ldap_message *msg);
+NTSTATUS ldap_request_wait(struct ldap_request *req);
+struct composite_context;
+NTSTATUS ldap_connect_recv(struct composite_context *ctx);
+NTSTATUS ldap_result_n(struct ldap_request *req, int n, struct ldap_message **msg);
+NTSTATUS ldap_result_one(struct ldap_request *req, struct ldap_message **msg, int type);
+NTSTATUS ldap_transaction(struct ldap_connection *conn, struct ldap_message *msg);
+const char *ldap_errstr(struct ldap_connection *conn,
+ TALLOC_CTX *mem_ctx,
+ NTSTATUS status);
+NTSTATUS ldap_check_response(struct ldap_connection *conn, struct ldap_Result *r);
+void ldap_set_reconn_params(struct ldap_connection *conn, int max_retries);
+int ildap_count_entries(struct ldap_connection *conn, struct ldap_message **res);
+NTSTATUS ildap_search_bytree(struct ldap_connection *conn, const char *basedn,
+ int scope, struct ldb_parse_tree *tree,
+ const char * const *attrs, bool attributesonly,
+ struct ldb_control **control_req,
+ struct ldb_control ***control_res,
+ struct ldap_message ***results);
+NTSTATUS ildap_search(struct ldap_connection *conn, const char *basedn,
+ int scope, const char *expression,
+ const char * const *attrs, bool attributesonly,
+ struct ldb_control **control_req,
+ struct ldb_control ***control_res,
+ struct ldap_message ***results);
+
+
+
diff --git a/source4/libcli/ldap/ldap_controls.c b/source4/libcli/ldap/ldap_controls.c
new file mode 100644
index 0000000000..7949758a80
--- /dev/null
+++ b/source4/libcli/ldap/ldap_controls.c
@@ -0,0 +1,1237 @@
+/*
+ Unix SMB/CIFS mplementation.
+ LDAP protocol helper functions for SAMBA
+
+ Copyright (C) Simo Sorce 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 <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "includes.h"
+#include "../lib/util/asn1.h"
+#include "libcli/ldap/ldap.h"
+#include "lib/ldb/include/ldb.h"
+#include "libcli/ldap/ldap_proto.h"
+#include "dsdb/samdb/samdb.h"
+
+static bool decode_server_sort_response(void *mem_ctx, DATA_BLOB in, void *_out)
+{
+ void **out = (void **)_out;
+ DATA_BLOB attr;
+ struct asn1_data *data = asn1_init(mem_ctx);
+ struct ldb_sort_resp_control *lsrc;
+
+ if (!data) return false;
+
+ if (!asn1_load(data, in)) {
+ return false;
+ }
+
+ lsrc = talloc(mem_ctx, struct ldb_sort_resp_control);
+ if (!lsrc) {
+ return false;
+ }
+
+ if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_read_enumerated(data, &(lsrc->result))) {
+ return false;
+ }
+
+ lsrc->attr_desc = NULL;
+ if (asn1_peek_tag(data, ASN1_OCTET_STRING)) {
+ if (!asn1_read_OctetString(data, mem_ctx, &attr)) {
+ return false;
+ }
+ lsrc->attr_desc = talloc_strndup(lsrc, (const char *)attr.data, attr.length);
+ if (!lsrc->attr_desc) {
+ return false;
+ }
+ }
+
+ if (!asn1_end_tag(data)) {
+ return false;
+ }
+
+ *out = lsrc;
+
+ return true;
+}
+
+static bool decode_server_sort_request(void *mem_ctx, DATA_BLOB in, void *_out)
+{
+ void **out = (void **)_out;
+ DATA_BLOB attr;
+ DATA_BLOB rule;
+ struct asn1_data *data = asn1_init(mem_ctx);
+ struct ldb_server_sort_control **lssc;
+ int num;
+
+ if (!data) return false;
+
+ if (!asn1_load(data, in)) {
+ return false;
+ }
+
+ if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ lssc = NULL;
+
+ for (num = 0; asn1_peek_tag(data, ASN1_SEQUENCE(0)); num++) {
+ lssc = talloc_realloc(mem_ctx, lssc, struct ldb_server_sort_control *, num + 2);
+ if (!lssc) {
+ return false;
+ }
+ lssc[num] = talloc_zero(lssc, struct ldb_server_sort_control);
+ if (!lssc[num]) {
+ return false;
+ }
+
+ if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_read_OctetString(data, mem_ctx, &attr)) {
+ return false;
+ }
+
+ lssc[num]->attributeName = talloc_strndup(lssc[num], (const char *)attr.data, attr.length);
+ if (!lssc [num]->attributeName) {
+ return false;
+ }
+
+ if (asn1_peek_tag(data, ASN1_OCTET_STRING)) {
+ if (!asn1_read_OctetString(data, mem_ctx, &rule)) {
+ return false;
+ }
+ lssc[num]->orderingRule = talloc_strndup(lssc[num], (const char *)rule.data, rule.length);
+ if (!lssc[num]->orderingRule) {
+ return false;
+ }
+ }
+
+ if (asn1_peek_tag(data, ASN1_BOOLEAN)) {
+ bool reverse;
+ if (!asn1_read_BOOLEAN(data, &reverse)) {
+ return false;
+ }
+ lssc[num]->reverse = reverse;
+ }
+
+ if (!asn1_end_tag(data)) {
+ return false;
+ }
+ }
+
+ if (lssc != NULL) {
+ lssc[num] = NULL;
+ }
+
+ if (!asn1_end_tag(data)) {
+ return false;
+ }
+
+ *out = lssc;
+
+ return true;
+}
+
+static bool decode_extended_dn_request(void *mem_ctx, DATA_BLOB in, void *_out)
+{
+ void **out = (void **)_out;
+ struct asn1_data *data;
+ struct ldb_extended_dn_control *ledc;
+
+ /* The content of this control is optional */
+ if (in.length == 0) {
+ *out = NULL;
+ return true;
+ }
+
+ data = asn1_init(mem_ctx);
+ if (!data) return false;
+
+ if (!asn1_load(data, in)) {
+ return false;
+ }
+
+ ledc = talloc(mem_ctx, struct ldb_extended_dn_control);
+ if (!ledc) {
+ return false;
+ }
+
+ if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_read_Integer(data, &(ledc->type))) {
+ return false;
+ }
+
+ if (!asn1_end_tag(data)) {
+ return false;
+ }
+
+ *out = ledc;
+
+ return true;
+}
+
+static bool decode_sd_flags_request(void *mem_ctx, DATA_BLOB in, void *_out)
+{
+ void **out = (void **)_out;
+ struct asn1_data *data = asn1_init(mem_ctx);
+ struct ldb_sd_flags_control *lsdfc;
+
+ if (!data) return false;
+
+ if (!asn1_load(data, in)) {
+ return false;
+ }
+
+ lsdfc = talloc(mem_ctx, struct ldb_sd_flags_control);
+ if (!lsdfc) {
+ return false;
+ }
+
+ if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_read_Integer(data, &(lsdfc->secinfo_flags))) {
+ return false;
+ }
+
+ if (!asn1_end_tag(data)) {
+ return false;
+ }
+
+ *out = lsdfc;
+
+ return true;
+}
+
+static bool decode_search_options_request(void *mem_ctx, DATA_BLOB in, void *_out)
+{
+ void **out = (void **)_out;
+ struct asn1_data *data = asn1_init(mem_ctx);
+ struct ldb_search_options_control *lsoc;
+
+ if (!data) return false;
+
+ if (!asn1_load(data, in)) {
+ return false;
+ }
+
+ lsoc = talloc(mem_ctx, struct ldb_search_options_control);
+ if (!lsoc) {
+ return false;
+ }
+
+ if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_read_Integer(data, &(lsoc->search_options))) {
+ return false;
+ }
+
+ if (!asn1_end_tag(data)) {
+ return false;
+ }
+
+ *out = lsoc;
+
+ return true;
+}
+
+static bool decode_paged_results_request(void *mem_ctx, DATA_BLOB in, void *_out)
+{
+ void **out = (void **)_out;
+ DATA_BLOB cookie;
+ struct asn1_data *data = asn1_init(mem_ctx);
+ struct ldb_paged_control *lprc;
+
+ if (!data) return false;
+
+ if (!asn1_load(data, in)) {
+ return false;
+ }
+
+ lprc = talloc(mem_ctx, struct ldb_paged_control);
+ if (!lprc) {
+ return false;
+ }
+
+ if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_read_Integer(data, &(lprc->size))) {
+ return false;
+ }
+
+ if (!asn1_read_OctetString(data, mem_ctx, &cookie)) {
+ return false;
+ }
+ lprc->cookie_len = cookie.length;
+ if (lprc->cookie_len) {
+ lprc->cookie = talloc_memdup(lprc, cookie.data, cookie.length);
+
+ if (!(lprc->cookie)) {
+ return false;
+ }
+ } else {
+ lprc->cookie = NULL;
+ }
+
+ if (!asn1_end_tag(data)) {
+ return false;
+ }
+
+ *out = lprc;
+
+ return true;
+}
+
+static bool decode_dirsync_request(void *mem_ctx, DATA_BLOB in, void *_out)
+{
+ void **out = (void **)_out;
+ DATA_BLOB cookie;
+ struct asn1_data *data = asn1_init(mem_ctx);
+ struct ldb_dirsync_control *ldc;
+
+ if (!data) return false;
+
+ if (!asn1_load(data, in)) {
+ return false;
+ }
+
+ ldc = talloc(mem_ctx, struct ldb_dirsync_control);
+ if (!ldc) {
+ return false;
+ }
+
+ if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_read_Integer(data, &(ldc->flags))) {
+ return false;
+ }
+
+ if (!asn1_read_Integer(data, &(ldc->max_attributes))) {
+ return false;
+ }
+
+ if (!asn1_read_OctetString(data, mem_ctx, &cookie)) {
+ return false;
+ }
+ ldc->cookie_len = cookie.length;
+ if (ldc->cookie_len) {
+ ldc->cookie = talloc_memdup(ldc, cookie.data, cookie.length);
+
+ if (!(ldc->cookie)) {
+ return false;
+ }
+ } else {
+ ldc->cookie = NULL;
+ }
+
+ if (!asn1_end_tag(data)) {
+ return false;
+ }
+
+ *out = ldc;
+
+ return true;
+}
+
+/* seem that this controls has 2 forms one in case it is used with
+ * a Search Request and another when used ina Search Response
+ */
+static bool decode_asq_control(void *mem_ctx, DATA_BLOB in, void *_out)
+{
+ void **out = (void **)_out;
+ DATA_BLOB source_attribute;
+ struct asn1_data *data = asn1_init(mem_ctx);
+ struct ldb_asq_control *lac;
+
+ if (!data) return false;
+
+ if (!asn1_load(data, in)) {
+ return false;
+ }
+
+ lac = talloc(mem_ctx, struct ldb_asq_control);
+ if (!lac) {
+ return false;
+ }
+
+ if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (asn1_peek_tag(data, ASN1_OCTET_STRING)) {
+
+ if (!asn1_read_OctetString(data, mem_ctx, &source_attribute)) {
+ return false;
+ }
+ lac->src_attr_len = source_attribute.length;
+ if (lac->src_attr_len) {
+ lac->source_attribute = talloc_strndup(lac, (const char *)source_attribute.data, source_attribute.length);
+
+ if (!(lac->source_attribute)) {
+ return false;
+ }
+ } else {
+ lac->source_attribute = NULL;
+ }
+
+ lac->request = 1;
+
+ } else if (asn1_peek_tag(data, ASN1_ENUMERATED)) {
+
+ if (!asn1_read_enumerated(data, &(lac->result))) {
+ return false;
+ }
+
+ lac->request = 0;
+
+ } else {
+ return false;
+ }
+
+ if (!asn1_end_tag(data)) {
+ return false;
+ }
+
+ *out = lac;
+
+ return true;
+}
+
+static bool decode_domain_scope_request(void *mem_ctx, DATA_BLOB in, void *_out)
+{
+ if (in.length != 0) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool decode_notification_request(void *mem_ctx, DATA_BLOB in, void *_out)
+{
+ if (in.length != 0) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool decode_show_deleted_request(void *mem_ctx, DATA_BLOB in, void *_out)
+{
+ if (in.length != 0) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool decode_permissive_modify_request(void *mem_ctx, DATA_BLOB in, void *_out)
+{
+ if (in.length != 0) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool decode_manageDSAIT_request(void *mem_ctx, DATA_BLOB in, void *_out)
+{
+ if (in.length != 0) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool decode_vlv_request(void *mem_ctx, DATA_BLOB in, void *_out)
+{
+ void **out = (void **)_out;
+ DATA_BLOB assertion_value, context_id;
+ struct asn1_data *data = asn1_init(mem_ctx);
+ struct ldb_vlv_req_control *lvrc;
+
+ if (!data) return false;
+
+ if (!asn1_load(data, in)) {
+ return false;
+ }
+
+ lvrc = talloc(mem_ctx, struct ldb_vlv_req_control);
+ if (!lvrc) {
+ return false;
+ }
+
+ if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_read_Integer(data, &(lvrc->beforeCount))) {
+ return false;
+ }
+
+ if (!asn1_read_Integer(data, &(lvrc->afterCount))) {
+ return false;
+ }
+
+ if (asn1_peek_tag(data, ASN1_CONTEXT(0))) {
+
+ lvrc->type = 0;
+
+ if (!asn1_start_tag(data, ASN1_CONTEXT(0))) {
+ return false;
+ }
+
+ if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_read_Integer(data, &(lvrc->match.byOffset.offset))) {
+ return false;
+ }
+
+ if (!asn1_read_Integer(data, &(lvrc->match.byOffset.contentCount))) {
+ return false;
+ }
+
+ if (!asn1_end_tag(data)) { /*SEQUENCE*/
+ return false;
+ }
+
+ if (!asn1_end_tag(data)) { /*CONTEXT*/
+ return false;
+ }
+
+ } else {
+
+ lvrc->type = 1;
+
+ if (!asn1_start_tag(data, ASN1_CONTEXT(1))) {
+ return false;
+ }
+
+ if (!asn1_read_OctetString(data, mem_ctx, &assertion_value)) {
+ return false;
+ }
+ lvrc->match.gtOrEq.value_len = assertion_value.length;
+ if (lvrc->match.gtOrEq.value_len) {
+ lvrc->match.gtOrEq.value = talloc_memdup(lvrc, assertion_value.data, assertion_value.length);
+
+ if (!(lvrc->match.gtOrEq.value)) {
+ return false;
+ }
+ } else {
+ lvrc->match.gtOrEq.value = NULL;
+ }
+
+ if (!asn1_end_tag(data)) { /*CONTEXT*/
+ return false;
+ }
+ }
+
+ if (asn1_peek_tag(data, ASN1_OCTET_STRING)) {
+ if (!asn1_read_OctetString(data, mem_ctx, &context_id)) {
+ return false;
+ }
+ lvrc->ctxid_len = context_id.length;
+ if (lvrc->ctxid_len) {
+ lvrc->contextId = talloc_memdup(lvrc, context_id.data, context_id.length);
+
+ if (!(lvrc->contextId)) {
+ return false;
+ }
+ } else {
+ lvrc->contextId = NULL;
+ }
+ } else {
+ lvrc->contextId = NULL;
+ lvrc->ctxid_len = 0;
+ }
+
+ if (!asn1_end_tag(data)) {
+ return false;
+ }
+
+ *out = lvrc;
+
+ return true;
+}
+
+static bool decode_vlv_response(void *mem_ctx, DATA_BLOB in, void *_out)
+{
+ void **out = (void **)_out;
+ DATA_BLOB context_id;
+ struct asn1_data *data = asn1_init(mem_ctx);
+ struct ldb_vlv_resp_control *lvrc;
+
+ if (!data) return false;
+
+ if (!asn1_load(data, in)) {
+ return false;
+ }
+
+ lvrc = talloc(mem_ctx, struct ldb_vlv_resp_control);
+ if (!lvrc) {
+ return false;
+ }
+
+ if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_read_Integer(data, &(lvrc->targetPosition))) {
+ return false;
+ }
+
+ if (!asn1_read_Integer(data, &(lvrc->contentCount))) {
+ return false;
+ }
+
+ if (!asn1_read_enumerated(data, &(lvrc->vlv_result))) {
+ return false;
+ }
+
+ if (asn1_peek_tag(data, ASN1_OCTET_STRING)) {
+ if (!asn1_read_OctetString(data, mem_ctx, &context_id)) {
+ return false;
+ }
+ lvrc->contextId = talloc_strndup(lvrc, (const char *)context_id.data, context_id.length);
+ if (!lvrc->contextId) {
+ return false;
+ }
+ lvrc->ctxid_len = context_id.length;
+ } else {
+ lvrc->contextId = NULL;
+ lvrc->ctxid_len = 0;
+ }
+
+ if (!asn1_end_tag(data)) {
+ return false;
+ }
+
+ *out = lvrc;
+
+ return true;
+}
+
+static bool encode_server_sort_response(void *mem_ctx, void *in, DATA_BLOB *out)
+{
+ struct ldb_sort_resp_control *lsrc = talloc_get_type(in, struct ldb_sort_resp_control);
+ struct asn1_data *data = asn1_init(mem_ctx);
+
+ if (!data) return false;
+
+ if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_write_enumerated(data, lsrc->result)) {
+ return false;
+ }
+
+ if (lsrc->attr_desc) {
+ if (!asn1_write_OctetString(data, lsrc->attr_desc, strlen(lsrc->attr_desc))) {
+ return false;
+ }
+ }
+
+ if (!asn1_pop_tag(data)) {
+ return false;
+ }
+
+ *out = data_blob_talloc(mem_ctx, data->data, data->length);
+ if (out->data == NULL) {
+ return false;
+ }
+ talloc_free(data);
+
+ return true;
+}
+
+static bool encode_server_sort_request(void *mem_ctx, void *in, DATA_BLOB *out)
+{
+ struct ldb_server_sort_control **lssc = talloc_get_type(in, struct ldb_server_sort_control *);
+ struct asn1_data *data = asn1_init(mem_ctx);
+ int num;
+
+ if (!data) return false;
+
+ if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ for (num = 0; lssc[num]; num++) {
+ if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_write_OctetString(data, lssc[num]->attributeName, strlen(lssc[num]->attributeName))) {
+ return false;
+ }
+
+ if (lssc[num]->orderingRule) {
+ if (!asn1_write_OctetString(data, lssc[num]->orderingRule, strlen(lssc[num]->orderingRule))) {
+ return false;
+ }
+ }
+
+ if (lssc[num]->reverse) {
+ if (!asn1_write_BOOLEAN(data, lssc[num]->reverse)) {
+ return false;
+ }
+ }
+
+ if (!asn1_pop_tag(data)) {
+ return false;
+ }
+ }
+
+ if (!asn1_pop_tag(data)) {
+ return false;
+ }
+
+ *out = data_blob_talloc(mem_ctx, data->data, data->length);
+ if (out->data == NULL) {
+ return false;
+ }
+ talloc_free(data);
+
+ return true;
+}
+
+static bool encode_extended_dn_request(void *mem_ctx, void *in, DATA_BLOB *out)
+{
+ struct ldb_extended_dn_control *ledc = talloc_get_type(in, struct ldb_extended_dn_control);
+ struct asn1_data *data;
+
+ if (!in) {
+ *out = data_blob(NULL, 0);
+ return true;
+ }
+
+ data = asn1_init(mem_ctx);
+
+ if (!data) return false;
+
+ if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_write_Integer(data, ledc->type)) {
+ return false;
+ }
+
+ if (!asn1_pop_tag(data)) {
+ return false;
+ }
+
+ *out = data_blob_talloc(mem_ctx, data->data, data->length);
+ if (out->data == NULL) {
+ return false;
+ }
+ talloc_free(data);
+
+ return true;
+}
+
+static bool encode_sd_flags_request(void *mem_ctx, void *in, DATA_BLOB *out)
+{
+ struct ldb_sd_flags_control *lsdfc = talloc_get_type(in, struct ldb_sd_flags_control);
+ struct asn1_data *data = asn1_init(mem_ctx);
+
+ if (!data) return false;
+
+ if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_write_Integer(data, lsdfc->secinfo_flags)) {
+ return false;
+ }
+
+ if (!asn1_pop_tag(data)) {
+ return false;
+ }
+
+ *out = data_blob_talloc(mem_ctx, data->data, data->length);
+ if (out->data == NULL) {
+ return false;
+ }
+ talloc_free(data);
+
+ return true;
+}
+
+static bool encode_search_options_request(void *mem_ctx, void *in, DATA_BLOB *out)
+{
+ struct ldb_search_options_control *lsoc = talloc_get_type(in, struct ldb_search_options_control);
+ struct asn1_data *data = asn1_init(mem_ctx);
+
+ if (!data) return false;
+
+ if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_write_Integer(data, lsoc->search_options)) {
+ return false;
+ }
+
+ if (!asn1_pop_tag(data)) {
+ return false;
+ }
+
+ *out = data_blob_talloc(mem_ctx, data->data, data->length);
+ if (out->data == NULL) {
+ return false;
+ }
+ talloc_free(data);
+
+ return true;
+}
+
+static bool encode_paged_results_request(void *mem_ctx, void *in, DATA_BLOB *out)
+{
+ struct ldb_paged_control *lprc = talloc_get_type(in, struct ldb_paged_control);
+ struct asn1_data *data = asn1_init(mem_ctx);
+
+ if (!data) return false;
+
+ if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_write_Integer(data, lprc->size)) {
+ return false;
+ }
+
+ if (!asn1_write_OctetString(data, lprc->cookie, lprc->cookie_len)) {
+ return false;
+ }
+
+ if (!asn1_pop_tag(data)) {
+ return false;
+ }
+
+ *out = data_blob_talloc(mem_ctx, data->data, data->length);
+ if (out->data == NULL) {
+ return false;
+ }
+ talloc_free(data);
+
+ return true;
+}
+
+/* seem that this controls has 2 forms one in case it is used with
+ * a Search Request and another when used ina Search Response
+ */
+static bool encode_asq_control(void *mem_ctx, void *in, DATA_BLOB *out)
+{
+ struct ldb_asq_control *lac = talloc_get_type(in, struct ldb_asq_control);
+ struct asn1_data *data = asn1_init(mem_ctx);
+
+ if (!data) return false;
+
+ if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (lac->request) {
+
+ if (!asn1_write_OctetString(data, lac->source_attribute, lac->src_attr_len)) {
+ return false;
+ }
+ } else {
+ if (!asn1_write_enumerated(data, lac->result)) {
+ return false;
+ }
+ }
+
+ if (!asn1_pop_tag(data)) {
+ return false;
+ }
+
+ *out = data_blob_talloc(mem_ctx, data->data, data->length);
+ if (out->data == NULL) {
+ return false;
+ }
+ talloc_free(data);
+
+ return true;
+}
+
+static bool encode_dirsync_request(void *mem_ctx, void *in, DATA_BLOB *out)
+{
+ struct ldb_dirsync_control *ldc = talloc_get_type(in, struct ldb_dirsync_control);
+ struct asn1_data *data = asn1_init(mem_ctx);
+
+ if (!data) return false;
+
+ if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_write_Integer(data, ldc->flags)) {
+ return false;
+ }
+
+ if (!asn1_write_Integer(data, ldc->max_attributes)) {
+ return false;
+ }
+
+ if (!asn1_write_OctetString(data, ldc->cookie, ldc->cookie_len)) {
+ return false;
+ }
+
+ if (!asn1_pop_tag(data)) {
+ return false;
+ }
+
+ *out = data_blob_talloc(mem_ctx, data->data, data->length);
+ if (out->data == NULL) {
+ return false;
+ }
+ talloc_free(data);
+
+ return true;
+}
+
+static bool encode_domain_scope_request(void *mem_ctx, void *in, DATA_BLOB *out)
+{
+ if (in) {
+ return false;
+ }
+
+ *out = data_blob(NULL, 0);
+ return true;
+}
+
+static bool encode_notification_request(void *mem_ctx, void *in, DATA_BLOB *out)
+{
+ if (in) {
+ return false;
+ }
+
+ *out = data_blob(NULL, 0);
+ return true;
+}
+
+static bool encode_show_deleted_request(void *mem_ctx, void *in, DATA_BLOB *out)
+{
+ if (in) {
+ return false;
+ }
+
+ *out = data_blob(NULL, 0);
+ return true;
+}
+
+static bool encode_permissive_modify_request(void *mem_ctx, void *in, DATA_BLOB *out)
+{
+ if (in) {
+ return false;
+ }
+
+ *out = data_blob(NULL, 0);
+ return true;
+}
+
+static bool encode_manageDSAIT_request(void *mem_ctx, void *in, DATA_BLOB *out)
+{
+ if (in) {
+ return false;
+ }
+
+ *out = data_blob(NULL, 0);
+ return true;
+}
+
+static bool encode_vlv_request(void *mem_ctx, void *in, DATA_BLOB *out)
+{
+ struct ldb_vlv_req_control *lvrc = talloc_get_type(in, struct ldb_vlv_req_control);
+ struct asn1_data *data = asn1_init(mem_ctx);
+
+ if (!data) return false;
+
+ if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_write_Integer(data, lvrc->beforeCount)) {
+ return false;
+ }
+
+ if (!asn1_write_Integer(data, lvrc->afterCount)) {
+ return false;
+ }
+
+ if (lvrc->type == 0) {
+ if (!asn1_push_tag(data, ASN1_CONTEXT(0))) {
+ return false;
+ }
+
+ if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_write_Integer(data, lvrc->match.byOffset.offset)) {
+ return false;
+ }
+
+ if (!asn1_write_Integer(data, lvrc->match.byOffset.contentCount)) {
+ return false;
+ }
+
+ if (!asn1_pop_tag(data)) { /*SEQUENCE*/
+ return false;
+ }
+
+ if (!asn1_pop_tag(data)) { /*CONTEXT*/
+ return false;
+ }
+ } else {
+ if (!asn1_push_tag(data, ASN1_CONTEXT(1))) {
+ return false;
+ }
+
+ if (!asn1_write_OctetString(data, lvrc->match.gtOrEq.value, lvrc->match.gtOrEq.value_len)) {
+ return false;
+ }
+
+ if (!asn1_pop_tag(data)) { /*CONTEXT*/
+ return false;
+ }
+ }
+
+ if (lvrc->ctxid_len) {
+ if (!asn1_write_OctetString(data, lvrc->contextId, lvrc->ctxid_len)) {
+ return false;
+ }
+ }
+
+ if (!asn1_pop_tag(data)) {
+ return false;
+ }
+
+ *out = data_blob_talloc(mem_ctx, data->data, data->length);
+ if (out->data == NULL) {
+ return false;
+ }
+ talloc_free(data);
+
+ return true;
+}
+
+static bool encode_vlv_response(void *mem_ctx, void *in, DATA_BLOB *out)
+{
+ struct ldb_vlv_resp_control *lvrc = talloc_get_type(in, struct ldb_vlv_resp_control);
+ struct asn1_data *data = asn1_init(mem_ctx);
+
+ if (!data) return false;
+
+ if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_write_Integer(data, lvrc->targetPosition)) {
+ return false;
+ }
+
+ if (!asn1_write_Integer(data, lvrc->contentCount)) {
+ return false;
+ }
+
+ if (!asn1_write_enumerated(data, lvrc->vlv_result)) {
+ return false;
+ }
+
+ if (lvrc->ctxid_len) {
+ if (!asn1_write_OctetString(data, lvrc->contextId, lvrc->ctxid_len)) {
+ return false;
+ }
+ }
+
+ if (!asn1_pop_tag(data)) {
+ return false;
+ }
+
+ *out = data_blob_talloc(mem_ctx, data->data, data->length);
+ if (out->data == NULL) {
+ return false;
+ }
+ talloc_free(data);
+
+ return true;
+}
+
+static bool encode_openldap_dereference(void *mem_ctx, void *in, DATA_BLOB *out)
+{
+ struct dsdb_openldap_dereference_control *control = talloc_get_type(in, struct dsdb_openldap_dereference_control);
+ int i,j;
+ struct asn1_data *data = asn1_init(mem_ctx);
+
+ if (!data) return false;
+
+ if (!control) return false;
+
+ if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ for (i=0; control->dereference && control->dereference[i]; i++) {
+ if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+ if (!asn1_write_OctetString(data, control->dereference[i]->source_attribute, strlen(control->dereference[i]->source_attribute))) {
+ return false;
+ }
+ if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+ for (j=0; control->dereference && control->dereference[i]->dereference_attribute[j]; j++) {
+ if (!asn1_write_OctetString(data, control->dereference[i]->dereference_attribute[j],
+ strlen(control->dereference[i]->dereference_attribute[j]))) {
+ return false;
+ }
+ }
+
+ asn1_pop_tag(data);
+ asn1_pop_tag(data);
+ }
+ asn1_pop_tag(data);
+
+ *out = data_blob_talloc(mem_ctx, data->data, data->length);
+ if (out->data == NULL) {
+ return false;
+ }
+ talloc_free(data);
+ return true;
+}
+
+static bool decode_openldap_dereference(void *mem_ctx, DATA_BLOB in, void *_out)
+{
+ void **out = (void **)_out;
+ struct asn1_data *data = asn1_init(mem_ctx);
+ struct dsdb_openldap_dereference_result_control *control;
+ struct dsdb_openldap_dereference_result **r = NULL;
+ int i = 0;
+ if (!data) return false;
+
+ control = talloc(mem_ctx, struct dsdb_openldap_dereference_result_control);
+ if (!control) return false;
+
+ if (!asn1_load(data, in)) {
+ return false;
+ }
+
+ control = talloc(mem_ctx, struct dsdb_openldap_dereference_result_control);
+ if (!control) {
+ return false;
+ }
+
+ if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ while (asn1_tag_remaining(data) > 0) {
+ r = talloc_realloc(control, r, struct dsdb_openldap_dereference_result *, i + 2);
+ if (!r) {
+ return false;
+ }
+ r[i] = talloc_zero(r, struct dsdb_openldap_dereference_result);
+ if (!r[i]) {
+ return false;
+ }
+
+ if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ asn1_read_OctetString_talloc(r[i], data, &r[i]->source_attribute);
+ asn1_read_OctetString_talloc(r[i], data, &r[i]->dereferenced_dn);
+ if (asn1_peek_tag(data, ASN1_CONTEXT(0))) {
+ if (!asn1_start_tag(data, ASN1_CONTEXT(0))) {
+ return false;
+ }
+
+ ldap_decode_attribs_bare(r, data, &r[i]->attributes,
+ &r[i]->num_attributes);
+
+ if (!asn1_end_tag(data)) {
+ return false;
+ }
+ }
+ if (!asn1_end_tag(data)) {
+ return false;
+ }
+ i++;
+ r[i] = NULL;
+ }
+
+ if (!asn1_end_tag(data)) {
+ return false;
+ }
+
+ control->attributes = r;
+ *out = control;
+
+ return true;
+}
+
+static const struct ldap_control_handler ldap_known_controls[] = {
+ { "1.2.840.113556.1.4.319", decode_paged_results_request, encode_paged_results_request },
+ { "1.2.840.113556.1.4.529", decode_extended_dn_request, encode_extended_dn_request },
+ { "1.2.840.113556.1.4.473", decode_server_sort_request, encode_server_sort_request },
+ { "1.2.840.113556.1.4.474", decode_server_sort_response, encode_server_sort_response },
+ { "1.2.840.113556.1.4.1504", decode_asq_control, encode_asq_control },
+ { "1.2.840.113556.1.4.841", decode_dirsync_request, encode_dirsync_request },
+ { "1.2.840.113556.1.4.528", decode_notification_request, encode_notification_request },
+ { "1.2.840.113556.1.4.417", decode_show_deleted_request, encode_show_deleted_request },
+ { "1.2.840.113556.1.4.1413", decode_permissive_modify_request, encode_permissive_modify_request },
+ { "1.2.840.113556.1.4.801", decode_sd_flags_request, encode_sd_flags_request },
+ { "1.2.840.113556.1.4.1339", decode_domain_scope_request, encode_domain_scope_request },
+ { "1.2.840.113556.1.4.1340", decode_search_options_request, encode_search_options_request },
+ { "2.16.840.1.113730.3.4.2", decode_manageDSAIT_request, encode_manageDSAIT_request },
+ { "2.16.840.1.113730.3.4.9", decode_vlv_request, encode_vlv_request },
+ { "2.16.840.1.113730.3.4.10", decode_vlv_response, encode_vlv_response },
+/* DSDB_CONTROL_CURRENT_PARTITION_OID is internal only, and has no network representation */
+ { "1.3.6.1.4.1.7165.4.3.2", NULL, NULL },
+/* DSDB_EXTENDED_REPLICATED_OBJECTS_OID is internal only, and has no network representation */
+ { "1.3.6.1.4.1.7165.4.4.1", NULL, NULL },
+ { DSDB_OPENLDAP_DEREFERENCE_CONTROL, decode_openldap_dereference, encode_openldap_dereference},
+ { NULL, NULL, NULL }
+};
+
+const struct ldap_control_handler *samba_ldap_control_handlers(void)
+{
+ return ldap_known_controls;
+}
+
diff --git a/source4/libcli/ldap/ldap_ildap.c b/source4/libcli/ldap/ldap_ildap.c
new file mode 100644
index 0000000000..f059e3882a
--- /dev/null
+++ b/source4/libcli/ldap/ldap_ildap.c
@@ -0,0 +1,129 @@
+/*
+ Unix SMB/CIFS mplementation.
+
+ ildap api - an api similar to the traditional ldap api
+
+ 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 <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "includes.h"
+#include "libcli/ldap/ldap.h"
+#include "libcli/ldap/ldap_client.h"
+
+
+/*
+ count the returned search entries
+*/
+_PUBLIC_ int ildap_count_entries(struct ldap_connection *conn, struct ldap_message **res)
+{
+ int i;
+ for (i=0;res && res[i];i++) /* noop */ ;
+ return i;
+}
+
+
+/*
+ perform a synchronous ldap search
+*/
+_PUBLIC_ NTSTATUS ildap_search_bytree(struct ldap_connection *conn, const char *basedn,
+ int scope, struct ldb_parse_tree *tree,
+ const char * const *attrs, bool attributesonly,
+ struct ldb_control **control_req,
+ struct ldb_control ***control_res,
+ struct ldap_message ***results)
+{
+ struct ldap_message *msg;
+ int n, i;
+ NTSTATUS status;
+ struct ldap_request *req;
+
+ if (control_res)
+ *control_res = NULL;
+ *results = NULL;
+
+ msg = new_ldap_message(conn);
+ NT_STATUS_HAVE_NO_MEMORY(msg);
+
+ for (n=0;attrs && attrs[n];n++) /* noop */ ;
+
+ msg->type = LDAP_TAG_SearchRequest;
+ msg->r.SearchRequest.basedn = basedn;
+ msg->r.SearchRequest.scope = scope;
+ msg->r.SearchRequest.deref = LDAP_DEREFERENCE_NEVER;
+ msg->r.SearchRequest.timelimit = 0;
+ msg->r.SearchRequest.sizelimit = 0;
+ msg->r.SearchRequest.attributesonly = attributesonly;
+ msg->r.SearchRequest.tree = tree;
+ msg->r.SearchRequest.num_attributes = n;
+ msg->r.SearchRequest.attributes = attrs;
+ msg->controls = control_req;
+
+ req = ldap_request_send(conn, msg);
+ talloc_steal(msg, req);
+
+ for (i=n=0;true;i++) {
+ struct ldap_message *res;
+ status = ldap_result_n(req, i, &res);
+ if (!NT_STATUS_IS_OK(status)) break;
+
+ if (res->type == LDAP_TAG_SearchResultDone) {
+ status = ldap_check_response(conn, &res->r.GeneralResult);
+ if (control_res) {
+ *control_res = talloc_steal(conn, res->controls);
+ }
+ break;
+ }
+
+ if (res->type != LDAP_TAG_SearchResultEntry &&
+ res->type != LDAP_TAG_SearchResultReference)
+ continue;
+
+ (*results) = talloc_realloc(conn, *results, struct ldap_message *, n+2);
+ if (*results == NULL) {
+ talloc_free(msg);
+ return NT_STATUS_NO_MEMORY;
+ }
+ (*results)[n] = talloc_steal(*results, res);
+ (*results)[n+1] = NULL;
+ n++;
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES)) {
+ status = NT_STATUS_OK;
+ }
+
+ return status;
+}
+
+/*
+ perform a ldap search
+*/
+_PUBLIC_ NTSTATUS ildap_search(struct ldap_connection *conn, const char *basedn,
+ int scope, const char *expression,
+ const char * const *attrs, bool attributesonly,
+ struct ldb_control **control_req,
+ struct ldb_control ***control_res,
+ struct ldap_message ***results)
+{
+ struct ldb_parse_tree *tree = ldb_parse_tree(conn, expression);
+ NTSTATUS status;
+ status = ildap_search_bytree(conn, basedn, scope, tree, attrs,
+ attributesonly, control_req,
+ control_res, results);
+ talloc_free(tree);
+ return status;
+}
diff --git a/source4/libcli/libcli.h b/source4/libcli/libcli.h
new file mode 100644
index 0000000000..e729e12721
--- /dev/null
+++ b/source4/libcli/libcli.h
@@ -0,0 +1,70 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB parameters and setup
+ Copyright (C) Andrew Tridgell 2004
+ Copyright (C) James Myers 2003 <myersjj@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __LIBCLI_H__
+#define __LIBCLI_H__
+
+#include "librpc/gen_ndr/nbt.h"
+
+struct substitute_context;
+
+/*
+ smbcli_state: internal state used in libcli library for single-threaded callers,
+ i.e. a single session on a single socket.
+ */
+struct smbcli_state {
+ struct smbcli_transport *transport;
+ struct smbcli_session *session;
+ struct smbcli_tree *tree;
+ struct substitute_context *substitute;
+ struct smblsa_state *lsa;
+};
+
+struct clilist_file_info {
+ uint64_t size;
+ uint16_t attrib;
+ time_t mtime;
+ const char *name;
+ const char *short_name;
+};
+
+struct nbt_dc_name {
+ const char *address;
+ const char *name;
+};
+
+struct cli_credentials;
+struct tevent_context;
+
+/* passed to br lock code. */
+enum brl_type {
+ READ_LOCK,
+ WRITE_LOCK,
+ PENDING_READ_LOCK,
+ PENDING_WRITE_LOCK
+};
+
+
+
+#include "libcli/raw/libcliraw.h"
+struct gensec_settings;
+#include "libcli/libcli_proto.h"
+
+#endif /* __LIBCLI_H__ */
diff --git a/source4/libcli/rap/rap.h b/source4/libcli/rap/rap.h
new file mode 100644
index 0000000000..6dcaa9bc83
--- /dev/null
+++ b/source4/libcli/rap/rap.h
@@ -0,0 +1,358 @@
+/*
+ Unix SMB/CIFS implementation.
+ RAP operations
+ Copyright (C) Volker Lendecke 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 <http://www.gnu.org/licenses/>.
+*/
+
+#define RAP_WshareEnum 0
+#define RAP_WshareGetInfo 1
+#define RAP_WshareSetInfo 2
+#define RAP_WshareAdd 3
+#define RAP_WshareDel 4
+#define RAP_NetShareCheck 5
+#define RAP_WsessionEnum 6
+#define RAP_WsessionGetInfo 7
+#define RAP_WsessionDel 8
+#define RAP_WconnectionEnum 9
+#define RAP_WfileEnum 10
+#define RAP_WfileGetInfo 11
+#define RAP_WfileClose 12
+#define RAP_WserverGetInfo 13
+#define RAP_WserverSetInfo 14
+#define RAP_WserverDiskEnum 15
+#define RAP_WserverAdminCommand 16
+#define RAP_NetAuditOpen 17
+#define RAP_WauditClear 18
+#define RAP_NetErrorLogOpen 19
+#define RAP_WerrorLogClear 20
+#define RAP_NetCharDevEnum 21
+#define RAP_NetCharDevGetInfo 22
+#define RAP_WCharDevControl 23
+#define RAP_NetCharDevQEnum 24
+#define RAP_NetCharDevQGetInfo 25
+#define RAP_WCharDevQSetInfo 26
+#define RAP_WCharDevQPurge 27
+#define RAP_WCharDevQPurgeSelf 28
+#define RAP_WMessageNameEnum 29
+#define RAP_WMessageNameGetInfo 30
+#define RAP_WMessageNameAdd 31
+#define RAP_WMessageNameDel 32
+#define RAP_WMessageNameFwd 33
+#define RAP_WMessageNameUnFwd 34
+#define RAP_WMessageBufferSend 35
+#define RAP_WMessageFileSend 36
+#define RAP_WMessageLogFileSet 37
+#define RAP_WMessageLogFileGet 38
+#define RAP_WServiceEnum 39
+#define RAP_WServiceInstall 40
+#define RAP_WServiceControl 41
+#define RAP_WAccessEnum 42
+#define RAP_WAccessGetInfo 43
+#define RAP_WAccessSetInfo 44
+#define RAP_WAccessAdd 45
+#define RAP_WAccessDel 46
+#define RAP_WGroupEnum 47
+#define RAP_WGroupAdd 48
+#define RAP_WGroupDel 49
+#define RAP_WGroupAddUser 50
+#define RAP_WGroupDelUser 51
+#define RAP_WGroupGetUsers 52
+#define RAP_WUserEnum 53
+#define RAP_WUserAdd 54
+#define RAP_WUserDel 55
+#define RAP_WUserGetInfo 56
+#define RAP_WUserSetInfo 57
+#define RAP_WUserPasswordSet 58
+#define RAP_WUserGetGroups 59
+#define RAP_WWkstaSetUID 62
+#define RAP_WWkstaGetInfo 63
+#define RAP_WWkstaSetInfo 64
+#define RAP_WUseEnum 65
+#define RAP_WUseAdd 66
+#define RAP_WUseDel 67
+#define RAP_WUseGetInfo 68
+#define RAP_WPrintQEnum 69
+#define RAP_WPrintQGetInfo 70
+#define RAP_WPrintQSetInfo 71
+#define RAP_WPrintQAdd 72
+#define RAP_WPrintQDel 73
+#define RAP_WPrintQPause 74
+#define RAP_WPrintQContinue 75
+#define RAP_WPrintJobEnum 76
+#define RAP_WPrintJobGetInfo 77
+#define RAP_WPrintJobSetInfo_OLD 78
+#define RAP_WPrintJobDel 81
+#define RAP_WPrintJobPause 82
+#define RAP_WPrintJobContinue 83
+#define RAP_WPrintDestEnum 84
+#define RAP_WPrintDestGetInfo 85
+#define RAP_WPrintDestControl 86
+#define RAP_WProfileSave 87
+#define RAP_WProfileLoad 88
+#define RAP_WStatisticsGet 89
+#define RAP_WStatisticsClear 90
+#define RAP_NetRemoteTOD 91
+#define RAP_WNetBiosEnum 92
+#define RAP_WNetBiosGetInfo 93
+#define RAP_NetServerEnum 94
+#define RAP_I_NetServerEnum 95
+#define RAP_WServiceGetInfo 96
+#define RAP_WPrintQPurge 103
+#define RAP_NetServerEnum2 104
+#define RAP_WAccessGetUserPerms 105
+#define RAP_WGroupGetInfo 106
+#define RAP_WGroupSetInfo 107
+#define RAP_WGroupSetUsers 108
+#define RAP_WUserSetGroups 109
+#define RAP_WUserModalsGet 110
+#define RAP_WUserModalsSet 111
+#define RAP_WFileEnum2 112
+#define RAP_WUserAdd2 113
+#define RAP_WUserSetInfo2 114
+#define RAP_WUserPasswordSet2 115
+#define RAP_I_NetServerEnum2 116
+#define RAP_WConfigGet2 117
+#define RAP_WConfigGetAll2 118
+#define RAP_WGetDCName 119
+#define RAP_NetHandleGetInfo 120
+#define RAP_NetHandleSetInfo 121
+#define RAP_WStatisticsGet2 122
+#define RAP_WBuildGetInfo 123
+#define RAP_WFileGetInfo2 124
+#define RAP_WFileClose2 125
+#define RAP_WNetServerReqChallenge 126
+#define RAP_WNetServerAuthenticate 127
+#define RAP_WNetServerPasswordSet 128
+#define RAP_WNetAccountDeltas 129
+#define RAP_WNetAccountSync 130
+#define RAP_WUserEnum2 131
+#define RAP_WWkstaUserLogon 132
+#define RAP_WWkstaUserLogoff 133
+#define RAP_WLogonEnum 134
+#define RAP_WErrorLogRead 135
+#define RAP_NetPathType 136
+#define RAP_NetPathCanonicalize 137
+#define RAP_NetPathCompare 138
+#define RAP_NetNameValidate 139
+#define RAP_NetNameCanonicalize 140
+#define RAP_NetNameCompare 141
+#define RAP_WAuditRead 142
+#define RAP_WPrintDestAdd 143
+#define RAP_WPrintDestSetInfo 144
+#define RAP_WPrintDestDel 145
+#define RAP_WUserValidate2 146
+#define RAP_WPrintJobSetInfo 147
+#define RAP_TI_NetServerDiskEnum 148
+#define RAP_TI_NetServerDiskGetInfo 149
+#define RAP_TI_FTVerifyMirror 150
+#define RAP_TI_FTAbortVerify 151
+#define RAP_TI_FTGetInfo 152
+#define RAP_TI_FTSetInfo 153
+#define RAP_TI_FTLockDisk 154
+#define RAP_TI_FTFixError 155
+#define RAP_TI_FTAbortFix 156
+#define RAP_TI_FTDiagnoseError 157
+#define RAP_TI_FTGetDriveStats 158
+#define RAP_TI_FTErrorGetInfo 160
+#define RAP_NetAccessCheck 163
+#define RAP_NetAlertRaise 164
+#define RAP_NetAlertStart 165
+#define RAP_NetAlertStop 166
+#define RAP_NetAuditWrite 167
+#define RAP_NetIRemoteAPI 168
+#define RAP_NetServiceStatus 169
+#define RAP_NetServerRegister 170
+#define RAP_NetServerDeregister 171
+#define RAP_NetSessionEntryMake 172
+#define RAP_NetSessionEntryClear 173
+#define RAP_NetSessionEntryGetInfo 174
+#define RAP_NetSessionEntrySetInfo 175
+#define RAP_NetConnectionEntryMake 176
+#define RAP_NetConnectionEntryClear 177
+#define RAP_NetConnectionEntrySetInfo 178
+#define RAP_NetConnectionEntryGetInfo 179
+#define RAP_NetFileEntryMake 180
+#define RAP_NetFileEntryClear 181
+#define RAP_NetFileEntrySetInfo 182
+#define RAP_NetFileEntryGetInfo 183
+#define RAP_AltSrvMessageBufferSend 184
+#define RAP_AltSrvMessageFileSend 185
+#define RAP_wI_NetRplWkstaEnum 186
+#define RAP_wI_NetRplWkstaGetInfo 187
+#define RAP_wI_NetRplWkstaSetInfo 188
+#define RAP_wI_NetRplWkstaAdd 189
+#define RAP_wI_NetRplWkstaDel 190
+#define RAP_wI_NetRplProfileEnum 191
+#define RAP_wI_NetRplProfileGetInfo 192
+#define RAP_wI_NetRplProfileSetInfo 193
+#define RAP_wI_NetRplProfileAdd 194
+#define RAP_wI_NetRplProfileDel 195
+#define RAP_wI_NetRplProfileClone 196
+#define RAP_wI_NetRplBaseProfileEnum 197
+#define RAP_WIServerSetInfo 201
+#define RAP_WPrintDriverEnum 205
+#define RAP_WPrintQProcessorEnum 206
+#define RAP_WPrintPortEnum 207
+#define RAP_WNetWriteUpdateLog 208
+#define RAP_WNetAccountUpdate 209
+#define RAP_WNetAccountConfirmUpdate 210
+#define RAP_WConfigSet 211
+#define RAP_WAccountsReplicate 212
+#define RAP_SamOEMChgPasswordUser2_P 214
+#define RAP_NetServerEnum3 215
+#define RAP_WprintDriverGetInfo 250
+#define RAP_WprintDriverSetInfo 251
+#define RAP_WaliasAdd 252
+#define RAP_WaliasDel 253
+#define RAP_WaliasGetInfo 254
+#define RAP_WaliasSetInfo 255
+#define RAP_WaliasEnum 256
+#define RAP_WuserGetLogonAsn 257
+#define RAP_WuserSetLogonAsn 258
+#define RAP_WuserGetAppSel 259
+#define RAP_WuserSetAppSel 260
+#define RAP_WappAdd 261
+#define RAP_WappDel 262
+#define RAP_WappGetInfo 263
+#define RAP_WappSetInfo 264
+#define RAP_WappEnum 265
+#define RAP_WUserDCDBInit 266
+#define RAP_WDASDAdd 267
+#define RAP_WDASDDel 268
+#define RAP_WDASDGetInfo 269
+#define RAP_WDASDSetInfo 270
+#define RAP_WDASDEnum 271
+#define RAP_WDASDCheck 272
+#define RAP_WDASDCtl 273
+#define RAP_WuserRemoteLogonCheck 274
+#define RAP_WUserPasswordSet3 275
+#define RAP_WCreateRIPLMachine 276
+#define RAP_WDeleteRIPLMachine 277
+#define RAP_WGetRIPLMachineInfo 278
+#define RAP_WSetRIPLMachineInfo 279
+#define RAP_WEnumRIPLMachine 280
+#define RAP_I_ShareAdd 281
+#define RAP_AliasEnum 282
+#define RAP_WaccessApply 283
+#define RAP_WPrt16Query 284
+#define RAP_WPrt16Set 285
+#define RAP_WUserDel100 286
+#define RAP_WUserRemoteLogonCheck2 287
+#define RAP_WRemoteTODSet 294
+#define RAP_WprintJobMoveAll 295
+#define RAP_W16AppParmAdd 296
+#define RAP_W16AppParmDel 297
+#define RAP_W16AppParmGet 298
+#define RAP_W16AppParmSet 299
+#define RAP_W16RIPLMachineCreate 300
+#define RAP_W16RIPLMachineGetInfo 301
+#define RAP_W16RIPLMachineSetInfo 302
+#define RAP_W16RIPLMachineEnum 303
+#define RAP_W16RIPLMachineListParmEnum 304
+#define RAP_W16RIPLMachClassGetInfo 305
+#define RAP_W16RIPLMachClassEnum 306
+#define RAP_W16RIPLMachClassCreate 307
+#define RAP_W16RIPLMachClassSetInfo 308
+#define RAP_W16RIPLMachClassDelete 309
+#define RAP_W16RIPLMachClassLPEnum 310
+#define RAP_W16RIPLMachineDelete 311
+#define RAP_W16WSLevelGetInfo 312
+#define RAP_WserverNameAdd 313
+#define RAP_WserverNameDel 314
+#define RAP_WserverNameEnum 315
+#define RAP_I_WDASDEnum 316
+#define RAP_WDASDEnumTerminate 317
+#define RAP_WDASDSetInfo2 318
+#define MAX_API 318
+
+struct rap_shareenum_info_0 {
+ char name[13];
+};
+
+struct rap_shareenum_info_1 {
+ char name[13];
+ char pad;
+ uint16_t type;
+ char *comment;
+};
+
+union rap_shareenum_info {
+ struct rap_shareenum_info_0 info0;
+ struct rap_shareenum_info_1 info1;
+};
+
+struct rap_NetShareEnum {
+ struct {
+ uint16_t level;
+ uint16_t bufsize;
+ } in;
+
+ struct {
+ uint16_t status;
+ uint16_t convert;
+ uint16_t count;
+ uint16_t available;
+ union rap_shareenum_info *info;
+ } out;
+};
+
+struct rap_server_info_0 {
+ char name[16];
+};
+
+struct rap_server_info_1 {
+ char name[16];
+ uint8_t version_major;
+ uint8_t version_minor;
+ uint32_t servertype;
+ char *comment;
+};
+
+union rap_server_info {
+ struct rap_server_info_0 info0;
+ struct rap_server_info_1 info1;
+};
+
+struct rap_NetServerEnum2 {
+ struct {
+ uint16_t level;
+ uint16_t bufsize;
+ uint32_t servertype;
+ const char *domain;
+ } in;
+
+ struct {
+ uint16_t status;
+ uint16_t convert;
+ uint16_t count;
+ uint16_t available;
+ union rap_server_info *info;
+ } out;
+};
+
+struct rap_WserverGetInfo {
+ struct {
+ uint16_t level;
+ uint16_t bufsize;
+ } in;
+
+ struct {
+ uint16_t status;
+ uint16_t convert;
+ uint16_t available;
+ union rap_server_info info;
+ } out;
+};
diff --git a/source4/libcli/raw/README b/source4/libcli/raw/README
new file mode 100644
index 0000000000..cb3e507e3a
--- /dev/null
+++ b/source4/libcli/raw/README
@@ -0,0 +1,5 @@
+Design notes for client library restructure:
+
+1 - no references to cli_state should exist in libcli/raw.
+2 - all interfaces to functions in this directory should use cli_session or cli_tree as
+ the primary context structure \ No newline at end of file
diff --git a/source4/libcli/raw/clierror.c b/source4/libcli/raw/clierror.c
new file mode 100644
index 0000000000..a41748640b
--- /dev/null
+++ b/source4/libcli/raw/clierror.c
@@ -0,0 +1,73 @@
+/*
+ Unix SMB/CIFS implementation.
+ client error handling routines
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) James 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+
+
+/***************************************************************************
+ Return an error message from the last response
+****************************************************************************/
+_PUBLIC_ const char *smbcli_errstr(struct smbcli_tree *tree)
+{
+ switch (tree->session->transport->error.etype) {
+ case ETYPE_SMB:
+ return nt_errstr(tree->session->transport->error.e.nt_status);
+
+ case ETYPE_SOCKET:
+ return "socket_error";
+
+ case ETYPE_NBT:
+ return "nbt_error";
+
+ case ETYPE_NONE:
+ return "no_error";
+ }
+ return NULL;
+}
+
+
+/* Return the 32-bit NT status code from the last packet */
+_PUBLIC_ NTSTATUS smbcli_nt_error(struct smbcli_tree *tree)
+{
+ switch (tree->session->transport->error.etype) {
+ case ETYPE_SMB:
+ return tree->session->transport->error.e.nt_status;
+
+ case ETYPE_SOCKET:
+ return NT_STATUS_UNSUCCESSFUL;
+
+ case ETYPE_NBT:
+ return NT_STATUS_UNSUCCESSFUL;
+
+ case ETYPE_NONE:
+ return NT_STATUS_OK;
+ }
+
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+
+/* Return true if the last packet was an error */
+bool smbcli_is_error(struct smbcli_tree *tree)
+{
+ return NT_STATUS_IS_ERR(smbcli_nt_error(tree));
+}
diff --git a/source4/libcli/raw/clioplock.c b/source4/libcli/raw/clioplock.c
new file mode 100644
index 0000000000..42ac6b517b
--- /dev/null
+++ b/source4/libcli/raw/clioplock.c
@@ -0,0 +1,62 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB client oplock 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+
+/****************************************************************************
+send an ack for an oplock break request
+****************************************************************************/
+_PUBLIC_ bool smbcli_oplock_ack(struct smbcli_tree *tree, uint16_t fnum, uint16_t ack_level)
+{
+ bool ret;
+ struct smbcli_request *req;
+
+ req = smbcli_request_setup(tree, SMBlockingX, 8, 0);
+
+ SSVAL(req->out.vwv,VWV(0),0xFF);
+ SSVAL(req->out.vwv,VWV(1),0);
+ SSVAL(req->out.vwv,VWV(2),fnum);
+ SCVAL(req->out.vwv,VWV(3),LOCKING_ANDX_OPLOCK_RELEASE);
+ SCVAL(req->out.vwv,VWV(3)+1,ack_level);
+ SIVAL(req->out.vwv,VWV(4),0);
+ SSVAL(req->out.vwv,VWV(6),0);
+ SSVAL(req->out.vwv,VWV(7),0);
+
+ /* this request does not expect a reply, so tell the signing
+ subsystem not to allocate an id for a reply */
+ req->one_way_request = 1;
+
+ ret = smbcli_request_send(req);
+
+ return ret;
+}
+
+
+/****************************************************************************
+set the oplock handler for a connection
+****************************************************************************/
+_PUBLIC_ void smbcli_oplock_handler(struct smbcli_transport *transport,
+ bool (*handler)(struct smbcli_transport *, uint16_t, uint16_t, uint8_t, void *),
+ void *private_data)
+{
+ transport->oplock.handler = handler;
+ transport->oplock.private_data = private_data;
+}
diff --git a/source4/libcli/raw/clisession.c b/source4/libcli/raw/clisession.c
new file mode 100644
index 0000000000..41765bfb2b
--- /dev/null
+++ b/source4/libcli/raw/clisession.c
@@ -0,0 +1,297 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB client session context management functions
+
+ Copyright (C) Andrew Tridgell 1994-2005
+ Copyright (C) James Myers 2003 <myersjj@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "system/filesys.h"
+
+#define SETUP_REQUEST_SESSION(cmd, wct, buflen) do { \
+ req = smbcli_request_setup_session(session, cmd, wct, buflen); \
+ if (!req) return NULL; \
+} while (0)
+
+
+/****************************************************************************
+ Initialize the session context
+****************************************************************************/
+struct smbcli_session *smbcli_session_init(struct smbcli_transport *transport,
+ TALLOC_CTX *parent_ctx, bool primary,
+ struct smbcli_session_options options)
+{
+ struct smbcli_session *session;
+ uint16_t flags2;
+ uint32_t capabilities;
+
+ session = talloc_zero(parent_ctx, struct smbcli_session);
+ if (!session) {
+ return NULL;
+ }
+
+ if (primary) {
+ session->transport = talloc_steal(session, transport);
+ } else {
+ session->transport = talloc_reference(session, transport);
+ }
+ session->pid = (uint16_t)getpid();
+ session->vuid = UID_FIELD_INVALID;
+ session->options = options;
+
+ capabilities = transport->negotiate.capabilities;
+
+ flags2 = FLAGS2_LONG_PATH_COMPONENTS | FLAGS2_EXTENDED_ATTRIBUTES;
+
+ if (capabilities & CAP_UNICODE) {
+ flags2 |= FLAGS2_UNICODE_STRINGS;
+ }
+ if (capabilities & CAP_STATUS32) {
+ flags2 |= FLAGS2_32_BIT_ERROR_CODES;
+ }
+ if (capabilities & CAP_EXTENDED_SECURITY) {
+ flags2 |= FLAGS2_EXTENDED_SECURITY;
+ }
+ if (session->transport->negotiate.sign_info.doing_signing) {
+ flags2 |= FLAGS2_SMB_SECURITY_SIGNATURES;
+ }
+
+ session->flags2 = flags2;
+
+ return session;
+}
+
+/****************************************************************************
+ Perform a session setup (async send)
+****************************************************************************/
+struct smbcli_request *smb_raw_sesssetup_send(struct smbcli_session *session,
+ union smb_sesssetup *parms)
+{
+ struct smbcli_request *req = NULL;
+
+ switch (parms->old.level) {
+ case RAW_SESSSETUP_OLD:
+ SETUP_REQUEST_SESSION(SMBsesssetupX, 10, 0);
+ SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+ SSVAL(req->out.vwv, VWV(1), 0);
+ SSVAL(req->out.vwv,VWV(2),parms->old.in.bufsize);
+ SSVAL(req->out.vwv,VWV(3),parms->old.in.mpx_max);
+ SSVAL(req->out.vwv,VWV(4),parms->old.in.vc_num);
+ SIVAL(req->out.vwv,VWV(5),parms->old.in.sesskey);
+ SSVAL(req->out.vwv,VWV(7),parms->old.in.password.length);
+ SIVAL(req->out.vwv,VWV(8), 0); /* reserved */
+ smbcli_req_append_blob(req, &parms->old.in.password);
+ smbcli_req_append_string(req, parms->old.in.user, STR_TERMINATE);
+ smbcli_req_append_string(req, parms->old.in.domain, STR_TERMINATE|STR_UPPER);
+ smbcli_req_append_string(req, parms->old.in.os, STR_TERMINATE);
+ smbcli_req_append_string(req, parms->old.in.lanman, STR_TERMINATE);
+ break;
+
+ case RAW_SESSSETUP_NT1:
+ SETUP_REQUEST_SESSION(SMBsesssetupX, 13, 0);
+ SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+ SSVAL(req->out.vwv, VWV(1), 0);
+ SSVAL(req->out.vwv, VWV(2), parms->nt1.in.bufsize);
+ SSVAL(req->out.vwv, VWV(3), parms->nt1.in.mpx_max);
+ SSVAL(req->out.vwv, VWV(4), parms->nt1.in.vc_num);
+ SIVAL(req->out.vwv, VWV(5), parms->nt1.in.sesskey);
+ SSVAL(req->out.vwv, VWV(7), parms->nt1.in.password1.length);
+ SSVAL(req->out.vwv, VWV(8), parms->nt1.in.password2.length);
+ SIVAL(req->out.vwv, VWV(9), 0); /* reserved */
+ SIVAL(req->out.vwv, VWV(11), parms->nt1.in.capabilities);
+ smbcli_req_append_blob(req, &parms->nt1.in.password1);
+ smbcli_req_append_blob(req, &parms->nt1.in.password2);
+ smbcli_req_append_string(req, parms->nt1.in.user, STR_TERMINATE);
+ smbcli_req_append_string(req, parms->nt1.in.domain, STR_TERMINATE|STR_UPPER);
+ smbcli_req_append_string(req, parms->nt1.in.os, STR_TERMINATE);
+ smbcli_req_append_string(req, parms->nt1.in.lanman, STR_TERMINATE);
+ break;
+
+ case RAW_SESSSETUP_SPNEGO:
+ SETUP_REQUEST_SESSION(SMBsesssetupX, 12, 0);
+ SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+ SSVAL(req->out.vwv, VWV(1), 0);
+ SSVAL(req->out.vwv, VWV(2), parms->spnego.in.bufsize);
+ SSVAL(req->out.vwv, VWV(3), parms->spnego.in.mpx_max);
+ SSVAL(req->out.vwv, VWV(4), parms->spnego.in.vc_num);
+ SIVAL(req->out.vwv, VWV(5), parms->spnego.in.sesskey);
+ SSVAL(req->out.vwv, VWV(7), parms->spnego.in.secblob.length);
+ SIVAL(req->out.vwv, VWV(8), 0); /* reserved */
+ SIVAL(req->out.vwv, VWV(10), parms->spnego.in.capabilities);
+ smbcli_req_append_blob(req, &parms->spnego.in.secblob);
+ smbcli_req_append_string(req, parms->spnego.in.os, STR_TERMINATE);
+ smbcli_req_append_string(req, parms->spnego.in.lanman, STR_TERMINATE);
+ smbcli_req_append_string(req, parms->spnego.in.workgroup, STR_TERMINATE);
+ break;
+
+ case RAW_SESSSETUP_SMB2:
+ return NULL;
+ }
+
+ if (!smbcli_request_send(req)) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+
+/****************************************************************************
+ Perform a session setup (async recv)
+****************************************************************************/
+NTSTATUS smb_raw_sesssetup_recv(struct smbcli_request *req,
+ TALLOC_CTX *mem_ctx,
+ union smb_sesssetup *parms)
+{
+ uint16_t len;
+ uint8_t *p;
+
+ if (!smbcli_request_receive(req)) {
+ return smbcli_request_destroy(req);
+ }
+
+ if (!NT_STATUS_IS_OK(req->status) &&
+ !NT_STATUS_EQUAL(req->status,NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ return smbcli_request_destroy(req);
+ }
+
+ switch (parms->old.level) {
+ case RAW_SESSSETUP_OLD:
+ SMBCLI_CHECK_WCT(req, 3);
+ ZERO_STRUCT(parms->old.out);
+ parms->old.out.vuid = SVAL(req->in.hdr, HDR_UID);
+ parms->old.out.action = SVAL(req->in.vwv, VWV(2));
+ p = req->in.data;
+ if (p) {
+ p += smbcli_req_pull_string(&req->in.bufinfo, mem_ctx, &parms->old.out.os, p, -1, STR_TERMINATE);
+ p += smbcli_req_pull_string(&req->in.bufinfo, mem_ctx, &parms->old.out.lanman, p, -1, STR_TERMINATE);
+ p += smbcli_req_pull_string(&req->in.bufinfo, mem_ctx, &parms->old.out.domain, p, -1, STR_TERMINATE);
+ }
+ break;
+
+ case RAW_SESSSETUP_NT1:
+ SMBCLI_CHECK_WCT(req, 3);
+ ZERO_STRUCT(parms->nt1.out);
+ parms->nt1.out.vuid = SVAL(req->in.hdr, HDR_UID);
+ parms->nt1.out.action = SVAL(req->in.vwv, VWV(2));
+ p = req->in.data;
+ if (p) {
+ p += smbcli_req_pull_string(&req->in.bufinfo, mem_ctx, &parms->nt1.out.os, p, -1, STR_TERMINATE);
+ p += smbcli_req_pull_string(&req->in.bufinfo, mem_ctx, &parms->nt1.out.lanman, p, -1, STR_TERMINATE);
+ if (p < (req->in.data + req->in.data_size)) {
+ p += smbcli_req_pull_string(&req->in.bufinfo, mem_ctx, &parms->nt1.out.domain, p, -1, STR_TERMINATE);
+ }
+ }
+ break;
+
+ case RAW_SESSSETUP_SPNEGO:
+ SMBCLI_CHECK_WCT(req, 4);
+ ZERO_STRUCT(parms->spnego.out);
+ parms->spnego.out.vuid = SVAL(req->in.hdr, HDR_UID);
+ parms->spnego.out.action = SVAL(req->in.vwv, VWV(2));
+ len = SVAL(req->in.vwv, VWV(3));
+ p = req->in.data;
+ if (!p) {
+ break;
+ }
+
+ parms->spnego.out.secblob = smbcli_req_pull_blob(&req->in.bufinfo, mem_ctx, p, len);
+ p += parms->spnego.out.secblob.length;
+ p += smbcli_req_pull_string(&req->in.bufinfo, mem_ctx, &parms->spnego.out.os, p, -1, STR_TERMINATE);
+ p += smbcli_req_pull_string(&req->in.bufinfo, mem_ctx, &parms->spnego.out.lanman, p, -1, STR_TERMINATE);
+ p += smbcli_req_pull_string(&req->in.bufinfo, mem_ctx, &parms->spnego.out.workgroup, p, -1, STR_TERMINATE);
+ break;
+
+ case RAW_SESSSETUP_SMB2:
+ req->status = NT_STATUS_INTERNAL_ERROR;
+ break;
+ }
+
+failed:
+ return smbcli_request_destroy(req);
+}
+
+
+/*
+ Perform a session setup (sync interface)
+*/
+NTSTATUS smb_raw_sesssetup(struct smbcli_session *session,
+ TALLOC_CTX *mem_ctx, union smb_sesssetup *parms)
+{
+ struct smbcli_request *req = smb_raw_sesssetup_send(session, parms);
+ return smb_raw_sesssetup_recv(req, mem_ctx, parms);
+}
+
+
+/****************************************************************************
+ Send a ulogoff (async send)
+*****************************************************************************/
+struct smbcli_request *smb_raw_ulogoff_send(struct smbcli_session *session)
+{
+ struct smbcli_request *req;
+
+ SETUP_REQUEST_SESSION(SMBulogoffX, 2, 0);
+
+ SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+ SSVAL(req->out.vwv, VWV(1), 0);
+
+ if (!smbcli_request_send(req)) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+/****************************************************************************
+ Send a ulogoff (sync interface)
+*****************************************************************************/
+NTSTATUS smb_raw_ulogoff(struct smbcli_session *session)
+{
+ struct smbcli_request *req = smb_raw_ulogoff_send(session);
+ return smbcli_request_simple_recv(req);
+}
+
+
+/****************************************************************************
+ Send a exit (async send)
+*****************************************************************************/
+struct smbcli_request *smb_raw_exit_send(struct smbcli_session *session)
+{
+ struct smbcli_request *req;
+
+ SETUP_REQUEST_SESSION(SMBexit, 0, 0);
+
+ if (!smbcli_request_send(req)) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+/****************************************************************************
+ Send a exit (sync interface)
+*****************************************************************************/
+_PUBLIC_ NTSTATUS smb_raw_exit(struct smbcli_session *session)
+{
+ struct smbcli_request *req = smb_raw_exit_send(session);
+ return smbcli_request_simple_recv(req);
+}
diff --git a/source4/libcli/raw/clisocket.c b/source4/libcli/raw/clisocket.c
new file mode 100644
index 0000000000..b9e83218dd
--- /dev/null
+++ b/source4/libcli/raw/clisocket.c
@@ -0,0 +1,249 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB client socket context management functions
+
+ Copyright (C) Andrew Tridgell 1994-2005
+ Copyright (C) James Myers 2003 <myersjj@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/events/events.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/composite/composite.h"
+#include "lib/socket/socket.h"
+#include "libcli/resolve/resolve.h"
+#include "param/param.h"
+#include "libcli/raw/raw_proto.h"
+
+struct sock_connect_state {
+ struct composite_context *ctx;
+ const char *host_name;
+ int num_ports;
+ uint16_t *ports;
+ const char *socket_options;
+ struct smbcli_socket *result;
+};
+
+/*
+ connect a smbcli_socket context to an IP/port pair
+ if port is 0 then choose 445 then 139
+*/
+
+static void smbcli_sock_connect_recv_conn(struct composite_context *ctx);
+
+struct composite_context *smbcli_sock_connect_send(TALLOC_CTX *mem_ctx,
+ const char *host_addr,
+ const char **ports,
+ const char *host_name,
+ struct resolve_context *resolve_ctx,
+ struct tevent_context *event_ctx,
+ const char *socket_options)
+{
+ struct composite_context *result, *ctx;
+ struct sock_connect_state *state;
+ int i;
+
+ result = talloc_zero(mem_ctx, struct composite_context);
+ if (result == NULL) goto failed;
+ result->state = COMPOSITE_STATE_IN_PROGRESS;
+
+ result->event_ctx = talloc_reference(result, event_ctx);
+ if (result->event_ctx == NULL) goto failed;
+
+ state = talloc(result, struct sock_connect_state);
+ if (state == NULL) goto failed;
+ state->ctx = result;
+ result->private_data = state;
+
+ state->host_name = talloc_strdup(state, host_name);
+ if (state->host_name == NULL) goto failed;
+
+ state->num_ports = str_list_length(ports);
+ state->ports = talloc_array(state, uint16_t, state->num_ports);
+ if (state->ports == NULL) goto failed;
+ for (i=0;ports[i];i++) {
+ state->ports[i] = atoi(ports[i]);
+ }
+ state->socket_options = talloc_reference(state, socket_options);
+
+ ctx = socket_connect_multi_send(state, host_addr,
+ state->num_ports, state->ports,
+ resolve_ctx,
+ state->ctx->event_ctx);
+ if (ctx == NULL) goto failed;
+ ctx->async.fn = smbcli_sock_connect_recv_conn;
+ ctx->async.private_data = state;
+ return result;
+
+failed:
+ talloc_free(result);
+ return NULL;
+}
+
+static void smbcli_sock_connect_recv_conn(struct composite_context *ctx)
+{
+ struct sock_connect_state *state =
+ talloc_get_type(ctx->async.private_data,
+ struct sock_connect_state);
+ struct socket_context *sock;
+ uint16_t port;
+
+ state->ctx->status = socket_connect_multi_recv(ctx, state, &sock,
+ &port);
+ if (!composite_is_ok(state->ctx)) return;
+
+ state->ctx->status =
+ socket_set_option(sock, state->socket_options, NULL);
+ if (!composite_is_ok(state->ctx)) return;
+
+
+ state->result = talloc_zero(state, struct smbcli_socket);
+ if (composite_nomem(state->result, state->ctx)) return;
+
+ state->result->sock = talloc_steal(state->result, sock);
+ state->result->port = port;
+ state->result->hostname = talloc_steal(sock, state->host_name);
+
+ state->result->event.ctx =
+ talloc_reference(state->result, state->ctx->event_ctx);
+ if (composite_nomem(state->result->event.ctx, state->ctx)) return;
+
+ composite_done(state->ctx);
+}
+
+/*
+ finish a smbcli_sock_connect_send() operation
+*/
+NTSTATUS smbcli_sock_connect_recv(struct composite_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct smbcli_socket **result)
+{
+ NTSTATUS status = composite_wait(c);
+ if (NT_STATUS_IS_OK(status)) {
+ struct sock_connect_state *state =
+ talloc_get_type(c->private_data,
+ struct sock_connect_state);
+ *result = talloc_steal(mem_ctx, state->result);
+ }
+ talloc_free(c);
+ return status;
+}
+
+/*
+ connect a smbcli_socket context to an IP/port pair
+ if port is 0 then choose the ports listed in smb.conf (normally 445 then 139)
+
+ sync version of the function
+*/
+NTSTATUS smbcli_sock_connect(TALLOC_CTX *mem_ctx,
+ const char *host_addr, const char **ports,
+ const char *host_name,
+ struct resolve_context *resolve_ctx,
+ struct tevent_context *event_ctx,
+ const char *socket_options,
+ struct smbcli_socket **result)
+{
+ struct composite_context *c =
+ smbcli_sock_connect_send(mem_ctx, host_addr, ports, host_name,
+ resolve_ctx,
+ event_ctx, socket_options);
+ return smbcli_sock_connect_recv(c, mem_ctx, result);
+}
+
+
+/****************************************************************************
+ mark the socket as dead
+****************************************************************************/
+_PUBLIC_ void smbcli_sock_dead(struct smbcli_socket *sock)
+{
+ talloc_free(sock->event.fde);
+ sock->event.fde = NULL;
+ talloc_free(sock->sock);
+ sock->sock = NULL;
+}
+
+/****************************************************************************
+ Set socket options on a open connection.
+****************************************************************************/
+void smbcli_sock_set_options(struct smbcli_socket *sock, const char *options)
+{
+ socket_set_option(sock->sock, options, NULL);
+}
+
+/****************************************************************************
+resolve a hostname and connect
+****************************************************************************/
+_PUBLIC_ struct smbcli_socket *smbcli_sock_connect_byname(const char *host, const char **ports,
+ TALLOC_CTX *mem_ctx,
+ struct resolve_context *resolve_ctx,
+ struct tevent_context *event_ctx,
+ const char *socket_options)
+{
+ int name_type = NBT_NAME_SERVER;
+ const char *address;
+ NTSTATUS status;
+ struct nbt_name nbt_name;
+ char *name, *p;
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+ struct smbcli_socket *result;
+
+ if (event_ctx == NULL) {
+ DEBUG(0, ("Invalid NULL event context passed in as parameter\n"));
+ return NULL;
+ }
+
+ if (tmp_ctx == NULL) {
+ DEBUG(0, ("talloc_new failed\n"));
+ return NULL;
+ }
+
+ name = talloc_strdup(tmp_ctx, host);
+ if (name == NULL) {
+ DEBUG(0, ("talloc_strdup failed\n"));
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+
+ /* allow hostnames of the form NAME#xx and do a netbios lookup */
+ if ((p = strchr(name, '#'))) {
+ name_type = strtol(p+1, NULL, 16);
+ *p = 0;
+ }
+
+ make_nbt_name(&nbt_name, host, name_type);
+
+ status = resolve_name(resolve_ctx, &nbt_name, tmp_ctx, &address, event_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+
+ status = smbcli_sock_connect(mem_ctx, address, ports, name, resolve_ctx,
+ event_ctx,
+ socket_options, &result);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(9, ("smbcli_sock_connect failed: %s\n",
+ nt_errstr(status)));
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+
+ talloc_free(tmp_ctx);
+
+ return result;
+}
diff --git a/source4/libcli/raw/clitransport.c b/source4/libcli/raw/clitransport.c
new file mode 100644
index 0000000000..5cf0272e88
--- /dev/null
+++ b/source4/libcli/raw/clitransport.c
@@ -0,0 +1,680 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB client transport context management functions
+
+ Copyright (C) Andrew Tridgell 1994-2005
+ Copyright (C) James Myers 2003 <myersjj@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "lib/socket/socket.h"
+#include "../lib/util/dlinklist.h"
+#include "lib/events/events.h"
+#include "lib/stream/packet.h"
+#include "librpc/gen_ndr/ndr_nbt.h"
+#include "../libcli/nbt/libnbt.h"
+
+
+/*
+ an event has happened on the socket
+*/
+static void smbcli_transport_event_handler(struct tevent_context *ev,
+ struct tevent_fd *fde,
+ uint16_t flags, void *private_data)
+{
+ struct smbcli_transport *transport = talloc_get_type(private_data,
+ struct smbcli_transport);
+ if (flags & EVENT_FD_READ) {
+ packet_recv(transport->packet);
+ return;
+ }
+ if (flags & EVENT_FD_WRITE) {
+ packet_queue_run(transport->packet);
+ }
+}
+
+/*
+ destroy a transport
+ */
+static int transport_destructor(struct smbcli_transport *transport)
+{
+ smbcli_transport_dead(transport, NT_STATUS_LOCAL_DISCONNECT);
+ return 0;
+}
+
+
+/*
+ handle receive errors
+*/
+static void smbcli_transport_error(void *private_data, NTSTATUS status)
+{
+ struct smbcli_transport *transport = talloc_get_type(private_data, struct smbcli_transport);
+ smbcli_transport_dead(transport, status);
+}
+
+static NTSTATUS smbcli_transport_finish_recv(void *private_data, DATA_BLOB blob);
+
+/*
+ create a transport structure based on an established socket
+*/
+struct smbcli_transport *smbcli_transport_init(struct smbcli_socket *sock,
+ TALLOC_CTX *parent_ctx,
+ bool primary,
+ struct smbcli_options *options,
+ struct smb_iconv_convenience *iconv_convenience)
+{
+ struct smbcli_transport *transport;
+
+ transport = talloc_zero(parent_ctx, struct smbcli_transport);
+ if (!transport) return NULL;
+
+ if (primary) {
+ transport->socket = talloc_steal(transport, sock);
+ } else {
+ transport->socket = talloc_reference(transport, sock);
+ }
+ transport->negotiate.protocol = PROTOCOL_NT1;
+ transport->options = *options;
+ transport->negotiate.max_xmit = transport->options.max_xmit;
+ transport->iconv_convenience = iconv_convenience;
+
+ /* setup the stream -> packet parser */
+ transport->packet = packet_init(transport);
+ if (transport->packet == NULL) {
+ talloc_free(transport);
+ return NULL;
+ }
+ packet_set_private(transport->packet, transport);
+ packet_set_socket(transport->packet, transport->socket->sock);
+ packet_set_callback(transport->packet, smbcli_transport_finish_recv);
+ packet_set_full_request(transport->packet, packet_full_request_nbt);
+ packet_set_error_handler(transport->packet, smbcli_transport_error);
+ packet_set_event_context(transport->packet, transport->socket->event.ctx);
+ packet_set_nofree(transport->packet);
+
+ smbcli_init_signing(transport);
+
+ ZERO_STRUCT(transport->called);
+
+ /* take over event handling from the socket layer - it only
+ handles events up until we are connected */
+ talloc_free(transport->socket->event.fde);
+ transport->socket->event.fde = event_add_fd(transport->socket->event.ctx,
+ transport->socket->sock,
+ socket_get_fd(transport->socket->sock),
+ EVENT_FD_READ,
+ smbcli_transport_event_handler,
+ transport);
+
+ packet_set_fde(transport->packet, transport->socket->event.fde);
+ packet_set_serialise(transport->packet);
+ talloc_set_destructor(transport, transport_destructor);
+
+ return transport;
+}
+
+/*
+ mark the transport as dead
+*/
+void smbcli_transport_dead(struct smbcli_transport *transport, NTSTATUS status)
+{
+ smbcli_sock_dead(transport->socket);
+
+ if (NT_STATUS_EQUAL(NT_STATUS_UNSUCCESSFUL, status)) {
+ status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+
+ /* kill only the first pending receive - this is so that if
+ that async function frees the connection we don't die trying
+ to use old memory. The caller has to cope with only one
+ network error */
+ if (transport->pending_recv) {
+ struct smbcli_request *req = transport->pending_recv;
+ req->state = SMBCLI_REQUEST_ERROR;
+ req->status = status;
+ DLIST_REMOVE(transport->pending_recv, req);
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+ }
+}
+
+
+/*
+ send a session request
+*/
+struct smbcli_request *smbcli_transport_connect_send(struct smbcli_transport *transport,
+ struct nbt_name *calling,
+ struct nbt_name *called)
+{
+ uint8_t *p;
+ struct smbcli_request *req;
+ DATA_BLOB calling_blob, called_blob;
+ TALLOC_CTX *tmp_ctx = talloc_new(transport);
+ NTSTATUS status;
+
+ status = nbt_name_dup(transport, called, &transport->called);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+
+ status = nbt_name_to_blob(tmp_ctx, transport->iconv_convenience, &calling_blob, calling);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+
+ status = nbt_name_to_blob(tmp_ctx, transport->iconv_convenience, &called_blob, called);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+
+ /* allocate output buffer */
+ req = smbcli_request_setup_nonsmb(transport,
+ NBT_HDR_SIZE +
+ calling_blob.length + called_blob.length);
+ if (req == NULL) goto failed;
+
+ /* put in the destination name */
+ p = req->out.buffer + NBT_HDR_SIZE;
+ memcpy(p, called_blob.data, called_blob.length);
+ p += called_blob.length;
+
+ memcpy(p, calling_blob.data, calling_blob.length);
+ p += calling_blob.length;
+
+ _smb_setlen(req->out.buffer, PTR_DIFF(p, req->out.buffer) - NBT_HDR_SIZE);
+ SCVAL(req->out.buffer,0,0x81);
+
+ if (!smbcli_request_send(req)) {
+ smbcli_request_destroy(req);
+ goto failed;
+ }
+
+ talloc_free(tmp_ctx);
+ return req;
+
+failed:
+ talloc_free(tmp_ctx);
+ return NULL;
+}
+
+/*
+ map a session request error to a NTSTATUS
+ */
+static NTSTATUS map_session_refused_error(uint8_t error)
+{
+ switch (error) {
+ case 0x80:
+ case 0x81:
+ return NT_STATUS_REMOTE_NOT_LISTENING;
+ case 0x82:
+ return NT_STATUS_RESOURCE_NAME_NOT_FOUND;
+ case 0x83:
+ return NT_STATUS_REMOTE_RESOURCES;
+ }
+ return NT_STATUS_UNEXPECTED_IO_ERROR;
+}
+
+
+/*
+ finish a smbcli_transport_connect()
+*/
+NTSTATUS smbcli_transport_connect_recv(struct smbcli_request *req)
+{
+ NTSTATUS status;
+
+ if (!smbcli_request_receive(req)) {
+ smbcli_request_destroy(req);
+ return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+
+ switch (CVAL(req->in.buffer,0)) {
+ case 0x82:
+ status = NT_STATUS_OK;
+ break;
+ case 0x83:
+ status = map_session_refused_error(CVAL(req->in.buffer,4));
+ break;
+ case 0x84:
+ DEBUG(1,("Warning: session retarget not supported\n"));
+ status = NT_STATUS_NOT_SUPPORTED;
+ break;
+ default:
+ status = NT_STATUS_UNEXPECTED_IO_ERROR;
+ break;
+ }
+
+ smbcli_request_destroy(req);
+ return status;
+}
+
+
+/*
+ send a session request (if needed)
+*/
+bool smbcli_transport_connect(struct smbcli_transport *transport,
+ struct nbt_name *calling,
+ struct nbt_name *called)
+{
+ struct smbcli_request *req;
+ NTSTATUS status;
+
+ if (transport->socket->port == 445) {
+ return true;
+ }
+
+ req = smbcli_transport_connect_send(transport,
+ calling, called);
+ status = smbcli_transport_connect_recv(req);
+ return NT_STATUS_IS_OK(status);
+}
+
+/****************************************************************************
+get next mid in sequence
+****************************************************************************/
+uint16_t smbcli_transport_next_mid(struct smbcli_transport *transport)
+{
+ uint16_t mid;
+ struct smbcli_request *req;
+
+ mid = transport->next_mid;
+
+again:
+ /* now check to see if this mid is being used by one of the
+ pending requests. This is quite efficient because the list is
+ usually very short */
+
+ /* the zero mid is reserved for requests that don't have a mid */
+ if (mid == 0) mid = 1;
+
+ for (req=transport->pending_recv; req; req=req->next) {
+ if (req->mid == mid) {
+ mid++;
+ goto again;
+ }
+ }
+
+ transport->next_mid = mid+1;
+ return mid;
+}
+
+static void idle_handler(struct tevent_context *ev,
+ struct tevent_timer *te, struct timeval t, void *private_data)
+{
+ struct smbcli_transport *transport = talloc_get_type(private_data,
+ struct smbcli_transport);
+ struct timeval next = timeval_add(&t, 0, transport->idle.period);
+ transport->socket->event.te = event_add_timed(transport->socket->event.ctx,
+ transport,
+ next,
+ idle_handler, transport);
+ transport->idle.func(transport, transport->idle.private_data);
+}
+
+/*
+ setup the idle handler for a transport
+ the period is in microseconds
+*/
+_PUBLIC_ void smbcli_transport_idle_handler(struct smbcli_transport *transport,
+ void (*idle_func)(struct smbcli_transport *, void *),
+ uint64_t period,
+ void *private_data)
+{
+ transport->idle.func = idle_func;
+ transport->idle.private_data = private_data;
+ transport->idle.period = period;
+
+ if (transport->socket->event.te != NULL) {
+ talloc_free(transport->socket->event.te);
+ }
+
+ transport->socket->event.te = event_add_timed(transport->socket->event.ctx,
+ transport,
+ timeval_current_ofs(0, period),
+ idle_handler, transport);
+}
+
+/*
+ we have a full request in our receive buffer - match it to a pending request
+ and process
+ */
+static NTSTATUS smbcli_transport_finish_recv(void *private_data, DATA_BLOB blob)
+{
+ struct smbcli_transport *transport = talloc_get_type(private_data,
+ struct smbcli_transport);
+ uint8_t *buffer, *hdr, *vwv;
+ int len;
+ uint16_t wct=0, mid = 0, op = 0;
+ struct smbcli_request *req = NULL;
+
+ buffer = blob.data;
+ len = blob.length;
+
+ hdr = buffer+NBT_HDR_SIZE;
+ vwv = hdr + HDR_VWV;
+
+ /* see if it could be an oplock break request */
+ if (smbcli_handle_oplock_break(transport, len, hdr, vwv)) {
+ talloc_free(buffer);
+ return NT_STATUS_OK;
+ }
+
+ /* at this point we need to check for a readbraw reply, as
+ these can be any length */
+ if (transport->readbraw_pending) {
+ transport->readbraw_pending = 0;
+
+ /* it must match the first entry in the pending queue
+ as the client is not allowed to have outstanding
+ readbraw requests */
+ req = transport->pending_recv;
+ if (!req) goto error;
+
+ req->in.buffer = buffer;
+ talloc_steal(req, buffer);
+ req->in.size = len;
+ req->in.allocated = req->in.size;
+ goto async;
+ }
+
+ if (len >= MIN_SMB_SIZE) {
+ /* extract the mid for matching to pending requests */
+ mid = SVAL(hdr, HDR_MID);
+ wct = CVAL(hdr, HDR_WCT);
+ op = CVAL(hdr, HDR_COM);
+ }
+
+ /* match the incoming request against the list of pending requests */
+ for (req=transport->pending_recv; req; req=req->next) {
+ if (req->mid == mid) break;
+ }
+
+ /* see if it's a ntcancel reply for the current MID */
+ req = smbcli_handle_ntcancel_reply(req, len, hdr);
+
+ if (!req) {
+ DEBUG(1,("Discarding unmatched reply with mid %d op %d\n", mid, op));
+ goto error;
+ }
+
+ /* fill in the 'in' portion of the matching request */
+ req->in.buffer = buffer;
+ talloc_steal(req, buffer);
+ req->in.size = len;
+ req->in.allocated = req->in.size;
+
+ /* handle NBT session replies */
+ if (req->in.size >= 4 && req->in.buffer[0] != 0) {
+ req->status = NT_STATUS_OK;
+ goto async;
+ }
+
+ /* handle non-SMB replies */
+ if (req->in.size < NBT_HDR_SIZE + MIN_SMB_SIZE) {
+ req->state = SMBCLI_REQUEST_ERROR;
+ goto error;
+ }
+
+ if (req->in.size < NBT_HDR_SIZE + MIN_SMB_SIZE + VWV(wct)) {
+ DEBUG(2,("bad reply size for mid %d\n", mid));
+ req->status = NT_STATUS_UNSUCCESSFUL;
+ req->state = SMBCLI_REQUEST_ERROR;
+ goto error;
+ }
+
+ req->in.hdr = hdr;
+ req->in.vwv = vwv;
+ req->in.wct = wct;
+ if (req->in.size >= NBT_HDR_SIZE + MIN_SMB_SIZE + VWV(wct)) {
+ req->in.data = req->in.vwv + VWV(wct) + 2;
+ req->in.data_size = SVAL(req->in.vwv, VWV(wct));
+ if (req->in.size < NBT_HDR_SIZE + MIN_SMB_SIZE + VWV(wct) + req->in.data_size) {
+ DEBUG(3,("bad data size for mid %d\n", mid));
+ /* blergh - w2k3 gives a bogus data size values in some
+ openX replies */
+ req->in.data_size = req->in.size - (NBT_HDR_SIZE + MIN_SMB_SIZE + VWV(wct));
+ }
+ }
+ req->in.ptr = req->in.data;
+ req->flags2 = SVAL(req->in.hdr, HDR_FLG2);
+
+ smb_setup_bufinfo(req);
+
+ if (!(req->flags2 & FLAGS2_32_BIT_ERROR_CODES)) {
+ int eclass = CVAL(req->in.hdr,HDR_RCLS);
+ int code = SVAL(req->in.hdr,HDR_ERR);
+ if (eclass == 0 && code == 0) {
+ transport->error.e.nt_status = NT_STATUS_OK;
+ } else {
+ transport->error.e.nt_status = NT_STATUS_DOS(eclass, code);
+ }
+ } else {
+ transport->error.e.nt_status = NT_STATUS(IVAL(req->in.hdr, HDR_RCLS));
+ }
+
+ req->status = transport->error.e.nt_status;
+ if (NT_STATUS_IS_OK(req->status)) {
+ transport->error.etype = ETYPE_NONE;
+ } else {
+ transport->error.etype = ETYPE_SMB;
+ }
+
+ if (!smbcli_request_check_sign_mac(req)) {
+ transport->error.etype = ETYPE_SOCKET;
+ transport->error.e.socket_error = SOCKET_READ_BAD_SIG;
+ req->state = SMBCLI_REQUEST_ERROR;
+ req->status = NT_STATUS_ACCESS_DENIED;
+ goto error;
+ };
+
+async:
+ /* if this request has an async handler then call that to
+ notify that the reply has been received. This might destroy
+ the request so it must happen last */
+
+ req->state = SMBCLI_REQUEST_DONE;
+
+ if (req->recv_helper.fn) {
+ /*
+ * let the recv helper decide in
+ * what state the request really is
+ */
+ req->state = req->recv_helper.fn(req);
+
+ /* if more parts are needed, wait for them */
+ if (req->state <= SMBCLI_REQUEST_RECV) {
+ return NT_STATUS_OK;
+ }
+ }
+ DLIST_REMOVE(transport->pending_recv, req);
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+ return NT_STATUS_OK;
+
+error:
+ if (req) {
+ DLIST_REMOVE(transport->pending_recv, req);
+ req->state = SMBCLI_REQUEST_ERROR;
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+ } else {
+ talloc_free(buffer);
+ }
+ return NT_STATUS_OK;
+}
+
+/*
+ process some read/write requests that are pending
+ return false if the socket is dead
+*/
+_PUBLIC_ bool smbcli_transport_process(struct smbcli_transport *transport)
+{
+ NTSTATUS status;
+ size_t npending;
+
+ packet_queue_run(transport->packet);
+ if (transport->socket->sock == NULL) {
+ return false;
+ }
+
+ status = socket_pending(transport->socket->sock, &npending);
+ if (NT_STATUS_IS_OK(status) && npending > 0) {
+ packet_recv(transport->packet);
+ }
+ if (transport->socket->sock == NULL) {
+ return false;
+ }
+ return true;
+}
+
+/*
+ handle timeouts of individual smb requests
+*/
+static void smbcli_timeout_handler(struct tevent_context *ev, struct tevent_timer *te,
+ struct timeval t, void *private_data)
+{
+ struct smbcli_request *req = talloc_get_type(private_data, struct smbcli_request);
+
+ if (req->state == SMBCLI_REQUEST_RECV) {
+ DLIST_REMOVE(req->transport->pending_recv, req);
+ }
+ req->status = NT_STATUS_IO_TIMEOUT;
+ req->state = SMBCLI_REQUEST_ERROR;
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+}
+
+
+/*
+ destroy a request
+*/
+static int smbcli_request_destructor(struct smbcli_request *req)
+{
+ if (req->state == SMBCLI_REQUEST_RECV) {
+ DLIST_REMOVE(req->transport->pending_recv, req);
+ }
+ return 0;
+}
+
+
+/*
+ put a request into the send queue
+*/
+void smbcli_transport_send(struct smbcli_request *req)
+{
+ DATA_BLOB blob;
+ NTSTATUS status;
+
+ /* check if the transport is dead */
+ if (req->transport->socket->sock == NULL) {
+ req->state = SMBCLI_REQUEST_ERROR;
+ req->status = NT_STATUS_NET_WRITE_FAULT;
+ return;
+ }
+
+ blob = data_blob_const(req->out.buffer, req->out.size);
+ status = packet_send(req->transport->packet, blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ req->state = SMBCLI_REQUEST_ERROR;
+ req->status = status;
+ return;
+ }
+
+ if (req->one_way_request) {
+ req->state = SMBCLI_REQUEST_DONE;
+ smbcli_request_destroy(req);
+ return;
+ }
+
+ req->state = SMBCLI_REQUEST_RECV;
+ DLIST_ADD(req->transport->pending_recv, req);
+
+ /* add a timeout */
+ if (req->transport->options.request_timeout) {
+ event_add_timed(req->transport->socket->event.ctx, req,
+ timeval_current_ofs(req->transport->options.request_timeout, 0),
+ smbcli_timeout_handler, req);
+ }
+
+ talloc_set_destructor(req, smbcli_request_destructor);
+}
+
+
+/****************************************************************************
+ Send an SMBecho (async send)
+*****************************************************************************/
+_PUBLIC_ struct smbcli_request *smb_raw_echo_send(struct smbcli_transport *transport,
+ struct smb_echo *p)
+{
+ struct smbcli_request *req;
+
+ req = smbcli_request_setup_transport(transport, SMBecho, 1, p->in.size);
+ if (!req) return NULL;
+
+ SSVAL(req->out.vwv, VWV(0), p->in.repeat_count);
+
+ memcpy(req->out.data, p->in.data, p->in.size);
+
+ ZERO_STRUCT(p->out);
+
+ if (!smbcli_request_send(req)) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+/****************************************************************************
+ raw echo interface (async recv)
+****************************************************************************/
+NTSTATUS smb_raw_echo_recv(struct smbcli_request *req, TALLOC_CTX *mem_ctx,
+ struct smb_echo *p)
+{
+ if (!smbcli_request_receive(req) ||
+ smbcli_request_is_error(req)) {
+ goto failed;
+ }
+
+ SMBCLI_CHECK_WCT(req, 1);
+ p->out.count++;
+ p->out.sequence_number = SVAL(req->in.vwv, VWV(0));
+ p->out.size = req->in.data_size;
+ talloc_free(p->out.data);
+ p->out.data = talloc_array(mem_ctx, uint8_t, p->out.size);
+ NT_STATUS_HAVE_NO_MEMORY(p->out.data);
+
+ if (!smbcli_raw_pull_data(&req->in.bufinfo, req->in.data, p->out.size, p->out.data)) {
+ req->status = NT_STATUS_BUFFER_TOO_SMALL;
+ }
+
+ if (p->out.count == p->in.repeat_count) {
+ return smbcli_request_destroy(req);
+ }
+
+ return NT_STATUS_OK;
+
+failed:
+ return smbcli_request_destroy(req);
+}
+
+/****************************************************************************
+ Send a echo (sync interface)
+*****************************************************************************/
+NTSTATUS smb_raw_echo(struct smbcli_transport *transport, struct smb_echo *p)
+{
+ struct smbcli_request *req = smb_raw_echo_send(transport, p);
+ return smbcli_request_simple_recv(req);
+}
diff --git a/source4/libcli/raw/clitree.c b/source4/libcli/raw/clitree.c
new file mode 100644
index 0000000000..a083396f35
--- /dev/null
+++ b/source4/libcli/raw/clitree.c
@@ -0,0 +1,216 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB client tree context management functions
+
+ Copyright (C) Andrew Tridgell 1994-2005
+ Copyright (C) James Myers 2003 <myersjj@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/smb_composite/smb_composite.h"
+
+#define SETUP_REQUEST_TREE(cmd, wct, buflen) do { \
+ req = smbcli_request_setup(tree, cmd, wct, buflen); \
+ if (!req) return NULL; \
+} while (0)
+
+/****************************************************************************
+ Initialize the tree context
+****************************************************************************/
+_PUBLIC_ struct smbcli_tree *smbcli_tree_init(struct smbcli_session *session,
+ TALLOC_CTX *parent_ctx, bool primary)
+{
+ struct smbcli_tree *tree;
+
+ tree = talloc_zero(parent_ctx, struct smbcli_tree);
+ if (!tree) {
+ return NULL;
+ }
+
+ if (primary) {
+ tree->session = talloc_steal(tree, session);
+ } else {
+ tree->session = talloc_reference(tree, session);
+ }
+
+
+ return tree;
+}
+
+/****************************************************************************
+ Send a tconX (async send)
+****************************************************************************/
+struct smbcli_request *smb_raw_tcon_send(struct smbcli_tree *tree,
+ union smb_tcon *parms)
+{
+ struct smbcli_request *req = NULL;
+
+ switch (parms->tcon.level) {
+ case RAW_TCON_TCON:
+ SETUP_REQUEST_TREE(SMBtcon, 0, 0);
+ smbcli_req_append_ascii4(req, parms->tcon.in.service, STR_ASCII);
+ smbcli_req_append_ascii4(req, parms->tcon.in.password,STR_ASCII);
+ smbcli_req_append_ascii4(req, parms->tcon.in.dev, STR_ASCII);
+ break;
+
+ case RAW_TCON_TCONX:
+ SETUP_REQUEST_TREE(SMBtconX, 4, 0);
+ SSVAL(req->out.vwv, VWV(0), 0xFF);
+ SSVAL(req->out.vwv, VWV(1), 0);
+ SSVAL(req->out.vwv, VWV(2), parms->tconx.in.flags);
+ SSVAL(req->out.vwv, VWV(3), parms->tconx.in.password.length);
+ smbcli_req_append_blob(req, &parms->tconx.in.password);
+ smbcli_req_append_string(req, parms->tconx.in.path, STR_TERMINATE | STR_UPPER);
+ smbcli_req_append_string(req, parms->tconx.in.device, STR_TERMINATE | STR_ASCII);
+ break;
+
+ case RAW_TCON_SMB2:
+ return NULL;
+ }
+
+ if (!smbcli_request_send(req)) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+/****************************************************************************
+ Send a tconX (async recv)
+****************************************************************************/
+NTSTATUS smb_raw_tcon_recv(struct smbcli_request *req, TALLOC_CTX *mem_ctx,
+ union smb_tcon *parms)
+{
+ uint8_t *p;
+
+ if (!smbcli_request_receive(req) ||
+ smbcli_request_is_error(req)) {
+ goto failed;
+ }
+
+ switch (parms->tcon.level) {
+ case RAW_TCON_TCON:
+ SMBCLI_CHECK_WCT(req, 2);
+ parms->tcon.out.max_xmit = SVAL(req->in.vwv, VWV(0));
+ parms->tcon.out.tid = SVAL(req->in.vwv, VWV(1));
+ break;
+
+ case RAW_TCON_TCONX:
+ ZERO_STRUCT(parms->tconx.out);
+ parms->tconx.out.tid = SVAL(req->in.hdr, HDR_TID);
+ if (req->in.wct >= 4) {
+ parms->tconx.out.options = SVAL(req->in.vwv, VWV(3));
+ }
+
+ /* output is actual service name */
+ p = req->in.data;
+ if (!p) break;
+
+ p += smbcli_req_pull_string(&req->in.bufinfo, mem_ctx, &parms->tconx.out.dev_type,
+ p, -1, STR_ASCII | STR_TERMINATE);
+ p += smbcli_req_pull_string(&req->in.bufinfo, mem_ctx, &parms->tconx.out.fs_type,
+ p, -1, STR_TERMINATE);
+ break;
+
+ case RAW_TCON_SMB2:
+ req->status = NT_STATUS_INTERNAL_ERROR;
+ break;
+ }
+
+failed:
+ return smbcli_request_destroy(req);
+}
+
+/****************************************************************************
+ Send a tconX (sync interface)
+****************************************************************************/
+_PUBLIC_ NTSTATUS smb_raw_tcon(struct smbcli_tree *tree, TALLOC_CTX *mem_ctx,
+ union smb_tcon *parms)
+{
+ struct smbcli_request *req = smb_raw_tcon_send(tree, parms);
+ return smb_raw_tcon_recv(req, mem_ctx, parms);
+}
+
+
+/****************************************************************************
+ Send a tree disconnect.
+****************************************************************************/
+_PUBLIC_ NTSTATUS smb_tree_disconnect(struct smbcli_tree *tree)
+{
+ struct smbcli_request *req;
+
+ if (!tree) return NT_STATUS_OK;
+ req = smbcli_request_setup(tree, SMBtdis, 0, 0);
+
+ if (smbcli_request_send(req)) {
+ (void) smbcli_request_receive(req);
+ }
+ return smbcli_request_destroy(req);
+}
+
+
+/*
+ a convenient function to establish a smbcli_tree from scratch
+*/
+NTSTATUS smbcli_tree_full_connection(TALLOC_CTX *parent_ctx,
+ struct smbcli_tree **ret_tree,
+ const char *dest_host, const char **dest_ports,
+ const char *service, const char *service_type,
+ const char *socket_options,
+ struct cli_credentials *credentials,
+ struct resolve_context *resolve_ctx,
+ struct tevent_context *ev,
+ struct smbcli_options *options,
+ struct smbcli_session_options *session_options,
+ struct smb_iconv_convenience *iconv_convenience,
+ struct gensec_settings *gensec_settings)
+{
+ struct smb_composite_connect io;
+ NTSTATUS status;
+ TALLOC_CTX *tmp_ctx = talloc_new(parent_ctx);
+ if (!tmp_ctx) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ io.in.dest_host = dest_host;
+ io.in.dest_ports = dest_ports;
+ io.in.socket_options = socket_options;
+ io.in.called_name = strupper_talloc(tmp_ctx, dest_host);
+ io.in.service = service;
+ io.in.service_type = service_type;
+ io.in.credentials = credentials;
+ io.in.gensec_settings = gensec_settings;
+ io.in.fallback_to_anonymous = false;
+
+ /* This workgroup gets sent out by the SPNEGO session setup.
+ * I don't know of any servers that look at it, so we
+ * hardcode it to "". */
+ io.in.workgroup = "";
+ io.in.options = *options;
+ io.in.session_options = *session_options;
+ io.in.iconv_convenience = iconv_convenience;
+
+ status = smb_composite_connect(&io, parent_ctx, resolve_ctx, ev);
+ if (NT_STATUS_IS_OK(status)) {
+ *ret_tree = io.out.tree;
+ }
+ talloc_free(tmp_ctx);
+ return status;
+}
diff --git a/source4/libcli/raw/interfaces.h b/source4/libcli/raw/interfaces.h
new file mode 100644
index 0000000000..a0584c0aa4
--- /dev/null
+++ b/source4/libcli/raw/interfaces.h
@@ -0,0 +1,2766 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB request interface structures
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) James J Myers 2003 <myersjj@samba.org>
+ Copyright (C) James Peach 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __LIBCLI_RAW_INTERFACES_H__
+#define __LIBCLI_RAW_INTERFACES_H__
+
+#include "smb.h"
+#include "librpc/gen_ndr/misc.h" /* for struct GUID */
+
+/* this structure is just a wrapper for a string, the only reason we
+ bother with this is that it allows us to check the length provided
+ on the wire in testsuite test code to ensure that we are
+ terminating names in the same way that win2003 is. The *ONLY* time
+ you should ever look at the 'private_length' field in this
+ structure is inside compliance test code, in all other cases just
+ use the null terminated char* as the definitive definition of the
+ string
+
+ also note that this structure is only used in packets where there
+ is an explicit length provided on the wire (hence the name). That
+ length is placed in 'private_length'. For packets where the length
+ is always determined by NULL or packet termination a normal char*
+ is used in the structure definition.
+ */
+struct smb_wire_string {
+ uint32_t private_length;
+ const char *s;
+};
+
+/*
+ * SMB2 uses a 16Byte handle,
+ * (we can maybe use struct GUID later)
+ */
+struct smb2_handle {
+ uint64_t data[2];
+};
+
+struct ntvfs_handle;
+
+/*
+ * a generic container for file handles or file pathes
+ * for qfileinfo/setfileinfo and qpathinfo/setpathinfo
+*/
+union smb_handle_or_path {
+ /*
+ * this is used for
+ * the qpathinfo and setpathinfo
+ * calls
+ */
+ const char *path;
+ /*
+ * this is used as file handle in SMB
+ */
+ uint16_t fnum;
+
+ /*
+ * this is used as file handle in SMB2
+ */
+ struct smb2_handle handle;
+
+ /*
+ * this is used as generic file handle for the NTVFS layer
+ */
+ struct ntvfs_handle *ntvfs;
+};
+
+/*
+ a generic container for file handles
+*/
+union smb_handle {
+ /*
+ * this is used as file handle in SMB
+ */
+ uint16_t fnum;
+
+ /*
+ * this is used as file handle in SMB2
+ */
+ struct smb2_handle handle;
+
+ /*
+ * this is used as generic file handle for the NTVFS layer
+ */
+ struct ntvfs_handle *ntvfs;
+};
+
+/*
+ this header defines the structures and unions used between the SMB
+ parser and the backends.
+*/
+
+/* struct used for SMBlseek call */
+union smb_seek {
+ struct {
+ struct {
+ union smb_handle file;
+ uint16_t mode;
+ int32_t offset; /* signed */
+ } in;
+ struct {
+ int32_t offset;
+ } out;
+ } lseek, generic;
+};
+
+/* struct used in unlink() call */
+union smb_unlink {
+ struct {
+ struct {
+ const char *pattern;
+ uint16_t attrib;
+ } in;
+ } unlink;
+};
+
+
+/* struct used in chkpath() call */
+union smb_chkpath {
+ struct {
+ struct {
+ const char *path;
+ } in;
+ } chkpath;
+};
+
+enum smb_mkdir_level {RAW_MKDIR_GENERIC, RAW_MKDIR_MKDIR, RAW_MKDIR_T2MKDIR};
+
+/* union used in mkdir() call */
+union smb_mkdir {
+ /* generic level */
+ struct {
+ enum smb_mkdir_level level;
+ } generic;
+
+ struct {
+ enum smb_mkdir_level level;
+ struct {
+ const char *path;
+ } in;
+ } mkdir;
+
+ struct {
+ enum smb_mkdir_level level;
+ struct {
+ const char *path;
+ uint_t num_eas;
+ struct ea_struct *eas;
+ } in;
+ } t2mkdir;
+};
+
+/* struct used in rmdir() call */
+struct smb_rmdir {
+ struct {
+ const char *path;
+ } in;
+};
+
+/* struct used in rename() call */
+enum smb_rename_level {RAW_RENAME_RENAME, RAW_RENAME_NTRENAME, RAW_RENAME_NTTRANS};
+
+union smb_rename {
+ struct {
+ enum smb_rename_level level;
+ } generic;
+
+ /* SMBrename interface */
+ struct {
+ enum smb_rename_level level;
+
+ struct {
+ const char *pattern1;
+ const char *pattern2;
+ uint16_t attrib;
+ } in;
+ } rename;
+
+
+ /* SMBntrename interface */
+ struct {
+ enum smb_rename_level level;
+
+ struct {
+ uint16_t attrib;
+ uint16_t flags; /* see RENAME_FLAG_* */
+ uint32_t cluster_size;
+ const char *old_name;
+ const char *new_name;
+ } in;
+ } ntrename;
+
+ /* NT TRANS rename interface */
+ struct {
+ enum smb_rename_level level;
+
+ struct {
+ union smb_handle file;
+ uint16_t flags;/* see RENAME_REPLACE_IF_EXISTS */
+ const char *new_name;
+ } in;
+ } nttrans;
+};
+
+enum smb_tcon_level {
+ RAW_TCON_TCON,
+ RAW_TCON_TCONX,
+ RAW_TCON_SMB2
+};
+
+/* union used in tree connect call */
+union smb_tcon {
+ /* generic interface */
+ struct {
+ enum smb_tcon_level level;
+ } generic;
+
+ /* SMBtcon interface */
+ struct {
+ enum smb_tcon_level level;
+
+ struct {
+ const char *service;
+ const char *password;
+ const char *dev;
+ } in;
+ struct {
+ uint16_t max_xmit;
+ uint16_t tid;
+ } out;
+ } tcon;
+
+ /* SMBtconX interface */
+ struct {
+ enum smb_tcon_level level;
+
+ struct {
+ uint16_t flags;
+ DATA_BLOB password;
+ const char *path;
+ const char *device;
+ } in;
+ struct {
+ uint16_t options;
+ char *dev_type;
+ char *fs_type;
+ uint16_t tid;
+ } out;
+ } tconx;
+
+ /* SMB2 TreeConnect */
+ struct smb2_tree_connect {
+ enum smb_tcon_level level;
+
+ struct {
+ /* static body buffer 8 (0x08) bytes */
+ uint16_t reserved;
+ /* uint16_t path_ofs */
+ /* uint16_t path_size */
+ /* dynamic body */
+ const char *path; /* as non-terminated UTF-16 on the wire */
+ } in;
+ struct {
+ /* static body buffer 16 (0x10) bytes */
+ /* uint16_t buffer_code; 0x10 */
+ uint8_t share_type;
+ uint8_t reserved;
+ uint32_t flags;
+ uint32_t capabilities;
+ uint32_t access_mask;
+
+ /* extracted from the SMB2 header */
+ uint32_t tid;
+ } out;
+ } smb2;
+};
+
+
+enum smb_sesssetup_level {
+ RAW_SESSSETUP_OLD,
+ RAW_SESSSETUP_NT1,
+ RAW_SESSSETUP_SPNEGO,
+ RAW_SESSSETUP_SMB2
+};
+
+/* union used in session_setup call */
+union smb_sesssetup {
+ /* the pre-NT1 interface */
+ struct {
+ enum smb_sesssetup_level level;
+
+ struct {
+ uint16_t bufsize;
+ uint16_t mpx_max;
+ uint16_t vc_num;
+ uint32_t sesskey;
+ DATA_BLOB password;
+ const char *user;
+ const char *domain;
+ const char *os;
+ const char *lanman;
+ } in;
+ struct {
+ uint16_t action;
+ uint16_t vuid;
+ char *os;
+ char *lanman;
+ char *domain;
+ } out;
+ } old;
+
+ /* the NT1 interface */
+ struct {
+ enum smb_sesssetup_level level;
+
+ struct {
+ uint16_t bufsize;
+ uint16_t mpx_max;
+ uint16_t vc_num;
+ uint32_t sesskey;
+ uint32_t capabilities;
+ DATA_BLOB password1;
+ DATA_BLOB password2;
+ const char *user;
+ const char *domain;
+ const char *os;
+ const char *lanman;
+ } in;
+ struct {
+ uint16_t action;
+ uint16_t vuid;
+ char *os;
+ char *lanman;
+ char *domain;
+ } out;
+ } nt1;
+
+
+ /* the SPNEGO interface */
+ struct {
+ enum smb_sesssetup_level level;
+
+ struct {
+ uint16_t bufsize;
+ uint16_t mpx_max;
+ uint16_t vc_num;
+ uint32_t sesskey;
+ uint32_t capabilities;
+ DATA_BLOB secblob;
+ const char *os;
+ const char *lanman;
+ const char *workgroup;
+ } in;
+ struct {
+ uint16_t action;
+ DATA_BLOB secblob;
+ char *os;
+ char *lanman;
+ char *workgroup;
+ uint16_t vuid;
+ } out;
+ } spnego;
+
+ /* SMB2 SessionSetup */
+ struct smb2_session_setup {
+ enum smb_sesssetup_level level;
+
+ struct {
+ /* static body 24 (0x18) bytes */
+ uint8_t vc_number;
+ uint8_t security_mode;
+ uint32_t capabilities;
+ uint32_t channel;
+ /* uint16_t secblob_ofs */
+ /* uint16_t secblob_size */
+ uint64_t previous_sessionid;
+ /* dynamic body */
+ DATA_BLOB secblob;
+ } in;
+ struct {
+ /* body buffer 8 (0x08) bytes */
+ uint16_t session_flags;
+ /* uint16_t secblob_ofs */
+ /* uint16_t secblob_size */
+ /* dynamic body */
+ DATA_BLOB secblob;
+
+ /* extracted from the SMB2 header */
+ uint64_t uid;
+ } out;
+ } smb2;
+};
+
+/* Note that the specified enum values are identical to the actual info-levels used
+ * on the wire.
+ */
+enum smb_fileinfo_level {
+ RAW_FILEINFO_GENERIC = 0xF000,
+ RAW_FILEINFO_GETATTR, /* SMBgetatr */
+ RAW_FILEINFO_GETATTRE, /* SMBgetattrE */
+ RAW_FILEINFO_SEC_DESC, /* NT_TRANSACT_QUERY_SECURITY_DESC */
+ RAW_FILEINFO_STANDARD = SMB_QFILEINFO_STANDARD,
+ RAW_FILEINFO_EA_SIZE = SMB_QFILEINFO_EA_SIZE,
+ RAW_FILEINFO_EA_LIST = SMB_QFILEINFO_EA_LIST,
+ RAW_FILEINFO_ALL_EAS = SMB_QFILEINFO_ALL_EAS,
+ RAW_FILEINFO_IS_NAME_VALID = SMB_QFILEINFO_IS_NAME_VALID,
+ RAW_FILEINFO_BASIC_INFO = SMB_QFILEINFO_BASIC_INFO,
+ RAW_FILEINFO_STANDARD_INFO = SMB_QFILEINFO_STANDARD_INFO,
+ RAW_FILEINFO_EA_INFO = SMB_QFILEINFO_EA_INFO,
+ RAW_FILEINFO_NAME_INFO = SMB_QFILEINFO_NAME_INFO,
+ RAW_FILEINFO_ALL_INFO = SMB_QFILEINFO_ALL_INFO,
+ RAW_FILEINFO_ALT_NAME_INFO = SMB_QFILEINFO_ALT_NAME_INFO,
+ RAW_FILEINFO_STREAM_INFO = SMB_QFILEINFO_STREAM_INFO,
+ RAW_FILEINFO_COMPRESSION_INFO = SMB_QFILEINFO_COMPRESSION_INFO,
+ RAW_FILEINFO_UNIX_BASIC = SMB_QFILEINFO_UNIX_BASIC,
+ RAW_FILEINFO_UNIX_INFO2 = SMB_QFILEINFO_UNIX_INFO2,
+ RAW_FILEINFO_UNIX_LINK = SMB_QFILEINFO_UNIX_LINK,
+ RAW_FILEINFO_BASIC_INFORMATION = SMB_QFILEINFO_BASIC_INFORMATION,
+ RAW_FILEINFO_STANDARD_INFORMATION = SMB_QFILEINFO_STANDARD_INFORMATION,
+ RAW_FILEINFO_INTERNAL_INFORMATION = SMB_QFILEINFO_INTERNAL_INFORMATION,
+ RAW_FILEINFO_EA_INFORMATION = SMB_QFILEINFO_EA_INFORMATION,
+ RAW_FILEINFO_ACCESS_INFORMATION = SMB_QFILEINFO_ACCESS_INFORMATION,
+ RAW_FILEINFO_NAME_INFORMATION = SMB_QFILEINFO_NAME_INFORMATION,
+ RAW_FILEINFO_POSITION_INFORMATION = SMB_QFILEINFO_POSITION_INFORMATION,
+ RAW_FILEINFO_MODE_INFORMATION = SMB_QFILEINFO_MODE_INFORMATION,
+ RAW_FILEINFO_ALIGNMENT_INFORMATION = SMB_QFILEINFO_ALIGNMENT_INFORMATION,
+ RAW_FILEINFO_ALL_INFORMATION = SMB_QFILEINFO_ALL_INFORMATION,
+ RAW_FILEINFO_ALT_NAME_INFORMATION = SMB_QFILEINFO_ALT_NAME_INFORMATION,
+ RAW_FILEINFO_STREAM_INFORMATION = SMB_QFILEINFO_STREAM_INFORMATION,
+ RAW_FILEINFO_COMPRESSION_INFORMATION = SMB_QFILEINFO_COMPRESSION_INFORMATION,
+ RAW_FILEINFO_NETWORK_OPEN_INFORMATION = SMB_QFILEINFO_NETWORK_OPEN_INFORMATION,
+ RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION = SMB_QFILEINFO_ATTRIBUTE_TAG_INFORMATION,
+ /* SMB2 specific levels */
+ RAW_FILEINFO_SMB2_ALL_EAS = 0x0f01,
+ RAW_FILEINFO_SMB2_ALL_INFORMATION = 0x1201
+};
+
+/* union used in qfileinfo() and qpathinfo() backend calls */
+union smb_fileinfo {
+ /* generic interface:
+ * matches RAW_FILEINFO_GENERIC */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ } in;
+ struct {
+ uint32_t attrib;
+ uint32_t ea_size;
+ uint_t num_eas;
+ struct ea_struct {
+ uint8_t flags;
+ struct smb_wire_string name;
+ DATA_BLOB value;
+ } *eas;
+ NTTIME create_time;
+ NTTIME access_time;
+ NTTIME write_time;
+ NTTIME change_time;
+ uint64_t alloc_size;
+ uint64_t size;
+ uint32_t nlink;
+ struct smb_wire_string fname;
+ struct smb_wire_string alt_fname;
+ uint8_t delete_pending;
+ uint8_t directory;
+ uint64_t compressed_size;
+ uint16_t format;
+ uint8_t unit_shift;
+ uint8_t chunk_shift;
+ uint8_t cluster_shift;
+ uint64_t file_id;
+ uint32_t access_flags; /* seen 0x001f01ff from w2k3 */
+ uint64_t position;
+ uint32_t mode;
+ uint32_t alignment_requirement;
+ uint32_t reparse_tag;
+ uint_t num_streams;
+ struct stream_struct {
+ uint64_t size;
+ uint64_t alloc_size;
+ struct smb_wire_string stream_name;
+ } *streams;
+ } out;
+ } generic;
+
+
+ /* SMBgetatr interface:
+ * matches RAW_FILEINFO_GETATTR */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ } in;
+ struct {
+ uint16_t attrib;
+ uint32_t size;
+ time_t write_time;
+ } out;
+ } getattr;
+
+ /* SMBgetattrE and RAW_FILEINFO_STANDARD interface */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ } in;
+ struct {
+ time_t create_time;
+ time_t access_time;
+ time_t write_time;
+ uint32_t size;
+ uint32_t alloc_size;
+ uint16_t attrib;
+ } out;
+ } getattre, standard;
+
+ /* trans2 RAW_FILEINFO_EA_SIZE interface */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ } in;
+ struct {
+ time_t create_time;
+ time_t access_time;
+ time_t write_time;
+ uint32_t size;
+ uint32_t alloc_size;
+ uint16_t attrib;
+ uint32_t ea_size;
+ } out;
+ } ea_size;
+
+ /* trans2 RAW_FILEINFO_EA_LIST interface */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ uint_t num_names;
+ struct ea_name {
+ struct smb_wire_string name;
+ } *ea_names;
+ } in;
+
+ struct smb_ea_list {
+ uint_t num_eas;
+ struct ea_struct *eas;
+ } out;
+ } ea_list;
+
+ /* trans2 RAW_FILEINFO_ALL_EAS and RAW_FILEINFO_FULL_EA_INFORMATION interfaces */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ /* SMB2 only - SMB2_CONTINUE_FLAG_* */
+ uint8_t continue_flags;
+ } in;
+ struct smb_ea_list out;
+ } all_eas;
+
+ /* trans2 qpathinfo RAW_FILEINFO_IS_NAME_VALID interface
+ only valid for a QPATHNAME call - no returned data */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ } in;
+ } is_name_valid;
+
+ /* RAW_FILEINFO_BASIC_INFO and RAW_FILEINFO_BASIC_INFORMATION interfaces */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ } in;
+ struct {
+ NTTIME create_time;
+ NTTIME access_time;
+ NTTIME write_time;
+ NTTIME change_time;
+ uint32_t attrib;
+ } out;
+ } basic_info;
+
+
+ /* RAW_FILEINFO_STANDARD_INFO and RAW_FILEINFO_STANDARD_INFORMATION interfaces */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ } in;
+ struct {
+ uint64_t alloc_size;
+ uint64_t size;
+ uint32_t nlink;
+ bool delete_pending;
+ bool directory;
+ } out;
+ } standard_info;
+
+ /* RAW_FILEINFO_EA_INFO and RAW_FILEINFO_EA_INFORMATION interfaces */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ } in;
+ struct {
+ uint32_t ea_size;
+ } out;
+ } ea_info;
+
+ /* RAW_FILEINFO_NAME_INFO and RAW_FILEINFO_NAME_INFORMATION interfaces */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ } in;
+ struct {
+ struct smb_wire_string fname;
+ } out;
+ } name_info;
+
+ /* RAW_FILEINFO_ALL_INFO and RAW_FILEINFO_ALL_INFORMATION interfaces */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ } in;
+ struct {
+ NTTIME create_time;
+ NTTIME access_time;
+ NTTIME write_time;
+ NTTIME change_time;
+ uint32_t attrib;
+ uint64_t alloc_size;
+ uint64_t size;
+ uint32_t nlink;
+ uint8_t delete_pending;
+ uint8_t directory;
+ uint32_t ea_size;
+ struct smb_wire_string fname;
+ } out;
+ } all_info;
+
+ /* RAW_FILEINFO_SMB2_ALL_INFORMATION interface */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ } in;
+ struct {
+ NTTIME create_time;
+ NTTIME access_time;
+ NTTIME write_time;
+ NTTIME change_time;
+ uint32_t attrib;
+ uint32_t unknown1;
+ uint64_t alloc_size;
+ uint64_t size;
+ uint32_t nlink;
+ uint8_t delete_pending;
+ uint8_t directory;
+ /* uint16_t _pad; */
+ uint64_t file_id;
+ uint32_t ea_size;
+ uint32_t access_mask;
+ uint64_t position;
+ uint32_t mode;
+ uint32_t alignment_requirement;
+ struct smb_wire_string fname;
+ } out;
+ } all_info2;
+
+ /* RAW_FILEINFO_ALT_NAME_INFO and RAW_FILEINFO_ALT_NAME_INFORMATION interfaces */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ } in;
+ struct {
+ struct smb_wire_string fname;
+ } out;
+ } alt_name_info;
+
+ /* RAW_FILEINFO_STREAM_INFO and RAW_FILEINFO_STREAM_INFORMATION interfaces */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ } in;
+ struct stream_information {
+ uint_t num_streams;
+ struct stream_struct *streams;
+ } out;
+ } stream_info;
+
+ /* RAW_FILEINFO_COMPRESSION_INFO and RAW_FILEINFO_COMPRESSION_INFORMATION interfaces */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ } in;
+ struct {
+ uint64_t compressed_size;
+ uint16_t format;
+ uint8_t unit_shift;
+ uint8_t chunk_shift;
+ uint8_t cluster_shift;
+ } out;
+ } compression_info;
+
+ /* RAW_FILEINFO_UNIX_BASIC interface */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ } in;
+ struct {
+ 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;
+ } out;
+ } unix_basic_info;
+
+ /* RAW_FILEINFO_UNIX_INFO2 interface */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ } in;
+ struct {
+ 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;
+ } out;
+ } unix_info2;
+
+ /* RAW_FILEINFO_UNIX_LINK interface */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ } in;
+ struct {
+ struct smb_wire_string link_dest;
+ } out;
+ } unix_link_info;
+
+ /* RAW_FILEINFO_INTERNAL_INFORMATION interface */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ } in;
+ struct {
+ uint64_t file_id;
+ } out;
+ } internal_information;
+
+ /* RAW_FILEINFO_ACCESS_INFORMATION interface */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ } in;
+ struct {
+ uint32_t access_flags;
+ } out;
+ } access_information;
+
+ /* RAW_FILEINFO_POSITION_INFORMATION interface */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ } in;
+ struct {
+ uint64_t position;
+ } out;
+ } position_information;
+
+ /* RAW_FILEINFO_MODE_INFORMATION interface */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ } in;
+ struct {
+ uint32_t mode;
+ } out;
+ } mode_information;
+
+ /* RAW_FILEINFO_ALIGNMENT_INFORMATION interface */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ } in;
+ struct {
+ uint32_t alignment_requirement;
+ } out;
+ } alignment_information;
+
+ /* RAW_FILEINFO_NETWORK_OPEN_INFORMATION interface */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ } in;
+ struct {
+ NTTIME create_time;
+ NTTIME access_time;
+ NTTIME write_time;
+ NTTIME change_time;
+ uint64_t alloc_size;
+ uint64_t size;
+ uint32_t attrib;
+ } out;
+ } network_open_information;
+
+
+ /* RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION interface */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ } in;
+ struct {
+ uint32_t attrib;
+ uint32_t reparse_tag;
+ } out;
+ } attribute_tag_information;
+
+ /* RAW_FILEINFO_SEC_DESC */
+ struct {
+ enum smb_fileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ uint32_t secinfo_flags;
+ } in;
+ struct {
+ struct security_descriptor *sd;
+ } out;
+ } query_secdesc;
+};
+
+
+enum smb_setfileinfo_level {
+ RAW_SFILEINFO_GENERIC = 0xF000,
+ RAW_SFILEINFO_SETATTR, /* SMBsetatr */
+ RAW_SFILEINFO_SETATTRE, /* SMBsetattrE */
+ RAW_SFILEINFO_SEC_DESC, /* NT_TRANSACT_SET_SECURITY_DESC */
+ RAW_SFILEINFO_STANDARD = SMB_SFILEINFO_STANDARD,
+ RAW_SFILEINFO_EA_SET = SMB_SFILEINFO_EA_SET,
+ RAW_SFILEINFO_BASIC_INFO = SMB_SFILEINFO_BASIC_INFO,
+ RAW_SFILEINFO_DISPOSITION_INFO = SMB_SFILEINFO_DISPOSITION_INFO,
+ RAW_SFILEINFO_ALLOCATION_INFO = SMB_SFILEINFO_ALLOCATION_INFO,
+ RAW_SFILEINFO_END_OF_FILE_INFO = SMB_SFILEINFO_END_OF_FILE_INFO,
+ RAW_SFILEINFO_UNIX_BASIC = SMB_SFILEINFO_UNIX_BASIC,
+ RAW_SFILEINFO_UNIX_INFO2 = SMB_SFILEINFO_UNIX_INFO2,
+ RAW_SFILEINFO_UNIX_LINK = SMB_SFILEINFO_UNIX_LINK,
+ RAW_SFILEINFO_UNIX_HLINK = SMB_SFILEINFO_UNIX_HLINK,
+ RAW_SFILEINFO_BASIC_INFORMATION = SMB_SFILEINFO_BASIC_INFORMATION,
+ RAW_SFILEINFO_RENAME_INFORMATION = SMB_SFILEINFO_RENAME_INFORMATION,
+ RAW_SFILEINFO_LINK_INFORMATION = SMB_SFILEINFO_LINK_INFORMATION,
+ RAW_SFILEINFO_DISPOSITION_INFORMATION = SMB_SFILEINFO_DISPOSITION_INFORMATION,
+ RAW_SFILEINFO_POSITION_INFORMATION = SMB_SFILEINFO_POSITION_INFORMATION,
+ RAW_SFILEINFO_FULL_EA_INFORMATION = SMB_SFILEINFO_FULL_EA_INFORMATION,
+ RAW_SFILEINFO_MODE_INFORMATION = SMB_SFILEINFO_MODE_INFORMATION,
+ RAW_SFILEINFO_ALLOCATION_INFORMATION = SMB_SFILEINFO_ALLOCATION_INFORMATION,
+ RAW_SFILEINFO_END_OF_FILE_INFORMATION = SMB_SFILEINFO_END_OF_FILE_INFORMATION,
+ RAW_SFILEINFO_PIPE_INFORMATION = SMB_SFILEINFO_PIPE_INFORMATION,
+ RAW_SFILEINFO_VALID_DATA_INFORMATION = SMB_SFILEINFO_VALID_DATA_INFORMATION,
+ RAW_SFILEINFO_SHORT_NAME_INFORMATION = SMB_SFILEINFO_SHORT_NAME_INFORMATION,
+ RAW_SFILEINFO_1025 = SMB_SFILEINFO_1025,
+ RAW_SFILEINFO_1027 = SMB_SFILEINFO_1027,
+ RAW_SFILEINFO_1029 = SMB_SFILEINFO_1029,
+ RAW_SFILEINFO_1030 = SMB_SFILEINFO_1030,
+ RAW_SFILEINFO_1031 = SMB_SFILEINFO_1031,
+ RAW_SFILEINFO_1032 = SMB_SFILEINFO_1032,
+ RAW_SFILEINFO_1036 = SMB_SFILEINFO_1036,
+ RAW_SFILEINFO_1041 = SMB_SFILEINFO_1041,
+ RAW_SFILEINFO_1042 = SMB_SFILEINFO_1042,
+ RAW_SFILEINFO_1043 = SMB_SFILEINFO_1043,
+ RAW_SFILEINFO_1044 = SMB_SFILEINFO_1044,
+
+ /* cope with breakage in SMB2 */
+ RAW_SFILEINFO_RENAME_INFORMATION_SMB2 = SMB_SFILEINFO_RENAME_INFORMATION|0x80000000,
+};
+
+/* union used in setfileinfo() and setpathinfo() calls */
+union smb_setfileinfo {
+ /* generic interface */
+ struct {
+ enum smb_setfileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ } in;
+ } generic;
+
+ /* RAW_SFILEINFO_SETATTR (SMBsetatr) interface - only via setpathinfo() */
+ struct {
+ enum smb_setfileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ uint16_t attrib;
+ time_t write_time;
+ } in;
+ } setattr;
+
+ /* RAW_SFILEINFO_SETATTRE (SMBsetattrE) interface - only via setfileinfo()
+ also RAW_SFILEINFO_STANDARD */
+ struct {
+ enum smb_setfileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ time_t create_time;
+ time_t access_time;
+ time_t write_time;
+ /* notice that size, alloc_size and attrib are not settable,
+ unlike the corresponding qfileinfo level */
+ } in;
+ } setattre, standard;
+
+ /* RAW_SFILEINFO_EA_SET interface */
+ struct {
+ enum smb_setfileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ uint_t num_eas;
+ struct ea_struct *eas;
+ } in;
+ } ea_set;
+
+ /* RAW_SFILEINFO_BASIC_INFO and
+ RAW_SFILEINFO_BASIC_INFORMATION interfaces */
+ struct {
+ enum smb_setfileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ NTTIME create_time;
+ NTTIME access_time;
+ NTTIME write_time;
+ NTTIME change_time;
+ uint32_t attrib;
+ uint32_t reserved;
+ } in;
+ } basic_info;
+
+ /* RAW_SFILEINFO_DISPOSITION_INFO and
+ RAW_SFILEINFO_DISPOSITION_INFORMATION interfaces */
+ struct {
+ enum smb_setfileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ bool delete_on_close;
+ } in;
+ } disposition_info;
+
+ /* RAW_SFILEINFO_ALLOCATION_INFO and
+ RAW_SFILEINFO_ALLOCATION_INFORMATION interfaces */
+ struct {
+ enum smb_setfileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ /* w2k3 rounds this up to nearest 4096 */
+ uint64_t alloc_size;
+ } in;
+ } allocation_info;
+
+ /* RAW_SFILEINFO_END_OF_FILE_INFO and
+ RAW_SFILEINFO_END_OF_FILE_INFORMATION interfaces */
+ struct {
+ enum smb_setfileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ uint64_t size;
+ } in;
+ } end_of_file_info;
+
+ /* RAW_SFILEINFO_RENAME_INFORMATION interface */
+ struct {
+ enum smb_setfileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ uint8_t overwrite;
+ uint64_t root_fid;
+ const char *new_name;
+ } in;
+ } rename_information;
+
+ /* RAW_SFILEINFO_LINK_INFORMATION interface */
+ struct {
+ enum smb_setfileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ uint8_t overwrite;
+ uint64_t root_fid;
+ const char *new_name;
+ } in;
+ } link_information;
+
+ /* RAW_SFILEINFO_POSITION_INFORMATION interface */
+ struct {
+ enum smb_setfileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ uint64_t position;
+ } in;
+ } position_information;
+
+ /* RAW_SFILEINFO_MODE_INFORMATION interface */
+ struct {
+ enum smb_setfileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ /* valid values seem to be 0, 2, 4 and 6 */
+ uint32_t mode;
+ } in;
+ } mode_information;
+
+ /* RAW_SFILEINFO_UNIX_BASIC interface */
+ struct {
+ enum smb_setfileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ uint32_t mode; /* yuck - this field remains to fix compile of libcli/clifile.c */
+ 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;
+ } in;
+ } unix_basic;
+
+ /* RAW_SFILEINFO_UNIX_INFO2 interface */
+ struct {
+ enum smb_setfileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ 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;
+ } in;
+ } unix_info2;
+
+ /* RAW_SFILEINFO_UNIX_LINK, RAW_SFILEINFO_UNIX_HLINK interface */
+ struct {
+ enum smb_setfileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ const char *link_dest;
+ } in;
+ } unix_link, unix_hlink;
+
+ /* RAW_FILEINFO_SET_SEC_DESC */
+ struct {
+ enum smb_setfileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ uint32_t secinfo_flags;
+ struct security_descriptor *sd;
+ } in;
+ } set_secdesc;
+
+ /* RAW_SFILEINFO_FULL_EA_INFORMATION */
+ struct {
+ enum smb_setfileinfo_level level;
+ struct {
+ union smb_handle_or_path file;
+ struct smb_ea_list eas;
+ } in;
+ } full_ea_information;
+};
+
+
+enum smb_fsinfo_level {
+ RAW_QFS_GENERIC = 0xF000,
+ RAW_QFS_DSKATTR, /* SMBdskattr */
+ RAW_QFS_ALLOCATION = SMB_QFS_ALLOCATION,
+ RAW_QFS_VOLUME = SMB_QFS_VOLUME,
+ RAW_QFS_VOLUME_INFO = SMB_QFS_VOLUME_INFO,
+ RAW_QFS_SIZE_INFO = SMB_QFS_SIZE_INFO,
+ RAW_QFS_DEVICE_INFO = SMB_QFS_DEVICE_INFO,
+ RAW_QFS_ATTRIBUTE_INFO = SMB_QFS_ATTRIBUTE_INFO,
+ RAW_QFS_UNIX_INFO = SMB_QFS_UNIX_INFO,
+ RAW_QFS_VOLUME_INFORMATION = SMB_QFS_VOLUME_INFORMATION,
+ RAW_QFS_SIZE_INFORMATION = SMB_QFS_SIZE_INFORMATION,
+ RAW_QFS_DEVICE_INFORMATION = SMB_QFS_DEVICE_INFORMATION,
+ RAW_QFS_ATTRIBUTE_INFORMATION = SMB_QFS_ATTRIBUTE_INFORMATION,
+ RAW_QFS_QUOTA_INFORMATION = SMB_QFS_QUOTA_INFORMATION,
+ RAW_QFS_FULL_SIZE_INFORMATION = SMB_QFS_FULL_SIZE_INFORMATION,
+ RAW_QFS_OBJECTID_INFORMATION = SMB_QFS_OBJECTID_INFORMATION};
+
+
+/* union for fsinfo() backend call. Note that there are no in
+ structures, as this call only contains out parameters */
+union smb_fsinfo {
+ /* generic interface */
+ struct {
+ enum smb_fsinfo_level level;
+ struct smb2_handle handle; /* only for smb2 */
+
+ struct {
+ uint32_t block_size;
+ uint64_t blocks_total;
+ uint64_t blocks_free;
+ uint32_t fs_id;
+ NTTIME create_time;
+ uint32_t serial_number;
+ uint32_t fs_attr;
+ uint32_t max_file_component_length;
+ uint32_t device_type;
+ uint32_t device_characteristics;
+ uint64_t quota_soft;
+ uint64_t quota_hard;
+ uint64_t quota_flags;
+ struct GUID guid;
+ char *volume_name;
+ char *fs_type;
+ } out;
+ } generic;
+
+ /* SMBdskattr interface */
+ struct {
+ enum smb_fsinfo_level level;
+
+ struct {
+ uint16_t units_total;
+ uint16_t blocks_per_unit;
+ uint16_t block_size;
+ uint16_t units_free;
+ } out;
+ } dskattr;
+
+ /* trans2 RAW_QFS_ALLOCATION interface */
+ struct {
+ enum smb_fsinfo_level level;
+
+ struct {
+ uint32_t fs_id;
+ uint32_t sectors_per_unit;
+ uint32_t total_alloc_units;
+ uint32_t avail_alloc_units;
+ uint16_t bytes_per_sector;
+ } out;
+ } allocation;
+
+ /* TRANS2 RAW_QFS_VOLUME interface */
+ struct {
+ enum smb_fsinfo_level level;
+
+ struct {
+ uint32_t serial_number;
+ struct smb_wire_string volume_name;
+ } out;
+ } volume;
+
+ /* TRANS2 RAW_QFS_VOLUME_INFO and RAW_QFS_VOLUME_INFORMATION interfaces */
+ struct {
+ enum smb_fsinfo_level level;
+ struct smb2_handle handle; /* only for smb2 */
+
+ struct {
+ NTTIME create_time;
+ uint32_t serial_number;
+ struct smb_wire_string volume_name;
+ } out;
+ } volume_info;
+
+ /* trans2 RAW_QFS_SIZE_INFO and RAW_QFS_SIZE_INFORMATION interfaces */
+ struct {
+ enum smb_fsinfo_level level;
+ struct smb2_handle handle; /* only for smb2 */
+
+ struct {
+ uint64_t total_alloc_units;
+ uint64_t avail_alloc_units; /* maps to call_avail_alloc_units */
+ uint32_t sectors_per_unit;
+ uint32_t bytes_per_sector;
+ } out;
+ } size_info;
+
+ /* TRANS2 RAW_QFS_DEVICE_INFO and RAW_QFS_DEVICE_INFORMATION interfaces */
+ struct {
+ enum smb_fsinfo_level level;
+ struct smb2_handle handle; /* only for smb2 */
+
+ struct {
+ uint32_t device_type;
+ uint32_t characteristics;
+ } out;
+ } device_info;
+
+
+ /* TRANS2 RAW_QFS_ATTRIBUTE_INFO and RAW_QFS_ATTRIBUTE_INFORMATION interfaces */
+ struct {
+ enum smb_fsinfo_level level;
+ struct smb2_handle handle; /* only for smb2 */
+
+ struct {
+ uint32_t fs_attr;
+ uint32_t max_file_component_length;
+ struct smb_wire_string fs_type;
+ } out;
+ } attribute_info;
+
+
+ /* TRANS2 RAW_QFS_UNIX_INFO interface */
+ struct {
+ enum smb_fsinfo_level level;
+
+ struct {
+ uint16_t major_version;
+ uint16_t minor_version;
+ uint64_t capability;
+ } out;
+ } unix_info;
+
+ /* trans2 RAW_QFS_QUOTA_INFORMATION interface */
+ struct {
+ enum smb_fsinfo_level level;
+ struct smb2_handle handle; /* only for smb2 */
+
+ struct {
+ uint64_t unknown[3];
+ uint64_t quota_soft;
+ uint64_t quota_hard;
+ uint64_t quota_flags;
+ } out;
+ } quota_information;
+
+ /* trans2 RAW_QFS_FULL_SIZE_INFORMATION interface */
+ struct {
+ enum smb_fsinfo_level level;
+ struct smb2_handle handle; /* only for smb2 */
+
+ struct {
+ uint64_t total_alloc_units;
+ uint64_t call_avail_alloc_units;
+ uint64_t actual_avail_alloc_units;
+ uint32_t sectors_per_unit;
+ uint32_t bytes_per_sector;
+ } out;
+ } full_size_information;
+
+ /* trans2 RAW_QFS_OBJECTID_INFORMATION interface */
+ struct {
+ enum smb_fsinfo_level level;
+ struct smb2_handle handle; /* only for smb2 */
+
+ struct {
+ struct GUID guid;
+ uint64_t unknown[6];
+ } out;
+ } objectid_information;
+};
+
+
+
+enum smb_open_level {
+ RAW_OPEN_OPEN,
+ RAW_OPEN_OPENX,
+ RAW_OPEN_MKNEW,
+ RAW_OPEN_CREATE,
+ RAW_OPEN_CTEMP,
+ RAW_OPEN_SPLOPEN,
+ RAW_OPEN_NTCREATEX,
+ RAW_OPEN_T2OPEN,
+ RAW_OPEN_NTTRANS_CREATE,
+ RAW_OPEN_OPENX_READX,
+ RAW_OPEN_SMB2
+};
+
+/* the generic interface is defined to be equal to the NTCREATEX interface */
+#define RAW_OPEN_GENERIC RAW_OPEN_NTCREATEX
+
+/* union for open() backend call */
+union smb_open {
+/*
+ * because the *.out.file structs are not aligned to the same offset for each level
+ * we provide a hepler macro that should be used to find the current smb_handle structure
+ */
+#define SMB_OPEN_OUT_FILE(op, file) do { \
+ switch (op->generic.level) { \
+ case RAW_OPEN_OPEN: \
+ file = &op->openold.out.file; \
+ break; \
+ case RAW_OPEN_OPENX: \
+ file = &op->openx.out.file; \
+ break; \
+ case RAW_OPEN_MKNEW: \
+ file = &op->mknew.out.file; \
+ break; \
+ case RAW_OPEN_CREATE: \
+ file = &op->create.out.file; \
+ break; \
+ case RAW_OPEN_CTEMP: \
+ file = &op->ctemp.out.file; \
+ break; \
+ case RAW_OPEN_SPLOPEN: \
+ file = &op->splopen.out.file; \
+ break; \
+ case RAW_OPEN_NTCREATEX: \
+ file = &op->ntcreatex.out.file; \
+ break; \
+ case RAW_OPEN_T2OPEN: \
+ file = &op->t2open.out.file; \
+ break; \
+ case RAW_OPEN_NTTRANS_CREATE: \
+ file = &op->nttrans.out.file; \
+ break; \
+ case RAW_OPEN_OPENX_READX: \
+ file = &op->openxreadx.out.file; \
+ break; \
+ case RAW_OPEN_SMB2: \
+ file = &op->smb2.out.file; \
+ break; \
+ default: \
+ /* this must be a programmer error */ \
+ file = NULL; \
+ break; \
+ } \
+} while (0)
+ /* SMBNTCreateX, nttrans and generic interface */
+ struct {
+ enum smb_open_level level;
+ struct {
+ uint32_t flags;
+ uint32_t root_fid;
+ uint32_t access_mask;
+ uint64_t alloc_size;
+ uint32_t file_attr;
+ uint32_t share_access;
+ uint32_t open_disposition;
+ uint32_t create_options;
+ uint32_t impersonation;
+ uint8_t security_flags;
+ /* NOTE: fname can also be a pointer to a
+ uint64_t file_id if create_options has the
+ NTCREATEX_OPTIONS_OPEN_BY_FILE_ID flag set */
+ const char *fname;
+
+ /* these last 2 elements are only used in the
+ NTTRANS varient of the call */
+ struct security_descriptor *sec_desc;
+ struct smb_ea_list *ea_list;
+
+ /* some optional parameters from the SMB2 varient */
+ bool query_maximal_access;
+ } in;
+ struct {
+ union smb_handle file;
+ uint8_t oplock_level;
+ uint32_t create_action;
+ NTTIME create_time;
+ NTTIME access_time;
+ NTTIME write_time;
+ NTTIME change_time;
+ uint32_t attrib;
+ uint64_t alloc_size;
+ uint64_t size;
+ uint16_t file_type;
+ uint16_t ipc_state;
+ uint8_t is_directory;
+
+ /* optional return values matching SMB2 tagged
+ values in the call */
+ uint32_t maximal_access;
+ } out;
+ } ntcreatex, nttrans, generic;
+
+ /* TRANS2_OPEN interface */
+ struct {
+ enum smb_open_level level;
+ struct {
+ uint16_t flags;
+ uint16_t open_mode;
+ uint16_t search_attrs;
+ uint16_t file_attrs;
+ time_t write_time;
+ uint16_t open_func;
+ uint32_t size;
+ uint32_t timeout;
+ const char *fname;
+ uint_t num_eas;
+ struct ea_struct *eas;
+ } in;
+ struct {
+ union smb_handle file;
+ uint16_t attrib;
+ time_t write_time;
+ uint32_t size;
+ uint16_t access;
+ uint16_t ftype;
+ uint16_t devstate;
+ uint16_t action;
+ uint32_t file_id;
+ } out;
+ } t2open;
+
+ /* SMBopen interface */
+ struct {
+ enum smb_open_level level;
+ struct {
+ uint16_t open_mode;
+ uint16_t search_attrs;
+ const char *fname;
+ } in;
+ struct {
+ union smb_handle file;
+ uint16_t attrib;
+ time_t write_time;
+ uint32_t size;
+ uint16_t rmode;
+ } out;
+ } openold;
+
+ /* SMBopenX interface */
+ struct {
+ enum smb_open_level level;
+ struct {
+ uint16_t flags;
+ uint16_t open_mode;
+ uint16_t search_attrs; /* not honoured by win2003 */
+ uint16_t file_attrs;
+ time_t write_time; /* not honoured by win2003 */
+ uint16_t open_func;
+ uint32_t size; /* note that this sets the
+ initial file size, not
+ just allocation size */
+ uint32_t timeout; /* not honoured by win2003 */
+ const char *fname;
+ } in;
+ struct {
+ union smb_handle file;
+ uint16_t attrib;
+ time_t write_time;
+ uint32_t size;
+ uint16_t access;
+ uint16_t ftype;
+ uint16_t devstate;
+ uint16_t action;
+ uint32_t unique_fid;
+ uint32_t access_mask;
+ uint32_t unknown;
+ } out;
+ } openx;
+
+ /* SMBmknew interface */
+ struct {
+ enum smb_open_level level;
+ struct {
+ uint16_t attrib;
+ time_t write_time;
+ const char *fname;
+ } in;
+ struct {
+ union smb_handle file;
+ } out;
+ } mknew, create;
+
+ /* SMBctemp interface */
+ struct {
+ enum smb_open_level level;
+ struct {
+ uint16_t attrib;
+ time_t write_time;
+ const char *directory;
+ } in;
+ struct {
+ union smb_handle file;
+ /* temp name, relative to directory */
+ char *name;
+ } out;
+ } ctemp;
+
+ /* SMBsplopen interface */
+ struct {
+ enum smb_open_level level;
+ struct {
+ uint16_t setup_length;
+ uint16_t mode;
+ const char *ident;
+ } in;
+ struct {
+ union smb_handle file;
+ } out;
+ } splopen;
+
+
+ /* chained OpenX/ReadX interface */
+ struct {
+ enum smb_open_level level;
+ struct {
+ uint16_t flags;
+ uint16_t open_mode;
+ uint16_t search_attrs; /* not honoured by win2003 */
+ uint16_t file_attrs;
+ time_t write_time; /* not honoured by win2003 */
+ uint16_t open_func;
+ uint32_t size; /* note that this sets the
+ initial file size, not
+ just allocation size */
+ uint32_t timeout; /* not honoured by win2003 */
+ const char *fname;
+
+ /* readx part */
+ uint64_t offset;
+ uint16_t mincnt;
+ uint32_t maxcnt;
+ uint16_t remaining;
+ } in;
+ struct {
+ union smb_handle file;
+ uint16_t attrib;
+ time_t write_time;
+ uint32_t size;
+ uint16_t access;
+ uint16_t ftype;
+ uint16_t devstate;
+ uint16_t action;
+ uint32_t unique_fid;
+ uint32_t access_mask;
+ uint32_t unknown;
+
+ /* readx part */
+ uint8_t *data;
+ uint16_t remaining;
+ uint16_t compaction_mode;
+ uint16_t nread;
+ } out;
+ } openxreadx;
+
+#define SMB2_CREATE_FLAG_REQUEST_OPLOCK 0x0100
+#define SMB2_CREATE_FLAG_REQUEST_EXCLUSIVE_OPLOCK 0x0800
+#define SMB2_CREATE_FLAG_GRANT_OPLOCK 0x0001
+#define SMB2_CREATE_FLAG_GRANT_EXCLUSIVE_OPLOCK 0x0080
+
+ /* SMB2 Create */
+ struct smb2_create {
+ enum smb_open_level level;
+ struct {
+ /* static body buffer 56 (0x38) bytes */
+ uint8_t security_flags; /* SMB2_SECURITY_* */
+ uint8_t oplock_level; /* SMB2_OPLOCK_LEVEL_* */
+ uint32_t impersonation_level; /* SMB2_IMPERSONATION_* */
+ uint64_t create_flags;
+ uint64_t reserved;
+ uint32_t desired_access;
+ uint32_t file_attributes;
+ uint32_t share_access; /* NTCREATEX_SHARE_ACCESS_* */
+ uint32_t create_disposition; /* NTCREATEX_DISP_* */
+ uint32_t create_options; /* NTCREATEX_OPTIONS_* */
+
+ /* uint16_t fname_ofs */
+ /* uint16_t fname_size */
+ /* uint32_t blob_ofs; */
+ /* uint32_t blob_size; */
+
+ /* dynamic body */
+ const char *fname;
+
+ /* now some optional parameters - encoded as tagged blobs */
+ struct smb_ea_list eas;
+ uint64_t alloc_size;
+ struct security_descriptor *sec_desc;
+ bool durable_open;
+ struct smb2_handle *durable_handle;
+ bool query_maximal_access;
+ NTTIME timewarp;
+ bool query_on_disk_id;
+
+ /* and any additional blobs the caller wants */
+ struct smb2_create_blobs {
+ uint32_t num_blobs;
+ struct smb2_create_blob {
+ const char *tag;
+ DATA_BLOB data;
+ } *blobs;
+ } blobs;
+ } in;
+ struct {
+ union smb_handle file;
+
+ /* static body buffer 88 (0x58) bytes */
+ /* uint16_t buffer_code; 0x59 = 0x58 + 1 */
+ uint8_t oplock_level;
+ uint8_t reserved;
+ uint32_t create_action;
+ NTTIME create_time;
+ NTTIME access_time;
+ NTTIME write_time;
+ NTTIME change_time;
+ uint64_t alloc_size;
+ uint64_t size;
+ uint32_t file_attr;
+ uint32_t reserved2;
+ /* struct smb2_handle handle;*/
+ /* uint32_t blob_ofs; */
+ /* uint32_t blob_size; */
+
+ /* optional return values matching tagged values in the call */
+ uint32_t maximal_access;
+ uint8_t on_disk_id[32];
+
+ /* tagged blobs in the reply */
+ struct smb2_create_blobs blobs;
+ } out;
+ } smb2;
+};
+
+
+
+enum smb_read_level {
+ RAW_READ_READBRAW,
+ RAW_READ_LOCKREAD,
+ RAW_READ_READ,
+ RAW_READ_READX,
+ RAW_READ_SMB2
+};
+
+#define RAW_READ_GENERIC RAW_READ_READX
+
+/* union for read() backend call
+
+ note that .infoX.out.data will be allocated before the backend is
+ called. It will be big enough to hold the maximum size asked for
+*/
+union smb_read {
+ /* SMBreadX (and generic) interface */
+ struct {
+ enum smb_read_level level;
+ struct {
+ union smb_handle file;
+ uint64_t offset;
+ uint32_t mincnt; /* enforced on SMB2, 16 bit on SMB */
+ uint32_t maxcnt;
+ uint16_t remaining;
+ bool read_for_execute;
+ } in;
+ struct {
+ uint8_t *data;
+ uint16_t remaining;
+ uint16_t compaction_mode;
+ uint32_t nread;
+ } out;
+ } readx, generic;
+
+ /* SMBreadbraw interface */
+ struct {
+ enum smb_read_level level;
+ struct {
+ union smb_handle file;
+ uint64_t offset;
+ uint16_t maxcnt;
+ uint16_t mincnt;
+ uint32_t timeout;
+ } in;
+ struct {
+ uint8_t *data;
+ uint32_t nread;
+ } out;
+ } readbraw;
+
+
+ /* SMBlockandread interface */
+ struct {
+ enum smb_read_level level;
+ struct {
+ union smb_handle file;
+ uint16_t count;
+ uint32_t offset;
+ uint16_t remaining;
+ } in;
+ struct {
+ uint8_t *data;
+ uint16_t nread;
+ } out;
+ } lockread;
+
+ /* SMBread interface */
+ struct {
+ enum smb_read_level level;
+ struct {
+ union smb_handle file;
+ uint16_t count;
+ uint32_t offset;
+ uint16_t remaining;
+ } in;
+ struct {
+ uint8_t *data;
+ uint16_t nread;
+ } out;
+ } read;
+
+ /* SMB2 Read */
+ struct smb2_read {
+ enum smb_read_level level;
+ struct {
+ union smb_handle file;
+
+ /* static body buffer 48 (0x30) bytes */
+ /* uint16_t buffer_code; 0x31 = 0x30 + 1 */
+ uint8_t _pad;
+ uint8_t reserved;
+ uint32_t length;
+ uint64_t offset;
+ /* struct smb2_handle handle; */
+ uint32_t min_count;
+ uint32_t channel;
+ uint32_t remaining;
+ /* the docs give no indication of what
+ these channel variables are for */
+ uint16_t channel_offset;
+ uint16_t channel_length;
+ } in;
+ struct {
+ /* static body buffer 16 (0x10) bytes */
+ /* uint16_t buffer_code; 0x11 = 0x10 + 1 */
+ /* uint8_t data_ofs; */
+ /* uint8_t reserved; */
+ /* uint32_t data_size; */
+ uint32_t remaining;
+ uint32_t reserved;
+
+ /* dynamic body */
+ DATA_BLOB data;
+ } out;
+ } smb2;
+};
+
+
+enum smb_write_level {
+ RAW_WRITE_WRITEUNLOCK,
+ RAW_WRITE_WRITE,
+ RAW_WRITE_WRITEX,
+ RAW_WRITE_WRITECLOSE,
+ RAW_WRITE_SPLWRITE,
+ RAW_WRITE_SMB2
+};
+
+#define RAW_WRITE_GENERIC RAW_WRITE_WRITEX
+
+/* union for write() backend call
+*/
+union smb_write {
+ /* SMBwriteX interface */
+ struct {
+ enum smb_write_level level;
+ struct {
+ union smb_handle file;
+ uint64_t offset;
+ uint16_t wmode;
+ uint16_t remaining;
+ uint32_t count;
+ const uint8_t *data;
+ } in;
+ struct {
+ uint32_t nwritten;
+ uint16_t remaining;
+ } out;
+ } writex, generic;
+
+ /* SMBwriteunlock interface */
+ struct {
+ enum smb_write_level level;
+ struct {
+ union smb_handle file;
+ uint16_t count;
+ uint32_t offset;
+ uint16_t remaining;
+ const uint8_t *data;
+ } in;
+ struct {
+ uint32_t nwritten;
+ } out;
+ } writeunlock;
+
+ /* SMBwrite interface */
+ struct {
+ enum smb_write_level level;
+ struct {
+ union smb_handle file;
+ uint16_t count;
+ uint32_t offset;
+ uint16_t remaining;
+ const uint8_t *data;
+ } in;
+ struct {
+ uint16_t nwritten;
+ } out;
+ } write;
+
+ /* SMBwriteclose interface */
+ struct {
+ enum smb_write_level level;
+ struct {
+ union smb_handle file;
+ uint16_t count;
+ uint32_t offset;
+ time_t mtime;
+ const uint8_t *data;
+ } in;
+ struct {
+ uint16_t nwritten;
+ } out;
+ } writeclose;
+
+ /* SMBsplwrite interface */
+ struct {
+ enum smb_write_level level;
+ struct {
+ union smb_handle file;
+ uint16_t count;
+ const uint8_t *data;
+ } in;
+ } splwrite;
+
+ /* SMB2 Write */
+ struct smb2_write {
+ enum smb_write_level level;
+ struct {
+ union smb_handle file;
+
+ /* static body buffer 48 (0x30) bytes */
+ /* uint16_t buffer_code; 0x31 = 0x30 + 1 */
+ /* uint16_t data_ofs; */
+ /* uint32_t data_size; */
+ uint64_t offset;
+ /* struct smb2_handle handle; */
+ uint64_t unknown1; /* 0xFFFFFFFFFFFFFFFF */
+ uint64_t unknown2; /* 0xFFFFFFFFFFFFFFFF */
+
+ /* dynamic body */
+ DATA_BLOB data;
+ } in;
+ struct {
+ /* static body buffer 17 (0x11) bytes */
+ /* uint16_t buffer_code; 0x11 = 0x10 + 1*/
+ uint16_t _pad;
+ uint32_t nwritten;
+ uint64_t unknown1; /* 0x0000000000000000 */
+ } out;
+ } smb2;
+};
+
+
+enum smb_lock_level {
+ RAW_LOCK_LOCK,
+ RAW_LOCK_UNLOCK,
+ RAW_LOCK_LOCKX,
+ RAW_LOCK_SMB2,
+ RAW_LOCK_SMB2_BREAK
+};
+
+#define RAW_LOCK_GENERIC RAW_LOCK_LOCKX
+
+/* union for lock() backend call
+*/
+union smb_lock {
+ /* SMBlockingX and generic interface */
+ struct {
+ enum smb_lock_level level;
+ struct {
+ union smb_handle file;
+ uint16_t mode;
+ uint32_t timeout;
+ uint16_t ulock_cnt;
+ uint16_t lock_cnt;
+ struct smb_lock_entry {
+ uint32_t pid; /* 16 bits in SMB1 */
+ uint64_t offset;
+ uint64_t count;
+ } *locks; /* unlocks are first in the arrray */
+ } in;
+ } generic, lockx;
+
+ /* SMBlock and SMBunlock interface */
+ struct {
+ enum smb_lock_level level;
+ struct {
+ union smb_handle file;
+ uint32_t count;
+ uint32_t offset;
+ } in;
+ } lock, unlock;
+
+ /* SMB2 Lock */
+ struct smb2_lock {
+ enum smb_lock_level level;
+ struct {
+ union smb_handle file;
+
+ /* static body buffer 48 (0x30) bytes */
+ /* uint16_t buffer_code; 0x30 */
+ uint16_t lock_count;
+ uint32_t reserved;
+ /* struct smb2_handle handle; */
+ struct smb2_lock_element {
+ uint64_t offset;
+ uint64_t length;
+/* these flags are the same as the SMB2 lock flags */
+#define SMB2_LOCK_FLAG_NONE 0x00000000
+#define SMB2_LOCK_FLAG_SHARED 0x00000001
+#define SMB2_LOCK_FLAG_EXCLUSIVE 0x00000002
+#define SMB2_LOCK_FLAG_UNLOCK 0x00000004
+#define SMB2_LOCK_FLAG_FAIL_IMMEDIATELY 0x00000010
+#define SMB2_LOCK_FLAG_ALL_MASK 0x00000017
+ uint32_t flags;
+ uint32_t reserved;
+ } *locks;
+ } in;
+ struct {
+ /* static body buffer 4 (0x04) bytes */
+ /* uint16_t buffer_code; 0x04 */
+ uint16_t reserved;
+ } out;
+ } smb2;
+
+ /* SMB2 Break */
+ struct smb2_break {
+ enum smb_lock_level level;
+ struct {
+ union smb_handle file;
+
+ /* static body buffer 24 (0x18) bytes */
+ uint8_t oplock_level;
+ uint8_t reserved;
+ uint32_t reserved2;
+ /* struct smb2_handle handle; */
+ } in, out;
+ } smb2_break;
+};
+
+
+enum smb_close_level {
+ RAW_CLOSE_CLOSE,
+ RAW_CLOSE_SPLCLOSE,
+ RAW_CLOSE_SMB2,
+ RAW_CLOSE_GENERIC,
+};
+
+/*
+ union for close() backend call
+*/
+union smb_close {
+ /* generic interface */
+ struct {
+ enum smb_close_level level;
+ struct {
+ union smb_handle file;
+ time_t write_time;
+#define SMB2_CLOSE_FLAGS_FULL_INFORMATION (1<<0)
+ uint16_t flags; /* SMB2_CLOSE_FLAGS_* */
+ } in;
+ struct {
+ uint16_t flags;
+ NTTIME create_time;
+ NTTIME access_time;
+ NTTIME write_time;
+ NTTIME change_time;
+ uint64_t alloc_size;
+ uint64_t size;
+ uint32_t file_attr;
+ } out;
+ } generic;
+
+ /* SMBclose interface */
+ struct {
+ enum smb_close_level level;
+ struct {
+ union smb_handle file;
+ time_t write_time;
+ } in;
+ } close;
+
+ /* SMBsplclose interface - empty! */
+ struct {
+ enum smb_close_level level;
+ struct {
+ union smb_handle file;
+ } in;
+ } splclose;
+
+ /* SMB2 Close */
+ struct smb2_close {
+ enum smb_close_level level;
+ struct {
+ union smb_handle file;
+
+ /* static body buffer 24 (0x18) bytes */
+ /* uint16_t buffer_code; 0x18 */
+ uint16_t flags; /* SMB2_CLOSE_FLAGS_* */
+ uint32_t _pad;
+ } in;
+ struct {
+ /* static body buffer 60 (0x3C) bytes */
+ /* uint16_t buffer_code; 0x3C */
+ uint16_t flags;
+ uint32_t _pad;
+ NTTIME create_time;
+ NTTIME access_time;
+ NTTIME write_time;
+ NTTIME change_time;
+ uint64_t alloc_size;
+ uint64_t size;
+ uint32_t file_attr;
+ } out;
+ } smb2;
+};
+
+
+enum smb_lpq_level {RAW_LPQ_GENERIC, RAW_LPQ_RETQ};
+
+/*
+ union for lpq() backend
+*/
+union smb_lpq {
+ /* generic interface */
+ struct {
+ enum smb_lpq_level level;
+
+ } generic;
+
+
+ /* SMBsplretq interface */
+ struct {
+ enum smb_lpq_level level;
+
+ struct {
+ uint16_t maxcount;
+ uint16_t startidx;
+ } in;
+ struct {
+ uint16_t count;
+ uint16_t restart_idx;
+ struct {
+ time_t time;
+ uint8_t status;
+ uint16_t job;
+ uint32_t size;
+ char *user;
+ } *queue;
+ } out;
+ } retq;
+};
+
+enum smb_ioctl_level {
+ RAW_IOCTL_IOCTL,
+ RAW_IOCTL_NTIOCTL,
+ RAW_IOCTL_SMB2,
+ RAW_IOCTL_SMB2_NO_HANDLE
+};
+
+/*
+ union for ioctl() backend
+*/
+union smb_ioctl {
+ /* generic interface */
+ struct {
+ enum smb_ioctl_level level;
+ struct {
+ union smb_handle file;
+ } in;
+ } generic;
+
+ /* struct for SMBioctl */
+ struct {
+ enum smb_ioctl_level level;
+ struct {
+ union smb_handle file;
+ uint32_t request;
+ } in;
+ struct {
+ DATA_BLOB blob;
+ } out;
+ } ioctl;
+
+
+ /* struct for NT ioctl call */
+ struct {
+ enum smb_ioctl_level level;
+ struct {
+ union smb_handle file;
+ uint32_t function;
+ bool fsctl;
+ uint8_t filter;
+ uint32_t max_data;
+ DATA_BLOB blob;
+ } in;
+ struct {
+ DATA_BLOB blob;
+ } out;
+ } ntioctl;
+
+ /* SMB2 Ioctl */
+ struct smb2_ioctl {
+ enum smb_ioctl_level level;
+ struct {
+ union smb_handle file;
+
+ /* static body buffer 56 (0x38) bytes */
+ /* uint16_t buffer_code; 0x39 = 0x38 + 1 */
+ uint16_t _pad;
+ uint32_t function;
+ /*struct smb2_handle handle;*/
+ /* uint32_t out_ofs; */
+ /* uint32_t out_size; */
+ uint32_t unknown2;
+ /* uint32_t in_ofs; */
+ /* uint32_t in_size; */
+ uint32_t max_response_size;
+ uint64_t flags;
+
+ /* dynamic body */
+ DATA_BLOB out;
+ DATA_BLOB in;
+ } in;
+ struct {
+ union smb_handle file;
+
+ /* static body buffer 48 (0x30) bytes */
+ /* uint16_t buffer_code; 0x31 = 0x30 + 1 */
+ uint16_t _pad;
+ uint32_t function;
+ /* struct smb2_handle handle; */
+ /* uint32_t in_ofs; */
+ /* uint32_t in_size; */
+ /* uint32_t out_ofs; */
+ /* uint32_t out_size; */
+ uint32_t unknown2;
+ uint32_t unknown3;
+
+ /* dynamic body */
+ DATA_BLOB in;
+ DATA_BLOB out;
+ } out;
+ } smb2;
+};
+
+enum smb_flush_level {
+ RAW_FLUSH_FLUSH,
+ RAW_FLUSH_ALL,
+ RAW_FLUSH_SMB2
+};
+
+union smb_flush {
+ /* struct for SMBflush */
+ struct {
+ enum smb_flush_level level;
+ struct {
+ union smb_handle file;
+ } in;
+ } flush, generic;
+
+ /* SMBflush with 0xFFFF wildcard fnum */
+ struct {
+ enum smb_flush_level level;
+ } flush_all;
+
+ /* SMB2 Flush */
+ struct smb2_flush {
+ enum smb_flush_level level;
+ struct {
+ union smb_handle file;
+ uint16_t reserved1;
+ uint32_t reserved2;
+ } in;
+ struct {
+ uint16_t reserved;
+ } out;
+ } smb2;
+};
+
+/* struct for SMBcopy */
+struct smb_copy {
+ struct {
+ uint16_t tid2;
+ uint16_t ofun;
+ uint16_t flags;
+ const char *path1;
+ const char *path2;
+ } in;
+ struct {
+ uint16_t count;
+ } out;
+};
+
+
+/* struct for transact/transact2 call */
+struct smb_trans2 {
+ struct {
+ uint16_t max_param;
+ uint16_t max_data;
+ uint8_t max_setup;
+ uint16_t flags;
+ uint32_t timeout;
+ uint8_t setup_count;
+ uint16_t *setup;
+ const char *trans_name; /* SMBtrans only */
+ DATA_BLOB params;
+ DATA_BLOB data;
+ } in;
+
+ struct {
+ uint8_t setup_count;
+ uint16_t *setup;
+ DATA_BLOB params;
+ DATA_BLOB data;
+ } out;
+};
+
+/* struct for nttransact2 call */
+struct smb_nttrans {
+ struct {
+ uint8_t max_setup;
+ uint32_t max_param;
+ uint32_t max_data;
+ uint8_t setup_count;
+ uint16_t function;
+ uint8_t *setup;
+ DATA_BLOB params;
+ DATA_BLOB data;
+ } in;
+
+ struct {
+ uint8_t setup_count; /* in units of 16 bit words */
+ uint8_t *setup;
+ DATA_BLOB params;
+ DATA_BLOB data;
+ } out;
+};
+
+enum smb_notify_level {
+ RAW_NOTIFY_NTTRANS,
+ RAW_NOTIFY_SMB2
+};
+
+union smb_notify {
+ /* struct for nttrans change notify call */
+ struct {
+ enum smb_notify_level level;
+
+ struct {
+ union smb_handle file;
+ uint32_t buffer_size;
+ uint32_t completion_filter;
+ bool recursive;
+ } in;
+
+ struct {
+ uint32_t num_changes;
+ struct notify_changes {
+ uint32_t action;
+ struct smb_wire_string name;
+ } *changes;
+ } out;
+ } nttrans;
+
+ struct smb2_notify {
+ enum smb_notify_level level;
+
+ struct {
+ union smb_handle file;
+ /* static body buffer 32 (0x20) bytes */
+ /* uint16_t buffer_code; 0x32 */
+ uint16_t recursive;
+ uint32_t buffer_size;
+ /*struct smb2_handle file;*/
+ uint32_t completion_filter;
+ uint32_t unknown;
+ } in;
+
+ struct {
+ /* static body buffer 8 (0x08) bytes */
+ /* uint16_t buffer_code; 0x09 = 0x08 + 1 */
+ /* uint16_t blob_ofs; */
+ /* uint16_t blob_size; */
+
+ /* dynamic body */
+ /*DATA_BLOB blob;*/
+
+ /* DATA_BLOB content */
+ uint32_t num_changes;
+ struct notify_changes *changes;
+ } out;
+ } smb2;
+};
+
+enum smb_search_level {
+ RAW_SEARCH_SEARCH, /* SMBsearch */
+ RAW_SEARCH_FFIRST, /* SMBffirst */
+ RAW_SEARCH_FUNIQUE, /* SMBfunique */
+ RAW_SEARCH_TRANS2, /* SMBtrans2 */
+ RAW_SEARCH_SMB2 /* SMB2 Find */
+};
+
+enum smb_search_data_level {
+ RAW_SEARCH_DATA_GENERIC = 0x10000, /* only used in the smbcli_ code */
+ RAW_SEARCH_DATA_SEARCH,
+ RAW_SEARCH_DATA_STANDARD = SMB_FIND_STANDARD,
+ RAW_SEARCH_DATA_EA_SIZE = SMB_FIND_EA_SIZE,
+ RAW_SEARCH_DATA_EA_LIST = SMB_FIND_EA_LIST,
+ RAW_SEARCH_DATA_DIRECTORY_INFO = SMB_FIND_DIRECTORY_INFO,
+ RAW_SEARCH_DATA_FULL_DIRECTORY_INFO = SMB_FIND_FULL_DIRECTORY_INFO,
+ RAW_SEARCH_DATA_NAME_INFO = SMB_FIND_NAME_INFO,
+ RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO = SMB_FIND_BOTH_DIRECTORY_INFO,
+ RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO = SMB_FIND_ID_FULL_DIRECTORY_INFO,
+ RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO = SMB_FIND_ID_BOTH_DIRECTORY_INFO,
+ RAW_SEARCH_DATA_UNIX_INFO = SMB_FIND_UNIX_INFO,
+ RAW_SEARCH_DATA_UNIX_INFO2 = SMB_FIND_UNIX_INFO2
+};
+
+/* union for file search */
+union smb_search_first {
+ struct {
+ enum smb_search_level level;
+ enum smb_search_data_level data_level;
+ } generic;
+
+ /* search (old) findfirst interface.
+ Also used for ffirst and funique. */
+ struct {
+ enum smb_search_level level;
+ enum smb_search_data_level data_level;
+
+ struct {
+ uint16_t max_count;
+ uint16_t search_attrib;
+ const char *pattern;
+ } in;
+ struct {
+ int16_t count;
+ } out;
+ } search_first;
+
+ /* trans2 findfirst interface */
+ struct {
+ enum smb_search_level level;
+ enum smb_search_data_level data_level;
+
+ struct {
+ uint16_t search_attrib;
+ uint16_t max_count;
+ uint16_t flags;
+ uint32_t storage_type;
+ const char *pattern;
+
+ /* the ea names are only used for RAW_SEARCH_EA_LIST */
+ uint_t num_names;
+ struct ea_name *ea_names;
+ } in;
+ struct {
+ uint16_t handle;
+ uint16_t count;
+ uint16_t end_of_search;
+ } out;
+ } t2ffirst;
+
+/*
+ SMB2 uses different level numbers for the same old SMB trans2 search levels
+*/
+#define SMB2_FIND_DIRECTORY_INFO 0x01
+#define SMB2_FIND_FULL_DIRECTORY_INFO 0x02
+#define SMB2_FIND_BOTH_DIRECTORY_INFO 0x03
+#define SMB2_FIND_NAME_INFO 0x0C
+#define SMB2_FIND_ID_BOTH_DIRECTORY_INFO 0x25
+#define SMB2_FIND_ID_FULL_DIRECTORY_INFO 0x26
+
+/* flags for SMB2 find */
+#define SMB2_CONTINUE_FLAG_RESTART 0x01
+#define SMB2_CONTINUE_FLAG_SINGLE 0x02
+#define SMB2_CONTINUE_FLAG_INDEX 0x04
+#define SMB2_CONTINUE_FLAG_REOPEN 0x10
+
+ /* SMB2 Find */
+ struct smb2_find {
+ enum smb_search_level level;
+ enum smb_search_data_level data_level;
+ struct {
+ union smb_handle file;
+
+ /* static body buffer 32 (0x20) bytes */
+ /* uint16_t buffer_code; 0x21 = 0x20 + 1 */
+ uint8_t level;
+ uint8_t continue_flags; /* SMB2_CONTINUE_FLAG_* */
+ uint32_t file_index;
+ /* struct smb2_handle handle; */
+ /* uint16_t pattern_ofs; */
+ /* uint16_t pattern_size; */
+ uint32_t max_response_size;
+
+ /* dynamic body */
+ const char *pattern;
+ } in;
+ struct {
+ /* static body buffer 8 (0x08) bytes */
+ /* uint16_t buffer_code; 0x08 */
+ /* uint16_t blob_ofs; */
+ /* uint32_t blob_size; */
+
+ /* dynamic body */
+ DATA_BLOB blob;
+ } out;
+ } smb2;
+};
+
+/* union for file search continue */
+union smb_search_next {
+ struct {
+ enum smb_search_level level;
+ enum smb_search_data_level data_level;
+ } generic;
+
+ /* search (old) findnext interface. Also used
+ for ffirst when continuing */
+ struct {
+ enum smb_search_level level;
+ enum smb_search_data_level data_level;
+
+ struct {
+ uint16_t max_count;
+ uint16_t search_attrib;
+ struct smb_search_id {
+ uint8_t reserved;
+ char name[11];
+ uint8_t handle;
+ uint32_t server_cookie;
+ uint32_t client_cookie;
+ } id;
+ } in;
+ struct {
+ uint16_t count;
+ } out;
+ } search_next;
+
+ /* trans2 findnext interface */
+ struct {
+ enum smb_search_level level;
+ enum smb_search_data_level data_level;
+
+ struct {
+ uint16_t handle;
+ uint16_t max_count;
+ uint32_t resume_key;
+ uint16_t flags;
+ const char *last_name;
+
+ /* the ea names are only used for RAW_SEARCH_EA_LIST */
+ uint_t num_names;
+ struct ea_name *ea_names;
+ } in;
+ struct {
+ uint16_t count;
+ uint16_t end_of_search;
+ } out;
+ } t2fnext;
+
+ /* SMB2 Find */
+ struct smb2_find smb2;
+};
+
+/* union for search reply file data */
+union smb_search_data {
+ /*
+ * search (old) findfirst
+ * RAW_SEARCH_DATA_SEARCH
+ */
+ struct {
+ uint16_t attrib;
+ time_t write_time;
+ uint32_t size;
+ struct smb_search_id id;
+ const char *name;
+ } search;
+
+ /* trans2 findfirst RAW_SEARCH_DATA_STANDARD level */
+ struct {
+ uint32_t resume_key;
+ time_t create_time;
+ time_t access_time;
+ time_t write_time;
+ uint32_t size;
+ uint32_t alloc_size;
+ uint16_t attrib;
+ struct smb_wire_string name;
+ } standard;
+
+ /* trans2 findfirst RAW_SEARCH_DATA_EA_SIZE level */
+ struct {
+ uint32_t resume_key;
+ time_t create_time;
+ time_t access_time;
+ time_t write_time;
+ uint32_t size;
+ uint32_t alloc_size;
+ uint16_t attrib;
+ uint32_t ea_size;
+ struct smb_wire_string name;
+ } ea_size;
+
+ /* trans2 findfirst RAW_SEARCH_DATA_EA_LIST level */
+ struct {
+ uint32_t resume_key;
+ time_t create_time;
+ time_t access_time;
+ time_t write_time;
+ uint32_t size;
+ uint32_t alloc_size;
+ uint16_t attrib;
+ struct smb_ea_list eas;
+ struct smb_wire_string name;
+ } ea_list;
+
+ /* RAW_SEARCH_DATA_DIRECTORY_INFO interface */
+ struct {
+ uint32_t file_index;
+ NTTIME create_time;
+ NTTIME access_time;
+ NTTIME write_time;
+ NTTIME change_time;
+ uint64_t size;
+ uint64_t alloc_size;
+ uint32_t attrib;
+ struct smb_wire_string name;
+ } directory_info;
+
+ /* RAW_SEARCH_DATA_FULL_DIRECTORY_INFO interface */
+ struct {
+ uint32_t file_index;
+ NTTIME create_time;
+ NTTIME access_time;
+ NTTIME write_time;
+ NTTIME change_time;
+ uint64_t size;
+ uint64_t alloc_size;
+ uint32_t attrib;
+ uint32_t ea_size;
+ struct smb_wire_string name;
+ } full_directory_info;
+
+ /* RAW_SEARCH_DATA_NAME_INFO interface */
+ struct {
+ uint32_t file_index;
+ struct smb_wire_string name;
+ } name_info;
+
+ /* RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO interface */
+ struct {
+ uint32_t file_index;
+ NTTIME create_time;
+ NTTIME access_time;
+ NTTIME write_time;
+ NTTIME change_time;
+ uint64_t size;
+ uint64_t alloc_size;
+ uint32_t attrib;
+ uint32_t ea_size;
+ struct smb_wire_string short_name;
+ struct smb_wire_string name;
+ } both_directory_info;
+
+ /* RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO interface */
+ struct {
+ uint32_t file_index;
+ NTTIME create_time;
+ NTTIME access_time;
+ NTTIME write_time;
+ NTTIME change_time;
+ uint64_t size;
+ uint64_t alloc_size;
+ uint32_t attrib;
+ uint32_t ea_size;
+ uint64_t file_id;
+ struct smb_wire_string name;
+ } id_full_directory_info;
+
+ /* RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO interface */
+ struct {
+ uint32_t file_index;
+ NTTIME create_time;
+ NTTIME access_time;
+ NTTIME write_time;
+ NTTIME change_time;
+ uint64_t size;
+ uint64_t alloc_size;
+ uint32_t attrib;
+ uint32_t ea_size;
+ uint64_t file_id;
+ struct smb_wire_string short_name;
+ struct smb_wire_string name;
+ } id_both_directory_info;
+
+ /* RAW_SEARCH_DATA_UNIX_INFO interface */
+ struct {
+ uint32_t file_index;
+ uint64_t size;
+ uint64_t alloc_size;
+ 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;
+ const char *name;
+ } unix_info;
+
+ /* RAW_SEARCH_DATA_UNIX_INFO2 interface */
+ struct {
+ uint32_t file_index;
+ 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;
+ struct smb_wire_string name;
+ } unix_info2;
+};
+
+/* Callback function passed to the raw search interface. */
+typedef bool (*smbcli_search_callback)(void *private_data, const union smb_search_data *file);
+
+enum smb_search_close_level {RAW_FINDCLOSE_GENERIC, RAW_FINDCLOSE_FCLOSE, RAW_FINDCLOSE_FINDCLOSE};
+
+/* union for file search close */
+union smb_search_close {
+ struct {
+ enum smb_search_close_level level;
+ } generic;
+
+ /* SMBfclose (old search) interface */
+ struct {
+ enum smb_search_close_level level;
+
+ struct {
+ /* max_count and search_attrib are not used, but are present */
+ uint16_t max_count;
+ uint16_t search_attrib;
+ struct smb_search_id id;
+ } in;
+ } fclose;
+
+ /* SMBfindclose interface */
+ struct {
+ enum smb_search_close_level level;
+
+ struct {
+ uint16_t handle;
+ } in;
+ } findclose;
+};
+
+
+/*
+ struct for SMBecho call
+*/
+struct smb_echo {
+ struct {
+ uint16_t repeat_count;
+ uint16_t size;
+ uint8_t *data;
+ } in;
+ struct {
+ uint16_t count;
+ uint16_t sequence_number;
+ uint16_t size;
+ uint8_t *data;
+ } out;
+};
+
+/*
+ struct for shadow copy volumes
+ */
+struct smb_shadow_copy {
+ struct {
+ union smb_handle file;
+ uint32_t max_data;
+ } in;
+ struct {
+ uint32_t num_volumes;
+ uint32_t num_names;
+ const char **names;
+ } out;
+};
+
+#endif /* __LIBCLI_RAW_INTERFACES_H__ */
diff --git a/source4/libcli/raw/ioctl.h b/source4/libcli/raw/ioctl.h
new file mode 100644
index 0000000000..a9d3d1b7a6
--- /dev/null
+++ b/source4/libcli/raw/ioctl.h
@@ -0,0 +1,59 @@
+/*
+ Unix SMB/CIFS implementation.
+ ioctl and fsctl definitions
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+
+/* ioctl codes */
+#define IOCTL_QUERY_JOB_INFO 0x530060
+
+
+/* filesystem control codes */
+#define FSCTL_METHOD_BUFFERED 0x00000000
+#define FSCTL_METHOD_IN_DIRECT 0x00000001
+#define FSCTL_METHOD_OUT_DIRECT 0x00000002
+#define FSCTL_METHOD_NEITHER 0x00000003
+
+#define FSCTL_ACCESS_ANY 0x00000000
+#define FSCTL_ACCESS_READ 0x00004000
+#define FSCTL_ACCESS_WRITE 0x00008000
+
+#define FSCTL_FILESYSTEM 0x00090000
+#define FSCTL_REQUEST_OPLOCK_LEVEL_1 (FSCTL_FILESYSTEM | FSCTL_ACCESS_ANY | 0x0000 | FSCTL_METHOD_BUFFERED)
+#define FSCTL_REQUEST_OPLOCK_LEVEL_2 (FSCTL_FILESYSTEM | FSCTL_ACCESS_ANY | 0x0004 | FSCTL_METHOD_BUFFERED)
+#define FSCTL_REQUEST_BATCH_OPLOCK (FSCTL_FILESYSTEM | FSCTL_ACCESS_ANY | 0x0008 | FSCTL_METHOD_BUFFERED)
+#define FSCTL_OPLOCK_BREAK_ACKNOWLEDGE (FSCTL_FILESYSTEM | FSCTL_ACCESS_ANY | 0x000C | FSCTL_METHOD_BUFFERED)
+#define FSCTL_OPBATCH_ACK_CLOSE_PENDING (FSCTL_FILESYSTEM | FSCTL_ACCESS_ANY | 0x0010 | FSCTL_METHOD_BUFFERED)
+#define FSCTL_OPLOCK_BREAK_NOTIFY (FSCTL_FILESYSTEM | FSCTL_ACCESS_ANY | 0x0014 | FSCTL_METHOD_BUFFERED)
+#define FSCTL_FILESYS_GET_STATISTICS (FSCTL_FILESYSTEM | FSCTL_ACCESS_ANY | 0x0060 | FSCTL_METHOD_BUFFERED)
+#define FSCTL_GET_NTFS_VOLUME_DATA (FSCTL_FILESYSTEM | FSCTL_ACCESS_ANY | 0x0064 | FSCTL_METHOD_BUFFERED)
+#define FSCTL_FIND_FILES_BY_SID (FSCTL_FILESYSTEM | FSCTL_ACCESS_ANY | 0x008C | FSCTL_METHOD_NEITHER)
+#define FSCTL_SET_OBJECT_ID (FSCTL_FILESYSTEM | FSCTL_ACCESS_ANY | 0x0098 | FSCTL_METHOD_BUFFERED)
+#define FSCTL_GET_OBJECT_ID (FSCTL_FILESYSTEM | FSCTL_ACCESS_ANY | 0x009C | FSCTL_METHOD_BUFFERED)
+#define FSCTL_DELETE_OBJECT_ID (FSCTL_FILESYSTEM | FSCTL_ACCESS_ANY | 0x00A0 | FSCTL_METHOD_BUFFERED)
+#define FSCTL_SET_REPARSE_POINT (FSCTL_FILESYSTEM | FSCTL_ACCESS_ANY | 0x00A4 | FSCTL_METHOD_BUFFERED)
+#define FSCTL_GET_REPARSE_POINT (FSCTL_FILESYSTEM | FSCTL_ACCESS_ANY | 0x00A8 | FSCTL_METHOD_BUFFERED)
+#define FSCTL_DELETE_REPARSE_POINT (FSCTL_FILESYSTEM | FSCTL_ACCESS_ANY | 0x00AC | FSCTL_METHOD_BUFFERED)
+#define FSCTL_CREATE_OR_GET_OBJECT_ID (FSCTL_FILESYSTEM | FSCTL_ACCESS_ANY | 0x00C0 | FSCTL_METHOD_BUFFERED)
+#define FSCTL_SET_SPARSE (FSCTL_FILESYSTEM | FSCTL_ACCESS_ANY | 0x00C4 | FSCTL_METHOD_BUFFERED)
+
+#define FSCTL_NAMED_PIPE 0x00110000
+#define FSCTL_NAMED_PIPE_READ_WRITE (FSCTL_NAMED_PIPE | FSCTL_ACCESS_ANY | 0xC014 | FSCTL_METHOD_NEITHER)
+
+#define FSCTL_NETWORK_FILESYSTEM 0x00140000
+#define FSCTL_GET_SHADOW_COPY_DATA (FSCTL_NETWORK_FILESYSTEM | FSCTL_ACCESS_READ | 0x0064 | FSCTL_METHOD_BUFFERED)
diff --git a/source4/libcli/raw/libcliraw.h b/source4/libcli/raw/libcliraw.h
new file mode 100644
index 0000000000..a9fcdab9cc
--- /dev/null
+++ b/source4/libcli/raw/libcliraw.h
@@ -0,0 +1,384 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB parameters and setup
+
+ Copyright (C) Andrew Tridgell 2002-2004
+ Copyright (C) James Myers 2003 <myersjj@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __LIBCLI_RAW_H__
+#define __LIBCLI_RAW_H__
+
+#include "libcli/raw/request.h"
+#include "librpc/gen_ndr/nbt.h"
+
+struct smbcli_tree; /* forward declare */
+struct smbcli_request; /* forward declare */
+struct smbcli_session; /* forward declare */
+struct smbcli_transport; /* forward declare */
+
+struct resolve_context;
+struct cli_credentials;
+struct gensec_settings;
+
+/* default timeout for all smb requests */
+#define SMB_REQUEST_TIMEOUT 60
+
+/* context that will be and has been negotiated between the client and server */
+struct smbcli_negotiate {
+ /*
+ * negotiated maximum transmit size - this is given to us by the server
+ */
+ uint32_t max_xmit;
+
+ /* maximum number of requests that can be multiplexed */
+ uint16_t max_mux;
+
+ /* the negotiatiated protocol */
+ enum protocol_types protocol;
+
+ uint8_t sec_mode; /* security mode returned by negprot */
+ uint8_t key_len;
+ DATA_BLOB server_guid; /* server_guid */
+ DATA_BLOB secblob; /* cryptkey or negTokenInit blob */
+ uint32_t sesskey;
+
+ struct smb_signing_context sign_info;
+
+ /* capabilities that the server reported */
+ uint32_t capabilities;
+
+ int server_zone;
+ time_t server_time;
+ uint_t readbraw_supported:1;
+ uint_t writebraw_supported:1;
+
+ char *server_domain;
+};
+
+/* this is the context for a SMB socket associated with the socket itself */
+struct smbcli_socket {
+ struct socket_context *sock;
+
+ /* what port we ended up connected to */
+ int port;
+
+ /* the hostname we connected to */
+ const char *hostname;
+
+ /* the event handle for waiting for socket IO */
+ struct {
+ struct tevent_context *ctx;
+ struct tevent_fd *fde;
+ struct tevent_timer *te;
+ } event;
+};
+
+/*
+ this structure allows applications to control the behaviour of the
+ client library
+*/
+struct smbcli_options {
+ uint_t use_oplocks:1;
+ uint_t use_level2_oplocks:1;
+ uint_t use_spnego:1;
+ uint_t unicode:1;
+ uint_t ntstatus_support:1;
+ int max_protocol;
+ uint32_t max_xmit;
+ uint16_t max_mux;
+ int request_timeout;
+ enum smb_signing_state signing;
+};
+
+/* this is the context for the client transport layer */
+struct smbcli_transport {
+ /* socket level info */
+ struct smbcli_socket *socket;
+
+ /* the next mid to be allocated - needed for signing and
+ request matching */
+ uint16_t next_mid;
+
+ /* negotiated protocol information */
+ struct smbcli_negotiate negotiate;
+
+ /* options to control the behaviour of the client code */
+ struct smbcli_options options;
+
+ /* is a readbraw pending? we need to handle that case
+ specially on receiving packets */
+ uint_t readbraw_pending:1;
+
+ /* an idle function - if this is defined then it will be
+ called once every period microseconds while we are waiting
+ for a packet */
+ struct {
+ void (*func)(struct smbcli_transport *, void *);
+ void *private_data;
+ uint_t period;
+ } idle;
+
+ /* the error fields from the last message */
+ struct {
+ enum {ETYPE_NONE, ETYPE_SMB, ETYPE_SOCKET, ETYPE_NBT} etype;
+ union {
+ NTSTATUS nt_status;
+ enum {SOCKET_READ_TIMEOUT,
+ SOCKET_READ_EOF,
+ SOCKET_READ_ERROR,
+ SOCKET_WRITE_ERROR,
+ SOCKET_READ_BAD_SIG} socket_error;
+ uint_t nbt_error;
+ } e;
+ } error;
+
+ struct {
+ /* a oplock break request handler */
+ bool (*handler)(struct smbcli_transport *transport,
+ uint16_t tid, uint16_t fnum, uint8_t level, void *private_data);
+ /* private data passed to the oplock handler */
+ void *private_data;
+ } oplock;
+
+ /* a list of async requests that are pending for receive on this connection */
+ struct smbcli_request *pending_recv;
+
+ /* remember the called name - some sub-protocols require us to
+ know the server name */
+ struct nbt_name called;
+
+ /* context of the stream -> packet parser */
+ struct packet_context *packet;
+
+ /* iconv convenience */
+ struct smb_iconv_convenience *iconv_convenience;
+};
+
+/* this is the context for the user */
+
+/* this is the context for the session layer */
+struct smbcli_session {
+ /* transport layer info */
+ struct smbcli_transport *transport;
+
+ /* after a session setup the server provides us with
+ a vuid identifying the security context */
+ uint16_t vuid;
+
+ /* default pid for this session */
+ uint32_t pid;
+
+ /* the flags2 for each packet - this allows
+ the user to control these for torture testing */
+ uint16_t flags2;
+
+ DATA_BLOB user_session_key;
+
+ /* the spnego context if we use extented security */
+ struct gensec_security *gensec;
+
+ struct smbcli_session_options {
+ uint_t lanman_auth:1;
+ uint_t ntlmv2_auth:1;
+ uint_t plaintext_auth:1;
+ } options;
+};
+
+/*
+ smbcli_tree context: internal state for a tree connection.
+ */
+struct smbcli_tree {
+ /* session layer info */
+ struct smbcli_session *session;
+
+ uint16_t tid; /* tree id, aka cnum */
+ char *device;
+ char *fs_type;
+};
+
+
+/*
+ a client request moves between the following 4 states.
+*/
+enum smbcli_request_state {SMBCLI_REQUEST_INIT, /* we are creating the request */
+ SMBCLI_REQUEST_RECV, /* we are waiting for a matching reply */
+ SMBCLI_REQUEST_DONE, /* the request is finished */
+ SMBCLI_REQUEST_ERROR}; /* a packet or transport level error has occurred */
+
+/* the context for a single SMB request. This is passed to any request-context
+ * functions (similar to context.h, the server version).
+ * This will allow requests to be multi-threaded. */
+struct smbcli_request {
+ /* allow a request to be part of a list of requests */
+ struct smbcli_request *next, *prev;
+
+ /* each request is in one of 4 possible states */
+ enum smbcli_request_state state;
+
+ /* a request always has a transport context, nearly always has
+ a session context and usually has a tree context */
+ struct smbcli_transport *transport;
+ struct smbcli_session *session;
+ struct smbcli_tree *tree;
+
+ /* a receive helper, smbcli_transport_finish_recv will not call
+ req->async.fn callback handler unless the recv_helper returns
+ a value > SMBCLI_REQUEST_RECV. */
+ struct {
+ enum smbcli_request_state (*fn)(struct smbcli_request *);
+ void *private_data;
+ } recv_helper;
+
+ /* the flags2 from the SMB request, in raw form (host byte
+ order). Used to parse strings */
+ uint16_t flags2;
+
+ /* the NT status for this request. Set by packet receive code
+ or code detecting error. */
+ NTSTATUS status;
+
+ /* the sequence number of this packet - used for signing */
+ uint_t seq_num;
+
+ /* list of ntcancel request for this requests */
+ struct smbcli_request *ntcancel;
+
+ /* set if this is a one-way request, meaning we are not
+ expecting a reply from the server. */
+ uint_t one_way_request:1;
+
+ /* set this when the request should only increment the signing
+ counter by one */
+ uint_t sign_single_increment:1;
+
+ /* the caller wants to do the signing check */
+ bool sign_caller_checks;
+
+ /* give the caller a chance to prevent the talloc_free() in the _recv() function */
+ bool do_not_free;
+
+ /* the mid of this packet - used to match replies */
+ uint16_t mid;
+
+ struct smb_request_buffer in;
+ struct smb_request_buffer out;
+
+ /* information on what to do with a reply when it is received
+ asyncronously. If this is not setup when a reply is received then
+ the reply is discarded
+
+ The private pointer is private to the caller of the client
+ library (the application), not private to the library
+ */
+ struct {
+ void (*fn)(struct smbcli_request *);
+ void *private_data;
+ } async;
+};
+
+/* useful way of catching wct errors with file and line number */
+#define SMBCLI_CHECK_MIN_WCT(req, wcount) if ((req)->in.wct < (wcount)) { \
+ DEBUG(1,("Unexpected WCT %d at %s(%d) - expected min %d\n", (req)->in.wct, __FILE__, __LINE__, wcount)); \
+ req->status = NT_STATUS_INVALID_PARAMETER; \
+ goto failed; \
+}
+
+#define SMBCLI_CHECK_WCT(req, wcount) if ((req)->in.wct != (wcount)) { \
+ DEBUG(1,("Unexpected WCT %d at %s(%d) - expected %d\n", (req)->in.wct, __FILE__, __LINE__, wcount)); \
+ req->status = NT_STATUS_INVALID_PARAMETER; \
+ goto failed; \
+}
+
+#include "libcli/raw/interfaces.h"
+
+NTSTATUS smb_raw_read_recv(struct smbcli_request *req, union smb_read *parms);
+struct smbcli_request *smb_raw_read_send(struct smbcli_tree *tree, union smb_read *parms);
+NTSTATUS smb_raw_trans_recv(struct smbcli_request *req,
+ TALLOC_CTX *mem_ctx,
+ struct smb_trans2 *parms);
+size_t smb_raw_max_trans_data(struct smbcli_tree *tree, size_t param_size);
+struct smbcli_request *smb_raw_trans_send(struct smbcli_tree *tree, struct smb_trans2 *parms);
+NTSTATUS smbcli_request_destroy(struct smbcli_request *req);
+struct smbcli_request *smb_raw_write_send(struct smbcli_tree *tree, union smb_write *parms);
+struct smbcli_request *smb_raw_close_send(struct smbcli_tree *tree, union smb_close *parms);
+NTSTATUS smb_raw_open_recv(struct smbcli_request *req, TALLOC_CTX *mem_ctx, union smb_open *parms);
+struct smbcli_request *smb_raw_open_send(struct smbcli_tree *tree, union smb_open *parms);
+
+bool smbcli_transport_process(struct smbcli_transport *transport);
+const char *smbcli_errstr(struct smbcli_tree *tree);
+NTSTATUS smb_raw_fsinfo(struct smbcli_tree *tree, TALLOC_CTX *mem_ctx, union smb_fsinfo *fsinfo);
+NTSTATUS smb_raw_pathinfo(struct smbcli_tree *tree, TALLOC_CTX *mem_ctx, union smb_fileinfo *parms);
+NTSTATUS smb_raw_shadow_data(struct smbcli_tree *tree, TALLOC_CTX *mem_ctx, struct smb_shadow_copy *info);
+NTSTATUS smb_raw_fileinfo(struct smbcli_tree *tree, TALLOC_CTX *mem_ctx, union smb_fileinfo *parms);
+struct smbcli_tree *smbcli_tree_init(struct smbcli_session *session, TALLOC_CTX *parent_ctx, bool primary);
+NTSTATUS smb_raw_tcon(struct smbcli_tree *tree, TALLOC_CTX *mem_ctx, union smb_tcon *parms);
+void smbcli_oplock_handler(struct smbcli_transport *transport,
+ bool (*handler)(struct smbcli_transport *, uint16_t, uint16_t, uint8_t, void *),
+ void *private_data);
+void smbcli_transport_idle_handler(struct smbcli_transport *transport,
+ void (*idle_func)(struct smbcli_transport *, void *),
+ uint64_t period,
+ void *private_data);
+NTSTATUS smbcli_request_simple_recv(struct smbcli_request *req);
+bool smbcli_oplock_ack(struct smbcli_tree *tree, uint16_t fnum, uint16_t ack_level);
+NTSTATUS smb_raw_open(struct smbcli_tree *tree, TALLOC_CTX *mem_ctx, union smb_open *parms);
+NTSTATUS smb_raw_close(struct smbcli_tree *tree, union smb_close *parms);
+NTSTATUS smb_raw_unlink(struct smbcli_tree *tree, union smb_unlink *parms);
+NTSTATUS smb_raw_chkpath(struct smbcli_tree *tree, union smb_chkpath *parms);
+NTSTATUS smb_raw_mkdir(struct smbcli_tree *tree, union smb_mkdir *parms);
+NTSTATUS smb_raw_rmdir(struct smbcli_tree *tree, struct smb_rmdir *parms);
+NTSTATUS smb_raw_rename(struct smbcli_tree *tree, union smb_rename *parms);
+NTSTATUS smb_raw_seek(struct smbcli_tree *tree, union smb_seek *parms);
+NTSTATUS smb_raw_read(struct smbcli_tree *tree, union smb_read *parms);
+NTSTATUS smb_raw_write(struct smbcli_tree *tree, union smb_write *parms);
+NTSTATUS smb_raw_lock(struct smbcli_tree *tree, union smb_lock *parms);
+NTSTATUS smb_raw_setpathinfo(struct smbcli_tree *tree, union smb_setfileinfo *parms);
+NTSTATUS smb_raw_setfileinfo(struct smbcli_tree *tree, union smb_setfileinfo *parms);
+
+struct smbcli_request *smb_raw_changenotify_send(struct smbcli_tree *tree, union smb_notify *parms);
+NTSTATUS smb_raw_changenotify_recv(struct smbcli_request *req, TALLOC_CTX *mem_ctx, union smb_notify *parms);
+
+NTSTATUS smb_tree_disconnect(struct smbcli_tree *tree);
+NTSTATUS smbcli_nt_error(struct smbcli_tree *tree);
+NTSTATUS smb_raw_exit(struct smbcli_session *session);
+NTSTATUS smb_raw_pathinfo_recv(struct smbcli_request *req,
+ TALLOC_CTX *mem_ctx,
+ union smb_fileinfo *parms);
+struct smbcli_request *smb_raw_pathinfo_send(struct smbcli_tree *tree,
+ union smb_fileinfo *parms);
+struct smbcli_request *smb_raw_setpathinfo_send(struct smbcli_tree *tree,
+ union smb_setfileinfo *parms);
+struct smbcli_request *smb_raw_echo_send(struct smbcli_transport *transport,
+ struct smb_echo *p);
+NTSTATUS smb_raw_search_first(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ union smb_search_first *io, void *private_data,
+ smbcli_search_callback callback);
+NTSTATUS smb_raw_flush(struct smbcli_tree *tree, union smb_flush *parms);
+
+NTSTATUS smb_raw_trans(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ struct smb_trans2 *parms);
+
+struct smbcli_socket *smbcli_sock_connect_byname(const char *host, const char **ports,
+ TALLOC_CTX *mem_ctx,
+ struct resolve_context *resolve_ctx,
+ struct tevent_context *event_ctx,
+ const char *socket_options);
+void smbcli_sock_dead(struct smbcli_socket *sock);
+
+#endif /* __LIBCLI_RAW__H__ */
diff --git a/source4/libcli/raw/rawacl.c b/source4/libcli/raw/rawacl.c
new file mode 100644
index 0000000000..e13ba85361
--- /dev/null
+++ b/source4/libcli/raw/rawacl.c
@@ -0,0 +1,163 @@
+/*
+ Unix SMB/CIFS implementation.
+ ACL get/set operations
+
+ Copyright (C) Andrew Tridgell 2003-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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "librpc/gen_ndr/ndr_security.h"
+
+/****************************************************************************
+fetch file ACL (async send)
+****************************************************************************/
+struct smbcli_request *smb_raw_query_secdesc_send(struct smbcli_tree *tree,
+ union smb_fileinfo *io)
+{
+ struct smb_nttrans nt;
+ uint8_t params[8];
+
+ nt.in.max_setup = 0;
+ nt.in.max_param = 4;
+ nt.in.max_data = 0xFFFF;
+ nt.in.setup_count = 0;
+ nt.in.function = NT_TRANSACT_QUERY_SECURITY_DESC;
+ nt.in.setup = NULL;
+
+ SSVAL(params, 0, io->query_secdesc.in.file.fnum);
+ SSVAL(params, 2, 0); /* padding */
+ SIVAL(params, 4, io->query_secdesc.in.secinfo_flags);
+
+ nt.in.params.data = params;
+ nt.in.params.length = 8;
+
+ nt.in.data = data_blob(NULL, 0);
+
+ return smb_raw_nttrans_send(tree, &nt);
+}
+
+
+/****************************************************************************
+fetch file ACL (async recv)
+****************************************************************************/
+NTSTATUS smb_raw_query_secdesc_recv(struct smbcli_request *req,
+ TALLOC_CTX *mem_ctx,
+ union smb_fileinfo *io)
+{
+ NTSTATUS status;
+ struct smb_nttrans nt;
+ struct ndr_pull *ndr;
+ enum ndr_err_code ndr_err;
+
+ status = smb_raw_nttrans_recv(req, mem_ctx, &nt);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* check that the basics are valid */
+ if (nt.out.params.length != 4 ||
+ IVAL(nt.out.params.data, 0) > nt.out.data.length) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ nt.out.data.length = IVAL(nt.out.params.data, 0);
+
+ ndr = ndr_pull_init_blob(&nt.out.data, mem_ctx, NULL);
+ if (!ndr) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ io->query_secdesc.out.sd = talloc(mem_ctx, struct security_descriptor);
+ if (!io->query_secdesc.out.sd) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ ndr_err = ndr_pull_security_descriptor(ndr, NDR_SCALARS|NDR_BUFFERS,
+ io->query_secdesc.out.sd);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/****************************************************************************
+fetch file ACL (sync interface)
+****************************************************************************/
+NTSTATUS smb_raw_query_secdesc(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ union smb_fileinfo *io)
+{
+ struct smbcli_request *req = smb_raw_query_secdesc_send(tree, io);
+ return smb_raw_query_secdesc_recv(req, mem_ctx, io);
+}
+
+
+
+/****************************************************************************
+set file ACL (async send)
+****************************************************************************/
+struct smbcli_request *smb_raw_set_secdesc_send(struct smbcli_tree *tree,
+ union smb_setfileinfo *io)
+{
+ struct smb_nttrans nt;
+ uint8_t params[8];
+ struct ndr_push *ndr;
+ struct smbcli_request *req;
+ enum ndr_err_code ndr_err;
+
+ nt.in.max_setup = 0;
+ nt.in.max_param = 0;
+ nt.in.max_data = 0;
+ nt.in.setup_count = 0;
+ nt.in.function = NT_TRANSACT_SET_SECURITY_DESC;
+ nt.in.setup = NULL;
+
+ SSVAL(params, 0, io->set_secdesc.in.file.fnum);
+ SSVAL(params, 2, 0); /* padding */
+ SIVAL(params, 4, io->set_secdesc.in.secinfo_flags);
+
+ nt.in.params.data = params;
+ nt.in.params.length = 8;
+
+ ndr = ndr_push_init_ctx(NULL, NULL);
+ if (!ndr) return NULL;
+
+ ndr_err = ndr_push_security_descriptor(ndr, NDR_SCALARS|NDR_BUFFERS, io->set_secdesc.in.sd);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ talloc_free(ndr);
+ return NULL;
+ }
+
+ nt.in.data = ndr_push_blob(ndr);
+
+ req = smb_raw_nttrans_send(tree, &nt);
+
+ talloc_free(ndr);
+ return req;
+}
+
+/****************************************************************************
+set file ACL (sync interface)
+****************************************************************************/
+NTSTATUS smb_raw_set_secdesc(struct smbcli_tree *tree,
+ union smb_setfileinfo *io)
+{
+ struct smbcli_request *req = smb_raw_set_secdesc_send(tree, io);
+ return smbcli_request_simple_recv(req);
+}
diff --git a/source4/libcli/raw/rawdate.c b/source4/libcli/raw/rawdate.c
new file mode 100644
index 0000000000..5264653cc6
--- /dev/null
+++ b/source4/libcli/raw/rawdate.c
@@ -0,0 +1,82 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ raw date handling functions
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+
+/*******************************************************************
+put a dos date into a buffer (time/date format)
+This takes GMT time and puts local time for zone_offset in the buffer
+********************************************************************/
+void raw_push_dos_date(struct smbcli_transport *transport,
+ uint8_t *buf, int offset, time_t unixdate)
+{
+ push_dos_date(buf, offset, unixdate, transport->negotiate.server_zone);
+}
+
+/*******************************************************************
+put a dos date into a buffer (date/time format)
+This takes GMT time and puts local time in the buffer
+********************************************************************/
+void raw_push_dos_date2(struct smbcli_transport *transport,
+ uint8_t *buf, int offset, time_t unixdate)
+{
+ push_dos_date2(buf, offset, unixdate, transport->negotiate.server_zone);
+}
+
+/*******************************************************************
+put a dos 32 bit "unix like" date into a buffer. This routine takes
+GMT and converts it to LOCAL time in zone_offset before putting it
+********************************************************************/
+void raw_push_dos_date3(struct smbcli_transport *transport,
+ uint8_t *buf, int offset, time_t unixdate)
+{
+ push_dos_date3(buf, offset, unixdate, transport->negotiate.server_zone);
+}
+
+/*******************************************************************
+convert a dos date
+********************************************************************/
+time_t raw_pull_dos_date(struct smbcli_transport *transport,
+ const uint8_t *date_ptr)
+{
+ return pull_dos_date(date_ptr, transport->negotiate.server_zone);
+}
+
+/*******************************************************************
+like raw_pull_dos_date() but the words are reversed
+********************************************************************/
+time_t raw_pull_dos_date2(struct smbcli_transport *transport,
+ const uint8_t *date_ptr)
+{
+ return pull_dos_date2(date_ptr, transport->negotiate.server_zone);
+}
+
+/*******************************************************************
+ create a unix GMT date from a dos date in 32 bit "unix like" format
+ these arrive in server zone, with corresponding DST
+ ******************************************************************/
+time_t raw_pull_dos_date3(struct smbcli_transport *transport,
+ const uint8_t *date_ptr)
+{
+ return pull_dos_date3(date_ptr, transport->negotiate.server_zone);
+}
diff --git a/source4/libcli/raw/raweas.c b/source4/libcli/raw/raweas.c
new file mode 100644
index 0000000000..09fd4aa412
--- /dev/null
+++ b/source4/libcli/raw/raweas.c
@@ -0,0 +1,365 @@
+/*
+ Unix SMB/CIFS implementation.
+ parsing of EA (extended attribute) lists
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "smb.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+
+/*
+ work out how many bytes on the wire a ea list will consume.
+ This assumes the names are strict ascii, which should be a
+ reasonable assumption
+*/
+size_t ea_list_size(uint_t num_eas, struct ea_struct *eas)
+{
+ uint_t total = 4;
+ int i;
+ for (i=0;i<num_eas;i++) {
+ total += 4 + strlen(eas[i].name.s)+1 + eas[i].value.length;
+ }
+ return total;
+}
+
+/*
+ work out how many bytes on the wire a ea name list will consume.
+*/
+static uint_t ea_name_list_size(uint_t num_names, struct ea_name *eas)
+{
+ uint_t total = 4;
+ int i;
+ for (i=0;i<num_names;i++) {
+ total += 1 + strlen(eas[i].name.s) + 1;
+ }
+ return total;
+}
+
+/*
+ work out how many bytes on the wire a chained ea list will consume.
+ This assumes the names are strict ascii, which should be a
+ reasonable assumption
+*/
+size_t ea_list_size_chained(uint_t num_eas, struct ea_struct *eas, unsigned alignment)
+{
+ uint_t total = 0;
+ int i;
+ for (i=0;i<num_eas;i++) {
+ uint_t len = 8 + strlen(eas[i].name.s)+1 + eas[i].value.length;
+ len = (len + (alignment-1)) & ~(alignment-1);
+ total += len;
+ }
+ return total;
+}
+
+/*
+ put a ea_list into a pre-allocated buffer - buffer must be at least
+ of size ea_list_size()
+*/
+void ea_put_list(uint8_t *data, uint_t num_eas, struct ea_struct *eas)
+{
+ int i;
+ uint32_t ea_size;
+
+ ea_size = ea_list_size(num_eas, eas);
+
+ SIVAL(data, 0, ea_size);
+ data += 4;
+
+ for (i=0;i<num_eas;i++) {
+ uint_t nlen = strlen(eas[i].name.s);
+ SCVAL(data, 0, eas[i].flags);
+ SCVAL(data, 1, nlen);
+ SSVAL(data, 2, eas[i].value.length);
+ memcpy(data+4, eas[i].name.s, nlen+1);
+ memcpy(data+4+nlen+1, eas[i].value.data, eas[i].value.length);
+ data += 4+nlen+1+eas[i].value.length;
+ }
+}
+
+
+/*
+ put a chained ea_list into a pre-allocated buffer - buffer must be
+ at least of size ea_list_size()
+*/
+void ea_put_list_chained(uint8_t *data, uint_t num_eas, struct ea_struct *eas,
+ unsigned alignment)
+{
+ int i;
+
+ for (i=0;i<num_eas;i++) {
+ uint_t nlen = strlen(eas[i].name.s);
+ uint32_t len = 8+nlen+1+eas[i].value.length;
+ uint_t pad = ((len + (alignment-1)) & ~(alignment-1)) - len;
+ if (i == num_eas-1) {
+ SIVAL(data, 0, 0);
+ } else {
+ SIVAL(data, 0, len+pad);
+ }
+ SCVAL(data, 4, eas[i].flags);
+ SCVAL(data, 5, nlen);
+ SSVAL(data, 6, eas[i].value.length);
+ memcpy(data+8, eas[i].name.s, nlen+1);
+ memcpy(data+8+nlen+1, eas[i].value.data, eas[i].value.length);
+ memset(data+len, 0, pad);
+ data += len + pad;
+ }
+}
+
+
+/*
+ pull a ea_struct from a buffer. Return the number of bytes consumed
+*/
+uint_t ea_pull_struct(const DATA_BLOB *blob,
+ TALLOC_CTX *mem_ctx,
+ struct ea_struct *ea)
+{
+ uint8_t nlen;
+ uint16_t vlen;
+
+ ZERO_STRUCTP(ea);
+
+ if (blob->length < 6) {
+ return 0;
+ }
+
+ ea->flags = CVAL(blob->data, 0);
+ nlen = CVAL(blob->data, 1);
+ vlen = SVAL(blob->data, 2);
+
+ if (nlen+1+vlen > blob->length-4) {
+ return 0;
+ }
+
+ ea->name.s = talloc_strndup(mem_ctx, (const char *)(blob->data+4), nlen);
+ ea->name.private_length = nlen;
+ ea->value = data_blob_talloc(mem_ctx, NULL, vlen+1);
+ if (!ea->value.data) return 0;
+ if (vlen) {
+ memcpy(ea->value.data, blob->data+4+nlen+1, vlen);
+ }
+ ea->value.data[vlen] = 0;
+ ea->value.length--;
+
+ return 4 + nlen+1 + vlen;
+}
+
+
+/*
+ pull a ea_list from a buffer
+*/
+NTSTATUS ea_pull_list(const DATA_BLOB *blob,
+ TALLOC_CTX *mem_ctx,
+ uint_t *num_eas, struct ea_struct **eas)
+{
+ int n;
+ uint32_t ea_size, ofs;
+
+ if (blob->length < 4) {
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+
+ ea_size = IVAL(blob->data, 0);
+ if (ea_size > blob->length) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ ofs = 4;
+ n = 0;
+ *num_eas = 0;
+ *eas = NULL;
+
+ while (ofs < ea_size) {
+ uint_t len;
+ DATA_BLOB blob2;
+
+ blob2.data = blob->data + ofs;
+ blob2.length = ea_size - ofs;
+
+ *eas = talloc_realloc(mem_ctx, *eas, struct ea_struct, n+1);
+ if (! *eas) return NT_STATUS_NO_MEMORY;
+
+ len = ea_pull_struct(&blob2, mem_ctx, &(*eas)[n]);
+ if (len == 0) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ ofs += len;
+ n++;
+ }
+
+ *num_eas = n;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ pull a chained ea_list from a buffer
+*/
+NTSTATUS ea_pull_list_chained(const DATA_BLOB *blob,
+ TALLOC_CTX *mem_ctx,
+ uint_t *num_eas, struct ea_struct **eas)
+{
+ int n;
+ uint32_t ofs;
+
+ if (blob->length < 4) {
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+
+ ofs = 0;
+ n = 0;
+ *num_eas = 0;
+ *eas = NULL;
+
+ while (ofs < blob->length) {
+ uint_t len;
+ DATA_BLOB blob2;
+ uint32_t next_ofs = IVAL(blob->data, ofs);
+
+ blob2.data = blob->data + ofs + 4;
+ blob2.length = blob->length - (ofs + 4);
+
+ *eas = talloc_realloc(mem_ctx, *eas, struct ea_struct, n+1);
+ if (! *eas) return NT_STATUS_NO_MEMORY;
+
+ len = ea_pull_struct(&blob2, mem_ctx, &(*eas)[n]);
+ if (len == 0) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ ofs += next_ofs;
+
+ if (ofs+4 > blob->length) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ n++;
+ if (next_ofs == 0) break;
+ }
+
+ *num_eas = n;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ pull a ea_name from a buffer. Return the number of bytes consumed
+*/
+static uint_t ea_pull_name(const DATA_BLOB *blob,
+ TALLOC_CTX *mem_ctx,
+ struct ea_name *ea)
+{
+ uint8_t nlen;
+
+ if (blob->length < 2) {
+ return 0;
+ }
+
+ nlen = CVAL(blob->data, 0);
+
+ if (nlen+2 > blob->length) {
+ return 0;
+ }
+
+ ea->name.s = talloc_strndup(mem_ctx, (const char *)(blob->data+1), nlen);
+ ea->name.private_length = nlen;
+
+ return nlen+2;
+}
+
+
+/*
+ pull a ea_name list from a buffer
+*/
+NTSTATUS ea_pull_name_list(const DATA_BLOB *blob,
+ TALLOC_CTX *mem_ctx,
+ uint_t *num_names, struct ea_name **ea_names)
+{
+ int n;
+ uint32_t ea_size, ofs;
+
+ if (blob->length < 4) {
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+
+ ea_size = IVAL(blob->data, 0);
+ if (ea_size > blob->length) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ ofs = 4;
+ n = 0;
+ *num_names = 0;
+ *ea_names = NULL;
+
+ while (ofs < ea_size) {
+ uint_t len;
+ DATA_BLOB blob2;
+
+ blob2.data = blob->data + ofs;
+ blob2.length = ea_size - ofs;
+
+ *ea_names = talloc_realloc(mem_ctx, *ea_names, struct ea_name, n+1);
+ if (! *ea_names) return NT_STATUS_NO_MEMORY;
+
+ len = ea_pull_name(&blob2, mem_ctx, &(*ea_names)[n]);
+ if (len == 0) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ ofs += len;
+ n++;
+ }
+
+ *num_names = n;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ put a ea_name list into a data blob
+*/
+bool ea_push_name_list(TALLOC_CTX *mem_ctx,
+ DATA_BLOB *data, uint_t num_names, struct ea_name *eas)
+{
+ int i;
+ uint32_t ea_size;
+ uint32_t off;
+
+ ea_size = ea_name_list_size(num_names, eas);
+
+ *data = data_blob_talloc(mem_ctx, NULL, ea_size);
+ if (data->data == NULL) {
+ return false;
+ }
+
+ SIVAL(data->data, 0, ea_size);
+ off = 4;
+
+ for (i=0;i<num_names;i++) {
+ uint_t nlen = strlen(eas[i].name.s);
+ SCVAL(data->data, off, nlen);
+ memcpy(data->data+off+1, eas[i].name.s, nlen+1);
+ off += 1+nlen+1;
+ }
+
+ return true;
+}
diff --git a/source4/libcli/raw/rawfile.c b/source4/libcli/raw/rawfile.c
new file mode 100644
index 0000000000..63de051b33
--- /dev/null
+++ b/source4/libcli/raw/rawfile.c
@@ -0,0 +1,1017 @@
+/*
+ Unix SMB/CIFS implementation.
+ client file operations
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Jeremy Allison 2001-2002
+ Copyright (C) James 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "smb.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "librpc/gen_ndr/ndr_security.h"
+
+#define SETUP_REQUEST(cmd, wct, buflen) do { \
+ req = smbcli_request_setup(tree, cmd, wct, buflen); \
+ if (!req) return NULL; \
+} while (0)
+
+/**
+ Return a string representing a CIFS attribute for a file.
+**/
+char *attrib_string(TALLOC_CTX *mem_ctx, uint32_t attrib)
+{
+ int i, len;
+ const struct {
+ char c;
+ uint16_t attr;
+ } attr_strs[] = {
+ {'V', FILE_ATTRIBUTE_VOLUME},
+ {'D', FILE_ATTRIBUTE_DIRECTORY},
+ {'A', FILE_ATTRIBUTE_ARCHIVE},
+ {'H', FILE_ATTRIBUTE_HIDDEN},
+ {'S', FILE_ATTRIBUTE_SYSTEM},
+ {'N', FILE_ATTRIBUTE_NORMAL},
+ {'R', FILE_ATTRIBUTE_READONLY},
+ {'d', FILE_ATTRIBUTE_DEVICE},
+ {'t', FILE_ATTRIBUTE_TEMPORARY},
+ {'s', FILE_ATTRIBUTE_SPARSE},
+ {'r', FILE_ATTRIBUTE_REPARSE_POINT},
+ {'c', FILE_ATTRIBUTE_COMPRESSED},
+ {'o', FILE_ATTRIBUTE_OFFLINE},
+ {'n', FILE_ATTRIBUTE_NONINDEXED},
+ {'e', FILE_ATTRIBUTE_ENCRYPTED}
+ };
+ char *ret;
+
+ ret = talloc_array(mem_ctx, char, ARRAY_SIZE(attr_strs)+1);
+ if (!ret) {
+ return NULL;
+ }
+
+ for (len=i=0; i<ARRAY_SIZE(attr_strs); i++) {
+ if (attrib & attr_strs[i].attr) {
+ ret[len++] = attr_strs[i].c;
+ }
+ }
+
+ ret[len] = 0;
+
+ talloc_set_name_const(ret, ret);
+
+ return ret;
+}
+
+/****************************************************************************
+ Rename a file - async interface
+****************************************************************************/
+struct smbcli_request *smb_raw_rename_send(struct smbcli_tree *tree,
+ union smb_rename *parms)
+{
+ struct smbcli_request *req = NULL;
+ struct smb_nttrans nt;
+ TALLOC_CTX *mem_ctx;
+
+ switch (parms->generic.level) {
+ case RAW_RENAME_RENAME:
+ SETUP_REQUEST(SMBmv, 1, 0);
+ SSVAL(req->out.vwv, VWV(0), parms->rename.in.attrib);
+ smbcli_req_append_ascii4(req, parms->rename.in.pattern1, STR_TERMINATE);
+ smbcli_req_append_ascii4(req, parms->rename.in.pattern2, STR_TERMINATE);
+ break;
+
+ case RAW_RENAME_NTRENAME:
+ SETUP_REQUEST(SMBntrename, 4, 0);
+ SSVAL(req->out.vwv, VWV(0), parms->ntrename.in.attrib);
+ SSVAL(req->out.vwv, VWV(1), parms->ntrename.in.flags);
+ SIVAL(req->out.vwv, VWV(2), parms->ntrename.in.cluster_size);
+ smbcli_req_append_ascii4(req, parms->ntrename.in.old_name, STR_TERMINATE);
+ smbcli_req_append_ascii4(req, parms->ntrename.in.new_name, STR_TERMINATE);
+ break;
+
+ case RAW_RENAME_NTTRANS:
+
+ mem_ctx = talloc_new(tree);
+
+ nt.in.max_setup = 0;
+ nt.in.max_param = 0;
+ nt.in.max_data = 0;
+ nt.in.setup_count = 0;
+ nt.in.setup = NULL;
+ nt.in.function = NT_TRANSACT_RENAME;
+ nt.in.params = data_blob_talloc(mem_ctx, NULL, 4);
+ nt.in.data = data_blob(NULL, 0);
+
+ SSVAL(nt.in.params.data, VWV(0), parms->nttrans.in.file.fnum);
+ SSVAL(nt.in.params.data, VWV(1), parms->nttrans.in.flags);
+
+ smbcli_blob_append_string(tree->session, mem_ctx,
+ &nt.in.params, parms->nttrans.in.new_name,
+ STR_TERMINATE);
+
+ req = smb_raw_nttrans_send(tree, &nt);
+ talloc_free(mem_ctx);
+ return req;
+ }
+
+ if (!smbcli_request_send(req)) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+/****************************************************************************
+ Rename a file - sync interface
+****************************************************************************/
+_PUBLIC_ NTSTATUS smb_raw_rename(struct smbcli_tree *tree,
+ union smb_rename *parms)
+{
+ struct smbcli_request *req = smb_raw_rename_send(tree, parms);
+ return smbcli_request_simple_recv(req);
+}
+
+
+/****************************************************************************
+ Delete a file - async interface
+****************************************************************************/
+struct smbcli_request *smb_raw_unlink_send(struct smbcli_tree *tree,
+ union smb_unlink *parms)
+{
+ struct smbcli_request *req;
+
+ SETUP_REQUEST(SMBunlink, 1, 0);
+
+ SSVAL(req->out.vwv, VWV(0), parms->unlink.in.attrib);
+ smbcli_req_append_ascii4(req, parms->unlink.in.pattern, STR_TERMINATE);
+
+ if (!smbcli_request_send(req)) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+ return req;
+}
+
+/*
+ delete a file - sync interface
+*/
+_PUBLIC_ NTSTATUS smb_raw_unlink(struct smbcli_tree *tree,
+ union smb_unlink *parms)
+{
+ struct smbcli_request *req = smb_raw_unlink_send(tree, parms);
+ return smbcli_request_simple_recv(req);
+}
+
+
+/****************************************************************************
+ create a directory using TRANSACT2_MKDIR - async interface
+****************************************************************************/
+static struct smbcli_request *smb_raw_t2mkdir_send(struct smbcli_tree *tree,
+ union smb_mkdir *parms)
+{
+ struct smb_trans2 t2;
+ uint16_t setup = TRANSACT2_MKDIR;
+ TALLOC_CTX *mem_ctx;
+ struct smbcli_request *req;
+ uint16_t data_total;
+
+ mem_ctx = talloc_init("t2mkdir");
+
+ data_total = ea_list_size(parms->t2mkdir.in.num_eas, parms->t2mkdir.in.eas);
+
+ 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(mem_ctx, NULL, 4);
+ t2.in.data = data_blob_talloc(mem_ctx, NULL, data_total);
+
+ SIVAL(t2.in.params.data, VWV(0), 0); /* reserved */
+
+ smbcli_blob_append_string(tree->session, mem_ctx,
+ &t2.in.params, parms->t2mkdir.in.path, STR_TERMINATE);
+
+ ea_put_list(t2.in.data.data, parms->t2mkdir.in.num_eas, parms->t2mkdir.in.eas);
+
+ req = smb_raw_trans2_send(tree, &t2);
+
+ talloc_free(mem_ctx);
+
+ return req;
+}
+
+/****************************************************************************
+ Create a directory - async interface
+****************************************************************************/
+struct smbcli_request *smb_raw_mkdir_send(struct smbcli_tree *tree,
+ union smb_mkdir *parms)
+{
+ struct smbcli_request *req;
+
+ if (parms->generic.level == RAW_MKDIR_T2MKDIR) {
+ return smb_raw_t2mkdir_send(tree, parms);
+ }
+
+ if (parms->generic.level != RAW_MKDIR_MKDIR) {
+ return NULL;
+ }
+
+ SETUP_REQUEST(SMBmkdir, 0, 0);
+
+ smbcli_req_append_ascii4(req, parms->mkdir.in.path, STR_TERMINATE);
+
+ if (!smbcli_request_send(req)) {
+ return NULL;
+ }
+
+ return req;
+}
+
+/****************************************************************************
+ Create a directory - sync interface
+****************************************************************************/
+_PUBLIC_ NTSTATUS smb_raw_mkdir(struct smbcli_tree *tree,
+ union smb_mkdir *parms)
+{
+ struct smbcli_request *req = smb_raw_mkdir_send(tree, parms);
+ return smbcli_request_simple_recv(req);
+}
+
+/****************************************************************************
+ Remove a directory - async interface
+****************************************************************************/
+struct smbcli_request *smb_raw_rmdir_send(struct smbcli_tree *tree,
+ struct smb_rmdir *parms)
+{
+ struct smbcli_request *req;
+
+ SETUP_REQUEST(SMBrmdir, 0, 0);
+
+ smbcli_req_append_ascii4(req, parms->in.path, STR_TERMINATE);
+
+ if (!smbcli_request_send(req)) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+/****************************************************************************
+ Remove a directory - sync interface
+****************************************************************************/
+_PUBLIC_ NTSTATUS smb_raw_rmdir(struct smbcli_tree *tree,
+ struct smb_rmdir *parms)
+{
+ struct smbcli_request *req = smb_raw_rmdir_send(tree, parms);
+ return smbcli_request_simple_recv(req);
+}
+
+
+/*
+ Open a file using TRANSACT2_OPEN - async recv
+*/
+static NTSTATUS smb_raw_nttrans_create_recv(struct smbcli_request *req,
+ TALLOC_CTX *mem_ctx,
+ union smb_open *parms)
+{
+ NTSTATUS status;
+ struct smb_nttrans nt;
+ uint8_t *params;
+
+ status = smb_raw_nttrans_recv(req, mem_ctx, &nt);
+ if (!NT_STATUS_IS_OK(status)) return status;
+
+ if (nt.out.params.length < 69) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ params = nt.out.params.data;
+
+ parms->ntcreatex.out.oplock_level = CVAL(params, 0);
+ parms->ntcreatex.out.file.fnum = SVAL(params, 2);
+ parms->ntcreatex.out.create_action = IVAL(params, 4);
+ parms->ntcreatex.out.create_time = smbcli_pull_nttime(params, 12);
+ parms->ntcreatex.out.access_time = smbcli_pull_nttime(params, 20);
+ parms->ntcreatex.out.write_time = smbcli_pull_nttime(params, 28);
+ parms->ntcreatex.out.change_time = smbcli_pull_nttime(params, 36);
+ parms->ntcreatex.out.attrib = IVAL(params, 44);
+ parms->ntcreatex.out.alloc_size = BVAL(params, 48);
+ parms->ntcreatex.out.size = BVAL(params, 56);
+ parms->ntcreatex.out.file_type = SVAL(params, 64);
+ parms->ntcreatex.out.ipc_state = SVAL(params, 66);
+ parms->ntcreatex.out.is_directory = CVAL(params, 68);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ Open a file using NTTRANS CREATE - async send
+*/
+static struct smbcli_request *smb_raw_nttrans_create_send(struct smbcli_tree *tree,
+ union smb_open *parms)
+{
+ struct smb_nttrans nt;
+ uint8_t *params;
+ TALLOC_CTX *mem_ctx = talloc_new(tree);
+ uint16_t fname_len;
+ DATA_BLOB sd_blob, ea_blob;
+ struct smbcli_request *req;
+
+ nt.in.max_setup = 0;
+ nt.in.max_param = 101;
+ nt.in.max_data = 0;
+ nt.in.setup_count = 0;
+ nt.in.function = NT_TRANSACT_CREATE;
+ nt.in.setup = NULL;
+
+ sd_blob = data_blob(NULL, 0);
+ ea_blob = data_blob(NULL, 0);
+
+ if (parms->ntcreatex.in.sec_desc) {
+ enum ndr_err_code ndr_err;
+ ndr_err = ndr_push_struct_blob(&sd_blob, mem_ctx, NULL,
+ parms->ntcreatex.in.sec_desc,
+ (ndr_push_flags_fn_t)ndr_push_security_descriptor);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+ }
+
+ if (parms->ntcreatex.in.ea_list) {
+ uint32_t ea_size = ea_list_size_chained(parms->ntcreatex.in.ea_list->num_eas,
+ parms->ntcreatex.in.ea_list->eas, 4);
+ ea_blob = data_blob_talloc(mem_ctx, NULL, ea_size);
+ if (ea_blob.data == NULL) {
+ return NULL;
+ }
+ ea_put_list_chained(ea_blob.data,
+ parms->ntcreatex.in.ea_list->num_eas,
+ parms->ntcreatex.in.ea_list->eas, 4);
+ }
+
+ nt.in.params = data_blob_talloc(mem_ctx, NULL, 53);
+ if (nt.in.params.data == NULL) {
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ /* build the parameter section */
+ params = nt.in.params.data;
+
+ SIVAL(params, 0, parms->ntcreatex.in.flags);
+ SIVAL(params, 4, parms->ntcreatex.in.root_fid);
+ SIVAL(params, 8, parms->ntcreatex.in.access_mask);
+ SBVAL(params, 12, parms->ntcreatex.in.alloc_size);
+ SIVAL(params, 20, parms->ntcreatex.in.file_attr);
+ SIVAL(params, 24, parms->ntcreatex.in.share_access);
+ SIVAL(params, 28, parms->ntcreatex.in.open_disposition);
+ SIVAL(params, 32, parms->ntcreatex.in.create_options);
+ SIVAL(params, 36, sd_blob.length);
+ SIVAL(params, 40, ea_blob.length);
+ SIVAL(params, 48, parms->ntcreatex.in.impersonation);
+ SCVAL(params, 52, parms->ntcreatex.in.security_flags);
+
+ /* the empty string first forces the correct alignment */
+ smbcli_blob_append_string(tree->session, mem_ctx, &nt.in.params,"", 0);
+ fname_len = smbcli_blob_append_string(tree->session, mem_ctx, &nt.in.params,
+ parms->ntcreatex.in.fname, STR_TERMINATE);
+
+ SIVAL(nt.in.params.data, 44, fname_len);
+
+ /* build the data section */
+ nt.in.data = data_blob_talloc(mem_ctx, NULL, sd_blob.length + ea_blob.length);
+ memcpy(nt.in.data.data, sd_blob.data, sd_blob.length);
+ memcpy(nt.in.data.data+sd_blob.length, ea_blob.data, ea_blob.length);
+
+ /* send the request on its way */
+ req = smb_raw_nttrans_send(tree, &nt);
+
+ talloc_free(mem_ctx);
+
+ return req;
+}
+
+
+/****************************************************************************
+ Open a file using TRANSACT2_OPEN - async send
+****************************************************************************/
+static struct smbcli_request *smb_raw_t2open_send(struct smbcli_tree *tree,
+ union smb_open *parms)
+{
+ struct smb_trans2 t2;
+ uint16_t setup = TRANSACT2_OPEN;
+ TALLOC_CTX *mem_ctx = talloc_init("smb_raw_t2open");
+ struct smbcli_request *req;
+ uint16_t list_size;
+
+ list_size = ea_list_size(parms->t2open.in.num_eas, parms->t2open.in.eas);
+
+ t2.in.max_param = 30;
+ 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(mem_ctx, NULL, 28);
+ t2.in.data = data_blob_talloc(mem_ctx, NULL, list_size);
+
+ SSVAL(t2.in.params.data, VWV(0), parms->t2open.in.flags);
+ SSVAL(t2.in.params.data, VWV(1), parms->t2open.in.open_mode);
+ SSVAL(t2.in.params.data, VWV(2), parms->t2open.in.search_attrs);
+ SSVAL(t2.in.params.data, VWV(3), parms->t2open.in.file_attrs);
+ raw_push_dos_date(tree->session->transport,
+ t2.in.params.data, VWV(4), parms->t2open.in.write_time);
+ SSVAL(t2.in.params.data, VWV(6), parms->t2open.in.open_func);
+ SIVAL(t2.in.params.data, VWV(7), parms->t2open.in.size);
+ SIVAL(t2.in.params.data, VWV(9), parms->t2open.in.timeout);
+ SIVAL(t2.in.params.data, VWV(11), 0);
+ SSVAL(t2.in.params.data, VWV(13), 0);
+
+ smbcli_blob_append_string(tree->session, mem_ctx,
+ &t2.in.params, parms->t2open.in.fname,
+ STR_TERMINATE);
+
+ ea_put_list(t2.in.data.data, parms->t2open.in.num_eas, parms->t2open.in.eas);
+
+ req = smb_raw_trans2_send(tree, &t2);
+
+ talloc_free(mem_ctx);
+
+ return req;
+}
+
+
+/****************************************************************************
+ Open a file using TRANSACT2_OPEN - async recv
+****************************************************************************/
+static NTSTATUS smb_raw_t2open_recv(struct smbcli_request *req, TALLOC_CTX *mem_ctx, union smb_open *parms)
+{
+ struct smbcli_transport *transport = req->transport;
+ struct smb_trans2 t2;
+ NTSTATUS status;
+
+ status = smb_raw_trans2_recv(req, mem_ctx, &t2);
+ if (!NT_STATUS_IS_OK(status)) return status;
+
+ if (t2.out.params.length < 30) {
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+
+ parms->t2open.out.file.fnum = SVAL(t2.out.params.data, VWV(0));
+ parms->t2open.out.attrib = SVAL(t2.out.params.data, VWV(1));
+ parms->t2open.out.write_time = raw_pull_dos_date3(transport, t2.out.params.data + VWV(2));
+ parms->t2open.out.size = IVAL(t2.out.params.data, VWV(4));
+ parms->t2open.out.access = SVAL(t2.out.params.data, VWV(6));
+ parms->t2open.out.ftype = SVAL(t2.out.params.data, VWV(7));
+ parms->t2open.out.devstate = SVAL(t2.out.params.data, VWV(8));
+ parms->t2open.out.action = SVAL(t2.out.params.data, VWV(9));
+ parms->t2open.out.file_id = SVAL(t2.out.params.data, VWV(10));
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Open a file - async send
+****************************************************************************/
+_PUBLIC_ struct smbcli_request *smb_raw_open_send(struct smbcli_tree *tree, union smb_open *parms)
+{
+ int len;
+ struct smbcli_request *req = NULL;
+ bool bigoffset = false;
+
+ switch (parms->generic.level) {
+ case RAW_OPEN_T2OPEN:
+ return smb_raw_t2open_send(tree, parms);
+
+ case RAW_OPEN_OPEN:
+ SETUP_REQUEST(SMBopen, 2, 0);
+ SSVAL(req->out.vwv, VWV(0), parms->openold.in.open_mode);
+ SSVAL(req->out.vwv, VWV(1), parms->openold.in.search_attrs);
+ smbcli_req_append_ascii4(req, parms->openold.in.fname, STR_TERMINATE);
+ break;
+
+ case RAW_OPEN_OPENX:
+ 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_string(req, parms->openx.in.fname, STR_TERMINATE);
+ break;
+
+ case RAW_OPEN_MKNEW:
+ SETUP_REQUEST(SMBmknew, 3, 0);
+ SSVAL(req->out.vwv, VWV(0), parms->mknew.in.attrib);
+ raw_push_dos_date3(tree->session->transport,
+ req->out.vwv, VWV(1), parms->mknew.in.write_time);
+ smbcli_req_append_ascii4(req, parms->mknew.in.fname, STR_TERMINATE);
+ break;
+
+ case RAW_OPEN_CREATE:
+ SETUP_REQUEST(SMBcreate, 3, 0);
+ SSVAL(req->out.vwv, VWV(0), parms->create.in.attrib);
+ raw_push_dos_date3(tree->session->transport,
+ req->out.vwv, VWV(1), parms->create.in.write_time);
+ smbcli_req_append_ascii4(req, parms->create.in.fname, STR_TERMINATE);
+ break;
+
+ case RAW_OPEN_CTEMP:
+ SETUP_REQUEST(SMBctemp, 3, 0);
+ SSVAL(req->out.vwv, VWV(0), parms->ctemp.in.attrib);
+ raw_push_dos_date3(tree->session->transport,
+ req->out.vwv, VWV(1), parms->ctemp.in.write_time);
+ smbcli_req_append_ascii4(req, parms->ctemp.in.directory, STR_TERMINATE);
+ break;
+
+ case RAW_OPEN_SPLOPEN:
+ SETUP_REQUEST(SMBsplopen, 2, 0);
+ SSVAL(req->out.vwv, VWV(0), parms->splopen.in.setup_length);
+ SSVAL(req->out.vwv, VWV(1), parms->splopen.in.mode);
+ break;
+
+ case RAW_OPEN_NTCREATEX:
+ SETUP_REQUEST(SMBntcreateX, 24, 0);
+ SSVAL(req->out.vwv, VWV(0),SMB_CHAIN_NONE);
+ SSVAL(req->out.vwv, VWV(1),0);
+ SCVAL(req->out.vwv, VWV(2),0); /* padding */
+ SIVAL(req->out.vwv, 7, parms->ntcreatex.in.flags);
+ SIVAL(req->out.vwv, 11, parms->ntcreatex.in.root_fid);
+ SIVAL(req->out.vwv, 15, parms->ntcreatex.in.access_mask);
+ SBVAL(req->out.vwv, 19, parms->ntcreatex.in.alloc_size);
+ SIVAL(req->out.vwv, 27, parms->ntcreatex.in.file_attr);
+ SIVAL(req->out.vwv, 31, parms->ntcreatex.in.share_access);
+ SIVAL(req->out.vwv, 35, parms->ntcreatex.in.open_disposition);
+ SIVAL(req->out.vwv, 39, parms->ntcreatex.in.create_options);
+ SIVAL(req->out.vwv, 43, parms->ntcreatex.in.impersonation);
+ SCVAL(req->out.vwv, 47, parms->ntcreatex.in.security_flags);
+
+ smbcli_req_append_string_len(req, parms->ntcreatex.in.fname, STR_TERMINATE, &len);
+ SSVAL(req->out.vwv, 5, len);
+ break;
+
+ case RAW_OPEN_NTTRANS_CREATE:
+ return smb_raw_nttrans_create_send(tree, parms);
+
+
+ case RAW_OPEN_OPENX_READX:
+ 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->openxreadx.in.flags);
+ SSVAL(req->out.vwv, VWV(3), parms->openxreadx.in.open_mode);
+ SSVAL(req->out.vwv, VWV(4), parms->openxreadx.in.search_attrs);
+ SSVAL(req->out.vwv, VWV(5), parms->openxreadx.in.file_attrs);
+ raw_push_dos_date3(tree->session->transport,
+ req->out.vwv, VWV(6), parms->openxreadx.in.write_time);
+ SSVAL(req->out.vwv, VWV(8), parms->openxreadx.in.open_func);
+ SIVAL(req->out.vwv, VWV(9), parms->openxreadx.in.size);
+ SIVAL(req->out.vwv, VWV(11),parms->openxreadx.in.timeout);
+ SIVAL(req->out.vwv, VWV(13),0);
+ smbcli_req_append_string(req, parms->openxreadx.in.fname, STR_TERMINATE);
+
+ if (tree->session->transport->negotiate.capabilities & CAP_LARGE_FILES) {
+ bigoffset = true;
+ }
+
+ smbcli_chained_request_setup(req, SMBreadX, bigoffset ? 12 : 10, 0);
+
+ SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+ SSVAL(req->out.vwv, VWV(1), 0);
+ SSVAL(req->out.vwv, VWV(2), 0);
+ SIVAL(req->out.vwv, VWV(3), parms->openxreadx.in.offset);
+ SSVAL(req->out.vwv, VWV(5), parms->openxreadx.in.maxcnt & 0xFFFF);
+ SSVAL(req->out.vwv, VWV(6), parms->openxreadx.in.mincnt);
+ SIVAL(req->out.vwv, VWV(7), parms->openxreadx.in.maxcnt >> 16);
+ SSVAL(req->out.vwv, VWV(9), parms->openxreadx.in.remaining);
+ if (bigoffset) {
+ SIVAL(req->out.vwv, VWV(10),parms->openxreadx.in.offset>>32);
+ }
+ break;
+ case RAW_OPEN_SMB2:
+ return NULL;
+ }
+
+ if (!smbcli_request_send(req)) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+/****************************************************************************
+ Open a file - async recv
+****************************************************************************/
+_PUBLIC_ NTSTATUS smb_raw_open_recv(struct smbcli_request *req, TALLOC_CTX *mem_ctx, union smb_open *parms)
+{
+ NTSTATUS status;
+
+ if (!smbcli_request_receive(req) ||
+ smbcli_request_is_error(req)) {
+ goto failed;
+ }
+
+ switch (parms->openold.level) {
+ case RAW_OPEN_T2OPEN:
+ return smb_raw_t2open_recv(req, mem_ctx, parms);
+
+ case RAW_OPEN_OPEN:
+ SMBCLI_CHECK_WCT(req, 7);
+ parms->openold.out.file.fnum = SVAL(req->in.vwv, VWV(0));
+ parms->openold.out.attrib = SVAL(req->in.vwv, VWV(1));
+ parms->openold.out.write_time = raw_pull_dos_date3(req->transport,
+ req->in.vwv + VWV(2));
+ parms->openold.out.size = IVAL(req->in.vwv, VWV(4));
+ parms->openold.out.rmode = SVAL(req->in.vwv, VWV(6));
+ break;
+
+ case RAW_OPEN_OPENX:
+ SMBCLI_CHECK_MIN_WCT(req, 15);
+ parms->openx.out.file.fnum = SVAL(req->in.vwv, VWV(2));
+ parms->openx.out.attrib = SVAL(req->in.vwv, VWV(3));
+ parms->openx.out.write_time = raw_pull_dos_date3(req->transport,
+ req->in.vwv + VWV(4));
+ parms->openx.out.size = IVAL(req->in.vwv, VWV(6));
+ parms->openx.out.access = SVAL(req->in.vwv, VWV(8));
+ parms->openx.out.ftype = SVAL(req->in.vwv, VWV(9));
+ parms->openx.out.devstate = SVAL(req->in.vwv, VWV(10));
+ parms->openx.out.action = SVAL(req->in.vwv, VWV(11));
+ parms->openx.out.unique_fid = IVAL(req->in.vwv, VWV(12));
+ if (req->in.wct >= 19) {
+ parms->openx.out.access_mask = IVAL(req->in.vwv, VWV(15));
+ parms->openx.out.unknown = IVAL(req->in.vwv, VWV(17));
+ } else {
+ parms->openx.out.access_mask = 0;
+ parms->openx.out.unknown = 0;
+ }
+ break;
+
+ case RAW_OPEN_MKNEW:
+ SMBCLI_CHECK_WCT(req, 1);
+ parms->mknew.out.file.fnum = SVAL(req->in.vwv, VWV(0));
+ break;
+
+ case RAW_OPEN_CREATE:
+ SMBCLI_CHECK_WCT(req, 1);
+ parms->create.out.file.fnum = SVAL(req->in.vwv, VWV(0));
+ break;
+
+ case RAW_OPEN_CTEMP:
+ SMBCLI_CHECK_WCT(req, 1);
+ parms->ctemp.out.file.fnum = SVAL(req->in.vwv, VWV(0));
+ smbcli_req_pull_string(&req->in.bufinfo, mem_ctx, &parms->ctemp.out.name, req->in.data, -1, STR_TERMINATE | STR_ASCII);
+ break;
+
+ case RAW_OPEN_SPLOPEN:
+ SMBCLI_CHECK_WCT(req, 1);
+ parms->splopen.out.file.fnum = SVAL(req->in.vwv, VWV(0));
+ break;
+
+ case RAW_OPEN_NTCREATEX:
+ SMBCLI_CHECK_MIN_WCT(req, 34);
+ parms->ntcreatex.out.oplock_level = CVAL(req->in.vwv, 4);
+ parms->ntcreatex.out.file.fnum = SVAL(req->in.vwv, 5);
+ parms->ntcreatex.out.create_action = IVAL(req->in.vwv, 7);
+ parms->ntcreatex.out.create_time = smbcli_pull_nttime(req->in.vwv, 11);
+ parms->ntcreatex.out.access_time = smbcli_pull_nttime(req->in.vwv, 19);
+ parms->ntcreatex.out.write_time = smbcli_pull_nttime(req->in.vwv, 27);
+ parms->ntcreatex.out.change_time = smbcli_pull_nttime(req->in.vwv, 35);
+ parms->ntcreatex.out.attrib = IVAL(req->in.vwv, 43);
+ parms->ntcreatex.out.alloc_size = BVAL(req->in.vwv, 47);
+ parms->ntcreatex.out.size = BVAL(req->in.vwv, 55);
+ parms->ntcreatex.out.file_type = SVAL(req->in.vwv, 63);
+ parms->ntcreatex.out.ipc_state = SVAL(req->in.vwv, 65);
+ parms->ntcreatex.out.is_directory = CVAL(req->in.vwv, 67);
+ break;
+
+ case RAW_OPEN_NTTRANS_CREATE:
+ return smb_raw_nttrans_create_recv(req, mem_ctx, parms);
+
+ case RAW_OPEN_OPENX_READX:
+ SMBCLI_CHECK_MIN_WCT(req, 15);
+ parms->openxreadx.out.file.fnum = SVAL(req->in.vwv, VWV(2));
+ parms->openxreadx.out.attrib = SVAL(req->in.vwv, VWV(3));
+ parms->openxreadx.out.write_time = raw_pull_dos_date3(req->transport,
+ req->in.vwv + VWV(4));
+ parms->openxreadx.out.size = IVAL(req->in.vwv, VWV(6));
+ parms->openxreadx.out.access = SVAL(req->in.vwv, VWV(8));
+ parms->openxreadx.out.ftype = SVAL(req->in.vwv, VWV(9));
+ parms->openxreadx.out.devstate = SVAL(req->in.vwv, VWV(10));
+ parms->openxreadx.out.action = SVAL(req->in.vwv, VWV(11));
+ parms->openxreadx.out.unique_fid = IVAL(req->in.vwv, VWV(12));
+ if (req->in.wct >= 19) {
+ parms->openxreadx.out.access_mask = IVAL(req->in.vwv, VWV(15));
+ parms->openxreadx.out.unknown = IVAL(req->in.vwv, VWV(17));
+ } else {
+ parms->openxreadx.out.access_mask = 0;
+ parms->openxreadx.out.unknown = 0;
+ }
+
+ status = smbcli_chained_advance(req);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ SMBCLI_CHECK_WCT(req, 12);
+ parms->openxreadx.out.remaining = SVAL(req->in.vwv, VWV(2));
+ parms->openxreadx.out.compaction_mode = SVAL(req->in.vwv, VWV(3));
+ parms->openxreadx.out.nread = SVAL(req->in.vwv, VWV(5));
+ if (parms->openxreadx.out.nread >
+ MAX(parms->openxreadx.in.mincnt, parms->openxreadx.in.maxcnt) ||
+ !smbcli_raw_pull_data(&req->in.bufinfo, req->in.hdr + SVAL(req->in.vwv, VWV(6)),
+ parms->openxreadx.out.nread,
+ parms->openxreadx.out.data)) {
+ req->status = NT_STATUS_BUFFER_TOO_SMALL;
+ }
+ break;
+ case RAW_OPEN_SMB2:
+ req->status = NT_STATUS_INTERNAL_ERROR;
+ break;
+ }
+
+failed:
+ return smbcli_request_destroy(req);
+}
+
+
+/****************************************************************************
+ Open a file - sync interface
+****************************************************************************/
+_PUBLIC_ NTSTATUS smb_raw_open(struct smbcli_tree *tree, TALLOC_CTX *mem_ctx, union smb_open *parms)
+{
+ struct smbcli_request *req = smb_raw_open_send(tree, parms);
+ return smb_raw_open_recv(req, mem_ctx, parms);
+}
+
+
+/****************************************************************************
+ Close a file - async send
+****************************************************************************/
+_PUBLIC_ struct smbcli_request *smb_raw_close_send(struct smbcli_tree *tree, union smb_close *parms)
+{
+ struct smbcli_request *req = NULL;
+
+ switch (parms->generic.level) {
+ case RAW_CLOSE_CLOSE:
+ SETUP_REQUEST(SMBclose, 3, 0);
+ SSVAL(req->out.vwv, VWV(0), parms->close.in.file.fnum);
+ raw_push_dos_date3(tree->session->transport,
+ req->out.vwv, VWV(1), parms->close.in.write_time);
+ break;
+
+ case RAW_CLOSE_SPLCLOSE:
+ SETUP_REQUEST(SMBsplclose, 3, 0);
+ SSVAL(req->out.vwv, VWV(0), parms->splclose.in.file.fnum);
+ SIVAL(req->out.vwv, VWV(1), 0); /* reserved */
+ break;
+
+ case RAW_CLOSE_SMB2:
+ return NULL;
+ }
+
+ if (!req) return NULL;
+
+ if (!smbcli_request_send(req)) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+
+/****************************************************************************
+ Close a file - sync interface
+****************************************************************************/
+_PUBLIC_ NTSTATUS smb_raw_close(struct smbcli_tree *tree, union smb_close *parms)
+{
+ struct smbcli_request *req = smb_raw_close_send(tree, parms);
+ return smbcli_request_simple_recv(req);
+}
+
+
+/****************************************************************************
+ Locking calls - async interface
+****************************************************************************/
+struct smbcli_request *smb_raw_lock_send(struct smbcli_tree *tree, union smb_lock *parms)
+{
+ struct smbcli_request *req = NULL;
+
+ switch (parms->generic.level) {
+ case RAW_LOCK_LOCK:
+ SETUP_REQUEST(SMBlock, 5, 0);
+ SSVAL(req->out.vwv, VWV(0), parms->lock.in.file.fnum);
+ SIVAL(req->out.vwv, VWV(1), parms->lock.in.count);
+ SIVAL(req->out.vwv, VWV(3), parms->lock.in.offset);
+ break;
+
+ case RAW_LOCK_UNLOCK:
+ SETUP_REQUEST(SMBunlock, 5, 0);
+ SSVAL(req->out.vwv, VWV(0), parms->unlock.in.file.fnum);
+ SIVAL(req->out.vwv, VWV(1), parms->unlock.in.count);
+ SIVAL(req->out.vwv, VWV(3), parms->unlock.in.offset);
+ break;
+
+ case RAW_LOCK_LOCKX: {
+ struct smb_lock_entry *lockp;
+ uint_t lck_size = (parms->lockx.in.mode & LOCKING_ANDX_LARGE_FILES)? 20 : 10;
+ uint_t lock_count = parms->lockx.in.ulock_cnt + parms->lockx.in.lock_cnt;
+ int i;
+
+ SETUP_REQUEST(SMBlockingX, 8, lck_size * lock_count);
+ SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+ SSVAL(req->out.vwv, VWV(1), 0);
+ SSVAL(req->out.vwv, VWV(2), parms->lockx.in.file.fnum);
+ SSVAL(req->out.vwv, VWV(3), parms->lockx.in.mode);
+ SIVAL(req->out.vwv, VWV(4), parms->lockx.in.timeout);
+ SSVAL(req->out.vwv, VWV(6), parms->lockx.in.ulock_cnt);
+ SSVAL(req->out.vwv, VWV(7), parms->lockx.in.lock_cnt);
+
+ /* copy in all the locks */
+ lockp = &parms->lockx.in.locks[0];
+ for (i = 0; i < lock_count; i++) {
+ uint8_t *p = req->out.data + lck_size * i;
+ SSVAL(p, 0, lockp[i].pid);
+ if (parms->lockx.in.mode & LOCKING_ANDX_LARGE_FILES) {
+ SSVAL(p, 2, 0); /* reserved */
+ SIVAL(p, 4, lockp[i].offset>>32);
+ SIVAL(p, 8, lockp[i].offset);
+ SIVAL(p, 12, lockp[i].count>>32);
+ SIVAL(p, 16, lockp[i].count);
+ } else {
+ SIVAL(p, 2, lockp[i].offset);
+ SIVAL(p, 6, lockp[i].count);
+ }
+ }
+ break;
+ }
+ case RAW_LOCK_SMB2:
+ return NULL;
+ }
+
+ if (!smbcli_request_send(req)) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+/****************************************************************************
+ Locking calls - sync interface
+****************************************************************************/
+_PUBLIC_ NTSTATUS smb_raw_lock(struct smbcli_tree *tree, union smb_lock *parms)
+{
+ struct smbcli_request *req = smb_raw_lock_send(tree, parms);
+ return smbcli_request_simple_recv(req);
+}
+
+
+/****************************************************************************
+ Check for existence of a dir - async send
+****************************************************************************/
+struct smbcli_request *smb_raw_chkpath_send(struct smbcli_tree *tree, union smb_chkpath *parms)
+{
+ struct smbcli_request *req;
+
+ SETUP_REQUEST(SMBchkpth, 0, 0);
+
+ smbcli_req_append_ascii4(req, parms->chkpath.in.path, STR_TERMINATE);
+
+ if (!smbcli_request_send(req)) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+/****************************************************************************
+ Check for existence of a dir - sync interface
+****************************************************************************/
+NTSTATUS smb_raw_chkpath(struct smbcli_tree *tree, union smb_chkpath *parms)
+{
+ struct smbcli_request *req = smb_raw_chkpath_send(tree, parms);
+ return smbcli_request_simple_recv(req);
+}
+
+/****************************************************************************
+ flush a file - async send
+ a flush with RAW_FLUSH_ALL will flush all files
+****************************************************************************/
+struct smbcli_request *smb_raw_flush_send(struct smbcli_tree *tree, union smb_flush *parms)
+{
+ struct smbcli_request *req;
+ uint16_t fnum=0;
+
+ switch (parms->generic.level) {
+ case RAW_FLUSH_FLUSH:
+ fnum = parms->flush.in.file.fnum;
+ break;
+ case RAW_FLUSH_ALL:
+ fnum = 0xFFFF;
+ break;
+ case RAW_FLUSH_SMB2:
+ return NULL;
+ }
+
+ SETUP_REQUEST(SMBflush, 1, 0);
+ SSVAL(req->out.vwv, VWV(0), fnum);
+
+ if (!smbcli_request_send(req)) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+
+/****************************************************************************
+ flush a file - sync interface
+****************************************************************************/
+_PUBLIC_ NTSTATUS smb_raw_flush(struct smbcli_tree *tree, union smb_flush *parms)
+{
+ struct smbcli_request *req = smb_raw_flush_send(tree, parms);
+ return smbcli_request_simple_recv(req);
+}
+
+
+/****************************************************************************
+ seek a file - async send
+****************************************************************************/
+struct smbcli_request *smb_raw_seek_send(struct smbcli_tree *tree,
+ union smb_seek *parms)
+{
+ struct smbcli_request *req;
+
+ SETUP_REQUEST(SMBlseek, 4, 0);
+
+ SSVAL(req->out.vwv, VWV(0), parms->lseek.in.file.fnum);
+ SSVAL(req->out.vwv, VWV(1), parms->lseek.in.mode);
+ SIVALS(req->out.vwv, VWV(2), parms->lseek.in.offset);
+
+ if (!smbcli_request_send(req)) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+ return req;
+}
+
+/****************************************************************************
+ seek a file - async receive
+****************************************************************************/
+NTSTATUS smb_raw_seek_recv(struct smbcli_request *req,
+ union smb_seek *parms)
+{
+ if (!smbcli_request_receive(req) ||
+ smbcli_request_is_error(req)) {
+ return smbcli_request_destroy(req);
+ }
+
+ SMBCLI_CHECK_WCT(req, 2);
+ parms->lseek.out.offset = IVAL(req->in.vwv, VWV(0));
+
+failed:
+ return smbcli_request_destroy(req);
+}
+
+/*
+ seek a file - sync interface
+*/
+_PUBLIC_ NTSTATUS smb_raw_seek(struct smbcli_tree *tree,
+ union smb_seek *parms)
+{
+ struct smbcli_request *req = smb_raw_seek_send(tree, parms);
+ return smb_raw_seek_recv(req, parms);
+}
diff --git a/source4/libcli/raw/rawfileinfo.c b/source4/libcli/raw/rawfileinfo.c
new file mode 100644
index 0000000000..09ecb40002
--- /dev/null
+++ b/source4/libcli/raw/rawfileinfo.c
@@ -0,0 +1,776 @@
+/*
+ Unix SMB/CIFS implementation.
+ client trans2 operations
+ Copyright (C) James Myers 2003
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) James Peach 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "librpc/gen_ndr/ndr_security.h"
+
+/* local macros to make the code more readable */
+#define FINFO_CHECK_MIN_SIZE(size) if (blob->length < (size)) { \
+ DEBUG(1,("Unexpected FILEINFO reply size %d for level %u - expected min of %d\n", \
+ (int)blob->length, parms->generic.level, (size))); \
+ return NT_STATUS_INFO_LENGTH_MISMATCH; \
+}
+#define FINFO_CHECK_SIZE(size) if (blob->length != (size)) { \
+ DEBUG(1,("Unexpected FILEINFO reply size %d for level %u - expected %d\n", \
+ (int)blob->length, parms->generic.level, (size))); \
+ return NT_STATUS_INFO_LENGTH_MISMATCH; \
+}
+
+/*
+ parse a stream information structure
+*/
+NTSTATUS smbcli_parse_stream_info(DATA_BLOB blob, TALLOC_CTX *mem_ctx,
+ struct stream_information *io)
+{
+ uint32_t ofs = 0;
+ io->num_streams = 0;
+ io->streams = NULL;
+
+ while (blob.length - ofs >= 24) {
+ uint_t n = io->num_streams;
+ uint32_t nlen, len;
+ bool ret;
+ void *vstr;
+ io->streams =
+ talloc_realloc(mem_ctx, io->streams, struct stream_struct, n+1);
+ if (!io->streams) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ nlen = IVAL(blob.data, ofs + 0x04);
+ io->streams[n].size = BVAL(blob.data, ofs + 0x08);
+ io->streams[n].alloc_size = BVAL(blob.data, ofs + 0x10);
+ if (nlen > blob.length - (ofs + 24)) {
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+ ret = convert_string_talloc(io->streams,
+ CH_UTF16, CH_UNIX,
+ blob.data+ofs+24, nlen, &vstr, NULL, false);
+ if (!ret) {
+ return NT_STATUS_ILLEGAL_CHARACTER;
+ }
+ io->streams[n].stream_name.s = (const char *)vstr;
+ io->streams[n].stream_name.private_length = nlen;
+ io->num_streams++;
+ len = IVAL(blob.data, ofs);
+ if (len > blob.length - ofs) {
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+ if (len == 0) break;
+ ofs += len;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ parse the fsinfo 'passthru' level replies
+*/
+NTSTATUS smb_raw_fileinfo_passthru_parse(const DATA_BLOB *blob, TALLOC_CTX *mem_ctx,
+ enum smb_fileinfo_level level,
+ union smb_fileinfo *parms)
+{
+ switch (level) {
+ case RAW_FILEINFO_BASIC_INFORMATION:
+ /* some servers return 40 bytes and some 36. w2k3 return 40, so thats
+ what we should do, but we need to accept 36 */
+ if (blob->length != 36) {
+ FINFO_CHECK_SIZE(40);
+ }
+ parms->basic_info.out.create_time = smbcli_pull_nttime(blob->data, 0);
+ parms->basic_info.out.access_time = smbcli_pull_nttime(blob->data, 8);
+ parms->basic_info.out.write_time = smbcli_pull_nttime(blob->data, 16);
+ parms->basic_info.out.change_time = smbcli_pull_nttime(blob->data, 24);
+ parms->basic_info.out.attrib = IVAL(blob->data, 32);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_STANDARD_INFORMATION:
+ FINFO_CHECK_SIZE(24);
+ parms->standard_info.out.alloc_size = BVAL(blob->data, 0);
+ parms->standard_info.out.size = BVAL(blob->data, 8);
+ parms->standard_info.out.nlink = IVAL(blob->data, 16);
+ parms->standard_info.out.delete_pending = CVAL(blob->data, 20);
+ parms->standard_info.out.directory = CVAL(blob->data, 21);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_EA_INFORMATION:
+ FINFO_CHECK_SIZE(4);
+ parms->ea_info.out.ea_size = IVAL(blob->data, 0);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_NAME_INFORMATION:
+ FINFO_CHECK_MIN_SIZE(4);
+ smbcli_blob_pull_string(NULL, mem_ctx, blob,
+ &parms->name_info.out.fname, 0, 4, STR_UNICODE);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_ALL_INFORMATION:
+ FINFO_CHECK_MIN_SIZE(72);
+ parms->all_info.out.create_time = smbcli_pull_nttime(blob->data, 0);
+ parms->all_info.out.access_time = smbcli_pull_nttime(blob->data, 8);
+ parms->all_info.out.write_time = smbcli_pull_nttime(blob->data, 16);
+ parms->all_info.out.change_time = smbcli_pull_nttime(blob->data, 24);
+ parms->all_info.out.attrib = IVAL(blob->data, 32);
+ parms->all_info.out.alloc_size = BVAL(blob->data, 40);
+ parms->all_info.out.size = BVAL(blob->data, 48);
+ parms->all_info.out.nlink = IVAL(blob->data, 56);
+ parms->all_info.out.delete_pending = CVAL(blob->data, 60);
+ parms->all_info.out.directory = CVAL(blob->data, 61);
+#if 1
+ parms->all_info.out.ea_size = IVAL(blob->data, 64);
+ smbcli_blob_pull_string(NULL, mem_ctx, blob,
+ &parms->all_info.out.fname, 68, 72, STR_UNICODE);
+#else
+ /* this is what the CIFS spec says - and its totally
+ wrong, but its useful having it here so we can
+ quickly adapt to broken servers when running
+ tests */
+ parms->all_info.out.ea_size = IVAL(blob->data, 72);
+ /* access flags 4 bytes at 76
+ current_position 8 bytes at 80
+ mode 4 bytes at 88
+ alignment 4 bytes at 92
+ */
+ smbcli_blob_pull_string(NULL, mem_ctx, blob,
+ &parms->all_info.out.fname, 96, 100, STR_UNICODE);
+#endif
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_ALT_NAME_INFORMATION:
+ FINFO_CHECK_MIN_SIZE(4);
+ smbcli_blob_pull_string(NULL, mem_ctx, blob,
+ &parms->alt_name_info.out.fname, 0, 4, STR_UNICODE);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_STREAM_INFORMATION:
+ return smbcli_parse_stream_info(*blob, mem_ctx, &parms->stream_info.out);
+
+ case RAW_FILEINFO_INTERNAL_INFORMATION:
+ FINFO_CHECK_SIZE(8);
+ parms->internal_information.out.file_id = BVAL(blob->data, 0);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_ACCESS_INFORMATION:
+ FINFO_CHECK_SIZE(4);
+ parms->access_information.out.access_flags = IVAL(blob->data, 0);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_POSITION_INFORMATION:
+ FINFO_CHECK_SIZE(8);
+ parms->position_information.out.position = BVAL(blob->data, 0);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_MODE_INFORMATION:
+ FINFO_CHECK_SIZE(4);
+ parms->mode_information.out.mode = IVAL(blob->data, 0);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_ALIGNMENT_INFORMATION:
+ FINFO_CHECK_SIZE(4);
+ parms->alignment_information.out.alignment_requirement
+ = IVAL(blob->data, 0);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_COMPRESSION_INFORMATION:
+ FINFO_CHECK_SIZE(16);
+ parms->compression_info.out.compressed_size = BVAL(blob->data, 0);
+ parms->compression_info.out.format = SVAL(blob->data, 8);
+ parms->compression_info.out.unit_shift = CVAL(blob->data, 10);
+ parms->compression_info.out.chunk_shift = CVAL(blob->data, 11);
+ parms->compression_info.out.cluster_shift = CVAL(blob->data, 12);
+ /* 3 bytes of padding */
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_NETWORK_OPEN_INFORMATION:
+ FINFO_CHECK_SIZE(56);
+ parms->network_open_information.out.create_time = smbcli_pull_nttime(blob->data, 0);
+ parms->network_open_information.out.access_time = smbcli_pull_nttime(blob->data, 8);
+ parms->network_open_information.out.write_time = smbcli_pull_nttime(blob->data, 16);
+ parms->network_open_information.out.change_time = smbcli_pull_nttime(blob->data, 24);
+ parms->network_open_information.out.alloc_size = BVAL(blob->data, 32);
+ parms->network_open_information.out.size = BVAL(blob->data, 40);
+ parms->network_open_information.out.attrib = IVAL(blob->data, 48);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION:
+ FINFO_CHECK_SIZE(8);
+ parms->attribute_tag_information.out.attrib = IVAL(blob->data, 0);
+ parms->attribute_tag_information.out.reparse_tag = IVAL(blob->data, 4);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_SMB2_ALL_EAS:
+ FINFO_CHECK_MIN_SIZE(4);
+ return ea_pull_list_chained(blob, mem_ctx,
+ &parms->all_eas.out.num_eas,
+ &parms->all_eas.out.eas);
+
+ case RAW_FILEINFO_SMB2_ALL_INFORMATION:
+ FINFO_CHECK_MIN_SIZE(0x64);
+ parms->all_info2.out.create_time = smbcli_pull_nttime(blob->data, 0x00);
+ parms->all_info2.out.access_time = smbcli_pull_nttime(blob->data, 0x08);
+ parms->all_info2.out.write_time = smbcli_pull_nttime(blob->data, 0x10);
+ parms->all_info2.out.change_time = smbcli_pull_nttime(blob->data, 0x18);
+ parms->all_info2.out.attrib = IVAL(blob->data, 0x20);
+ parms->all_info2.out.unknown1 = IVAL(blob->data, 0x24);
+ parms->all_info2.out.alloc_size = BVAL(blob->data, 0x28);
+ parms->all_info2.out.size = BVAL(blob->data, 0x30);
+ parms->all_info2.out.nlink = IVAL(blob->data, 0x38);
+ parms->all_info2.out.delete_pending = CVAL(blob->data, 0x3C);
+ parms->all_info2.out.directory = CVAL(blob->data, 0x3D);
+ /* 0x3E-0x3F padding */
+ parms->all_info2.out.file_id = BVAL(blob->data, 0x40);
+ parms->all_info2.out.ea_size = IVAL(blob->data, 0x48);
+ parms->all_info2.out.access_mask = IVAL(blob->data, 0x4C);
+ parms->all_info2.out.position = BVAL(blob->data, 0x50);
+ parms->all_info2.out.mode = IVAL(blob->data, 0x58);
+ parms->all_info2.out.alignment_requirement = IVAL(blob->data, 0x5C);
+ smbcli_blob_pull_string(NULL, mem_ctx, blob,
+ &parms->all_info2.out.fname, 0x60, 0x64, STR_UNICODE);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_SEC_DESC: {
+ enum ndr_err_code ndr_err;
+
+ parms->query_secdesc.out.sd = talloc(mem_ctx, struct security_descriptor);
+ NT_STATUS_HAVE_NO_MEMORY(parms->query_secdesc.out.sd);
+
+ ndr_err = ndr_pull_struct_blob(blob, mem_ctx, NULL,
+ parms->query_secdesc.out.sd,
+ (ndr_pull_flags_fn_t)ndr_pull_security_descriptor);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ return NT_STATUS_OK;
+ }
+
+ default:
+ break;
+ }
+
+ return NT_STATUS_INVALID_LEVEL;
+}
+
+
+/****************************************************************************
+ Handle qfileinfo/qpathinfo trans2 backend.
+****************************************************************************/
+static NTSTATUS smb_raw_info_backend(struct smbcli_session *session,
+ TALLOC_CTX *mem_ctx,
+ union smb_fileinfo *parms,
+ DATA_BLOB *blob)
+{
+ switch (parms->generic.level) {
+ case RAW_FILEINFO_GENERIC:
+ case RAW_FILEINFO_GETATTR:
+ case RAW_FILEINFO_GETATTRE:
+ case RAW_FILEINFO_SEC_DESC:
+ /* not handled here */
+ return NT_STATUS_INVALID_LEVEL;
+
+ case RAW_FILEINFO_STANDARD:
+ FINFO_CHECK_SIZE(22);
+ parms->standard.out.create_time = raw_pull_dos_date2(session->transport,
+ blob->data + 0);
+ parms->standard.out.access_time = raw_pull_dos_date2(session->transport,
+ blob->data + 4);
+ parms->standard.out.write_time = raw_pull_dos_date2(session->transport,
+ blob->data + 8);
+ parms->standard.out.size = IVAL(blob->data, 12);
+ parms->standard.out.alloc_size = IVAL(blob->data, 16);
+ parms->standard.out.attrib = SVAL(blob->data, 20);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_EA_SIZE:
+ FINFO_CHECK_SIZE(26);
+ parms->ea_size.out.create_time = raw_pull_dos_date2(session->transport,
+ blob->data + 0);
+ parms->ea_size.out.access_time = raw_pull_dos_date2(session->transport,
+ blob->data + 4);
+ parms->ea_size.out.write_time = raw_pull_dos_date2(session->transport,
+ blob->data + 8);
+ parms->ea_size.out.size = IVAL(blob->data, 12);
+ parms->ea_size.out.alloc_size = IVAL(blob->data, 16);
+ parms->ea_size.out.attrib = SVAL(blob->data, 20);
+ parms->ea_size.out.ea_size = IVAL(blob->data, 22);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_EA_LIST:
+ FINFO_CHECK_MIN_SIZE(4);
+ return ea_pull_list(blob, mem_ctx,
+ &parms->ea_list.out.num_eas,
+ &parms->ea_list.out.eas);
+
+ case RAW_FILEINFO_ALL_EAS:
+ FINFO_CHECK_MIN_SIZE(4);
+ return ea_pull_list(blob, mem_ctx,
+ &parms->all_eas.out.num_eas,
+ &parms->all_eas.out.eas);
+
+ case RAW_FILEINFO_IS_NAME_VALID:
+ /* no data! */
+ FINFO_CHECK_SIZE(0);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_BASIC_INFO:
+ case RAW_FILEINFO_BASIC_INFORMATION:
+ return smb_raw_fileinfo_passthru_parse(blob, mem_ctx,
+ RAW_FILEINFO_BASIC_INFORMATION, parms);
+
+ case RAW_FILEINFO_STANDARD_INFO:
+ case RAW_FILEINFO_STANDARD_INFORMATION:
+ return smb_raw_fileinfo_passthru_parse(blob, mem_ctx,
+ RAW_FILEINFO_STANDARD_INFORMATION, parms);
+
+ case RAW_FILEINFO_EA_INFO:
+ case RAW_FILEINFO_EA_INFORMATION:
+ return smb_raw_fileinfo_passthru_parse(blob, mem_ctx,
+ RAW_FILEINFO_EA_INFORMATION, parms);
+
+ case RAW_FILEINFO_NAME_INFO:
+ case RAW_FILEINFO_NAME_INFORMATION:
+ return smb_raw_fileinfo_passthru_parse(blob, mem_ctx,
+ RAW_FILEINFO_NAME_INFORMATION, parms);
+
+ case RAW_FILEINFO_ALL_INFO:
+ case RAW_FILEINFO_ALL_INFORMATION:
+ return smb_raw_fileinfo_passthru_parse(blob, mem_ctx,
+ RAW_FILEINFO_ALL_INFORMATION, parms);
+
+ case RAW_FILEINFO_ALT_NAME_INFO:
+ case RAW_FILEINFO_ALT_NAME_INFORMATION:
+ return smb_raw_fileinfo_passthru_parse(blob, mem_ctx,
+ RAW_FILEINFO_ALT_NAME_INFORMATION, parms);
+
+ case RAW_FILEINFO_STREAM_INFO:
+ case RAW_FILEINFO_STREAM_INFORMATION:
+ return smb_raw_fileinfo_passthru_parse(blob, mem_ctx,
+ RAW_FILEINFO_STREAM_INFORMATION, parms);
+
+ case RAW_FILEINFO_INTERNAL_INFORMATION:
+ return smb_raw_fileinfo_passthru_parse(blob, mem_ctx,
+ RAW_FILEINFO_INTERNAL_INFORMATION, parms);
+
+ case RAW_FILEINFO_ACCESS_INFORMATION:
+ return smb_raw_fileinfo_passthru_parse(blob, mem_ctx,
+ RAW_FILEINFO_ACCESS_INFORMATION, parms);
+
+ case RAW_FILEINFO_POSITION_INFORMATION:
+ return smb_raw_fileinfo_passthru_parse(blob, mem_ctx,
+ RAW_FILEINFO_POSITION_INFORMATION, parms);
+
+ case RAW_FILEINFO_MODE_INFORMATION:
+ return smb_raw_fileinfo_passthru_parse(blob, mem_ctx,
+ RAW_FILEINFO_MODE_INFORMATION, parms);
+
+ case RAW_FILEINFO_ALIGNMENT_INFORMATION:
+ return smb_raw_fileinfo_passthru_parse(blob, mem_ctx,
+ RAW_FILEINFO_ALIGNMENT_INFORMATION, parms);
+
+ case RAW_FILEINFO_COMPRESSION_INFO:
+ case RAW_FILEINFO_COMPRESSION_INFORMATION:
+ return smb_raw_fileinfo_passthru_parse(blob, mem_ctx,
+ RAW_FILEINFO_COMPRESSION_INFORMATION, parms);
+
+ case RAW_FILEINFO_UNIX_BASIC:
+ FINFO_CHECK_SIZE(100);
+ parms->unix_basic_info.out.end_of_file = BVAL(blob->data, 0);
+ parms->unix_basic_info.out.num_bytes = BVAL(blob->data, 8);
+ parms->unix_basic_info.out.status_change_time = smbcli_pull_nttime(blob->data, 16);
+ parms->unix_basic_info.out.access_time = smbcli_pull_nttime(blob->data, 24);
+ parms->unix_basic_info.out.change_time = smbcli_pull_nttime(blob->data, 32);
+ parms->unix_basic_info.out.uid = BVAL(blob->data, 40);
+ parms->unix_basic_info.out.gid = BVAL(blob->data, 48);
+ parms->unix_basic_info.out.file_type = IVAL(blob->data, 52);
+ parms->unix_basic_info.out.dev_major = BVAL(blob->data, 60);
+ parms->unix_basic_info.out.dev_minor = BVAL(blob->data, 68);
+ parms->unix_basic_info.out.unique_id = BVAL(blob->data, 76);
+ parms->unix_basic_info.out.permissions = BVAL(blob->data, 84);
+ parms->unix_basic_info.out.nlink = BVAL(blob->data, 92);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_UNIX_INFO2:
+ FINFO_CHECK_SIZE(116);
+ parms->unix_info2.out.end_of_file = BVAL(blob->data, 0);
+ parms->unix_info2.out.num_bytes = BVAL(blob->data, 8);
+ parms->unix_info2.out.status_change_time = smbcli_pull_nttime(blob->data, 16);
+ parms->unix_info2.out.access_time = smbcli_pull_nttime(blob->data, 24);
+ parms->unix_info2.out.change_time = smbcli_pull_nttime(blob->data, 32);
+ parms->unix_info2.out.uid = BVAL(blob->data, 40);
+ parms->unix_info2.out.gid = BVAL(blob->data, 48);
+ parms->unix_info2.out.file_type = IVAL(blob->data, 52);
+ parms->unix_info2.out.dev_major = BVAL(blob->data, 60);
+ parms->unix_info2.out.dev_minor = BVAL(blob->data, 68);
+ parms->unix_info2.out.unique_id = BVAL(blob->data, 76);
+ parms->unix_info2.out.permissions = BVAL(blob->data, 84);
+ parms->unix_info2.out.nlink = BVAL(blob->data, 92);
+ parms->unix_info2.out.create_time = smbcli_pull_nttime(blob->data, 100);
+ parms->unix_info2.out.file_flags = IVAL(blob->data, 108);
+ parms->unix_info2.out.flags_mask = IVAL(blob->data, 112);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_UNIX_LINK:
+ smbcli_blob_pull_string(session, mem_ctx, blob,
+ &parms->unix_link_info.out.link_dest, 0, 4, STR_UNICODE);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_NETWORK_OPEN_INFORMATION:
+ return smb_raw_fileinfo_passthru_parse(blob, mem_ctx,
+ RAW_FILEINFO_NETWORK_OPEN_INFORMATION, parms);
+
+ case RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION:
+ return smb_raw_fileinfo_passthru_parse(blob, mem_ctx,
+ RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION, parms);
+
+ case RAW_FILEINFO_SMB2_ALL_INFORMATION:
+ return smb_raw_fileinfo_passthru_parse(blob, mem_ctx,
+ RAW_FILEINFO_SMB2_ALL_INFORMATION, parms);
+
+ case RAW_FILEINFO_SMB2_ALL_EAS:
+ return smb_raw_fileinfo_passthru_parse(blob, mem_ctx,
+ RAW_FILEINFO_SMB2_ALL_EAS, parms);
+
+ }
+
+ return NT_STATUS_INVALID_LEVEL;
+}
+
+
+/****************************************************************************
+ Very raw query file info - returns param/data blobs - (async send)
+****************************************************************************/
+static struct smbcli_request *smb_raw_fileinfo_blob_send(struct smbcli_tree *tree,
+ uint16_t fnum,
+ uint16_t info_level,
+ DATA_BLOB data)
+{
+ struct smb_trans2 tp;
+ uint16_t setup = TRANSACT2_QFILEINFO;
+ struct smbcli_request *req;
+ TALLOC_CTX *mem_ctx = talloc_init("raw_fileinfo");
+
+ tp.in.max_setup = 0;
+ tp.in.flags = 0;
+ tp.in.timeout = 0;
+ tp.in.setup_count = 1;
+ tp.in.data = data;
+ tp.in.max_param = 2;
+ tp.in.max_data = 0xFFFF;
+ tp.in.setup = &setup;
+
+ tp.in.params = data_blob_talloc(mem_ctx, NULL, 4);
+ if (!tp.in.params.data) {
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ SSVAL(tp.in.params.data, 0, fnum);
+ SSVAL(tp.in.params.data, 2, info_level);
+
+ req = smb_raw_trans2_send(tree, &tp);
+
+ talloc_free(mem_ctx);
+
+ return req;
+}
+
+
+/****************************************************************************
+ Very raw query file info - returns param/data blobs - (async recv)
+****************************************************************************/
+static NTSTATUS smb_raw_fileinfo_blob_recv(struct smbcli_request *req,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *blob)
+{
+ struct smb_trans2 tp;
+ NTSTATUS status = smb_raw_trans2_recv(req, mem_ctx, &tp);
+ if (NT_STATUS_IS_OK(status)) {
+ *blob = tp.out.data;
+ }
+ return status;
+}
+
+/****************************************************************************
+ Very raw query path info - returns param/data blobs (async send)
+****************************************************************************/
+static struct smbcli_request *smb_raw_pathinfo_blob_send(struct smbcli_tree *tree,
+ const char *fname,
+ uint16_t info_level,
+ DATA_BLOB data)
+{
+ struct smb_trans2 tp;
+ uint16_t setup = TRANSACT2_QPATHINFO;
+ struct smbcli_request *req;
+ TALLOC_CTX *mem_ctx = talloc_init("raw_pathinfo");
+
+ tp.in.max_setup = 0;
+ tp.in.flags = 0;
+ tp.in.timeout = 0;
+ tp.in.setup_count = 1;
+ tp.in.data = data;
+ tp.in.max_param = 2;
+ tp.in.max_data = 0xFFFF;
+ tp.in.setup = &setup;
+
+ tp.in.params = data_blob_talloc(mem_ctx, NULL, 6);
+ if (!tp.in.params.data) {
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ SSVAL(tp.in.params.data, 0, info_level);
+ SIVAL(tp.in.params.data, 2, 0);
+ smbcli_blob_append_string(tree->session, mem_ctx, &tp.in.params,
+ fname, STR_TERMINATE);
+
+ req = smb_raw_trans2_send(tree, &tp);
+
+ talloc_free(mem_ctx);
+
+ return req;
+}
+
+/****************************************************************************
+ send a SMBgetatr (async send)
+****************************************************************************/
+static struct smbcli_request *smb_raw_getattr_send(struct smbcli_tree *tree,
+ union smb_fileinfo *parms)
+{
+ struct smbcli_request *req;
+
+ req = smbcli_request_setup(tree, SMBgetatr, 0, 0);
+ if (!req) return NULL;
+
+ smbcli_req_append_ascii4(req, parms->getattr.in.file.path, STR_TERMINATE);
+
+ if (!smbcli_request_send(req)) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+/****************************************************************************
+ send a SMBgetatr (async recv)
+****************************************************************************/
+static NTSTATUS smb_raw_getattr_recv(struct smbcli_request *req,
+ union smb_fileinfo *parms)
+{
+ if (!smbcli_request_receive(req) ||
+ smbcli_request_is_error(req)) {
+ return smbcli_request_destroy(req);
+ }
+
+ SMBCLI_CHECK_WCT(req, 10);
+ parms->getattr.out.attrib = SVAL(req->in.vwv, VWV(0));
+ parms->getattr.out.write_time = raw_pull_dos_date3(req->transport,
+ req->in.vwv + VWV(1));
+ parms->getattr.out.size = IVAL(req->in.vwv, VWV(3));
+
+failed:
+ return smbcli_request_destroy(req);
+}
+
+
+/****************************************************************************
+ Handle SMBgetattrE (async send)
+****************************************************************************/
+static struct smbcli_request *smb_raw_getattrE_send(struct smbcli_tree *tree,
+ union smb_fileinfo *parms)
+{
+ struct smbcli_request *req;
+
+ req = smbcli_request_setup(tree, SMBgetattrE, 1, 0);
+ if (!req) return NULL;
+
+ SSVAL(req->out.vwv, VWV(0), parms->getattre.in.file.fnum);
+ if (!smbcli_request_send(req)) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+/****************************************************************************
+ Handle SMBgetattrE (async send)
+****************************************************************************/
+static NTSTATUS smb_raw_getattrE_recv(struct smbcli_request *req,
+ union smb_fileinfo *parms)
+{
+ if (!smbcli_request_receive(req) ||
+ smbcli_request_is_error(req)) {
+ return smbcli_request_destroy(req);
+ }
+
+ SMBCLI_CHECK_WCT(req, 11);
+ parms->getattre.out.create_time = raw_pull_dos_date2(req->transport,
+ req->in.vwv + VWV(0));
+ parms->getattre.out.access_time = raw_pull_dos_date2(req->transport,
+ req->in.vwv + VWV(2));
+ parms->getattre.out.write_time = raw_pull_dos_date2(req->transport,
+ req->in.vwv + VWV(4));
+ parms->getattre.out.size = IVAL(req->in.vwv, VWV(6));
+ parms->getattre.out.alloc_size = IVAL(req->in.vwv, VWV(8));
+ parms->getattre.out.attrib = SVAL(req->in.vwv, VWV(10));
+
+failed:
+ return smbcli_request_destroy(req);
+}
+
+
+/****************************************************************************
+ Query file info (async send)
+****************************************************************************/
+struct smbcli_request *smb_raw_fileinfo_send(struct smbcli_tree *tree,
+ union smb_fileinfo *parms)
+{
+ DATA_BLOB data;
+ struct smbcli_request *req;
+
+ /* pass off the non-trans2 level to specialised functions */
+ if (parms->generic.level == RAW_FILEINFO_GETATTRE) {
+ return smb_raw_getattrE_send(tree, parms);
+ }
+ if (parms->generic.level == RAW_FILEINFO_SEC_DESC) {
+ return smb_raw_query_secdesc_send(tree, parms);
+ }
+ if (parms->generic.level >= RAW_FILEINFO_GENERIC) {
+ return NULL;
+ }
+
+ data = data_blob(NULL, 0);
+
+ if (parms->generic.level == RAW_FILEINFO_EA_LIST) {
+ if (!ea_push_name_list(tree,
+ &data,
+ parms->ea_list.in.num_names,
+ parms->ea_list.in.ea_names)) {
+ return NULL;
+ }
+ }
+
+ req = smb_raw_fileinfo_blob_send(tree,
+ parms->generic.in.file.fnum,
+ parms->generic.level, data);
+
+ data_blob_free(&data);
+
+ return req;
+}
+
+/****************************************************************************
+ Query file info (async recv)
+****************************************************************************/
+NTSTATUS smb_raw_fileinfo_recv(struct smbcli_request *req,
+ TALLOC_CTX *mem_ctx,
+ union smb_fileinfo *parms)
+{
+ DATA_BLOB blob;
+ NTSTATUS status;
+ struct smbcli_session *session = req?req->session:NULL;
+
+ if (parms->generic.level == RAW_FILEINFO_GETATTRE) {
+ return smb_raw_getattrE_recv(req, parms);
+ }
+ if (parms->generic.level == RAW_FILEINFO_SEC_DESC) {
+ return smb_raw_query_secdesc_recv(req, mem_ctx, parms);
+ }
+ if (parms->generic.level == RAW_FILEINFO_GETATTR) {
+ return smb_raw_getattr_recv(req, parms);
+ }
+
+ status = smb_raw_fileinfo_blob_recv(req, mem_ctx, &blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return smb_raw_info_backend(session, mem_ctx, parms, &blob);
+}
+
+/****************************************************************************
+ Query file info (sync interface)
+****************************************************************************/
+_PUBLIC_ NTSTATUS smb_raw_fileinfo(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ union smb_fileinfo *parms)
+{
+ struct smbcli_request *req = smb_raw_fileinfo_send(tree, parms);
+ return smb_raw_fileinfo_recv(req, mem_ctx, parms);
+}
+
+/****************************************************************************
+ Query path info (async send)
+****************************************************************************/
+_PUBLIC_ struct smbcli_request *smb_raw_pathinfo_send(struct smbcli_tree *tree,
+ union smb_fileinfo *parms)
+{
+ DATA_BLOB data;
+ struct smbcli_request *req;
+
+ if (parms->generic.level == RAW_FILEINFO_GETATTR) {
+ return smb_raw_getattr_send(tree, parms);
+ }
+ if (parms->generic.level >= RAW_FILEINFO_GENERIC) {
+ return NULL;
+ }
+
+ data = data_blob(NULL, 0);
+
+ if (parms->generic.level == RAW_FILEINFO_EA_LIST) {
+ if (!ea_push_name_list(tree,
+ &data,
+ parms->ea_list.in.num_names,
+ parms->ea_list.in.ea_names)) {
+ return NULL;
+ }
+ }
+
+ req = smb_raw_pathinfo_blob_send(tree, parms->generic.in.file.path,
+ parms->generic.level, data);
+ data_blob_free(&data);
+
+ return req;
+}
+
+/****************************************************************************
+ Query path info (async recv)
+****************************************************************************/
+_PUBLIC_ NTSTATUS smb_raw_pathinfo_recv(struct smbcli_request *req,
+ TALLOC_CTX *mem_ctx,
+ union smb_fileinfo *parms)
+{
+ /* recv is idential to fileinfo */
+ return smb_raw_fileinfo_recv(req, mem_ctx, parms);
+}
+
+/****************************************************************************
+ Query path info (sync interface)
+****************************************************************************/
+_PUBLIC_ NTSTATUS smb_raw_pathinfo(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ union smb_fileinfo *parms)
+{
+ struct smbcli_request *req = smb_raw_pathinfo_send(tree, parms);
+ return smb_raw_pathinfo_recv(req, mem_ctx, parms);
+}
diff --git a/source4/libcli/raw/rawfsinfo.c b/source4/libcli/raw/rawfsinfo.c
new file mode 100644
index 0000000000..43a0919e38
--- /dev/null
+++ b/source4/libcli/raw/rawfsinfo.c
@@ -0,0 +1,336 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ RAW_QFS_* 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+
+/****************************************************************************
+ Query FS Info - SMBdskattr call (async send)
+****************************************************************************/
+static struct smbcli_request *smb_raw_dskattr_send(struct smbcli_tree *tree,
+ union smb_fsinfo *fsinfo)
+{
+ struct smbcli_request *req;
+
+ req = smbcli_request_setup(tree, SMBdskattr, 0, 0);
+
+ if (!smbcli_request_send(req)) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+/****************************************************************************
+ Query FS Info - SMBdskattr call (async recv)
+****************************************************************************/
+static NTSTATUS smb_raw_dskattr_recv(struct smbcli_request *req,
+ union smb_fsinfo *fsinfo)
+{
+ if (!smbcli_request_receive(req) ||
+ smbcli_request_is_error(req)) {
+ goto failed;
+ }
+
+ SMBCLI_CHECK_WCT(req, 5);
+ fsinfo->dskattr.out.units_total = SVAL(req->in.vwv, VWV(0));
+ fsinfo->dskattr.out.blocks_per_unit = SVAL(req->in.vwv, VWV(1));
+ fsinfo->dskattr.out.block_size = SVAL(req->in.vwv, VWV(2));
+ fsinfo->dskattr.out.units_free = SVAL(req->in.vwv, VWV(3));
+
+failed:
+ return smbcli_request_destroy(req);
+}
+
+
+/****************************************************************************
+ RAW_QFS_ trans2 interface via blobs (async send)
+****************************************************************************/
+static struct smbcli_request *smb_raw_qfsinfo_send(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ uint16_t info_level)
+{
+ struct smb_trans2 tp;
+ uint16_t setup = TRANSACT2_QFSINFO;
+
+ 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 = 0xFFFF;
+ tp.in.setup = &setup;
+ tp.in.data = data_blob(NULL, 0);
+ tp.in.timeout = 0;
+
+ tp.in.params = data_blob_talloc(mem_ctx, NULL, 2);
+ if (!tp.in.params.data) {
+ return NULL;
+ }
+ SSVAL(tp.in.params.data, 0, info_level);
+
+ return smb_raw_trans2_send(tree, &tp);
+}
+
+/****************************************************************************
+ RAW_QFS_ trans2 interface via blobs (async recv)
+****************************************************************************/
+static NTSTATUS smb_raw_qfsinfo_blob_recv(struct smbcli_request *req,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *blob)
+{
+ struct smb_trans2 tp;
+ NTSTATUS status;
+
+ status = smb_raw_trans2_recv(req, mem_ctx, &tp);
+
+ if (NT_STATUS_IS_OK(status)) {
+ (*blob) = tp.out.data;
+ }
+
+ return status;
+}
+
+
+/* local macros to make the code more readable */
+#define QFS_CHECK_MIN_SIZE(size) if (blob.length < (size)) { \
+ DEBUG(1,("Unexpected QFS reply size %d for level %u - expected min of %d\n", \
+ (int)blob.length, fsinfo->generic.level, (size))); \
+ status = NT_STATUS_INFO_LENGTH_MISMATCH; \
+ goto failed; \
+}
+#define QFS_CHECK_SIZE(size) if (blob.length != (size)) { \
+ DEBUG(1,("Unexpected QFS reply size %d for level %u - expected %d\n", \
+ (int)blob.length, fsinfo->generic.level, (size))); \
+ status = NT_STATUS_INFO_LENGTH_MISMATCH; \
+ goto failed; \
+}
+
+
+/****************************************************************************
+ Query FSInfo raw interface (async send)
+****************************************************************************/
+struct smbcli_request *smb_raw_fsinfo_send(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ union smb_fsinfo *fsinfo)
+{
+ uint16_t info_level;
+
+ /* handle the only non-trans2 call separately */
+ if (fsinfo->generic.level == RAW_QFS_DSKATTR) {
+ return smb_raw_dskattr_send(tree, fsinfo);
+ }
+ if (fsinfo->generic.level >= RAW_QFS_GENERIC) {
+ return NULL;
+ }
+
+ /* the headers map the trans2 levels direct to info levels */
+ info_level = (uint16_t)fsinfo->generic.level;
+
+ return smb_raw_qfsinfo_send(tree, mem_ctx, info_level);
+}
+
+/*
+ parse the fsinfo 'passthru' level replies
+*/
+NTSTATUS smb_raw_fsinfo_passthru_parse(DATA_BLOB blob, TALLOC_CTX *mem_ctx,
+ enum smb_fsinfo_level level,
+ union smb_fsinfo *fsinfo)
+{
+ NTSTATUS status = NT_STATUS_OK;
+ enum ndr_err_code ndr_err;
+ int i;
+
+ /* parse the results */
+ switch (level) {
+ case RAW_QFS_VOLUME_INFORMATION:
+ QFS_CHECK_MIN_SIZE(18);
+ fsinfo->volume_info.out.create_time = smbcli_pull_nttime(blob.data, 0);
+ fsinfo->volume_info.out.serial_number = IVAL(blob.data, 8);
+ smbcli_blob_pull_string(NULL, mem_ctx, &blob,
+ &fsinfo->volume_info.out.volume_name,
+ 12, 18, STR_UNICODE);
+ break;
+
+ case RAW_QFS_SIZE_INFORMATION:
+ QFS_CHECK_SIZE(24);
+ fsinfo->size_info.out.total_alloc_units = BVAL(blob.data, 0);
+ fsinfo->size_info.out.avail_alloc_units = BVAL(blob.data, 8);
+ fsinfo->size_info.out.sectors_per_unit = IVAL(blob.data, 16);
+ fsinfo->size_info.out.bytes_per_sector = IVAL(blob.data, 20);
+ break;
+
+ case RAW_QFS_DEVICE_INFORMATION:
+ QFS_CHECK_SIZE(8);
+ fsinfo->device_info.out.device_type = IVAL(blob.data, 0);
+ fsinfo->device_info.out.characteristics = IVAL(blob.data, 4);
+ break;
+
+ case RAW_QFS_ATTRIBUTE_INFORMATION:
+ QFS_CHECK_MIN_SIZE(12);
+ fsinfo->attribute_info.out.fs_attr = IVAL(blob.data, 0);
+ fsinfo->attribute_info.out.max_file_component_length = IVAL(blob.data, 4);
+ smbcli_blob_pull_string(NULL, mem_ctx, &blob,
+ &fsinfo->attribute_info.out.fs_type,
+ 8, 12, STR_UNICODE);
+ break;
+
+ case RAW_QFS_QUOTA_INFORMATION:
+ QFS_CHECK_SIZE(48);
+ fsinfo->quota_information.out.unknown[0] = BVAL(blob.data, 0);
+ fsinfo->quota_information.out.unknown[1] = BVAL(blob.data, 8);
+ fsinfo->quota_information.out.unknown[2] = BVAL(blob.data, 16);
+ fsinfo->quota_information.out.quota_soft = BVAL(blob.data, 24);
+ fsinfo->quota_information.out.quota_hard = BVAL(blob.data, 32);
+ fsinfo->quota_information.out.quota_flags = BVAL(blob.data, 40);
+ break;
+
+ case RAW_QFS_FULL_SIZE_INFORMATION:
+ QFS_CHECK_SIZE(32);
+ fsinfo->full_size_information.out.total_alloc_units = BVAL(blob.data, 0);
+ fsinfo->full_size_information.out.call_avail_alloc_units = BVAL(blob.data, 8);
+ fsinfo->full_size_information.out.actual_avail_alloc_units = BVAL(blob.data, 16);
+ fsinfo->full_size_information.out.sectors_per_unit = IVAL(blob.data, 24);
+ fsinfo->full_size_information.out.bytes_per_sector = IVAL(blob.data, 28);
+ break;
+
+ case RAW_QFS_OBJECTID_INFORMATION:
+ QFS_CHECK_SIZE(64);
+ ndr_err = ndr_pull_struct_blob(&blob, mem_ctx, NULL, &fsinfo->objectid_information.out.guid,
+ (ndr_pull_flags_fn_t)ndr_pull_GUID);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ }
+ for (i=0;i<6;i++) {
+ fsinfo->objectid_information.out.unknown[i] = BVAL(blob.data, 16 + i*8);
+ }
+ break;
+
+ default:
+ status = NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+failed:
+ return status;
+}
+
+
+/****************************************************************************
+ Query FSInfo raw interface (async recv)
+****************************************************************************/
+NTSTATUS smb_raw_fsinfo_recv(struct smbcli_request *req,
+ TALLOC_CTX *mem_ctx,
+ union smb_fsinfo *fsinfo)
+{
+ DATA_BLOB blob;
+ NTSTATUS status;
+ struct smbcli_session *session = req?req->session:NULL;
+
+ if (fsinfo->generic.level == RAW_QFS_DSKATTR) {
+ return smb_raw_dskattr_recv(req, fsinfo);
+ }
+
+ status = smb_raw_qfsinfo_blob_recv(req, mem_ctx, &blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* parse the results */
+ switch (fsinfo->generic.level) {
+ case RAW_QFS_GENERIC:
+ case RAW_QFS_DSKATTR:
+ /* handled above */
+ break;
+
+ case RAW_QFS_ALLOCATION:
+ QFS_CHECK_SIZE(18);
+ fsinfo->allocation.out.fs_id = IVAL(blob.data, 0);
+ fsinfo->allocation.out.sectors_per_unit = IVAL(blob.data, 4);
+ fsinfo->allocation.out.total_alloc_units = IVAL(blob.data, 8);
+ fsinfo->allocation.out.avail_alloc_units = IVAL(blob.data, 12);
+ fsinfo->allocation.out.bytes_per_sector = SVAL(blob.data, 16);
+ break;
+
+ case RAW_QFS_VOLUME:
+ QFS_CHECK_MIN_SIZE(5);
+ fsinfo->volume.out.serial_number = IVAL(blob.data, 0);
+ smbcli_blob_pull_string(session, mem_ctx, &blob,
+ &fsinfo->volume.out.volume_name,
+ 4, 5, STR_LEN8BIT | STR_NOALIGN);
+ break;
+
+ case RAW_QFS_VOLUME_INFO:
+ case RAW_QFS_VOLUME_INFORMATION:
+ return smb_raw_fsinfo_passthru_parse(blob, mem_ctx,
+ RAW_QFS_VOLUME_INFORMATION, fsinfo);
+
+ case RAW_QFS_SIZE_INFO:
+ case RAW_QFS_SIZE_INFORMATION:
+ return smb_raw_fsinfo_passthru_parse(blob, mem_ctx,
+ RAW_QFS_SIZE_INFORMATION, fsinfo);
+
+ case RAW_QFS_DEVICE_INFO:
+ case RAW_QFS_DEVICE_INFORMATION:
+ return smb_raw_fsinfo_passthru_parse(blob, mem_ctx,
+ RAW_QFS_DEVICE_INFORMATION, fsinfo);
+
+ case RAW_QFS_ATTRIBUTE_INFO:
+ case RAW_QFS_ATTRIBUTE_INFORMATION:
+ return smb_raw_fsinfo_passthru_parse(blob, mem_ctx,
+ RAW_QFS_ATTRIBUTE_INFORMATION, fsinfo);
+
+ case RAW_QFS_UNIX_INFO:
+ QFS_CHECK_SIZE(12);
+ fsinfo->unix_info.out.major_version = SVAL(blob.data, 0);
+ fsinfo->unix_info.out.minor_version = SVAL(blob.data, 2);
+ fsinfo->unix_info.out.capability = SVAL(blob.data, 4);
+ break;
+
+ case RAW_QFS_QUOTA_INFORMATION:
+ return smb_raw_fsinfo_passthru_parse(blob, mem_ctx,
+ RAW_QFS_QUOTA_INFORMATION, fsinfo);
+
+ case RAW_QFS_FULL_SIZE_INFORMATION:
+ return smb_raw_fsinfo_passthru_parse(blob, mem_ctx,
+ RAW_QFS_FULL_SIZE_INFORMATION, fsinfo);
+
+ case RAW_QFS_OBJECTID_INFORMATION:
+ return smb_raw_fsinfo_passthru_parse(blob, mem_ctx,
+ RAW_QFS_OBJECTID_INFORMATION, fsinfo);
+ }
+
+failed:
+ return status;
+}
+
+/****************************************************************************
+ Query FSInfo raw interface (sync interface)
+****************************************************************************/
+_PUBLIC_ NTSTATUS smb_raw_fsinfo(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ union smb_fsinfo *fsinfo)
+{
+ struct smbcli_request *req = smb_raw_fsinfo_send(tree, mem_ctx, fsinfo);
+ return smb_raw_fsinfo_recv(req, mem_ctx, fsinfo);
+}
diff --git a/source4/libcli/raw/rawioctl.c b/source4/libcli/raw/rawioctl.c
new file mode 100644
index 0000000000..77c7b03f15
--- /dev/null
+++ b/source4/libcli/raw/rawioctl.c
@@ -0,0 +1,173 @@
+/*
+ Unix SMB/CIFS implementation.
+ client file operations
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) James J Myers 2003 <myersjj@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+
+#define SETUP_REQUEST(cmd, wct, buflen) do { \
+ req = smbcli_request_setup(tree, cmd, wct, buflen); \
+ if (!req) return NULL; \
+} while (0)
+
+/*
+ send a raw smb ioctl - async send
+*/
+static struct smbcli_request *smb_raw_smbioctl_send(struct smbcli_tree *tree,
+ union smb_ioctl *parms)
+{
+ struct smbcli_request *req;
+
+ SETUP_REQUEST(SMBioctl, 3, 0);
+
+ SSVAL(req->out.vwv, VWV(0), parms->ioctl.in.file.fnum);
+ SIVAL(req->out.vwv, VWV(1), parms->ioctl.in.request);
+
+ if (!smbcli_request_send(req)) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+/*
+ send a raw smb ioctl - async recv
+*/
+static NTSTATUS smb_raw_smbioctl_recv(struct smbcli_request *req,
+ TALLOC_CTX *mem_ctx,
+ union smb_ioctl *parms)
+{
+ if (!smbcli_request_receive(req) ||
+ smbcli_request_is_error(req)) {
+ return smbcli_request_destroy(req);
+ }
+
+ parms->ioctl.out.blob = smbcli_req_pull_blob(&req->in.bufinfo, mem_ctx, req->in.data, -1);
+ return smbcli_request_destroy(req);
+}
+
+
+
+/****************************************************************************
+NT ioctl (async send)
+****************************************************************************/
+static struct smbcli_request *smb_raw_ntioctl_send(struct smbcli_tree *tree,
+ union smb_ioctl *parms)
+{
+ struct smb_nttrans nt;
+ uint8_t setup[8];
+
+ nt.in.max_setup = 4;
+ nt.in.max_param = 0;
+ nt.in.max_data = parms->ntioctl.in.max_data;
+ nt.in.setup_count = 4;
+ nt.in.setup = setup;
+ SIVAL(setup, 0, parms->ntioctl.in.function);
+ SSVAL(setup, 4, parms->ntioctl.in.file.fnum);
+ SCVAL(setup, 6, parms->ntioctl.in.fsctl);
+ SCVAL(setup, 7, parms->ntioctl.in.filter);
+ nt.in.function = NT_TRANSACT_IOCTL;
+ nt.in.params = data_blob(NULL, 0);
+ nt.in.data = parms->ntioctl.in.blob;
+
+ return smb_raw_nttrans_send(tree, &nt);
+}
+
+/****************************************************************************
+NT ioctl (async recv)
+****************************************************************************/
+static NTSTATUS smb_raw_ntioctl_recv(struct smbcli_request *req,
+ TALLOC_CTX *mem_ctx,
+ union smb_ioctl *parms)
+{
+ NTSTATUS status;
+ struct smb_nttrans nt;
+ TALLOC_CTX *tmp_mem;
+
+ tmp_mem = talloc_new(mem_ctx);
+ NT_STATUS_HAVE_NO_MEMORY(tmp_mem);
+
+ status = smb_raw_nttrans_recv(req, tmp_mem, &nt);
+ if (!NT_STATUS_IS_OK(status)) goto fail;
+
+ parms->ntioctl.out.blob = nt.out.data;
+ talloc_steal(mem_ctx, parms->ntioctl.out.blob.data);
+
+fail:
+ talloc_free(tmp_mem);
+ return status;
+}
+
+
+/*
+ send a raw ioctl - async send
+*/
+struct smbcli_request *smb_raw_ioctl_send(struct smbcli_tree *tree, union smb_ioctl *parms)
+{
+ struct smbcli_request *req = NULL;
+
+ switch (parms->generic.level) {
+ case RAW_IOCTL_IOCTL:
+ req = smb_raw_smbioctl_send(tree, parms);
+ break;
+
+ case RAW_IOCTL_NTIOCTL:
+ req = smb_raw_ntioctl_send(tree, parms);
+ break;
+
+ case RAW_IOCTL_SMB2:
+ case RAW_IOCTL_SMB2_NO_HANDLE:
+ return NULL;
+ }
+
+ return req;
+}
+
+/*
+ recv a raw ioctl - async recv
+*/
+NTSTATUS smb_raw_ioctl_recv(struct smbcli_request *req,
+ TALLOC_CTX *mem_ctx, union smb_ioctl *parms)
+{
+ switch (parms->generic.level) {
+ case RAW_IOCTL_IOCTL:
+ return smb_raw_smbioctl_recv(req, mem_ctx, parms);
+
+ case RAW_IOCTL_NTIOCTL:
+ return smb_raw_ntioctl_recv(req, mem_ctx, parms);
+
+ case RAW_IOCTL_SMB2:
+ case RAW_IOCTL_SMB2_NO_HANDLE:
+ break;
+ }
+ return NT_STATUS_INVALID_LEVEL;
+}
+
+/*
+ send a raw ioctl - sync interface
+*/
+NTSTATUS smb_raw_ioctl(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx, union smb_ioctl *parms)
+{
+ struct smbcli_request *req;
+ req = smb_raw_ioctl_send(tree, parms);
+ return smb_raw_ioctl_recv(req, mem_ctx, parms);
+}
diff --git a/source4/libcli/raw/rawlpq.c b/source4/libcli/raw/rawlpq.c
new file mode 100644
index 0000000000..eddb3e0843
--- /dev/null
+++ b/source4/libcli/raw/rawlpq.c
@@ -0,0 +1,49 @@
+/*
+ Unix SMB/CIFS implementation.
+ client lpq operations
+ Copyright (C) Tim Potter 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "smb.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+
+/****************************************************************************
+ lpq - async send
+****************************************************************************/
+struct smbcli_request *smb_raw_lpq_send(struct smbcli_tree *tree,
+ union smb_lpq *parms)
+{
+ return NULL;
+}
+
+/****************************************************************************
+ lpq - async receive
+****************************************************************************/
+NTSTATUS smb_raw_lpq_recv(struct smbcli_request *req, union smb_lpq *parms)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/*
+ lpq - sync interface
+*/
+NTSTATUS smb_raw_lpq(struct smbcli_tree *tree, union smb_lpq *parms)
+{
+ struct smbcli_request *req = smb_raw_lpq_send(tree, parms);
+ return smb_raw_lpq_recv(req, parms);
+}
diff --git a/source4/libcli/raw/rawnegotiate.c b/source4/libcli/raw/rawnegotiate.c
new file mode 100644
index 0000000000..dedc891ac1
--- /dev/null
+++ b/source4/libcli/raw/rawnegotiate.c
@@ -0,0 +1,206 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB client negotiate context management functions
+
+ Copyright (C) Andrew Tridgell 1994-2005
+ Copyright (C) James Myers 2003 <myersjj@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "system/time.h"
+
+static const struct {
+ enum protocol_types prot;
+ const char *name;
+} prots[] = {
+ {PROTOCOL_CORE,"PC NETWORK PROGRAM 1.0"},
+ {PROTOCOL_COREPLUS,"MICROSOFT NETWORKS 1.03"},
+ {PROTOCOL_LANMAN1,"MICROSOFT NETWORKS 3.0"},
+ {PROTOCOL_LANMAN1,"LANMAN1.0"},
+ {PROTOCOL_LANMAN1,"Windows for Workgroups 3.1a"},
+ {PROTOCOL_LANMAN2,"LM1.2X002"},
+ {PROTOCOL_LANMAN2,"DOS LANMAN2.1"},
+ {PROTOCOL_LANMAN2,"LANMAN2.1"},
+ {PROTOCOL_LANMAN2,"Samba"},
+ {PROTOCOL_NT1,"NT LANMAN 1.0"},
+ {PROTOCOL_NT1,"NT LM 0.12"},
+#if 0
+ /* we don't yet handle chaining a SMB transport onto SMB2 */
+ {PROTOCOL_SMB2,"SMB 2.002"},
+#endif
+};
+
+/*
+ Send a negprot command.
+*/
+struct smbcli_request *smb_raw_negotiate_send(struct smbcli_transport *transport,
+ bool unicode,
+ int maxprotocol)
+{
+ struct smbcli_request *req;
+ int i;
+ uint16_t flags2 = 0;
+
+ req = smbcli_request_setup_transport(transport, SMBnegprot, 0, 0);
+ if (!req) {
+ return NULL;
+ }
+
+ flags2 |= FLAGS2_32_BIT_ERROR_CODES;
+ if (unicode) {
+ flags2 |= FLAGS2_UNICODE_STRINGS;
+ }
+ flags2 |= FLAGS2_EXTENDED_ATTRIBUTES;
+ flags2 |= FLAGS2_LONG_PATH_COMPONENTS;
+ flags2 |= FLAGS2_IS_LONG_NAME;
+
+ if (transport->options.use_spnego) {
+ flags2 |= FLAGS2_EXTENDED_SECURITY;
+ }
+
+ SSVAL(req->out.hdr,HDR_FLG2, flags2);
+
+ /* setup the protocol strings */
+ for (i=0; i < ARRAY_SIZE(prots) && prots[i].prot <= maxprotocol; i++) {
+ smbcli_req_append_bytes(req, (const uint8_t *)"\2", 1);
+ smbcli_req_append_string(req, prots[i].name, STR_TERMINATE | STR_ASCII);
+ }
+
+ if (!smbcli_request_send(req)) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+/*
+ Send a negprot command.
+*/
+NTSTATUS smb_raw_negotiate_recv(struct smbcli_request *req)
+{
+ struct smbcli_transport *transport = req->transport;
+ int protocol;
+
+ if (!smbcli_request_receive(req) ||
+ smbcli_request_is_error(req)) {
+ return smbcli_request_destroy(req);
+ }
+
+ SMBCLI_CHECK_MIN_WCT(req, 1);
+
+ protocol = SVALS(req->in.vwv, VWV(0));
+
+ if (protocol >= ARRAY_SIZE(prots) || protocol < 0) {
+ req->status = NT_STATUS_UNSUCCESSFUL;
+ return smbcli_request_destroy(req);
+ }
+
+ transport->negotiate.protocol = prots[protocol].prot;
+
+ if (transport->negotiate.protocol >= PROTOCOL_NT1) {
+ NTTIME ntt;
+
+ /* NT protocol */
+ SMBCLI_CHECK_WCT(req, 17);
+ transport->negotiate.sec_mode = CVAL(req->in.vwv,VWV(1));
+ transport->negotiate.max_mux = SVAL(req->in.vwv,VWV(1)+1);
+ transport->negotiate.max_xmit = IVAL(req->in.vwv,VWV(3)+1);
+ transport->negotiate.sesskey = IVAL(req->in.vwv,VWV(7)+1);
+ transport->negotiate.capabilities = IVAL(req->in.vwv,VWV(9)+1);
+
+ /* this time arrives in real GMT */
+ ntt = smbcli_pull_nttime(req->in.vwv, VWV(11)+1);
+ transport->negotiate.server_time = nt_time_to_unix(ntt);
+ transport->negotiate.server_zone = SVALS(req->in.vwv,VWV(15)+1) * 60;
+ transport->negotiate.key_len = CVAL(req->in.vwv,VWV(16)+1);
+
+ if (transport->negotiate.capabilities & CAP_EXTENDED_SECURITY) {
+ if (req->in.data_size < 16) {
+ goto failed;
+ }
+ transport->negotiate.server_guid = smbcli_req_pull_blob(&req->in.bufinfo, transport, req->in.data, 16);
+ transport->negotiate.secblob = smbcli_req_pull_blob(&req->in.bufinfo, transport, req->in.data + 16, req->in.data_size - 16);
+ } else {
+ if (req->in.data_size < (transport->negotiate.key_len)) {
+ goto failed;
+ }
+ transport->negotiate.secblob = smbcli_req_pull_blob(&req->in.bufinfo, transport, req->in.data, transport->negotiate.key_len);
+ smbcli_req_pull_string(&req->in.bufinfo, transport, &transport->negotiate.server_domain,
+ req->in.data+transport->negotiate.key_len,
+ req->in.data_size-transport->negotiate.key_len, STR_UNICODE|STR_NOALIGN);
+ /* here comes the server name */
+ }
+
+ if (transport->negotiate.capabilities & CAP_RAW_MODE) {
+ transport->negotiate.readbraw_supported = true;
+ transport->negotiate.writebraw_supported = true;
+ }
+ } else if (transport->negotiate.protocol >= PROTOCOL_LANMAN1) {
+ SMBCLI_CHECK_WCT(req, 13);
+ transport->negotiate.sec_mode = SVAL(req->in.vwv,VWV(1));
+ transport->negotiate.max_xmit = SVAL(req->in.vwv,VWV(2));
+ transport->negotiate.sesskey = IVAL(req->in.vwv,VWV(6));
+ transport->negotiate.server_zone = SVALS(req->in.vwv,VWV(10)) * 60;
+
+ /* this time is converted to GMT by raw_pull_dos_date */
+ transport->negotiate.server_time = raw_pull_dos_date(transport,
+ req->in.vwv+VWV(8));
+ if ((SVAL(req->in.vwv,VWV(5)) & 0x1)) {
+ transport->negotiate.readbraw_supported = 1;
+ }
+ if ((SVAL(req->in.vwv,VWV(5)) & 0x2)) {
+ transport->negotiate.writebraw_supported = 1;
+ }
+ transport->negotiate.secblob = smbcli_req_pull_blob(&req->in.bufinfo, transport,
+ req->in.data, req->in.data_size);
+ } else {
+ /* the old core protocol */
+ transport->negotiate.sec_mode = 0;
+ transport->negotiate.server_time = time(NULL);
+ transport->negotiate.max_xmit = transport->options.max_xmit;
+ transport->negotiate.server_zone = get_time_zone(transport->negotiate.server_time);
+ }
+
+ /* a way to force ascii SMB */
+ if (!transport->options.unicode) {
+ transport->negotiate.capabilities &= ~CAP_UNICODE;
+ }
+
+ if (!transport->options.ntstatus_support) {
+ transport->negotiate.capabilities &= ~CAP_STATUS32;
+ }
+
+ if (!transport->options.use_level2_oplocks) {
+ transport->negotiate.capabilities &= ~CAP_LEVEL_II_OPLOCKS;
+ }
+
+failed:
+ return smbcli_request_destroy(req);
+}
+
+
+/*
+ Send a negprot command (sync interface)
+*/
+NTSTATUS smb_raw_negotiate(struct smbcli_transport *transport, bool unicode, int maxprotocol)
+{
+ struct smbcli_request *req = smb_raw_negotiate_send(transport, unicode, maxprotocol);
+ return smb_raw_negotiate_recv(req);
+}
diff --git a/source4/libcli/raw/rawnotify.c b/source4/libcli/raw/rawnotify.c
new file mode 100644
index 0000000000..28ab3eb1a4
--- /dev/null
+++ b/source4/libcli/raw/rawnotify.c
@@ -0,0 +1,168 @@
+/*
+ Unix SMB/CIFS implementation.
+ client change notify 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "../lib/util/dlinklist.h"
+
+/****************************************************************************
+change notify (async send)
+****************************************************************************/
+_PUBLIC_ struct smbcli_request *smb_raw_changenotify_send(struct smbcli_tree *tree, union smb_notify *parms)
+{
+ struct smb_nttrans nt;
+ uint8_t setup[8];
+
+ if (parms->nttrans.level != RAW_NOTIFY_NTTRANS) {
+ return NULL;
+ }
+
+ nt.in.max_setup = 0;
+ nt.in.max_param = parms->nttrans.in.buffer_size;
+ nt.in.max_data = 0;
+ nt.in.setup_count = 4;
+ nt.in.setup = setup;
+ SIVAL(setup, 0, parms->nttrans.in.completion_filter);
+ SSVAL(setup, 4, parms->nttrans.in.file.fnum);
+ SSVAL(setup, 6, parms->nttrans.in.recursive);
+ nt.in.function = NT_TRANSACT_NOTIFY_CHANGE;
+ nt.in.params = data_blob(NULL, 0);
+ nt.in.data = data_blob(NULL, 0);
+
+ return smb_raw_nttrans_send(tree, &nt);
+}
+
+/****************************************************************************
+change notify (async recv)
+****************************************************************************/
+_PUBLIC_ NTSTATUS smb_raw_changenotify_recv(struct smbcli_request *req,
+ TALLOC_CTX *mem_ctx, union smb_notify *parms)
+{
+ struct smb_nttrans nt;
+ NTSTATUS status;
+ uint32_t ofs, i;
+ struct smbcli_session *session = req?req->session:NULL;
+
+ if (parms->nttrans.level != RAW_NOTIFY_NTTRANS) {
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ status = smb_raw_nttrans_recv(req, mem_ctx, &nt);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ parms->nttrans.out.changes = NULL;
+ parms->nttrans.out.num_changes = 0;
+
+ /* count them */
+ for (ofs=0; nt.out.params.length - ofs > 12; ) {
+ uint32_t next = IVAL(nt.out.params.data, ofs);
+ parms->nttrans.out.num_changes++;
+ if (next == 0 ||
+ ofs + next >= nt.out.params.length) break;
+ ofs += next;
+ }
+
+ /* allocate array */
+ parms->nttrans.out.changes = talloc_array(mem_ctx, struct notify_changes, parms->nttrans.out.num_changes);
+ if (!parms->nttrans.out.changes) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=ofs=0; i<parms->nttrans.out.num_changes; i++) {
+ parms->nttrans.out.changes[i].action = IVAL(nt.out.params.data, ofs+4);
+ smbcli_blob_pull_string(session, mem_ctx, &nt.out.params,
+ &parms->nttrans.out.changes[i].name,
+ ofs+8, ofs+12, STR_UNICODE);
+ ofs += IVAL(nt.out.params.data, ofs);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ handle ntcancel replies from the server,
+ as the MID of the real reply and the ntcancel reply is the same
+ we need to do find out to what request the reply belongs
+****************************************************************************/
+struct smbcli_request *smbcli_handle_ntcancel_reply(struct smbcli_request *req,
+ size_t len, const uint8_t *hdr)
+{
+ struct smbcli_request *ntcancel;
+
+ if (!req) return req;
+
+ if (!req->ntcancel) return req;
+
+ if (len >= MIN_SMB_SIZE + NBT_HDR_SIZE &&
+ (CVAL(hdr, HDR_FLG) & FLAG_REPLY) &&
+ CVAL(hdr,HDR_COM) == SMBntcancel) {
+ ntcancel = req->ntcancel;
+ DLIST_REMOVE(req->ntcancel, ntcancel);
+
+ /*
+ * TODO: untill we understand how the
+ * smb_signing works for this case we
+ * return NULL, to just ignore the packet
+ */
+ /*return ntcancel;*/
+ return NULL;
+ }
+
+ return req;
+}
+
+/****************************************************************************
+ Send a NT Cancel request - used to hurry along a pending request. Usually
+ used to cancel a pending change notify request
+ note that this request does not expect a response!
+****************************************************************************/
+NTSTATUS smb_raw_ntcancel(struct smbcli_request *oldreq)
+{
+ struct smbcli_request *req;
+
+ req = smbcli_request_setup_transport(oldreq->transport, SMBntcancel, 0, 0);
+
+ SSVAL(req->out.hdr, HDR_MID, SVAL(oldreq->out.hdr, HDR_MID));
+ SSVAL(req->out.hdr, HDR_PID, SVAL(oldreq->out.hdr, HDR_PID));
+ SSVAL(req->out.hdr, HDR_TID, SVAL(oldreq->out.hdr, HDR_TID));
+ SSVAL(req->out.hdr, HDR_UID, SVAL(oldreq->out.hdr, HDR_UID));
+
+ /* this request does not expect a reply, so tell the signing
+ subsystem not to allocate an id for a reply */
+ req->sign_single_increment = 1;
+ req->one_way_request = 1;
+
+ /*
+ * smbcli_request_send() free's oneway requests
+ * but we want to keep it under oldreq->ntcancel
+ */
+ if (!talloc_reference(oldreq, req)) {
+ talloc_free(req);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ smbcli_request_send(req);
+
+ DLIST_ADD_END(oldreq->ntcancel, req, struct smbcli_request *);
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/libcli/raw/rawreadwrite.c b/source4/libcli/raw/rawreadwrite.c
new file mode 100644
index 0000000000..a8c7996310
--- /dev/null
+++ b/source4/libcli/raw/rawreadwrite.c
@@ -0,0 +1,349 @@
+/*
+ Unix SMB/CIFS implementation.
+ client file read/write routines
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) James 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+
+#define SETUP_REQUEST(cmd, wct, buflen) do { \
+ req = smbcli_request_setup(tree, cmd, wct, buflen); \
+ if (!req) return NULL; \
+} while (0)
+
+/****************************************************************************
+ low level read operation (async send)
+****************************************************************************/
+_PUBLIC_ struct smbcli_request *smb_raw_read_send(struct smbcli_tree *tree, union smb_read *parms)
+{
+ bool bigoffset = false;
+ struct smbcli_request *req = NULL;
+
+ switch (parms->generic.level) {
+ case RAW_READ_READBRAW:
+ if (tree->session->transport->negotiate.capabilities & CAP_LARGE_FILES) {
+ bigoffset = true;
+ }
+ SETUP_REQUEST(SMBreadbraw, bigoffset? 10:8, 0);
+ SSVAL(req->out.vwv, VWV(0), parms->readbraw.in.file.fnum);
+ SIVAL(req->out.vwv, VWV(1), parms->readbraw.in.offset);
+ SSVAL(req->out.vwv, VWV(3), parms->readbraw.in.maxcnt);
+ SSVAL(req->out.vwv, VWV(4), parms->readbraw.in.mincnt);
+ SIVAL(req->out.vwv, VWV(5), parms->readbraw.in.timeout);
+ SSVAL(req->out.vwv, VWV(7), 0); /* reserved */
+ if (bigoffset) {
+ SIVAL(req->out.vwv, VWV(8),parms->readbraw.in.offset>>32);
+ }
+ break;
+
+ case RAW_READ_LOCKREAD:
+ SETUP_REQUEST(SMBlockread, 5, 0);
+ SSVAL(req->out.vwv, VWV(0), parms->lockread.in.file.fnum);
+ SSVAL(req->out.vwv, VWV(1), parms->lockread.in.count);
+ SIVAL(req->out.vwv, VWV(2), parms->lockread.in.offset);
+ SSVAL(req->out.vwv, VWV(4), parms->lockread.in.remaining);
+ break;
+
+ case RAW_READ_READ:
+ SETUP_REQUEST(SMBread, 5, 0);
+ SSVAL(req->out.vwv, VWV(0), parms->read.in.file.fnum);
+ SSVAL(req->out.vwv, VWV(1), parms->read.in.count);
+ SIVAL(req->out.vwv, VWV(2), parms->read.in.offset);
+ SSVAL(req->out.vwv, VWV(4), parms->read.in.remaining);
+ break;
+
+ case RAW_READ_READX:
+ if (tree->session->transport->negotiate.capabilities & CAP_LARGE_FILES) {
+ bigoffset = true;
+ }
+ SETUP_REQUEST(SMBreadX, bigoffset ? 12 : 10, 0);
+ SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+ SSVAL(req->out.vwv, VWV(1), 0);
+ SSVAL(req->out.vwv, VWV(2), parms->readx.in.file.fnum);
+ SIVAL(req->out.vwv, VWV(3), parms->readx.in.offset);
+ SSVAL(req->out.vwv, VWV(5), parms->readx.in.maxcnt & 0xFFFF);
+ SSVAL(req->out.vwv, VWV(6), parms->readx.in.mincnt);
+ SIVAL(req->out.vwv, VWV(7), parms->readx.in.maxcnt >> 16);
+ SSVAL(req->out.vwv, VWV(9), parms->readx.in.remaining);
+ /*
+ * TODO: give an error when the offset is 64 bit
+ * and the server doesn't support it
+ */
+ if (bigoffset) {
+ SIVAL(req->out.vwv, VWV(10),parms->readx.in.offset>>32);
+ }
+ if (parms->readx.in.read_for_execute) {
+ uint16_t flags2 = SVAL(req->out.hdr, HDR_FLG2);
+ flags2 |= FLAGS2_READ_PERMIT_EXECUTE;
+ SSVAL(req->out.hdr, HDR_FLG2, flags2);
+ }
+ break;
+
+ case RAW_READ_SMB2:
+ return NULL;
+ }
+
+ if (!smbcli_request_send(req)) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+
+ /* the transport layer needs to know that a readbraw is pending
+ and handle receives a little differently */
+ if (parms->generic.level == RAW_READ_READBRAW) {
+ tree->session->transport->readbraw_pending = 1;
+ }
+
+ return req;
+}
+
+/****************************************************************************
+ low level read operation (async recv)
+****************************************************************************/
+_PUBLIC_ NTSTATUS smb_raw_read_recv(struct smbcli_request *req, union smb_read *parms)
+{
+ if (!smbcli_request_receive(req) ||
+ smbcli_request_is_error(req)) {
+ goto failed;
+ }
+
+ switch (parms->generic.level) {
+ case RAW_READ_READBRAW:
+ parms->readbraw.out.nread = req->in.size - NBT_HDR_SIZE;
+ if (parms->readbraw.out.nread >
+ MAX(parms->readx.in.mincnt, parms->readx.in.maxcnt)) {
+ req->status = NT_STATUS_BUFFER_TOO_SMALL;
+ goto failed;
+ }
+ memcpy(parms->readbraw.out.data, req->in.buffer + NBT_HDR_SIZE, parms->readbraw.out.nread);
+ break;
+
+ case RAW_READ_LOCKREAD:
+ SMBCLI_CHECK_WCT(req, 5);
+ parms->lockread.out.nread = SVAL(req->in.vwv, VWV(0));
+ if (parms->lockread.out.nread > parms->lockread.in.count ||
+ !smbcli_raw_pull_data(&req->in.bufinfo, req->in.data+3,
+ parms->lockread.out.nread, parms->lockread.out.data)) {
+ req->status = NT_STATUS_BUFFER_TOO_SMALL;
+ }
+ break;
+
+ case RAW_READ_READ:
+ /* there are 4 reserved words in the reply */
+ SMBCLI_CHECK_WCT(req, 5);
+ parms->read.out.nread = SVAL(req->in.vwv, VWV(0));
+ if (parms->read.out.nread > parms->read.in.count ||
+ !smbcli_raw_pull_data(&req->in.bufinfo, req->in.data+3,
+ parms->read.out.nread, parms->read.out.data)) {
+ req->status = NT_STATUS_BUFFER_TOO_SMALL;
+ }
+ break;
+
+ case RAW_READ_READX:
+ /* there are 5 reserved words in the reply */
+ SMBCLI_CHECK_WCT(req, 12);
+ parms->readx.out.remaining = SVAL(req->in.vwv, VWV(2));
+ parms->readx.out.compaction_mode = SVAL(req->in.vwv, VWV(3));
+ parms->readx.out.nread = SVAL(req->in.vwv, VWV(5));
+
+ /* handle oversize replies for non-chained readx replies with
+ CAP_LARGE_READX. The snia spec has must to answer for. */
+ if ((req->tree->session->transport->negotiate.capabilities & CAP_LARGE_READX)
+ && CVAL(req->in.vwv, VWV(0)) == SMB_CHAIN_NONE &&
+ req->in.size >= 0x10000) {
+ parms->readx.out.nread += (SVAL(req->in.vwv, VWV(7)) << 16);
+ if (req->in.hdr + SVAL(req->in.vwv, VWV(6)) +
+ parms->readx.out.nread <=
+ req->in.buffer + req->in.size) {
+ req->in.data_size += (SVAL(req->in.vwv, VWV(7)) << 16);
+
+ /* update the bufinfo with the new size */
+ smb_setup_bufinfo(req);
+ }
+ }
+
+ if (parms->readx.out.nread > MAX(parms->readx.in.mincnt, parms->readx.in.maxcnt) ||
+ !smbcli_raw_pull_data(&req->in.bufinfo, req->in.hdr + SVAL(req->in.vwv, VWV(6)),
+ parms->readx.out.nread,
+ parms->readx.out.data)) {
+ req->status = NT_STATUS_BUFFER_TOO_SMALL;
+ }
+ break;
+
+ case RAW_READ_SMB2:
+ req->status = NT_STATUS_INTERNAL_ERROR;
+ break;
+ }
+
+failed:
+ return smbcli_request_destroy(req);
+}
+
+/****************************************************************************
+ low level read operation (sync interface)
+****************************************************************************/
+_PUBLIC_ NTSTATUS smb_raw_read(struct smbcli_tree *tree, union smb_read *parms)
+{
+ struct smbcli_request *req = smb_raw_read_send(tree, parms);
+ return smb_raw_read_recv(req, parms);
+}
+
+
+/****************************************************************************
+ raw write interface (async send)
+****************************************************************************/
+_PUBLIC_ struct smbcli_request *smb_raw_write_send(struct smbcli_tree *tree, union smb_write *parms)
+{
+ bool bigoffset = false;
+ struct smbcli_request *req = NULL;
+
+ switch (parms->generic.level) {
+ case RAW_WRITE_WRITEUNLOCK:
+ SETUP_REQUEST(SMBwriteunlock, 5, 3 + parms->writeunlock.in.count);
+ SSVAL(req->out.vwv, VWV(0), parms->writeunlock.in.file.fnum);
+ SSVAL(req->out.vwv, VWV(1), parms->writeunlock.in.count);
+ SIVAL(req->out.vwv, VWV(2), parms->writeunlock.in.offset);
+ SSVAL(req->out.vwv, VWV(4), parms->writeunlock.in.remaining);
+ SCVAL(req->out.data, 0, SMB_DATA_BLOCK);
+ SSVAL(req->out.data, 1, parms->writeunlock.in.count);
+ if (parms->writeunlock.in.count > 0) {
+ memcpy(req->out.data+3, parms->writeunlock.in.data,
+ parms->writeunlock.in.count);
+ }
+ break;
+
+ case RAW_WRITE_WRITE:
+ SETUP_REQUEST(SMBwrite, 5, 3 + parms->write.in.count);
+ SSVAL(req->out.vwv, VWV(0), parms->write.in.file.fnum);
+ SSVAL(req->out.vwv, VWV(1), parms->write.in.count);
+ SIVAL(req->out.vwv, VWV(2), parms->write.in.offset);
+ SSVAL(req->out.vwv, VWV(4), parms->write.in.remaining);
+ SCVAL(req->out.data, 0, SMB_DATA_BLOCK);
+ SSVAL(req->out.data, 1, parms->write.in.count);
+ if (parms->write.in.count > 0) {
+ memcpy(req->out.data+3, parms->write.in.data, parms->write.in.count);
+ }
+ break;
+
+ case RAW_WRITE_WRITECLOSE:
+ SETUP_REQUEST(SMBwriteclose, 6, 1 + parms->writeclose.in.count);
+ SSVAL(req->out.vwv, VWV(0), parms->writeclose.in.file.fnum);
+ SSVAL(req->out.vwv, VWV(1), parms->writeclose.in.count);
+ SIVAL(req->out.vwv, VWV(2), parms->writeclose.in.offset);
+ raw_push_dos_date3(tree->session->transport,
+ req->out.vwv, VWV(4), parms->writeclose.in.mtime);
+ SCVAL(req->out.data, 0, 0);
+ if (parms->writeclose.in.count > 0) {
+ memcpy(req->out.data+1, parms->writeclose.in.data,
+ parms->writeclose.in.count);
+ }
+ break;
+
+ case RAW_WRITE_WRITEX:
+ if (tree->session->transport->negotiate.capabilities & CAP_LARGE_FILES) {
+ bigoffset = true;
+ }
+ SETUP_REQUEST(SMBwriteX, bigoffset ? 14 : 12, parms->writex.in.count);
+ SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+ SSVAL(req->out.vwv, VWV(1), 0);
+ SSVAL(req->out.vwv, VWV(2), parms->writex.in.file.fnum);
+ SIVAL(req->out.vwv, VWV(3), parms->writex.in.offset);
+ SIVAL(req->out.vwv, VWV(5), 0); /* reserved */
+ SSVAL(req->out.vwv, VWV(7), parms->writex.in.wmode);
+ SSVAL(req->out.vwv, VWV(8), parms->writex.in.remaining);
+ SSVAL(req->out.vwv, VWV(9), parms->writex.in.count>>16);
+ SSVAL(req->out.vwv, VWV(10), parms->writex.in.count);
+ SSVAL(req->out.vwv, VWV(11), PTR_DIFF(req->out.data, req->out.hdr));
+ if (bigoffset) {
+ SIVAL(req->out.vwv,VWV(12),parms->writex.in.offset>>32);
+ }
+ if (parms->writex.in.count > 0) {
+ memcpy(req->out.data, parms->writex.in.data, parms->writex.in.count);
+ }
+ break;
+
+ case RAW_WRITE_SPLWRITE:
+ SETUP_REQUEST(SMBsplwr, 1, parms->splwrite.in.count);
+ SSVAL(req->out.vwv, VWV(0), parms->splwrite.in.file.fnum);
+ if (parms->splwrite.in.count > 0) {
+ memcpy(req->out.data, parms->splwrite.in.data, parms->splwrite.in.count);
+ }
+ break;
+
+ case RAW_WRITE_SMB2:
+ return NULL;
+ }
+
+ if (!smbcli_request_send(req)) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+
+/****************************************************************************
+ raw write interface (async recv)
+****************************************************************************/
+NTSTATUS smb_raw_write_recv(struct smbcli_request *req, union smb_write *parms)
+{
+ if (!smbcli_request_receive(req) ||
+ smbcli_request_is_error(req)) {
+ goto failed;
+ }
+
+ switch (parms->generic.level) {
+ case RAW_WRITE_WRITEUNLOCK:
+ SMBCLI_CHECK_WCT(req, 1);
+ parms->writeunlock.out.nwritten = SVAL(req->in.vwv, VWV(0));
+ break;
+ case RAW_WRITE_WRITE:
+ SMBCLI_CHECK_WCT(req, 1);
+ parms->write.out.nwritten = SVAL(req->in.vwv, VWV(0));
+ break;
+ case RAW_WRITE_WRITECLOSE:
+ SMBCLI_CHECK_WCT(req, 1);
+ parms->writeclose.out.nwritten = SVAL(req->in.vwv, VWV(0));
+ break;
+ case RAW_WRITE_WRITEX:
+ SMBCLI_CHECK_WCT(req, 6);
+ parms->writex.out.nwritten = SVAL(req->in.vwv, VWV(2));
+ parms->writex.out.nwritten += (CVAL(req->in.vwv, VWV(4)) << 16);
+ parms->writex.out.remaining = SVAL(req->in.vwv, VWV(3));
+ break;
+ case RAW_WRITE_SPLWRITE:
+ break;
+ case RAW_WRITE_SMB2:
+ req->status = NT_STATUS_INTERNAL_ERROR;
+ break;
+ }
+
+failed:
+ return smbcli_request_destroy(req);
+}
+
+/****************************************************************************
+ raw write interface (sync interface)
+****************************************************************************/
+_PUBLIC_ NTSTATUS smb_raw_write(struct smbcli_tree *tree, union smb_write *parms)
+{
+ struct smbcli_request *req = smb_raw_write_send(tree, parms);
+ return smb_raw_write_recv(req, parms);
+}
diff --git a/source4/libcli/raw/rawrequest.c b/source4/libcli/raw/rawrequest.c
new file mode 100644
index 0000000000..0f04190a8b
--- /dev/null
+++ b/source4/libcli/raw/rawrequest.c
@@ -0,0 +1,1029 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) James Myers 2003 <myersjj@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ this file implements functions for manipulating the 'struct smbcli_request' structure in libsmb
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "../lib/util/dlinklist.h"
+#include "lib/events/events.h"
+#include "librpc/ndr/libndr.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+
+/* we over allocate the data buffer to prevent too many realloc calls */
+#define REQ_OVER_ALLOCATION 0
+
+/* assume that a character will not consume more than 3 bytes per char */
+#define MAX_BYTES_PER_CHAR 3
+
+/* setup the bufinfo used for strings and range checking */
+void smb_setup_bufinfo(struct smbcli_request *req)
+{
+ req->in.bufinfo.mem_ctx = req;
+ req->in.bufinfo.flags = 0;
+ if (req->flags2 & FLAGS2_UNICODE_STRINGS) {
+ req->in.bufinfo.flags = BUFINFO_FLAG_UNICODE;
+ }
+ req->in.bufinfo.align_base = req->in.buffer;
+ req->in.bufinfo.data = req->in.data;
+ req->in.bufinfo.data_size = req->in.data_size;
+}
+
+
+/* destroy a request structure and return final status */
+_PUBLIC_ NTSTATUS smbcli_request_destroy(struct smbcli_request *req)
+{
+ NTSTATUS status;
+
+ /* this is the error code we give the application for when a
+ _send() call fails completely */
+ if (!req) return NT_STATUS_UNSUCCESSFUL;
+
+ if (req->transport) {
+ /* remove it from the list of pending requests (a null op if
+ its not in the list) */
+ DLIST_REMOVE(req->transport->pending_recv, req);
+ }
+
+ if (req->state == SMBCLI_REQUEST_ERROR &&
+ NT_STATUS_IS_OK(req->status)) {
+ req->status = NT_STATUS_INTERNAL_ERROR;
+ }
+
+ status = req->status;
+
+ if (!req->do_not_free) {
+ talloc_free(req);
+ }
+
+ return status;
+}
+
+
+/*
+ low-level function to setup a request buffer for a non-SMB packet
+ at the transport level
+*/
+struct smbcli_request *smbcli_request_setup_nonsmb(struct smbcli_transport *transport, size_t size)
+{
+ struct smbcli_request *req;
+
+ req = talloc(transport, struct smbcli_request);
+ if (!req) {
+ return NULL;
+ }
+ ZERO_STRUCTP(req);
+
+ /* setup the request context */
+ req->state = SMBCLI_REQUEST_INIT;
+ req->transport = transport;
+ req->session = NULL;
+ req->tree = NULL;
+ req->out.size = size;
+
+ /* over allocate by a small amount */
+ req->out.allocated = req->out.size + REQ_OVER_ALLOCATION;
+
+ req->out.buffer = talloc_array(req, uint8_t, req->out.allocated);
+ if (!req->out.buffer) {
+ return NULL;
+ }
+
+ SIVAL(req->out.buffer, 0, 0);
+
+ return req;
+}
+
+
+/*
+ setup a SMB packet at transport level
+*/
+struct smbcli_request *smbcli_request_setup_transport(struct smbcli_transport *transport,
+ uint8_t command, uint_t wct, uint_t buflen)
+{
+ struct smbcli_request *req;
+
+ req = smbcli_request_setup_nonsmb(transport, NBT_HDR_SIZE + MIN_SMB_SIZE + wct*2 + buflen);
+
+ if (!req) return NULL;
+
+ req->out.hdr = req->out.buffer + NBT_HDR_SIZE;
+ req->out.vwv = req->out.hdr + HDR_VWV;
+ req->out.wct = wct;
+ req->out.data = req->out.vwv + VWV(wct) + 2;
+ req->out.data_size = buflen;
+ req->out.ptr = req->out.data;
+
+ SCVAL(req->out.hdr, HDR_WCT, wct);
+ SSVAL(req->out.vwv, VWV(wct), buflen);
+
+ memcpy(req->out.hdr, "\377SMB", 4);
+ SCVAL(req->out.hdr,HDR_COM,command);
+
+ SCVAL(req->out.hdr,HDR_FLG, FLAG_CASELESS_PATHNAMES);
+ SSVAL(req->out.hdr,HDR_FLG2, 0);
+
+ if (command != SMBtranss && command != SMBtranss2) {
+ /* assign a mid */
+ req->mid = smbcli_transport_next_mid(transport);
+ }
+
+ /* copy the pid, uid and mid to the request */
+ SSVAL(req->out.hdr, HDR_PID, 0);
+ SSVAL(req->out.hdr, HDR_UID, 0);
+ SSVAL(req->out.hdr, HDR_MID, req->mid);
+ SSVAL(req->out.hdr, HDR_TID,0);
+ SSVAL(req->out.hdr, HDR_PIDHIGH,0);
+ SIVAL(req->out.hdr, HDR_RCLS, 0);
+ memset(req->out.hdr+HDR_SS_FIELD, 0, 10);
+
+ return req;
+}
+
+/*
+ setup a reply in req->out with the given word count and initial data
+ buffer size. the caller will then fill in the command words and
+ data before calling smbcli_request_send() to send the reply on its
+ way. This interface is used before a session is setup.
+*/
+struct smbcli_request *smbcli_request_setup_session(struct smbcli_session *session,
+ uint8_t command, uint_t wct, size_t buflen)
+{
+ struct smbcli_request *req;
+
+ req = smbcli_request_setup_transport(session->transport, command, wct, buflen);
+
+ if (!req) return NULL;
+
+ req->session = session;
+
+ SSVAL(req->out.hdr, HDR_FLG2, session->flags2);
+ SSVAL(req->out.hdr, HDR_PID, session->pid & 0xFFFF);
+ SSVAL(req->out.hdr, HDR_PIDHIGH, session->pid >> 16);
+ SSVAL(req->out.hdr, HDR_UID, session->vuid);
+
+ return req;
+}
+
+/*
+ setup a request for tree based commands
+*/
+struct smbcli_request *smbcli_request_setup(struct smbcli_tree *tree,
+ uint8_t command,
+ uint_t wct, uint_t buflen)
+{
+ struct smbcli_request *req;
+
+ req = smbcli_request_setup_session(tree->session, command, wct, buflen);
+ if (req) {
+ req->tree = tree;
+ SSVAL(req->out.hdr,HDR_TID,tree->tid);
+ }
+ return req;
+}
+
+
+/*
+ grow the allocation of the data buffer portion of a reply
+ packet. Note that as this can reallocate the packet buffer this
+ invalidates any local pointers into the packet.
+
+ To cope with this req->out.ptr is supplied. This will be updated to
+ point at the same offset into the packet as before this call
+*/
+static void smbcli_req_grow_allocation(struct smbcli_request *req, uint_t new_size)
+{
+ int delta;
+ uint8_t *buf2;
+
+ delta = new_size - req->out.data_size;
+ if (delta + req->out.size <= req->out.allocated) {
+ /* it fits in the preallocation */
+ return;
+ }
+
+ /* we need to realloc */
+ req->out.allocated = req->out.size + delta + REQ_OVER_ALLOCATION;
+ buf2 = talloc_realloc(req, req->out.buffer, uint8_t, req->out.allocated);
+ if (buf2 == NULL) {
+ smb_panic("out of memory in req_grow_allocation");
+ }
+
+ if (buf2 == req->out.buffer) {
+ /* the malloc library gave us the same pointer */
+ return;
+ }
+
+ /* update the pointers into the packet */
+ req->out.data = buf2 + PTR_DIFF(req->out.data, req->out.buffer);
+ req->out.ptr = buf2 + PTR_DIFF(req->out.ptr, req->out.buffer);
+ req->out.vwv = buf2 + PTR_DIFF(req->out.vwv, req->out.buffer);
+ req->out.hdr = buf2 + PTR_DIFF(req->out.hdr, req->out.buffer);
+
+ req->out.buffer = buf2;
+}
+
+
+/*
+ grow the data buffer portion of a reply packet. Note that as this
+ can reallocate the packet buffer this invalidates any local pointers
+ into the packet.
+
+ To cope with this req->out.ptr is supplied. This will be updated to
+ point at the same offset into the packet as before this call
+*/
+static void smbcli_req_grow_data(struct smbcli_request *req, uint_t new_size)
+{
+ int delta;
+
+ smbcli_req_grow_allocation(req, new_size);
+
+ delta = new_size - req->out.data_size;
+
+ req->out.size += delta;
+ req->out.data_size += delta;
+
+ /* set the BCC to the new data size */
+ SSVAL(req->out.vwv, VWV(req->out.wct), new_size);
+}
+
+
+/*
+ setup a chained reply in req->out with the given word count and
+ initial data buffer size.
+*/
+NTSTATUS smbcli_chained_request_setup(struct smbcli_request *req,
+ uint8_t command,
+ uint_t wct, size_t buflen)
+{
+ uint_t new_size = 1 + (wct*2) + 2 + buflen;
+
+ SSVAL(req->out.vwv, VWV(0), command);
+ SSVAL(req->out.vwv, VWV(1), req->out.size - NBT_HDR_SIZE);
+
+ smbcli_req_grow_allocation(req, req->out.data_size + new_size);
+
+ req->out.vwv = req->out.buffer + req->out.size + 1;
+ SCVAL(req->out.vwv, -1, wct);
+ SSVAL(req->out.vwv, VWV(wct), buflen);
+
+ req->out.size += new_size;
+ req->out.data_size += new_size;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ aadvance to the next chained reply in a request
+*/
+NTSTATUS smbcli_chained_advance(struct smbcli_request *req)
+{
+ uint8_t *buffer;
+
+ if (CVAL(req->in.vwv, VWV(0)) == SMB_CHAIN_NONE) {
+ return NT_STATUS_NOT_FOUND;
+ }
+
+ buffer = req->in.hdr + SVAL(req->in.vwv, VWV(1));
+
+ if (buffer + 3 > req->in.buffer + req->in.size) {
+ return NT_STATUS_BUFFER_TOO_SMALL;
+ }
+
+ req->in.vwv = buffer + 1;
+ req->in.wct = CVAL(buffer, 0);
+ if (buffer + 3 + req->in.wct*2 > req->in.buffer + req->in.size) {
+ return NT_STATUS_BUFFER_TOO_SMALL;
+ }
+ req->in.data = req->in.vwv + 2 + req->in.wct * 2;
+ req->in.data_size = SVAL(req->in.vwv, VWV(req->in.wct));
+
+ /* fix the bufinfo */
+ smb_setup_bufinfo(req);
+
+ if (buffer + 3 + req->in.wct*2 + req->in.data_size >
+ req->in.buffer + req->in.size) {
+ return NT_STATUS_BUFFER_TOO_SMALL;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ send a message
+*/
+bool smbcli_request_send(struct smbcli_request *req)
+{
+ if (IVAL(req->out.buffer, 0) == 0) {
+ _smb_setlen(req->out.buffer, req->out.size - NBT_HDR_SIZE);
+ }
+
+ smbcli_request_calculate_sign_mac(req);
+
+ smbcli_transport_send(req);
+
+ return true;
+}
+
+
+/*
+ receive a response to a packet
+*/
+bool smbcli_request_receive(struct smbcli_request *req)
+{
+ /* req can be NULL when a send has failed. This eliminates lots of NULL
+ checks in each module */
+ if (!req) return false;
+
+ /* keep receiving packets until this one is replied to */
+ while (req->state <= SMBCLI_REQUEST_RECV) {
+ if (event_loop_once(req->transport->socket->event.ctx) != 0) {
+ return false;
+ }
+ }
+
+ return req->state == SMBCLI_REQUEST_DONE;
+}
+
+
+/*
+ handle oplock break requests from the server - return true if the request was
+ an oplock break
+*/
+bool smbcli_handle_oplock_break(struct smbcli_transport *transport, uint_t len, const uint8_t *hdr, const uint8_t *vwv)
+{
+ /* we must be very fussy about what we consider an oplock break to avoid
+ matching readbraw replies */
+ if (len != MIN_SMB_SIZE + VWV(8) + NBT_HDR_SIZE ||
+ (CVAL(hdr, HDR_FLG) & FLAG_REPLY) ||
+ CVAL(hdr,HDR_COM) != SMBlockingX ||
+ SVAL(hdr, HDR_MID) != 0xFFFF ||
+ SVAL(vwv,VWV(6)) != 0 ||
+ SVAL(vwv,VWV(7)) != 0) {
+ return false;
+ }
+
+ if (transport->oplock.handler) {
+ uint16_t tid = SVAL(hdr, HDR_TID);
+ uint16_t fnum = SVAL(vwv,VWV(2));
+ uint8_t level = CVAL(vwv,VWV(3)+1);
+ transport->oplock.handler(transport, tid, fnum, level, transport->oplock.private_data);
+ }
+
+ return true;
+}
+
+/*
+ wait for a reply to be received for a packet that just returns an error
+ code and nothing more
+*/
+_PUBLIC_ NTSTATUS smbcli_request_simple_recv(struct smbcli_request *req)
+{
+ (void) smbcli_request_receive(req);
+ return smbcli_request_destroy(req);
+}
+
+
+/* Return true if the last packet was in error */
+bool smbcli_request_is_error(struct smbcli_request *req)
+{
+ return NT_STATUS_IS_ERR(req->status);
+}
+
+/*
+ append a string into the data portion of the request packet
+
+ return the number of bytes added to the packet
+*/
+size_t smbcli_req_append_string(struct smbcli_request *req, const char *str, uint_t flags)
+{
+ size_t len;
+
+ /* determine string type to use */
+ if (!(flags & (STR_ASCII|STR_UNICODE))) {
+ flags |= (req->transport->negotiate.capabilities & CAP_UNICODE) ? STR_UNICODE : STR_ASCII;
+ }
+
+ len = (strlen(str)+2) * MAX_BYTES_PER_CHAR;
+
+ smbcli_req_grow_allocation(req, len + req->out.data_size);
+
+ len = push_string(req->out.data + req->out.data_size, str, len, flags);
+
+ smbcli_req_grow_data(req, len + req->out.data_size);
+
+ return len;
+}
+
+
+/*
+ this is like smbcli_req_append_string but it also return the
+ non-terminated string byte length, which can be less than the number
+ of bytes consumed in the packet for 2 reasons:
+
+ 1) the string in the packet may be null terminated
+ 2) the string in the packet may need a 1 byte UCS2 alignment
+
+ this is used in places where the non-terminated string byte length is
+ placed in the packet as a separate field
+*/
+size_t smbcli_req_append_string_len(struct smbcli_request *req, const char *str, uint_t flags, int *len)
+{
+ int diff = 0;
+ size_t ret;
+
+ /* determine string type to use */
+ if (!(flags & (STR_ASCII|STR_UNICODE))) {
+ flags |= (req->transport->negotiate.capabilities & CAP_UNICODE) ? STR_UNICODE : STR_ASCII;
+ }
+
+ /* see if an alignment byte will be used */
+ if ((flags & STR_UNICODE) && !(flags & STR_NOALIGN)) {
+ diff = ucs2_align(NULL, req->out.data + req->out.data_size, flags);
+ }
+
+ /* do the hard work */
+ ret = smbcli_req_append_string(req, str, flags);
+
+ /* see if we need to subtract the termination */
+ if (flags & STR_TERMINATE) {
+ diff += (flags & STR_UNICODE) ? 2 : 1;
+ }
+
+ if (ret >= diff) {
+ (*len) = ret - diff;
+ } else {
+ (*len) = ret;
+ }
+
+ return ret;
+}
+
+
+/*
+ push a string into the data portion of the request packet, growing it if necessary
+ this gets quite tricky - please be very careful to cover all cases when modifying this
+
+ if dest is NULL, then put the string at the end of the data portion of the packet
+
+ if dest_len is -1 then no limit applies
+*/
+size_t smbcli_req_append_ascii4(struct smbcli_request *req, const char *str, uint_t flags)
+{
+ size_t size;
+ smbcli_req_append_bytes(req, (const uint8_t *)"\4", 1);
+ size = smbcli_req_append_string(req, str, flags);
+ return size + 1;
+}
+
+
+/*
+ push a blob into the data portion of the request packet, growing it if necessary
+ this gets quite tricky - please be very careful to cover all cases when modifying this
+
+ if dest is NULL, then put the blob at the end of the data portion of the packet
+*/
+size_t smbcli_req_append_blob(struct smbcli_request *req, const DATA_BLOB *blob)
+{
+ smbcli_req_grow_allocation(req, req->out.data_size + blob->length);
+ memcpy(req->out.data + req->out.data_size, blob->data, blob->length);
+ smbcli_req_grow_data(req, req->out.data_size + blob->length);
+ return blob->length;
+}
+
+/*
+ append raw bytes into the data portion of the request packet
+ return the number of bytes added
+*/
+size_t smbcli_req_append_bytes(struct smbcli_request *req, const uint8_t *bytes, size_t byte_len)
+{
+ smbcli_req_grow_allocation(req, byte_len + req->out.data_size);
+ memcpy(req->out.data + req->out.data_size, bytes, byte_len);
+ smbcli_req_grow_data(req, byte_len + req->out.data_size);
+ return byte_len;
+}
+
+/*
+ append variable block (type 5 buffer) into the data portion of the request packet
+ return the number of bytes added
+*/
+size_t smbcli_req_append_var_block(struct smbcli_request *req, const uint8_t *bytes, uint16_t byte_len)
+{
+ smbcli_req_grow_allocation(req, byte_len + 3 + req->out.data_size);
+ SCVAL(req->out.data + req->out.data_size, 0, 5);
+ SSVAL(req->out.data + req->out.data_size, 1, byte_len); /* add field length */
+ if (byte_len > 0) {
+ memcpy(req->out.data + req->out.data_size + 3, bytes, byte_len);
+ }
+ smbcli_req_grow_data(req, byte_len + 3 + req->out.data_size);
+ return byte_len + 3;
+}
+
+
+/*
+ pull a UCS2 string from a request packet, returning a talloced unix string
+
+ the string length is limited by the 3 things:
+ - the data size in the request (end of packet)
+ - the passed 'byte_len' if it is not -1
+ - the end of string (null termination)
+
+ Note that 'byte_len' is the number of bytes in the packet
+
+ on failure zero is returned and *dest is set to NULL, otherwise the number
+ of bytes consumed in the packet is returned
+*/
+static size_t smbcli_req_pull_ucs2(struct request_bufinfo *bufinfo, TALLOC_CTX *mem_ctx,
+ char **dest, const uint8_t *src, int byte_len, uint_t flags)
+{
+ int src_len, src_len2, alignment=0;
+ bool ret;
+ size_t ret_size;
+
+ if (!(flags & STR_NOALIGN) && ucs2_align(bufinfo->align_base, src, flags)) {
+ src++;
+ alignment=1;
+ if (byte_len != -1) {
+ byte_len--;
+ }
+ }
+
+ src_len = bufinfo->data_size - PTR_DIFF(src, bufinfo->data);
+ if (src_len < 0) {
+ *dest = NULL;
+ return 0;
+ }
+ if (byte_len != -1 && src_len > byte_len) {
+ src_len = byte_len;
+ }
+
+ src_len2 = utf16_len_n(src, src_len);
+
+ /* ucs2 strings must be at least 2 bytes long */
+ if (src_len2 < 2) {
+ *dest = NULL;
+ return 0;
+ }
+
+ ret = convert_string_talloc(mem_ctx, CH_UTF16, CH_UNIX, src, src_len2, (void **)dest, &ret_size, false);
+ if (!ret) {
+ *dest = NULL;
+ return 0;
+ }
+
+ return src_len2 + alignment;
+}
+
+/*
+ pull a ascii string from a request packet, returning a talloced string
+
+ the string length is limited by the 3 things:
+ - the data size in the request (end of packet)
+ - the passed 'byte_len' if it is not -1
+ - the end of string (null termination)
+
+ Note that 'byte_len' is the number of bytes in the packet
+
+ on failure zero is returned and *dest is set to NULL, otherwise the number
+ of bytes consumed in the packet is returned
+*/
+size_t smbcli_req_pull_ascii(struct request_bufinfo *bufinfo, TALLOC_CTX *mem_ctx,
+ char **dest, const uint8_t *src, int byte_len, uint_t flags)
+{
+ int src_len, src_len2;
+ bool ret;
+ size_t ret_size;
+
+ src_len = bufinfo->data_size - PTR_DIFF(src, bufinfo->data);
+ if (src_len < 0) {
+ *dest = NULL;
+ return 0;
+ }
+ if (byte_len != -1 && src_len > byte_len) {
+ src_len = byte_len;
+ }
+ src_len2 = strnlen((const char *)src, src_len);
+ if (src_len2 < src_len - 1) {
+ /* include the termination if we didn't reach the end of the packet */
+ src_len2++;
+ }
+
+ ret = convert_string_talloc(mem_ctx, CH_DOS, CH_UNIX, src, src_len2, (void **)dest, &ret_size, false);
+
+ if (!ret) {
+ *dest = NULL;
+ return 0;
+ }
+
+ return ret_size;
+}
+
+/**
+ pull a string from a request packet, returning a talloced string
+
+ the string length is limited by the 3 things:
+ - the data size in the request (end of packet)
+ - the passed 'byte_len' if it is not -1
+ - the end of string (null termination)
+
+ Note that 'byte_len' is the number of bytes in the packet
+
+ on failure zero is returned and *dest is set to NULL, otherwise the number
+ of bytes consumed in the packet is returned
+*/
+size_t smbcli_req_pull_string(struct request_bufinfo *bufinfo, TALLOC_CTX *mem_ctx,
+ char **dest, const uint8_t *src, int byte_len, uint_t flags)
+{
+ if (!(flags & STR_ASCII) &&
+ (((flags & STR_UNICODE) || (bufinfo->flags & BUFINFO_FLAG_UNICODE)))) {
+ return smbcli_req_pull_ucs2(bufinfo, mem_ctx, dest, src, byte_len, flags);
+ }
+
+ return smbcli_req_pull_ascii(bufinfo, mem_ctx, dest, src, byte_len, flags);
+}
+
+
+/**
+ pull a DATA_BLOB from a reply packet, returning a talloced blob
+ make sure we don't go past end of packet
+
+ if byte_len is -1 then limit the blob only by packet size
+*/
+DATA_BLOB smbcli_req_pull_blob(struct request_bufinfo *bufinfo, TALLOC_CTX *mem_ctx, const uint8_t *src, int byte_len)
+{
+ int src_len;
+
+ src_len = bufinfo->data_size - PTR_DIFF(src, bufinfo->data);
+
+ if (src_len < 0) {
+ return data_blob(NULL, 0);
+ }
+
+ if (byte_len != -1 && src_len > byte_len) {
+ src_len = byte_len;
+ }
+
+ return data_blob_talloc(mem_ctx, src, src_len);
+}
+
+/* check that a lump of data in a request is within the bounds of the data section of
+ the packet */
+static bool smbcli_req_data_oob(struct request_bufinfo *bufinfo, const uint8_t *ptr, uint32_t count)
+{
+ /* be careful with wraparound! */
+ if ((uintptr_t)ptr < (uintptr_t)bufinfo->data ||
+ (uintptr_t)ptr >= (uintptr_t)bufinfo->data + bufinfo->data_size ||
+ count > bufinfo->data_size ||
+ (uintptr_t)ptr + count > (uintptr_t)bufinfo->data + bufinfo->data_size) {
+ return true;
+ }
+ return false;
+}
+
+/*
+ pull a lump of data from a request packet
+
+ return false if any part is outside the data portion of the packet
+*/
+bool smbcli_raw_pull_data(struct request_bufinfo *bufinfo, const uint8_t *src, int len, uint8_t *dest)
+{
+ if (len == 0) return true;
+
+ if (smbcli_req_data_oob(bufinfo, src, len)) {
+ return false;
+ }
+
+ memcpy(dest, src, len);
+ return true;
+}
+
+
+/*
+ put a NTTIME into a packet
+*/
+void smbcli_push_nttime(void *base, uint16_t offset, NTTIME t)
+{
+ SBVAL(base, offset, t);
+}
+
+/*
+ pull a NTTIME from a packet
+*/
+NTTIME smbcli_pull_nttime(void *base, uint16_t offset)
+{
+ NTTIME ret = BVAL(base, offset);
+ return ret;
+}
+
+/**
+ pull a UCS2 string from a blob, returning a talloced unix string
+
+ the string length is limited by the 3 things:
+ - the data size in the blob
+ - the passed 'byte_len' if it is not -1
+ - the end of string (null termination)
+
+ Note that 'byte_len' is the number of bytes in the packet
+
+ on failure zero is returned and *dest is set to NULL, otherwise the number
+ of bytes consumed in the blob is returned
+*/
+size_t smbcli_blob_pull_ucs2(TALLOC_CTX* mem_ctx,
+ const DATA_BLOB *blob, const char **dest,
+ const uint8_t *src, int byte_len, uint_t flags)
+{
+ int src_len, src_len2, alignment=0;
+ size_t ret_size;
+ bool ret;
+ char *dest2;
+
+ if (src < blob->data ||
+ src >= (blob->data + blob->length)) {
+ *dest = NULL;
+ return 0;
+ }
+
+ src_len = blob->length - PTR_DIFF(src, blob->data);
+
+ if (byte_len != -1 && src_len > byte_len) {
+ src_len = byte_len;
+ }
+
+ if (!(flags & STR_NOALIGN) && ucs2_align(blob->data, src, flags)) {
+ src++;
+ alignment=1;
+ src_len--;
+ }
+
+ if (src_len < 2) {
+ *dest = NULL;
+ return 0;
+ }
+
+ src_len2 = utf16_len_n(src, src_len);
+
+ ret = convert_string_talloc(mem_ctx, CH_UTF16, CH_UNIX, src, src_len2, (void **)&dest2, &ret_size, false);
+ if (!ret) {
+ *dest = NULL;
+ return 0;
+ }
+ *dest = dest2;
+
+ return src_len2 + alignment;
+}
+
+/**
+ pull a ascii string from a blob, returning a talloced string
+
+ the string length is limited by the 3 things:
+ - the data size in the blob
+ - the passed 'byte_len' if it is not -1
+ - the end of string (null termination)
+
+ Note that 'byte_len' is the number of bytes in the blob
+
+ on failure zero is returned and *dest is set to NULL, otherwise the number
+ of bytes consumed in the blob is returned
+*/
+static size_t smbcli_blob_pull_ascii(TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *blob, const char **dest,
+ const uint8_t *src, int byte_len, uint_t flags)
+{
+ int src_len, src_len2;
+ size_t ret_size;
+ bool ret;
+ char *dest2;
+
+ src_len = blob->length - PTR_DIFF(src, blob->data);
+ if (src_len < 0) {
+ *dest = NULL;
+ return 0;
+ }
+ if (byte_len != -1 && src_len > byte_len) {
+ src_len = byte_len;
+ }
+ src_len2 = strnlen((const char *)src, src_len);
+
+ if (src_len2 < src_len - 1) {
+ /* include the termination if we didn't reach the end of the packet */
+ src_len2++;
+ }
+
+ ret = convert_string_talloc(mem_ctx, CH_DOS, CH_UNIX, src, src_len2, (void **)&dest2, &ret_size, false);
+
+ if (!ret) {
+ *dest = NULL;
+ return 0;
+ }
+ *dest = dest2;
+
+ return ret_size;
+}
+
+/**
+ pull a string from a blob, returning a talloced struct smb_wire_string
+
+ the string length is limited by the 3 things:
+ - the data size in the blob
+ - length field on the wire
+ - the end of string (null termination)
+
+ if STR_LEN8BIT is set in the flags then assume the length field is
+ 8 bits, instead of 32
+
+ on failure zero is returned and dest->s is set to NULL, otherwise the number
+ of bytes consumed in the blob is returned
+*/
+size_t smbcli_blob_pull_string(struct smbcli_session *session,
+ TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *blob,
+ struct smb_wire_string *dest,
+ uint16_t len_offset, uint16_t str_offset,
+ uint_t flags)
+{
+ int extra;
+ dest->s = NULL;
+
+ if (!(flags & STR_ASCII)) {
+ /* this is here to cope with SMB2 calls using the SMB
+ parsers. SMB2 will pass smbcli_session==NULL, which forces
+ unicode on (as used by SMB2) */
+ if (session == NULL) {
+ flags |= STR_UNICODE;
+ } else if (session->transport->negotiate.capabilities & CAP_UNICODE) {
+ flags |= STR_UNICODE;
+ }
+ }
+
+ if (flags & STR_LEN8BIT) {
+ if (len_offset > blob->length-1) {
+ return 0;
+ }
+ dest->private_length = CVAL(blob->data, len_offset);
+ } else {
+ if (len_offset > blob->length-4) {
+ return 0;
+ }
+ dest->private_length = IVAL(blob->data, len_offset);
+ }
+ extra = 0;
+ dest->s = NULL;
+ if (!(flags & STR_ASCII) && (flags & STR_UNICODE)) {
+ int align = 0;
+ if ((str_offset&1) && !(flags & STR_NOALIGN)) {
+ align = 1;
+ }
+ if (flags & STR_LEN_NOTERM) {
+ extra = 2;
+ }
+ return align + extra + smbcli_blob_pull_ucs2(mem_ctx, blob, &dest->s,
+ blob->data+str_offset+align,
+ dest->private_length, flags);
+ }
+
+ if (flags & STR_LEN_NOTERM) {
+ extra = 1;
+ }
+
+ return extra + smbcli_blob_pull_ascii(mem_ctx, blob, &dest->s,
+ blob->data+str_offset, dest->private_length, flags);
+}
+
+/**
+ pull a string from a blob, returning a talloced char *
+
+ Currently only used by the UNIX search info level.
+
+ the string length is limited by 2 things:
+ - the data size in the blob
+ - the end of string (null termination)
+
+ on failure zero is returned and dest->s is set to NULL, otherwise the number
+ of bytes consumed in the blob is returned
+*/
+size_t smbcli_blob_pull_unix_string(struct smbcli_session *session,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *blob,
+ const char **dest,
+ uint16_t str_offset,
+ uint_t flags)
+{
+ int extra = 0;
+ *dest = NULL;
+
+ if (!(flags & STR_ASCII) &&
+ ((flags & STR_UNICODE) ||
+ (session->transport->negotiate.capabilities & CAP_UNICODE))) {
+ int align = 0;
+ if ((str_offset&1) && !(flags & STR_NOALIGN)) {
+ align = 1;
+ }
+ if (flags & STR_LEN_NOTERM) {
+ extra = 2;
+ }
+ return align + extra + smbcli_blob_pull_ucs2(mem_ctx, blob, dest,
+ blob->data+str_offset+align,
+ -1, flags);
+ }
+
+ if (flags & STR_LEN_NOTERM) {
+ extra = 1;
+ }
+
+ return extra + smbcli_blob_pull_ascii(mem_ctx, blob, dest,
+ blob->data+str_offset, -1, flags);
+}
+
+
+/*
+ append a string into a blob
+*/
+size_t smbcli_blob_append_string(struct smbcli_session *session,
+ TALLOC_CTX *mem_ctx, DATA_BLOB *blob,
+ const char *str, uint_t flags)
+{
+ size_t max_len;
+ int len;
+
+ if (!str) return 0;
+
+ /* determine string type to use */
+ if (!(flags & (STR_ASCII|STR_UNICODE))) {
+ flags |= (session->transport->negotiate.capabilities & CAP_UNICODE) ? STR_UNICODE : STR_ASCII;
+ }
+
+ max_len = (strlen(str)+2) * MAX_BYTES_PER_CHAR;
+
+ blob->data = talloc_realloc(mem_ctx, blob->data, uint8_t, blob->length + max_len);
+ if (!blob->data) {
+ return 0;
+ }
+
+ len = push_string(blob->data + blob->length, str, max_len, flags);
+
+ blob->length += len;
+
+ return len;
+}
+
+/*
+ pull a GUID structure from the wire. The buffer must be at least 16
+ bytes long
+ */
+enum ndr_err_code smbcli_pull_guid(void *base, uint16_t offset,
+ struct GUID *guid)
+{
+ DATA_BLOB blob;
+ TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+ enum ndr_err_code ndr_err;
+
+ ZERO_STRUCTP(guid);
+
+ blob.data = offset + (uint8_t *)base;
+ blob.length = 16;
+ ndr_err = ndr_pull_struct_blob(&blob, tmp_ctx, NULL, guid,
+ (ndr_pull_flags_fn_t)ndr_pull_GUID);
+ talloc_free(tmp_ctx);
+ return ndr_err;
+}
+
+/*
+ push a guid onto the wire. The buffer must hold 16 bytes
+ */
+enum ndr_err_code smbcli_push_guid(void *base, uint16_t offset,
+ const struct GUID *guid)
+{
+ TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+ enum ndr_err_code ndr_err;
+ DATA_BLOB blob;
+ ndr_err = ndr_push_struct_blob(&blob, tmp_ctx, NULL,
+ guid, (ndr_push_flags_fn_t)ndr_push_GUID);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err) || blob.length != 16) {
+ talloc_free(tmp_ctx);
+ return ndr_err;
+ }
+ memcpy(offset + (uint8_t *)base, blob.data, blob.length);
+ talloc_free(tmp_ctx);
+ return ndr_err;
+}
diff --git a/source4/libcli/raw/rawsearch.c b/source4/libcli/raw/rawsearch.c
new file mode 100644
index 0000000000..9c90d45492
--- /dev/null
+++ b/source4/libcli/raw/rawsearch.c
@@ -0,0 +1,841 @@
+/*
+ Unix SMB/CIFS implementation.
+ client directory search routines
+ Copyright (C) James Myers 2003 <myersjj@samba.org>
+ Copyright (C) James Peach 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+
+/****************************************************************************
+ Old style search backend - process output.
+****************************************************************************/
+static void smb_raw_search_backend(struct smbcli_request *req,
+ TALLOC_CTX *mem_ctx,
+ uint16_t count,
+ void *private_data,
+ smbcli_search_callback callback)
+
+{
+ union smb_search_data search_data;
+ int i;
+ uint8_t *p;
+
+ if (req->in.data_size < 3 + count*43) {
+ req->status = NT_STATUS_INVALID_PARAMETER;
+ return;
+ }
+
+ p = req->in.data + 3;
+
+ for (i=0; i < count; i++) {
+ char *name;
+
+ search_data.search.id.reserved = CVAL(p, 0);
+ memcpy(search_data.search.id.name, p+1, 11);
+ search_data.search.id.handle = CVAL(p, 12);
+ search_data.search.id.server_cookie = IVAL(p, 13);
+ search_data.search.id.client_cookie = IVAL(p, 17);
+ search_data.search.attrib = CVAL(p, 21);
+ search_data.search.write_time = raw_pull_dos_date(req->transport,
+ p + 22);
+ search_data.search.size = IVAL(p, 26);
+ smbcli_req_pull_ascii(&req->in.bufinfo, mem_ctx, &name, p+30, 13, STR_ASCII);
+ search_data.search.name = name;
+ if (!callback(private_data, &search_data)) {
+ break;
+ }
+ p += 43;
+ }
+}
+
+/****************************************************************************
+ Old style search first.
+****************************************************************************/
+static NTSTATUS smb_raw_search_first_old(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ union smb_search_first *io, void *private_data,
+ smbcli_search_callback callback)
+
+{
+ struct smbcli_request *req;
+ uint8_t op = SMBsearch;
+
+ if (io->generic.level == RAW_SEARCH_FFIRST) {
+ op = SMBffirst;
+ } else if (io->generic.level == RAW_SEARCH_FUNIQUE) {
+ op = SMBfunique;
+ }
+
+ req = smbcli_request_setup(tree, op, 2, 0);
+ if (!req) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ SSVAL(req->out.vwv, VWV(0), io->search_first.in.max_count);
+ SSVAL(req->out.vwv, VWV(1), io->search_first.in.search_attrib);
+ smbcli_req_append_ascii4(req, io->search_first.in.pattern, STR_TERMINATE);
+ smbcli_req_append_var_block(req, NULL, 0);
+
+ if (!smbcli_request_send(req) ||
+ !smbcli_request_receive(req)) {
+ return smbcli_request_destroy(req);
+ }
+
+ if (NT_STATUS_IS_OK(req->status)) {
+ io->search_first.out.count = SVAL(req->in.vwv, VWV(0));
+ smb_raw_search_backend(req, mem_ctx, io->search_first.out.count, private_data, callback);
+ }
+
+ return smbcli_request_destroy(req);
+}
+
+/****************************************************************************
+ Old style search next.
+****************************************************************************/
+static NTSTATUS smb_raw_search_next_old(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ union smb_search_next *io, void *private_data,
+ smbcli_search_callback callback)
+
+{
+ struct smbcli_request *req;
+ uint8_t var_block[21];
+ uint8_t op = SMBsearch;
+
+ if (io->generic.level == RAW_SEARCH_FFIRST) {
+ op = SMBffirst;
+ }
+
+ req = smbcli_request_setup(tree, op, 2, 0);
+ if (!req) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ SSVAL(req->out.vwv, VWV(0), io->search_next.in.max_count);
+ SSVAL(req->out.vwv, VWV(1), io->search_next.in.search_attrib);
+ smbcli_req_append_ascii4(req, "", STR_TERMINATE);
+
+ SCVAL(var_block, 0, io->search_next.in.id.reserved);
+ memcpy(&var_block[1], io->search_next.in.id.name, 11);
+ SCVAL(var_block, 12, io->search_next.in.id.handle);
+ SIVAL(var_block, 13, io->search_next.in.id.server_cookie);
+ SIVAL(var_block, 17, io->search_next.in.id.client_cookie);
+
+ smbcli_req_append_var_block(req, var_block, 21);
+
+ if (!smbcli_request_send(req) ||
+ !smbcli_request_receive(req)) {
+ return smbcli_request_destroy(req);
+ }
+
+ if (NT_STATUS_IS_OK(req->status)) {
+ io->search_next.out.count = SVAL(req->in.vwv, VWV(0));
+ smb_raw_search_backend(req, mem_ctx, io->search_next.out.count, private_data, callback);
+ }
+
+ return smbcli_request_destroy(req);
+}
+
+
+/****************************************************************************
+ Old style search next.
+****************************************************************************/
+static NTSTATUS smb_raw_search_close_old(struct smbcli_tree *tree,
+ union smb_search_close *io)
+{
+ struct smbcli_request *req;
+ uint8_t var_block[21];
+
+ req = smbcli_request_setup(tree, SMBfclose, 2, 0);
+ if (!req) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ SSVAL(req->out.vwv, VWV(0), io->fclose.in.max_count);
+ SSVAL(req->out.vwv, VWV(1), io->fclose.in.search_attrib);
+ smbcli_req_append_ascii4(req, "", STR_TERMINATE);
+
+ SCVAL(var_block, 0, io->fclose.in.id.reserved);
+ memcpy(&var_block[1], io->fclose.in.id.name, 11);
+ SCVAL(var_block, 12, io->fclose.in.id.handle);
+ SIVAL(var_block, 13, io->fclose.in.id.server_cookie);
+ SIVAL(var_block, 17, io->fclose.in.id.client_cookie);
+
+ smbcli_req_append_var_block(req, var_block, 21);
+
+ if (!smbcli_request_send(req) ||
+ !smbcli_request_receive(req)) {
+ return smbcli_request_destroy(req);
+ }
+
+ return smbcli_request_destroy(req);
+}
+
+
+
+/****************************************************************************
+ Very raw search first - returns param/data blobs.
+****************************************************************************/
+static NTSTATUS smb_raw_search_first_blob(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx, /* used to allocate output blobs */
+ union smb_search_first *io,
+ DATA_BLOB *out_param_blob,
+ DATA_BLOB *out_data_blob)
+{
+ struct smb_trans2 tp;
+ uint16_t setup = TRANSACT2_FINDFIRST;
+ NTSTATUS status;
+
+ tp.in.max_setup = 0;
+ tp.in.flags = 0;
+ tp.in.timeout = 0;
+ tp.in.setup_count = 1;
+ tp.in.data = data_blob(NULL, 0);
+ tp.in.max_param = 10;
+ tp.in.max_data = 0xFFFF;
+ tp.in.setup = &setup;
+
+ if (io->t2ffirst.level != RAW_SEARCH_TRANS2) {
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ if (io->t2ffirst.data_level >= RAW_SEARCH_DATA_GENERIC) {
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ if (io->t2ffirst.data_level == RAW_SEARCH_DATA_EA_LIST) {
+ if (!ea_push_name_list(mem_ctx,
+ &tp.in.data,
+ io->t2ffirst.in.num_names,
+ io->t2ffirst.in.ea_names)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ tp.in.params = data_blob_talloc(mem_ctx, NULL, 12);
+ if (!tp.in.params.data) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ SSVAL(tp.in.params.data, 0, io->t2ffirst.in.search_attrib);
+ SSVAL(tp.in.params.data, 2, io->t2ffirst.in.max_count);
+ SSVAL(tp.in.params.data, 4, io->t2ffirst.in.flags);
+ SSVAL(tp.in.params.data, 6, io->t2ffirst.data_level);
+ SIVAL(tp.in.params.data, 8, io->t2ffirst.in.storage_type);
+
+ smbcli_blob_append_string(tree->session, mem_ctx, &tp.in.params,
+ io->t2ffirst.in.pattern, STR_TERMINATE);
+
+ status = smb_raw_trans2(tree, mem_ctx, &tp);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ out_param_blob->length = tp.out.params.length;
+ out_param_blob->data = tp.out.params.data;
+ out_data_blob->length = tp.out.data.length;
+ out_data_blob->data = tp.out.data.data;
+
+ return NT_STATUS_OK;
+}
+
+
+/****************************************************************************
+ Very raw search first - returns param/data blobs.
+ Used in CIFS-on-CIFS NTVFS.
+****************************************************************************/
+static NTSTATUS smb_raw_search_next_blob(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ union smb_search_next *io,
+ DATA_BLOB *out_param_blob,
+ DATA_BLOB *out_data_blob)
+{
+ struct smb_trans2 tp;
+ uint16_t setup = TRANSACT2_FINDNEXT;
+ NTSTATUS status;
+
+ tp.in.max_setup = 0;
+ tp.in.flags = 0;
+ tp.in.timeout = 0;
+ tp.in.setup_count = 1;
+ tp.in.data = data_blob(NULL, 0);
+ tp.in.max_param = 10;
+ tp.in.max_data = 0xFFFF;
+ tp.in.setup = &setup;
+
+ if (io->t2fnext.level != RAW_SEARCH_TRANS2) {
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ if (io->t2fnext.data_level >= RAW_SEARCH_DATA_GENERIC) {
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ if (io->t2fnext.data_level == RAW_SEARCH_DATA_EA_LIST) {
+ if (!ea_push_name_list(mem_ctx,
+ &tp.in.data,
+ io->t2fnext.in.num_names,
+ io->t2fnext.in.ea_names)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ tp.in.params = data_blob_talloc(mem_ctx, NULL, 12);
+ if (!tp.in.params.data) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ SSVAL(tp.in.params.data, 0, io->t2fnext.in.handle);
+ SSVAL(tp.in.params.data, 2, io->t2fnext.in.max_count);
+ SSVAL(tp.in.params.data, 4, io->t2fnext.data_level);
+ SIVAL(tp.in.params.data, 6, io->t2fnext.in.resume_key);
+ SSVAL(tp.in.params.data, 10, io->t2fnext.in.flags);
+
+ smbcli_blob_append_string(tree->session, mem_ctx, &tp.in.params,
+ io->t2fnext.in.last_name,
+ STR_TERMINATE);
+
+ status = smb_raw_trans2(tree, mem_ctx, &tp);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ out_param_blob->length = tp.out.params.length;
+ out_param_blob->data = tp.out.params.data;
+ out_data_blob->length = tp.out.data.length;
+ out_data_blob->data = tp.out.data.data;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ parse the wire search formats that are in common between SMB and
+ SMB2
+*/
+NTSTATUS smb_raw_search_common(TALLOC_CTX *mem_ctx,
+ enum smb_search_data_level level,
+ const DATA_BLOB *blob,
+ union smb_search_data *data,
+ uint_t *next_ofs,
+ uint_t str_flags)
+{
+ uint_t len, blen;
+
+ if (blob->length < 4) {
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+
+ *next_ofs = IVAL(blob->data, 0);
+ if (*next_ofs != 0) {
+ blen = *next_ofs;
+ } else {
+ blen = blob->length;
+ }
+
+ switch (level) {
+ case RAW_SEARCH_DATA_DIRECTORY_INFO:
+ if (blen < 65) return NT_STATUS_INFO_LENGTH_MISMATCH;
+ data->directory_info.file_index = IVAL(blob->data, 4);
+ data->directory_info.create_time = smbcli_pull_nttime(blob->data, 8);
+ data->directory_info.access_time = smbcli_pull_nttime(blob->data, 16);
+ data->directory_info.write_time = smbcli_pull_nttime(blob->data, 24);
+ data->directory_info.change_time = smbcli_pull_nttime(blob->data, 32);
+ data->directory_info.size = BVAL(blob->data, 40);
+ data->directory_info.alloc_size = BVAL(blob->data, 48);
+ data->directory_info.attrib = IVAL(blob->data, 56);
+ len = smbcli_blob_pull_string(NULL, mem_ctx, blob,
+ &data->directory_info.name,
+ 60, 64, str_flags);
+ if (*next_ofs != 0 && *next_ofs < 64+len) {
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+ return NT_STATUS_OK;
+
+ case RAW_SEARCH_DATA_FULL_DIRECTORY_INFO:
+ if (blen < 69) return NT_STATUS_INFO_LENGTH_MISMATCH;
+ data->full_directory_info.file_index = IVAL(blob->data, 4);
+ data->full_directory_info.create_time = smbcli_pull_nttime(blob->data, 8);
+ data->full_directory_info.access_time = smbcli_pull_nttime(blob->data, 16);
+ data->full_directory_info.write_time = smbcli_pull_nttime(blob->data, 24);
+ data->full_directory_info.change_time = smbcli_pull_nttime(blob->data, 32);
+ data->full_directory_info.size = BVAL(blob->data, 40);
+ data->full_directory_info.alloc_size = BVAL(blob->data, 48);
+ data->full_directory_info.attrib = IVAL(blob->data, 56);
+ data->full_directory_info.ea_size = IVAL(blob->data, 64);
+ len = smbcli_blob_pull_string(NULL, mem_ctx, blob,
+ &data->full_directory_info.name,
+ 60, 68, str_flags);
+ if (*next_ofs != 0 && *next_ofs < 68+len) {
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+ return NT_STATUS_OK;
+
+ case RAW_SEARCH_DATA_NAME_INFO:
+ if (blen < 13) return NT_STATUS_INFO_LENGTH_MISMATCH;
+ data->name_info.file_index = IVAL(blob->data, 4);
+ len = smbcli_blob_pull_string(NULL, mem_ctx, blob,
+ &data->name_info.name,
+ 8, 12, str_flags);
+ if (*next_ofs != 0 && *next_ofs < 12+len) {
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+ return NT_STATUS_OK;
+
+
+ case RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO:
+ if (blen < 95) return NT_STATUS_INFO_LENGTH_MISMATCH;
+ data->both_directory_info.file_index = IVAL(blob->data, 4);
+ data->both_directory_info.create_time = smbcli_pull_nttime(blob->data, 8);
+ data->both_directory_info.access_time = smbcli_pull_nttime(blob->data, 16);
+ data->both_directory_info.write_time = smbcli_pull_nttime(blob->data, 24);
+ data->both_directory_info.change_time = smbcli_pull_nttime(blob->data, 32);
+ data->both_directory_info.size = BVAL(blob->data, 40);
+ data->both_directory_info.alloc_size = BVAL(blob->data, 48);
+ data->both_directory_info.attrib = IVAL(blob->data, 56);
+ data->both_directory_info.ea_size = IVAL(blob->data, 64);
+ smbcli_blob_pull_string(NULL, mem_ctx, blob,
+ &data->both_directory_info.short_name,
+ 68, 70, STR_LEN8BIT | STR_UNICODE);
+ len = smbcli_blob_pull_string(NULL, mem_ctx, blob,
+ &data->both_directory_info.name,
+ 60, 94, str_flags);
+ if (*next_ofs != 0 && *next_ofs < 94+len) {
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+ return NT_STATUS_OK;
+
+
+ case RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO:
+ if (blen < 81) return NT_STATUS_INFO_LENGTH_MISMATCH;
+ data->id_full_directory_info.file_index = IVAL(blob->data, 4);
+ data->id_full_directory_info.create_time = smbcli_pull_nttime(blob->data, 8);
+ data->id_full_directory_info.access_time = smbcli_pull_nttime(blob->data, 16);
+ data->id_full_directory_info.write_time = smbcli_pull_nttime(blob->data, 24);
+ data->id_full_directory_info.change_time = smbcli_pull_nttime(blob->data, 32);
+ data->id_full_directory_info.size = BVAL(blob->data, 40);
+ data->id_full_directory_info.alloc_size = BVAL(blob->data, 48);
+ data->id_full_directory_info.attrib = IVAL(blob->data, 56);
+ data->id_full_directory_info.ea_size = IVAL(blob->data, 64);
+ data->id_full_directory_info.file_id = BVAL(blob->data, 72);
+ len = smbcli_blob_pull_string(NULL, mem_ctx, blob,
+ &data->id_full_directory_info.name,
+ 60, 80, str_flags);
+ if (*next_ofs != 0 && *next_ofs < 80+len) {
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+ return NT_STATUS_OK;
+
+ case RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO:
+ if (blen < 105) return NT_STATUS_INFO_LENGTH_MISMATCH;
+ data->id_both_directory_info.file_index = IVAL(blob->data, 4);
+ data->id_both_directory_info.create_time = smbcli_pull_nttime(blob->data, 8);
+ data->id_both_directory_info.access_time = smbcli_pull_nttime(blob->data, 16);
+ data->id_both_directory_info.write_time = smbcli_pull_nttime(blob->data, 24);
+ data->id_both_directory_info.change_time = smbcli_pull_nttime(blob->data, 32);
+ data->id_both_directory_info.size = BVAL(blob->data, 40);
+ data->id_both_directory_info.alloc_size = BVAL(blob->data, 48);
+ data->id_both_directory_info.attrib = SVAL(blob->data, 56);
+ data->id_both_directory_info.ea_size = IVAL(blob->data, 64);
+ smbcli_blob_pull_string(NULL, mem_ctx, blob,
+ &data->id_both_directory_info.short_name,
+ 68, 70, STR_LEN8BIT | STR_UNICODE);
+ data->id_both_directory_info.file_id = BVAL(blob->data, 96);
+ len = smbcli_blob_pull_string(NULL, mem_ctx, blob,
+ &data->id_both_directory_info.name,
+ 60, 104, str_flags);
+ if (*next_ofs != 0 && *next_ofs < 104+len) {
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+ return NT_STATUS_OK;
+
+ default:
+ break;
+ }
+
+ /* invalid level */
+ return NT_STATUS_INVALID_INFO_CLASS;
+}
+
+
+/*
+ parse a trans2 search response.
+ Return the number of bytes consumed
+ return 0 for success with end of list
+ return -1 for a parse error
+*/
+static int parse_trans2_search(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ enum smb_search_data_level level,
+ uint16_t flags,
+ DATA_BLOB *blob,
+ union smb_search_data *data)
+{
+ uint_t len, ofs;
+ uint32_t ea_size;
+ DATA_BLOB eablob;
+ NTSTATUS status;
+
+ switch (level) {
+ case RAW_SEARCH_DATA_GENERIC:
+ case RAW_SEARCH_DATA_SEARCH:
+ /* handled elsewhere */
+ return -1;
+
+ case RAW_SEARCH_DATA_STANDARD:
+ if (flags & FLAG_TRANS2_FIND_REQUIRE_RESUME) {
+ if (blob->length < 4) return -1;
+ data->standard.resume_key = IVAL(blob->data, 0);
+ blob->data += 4;
+ blob->length -= 4;
+ }
+ if (blob->length < 24) return -1;
+ data->standard.create_time = raw_pull_dos_date2(tree->session->transport,
+ blob->data + 0);
+ data->standard.access_time = raw_pull_dos_date2(tree->session->transport,
+ blob->data + 4);
+ data->standard.write_time = raw_pull_dos_date2(tree->session->transport,
+ blob->data + 8);
+ data->standard.size = IVAL(blob->data, 12);
+ data->standard.alloc_size = IVAL(blob->data, 16);
+ data->standard.attrib = SVAL(blob->data, 20);
+ len = smbcli_blob_pull_string(tree->session, mem_ctx, blob,
+ &data->standard.name,
+ 22, 23, STR_LEN8BIT | STR_TERMINATE | STR_LEN_NOTERM);
+ return len + 23;
+
+ case RAW_SEARCH_DATA_EA_SIZE:
+ if (flags & FLAG_TRANS2_FIND_REQUIRE_RESUME) {
+ if (blob->length < 4) return -1;
+ data->ea_size.resume_key = IVAL(blob->data, 0);
+ blob->data += 4;
+ blob->length -= 4;
+ }
+ if (blob->length < 28) return -1;
+ data->ea_size.create_time = raw_pull_dos_date2(tree->session->transport,
+ blob->data + 0);
+ data->ea_size.access_time = raw_pull_dos_date2(tree->session->transport,
+ blob->data + 4);
+ data->ea_size.write_time = raw_pull_dos_date2(tree->session->transport,
+ blob->data + 8);
+ data->ea_size.size = IVAL(blob->data, 12);
+ data->ea_size.alloc_size = IVAL(blob->data, 16);
+ data->ea_size.attrib = SVAL(blob->data, 20);
+ data->ea_size.ea_size = IVAL(blob->data, 22);
+ len = smbcli_blob_pull_string(tree->session, mem_ctx, blob,
+ &data->ea_size.name,
+ 26, 27, STR_LEN8BIT | STR_TERMINATE | STR_NOALIGN);
+ return len + 27 + 1;
+
+ case RAW_SEARCH_DATA_EA_LIST:
+ if (flags & FLAG_TRANS2_FIND_REQUIRE_RESUME) {
+ if (blob->length < 4) return -1;
+ data->ea_list.resume_key = IVAL(blob->data, 0);
+ blob->data += 4;
+ blob->length -= 4;
+ }
+ if (blob->length < 28) return -1;
+ data->ea_list.create_time = raw_pull_dos_date2(tree->session->transport,
+ blob->data + 0);
+ data->ea_list.access_time = raw_pull_dos_date2(tree->session->transport,
+ blob->data + 4);
+ data->ea_list.write_time = raw_pull_dos_date2(tree->session->transport,
+ blob->data + 8);
+ data->ea_list.size = IVAL(blob->data, 12);
+ data->ea_list.alloc_size = IVAL(blob->data, 16);
+ data->ea_list.attrib = SVAL(blob->data, 20);
+ ea_size = IVAL(blob->data, 22);
+ if (ea_size > 0xFFFF) {
+ return -1;
+ }
+ eablob.data = blob->data + 22;
+ eablob.length = ea_size;
+ if (eablob.length > blob->length - 24) {
+ return -1;
+ }
+ status = ea_pull_list(&eablob, mem_ctx,
+ &data->ea_list.eas.num_eas,
+ &data->ea_list.eas.eas);
+ if (!NT_STATUS_IS_OK(status)) {
+ return -1;
+ }
+ len = smbcli_blob_pull_string(tree->session, mem_ctx, blob,
+ &data->ea_list.name,
+ 22+ea_size, 23+ea_size,
+ STR_LEN8BIT | STR_NOALIGN);
+ return len + ea_size + 23 + 1;
+
+ case RAW_SEARCH_DATA_UNIX_INFO:
+ if (blob->length < 109) return -1;
+ ofs = IVAL(blob->data, 0);
+ data->unix_info.file_index = IVAL(blob->data, 4);
+ data->unix_info.size = BVAL(blob->data, 8);
+ data->unix_info.alloc_size = BVAL(blob->data, 16);
+ data->unix_info.status_change_time = smbcli_pull_nttime(blob->data, 24);
+ data->unix_info.access_time = smbcli_pull_nttime(blob->data, 32);
+ data->unix_info.change_time = smbcli_pull_nttime(blob->data, 40);
+ data->unix_info.uid = IVAL(blob->data, 48);
+ data->unix_info.gid = IVAL(blob->data, 56);
+ data->unix_info.file_type = IVAL(blob->data, 64);
+ data->unix_info.dev_major = BVAL(blob->data, 68);
+ data->unix_info.dev_minor = BVAL(blob->data, 76);
+ data->unix_info.unique_id = BVAL(blob->data, 84);
+ data->unix_info.permissions = IVAL(blob->data, 92);
+ data->unix_info.nlink = IVAL(blob->data, 100);
+ /* There is no length field for this name but we know it's null terminated. */
+ len = smbcli_blob_pull_unix_string(tree->session, mem_ctx, blob,
+ &data->unix_info.name, 108, 0);
+ if (ofs != 0 && ofs < 108+len) {
+ return -1;
+ }
+ return ofs;
+
+ case RAW_SEARCH_DATA_UNIX_INFO2:
+ /* 8 - size of ofs + file_index
+ * 116 - size of unix_info2
+ * 4 - size of name length
+ * 2 - "." is the shortest name
+ */
+ if (blob->length < (116 + 8 + 4 + 2)) {
+ return -1;
+ }
+
+ ofs = IVAL(blob->data, 0);
+ data->unix_info2.file_index = IVAL(blob->data, 4);
+ data->unix_info2.end_of_file = BVAL(blob->data, 8);
+ data->unix_info2.num_bytes = BVAL(blob->data, 16);
+ data->unix_info2.status_change_time = smbcli_pull_nttime(blob->data, 24);
+ data->unix_info2.access_time = smbcli_pull_nttime(blob->data, 32);
+ data->unix_info2.change_time = smbcli_pull_nttime(blob->data, 40);
+ data->unix_info2.uid = IVAL(blob->data, 48);
+ data->unix_info2.gid = IVAL(blob->data, 56);
+ data->unix_info2.file_type = IVAL(blob->data, 64);
+ data->unix_info2.dev_major = BVAL(blob->data, 68);
+ data->unix_info2.dev_minor = BVAL(blob->data, 76);
+ data->unix_info2.unique_id = BVAL(blob->data, 84);
+ data->unix_info2.permissions = IVAL(blob->data, 92);
+ data->unix_info2.nlink = IVAL(blob->data, 100);
+ data->unix_info2.create_time = smbcli_pull_nttime(blob->data, 108);
+ data->unix_info2.file_flags = IVAL(blob->data, 116);
+ data->unix_info2.flags_mask = IVAL(blob->data, 120);
+
+ /* There is a 4 byte length field for this name. The length
+ * does not include the NULL terminator.
+ */
+ len = smbcli_blob_pull_string(tree->session, mem_ctx, blob,
+ &data->unix_info2.name,
+ 8 + 116, /* offset to length */
+ 8 + 116 + 4, /* offset to string */
+ 0);
+
+ if (ofs != 0 && ofs < (8 + 116 + 4 + len)) {
+ return -1;
+ }
+
+ return ofs;
+
+ case RAW_SEARCH_DATA_DIRECTORY_INFO:
+ case RAW_SEARCH_DATA_FULL_DIRECTORY_INFO:
+ case RAW_SEARCH_DATA_NAME_INFO:
+ case RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO:
+ case RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO:
+ case RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO: {
+ uint_t str_flags = STR_UNICODE;
+ if (!(tree->session->transport->negotiate.capabilities & CAP_UNICODE)) {
+ str_flags = STR_ASCII;
+ }
+
+ status = smb_raw_search_common(mem_ctx, level, blob, data, &ofs, str_flags);
+ if (!NT_STATUS_IS_OK(status)) {
+ return -1;
+ }
+ return ofs;
+ }
+ }
+
+ /* invalid level */
+ return -1;
+}
+
+/****************************************************************************
+ Trans2 search backend - process output.
+****************************************************************************/
+static NTSTATUS smb_raw_t2search_backend(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ enum smb_search_data_level level,
+ uint16_t flags,
+ int16_t count,
+ DATA_BLOB *blob,
+ void *private_data,
+ smbcli_search_callback callback)
+
+{
+ int i;
+ DATA_BLOB blob2;
+
+ blob2.data = blob->data;
+ blob2.length = blob->length;
+
+ for (i=0; i < count; i++) {
+ union smb_search_data search_data;
+ uint_t len;
+
+ len = parse_trans2_search(tree, mem_ctx, level, flags, &blob2, &search_data);
+ if (len == -1) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* the callback function can tell us that no more will
+ fit - in that case we stop, but it isn't an error */
+ if (!callback(private_data, &search_data)) {
+ break;
+ }
+
+ if (len == 0) break;
+
+ blob2.data += len;
+ blob2.length -= len;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/* Implements trans2findfirst2 and old search
+ */
+_PUBLIC_ NTSTATUS smb_raw_search_first(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ union smb_search_first *io, void *private_data,
+ smbcli_search_callback callback)
+{
+ DATA_BLOB p_blob, d_blob;
+ NTSTATUS status;
+
+ switch (io->generic.level) {
+ case RAW_SEARCH_SEARCH:
+ case RAW_SEARCH_FFIRST:
+ case RAW_SEARCH_FUNIQUE:
+ return smb_raw_search_first_old(tree, mem_ctx, io, private_data, callback);
+
+ case RAW_SEARCH_TRANS2:
+ break;
+
+ case RAW_SEARCH_SMB2:
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ status = smb_raw_search_first_blob(tree, mem_ctx,
+ io, &p_blob, &d_blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (p_blob.length < 10) {
+ DEBUG(1,("smb_raw_search_first: parms wrong size %d != expected_param_size\n",
+ (int)p_blob.length));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* process output data */
+ io->t2ffirst.out.handle = SVAL(p_blob.data, 0);
+ io->t2ffirst.out.count = SVAL(p_blob.data, 2);
+ io->t2ffirst.out.end_of_search = SVAL(p_blob.data, 4);
+
+ status = smb_raw_t2search_backend(tree, mem_ctx,
+ io->generic.data_level,
+ io->t2ffirst.in.flags, io->t2ffirst.out.count,
+ &d_blob, private_data, callback);
+
+ return status;
+}
+
+/* Implements trans2findnext2 and old smbsearch
+ */
+NTSTATUS smb_raw_search_next(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ union smb_search_next *io, void *private_data,
+ smbcli_search_callback callback)
+{
+ DATA_BLOB p_blob, d_blob;
+ NTSTATUS status;
+
+ switch (io->generic.level) {
+ case RAW_SEARCH_SEARCH:
+ case RAW_SEARCH_FFIRST:
+ return smb_raw_search_next_old(tree, mem_ctx, io, private_data, callback);
+
+ case RAW_SEARCH_FUNIQUE:
+ return NT_STATUS_INVALID_LEVEL;
+
+ case RAW_SEARCH_TRANS2:
+ break;
+
+ case RAW_SEARCH_SMB2:
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ status = smb_raw_search_next_blob(tree, mem_ctx,
+ io, &p_blob, &d_blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (p_blob.length != 8) {
+ DEBUG(1,("smb_raw_search_next: parms wrong size %d != expected_param_size\n",
+ (int)p_blob.length));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* process output data */
+ io->t2fnext.out.count = SVAL(p_blob.data, 0);
+ io->t2fnext.out.end_of_search = SVAL(p_blob.data, 2);
+
+ status = smb_raw_t2search_backend(tree, mem_ctx,
+ io->generic.data_level,
+ io->t2fnext.in.flags, io->t2fnext.out.count,
+ &d_blob, private_data, callback);
+
+ return status;
+}
+
+/*
+ Implements trans2findclose2
+ */
+NTSTATUS smb_raw_search_close(struct smbcli_tree *tree,
+ union smb_search_close *io)
+{
+ struct smbcli_request *req;
+
+ if (io->generic.level == RAW_FINDCLOSE_FCLOSE) {
+ return smb_raw_search_close_old(tree, io);
+ }
+
+ req = smbcli_request_setup(tree, SMBfindclose, 1, 0);
+ if (!req) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ SSVAL(req->out.vwv, VWV(0), io->findclose.in.handle);
+
+ if (smbcli_request_send(req)) {
+ (void) smbcli_request_receive(req);
+ }
+
+ return smbcli_request_destroy(req);
+}
diff --git a/source4/libcli/raw/rawsetfileinfo.c b/source4/libcli/raw/rawsetfileinfo.c
new file mode 100644
index 0000000000..f7dfb933f1
--- /dev/null
+++ b/source4/libcli/raw/rawsetfileinfo.c
@@ -0,0 +1,492 @@
+/*
+ Unix SMB/CIFS implementation.
+ RAW_SFILEINFO_* calls
+ Copyright (C) James Myers 2003
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) James Peach 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "librpc/gen_ndr/ndr_security.h"
+
+
+/*
+ Handle setfileinfo/setpathinfo passthu constructions
+*/
+bool smb_raw_setfileinfo_passthru(TALLOC_CTX *mem_ctx,
+ enum smb_setfileinfo_level level,
+ union smb_setfileinfo *parms,
+ DATA_BLOB *blob)
+{
+ uint_t len;
+
+#define NEED_BLOB(n) do { \
+ *blob = data_blob_talloc(mem_ctx, NULL, n); \
+ if (blob->data == NULL && n != 0) return false; \
+ } while (0)
+
+ switch (level) {
+ case RAW_SFILEINFO_BASIC_INFORMATION:
+ NEED_BLOB(40);
+ smbcli_push_nttime(blob->data, 0, parms->basic_info.in.create_time);
+ smbcli_push_nttime(blob->data, 8, parms->basic_info.in.access_time);
+ smbcli_push_nttime(blob->data, 16, parms->basic_info.in.write_time);
+ smbcli_push_nttime(blob->data, 24, parms->basic_info.in.change_time);
+ SIVAL(blob->data, 32, parms->basic_info.in.attrib);
+ SIVAL(blob->data, 36, 0); /* padding */
+ return true;
+
+ case RAW_SFILEINFO_DISPOSITION_INFORMATION:
+ NEED_BLOB(4);
+ SIVAL(blob->data, 0, parms->disposition_info.in.delete_on_close);
+ return true;
+
+ case RAW_SFILEINFO_ALLOCATION_INFORMATION:
+ NEED_BLOB(8);
+ SBVAL(blob->data, 0, parms->allocation_info.in.alloc_size);
+ return true;
+
+ case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
+ NEED_BLOB(8);
+ SBVAL(blob->data, 0, parms->end_of_file_info.in.size);
+ return true;
+
+ case RAW_SFILEINFO_RENAME_INFORMATION:
+ NEED_BLOB(12);
+ SIVAL(blob->data, 0, parms->rename_information.in.overwrite);
+ SIVAL(blob->data, 4, parms->rename_information.in.root_fid);
+ len = smbcli_blob_append_string(NULL, mem_ctx, blob,
+ parms->rename_information.in.new_name,
+ STR_UNICODE|STR_TERMINATE);
+ SIVAL(blob->data, 8, len - 2);
+ return true;
+
+ case RAW_SFILEINFO_RENAME_INFORMATION_SMB2:
+ NEED_BLOB(20);
+ SIVAL(blob->data, 0, parms->rename_information.in.overwrite);
+ SBVAL(blob->data, 8, parms->rename_information.in.root_fid);
+ len = smbcli_blob_append_string(NULL, mem_ctx, blob,
+ parms->rename_information.in.new_name,
+ STR_UNICODE|STR_TERMINATE);
+ SIVAL(blob->data, 16, len - 2);
+ return true;
+
+ case RAW_SFILEINFO_POSITION_INFORMATION:
+ NEED_BLOB(8);
+ SBVAL(blob->data, 0, parms->position_information.in.position);
+ return true;
+
+ case RAW_SFILEINFO_MODE_INFORMATION:
+ NEED_BLOB(4);
+ SIVAL(blob->data, 0, parms->mode_information.in.mode);
+ return true;
+
+ case RAW_FILEINFO_SEC_DESC: {
+ enum ndr_err_code ndr_err;
+
+ ndr_err = ndr_push_struct_blob(blob, mem_ctx, NULL,
+ parms->set_secdesc.in.sd,
+ (ndr_push_flags_fn_t)ndr_push_security_descriptor);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ case RAW_SFILEINFO_FULL_EA_INFORMATION:
+ printf("num_eas=%d\n", parms->full_ea_information.in.eas.num_eas);
+ NEED_BLOB(ea_list_size_chained(
+ parms->full_ea_information.in.eas.num_eas,
+ parms->full_ea_information.in.eas.eas, 4));
+ ea_put_list_chained(blob->data,
+ parms->full_ea_information.in.eas.num_eas,
+ parms->full_ea_information.in.eas.eas, 4);
+ return true;
+
+ /* Unhandled levels */
+ case RAW_SFILEINFO_PIPE_INFORMATION:
+ case RAW_SFILEINFO_VALID_DATA_INFORMATION:
+ case RAW_SFILEINFO_SHORT_NAME_INFORMATION:
+ case RAW_SFILEINFO_1025:
+ case RAW_SFILEINFO_1027:
+ case RAW_SFILEINFO_1029:
+ case RAW_SFILEINFO_1030:
+ case RAW_SFILEINFO_1031:
+ case RAW_SFILEINFO_1032:
+ case RAW_SFILEINFO_1036:
+ case RAW_SFILEINFO_1041:
+ case RAW_SFILEINFO_1042:
+ case RAW_SFILEINFO_1043:
+ case RAW_SFILEINFO_1044:
+ break;
+
+ default:
+ DEBUG(0,("Unhandled setfileinfo passthru level %d\n", level));
+ return false;
+ }
+
+ return false;
+}
+
+/*
+ Handle setfileinfo/setpathinfo trans2 backend.
+*/
+static bool smb_raw_setinfo_backend(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ union smb_setfileinfo *parms,
+ DATA_BLOB *blob)
+{
+ switch (parms->generic.level) {
+ case RAW_SFILEINFO_GENERIC:
+ case RAW_SFILEINFO_SETATTR:
+ case RAW_SFILEINFO_SETATTRE:
+ case RAW_SFILEINFO_SEC_DESC:
+ /* not handled here */
+ return false;
+
+ case RAW_SFILEINFO_STANDARD:
+ NEED_BLOB(12);
+ raw_push_dos_date2(tree->session->transport,
+ blob->data, 0, parms->standard.in.create_time);
+ raw_push_dos_date2(tree->session->transport,
+ blob->data, 4, parms->standard.in.access_time);
+ raw_push_dos_date2(tree->session->transport,
+ blob->data, 8, parms->standard.in.write_time);
+ return true;
+
+ case RAW_SFILEINFO_EA_SET:
+ NEED_BLOB(ea_list_size(parms->ea_set.in.num_eas, parms->ea_set.in.eas));
+ ea_put_list(blob->data, parms->ea_set.in.num_eas, parms->ea_set.in.eas);
+ return true;
+
+ case RAW_SFILEINFO_BASIC_INFO:
+ case RAW_SFILEINFO_BASIC_INFORMATION:
+ return smb_raw_setfileinfo_passthru(mem_ctx, RAW_SFILEINFO_BASIC_INFORMATION,
+ parms, blob);
+
+ case RAW_SFILEINFO_UNIX_BASIC:
+ NEED_BLOB(100);
+ SBVAL(blob->data, 0, parms->unix_basic.in.end_of_file);
+ SBVAL(blob->data, 8, parms->unix_basic.in.num_bytes);
+ smbcli_push_nttime(blob->data, 16, parms->unix_basic.in.status_change_time);
+ smbcli_push_nttime(blob->data, 24, parms->unix_basic.in.access_time);
+ smbcli_push_nttime(blob->data, 32, parms->unix_basic.in.change_time);
+ SBVAL(blob->data, 40, parms->unix_basic.in.uid);
+ SBVAL(blob->data, 48, parms->unix_basic.in.gid);
+ SIVAL(blob->data, 56, parms->unix_basic.in.file_type);
+ SBVAL(blob->data, 60, parms->unix_basic.in.dev_major);
+ SBVAL(blob->data, 68, parms->unix_basic.in.dev_minor);
+ SBVAL(blob->data, 76, parms->unix_basic.in.unique_id);
+ SBVAL(blob->data, 84, parms->unix_basic.in.permissions);
+ SBVAL(blob->data, 92, parms->unix_basic.in.nlink);
+ return true;
+
+ case RAW_SFILEINFO_UNIX_INFO2:
+ NEED_BLOB(116);
+ SBVAL(blob->data, 0, parms->unix_info2.in.end_of_file);
+ SBVAL(blob->data, 8, parms->unix_info2.in.num_bytes);
+ smbcli_push_nttime(blob->data, 16, parms->unix_info2.in.status_change_time);
+ smbcli_push_nttime(blob->data, 24, parms->unix_info2.in.access_time);
+ smbcli_push_nttime(blob->data, 32, parms->unix_info2.in.change_time);
+ SBVAL(blob->data, 40,parms->unix_info2.in.uid);
+ SBVAL(blob->data, 48,parms->unix_info2.in.gid);
+ SIVAL(blob->data, 56,parms->unix_info2.in.file_type);
+ SBVAL(blob->data, 60,parms->unix_info2.in.dev_major);
+ SBVAL(blob->data, 68,parms->unix_info2.in.dev_minor);
+ SBVAL(blob->data, 76,parms->unix_info2.in.unique_id);
+ SBVAL(blob->data, 84,parms->unix_info2.in.permissions);
+ SBVAL(blob->data, 92,parms->unix_info2.in.nlink);
+ smbcli_push_nttime(blob->data, 100, parms->unix_info2.in.create_time);
+ SIVAL(blob->data, 108, parms->unix_info2.in.file_flags);
+ SIVAL(blob->data, 112, parms->unix_info2.in.flags_mask);
+ return true;
+
+ case RAW_SFILEINFO_DISPOSITION_INFO:
+ case RAW_SFILEINFO_DISPOSITION_INFORMATION:
+ return smb_raw_setfileinfo_passthru(mem_ctx, RAW_SFILEINFO_DISPOSITION_INFORMATION,
+ parms, blob);
+
+ case RAW_SFILEINFO_ALLOCATION_INFO:
+ case RAW_SFILEINFO_ALLOCATION_INFORMATION:
+ return smb_raw_setfileinfo_passthru(mem_ctx, RAW_SFILEINFO_ALLOCATION_INFORMATION,
+ parms, blob);
+
+ case RAW_SFILEINFO_END_OF_FILE_INFO:
+ case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
+ return smb_raw_setfileinfo_passthru(mem_ctx, RAW_SFILEINFO_END_OF_FILE_INFORMATION,
+ parms, blob);
+
+ case RAW_SFILEINFO_RENAME_INFORMATION:
+ return smb_raw_setfileinfo_passthru(mem_ctx, RAW_SFILEINFO_RENAME_INFORMATION,
+ parms, blob);
+
+ case RAW_SFILEINFO_POSITION_INFORMATION:
+ return smb_raw_setfileinfo_passthru(mem_ctx, RAW_SFILEINFO_POSITION_INFORMATION,
+ parms, blob);
+
+ case RAW_SFILEINFO_MODE_INFORMATION:
+ return smb_raw_setfileinfo_passthru(mem_ctx, RAW_SFILEINFO_MODE_INFORMATION,
+ parms, blob);
+
+ /* Unhandled passthru levels */
+ case RAW_SFILEINFO_PIPE_INFORMATION:
+ case RAW_SFILEINFO_VALID_DATA_INFORMATION:
+ case RAW_SFILEINFO_SHORT_NAME_INFORMATION:
+ case RAW_SFILEINFO_FULL_EA_INFORMATION:
+ case RAW_SFILEINFO_1025:
+ case RAW_SFILEINFO_1027:
+ case RAW_SFILEINFO_1029:
+ case RAW_SFILEINFO_1030:
+ case RAW_SFILEINFO_1031:
+ case RAW_SFILEINFO_1032:
+ case RAW_SFILEINFO_1036:
+ case RAW_SFILEINFO_1041:
+ case RAW_SFILEINFO_1042:
+ case RAW_SFILEINFO_1043:
+ case RAW_SFILEINFO_1044:
+ return smb_raw_setfileinfo_passthru(mem_ctx, parms->generic.level,
+ parms, blob);
+
+ /* Unhandled levels */
+
+ case RAW_SFILEINFO_UNIX_LINK:
+ case RAW_SFILEINFO_UNIX_HLINK:
+ case RAW_SFILEINFO_RENAME_INFORMATION_SMB2:
+ break;
+ }
+
+ return false;
+}
+
+/****************************************************************************
+ Very raw set file info - takes data blob (async send)
+****************************************************************************/
+static struct smbcli_request *smb_raw_setfileinfo_blob_send(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ uint16_t fnum,
+ uint16_t info_level,
+ DATA_BLOB *blob)
+{
+ struct smb_trans2 tp;
+ uint16_t setup = TRANSACT2_SETFILEINFO;
+
+ tp.in.max_setup = 0;
+ tp.in.flags = 0;
+ tp.in.timeout = 0;
+ tp.in.setup_count = 1;
+ tp.in.max_param = 2;
+ tp.in.max_data = 0;
+ tp.in.setup = &setup;
+
+ tp.in.params = data_blob_talloc(mem_ctx, NULL, 6);
+ if (!tp.in.params.data) {
+ return NULL;
+ }
+ SSVAL(tp.in.params.data, 0, fnum);
+ SSVAL(tp.in.params.data, 2, info_level);
+ SSVAL(tp.in.params.data, 4, 0); /* reserved */
+
+ tp.in.data = *blob;
+
+ return smb_raw_trans2_send(tree, &tp);
+}
+
+/****************************************************************************
+ Very raw set path info - takes data blob
+****************************************************************************/
+static struct smbcli_request *smb_raw_setpathinfo_blob_send(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ const char *fname,
+ uint16_t info_level,
+ DATA_BLOB *blob)
+{
+ struct smb_trans2 tp;
+ uint16_t setup = TRANSACT2_SETPATHINFO;
+
+ tp.in.max_setup = 0;
+ tp.in.flags = 0;
+ tp.in.timeout = 0;
+ tp.in.setup_count = 1;
+ tp.in.max_param = 2;
+ tp.in.max_data = 0;
+ tp.in.setup = &setup;
+
+ tp.in.params = data_blob_talloc(mem_ctx, NULL, 6);
+ if (!tp.in.params.data) {
+ return NULL;
+ }
+ SSVAL(tp.in.params.data, 0, info_level);
+ SIVAL(tp.in.params.data, 2, 0);
+ smbcli_blob_append_string(tree->session, mem_ctx,
+ &tp.in.params,
+ fname, STR_TERMINATE);
+
+ tp.in.data = *blob;
+
+ return smb_raw_trans2_send(tree, &tp);
+}
+
+/****************************************************************************
+ Handle setattr (async send)
+****************************************************************************/
+static struct smbcli_request *smb_raw_setattr_send(struct smbcli_tree *tree,
+ union smb_setfileinfo *parms)
+{
+ struct smbcli_request *req;
+
+ req = smbcli_request_setup(tree, SMBsetatr, 8, 0);
+ if (!req) return NULL;
+
+ SSVAL(req->out.vwv, VWV(0), parms->setattr.in.attrib);
+ raw_push_dos_date3(tree->session->transport,
+ req->out.vwv, VWV(1), parms->setattr.in.write_time);
+ memset(req->out.vwv + VWV(3), 0, 10); /* reserved */
+ smbcli_req_append_ascii4(req, parms->setattr.in.file.path, STR_TERMINATE);
+ smbcli_req_append_ascii4(req, "", STR_TERMINATE);
+
+ if (!smbcli_request_send(req)) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+/****************************************************************************
+ Handle setattrE. (async send)
+****************************************************************************/
+static struct smbcli_request *smb_raw_setattrE_send(struct smbcli_tree *tree,
+ union smb_setfileinfo *parms)
+{
+ struct smbcli_request *req;
+
+ req = smbcli_request_setup(tree, SMBsetattrE, 7, 0);
+ if (!req) return NULL;
+
+ SSVAL(req->out.vwv, VWV(0), parms->setattre.in.file.fnum);
+ raw_push_dos_date2(tree->session->transport,
+ req->out.vwv, VWV(1), parms->setattre.in.create_time);
+ raw_push_dos_date2(tree->session->transport,
+ req->out.vwv, VWV(3), parms->setattre.in.access_time);
+ raw_push_dos_date2(tree->session->transport,
+ req->out.vwv, VWV(5), parms->setattre.in.write_time);
+
+ if (!smbcli_request_send(req)) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+/****************************************************************************
+ Set file info (async send)
+****************************************************************************/
+struct smbcli_request *smb_raw_setfileinfo_send(struct smbcli_tree *tree,
+ union smb_setfileinfo *parms)
+{
+ DATA_BLOB blob;
+ TALLOC_CTX *mem_ctx;
+ struct smbcli_request *req;
+
+ if (parms->generic.level == RAW_SFILEINFO_SETATTRE) {
+ return smb_raw_setattrE_send(tree, parms);
+ }
+ if (parms->generic.level == RAW_SFILEINFO_SEC_DESC) {
+ return smb_raw_set_secdesc_send(tree, parms);
+ }
+ if (parms->generic.level >= RAW_SFILEINFO_GENERIC) {
+ return NULL;
+ }
+
+ mem_ctx = talloc_init("setpathinfo");
+ if (!mem_ctx) return NULL;
+
+ if (!smb_raw_setinfo_backend(tree, mem_ctx, parms, &blob)) {
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ /* send request and process the output */
+ req = smb_raw_setfileinfo_blob_send(tree,
+ mem_ctx,
+ parms->generic.in.file.fnum,
+ parms->generic.level,
+ &blob);
+
+ talloc_free(mem_ctx);
+ return req;
+}
+
+/****************************************************************************
+ Set file info (async send)
+****************************************************************************/
+_PUBLIC_ NTSTATUS smb_raw_setfileinfo(struct smbcli_tree *tree,
+ union smb_setfileinfo *parms)
+{
+ struct smbcli_request *req = smb_raw_setfileinfo_send(tree, parms);
+ return smbcli_request_simple_recv(req);
+}
+
+
+/****************************************************************************
+ Set path info (async send)
+****************************************************************************/
+_PUBLIC_ struct smbcli_request *smb_raw_setpathinfo_send(struct smbcli_tree *tree,
+ union smb_setfileinfo *parms)
+{
+ DATA_BLOB blob;
+ TALLOC_CTX *mem_ctx;
+ struct smbcli_request *req;
+
+ if (parms->generic.level == RAW_SFILEINFO_SETATTR) {
+ return smb_raw_setattr_send(tree, parms);
+ }
+ if (parms->generic.level >= RAW_SFILEINFO_GENERIC) {
+ return NULL;
+ }
+
+ mem_ctx = talloc_init("setpathinfo");
+ if (!mem_ctx) return NULL;
+
+ if (!smb_raw_setinfo_backend(tree, mem_ctx, parms, &blob)) {
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ /* send request and process the output */
+ req = smb_raw_setpathinfo_blob_send(tree,
+ mem_ctx,
+ parms->generic.in.file.path,
+ parms->generic.level,
+ &blob);
+
+ talloc_free(mem_ctx);
+ return req;
+}
+
+/****************************************************************************
+ Set path info (sync interface)
+****************************************************************************/
+_PUBLIC_ NTSTATUS smb_raw_setpathinfo(struct smbcli_tree *tree,
+ union smb_setfileinfo *parms)
+{
+ struct smbcli_request *req = smb_raw_setpathinfo_send(tree, parms);
+ return smbcli_request_simple_recv(req);
+}
diff --git a/source4/libcli/raw/rawshadow.c b/source4/libcli/raw/rawshadow.c
new file mode 100644
index 0000000000..b318c3e025
--- /dev/null
+++ b/source4/libcli/raw/rawshadow.c
@@ -0,0 +1,82 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ shadow copy file operations
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/raw/ioctl.h"
+
+/*
+ get shadow volume data
+*/
+_PUBLIC_ NTSTATUS smb_raw_shadow_data(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx, struct smb_shadow_copy *info)
+{
+ union smb_ioctl nt;
+ NTSTATUS status;
+ DATA_BLOB blob;
+ uint32_t dlength;
+ int i;
+ uint32_t ofs;
+
+ nt.ntioctl.level = RAW_IOCTL_NTIOCTL;
+ nt.ntioctl.in.function = FSCTL_GET_SHADOW_COPY_DATA;
+ nt.ntioctl.in.file.fnum = info->in.file.fnum;
+ nt.ntioctl.in.fsctl = true;
+ nt.ntioctl.in.filter = 0;
+ nt.ntioctl.in.max_data = info->in.max_data;
+ nt.ntioctl.in.blob = data_blob(NULL, 0);
+
+ status = smb_raw_ioctl(tree, mem_ctx, &nt);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ blob = nt.ntioctl.out.blob;
+
+ if (blob.length < 12) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ info->out.num_volumes = IVAL(blob.data, 0);
+ info->out.num_names = IVAL(blob.data, 4);
+ dlength = IVAL(blob.data, 8);
+ if (dlength > blob.length - 12) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ info->out.names = talloc_array(mem_ctx, const char *, info->out.num_names);
+ NT_STATUS_HAVE_NO_MEMORY(info->out.names);
+
+ ofs = 12;
+ for (i=0;i<info->out.num_names;i++) {
+ size_t len;
+ len = smbcli_blob_pull_ucs2(info->out.names,
+ &blob, &info->out.names[i],
+ blob.data+ofs, -1, STR_TERMINATE);
+ if (len == 0) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ ofs += len;
+ }
+
+ return status;
+}
diff --git a/source4/libcli/raw/rawtrans.c b/source4/libcli/raw/rawtrans.c
new file mode 100644
index 0000000000..d68df56705
--- /dev/null
+++ b/source4/libcli/raw/rawtrans.c
@@ -0,0 +1,961 @@
+/*
+ Unix SMB/CIFS implementation.
+ raw trans/trans2/nttrans operations
+
+ Copyright (C) James Myers 2003 <myersjj@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "../lib/util/dlinklist.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+
+#define TORTURE_TRANS_DATA 0
+
+/*
+ check out of bounds for incoming data
+*/
+static bool raw_trans_oob(struct smbcli_request *req,
+ uint_t offset, uint_t count)
+{
+ uint8_t *ptr;
+
+ if (count == 0) {
+ return false;
+ }
+
+ ptr = req->in.hdr + offset;
+
+ /* be careful with wraparound! */
+ if ((uintptr_t)ptr < (uintptr_t)req->in.data ||
+ (uintptr_t)ptr >= (uintptr_t)req->in.data + req->in.data_size ||
+ count > req->in.data_size ||
+ (uintptr_t)ptr + count > (uintptr_t)req->in.data + req->in.data_size) {
+ return true;
+ }
+ return false;
+}
+
+static size_t raw_trans_space_left(struct smbcli_request *req)
+{
+ if (req->transport->negotiate.max_xmit <= req->out.size) {
+ return 0;
+ }
+
+ return req->transport->negotiate.max_xmit - req->out.size;
+}
+
+struct smb_raw_trans2_recv_state {
+ uint8_t command;
+ uint32_t params_total;
+ uint32_t data_total;
+ uint32_t params_left;
+ uint32_t data_left;
+ bool got_first;
+ uint32_t recvd_data;
+ uint32_t recvd_param;
+ struct smb_trans2 io;
+};
+
+NTSTATUS smb_raw_trans2_recv(struct smbcli_request *req,
+ TALLOC_CTX *mem_ctx,
+ struct smb_trans2 *parms)
+{
+ struct smb_raw_trans2_recv_state *state;
+
+ if (!smbcli_request_receive(req) ||
+ smbcli_request_is_error(req)) {
+ goto failed;
+ }
+
+ state = talloc_get_type(req->recv_helper.private_data,
+ struct smb_raw_trans2_recv_state);
+
+ parms->out = state->io.out;
+ talloc_steal(mem_ctx, parms->out.setup);
+ talloc_steal(mem_ctx, parms->out.params.data);
+ talloc_steal(mem_ctx, parms->out.data.data);
+ talloc_free(state);
+
+ ZERO_STRUCT(req->recv_helper);
+
+failed:
+ return smbcli_request_destroy(req);
+}
+
+static enum smbcli_request_state smb_raw_trans2_ship_rest(struct smbcli_request *req,
+ struct smb_raw_trans2_recv_state *state);
+
+/*
+ * This helper returns SMBCLI_REQUEST_RECV until all data has arrived
+ */
+static enum smbcli_request_state smb_raw_trans2_recv_helper(struct smbcli_request *req)
+{
+ struct smb_raw_trans2_recv_state *state = talloc_get_type(req->recv_helper.private_data,
+ struct smb_raw_trans2_recv_state);
+ uint16_t param_count, param_ofs, param_disp;
+ uint16_t data_count, data_ofs, data_disp;
+ uint16_t total_data, total_param;
+ uint8_t setup_count;
+
+ /*
+ * An NT RPC pipe call can return ERRDOS, ERRmoredata
+ * to a trans call. This is not an error and should not
+ * be treated as such.
+ */
+ if (smbcli_request_is_error(req)) {
+ goto failed;
+ }
+
+ if (state->params_left > 0 || state->data_left > 0) {
+ return smb_raw_trans2_ship_rest(req, state);
+ }
+
+ SMBCLI_CHECK_MIN_WCT(req, 10);
+
+ total_data = SVAL(req->in.vwv, VWV(1));
+ total_param = SVAL(req->in.vwv, VWV(0));
+ setup_count = CVAL(req->in.vwv, VWV(9));
+
+ param_count = SVAL(req->in.vwv, VWV(3));
+ param_ofs = SVAL(req->in.vwv, VWV(4));
+ param_disp = SVAL(req->in.vwv, VWV(5));
+
+ data_count = SVAL(req->in.vwv, VWV(6));
+ data_ofs = SVAL(req->in.vwv, VWV(7));
+ data_disp = SVAL(req->in.vwv, VWV(8));
+
+ if (!state->got_first) {
+ if (total_param > 0) {
+ state->io.out.params = data_blob_talloc(state, NULL, total_param);
+ if (!state->io.out.params.data) {
+ goto nomem;
+ }
+ }
+
+ if (total_data > 0) {
+ state->io.out.data = data_blob_talloc(state, NULL, total_data);
+ if (!state->io.out.data.data) {
+ goto nomem;
+ }
+ }
+
+ if (setup_count > 0) {
+ uint16_t i;
+
+ SMBCLI_CHECK_WCT(req, 10 + setup_count);
+
+ state->io.out.setup_count = setup_count;
+ state->io.out.setup = talloc_array(state, uint16_t, setup_count);
+ if (!state->io.out.setup) {
+ goto nomem;
+ }
+ for (i=0; i < setup_count; i++) {
+ state->io.out.setup[i] = SVAL(req->in.vwv, VWV(10+i));
+ }
+ }
+
+ state->got_first = true;
+ }
+
+ if (total_data > state->io.out.data.length ||
+ total_param > state->io.out.params.length) {
+ /* they must *only* shrink */
+ DEBUG(1,("smb_raw_trans2_recv_helper: data/params expanded!\n"));
+ req->status = NT_STATUS_BUFFER_TOO_SMALL;
+ goto failed;
+ }
+
+ state->io.out.data.length = total_data;
+ state->io.out.params.length = total_param;
+
+ if (data_count + data_disp > total_data ||
+ param_count + param_disp > total_param) {
+ DEBUG(1,("smb_raw_trans2_recv_helper: Buffer overflow\n"));
+ req->status = NT_STATUS_BUFFER_TOO_SMALL;
+ goto failed;
+ }
+
+ /* check the server isn't being nasty */
+ if (raw_trans_oob(req, param_ofs, param_count) ||
+ raw_trans_oob(req, data_ofs, data_count)) {
+ DEBUG(1,("smb_raw_trans2_recv_helper: out of bounds parameters!\n"));
+ req->status = NT_STATUS_BUFFER_TOO_SMALL;
+ goto failed;
+ }
+
+ if (data_count) {
+ memcpy(state->io.out.data.data + data_disp,
+ req->in.hdr + data_ofs,
+ data_count);
+ }
+
+ if (param_count) {
+ memcpy(state->io.out.params.data + param_disp,
+ req->in.hdr + param_ofs,
+ param_count);
+ }
+
+ state->recvd_param += param_count;
+ state->recvd_data += data_count;
+
+ if (state->recvd_data < total_data ||
+ state->recvd_param < total_param) {
+
+ /* we don't need the in buffer any more */
+ talloc_free(req->in.buffer);
+ ZERO_STRUCT(req->in);
+
+ /* we still wait for more data */
+ DEBUG(10,("smb_raw_trans2_recv_helper: more data needed\n"));
+ return SMBCLI_REQUEST_RECV;
+ }
+
+ DEBUG(10,("smb_raw_trans2_recv_helper: done\n"));
+ return SMBCLI_REQUEST_DONE;
+
+nomem:
+ req->status = NT_STATUS_NO_MEMORY;
+failed:
+ return SMBCLI_REQUEST_ERROR;
+}
+
+_PUBLIC_ NTSTATUS smb_raw_trans_recv(struct smbcli_request *req,
+ TALLOC_CTX *mem_ctx,
+ struct smb_trans2 *parms)
+{
+ return smb_raw_trans2_recv(req, mem_ctx, parms);
+}
+
+
+/*
+ trans/trans2 raw async interface - only BLOBs used in this interface.
+*/
+struct smbcli_request *smb_raw_trans_send_backend(struct smbcli_tree *tree,
+ struct smb_trans2 *parms,
+ uint8_t command)
+{
+ struct smb_raw_trans2_recv_state *state;
+ struct smbcli_request *req;
+ int i;
+ int padding;
+ size_t space_left;
+ size_t namelen = 0;
+ DATA_BLOB params_chunk;
+ uint16_t ofs;
+ uint16_t params_ofs = 0;
+ DATA_BLOB data_chunk;
+ uint16_t data_ofs = 0;
+
+ if (parms->in.params.length > UINT16_MAX ||
+ parms->in.data.length > UINT16_MAX) {
+ DEBUG(3,("Attempt to send invalid trans2 request (params %u, data %u)\n",
+ (unsigned)parms->in.params.length, (unsigned)parms->in.data.length));
+ return NULL;
+ }
+
+
+ if (command == SMBtrans)
+ padding = 1;
+ else
+ padding = 3;
+
+ req = smbcli_request_setup(tree, command,
+ 14 + parms->in.setup_count,
+ padding);
+ if (!req) {
+ return NULL;
+ }
+
+ state = talloc_zero(req, struct smb_raw_trans2_recv_state);
+ if (!state) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+
+ state->command = command;
+
+ /* make sure we don't leak data via the padding */
+ memset(req->out.data, 0, padding);
+
+ /* Watch out, this changes the req->out.* pointers */
+ if (command == SMBtrans && parms->in.trans_name) {
+ namelen = smbcli_req_append_string(req, parms->in.trans_name,
+ STR_TERMINATE);
+ }
+
+ ofs = PTR_DIFF(req->out.data,req->out.hdr)+padding+namelen;
+
+ /* see how much bytes of the params block we can ship in the first request */
+ space_left = raw_trans_space_left(req);
+
+ params_chunk.length = MIN(parms->in.params.length, space_left);
+ params_chunk.data = parms->in.params.data;
+ params_ofs = ofs;
+
+ state->params_left = parms->in.params.length - params_chunk.length;
+
+ if (state->params_left > 0) {
+ /* we copy the whole params block, if needed we can optimize that latter */
+ state->io.in.params = data_blob_talloc(state, NULL, parms->in.params.length);
+ if (!state->io.in.params.data) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+ memcpy(state->io.in.params.data,
+ parms->in.params.data,
+ parms->in.params.length);
+ }
+
+ /* see how much bytes of the data block we can ship in the first request */
+ space_left -= params_chunk.length;
+
+#if TORTURE_TRANS_DATA
+ if (space_left > 1) {
+ space_left /= 2;
+ }
+#endif
+
+ data_chunk.length = MIN(parms->in.data.length, space_left);
+ data_chunk.data = parms->in.data.data;
+ data_ofs = params_ofs + params_chunk.length;
+
+ state->data_left = parms->in.data.length - data_chunk.length;
+
+ if (state->data_left > 0) {
+ /* we copy the whole params block, if needed we can optimize that latter */
+ state->io.in.data = data_blob_talloc(state, NULL, parms->in.data.length);
+ if (!state->io.in.data.data) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+ memcpy(state->io.in.data.data,
+ parms->in.data.data,
+ parms->in.data.length);
+ }
+
+ state->params_total = parms->in.params.length;
+ state->data_total = parms->in.data.length;
+
+ /* primary request */
+ SSVAL(req->out.vwv,VWV(0),parms->in.params.length);
+ SSVAL(req->out.vwv,VWV(1),parms->in.data.length);
+ SSVAL(req->out.vwv,VWV(2),parms->in.max_param);
+ SSVAL(req->out.vwv,VWV(3),parms->in.max_data);
+ SCVAL(req->out.vwv,VWV(4),parms->in.max_setup);
+ SCVAL(req->out.vwv,VWV(4)+1,0); /* reserved */
+ SSVAL(req->out.vwv,VWV(5),parms->in.flags);
+ SIVAL(req->out.vwv,VWV(6),parms->in.timeout);
+ SSVAL(req->out.vwv,VWV(8),0); /* reserved */
+ SSVAL(req->out.vwv,VWV(9),params_chunk.length);
+ SSVAL(req->out.vwv,VWV(10),params_ofs);
+ SSVAL(req->out.vwv,VWV(11),data_chunk.length);
+ SSVAL(req->out.vwv,VWV(12),data_ofs);
+ SCVAL(req->out.vwv,VWV(13),parms->in.setup_count);
+ SCVAL(req->out.vwv,VWV(13)+1,0); /* reserved */
+ for (i=0;i<parms->in.setup_count;i++) {
+ SSVAL(req->out.vwv,VWV(14)+VWV(i),parms->in.setup[i]);
+ }
+ smbcli_req_append_blob(req, &params_chunk);
+ smbcli_req_append_blob(req, &data_chunk);
+
+ /* add the helper which will check that all multi-part replies are
+ in before an async client callack will be issued */
+ req->recv_helper.fn = smb_raw_trans2_recv_helper;
+ req->recv_helper.private_data = state;
+
+ if (!smbcli_request_send(req)) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+static enum smbcli_request_state smb_raw_trans2_ship_next(struct smbcli_request *req,
+ struct smb_raw_trans2_recv_state *state)
+{
+ struct smbcli_request *req2;
+ size_t space_left;
+ DATA_BLOB params_chunk;
+ uint16_t ofs;
+ uint16_t params_ofs = 0;
+ uint16_t params_disp = 0;
+ DATA_BLOB data_chunk;
+ uint16_t data_ofs = 0;
+ uint16_t data_disp = 0;
+ uint8_t wct;
+
+ if (state->command == SMBtrans2) {
+ wct = 9;
+ } else {
+ wct = 8;
+ }
+
+ req2 = smbcli_request_setup(req->tree, state->command+1, wct, 0);
+ if (!req2) {
+ goto nomem;
+ }
+ req2->mid = req->mid;
+ SSVAL(req2->out.hdr, HDR_MID, req2->mid);
+
+ ofs = PTR_DIFF(req2->out.data,req2->out.hdr);
+
+ /* see how much bytes of the params block we can ship in the first request */
+ space_left = raw_trans_space_left(req2);
+
+ params_disp = state->io.in.params.length - state->params_left;
+ params_chunk.length = MIN(state->params_left, space_left);
+ params_chunk.data = state->io.in.params.data + params_disp;
+ params_ofs = ofs;
+
+ state->params_left -= params_chunk.length;
+
+ /* see how much bytes of the data block we can ship in the first request */
+ space_left -= params_chunk.length;
+
+#if TORTURE_TRANS_DATA
+ if (space_left > 1) {
+ space_left /= 2;
+ }
+#endif
+
+ data_disp = state->io.in.data.length - state->data_left;
+ data_chunk.length = MIN(state->data_left, space_left);
+ data_chunk.data = state->io.in.data.data + data_disp;
+ data_ofs = params_ofs+params_chunk.length;
+
+ state->data_left -= data_chunk.length;
+
+ SSVAL(req2->out.vwv,VWV(0), state->params_total);
+ SSVAL(req2->out.vwv,VWV(1), state->data_total);
+ SSVAL(req2->out.vwv,VWV(2), params_chunk.length);
+ SSVAL(req2->out.vwv,VWV(3), params_ofs);
+ SSVAL(req2->out.vwv,VWV(4), params_disp);
+ SSVAL(req2->out.vwv,VWV(5), data_chunk.length);
+ SSVAL(req2->out.vwv,VWV(6), data_ofs);
+ SSVAL(req2->out.vwv,VWV(7), data_disp);
+ if (wct == 9) {
+ SSVAL(req2->out.vwv,VWV(8), 0xFFFF);
+ }
+
+ smbcli_req_append_blob(req2, &params_chunk);
+ smbcli_req_append_blob(req2, &data_chunk);
+
+ /*
+ * it's a one way request but we need
+ * the seq_num, so we destroy req2 by hand
+ */
+ if (!smbcli_request_send(req2)) {
+ goto failed;
+ }
+
+ req->seq_num = req2->seq_num;
+ smbcli_request_destroy(req2);
+
+ return SMBCLI_REQUEST_RECV;
+
+nomem:
+ req->status = NT_STATUS_NO_MEMORY;
+failed:
+ if (req2) {
+ req->status = smbcli_request_destroy(req2);
+ }
+ return SMBCLI_REQUEST_ERROR;
+}
+
+static enum smbcli_request_state smb_raw_trans2_ship_rest(struct smbcli_request *req,
+ struct smb_raw_trans2_recv_state *state)
+{
+ enum smbcli_request_state ret = SMBCLI_REQUEST_ERROR;
+
+ while (state->params_left > 0 || state->data_left > 0) {
+ ret = smb_raw_trans2_ship_next(req, state);
+ if (ret != SMBCLI_REQUEST_RECV) {
+ break;
+ }
+ }
+
+ return ret;
+}
+
+
+/*
+ trans/trans2 raw async interface - only BLOBs used in this interface.
+ note that this doesn't yet support multi-part requests
+*/
+_PUBLIC_ struct smbcli_request *smb_raw_trans_send(struct smbcli_tree *tree,
+ struct smb_trans2 *parms)
+{
+ return smb_raw_trans_send_backend(tree, parms, SMBtrans);
+}
+
+struct smbcli_request *smb_raw_trans2_send(struct smbcli_tree *tree,
+ struct smb_trans2 *parms)
+{
+ return smb_raw_trans_send_backend(tree, parms, SMBtrans2);
+}
+
+/*
+ trans2 synchronous blob interface
+*/
+NTSTATUS smb_raw_trans2(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ struct smb_trans2 *parms)
+{
+ struct smbcli_request *req;
+ req = smb_raw_trans2_send(tree, parms);
+ if (!req) return NT_STATUS_UNSUCCESSFUL;
+ return smb_raw_trans2_recv(req, mem_ctx, parms);
+}
+
+
+/*
+ trans synchronous blob interface
+*/
+_PUBLIC_ NTSTATUS smb_raw_trans(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ struct smb_trans2 *parms)
+{
+ struct smbcli_request *req;
+ req = smb_raw_trans_send(tree, parms);
+ if (!req) return NT_STATUS_UNSUCCESSFUL;
+ return smb_raw_trans_recv(req, mem_ctx, parms);
+}
+
+struct smb_raw_nttrans_recv_state {
+ uint32_t params_total;
+ uint32_t data_total;
+ uint32_t params_left;
+ uint32_t data_left;
+ bool got_first;
+ uint32_t recvd_data;
+ uint32_t recvd_param;
+ struct smb_nttrans io;
+};
+
+NTSTATUS smb_raw_nttrans_recv(struct smbcli_request *req,
+ TALLOC_CTX *mem_ctx,
+ struct smb_nttrans *parms)
+{
+ struct smb_raw_nttrans_recv_state *state;
+
+ if (!smbcli_request_receive(req) ||
+ smbcli_request_is_error(req)) {
+ goto failed;
+ }
+
+ state = talloc_get_type(req->recv_helper.private_data,
+ struct smb_raw_nttrans_recv_state);
+
+ parms->out = state->io.out;
+ talloc_steal(mem_ctx, parms->out.setup);
+ talloc_steal(mem_ctx, parms->out.params.data);
+ talloc_steal(mem_ctx, parms->out.data.data);
+ talloc_free(state);
+
+ ZERO_STRUCT(req->recv_helper);
+
+failed:
+ return smbcli_request_destroy(req);
+}
+
+static enum smbcli_request_state smb_raw_nttrans_ship_rest(struct smbcli_request *req,
+ struct smb_raw_nttrans_recv_state *state);
+
+/*
+ * This helper returns SMBCLI_REQUEST_RECV until all data has arrived
+ */
+static enum smbcli_request_state smb_raw_nttrans_recv_helper(struct smbcli_request *req)
+{
+ struct smb_raw_nttrans_recv_state *state = talloc_get_type(req->recv_helper.private_data,
+ struct smb_raw_nttrans_recv_state);
+ uint32_t param_count, param_ofs, param_disp;
+ uint32_t data_count, data_ofs, data_disp;
+ uint32_t total_data, total_param;
+ uint8_t setup_count;
+
+ /*
+ * An NT RPC pipe call can return ERRDOS, ERRmoredata
+ * to a trans call. This is not an error and should not
+ * be treated as such.
+ */
+ if (smbcli_request_is_error(req)) {
+ goto failed;
+ }
+
+ /* sanity check */
+ if (CVAL(req->in.hdr, HDR_COM) != SMBnttrans) {
+ DEBUG(0,("smb_raw_nttrans_recv_helper: Expected %s response, got command 0x%02x\n",
+ "SMBnttrans",
+ CVAL(req->in.hdr,HDR_COM)));
+ req->status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto failed;
+ }
+
+ if (state->params_left > 0 || state->data_left > 0) {
+ return smb_raw_nttrans_ship_rest(req, state);
+ }
+
+ /* this is the first packet of the response */
+ SMBCLI_CHECK_MIN_WCT(req, 18);
+
+ total_param = IVAL(req->in.vwv, 3);
+ total_data = IVAL(req->in.vwv, 7);
+ setup_count = CVAL(req->in.vwv, 35);
+
+ param_count = IVAL(req->in.vwv, 11);
+ param_ofs = IVAL(req->in.vwv, 15);
+ param_disp = IVAL(req->in.vwv, 19);
+
+ data_count = IVAL(req->in.vwv, 23);
+ data_ofs = IVAL(req->in.vwv, 27);
+ data_disp = IVAL(req->in.vwv, 31);
+
+ if (!state->got_first) {
+ if (total_param > 0) {
+ state->io.out.params = data_blob_talloc(state, NULL, total_param);
+ if (!state->io.out.params.data) {
+ goto nomem;
+ }
+ }
+
+ if (total_data > 0) {
+ state->io.out.data = data_blob_talloc(state, NULL, total_data);
+ if (!state->io.out.data.data) {
+ goto nomem;
+ }
+ }
+
+ if (setup_count > 0) {
+ SMBCLI_CHECK_WCT(req, 18 + setup_count);
+
+ state->io.out.setup_count = setup_count;
+ state->io.out.setup = talloc_array(state, uint8_t,
+ setup_count * VWV(1));
+ if (!state->io.out.setup) {
+ goto nomem;
+ }
+ memcpy(state->io.out.setup, (uint8_t *)req->out.vwv + VWV(18),
+ setup_count * VWV(1));
+ }
+
+ state->got_first = true;
+ }
+
+ if (total_data > state->io.out.data.length ||
+ total_param > state->io.out.params.length) {
+ /* they must *only* shrink */
+ DEBUG(1,("smb_raw_nttrans_recv_helper: data/params expanded!\n"));
+ req->status = NT_STATUS_BUFFER_TOO_SMALL;
+ goto failed;
+ }
+
+ state->io.out.data.length = total_data;
+ state->io.out.params.length = total_param;
+
+ if (data_count + data_disp > total_data ||
+ param_count + param_disp > total_param) {
+ DEBUG(1,("smb_raw_nttrans_recv_helper: Buffer overflow\n"));
+ req->status = NT_STATUS_BUFFER_TOO_SMALL;
+ goto failed;
+ }
+
+ /* check the server isn't being nasty */
+ if (raw_trans_oob(req, param_ofs, param_count) ||
+ raw_trans_oob(req, data_ofs, data_count)) {
+ DEBUG(1,("smb_raw_nttrans_recv_helper: out of bounds parameters!\n"));
+ req->status = NT_STATUS_BUFFER_TOO_SMALL;
+ goto failed;
+ }
+
+ if (data_count) {
+ memcpy(state->io.out.data.data + data_disp,
+ req->in.hdr + data_ofs,
+ data_count);
+ }
+
+ if (param_count) {
+ memcpy(state->io.out.params.data + param_disp,
+ req->in.hdr + param_ofs,
+ param_count);
+ }
+
+ state->recvd_param += param_count;
+ state->recvd_data += data_count;
+
+ if (state->recvd_data < total_data ||
+ state->recvd_param < total_param) {
+
+ /* we don't need the in buffer any more */
+ talloc_free(req->in.buffer);
+ ZERO_STRUCT(req->in);
+
+ /* we still wait for more data */
+ DEBUG(10,("smb_raw_nttrans_recv_helper: more data needed\n"));
+ return SMBCLI_REQUEST_RECV;
+ }
+
+ DEBUG(10,("smb_raw_nttrans_recv_helper: done\n"));
+ return SMBCLI_REQUEST_DONE;
+
+nomem:
+ req->status = NT_STATUS_NO_MEMORY;
+failed:
+ return SMBCLI_REQUEST_ERROR;
+}
+
+/****************************************************************************
+ nttrans raw - only BLOBs used in this interface.
+ at the moment we only handle a single primary request
+****************************************************************************/
+struct smbcli_request *smb_raw_nttrans_send(struct smbcli_tree *tree,
+ struct smb_nttrans *parms)
+{
+ struct smbcli_request *req;
+ struct smb_raw_nttrans_recv_state *state;
+ uint32_t ofs;
+ size_t space_left;
+ DATA_BLOB params_chunk;
+ uint32_t params_ofs;
+ DATA_BLOB data_chunk;
+ uint32_t data_ofs;
+ int align = 0;
+
+ /* only align if there are parameters or data */
+ if (parms->in.params.length || parms->in.data.length) {
+ align = 3;
+ }
+
+ req = smbcli_request_setup(tree, SMBnttrans,
+ 19 + parms->in.setup_count, align);
+ if (!req) {
+ return NULL;
+ }
+
+ state = talloc_zero(req, struct smb_raw_nttrans_recv_state);
+ if (!state) {
+ talloc_free(req);
+ return NULL;
+ }
+
+ /* fill in SMB parameters */
+
+ if (align != 0) {
+ memset(req->out.data, 0, align);
+ }
+
+ ofs = PTR_DIFF(req->out.data,req->out.hdr)+align;
+
+ /* see how much bytes of the params block we can ship in the first request */
+ space_left = raw_trans_space_left(req);
+
+ params_chunk.length = MIN(parms->in.params.length, space_left);
+ params_chunk.data = parms->in.params.data;
+ params_ofs = ofs;
+
+ state->params_left = parms->in.params.length - params_chunk.length;
+
+ if (state->params_left > 0) {
+ /* we copy the whole params block, if needed we can optimize that latter */
+ state->io.in.params = data_blob_talloc(state, NULL, parms->in.params.length);
+ if (!state->io.in.params.data) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+ memcpy(state->io.in.params.data,
+ parms->in.params.data,
+ parms->in.params.length);
+ }
+
+ /* see how much bytes of the data block we can ship in the first request */
+ space_left -= params_chunk.length;
+
+#if TORTURE_TRANS_DATA
+ if (space_left > 1) {
+ space_left /= 2;
+ }
+#endif
+
+ data_chunk.length = MIN(parms->in.data.length, space_left);
+ data_chunk.data = parms->in.data.data;
+ data_ofs = params_ofs + params_chunk.length;
+
+ state->data_left = parms->in.data.length - data_chunk.length;
+
+ if (state->data_left > 0) {
+ /* we copy the whole params block, if needed we can optimize that latter */
+ state->io.in.data = data_blob_talloc(state, NULL, parms->in.data.length);
+ if (!state->io.in.data.data) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+ memcpy(state->io.in.data.data,
+ parms->in.data.data,
+ parms->in.data.length);
+ }
+
+ state->params_total = parms->in.params.length;
+ state->data_total = parms->in.data.length;
+
+ SCVAL(req->out.vwv, 0, parms->in.max_setup);
+ SSVAL(req->out.vwv, 1, 0); /* reserved */
+ SIVAL(req->out.vwv, 3, parms->in.params.length);
+ SIVAL(req->out.vwv, 7, parms->in.data.length);
+ SIVAL(req->out.vwv, 11, parms->in.max_param);
+ SIVAL(req->out.vwv, 15, parms->in.max_data);
+ SIVAL(req->out.vwv, 19, params_chunk.length);
+ SIVAL(req->out.vwv, 23, params_ofs);
+ SIVAL(req->out.vwv, 27, data_chunk.length);
+ SIVAL(req->out.vwv, 31, data_ofs);
+ SCVAL(req->out.vwv, 35, parms->in.setup_count);
+ SSVAL(req->out.vwv, 36, parms->in.function);
+ memcpy(req->out.vwv + VWV(19), parms->in.setup,
+ sizeof(uint16_t) * parms->in.setup_count);
+
+ smbcli_req_append_blob(req, &params_chunk);
+ smbcli_req_append_blob(req, &data_chunk);
+
+ /* add the helper which will check that all multi-part replies are
+ in before an async client callack will be issued */
+ req->recv_helper.fn = smb_raw_nttrans_recv_helper;
+ req->recv_helper.private_data = state;
+
+ if (!smbcli_request_send(req)) {
+ smbcli_request_destroy(req);
+ return NULL;
+ }
+
+ return req;
+}
+
+static enum smbcli_request_state smb_raw_nttrans_ship_next(struct smbcli_request *req,
+ struct smb_raw_nttrans_recv_state *state)
+{
+ struct smbcli_request *req2;
+ size_t space_left;
+ DATA_BLOB params_chunk;
+ uint32_t ofs;
+ uint32_t params_ofs = 0;
+ uint32_t params_disp = 0;
+ DATA_BLOB data_chunk;
+ uint32_t data_ofs = 0;
+ uint32_t data_disp = 0;
+
+ req2 = smbcli_request_setup(req->tree, SMBnttranss, 18, 0);
+ if (!req2) {
+ goto nomem;
+ }
+ req2->mid = req->mid;
+ SSVAL(req2->out.hdr, HDR_MID, req2->mid);
+
+ ofs = PTR_DIFF(req2->out.data,req2->out.hdr);
+
+ /* see how much bytes of the params block we can ship in the first request */
+ space_left = raw_trans_space_left(req2);
+
+ params_disp = state->io.in.params.length - state->params_left;
+ params_chunk.length = MIN(state->params_left, space_left);
+ params_chunk.data = state->io.in.params.data + params_disp;
+ params_ofs = ofs;
+
+ state->params_left -= params_chunk.length;
+
+ /* see how much bytes of the data block we can ship in the first request */
+ space_left -= params_chunk.length;
+
+#if TORTURE_TRANS_DATA
+ if (space_left > 1) {
+ space_left /= 2;
+ }
+#endif
+
+ data_disp = state->io.in.data.length - state->data_left;
+ data_chunk.length = MIN(state->data_left, space_left);
+ data_chunk.data = state->io.in.data.data + data_disp;
+ data_ofs = params_ofs+params_chunk.length;
+
+ state->data_left -= data_chunk.length;
+
+ SSVAL(req2->out.vwv,0, 0); /* reserved */
+ SCVAL(req2->out.vwv,2, 0); /* reserved */
+ SIVAL(req2->out.vwv,3, state->params_total);
+ SIVAL(req2->out.vwv,7, state->data_total);
+ SIVAL(req2->out.vwv,11, params_chunk.length);
+ SIVAL(req2->out.vwv,15, params_ofs);
+ SIVAL(req2->out.vwv,19, params_disp);
+ SIVAL(req2->out.vwv,23, data_chunk.length);
+ SIVAL(req2->out.vwv,27, data_ofs);
+ SIVAL(req2->out.vwv,31, data_disp);
+ SCVAL(req2->out.vwv,35, 0); /* reserved */
+
+ smbcli_req_append_blob(req2, &params_chunk);
+ smbcli_req_append_blob(req2, &data_chunk);
+
+ /*
+ * it's a one way request but we need
+ * the seq_num, so we destroy req2 by hand
+ */
+ if (!smbcli_request_send(req2)) {
+ goto failed;
+ }
+
+ req->seq_num = req2->seq_num;
+ smbcli_request_destroy(req2);
+
+ return SMBCLI_REQUEST_RECV;
+
+nomem:
+ req->status = NT_STATUS_NO_MEMORY;
+failed:
+ if (req2) {
+ req->status = smbcli_request_destroy(req2);
+ }
+ return SMBCLI_REQUEST_ERROR;
+}
+
+static enum smbcli_request_state smb_raw_nttrans_ship_rest(struct smbcli_request *req,
+ struct smb_raw_nttrans_recv_state *state)
+{
+ enum smbcli_request_state ret = SMBCLI_REQUEST_ERROR;
+
+ while (state->params_left > 0 || state->data_left > 0) {
+ ret = smb_raw_nttrans_ship_next(req, state);
+ if (ret != SMBCLI_REQUEST_RECV) {
+ break;
+ }
+ }
+
+ return ret;
+}
+
+
+/****************************************************************************
+ receive a SMB nttrans response allocating the necessary memory
+ ****************************************************************************/
+NTSTATUS smb_raw_nttrans(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ struct smb_nttrans *parms)
+{
+ struct smbcli_request *req;
+
+ req = smb_raw_nttrans_send(tree, parms);
+ if (!req) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return smb_raw_nttrans_recv(req, mem_ctx, parms);
+}
diff --git a/source4/libcli/raw/request.h b/source4/libcli/raw/request.h
new file mode 100644
index 0000000000..2a572e58ee
--- /dev/null
+++ b/source4/libcli/raw/request.h
@@ -0,0 +1,78 @@
+#ifndef _REQUEST_H
+#define _REQUEST_H
+/*
+ Unix SMB/CIFS implementation.
+ SMB parameters and setup
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) James Myers 2003 <myersjj@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "libcli/raw/signing.h"
+
+#define BUFINFO_FLAG_UNICODE 0x0001
+#define BUFINFO_FLAG_SMB2 0x0002
+
+/*
+ buffer limit structure used by both SMB and SMB2
+ */
+struct request_bufinfo {
+ TALLOC_CTX *mem_ctx;
+ uint32_t flags;
+ const uint8_t *align_base;
+ const uint8_t *data;
+ size_t data_size;
+};
+
+/*
+ Shared state structure between client and server, representing the basic packet.
+*/
+
+struct smb_request_buffer {
+ /* the raw SMB buffer, including the 4 byte length header */
+ uint8_t *buffer;
+
+ /* the size of the raw buffer, including 4 byte header */
+ size_t size;
+
+ /* how much has been allocated - on reply the buffer is over-allocated to
+ prevent too many realloc() calls
+ */
+ size_t allocated;
+
+ /* the start of the SMB header - this is always buffer+4 */
+ uint8_t *hdr;
+
+ /* the command words and command word count. vwv points
+ into the raw buffer */
+ uint8_t *vwv;
+ uint_t wct;
+
+ /* the data buffer and size. data points into the raw buffer */
+ uint8_t *data;
+ size_t data_size;
+
+ /* ptr is used as a moving pointer into the data area
+ * of the packet. The reason its here and not a local
+ * variable in each function is that when a realloc of
+ * a send packet is done we need to move this
+ * pointer */
+ uint8_t *ptr;
+
+ /* this is used to range check and align strings and buffers */
+ struct request_bufinfo bufinfo;
+};
+
+#endif
diff --git a/source4/libcli/raw/signing.h b/source4/libcli/raw/signing.h
new file mode 100644
index 0000000000..56e977ed7c
--- /dev/null
+++ b/source4/libcli/raw/signing.h
@@ -0,0 +1,43 @@
+#ifndef _SIGNING_H
+#define _SIGNING_H
+/*
+ Unix SMB/CIFS implementation.
+ SMB Signing
+
+ Andrew Bartlett <abartlet@samba.org> 2003-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 <http://www.gnu.org/licenses/>.
+*/
+
+enum smb_signing_engine_state {
+ SMB_SIGNING_ENGINE_OFF,
+ SMB_SIGNING_ENGINE_BSRSPYL,
+ SMB_SIGNING_ENGINE_ON
+};
+
+enum smb_signing_state {
+ SMB_SIGNING_OFF, SMB_SIGNING_SUPPORTED,
+ SMB_SIGNING_REQUIRED, SMB_SIGNING_AUTO};
+
+struct smb_signing_context {
+ enum smb_signing_engine_state signing_state;
+ DATA_BLOB mac_key;
+ uint32_t next_seq_num;
+ bool allow_smb_signing;
+ bool doing_signing;
+ bool mandatory_signing;
+ bool seen_valid; /* Have I ever seen a validly signed packet? */
+};
+
+#endif
diff --git a/source4/libcli/raw/smb.h b/source4/libcli/raw/smb.h
new file mode 100644
index 0000000000..d4091acf48
--- /dev/null
+++ b/source4/libcli/raw/smb.h
@@ -0,0 +1,622 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB parameters and setup, plus a whole lot more.
+
+ Copyright (C) Andrew Tridgell 1992-2000
+ Copyright (C) John H Terpstra 1996-2002
+ Copyright (C) Luke Kenneth Casson Leighton 1996-2000
+ Copyright (C) Paul Ashton 1998-2000
+ Copyright (C) Simo Sorce 2001-2002
+ Copyright (C) Martin Pool 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _SMB_H
+#define _SMB_H
+
+/* deny modes */
+#define DENY_DOS 0
+#define DENY_ALL 1
+#define DENY_WRITE 2
+#define DENY_READ 3
+#define DENY_NONE 4
+#define DENY_FCB 7
+
+/* open modes */
+#define DOS_OPEN_RDONLY 0
+#define DOS_OPEN_WRONLY 1
+#define DOS_OPEN_RDWR 2
+#define DOS_OPEN_FCB 0xF
+
+
+/**********************************/
+/* SMBopen field definitions */
+#define OPEN_FLAGS_DENY_MASK 0x70
+#define OPEN_FLAGS_DENY_DOS 0x00
+#define OPEN_FLAGS_DENY_ALL 0x10
+#define OPEN_FLAGS_DENY_WRITE 0x20
+#define OPEN_FLAGS_DENY_READ 0x30
+#define OPEN_FLAGS_DENY_NONE 0x40
+
+#define OPEN_FLAGS_MODE_MASK 0x0F
+#define OPEN_FLAGS_OPEN_READ 0
+#define OPEN_FLAGS_OPEN_WRITE 1
+#define OPEN_FLAGS_OPEN_RDWR 2
+#define OPEN_FLAGS_FCB 0xFF
+
+
+/**********************************/
+/* SMBopenX field definitions */
+
+/* OpenX Flags field. */
+#define OPENX_FLAGS_ADDITIONAL_INFO 0x01
+#define OPENX_FLAGS_REQUEST_OPLOCK 0x02
+#define OPENX_FLAGS_REQUEST_BATCH_OPLOCK 0x04
+#define OPENX_FLAGS_EA_LEN 0x08
+#define OPENX_FLAGS_EXTENDED_RETURN 0x10
+
+/* desired access (open_mode), split info 4 4-bit nibbles */
+#define OPENX_MODE_ACCESS_MASK 0x000F
+#define OPENX_MODE_ACCESS_READ 0x0000
+#define OPENX_MODE_ACCESS_WRITE 0x0001
+#define OPENX_MODE_ACCESS_RDWR 0x0002
+#define OPENX_MODE_ACCESS_EXEC 0x0003
+#define OPENX_MODE_ACCESS_FCB 0x000F
+
+#define OPENX_MODE_DENY_SHIFT 4
+#define OPENX_MODE_DENY_MASK (0xF << OPENX_MODE_DENY_SHIFT)
+#define OPENX_MODE_DENY_DOS (DENY_DOS << OPENX_MODE_DENY_SHIFT)
+#define OPENX_MODE_DENY_ALL (DENY_ALL << OPENX_MODE_DENY_SHIFT)
+#define OPENX_MODE_DENY_WRITE (DENY_WRITE << OPENX_MODE_DENY_SHIFT)
+#define OPENX_MODE_DENY_READ (DENY_READ << OPENX_MODE_DENY_SHIFT)
+#define OPENX_MODE_DENY_NONE (DENY_NONE << OPENX_MODE_DENY_SHIFT)
+#define OPENX_MODE_DENY_FCB (0xF << OPENX_MODE_DENY_SHIFT)
+
+#define OPENX_MODE_LOCALITY_MASK 0x0F00 /* what does this do? */
+
+#define OPENX_MODE_NO_CACHE 0x1000
+#define OPENX_MODE_WRITE_THRU 0x4000
+
+/* open function values */
+#define OPENX_OPEN_FUNC_MASK 0x3
+#define OPENX_OPEN_FUNC_FAIL 0x0
+#define OPENX_OPEN_FUNC_OPEN 0x1
+#define OPENX_OPEN_FUNC_TRUNC 0x2
+
+/* The above can be OR'ed with... */
+#define OPENX_OPEN_FUNC_CREATE 0x10
+
+/* openx action in reply */
+#define OPENX_ACTION_EXISTED 1
+#define OPENX_ACTION_CREATED 2
+#define OPENX_ACTION_TRUNCATED 3
+
+
+/**********************************/
+/* SMBntcreateX field definitions */
+
+/* ntcreatex flags field. */
+#define NTCREATEX_FLAGS_REQUEST_OPLOCK 0x02
+#define NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK 0x04
+#define NTCREATEX_FLAGS_OPEN_DIRECTORY 0x08 /* TODO: opens parent? we need
+ a test suite for this */
+#define NTCREATEX_FLAGS_EXTENDED 0x10
+
+/* the ntcreatex access_mask field
+ this is split into 4 pieces
+ AAAABBBBCCCCCCCCDDDDDDDDDDDDDDDD
+ A -> GENERIC_RIGHT_*
+ B -> SEC_RIGHT_*
+ C -> STD_RIGHT_*
+ D -> SA_RIGHT_*
+
+ which set of SA_RIGHT_* bits is applicable depends on the type
+ of object.
+*/
+
+
+
+/* ntcreatex share_access field */
+#define NTCREATEX_SHARE_ACCESS_NONE 0
+#define NTCREATEX_SHARE_ACCESS_READ 1
+#define NTCREATEX_SHARE_ACCESS_WRITE 2
+#define NTCREATEX_SHARE_ACCESS_DELETE 4
+#define NTCREATEX_SHARE_ACCESS_MASK 7
+
+/* ntcreatex open_disposition field */
+#define NTCREATEX_DISP_SUPERSEDE 0 /* supersede existing file (if it exists) */
+#define NTCREATEX_DISP_OPEN 1 /* if file exists open it, else fail */
+#define NTCREATEX_DISP_CREATE 2 /* if file exists fail, else create it */
+#define NTCREATEX_DISP_OPEN_IF 3 /* if file exists open it, else create it */
+#define NTCREATEX_DISP_OVERWRITE 4 /* if exists overwrite, else fail */
+#define NTCREATEX_DISP_OVERWRITE_IF 5 /* if exists overwrite, else create */
+
+/* ntcreatex create_options field */
+#define NTCREATEX_OPTIONS_DIRECTORY 0x0001
+#define NTCREATEX_OPTIONS_WRITE_THROUGH 0x0002
+#define NTCREATEX_OPTIONS_SEQUENTIAL_ONLY 0x0004
+#define NTCREATEX_OPTIONS_NO_INTERMEDIATE_BUFFERING 0x0008
+#define NTCREATEX_OPTIONS_SYNC_ALERT 0x0010
+#define NTCREATEX_OPTIONS_ASYNC_ALERT 0x0020
+#define NTCREATEX_OPTIONS_NON_DIRECTORY_FILE 0x0040
+#define NTCREATEX_OPTIONS_TREE_CONNECTION 0x0080
+#define NTCREATEX_OPTIONS_COMPLETE_IF_OPLOCKED 0x0100
+#define NTCREATEX_OPTIONS_NO_EA_KNOWLEDGE 0x0200
+#define NTCREATEX_OPTIONS_OPEN_FOR_RECOVERY 0x0400
+#define NTCREATEX_OPTIONS_RANDOM_ACCESS 0x0800
+#define NTCREATEX_OPTIONS_DELETE_ON_CLOSE 0x1000
+#define NTCREATEX_OPTIONS_OPEN_BY_FILE_ID 0x2000
+#define NTCREATEX_OPTIONS_BACKUP_INTENT 0x4000
+#define NTCREATEX_OPTIONS_NO_COMPRESSION 0x8000
+/* Must be ignored by the server, per MS-SMB 2.2.8 */
+#define NTCREATEX_OPTIONS_OPFILTER 0x00100000
+#define NTCREATEX_OPTIONS_REPARSE_POINT 0x00200000
+/* Don't pull this file off tape in a HSM system */
+#define NTCREATEX_OPTIONS_NO_RECALL 0x00400000
+/* Must be ignored by the server, per MS-SMB 2.2.8 */
+#define NTCREATEX_OPTIONS_FREE_SPACE_QUERY 0x00800000
+
+#define NTCREATEX_OPTIONS_MUST_IGNORE_MASK (NTCREATEX_OPTIONS_TREE_CONNECTION | \
+ NTCREATEX_OPTIONS_OPEN_FOR_RECOVERY | \
+ NTCREATEX_OPTIONS_FREE_SPACE_QUERY | \
+ 0x000F0000)
+
+#define NTCREATEX_OPTIONS_NOT_SUPPORTED_MASK (NTCREATEX_OPTIONS_OPEN_BY_FILE_ID)
+
+#define NTCREATEX_OPTIONS_INVALID_PARAM_MASK (NTCREATEX_OPTIONS_OPFILTER | \
+ NTCREATEX_OPTIONS_SYNC_ALERT | \
+ NTCREATEX_OPTIONS_ASYNC_ALERT | \
+ NTCREATEX_OPTIONS_OPFILTER | \
+ 0xFF000000)
+
+/*
+ * We reuse some ignored flags for private use.
+ * This values have different meaning for some ntvfs backends.
+ *
+ * TODO: use values that are ignore for sure...
+ */
+#define NTCREATEX_OPTIONS_PRIVATE_DENY_DOS 0x00010000
+#define NTCREATEX_OPTIONS_PRIVATE_DENY_FCB 0x00020000
+#define NTCREATEX_OPTIONS_PRIVATE_MASK (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS | \
+ NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)
+
+/* ntcreatex impersonation field */
+#define NTCREATEX_IMPERSONATION_ANONYMOUS 0
+#define NTCREATEX_IMPERSONATION_IDENTIFICATION 1
+#define NTCREATEX_IMPERSONATION_IMPERSONATION 2
+#define NTCREATEX_IMPERSONATION_DELEGATION 3
+
+/* ntcreatex security flags bit field */
+#define NTCREATEX_SECURITY_DYNAMIC 1
+#define NTCREATEX_SECURITY_ALL 2
+
+/* ntcreatex create_action in reply */
+#define NTCREATEX_ACTION_EXISTED 1
+#define NTCREATEX_ACTION_CREATED 2
+#define NTCREATEX_ACTION_TRUNCATED 3
+/* the value 5 can also be returned when you try to create a directory with
+ incorrect parameters - what does it mean? maybe created temporary file? */
+#define NTCREATEX_ACTION_UNKNOWN 5
+
+#define SMB_MAGIC 0x424D53FF /* 0xFF 'S' 'M' 'B' */
+
+/* the basic packet size, assuming no words or bytes. Does not include the NBT header */
+#define MIN_SMB_SIZE 35
+
+/* when using NBT encapsulation every packet has a 4 byte header */
+#define NBT_HDR_SIZE 4
+
+/* offsets into message header for common items - NOTE: These have
+ changed from being offsets from the base of the NBT packet to the base of the SMB packet.
+ this has reduced all these values by 4
+*/
+#define HDR_COM 4
+#define HDR_RCLS 5
+#define HDR_REH 6
+#define HDR_ERR 7
+#define HDR_FLG 9
+#define HDR_FLG2 10
+#define HDR_PIDHIGH 12
+#define HDR_SS_FIELD 14
+#define HDR_TID 24
+#define HDR_PID 26
+#define HDR_UID 28
+#define HDR_MID 30
+#define HDR_WCT 32
+#define HDR_VWV 33
+
+
+/* types of buffers in core SMB protocol */
+#define SMB_DATA_BLOCK 0x1
+#define SMB_ASCII4 0x4
+
+
+/* flag defines. CIFS spec 3.1.1 */
+#define FLAG_SUPPORT_LOCKREAD 0x01
+#define FLAG_CLIENT_BUF_AVAIL 0x02
+#define FLAG_RESERVED 0x04
+#define FLAG_CASELESS_PATHNAMES 0x08
+#define FLAG_CANONICAL_PATHNAMES 0x10
+#define FLAG_REQUEST_OPLOCK 0x20
+#define FLAG_REQUEST_BATCH_OPLOCK 0x40
+#define FLAG_REPLY 0x80
+
+/* the complete */
+#define SMBmkdir 0x00 /* create directory */
+#define SMBrmdir 0x01 /* delete directory */
+#define SMBopen 0x02 /* open file */
+#define SMBcreate 0x03 /* create file */
+#define SMBclose 0x04 /* close file */
+#define SMBflush 0x05 /* flush file */
+#define SMBunlink 0x06 /* delete file */
+#define SMBmv 0x07 /* rename file */
+#define SMBgetatr 0x08 /* get file attributes */
+#define SMBsetatr 0x09 /* set file attributes */
+#define SMBread 0x0A /* read from file */
+#define SMBwrite 0x0B /* write to file */
+#define SMBlock 0x0C /* lock byte range */
+#define SMBunlock 0x0D /* unlock byte range */
+#define SMBctemp 0x0E /* create temporary file */
+#define SMBmknew 0x0F /* make new file */
+#define SMBchkpth 0x10 /* check directory path */
+#define SMBexit 0x11 /* process exit */
+#define SMBlseek 0x12 /* seek */
+#define SMBtcon 0x70 /* tree connect */
+#define SMBtconX 0x75 /* tree connect and X*/
+#define SMBtdis 0x71 /* tree disconnect */
+#define SMBnegprot 0x72 /* negotiate protocol */
+#define SMBdskattr 0x80 /* get disk attributes */
+#define SMBsearch 0x81 /* search directory */
+#define SMBsplopen 0xC0 /* open print spool file */
+#define SMBsplwr 0xC1 /* write to print spool file */
+#define SMBsplclose 0xC2 /* close print spool file */
+#define SMBsplretq 0xC3 /* return print queue */
+#define SMBsends 0xD0 /* send single block message */
+#define SMBsendb 0xD1 /* send broadcast message */
+#define SMBfwdname 0xD2 /* forward user name */
+#define SMBcancelf 0xD3 /* cancel forward */
+#define SMBgetmac 0xD4 /* get machine name */
+#define SMBsendstrt 0xD5 /* send start of multi-block message */
+#define SMBsendend 0xD6 /* send end of multi-block message */
+#define SMBsendtxt 0xD7 /* send text of multi-block message */
+
+/* Core+ protocol */
+#define SMBlockread 0x13 /* Lock a range and read */
+#define SMBwriteunlock 0x14 /* write then range then unlock it */
+#define SMBreadbraw 0x1a /* read a block of data with no smb header */
+#define SMBwritebraw 0x1d /* write a block of data with no smb header */
+#define SMBwritec 0x20 /* secondary write request */
+#define SMBwriteclose 0x2c /* write a file then close it */
+
+/* dos extended protocol */
+#define SMBreadBraw 0x1A /* read block raw */
+#define SMBreadBmpx 0x1B /* read block multiplexed */
+#define SMBreadBs 0x1C /* read block (secondary response) */
+#define SMBwriteBraw 0x1D /* write block raw */
+#define SMBwriteBmpx 0x1E /* write block multiplexed */
+#define SMBwriteBs 0x1F /* write block (secondary request) */
+#define SMBwriteC 0x20 /* write complete response */
+#define SMBsetattrE 0x22 /* set file attributes expanded */
+#define SMBgetattrE 0x23 /* get file attributes expanded */
+#define SMBlockingX 0x24 /* lock/unlock byte ranges and X */
+#define SMBtrans 0x25 /* transaction - name, bytes in/out */
+#define SMBtranss 0x26 /* transaction (secondary request/response) */
+#define SMBioctl 0x27 /* IOCTL */
+#define SMBioctls 0x28 /* IOCTL (secondary request/response) */
+#define SMBcopy 0x29 /* copy */
+#define SMBmove 0x2A /* move */
+#define SMBecho 0x2B /* echo */
+#define SMBopenX 0x2D /* open and X */
+#define SMBreadX 0x2E /* read and X */
+#define SMBwriteX 0x2F /* write and X */
+#define SMBsesssetupX 0x73 /* Session Set Up & X (including User Logon) */
+#define SMBffirst 0x82 /* find first */
+#define SMBfunique 0x83 /* find unique */
+#define SMBfclose 0x84 /* find close */
+#define SMBkeepalive 0x85 /* keepalive */
+#define SMBinvalid 0xFE /* invalid command */
+
+/* Extended 2.0 protocol */
+#define SMBtrans2 0x32 /* TRANS2 protocol set */
+#define SMBtranss2 0x33 /* TRANS2 protocol set, secondary command */
+#define SMBfindclose 0x34 /* Terminate a TRANSACT2_FINDFIRST */
+#define SMBfindnclose 0x35 /* Terminate a TRANSACT2_FINDNOTIFYFIRST */
+#define SMBulogoffX 0x74 /* user logoff */
+
+/* NT SMB extensions. */
+#define SMBnttrans 0xA0 /* NT transact */
+#define SMBnttranss 0xA1 /* NT transact secondary */
+#define SMBntcreateX 0xA2 /* NT create and X */
+#define SMBntcancel 0xA4 /* NT cancel */
+#define SMBntrename 0xA5 /* NT rename */
+
+/* used to indicate end of chain */
+#define SMB_CHAIN_NONE 0xFF
+
+/* These are the trans subcommands */
+#define TRANSACT_SETNAMEDPIPEHANDLESTATE 0x01
+#define TRANSACT_DCERPCCMD 0x26
+#define TRANSACT_WAITNAMEDPIPEHANDLESTATE 0x53
+
+/* These are the NT transact sub commands. */
+#define NT_TRANSACT_CREATE 1
+#define NT_TRANSACT_IOCTL 2
+#define NT_TRANSACT_SET_SECURITY_DESC 3
+#define NT_TRANSACT_NOTIFY_CHANGE 4
+#define NT_TRANSACT_RENAME 5
+#define NT_TRANSACT_QUERY_SECURITY_DESC 6
+
+/* this is used on a TConX. I'm not sure the name is very helpful though */
+#define SMB_SUPPORT_SEARCH_BITS 0x0001
+#define SMB_SHARE_IN_DFS 0x0002
+
+/* Named pipe write mode flags. Used in writeX calls. */
+#define PIPE_RAW_MODE 0x4
+#define PIPE_START_MESSAGE 0x8
+
+/* the desired access to use when opening a pipe */
+#define DESIRED_ACCESS_PIPE 0x2019f
+
+
+/* Mapping of generic access rights for files to specific rights. */
+#define FILE_GENERIC_ALL (STANDARD_RIGHTS_REQUIRED_ACCESS| NT_ACCESS_SYNCHRONIZE_ACCESS|FILE_ALL_ACCESS)
+
+#define FILE_GENERIC_READ (STANDARD_RIGHTS_READ_ACCESS|FILE_READ_DATA|FILE_READ_ATTRIBUTES|\
+ FILE_READ_EA|NT_ACCESS_SYNCHRONIZE_ACCESS)
+
+#define FILE_GENERIC_WRITE (STANDARD_RIGHTS_WRITE_ACCESS|FILE_WRITE_DATA|FILE_WRITE_ATTRIBUTES|\
+ FILE_WRITE_EA|FILE_APPEND_DATA|NT_ACCESS_SYNCHRONIZE_ACCESS)
+
+#define FILE_GENERIC_EXECUTE (STANDARD_RIGHTS_EXECUTE_ACCESS|FILE_READ_ATTRIBUTES|\
+ FILE_EXECUTE|NT_ACCESS_SYNCHRONIZE_ACCESS)
+
+
+/* FileAttributes (search attributes) field */
+#define FILE_ATTRIBUTE_READONLY 0x0001
+#define FILE_ATTRIBUTE_HIDDEN 0x0002
+#define FILE_ATTRIBUTE_SYSTEM 0x0004
+#define FILE_ATTRIBUTE_VOLUME 0x0008
+#define FILE_ATTRIBUTE_DIRECTORY 0x0010
+#define FILE_ATTRIBUTE_ARCHIVE 0x0020
+#define FILE_ATTRIBUTE_DEVICE 0x0040
+#define FILE_ATTRIBUTE_NORMAL 0x0080
+#define FILE_ATTRIBUTE_TEMPORARY 0x0100
+#define FILE_ATTRIBUTE_SPARSE 0x0200
+#define FILE_ATTRIBUTE_REPARSE_POINT 0x0400
+#define FILE_ATTRIBUTE_COMPRESSED 0x0800
+#define FILE_ATTRIBUTE_OFFLINE 0x1000
+#define FILE_ATTRIBUTE_NONINDEXED 0x2000
+#define FILE_ATTRIBUTE_ENCRYPTED 0x4000
+#define FILE_ATTRIBUTE_ALL_MASK 0x7FFF
+
+/* Flags - combined with attributes. */
+#define FILE_FLAG_WRITE_THROUGH 0x80000000L
+#define FILE_FLAG_NO_BUFFERING 0x20000000L
+#define FILE_FLAG_RANDOM_ACCESS 0x10000000L
+#define FILE_FLAG_SEQUENTIAL_SCAN 0x08000000L
+#define FILE_FLAG_DELETE_ON_CLOSE 0x04000000L
+#define FILE_FLAG_BACKUP_SEMANTICS 0x02000000L /* only if backup/restore privilege? */
+#define FILE_FLAG_POSIX_SEMANTICS 0x01000000L
+
+/* Responses when opening a file. */
+#define FILE_WAS_SUPERSEDED 0
+#define FILE_WAS_OPENED 1
+#define FILE_WAS_CREATED 2
+#define FILE_WAS_OVERWRITTEN 3
+
+/* File type flags */
+#define FILE_TYPE_DISK 0
+#define FILE_TYPE_BYTE_MODE_PIPE 1
+#define FILE_TYPE_MESSAGE_MODE_PIPE 2
+#define FILE_TYPE_PRINTER 3
+#define FILE_TYPE_COMM_DEVICE 4
+#define FILE_TYPE_UNKNOWN 0xFFFF
+
+/* Flag for NT transact rename call. */
+#define RENAME_REPLACE_IF_EXISTS 1
+
+/* flags for SMBntrename call */
+#define RENAME_FLAG_MOVE_CLUSTER_INFORMATION 0x102 /* ???? */
+#define RENAME_FLAG_HARD_LINK 0x103
+#define RENAME_FLAG_RENAME 0x104
+#define RENAME_FLAG_COPY 0x105
+
+/* Filesystem Attributes. */
+#define FILE_CASE_SENSITIVE_SEARCH 0x01
+#define FILE_CASE_PRESERVED_NAMES 0x02
+#define FILE_UNICODE_ON_DISK 0x04
+/* According to cifs9f, this is 4, not 8 */
+/* Acconding to testing, this actually sets the security attribute! */
+#define FILE_PERSISTENT_ACLS 0x08
+/* These entries added from cifs9f --tsb */
+#define FILE_FILE_COMPRESSION 0x10
+#define FILE_VOLUME_QUOTAS 0x20
+/* I think this is wrong. JRA #define FILE_DEVICE_IS_MOUNTED 0x20 */
+#define FILE_VOLUME_SPARSE_FILE 0x40
+#define FILE_VOLUME_IS_COMPRESSED 0x8000
+
+/* ChangeNotify flags. */
+#define FILE_NOTIFY_CHANGE_FILE_NAME 0x00000001
+#define FILE_NOTIFY_CHANGE_DIR_NAME 0x00000002
+#define FILE_NOTIFY_CHANGE_ATTRIBUTES 0x00000004
+#define FILE_NOTIFY_CHANGE_SIZE 0x00000008
+#define FILE_NOTIFY_CHANGE_LAST_WRITE 0x00000010
+#define FILE_NOTIFY_CHANGE_LAST_ACCESS 0x00000020
+#define FILE_NOTIFY_CHANGE_CREATION 0x00000040
+#define FILE_NOTIFY_CHANGE_EA 0x00000080
+#define FILE_NOTIFY_CHANGE_SECURITY 0x00000100
+#define FILE_NOTIFY_CHANGE_STREAM_NAME 0x00000200
+#define FILE_NOTIFY_CHANGE_STREAM_SIZE 0x00000400
+#define FILE_NOTIFY_CHANGE_STREAM_WRITE 0x00000800
+
+#define FILE_NOTIFY_CHANGE_NAME \
+ (FILE_NOTIFY_CHANGE_FILE_NAME|FILE_NOTIFY_CHANGE_DIR_NAME)
+
+/* change notify action results */
+#define NOTIFY_ACTION_ADDED 1
+#define NOTIFY_ACTION_REMOVED 2
+#define NOTIFY_ACTION_MODIFIED 3
+#define NOTIFY_ACTION_OLD_NAME 4
+#define NOTIFY_ACTION_NEW_NAME 5
+#define NOTIFY_ACTION_ADDED_STREAM 6
+#define NOTIFY_ACTION_REMOVED_STREAM 7
+#define NOTIFY_ACTION_MODIFIED_STREAM 8
+
+/* seek modes for smb_seek */
+#define SEEK_MODE_START 0
+#define SEEK_MODE_CURRENT 1
+#define SEEK_MODE_END 2
+
+/* where to find the base of the SMB packet proper */
+/* REWRITE TODO: smb_base needs to be removed */
+#define smb_base(buf) (((char *)(buf))+4)
+
+/* we don't allow server strings to be longer than 48 characters as
+ otherwise NT will not honour the announce packets */
+#define MAX_SERVER_STRING_LENGTH 48
+
+/* This was set by JHT in liaison with Jeremy Allison early 1997
+ * History:
+ * Version 4.0 - never made public
+ * Version 4.10 - New to 1.9.16p2, lost in space 1.9.16p3 to 1.9.16p9
+ * - Reappeared in 1.9.16p11 with fixed smbd services
+ * Version 4.20 - To indicate that nmbd and browsing now works better
+ * Version 4.50 - Set at release of samba-2.2.0 by JHT
+ *
+ * Note: In the presence of NT4.X do not set above 4.9
+ * Setting this above 4.9 can have undesired side-effects.
+ * This may change again in Samba-3.0 after further testing. JHT
+ */
+
+#define DEFAULT_MAJOR_VERSION 0x04
+#define DEFAULT_MINOR_VERSION 0x09
+
+/* Browser Election Values */
+#define BROWSER_ELECTION_VERSION 0x010f
+#define BROWSER_CONSTANT 0xaa55
+
+/* Sercurity mode bits. */
+#define NEGOTIATE_SECURITY_USER_LEVEL 0x01
+#define NEGOTIATE_SECURITY_CHALLENGE_RESPONSE 0x02
+#define NEGOTIATE_SECURITY_SIGNATURES_ENABLED 0x04
+#define NEGOTIATE_SECURITY_SIGNATURES_REQUIRED 0x08
+
+/* NT Flags2 bits - cifs6.txt section 3.1.2 */
+#define FLAGS2_LONG_PATH_COMPONENTS 0x0001
+#define FLAGS2_EXTENDED_ATTRIBUTES 0x0002
+#define FLAGS2_SMB_SECURITY_SIGNATURES 0x0004
+#define FLAGS2_IS_LONG_NAME 0x0040
+#define FLAGS2_EXTENDED_SECURITY 0x0800
+#define FLAGS2_DFS_PATHNAMES 0x1000
+#define FLAGS2_READ_PERMIT_EXECUTE 0x2000
+#define FLAGS2_32_BIT_ERROR_CODES 0x4000
+#define FLAGS2_UNICODE_STRINGS 0x8000
+
+
+/* CIFS protocol capabilities */
+#define CAP_RAW_MODE 0x00000001
+#define CAP_MPX_MODE 0x00000002
+#define CAP_UNICODE 0x00000004
+#define CAP_LARGE_FILES 0x00000008
+#define CAP_NT_SMBS 0x00000010
+#define CAP_RPC_REMOTE_APIS 0x00000020
+#define CAP_STATUS32 0x00000040
+#define CAP_LEVEL_II_OPLOCKS 0x00000080
+#define CAP_LOCK_AND_READ 0x00000100
+#define CAP_NT_FIND 0x00000200
+#define CAP_DFS 0x00001000
+#define CAP_W2K_SMBS 0x00002000
+#define CAP_LARGE_READX 0x00004000
+#define CAP_LARGE_WRITEX 0x00008000
+#define CAP_UNIX 0x00800000 /* Capabilities for UNIX extensions. Created by HP. */
+#define CAP_EXTENDED_SECURITY 0x80000000
+
+/*
+ * Global value meaning that the smb_uid field should be
+ * ingored (in share level security and protocol level == CORE)
+ */
+
+#define UID_FIELD_INVALID 0
+
+/* Lock types. */
+#define LOCKING_ANDX_SHARED_LOCK 0x01
+#define LOCKING_ANDX_OPLOCK_RELEASE 0x02
+#define LOCKING_ANDX_CHANGE_LOCKTYPE 0x04
+#define LOCKING_ANDX_CANCEL_LOCK 0x08
+#define LOCKING_ANDX_LARGE_FILES 0x10
+
+/*
+ * Bits we test with.
+ */
+
+#define OPLOCK_NONE 0
+#define OPLOCK_EXCLUSIVE 1
+#define OPLOCK_BATCH 2
+#define OPLOCK_LEVEL_II 4
+
+#define CORE_OPLOCK_GRANTED (1<<5)
+#define EXTENDED_OPLOCK_GRANTED (1<<15)
+
+/*
+ * Return values for oplock types.
+ */
+
+#define NO_OPLOCK_RETURN 0
+#define EXCLUSIVE_OPLOCK_RETURN 1
+#define BATCH_OPLOCK_RETURN 2
+#define LEVEL_II_OPLOCK_RETURN 3
+
+/* oplock levels sent in oplock break */
+#define OPLOCK_BREAK_TO_NONE 0
+#define OPLOCK_BREAK_TO_LEVEL_II 1
+
+
+#define CMD_REPLY 0x8000
+
+/* The maximum length of a trust account password.
+ Used when we randomly create it, 15 char passwords
+ exceed NT4's max password length */
+
+#define DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH 14
+
+
+/*
+ filesystem attribute bits
+*/
+#define FS_ATTR_CASE_SENSITIVE_SEARCH 0x00000001
+#define FS_ATTR_CASE_PRESERVED_NAMES 0x00000002
+#define FS_ATTR_UNICODE_ON_DISK 0x00000004
+#define FS_ATTR_PERSISTANT_ACLS 0x00000008
+#define FS_ATTR_COMPRESSION 0x00000010
+#define FS_ATTR_QUOTAS 0x00000020
+#define FS_ATTR_SPARSE_FILES 0x00000040
+#define FS_ATTR_REPARSE_POINTS 0x00000080
+#define FS_ATTR_REMOTE_STORAGE 0x00000100
+#define FS_ATTR_LFN_SUPPORT 0x00004000
+#define FS_ATTR_IS_COMPRESSED 0x00008000
+#define FS_ATTR_OBJECT_IDS 0x00010000
+#define FS_ATTR_ENCRYPTION 0x00020000
+#define FS_ATTR_NAMED_STREAMS 0x00040000
+
+#define smb_len(buf) (PVAL(buf,3)|(PVAL(buf,2)<<8)|(PVAL(buf,1)<<16))
+#define _smb_setlen(buf,len) do {(buf)[0] = 0; (buf)[1] = ((len)&0x10000)>>16; \
+ (buf)[2] = ((len)&0xFF00)>>8; (buf)[3] = (len)&0xFF;} while (0)
+#define _smb2_setlen(buf,len) do {(buf)[0] = 0; (buf)[1] = ((len)&0xFF0000)>>16; \
+ (buf)[2] = ((len)&0xFF00)>>8; (buf)[3] = (len)&0xFF;} while (0)
+
+#include "libcli/raw/trans2.h"
+#include "libcli/raw/interfaces.h"
+
+#endif /* _SMB_H */
diff --git a/source4/libcli/raw/smb_signing.c b/source4/libcli/raw/smb_signing.c
new file mode 100644
index 0000000000..9f94039078
--- /dev/null
+++ b/source4/libcli/raw/smb_signing.c
@@ -0,0 +1,407 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB Signing Code
+ Copyright (C) Jeremy Allison 2002.
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2002-2003
+ Copyright (C) James J Myers <myersjj@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "smb.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "../lib/crypto/crypto.h"
+
+/***********************************************************
+ SMB signing - Common code before we set a new signing implementation
+************************************************************/
+bool set_smb_signing_common(struct smb_signing_context *sign_info)
+{
+ if (sign_info->doing_signing) {
+ DEBUG(5, ("SMB Signing already in progress, so we don't start it again\n"));
+ return false;
+ }
+
+ if (!sign_info->allow_smb_signing) {
+ DEBUG(5, ("SMB Signing has been locally disabled\n"));
+ return false;
+ }
+
+ return true;
+}
+
+/***********************************************************
+ SMB signing - Common code before we set a new signing implementation
+************************************************************/
+static bool smbcli_set_smb_signing_common(struct smbcli_transport *transport)
+{
+ if (!set_smb_signing_common(&transport->negotiate.sign_info)) {
+ return false;
+ }
+
+ if (!(transport->negotiate.sec_mode &
+ (NEGOTIATE_SECURITY_SIGNATURES_REQUIRED|NEGOTIATE_SECURITY_SIGNATURES_ENABLED))) {
+ DEBUG(5, ("SMB Signing is not negotiated by the peer\n"));
+ return false;
+ }
+
+ /* These calls are INCOMPATIBLE with SMB signing */
+ transport->negotiate.readbraw_supported = false;
+ transport->negotiate.writebraw_supported = false;
+
+ return true;
+}
+
+void mark_packet_signed(struct smb_request_buffer *out)
+{
+ uint16_t flags2;
+ flags2 = SVAL(out->hdr, HDR_FLG2);
+ flags2 |= FLAGS2_SMB_SECURITY_SIGNATURES;
+ SSVAL(out->hdr, HDR_FLG2, flags2);
+}
+
+bool signing_good(struct smb_signing_context *sign_info,
+ unsigned int seq, bool good)
+{
+ if (good) {
+ if (!sign_info->doing_signing) {
+ DEBUG(5, ("Seen valid packet, so turning signing on\n"));
+ sign_info->doing_signing = true;
+ }
+ if (!sign_info->seen_valid) {
+ DEBUG(5, ("Seen valid packet, so marking signing as 'seen valid'\n"));
+ sign_info->seen_valid = true;
+ }
+ } else {
+ if (!sign_info->seen_valid) {
+ /* If we have never seen a good packet, just turn it off */
+ DEBUG(5, ("signing_good: signing negotiated but not required and peer\n"
+ "isn't sending correct signatures. Turning off.\n"));
+ smbcli_set_signing_off(sign_info);
+ return true;
+ } else {
+ /* bad packet after signing started - fail and disconnect. */
+ DEBUG(0, ("signing_good: BAD SIG: seq %u\n", seq));
+ return false;
+ }
+ }
+ return true;
+}
+
+void sign_outgoing_message(struct smb_request_buffer *out, DATA_BLOB *mac_key, unsigned int seq_num)
+{
+ uint8_t calc_md5_mac[16];
+ struct MD5Context md5_ctx;
+
+ /*
+ * Firstly put the sequence number into the first 4 bytes.
+ * and zero out the next 4 bytes.
+ */
+ SIVAL(out->hdr, HDR_SS_FIELD, seq_num);
+ SIVAL(out->hdr, HDR_SS_FIELD + 4, 0);
+
+ /* mark the packet as signed - BEFORE we sign it...*/
+ mark_packet_signed(out);
+
+ /* Calculate the 16 byte MAC and place first 8 bytes into the field. */
+ MD5Init(&md5_ctx);
+ MD5Update(&md5_ctx, mac_key->data, mac_key->length);
+ MD5Update(&md5_ctx,
+ out->buffer + NBT_HDR_SIZE,
+ out->size - NBT_HDR_SIZE);
+ MD5Final(calc_md5_mac, &md5_ctx);
+
+ memcpy(&out->hdr[HDR_SS_FIELD], calc_md5_mac, 8);
+
+ DEBUG(5, ("sign_outgoing_message: SENT SIG (seq: %d): sent SMB signature of\n",
+ seq_num));
+ dump_data(5, calc_md5_mac, 8);
+/* req->out.hdr[HDR_SS_FIELD+2]=0;
+ Uncomment this to test if the remote server actually verifies signitures...*/
+}
+
+bool check_signed_incoming_message(struct smb_request_buffer *in, DATA_BLOB *mac_key, uint_t seq_num)
+{
+ bool good;
+ uint8_t calc_md5_mac[16];
+ uint8_t *server_sent_mac;
+ uint8_t sequence_buf[8];
+ struct MD5Context md5_ctx;
+ const size_t offset_end_of_sig = (HDR_SS_FIELD + 8);
+ int i;
+ const int sign_range = 0;
+
+ /* room enough for the signature? */
+ if (in->size < NBT_HDR_SIZE + HDR_SS_FIELD + 8) {
+ return false;
+ }
+
+ if (!mac_key->length) {
+ /* NO key yet */
+ return false;
+ }
+
+ /* its quite bogus to be guessing sequence numbers, but very useful
+ when debugging signing implementations */
+ for (i = 0-sign_range; i <= 0+sign_range; i++) {
+ /*
+ * Firstly put the sequence number into the first 4 bytes.
+ * and zero out the next 4 bytes.
+ */
+ SIVAL(sequence_buf, 0, seq_num + i);
+ SIVAL(sequence_buf, 4, 0);
+
+ /* get a copy of the server-sent mac */
+ server_sent_mac = &in->hdr[HDR_SS_FIELD];
+
+ /* Calculate the 16 byte MAC and place first 8 bytes into the field. */
+ MD5Init(&md5_ctx);
+ MD5Update(&md5_ctx, mac_key->data,
+ mac_key->length);
+ MD5Update(&md5_ctx, in->hdr, HDR_SS_FIELD);
+ MD5Update(&md5_ctx, sequence_buf, sizeof(sequence_buf));
+
+ MD5Update(&md5_ctx, in->hdr + offset_end_of_sig,
+ in->size - NBT_HDR_SIZE - (offset_end_of_sig));
+ MD5Final(calc_md5_mac, &md5_ctx);
+
+ good = (memcmp(server_sent_mac, calc_md5_mac, 8) == 0);
+
+ if (i == 0) {
+ if (!good) {
+ DEBUG(5, ("check_signed_incoming_message: BAD SIG (seq: %d): wanted SMB signature of\n", seq_num + i));
+ dump_data(5, calc_md5_mac, 8);
+
+ DEBUG(5, ("check_signed_incoming_message: BAD SIG (seq: %d): got SMB signature of\n", seq_num + i));
+ dump_data(5, server_sent_mac, 8);
+ } else {
+ DEBUG(15, ("check_signed_incoming_message: GOOD SIG (seq: %d): got SMB signature of\n", seq_num + i));
+ dump_data(5, server_sent_mac, 8);
+ }
+ }
+
+ if (good) break;
+ }
+
+ if (good && i != 0) {
+ DEBUG(0,("SIGNING OFFSET %d (should be %d)\n", i, seq_num));
+ }
+
+ return good;
+}
+
+static void smbcli_req_allocate_seq_num(struct smbcli_request *req)
+{
+ req->seq_num = req->transport->negotiate.sign_info.next_seq_num;
+
+ /* some requests (eg. NTcancel) are one way, and the sequence number
+ should be increased by 1 not 2 */
+ if (req->sign_single_increment) {
+ req->transport->negotiate.sign_info.next_seq_num += 1;
+ } else {
+ req->transport->negotiate.sign_info.next_seq_num += 2;
+ }
+}
+
+/***********************************************************
+ SMB signing - Simple implementation - calculate a MAC to send.
+************************************************************/
+void smbcli_request_calculate_sign_mac(struct smbcli_request *req)
+{
+#if 0
+ /* enable this when packet signing is preventing you working out why valgrind
+ says that data is uninitialised */
+ file_save("pkt.dat", req->out.buffer, req->out.size);
+#endif
+
+ switch (req->transport->negotiate.sign_info.signing_state) {
+ case SMB_SIGNING_ENGINE_OFF:
+ break;
+
+ case SMB_SIGNING_ENGINE_BSRSPYL:
+ /* mark the packet as signed - BEFORE we sign it...*/
+ mark_packet_signed(&req->out);
+
+ /* I wonder what BSRSPYL stands for - but this is what MS
+ actually sends! */
+ memcpy((req->out.hdr + HDR_SS_FIELD), "BSRSPYL ", 8);
+ break;
+
+ case SMB_SIGNING_ENGINE_ON:
+
+ smbcli_req_allocate_seq_num(req);
+ sign_outgoing_message(&req->out,
+ &req->transport->negotiate.sign_info.mac_key,
+ req->seq_num);
+ break;
+ }
+ return;
+}
+
+
+/**
+ SMB signing - NULL implementation
+
+ @note Used as an initialisation only - it will not correctly
+ shut down a real signing mechanism
+*/
+bool smbcli_set_signing_off(struct smb_signing_context *sign_info)
+{
+ DEBUG(5, ("Shutdown SMB signing\n"));
+ sign_info->doing_signing = false;
+ data_blob_free(&sign_info->mac_key);
+ sign_info->signing_state = SMB_SIGNING_ENGINE_OFF;
+ return true;
+}
+
+/**
+ SMB signing - TEMP implementation - setup the MAC key.
+
+*/
+bool smbcli_temp_set_signing(struct smbcli_transport *transport)
+{
+ if (!smbcli_set_smb_signing_common(transport)) {
+ return false;
+ }
+ DEBUG(5, ("BSRSPYL SMB signing enabled\n"));
+ smbcli_set_signing_off(&transport->negotiate.sign_info);
+
+ transport->negotiate.sign_info.mac_key = data_blob(NULL, 0);
+ transport->negotiate.sign_info.signing_state = SMB_SIGNING_ENGINE_BSRSPYL;
+
+ return true;
+}
+
+/***********************************************************
+ SMB signing - Simple implementation - check a MAC sent by server.
+************************************************************/
+/**
+ * Check a packet supplied by the server.
+ * @return false if we had an established signing connection
+ * which had a back checksum, true otherwise
+ */
+bool smbcli_request_check_sign_mac(struct smbcli_request *req)
+{
+ bool good;
+
+ if (!req->transport->negotiate.sign_info.doing_signing &&
+ req->sign_caller_checks) {
+ return true;
+ }
+
+ req->sign_caller_checks = false;
+
+ switch (req->transport->negotiate.sign_info.signing_state)
+ {
+ case SMB_SIGNING_ENGINE_OFF:
+ return true;
+ case SMB_SIGNING_ENGINE_BSRSPYL:
+ return true;
+
+ case SMB_SIGNING_ENGINE_ON:
+ {
+ if (req->in.size < (HDR_SS_FIELD + 8)) {
+ return false;
+ } else {
+ good = check_signed_incoming_message(&req->in,
+ &req->transport->negotiate.sign_info.mac_key,
+ req->seq_num+1);
+
+ return signing_good(&req->transport->negotiate.sign_info,
+ req->seq_num+1, good);
+ }
+ }
+ }
+ return false;
+}
+
+
+/***********************************************************
+ SMB signing - Simple implementation - setup the MAC key.
+************************************************************/
+bool smbcli_simple_set_signing(TALLOC_CTX *mem_ctx,
+ struct smb_signing_context *sign_info,
+ const DATA_BLOB *user_session_key,
+ const DATA_BLOB *response)
+{
+ if (sign_info->mandatory_signing) {
+ DEBUG(5, ("Mandatory SMB signing enabled!\n"));
+ }
+
+ DEBUG(5, ("SMB signing enabled!\n"));
+
+ if (response && response->length) {
+ sign_info->mac_key = data_blob_talloc(mem_ctx, NULL, response->length + user_session_key->length);
+ } else {
+ sign_info->mac_key = data_blob_talloc(mem_ctx, NULL, user_session_key->length);
+ }
+
+ memcpy(&sign_info->mac_key.data[0], user_session_key->data, user_session_key->length);
+
+ if (response && response->length) {
+ memcpy(&sign_info->mac_key.data[user_session_key->length],response->data, response->length);
+ }
+
+ dump_data_pw("Started Signing with key:\n", sign_info->mac_key.data, sign_info->mac_key.length);
+
+ sign_info->signing_state = SMB_SIGNING_ENGINE_ON;
+ sign_info->next_seq_num = 2;
+
+ return true;
+}
+
+
+/***********************************************************
+ SMB signing - Simple implementation - setup the MAC key.
+************************************************************/
+bool smbcli_transport_simple_set_signing(struct smbcli_transport *transport,
+ const DATA_BLOB user_session_key,
+ const DATA_BLOB response)
+{
+ if (!smbcli_set_smb_signing_common(transport)) {
+ return false;
+ }
+
+ return smbcli_simple_set_signing(transport,
+ &transport->negotiate.sign_info,
+ &user_session_key,
+ &response);
+}
+
+
+bool smbcli_init_signing(struct smbcli_transport *transport)
+{
+ transport->negotiate.sign_info.next_seq_num = 0;
+ transport->negotiate.sign_info.mac_key = data_blob(NULL, 0);
+ if (!smbcli_set_signing_off(&transport->negotiate.sign_info)) {
+ return false;
+ }
+
+ switch (transport->options.signing) {
+ case SMB_SIGNING_OFF:
+ transport->negotiate.sign_info.allow_smb_signing = false;
+ break;
+ case SMB_SIGNING_SUPPORTED:
+ case SMB_SIGNING_AUTO:
+ transport->negotiate.sign_info.allow_smb_signing = true;
+ break;
+ case SMB_SIGNING_REQUIRED:
+ transport->negotiate.sign_info.allow_smb_signing = true;
+ transport->negotiate.sign_info.mandatory_signing = true;
+ break;
+ }
+ return true;
+}
diff --git a/source4/libcli/raw/trans2.h b/source4/libcli/raw/trans2.h
new file mode 100644
index 0000000000..63632eb5ed
--- /dev/null
+++ b/source4/libcli/raw/trans2.h
@@ -0,0 +1,476 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB transaction2 handling
+ Copyright (C) Jeremy Allison 1994-2002.
+ Copyright (C) Andrew Tridgell 1995-2003.
+ Copyright (C) James Peach 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _TRANS2_H_
+#define _TRANS2_H_
+
+/* These are the TRANS2 sub commands */
+#define TRANSACT2_OPEN 0
+#define TRANSACT2_FINDFIRST 1
+#define TRANSACT2_FINDNEXT 2
+#define TRANSACT2_QFSINFO 3
+#define TRANSACT2_SETFSINFO 4
+#define TRANSACT2_QPATHINFO 5
+#define TRANSACT2_SETPATHINFO 6
+#define TRANSACT2_QFILEINFO 7
+#define TRANSACT2_SETFILEINFO 8
+#define TRANSACT2_FSCTL 9
+#define TRANSACT2_IOCTL 0xA
+#define TRANSACT2_FINDNOTIFYFIRST 0xB
+#define TRANSACT2_FINDNOTIFYNEXT 0xC
+#define TRANSACT2_MKDIR 0xD
+#define TRANSACT2_SESSION_SETUP 0xE
+#define TRANSACT2_GET_DFS_REFERRAL 0x10
+#define TRANSACT2_REPORT_DFS_INCONSISTANCY 0x11
+
+
+/* trans2 Query FS info levels */
+/*
+w2k3 TRANS2ALIASES:
+Checking for QFSINFO aliases
+ Found level 1 (0x001) of size 18 (0x12)
+ Found level 2 (0x002) of size 12 (0x0c)
+ Found level 258 (0x102) of size 26 (0x1a)
+ Found level 259 (0x103) of size 24 (0x18)
+ Found level 260 (0x104) of size 8 (0x08)
+ Found level 261 (0x105) of size 20 (0x14)
+ Found level 1001 (0x3e9) of size 26 (0x1a)
+ Found level 1003 (0x3eb) of size 24 (0x18)
+ Found level 1004 (0x3ec) of size 8 (0x08)
+ Found level 1005 (0x3ed) of size 20 (0x14)
+ Found level 1006 (0x3ee) of size 48 (0x30)
+ Found level 1007 (0x3ef) of size 32 (0x20)
+ Found level 1008 (0x3f0) of size 64 (0x40)
+Found 13 levels with success status
+ Level 261 (0x105) and level 1005 (0x3ed) are possible aliases
+ Level 260 (0x104) and level 1004 (0x3ec) are possible aliases
+ Level 259 (0x103) and level 1003 (0x3eb) are possible aliases
+ Level 258 (0x102) and level 1001 (0x3e9) are possible aliases
+Found 4 aliased levels
+*/
+#define SMB_QFS_ALLOCATION 1
+#define SMB_QFS_VOLUME 2
+#define SMB_QFS_VOLUME_INFO 0x102
+#define SMB_QFS_SIZE_INFO 0x103
+#define SMB_QFS_DEVICE_INFO 0x104
+#define SMB_QFS_ATTRIBUTE_INFO 0x105
+#define SMB_QFS_UNIX_INFO 0x200
+#define SMB_QFS_POSIX_INFO 0x201
+#define SMB_QFS_POSIX_WHOAMI 0x202
+#define SMB_QFS_VOLUME_INFORMATION 1001
+#define SMB_QFS_SIZE_INFORMATION 1003
+#define SMB_QFS_DEVICE_INFORMATION 1004
+#define SMB_QFS_ATTRIBUTE_INFORMATION 1005
+#define SMB_QFS_QUOTA_INFORMATION 1006
+#define SMB_QFS_FULL_SIZE_INFORMATION 1007
+#define SMB_QFS_OBJECTID_INFORMATION 1008
+
+
+/* trans2 qfileinfo/qpathinfo */
+/* w2k3 TRANS2ALIASES:
+Checking for QPATHINFO aliases
+setting up complex file \qpathinfo_aliases.txt
+ Found level 1 (0x001) of size 22 (0x16)
+ Found level 2 (0x002) of size 26 (0x1a)
+ Found level 4 (0x004) of size 41 (0x29)
+ Found level 6 (0x006) of size 0 (0x00)
+ Found level 257 (0x101) of size 40 (0x28)
+ Found level 258 (0x102) of size 24 (0x18)
+ Found level 259 (0x103) of size 4 (0x04)
+ Found level 260 (0x104) of size 48 (0x30)
+ Found level 263 (0x107) of size 126 (0x7e)
+ Found level 264 (0x108) of size 28 (0x1c)
+ Found level 265 (0x109) of size 38 (0x26)
+ Found level 267 (0x10b) of size 16 (0x10)
+ Found level 1004 (0x3ec) of size 40 (0x28)
+ Found level 1005 (0x3ed) of size 24 (0x18)
+ Found level 1006 (0x3ee) of size 8 (0x08)
+ Found level 1007 (0x3ef) of size 4 (0x04)
+ Found level 1008 (0x3f0) of size 4 (0x04)
+ Found level 1009 (0x3f1) of size 48 (0x30)
+ Found level 1014 (0x3f6) of size 8 (0x08)
+ Found level 1016 (0x3f8) of size 4 (0x04)
+ Found level 1017 (0x3f9) of size 4 (0x04)
+ Found level 1018 (0x3fa) of size 126 (0x7e)
+ Found level 1021 (0x3fd) of size 28 (0x1c)
+ Found level 1022 (0x3fe) of size 38 (0x26)
+ Found level 1028 (0x404) of size 16 (0x10)
+ Found level 1034 (0x40a) of size 56 (0x38)
+ Found level 1035 (0x40b) of size 8 (0x08)
+Found 27 levels with success status
+ Level 267 (0x10b) and level 1028 (0x404) are possible aliases
+ Level 265 (0x109) and level 1022 (0x3fe) are possible aliases
+ Level 264 (0x108) and level 1021 (0x3fd) are possible aliases
+ Level 263 (0x107) and level 1018 (0x3fa) are possible aliases
+ Level 260 (0x104) and level 1009 (0x3f1) are possible aliases
+ Level 259 (0x103) and level 1007 (0x3ef) are possible aliases
+ Level 258 (0x102) and level 1005 (0x3ed) are possible aliases
+ Level 257 (0x101) and level 1004 (0x3ec) are possible aliases
+Found 8 aliased levels
+*/
+#define SMB_QFILEINFO_STANDARD 1
+#define SMB_QFILEINFO_EA_SIZE 2
+#define SMB_QFILEINFO_EA_LIST 3
+#define SMB_QFILEINFO_ALL_EAS 4
+#define SMB_QFILEINFO_IS_NAME_VALID 6 /* only for QPATHINFO */
+#define SMB_QFILEINFO_BASIC_INFO 0x101
+#define SMB_QFILEINFO_STANDARD_INFO 0x102
+#define SMB_QFILEINFO_EA_INFO 0x103
+#define SMB_QFILEINFO_NAME_INFO 0x104
+#define SMB_QFILEINFO_ALL_INFO 0x107
+#define SMB_QFILEINFO_ALT_NAME_INFO 0x108
+#define SMB_QFILEINFO_STREAM_INFO 0x109
+#define SMB_QFILEINFO_COMPRESSION_INFO 0x10b
+#define SMB_QFILEINFO_UNIX_BASIC 0x200
+#define SMB_QFILEINFO_UNIX_LINK 0x201
+#define SMB_QFILEINFO_UNIX_INFO2 0x20b
+#define SMB_QFILEINFO_BASIC_INFORMATION 1004
+#define SMB_QFILEINFO_STANDARD_INFORMATION 1005
+#define SMB_QFILEINFO_INTERNAL_INFORMATION 1006
+#define SMB_QFILEINFO_EA_INFORMATION 1007
+#define SMB_QFILEINFO_ACCESS_INFORMATION 1008
+#define SMB_QFILEINFO_NAME_INFORMATION 1009
+#define SMB_QFILEINFO_POSITION_INFORMATION 1014
+#define SMB_QFILEINFO_MODE_INFORMATION 1016
+#define SMB_QFILEINFO_ALIGNMENT_INFORMATION 1017
+#define SMB_QFILEINFO_ALL_INFORMATION 1018
+#define SMB_QFILEINFO_ALT_NAME_INFORMATION 1021
+#define SMB_QFILEINFO_STREAM_INFORMATION 1022
+#define SMB_QFILEINFO_COMPRESSION_INFORMATION 1028
+#define SMB_QFILEINFO_NETWORK_OPEN_INFORMATION 1034
+#define SMB_QFILEINFO_ATTRIBUTE_TAG_INFORMATION 1035
+
+
+
+/* trans2 setfileinfo/setpathinfo levels */
+/*
+w2k3 TRANS2ALIASES
+Checking for SETFILEINFO aliases
+setting up complex file \setfileinfo_aliases.txt
+ Found level 1 (0x001) of size 2 (0x02)
+ Found level 2 (0x002) of size 2 (0x02)
+ Found level 257 (0x101) of size 40 (0x28)
+ Found level 258 (0x102) of size 2 (0x02)
+ Found level 259 (0x103) of size 8 (0x08)
+ Found level 260 (0x104) of size 8 (0x08)
+ Found level 1004 (0x3ec) of size 40 (0x28)
+ Found level 1010 (0x3f2) of size 2 (0x02)
+ Found level 1013 (0x3f5) of size 2 (0x02)
+ Found level 1014 (0x3f6) of size 8 (0x08)
+ Found level 1016 (0x3f8) of size 4 (0x04)
+ Found level 1019 (0x3fb) of size 8 (0x08)
+ Found level 1020 (0x3fc) of size 8 (0x08)
+ Found level 1023 (0x3ff) of size 8 (0x08)
+ Found level 1025 (0x401) of size 16 (0x10)
+ Found level 1029 (0x405) of size 72 (0x48)
+ Found level 1032 (0x408) of size 56 (0x38)
+ Found level 1039 (0x40f) of size 8 (0x08)
+ Found level 1040 (0x410) of size 8 (0x08)
+Found 19 valid levels
+
+Checking for SETPATHINFO aliases
+ Found level 1004 (0x3ec) of size 40 (0x28)
+ Found level 1010 (0x3f2) of size 2 (0x02)
+ Found level 1013 (0x3f5) of size 2 (0x02)
+ Found level 1014 (0x3f6) of size 8 (0x08)
+ Found level 1016 (0x3f8) of size 4 (0x04)
+ Found level 1019 (0x3fb) of size 8 (0x08)
+ Found level 1020 (0x3fc) of size 8 (0x08)
+ Found level 1023 (0x3ff) of size 8 (0x08)
+ Found level 1025 (0x401) of size 16 (0x10)
+ Found level 1029 (0x405) of size 72 (0x48)
+ Found level 1032 (0x408) of size 56 (0x38)
+ Found level 1039 (0x40f) of size 8 (0x08)
+ Found level 1040 (0x410) of size 8 (0x08)
+Found 13 valid levels
+*/
+#define SMB_SFILEINFO_STANDARD 1
+#define SMB_SFILEINFO_EA_SET 2
+#define SMB_SFILEINFO_BASIC_INFO 0x101
+#define SMB_SFILEINFO_DISPOSITION_INFO 0x102
+#define SMB_SFILEINFO_ALLOCATION_INFO 0x103
+#define SMB_SFILEINFO_END_OF_FILE_INFO 0x104
+#define SMB_SFILEINFO_UNIX_BASIC 0x200
+#define SMB_SFILEINFO_UNIX_LINK 0x201
+#define SMB_SPATHINFO_UNIX_HLINK 0x203
+#define SMB_SPATHINFO_POSIX_ACL 0x204
+#define SMB_SPATHINFO_XATTR 0x205
+#define SMB_SFILEINFO_ATTR_FLAGS 0x206
+#define SMB_SFILEINFO_UNIX_INFO2 0x20b
+#define SMB_SFILEINFO_BASIC_INFORMATION 1004
+#define SMB_SFILEINFO_RENAME_INFORMATION 1010
+#define SMB_SFILEINFO_LINK_INFORMATION 1011
+#define SMB_SFILEINFO_DISPOSITION_INFORMATION 1013
+#define SMB_SFILEINFO_POSITION_INFORMATION 1014
+#define SMB_SFILEINFO_FULL_EA_INFORMATION 1015
+#define SMB_SFILEINFO_MODE_INFORMATION 1016
+#define SMB_SFILEINFO_ALLOCATION_INFORMATION 1019
+#define SMB_SFILEINFO_END_OF_FILE_INFORMATION 1020
+#define SMB_SFILEINFO_PIPE_INFORMATION 1023
+#define SMB_SFILEINFO_VALID_DATA_INFORMATION 1039
+#define SMB_SFILEINFO_SHORT_NAME_INFORMATION 1040
+
+/* filemon shows FilePipeRemoteInformation */
+#define SMB_SFILEINFO_1025 1025
+
+/* vista scan responds */
+#define SMB_SFILEINFO_1027 1027
+
+/* filemon shows CopyOnWriteInformation */
+#define SMB_SFILEINFO_1029 1029
+
+/* filemon shows OleClassIdInformation */
+#define SMB_SFILEINFO_1032 1032
+
+/* vista scan responds to these */
+#define SMB_SFILEINFO_1030 1030
+#define SMB_SFILEINFO_1031 1031
+#define SMB_SFILEINFO_1036 1036
+#define SMB_SFILEINFO_1041 1041
+#define SMB_SFILEINFO_1042 1042
+#define SMB_SFILEINFO_1043 1043
+#define SMB_SFILEINFO_1044 1044
+
+/* trans2 findfirst levels */
+/*
+w2k3 TRANS2ALIASES:
+Checking for FINDFIRST aliases
+ Found level 1 (0x001) of size 68 (0x44)
+ Found level 2 (0x002) of size 70 (0x46)
+ Found level 257 (0x101) of size 108 (0x6c)
+ Found level 258 (0x102) of size 116 (0x74)
+ Found level 259 (0x103) of size 60 (0x3c)
+ Found level 260 (0x104) of size 140 (0x8c)
+ Found level 261 (0x105) of size 124 (0x7c)
+ Found level 262 (0x106) of size 148 (0x94)
+Found 8 levels with success status
+Found 0 aliased levels
+*/
+#define SMB_FIND_STANDARD 1
+#define SMB_FIND_EA_SIZE 2
+#define SMB_FIND_EA_LIST 3
+#define SMB_FIND_DIRECTORY_INFO 0x101
+#define SMB_FIND_FULL_DIRECTORY_INFO 0x102
+#define SMB_FIND_NAME_INFO 0x103
+#define SMB_FIND_BOTH_DIRECTORY_INFO 0x104
+#define SMB_FIND_ID_FULL_DIRECTORY_INFO 0x105
+#define SMB_FIND_ID_BOTH_DIRECTORY_INFO 0x106
+#define SMB_FIND_UNIX_INFO 0x202
+#define SMB_FIND_UNIX_INFO2 0x20b
+
+/* flags on trans2 findfirst/findnext that control search */
+#define FLAG_TRANS2_FIND_CLOSE 0x1
+#define FLAG_TRANS2_FIND_CLOSE_IF_END 0x2
+#define FLAG_TRANS2_FIND_REQUIRE_RESUME 0x4
+#define FLAG_TRANS2_FIND_CONTINUE 0x8
+#define FLAG_TRANS2_FIND_BACKUP_INTENT 0x10
+
+/*
+ * DeviceType and Characteristics returned in a
+ * SMB_QFS_DEVICE_INFO call.
+ */
+#define QFS_DEVICETYPE_CD_ROM 0x2
+#define QFS_DEVICETYPE_CD_ROM_FILE_SYSTEM 0x3
+#define QFS_DEVICETYPE_DISK 0x7
+#define QFS_DEVICETYPE_DISK_FILE_SYSTEM 0x8
+#define QFS_DEVICETYPE_FILE_SYSTEM 0x9
+
+/* Characteristics. */
+#define QFS_TYPE_REMOVABLE_MEDIA 0x1
+#define QFS_TYPE_READ_ONLY_DEVICE 0x2
+#define QFS_TYPE_FLOPPY 0x4
+#define QFS_TYPE_WORM 0x8
+#define QFS_TYPE_REMOTE 0x10
+#define QFS_TYPE_MOUNTED 0x20
+#define QFS_TYPE_VIRTUAL 0x40
+
+
+/*
+ * Thursby MAC extensions....
+ */
+
+/*
+ * MAC CIFS Extensions have the range 0x300 - 0x2FF reserved.
+ * Supposedly Microsoft have agreed to this.
+ */
+
+#define MIN_MAC_INFO_LEVEL 0x300
+#define MAX_MAC_INFO_LEVEL 0x3FF
+#define SMB_QFS_MAC_FS_INFO 0x301
+
+
+
+/* UNIX CIFS Extensions - created by HP */
+/*
+ * UNIX CIFS Extensions have the range 0x200 - 0x2FF reserved.
+ * Supposedly Microsoft have agreed to this.
+ */
+
+#define MIN_UNIX_INFO_LEVEL 0x200
+#define MAX_UNIX_INFO_LEVEL 0x2FF
+
+#define INFO_LEVEL_IS_UNIX(level) (((level) >= MIN_UNIX_INFO_LEVEL) && ((level) <= MAX_UNIX_INFO_LEVEL))
+
+#define SMB_MODE_NO_CHANGE 0xFFFFFFFF /* file mode value which */
+ /* means "don't change it" */
+#define SMB_UID_NO_CHANGE 0xFFFFFFFF
+#define SMB_GID_NO_CHANGE 0xFFFFFFFF
+
+#define SMB_SIZE_NO_CHANGE_LO 0xFFFFFFFF
+#define SMB_SIZE_NO_CHANGE_HI 0xFFFFFFFF
+
+#define SMB_TIME_NO_CHANGE_LO 0xFFFFFFFF
+#define SMB_TIME_NO_CHANGE_HI 0xFFFFFFFF
+
+/*
+UNIX_BASIC info level:
+
+Offset Size Name
+0 LARGE_INTEGER EndOfFile File size
+8 LARGE_INTEGER Blocks Number of bytes used on disk (st_blocks).
+16 LARGE_INTEGER CreationTime Creation time
+24 LARGE_INTEGER LastAccessTime Last access time
+32 LARGE_INTEGER LastModificationTime Last modification time
+40 LARGE_INTEGER Uid Numeric user id for the owner
+48 LARGE_INTEGER Gid Numeric group id of owner
+56 ULONG Type Enumeration specifying the pathname type:
+ 0 -- File
+ 1 -- Directory
+ 2 -- Symbolic link
+ 3 -- Character device
+ 4 -- Block device
+ 5 -- FIFO (named pipe)
+ 6 -- Unix domain socket
+
+60 LARGE_INTEGER devmajor Major device number if type is device
+68 LARGE_INTEGER devminor Minor device number if type is device
+76 LARGE_INTEGER uniqueid This is a server-assigned unique id for the file. The client
+ will typically map this onto an inode number. The scope of
+ uniqueness is the share.
+84 LARGE_INTEGER permissions Standard UNIX file permissions - see below.
+92 LARGE_INTEGER nlinks The number of directory entries that map to this entry
+ (number of hard links)
+
+100 - end.
+*/
+
+/*
+SMB_QUERY_FILE_UNIX_INFO2 is SMB_QUERY_FILE_UNIX_BASIC with create
+time and file flags appended. The corresponding info level for
+findfirst/findnext is SMB_FIND_FILE_UNIX_UNIX2.
+
+Size Offset Value
+---------------------
+0 LARGE_INTEGER EndOfFile File size
+8 LARGE_INTEGER Blocks Number of blocks used on disk
+16 LARGE_INTEGER ChangeTime Attribute change time
+24 LARGE_INTEGER LastAccessTime Last access time
+32 LARGE_INTEGER LastModificationTime Last modification time
+40 LARGE_INTEGER Uid Numeric user id for the owner
+48 LARGE_INTEGER Gid Numeric group id of owner
+56 ULONG Type Enumeration specifying the file type
+60 LARGE_INTEGER devmajor Major device number if type is device
+68 LARGE_INTEGER devminor Minor device number if type is device
+76 LARGE_INTEGER uniqueid This is a server-assigned unique id
+84 LARGE_INTEGER permissions Standard UNIX permissions
+92 LARGE_INTEGER nlinks Number of hard link)
+100 LARGE_INTEGER CreationTime Create/birth time
+108 ULONG FileFlags File flags enumeration
+112 ULONG FileFlagsMask Mask of valid flags
+*/
+
+/* UNIX filetype mappings. */
+
+#define UNIX_TYPE_FILE 0
+#define UNIX_TYPE_DIR 1
+#define UNIX_TYPE_SYMLINK 2
+#define UNIX_TYPE_CHARDEV 3
+#define UNIX_TYPE_BLKDEV 4
+#define UNIX_TYPE_FIFO 5
+#define UNIX_TYPE_SOCKET 6
+#define UNIX_TYPE_UNKNOWN 0xFFFFFFFF
+
+/*
+ * Oh this is fun. "Standard UNIX permissions" has no
+ * meaning in POSIX. We need to define the mapping onto
+ * and off the wire as this was not done in the original HP
+ * spec. JRA.
+ */
+
+#define UNIX_X_OTH 0000001
+#define UNIX_W_OTH 0000002
+#define UNIX_R_OTH 0000004
+#define UNIX_X_GRP 0000010
+#define UNIX_W_GRP 0000020
+#define UNIX_R_GRP 0000040
+#define UNIX_X_USR 0000100
+#define UNIX_W_USR 0000200
+#define UNIX_R_USR 0000400
+#define UNIX_STICKY 0001000
+#define UNIX_SET_GID 0002000
+#define UNIX_SET_UID 0004000
+
+/* Masks for the above */
+#define UNIX_OTH_MASK 0000007
+#define UNIX_GRP_MASK 0000070
+#define UNIX_USR_MASK 0000700
+#define UNIX_PERM_MASK 0000777
+#define UNIX_EXTRA_MASK 0007000
+#define UNIX_ALL_MASK 0007777
+
+/* Flags for the file_flags field in UNIX_INFO2: */
+#define EXT_SECURE_DELETE 0x00000001
+#define EXT_ENABLE_UNDELETE 0x00000002
+#define EXT_SYNCHRONOUS 0x00000004
+#define EXT_IMMUTABLE 0x00000008
+#define EXT_OPEN_APPEND_ONLY 0x00000010
+#define EXT_DO_NOT_BACKUP 0x00000020
+#define EXT_NO_UPDATE_ATIME 0x00000040
+#define EXT_HIDDEN 0x00000080
+
+#define SMB_QFILEINFO_UNIX_LINK 0x201
+#define SMB_SFILEINFO_UNIX_LINK 0x201
+#define SMB_SFILEINFO_UNIX_HLINK 0x203
+
+/*
+ Info level for QVOLINFO - returns version of CIFS UNIX extensions, plus
+ 64-bits worth of capability fun :-).
+*/
+
+#define SMB_QUERY_CIFS_UNIX_INFO 0x200
+
+/* Returns the following.
+
+ UINT16 major version number
+ UINT16 minor version number
+ LARGE_INTEGER capability bitfield
+
+*/
+
+#define CIFS_UNIX_MAJOR_VERSION 1
+#define CIFS_UNIX_MINOR_VERSION 0
+
+#define CIFS_UNIX_FCNTL_LOCKS_CAP 0x1
+#define CIFS_UNIX_POSIX_ACLS_CAP 0x2
+
+/* ... more as we think of them :-). */
+
+#endif
diff --git a/source4/libcli/resolve/bcast.c b/source4/libcli/resolve/bcast.c
new file mode 100644
index 0000000000..da58eb8665
--- /dev/null
+++ b/source4/libcli/resolve/bcast.c
@@ -0,0 +1,106 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ broadcast name resolution module
+
+ Copyright (C) Andrew Tridgell 2005
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/resolve/resolve.h"
+#include "system/network.h"
+#include "lib/socket/netif.h"
+#include "param/param.h"
+
+struct resolve_bcast_data {
+ struct interface *ifaces;
+ uint16_t nbt_port;
+ int nbt_timeout;
+};
+
+/**
+ broadcast name resolution method - async send
+ */
+struct composite_context *resolve_name_bcast_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *event_ctx,
+ void *userdata, uint32_t flags,
+ uint16_t port,
+ struct nbt_name *name)
+{
+ int num_interfaces;
+ const char **address_list;
+ struct composite_context *c;
+ int i, count=0;
+ struct resolve_bcast_data *data = talloc_get_type(userdata, struct resolve_bcast_data);
+
+ num_interfaces = iface_count(data->ifaces);
+
+ address_list = talloc_array(mem_ctx, const char *, num_interfaces+1);
+ if (address_list == NULL) return NULL;
+
+ for (i=0;i<num_interfaces;i++) {
+ const char *bcast = iface_n_bcast(data->ifaces, i);
+ if (bcast == NULL) continue;
+ address_list[count] = talloc_strdup(address_list, bcast);
+ if (address_list[count] == NULL) {
+ talloc_free(address_list);
+ return NULL;
+ }
+ count++;
+ }
+ address_list[count] = NULL;
+
+ c = resolve_name_nbtlist_send(mem_ctx, event_ctx, flags, port, name,
+ address_list, data->ifaces, data->nbt_port,
+ data->nbt_timeout, true, false);
+ talloc_free(address_list);
+
+ return c;
+}
+
+/*
+ broadcast name resolution method - recv side
+ */
+NTSTATUS resolve_name_bcast_recv(struct composite_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct socket_address ***addrs,
+ char ***names)
+{
+ NTSTATUS status = resolve_name_nbtlist_recv(c, mem_ctx, addrs, names);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
+ /* this makes much more sense for a bcast name resolution
+ timeout */
+ status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+ return status;
+}
+
+bool resolve_context_add_bcast_method(struct resolve_context *ctx, struct interface *ifaces, uint16_t nbt_port, int nbt_timeout)
+{
+ struct resolve_bcast_data *data = talloc(ctx, struct resolve_bcast_data);
+ data->ifaces = ifaces;
+ data->nbt_port = nbt_port;
+ data->nbt_timeout = nbt_timeout;
+ return resolve_context_add_method(ctx, resolve_name_bcast_send, resolve_name_bcast_recv, data);
+}
+
+bool resolve_context_add_bcast_method_lp(struct resolve_context *ctx, struct loadparm_context *lp_ctx)
+{
+ struct interface *ifaces;
+ load_interfaces(ctx, lp_interfaces(lp_ctx), &ifaces);
+ return resolve_context_add_bcast_method(ctx, ifaces, lp_nbt_port(lp_ctx), lp_parm_int(lp_ctx, NULL, "nbt", "timeout", 1));
+}
diff --git a/source4/libcli/resolve/dns_ex.c b/source4/libcli/resolve/dns_ex.c
new file mode 100644
index 0000000000..ce41d0cea8
--- /dev/null
+++ b/source4/libcli/resolve/dns_ex.c
@@ -0,0 +1,541 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ async getaddrinfo()/dns_lookup() name resolution module
+
+ Copyright (C) Andrew Tridgell 2005
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ this module uses a fork() per getaddrinfo() or dns_looup() call.
+ At first that might seem crazy, but it is actually very fast,
+ and solves many of the tricky problems of keeping a child
+ hanging around in a librar (like what happens when the parent forks).
+ We use a talloc destructor to ensure that the child is cleaned up
+ when we have finished with this name resolution.
+*/
+
+#include "includes.h"
+#include "lib/events/events.h"
+#include "system/network.h"
+#include "system/filesys.h"
+#include "lib/socket/socket.h"
+#include "libcli/composite/composite.h"
+#include "librpc/gen_ndr/ndr_nbt.h"
+#include "libcli/resolve/resolve.h"
+
+#ifdef class
+#undef class
+#endif
+
+#include "heimdal/lib/roken/resolve.h"
+
+struct dns_ex_state {
+ bool do_fallback;
+ uint32_t flags;
+ uint16_t port;
+ struct nbt_name name;
+ struct socket_address **addrs;
+ char **names;
+ pid_t child;
+ int child_fd;
+ struct tevent_fd *fde;
+ struct tevent_context *event_ctx;
+};
+
+/*
+ kill off a wayward child if needed. This allows us to stop an async
+ name resolution without leaving a potentially blocking call running
+ in a child
+*/
+static int dns_ex_destructor(struct dns_ex_state *state)
+{
+ int status;
+
+ kill(state->child, SIGTERM);
+ close(state->child_fd);
+ if (waitpid(state->child, &status, WNOHANG) == 0) {
+ kill(state->child, SIGKILL);
+ waitpid(state->child, &status, 0);
+ }
+
+ return 0;
+}
+
+/*
+ the blocking child
+*/
+static void run_child_dns_lookup(struct dns_ex_state *state, int fd)
+{
+ struct dns_reply *reply;
+ struct resource_record *rr;
+ uint32_t count = 0;
+ uint32_t srv_valid = 0;
+ struct resource_record **srv_rr;
+ uint32_t addrs_valid = 0;
+ struct resource_record **addrs_rr;
+ char *addrs;
+ bool first;
+ uint32_t i;
+ bool do_srv = (state->flags & RESOLVE_NAME_FLAG_DNS_SRV);
+
+ /* this is the blocking call we are going to lots of trouble
+ to avoid in the parent */
+ reply = dns_lookup(state->name.name, do_srv?"SRV":"A");
+ if (!reply) {
+ goto done;
+ }
+
+ if (do_srv) {
+ dns_srv_order(reply);
+ }
+
+ /* Loop over all returned records and pick the "srv" records */
+ for (rr=reply->head; rr; rr=rr->next) {
+ /* we are only interested in the IN class */
+ if (rr->class != C_IN) {
+ continue;
+ }
+
+ if (do_srv) {
+ /* we are only interested in SRV records */
+ if (rr->type != T_SRV) {
+ continue;
+ }
+
+ /* verify we actually have a SRV record here */
+ if (!rr->u.srv) {
+ continue;
+ }
+
+ /* Verify we got a port */
+ if (rr->u.srv->port == 0) {
+ continue;
+ }
+ } else {
+ /* we are only interested in A records */
+ /* TODO: add AAAA support */
+ if (rr->type != T_A) {
+ continue;
+ }
+
+ /* verify we actually have a A record here */
+ if (!rr->u.a) {
+ continue;
+ }
+ }
+ count++;
+ }
+
+ if (count == 0) {
+ goto done;
+ }
+
+ srv_rr = talloc_zero_array(state,
+ struct resource_record *,
+ count);
+ if (!srv_rr) {
+ goto done;
+ }
+
+ addrs_rr = talloc_zero_array(state,
+ struct resource_record *,
+ count);
+ if (!addrs_rr) {
+ goto done;
+ }
+
+ /* Loop over all returned records and pick the records */
+ for (rr=reply->head;rr;rr=rr->next) {
+ /* we are only interested in the IN class */
+ if (rr->class != C_IN) {
+ continue;
+ }
+
+ if (do_srv) {
+ /* we are only interested in SRV records */
+ if (rr->type != T_SRV) {
+ continue;
+ }
+
+ /* verify we actually have a srv record here */
+ if (!rr->u.srv) {
+ continue;
+ }
+
+ /* Verify we got a port */
+ if (rr->u.srv->port == 0) {
+ continue;
+ }
+
+ srv_rr[srv_valid] = rr;
+ srv_valid++;
+ } else {
+ /* we are only interested in A records */
+ /* TODO: add AAAA support */
+ if (rr->type != T_A) {
+ continue;
+ }
+
+ /* verify we actually have a A record here */
+ if (!rr->u.a) {
+ continue;
+ }
+
+ addrs_rr[addrs_valid] = rr;
+ addrs_valid++;
+ }
+ }
+
+ for (i=0; i < srv_valid; i++) {
+ for (rr=reply->head;rr;rr=rr->next) {
+
+ if (rr->class != C_IN) {
+ continue;
+ }
+
+ /* we are only interested in SRV records */
+ if (rr->type != T_A) {
+ continue;
+ }
+
+ /* verify we actually have a srv record here */
+ if (strcmp(&srv_rr[i]->u.srv->target[0], rr->domain) != 0) {
+ continue;
+ }
+
+ addrs_rr[i] = rr;
+ addrs_valid++;
+ break;
+ }
+ }
+
+ if (addrs_valid == 0) {
+ goto done;
+ }
+
+ addrs = talloc_strdup(state, "");
+ if (!addrs) {
+ goto done;
+ }
+ first = true;
+ for (i=0; i < count; i++) {
+ uint16_t port;
+ if (!addrs_rr[i]) {
+ continue;
+ }
+
+ if (srv_rr[i] &&
+ (state->flags & RESOLVE_NAME_FLAG_OVERWRITE_PORT)) {
+ port = srv_rr[i]->u.srv->port;
+ } else {
+ port = state->port;
+ }
+
+ addrs = talloc_asprintf_append_buffer(addrs, "%s%s:%u/%s",
+ first?"":",",
+ inet_ntoa(*addrs_rr[i]->u.a),
+ port,
+ addrs_rr[i]->domain);
+ if (!addrs) {
+ goto done;
+ }
+ first = false;
+ }
+
+ if (addrs) {
+ write(fd, addrs, talloc_get_size(addrs));
+ }
+
+done:
+ close(fd);
+}
+
+/*
+ the blocking child
+*/
+static void run_child_getaddrinfo(struct dns_ex_state *state, int fd)
+{
+ int ret;
+ struct addrinfo hints;
+ struct addrinfo *res;
+ struct addrinfo *res_list = NULL;
+ char *addrs;
+ bool first;
+
+ ZERO_STRUCT(hints);
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_family = AF_INET;/* TODO: add AF_INET6 support */
+ hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV;
+
+ ret = getaddrinfo(state->name.name, "0", &hints, &res_list);
+#ifdef EAI_NODATA
+ if (ret == EAI_NODATA && state->do_fallback) {
+#else
+ if (ret == EAI_NONAME && state->do_fallback) {
+#endif
+ /* getaddrinfo() doesn't handle CNAME records */
+ run_child_dns_lookup(state, fd);
+ return;
+ }
+ if (ret != 0) {
+ goto done;
+ }
+
+ addrs = talloc_strdup(state, "");
+ if (!addrs) {
+ goto done;
+ }
+ first = true;
+ for (res = res_list; res; res = res->ai_next) {
+ struct sockaddr_in *in;
+
+ if (res->ai_family != AF_INET) {
+ continue;
+ }
+ in = (struct sockaddr_in *)res->ai_addr;
+
+ addrs = talloc_asprintf_append_buffer(addrs, "%s%s:%u/%s",
+ first?"":",",
+ inet_ntoa(in->sin_addr),
+ state->port,
+ state->name.name);
+ if (!addrs) {
+ goto done;
+ }
+ first = false;
+ }
+
+ if (addrs) {
+ write(fd, addrs, talloc_get_size(addrs));
+ }
+done:
+ if (res_list) {
+ freeaddrinfo(res_list);
+ }
+ close(fd);
+}
+
+/*
+ handle a read event on the pipe
+*/
+static void pipe_handler(struct tevent_context *ev, struct tevent_fd *fde,
+ uint16_t flags, void *private_data)
+{
+ struct composite_context *c = talloc_get_type(private_data, struct composite_context);
+ struct dns_ex_state *state = talloc_get_type(c->private_data,
+ struct dns_ex_state);
+ char *address;
+ uint32_t num_addrs, i;
+ char **addrs;
+ int ret;
+ int status;
+ int value = 0;
+
+ /* if we get any event from the child then we know that we
+ won't need to kill it off */
+ talloc_set_destructor(state, NULL);
+
+ if (ioctl(state->child_fd, FIONREAD, &value) != 0) {
+ value = 8192;
+ }
+
+ address = talloc_array(state, char, value+1);
+ if (address) {
+ /* yes, we don't care about EAGAIN or other niceities
+ here. They just can't happen with this parent/child
+ relationship, and even if they did then giving an error is
+ the right thing to do */
+ ret = read(state->child_fd, address, value);
+ } else {
+ ret = -1;
+ }
+ close(state->child_fd);
+ if (waitpid(state->child, &status, WNOHANG) == 0) {
+ kill(state->child, SIGKILL);
+ waitpid(state->child, &status, 0);
+ }
+
+ if (ret <= 0) {
+ composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ return;
+ }
+
+ /* enusre the address looks good */
+ address[ret] = 0;
+
+ addrs = str_list_make(state, address, ",");
+ if (composite_nomem(addrs, c)) return;
+
+ num_addrs = str_list_length((const char * const *)addrs);
+
+ state->addrs = talloc_array(state, struct socket_address *,
+ num_addrs+1);
+ if (composite_nomem(state->addrs, c)) return;
+
+ state->names = talloc_array(state, char *, num_addrs+1);
+ if (composite_nomem(state->names, c)) return;
+
+ for (i=0; i < num_addrs; i++) {
+ uint32_t port = 0;
+ char *p = strrchr(addrs[i], ':');
+ char *n;
+
+ if (!p) {
+ composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ return;
+ }
+
+ *p = '\0';
+ p++;
+
+ n = strrchr(p, '/');
+ if (!n) {
+ composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ return;
+ }
+
+ *n = '\0';
+ n++;
+
+ if (strcmp(addrs[i], "0.0.0.0") == 0 ||
+ inet_addr(addrs[i]) == INADDR_NONE) {
+ composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ return;
+ }
+ port = strtoul(p, NULL, 10);
+ if (port > UINT16_MAX) {
+ composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ return;
+ }
+ state->addrs[i] = socket_address_from_strings(state->addrs,
+ "ipv4",
+ addrs[i],
+ port);
+ if (composite_nomem(state->addrs[i], c)) return;
+
+ state->names[i] = talloc_strdup(state->names, n);
+ if (composite_nomem(state->names[i], c)) return;
+ }
+ state->addrs[i] = NULL;
+ state->names[i] = NULL;
+
+ composite_done(c);
+}
+
+/*
+ getaddrinfo() or dns_lookup() name resolution method - async send
+ */
+struct composite_context *resolve_name_dns_ex_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *event_ctx,
+ void *privdata,
+ uint32_t flags,
+ uint16_t port,
+ struct nbt_name *name,
+ bool do_fallback)
+{
+ struct composite_context *c;
+ struct dns_ex_state *state;
+ int fd[2] = { -1, -1 };
+ int ret;
+
+ c = composite_create(mem_ctx, event_ctx);
+ if (c == NULL) return NULL;
+
+ if (flags & RESOLVE_NAME_FLAG_FORCE_NBT) {
+ composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ return c;
+ }
+
+ state = talloc_zero(c, struct dns_ex_state);
+ if (composite_nomem(state, c)) return c;
+ c->private_data = state;
+
+ c->status = nbt_name_dup(state, name, &state->name);
+ if (!composite_is_ok(c)) return c;
+
+ /* setup a pipe to chat to our child */
+ ret = pipe(fd);
+ if (ret == -1) {
+ composite_error(c, map_nt_error_from_unix(errno));
+ return c;
+ }
+
+ state->do_fallback = do_fallback;
+ state->flags = flags;
+ state->port = port;
+
+ state->child_fd = fd[0];
+ state->event_ctx = c->event_ctx;
+
+ /* we need to put the child in our event context so
+ we know when the dns_lookup() has finished */
+ state->fde = event_add_fd(c->event_ctx, c, state->child_fd, EVENT_FD_READ,
+ pipe_handler, c);
+ if (composite_nomem(state->fde, c)) {
+ close(fd[0]);
+ close(fd[1]);
+ return c;
+ }
+
+ state->child = fork();
+ if (state->child == (pid_t)-1) {
+ composite_error(c, map_nt_error_from_unix(errno));
+ return c;
+ }
+
+ if (state->child == 0) {
+ close(fd[0]);
+ if (state->flags & RESOLVE_NAME_FLAG_FORCE_DNS) {
+ run_child_dns_lookup(state, fd[1]);
+ } else {
+ run_child_getaddrinfo(state, fd[1]);
+ }
+ _exit(0);
+ }
+ close(fd[1]);
+
+ /* cleanup wayward children */
+ talloc_set_destructor(state, dns_ex_destructor);
+
+ return c;
+}
+
+/*
+ getaddrinfo() or dns_lookup() name resolution method - recv side
+*/
+NTSTATUS resolve_name_dns_ex_recv(struct composite_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct socket_address ***addrs,
+ char ***names)
+{
+ NTSTATUS status;
+
+ status = composite_wait(c);
+
+ if (NT_STATUS_IS_OK(status)) {
+ struct dns_ex_state *state = talloc_get_type(c->private_data,
+ struct dns_ex_state);
+ *addrs = talloc_steal(mem_ctx, state->addrs);
+ if (names) {
+ *names = talloc_steal(mem_ctx, state->names);
+ }
+ }
+
+ talloc_free(c);
+ return status;
+}
diff --git a/source4/libcli/resolve/host.c b/source4/libcli/resolve/host.c
new file mode 100644
index 0000000000..755a4e8304
--- /dev/null
+++ b/source4/libcli/resolve/host.c
@@ -0,0 +1,60 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ async "host" name resolution module
+
+ Copyright (C) Andrew Tridgell 2005
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/events/events.h"
+#include "system/network.h"
+#include "system/filesys.h"
+#include "lib/socket/socket.h"
+#include "libcli/composite/composite.h"
+#include "librpc/gen_ndr/ndr_nbt.h"
+#include "libcli/resolve/resolve.h"
+
+/*
+ getaddrinfo() (with fallback to dns_lookup()) name resolution method - async send
+ */
+struct composite_context *resolve_name_host_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *event_ctx,
+ void *privdata, uint32_t flags,
+ uint16_t port,
+ struct nbt_name *name)
+{
+ return resolve_name_dns_ex_send(mem_ctx, event_ctx, NULL, flags,
+ port, name, true);
+}
+
+/*
+ getaddrinfo() (with fallback to dns_lookup()) name resolution method - recv side
+*/
+NTSTATUS resolve_name_host_recv(struct composite_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct socket_address ***addrs,
+ char ***names)
+{
+ return resolve_name_dns_ex_recv(c, mem_ctx, addrs, names);
+}
+
+bool resolve_context_add_host_method(struct resolve_context *ctx)
+{
+ return resolve_context_add_method(ctx, resolve_name_host_send, resolve_name_host_recv,
+ NULL);
+}
diff --git a/source4/libcli/resolve/nbtlist.c b/source4/libcli/resolve/nbtlist.c
new file mode 100644
index 0000000000..73085b87cb
--- /dev/null
+++ b/source4/libcli/resolve/nbtlist.c
@@ -0,0 +1,224 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ nbt list of addresses name resolution module
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ TODO: we should lower the timeout, and add retries for each name
+*/
+
+#include "includes.h"
+#include "libcli/composite/composite.h"
+#include "system/network.h"
+#include "lib/socket/socket.h"
+#include "lib/socket/netif.h"
+#include "librpc/gen_ndr/ndr_nbt.h"
+#include "../libcli/nbt/libnbt.h"
+#include "param/param.h"
+#include "libcli/resolve/resolve.h"
+
+struct nbtlist_state {
+ uint16_t flags;
+ uint16_t port;
+ struct nbt_name name;
+ struct nbt_name_socket *nbtsock;
+ int num_queries;
+ struct nbt_name_request **queries;
+ struct nbt_name_query *io_queries;
+ struct socket_address **addrs;
+ char **names;
+ struct interface *ifaces;
+};
+
+/*
+ handle events during nbtlist name resolution
+*/
+static void nbtlist_handler(struct nbt_name_request *req)
+{
+ struct composite_context *c = talloc_get_type(req->async.private_data,
+ struct composite_context);
+ struct nbtlist_state *state = talloc_get_type(c->private_data, struct nbtlist_state);
+ struct nbt_name_query *q;
+ int i;
+
+ for (i=0;i<state->num_queries;i++) {
+ if (req == state->queries[i]) break;
+ }
+
+ if (i == state->num_queries) {
+ /* not for us?! */
+ composite_error(c, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
+ q = &state->io_queries[i];
+
+ c->status = nbt_name_query_recv(req, state, q);
+
+ /* free the network resource directly */
+ talloc_free(state->nbtsock);
+ if (!composite_is_ok(c)) return;
+
+ if (q->out.num_addrs < 1) {
+ composite_error(c, NT_STATUS_UNEXPECTED_NETWORK_ERROR);
+ return;
+ }
+
+ state->addrs = talloc_array(state, struct socket_address *,
+ q->out.num_addrs + 1);
+ if (composite_nomem(state->addrs, c)) return;
+
+ state->names = talloc_array(state, char *, q->out.num_addrs + 1);
+ if (composite_nomem(state->names, c)) return;
+
+ for (i=0;i<q->out.num_addrs;i++) {
+ state->addrs[i] = socket_address_from_strings(state->addrs,
+ "ipv4",
+ q->out.reply_addrs[i],
+ state->port);
+ if (composite_nomem(state->addrs[i], c)) return;
+
+ state->names[i] = talloc_strdup(state->names, state->name.name);
+ if (composite_nomem(state->names[i], c)) return;
+ }
+ state->addrs[i] = NULL;
+ state->names[i] = NULL;
+
+ composite_done(c);
+}
+
+/*
+ nbtlist name resolution method - async send
+ */
+struct composite_context *resolve_name_nbtlist_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *event_ctx,
+ uint32_t flags,
+ uint16_t port,
+ struct nbt_name *name,
+ const char **address_list,
+ struct interface *ifaces,
+ uint16_t nbt_port,
+ int nbt_timeout,
+ bool broadcast,
+ bool wins_lookup)
+{
+ struct composite_context *c;
+ struct nbtlist_state *state;
+ int i;
+
+ c = composite_create(mem_ctx, event_ctx);
+ if (c == NULL) return NULL;
+
+ if (flags & RESOLVE_NAME_FLAG_FORCE_DNS) {
+ composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ return c;
+ }
+
+ if (strlen(name->name) > 15) {
+ composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ return c;
+ }
+
+ state = talloc(c, struct nbtlist_state);
+ if (composite_nomem(state, c)) return c;
+ c->private_data = state;
+
+ state->flags = flags;
+ state->port = port;
+
+ c->status = nbt_name_dup(state, name, &state->name);
+ if (!composite_is_ok(c)) return c;
+
+ state->name.name = strupper_talloc(state, state->name.name);
+ if (composite_nomem(state->name.name, c)) return c;
+ if (state->name.scope) {
+ state->name.scope = strupper_talloc(state, state->name.scope);
+ if (composite_nomem(state->name.scope, c)) return c;
+ }
+
+ state->ifaces = talloc_reference(state, ifaces);
+
+ /*
+ * we can't push long names on the wire,
+ * so bail out here to give a useful error message
+ */
+ if (strlen(state->name.name) > 15) {
+ composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ return c;
+ }
+
+ state->nbtsock = nbt_name_socket_init(state, event_ctx,
+ global_iconv_convenience);
+ if (composite_nomem(state->nbtsock, c)) return c;
+
+ /* count the address_list size */
+ for (i=0;address_list[i];i++) /* noop */ ;
+
+ state->num_queries = i;
+ state->io_queries = talloc_array(state, struct nbt_name_query, state->num_queries);
+ if (composite_nomem(state->io_queries, c)) return c;
+
+ state->queries = talloc_array(state, struct nbt_name_request *, state->num_queries);
+ if (composite_nomem(state->queries, c)) return c;
+
+ for (i=0;i<state->num_queries;i++) {
+ state->io_queries[i].in.name = state->name;
+ state->io_queries[i].in.dest_addr = talloc_strdup(state->io_queries, address_list[i]);
+ state->io_queries[i].in.dest_port = nbt_port;
+ if (composite_nomem(state->io_queries[i].in.dest_addr, c)) return c;
+
+ state->io_queries[i].in.broadcast = broadcast;
+ state->io_queries[i].in.wins_lookup = wins_lookup;
+ state->io_queries[i].in.timeout = nbt_timeout;
+ state->io_queries[i].in.retries = 2;
+
+ state->queries[i] = nbt_name_query_send(state->nbtsock, &state->io_queries[i]);
+ if (composite_nomem(state->queries[i], c)) return c;
+
+ state->queries[i]->async.fn = nbtlist_handler;
+ state->queries[i]->async.private_data = c;
+ }
+
+ return c;
+}
+
+/*
+ nbt list of addresses name resolution method - recv side
+ */
+NTSTATUS resolve_name_nbtlist_recv(struct composite_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct socket_address ***addrs,
+ char ***names)
+{
+ NTSTATUS status;
+
+ status = composite_wait(c);
+
+ if (NT_STATUS_IS_OK(status)) {
+ struct nbtlist_state *state = talloc_get_type(c->private_data, struct nbtlist_state);
+ *addrs = talloc_steal(mem_ctx, state->addrs);
+ if (names) {
+ *names = talloc_steal(mem_ctx, state->names);
+ }
+ }
+
+ talloc_free(c);
+ return status;
+}
+
diff --git a/source4/libcli/resolve/resolve.c b/source4/libcli/resolve/resolve.c
new file mode 100644
index 0000000000..6a3d5daecc
--- /dev/null
+++ b/source4/libcli/resolve/resolve.c
@@ -0,0 +1,283 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ general name resolution interface
+
+ Copyright (C) Andrew Tridgell 2005
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/composite/composite.h"
+#include "libcli/resolve/resolve.h"
+#include "librpc/gen_ndr/ndr_nbt.h"
+#include "system/network.h"
+#include "lib/socket/socket.h"
+#include "../lib/util/dlinklist.h"
+
+struct resolve_state {
+ struct resolve_context *ctx;
+ struct resolve_method *method;
+ uint32_t flags;
+ uint16_t port;
+ struct nbt_name name;
+ struct composite_context *creq;
+ struct socket_address **addrs;
+ char **names;
+};
+
+static struct composite_context *setup_next_method(struct composite_context *c);
+
+
+struct resolve_context {
+ struct resolve_method {
+ resolve_name_send_fn send_fn;
+ resolve_name_recv_fn recv_fn;
+ void *privdata;
+ struct resolve_method *prev, *next;
+ } *methods;
+};
+
+/**
+ * Initialize a resolve context
+ */
+struct resolve_context *resolve_context_init(TALLOC_CTX *mem_ctx)
+{
+ return talloc_zero(mem_ctx, struct resolve_context);
+}
+
+/**
+ * Add a resolve method
+ */
+bool resolve_context_add_method(struct resolve_context *ctx, resolve_name_send_fn send_fn,
+ resolve_name_recv_fn recv_fn, void *userdata)
+{
+ struct resolve_method *method = talloc_zero(ctx, struct resolve_method);
+
+ if (method == NULL)
+ return false;
+
+ method->send_fn = send_fn;
+ method->recv_fn = recv_fn;
+ method->privdata = userdata;
+ DLIST_ADD_END(ctx->methods, method, struct resolve_method *);
+ return true;
+}
+
+/**
+ handle completion of one name resolve method
+*/
+static void resolve_handler(struct composite_context *creq)
+{
+ struct composite_context *c = (struct composite_context *)creq->async.private_data;
+ struct resolve_state *state = talloc_get_type(c->private_data, struct resolve_state);
+ const struct resolve_method *method = state->method;
+
+ c->status = method->recv_fn(creq, state, &state->addrs, &state->names);
+
+ if (!NT_STATUS_IS_OK(c->status)) {
+ state->method = state->method->next;
+ state->creq = setup_next_method(c);
+ if (state->creq != NULL) {
+ return;
+ }
+ }
+
+ if (!NT_STATUS_IS_OK(c->status)) {
+ c->state = COMPOSITE_STATE_ERROR;
+ } else {
+ c->state = COMPOSITE_STATE_DONE;
+ }
+ if (c->async.fn) {
+ c->async.fn(c);
+ }
+}
+
+
+static struct composite_context *setup_next_method(struct composite_context *c)
+{
+ struct resolve_state *state = talloc_get_type(c->private_data, struct resolve_state);
+ struct composite_context *creq = NULL;
+
+ do {
+ if (state->method) {
+ creq = state->method->send_fn(c, c->event_ctx,
+ state->method->privdata,
+ state->flags,
+ state->port,
+ &state->name);
+ }
+ if (creq == NULL && state->method) state->method = state->method->next;
+
+ } while (!creq && state->method);
+
+ if (creq) {
+ creq->async.fn = resolve_handler;
+ creq->async.private_data = c;
+ }
+
+ return creq;
+}
+
+/*
+ general name resolution - async send
+ */
+struct composite_context *resolve_name_all_send(struct resolve_context *ctx,
+ uint32_t flags,
+ uint16_t port,
+ struct nbt_name *name,
+ struct tevent_context *event_ctx)
+{
+ struct composite_context *c;
+ struct resolve_state *state;
+
+ if (ctx == NULL || event_ctx == NULL) {
+ return NULL;
+ }
+
+ c = composite_create(ctx, event_ctx);
+ if (c == NULL) return NULL;
+
+ if (composite_nomem(c->event_ctx, c)) return c;
+
+ state = talloc(c, struct resolve_state);
+ if (composite_nomem(state, c)) return c;
+ c->private_data = state;
+
+ state->flags = flags;
+ state->port = port;
+
+ c->status = nbt_name_dup(state, name, &state->name);
+ if (!composite_is_ok(c)) return c;
+
+ state->ctx = talloc_reference(state, ctx);
+ if (composite_nomem(state->ctx, c)) return c;
+
+ if (is_ipaddress(state->name.name) ||
+ strcasecmp(state->name.name, "localhost") == 0) {
+ struct in_addr ip = interpret_addr2(state->name.name);
+
+ state->addrs = talloc_array(state, struct socket_address *, 2);
+ if (composite_nomem(state->addrs, c)) return c;
+ state->addrs[0] = socket_address_from_strings(state->addrs, "ipv4",
+ inet_ntoa(ip), 0);
+ if (composite_nomem(state->addrs[0], c)) return c;
+ state->addrs[1] = NULL;
+ state->names = talloc_array(state, char *, 2);
+ if (composite_nomem(state->names, c)) return c;
+ state->names[0] = talloc_strdup(state->names, state->name.name);
+ if (composite_nomem(state->names[0], c)) return c;
+ state->names[1] = NULL;
+ composite_done(c);
+ return c;
+ }
+
+ state->method = ctx->methods;
+ if (state->method == NULL) {
+ composite_error(c, NT_STATUS_BAD_NETWORK_NAME);
+ return c;
+ }
+ state->creq = setup_next_method(c);
+ if (composite_nomem(state->creq, c)) return c;
+
+ return c;
+}
+
+/*
+ general name resolution method - recv side
+ */
+NTSTATUS resolve_name_all_recv(struct composite_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct socket_address ***addrs,
+ char ***names)
+{
+ NTSTATUS status;
+
+ status = composite_wait(c);
+
+ if (NT_STATUS_IS_OK(status)) {
+ struct resolve_state *state = talloc_get_type(c->private_data, struct resolve_state);
+ *addrs = talloc_steal(mem_ctx, state->addrs);
+ if (names) {
+ *names = talloc_steal(mem_ctx, state->names);
+ }
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+struct composite_context *resolve_name_send(struct resolve_context *ctx,
+ struct nbt_name *name,
+ struct tevent_context *event_ctx)
+{
+ return resolve_name_all_send(ctx, 0, 0, name, event_ctx);
+}
+
+NTSTATUS resolve_name_recv(struct composite_context *c,
+ TALLOC_CTX *mem_ctx,
+ const char **reply_addr)
+{
+ NTSTATUS status;
+ struct socket_address **addrs = NULL;
+
+ status = resolve_name_all_recv(c, mem_ctx, &addrs, NULL);
+
+ if (NT_STATUS_IS_OK(status)) {
+ *reply_addr = talloc_steal(mem_ctx, addrs[0]->addr);
+ talloc_free(addrs);
+ }
+
+ return status;
+}
+
+/*
+ general name resolution - sync call
+ */
+NTSTATUS resolve_name(struct resolve_context *ctx,
+ struct nbt_name *name,
+ TALLOC_CTX *mem_ctx,
+ const char **reply_addr,
+ struct tevent_context *ev)
+{
+ struct composite_context *c = resolve_name_send(ctx, name, ev);
+ return resolve_name_recv(c, mem_ctx, reply_addr);
+}
+
+/* Initialise a struct nbt_name with a NULL scope */
+
+void make_nbt_name(struct nbt_name *nbt, const char *name, int type)
+{
+ nbt->name = name;
+ nbt->scope = NULL;
+ nbt->type = type;
+}
+
+/* Initialise a struct nbt_name with a NBT_NAME_CLIENT (0x00) name */
+
+void make_nbt_name_client(struct nbt_name *nbt, const char *name)
+{
+ make_nbt_name(nbt, name, NBT_NAME_CLIENT);
+}
+
+/* Initialise a struct nbt_name with a NBT_NAME_SERVER (0x20) name */
+
+void make_nbt_name_server(struct nbt_name *nbt, const char *name)
+{
+ make_nbt_name(nbt, name, NBT_NAME_SERVER);
+}
+
+
diff --git a/source4/libcli/resolve/resolve.h b/source4/libcli/resolve/resolve.h
new file mode 100644
index 0000000000..8ace5740a9
--- /dev/null
+++ b/source4/libcli/resolve/resolve.h
@@ -0,0 +1,53 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ general name resolution interface
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __LIBCLI_RESOLVE_H__
+#define __LIBCLI_RESOLVE_H__
+
+struct socket_address;
+struct tevent_context;
+
+#include "../libcli/nbt/libnbt.h"
+
+/* force that only NBT name resolution is used */
+#define RESOLVE_NAME_FLAG_FORCE_NBT 0x00000001
+/* force that only DNS name resolution is used */
+#define RESOLVE_NAME_FLAG_FORCE_DNS 0x00000002
+/* tell the dns resolver to do a DNS SRV lookup */
+#define RESOLVE_NAME_FLAG_DNS_SRV 0x00000004
+/* allow the resolver to overwrite the given port, e.g. for DNS SRV */
+#define RESOLVE_NAME_FLAG_OVERWRITE_PORT 0x00000008
+
+typedef struct composite_context *(*resolve_name_send_fn)(TALLOC_CTX *mem_ctx,
+ struct tevent_context *,
+ void *privdata,
+ uint32_t flags,
+ uint16_t port,
+ struct nbt_name *);
+typedef NTSTATUS (*resolve_name_recv_fn)(struct composite_context *creq,
+ TALLOC_CTX *mem_ctx,
+ struct socket_address ***addrs,
+ char ***names);
+#include "libcli/resolve/proto.h"
+struct interface;
+#include "libcli/resolve/lp_proto.h"
+
+#endif /* __LIBCLI_RESOLVE_H__ */
diff --git a/source4/libcli/resolve/resolve_lp.c b/source4/libcli/resolve/resolve_lp.c
new file mode 100644
index 0000000000..b41e2b98d8
--- /dev/null
+++ b/source4/libcli/resolve/resolve_lp.c
@@ -0,0 +1,46 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Jelmer Vernooij <jelmer@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/resolve/resolve.h"
+#include "param/param.h"
+
+struct resolve_context *lp_resolve_context(struct loadparm_context *lp_ctx)
+{
+ const char **methods = lp_name_resolve_order(lp_ctx);
+ int i;
+ struct resolve_context *ret = resolve_context_init(lp_ctx);
+
+ if (ret == NULL)
+ return NULL;
+
+ for (i = 0; methods != NULL && methods[i] != NULL; i++) {
+ if (!strcmp(methods[i], "wins")) {
+ resolve_context_add_wins_method_lp(ret, lp_ctx);
+ } else if (!strcmp(methods[i], "bcast")) {
+ resolve_context_add_bcast_method_lp(ret, lp_ctx);
+ } else if (!strcmp(methods[i], "host")) {
+ resolve_context_add_host_method(ret);
+ } else {
+ DEBUG(0, ("Unknown resolve method '%s'\n", methods[i]));
+ }
+ }
+
+ return ret;
+}
diff --git a/source4/libcli/resolve/testsuite.c b/source4/libcli/resolve/testsuite.c
new file mode 100644
index 0000000000..fdbb430a9f
--- /dev/null
+++ b/source4/libcli/resolve/testsuite.c
@@ -0,0 +1,90 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ local test for async resolve code
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/events/events.h"
+#include "libcli/resolve/resolve.h"
+#include "torture/torture.h"
+#include "system/network.h"
+
+static bool test_async_resolve(struct torture_context *tctx)
+{
+ struct nbt_name n;
+ struct tevent_context *ev;
+ int timelimit = torture_setting_int(tctx, "timelimit", 2);
+ const char *host = torture_setting_string(tctx, "host", NULL);
+ int count = 0;
+ struct timeval tv = timeval_current();
+ TALLOC_CTX *mem_ctx = tctx;
+
+ ev = tctx->ev;
+
+ ZERO_STRUCT(n);
+ n.name = host;
+
+ torture_comment(tctx, "Testing async resolve of '%s' for %d seconds\n",
+ host, timelimit);
+ while (timeval_elapsed(&tv) < timelimit) {
+ struct socket_address **s;
+ struct composite_context *c = resolve_name_host_send(mem_ctx, ev, NULL, 0, 0, &n);
+ torture_assert(tctx, c != NULL, "resolve_name_host_send");
+ torture_assert_ntstatus_ok(tctx, resolve_name_host_recv(c, mem_ctx, &s, NULL),
+ "async resolve failed");
+ count++;
+ }
+
+ torture_comment(tctx, "async rate of %.1f resolves/sec\n",
+ count/timeval_elapsed(&tv));
+ return true;
+}
+
+/*
+ test resolution using sync method
+*/
+static bool test_sync_resolve(struct torture_context *tctx)
+{
+ int timelimit = torture_setting_int(tctx, "timelimit", 2);
+ struct timeval tv = timeval_current();
+ int count = 0;
+ const char *host = torture_setting_string(tctx, "host", NULL);
+
+ torture_comment(tctx, "Testing sync resolve of '%s' for %d seconds\n",
+ host, timelimit);
+ while (timeval_elapsed(&tv) < timelimit) {
+ inet_ntoa(interpret_addr2(host));
+ count++;
+ }
+
+ torture_comment(tctx, "sync rate of %.1f resolves/sec\n",
+ count/timeval_elapsed(&tv));
+ return true;
+}
+
+
+struct torture_suite *torture_local_resolve(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "RESOLVE");
+
+ torture_suite_add_simple_test(suite, "async", test_async_resolve);
+ torture_suite_add_simple_test(suite, "sync", test_sync_resolve);
+
+ return suite;
+}
diff --git a/source4/libcli/resolve/wins.c b/source4/libcli/resolve/wins.c
new file mode 100644
index 0000000000..03eaf8786f
--- /dev/null
+++ b/source4/libcli/resolve/wins.c
@@ -0,0 +1,82 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ wins name resolution module
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "../libcli/nbt/libnbt.h"
+#include "libcli/resolve/resolve.h"
+#include "param/param.h"
+#include "lib/socket/socket.h"
+#include "lib/socket/netif.h"
+
+struct resolve_wins_data {
+ const char **address_list;
+ struct interface *ifaces;
+ uint16_t nbt_port;
+ int nbt_timeout;
+};
+
+/**
+ wins name resolution method - async send
+ */
+struct composite_context *resolve_name_wins_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *event_ctx,
+ void *userdata,
+ uint32_t flags,
+ uint16_t port,
+ struct nbt_name *name)
+{
+ struct resolve_wins_data *wins_data = talloc_get_type(userdata, struct resolve_wins_data);
+ if (wins_data->address_list == NULL) return NULL;
+ return resolve_name_nbtlist_send(mem_ctx, event_ctx, flags, port, name,
+ wins_data->address_list, wins_data->ifaces,
+ wins_data->nbt_port, wins_data->nbt_timeout,
+ false, true);
+}
+
+/*
+ wins name resolution method - recv side
+ */
+NTSTATUS resolve_name_wins_recv(struct composite_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct socket_address ***addrs,
+ char ***names)
+{
+ return resolve_name_nbtlist_recv(c, mem_ctx, addrs, names);
+}
+
+bool resolve_context_add_wins_method(struct resolve_context *ctx, const char **address_list, struct interface *ifaces, uint16_t nbt_port, int nbt_timeout)
+{
+ struct resolve_wins_data *wins_data = talloc(ctx, struct resolve_wins_data);
+ wins_data->address_list = (const char **)str_list_copy(wins_data, address_list);
+ wins_data->ifaces = talloc_reference(wins_data, ifaces);
+ wins_data->nbt_port = nbt_port;
+ wins_data->nbt_timeout = nbt_timeout;
+ return resolve_context_add_method(ctx, resolve_name_wins_send, resolve_name_wins_recv,
+ wins_data);
+}
+
+bool resolve_context_add_wins_method_lp(struct resolve_context *ctx, struct loadparm_context *lp_ctx)
+{
+ struct interface *ifaces;
+ load_interfaces(ctx, lp_interfaces(lp_ctx), &ifaces);
+ return resolve_context_add_wins_method(ctx, lp_wins_server_list(lp_ctx), ifaces, lp_nbt_port(lp_ctx), lp_parm_int(lp_ctx, NULL, "nbt", "timeout", 1));
+}
diff --git a/source4/libcli/security/access_check.c b/source4/libcli/security/access_check.c
new file mode 100644
index 0000000000..af6a3d6fb3
--- /dev/null
+++ b/source4/libcli/security/access_check.c
@@ -0,0 +1,155 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ security access checking routines
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/security/security.h"
+
+
+/*
+ perform a SEC_FLAG_MAXIMUM_ALLOWED access check
+*/
+static uint32_t access_check_max_allowed(const struct security_descriptor *sd,
+ const struct security_token *token)
+{
+ uint32_t denied = 0, granted = 0;
+ unsigned i;
+
+ if (security_token_has_sid(token, sd->owner_sid)) {
+ granted |= SEC_STD_WRITE_DAC | SEC_STD_READ_CONTROL | SEC_STD_DELETE;
+ } else if (security_token_has_privilege(token, SEC_PRIV_RESTORE)) {
+ granted |= SEC_STD_DELETE;
+ }
+
+ if (sd->dacl == NULL) {
+ return granted & ~denied;
+ }
+
+ for (i = 0;i<sd->dacl->num_aces; i++) {
+ struct security_ace *ace = &sd->dacl->aces[i];
+
+ if (ace->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
+ continue;
+ }
+
+ if (!security_token_has_sid(token, &ace->trustee)) {
+ continue;
+ }
+
+ switch (ace->type) {
+ case SEC_ACE_TYPE_ACCESS_ALLOWED:
+ granted |= ace->access_mask;
+ break;
+ case SEC_ACE_TYPE_ACCESS_DENIED:
+ case SEC_ACE_TYPE_ACCESS_DENIED_OBJECT:
+ denied |= ace->access_mask;
+ break;
+ default: /* Other ACE types not handled/supported */
+ break;
+ }
+ }
+
+ return granted & ~denied;
+}
+
+/*
+ the main entry point for access checking.
+*/
+NTSTATUS sec_access_check(const struct security_descriptor *sd,
+ const struct security_token *token,
+ uint32_t access_desired,
+ uint32_t *access_granted)
+{
+ int i;
+ uint32_t bits_remaining;
+
+ *access_granted = access_desired;
+ bits_remaining = access_desired;
+
+ /* handle the maximum allowed flag */
+ if (access_desired & SEC_FLAG_MAXIMUM_ALLOWED) {
+ access_desired |= access_check_max_allowed(sd, token);
+ access_desired &= ~SEC_FLAG_MAXIMUM_ALLOWED;
+ *access_granted = access_desired;
+ bits_remaining = access_desired & ~SEC_STD_DELETE;
+ }
+
+ if (access_desired & SEC_FLAG_SYSTEM_SECURITY) {
+ if (security_token_has_privilege(token, SEC_PRIV_SECURITY)) {
+ bits_remaining &= ~SEC_FLAG_SYSTEM_SECURITY;
+ } else {
+ return NT_STATUS_PRIVILEGE_NOT_HELD;
+ }
+ }
+
+ /* a NULL dacl allows access */
+ if ((sd->type & SEC_DESC_DACL_PRESENT) && sd->dacl == NULL) {
+ *access_granted = access_desired;
+ return NT_STATUS_OK;
+ }
+
+ /* the owner always gets SEC_STD_WRITE_DAC, SEC_STD_READ_CONTROL and SEC_STD_DELETE */
+ if ((bits_remaining & (SEC_STD_WRITE_DAC|SEC_STD_READ_CONTROL|SEC_STD_DELETE)) &&
+ security_token_has_sid(token, sd->owner_sid)) {
+ bits_remaining &= ~(SEC_STD_WRITE_DAC|SEC_STD_READ_CONTROL|SEC_STD_DELETE);
+ }
+ if ((bits_remaining & SEC_STD_DELETE) &&
+ security_token_has_privilege(token, SEC_PRIV_RESTORE)) {
+ bits_remaining &= ~SEC_STD_DELETE;
+ }
+
+ if (sd->dacl == NULL) {
+ goto done;
+ }
+
+ /* check each ace in turn. */
+ for (i=0; bits_remaining && i < sd->dacl->num_aces; i++) {
+ struct security_ace *ace = &sd->dacl->aces[i];
+
+ if (ace->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
+ continue;
+ }
+
+ if (!security_token_has_sid(token, &ace->trustee)) {
+ continue;
+ }
+
+ switch (ace->type) {
+ case SEC_ACE_TYPE_ACCESS_ALLOWED:
+ bits_remaining &= ~ace->access_mask;
+ break;
+ case SEC_ACE_TYPE_ACCESS_DENIED:
+ case SEC_ACE_TYPE_ACCESS_DENIED_OBJECT:
+ if (bits_remaining & ace->access_mask) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ break;
+ default: /* Other ACE types not handled/supported */
+ break;
+ }
+ }
+
+done:
+ if (bits_remaining != 0) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/libcli/security/config.mk b/source4/libcli/security/config.mk
new file mode 100644
index 0000000000..d6d9ad5545
--- /dev/null
+++ b/source4/libcli/security/config.mk
@@ -0,0 +1,10 @@
+[SUBSYSTEM::LIBSECURITY]
+PUBLIC_DEPENDENCIES = LIBNDR LIBSECURITY_COMMON
+
+LIBSECURITY_OBJ_FILES = $(addprefix $(libclisrcdir)/security/, \
+ security_token.o security_descriptor.o \
+ access_check.o privilege.o sddl.o) \
+ ../libcli/security/secace.o \
+ ../libcli/security/secacl.o
+
+$(eval $(call proto_header_template,$(libclisrcdir)/security/proto.h,$(LIBSECURITY_OBJ_FILES:.o=.c)))
diff --git a/source4/libcli/security/privilege.c b/source4/libcli/security/privilege.c
new file mode 100644
index 0000000000..2cbef13538
--- /dev/null
+++ b/source4/libcli/security/privilege.c
@@ -0,0 +1,241 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ manipulate privileges
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "librpc/gen_ndr/security.h"
+#include "libcli/security/security.h"
+
+
+static const struct {
+ enum sec_privilege privilege;
+ const char *name;
+ const char *display_name;
+} privilege_names[] = {
+ {SEC_PRIV_SECURITY,
+ "SeSecurityPrivilege",
+ "System security"},
+
+ {SEC_PRIV_BACKUP,
+ "SeBackupPrivilege",
+ "Backup files and directories"},
+
+ {SEC_PRIV_RESTORE,
+ "SeRestorePrivilege",
+ "Restore files and directories"},
+
+ {SEC_PRIV_SYSTEMTIME,
+ "SeSystemtimePrivilege",
+ "Set the system clock"},
+
+ {SEC_PRIV_SHUTDOWN,
+ "SeShutdownPrivilege",
+ "Shutdown the system"},
+
+ {SEC_PRIV_REMOTE_SHUTDOWN,
+ "SeRemoteShutdownPrivilege",
+ "Shutdown the system remotely"},
+
+ {SEC_PRIV_TAKE_OWNERSHIP,
+ "SeTakeOwnershipPrivilege",
+ "Take ownership of files and directories"},
+
+ {SEC_PRIV_DEBUG,
+ "SeDebugPrivilege",
+ "Debug processes"},
+
+ {SEC_PRIV_SYSTEM_ENVIRONMENT,
+ "SeSystemEnvironmentPrivilege",
+ "Modify system environment"},
+
+ {SEC_PRIV_SYSTEM_PROFILE,
+ "SeSystemProfilePrivilege",
+ "Profile the system"},
+
+ {SEC_PRIV_PROFILE_SINGLE_PROCESS,
+ "SeProfileSingleProcessPrivilege",
+ "Profile one process"},
+
+ {SEC_PRIV_INCREASE_BASE_PRIORITY,
+ "SeIncreaseBasePriorityPrivilege",
+ "Increase base priority"},
+
+ {SEC_PRIV_LOAD_DRIVER,
+ "SeLoadDriverPrivilege",
+ "Load drivers"},
+
+ {SEC_PRIV_CREATE_PAGEFILE,
+ "SeCreatePagefilePrivilege",
+ "Create page files"},
+
+ {SEC_PRIV_INCREASE_QUOTA,
+ "SeIncreaseQuotaPrivilege",
+ "Increase quota"},
+
+ {SEC_PRIV_CHANGE_NOTIFY,
+ "SeChangeNotifyPrivilege",
+ "Register for change notify"},
+
+ {SEC_PRIV_UNDOCK,
+ "SeUndockPrivilege",
+ "Undock devices"},
+
+ {SEC_PRIV_MANAGE_VOLUME,
+ "SeManageVolumePrivilege",
+ "Manage system volumes"},
+
+ {SEC_PRIV_IMPERSONATE,
+ "SeImpersonatePrivilege",
+ "Impersonate users"},
+
+ {SEC_PRIV_CREATE_GLOBAL,
+ "SeCreateGlobalPrivilege",
+ "Create global"},
+
+ {SEC_PRIV_ENABLE_DELEGATION,
+ "SeEnableDelegationPrivilege",
+ "Enable Delegation"},
+
+ {SEC_PRIV_INTERACTIVE_LOGON,
+ "SeInteractiveLogonRight",
+ "Interactive logon"},
+
+ {SEC_PRIV_NETWORK_LOGON,
+ "SeNetworkLogonRight",
+ "Network logon"},
+
+ {SEC_PRIV_REMOTE_INTERACTIVE_LOGON,
+ "SeRemoteInteractiveLogonRight",
+ "Remote Interactive logon"}
+};
+
+
+/*
+ map a privilege id to the wire string constant
+*/
+const char *sec_privilege_name(enum sec_privilege privilege)
+{
+ int i;
+ for (i=0;i<ARRAY_SIZE(privilege_names);i++) {
+ if (privilege_names[i].privilege == privilege) {
+ return privilege_names[i].name;
+ }
+ }
+ return NULL;
+}
+
+/*
+ map a privilege id to a privilege display name. Return NULL if not found
+
+ TODO: this should use language mappings
+*/
+const char *sec_privilege_display_name(enum sec_privilege privilege, uint16_t *language)
+{
+ int i;
+ if (privilege < 1 || privilege > 64) {
+ return NULL;
+ }
+ for (i=0;i<ARRAY_SIZE(privilege_names);i++) {
+ if (privilege_names[i].privilege == privilege) {
+ return privilege_names[i].display_name;
+ }
+ }
+ return NULL;
+}
+
+/*
+ map a privilege name to a privilege id. Return -1 if not found
+*/
+enum sec_privilege sec_privilege_id(const char *name)
+{
+ int i;
+ for (i=0;i<ARRAY_SIZE(privilege_names);i++) {
+ if (strcasecmp(privilege_names[i].name, name) == 0) {
+ return privilege_names[i].privilege;
+ }
+ }
+ return -1;
+}
+
+
+/*
+ return a privilege mask given a privilege id
+*/
+static uint64_t sec_privilege_mask(enum sec_privilege privilege)
+{
+ uint64_t mask = 1;
+
+ if (privilege < 1 || privilege > 64) {
+ return 0;
+ }
+
+ mask <<= (privilege-1);
+ return mask;
+}
+
+
+/*
+ return true if a security_token has a particular privilege bit set
+*/
+bool security_token_has_privilege(const struct security_token *token, enum sec_privilege privilege)
+{
+ uint64_t mask;
+
+ if (privilege < 1 || privilege > 64) {
+ return false;
+ }
+
+ mask = sec_privilege_mask(privilege);
+ if (token->privilege_mask & mask) {
+ return true;
+ }
+ return false;
+}
+
+/*
+ set a bit in the privilege mask
+*/
+void security_token_set_privilege(struct security_token *token, enum sec_privilege privilege)
+{
+ if (privilege < 1 || privilege > 64) {
+ return;
+ }
+ token->privilege_mask |= sec_privilege_mask(privilege);
+}
+
+void security_token_debug_privileges(int dbg_lev, const struct security_token *token)
+{
+ DEBUGADD(dbg_lev, (" Privileges (0x%16llX):\n",
+ (unsigned long long) token->privilege_mask));
+
+ if (token->privilege_mask) {
+ int i = 0;
+ uint_t privilege;
+
+ for (privilege = 1; privilege <= 64; privilege++) {
+ uint64_t mask = sec_privilege_mask(privilege);
+
+ if (token->privilege_mask & mask) {
+ DEBUGADD(dbg_lev, (" Privilege[%3lu]: %s\n", (unsigned long)i++,
+ sec_privilege_name(privilege)));
+ }
+ }
+ }
+}
diff --git a/source4/libcli/security/sddl.c b/source4/libcli/security/sddl.c
new file mode 100644
index 0000000000..a8d893f085
--- /dev/null
+++ b/source4/libcli/security/sddl.c
@@ -0,0 +1,598 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ security descriptor description language functions
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/security/security.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "system/locale.h"
+
+struct flag_map {
+ const char *name;
+ uint32_t flag;
+};
+
+/*
+ map a series of letter codes into a uint32_t
+*/
+static bool sddl_map_flags(const struct flag_map *map, const char *str,
+ uint32_t *flags, size_t *len)
+{
+ const char *str0 = str;
+ if (len) *len = 0;
+ *flags = 0;
+ while (str[0] && isupper(str[0])) {
+ int i;
+ for (i=0;map[i].name;i++) {
+ size_t l = strlen(map[i].name);
+ if (strncmp(map[i].name, str, l) == 0) {
+ *flags |= map[i].flag;
+ str += l;
+ if (len) *len += l;
+ break;
+ }
+ }
+ if (map[i].name == NULL) {
+ DEBUG(1, ("Unknown flag - %s in %s\n", str, str0));
+ return false;
+ }
+ }
+ return true;
+}
+
+/*
+ a mapping between the 2 letter SID codes and sid strings
+*/
+static const struct {
+ const char *code;
+ const char *sid;
+ uint32_t rid;
+} sid_codes[] = {
+ { "AO", SID_BUILTIN_ACCOUNT_OPERATORS },
+ { "BA", SID_BUILTIN_ADMINISTRATORS },
+ { "RU", SID_BUILTIN_PREW2K },
+ { "PO", SID_BUILTIN_PRINT_OPERATORS },
+ { "RS", SID_BUILTIN_RAS_SERVERS },
+
+ { "AU", SID_NT_AUTHENTICATED_USERS },
+ { "SY", SID_NT_SYSTEM },
+ { "PS", SID_NT_SELF },
+ { "WD", SID_WORLD },
+ { "ED", SID_NT_ENTERPRISE_DCS },
+
+ { "CO", SID_CREATOR_OWNER },
+ { "CG", SID_CREATOR_GROUP },
+
+ { "DA", NULL, DOMAIN_RID_ADMINS },
+ { "EA", NULL, DOMAIN_RID_ENTERPRISE_ADMINS },
+ { "DD", NULL, DOMAIN_RID_DCS },
+ { "DU", NULL, DOMAIN_RID_USERS },
+ { "CA", NULL, DOMAIN_RID_CERT_ADMINS },
+};
+
+/*
+ decode a SID
+ It can either be a special 2 letter code, or in S-* format
+*/
+static struct dom_sid *sddl_decode_sid(TALLOC_CTX *mem_ctx, const char **sddlp,
+ const struct dom_sid *domain_sid)
+{
+ const char *sddl = (*sddlp);
+ int i;
+
+ /* see if its in the numeric format */
+ if (strncmp(sddl, "S-", 2) == 0) {
+ struct dom_sid *sid;
+ char *sid_str;
+ size_t len = strspn(sddl+2, "-0123456789");
+ sid_str = talloc_strndup(mem_ctx, sddl, len+2);
+ if (!sid_str) {
+ return NULL;
+ }
+ (*sddlp) += len+2;
+ sid = dom_sid_parse_talloc(mem_ctx, sid_str);
+ talloc_free(sid_str);
+ return sid;
+ }
+
+ /* now check for one of the special codes */
+ for (i=0;i<ARRAY_SIZE(sid_codes);i++) {
+ if (strncmp(sid_codes[i].code, sddl, 2) == 0) break;
+ }
+ if (i == ARRAY_SIZE(sid_codes)) {
+ DEBUG(1,("Unknown sddl sid code '%2.2s'\n", sddl));
+ return NULL;
+ }
+
+ (*sddlp) += 2;
+
+ if (sid_codes[i].sid == NULL) {
+ return dom_sid_add_rid(mem_ctx, domain_sid, sid_codes[i].rid);
+ }
+
+ return dom_sid_parse_talloc(mem_ctx, sid_codes[i].sid);
+}
+
+static const struct flag_map ace_types[] = {
+ { "AU", SEC_ACE_TYPE_SYSTEM_AUDIT },
+ { "AL", SEC_ACE_TYPE_SYSTEM_ALARM },
+ { "OA", SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT },
+ { "OD", SEC_ACE_TYPE_ACCESS_DENIED_OBJECT },
+ { "OU", SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT },
+ { "OL", SEC_ACE_TYPE_SYSTEM_ALARM_OBJECT },
+ { "A", SEC_ACE_TYPE_ACCESS_ALLOWED },
+ { "D", SEC_ACE_TYPE_ACCESS_DENIED },
+ { NULL, 0 }
+};
+
+static const struct flag_map ace_flags[] = {
+ { "OI", SEC_ACE_FLAG_OBJECT_INHERIT },
+ { "CI", SEC_ACE_FLAG_CONTAINER_INHERIT },
+ { "NP", SEC_ACE_FLAG_NO_PROPAGATE_INHERIT },
+ { "IO", SEC_ACE_FLAG_INHERIT_ONLY },
+ { "ID", SEC_ACE_FLAG_INHERITED_ACE },
+ { "SA", SEC_ACE_FLAG_SUCCESSFUL_ACCESS },
+ { "FA", SEC_ACE_FLAG_FAILED_ACCESS },
+ { NULL, 0 },
+};
+
+static const struct flag_map ace_access_mask[] = {
+ { "RP", SEC_ADS_READ_PROP },
+ { "WP", SEC_ADS_WRITE_PROP },
+ { "CR", SEC_ADS_CONTROL_ACCESS },
+ { "CC", SEC_ADS_CREATE_CHILD },
+ { "DC", SEC_ADS_DELETE_CHILD },
+ { "LC", SEC_ADS_LIST },
+ { "LO", SEC_ADS_LIST_OBJECT },
+ { "RC", SEC_STD_READ_CONTROL },
+ { "WO", SEC_STD_WRITE_OWNER },
+ { "WD", SEC_STD_WRITE_DAC },
+ { "SD", SEC_STD_DELETE },
+ { "DT", SEC_ADS_DELETE_TREE },
+ { "SW", SEC_ADS_SELF_WRITE },
+ { "GA", SEC_GENERIC_ALL },
+ { "GR", SEC_GENERIC_READ },
+ { "GW", SEC_GENERIC_WRITE },
+ { "GX", SEC_GENERIC_EXECUTE },
+ { NULL, 0 }
+};
+
+/*
+ decode an ACE
+ return true on success, false on failure
+ note that this routine modifies the string
+*/
+static bool sddl_decode_ace(TALLOC_CTX *mem_ctx, struct security_ace *ace, char *str,
+ const struct dom_sid *domain_sid)
+{
+ const char *tok[6];
+ const char *s;
+ int i;
+ uint32_t v;
+ struct dom_sid *sid;
+
+ ZERO_STRUCTP(ace);
+
+ /* parse out the 6 tokens */
+ tok[0] = str;
+ for (i=0;i<5;i++) {
+ char *ptr = strchr(str, ';');
+ if (ptr == NULL) return false;
+ *ptr = 0;
+ str = ptr+1;
+ tok[i+1] = str;
+ }
+
+ /* parse ace type */
+ if (!sddl_map_flags(ace_types, tok[0], &v, NULL)) {
+ return false;
+ }
+ ace->type = v;
+
+ /* ace flags */
+ if (!sddl_map_flags(ace_flags, tok[1], &v, NULL)) {
+ return false;
+ }
+ ace->flags = v;
+
+ /* access mask */
+ if (strncmp(tok[2], "0x", 2) == 0) {
+ ace->access_mask = strtol(tok[2], NULL, 16);
+ } else {
+ if (!sddl_map_flags(ace_access_mask, tok[2], &v, NULL)) {
+ return false;
+ }
+ ace->access_mask = v;
+ }
+
+ /* object */
+ if (tok[3][0] != 0) {
+ NTSTATUS status = GUID_from_string(tok[3],
+ &ace->object.object.type.type);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+ ace->object.object.flags |= SEC_ACE_OBJECT_TYPE_PRESENT;
+ }
+
+ /* inherit object */
+ if (tok[4][0] != 0) {
+ NTSTATUS status = GUID_from_string(tok[4],
+ &ace->object.object.inherited_type.inherited_type);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+ ace->object.object.flags |= SEC_ACE_INHERITED_OBJECT_TYPE_PRESENT;
+ }
+
+ /* trustee */
+ s = tok[5];
+ sid = sddl_decode_sid(mem_ctx, &s, domain_sid);
+ if (sid == NULL) {
+ return false;
+ }
+ ace->trustee = *sid;
+ talloc_free(sid);
+
+ return true;
+}
+
+static const struct flag_map acl_flags[] = {
+ { "P", SEC_DESC_DACL_PROTECTED },
+ { "AR", SEC_DESC_DACL_AUTO_INHERIT_REQ },
+ { "AI", SEC_DESC_DACL_AUTO_INHERITED },
+ { NULL, 0 }
+};
+
+/*
+ decode an ACL
+*/
+static struct security_acl *sddl_decode_acl(struct security_descriptor *sd,
+ const char **sddlp, uint32_t *flags,
+ const struct dom_sid *domain_sid)
+{
+ const char *sddl = *sddlp;
+ struct security_acl *acl;
+ size_t len;
+
+ *flags = 0;
+
+ acl = talloc_zero(sd, struct security_acl);
+ if (acl == NULL) return NULL;
+ acl->revision = SECURITY_ACL_REVISION_NT4;
+
+ if (isupper(sddl[0]) && sddl[1] == ':') {
+ /* its an empty ACL */
+ return acl;
+ }
+
+ /* work out the ACL flags */
+ if (!sddl_map_flags(acl_flags, sddl, flags, &len)) {
+ talloc_free(acl);
+ return NULL;
+ }
+ sddl += len;
+
+ /* now the ACEs */
+ while (*sddl == '(') {
+ char *astr;
+ len = strcspn(sddl+1, ")");
+ astr = talloc_strndup(acl, sddl+1, len);
+ if (astr == NULL || sddl[len+1] != ')') {
+ talloc_free(acl);
+ return NULL;
+ }
+ acl->aces = talloc_realloc(acl, acl->aces, struct security_ace,
+ acl->num_aces+1);
+ if (acl->aces == NULL) {
+ talloc_free(acl);
+ return NULL;
+ }
+ if (!sddl_decode_ace(acl->aces, &acl->aces[acl->num_aces],
+ astr, domain_sid)) {
+ talloc_free(acl);
+ return NULL;
+ }
+ switch (acl->aces[acl->num_aces].type) {
+ case SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT:
+ case SEC_ACE_TYPE_ACCESS_DENIED_OBJECT:
+ case SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT:
+ case SEC_ACE_TYPE_SYSTEM_ALARM_OBJECT:
+ acl->revision = SECURITY_ACL_REVISION_ADS;
+ break;
+ default:
+ break;
+ }
+ talloc_free(astr);
+ sddl += len+2;
+ acl->num_aces++;
+ }
+
+ (*sddlp) = sddl;
+ return acl;
+}
+
+/*
+ decode a security descriptor in SDDL format
+*/
+struct security_descriptor *sddl_decode(TALLOC_CTX *mem_ctx, const char *sddl,
+ const struct dom_sid *domain_sid)
+{
+ struct security_descriptor *sd;
+ sd = talloc_zero(mem_ctx, struct security_descriptor);
+
+ sd->revision = SECURITY_DESCRIPTOR_REVISION_1;
+ sd->type = SEC_DESC_SELF_RELATIVE;
+
+ while (*sddl) {
+ uint32_t flags;
+ char c = sddl[0];
+ if (sddl[1] != ':') goto failed;
+
+ sddl += 2;
+ switch (c) {
+ case 'D':
+ if (sd->dacl != NULL) goto failed;
+ sd->dacl = sddl_decode_acl(sd, &sddl, &flags, domain_sid);
+ if (sd->dacl == NULL) goto failed;
+ sd->type |= flags | SEC_DESC_DACL_PRESENT;
+ break;
+ case 'S':
+ if (sd->sacl != NULL) goto failed;
+ sd->sacl = sddl_decode_acl(sd, &sddl, &flags, domain_sid);
+ if (sd->sacl == NULL) goto failed;
+ /* this relies on the SEC_DESC_SACL_* flags being
+ 1 bit shifted from the SEC_DESC_DACL_* flags */
+ sd->type |= (flags<<1) | SEC_DESC_SACL_PRESENT;
+ break;
+ case 'O':
+ if (sd->owner_sid != NULL) goto failed;
+ sd->owner_sid = sddl_decode_sid(sd, &sddl, domain_sid);
+ if (sd->owner_sid == NULL) goto failed;
+ break;
+ case 'G':
+ if (sd->group_sid != NULL) goto failed;
+ sd->group_sid = sddl_decode_sid(sd, &sddl, domain_sid);
+ if (sd->group_sid == NULL) goto failed;
+ break;
+ }
+ }
+
+ return sd;
+
+failed:
+ DEBUG(2,("Badly formatted SDDL '%s'\n", sddl));
+ talloc_free(sd);
+ return NULL;
+}
+
+/*
+ turn a set of flags into a string
+*/
+static char *sddl_flags_to_string(TALLOC_CTX *mem_ctx, const struct flag_map *map,
+ uint32_t flags, bool check_all)
+{
+ int i;
+ char *s;
+
+ /* try to find an exact match */
+ for (i=0;map[i].name;i++) {
+ if (map[i].flag == flags) {
+ return talloc_strdup(mem_ctx, map[i].name);
+ }
+ }
+
+ s = talloc_strdup(mem_ctx, "");
+
+ /* now by bits */
+ for (i=0;map[i].name;i++) {
+ if ((flags & map[i].flag) != 0) {
+ s = talloc_asprintf_append_buffer(s, "%s", map[i].name);
+ if (s == NULL) goto failed;
+ flags &= ~map[i].flag;
+ }
+ }
+
+ if (check_all && flags != 0) {
+ goto failed;
+ }
+
+ return s;
+
+failed:
+ talloc_free(s);
+ return NULL;
+}
+
+/*
+ encode a sid in SDDL format
+*/
+static char *sddl_encode_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
+ const struct dom_sid *domain_sid)
+{
+ int i;
+ char *sidstr;
+
+ sidstr = dom_sid_string(mem_ctx, sid);
+ if (sidstr == NULL) return NULL;
+
+ /* seen if its a well known sid */
+ for (i=0;sid_codes[i].sid;i++) {
+ if (strcmp(sidstr, sid_codes[i].sid) == 0) {
+ talloc_free(sidstr);
+ return talloc_strdup(mem_ctx, sid_codes[i].code);
+ }
+ }
+
+ /* or a well known rid in our domain */
+ if (dom_sid_in_domain(domain_sid, sid)) {
+ uint32_t rid = sid->sub_auths[sid->num_auths-1];
+ for (;i<ARRAY_SIZE(sid_codes);i++) {
+ if (rid == sid_codes[i].rid) {
+ talloc_free(sidstr);
+ return talloc_strdup(mem_ctx, sid_codes[i].code);
+ }
+ }
+ }
+
+ talloc_free(sidstr);
+
+ /* TODO: encode well known sids as two letter codes */
+ return dom_sid_string(mem_ctx, sid);
+}
+
+
+/*
+ encode an ACE in SDDL format
+*/
+static char *sddl_encode_ace(TALLOC_CTX *mem_ctx, const struct security_ace *ace,
+ const struct dom_sid *domain_sid)
+{
+ char *sddl = NULL;
+ TALLOC_CTX *tmp_ctx;
+ const char *s_type="", *s_flags="", *s_mask="",
+ *s_object="", *s_iobject="", *s_trustee="";
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) {
+ DEBUG(0, ("talloc_new failed\n"));
+ return NULL;
+ }
+
+ s_type = sddl_flags_to_string(tmp_ctx, ace_types, ace->type, true);
+ if (s_type == NULL) goto failed;
+
+ s_flags = sddl_flags_to_string(tmp_ctx, ace_flags, ace->flags, true);
+ if (s_flags == NULL) goto failed;
+
+ s_mask = sddl_flags_to_string(tmp_ctx, ace_access_mask, ace->access_mask, true);
+ if (s_mask == NULL) {
+ s_mask = talloc_asprintf(tmp_ctx, "0x%08x", ace->access_mask);
+ if (s_mask == NULL) goto failed;
+ }
+
+ if (ace->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT ||
+ ace->type == SEC_ACE_TYPE_ACCESS_DENIED_OBJECT ||
+ ace->type == SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT ||
+ ace->type == SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT) {
+ if (ace->object.object.flags & SEC_ACE_OBJECT_TYPE_PRESENT) {
+ s_object = GUID_string(tmp_ctx, &ace->object.object.type.type);
+ if (s_object == NULL) goto failed;
+ }
+
+ if (ace->object.object.flags & SEC_ACE_INHERITED_OBJECT_TYPE_PRESENT) {
+ s_iobject = GUID_string(tmp_ctx, &ace->object.object.inherited_type.inherited_type);
+ if (s_iobject == NULL) goto failed;
+ }
+ }
+
+ s_trustee = sddl_encode_sid(tmp_ctx, &ace->trustee, domain_sid);
+ if (s_trustee == NULL) goto failed;
+
+ sddl = talloc_asprintf(mem_ctx, "%s;%s;%s;%s;%s;%s",
+ s_type, s_flags, s_mask, s_object, s_iobject, s_trustee);
+
+failed:
+ talloc_free(tmp_ctx);
+ return sddl;
+}
+
+/*
+ encode an ACL in SDDL format
+*/
+static char *sddl_encode_acl(TALLOC_CTX *mem_ctx, const struct security_acl *acl,
+ uint32_t flags, const struct dom_sid *domain_sid)
+{
+ char *sddl;
+ int i;
+
+ /* add any ACL flags */
+ sddl = sddl_flags_to_string(mem_ctx, acl_flags, flags, false);
+ if (sddl == NULL) goto failed;
+
+ /* now the ACEs, encoded in braces */
+ for (i=0;i<acl->num_aces;i++) {
+ char *ace = sddl_encode_ace(sddl, &acl->aces[i], domain_sid);
+ if (ace == NULL) goto failed;
+ sddl = talloc_asprintf_append_buffer(sddl, "(%s)", ace);
+ if (sddl == NULL) goto failed;
+ talloc_free(ace);
+ }
+
+ return sddl;
+
+failed:
+ talloc_free(sddl);
+ return NULL;
+}
+
+
+/*
+ encode a security descriptor to SDDL format
+*/
+char *sddl_encode(TALLOC_CTX *mem_ctx, const struct security_descriptor *sd,
+ const struct dom_sid *domain_sid)
+{
+ char *sddl;
+ TALLOC_CTX *tmp_ctx;
+
+ /* start with a blank string */
+ sddl = talloc_strdup(mem_ctx, "");
+ if (sddl == NULL) goto failed;
+
+ tmp_ctx = talloc_new(mem_ctx);
+
+ if (sd->owner_sid != NULL) {
+ char *sid = sddl_encode_sid(tmp_ctx, sd->owner_sid, domain_sid);
+ if (sid == NULL) goto failed;
+ sddl = talloc_asprintf_append_buffer(sddl, "O:%s", sid);
+ if (sddl == NULL) goto failed;
+ }
+
+ if (sd->group_sid != NULL) {
+ char *sid = sddl_encode_sid(tmp_ctx, sd->group_sid, domain_sid);
+ if (sid == NULL) goto failed;
+ sddl = talloc_asprintf_append_buffer(sddl, "G:%s", sid);
+ if (sddl == NULL) goto failed;
+ }
+
+ if ((sd->type & SEC_DESC_DACL_PRESENT) && sd->dacl != NULL) {
+ char *acl = sddl_encode_acl(tmp_ctx, sd->dacl, sd->type, domain_sid);
+ if (acl == NULL) goto failed;
+ sddl = talloc_asprintf_append_buffer(sddl, "D:%s", acl);
+ if (sddl == NULL) goto failed;
+ }
+
+ if ((sd->type & SEC_DESC_SACL_PRESENT) && sd->sacl != NULL) {
+ char *acl = sddl_encode_acl(tmp_ctx, sd->sacl, sd->type>>1, domain_sid);
+ if (acl == NULL) goto failed;
+ sddl = talloc_asprintf_append_buffer(sddl, "S:%s", acl);
+ if (sddl == NULL) goto failed;
+ }
+
+ talloc_free(tmp_ctx);
+ return sddl;
+
+failed:
+ talloc_free(sddl);
+ return NULL;
+}
+
+
diff --git a/source4/libcli/security/security.h b/source4/libcli/security/security.h
new file mode 100644
index 0000000000..2608c9f7ed
--- /dev/null
+++ b/source4/libcli/security/security.h
@@ -0,0 +1,35 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "librpc/gen_ndr/security.h"
+
+enum security_user_level {
+ SECURITY_ANONYMOUS,
+ SECURITY_USER,
+ SECURITY_ADMINISTRATOR,
+ SECURITY_SYSTEM
+};
+
+struct auth_session_info;
+
+/* Moved the dom_sid functions to the top level dir with manual proto header */
+#include "libcli/security/dom_sid.h"
+#include "libcli/security/secace.h"
+#include "libcli/security/secacl.h"
+#include "libcli/security/proto.h"
diff --git a/source4/libcli/security/security_descriptor.c b/source4/libcli/security/security_descriptor.c
new file mode 100644
index 0000000000..8e9c7eb4a9
--- /dev/null
+++ b/source4/libcli/security/security_descriptor.c
@@ -0,0 +1,533 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ security descriptror utility functions
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/security/security.h"
+
+/*
+ return a blank security descriptor (no owners, dacl or sacl)
+*/
+struct security_descriptor *security_descriptor_initialise(TALLOC_CTX *mem_ctx)
+{
+ struct security_descriptor *sd;
+
+ sd = talloc(mem_ctx, struct security_descriptor);
+ if (!sd) {
+ return NULL;
+ }
+
+ sd->revision = SD_REVISION;
+ /* we mark as self relative, even though it isn't while it remains
+ a pointer in memory because this simplifies the ndr code later.
+ All SDs that we store/emit are in fact SELF_RELATIVE
+ */
+ sd->type = SEC_DESC_SELF_RELATIVE;
+
+ sd->owner_sid = NULL;
+ sd->group_sid = NULL;
+ sd->sacl = NULL;
+ sd->dacl = NULL;
+
+ return sd;
+}
+
+static struct security_acl *security_acl_dup(TALLOC_CTX *mem_ctx,
+ const struct security_acl *oacl)
+{
+ struct security_acl *nacl;
+
+ nacl = talloc (mem_ctx, struct security_acl);
+ if (nacl == NULL) {
+ return NULL;
+ }
+
+ nacl->aces = (struct security_ace *)talloc_memdup (nacl, oacl->aces, sizeof(struct security_ace) * oacl->num_aces);
+ if ((nacl->aces == NULL) && (oacl->num_aces > 0)) {
+ goto failed;
+ }
+
+ nacl->revision = oacl->revision;
+ nacl->size = oacl->size;
+ nacl->num_aces = oacl->num_aces;
+
+ return nacl;
+
+ failed:
+ talloc_free (nacl);
+ return NULL;
+
+}
+
+/*
+ talloc and copy a security descriptor
+ */
+struct security_descriptor *security_descriptor_copy(TALLOC_CTX *mem_ctx,
+ const struct security_descriptor *osd)
+{
+ struct security_descriptor *nsd;
+
+ nsd = talloc_zero(mem_ctx, struct security_descriptor);
+ if (!nsd) {
+ return NULL;
+ }
+
+ if (osd->owner_sid) {
+ nsd->owner_sid = dom_sid_dup(nsd, osd->owner_sid);
+ if (nsd->owner_sid == NULL) {
+ goto failed;
+ }
+ }
+
+ if (osd->group_sid) {
+ nsd->group_sid = dom_sid_dup(nsd, osd->group_sid);
+ if (nsd->group_sid == NULL) {
+ goto failed;
+ }
+ }
+
+ if (osd->sacl) {
+ nsd->sacl = security_acl_dup(nsd, osd->sacl);
+ if (nsd->sacl == NULL) {
+ goto failed;
+ }
+ }
+
+ if (osd->dacl) {
+ nsd->dacl = security_acl_dup(nsd, osd->dacl);
+ if (nsd->dacl == NULL) {
+ goto failed;
+ }
+ }
+
+ nsd->revision = osd->revision;
+ nsd->type = osd->type;
+
+ return nsd;
+
+ failed:
+ talloc_free(nsd);
+
+ return NULL;
+}
+
+/*
+ add an ACE to an ACL of a security_descriptor
+*/
+
+static NTSTATUS security_descriptor_acl_add(struct security_descriptor *sd,
+ bool add_to_sacl,
+ const struct security_ace *ace)
+{
+ struct security_acl *acl = NULL;
+
+ if (add_to_sacl) {
+ acl = sd->sacl;
+ } else {
+ acl = sd->dacl;
+ }
+
+ if (acl == NULL) {
+ acl = talloc(sd, struct security_acl);
+ if (acl == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ acl->revision = SECURITY_ACL_REVISION_NT4;
+ acl->size = 0;
+ acl->num_aces = 0;
+ acl->aces = NULL;
+ }
+
+ acl->aces = talloc_realloc(acl, acl->aces,
+ struct security_ace, acl->num_aces+1);
+ if (acl->aces == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ acl->aces[acl->num_aces] = *ace;
+
+ switch (acl->aces[acl->num_aces].type) {
+ case SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT:
+ case SEC_ACE_TYPE_ACCESS_DENIED_OBJECT:
+ case SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT:
+ case SEC_ACE_TYPE_SYSTEM_ALARM_OBJECT:
+ acl->revision = SECURITY_ACL_REVISION_ADS;
+ break;
+ default:
+ break;
+ }
+
+ acl->num_aces++;
+
+ if (add_to_sacl) {
+ sd->sacl = acl;
+ sd->type |= SEC_DESC_SACL_PRESENT;
+ } else {
+ sd->dacl = acl;
+ sd->type |= SEC_DESC_DACL_PRESENT;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ add an ACE to the SACL of a security_descriptor
+*/
+
+NTSTATUS security_descriptor_sacl_add(struct security_descriptor *sd,
+ const struct security_ace *ace)
+{
+ return security_descriptor_acl_add(sd, true, ace);
+}
+
+/*
+ add an ACE to the DACL of a security_descriptor
+*/
+
+NTSTATUS security_descriptor_dacl_add(struct security_descriptor *sd,
+ const struct security_ace *ace)
+{
+ return security_descriptor_acl_add(sd, false, ace);
+}
+
+/*
+ delete the ACE corresponding to the given trustee in an ACL of a
+ security_descriptor
+*/
+
+static NTSTATUS security_descriptor_acl_del(struct security_descriptor *sd,
+ bool sacl_del,
+ const struct dom_sid *trustee)
+{
+ int i;
+ bool found = false;
+ struct security_acl *acl = NULL;
+
+ if (sacl_del) {
+ acl = sd->sacl;
+ } else {
+ acl = sd->dacl;
+ }
+
+ if (acl == NULL) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ /* there can be multiple ace's for one trustee */
+ for (i=0;i<acl->num_aces;i++) {
+ if (dom_sid_equal(trustee, &acl->aces[i].trustee)) {
+ memmove(&acl->aces[i], &acl->aces[i+1],
+ sizeof(acl->aces[i]) * (acl->num_aces - (i+1)));
+ acl->num_aces--;
+ if (acl->num_aces == 0) {
+ acl->aces = NULL;
+ }
+ found = true;
+ }
+ }
+
+ if (!found) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ acl->revision = SECURITY_ACL_REVISION_NT4;
+
+ for (i=0;i<acl->num_aces;i++) {
+ switch (acl->aces[i].type) {
+ case SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT:
+ case SEC_ACE_TYPE_ACCESS_DENIED_OBJECT:
+ case SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT:
+ case SEC_ACE_TYPE_SYSTEM_ALARM_OBJECT:
+ acl->revision = SECURITY_ACL_REVISION_ADS;
+ return NT_STATUS_OK;
+ default:
+ break; /* only for the switch statement */
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ delete the ACE corresponding to the given trustee in the DACL of a
+ security_descriptor
+*/
+
+NTSTATUS security_descriptor_dacl_del(struct security_descriptor *sd,
+ const struct dom_sid *trustee)
+{
+ return security_descriptor_acl_del(sd, false, trustee);
+}
+
+/*
+ delete the ACE corresponding to the given trustee in the SACL of a
+ security_descriptor
+*/
+
+NTSTATUS security_descriptor_sacl_del(struct security_descriptor *sd,
+ const struct dom_sid *trustee)
+{
+ return security_descriptor_acl_del(sd, true, trustee);
+}
+
+/*
+ compare two security ace structures
+*/
+bool security_ace_equal(const struct security_ace *ace1,
+ const struct security_ace *ace2)
+{
+ if (ace1 == ace2) return true;
+ if (!ace1 || !ace2) return false;
+ if (ace1->type != ace2->type) return false;
+ if (ace1->flags != ace2->flags) return false;
+ if (ace1->access_mask != ace2->access_mask) return false;
+ if (!dom_sid_equal(&ace1->trustee, &ace2->trustee)) return false;
+
+ return true;
+}
+
+
+/*
+ compare two security acl structures
+*/
+bool security_acl_equal(const struct security_acl *acl1,
+ const struct security_acl *acl2)
+{
+ int i;
+
+ if (acl1 == acl2) return true;
+ if (!acl1 || !acl2) return false;
+ if (acl1->revision != acl2->revision) return false;
+ if (acl1->num_aces != acl2->num_aces) return false;
+
+ for (i=0;i<acl1->num_aces;i++) {
+ if (!security_ace_equal(&acl1->aces[i], &acl2->aces[i])) return false;
+ }
+ return true;
+}
+
+/*
+ compare two security descriptors.
+*/
+bool security_descriptor_equal(const struct security_descriptor *sd1,
+ const struct security_descriptor *sd2)
+{
+ if (sd1 == sd2) return true;
+ if (!sd1 || !sd2) return false;
+ if (sd1->revision != sd2->revision) return false;
+ if (sd1->type != sd2->type) return false;
+
+ if (!dom_sid_equal(sd1->owner_sid, sd2->owner_sid)) return false;
+ if (!dom_sid_equal(sd1->group_sid, sd2->group_sid)) return false;
+ if (!security_acl_equal(sd1->sacl, sd2->sacl)) return false;
+ if (!security_acl_equal(sd1->dacl, sd2->dacl)) return false;
+
+ return true;
+}
+
+/*
+ compare two security descriptors, but allow certain (missing) parts
+ to be masked out of the comparison
+*/
+bool security_descriptor_mask_equal(const struct security_descriptor *sd1,
+ const struct security_descriptor *sd2,
+ uint32_t mask)
+{
+ if (sd1 == sd2) return true;
+ if (!sd1 || !sd2) return false;
+ if (sd1->revision != sd2->revision) return false;
+ if ((sd1->type & mask) != (sd2->type & mask)) return false;
+
+ if (!dom_sid_equal(sd1->owner_sid, sd2->owner_sid)) return false;
+ if (!dom_sid_equal(sd1->group_sid, sd2->group_sid)) return false;
+ if ((mask & SEC_DESC_DACL_PRESENT) && !security_acl_equal(sd1->dacl, sd2->dacl)) return false;
+ if ((mask & SEC_DESC_SACL_PRESENT) && !security_acl_equal(sd1->sacl, sd2->sacl)) return false;
+
+ return true;
+}
+
+
+static struct security_descriptor *security_descriptor_appendv(struct security_descriptor *sd,
+ bool add_ace_to_sacl,
+ va_list ap)
+{
+ const char *sidstr;
+
+ while ((sidstr = va_arg(ap, const char *))) {
+ struct dom_sid *sid;
+ struct security_ace *ace = talloc(sd, struct security_ace);
+ NTSTATUS status;
+
+ if (ace == NULL) {
+ talloc_free(sd);
+ return NULL;
+ }
+ ace->type = va_arg(ap, unsigned int);
+ ace->access_mask = va_arg(ap, unsigned int);
+ ace->flags = va_arg(ap, unsigned int);
+ sid = dom_sid_parse_talloc(ace, sidstr);
+ if (sid == NULL) {
+ talloc_free(sd);
+ return NULL;
+ }
+ ace->trustee = *sid;
+ if (add_ace_to_sacl) {
+ status = security_descriptor_sacl_add(sd, ace);
+ } else {
+ status = security_descriptor_dacl_add(sd, ace);
+ }
+ /* TODO: check: would talloc_free(ace) here be correct? */
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(sd);
+ return NULL;
+ }
+ }
+
+ return sd;
+}
+
+struct security_descriptor *security_descriptor_append(struct security_descriptor *sd,
+ ...)
+{
+ va_list ap;
+
+ va_start(ap, sd);
+ sd = security_descriptor_appendv(sd, false, ap);
+ va_end(ap);
+
+ return sd;
+}
+
+static struct security_descriptor *security_descriptor_createv(TALLOC_CTX *mem_ctx,
+ uint16_t sd_type,
+ const char *owner_sid,
+ const char *group_sid,
+ bool add_ace_to_sacl,
+ va_list ap)
+{
+ struct security_descriptor *sd;
+
+ sd = security_descriptor_initialise(mem_ctx);
+ if (sd == NULL) {
+ return NULL;
+ }
+
+ sd->type |= sd_type;
+
+ if (owner_sid) {
+ sd->owner_sid = dom_sid_parse_talloc(sd, owner_sid);
+ if (sd->owner_sid == NULL) {
+ talloc_free(sd);
+ return NULL;
+ }
+ }
+ if (group_sid) {
+ sd->group_sid = dom_sid_parse_talloc(sd, group_sid);
+ if (sd->group_sid == NULL) {
+ talloc_free(sd);
+ return NULL;
+ }
+ }
+
+ return security_descriptor_appendv(sd, add_ace_to_sacl, ap);
+}
+
+/*
+ create a security descriptor using string SIDs. This is used by the
+ torture code to allow the easy creation of complex ACLs
+ This is a varargs function. The list of DACL ACEs ends with a NULL sid.
+
+ Each ACE contains a set of 4 parameters:
+ SID, ACCESS_TYPE, MASK, FLAGS
+
+ a typical call would be:
+
+ sd = security_descriptor_dacl_create(mem_ctx,
+ sd_type_flags,
+ mysid,
+ mygroup,
+ SID_NT_AUTHENTICATED_USERS,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ SEC_FILE_ALL,
+ SEC_ACE_FLAG_OBJECT_INHERIT,
+ NULL);
+ that would create a sd with one DACL ACE
+*/
+
+struct security_descriptor *security_descriptor_dacl_create(TALLOC_CTX *mem_ctx,
+ uint16_t sd_type,
+ const char *owner_sid,
+ const char *group_sid,
+ ...)
+{
+ struct security_descriptor *sd = NULL;
+ va_list ap;
+ va_start(ap, group_sid);
+ sd = security_descriptor_createv(mem_ctx, sd_type, owner_sid,
+ group_sid, false, ap);
+ va_end(ap);
+
+ return sd;
+}
+
+struct security_descriptor *security_descriptor_sacl_create(TALLOC_CTX *mem_ctx,
+ uint16_t sd_type,
+ const char *owner_sid,
+ const char *group_sid,
+ ...)
+{
+ struct security_descriptor *sd = NULL;
+ va_list ap;
+ va_start(ap, group_sid);
+ sd = security_descriptor_createv(mem_ctx, sd_type, owner_sid,
+ group_sid, true, ap);
+ va_end(ap);
+
+ return sd;
+}
+
+struct security_ace *security_ace_create(TALLOC_CTX *mem_ctx,
+ const char *sid_str,
+ enum security_ace_type type,
+ uint32_t access_mask,
+ uint8_t flags)
+
+{
+ struct dom_sid *sid;
+ struct security_ace *ace;
+
+ ace = talloc_zero(mem_ctx, struct security_ace);
+ if (ace == NULL) {
+ return NULL;
+ }
+
+ sid = dom_sid_parse_talloc(ace, sid_str);
+ if (sid == NULL) {
+ talloc_free(ace);
+ return NULL;
+ }
+
+ ace->trustee = *sid;
+ ace->type = type;
+ ace->access_mask = access_mask;
+ ace->flags = flags;
+
+ return ace;
+}
diff --git a/source4/libcli/security/security_token.c b/source4/libcli/security/security_token.c
new file mode 100644
index 0000000000..e1349e06f8
--- /dev/null
+++ b/source4/libcli/security/security_token.c
@@ -0,0 +1,169 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ security descriptror utility functions
+
+ Copyright (C) Andrew Tridgell 2004
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/security/security.h"
+#include "auth/session.h"
+
+/*
+ return a blank security token
+*/
+struct security_token *security_token_initialise(TALLOC_CTX *mem_ctx)
+{
+ struct security_token *st;
+
+ st = talloc(mem_ctx, struct security_token);
+ if (!st) {
+ return NULL;
+ }
+
+ st->user_sid = NULL;
+ st->group_sid = NULL;
+ st->num_sids = 0;
+ st->sids = NULL;
+ st->privilege_mask = 0;
+
+ return st;
+}
+
+/****************************************************************************
+ prints a struct security_token to debug output.
+****************************************************************************/
+void security_token_debug(int dbg_lev, const struct security_token *token)
+{
+ TALLOC_CTX *mem_ctx;
+ int i;
+
+ if (!token) {
+ DEBUG(dbg_lev, ("Security token: (NULL)\n"));
+ return;
+ }
+
+ mem_ctx = talloc_init("security_token_debug()");
+ if (!mem_ctx) {
+ return;
+ }
+
+ DEBUG(dbg_lev, ("Security token of user %s\n",
+ dom_sid_string(mem_ctx, token->user_sid) ));
+ DEBUGADD(dbg_lev, (" SIDs (%lu):\n",
+ (unsigned long)token->num_sids));
+ for (i = 0; i < token->num_sids; i++) {
+ DEBUGADD(dbg_lev, (" SID[%3lu]: %s\n", (unsigned long)i,
+ dom_sid_string(mem_ctx, token->sids[i])));
+ }
+
+ security_token_debug_privileges(dbg_lev, token);
+
+ talloc_free(mem_ctx);
+}
+
+/* These really should be cheaper... */
+
+bool security_token_is_sid(const struct security_token *token, const struct dom_sid *sid)
+{
+ if (dom_sid_equal(token->user_sid, sid)) {
+ return true;
+ }
+ return false;
+}
+
+bool security_token_is_sid_string(const struct security_token *token, const char *sid_string)
+{
+ bool ret;
+ struct dom_sid *sid = dom_sid_parse_talloc(NULL, sid_string);
+ if (!sid) return false;
+
+ ret = security_token_is_sid(token, sid);
+
+ talloc_free(sid);
+ return ret;
+}
+
+bool security_token_is_system(const struct security_token *token)
+{
+ return security_token_is_sid_string(token, SID_NT_SYSTEM);
+}
+
+bool security_token_is_anonymous(const struct security_token *token)
+{
+ return security_token_is_sid_string(token, SID_NT_ANONYMOUS);
+}
+
+bool security_token_has_sid(const struct security_token *token, const struct dom_sid *sid)
+{
+ int i;
+ for (i = 0; i < token->num_sids; i++) {
+ if (dom_sid_equal(token->sids[i], sid)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool security_token_has_sid_string(const struct security_token *token, const char *sid_string)
+{
+ bool ret;
+ struct dom_sid *sid = dom_sid_parse_talloc(NULL, sid_string);
+ if (!sid) return false;
+
+ ret = security_token_has_sid(token, sid);
+
+ talloc_free(sid);
+ return ret;
+}
+
+bool security_token_has_builtin_administrators(const struct security_token *token)
+{
+ return security_token_has_sid_string(token, SID_BUILTIN_ADMINISTRATORS);
+}
+
+bool security_token_has_nt_authenticated_users(const struct security_token *token)
+{
+ return security_token_has_sid_string(token, SID_NT_AUTHENTICATED_USERS);
+}
+
+enum security_user_level security_session_user_level(struct auth_session_info *session_info)
+{
+ if (!session_info) {
+ return SECURITY_ANONYMOUS;
+ }
+
+ if (security_token_is_system(session_info->security_token)) {
+ return SECURITY_SYSTEM;
+ }
+
+ if (security_token_is_anonymous(session_info->security_token)) {
+ return SECURITY_ANONYMOUS;
+ }
+
+ if (security_token_has_builtin_administrators(session_info->security_token)) {
+ return SECURITY_ADMINISTRATOR;
+ }
+
+ if (security_token_has_nt_authenticated_users(session_info->security_token)) {
+ return SECURITY_USER;
+ }
+
+ return SECURITY_ANONYMOUS;
+}
+
diff --git a/source4/libcli/security/tests/bindings.py b/source4/libcli/security/tests/bindings.py
new file mode 100644
index 0000000000..24ee01c37f
--- /dev/null
+++ b/source4/libcli/security/tests/bindings.py
@@ -0,0 +1,97 @@
+#!/usr/bin/python
+
+# Unix SMB/CIFS implementation.
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 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 <http://www.gnu.org/licenses/>.
+#
+
+import unittest
+from samba.dcerpc import security
+
+class SecurityTokenTests(unittest.TestCase):
+ def setUp(self):
+ self.token = security.token()
+
+ def test_is_system(self):
+ self.assertFalse(self.token.is_system())
+
+ def test_is_anonymous(self):
+ self.assertFalse(self.token.is_anonymous())
+
+ def test_has_builtin_administrators(self):
+ self.assertFalse(self.token.has_builtin_administrators())
+
+ def test_has_nt_authenticated_users(self):
+ self.assertFalse(self.token.has_nt_authenticated_users())
+
+ def test_has_priv(self):
+ self.assertFalse(self.token.has_privilege(security.SEC_PRIV_SHUTDOWN))
+
+ def test_set_priv(self):
+ self.assertFalse(self.token.has_privilege(security.SEC_PRIV_SHUTDOWN))
+ self.assertFalse(self.token.set_privilege(security.SEC_PRIV_SHUTDOWN))
+ self.assertTrue(self.token.has_privilege(security.SEC_PRIV_SHUTDOWN))
+
+
+class SecurityDescriptorTests(unittest.TestCase):
+ def setUp(self):
+ self.descriptor = security.descriptor()
+
+ def test_from_sddl(self):
+ desc = security.descriptor.from_sddl("O:AOG:DAD:(A;;RPWPCCDCLCSWRCWDWOGA;;;S-1-0-0)", security.dom_sid("S-2-0-0"))
+ self.assertEquals(desc.group_sid, security.dom_sid('S-2-0-0-512'))
+ self.assertEquals(desc.owner_sid, security.dom_sid('S-1-5-32-548'))
+ self.assertEquals(desc.revision, 1)
+ self.assertEquals(desc.sacl, None)
+ self.assertEquals(desc.type, 0x8004)
+
+ def test_as_sddl(self):
+ text = "O:AOG:DAD:(A;;RPWPCCDCLCSWRCWDWOGA;;;S-1-0-0)"
+ dom = security.dom_sid("S-2-0-0")
+ desc1 = security.descriptor.from_sddl(text, dom)
+ desc2 = security.descriptor.from_sddl(desc1.as_sddl(dom), dom)
+ self.assertEquals(desc1.group_sid, desc2.group_sid)
+ self.assertEquals(desc1.owner_sid, desc2.owner_sid)
+ self.assertEquals(desc1.sacl, desc2.sacl)
+ self.assertEquals(desc1.type, desc2.type)
+
+
+class DomSidTests(unittest.TestCase):
+ def test_parse_sid(self):
+ sid = security.dom_sid("S-1-5-21")
+ self.assertEquals("S-1-5-21", str(sid))
+
+ def test_sid_equal(self):
+ sid1 = security.dom_sid("S-1-5-21")
+ sid2 = security.dom_sid("S-1-5-21")
+ self.assertEquals(sid1, sid1)
+ self.assertEquals(sid1, sid2)
+
+ def test_random(self):
+ sid = security.random_sid()
+ self.assertTrue(str(sid).startswith("S-1-5-21-"))
+
+ def test_repr(self):
+ sid = security.random_sid()
+ self.assertTrue(repr(sid).startswith("dom_sid('S-1-5-21-"))
+
+
+class PrivilegeTests(unittest.TestCase):
+ def test_privilege_name(self):
+ self.assertEquals("SeShutdownPrivilege", security.privilege_name(security.SEC_PRIV_SHUTDOWN))
+
+ def test_privilege_id(self):
+ self.assertEquals(security.SEC_PRIV_SHUTDOWN, security.privilege_id("SeShutdownPrivilege"))
+
diff --git a/source4/libcli/security/tests/sddl.c b/source4/libcli/security/tests/sddl.c
new file mode 100644
index 0000000000..9e7705ea92
--- /dev/null
+++ b/source4/libcli/security/tests/sddl.c
@@ -0,0 +1,105 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ local testing of SDDL parsing
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/security/security.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/ndr_security.h"
+
+
+/*
+ test one SDDL example
+*/
+static bool test_sddl(struct torture_context *tctx,
+ const void *test_data)
+{
+ struct security_descriptor *sd, *sd2;
+ struct dom_sid *domain;
+ const char *sddl = (const char *)test_data;
+ const char *sddl2;
+ TALLOC_CTX *mem_ctx = tctx;
+
+
+ domain = dom_sid_parse_talloc(mem_ctx, "S-1-2-3-4");
+ sd = sddl_decode(mem_ctx, sddl, domain);
+ torture_assert(tctx, sd != NULL, talloc_asprintf(tctx,
+ "Failed to decode '%s'\n", sddl));
+
+ sddl2 = sddl_encode(mem_ctx, sd, domain);
+ torture_assert(tctx, sddl2 != NULL, talloc_asprintf(tctx,
+ "Failed to re-encode '%s'\n", sddl));
+
+ sd2 = sddl_decode(mem_ctx, sddl2, domain);
+ torture_assert(tctx, sd2 != NULL, talloc_asprintf(tctx,
+ "Failed to decode2 '%s'\n", sddl2));
+
+ torture_assert(tctx, security_descriptor_equal(sd, sd2),
+ talloc_asprintf(tctx, "Failed equality test for '%s'\n", sddl));
+
+#if 0
+ /* flags don't have a canonical order ... */
+ if (strcmp(sddl, sddl2) != 0) {
+ printf("Failed sddl equality test\norig: %s\n new: %s\n", sddl, sddl2);
+ }
+#endif
+
+ if (DEBUGLVL(2)) {
+ NDR_PRINT_DEBUG(security_descriptor, sd);
+ }
+ talloc_free(sd);
+ talloc_free(domain);
+ return true;
+}
+
+static const char *examples[] = {
+ "D:(A;;CC;;;BA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)",
+ "D:(A;;GA;;;SY)",
+ "D:(A;;RP;;;WD)(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)(A;;RPLCLORC;;;AU)(A;;RPWPCRLCLOCCRCWDWOSW;;;DA)(A;CI;RPWPCRLCLOCCRCWDWOSDSW;;;BA)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;SY)(A;CI;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;EA)(A;CI;LC;;;RU)(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)(A;;RPRC;;;RU)(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(A;;LCRPLORC;;;ED)(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)(OA;CIIO;RPLCLORC;;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;S-1-5-32-557)(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)S:(AU;SA;WDWOWP;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)",
+ "D:(A;;RPLCLORC;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)",
+ "D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;AO)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPCRLCLORCSDDT;;;CO)(OA;;WP;4c164200-20c0-11d0-a768-00aa006e0529;;CO)(A;;RPLCLORC;;;AU)(OA;;CR;ab721a53-1e2f-11d0-9819-00aa0040529b;;WD)(A;;CCDC;;;PS)(OA;;CCDC;bf967aa8-0de6-11d0-a285-00aa003049e2;;PO)(OA;;RPWP;bf967a7f-0de6-11d0-a285-00aa003049e2;;CA)(OA;;SW;f3a64788-5306-11d1-a9c5-0000f80367c1;;PS)(OA;;RPWP;77B5B886-944A-11d1-AEBD-0000F80367C1;;PS)(OA;;SW;72e39547-7b18-11d1-adef-00c04fd8d5cd;;PS)(OA;;SW;72e39547-7b18-11d1-adef-00c04fd8d5cd;;CO)(OA;;SW;f3a64788-5306-11d1-a9c5-0000f80367c1;;CO)(OA;;WP;3e0abfd0-126a-11d0-a060-00aa006c33ed;bf967a86-0de6-11d0-a285-00aa003049e2;CO)(OA;;WP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967a86-0de6-11d0-a285-00aa003049e2;CO)(OA;;WP;bf967950-0de6-11d0-a285-00aa003049e2;bf967a86-0de6-11d0-a285-00aa003049e2;CO)(OA;;WP;bf967953-0de6-11d0-a285-00aa003049e2;bf967a86-0de6-11d0-a285-00aa003049e2;CO)(OA;;RP;46a9b11d-60ae-405a-b7e8-ff8a58d456d2;;S-1-5-32-560)",
+ "D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)",
+ "D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;AO)(A;;RPLCLORC;;;PS)(OA;;CR;ab721a55-1e2f-11d0-9819-00aa0040529b;;AU)(OA;;RP;46a9b11d-60ae-405a-b7e8-ff8a58d456d2;;S-1-5-32-560)",
+ "D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;CO)",
+ "D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)S:(AU;SA;CRWP;;;WD)",
+ "D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;AO)(A;;RPLCLORC;;;PS)(OA;;CR;ab721a53-1e2f-11d0-9819-00aa0040529b;;PS)(OA;;CR;ab721a54-1e2f-11d0-9819-00aa0040529b;;PS)(OA;;CR;ab721a56-1e2f-11d0-9819-00aa0040529b;;PS)(OA;;RPWP;77B5B886-944A-11d1-AEBD-0000F80367C1;;PS)(OA;;RPWP;E45795B2-9455-11d1-AEBD-0000F80367C1;;PS)(OA;;RPWP;E45795B3-9455-11d1-AEBD-0000F80367C1;;PS)(OA;;RP;037088f8-0ae1-11d2-b422-00a0c968f939;;RS)(OA;;RP;4c164200-20c0-11d0-a768-00aa006e0529;;RS)(OA;;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;;RS)(A;;RC;;;AU)(OA;;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;;AU)(OA;;RP;77B5B886-944A-11d1-AEBD-0000F80367C1;;AU)(OA;;RP;E45795B3-9455-11d1-AEBD-0000F80367C1;;AU)(OA;;RP;e48d0154-bcf8-11d1-8702-00c04fb96050;;AU)(OA;;CR;ab721a53-1e2f-11d0-9819-00aa0040529b;;WD)(OA;;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;;RS)(OA;;RPWP;bf967a7f-0de6-11d0-a285-00aa003049e2;;CA)(OA;;RP;46a9b11d-60ae-405a-b7e8-ff8a58d456d2;;S-1-5-32-560)(OA;;WPRP;6db69a1c-9422-11d1-aebd-0000f80367c1;;S-1-5-32-561)",
+ "D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)",
+ "D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)(A;;LCRPLORC;;;ED)",
+ "D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(OA;;CCDC;bf967a86-0de6-11d0-a285-00aa003049e2;;AO)(OA;;CCDC;bf967aba-0de6-11d0-a285-00aa003049e2;;AO)(OA;;CCDC;bf967a9c-0de6-11d0-a285-00aa003049e2;;AO)(OA;;CCDC;bf967aa8-0de6-11d0-a285-00aa003049e2;;PO)(A;;RPLCLORC;;;AU)(A;;LCRPLORC;;;ED)(OA;;CCDC;4828CC14-1437-45bc-9B07-AD6F015E5F28;;AO)",
+ "D:(A;;RPWPCRCCDCLCLORCWOWDSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)",
+ "D:(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)",
+ "D:S:",
+ "D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)"
+};
+
+/* test a set of example SDDL strings */
+struct torture_suite *torture_local_sddl(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "SDDL");
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(examples); i++) {
+ torture_suite_add_simple_tcase_const(suite,
+ talloc_asprintf(suite, "%d", i),
+ test_sddl, examples[i]);
+ }
+
+ return suite;
+}
diff --git a/source4/libcli/smb2/break.c b/source4/libcli/smb2/break.c
new file mode 100644
index 0000000000..fe0cceb829
--- /dev/null
+++ b/source4/libcli/smb2/break.c
@@ -0,0 +1,74 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client oplock break handling
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/*
+ send a break request
+*/
+struct smb2_request *smb2_break_send(struct smb2_tree *tree, struct smb2_break *io)
+{
+ struct smb2_request *req;
+
+ req = smb2_request_init_tree(tree, SMB2_OP_BREAK, 0x18, false, 0);
+ if (req == NULL) return NULL;
+
+ SCVAL(req->out.body, 0x02, io->in.oplock_level);
+ SCVAL(req->out.body, 0x03, io->in.reserved);
+ SIVAL(req->out.body, 0x04, io->in.reserved2);
+ smb2_push_handle(req->out.body+0x08, &io->in.file.handle);
+
+ smb2_transport_send(req);
+
+ return req;
+}
+
+
+/*
+ recv a break reply
+*/
+NTSTATUS smb2_break_recv(struct smb2_request *req, struct smb2_break *io)
+{
+ if (!smb2_request_receive(req) ||
+ !smb2_request_is_ok(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x18, false);
+
+ io->out.oplock_level = CVAL(req->in.body, 0x02);
+ io->out.reserved = CVAL(req->in.body, 0x03);
+ io->out.reserved2 = IVAL(req->in.body, 0x04);
+ smb2_pull_handle(req->in.body+0x08, &io->out.file.handle);
+
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync flush request
+*/
+NTSTATUS smb2_break(struct smb2_tree *tree, struct smb2_break *io)
+{
+ struct smb2_request *req = smb2_break_send(tree, io);
+ return smb2_break_recv(req, io);
+}
diff --git a/source4/libcli/smb2/cancel.c b/source4/libcli/smb2/cancel.c
new file mode 100644
index 0000000000..65f02187c1
--- /dev/null
+++ b/source4/libcli/smb2/cancel.c
@@ -0,0 +1,77 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client notify calls
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/*
+ send a cancel request
+*/
+NTSTATUS smb2_cancel(struct smb2_request *r)
+{
+ NTSTATUS status;
+ struct smb2_request *c;
+ uint32_t old_timeout;
+ uint64_t old_seqnum;
+
+ /*
+ * if we don't get a pending id yet, we just
+ * mark the request for pending, so that we directly
+ * send the cancel after getting the pending id
+ */
+ if (!r->cancel.can_cancel) {
+ r->cancel.do_cancel++;
+ return NT_STATUS_OK;
+ }
+
+ /* we don't want a seqmun for a SMB2 Cancel */
+ old_seqnum = r->transport->seqnum;
+ c = smb2_request_init(r->transport, SMB2_OP_CANCEL, 0x04, false, 0);
+ r->transport->seqnum = old_seqnum;
+ NT_STATUS_HAVE_NO_MEMORY(c);
+ c->seqnum = 0;
+
+ SIVAL(c->out.hdr, SMB2_HDR_FLAGS, 0x00000002);
+ SSVAL(c->out.hdr, SMB2_HDR_CREDIT, 0x0030);
+ SIVAL(c->out.hdr, SMB2_HDR_PID, r->cancel.pending_id);
+ SBVAL(c->out.hdr, SMB2_HDR_MESSAGE_ID, c->seqnum);
+ if (r->session) {
+ SBVAL(c->out.hdr, SMB2_HDR_SESSION_ID, r->session->uid);
+ }
+
+ SSVAL(c->out.body, 0x02, 0);
+
+ old_timeout = c->transport->options.request_timeout;
+ c->transport->options.request_timeout = 0;
+ smb2_transport_send(c);
+ c->transport->options.request_timeout = old_timeout;
+
+ if (c->state == SMB2_REQUEST_ERROR) {
+ status = c->status;
+ } else {
+ status = NT_STATUS_OK;
+ }
+
+ talloc_free(c);
+ return status;
+}
diff --git a/source4/libcli/smb2/close.c b/source4/libcli/smb2/close.c
new file mode 100644
index 0000000000..4e6f33095f
--- /dev/null
+++ b/source4/libcli/smb2/close.c
@@ -0,0 +1,80 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client close handling
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/*
+ send a close request
+*/
+struct smb2_request *smb2_close_send(struct smb2_tree *tree, struct smb2_close *io)
+{
+ struct smb2_request *req;
+
+ req = smb2_request_init_tree(tree, SMB2_OP_CLOSE, 0x18, false, 0);
+ if (req == NULL) return NULL;
+
+ SSVAL(req->out.body, 0x02, io->in.flags);
+ SIVAL(req->out.body, 0x04, 0); /* pad */
+ smb2_push_handle(req->out.body+0x08, &io->in.file.handle);
+
+ smb2_transport_send(req);
+
+ return req;
+}
+
+
+/*
+ recv a close reply
+*/
+NTSTATUS smb2_close_recv(struct smb2_request *req, struct smb2_close *io)
+{
+ if (!smb2_request_receive(req) ||
+ !smb2_request_is_ok(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x3C, false);
+
+ io->out.flags = SVAL(req->in.body, 0x02);
+ io->out._pad = IVAL(req->in.body, 0x04);
+ io->out.create_time = smbcli_pull_nttime(req->in.body, 0x08);
+ io->out.access_time = smbcli_pull_nttime(req->in.body, 0x10);
+ io->out.write_time = smbcli_pull_nttime(req->in.body, 0x18);
+ io->out.change_time = smbcli_pull_nttime(req->in.body, 0x20);
+ io->out.alloc_size = BVAL(req->in.body, 0x28);
+ io->out.size = BVAL(req->in.body, 0x30);
+ io->out.file_attr = IVAL(req->in.body, 0x38);
+
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync close request
+*/
+NTSTATUS smb2_close(struct smb2_tree *tree, struct smb2_close *io)
+{
+ struct smb2_request *req = smb2_close_send(tree, io);
+ return smb2_close_recv(req, io);
+}
diff --git a/source4/libcli/smb2/config.mk b/source4/libcli/smb2/config.mk
new file mode 100644
index 0000000000..322bca1416
--- /dev/null
+++ b/source4/libcli/smb2/config.mk
@@ -0,0 +1,10 @@
+[SUBSYSTEM::LIBCLI_SMB2]
+PUBLIC_DEPENDENCIES = LIBCLI_RAW LIBPACKET gensec
+
+LIBCLI_SMB2_OBJ_FILES = $(addprefix $(libclisrcdir)/smb2/, \
+ transport.o request.o negprot.o session.o tcon.o \
+ create.o close.o connect.o getinfo.o write.o read.o \
+ setinfo.o find.o ioctl.o logoff.o tdis.o flush.o \
+ lock.o notify.o cancel.o keepalive.o break.o util.o signing.o)
+
+$(eval $(call proto_header_template,$(libclisrcdir)/smb2/smb2_proto.h,$(LIBCLI_SMB2_OBJ_FILES:.o=.c)))
diff --git a/source4/libcli/smb2/connect.c b/source4/libcli/smb2/connect.c
new file mode 100644
index 0000000000..11bec42737
--- /dev/null
+++ b/source4/libcli/smb2/connect.c
@@ -0,0 +1,311 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 composite connection setup
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "libcli/composite/composite.h"
+#include "libcli/resolve/resolve.h"
+#include "param/param.h"
+
+struct smb2_connect_state {
+ struct cli_credentials *credentials;
+ struct resolve_context *resolve_ctx;
+ const char *host;
+ const char *share;
+ const char **ports;
+ const char *socket_options;
+ struct gensec_settings *gensec_settings;
+ struct smbcli_options options;
+ struct smb2_negprot negprot;
+ struct smb2_tree_connect tcon;
+ struct smb2_session *session;
+ struct smb2_tree *tree;
+};
+
+/*
+ continue after tcon reply
+*/
+static void continue_tcon(struct smb2_request *req)
+{
+ struct composite_context *c = talloc_get_type(req->async.private_data,
+ struct composite_context);
+ struct smb2_connect_state *state = talloc_get_type(c->private_data,
+ struct smb2_connect_state);
+
+ c->status = smb2_tree_connect_recv(req, &state->tcon);
+ if (!composite_is_ok(c)) return;
+
+ state->tree->tid = state->tcon.out.tid;
+
+ composite_done(c);
+}
+
+/*
+ continue after a session setup
+*/
+static void continue_session(struct composite_context *creq)
+{
+ struct composite_context *c = talloc_get_type(creq->async.private_data,
+ struct composite_context);
+ struct smb2_connect_state *state = talloc_get_type(c->private_data,
+ struct smb2_connect_state);
+ struct smb2_request *req;
+
+ c->status = smb2_session_setup_spnego_recv(creq);
+ if (!composite_is_ok(c)) return;
+
+ state->tree = smb2_tree_init(state->session, state, true);
+ if (composite_nomem(state->tree, c)) return;
+
+ state->tcon.in.reserved = 0;
+ state->tcon.in.path = talloc_asprintf(state, "\\\\%s\\%s",
+ state->host, state->share);
+ if (composite_nomem(state->tcon.in.path, c)) return;
+
+ req = smb2_tree_connect_send(state->tree, &state->tcon);
+ if (composite_nomem(req, c)) return;
+
+ req->async.fn = continue_tcon;
+ req->async.private_data = c;
+}
+
+/*
+ continue after negprot reply
+*/
+static void continue_negprot(struct smb2_request *req)
+{
+ struct composite_context *c = talloc_get_type(req->async.private_data,
+ struct composite_context);
+ struct smb2_connect_state *state = talloc_get_type(c->private_data,
+ struct smb2_connect_state);
+ struct smb2_transport *transport = req->transport;
+ struct composite_context *creq;
+
+ c->status = smb2_negprot_recv(req, c, &state->negprot);
+ if (!composite_is_ok(c)) return;
+
+ transport->negotiate.system_time = state->negprot.out.system_time;
+ transport->negotiate.server_start_time = state->negprot.out.server_start_time;
+ transport->negotiate.security_mode = state->negprot.out.security_mode;
+
+ switch (transport->options.signing) {
+ case SMB_SIGNING_OFF:
+ if (transport->negotiate.security_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED) {
+ composite_error(c, NT_STATUS_ACCESS_DENIED);
+ return;
+ }
+ transport->signing_required = false;
+ break;
+ case SMB_SIGNING_SUPPORTED:
+ if (transport->negotiate.security_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED) {
+ transport->signing_required = true;
+ } else {
+ transport->signing_required = false;
+ }
+ break;
+ case SMB_SIGNING_AUTO:
+ if (transport->negotiate.security_mode & SMB2_NEGOTIATE_SIGNING_ENABLED) {
+ transport->signing_required = true;
+ } else {
+ transport->signing_required = false;
+ }
+ break;
+ case SMB_SIGNING_REQUIRED:
+ if (transport->negotiate.security_mode & SMB2_NEGOTIATE_SIGNING_ENABLED) {
+ transport->signing_required = true;
+ } else {
+ composite_error(c, NT_STATUS_ACCESS_DENIED);
+ return;
+ }
+ break;
+ }
+
+ state->session = smb2_session_init(transport, state->gensec_settings, state, true);
+ if (composite_nomem(state->session, c)) return;
+
+ creq = smb2_session_setup_spnego_send(state->session, state->credentials);
+
+ composite_continue(c, creq, continue_session, c);
+}
+
+/*
+ continue after a socket connect completes
+*/
+static void continue_socket(struct composite_context *creq)
+{
+ struct composite_context *c = talloc_get_type(creq->async.private_data,
+ struct composite_context);
+ struct smb2_connect_state *state = talloc_get_type(c->private_data,
+ struct smb2_connect_state);
+ struct smbcli_socket *sock;
+ struct smb2_transport *transport;
+ struct smb2_request *req;
+ uint16_t dialects[2];
+
+ c->status = smbcli_sock_connect_recv(creq, state, &sock);
+ if (!composite_is_ok(c)) return;
+
+ transport = smb2_transport_init(sock, state, &state->options);
+ if (composite_nomem(transport, c)) return;
+
+ ZERO_STRUCT(state->negprot);
+ state->negprot.in.dialect_count = 2;
+ switch (transport->options.signing) {
+ case SMB_SIGNING_OFF:
+ state->negprot.in.security_mode = 0;
+ break;
+ case SMB_SIGNING_SUPPORTED:
+ case SMB_SIGNING_AUTO:
+ state->negprot.in.security_mode = SMB2_NEGOTIATE_SIGNING_ENABLED;
+ break;
+ case SMB_SIGNING_REQUIRED:
+ state->negprot.in.security_mode =
+ SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED;
+ break;
+ }
+ state->negprot.in.capabilities = 0;
+ unix_to_nt_time(&state->negprot.in.start_time, time(NULL));
+ dialects[0] = SMB2_DIALECT_REVISION;
+ dialects[1] = 0;
+ state->negprot.in.dialects = dialects;
+
+ req = smb2_negprot_send(transport, &state->negprot);
+ if (composite_nomem(req, c)) return;
+
+ req->async.fn = continue_negprot;
+ req->async.private_data = c;
+}
+
+
+/*
+ continue after a resolve finishes
+*/
+static void continue_resolve(struct composite_context *creq)
+{
+ struct composite_context *c = talloc_get_type(creq->async.private_data,
+ struct composite_context);
+ struct smb2_connect_state *state = talloc_get_type(c->private_data,
+ struct smb2_connect_state);
+ const char *addr;
+ const char **ports;
+ const char *default_ports[] = { "445", NULL };
+
+ c->status = resolve_name_recv(creq, state, &addr);
+ if (!composite_is_ok(c)) return;
+
+ if (state->ports == NULL) {
+ ports = default_ports;
+ } else {
+ ports = state->ports;
+ }
+
+ creq = smbcli_sock_connect_send(state, addr, ports, state->host, state->resolve_ctx, c->event_ctx, state->socket_options);
+
+ composite_continue(c, creq, continue_socket, c);
+}
+
+/*
+ a composite function that does a full negprot/sesssetup/tcon, returning
+ a connected smb2_tree
+ */
+struct composite_context *smb2_connect_send(TALLOC_CTX *mem_ctx,
+ const char *host,
+ const char **ports,
+ const char *share,
+ struct resolve_context *resolve_ctx,
+ struct cli_credentials *credentials,
+ struct tevent_context *ev,
+ struct smbcli_options *options,
+ const char *socket_options,
+ struct gensec_settings *gensec_settings)
+{
+ struct composite_context *c;
+ struct smb2_connect_state *state;
+ struct nbt_name name;
+ struct composite_context *creq;
+
+ c = composite_create(mem_ctx, ev);
+ if (c == NULL) return NULL;
+
+ state = talloc(c, struct smb2_connect_state);
+ if (composite_nomem(state, c)) return c;
+ c->private_data = state;
+
+ state->credentials = credentials;
+ state->options = *options;
+ state->host = talloc_strdup(c, host);
+ if (composite_nomem(state->host, c)) return c;
+ state->ports = talloc_reference(state, ports);
+ state->share = talloc_strdup(c, share);
+ if (composite_nomem(state->share, c)) return c;
+ state->resolve_ctx = talloc_reference(state, resolve_ctx);
+ state->socket_options = talloc_reference(state, socket_options);
+ state->gensec_settings = talloc_reference(state, gensec_settings);
+
+ ZERO_STRUCT(name);
+ name.name = host;
+
+ creq = resolve_name_send(resolve_ctx, &name, c->event_ctx);
+ composite_continue(c, creq, continue_resolve, c);
+ return c;
+}
+
+/*
+ receive a connect reply
+*/
+NTSTATUS smb2_connect_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
+ struct smb2_tree **tree)
+{
+ NTSTATUS status;
+ struct smb2_connect_state *state = talloc_get_type(c->private_data,
+ struct smb2_connect_state);
+ status = composite_wait(c);
+ if (NT_STATUS_IS_OK(status)) {
+ *tree = talloc_steal(mem_ctx, state->tree);
+ }
+ talloc_free(c);
+ return status;
+}
+
+/*
+ sync version of smb2_connect
+*/
+NTSTATUS smb2_connect(TALLOC_CTX *mem_ctx,
+ const char *host, const char **ports,
+ const char *share,
+ struct resolve_context *resolve_ctx,
+ struct cli_credentials *credentials,
+ struct smb2_tree **tree,
+ struct tevent_context *ev,
+ struct smbcli_options *options,
+ const char *socket_options,
+ struct gensec_settings *gensec_settings)
+{
+ struct composite_context *c = smb2_connect_send(mem_ctx, host, ports,
+ share, resolve_ctx,
+ credentials, ev, options,
+ socket_options,
+ gensec_settings);
+ return smb2_connect_recv(c, mem_ctx, tree);
+}
diff --git a/source4/libcli/smb2/create.c b/source4/libcli/smb2/create.c
new file mode 100644
index 0000000000..8a40e56a00
--- /dev/null
+++ b/source4/libcli/smb2/create.c
@@ -0,0 +1,419 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client tree handling
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "librpc/gen_ndr/ndr_security.h"
+
+
+/*
+ parse a set of SMB2 create blobs
+*/
+NTSTATUS smb2_create_blob_parse(TALLOC_CTX *mem_ctx, const DATA_BLOB buffer,
+ struct smb2_create_blobs *blobs)
+{
+ const uint8_t *data = buffer.data;
+ uint32_t remaining = buffer.length;
+
+ while (remaining > 0) {
+ uint32_t next;
+ uint32_t name_offset, name_length;
+ uint32_t reserved, data_offset;
+ uint32_t data_length;
+ char *tag;
+ DATA_BLOB b;
+ NTSTATUS status;
+
+ if (remaining < 16) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ next = IVAL(data, 0);
+ name_offset = SVAL(data, 4);
+ name_length = SVAL(data, 6);
+ reserved = SVAL(data, 8);
+ data_offset = SVAL(data, 10);
+ data_length = IVAL(data, 12);
+
+ if ((next & 0x7) != 0 ||
+ next > remaining ||
+ name_offset < 16 ||
+ name_offset > remaining ||
+ name_length != 4 || /* windows enforces this */
+ name_offset + name_length > remaining ||
+ data_offset < name_offset + name_length ||
+ data_offset > remaining ||
+ data_offset + (uint64_t)data_length > remaining) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ tag = talloc_strndup(mem_ctx, (const char *)data + name_offset, name_length);
+ if (tag == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ b = data_blob_const(data+data_offset, data_length);
+ status = smb2_create_blob_add(mem_ctx, blobs, tag, b);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ talloc_free(tag);
+
+ if (next == 0) break;
+
+ remaining -= next;
+ data += next;
+
+ if (remaining < 16) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ add a blob to a smb2_create attribute blob
+*/
+static NTSTATUS smb2_create_blob_push_one(TALLOC_CTX *mem_ctx, DATA_BLOB *buffer,
+ const struct smb2_create_blob *blob,
+ bool last)
+{
+ uint32_t ofs = buffer->length;
+ size_t tag_length = strlen(blob->tag);
+ uint8_t pad = smb2_padding_size(blob->data.length+tag_length, 4);
+
+ if (!data_blob_realloc(mem_ctx, buffer,
+ buffer->length + 0x14 + tag_length + blob->data.length + pad))
+ return NT_STATUS_NO_MEMORY;
+
+ if (last) {
+ SIVAL(buffer->data, ofs+0x00, 0);
+ } else {
+ SIVAL(buffer->data, ofs+0x00, 0x14 + tag_length + blob->data.length + pad);
+ }
+ SSVAL(buffer->data, ofs+0x04, 0x10); /* offset of tag */
+ SIVAL(buffer->data, ofs+0x06, tag_length); /* tag length */
+ SSVAL(buffer->data, ofs+0x0A, 0x14 + tag_length); /* offset of data */
+ SIVAL(buffer->data, ofs+0x0C, blob->data.length);
+ memcpy(buffer->data+ofs+0x10, blob->tag, tag_length);
+ SIVAL(buffer->data, ofs+0x10+tag_length, 0); /* pad? */
+ memcpy(buffer->data+ofs+0x14+tag_length, blob->data.data, blob->data.length);
+ memset(buffer->data+ofs+0x14+tag_length+blob->data.length, 0, pad);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ create a buffer of a set of create blobs
+*/
+NTSTATUS smb2_create_blob_push(TALLOC_CTX *mem_ctx, DATA_BLOB *buffer,
+ const struct smb2_create_blobs blobs)
+{
+ int i;
+ NTSTATUS status;
+
+ *buffer = data_blob(NULL, 0);
+ for (i=0; i < blobs.num_blobs; i++) {
+ bool last = false;
+ const struct smb2_create_blob *c;
+
+ if ((i + 1) == blobs.num_blobs) {
+ last = true;
+ }
+
+ c = &blobs.blobs[i];
+ status = smb2_create_blob_push_one(mem_ctx, buffer, c, last);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+ return NT_STATUS_OK;
+}
+
+
+NTSTATUS smb2_create_blob_add(TALLOC_CTX *mem_ctx, struct smb2_create_blobs *b,
+ const char *tag, DATA_BLOB data)
+{
+ struct smb2_create_blob *array;
+
+ array = talloc_realloc(mem_ctx, b->blobs,
+ struct smb2_create_blob,
+ b->num_blobs + 1);
+ NT_STATUS_HAVE_NO_MEMORY(array);
+ b->blobs = array;
+
+ b->blobs[b->num_blobs].tag = talloc_strdup(b->blobs, tag);
+ NT_STATUS_HAVE_NO_MEMORY(b->blobs[b->num_blobs].tag);
+
+ if (data.data) {
+ b->blobs[b->num_blobs].data = data_blob_talloc(b->blobs,
+ data.data,
+ data.length);
+ NT_STATUS_HAVE_NO_MEMORY(b->blobs[b->num_blobs].data.data);
+ } else {
+ b->blobs[b->num_blobs].data = data_blob(NULL, 0);
+ }
+
+ b->num_blobs += 1;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ send a create request
+*/
+struct smb2_request *smb2_create_send(struct smb2_tree *tree, struct smb2_create *io)
+{
+ struct smb2_request *req;
+ NTSTATUS status;
+ DATA_BLOB blob;
+ struct smb2_create_blobs blobs;
+ int i;
+
+ ZERO_STRUCT(blobs);
+
+ req = smb2_request_init_tree(tree, SMB2_OP_CREATE, 0x38, true, 0);
+ if (req == NULL) return NULL;
+
+ SCVAL(req->out.body, 0x02, io->in.security_flags);
+ SCVAL(req->out.body, 0x03, io->in.oplock_level);
+ SIVAL(req->out.body, 0x04, io->in.impersonation_level);
+ SBVAL(req->out.body, 0x08, io->in.create_flags);
+ SBVAL(req->out.body, 0x10, io->in.reserved);
+ SIVAL(req->out.body, 0x18, io->in.desired_access);
+ SIVAL(req->out.body, 0x1C, io->in.file_attributes);
+ SIVAL(req->out.body, 0x20, io->in.share_access);
+ SIVAL(req->out.body, 0x24, io->in.create_disposition);
+ SIVAL(req->out.body, 0x28, io->in.create_options);
+
+ status = smb2_push_o16s16_string(&req->out, 0x2C, io->in.fname);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+
+ /* now add all the optional blobs */
+ if (io->in.eas.num_eas != 0) {
+ DATA_BLOB b = data_blob_talloc(req, NULL,
+ ea_list_size_chained(io->in.eas.num_eas, io->in.eas.eas, 4));
+ ea_put_list_chained(b.data, io->in.eas.num_eas, io->in.eas.eas, 4);
+ status = smb2_create_blob_add(req, &blobs,
+ SMB2_CREATE_TAG_EXTA, b);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+ data_blob_free(&b);
+ }
+
+ /* an empty MxAc tag seems to be used to ask the server to
+ return the maximum access mask allowed on the file */
+ if (io->in.query_maximal_access) {
+ /* TODO: MS-SMB2 2.2.13.2.5 says this can contain a timestamp? What to do
+ with that if it doesn't match? */
+ status = smb2_create_blob_add(req, &blobs,
+ SMB2_CREATE_TAG_MXAC, data_blob(NULL, 0));
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+ }
+
+ if (io->in.alloc_size != 0) {
+ uint8_t data[8];
+ SBVAL(data, 0, io->in.alloc_size);
+ status = smb2_create_blob_add(req, &blobs,
+ SMB2_CREATE_TAG_ALSI, data_blob_const(data, 8));
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+ }
+
+ if (io->in.durable_open) {
+ status = smb2_create_blob_add(req, &blobs,
+ SMB2_CREATE_TAG_DHNQ, data_blob_talloc_zero(req, 16));
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+ }
+
+ if (io->in.durable_handle) {
+ uint8_t data[16];
+ smb2_push_handle(data, io->in.durable_handle);
+ status = smb2_create_blob_add(req, &blobs,
+ SMB2_CREATE_TAG_DHNC, data_blob_const(data, 16));
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+ }
+
+ if (io->in.timewarp) {
+ uint8_t data[8];
+ SBVAL(data, 0, io->in.timewarp);
+ status = smb2_create_blob_add(req, &blobs,
+ SMB2_CREATE_TAG_TWRP, data_blob_const(data, 8));
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+ }
+
+ if (io->in.sec_desc) {
+ enum ndr_err_code ndr_err;
+ DATA_BLOB sd_blob;
+ ndr_err = ndr_push_struct_blob(&sd_blob, req, NULL,
+ io->in.sec_desc,
+ (ndr_push_flags_fn_t)ndr_push_security_descriptor);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ talloc_free(req);
+ return NULL;
+ }
+ status = smb2_create_blob_add(req, &blobs,
+ SMB2_CREATE_TAG_SECD, sd_blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+ }
+
+ if (io->in.query_on_disk_id) {
+ status = smb2_create_blob_add(req, &blobs,
+ SMB2_CREATE_TAG_QFID, data_blob(NULL, 0));
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+ }
+
+ /* and any custom blobs */
+ for (i=0;i<io->in.blobs.num_blobs;i++) {
+ status = smb2_create_blob_add(req, &blobs,
+ io->in.blobs.blobs[i].tag,
+ io->in.blobs.blobs[i].data);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+ }
+
+
+ status = smb2_create_blob_push(req, &blob, blobs);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+
+ status = smb2_push_o32s32_blob(&req->out, 0x30, blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+
+ data_blob_free(&blob);
+
+ smb2_transport_send(req);
+
+ return req;
+}
+
+
+/*
+ recv a create reply
+*/
+NTSTATUS smb2_create_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx, struct smb2_create *io)
+{
+ NTSTATUS status;
+ DATA_BLOB blob;
+ int i;
+
+ if (!smb2_request_receive(req) ||
+ !smb2_request_is_ok(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x58, true);
+ ZERO_STRUCT(io->out);
+ io->out.oplock_level = CVAL(req->in.body, 0x02);
+ io->out.reserved = CVAL(req->in.body, 0x03);
+ io->out.create_action = IVAL(req->in.body, 0x04);
+ io->out.create_time = smbcli_pull_nttime(req->in.body, 0x08);
+ io->out.access_time = smbcli_pull_nttime(req->in.body, 0x10);
+ io->out.write_time = smbcli_pull_nttime(req->in.body, 0x18);
+ io->out.change_time = smbcli_pull_nttime(req->in.body, 0x20);
+ io->out.alloc_size = BVAL(req->in.body, 0x28);
+ io->out.size = BVAL(req->in.body, 0x30);
+ io->out.file_attr = IVAL(req->in.body, 0x38);
+ io->out.reserved2 = IVAL(req->in.body, 0x3C);
+ smb2_pull_handle(req->in.body+0x40, &io->out.file.handle);
+ status = smb2_pull_o32s32_blob(&req->in, mem_ctx, req->in.body+0x50, &blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ smb2_request_destroy(req);
+ return status;
+ }
+
+ status = smb2_create_blob_parse(mem_ctx, blob, &io->out.blobs);
+ if (!NT_STATUS_IS_OK(status)) {
+ smb2_request_destroy(req);
+ return status;
+ }
+
+ /* pull out the parsed blobs */
+ for (i=0;i<io->out.blobs.num_blobs;i++) {
+ if (strcmp(io->out.blobs.blobs[i].tag, SMB2_CREATE_TAG_MXAC) == 0) {
+ /* TODO: this also contains a status field in
+ first 4 bytes */
+ if (io->out.blobs.blobs[i].data.length != 8) {
+ smb2_request_destroy(req);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ io->out.maximal_access = IVAL(io->out.blobs.blobs[i].data.data, 4);
+ }
+ if (strcmp(io->out.blobs.blobs[i].tag, SMB2_CREATE_TAG_QFID) == 0) {
+ if (io->out.blobs.blobs[i].data.length != 32) {
+ smb2_request_destroy(req);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ memcpy(io->out.on_disk_id, io->out.blobs.blobs[i].data.data, 32);
+ }
+ }
+
+ data_blob_free(&blob);
+
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync create request
+*/
+NTSTATUS smb2_create(struct smb2_tree *tree, TALLOC_CTX *mem_ctx, struct smb2_create *io)
+{
+ struct smb2_request *req = smb2_create_send(tree, io);
+ return smb2_create_recv(req, mem_ctx, io);
+}
diff --git a/source4/libcli/smb2/find.c b/source4/libcli/smb2/find.c
new file mode 100644
index 0000000000..8ebfd81bcd
--- /dev/null
+++ b/source4/libcli/smb2/find.c
@@ -0,0 +1,180 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client find calls
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/*
+ send a find request
+*/
+struct smb2_request *smb2_find_send(struct smb2_tree *tree, struct smb2_find *io)
+{
+ struct smb2_request *req;
+ NTSTATUS status;
+
+ req = smb2_request_init_tree(tree, SMB2_OP_FIND, 0x20, true, 0);
+ if (req == NULL) return NULL;
+
+ SCVAL(req->out.body, 0x02, io->in.level);
+ SCVAL(req->out.body, 0x03, io->in.continue_flags);
+ SIVAL(req->out.body, 0x04, io->in.file_index);
+ smb2_push_handle(req->out.body+0x08, &io->in.file.handle);
+
+ status = smb2_push_o16s16_string(&req->out, 0x18, io->in.pattern);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+
+ SIVAL(req->out.body, 0x1C, io->in.max_response_size);
+
+ smb2_transport_send(req);
+
+ return req;
+}
+
+
+/*
+ recv a find reply
+*/
+NTSTATUS smb2_find_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx,
+ struct smb2_find *io)
+{
+ NTSTATUS status;
+
+ if (!smb2_request_receive(req) ||
+ smb2_request_is_error(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x08, true);
+
+ status = smb2_pull_o16s32_blob(&req->in, mem_ctx,
+ req->in.body+0x02, &io->out.blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync find request
+*/
+NTSTATUS smb2_find(struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
+ struct smb2_find *io)
+{
+ struct smb2_request *req = smb2_find_send(tree, io);
+ return smb2_find_recv(req, mem_ctx, io);
+}
+
+
+/*
+ a varient of smb2_find_recv that parses the resulting blob into
+ smb_search_data structures
+*/
+NTSTATUS smb2_find_level_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx,
+ uint8_t level, uint_t *count,
+ union smb_search_data **io)
+{
+ struct smb2_find f;
+ NTSTATUS status;
+ DATA_BLOB b;
+ enum smb_search_data_level smb_level;
+ uint_t next_ofs=0;
+
+ switch (level) {
+ case SMB2_FIND_DIRECTORY_INFO:
+ smb_level = RAW_SEARCH_DATA_DIRECTORY_INFO;
+ break;
+ case SMB2_FIND_FULL_DIRECTORY_INFO:
+ smb_level = RAW_SEARCH_DATA_FULL_DIRECTORY_INFO;
+ break;
+ case SMB2_FIND_BOTH_DIRECTORY_INFO:
+ smb_level = RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO;
+ break;
+ case SMB2_FIND_NAME_INFO:
+ smb_level = RAW_SEARCH_DATA_NAME_INFO;
+ break;
+ case SMB2_FIND_ID_FULL_DIRECTORY_INFO:
+ smb_level = RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO;
+ break;
+ case SMB2_FIND_ID_BOTH_DIRECTORY_INFO:
+ smb_level = RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO;
+ break;
+ default:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ status = smb2_find_recv(req, mem_ctx, &f);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ b = f.out.blob;
+ *io = NULL;
+ *count = 0;
+
+ do {
+ union smb_search_data *io2;
+
+ io2 = talloc_realloc(mem_ctx, *io, union smb_search_data, (*count)+1);
+ if (io2 == NULL) {
+ data_blob_free(&f.out.blob);
+ talloc_free(*io);
+ return NT_STATUS_NO_MEMORY;
+ }
+ *io = io2;
+
+ status = smb_raw_search_common(*io, smb_level, &b, (*io) + (*count),
+ &next_ofs, STR_UNICODE);
+
+ if (NT_STATUS_IS_OK(status) &&
+ next_ofs >= b.length) {
+ data_blob_free(&f.out.blob);
+ talloc_free(*io);
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+
+ (*count)++;
+
+ b = data_blob_const(b.data+next_ofs, b.length - next_ofs);
+ } while (NT_STATUS_IS_OK(status) && next_ofs != 0);
+
+ data_blob_free(&f.out.blob);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ a varient of smb2_find that parses the resulting blob into
+ smb_search_data structures
+*/
+NTSTATUS smb2_find_level(struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
+ struct smb2_find *f,
+ uint_t *count, union smb_search_data **io)
+{
+ struct smb2_request *req;
+
+ req = smb2_find_send(tree, f);
+ return smb2_find_level_recv(req, mem_ctx, f->in.level, count, io);
+}
diff --git a/source4/libcli/smb2/flush.c b/source4/libcli/smb2/flush.c
new file mode 100644
index 0000000000..577d1ba1ba
--- /dev/null
+++ b/source4/libcli/smb2/flush.c
@@ -0,0 +1,70 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client flush handling
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/*
+ send a flush request
+*/
+struct smb2_request *smb2_flush_send(struct smb2_tree *tree, struct smb2_flush *io)
+{
+ struct smb2_request *req;
+
+ req = smb2_request_init_tree(tree, SMB2_OP_FLUSH, 0x18, false, 0);
+ if (req == NULL) return NULL;
+
+ SSVAL(req->out.body, 0x02, io->in.reserved1);
+ SIVAL(req->out.body, 0x04, io->in.reserved2);
+ smb2_push_handle(req->out.body+0x08, &io->in.file.handle);
+
+ smb2_transport_send(req);
+
+ return req;
+}
+
+
+/*
+ recv a flush reply
+*/
+NTSTATUS smb2_flush_recv(struct smb2_request *req, struct smb2_flush *io)
+{
+ if (!smb2_request_receive(req) ||
+ !smb2_request_is_ok(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x04, false);
+
+ io->out.reserved = SVAL(req->in.body, 0x02);
+
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync flush request
+*/
+NTSTATUS smb2_flush(struct smb2_tree *tree, struct smb2_flush *io)
+{
+ struct smb2_request *req = smb2_flush_send(tree, io);
+ return smb2_flush_recv(req, io);
+}
diff --git a/source4/libcli/smb2/getinfo.c b/source4/libcli/smb2/getinfo.c
new file mode 100644
index 0000000000..14d911683e
--- /dev/null
+++ b/source4/libcli/smb2/getinfo.c
@@ -0,0 +1,220 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client getinfo calls
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/*
+ send a getinfo request
+*/
+struct smb2_request *smb2_getinfo_send(struct smb2_tree *tree, struct smb2_getinfo *io)
+{
+ struct smb2_request *req;
+ NTSTATUS status;
+
+ req = smb2_request_init_tree(tree, SMB2_OP_GETINFO, 0x28, true,
+ io->in.blob.length);
+ if (req == NULL) return NULL;
+
+ SCVAL(req->out.body, 0x02, io->in.info_type);
+ SCVAL(req->out.body, 0x03, io->in.info_class);
+ SIVAL(req->out.body, 0x04, io->in.output_buffer_length);
+ SIVAL(req->out.body, 0x0C, io->in.reserved);
+ SIVAL(req->out.body, 0x08, io->in.input_buffer_length);
+ SIVAL(req->out.body, 0x10, io->in.additional_information);
+ SIVAL(req->out.body, 0x14, io->in.getinfo_flags);
+ smb2_push_handle(req->out.body+0x18, &io->in.file.handle);
+
+ /* this blob is used for quota queries */
+ status = smb2_push_o32s32_blob(&req->out, 0x08, io->in.blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+ smb2_transport_send(req);
+
+ return req;
+}
+
+
+/*
+ recv a getinfo reply
+*/
+NTSTATUS smb2_getinfo_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx,
+ struct smb2_getinfo *io)
+{
+ NTSTATUS status;
+
+ if (!smb2_request_receive(req) ||
+ smb2_request_is_error(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x08, true);
+
+ status = smb2_pull_o16s16_blob(&req->in, mem_ctx, req->in.body+0x02, &io->out.blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync getinfo request
+*/
+NTSTATUS smb2_getinfo(struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
+ struct smb2_getinfo *io)
+{
+ struct smb2_request *req = smb2_getinfo_send(tree, io);
+ return smb2_getinfo_recv(req, mem_ctx, io);
+}
+
+
+/*
+ map a generic info level to a SMB2 info level
+*/
+uint16_t smb2_getinfo_map_level(uint16_t level, uint8_t info_class)
+{
+ if (info_class == SMB2_GETINFO_FILE &&
+ level == RAW_FILEINFO_SEC_DESC) {
+ return SMB2_GETINFO_SECURITY;
+ }
+ if ((level & 0xFF) == info_class) {
+ return level;
+ } else if (level > 1000) {
+ return ((level-1000)<<8) | info_class;
+ }
+ DEBUG(0,("Unable to map SMB2 info level 0x%04x of class %d\n",
+ level, info_class));
+ return 0;
+}
+
+/*
+ level specific getinfo call - async send
+*/
+struct smb2_request *smb2_getinfo_file_send(struct smb2_tree *tree, union smb_fileinfo *io)
+{
+ struct smb2_getinfo b;
+ uint16_t smb2_level = smb2_getinfo_map_level(io->generic.level, SMB2_GETINFO_FILE);
+
+ if (smb2_level == 0) {
+ return NULL;
+ }
+
+ ZERO_STRUCT(b);
+ b.in.info_type = smb2_level & 0xFF;
+ b.in.info_class = smb2_level >> 8;
+ b.in.output_buffer_length = 0x10000;
+ b.in.input_buffer_length = 0;
+ b.in.file.handle = io->generic.in.file.handle;
+
+ if (io->generic.level == RAW_FILEINFO_SEC_DESC) {
+ b.in.additional_information = io->query_secdesc.in.secinfo_flags;
+ }
+ if (io->generic.level == RAW_FILEINFO_SMB2_ALL_EAS) {
+ b.in.getinfo_flags = io->all_eas.in.continue_flags;
+ }
+
+ return smb2_getinfo_send(tree, &b);
+}
+
+/*
+ recv a getinfo reply and parse the level info
+*/
+NTSTATUS smb2_getinfo_file_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx,
+ union smb_fileinfo *io)
+{
+ struct smb2_getinfo b;
+ NTSTATUS status;
+
+ status = smb2_getinfo_recv(req, mem_ctx, &b);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ status = smb_raw_fileinfo_passthru_parse(&b.out.blob, mem_ctx, io->generic.level, io);
+ data_blob_free(&b.out.blob);
+
+ return status;
+}
+
+/*
+ level specific getinfo call
+*/
+NTSTATUS smb2_getinfo_file(struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
+ union smb_fileinfo *io)
+{
+ struct smb2_request *req = smb2_getinfo_file_send(tree, io);
+ return smb2_getinfo_file_recv(req, mem_ctx, io);
+}
+
+
+/*
+ level specific getinfo call - async send
+*/
+struct smb2_request *smb2_getinfo_fs_send(struct smb2_tree *tree, union smb_fsinfo *io)
+{
+ struct smb2_getinfo b;
+ uint16_t smb2_level = smb2_getinfo_map_level(io->generic.level, SMB2_GETINFO_FS);
+
+ if (smb2_level == 0) {
+ return NULL;
+ }
+
+ ZERO_STRUCT(b);
+ b.in.output_buffer_length = 0x10000;
+ b.in.file.handle = io->generic.handle;
+ b.in.info_type = smb2_level & 0xFF;
+ b.in.info_class = smb2_level >> 8;
+
+ return smb2_getinfo_send(tree, &b);
+}
+
+/*
+ recv a getinfo reply and parse the level info
+*/
+NTSTATUS smb2_getinfo_fs_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx,
+ union smb_fsinfo *io)
+{
+ struct smb2_getinfo b;
+ NTSTATUS status;
+
+ status = smb2_getinfo_recv(req, mem_ctx, &b);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ status = smb_raw_fsinfo_passthru_parse(b.out.blob, mem_ctx, io->generic.level, io);
+ data_blob_free(&b.out.blob);
+
+ return status;
+}
+
+/*
+ level specific getinfo call
+*/
+NTSTATUS smb2_getinfo_fs(struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
+ union smb_fsinfo *io)
+{
+ struct smb2_request *req = smb2_getinfo_fs_send(tree, io);
+ return smb2_getinfo_fs_recv(req, mem_ctx, io);
+}
+
diff --git a/source4/libcli/smb2/ioctl.c b/source4/libcli/smb2/ioctl.c
new file mode 100644
index 0000000000..d81bca517f
--- /dev/null
+++ b/source4/libcli/smb2/ioctl.c
@@ -0,0 +1,109 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client ioctl call
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/*
+ send a ioctl request
+*/
+struct smb2_request *smb2_ioctl_send(struct smb2_tree *tree, struct smb2_ioctl *io)
+{
+ NTSTATUS status;
+ struct smb2_request *req;
+
+ req = smb2_request_init_tree(tree, SMB2_OP_IOCTL, 0x38, true,
+ io->in.in.length+io->in.out.length);
+ if (req == NULL) return NULL;
+
+ SSVAL(req->out.body, 0x02, 0); /* pad */
+ SIVAL(req->out.body, 0x04, io->in.function);
+ smb2_push_handle(req->out.body+0x08, &io->in.file.handle);
+
+ status = smb2_push_o32s32_blob(&req->out, 0x18, io->in.out);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+
+ SIVAL(req->out.body, 0x20, io->in.unknown2);
+
+ status = smb2_push_o32s32_blob(&req->out, 0x24, io->in.in);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+
+ SIVAL(req->out.body, 0x2C, io->in.max_response_size);
+ SBVAL(req->out.body, 0x30, io->in.flags);
+
+ smb2_transport_send(req);
+
+ return req;
+}
+
+
+/*
+ recv a ioctl reply
+*/
+NTSTATUS smb2_ioctl_recv(struct smb2_request *req,
+ TALLOC_CTX *mem_ctx, struct smb2_ioctl *io)
+{
+ NTSTATUS status;
+
+ if (!smb2_request_receive(req) ||
+ smb2_request_is_error(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x30, true);
+
+ io->out._pad = SVAL(req->in.body, 0x02);
+ io->out.function = IVAL(req->in.body, 0x04);
+ smb2_pull_handle(req->in.body+0x08, &io->out.file.handle);
+
+ status = smb2_pull_o32s32_blob(&req->in, mem_ctx, req->in.body+0x18, &io->out.in);
+ if (!NT_STATUS_IS_OK(status)) {
+ smb2_request_destroy(req);
+ return status;
+ }
+
+ status = smb2_pull_o32s32_blob(&req->in, mem_ctx, req->in.body+0x20, &io->out.out);
+ if (!NT_STATUS_IS_OK(status)) {
+ smb2_request_destroy(req);
+ return status;
+ }
+
+ io->out.unknown2 = IVAL(req->in.body, 0x28);
+ io->out.unknown3 = IVAL(req->in.body, 0x2C);
+
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync ioctl request
+*/
+NTSTATUS smb2_ioctl(struct smb2_tree *tree, TALLOC_CTX *mem_ctx, struct smb2_ioctl *io)
+{
+ struct smb2_request *req = smb2_ioctl_send(tree, io);
+ return smb2_ioctl_recv(req, mem_ctx, io);
+}
diff --git a/source4/libcli/smb2/keepalive.c b/source4/libcli/smb2/keepalive.c
new file mode 100644
index 0000000000..402b063e81
--- /dev/null
+++ b/source4/libcli/smb2/keepalive.c
@@ -0,0 +1,65 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client keepalive handling
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/*
+ send a keepalive request
+*/
+struct smb2_request *smb2_keepalive_send(struct smb2_transport *transport)
+{
+ struct smb2_request *req;
+
+ req = smb2_request_init(transport, SMB2_OP_KEEPALIVE, 0x04, false, 0);
+ if (req == NULL) return NULL;
+
+ SSVAL(req->out.body, 0x02, 0);
+
+ smb2_transport_send(req);
+
+ return req;
+}
+
+
+/*
+ recv a keepalive reply
+*/
+NTSTATUS smb2_keepalive_recv(struct smb2_request *req)
+{
+ if (!smb2_request_receive(req) ||
+ !smb2_request_is_ok(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x04, false);
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync keepalive request
+*/
+NTSTATUS smb2_keepalive(struct smb2_transport *transport)
+{
+ struct smb2_request *req = smb2_keepalive_send(transport);
+ return smb2_keepalive_recv(req);
+}
diff --git a/source4/libcli/smb2/lock.c b/source4/libcli/smb2/lock.c
new file mode 100644
index 0000000000..62c6e5dba7
--- /dev/null
+++ b/source4/libcli/smb2/lock.c
@@ -0,0 +1,82 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client lock handling
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/*
+ send a lock request
+*/
+struct smb2_request *smb2_lock_send(struct smb2_tree *tree, struct smb2_lock *io)
+{
+ struct smb2_request *req;
+ int i;
+
+ req = smb2_request_init_tree(tree, SMB2_OP_LOCK,
+ 24 + io->in.lock_count*24, false, 0);
+ if (req == NULL) return NULL;
+
+ /* this is quite bizarre - the spec says we must lie about the length! */
+ SSVAL(req->out.body, 0, 0x30);
+
+ SSVAL(req->out.body, 0x02, io->in.lock_count);
+ SIVAL(req->out.body, 0x04, io->in.reserved);
+ smb2_push_handle(req->out.body+0x08, &io->in.file.handle);
+
+ for (i=0;i<io->in.lock_count;i++) {
+ SBVAL(req->out.body, 0x18 + i*24, io->in.locks[i].offset);
+ SBVAL(req->out.body, 0x20 + i*24, io->in.locks[i].length);
+ SIVAL(req->out.body, 0x28 + i*24, io->in.locks[i].flags);
+ SIVAL(req->out.body, 0x2C + i*24, io->in.locks[i].reserved);
+ }
+
+ smb2_transport_send(req);
+
+ return req;
+}
+
+
+/*
+ recv a lock reply
+*/
+NTSTATUS smb2_lock_recv(struct smb2_request *req, struct smb2_lock *io)
+{
+ if (!smb2_request_receive(req) ||
+ smb2_request_is_error(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x04, false);
+
+ io->out.reserved = SVAL(req->in.body, 0x02);
+
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync lock request
+*/
+NTSTATUS smb2_lock(struct smb2_tree *tree, struct smb2_lock *io)
+{
+ struct smb2_request *req = smb2_lock_send(tree, io);
+ return smb2_lock_recv(req, io);
+}
diff --git a/source4/libcli/smb2/logoff.c b/source4/libcli/smb2/logoff.c
new file mode 100644
index 0000000000..e3f83f27d8
--- /dev/null
+++ b/source4/libcli/smb2/logoff.c
@@ -0,0 +1,69 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client logoff handling
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/*
+ send a logoff request
+*/
+struct smb2_request *smb2_logoff_send(struct smb2_session *session)
+{
+ struct smb2_request *req;
+
+ req = smb2_request_init(session->transport, SMB2_OP_LOGOFF, 0x04, false, 0);
+ if (req == NULL) return NULL;
+
+ req->session = session;
+
+ SBVAL(req->out.hdr, SMB2_HDR_SESSION_ID, session->uid);
+
+ SSVAL(req->out.body, 0x02, 0);
+
+ smb2_transport_send(req);
+
+ return req;
+}
+
+
+/*
+ recv a logoff reply
+*/
+NTSTATUS smb2_logoff_recv(struct smb2_request *req)
+{
+ if (!smb2_request_receive(req) ||
+ !smb2_request_is_ok(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x04, false);
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync logoff request
+*/
+NTSTATUS smb2_logoff(struct smb2_session *session)
+{
+ struct smb2_request *req = smb2_logoff_send(session);
+ return smb2_logoff_recv(req);
+}
diff --git a/source4/libcli/smb2/negprot.c b/source4/libcli/smb2/negprot.c
new file mode 100644
index 0000000000..c1f0cf0b24
--- /dev/null
+++ b/source4/libcli/smb2/negprot.c
@@ -0,0 +1,113 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client negprot handling
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "librpc/ndr/libndr.h"
+
+/*
+ send a negprot request
+*/
+struct smb2_request *smb2_negprot_send(struct smb2_transport *transport,
+ struct smb2_negprot *io)
+{
+ struct smb2_request *req;
+ uint16_t size = 0x24 + io->in.dialect_count*2;
+ enum ndr_err_code ndr_err;
+ int i;
+
+ req = smb2_request_init(transport, SMB2_OP_NEGPROT, size, false, 0);
+ if (req == NULL) return NULL;
+
+
+ SSVAL(req->out.body, 0x00, 0x24);
+ SSVAL(req->out.body, 0x02, io->in.dialect_count);
+ SSVAL(req->out.body, 0x04, io->in.security_mode);
+ SSVAL(req->out.body, 0x06, io->in.reserved);
+ SIVAL(req->out.body, 0x08, io->in.capabilities);
+ ndr_err = smbcli_push_guid(req->out.body, 0x0C, &io->in.client_guid);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ talloc_free(req);
+ return NULL;
+ }
+ smbcli_push_nttime(req->out.body, 0x1C, io->in.start_time);
+ for (i=0;i<io->in.dialect_count;i++) {
+ SSVAL(req->out.body, 0x24 + i*2, io->in.dialects[i]);
+ }
+
+ smb2_transport_send(req);
+
+ return req;
+}
+
+/*
+ recv a negprot reply
+*/
+NTSTATUS smb2_negprot_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx,
+ struct smb2_negprot *io)
+{
+ NTSTATUS status;
+ enum ndr_err_code ndr_err;
+
+ if (!smb2_request_receive(req) ||
+ smb2_request_is_error(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x40, true);
+
+ io->out.security_mode = SVAL(req->in.body, 0x02);
+ io->out.dialect_revision = SVAL(req->in.body, 0x04);
+ io->out.reserved = SVAL(req->in.body, 0x06);
+ ndr_err = smbcli_pull_guid(req->in.body, 0x08, &io->in.client_guid);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ smb2_request_destroy(req);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ io->out.capabilities = IVAL(req->in.body, 0x18);
+ io->out.max_transact_size = IVAL(req->in.body, 0x1C);
+ io->out.max_read_size = IVAL(req->in.body, 0x20);
+ io->out.max_write_size = IVAL(req->in.body, 0x24);
+ io->out.system_time = smbcli_pull_nttime(req->in.body, 0x28);
+ io->out.server_start_time = smbcli_pull_nttime(req->in.body, 0x30);
+ io->out.reserved2 = IVAL(req->in.body, 0x3C);
+
+ status = smb2_pull_o16s16_blob(&req->in, mem_ctx, req->in.body+0x38, &io->out.secblob);
+ if (!NT_STATUS_IS_OK(status)) {
+ smb2_request_destroy(req);
+ return status;
+ }
+
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync negprot request
+*/
+NTSTATUS smb2_negprot(struct smb2_transport *transport,
+ TALLOC_CTX *mem_ctx, struct smb2_negprot *io)
+{
+ struct smb2_request *req = smb2_negprot_send(transport, io);
+ return smb2_negprot_recv(req, mem_ctx, io);
+}
diff --git a/source4/libcli/smb2/notify.c b/source4/libcli/smb2/notify.c
new file mode 100644
index 0000000000..ef7341cae8
--- /dev/null
+++ b/source4/libcli/smb2/notify.c
@@ -0,0 +1,114 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client notify calls
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/*
+ send a notify request
+*/
+struct smb2_request *smb2_notify_send(struct smb2_tree *tree, struct smb2_notify *io)
+{
+ struct smb2_request *req;
+ uint32_t old_timeout;
+
+ req = smb2_request_init_tree(tree, SMB2_OP_NOTIFY, 0x20, false, 0);
+ if (req == NULL) return NULL;
+
+ SSVAL(req->out.hdr, SMB2_HDR_CREDIT, 0x0030);
+
+ SSVAL(req->out.body, 0x02, io->in.recursive);
+ SIVAL(req->out.body, 0x04, io->in.buffer_size);
+ smb2_push_handle(req->out.body+0x08, &io->in.file.handle);
+ SIVAL(req->out.body, 0x18, io->in.completion_filter);
+ SIVAL(req->out.body, 0x1C, io->in.unknown);
+
+ old_timeout = req->transport->options.request_timeout;
+ req->transport->options.request_timeout = 0;
+ smb2_transport_send(req);
+ req->transport->options.request_timeout = old_timeout;
+
+ return req;
+}
+
+
+/*
+ recv a notify reply
+*/
+NTSTATUS smb2_notify_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx,
+ struct smb2_notify *io)
+{
+ NTSTATUS status;
+ DATA_BLOB blob;
+ uint32_t ofs, i;
+
+ if (!smb2_request_receive(req) ||
+ !smb2_request_is_ok(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x08, true);
+
+ status = smb2_pull_o16s32_blob(&req->in, mem_ctx, req->in.body+0x02, &blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ io->out.changes = NULL;
+ io->out.num_changes = 0;
+
+ /* count them */
+ for (ofs=0; blob.length - ofs > 12; ) {
+ uint32_t next = IVAL(blob.data, ofs);
+ io->out.num_changes++;
+ if (next == 0 || (ofs + next) >= blob.length) break;
+ ofs += next;
+ }
+
+ /* allocate array */
+ io->out.changes = talloc_array(mem_ctx, struct notify_changes, io->out.num_changes);
+ if (!io->out.changes) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=ofs=0; i<io->out.num_changes; i++) {
+ io->out.changes[i].action = IVAL(blob.data, ofs+4);
+ smbcli_blob_pull_string(NULL, mem_ctx, &blob,
+ &io->out.changes[i].name,
+ ofs+8, ofs+12, STR_UNICODE);
+ ofs += IVAL(blob.data, ofs);
+ }
+
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync notify request
+*/
+NTSTATUS smb2_notify(struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
+ struct smb2_notify *io)
+{
+ struct smb2_request *req = smb2_notify_send(tree, io);
+ return smb2_notify_recv(req, mem_ctx, io);
+}
diff --git a/source4/libcli/smb2/read.c b/source4/libcli/smb2/read.c
new file mode 100644
index 0000000000..9d40e32a4d
--- /dev/null
+++ b/source4/libcli/smb2/read.c
@@ -0,0 +1,87 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client read call
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/*
+ send a read request
+*/
+struct smb2_request *smb2_read_send(struct smb2_tree *tree, struct smb2_read *io)
+{
+ struct smb2_request *req;
+
+ req = smb2_request_init_tree(tree, SMB2_OP_READ, 0x30, true, 0);
+ if (req == NULL) return NULL;
+
+ SCVAL(req->out.body, 0x02, 0); /* pad */
+ SCVAL(req->out.body, 0x03, 0); /* reserved */
+ SIVAL(req->out.body, 0x04, io->in.length);
+ SBVAL(req->out.body, 0x08, io->in.offset);
+ smb2_push_handle(req->out.body+0x10, &io->in.file.handle);
+ SIVAL(req->out.body, 0x20, io->in.min_count);
+ SIVAL(req->out.body, 0x24, io->in.channel);
+ SIVAL(req->out.body, 0x28, io->in.remaining);
+ SSVAL(req->out.body, 0x2C, io->in.channel_offset);
+ SSVAL(req->out.body, 0x2E, io->in.channel_length);
+
+ smb2_transport_send(req);
+
+ return req;
+}
+
+
+/*
+ recv a read reply
+*/
+NTSTATUS smb2_read_recv(struct smb2_request *req,
+ TALLOC_CTX *mem_ctx, struct smb2_read *io)
+{
+ NTSTATUS status;
+
+ if (!smb2_request_receive(req) ||
+ !smb2_request_is_ok(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x10, true);
+
+ status = smb2_pull_o16s32_blob(&req->in, mem_ctx, req->in.body+0x02, &io->out.data);
+ if (!NT_STATUS_IS_OK(status)) {
+ smb2_request_destroy(req);
+ return status;
+ }
+
+ io->out.remaining = IVAL(req->in.body, 0x08);
+ io->out.reserved = IVAL(req->in.body, 0x0C);
+
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync read request
+*/
+NTSTATUS smb2_read(struct smb2_tree *tree, TALLOC_CTX *mem_ctx, struct smb2_read *io)
+{
+ struct smb2_request *req = smb2_read_send(tree, io);
+ return smb2_read_recv(req, mem_ctx, io);
+}
diff --git a/source4/libcli/smb2/request.c b/source4/libcli/smb2/request.c
new file mode 100644
index 0000000000..649a1db8d5
--- /dev/null
+++ b/source4/libcli/smb2/request.c
@@ -0,0 +1,737 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client request handling
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/smb2/smb2.h"
+#include "../lib/util/dlinklist.h"
+#include "lib/events/events.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/* fill in the bufinfo */
+void smb2_setup_bufinfo(struct smb2_request *req)
+{
+ req->in.bufinfo.mem_ctx = req;
+ req->in.bufinfo.flags = BUFINFO_FLAG_UNICODE | BUFINFO_FLAG_SMB2;
+ req->in.bufinfo.align_base = req->in.buffer;
+ if (req->in.dynamic) {
+ req->in.bufinfo.data = req->in.dynamic;
+ req->in.bufinfo.data_size = req->in.body_size - req->in.body_fixed;
+ } else {
+ req->in.bufinfo.data = NULL;
+ req->in.bufinfo.data_size = 0;
+ }
+}
+
+
+/* destroy a request structure */
+static int smb2_request_destructor(struct smb2_request *req)
+{
+ if (req->transport) {
+ /* remove it from the list of pending requests (a null op if
+ its not in the list) */
+ DLIST_REMOVE(req->transport->pending_recv, req);
+ }
+ return 0;
+}
+
+/*
+ initialise a smb2 request
+*/
+struct smb2_request *smb2_request_init(struct smb2_transport *transport, uint16_t opcode,
+ uint16_t body_fixed_size, bool body_dynamic_present,
+ uint32_t body_dynamic_size)
+{
+ struct smb2_request *req;
+ uint64_t seqnum;
+
+ if (body_dynamic_present) {
+ if (body_dynamic_size == 0) {
+ body_dynamic_size = 1;
+ }
+ } else {
+ body_dynamic_size = 0;
+ }
+
+ req = talloc(transport, struct smb2_request);
+ if (req == NULL) return NULL;
+
+ seqnum = transport->seqnum++;
+ if (seqnum == UINT64_MAX) {
+ seqnum = transport->seqnum++;
+ }
+
+ req->state = SMB2_REQUEST_INIT;
+ req->transport = transport;
+ req->session = NULL;
+ req->tree = NULL;
+ req->seqnum = seqnum;
+ req->status = NT_STATUS_OK;
+ req->async.fn = NULL;
+ req->next = req->prev = NULL;
+
+ ZERO_STRUCT(req->cancel);
+ ZERO_STRUCT(req->in);
+
+ req->out.size = SMB2_HDR_BODY+NBT_HDR_SIZE+body_fixed_size;
+
+ req->out.allocated = req->out.size + body_dynamic_size;
+ req->out.buffer = talloc_array(req, uint8_t, req->out.allocated);
+ if (req->out.buffer == NULL) {
+ talloc_free(req);
+ return NULL;
+ }
+
+ req->out.hdr = req->out.buffer + NBT_HDR_SIZE;
+ req->out.body = req->out.hdr + SMB2_HDR_BODY;
+ req->out.body_fixed= body_fixed_size;
+ req->out.body_size = body_fixed_size;
+ req->out.dynamic = (body_dynamic_size ? req->out.body + body_fixed_size : NULL);
+
+ SIVAL(req->out.hdr, 0, SMB2_MAGIC);
+ SSVAL(req->out.hdr, SMB2_HDR_LENGTH, SMB2_HDR_BODY);
+ SSVAL(req->out.hdr, SMB2_HDR_EPOCH, 0);
+ SIVAL(req->out.hdr, SMB2_HDR_STATUS, 0);
+ SSVAL(req->out.hdr, SMB2_HDR_OPCODE, opcode);
+ SSVAL(req->out.hdr, SMB2_HDR_CREDIT, 0);
+ SIVAL(req->out.hdr, SMB2_HDR_FLAGS, 0);
+ SIVAL(req->out.hdr, SMB2_HDR_NEXT_COMMAND, 0);
+ SBVAL(req->out.hdr, SMB2_HDR_MESSAGE_ID, req->seqnum);
+ SIVAL(req->out.hdr, SMB2_HDR_PID, 0);
+ SIVAL(req->out.hdr, SMB2_HDR_TID, 0);
+ SBVAL(req->out.hdr, SMB2_HDR_SESSION_ID, 0);
+ memset(req->out.hdr+SMB2_HDR_SIGNATURE, 0, 16);
+
+ /* set the length of the fixed body part and +1 if there's a dynamic part also */
+ SSVAL(req->out.body, 0, body_fixed_size + (body_dynamic_size?1:0));
+
+ /*
+ * if we have a dynamic part, make sure the first byte
+ * which is always be part of the packet is initialized
+ */
+ if (body_dynamic_size) {
+ req->out.size += 1;
+ SCVAL(req->out.dynamic, 0, 0);
+ }
+
+ talloc_set_destructor(req, smb2_request_destructor);
+
+ return req;
+}
+
+/*
+ initialise a smb2 request for tree operations
+*/
+struct smb2_request *smb2_request_init_tree(struct smb2_tree *tree, uint16_t opcode,
+ uint16_t body_fixed_size, bool body_dynamic_present,
+ uint32_t body_dynamic_size)
+{
+ struct smb2_request *req = smb2_request_init(tree->session->transport, opcode,
+ body_fixed_size, body_dynamic_present,
+ body_dynamic_size);
+ if (req == NULL) return NULL;
+
+ SBVAL(req->out.hdr, SMB2_HDR_SESSION_ID, tree->session->uid);
+ SIVAL(req->out.hdr, SMB2_HDR_TID, tree->tid);
+ req->session = tree->session;
+ req->tree = tree;
+
+ return req;
+}
+
+/* destroy a request structure and return final status */
+NTSTATUS smb2_request_destroy(struct smb2_request *req)
+{
+ NTSTATUS status;
+
+ /* this is the error code we give the application for when a
+ _send() call fails completely */
+ if (!req) return NT_STATUS_UNSUCCESSFUL;
+
+ if (req->state == SMB2_REQUEST_ERROR &&
+ NT_STATUS_IS_OK(req->status)) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ } else {
+ status = req->status;
+ }
+
+ talloc_free(req);
+ return status;
+}
+
+/*
+ receive a response to a packet
+*/
+bool smb2_request_receive(struct smb2_request *req)
+{
+ /* req can be NULL when a send has failed. This eliminates lots of NULL
+ checks in each module */
+ if (!req) return false;
+
+ /* keep receiving packets until this one is replied to */
+ while (req->state <= SMB2_REQUEST_RECV) {
+ if (event_loop_once(req->transport->socket->event.ctx) != 0) {
+ return false;
+ }
+ }
+
+ return req->state == SMB2_REQUEST_DONE;
+}
+
+/* Return true if the last packet was in error */
+bool smb2_request_is_error(struct smb2_request *req)
+{
+ return NT_STATUS_IS_ERR(req->status);
+}
+
+/* Return true if the last packet was OK */
+bool smb2_request_is_ok(struct smb2_request *req)
+{
+ return NT_STATUS_IS_OK(req->status);
+}
+
+/*
+ check if a range in the reply body is out of bounds
+*/
+bool smb2_oob(struct smb2_request_buffer *buf, const uint8_t *ptr, size_t size)
+{
+ if (size == 0) {
+ /* zero bytes is never out of range */
+ return false;
+ }
+ /* be careful with wraparound! */
+ if ((uintptr_t)ptr < (uintptr_t)buf->body ||
+ (uintptr_t)ptr >= (uintptr_t)buf->body + buf->body_size ||
+ size > buf->body_size ||
+ (uintptr_t)ptr + size > (uintptr_t)buf->body + buf->body_size) {
+ return true;
+ }
+ return false;
+}
+
+size_t smb2_padding_size(uint32_t offset, size_t n)
+{
+ if ((offset & (n-1)) == 0) return 0;
+ return n - (offset & (n-1));
+}
+
+static size_t smb2_padding_fix(struct smb2_request_buffer *buf)
+{
+ if (buf->dynamic == (buf->body + buf->body_fixed)) {
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ grow a SMB2 buffer by the specified amount
+*/
+static NTSTATUS smb2_grow_buffer(struct smb2_request_buffer *buf, size_t increase)
+{
+ size_t dynamic_ofs;
+ uint8_t *buffer_ptr;
+ uint32_t newsize = buf->size + increase;
+
+ /* a packet size should be limited a bit */
+ if (newsize >= 0x00FFFFFF) return NT_STATUS_MARSHALL_OVERFLOW;
+
+ if (newsize <= buf->allocated) return NT_STATUS_OK;
+
+ dynamic_ofs = buf->dynamic - buf->buffer;
+
+ buffer_ptr = talloc_realloc(buf, buf->buffer, uint8_t, newsize);
+ NT_STATUS_HAVE_NO_MEMORY(buffer_ptr);
+
+ buf->buffer = buffer_ptr;
+ buf->hdr = buf->buffer + NBT_HDR_SIZE;
+ buf->body = buf->hdr + SMB2_HDR_BODY;
+ buf->dynamic = buf->buffer + dynamic_ofs;
+ buf->allocated = newsize;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ pull a uint16_t ofs/ uint16_t length/blob triple from a data blob
+ the ptr points to the start of the offset/length pair
+*/
+NTSTATUS smb2_pull_o16s16_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx, uint8_t *ptr, DATA_BLOB *blob)
+{
+ uint16_t ofs, size;
+ if (smb2_oob(buf, ptr, 4)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ ofs = SVAL(ptr, 0);
+ size = SVAL(ptr, 2);
+ if (ofs == 0) {
+ *blob = data_blob(NULL, 0);
+ return NT_STATUS_OK;
+ }
+ if (smb2_oob(buf, buf->hdr + ofs, size)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ *blob = data_blob_talloc(mem_ctx, buf->hdr + ofs, size);
+ NT_STATUS_HAVE_NO_MEMORY(blob->data);
+ return NT_STATUS_OK;
+}
+
+/*
+ push a uint16_t ofs/ uint16_t length/blob triple into a data blob
+ the ofs points to the start of the offset/length pair, and is relative
+ to the body start
+*/
+NTSTATUS smb2_push_o16s16_blob(struct smb2_request_buffer *buf,
+ uint16_t ofs, DATA_BLOB blob)
+{
+ NTSTATUS status;
+ size_t offset;
+ size_t padding_length;
+ size_t padding_fix;
+ uint8_t *ptr = buf->body+ofs;
+
+ if (buf->dynamic == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* we have only 16 bit for the size */
+ if (blob.length > 0xFFFF) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* check if there're enough room for ofs and size */
+ if (smb2_oob(buf, ptr, 4)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (blob.data == NULL) {
+ if (blob.length != 0) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ SSVAL(ptr, 0, 0);
+ SSVAL(ptr, 2, 0);
+ return NT_STATUS_OK;
+ }
+
+ offset = buf->dynamic - buf->hdr;
+ padding_length = smb2_padding_size(offset, 2);
+ offset += padding_length;
+ padding_fix = smb2_padding_fix(buf);
+
+ SSVAL(ptr, 0, offset);
+ SSVAL(ptr, 2, blob.length);
+
+ status = smb2_grow_buffer(buf, blob.length + padding_length - padding_fix);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ memset(buf->dynamic, 0, padding_length);
+ buf->dynamic += padding_length;
+
+ memcpy(buf->dynamic, blob.data, blob.length);
+ buf->dynamic += blob.length;
+
+ buf->size += blob.length + padding_length - padding_fix;
+ buf->body_size += blob.length + padding_length;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ push a uint16_t ofs/ uint32_t length/blob triple into a data blob
+ the ofs points to the start of the offset/length pair, and is relative
+ to the body start
+*/
+NTSTATUS smb2_push_o16s32_blob(struct smb2_request_buffer *buf,
+ uint16_t ofs, DATA_BLOB blob)
+{
+ NTSTATUS status;
+ size_t offset;
+ size_t padding_length;
+ size_t padding_fix;
+ uint8_t *ptr = buf->body+ofs;
+
+ if (buf->dynamic == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* check if there're enough room for ofs and size */
+ if (smb2_oob(buf, ptr, 6)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (blob.data == NULL) {
+ if (blob.length != 0) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ SSVAL(ptr, 0, 0);
+ SIVAL(ptr, 2, 0);
+ return NT_STATUS_OK;
+ }
+
+ offset = buf->dynamic - buf->hdr;
+ padding_length = smb2_padding_size(offset, 2);
+ offset += padding_length;
+ padding_fix = smb2_padding_fix(buf);
+
+ SSVAL(ptr, 0, offset);
+ SIVAL(ptr, 2, blob.length);
+
+ status = smb2_grow_buffer(buf, blob.length + padding_length - padding_fix);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ memset(buf->dynamic, 0, padding_length);
+ buf->dynamic += padding_length;
+
+ memcpy(buf->dynamic, blob.data, blob.length);
+ buf->dynamic += blob.length;
+
+ buf->size += blob.length + padding_length - padding_fix;
+ buf->body_size += blob.length + padding_length;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ push a uint32_t ofs/ uint32_t length/blob triple into a data blob
+ the ofs points to the start of the offset/length pair, and is relative
+ to the body start
+*/
+NTSTATUS smb2_push_o32s32_blob(struct smb2_request_buffer *buf,
+ uint32_t ofs, DATA_BLOB blob)
+{
+ NTSTATUS status;
+ size_t offset;
+ size_t padding_length;
+ size_t padding_fix;
+ uint8_t *ptr = buf->body+ofs;
+
+ if (buf->dynamic == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* check if there're enough room for ofs and size */
+ if (smb2_oob(buf, ptr, 8)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (blob.data == NULL) {
+ if (blob.length != 0) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ SIVAL(ptr, 0, 0);
+ SIVAL(ptr, 4, 0);
+ return NT_STATUS_OK;
+ }
+
+ offset = buf->dynamic - buf->hdr;
+ padding_length = smb2_padding_size(offset, 8);
+ offset += padding_length;
+ padding_fix = smb2_padding_fix(buf);
+
+ SIVAL(ptr, 0, offset);
+ SIVAL(ptr, 4, blob.length);
+
+ status = smb2_grow_buffer(buf, blob.length + padding_length - padding_fix);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ memset(buf->dynamic, 0, padding_length);
+ buf->dynamic += padding_length;
+
+ memcpy(buf->dynamic, blob.data, blob.length);
+ buf->dynamic += blob.length;
+
+ buf->size += blob.length + padding_length - padding_fix;
+ buf->body_size += blob.length + padding_length;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ push a uint32_t length/ uint32_t ofs/blob triple into a data blob
+ the ofs points to the start of the length/offset pair, and is relative
+ to the body start
+*/
+NTSTATUS smb2_push_s32o32_blob(struct smb2_request_buffer *buf,
+ uint32_t ofs, DATA_BLOB blob)
+{
+ NTSTATUS status;
+ size_t offset;
+ size_t padding_length;
+ size_t padding_fix;
+ uint8_t *ptr = buf->body+ofs;
+
+ if (buf->dynamic == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* check if there're enough room for ofs and size */
+ if (smb2_oob(buf, ptr, 8)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (blob.data == NULL) {
+ if (blob.length != 0) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ SIVAL(ptr, 0, 0);
+ SIVAL(ptr, 4, 0);
+ return NT_STATUS_OK;
+ }
+
+ offset = buf->dynamic - buf->hdr;
+ padding_length = smb2_padding_size(offset, 8);
+ offset += padding_length;
+ padding_fix = smb2_padding_fix(buf);
+
+ SIVAL(ptr, 0, blob.length);
+ SIVAL(ptr, 4, offset);
+
+ status = smb2_grow_buffer(buf, blob.length + padding_length - padding_fix);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ memset(buf->dynamic, 0, padding_length);
+ buf->dynamic += padding_length;
+
+ memcpy(buf->dynamic, blob.data, blob.length);
+ buf->dynamic += blob.length;
+
+ buf->size += blob.length + padding_length - padding_fix;
+ buf->body_size += blob.length + padding_length;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ pull a uint16_t ofs/ uint32_t length/blob triple from a data blob
+ the ptr points to the start of the offset/length pair
+*/
+NTSTATUS smb2_pull_o16s32_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx, uint8_t *ptr, DATA_BLOB *blob)
+{
+ uint16_t ofs;
+ uint32_t size;
+
+ if (smb2_oob(buf, ptr, 6)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ ofs = SVAL(ptr, 0);
+ size = IVAL(ptr, 2);
+ if (ofs == 0) {
+ *blob = data_blob(NULL, 0);
+ return NT_STATUS_OK;
+ }
+ if (smb2_oob(buf, buf->hdr + ofs, size)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ *blob = data_blob_talloc(mem_ctx, buf->hdr + ofs, size);
+ NT_STATUS_HAVE_NO_MEMORY(blob->data);
+ return NT_STATUS_OK;
+}
+
+/*
+ pull a uint32_t ofs/ uint32_t length/blob triple from a data blob
+ the ptr points to the start of the offset/length pair
+*/
+NTSTATUS smb2_pull_o32s32_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx, uint8_t *ptr, DATA_BLOB *blob)
+{
+ uint32_t ofs, size;
+ if (smb2_oob(buf, ptr, 8)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ ofs = IVAL(ptr, 0);
+ size = IVAL(ptr, 4);
+ if (ofs == 0) {
+ *blob = data_blob(NULL, 0);
+ return NT_STATUS_OK;
+ }
+ if (smb2_oob(buf, buf->hdr + ofs, size)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ *blob = data_blob_talloc(mem_ctx, buf->hdr + ofs, size);
+ NT_STATUS_HAVE_NO_MEMORY(blob->data);
+ return NT_STATUS_OK;
+}
+
+/*
+ pull a uint16_t ofs/ uint32_t length/blob triple from a data blob
+ the ptr points to the start of the offset/length pair
+
+ In this varient the uint16_t is padded by an extra 2 bytes, making
+ the size aligned on 4 byte boundary
+*/
+NTSTATUS smb2_pull_o16As32_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx, uint8_t *ptr, DATA_BLOB *blob)
+{
+ uint32_t ofs, size;
+ if (smb2_oob(buf, ptr, 8)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ ofs = SVAL(ptr, 0);
+ size = IVAL(ptr, 4);
+ if (ofs == 0) {
+ *blob = data_blob(NULL, 0);
+ return NT_STATUS_OK;
+ }
+ if (smb2_oob(buf, buf->hdr + ofs, size)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ *blob = data_blob_talloc(mem_ctx, buf->hdr + ofs, size);
+ NT_STATUS_HAVE_NO_MEMORY(blob->data);
+ return NT_STATUS_OK;
+}
+
+/*
+ pull a uint32_t length/ uint32_t ofs/blob triple from a data blob
+ the ptr points to the start of the offset/length pair
+*/
+NTSTATUS smb2_pull_s32o32_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx, uint8_t *ptr, DATA_BLOB *blob)
+{
+ uint32_t ofs, size;
+ if (smb2_oob(buf, ptr, 8)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ size = IVAL(ptr, 0);
+ ofs = IVAL(ptr, 4);
+ if (ofs == 0) {
+ *blob = data_blob(NULL, 0);
+ return NT_STATUS_OK;
+ }
+ if (smb2_oob(buf, buf->hdr + ofs, size)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ *blob = data_blob_talloc(mem_ctx, buf->hdr + ofs, size);
+ NT_STATUS_HAVE_NO_MEMORY(blob->data);
+ return NT_STATUS_OK;
+}
+
+/*
+ pull a uint32_t length/ uint16_t ofs/blob triple from a data blob
+ the ptr points to the start of the offset/length pair
+*/
+NTSTATUS smb2_pull_s32o16_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx, uint8_t *ptr, DATA_BLOB *blob)
+{
+ uint32_t ofs, size;
+ if (smb2_oob(buf, ptr, 8)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ size = IVAL(ptr, 0);
+ ofs = SVAL(ptr, 4);
+ if (ofs == 0) {
+ *blob = data_blob(NULL, 0);
+ return NT_STATUS_OK;
+ }
+ if (smb2_oob(buf, buf->hdr + ofs, size)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ *blob = data_blob_talloc(mem_ctx, buf->hdr + ofs, size);
+ NT_STATUS_HAVE_NO_MEMORY(blob->data);
+ return NT_STATUS_OK;
+}
+
+/*
+ pull a string in a uint16_t ofs/ uint16_t length/blob format
+ UTF-16 without termination
+*/
+NTSTATUS smb2_pull_o16s16_string(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx,
+ uint8_t *ptr, const char **str)
+{
+ DATA_BLOB blob;
+ NTSTATUS status;
+ void *vstr;
+ bool ret;
+
+ status = smb2_pull_o16s16_blob(buf, mem_ctx, ptr, &blob);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ if (blob.data == NULL) {
+ *str = NULL;
+ return NT_STATUS_OK;
+ }
+
+ if (blob.length == 0) {
+ char *s;
+ s = talloc_strdup(mem_ctx, "");
+ NT_STATUS_HAVE_NO_MEMORY(s);
+ *str = s;
+ return NT_STATUS_OK;
+ }
+
+ ret = convert_string_talloc(mem_ctx, CH_UTF16, CH_UNIX,
+ blob.data, blob.length, &vstr, NULL, false);
+ data_blob_free(&blob);
+ (*str) = (char *)vstr;
+ if (!ret) {
+ return NT_STATUS_ILLEGAL_CHARACTER;
+ }
+ return NT_STATUS_OK;
+}
+
+/*
+ push a string in a uint16_t ofs/ uint16_t length/blob format
+ UTF-16 without termination
+*/
+NTSTATUS smb2_push_o16s16_string(struct smb2_request_buffer *buf,
+ uint16_t ofs, const char *str)
+{
+ DATA_BLOB blob;
+ NTSTATUS status;
+ bool ret;
+
+ if (str == NULL) {
+ return smb2_push_o16s16_blob(buf, ofs, data_blob(NULL, 0));
+ }
+
+ if (*str == 0) {
+ blob.data = discard_const_p(uint8_t, str);
+ blob.length = 0;
+ return smb2_push_o16s16_blob(buf, ofs, blob);
+ }
+
+ ret = convert_string_talloc(buf->buffer, CH_UNIX, CH_UTF16,
+ str, strlen(str), (void **)&blob.data, &blob.length,
+ false);
+ if (!ret) {
+ return NT_STATUS_ILLEGAL_CHARACTER;
+ }
+
+ status = smb2_push_o16s16_blob(buf, ofs, blob);
+ data_blob_free(&blob);
+ return status;
+}
+
+/*
+ push a file handle into a buffer
+*/
+void smb2_push_handle(uint8_t *data, struct smb2_handle *h)
+{
+ SBVAL(data, 0, h->data[0]);
+ SBVAL(data, 8, h->data[1]);
+}
+
+/*
+ pull a file handle from a buffer
+*/
+void smb2_pull_handle(uint8_t *ptr, struct smb2_handle *h)
+{
+ h->data[0] = BVAL(ptr, 0);
+ h->data[1] = BVAL(ptr, 8);
+}
diff --git a/source4/libcli/smb2/session.c b/source4/libcli/smb2/session.c
new file mode 100644
index 0000000000..127bb9bcae
--- /dev/null
+++ b/source4/libcli/smb2/session.c
@@ -0,0 +1,274 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client session handling
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "libcli/composite/composite.h"
+#include "auth/gensec/gensec.h"
+
+/**
+ initialise a smb2_session structure
+ */
+struct smb2_session *smb2_session_init(struct smb2_transport *transport,
+ struct gensec_settings *settings,
+ TALLOC_CTX *parent_ctx, bool primary)
+{
+ struct smb2_session *session;
+ NTSTATUS status;
+
+ session = talloc_zero(parent_ctx, struct smb2_session);
+ if (!session) {
+ return NULL;
+ }
+ if (primary) {
+ session->transport = talloc_steal(session, transport);
+ } else {
+ session->transport = talloc_reference(session, transport);
+ }
+
+ /* prepare a gensec context for later use */
+ status = gensec_client_start(session, &session->gensec,
+ session->transport->socket->event.ctx,
+ settings);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(session);
+ return NULL;
+ }
+
+ gensec_want_feature(session->gensec, GENSEC_FEATURE_SESSION_KEY);
+
+ return session;
+}
+
+/**
+ send a session setup request
+*/
+struct smb2_request *smb2_session_setup_send(struct smb2_session *session,
+ struct smb2_session_setup *io)
+{
+ struct smb2_request *req;
+ NTSTATUS status;
+
+ req = smb2_request_init(session->transport, SMB2_OP_SESSSETUP,
+ 0x18, true, io->in.secblob.length);
+ if (req == NULL) return NULL;
+
+ SBVAL(req->out.hdr, SMB2_HDR_SESSION_ID, session->uid);
+ SCVAL(req->out.body, 0x02, io->in.vc_number);
+ SCVAL(req->out.body, 0x03, io->in.security_mode);
+ SIVAL(req->out.body, 0x04, io->in.capabilities);
+ SIVAL(req->out.body, 0x08, io->in.channel);
+ SBVAL(req->out.body, 0x10, io->in.previous_sessionid);
+
+ req->session = session;
+
+ status = smb2_push_o16s16_blob(&req->out, 0x0C, io->in.secblob);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+
+ smb2_transport_send(req);
+
+ return req;
+}
+
+
+/**
+ recv a session setup reply
+*/
+NTSTATUS smb2_session_setup_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx,
+ struct smb2_session_setup *io)
+{
+ NTSTATUS status;
+
+ if (!smb2_request_receive(req) ||
+ (smb2_request_is_error(req) &&
+ !NT_STATUS_EQUAL(req->status, NT_STATUS_MORE_PROCESSING_REQUIRED))) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x08, true);
+
+ io->out.session_flags = SVAL(req->in.body, 0x02);
+ io->out.uid = BVAL(req->in.hdr, SMB2_HDR_SESSION_ID);
+
+ status = smb2_pull_o16s16_blob(&req->in, mem_ctx, req->in.body+0x04, &io->out.secblob);
+ if (!NT_STATUS_IS_OK(status)) {
+ smb2_request_destroy(req);
+ return status;
+ }
+
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync session setup request
+*/
+NTSTATUS smb2_session_setup(struct smb2_session *session,
+ TALLOC_CTX *mem_ctx, struct smb2_session_setup *io)
+{
+ struct smb2_request *req = smb2_session_setup_send(session, io);
+ return smb2_session_setup_recv(req, mem_ctx, io);
+}
+
+
+struct smb2_session_state {
+ struct smb2_session_setup io;
+ struct smb2_request *req;
+ NTSTATUS gensec_status;
+};
+
+/*
+ handle continuations of the spnego session setup
+*/
+static void session_request_handler(struct smb2_request *req)
+{
+ struct composite_context *c = talloc_get_type(req->async.private_data,
+ struct composite_context);
+ struct smb2_session_state *state = talloc_get_type(c->private_data,
+ struct smb2_session_state);
+ struct smb2_session *session = req->session;
+
+ c->status = smb2_session_setup_recv(req, c, &state->io);
+ if (NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED) ||
+ (NT_STATUS_IS_OK(c->status) &&
+ NT_STATUS_EQUAL(state->gensec_status, NT_STATUS_MORE_PROCESSING_REQUIRED))) {
+ NTSTATUS session_key_err;
+ DATA_BLOB session_key;
+ c->status = gensec_update(session->gensec, c,
+ state->io.out.secblob,
+ &state->io.in.secblob);
+ state->gensec_status = c->status;
+
+ session_key_err = gensec_session_key(session->gensec, &session_key);
+ if (NT_STATUS_IS_OK(session_key_err)) {
+ session->session_key = session_key;
+ }
+ }
+
+ session->uid = state->io.out.uid;
+
+ if (NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ state->req = smb2_session_setup_send(session, &state->io);
+ if (state->req == NULL) {
+ composite_error(c, NT_STATUS_NO_MEMORY);
+ return;
+ }
+
+ state->req->async.fn = session_request_handler;
+ state->req->async.private_data = c;
+ return;
+ }
+
+ if (!NT_STATUS_IS_OK(c->status)) {
+ composite_error(c, c->status);
+ return;
+ }
+
+ if (session->transport->signing_required) {
+ if (session->session_key.length == 0) {
+ DEBUG(0,("Wrong session key length %u for SMB2 signing\n",
+ (unsigned)session->session_key.length));
+ composite_error(c, NT_STATUS_ACCESS_DENIED);
+ return;
+ }
+ session->signing_active = true;
+ }
+
+ composite_done(c);
+}
+
+/*
+ a composite function that does a full SPNEGO session setup
+ */
+struct composite_context *smb2_session_setup_spnego_send(struct smb2_session *session,
+ struct cli_credentials *credentials)
+{
+ struct composite_context *c;
+ struct smb2_session_state *state;
+
+ c = composite_create(session, session->transport->socket->event.ctx);
+ if (c == NULL) return NULL;
+
+ state = talloc(c, struct smb2_session_state);
+ if (composite_nomem(state, c)) return c;
+ c->private_data = state;
+
+ ZERO_STRUCT(state->io);
+ state->io.in.vc_number = 0;
+ if (session->transport->signing_required) {
+ state->io.in.security_mode =
+ SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED;
+ }
+ state->io.in.capabilities = 0;
+ state->io.in.channel = 0;
+ state->io.in.previous_sessionid = 0;
+
+ c->status = gensec_set_credentials(session->gensec, credentials);
+ if (!composite_is_ok(c)) return c;
+
+ c->status = gensec_set_target_hostname(session->gensec,
+ session->transport->socket->hostname);
+ if (!composite_is_ok(c)) return c;
+
+ c->status = gensec_set_target_service(session->gensec, "cifs");
+ if (!composite_is_ok(c)) return c;
+
+ c->status = gensec_start_mech_by_oid(session->gensec, GENSEC_OID_SPNEGO);
+ if (!composite_is_ok(c)) return c;
+
+ c->status = gensec_update(session->gensec, c,
+ session->transport->negotiate.secblob,
+ &state->io.in.secblob);
+ if (!NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ composite_error(c, c->status);
+ return c;
+ }
+ state->gensec_status = c->status;
+
+ state->req = smb2_session_setup_send(session, &state->io);
+ composite_continue_smb2(c, state->req, session_request_handler, c);
+ return c;
+}
+
+/*
+ receive a composite session setup reply
+*/
+NTSTATUS smb2_session_setup_spnego_recv(struct composite_context *c)
+{
+ NTSTATUS status;
+ status = composite_wait(c);
+ talloc_free(c);
+ return status;
+}
+
+/*
+ sync version of smb2_session_setup_spnego
+*/
+NTSTATUS smb2_session_setup_spnego(struct smb2_session *session,
+ struct cli_credentials *credentials)
+{
+ struct composite_context *c = smb2_session_setup_spnego_send(session, credentials);
+ return smb2_session_setup_spnego_recv(c);
+}
diff --git a/source4/libcli/smb2/setinfo.c b/source4/libcli/smb2/setinfo.c
new file mode 100644
index 0000000000..69c0f45b63
--- /dev/null
+++ b/source4/libcli/smb2/setinfo.c
@@ -0,0 +1,122 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client setinfo calls
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/*
+ send a setinfo request
+*/
+struct smb2_request *smb2_setinfo_send(struct smb2_tree *tree, struct smb2_setinfo *io)
+{
+ NTSTATUS status;
+ struct smb2_request *req;
+
+ req = smb2_request_init_tree(tree, SMB2_OP_SETINFO, 0x20, true, io->in.blob.length);
+ if (req == NULL) return NULL;
+
+ SSVAL(req->out.body, 0x02, io->in.level);
+
+ status = smb2_push_s32o32_blob(&req->out, 0x04, io->in.blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+
+ SIVAL(req->out.body, 0x0C, io->in.flags);
+ smb2_push_handle(req->out.body+0x10, &io->in.file.handle);
+
+ smb2_transport_send(req);
+
+ return req;
+}
+
+
+/*
+ recv a setinfo reply
+*/
+NTSTATUS smb2_setinfo_recv(struct smb2_request *req)
+{
+ if (!smb2_request_receive(req) ||
+ !smb2_request_is_ok(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x02, false);
+
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync setinfo request
+*/
+NTSTATUS smb2_setinfo(struct smb2_tree *tree, struct smb2_setinfo *io)
+{
+ struct smb2_request *req = smb2_setinfo_send(tree, io);
+ return smb2_setinfo_recv(req);
+}
+
+/*
+ level specific file setinfo call - async send
+*/
+struct smb2_request *smb2_setinfo_file_send(struct smb2_tree *tree, union smb_setfileinfo *io)
+{
+ struct smb2_setinfo b;
+ uint16_t smb2_level = smb2_getinfo_map_level(io->generic.level, SMB2_GETINFO_FILE);
+ struct smb2_request *req;
+
+ if (smb2_level == 0) {
+ return NULL;
+ }
+
+ ZERO_STRUCT(b);
+ b.in.level = smb2_level;
+ b.in.file.handle = io->generic.in.file.handle;
+
+ /* change levels so the parsers know it is SMB2 */
+ if (io->generic.level == RAW_SFILEINFO_RENAME_INFORMATION) {
+ io->generic.level = RAW_SFILEINFO_RENAME_INFORMATION_SMB2;
+ }
+
+ if (!smb_raw_setfileinfo_passthru(tree, io->generic.level, io, &b.in.blob)) {
+ return NULL;
+ }
+
+ if (io->generic.level == RAW_SFILEINFO_SEC_DESC) {
+ b.in.flags = io->set_secdesc.in.secinfo_flags;
+ }
+
+ req = smb2_setinfo_send(tree, &b);
+ data_blob_free(&b.in.blob);
+ return req;
+}
+
+/*
+ level specific file setinfo call - sync
+*/
+NTSTATUS smb2_setinfo_file(struct smb2_tree *tree, union smb_setfileinfo *io)
+{
+ struct smb2_request *req = smb2_setinfo_file_send(tree, io);
+ return smb2_setinfo_recv(req);
+}
diff --git a/source4/libcli/smb2/signing.c b/source4/libcli/smb2/signing.c
new file mode 100644
index 0000000000..101fb00c12
--- /dev/null
+++ b/source4/libcli/smb2/signing.c
@@ -0,0 +1,117 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 Signing Code
+
+ Copyright (C) Andrew Tridgell <tridge@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "../lib/crypto/crypto.h"
+
+/*
+ sign an outgoing message
+ */
+NTSTATUS smb2_sign_message(struct smb2_request_buffer *buf, DATA_BLOB session_key)
+{
+ struct HMACSHA256Context m;
+ uint8_t res[32];
+ uint64_t session_id;
+
+ if (buf->size < NBT_HDR_SIZE + SMB2_HDR_SIGNATURE + 16) {
+ /* can't sign non-SMB2 messages */
+ return NT_STATUS_OK;
+ }
+
+ session_id = BVAL(buf->hdr, SMB2_HDR_SESSION_ID);
+ if (session_id == 0) {
+ /* we don't sign messages with a zero session_id. See
+ MS-SMB2 3.2.4.1.1 */
+ return NT_STATUS_OK;
+ }
+
+ if (session_key.length == 0) {
+ DEBUG(2,("Wrong session key length %u for SMB2 signing\n",
+ (unsigned)session_key.length));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ memset(buf->hdr + SMB2_HDR_SIGNATURE, 0, 16);
+
+ SIVAL(buf->hdr, SMB2_HDR_FLAGS, IVAL(buf->hdr, SMB2_HDR_FLAGS) | SMB2_HDR_FLAG_SIGNED);
+
+ ZERO_STRUCT(m);
+ hmac_sha256_init(session_key.data, MIN(session_key.length, 16), &m);
+ hmac_sha256_update(buf->buffer+NBT_HDR_SIZE, buf->size-NBT_HDR_SIZE, &m);
+ hmac_sha256_final(res, &m);
+ DEBUG(5,("signed SMB2 message of size %u\n", (unsigned)buf->size - NBT_HDR_SIZE));
+
+ memcpy(buf->hdr + SMB2_HDR_SIGNATURE, res, 16);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ check an incoming signature
+ */
+NTSTATUS smb2_check_signature(struct smb2_request_buffer *buf, DATA_BLOB session_key)
+{
+ uint64_t session_id;
+ struct HMACSHA256Context m;
+ uint8_t res[SHA256_DIGEST_LENGTH];
+ uint8_t sig[16];
+
+ if (buf->size < NBT_HDR_SIZE + SMB2_HDR_SIGNATURE + 16) {
+ /* can't check non-SMB2 messages */
+ return NT_STATUS_OK;
+ }
+
+ session_id = BVAL(buf->hdr, SMB2_HDR_SESSION_ID);
+ if (session_id == 0) {
+ /* don't sign messages with a zero session_id. See
+ MS-SMB2 3.2.4.1.1 */
+ return NT_STATUS_OK;
+ }
+
+ if (session_key.length == 0) {
+ /* we don't have the session key yet */
+ return NT_STATUS_OK;
+ }
+
+ memcpy(sig, buf->hdr+SMB2_HDR_SIGNATURE, 16);
+
+ memset(buf->hdr + SMB2_HDR_SIGNATURE, 0, 16);
+
+ ZERO_STRUCT(m);
+ hmac_sha256_init(session_key.data, MIN(session_key.length, 16), &m);
+ hmac_sha256_update(buf->hdr, buf->size-NBT_HDR_SIZE, &m);
+ hmac_sha256_final(res, &m);
+
+ memcpy(buf->hdr+SMB2_HDR_SIGNATURE, sig, 16);
+
+ if (memcmp(res, sig, 16) != 0) {
+ DEBUG(0,("Bad SMB2 signature for message of size %u\n",
+ (unsigned)buf->size-NBT_HDR_SIZE));
+ dump_data(0, sig, 16);
+ dump_data(0, res, 16);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/libcli/smb2/smb2.h b/source4/libcli/smb2/smb2.h
new file mode 100644
index 0000000000..d1d5b842c3
--- /dev/null
+++ b/source4/libcli/smb2/smb2.h
@@ -0,0 +1,303 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client library header
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __LIBCLI_SMB2_SMB2_H__
+#define __LIBCLI_SMB2_SMB2_H__
+
+#include "libcli/raw/request.h"
+#include "libcli/raw/libcliraw.h"
+
+struct smb2_handle;
+
+/*
+ information returned from the negotiate process
+*/
+struct smb2_negotiate {
+ DATA_BLOB secblob;
+ NTTIME system_time;
+ NTTIME server_start_time;
+ uint16_t security_mode;
+};
+
+/* this is the context for the smb2 transport layer */
+struct smb2_transport {
+ /* socket level info */
+ struct smbcli_socket *socket;
+
+ struct smb2_negotiate negotiate;
+
+ /* next seqnum to allocate */
+ uint64_t seqnum;
+
+ /* a list of requests that are pending for receive on this
+ connection */
+ struct smb2_request *pending_recv;
+
+ /* context of the stream -> packet parser */
+ struct packet_context *packet;
+
+ /* an idle function - if this is defined then it will be
+ called once every period microseconds while we are waiting
+ for a packet */
+ struct {
+ void (*func)(struct smb2_transport *, void *);
+ void *private_data;
+ uint_t period;
+ } idle;
+
+ struct {
+ /* a oplock break request handler */
+ bool (*handler)(struct smb2_transport *transport,
+ const struct smb2_handle *handle,
+ uint8_t level, void *private_data);
+ /* private data passed to the oplock handler */
+ void *private_data;
+ } oplock;
+
+ struct smbcli_options options;
+
+ bool signing_required;
+};
+
+
+/*
+ SMB2 tree context
+*/
+struct smb2_tree {
+ struct smb2_session *session;
+ uint32_t tid;
+};
+
+/*
+ SMB2 session context
+*/
+struct smb2_session {
+ struct smb2_transport *transport;
+ struct gensec_security *gensec;
+ uint64_t uid;
+ DATA_BLOB session_key;
+ bool signing_active;
+};
+
+
+struct smb2_request_buffer {
+ /* the raw SMB2 buffer, including the 4 byte length header */
+ uint8_t *buffer;
+
+ /* the size of the raw buffer, including 4 byte header */
+ size_t size;
+
+ /* how much has been allocated - on reply the buffer is over-allocated to
+ prevent too many realloc() calls
+ */
+ size_t allocated;
+
+ /* the start of the SMB2 header - this is always buffer+4 */
+ uint8_t *hdr;
+
+ /* the packet body */
+ uint8_t *body;
+ size_t body_fixed;
+ size_t body_size;
+
+ /* this point to the next dynamic byte that can be used
+ * this will be moved when some dynamic data is pushed
+ */
+ uint8_t *dynamic;
+
+ /* this is used to range check and align strings and buffers */
+ struct request_bufinfo bufinfo;
+};
+
+
+/*
+ a client request moves between the following 4 states.
+*/
+enum smb2_request_state {SMB2_REQUEST_INIT, /* we are creating the request */
+ SMB2_REQUEST_RECV, /* we are waiting for a matching reply */
+ SMB2_REQUEST_DONE, /* the request is finished */
+ SMB2_REQUEST_ERROR}; /* a packet or transport level error has occurred */
+
+/* the context for a single SMB2 request */
+struct smb2_request {
+ /* allow a request to be part of a list of requests */
+ struct smb2_request *next, *prev;
+
+ /* each request is in one of 3 possible states */
+ enum smb2_request_state state;
+
+ struct smb2_transport *transport;
+ struct smb2_session *session;
+ struct smb2_tree *tree;
+
+ uint64_t seqnum;
+
+ struct {
+ bool do_cancel;
+ bool can_cancel;
+ uint32_t pending_id;
+ } cancel;
+
+ /* the NT status for this request. Set by packet receive code
+ or code detecting error. */
+ NTSTATUS status;
+
+ struct smb2_request_buffer in;
+ struct smb2_request_buffer out;
+
+ /* information on what to do with a reply when it is received
+ asyncronously. If this is not setup when a reply is received then
+ the reply is discarded
+
+ The private pointer is private to the caller of the client
+ library (the application), not private to the library
+ */
+ struct {
+ void (*fn)(struct smb2_request *);
+ void *private_data;
+ } async;
+};
+
+
+#define SMB2_MIN_SIZE 0x42
+#define SMB2_MIN_SIZE_NO_BODY 0x40
+
+/* offsets into header elements for a sync SMB2 request */
+#define SMB2_HDR_PROTOCOL_ID 0x00
+#define SMB2_HDR_LENGTH 0x04
+#define SMB2_HDR_EPOCH 0x06
+#define SMB2_HDR_STATUS 0x08
+#define SMB2_HDR_OPCODE 0x0c
+#define SMB2_HDR_CREDIT 0x0e
+#define SMB2_HDR_FLAGS 0x10
+#define SMB2_HDR_NEXT_COMMAND 0x14
+#define SMB2_HDR_MESSAGE_ID 0x18
+#define SMB2_HDR_PID 0x20
+#define SMB2_HDR_TID 0x24
+#define SMB2_HDR_SESSION_ID 0x28
+#define SMB2_HDR_SIGNATURE 0x30 /* 16 bytes */
+#define SMB2_HDR_BODY 0x40
+
+/* header flags */
+#define SMB2_HDR_FLAG_REDIRECT 0x01
+#define SMB2_HDR_FLAG_ASYNC 0x02
+#define SMB2_HDR_FLAG_CHAINED 0x04
+#define SMB2_HDR_FLAG_SIGNED 0x08
+#define SMB2_HDR_FLAG_DFS 0x10000000
+
+/* SMB2 opcodes */
+#define SMB2_OP_NEGPROT 0x00
+#define SMB2_OP_SESSSETUP 0x01
+#define SMB2_OP_LOGOFF 0x02
+#define SMB2_OP_TCON 0x03
+#define SMB2_OP_TDIS 0x04
+#define SMB2_OP_CREATE 0x05
+#define SMB2_OP_CLOSE 0x06
+#define SMB2_OP_FLUSH 0x07
+#define SMB2_OP_READ 0x08
+#define SMB2_OP_WRITE 0x09
+#define SMB2_OP_LOCK 0x0a
+#define SMB2_OP_IOCTL 0x0b
+#define SMB2_OP_CANCEL 0x0c
+#define SMB2_OP_KEEPALIVE 0x0d
+#define SMB2_OP_FIND 0x0e
+#define SMB2_OP_NOTIFY 0x0f
+#define SMB2_OP_GETINFO 0x10
+#define SMB2_OP_SETINFO 0x11
+#define SMB2_OP_BREAK 0x12
+
+#define SMB2_MAGIC 0x424D53FE /* 0xFE 'S' 'M' 'B' */
+
+/* the dialect we support */
+#define SMB2_DIALECT_REVISION 0x202
+
+/* SMB2 negotiate security_mode */
+#define SMB2_NEGOTIATE_SIGNING_ENABLED 0x01
+#define SMB2_NEGOTIATE_SIGNING_REQUIRED 0x02
+
+/* SMB2 capabilities - only 1 so far. I'm sure more will be added */
+#define SMB2_CAP_DFS 0x0
+/* so we can spot new caps as added */
+#define SMB2_CAP_ALL SMB2_CAP_DFS
+
+/* SMB2 share flags */
+#define SMB2_SHAREFLAG_MANUAL_CACHING 0x0000
+#define SMB2_SHAREFLAG_AUTO_CACHING 0x0010
+#define SMB2_SHAREFLAG_VDO_CACHING 0x0020
+#define SMB2_SHAREFLAG_NO_CACHING 0x0030
+#define SMB2_SHAREFLAG_DFS 0x0001
+#define SMB2_SHAREFLAG_DFS_ROOT 0x0002
+#define SMB2_SHAREFLAG_RESTRICT_EXCLUSIVE_OPENS 0x0100
+#define SMB2_SHAREFLAG_FORCE_SHARED_DELETE 0x0200
+#define SMB2_SHAREFLAG_ALLOW_NAMESPACE_CACHING 0x0400
+#define SMB2_SHAREFLAG_ACCESS_BASED_DIRECTORY_ENUM 0x0800
+#define SMB2_SHAREFLAG_ALL 0x0F33
+
+/* SMB2 create security flags */
+#define SMB2_SECURITY_DYNAMIC_TRACKING 0x01
+#define SMB2_SECURITY_EFFECTIVE_ONLY 0x02
+
+/* SMB2 requested oplock levels */
+#define SMB2_OPLOCK_LEVEL_NONE 0x00
+#define SMB2_OPLOCK_LEVEL_II 0x01
+#define SMB2_OPLOCK_LEVEL_EXCLUSIVE 0x08
+#define SMB2_OPLOCK_LEVEL_BATCH 0x09
+
+/* SMB2 impersonation levels */
+#define SMB2_IMPERSONATION_ANONYMOUS 0x00
+#define SMB2_IMPERSONATION_IDENTIFICATION 0x01
+#define SMB2_IMPERSONATION_IMPERSONATION 0x02
+#define SMB2_IMPERSONATION_DELEGATE 0x03
+
+/* SMB2 create tags */
+#define SMB2_CREATE_TAG_EXTA "ExtA"
+#define SMB2_CREATE_TAG_MXAC "MxAc"
+#define SMB2_CREATE_TAG_SECD "SecD"
+#define SMB2_CREATE_TAG_DHNQ "DHnQ"
+#define SMB2_CREATE_TAG_DHNC "DHnC"
+#define SMB2_CREATE_TAG_ALSI "AlSi"
+#define SMB2_CREATE_TAG_TWRP "TWrp"
+#define SMB2_CREATE_TAG_QFID "QFid"
+
+/* SMB2 Create ignore some more create_options */
+#define SMB2_CREATE_OPTIONS_NOT_SUPPORTED_MASK (NTCREATEX_OPTIONS_TREE_CONNECTION | \
+ NTCREATEX_OPTIONS_OPFILTER)
+
+/*
+ check that a body has the expected size
+*/
+#define SMB2_CHECK_PACKET_RECV(req, size, dynamic) do { \
+ size_t is_size = req->in.body_size; \
+ uint16_t field_size = SVAL(req->in.body, 0); \
+ uint16_t want_size = ((dynamic)?(size)+1:(size)); \
+ if (is_size < (size)) { \
+ DEBUG(0,("%s: buffer too small 0x%x. Expected 0x%x\n", \
+ __location__, (unsigned)is_size, (unsigned)want_size)); \
+ return NT_STATUS_BUFFER_TOO_SMALL; \
+ }\
+ if (field_size != want_size) { \
+ DEBUG(0,("%s: unexpected fixed body size 0x%x. Expected 0x%x\n", \
+ __location__, (unsigned)field_size, (unsigned)want_size)); \
+ return NT_STATUS_INVALID_PARAMETER; \
+ } \
+} while (0)
+
+#endif
diff --git a/source4/libcli/smb2/smb2_calls.h b/source4/libcli/smb2/smb2_calls.h
new file mode 100644
index 0000000000..b89770fbe6
--- /dev/null
+++ b/source4/libcli/smb2/smb2_calls.h
@@ -0,0 +1,111 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client calls
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "libcli/raw/interfaces.h"
+
+struct smb2_negprot {
+ struct {
+ uint16_t dialect_count; /* size of dialects array */
+ uint16_t security_mode; /* 0==signing disabled
+ 1==signing enabled */
+ uint16_t reserved;
+ uint32_t capabilities;
+ struct GUID client_guid;
+ NTTIME start_time;
+ uint16_t *dialects;
+ } in;
+ struct {
+ /* static body buffer 64 (0x40) bytes */
+ /* uint16_t buffer_code; 0x41 = 0x40 + 1 */
+ uint16_t security_mode; /* SMB2_NEGOTIATE_SIGNING_* */
+ uint16_t dialect_revision;
+ uint16_t reserved;
+ struct GUID server_guid;
+ uint32_t capabilities;
+ uint32_t max_transact_size;
+ uint32_t max_read_size;
+ uint32_t max_write_size;
+ NTTIME system_time;
+ NTTIME server_start_time;
+ /* uint16_t secblob_ofs */
+ /* uint16_t secblob_size */
+ uint32_t reserved2;
+ DATA_BLOB secblob;
+ } out;
+};
+
+/* getinfo classes */
+#define SMB2_GETINFO_FILE 0x01
+#define SMB2_GETINFO_FS 0x02
+#define SMB2_GETINFO_SECURITY 0x03
+#define SMB2_GETINFO_QUOTA 0x04
+
+#define SMB2_GETINFO_ADD_OWNER_SECURITY 0x01
+#define SMB2_GETINFO_ADD_GROUP_SECURITY 0x02
+#define SMB2_GETINFO_ADD_DACL_SECURITY 0x04
+#define SMB2_GETINFO_ADD_SACL_SECURITY 0x08
+#define SMB2_GETINFO_ADD_LABEL_SECURITY 0x10
+
+/* NOTE! the getinfo fs and file levels exactly match up with the
+ 'passthru' SMB levels, which are levels >= 1000. The SMB2 client
+ lib uses the names from the libcli/raw/ library */
+
+struct smb2_getinfo {
+ struct {
+ /* static body buffer 40 (0x28) bytes */
+ /* uint16_t buffer_code; 0x29 = 0x28 + 1 */
+ uint8_t info_type;
+ uint8_t info_class;
+ uint32_t output_buffer_length;
+ /* uint32_t input_buffer_offset; */
+ uint32_t reserved;
+ uint32_t input_buffer_length;
+ uint32_t additional_information; /* SMB2_GETINFO_ADD_* */
+ uint32_t getinfo_flags; /* level specific */
+ union smb_handle file;
+ DATA_BLOB blob;
+ } in;
+
+ struct {
+ /* static body buffer 8 (0x08) bytes */
+ /* uint16_t buffer_code; 0x09 = 0x08 + 1 */
+ /* uint16_t blob_ofs; */
+ /* uint16_t blob_size; */
+
+ /* dynamic body */
+ DATA_BLOB blob;
+ } out;
+};
+
+struct smb2_setinfo {
+ struct {
+ uint16_t level;
+ uint32_t flags;
+ union smb_handle file;
+ DATA_BLOB blob;
+ } in;
+};
+
+struct cli_credentials;
+struct tevent_context;
+struct resolve_context;
+struct gensec_settings;
+#include "libcli/smb2/smb2_proto.h"
diff --git a/source4/libcli/smb2/tcon.c b/source4/libcli/smb2/tcon.c
new file mode 100644
index 0000000000..ec7152b264
--- /dev/null
+++ b/source4/libcli/smb2/tcon.c
@@ -0,0 +1,112 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client tree handling
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/*
+ initialise a smb2_session structure
+ */
+struct smb2_tree *smb2_tree_init(struct smb2_session *session,
+ TALLOC_CTX *parent_ctx, bool primary)
+{
+ struct smb2_tree *tree;
+
+ tree = talloc_zero(parent_ctx, struct smb2_tree);
+ if (!session) {
+ return NULL;
+ }
+ if (primary) {
+ tree->session = talloc_steal(tree, session);
+ } else {
+ tree->session = talloc_reference(tree, session);
+ }
+ return tree;
+}
+
+/*
+ send a tree connect
+*/
+struct smb2_request *smb2_tree_connect_send(struct smb2_tree *tree,
+ struct smb2_tree_connect *io)
+{
+ struct smb2_request *req;
+ NTSTATUS status;
+
+ req = smb2_request_init(tree->session->transport, SMB2_OP_TCON,
+ 0x08, true, 0);
+ if (req == NULL) return NULL;
+
+ SBVAL(req->out.hdr, SMB2_HDR_SESSION_ID, tree->session->uid);
+ req->session = tree->session;
+
+ SSVAL(req->out.body, 0x02, io->in.reserved);
+ status = smb2_push_o16s16_string(&req->out, 0x04, io->in.path);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+
+ smb2_transport_send(req);
+
+ return req;
+}
+
+
+/*
+ recv a tree connect reply
+*/
+NTSTATUS smb2_tree_connect_recv(struct smb2_request *req, struct smb2_tree_connect *io)
+{
+ if (!smb2_request_receive(req) ||
+ smb2_request_is_error(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x10, false);
+
+ io->out.tid = IVAL(req->in.hdr, SMB2_HDR_TID);
+
+ io->out.share_type = CVAL(req->in.body, 0x02);
+ io->out.reserved = CVAL(req->in.body, 0x03);
+ io->out.flags = IVAL(req->in.body, 0x04);
+ io->out.capabilities= IVAL(req->in.body, 0x08);
+ io->out.access_mask = IVAL(req->in.body, 0x0C);
+
+ if (io->out.capabilities & ~SMB2_CAP_ALL) {
+ DEBUG(0,("Unknown capabilities mask 0x%x\n", io->out.capabilities));
+ }
+ if (io->out.flags & ~SMB2_SHAREFLAG_ALL) {
+ DEBUG(0,("Unknown tcon shareflag 0x%x\n", io->out.flags));
+ }
+
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync tree connect request
+*/
+NTSTATUS smb2_tree_connect(struct smb2_tree *tree, struct smb2_tree_connect *io)
+{
+ struct smb2_request *req = smb2_tree_connect_send(tree, io);
+ return smb2_tree_connect_recv(req, io);
+}
diff --git a/source4/libcli/smb2/tdis.c b/source4/libcli/smb2/tdis.c
new file mode 100644
index 0000000000..5adad9dc6e
--- /dev/null
+++ b/source4/libcli/smb2/tdis.c
@@ -0,0 +1,65 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client tdis handling
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/*
+ send a tdis request
+*/
+struct smb2_request *smb2_tdis_send(struct smb2_tree *tree)
+{
+ struct smb2_request *req;
+
+ req = smb2_request_init_tree(tree, SMB2_OP_TDIS, 0x04, false, 0);
+ if (req == NULL) return NULL;
+
+ SSVAL(req->out.body, 0x02, 0);
+
+ smb2_transport_send(req);
+
+ return req;
+}
+
+
+/*
+ recv a tdis reply
+*/
+NTSTATUS smb2_tdis_recv(struct smb2_request *req)
+{
+ if (!smb2_request_receive(req) ||
+ !smb2_request_is_ok(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x04, false);
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync tdis request
+*/
+NTSTATUS smb2_tdis(struct smb2_tree *tree)
+{
+ struct smb2_request *req = smb2_tdis_send(tree);
+ return smb2_tdis_recv(req);
+}
diff --git a/source4/libcli/smb2/transport.c b/source4/libcli/smb2/transport.c
new file mode 100644
index 0000000000..e112544c62
--- /dev/null
+++ b/source4/libcli/smb2/transport.c
@@ -0,0 +1,417 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client transport context management functions
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "lib/socket/socket.h"
+#include "lib/events/events.h"
+#include "lib/stream/packet.h"
+#include "../lib/util/dlinklist.h"
+
+
+/*
+ an event has happened on the socket
+*/
+static void smb2_transport_event_handler(struct tevent_context *ev,
+ struct tevent_fd *fde,
+ uint16_t flags, void *private_data)
+{
+ struct smb2_transport *transport = talloc_get_type(private_data,
+ struct smb2_transport);
+ if (flags & EVENT_FD_READ) {
+ packet_recv(transport->packet);
+ return;
+ }
+ if (flags & EVENT_FD_WRITE) {
+ packet_queue_run(transport->packet);
+ }
+}
+
+/*
+ destroy a transport
+ */
+static int transport_destructor(struct smb2_transport *transport)
+{
+ smb2_transport_dead(transport, NT_STATUS_LOCAL_DISCONNECT);
+ return 0;
+}
+
+
+/*
+ handle receive errors
+*/
+static void smb2_transport_error(void *private_data, NTSTATUS status)
+{
+ struct smb2_transport *transport = talloc_get_type(private_data,
+ struct smb2_transport);
+ smb2_transport_dead(transport, status);
+}
+
+static NTSTATUS smb2_transport_finish_recv(void *private_data, DATA_BLOB blob);
+
+/*
+ create a transport structure based on an established socket
+*/
+struct smb2_transport *smb2_transport_init(struct smbcli_socket *sock,
+ TALLOC_CTX *parent_ctx,
+ struct smbcli_options *options)
+{
+ struct smb2_transport *transport;
+
+ transport = talloc_zero(parent_ctx, struct smb2_transport);
+ if (!transport) return NULL;
+
+ transport->socket = talloc_steal(transport, sock);
+ transport->options = *options;
+
+ /* setup the stream -> packet parser */
+ transport->packet = packet_init(transport);
+ if (transport->packet == NULL) {
+ talloc_free(transport);
+ return NULL;
+ }
+ packet_set_private(transport->packet, transport);
+ packet_set_socket(transport->packet, transport->socket->sock);
+ packet_set_callback(transport->packet, smb2_transport_finish_recv);
+ packet_set_full_request(transport->packet, packet_full_request_nbt);
+ packet_set_error_handler(transport->packet, smb2_transport_error);
+ packet_set_event_context(transport->packet, transport->socket->event.ctx);
+ packet_set_nofree(transport->packet);
+
+ /* take over event handling from the socket layer - it only
+ handles events up until we are connected */
+ talloc_free(transport->socket->event.fde);
+ transport->socket->event.fde = event_add_fd(transport->socket->event.ctx,
+ transport->socket,
+ socket_get_fd(transport->socket->sock),
+ EVENT_FD_READ,
+ smb2_transport_event_handler,
+ transport);
+
+ packet_set_fde(transport->packet, transport->socket->event.fde);
+ packet_set_serialise(transport->packet);
+
+ talloc_set_destructor(transport, transport_destructor);
+
+ return transport;
+}
+
+/*
+ mark the transport as dead
+*/
+void smb2_transport_dead(struct smb2_transport *transport, NTSTATUS status)
+{
+ smbcli_sock_dead(transport->socket);
+
+ if (NT_STATUS_EQUAL(NT_STATUS_UNSUCCESSFUL, status)) {
+ status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+
+ /* kill all pending receives */
+ while (transport->pending_recv) {
+ struct smb2_request *req = transport->pending_recv;
+ req->state = SMB2_REQUEST_ERROR;
+ req->status = status;
+ DLIST_REMOVE(transport->pending_recv, req);
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+ }
+}
+
+static NTSTATUS smb2_handle_oplock_break(struct smb2_transport *transport,
+ const DATA_BLOB *blob)
+{
+ uint8_t *hdr;
+ uint16_t opcode;
+
+ hdr = blob->data+NBT_HDR_SIZE;
+
+ if (blob->length < (SMB2_MIN_SIZE+0x18)) {
+ DEBUG(1,("Discarding smb2 oplock reply of size %u\n",
+ (unsigned)blob->length));
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ opcode = SVAL(hdr, SMB2_HDR_OPCODE);
+
+ if (opcode != SMB2_OP_BREAK) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ if (transport->oplock.handler) {
+ uint8_t *body = hdr+SMB2_HDR_BODY;
+ struct smb2_handle h;
+ uint8_t level;
+
+ level = CVAL(body, 0x02);
+ smb2_pull_handle(body+0x08, &h);
+
+ transport->oplock.handler(transport, &h, level,
+ transport->oplock.private_data);
+ } else {
+ DEBUG(5,("Got SMB2 oplock break with no handler\n"));
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ we have a full request in our receive buffer - match it to a pending request
+ and process
+ */
+static NTSTATUS smb2_transport_finish_recv(void *private_data, DATA_BLOB blob)
+{
+ struct smb2_transport *transport = talloc_get_type(private_data,
+ struct smb2_transport);
+ uint8_t *buffer, *hdr;
+ int len;
+ struct smb2_request *req = NULL;
+ uint64_t seqnum;
+ uint32_t flags;
+ uint16_t buffer_code;
+ uint32_t dynamic_size;
+ uint32_t i;
+ NTSTATUS status;
+
+ buffer = blob.data;
+ len = blob.length;
+
+ hdr = buffer+NBT_HDR_SIZE;
+
+ if (len < SMB2_MIN_SIZE) {
+ DEBUG(1,("Discarding smb2 reply of size %d\n", len));
+ goto error;
+ }
+
+ flags = IVAL(hdr, SMB2_HDR_FLAGS);
+ seqnum = BVAL(hdr, SMB2_HDR_MESSAGE_ID);
+
+ /* see MS-SMB2 3.2.5.19 */
+ if (seqnum == UINT64_MAX) {
+ return smb2_handle_oplock_break(transport, &blob);
+ }
+
+ /* match the incoming request against the list of pending requests */
+ for (req=transport->pending_recv; req; req=req->next) {
+ if (req->seqnum == seqnum) break;
+ }
+
+ if (!req) {
+ DEBUG(1,("Discarding unmatched reply with seqnum 0x%llx op %d\n",
+ (long long)seqnum, SVAL(hdr, SMB2_HDR_OPCODE)));
+ goto error;
+ }
+
+ /* fill in the 'in' portion of the matching request */
+ req->in.buffer = buffer;
+ talloc_steal(req, buffer);
+ req->in.size = len;
+ req->in.allocated = req->in.size;
+
+ req->in.hdr = hdr;
+ req->in.body = hdr+SMB2_HDR_BODY;
+ req->in.body_size = req->in.size - (SMB2_HDR_BODY+NBT_HDR_SIZE);
+ req->status = NT_STATUS(IVAL(hdr, SMB2_HDR_STATUS));
+
+ if ((flags & SMB2_HDR_FLAG_ASYNC) &&
+ NT_STATUS_EQUAL(req->status, STATUS_PENDING)) {
+ req->cancel.can_cancel = true;
+ req->cancel.pending_id = IVAL(hdr, SMB2_HDR_PID);
+ for (i=0; i< req->cancel.do_cancel; i++) {
+ smb2_cancel(req);
+ }
+ talloc_free(buffer);
+ return NT_STATUS_OK;
+ }
+
+ if (req->session && req->session->signing_active) {
+ status = smb2_check_signature(&req->in,
+ req->session->session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ /* the spec says to ignore packets with a bad signature */
+ talloc_free(buffer);
+ return status;
+ }
+ }
+
+ buffer_code = SVAL(req->in.body, 0);
+ req->in.body_fixed = (buffer_code & ~1);
+ req->in.dynamic = NULL;
+ dynamic_size = req->in.body_size - req->in.body_fixed;
+ if (dynamic_size != 0 && (buffer_code & 1)) {
+ req->in.dynamic = req->in.body + req->in.body_fixed;
+ if (smb2_oob(&req->in, req->in.dynamic, dynamic_size)) {
+ DEBUG(1,("SMB2 request invalid dynamic size 0x%x\n",
+ dynamic_size));
+ goto error;
+ }
+ }
+
+ smb2_setup_bufinfo(req);
+
+ DEBUG(2, ("SMB2 RECV seqnum=0x%llx\n", (long long)req->seqnum));
+ dump_data(5, req->in.body, req->in.body_size);
+
+ /* if this request has an async handler then call that to
+ notify that the reply has been received. This might destroy
+ the request so it must happen last */
+ DLIST_REMOVE(transport->pending_recv, req);
+ req->state = SMB2_REQUEST_DONE;
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+ return NT_STATUS_OK;
+
+error:
+ dump_data(5, buffer, len);
+ if (req) {
+ DLIST_REMOVE(transport->pending_recv, req);
+ req->state = SMB2_REQUEST_ERROR;
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+ } else {
+ talloc_free(buffer);
+ }
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+/*
+ handle timeouts of individual smb requests
+*/
+static void smb2_timeout_handler(struct tevent_context *ev, struct tevent_timer *te,
+ struct timeval t, void *private_data)
+{
+ struct smb2_request *req = talloc_get_type(private_data, struct smb2_request);
+
+ if (req->state == SMB2_REQUEST_RECV) {
+ DLIST_REMOVE(req->transport->pending_recv, req);
+ }
+ req->status = NT_STATUS_IO_TIMEOUT;
+ req->state = SMB2_REQUEST_ERROR;
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+}
+
+
+/*
+ destroy a request
+*/
+static int smb2_request_destructor(struct smb2_request *req)
+{
+ if (req->state == SMB2_REQUEST_RECV) {
+ DLIST_REMOVE(req->transport->pending_recv, req);
+ }
+ return 0;
+}
+
+
+/*
+ put a request into the send queue
+*/
+void smb2_transport_send(struct smb2_request *req)
+{
+ DATA_BLOB blob;
+ NTSTATUS status;
+
+ _smb2_setlen(req->out.buffer, req->out.size - NBT_HDR_SIZE);
+
+ DEBUG(2, ("SMB2 send seqnum=0x%llx\n", (long long)req->seqnum));
+ dump_data(5, req->out.body, req->out.body_size);
+
+ /* check if the transport is dead */
+ if (req->transport->socket->sock == NULL) {
+ req->state = SMB2_REQUEST_ERROR;
+ req->status = NT_STATUS_NET_WRITE_FAULT;
+ return;
+ }
+
+ /* possibly sign the message */
+ if (req->session && req->session->signing_active) {
+ status = smb2_sign_message(&req->out, req->session->session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ req->state = SMB2_REQUEST_ERROR;
+ req->status = status;
+ return;
+ }
+ }
+
+ blob = data_blob_const(req->out.buffer, req->out.size);
+ status = packet_send(req->transport->packet, blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ req->state = SMB2_REQUEST_ERROR;
+ req->status = status;
+ return;
+ }
+
+ req->state = SMB2_REQUEST_RECV;
+ DLIST_ADD(req->transport->pending_recv, req);
+
+ /* add a timeout */
+ if (req->transport->options.request_timeout) {
+ event_add_timed(req->transport->socket->event.ctx, req,
+ timeval_current_ofs(req->transport->options.request_timeout, 0),
+ smb2_timeout_handler, req);
+ }
+
+ talloc_set_destructor(req, smb2_request_destructor);
+}
+
+static void idle_handler(struct tevent_context *ev,
+ struct tevent_timer *te, struct timeval t, void *private_data)
+{
+ struct smb2_transport *transport = talloc_get_type(private_data,
+ struct smb2_transport);
+ struct timeval next = timeval_add(&t, 0, transport->idle.period);
+ transport->socket->event.te = event_add_timed(transport->socket->event.ctx,
+ transport,
+ next,
+ idle_handler, transport);
+ transport->idle.func(transport, transport->idle.private_data);
+}
+
+/*
+ setup the idle handler for a transport
+ the period is in microseconds
+*/
+void smb2_transport_idle_handler(struct smb2_transport *transport,
+ void (*idle_func)(struct smb2_transport *, void *),
+ uint64_t period,
+ void *private_data)
+{
+ transport->idle.func = idle_func;
+ transport->idle.private_data = private_data;
+ transport->idle.period = period;
+
+ if (transport->socket->event.te != NULL) {
+ talloc_free(transport->socket->event.te);
+ }
+
+ transport->socket->event.te = event_add_timed(transport->socket->event.ctx,
+ transport,
+ timeval_current_ofs(0, period),
+ idle_handler, transport);
+}
diff --git a/source4/libcli/smb2/util.c b/source4/libcli/smb2/util.c
new file mode 100644
index 0000000000..a360d8fbdf
--- /dev/null
+++ b/source4/libcli/smb2/util.c
@@ -0,0 +1,216 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client utility functions
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "libcli/smb_composite/smb_composite.h"
+
+/*
+ simple close wrapper with SMB2
+*/
+NTSTATUS smb2_util_close(struct smb2_tree *tree, struct smb2_handle h)
+{
+ struct smb2_close c;
+
+ ZERO_STRUCT(c);
+ c.in.file.handle = h;
+
+ return smb2_close(tree, &c);
+}
+
+/*
+ unlink a file with SMB2
+*/
+NTSTATUS smb2_util_unlink(struct smb2_tree *tree, const char *fname)
+{
+ union smb_unlink io;
+
+ ZERO_STRUCT(io);
+ io.unlink.in.pattern = fname;
+
+ return smb2_composite_unlink(tree, &io);
+}
+
+
+/*
+ rmdir with SMB2
+*/
+NTSTATUS smb2_util_rmdir(struct smb2_tree *tree, const char *dname)
+{
+ struct smb_rmdir io;
+
+ ZERO_STRUCT(io);
+ io.in.path = dname;
+
+ return smb2_composite_rmdir(tree, &io);
+}
+
+
+/*
+ mkdir with SMB2
+*/
+NTSTATUS smb2_util_mkdir(struct smb2_tree *tree, const char *dname)
+{
+ union smb_mkdir io;
+
+ ZERO_STRUCT(io);
+ io.mkdir.level = RAW_MKDIR_MKDIR;
+ io.mkdir.in.path = dname;
+
+ return smb2_composite_mkdir(tree, &io);
+}
+
+
+/*
+ set file attribute with SMB2
+*/
+NTSTATUS smb2_util_setatr(struct smb2_tree *tree, const char *name, uint32_t attrib)
+{
+ union smb_setfileinfo io;
+
+ ZERO_STRUCT(io);
+ io.basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION;
+ io.basic_info.in.file.path = name;
+ io.basic_info.in.attrib = attrib;
+
+ return smb2_composite_setpathinfo(tree, &io);
+}
+
+
+
+
+/*
+ recursively descend a tree deleting all files
+ returns the number of files deleted, or -1 on error
+*/
+int smb2_deltree(struct smb2_tree *tree, const char *dname)
+{
+ NTSTATUS status;
+ uint32_t total_deleted = 0;
+ uint_t count, i;
+ union smb_search_data *list;
+ TALLOC_CTX *tmp_ctx = talloc_new(tree);
+ struct smb2_find f;
+ struct smb2_create create_parm;
+
+ /* it might be a file */
+ status = smb2_util_unlink(tree, dname);
+ if (NT_STATUS_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ return 1;
+ }
+ if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_NOT_FOUND) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_FILE)) {
+ talloc_free(tmp_ctx);
+ return 0;
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_CANNOT_DELETE)) {
+ /* it could be read-only */
+ status = smb2_util_setatr(tree, dname, FILE_ATTRIBUTE_NORMAL);
+ status = smb2_util_unlink(tree, dname);
+ }
+ if (NT_STATUS_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ return 1;
+ }
+
+ ZERO_STRUCT(create_parm);
+ create_parm.in.desired_access = SEC_FILE_READ_DATA;
+ create_parm.in.share_access =
+ NTCREATEX_SHARE_ACCESS_READ|
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ create_parm.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
+ create_parm.in.create_disposition = NTCREATEX_DISP_OPEN;
+ create_parm.in.fname = dname;
+
+ status = smb2_create(tree, tmp_ctx, &create_parm);
+ if (NT_STATUS_IS_ERR(status)) {
+ DEBUG(2,("Failed to open %s - %s\n", dname, nt_errstr(status)));
+ talloc_free(tmp_ctx);
+ return -1;
+ }
+
+
+ ZERO_STRUCT(f);
+ f.in.file.handle = create_parm.out.file.handle;
+ f.in.max_response_size = 0x10000;
+ f.in.level = SMB2_FIND_NAME_INFO;
+ f.in.pattern = "*";
+
+ status = smb2_find_level(tree, tmp_ctx, &f, &count, &list);
+ if (NT_STATUS_IS_ERR(status)) {
+ DEBUG(2,("Failed to list %s - %s\n",
+ dname, nt_errstr(status)));
+ smb2_util_close(tree, create_parm.out.file.handle);
+ talloc_free(tmp_ctx);
+ return -1;
+ }
+
+ for (i=0;i<count;i++) {
+ char *name;
+ if (strcmp(".", list[i].name_info.name.s) == 0 ||
+ strcmp("..", list[i].name_info.name.s) == 0) {
+ continue;
+ }
+ name = talloc_asprintf(tmp_ctx, "%s\\%s", dname, list[i].name_info.name.s);
+ status = smb2_util_unlink(tree, name);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_CANNOT_DELETE)) {
+ /* it could be read-only */
+ status = smb2_util_setatr(tree, name, FILE_ATTRIBUTE_NORMAL);
+ status = smb2_util_unlink(tree, name);
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) {
+ int ret;
+ ret = smb2_deltree(tree, name);
+ if (ret > 0) total_deleted += ret;
+ }
+ talloc_free(name);
+ if (NT_STATUS_IS_OK(status)) {
+ total_deleted++;
+ }
+ }
+
+ smb2_util_close(tree, create_parm.out.file.handle);
+
+ status = smb2_util_rmdir(tree, dname);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_CANNOT_DELETE)) {
+ /* it could be read-only */
+ status = smb2_util_setatr(tree, dname, FILE_ATTRIBUTE_NORMAL);
+ status = smb2_util_rmdir(tree, dname);
+ }
+
+ if (NT_STATUS_IS_ERR(status)) {
+ DEBUG(2,("Failed to delete %s - %s\n",
+ dname, nt_errstr(status)));
+ talloc_free(tmp_ctx);
+ return -1;
+ }
+
+ talloc_free(tmp_ctx);
+
+ return total_deleted;
+}
diff --git a/source4/libcli/smb2/write.c b/source4/libcli/smb2/write.c
new file mode 100644
index 0000000000..bc283370d7
--- /dev/null
+++ b/source4/libcli/smb2/write.c
@@ -0,0 +1,81 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client write call
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/*
+ send a write request
+*/
+struct smb2_request *smb2_write_send(struct smb2_tree *tree, struct smb2_write *io)
+{
+ NTSTATUS status;
+ struct smb2_request *req;
+
+ req = smb2_request_init_tree(tree, SMB2_OP_WRITE, 0x30, true, io->in.data.length);
+ if (req == NULL) return NULL;
+
+ status = smb2_push_o16s32_blob(&req->out, 0x02, io->in.data);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+
+ SBVAL(req->out.body, 0x08, io->in.offset);
+ smb2_push_handle(req->out.body+0x10, &io->in.file.handle);
+
+ SBVAL(req->out.body, 0x20, io->in.unknown1);
+ SBVAL(req->out.body, 0x28, io->in.unknown2);
+
+ smb2_transport_send(req);
+
+ return req;
+}
+
+
+/*
+ recv a write reply
+*/
+NTSTATUS smb2_write_recv(struct smb2_request *req, struct smb2_write *io)
+{
+ if (!smb2_request_receive(req) ||
+ smb2_request_is_error(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x10, true);
+
+ io->out._pad = SVAL(req->in.body, 0x02);
+ io->out.nwritten = IVAL(req->in.body, 0x04);
+ io->out.unknown1 = BVAL(req->in.body, 0x08);
+
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync write request
+*/
+NTSTATUS smb2_write(struct smb2_tree *tree, struct smb2_write *io)
+{
+ struct smb2_request *req = smb2_write_send(tree, io);
+ return smb2_write_recv(req, io);
+}
diff --git a/source4/libcli/smb_composite/appendacl.c b/source4/libcli/smb_composite/appendacl.c
new file mode 100644
index 0000000000..69ed62a106
--- /dev/null
+++ b/source4/libcli/smb_composite/appendacl.c
@@ -0,0 +1,313 @@
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/composite/composite.h"
+#include "libcli/security/security.h"
+#include "libcli/smb_composite/smb_composite.h"
+
+/* the stages of this call */
+enum appendacl_stage {APPENDACL_OPENPATH, APPENDACL_GET,
+ APPENDACL_SET, APPENDACL_GETAGAIN, APPENDACL_CLOSEPATH};
+
+static void appendacl_handler(struct smbcli_request *req);
+
+struct appendacl_state {
+ enum appendacl_stage stage;
+ struct smb_composite_appendacl *io;
+
+ union smb_open *io_open;
+ union smb_setfileinfo *io_setfileinfo;
+ union smb_fileinfo *io_fileinfo;
+
+ struct smbcli_request *req;
+};
+
+
+static NTSTATUS appendacl_open(struct composite_context *c,
+ struct smb_composite_appendacl *io)
+{
+ struct appendacl_state *state = talloc_get_type(c->private_data, struct appendacl_state);
+ struct smbcli_tree *tree = state->req->tree;
+ NTSTATUS status;
+
+ status = smb_raw_open_recv(state->req, c, state->io_open);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ /* setup structures for getting fileinfo */
+ state->io_fileinfo = talloc(c, union smb_fileinfo);
+ NT_STATUS_HAVE_NO_MEMORY(state->io_fileinfo);
+
+ state->io_fileinfo->query_secdesc.level = RAW_FILEINFO_SEC_DESC;
+ state->io_fileinfo->query_secdesc.in.file.fnum = state->io_open->ntcreatex.out.file.fnum;
+ state->io_fileinfo->query_secdesc.in.secinfo_flags = SECINFO_DACL;
+
+ state->req = smb_raw_fileinfo_send(tree, state->io_fileinfo);
+ NT_STATUS_HAVE_NO_MEMORY(state->req);
+
+ /* set the handler */
+ state->req->async.fn = appendacl_handler;
+ state->req->async.private_data = c;
+ state->stage = APPENDACL_GET;
+
+ talloc_free (state->io_open);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS appendacl_get(struct composite_context *c,
+ struct smb_composite_appendacl *io)
+{
+ struct appendacl_state *state = talloc_get_type(c->private_data, struct appendacl_state);
+ struct smbcli_tree *tree = state->req->tree;
+ int i;
+ NTSTATUS status;
+
+ status = smb_raw_fileinfo_recv(state->req, state->io_fileinfo, state->io_fileinfo);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ /* setup structures for setting fileinfo */
+ state->io_setfileinfo = talloc(c, union smb_setfileinfo);
+ NT_STATUS_HAVE_NO_MEMORY(state->io_setfileinfo);
+
+ state->io_setfileinfo->set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
+ state->io_setfileinfo->set_secdesc.in.file.fnum = state->io_fileinfo->query_secdesc.in.file.fnum;
+
+ state->io_setfileinfo->set_secdesc.in.secinfo_flags = SECINFO_DACL;
+ state->io_setfileinfo->set_secdesc.in.sd = state->io_fileinfo->query_secdesc.out.sd;
+ talloc_steal(state->io_setfileinfo, state->io_setfileinfo->set_secdesc.in.sd);
+
+ /* append all aces from io->in.sd->dacl to new security descriptor */
+ if (io->in.sd->dacl != NULL) {
+ for (i = 0; i < io->in.sd->dacl->num_aces; i++) {
+ security_descriptor_dacl_add(state->io_setfileinfo->set_secdesc.in.sd,
+ &(io->in.sd->dacl->aces[i]));
+ }
+ }
+
+ status = smb_raw_setfileinfo(tree, state->io_setfileinfo);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ state->req = smb_raw_setfileinfo_send(tree, state->io_setfileinfo);
+ NT_STATUS_HAVE_NO_MEMORY(state->req);
+
+ /* call handler when done setting new security descriptor on file */
+ state->req->async.fn = appendacl_handler;
+ state->req->async.private_data = c;
+ state->stage = APPENDACL_SET;
+
+ talloc_free (state->io_fileinfo);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS appendacl_set(struct composite_context *c,
+ struct smb_composite_appendacl *io)
+{
+ struct appendacl_state *state = talloc_get_type(c->private_data, struct appendacl_state);
+ struct smbcli_tree *tree = state->req->tree;
+ NTSTATUS status;
+
+ status = smbcli_request_simple_recv(state->req);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ /* setup structures for getting fileinfo */
+ state->io_fileinfo = talloc(c, union smb_fileinfo);
+ NT_STATUS_HAVE_NO_MEMORY(state->io_fileinfo);
+
+
+ state->io_fileinfo->query_secdesc.level = RAW_FILEINFO_SEC_DESC;
+ state->io_fileinfo->query_secdesc.in.file.fnum = state->io_setfileinfo->set_secdesc.in.file.fnum;
+ state->io_fileinfo->query_secdesc.in.secinfo_flags = SECINFO_DACL;
+
+ state->req = smb_raw_fileinfo_send(tree, state->io_fileinfo);
+ NT_STATUS_HAVE_NO_MEMORY(state->req);
+
+ /* set the handler */
+ state->req->async.fn = appendacl_handler;
+ state->req->async.private_data = c;
+ state->stage = APPENDACL_GETAGAIN;
+
+ talloc_free (state->io_setfileinfo);
+
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS appendacl_getagain(struct composite_context *c,
+ struct smb_composite_appendacl *io)
+{
+ struct appendacl_state *state = talloc_get_type(c->private_data, struct appendacl_state);
+ struct smbcli_tree *tree = state->req->tree;
+ union smb_close *io_close;
+ NTSTATUS status;
+
+ status = smb_raw_fileinfo_recv(state->req, c, state->io_fileinfo);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ io->out.sd = state->io_fileinfo->query_secdesc.out.sd;
+
+ /* setup structures for close */
+ io_close = talloc(c, union smb_close);
+ NT_STATUS_HAVE_NO_MEMORY(io_close);
+
+ io_close->close.level = RAW_CLOSE_CLOSE;
+ io_close->close.in.file.fnum = state->io_fileinfo->query_secdesc.in.file.fnum;
+ io_close->close.in.write_time = 0;
+
+ state->req = smb_raw_close_send(tree, io_close);
+ NT_STATUS_HAVE_NO_MEMORY(state->req);
+
+ /* call the handler */
+ state->req->async.fn = appendacl_handler;
+ state->req->async.private_data = c;
+ state->stage = APPENDACL_CLOSEPATH;
+
+ talloc_free (state->io_fileinfo);
+
+ return NT_STATUS_OK;
+}
+
+
+
+static NTSTATUS appendacl_close(struct composite_context *c,
+ struct smb_composite_appendacl *io)
+{
+ struct appendacl_state *state = talloc_get_type(c->private_data, struct appendacl_state);
+ NTSTATUS status;
+
+ status = smbcli_request_simple_recv(state->req);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ c->state = COMPOSITE_STATE_DONE;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ handler for completion of a sub-request in appendacl
+*/
+static void appendacl_handler(struct smbcli_request *req)
+{
+ struct composite_context *c = (struct composite_context *)req->async.private_data;
+ struct appendacl_state *state = talloc_get_type(c->private_data, struct appendacl_state);
+
+ /* when this handler is called, the stage indicates what
+ call has just finished */
+ switch (state->stage) {
+ case APPENDACL_OPENPATH:
+ c->status = appendacl_open(c, state->io);
+ break;
+
+ case APPENDACL_GET:
+ c->status = appendacl_get(c, state->io);
+ break;
+
+ case APPENDACL_SET:
+ c->status = appendacl_set(c, state->io);
+ break;
+
+ case APPENDACL_GETAGAIN:
+ c->status = appendacl_getagain(c, state->io);
+ break;
+
+ case APPENDACL_CLOSEPATH:
+ c->status = appendacl_close(c, state->io);
+ break;
+ }
+
+ /* We should get here if c->state >= SMBCLI_REQUEST_DONE */
+ if (!NT_STATUS_IS_OK(c->status)) {
+ c->state = COMPOSITE_STATE_ERROR;
+ }
+
+ if (c->state >= COMPOSITE_STATE_DONE &&
+ c->async.fn) {
+ c->async.fn(c);
+ }
+}
+
+
+/*
+ composite appendacl call - does an open followed by a number setfileinfo,
+ after that new acls are read with fileinfo, followed by a close
+*/
+struct composite_context *smb_composite_appendacl_send(struct smbcli_tree *tree,
+ struct smb_composite_appendacl *io)
+{
+ struct composite_context *c;
+ struct appendacl_state *state;
+
+ c = talloc_zero(tree, struct composite_context);
+ if (c == NULL) goto failed;
+
+ state = talloc(c, struct appendacl_state);
+ if (state == NULL) goto failed;
+
+ state->io = io;
+
+ c->private_data = state;
+ c->state = COMPOSITE_STATE_IN_PROGRESS;
+ c->event_ctx = tree->session->transport->socket->event.ctx;
+
+ /* setup structures for opening file */
+ state->io_open = talloc_zero(c, union smb_open);
+ if (state->io_open == NULL) goto failed;
+
+ state->io_open->ntcreatex.level = RAW_OPEN_NTCREATEX;
+ state->io_open->ntcreatex.in.root_fid = 0;
+ state->io_open->ntcreatex.in.flags = 0;
+ state->io_open->ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ state->io_open->ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ state->io_open->ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
+ state->io_open->ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
+ state->io_open->ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ state->io_open->ntcreatex.in.security_flags = 0;
+ state->io_open->ntcreatex.in.fname = io->in.fname;
+
+ /* send the open on its way */
+ state->req = smb_raw_open_send(tree, state->io_open);
+ if (state->req == NULL) goto failed;
+
+ /* setup the callback handler */
+ state->req->async.fn = appendacl_handler;
+ state->req->async.private_data = c;
+ state->stage = APPENDACL_OPENPATH;
+
+ return c;
+
+failed:
+ talloc_free(c);
+ return NULL;
+}
+
+
+/*
+ composite appendacl call - recv side
+*/
+NTSTATUS smb_composite_appendacl_recv(struct composite_context *c, TALLOC_CTX *mem_ctx)
+{
+ NTSTATUS status;
+
+ status = composite_wait(c);
+
+ if (NT_STATUS_IS_OK(status)) {
+ struct appendacl_state *state = talloc_get_type(c->private_data, struct appendacl_state);
+ state->io->out.sd = security_descriptor_copy (mem_ctx, state->io->out.sd);
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+
+/*
+ composite appendacl call - sync interface
+*/
+NTSTATUS smb_composite_appendacl(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ struct smb_composite_appendacl *io)
+{
+ struct composite_context *c = smb_composite_appendacl_send(tree, io);
+ return smb_composite_appendacl_recv(c, mem_ctx);
+}
+
diff --git a/source4/libcli/smb_composite/connect.c b/source4/libcli/smb_composite/connect.c
new file mode 100644
index 0000000000..3db777ddc8
--- /dev/null
+++ b/source4/libcli/smb_composite/connect.c
@@ -0,0 +1,520 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+/*
+ a composite API for making a full SMB connection
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/composite/composite.h"
+#include "libcli/smb_composite/smb_composite.h"
+#include "lib/events/events.h"
+#include "libcli/resolve/resolve.h"
+#include "auth/credentials/credentials.h"
+#include "librpc/gen_ndr/ndr_nbt.h"
+#include "param/param.h"
+
+/* the stages of this call */
+enum connect_stage {CONNECT_RESOLVE,
+ CONNECT_SOCKET,
+ CONNECT_SESSION_REQUEST,
+ CONNECT_NEGPROT,
+ CONNECT_SESSION_SETUP,
+ CONNECT_SESSION_SETUP_ANON,
+ CONNECT_TCON,
+ CONNECT_DONE
+};
+
+struct connect_state {
+ enum connect_stage stage;
+ struct smbcli_socket *sock;
+ struct smbcli_transport *transport;
+ struct smbcli_session *session;
+ struct smb_composite_connect *io;
+ union smb_tcon *io_tcon;
+ struct smb_composite_sesssetup *io_setup;
+ struct smbcli_request *req;
+ struct composite_context *creq;
+};
+
+
+static void request_handler(struct smbcli_request *);
+static void composite_handler(struct composite_context *);
+
+/*
+ a tree connect request has completed
+*/
+static NTSTATUS connect_tcon(struct composite_context *c,
+ struct smb_composite_connect *io)
+{
+ struct connect_state *state = talloc_get_type(c->private_data, struct connect_state);
+ NTSTATUS status;
+
+ status = smb_raw_tcon_recv(state->req, c, state->io_tcon);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ io->out.tree->tid = state->io_tcon->tconx.out.tid;
+ if (state->io_tcon->tconx.out.dev_type) {
+ io->out.tree->device = talloc_strdup(io->out.tree,
+ state->io_tcon->tconx.out.dev_type);
+ }
+ if (state->io_tcon->tconx.out.fs_type) {
+ io->out.tree->fs_type = talloc_strdup(io->out.tree,
+ state->io_tcon->tconx.out.fs_type);
+ }
+
+ state->stage = CONNECT_DONE;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ a session setup request with anonymous fallback has completed
+*/
+static NTSTATUS connect_session_setup_anon(struct composite_context *c,
+ struct smb_composite_connect *io)
+{
+ struct connect_state *state = talloc_get_type(c->private_data, struct connect_state);
+ NTSTATUS status;
+
+ status = smb_composite_sesssetup_recv(state->creq);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ io->out.anonymous_fallback_done = true;
+
+ state->session->vuid = state->io_setup->out.vuid;
+
+ /* setup for a tconx */
+ state->io_tcon = talloc(c, union smb_tcon);
+ NT_STATUS_HAVE_NO_MEMORY(state->io_tcon);
+
+ /* connect to a share using a tree connect */
+ state->io_tcon->generic.level = RAW_TCON_TCONX;
+ state->io_tcon->tconx.in.flags = 0;
+ state->io_tcon->tconx.in.password = data_blob(NULL, 0);
+
+ state->io_tcon->tconx.in.path = talloc_asprintf(state->io_tcon,
+ "\\\\%s\\%s",
+ io->in.called_name,
+ io->in.service);
+ NT_STATUS_HAVE_NO_MEMORY(state->io_tcon->tconx.in.path);
+ if (!io->in.service_type) {
+ state->io_tcon->tconx.in.device = "?????";
+ } else {
+ state->io_tcon->tconx.in.device = io->in.service_type;
+ }
+
+ state->req = smb_raw_tcon_send(io->out.tree, state->io_tcon);
+ NT_STATUS_HAVE_NO_MEMORY(state->req);
+ if (state->req->state == SMBCLI_REQUEST_ERROR) {
+ return state->req->status;
+ }
+
+ state->req->async.fn = request_handler;
+ state->req->async.private_data = c;
+ state->stage = CONNECT_TCON;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ a session setup request has completed
+*/
+static NTSTATUS connect_session_setup(struct composite_context *c,
+ struct smb_composite_connect *io)
+{
+ struct connect_state *state = talloc_get_type(c->private_data, struct connect_state);
+ NTSTATUS status;
+
+ status = smb_composite_sesssetup_recv(state->creq);
+
+ if (!NT_STATUS_IS_OK(status) &&
+ !cli_credentials_is_anonymous(state->io->in.credentials) &&
+ io->in.fallback_to_anonymous) {
+
+ state->io_setup->in.credentials = cli_credentials_init(state);
+ NT_STATUS_HAVE_NO_MEMORY(state->io_setup->in.credentials);
+ cli_credentials_set_workstation(state->io_setup->in.credentials,
+ cli_credentials_get_workstation(state->io->in.credentials),
+ CRED_SPECIFIED);
+ cli_credentials_set_anonymous(state->io_setup->in.credentials);
+
+ /* If the preceding attempt was with extended security, we
+ * have been given a uid in the NTLMSSP_CHALLENGE reply. This
+ * would lead to an invalid uid in the anonymous fallback */
+ state->session->vuid = 0;
+ data_blob_free(&state->session->user_session_key);
+ talloc_free(state->session->gensec);
+ state->session->gensec = NULL;
+
+ state->creq = smb_composite_sesssetup_send(state->session,
+ state->io_setup);
+ NT_STATUS_HAVE_NO_MEMORY(state->creq);
+ if (state->creq->state == COMPOSITE_STATE_ERROR) {
+ return state->creq->status;
+ }
+ state->creq->async.fn = composite_handler;
+ state->creq->async.private_data = c;
+ state->stage = CONNECT_SESSION_SETUP_ANON;
+
+ return NT_STATUS_OK;
+ }
+
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ state->session->vuid = state->io_setup->out.vuid;
+
+ /* If we don't have a remote share name then this indicates that
+ * we don't want to do a tree connect */
+ if (!io->in.service) {
+ state->stage = CONNECT_DONE;
+ return NT_STATUS_OK;
+ }
+
+ state->io_tcon = talloc(c, union smb_tcon);
+ NT_STATUS_HAVE_NO_MEMORY(state->io_tcon);
+
+ /* connect to a share using a tree connect */
+ state->io_tcon->generic.level = RAW_TCON_TCONX;
+ state->io_tcon->tconx.in.flags = 0;
+ state->io_tcon->tconx.in.password = data_blob(NULL, 0);
+
+ state->io_tcon->tconx.in.path = talloc_asprintf(state->io_tcon,
+ "\\\\%s\\%s",
+ io->in.called_name,
+ io->in.service);
+ NT_STATUS_HAVE_NO_MEMORY(state->io_tcon->tconx.in.path);
+ if (!io->in.service_type) {
+ state->io_tcon->tconx.in.device = "?????";
+ } else {
+ state->io_tcon->tconx.in.device = io->in.service_type;
+ }
+
+ state->req = smb_raw_tcon_send(io->out.tree, state->io_tcon);
+ NT_STATUS_HAVE_NO_MEMORY(state->req);
+ if (state->req->state == SMBCLI_REQUEST_ERROR) {
+ return state->req->status;
+ }
+
+ state->req->async.fn = request_handler;
+ state->req->async.private_data = c;
+ state->stage = CONNECT_TCON;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ a negprot request has completed
+*/
+static NTSTATUS connect_negprot(struct composite_context *c,
+ struct smb_composite_connect *io)
+{
+ struct connect_state *state = talloc_get_type(c->private_data, struct connect_state);
+ NTSTATUS status;
+
+ status = smb_raw_negotiate_recv(state->req);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ /* next step is a session setup */
+ state->session = smbcli_session_init(state->transport, state, true, io->in.session_options);
+ NT_STATUS_HAVE_NO_MEMORY(state->session);
+
+ /* setup for a tconx (or at least have the structure ready to
+ * return, if we won't go that far) */
+ io->out.tree = smbcli_tree_init(state->session, state, true);
+ NT_STATUS_HAVE_NO_MEMORY(io->out.tree);
+
+ /* If we don't have any credentials then this indicates that
+ * we don't want to do a session setup */
+ if (!io->in.credentials) {
+ state->stage = CONNECT_DONE;
+ return NT_STATUS_OK;
+ }
+
+ state->io_setup = talloc(c, struct smb_composite_sesssetup);
+ NT_STATUS_HAVE_NO_MEMORY(state->io_setup);
+
+ /* prepare a session setup to establish a security context */
+ state->io_setup->in.sesskey = state->transport->negotiate.sesskey;
+ state->io_setup->in.capabilities = state->transport->negotiate.capabilities;
+ state->io_setup->in.credentials = io->in.credentials;
+ state->io_setup->in.workgroup = io->in.workgroup;
+ state->io_setup->in.gensec_settings = io->in.gensec_settings;
+
+ state->creq = smb_composite_sesssetup_send(state->session, state->io_setup);
+ NT_STATUS_HAVE_NO_MEMORY(state->creq);
+ if (state->creq->state == COMPOSITE_STATE_ERROR) {
+ return state->creq->status;
+ }
+
+ state->creq->async.fn = composite_handler;
+ state->creq->async.private_data = c;
+
+ state->stage = CONNECT_SESSION_SETUP;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ setup a negprot send
+*/
+static NTSTATUS connect_send_negprot(struct composite_context *c,
+ struct smb_composite_connect *io)
+{
+ struct connect_state *state = talloc_get_type(c->private_data, struct connect_state);
+
+ state->req = smb_raw_negotiate_send(state->transport, io->in.options.unicode, io->in.options.max_protocol);
+ NT_STATUS_HAVE_NO_MEMORY(state->req);
+
+ state->req->async.fn = request_handler;
+ state->req->async.private_data = c;
+ state->stage = CONNECT_NEGPROT;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ a session request operation has completed
+*/
+static NTSTATUS connect_session_request(struct composite_context *c,
+ struct smb_composite_connect *io)
+{
+ struct connect_state *state = talloc_get_type(c->private_data, struct connect_state);
+ NTSTATUS status;
+
+ status = smbcli_transport_connect_recv(state->req);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ /* next step is a negprot */
+ return connect_send_negprot(c, io);
+}
+
+/*
+ a socket connection operation has completed
+*/
+static NTSTATUS connect_socket(struct composite_context *c,
+ struct smb_composite_connect *io)
+{
+ struct connect_state *state = talloc_get_type(c->private_data, struct connect_state);
+ NTSTATUS status;
+ struct nbt_name calling, called;
+
+ status = smbcli_sock_connect_recv(state->creq, state, &state->sock);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ /* the socket is up - we can initialise the smbcli transport layer */
+ state->transport = smbcli_transport_init(state->sock, state, true,
+ &io->in.options, io->in.iconv_convenience);
+ NT_STATUS_HAVE_NO_MEMORY(state->transport);
+
+ if (is_ipaddress(state->sock->hostname) &&
+ (state->io->in.called_name != NULL)) {
+ /* If connecting to an IP address, we might want the real name
+ * of the host for later kerberos. The called name is a better
+ * approximation */
+ state->sock->hostname =
+ talloc_strdup(state->sock, io->in.called_name);
+ NT_STATUS_HAVE_NO_MEMORY(state->sock->hostname);
+ }
+
+ make_nbt_name_client(&calling, cli_credentials_get_workstation(io->in.credentials));
+
+ nbt_choose_called_name(state, &called, io->in.called_name, NBT_NAME_SERVER);
+
+ /* we have a connected socket - next step is a session
+ request, if needed. Port 445 doesn't need it, so it goes
+ straight to the negprot */
+ if (state->sock->port == 445) {
+ status = nbt_name_dup(state->transport, &called,
+ &state->transport->called);
+ NT_STATUS_NOT_OK_RETURN(status);
+ return connect_send_negprot(c, io);
+ }
+
+ state->req = smbcli_transport_connect_send(state->transport, &calling, &called);
+ NT_STATUS_HAVE_NO_MEMORY(state->req);
+
+ state->req->async.fn = request_handler;
+ state->req->async.private_data = c;
+ state->stage = CONNECT_SESSION_REQUEST;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ called when name resolution is finished
+*/
+static NTSTATUS connect_resolve(struct composite_context *c,
+ struct smb_composite_connect *io)
+{
+ struct connect_state *state = talloc_get_type(c->private_data, struct connect_state);
+ NTSTATUS status;
+ const char *address;
+
+ status = resolve_name_recv(state->creq, state, &address);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ state->creq = smbcli_sock_connect_send(state, address,
+ io->in.dest_ports,
+ io->in.dest_host,
+ NULL, c->event_ctx,
+ io->in.socket_options);
+ NT_STATUS_HAVE_NO_MEMORY(state->creq);
+
+ state->stage = CONNECT_SOCKET;
+ state->creq->async.private_data = c;
+ state->creq->async.fn = composite_handler;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ handle and dispatch state transitions
+*/
+static void state_handler(struct composite_context *c)
+{
+ struct connect_state *state = talloc_get_type(c->private_data, struct connect_state);
+
+ switch (state->stage) {
+ case CONNECT_RESOLVE:
+ c->status = connect_resolve(c, state->io);
+ break;
+ case CONNECT_SOCKET:
+ c->status = connect_socket(c, state->io);
+ break;
+ case CONNECT_SESSION_REQUEST:
+ c->status = connect_session_request(c, state->io);
+ break;
+ case CONNECT_NEGPROT:
+ c->status = connect_negprot(c, state->io);
+ break;
+ case CONNECT_SESSION_SETUP:
+ c->status = connect_session_setup(c, state->io);
+ break;
+ case CONNECT_SESSION_SETUP_ANON:
+ c->status = connect_session_setup_anon(c, state->io);
+ break;
+ case CONNECT_TCON:
+ c->status = connect_tcon(c, state->io);
+ break;
+ }
+
+ if (state->stage == CONNECT_DONE) {
+ /* all done! */
+ composite_done(c);
+ } else {
+ composite_is_ok(c);
+ }
+}
+
+
+/*
+ handler for completion of a smbcli_request sub-request
+*/
+static void request_handler(struct smbcli_request *req)
+{
+ struct composite_context *c = talloc_get_type(req->async.private_data,
+ struct composite_context);
+ state_handler(c);
+}
+
+/*
+ handler for completion of a smbcli_composite sub-request
+*/
+static void composite_handler(struct composite_context *creq)
+{
+ struct composite_context *c = talloc_get_type(creq->async.private_data,
+ struct composite_context);
+ state_handler(c);
+}
+
+/*
+ a function to establish a smbcli_tree from scratch
+*/
+struct composite_context *smb_composite_connect_send(struct smb_composite_connect *io,
+ TALLOC_CTX *mem_ctx,
+ struct resolve_context *resolve_ctx,
+ struct tevent_context *event_ctx)
+{
+ struct composite_context *c;
+ struct connect_state *state;
+ struct nbt_name name;
+
+ c = talloc_zero(mem_ctx, struct composite_context);
+ if (c == NULL) goto failed;
+
+ c->event_ctx = talloc_reference(c, event_ctx);
+ if (c->event_ctx == NULL) goto failed;
+
+ state = talloc_zero(c, struct connect_state);
+ if (state == NULL) goto failed;
+
+ if (io->in.gensec_settings == NULL) goto failed;
+ state->io = io;
+
+ c->state = COMPOSITE_STATE_IN_PROGRESS;
+ c->private_data = state;
+
+ state->stage = CONNECT_RESOLVE;
+ make_nbt_name_server(&name, io->in.dest_host);
+ state->creq = resolve_name_send(resolve_ctx, &name, c->event_ctx);
+
+ if (state->creq == NULL) goto failed;
+ state->creq->async.private_data = c;
+ state->creq->async.fn = composite_handler;
+
+ return c;
+failed:
+ talloc_free(c);
+ return NULL;
+}
+
+/*
+ recv half of async composite connect code
+*/
+NTSTATUS smb_composite_connect_recv(struct composite_context *c, TALLOC_CTX *mem_ctx)
+{
+ NTSTATUS status;
+
+ status = composite_wait(c);
+
+ if (NT_STATUS_IS_OK(status)) {
+ struct connect_state *state = talloc_get_type(c->private_data, struct connect_state);
+ talloc_steal(mem_ctx, state->io->out.tree);
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+/*
+ sync version of smb_composite_connect
+*/
+NTSTATUS smb_composite_connect(struct smb_composite_connect *io, TALLOC_CTX *mem_ctx,
+ struct resolve_context *resolve_ctx,
+ struct tevent_context *ev)
+{
+ struct composite_context *c = smb_composite_connect_send(io, mem_ctx, resolve_ctx, ev);
+ return smb_composite_connect_recv(c, mem_ctx);
+}
diff --git a/source4/libcli/smb_composite/fetchfile.c b/source4/libcli/smb_composite/fetchfile.c
new file mode 100644
index 0000000000..210a2940b9
--- /dev/null
+++ b/source4/libcli/smb_composite/fetchfile.c
@@ -0,0 +1,192 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+/*
+ a composite API for loading a whole file into memory
+*/
+
+#include "includes.h"
+#include "libcli/composite/composite.h"
+#include "libcli/smb_composite/smb_composite.h"
+#include "libcli/resolve/resolve.h"
+
+enum fetchfile_stage {FETCHFILE_CONNECT,
+ FETCHFILE_READ};
+
+struct fetchfile_state {
+ enum fetchfile_stage stage;
+ struct smb_composite_fetchfile *io;
+ struct composite_context *creq;
+ struct smb_composite_connect *connect;
+ struct smb_composite_loadfile *loadfile;
+};
+
+static void fetchfile_composite_handler(struct composite_context *req);
+
+static NTSTATUS fetchfile_connect(struct composite_context *c,
+ struct smb_composite_fetchfile *io)
+{
+ NTSTATUS status;
+ struct fetchfile_state *state;
+ state = talloc_get_type(c->private_data, struct fetchfile_state);
+
+ status = smb_composite_connect_recv(state->creq, c);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ state->loadfile = talloc(state, struct smb_composite_loadfile);
+ NT_STATUS_HAVE_NO_MEMORY(state->loadfile);
+
+ state->loadfile->in.fname = io->in.filename;
+
+ state->creq = smb_composite_loadfile_send(state->connect->out.tree,
+ state->loadfile);
+ NT_STATUS_HAVE_NO_MEMORY(state->creq);
+
+ state->creq->async.private_data = c;
+ state->creq->async.fn = fetchfile_composite_handler;
+
+ state->stage = FETCHFILE_READ;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS fetchfile_read(struct composite_context *c,
+ struct smb_composite_fetchfile *io)
+{
+ NTSTATUS status;
+ struct fetchfile_state *state;
+ state = talloc_get_type(c->private_data, struct fetchfile_state);
+
+ status = smb_composite_loadfile_recv(state->creq, NULL);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ io->out.data = state->loadfile->out.data;
+ io->out.size = state->loadfile->out.size;
+
+ c->state = COMPOSITE_STATE_DONE;
+ if (c->async.fn)
+ c->async.fn(c);
+
+ return NT_STATUS_OK;
+}
+
+static void fetchfile_state_handler(struct composite_context *c)
+{
+ struct fetchfile_state *state;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+
+ state = talloc_get_type(c->private_data, struct fetchfile_state);
+
+ /* when this handler is called, the stage indicates what
+ call has just finished */
+ switch (state->stage) {
+ case FETCHFILE_CONNECT:
+ status = fetchfile_connect(c, state->io);
+ break;
+ case FETCHFILE_READ:
+ status = fetchfile_read(c, state->io);
+ break;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ c->status = status;
+ c->state = COMPOSITE_STATE_ERROR;
+ if (c->async.fn) {
+ c->async.fn(c);
+ }
+ }
+}
+
+static void fetchfile_composite_handler(struct composite_context *creq)
+{
+ struct composite_context *c = talloc_get_type(creq->async.private_data,
+ struct composite_context);
+ fetchfile_state_handler(c);
+}
+
+struct composite_context *smb_composite_fetchfile_send(struct smb_composite_fetchfile *io,
+ struct tevent_context *event_ctx)
+{
+ struct composite_context *c;
+ struct fetchfile_state *state;
+
+ c = talloc_zero(NULL, struct composite_context);
+ if (c == NULL) goto failed;
+
+ state = talloc(c, struct fetchfile_state);
+ if (state == NULL) goto failed;
+
+ state->connect = talloc(state, struct smb_composite_connect);
+ if (state->connect == NULL) goto failed;
+
+ state->io = io;
+
+ state->connect->in.dest_host = io->in.dest_host;
+ state->connect->in.dest_ports = io->in.ports;
+ state->connect->in.socket_options = io->in.socket_options;
+ state->connect->in.called_name = io->in.called_name;
+ state->connect->in.service = io->in.service;
+ state->connect->in.service_type = io->in.service_type;
+ state->connect->in.credentials = io->in.credentials;
+ state->connect->in.fallback_to_anonymous = false;
+ state->connect->in.workgroup = io->in.workgroup;
+ state->connect->in.gensec_settings = io->in.gensec_settings;
+ state->connect->in.iconv_convenience = io->in.iconv_convenience;
+
+ state->connect->in.options = io->in.options;
+ state->connect->in.session_options = io->in.session_options;
+
+ state->creq = smb_composite_connect_send(state->connect, state,
+ io->in.resolve_ctx, event_ctx);
+ if (state->creq == NULL) goto failed;
+
+ state->creq->async.private_data = c;
+ state->creq->async.fn = fetchfile_composite_handler;
+
+ c->state = COMPOSITE_STATE_IN_PROGRESS;
+ state->stage = FETCHFILE_CONNECT;
+ c->private_data = state;
+
+ return c;
+ failed:
+ talloc_free(c);
+ return NULL;
+}
+
+NTSTATUS smb_composite_fetchfile_recv(struct composite_context *c,
+ TALLOC_CTX *mem_ctx)
+{
+ NTSTATUS status;
+
+ status = composite_wait(c);
+
+ if (NT_STATUS_IS_OK(status)) {
+ struct fetchfile_state *state = talloc_get_type(c->private_data, struct fetchfile_state);
+ talloc_steal(mem_ctx, state->io->out.data);
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+NTSTATUS smb_composite_fetchfile(struct smb_composite_fetchfile *io,
+ TALLOC_CTX *mem_ctx)
+{
+ struct composite_context *c = smb_composite_fetchfile_send(io, NULL);
+ return smb_composite_fetchfile_recv(c, mem_ctx);
+}
diff --git a/source4/libcli/smb_composite/fsinfo.c b/source4/libcli/smb_composite/fsinfo.c
new file mode 100644
index 0000000000..3bc93b62ab
--- /dev/null
+++ b/source4/libcli/smb_composite/fsinfo.c
@@ -0,0 +1,210 @@
+/*
+ a composite API for quering file system information
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/composite/composite.h"
+#include "libcli/smb_composite/smb_composite.h"
+#include "libcli/resolve/resolve.h"
+
+/* the stages of this call */
+enum fsinfo_stage {FSINFO_CONNECT, FSINFO_QUERY};
+
+
+static void fsinfo_raw_handler(struct smbcli_request *req);
+static void fsinfo_composite_handler(struct composite_context *c);
+static void fsinfo_state_handler(struct composite_context *c);
+
+struct fsinfo_state {
+ enum fsinfo_stage stage;
+ struct composite_context *creq;
+ struct smb_composite_fsinfo *io;
+ struct smb_composite_connect *connect;
+ union smb_fsinfo *fsinfo;
+ struct smbcli_tree *tree;
+ struct smbcli_request *req;
+};
+
+static NTSTATUS fsinfo_connect(struct composite_context *c,
+ struct smb_composite_fsinfo *io)
+{
+ NTSTATUS status;
+ struct fsinfo_state *state;
+ state = talloc_get_type(c->private_data, struct fsinfo_state);
+
+ status = smb_composite_connect_recv(state->creq, c);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ state->fsinfo = talloc(state, union smb_fsinfo);
+ NT_STATUS_HAVE_NO_MEMORY(state->fsinfo);
+
+ state->fsinfo->generic.level = io->in.level;
+
+ state->req = smb_raw_fsinfo_send(state->connect->out.tree,
+ state,
+ state->fsinfo);
+ NT_STATUS_HAVE_NO_MEMORY(state->req);
+
+ state->req->async.private_data = c;
+ state->req->async.fn = fsinfo_raw_handler;
+
+ state->stage = FSINFO_QUERY;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS fsinfo_query(struct composite_context *c,
+ struct smb_composite_fsinfo *io)
+{
+ NTSTATUS status;
+ struct fsinfo_state *state;
+ state = talloc_get_type(c->private_data, struct fsinfo_state);
+
+ status = smb_raw_fsinfo_recv(state->req, state, state->fsinfo);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ state->io->out.fsinfo = state->fsinfo;
+
+ c->state = COMPOSITE_STATE_DONE;
+
+ if (c->async.fn)
+ c->async.fn(c);
+
+ return NT_STATUS_OK;
+
+}
+
+/*
+ handler for completion of a sub-request in fsinfo
+*/
+static void fsinfo_state_handler(struct composite_context *creq)
+{
+ struct fsinfo_state *state = talloc_get_type(creq->private_data, struct fsinfo_state);
+
+ /* when this handler is called, the stage indicates what
+ call has just finished */
+ switch (state->stage) {
+ case FSINFO_CONNECT:
+ creq->status = fsinfo_connect(creq, state->io);
+ break;
+
+ case FSINFO_QUERY:
+ creq->status = fsinfo_query(creq, state->io);
+ break;
+ }
+
+ if (!NT_STATUS_IS_OK(creq->status)) {
+ creq->state = COMPOSITE_STATE_ERROR;
+ }
+
+ if (creq->state >= COMPOSITE_STATE_DONE && creq->async.fn) {
+ creq->async.fn(creq);
+ }
+}
+
+/*
+ As raw and composite handlers take different requests, we need to handlers
+ to adapt both for the same state machine in fsinfo_state_handler()
+*/
+static void fsinfo_raw_handler(struct smbcli_request *req)
+{
+ struct composite_context *c = talloc_get_type(req->async.private_data,
+ struct composite_context);
+ fsinfo_state_handler(c);
+}
+
+static void fsinfo_composite_handler(struct composite_context *creq)
+{
+ struct composite_context *c = talloc_get_type(creq->async.private_data,
+ struct composite_context);
+ fsinfo_state_handler(c);
+}
+
+/*
+ composite fsinfo call - connects to a tree and queries a file system information
+*/
+struct composite_context *smb_composite_fsinfo_send(struct smbcli_tree *tree,
+ struct smb_composite_fsinfo *io,
+ struct resolve_context *resolve_ctx)
+{
+ struct composite_context *c;
+ struct fsinfo_state *state;
+
+ c = talloc_zero(tree, struct composite_context);
+ if (c == NULL) goto failed;
+
+ state = talloc(c, struct fsinfo_state);
+ if (state == NULL) goto failed;
+
+ state->io = io;
+
+ state->connect = talloc(state, struct smb_composite_connect);
+
+ if (state->connect == NULL) goto failed;
+
+ state->connect->in.dest_host = io->in.dest_host;
+ state->connect->in.dest_ports = io->in.dest_ports;
+ state->connect->in.socket_options = io->in.socket_options;
+ state->connect->in.called_name = io->in.called_name;
+ state->connect->in.service = io->in.service;
+ state->connect->in.service_type = io->in.service_type;
+ state->connect->in.credentials = io->in.credentials;
+ state->connect->in.fallback_to_anonymous = false;
+ state->connect->in.workgroup = io->in.workgroup;
+ state->connect->in.iconv_convenience = io->in.iconv_convenience;
+ state->connect->in.gensec_settings = io->in.gensec_settings;
+
+ state->connect->in.options = tree->session->transport->options;
+ state->connect->in.session_options = tree->session->options;
+
+ c->state = COMPOSITE_STATE_IN_PROGRESS;
+ state->stage = FSINFO_CONNECT;
+ c->private_data = state;
+
+ state->creq = smb_composite_connect_send(state->connect, state,
+ resolve_ctx, c->event_ctx);
+
+ if (state->creq == NULL) goto failed;
+
+ state->creq->async.private_data = c;
+ state->creq->async.fn = fsinfo_composite_handler;
+
+ return c;
+failed:
+ talloc_free(c);
+ return NULL;
+}
+
+/*
+ composite fsinfo call - recv side
+*/
+NTSTATUS smb_composite_fsinfo_recv(struct composite_context *c, TALLOC_CTX *mem_ctx)
+{
+ NTSTATUS status;
+
+ status = composite_wait(c);
+
+ if (NT_STATUS_IS_OK(status)) {
+ struct fsinfo_state *state = talloc_get_type(c->private_data, struct fsinfo_state);
+ talloc_steal(mem_ctx, state->io->out.fsinfo);
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+
+/*
+ composite fsinfo call - sync interface
+*/
+NTSTATUS smb_composite_fsinfo(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ struct smb_composite_fsinfo *io,
+ struct resolve_context *resolve_ctx)
+{
+ struct composite_context *c = smb_composite_fsinfo_send(tree, io, resolve_ctx);
+ return smb_composite_fsinfo_recv(c, mem_ctx);
+}
+
diff --git a/source4/libcli/smb_composite/loadfile.c b/source4/libcli/smb_composite/loadfile.c
new file mode 100644
index 0000000000..994c29c77d
--- /dev/null
+++ b/source4/libcli/smb_composite/loadfile.c
@@ -0,0 +1,293 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+/*
+ a composite API for loading a whole file into memory
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/composite/composite.h"
+#include "libcli/smb_composite/smb_composite.h"
+
+/* the stages of this call */
+enum loadfile_stage {LOADFILE_OPEN, LOADFILE_READ, LOADFILE_CLOSE};
+
+
+static void loadfile_handler(struct smbcli_request *req);
+
+struct loadfile_state {
+ enum loadfile_stage stage;
+ struct smb_composite_loadfile *io;
+ struct smbcli_request *req;
+ union smb_open *io_open;
+ union smb_read *io_read;
+};
+
+/*
+ setup for the close
+*/
+static NTSTATUS setup_close(struct composite_context *c,
+ struct smbcli_tree *tree, uint16_t fnum)
+{
+ struct loadfile_state *state = talloc_get_type(c->private_data, struct loadfile_state);
+ union smb_close *io_close;
+
+ /* nothing to read, setup the close */
+ io_close = talloc(c, union smb_close);
+ NT_STATUS_HAVE_NO_MEMORY(io_close);
+
+ io_close->close.level = RAW_CLOSE_CLOSE;
+ io_close->close.in.file.fnum = fnum;
+ io_close->close.in.write_time = 0;
+
+ state->req = smb_raw_close_send(tree, io_close);
+ NT_STATUS_HAVE_NO_MEMORY(state->req);
+
+ /* call the handler again when the close is done */
+ state->req->async.fn = loadfile_handler;
+ state->req->async.private_data = c;
+ state->stage = LOADFILE_CLOSE;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ called when the open is done - pull the results and setup for the
+ first readx, or close if the file is zero size
+*/
+static NTSTATUS loadfile_open(struct composite_context *c,
+ struct smb_composite_loadfile *io)
+{
+ struct loadfile_state *state = talloc_get_type(c->private_data, struct loadfile_state);
+ struct smbcli_tree *tree = state->req->tree;
+ NTSTATUS status;
+
+ status = smb_raw_open_recv(state->req, c, state->io_open);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ /* don't allow stupidly large loads */
+ if (state->io_open->ntcreatex.out.size > 100*1000*1000) {
+ return NT_STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ /* allocate space for the file data */
+ io->out.size = state->io_open->ntcreatex.out.size;
+ io->out.data = talloc_array(c, uint8_t, io->out.size);
+ NT_STATUS_HAVE_NO_MEMORY(io->out.data);
+
+ if (io->out.size == 0) {
+ return setup_close(c, tree, state->io_open->ntcreatex.out.file.fnum);
+ }
+
+ /* setup for the read */
+ state->io_read = talloc(c, union smb_read);
+ NT_STATUS_HAVE_NO_MEMORY(state->io_read);
+
+ state->io_read->readx.level = RAW_READ_READX;
+ state->io_read->readx.in.file.fnum = state->io_open->ntcreatex.out.file.fnum;
+ state->io_read->readx.in.offset = 0;
+ state->io_read->readx.in.mincnt = MIN(32768, io->out.size);
+ state->io_read->readx.in.maxcnt = state->io_read->readx.in.mincnt;
+ state->io_read->readx.in.remaining = 0;
+ state->io_read->readx.in.read_for_execute = false;
+ state->io_read->readx.out.data = io->out.data;
+
+ state->req = smb_raw_read_send(tree, state->io_read);
+ NT_STATUS_HAVE_NO_MEMORY(state->req);
+
+ /* call the handler again when the first read is done */
+ state->req->async.fn = loadfile_handler;
+ state->req->async.private_data = c;
+ state->stage = LOADFILE_READ;
+
+ talloc_free(state->io_open);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ called when a read is done - pull the results and setup for the
+ next read, or close if the file is all done
+*/
+static NTSTATUS loadfile_read(struct composite_context *c,
+ struct smb_composite_loadfile *io)
+{
+ struct loadfile_state *state = talloc_get_type(c->private_data, struct loadfile_state);
+ struct smbcli_tree *tree = state->req->tree;
+ NTSTATUS status;
+
+ status = smb_raw_read_recv(state->req, state->io_read);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ /* we might be done */
+ if (state->io_read->readx.in.offset +
+ state->io_read->readx.out.nread == io->out.size) {
+ return setup_close(c, tree, state->io_read->readx.in.file.fnum);
+ }
+
+ /* setup for the next read */
+ state->io_read->readx.in.offset += state->io_read->readx.out.nread;
+ state->io_read->readx.in.mincnt = MIN(32768, io->out.size - state->io_read->readx.in.offset);
+ state->io_read->readx.out.data = io->out.data + state->io_read->readx.in.offset;
+
+ state->req = smb_raw_read_send(tree, state->io_read);
+ NT_STATUS_HAVE_NO_MEMORY(state->req);
+
+ /* call the handler again when the read is done */
+ state->req->async.fn = loadfile_handler;
+ state->req->async.private_data = c;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ called when the close is done, check the status and cleanup
+*/
+static NTSTATUS loadfile_close(struct composite_context *c,
+ struct smb_composite_loadfile *io)
+{
+ struct loadfile_state *state = talloc_get_type(c->private_data, struct loadfile_state);
+ NTSTATUS status;
+
+ status = smbcli_request_simple_recv(state->req);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ c->state = COMPOSITE_STATE_DONE;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ handler for completion of a sub-request in loadfile
+*/
+static void loadfile_handler(struct smbcli_request *req)
+{
+ struct composite_context *c = (struct composite_context *)req->async.private_data;
+ struct loadfile_state *state = talloc_get_type(c->private_data, struct loadfile_state);
+
+ /* when this handler is called, the stage indicates what
+ call has just finished */
+ switch (state->stage) {
+ case LOADFILE_OPEN:
+ c->status = loadfile_open(c, state->io);
+ break;
+
+ case LOADFILE_READ:
+ c->status = loadfile_read(c, state->io);
+ break;
+
+ case LOADFILE_CLOSE:
+ c->status = loadfile_close(c, state->io);
+ break;
+ }
+
+ if (!NT_STATUS_IS_OK(c->status)) {
+ c->state = COMPOSITE_STATE_ERROR;
+ }
+
+ if (c->state >= COMPOSITE_STATE_DONE &&
+ c->async.fn) {
+ c->async.fn(c);
+ }
+}
+
+/*
+ composite loadfile call - does an openx followed by a number of readx calls,
+ followed by a close
+*/
+struct composite_context *smb_composite_loadfile_send(struct smbcli_tree *tree,
+ struct smb_composite_loadfile *io)
+{
+ struct composite_context *c;
+ struct loadfile_state *state;
+
+ c = talloc_zero(tree, struct composite_context);
+ if (c == NULL) goto failed;
+
+ state = talloc(c, struct loadfile_state);
+ if (state == NULL) goto failed;
+
+ state->io = io;
+
+ c->private_data = state;
+ c->state = COMPOSITE_STATE_IN_PROGRESS;
+ c->event_ctx = tree->session->transport->socket->event.ctx;
+
+ /* setup for the open */
+ state->io_open = talloc_zero(c, union smb_open);
+ if (state->io_open == NULL) goto failed;
+
+ state->io_open->ntcreatex.level = RAW_OPEN_NTCREATEX;
+ state->io_open->ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED;
+ state->io_open->ntcreatex.in.access_mask = SEC_FILE_READ_DATA;
+ state->io_open->ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ state->io_open->ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
+ state->io_open->ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
+ state->io_open->ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ state->io_open->ntcreatex.in.fname = io->in.fname;
+
+ /* send the open on its way */
+ state->req = smb_raw_open_send(tree, state->io_open);
+ if (state->req == NULL) goto failed;
+
+ /* setup the callback handler */
+ state->req->async.fn = loadfile_handler;
+ state->req->async.private_data = c;
+ state->stage = LOADFILE_OPEN;
+
+ return c;
+
+failed:
+ talloc_free(c);
+ return NULL;
+}
+
+
+/*
+ composite loadfile call - recv side
+*/
+NTSTATUS smb_composite_loadfile_recv(struct composite_context *c, TALLOC_CTX *mem_ctx)
+{
+ NTSTATUS status;
+
+ status = composite_wait(c);
+
+ if (NT_STATUS_IS_OK(status)) {
+ struct loadfile_state *state = talloc_get_type(c->private_data, struct loadfile_state);
+ talloc_steal(mem_ctx, state->io->out.data);
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+
+/*
+ composite loadfile call - sync interface
+*/
+NTSTATUS smb_composite_loadfile(struct smbcli_tree *tree,
+ TALLOC_CTX *mem_ctx,
+ struct smb_composite_loadfile *io)
+{
+ struct composite_context *c = smb_composite_loadfile_send(tree, io);
+ return smb_composite_loadfile_recv(c, mem_ctx);
+}
+
diff --git a/source4/libcli/smb_composite/savefile.c b/source4/libcli/smb_composite/savefile.c
new file mode 100644
index 0000000000..25a35c01a9
--- /dev/null
+++ b/source4/libcli/smb_composite/savefile.c
@@ -0,0 +1,288 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+/*
+ a composite API for saving a whole file from memory
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/composite/composite.h"
+#include "libcli/smb_composite/smb_composite.h"
+
+/* the stages of this call */
+enum savefile_stage {SAVEFILE_OPEN, SAVEFILE_WRITE, SAVEFILE_CLOSE};
+
+static void savefile_handler(struct smbcli_request *req);
+
+struct savefile_state {
+ enum savefile_stage stage;
+ off_t total_written;
+ struct smb_composite_savefile *io;
+ union smb_open *io_open;
+ union smb_write *io_write;
+ struct smbcli_request *req;
+};
+
+
+/*
+ setup for the close
+*/
+static NTSTATUS setup_close(struct composite_context *c,
+ struct smbcli_tree *tree, uint16_t fnum)
+{
+ struct savefile_state *state = talloc_get_type(c->private_data, struct savefile_state);
+ union smb_close *io_close;
+
+ /* nothing to write, setup the close */
+ io_close = talloc(c, union smb_close);
+ NT_STATUS_HAVE_NO_MEMORY(io_close);
+
+ io_close->close.level = RAW_CLOSE_CLOSE;
+ io_close->close.in.file.fnum = fnum;
+ io_close->close.in.write_time = 0;
+
+ state->req = smb_raw_close_send(tree, io_close);
+ NT_STATUS_HAVE_NO_MEMORY(state->req);
+
+ /* call the handler again when the close is done */
+ state->stage = SAVEFILE_CLOSE;
+ state->req->async.fn = savefile_handler;
+ state->req->async.private_data = c;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ called when the open is done - pull the results and setup for the
+ first writex, or close if the file is zero size
+*/
+static NTSTATUS savefile_open(struct composite_context *c,
+ struct smb_composite_savefile *io)
+{
+ struct savefile_state *state = talloc_get_type(c->private_data, struct savefile_state);
+ union smb_write *io_write;
+ struct smbcli_tree *tree = state->req->tree;
+ NTSTATUS status;
+ uint32_t max_xmit = tree->session->transport->negotiate.max_xmit;
+
+ status = smb_raw_open_recv(state->req, c, state->io_open);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ if (io->in.size == 0) {
+ return setup_close(c, tree, state->io_open->ntcreatex.out.file.fnum);
+ }
+
+ /* setup for the first write */
+ io_write = talloc(c, union smb_write);
+ NT_STATUS_HAVE_NO_MEMORY(io_write);
+
+ io_write->writex.level = RAW_WRITE_WRITEX;
+ io_write->writex.in.file.fnum = state->io_open->ntcreatex.out.file.fnum;
+ io_write->writex.in.offset = 0;
+ io_write->writex.in.wmode = 0;
+ io_write->writex.in.remaining = 0;
+ io_write->writex.in.count = MIN(max_xmit - 100, io->in.size);
+ io_write->writex.in.data = io->in.data;
+ state->io_write = io_write;
+
+ state->req = smb_raw_write_send(tree, io_write);
+ NT_STATUS_HAVE_NO_MEMORY(state->req);
+
+ /* call the handler again when the first write is done */
+ state->stage = SAVEFILE_WRITE;
+ state->req->async.fn = savefile_handler;
+ state->req->async.private_data = c;
+ talloc_free(state->io_open);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ called when a write is done - pull the results and setup for the
+ next write, or close if the file is all done
+*/
+static NTSTATUS savefile_write(struct composite_context *c,
+ struct smb_composite_savefile *io)
+{
+ struct savefile_state *state = talloc_get_type(c->private_data, struct savefile_state);
+ struct smbcli_tree *tree = state->req->tree;
+ NTSTATUS status;
+ uint32_t max_xmit = tree->session->transport->negotiate.max_xmit;
+
+ status = smb_raw_write_recv(state->req, state->io_write);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ state->total_written += state->io_write->writex.out.nwritten;
+
+ /* we might be done */
+ if (state->io_write->writex.out.nwritten != state->io_write->writex.in.count ||
+ state->total_written == io->in.size) {
+ return setup_close(c, tree, state->io_write->writex.in.file.fnum);
+ }
+
+ /* setup for the next write */
+ state->io_write->writex.in.offset = state->total_written;
+ state->io_write->writex.in.count = MIN(max_xmit - 100,
+ io->in.size - state->total_written);
+ state->io_write->writex.in.data = io->in.data + state->total_written;
+
+ state->req = smb_raw_write_send(tree, state->io_write);
+ NT_STATUS_HAVE_NO_MEMORY(state->req);
+
+ /* call the handler again when the write is done */
+ state->req->async.fn = savefile_handler;
+ state->req->async.private_data = c;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ called when the close is done, check the status and cleanup
+*/
+static NTSTATUS savefile_close(struct composite_context *c,
+ struct smb_composite_savefile *io)
+{
+ struct savefile_state *state = talloc_get_type(c->private_data, struct savefile_state);
+ NTSTATUS status;
+
+ status = smbcli_request_simple_recv(state->req);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ if (state->total_written != io->in.size) {
+ return NT_STATUS_DISK_FULL;
+ }
+
+ c->state = COMPOSITE_STATE_DONE;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ handler for completion of a sub-request in savefile
+*/
+static void savefile_handler(struct smbcli_request *req)
+{
+ struct composite_context *c = (struct composite_context *)req->async.private_data;
+ struct savefile_state *state = talloc_get_type(c->private_data, struct savefile_state);
+
+ /* when this handler is called, the stage indicates what
+ call has just finished */
+ switch (state->stage) {
+ case SAVEFILE_OPEN:
+ c->status = savefile_open(c, state->io);
+ break;
+
+ case SAVEFILE_WRITE:
+ c->status = savefile_write(c, state->io);
+ break;
+
+ case SAVEFILE_CLOSE:
+ c->status = savefile_close(c, state->io);
+ break;
+ }
+
+ if (!NT_STATUS_IS_OK(c->status)) {
+ c->state = COMPOSITE_STATE_ERROR;
+ }
+
+ if (c->state >= COMPOSITE_STATE_DONE &&
+ c->async.fn) {
+ c->async.fn(c);
+ }
+}
+
+/*
+ composite savefile call - does an openx followed by a number of writex calls,
+ followed by a close
+*/
+struct composite_context *smb_composite_savefile_send(struct smbcli_tree *tree,
+ struct smb_composite_savefile *io)
+{
+ struct composite_context *c;
+ struct savefile_state *state;
+ union smb_open *io_open;
+
+ c = talloc_zero(tree, struct composite_context);
+ if (c == NULL) goto failed;
+
+ c->state = COMPOSITE_STATE_IN_PROGRESS;
+ c->event_ctx = tree->session->transport->socket->event.ctx;
+
+ state = talloc(c, struct savefile_state);
+ if (state == NULL) goto failed;
+
+ state->stage = SAVEFILE_OPEN;
+ state->total_written = 0;
+ state->io = io;
+
+ /* setup for the open */
+ io_open = talloc_zero(c, union smb_open);
+ if (io_open == NULL) goto failed;
+
+ io_open->ntcreatex.level = RAW_OPEN_NTCREATEX;
+ io_open->ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED;
+ io_open->ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA;
+ 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.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io_open->ntcreatex.in.fname = io->in.fname;
+ state->io_open = io_open;
+
+ /* send the open on its way */
+ state->req = smb_raw_open_send(tree, io_open);
+ if (state->req == NULL) goto failed;
+
+ /* setup the callback handler */
+ state->req->async.fn = savefile_handler;
+ state->req->async.private_data = c;
+ c->private_data = state;
+
+ return c;
+
+failed:
+ talloc_free(c);
+ return NULL;
+}
+
+
+/*
+ composite savefile call - recv side
+*/
+NTSTATUS smb_composite_savefile_recv(struct composite_context *c)
+{
+ NTSTATUS status;
+ status = composite_wait(c);
+ talloc_free(c);
+ return status;
+}
+
+
+/*
+ composite savefile call - sync interface
+*/
+NTSTATUS smb_composite_savefile(struct smbcli_tree *tree,
+ struct smb_composite_savefile *io)
+{
+ struct composite_context *c = smb_composite_savefile_send(tree, io);
+ return smb_composite_savefile_recv(c);
+}
diff --git a/source4/libcli/smb_composite/sesssetup.c b/source4/libcli/smb_composite/sesssetup.c
new file mode 100644
index 0000000000..83d15e98eb
--- /dev/null
+++ b/source4/libcli/smb_composite/sesssetup.c
@@ -0,0 +1,570 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+/*
+ a composite API for making handling a generic async session setup
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/composite/composite.h"
+#include "libcli/smb_composite/smb_composite.h"
+#include "libcli/smb_composite/proto.h"
+#include "libcli/auth/libcli_auth.h"
+#include "auth/auth.h"
+#include "auth/gensec/gensec.h"
+#include "auth/credentials/credentials.h"
+#include "version.h"
+#include "param/param.h"
+
+struct sesssetup_state {
+ union smb_sesssetup setup;
+ NTSTATUS remote_status;
+ NTSTATUS gensec_status;
+ struct smb_composite_sesssetup *io;
+ struct smbcli_request *req;
+};
+
+static int sesssetup_state_destructor(struct sesssetup_state *state)
+{
+ if (state->req) {
+ talloc_free(state->req);
+ state->req = NULL;
+ }
+
+ return 0;
+}
+
+static NTSTATUS session_setup_old(struct composite_context *c,
+ struct smbcli_session *session,
+ struct smb_composite_sesssetup *io,
+ struct smbcli_request **req);
+static NTSTATUS session_setup_nt1(struct composite_context *c,
+ struct smbcli_session *session,
+ struct smb_composite_sesssetup *io,
+ struct smbcli_request **req);
+static NTSTATUS session_setup_spnego(struct composite_context *c,
+ struct smbcli_session *session,
+ struct smb_composite_sesssetup *io,
+ struct smbcli_request **req);
+
+/*
+ store the user session key for a transport
+*/
+static void set_user_session_key(struct smbcli_session *session,
+ const DATA_BLOB *session_key)
+{
+ session->user_session_key = data_blob_talloc(session,
+ session_key->data,
+ session_key->length);
+}
+
+/*
+ handler for completion of a smbcli_request sub-request
+*/
+static void request_handler(struct smbcli_request *req)
+{
+ struct composite_context *c = (struct composite_context *)req->async.private_data;
+ struct sesssetup_state *state = talloc_get_type(c->private_data, struct sesssetup_state);
+ struct smbcli_session *session = req->session;
+ DATA_BLOB session_key = data_blob(NULL, 0);
+ DATA_BLOB null_data_blob = data_blob(NULL, 0);
+ NTSTATUS session_key_err, nt_status;
+ struct smbcli_request *check_req = NULL;
+
+ if (req->sign_caller_checks) {
+ req->do_not_free = true;
+ check_req = req;
+ }
+
+ state->remote_status = smb_raw_sesssetup_recv(req, state, &state->setup);
+ c->status = state->remote_status;
+ state->req = NULL;
+
+ /*
+ * we only need to check the signature if the
+ * NT_STATUS_OK is returned
+ */
+ if (!NT_STATUS_IS_OK(state->remote_status)) {
+ talloc_free(check_req);
+ check_req = NULL;
+ }
+
+ switch (state->setup.old.level) {
+ case RAW_SESSSETUP_OLD:
+ state->io->out.vuid = state->setup.old.out.vuid;
+ /* This doesn't work, as this only happens on old
+ * protocols, where this comparison won't match. */
+ if (NT_STATUS_EQUAL(c->status, NT_STATUS_LOGON_FAILURE)) {
+ /* we neet to reset the vuid for a new try */
+ session->vuid = 0;
+ if (cli_credentials_wrong_password(state->io->in.credentials)) {
+ nt_status = session_setup_old(c, session,
+ state->io,
+ &state->req);
+ if (NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(check_req);
+ c->status = nt_status;
+ composite_continue_smb(c, state->req, request_handler, c);
+ return;
+ }
+ }
+ }
+ break;
+
+ case RAW_SESSSETUP_NT1:
+ state->io->out.vuid = state->setup.nt1.out.vuid;
+ if (NT_STATUS_EQUAL(c->status, NT_STATUS_LOGON_FAILURE)) {
+ /* we neet to reset the vuid for a new try */
+ session->vuid = 0;
+ if (cli_credentials_wrong_password(state->io->in.credentials)) {
+ nt_status = session_setup_nt1(c, session,
+ state->io,
+ &state->req);
+ if (NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(check_req);
+ c->status = nt_status;
+ composite_continue_smb(c, state->req, request_handler, c);
+ return;
+ }
+ }
+ }
+ break;
+
+ case RAW_SESSSETUP_SPNEGO:
+ state->io->out.vuid = state->setup.spnego.out.vuid;
+ if (NT_STATUS_EQUAL(c->status, NT_STATUS_LOGON_FAILURE)) {
+ /* we need to reset the vuid for a new try */
+ session->vuid = 0;
+ if (cli_credentials_wrong_password(state->io->in.credentials)) {
+ nt_status = session_setup_spnego(c, session,
+ state->io,
+ &state->req);
+ if (NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(check_req);
+ c->status = nt_status;
+ composite_continue_smb(c, state->req, request_handler, c);
+ return;
+ }
+ }
+ }
+ if (!NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED) &&
+ !NT_STATUS_IS_OK(c->status)) {
+ break;
+ }
+ if (NT_STATUS_EQUAL(state->gensec_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+
+ /* The status value here, from the earlier pass at GENSEC is
+ * vital to the security of the system. Even if the other end
+ * accepts, if GENSEC claims 'MORE_PROCESSING_REQUIRED' then
+ * you must keep feeding it blobs, or else the remote
+ * host/attacker might avoid mutal authentication
+ * requirements */
+
+ state->gensec_status = gensec_update(session->gensec, state,
+ state->setup.spnego.out.secblob,
+ &state->setup.spnego.in.secblob);
+ c->status = state->gensec_status;
+ if (!NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED) &&
+ !NT_STATUS_IS_OK(c->status)) {
+ break;
+ }
+ } else {
+ state->setup.spnego.in.secblob = data_blob(NULL, 0);
+ }
+
+ if (NT_STATUS_IS_OK(state->remote_status)) {
+ if (state->setup.spnego.in.secblob.length) {
+ c->status = NT_STATUS_INTERNAL_ERROR;
+ break;
+ }
+ session_key_err = gensec_session_key(session->gensec, &session_key);
+ if (NT_STATUS_IS_OK(session_key_err)) {
+ set_user_session_key(session, &session_key);
+ smbcli_transport_simple_set_signing(session->transport, session_key, null_data_blob);
+ }
+ }
+
+ if (state->setup.spnego.in.secblob.length) {
+ /*
+ * set the session->vuid value only for calling
+ * smb_raw_sesssetup_send()
+ */
+ uint16_t vuid = session->vuid;
+ session->vuid = state->io->out.vuid;
+ state->req = smb_raw_sesssetup_send(session, &state->setup);
+ session->vuid = vuid;
+ if (state->req) {
+ state->req->sign_caller_checks = true;
+ }
+ composite_continue_smb(c, state->req, request_handler, c);
+ return;
+ }
+ break;
+
+ case RAW_SESSSETUP_SMB2:
+ c->status = NT_STATUS_INTERNAL_ERROR;
+ break;
+ }
+
+ if (check_req) {
+ check_req->sign_caller_checks = false;
+ if (!smbcli_request_check_sign_mac(check_req)) {
+ c->status = NT_STATUS_ACCESS_DENIED;
+ }
+ talloc_free(check_req);
+ check_req = NULL;
+ }
+
+ /* enforce the local signing required flag */
+ if (NT_STATUS_IS_OK(c->status) && !cli_credentials_is_anonymous(state->io->in.credentials)) {
+ if (!session->transport->negotiate.sign_info.doing_signing
+ && session->transport->negotiate.sign_info.mandatory_signing) {
+ DEBUG(0, ("SMB signing required, but server does not support it\n"));
+ c->status = NT_STATUS_ACCESS_DENIED;
+ }
+ }
+
+ if (!NT_STATUS_IS_OK(c->status)) {
+ composite_error(c, c->status);
+ return;
+ }
+
+ composite_done(c);
+}
+
+
+/*
+ send a nt1 style session setup
+*/
+static NTSTATUS session_setup_nt1(struct composite_context *c,
+ struct smbcli_session *session,
+ struct smb_composite_sesssetup *io,
+ struct smbcli_request **req)
+{
+ NTSTATUS nt_status = NT_STATUS_INTERNAL_ERROR;
+ struct sesssetup_state *state = talloc_get_type(c->private_data, struct sesssetup_state);
+ DATA_BLOB names_blob = NTLMv2_generate_names_blob(state, session->transport->socket->hostname, cli_credentials_get_domain(io->in.credentials));
+ DATA_BLOB session_key = data_blob(NULL, 0);
+ int flags = CLI_CRED_NTLM_AUTH;
+
+ smbcli_temp_set_signing(session->transport);
+
+ if (session->options.lanman_auth) {
+ flags |= CLI_CRED_LANMAN_AUTH;
+ }
+
+ if (session->options.ntlmv2_auth) {
+ flags |= CLI_CRED_NTLMv2_AUTH;
+ }
+
+ state->setup.nt1.level = RAW_SESSSETUP_NT1;
+ state->setup.nt1.in.bufsize = session->transport->options.max_xmit;
+ state->setup.nt1.in.mpx_max = session->transport->options.max_mux;
+ state->setup.nt1.in.vc_num = 1;
+ state->setup.nt1.in.sesskey = io->in.sesskey;
+ state->setup.nt1.in.capabilities = io->in.capabilities;
+ state->setup.nt1.in.os = "Unix";
+ state->setup.nt1.in.lanman = talloc_asprintf(state, "Samba %s", SAMBA_VERSION_STRING);
+
+ cli_credentials_get_ntlm_username_domain(io->in.credentials, state,
+ &state->setup.nt1.in.user,
+ &state->setup.nt1.in.domain);
+
+
+ if (session->transport->negotiate.sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) {
+ nt_status = cli_credentials_get_ntlm_response(io->in.credentials, state,
+ &flags,
+ session->transport->negotiate.secblob,
+ names_blob,
+ &state->setup.nt1.in.password1,
+ &state->setup.nt1.in.password2,
+ NULL, &session_key);
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+ } else if (session->options.plaintext_auth) {
+ const char *password = cli_credentials_get_password(io->in.credentials);
+ state->setup.nt1.in.password1 = data_blob_talloc(state, password, strlen(password));
+ state->setup.nt1.in.password2 = data_blob(NULL, 0);
+ } else {
+ /* could match windows client and return 'cannot logon from this workstation', but it just confuses everybody */
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ *req = smb_raw_sesssetup_send(session, &state->setup);
+ if (!*req) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ smbcli_transport_simple_set_signing(session->transport, session_key,
+ state->setup.nt1.in.password2);
+ set_user_session_key(session, &session_key);
+
+ data_blob_free(&session_key);
+ }
+
+ return (*req)->status;
+}
+
+
+/*
+ old style session setup (pre NT1 protocol level)
+*/
+static NTSTATUS session_setup_old(struct composite_context *c,
+ struct smbcli_session *session,
+ struct smb_composite_sesssetup *io,
+ struct smbcli_request **req)
+{
+ NTSTATUS nt_status;
+ struct sesssetup_state *state = talloc_get_type(c->private_data, struct sesssetup_state);
+ const char *password = cli_credentials_get_password(io->in.credentials);
+ DATA_BLOB names_blob = NTLMv2_generate_names_blob(state, session->transport->socket->hostname, cli_credentials_get_domain(io->in.credentials));
+ DATA_BLOB session_key;
+ int flags = 0;
+ if (session->options.lanman_auth) {
+ flags |= CLI_CRED_LANMAN_AUTH;
+ }
+
+ if (session->options.ntlmv2_auth) {
+ flags |= CLI_CRED_NTLMv2_AUTH;
+ }
+
+ state->setup.old.level = RAW_SESSSETUP_OLD;
+ state->setup.old.in.bufsize = session->transport->options.max_xmit;
+ state->setup.old.in.mpx_max = session->transport->options.max_mux;
+ state->setup.old.in.vc_num = 1;
+ state->setup.old.in.sesskey = io->in.sesskey;
+ state->setup.old.in.os = "Unix";
+ state->setup.old.in.lanman = talloc_asprintf(state, "Samba %s", SAMBA_VERSION_STRING);
+ cli_credentials_get_ntlm_username_domain(io->in.credentials, state,
+ &state->setup.old.in.user,
+ &state->setup.old.in.domain);
+
+ if (session->transport->negotiate.sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) {
+ nt_status = cli_credentials_get_ntlm_response(io->in.credentials, state,
+ &flags,
+ session->transport->negotiate.secblob,
+ names_blob,
+ &state->setup.old.in.password,
+ NULL,
+ NULL, &session_key);
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+ set_user_session_key(session, &session_key);
+
+ data_blob_free(&session_key);
+ } else if (session->options.plaintext_auth) {
+ state->setup.old.in.password = data_blob_talloc(state, password, strlen(password));
+ } else {
+ /* could match windows client and return 'cannot logon from this workstation', but it just confuses everybody */
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ *req = smb_raw_sesssetup_send(session, &state->setup);
+ if (!*req) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ return (*req)->status;
+}
+
+
+/*
+ Modern, all singing, all dancing extended security (and possibly SPNEGO) request
+*/
+static NTSTATUS session_setup_spnego(struct composite_context *c,
+ struct smbcli_session *session,
+ struct smb_composite_sesssetup *io,
+ struct smbcli_request **req)
+{
+ struct sesssetup_state *state = talloc_get_type(c->private_data, struct sesssetup_state);
+ NTSTATUS status;
+ const char *chosen_oid = NULL;
+
+ state->setup.spnego.level = RAW_SESSSETUP_SPNEGO;
+ state->setup.spnego.in.bufsize = session->transport->options.max_xmit;
+ state->setup.spnego.in.mpx_max = session->transport->options.max_mux;
+ state->setup.spnego.in.vc_num = 1;
+ state->setup.spnego.in.sesskey = io->in.sesskey;
+ state->setup.spnego.in.capabilities = io->in.capabilities;
+ state->setup.spnego.in.os = "Unix";
+ state->setup.spnego.in.lanman = talloc_asprintf(state, "Samba %s", SAMBA_VERSION_STRING);
+ state->setup.spnego.in.workgroup = io->in.workgroup;
+
+ smbcli_temp_set_signing(session->transport);
+
+ status = gensec_client_start(session, &session->gensec, c->event_ctx,
+ io->in.gensec_settings);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to start GENSEC client mode: %s\n", nt_errstr(status)));
+ return status;
+ }
+
+ gensec_want_feature(session->gensec, GENSEC_FEATURE_SESSION_KEY);
+
+ status = gensec_set_credentials(session->gensec, io->in.credentials);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to start set GENSEC client credentials: %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+
+ status = gensec_set_target_hostname(session->gensec, session->transport->socket->hostname);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to start set GENSEC target hostname: %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+
+ status = gensec_set_target_service(session->gensec, "cifs");
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to start set GENSEC target service: %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+
+ if (session->transport->negotiate.secblob.length) {
+ chosen_oid = GENSEC_OID_SPNEGO;
+ status = gensec_start_mech_by_oid(session->gensec, chosen_oid);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to start set GENSEC client mechanism %s: %s\n",
+ gensec_get_name_by_oid(session->gensec, chosen_oid), nt_errstr(status)));
+ chosen_oid = GENSEC_OID_NTLMSSP;
+ status = gensec_start_mech_by_oid(session->gensec, chosen_oid);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to start set (fallback) GENSEC client mechanism %s: %s\n",
+ gensec_get_name_by_oid(session->gensec, chosen_oid),
+ nt_errstr(status)));
+ return status;
+ }
+ }
+ } else {
+ /* without a sec blob, means raw NTLMSSP */
+ chosen_oid = GENSEC_OID_NTLMSSP;
+ status = gensec_start_mech_by_oid(session->gensec, chosen_oid);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to start set GENSEC client mechanism %s: %s\n",
+ gensec_get_name_by_oid(session->gensec, chosen_oid), nt_errstr(status)));
+ }
+ }
+
+ if ((const void *)chosen_oid == (const void *)GENSEC_OID_SPNEGO) {
+ status = gensec_update(session->gensec, state,
+ session->transport->negotiate.secblob,
+ &state->setup.spnego.in.secblob);
+ } else {
+ status = gensec_update(session->gensec, state,
+ data_blob(NULL, 0),
+ &state->setup.spnego.in.secblob);
+
+ }
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) &&
+ !NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed initial gensec_update with mechanism %s: %s\n",
+ gensec_get_name_by_oid(session->gensec, chosen_oid),
+ nt_errstr(status)));
+ return status;
+ }
+ state->gensec_status = status;
+
+ *req = smb_raw_sesssetup_send(session, &state->setup);
+ if (!*req) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /*
+ * we need to check the signature ourself
+ * as the session key might be the acceptor subkey
+ * which comes within the response itself
+ */
+ (*req)->sign_caller_checks = true;
+
+ return (*req)->status;
+}
+
+
+/*
+ composite session setup function that hides the details of all the
+ different session setup varients, including the multi-pass nature of
+ the spnego varient
+*/
+struct composite_context *smb_composite_sesssetup_send(struct smbcli_session *session,
+ struct smb_composite_sesssetup *io)
+{
+ struct composite_context *c;
+ struct sesssetup_state *state;
+ NTSTATUS status;
+
+ c = composite_create(session, session->transport->socket->event.ctx);
+ if (c == NULL) return NULL;
+
+ state = talloc_zero(c, struct sesssetup_state);
+ if (composite_nomem(state, c)) return c;
+ c->private_data = state;
+
+ state->io = io;
+
+ talloc_set_destructor(state, sesssetup_state_destructor);
+
+ /* no session setup at all in earliest protocol varients */
+ if (session->transport->negotiate.protocol < PROTOCOL_LANMAN1) {
+ ZERO_STRUCT(io->out);
+ composite_done(c);
+ return c;
+ }
+
+ /* see what session setup interface we will use */
+ if (session->transport->negotiate.protocol < PROTOCOL_NT1) {
+ status = session_setup_old(c, session, io, &state->req);
+ } else if (!session->transport->options.use_spnego ||
+ !(io->in.capabilities & CAP_EXTENDED_SECURITY)) {
+ status = session_setup_nt1(c, session, io, &state->req);
+ } else {
+ status = session_setup_spnego(c, session, io, &state->req);
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) ||
+ NT_STATUS_IS_OK(status)) {
+ composite_continue_smb(c, state->req, request_handler, c);
+ return c;
+ }
+
+ composite_error(c, status);
+ return c;
+}
+
+
+/*
+ receive a composite session setup reply
+*/
+NTSTATUS smb_composite_sesssetup_recv(struct composite_context *c)
+{
+ NTSTATUS status;
+ status = composite_wait(c);
+ talloc_free(c);
+ return status;
+}
+
+/*
+ sync version of smb_composite_sesssetup
+*/
+NTSTATUS smb_composite_sesssetup(struct smbcli_session *session, struct smb_composite_sesssetup *io)
+{
+ struct composite_context *c = smb_composite_sesssetup_send(session, io);
+ return smb_composite_sesssetup_recv(c);
+}
diff --git a/source4/libcli/smb_composite/smb2.c b/source4/libcli/smb_composite/smb2.c
new file mode 100644
index 0000000000..d71708a974
--- /dev/null
+++ b/source4/libcli/smb_composite/smb2.c
@@ -0,0 +1,370 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+/*
+ a composite API for making SMB-like calls using SMB2. This is useful
+ as SMB2 often requires more than one requests where a single SMB
+ request would do. In converting code that uses SMB to use SMB2,
+ these routines make life a lot easier
+*/
+
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/composite/composite.h"
+#include "libcli/smb_composite/smb_composite.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/*
+ continue after a SMB2 close
+ */
+static void continue_close(struct smb2_request *req)
+{
+ struct composite_context *ctx = talloc_get_type(req->async.private_data,
+ struct composite_context);
+ NTSTATUS status;
+ struct smb2_close close_parm;
+
+ status = smb2_close_recv(req, &close_parm);
+ composite_error(ctx, status);
+}
+
+/*
+ continue after the create in a composite unlink
+ */
+static void continue_unlink(struct smb2_request *req)
+{
+ struct composite_context *ctx = talloc_get_type(req->async.private_data,
+ struct composite_context);
+ struct smb2_tree *tree = req->tree;
+ struct smb2_create create_parm;
+ struct smb2_close close_parm;
+ NTSTATUS status;
+
+ status = smb2_create_recv(req, ctx, &create_parm);
+ if (!NT_STATUS_IS_OK(status)) {
+ composite_error(ctx, status);
+ return;
+ }
+
+ ZERO_STRUCT(close_parm);
+ close_parm.in.file.handle = create_parm.out.file.handle;
+
+ req = smb2_close_send(tree, &close_parm);
+ composite_continue_smb2(ctx, req, continue_close, ctx);
+}
+
+/*
+ composite SMB2 unlink call
+*/
+struct composite_context *smb2_composite_unlink_send(struct smb2_tree *tree,
+ union smb_unlink *io)
+{
+ struct composite_context *ctx;
+ struct smb2_create create_parm;
+ struct smb2_request *req;
+
+ ctx = composite_create(tree, tree->session->transport->socket->event.ctx);
+ if (ctx == NULL) return NULL;
+
+ /* check for wildcards - we could support these with a
+ search, but for now they aren't necessary */
+ if (strpbrk(io->unlink.in.pattern, "*?<>") != NULL) {
+ composite_error(ctx, NT_STATUS_NOT_SUPPORTED);
+ return ctx;
+ }
+
+ ZERO_STRUCT(create_parm);
+ create_parm.in.desired_access = SEC_STD_DELETE;
+ create_parm.in.create_disposition = NTCREATEX_DISP_OPEN;
+ create_parm.in.share_access =
+ NTCREATEX_SHARE_ACCESS_DELETE|
+ NTCREATEX_SHARE_ACCESS_READ|
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ create_parm.in.create_options =
+ NTCREATEX_OPTIONS_DELETE_ON_CLOSE |
+ NTCREATEX_OPTIONS_NON_DIRECTORY_FILE;
+ create_parm.in.fname = io->unlink.in.pattern;
+ if (create_parm.in.fname[0] == '\\') {
+ create_parm.in.fname++;
+ }
+
+ req = smb2_create_send(tree, &create_parm);
+
+ composite_continue_smb2(ctx, req, continue_unlink, ctx);
+ return ctx;
+}
+
+
+/*
+ composite unlink call - sync interface
+*/
+NTSTATUS smb2_composite_unlink(struct smb2_tree *tree, union smb_unlink *io)
+{
+ struct composite_context *c = smb2_composite_unlink_send(tree, io);
+ return composite_wait_free(c);
+}
+
+
+
+
+/*
+ continue after the create in a composite mkdir
+ */
+static void continue_mkdir(struct smb2_request *req)
+{
+ struct composite_context *ctx = talloc_get_type(req->async.private_data,
+ struct composite_context);
+ struct smb2_tree *tree = req->tree;
+ struct smb2_create create_parm;
+ struct smb2_close close_parm;
+ NTSTATUS status;
+
+ status = smb2_create_recv(req, ctx, &create_parm);
+ if (!NT_STATUS_IS_OK(status)) {
+ composite_error(ctx, status);
+ return;
+ }
+
+ ZERO_STRUCT(close_parm);
+ close_parm.in.file.handle = create_parm.out.file.handle;
+
+ req = smb2_close_send(tree, &close_parm);
+ composite_continue_smb2(ctx, req, continue_close, ctx);
+}
+
+/*
+ composite SMB2 mkdir call
+*/
+struct composite_context *smb2_composite_mkdir_send(struct smb2_tree *tree,
+ union smb_mkdir *io)
+{
+ struct composite_context *ctx;
+ struct smb2_create create_parm;
+ struct smb2_request *req;
+
+ ctx = composite_create(tree, tree->session->transport->socket->event.ctx);
+ if (ctx == NULL) return NULL;
+
+ ZERO_STRUCT(create_parm);
+
+ create_parm.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED;
+ create_parm.in.share_access =
+ NTCREATEX_SHARE_ACCESS_READ|
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ create_parm.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
+ create_parm.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
+ create_parm.in.create_disposition = NTCREATEX_DISP_CREATE;
+ create_parm.in.fname = io->mkdir.in.path;
+ if (create_parm.in.fname[0] == '\\') {
+ create_parm.in.fname++;
+ }
+
+ req = smb2_create_send(tree, &create_parm);
+
+ composite_continue_smb2(ctx, req, continue_mkdir, ctx);
+
+ return ctx;
+}
+
+
+/*
+ composite mkdir call - sync interface
+*/
+NTSTATUS smb2_composite_mkdir(struct smb2_tree *tree, union smb_mkdir *io)
+{
+ struct composite_context *c = smb2_composite_mkdir_send(tree, io);
+ return composite_wait_free(c);
+}
+
+
+
+/*
+ continue after the create in a composite rmdir
+ */
+static void continue_rmdir(struct smb2_request *req)
+{
+ struct composite_context *ctx = talloc_get_type(req->async.private_data,
+ struct composite_context);
+ struct smb2_tree *tree = req->tree;
+ struct smb2_create create_parm;
+ struct smb2_close close_parm;
+ NTSTATUS status;
+
+ status = smb2_create_recv(req, ctx, &create_parm);
+ if (!NT_STATUS_IS_OK(status)) {
+ composite_error(ctx, status);
+ return;
+ }
+
+ ZERO_STRUCT(close_parm);
+ close_parm.in.file.handle = create_parm.out.file.handle;
+
+ req = smb2_close_send(tree, &close_parm);
+ composite_continue_smb2(ctx, req, continue_close, ctx);
+}
+
+/*
+ composite SMB2 rmdir call
+*/
+struct composite_context *smb2_composite_rmdir_send(struct smb2_tree *tree,
+ struct smb_rmdir *io)
+{
+ struct composite_context *ctx;
+ struct smb2_create create_parm;
+ struct smb2_request *req;
+
+ ctx = composite_create(tree, tree->session->transport->socket->event.ctx);
+ if (ctx == NULL) return NULL;
+
+ ZERO_STRUCT(create_parm);
+ create_parm.in.desired_access = SEC_STD_DELETE;
+ create_parm.in.create_disposition = NTCREATEX_DISP_OPEN;
+ create_parm.in.share_access =
+ NTCREATEX_SHARE_ACCESS_DELETE|
+ NTCREATEX_SHARE_ACCESS_READ|
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ create_parm.in.create_options =
+ NTCREATEX_OPTIONS_DIRECTORY |
+ NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
+ create_parm.in.fname = io->in.path;
+ if (create_parm.in.fname[0] == '\\') {
+ create_parm.in.fname++;
+ }
+
+ req = smb2_create_send(tree, &create_parm);
+
+ composite_continue_smb2(ctx, req, continue_rmdir, ctx);
+ return ctx;
+}
+
+
+/*
+ composite rmdir call - sync interface
+*/
+NTSTATUS smb2_composite_rmdir(struct smb2_tree *tree, struct smb_rmdir *io)
+{
+ struct composite_context *c = smb2_composite_rmdir_send(tree, io);
+ return composite_wait_free(c);
+}
+
+
+/*
+ continue after the setfileinfo in a composite setpathinfo
+ */
+static void continue_setpathinfo_close(struct smb2_request *req)
+{
+ struct composite_context *ctx = talloc_get_type(req->async.private_data,
+ struct composite_context);
+ struct smb2_tree *tree = req->tree;
+ struct smb2_close close_parm;
+ NTSTATUS status;
+ union smb_setfileinfo *io2 = talloc_get_type(ctx->private_data,
+ union smb_setfileinfo);
+
+ status = smb2_setinfo_recv(req);
+ if (!NT_STATUS_IS_OK(status)) {
+ composite_error(ctx, status);
+ return;
+ }
+
+ ZERO_STRUCT(close_parm);
+ close_parm.in.file.handle = io2->generic.in.file.handle;
+
+ req = smb2_close_send(tree, &close_parm);
+ composite_continue_smb2(ctx, req, continue_close, ctx);
+}
+
+
+/*
+ continue after the create in a composite setpathinfo
+ */
+static void continue_setpathinfo(struct smb2_request *req)
+{
+ struct composite_context *ctx = talloc_get_type(req->async.private_data,
+ struct composite_context);
+ struct smb2_tree *tree = req->tree;
+ struct smb2_create create_parm;
+ NTSTATUS status;
+ union smb_setfileinfo *io2 = talloc_get_type(ctx->private_data,
+ union smb_setfileinfo);
+
+ status = smb2_create_recv(req, ctx, &create_parm);
+ if (!NT_STATUS_IS_OK(status)) {
+ composite_error(ctx, status);
+ return;
+ }
+
+ io2->generic.in.file.handle = create_parm.out.file.handle;
+
+ req = smb2_setinfo_file_send(tree, io2);
+ composite_continue_smb2(ctx, req, continue_setpathinfo_close, ctx);
+}
+
+
+/*
+ composite SMB2 setpathinfo call
+*/
+struct composite_context *smb2_composite_setpathinfo_send(struct smb2_tree *tree,
+ union smb_setfileinfo *io)
+{
+ struct composite_context *ctx;
+ struct smb2_create create_parm;
+ struct smb2_request *req;
+ union smb_setfileinfo *io2;
+
+ ctx = composite_create(tree, tree->session->transport->socket->event.ctx);
+ if (ctx == NULL) return NULL;
+
+ ZERO_STRUCT(create_parm);
+ create_parm.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED;
+ create_parm.in.create_disposition = NTCREATEX_DISP_OPEN;
+ create_parm.in.share_access =
+ NTCREATEX_SHARE_ACCESS_DELETE|
+ NTCREATEX_SHARE_ACCESS_READ|
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ create_parm.in.create_options = 0;
+ create_parm.in.fname = io->generic.in.file.path;
+ if (create_parm.in.fname[0] == '\\') {
+ create_parm.in.fname++;
+ }
+
+ req = smb2_create_send(tree, &create_parm);
+
+ io2 = talloc(ctx, union smb_setfileinfo);
+ if (composite_nomem(io2, ctx)) {
+ return ctx;
+ }
+ *io2 = *io;
+
+ ctx->private_data = io2;
+
+ composite_continue_smb2(ctx, req, continue_setpathinfo, ctx);
+ return ctx;
+}
+
+
+/*
+ composite setpathinfo call
+ */
+NTSTATUS smb2_composite_setpathinfo(struct smb2_tree *tree, union smb_setfileinfo *io)
+{
+ struct composite_context *c = smb2_composite_setpathinfo_send(tree, io);
+ return composite_wait_free(c);
+}
diff --git a/source4/libcli/smb_composite/smb_composite.h b/source4/libcli/smb_composite/smb_composite.h
new file mode 100644
index 0000000000..a1e1e99d7e
--- /dev/null
+++ b/source4/libcli/smb_composite/smb_composite.h
@@ -0,0 +1,195 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB composite request interfaces
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ this defines the structures associated with "composite"
+ requests. Composite requests are libcli requests that are internally
+ implemented as multiple libcli/raw/ calls, but can be treated as a
+ single call via these composite calls. The composite calls are
+ particularly designed to be used in async applications
+*/
+
+#include "libcli/raw/signing.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/smb2/smb2.h"
+
+
+/*
+ a composite open/read(s)/close request that loads a whole file
+ into memory. Used as a demo of the composite system.
+*/
+struct smb_composite_loadfile {
+ struct {
+ const char *fname;
+ } in;
+ struct {
+ uint8_t *data;
+ uint32_t size;
+ } out;
+};
+
+struct smb_composite_fetchfile {
+ struct {
+ const char *dest_host;
+ const char **ports;
+ const char *called_name;
+ const char *service;
+ const char *service_type;
+ const char *socket_options;
+ struct cli_credentials *credentials;
+ const char *workgroup;
+ const char *filename;
+ struct smbcli_options options;
+ struct smbcli_session_options session_options;
+ struct resolve_context *resolve_ctx;
+ struct smb_iconv_convenience *iconv_convenience;
+ struct gensec_settings *gensec_settings;
+ } in;
+ struct {
+ uint8_t *data;
+ uint32_t size;
+ } out;
+};
+
+/*
+ a composite open/write(s)/close request that saves a whole file from
+ memory. Used as a demo of the composite system.
+*/
+struct smb_composite_savefile {
+ struct {
+ const char *fname;
+ uint8_t *data;
+ uint32_t size;
+ } in;
+};
+
+
+/*
+ a composite request for a full connection to a remote server. Includes
+
+ - socket establishment
+ - session request
+ - negprot
+ - session setup (if credentials are not NULL)
+ - tree connect (if service is not NULL)
+*/
+struct smb_composite_connect {
+ struct {
+ const char *dest_host;
+ const char **dest_ports;
+ const char *socket_options;
+ const char *called_name;
+ const char *service;
+ const char *service_type;
+ struct cli_credentials *credentials;
+ bool fallback_to_anonymous;
+ const char *workgroup;
+ struct smbcli_options options;
+ struct smbcli_session_options session_options;
+ struct smb_iconv_convenience *iconv_convenience;
+ struct gensec_settings *gensec_settings;
+ } in;
+ struct {
+ struct smbcli_tree *tree;
+ bool anonymous_fallback_done;
+ } out;
+};
+
+
+/*
+ generic session setup interface that takes care of which
+ session setup varient to use
+*/
+struct smb_composite_sesssetup {
+ struct {
+ uint32_t sesskey;
+ uint32_t capabilities;
+ struct cli_credentials *credentials;
+ const char *workgroup;
+ struct gensec_settings *gensec_settings;
+ } in;
+ struct {
+ uint16_t vuid;
+ } out;
+};
+
+/*
+ query file system info
+*/
+struct smb_composite_fsinfo {
+ struct {
+ const char *dest_host;
+ const char **dest_ports;
+ const char *socket_options;
+ const char *called_name;
+ const char *service;
+ const char *service_type;
+ struct cli_credentials *credentials;
+ const char *workgroup;
+ enum smb_fsinfo_level level;
+ struct smb_iconv_convenience *iconv_convenience;
+ struct gensec_settings *gensec_settings;
+ } in;
+
+ struct {
+ union smb_fsinfo *fsinfo;
+ } out;
+};
+
+/*
+ composite call for appending new acl to the file's security descriptor and get
+ new full acl
+*/
+
+struct smb_composite_appendacl {
+ struct {
+ const char *fname;
+
+ const struct security_descriptor *sd;
+ } in;
+
+ struct {
+ struct security_descriptor *sd;
+ } out;
+};
+
+/*
+ a composite API to fire connect() calls to multiple targets, picking the
+ first one.
+*/
+
+struct smb_composite_connectmulti {
+ struct {
+ int num_dests;
+ const char **hostnames;
+ const char **addresses;
+ int *ports; /* Either NULL for lp_smb_ports() per
+ * destination or a list of explicit ports */
+ } in;
+ struct {
+ struct smbcli_socket *socket;
+ } out;
+};
+
+struct smbcli_session;
+struct resolve_context;
+
+#include "libcli/smb_composite/proto.h"
diff --git a/source4/libcli/smbc/README b/source4/libcli/smbc/README
new file mode 100644
index 0000000000..66b0782722
--- /dev/null
+++ b/source4/libcli/smbc/README
@@ -0,0 +1 @@
+This is where the new samba4 libsmbclient will live.
diff --git a/source4/libcli/util/clilsa.c b/source4/libcli/util/clilsa.c
new file mode 100644
index 0000000000..16967d73b0
--- /dev/null
+++ b/source4/libcli/util/clilsa.c
@@ -0,0 +1,359 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ lsa calls for file sharing connections
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ when dealing with ACLs the file sharing client code needs to
+ sometimes make LSA RPC calls. This code provides an easy interface
+ for doing those calls.
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/libcli.h"
+#include "libcli/security/security.h"
+#include "librpc/gen_ndr/ndr_lsa.h"
+#include "librpc/gen_ndr/ndr_lsa_c.h"
+#include "libcli/util/clilsa.h"
+
+struct smblsa_state {
+ struct dcerpc_pipe *pipe;
+ struct smbcli_tree *ipc_tree;
+ struct policy_handle handle;
+};
+
+/*
+ establish the lsa pipe connection
+*/
+static NTSTATUS smblsa_connect(struct smbcli_state *cli)
+{
+ struct smblsa_state *lsa;
+ NTSTATUS status;
+ struct lsa_OpenPolicy r;
+ uint16_t system_name = '\\';
+ union smb_tcon tcon;
+ struct lsa_ObjectAttribute attr;
+ struct lsa_QosInfo qos;
+
+ if (cli->lsa != NULL) {
+ return NT_STATUS_OK;
+ }
+
+ lsa = talloc(cli, struct smblsa_state);
+ if (lsa == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ lsa->ipc_tree = smbcli_tree_init(cli->session, lsa, false);
+ if (lsa->ipc_tree == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* connect to IPC$ */
+ tcon.generic.level = RAW_TCON_TCONX;
+ tcon.tconx.in.flags = 0;
+ tcon.tconx.in.password = data_blob(NULL, 0);
+ tcon.tconx.in.path = "ipc$";
+ tcon.tconx.in.device = "IPC";
+ status = smb_raw_tcon(lsa->ipc_tree, lsa, &tcon);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lsa);
+ return status;
+ }
+ lsa->ipc_tree->tid = tcon.tconx.out.tid;
+
+ lsa->pipe = dcerpc_pipe_init(lsa, cli->transport->socket->event.ctx,
+ cli->transport->iconv_convenience);
+ if (lsa->pipe == NULL) {
+ talloc_free(lsa);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* open the LSA pipe */
+ status = dcerpc_pipe_open_smb(lsa->pipe, lsa->ipc_tree, NDR_LSARPC_NAME);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lsa);
+ return status;
+ }
+
+ /* bind to the LSA pipe */
+ status = dcerpc_bind_auth_none(lsa->pipe, &ndr_table_lsarpc);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lsa);
+ return status;
+ }
+
+
+ /* open a lsa 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 = &system_name;
+ r.in.attr = &attr;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.handle = &lsa->handle;
+
+ status = dcerpc_lsa_OpenPolicy(lsa->pipe, lsa, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lsa);
+ return status;
+ }
+
+ cli->lsa = lsa;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ return the set of privileges for the given sid
+*/
+NTSTATUS smblsa_sid_privileges(struct smbcli_state *cli, struct dom_sid *sid,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_RightSet *rights)
+{
+ NTSTATUS status;
+ struct lsa_EnumAccountRights r;
+
+ status = smblsa_connect(cli);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ r.in.handle = &cli->lsa->handle;
+ r.in.sid = sid;
+ r.out.rights = rights;
+
+ return dcerpc_lsa_EnumAccountRights(cli->lsa->pipe, mem_ctx, &r);
+}
+
+
+/*
+ check if a named sid has a particular named privilege
+*/
+NTSTATUS smblsa_sid_check_privilege(struct smbcli_state *cli,
+ const char *sid_str,
+ const char *privilege)
+{
+ struct lsa_RightSet rights;
+ NTSTATUS status;
+ TALLOC_CTX *mem_ctx = talloc_new(cli);
+ struct dom_sid *sid;
+ unsigned i;
+
+ sid = dom_sid_parse_talloc(mem_ctx, sid_str);
+ if (sid == NULL) {
+ talloc_free(mem_ctx);
+ return NT_STATUS_INVALID_SID;
+ }
+
+ status = smblsa_sid_privileges(cli, sid, mem_ctx, &rights);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(mem_ctx);
+ return status;
+ }
+
+ for (i=0;i<rights.count;i++) {
+ if (strcmp(rights.names[i].string, privilege) == 0) {
+ talloc_free(mem_ctx);
+ return NT_STATUS_OK;
+ }
+ }
+
+ talloc_free(mem_ctx);
+ return NT_STATUS_NOT_FOUND;
+}
+
+
+/*
+ lookup a SID, returning its name
+*/
+NTSTATUS smblsa_lookup_sid(struct smbcli_state *cli,
+ const char *sid_str,
+ TALLOC_CTX *mem_ctx,
+ const char **name)
+{
+ struct lsa_LookupSids r;
+ struct lsa_TransNameArray names;
+ struct lsa_SidArray sids;
+ struct lsa_RefDomainList *domains = NULL;
+ uint32_t count = 1;
+ NTSTATUS status;
+ struct dom_sid *sid;
+ TALLOC_CTX *mem_ctx2 = talloc_new(mem_ctx);
+
+ status = smblsa_connect(cli);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ sid = dom_sid_parse_talloc(mem_ctx2, sid_str);
+ if (sid == NULL) {
+ return NT_STATUS_INVALID_SID;
+ }
+
+ names.count = 0;
+ names.names = NULL;
+
+ sids.num_sids = 1;
+ sids.sids = talloc(mem_ctx2, struct lsa_SidPtr);
+ sids.sids[0].sid = sid;
+
+ r.in.handle = &cli->lsa->handle;
+ r.in.sids = &sids;
+ r.in.names = &names;
+ r.in.level = 1;
+ r.in.count = &count;
+ r.out.count = &count;
+ r.out.names = &names;
+ r.out.domains = &domains;
+
+ status = dcerpc_lsa_LookupSids(cli->lsa->pipe, mem_ctx2, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(mem_ctx2);
+ return status;
+ }
+ if (names.count != 1) {
+ talloc_free(mem_ctx2);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ (*name) = talloc_asprintf(mem_ctx, "%s\\%s",
+ domains->domains[0].name.string,
+ names.names[0].name.string);
+
+ talloc_free(mem_ctx2);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ lookup a name, returning its sid
+*/
+NTSTATUS smblsa_lookup_name(struct smbcli_state *cli,
+ const char *name,
+ TALLOC_CTX *mem_ctx,
+ const char **sid_str)
+{
+ struct lsa_LookupNames r;
+ struct lsa_TransSidArray sids;
+ struct lsa_String names;
+ struct lsa_RefDomainList *domains = NULL;
+ uint32_t count = 1;
+ NTSTATUS status;
+ struct dom_sid *sid;
+ TALLOC_CTX *mem_ctx2 = talloc_new(mem_ctx);
+ uint32_t rid;
+
+ status = smblsa_connect(cli);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ sids.count = 0;
+ sids.sids = NULL;
+
+ names.string = name;
+
+ r.in.handle = &cli->lsa->handle;
+ r.in.num_names = 1;
+ r.in.names = &names;
+ r.in.sids = &sids;
+ r.in.level = 1;
+ r.in.count = &count;
+ r.out.count = &count;
+ r.out.sids = &sids;
+ r.out.domains = &domains;
+
+ status = dcerpc_lsa_LookupNames(cli->lsa->pipe, mem_ctx2, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(mem_ctx2);
+ return status;
+ }
+ if (sids.count != 1) {
+ talloc_free(mem_ctx2);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ sid = domains->domains[0].sid;
+ rid = sids.sids[0].rid;
+
+ (*sid_str) = talloc_asprintf(mem_ctx, "%s-%u",
+ dom_sid_string(mem_ctx2, sid), rid);
+
+ talloc_free(mem_ctx2);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ add a set of privileges to the given sid
+*/
+NTSTATUS smblsa_sid_add_privileges(struct smbcli_state *cli, struct dom_sid *sid,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_RightSet *rights)
+{
+ NTSTATUS status;
+ struct lsa_AddAccountRights r;
+
+ status = smblsa_connect(cli);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ r.in.handle = &cli->lsa->handle;
+ r.in.sid = sid;
+ r.in.rights = rights;
+
+ return dcerpc_lsa_AddAccountRights(cli->lsa->pipe, mem_ctx, &r);
+}
+
+/*
+ remove a set of privileges from the given sid
+*/
+NTSTATUS smblsa_sid_del_privileges(struct smbcli_state *cli, struct dom_sid *sid,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_RightSet *rights)
+{
+ NTSTATUS status;
+ struct lsa_RemoveAccountRights r;
+
+ status = smblsa_connect(cli);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ r.in.handle = &cli->lsa->handle;
+ r.in.sid = sid;
+ r.in.remove_all = 0;
+ r.in.rights = rights;
+
+ return dcerpc_lsa_RemoveAccountRights(cli->lsa->pipe, mem_ctx, &r);
+}
diff --git a/source4/libcli/util/errormap.c b/source4/libcli/util/errormap.c
new file mode 100644
index 0000000000..930e45b214
--- /dev/null
+++ b/source4/libcli/util/errormap.c
@@ -0,0 +1,1408 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * error mapping functions
+ * Copyright (C) Andrew Tridgell 2001
+ * Copyright (C) Andrew Bartlett 2001
+ * Copyright (C) Tim Potter 2000
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "librpc/ndr/libndr.h"
+
+/* This map was extracted by the ERRMAPEXTRACT smbtorture command.
+ The setup was a Samba HEAD (2002-01-03) PDC and an Win2k member
+ workstation. The PDC was modified (by using the 'name_to_nt_status'
+ authentication module) to convert the username (in hex) into the
+ corresponding NTSTATUS error return.
+
+ By opening two nbt sessions to the Win2k workstation, one negotiating
+ DOS and one negotiating NT errors it was possible to extract the
+ error mapping. (Because the server only supplies NT errors, the
+ NT4 workstation had to use its own error tables to convert these
+ to dos errors).
+
+ Some errors show up as 'squashed' because the NT error connection
+ got back a different error to the one it sent, so a mapping could
+ not be determined (a guess has been made in this case, to map the
+ error as squashed). This is done mainly to prevent users from getting
+ NT_STATUS_WRONG_PASSWORD and NT_STATUS_NO_SUCH_USER errors (they get
+ NT_STATUS_LOGON_FAILURE instead.
+
+ -- abartlet (2002-01-03)
+*/
+
+/* NT status -> dos error map */
+static const struct {
+ uint8_t dos_class;
+ uint32_t dos_code;
+ NTSTATUS ntstatus;
+} ntstatus_to_dos_map[] = {
+ {ERRDOS, ERRnofiles, STATUS_NO_MORE_FILES},
+ {ERRDOS, ERRnofiles, NT_STATUS_NO_MORE_ENTRIES},
+ {ERRDOS, ERRgeneral, NT_STATUS_UNSUCCESSFUL},
+ {ERRDOS, ERRbadfunc, NT_STATUS_NOT_IMPLEMENTED},
+ {ERRDOS, 87, NT_STATUS_INVALID_INFO_CLASS},
+ {ERRDOS, 24, NT_STATUS_INFO_LENGTH_MISMATCH},
+ {ERRHRD, ERRgeneral, NT_STATUS_ACCESS_VIOLATION},
+ {ERRHRD, ERRgeneral, NT_STATUS_IN_PAGE_ERROR},
+ {ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_QUOTA},
+ {ERRDOS, ERRbadfid, NT_STATUS_INVALID_HANDLE},
+ {ERRHRD, ERRgeneral, NT_STATUS_BAD_INITIAL_STACK},
+ {ERRDOS, 193, NT_STATUS_BAD_INITIAL_PC},
+ {ERRDOS, 87, NT_STATUS_INVALID_CID},
+ {ERRHRD, ERRgeneral, NT_STATUS_TIMER_NOT_CANCELED},
+ {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER},
+ {ERRDOS, ERRbadfile, NT_STATUS_NO_SUCH_DEVICE},
+ {ERRDOS, ERRbadfile, NT_STATUS_NO_SUCH_FILE},
+ {ERRDOS, ERRbadfunc, NT_STATUS_INVALID_DEVICE_REQUEST},
+ {ERRDOS, 38, NT_STATUS_END_OF_FILE},
+ {ERRDOS, 34, NT_STATUS_WRONG_VOLUME},
+ {ERRDOS, 21, NT_STATUS_NO_MEDIA_IN_DEVICE},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNRECOGNIZED_MEDIA},
+ {ERRDOS, 27, NT_STATUS_NONEXISTENT_SECTOR},
+/** Session setup succeeded. This shouldn't happen...*/
+/** Session setup succeeded. This shouldn't happen...*/
+/** NT error on DOS connection! (NT_STATUS_OK) */
+/* { This NT error code was 'sqashed'
+ from NT_STATUS_MORE_PROCESSING_REQUIRED to NT_STATUS_OK
+ during the session setup }
+*/
+#if 0
+ {SUCCESS, 0, NT_STATUS_OK},
+#endif
+ {ERRDOS, ERRnomem, NT_STATUS_NO_MEMORY},
+ {ERRDOS, 487, NT_STATUS_CONFLICTING_ADDRESSES},
+ {ERRDOS, 487, NT_STATUS_NOT_MAPPED_VIEW},
+ {ERRDOS, 87, NT_STATUS_UNABLE_TO_FREE_VM},
+ {ERRDOS, 87, NT_STATUS_UNABLE_TO_DELETE_SECTION},
+ {ERRDOS, 2142, NT_STATUS_INVALID_SYSTEM_SERVICE},
+ {ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_INSTRUCTION},
+ {ERRDOS, ERRnoaccess, NT_STATUS_INVALID_LOCK_SEQUENCE},
+ {ERRDOS, ERRnoaccess, NT_STATUS_INVALID_VIEW_SIZE},
+ {ERRDOS, 193, NT_STATUS_INVALID_FILE_FOR_SECTION},
+ {ERRDOS, ERRnoaccess, NT_STATUS_ALREADY_COMMITTED},
+/* { This NT error code was 'sqashed'
+ from NT_STATUS_ACCESS_DENIED to NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE
+ during the session setup }
+*/
+ {ERRDOS, ERRnoaccess, NT_STATUS_ACCESS_DENIED},
+ {ERRDOS, 111, NT_STATUS_BUFFER_TOO_SMALL},
+ {ERRDOS, ERRbadfid, NT_STATUS_OBJECT_TYPE_MISMATCH},
+ {ERRHRD, ERRgeneral, NT_STATUS_NONCONTINUABLE_EXCEPTION},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_DISPOSITION},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNWIND},
+ {ERRHRD, ERRgeneral, NT_STATUS_BAD_STACK},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_UNWIND_TARGET},
+ {ERRDOS, 158, NT_STATUS_NOT_LOCKED},
+ {ERRHRD, ERRgeneral, NT_STATUS_PARITY_ERROR},
+ {ERRDOS, 487, NT_STATUS_UNABLE_TO_DECOMMIT_VM},
+ {ERRDOS, 487, NT_STATUS_NOT_COMMITTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_PORT_ATTRIBUTES},
+ {ERRHRD, ERRgeneral, NT_STATUS_PORT_MESSAGE_TOO_LONG},
+ {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_MIX},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_QUOTA_LOWER},
+ {ERRHRD, ERRgeneral, NT_STATUS_DISK_CORRUPT_ERROR},
+ {ERRDOS, ERRinvalidname, NT_STATUS_OBJECT_NAME_INVALID},
+ {ERRDOS, ERRbadfile, NT_STATUS_OBJECT_NAME_NOT_FOUND},
+ {ERRDOS, ERRfilexists, NT_STATUS_OBJECT_NAME_COLLISION},
+ {ERRHRD, ERRgeneral, NT_STATUS_HANDLE_NOT_WAITABLE},
+ {ERRDOS, ERRbadfid, NT_STATUS_PORT_DISCONNECTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_DEVICE_ALREADY_ATTACHED},
+ {ERRDOS, 161, NT_STATUS_OBJECT_PATH_INVALID},
+ {ERRDOS, ERRbadpath, NT_STATUS_OBJECT_PATH_NOT_FOUND},
+ {ERRDOS, 161, NT_STATUS_OBJECT_PATH_SYNTAX_BAD},
+ {ERRHRD, ERRgeneral, NT_STATUS_DATA_OVERRUN},
+ {ERRHRD, ERRgeneral, NT_STATUS_DATA_LATE_ERROR},
+ {ERRDOS, 23, NT_STATUS_DATA_ERROR},
+ {ERRDOS, 23, NT_STATUS_CRC_ERROR},
+ {ERRDOS, ERRnomem, NT_STATUS_SECTION_TOO_BIG},
+ {ERRDOS, ERRnoaccess, NT_STATUS_PORT_CONNECTION_REFUSED},
+ {ERRDOS, ERRbadfid, NT_STATUS_INVALID_PORT_HANDLE},
+ {ERRDOS, ERRbadshare, NT_STATUS_SHARING_VIOLATION},
+ {ERRHRD, ERRgeneral, NT_STATUS_QUOTA_EXCEEDED},
+ {ERRDOS, 87, NT_STATUS_INVALID_PAGE_PROTECTION},
+ {ERRDOS, 288, NT_STATUS_MUTANT_NOT_OWNED},
+ {ERRDOS, 298, NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED},
+ {ERRDOS, 87, NT_STATUS_PORT_ALREADY_SET},
+ {ERRDOS, 87, NT_STATUS_SECTION_NOT_IMAGE},
+ {ERRDOS, 156, NT_STATUS_SUSPEND_COUNT_EXCEEDED},
+ {ERRDOS, ERRnoaccess, NT_STATUS_THREAD_IS_TERMINATING},
+ {ERRDOS, 87, NT_STATUS_BAD_WORKING_SET_LIMIT},
+ {ERRDOS, 87, NT_STATUS_INCOMPATIBLE_FILE_MAP},
+ {ERRDOS, 87, NT_STATUS_SECTION_PROTECTION},
+ {ERRDOS, 282, NT_STATUS_EAS_NOT_SUPPORTED},
+ {ERRDOS, 255, NT_STATUS_EA_TOO_LARGE},
+ {ERRHRD, ERRgeneral, NT_STATUS_NONEXISTENT_EA_ENTRY},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_EAS_ON_FILE},
+ {ERRHRD, ERRgeneral, NT_STATUS_EA_CORRUPT_ERROR},
+ {ERRDOS, ERRlock, NT_STATUS_FILE_LOCK_CONFLICT},
+ {ERRDOS, ERRlock, NT_STATUS_LOCK_NOT_GRANTED},
+ {ERRDOS, ERRnoaccess, NT_STATUS_DELETE_PENDING},
+ {ERRDOS, ERRunsup, NT_STATUS_CTL_FILE_NOT_SUPPORTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNKNOWN_REVISION},
+ {ERRHRD, ERRgeneral, NT_STATUS_REVISION_MISMATCH},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_OWNER},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_PRIMARY_GROUP},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_IMPERSONATION_TOKEN},
+ {ERRHRD, ERRgeneral, NT_STATUS_CANT_DISABLE_MANDATORY},
+ {ERRDOS, 2215, NT_STATUS_NO_LOGON_SERVERS},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_LOGON_SESSION},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_PRIVILEGE},
+ {ERRDOS, ERRnoaccess, NT_STATUS_PRIVILEGE_NOT_HELD},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_ACCOUNT_NAME},
+ {ERRHRD, ERRgeneral, NT_STATUS_USER_EXISTS},
+/* { This NT error code was 'sqashed'
+ from NT_STATUS_NO_SUCH_USER to NT_STATUS_LOGON_FAILURE
+ during the session setup }
+*/
+ {ERRDOS, ERRnoaccess, NT_STATUS_NO_SUCH_USER},
+ {ERRHRD, ERRgeneral, NT_STATUS_GROUP_EXISTS},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_GROUP},
+ {ERRHRD, ERRgeneral, NT_STATUS_MEMBER_IN_GROUP},
+ {ERRHRD, ERRgeneral, NT_STATUS_MEMBER_NOT_IN_GROUP},
+ {ERRHRD, ERRgeneral, NT_STATUS_LAST_ADMIN},
+/* { This NT error code was 'sqashed'
+ from NT_STATUS_WRONG_PASSWORD to NT_STATUS_LOGON_FAILURE
+ during the session setup }
+*/
+ {ERRSRV, ERRbadpw, NT_STATUS_WRONG_PASSWORD},
+ {ERRHRD, ERRgeneral, NT_STATUS_ILL_FORMED_PASSWORD},
+ {ERRHRD, ERRgeneral, NT_STATUS_PASSWORD_RESTRICTION},
+ {ERRDOS, ERRnoaccess, NT_STATUS_LOGON_FAILURE},
+ {ERRHRD, ERRgeneral, NT_STATUS_ACCOUNT_RESTRICTION},
+ {ERRSRV, 2241, NT_STATUS_INVALID_LOGON_HOURS},
+ {ERRSRV, 2240, NT_STATUS_INVALID_WORKSTATION},
+ {ERRSRV, 2242, NT_STATUS_PASSWORD_EXPIRED},
+ {ERRSRV, 2239, NT_STATUS_ACCOUNT_DISABLED},
+ {ERRHRD, ERRgeneral, NT_STATUS_NONE_MAPPED},
+ {ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_LUIDS_REQUESTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_LUIDS_EXHAUSTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_SUB_AUTHORITY},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_ACL},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_SID},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_SECURITY_DESCR},
+ {ERRDOS, 127, NT_STATUS_PROCEDURE_NOT_FOUND},
+ {ERRDOS, 193, NT_STATUS_INVALID_IMAGE_FORMAT},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_TOKEN},
+ {ERRHRD, ERRgeneral, NT_STATUS_BAD_INHERITANCE_ACL},
+ {ERRDOS, 158, NT_STATUS_RANGE_NOT_LOCKED},
+ {ERRDOS, 112, NT_STATUS_DISK_FULL},
+ {ERRHRD, ERRgeneral, NT_STATUS_SERVER_DISABLED},
+ {ERRHRD, ERRgeneral, NT_STATUS_SERVER_NOT_DISABLED},
+ {ERRDOS, 68, NT_STATUS_TOO_MANY_GUIDS_REQUESTED},
+ {ERRDOS, 259, NT_STATUS_GUIDS_EXHAUSTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_ID_AUTHORITY},
+ {ERRDOS, 259, NT_STATUS_AGENTS_EXHAUSTED},
+ {ERRDOS, 154, NT_STATUS_INVALID_VOLUME_LABEL},
+ {ERRDOS, ERRres, NT_STATUS_SECTION_NOT_EXTENDED},
+ {ERRDOS, 487, NT_STATUS_NOT_MAPPED_DATA},
+ {ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_DATA_NOT_FOUND},
+ {ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_TYPE_NOT_FOUND},
+ {ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_NAME_NOT_FOUND},
+ {ERRHRD, ERRgeneral, NT_STATUS_ARRAY_BOUNDS_EXCEEDED},
+ {ERRHRD, ERRgeneral, NT_STATUS_FLOAT_DENORMAL_OPERAND},
+ {ERRHRD, ERRgeneral, NT_STATUS_FLOAT_DIVIDE_BY_ZERO},
+ {ERRHRD, ERRgeneral, NT_STATUS_FLOAT_INEXACT_RESULT},
+ {ERRHRD, ERRgeneral, NT_STATUS_FLOAT_INVALID_OPERATION},
+ {ERRHRD, ERRgeneral, NT_STATUS_FLOAT_OVERFLOW},
+ {ERRHRD, ERRgeneral, NT_STATUS_FLOAT_STACK_CHECK},
+ {ERRHRD, ERRgeneral, NT_STATUS_FLOAT_UNDERFLOW},
+ {ERRHRD, ERRgeneral, NT_STATUS_INTEGER_DIVIDE_BY_ZERO},
+ {ERRDOS, 534, NT_STATUS_INTEGER_OVERFLOW},
+ {ERRHRD, ERRgeneral, NT_STATUS_PRIVILEGED_INSTRUCTION},
+ {ERRDOS, ERRnomem, NT_STATUS_TOO_MANY_PAGING_FILES},
+ {ERRHRD, ERRgeneral, NT_STATUS_FILE_INVALID},
+ {ERRHRD, ERRgeneral, NT_STATUS_ALLOTTED_SPACE_EXCEEDED},
+/* { This NT error code was 'sqashed'
+ from NT_STATUS_INSUFFICIENT_RESOURCES to NT_STATUS_INSUFF_SERVER_RESOURCES
+ during the session setup }
+*/
+ {ERRDOS, ERRnomem, NT_STATUS_INSUFFICIENT_RESOURCES},
+ {ERRDOS, ERRbadpath, NT_STATUS_DFS_EXIT_PATH_FOUND},
+ {ERRDOS, 23, NT_STATUS_DEVICE_DATA_ERROR},
+ {ERRHRD, ERRgeneral, NT_STATUS_DEVICE_NOT_CONNECTED},
+ {ERRDOS, 21, NT_STATUS_DEVICE_POWER_FAILURE},
+ {ERRDOS, 487, NT_STATUS_FREE_VM_NOT_AT_BASE},
+ {ERRDOS, 487, NT_STATUS_MEMORY_NOT_ALLOCATED},
+ {ERRHRD, ERRgeneral, NT_STATUS_WORKING_SET_QUOTA},
+ {ERRDOS, 19, NT_STATUS_MEDIA_WRITE_PROTECTED},
+ {ERRDOS, 21, NT_STATUS_DEVICE_NOT_READY},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_GROUP_ATTRIBUTES},
+ {ERRHRD, ERRgeneral, NT_STATUS_BAD_IMPERSONATION_LEVEL},
+ {ERRHRD, ERRgeneral, NT_STATUS_CANT_OPEN_ANONYMOUS},
+ {ERRHRD, ERRgeneral, NT_STATUS_BAD_VALIDATION_CLASS},
+ {ERRHRD, ERRgeneral, NT_STATUS_BAD_TOKEN_TYPE},
+ {ERRDOS, 87, NT_STATUS_BAD_MASTER_BOOT_RECORD},
+ {ERRHRD, ERRgeneral, NT_STATUS_INSTRUCTION_MISALIGNMENT},
+ {ERRDOS, ERRpipebusy, NT_STATUS_INSTANCE_NOT_AVAILABLE},
+ {ERRDOS, ERRpipebusy, NT_STATUS_PIPE_NOT_AVAILABLE},
+ {ERRDOS, ERRbadpipe, NT_STATUS_INVALID_PIPE_STATE},
+ {ERRDOS, ERRpipebusy, NT_STATUS_PIPE_BUSY},
+ {ERRDOS, ERRbadfunc, NT_STATUS_ILLEGAL_FUNCTION},
+ {ERRDOS, ERRnotconnected, NT_STATUS_PIPE_DISCONNECTED},
+ {ERRDOS, ERRpipeclosing, NT_STATUS_PIPE_CLOSING},
+ {ERRHRD, ERRgeneral, NT_STATUS_PIPE_CONNECTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_PIPE_LISTENING},
+ {ERRDOS, ERRbadpipe, NT_STATUS_INVALID_READ_MODE},
+ {ERRDOS, 121, NT_STATUS_IO_TIMEOUT},
+ {ERRDOS, 38, NT_STATUS_FILE_FORCED_CLOSED},
+ {ERRHRD, ERRgeneral, NT_STATUS_PROFILING_NOT_STARTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_PROFILING_NOT_STOPPED},
+ {ERRHRD, ERRgeneral, NT_STATUS_COULD_NOT_INTERPRET},
+ {ERRDOS, ERRnoaccess, NT_STATUS_FILE_IS_A_DIRECTORY},
+ {ERRDOS, ERRunsup, NT_STATUS_NOT_SUPPORTED},
+ {ERRDOS, 51, NT_STATUS_REMOTE_NOT_LISTENING},
+ {ERRDOS, 52, NT_STATUS_DUPLICATE_NAME},
+ {ERRDOS, 53, NT_STATUS_BAD_NETWORK_PATH},
+ {ERRDOS, 54, NT_STATUS_NETWORK_BUSY},
+ {ERRDOS, 55, NT_STATUS_DEVICE_DOES_NOT_EXIST},
+ {ERRDOS, 56, NT_STATUS_TOO_MANY_COMMANDS},
+ {ERRDOS, 57, NT_STATUS_ADAPTER_HARDWARE_ERROR},
+ {ERRDOS, 58, NT_STATUS_INVALID_NETWORK_RESPONSE},
+ {ERRDOS, 59, NT_STATUS_UNEXPECTED_NETWORK_ERROR},
+ {ERRDOS, 60, NT_STATUS_BAD_REMOTE_ADAPTER},
+ {ERRDOS, 61, NT_STATUS_PRINT_QUEUE_FULL},
+ {ERRDOS, 62, NT_STATUS_NO_SPOOL_SPACE},
+ {ERRDOS, 63, NT_STATUS_PRINT_CANCELLED},
+ {ERRDOS, 64, NT_STATUS_NETWORK_NAME_DELETED},
+ {ERRDOS, 65, NT_STATUS_NETWORK_ACCESS_DENIED},
+ {ERRDOS, 66, NT_STATUS_BAD_DEVICE_TYPE},
+ {ERRDOS, ERRnosuchshare, NT_STATUS_BAD_NETWORK_NAME},
+ {ERRDOS, 68, NT_STATUS_TOO_MANY_NAMES},
+ {ERRDOS, 69, NT_STATUS_TOO_MANY_SESSIONS},
+ {ERRDOS, 70, NT_STATUS_SHARING_PAUSED},
+ {ERRDOS, 71, NT_STATUS_REQUEST_NOT_ACCEPTED},
+ {ERRDOS, 72, NT_STATUS_REDIRECTOR_PAUSED},
+ {ERRDOS, 88, NT_STATUS_NET_WRITE_FAULT},
+ {ERRHRD, ERRgeneral, NT_STATUS_PROFILING_AT_LIMIT},
+ {ERRDOS, ERRdiffdevice, NT_STATUS_NOT_SAME_DEVICE},
+ {ERRDOS, ERRnoaccess, NT_STATUS_FILE_RENAMED},
+ {ERRDOS, 240, NT_STATUS_VIRTUAL_CIRCUIT_CLOSED},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_SECURITY_ON_OBJECT},
+ {ERRHRD, ERRgeneral, NT_STATUS_CANT_WAIT},
+ {ERRDOS, ERRpipeclosing, NT_STATUS_PIPE_EMPTY},
+ {ERRHRD, ERRgeneral, NT_STATUS_CANT_ACCESS_DOMAIN_INFO},
+ {ERRHRD, ERRgeneral, NT_STATUS_CANT_TERMINATE_SELF},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_SERVER_STATE},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_DOMAIN_STATE},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_DOMAIN_ROLE},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_DOMAIN},
+ {ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_EXISTS},
+ {ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_LIMIT_EXCEEDED},
+ {ERRDOS, 300, NT_STATUS_OPLOCK_NOT_GRANTED},
+ {ERRDOS, 301, NT_STATUS_INVALID_OPLOCK_PROTOCOL},
+ {ERRHRD, ERRgeneral, NT_STATUS_INTERNAL_DB_CORRUPTION},
+ {ERRHRD, ERRgeneral, NT_STATUS_INTERNAL_ERROR},
+ {ERRHRD, ERRgeneral, NT_STATUS_GENERIC_NOT_MAPPED},
+ {ERRHRD, ERRgeneral, NT_STATUS_BAD_DESCRIPTOR_FORMAT},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_USER_BUFFER},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_IO_ERROR},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_MM_CREATE_ERR},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_MM_MAP_ERROR},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_MM_EXTEND_ERR},
+ {ERRHRD, ERRgeneral, NT_STATUS_NOT_LOGON_PROCESS},
+ {ERRHRD, ERRgeneral, NT_STATUS_LOGON_SESSION_EXISTS},
+ {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_1},
+ {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_2},
+ {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_3},
+ {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_4},
+ {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_5},
+ {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_6},
+ {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_7},
+ {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_8},
+ {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_9},
+ {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_10},
+ {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_11},
+ {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_12},
+ {ERRDOS, ERRbadpath, NT_STATUS_REDIRECTOR_NOT_STARTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_REDIRECTOR_STARTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_STACK_OVERFLOW},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_PACKAGE},
+ {ERRHRD, ERRgeneral, NT_STATUS_BAD_FUNCTION_TABLE},
+ {ERRDOS, 203, NT_STATUS(0xc0000100)},
+ {ERRDOS, 145, NT_STATUS_DIRECTORY_NOT_EMPTY},
+ {ERRHRD, ERRgeneral, NT_STATUS_FILE_CORRUPT_ERROR},
+ {ERRDOS, 267, NT_STATUS_NOT_A_DIRECTORY},
+ {ERRHRD, ERRgeneral, NT_STATUS_BAD_LOGON_SESSION_STATE},
+ {ERRHRD, ERRgeneral, NT_STATUS_LOGON_SESSION_COLLISION},
+ {ERRDOS, 206, NT_STATUS_NAME_TOO_LONG},
+ {ERRDOS, 2401, NT_STATUS_FILES_OPEN},
+ {ERRDOS, 2404, NT_STATUS_CONNECTION_IN_USE},
+ {ERRHRD, ERRgeneral, NT_STATUS_MESSAGE_NOT_FOUND},
+ {ERRDOS, ERRnoaccess, NT_STATUS_PROCESS_IS_TERMINATING},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_LOGON_TYPE},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_GUID_TRANSLATION},
+ {ERRHRD, ERRgeneral, NT_STATUS_CANNOT_IMPERSONATE},
+ {ERRHRD, ERRgeneral, NT_STATUS_IMAGE_ALREADY_LOADED},
+ {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_NOT_PRESENT},
+ {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_LID_NOT_EXIST},
+ {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_LID_ALREADY_OWNED},
+ {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_NOT_LID_OWNER},
+ {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_INVALID_COMMAND},
+ {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_INVALID_LID},
+ {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE},
+ {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_INVALID_SELECTOR},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_LDT},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_LDT_SIZE},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_LDT_OFFSET},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_LDT_DESCRIPTOR},
+ {ERRDOS, 193, NT_STATUS_INVALID_IMAGE_NE_FORMAT},
+ {ERRHRD, ERRgeneral, NT_STATUS_RXACT_INVALID_STATE},
+ {ERRHRD, ERRgeneral, NT_STATUS_RXACT_COMMIT_FAILURE},
+ {ERRHRD, ERRgeneral, NT_STATUS_MAPPED_FILE_SIZE_ZERO},
+ {ERRDOS, ERRnofids, NT_STATUS_TOO_MANY_OPENED_FILES},
+ {ERRHRD, ERRgeneral, NT_STATUS_CANCELLED},
+ {ERRDOS, ERRnoaccess, NT_STATUS_CANNOT_DELETE},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_COMPUTER_NAME},
+ {ERRDOS, ERRnoaccess, NT_STATUS_FILE_DELETED},
+ {ERRHRD, ERRgeneral, NT_STATUS_SPECIAL_ACCOUNT},
+ {ERRHRD, ERRgeneral, NT_STATUS_SPECIAL_GROUP},
+ {ERRHRD, ERRgeneral, NT_STATUS_SPECIAL_USER},
+ {ERRHRD, ERRgeneral, NT_STATUS_MEMBERS_PRIMARY_GROUP},
+ {ERRDOS, ERRbadfid, NT_STATUS_FILE_CLOSED},
+ {ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_THREADS},
+ {ERRHRD, ERRgeneral, NT_STATUS_THREAD_NOT_IN_PROCESS},
+ {ERRHRD, ERRgeneral, NT_STATUS_TOKEN_ALREADY_IN_USE},
+ {ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_QUOTA_EXCEEDED},
+ {ERRHRD, ERRgeneral, NT_STATUS_COMMITMENT_LIMIT},
+ {ERRDOS, 193, NT_STATUS_INVALID_IMAGE_LE_FORMAT},
+ {ERRDOS, 193, NT_STATUS_INVALID_IMAGE_NOT_MZ},
+ {ERRDOS, 193, NT_STATUS_INVALID_IMAGE_PROTECT},
+ {ERRDOS, 193, NT_STATUS_INVALID_IMAGE_WIN_16},
+ {ERRHRD, ERRgeneral, NT_STATUS_LOGON_SERVER_CONFLICT},
+ {ERRHRD, ERRgeneral, NT_STATUS_TIME_DIFFERENCE_AT_DC},
+ {ERRHRD, ERRgeneral, NT_STATUS_SYNCHRONIZATION_REQUIRED},
+ {ERRDOS, 126, NT_STATUS_DLL_NOT_FOUND},
+ {ERRHRD, ERRgeneral, NT_STATUS_OPEN_FAILED},
+ {ERRHRD, ERRgeneral, NT_STATUS_IO_PRIVILEGE_FAILED},
+ {ERRDOS, 182, NT_STATUS_ORDINAL_NOT_FOUND},
+ {ERRDOS, 127, NT_STATUS_ENTRYPOINT_NOT_FOUND},
+ {ERRHRD, ERRgeneral, NT_STATUS_CONTROL_C_EXIT},
+ {ERRDOS, 64, NT_STATUS_LOCAL_DISCONNECT},
+ {ERRDOS, 64, NT_STATUS_REMOTE_DISCONNECT},
+ {ERRDOS, 51, NT_STATUS_REMOTE_RESOURCES},
+ {ERRDOS, 59, NT_STATUS_LINK_FAILED},
+ {ERRDOS, 59, NT_STATUS_LINK_TIMEOUT},
+ {ERRDOS, 59, NT_STATUS_INVALID_CONNECTION},
+ {ERRDOS, 59, NT_STATUS_INVALID_ADDRESS},
+ {ERRHRD, ERRgeneral, NT_STATUS_DLL_INIT_FAILED},
+ {ERRHRD, ERRgeneral, NT_STATUS_MISSING_SYSTEMFILE},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNHANDLED_EXCEPTION},
+ {ERRHRD, ERRgeneral, NT_STATUS_APP_INIT_FAILURE},
+ {ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_CREATE_FAILED},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_PAGEFILE},
+ {ERRDOS, 124, NT_STATUS_INVALID_LEVEL},
+ {ERRDOS, 86, NT_STATUS_WRONG_PASSWORD_CORE},
+ {ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_FLOAT_CONTEXT},
+ {ERRDOS, 109, NT_STATUS_PIPE_BROKEN},
+ {ERRHRD, ERRgeneral, NT_STATUS_REGISTRY_CORRUPT},
+ {ERRHRD, ERRgeneral, NT_STATUS_REGISTRY_IO_FAILED},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_EVENT_PAIR},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNRECOGNIZED_VOLUME},
+ {ERRHRD, ERRgeneral, NT_STATUS_SERIAL_NO_DEVICE_INITED},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_ALIAS},
+ {ERRHRD, ERRgeneral, NT_STATUS_MEMBER_NOT_IN_ALIAS},
+ {ERRHRD, ERRgeneral, NT_STATUS_MEMBER_IN_ALIAS},
+ {ERRHRD, ERRgeneral, NT_STATUS_ALIAS_EXISTS},
+ {ERRHRD, ERRgeneral, NT_STATUS_LOGON_NOT_GRANTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_SECRETS},
+ {ERRHRD, ERRgeneral, NT_STATUS_SECRET_TOO_LONG},
+ {ERRHRD, ERRgeneral, NT_STATUS_INTERNAL_DB_ERROR},
+ {ERRHRD, ERRgeneral, NT_STATUS_FULLSCREEN_MODE},
+ {ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_CONTEXT_IDS},
+ {ERRDOS, ERRnoaccess, NT_STATUS_LOGON_TYPE_NOT_GRANTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_NOT_REGISTRY_FILE},
+ {ERRHRD, ERRgeneral, NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED},
+ {ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR},
+ {ERRHRD, ERRgeneral, NT_STATUS_FT_MISSING_MEMBER},
+ {ERRHRD, ERRgeneral, NT_STATUS_ILL_FORMED_SERVICE_ENTRY},
+ {ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_CHARACTER},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNMAPPABLE_CHARACTER},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNDEFINED_CHARACTER},
+ {ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_VOLUME},
+ {ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND},
+ {ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_WRONG_CYLINDER},
+ {ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_UNKNOWN_ERROR},
+ {ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_BAD_REGISTERS},
+ {ERRHRD, ERRgeneral, NT_STATUS_DISK_RECALIBRATE_FAILED},
+ {ERRHRD, ERRgeneral, NT_STATUS_DISK_OPERATION_FAILED},
+ {ERRHRD, ERRgeneral, NT_STATUS_DISK_RESET_FAILED},
+ {ERRHRD, ERRgeneral, NT_STATUS_SHARED_IRQ_BUSY},
+ {ERRHRD, ERRgeneral, NT_STATUS_FT_ORPHANING},
+ {ERRHRD, ERRgeneral, NT_STATUS(0xc000016e)},
+ {ERRHRD, ERRgeneral, NT_STATUS(0xc000016f)},
+ {ERRHRD, ERRgeneral, NT_STATUS(0xc0000170)},
+ {ERRHRD, ERRgeneral, NT_STATUS(0xc0000171)},
+ {ERRHRD, ERRgeneral, NT_STATUS_PARTITION_FAILURE},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_BLOCK_LENGTH},
+ {ERRHRD, ERRgeneral, NT_STATUS_DEVICE_NOT_PARTITIONED},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNABLE_TO_LOCK_MEDIA},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNABLE_TO_UNLOAD_MEDIA},
+ {ERRHRD, ERRgeneral, NT_STATUS_EOM_OVERFLOW},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_MEDIA},
+ {ERRHRD, ERRgeneral, NT_STATUS(0xc0000179)},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_MEMBER},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_MEMBER},
+ {ERRHRD, ERRgeneral, NT_STATUS_KEY_DELETED},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_LOG_SPACE},
+ {ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_SIDS},
+ {ERRHRD, ERRgeneral, NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED},
+ {ERRHRD, ERRgeneral, NT_STATUS_KEY_HAS_CHILDREN},
+ {ERRHRD, ERRgeneral, NT_STATUS_CHILD_MUST_BE_VOLATILE},
+ {ERRDOS, 87, NT_STATUS_DEVICE_CONFIGURATION_ERROR},
+ {ERRHRD, ERRgeneral, NT_STATUS_DRIVER_INTERNAL_ERROR},
+ {ERRDOS, 22, NT_STATUS_INVALID_DEVICE_STATE},
+ {ERRHRD, ERRgeneral, NT_STATUS_IO_DEVICE_ERROR},
+ {ERRHRD, ERRgeneral, NT_STATUS_DEVICE_PROTOCOL_ERROR},
+ {ERRHRD, ERRgeneral, NT_STATUS_BACKUP_CONTROLLER},
+ {ERRHRD, ERRgeneral, NT_STATUS_LOG_FILE_FULL},
+ {ERRDOS, 19, NT_STATUS_TOO_LATE},
+ {ERRDOS, ERRnoaccess, NT_STATUS_NO_TRUST_LSA_SECRET},
+/* { This NT error code was 'sqashed'
+ from NT_STATUS_NO_TRUST_SAM_ACCOUNT to NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE
+ during the session setup }
+*/
+ {ERRDOS, ERRnoaccess, NT_STATUS_NO_TRUST_SAM_ACCOUNT},
+ {ERRDOS, ERRnoaccess, NT_STATUS_TRUSTED_DOMAIN_FAILURE},
+ {ERRDOS, ERRnoaccess, NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE},
+ {ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_FILE_CORRUPT},
+ {ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_CANT_START},
+ {ERRDOS, ERRnoaccess, NT_STATUS_TRUST_FAILURE},
+ {ERRHRD, ERRgeneral, NT_STATUS_MUTANT_LIMIT_EXCEEDED},
+ {ERRDOS, ERRinvgroup, NT_STATUS_NETLOGON_NOT_STARTED},
+ {ERRSRV, 2239, NT_STATUS_ACCOUNT_EXPIRED},
+ {ERRHRD, ERRgeneral, NT_STATUS_POSSIBLE_DEADLOCK},
+ {ERRHRD, ERRgeneral, NT_STATUS_NETWORK_CREDENTIAL_CONFLICT},
+ {ERRHRD, ERRgeneral, NT_STATUS_REMOTE_SESSION_LIMIT},
+ {ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_FILE_CHANGED},
+ {ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT},
+ {ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT},
+ {ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT},
+/* { This NT error code was 'sqashed'
+ from NT_STATUS_DOMAIN_TRUST_INCONSISTENT to NT_STATUS_LOGON_FAILURE
+ during the session setup }
+*/
+ {ERRDOS, ERRnoaccess, NT_STATUS_DOMAIN_TRUST_INCONSISTENT},
+ {ERRHRD, ERRgeneral, NT_STATUS_FS_DRIVER_REQUIRED},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_USER_SESSION_KEY},
+ {ERRDOS, 59, NT_STATUS_USER_SESSION_DELETED},
+ {ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_LANG_NOT_FOUND},
+ {ERRDOS, ERRnomem, NT_STATUS_INSUFF_SERVER_RESOURCES},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_BUFFER_SIZE},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_ADDRESS_COMPONENT},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_ADDRESS_WILDCARD},
+ {ERRDOS, 68, NT_STATUS_TOO_MANY_ADDRESSES},
+ {ERRDOS, 52, NT_STATUS_ADDRESS_ALREADY_EXISTS},
+ {ERRDOS, 64, NT_STATUS_ADDRESS_CLOSED},
+ {ERRDOS, 64, NT_STATUS_CONNECTION_DISCONNECTED},
+ {ERRDOS, 64, NT_STATUS_CONNECTION_RESET},
+ {ERRDOS, 68, NT_STATUS_TOO_MANY_NODES},
+ {ERRDOS, 59, NT_STATUS_TRANSACTION_ABORTED},
+ {ERRDOS, 59, NT_STATUS_TRANSACTION_TIMED_OUT},
+ {ERRDOS, 59, NT_STATUS_TRANSACTION_NO_RELEASE},
+ {ERRDOS, 59, NT_STATUS_TRANSACTION_NO_MATCH},
+ {ERRDOS, 59, NT_STATUS_TRANSACTION_RESPONDED},
+ {ERRDOS, 59, NT_STATUS_TRANSACTION_INVALID_ID},
+ {ERRDOS, 59, NT_STATUS_TRANSACTION_INVALID_TYPE},
+ {ERRDOS, ERRunsup, NT_STATUS_NOT_SERVER_SESSION},
+ {ERRDOS, ERRunsup, NT_STATUS_NOT_CLIENT_SESSION},
+ {ERRHRD, ERRgeneral, NT_STATUS_CANNOT_LOAD_REGISTRY_FILE},
+ {ERRHRD, ERRgeneral, NT_STATUS_DEBUG_ATTACH_FAILED},
+ {ERRHRD, ERRgeneral, NT_STATUS_SYSTEM_PROCESS_TERMINATED},
+ {ERRHRD, ERRgeneral, NT_STATUS_DATA_NOT_ACCEPTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_BROWSER_SERVERS_FOUND},
+ {ERRHRD, ERRgeneral, NT_STATUS_VDM_HARD_ERROR},
+ {ERRHRD, ERRgeneral, NT_STATUS_DRIVER_CANCEL_TIMEOUT},
+ {ERRHRD, ERRgeneral, NT_STATUS_REPLY_MESSAGE_MISMATCH},
+ {ERRHRD, ERRgeneral, NT_STATUS_MAPPED_ALIGNMENT},
+ {ERRDOS, 193, NT_STATUS_IMAGE_CHECKSUM_MISMATCH},
+ {ERRHRD, ERRgeneral, NT_STATUS_LOST_WRITEBEHIND_DATA},
+ {ERRHRD, ERRgeneral, NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID},
+ {ERRSRV, 2242, NT_STATUS_PASSWORD_MUST_CHANGE},
+ {ERRHRD, ERRgeneral, NT_STATUS_NOT_FOUND},
+ {ERRHRD, ERRgeneral, NT_STATUS_NOT_TINY_STREAM},
+ {ERRHRD, ERRgeneral, NT_STATUS_RECOVERY_FAILURE},
+ {ERRHRD, ERRgeneral, NT_STATUS_STACK_OVERFLOW_READ},
+ {ERRHRD, ERRgeneral, NT_STATUS_FAIL_CHECK},
+ {ERRHRD, ERRgeneral, NT_STATUS_DUPLICATE_OBJECTID},
+ {ERRHRD, ERRgeneral, NT_STATUS_OBJECTID_EXISTS},
+ {ERRHRD, ERRgeneral, NT_STATUS_CONVERT_TO_LARGE},
+ {ERRHRD, ERRgeneral, NT_STATUS_RETRY},
+ {ERRHRD, ERRgeneral, NT_STATUS_FOUND_OUT_OF_SCOPE},
+ {ERRHRD, ERRgeneral, NT_STATUS_ALLOCATE_BUCKET},
+ {ERRHRD, ERRgeneral, NT_STATUS_PROPSET_NOT_FOUND},
+ {ERRHRD, ERRgeneral, NT_STATUS_MARSHALL_OVERFLOW},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_VARIANT},
+ {ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND},
+ {ERRDOS, ERRnoaccess, NT_STATUS_ACCOUNT_LOCKED_OUT},
+ {ERRDOS, ERRbadfid, NT_STATUS_HANDLE_NOT_CLOSABLE},
+ {ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_REFUSED},
+ {ERRHRD, ERRgeneral, NT_STATUS_GRACEFUL_DISCONNECT},
+ {ERRHRD, ERRgeneral, NT_STATUS_ADDRESS_ALREADY_ASSOCIATED},
+ {ERRHRD, ERRgeneral, NT_STATUS_ADDRESS_NOT_ASSOCIATED},
+ {ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_INVALID},
+ {ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_ACTIVE},
+ {ERRHRD, ERRgeneral, NT_STATUS_NETWORK_UNREACHABLE},
+ {ERRHRD, ERRgeneral, NT_STATUS_HOST_UNREACHABLE},
+ {ERRHRD, ERRgeneral, NT_STATUS_PROTOCOL_UNREACHABLE},
+ {ERRHRD, ERRgeneral, NT_STATUS_PORT_UNREACHABLE},
+ {ERRHRD, ERRgeneral, NT_STATUS_REQUEST_ABORTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_ABORTED},
+ {ERRHRD, ERRgeneral, NT_STATUS_BAD_COMPRESSION_BUFFER},
+ {ERRHRD, ERRgeneral, NT_STATUS_USER_MAPPED_FILE},
+ {ERRHRD, ERRgeneral, NT_STATUS_AUDIT_FAILED},
+ {ERRHRD, ERRgeneral, NT_STATUS_TIMER_RESOLUTION_NOT_SET},
+ {ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_COUNT_LIMIT},
+ {ERRHRD, ERRgeneral, NT_STATUS_LOGIN_TIME_RESTRICTION},
+ {ERRHRD, ERRgeneral, NT_STATUS_LOGIN_WKSTA_RESTRICTION},
+ {ERRDOS, 193, NT_STATUS_IMAGE_MP_UP_MISMATCH},
+ {ERRHRD, ERRgeneral, NT_STATUS(0xc000024a)},
+ {ERRHRD, ERRgeneral, NT_STATUS(0xc000024b)},
+ {ERRHRD, ERRgeneral, NT_STATUS(0xc000024c)},
+ {ERRHRD, ERRgeneral, NT_STATUS(0xc000024d)},
+ {ERRHRD, ERRgeneral, NT_STATUS(0xc000024e)},
+ {ERRHRD, ERRgeneral, NT_STATUS(0xc000024f)},
+ {ERRHRD, ERRgeneral, NT_STATUS_INSUFFICIENT_LOGON_INFO},
+ {ERRHRD, ERRgeneral, NT_STATUS_BAD_DLL_ENTRYPOINT},
+ {ERRHRD, ERRgeneral, NT_STATUS_BAD_SERVICE_ENTRYPOINT},
+ {ERRHRD, ERRgeneral, NT_STATUS_LPC_REPLY_LOST},
+ {ERRHRD, ERRgeneral, NT_STATUS_IP_ADDRESS_CONFLICT1},
+ {ERRHRD, ERRgeneral, NT_STATUS_IP_ADDRESS_CONFLICT2},
+ {ERRHRD, ERRgeneral, NT_STATUS_REGISTRY_QUOTA_LIMIT},
+ {ERRSRV, ERRbadtype, NT_STATUS_PATH_NOT_COVERED},
+ {ERRHRD, ERRgeneral, NT_STATUS_NO_CALLBACK_ACTIVE},
+ {ERRHRD, ERRgeneral, NT_STATUS_LICENSE_QUOTA_EXCEEDED},
+ {ERRHRD, ERRgeneral, NT_STATUS_PWD_TOO_SHORT},
+ {ERRHRD, ERRgeneral, NT_STATUS_PWD_TOO_RECENT},
+ {ERRHRD, ERRgeneral, NT_STATUS_PWD_HISTORY_CONFLICT},
+ {ERRHRD, ERRgeneral, NT_STATUS(0xc000025d)},
+ {ERRHRD, ERRgeneral, NT_STATUS_PLUGPLAY_NO_DEVICE},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNSUPPORTED_COMPRESSION},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_HW_PROFILE},
+ {ERRHRD, ERRgeneral, NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH},
+ {ERRDOS, 182, NT_STATUS_DRIVER_ORDINAL_NOT_FOUND},
+ {ERRDOS, 127, NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND},
+ {ERRDOS, 288, NT_STATUS_RESOURCE_NOT_OWNED},
+ {ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_LINKS},
+ {ERRHRD, ERRgeneral, NT_STATUS_QUOTA_LIST_INCONSISTENT},
+ {ERRHRD, ERRgeneral, NT_STATUS_FILE_IS_OFFLINE},
+ {ERRDOS, 21, NT_STATUS(0xc000026e)},
+ {ERRDOS, 161, NT_STATUS(0xc0000281)},
+ {ERRDOS, ERRnoaccess, NT_STATUS(0xc000028a)},
+ {ERRDOS, ERRnoaccess, NT_STATUS(0xc000028b)},
+ {ERRHRD, ERRgeneral, NT_STATUS(0xc000028c)},
+ {ERRDOS, ERRnoaccess, NT_STATUS(0xc000028d)},
+ {ERRDOS, ERRnoaccess, NT_STATUS(0xc000028e)},
+ {ERRDOS, ERRnoaccess, NT_STATUS(0xc000028f)},
+ {ERRDOS, ERRnoaccess, NT_STATUS(0xc0000290)},
+ {ERRDOS, ERRbadfunc, NT_STATUS(0xc000029c)},
+};
+
+
+/* errmap NTSTATUS->Win32 */
+static const struct {
+ NTSTATUS ntstatus;
+ WERROR werror;
+} ntstatus_to_werror_map[] = {
+ /*
+ * we add this manualy here, so that W_ERROR(0x5)
+ * gets mapped to NTSTATUS_ACCESS_DENIED
+ */
+ {NT_STATUS_ACCESS_DENIED, WERR_ACCESS_DENIED},
+ {NT_STATUS(0x103), W_ERROR(0x3e5)},
+ {NT_STATUS(0x105), W_ERROR(0xea)},
+ {NT_STATUS(0x106), W_ERROR(0x514)},
+ {NT_STATUS(0x107), W_ERROR(0x515)},
+ {NT_STATUS(0x10c), W_ERROR(0x3fe)},
+ {NT_STATUS(0x10d), W_ERROR(0x516)},
+ {NT_STATUS(0x121), W_ERROR(0x2009)},
+ {NT_STATUS(0xc0000001), W_ERROR(0x1f)},
+ {NT_STATUS(0xc0000002), W_ERROR(0x1)},
+ {NT_STATUS(0xc0000003), W_ERROR(0x57)},
+ {NT_STATUS(0xc0000004), W_ERROR(0x18)},
+ {NT_STATUS(0xc0000005), W_ERROR(0x3e6)},
+ {NT_STATUS(0xc0000006), W_ERROR(0x3e7)},
+ {NT_STATUS(0xc0000007), W_ERROR(0x5ae)},
+ {NT_STATUS(0xc0000008), W_ERROR(0x6)},
+ {NT_STATUS(0xc0000009), W_ERROR(0x3e9)},
+ {NT_STATUS(0xc000000a), W_ERROR(0xc1)},
+ {NT_STATUS(0xc000000b), W_ERROR(0x57)},
+ {NT_STATUS(0xc000000d), W_ERROR(0x57)},
+ {NT_STATUS(0xc000000e), W_ERROR(0x2)},
+ {NT_STATUS(0xc000000f), W_ERROR(0x2)},
+ {NT_STATUS(0xc0000010), W_ERROR(0x1)},
+ {NT_STATUS(0xc0000011), W_ERROR(0x26)},
+ {NT_STATUS(0xc0000012), W_ERROR(0x22)},
+ {NT_STATUS(0xc0000013), W_ERROR(0x15)},
+ {NT_STATUS(0xc0000014), W_ERROR(0x6f9)},
+ {NT_STATUS(0xc0000015), W_ERROR(0x1b)},
+ {NT_STATUS(0xc0000016), W_ERROR(0xea)},
+ {NT_STATUS(0xc0000017), W_ERROR(0x8)},
+ {NT_STATUS(0xc0000018), W_ERROR(0x1e7)},
+ {NT_STATUS(0xc0000019), W_ERROR(0x1e7)},
+ {NT_STATUS(0xc000001a), W_ERROR(0x57)},
+ {NT_STATUS(0xc000001b), W_ERROR(0x57)},
+ {NT_STATUS(0xc000001c), W_ERROR(0x1)},
+ {NT_STATUS(0xc000001d), W_ERROR(0xc000001d)},
+ {NT_STATUS(0xc000001e), W_ERROR(0x5)},
+ {NT_STATUS(0xc000001f), W_ERROR(0x5)},
+ {NT_STATUS(0xc0000020), W_ERROR(0xc1)},
+ {NT_STATUS(0xc0000021), W_ERROR(0x5)},
+ {NT_STATUS(0xc0000022), W_ERROR(0x5)},
+ {NT_STATUS(0xc0000023), W_ERROR(0x7a)},
+ {NT_STATUS(0xc0000024), W_ERROR(0x6)},
+ {NT_STATUS(0xc0000025), W_ERROR(0xc0000025)},
+ {NT_STATUS(0xc0000026), W_ERROR(0xc0000026)},
+ {NT_STATUS(0xc000002a), W_ERROR(0x9e)},
+ {NT_STATUS(0xc000002b), W_ERROR(0xc000002b)},
+ {NT_STATUS(0xc000002c), W_ERROR(0x1e7)},
+ {NT_STATUS(0xc000002d), W_ERROR(0x1e7)},
+ {NT_STATUS(0xc0000030), W_ERROR(0x57)},
+ {NT_STATUS(0xc0000032), W_ERROR(0x571)},
+ {NT_STATUS(0xc0000033), W_ERROR(0x7b)},
+ {NT_STATUS(0xc0000034), W_ERROR(0x2)},
+ {NT_STATUS(0xc0000035), W_ERROR(0xb7)},
+ {NT_STATUS(0xc0000037), W_ERROR(0x6)},
+ {NT_STATUS(0xc0000039), W_ERROR(0xa1)},
+ {NT_STATUS(0xc000003a), W_ERROR(0x3)},
+ {NT_STATUS(0xc000003b), W_ERROR(0xa1)},
+ {NT_STATUS(0xc000003c), W_ERROR(0x45d)},
+ {NT_STATUS(0xc000003d), W_ERROR(0x45d)},
+ {NT_STATUS(0xc000003e), W_ERROR(0x17)},
+ {NT_STATUS(0xc000003f), W_ERROR(0x17)},
+ {NT_STATUS(0xc0000040), W_ERROR(0x8)},
+ {NT_STATUS(0xc0000041), W_ERROR(0x5)},
+ {NT_STATUS(0xc0000042), W_ERROR(0x6)},
+ {NT_STATUS(0xc0000043), W_ERROR(0x20)},
+ {NT_STATUS(0xc0000044), W_ERROR(0x718)},
+ {NT_STATUS(0xc0000045), W_ERROR(0x57)},
+ {NT_STATUS(0xc0000046), W_ERROR(0x120)},
+ {NT_STATUS(0xc0000047), W_ERROR(0x12a)},
+ {NT_STATUS(0xc0000048), W_ERROR(0x57)},
+ {NT_STATUS(0xc0000049), W_ERROR(0x57)},
+ {NT_STATUS(0xc000004a), W_ERROR(0x9c)},
+ {NT_STATUS(0xc000004b), W_ERROR(0x5)},
+ {NT_STATUS(0xc000004c), W_ERROR(0x57)},
+ {NT_STATUS(0xc000004d), W_ERROR(0x57)},
+ {NT_STATUS(0xc000004e), W_ERROR(0x57)},
+ {NT_STATUS(0xc000004f), W_ERROR(0x11a)},
+ {NT_STATUS(0xc0000050), W_ERROR(0xff)},
+ {NT_STATUS(0xc0000051), W_ERROR(0x570)},
+ {NT_STATUS(0xc0000052), W_ERROR(0x570)},
+ {NT_STATUS(0xc0000053), W_ERROR(0x570)},
+ {NT_STATUS(0xc0000054), W_ERROR(0x21)},
+ {NT_STATUS(0xc0000055), W_ERROR(0x21)},
+ {NT_STATUS(0xc0000056), W_ERROR(0x5)},
+ {NT_STATUS(0xc0000057), W_ERROR(0x32)},
+ {NT_STATUS(0xc0000058), W_ERROR(0x519)},
+ {NT_STATUS(0xc0000059), W_ERROR(0x51a)},
+ {NT_STATUS(0xc000005a), W_ERROR(0x51b)},
+ {NT_STATUS(0xc000005b), W_ERROR(0x51c)},
+ {NT_STATUS(0xc000005c), W_ERROR(0x51d)},
+ {NT_STATUS(0xc000005d), W_ERROR(0x51e)},
+ {NT_STATUS(0xc000005e), W_ERROR(0x51f)},
+ {NT_STATUS(0xc000005f), W_ERROR(0x520)},
+ {NT_STATUS(0xc0000060), W_ERROR(0x521)},
+ {NT_STATUS(0xc0000061), W_ERROR(0x522)},
+ {NT_STATUS(0xc0000062), W_ERROR(0x523)},
+ {NT_STATUS(0xc0000063), W_ERROR(0x524)},
+ {NT_STATUS(0xc0000064), W_ERROR(0x525)},
+ {NT_STATUS(0xc0000065), W_ERROR(0x526)},
+ {NT_STATUS(0xc0000066), W_ERROR(0x527)},
+ {NT_STATUS(0xc0000067), W_ERROR(0x528)},
+ {NT_STATUS(0xc0000068), W_ERROR(0x529)},
+ {NT_STATUS(0xc0000069), W_ERROR(0x52a)},
+ {NT_STATUS(0xc000006a), W_ERROR(0x56)},
+ {NT_STATUS(0xc000006b), W_ERROR(0x52c)},
+ {NT_STATUS(0xc000006c), W_ERROR(0x52d)},
+ {NT_STATUS(0xc000006d), W_ERROR(0x52e)},
+ {NT_STATUS(0xc000006e), W_ERROR(0x52f)},
+ {NT_STATUS(0xc000006f), W_ERROR(0x530)},
+ {NT_STATUS(0xc0000070), W_ERROR(0x531)},
+ {NT_STATUS(0xc0000071), W_ERROR(0x532)},
+ {NT_STATUS(0xc0000072), W_ERROR(0x533)},
+ {NT_STATUS(0xc0000073), W_ERROR(0x534)},
+ {NT_STATUS(0xc0000074), W_ERROR(0x535)},
+ {NT_STATUS(0xc0000075), W_ERROR(0x536)},
+ {NT_STATUS(0xc0000076), W_ERROR(0x537)},
+ {NT_STATUS(0xc0000077), W_ERROR(0x538)},
+ {NT_STATUS(0xc0000078), W_ERROR(0x539)},
+ {NT_STATUS(0xc0000079), W_ERROR(0x53a)},
+ {NT_STATUS(0xc000007a), W_ERROR(0x7f)},
+ {NT_STATUS(0xc000007b), W_ERROR(0xc1)},
+ {NT_STATUS(0xc000007c), W_ERROR(0x3f0)},
+ {NT_STATUS(0xc000007d), W_ERROR(0x53c)},
+ {NT_STATUS(0xc000007e), W_ERROR(0x9e)},
+ {NT_STATUS(0xc000007f), W_ERROR(0x70)},
+ {NT_STATUS(0xc0000080), W_ERROR(0x53d)},
+ {NT_STATUS(0xc0000081), W_ERROR(0x53e)},
+ {NT_STATUS(0xc0000082), W_ERROR(0x44)},
+ {NT_STATUS(0xc0000083), W_ERROR(0x103)},
+ {NT_STATUS(0xc0000084), W_ERROR(0x53f)},
+ {NT_STATUS(0xc0000085), W_ERROR(0x103)},
+ {NT_STATUS(0xc0000086), W_ERROR(0x9a)},
+ {NT_STATUS(0xc0000087), W_ERROR(0xe)},
+ {NT_STATUS(0xc0000088), W_ERROR(0x1e7)},
+ {NT_STATUS(0xc0000089), W_ERROR(0x714)},
+ {NT_STATUS(0xc000008a), W_ERROR(0x715)},
+ {NT_STATUS(0xc000008b), W_ERROR(0x716)},
+ {NT_STATUS(0xc000008c), W_ERROR(0xc000008c)},
+ {NT_STATUS(0xc000008d), W_ERROR(0xc000008d)},
+ {NT_STATUS(0xc000008e), W_ERROR(0xc000008e)},
+ {NT_STATUS(0xc000008f), W_ERROR(0xc000008f)},
+ {NT_STATUS(0xc0000090), W_ERROR(0xc0000090)},
+ {NT_STATUS(0xc0000091), W_ERROR(0xc0000091)},
+ {NT_STATUS(0xc0000092), W_ERROR(0xc0000092)},
+ {NT_STATUS(0xc0000093), W_ERROR(0xc0000093)},
+ {NT_STATUS(0xc0000094), W_ERROR(0xc0000094)},
+ {NT_STATUS(0xc0000095), W_ERROR(0x216)},
+ {NT_STATUS(0xc0000096), W_ERROR(0xc0000096)},
+ {NT_STATUS(0xc0000097), W_ERROR(0x8)},
+ {NT_STATUS(0xc0000098), W_ERROR(0x3ee)},
+ {NT_STATUS(0xc0000099), W_ERROR(0x540)},
+ {NT_STATUS(0xc000009a), W_ERROR(0x5aa)},
+ {NT_STATUS(0xc000009b), W_ERROR(0x3)},
+ {NT_STATUS(0xc000009c), W_ERROR(0x17)},
+ {NT_STATUS(0xc000009d), W_ERROR(0x48f)},
+ {NT_STATUS(0xc000009e), W_ERROR(0x15)},
+ {NT_STATUS(0xc000009f), W_ERROR(0x1e7)},
+ {NT_STATUS(0xc00000a0), W_ERROR(0x1e7)},
+ {NT_STATUS(0xc00000a1), W_ERROR(0x5ad)},
+ {NT_STATUS(0xc00000a2), W_ERROR(0x13)},
+ {NT_STATUS(0xc00000a3), W_ERROR(0x15)},
+ {NT_STATUS(0xc00000a4), W_ERROR(0x541)},
+ {NT_STATUS(0xc00000a5), W_ERROR(0x542)},
+ {NT_STATUS(0xc00000a6), W_ERROR(0x543)},
+ {NT_STATUS(0xc00000a7), W_ERROR(0x544)},
+ {NT_STATUS(0xc00000a8), W_ERROR(0x545)},
+ {NT_STATUS(0xc00000a9), W_ERROR(0x57)},
+ {NT_STATUS(0xc00000ab), W_ERROR(0xe7)},
+ {NT_STATUS(0xc00000ac), W_ERROR(0xe7)},
+ {NT_STATUS(0xc00000ad), W_ERROR(0xe6)},
+ {NT_STATUS(0xc00000ae), W_ERROR(0xe7)},
+ {NT_STATUS(0xc00000af), W_ERROR(0x1)},
+ {NT_STATUS(0xc00000b0), W_ERROR(0xe9)},
+ {NT_STATUS(0xc00000b1), W_ERROR(0xe8)},
+ {NT_STATUS(0xc00000b2), W_ERROR(0x217)},
+ {NT_STATUS(0xc00000b3), W_ERROR(0x218)},
+ {NT_STATUS(0xc00000b4), W_ERROR(0xe6)},
+ {NT_STATUS(0xc00000b5), W_ERROR(0x79)},
+ {NT_STATUS(0xc00000b6), W_ERROR(0x26)},
+ {NT_STATUS(0xc00000ba), W_ERROR(0x5)},
+ {NT_STATUS(0xc00000bb), W_ERROR(0x32)},
+ {NT_STATUS(0xc00000bc), W_ERROR(0x33)},
+ {NT_STATUS(0xc00000bd), W_ERROR(0x34)},
+ {NT_STATUS(0xc00000be), W_ERROR(0x35)},
+ {NT_STATUS(0xc00000bf), W_ERROR(0x36)},
+ {NT_STATUS(0xc00000c0), W_ERROR(0x37)},
+ {NT_STATUS(0xc00000c1), W_ERROR(0x38)},
+ {NT_STATUS(0xc00000c2), W_ERROR(0x39)},
+ {NT_STATUS(0xc00000c3), W_ERROR(0x3a)},
+ {NT_STATUS(0xc00000c4), W_ERROR(0x3b)},
+ {NT_STATUS(0xc00000c5), W_ERROR(0x3c)},
+ {NT_STATUS(0xc00000c6), W_ERROR(0x3d)},
+ {NT_STATUS(0xc00000c7), W_ERROR(0x3e)},
+ {NT_STATUS(0xc00000c8), W_ERROR(0x3f)},
+ {NT_STATUS(0xc00000c9), W_ERROR(0x40)},
+ {NT_STATUS(0xc00000ca), W_ERROR(0x41)},
+ {NT_STATUS(0xc00000cb), W_ERROR(0x42)},
+ {NT_STATUS(0xc00000cc), W_ERROR(0x43)},
+ {NT_STATUS(0xc00000cd), W_ERROR(0x44)},
+ {NT_STATUS(0xc00000ce), W_ERROR(0x45)},
+ {NT_STATUS(0xc00000cf), W_ERROR(0x46)},
+ {NT_STATUS(0xc00000d0), W_ERROR(0x47)},
+ {NT_STATUS(0xc00000d1), W_ERROR(0x48)},
+ {NT_STATUS(0xc00000d2), W_ERROR(0x58)},
+ {NT_STATUS(0xc00000d4), W_ERROR(0x11)},
+ {NT_STATUS(0xc00000d5), W_ERROR(0x5)},
+ {NT_STATUS(0xc00000d6), W_ERROR(0xf0)},
+ {NT_STATUS(0xc00000d7), W_ERROR(0x546)},
+ {NT_STATUS(0xc00000d9), W_ERROR(0xe8)},
+ {NT_STATUS(0xc00000da), W_ERROR(0x547)},
+ {NT_STATUS(0xc00000dc), W_ERROR(0x548)},
+ {NT_STATUS(0xc00000dd), W_ERROR(0x549)},
+ {NT_STATUS(0xc00000de), W_ERROR(0x54a)},
+ {NT_STATUS(0xc00000df), W_ERROR(0x54b)},
+ {NT_STATUS(0xc00000e0), W_ERROR(0x54c)},
+ {NT_STATUS(0xc00000e1), W_ERROR(0x54d)},
+ {NT_STATUS(0xc00000e2), W_ERROR(0x12c)},
+ {NT_STATUS(0xc00000e3), W_ERROR(0x12d)},
+ {NT_STATUS(0xc00000e4), W_ERROR(0x54e)},
+ {NT_STATUS(0xc00000e5), W_ERROR(0x54f)},
+ {NT_STATUS(0xc00000e6), W_ERROR(0x550)},
+ {NT_STATUS(0xc00000e7), W_ERROR(0x551)},
+ {NT_STATUS(0xc00000e8), W_ERROR(0x6f8)},
+ {NT_STATUS(0xc00000ed), W_ERROR(0x552)},
+ {NT_STATUS(0xc00000ee), W_ERROR(0x553)},
+ {NT_STATUS(0xc00000ef), W_ERROR(0x57)},
+ {NT_STATUS(0xc00000f0), W_ERROR(0x57)},
+ {NT_STATUS(0xc00000f1), W_ERROR(0x57)},
+ {NT_STATUS(0xc00000f2), W_ERROR(0x57)},
+ {NT_STATUS(0xc00000f3), W_ERROR(0x57)},
+ {NT_STATUS(0xc00000f4), W_ERROR(0x57)},
+ {NT_STATUS(0xc00000f5), W_ERROR(0x57)},
+ {NT_STATUS(0xc00000f6), W_ERROR(0x57)},
+ {NT_STATUS(0xc00000f7), W_ERROR(0x57)},
+ {NT_STATUS(0xc00000f8), W_ERROR(0x57)},
+ {NT_STATUS(0xc00000f9), W_ERROR(0x57)},
+ {NT_STATUS(0xc00000fa), W_ERROR(0x57)},
+ {NT_STATUS(0xc00000fb), W_ERROR(0x3)},
+ {NT_STATUS(0xc00000fd), W_ERROR(0x3e9)},
+ {NT_STATUS(0xc00000fe), W_ERROR(0x554)},
+ {NT_STATUS(0xc0000100), W_ERROR(0xcb)},
+ {NT_STATUS(0xc0000101), W_ERROR(0x91)},
+ {NT_STATUS(0xc0000102), W_ERROR(0x570)},
+ {NT_STATUS(0xc0000103), W_ERROR(0x10b)},
+ {NT_STATUS(0xc0000104), W_ERROR(0x555)},
+ {NT_STATUS(0xc0000105), W_ERROR(0x556)},
+ {NT_STATUS(0xc0000106), W_ERROR(0xce)},
+ {NT_STATUS(0xc0000107), W_ERROR(0x961)},
+ {NT_STATUS(0xc0000108), W_ERROR(0x964)},
+ {NT_STATUS(0xc000010a), W_ERROR(0x5)},
+ {NT_STATUS(0xc000010b), W_ERROR(0x557)},
+ {NT_STATUS(0xc000010d), W_ERROR(0x558)},
+ {NT_STATUS(0xc000010e), W_ERROR(0x420)},
+ {NT_STATUS(0xc0000117), W_ERROR(0x5a4)},
+ {NT_STATUS(0xc000011b), W_ERROR(0xc1)},
+ {NT_STATUS(0xc000011c), W_ERROR(0x559)},
+ {NT_STATUS(0xc000011d), W_ERROR(0x55a)},
+ {NT_STATUS(0xc000011e), W_ERROR(0x3ee)},
+ {NT_STATUS(0xc000011f), W_ERROR(0x4)},
+ {NT_STATUS(0xc0000120), W_ERROR(0x3e3)},
+ {NT_STATUS(0xc0000121), W_ERROR(0x5)},
+ {NT_STATUS(0xc0000122), W_ERROR(0x4ba)},
+ {NT_STATUS(0xc0000123), W_ERROR(0x5)},
+ {NT_STATUS(0xc0000124), W_ERROR(0x55b)},
+ {NT_STATUS(0xc0000125), W_ERROR(0x55c)},
+ {NT_STATUS(0xc0000126), W_ERROR(0x55d)},
+ {NT_STATUS(0xc0000127), W_ERROR(0x55e)},
+ {NT_STATUS(0xc0000128), W_ERROR(0x6)},
+ {NT_STATUS(0xc000012b), W_ERROR(0x55f)},
+ {NT_STATUS(0xc000012d), W_ERROR(0x5af)},
+ {NT_STATUS(0xc000012e), W_ERROR(0xc1)},
+ {NT_STATUS(0xc000012f), W_ERROR(0xc1)},
+ {NT_STATUS(0xc0000130), W_ERROR(0xc1)},
+ {NT_STATUS(0xc0000131), W_ERROR(0xc1)},
+ {NT_STATUS(0xc0000133), W_ERROR(0x576)},
+ {NT_STATUS(0xc0000135), W_ERROR(0x7e)},
+ {NT_STATUS(0xc0000138), W_ERROR(0xb6)},
+ {NT_STATUS(0xc0000139), W_ERROR(0x7f)},
+ {NT_STATUS(0xc000013b), W_ERROR(0x40)},
+ {NT_STATUS(0xc000013c), W_ERROR(0x40)},
+ {NT_STATUS(0xc000013d), W_ERROR(0x33)},
+ {NT_STATUS(0xc000013e), W_ERROR(0x3b)},
+ {NT_STATUS(0xc000013f), W_ERROR(0x3b)},
+ {NT_STATUS(0xc0000140), W_ERROR(0x3b)},
+ {NT_STATUS(0xc0000141), W_ERROR(0x3b)},
+ {NT_STATUS(0xc0000142), W_ERROR(0x45a)},
+ {NT_STATUS(0xc0000148), W_ERROR(0x7c)},
+ {NT_STATUS(0xc0000149), W_ERROR(0x56)},
+ {NT_STATUS(0xc000014b), W_ERROR(0x6d)},
+ {NT_STATUS(0xc000014c), W_ERROR(0x3f1)},
+ {NT_STATUS(0xc000014d), W_ERROR(0x3f8)},
+ {NT_STATUS(0xc000014f), W_ERROR(0x3ed)},
+ {NT_STATUS(0xc0000150), W_ERROR(0x45e)},
+ {NT_STATUS(0xc0000151), W_ERROR(0x560)},
+ {NT_STATUS(0xc0000152), W_ERROR(0x561)},
+ {NT_STATUS(0xc0000153), W_ERROR(0x562)},
+ {NT_STATUS(0xc0000154), W_ERROR(0x563)},
+ {NT_STATUS(0xc0000155), W_ERROR(0x564)},
+ {NT_STATUS(0xc0000156), W_ERROR(0x565)},
+ {NT_STATUS(0xc0000157), W_ERROR(0x566)},
+ {NT_STATUS(0xc0000158), W_ERROR(0x567)},
+ {NT_STATUS(0xc0000159), W_ERROR(0x3ef)},
+ {NT_STATUS(0xc000015a), W_ERROR(0x568)},
+ {NT_STATUS(0xc000015b), W_ERROR(0x569)},
+ {NT_STATUS(0xc000015c), W_ERROR(0x3f9)},
+ {NT_STATUS(0xc000015d), W_ERROR(0x56a)},
+ {NT_STATUS(0xc000015f), W_ERROR(0x45d)},
+ {NT_STATUS(0xc0000162), W_ERROR(0x459)},
+ {NT_STATUS(0xc0000165), W_ERROR(0x462)},
+ {NT_STATUS(0xc0000166), W_ERROR(0x463)},
+ {NT_STATUS(0xc0000167), W_ERROR(0x464)},
+ {NT_STATUS(0xc0000168), W_ERROR(0x465)},
+ {NT_STATUS(0xc0000169), W_ERROR(0x466)},
+ {NT_STATUS(0xc000016a), W_ERROR(0x467)},
+ {NT_STATUS(0xc000016b), W_ERROR(0x468)},
+ {NT_STATUS(0xc000016c), W_ERROR(0x45f)},
+ {NT_STATUS(0xc000016d), W_ERROR(0x45d)},
+ {NT_STATUS(0xc0000172), W_ERROR(0x451)},
+ {NT_STATUS(0xc0000173), W_ERROR(0x452)},
+ {NT_STATUS(0xc0000174), W_ERROR(0x453)},
+ {NT_STATUS(0xc0000175), W_ERROR(0x454)},
+ {NT_STATUS(0xc0000176), W_ERROR(0x455)},
+ {NT_STATUS(0xc0000177), W_ERROR(0x469)},
+ {NT_STATUS(0xc0000178), W_ERROR(0x458)},
+ {NT_STATUS(0xc000017a), W_ERROR(0x56b)},
+ {NT_STATUS(0xc000017b), W_ERROR(0x56c)},
+ {NT_STATUS(0xc000017c), W_ERROR(0x3fa)},
+ {NT_STATUS(0xc000017d), W_ERROR(0x3fb)},
+ {NT_STATUS(0xc000017e), W_ERROR(0x56d)},
+ {NT_STATUS(0xc000017f), W_ERROR(0x56e)},
+ {NT_STATUS(0xc0000180), W_ERROR(0x3fc)},
+ {NT_STATUS(0xc0000181), W_ERROR(0x3fd)},
+ {NT_STATUS(0xc0000182), W_ERROR(0x57)},
+ {NT_STATUS(0xc0000183), W_ERROR(0x45d)},
+ {NT_STATUS(0xc0000184), W_ERROR(0x16)},
+ {NT_STATUS(0xc0000185), W_ERROR(0x45d)},
+ {NT_STATUS(0xc0000186), W_ERROR(0x45d)},
+ {NT_STATUS(0xc0000188), W_ERROR(0x5de)},
+ {NT_STATUS(0xc0000189), W_ERROR(0x13)},
+ {NT_STATUS(0xc000018a), W_ERROR(0x6fa)},
+ {NT_STATUS(0xc000018b), W_ERROR(0x6fb)},
+ {NT_STATUS(0xc000018c), W_ERROR(0x6fc)},
+ {NT_STATUS(0xc000018d), W_ERROR(0x6fd)},
+ {NT_STATUS(0xc000018e), W_ERROR(0x5dc)},
+ {NT_STATUS(0xc000018f), W_ERROR(0x5dd)},
+ {NT_STATUS(0xc0000190), W_ERROR(0x6fe)},
+ {NT_STATUS(0xc0000192), W_ERROR(0x700)},
+ {NT_STATUS(0xc0000193), W_ERROR(0x701)},
+ {NT_STATUS(0xc0000194), W_ERROR(0x46b)},
+ {NT_STATUS(0xc0000195), W_ERROR(0x4c3)},
+ {NT_STATUS(0xc0000196), W_ERROR(0x4c4)},
+ {NT_STATUS(0xc0000197), W_ERROR(0x5df)},
+ {NT_STATUS(0xc0000198), W_ERROR(0x70f)},
+ {NT_STATUS(0xc0000199), W_ERROR(0x710)},
+ {NT_STATUS(0xc000019a), W_ERROR(0x711)},
+ {NT_STATUS(0xc000019b), W_ERROR(0x712)},
+ {NT_STATUS(0xc0000202), W_ERROR(0x572)},
+ {NT_STATUS(0xc0000203), W_ERROR(0x3b)},
+ {NT_STATUS(0xc0000204), W_ERROR(0x717)},
+ {NT_STATUS(0xc0000205), W_ERROR(0x46a)},
+ {NT_STATUS(0xc0000206), W_ERROR(0x6f8)},
+ {NT_STATUS(0xc0000207), W_ERROR(0x4be)},
+ {NT_STATUS(0xc0000208), W_ERROR(0x4be)},
+ {NT_STATUS(0xc0000209), W_ERROR(0x44)},
+ {NT_STATUS(0xc000020a), W_ERROR(0x34)},
+ {NT_STATUS(0xc000020b), W_ERROR(0x40)},
+ {NT_STATUS(0xc000020c), W_ERROR(0x40)},
+ {NT_STATUS(0xc000020d), W_ERROR(0x40)},
+ {NT_STATUS(0xc000020e), W_ERROR(0x44)},
+ {NT_STATUS(0xc000020f), W_ERROR(0x3b)},
+ {NT_STATUS(0xc0000210), W_ERROR(0x3b)},
+ {NT_STATUS(0xc0000211), W_ERROR(0x3b)},
+ {NT_STATUS(0xc0000212), W_ERROR(0x3b)},
+ {NT_STATUS(0xc0000213), W_ERROR(0x3b)},
+ {NT_STATUS(0xc0000214), W_ERROR(0x3b)},
+ {NT_STATUS(0xc0000215), W_ERROR(0x3b)},
+ {NT_STATUS(0xc0000216), W_ERROR(0x32)},
+ {NT_STATUS(0xc0000217), W_ERROR(0x32)},
+ {NT_STATUS(0xc000021c), W_ERROR(0x17e6)},
+ {NT_STATUS(0xc0000220), W_ERROR(0x46c)},
+ {NT_STATUS(0xc0000221), W_ERROR(0xc1)},
+ {NT_STATUS(0xc0000224), W_ERROR(0x773)},
+ {NT_STATUS(0xc0000225), W_ERROR(0x490)},
+ {NT_STATUS(0xc000022a), W_ERROR(0xc000022a)},
+ {NT_STATUS(0xc000022b), W_ERROR(0xc000022b)},
+ {NT_STATUS(0xc000022d), W_ERROR(0x4d5)},
+ {NT_STATUS(0xc0000230), W_ERROR(0x492)},
+ {NT_STATUS(0xc0000233), W_ERROR(0x774)},
+ {NT_STATUS(0xc0000234), W_ERROR(0x775)},
+ {NT_STATUS(0xc0000235), W_ERROR(0x6)},
+ {NT_STATUS(0xc0000236), W_ERROR(0x4c9)},
+ {NT_STATUS(0xc0000237), W_ERROR(0x4ca)},
+ {NT_STATUS(0xc0000238), W_ERROR(0x4cb)},
+ {NT_STATUS(0xc0000239), W_ERROR(0x4cc)},
+ {NT_STATUS(0xc000023a), W_ERROR(0x4cd)},
+ {NT_STATUS(0xc000023b), W_ERROR(0x4ce)},
+ {NT_STATUS(0xc000023c), W_ERROR(0x4cf)},
+ {NT_STATUS(0xc000023d), W_ERROR(0x4d0)},
+ {NT_STATUS(0xc000023e), W_ERROR(0x4d1)},
+ {NT_STATUS(0xc000023f), W_ERROR(0x4d2)},
+ {NT_STATUS(0xc0000240), W_ERROR(0x4d3)},
+ {NT_STATUS(0xc0000241), W_ERROR(0x4d4)},
+ {NT_STATUS(0xc0000243), W_ERROR(0x4c8)},
+ {NT_STATUS(0xc0000246), W_ERROR(0x4d6)},
+ {NT_STATUS(0xc0000247), W_ERROR(0x4d7)},
+ {NT_STATUS(0xc0000248), W_ERROR(0x4d8)},
+ {NT_STATUS(0xc0000249), W_ERROR(0xc1)},
+ {NT_STATUS(0xc0000253), W_ERROR(0x54f)},
+ {NT_STATUS(0xc0000257), W_ERROR(0x4d0)},
+ {NT_STATUS(0xc0000259), W_ERROR(0x573)},
+ {NT_STATUS(0xc000025e), W_ERROR(0x422)},
+ {NT_STATUS(0xc0000262), W_ERROR(0xb6)},
+ {NT_STATUS(0xc0000263), W_ERROR(0x7f)},
+ {NT_STATUS(0xc0000264), W_ERROR(0x120)},
+ {NT_STATUS(0xc0000265), W_ERROR(0x476)},
+ {NT_STATUS(0xc0000267), W_ERROR(0x10fe)},
+ {NT_STATUS(0xc000026c), W_ERROR(0x7d1)},
+ {NT_STATUS(0xc000026d), W_ERROR(0x4b1)},
+ {NT_STATUS(0xc000026e), W_ERROR(0x15)},
+ {NT_STATUS(0xc0000272), W_ERROR(0x491)},
+ {NT_STATUS(0xc0000275), W_ERROR(0x1126)},
+ {NT_STATUS(0xc0000276), W_ERROR(0x1129)},
+ {NT_STATUS(0xc0000277), W_ERROR(0x112a)},
+ {NT_STATUS(0xc0000278), W_ERROR(0x1128)},
+ {NT_STATUS(0xc0000279), W_ERROR(0x780)},
+ {NT_STATUS(0xc0000280), W_ERROR(0x781)},
+ {NT_STATUS(0xc0000281), W_ERROR(0xa1)},
+ {NT_STATUS(0xc0000283), W_ERROR(0x488)},
+ {NT_STATUS(0xc0000284), W_ERROR(0x489)},
+ {NT_STATUS(0xc0000285), W_ERROR(0x48a)},
+ {NT_STATUS(0xc0000286), W_ERROR(0x48b)},
+ {NT_STATUS(0xc0000287), W_ERROR(0x48c)},
+ {NT_STATUS(0xc000028a), W_ERROR(0x5)},
+ {NT_STATUS(0xc000028b), W_ERROR(0x5)},
+ {NT_STATUS(0xc000028d), W_ERROR(0x5)},
+ {NT_STATUS(0xc000028e), W_ERROR(0x5)},
+ {NT_STATUS(0xc000028f), W_ERROR(0x5)},
+ {NT_STATUS(0xc0000290), W_ERROR(0x5)},
+ {NT_STATUS(0xc0000291), W_ERROR(0x1777)},
+ {NT_STATUS(0xc0000292), W_ERROR(0x1778)},
+ {NT_STATUS(0xc0000293), W_ERROR(0x1772)},
+ {NT_STATUS(0xc0000295), W_ERROR(0x1068)},
+ {NT_STATUS(0xc0000296), W_ERROR(0x1069)},
+ {NT_STATUS(0xc0000297), W_ERROR(0x106a)},
+ {NT_STATUS(0xc0000298), W_ERROR(0x106b)},
+ {NT_STATUS(0xc0000299), W_ERROR(0x201a)},
+ {NT_STATUS(0xc000029a), W_ERROR(0x201b)},
+ {NT_STATUS(0xc000029b), W_ERROR(0x201c)},
+ {NT_STATUS(0xc000029c), W_ERROR(0x1)},
+ {NT_STATUS(0xc000029d), W_ERROR(0x10ff)},
+ {NT_STATUS(0xc000029e), W_ERROR(0x1100)},
+ {NT_STATUS(0xc000029f), W_ERROR(0x494)},
+ {NT_STATUS(0xc00002a1), W_ERROR(0x200a)},
+ {NT_STATUS(0xc00002a2), W_ERROR(0x200b)},
+ {NT_STATUS(0xc00002a3), W_ERROR(0x200c)},
+ {NT_STATUS(0xc00002a4), W_ERROR(0x200d)},
+ {NT_STATUS(0xc00002a5), W_ERROR(0x200e)},
+ {NT_STATUS(0xc00002a6), W_ERROR(0x200f)},
+ {NT_STATUS(0xc00002a7), W_ERROR(0x2010)},
+ {NT_STATUS(0xc00002a8), W_ERROR(0x2011)},
+ {NT_STATUS(0xc00002a9), W_ERROR(0x2012)},
+ {NT_STATUS(0xc00002aa), W_ERROR(0x2013)},
+ {NT_STATUS(0xc00002ab), W_ERROR(0x2014)},
+ {NT_STATUS(0xc00002ac), W_ERROR(0x2015)},
+ {NT_STATUS(0xc00002ad), W_ERROR(0x2016)},
+ {NT_STATUS(0xc00002ae), W_ERROR(0x2017)},
+ {NT_STATUS(0xc00002af), W_ERROR(0x2018)},
+ {NT_STATUS(0xc00002b0), W_ERROR(0x2019)},
+ {NT_STATUS(0xc00002b1), W_ERROR(0x211e)},
+ {NT_STATUS(0xc00002b2), W_ERROR(0x1127)},
+ {NT_STATUS(0xc00002b6), W_ERROR(0x651)},
+ {NT_STATUS(0xc00002b7), W_ERROR(0x49a)},
+ {NT_STATUS(0xc00002b8), W_ERROR(0x49b)},
+ {NT_STATUS(0xc00002c1), W_ERROR(0x2024)},
+ {NT_STATUS(0xc00002c3), W_ERROR(0x575)},
+ {NT_STATUS(0xc00002c5), W_ERROR(0x3e6)},
+ {NT_STATUS(0xc00002c6), W_ERROR(0x1075)},
+ {NT_STATUS(0xc00002c7), W_ERROR(0x1076)},
+ {NT_STATUS(0xc00002ca), W_ERROR(0x10e8)},
+ {NT_STATUS(0xc00002cb), W_ERROR(0x2138)},
+ {NT_STATUS(0xc00002cc), W_ERROR(0x4e3)},
+ {NT_STATUS(0xc00002cd), W_ERROR(0x2139)},
+ {NT_STATUS(0xc00002cf), W_ERROR(0x49d)},
+ {NT_STATUS(0xc00002d0), W_ERROR(0x213a)},
+ {NT_STATUS(0xc00002d4), W_ERROR(0x2141)},
+ {NT_STATUS(0xc00002d5), W_ERROR(0x2142)},
+ {NT_STATUS(0xc00002d6), W_ERROR(0x2143)},
+ {NT_STATUS(0xc00002d7), W_ERROR(0x2144)},
+ {NT_STATUS(0xc00002d8), W_ERROR(0x2145)},
+ {NT_STATUS(0xc00002d9), W_ERROR(0x2146)},
+ {NT_STATUS(0xc00002da), W_ERROR(0x2147)},
+ {NT_STATUS(0xc00002db), W_ERROR(0x2148)},
+ {NT_STATUS(0xc00002dc), W_ERROR(0x2149)},
+ {NT_STATUS(0xc00002dd), W_ERROR(0x32)},
+ {NT_STATUS(0xc00002df), W_ERROR(0x2151)},
+ {NT_STATUS(0xc00002e0), W_ERROR(0x2152)},
+ {NT_STATUS(0xc00002e1), W_ERROR(0x2153)},
+ {NT_STATUS(0xc00002e2), W_ERROR(0x2154)},
+ {NT_STATUS(0xc00002e3), W_ERROR(0x215d)},
+ {NT_STATUS(0xc00002e4), W_ERROR(0x2163)},
+ {NT_STATUS(0xc00002e5), W_ERROR(0x2164)},
+ {NT_STATUS(0xc00002e6), W_ERROR(0x2165)},
+ {NT_STATUS(0xc00002e7), W_ERROR(0x216d)},
+ {NT_STATUS(0xc00002fe), W_ERROR(0x45b)},
+ {NT_STATUS(0xc00002ff), W_ERROR(0x4e7)},
+ {NT_STATUS(0xc0000300), W_ERROR(0x4e6)},
+ {NT_STATUS(0x80000001), W_ERROR(0x80000001)},
+ {NT_STATUS(0x80000002), W_ERROR(0x3e6)},
+ {NT_STATUS(0x80000003), W_ERROR(0x80000003)},
+ {NT_STATUS(0x80000004), W_ERROR(0x80000004)},
+ {NT_STATUS(0x80000005), W_ERROR(0xea)},
+ {NT_STATUS(0x80000006), W_ERROR(0x12)},
+ {NT_STATUS(0x8000000b), W_ERROR(0x56f)},
+ {NT_STATUS(0x8000000d), W_ERROR(0x12b)},
+ {NT_STATUS(0x8000000e), W_ERROR(0x1c)},
+ {NT_STATUS(0x8000000f), W_ERROR(0x15)},
+ {NT_STATUS(0x80000010), W_ERROR(0x15)},
+ {NT_STATUS(0x80000011), W_ERROR(0xaa)},
+ {NT_STATUS(0x80000012), W_ERROR(0x103)},
+ {NT_STATUS(0x80000013), W_ERROR(0xfe)},
+ {NT_STATUS(0x80000014), W_ERROR(0xff)},
+ {NT_STATUS(0x80000015), W_ERROR(0xff)},
+ {NT_STATUS(0x80000016), W_ERROR(0x456)},
+ {NT_STATUS(0x8000001a), W_ERROR(0x103)},
+ {NT_STATUS(0x8000001b), W_ERROR(0x44d)},
+ {NT_STATUS(0x8000001c), W_ERROR(0x456)},
+ {NT_STATUS(0x8000001d), W_ERROR(0x457)},
+ {NT_STATUS(0x8000001e), W_ERROR(0x44c)},
+ {NT_STATUS(0x8000001f), W_ERROR(0x44e)},
+ {NT_STATUS(0x80000021), W_ERROR(0x44f)},
+ {NT_STATUS(0x80000022), W_ERROR(0x450)},
+ {NT_STATUS(0x80000025), W_ERROR(0x962)},
+ {NT_STATUS(0x80000288), W_ERROR(0x48d)},
+ {NT_STATUS(0x80000289), W_ERROR(0x48e)},
+ {NT_STATUS_OK, WERR_OK}
+};
+
+bool ntstatus_check_dos_mapping = true;
+
+/*
+ check if a DOS encoded NTSTATUS code maps to the given NTSTATUS code
+*/
+bool ntstatus_dos_equal(NTSTATUS status1, NTSTATUS status2)
+{
+ /* when we negotiate nt status support, we don't want to consider
+ the mapping of dos codes, as we want to catch the cases where
+ a forced dos code is needed
+ */
+ if (ntstatus_check_dos_mapping) {
+ return NT_STATUS_V(status1) == NT_STATUS_V(status2);
+ }
+
+ /* otherwise check if the mapping comes out right. Note that it is important
+ that we do the mapping only from ntstatus -> dos and not from dos -> ntstatus,
+ as that is the mapping that servers must do */
+ if (!NT_STATUS_IS_DOS(status1) && NT_STATUS_IS_DOS(status2)) {
+ uint8_t eclass;
+ uint32_t ecode;
+ ntstatus_to_dos(status1, &eclass, &ecode);
+ return eclass == NT_STATUS_DOS_CLASS(status2) &&
+ ecode == NT_STATUS_DOS_CODE(status2);
+ }
+ if (NT_STATUS_IS_DOS(status1) && !NT_STATUS_IS_DOS(status2)) {
+ uint8_t eclass;
+ uint32_t ecode;
+ ntstatus_to_dos(status2, &eclass, &ecode);
+ return eclass == NT_STATUS_DOS_CLASS(status1) &&
+ ecode == NT_STATUS_DOS_CODE(status1);
+ }
+ return NT_STATUS_V(status1) == NT_STATUS_V(status2);
+}
+
+/*****************************************************************************
+convert a NT status code to a dos class/code
+ *****************************************************************************/
+void ntstatus_to_dos(NTSTATUS ntstatus, uint8_t *eclass, uint32_t *ecode)
+{
+ int i;
+ if (NT_STATUS_IS_OK(ntstatus)) {
+ *eclass = 0;
+ *ecode = 0;
+ return;
+ }
+ if (NT_STATUS_IS_DOS(ntstatus)) {
+ *eclass = NT_STATUS_DOS_CLASS(ntstatus);
+ *ecode = NT_STATUS_DOS_CODE(ntstatus);
+ return;
+ }
+ for (i=0; NT_STATUS_V(ntstatus_to_dos_map[i].ntstatus); i++) {
+ if (NT_STATUS_V(ntstatus) ==
+ NT_STATUS_V(ntstatus_to_dos_map[i].ntstatus)) {
+ *eclass = ntstatus_to_dos_map[i].dos_class;
+ *ecode = ntstatus_to_dos_map[i].dos_code;
+ return;
+ }
+ }
+ *eclass = ERRHRD;
+ *ecode = ERRgeneral;
+}
+
+
+/*****************************************************************************
+convert a WERROR to a NT status32 code
+ *****************************************************************************/
+NTSTATUS werror_to_ntstatus(WERROR error)
+{
+ int i;
+ if (W_ERROR_IS_OK(error)) return NT_STATUS_OK;
+ for (i=0; NT_STATUS_V(ntstatus_to_werror_map[i].ntstatus); i++) {
+ if (W_ERROR_V(error) ==
+ W_ERROR_V(ntstatus_to_werror_map[i].werror)) {
+ return ntstatus_to_werror_map[i].ntstatus;
+ }
+ }
+
+ /* just guess ... */
+ return NT_STATUS(W_ERROR_V(error) | 0xc0000000);
+}
+
+/*****************************************************************************
+convert a NTSTATUS to a WERROR
+ *****************************************************************************/
+WERROR ntstatus_to_werror(NTSTATUS error)
+{
+ int i;
+ if (NT_STATUS_IS_OK(error)) return WERR_OK;
+ for (i=0; NT_STATUS_V(ntstatus_to_werror_map[i].ntstatus); i++) {
+ if (NT_STATUS_V(error) ==
+ NT_STATUS_V(ntstatus_to_werror_map[i].ntstatus)) {
+ return ntstatus_to_werror_map[i].werror;
+ }
+ }
+
+ /* a lame guess */
+ return W_ERROR(NT_STATUS_V(error) & 0xffff);
+}
+
+/* Mapping between Unix, DOS and NT error numbers */
+
+struct unix_error_map {
+ int unix_error;
+ NTSTATUS nt_error;
+};
+
+const struct unix_error_map unix_nt_errmap[] = {
+ { EAGAIN, STATUS_MORE_ENTRIES },
+ { EINTR, STATUS_MORE_ENTRIES },
+ { ENOBUFS, STATUS_MORE_ENTRIES },
+#ifdef EWOULDBLOCK
+ { EWOULDBLOCK, STATUS_MORE_ENTRIES },
+#endif
+ { EINPROGRESS, NT_STATUS_MORE_PROCESSING_REQUIRED },
+ { EPERM, NT_STATUS_ACCESS_DENIED },
+ { EACCES, NT_STATUS_ACCESS_DENIED },
+ { ENOENT, NT_STATUS_OBJECT_NAME_NOT_FOUND },
+ { ENOTDIR, NT_STATUS_NOT_A_DIRECTORY },
+ { EIO, NT_STATUS_IO_DEVICE_ERROR },
+ { EBADF, NT_STATUS_INVALID_HANDLE },
+ { EINVAL, NT_STATUS_INVALID_PARAMETER },
+ { EEXIST, NT_STATUS_OBJECT_NAME_COLLISION},
+ { ENFILE, NT_STATUS_TOO_MANY_OPENED_FILES },
+ { EMFILE, NT_STATUS_TOO_MANY_OPENED_FILES },
+ { ENOSPC, NT_STATUS_DISK_FULL },
+ { EISDIR, NT_STATUS_FILE_IS_A_DIRECTORY },
+ { ENOTSOCK, NT_STATUS_INVALID_HANDLE },
+ { EFAULT, NT_STATUS_INVALID_PARAMETER },
+ { EMSGSIZE, NT_STATUS_INVALID_BUFFER_SIZE },
+ { ENOMEM, NT_STATUS_NO_MEMORY },
+ { EPIPE, NT_STATUS_CONNECTION_DISCONNECTED },
+ { ECONNREFUSED, NT_STATUS_CONNECTION_REFUSED },
+#ifdef ECONNRESET
+ { ECONNRESET, NT_STATUS_CONNECTION_RESET },
+#endif
+ { EBUSY, NT_STATUS_SHARING_VIOLATION },
+#ifdef ENOTSUP
+ { ENOTSUP, NT_STATUS_NOT_SUPPORTED},
+#endif
+#ifdef EOPNOTSUPP
+ { EOPNOTSUPP, NT_STATUS_NOT_SUPPORTED},
+#endif
+#ifdef EHOSTUNREACH
+ { EHOSTUNREACH, NT_STATUS_HOST_UNREACHABLE },
+#endif
+#ifdef ENETUNREACH
+ { ENETUNREACH, NT_STATUS_NETWORK_UNREACHABLE },
+#endif
+#ifdef ETIMEDOUT
+ { ETIMEDOUT, NT_STATUS_IO_TIMEOUT },
+#endif
+#ifdef EADDRINUSE
+ { EADDRINUSE, NT_STATUS_ADDRESS_ALREADY_ASSOCIATED },
+#endif
+#ifdef ENOATTR
+ { ENOATTR, NT_STATUS_NOT_FOUND },
+#endif
+#ifdef ENODATA
+ { ENODATA, NT_STATUS_NOT_FOUND },
+#endif
+#ifdef EDQUOT
+ { EDQUOT, NT_STATUS_QUOTA_EXCEEDED },
+#endif
+#ifdef ENOTEMPTY
+ { ENOTEMPTY, NT_STATUS_DIRECTORY_NOT_EMPTY },
+#endif
+#ifdef EXDEV
+ { EXDEV, NT_STATUS_NOT_SAME_DEVICE },
+#endif
+#ifdef EROFS
+ { EROFS, NT_STATUS_MEDIA_WRITE_PROTECTED },
+#endif
+#ifdef ENAMETOOLONG
+ { ENAMETOOLONG, NT_STATUS_NAME_TOO_LONG },
+#endif
+#ifdef EFBIG
+ { EFBIG, NT_STATUS_DISK_FULL },
+#endif
+#ifdef EADDRNOTAVAIL
+ { EADDRNOTAVAIL,NT_STATUS_ADDRESS_NOT_ASSOCIATED },
+#endif
+#ifdef ESOCKTNOSUPPORT
+ { ESOCKTNOSUPPORT,NT_STATUS_INVALID_PARAMETER_MIX },
+#endif
+#ifdef EAFNOSUPPORT
+ { EAFNOSUPPORT, NT_STATUS_INVALID_PARAMETER_MIX },
+#endif
+#ifdef ENOPROTOOPT
+ { ENOPROTOOPT, NT_STATUS_INVALID_PARAMETER_MIX },
+#endif
+#ifdef ENODEV
+ { ENODEV, NT_STATUS_NO_SUCH_DEVICE },
+#endif
+#ifdef ENOSYS
+ { ENOSYS, NT_STATUS_INVALID_SYSTEM_SERVICE },
+#endif
+#ifdef ECANCELED
+ { ECANCELED, NT_STATUS_CANCELLED },
+#endif
+
+ { 0, NT_STATUS_UNSUCCESSFUL }
+};
+
+
+/*********************************************************************
+ Map an NT error code from a Unix error code.
+*********************************************************************/
+NTSTATUS map_nt_error_from_unix(int unix_error)
+{
+ int i;
+
+ /* Look through list */
+ for (i=0;i<ARRAY_SIZE(unix_nt_errmap);i++) {
+ if (unix_nt_errmap[i].unix_error == unix_error) {
+ return unix_nt_errmap[i].nt_error;
+ }
+ }
+
+ /* Default return */
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+NTSTATUS ndr_map_error2ntstatus(enum ndr_err_code ndr_err)
+{
+ switch (ndr_err) {
+ case NDR_ERR_SUCCESS:
+ return NT_STATUS_OK;
+ case NDR_ERR_BUFSIZE:
+ return NT_STATUS_BUFFER_TOO_SMALL;
+ case NDR_ERR_TOKEN:
+ return NT_STATUS_INTERNAL_ERROR;
+ case NDR_ERR_ALLOC:
+ return NT_STATUS_NO_MEMORY;
+ case NDR_ERR_ARRAY_SIZE:
+ return NT_STATUS_ARRAY_BOUNDS_EXCEEDED;
+ case NDR_ERR_INVALID_POINTER:
+ return NT_STATUS_INVALID_PARAMETER_MIX;
+ case NDR_ERR_UNREAD_BYTES:
+ return NT_STATUS_PORT_MESSAGE_TOO_LONG;
+ default:
+ break;
+ }
+
+ /* we should map all error codes to different status codes */
+ return NT_STATUS_INVALID_PARAMETER;
+}
diff --git a/source4/libcli/util/nterr.c b/source4/libcli/util/nterr.c
new file mode 100644
index 0000000000..4e7cdf5c3a
--- /dev/null
+++ b/source4/libcli/util/nterr.c
@@ -0,0 +1,898 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Luke Kenneth Casson Leighton 1997-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 <http://www.gnu.org/licenses/>.
+ */
+
+/* NT error codes. please read nterr.h */
+
+#include "includes.h"
+#include "../libcli/ldap/ldap_errors.h"
+#undef strcasecmp
+
+typedef struct
+{
+ const char *nt_errstr;
+ NTSTATUS nt_errcode;
+} nt_err_code_struct;
+
+#define DOS_CODE(class, code) { #class ":" #code, NT_STATUS_DOS(class, code) }
+#define LDAP_CODE(code) { #code, NT_STATUS_LDAP(code) }
+
+static const nt_err_code_struct nt_errs[] =
+{
+ { "NT_STATUS_OK", NT_STATUS_OK },
+ { "STATUS_NO_MORE_FILES", STATUS_NO_MORE_FILES },
+ { "STATUS_NO_MORE_EAS", STATUS_NO_MORE_EAS },
+ { "STATUS_INVALID_EA_NAME", STATUS_INVALID_EA_NAME },
+ { "STATUS_EA_LIST_INCONSISTENT", STATUS_EA_LIST_INCONSISTENT },
+ { "STATUS_INVALID_EA_FLAG", STATUS_INVALID_EA_FLAG },
+ { "NT_STATUS_UNSUCCESSFUL", NT_STATUS_UNSUCCESSFUL },
+ { "NT_STATUS_NOT_IMPLEMENTED", NT_STATUS_NOT_IMPLEMENTED },
+ { "NT_STATUS_INVALID_INFO_CLASS", NT_STATUS_INVALID_INFO_CLASS },
+ { "NT_STATUS_INFO_LENGTH_MISMATCH", NT_STATUS_INFO_LENGTH_MISMATCH },
+ { "NT_STATUS_ACCESS_VIOLATION", NT_STATUS_ACCESS_VIOLATION },
+ { "STATUS_BUFFER_OVERFLOW", STATUS_BUFFER_OVERFLOW },
+ { "NT_STATUS_IN_PAGE_ERROR", NT_STATUS_IN_PAGE_ERROR },
+ { "NT_STATUS_PAGEFILE_QUOTA", NT_STATUS_PAGEFILE_QUOTA },
+ { "NT_STATUS_INVALID_HANDLE", NT_STATUS_INVALID_HANDLE },
+ { "NT_STATUS_BAD_INITIAL_STACK", NT_STATUS_BAD_INITIAL_STACK },
+ { "NT_STATUS_BAD_INITIAL_PC", NT_STATUS_BAD_INITIAL_PC },
+ { "NT_STATUS_INVALID_CID", NT_STATUS_INVALID_CID },
+ { "NT_STATUS_TIMER_NOT_CANCELED", NT_STATUS_TIMER_NOT_CANCELED },
+ { "NT_STATUS_INVALID_PARAMETER", NT_STATUS_INVALID_PARAMETER },
+ { "NT_STATUS_NO_SUCH_DEVICE", NT_STATUS_NO_SUCH_DEVICE },
+ { "NT_STATUS_NO_SUCH_FILE", NT_STATUS_NO_SUCH_FILE },
+ { "NT_STATUS_INVALID_DEVICE_REQUEST", NT_STATUS_INVALID_DEVICE_REQUEST },
+ { "NT_STATUS_END_OF_FILE", NT_STATUS_END_OF_FILE },
+ { "NT_STATUS_WRONG_VOLUME", NT_STATUS_WRONG_VOLUME },
+ { "NT_STATUS_NO_MEDIA_IN_DEVICE", NT_STATUS_NO_MEDIA_IN_DEVICE },
+ { "NT_STATUS_UNRECOGNIZED_MEDIA", NT_STATUS_UNRECOGNIZED_MEDIA },
+ { "NT_STATUS_NONEXISTENT_SECTOR", NT_STATUS_NONEXISTENT_SECTOR },
+ { "NT_STATUS_MORE_PROCESSING_REQUIRED", NT_STATUS_MORE_PROCESSING_REQUIRED },
+ { "NT_STATUS_NO_MEMORY", NT_STATUS_NO_MEMORY },
+ { "NT_STATUS_CONFLICTING_ADDRESSES", NT_STATUS_CONFLICTING_ADDRESSES },
+ { "NT_STATUS_NOT_MAPPED_VIEW", NT_STATUS_NOT_MAPPED_VIEW },
+ { "NT_STATUS_UNABLE_TO_FREE_VM", NT_STATUS_UNABLE_TO_FREE_VM },
+ { "NT_STATUS_UNABLE_TO_DELETE_SECTION", NT_STATUS_UNABLE_TO_DELETE_SECTION },
+ { "NT_STATUS_INVALID_SYSTEM_SERVICE", NT_STATUS_INVALID_SYSTEM_SERVICE },
+ { "NT_STATUS_ILLEGAL_INSTRUCTION", NT_STATUS_ILLEGAL_INSTRUCTION },
+ { "NT_STATUS_INVALID_LOCK_SEQUENCE", NT_STATUS_INVALID_LOCK_SEQUENCE },
+ { "NT_STATUS_INVALID_VIEW_SIZE", NT_STATUS_INVALID_VIEW_SIZE },
+ { "NT_STATUS_INVALID_FILE_FOR_SECTION", NT_STATUS_INVALID_FILE_FOR_SECTION },
+ { "NT_STATUS_ALREADY_COMMITTED", NT_STATUS_ALREADY_COMMITTED },
+ { "NT_STATUS_ACCESS_DENIED", NT_STATUS_ACCESS_DENIED },
+ { "NT_STATUS_BUFFER_TOO_SMALL", NT_STATUS_BUFFER_TOO_SMALL },
+ { "NT_STATUS_OBJECT_TYPE_MISMATCH", NT_STATUS_OBJECT_TYPE_MISMATCH },
+ { "NT_STATUS_NONCONTINUABLE_EXCEPTION", NT_STATUS_NONCONTINUABLE_EXCEPTION },
+ { "NT_STATUS_INVALID_DISPOSITION", NT_STATUS_INVALID_DISPOSITION },
+ { "NT_STATUS_UNWIND", NT_STATUS_UNWIND },
+ { "NT_STATUS_BAD_STACK", NT_STATUS_BAD_STACK },
+ { "NT_STATUS_INVALID_UNWIND_TARGET", NT_STATUS_INVALID_UNWIND_TARGET },
+ { "NT_STATUS_NOT_LOCKED", NT_STATUS_NOT_LOCKED },
+ { "NT_STATUS_PARITY_ERROR", NT_STATUS_PARITY_ERROR },
+ { "NT_STATUS_UNABLE_TO_DECOMMIT_VM", NT_STATUS_UNABLE_TO_DECOMMIT_VM },
+ { "NT_STATUS_NOT_COMMITTED", NT_STATUS_NOT_COMMITTED },
+ { "NT_STATUS_INVALID_PORT_ATTRIBUTES", NT_STATUS_INVALID_PORT_ATTRIBUTES },
+ { "NT_STATUS_PORT_MESSAGE_TOO_LONG", NT_STATUS_PORT_MESSAGE_TOO_LONG },
+ { "NT_STATUS_INVALID_PARAMETER_MIX", NT_STATUS_INVALID_PARAMETER_MIX },
+ { "NT_STATUS_INVALID_QUOTA_LOWER", NT_STATUS_INVALID_QUOTA_LOWER },
+ { "NT_STATUS_DISK_CORRUPT_ERROR", NT_STATUS_DISK_CORRUPT_ERROR },
+ { "NT_STATUS_OBJECT_NAME_INVALID", NT_STATUS_OBJECT_NAME_INVALID },
+ { "NT_STATUS_OBJECT_NAME_NOT_FOUND", NT_STATUS_OBJECT_NAME_NOT_FOUND },
+ { "NT_STATUS_OBJECT_NAME_COLLISION", NT_STATUS_OBJECT_NAME_COLLISION },
+ { "NT_STATUS_HANDLE_NOT_WAITABLE", NT_STATUS_HANDLE_NOT_WAITABLE },
+ { "NT_STATUS_PORT_DISCONNECTED", NT_STATUS_PORT_DISCONNECTED },
+ { "NT_STATUS_DEVICE_ALREADY_ATTACHED", NT_STATUS_DEVICE_ALREADY_ATTACHED },
+ { "NT_STATUS_OBJECT_PATH_INVALID", NT_STATUS_OBJECT_PATH_INVALID },
+ { "NT_STATUS_OBJECT_PATH_NOT_FOUND", NT_STATUS_OBJECT_PATH_NOT_FOUND },
+ { "NT_STATUS_OBJECT_PATH_SYNTAX_BAD", NT_STATUS_OBJECT_PATH_SYNTAX_BAD },
+ { "NT_STATUS_DATA_OVERRUN", NT_STATUS_DATA_OVERRUN },
+ { "NT_STATUS_DATA_LATE_ERROR", NT_STATUS_DATA_LATE_ERROR },
+ { "NT_STATUS_DATA_ERROR", NT_STATUS_DATA_ERROR },
+ { "NT_STATUS_CRC_ERROR", NT_STATUS_CRC_ERROR },
+ { "NT_STATUS_SECTION_TOO_BIG", NT_STATUS_SECTION_TOO_BIG },
+ { "NT_STATUS_PORT_CONNECTION_REFUSED", NT_STATUS_PORT_CONNECTION_REFUSED },
+ { "NT_STATUS_INVALID_PORT_HANDLE", NT_STATUS_INVALID_PORT_HANDLE },
+ { "NT_STATUS_SHARING_VIOLATION", NT_STATUS_SHARING_VIOLATION },
+ { "NT_STATUS_QUOTA_EXCEEDED", NT_STATUS_QUOTA_EXCEEDED },
+ { "NT_STATUS_INVALID_PAGE_PROTECTION", NT_STATUS_INVALID_PAGE_PROTECTION },
+ { "NT_STATUS_MUTANT_NOT_OWNED", NT_STATUS_MUTANT_NOT_OWNED },
+ { "NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED", NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED },
+ { "NT_STATUS_PORT_ALREADY_SET", NT_STATUS_PORT_ALREADY_SET },
+ { "NT_STATUS_SECTION_NOT_IMAGE", NT_STATUS_SECTION_NOT_IMAGE },
+ { "NT_STATUS_SUSPEND_COUNT_EXCEEDED", NT_STATUS_SUSPEND_COUNT_EXCEEDED },
+ { "NT_STATUS_THREAD_IS_TERMINATING", NT_STATUS_THREAD_IS_TERMINATING },
+ { "NT_STATUS_BAD_WORKING_SET_LIMIT", NT_STATUS_BAD_WORKING_SET_LIMIT },
+ { "NT_STATUS_INCOMPATIBLE_FILE_MAP", NT_STATUS_INCOMPATIBLE_FILE_MAP },
+ { "NT_STATUS_SECTION_PROTECTION", NT_STATUS_SECTION_PROTECTION },
+ { "NT_STATUS_EAS_NOT_SUPPORTED", NT_STATUS_EAS_NOT_SUPPORTED },
+ { "NT_STATUS_EA_TOO_LARGE", NT_STATUS_EA_TOO_LARGE },
+ { "NT_STATUS_NONEXISTENT_EA_ENTRY", NT_STATUS_NONEXISTENT_EA_ENTRY },
+ { "NT_STATUS_NO_EAS_ON_FILE", NT_STATUS_NO_EAS_ON_FILE },
+ { "NT_STATUS_EA_CORRUPT_ERROR", NT_STATUS_EA_CORRUPT_ERROR },
+ { "NT_STATUS_FILE_LOCK_CONFLICT", NT_STATUS_FILE_LOCK_CONFLICT },
+ { "NT_STATUS_LOCK_NOT_GRANTED", NT_STATUS_LOCK_NOT_GRANTED },
+ { "NT_STATUS_DELETE_PENDING", NT_STATUS_DELETE_PENDING },
+ { "NT_STATUS_CTL_FILE_NOT_SUPPORTED", NT_STATUS_CTL_FILE_NOT_SUPPORTED },
+ { "NT_STATUS_UNKNOWN_REVISION", NT_STATUS_UNKNOWN_REVISION },
+ { "NT_STATUS_REVISION_MISMATCH", NT_STATUS_REVISION_MISMATCH },
+ { "NT_STATUS_INVALID_OWNER", NT_STATUS_INVALID_OWNER },
+ { "NT_STATUS_INVALID_PRIMARY_GROUP", NT_STATUS_INVALID_PRIMARY_GROUP },
+ { "NT_STATUS_NO_IMPERSONATION_TOKEN", NT_STATUS_NO_IMPERSONATION_TOKEN },
+ { "NT_STATUS_CANT_DISABLE_MANDATORY", NT_STATUS_CANT_DISABLE_MANDATORY },
+ { "NT_STATUS_NO_LOGON_SERVERS", NT_STATUS_NO_LOGON_SERVERS },
+ { "NT_STATUS_NO_SUCH_LOGON_SESSION", NT_STATUS_NO_SUCH_LOGON_SESSION },
+ { "NT_STATUS_NO_SUCH_PRIVILEGE", NT_STATUS_NO_SUCH_PRIVILEGE },
+ { "NT_STATUS_PRIVILEGE_NOT_HELD", NT_STATUS_PRIVILEGE_NOT_HELD },
+ { "NT_STATUS_INVALID_ACCOUNT_NAME", NT_STATUS_INVALID_ACCOUNT_NAME },
+ { "NT_STATUS_USER_EXISTS", NT_STATUS_USER_EXISTS },
+ { "NT_STATUS_NO_SUCH_USER", NT_STATUS_NO_SUCH_USER },
+ { "NT_STATUS_GROUP_EXISTS", NT_STATUS_GROUP_EXISTS },
+ { "NT_STATUS_NO_SUCH_GROUP", NT_STATUS_NO_SUCH_GROUP },
+ { "NT_STATUS_MEMBER_IN_GROUP", NT_STATUS_MEMBER_IN_GROUP },
+ { "NT_STATUS_MEMBER_NOT_IN_GROUP", NT_STATUS_MEMBER_NOT_IN_GROUP },
+ { "NT_STATUS_LAST_ADMIN", NT_STATUS_LAST_ADMIN },
+ { "NT_STATUS_WRONG_PASSWORD", NT_STATUS_WRONG_PASSWORD },
+ { "NT_STATUS_ILL_FORMED_PASSWORD", NT_STATUS_ILL_FORMED_PASSWORD },
+ { "NT_STATUS_PASSWORD_RESTRICTION", NT_STATUS_PASSWORD_RESTRICTION },
+ { "NT_STATUS_LOGON_FAILURE", NT_STATUS_LOGON_FAILURE },
+ { "NT_STATUS_ACCOUNT_RESTRICTION", NT_STATUS_ACCOUNT_RESTRICTION },
+ { "NT_STATUS_INVALID_LOGON_HOURS", NT_STATUS_INVALID_LOGON_HOURS },
+ { "NT_STATUS_INVALID_WORKSTATION", NT_STATUS_INVALID_WORKSTATION },
+ { "NT_STATUS_PASSWORD_EXPIRED", NT_STATUS_PASSWORD_EXPIRED },
+ { "NT_STATUS_ACCOUNT_DISABLED", NT_STATUS_ACCOUNT_DISABLED },
+ { "NT_STATUS_NONE_MAPPED", NT_STATUS_NONE_MAPPED },
+ { "NT_STATUS_TOO_MANY_LUIDS_REQUESTED", NT_STATUS_TOO_MANY_LUIDS_REQUESTED },
+ { "NT_STATUS_LUIDS_EXHAUSTED", NT_STATUS_LUIDS_EXHAUSTED },
+ { "NT_STATUS_INVALID_SUB_AUTHORITY", NT_STATUS_INVALID_SUB_AUTHORITY },
+ { "NT_STATUS_INVALID_ACL", NT_STATUS_INVALID_ACL },
+ { "NT_STATUS_INVALID_SID", NT_STATUS_INVALID_SID },
+ { "NT_STATUS_INVALID_SECURITY_DESCR", NT_STATUS_INVALID_SECURITY_DESCR },
+ { "NT_STATUS_PROCEDURE_NOT_FOUND", NT_STATUS_PROCEDURE_NOT_FOUND },
+ { "NT_STATUS_INVALID_IMAGE_FORMAT", NT_STATUS_INVALID_IMAGE_FORMAT },
+ { "NT_STATUS_NO_TOKEN", NT_STATUS_NO_TOKEN },
+ { "NT_STATUS_BAD_INHERITANCE_ACL", NT_STATUS_BAD_INHERITANCE_ACL },
+ { "NT_STATUS_RANGE_NOT_LOCKED", NT_STATUS_RANGE_NOT_LOCKED },
+ { "NT_STATUS_DISK_FULL", NT_STATUS_DISK_FULL },
+ { "NT_STATUS_SERVER_DISABLED", NT_STATUS_SERVER_DISABLED },
+ { "NT_STATUS_SERVER_NOT_DISABLED", NT_STATUS_SERVER_NOT_DISABLED },
+ { "NT_STATUS_TOO_MANY_GUIDS_REQUESTED", NT_STATUS_TOO_MANY_GUIDS_REQUESTED },
+ { "NT_STATUS_GUIDS_EXHAUSTED", NT_STATUS_GUIDS_EXHAUSTED },
+ { "NT_STATUS_INVALID_ID_AUTHORITY", NT_STATUS_INVALID_ID_AUTHORITY },
+ { "NT_STATUS_AGENTS_EXHAUSTED", NT_STATUS_AGENTS_EXHAUSTED },
+ { "NT_STATUS_INVALID_VOLUME_LABEL", NT_STATUS_INVALID_VOLUME_LABEL },
+ { "NT_STATUS_SECTION_NOT_EXTENDED", NT_STATUS_SECTION_NOT_EXTENDED },
+ { "NT_STATUS_NOT_MAPPED_DATA", NT_STATUS_NOT_MAPPED_DATA },
+ { "NT_STATUS_RESOURCE_DATA_NOT_FOUND", NT_STATUS_RESOURCE_DATA_NOT_FOUND },
+ { "NT_STATUS_RESOURCE_TYPE_NOT_FOUND", NT_STATUS_RESOURCE_TYPE_NOT_FOUND },
+ { "NT_STATUS_RESOURCE_NAME_NOT_FOUND", NT_STATUS_RESOURCE_NAME_NOT_FOUND },
+ { "NT_STATUS_ARRAY_BOUNDS_EXCEEDED", NT_STATUS_ARRAY_BOUNDS_EXCEEDED },
+ { "NT_STATUS_FLOAT_DENORMAL_OPERAND", NT_STATUS_FLOAT_DENORMAL_OPERAND },
+ { "NT_STATUS_FLOAT_DIVIDE_BY_ZERO", NT_STATUS_FLOAT_DIVIDE_BY_ZERO },
+ { "NT_STATUS_FLOAT_INEXACT_RESULT", NT_STATUS_FLOAT_INEXACT_RESULT },
+ { "NT_STATUS_FLOAT_INVALID_OPERATION", NT_STATUS_FLOAT_INVALID_OPERATION },
+ { "NT_STATUS_FLOAT_OVERFLOW", NT_STATUS_FLOAT_OVERFLOW },
+ { "NT_STATUS_FLOAT_STACK_CHECK", NT_STATUS_FLOAT_STACK_CHECK },
+ { "NT_STATUS_FLOAT_UNDERFLOW", NT_STATUS_FLOAT_UNDERFLOW },
+ { "NT_STATUS_INTEGER_DIVIDE_BY_ZERO", NT_STATUS_INTEGER_DIVIDE_BY_ZERO },
+ { "NT_STATUS_INTEGER_OVERFLOW", NT_STATUS_INTEGER_OVERFLOW },
+ { "NT_STATUS_PRIVILEGED_INSTRUCTION", NT_STATUS_PRIVILEGED_INSTRUCTION },
+ { "NT_STATUS_TOO_MANY_PAGING_FILES", NT_STATUS_TOO_MANY_PAGING_FILES },
+ { "NT_STATUS_FILE_INVALID", NT_STATUS_FILE_INVALID },
+ { "NT_STATUS_ALLOTTED_SPACE_EXCEEDED", NT_STATUS_ALLOTTED_SPACE_EXCEEDED },
+ { "NT_STATUS_INSUFFICIENT_RESOURCES", NT_STATUS_INSUFFICIENT_RESOURCES },
+ { "NT_STATUS_DFS_EXIT_PATH_FOUND", NT_STATUS_DFS_EXIT_PATH_FOUND },
+ { "NT_STATUS_DEVICE_DATA_ERROR", NT_STATUS_DEVICE_DATA_ERROR },
+ { "NT_STATUS_DEVICE_NOT_CONNECTED", NT_STATUS_DEVICE_NOT_CONNECTED },
+ { "NT_STATUS_DEVICE_POWER_FAILURE", NT_STATUS_DEVICE_POWER_FAILURE },
+ { "NT_STATUS_FREE_VM_NOT_AT_BASE", NT_STATUS_FREE_VM_NOT_AT_BASE },
+ { "NT_STATUS_MEMORY_NOT_ALLOCATED", NT_STATUS_MEMORY_NOT_ALLOCATED },
+ { "NT_STATUS_WORKING_SET_QUOTA", NT_STATUS_WORKING_SET_QUOTA },
+ { "NT_STATUS_MEDIA_WRITE_PROTECTED", NT_STATUS_MEDIA_WRITE_PROTECTED },
+ { "NT_STATUS_DEVICE_NOT_READY", NT_STATUS_DEVICE_NOT_READY },
+ { "NT_STATUS_INVALID_GROUP_ATTRIBUTES", NT_STATUS_INVALID_GROUP_ATTRIBUTES },
+ { "NT_STATUS_BAD_IMPERSONATION_LEVEL", NT_STATUS_BAD_IMPERSONATION_LEVEL },
+ { "NT_STATUS_CANT_OPEN_ANONYMOUS", NT_STATUS_CANT_OPEN_ANONYMOUS },
+ { "NT_STATUS_BAD_VALIDATION_CLASS", NT_STATUS_BAD_VALIDATION_CLASS },
+ { "NT_STATUS_BAD_TOKEN_TYPE", NT_STATUS_BAD_TOKEN_TYPE },
+ { "NT_STATUS_BAD_MASTER_BOOT_RECORD", NT_STATUS_BAD_MASTER_BOOT_RECORD },
+ { "NT_STATUS_INSTRUCTION_MISALIGNMENT", NT_STATUS_INSTRUCTION_MISALIGNMENT },
+ { "NT_STATUS_INSTANCE_NOT_AVAILABLE", NT_STATUS_INSTANCE_NOT_AVAILABLE },
+ { "NT_STATUS_PIPE_NOT_AVAILABLE", NT_STATUS_PIPE_NOT_AVAILABLE },
+ { "NT_STATUS_INVALID_PIPE_STATE", NT_STATUS_INVALID_PIPE_STATE },
+ { "NT_STATUS_PIPE_BUSY", NT_STATUS_PIPE_BUSY },
+ { "NT_STATUS_ILLEGAL_FUNCTION", NT_STATUS_ILLEGAL_FUNCTION },
+ { "NT_STATUS_PIPE_DISCONNECTED", NT_STATUS_PIPE_DISCONNECTED },
+ { "NT_STATUS_PIPE_CLOSING", NT_STATUS_PIPE_CLOSING },
+ { "NT_STATUS_PIPE_CONNECTED", NT_STATUS_PIPE_CONNECTED },
+ { "NT_STATUS_PIPE_LISTENING", NT_STATUS_PIPE_LISTENING },
+ { "NT_STATUS_INVALID_READ_MODE", NT_STATUS_INVALID_READ_MODE },
+ { "NT_STATUS_IO_TIMEOUT", NT_STATUS_IO_TIMEOUT },
+ { "NT_STATUS_FILE_FORCED_CLOSED", NT_STATUS_FILE_FORCED_CLOSED },
+ { "NT_STATUS_PROFILING_NOT_STARTED", NT_STATUS_PROFILING_NOT_STARTED },
+ { "NT_STATUS_PROFILING_NOT_STOPPED", NT_STATUS_PROFILING_NOT_STOPPED },
+ { "NT_STATUS_COULD_NOT_INTERPRET", NT_STATUS_COULD_NOT_INTERPRET },
+ { "NT_STATUS_FILE_IS_A_DIRECTORY", NT_STATUS_FILE_IS_A_DIRECTORY },
+ { "NT_STATUS_NOT_SUPPORTED", NT_STATUS_NOT_SUPPORTED },
+ { "NT_STATUS_REMOTE_NOT_LISTENING", NT_STATUS_REMOTE_NOT_LISTENING },
+ { "NT_STATUS_DUPLICATE_NAME", NT_STATUS_DUPLICATE_NAME },
+ { "NT_STATUS_BAD_NETWORK_PATH", NT_STATUS_BAD_NETWORK_PATH },
+ { "NT_STATUS_NETWORK_BUSY", NT_STATUS_NETWORK_BUSY },
+ { "NT_STATUS_DEVICE_DOES_NOT_EXIST", NT_STATUS_DEVICE_DOES_NOT_EXIST },
+ { "NT_STATUS_TOO_MANY_COMMANDS", NT_STATUS_TOO_MANY_COMMANDS },
+ { "NT_STATUS_ADAPTER_HARDWARE_ERROR", NT_STATUS_ADAPTER_HARDWARE_ERROR },
+ { "NT_STATUS_INVALID_NETWORK_RESPONSE", NT_STATUS_INVALID_NETWORK_RESPONSE },
+ { "NT_STATUS_UNEXPECTED_NETWORK_ERROR", NT_STATUS_UNEXPECTED_NETWORK_ERROR },
+ { "NT_STATUS_BAD_REMOTE_ADAPTER", NT_STATUS_BAD_REMOTE_ADAPTER },
+ { "NT_STATUS_PRINT_QUEUE_FULL", NT_STATUS_PRINT_QUEUE_FULL },
+ { "NT_STATUS_NO_SPOOL_SPACE", NT_STATUS_NO_SPOOL_SPACE },
+ { "NT_STATUS_PRINT_CANCELLED", NT_STATUS_PRINT_CANCELLED },
+ { "NT_STATUS_NETWORK_NAME_DELETED", NT_STATUS_NETWORK_NAME_DELETED },
+ { "NT_STATUS_NETWORK_ACCESS_DENIED", NT_STATUS_NETWORK_ACCESS_DENIED },
+ { "NT_STATUS_BAD_DEVICE_TYPE", NT_STATUS_BAD_DEVICE_TYPE },
+ { "NT_STATUS_BAD_NETWORK_NAME", NT_STATUS_BAD_NETWORK_NAME },
+ { "NT_STATUS_TOO_MANY_NAMES", NT_STATUS_TOO_MANY_NAMES },
+ { "NT_STATUS_TOO_MANY_SESSIONS", NT_STATUS_TOO_MANY_SESSIONS },
+ { "NT_STATUS_SHARING_PAUSED", NT_STATUS_SHARING_PAUSED },
+ { "NT_STATUS_REQUEST_NOT_ACCEPTED", NT_STATUS_REQUEST_NOT_ACCEPTED },
+ { "NT_STATUS_REDIRECTOR_PAUSED", NT_STATUS_REDIRECTOR_PAUSED },
+ { "NT_STATUS_NET_WRITE_FAULT", NT_STATUS_NET_WRITE_FAULT },
+ { "NT_STATUS_PROFILING_AT_LIMIT", NT_STATUS_PROFILING_AT_LIMIT },
+ { "NT_STATUS_NOT_SAME_DEVICE", NT_STATUS_NOT_SAME_DEVICE },
+ { "NT_STATUS_FILE_RENAMED", NT_STATUS_FILE_RENAMED },
+ { "NT_STATUS_VIRTUAL_CIRCUIT_CLOSED", NT_STATUS_VIRTUAL_CIRCUIT_CLOSED },
+ { "NT_STATUS_NO_SECURITY_ON_OBJECT", NT_STATUS_NO_SECURITY_ON_OBJECT },
+ { "NT_STATUS_CANT_WAIT", NT_STATUS_CANT_WAIT },
+ { "NT_STATUS_PIPE_EMPTY", NT_STATUS_PIPE_EMPTY },
+ { "NT_STATUS_CANT_ACCESS_DOMAIN_INFO", NT_STATUS_CANT_ACCESS_DOMAIN_INFO },
+ { "NT_STATUS_CANT_TERMINATE_SELF", NT_STATUS_CANT_TERMINATE_SELF },
+ { "NT_STATUS_INVALID_SERVER_STATE", NT_STATUS_INVALID_SERVER_STATE },
+ { "NT_STATUS_INVALID_DOMAIN_STATE", NT_STATUS_INVALID_DOMAIN_STATE },
+ { "NT_STATUS_INVALID_DOMAIN_ROLE", NT_STATUS_INVALID_DOMAIN_ROLE },
+ { "NT_STATUS_NO_SUCH_DOMAIN", NT_STATUS_NO_SUCH_DOMAIN },
+ { "NT_STATUS_DOMAIN_EXISTS", NT_STATUS_DOMAIN_EXISTS },
+ { "NT_STATUS_DOMAIN_LIMIT_EXCEEDED", NT_STATUS_DOMAIN_LIMIT_EXCEEDED },
+ { "NT_STATUS_OPLOCK_NOT_GRANTED", NT_STATUS_OPLOCK_NOT_GRANTED },
+ { "NT_STATUS_INVALID_OPLOCK_PROTOCOL", NT_STATUS_INVALID_OPLOCK_PROTOCOL },
+ { "NT_STATUS_INTERNAL_DB_CORRUPTION", NT_STATUS_INTERNAL_DB_CORRUPTION },
+ { "NT_STATUS_INTERNAL_ERROR", NT_STATUS_INTERNAL_ERROR },
+ { "NT_STATUS_GENERIC_NOT_MAPPED", NT_STATUS_GENERIC_NOT_MAPPED },
+ { "NT_STATUS_BAD_DESCRIPTOR_FORMAT", NT_STATUS_BAD_DESCRIPTOR_FORMAT },
+ { "NT_STATUS_INVALID_USER_BUFFER", NT_STATUS_INVALID_USER_BUFFER },
+ { "NT_STATUS_UNEXPECTED_IO_ERROR", NT_STATUS_UNEXPECTED_IO_ERROR },
+ { "NT_STATUS_UNEXPECTED_MM_CREATE_ERR", NT_STATUS_UNEXPECTED_MM_CREATE_ERR },
+ { "NT_STATUS_UNEXPECTED_MM_MAP_ERROR", NT_STATUS_UNEXPECTED_MM_MAP_ERROR },
+ { "NT_STATUS_UNEXPECTED_MM_EXTEND_ERR", NT_STATUS_UNEXPECTED_MM_EXTEND_ERR },
+ { "NT_STATUS_NOT_LOGON_PROCESS", NT_STATUS_NOT_LOGON_PROCESS },
+ { "NT_STATUS_LOGON_SESSION_EXISTS", NT_STATUS_LOGON_SESSION_EXISTS },
+ { "NT_STATUS_INVALID_PARAMETER_1", NT_STATUS_INVALID_PARAMETER_1 },
+ { "NT_STATUS_INVALID_PARAMETER_2", NT_STATUS_INVALID_PARAMETER_2 },
+ { "NT_STATUS_INVALID_PARAMETER_3", NT_STATUS_INVALID_PARAMETER_3 },
+ { "NT_STATUS_INVALID_PARAMETER_4", NT_STATUS_INVALID_PARAMETER_4 },
+ { "NT_STATUS_INVALID_PARAMETER_5", NT_STATUS_INVALID_PARAMETER_5 },
+ { "NT_STATUS_INVALID_PARAMETER_6", NT_STATUS_INVALID_PARAMETER_6 },
+ { "NT_STATUS_INVALID_PARAMETER_7", NT_STATUS_INVALID_PARAMETER_7 },
+ { "NT_STATUS_INVALID_PARAMETER_8", NT_STATUS_INVALID_PARAMETER_8 },
+ { "NT_STATUS_INVALID_PARAMETER_9", NT_STATUS_INVALID_PARAMETER_9 },
+ { "NT_STATUS_INVALID_PARAMETER_10", NT_STATUS_INVALID_PARAMETER_10 },
+ { "NT_STATUS_INVALID_PARAMETER_11", NT_STATUS_INVALID_PARAMETER_11 },
+ { "NT_STATUS_INVALID_PARAMETER_12", NT_STATUS_INVALID_PARAMETER_12 },
+ { "NT_STATUS_REDIRECTOR_NOT_STARTED", NT_STATUS_REDIRECTOR_NOT_STARTED },
+ { "NT_STATUS_REDIRECTOR_STARTED", NT_STATUS_REDIRECTOR_STARTED },
+ { "NT_STATUS_STACK_OVERFLOW", NT_STATUS_STACK_OVERFLOW },
+ { "NT_STATUS_NO_SUCH_PACKAGE", NT_STATUS_NO_SUCH_PACKAGE },
+ { "NT_STATUS_BAD_FUNCTION_TABLE", NT_STATUS_BAD_FUNCTION_TABLE },
+ { "NT_STATUS_DIRECTORY_NOT_EMPTY", NT_STATUS_DIRECTORY_NOT_EMPTY },
+ { "NT_STATUS_FILE_CORRUPT_ERROR", NT_STATUS_FILE_CORRUPT_ERROR },
+ { "NT_STATUS_NOT_A_DIRECTORY", NT_STATUS_NOT_A_DIRECTORY },
+ { "NT_STATUS_BAD_LOGON_SESSION_STATE", NT_STATUS_BAD_LOGON_SESSION_STATE },
+ { "NT_STATUS_LOGON_SESSION_COLLISION", NT_STATUS_LOGON_SESSION_COLLISION },
+ { "NT_STATUS_NAME_TOO_LONG", NT_STATUS_NAME_TOO_LONG },
+ { "NT_STATUS_FILES_OPEN", NT_STATUS_FILES_OPEN },
+ { "NT_STATUS_CONNECTION_IN_USE", NT_STATUS_CONNECTION_IN_USE },
+ { "NT_STATUS_MESSAGE_NOT_FOUND", NT_STATUS_MESSAGE_NOT_FOUND },
+ { "NT_STATUS_PROCESS_IS_TERMINATING", NT_STATUS_PROCESS_IS_TERMINATING },
+ { "NT_STATUS_INVALID_LOGON_TYPE", NT_STATUS_INVALID_LOGON_TYPE },
+ { "NT_STATUS_NO_GUID_TRANSLATION", NT_STATUS_NO_GUID_TRANSLATION },
+ { "NT_STATUS_CANNOT_IMPERSONATE", NT_STATUS_CANNOT_IMPERSONATE },
+ { "NT_STATUS_IMAGE_ALREADY_LOADED", NT_STATUS_IMAGE_ALREADY_LOADED },
+ { "NT_STATUS_ABIOS_NOT_PRESENT", NT_STATUS_ABIOS_NOT_PRESENT },
+ { "NT_STATUS_ABIOS_LID_NOT_EXIST", NT_STATUS_ABIOS_LID_NOT_EXIST },
+ { "NT_STATUS_ABIOS_LID_ALREADY_OWNED", NT_STATUS_ABIOS_LID_ALREADY_OWNED },
+ { "NT_STATUS_ABIOS_NOT_LID_OWNER", NT_STATUS_ABIOS_NOT_LID_OWNER },
+ { "NT_STATUS_ABIOS_INVALID_COMMAND", NT_STATUS_ABIOS_INVALID_COMMAND },
+ { "NT_STATUS_ABIOS_INVALID_LID", NT_STATUS_ABIOS_INVALID_LID },
+ { "NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE", NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE },
+ { "NT_STATUS_ABIOS_INVALID_SELECTOR", NT_STATUS_ABIOS_INVALID_SELECTOR },
+ { "NT_STATUS_NO_LDT", NT_STATUS_NO_LDT },
+ { "NT_STATUS_INVALID_LDT_SIZE", NT_STATUS_INVALID_LDT_SIZE },
+ { "NT_STATUS_INVALID_LDT_OFFSET", NT_STATUS_INVALID_LDT_OFFSET },
+ { "NT_STATUS_INVALID_LDT_DESCRIPTOR", NT_STATUS_INVALID_LDT_DESCRIPTOR },
+ { "NT_STATUS_INVALID_IMAGE_NE_FORMAT", NT_STATUS_INVALID_IMAGE_NE_FORMAT },
+ { "NT_STATUS_RXACT_INVALID_STATE", NT_STATUS_RXACT_INVALID_STATE },
+ { "NT_STATUS_RXACT_COMMIT_FAILURE", NT_STATUS_RXACT_COMMIT_FAILURE },
+ { "NT_STATUS_MAPPED_FILE_SIZE_ZERO", NT_STATUS_MAPPED_FILE_SIZE_ZERO },
+ { "NT_STATUS_TOO_MANY_OPENED_FILES", NT_STATUS_TOO_MANY_OPENED_FILES },
+ { "NT_STATUS_CANCELLED", NT_STATUS_CANCELLED },
+ { "NT_STATUS_CANNOT_DELETE", NT_STATUS_CANNOT_DELETE },
+ { "NT_STATUS_INVALID_COMPUTER_NAME", NT_STATUS_INVALID_COMPUTER_NAME },
+ { "NT_STATUS_FILE_DELETED", NT_STATUS_FILE_DELETED },
+ { "NT_STATUS_SPECIAL_ACCOUNT", NT_STATUS_SPECIAL_ACCOUNT },
+ { "NT_STATUS_SPECIAL_GROUP", NT_STATUS_SPECIAL_GROUP },
+ { "NT_STATUS_SPECIAL_USER", NT_STATUS_SPECIAL_USER },
+ { "NT_STATUS_MEMBERS_PRIMARY_GROUP", NT_STATUS_MEMBERS_PRIMARY_GROUP },
+ { "NT_STATUS_FILE_CLOSED", NT_STATUS_FILE_CLOSED },
+ { "NT_STATUS_TOO_MANY_THREADS", NT_STATUS_TOO_MANY_THREADS },
+ { "NT_STATUS_THREAD_NOT_IN_PROCESS", NT_STATUS_THREAD_NOT_IN_PROCESS },
+ { "NT_STATUS_TOKEN_ALREADY_IN_USE", NT_STATUS_TOKEN_ALREADY_IN_USE },
+ { "NT_STATUS_PAGEFILE_QUOTA_EXCEEDED", NT_STATUS_PAGEFILE_QUOTA_EXCEEDED },
+ { "NT_STATUS_COMMITMENT_LIMIT", NT_STATUS_COMMITMENT_LIMIT },
+ { "NT_STATUS_INVALID_IMAGE_LE_FORMAT", NT_STATUS_INVALID_IMAGE_LE_FORMAT },
+ { "NT_STATUS_INVALID_IMAGE_NOT_MZ", NT_STATUS_INVALID_IMAGE_NOT_MZ },
+ { "NT_STATUS_INVALID_IMAGE_PROTECT", NT_STATUS_INVALID_IMAGE_PROTECT },
+ { "NT_STATUS_INVALID_IMAGE_WIN_16", NT_STATUS_INVALID_IMAGE_WIN_16 },
+ { "NT_STATUS_LOGON_SERVER_CONFLICT", NT_STATUS_LOGON_SERVER_CONFLICT },
+ { "NT_STATUS_TIME_DIFFERENCE_AT_DC", NT_STATUS_TIME_DIFFERENCE_AT_DC },
+ { "NT_STATUS_SYNCHRONIZATION_REQUIRED", NT_STATUS_SYNCHRONIZATION_REQUIRED },
+ { "NT_STATUS_DLL_NOT_FOUND", NT_STATUS_DLL_NOT_FOUND },
+ { "NT_STATUS_OPEN_FAILED", NT_STATUS_OPEN_FAILED },
+ { "NT_STATUS_IO_PRIVILEGE_FAILED", NT_STATUS_IO_PRIVILEGE_FAILED },
+ { "NT_STATUS_ORDINAL_NOT_FOUND", NT_STATUS_ORDINAL_NOT_FOUND },
+ { "NT_STATUS_ENTRYPOINT_NOT_FOUND", NT_STATUS_ENTRYPOINT_NOT_FOUND },
+ { "NT_STATUS_CONTROL_C_EXIT", NT_STATUS_CONTROL_C_EXIT },
+ { "NT_STATUS_LOCAL_DISCONNECT", NT_STATUS_LOCAL_DISCONNECT },
+ { "NT_STATUS_REMOTE_DISCONNECT", NT_STATUS_REMOTE_DISCONNECT },
+ { "NT_STATUS_REMOTE_RESOURCES", NT_STATUS_REMOTE_RESOURCES },
+ { "NT_STATUS_LINK_FAILED", NT_STATUS_LINK_FAILED },
+ { "NT_STATUS_LINK_TIMEOUT", NT_STATUS_LINK_TIMEOUT },
+ { "NT_STATUS_INVALID_CONNECTION", NT_STATUS_INVALID_CONNECTION },
+ { "NT_STATUS_INVALID_ADDRESS", NT_STATUS_INVALID_ADDRESS },
+ { "NT_STATUS_DLL_INIT_FAILED", NT_STATUS_DLL_INIT_FAILED },
+ { "NT_STATUS_MISSING_SYSTEMFILE", NT_STATUS_MISSING_SYSTEMFILE },
+ { "NT_STATUS_UNHANDLED_EXCEPTION", NT_STATUS_UNHANDLED_EXCEPTION },
+ { "NT_STATUS_APP_INIT_FAILURE", NT_STATUS_APP_INIT_FAILURE },
+ { "NT_STATUS_PAGEFILE_CREATE_FAILED", NT_STATUS_PAGEFILE_CREATE_FAILED },
+ { "NT_STATUS_NO_PAGEFILE", NT_STATUS_NO_PAGEFILE },
+ { "NT_STATUS_INVALID_LEVEL", NT_STATUS_INVALID_LEVEL },
+ { "NT_STATUS_WRONG_PASSWORD_CORE", NT_STATUS_WRONG_PASSWORD_CORE },
+ { "NT_STATUS_ILLEGAL_FLOAT_CONTEXT", NT_STATUS_ILLEGAL_FLOAT_CONTEXT },
+ { "NT_STATUS_PIPE_BROKEN", NT_STATUS_PIPE_BROKEN },
+ { "NT_STATUS_REGISTRY_CORRUPT", NT_STATUS_REGISTRY_CORRUPT },
+ { "NT_STATUS_REGISTRY_IO_FAILED", NT_STATUS_REGISTRY_IO_FAILED },
+ { "NT_STATUS_NO_EVENT_PAIR", NT_STATUS_NO_EVENT_PAIR },
+ { "NT_STATUS_UNRECOGNIZED_VOLUME", NT_STATUS_UNRECOGNIZED_VOLUME },
+ { "NT_STATUS_SERIAL_NO_DEVICE_INITED", NT_STATUS_SERIAL_NO_DEVICE_INITED },
+ { "NT_STATUS_NO_SUCH_ALIAS", NT_STATUS_NO_SUCH_ALIAS },
+ { "NT_STATUS_MEMBER_NOT_IN_ALIAS", NT_STATUS_MEMBER_NOT_IN_ALIAS },
+ { "NT_STATUS_MEMBER_IN_ALIAS", NT_STATUS_MEMBER_IN_ALIAS },
+ { "NT_STATUS_ALIAS_EXISTS", NT_STATUS_ALIAS_EXISTS },
+ { "NT_STATUS_LOGON_NOT_GRANTED", NT_STATUS_LOGON_NOT_GRANTED },
+ { "NT_STATUS_TOO_MANY_SECRETS", NT_STATUS_TOO_MANY_SECRETS },
+ { "NT_STATUS_SECRET_TOO_LONG", NT_STATUS_SECRET_TOO_LONG },
+ { "NT_STATUS_INTERNAL_DB_ERROR", NT_STATUS_INTERNAL_DB_ERROR },
+ { "NT_STATUS_FULLSCREEN_MODE", NT_STATUS_FULLSCREEN_MODE },
+ { "NT_STATUS_TOO_MANY_CONTEXT_IDS", NT_STATUS_TOO_MANY_CONTEXT_IDS },
+ { "NT_STATUS_LOGON_TYPE_NOT_GRANTED", NT_STATUS_LOGON_TYPE_NOT_GRANTED },
+ { "NT_STATUS_NOT_REGISTRY_FILE", NT_STATUS_NOT_REGISTRY_FILE },
+ { "NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED", NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED },
+ { "NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR", NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR },
+ { "NT_STATUS_FT_MISSING_MEMBER", NT_STATUS_FT_MISSING_MEMBER },
+ { "NT_STATUS_ILL_FORMED_SERVICE_ENTRY", NT_STATUS_ILL_FORMED_SERVICE_ENTRY },
+ { "NT_STATUS_ILLEGAL_CHARACTER", NT_STATUS_ILLEGAL_CHARACTER },
+ { "NT_STATUS_UNMAPPABLE_CHARACTER", NT_STATUS_UNMAPPABLE_CHARACTER },
+ { "NT_STATUS_UNDEFINED_CHARACTER", NT_STATUS_UNDEFINED_CHARACTER },
+ { "NT_STATUS_FLOPPY_VOLUME", NT_STATUS_FLOPPY_VOLUME },
+ { "NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND", NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND },
+ { "NT_STATUS_FLOPPY_WRONG_CYLINDER", NT_STATUS_FLOPPY_WRONG_CYLINDER },
+ { "NT_STATUS_FLOPPY_UNKNOWN_ERROR", NT_STATUS_FLOPPY_UNKNOWN_ERROR },
+ { "NT_STATUS_FLOPPY_BAD_REGISTERS", NT_STATUS_FLOPPY_BAD_REGISTERS },
+ { "NT_STATUS_DISK_RECALIBRATE_FAILED", NT_STATUS_DISK_RECALIBRATE_FAILED },
+ { "NT_STATUS_DISK_OPERATION_FAILED", NT_STATUS_DISK_OPERATION_FAILED },
+ { "NT_STATUS_DISK_RESET_FAILED", NT_STATUS_DISK_RESET_FAILED },
+ { "NT_STATUS_SHARED_IRQ_BUSY", NT_STATUS_SHARED_IRQ_BUSY },
+ { "NT_STATUS_FT_ORPHANING", NT_STATUS_FT_ORPHANING },
+ { "NT_STATUS_PARTITION_FAILURE", NT_STATUS_PARTITION_FAILURE },
+ { "NT_STATUS_INVALID_BLOCK_LENGTH", NT_STATUS_INVALID_BLOCK_LENGTH },
+ { "NT_STATUS_DEVICE_NOT_PARTITIONED", NT_STATUS_DEVICE_NOT_PARTITIONED },
+ { "NT_STATUS_UNABLE_TO_LOCK_MEDIA", NT_STATUS_UNABLE_TO_LOCK_MEDIA },
+ { "NT_STATUS_UNABLE_TO_UNLOAD_MEDIA", NT_STATUS_UNABLE_TO_UNLOAD_MEDIA },
+ { "NT_STATUS_EOM_OVERFLOW", NT_STATUS_EOM_OVERFLOW },
+ { "NT_STATUS_NO_MEDIA", NT_STATUS_NO_MEDIA },
+ { "NT_STATUS_NO_SUCH_MEMBER", NT_STATUS_NO_SUCH_MEMBER },
+ { "NT_STATUS_INVALID_MEMBER", NT_STATUS_INVALID_MEMBER },
+ { "NT_STATUS_KEY_DELETED", NT_STATUS_KEY_DELETED },
+ { "NT_STATUS_NO_LOG_SPACE", NT_STATUS_NO_LOG_SPACE },
+ { "NT_STATUS_TOO_MANY_SIDS", NT_STATUS_TOO_MANY_SIDS },
+ { "NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED", NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED },
+ { "NT_STATUS_KEY_HAS_CHILDREN", NT_STATUS_KEY_HAS_CHILDREN },
+ { "NT_STATUS_CHILD_MUST_BE_VOLATILE", NT_STATUS_CHILD_MUST_BE_VOLATILE },
+ { "NT_STATUS_DEVICE_CONFIGURATION_ERROR", NT_STATUS_DEVICE_CONFIGURATION_ERROR },
+ { "NT_STATUS_DRIVER_INTERNAL_ERROR", NT_STATUS_DRIVER_INTERNAL_ERROR },
+ { "NT_STATUS_INVALID_DEVICE_STATE", NT_STATUS_INVALID_DEVICE_STATE },
+ { "NT_STATUS_IO_DEVICE_ERROR", NT_STATUS_IO_DEVICE_ERROR },
+ { "NT_STATUS_DEVICE_PROTOCOL_ERROR", NT_STATUS_DEVICE_PROTOCOL_ERROR },
+ { "NT_STATUS_BACKUP_CONTROLLER", NT_STATUS_BACKUP_CONTROLLER },
+ { "NT_STATUS_LOG_FILE_FULL", NT_STATUS_LOG_FILE_FULL },
+ { "NT_STATUS_TOO_LATE", NT_STATUS_TOO_LATE },
+ { "NT_STATUS_NO_TRUST_LSA_SECRET", NT_STATUS_NO_TRUST_LSA_SECRET },
+ { "NT_STATUS_NO_TRUST_SAM_ACCOUNT", NT_STATUS_NO_TRUST_SAM_ACCOUNT },
+ { "NT_STATUS_TRUSTED_DOMAIN_FAILURE", NT_STATUS_TRUSTED_DOMAIN_FAILURE },
+ { "NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE", NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE },
+ { "NT_STATUS_EVENTLOG_FILE_CORRUPT", NT_STATUS_EVENTLOG_FILE_CORRUPT },
+ { "NT_STATUS_EVENTLOG_CANT_START", NT_STATUS_EVENTLOG_CANT_START },
+ { "NT_STATUS_TRUST_FAILURE", NT_STATUS_TRUST_FAILURE },
+ { "NT_STATUS_MUTANT_LIMIT_EXCEEDED", NT_STATUS_MUTANT_LIMIT_EXCEEDED },
+ { "NT_STATUS_NETLOGON_NOT_STARTED", NT_STATUS_NETLOGON_NOT_STARTED },
+ { "NT_STATUS_ACCOUNT_EXPIRED", NT_STATUS_ACCOUNT_EXPIRED },
+ { "NT_STATUS_POSSIBLE_DEADLOCK", NT_STATUS_POSSIBLE_DEADLOCK },
+ { "NT_STATUS_NETWORK_CREDENTIAL_CONFLICT", NT_STATUS_NETWORK_CREDENTIAL_CONFLICT },
+ { "NT_STATUS_REMOTE_SESSION_LIMIT", NT_STATUS_REMOTE_SESSION_LIMIT },
+ { "NT_STATUS_EVENTLOG_FILE_CHANGED", NT_STATUS_EVENTLOG_FILE_CHANGED },
+ { "NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT", NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT },
+ { "NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT", NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT },
+ { "NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT", NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT },
+ { "NT_STATUS_DOMAIN_TRUST_INCONSISTENT", NT_STATUS_DOMAIN_TRUST_INCONSISTENT },
+ { "NT_STATUS_FS_DRIVER_REQUIRED", NT_STATUS_FS_DRIVER_REQUIRED },
+ { "NT_STATUS_NO_USER_SESSION_KEY", NT_STATUS_NO_USER_SESSION_KEY },
+ { "NT_STATUS_USER_SESSION_DELETED", NT_STATUS_USER_SESSION_DELETED },
+ { "NT_STATUS_RESOURCE_LANG_NOT_FOUND", NT_STATUS_RESOURCE_LANG_NOT_FOUND },
+ { "NT_STATUS_INSUFF_SERVER_RESOURCES", NT_STATUS_INSUFF_SERVER_RESOURCES },
+ { "NT_STATUS_INVALID_BUFFER_SIZE", NT_STATUS_INVALID_BUFFER_SIZE },
+ { "NT_STATUS_INVALID_ADDRESS_COMPONENT", NT_STATUS_INVALID_ADDRESS_COMPONENT },
+ { "NT_STATUS_INVALID_ADDRESS_WILDCARD", NT_STATUS_INVALID_ADDRESS_WILDCARD },
+ { "NT_STATUS_TOO_MANY_ADDRESSES", NT_STATUS_TOO_MANY_ADDRESSES },
+ { "NT_STATUS_ADDRESS_ALREADY_EXISTS", NT_STATUS_ADDRESS_ALREADY_EXISTS },
+ { "NT_STATUS_ADDRESS_CLOSED", NT_STATUS_ADDRESS_CLOSED },
+ { "NT_STATUS_CONNECTION_DISCONNECTED", NT_STATUS_CONNECTION_DISCONNECTED },
+ { "NT_STATUS_CONNECTION_RESET", NT_STATUS_CONNECTION_RESET },
+ { "NT_STATUS_TOO_MANY_NODES", NT_STATUS_TOO_MANY_NODES },
+ { "NT_STATUS_TRANSACTION_ABORTED", NT_STATUS_TRANSACTION_ABORTED },
+ { "NT_STATUS_TRANSACTION_TIMED_OUT", NT_STATUS_TRANSACTION_TIMED_OUT },
+ { "NT_STATUS_TRANSACTION_NO_RELEASE", NT_STATUS_TRANSACTION_NO_RELEASE },
+ { "NT_STATUS_TRANSACTION_NO_MATCH", NT_STATUS_TRANSACTION_NO_MATCH },
+ { "NT_STATUS_TRANSACTION_RESPONDED", NT_STATUS_TRANSACTION_RESPONDED },
+ { "NT_STATUS_TRANSACTION_INVALID_ID", NT_STATUS_TRANSACTION_INVALID_ID },
+ { "NT_STATUS_TRANSACTION_INVALID_TYPE", NT_STATUS_TRANSACTION_INVALID_TYPE },
+ { "NT_STATUS_NOT_SERVER_SESSION", NT_STATUS_NOT_SERVER_SESSION },
+ { "NT_STATUS_NOT_CLIENT_SESSION", NT_STATUS_NOT_CLIENT_SESSION },
+ { "NT_STATUS_CANNOT_LOAD_REGISTRY_FILE", NT_STATUS_CANNOT_LOAD_REGISTRY_FILE },
+ { "NT_STATUS_DEBUG_ATTACH_FAILED", NT_STATUS_DEBUG_ATTACH_FAILED },
+ { "NT_STATUS_SYSTEM_PROCESS_TERMINATED", NT_STATUS_SYSTEM_PROCESS_TERMINATED },
+ { "NT_STATUS_DATA_NOT_ACCEPTED", NT_STATUS_DATA_NOT_ACCEPTED },
+ { "NT_STATUS_NO_BROWSER_SERVERS_FOUND", NT_STATUS_NO_BROWSER_SERVERS_FOUND },
+ { "NT_STATUS_VDM_HARD_ERROR", NT_STATUS_VDM_HARD_ERROR },
+ { "NT_STATUS_DRIVER_CANCEL_TIMEOUT", NT_STATUS_DRIVER_CANCEL_TIMEOUT },
+ { "NT_STATUS_REPLY_MESSAGE_MISMATCH", NT_STATUS_REPLY_MESSAGE_MISMATCH },
+ { "NT_STATUS_MAPPED_ALIGNMENT", NT_STATUS_MAPPED_ALIGNMENT },
+ { "NT_STATUS_IMAGE_CHECKSUM_MISMATCH", NT_STATUS_IMAGE_CHECKSUM_MISMATCH },
+ { "NT_STATUS_LOST_WRITEBEHIND_DATA", NT_STATUS_LOST_WRITEBEHIND_DATA },
+ { "NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID", NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID },
+ { "NT_STATUS_PASSWORD_MUST_CHANGE", NT_STATUS_PASSWORD_MUST_CHANGE },
+ { "NT_STATUS_NOT_FOUND", NT_STATUS_NOT_FOUND },
+ { "NT_STATUS_NOT_TINY_STREAM", NT_STATUS_NOT_TINY_STREAM },
+ { "NT_STATUS_RECOVERY_FAILURE", NT_STATUS_RECOVERY_FAILURE },
+ { "NT_STATUS_STACK_OVERFLOW_READ", NT_STATUS_STACK_OVERFLOW_READ },
+ { "NT_STATUS_FAIL_CHECK", NT_STATUS_FAIL_CHECK },
+ { "NT_STATUS_DUPLICATE_OBJECTID", NT_STATUS_DUPLICATE_OBJECTID },
+ { "NT_STATUS_OBJECTID_EXISTS", NT_STATUS_OBJECTID_EXISTS },
+ { "NT_STATUS_CONVERT_TO_LARGE", NT_STATUS_CONVERT_TO_LARGE },
+ { "NT_STATUS_RETRY", NT_STATUS_RETRY },
+ { "NT_STATUS_FOUND_OUT_OF_SCOPE", NT_STATUS_FOUND_OUT_OF_SCOPE },
+ { "NT_STATUS_ALLOCATE_BUCKET", NT_STATUS_ALLOCATE_BUCKET },
+ { "NT_STATUS_PROPSET_NOT_FOUND", NT_STATUS_PROPSET_NOT_FOUND },
+ { "NT_STATUS_MARSHALL_OVERFLOW", NT_STATUS_MARSHALL_OVERFLOW },
+ { "NT_STATUS_INVALID_VARIANT", NT_STATUS_INVALID_VARIANT },
+ { "NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND", NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND },
+ { "NT_STATUS_ACCOUNT_LOCKED_OUT", NT_STATUS_ACCOUNT_LOCKED_OUT },
+ { "NT_STATUS_HANDLE_NOT_CLOSABLE", NT_STATUS_HANDLE_NOT_CLOSABLE },
+ { "NT_STATUS_CONNECTION_REFUSED", NT_STATUS_CONNECTION_REFUSED },
+ { "NT_STATUS_GRACEFUL_DISCONNECT", NT_STATUS_GRACEFUL_DISCONNECT },
+ { "NT_STATUS_ADDRESS_ALREADY_ASSOCIATED", NT_STATUS_ADDRESS_ALREADY_ASSOCIATED },
+ { "NT_STATUS_ADDRESS_NOT_ASSOCIATED", NT_STATUS_ADDRESS_NOT_ASSOCIATED },
+ { "NT_STATUS_CONNECTION_INVALID", NT_STATUS_CONNECTION_INVALID },
+ { "NT_STATUS_CONNECTION_ACTIVE", NT_STATUS_CONNECTION_ACTIVE },
+ { "NT_STATUS_NETWORK_UNREACHABLE", NT_STATUS_NETWORK_UNREACHABLE },
+ { "NT_STATUS_HOST_UNREACHABLE", NT_STATUS_HOST_UNREACHABLE },
+ { "NT_STATUS_PROTOCOL_UNREACHABLE", NT_STATUS_PROTOCOL_UNREACHABLE },
+ { "NT_STATUS_PORT_UNREACHABLE", NT_STATUS_PORT_UNREACHABLE },
+ { "NT_STATUS_REQUEST_ABORTED", NT_STATUS_REQUEST_ABORTED },
+ { "NT_STATUS_CONNECTION_ABORTED", NT_STATUS_CONNECTION_ABORTED },
+ { "NT_STATUS_BAD_COMPRESSION_BUFFER", NT_STATUS_BAD_COMPRESSION_BUFFER },
+ { "NT_STATUS_USER_MAPPED_FILE", NT_STATUS_USER_MAPPED_FILE },
+ { "NT_STATUS_AUDIT_FAILED", NT_STATUS_AUDIT_FAILED },
+ { "NT_STATUS_TIMER_RESOLUTION_NOT_SET", NT_STATUS_TIMER_RESOLUTION_NOT_SET },
+ { "NT_STATUS_CONNECTION_COUNT_LIMIT", NT_STATUS_CONNECTION_COUNT_LIMIT },
+ { "NT_STATUS_LOGIN_TIME_RESTRICTION", NT_STATUS_LOGIN_TIME_RESTRICTION },
+ { "NT_STATUS_LOGIN_WKSTA_RESTRICTION", NT_STATUS_LOGIN_WKSTA_RESTRICTION },
+ { "NT_STATUS_IMAGE_MP_UP_MISMATCH", NT_STATUS_IMAGE_MP_UP_MISMATCH },
+ { "NT_STATUS_INSUFFICIENT_LOGON_INFO", NT_STATUS_INSUFFICIENT_LOGON_INFO },
+ { "NT_STATUS_BAD_DLL_ENTRYPOINT", NT_STATUS_BAD_DLL_ENTRYPOINT },
+ { "NT_STATUS_BAD_SERVICE_ENTRYPOINT", NT_STATUS_BAD_SERVICE_ENTRYPOINT },
+ { "NT_STATUS_LPC_REPLY_LOST", NT_STATUS_LPC_REPLY_LOST },
+ { "NT_STATUS_IP_ADDRESS_CONFLICT1", NT_STATUS_IP_ADDRESS_CONFLICT1 },
+ { "NT_STATUS_IP_ADDRESS_CONFLICT2", NT_STATUS_IP_ADDRESS_CONFLICT2 },
+ { "NT_STATUS_REGISTRY_QUOTA_LIMIT", NT_STATUS_REGISTRY_QUOTA_LIMIT },
+ { "NT_STATUS_PATH_NOT_COVERED", NT_STATUS_PATH_NOT_COVERED },
+ { "NT_STATUS_NO_CALLBACK_ACTIVE", NT_STATUS_NO_CALLBACK_ACTIVE },
+ { "NT_STATUS_LICENSE_QUOTA_EXCEEDED", NT_STATUS_LICENSE_QUOTA_EXCEEDED },
+ { "NT_STATUS_PWD_TOO_SHORT", NT_STATUS_PWD_TOO_SHORT },
+ { "NT_STATUS_PWD_TOO_RECENT", NT_STATUS_PWD_TOO_RECENT },
+ { "NT_STATUS_PWD_HISTORY_CONFLICT", NT_STATUS_PWD_HISTORY_CONFLICT },
+ { "NT_STATUS_PLUGPLAY_NO_DEVICE", NT_STATUS_PLUGPLAY_NO_DEVICE },
+ { "NT_STATUS_UNSUPPORTED_COMPRESSION", NT_STATUS_UNSUPPORTED_COMPRESSION },
+ { "NT_STATUS_INVALID_HW_PROFILE", NT_STATUS_INVALID_HW_PROFILE },
+ { "NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH", NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH },
+ { "NT_STATUS_DRIVER_ORDINAL_NOT_FOUND", NT_STATUS_DRIVER_ORDINAL_NOT_FOUND },
+ { "NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND", NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND },
+ { "NT_STATUS_RESOURCE_NOT_OWNED", NT_STATUS_RESOURCE_NOT_OWNED },
+ { "NT_STATUS_TOO_MANY_LINKS", NT_STATUS_TOO_MANY_LINKS },
+ { "NT_STATUS_QUOTA_LIST_INCONSISTENT", NT_STATUS_QUOTA_LIST_INCONSISTENT },
+ { "NT_STATUS_FILE_IS_OFFLINE", NT_STATUS_FILE_IS_OFFLINE },
+ { "NT_STATUS_NO_MORE_ENTRIES", NT_STATUS_NO_MORE_ENTRIES },
+ { "NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED", NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED },
+ { "NT_STATUS_RPC_UNSUPPORTED_NAME_SYNTAX", NT_STATUS_RPC_UNSUPPORTED_NAME_SYNTAX },
+ { "NT_STATUS_CURRENT_DOMAIN_NOT_ALLOWED", NT_STATUS_CURRENT_DOMAIN_NOT_ALLOWED },
+ { "NT_STATUS_OBJECTID_NOT_FOUND", NT_STATUS_OBJECTID_NOT_FOUND },
+ { "NT_STATUS_DOWNGRADE_DETECTED", NT_STATUS_DOWNGRADE_DETECTED },
+ { "NT_STATUS_DS_BUSY", NT_STATUS_DS_BUSY },
+ { "STATUS_MORE_ENTRIES", STATUS_MORE_ENTRIES },
+ { "STATUS_SOME_UNMAPPED", STATUS_SOME_UNMAPPED },
+ { "STATUS_NOTIFY_CLEANUP", STATUS_NOTIFY_CLEANUP },
+ { "STATUS_NOTIFY_ENUM_DIR", STATUS_NOTIFY_ENUM_DIR },
+
+ DOS_CODE(ERRDOS, ERRsuccess),
+ DOS_CODE(ERRDOS, ERRbadfunc),
+ DOS_CODE(ERRDOS, ERRbadfile),
+ DOS_CODE(ERRDOS, ERRbadpath),
+ DOS_CODE(ERRDOS, ERRnofids),
+ DOS_CODE(ERRDOS, ERRnoaccess),
+ DOS_CODE(ERRDOS, ERRbadfid),
+ DOS_CODE(ERRDOS, ERRbadmcb),
+ DOS_CODE(ERRDOS, ERRnomem),
+ DOS_CODE(ERRDOS, ERRbadmem),
+ DOS_CODE(ERRDOS, ERRbadenv),
+ DOS_CODE(ERRDOS, ERRbadaccess),
+ DOS_CODE(ERRDOS, ERRbaddata),
+ DOS_CODE(ERRDOS, ERRres),
+ DOS_CODE(ERRDOS, ERRbaddrive),
+ DOS_CODE(ERRDOS, ERRremcd),
+ DOS_CODE(ERRDOS, ERRdiffdevice),
+ DOS_CODE(ERRDOS, ERRnofiles),
+ DOS_CODE(ERRDOS, ERRgeneral),
+ DOS_CODE(ERRDOS, ERRbadshare),
+ DOS_CODE(ERRDOS, ERRlock),
+ DOS_CODE(ERRDOS, ERRunsup),
+ DOS_CODE(ERRDOS, ERRnetnamedel),
+ DOS_CODE(ERRDOS, ERRnosuchshare),
+ DOS_CODE(ERRDOS, ERRfilexists),
+ DOS_CODE(ERRDOS, ERRinvalidparam),
+ DOS_CODE(ERRDOS, ERRcannotopen),
+ DOS_CODE(ERRDOS, ERRinsufficientbuffer),
+ DOS_CODE(ERRDOS, ERRinvalidname),
+ DOS_CODE(ERRDOS, ERRunknownlevel),
+ DOS_CODE(ERRDOS, ERRnotlocked),
+ DOS_CODE(ERRDOS, ERRinvalidpath),
+ DOS_CODE(ERRDOS, ERRcancelviolation),
+ DOS_CODE(ERRDOS, ERRnoatomiclocks),
+ DOS_CODE(ERRDOS, ERRrename),
+ DOS_CODE(ERRDOS, ERRbadpipe),
+ DOS_CODE(ERRDOS, ERRpipebusy),
+ DOS_CODE(ERRDOS, ERRpipeclosing),
+ DOS_CODE(ERRDOS, ERRnotconnected),
+ DOS_CODE(ERRDOS, ERRmoredata),
+ DOS_CODE(ERRDOS, ERRnomoreitems),
+ DOS_CODE(ERRDOS, ERRbaddirectory),
+ DOS_CODE(ERRDOS, ERReasnotsupported),
+ DOS_CODE(ERRDOS, ERRlogonfailure),
+ DOS_CODE(ERRDOS, ERRbuftoosmall),
+ DOS_CODE(ERRDOS, ERRunknownipc),
+ DOS_CODE(ERRDOS, ERRnosuchprintjob),
+ DOS_CODE(ERRDOS, ERRinvgroup),
+ DOS_CODE(ERRDOS, ERRnoipc),
+ DOS_CODE(ERRDOS, ERRdriveralreadyinstalled),
+ DOS_CODE(ERRDOS, ERRunknownprinterport),
+ DOS_CODE(ERRDOS, ERRunknownprinterdriver),
+ DOS_CODE(ERRDOS, ERRunknownprintprocessor),
+ DOS_CODE(ERRDOS, ERRinvalidseparatorfile),
+ DOS_CODE(ERRDOS, ERRinvalidjobpriority),
+ DOS_CODE(ERRDOS, ERRinvalidprintername),
+ DOS_CODE(ERRDOS, ERRprinteralreadyexists),
+ DOS_CODE(ERRDOS, ERRinvalidprintercommand),
+ DOS_CODE(ERRDOS, ERRinvaliddatatype),
+ DOS_CODE(ERRDOS, ERRinvalidenvironment),
+ DOS_CODE(ERRDOS, ERRunknownprintmonitor),
+ DOS_CODE(ERRDOS, ERRprinterdriverinuse),
+ DOS_CODE(ERRDOS, ERRspoolfilenotfound),
+ DOS_CODE(ERRDOS, ERRnostartdoc),
+ DOS_CODE(ERRDOS, ERRnoaddjob),
+ DOS_CODE(ERRDOS, ERRprintprocessoralreadyinstalled),
+ DOS_CODE(ERRDOS, ERRprintmonitoralreadyinstalled),
+ DOS_CODE(ERRDOS, ERRinvalidprintmonitor),
+ DOS_CODE(ERRDOS, ERRprintmonitorinuse),
+ DOS_CODE(ERRDOS, ERRprinterhasjobsqueued),
+ DOS_CODE(ERRDOS, ERReainconsistent),
+
+ DOS_CODE(ERRSRV, ERRerror),
+ DOS_CODE(ERRSRV, ERRbadpw),
+ DOS_CODE(ERRSRV, ERRbadtype),
+ DOS_CODE(ERRSRV, ERRaccess),
+ DOS_CODE(ERRSRV, ERRinvnid),
+ DOS_CODE(ERRSRV, ERRinvnetname),
+ DOS_CODE(ERRSRV, ERRinvdevice),
+ DOS_CODE(ERRSRV, ERRqfull),
+ DOS_CODE(ERRSRV, ERRqtoobig),
+ DOS_CODE(ERRSRV, ERRinvpfid),
+ DOS_CODE(ERRSRV, ERRsmbcmd),
+ DOS_CODE(ERRSRV, ERRsrverror),
+ DOS_CODE(ERRSRV, ERRfilespecs),
+ DOS_CODE(ERRSRV, ERRbadlink),
+ DOS_CODE(ERRSRV, ERRbadpermits),
+ DOS_CODE(ERRSRV, ERRbadpid),
+ DOS_CODE(ERRSRV, ERRsetattrmode),
+ DOS_CODE(ERRSRV, ERRpaused),
+ DOS_CODE(ERRSRV, ERRmsgoff),
+ DOS_CODE(ERRSRV, ERRnoroom),
+ DOS_CODE(ERRSRV, ERRrmuns),
+ DOS_CODE(ERRSRV, ERRtimeout),
+ DOS_CODE(ERRSRV, ERRnoresource),
+ DOS_CODE(ERRSRV, ERRtoomanyuids),
+ DOS_CODE(ERRSRV, ERRbaduid),
+ DOS_CODE(ERRSRV, ERRuseMPX),
+ DOS_CODE(ERRSRV, ERRuseSTD),
+ DOS_CODE(ERRSRV, ERRcontMPX),
+ DOS_CODE(ERRSRV, ERRnosupport),
+ DOS_CODE(ERRSRV, ERRunknownsmb),
+
+ DOS_CODE(ERRHRD, ERRnowrite),
+ DOS_CODE(ERRHRD, ERRbadunit),
+ DOS_CODE(ERRHRD, ERRnotready),
+ DOS_CODE(ERRHRD, ERRbadcmd),
+ DOS_CODE(ERRHRD, ERRdata),
+ DOS_CODE(ERRHRD, ERRbadreq),
+ DOS_CODE(ERRHRD, ERRseek),
+ DOS_CODE(ERRHRD, ERRbadmedia),
+ DOS_CODE(ERRHRD, ERRbadsector),
+ DOS_CODE(ERRHRD, ERRnopaper),
+ DOS_CODE(ERRHRD, ERRwrite),
+ DOS_CODE(ERRHRD, ERRread),
+ DOS_CODE(ERRHRD, ERRgeneral),
+ DOS_CODE(ERRHRD, ERRwrongdisk),
+ DOS_CODE(ERRHRD, ERRFCBunavail),
+ DOS_CODE(ERRHRD, ERRsharebufexc),
+ DOS_CODE(ERRHRD, ERRdiskfull),
+
+ LDAP_CODE(LDAP_SUCCESS),
+ LDAP_CODE(LDAP_OPERATIONS_ERROR),
+ LDAP_CODE(LDAP_PROTOCOL_ERROR),
+ LDAP_CODE(LDAP_TIME_LIMIT_EXCEEDED),
+ LDAP_CODE(LDAP_SIZE_LIMIT_EXCEEDED),
+ LDAP_CODE(LDAP_COMPARE_FALSE),
+ LDAP_CODE(LDAP_COMPARE_TRUE),
+ LDAP_CODE(LDAP_AUTH_METHOD_NOT_SUPPORTED),
+ LDAP_CODE(LDAP_STRONG_AUTH_REQUIRED),
+ LDAP_CODE(LDAP_REFERRAL),
+ LDAP_CODE(LDAP_ADMIN_LIMIT_EXCEEDED),
+ LDAP_CODE(LDAP_UNAVAILABLE_CRITICAL_EXTENSION),
+ LDAP_CODE(LDAP_CONFIDENTIALITY_REQUIRED),
+ LDAP_CODE(LDAP_SASL_BIND_IN_PROGRESS),
+ LDAP_CODE(LDAP_NO_SUCH_ATTRIBUTE),
+ LDAP_CODE(LDAP_UNDEFINED_ATTRIBUTE_TYPE),
+ LDAP_CODE(LDAP_INAPPROPRIATE_MATCHING),
+ LDAP_CODE(LDAP_CONSTRAINT_VIOLATION),
+ LDAP_CODE(LDAP_ATTRIBUTE_OR_VALUE_EXISTS),
+ LDAP_CODE(LDAP_INVALID_ATTRIBUTE_SYNTAX),
+ LDAP_CODE(LDAP_NO_SUCH_OBJECT),
+ LDAP_CODE(LDAP_ALIAS_PROBLEM),
+ LDAP_CODE(LDAP_INVALID_DN_SYNTAX),
+ LDAP_CODE(LDAP_ALIAS_DEREFERENCING_PROBLEM),
+ LDAP_CODE(LDAP_INAPPROPRIATE_AUTHENTICATION),
+ LDAP_CODE(LDAP_INVALID_CREDENTIALS),
+ LDAP_CODE(LDAP_INSUFFICIENT_ACCESS_RIGHTS),
+ LDAP_CODE(LDAP_BUSY),
+ LDAP_CODE(LDAP_UNAVAILABLE),
+ LDAP_CODE(LDAP_UNWILLING_TO_PERFORM),
+ LDAP_CODE(LDAP_LOOP_DETECT),
+ LDAP_CODE(LDAP_NAMING_VIOLATION),
+ LDAP_CODE(LDAP_OBJECT_CLASS_VIOLATION),
+ LDAP_CODE(LDAP_NOT_ALLOWED_ON_NON_LEAF),
+ LDAP_CODE(LDAP_NOT_ALLOWED_ON_RDN),
+ LDAP_CODE(LDAP_ENTRY_ALREADY_EXISTS),
+ LDAP_CODE(LDAP_OBJECT_CLASS_MODS_PROHIBITED),
+ LDAP_CODE(LDAP_AFFECTS_MULTIPLE_DSAS),
+ LDAP_CODE(LDAP_OTHER),
+
+ { NULL, NT_STATUS(0) }
+};
+
+static const nt_err_code_struct nt_err_desc[] =
+{
+ { "Success", NT_STATUS_OK },
+ { "Undetermined error", NT_STATUS_UNSUCCESSFUL },
+ { "Access denied", NT_STATUS_ACCESS_DENIED },
+ { "Account locked out", NT_STATUS_ACCOUNT_LOCKED_OUT },
+ { "Must change password", NT_STATUS_PASSWORD_MUST_CHANGE },
+ { "Password is too short", NT_STATUS_PWD_TOO_SHORT },
+ { "Password is too recent", NT_STATUS_PWD_TOO_RECENT },
+ { "Password history conflict", NT_STATUS_PWD_HISTORY_CONFLICT },
+ { "No logon servers", NT_STATUS_NO_LOGON_SERVERS },
+ { "Improperly formed account name", NT_STATUS_INVALID_ACCOUNT_NAME },
+ { "User exists", NT_STATUS_USER_EXISTS },
+ { "No such user", NT_STATUS_NO_SUCH_USER },
+ { "Group exists", NT_STATUS_GROUP_EXISTS },
+ { "No such group", NT_STATUS_NO_SUCH_GROUP },
+ { "Member not in group", NT_STATUS_MEMBER_NOT_IN_GROUP },
+ { "Wrong Password", NT_STATUS_WRONG_PASSWORD },
+ { "Ill formed password", NT_STATUS_ILL_FORMED_PASSWORD },
+ { "Password restriction", NT_STATUS_PASSWORD_RESTRICTION },
+ { "Logon failure", NT_STATUS_LOGON_FAILURE },
+ { "Account restriction", NT_STATUS_ACCOUNT_RESTRICTION },
+ { "Invalid logon hours", NT_STATUS_INVALID_LOGON_HOURS },
+ { "Invalid workstation", NT_STATUS_INVALID_WORKSTATION },
+ { "Password expired", NT_STATUS_PASSWORD_EXPIRED },
+ { "Account disabled", NT_STATUS_ACCOUNT_DISABLED },
+ { "Unexpected information received", NT_STATUS_INVALID_PARAMETER },
+ { "Memory allocation error", NT_STATUS_NO_MEMORY },
+ { "No domain controllers located", NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND },
+ { "Account locked out", NT_STATUS_ACCOUNT_LOCKED_OUT },
+ { "Named pipe not available", NT_STATUS_PIPE_NOT_AVAILABLE },
+ { "Not implemented", NT_STATUS_NOT_IMPLEMENTED },
+ { "Invalid information class", NT_STATUS_INVALID_INFO_CLASS },
+ { "Information length mismatch", NT_STATUS_INFO_LENGTH_MISMATCH },
+ { "Access violation", NT_STATUS_ACCESS_VIOLATION },
+ { "Invalid handle", NT_STATUS_INVALID_HANDLE },
+ { "Invalid parameter", NT_STATUS_INVALID_PARAMETER },
+ { "No memory", NT_STATUS_NO_MEMORY },
+ { "Buffer too small", NT_STATUS_BUFFER_TOO_SMALL },
+ { "Revision mismatch", NT_STATUS_REVISION_MISMATCH },
+ { "No logon servers", NT_STATUS_NO_LOGON_SERVERS },
+ { "No such logon session", NT_STATUS_NO_SUCH_LOGON_SESSION },
+ { "No such privilege", NT_STATUS_NO_SUCH_PRIVILEGE },
+ { "Procedure not found", NT_STATUS_PROCEDURE_NOT_FOUND },
+ { "Server disabled", NT_STATUS_SERVER_DISABLED },
+ { "Invalid pipe state", NT_STATUS_INVALID_PIPE_STATE },
+ { "Named pipe busy", NT_STATUS_PIPE_BUSY },
+ { "Illegal function", NT_STATUS_ILLEGAL_FUNCTION },
+ { "Named pipe dicconnected", NT_STATUS_PIPE_DISCONNECTED },
+ { "Named pipe closing", NT_STATUS_PIPE_CLOSING },
+ { "Remote host not listening", NT_STATUS_REMOTE_NOT_LISTENING },
+ { "Duplicate name on network", NT_STATUS_DUPLICATE_NAME },
+ { "Print queue is full", NT_STATUS_PRINT_QUEUE_FULL },
+ { "No print spool space available", NT_STATUS_NO_SPOOL_SPACE },
+ { "Too many names", NT_STATUS_TOO_MANY_NAMES },
+ { "Too many sessions", NT_STATUS_TOO_MANY_SESSIONS },
+ { "Invalid server state", NT_STATUS_INVALID_SERVER_STATE },
+ { "Invalid domain state", NT_STATUS_INVALID_DOMAIN_STATE },
+ { "Invalid domain role", NT_STATUS_INVALID_DOMAIN_ROLE },
+ { "No such domain", NT_STATUS_NO_SUCH_DOMAIN },
+ { "Domain exists", NT_STATUS_DOMAIN_EXISTS },
+ { "Domain limit exceeded", NT_STATUS_DOMAIN_LIMIT_EXCEEDED },
+ { "Bad logon session state", NT_STATUS_BAD_LOGON_SESSION_STATE },
+ { "Logon session collision", NT_STATUS_LOGON_SESSION_COLLISION },
+ { "Invalid logon type", NT_STATUS_INVALID_LOGON_TYPE },
+ { "Cancelled", NT_STATUS_CANCELLED },
+ { "Invalid computer name", NT_STATUS_INVALID_COMPUTER_NAME },
+ { "Logon server conflict", NT_STATUS_LOGON_SERVER_CONFLICT },
+ { "Time difference at domain controller", NT_STATUS_TIME_DIFFERENCE_AT_DC },
+ { "Pipe broken", NT_STATUS_PIPE_BROKEN },
+ { "Registry corrupt", NT_STATUS_REGISTRY_CORRUPT },
+ { "Too many secrets", NT_STATUS_TOO_MANY_SECRETS },
+ { "Too many SIDs", NT_STATUS_TOO_MANY_SIDS },
+ { "Lanmanager cross encryption required", NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED },
+ { "Log file full", NT_STATUS_LOG_FILE_FULL },
+ { "No trusted LSA secret", NT_STATUS_NO_TRUST_LSA_SECRET },
+ { "No trusted SAM account", NT_STATUS_NO_TRUST_SAM_ACCOUNT },
+ { "Trusted domain failure", NT_STATUS_TRUSTED_DOMAIN_FAILURE },
+ { "Trust relationship failure", NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE },
+ { "Trust failure", NT_STATUS_TRUST_FAILURE },
+ { "Netlogon service not started", NT_STATUS_NETLOGON_NOT_STARTED },
+ { "Account expired", NT_STATUS_ACCOUNT_EXPIRED },
+ { "Network credential conflict", NT_STATUS_NETWORK_CREDENTIAL_CONFLICT },
+ { "Remote session limit", NT_STATUS_REMOTE_SESSION_LIMIT },
+ { "No logon interdomain trust account", NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT },
+ { "No logon workstation trust account", NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT },
+ { "No logon server trust account", NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT },
+ { "Domain trust inconsistent", NT_STATUS_DOMAIN_TRUST_INCONSISTENT },
+ { "No user session key available", NT_STATUS_NO_USER_SESSION_KEY },
+ { "User session deleted", NT_STATUS_USER_SESSION_DELETED },
+ { "Insufficient server resources", NT_STATUS_INSUFF_SERVER_RESOURCES },
+ { "Insufficient logon information", NT_STATUS_INSUFFICIENT_LOGON_INFO },
+
+ { "License quota exceeded", NT_STATUS_LICENSE_QUOTA_EXCEEDED },
+
+ { NULL, NT_STATUS(0) }
+};
+
+/*****************************************************************************
+ returns an NT error message. not amazingly helpful, but better than a number.
+ *****************************************************************************/
+const char *nt_errstr(NTSTATUS nt_code)
+{
+ static char msg[40];
+ int idx = 0;
+
+ while (nt_errs[idx].nt_errstr != NULL) {
+ if (NT_STATUS_V(nt_errs[idx].nt_errcode) ==
+ NT_STATUS_V(nt_code)) {
+ return nt_errs[idx].nt_errstr;
+ }
+ idx++;
+ }
+
+ if (NT_STATUS_IS_LDAP(nt_code)) {
+ slprintf(msg, sizeof(msg), "LDAP code %u", NT_STATUS_LDAP_CODE(nt_code));
+ return msg;
+ }
+
+ slprintf(msg, sizeof(msg), "NT code 0x%08x", NT_STATUS_V(nt_code));
+
+ return msg;
+}
+
+/************************************************************************
+ Print friendler version fo NT error code
+ ***********************************************************************/
+const char *get_friendly_nt_error_msg(NTSTATUS nt_code)
+{
+ int idx = 0;
+
+ while (nt_err_desc[idx].nt_errstr != NULL) {
+ if (NT_STATUS_V(nt_err_desc[idx].nt_errcode) == NT_STATUS_V(nt_code)) {
+ return nt_err_desc[idx].nt_errstr;
+ }
+ idx++;
+ }
+
+ /* fall back to NT_STATUS_XXX string */
+ return nt_errstr(nt_code);
+}
+
+/*****************************************************************************
+ returns an NT_STATUS constant as a string for inclusion in autogen C code
+ *****************************************************************************/
+const char *get_nt_error_c_code(NTSTATUS nt_code)
+{
+ static char out[40];
+ int idx = 0;
+
+ while (nt_errs[idx].nt_errstr != NULL) {
+ if (NT_STATUS_V(nt_errs[idx].nt_errcode) ==
+ NT_STATUS_V(nt_code)) {
+ return nt_errs[idx].nt_errstr;
+ }
+ idx++;
+ }
+
+ slprintf(out, sizeof(out), "NT_STATUS(0x%08x)", NT_STATUS_V(nt_code));
+
+ return out;
+}
+
+/*****************************************************************************
+ returns the NT_STATUS constant matching the string supplied (as an NTSTATUS)
+ *****************************************************************************/
+NTSTATUS nt_status_string_to_code(const char *nt_status_str)
+{
+ int idx = 0;
+
+ while (nt_errs[idx].nt_errstr != NULL) {
+ if (strcasecmp(nt_errs[idx].nt_errstr, nt_status_str) == 0) {
+ return nt_errs[idx].nt_errcode;
+ }
+ idx++;
+ }
+ return NT_STATUS_UNSUCCESSFUL;
+}
diff --git a/source4/libcli/util/pyerrors.h b/source4/libcli/util/pyerrors.h
new file mode 100644
index 0000000000..66823f4a41
--- /dev/null
+++ b/source4/libcli/util/pyerrors.h
@@ -0,0 +1,45 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Jelmer Vernooij <jelmer@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __PYERRORS_H__
+#define __PYERRORS_H__
+
+#define PyErr_FromWERROR(err) Py_BuildValue("(i,s)", W_ERROR_V(err), discard_const_p(char, win_errstr(err)))
+
+#define PyErr_FromNTSTATUS(status) Py_BuildValue("(i,s)", NT_STATUS_V(status), discard_const_p(char, get_friendly_nt_error_msg(status)))
+
+#define PyErr_SetWERROR(err) \
+ PyErr_SetObject(PyExc_RuntimeError, PyErr_FromWERROR(err))
+
+#define PyErr_SetNTSTATUS(status) \
+ PyErr_SetObject(PyExc_RuntimeError, PyErr_FromNTSTATUS(status))
+
+#define PyErr_NTSTATUS_IS_ERR_RAISE(status) \
+ if (NT_STATUS_IS_ERR(status)) { \
+ PyErr_SetNTSTATUS(status); \
+ return NULL; \
+ }
+
+#define PyErr_WERROR_IS_ERR_RAISE(status) \
+ if (!W_ERROR_IS_OK(status)) { \
+ PyErr_SetWERROR(status); \
+ return NULL; \
+ }
+
+#endif /* __PYERRORS_H__ */
diff --git a/source4/libcli/wbclient/config.mk b/source4/libcli/wbclient/config.mk
new file mode 100644
index 0000000000..00df5dbb22
--- /dev/null
+++ b/source4/libcli/wbclient/config.mk
@@ -0,0 +1,5 @@
+[SUBSYSTEM::LIBWBCLIENT]
+PUBLIC_DEPENDENCIES = LIBSAMBA-ERRORS LIBEVENTS
+PRIVATE_DEPENDENCIES = NDR_WINBIND MESSAGING
+
+LIBWBCLIENT_OBJ_FILES = $(libclisrcdir)/wbclient/wbclient.o
diff --git a/source4/libcli/wbclient/wbclient.c b/source4/libcli/wbclient/wbclient.c
new file mode 100644
index 0000000000..da7d678da9
--- /dev/null
+++ b/source4/libcli/wbclient/wbclient.c
@@ -0,0 +1,210 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind client library.
+
+ Copyright (C) 2008 Kai Blin <kai@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/wbclient/wbclient.h"
+
+/**
+ * Get the server_id of the winbind task.
+ *
+ * \param[in] msg_ctx message context to use
+ * \param[in] mem_ctx talloc context to use
+ * \param[out] ids array of server_id structs containing the winbind id
+ * \return NT_STATUS_OK on success, NT_STATUS_INTERNAL_ERROR on failure
+ */
+static NTSTATUS get_server_id(struct messaging_context *msg_ctx,
+ TALLOC_CTX *mem_ctx, struct server_id **ids)
+{
+ *ids = irpc_servers_byname(msg_ctx, mem_ctx, "winbind_server");
+ if (*ids == NULL || (*ids)[0].id == 0) {
+ DEBUG(0, ("Geting the winbind server ID failed.\n"));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ return NT_STATUS_OK;
+}
+
+/**
+ * Initialize the wbclient context, talloc_free() when done.
+ *
+ * \param mem_ctx talloc context to allocate memory from
+ * \param msg_ctx message context to use
+ * \param
+ */
+struct wbc_context *wbc_init(TALLOC_CTX *mem_ctx,
+ struct messaging_context *msg_ctx,
+ struct tevent_context *event_ctx)
+{
+ struct wbc_context *ctx;
+ NTSTATUS status;
+
+ ctx = talloc(mem_ctx, struct wbc_context);
+ if (ctx == NULL) return NULL;
+
+ status = get_server_id(msg_ctx, mem_ctx, &ctx->ids);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(ctx);
+ return NULL;
+ }
+
+ ctx->msg_ctx = msg_ctx;
+ ctx->event_ctx = event_ctx;
+
+ return ctx;
+}
+
+struct wbc_idmap_state {
+ struct composite_context *ctx;
+ struct winbind_get_idmap *req;
+ struct irpc_request *irpc_req;
+ struct id_mapping *ids;
+};
+
+static void sids_to_xids_recv_ids(struct irpc_request *req);
+
+struct composite_context *wbc_sids_to_xids_send(struct wbc_context *wbc_ctx,
+ TALLOC_CTX *mem_ctx,
+ uint32_t count,
+ struct id_mapping *ids)
+{
+ struct composite_context *ctx;
+ struct wbc_idmap_state *state;
+
+ DEBUG(5, ("wbc_sids_to_xids called\n"));
+
+ ctx = composite_create(mem_ctx, wbc_ctx->event_ctx);
+ if (ctx == NULL) return NULL;
+
+ state = talloc(ctx, struct wbc_idmap_state);
+ if (composite_nomem(state, ctx)) return ctx;
+ ctx->private_data = state;
+
+ state->req = talloc(state, struct winbind_get_idmap);
+ if (composite_nomem(state->req, ctx)) return ctx;
+
+ state->req->in.count = count;
+ state->req->in.level = WINBIND_IDMAP_LEVEL_SIDS_TO_XIDS;
+ state->req->in.ids = ids;
+ state->ctx = ctx;
+
+ state->irpc_req = IRPC_CALL_SEND(wbc_ctx->msg_ctx, wbc_ctx->ids[0],
+ winbind, WINBIND_GET_IDMAP, state->req,
+ state);
+ if (composite_nomem(state->irpc_req, ctx)) return ctx;
+
+ composite_continue_irpc(ctx, state->irpc_req, sids_to_xids_recv_ids,
+ state);
+ return ctx;
+}
+
+static void sids_to_xids_recv_ids(struct irpc_request *req)
+{
+ struct wbc_idmap_state *state = talloc_get_type_abort(
+ req->async.private_data,
+ struct wbc_idmap_state);
+
+ state->ctx->status = irpc_call_recv(state->irpc_req);
+ if (!composite_is_ok(state->ctx)) return;
+
+ state->ids = state->req->out.ids;
+ composite_done(state->ctx);
+}
+
+NTSTATUS wbc_sids_to_xids_recv(struct composite_context *ctx,
+ struct id_mapping **ids)
+{
+ NTSTATUS status = composite_wait(ctx);
+ DEBUG(5, ("wbc_sids_to_xids_recv called\n"));
+ if (NT_STATUS_IS_OK(status)) {
+ struct wbc_idmap_state *state = talloc_get_type_abort(
+ ctx->private_data,
+ struct wbc_idmap_state);
+ *ids = state->ids;
+ }
+
+ return status;
+}
+
+static void xids_to_sids_recv_ids(struct irpc_request *req);
+
+struct composite_context *wbc_xids_to_sids_send(struct wbc_context *wbc_ctx,
+ TALLOC_CTX *mem_ctx,
+ uint32_t count,
+ struct id_mapping *ids)
+{
+ struct composite_context *ctx;
+ struct wbc_idmap_state *state;
+
+ DEBUG(5, ("wbc_xids_to_sids called\n"));
+
+ ctx = composite_create(mem_ctx, wbc_ctx->event_ctx);
+ if (ctx == NULL) return NULL;
+
+ state = talloc(ctx, struct wbc_idmap_state);
+ if (composite_nomem(state, ctx)) return ctx;
+ ctx->private_data = state;
+
+ state->req = talloc(state, struct winbind_get_idmap);
+ if (composite_nomem(state->req, ctx)) return ctx;
+
+ state->req->in.count = count;
+ state->req->in.level = WINBIND_IDMAP_LEVEL_XIDS_TO_SIDS;
+ state->req->in.ids = ids;
+ state->ctx = ctx;
+
+ state->irpc_req = IRPC_CALL_SEND(wbc_ctx->msg_ctx, wbc_ctx->ids[0],
+ winbind, WINBIND_GET_IDMAP, state->req,
+ state);
+ if (composite_nomem(state->irpc_req, ctx)) return ctx;
+
+ composite_continue_irpc(ctx, state->irpc_req, xids_to_sids_recv_ids,
+ state);
+
+ return ctx;
+}
+
+static void xids_to_sids_recv_ids(struct irpc_request *req)
+{
+ struct wbc_idmap_state *state = talloc_get_type_abort(
+ req->async.private_data,
+ struct wbc_idmap_state);
+
+ state->ctx->status = irpc_call_recv(state->irpc_req);
+ if (!composite_is_ok(state->ctx)) return;
+
+ state->ids = state->req->out.ids;
+ composite_done(state->ctx);
+}
+
+NTSTATUS wbc_xids_to_sids_recv(struct composite_context *ctx,
+ struct id_mapping **ids)
+{
+ NTSTATUS status = composite_wait(ctx);
+ DEBUG(5, ("wbc_xids_to_sids_recv called\n"));
+ if (NT_STATUS_IS_OK(status)) {
+ struct wbc_idmap_state *state = talloc_get_type_abort(
+ ctx->private_data,
+ struct wbc_idmap_state);
+ *ids = state->ids;
+ }
+
+ return status;
+}
+
diff --git a/source4/libcli/wbclient/wbclient.h b/source4/libcli/wbclient/wbclient.h
new file mode 100644
index 0000000000..416dc78324
--- /dev/null
+++ b/source4/libcli/wbclient/wbclient.h
@@ -0,0 +1,50 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind client library.
+
+ Copyright (C) 2008 Kai Blin <kai@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "lib/messaging/irpc.h"
+#include "libcli/composite/composite.h"
+#include "librpc/gen_ndr/ndr_winbind.h"
+
+struct wbc_context {
+ struct messaging_context *msg_ctx;
+ struct tevent_context *event_ctx;
+ struct server_id *ids;
+};
+
+struct wbc_context *wbc_init(TALLOC_CTX *mem_ctx,
+ struct messaging_context *msg_ctx,
+ struct tevent_context *event_ctx);
+
+struct composite_context *wbc_sids_to_xids_send(struct wbc_context *wbc_ctx,
+ TALLOC_CTX *mem_ctx,
+ uint32_t count,
+ struct id_mapping *ids);
+
+NTSTATUS wbc_sids_to_xids_recv(struct composite_context *ctx,
+ struct id_mapping **ids);
+
+struct composite_context *wbc_xids_to_sids_send(struct wbc_context *wbc_ctx,
+ TALLOC_CTX *mem_ctx,
+ uint32_t count,
+ struct id_mapping *ids);
+
+NTSTATUS wbc_xids_to_sids_recv(struct composite_context *ctx,
+ struct id_mapping **ids);
+
diff --git a/source4/libcli/wrepl/winsrepl.c b/source4/libcli/wrepl/winsrepl.c
new file mode 100644
index 0000000000..48a6abba9d
--- /dev/null
+++ b/source4/libcli/wrepl/winsrepl.c
@@ -0,0 +1,871 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ low level WINS replication client code
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/events/events.h"
+#include "../lib/util/dlinklist.h"
+#include "lib/socket/socket.h"
+#include "libcli/wrepl/winsrepl.h"
+#include "librpc/gen_ndr/ndr_winsrepl.h"
+#include "lib/stream/packet.h"
+#include "libcli/composite/composite.h"
+#include "system/network.h"
+#include "lib/socket/netif.h"
+#include "param/param.h"
+
+static struct wrepl_request *wrepl_request_finished(struct wrepl_request *req, NTSTATUS status);
+
+/*
+ mark all pending requests as dead - called when a socket error happens
+*/
+static void wrepl_socket_dead(struct wrepl_socket *wrepl_socket, NTSTATUS status)
+{
+ wrepl_socket->dead = true;
+
+ if (wrepl_socket->packet) {
+ packet_recv_disable(wrepl_socket->packet);
+ packet_set_fde(wrepl_socket->packet, NULL);
+ packet_set_socket(wrepl_socket->packet, NULL);
+ }
+
+ if (wrepl_socket->event.fde) {
+ talloc_free(wrepl_socket->event.fde);
+ wrepl_socket->event.fde = NULL;
+ }
+
+ if (wrepl_socket->sock) {
+ talloc_free(wrepl_socket->sock);
+ wrepl_socket->sock = NULL;
+ }
+
+ if (NT_STATUS_EQUAL(NT_STATUS_UNSUCCESSFUL, status)) {
+ status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+ while (wrepl_socket->recv_queue) {
+ struct wrepl_request *req = wrepl_socket->recv_queue;
+ DLIST_REMOVE(wrepl_socket->recv_queue, req);
+ wrepl_request_finished(req, status);
+ }
+
+ talloc_set_destructor(wrepl_socket, NULL);
+ if (wrepl_socket->free_skipped) {
+ talloc_free(wrepl_socket);
+ }
+}
+
+static void wrepl_request_timeout_handler(struct tevent_context *ev, struct tevent_timer *te,
+ struct timeval t, void *ptr)
+{
+ struct wrepl_request *req = talloc_get_type(ptr, struct wrepl_request);
+ wrepl_socket_dead(req->wrepl_socket, NT_STATUS_IO_TIMEOUT);
+}
+
+/*
+ handle recv events
+*/
+static NTSTATUS wrepl_finish_recv(void *private_data, DATA_BLOB packet_blob_in)
+{
+ struct wrepl_socket *wrepl_socket = talloc_get_type(private_data, struct wrepl_socket);
+ struct wrepl_request *req = wrepl_socket->recv_queue;
+ DATA_BLOB blob;
+ enum ndr_err_code ndr_err;
+
+ if (!req) {
+ DEBUG(1,("Received unexpected WINS packet of length %u!\n",
+ (unsigned)packet_blob_in.length));
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ req->packet = talloc(req, struct wrepl_packet);
+ NT_STATUS_HAVE_NO_MEMORY(req->packet);
+
+ blob.data = packet_blob_in.data + 4;
+ blob.length = packet_blob_in.length - 4;
+
+ /* we have a full request - parse it */
+ ndr_err = ndr_pull_struct_blob(&blob, req->packet, wrepl_socket->iconv_convenience, req->packet,
+ (ndr_pull_flags_fn_t)ndr_pull_wrepl_packet);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
+ wrepl_request_finished(req, status);
+ return NT_STATUS_OK;
+ }
+
+ if (DEBUGLVL(10)) {
+ DEBUG(10,("Received WINS packet of length %u\n",
+ (unsigned)packet_blob_in.length));
+ NDR_PRINT_DEBUG(wrepl_packet, req->packet);
+ }
+
+ wrepl_request_finished(req, NT_STATUS_OK);
+ return NT_STATUS_OK;
+}
+
+/*
+ handler for winrepl events
+*/
+static void wrepl_handler(struct tevent_context *ev, struct tevent_fd *fde,
+ uint16_t flags, void *private_data)
+{
+ struct wrepl_socket *wrepl_socket = talloc_get_type(private_data,
+ struct wrepl_socket);
+ if (flags & EVENT_FD_READ) {
+ packet_recv(wrepl_socket->packet);
+ return;
+ }
+ if (flags & EVENT_FD_WRITE) {
+ packet_queue_run(wrepl_socket->packet);
+ }
+}
+
+static void wrepl_error(void *private_data, NTSTATUS status)
+{
+ struct wrepl_socket *wrepl_socket = talloc_get_type(private_data,
+ struct wrepl_socket);
+ wrepl_socket_dead(wrepl_socket, status);
+}
+
+
+/*
+ destroy a wrepl_socket destructor
+*/
+static int wrepl_socket_destructor(struct wrepl_socket *sock)
+{
+ if (sock->dead) {
+ sock->free_skipped = true;
+ return -1;
+ }
+ wrepl_socket_dead(sock, NT_STATUS_LOCAL_DISCONNECT);
+ return 0;
+}
+
+/*
+ initialise a wrepl_socket. The event_ctx is optional, if provided then
+ operations will use that event context
+*/
+struct wrepl_socket *wrepl_socket_init(TALLOC_CTX *mem_ctx,
+ struct tevent_context *event_ctx,
+ struct smb_iconv_convenience *iconv_convenience)
+{
+ struct wrepl_socket *wrepl_socket;
+ NTSTATUS status;
+
+ wrepl_socket = talloc_zero(mem_ctx, struct wrepl_socket);
+ if (!wrepl_socket) return NULL;
+
+ wrepl_socket->event.ctx = talloc_reference(wrepl_socket, event_ctx);
+ if (!wrepl_socket->event.ctx) goto failed;
+
+ wrepl_socket->iconv_convenience = iconv_convenience;
+
+ status = socket_create("ip", SOCKET_TYPE_STREAM, &wrepl_socket->sock, 0);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+
+ talloc_steal(wrepl_socket, wrepl_socket->sock);
+
+ wrepl_socket->request_timeout = WREPL_SOCKET_REQUEST_TIMEOUT;
+
+ talloc_set_destructor(wrepl_socket, wrepl_socket_destructor);
+
+ return wrepl_socket;
+
+failed:
+ talloc_free(wrepl_socket);
+ return NULL;
+}
+
+/*
+ initialise a wrepl_socket from an already existing connection
+*/
+struct wrepl_socket *wrepl_socket_merge(TALLOC_CTX *mem_ctx,
+ struct tevent_context *event_ctx,
+ struct socket_context *sock,
+ struct packet_context *pack)
+{
+ struct wrepl_socket *wrepl_socket;
+
+ wrepl_socket = talloc_zero(mem_ctx, struct wrepl_socket);
+ if (wrepl_socket == NULL) goto failed;
+
+ wrepl_socket->event.ctx = talloc_reference(wrepl_socket, event_ctx);
+ if (wrepl_socket->event.ctx == NULL) goto failed;
+
+ wrepl_socket->sock = sock;
+ talloc_steal(wrepl_socket, wrepl_socket->sock);
+
+
+ wrepl_socket->request_timeout = WREPL_SOCKET_REQUEST_TIMEOUT;
+
+ wrepl_socket->event.fde = event_add_fd(wrepl_socket->event.ctx, wrepl_socket,
+ socket_get_fd(wrepl_socket->sock),
+ EVENT_FD_READ,
+ wrepl_handler, wrepl_socket);
+ if (wrepl_socket->event.fde == NULL) {
+ goto failed;
+ }
+
+ wrepl_socket->packet = pack;
+ talloc_steal(wrepl_socket, wrepl_socket->packet);
+ packet_set_private(wrepl_socket->packet, wrepl_socket);
+ packet_set_socket(wrepl_socket->packet, wrepl_socket->sock);
+ packet_set_callback(wrepl_socket->packet, wrepl_finish_recv);
+ packet_set_full_request(wrepl_socket->packet, packet_full_request_u32);
+ packet_set_error_handler(wrepl_socket->packet, wrepl_error);
+ packet_set_event_context(wrepl_socket->packet, wrepl_socket->event.ctx);
+ packet_set_fde(wrepl_socket->packet, wrepl_socket->event.fde);
+ packet_set_serialise(wrepl_socket->packet);
+
+ talloc_set_destructor(wrepl_socket, wrepl_socket_destructor);
+
+ return wrepl_socket;
+
+failed:
+ talloc_free(wrepl_socket);
+ return NULL;
+}
+
+/*
+ destroy a wrepl_request
+*/
+static int wrepl_request_destructor(struct wrepl_request *req)
+{
+ if (req->state == WREPL_REQUEST_RECV) {
+ DLIST_REMOVE(req->wrepl_socket->recv_queue, req);
+ }
+ req->state = WREPL_REQUEST_ERROR;
+ return 0;
+}
+
+/*
+ wait for a request to complete
+*/
+static NTSTATUS wrepl_request_wait(struct wrepl_request *req)
+{
+ NT_STATUS_HAVE_NO_MEMORY(req);
+ while (req->state < WREPL_REQUEST_DONE) {
+ event_loop_once(req->wrepl_socket->event.ctx);
+ }
+ return req->status;
+}
+
+struct wrepl_connect_state {
+ struct composite_context *result;
+ struct wrepl_socket *wrepl_socket;
+ struct composite_context *creq;
+};
+
+/*
+ handler for winrepl connection completion
+*/
+static void wrepl_connect_handler(struct composite_context *creq)
+{
+ struct wrepl_connect_state *state = talloc_get_type(creq->async.private_data,
+ struct wrepl_connect_state);
+ struct wrepl_socket *wrepl_socket = state->wrepl_socket;
+ struct composite_context *result = state->result;
+
+ result->status = socket_connect_recv(state->creq);
+ if (!composite_is_ok(result)) return;
+
+ wrepl_socket->event.fde = event_add_fd(wrepl_socket->event.ctx, wrepl_socket,
+ socket_get_fd(wrepl_socket->sock),
+ EVENT_FD_READ,
+ wrepl_handler, wrepl_socket);
+ if (composite_nomem(wrepl_socket->event.fde, result)) return;
+
+ /* setup the stream -> packet parser */
+ wrepl_socket->packet = packet_init(wrepl_socket);
+ if (composite_nomem(wrepl_socket->packet, result)) return;
+ packet_set_private(wrepl_socket->packet, wrepl_socket);
+ packet_set_socket(wrepl_socket->packet, wrepl_socket->sock);
+ packet_set_callback(wrepl_socket->packet, wrepl_finish_recv);
+ packet_set_full_request(wrepl_socket->packet, packet_full_request_u32);
+ packet_set_error_handler(wrepl_socket->packet, wrepl_error);
+ packet_set_event_context(wrepl_socket->packet, wrepl_socket->event.ctx);
+ packet_set_fde(wrepl_socket->packet, wrepl_socket->event.fde);
+ packet_set_serialise(wrepl_socket->packet);
+
+ composite_done(result);
+}
+
+const char *wrepl_best_ip(struct loadparm_context *lp_ctx, const char *peer_ip)
+{
+ struct interface *ifaces;
+ load_interfaces(lp_ctx, lp_interfaces(lp_ctx), &ifaces);
+ return iface_best_ip(ifaces, peer_ip);
+}
+
+
+/*
+ connect a wrepl_socket to a WINS server
+*/
+struct composite_context *wrepl_connect_send(struct wrepl_socket *wrepl_socket,
+ const char *our_ip, const char *peer_ip)
+{
+ struct composite_context *result;
+ struct wrepl_connect_state *state;
+ struct socket_address *peer, *us;
+
+ result = talloc_zero(wrepl_socket, struct composite_context);
+ if (!result) return NULL;
+
+ result->state = COMPOSITE_STATE_IN_PROGRESS;
+ result->event_ctx = wrepl_socket->event.ctx;
+
+ state = talloc_zero(result, struct wrepl_connect_state);
+ if (composite_nomem(state, result)) return result;
+ result->private_data = state;
+ state->result = result;
+ state->wrepl_socket = wrepl_socket;
+
+ us = socket_address_from_strings(state, wrepl_socket->sock->backend_name,
+ our_ip, 0);
+ if (composite_nomem(us, result)) return result;
+
+ peer = socket_address_from_strings(state, wrepl_socket->sock->backend_name,
+ peer_ip, WINS_REPLICATION_PORT);
+ if (composite_nomem(peer, result)) return result;
+
+ state->creq = socket_connect_send(wrepl_socket->sock, us, peer,
+ 0, wrepl_socket->event.ctx);
+ composite_continue(result, state->creq, wrepl_connect_handler, state);
+ return result;
+}
+
+/*
+ connect a wrepl_socket to a WINS server - recv side
+*/
+NTSTATUS wrepl_connect_recv(struct composite_context *result)
+{
+ struct wrepl_connect_state *state = talloc_get_type(result->private_data,
+ struct wrepl_connect_state);
+ struct wrepl_socket *wrepl_socket = state->wrepl_socket;
+ NTSTATUS status = composite_wait(result);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ wrepl_socket_dead(wrepl_socket, status);
+ }
+
+ talloc_free(result);
+ return status;
+}
+
+/*
+ connect a wrepl_socket to a WINS server - sync API
+*/
+NTSTATUS wrepl_connect(struct wrepl_socket *wrepl_socket,
+ const char *our_ip, const char *peer_ip)
+{
+ struct composite_context *c_req = wrepl_connect_send(wrepl_socket, our_ip, peer_ip);
+ return wrepl_connect_recv(c_req);
+}
+
+/*
+ callback from wrepl_request_trigger()
+*/
+static void wrepl_request_trigger_handler(struct tevent_context *ev, struct tevent_timer *te,
+ struct timeval t, void *ptr)
+{
+ struct wrepl_request *req = talloc_get_type(ptr, struct wrepl_request);
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+}
+
+/*
+ trigger an immediate event on a wrepl_request
+ the return value should only be used in wrepl_request_send()
+ this is the only place where req->trigger is true
+*/
+static struct wrepl_request *wrepl_request_finished(struct wrepl_request *req, NTSTATUS status)
+{
+ struct tevent_timer *te;
+
+ if (req->state == WREPL_REQUEST_RECV) {
+ DLIST_REMOVE(req->wrepl_socket->recv_queue, req);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ req->state = WREPL_REQUEST_ERROR;
+ } else {
+ req->state = WREPL_REQUEST_DONE;
+ }
+
+ req->status = status;
+
+ if (req->trigger) {
+ req->trigger = false;
+ /* a zero timeout means immediate */
+ te = event_add_timed(req->wrepl_socket->event.ctx,
+ req, timeval_zero(),
+ wrepl_request_trigger_handler, req);
+ if (!te) {
+ talloc_free(req);
+ return NULL;
+ }
+ return req;
+ }
+
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+ return NULL;
+}
+
+struct wrepl_send_ctrl_state {
+ struct wrepl_send_ctrl ctrl;
+ struct wrepl_request *req;
+ struct wrepl_socket *wrepl_sock;
+};
+
+static int wrepl_send_ctrl_destructor(struct wrepl_send_ctrl_state *s)
+{
+ struct wrepl_request *req = s->wrepl_sock->recv_queue;
+
+ /* check if the request is still in WREPL_STATE_RECV,
+ * we need this here because the caller has may called
+ * talloc_free(req) and wrepl_send_ctrl_state isn't
+ * a talloc child of the request, so our s->req pointer
+ * is maybe invalid!
+ */
+ for (; req; req = req->next) {
+ if (req == s->req) break;
+ }
+ if (!req) return 0;
+
+ /* here, we need to make sure the async request handler is called
+ * later in the next event_loop and now now
+ */
+ req->trigger = true;
+ wrepl_request_finished(req, NT_STATUS_OK);
+
+ if (s->ctrl.disconnect_after_send) {
+ wrepl_socket_dead(s->wrepl_sock, NT_STATUS_LOCAL_DISCONNECT);
+ }
+
+ return 0;
+}
+
+/*
+ send a generic wins replication request
+*/
+struct wrepl_request *wrepl_request_send(struct wrepl_socket *wrepl_socket,
+ struct wrepl_packet *packet,
+ struct wrepl_send_ctrl *ctrl)
+{
+ struct wrepl_request *req;
+ struct wrepl_wrap wrap;
+ DATA_BLOB blob;
+ NTSTATUS status;
+ enum ndr_err_code ndr_err;
+
+ req = talloc_zero(wrepl_socket, struct wrepl_request);
+ if (!req) return NULL;
+ req->wrepl_socket = wrepl_socket;
+ req->state = WREPL_REQUEST_RECV;
+ req->trigger = true;
+
+ DLIST_ADD_END(wrepl_socket->recv_queue, req, struct wrepl_request *);
+ talloc_set_destructor(req, wrepl_request_destructor);
+
+ if (wrepl_socket->dead) {
+ return wrepl_request_finished(req, NT_STATUS_INVALID_CONNECTION);
+ }
+
+ wrap.packet = *packet;
+ ndr_err = ndr_push_struct_blob(&blob, req, wrepl_socket->iconv_convenience, &wrap,
+ (ndr_push_flags_fn_t)ndr_push_wrepl_wrap);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ return wrepl_request_finished(req, status);
+ }
+
+ if (DEBUGLVL(10)) {
+ DEBUG(10,("Sending WINS packet of length %u\n",
+ (unsigned)blob.length));
+ NDR_PRINT_DEBUG(wrepl_packet, &wrap.packet);
+ }
+
+ if (wrepl_socket->request_timeout > 0) {
+ req->te = event_add_timed(wrepl_socket->event.ctx, req,
+ timeval_current_ofs(wrepl_socket->request_timeout, 0),
+ wrepl_request_timeout_handler, req);
+ if (!req->te) return wrepl_request_finished(req, NT_STATUS_NO_MEMORY);
+ }
+
+ if (ctrl && (ctrl->send_only || ctrl->disconnect_after_send)) {
+ struct wrepl_send_ctrl_state *s = talloc(blob.data, struct wrepl_send_ctrl_state);
+ if (!s) return wrepl_request_finished(req, NT_STATUS_NO_MEMORY);
+ s->ctrl = *ctrl;
+ s->req = req;
+ s->wrepl_sock = wrepl_socket;
+ talloc_set_destructor(s, wrepl_send_ctrl_destructor);
+ }
+
+ status = packet_send(wrepl_socket->packet, blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ return wrepl_request_finished(req, status);
+ }
+
+ req->trigger = false;
+ return req;
+}
+
+/*
+ receive a generic WINS replication reply
+*/
+NTSTATUS wrepl_request_recv(struct wrepl_request *req,
+ TALLOC_CTX *mem_ctx,
+ struct wrepl_packet **packet)
+{
+ NTSTATUS status = wrepl_request_wait(req);
+ if (NT_STATUS_IS_OK(status) && packet) {
+ *packet = talloc_steal(mem_ctx, req->packet);
+ }
+ talloc_free(req);
+ return status;
+}
+
+/*
+ a full WINS replication request/response
+*/
+NTSTATUS wrepl_request(struct wrepl_socket *wrepl_socket,
+ TALLOC_CTX *mem_ctx,
+ struct wrepl_packet *req_packet,
+ struct wrepl_packet **reply_packet)
+{
+ struct wrepl_request *req = wrepl_request_send(wrepl_socket, req_packet, NULL);
+ return wrepl_request_recv(req, mem_ctx, reply_packet);
+}
+
+
+/*
+ setup an association - send
+*/
+struct wrepl_request *wrepl_associate_send(struct wrepl_socket *wrepl_socket,
+ struct wrepl_associate *io)
+{
+ struct wrepl_packet *packet;
+ struct wrepl_request *req;
+
+ packet = talloc_zero(wrepl_socket, struct wrepl_packet);
+ if (packet == NULL) return NULL;
+
+ packet->opcode = WREPL_OPCODE_BITS;
+ packet->mess_type = WREPL_START_ASSOCIATION;
+ packet->message.start.minor_version = 2;
+ packet->message.start.major_version = 5;
+
+ /*
+ * nt4 uses 41 bytes for the start_association call
+ * so do it the same and as we don't know th emeanings of this bytes
+ * we just send zeros and nt4, w2k and w2k3 seems to be happy with this
+ *
+ * if we don't do this nt4 uses an old version of the wins replication protocol
+ * and that would break nt4 <-> samba replication
+ */
+ packet->padding = data_blob_talloc(packet, NULL, 21);
+ if (packet->padding.data == NULL) {
+ talloc_free(packet);
+ return NULL;
+ }
+ memset(packet->padding.data, 0, packet->padding.length);
+
+ req = wrepl_request_send(wrepl_socket, packet, NULL);
+
+ talloc_free(packet);
+
+ return req;
+}
+
+/*
+ setup an association - recv
+*/
+NTSTATUS wrepl_associate_recv(struct wrepl_request *req,
+ struct wrepl_associate *io)
+{
+ struct wrepl_packet *packet=NULL;
+ NTSTATUS status;
+ status = wrepl_request_recv(req, req->wrepl_socket, &packet);
+ NT_STATUS_NOT_OK_RETURN(status);
+ if (packet->mess_type != WREPL_START_ASSOCIATION_REPLY) {
+ status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+ if (NT_STATUS_IS_OK(status)) {
+ io->out.assoc_ctx = packet->message.start_reply.assoc_ctx;
+ io->out.major_version = packet->message.start_reply.major_version;
+ }
+ talloc_free(packet);
+ return status;
+}
+
+/*
+ setup an association - sync api
+*/
+NTSTATUS wrepl_associate(struct wrepl_socket *wrepl_socket,
+ struct wrepl_associate *io)
+{
+ struct wrepl_request *req = wrepl_associate_send(wrepl_socket, io);
+ return wrepl_associate_recv(req, io);
+}
+
+
+/*
+ stop an association - send
+*/
+struct wrepl_request *wrepl_associate_stop_send(struct wrepl_socket *wrepl_socket,
+ struct wrepl_associate_stop *io)
+{
+ struct wrepl_packet *packet;
+ struct wrepl_request *req;
+ struct wrepl_send_ctrl ctrl;
+
+ packet = talloc_zero(wrepl_socket, struct wrepl_packet);
+ if (packet == NULL) return NULL;
+
+ packet->opcode = WREPL_OPCODE_BITS;
+ packet->assoc_ctx = io->in.assoc_ctx;
+ packet->mess_type = WREPL_STOP_ASSOCIATION;
+ packet->message.stop.reason = io->in.reason;
+
+ ZERO_STRUCT(ctrl);
+ if (io->in.reason == 0) {
+ ctrl.send_only = true;
+ ctrl.disconnect_after_send = true;
+ }
+
+ req = wrepl_request_send(wrepl_socket, packet, &ctrl);
+
+ talloc_free(packet);
+
+ return req;
+}
+
+/*
+ stop an association - recv
+*/
+NTSTATUS wrepl_associate_stop_recv(struct wrepl_request *req,
+ struct wrepl_associate_stop *io)
+{
+ struct wrepl_packet *packet=NULL;
+ NTSTATUS status;
+ status = wrepl_request_recv(req, req->wrepl_socket, &packet);
+ NT_STATUS_NOT_OK_RETURN(status);
+ talloc_free(packet);
+ return status;
+}
+
+/*
+ setup an association - sync api
+*/
+NTSTATUS wrepl_associate_stop(struct wrepl_socket *wrepl_socket,
+ struct wrepl_associate_stop *io)
+{
+ struct wrepl_request *req = wrepl_associate_stop_send(wrepl_socket, io);
+ return wrepl_associate_stop_recv(req, io);
+}
+
+/*
+ fetch the partner tables - send
+*/
+struct wrepl_request *wrepl_pull_table_send(struct wrepl_socket *wrepl_socket,
+ struct wrepl_pull_table *io)
+{
+ struct wrepl_packet *packet;
+ struct wrepl_request *req;
+
+ packet = talloc_zero(wrepl_socket, struct wrepl_packet);
+ if (packet == NULL) return NULL;
+
+ packet->opcode = WREPL_OPCODE_BITS;
+ packet->assoc_ctx = io->in.assoc_ctx;
+ packet->mess_type = WREPL_REPLICATION;
+ packet->message.replication.command = WREPL_REPL_TABLE_QUERY;
+
+ req = wrepl_request_send(wrepl_socket, packet, NULL);
+
+ talloc_free(packet);
+
+ return req;
+}
+
+
+/*
+ fetch the partner tables - recv
+*/
+NTSTATUS wrepl_pull_table_recv(struct wrepl_request *req,
+ TALLOC_CTX *mem_ctx,
+ struct wrepl_pull_table *io)
+{
+ struct wrepl_packet *packet=NULL;
+ NTSTATUS status;
+ struct wrepl_table *table;
+ int i;
+
+ status = wrepl_request_recv(req, req->wrepl_socket, &packet);
+ NT_STATUS_NOT_OK_RETURN(status);
+ if (packet->mess_type != WREPL_REPLICATION) {
+ status = NT_STATUS_NETWORK_ACCESS_DENIED;
+ } else if (packet->message.replication.command != WREPL_REPL_TABLE_REPLY) {
+ status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+
+ table = &packet->message.replication.info.table;
+ io->out.num_partners = table->partner_count;
+ io->out.partners = talloc_steal(mem_ctx, table->partners);
+ for (i=0;i<io->out.num_partners;i++) {
+ talloc_steal(io->out.partners, io->out.partners[i].address);
+ }
+
+failed:
+ talloc_free(packet);
+ return status;
+}
+
+
+/*
+ fetch the partner table - sync api
+*/
+NTSTATUS wrepl_pull_table(struct wrepl_socket *wrepl_socket,
+ TALLOC_CTX *mem_ctx,
+ struct wrepl_pull_table *io)
+{
+ struct wrepl_request *req = wrepl_pull_table_send(wrepl_socket, io);
+ return wrepl_pull_table_recv(req, mem_ctx, io);
+}
+
+
+/*
+ fetch the names for a WINS partner - send
+*/
+struct wrepl_request *wrepl_pull_names_send(struct wrepl_socket *wrepl_socket,
+ struct wrepl_pull_names *io)
+{
+ struct wrepl_packet *packet;
+ struct wrepl_request *req;
+
+ packet = talloc_zero(wrepl_socket, struct wrepl_packet);
+ if (packet == NULL) return NULL;
+
+ packet->opcode = WREPL_OPCODE_BITS;
+ packet->assoc_ctx = io->in.assoc_ctx;
+ packet->mess_type = WREPL_REPLICATION;
+ packet->message.replication.command = WREPL_REPL_SEND_REQUEST;
+ packet->message.replication.info.owner = io->in.partner;
+
+ req = wrepl_request_send(wrepl_socket, packet, NULL);
+
+ talloc_free(packet);
+
+ return req;
+}
+
+/*
+ fetch the names for a WINS partner - recv
+*/
+NTSTATUS wrepl_pull_names_recv(struct wrepl_request *req,
+ TALLOC_CTX *mem_ctx,
+ struct wrepl_pull_names *io)
+{
+ struct wrepl_packet *packet=NULL;
+ NTSTATUS status;
+ int i;
+
+ status = wrepl_request_recv(req, req->wrepl_socket, &packet);
+ NT_STATUS_NOT_OK_RETURN(status);
+ if (packet->mess_type != WREPL_REPLICATION ||
+ packet->message.replication.command != WREPL_REPL_SEND_REPLY) {
+ status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+
+ io->out.num_names = packet->message.replication.info.reply.num_names;
+
+ io->out.names = talloc_array(packet, struct wrepl_name, io->out.num_names);
+ if (io->out.names == NULL) goto nomem;
+
+ /* convert the list of names and addresses to a sane format */
+ for (i=0;i<io->out.num_names;i++) {
+ struct wrepl_wins_name *wname = &packet->message.replication.info.reply.names[i];
+ struct wrepl_name *name = &io->out.names[i];
+
+ name->name = *wname->name;
+ talloc_steal(io->out.names, wname->name);
+ name->type = WREPL_NAME_TYPE(wname->flags);
+ name->state = WREPL_NAME_STATE(wname->flags);
+ name->node = WREPL_NAME_NODE(wname->flags);
+ name->is_static = WREPL_NAME_IS_STATIC(wname->flags);
+ name->raw_flags = wname->flags;
+ name->version_id= wname->id;
+ name->owner = talloc_strdup(io->out.names, io->in.partner.address);
+ if (name->owner == NULL) goto nomem;
+
+ /* trying to save 1 or 2 bytes on the wire isn't a good idea */
+ if (wname->flags & 2) {
+ int j;
+
+ name->num_addresses = wname->addresses.addresses.num_ips;
+ name->addresses = talloc_array(io->out.names,
+ struct wrepl_address,
+ name->num_addresses);
+ if (name->addresses == NULL) goto nomem;
+ for (j=0;j<name->num_addresses;j++) {
+ name->addresses[j].owner =
+ talloc_steal(name->addresses,
+ wname->addresses.addresses.ips[j].owner);
+ name->addresses[j].address =
+ talloc_steal(name->addresses,
+ wname->addresses.addresses.ips[j].ip);
+ }
+ } else {
+ name->num_addresses = 1;
+ name->addresses = talloc(io->out.names, struct wrepl_address);
+ if (name->addresses == NULL) goto nomem;
+ name->addresses[0].owner = talloc_strdup(name->addresses,io->in.partner.address);
+ if (name->addresses[0].owner == NULL) goto nomem;
+ name->addresses[0].address = talloc_steal(name->addresses,
+ wname->addresses.ip);
+ }
+ }
+
+ talloc_steal(mem_ctx, io->out.names);
+ talloc_free(packet);
+ return NT_STATUS_OK;
+nomem:
+ status = NT_STATUS_NO_MEMORY;
+failed:
+ talloc_free(packet);
+ return status;
+}
+
+
+
+/*
+ fetch the names for a WINS partner - sync api
+*/
+NTSTATUS wrepl_pull_names(struct wrepl_socket *wrepl_socket,
+ TALLOC_CTX *mem_ctx,
+ struct wrepl_pull_names *io)
+{
+ struct wrepl_request *req = wrepl_pull_names_send(wrepl_socket, io);
+ return wrepl_pull_names_recv(req, mem_ctx, io);
+}
diff --git a/source4/libcli/wrepl/winsrepl.h b/source4/libcli/wrepl/winsrepl.h
new file mode 100644
index 0000000000..ec1fb6bb59
--- /dev/null
+++ b/source4/libcli/wrepl/winsrepl.h
@@ -0,0 +1,162 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ structures for WINS replication client library
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "librpc/gen_ndr/nbt.h"
+#include "librpc/gen_ndr/winsrepl.h"
+
+/*
+ main context structure for the wins replication client library
+*/
+struct wrepl_socket {
+ struct socket_context *sock;
+ struct packet_context *packet;
+
+ struct {
+ struct tevent_context *ctx;
+ struct tevent_fd *fde;
+ } event;
+
+ /* a queue of replies waiting to be received */
+ struct wrepl_request *recv_queue;
+
+ /* the default timeout for requests, 0 means no timeout */
+#define WREPL_SOCKET_REQUEST_TIMEOUT (60)
+ uint32_t request_timeout;
+
+ /* counter for request timeouts, after 2 timeouts the socket is marked as dead */
+ uint32_t timeout_count;
+
+ /* remember is the socket is dead */
+ bool dead;
+
+ /* remember if we need to free the wrepl_socket at the end of wrepl_socket_dead() */
+ bool free_skipped;
+
+ struct smb_iconv_convenience *iconv_convenience;
+};
+
+struct wrepl_send_ctrl {
+ bool send_only;
+ bool disconnect_after_send;
+};
+
+enum wrepl_request_state {
+ WREPL_REQUEST_INIT = 0,
+ WREPL_REQUEST_RECV = 1,
+ WREPL_REQUEST_DONE = 2,
+ WREPL_REQUEST_ERROR = 3
+};
+
+/*
+ a WINS replication request
+*/
+struct wrepl_request {
+ struct wrepl_request *next, *prev;
+ struct wrepl_socket *wrepl_socket;
+
+ enum wrepl_request_state state;
+ bool trigger;
+ NTSTATUS status;
+
+ struct tevent_timer *te;
+
+ struct wrepl_packet *packet;
+
+ struct {
+ void (*fn)(struct wrepl_request *);
+ void *private_data;
+ } async;
+};
+
+
+/*
+ setup an association
+*/
+struct wrepl_associate {
+ struct {
+ uint32_t assoc_ctx;
+ uint16_t major_version;
+ } out;
+};
+
+/*
+ setup an association
+*/
+struct wrepl_associate_stop {
+ struct {
+ uint32_t assoc_ctx;
+ uint32_t reason;
+ } in;
+};
+
+/*
+ pull the partner table
+*/
+struct wrepl_pull_table {
+ struct {
+ uint32_t assoc_ctx;
+ } in;
+ struct {
+ uint32_t num_partners;
+ struct wrepl_wins_owner *partners;
+ } out;
+};
+
+#define WREPL_NAME_TYPE(flags) (flags & WREPL_FLAGS_RECORD_TYPE)
+#define WREPL_NAME_STATE(flags) ((flags & WREPL_FLAGS_RECORD_STATE)>>2)
+#define WREPL_NAME_NODE(flags) ((flags & WREPL_FLAGS_NODE_TYPE)>>5)
+#define WREPL_NAME_IS_STATIC(flags) ((flags & WREPL_FLAGS_IS_STATIC)?true:false)
+
+#define WREPL_NAME_FLAGS(type, state, node, is_static) \
+ (type | (state << 2) | (node << 5) | \
+ (is_static ? WREPL_FLAGS_IS_STATIC : 0))
+
+/*
+ a full pull replication
+*/
+struct wrepl_pull_names {
+ struct {
+ uint32_t assoc_ctx;
+ struct wrepl_wins_owner partner;
+ } in;
+ struct {
+ uint32_t num_names;
+ struct wrepl_name {
+ struct nbt_name name;
+ enum wrepl_name_type type;
+ enum wrepl_name_state state;
+ enum wrepl_name_node node;
+ bool is_static;
+ uint32_t raw_flags;
+ uint64_t version_id;
+ const char *owner;
+ uint32_t num_addresses;
+ struct wrepl_address {
+ const char *owner;
+ const char *address;
+ } *addresses;
+ } *names;
+ } out;
+};
+
+struct resolve_context;
+
+#include "libcli/wrepl/winsrepl_proto.h"
diff --git a/source4/libnet/composite.h b/source4/libnet/composite.h
new file mode 100644
index 0000000000..50bf1a761c
--- /dev/null
+++ b/source4/libnet/composite.h
@@ -0,0 +1,56 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Monitor structure and message types definitions. Composite function monitoring
+ * allows client application to be notified on function progress. This enables
+ * eg. gui client to display progress bars, status messages, etc.
+ */
+
+
+#define mon_SamrCreateUser (0x00000001)
+#define mon_SamrOpenUser (0x00000002)
+#define mon_SamrQueryUser (0x00000003)
+#define mon_SamrCloseUser (0x00000004)
+#define mon_SamrLookupName (0x00000005)
+#define mon_SamrDeleteUser (0x00000006)
+#define mon_SamrSetUser (0x00000007)
+#define mon_SamrClose (0x00000008)
+#define mon_SamrConnect (0x00000009)
+#define mon_SamrLookupDomain (0x0000000A)
+#define mon_SamrOpenDomain (0x0000000B)
+#define mon_SamrEnumDomains (0x0000000C)
+#define mon_LsaOpenPolicy (0x0000000D)
+#define mon_LsaQueryPolicy (0x0000000E)
+#define mon_LsaClose (0x0000000F)
+#define mon_SamrOpenGroup (0x00000010)
+#define mon_SamrQueryGroup (0x00000011)
+
+#define mon_NetLookupDc (0x00000100)
+#define mon_NetRpcConnect (0x00000200)
+
+#define mon_Mask_Rpc (0x000000FF)
+#define mon_Mask_Net (0x0000FF00)
+
+
+struct monitor_msg {
+ uint32_t type;
+ void *data;
+ size_t data_size;
+};
diff --git a/source4/libnet/config.mk b/source4/libnet/config.mk
new file mode 100644
index 0000000000..fac8af18b7
--- /dev/null
+++ b/source4/libnet/config.mk
@@ -0,0 +1,18 @@
+[SUBSYSTEM::LIBSAMBA-NET]
+PUBLIC_DEPENDENCIES = CREDENTIALS dcerpc dcerpc_samr RPC_NDR_LSA RPC_NDR_SRVSVC RPC_NDR_DRSUAPI LIBCLI_COMPOSITE LIBCLI_RESOLVE LIBCLI_FINDDCS LIBCLI_CLDAP LIBCLI_FINDDCS gensec_schannel LIBCLI_AUTH LIBNDR SMBPASSWD PROVISION
+
+LIBSAMBA-NET_OBJ_FILES = $(addprefix $(libnetsrcdir)/, \
+ libnet.o libnet_passwd.o libnet_time.o libnet_rpc.o \
+ libnet_join.o libnet_site.o libnet_become_dc.o libnet_unbecome_dc.o \
+ libnet_vampire.o libnet_samdump.o libnet_samdump_keytab.o \
+ libnet_samsync_ldb.o libnet_user.o libnet_group.o libnet_share.o \
+ libnet_lookup.o libnet_domain.o userinfo.o groupinfo.o userman.o \
+ groupman.o prereq_domain.o libnet_samsync.o)
+
+$(eval $(call proto_header_template,$(libnetsrcdir)/libnet_proto.h,$(LIBSAMBA-NET_OBJ_FILES:.o=.c)))
+
+[PYTHON::python_net]
+LIBRARY_REALNAME = samba/net.$(SHLIBEXT)
+PRIVATE_DEPENDENCIES = LIBSAMBA-NET
+
+python_net_OBJ_FILES = $(libnetsrcdir)/py_net.o
diff --git a/source4/libnet/groupinfo.c b/source4/libnet/groupinfo.c
new file mode 100644
index 0000000000..1779c2816e
--- /dev/null
+++ b/source4/libnet/groupinfo.c
@@ -0,0 +1,364 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ a composite function for getting group information via samr pipe
+*/
+
+
+#include "includes.h"
+#include "libcli/composite/composite.h"
+#include "librpc/gen_ndr/security.h"
+#include "libcli/security/security.h"
+#include "libnet/libnet.h"
+#include "librpc/gen_ndr/ndr_samr_c.h"
+
+
+struct groupinfo_state {
+ struct dcerpc_pipe *pipe;
+ struct policy_handle domain_handle;
+ struct policy_handle group_handle;
+ uint16_t level;
+ struct samr_LookupNames lookup;
+ struct samr_OpenGroup opengroup;
+ struct samr_QueryGroupInfo querygroupinfo;
+ struct samr_Close samrclose;
+ union samr_GroupInfo *info;
+
+ /* information about the progress */
+ void (*monitor_fn)(struct monitor_msg *);
+};
+
+
+static void continue_groupinfo_lookup(struct rpc_request *req);
+static void continue_groupinfo_opengroup(struct rpc_request *req);
+static void continue_groupinfo_getgroup(struct rpc_request *req);
+static void continue_groupinfo_closegroup(struct rpc_request *req);
+
+
+/**
+ * Stage 1 (optional): Look for a group name in SAM server.
+ */
+static void continue_groupinfo_lookup(struct rpc_request *req)
+{
+ struct composite_context *c;
+ struct groupinfo_state *s;
+ struct rpc_request *opengroup_req;
+ struct monitor_msg msg;
+ struct msg_rpc_lookup_name *msg_lookup;
+
+ c = talloc_get_type(req->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct groupinfo_state);
+
+ /* receive samr_Lookup reply */
+ c->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(c)) return;
+
+ /* there could be a problem with name resolving itself */
+ if (!NT_STATUS_IS_OK(s->lookup.out.result)) {
+ composite_error(c, s->lookup.out.result);
+ return;
+ }
+
+ /* issue a monitor message */
+ if (s->monitor_fn) {
+ msg.type = mon_SamrLookupName;
+ msg_lookup = talloc(s, struct msg_rpc_lookup_name);
+ msg_lookup->rid = s->lookup.out.rids->ids;
+ msg_lookup->count = s->lookup.out.rids->count;
+ msg.data = (void*)msg_lookup;
+ msg.data_size = sizeof(*msg_lookup);
+
+ s->monitor_fn(&msg);
+ }
+
+
+ /* have we actually got name resolved
+ - we're looking for only one at the moment */
+ if (s->lookup.out.rids->count == 0) {
+ composite_error(c, NT_STATUS_NO_SUCH_USER);
+ }
+
+ /* TODO: find proper status code for more than one rid found */
+
+ /* prepare parameters for LookupNames */
+ s->opengroup.in.domain_handle = &s->domain_handle;
+ s->opengroup.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ s->opengroup.in.rid = s->lookup.out.rids->ids[0];
+ s->opengroup.out.group_handle = &s->group_handle;
+
+ /* send request */
+ opengroup_req = dcerpc_samr_OpenGroup_send(s->pipe, c, &s->opengroup);
+ if (composite_nomem(opengroup_req, c)) return;
+
+ composite_continue_rpc(c, opengroup_req, continue_groupinfo_opengroup, c);
+}
+
+
+/**
+ * Stage 2: Open group policy handle.
+ */
+static void continue_groupinfo_opengroup(struct rpc_request *req)
+{
+ struct composite_context *c;
+ struct groupinfo_state *s;
+ struct rpc_request *querygroup_req;
+ struct monitor_msg msg;
+ struct msg_rpc_open_group *msg_open;
+
+ c = talloc_get_type(req->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct groupinfo_state);
+
+ /* receive samr_OpenGroup reply */
+ c->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(c)) return;
+
+ if (!NT_STATUS_IS_OK(s->querygroupinfo.out.result)) {
+ composite_error(c, s->querygroupinfo.out.result);
+ return;
+ }
+
+ /* issue a monitor message */
+ if (s->monitor_fn) {
+ msg.type = mon_SamrOpenGroup;
+ msg_open = talloc(s, struct msg_rpc_open_group);
+ msg_open->rid = s->opengroup.in.rid;
+ msg_open->access_mask = s->opengroup.in.access_mask;
+ msg.data = (void*)msg_open;
+ msg.data_size = sizeof(*msg_open);
+
+ s->monitor_fn(&msg);
+ }
+
+ /* prepare parameters for QueryGroupInfo call */
+ s->querygroupinfo.in.group_handle = &s->group_handle;
+ s->querygroupinfo.in.level = s->level;
+ s->querygroupinfo.out.info = talloc(s, union samr_GroupInfo *);
+ if (composite_nomem(s->querygroupinfo.out.info, c)) return;
+
+ /* queue rpc call, set event handling and new state */
+ querygroup_req = dcerpc_samr_QueryGroupInfo_send(s->pipe, c, &s->querygroupinfo);
+ if (composite_nomem(querygroup_req, c)) return;
+
+ composite_continue_rpc(c, querygroup_req, continue_groupinfo_getgroup, c);
+}
+
+
+/**
+ * Stage 3: Get requested group information.
+ */
+static void continue_groupinfo_getgroup(struct rpc_request *req)
+{
+ struct composite_context *c;
+ struct groupinfo_state *s;
+ struct rpc_request *close_req;
+ struct monitor_msg msg;
+ struct msg_rpc_query_group *msg_query;
+
+ c = talloc_get_type(req->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct groupinfo_state);
+
+ /* receive samr_QueryGroupInfo reply */
+ c->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(c)) return;
+
+ /* check if querygroup itself went ok */
+ if (!NT_STATUS_IS_OK(s->querygroupinfo.out.result)) {
+ composite_error(c, s->querygroupinfo.out.result);
+ return;
+ }
+
+ s->info = talloc_steal(s, *s->querygroupinfo.out.info);
+
+ /* issue a monitor message */
+ if (s->monitor_fn) {
+ msg.type = mon_SamrQueryGroup;
+ msg_query = talloc(s, struct msg_rpc_query_group);
+ msg_query->level = s->querygroupinfo.in.level;
+ msg.data = (void*)msg_query;
+ msg.data_size = sizeof(*msg_query);
+
+ s->monitor_fn(&msg);
+ }
+
+ /* prepare arguments for Close call */
+ s->samrclose.in.handle = &s->group_handle;
+ s->samrclose.out.handle = &s->group_handle;
+
+ /* queue rpc call, set event handling and new state */
+ close_req = dcerpc_samr_Close_send(s->pipe, c, &s->samrclose);
+ if (composite_nomem(close_req, c)) return;
+
+ composite_continue_rpc(c, close_req, continue_groupinfo_closegroup, c);
+}
+
+
+/**
+ * Stage 4: Close policy handle associated with opened group.
+ */
+static void continue_groupinfo_closegroup(struct rpc_request *req)
+{
+ struct composite_context *c;
+ struct groupinfo_state *s;
+ struct monitor_msg msg;
+ struct msg_rpc_close_group *msg_close;
+
+ c = talloc_get_type(req->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct groupinfo_state);
+
+ /* receive samr_Close reply */
+ c->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(c)) return;
+
+ if (!NT_STATUS_IS_OK(s->samrclose.out.result)) {
+ composite_error(c, s->samrclose.out.result);
+ return;
+ }
+
+ /* issue a monitor message */
+ if (s->monitor_fn) {
+ msg.type = mon_SamrClose;
+ msg_close = talloc(s, struct msg_rpc_close_group);
+ msg_close->rid = s->opengroup.in.rid;
+ msg.data = (void*)msg_close;
+ msg.data_size = sizeof(*msg_close);
+
+ s->monitor_fn(&msg);
+ }
+
+ composite_done(c);
+}
+
+
+/**
+ * Sends asynchronous groupinfo request
+ *
+ * @param p dce/rpc call pipe
+ * @param io arguments and results of the call
+ */
+struct composite_context *libnet_rpc_groupinfo_send(struct dcerpc_pipe *p,
+ struct libnet_rpc_groupinfo *io,
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *c;
+ struct groupinfo_state *s;
+ struct dom_sid *sid;
+ struct rpc_request *opengroup_req, *lookup_req;
+
+ if (!p || !io) return NULL;
+
+ c = composite_create(p, dcerpc_event_context(p));
+ if (c == NULL) return c;
+
+ s = talloc_zero(c, struct groupinfo_state);
+ if (composite_nomem(s, c)) return c;
+
+ c->private_data = s;
+
+ s->level = io->in.level;
+ s->pipe = p;
+ s->domain_handle = io->in.domain_handle;
+ s->monitor_fn = monitor;
+
+ if (io->in.sid) {
+ sid = dom_sid_parse_talloc(s, io->in.sid);
+ if (composite_nomem(sid, c)) return c;
+
+ s->opengroup.in.domain_handle = &s->domain_handle;
+ s->opengroup.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ s->opengroup.in.rid = sid->sub_auths[sid->num_auths - 1];
+ s->opengroup.out.group_handle = &s->group_handle;
+
+ /* send request */
+ opengroup_req = dcerpc_samr_OpenGroup_send(p, c, &s->opengroup);
+ if (composite_nomem(opengroup_req, c)) return c;
+
+ composite_continue_rpc(c, opengroup_req, continue_groupinfo_opengroup, c);
+
+ } else {
+ /* preparing parameters to send rpc request */
+ s->lookup.in.domain_handle = &s->domain_handle;
+ s->lookup.in.num_names = 1;
+ s->lookup.in.names = talloc_array(s, struct lsa_String, 1);
+ if (composite_nomem(s->lookup.in.names, c)) return c;
+
+ s->lookup.in.names[0].string = talloc_strdup(s, io->in.groupname);
+ if (composite_nomem(s->lookup.in.names[0].string, c)) return c;
+ s->lookup.out.rids = talloc_zero(s, struct samr_Ids);
+ s->lookup.out.types = talloc_zero(s, struct samr_Ids);
+ if (composite_nomem(s->lookup.out.rids, c)) return c;
+ if (composite_nomem(s->lookup.out.types, c)) return c;
+
+ /* send request */
+ lookup_req = dcerpc_samr_LookupNames_send(p, c, &s->lookup);
+ if (composite_nomem(lookup_req, c)) return c;
+
+ composite_continue_rpc(c, lookup_req, continue_groupinfo_lookup, c);
+ }
+
+ return c;
+}
+
+
+/**
+ * Waits for and receives result of asynchronous groupinfo call
+ *
+ * @param c composite context returned by asynchronous groupinfo call
+ * @param mem_ctx memory context of the call
+ * @param io pointer to results (and arguments) of the call
+ * @return nt status code of execution
+ */
+
+NTSTATUS libnet_rpc_groupinfo_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
+ struct libnet_rpc_groupinfo *io)
+{
+ NTSTATUS status;
+ struct groupinfo_state *s;
+
+ /* wait for results of sending request */
+ status = composite_wait(c);
+
+ if (NT_STATUS_IS_OK(status) && io) {
+ s = talloc_get_type(c->private_data, struct groupinfo_state);
+ talloc_steal(mem_ctx, s->info);
+ io->out.info = *s->info;
+ }
+
+ /* memory context associated to composite context is no longer needed */
+ talloc_free(c);
+ return status;
+}
+
+
+/**
+ * Synchronous version of groupinfo call
+ *
+ * @param pipe dce/rpc call pipe
+ * @param mem_ctx memory context for the call
+ * @param io arguments and results of the call
+ * @return nt status code of execution
+ */
+
+NTSTATUS libnet_rpc_groupinfo(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_rpc_groupinfo *io)
+{
+ struct composite_context *c = libnet_rpc_groupinfo_send(p, io, NULL);
+ return libnet_rpc_groupinfo_recv(c, mem_ctx, io);
+}
diff --git a/source4/libnet/groupinfo.h b/source4/libnet/groupinfo.h
new file mode 100644
index 0000000000..ad1384023b
--- /dev/null
+++ b/source4/libnet/groupinfo.h
@@ -0,0 +1,54 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+
+#include "librpc/gen_ndr/samr.h"
+
+/*
+ * IO structures for groupinfo.c functions
+ */
+
+struct libnet_rpc_groupinfo {
+ struct {
+ struct policy_handle domain_handle;
+ const char *groupname;
+ const char *sid;
+ uint16_t level;
+ } in;
+ struct {
+ union samr_GroupInfo info;
+ } out;
+};
+
+
+/*
+ * Monitor messages sent from groupinfo.c functions
+ */
+
+struct msg_rpc_open_group {
+ uint32_t rid, access_mask;
+};
+
+struct msg_rpc_query_group {
+ uint16_t level;
+};
+
+struct msg_rpc_close_group {
+ uint32_t rid;
+};
diff --git a/source4/libnet/groupman.c b/source4/libnet/groupman.c
new file mode 100644
index 0000000000..0f54db9705
--- /dev/null
+++ b/source4/libnet/groupman.c
@@ -0,0 +1,314 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ a composite function for manipulating (add/edit/del) groups via samr pipe
+*/
+
+#include "includes.h"
+#include "libcli/composite/composite.h"
+#include "libnet/libnet.h"
+#include "librpc/gen_ndr/ndr_samr_c.h"
+
+
+struct groupadd_state {
+ struct dcerpc_pipe *pipe;
+ struct policy_handle domain_handle;
+ struct samr_CreateDomainGroup creategroup;
+ struct policy_handle group_handle;
+ uint32_t group_rid;
+
+ void (*monitor_fn)(struct monitor_msg*);
+};
+
+
+static void continue_groupadd_created(struct rpc_request *req);
+
+
+struct composite_context* libnet_rpc_groupadd_send(struct dcerpc_pipe *p,
+ struct libnet_rpc_groupadd *io,
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *c;
+ struct groupadd_state *s;
+ struct rpc_request *create_req;
+
+ if (!p || !io) return NULL;
+
+ c = composite_create(p, dcerpc_event_context(p));
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct groupadd_state);
+ if (composite_nomem(s, c)) return c;
+
+ c->private_data = s;
+
+ s->domain_handle = io->in.domain_handle;
+ s->pipe = p;
+ s->monitor_fn = monitor;
+
+ s->creategroup.in.domain_handle = &s->domain_handle;
+
+ s->creategroup.in.name = talloc_zero(c, struct lsa_String);
+ if (composite_nomem(s->creategroup.in.name, c)) return c;
+
+ s->creategroup.in.name->string = talloc_strdup(c, io->in.groupname);
+ if (composite_nomem(s->creategroup.in.name->string, c)) return c;
+
+ s->creategroup.in.access_mask = 0;
+
+ s->creategroup.out.group_handle = &s->group_handle;
+ s->creategroup.out.rid = &s->group_rid;
+
+ create_req = dcerpc_samr_CreateDomainGroup_send(s->pipe, c, &s->creategroup);
+ if (composite_nomem(create_req, c)) return c;
+
+ composite_continue_rpc(c, create_req, continue_groupadd_created, c);
+ return c;
+}
+
+
+NTSTATUS libnet_rpc_groupadd_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
+ struct libnet_rpc_groupadd *io)
+{
+ NTSTATUS status;
+ struct groupadd_state *s;
+
+ status = composite_wait(c);
+ if (NT_STATUS_IS_OK(status)) {
+ s = talloc_get_type(c, struct groupadd_state);
+ }
+
+ return status;
+}
+
+
+static void continue_groupadd_created(struct rpc_request *req)
+{
+ struct composite_context *c;
+ struct groupadd_state *s;
+
+ c = talloc_get_type(req->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct groupadd_state);
+
+ c->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(c)) return;
+
+ c->status = s->creategroup.out.result;
+ if (!composite_is_ok(c)) return;
+
+ composite_done(c);
+}
+
+
+NTSTATUS libnet_rpc_groupadd(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct libnet_rpc_groupadd *io)
+{
+ struct composite_context *c;
+
+ c = libnet_rpc_groupadd_send(p, io, NULL);
+ return libnet_rpc_groupadd_recv(c, mem_ctx, io);
+}
+
+
+struct groupdel_state {
+ struct dcerpc_pipe *pipe;
+ struct policy_handle domain_handle;
+ struct policy_handle group_handle;
+ struct samr_LookupNames lookupname;
+ struct samr_OpenGroup opengroup;
+ struct samr_DeleteDomainGroup deletegroup;
+
+ /* information about the progress */
+ void (*monitor_fn)(struct monitor_msg *);
+};
+
+
+static void continue_groupdel_name_found(struct rpc_request *req);
+static void continue_groupdel_group_opened(struct rpc_request *req);
+static void continue_groupdel_deleted(struct rpc_request *req);
+
+
+struct composite_context* libnet_rpc_groupdel_send(struct dcerpc_pipe *p,
+ struct libnet_rpc_groupdel *io,
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *c;
+ struct groupdel_state *s;
+ struct rpc_request *lookup_req;
+
+ /* composite context allocation and setup */
+ c = composite_create(p, dcerpc_event_context(p));
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct groupdel_state);
+ if (composite_nomem(s, c)) return c;
+
+ c->private_data = s;
+
+ /* store function parameters in the state structure */
+ s->pipe = p;
+ s->domain_handle = io->in.domain_handle;
+ s->monitor_fn = monitor;
+
+ /* prepare parameters to send rpc request */
+ s->lookupname.in.domain_handle = &io->in.domain_handle;
+ s->lookupname.in.num_names = 1;
+ s->lookupname.in.names = talloc_zero(s, struct lsa_String);
+ s->lookupname.in.names->string = io->in.groupname;
+ s->lookupname.out.rids = talloc_zero(s, struct samr_Ids);
+ s->lookupname.out.types = talloc_zero(s, struct samr_Ids);
+ if (composite_nomem(s->lookupname.out.rids, c)) return c;
+ if (composite_nomem(s->lookupname.out.types, c)) return c;
+
+ /* send the request */
+ lookup_req = dcerpc_samr_LookupNames_send(p, c, &s->lookupname);
+ if (composite_nomem(lookup_req, c)) return c;
+
+ composite_continue_rpc(c, lookup_req, continue_groupdel_name_found, c);
+ return c;
+}
+
+
+static void continue_groupdel_name_found(struct rpc_request *req)
+{
+ struct composite_context *c;
+ struct groupdel_state *s;
+ struct rpc_request *opengroup_req;
+
+ c = talloc_get_type(req->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct groupdel_state);
+
+ /* receive samr_LookupNames result */
+ c->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(c)) return;
+
+ c->status = s->lookupname.out.result;
+ if (!NT_STATUS_IS_OK(c->status)) {
+ composite_error(c, c->status);
+ return;
+ }
+
+ /* what to do when there's no group account to delete
+ and what if there's more than one rid resolved */
+ if (!s->lookupname.out.rids->count) {
+ c->status = NT_STATUS_NO_SUCH_GROUP;
+ composite_error(c, c->status);
+ return;
+
+ } else if (!s->lookupname.out.rids->count > 1) {
+ c->status = NT_STATUS_INVALID_ACCOUNT_NAME;
+ composite_error(c, c->status);
+ return;
+ }
+
+ /* prepare the arguments for rpc call */
+ s->opengroup.in.domain_handle = &s->domain_handle;
+ s->opengroup.in.rid = s->lookupname.out.rids->ids[0];
+ s->opengroup.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ s->opengroup.out.group_handle = &s->group_handle;
+
+ /* send rpc request */
+ opengroup_req = dcerpc_samr_OpenGroup_send(s->pipe, c, &s->opengroup);
+ if (composite_nomem(opengroup_req, c)) return;
+
+ composite_continue_rpc(c, opengroup_req, continue_groupdel_group_opened, c);
+}
+
+
+static void continue_groupdel_group_opened(struct rpc_request *req)
+{
+ struct composite_context *c;
+ struct groupdel_state *s;
+ struct rpc_request *delgroup_req;
+
+ c = talloc_get_type(req->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct groupdel_state);
+
+ /* receive samr_OpenGroup result */
+ c->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(c)) return;
+
+ c->status = s->opengroup.out.result;
+ if (!NT_STATUS_IS_OK(c->status)) {
+ composite_error(c, c->status);
+ return;
+ }
+
+ /* prepare the final rpc call arguments */
+ s->deletegroup.in.group_handle = &s->group_handle;
+ s->deletegroup.out.group_handle = &s->group_handle;
+
+ /* send rpc request */
+ delgroup_req = dcerpc_samr_DeleteDomainGroup_send(s->pipe, c, &s->deletegroup);
+ if (composite_nomem(delgroup_req, c)) return;
+
+ /* callback handler setup */
+ composite_continue_rpc(c, delgroup_req, continue_groupdel_deleted, c);
+}
+
+
+static void continue_groupdel_deleted(struct rpc_request *req)
+{
+ struct composite_context *c;
+ struct groupdel_state *s;
+
+ c = talloc_get_type(req->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct groupdel_state);
+
+ /* receive samr_DeleteGroup result */
+ c->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(c)) return;
+
+ /* return the actual function call status */
+ c->status = s->deletegroup.out.result;
+ if (!NT_STATUS_IS_OK(c->status)) {
+ composite_error(c, c->status);
+ return;
+ }
+
+ composite_done(c);
+}
+
+
+NTSTATUS libnet_rpc_groupdel_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
+ struct libnet_rpc_groupdel *io)
+{
+ NTSTATUS status;
+ struct groupdel_state *s;
+
+ status = composite_wait(c);
+ if (NT_STATUS_IS_OK(status) && io) {
+ s = talloc_get_type(c->private_data, struct groupdel_state);
+ io->out.group_handle = s->group_handle;
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+
+NTSTATUS libnet_rpc_groupdel(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct libnet_rpc_groupdel *io)
+{
+ struct composite_context *c;
+
+ c = libnet_rpc_groupdel_send(p, io, NULL);
+ return libnet_rpc_groupdel_recv(c, mem_ctx, io);
+}
diff --git a/source4/libnet/groupman.h b/source4/libnet/groupman.h
new file mode 100644
index 0000000000..cd4452e73c
--- /dev/null
+++ b/source4/libnet/groupman.h
@@ -0,0 +1,46 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "librpc/gen_ndr/misc.h"
+
+
+/*
+ * IO structures for groupman.c functions
+ */
+
+struct libnet_rpc_groupadd {
+ struct {
+ struct policy_handle domain_handle;
+ const char *groupname;
+ } in;
+ struct {
+ struct policy_handle group_handle;
+ } out;
+};
+
+
+struct libnet_rpc_groupdel {
+ struct {
+ struct policy_handle domain_handle;
+ const char *groupname;
+ } in;
+ struct {
+ struct policy_handle group_handle;
+ } out;
+};
diff --git a/source4/libnet/libnet.c b/source4/libnet/libnet.c
new file mode 100644
index 0000000000..b10fb65df6
--- /dev/null
+++ b/source4/libnet/libnet.c
@@ -0,0 +1,56 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Stefan Metzmacher 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libnet/libnet.h"
+#include "lib/events/events.h"
+#include "param/param.h"
+#include "libcli/resolve/resolve.h"
+
+struct libnet_context *libnet_context_init(struct tevent_context *ev,
+ struct loadparm_context *lp_ctx)
+{
+ struct libnet_context *ctx;
+
+ /* We require an event context here */
+ if (!ev) {
+ return NULL;
+ }
+
+ /* create brand new libnet context */
+ ctx = talloc(ev, struct libnet_context);
+ if (!ctx) {
+ return NULL;
+ }
+
+ ctx->event_ctx = ev;
+ ctx->lp_ctx = lp_ctx;
+
+ /* name resolution methods */
+ ctx->resolve_ctx = lp_resolve_context(lp_ctx);
+
+ /* connected services' params */
+ ZERO_STRUCT(ctx->samr);
+ ZERO_STRUCT(ctx->lsa);
+
+ /* default buffer size for various operations requiring specifying a buffer */
+ ctx->samr.buf_size = 128;
+
+ return ctx;
+}
diff --git a/source4/libnet/libnet.h b/source4/libnet/libnet.h
new file mode 100644
index 0000000000..543a131806
--- /dev/null
+++ b/source4/libnet/libnet.h
@@ -0,0 +1,78 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Stefan Metzmacher 2004
+ Copyright (C) Rafal Szczesniak 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "librpc/gen_ndr/misc.h"
+
+struct libnet_context {
+ /* here we need:
+ * a client env context
+ * a user env context
+ */
+ struct cli_credentials *cred;
+
+ /* samr connection parameters - opened handles and related properties */
+ struct {
+ struct dcerpc_pipe *pipe;
+ const char *name;
+ struct dom_sid *sid;
+ uint32_t access_mask;
+ struct policy_handle handle;
+ struct policy_handle connect_handle;
+ int buf_size;
+ } samr;
+
+ /* lsa connection parameters - opened handles and related properties */
+ struct {
+ struct dcerpc_pipe *pipe;
+ const char *name;
+ uint32_t access_mask;
+ struct policy_handle handle;
+ } lsa;
+
+ /* name resolution methods */
+ struct resolve_context *resolve_ctx;
+
+ struct tevent_context *event_ctx;
+
+ struct loadparm_context *lp_ctx;
+};
+
+
+#include "lib/ldb/include/ldb.h"
+#include "libnet/composite.h"
+#include "libnet/userman.h"
+#include "libnet/userinfo.h"
+#include "libnet/groupinfo.h"
+#include "libnet/groupman.h"
+#include "libnet/libnet_passwd.h"
+#include "libnet/libnet_time.h"
+#include "libnet/libnet_rpc.h"
+#include "libnet/libnet_join.h"
+#include "libnet/libnet_site.h"
+#include "libnet/libnet_become_dc.h"
+#include "libnet/libnet_unbecome_dc.h"
+#include "libnet/libnet_samsync.h"
+#include "libnet/libnet_vampire.h"
+#include "libnet/libnet_user.h"
+#include "libnet/libnet_group.h"
+#include "libnet/libnet_share.h"
+#include "libnet/libnet_lookup.h"
+#include "libnet/libnet_domain.h"
+#include "libnet/libnet_proto.h"
diff --git a/source4/libnet/libnet_become_dc.c b/source4/libnet/libnet_become_dc.c
new file mode 100644
index 0000000000..bf046745e6
--- /dev/null
+++ b/source4/libnet/libnet_become_dc.c
@@ -0,0 +1,3032 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Stefan Metzmacher <metze@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libnet/libnet.h"
+#include "libcli/composite/composite.h"
+#include "libcli/cldap/cldap.h"
+#include "lib/ldb/include/ldb.h"
+#include "lib/ldb/include/ldb_errors.h"
+#include "lib/ldb_wrap.h"
+#include "dsdb/samdb/samdb.h"
+#include "dsdb/common/flags.h"
+#include "librpc/gen_ndr/ndr_drsuapi_c.h"
+#include "libcli/security/security.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "librpc/gen_ndr/ndr_nbt.h"
+#include "librpc/gen_ndr/ndr_drsuapi.h"
+#include "auth/gensec/gensec.h"
+#include "param/param.h"
+
+/*****************************************************************************
+ * Windows 2003 (w2k3) does the following steps when changing the server role
+ * from domain member to domain controller
+ *
+ * We mostly do the same.
+ *****************************************************************************/
+
+/*
+ * lookup DC:
+ * - using nbt name<1C> request and a samlogon mailslot request
+ * or
+ * - using a DNS SRV _ldap._tcp.dc._msdcs. request and a CLDAP netlogon request
+ *
+ * see: becomeDC_recv_cldap() and becomeDC_send_cldap()
+ */
+
+/*
+ * Open 1st LDAP connection to the DC using admin credentials
+ *
+ * see: becomeDC_connect_ldap1() and becomeDC_ldap_connect()
+ */
+
+/*
+ * LDAP search 1st LDAP connection:
+ *
+ * see: becomeDC_ldap1_rootdse()
+ *
+ * Request:
+ * basedn: ""
+ * scope: base
+ * filter: (objectClass=*)
+ * attrs: *
+ * Result:
+ * ""
+ * currentTime: 20061202155100.0Z
+ * subschemaSubentry: CN=Aggregate,CN=Schema,CN=Configuration,<domain_partition>
+ * dsServiceName: CN=<netbios_name>,CN=Servers,CN=<site_name>,CN=Sites,CN=Configuration,<domain_partition>
+ * namingContexts: <domain_partition>
+ * CN=Configuration,<domain_partition>
+ * CN=Schema,CN=Configuration,<domain_partition>
+ * defaultNamingContext: <domain_partition>
+ * schemaNamingContext: CN=Schema,CN=Configuration,<domain_partition>
+ * configurationNamingContext:CN=Configuration,<domain_partition>
+ * rootDomainNamingContext:<domain_partition>
+ * supportedControl: ...
+ * supportedLDAPVersion: 3
+ * 2
+ * supportedLDAPPolicies: ...
+ * highestCommitedUSN: ...
+ * supportedSASLMechanisms:GSSAPI
+ * GSS-SPNEGO
+ * EXTERNAL
+ * DIGEST-MD5
+ * dnsHostName: <dns_host_name>
+ * ldapServiceName: <domain_dns_name>:<netbios_name>$@<REALM>
+ * serverName: CN=Servers,CN=<site_name>,CN=Sites,CN=Configuration,<domain_partition>
+ * supportedCapabilities: ...
+ * isSyncronized: TRUE
+ * isGlobalCatalogReady: TRUE
+ * domainFunctionality: 0
+ * forestFunctionality: 0
+ * domainControllerFunctionality: 2
+ */
+
+/*
+ * LDAP search 1st LDAP connection:
+ *
+ * see: becomeDC_ldap1_crossref_behavior_version()
+ *
+ * Request:
+ * basedn: CN=Configuration,<domain_partition>
+ * scope: one
+ * filter: (cn=Partitions)
+ * attrs: msDS-Behavior-Version
+ * Result:
+ * CN=Partitions,CN=Configuration,<domain_partition>
+ * msDS-Behavior-Version: 0
+ */
+
+/*
+ * LDAP search 1st LDAP connection:
+ *
+ * NOTE: this seems to be a bug! as the messageID of the LDAP message is corrupted!
+ *
+ * not implemented here
+ *
+ * Request:
+ * basedn: CN=Schema,CN=Configuration,<domain_partition>
+ * scope: one
+ * filter: (cn=Partitions)
+ * attrs: msDS-Behavior-Version
+ * Result:
+ * <none>
+ *
+ */
+
+/*
+ * LDAP search 1st LDAP connection:
+ *
+ * see: becomeDC_ldap1_domain_behavior_version()
+ *
+ * Request:
+ * basedn: <domain_partition>
+ * scope: base
+ * filter: (objectClass=*)
+ * attrs: msDS-Behavior-Version
+ * Result:
+ * <domain_partition>
+ * msDS-Behavior-Version: 0
+ */
+
+/*
+ * LDAP search 1st LDAP connection:
+ *
+ * see: becomeDC_ldap1_schema_object_version()
+ *
+ * Request:
+ * basedn: CN=Schema,CN=Configuration,<domain_partition>
+ * scope: base
+ * filter: (objectClass=*)
+ * attrs: objectVersion
+ * Result:
+ * CN=Schema,CN=Configuration,<domain_partition>
+ * objectVersion: 30
+ */
+
+/*
+ * LDAP search 1st LDAP connection:
+ *
+ * not implemented, because the information is already there
+ *
+ * Request:
+ * basedn: ""
+ * scope: base
+ * filter: (objectClass=*)
+ * attrs: defaultNamingContext
+ * dnsHostName
+ * Result:
+ * ""
+ * defaultNamingContext: <domain_partition>
+ * dnsHostName: <dns_host_name>
+ */
+
+/*
+ * LDAP search 1st LDAP connection:
+ *
+ * see: becomeDC_ldap1_infrastructure_fsmo()
+ *
+ * Request:
+ * basedn: <WKGUID=2fbac1870ade11d297c400c04fd8d5cd,domain_partition>
+ * scope: base
+ * filter: (objectClass=*)
+ * attrs: 1.1
+ * Result:
+ * CN=Infrastructure,<domain_partition>
+ */
+
+/*
+ * LDAP search 1st LDAP connection:
+ *
+ * see: becomeDC_ldap1_w2k3_update_revision()
+ *
+ * Request:
+ * basedn: CN=Windows2003Update,CN=DomainUpdates,CN=System,<domain_partition>
+ * scope: base
+ * filter: (objectClass=*)
+ * attrs: revision
+ * Result:
+ * CN=Windows2003Update,CN=DomainUpdates,CN=System,<domain_partition>
+ * revision: 8
+ */
+
+/*
+ * LDAP search 1st LDAP connection:
+ *
+ * see: becomeDC_ldap1_infrastructure_fsmo()
+ *
+ * Request:
+ * basedn: CN=Infrastructure,<domain_partition>
+ * scope: base
+ * filter: (objectClass=*)
+ * attrs: fSMORoleOwner
+ * Result:
+ * CN=Infrastructure,<domain_partition>
+ * fSMORoleOwner: CN=NTDS Settings,<infrastructure_fsmo_server_object>
+ */
+
+/*
+ * LDAP search 1st LDAP connection:
+ *
+ * see: becomeDC_ldap1_infrastructure_fsmo()
+ *
+ * Request:
+ * basedn: <infrastructure_fsmo_server_object>
+ * scope: base
+ * filter: (objectClass=*)
+ * attrs: dnsHostName
+ * Result:
+ * <infrastructure_fsmo_server_object>
+ * dnsHostName: <dns_host_name>
+ */
+
+/*
+ * LDAP search 1st LDAP connection:
+ *
+ * see: becomeDC_ldap1_infrastructure_fsmo()
+ *
+ * Request:
+ * basedn: CN=NTDS Settings,<infrastructure_fsmo_server_object>
+ * scope: base
+ * filter: (objectClass=*)
+ * attrs: objectGUID
+ * Result:
+ * CN=NTDS Settings,<infrastructure_fsmo_server_object>
+ * objectGUID: <object_guid>
+ */
+
+/*
+ * LDAP search 1st LDAP connection:
+ *
+ * see: becomeDC_ldap1_rid_manager_fsmo()
+ *
+ * Request:
+ * basedn: <domain_partition>
+ * scope: base
+ * filter: (objectClass=*)
+ * attrs: rIDManagerReference
+ * Result:
+ * <domain_partition>
+ * rIDManagerReference: CN=RID Manager$,CN=System,<domain_partition>
+ */
+
+/*
+ * LDAP search 1st LDAP connection:
+ *
+ * see: becomeDC_ldap1_rid_manager_fsmo()
+ *
+ * Request:
+ * basedn: CN=RID Manager$,CN=System,<domain_partition>
+ * scope: base
+ * filter: (objectClass=*)
+ * attrs: fSMORoleOwner
+ * Result:
+ * CN=Infrastructure,<domain_partition>
+ * fSMORoleOwner: CN=NTDS Settings,<rid_manager_fsmo_server_object>
+ */
+
+/*
+ * LDAP search 1st LDAP connection:
+ *
+ * see: becomeDC_ldap1_rid_manager_fsmo()
+ *
+ * Request:
+ * basedn: <rid_manager_fsmo_server_object>
+ * scope: base
+ * filter: (objectClass=*)
+ * attrs: dnsHostName
+ * Result:
+ * <rid_manager_fsmo_server_object>
+ * dnsHostName: <dns_host_name>
+ */
+
+/*
+ * LDAP search 1st LDAP connection:
+ *
+ * see: becomeDC_ldap1_rid_manager_fsmo()
+ *
+ * Request:
+ * basedn: CN=NTDS Settings,<rid_manager_fsmo_server_object>
+ * scope: base
+ * filter: (objectClass=*)
+ * attrs: msDs-ReplicationEpoch
+ * Result:
+ * CN=NTDS Settings,<rid_manager_fsmo_server_object>
+ */
+
+/*
+ * LDAP search 1st LDAP connection:
+ *
+ * see: becomeDC_ldap1_site_object()
+ *
+ * Request:
+ * basedn: CN=<new_dc_site_name>,CN=Sites,CN=Configuration,<domain_partition>
+ * scope: base
+ * filter: (objectClass=*)
+ * attrs:
+ * Result:
+ * CN=<new_dc_site_name>,CN=Sites,CN=Configuration,<domain_partition>
+ * objectClass: top
+ * site
+ * cn: <new_dc_site_name>
+ * distinguishedName:CN=<new_dc_site_name>,CN=Sites,CN=Configuration,<domain_partition>
+ * instanceType: 4
+ * whenCreated: ...
+ * whenChanged: ...
+ * uSNCreated: ...
+ * uSNChanged: ...
+ * showInAdvancedViewOnly: TRUE
+ * name: <new_dc_site_name>
+ * objectGUID: <object_guid>
+ * systemFlags: 1107296256 <0x42000000>
+ * objectCategory: CN=Site,C=Schema,CN=Configuration,<domain_partition>
+ */
+
+/***************************************************************
+ * Add this stage we call the check_options() callback function
+ * of the caller, to see if he wants us to continue
+ *
+ * see: becomeDC_check_options()
+ ***************************************************************/
+
+/*
+ * LDAP search 1st LDAP connection:
+ *
+ * see: becomeDC_ldap1_computer_object()
+ *
+ * Request:
+ * basedn: <domain_partition>
+ * scope: sub
+ * filter: (&(|(objectClass=user)(objectClass=computer))(sAMAccountName=<new_dc_account_name>))
+ * attrs: distinguishedName
+ * userAccountControl
+ * Result:
+ * CN=<new_dc_netbios_name>,CN=Computers,<domain_partition>
+ * distinguishedName: CN=<new_dc_netbios_name>,CN=Computers,<domain_partition>
+ * userAccoountControl: 4096 <0x1000>
+ */
+
+/*
+ * LDAP search 1st LDAP connection:
+ *
+ * see: becomeDC_ldap1_server_object_1()
+ *
+ * Request:
+ * basedn: CN=<new_dc_netbios_name>,CN=Servers,CN=<new_dc_site_name>,CN=Sites,CN=Configuration,<domain_partition>
+ * scope: base
+ * filter: (objectClass=*)
+ * attrs:
+ * Result:
+ * <noSuchObject>
+ * <matchedDN:CN=Servers,CN=<new_dc_site_name>,CN=Sites,CN=Configuration,<domain_partition>>
+ */
+
+/*
+ * LDAP search 1st LDAP connection:
+ *
+ * see: becomeDC_ldap1_server_object_2()
+ *
+ * Request:
+ * basedn: CN=<new_dc_netbios_name>,CN=Computers,<domain_partition>
+ * scope: base
+ * filter: (objectClass=*)
+ * attrs: serverReferenceBL
+ * typesOnly: TRUE!!!
+ * Result:
+ * CN=<new_dc_netbios_name>,CN=Computers,<domain_partition>
+ */
+
+/*
+ * LDAP add 1st LDAP connection:
+ *
+ * see: becomeDC_ldap1_server_object_add()
+ *
+ * Request:
+ * CN=<new_dc_netbios_name>,CN=Computers,<domain_partition>
+ * objectClass: server
+ * systemFlags: 50000000 <0x2FAF080>
+ * serverReference:CN=<new_dc_netbios_name>,CN=Computers,<domain_partition>
+ * Result:
+ * <success>
+ */
+
+/*
+ * LDAP search 1st LDAP connection:
+ *
+ * not implemented, maybe we can add that later
+ *
+ * Request:
+ * basedn: CN=NTDS Settings,CN=<new_dc_netbios_name>,CN=Servers,CN=<new_dc_site_name>,CN=Sites,CN=Configuration,<domain_partition>
+ * scope: base
+ * filter: (objectClass=*)
+ * attrs:
+ * Result:
+ * <noSuchObject>
+ * <matchedDN:CN=<new_dc_netbios_name>,CN=Servers,CN=<new_dc_site_name>,CN=Sites,CN=Configuration,<domain_partition>>
+ */
+
+/*
+ * LDAP search 1st LDAP connection:
+ *
+ * not implemented because it gives no new information
+ *
+ * Request:
+ * basedn: CN=Partitions,CN=Configuration,<domain_partition>
+ * scope: sub
+ * filter: (nCName=<domain_partition>)
+ * attrs: nCName
+ * dnsRoot
+ * controls: LDAP_SERVER_EXTENDED_DN_OID:critical=false
+ * Result:
+ * <GUID=<hex_guid>>;CN=<domain_netbios_name>,CN=Partitions,<domain_partition>>
+ * nCName: <GUID=<hex_guid>>;<SID=<hex_sid>>;<domain_partition>>
+ * dnsRoot: <domain_dns_name>
+ */
+
+/*
+ * LDAP modify 1st LDAP connection:
+ *
+ * see: becomeDC_ldap1_server_object_modify()
+ *
+ * Request (add):
+ * CN=<new_dc_netbios_name>,CN=Servers,CN=<new_dc_site_name>,CN=Sites,CN=Configuration,<domain_partition>>
+ * serverReference:CN=<new_dc_netbios_name>,CN=Computers,<domain_partition>
+ * Result:
+ * <attributeOrValueExist>
+ */
+
+/*
+ * LDAP modify 1st LDAP connection:
+ *
+ * see: becomeDC_ldap1_server_object_modify()
+ *
+ * Request (replace):
+ * CN=<new_dc_netbios_name>,CN=Servers,CN=<new_dc_site_name>,CN=Sites,CN=Configuration,<domain_partition>>
+ * serverReference:CN=<new_dc_netbios_name>,CN=Computers,<domain_partition>
+ * Result:
+ * <success>
+ */
+
+/*
+ * Open 1st DRSUAPI connection to the DC using admin credentials
+ * DsBind with DRSUAPI_DS_BIND_GUID_W2K3 ("6afab99c-6e26-464a-975f-f58f105218bc")
+ * (w2k3 does 2 DsBind() calls here..., where is first is unused and contains garbage at the end)
+ *
+ * see: becomeDC_drsuapi_connect_send(), becomeDC_drsuapi1_connect_recv(),
+ * becomeDC_drsuapi_bind_send(), becomeDC_drsuapi_bind_recv() and becomeDC_drsuapi1_bind_recv()
+ */
+
+/*
+ * DsAddEntry to create the CN=NTDS Settings,CN=<machine_name>,CN=Servers,CN=Default-First-Site-Name, ...
+ * on the 1st DRSUAPI connection
+ *
+ * see: becomeDC_drsuapi1_add_entry_send() and becomeDC_drsuapi1_add_entry_recv()
+ */
+
+/***************************************************************
+ * Add this stage we call the prepare_db() callback function
+ * of the caller, to see if he wants us to continue
+ *
+ * see: becomeDC_prepare_db()
+ ***************************************************************/
+
+/*
+ * Open 2nd and 3rd DRSUAPI connection to the DC using admin credentials
+ * - a DsBind with DRSUAPI_DS_BIND_GUID_W2K3 ("6afab99c-6e26-464a-975f-f58f105218bc")
+ * on the 2nd connection
+ *
+ * see: becomeDC_drsuapi_connect_send(), becomeDC_drsuapi2_connect_recv(),
+ * becomeDC_drsuapi_bind_send(), becomeDC_drsuapi_bind_recv(), becomeDC_drsuapi2_bind_recv()
+ * and becomeDC_drsuapi3_connect_recv()
+ */
+
+/*
+ * replicate CN=Schema,CN=Configuration,...
+ * on the 3rd DRSUAPI connection and the bind_handle from the 2nd connection
+ *
+ * see: becomeDC_drsuapi_pull_partition_send(), becomeDC_drsuapi_pull_partition_recv(),
+ * becomeDC_drsuapi3_pull_schema_send() and becomeDC_drsuapi3_pull_schema_recv()
+ *
+ ***************************************************************
+ * Add this stage we call the schema_chunk() callback function
+ * for each replication message
+ ***************************************************************/
+
+/*
+ * replicate CN=Configuration,...
+ * on the 3rd DRSUAPI connection and the bind_handle from the 2nd connection
+ *
+ * see: becomeDC_drsuapi_pull_partition_send(), becomeDC_drsuapi_pull_partition_recv(),
+ * becomeDC_drsuapi3_pull_config_send() and becomeDC_drsuapi3_pull_config_recv()
+ *
+ ***************************************************************
+ * Add this stage we call the config_chunk() callback function
+ * for each replication message
+ ***************************************************************/
+
+/*
+ * LDAP unbind on the 1st LDAP connection
+ *
+ * not implemented, because it's not needed...
+ */
+
+/*
+ * Open 2nd LDAP connection to the DC using admin credentials
+ *
+ * see: becomeDC_connect_ldap2() and becomeDC_ldap_connect()
+ */
+
+/*
+ * LDAP search 2nd LDAP connection:
+ *
+ * not implemented because it gives no new information
+ * same as becomeDC_ldap1_computer_object()
+ *
+ * Request:
+ * basedn: <domain_partition>
+ * scope: sub
+ * filter: (&(|(objectClass=user)(objectClass=computer))(sAMAccountName=<new_dc_account_name>))
+ * attrs: distinguishedName
+ * userAccountControl
+ * Result:
+ * CN=<new_dc_netbios_name>,CN=Computers,<domain_partition>
+ * distinguishedName: CN=<new_dc_netbios_name>,CN=Computers,<domain_partition>
+ * userAccoountControl: 4096 <0x00001000>
+ */
+
+/*
+ * LDAP search 2nd LDAP connection:
+ *
+ * not implemented because it gives no new information
+ * same as becomeDC_ldap1_computer_object()
+ *
+ * Request:
+ * basedn: CN=<new_dc_netbios_name>,CN=Computers,<domain_partition>
+ * scope: base
+ * filter: (objectClass=*)
+ * attrs: userAccountControl
+ * Result:
+ * CN=<new_dc_netbios_name>,CN=Computers,<domain_partition>
+ * userAccoountControl: 4096 <0x00001000>
+ */
+
+/*
+ * LDAP modify 2nd LDAP connection:
+ *
+ * see: becomeDC_ldap2_modify_computer()
+ *
+ * Request (replace):
+ * CN=<new_dc_netbios_name>,CN=Computers,<domain_partition>
+ * userAccoountControl: 532480 <0x82000>
+ * Result:
+ * <success>
+ */
+
+/*
+ * LDAP search 2nd LDAP connection:
+ *
+ * see: becomeDC_ldap2_move_computer()
+ *
+ * Request:
+ * basedn: <WKGUID=2fbac1870ade11d297c400c04fd8d5cd,<domain_partition>>
+ * scope: base
+ * filter: (objectClass=*)
+ * attrs: 1.1
+ * Result:
+ * CN=Domain Controllers,<domain_partition>
+ */
+
+/*
+ * LDAP search 2nd LDAP connection:
+ *
+ * not implemented because it gives no new information
+ *
+ * Request:
+ * basedn: CN=Domain Controllers,<domain_partition>
+ * scope: base
+ * filter: (objectClass=*)
+ * attrs: distinguishedName
+ * Result:
+ * CN=Domain Controller,<domain_partition>
+ * distinguishedName: CN=Domain Controllers,<domain_partition>
+ */
+
+/*
+ * LDAP modifyRDN 2nd LDAP connection:
+ *
+ * see: becomeDC_ldap2_move_computer()
+ *
+ * Request:
+ * entry: CN=<new_dc_netbios_name>,CN=Computers,<domain_partition>
+ * newrdn: CN=<new_dc_netbios_name>
+ * deleteoldrdn: TRUE
+ * newparent: CN=Domain Controllers,<domain_partition>
+ * Result:
+ * <success>
+ */
+
+/*
+ * LDAP unbind on the 2nd LDAP connection
+ *
+ * not implemented, because it's not needed...
+ */
+
+/*
+ * replicate Domain Partition
+ * on the 3rd DRSUAPI connection and the bind_handle from the 2nd connection
+ *
+ * see: becomeDC_drsuapi_pull_partition_send(), becomeDC_drsuapi_pull_partition_recv(),
+ * becomeDC_drsuapi3_pull_domain_send() and becomeDC_drsuapi3_pull_domain_recv()
+ *
+ ***************************************************************
+ * Add this stage we call the domain_chunk() callback function
+ * for each replication message
+ ***************************************************************/
+
+/* call DsReplicaUpdateRefs() for all partitions like this:
+ * req1: struct drsuapi_DsReplicaUpdateRefsRequest1
+ *
+ * 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 : '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=<machine_name>,CN=Servers,CN=Default-First-Site-Name, ...
+ * on the 2nd!!! DRSUAPI connection
+ *
+ * see: becomeDC_drsuapi_update_refs_send(), becomeDC_drsuapi2_update_refs_schema_recv(),
+ * becomeDC_drsuapi2_update_refs_config_recv() and becomeDC_drsuapi2_update_refs_domain_recv()
+ */
+
+/*
+ * Windows does opens the 4th and 5th DRSUAPI connection...
+ * and does a DsBind() with the objectGUID from DsAddEntry() as bind_guid
+ * on the 4th connection
+ *
+ * and then 2 full replications of the domain partition on the 5th connection
+ * with the bind_handle from the 4th connection
+ *
+ * not implemented because it gives no new information
+ */
+
+struct libnet_BecomeDC_state {
+ struct composite_context *creq;
+
+ struct libnet_context *libnet;
+
+ struct dom_sid zero_sid;
+
+ struct {
+ struct cldap_socket *sock;
+ struct cldap_netlogon io;
+ struct NETLOGON_SAM_LOGON_RESPONSE_EX netlogon;
+ } cldap;
+
+ struct becomeDC_ldap {
+ struct ldb_context *ldb;
+ const struct ldb_message *rootdse;
+ } ldap1, ldap2;
+
+ struct becomeDC_drsuapi {
+ struct libnet_BecomeDC_state *s;
+ struct dcerpc_binding *binding;
+ struct dcerpc_pipe *pipe;
+ DATA_BLOB gensec_skey;
+ struct drsuapi_DsBind bind_r;
+ struct GUID bind_guid;
+ struct drsuapi_DsBindInfoCtr bind_info_ctr;
+ struct drsuapi_DsBindInfo28 local_info28;
+ struct drsuapi_DsBindInfo28 remote_info28;
+ struct policy_handle bind_handle;
+ } drsuapi1, drsuapi2, drsuapi3;
+
+ struct libnet_BecomeDC_Domain domain;
+ struct libnet_BecomeDC_Forest forest;
+ struct libnet_BecomeDC_SourceDSA source_dsa;
+ struct libnet_BecomeDC_DestDSA dest_dsa;
+
+ struct libnet_BecomeDC_Partition schema_part, config_part, domain_part;
+
+ struct becomeDC_fsmo {
+ const char *dns_name;
+ const char *server_dn_str;
+ const char *ntds_dn_str;
+ struct GUID ntds_guid;
+ } infrastructure_fsmo;
+
+ struct becomeDC_fsmo rid_manager_fsmo;
+
+ struct libnet_BecomeDC_CheckOptions _co;
+ struct libnet_BecomeDC_PrepareDB _pp;
+ struct libnet_BecomeDC_StoreChunk _sc;
+ struct libnet_BecomeDC_Callbacks callbacks;
+};
+
+static void becomeDC_recv_cldap(struct cldap_request *req);
+
+static void becomeDC_send_cldap(struct libnet_BecomeDC_state *s)
+{
+ struct composite_context *c = s->creq;
+ struct cldap_request *req;
+
+ s->cldap.io.in.dest_address = s->source_dsa.address;
+ s->cldap.io.in.dest_port = lp_cldap_port(s->libnet->lp_ctx);
+ s->cldap.io.in.realm = s->domain.dns_name;
+ s->cldap.io.in.host = s->dest_dsa.netbios_name;
+ s->cldap.io.in.user = NULL;
+ s->cldap.io.in.domain_guid = NULL;
+ s->cldap.io.in.domain_sid = NULL;
+ s->cldap.io.in.acct_control = -1;
+ s->cldap.io.in.version = NETLOGON_NT_VERSION_5 | NETLOGON_NT_VERSION_5EX;
+ s->cldap.io.in.map_response = true;
+
+ s->cldap.sock = cldap_socket_init(s, s->libnet->event_ctx,
+ lp_iconv_convenience(s->libnet->lp_ctx));
+ if (composite_nomem(s->cldap.sock, c)) return;
+
+ req = cldap_netlogon_send(s->cldap.sock, &s->cldap.io);
+ if (composite_nomem(req, c)) return;
+ req->async.fn = becomeDC_recv_cldap;
+ req->async.private_data = s;
+}
+
+static void becomeDC_connect_ldap1(struct libnet_BecomeDC_state *s);
+
+static void becomeDC_recv_cldap(struct cldap_request *req)
+{
+ struct libnet_BecomeDC_state *s = talloc_get_type(req->async.private_data,
+ struct libnet_BecomeDC_state);
+ struct composite_context *c = s->creq;
+
+ c->status = cldap_netlogon_recv(req, s, &s->cldap.io);
+ if (!composite_is_ok(c)) return;
+
+ s->cldap.netlogon = s->cldap.io.out.netlogon.data.nt5_ex;
+
+ s->domain.dns_name = s->cldap.netlogon.dns_domain;
+ s->domain.netbios_name = s->cldap.netlogon.domain;
+ s->domain.guid = s->cldap.netlogon.domain_uuid;
+
+ s->forest.dns_name = s->cldap.netlogon.forest;
+
+ s->source_dsa.dns_name = s->cldap.netlogon.pdc_dns_name;
+ s->source_dsa.netbios_name = s->cldap.netlogon.pdc_name;
+ s->source_dsa.site_name = s->cldap.netlogon.server_site;
+
+ s->dest_dsa.site_name = s->cldap.netlogon.client_site;
+
+ becomeDC_connect_ldap1(s);
+}
+
+static NTSTATUS becomeDC_ldap_connect(struct libnet_BecomeDC_state *s,
+ struct becomeDC_ldap *ldap)
+{
+ char *url;
+
+ url = talloc_asprintf(s, "ldap://%s/", s->source_dsa.dns_name);
+ NT_STATUS_HAVE_NO_MEMORY(url);
+
+ ldap->ldb = ldb_wrap_connect(s, s->libnet->event_ctx, s->libnet->lp_ctx, url,
+ NULL,
+ s->libnet->cred,
+ 0, NULL);
+ talloc_free(url);
+ if (ldap->ldb == NULL) {
+ return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS becomeDC_ldap1_rootdse(struct libnet_BecomeDC_state *s)
+{
+ int ret;
+ struct ldb_result *r;
+ struct ldb_dn *basedn;
+ static const char *attrs[] = {
+ "*",
+ NULL
+ };
+
+ basedn = ldb_dn_new(s, s->ldap1.ldb, NULL);
+ NT_STATUS_HAVE_NO_MEMORY(basedn);
+
+ ret = ldb_search(s->ldap1.ldb, s, &r, basedn, LDB_SCOPE_BASE, attrs,
+ "(objectClass=*)");
+ talloc_free(basedn);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_LDAP(ret);
+ } else if (r->count != 1) {
+ talloc_free(r);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ s->ldap1.rootdse = r->msgs[0];
+
+ s->domain.dn_str = ldb_msg_find_attr_as_string(s->ldap1.rootdse, "defaultNamingContext", NULL);
+ if (!s->domain.dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;
+
+ s->forest.root_dn_str = ldb_msg_find_attr_as_string(s->ldap1.rootdse, "rootDomainNamingContext", NULL);
+ if (!s->forest.root_dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ s->forest.config_dn_str = ldb_msg_find_attr_as_string(s->ldap1.rootdse, "configurationNamingContext", NULL);
+ if (!s->forest.config_dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ s->forest.schema_dn_str = ldb_msg_find_attr_as_string(s->ldap1.rootdse, "schemaNamingContext", NULL);
+ if (!s->forest.schema_dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;
+
+ s->source_dsa.server_dn_str = ldb_msg_find_attr_as_string(s->ldap1.rootdse, "serverName", NULL);
+ if (!s->source_dsa.server_dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ s->source_dsa.ntds_dn_str = ldb_msg_find_attr_as_string(s->ldap1.rootdse, "dsServiceName", NULL);
+ if (!s->source_dsa.ntds_dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS becomeDC_ldap1_crossref_behavior_version(struct libnet_BecomeDC_state *s)
+{
+ int ret;
+ struct ldb_result *r;
+ struct ldb_dn *basedn;
+ static const char *attrs[] = {
+ "msDs-Behavior-Version",
+ NULL
+ };
+
+ basedn = ldb_dn_new(s, s->ldap1.ldb, s->forest.config_dn_str);
+ NT_STATUS_HAVE_NO_MEMORY(basedn);
+
+ ret = ldb_search(s->ldap1.ldb, s, &r, basedn, LDB_SCOPE_ONELEVEL, attrs,
+ "(cn=Partitions)");
+ talloc_free(basedn);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_LDAP(ret);
+ } else if (r->count != 1) {
+ talloc_free(r);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ s->forest.crossref_behavior_version = ldb_msg_find_attr_as_uint(r->msgs[0], "msDs-Behavior-Version", 0);
+
+ talloc_free(r);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS becomeDC_ldap1_domain_behavior_version(struct libnet_BecomeDC_state *s)
+{
+ int ret;
+ struct ldb_result *r;
+ struct ldb_dn *basedn;
+ static const char *attrs[] = {
+ "msDs-Behavior-Version",
+ NULL
+ };
+
+ basedn = ldb_dn_new(s, s->ldap1.ldb, s->domain.dn_str);
+ NT_STATUS_HAVE_NO_MEMORY(basedn);
+
+ ret = ldb_search(s->ldap1.ldb, s, &r, basedn, LDB_SCOPE_BASE, attrs,
+ "(objectClass=*)");
+ talloc_free(basedn);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_LDAP(ret);
+ } else if (r->count != 1) {
+ talloc_free(r);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ s->domain.behavior_version = ldb_msg_find_attr_as_uint(r->msgs[0], "msDs-Behavior-Version", 0);
+
+ talloc_free(r);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS becomeDC_ldap1_schema_object_version(struct libnet_BecomeDC_state *s)
+{
+ int ret;
+ struct ldb_result *r;
+ struct ldb_dn *basedn;
+ static const char *attrs[] = {
+ "objectVersion",
+ NULL
+ };
+
+ basedn = ldb_dn_new(s, s->ldap1.ldb, s->forest.schema_dn_str);
+ NT_STATUS_HAVE_NO_MEMORY(basedn);
+
+ ret = ldb_search(s->ldap1.ldb, s, &r, basedn, LDB_SCOPE_BASE, attrs,
+ "(objectClass=*)");
+ talloc_free(basedn);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_LDAP(ret);
+ } else if (r->count != 1) {
+ talloc_free(r);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ s->forest.schema_object_version = ldb_msg_find_attr_as_uint(r->msgs[0], "objectVersion", 0);
+
+ talloc_free(r);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS becomeDC_ldap1_w2k3_update_revision(struct libnet_BecomeDC_state *s)
+{
+ int ret;
+ struct ldb_result *r;
+ struct ldb_dn *basedn;
+ static const char *attrs[] = {
+ "revision",
+ NULL
+ };
+
+ basedn = ldb_dn_new_fmt(s, s->ldap1.ldb, "CN=Windows2003Update,CN=DomainUpdates,CN=System,%s",
+ s->domain.dn_str);
+ NT_STATUS_HAVE_NO_MEMORY(basedn);
+
+ ret = ldb_search(s->ldap1.ldb, s, &r, basedn, LDB_SCOPE_BASE, attrs,
+ "(objectClass=*)");
+ talloc_free(basedn);
+ if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+ /* w2k doesn't have this object */
+ s->domain.w2k3_update_revision = 0;
+ return NT_STATUS_OK;
+ } else if (ret != LDB_SUCCESS) {
+ return NT_STATUS_LDAP(ret);
+ } else if (r->count != 1) {
+ talloc_free(r);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ s->domain.w2k3_update_revision = ldb_msg_find_attr_as_uint(r->msgs[0], "revision", 0);
+
+ talloc_free(r);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS becomeDC_ldap1_infrastructure_fsmo(struct libnet_BecomeDC_state *s)
+{
+ int ret;
+ struct ldb_result *r;
+ struct ldb_dn *basedn;
+ struct ldb_dn *ntds_dn;
+ struct ldb_dn *server_dn;
+ static const char *_1_1_attrs[] = {
+ "1.1",
+ NULL
+ };
+ static const char *fsmo_attrs[] = {
+ "fSMORoleOwner",
+ NULL
+ };
+ static const char *dns_attrs[] = {
+ "dnsHostName",
+ NULL
+ };
+ static const char *guid_attrs[] = {
+ "objectGUID",
+ NULL
+ };
+
+ basedn = ldb_dn_new_fmt(s, s->ldap1.ldb, "<WKGUID=2fbac1870ade11d297c400c04fd8d5cd,%s>",
+ s->domain.dn_str);
+ NT_STATUS_HAVE_NO_MEMORY(basedn);
+
+ ret = ldb_search(s->ldap1.ldb, s, &r, basedn, LDB_SCOPE_BASE,
+ _1_1_attrs, "(objectClass=*)");
+ talloc_free(basedn);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_LDAP(ret);
+ } else if (r->count != 1) {
+ talloc_free(r);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ basedn = talloc_steal(s, r->msgs[0]->dn);
+ talloc_free(r);
+
+ ret = ldb_search(s->ldap1.ldb, s, &r, basedn, LDB_SCOPE_BASE,
+ fsmo_attrs, "(objectClass=*)");
+ talloc_free(basedn);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_LDAP(ret);
+ } else if (r->count != 1) {
+ talloc_free(r);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ s->infrastructure_fsmo.ntds_dn_str = samdb_result_string(r->msgs[0], "fSMORoleOwner", NULL);
+ if (!s->infrastructure_fsmo.ntds_dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ talloc_steal(s, s->infrastructure_fsmo.ntds_dn_str);
+
+ talloc_free(r);
+
+ ntds_dn = ldb_dn_new(s, s->ldap1.ldb, s->infrastructure_fsmo.ntds_dn_str);
+ NT_STATUS_HAVE_NO_MEMORY(ntds_dn);
+
+ server_dn = ldb_dn_get_parent(s, ntds_dn);
+ NT_STATUS_HAVE_NO_MEMORY(server_dn);
+
+ s->infrastructure_fsmo.server_dn_str = ldb_dn_alloc_linearized(s, server_dn);
+ NT_STATUS_HAVE_NO_MEMORY(s->infrastructure_fsmo.server_dn_str);
+
+ ret = ldb_search(s->ldap1.ldb, s, &r, server_dn, LDB_SCOPE_BASE,
+ dns_attrs, "(objectClass=*)");
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_LDAP(ret);
+ } else if (r->count != 1) {
+ talloc_free(r);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ s->infrastructure_fsmo.dns_name = samdb_result_string(r->msgs[0], "dnsHostName", NULL);
+ if (!s->infrastructure_fsmo.dns_name) return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ talloc_steal(s, s->infrastructure_fsmo.dns_name);
+
+ talloc_free(r);
+
+ ret = ldb_search(s->ldap1.ldb, s, &r, ntds_dn, LDB_SCOPE_BASE,
+ guid_attrs, "(objectClass=*)");
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_LDAP(ret);
+ } else if (r->count != 1) {
+ talloc_free(r);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ s->infrastructure_fsmo.ntds_guid = samdb_result_guid(r->msgs[0], "objectGUID");
+
+ talloc_free(r);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS becomeDC_ldap1_rid_manager_fsmo(struct libnet_BecomeDC_state *s)
+{
+ int ret;
+ struct ldb_result *r;
+ struct ldb_dn *basedn;
+ const char *reference_dn_str;
+ struct ldb_dn *ntds_dn;
+ struct ldb_dn *server_dn;
+ static const char *rid_attrs[] = {
+ "rIDManagerReference",
+ NULL
+ };
+ static const char *fsmo_attrs[] = {
+ "fSMORoleOwner",
+ NULL
+ };
+ static const char *dns_attrs[] = {
+ "dnsHostName",
+ NULL
+ };
+ static const char *guid_attrs[] = {
+ "objectGUID",
+ NULL
+ };
+
+ basedn = ldb_dn_new(s, s->ldap1.ldb, s->domain.dn_str);
+ NT_STATUS_HAVE_NO_MEMORY(basedn);
+
+ ret = ldb_search(s->ldap1.ldb, s, &r, basedn, LDB_SCOPE_BASE,
+ rid_attrs, "(objectClass=*)");
+ talloc_free(basedn);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_LDAP(ret);
+ } else if (r->count != 1) {
+ talloc_free(r);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ reference_dn_str = samdb_result_string(r->msgs[0], "rIDManagerReference", NULL);
+ if (!reference_dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;
+
+ basedn = ldb_dn_new(s, s->ldap1.ldb, reference_dn_str);
+ NT_STATUS_HAVE_NO_MEMORY(basedn);
+
+ talloc_free(r);
+
+ ret = ldb_search(s->ldap1.ldb, s, &r, basedn, LDB_SCOPE_BASE,
+ fsmo_attrs, "(objectClass=*)");
+ talloc_free(basedn);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_LDAP(ret);
+ } else if (r->count != 1) {
+ talloc_free(r);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ s->rid_manager_fsmo.ntds_dn_str = samdb_result_string(r->msgs[0], "fSMORoleOwner", NULL);
+ if (!s->rid_manager_fsmo.ntds_dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ talloc_steal(s, s->rid_manager_fsmo.ntds_dn_str);
+
+ talloc_free(r);
+
+ ntds_dn = ldb_dn_new(s, s->ldap1.ldb, s->rid_manager_fsmo.ntds_dn_str);
+ NT_STATUS_HAVE_NO_MEMORY(ntds_dn);
+
+ server_dn = ldb_dn_get_parent(s, ntds_dn);
+ NT_STATUS_HAVE_NO_MEMORY(server_dn);
+
+ s->rid_manager_fsmo.server_dn_str = ldb_dn_alloc_linearized(s, server_dn);
+ NT_STATUS_HAVE_NO_MEMORY(s->rid_manager_fsmo.server_dn_str);
+
+ ret = ldb_search(s->ldap1.ldb, s, &r, server_dn, LDB_SCOPE_BASE,
+ dns_attrs, "(objectClass=*)");
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_LDAP(ret);
+ } else if (r->count != 1) {
+ talloc_free(r);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ s->rid_manager_fsmo.dns_name = samdb_result_string(r->msgs[0], "dnsHostName", NULL);
+ if (!s->rid_manager_fsmo.dns_name) return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ talloc_steal(s, s->rid_manager_fsmo.dns_name);
+
+ talloc_free(r);
+
+ ret = ldb_search(s->ldap1.ldb, s, &r, ntds_dn, LDB_SCOPE_BASE,
+ guid_attrs, "(objectClass=*)");
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_LDAP(ret);
+ } else if (r->count != 1) {
+ talloc_free(r);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ s->rid_manager_fsmo.ntds_guid = samdb_result_guid(r->msgs[0], "objectGUID");
+
+ talloc_free(r);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS becomeDC_ldap1_site_object(struct libnet_BecomeDC_state *s)
+{
+ int ret;
+ struct ldb_result *r;
+ struct ldb_dn *basedn;
+
+ basedn = ldb_dn_new_fmt(s, s->ldap1.ldb, "CN=%s,CN=Sites,%s",
+ s->dest_dsa.site_name,
+ s->forest.config_dn_str);
+ NT_STATUS_HAVE_NO_MEMORY(basedn);
+
+ ret = ldb_search(s->ldap1.ldb, s, &r, basedn, LDB_SCOPE_BASE,
+ NULL, "(objectClass=*)");
+ talloc_free(basedn);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_LDAP(ret);
+ } else if (r->count != 1) {
+ talloc_free(r);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ s->dest_dsa.site_guid = samdb_result_guid(r->msgs[0], "objectGUID");
+
+ talloc_free(r);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS becomeDC_check_options(struct libnet_BecomeDC_state *s)
+{
+ if (!s->callbacks.check_options) return NT_STATUS_OK;
+
+ s->_co.domain = &s->domain;
+ s->_co.forest = &s->forest;
+ s->_co.source_dsa = &s->source_dsa;
+
+ return s->callbacks.check_options(s->callbacks.private_data, &s->_co);
+}
+
+static NTSTATUS becomeDC_ldap1_computer_object(struct libnet_BecomeDC_state *s)
+{
+ int ret;
+ struct ldb_result *r;
+ struct ldb_dn *basedn;
+ static const char *attrs[] = {
+ "distinguishedName",
+ "userAccountControl",
+ NULL
+ };
+
+ basedn = ldb_dn_new(s, s->ldap1.ldb, s->domain.dn_str);
+ NT_STATUS_HAVE_NO_MEMORY(basedn);
+
+ ret = ldb_search(s->ldap1.ldb, s, &r, basedn, LDB_SCOPE_SUBTREE, attrs,
+ "(&(|(objectClass=user)(objectClass=computer))(sAMAccountName=%s$))",
+ s->dest_dsa.netbios_name);
+ talloc_free(basedn);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_LDAP(ret);
+ } else if (r->count != 1) {
+ talloc_free(r);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ s->dest_dsa.computer_dn_str = samdb_result_string(r->msgs[0], "distinguishedName", NULL);
+ if (!s->dest_dsa.computer_dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ talloc_steal(s, s->dest_dsa.computer_dn_str);
+
+ s->dest_dsa.user_account_control = samdb_result_uint(r->msgs[0], "userAccountControl", 0);
+
+ talloc_free(r);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS becomeDC_ldap1_server_object_1(struct libnet_BecomeDC_state *s)
+{
+ int ret;
+ struct ldb_result *r;
+ struct ldb_dn *basedn;
+ const char *server_reference_dn_str;
+ struct ldb_dn *server_reference_dn;
+ struct ldb_dn *computer_dn;
+
+ basedn = ldb_dn_new_fmt(s, s->ldap1.ldb, "CN=%s,CN=Servers,CN=%s,CN=Sites,%s",
+ s->dest_dsa.netbios_name,
+ s->dest_dsa.site_name,
+ s->forest.config_dn_str);
+ NT_STATUS_HAVE_NO_MEMORY(basedn);
+
+ ret = ldb_search(s->ldap1.ldb, s, &r, basedn, LDB_SCOPE_BASE,
+ NULL, "(objectClass=*)");
+ talloc_free(basedn);
+ if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+ /* if the object doesn't exist, we'll create it later */
+ return NT_STATUS_OK;
+ } else if (ret != LDB_SUCCESS) {
+ return NT_STATUS_LDAP(ret);
+ } else if (r->count != 1) {
+ talloc_free(r);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ server_reference_dn_str = samdb_result_string(r->msgs[0], "serverReference", NULL);
+ if (server_reference_dn_str) {
+ server_reference_dn = ldb_dn_new(r, s->ldap1.ldb, server_reference_dn_str);
+ NT_STATUS_HAVE_NO_MEMORY(server_reference_dn);
+
+ computer_dn = ldb_dn_new(r, s->ldap1.ldb, s->dest_dsa.computer_dn_str);
+ NT_STATUS_HAVE_NO_MEMORY(computer_dn);
+
+ /*
+ * if the server object belongs to another DC in another domain in the forest,
+ * we should not touch this object!
+ */
+ if (ldb_dn_compare(computer_dn, server_reference_dn) != 0) {
+ talloc_free(r);
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+ }
+
+ /* if the server object is already for the dest_dsa, then we don't need to create it */
+ s->dest_dsa.server_dn_str = samdb_result_string(r->msgs[0], "distinguishedName", NULL);
+ if (!s->dest_dsa.server_dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ talloc_steal(s, s->dest_dsa.server_dn_str);
+
+ talloc_free(r);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS becomeDC_ldap1_server_object_2(struct libnet_BecomeDC_state *s)
+{
+ int ret;
+ struct ldb_result *r;
+ struct ldb_dn *basedn;
+ const char *server_reference_bl_dn_str;
+ static const char *attrs[] = {
+ "serverReferenceBL",
+ NULL
+ };
+
+ /* if the server_dn_str has a valid value, we skip this lookup */
+ if (s->dest_dsa.server_dn_str) return NT_STATUS_OK;
+
+ basedn = ldb_dn_new(s, s->ldap1.ldb, s->dest_dsa.computer_dn_str);
+ NT_STATUS_HAVE_NO_MEMORY(basedn);
+
+ ret = ldb_search(s->ldap1.ldb, s, &r, basedn, LDB_SCOPE_BASE,
+ attrs, "(objectClass=*)");
+ talloc_free(basedn);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_LDAP(ret);
+ } else if (r->count != 1) {
+ talloc_free(r);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ server_reference_bl_dn_str = samdb_result_string(r->msgs[0], "serverReferenceBL", NULL);
+ if (!server_reference_bl_dn_str) {
+ /* if no back link is present, we're done for this function */
+ talloc_free(r);
+ return NT_STATUS_OK;
+ }
+
+ /* if the server object is already for the dest_dsa, then we don't need to create it */
+ s->dest_dsa.server_dn_str = samdb_result_string(r->msgs[0], "serverReferenceBL", NULL);
+ if (s->dest_dsa.server_dn_str) {
+ /* if a back link is present, we know that the server object is present */
+ talloc_steal(s, s->dest_dsa.server_dn_str);
+ }
+
+ talloc_free(r);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS becomeDC_ldap1_server_object_add(struct libnet_BecomeDC_state *s)
+{
+ int ret;
+ struct ldb_message *msg;
+ char *server_dn_str;
+
+ /* if the server_dn_str has a valid value, we skip this lookup */
+ if (s->dest_dsa.server_dn_str) return NT_STATUS_OK;
+
+ msg = ldb_msg_new(s);
+ NT_STATUS_HAVE_NO_MEMORY(msg);
+
+ msg->dn = ldb_dn_new_fmt(msg, s->ldap1.ldb, "CN=%s,CN=Servers,CN=%s,CN=Sites,%s",
+ s->dest_dsa.netbios_name,
+ s->dest_dsa.site_name,
+ s->forest.config_dn_str);
+ NT_STATUS_HAVE_NO_MEMORY(msg->dn);
+
+ ret = ldb_msg_add_string(msg, "objectClass", "server");
+ if (ret != 0) {
+ talloc_free(msg);
+ return NT_STATUS_NO_MEMORY;
+ }
+ ret = ldb_msg_add_string(msg, "systemFlags", "50000000");
+ if (ret != 0) {
+ talloc_free(msg);
+ return NT_STATUS_NO_MEMORY;
+ }
+ ret = ldb_msg_add_string(msg, "serverReference", s->dest_dsa.computer_dn_str);
+ if (ret != 0) {
+ talloc_free(msg);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ server_dn_str = ldb_dn_alloc_linearized(s, msg->dn);
+ NT_STATUS_HAVE_NO_MEMORY(server_dn_str);
+
+ ret = ldb_add(s->ldap1.ldb, msg);
+ talloc_free(msg);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(server_dn_str);
+ return NT_STATUS_LDAP(ret);
+ }
+
+ s->dest_dsa.server_dn_str = server_dn_str;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS becomeDC_ldap1_server_object_modify(struct libnet_BecomeDC_state *s)
+{
+ int ret;
+ struct ldb_message *msg;
+ uint32_t i;
+
+ /* make a 'modify' msg, and only for serverReference */
+ msg = ldb_msg_new(s);
+ NT_STATUS_HAVE_NO_MEMORY(msg);
+ msg->dn = ldb_dn_new(msg, s->ldap1.ldb, s->dest_dsa.server_dn_str);
+ NT_STATUS_HAVE_NO_MEMORY(msg->dn);
+
+ ret = ldb_msg_add_string(msg, "serverReference", s->dest_dsa.computer_dn_str);
+ if (ret != 0) {
+ talloc_free(msg);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* mark all the message elements (should be just one)
+ as LDB_FLAG_MOD_ADD */
+ for (i=0;i<msg->num_elements;i++) {
+ msg->elements[i].flags = LDB_FLAG_MOD_ADD;
+ }
+
+ ret = ldb_modify(s->ldap1.ldb, msg);
+ if (ret == LDB_SUCCESS) {
+ talloc_free(msg);
+ return NT_STATUS_OK;
+ } else if (ret == LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS) {
+ /* retry with LDB_FLAG_MOD_REPLACE */
+ } else {
+ talloc_free(msg);
+ return NT_STATUS_LDAP(ret);
+ }
+
+ /* mark all the message elements (should be just one)
+ as LDB_FLAG_MOD_REPLACE */
+ for (i=0;i<msg->num_elements;i++) {
+ msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
+ }
+
+ ret = ldb_modify(s->ldap1.ldb, msg);
+ talloc_free(msg);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_LDAP(ret);
+ }
+
+ return NT_STATUS_OK;
+}
+
+static void becomeDC_drsuapi_connect_send(struct libnet_BecomeDC_state *s,
+ struct becomeDC_drsuapi *drsuapi,
+ void (*recv_fn)(struct composite_context *req));
+static void becomeDC_drsuapi1_connect_recv(struct composite_context *req);
+static void becomeDC_connect_ldap2(struct libnet_BecomeDC_state *s);
+
+static void becomeDC_connect_ldap1(struct libnet_BecomeDC_state *s)
+{
+ struct composite_context *c = s->creq;
+
+ c->status = becomeDC_ldap_connect(s, &s->ldap1);
+ if (!composite_is_ok(c)) return;
+
+ c->status = becomeDC_ldap1_rootdse(s);
+ if (!composite_is_ok(c)) return;
+
+ c->status = becomeDC_ldap1_crossref_behavior_version(s);
+ if (!composite_is_ok(c)) return;
+
+ c->status = becomeDC_ldap1_domain_behavior_version(s);
+ if (!composite_is_ok(c)) return;
+
+ c->status = becomeDC_ldap1_schema_object_version(s);
+ if (!composite_is_ok(c)) return;
+
+ c->status = becomeDC_ldap1_w2k3_update_revision(s);
+ if (!composite_is_ok(c)) return;
+
+ c->status = becomeDC_ldap1_infrastructure_fsmo(s);
+ if (!composite_is_ok(c)) return;
+
+ c->status = becomeDC_ldap1_rid_manager_fsmo(s);
+ if (!composite_is_ok(c)) return;
+
+ c->status = becomeDC_ldap1_site_object(s);
+ if (!composite_is_ok(c)) return;
+
+ c->status = becomeDC_check_options(s);
+ if (!composite_is_ok(c)) return;
+
+ c->status = becomeDC_ldap1_computer_object(s);
+ if (!composite_is_ok(c)) return;
+
+ c->status = becomeDC_ldap1_server_object_1(s);
+ if (!composite_is_ok(c)) return;
+
+ c->status = becomeDC_ldap1_server_object_2(s);
+ if (!composite_is_ok(c)) return;
+
+ c->status = becomeDC_ldap1_server_object_add(s);
+ if (!composite_is_ok(c)) return;
+
+ c->status = becomeDC_ldap1_server_object_modify(s);
+ if (!composite_is_ok(c)) return;
+
+ becomeDC_drsuapi_connect_send(s, &s->drsuapi1, becomeDC_drsuapi1_connect_recv);
+}
+
+static void becomeDC_drsuapi_connect_send(struct libnet_BecomeDC_state *s,
+ struct becomeDC_drsuapi *drsuapi,
+ void (*recv_fn)(struct composite_context *req))
+{
+ struct composite_context *c = s->creq;
+ struct composite_context *creq;
+ char *binding_str;
+
+ drsuapi->s = s;
+
+ if (!drsuapi->binding) {
+ const char *krb5_str = "";
+ const char *print_str = "";
+ /*
+ * Note: Replication only works with Windows 2000 when 'krb5' is
+ * passed as auth_type here. If NTLMSSP is used, Windows
+ * 2000 returns garbage in the DsGetNCChanges() response
+ * if encrypted password attributes would be in the response.
+ * That means the replication of the schema and configuration
+ * partition works fine, but it fails for the domain partition.
+ */
+ if (lp_parm_bool(s->libnet->lp_ctx, NULL, "become_dc",
+ "force krb5", true))
+ {
+ krb5_str = "krb5,";
+ }
+ if (lp_parm_bool(s->libnet->lp_ctx, NULL, "become_dc",
+ "print", false))
+ {
+ print_str = "print,";
+ }
+ binding_str = talloc_asprintf(s, "ncacn_ip_tcp:%s[%s%sseal]",
+ s->source_dsa.dns_name,
+ krb5_str, print_str);
+ if (composite_nomem(binding_str, c)) return;
+ c->status = dcerpc_parse_binding(s, binding_str, &drsuapi->binding);
+ talloc_free(binding_str);
+ if (!composite_is_ok(c)) return;
+ }
+
+ creq = dcerpc_pipe_connect_b_send(s, drsuapi->binding, &ndr_table_drsuapi,
+ s->libnet->cred, s->libnet->event_ctx,
+ s->libnet->lp_ctx);
+ composite_continue(c, creq, recv_fn, s);
+}
+
+static void becomeDC_drsuapi_bind_send(struct libnet_BecomeDC_state *s,
+ struct becomeDC_drsuapi *drsuapi,
+ void (*recv_fn)(struct rpc_request *req));
+static void becomeDC_drsuapi1_bind_recv(struct rpc_request *req);
+
+static void becomeDC_drsuapi1_connect_recv(struct composite_context *req)
+{
+ struct libnet_BecomeDC_state *s = talloc_get_type(req->async.private_data,
+ struct libnet_BecomeDC_state);
+ struct composite_context *c = s->creq;
+
+ c->status = dcerpc_pipe_connect_b_recv(req, s, &s->drsuapi1.pipe);
+ if (!composite_is_ok(c)) return;
+
+ c->status = gensec_session_key(s->drsuapi1.pipe->conn->security_state.generic_state,
+ &s->drsuapi1.gensec_skey);
+ if (!composite_is_ok(c)) return;
+
+ becomeDC_drsuapi_bind_send(s, &s->drsuapi1, becomeDC_drsuapi1_bind_recv);
+}
+
+static void becomeDC_drsuapi_bind_send(struct libnet_BecomeDC_state *s,
+ struct becomeDC_drsuapi *drsuapi,
+ void (*recv_fn)(struct rpc_request *req))
+{
+ struct composite_context *c = s->creq;
+ struct rpc_request *req;
+ struct drsuapi_DsBindInfo28 *bind_info28;
+
+ GUID_from_string(DRSUAPI_DS_BIND_GUID_W2K3, &drsuapi->bind_guid);
+
+ bind_info28 = &drsuapi->local_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;
+ if (s->domain.behavior_version == 2) {
+ /* TODO: find out how this is really triggered! */
+ 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_00100000;
+ 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;
+#if 0 /* we don't support XPRESS compression yet */
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_XPRESS_COMPRESS;
+#endif
+ bind_info28->site_guid = s->dest_dsa.site_guid;
+ bind_info28->pid = 0;
+ bind_info28->repl_epoch = 0;
+
+ drsuapi->bind_info_ctr.length = 28;
+ drsuapi->bind_info_ctr.info.info28 = *bind_info28;
+
+ drsuapi->bind_r.in.bind_guid = &drsuapi->bind_guid;
+ drsuapi->bind_r.in.bind_info = &drsuapi->bind_info_ctr;
+ drsuapi->bind_r.out.bind_handle = &drsuapi->bind_handle;
+
+ req = dcerpc_drsuapi_DsBind_send(drsuapi->pipe, s, &drsuapi->bind_r);
+ composite_continue_rpc(c, req, recv_fn, s);
+}
+
+static WERROR becomeDC_drsuapi_bind_recv(struct libnet_BecomeDC_state *s,
+ struct becomeDC_drsuapi *drsuapi)
+{
+ if (!W_ERROR_IS_OK(drsuapi->bind_r.out.result)) {
+ return drsuapi->bind_r.out.result;
+ }
+
+ ZERO_STRUCT(drsuapi->remote_info28);
+ if (drsuapi->bind_r.out.bind_info) {
+ switch (drsuapi->bind_r.out.bind_info->length) {
+ case 24: {
+ struct drsuapi_DsBindInfo24 *info24;
+ info24 = &drsuapi->bind_r.out.bind_info->info.info24;
+ drsuapi->remote_info28.supported_extensions = info24->supported_extensions;
+ drsuapi->remote_info28.site_guid = info24->site_guid;
+ drsuapi->remote_info28.pid = info24->pid;
+ drsuapi->remote_info28.repl_epoch = 0;
+ break;
+ }
+ case 48: {
+ struct drsuapi_DsBindInfo48 *info48;
+ info48 = &drsuapi->bind_r.out.bind_info->info.info48;
+ drsuapi->remote_info28.supported_extensions = info48->supported_extensions;
+ drsuapi->remote_info28.site_guid = info48->site_guid;
+ drsuapi->remote_info28.pid = info48->pid;
+ drsuapi->remote_info28.repl_epoch = info48->repl_epoch;
+ break;
+ }
+ case 28:
+ drsuapi->remote_info28 = drsuapi->bind_r.out.bind_info->info.info28;
+ break;
+ }
+ }
+
+ return WERR_OK;
+}
+
+static void becomeDC_drsuapi1_add_entry_send(struct libnet_BecomeDC_state *s);
+
+static void becomeDC_drsuapi1_bind_recv(struct rpc_request *req)
+{
+ struct libnet_BecomeDC_state *s = talloc_get_type(req->async.private_data,
+ struct libnet_BecomeDC_state);
+ struct composite_context *c = s->creq;
+ WERROR status;
+
+ bool print = false;
+
+ if (req->p->conn->flags & DCERPC_DEBUG_PRINT_OUT) {
+ print = true;
+ }
+
+ c->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(c)) return;
+
+ if (print) {
+ NDR_PRINT_OUT_DEBUG(drsuapi_DsBind, &s->drsuapi1.bind_r);
+ }
+
+ status = becomeDC_drsuapi_bind_recv(s, &s->drsuapi1);
+ if (!W_ERROR_IS_OK(status)) {
+ composite_error(c, werror_to_ntstatus(status));
+ return;
+ }
+
+ becomeDC_drsuapi1_add_entry_send(s);
+}
+
+static void becomeDC_drsuapi1_add_entry_recv(struct rpc_request *req);
+
+static void becomeDC_drsuapi1_add_entry_send(struct libnet_BecomeDC_state *s)
+{
+ struct composite_context *c = s->creq;
+ struct rpc_request *req;
+ struct drsuapi_DsAddEntry *r;
+ struct drsuapi_DsReplicaObjectIdentifier *identifier;
+ uint32_t num_attrs, i = 0;
+ struct drsuapi_DsReplicaAttribute *attrs;
+ struct smb_iconv_convenience *iconv_convenience = lp_iconv_convenience(s->libnet->lp_ctx);
+ enum ndr_err_code ndr_err;
+ bool w2k3;
+
+ /* choose a random invocationId */
+ s->dest_dsa.invocation_id = GUID_random();
+
+ /*
+ * if the schema version indicates w2k3, then
+ * also send some w2k3 specific attributes
+ */
+ if (s->forest.schema_object_version >= 30) {
+ w2k3 = true;
+ } else {
+ w2k3 = false;
+ }
+
+ r = talloc_zero(s, struct drsuapi_DsAddEntry);
+ if (composite_nomem(r, c)) return;
+
+ /* setup identifier */
+ identifier = talloc(r, struct drsuapi_DsReplicaObjectIdentifier);
+ if (composite_nomem(identifier, c)) return;
+ identifier->guid = GUID_zero();
+ identifier->sid = s->zero_sid;
+ identifier->dn = talloc_asprintf(identifier, "CN=NTDS Settings,%s",
+ s->dest_dsa.server_dn_str);
+ if (composite_nomem(identifier->dn, c)) return;
+
+ /* allocate attribute array */
+ num_attrs = 11;
+ attrs = talloc_array(r, struct drsuapi_DsReplicaAttribute, num_attrs);
+ if (composite_nomem(attrs, c)) return;
+
+ /* ntSecurityDescriptor */
+ {
+ struct drsuapi_DsAttributeValue *vs;
+ DATA_BLOB *vd;
+ struct security_descriptor *v;
+ struct dom_sid *domain_admins_sid;
+ const char *domain_admins_sid_str;
+
+ vs = talloc_array(attrs, struct drsuapi_DsAttributeValue, 1);
+ if (composite_nomem(vs, c)) return;
+
+ vd = talloc_array(vs, DATA_BLOB, 1);
+ if (composite_nomem(vd, c)) return;
+
+ domain_admins_sid = dom_sid_add_rid(vs, s->domain.sid, DOMAIN_RID_ADMINS);
+ if (composite_nomem(domain_admins_sid, c)) return;
+
+ domain_admins_sid_str = dom_sid_string(domain_admins_sid, domain_admins_sid);
+ if (composite_nomem(domain_admins_sid_str, c)) return;
+
+ v = security_descriptor_dacl_create(vd,
+ 0,
+ /* owner: domain admins */
+ domain_admins_sid_str,
+ /* owner group: domain admins */
+ domain_admins_sid_str,
+ /* authenticated users */
+ SID_NT_AUTHENTICATED_USERS,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ SEC_STD_READ_CONTROL |
+ SEC_ADS_LIST |
+ SEC_ADS_READ_PROP |
+ SEC_ADS_LIST_OBJECT,
+ 0,
+ /* domain admins */
+ domain_admins_sid_str,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ SEC_STD_REQUIRED |
+ SEC_ADS_CREATE_CHILD |
+ SEC_ADS_LIST |
+ SEC_ADS_SELF_WRITE |
+ SEC_ADS_READ_PROP |
+ SEC_ADS_WRITE_PROP |
+ SEC_ADS_DELETE_TREE |
+ SEC_ADS_LIST_OBJECT |
+ SEC_ADS_CONTROL_ACCESS,
+ 0,
+ /* system */
+ SID_NT_SYSTEM,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ SEC_STD_REQUIRED |
+ SEC_ADS_CREATE_CHILD |
+ SEC_ADS_DELETE_CHILD |
+ SEC_ADS_LIST |
+ SEC_ADS_SELF_WRITE |
+ SEC_ADS_READ_PROP |
+ SEC_ADS_WRITE_PROP |
+ SEC_ADS_DELETE_TREE |
+ SEC_ADS_LIST_OBJECT |
+ SEC_ADS_CONTROL_ACCESS,
+ 0,
+ /* end */
+ NULL);
+ if (composite_nomem(v, c)) return;
+
+ ndr_err = ndr_push_struct_blob(&vd[0], vd, iconv_convenience, v,(ndr_push_flags_fn_t)ndr_push_security_descriptor);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ c->status = ndr_map_error2ntstatus(ndr_err);
+ if (!composite_is_ok(c)) return;
+ }
+
+ vs[0].blob = &vd[0];
+
+ attrs[i].attid = DRSUAPI_ATTRIBUTE_ntSecurityDescriptor;
+ attrs[i].value_ctr.num_values = 1;
+ attrs[i].value_ctr.values = vs;
+
+ i++;
+ }
+
+ /* objectClass: nTDSDSA */
+ {
+ struct drsuapi_DsAttributeValue *vs;
+ DATA_BLOB *vd;
+
+ vs = talloc_array(attrs, struct drsuapi_DsAttributeValue, 1);
+ if (composite_nomem(vs, c)) return;
+
+ vd = talloc_array(vs, DATA_BLOB, 1);
+ if (composite_nomem(vd, c)) return;
+
+ vd[0] = data_blob_talloc(vd, NULL, 4);
+ if (composite_nomem(vd[0].data, c)) return;
+
+ /* value for nTDSDSA */
+ SIVAL(vd[0].data, 0, 0x0017002F);
+
+ vs[0].blob = &vd[0];
+
+ attrs[i].attid = DRSUAPI_ATTRIBUTE_objectClass;
+ attrs[i].value_ctr.num_values = 1;
+ attrs[i].value_ctr.values = vs;
+
+ i++;
+ }
+
+ /* objectCategory: CN=NTDS-DSA,CN=Schema,... */
+ {
+ struct drsuapi_DsAttributeValue *vs;
+ DATA_BLOB *vd;
+ struct drsuapi_DsReplicaObjectIdentifier3 v[1];
+
+ vs = talloc_array(attrs, struct drsuapi_DsAttributeValue, 1);
+ if (composite_nomem(vs, c)) return;
+
+ vd = talloc_array(vs, DATA_BLOB, 1);
+ if (composite_nomem(vd, c)) return;
+
+ v[0].guid = GUID_zero();
+ v[0].sid = s->zero_sid;
+ v[0].dn = talloc_asprintf(vd, "CN=NTDS-DSA,%s",
+ s->forest.schema_dn_str);
+ if (composite_nomem(v[0].dn, c)) return;
+
+ ndr_err = ndr_push_struct_blob(&vd[0], vd, iconv_convenience, &v[0],
+ (ndr_push_flags_fn_t)ndr_push_drsuapi_DsReplicaObjectIdentifier3);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ c->status = ndr_map_error2ntstatus(ndr_err);
+ if (!composite_is_ok(c)) return;
+ }
+
+ vs[0].blob = &vd[0];
+
+ attrs[i].attid = DRSUAPI_ATTRIBUTE_objectCategory;
+ attrs[i].value_ctr.num_values = 1;
+ attrs[i].value_ctr.values = vs;
+
+ i++;
+ }
+
+ /* invocationId: random guid */
+ {
+ struct drsuapi_DsAttributeValue *vs;
+ DATA_BLOB *vd;
+ const struct GUID *v;
+
+ vs = talloc_array(attrs, struct drsuapi_DsAttributeValue, 1);
+ if (composite_nomem(vs, c)) return;
+
+ vd = talloc_array(vs, DATA_BLOB, 1);
+ if (composite_nomem(vd, c)) return;
+
+ v = &s->dest_dsa.invocation_id;
+
+ ndr_err = ndr_push_struct_blob(&vd[0], vd, iconv_convenience, v, (ndr_push_flags_fn_t)ndr_push_GUID);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ c->status = ndr_map_error2ntstatus(ndr_err);
+ if (!composite_is_ok(c)) return;
+ }
+
+ vs[0].blob = &vd[0];
+
+ attrs[i].attid = DRSUAPI_ATTRIBUTE_invocationId;
+ attrs[i].value_ctr.num_values = 1;
+ attrs[i].value_ctr.values = vs;
+
+ i++;
+ }
+
+ /* hasMasterNCs: ... */
+ {
+ struct drsuapi_DsAttributeValue *vs;
+ DATA_BLOB *vd;
+ struct drsuapi_DsReplicaObjectIdentifier3 v[3];
+
+ vs = talloc_array(attrs, struct drsuapi_DsAttributeValue, 3);
+ if (composite_nomem(vs, c)) return;
+
+ vd = talloc_array(vs, DATA_BLOB, 3);
+ if (composite_nomem(vd, c)) return;
+
+ v[0].guid = GUID_zero();
+ v[0].sid = s->zero_sid;
+ v[0].dn = s->forest.config_dn_str;
+
+ v[1].guid = GUID_zero();
+ v[1].sid = s->zero_sid;
+ v[1].dn = s->domain.dn_str;
+
+ v[2].guid = GUID_zero();
+ v[2].sid = s->zero_sid;
+ v[2].dn = s->forest.schema_dn_str;
+
+ ndr_err = ndr_push_struct_blob(&vd[0], vd, iconv_convenience, &v[0],
+ (ndr_push_flags_fn_t)ndr_push_drsuapi_DsReplicaObjectIdentifier3);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ c->status = ndr_map_error2ntstatus(ndr_err);
+ if (!composite_is_ok(c)) return;
+ }
+
+ ndr_err = ndr_push_struct_blob(&vd[1], vd, iconv_convenience, &v[1],
+ (ndr_push_flags_fn_t)ndr_push_drsuapi_DsReplicaObjectIdentifier3);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ c->status = ndr_map_error2ntstatus(ndr_err);
+ if (!composite_is_ok(c)) return;
+ }
+
+ ndr_err = ndr_push_struct_blob(&vd[2], vd, iconv_convenience, &v[2],
+ (ndr_push_flags_fn_t)ndr_push_drsuapi_DsReplicaObjectIdentifier3);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ c->status = ndr_map_error2ntstatus(ndr_err);
+ if (!composite_is_ok(c)) return;
+ }
+
+ vs[0].blob = &vd[0];
+ vs[1].blob = &vd[1];
+ vs[2].blob = &vd[2];
+
+ attrs[i].attid = DRSUAPI_ATTRIBUTE_hasMasterNCs;
+ attrs[i].value_ctr.num_values = 3;
+ attrs[i].value_ctr.values = vs;
+
+ i++;
+ }
+
+ /* msDS-hasMasterNCs: ... */
+ if (w2k3) {
+ struct drsuapi_DsAttributeValue *vs;
+ DATA_BLOB *vd;
+ struct drsuapi_DsReplicaObjectIdentifier3 v[3];
+
+ vs = talloc_array(attrs, struct drsuapi_DsAttributeValue, 3);
+ if (composite_nomem(vs, c)) return;
+
+ vd = talloc_array(vs, DATA_BLOB, 3);
+ if (composite_nomem(vd, c)) return;
+
+ v[0].guid = GUID_zero();
+ v[0].sid = s->zero_sid;
+ v[0].dn = s->forest.config_dn_str;
+
+ v[1].guid = GUID_zero();
+ v[1].sid = s->zero_sid;
+ v[1].dn = s->domain.dn_str;
+
+ v[2].guid = GUID_zero();
+ v[2].sid = s->zero_sid;
+ v[2].dn = s->forest.schema_dn_str;
+
+ ndr_err = ndr_push_struct_blob(&vd[0], vd, iconv_convenience, &v[0],
+ (ndr_push_flags_fn_t)ndr_push_drsuapi_DsReplicaObjectIdentifier3);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ c->status = ndr_map_error2ntstatus(ndr_err);
+ if (!composite_is_ok(c)) return;
+ }
+
+ ndr_err = ndr_push_struct_blob(&vd[1], vd, iconv_convenience, &v[1],
+ (ndr_push_flags_fn_t)ndr_push_drsuapi_DsReplicaObjectIdentifier3);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ c->status = ndr_map_error2ntstatus(ndr_err);
+ if (!composite_is_ok(c)) return;
+ }
+
+ ndr_err = ndr_push_struct_blob(&vd[2], vd, iconv_convenience, &v[2],
+ (ndr_push_flags_fn_t)ndr_push_drsuapi_DsReplicaObjectIdentifier3);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ c->status = ndr_map_error2ntstatus(ndr_err);
+ if (!composite_is_ok(c)) return;
+ }
+
+ vs[0].blob = &vd[0];
+ vs[1].blob = &vd[1];
+ vs[2].blob = &vd[2];
+
+ attrs[i].attid = DRSUAPI_ATTRIBUTE_msDS_hasMasterNCs;
+ attrs[i].value_ctr.num_values = 3;
+ attrs[i].value_ctr.values = vs;
+
+ i++;
+ }
+
+ /* dMDLocation: CN=Schema,... */
+ {
+ struct drsuapi_DsAttributeValue *vs;
+ DATA_BLOB *vd;
+ struct drsuapi_DsReplicaObjectIdentifier3 v[1];
+
+ vs = talloc_array(attrs, struct drsuapi_DsAttributeValue, 1);
+ if (composite_nomem(vs, c)) return;
+
+ vd = talloc_array(vs, DATA_BLOB, 1);
+ if (composite_nomem(vd, c)) return;
+
+ v[0].guid = GUID_zero();
+ v[0].sid = s->zero_sid;
+ v[0].dn = s->forest.schema_dn_str;
+
+ ndr_err = ndr_push_struct_blob(&vd[0], vd, iconv_convenience, &v[0],
+ (ndr_push_flags_fn_t)ndr_push_drsuapi_DsReplicaObjectIdentifier3);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ c->status = ndr_map_error2ntstatus(ndr_err);
+ if (!composite_is_ok(c)) return;
+ }
+
+ vs[0].blob = &vd[0];
+
+ attrs[i].attid = DRSUAPI_ATTRIBUTE_dMDLocation;
+ attrs[i].value_ctr.num_values = 1;
+ attrs[i].value_ctr.values = vs;
+
+ i++;
+ }
+
+ /* msDS-HasDomainNCs: <domain_partition> */
+ if (w2k3) {
+ struct drsuapi_DsAttributeValue *vs;
+ DATA_BLOB *vd;
+ struct drsuapi_DsReplicaObjectIdentifier3 v[1];
+
+ vs = talloc_array(attrs, struct drsuapi_DsAttributeValue, 1);
+ if (composite_nomem(vs, c)) return;
+
+ vd = talloc_array(vs, DATA_BLOB, 1);
+ if (composite_nomem(vd, c)) return;
+
+ v[0].guid = GUID_zero();
+ v[0].sid = s->zero_sid;
+ v[0].dn = s->domain.dn_str;
+
+ ndr_err = ndr_push_struct_blob(&vd[0], vd, iconv_convenience, &v[0],
+ (ndr_push_flags_fn_t)ndr_push_drsuapi_DsReplicaObjectIdentifier3);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ c->status = ndr_map_error2ntstatus(ndr_err);
+ if (!composite_is_ok(c)) return;
+ }
+
+ vs[0].blob = &vd[0];
+
+ attrs[i].attid = DRSUAPI_ATTRIBUTE_msDS_HasDomainNCs;
+ attrs[i].value_ctr.num_values = 1;
+ attrs[i].value_ctr.values = vs;
+
+ i++;
+ }
+
+ /* msDS-Behavior-Version */
+ if (w2k3) {
+ struct drsuapi_DsAttributeValue *vs;
+ DATA_BLOB *vd;
+
+ vs = talloc_array(attrs, struct drsuapi_DsAttributeValue, 1);
+ if (composite_nomem(vs, c)) return;
+
+ vd = talloc_array(vs, DATA_BLOB, 1);
+ if (composite_nomem(vd, c)) return;
+
+ vd[0] = data_blob_talloc(vd, NULL, 4);
+ if (composite_nomem(vd[0].data, c)) return;
+
+ SIVAL(vd[0].data, 0, DS_BEHAVIOR_WIN2008);
+
+ vs[0].blob = &vd[0];
+
+ attrs[i].attid = DRSUAPI_ATTRIBUTE_msDS_Behavior_Version;
+ attrs[i].value_ctr.num_values = 1;
+ attrs[i].value_ctr.values = vs;
+
+ i++;
+ }
+
+ /* systemFlags */
+ {
+ struct drsuapi_DsAttributeValue *vs;
+ DATA_BLOB *vd;
+
+ vs = talloc_array(attrs, struct drsuapi_DsAttributeValue, 1);
+ if (composite_nomem(vs, c)) return;
+
+ vd = talloc_array(vs, DATA_BLOB, 1);
+ if (composite_nomem(vd, c)) return;
+
+ vd[0] = data_blob_talloc(vd, NULL, 4);
+ if (composite_nomem(vd[0].data, c)) return;
+
+ SIVAL(vd[0].data, 0, SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE);
+
+ vs[0].blob = &vd[0];
+
+ attrs[i].attid = DRSUAPI_ATTRIBUTE_systemFlags;
+ attrs[i].value_ctr.num_values = 1;
+ attrs[i].value_ctr.values = vs;
+
+ i++;
+ }
+
+ /* serverReference: ... */
+ {
+ struct drsuapi_DsAttributeValue *vs;
+ DATA_BLOB *vd;
+ struct drsuapi_DsReplicaObjectIdentifier3 v[1];
+
+ vs = talloc_array(attrs, struct drsuapi_DsAttributeValue, 1);
+ if (composite_nomem(vs, c)) return;
+
+ vd = talloc_array(vs, DATA_BLOB, 1);
+ if (composite_nomem(vd, c)) return;
+
+ v[0].guid = GUID_zero();
+ v[0].sid = s->zero_sid;
+ v[0].dn = s->dest_dsa.computer_dn_str;
+
+ ndr_err = ndr_push_struct_blob(&vd[0], vd, iconv_convenience, &v[0],
+ (ndr_push_flags_fn_t)ndr_push_drsuapi_DsReplicaObjectIdentifier3);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ c->status = ndr_map_error2ntstatus(ndr_err);
+ if (!composite_is_ok(c)) return;
+ }
+
+ vs[0].blob = &vd[0];
+
+ attrs[i].attid = DRSUAPI_ATTRIBUTE_serverReference;
+ attrs[i].value_ctr.num_values = 1;
+ attrs[i].value_ctr.values = vs;
+
+ i++;
+ }
+
+ /* truncate the attribute list to the attribute count we have filled in */
+ num_attrs = i;
+
+ /* setup request structure */
+ r->in.bind_handle = &s->drsuapi1.bind_handle;
+ r->in.level = 2;
+ r->in.req = talloc(s, union drsuapi_DsAddEntryRequest);
+ r->in.req->req2.first_object.next_object = NULL;
+ r->in.req->req2.first_object.object.identifier = identifier;
+ r->in.req->req2.first_object.object.flags = 0x00000000;
+ r->in.req->req2.first_object.object.attribute_ctr.num_attributes= num_attrs;
+ r->in.req->req2.first_object.object.attribute_ctr.attributes = attrs;
+
+ r->out.level_out = talloc(s, int32_t);
+ r->out.ctr = talloc(s, union drsuapi_DsAddEntryCtr);
+
+ req = dcerpc_drsuapi_DsAddEntry_send(s->drsuapi1.pipe, r, r);
+ composite_continue_rpc(c, req, becomeDC_drsuapi1_add_entry_recv, s);
+}
+
+static void becomeDC_drsuapi2_connect_recv(struct composite_context *req);
+static NTSTATUS becomeDC_prepare_db(struct libnet_BecomeDC_state *s);
+
+static void becomeDC_drsuapi1_add_entry_recv(struct rpc_request *req)
+{
+ struct libnet_BecomeDC_state *s = talloc_get_type(req->async.private_data,
+ struct libnet_BecomeDC_state);
+ struct composite_context *c = s->creq;
+ struct drsuapi_DsAddEntry *r = talloc_get_type(req->ndr.struct_ptr,
+ struct drsuapi_DsAddEntry);
+ char *binding_str;
+ bool print = false;
+
+ if (req->p->conn->flags & DCERPC_DEBUG_PRINT_OUT) {
+ print = true;
+ }
+
+ c->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(c)) return;
+
+ if (print) {
+ NDR_PRINT_OUT_DEBUG(drsuapi_DsAddEntry, r);
+ }
+
+ if (!W_ERROR_IS_OK(r->out.result)) {
+ composite_error(c, werror_to_ntstatus(r->out.result));
+ return;
+ }
+
+ if (*r->out.level_out == 3) {
+ if (r->out.ctr->ctr3.count != 1) {
+ WERROR status;
+
+ if (r->out.ctr->ctr3.level != 1) {
+ composite_error(c, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ if (!r->out.ctr->ctr3.error) {
+ composite_error(c, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ status = r->out.ctr->ctr3.error->info1.status;
+
+ if (!r->out.ctr->ctr3.error->info1.info) {
+ composite_error(c, werror_to_ntstatus(status));
+ return;
+ }
+
+ /* see if we can get a more detailed error */
+ switch (r->out.ctr->ctr3.error->info1.level) {
+ case 1:
+ status = r->out.ctr->ctr3.error->info1.info->error1.status;
+ break;
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ status = r->out.ctr->ctr3.error->info1.info->errorX.status;
+ break;
+ }
+
+ composite_error(c, werror_to_ntstatus(status));
+ return;
+ }
+
+ s->dest_dsa.ntds_guid = r->out.ctr->ctr3.objects[0].guid;
+ } else if (*r->out.level_out == 2) {
+ if (r->out.ctr->ctr2.count != 1) {
+ composite_error(c, werror_to_ntstatus(r->out.ctr->ctr2.error.status));
+ return;
+ }
+
+ s->dest_dsa.ntds_guid = r->out.ctr->ctr2.objects[0].guid;
+ } else {
+ composite_error(c, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ talloc_free(r);
+
+ s->dest_dsa.ntds_dn_str = talloc_asprintf(s, "CN=NTDS Settings,%s",
+ s->dest_dsa.server_dn_str);
+ if (composite_nomem(s->dest_dsa.ntds_dn_str, c)) return;
+
+ c->status = becomeDC_prepare_db(s);
+ if (!composite_is_ok(c)) return;
+
+ /* this avoids the epmapper lookup on the 2nd connection */
+ binding_str = dcerpc_binding_string(s, s->drsuapi1.binding);
+ if (composite_nomem(binding_str, c)) return;
+
+ c->status = dcerpc_parse_binding(s, binding_str, &s->drsuapi2.binding);
+ talloc_free(binding_str);
+ if (!composite_is_ok(c)) return;
+
+ /* w2k3 uses the same assoc_group_id as on the first connection, so we do */
+ s->drsuapi2.binding->assoc_group_id = s->drsuapi1.pipe->assoc_group_id;
+
+ becomeDC_drsuapi_connect_send(s, &s->drsuapi2, becomeDC_drsuapi2_connect_recv);
+}
+
+static NTSTATUS becomeDC_prepare_db(struct libnet_BecomeDC_state *s)
+{
+ if (!s->callbacks.prepare_db) return NT_STATUS_OK;
+
+ s->_pp.domain = &s->domain;
+ s->_pp.forest = &s->forest;
+ s->_pp.source_dsa = &s->source_dsa;
+ s->_pp.dest_dsa = &s->dest_dsa;
+
+ return s->callbacks.prepare_db(s->callbacks.private_data, &s->_pp);
+}
+
+static void becomeDC_drsuapi2_bind_recv(struct rpc_request *req);
+
+static void becomeDC_drsuapi2_connect_recv(struct composite_context *req)
+{
+ struct libnet_BecomeDC_state *s = talloc_get_type(req->async.private_data,
+ struct libnet_BecomeDC_state);
+ struct composite_context *c = s->creq;
+
+ c->status = dcerpc_pipe_connect_b_recv(req, s, &s->drsuapi2.pipe);
+ if (!composite_is_ok(c)) return;
+
+ c->status = gensec_session_key(s->drsuapi2.pipe->conn->security_state.generic_state,
+ &s->drsuapi2.gensec_skey);
+ if (!composite_is_ok(c)) return;
+
+ becomeDC_drsuapi_bind_send(s, &s->drsuapi2, becomeDC_drsuapi2_bind_recv);
+}
+
+static void becomeDC_drsuapi3_connect_recv(struct composite_context *req);
+
+static void becomeDC_drsuapi2_bind_recv(struct rpc_request *req)
+{
+ struct libnet_BecomeDC_state *s = talloc_get_type(req->async.private_data,
+ struct libnet_BecomeDC_state);
+ struct composite_context *c = s->creq;
+ char *binding_str;
+ WERROR status;
+
+ bool print = false;
+
+ if (req->p->conn->flags & DCERPC_DEBUG_PRINT_OUT) {
+ print = true;
+ }
+
+ c->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(c)) return;
+
+ if (print) {
+ NDR_PRINT_OUT_DEBUG(drsuapi_DsBind, &s->drsuapi2.bind_r);
+ }
+
+ status = becomeDC_drsuapi_bind_recv(s, &s->drsuapi2);
+ if (!W_ERROR_IS_OK(status)) {
+ composite_error(c, werror_to_ntstatus(status));
+ return;
+ }
+
+ /* this avoids the epmapper lookup on the 3rd connection */
+ binding_str = dcerpc_binding_string(s, s->drsuapi1.binding);
+ if (composite_nomem(binding_str, c)) return;
+
+ c->status = dcerpc_parse_binding(s, binding_str, &s->drsuapi3.binding);
+ talloc_free(binding_str);
+ if (!composite_is_ok(c)) return;
+
+ /* w2k3 uses the same assoc_group_id as on the first connection, so we do */
+ s->drsuapi3.binding->assoc_group_id = s->drsuapi1.pipe->assoc_group_id;
+ /* w2k3 uses the concurrent multiplex feature on the 3rd connection, so we do */
+ s->drsuapi3.binding->flags |= DCERPC_CONCURRENT_MULTIPLEX;
+
+ becomeDC_drsuapi_connect_send(s, &s->drsuapi3, becomeDC_drsuapi3_connect_recv);
+}
+
+static void becomeDC_drsuapi3_pull_schema_send(struct libnet_BecomeDC_state *s);
+
+static void becomeDC_drsuapi3_connect_recv(struct composite_context *req)
+{
+ struct libnet_BecomeDC_state *s = talloc_get_type(req->async.private_data,
+ struct libnet_BecomeDC_state);
+ struct composite_context *c = s->creq;
+
+ c->status = dcerpc_pipe_connect_b_recv(req, s, &s->drsuapi3.pipe);
+ if (!composite_is_ok(c)) return;
+
+ c->status = gensec_session_key(s->drsuapi3.pipe->conn->security_state.generic_state,
+ &s->drsuapi3.gensec_skey);
+ if (!composite_is_ok(c)) return;
+
+ becomeDC_drsuapi3_pull_schema_send(s);
+}
+
+static void becomeDC_drsuapi_pull_partition_send(struct libnet_BecomeDC_state *s,
+ struct becomeDC_drsuapi *drsuapi_h,
+ struct becomeDC_drsuapi *drsuapi_p,
+ struct libnet_BecomeDC_Partition *partition,
+ void (*recv_fn)(struct rpc_request *req))
+{
+ struct composite_context *c = s->creq;
+ struct rpc_request *req;
+ struct drsuapi_DsGetNCChanges *r;
+
+ r = talloc(s, struct drsuapi_DsGetNCChanges);
+ if (composite_nomem(r, c)) return;
+
+ r->out.level_out = talloc(r, int32_t);
+ if (composite_nomem(r->out.level_out, c)) return;
+ r->in.req = talloc(r, union drsuapi_DsGetNCChangesRequest);
+ if (composite_nomem(r->in.req, c)) return;
+ r->out.ctr = talloc(r, union drsuapi_DsGetNCChangesCtr);
+ if (composite_nomem(r->out.ctr, c)) return;
+
+ r->in.bind_handle = &drsuapi_h->bind_handle;
+ if (drsuapi_h->remote_info28.supported_extensions & DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8) {
+ r->in.level = 8;
+ r->in.req->req8.destination_dsa_guid = partition->destination_dsa_guid;
+ r->in.req->req8.source_dsa_invocation_id= partition->source_dsa_invocation_id;
+ r->in.req->req8.naming_context = &partition->nc;
+ r->in.req->req8.highwatermark = partition->highwatermark;
+ r->in.req->req8.uptodateness_vector = NULL;
+ r->in.req->req8.replica_flags = partition->replica_flags;
+ r->in.req->req8.max_object_count = 133;
+ r->in.req->req8.max_ndr_size = 1336811;
+ 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;
+ } else {
+ r->in.level = 5;
+ r->in.req->req5.destination_dsa_guid = partition->destination_dsa_guid;
+ r->in.req->req5.source_dsa_invocation_id= partition->source_dsa_invocation_id;
+ r->in.req->req5.naming_context = &partition->nc;
+ r->in.req->req5.highwatermark = partition->highwatermark;
+ r->in.req->req5.uptodateness_vector = NULL;
+ r->in.req->req5.replica_flags = partition->replica_flags;
+ 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;
+ }
+
+ /*
+ * we should try to use the drsuapi_p->pipe here, as w2k3 does
+ * but it seems that some extra flags in the DCERPC Bind call
+ * are needed for it. Or the same KRB5 TGS is needed on both
+ * connections.
+ */
+ req = dcerpc_drsuapi_DsGetNCChanges_send(drsuapi_p->pipe, r, r);
+ composite_continue_rpc(c, req, recv_fn, s);
+}
+
+static WERROR becomeDC_drsuapi_pull_partition_recv(struct libnet_BecomeDC_state *s,
+ struct becomeDC_drsuapi *drsuapi_h,
+ struct becomeDC_drsuapi *drsuapi_p,
+ struct libnet_BecomeDC_Partition *partition,
+ struct drsuapi_DsGetNCChanges *r)
+{
+ uint32_t ctr_level = 0;
+ struct drsuapi_DsGetNCChangesCtr1 *ctr1 = NULL;
+ struct drsuapi_DsGetNCChangesCtr6 *ctr6 = NULL;
+ struct GUID *source_dsa_guid;
+ struct GUID *source_dsa_invocation_id;
+ struct drsuapi_DsReplicaHighWaterMark *new_highwatermark;
+ bool more_data = false;
+ NTSTATUS nt_status;
+
+ if (!W_ERROR_IS_OK(r->out.result)) {
+ return r->out.result;
+ }
+
+ if (*r->out.level_out == 1) {
+ ctr_level = 1;
+ ctr1 = &r->out.ctr->ctr1;
+ } else if (*r->out.level_out == 2 &&
+ r->out.ctr->ctr2.mszip1.ts) {
+ ctr_level = 1;
+ ctr1 = &r->out.ctr->ctr2.mszip1.ts->ctr1;
+ } else if (*r->out.level_out == 6) {
+ ctr_level = 6;
+ ctr6 = &r->out.ctr->ctr6;
+ } else if (*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) {
+ ctr_level = 6;
+ ctr6 = &r->out.ctr->ctr7.ctr.mszip6.ts->ctr6;
+ } else if (*r->out.level_out == 7 &&
+ r->out.ctr->ctr7.level == 6 &&
+ r->out.ctr->ctr7.type == DRSUAPI_COMPRESSION_TYPE_XPRESS &&
+ r->out.ctr->ctr7.ctr.xpress6.ts) {
+ ctr_level = 6;
+ ctr6 = &r->out.ctr->ctr7.ctr.xpress6.ts->ctr6;
+ } else {
+ return WERR_BAD_NET_RESP;
+ }
+
+ if (!ctr1 && ! ctr6) {
+ return WERR_BAD_NET_RESP;
+ }
+
+ if (ctr_level == 6) {
+ if (!W_ERROR_IS_OK(ctr6->drs_error)) {
+ return ctr6->drs_error;
+ }
+ }
+
+ switch (ctr_level) {
+ case 1:
+ source_dsa_guid = &ctr1->source_dsa_guid;
+ source_dsa_invocation_id = &ctr1->source_dsa_invocation_id;
+ new_highwatermark = &ctr1->new_highwatermark;
+ more_data = ctr1->more_data;
+ break;
+ case 6:
+ source_dsa_guid = &ctr6->source_dsa_guid;
+ source_dsa_invocation_id = &ctr6->source_dsa_invocation_id;
+ new_highwatermark = &ctr6->new_highwatermark;
+ more_data = ctr6->more_data;
+ break;
+ }
+
+ partition->highwatermark = *new_highwatermark;
+ partition->source_dsa_guid = *source_dsa_guid;
+ partition->source_dsa_invocation_id = *source_dsa_invocation_id;
+ partition->more_data = more_data;
+
+ if (!partition->store_chunk) return WERR_OK;
+
+ s->_sc.domain = &s->domain;
+ s->_sc.forest = &s->forest;
+ s->_sc.source_dsa = &s->source_dsa;
+ s->_sc.dest_dsa = &s->dest_dsa;
+ s->_sc.partition = partition;
+ s->_sc.ctr_level = ctr_level;
+ s->_sc.ctr1 = ctr1;
+ s->_sc.ctr6 = ctr6;
+ /*
+ * we need to use the drsuapi_p->gensec_skey here,
+ * when we use drsuapi_p->pipe in the for this request
+ */
+ s->_sc.gensec_skey = &drsuapi_p->gensec_skey;
+
+ nt_status = partition->store_chunk(s->callbacks.private_data, &s->_sc);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return ntstatus_to_werror(nt_status);
+ }
+
+ return WERR_OK;
+}
+
+static void becomeDC_drsuapi3_pull_schema_recv(struct rpc_request *req);
+
+static void becomeDC_drsuapi3_pull_schema_send(struct libnet_BecomeDC_state *s)
+{
+ s->schema_part.nc.guid = GUID_zero();
+ s->schema_part.nc.sid = s->zero_sid;
+ s->schema_part.nc.dn = s->forest.schema_dn_str;
+
+ s->schema_part.destination_dsa_guid = s->drsuapi2.bind_guid;
+
+ s->schema_part.replica_flags = DRSUAPI_DS_REPLICA_NEIGHBOUR_WRITEABLE
+ | DRSUAPI_DS_REPLICA_NEIGHBOUR_SYNC_ON_STARTUP
+ | DRSUAPI_DS_REPLICA_NEIGHBOUR_DO_SCHEDULED_SYNCS
+ | DRSUAPI_DS_REPLICA_NEIGHBOUR_FULL_IN_PROGRESS
+ | DRSUAPI_DS_REPLICA_NEIGHBOUR_NEVER_SYNCED
+ | DRSUAPI_DS_REPLICA_NEIGHBOUR_COMPRESS_CHANGES;
+
+ s->schema_part.store_chunk = s->callbacks.schema_chunk;
+
+ becomeDC_drsuapi_pull_partition_send(s, &s->drsuapi2, &s->drsuapi3, &s->schema_part,
+ becomeDC_drsuapi3_pull_schema_recv);
+}
+
+static void becomeDC_drsuapi3_pull_config_send(struct libnet_BecomeDC_state *s);
+
+static void becomeDC_drsuapi3_pull_schema_recv(struct rpc_request *req)
+{
+ struct libnet_BecomeDC_state *s = talloc_get_type(req->async.private_data,
+ struct libnet_BecomeDC_state);
+ struct composite_context *c = s->creq;
+ struct drsuapi_DsGetNCChanges *r = talloc_get_type(req->ndr.struct_ptr,
+ struct drsuapi_DsGetNCChanges);
+ WERROR status;
+
+ bool print = false;
+
+ if (req->p->conn->flags & DCERPC_DEBUG_PRINT_OUT) {
+ print = true;
+ }
+
+ c->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(c)) return;
+
+ if (print) {
+ NDR_PRINT_OUT_DEBUG(drsuapi_DsGetNCChanges, r);
+ }
+
+ status = becomeDC_drsuapi_pull_partition_recv(s, &s->drsuapi2, &s->drsuapi3, &s->schema_part, r);
+ if (!W_ERROR_IS_OK(status)) {
+ composite_error(c, werror_to_ntstatus(status));
+ return;
+ }
+
+ talloc_free(r);
+
+ if (s->schema_part.more_data) {
+ becomeDC_drsuapi_pull_partition_send(s, &s->drsuapi2, &s->drsuapi3, &s->schema_part,
+ becomeDC_drsuapi3_pull_schema_recv);
+ return;
+ }
+
+ becomeDC_drsuapi3_pull_config_send(s);
+}
+
+static void becomeDC_drsuapi3_pull_config_recv(struct rpc_request *req);
+
+static void becomeDC_drsuapi3_pull_config_send(struct libnet_BecomeDC_state *s)
+{
+ s->config_part.nc.guid = GUID_zero();
+ s->config_part.nc.sid = s->zero_sid;
+ s->config_part.nc.dn = s->forest.config_dn_str;
+
+ s->config_part.destination_dsa_guid = s->drsuapi2.bind_guid;
+
+ s->config_part.replica_flags = DRSUAPI_DS_REPLICA_NEIGHBOUR_WRITEABLE
+ | DRSUAPI_DS_REPLICA_NEIGHBOUR_SYNC_ON_STARTUP
+ | DRSUAPI_DS_REPLICA_NEIGHBOUR_DO_SCHEDULED_SYNCS
+ | DRSUAPI_DS_REPLICA_NEIGHBOUR_FULL_IN_PROGRESS
+ | DRSUAPI_DS_REPLICA_NEIGHBOUR_NEVER_SYNCED
+ | DRSUAPI_DS_REPLICA_NEIGHBOUR_COMPRESS_CHANGES;
+
+ s->config_part.store_chunk = s->callbacks.config_chunk;
+
+ becomeDC_drsuapi_pull_partition_send(s, &s->drsuapi2, &s->drsuapi3, &s->config_part,
+ becomeDC_drsuapi3_pull_config_recv);
+}
+
+static void becomeDC_drsuapi3_pull_config_recv(struct rpc_request *req)
+{
+ struct libnet_BecomeDC_state *s = talloc_get_type(req->async.private_data,
+ struct libnet_BecomeDC_state);
+ struct composite_context *c = s->creq;
+ struct drsuapi_DsGetNCChanges *r = talloc_get_type(req->ndr.struct_ptr,
+ struct drsuapi_DsGetNCChanges);
+ WERROR status;
+
+ bool print = false;
+
+ if (req->p->conn->flags & DCERPC_DEBUG_PRINT_OUT) {
+ print = true;
+ }
+
+ c->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(c)) return;
+
+ if (print) {
+ NDR_PRINT_OUT_DEBUG(drsuapi_DsGetNCChanges, r);
+ }
+
+ status = becomeDC_drsuapi_pull_partition_recv(s, &s->drsuapi2, &s->drsuapi3, &s->config_part, r);
+ if (!W_ERROR_IS_OK(status)) {
+ composite_error(c, werror_to_ntstatus(status));
+ return;
+ }
+
+ talloc_free(r);
+
+ if (s->config_part.more_data) {
+ becomeDC_drsuapi_pull_partition_send(s, &s->drsuapi2, &s->drsuapi3, &s->config_part,
+ becomeDC_drsuapi3_pull_config_recv);
+ return;
+ }
+
+ becomeDC_connect_ldap2(s);
+}
+
+static void becomeDC_drsuapi3_pull_domain_recv(struct rpc_request *req);
+
+static void becomeDC_drsuapi3_pull_domain_send(struct libnet_BecomeDC_state *s)
+{
+ s->domain_part.nc.guid = GUID_zero();
+ s->domain_part.nc.sid = s->zero_sid;
+ s->domain_part.nc.dn = s->domain.dn_str;
+
+ s->domain_part.destination_dsa_guid = s->drsuapi2.bind_guid;
+
+ s->domain_part.replica_flags = DRSUAPI_DS_REPLICA_NEIGHBOUR_WRITEABLE
+ | DRSUAPI_DS_REPLICA_NEIGHBOUR_SYNC_ON_STARTUP
+ | DRSUAPI_DS_REPLICA_NEIGHBOUR_DO_SCHEDULED_SYNCS
+ | DRSUAPI_DS_REPLICA_NEIGHBOUR_FULL_IN_PROGRESS
+ | DRSUAPI_DS_REPLICA_NEIGHBOUR_NEVER_SYNCED
+ | DRSUAPI_DS_REPLICA_NEIGHBOUR_COMPRESS_CHANGES;
+
+ s->domain_part.store_chunk = s->callbacks.domain_chunk;
+
+ becomeDC_drsuapi_pull_partition_send(s, &s->drsuapi2, &s->drsuapi3, &s->domain_part,
+ becomeDC_drsuapi3_pull_domain_recv);
+}
+
+static void becomeDC_drsuapi_update_refs_send(struct libnet_BecomeDC_state *s,
+ struct becomeDC_drsuapi *drsuapi,
+ struct libnet_BecomeDC_Partition *partition,
+ void (*recv_fn)(struct rpc_request *req));
+static void becomeDC_drsuapi2_update_refs_schema_recv(struct rpc_request *req);
+
+static void becomeDC_drsuapi3_pull_domain_recv(struct rpc_request *req)
+{
+ struct libnet_BecomeDC_state *s = talloc_get_type(req->async.private_data,
+ struct libnet_BecomeDC_state);
+ struct composite_context *c = s->creq;
+ struct drsuapi_DsGetNCChanges *r = talloc_get_type(req->ndr.struct_ptr,
+ struct drsuapi_DsGetNCChanges);
+ WERROR status;
+ bool print = false;
+
+ if (req->p->conn->flags & DCERPC_DEBUG_PRINT_OUT) {
+ print = true;
+ }
+
+ c->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(c)) return;
+
+ if (print) {
+ NDR_PRINT_OUT_DEBUG(drsuapi_DsGetNCChanges, r);
+ }
+
+ status = becomeDC_drsuapi_pull_partition_recv(s, &s->drsuapi2, &s->drsuapi3, &s->domain_part, r);
+ if (!W_ERROR_IS_OK(status)) {
+ composite_error(c, werror_to_ntstatus(status));
+ return;
+ }
+
+ talloc_free(r);
+
+ if (s->domain_part.more_data) {
+ becomeDC_drsuapi_pull_partition_send(s, &s->drsuapi2, &s->drsuapi3, &s->domain_part,
+ becomeDC_drsuapi3_pull_domain_recv);
+ return;
+ }
+
+ becomeDC_drsuapi_update_refs_send(s, &s->drsuapi2, &s->schema_part,
+ becomeDC_drsuapi2_update_refs_schema_recv);
+}
+
+static void becomeDC_drsuapi_update_refs_send(struct libnet_BecomeDC_state *s,
+ struct becomeDC_drsuapi *drsuapi,
+ struct libnet_BecomeDC_Partition *partition,
+ void (*recv_fn)(struct rpc_request *req))
+{
+ struct composite_context *c = s->creq;
+ struct rpc_request *req;
+ struct drsuapi_DsReplicaUpdateRefs *r;
+ const char *ntds_guid_str;
+ const char *ntds_dns_name;
+
+ r = talloc(s, struct drsuapi_DsReplicaUpdateRefs);
+ if (composite_nomem(r, c)) return;
+
+ ntds_guid_str = GUID_string(r, &s->dest_dsa.ntds_guid);
+ if (composite_nomem(ntds_guid_str, c)) return;
+
+ ntds_dns_name = talloc_asprintf(r, "%s._msdcs.%s",
+ ntds_guid_str,
+ s->domain.dns_name);
+ if (composite_nomem(ntds_dns_name, c)) return;
+
+ r->in.bind_handle = &drsuapi->bind_handle;
+ r->in.level = 1;
+ r->in.req.req1.naming_context = &partition->nc;
+ r->in.req.req1.dest_dsa_dns_name= ntds_dns_name;
+ r->in.req.req1.dest_dsa_guid = s->dest_dsa.ntds_guid;
+ r->in.req.req1.options = DRSUAPI_DS_REPLICA_UPDATE_ADD_REFERENCE
+ | DRSUAPI_DS_REPLICA_UPDATE_DELETE_REFERENCE
+ | DRSUAPI_DS_REPLICA_UPDATE_0x00000010;
+
+ req = dcerpc_drsuapi_DsReplicaUpdateRefs_send(drsuapi->pipe, r, r);
+ composite_continue_rpc(c, req, recv_fn, s);
+}
+
+static void becomeDC_drsuapi2_update_refs_config_recv(struct rpc_request *req);
+
+static void becomeDC_drsuapi2_update_refs_schema_recv(struct rpc_request *req)
+{
+ struct libnet_BecomeDC_state *s = talloc_get_type(req->async.private_data,
+ struct libnet_BecomeDC_state);
+ struct composite_context *c = s->creq;
+ struct drsuapi_DsReplicaUpdateRefs *r = talloc_get_type(req->ndr.struct_ptr,
+ struct drsuapi_DsReplicaUpdateRefs);
+ bool print = false;
+
+ if (req->p->conn->flags & DCERPC_DEBUG_PRINT_OUT) {
+ print = true;
+ }
+
+ c->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(c)) return;
+
+ if (print) {
+ NDR_PRINT_OUT_DEBUG(drsuapi_DsReplicaUpdateRefs, r);
+ }
+
+ if (!W_ERROR_IS_OK(r->out.result)) {
+ composite_error(c, werror_to_ntstatus(r->out.result));
+ return;
+ }
+
+ talloc_free(r);
+
+ becomeDC_drsuapi_update_refs_send(s, &s->drsuapi2, &s->config_part,
+ becomeDC_drsuapi2_update_refs_config_recv);
+}
+
+static void becomeDC_drsuapi2_update_refs_domain_recv(struct rpc_request *req);
+
+static void becomeDC_drsuapi2_update_refs_config_recv(struct rpc_request *req)
+{
+ struct libnet_BecomeDC_state *s = talloc_get_type(req->async.private_data,
+ struct libnet_BecomeDC_state);
+ struct composite_context *c = s->creq;
+ struct drsuapi_DsReplicaUpdateRefs *r = talloc_get_type(req->ndr.struct_ptr,
+ struct drsuapi_DsReplicaUpdateRefs);
+
+ c->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(c)) return;
+
+ if (!W_ERROR_IS_OK(r->out.result)) {
+ composite_error(c, werror_to_ntstatus(r->out.result));
+ return;
+ }
+
+ talloc_free(r);
+
+ becomeDC_drsuapi_update_refs_send(s, &s->drsuapi2, &s->domain_part,
+ becomeDC_drsuapi2_update_refs_domain_recv);
+}
+
+static void becomeDC_drsuapi2_update_refs_domain_recv(struct rpc_request *req)
+{
+ struct libnet_BecomeDC_state *s = talloc_get_type(req->async.private_data,
+ struct libnet_BecomeDC_state);
+ struct composite_context *c = s->creq;
+ struct drsuapi_DsReplicaUpdateRefs *r = talloc_get_type(req->ndr.struct_ptr,
+ struct drsuapi_DsReplicaUpdateRefs);
+
+ c->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(c)) return;
+
+ if (!W_ERROR_IS_OK(r->out.result)) {
+ composite_error(c, werror_to_ntstatus(r->out.result));
+ return;
+ }
+
+ talloc_free(r);
+
+ /* TODO: use DDNS updates and register dns names */
+ composite_done(c);
+}
+
+static NTSTATUS becomeDC_ldap2_modify_computer(struct libnet_BecomeDC_state *s)
+{
+ int ret;
+ struct ldb_message *msg;
+ uint32_t i;
+ uint32_t user_account_control = UF_SERVER_TRUST_ACCOUNT |
+ UF_TRUSTED_FOR_DELEGATION;
+
+ /* as the value is already as we want it to be, we're done */
+ if (s->dest_dsa.user_account_control == user_account_control) {
+ return NT_STATUS_OK;
+ }
+
+ /* make a 'modify' msg, and only for serverReference */
+ msg = ldb_msg_new(s);
+ NT_STATUS_HAVE_NO_MEMORY(msg);
+ msg->dn = ldb_dn_new(msg, s->ldap2.ldb, s->dest_dsa.computer_dn_str);
+ NT_STATUS_HAVE_NO_MEMORY(msg->dn);
+
+ ret = ldb_msg_add_fmt(msg, "userAccountControl", "%u", user_account_control);
+ if (ret != 0) {
+ talloc_free(msg);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* mark all the message elements (should be just one)
+ as LDB_FLAG_MOD_REPLACE */
+ for (i=0;i<msg->num_elements;i++) {
+ msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
+ }
+
+ ret = ldb_modify(s->ldap2.ldb, msg);
+ talloc_free(msg);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_LDAP(ret);
+ }
+
+ s->dest_dsa.user_account_control = user_account_control;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS becomeDC_ldap2_move_computer(struct libnet_BecomeDC_state *s)
+{
+ int ret;
+ struct ldb_result *r;
+ struct ldb_dn *basedn;
+ struct ldb_dn *old_dn;
+ struct ldb_dn *new_dn;
+ static const char *_1_1_attrs[] = {
+ "1.1",
+ NULL
+ };
+
+ basedn = ldb_dn_new_fmt(s, s->ldap2.ldb, "<WKGUID=a361b2ffffd211d1aa4b00c04fd7d83a,%s>",
+ s->domain.dn_str);
+ NT_STATUS_HAVE_NO_MEMORY(basedn);
+
+ ret = ldb_search(s->ldap2.ldb, s, &r, basedn, LDB_SCOPE_BASE,
+ _1_1_attrs, "(objectClass=*)");
+ talloc_free(basedn);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_LDAP(ret);
+ } else if (r->count != 1) {
+ talloc_free(r);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ old_dn = ldb_dn_new(r, s->ldap2.ldb, s->dest_dsa.computer_dn_str);
+ NT_STATUS_HAVE_NO_MEMORY(old_dn);
+
+ new_dn = r->msgs[0]->dn;
+
+ if (!ldb_dn_add_child_fmt(new_dn, "CN=%s", s->dest_dsa.netbios_name)) {
+ talloc_free(r);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (ldb_dn_compare(old_dn, new_dn) == 0) {
+ /* we don't need to rename if the old and new dn match */
+ talloc_free(r);
+ return NT_STATUS_OK;
+ }
+
+ ret = ldb_rename(s->ldap2.ldb, old_dn, new_dn);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(r);
+ return NT_STATUS_LDAP(ret);
+ }
+
+ s->dest_dsa.computer_dn_str = ldb_dn_alloc_linearized(s, new_dn);
+ NT_STATUS_HAVE_NO_MEMORY(s->dest_dsa.computer_dn_str);
+
+ talloc_free(r);
+
+ return NT_STATUS_OK;
+}
+
+static void becomeDC_connect_ldap2(struct libnet_BecomeDC_state *s)
+{
+ struct composite_context *c = s->creq;
+
+ c->status = becomeDC_ldap_connect(s, &s->ldap2);
+ if (!composite_is_ok(c)) return;
+
+ c->status = becomeDC_ldap2_modify_computer(s);
+ if (!composite_is_ok(c)) return;
+
+ c->status = becomeDC_ldap2_move_computer(s);
+ if (!composite_is_ok(c)) return;
+
+ becomeDC_drsuapi3_pull_domain_send(s);
+}
+
+struct composite_context *libnet_BecomeDC_send(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_BecomeDC *r)
+{
+ struct composite_context *c;
+ struct libnet_BecomeDC_state *s;
+ char *tmp_name;
+
+ c = composite_create(mem_ctx, ctx->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct libnet_BecomeDC_state);
+ if (composite_nomem(s, c)) return c;
+ c->private_data = s;
+ s->creq = c;
+ s->libnet = ctx;
+
+ /* Domain input */
+ s->domain.dns_name = talloc_strdup(s, r->in.domain_dns_name);
+ if (composite_nomem(s->domain.dns_name, c)) return c;
+ s->domain.netbios_name = talloc_strdup(s, r->in.domain_netbios_name);
+ if (composite_nomem(s->domain.netbios_name, c)) return c;
+ s->domain.sid = dom_sid_dup(s, r->in.domain_sid);
+ if (composite_nomem(s->domain.sid, c)) return c;
+
+ /* Source DSA input */
+ s->source_dsa.address = talloc_strdup(s, r->in.source_dsa_address);
+ if (composite_nomem(s->source_dsa.address, c)) return c;
+
+ /* Destination DSA input */
+ s->dest_dsa.netbios_name= talloc_strdup(s, r->in.dest_dsa_netbios_name);
+ if (composite_nomem(s->dest_dsa.netbios_name, c)) return c;
+
+ /* Destination DSA dns_name construction */
+ tmp_name = strlower_talloc(s, s->dest_dsa.netbios_name);
+ if (composite_nomem(tmp_name, c)) return c;
+ tmp_name = talloc_asprintf_append_buffer(tmp_name, ".%s",s->domain.dns_name);
+ if (composite_nomem(tmp_name, c)) return c;
+ s->dest_dsa.dns_name = tmp_name;
+
+ /* Callback function pointers */
+ s->callbacks = r->in.callbacks;
+
+ becomeDC_send_cldap(s);
+ return c;
+}
+
+NTSTATUS libnet_BecomeDC_recv(struct composite_context *c, TALLOC_CTX *mem_ctx, struct libnet_BecomeDC *r)
+{
+ NTSTATUS status;
+
+ status = composite_wait(c);
+
+ ZERO_STRUCT(r->out);
+
+ talloc_free(c);
+ return status;
+}
+
+NTSTATUS libnet_BecomeDC(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_BecomeDC *r)
+{
+ NTSTATUS status;
+ struct composite_context *c;
+ c = libnet_BecomeDC_send(ctx, mem_ctx, r);
+ status = libnet_BecomeDC_recv(c, mem_ctx, r);
+ return status;
+}
diff --git a/source4/libnet/libnet_become_dc.h b/source4/libnet/libnet_become_dc.h
new file mode 100644
index 0000000000..58fc4dc15d
--- /dev/null
+++ b/source4/libnet/libnet_become_dc.h
@@ -0,0 +1,146 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Stefan Metzmacher <metze@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _LIBNET_BECOME_DC_H
+#define _LIBNET_BECOME_DC_H
+
+#include "librpc/gen_ndr/drsuapi.h"
+
+struct libnet_BecomeDC_Domain {
+ /* input */
+ const char *dns_name;
+ const char *netbios_name;
+ const struct dom_sid *sid;
+
+ /* constructed */
+ struct GUID guid;
+ const char *dn_str;
+ uint32_t behavior_version;
+ uint32_t w2k3_update_revision;
+};
+
+struct libnet_BecomeDC_Forest {
+ /* constructed */
+ const char *dns_name;
+ const char *root_dn_str;
+ const char *config_dn_str;
+ uint32_t crossref_behavior_version;
+ const char *schema_dn_str;
+ uint32_t schema_object_version;
+};
+
+struct libnet_BecomeDC_SourceDSA {
+ /* input */
+ const char *address;
+
+ /* constructed */
+ const char *dns_name;
+ const char *netbios_name;
+ const char *site_name;
+ const char *server_dn_str;
+ const char *ntds_dn_str;
+};
+
+struct libnet_BecomeDC_CheckOptions {
+ const struct libnet_BecomeDC_Domain *domain;
+ const struct libnet_BecomeDC_Forest *forest;
+ const struct libnet_BecomeDC_SourceDSA *source_dsa;
+};
+
+struct libnet_BecomeDC_DestDSA {
+ /* input */
+ const char *netbios_name;
+
+ /* constructed */
+ const char *dns_name;
+ const char *site_name;
+ struct GUID site_guid;
+ const char *computer_dn_str;
+ const char *server_dn_str;
+ const char *ntds_dn_str;
+ struct GUID ntds_guid;
+ struct GUID invocation_id;
+ uint32_t user_account_control;
+};
+
+struct libnet_BecomeDC_PrepareDB {
+ const struct libnet_BecomeDC_Domain *domain;
+ const struct libnet_BecomeDC_Forest *forest;
+ const struct libnet_BecomeDC_SourceDSA *source_dsa;
+ const struct libnet_BecomeDC_DestDSA *dest_dsa;
+};
+
+struct libnet_BecomeDC_StoreChunk;
+
+struct libnet_BecomeDC_Partition {
+ struct drsuapi_DsReplicaObjectIdentifier nc;
+ struct GUID destination_dsa_guid;
+ struct GUID source_dsa_guid;
+ struct GUID source_dsa_invocation_id;
+ struct drsuapi_DsReplicaHighWaterMark highwatermark;
+ bool more_data;
+ uint32_t replica_flags;
+
+ NTSTATUS (*store_chunk)(void *private_data,
+ const struct libnet_BecomeDC_StoreChunk *info);
+};
+
+struct libnet_BecomeDC_StoreChunk {
+ const struct libnet_BecomeDC_Domain *domain;
+ const struct libnet_BecomeDC_Forest *forest;
+ const struct libnet_BecomeDC_SourceDSA *source_dsa;
+ const struct libnet_BecomeDC_DestDSA *dest_dsa;
+ const struct libnet_BecomeDC_Partition *partition;
+ uint32_t ctr_level;
+ const struct drsuapi_DsGetNCChangesCtr1 *ctr1;
+ const struct drsuapi_DsGetNCChangesCtr6 *ctr6;
+ const DATA_BLOB *gensec_skey;
+};
+
+struct libnet_BecomeDC_Callbacks {
+ void *private_data;
+ NTSTATUS (*check_options)(void *private_data,
+ const struct libnet_BecomeDC_CheckOptions *info);
+ NTSTATUS (*prepare_db)(void *private_data,
+ const struct libnet_BecomeDC_PrepareDB *info);
+ NTSTATUS (*schema_chunk)(void *private_data,
+ const struct libnet_BecomeDC_StoreChunk *info);
+ NTSTATUS (*config_chunk)(void *private_data,
+ const struct libnet_BecomeDC_StoreChunk *info);
+ NTSTATUS (*domain_chunk)(void *private_data,
+ const struct libnet_BecomeDC_StoreChunk *info);
+};
+
+struct libnet_BecomeDC {
+ struct {
+ const char *domain_dns_name;
+ const char *domain_netbios_name;
+ const struct dom_sid *domain_sid;
+ const char *source_dsa_address;
+ const char *dest_dsa_netbios_name;
+
+ struct libnet_BecomeDC_Callbacks callbacks;
+ } in;
+
+ struct {
+ const char *error_string;
+ } out;
+};
+
+#endif /* _LIBNET_BECOME_DC_H */
diff --git a/source4/libnet/libnet_domain.c b/source4/libnet/libnet_domain.c
new file mode 100644
index 0000000000..eb6920d88e
--- /dev/null
+++ b/source4/libnet/libnet_domain.c
@@ -0,0 +1,1259 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ a composite function for domain handling on samr and lsa pipes
+*/
+
+#include "includes.h"
+#include "libcli/composite/composite.h"
+#include "libnet/libnet.h"
+#include "librpc/gen_ndr/ndr_samr_c.h"
+#include "librpc/gen_ndr/ndr_lsa_c.h"
+
+
+struct domain_open_samr_state {
+ struct libnet_context *ctx;
+ struct dcerpc_pipe *pipe;
+ struct libnet_RpcConnect rpcconn;
+ struct samr_Connect connect;
+ struct samr_LookupDomain lookup;
+ struct samr_OpenDomain open;
+ struct samr_Close close;
+ struct lsa_String domain_name;
+ uint32_t access_mask;
+ struct policy_handle connect_handle;
+ struct policy_handle domain_handle;
+ struct dom_sid2 *domain_sid;
+
+ /* information about the progress */
+ void (*monitor_fn)(struct monitor_msg*);
+};
+
+
+static void continue_domain_open_close(struct rpc_request *req);
+static void continue_domain_open_connect(struct rpc_request *req);
+static void continue_domain_open_lookup(struct rpc_request *req);
+static void continue_domain_open_open(struct rpc_request *req);
+
+
+/**
+ * Stage 0.5 (optional): Connect to samr rpc pipe
+ */
+static void continue_domain_open_rpc_connect(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct domain_open_samr_state *s;
+ struct rpc_request *conn_req;
+
+ c = talloc_get_type(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct domain_open_samr_state);
+
+ c->status = libnet_RpcConnect_recv(ctx, s->ctx, c, &s->rpcconn);
+ if (!composite_is_ok(c)) return;
+
+ s->pipe = s->rpcconn.out.dcerpc_pipe;
+
+ /* preparing parameters for samr_Connect rpc call */
+ s->connect.in.system_name = 0;
+ s->connect.in.access_mask = s->access_mask;
+ s->connect.out.connect_handle = &s->connect_handle;
+
+ /* send request */
+ conn_req = dcerpc_samr_Connect_send(s->pipe, c, &s->connect);
+ if (composite_nomem(conn_req, c)) return;
+
+ /* callback handler */
+ composite_continue_rpc(c, conn_req, continue_domain_open_connect, c);
+}
+
+
+/**
+ * Stage 0.5 (optional): Close existing (in libnet context) domain
+ * handle
+ */
+static void continue_domain_open_close(struct rpc_request *req)
+{
+ struct composite_context *c;
+ struct domain_open_samr_state *s;
+ struct rpc_request *conn_req;
+
+ c = talloc_get_type(req->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct domain_open_samr_state);
+
+ /* receive samr_Close reply */
+ c->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(c)) return;
+
+ if (s->monitor_fn) {
+ struct monitor_msg msg;
+
+ msg.type = mon_SamrClose;
+ msg.data = NULL;
+ msg.data_size = 0;
+ s->monitor_fn(&msg);
+ }
+
+ /* reset domain handle and associated data in libnet_context */
+ s->ctx->samr.name = NULL;
+ s->ctx->samr.access_mask = 0;
+ ZERO_STRUCT(s->ctx->samr.handle);
+
+ /* preparing parameters for samr_Connect rpc call */
+ s->connect.in.system_name = 0;
+ s->connect.in.access_mask = s->access_mask;
+ s->connect.out.connect_handle = &s->connect_handle;
+
+ /* send request */
+ conn_req = dcerpc_samr_Connect_send(s->pipe, c, &s->connect);
+ if (composite_nomem(conn_req, c)) return;
+
+ /* callback handler */
+ composite_continue_rpc(c, conn_req, continue_domain_open_connect, c);
+}
+
+
+/**
+ * Stage 1: Connect to SAM server.
+ */
+static void continue_domain_open_connect(struct rpc_request *req)
+{
+ struct composite_context *c;
+ struct domain_open_samr_state *s;
+ struct rpc_request *lookup_req;
+ struct samr_LookupDomain *r;
+
+ c = talloc_get_type(req->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct domain_open_samr_state);
+
+ /* receive samr_Connect reply */
+ c->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(c)) return;
+
+ if (s->monitor_fn) {
+ struct monitor_msg msg;
+
+ msg.type = mon_SamrConnect;
+ msg.data = NULL;
+ msg.data_size = 0;
+ s->monitor_fn(&msg);
+ }
+
+ r = &s->lookup;
+
+ /* prepare for samr_LookupDomain call */
+ r->in.connect_handle = &s->connect_handle;
+ r->in.domain_name = &s->domain_name;
+ r->out.sid = talloc(s, struct dom_sid2 *);
+ if (composite_nomem(r->out.sid, c)) return;
+
+ lookup_req = dcerpc_samr_LookupDomain_send(s->pipe, c, r);
+ if (composite_nomem(lookup_req, c)) return;
+
+ composite_continue_rpc(c, lookup_req, continue_domain_open_lookup, c);
+}
+
+
+/**
+ * Stage 2: Lookup domain by name.
+ */
+static void continue_domain_open_lookup(struct rpc_request *req)
+{
+ struct composite_context *c;
+ struct domain_open_samr_state *s;
+ struct rpc_request *opendom_req;
+ struct samr_OpenDomain *r;
+
+ c = talloc_get_type(req->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct domain_open_samr_state);
+
+ /* receive samr_LookupDomain reply */
+ c->status = dcerpc_ndr_request_recv(req);
+
+ if (s->monitor_fn) {
+ struct monitor_msg msg;
+ struct msg_rpc_lookup_domain data;
+
+ data.domain_name = s->domain_name.string;
+
+ msg.type = mon_SamrLookupDomain;
+ msg.data = (void*)&data;
+ msg.data_size = sizeof(data);
+ s->monitor_fn(&msg);
+ }
+
+ r = &s->open;
+
+ /* check the rpc layer status */
+ if (!composite_is_ok(c));
+
+ /* check the rpc call itself status */
+ if (!NT_STATUS_IS_OK(s->lookup.out.result)) {
+ composite_error(c, s->lookup.out.result);
+ return;
+ }
+
+ /* prepare for samr_OpenDomain call */
+ r->in.connect_handle = &s->connect_handle;
+ r->in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r->in.sid = *s->lookup.out.sid;
+ r->out.domain_handle = &s->domain_handle;
+
+ opendom_req = dcerpc_samr_OpenDomain_send(s->pipe, c, r);
+ if (composite_nomem(opendom_req, c)) return;
+
+ composite_continue_rpc(c, opendom_req, continue_domain_open_open, c);
+}
+
+
+/*
+ * Stage 3: Open domain.
+ */
+static void continue_domain_open_open(struct rpc_request *req)
+{
+ struct composite_context *c;
+ struct domain_open_samr_state *s;
+
+ c = talloc_get_type(req->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct domain_open_samr_state);
+
+ /* receive samr_OpenDomain reply */
+ c->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(c)) return;
+
+ if (s->monitor_fn) {
+ struct monitor_msg msg;
+
+ msg.type = mon_SamrOpenDomain;
+ msg.data = NULL;
+ msg.data_size = 0;
+ s->monitor_fn(&msg);
+ }
+
+ composite_done(c);
+}
+
+
+/**
+ * Sends asynchronous DomainOpenSamr request
+ *
+ * @param ctx initialised libnet context
+ * @param io arguments and results of the call
+ * @param monitor pointer to monitor function that is passed monitor message
+ */
+
+struct composite_context *libnet_DomainOpenSamr_send(struct libnet_context *ctx,
+ struct libnet_DomainOpen *io,
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *c;
+ struct domain_open_samr_state *s;
+ struct composite_context *rpcconn_req;
+ struct rpc_request *close_req, *conn_req;
+
+ c = composite_create(ctx, ctx->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct domain_open_samr_state);
+ if (composite_nomem(s, c)) return c;
+
+ c->private_data = s;
+ s->monitor_fn = monitor;
+
+ s->ctx = ctx;
+ s->pipe = ctx->samr.pipe;
+ s->access_mask = io->in.access_mask;
+ s->domain_name.string = talloc_strdup(c, io->in.domain_name);
+
+ /* check, if there's samr pipe opened already, before opening a domain */
+ if (ctx->samr.pipe == NULL) {
+
+ /* attempting to connect a domain controller */
+ s->rpcconn.level = LIBNET_RPC_CONNECT_DC;
+ s->rpcconn.in.name = io->in.domain_name;
+ s->rpcconn.in.dcerpc_iface = &ndr_table_samr;
+
+ /* send rpc pipe connect request */
+ rpcconn_req = libnet_RpcConnect_send(ctx, c, &s->rpcconn, s->monitor_fn);
+ if (composite_nomem(rpcconn_req, c)) return c;
+
+ composite_continue(c, rpcconn_req, continue_domain_open_rpc_connect, c);
+ return c;
+ }
+
+ /* libnet context's domain handle is not empty, so check out what
+ was opened first, before doing anything */
+ if (!policy_handle_empty(&ctx->samr.handle)) {
+ if (strequal(ctx->samr.name, io->in.domain_name) &&
+ ctx->samr.access_mask == io->in.access_mask) {
+
+ /* this domain is already opened */
+ composite_done(c);
+ return c;
+
+ } else {
+ /* another domain or access rights have been
+ requested - close the existing handle first */
+ s->close.in.handle = &ctx->samr.handle;
+
+ /* send request to close domain handle */
+ close_req = dcerpc_samr_Close_send(s->pipe, c, &s->close);
+ if (composite_nomem(close_req, c)) return c;
+
+ /* callback handler */
+ composite_continue_rpc(c, close_req, continue_domain_open_close, c);
+ return c;
+ }
+ }
+
+ /* preparing parameters for samr_Connect rpc call */
+ s->connect.in.system_name = 0;
+ s->connect.in.access_mask = s->access_mask;
+ s->connect.out.connect_handle = &s->connect_handle;
+
+ /* send request */
+ conn_req = dcerpc_samr_Connect_send(s->pipe, c, &s->connect);
+ if (composite_nomem(conn_req, c)) return c;
+
+ /* callback handler */
+ composite_continue_rpc(c, conn_req, continue_domain_open_connect, c);
+ return c;
+}
+
+
+/**
+ * Waits for and receives result of asynchronous DomainOpenSamr call
+ *
+ * @param c composite context returned by asynchronous DomainOpen call
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of the call
+ * @param io pointer to results (and arguments) of the call
+ * @return nt status code of execution
+ */
+
+NTSTATUS libnet_DomainOpenSamr_recv(struct composite_context *c, struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx, struct libnet_DomainOpen *io)
+{
+ NTSTATUS status;
+ struct domain_open_samr_state *s;
+
+ /* wait for results of sending request */
+ status = composite_wait(c);
+
+ if (NT_STATUS_IS_OK(status) && io) {
+ s = talloc_get_type(c->private_data, struct domain_open_samr_state);
+ io->out.domain_handle = s->domain_handle;
+
+ /* store the resulting handle and related data for use by other
+ libnet functions */
+ ctx->samr.connect_handle = s->connect_handle;
+ ctx->samr.handle = s->domain_handle;
+ ctx->samr.sid = talloc_steal(ctx, *s->lookup.out.sid);
+ ctx->samr.name = talloc_steal(ctx, s->domain_name.string);
+ ctx->samr.access_mask = s->access_mask;
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+
+struct domain_open_lsa_state {
+ const char *name;
+ uint32_t access_mask;
+ struct libnet_context *ctx;
+ struct libnet_RpcConnect rpcconn;
+ struct lsa_OpenPolicy2 openpol;
+ struct policy_handle handle;
+ struct dcerpc_pipe *pipe;
+
+ /* information about the progress */
+ void (*monitor_fn)(struct monitor_msg*);
+};
+
+
+static void continue_rpc_connect_lsa(struct composite_context *ctx);
+static void continue_lsa_policy_open(struct rpc_request *req);
+
+
+/**
+ * Sends asynchronous DomainOpenLsa request
+ *
+ * @param ctx initialised libnet context
+ * @param io arguments and results of the call
+ * @param monitor pointer to monitor function that is passed monitor message
+ */
+
+struct composite_context* libnet_DomainOpenLsa_send(struct libnet_context *ctx,
+ struct libnet_DomainOpen *io,
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *c;
+ struct domain_open_lsa_state *s;
+ struct composite_context *rpcconn_req;
+ struct rpc_request *openpol_req;
+ struct lsa_QosInfo *qos;
+
+ /* create composite context and state */
+ c = composite_create(ctx, ctx->event_ctx);
+ if (c == NULL) return c;
+
+ s = talloc_zero(c, struct domain_open_lsa_state);
+ if (composite_nomem(s, c)) return c;
+
+ c->private_data = s;
+
+ /* store arguments in the state structure */
+ s->name = talloc_strdup(c, io->in.domain_name);
+ s->access_mask = io->in.access_mask;
+ s->ctx = ctx;
+
+ /* check, if there's lsa pipe opened already, before opening a handle */
+ if (ctx->lsa.pipe == NULL) {
+
+ /* attempting to connect a domain controller */
+ s->rpcconn.level = LIBNET_RPC_CONNECT_DC;
+ s->rpcconn.in.name = talloc_strdup(c, io->in.domain_name);
+ s->rpcconn.in.dcerpc_iface = &ndr_table_lsarpc;
+
+ /* send rpc pipe connect request */
+ rpcconn_req = libnet_RpcConnect_send(ctx, c, &s->rpcconn, s->monitor_fn);
+ if (composite_nomem(rpcconn_req, c)) return c;
+
+ composite_continue(c, rpcconn_req, continue_rpc_connect_lsa, c);
+ return c;
+ }
+
+ s->pipe = ctx->lsa.pipe;
+
+ /* preparing parameters for lsa_OpenPolicy2 rpc call */
+ s->openpol.in.system_name = s->name;
+ s->openpol.in.access_mask = s->access_mask;
+ s->openpol.in.attr = talloc_zero(c, struct lsa_ObjectAttribute);
+
+ qos = talloc_zero(c, struct lsa_QosInfo);
+ qos->len = 0;
+ qos->impersonation_level = 2;
+ qos->context_mode = 1;
+ qos->effective_only = 0;
+
+ s->openpol.in.attr->sec_qos = qos;
+ s->openpol.out.handle = &s->handle;
+
+ /* send rpc request */
+ openpol_req = dcerpc_lsa_OpenPolicy2_send(s->pipe, c, &s->openpol);
+ if (composite_nomem(openpol_req, c)) return c;
+
+ composite_continue_rpc(c, openpol_req, continue_lsa_policy_open, c);
+ return c;
+}
+
+
+/*
+ Stage 0.5 (optional): Rpc pipe connected, send lsa open policy request
+ */
+static void continue_rpc_connect_lsa(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct domain_open_lsa_state *s;
+ struct lsa_QosInfo *qos;
+ struct rpc_request *openpol_req;
+
+ c = talloc_get_type(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct domain_open_lsa_state);
+
+ /* receive rpc connection */
+ c->status = libnet_RpcConnect_recv(ctx, s->ctx, c, &s->rpcconn);
+ if (!composite_is_ok(c)) return;
+
+ /* RpcConnect function leaves the pipe in libnet context,
+ so get it from there */
+ s->pipe = s->ctx->lsa.pipe;
+
+ /* prepare lsa_OpenPolicy2 call */
+ s->openpol.in.system_name = s->name;
+ s->openpol.in.access_mask = s->access_mask;
+ s->openpol.in.attr = talloc_zero(c, struct lsa_ObjectAttribute);
+
+ qos = talloc_zero(c, struct lsa_QosInfo);
+ qos->len = 0;
+ qos->impersonation_level = 2;
+ qos->context_mode = 1;
+ qos->effective_only = 0;
+
+ s->openpol.in.attr->sec_qos = qos;
+ s->openpol.out.handle = &s->handle;
+
+ /* send rpc request */
+ openpol_req = dcerpc_lsa_OpenPolicy2_send(s->pipe, c, &s->openpol);
+ if (composite_nomem(openpol_req, c)) return;
+
+ composite_continue_rpc(c, openpol_req, continue_lsa_policy_open, c);
+}
+
+
+/*
+ Stage 1: Lsa policy opened - we're done, if successfully
+ */
+static void continue_lsa_policy_open(struct rpc_request *req)
+{
+ struct composite_context *c;
+ struct domain_open_lsa_state *s;
+
+ c = talloc_get_type(req->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct domain_open_lsa_state);
+
+ c->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(c)) return;
+
+ if (s->monitor_fn) {
+ struct monitor_msg msg;
+
+ msg.type = mon_LsaOpenPolicy;
+ msg.data = NULL;
+ msg.data_size = 0;
+ s->monitor_fn(&msg);
+ }
+
+ composite_done(c);
+}
+
+
+/**
+ * Receives result of asynchronous DomainOpenLsa call
+ *
+ * @param c composite context returned by asynchronous DomainOpenLsa call
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of the call
+ * @param io pointer to results (and arguments) of the call
+ * @return nt status code of execution
+ */
+
+NTSTATUS libnet_DomainOpenLsa_recv(struct composite_context *c, struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx, struct libnet_DomainOpen *io)
+{
+ NTSTATUS status;
+ struct domain_open_lsa_state *s;
+
+ status = composite_wait(c);
+
+ if (NT_STATUS_IS_OK(status) && io) {
+ /* everything went fine - get the results and
+ return the error string */
+ s = talloc_get_type(c->private_data, struct domain_open_lsa_state);
+ io->out.domain_handle = s->handle;
+
+ ctx->lsa.handle = s->handle;
+ ctx->lsa.name = talloc_steal(ctx, s->name);
+ ctx->lsa.access_mask = s->access_mask;
+
+ io->out.error_string = talloc_strdup(mem_ctx, "Success");
+
+ } else if (!NT_STATUS_IS_OK(status)) {
+ /* there was an error, so provide nt status code description */
+ io->out.error_string = talloc_asprintf(mem_ctx,
+ "Failed to open domain: %s",
+ nt_errstr(status));
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+
+/**
+ * Sends a request to open a domain in desired service
+ *
+ * @param ctx initalised libnet context
+ * @param io arguments and results of the call
+ * @param monitor pointer to monitor function that is passed monitor message
+ */
+
+struct composite_context* libnet_DomainOpen_send(struct libnet_context *ctx,
+ struct libnet_DomainOpen *io,
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *c;
+
+ switch (io->in.type) {
+ case DOMAIN_LSA:
+ /* reques to open a policy handle on \pipe\lsarpc */
+ c = libnet_DomainOpenLsa_send(ctx, io, monitor);
+ break;
+
+ case DOMAIN_SAMR:
+ default:
+ /* request to open a domain policy handle on \pipe\samr */
+ c = libnet_DomainOpenSamr_send(ctx, io, monitor);
+ break;
+ }
+
+ return c;
+}
+
+
+/**
+ * Receive result of domain open request
+ *
+ * @param c composite context returned by DomainOpen_send function
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of the call
+ * @param io results and arguments of the call
+ */
+
+NTSTATUS libnet_DomainOpen_recv(struct composite_context *c, struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx, struct libnet_DomainOpen *io)
+{
+ NTSTATUS status;
+
+ switch (io->in.type) {
+ case DOMAIN_LSA:
+ status = libnet_DomainOpenLsa_recv(c, ctx, mem_ctx, io);
+ break;
+
+ case DOMAIN_SAMR:
+ default:
+ status = libnet_DomainOpenSamr_recv(c, ctx, mem_ctx, io);
+ break;
+ }
+
+ return status;
+}
+
+
+/**
+ * Synchronous version of DomainOpen call
+ *
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context for the call
+ * @param io arguments and results of the call
+ * @return nt status code of execution
+ */
+
+NTSTATUS libnet_DomainOpen(struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_DomainOpen *io)
+{
+ struct composite_context *c = libnet_DomainOpen_send(ctx, io, NULL);
+ return libnet_DomainOpen_recv(c, ctx, mem_ctx, io);
+}
+
+
+struct domain_close_lsa_state {
+ struct dcerpc_pipe *pipe;
+ struct lsa_Close close;
+ struct policy_handle handle;
+
+ void (*monitor_fn)(struct monitor_msg*);
+};
+
+
+static void continue_lsa_close(struct rpc_request *req);
+
+
+struct composite_context* libnet_DomainCloseLsa_send(struct libnet_context *ctx,
+ struct libnet_DomainClose *io,
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *c;
+ struct domain_close_lsa_state *s;
+ struct rpc_request *close_req;
+
+ /* composite context and state structure allocation */
+ c = composite_create(ctx, ctx->event_ctx);
+ if (c == NULL) return c;
+
+ s = talloc_zero(c, struct domain_close_lsa_state);
+ if (composite_nomem(s, c)) return c;
+
+ c->private_data = s;
+ s->monitor_fn = monitor;
+
+ /* TODO: check if lsa pipe pointer is non-null */
+
+ if (!strequal(ctx->lsa.name, io->in.domain_name)) {
+ composite_error(c, NT_STATUS_INVALID_PARAMETER);
+ return c;
+ }
+
+ /* get opened lsarpc pipe pointer */
+ s->pipe = ctx->lsa.pipe;
+
+ /* prepare close handle call arguments */
+ s->close.in.handle = &ctx->lsa.handle;
+ s->close.out.handle = &s->handle;
+
+ /* send the request */
+ close_req = dcerpc_lsa_Close_send(s->pipe, c, &s->close);
+ if (composite_nomem(close_req, c)) return c;
+
+ composite_continue_rpc(c, close_req, continue_lsa_close, c);
+ return c;
+}
+
+
+/*
+ Stage 1: Receive result of lsa close call
+*/
+static void continue_lsa_close(struct rpc_request *req)
+{
+ struct composite_context *c;
+ struct domain_close_lsa_state *s;
+
+ c = talloc_get_type(req->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct domain_close_lsa_state);
+
+ c->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(c)) return;
+
+ if (s->monitor_fn) {
+ struct monitor_msg msg;
+
+ msg.type = mon_LsaClose;
+ msg.data = NULL;
+ msg.data_size = 0;
+ s->monitor_fn(&msg);
+ }
+
+ composite_done(c);
+}
+
+
+NTSTATUS libnet_DomainCloseLsa_recv(struct composite_context *c, struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx, struct libnet_DomainClose *io)
+{
+ NTSTATUS status;
+
+ status = composite_wait(c);
+
+ if (NT_STATUS_IS_OK(status) && io) {
+ /* policy handle closed successfully */
+
+ ctx->lsa.name = NULL;
+ ZERO_STRUCT(ctx->lsa.handle);
+
+ io->out.error_string = talloc_asprintf(mem_ctx, "Success");
+
+ } else if (!NT_STATUS_IS_OK(status)) {
+ /* there was an error, so return description of the status code */
+ io->out.error_string = talloc_asprintf(mem_ctx, "Error: %s", nt_errstr(status));
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+
+struct domain_close_samr_state {
+ struct samr_Close close;
+ struct policy_handle handle;
+
+ void (*monitor_fn)(struct monitor_msg*);
+};
+
+
+static void continue_samr_close(struct rpc_request *req);
+
+
+struct composite_context* libnet_DomainCloseSamr_send(struct libnet_context *ctx,
+ struct libnet_DomainClose *io,
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *c;
+ struct domain_close_samr_state *s;
+ struct rpc_request *close_req;
+
+ /* composite context and state structure allocation */
+ c = composite_create(ctx, ctx->event_ctx);
+ if (c == NULL) return c;
+
+ s = talloc_zero(c, struct domain_close_samr_state);
+ if (composite_nomem(s, c)) return c;
+
+ c->private_data = s;
+ s->monitor_fn = monitor;
+
+ /* TODO: check if samr pipe pointer is non-null */
+
+ if (!strequal(ctx->samr.name, io->in.domain_name)) {
+ composite_error(c, NT_STATUS_INVALID_PARAMETER);
+ return c;
+ }
+
+ /* prepare close domain handle call arguments */
+ ZERO_STRUCT(s->close);
+ s->close.in.handle = &ctx->samr.handle;
+ s->close.out.handle = &s->handle;
+
+ /* send the request */
+ close_req = dcerpc_samr_Close_send(ctx->samr.pipe, ctx, &s->close);
+ if (composite_nomem(close_req, c)) return c;
+
+ composite_continue_rpc(c, close_req, continue_samr_close, c);
+ return c;
+}
+
+
+/*
+ Stage 1: Receive result of samr close call
+*/
+static void continue_samr_close(struct rpc_request *req)
+{
+ struct composite_context *c;
+ struct domain_close_samr_state *s;
+
+ c = talloc_get_type(req->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct domain_close_samr_state);
+
+ c->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(c)) return;
+
+ if (s->monitor_fn) {
+ struct monitor_msg msg;
+
+ msg.type = mon_SamrClose;
+ msg.data = NULL;
+ msg.data_size = 0;
+ s->monitor_fn(&msg);
+ }
+
+ composite_done(c);
+}
+
+
+NTSTATUS libnet_DomainCloseSamr_recv(struct composite_context *c, struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx, struct libnet_DomainClose *io)
+{
+ NTSTATUS status;
+
+ status = composite_wait(c);
+
+ if (NT_STATUS_IS_OK(status) && io) {
+ /* domain policy handle closed successfully */
+
+ ZERO_STRUCT(ctx->samr.handle);
+ talloc_free(discard_const_p(char, ctx->samr.name));
+ talloc_free(ctx->samr.sid);
+ ctx->samr.name = NULL;
+ ctx->samr.sid = NULL;
+
+ io->out.error_string = talloc_asprintf(mem_ctx, "Success");
+
+ } else if (!NT_STATUS_IS_OK(status)) {
+ /* there was an error, so return description of the status code */
+ io->out.error_string = talloc_asprintf(mem_ctx, "Error: %s", nt_errstr(status));
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+
+struct composite_context* libnet_DomainClose_send(struct libnet_context *ctx,
+ struct libnet_DomainClose *io,
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *c;
+
+ switch (io->in.type) {
+ case DOMAIN_LSA:
+ /* request to close policy handle on \pipe\lsarpc */
+ c = libnet_DomainCloseLsa_send(ctx, io, monitor);
+ break;
+
+ case DOMAIN_SAMR:
+ default:
+ /* request to close domain policy handle on \pipe\samr */
+ c = libnet_DomainCloseSamr_send(ctx, io, monitor);
+ break;
+ }
+
+ return c;
+}
+
+
+NTSTATUS libnet_DomainClose_recv(struct composite_context *c, struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx, struct libnet_DomainClose *io)
+{
+ NTSTATUS status;
+
+ switch (io->in.type) {
+ case DOMAIN_LSA:
+ /* receive result of closing lsa policy handle */
+ status = libnet_DomainCloseLsa_recv(c, ctx, mem_ctx, io);
+ break;
+
+ case DOMAIN_SAMR:
+ default:
+ /* receive result of closing samr domain policy handle */
+ status = libnet_DomainCloseSamr_recv(c, ctx, mem_ctx, io);
+ break;
+ }
+
+ return status;
+}
+
+
+NTSTATUS libnet_DomainClose(struct libnet_context *ctx, TALLOC_CTX *mem_ctx,
+ struct libnet_DomainClose *io)
+{
+ struct composite_context *c;
+
+ c = libnet_DomainClose_send(ctx, io, NULL);
+ return libnet_DomainClose_recv(c, ctx, mem_ctx, io);
+}
+
+
+struct domain_list_state {
+ struct libnet_context *ctx;
+ struct libnet_RpcConnect rpcconn;
+ struct samr_Connect samrconn;
+ struct samr_EnumDomains enumdom;
+ struct samr_Close samrclose;
+ const char *hostname;
+ struct policy_handle connect_handle;
+ int buf_size;
+ struct domainlist *domains;
+ uint32_t resume_handle;
+ uint32_t count;
+
+ void (*monitor_fn)(struct monitor_msg*);
+};
+
+
+static void continue_rpc_connect(struct composite_context *c);
+static void continue_samr_connect(struct rpc_request *c);
+static void continue_samr_enum_domains(struct rpc_request *req);
+static void continue_samr_close_handle(struct rpc_request *req);
+
+static struct domainlist* get_domain_list(TALLOC_CTX *mem_ctx, struct domain_list_state *s);
+
+
+/*
+ Stage 1: Receive connected rpc pipe and send connection
+ request to SAMR service
+*/
+static void continue_rpc_connect(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct domain_list_state *s;
+ struct rpc_request *samrconn_req;
+
+ c = talloc_get_type(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct domain_list_state);
+
+ c->status = libnet_RpcConnect_recv(ctx, s->ctx, c, &s->rpcconn);
+ if (!composite_is_ok(c)) return;
+
+ s->samrconn.in.system_name = 0;
+ s->samrconn.in.access_mask = SEC_GENERIC_READ; /* should be enough */
+ s->samrconn.out.connect_handle = &s->connect_handle;
+
+ samrconn_req = dcerpc_samr_Connect_send(s->ctx->samr.pipe, c, &s->samrconn);
+ if (composite_nomem(samrconn_req, c)) return;
+
+ composite_continue_rpc(c, samrconn_req, continue_samr_connect, c);
+}
+
+
+/*
+ Stage 2: Receive policy handle to the connected SAMR service and issue
+ a request to enumerate domain databases available
+*/
+static void continue_samr_connect(struct rpc_request *req)
+{
+ struct composite_context *c;
+ struct domain_list_state *s;
+ struct rpc_request *enumdom_req;
+
+ c = talloc_get_type(req->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct domain_list_state);
+
+ c->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(c)) return;
+
+ if (s->monitor_fn) {
+ struct monitor_msg msg;
+
+ msg.type = mon_SamrConnect;
+ msg.data = NULL;
+ msg.data_size = 0;
+ s->monitor_fn(&msg);
+ }
+
+ s->enumdom.in.connect_handle = &s->connect_handle;
+ s->enumdom.in.resume_handle = &s->resume_handle;
+ s->enumdom.in.buf_size = s->buf_size;
+ s->enumdom.out.resume_handle = &s->resume_handle;
+ s->enumdom.out.num_entries = talloc(s, uint32_t);
+ if (composite_nomem(s->enumdom.out.num_entries, c)) return;
+ s->enumdom.out.sam = talloc(s, struct samr_SamArray *);
+ if (composite_nomem(s->enumdom.out.sam, c)) return;
+
+ enumdom_req = dcerpc_samr_EnumDomains_send(s->ctx->samr.pipe, c, &s->enumdom);
+ if (composite_nomem(enumdom_req, c)) return;
+
+ composite_continue_rpc(c, enumdom_req, continue_samr_enum_domains, c);
+}
+
+
+/*
+ Stage 3: Receive domain names available and repeat the request
+ enumeration is not complete yet. Close samr connection handle
+ upon completion.
+*/
+static void continue_samr_enum_domains(struct rpc_request *req)
+{
+ struct composite_context *c;
+ struct domain_list_state *s;
+ struct rpc_request *enumdom_req;
+ struct rpc_request *samrclose_req;
+
+ c = talloc_get_type(req->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct domain_list_state);
+
+ c->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(c)) return;
+
+ if (s->monitor_fn) {
+ struct monitor_msg msg;
+
+ msg.type = mon_SamrEnumDomains;
+ msg.data = NULL;
+ msg.data_size = 0;
+ s->monitor_fn(&msg);
+ }
+
+ if (NT_STATUS_IS_OK(s->enumdom.out.result)) {
+
+ s->domains = get_domain_list(c, s);
+
+ } else if (NT_STATUS_EQUAL(s->enumdom.out.result, STATUS_MORE_ENTRIES)) {
+
+ s->domains = get_domain_list(c, s);
+
+ /* prepare next round of enumeration */
+ s->enumdom.in.connect_handle = &s->connect_handle;
+ s->enumdom.in.resume_handle = &s->resume_handle;
+ s->enumdom.in.buf_size = s->ctx->samr.buf_size;
+ s->enumdom.out.resume_handle = &s->resume_handle;
+
+ /* send the request */
+ enumdom_req = dcerpc_samr_EnumDomains_send(s->ctx->samr.pipe, c, &s->enumdom);
+ if (composite_nomem(enumdom_req, c)) return;
+
+ composite_continue_rpc(c, enumdom_req, continue_samr_enum_domains, c);
+
+ } else {
+ composite_error(c, s->enumdom.out.result);
+ return;
+ }
+
+ /* close samr connection handle */
+ s->samrclose.in.handle = &s->connect_handle;
+ s->samrclose.out.handle = &s->connect_handle;
+
+ /* send the request */
+ samrclose_req = dcerpc_samr_Close_send(s->ctx->samr.pipe, c, &s->samrclose);
+ if (composite_nomem(samrclose_req, c)) return;
+
+ composite_continue_rpc(c, samrclose_req, continue_samr_close_handle, c);
+}
+
+
+/*
+ Stage 4: Receive result of closing samr connection handle.
+*/
+static void continue_samr_close_handle(struct rpc_request *req)
+{
+ struct composite_context *c;
+ struct domain_list_state *s;
+
+ c = talloc_get_type(req->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct domain_list_state);
+
+ c->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(c)) return;
+
+ if (s->monitor_fn) {
+ struct monitor_msg msg;
+
+ msg.type = mon_SamrClose;
+ msg.data = NULL;
+ msg.data_size = 0;
+ s->monitor_fn(&msg);
+ }
+
+ /* did everything go fine ? */
+ if (!NT_STATUS_IS_OK(s->samrclose.out.result)) {
+ composite_error(c, s->samrclose.out.result);
+ }
+
+ composite_done(c);
+}
+
+
+/*
+ Utility function to copy domain names from result of samr_EnumDomains call
+*/
+static struct domainlist* get_domain_list(TALLOC_CTX *mem_ctx, struct domain_list_state *s)
+{
+ int i;
+ if (mem_ctx == NULL || s == NULL) return NULL;
+
+ /* prepare domains array */
+ if (s->domains == NULL) {
+ s->domains = talloc_array(mem_ctx, struct domainlist,
+ *s->enumdom.out.num_entries);
+ } else {
+ s->domains = talloc_realloc(mem_ctx, s->domains, struct domainlist,
+ s->count + *s->enumdom.out.num_entries);
+ }
+
+ /* copy domain names returned from samr_EnumDomains call */
+ for (i = s->count; i < s->count + *s->enumdom.out.num_entries; i++)
+ {
+ struct lsa_String *domain_name = &(*s->enumdom.out.sam)->entries[i - s->count].name;
+
+ /* strdup name as a child of allocated array to make it follow the array
+ in case of talloc_steal or talloc_free */
+ s->domains[i].name = talloc_strdup(s->domains, domain_name->string);
+ s->domains[i].sid = NULL; /* this is to be filled out later */
+ }
+
+ /* number of entries returned (domains enumerated) */
+ s->count += *s->enumdom.out.num_entries;
+
+ return s->domains;
+}
+
+
+/**
+ * Sends a request to list domains on given host
+ *
+ * @param ctx initalised libnet context
+ * @param mem_ctx memory context
+ * @param io arguments and results of the call
+ * @param monitor pointer to monitor function that is passed monitor messages
+ */
+
+struct composite_context* libnet_DomainList_send(struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_DomainList *io,
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *c;
+ struct domain_list_state *s;
+ struct composite_context *rpcconn_req;
+ struct rpc_request *samrconn_req;
+
+ /* composite context and state structure allocation */
+ c = composite_create(ctx, ctx->event_ctx);
+ if (c == NULL) return c;
+
+ s = talloc_zero(c, struct domain_list_state);
+ if (composite_nomem(s, c)) return c;
+
+ c->private_data = s;
+ s->monitor_fn = monitor;
+
+ s->ctx = ctx;
+ s->hostname = talloc_strdup(c, io->in.hostname);
+ if (composite_nomem(s->hostname, c)) return c;
+
+ /* check whether samr pipe has already been opened */
+ if (ctx->samr.pipe == NULL) {
+ /* prepare rpc connect call */
+ s->rpcconn.level = LIBNET_RPC_CONNECT_SERVER;
+ s->rpcconn.in.name = s->hostname;
+ s->rpcconn.in.dcerpc_iface = &ndr_table_samr;
+
+ rpcconn_req = libnet_RpcConnect_send(ctx, c, &s->rpcconn, s->monitor_fn);
+ if (composite_nomem(rpcconn_req, c)) return c;
+
+ composite_continue(c, rpcconn_req, continue_rpc_connect, c);
+
+ } else {
+ /* prepare samr_Connect call */
+ s->samrconn.in.system_name = 0;
+ s->samrconn.in.access_mask = SEC_GENERIC_READ;
+ s->samrconn.out.connect_handle = &s->connect_handle;
+
+ samrconn_req = dcerpc_samr_Connect_send(s->ctx->samr.pipe, c, &s->samrconn);
+ if (composite_nomem(samrconn_req, c)) return c;
+
+ composite_continue_rpc(c, samrconn_req, continue_samr_connect, c);
+ }
+
+ return c;
+}
+
+
+/**
+ * Receive result of domain list request
+ *
+ * @param c composite context returned by DomainList_send function
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of the call
+ * @param io results and arguments of the call
+ */
+
+NTSTATUS libnet_DomainList_recv(struct composite_context *c, struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx, struct libnet_DomainList *io)
+{
+ NTSTATUS status;
+ struct domain_list_state *s;
+
+ status = composite_wait(c);
+
+ s = talloc_get_type(c->private_data, struct domain_list_state);
+
+ if (NT_STATUS_IS_OK(status) && ctx && mem_ctx && io) {
+ /* fetch the results to be returned by io structure */
+ io->out.count = s->count;
+ io->out.domains = talloc_steal(mem_ctx, s->domains);
+ io->out.error_string = talloc_asprintf(mem_ctx, "Success");
+
+ } else if (!NT_STATUS_IS_OK(status)) {
+ /* there was an error, so return description of the status code */
+ io->out.error_string = talloc_asprintf(mem_ctx, "Error: %s", nt_errstr(status));
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+
+/**
+ * Synchronous version of DomainList call
+ *
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context for the call
+ * @param io arguments and results of the call
+ * @return nt status code of execution
+ */
+
+NTSTATUS libnet_DomainList(struct libnet_context *ctx, TALLOC_CTX *mem_ctx,
+ struct libnet_DomainList *io)
+{
+ struct composite_context *c;
+
+ c = libnet_DomainList_send(ctx, mem_ctx, io, NULL);
+ return libnet_DomainList_recv(c, ctx, mem_ctx, io);
+}
diff --git a/source4/libnet/libnet_domain.h b/source4/libnet/libnet_domain.h
new file mode 100644
index 0000000000..ae698e3991
--- /dev/null
+++ b/source4/libnet/libnet_domain.h
@@ -0,0 +1,70 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+
+enum service_type { DOMAIN_SAMR, DOMAIN_LSA };
+
+/*
+ * struct definition for opening a domain
+ */
+
+struct libnet_DomainOpen {
+ struct {
+ enum service_type type;
+ const char *domain_name;
+ uint32_t access_mask;
+ } in;
+ struct {
+ struct policy_handle domain_handle;
+ const char *error_string;
+ } out;
+};
+
+
+struct libnet_DomainClose {
+ struct {
+ enum service_type type;
+ const char *domain_name;
+ } in;
+ struct {
+ const char *error_string;
+ } out;
+};
+
+
+struct libnet_DomainList {
+ struct {
+ const char *hostname;
+ } in;
+ struct {
+ int count;
+
+ struct domainlist {
+ const char *sid;
+ const char *name;
+ } *domains;
+
+ const char *error_string;
+ } out;
+};
+
+
+struct msg_rpc_lookup_domain {
+ const char *domain_name;
+};
diff --git a/source4/libnet/libnet_group.c b/source4/libnet/libnet_group.c
new file mode 100644
index 0000000000..b0669640f3
--- /dev/null
+++ b/source4/libnet/libnet_group.c
@@ -0,0 +1,745 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+
+#include "includes.h"
+#include "libnet/libnet.h"
+#include "libcli/composite/composite.h"
+#include "librpc/gen_ndr/lsa.h"
+#include "librpc/gen_ndr/ndr_lsa_c.h"
+#include "librpc/gen_ndr/samr.h"
+#include "librpc/gen_ndr/ndr_samr_c.h"
+#include "libcli/security/security.h"
+
+
+struct create_group_state {
+ struct libnet_context *ctx;
+ struct libnet_CreateGroup r;
+ struct libnet_DomainOpen domain_open;
+ struct libnet_rpc_groupadd group_add;
+
+ /* information about the progress */
+ void (*monitor_fn)(struct monitor_msg *);
+};
+
+
+static void continue_domain_opened(struct composite_context *ctx);
+static void continue_rpc_group_added(struct composite_context *ctx);
+
+
+struct composite_context* libnet_CreateGroup_send(struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_CreateGroup *r,
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *c;
+ struct create_group_state *s;
+ struct composite_context *create_req;
+ bool prereq_met = false;
+
+ /* composite context allocation and setup */
+ c = composite_create(mem_ctx, ctx->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct create_group_state);
+ if (composite_nomem(s, c)) return c;
+
+ c->private_data = s;
+
+ s->ctx = ctx;
+ s->r = *r;
+ ZERO_STRUCT(s->r.out);
+
+ /* prerequisite: make sure we have a valid samr domain handle */
+ prereq_met = samr_domain_opened(ctx, s->r.in.domain_name, &c, &s->domain_open,
+ continue_domain_opened, monitor);
+ if (!prereq_met) return c;
+
+ /* prepare arguments of rpc group add call */
+ s->group_add.in.groupname = r->in.group_name;
+ s->group_add.in.domain_handle = ctx->samr.handle;
+
+ /* send the request */
+ create_req = libnet_rpc_groupadd_send(ctx->samr.pipe, &s->group_add, monitor);
+ if (composite_nomem(create_req, c)) return c;
+
+ composite_continue(c, create_req, continue_rpc_group_added, c);
+ return c;
+}
+
+
+static void continue_domain_opened(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct create_group_state *s;
+ struct composite_context *create_req;
+
+ c = talloc_get_type(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct create_group_state);
+
+ c->status = libnet_DomainOpen_recv(ctx, s->ctx, c, &s->domain_open);
+ if (!composite_is_ok(c)) return;
+
+ /* prepare arguments of groupadd call */
+ s->group_add.in.groupname = s->r.in.group_name;
+ s->group_add.in.domain_handle = s->ctx->samr.handle;
+
+ /* send the request */
+ create_req = libnet_rpc_groupadd_send(s->ctx->samr.pipe, &s->group_add,
+ s->monitor_fn);
+ if (composite_nomem(create_req, c)) return;
+
+ composite_continue(c, create_req, continue_rpc_group_added, c);
+}
+
+
+static void continue_rpc_group_added(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct create_group_state *s;
+
+ c = talloc_get_type(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct create_group_state);
+
+ /* receive result of group add call */
+ c->status = libnet_rpc_groupadd_recv(ctx, c, &s->group_add);
+ if (!composite_is_ok(c)) return;
+
+ /* we're done */
+ composite_done(c);
+}
+
+
+/**
+ * Receive result of CreateGroup call
+ *
+ * @param c composite context returned by send request routine
+ * @param mem_ctx memory context of this call
+ * @param r pointer to a structure containing arguments and result of this call
+ * @return nt status
+ */
+NTSTATUS libnet_CreateGroup_recv(struct composite_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_CreateGroup *r)
+{
+ NTSTATUS status;
+ struct create_group_state *s;
+
+ status = composite_wait(c);
+ if (!NT_STATUS_IS_OK(status)) {
+ s = talloc_get_type(c->private_data, struct create_group_state);
+ r->out.error_string = talloc_strdup(mem_ctx, nt_errstr(status));
+ }
+
+ return status;
+}
+
+
+/**
+ * Create domain group
+ *
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param io pointer to structure containing arguments and result of this call
+ * @return nt status
+ */
+NTSTATUS libnet_CreateGroup(struct libnet_context *ctx, TALLOC_CTX *mem_ctx,
+ struct libnet_CreateGroup *io)
+{
+ struct composite_context *c;
+
+ c = libnet_CreateGroup_send(ctx, mem_ctx, io, NULL);
+ return libnet_CreateGroup_recv(c, mem_ctx, io);
+}
+
+
+struct group_info_state {
+ struct libnet_context *ctx;
+ const char *domain_name;
+ enum libnet_GroupInfo_level level;
+ const char *group_name;
+ const char *sid_string;
+ struct libnet_LookupName lookup;
+ struct libnet_DomainOpen domopen;
+ struct libnet_rpc_groupinfo info;
+
+ /* information about the progress */
+ void (*monitor_fn)(struct monitor_msg *);
+};
+
+
+static void continue_domain_open_info(struct composite_context *ctx);
+static void continue_name_found(struct composite_context *ctx);
+static void continue_group_info(struct composite_context *ctx);
+
+/**
+ * Sends request to get group information
+ *
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param io pointer to structure containing arguments the call
+ * @param monitor function pointer for receiving monitor messages
+ * @return composite context of this request
+ */
+struct composite_context* libnet_GroupInfo_send(struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_GroupInfo *io,
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *c;
+ struct group_info_state *s;
+ bool prereq_met = false;
+ struct composite_context *lookup_req, *info_req;
+
+ /* composite context allocation and setup */
+ c = composite_create(mem_ctx, ctx->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct group_info_state);
+ if (composite_nomem(s, c)) return c;
+
+ c->private_data = s;
+
+ /* store arguments in the state structure */
+ s->monitor_fn = monitor;
+ s->ctx = ctx;
+ s->domain_name = talloc_strdup(c, io->in.domain_name);
+ s->level = io->in.level;
+ switch(s->level) {
+ case GROUP_INFO_BY_NAME:
+ s->group_name = talloc_strdup(c, io->in.data.group_name);
+ s->sid_string = NULL;
+ break;
+ case GROUP_INFO_BY_SID:
+ s->group_name = NULL;
+ s->sid_string = dom_sid_string(c, io->in.data.group_sid);
+ break;
+ }
+
+ /* prerequisite: make sure the domain is opened */
+ prereq_met = samr_domain_opened(ctx, s->domain_name, &c, &s->domopen,
+ continue_domain_open_info, monitor);
+ if (!prereq_met) return c;
+
+ switch(s->level) {
+ case GROUP_INFO_BY_NAME:
+ /* prepare arguments for LookupName call */
+ s->lookup.in.name = s->group_name;
+ s->lookup.in.domain_name = s->domain_name;
+
+ /* send the request */
+ lookup_req = libnet_LookupName_send(s->ctx, c, &s->lookup, s->monitor_fn);
+ if (composite_nomem(lookup_req, c)) return c;
+
+ /* set the next stage */
+ composite_continue(c, lookup_req, continue_name_found, c);
+ break;
+ case GROUP_INFO_BY_SID:
+ /* prepare arguments for groupinfo call */
+ s->info.in.domain_handle = s->ctx->samr.handle;
+ s->info.in.sid = s->sid_string;
+ /* we're looking for all information available */
+ s->info.in.level = GROUPINFOALL;
+
+ /* send the request */
+ info_req = libnet_rpc_groupinfo_send(s->ctx->samr.pipe, &s->info, s->monitor_fn);
+ if (composite_nomem(info_req, c)) return c;
+
+ /* set the next stage */
+ composite_continue(c, info_req, continue_group_info, c);
+ break;
+ }
+
+ return c;
+}
+
+
+/*
+ * Stage 0.5 (optional): receive opened domain and send lookup name request
+ */
+static void continue_domain_open_info(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct group_info_state *s;
+ struct composite_context *lookup_req, *info_req;
+
+ c = talloc_get_type(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct group_info_state);
+
+ /* receive domain handle */
+ c->status = libnet_DomainOpen_recv(ctx, s->ctx, c, &s->domopen);
+ if (!composite_is_ok(c)) return;
+
+ switch(s->level) {
+ case GROUP_INFO_BY_NAME:
+ /* prepare arguments for LookupName call */
+ s->lookup.in.name = s->group_name;
+ s->lookup.in.domain_name = s->domain_name;
+
+ /* send the request */
+ lookup_req = libnet_LookupName_send(s->ctx, c, &s->lookup, s->monitor_fn);
+ if (composite_nomem(lookup_req, c)) return;
+
+ /* set the next stage */
+ composite_continue(c, lookup_req, continue_name_found, c);
+ break;
+ case GROUP_INFO_BY_SID:
+ /* prepare arguments for groupinfo call */
+ s->info.in.domain_handle = s->ctx->samr.handle;
+ s->info.in.sid = s->sid_string;
+ /* we're looking for all information available */
+ s->info.in.level = GROUPINFOALL;
+
+ /* send the request */
+ info_req = libnet_rpc_groupinfo_send(s->ctx->samr.pipe, &s->info, s->monitor_fn);
+ if (composite_nomem(info_req, c)) return;
+
+ /* set the next stage */
+ composite_continue(c, info_req, continue_group_info, c);
+ break;
+
+ }
+}
+
+
+/*
+ * Stage 1: Receive SID found and send request for group info
+ */
+static void continue_name_found(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct group_info_state *s;
+ struct composite_context *info_req;
+
+ c = talloc_get_type(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct group_info_state);
+
+ /* receive SID assiociated with name found */
+ c->status = libnet_LookupName_recv(ctx, c, &s->lookup);
+ if (!composite_is_ok(c)) return;
+
+ /* Is is a group SID actually ? */
+ if (s->lookup.out.sid_type != SID_NAME_DOM_GRP &&
+ s->lookup.out.sid_type != SID_NAME_ALIAS) {
+ composite_error(c, NT_STATUS_NO_SUCH_GROUP);
+ }
+
+ /* prepare arguments for groupinfo call */
+ s->info.in.domain_handle = s->ctx->samr.handle;
+ s->info.in.groupname = s->group_name;
+ s->info.in.sid = s->lookup.out.sidstr;
+ /* we're looking for all information available */
+ s->info.in.level = GROUPINFOALL;
+
+ /* send the request */
+ info_req = libnet_rpc_groupinfo_send(s->ctx->samr.pipe, &s->info, s->monitor_fn);
+ if (composite_nomem(info_req, c)) return;
+
+ /* set the next stage */
+ composite_continue(c, info_req, continue_group_info, c);
+}
+
+
+/*
+ * Stage 2: Receive group information
+ */
+static void continue_group_info(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct group_info_state *s;
+
+ c = talloc_get_type(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct group_info_state);
+
+ /* receive group information */
+ c->status = libnet_rpc_groupinfo_recv(ctx, c, &s->info);
+ if (!composite_is_ok(c)) return;
+
+ /* we're done */
+ composite_done(c);
+}
+
+
+/*
+ * Receive group information
+ *
+ * @param c composite context returned by libnet_GroupInfo_send
+ * @param mem_ctx memory context of this call
+ * @param io pointer to structure receiving results of the call
+ * @result nt status
+ */
+NTSTATUS libnet_GroupInfo_recv(struct composite_context* c, TALLOC_CTX *mem_ctx,
+ struct libnet_GroupInfo *io)
+{
+ NTSTATUS status;
+ struct group_info_state *s;
+
+ status = composite_wait(c);
+ if (NT_STATUS_IS_OK(status)) {
+ /* put the results into io structure if everything went fine */
+ s = talloc_get_type(c->private_data, struct group_info_state);
+
+ io->out.group_name = talloc_steal(mem_ctx,
+ s->info.out.info.all.name.string);
+ io->out.group_sid = talloc_steal(mem_ctx, s->lookup.out.sid);
+ io->out.num_members = s->info.out.info.all.num_members;
+ io->out.description = talloc_steal(mem_ctx, s->info.out.info.all.description.string);
+
+ io->out.error_string = talloc_strdup(mem_ctx, "Success");
+
+ } else {
+ io->out.error_string = talloc_asprintf(mem_ctx, "Error: %s", nt_errstr(status));
+ }
+
+ talloc_free(c);
+
+ return status;
+}
+
+
+/**
+ * Obtains specified group information
+ *
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of the call
+ * @param io pointer to a structure containing arguments and results of the call
+ */
+NTSTATUS libnet_GroupInfo(struct libnet_context *ctx, TALLOC_CTX *mem_ctx,
+ struct libnet_GroupInfo *io)
+{
+ struct composite_context *c = libnet_GroupInfo_send(ctx, mem_ctx,
+ io, NULL);
+ return libnet_GroupInfo_recv(c, mem_ctx, io);
+}
+
+
+struct grouplist_state {
+ struct libnet_context *ctx;
+ const char *domain_name;
+ struct lsa_DomainInfo dominfo;
+ int page_size;
+ uint32_t resume_index;
+ struct grouplist *groups;
+ uint32_t count;
+
+ struct libnet_DomainOpen domain_open;
+ struct lsa_QueryInfoPolicy query_domain;
+ struct samr_EnumDomainGroups group_list;
+
+ void (*monitor_fn)(struct monitor_msg*);
+};
+
+
+static void continue_lsa_domain_opened(struct composite_context *ctx);
+static void continue_domain_queried(struct rpc_request *req);
+static void continue_samr_domain_opened(struct composite_context *ctx);
+static void continue_domain_queried(struct rpc_request *req);
+static void continue_groups_enumerated(struct rpc_request *req);
+
+
+/**
+ * Sends request to list (enumerate) group accounts
+ *
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param io pointer to structure containing arguments and results of this call
+ * @param monitor function pointer for receiving monitor messages
+ * @return compostite context of this request
+ */
+struct composite_context *libnet_GroupList_send(struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_GroupList *io,
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *c;
+ struct grouplist_state *s;
+ struct rpc_request *query_req;
+ bool prereq_met = false;
+
+ /* composite context allocation and setup */
+ c = composite_create(mem_ctx, ctx->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct grouplist_state);
+ if (composite_nomem(s, c)) return c;
+
+ c->private_data = s;
+
+ /* store the arguments in the state structure */
+ s->ctx = ctx;
+ s->page_size = io->in.page_size;
+ s->resume_index = (uint32_t)io->in.resume_index;
+ s->domain_name = talloc_strdup(c, io->in.domain_name);
+ s->monitor_fn = monitor;
+
+ /* make sure we have lsa domain handle before doing anything */
+ prereq_met = lsa_domain_opened(ctx, s->domain_name, &c, &s->domain_open,
+ continue_lsa_domain_opened, monitor);
+ if (!prereq_met) return c;
+
+ /* prepare arguments of QueryDomainInfo call */
+ s->query_domain.in.handle = &ctx->lsa.handle;
+ s->query_domain.in.level = LSA_POLICY_INFO_DOMAIN;
+ s->query_domain.out.info = talloc_zero(c, union lsa_PolicyInformation *);
+ if (composite_nomem(s->query_domain.out.info, c)) return c;
+
+ /* send the request */
+ query_req = dcerpc_lsa_QueryInfoPolicy_send(ctx->lsa.pipe, c, &s->query_domain);
+ if (composite_nomem(query_req, c)) return c;
+
+ composite_continue_rpc(c, query_req, continue_domain_queried, c);
+ return c;
+}
+
+
+/*
+ * Stage 0.5 (optional): receive lsa domain handle and send
+ * request to query domain info
+ */
+static void continue_lsa_domain_opened(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct grouplist_state *s;
+ struct rpc_request *query_req;
+
+ c = talloc_get_type(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct grouplist_state);
+
+ /* receive lsa domain handle */
+ c->status = libnet_DomainOpen_recv(ctx, s->ctx, c, &s->domain_open);
+ if (!composite_is_ok(c)) return;
+
+ /* prepare arguments of QueryDomainInfo call */
+ s->query_domain.in.handle = &s->ctx->lsa.handle;
+ s->query_domain.in.level = LSA_POLICY_INFO_DOMAIN;
+ s->query_domain.out.info = talloc_zero(c, union lsa_PolicyInformation *);
+ if (composite_nomem(s->query_domain.out.info, c)) return;
+
+ /* send the request */
+ query_req = dcerpc_lsa_QueryInfoPolicy_send(s->ctx->lsa.pipe, c, &s->query_domain);
+ if (composite_nomem(query_req, c)) return;
+
+ composite_continue_rpc(c, query_req, continue_domain_queried, c);
+}
+
+
+/*
+ * Stage 1: receive domain info and request to enum groups
+ * provided a valid samr handle is opened
+ */
+static void continue_domain_queried(struct rpc_request *req)
+{
+ struct composite_context *c;
+ struct grouplist_state *s;
+ struct rpc_request *enum_req;
+ bool prereq_met = false;
+
+ c = talloc_get_type(req->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct grouplist_state);
+
+ /* receive result of rpc request */
+ c->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(c)) return;
+
+ /* get the returned domain info */
+ s->dominfo = (*s->query_domain.out.info)->domain;
+
+ /* make sure we have samr domain handle before continuing */
+ prereq_met = samr_domain_opened(s->ctx, s->domain_name, &c, &s->domain_open,
+ continue_samr_domain_opened, s->monitor_fn);
+ if (!prereq_met) return;
+
+ /* prepare arguments od EnumDomainGroups call */
+ s->group_list.in.domain_handle = &s->ctx->samr.handle;
+ s->group_list.in.max_size = s->page_size;
+ s->group_list.in.resume_handle = &s->resume_index;
+ s->group_list.out.resume_handle = &s->resume_index;
+ s->group_list.out.num_entries = talloc(s, uint32_t);
+ if (composite_nomem(s->group_list.out.num_entries, c)) return;
+ s->group_list.out.sam = talloc(s, struct samr_SamArray *);
+ if (composite_nomem(s->group_list.out.sam, c)) return;
+
+ /* send the request */
+ enum_req = dcerpc_samr_EnumDomainGroups_send(s->ctx->samr.pipe, c, &s->group_list);
+ if (composite_nomem(enum_req, c)) return;
+
+ composite_continue_rpc(c, enum_req, continue_groups_enumerated, c);
+}
+
+
+/*
+ * Stage 1.5 (optional): receive samr domain handle
+ * and request to enumerate accounts
+ */
+static void continue_samr_domain_opened(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct grouplist_state *s;
+ struct rpc_request *enum_req;
+
+ c = talloc_get_type(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct grouplist_state);
+
+ /* receive samr domain handle */
+ c->status = libnet_DomainOpen_recv(ctx, s->ctx, c, &s->domain_open);
+ if (!composite_is_ok(c)) return;
+
+ /* prepare arguments of EnumDomainGroups call */
+ s->group_list.in.domain_handle = &s->ctx->samr.handle;
+ s->group_list.in.max_size = s->page_size;
+ s->group_list.in.resume_handle = &s->resume_index;
+ s->group_list.out.resume_handle = &s->resume_index;
+ s->group_list.out.num_entries = talloc(s, uint32_t);
+ if (composite_nomem(s->group_list.out.num_entries, c)) return;
+ s->group_list.out.sam = talloc(s, struct samr_SamArray *);
+ if (composite_nomem(s->group_list.out.sam, c)) return;
+
+ /* send the request */
+ enum_req = dcerpc_samr_EnumDomainGroups_send(s->ctx->samr.pipe, c, &s->group_list);
+ if (composite_nomem(enum_req, c)) return;
+
+ composite_continue_rpc(c, enum_req, continue_groups_enumerated, c);
+}
+
+
+/*
+ * Stage 2: receive enumerated groups and their rids
+ */
+static void continue_groups_enumerated(struct rpc_request *req)
+{
+ struct composite_context *c;
+ struct grouplist_state *s;
+ int i;
+
+ c = talloc_get_type(req->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct grouplist_state);
+
+ /* receive result of rpc request */
+ c->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(c)) return;
+
+ /* get the actual status of the rpc call result
+ (instead of rpc layer) */
+ c->status = s->group_list.out.result;
+
+ /* we're interested in status "ok" as well as two
+ enum-specific status codes */
+ if (NT_STATUS_IS_OK(c->status) ||
+ NT_STATUS_EQUAL(c->status, STATUS_MORE_ENTRIES) ||
+ NT_STATUS_EQUAL(c->status, NT_STATUS_NO_MORE_ENTRIES)) {
+
+ /* get enumerated accounts counter and resume handle (the latter allows
+ making subsequent call to continue enumeration) */
+ s->resume_index = *s->group_list.out.resume_handle;
+ s->count = *s->group_list.out.num_entries;
+
+ /* prepare returned group accounts array */
+ s->groups = talloc_array(c, struct grouplist, (*s->group_list.out.sam)->count);
+ if (composite_nomem(s->groups, c)) return;
+
+ for (i = 0; i < (*s->group_list.out.sam)->count; i++) {
+ struct dom_sid *group_sid;
+ struct samr_SamEntry *entry = &(*s->group_list.out.sam)->entries[i];
+ struct dom_sid *domain_sid = (*s->query_domain.out.info)->domain.sid;
+
+ /* construct group sid from returned rid and queried domain sid */
+ group_sid = dom_sid_add_rid(c, domain_sid, entry->idx);
+ if (composite_nomem(group_sid, c)) return;
+
+ /* groupname */
+ s->groups[i].groupname = talloc_strdup(c, entry->name.string);
+ if (composite_nomem(s->groups[i].groupname, c)) return;
+
+ /* sid string */
+ s->groups[i].sid = dom_sid_string(c, group_sid);
+ if (composite_nomem(s->groups[i].sid, c)) return;
+ }
+
+ /* that's it */
+ composite_done(c);
+
+ } else {
+ /* something went wrong */
+ composite_error(c, c->status);
+ }
+}
+
+
+/**
+ * Receive result of GroupList call
+ *
+ * @param c composite context returned by send request routine
+ * @param mem_ctx memory context of this call
+ * @param io pointer to structure containing arguments and result of this call
+ * @param nt status
+ */
+NTSTATUS libnet_GroupList_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
+ struct libnet_GroupList *io)
+{
+ NTSTATUS status;
+ struct grouplist_state *s;
+
+ if (c == NULL || mem_ctx == NULL || io == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = composite_wait(c);
+ if (NT_STATUS_IS_OK(status) ||
+ NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES)) {
+
+ s = talloc_get_type(c->private_data, struct grouplist_state);
+
+ /* get results from composite context */
+ io->out.count = s->count;
+ io->out.resume_index = s->resume_index;
+ io->out.groups = talloc_steal(mem_ctx, s->groups);
+
+ if (NT_STATUS_IS_OK(status)) {
+ io->out.error_string = talloc_asprintf(mem_ctx, "Success");
+ } else {
+ /* success, but we're not done yet */
+ io->out.error_string = talloc_asprintf(mem_ctx, "Success (status: %s)",
+ nt_errstr(status));
+ }
+
+ } else {
+ io->out.error_string = talloc_asprintf(mem_ctx, "Error: %s", nt_errstr(status));
+ }
+
+ return status;
+}
+
+
+/**
+ * Enumerate domain groups
+ *
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param io pointer to structure containing arguments and result of this call
+ * @return nt status
+ */
+NTSTATUS libnet_GroupList(struct libnet_context *ctx, TALLOC_CTX *mem_ctx,
+ struct libnet_GroupList *io)
+{
+ struct composite_context *c;
+
+ c = libnet_GroupList_send(ctx, mem_ctx, io, NULL);
+ return libnet_GroupList_recv(c, mem_ctx, io);
+}
diff --git a/source4/libnet/libnet_group.h b/source4/libnet/libnet_group.h
new file mode 100644
index 0000000000..b80d3449c8
--- /dev/null
+++ b/source4/libnet/libnet_group.h
@@ -0,0 +1,74 @@
+/*
+ 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+
+struct libnet_CreateGroup {
+ struct {
+ const char *group_name;
+ const char *domain_name;
+ } in;
+ struct {
+ const char *error_string;
+ } out;
+};
+
+enum libnet_GroupInfo_level {
+ GROUP_INFO_BY_NAME=0,
+ GROUP_INFO_BY_SID
+};
+
+struct libnet_GroupInfo {
+ struct {
+ const char *domain_name;
+ enum libnet_GroupInfo_level level;
+ union {
+ const char *group_name;
+ const struct dom_sid *group_sid;
+ } data;
+ } in;
+ struct {
+ const char *group_name;
+ struct dom_sid *group_sid;
+ uint32_t num_members;
+ const char *description;
+
+ const char *error_string;
+ } out;
+};
+
+
+struct libnet_GroupList {
+ struct {
+ const char *domain_name;
+ int page_size;
+ uint resume_index;
+ } in;
+ struct {
+ int count;
+ uint resume_index;
+
+ struct grouplist {
+ const char *sid;
+ const char *groupname;
+ } *groups;
+
+ const char *error_string;
+ } out;
+};
diff --git a/source4/libnet/libnet_join.c b/source4/libnet/libnet_join.c
new file mode 100644
index 0000000000..0a4e357925
--- /dev/null
+++ b/source4/libnet/libnet_join.c
@@ -0,0 +1,1235 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Stefan Metzmacher 2004
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libnet/libnet.h"
+#include "librpc/gen_ndr/ndr_drsuapi_c.h"
+#include "lib/ldb/include/ldb.h"
+#include "lib/ldb/include/ldb_errors.h"
+#include "param/secrets.h"
+#include "dsdb/samdb/samdb.h"
+#include "ldb_wrap.h"
+#include "../lib/util/util_ldb.h"
+#include "libcli/security/security.h"
+#include "auth/credentials/credentials.h"
+#include "auth/credentials/credentials_krb5.h"
+#include "librpc/gen_ndr/ndr_samr_c.h"
+#include "param/param.h"
+
+/*
+ * complete a domain join, when joining to a AD domain:
+ * 1.) connect and bind to the DRSUAPI pipe
+ * 2.) do a DsCrackNames() to find the machine account dn
+ * 3.) connect to LDAP
+ * 4.) do an ldap search to find the "msDS-KeyVersionNumber" of the machine account
+ * 5.) set the servicePrincipalName's of the machine account via LDAP, (maybe we should use DsWriteAccountSpn()...)
+ * 6.) do a DsCrackNames() to find the domain dn
+ * 7.) find out Site specific stuff, look at libnet_JoinSite() for details
+ */
+static NTSTATUS libnet_JoinADSDomain(struct libnet_context *ctx, struct libnet_JoinDomain *r)
+{
+ NTSTATUS status;
+
+ TALLOC_CTX *tmp_ctx;
+
+ const char *realm = r->out.realm;
+
+ struct dcerpc_binding *samr_binding = r->out.samr_binding;
+
+ struct dcerpc_pipe *drsuapi_pipe;
+ struct dcerpc_binding *drsuapi_binding;
+ struct drsuapi_DsBind r_drsuapi_bind;
+ struct drsuapi_DsCrackNames r_crack_names;
+ struct drsuapi_DsNameString names[1];
+ struct policy_handle drsuapi_bind_handle;
+ struct GUID drsuapi_bind_guid;
+
+ struct ldb_context *remote_ldb;
+ struct ldb_dn *account_dn;
+ const char *account_dn_str;
+ const char *remote_ldb_url;
+ struct ldb_result *res;
+ struct ldb_message *msg;
+
+ int ret, rtn;
+
+ const char * const attrs[] = {
+ "msDS-KeyVersionNumber",
+ "servicePrincipalName",
+ "dNSHostName",
+ "objectGUID",
+ NULL,
+ };
+
+ r->out.error_string = NULL;
+
+ /* We need to convert between a samAccountName and domain to a
+ * DN in the directory. The correct way to do this is with
+ * DRSUAPI CrackNames */
+
+ /* Fiddle with the bindings, so get to DRSUAPI on
+ * NCACN_IP_TCP, sealed */
+ tmp_ctx = talloc_named(r, 0, "libnet_JoinADSDomain temp context");
+ if (!tmp_ctx) {
+ r->out.error_string = NULL;
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ drsuapi_binding = talloc(tmp_ctx, struct dcerpc_binding);
+ if (!drsuapi_binding) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ *drsuapi_binding = *samr_binding;
+
+ /* DRSUAPI is only available on IP_TCP, and locally on NCALRPC */
+ if (drsuapi_binding->transport != NCALRPC) {
+ drsuapi_binding->transport = NCACN_IP_TCP;
+ }
+ drsuapi_binding->endpoint = NULL;
+ drsuapi_binding->flags |= DCERPC_SEAL;
+
+ status = dcerpc_pipe_connect_b(tmp_ctx,
+ &drsuapi_pipe,
+ drsuapi_binding,
+ &ndr_table_drsuapi,
+ ctx->cred,
+ ctx->event_ctx,
+ ctx->lp_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->out.error_string = talloc_asprintf(r,
+ "Connection to DRSUAPI pipe of PDC of domain '%s' failed: %s",
+ r->out.domain_name,
+ nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ /* get a DRSUAPI pipe handle */
+ GUID_from_string(DRSUAPI_DS_BIND_GUID, &drsuapi_bind_guid);
+
+ r_drsuapi_bind.in.bind_guid = &drsuapi_bind_guid;
+ r_drsuapi_bind.in.bind_info = NULL;
+ r_drsuapi_bind.out.bind_handle = &drsuapi_bind_handle;
+
+ status = dcerpc_drsuapi_DsBind(drsuapi_pipe, tmp_ctx, &r_drsuapi_bind);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ r->out.error_string
+ = talloc_asprintf(r,
+ "dcerpc_drsuapi_DsBind failed - %s",
+ dcerpc_errstr(tmp_ctx, drsuapi_pipe->last_fault_code));
+ talloc_free(tmp_ctx);
+ return status;
+ } else {
+ r->out.error_string
+ = talloc_asprintf(r,
+ "dcerpc_drsuapi_DsBind failed - %s",
+ nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return status;
+ }
+ } else if (!W_ERROR_IS_OK(r_drsuapi_bind.out.result)) {
+ r->out.error_string
+ = talloc_asprintf(r,
+ "DsBind failed - %s",
+ win_errstr(r_drsuapi_bind.out.result));
+ talloc_free(tmp_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* Actually 'crack' the names */
+ ZERO_STRUCT(r_crack_names);
+ r_crack_names.in.bind_handle = &drsuapi_bind_handle;
+ r_crack_names.in.level = 1;
+ r_crack_names.in.req = talloc(r, union drsuapi_DsNameRequest);
+ if (!r_crack_names.in.req) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ r_crack_names.in.req->req1.codepage = 1252; /* western european */
+ r_crack_names.in.req->req1.language = 0x00000407; /* german */
+ r_crack_names.in.req->req1.count = 1;
+ r_crack_names.in.req->req1.names = names;
+ r_crack_names.in.req->req1.format_flags = DRSUAPI_DS_NAME_FLAG_NO_FLAGS;
+ r_crack_names.in.req->req1.format_offered = DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY;
+ r_crack_names.in.req->req1.format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779;
+ names[0].str = dom_sid_string(tmp_ctx, r->out.account_sid);
+ if (!names[0].str) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ r_crack_names.out.ctr = talloc(r, union drsuapi_DsNameCtr);
+ r_crack_names.out.level_out = talloc(r, int32_t);
+ if (!r_crack_names.out.ctr || !r_crack_names.out.level_out) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = dcerpc_drsuapi_DsCrackNames(drsuapi_pipe, tmp_ctx, &r_crack_names);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ r->out.error_string
+ = talloc_asprintf(r,
+ "dcerpc_drsuapi_DsCrackNames for [%s] failed - %s",
+ names[0].str,
+ dcerpc_errstr(tmp_ctx, drsuapi_pipe->last_fault_code));
+ talloc_free(tmp_ctx);
+ return status;
+ } else {
+ r->out.error_string
+ = talloc_asprintf(r,
+ "dcerpc_drsuapi_DsCrackNames for [%s] failed - %s",
+ names[0].str,
+ nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return status;
+ }
+ } else if (!W_ERROR_IS_OK(r_crack_names.out.result)) {
+ r->out.error_string
+ = talloc_asprintf(r,
+ "DsCrackNames failed - %s", win_errstr(r_crack_names.out.result));
+ talloc_free(tmp_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ } else if (*r_crack_names.out.level_out != 1
+ || !r_crack_names.out.ctr->ctr1
+ || r_crack_names.out.ctr->ctr1->count != 1) {
+ r->out.error_string = talloc_asprintf(r, "DsCrackNames failed");
+ talloc_free(tmp_ctx);
+ return NT_STATUS_INVALID_PARAMETER;
+ } else if (r_crack_names.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) {
+ r->out.error_string = talloc_asprintf(r, "DsCrackNames failed: %d", r_crack_names.out.ctr->ctr1->array[0].status);
+ talloc_free(tmp_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ } else if (r_crack_names.out.ctr->ctr1->array[0].result_name == NULL) {
+ r->out.error_string = talloc_asprintf(r, "DsCrackNames failed: no result name");
+ talloc_free(tmp_ctx);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* Store the DN of our machine account. */
+ account_dn_str = r_crack_names.out.ctr->ctr1->array[0].result_name;
+
+ /* Now we know the user's DN, open with LDAP, read and modify a few things */
+
+ remote_ldb_url = talloc_asprintf(tmp_ctx, "ldap://%s",
+ drsuapi_binding->target_hostname);
+ if (!remote_ldb_url) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ remote_ldb = ldb_wrap_connect(tmp_ctx, ctx->event_ctx, ctx->lp_ctx,
+ remote_ldb_url,
+ NULL, ctx->cred, 0, NULL);
+ if (!remote_ldb) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ account_dn = ldb_dn_new(tmp_ctx, remote_ldb, account_dn_str);
+ if (! ldb_dn_validate(account_dn)) {
+ r->out.error_string = talloc_asprintf(r, "Invalid account dn: %s",
+ account_dn_str);
+ talloc_free(tmp_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* search for the user's record */
+ ret = ldb_search(remote_ldb, tmp_ctx, &res,
+ account_dn, LDB_SCOPE_BASE, attrs, NULL);
+ if (ret != LDB_SUCCESS) {
+ r->out.error_string = talloc_asprintf(r, "ldb_search for %s failed - %s",
+ account_dn_str, ldb_errstring(remote_ldb));
+ talloc_free(tmp_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (res->count != 1) {
+ r->out.error_string = talloc_asprintf(r, "ldb_search for %s failed - found %d entries",
+ account_dn_str, res->count);
+ talloc_free(tmp_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* Prepare a new message, for the modify */
+ msg = ldb_msg_new(tmp_ctx);
+ if (!msg) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ msg->dn = res->msgs[0]->dn;
+
+ {
+ int i;
+ const char *service_principal_name[6];
+ const char *dns_host_name = strlower_talloc(tmp_ctx,
+ talloc_asprintf(tmp_ctx,
+ "%s.%s",
+ r->in.netbios_name,
+ realm));
+
+ if (!dns_host_name) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ service_principal_name[0] = talloc_asprintf(tmp_ctx, "host/%s", dns_host_name);
+ service_principal_name[1] = talloc_asprintf(tmp_ctx, "host/%s", strlower_talloc(tmp_ctx, r->in.netbios_name));
+ service_principal_name[2] = talloc_asprintf(tmp_ctx, "host/%s/%s", dns_host_name, realm);
+ service_principal_name[3] = talloc_asprintf(tmp_ctx, "host/%s/%s", strlower_talloc(tmp_ctx, r->in.netbios_name), realm);
+ service_principal_name[4] = talloc_asprintf(tmp_ctx, "host/%s/%s", dns_host_name, r->out.domain_name);
+ service_principal_name[5] = talloc_asprintf(tmp_ctx, "host/%s/%s", strlower_talloc(tmp_ctx, r->in.netbios_name), r->out.domain_name);
+
+ for (i=0; i < ARRAY_SIZE(service_principal_name); i++) {
+ if (!service_principal_name[i]) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ rtn = samdb_msg_add_string(remote_ldb, tmp_ctx, msg, "servicePrincipalName", service_principal_name[i]);
+ if (rtn == -1) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ rtn = samdb_msg_add_string(remote_ldb, tmp_ctx, msg, "dNSHostName", dns_host_name);
+ if (rtn == -1) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rtn = samdb_replace(remote_ldb, tmp_ctx, msg);
+ if (rtn != 0) {
+ r->out.error_string
+ = talloc_asprintf(r,
+ "Failed to replace entries on %s",
+ ldb_dn_get_linearized(msg->dn));
+ talloc_free(tmp_ctx);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ }
+
+ /* DsCrackNames to find out the DN of the domain. */
+ r_crack_names.in.req->req1.format_offered = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT;
+ r_crack_names.in.req->req1.format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779;
+ names[0].str = talloc_asprintf(tmp_ctx, "%s\\", r->out.domain_name);
+ if (!names[0].str) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = dcerpc_drsuapi_DsCrackNames(drsuapi_pipe, tmp_ctx, &r_crack_names);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ r->out.error_string
+ = talloc_asprintf(r,
+ "dcerpc_drsuapi_DsCrackNames for [%s] failed - %s",
+ r->in.domain_name,
+ dcerpc_errstr(tmp_ctx, drsuapi_pipe->last_fault_code));
+ talloc_free(tmp_ctx);
+ return status;
+ } else {
+ r->out.error_string
+ = talloc_asprintf(r,
+ "dcerpc_drsuapi_DsCrackNames for [%s] failed - %s",
+ r->in.domain_name,
+ nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return status;
+ }
+ } else if (!W_ERROR_IS_OK(r_crack_names.out.result)) {
+ r->out.error_string
+ = talloc_asprintf(r,
+ "DsCrackNames failed - %s", win_errstr(r_crack_names.out.result));
+ talloc_free(tmp_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ } else if (*r_crack_names.out.level_out != 1
+ || !r_crack_names.out.ctr->ctr1
+ || r_crack_names.out.ctr->ctr1->count != 1
+ || !r_crack_names.out.ctr->ctr1->array[0].result_name
+ || r_crack_names.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) {
+ r->out.error_string = talloc_asprintf(r, "DsCrackNames failed");
+ talloc_free(tmp_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* Store the account DN. */
+ r->out.account_dn_str = account_dn_str;
+ talloc_steal(r, account_dn_str);
+
+ /* Store the domain DN. */
+ r->out.domain_dn_str = r_crack_names.out.ctr->ctr1->array[0].result_name;
+ talloc_steal(r, r_crack_names.out.ctr->ctr1->array[0].result_name);
+
+ /* Store the KVNO of the account, critical for some kerberos
+ * operations */
+ r->out.kvno = ldb_msg_find_attr_as_uint(res->msgs[0], "msDS-KeyVersionNumber", 0);
+
+ /* Store the account GUID. */
+ r->out.account_guid = samdb_result_guid(res->msgs[0], "objectGUID");
+
+ if (r->in.acct_type == ACB_SVRTRUST) {
+ status = libnet_JoinSite(ctx, remote_ldb, r);
+ }
+ talloc_free(tmp_ctx);
+
+ return status;
+}
+
+/*
+ * do a domain join using DCERPC/SAMR calls
+ * - connect to the LSA pipe, to try and find out information about the domain
+ * - create a secondary connection to SAMR pipe
+ * - do a samr_Connect to get a policy handle
+ * - do a samr_LookupDomain to get the domain sid
+ * - do a samr_OpenDomain to get a domain handle
+ * - do a samr_CreateAccount to try and get a new account
+ *
+ * If that fails, do:
+ * - do a samr_LookupNames to get the users rid
+ * - do a samr_OpenUser to get a user handle
+ * - potentially delete and recreate the user
+ * - assert the account is of the right type with samrQueryUserInfo
+ *
+ * - call libnet_SetPassword_samr_handle to set the password,
+ * and pass a samr_UserInfo21 struct to set full_name and the account flags
+ *
+ * - do some ADS specific things when we join as Domain Controller,
+ * look at libnet_joinADSDomain() for the details
+ */
+NTSTATUS libnet_JoinDomain(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_JoinDomain *r)
+{
+ TALLOC_CTX *tmp_ctx;
+
+ NTSTATUS status, cu_status;
+
+ struct libnet_RpcConnect *connect_with_info;
+ struct dcerpc_pipe *samr_pipe;
+
+ struct samr_Connect sc;
+ struct policy_handle p_handle;
+ struct samr_OpenDomain od;
+ struct policy_handle d_handle;
+ struct samr_LookupNames ln;
+ struct samr_Ids rids, types;
+ struct samr_OpenUser ou;
+ struct samr_CreateUser2 cu;
+ struct policy_handle *u_handle = NULL;
+ struct samr_QueryUserInfo qui;
+ union samr_UserInfo *uinfo;
+ struct samr_UserInfo21 u_info21;
+ union libnet_SetPassword r2;
+ struct samr_GetUserPwInfo pwp;
+ struct samr_PwInfo info;
+ struct lsa_String samr_account_name;
+
+ uint32_t acct_flags, old_acct_flags;
+ uint32_t rid, access_granted;
+ int policy_min_pw_len = 0;
+
+ struct dom_sid *account_sid = NULL;
+ const char *password_str = NULL;
+
+ r->out.error_string = NULL;
+ r2.samr_handle.out.error_string = NULL;
+
+ tmp_ctx = talloc_named(mem_ctx, 0, "libnet_Join temp context");
+ if (!tmp_ctx) {
+ r->out.error_string = NULL;
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ u_handle = talloc(tmp_ctx, struct policy_handle);
+ if (!u_handle) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ connect_with_info = talloc(tmp_ctx, struct libnet_RpcConnect);
+ if (!connect_with_info) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* prepare connect to the SAMR pipe of PDC */
+ if (r->in.level == LIBNET_JOINDOMAIN_AUTOMATIC) {
+ connect_with_info->in.binding = NULL;
+ connect_with_info->in.name = r->in.domain_name;
+ } else {
+ connect_with_info->in.binding = r->in.binding;
+ connect_with_info->in.name = NULL;
+ }
+
+ /* This level makes a connection to the LSA pipe on the way,
+ * to get some useful bits of information about the domain */
+ connect_with_info->level = LIBNET_RPC_CONNECT_DC_INFO;
+ connect_with_info->in.dcerpc_iface = &ndr_table_samr;
+
+ /*
+ establish the SAMR connection
+ */
+ status = libnet_RpcConnect(ctx, tmp_ctx, connect_with_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (r->in.binding) {
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "Connection to SAMR pipe of DC %s failed: %s",
+ r->in.binding, connect_with_info->out.error_string);
+ } else {
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "Connection to SAMR pipe of PDC for %s failed: %s",
+ r->in.domain_name, connect_with_info->out.error_string);
+ }
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ samr_pipe = connect_with_info->out.dcerpc_pipe;
+
+ status = dcerpc_pipe_auth(tmp_ctx, &samr_pipe,
+ connect_with_info->out.dcerpc_pipe->binding,
+ &ndr_table_samr, ctx->cred, ctx->lp_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "SAMR bind failed: %s",
+ nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ /* prepare samr_Connect */
+ ZERO_STRUCT(p_handle);
+ sc.in.system_name = NULL;
+ sc.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ sc.out.connect_handle = &p_handle;
+
+ /* 2. do a samr_Connect to get a policy handle */
+ status = dcerpc_samr_Connect(samr_pipe, tmp_ctx, &sc);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "samr_Connect failed: %s",
+ nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ /* If this is a connection on ncacn_ip_tcp to Win2k3 SP1, we don't get back this useful info */
+ if (!connect_with_info->out.domain_name) {
+ if (r->in.level == LIBNET_JOINDOMAIN_AUTOMATIC) {
+ connect_with_info->out.domain_name = talloc_strdup(tmp_ctx, r->in.domain_name);
+ } else {
+ /* Bugger, we just lost our way to automaticly find the domain name */
+ connect_with_info->out.domain_name = talloc_strdup(tmp_ctx, lp_workgroup(ctx->lp_ctx));
+ connect_with_info->out.realm = talloc_strdup(tmp_ctx, lp_realm(ctx->lp_ctx));
+ }
+ }
+
+ /* Perhaps we didn't get a SID above, because we are against ncacn_ip_tcp */
+ if (!connect_with_info->out.domain_sid) {
+ struct lsa_String name;
+ struct samr_LookupDomain l;
+ struct dom_sid2 *sid = NULL;
+ name.string = connect_with_info->out.domain_name;
+ l.in.connect_handle = &p_handle;
+ l.in.domain_name = &name;
+ l.out.sid = &sid;
+
+ status = dcerpc_samr_LookupDomain(samr_pipe, tmp_ctx, &l);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "SAMR LookupDomain failed: %s",
+ nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return status;
+ }
+ connect_with_info->out.domain_sid = *l.out.sid;
+ }
+
+ /* prepare samr_OpenDomain */
+ ZERO_STRUCT(d_handle);
+ od.in.connect_handle = &p_handle;
+ od.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ od.in.sid = connect_with_info->out.domain_sid;
+ od.out.domain_handle = &d_handle;
+
+ /* do a samr_OpenDomain to get a domain handle */
+ status = dcerpc_samr_OpenDomain(samr_pipe, tmp_ctx, &od);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "samr_OpenDomain for [%s] failed: %s",
+ dom_sid_string(tmp_ctx, connect_with_info->out.domain_sid),
+ nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ /* prepare samr_CreateUser2 */
+ ZERO_STRUCTP(u_handle);
+ cu.in.domain_handle = &d_handle;
+ cu.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ samr_account_name.string = r->in.account_name;
+ cu.in.account_name = &samr_account_name;
+ cu.in.acct_flags = r->in.acct_type;
+ cu.out.user_handle = u_handle;
+ cu.out.rid = &rid;
+ cu.out.access_granted = &access_granted;
+
+ /* do a samr_CreateUser2 to get an account handle, or an error */
+ cu_status = dcerpc_samr_CreateUser2(samr_pipe, tmp_ctx, &cu);
+ status = cu_status;
+ if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
+ /* prepare samr_LookupNames */
+ ln.in.domain_handle = &d_handle;
+ ln.in.num_names = 1;
+ ln.in.names = talloc_array(tmp_ctx, struct lsa_String, 1);
+ ln.out.rids = &rids;
+ ln.out.types = &types;
+ if (!ln.in.names) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ ln.in.names[0].string = r->in.account_name;
+
+ /* 5. do a samr_LookupNames to get the users rid */
+ status = dcerpc_samr_LookupNames(samr_pipe, tmp_ctx, &ln);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "samr_LookupNames for [%s] failed: %s",
+ r->in.account_name,
+ nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ /* check if we got one RID for the user */
+ if (ln.out.rids->count != 1) {
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "samr_LookupNames for [%s] returns %d RIDs",
+ r->in.account_name, ln.out.rids->count);
+ talloc_free(tmp_ctx);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* prepare samr_OpenUser */
+ ZERO_STRUCTP(u_handle);
+ ou.in.domain_handle = &d_handle;
+ ou.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ ou.in.rid = ln.out.rids->ids[0];
+ rid = ou.in.rid;
+ ou.out.user_handle = u_handle;
+
+ /* 6. do a samr_OpenUser to get a user handle */
+ status = dcerpc_samr_OpenUser(samr_pipe, tmp_ctx, &ou);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "samr_OpenUser for [%s] failed: %s",
+ r->in.account_name,
+ nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ if (r->in.recreate_account) {
+ struct samr_DeleteUser d;
+ d.in.user_handle = u_handle;
+ d.out.user_handle = u_handle;
+ status = dcerpc_samr_DeleteUser(samr_pipe, mem_ctx, &d);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "samr_DeleteUser (for recreate) of [%s] failed: %s",
+ r->in.account_name,
+ nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ /* We want to recreate, so delete and another samr_CreateUser2 */
+
+ /* &cu filled in above */
+ status = dcerpc_samr_CreateUser2(samr_pipe, tmp_ctx, &cu);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "samr_CreateUser2 (recreate) for [%s] failed: %s",
+ r->in.account_name, nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return status;
+ }
+ }
+ } else if (!NT_STATUS_IS_OK(status)) {
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "samr_CreateUser2 for [%s] failed: %s",
+ r->in.account_name, nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ /* prepare samr_QueryUserInfo (get flags) */
+ qui.in.user_handle = u_handle;
+ qui.in.level = 16;
+ qui.out.info = &uinfo;
+
+ status = dcerpc_samr_QueryUserInfo(samr_pipe, tmp_ctx, &qui);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "samr_QueryUserInfo for [%s] failed: %s",
+ r->in.account_name,
+ nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ if (!uinfo) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ r->out.error_string
+ = talloc_asprintf(mem_ctx,
+ "samr_QueryUserInfo failed to return qui.out.info for [%s]: %s",
+ r->in.account_name, nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ old_acct_flags = (uinfo->info16.acct_flags & (ACB_WSTRUST | ACB_SVRTRUST | ACB_DOMTRUST));
+ /* Possibly bail if the account is of the wrong type */
+ if (old_acct_flags
+ != r->in.acct_type) {
+ const char *old_account_type, *new_account_type;
+ switch (old_acct_flags) {
+ case ACB_WSTRUST:
+ old_account_type = "domain member (member)";
+ break;
+ case ACB_SVRTRUST:
+ old_account_type = "domain controller (bdc)";
+ break;
+ case ACB_DOMTRUST:
+ old_account_type = "trusted domain";
+ break;
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ switch (r->in.acct_type) {
+ case ACB_WSTRUST:
+ new_account_type = "domain member (member)";
+ break;
+ case ACB_SVRTRUST:
+ new_account_type = "domain controller (bdc)";
+ break;
+ case ACB_DOMTRUST:
+ new_account_type = "trusted domain";
+ break;
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!NT_STATUS_EQUAL(cu_status, NT_STATUS_USER_EXISTS)) {
+ /* We created a new user, but they didn't come out the right type?!? */
+ r->out.error_string
+ = talloc_asprintf(mem_ctx,
+ "We asked to create a new machine account (%s) of type %s, "
+ "but we got an account of type %s. This is unexpected. "
+ "Perhaps delete the account and try again.",
+ r->in.account_name, new_account_type, old_account_type);
+ talloc_free(tmp_ctx);
+ return NT_STATUS_INVALID_PARAMETER;
+ } else {
+ /* The account is of the wrong type, so bail */
+
+ /* TODO: We should allow a --force option to override, and redo this from the top setting r.in.recreate_account */
+ r->out.error_string
+ = talloc_asprintf(mem_ctx,
+ "The machine account (%s) already exists in the domain %s, "
+ "but is a %s. You asked to join as a %s. Please delete "
+ "the account and try again.",
+ r->in.account_name, connect_with_info->out.domain_name, old_account_type, new_account_type);
+ talloc_free(tmp_ctx);
+ return NT_STATUS_USER_EXISTS;
+ }
+ } else {
+ acct_flags = uinfo->info16.acct_flags;
+ }
+
+ acct_flags = (acct_flags & ~(ACB_DISABLED|ACB_PWNOTREQ));
+
+ /* Find out what password policy this user has */
+ pwp.in.user_handle = u_handle;
+ pwp.out.info = &info;
+
+ status = dcerpc_samr_GetUserPwInfo(samr_pipe, tmp_ctx, &pwp);
+ if (NT_STATUS_IS_OK(status)) {
+ policy_min_pw_len = pwp.out.info->min_password_length;
+ }
+
+ /* Grab a password of that minimum length */
+
+ password_str = generate_random_str(tmp_ctx, MAX(8, policy_min_pw_len));
+
+ /* set full_name and reset flags */
+ ZERO_STRUCT(u_info21);
+ u_info21.full_name.string = r->in.account_name;
+ u_info21.acct_flags = acct_flags;
+ u_info21.fields_present = SAMR_FIELD_FULL_NAME | SAMR_FIELD_ACCT_FLAGS;
+
+ r2.samr_handle.level = LIBNET_SET_PASSWORD_SAMR_HANDLE;
+ r2.samr_handle.in.account_name = r->in.account_name;
+ r2.samr_handle.in.newpassword = password_str;
+ r2.samr_handle.in.user_handle = u_handle;
+ r2.samr_handle.in.dcerpc_pipe = samr_pipe;
+ r2.samr_handle.in.info21 = &u_info21;
+
+ status = libnet_SetPassword(ctx, tmp_ctx, &r2);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->out.error_string = talloc_steal(mem_ctx, r2.samr_handle.out.error_string);
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ account_sid = dom_sid_add_rid(mem_ctx, connect_with_info->out.domain_sid, rid);
+ if (!account_sid) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* Finish out by pushing various bits of status data out for the caller to use */
+ r->out.join_password = password_str;
+ talloc_steal(mem_ctx, r->out.join_password);
+
+ r->out.domain_sid = connect_with_info->out.domain_sid;
+ talloc_steal(mem_ctx, r->out.domain_sid);
+
+ r->out.account_sid = account_sid;
+ talloc_steal(mem_ctx, r->out.account_sid);
+
+ r->out.domain_name = connect_with_info->out.domain_name;
+ talloc_steal(mem_ctx, r->out.domain_name);
+ r->out.realm = connect_with_info->out.realm;
+ talloc_steal(mem_ctx, r->out.realm);
+ r->out.samr_pipe = samr_pipe;
+ talloc_steal(mem_ctx, samr_pipe);
+ r->out.samr_binding = samr_pipe->binding;
+ talloc_steal(mem_ctx, r->out.samr_binding);
+ r->out.user_handle = u_handle;
+ talloc_steal(mem_ctx, u_handle);
+ r->out.error_string = r2.samr_handle.out.error_string;
+ talloc_steal(mem_ctx, r2.samr_handle.out.error_string);
+ r->out.kvno = 0;
+ r->out.server_dn_str = NULL;
+ talloc_free(tmp_ctx);
+
+ /* Now, if it was AD, then we want to start looking changing a
+ * few more things. Otherwise, we are done. */
+ if (r->out.realm) {
+ status = libnet_JoinADSDomain(ctx, r);
+ return status;
+ }
+
+ return status;
+}
+
+NTSTATUS libnet_set_join_secrets(struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_set_join_secrets *r)
+{
+ TALLOC_CTX *tmp_mem;
+ int ret, rtn;
+ struct ldb_context *ldb;
+ struct ldb_dn *base_dn;
+ struct ldb_message **msgs, *msg;
+ const char *sct;
+ const char * const attrs[] = {
+ "whenChanged",
+ "secret",
+ "priorSecret",
+ "priorChanged",
+ "krb5Keytab",
+ "privateKeytab",
+ NULL
+ };
+
+ tmp_mem = talloc_new(mem_ctx);
+ if (!tmp_mem) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* Open the secrets database */
+ ldb = secrets_db_connect(tmp_mem, ctx->event_ctx, ctx->lp_ctx);
+ if (!ldb) {
+ r->out.error_string
+ = talloc_asprintf(mem_ctx,
+ "Could not open secrets database");
+ talloc_free(tmp_mem);
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ }
+
+ /*
+ * now prepare the record for secrets.ldb
+ */
+ sct = talloc_asprintf(tmp_mem, "%d", r->in.join_type);
+ if (!sct) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_mem);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ msg = ldb_msg_new(tmp_mem);
+ if (!msg) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_mem);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ base_dn = ldb_dn_new(tmp_mem, ldb, "cn=Primary Domains");
+ if (!base_dn) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_mem);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ msg->dn = ldb_dn_copy(tmp_mem, base_dn);
+ if ( ! ldb_dn_add_child_fmt(msg->dn, "flatname=%s", r->in.domain_name)) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_mem);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rtn = samdb_msg_add_string(ldb, tmp_mem, msg, "flatname", r->in.domain_name);
+ if (rtn == -1) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_mem);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (r->in.realm) {
+ rtn = samdb_msg_add_string(ldb, tmp_mem, msg, "realm", r->in.realm);
+ if (rtn == -1) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_mem);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rtn = samdb_msg_add_string(ldb, tmp_mem, msg, "objectClass", "primaryDomain");
+ if (rtn == -1) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_mem);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ rtn = samdb_msg_add_string(ldb, tmp_mem, msg, "objectClass", "primaryDomain");
+ if (rtn == -1) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_mem);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rtn = samdb_msg_add_string(ldb, tmp_mem, msg, "secret", r->in.join_password);
+ if (rtn == -1) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_mem);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rtn = samdb_msg_add_string(ldb, tmp_mem, msg, "samAccountName", r->in.account_name);
+ if (rtn == -1) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_mem);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rtn = samdb_msg_add_string(ldb, tmp_mem, msg, "secureChannelType", sct);
+ if (rtn == -1) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_mem);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (r->in.kvno) {
+ rtn = samdb_msg_add_uint(ldb, tmp_mem, msg, "msDS-KeyVersionNumber",
+ r->in.kvno);
+ if (rtn == -1) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_mem);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ if (r->in.domain_sid) {
+ rtn = samdb_msg_add_dom_sid(ldb, tmp_mem, msg, "objectSid",
+ r->in.domain_sid);
+ if (rtn == -1) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_mem);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ /*
+ * search for the secret record
+ * - remove the records we find
+ * - and fetch the old secret and store it under priorSecret
+ */
+ ret = gendb_search(ldb,
+ tmp_mem, base_dn,
+ &msgs, attrs,
+ "(|" SECRETS_PRIMARY_DOMAIN_FILTER "(realm=%s))",
+ r->in.domain_name, r->in.realm);
+ if (ret == 0) {
+ rtn = samdb_msg_set_string(ldb, tmp_mem, msg, "secretsKeytab", "secrets.keytab");
+ if (rtn == -1) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_mem);
+ return NT_STATUS_NO_MEMORY;
+ }
+ } else if (ret == -1) {
+ r->out.error_string
+ = talloc_asprintf(mem_ctx,
+ "Search for domain: %s and realm: %s failed: %s",
+ r->in.domain_name, r->in.realm, ldb_errstring(ldb));
+ talloc_free(tmp_mem);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ } else {
+ const struct ldb_val *private_keytab;
+ const struct ldb_val *krb5_main_keytab;
+ const struct ldb_val *prior_secret;
+ const struct ldb_val *prior_modified_time;
+ int i;
+
+ for (i = 0; i < ret; i++) {
+ ldb_delete(ldb, msgs[i]->dn);
+ }
+
+ prior_secret = ldb_msg_find_ldb_val(msgs[0], "secret");
+ if (prior_secret) {
+ rtn = samdb_msg_set_value(ldb, tmp_mem, msg, "priorSecret", prior_secret);
+ if (rtn == -1) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_mem);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+ rtn = samdb_msg_set_string(ldb, tmp_mem, msg, "secret", r->in.join_password);
+ if (rtn == -1) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_mem);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ prior_modified_time = ldb_msg_find_ldb_val(msgs[0],
+ "whenChanged");
+ if (prior_modified_time) {
+ rtn = samdb_msg_set_value(ldb, tmp_mem, msg, "priorWhenChanged",
+ prior_modified_time);
+ if (rtn == -1) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_mem);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ rtn = samdb_msg_set_string(ldb, tmp_mem, msg, "samAccountName", r->in.account_name);
+ if (rtn == -1) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_mem);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rtn = samdb_msg_set_string(ldb, tmp_mem, msg, "secureChannelType", sct);
+ if (rtn == -1) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_mem);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* We will want to keep the keytab names */
+ private_keytab = ldb_msg_find_ldb_val(msgs[0], "privateKeytab");
+ if (private_keytab) {
+ rtn = samdb_msg_set_value(ldb, tmp_mem, msg, "privateKeytab", private_keytab);
+ if (rtn == -1) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_mem);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+ krb5_main_keytab = ldb_msg_find_ldb_val(msgs[0], "krb5Keytab");
+ if (krb5_main_keytab) {
+ rtn = samdb_msg_set_value(ldb, tmp_mem, msg,
+ "krb5Keytab", krb5_main_keytab);
+ if (rtn == -1) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_mem);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+ }
+
+ /* create the secret */
+ ret = ldb_add(ldb, msg);
+ if (ret != 0) {
+ r->out.error_string = talloc_asprintf(mem_ctx, "Failed to create secret record %s",
+ ldb_dn_get_linearized(msg->dn));
+ talloc_free(tmp_mem);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS libnet_Join_primary_domain(struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_Join *r)
+{
+ NTSTATUS status;
+ TALLOC_CTX *tmp_mem;
+ struct libnet_JoinDomain *r2;
+ struct libnet_set_join_secrets *r3;
+ uint32_t acct_type = 0;
+ const char *account_name;
+ const char *netbios_name;
+
+ r->out.error_string = NULL;
+
+ tmp_mem = talloc_new(mem_ctx);
+ if (!tmp_mem) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ r2 = talloc(tmp_mem, struct libnet_JoinDomain);
+ if (!r2) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_mem);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (r->in.join_type == SEC_CHAN_BDC) {
+ acct_type = ACB_SVRTRUST;
+ } else if (r->in.join_type == SEC_CHAN_WKSTA) {
+ acct_type = ACB_WSTRUST;
+ } else {
+ r->out.error_string = NULL;
+ talloc_free(tmp_mem);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (r->in.netbios_name != NULL) {
+ netbios_name = r->in.netbios_name;
+ } else {
+ netbios_name = talloc_reference(tmp_mem, lp_netbios_name(ctx->lp_ctx));
+ if (!netbios_name) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_mem);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ account_name = talloc_asprintf(tmp_mem, "%s$", netbios_name);
+ if (!account_name) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_mem);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /*
+ * join the domain
+ */
+ ZERO_STRUCTP(r2);
+ r2->in.domain_name = r->in.domain_name;
+ r2->in.account_name = account_name;
+ r2->in.netbios_name = netbios_name;
+ r2->in.level = LIBNET_JOINDOMAIN_AUTOMATIC;
+ r2->in.acct_type = acct_type;
+ r2->in.recreate_account = false;
+ status = libnet_JoinDomain(ctx, r2, r2);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->out.error_string = talloc_steal(mem_ctx, r2->out.error_string);
+ talloc_free(tmp_mem);
+ return status;
+ }
+
+ r3 = talloc(tmp_mem, struct libnet_set_join_secrets);
+ if (!r3) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_mem);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ZERO_STRUCTP(r3);
+ r3->in.domain_name = r2->out.domain_name;
+ r3->in.realm = r2->out.realm;
+ r3->in.account_name = account_name;
+ r3->in.netbios_name = netbios_name;
+ r3->in.join_type = r->in.join_type;
+ r3->in.join_password = r2->out.join_password;
+ r3->in.kvno = r2->out.kvno;
+ r3->in.domain_sid = r2->out.domain_sid;
+
+ status = libnet_set_join_secrets(ctx, r3, r3);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->out.error_string = talloc_steal(mem_ctx, r3->out.error_string);
+ talloc_free(tmp_mem);
+ return status;
+ }
+
+ /* move all out parameter to the callers TALLOC_CTX */
+ r->out.error_string = NULL;
+ r->out.join_password = r2->out.join_password;
+ talloc_steal(mem_ctx, r2->out.join_password);
+ r->out.domain_sid = r2->out.domain_sid;
+ talloc_steal(mem_ctx, r2->out.domain_sid);
+ r->out.domain_name = r2->out.domain_name;
+ talloc_steal(mem_ctx, r2->out.domain_name);
+ talloc_free(tmp_mem);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS libnet_Join(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_Join *r)
+{
+ switch (r->in.join_type) {
+ case SEC_CHAN_WKSTA:
+ return libnet_Join_primary_domain(ctx, mem_ctx, r);
+ case SEC_CHAN_BDC:
+ return libnet_Join_primary_domain(ctx, mem_ctx, r);
+ case SEC_CHAN_DOMAIN:
+ case SEC_CHAN_DNS_DOMAIN:
+ case SEC_CHAN_NULL:
+ break;
+ }
+
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "Invalid join type specified (%08X) attempting to join domain %s",
+ r->in.join_type, r->in.domain_name);
+ return NT_STATUS_INVALID_PARAMETER;
+}
diff --git a/source4/libnet/libnet_join.h b/source4/libnet/libnet_join.h
new file mode 100644
index 0000000000..79884130d8
--- /dev/null
+++ b/source4/libnet/libnet_join.h
@@ -0,0 +1,100 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Stefan Metzmacher 2004
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __LIBNET_JOIN_H__
+#define __LIBNET_JOIN_H__
+
+#include "librpc/gen_ndr/netlogon.h"
+
+enum libnet_Join_level {
+ LIBNET_JOIN_AUTOMATIC,
+ LIBNET_JOIN_SPECIFIED,
+};
+
+enum libnet_JoinDomain_level {
+ LIBNET_JOINDOMAIN_AUTOMATIC,
+ LIBNET_JOINDOMAIN_SPECIFIED,
+};
+
+struct libnet_JoinDomain {
+ struct {
+ const char *domain_name;
+ const char *account_name;
+ const char *netbios_name;
+ const char *binding;
+ enum libnet_JoinDomain_level level;
+ uint32_t acct_type;
+ bool recreate_account;
+ } in;
+
+ struct {
+ const char *error_string;
+ const char *join_password;
+ struct dom_sid *domain_sid;
+ const char *domain_name;
+ const char *realm;
+ const char *domain_dn_str;
+ const char *account_dn_str;
+ const char *server_dn_str;
+ uint32_t kvno; /* msDS-KeyVersionNumber */
+ struct dcerpc_pipe *samr_pipe;
+ struct dcerpc_binding *samr_binding;
+ struct policy_handle *user_handle;
+ struct dom_sid *account_sid;
+ struct GUID account_guid;
+ } out;
+};
+
+struct libnet_Join {
+ struct {
+ const char *domain_name;
+ const char *netbios_name;
+ enum netr_SchannelType join_type;
+ enum libnet_Join_level level;
+ } in;
+
+ struct {
+ const char *error_string;
+ const char *join_password;
+ struct dom_sid *domain_sid;
+ const char *domain_name;
+ } out;
+};
+
+struct libnet_set_join_secrets {
+ struct {
+ const char *domain_name;
+ const char *realm;
+ const char *netbios_name;
+ const char *account_name;
+ enum netr_SchannelType join_type;
+ const char *join_password;
+ int kvno;
+ struct dom_sid *domain_sid;
+ } in;
+
+ struct {
+ const char *error_string;
+ } out;
+};
+
+
+#endif /* __LIBNET_JOIN_H__ */
diff --git a/source4/libnet/libnet_lookup.c b/source4/libnet/libnet_lookup.c
new file mode 100644
index 0000000000..fc307823b8
--- /dev/null
+++ b/source4/libnet/libnet_lookup.c
@@ -0,0 +1,417 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ a composite function for name resolving
+*/
+
+#include "includes.h"
+#include "lib/events/events.h"
+#include "libnet/libnet.h"
+#include "libcli/composite/composite.h"
+#include "auth/credentials/credentials.h"
+#include "lib/messaging/messaging.h"
+#include "lib/messaging/irpc.h"
+#include "libcli/resolve/resolve.h"
+#include "libcli/finddcs.h"
+#include "libcli/security/security.h"
+#include "librpc/gen_ndr/lsa.h"
+#include "librpc/gen_ndr/ndr_lsa_c.h"
+
+#include "param/param.h"
+
+struct lookup_state {
+ struct nbt_name hostname;
+ const char *address;
+};
+
+
+static void continue_name_resolved(struct composite_context *ctx);
+
+
+/**
+ * Sends asynchronous Lookup request
+ *
+ * @param io arguments and result of the call
+ */
+
+struct composite_context *libnet_Lookup_send(struct libnet_context *ctx,
+ struct libnet_Lookup *io)
+{
+ struct composite_context *c;
+ struct lookup_state *s;
+ struct composite_context *cresolve_req;
+ struct resolve_context *resolve_ctx;
+
+ /* allocate context and state structures */
+ c = composite_create(ctx, ctx->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct lookup_state);
+ if (composite_nomem(s, c)) return c;
+
+ c->private_data = s;
+
+ if (io == NULL || io->in.hostname == NULL) {
+ composite_error(c, NT_STATUS_INVALID_PARAMETER);
+ return c;
+ }
+
+ /* parameters */
+ s->hostname.name = talloc_strdup(s, io->in.hostname);
+ if (composite_nomem(s->hostname.name, c)) return c;
+
+ s->hostname.type = io->in.type;
+ s->hostname.scope = NULL;
+
+ /* name resolution methods */
+ if (io->in.resolve_ctx) {
+ resolve_ctx = io->in.resolve_ctx;
+ } else {
+ resolve_ctx = ctx->resolve_ctx;
+ }
+
+ /* send resolve request */
+ cresolve_req = resolve_name_send(resolve_ctx, &s->hostname, c->event_ctx);
+ if (composite_nomem(cresolve_req, c)) return c;
+
+ composite_continue(c, cresolve_req, continue_name_resolved, c);
+ return c;
+}
+
+
+static void continue_name_resolved(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct lookup_state *s;
+
+ c = talloc_get_type(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct lookup_state);
+
+ c->status = resolve_name_recv(ctx, s, &s->address);
+
+ composite_done(c);
+}
+
+
+/**
+ * Waits for and receives results of asynchronous Lookup call
+ *
+ * @param c composite context returned by asynchronous Lookup call
+ * @param mem_ctx memory context of the call
+ * @param io pointer to results (and arguments) of the call
+ * @return nt status code of execution
+ */
+
+NTSTATUS libnet_Lookup_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
+ struct libnet_Lookup *io)
+{
+ NTSTATUS status;
+ struct lookup_state *s;
+
+ status = composite_wait(c);
+ if (NT_STATUS_IS_OK(status)) {
+ s = talloc_get_type(c->private_data, struct lookup_state);
+
+ io->out.address = (const char **)str_list_make(mem_ctx, s->address, NULL);
+ NT_STATUS_HAVE_NO_MEMORY(io->out.address);
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+
+/**
+ * Synchronous version of Lookup call
+ *
+ * @param mem_ctx memory context for the call
+ * @param io arguments and results of the call
+ * @return nt status code of execution
+ */
+
+NTSTATUS libnet_Lookup(struct libnet_context *ctx, TALLOC_CTX *mem_ctx,
+ struct libnet_Lookup *io)
+{
+ struct composite_context *c = libnet_Lookup_send(ctx, io);
+ return libnet_Lookup_recv(c, mem_ctx, io);
+}
+
+
+/*
+ * Shortcut functions to find common types of name
+ * (and skip nbt name type argument)
+ */
+
+
+/**
+ * Sends asynchronous LookupHost request
+ */
+struct composite_context* libnet_LookupHost_send(struct libnet_context *ctx,
+ struct libnet_Lookup *io)
+{
+ io->in.type = NBT_NAME_SERVER;
+ return libnet_Lookup_send(ctx, io);
+}
+
+
+
+/**
+ * Synchronous version of LookupHost call
+ */
+NTSTATUS libnet_LookupHost(struct libnet_context *ctx, TALLOC_CTX *mem_ctx,
+ struct libnet_Lookup *io)
+{
+ struct composite_context *c = libnet_LookupHost_send(ctx, io);
+ return libnet_Lookup_recv(c, mem_ctx, io);
+}
+
+
+/**
+ * Sends asynchronous LookupDCs request
+ */
+struct composite_context* libnet_LookupDCs_send(struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_LookupDCs *io)
+{
+ struct composite_context *c;
+ struct messaging_context *msg_ctx =
+ messaging_client_init(mem_ctx, lp_messaging_path(mem_ctx, ctx->lp_ctx),
+ lp_iconv_convenience(ctx->lp_ctx), ctx->event_ctx);
+
+ c = finddcs_send(mem_ctx, lp_netbios_name(ctx->lp_ctx), lp_nbt_port(ctx->lp_ctx),
+ io->in.domain_name, io->in.name_type,
+ NULL, lp_iconv_convenience(ctx->lp_ctx),
+ ctx->resolve_ctx, ctx->event_ctx, msg_ctx);
+ return c;
+}
+
+/**
+ * Waits for and receives results of asynchronous Lookup call
+ *
+ * @param c composite context returned by asynchronous Lookup call
+ * @param mem_ctx memory context of the call
+ * @param io pointer to results (and arguments) of the call
+ * @return nt status code of execution
+ */
+
+NTSTATUS libnet_LookupDCs_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
+ struct libnet_LookupDCs *io)
+{
+ NTSTATUS status;
+ status = finddcs_recv(c, mem_ctx, &io->out.num_dcs, &io->out.dcs);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ return status;
+}
+
+
+/**
+ * Synchronous version of LookupDCs
+ */
+NTSTATUS libnet_LookupDCs(struct libnet_context *ctx, TALLOC_CTX *mem_ctx,
+ struct libnet_LookupDCs *io)
+{
+ struct composite_context *c = libnet_LookupDCs_send(ctx, mem_ctx, io);
+ return libnet_LookupDCs_recv(c, mem_ctx, io);
+}
+
+
+struct lookup_name_state {
+ struct libnet_context *ctx;
+ const char *name;
+ uint32_t count;
+ struct libnet_DomainOpen domopen;
+ struct lsa_LookupNames lookup;
+ struct lsa_TransSidArray sids;
+ struct lsa_String *names;
+
+ /* information about the progress */
+ void (*monitor_fn)(struct monitor_msg *);
+};
+
+
+static bool prepare_lookup_params(struct libnet_context *ctx,
+ struct composite_context *c,
+ struct lookup_name_state *s);
+static void continue_lookup_name(struct composite_context *ctx);
+static void continue_name_found(struct rpc_request *req);
+
+
+struct composite_context* libnet_LookupName_send(struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_LookupName *io,
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *c;
+ struct lookup_name_state *s;
+ struct rpc_request *lookup_req;
+ bool prereq_met = false;
+
+ c = composite_create(mem_ctx, ctx->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct lookup_name_state);
+ if (composite_nomem(s, c)) return c;
+
+ c->private_data = s;
+
+ s->name = talloc_strdup(c, io->in.name);
+ s->monitor_fn = monitor;
+ s->ctx = ctx;
+
+ prereq_met = lsa_domain_opened(ctx, io->in.domain_name, &c, &s->domopen,
+ continue_lookup_name, monitor);
+ if (!prereq_met) return c;
+
+ if (!prepare_lookup_params(ctx, c, s)) return c;
+
+ lookup_req = dcerpc_lsa_LookupNames_send(ctx->lsa.pipe, c, &s->lookup);
+ if (composite_nomem(lookup_req, c)) return c;
+
+ composite_continue_rpc(c, lookup_req, continue_name_found, c);
+ return c;
+}
+
+
+static bool prepare_lookup_params(struct libnet_context *ctx,
+ struct composite_context *c,
+ struct lookup_name_state *s)
+{
+ const int single_name = 1;
+
+ s->sids.count = 0;
+ s->sids.sids = NULL;
+
+ s->names = talloc_array(ctx, struct lsa_String, single_name);
+ if (composite_nomem(s->names, c)) return false;
+ s->names[0].string = s->name;
+
+ s->lookup.in.handle = &ctx->lsa.handle;
+ s->lookup.in.num_names = single_name;
+ s->lookup.in.names = s->names;
+ s->lookup.in.sids = &s->sids;
+ s->lookup.in.level = 1;
+ s->lookup.in.count = &s->count;
+ s->lookup.out.count = &s->count;
+ s->lookup.out.sids = &s->sids;
+ s->lookup.out.domains = talloc_zero(ctx, struct lsa_RefDomainList *);
+ if (composite_nomem(s->lookup.out.domains, c)) return false;
+
+ return true;
+}
+
+
+static void continue_lookup_name(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct lookup_name_state *s;
+ struct rpc_request *lookup_req;
+
+ c = talloc_get_type(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct lookup_name_state);
+
+ c->status = libnet_DomainOpen_recv(ctx, s->ctx, c, &s->domopen);
+ if (!composite_is_ok(c)) return;
+
+ if (!prepare_lookup_params(s->ctx, c, s)) return;
+
+ lookup_req = dcerpc_lsa_LookupNames_send(s->ctx->lsa.pipe, c, &s->lookup);
+ if (composite_nomem(lookup_req, c)) return;
+
+ composite_continue_rpc(c, lookup_req, continue_name_found, c);
+}
+
+
+static void continue_name_found(struct rpc_request *req)
+{
+ struct composite_context *c;
+ struct lookup_name_state *s;
+
+ c = talloc_get_type(req->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct lookup_name_state);
+
+ c->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(c)) return;
+
+ c->status = s->lookup.out.result;
+ if (!composite_is_ok(c)) return;
+
+ composite_done(c);
+}
+
+
+NTSTATUS libnet_LookupName_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
+ struct libnet_LookupName *io)
+{
+ NTSTATUS status;
+ struct lookup_name_state *s;
+
+ status = composite_wait(c);
+
+ if (NT_STATUS_IS_OK(status)) {
+ s = talloc_get_type(c->private_data, struct lookup_name_state);
+
+ io->out.rid = 0;
+ io->out.sid = NULL;
+ io->out.sidstr = NULL;
+
+ if (*s->lookup.out.count > 0) {
+ struct lsa_RefDomainList *domains = *s->lookup.out.domains;
+ struct lsa_TransSidArray *sids = s->lookup.out.sids;
+
+ if (domains == NULL || sids == NULL) {
+ status = NT_STATUS_UNSUCCESSFUL;
+ io->out.error_string = talloc_asprintf(mem_ctx, "Error: %s", nt_errstr(status));
+ goto done;
+ }
+
+ if (sids->count > 0) {
+ io->out.rid = sids->sids[0].rid;
+ io->out.sid_type = sids->sids[0].sid_type;
+ if (domains->count > 0) {
+ io->out.sid = dom_sid_add_rid(mem_ctx, domains->domains[0].sid, io->out.rid);
+ NT_STATUS_HAVE_NO_MEMORY(io->out.sid);
+ io->out.sidstr = dom_sid_string(mem_ctx, io->out.sid);
+ NT_STATUS_HAVE_NO_MEMORY(io->out.sidstr);
+ }
+ }
+ }
+
+ io->out.error_string = talloc_strdup(mem_ctx, "Success");
+
+ } else if (!NT_STATUS_IS_OK(status)) {
+ io->out.error_string = talloc_asprintf(mem_ctx, "Error: %s", nt_errstr(status));
+ }
+
+done:
+ talloc_free(c);
+ return status;
+}
+
+
+NTSTATUS libnet_LookupName(struct libnet_context *ctx, TALLOC_CTX *mem_ctx,
+ struct libnet_LookupName *io)
+{
+ struct composite_context *c;
+
+ c = libnet_LookupName_send(ctx, mem_ctx, io, NULL);
+ return libnet_LookupName_recv(c, mem_ctx, io);
+}
diff --git a/source4/libnet/libnet_lookup.h b/source4/libnet/libnet_lookup.h
new file mode 100644
index 0000000000..189ae58b39
--- /dev/null
+++ b/source4/libnet/libnet_lookup.h
@@ -0,0 +1,69 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+
+struct libnet_Lookup {
+ struct {
+ const char *hostname;
+ int type;
+ struct resolve_context *resolve_ctx;
+ } in;
+ struct {
+ const char **address;
+ } out;
+};
+
+
+struct libnet_LookupDCs {
+ struct {
+ const char *domain_name;
+ int name_type;
+ } in;
+ struct {
+ int num_dcs;
+ struct nbt_dc_name *dcs;
+ } out;
+};
+
+
+struct libnet_LookupName {
+ struct {
+ const char *name;
+ const char *domain_name;
+ } in;
+ struct {
+ struct dom_sid *sid;
+ int rid;
+ enum lsa_SidType sid_type;
+ const char *sidstr;
+ const char *error_string;
+ } out;
+};
+
+
+/*
+ * Monitor messages sent from libnet_lookup.c functions
+ */
+
+struct msg_net_lookup_dc {
+ const char *domain_name;
+ const char *hostname;
+ const char *address;
+};
+
diff --git a/source4/libnet/libnet_passwd.c b/source4/libnet/libnet_passwd.c
new file mode 100644
index 0000000000..c2db219a06
--- /dev/null
+++ b/source4/libnet/libnet_passwd.c
@@ -0,0 +1,711 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Stefan Metzmacher 2004
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libnet/libnet.h"
+#include "../lib/crypto/crypto.h"
+#include "libcli/auth/libcli_auth.h"
+#include "librpc/gen_ndr/ndr_samr_c.h"
+
+/*
+ * do a password change using DCERPC/SAMR calls
+ * 1. connect to the SAMR pipe of users domain PDC (maybe a standalone server or workstation)
+ * 2. try samr_ChangePasswordUser3
+ * 3. try samr_ChangePasswordUser2
+ * 4. try samr_OemChangePasswordUser2
+ * (not yet: 5. try samr_ChangePasswordUser)
+ */
+static NTSTATUS libnet_ChangePassword_samr(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_ChangePassword *r)
+{
+ NTSTATUS status;
+ struct libnet_RpcConnect c;
+#if 0
+ struct policy_handle user_handle;
+ struct samr_Password hash1, hash2, hash3, hash4, hash5, hash6;
+ struct samr_ChangePasswordUser pw;
+#endif
+ struct samr_OemChangePasswordUser2 oe2;
+ struct samr_ChangePasswordUser2 pw2;
+ struct samr_ChangePasswordUser3 pw3;
+ struct lsa_String server, account;
+ struct lsa_AsciiString a_server, a_account;
+ struct samr_CryptPassword nt_pass, lm_pass;
+ struct samr_Password nt_verifier, lm_verifier;
+ uint8_t old_nt_hash[16], new_nt_hash[16];
+ uint8_t old_lm_hash[16], new_lm_hash[16];
+ struct samr_DomInfo1 *dominfo = NULL;
+ struct samr_ChangeReject *reject = NULL;
+
+ /* prepare connect to the SAMR pipe of the users domain PDC */
+ c.level = LIBNET_RPC_CONNECT_PDC;
+ c.in.name = r->samr.in.domain_name;
+ c.in.dcerpc_iface = &ndr_table_samr;
+
+ /* 1. connect to the SAMR pipe of users domain PDC (maybe a standalone server or workstation) */
+ status = libnet_RpcConnect(ctx, mem_ctx, &c);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->samr.out.error_string = talloc_asprintf(mem_ctx,
+ "Connection to SAMR pipe of PDC of domain '%s' failed: %s",
+ r->samr.in.domain_name, nt_errstr(status));
+ return status;
+ }
+
+ /* prepare password change for account */
+ server.string = talloc_asprintf(mem_ctx, "\\\\%s", dcerpc_server_name(c.out.dcerpc_pipe));
+ account.string = r->samr.in.account_name;
+
+ E_md4hash(r->samr.in.oldpassword, old_nt_hash);
+ E_md4hash(r->samr.in.newpassword, new_nt_hash);
+
+ E_deshash(r->samr.in.oldpassword, old_lm_hash);
+ E_deshash(r->samr.in.newpassword, new_lm_hash);
+
+ /* prepare samr_ChangePasswordUser3 */
+ encode_pw_buffer(lm_pass.data, r->samr.in.newpassword, STR_UNICODE);
+ arcfour_crypt(lm_pass.data, old_nt_hash, 516);
+ E_old_pw_hash(new_lm_hash, old_lm_hash, lm_verifier.hash);
+
+ encode_pw_buffer(nt_pass.data, r->samr.in.newpassword, STR_UNICODE);
+ arcfour_crypt(nt_pass.data, old_nt_hash, 516);
+ E_old_pw_hash(new_nt_hash, old_nt_hash, nt_verifier.hash);
+
+ pw3.in.server = &server;
+ pw3.in.account = &account;
+ pw3.in.nt_password = &nt_pass;
+ pw3.in.nt_verifier = &nt_verifier;
+ pw3.in.lm_change = 1;
+ pw3.in.lm_password = &lm_pass;
+ pw3.in.lm_verifier = &lm_verifier;
+ pw3.in.password3 = NULL;
+ pw3.out.dominfo = &dominfo;
+ pw3.out.reject = &reject;
+
+ /* 2. try samr_ChangePasswordUser3 */
+ status = dcerpc_samr_ChangePasswordUser3(c.out.dcerpc_pipe, mem_ctx, &pw3);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->samr.out.error_string = talloc_asprintf(mem_ctx,
+ "samr_ChangePasswordUser3 failed: %s",
+ nt_errstr(status));
+ goto ChangePasswordUser2;
+ }
+
+ /* check result of samr_ChangePasswordUser3 */
+ if (!NT_STATUS_IS_OK(pw3.out.result)) {
+ r->samr.out.error_string = talloc_asprintf(mem_ctx,
+ "samr_ChangePasswordUser3 for '%s\\%s' failed: %s",
+ r->samr.in.domain_name, r->samr.in.account_name,
+ nt_errstr(pw3.out.result));
+ /* TODO: give the reason of the reject */
+ if (NT_STATUS_EQUAL(pw3.out.result, NT_STATUS_PASSWORD_RESTRICTION)) {
+ status = pw3.out.result;
+ goto disconnect;
+ }
+ goto ChangePasswordUser2;
+ }
+
+ goto disconnect;
+
+ChangePasswordUser2:
+ /* prepare samr_ChangePasswordUser2 */
+ encode_pw_buffer(lm_pass.data, r->samr.in.newpassword, STR_ASCII|STR_TERMINATE);
+ arcfour_crypt(lm_pass.data, old_lm_hash, 516);
+ E_old_pw_hash(new_lm_hash, old_lm_hash, lm_verifier.hash);
+
+ encode_pw_buffer(nt_pass.data, r->samr.in.newpassword, STR_UNICODE);
+ arcfour_crypt(nt_pass.data, old_nt_hash, 516);
+ E_old_pw_hash(new_nt_hash, old_nt_hash, nt_verifier.hash);
+
+ pw2.in.server = &server;
+ pw2.in.account = &account;
+ pw2.in.nt_password = &nt_pass;
+ pw2.in.nt_verifier = &nt_verifier;
+ pw2.in.lm_change = 1;
+ pw2.in.lm_password = &lm_pass;
+ pw2.in.lm_verifier = &lm_verifier;
+
+ /* 3. try samr_ChangePasswordUser2 */
+ status = dcerpc_samr_ChangePasswordUser2(c.out.dcerpc_pipe, mem_ctx, &pw2);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->samr.out.error_string = talloc_asprintf(mem_ctx,
+ "samr_ChangePasswordUser2 failed: %s",
+ nt_errstr(status));
+ goto OemChangePasswordUser2;
+ }
+
+ /* check result of samr_ChangePasswordUser2 */
+ if (!NT_STATUS_IS_OK(pw2.out.result)) {
+ r->samr.out.error_string = talloc_asprintf(mem_ctx,
+ "samr_ChangePasswordUser2 for '%s\\%s' failed: %s",
+ r->samr.in.domain_name, r->samr.in.account_name,
+ nt_errstr(pw2.out.result));
+ if (NT_STATUS_EQUAL(pw2.out.result, NT_STATUS_PASSWORD_RESTRICTION)) {
+ status = pw2.out.result;
+ goto disconnect;
+ }
+ goto OemChangePasswordUser2;
+ }
+
+ goto disconnect;
+
+OemChangePasswordUser2:
+ /* prepare samr_OemChangePasswordUser2 */
+ a_server.string = talloc_asprintf(mem_ctx, "\\\\%s", dcerpc_server_name(c.out.dcerpc_pipe));
+ a_account.string = r->samr.in.account_name;
+
+ encode_pw_buffer(lm_pass.data, r->samr.in.newpassword, STR_ASCII);
+ arcfour_crypt(lm_pass.data, old_lm_hash, 516);
+ E_old_pw_hash(new_lm_hash, old_lm_hash, lm_verifier.hash);
+
+ oe2.in.server = &a_server;
+ oe2.in.account = &a_account;
+ oe2.in.password = &lm_pass;
+ oe2.in.hash = &lm_verifier;
+
+ /* 4. try samr_OemChangePasswordUser2 */
+ status = dcerpc_samr_OemChangePasswordUser2(c.out.dcerpc_pipe, mem_ctx, &oe2);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->samr.out.error_string = talloc_asprintf(mem_ctx,
+ "samr_OemChangePasswordUser2 failed: %s",
+ nt_errstr(status));
+ goto ChangePasswordUser;
+ }
+
+ /* check result of samr_OemChangePasswordUser2 */
+ if (!NT_STATUS_IS_OK(oe2.out.result)) {
+ r->samr.out.error_string = talloc_asprintf(mem_ctx,
+ "samr_OemChangePasswordUser2 for '%s\\%s' failed: %s",
+ r->samr.in.domain_name, r->samr.in.account_name,
+ nt_errstr(oe2.out.result));
+ if (NT_STATUS_EQUAL(oe2.out.result, NT_STATUS_PASSWORD_RESTRICTION)) {
+ status = oe2.out.result;
+ goto disconnect;
+ }
+ goto ChangePasswordUser;
+ }
+
+ goto disconnect;
+
+ChangePasswordUser:
+#if 0
+ /* prepare samr_ChangePasswordUser */
+ 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);
+
+ /* TODO: ask for a user_handle */
+ pw.in.handle = &user_handle;
+ pw.in.lm_present = 1;
+ pw.in.old_lm_crypted = &hash1;
+ pw.in.new_lm_crypted = &hash2;
+ pw.in.nt_present = 1;
+ pw.in.old_nt_crypted = &hash3;
+ pw.in.new_nt_crypted = &hash4;
+ pw.in.cross1_present = 1;
+ pw.in.nt_cross = &hash5;
+ pw.in.cross2_present = 1;
+ pw.in.lm_cross = &hash6;
+
+ /* 5. try samr_ChangePasswordUser */
+ status = dcerpc_samr_ChangePasswordUser(c.pdc.out.dcerpc_pipe, mem_ctx, &pw);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->samr.out.error_string = talloc_asprintf(mem_ctx,
+ "samr_ChangePasswordUser failed: %s",
+ nt_errstr(status));
+ goto disconnect;
+ }
+
+ /* check result of samr_ChangePasswordUser */
+ if (!NT_STATUS_IS_OK(pw.out.result)) {
+ r->samr.out.error_string = talloc_asprintf(mem_ctx,
+ "samr_ChangePasswordUser for '%s\\%s' failed: %s",
+ r->samr.in.domain_name, r->samr.in.account_name,
+ nt_errstr(pw.out.result));
+ if (NT_STATUS_EQUAL(pw.out.result, NT_STATUS_PASSWORD_RESTRICTION)) {
+ status = pw.out.result;
+ goto disconnect;
+ }
+ goto disconnect;
+ }
+#endif
+disconnect:
+ /* close connection */
+ talloc_free(c.out.dcerpc_pipe);
+
+ return status;
+}
+
+static NTSTATUS libnet_ChangePassword_generic(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_ChangePassword *r)
+{
+ NTSTATUS status;
+ union libnet_ChangePassword r2;
+
+ r2.samr.level = LIBNET_CHANGE_PASSWORD_SAMR;
+ r2.samr.in.account_name = r->generic.in.account_name;
+ r2.samr.in.domain_name = r->generic.in.domain_name;
+ r2.samr.in.oldpassword = r->generic.in.oldpassword;
+ r2.samr.in.newpassword = r->generic.in.newpassword;
+
+ status = libnet_ChangePassword(ctx, mem_ctx, &r2);
+
+ r->generic.out.error_string = r2.samr.out.error_string;
+
+ return status;
+}
+
+NTSTATUS libnet_ChangePassword(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_ChangePassword *r)
+{
+ switch (r->generic.level) {
+ case LIBNET_CHANGE_PASSWORD_GENERIC:
+ return libnet_ChangePassword_generic(ctx, mem_ctx, r);
+ case LIBNET_CHANGE_PASSWORD_SAMR:
+ return libnet_ChangePassword_samr(ctx, mem_ctx, r);
+ case LIBNET_CHANGE_PASSWORD_KRB5:
+ return NT_STATUS_NOT_IMPLEMENTED;
+ case LIBNET_CHANGE_PASSWORD_LDAP:
+ return NT_STATUS_NOT_IMPLEMENTED;
+ case LIBNET_CHANGE_PASSWORD_RAP:
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ return NT_STATUS_INVALID_LEVEL;
+}
+
+static NTSTATUS libnet_SetPassword_samr_handle_26(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_SetPassword *r)
+{
+ NTSTATUS status;
+ struct samr_SetUserInfo2 sui;
+ union samr_UserInfo u_info;
+ DATA_BLOB session_key;
+ DATA_BLOB confounded_session_key = data_blob_talloc(mem_ctx, NULL, 16);
+ uint8_t confounder[16];
+ struct MD5Context md5;
+
+ if (r->samr_handle.in.info21) {
+ return NT_STATUS_INVALID_PARAMETER_MIX;
+ }
+
+ /* prepare samr_SetUserInfo2 level 26 */
+ ZERO_STRUCT(u_info);
+ encode_pw_buffer(u_info.info26.password.data, r->samr_handle.in.newpassword, STR_UNICODE);
+ u_info.info26.password_expired = 0;
+
+ status = dcerpc_fetch_session_key(r->samr_handle.in.dcerpc_pipe, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->samr_handle.out.error_string = talloc_asprintf(mem_ctx,
+ "dcerpc_fetch_session_key failed: %s",
+ nt_errstr(status));
+ return status;
+ }
+
+ generate_random_buffer((uint8_t *)confounder, 16);
+
+ MD5Init(&md5);
+ MD5Update(&md5, confounder, 16);
+ MD5Update(&md5, session_key.data, session_key.length);
+ MD5Final(confounded_session_key.data, &md5);
+
+ arcfour_crypt_blob(u_info.info26.password.data, 516, &confounded_session_key);
+ memcpy(&u_info.info26.password.data[516], confounder, 16);
+
+ sui.in.user_handle = r->samr_handle.in.user_handle;
+ sui.in.info = &u_info;
+ sui.in.level = 26;
+
+ /* 7. try samr_SetUserInfo2 level 26 to set the password */
+ status = dcerpc_samr_SetUserInfo2(r->samr_handle.in.dcerpc_pipe, mem_ctx, &sui);
+ /* check result of samr_SetUserInfo2 level 26 */
+ if (!NT_STATUS_IS_OK(status)) {
+ r->samr_handle.out.error_string
+ = talloc_asprintf(mem_ctx,
+ "SetUserInfo2 level 26 for [%s] failed: %s",
+ r->samr_handle.in.account_name, nt_errstr(status));
+ }
+ return status;
+}
+
+static NTSTATUS libnet_SetPassword_samr_handle_25(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_SetPassword *r)
+{
+ NTSTATUS status;
+ struct samr_SetUserInfo2 sui;
+ union samr_UserInfo u_info;
+ DATA_BLOB session_key;
+ DATA_BLOB confounded_session_key = data_blob_talloc(mem_ctx, NULL, 16);
+ uint8_t confounder[16];
+ struct MD5Context md5;
+
+ if (!r->samr_handle.in.info21) {
+ return NT_STATUS_INVALID_PARAMETER_MIX;
+ }
+
+ /* prepare samr_SetUserInfo2 level 25 */
+ ZERO_STRUCT(u_info);
+ u_info.info25.info = *r->samr_handle.in.info21;
+ u_info.info25.info.fields_present |= SAMR_FIELD_NT_PASSWORD_PRESENT;
+ encode_pw_buffer(u_info.info25.password.data, r->samr_handle.in.newpassword, STR_UNICODE);
+
+ status = dcerpc_fetch_session_key(r->samr_handle.in.dcerpc_pipe, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->samr_handle.out.error_string = talloc_asprintf(mem_ctx,
+ "dcerpc_fetch_session_key failed: %s",
+ nt_errstr(status));
+ return status;
+ }
+
+ generate_random_buffer((uint8_t *)confounder, 16);
+
+ MD5Init(&md5);
+ MD5Update(&md5, confounder, 16);
+ MD5Update(&md5, session_key.data, session_key.length);
+ MD5Final(confounded_session_key.data, &md5);
+
+ arcfour_crypt_blob(u_info.info25.password.data, 516, &confounded_session_key);
+ memcpy(&u_info.info25.password.data[516], confounder, 16);
+
+ sui.in.user_handle = r->samr_handle.in.user_handle;
+ sui.in.info = &u_info;
+ sui.in.level = 25;
+
+ /* 8. try samr_SetUserInfo2 level 25 to set the password */
+ status = dcerpc_samr_SetUserInfo2(r->samr_handle.in.dcerpc_pipe, mem_ctx, &sui);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->samr_handle.out.error_string
+ = talloc_asprintf(mem_ctx,
+ "SetUserInfo2 level 25 for [%s] failed: %s",
+ r->samr_handle.in.account_name, nt_errstr(status));
+ }
+ return status;
+}
+
+static NTSTATUS libnet_SetPassword_samr_handle_24(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_SetPassword *r)
+{
+ NTSTATUS status;
+ struct samr_SetUserInfo2 sui;
+ union samr_UserInfo u_info;
+ DATA_BLOB session_key;
+
+ if (r->samr_handle.in.info21) {
+ return NT_STATUS_INVALID_PARAMETER_MIX;
+ }
+
+ /* prepare samr_SetUserInfo2 level 24 */
+ ZERO_STRUCT(u_info);
+ encode_pw_buffer(u_info.info24.password.data, r->samr_handle.in.newpassword, STR_UNICODE);
+ u_info.info24.password_expired = 0;
+
+ status = dcerpc_fetch_session_key(r->samr_handle.in.dcerpc_pipe, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->samr_handle.out.error_string = talloc_asprintf(mem_ctx,
+ "dcerpc_fetch_session_key failed: %s",
+ nt_errstr(status));
+ return status;
+ }
+
+ arcfour_crypt_blob(u_info.info24.password.data, 516, &session_key);
+
+ sui.in.user_handle = r->samr_handle.in.user_handle;
+ sui.in.info = &u_info;
+ sui.in.level = 24;
+
+ /* 9. try samr_SetUserInfo2 level 24 to set the password */
+ status = dcerpc_samr_SetUserInfo2(r->samr_handle.in.dcerpc_pipe, mem_ctx, &sui);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->samr_handle.out.error_string
+ = talloc_asprintf(mem_ctx,
+ "SetUserInfo2 level 24 for [%s] failed: %s",
+ r->samr_handle.in.account_name, nt_errstr(status));
+ }
+ return status;
+}
+
+static NTSTATUS libnet_SetPassword_samr_handle_23(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_SetPassword *r)
+{
+ NTSTATUS status;
+ struct samr_SetUserInfo2 sui;
+ union samr_UserInfo u_info;
+ DATA_BLOB session_key;
+
+ if (!r->samr_handle.in.info21) {
+ return NT_STATUS_INVALID_PARAMETER_MIX;
+ }
+
+ /* prepare samr_SetUserInfo2 level 23 */
+ ZERO_STRUCT(u_info);
+ u_info.info23.info = *r->samr_handle.in.info21;
+ u_info.info23.info.fields_present |= SAMR_FIELD_NT_PASSWORD_PRESENT;
+ encode_pw_buffer(u_info.info23.password.data, r->samr_handle.in.newpassword, STR_UNICODE);
+
+ status = dcerpc_fetch_session_key(r->samr_handle.in.dcerpc_pipe, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->samr_handle.out.error_string
+ = talloc_asprintf(mem_ctx,
+ "dcerpc_fetch_session_key failed: %s",
+ nt_errstr(status));
+ return status;
+ }
+
+ arcfour_crypt_blob(u_info.info23.password.data, 516, &session_key);
+
+ sui.in.user_handle = r->samr_handle.in.user_handle;
+ sui.in.info = &u_info;
+ sui.in.level = 23;
+
+ /* 10. try samr_SetUserInfo2 level 23 to set the password */
+ status = dcerpc_samr_SetUserInfo2(r->samr_handle.in.dcerpc_pipe, mem_ctx, &sui);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->samr_handle.out.error_string
+ = talloc_asprintf(mem_ctx,
+ "SetUserInfo2 level 23 for [%s] failed: %s",
+ r->samr_handle.in.account_name, nt_errstr(status));
+ }
+ return status;
+}
+
+/*
+ * 1. try samr_SetUserInfo2 level 26 to set the password
+ * 2. try samr_SetUserInfo2 level 25 to set the password
+ * 3. try samr_SetUserInfo2 level 24 to set the password
+ * 4. try samr_SetUserInfo2 level 23 to set the password
+*/
+static NTSTATUS libnet_SetPassword_samr_handle(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_SetPassword *r)
+{
+
+ NTSTATUS status;
+ enum libnet_SetPassword_level levels[] = {
+ LIBNET_SET_PASSWORD_SAMR_HANDLE_26,
+ LIBNET_SET_PASSWORD_SAMR_HANDLE_25,
+ LIBNET_SET_PASSWORD_SAMR_HANDLE_24,
+ LIBNET_SET_PASSWORD_SAMR_HANDLE_23,
+ };
+ int i;
+
+ for (i=0; i < ARRAY_SIZE(levels); i++) {
+ r->generic.level = levels[i];
+ status = libnet_SetPassword(ctx, mem_ctx, r);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_INFO_CLASS)
+ || NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER_MIX)
+ || NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ /* Try another password set mechanism */
+ continue;
+ }
+ break;
+ }
+
+ return status;
+}
+/*
+ * set a password with DCERPC/SAMR calls
+ * 1. connect to the SAMR pipe of users domain PDC (maybe a standalone server or workstation)
+ * is it correct to contact the the pdc of the domain of the user who's password should be set?
+ * 2. do a samr_Connect to get a policy handle
+ * 3. do a samr_LookupDomain to get the domain sid
+ * 4. do a samr_OpenDomain to get a domain handle
+ * 5. do a samr_LookupNames to get the users rid
+ * 6. do a samr_OpenUser to get a user handle
+ * 7 call libnet_SetPassword_samr_handle to set the password
+ */
+static NTSTATUS libnet_SetPassword_samr(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_SetPassword *r)
+{
+ NTSTATUS status;
+ struct libnet_RpcConnect c;
+ struct samr_Connect sc;
+ struct policy_handle p_handle;
+ struct samr_LookupDomain ld;
+ struct dom_sid2 *sid = NULL;
+ struct lsa_String d_name;
+ struct samr_OpenDomain od;
+ struct policy_handle d_handle;
+ struct samr_LookupNames ln;
+ struct samr_Ids rids, types;
+ struct samr_OpenUser ou;
+ struct policy_handle u_handle;
+ union libnet_SetPassword r2;
+
+ /* prepare connect to the SAMR pipe of users domain PDC */
+ c.level = LIBNET_RPC_CONNECT_PDC;
+ c.in.name = r->samr.in.domain_name;
+ c.in.dcerpc_iface = &ndr_table_samr;
+
+ /* 1. connect to the SAMR pipe of users domain PDC (maybe a standalone server or workstation) */
+ status = libnet_RpcConnect(ctx, mem_ctx, &c);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->samr.out.error_string = talloc_asprintf(mem_ctx,
+ "Connection to SAMR pipe of PDC of domain '%s' failed: %s",
+ r->samr.in.domain_name, nt_errstr(status));
+ return status;
+ }
+
+ /* prepare samr_Connect */
+ ZERO_STRUCT(p_handle);
+ sc.in.system_name = NULL;
+ sc.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ sc.out.connect_handle = &p_handle;
+
+ /* 2. do a samr_Connect to get a policy handle */
+ status = dcerpc_samr_Connect(c.out.dcerpc_pipe, mem_ctx, &sc);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->samr.out.error_string = talloc_asprintf(mem_ctx,
+ "samr_Connect failed: %s",
+ nt_errstr(status));
+ goto disconnect;
+ }
+
+ /* prepare samr_LookupDomain */
+ d_name.string = r->samr.in.domain_name;
+ ld.in.connect_handle = &p_handle;
+ ld.in.domain_name = &d_name;
+ ld.out.sid = &sid;
+
+ /* 3. do a samr_LookupDomain to get the domain sid */
+ status = dcerpc_samr_LookupDomain(c.out.dcerpc_pipe, mem_ctx, &ld);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->samr.out.error_string = talloc_asprintf(mem_ctx,
+ "samr_LookupDomain for [%s] failed: %s",
+ r->samr.in.domain_name, nt_errstr(status));
+ goto disconnect;
+ }
+
+ /* prepare samr_OpenDomain */
+ ZERO_STRUCT(d_handle);
+ od.in.connect_handle = &p_handle;
+ od.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ od.in.sid = *ld.out.sid;
+ od.out.domain_handle = &d_handle;
+
+ /* 4. do a samr_OpenDomain to get a domain handle */
+ status = dcerpc_samr_OpenDomain(c.out.dcerpc_pipe, mem_ctx, &od);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->samr.out.error_string = talloc_asprintf(mem_ctx,
+ "samr_OpenDomain for [%s] failed: %s",
+ r->samr.in.domain_name, nt_errstr(status));
+ goto disconnect;
+ }
+
+ /* prepare samr_LookupNames */
+ ln.in.domain_handle = &d_handle;
+ ln.in.num_names = 1;
+ ln.in.names = talloc_array(mem_ctx, struct lsa_String, 1);
+ ln.out.rids = &rids;
+ ln.out.types = &types;
+ if (!ln.in.names) {
+ r->samr.out.error_string = "Out of Memory";
+ return NT_STATUS_NO_MEMORY;
+ }
+ ln.in.names[0].string = r->samr.in.account_name;
+
+ /* 5. do a samr_LookupNames to get the users rid */
+ status = dcerpc_samr_LookupNames(c.out.dcerpc_pipe, mem_ctx, &ln);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->samr.out.error_string = talloc_asprintf(mem_ctx,
+ "samr_LookupNames for [%s] failed: %s",
+ r->samr.in.account_name, nt_errstr(status));
+ goto disconnect;
+ }
+
+ /* check if we got one RID for the user */
+ if (ln.out.rids->count != 1) {
+ r->samr.out.error_string = talloc_asprintf(mem_ctx,
+ "samr_LookupNames for [%s] returns %d RIDs",
+ r->samr.in.account_name, ln.out.rids->count);
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto disconnect;
+ }
+
+ /* prepare samr_OpenUser */
+ ZERO_STRUCT(u_handle);
+ ou.in.domain_handle = &d_handle;
+ ou.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ ou.in.rid = ln.out.rids->ids[0];
+ ou.out.user_handle = &u_handle;
+
+ /* 6. do a samr_OpenUser to get a user handle */
+ status = dcerpc_samr_OpenUser(c.out.dcerpc_pipe, mem_ctx, &ou);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->samr.out.error_string = talloc_asprintf(mem_ctx,
+ "samr_OpenUser for [%s] failed: %s",
+ r->samr.in.account_name, nt_errstr(status));
+ goto disconnect;
+ }
+
+ r2.samr_handle.level = LIBNET_SET_PASSWORD_SAMR_HANDLE;
+ r2.samr_handle.in.account_name = r->samr.in.account_name;
+ r2.samr_handle.in.newpassword = r->samr.in.newpassword;
+ r2.samr_handle.in.user_handle = &u_handle;
+ r2.samr_handle.in.dcerpc_pipe = c.out.dcerpc_pipe;
+ r2.samr_handle.in.info21 = NULL;
+
+ status = libnet_SetPassword(ctx, mem_ctx, &r2);
+
+ r->generic.out.error_string = r2.samr_handle.out.error_string;
+
+disconnect:
+ /* close connection */
+ talloc_free(c.out.dcerpc_pipe);
+
+ return status;
+}
+
+static NTSTATUS libnet_SetPassword_generic(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_SetPassword *r)
+{
+ NTSTATUS status;
+ union libnet_SetPassword r2;
+
+ r2.samr.level = LIBNET_SET_PASSWORD_SAMR;
+ r2.samr.in.account_name = r->generic.in.account_name;
+ r2.samr.in.domain_name = r->generic.in.domain_name;
+ r2.samr.in.newpassword = r->generic.in.newpassword;
+
+ r->generic.out.error_string = "Unknown Error";
+ status = libnet_SetPassword(ctx, mem_ctx, &r2);
+
+ r->generic.out.error_string = r2.samr.out.error_string;
+
+ return status;
+}
+
+NTSTATUS libnet_SetPassword(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_SetPassword *r)
+{
+ switch (r->generic.level) {
+ case LIBNET_SET_PASSWORD_GENERIC:
+ return libnet_SetPassword_generic(ctx, mem_ctx, r);
+ case LIBNET_SET_PASSWORD_SAMR:
+ return libnet_SetPassword_samr(ctx, mem_ctx, r);
+ case LIBNET_SET_PASSWORD_SAMR_HANDLE:
+ return libnet_SetPassword_samr_handle(ctx, mem_ctx, r);
+ case LIBNET_SET_PASSWORD_SAMR_HANDLE_26:
+ return libnet_SetPassword_samr_handle_26(ctx, mem_ctx, r);
+ case LIBNET_SET_PASSWORD_SAMR_HANDLE_25:
+ return libnet_SetPassword_samr_handle_25(ctx, mem_ctx, r);
+ case LIBNET_SET_PASSWORD_SAMR_HANDLE_24:
+ return libnet_SetPassword_samr_handle_24(ctx, mem_ctx, r);
+ case LIBNET_SET_PASSWORD_SAMR_HANDLE_23:
+ return libnet_SetPassword_samr_handle_23(ctx, mem_ctx, r);
+ case LIBNET_SET_PASSWORD_KRB5:
+ return NT_STATUS_NOT_IMPLEMENTED;
+ case LIBNET_SET_PASSWORD_LDAP:
+ return NT_STATUS_NOT_IMPLEMENTED;
+ case LIBNET_SET_PASSWORD_RAP:
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ return NT_STATUS_INVALID_LEVEL;
+}
diff --git a/source4/libnet/libnet_passwd.h b/source4/libnet/libnet_passwd.h
new file mode 100644
index 0000000000..f9fb5dad75
--- /dev/null
+++ b/source4/libnet/libnet_passwd.h
@@ -0,0 +1,137 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Stefan Metzmacher 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 <http://www.gnu.org/licenses/>.
+*/
+
+/* struct and enum for doing a remote password change */
+enum libnet_ChangePassword_level {
+ LIBNET_CHANGE_PASSWORD_GENERIC,
+ LIBNET_CHANGE_PASSWORD_SAMR,
+ LIBNET_CHANGE_PASSWORD_KRB5,
+ LIBNET_CHANGE_PASSWORD_LDAP,
+ LIBNET_CHANGE_PASSWORD_RAP
+};
+
+union libnet_ChangePassword {
+ struct {
+ enum libnet_ChangePassword_level level;
+
+ struct _libnet_ChangePassword_in {
+ const char *account_name;
+ const char *domain_name;
+ const char *oldpassword;
+ const char *newpassword;
+ } in;
+
+ struct _libnet_ChangePassword_out {
+ const char *error_string;
+ } out;
+ } generic;
+
+ struct {
+ enum libnet_ChangePassword_level level;
+ struct _libnet_ChangePassword_in in;
+ struct _libnet_ChangePassword_out out;
+ } samr;
+
+ struct {
+ enum libnet_ChangePassword_level level;
+ struct _libnet_ChangePassword_in in;
+ struct _libnet_ChangePassword_out out;
+ } krb5;
+
+ struct {
+ enum libnet_ChangePassword_level level;
+ struct _libnet_ChangePassword_in in;
+ struct _libnet_ChangePassword_out out;
+ } ldap;
+
+ struct {
+ enum libnet_ChangePassword_level level;
+ struct _libnet_ChangePassword_in in;
+ struct _libnet_ChangePassword_out out;
+ } rap;
+};
+
+/* struct and enum for doing a remote password set */
+enum libnet_SetPassword_level {
+ LIBNET_SET_PASSWORD_GENERIC,
+ LIBNET_SET_PASSWORD_SAMR,
+ LIBNET_SET_PASSWORD_SAMR_HANDLE,
+ LIBNET_SET_PASSWORD_SAMR_HANDLE_26,
+ LIBNET_SET_PASSWORD_SAMR_HANDLE_25,
+ LIBNET_SET_PASSWORD_SAMR_HANDLE_24,
+ LIBNET_SET_PASSWORD_SAMR_HANDLE_23,
+ LIBNET_SET_PASSWORD_KRB5,
+ LIBNET_SET_PASSWORD_LDAP,
+ LIBNET_SET_PASSWORD_RAP
+};
+
+union libnet_SetPassword {
+ struct {
+ enum libnet_SetPassword_level level;
+
+ struct _libnet_SetPassword_in {
+ const char *account_name;
+ const char *domain_name;
+ const char *newpassword;
+ } in;
+
+ struct _libnet_SetPassword_out {
+ const char *error_string;
+ } out;
+ } generic;
+
+ struct {
+ enum libnet_SetPassword_level level;
+ struct _libnet_SetPassword_samr_handle_in {
+ const char *account_name; /* for debug only */
+ struct policy_handle *user_handle;
+ struct dcerpc_pipe *dcerpc_pipe;
+ const char *newpassword;
+ struct samr_UserInfo21 *info21; /* can be NULL,
+ * for level 26,24 it must be NULL
+ * for level 25,23 it must be non-NULL
+ */
+ } in;
+ struct _libnet_SetPassword_out out;
+ } samr_handle;
+
+ struct {
+ enum libnet_SetPassword_level level;
+ struct _libnet_SetPassword_in in;
+ struct _libnet_SetPassword_out out;
+ } samr;
+
+ struct {
+ enum libnet_SetPassword_level level;
+ struct _libnet_SetPassword_in in;
+ struct _libnet_SetPassword_out out;
+ } krb5;
+
+ struct {
+ enum libnet_SetPassword_level level;
+ struct _libnet_SetPassword_in in;
+ struct _libnet_SetPassword_out out;
+ } ldap;
+
+ struct {
+ enum libnet_ChangePassword_level level;
+ struct _libnet_SetPassword_in in;
+ struct _libnet_SetPassword_out out;
+ } rap;
+};
diff --git a/source4/libnet/libnet_rpc.c b/source4/libnet/libnet_rpc.c
new file mode 100644
index 0000000000..a0d93287a5
--- /dev/null
+++ b/source4/libnet/libnet_rpc.c
@@ -0,0 +1,978 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Stefan Metzmacher 2004
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libnet/libnet.h"
+#include "libcli/libcli.h"
+#include "libcli/composite/composite.h"
+#include "librpc/rpc/dcerpc.h"
+#include "librpc/rpc/dcerpc_proto.h"
+#include "librpc/gen_ndr/ndr_lsa_c.h"
+#include "librpc/gen_ndr/ndr_samr.h"
+
+
+struct rpc_connect_srv_state {
+ struct libnet_context *ctx;
+ struct libnet_RpcConnect r;
+ const char *binding;
+
+ /* information about the progress */
+ void (*monitor_fn)(struct monitor_msg*);
+};
+
+
+static void continue_pipe_connect(struct composite_context *ctx);
+
+
+/**
+ * Initiates connection to rpc pipe on remote server
+ *
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param r data structure containing necessary parameters and return values
+ * @return composite context of this call
+ **/
+
+static struct composite_context* libnet_RpcConnectSrv_send(struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_RpcConnect *r,
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *c;
+ struct rpc_connect_srv_state *s;
+ struct dcerpc_binding *b;
+ struct composite_context *pipe_connect_req;
+
+ /* composite context allocation and setup */
+ c = composite_create(ctx, ctx->event_ctx);
+ if (c == NULL) return c;
+
+ s = talloc_zero(c, struct rpc_connect_srv_state);
+ if (composite_nomem(s, c)) return c;
+
+ c->private_data = s;
+ s->monitor_fn = monitor;
+
+ s->ctx = ctx;
+ s->r = *r;
+ ZERO_STRUCT(s->r.out);
+
+ /* prepare binding string */
+ switch (r->level) {
+ case LIBNET_RPC_CONNECT_SERVER:
+ s->binding = talloc_asprintf(s, "ncacn_np:%s", r->in.name);
+ break;
+ case LIBNET_RPC_CONNECT_SERVER_ADDRESS:
+ s->binding = talloc_asprintf(s, "ncacn_np:%s", r->in.address);
+ break;
+
+ case LIBNET_RPC_CONNECT_BINDING:
+ s->binding = talloc_strdup(s, r->in.binding);
+ break;
+
+ case LIBNET_RPC_CONNECT_DC:
+ case LIBNET_RPC_CONNECT_PDC:
+ /* this should never happen - DC and PDC level has a separate
+ composite function */
+ case LIBNET_RPC_CONNECT_DC_INFO:
+ /* this should never happen - DC_INFO level has a separate
+ composite function */
+ composite_error(c, NT_STATUS_INVALID_LEVEL);
+ return c;
+ }
+
+ /* parse binding string to the structure */
+ c->status = dcerpc_parse_binding(c, s->binding, &b);
+ if (!NT_STATUS_IS_OK(c->status)) {
+ DEBUG(0, ("Failed to parse dcerpc binding '%s'\n", s->binding));
+ composite_error(c, c->status);
+ return c;
+ }
+
+ if (r->level == LIBNET_RPC_CONNECT_SERVER_ADDRESS) {
+ b->target_hostname = talloc_reference(b, r->in.name);
+ if (composite_nomem(b->target_hostname, c)) {
+ return c;
+ }
+ }
+
+ /* connect to remote dcerpc pipe */
+ pipe_connect_req = dcerpc_pipe_connect_b_send(c, b, r->in.dcerpc_iface,
+ ctx->cred, c->event_ctx,
+ ctx->lp_ctx);
+ if (composite_nomem(pipe_connect_req, c)) return c;
+
+ composite_continue(c, pipe_connect_req, continue_pipe_connect, c);
+ return c;
+}
+
+
+/*
+ Step 2 of RpcConnectSrv - get rpc connection
+*/
+static void continue_pipe_connect(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct rpc_connect_srv_state *s;
+
+ c = talloc_get_type(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct rpc_connect_srv_state);
+
+ /* receive result of rpc pipe connection */
+ c->status = dcerpc_pipe_connect_b_recv(ctx, c, &s->r.out.dcerpc_pipe);
+
+ /* post monitor message */
+ if (s->monitor_fn) {
+ struct monitor_msg msg;
+ struct msg_net_rpc_connect data;
+ struct dcerpc_binding *binding = s->r.out.dcerpc_pipe->binding;
+
+ /* prepare monitor message and post it */
+ data.host = binding->host;
+ data.endpoint = binding->endpoint;
+ data.transport = binding->transport;
+ data.domain_name = binding->target_hostname;
+
+ msg.type = mon_NetRpcConnect;
+ msg.data = (void*)&data;
+ msg.data_size = sizeof(data);
+ s->monitor_fn(&msg);
+ }
+
+ composite_done(c);
+}
+
+
+/**
+ * Receives result of connection to rpc pipe on remote server
+ *
+ * @param c composite context
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param r data structure containing necessary parameters and return values
+ * @return nt status of rpc connection
+ **/
+
+static NTSTATUS libnet_RpcConnectSrv_recv(struct composite_context *c,
+ struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_RpcConnect *r)
+{
+ NTSTATUS status;
+ struct rpc_connect_srv_state *s = talloc_get_type(c->private_data,
+ struct rpc_connect_srv_state);
+
+ status = composite_wait(c);
+ if (NT_STATUS_IS_OK(status)) {
+ /* move the returned rpc pipe between memory contexts */
+ s = talloc_get_type(c->private_data, struct rpc_connect_srv_state);
+ r->out.dcerpc_pipe = talloc_steal(mem_ctx, s->r.out.dcerpc_pipe);
+
+ /* reference created pipe structure to long-term libnet_context
+ so that it can be used by other api functions even after short-term
+ mem_ctx is freed */
+ if (r->in.dcerpc_iface == &ndr_table_samr) {
+ ctx->samr.pipe = talloc_reference(ctx, r->out.dcerpc_pipe);
+
+ } else if (r->in.dcerpc_iface == &ndr_table_lsarpc) {
+ ctx->lsa.pipe = talloc_reference(ctx, r->out.dcerpc_pipe);
+ }
+
+ r->out.error_string = talloc_strdup(mem_ctx, "Success");
+
+ } else {
+ r->out.error_string = talloc_asprintf(mem_ctx, "Error: %s", nt_errstr(status));
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+
+struct rpc_connect_dc_state {
+ struct libnet_context *ctx;
+ struct libnet_RpcConnect r;
+ struct libnet_RpcConnect r2;
+ struct libnet_LookupDCs f;
+ const char *connect_name;
+
+ /* information about the progress */
+ void (*monitor_fn)(struct monitor_msg *);
+};
+
+
+static void continue_lookup_dc(struct composite_context *ctx);
+static void continue_rpc_connect(struct composite_context *ctx);
+
+
+/**
+ * Initiates connection to rpc pipe on domain pdc
+ *
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param r data structure containing necessary parameters and return values
+ * @return composite context of this call
+ **/
+
+static struct composite_context* libnet_RpcConnectDC_send(struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_RpcConnect *r,
+ void (*monitor)(struct monitor_msg *msg))
+{
+ struct composite_context *c;
+ struct rpc_connect_dc_state *s;
+ struct composite_context *lookup_dc_req;
+
+ /* composite context allocation and setup */
+ c = composite_create(ctx, ctx->event_ctx);
+ if (c == NULL) return c;
+
+ s = talloc_zero(c, struct rpc_connect_dc_state);
+ if (composite_nomem(s, c)) return c;
+
+ c->private_data = s;
+ s->monitor_fn = monitor;
+
+ s->ctx = ctx;
+ s->r = *r;
+ ZERO_STRUCT(s->r.out);
+
+ switch (r->level) {
+ case LIBNET_RPC_CONNECT_PDC:
+ s->f.in.name_type = NBT_NAME_PDC;
+ break;
+
+ case LIBNET_RPC_CONNECT_DC:
+ s->f.in.name_type = NBT_NAME_LOGON;
+ break;
+
+ default:
+ break;
+ }
+
+ s->f.in.domain_name = r->in.name;
+ s->f.out.num_dcs = 0;
+ s->f.out.dcs = NULL;
+
+ /* find the domain pdc first */
+ lookup_dc_req = libnet_LookupDCs_send(ctx, c, &s->f);
+ if (composite_nomem(lookup_dc_req, c)) return c;
+
+ composite_continue(c, lookup_dc_req, continue_lookup_dc, c);
+ return c;
+}
+
+
+/*
+ Step 2 of RpcConnectDC: get domain controller name and
+ initiate RpcConnect to it
+*/
+static void continue_lookup_dc(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct rpc_connect_dc_state *s;
+ struct composite_context *rpc_connect_req;
+ struct monitor_msg msg;
+ struct msg_net_lookup_dc data;
+
+ c = talloc_get_type(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct rpc_connect_dc_state);
+
+ /* receive result of domain controller lookup */
+ c->status = libnet_LookupDCs_recv(ctx, c, &s->f);
+ if (!composite_is_ok(c)) return;
+
+ /* decide on preferred address type depending on DC type */
+ s->connect_name = s->f.out.dcs[0].name;
+
+ /* post monitor message */
+ if (s->monitor_fn) {
+ /* prepare a monitor message and post it */
+ data.domain_name = s->f.in.domain_name;
+ data.hostname = s->f.out.dcs[0].name;
+ data.address = s->f.out.dcs[0].address;
+
+ msg.type = mon_NetLookupDc;
+ msg.data = &data;
+ msg.data_size = sizeof(data);
+ s->monitor_fn(&msg);
+ }
+
+ /* ok, pdc has been found so do attempt to rpc connect */
+ s->r2.level = LIBNET_RPC_CONNECT_SERVER_ADDRESS;
+
+ /* this will cause yet another name resolution, but at least
+ * we pass the right name down the stack now */
+ s->r2.in.name = talloc_strdup(s, s->connect_name);
+ s->r2.in.address = talloc_steal(s, s->f.out.dcs[0].address);
+ s->r2.in.dcerpc_iface = s->r.in.dcerpc_iface;
+
+ /* send rpc connect request to the server */
+ rpc_connect_req = libnet_RpcConnectSrv_send(s->ctx, c, &s->r2, s->monitor_fn);
+ if (composite_nomem(rpc_connect_req, c)) return;
+
+ composite_continue(c, rpc_connect_req, continue_rpc_connect, c);
+}
+
+
+/*
+ Step 3 of RpcConnectDC: get rpc connection to the server
+*/
+static void continue_rpc_connect(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct rpc_connect_dc_state *s;
+
+ c = talloc_get_type(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct rpc_connect_dc_state);
+
+ c->status = libnet_RpcConnectSrv_recv(ctx, s->ctx, c, &s->r2);
+
+ /* error string is to be passed anyway */
+ s->r.out.error_string = s->r2.out.error_string;
+ if (!composite_is_ok(c)) return;
+
+ s->r.out.dcerpc_pipe = s->r2.out.dcerpc_pipe;
+
+ /* post monitor message */
+ if (s->monitor_fn) {
+ struct monitor_msg msg;
+ struct msg_net_rpc_connect data;
+ struct dcerpc_binding *binding = s->r.out.dcerpc_pipe->binding;
+
+ data.host = binding->host;
+ data.endpoint = binding->endpoint;
+ data.transport = binding->transport;
+ data.domain_name = binding->target_hostname;
+
+ msg.type = mon_NetRpcConnect;
+ msg.data = (void*)&data;
+ msg.data_size = sizeof(data);
+ s->monitor_fn(&msg);
+ }
+
+ composite_done(c);
+}
+
+
+/**
+ * Receives result of connection to rpc pipe on domain pdc
+ *
+ * @param c composite context
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param r data structure containing necessary parameters and return values
+ * @return nt status of rpc connection
+ **/
+
+static NTSTATUS libnet_RpcConnectDC_recv(struct composite_context *c,
+ struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_RpcConnect *r)
+{
+ NTSTATUS status;
+ struct rpc_connect_dc_state *s = talloc_get_type(c->private_data,
+ struct rpc_connect_dc_state);
+
+ status = composite_wait(c);
+ if (NT_STATUS_IS_OK(status)) {
+ /* move connected rpc pipe between memory contexts */
+ r->out.dcerpc_pipe = talloc_steal(mem_ctx, s->r.out.dcerpc_pipe);
+
+ /* reference created pipe structure to long-term libnet_context
+ so that it can be used by other api functions even after short-term
+ mem_ctx is freed */
+ if (r->in.dcerpc_iface == &ndr_table_samr) {
+ ctx->samr.pipe = talloc_reference(ctx, r->out.dcerpc_pipe);
+
+ } else if (r->in.dcerpc_iface == &ndr_table_lsarpc) {
+ ctx->lsa.pipe = talloc_reference(ctx, r->out.dcerpc_pipe);
+ }
+
+ } else {
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "Failed to rpc connect: %s",
+ nt_errstr(status));
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+
+
+struct rpc_connect_dci_state {
+ struct libnet_context *ctx;
+ struct libnet_RpcConnect r;
+ struct libnet_RpcConnect rpc_conn;
+ struct policy_handle lsa_handle;
+ struct lsa_QosInfo qos;
+ struct lsa_ObjectAttribute attr;
+ struct lsa_OpenPolicy2 lsa_open_policy;
+ struct dcerpc_pipe *lsa_pipe;
+ struct lsa_QueryInfoPolicy2 lsa_query_info2;
+ struct lsa_QueryInfoPolicy lsa_query_info;
+ struct dcerpc_binding *final_binding;
+ struct dcerpc_pipe *final_pipe;
+
+ /* information about the progress */
+ void (*monitor_fn)(struct monitor_msg*);
+};
+
+
+static void continue_dci_rpc_connect(struct composite_context *ctx);
+static void continue_lsa_policy(struct rpc_request *req);
+static void continue_lsa_query_info(struct rpc_request *req);
+static void continue_lsa_query_info2(struct rpc_request *req);
+static void continue_epm_map_binding(struct composite_context *ctx);
+static void continue_secondary_conn(struct composite_context *ctx);
+static void continue_epm_map_binding_send(struct composite_context *c);
+
+
+/**
+ * Initiates connection to rpc pipe on remote server or pdc. Received result
+ * contains info on the domain name, domain sid and realm.
+ *
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param r data structure containing necessary parameters and return values. Must be a talloc context
+ * @return composite context of this call
+ **/
+
+static struct composite_context* libnet_RpcConnectDCInfo_send(struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_RpcConnect *r,
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *c, *conn_req;
+ struct rpc_connect_dci_state *s;
+
+ /* composite context allocation and setup */
+ c = composite_create(ctx, ctx->event_ctx);
+ if (c == NULL) return c;
+
+ s = talloc_zero(c, struct rpc_connect_dci_state);
+ if (composite_nomem(s, c)) return c;
+
+ c->private_data = s;
+ s->monitor_fn = monitor;
+
+ s->ctx = ctx;
+ s->r = *r;
+ ZERO_STRUCT(s->r.out);
+
+ /* proceed to pure rpc connection if the binding string is provided,
+ otherwise try to connect domain controller */
+ if (r->in.binding == NULL) {
+ s->rpc_conn.in.name = r->in.name;
+ s->rpc_conn.level = LIBNET_RPC_CONNECT_DC;
+ } else {
+ s->rpc_conn.in.binding = r->in.binding;
+ s->rpc_conn.level = LIBNET_RPC_CONNECT_BINDING;
+ }
+
+ /* we need to query information on lsarpc interface first */
+ s->rpc_conn.in.dcerpc_iface = &ndr_table_lsarpc;
+
+ /* request connection to the lsa pipe on the pdc */
+ conn_req = libnet_RpcConnect_send(ctx, c, &s->rpc_conn, s->monitor_fn);
+ if (composite_nomem(c, conn_req)) return c;
+
+ composite_continue(c, conn_req, continue_dci_rpc_connect, c);
+ return c;
+}
+
+
+/*
+ Step 2 of RpcConnectDCInfo: receive opened rpc pipe and open
+ lsa policy handle
+*/
+static void continue_dci_rpc_connect(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct rpc_connect_dci_state *s;
+ struct rpc_request *open_pol_req;
+
+ c = talloc_get_type(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct rpc_connect_dci_state);
+
+ c->status = libnet_RpcConnect_recv(ctx, s->ctx, c, &s->rpc_conn);
+ if (!NT_STATUS_IS_OK(c->status)) {
+ composite_error(c, c->status);
+ return;
+ }
+
+ /* post monitor message */
+ if (s->monitor_fn) {
+ struct monitor_msg msg;
+ struct msg_net_rpc_connect data;
+ struct dcerpc_binding *binding = s->r.out.dcerpc_pipe->binding;
+
+ data.host = binding->host;
+ data.endpoint = binding->endpoint;
+ data.transport = binding->transport;
+ data.domain_name = binding->target_hostname;
+
+ msg.type = mon_NetRpcConnect;
+ msg.data = (void*)&data;
+ msg.data_size = sizeof(data);
+ s->monitor_fn(&msg);
+ }
+
+ /* prepare to open a policy handle on lsa pipe */
+ s->lsa_pipe = s->ctx->lsa.pipe;
+
+ s->qos.len = 0;
+ s->qos.impersonation_level = 2;
+ s->qos.context_mode = 1;
+ s->qos.effective_only = 0;
+
+ s->attr.sec_qos = &s->qos;
+
+ s->lsa_open_policy.in.attr = &s->attr;
+ s->lsa_open_policy.in.system_name = talloc_asprintf(c, "\\");
+ if (composite_nomem(s->lsa_open_policy.in.system_name, c)) return;
+
+ s->lsa_open_policy.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ s->lsa_open_policy.out.handle = &s->lsa_handle;
+
+ open_pol_req = dcerpc_lsa_OpenPolicy2_send(s->lsa_pipe, c, &s->lsa_open_policy);
+ if (composite_nomem(open_pol_req, c)) return;
+
+ composite_continue_rpc(c, open_pol_req, continue_lsa_policy, c);
+}
+
+
+/*
+ Step 3 of RpcConnectDCInfo: Get policy handle and query lsa info
+ for kerberos realm (dns name) and guid. The query may fail.
+*/
+static void continue_lsa_policy(struct rpc_request *req)
+{
+ struct composite_context *c;
+ struct rpc_connect_dci_state *s;
+ struct rpc_request *query_info_req;
+
+ c = talloc_get_type(req->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct rpc_connect_dci_state);
+
+ c->status = dcerpc_ndr_request_recv(req);
+ if (!NT_STATUS_IS_OK(c->status)) {
+ composite_error(c, c->status);
+ return;
+ }
+
+ if (NT_STATUS_EQUAL(s->lsa_open_policy.out.result, NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED)) {
+ s->r.out.realm = NULL;
+ s->r.out.guid = NULL;
+ s->r.out.domain_name = NULL;
+ s->r.out.domain_sid = NULL;
+
+ /* Skip to the creating the actual connection, no info available on this transport */
+ continue_epm_map_binding_send(c);
+ return;
+
+ } else if (!NT_STATUS_IS_OK(s->lsa_open_policy.out.result)) {
+ composite_error(c, s->lsa_open_policy.out.result);
+ return;
+ }
+
+ /* post monitor message */
+ if (s->monitor_fn) {
+ struct monitor_msg msg;
+
+ msg.type = mon_LsaOpenPolicy;
+ msg.data = NULL;
+ msg.data_size = 0;
+ s->monitor_fn(&msg);
+ }
+
+ /* query lsa info for dns domain name and guid */
+ s->lsa_query_info2.in.handle = &s->lsa_handle;
+ s->lsa_query_info2.in.level = LSA_POLICY_INFO_DNS;
+ s->lsa_query_info2.out.info = talloc_zero(c, union lsa_PolicyInformation *);
+ if (composite_nomem(s->lsa_query_info2.out.info, c)) return;
+
+ query_info_req = dcerpc_lsa_QueryInfoPolicy2_send(s->lsa_pipe, c, &s->lsa_query_info2);
+ if (composite_nomem(query_info_req, c)) return;
+
+ composite_continue_rpc(c, query_info_req, continue_lsa_query_info2, c);
+}
+
+
+/*
+ Step 4 of RpcConnectDCInfo: Get realm and guid if provided (rpc call
+ may result in failure) and query lsa info for domain name and sid.
+*/
+static void continue_lsa_query_info2(struct rpc_request *req)
+{
+ struct composite_context *c;
+ struct rpc_connect_dci_state *s;
+ struct rpc_request *query_info_req;
+
+ c = talloc_get_type(req->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct rpc_connect_dci_state);
+
+ c->status = dcerpc_ndr_request_recv(req);
+
+ /* In case of error just null the realm and guid and proceed
+ to the next step. After all, it doesn't have to be AD domain
+ controller we talking to - NT-style PDC also counts */
+
+ if (NT_STATUS_EQUAL(c->status, NT_STATUS_NET_WRITE_FAULT)) {
+ s->r.out.realm = NULL;
+ s->r.out.guid = NULL;
+
+ } else {
+ if (!NT_STATUS_IS_OK(c->status)) {
+ s->r.out.error_string = talloc_asprintf(c,
+ "lsa_QueryInfoPolicy2 failed: %s",
+ nt_errstr(c->status));
+ composite_error(c, c->status);
+ return;
+ }
+
+ if (!NT_STATUS_IS_OK(s->lsa_query_info2.out.result)) {
+ s->r.out.error_string = talloc_asprintf(c,
+ "lsa_QueryInfoPolicy2 failed: %s",
+ nt_errstr(s->lsa_query_info2.out.result));
+ composite_error(c, s->lsa_query_info2.out.result);
+ return;
+ }
+
+ /* Copy the dns domain name and guid from the query result */
+
+ /* this should actually be a conversion from lsa_StringLarge */
+ s->r.out.realm = (*s->lsa_query_info2.out.info)->dns.dns_domain.string;
+ s->r.out.guid = talloc(c, struct GUID);
+ if (composite_nomem(s->r.out.guid, c)) {
+ s->r.out.error_string = NULL;
+ return;
+ }
+ *s->r.out.guid = (*s->lsa_query_info2.out.info)->dns.domain_guid;
+ }
+
+ /* post monitor message */
+ if (s->monitor_fn) {
+ struct monitor_msg msg;
+
+ msg.type = mon_LsaQueryPolicy;
+ msg.data = NULL;
+ msg.data_size = 0;
+ s->monitor_fn(&msg);
+ }
+
+ /* query lsa info for domain name and sid */
+ s->lsa_query_info.in.handle = &s->lsa_handle;
+ s->lsa_query_info.in.level = LSA_POLICY_INFO_DOMAIN;
+ s->lsa_query_info.out.info = talloc_zero(c, union lsa_PolicyInformation *);
+ if (composite_nomem(s->lsa_query_info.out.info, c)) return;
+
+ query_info_req = dcerpc_lsa_QueryInfoPolicy_send(s->lsa_pipe, c, &s->lsa_query_info);
+ if (composite_nomem(query_info_req, c)) return;
+
+ composite_continue_rpc(c, query_info_req, continue_lsa_query_info, c);
+}
+
+
+/*
+ Step 5 of RpcConnectDCInfo: Get domain name and sid
+*/
+static void continue_lsa_query_info(struct rpc_request *req)
+{
+ struct composite_context *c;
+ struct rpc_connect_dci_state *s;
+
+ c = talloc_get_type(req->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct rpc_connect_dci_state);
+
+ c->status = dcerpc_ndr_request_recv(req);
+ if (!NT_STATUS_IS_OK(c->status)) {
+ s->r.out.error_string = talloc_asprintf(c,
+ "lsa_QueryInfoPolicy failed: %s",
+ nt_errstr(c->status));
+ composite_error(c, c->status);
+ return;
+ }
+
+ /* post monitor message */
+ if (s->monitor_fn) {
+ struct monitor_msg msg;
+
+ msg.type = mon_LsaQueryPolicy;
+ msg.data = NULL;
+ msg.data_size = 0;
+ s->monitor_fn(&msg);
+ }
+
+ /* Copy the domain name and sid from the query result */
+ s->r.out.domain_sid = (*s->lsa_query_info.out.info)->domain.sid;
+ s->r.out.domain_name = (*s->lsa_query_info.out.info)->domain.name.string;
+
+ continue_epm_map_binding_send(c);
+}
+
+/*
+ Step 5 (continued) of RpcConnectDCInfo: request endpoint
+ map binding.
+
+ We may short-cut to this step if we don't support LSA OpenPolicy on this transport
+*/
+static void continue_epm_map_binding_send(struct composite_context *c)
+{
+ struct rpc_connect_dci_state *s;
+ struct composite_context *epm_map_req;
+ s = talloc_get_type(c->private_data, struct rpc_connect_dci_state);
+
+ /* prepare to get endpoint mapping for the requested interface */
+ s->final_binding = talloc(s, struct dcerpc_binding);
+ if (composite_nomem(s->final_binding, c)) return;
+
+ *s->final_binding = *s->lsa_pipe->binding;
+ /* Ensure we keep hold of the member elements */
+ if (composite_nomem(talloc_reference(s->final_binding, s->lsa_pipe->binding), c)) return;
+
+ epm_map_req = dcerpc_epm_map_binding_send(c, s->final_binding, s->r.in.dcerpc_iface,
+ s->lsa_pipe->conn->event_ctx, s->ctx->lp_ctx);
+ if (composite_nomem(epm_map_req, c)) return;
+
+ composite_continue(c, epm_map_req, continue_epm_map_binding, c);
+}
+
+/*
+ Step 6 of RpcConnectDCInfo: Receive endpoint mapping and create secondary
+ rpc connection derived from already used pipe but connected to the requested
+ one (as specified in libnet_RpcConnect structure)
+*/
+static void continue_epm_map_binding(struct composite_context *ctx)
+{
+ struct composite_context *c, *sec_conn_req;
+ struct rpc_connect_dci_state *s;
+
+ c = talloc_get_type(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct rpc_connect_dci_state);
+
+ c->status = dcerpc_epm_map_binding_recv(ctx);
+ if (!NT_STATUS_IS_OK(c->status)) {
+ s->r.out.error_string = talloc_asprintf(c,
+ "failed to map pipe with endpoint mapper - %s",
+ nt_errstr(c->status));
+ composite_error(c, c->status);
+ return;
+ }
+
+ /* create secondary connection derived from lsa pipe */
+ sec_conn_req = dcerpc_secondary_connection_send(s->lsa_pipe, s->final_binding);
+ if (composite_nomem(sec_conn_req, c)) return;
+
+ composite_continue(c, sec_conn_req, continue_secondary_conn, c);
+}
+
+
+/*
+ Step 7 of RpcConnectDCInfo: Get actual pipe to be returned
+ and complete this composite call
+*/
+static void continue_secondary_conn(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct rpc_connect_dci_state *s;
+
+ c = talloc_get_type(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct rpc_connect_dci_state);
+
+ c->status = dcerpc_secondary_connection_recv(ctx, &s->final_pipe);
+ if (!NT_STATUS_IS_OK(c->status)) {
+ s->r.out.error_string = talloc_asprintf(c,
+ "secondary connection failed: %s",
+ nt_errstr(c->status));
+
+ composite_error(c, c->status);
+ return;
+ }
+
+ s->r.out.dcerpc_pipe = s->final_pipe;
+
+ /* post monitor message */
+ if (s->monitor_fn) {
+ struct monitor_msg msg;
+ struct msg_net_rpc_connect data;
+ struct dcerpc_binding *binding = s->r.out.dcerpc_pipe->binding;
+
+ /* prepare monitor message and post it */
+ data.host = binding->host;
+ data.endpoint = binding->endpoint;
+ data.transport = binding->transport;
+ data.domain_name = binding->target_hostname;
+
+ msg.type = mon_NetRpcConnect;
+ msg.data = (void*)&data;
+ msg.data_size = sizeof(data);
+ s->monitor_fn(&msg);
+ }
+
+ composite_done(c);
+}
+
+
+/**
+ * Receives result of connection to rpc pipe and gets basic
+ * domain info (name, sid, realm, guid)
+ *
+ * @param c composite context
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param r data structure containing return values
+ * @return nt status of rpc connection
+ **/
+
+static NTSTATUS libnet_RpcConnectDCInfo_recv(struct composite_context *c, struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx, struct libnet_RpcConnect *r)
+{
+ NTSTATUS status;
+ struct rpc_connect_dci_state *s = talloc_get_type(c->private_data,
+ struct rpc_connect_dci_state);
+
+ status = composite_wait(c);
+ if (NT_STATUS_IS_OK(status)) {
+ r->out.realm = talloc_steal(mem_ctx, s->r.out.realm);
+ r->out.guid = talloc_steal(mem_ctx, s->r.out.guid);
+ r->out.domain_name = talloc_steal(mem_ctx, s->r.out.domain_name);
+ r->out.domain_sid = talloc_steal(mem_ctx, s->r.out.domain_sid);
+
+ r->out.dcerpc_pipe = talloc_steal(mem_ctx, s->r.out.dcerpc_pipe);
+
+ /* reference created pipe structure to long-term libnet_context
+ so that it can be used by other api functions even after short-term
+ mem_ctx is freed */
+ if (r->in.dcerpc_iface == &ndr_table_samr) {
+ ctx->samr.pipe = talloc_reference(ctx, r->out.dcerpc_pipe);
+
+ } else if (r->in.dcerpc_iface == &ndr_table_lsarpc) {
+ ctx->lsa.pipe = talloc_reference(ctx, r->out.dcerpc_pipe);
+ }
+
+ } else {
+ if (s->r.out.error_string) {
+ r->out.error_string = talloc_steal(mem_ctx, s->r.out.error_string);
+ } else if (r->in.binding == NULL) {
+ r->out.error_string = talloc_asprintf(mem_ctx, "Connection to DC failed: %s", nt_errstr(status));
+ } else {
+ r->out.error_string = talloc_asprintf(mem_ctx, "Connection to DC %s failed: %s",
+ r->in.binding, nt_errstr(status));
+ }
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+
+/**
+ * Initiates connection to rpc pipe on remote server or pdc, optionally
+ * providing domain info
+ *
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param r data structure containing necessary parameters and return values
+ * @return composite context of this call
+ **/
+
+struct composite_context* libnet_RpcConnect_send(struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_RpcConnect *r,
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *c;
+
+ switch (r->level) {
+ case LIBNET_RPC_CONNECT_SERVER:
+ case LIBNET_RPC_CONNECT_SERVER_ADDRESS:
+ case LIBNET_RPC_CONNECT_BINDING:
+ c = libnet_RpcConnectSrv_send(ctx, mem_ctx, r, monitor);
+ break;
+
+ case LIBNET_RPC_CONNECT_PDC:
+ case LIBNET_RPC_CONNECT_DC:
+ c = libnet_RpcConnectDC_send(ctx, mem_ctx, r, monitor);
+ break;
+
+ case LIBNET_RPC_CONNECT_DC_INFO:
+ c = libnet_RpcConnectDCInfo_send(ctx, mem_ctx, r, monitor);
+ break;
+
+ default:
+ c = talloc_zero(mem_ctx, struct composite_context);
+ composite_error(c, NT_STATUS_INVALID_LEVEL);
+ }
+
+ return c;
+}
+
+
+/**
+ * Receives result of connection to rpc pipe on remote server or pdc
+ *
+ * @param c composite context
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param r data structure containing necessary parameters and return values
+ * @return nt status of rpc connection
+ **/
+
+NTSTATUS libnet_RpcConnect_recv(struct composite_context *c, struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx, struct libnet_RpcConnect *r)
+{
+ switch (r->level) {
+ case LIBNET_RPC_CONNECT_SERVER:
+ case LIBNET_RPC_CONNECT_BINDING:
+ return libnet_RpcConnectSrv_recv(c, ctx, mem_ctx, r);
+
+ case LIBNET_RPC_CONNECT_PDC:
+ case LIBNET_RPC_CONNECT_DC:
+ return libnet_RpcConnectDC_recv(c, ctx, mem_ctx, r);
+
+ case LIBNET_RPC_CONNECT_DC_INFO:
+ return libnet_RpcConnectDCInfo_recv(c, ctx, mem_ctx, r);
+
+ default:
+ ZERO_STRUCT(r->out);
+ return NT_STATUS_INVALID_LEVEL;
+ }
+}
+
+
+/**
+ * Connect to a rpc pipe on a remote server - sync version
+ *
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param r data structure containing necessary parameters and return values
+ * @return nt status of rpc connection
+ **/
+
+NTSTATUS libnet_RpcConnect(struct libnet_context *ctx, TALLOC_CTX *mem_ctx,
+ struct libnet_RpcConnect *r)
+{
+ struct composite_context *c;
+
+ c = libnet_RpcConnect_send(ctx, mem_ctx, r, NULL);
+ return libnet_RpcConnect_recv(c, ctx, mem_ctx, r);
+}
diff --git a/source4/libnet/libnet_rpc.h b/source4/libnet/libnet_rpc.h
new file mode 100644
index 0000000000..b3e1620c75
--- /dev/null
+++ b/source4/libnet/libnet_rpc.h
@@ -0,0 +1,72 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Stefan Metzmacher 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 <http://www.gnu.org/licenses/>.
+*/
+
+
+#include "librpc/rpc/dcerpc.h"
+
+/*
+ * struct definition for connecting to a dcerpc inferface
+ */
+
+enum libnet_RpcConnect_level {
+ LIBNET_RPC_CONNECT_SERVER, /* connect to a standalone rpc server */
+ LIBNET_RPC_CONNECT_SERVER_ADDRESS, /* connect to a standalone rpc server,
+ knowing both name and address */
+ LIBNET_RPC_CONNECT_PDC, /* connect to a domain pdc (resolves domain
+ name to a pdc address before connecting) */
+ LIBNET_RPC_CONNECT_DC, /* connect to any DC (resolves domain
+ name to a DC address before connecting) */
+ LIBNET_RPC_CONNECT_BINDING, /* specified binding string */
+ LIBNET_RPC_CONNECT_DC_INFO /* connect to a DC and provide basic domain
+ information (name, realm, sid, guid) */
+};
+
+struct libnet_RpcConnect {
+ enum libnet_RpcConnect_level level;
+
+ struct {
+ const char *name;
+ const char *address;
+ const char *binding;
+ const struct ndr_interface_table *dcerpc_iface;
+ } in;
+ struct {
+ struct dcerpc_pipe *dcerpc_pipe;
+
+ /* parameters provided in LIBNET_RPC_CONNECT_DC_INFO level, null otherwise */
+ const char *domain_name;
+ struct dom_sid *domain_sid;
+ const char *realm; /* these parameters are only present if */
+ struct GUID *guid; /* the remote server is known to be AD */
+
+ const char *error_string;
+ } out;
+};
+
+
+/*
+ * Monitor messages sent from libnet_rpc.c functions
+ */
+
+struct msg_net_rpc_connect {
+ const char *host;
+ const char *domain_name;
+ const char *endpoint;
+ enum dcerpc_transport_t transport;
+};
diff --git a/source4/libnet/libnet_samdump.c b/source4/libnet/libnet_samdump.c
new file mode 100644
index 0000000000..10a14655f3
--- /dev/null
+++ b/source4/libnet/libnet_samdump.c
@@ -0,0 +1,205 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Extract the user/system database from a remote SamSync server
+
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+
+#include "includes.h"
+#include "libnet/libnet.h"
+#include "../lib/util/dlinklist.h"
+#include "samba3/samba3.h"
+#include "libcli/security/security.h"
+
+
+struct samdump_secret {
+ struct samdump_secret *prev, *next;
+ DATA_BLOB secret;
+ char *name;
+ NTTIME mtime;
+};
+
+struct samdump_trusted_domain {
+ struct samdump_trusted_domain *prev, *next;
+ struct dom_sid *sid;
+ char *name;
+};
+
+struct samdump_state {
+ struct samdump_secret *secrets;
+ struct samdump_trusted_domain *trusted_domains;
+};
+
+static NTSTATUS vampire_samdump_handle_user(TALLOC_CTX *mem_ctx,
+ struct netr_DELTA_ENUM *delta)
+{
+ uint32_t rid = delta->delta_id_union.rid;
+ struct netr_DELTA_USER *user = delta->delta_union.user;
+ const char *username = user->account_name.string;
+ char *hex_lm_password;
+ char *hex_nt_password;
+
+ hex_lm_password = smbpasswd_sethexpwd(mem_ctx,
+ user->lm_password_present ? &user->lmpassword : NULL,
+ user->acct_flags);
+ hex_nt_password = smbpasswd_sethexpwd(mem_ctx,
+ user->nt_password_present ? &user->ntpassword : NULL,
+ user->acct_flags);
+
+ printf("%s:%d:%s:%s:%s:LCT-%08X\n", username,
+ rid, hex_lm_password, hex_nt_password,
+ smbpasswd_encode_acb_info(mem_ctx, user->acct_flags),
+ (unsigned int)nt_time_to_unix(user->last_password_change));
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS vampire_samdump_handle_secret(TALLOC_CTX *mem_ctx,
+ struct samdump_state *samdump_state,
+ struct netr_DELTA_ENUM *delta)
+{
+ struct netr_DELTA_SECRET *secret = delta->delta_union.secret;
+ const char *name = delta->delta_id_union.name;
+ struct samdump_secret *n = talloc(samdump_state, struct samdump_secret);
+
+ n->name = talloc_strdup(n, name);
+ n->secret = data_blob_talloc(n, secret->current_cipher.cipher_data, secret->current_cipher.maxlen);
+ n->mtime = secret->current_cipher_set_time;
+
+ DLIST_ADD(samdump_state->secrets, n);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS vampire_samdump_handle_trusted_domain(TALLOC_CTX *mem_ctx,
+ struct samdump_state *samdump_state,
+ struct netr_DELTA_ENUM *delta)
+{
+ struct netr_DELTA_TRUSTED_DOMAIN *trusted_domain = delta->delta_union.trusted_domain;
+ struct dom_sid *dom_sid = delta->delta_id_union.sid;
+
+ struct samdump_trusted_domain *n = talloc(samdump_state, struct samdump_trusted_domain);
+
+ n->name = talloc_strdup(n, trusted_domain->domain_name.string);
+ n->sid = talloc_steal(n, dom_sid);
+
+ DLIST_ADD(samdump_state->trusted_domains, n);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS libnet_samdump_fn(TALLOC_CTX *mem_ctx,
+ void *private_data,
+ enum netr_SamDatabaseID database,
+ struct netr_DELTA_ENUM *delta,
+ char **error_string)
+{
+ NTSTATUS nt_status = NT_STATUS_OK;
+ struct samdump_state *samdump_state = (struct samdump_state *)private_data;
+
+ *error_string = NULL;
+ switch (delta->delta_type) {
+ case NETR_DELTA_USER:
+ {
+ /* not interested in builtin users */
+ if (database == SAM_DATABASE_DOMAIN) {
+ nt_status = vampire_samdump_handle_user(mem_ctx,
+ delta);
+ }
+ break;
+ }
+ case NETR_DELTA_SECRET:
+ {
+ nt_status = vampire_samdump_handle_secret(mem_ctx,
+ samdump_state,
+ delta);
+ break;
+ }
+ case NETR_DELTA_TRUSTED_DOMAIN:
+ {
+ nt_status = vampire_samdump_handle_trusted_domain(mem_ctx,
+ samdump_state,
+ delta);
+ break;
+ }
+ default:
+ /* Can't dump them all right now */
+ break;
+ }
+ return nt_status;
+}
+
+NTSTATUS libnet_SamDump(struct libnet_context *ctx, TALLOC_CTX *mem_ctx,
+ struct libnet_SamDump *r)
+{
+ NTSTATUS nt_status;
+ struct libnet_SamSync r2;
+ struct samdump_state *samdump_state = talloc(mem_ctx, struct samdump_state);
+
+ struct samdump_trusted_domain *t;
+ struct samdump_secret *s;
+
+ if (!samdump_state) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ samdump_state->secrets = NULL;
+ samdump_state->trusted_domains = NULL;
+
+ r2.out.error_string = NULL;
+ r2.in.binding_string = r->in.binding_string;
+ r2.in.rid_crypt = lp_parm_bool(ctx->lp_ctx, NULL, "vampire", "rid decrypt", true);
+ r2.in.init_fn = NULL;
+ r2.in.delta_fn = libnet_samdump_fn;
+ r2.in.fn_ctx = samdump_state;
+ r2.in.machine_account = r->in.machine_account;
+ nt_status = libnet_SamSync_netlogon(ctx, samdump_state, &r2);
+ r->out.error_string = r2.out.error_string;
+ talloc_steal(mem_ctx, r->out.error_string);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(samdump_state);
+ return nt_status;
+ }
+
+ printf("Trusted domains, sids and secrets:\n");
+ for (t=samdump_state->trusted_domains; t; t=t->next) {
+ char *secret_name = talloc_asprintf(mem_ctx, "G$$%s", t->name);
+ for (s=samdump_state->secrets; s; s=s->next) {
+ char *secret_string;
+ if (strcasecmp_m(s->name, secret_name) != 0) {
+ continue;
+ }
+ if (!convert_string_talloc_convenience(mem_ctx, lp_iconv_convenience(ctx->lp_ctx), CH_UTF16, CH_UNIX,
+ s->secret.data, s->secret.length,
+ (void **)&secret_string, NULL, false)) {
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "Could not convert secret for domain %s to a string",
+ t->name);
+ talloc_free(samdump_state);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ printf("%s\t%s\t%s\n",
+ t->name, dom_sid_string(mem_ctx, t->sid),
+ secret_string);
+ }
+ }
+ talloc_free(samdump_state);
+ return nt_status;
+}
+
diff --git a/source4/libnet/libnet_samdump_keytab.c b/source4/libnet/libnet_samdump_keytab.c
new file mode 100644
index 0000000000..a1846b81da
--- /dev/null
+++ b/source4/libnet/libnet_samdump_keytab.c
@@ -0,0 +1,132 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Extract kerberos keys from a remote SamSync server
+
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+
+#include "includes.h"
+#include "libnet/libnet.h"
+#include "system/kerberos.h"
+#include "auth/credentials/credentials.h"
+#include "auth/credentials/credentials_krb5.h"
+#include "param/param.h"
+#include "lib/events/events.h"
+
+static NTSTATUS samdump_keytab_handle_user(TALLOC_CTX *mem_ctx,
+ struct tevent_context *event_ctx,
+ struct loadparm_context *lp_ctx,
+ const char *keytab_name,
+ struct netr_DELTA_ENUM *delta)
+{
+ struct netr_DELTA_USER *user = delta->delta_union.user;
+ const char *username = user->account_name.string;
+ struct cli_credentials *credentials;
+ int ret;
+
+ if (!user->nt_password_present) {
+ /* We can't do anything here */
+ return NT_STATUS_OK;
+ }
+
+ credentials = cli_credentials_init(mem_ctx);
+ if (!credentials) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ cli_credentials_set_conf(credentials, lp_ctx);
+ cli_credentials_set_username(credentials, username, CRED_SPECIFIED);
+
+ /* We really should consult ldap in the main SamSync code, and
+ * pass a value in here */
+ cli_credentials_set_kvno(credentials, 0);
+ cli_credentials_set_nt_hash(credentials, &user->ntpassword, CRED_SPECIFIED);
+ ret = cli_credentials_set_keytab_name(credentials, event_ctx, lp_ctx, keytab_name, CRED_SPECIFIED);
+ if (ret) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ ret = cli_credentials_update_keytab(credentials, event_ctx, lp_ctx);
+ if (ret) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return NT_STATUS_OK;
+}
+
+struct libnet_samdump_keytab_data {
+ const char *keytab_name;
+ struct tevent_context *ev_ctx;
+ struct loadparm_context *lp_ctx;
+};
+
+static NTSTATUS libnet_samdump_keytab_fn(TALLOC_CTX *mem_ctx,
+ void *private_data,
+ enum netr_SamDatabaseID database,
+ struct netr_DELTA_ENUM *delta,
+ char **error_string)
+{
+ NTSTATUS nt_status = NT_STATUS_OK;
+ struct libnet_samdump_keytab_data *data = private_data;
+ *error_string = NULL;
+ switch (delta->delta_type) {
+ case NETR_DELTA_USER:
+ {
+ /* not interested in builtin users */
+ if (database == SAM_DATABASE_DOMAIN) {
+ nt_status = samdump_keytab_handle_user(mem_ctx,
+ data->ev_ctx,
+ data->lp_ctx,
+ data->keytab_name,
+ delta);
+ break;
+ }
+ }
+ default:
+ /* Can't dump them all right now */
+ break;
+ }
+ return nt_status;
+}
+
+NTSTATUS libnet_SamDump_keytab(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_SamDump_keytab *r)
+{
+ NTSTATUS nt_status;
+ struct libnet_samdump_keytab_data data;
+ struct libnet_SamSync r2;
+
+ data.keytab_name = r->in.keytab_name;
+ data.ev_ctx = ctx->event_ctx;
+ data.lp_ctx = ctx->lp_ctx;
+
+ r2.out.error_string = NULL;
+ r2.in.binding_string = r->in.binding_string;
+ r2.in.rid_crypt = true;
+ r2.in.init_fn = NULL;
+ r2.in.delta_fn = libnet_samdump_keytab_fn;
+ r2.in.fn_ctx = &data;
+ r2.in.machine_account = r->in.machine_account;
+ nt_status = libnet_SamSync_netlogon(ctx, mem_ctx, &r2);
+ r->out.error_string = r2.out.error_string;
+ talloc_steal(mem_ctx, r->out.error_string);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ return nt_status;
+}
diff --git a/source4/libnet/libnet_samsync.c b/source4/libnet/libnet_samsync.c
new file mode 100644
index 0000000000..51e49e94a0
--- /dev/null
+++ b/source4/libnet/libnet_samsync.c
@@ -0,0 +1,412 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Extract the user/system database from a remote SamSync server
+
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+
+#include "includes.h"
+#include "libnet/libnet.h"
+#include "libcli/auth/libcli_auth.h"
+#include "auth/gensec/gensec.h"
+#include "auth/credentials/credentials.h"
+#include "auth/gensec/schannel_proto.h"
+#include "librpc/gen_ndr/ndr_netlogon.h"
+#include "librpc/gen_ndr/ndr_netlogon_c.h"
+#include "param/param.h"
+
+
+/**
+ * Decrypt and extract the user's passwords.
+ *
+ * The writes decrypted (no longer 'RID encrypted' or arcfour encrypted) passwords back into the structure
+ */
+static NTSTATUS fix_user(TALLOC_CTX *mem_ctx,
+ struct creds_CredentialState *creds,
+ bool rid_crypt,
+ enum netr_SamDatabaseID database,
+ struct netr_DELTA_ENUM *delta,
+ char **error_string)
+{
+
+ uint32_t rid = delta->delta_id_union.rid;
+ struct netr_DELTA_USER *user = delta->delta_union.user;
+ struct samr_Password lm_hash;
+ struct samr_Password nt_hash;
+ const char *username = user->account_name.string;
+
+ if (rid_crypt) {
+ if (user->lm_password_present) {
+ sam_rid_crypt(rid, user->lmpassword.hash, lm_hash.hash, 0);
+ user->lmpassword = lm_hash;
+ }
+
+ if (user->nt_password_present) {
+ sam_rid_crypt(rid, user->ntpassword.hash, nt_hash.hash, 0);
+ user->ntpassword = 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;
+ creds_arcfour_crypt(creds, data.data, data.length);
+ user->user_private_info.SensitiveData = data.data;
+ user->user_private_info.DataLength = data.length;
+
+ ndr_err = ndr_pull_struct_blob(&data, mem_ctx, NULL, &keys, (ndr_pull_flags_fn_t)ndr_pull_netr_USER_KEYS);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ *error_string = talloc_asprintf(mem_ctx, "Failed to parse Sensitive Data for %s:", username);
+ dump_data(10, data.data, data.length);
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ if (keys.keys.keys2.lmpassword.length == 16) {
+ if (rid_crypt) {
+ sam_rid_crypt(rid, keys.keys.keys2.lmpassword.pwd.hash, lm_hash.hash, 0);
+ user->lmpassword = lm_hash;
+ } else {
+ user->lmpassword = keys.keys.keys2.lmpassword.pwd;
+ }
+ user->lm_password_present = true;
+ }
+ if (keys.keys.keys2.ntpassword.length == 16) {
+ if (rid_crypt) {
+ sam_rid_crypt(rid, keys.keys.keys2.ntpassword.pwd.hash, nt_hash.hash, 0);
+ user->ntpassword = nt_hash;
+ } else {
+ user->ntpassword = keys.keys.keys2.ntpassword.pwd;
+ }
+ user->nt_password_present = true;
+ }
+ /* TODO: rid decrypt history fields */
+ }
+ return NT_STATUS_OK;
+}
+
+/**
+ * Decrypt and extract the secrets
+ *
+ * The writes decrypted secrets back into the structure
+ */
+static NTSTATUS fix_secret(TALLOC_CTX *mem_ctx,
+ struct creds_CredentialState *creds,
+ enum netr_SamDatabaseID database,
+ struct netr_DELTA_ENUM *delta,
+ char **error_string)
+{
+ struct netr_DELTA_SECRET *secret = delta->delta_union.secret;
+ creds_arcfour_crypt(creds, secret->current_cipher.cipher_data,
+ secret->current_cipher.maxlen);
+
+ creds_arcfour_crypt(creds, secret->old_cipher.cipher_data,
+ secret->old_cipher.maxlen);
+
+ return NT_STATUS_OK;
+}
+
+/**
+ * Fix up the delta, dealing with encryption issues so that the final
+ * callback need only do the printing or application logic
+ */
+
+static NTSTATUS fix_delta(TALLOC_CTX *mem_ctx,
+ struct creds_CredentialState *creds,
+ bool rid_crypt,
+ enum netr_SamDatabaseID database,
+ struct netr_DELTA_ENUM *delta,
+ char **error_string)
+{
+ NTSTATUS nt_status = NT_STATUS_OK;
+ *error_string = NULL;
+ switch (delta->delta_type) {
+ case NETR_DELTA_USER:
+ {
+ nt_status = fix_user(mem_ctx,
+ creds,
+ rid_crypt,
+ database,
+ delta,
+ error_string);
+ break;
+ }
+ case NETR_DELTA_SECRET:
+ {
+ nt_status = fix_secret(mem_ctx,
+ creds,
+ database,
+ delta,
+ error_string);
+ break;
+ }
+ default:
+ break;
+ }
+ return nt_status;
+}
+
+NTSTATUS libnet_SamSync_netlogon(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_SamSync *r)
+{
+ NTSTATUS nt_status, dbsync_nt_status;
+ TALLOC_CTX *samsync_ctx, *loop_ctx, *delta_ctx;
+ struct creds_CredentialState *creds;
+ struct netr_DatabaseSync dbsync;
+ struct netr_Authenticator credential, return_authenticator;
+ struct netr_DELTA_ENUM_ARRAY *delta_enum_array = NULL;
+ struct cli_credentials *machine_account;
+ struct dcerpc_pipe *p;
+ struct libnet_context *machine_net_ctx;
+ struct libnet_RpcConnect *c;
+ struct libnet_SamSync_state *state;
+ const enum netr_SamDatabaseID database_ids[] = {SAM_DATABASE_DOMAIN, SAM_DATABASE_BUILTIN, SAM_DATABASE_PRIVS};
+ int i;
+
+ samsync_ctx = talloc_named(mem_ctx, 0, "SamSync top context");
+
+ if (!r->in.machine_account) {
+ machine_account = cli_credentials_init(samsync_ctx);
+ if (!machine_account) {
+ talloc_free(samsync_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ cli_credentials_set_conf(machine_account, ctx->lp_ctx);
+ nt_status = cli_credentials_set_machine_account(machine_account, ctx->lp_ctx);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ r->out.error_string = talloc_strdup(mem_ctx, "Could not obtain machine account password - are we joined to the domain?");
+ talloc_free(samsync_ctx);
+ return nt_status;
+ }
+ } else {
+ machine_account = r->in.machine_account;
+ }
+
+ /* We cannot do this unless we are a BDC. Check, before we get odd errors later */
+ if (cli_credentials_get_secure_channel_type(machine_account) != SEC_CHAN_BDC) {
+ r->out.error_string
+ = talloc_asprintf(mem_ctx,
+ "Our join to domain %s is not as a BDC (%d), please rejoin as a BDC",
+ cli_credentials_get_domain(machine_account),
+ cli_credentials_get_secure_channel_type(machine_account));
+ talloc_free(samsync_ctx);
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ }
+
+ c = talloc(samsync_ctx, struct libnet_RpcConnect);
+ if (!c) {
+ r->out.error_string = NULL;
+ talloc_free(samsync_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ c->level = LIBNET_RPC_CONNECT_DC_INFO;
+ if (r->in.binding_string) {
+ c->in.binding = r->in.binding_string;
+ c->in.name = NULL;
+ } else {
+ c->in.binding = NULL;
+ c->in.name = cli_credentials_get_domain(machine_account);
+ }
+
+ /* prepare connect to the NETLOGON pipe of PDC */
+ c->in.dcerpc_iface = &ndr_table_netlogon;
+
+ /* We must do this as the machine, not as any command-line
+ * user. So we override the credentials in the
+ * libnet_context */
+ machine_net_ctx = talloc(samsync_ctx, struct libnet_context);
+ if (!machine_net_ctx) {
+ r->out.error_string = NULL;
+ talloc_free(samsync_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ *machine_net_ctx = *ctx;
+ machine_net_ctx->cred = machine_account;
+
+ /* connect to the NETLOGON pipe of the PDC */
+ nt_status = libnet_RpcConnect(machine_net_ctx, samsync_ctx, c);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ if (r->in.binding_string) {
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "Connection to NETLOGON pipe of DC %s failed: %s",
+ r->in.binding_string, c->out.error_string);
+ } else {
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "Connection to NETLOGON pipe of DC for %s failed: %s",
+ c->in.name, c->out.error_string);
+ }
+ talloc_free(samsync_ctx);
+ return nt_status;
+ }
+
+ /* This makes a new pipe, on which we can do schannel. We
+ * should do this in the RpcConnect code, but the abstaction
+ * layers do not suit yet */
+
+ nt_status = dcerpc_secondary_connection(c->out.dcerpc_pipe, &p,
+ c->out.dcerpc_pipe->binding);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "Secondary connection to NETLOGON pipe of DC %s failed: %s",
+ dcerpc_server_name(p), nt_errstr(nt_status));
+ talloc_free(samsync_ctx);
+ return nt_status;
+ }
+
+ nt_status = dcerpc_bind_auth_schannel(samsync_ctx, p, &ndr_table_netlogon,
+ machine_account, ctx->lp_ctx, DCERPC_AUTH_LEVEL_PRIVACY);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "SCHANNEL authentication to NETLOGON pipe of DC %s failed: %s",
+ dcerpc_server_name(p), nt_errstr(nt_status));
+ talloc_free(samsync_ctx);
+ return nt_status;
+ }
+
+ state = talloc(samsync_ctx, struct libnet_SamSync_state);
+ if (!state) {
+ r->out.error_string = NULL;
+ talloc_free(samsync_ctx);
+ return nt_status;
+ }
+
+ state->domain_name = c->out.domain_name;
+ state->domain_sid = c->out.domain_sid;
+ state->realm = c->out.realm;
+ state->domain_guid = c->out.guid;
+ state->machine_net_ctx = machine_net_ctx;
+ state->netlogon_pipe = p;
+
+ /* initialise the callback layer. It may wish to contact the
+ * server with ldap, now we know the name */
+
+ if (r->in.init_fn) {
+ char *error_string;
+ nt_status = r->in.init_fn(samsync_ctx,
+ r->in.fn_ctx,
+ state,
+ &error_string);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ r->out.error_string = talloc_steal(mem_ctx, error_string);
+ talloc_free(samsync_ctx);
+ return nt_status;
+ }
+ }
+
+ /* get NETLOGON credentials */
+
+ nt_status = dcerpc_schannel_creds(p->conn->security_state.generic_state, samsync_ctx, &creds);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ r->out.error_string = talloc_strdup(mem_ctx, "Could not obtain NETLOGON credentials from DCERPC/GENSEC layer");
+ talloc_free(samsync_ctx);
+ return nt_status;
+ }
+
+ /* Setup details for the synchronisation */
+
+ ZERO_STRUCT(return_authenticator);
+
+ dbsync.in.logon_server = talloc_asprintf(samsync_ctx, "\\\\%s", dcerpc_server_name(p));
+ dbsync.in.computername = cli_credentials_get_workstation(machine_account);
+ dbsync.in.preferredmaximumlength = (uint32_t)-1;
+ dbsync.in.return_authenticator = &return_authenticator;
+ dbsync.out.return_authenticator = &return_authenticator;
+ dbsync.out.delta_enum_array = &delta_enum_array;
+
+ for (i=0;i< ARRAY_SIZE(database_ids); i++) {
+
+ uint32_t sync_context = 0;
+
+ dbsync.in.database_id = database_ids[i];
+ dbsync.in.sync_context = &sync_context;
+ dbsync.out.sync_context = &sync_context;
+
+ do {
+ int d;
+ loop_ctx = talloc_named(samsync_ctx, 0, "DatabaseSync loop context");
+ creds_client_authenticator(creds, &credential);
+
+ dbsync.in.credential = &credential;
+
+ dbsync_nt_status = dcerpc_netr_DatabaseSync(p, loop_ctx, &dbsync);
+ if (!NT_STATUS_IS_OK(dbsync_nt_status) &&
+ !NT_STATUS_EQUAL(dbsync_nt_status, STATUS_MORE_ENTRIES)) {
+ r->out.error_string = talloc_asprintf(mem_ctx, "DatabaseSync failed - %s", nt_errstr(nt_status));
+ talloc_free(samsync_ctx);
+ return nt_status;
+ }
+
+ if (!creds_client_check(creds, &dbsync.out.return_authenticator->cred)) {
+ r->out.error_string = talloc_strdup(mem_ctx, "Credential chaining on incoming DatabaseSync failed");
+ talloc_free(samsync_ctx);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ dbsync.in.sync_context = dbsync.out.sync_context;
+
+ /* For every single remote 'delta' entry: */
+ for (d=0; d < delta_enum_array->num_deltas; d++) {
+ char *error_string = NULL;
+ delta_ctx = talloc_named(loop_ctx, 0, "DatabaseSync delta context");
+ /* 'Fix' elements, by decrypting and
+ * de-obfuscating the data */
+ nt_status = fix_delta(delta_ctx,
+ creds,
+ r->in.rid_crypt,
+ dbsync.in.database_id,
+ &delta_enum_array->delta_enum[d],
+ &error_string);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ r->out.error_string = talloc_steal(mem_ctx, error_string);
+ talloc_free(samsync_ctx);
+ return nt_status;
+ }
+
+ /* Now call the callback. This will
+ * do something like print the data or
+ * write to an ldb */
+ nt_status = r->in.delta_fn(delta_ctx,
+ r->in.fn_ctx,
+ dbsync.in.database_id,
+ &delta_enum_array->delta_enum[d],
+ &error_string);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ r->out.error_string = talloc_steal(mem_ctx, error_string);
+ talloc_free(samsync_ctx);
+ return nt_status;
+ }
+ talloc_free(delta_ctx);
+ }
+ talloc_free(loop_ctx);
+ } while (NT_STATUS_EQUAL(dbsync_nt_status, STATUS_MORE_ENTRIES));
+
+ if (!NT_STATUS_IS_OK(dbsync_nt_status)) {
+ r->out.error_string = talloc_asprintf(mem_ctx, "libnet_SamSync_netlogon failed: unexpected inconsistancy. Should not get error %s here", nt_errstr(nt_status));
+ talloc_free(samsync_ctx);
+ return dbsync_nt_status;
+ }
+ nt_status = NT_STATUS_OK;
+ }
+ talloc_free(samsync_ctx);
+ return nt_status;
+}
+
diff --git a/source4/libnet/libnet_samsync.h b/source4/libnet/libnet_samsync.h
new file mode 100644
index 0000000000..c2295f3957
--- /dev/null
+++ b/source4/libnet/libnet_samsync.h
@@ -0,0 +1,84 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "librpc/gen_ndr/netlogon.h"
+
+struct libnet_SamSync_state {
+ struct libnet_context *machine_net_ctx;
+ struct dcerpc_pipe *netlogon_pipe;
+ const char *domain_name;
+ const struct dom_sid *domain_sid;
+ const char *realm;
+ struct GUID *domain_guid;
+};
+
+/* struct and enum for doing a remote domain vampire dump */
+struct libnet_SamSync {
+ struct {
+ const char *binding_string;
+ bool rid_crypt;
+ NTSTATUS (*init_fn)(TALLOC_CTX *mem_ctx,
+ void *private_data,
+ struct libnet_SamSync_state *samsync_state,
+ char **error_string);
+ NTSTATUS (*delta_fn)(TALLOC_CTX *mem_ctx,
+ void *private_data,
+ enum netr_SamDatabaseID database,
+ struct netr_DELTA_ENUM *delta,
+ char **error_string);
+ void *fn_ctx;
+ struct cli_credentials *machine_account;
+ } in;
+ struct {
+ const char *error_string;
+ } out;
+};
+
+struct libnet_SamDump {
+ struct {
+ const char *binding_string;
+ struct cli_credentials *machine_account;
+ } in;
+ struct {
+ const char *error_string;
+ } out;
+};
+
+struct libnet_SamDump_keytab {
+ struct {
+ const char *binding_string;
+ const char *keytab_name;
+ struct cli_credentials *machine_account;
+ } in;
+ struct {
+ const char *error_string;
+ } out;
+};
+
+struct libnet_samsync_ldb {
+ struct {
+ const char *binding_string;
+ struct cli_credentials *machine_account;
+ struct auth_session_info *session_info;
+ } in;
+ struct {
+ const char *error_string;
+ } out;
+};
+
diff --git a/source4/libnet/libnet_samsync_ldb.c b/source4/libnet/libnet_samsync_ldb.c
new file mode 100644
index 0000000000..e24c54a8c2
--- /dev/null
+++ b/source4/libnet/libnet_samsync_ldb.c
@@ -0,0 +1,1251 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Extract the user/system database from a remote SamSync server
+
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
+ Copyright (C) Andrew Tridgell 2004
+ Copyright (C) Volker Lendecke 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 <http://www.gnu.org/licenses/>.
+*/
+
+
+#include "includes.h"
+#include "libnet/libnet.h"
+#include "libcli/ldap/ldap_ndr.h"
+#include "dsdb/samdb/samdb.h"
+#include "auth/auth.h"
+#include "../lib/util/util_ldb.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "ldb_wrap.h"
+#include "libcli/security/security.h"
+#include "librpc/rpc/dcerpc.h"
+#include "param/param.h"
+
+struct samsync_ldb_secret {
+ struct samsync_ldb_secret *prev, *next;
+ DATA_BLOB secret;
+ char *name;
+ NTTIME mtime;
+};
+
+struct samsync_ldb_trusted_domain {
+ struct samsync_ldb_trusted_domain *prev, *next;
+ struct dom_sid *sid;
+ char *name;
+};
+
+struct samsync_ldb_state {
+ /* Values from the LSA lookup */
+ const struct libnet_SamSync_state *samsync_state;
+
+ struct dom_sid *dom_sid[3];
+ struct ldb_context *sam_ldb, *remote_ldb;
+ struct ldb_dn *base_dn[3];
+ struct samsync_ldb_secret *secrets;
+ struct samsync_ldb_trusted_domain *trusted_domains;
+};
+
+static NTSTATUS samsync_ldb_add_foreignSecurityPrincipal(TALLOC_CTX *mem_ctx,
+ struct samsync_ldb_state *state,
+ struct dom_sid *sid,
+ struct ldb_dn **fsp_dn,
+ char **error_string)
+{
+ const char *sidstr = dom_sid_string(mem_ctx, sid);
+ /* We assume that ForeignSecurityPrincipals are under the BASEDN of the main domain */
+ struct ldb_dn *basedn = samdb_search_dn(state->sam_ldb, mem_ctx,
+ state->base_dn[SAM_DATABASE_DOMAIN],
+ "(&(objectClass=container)(cn=ForeignSecurityPrincipals))");
+ struct ldb_message *msg;
+ int ret;
+
+ if (!sidstr) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (basedn == NULL) {
+ *error_string = talloc_asprintf(mem_ctx,
+ "Failed to find DN for "
+ "ForeignSecurityPrincipal container under %s",
+ ldb_dn_get_linearized(state->base_dn[SAM_DATABASE_DOMAIN]));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* add core elements to the ldb_message for the alias */
+ msg->dn = basedn;
+ if ( ! ldb_dn_add_child_fmt(msg->dn, "CN=%s", sidstr))
+ return NT_STATUS_UNSUCCESSFUL;
+
+ samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
+ "objectClass",
+ "foreignSecurityPrincipal");
+
+ *fsp_dn = msg->dn;
+
+ /* create the alias */
+ ret = ldb_add(state->sam_ldb, msg);
+ if (ret != 0) {
+ *error_string = talloc_asprintf(mem_ctx, "Failed to create foreignSecurityPrincipal "
+ "record %s: %s",
+ ldb_dn_get_linearized(msg->dn),
+ ldb_errstring(state->sam_ldb));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS samsync_ldb_handle_domain(TALLOC_CTX *mem_ctx,
+ struct samsync_ldb_state *state,
+ enum netr_SamDatabaseID database,
+ struct netr_DELTA_ENUM *delta,
+ char **error_string)
+{
+ struct netr_DELTA_DOMAIN *domain = delta->delta_union.domain;
+ const char *domain_name = domain->domain_name.string;
+ struct ldb_message *msg;
+ int ret;
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (database == SAM_DATABASE_DOMAIN) {
+ struct ldb_dn *partitions_basedn;
+ const char *domain_attrs[] = {"nETBIOSName", "nCName", NULL};
+ struct ldb_message **msgs_domain;
+ int ret_domain;
+
+ partitions_basedn = samdb_partitions_dn(state->sam_ldb, mem_ctx);
+
+ ret_domain = gendb_search(state->sam_ldb, mem_ctx, partitions_basedn, &msgs_domain, domain_attrs,
+ "(&(&(nETBIOSName=%s)(objectclass=crossRef))(ncName=*))",
+ domain_name);
+ if (ret_domain == -1) {
+ *error_string = talloc_asprintf(mem_ctx, "gendb_search for domain failed: %s", ldb_errstring(state->sam_ldb));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ if (ret_domain != 1) {
+ *error_string = talloc_asprintf(mem_ctx, "Failed to find existing domain record for %s: %d results", domain_name,
+ ret_domain);
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ state->base_dn[database] = samdb_result_dn(state->sam_ldb, state, msgs_domain[0], "nCName", NULL);
+
+ if (state->dom_sid[database]) {
+ /* Update the domain sid with the incoming
+ * domain (found on LSA pipe, database sid may
+ * be random) */
+ samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx,
+ msg, "objectSid", state->dom_sid[database]);
+ } else {
+ /* Well, we will have to use the one from the database */
+ state->dom_sid[database] = samdb_search_dom_sid(state->sam_ldb, state,
+ state->base_dn[database],
+ "objectSid", NULL);
+ }
+
+ if (state->samsync_state->domain_guid) {
+ enum ndr_err_code ndr_err;
+ struct ldb_val v;
+ ndr_err = ndr_push_struct_blob(&v, msg, NULL,
+ state->samsync_state->domain_guid,
+ (ndr_push_flags_fn_t)ndr_push_GUID);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ *error_string = talloc_asprintf(mem_ctx, "ndr_push of domain GUID failed!");
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ ldb_msg_add_value(msg, "objectGUID", &v, NULL);
+ }
+ } else if (database == SAM_DATABASE_BUILTIN) {
+ /* work out the builtin_dn - useful for so many calls its worth
+ fetching here */
+ const char *dnstring = samdb_search_string(state->sam_ldb, mem_ctx, NULL,
+ "distinguishedName", "objectClass=builtinDomain");
+ state->base_dn[database] = ldb_dn_new(state, state->sam_ldb, dnstring);
+ if ( ! ldb_dn_validate(state->base_dn[database])) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ } else {
+ /* PRIVs DB */
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ msg->dn = talloc_reference(mem_ctx, state->base_dn[database]);
+ if (!msg->dn) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ samdb_msg_add_string(state->sam_ldb, mem_ctx,
+ msg, "oEMInformation", domain->oem_information.string);
+
+ samdb_msg_add_int64(state->sam_ldb, mem_ctx,
+ msg, "forceLogoff", domain->force_logoff_time);
+
+ samdb_msg_add_uint(state->sam_ldb, mem_ctx,
+ msg, "minPwdLen", domain->min_password_length);
+
+ samdb_msg_add_int64(state->sam_ldb, mem_ctx,
+ msg, "maxPwdAge", domain->max_password_age);
+
+ samdb_msg_add_int64(state->sam_ldb, mem_ctx,
+ msg, "minPwdAge", domain->min_password_age);
+
+ samdb_msg_add_uint(state->sam_ldb, mem_ctx,
+ msg, "pwdHistoryLength", domain->password_history_length);
+
+ samdb_msg_add_uint64(state->sam_ldb, mem_ctx,
+ msg, "modifiedCount",
+ domain->sequence_num);
+
+ samdb_msg_add_uint64(state->sam_ldb, mem_ctx,
+ msg, "creationTime", domain->domain_create_time);
+
+ /* TODO: Account lockout, password properties */
+
+ ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
+
+ if (ret) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS samsync_ldb_handle_user(TALLOC_CTX *mem_ctx,
+ struct samsync_ldb_state *state,
+ enum netr_SamDatabaseID database,
+ struct netr_DELTA_ENUM *delta,
+ char **error_string)
+{
+ uint32_t rid = delta->delta_id_union.rid;
+ struct netr_DELTA_USER *user = delta->delta_union.user;
+ const char *container, *obj_class;
+ char *cn_name;
+ int cn_name_len;
+ const struct dom_sid *user_sid;
+ struct ldb_message *msg;
+ struct ldb_message **msgs;
+ struct ldb_message **remote_msgs = NULL;
+ int ret, i;
+ uint32_t acb;
+ bool add = false;
+ const char *attrs[] = { NULL };
+ /* we may change this to a global search, then fill in only the things not in ldap later */
+ const char *remote_attrs[] = { "userPrincipalName", "servicePrincipalName",
+ "msDS-KeyVersionNumber", "objectGUID", NULL};
+
+ user_sid = dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid);
+ if (!user_sid) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ msg->dn = NULL;
+ /* search for the user, by rid */
+ ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database],
+ &msgs, attrs, "(&(objectClass=user)(objectSid=%s))",
+ ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
+
+ if (ret == -1) {
+ *error_string = talloc_asprintf(mem_ctx, "LDB for user %s failed: %s",
+ dom_sid_string(mem_ctx, user_sid),
+ ldb_errstring(state->sam_ldb));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ } else if (ret == 0) {
+ add = true;
+ } else if (ret > 1) {
+ *error_string = talloc_asprintf(mem_ctx, "More than one user with SID: %s in local LDB",
+ dom_sid_string(mem_ctx, user_sid));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ } else {
+ msg->dn = msgs[0]->dn;
+ talloc_steal(msg, msgs[0]->dn);
+ }
+
+ /* and do the same on the remote database */
+ if (state->remote_ldb) {
+ ret = gendb_search(state->remote_ldb, mem_ctx, state->base_dn[database],
+ &remote_msgs, remote_attrs, "(&(objectClass=user)(objectSid=%s))",
+ ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
+
+ if (ret == -1) {
+ *error_string = talloc_asprintf(mem_ctx, "remote LDAP for user %s failed: %s",
+ dom_sid_string(mem_ctx, user_sid),
+ ldb_errstring(state->remote_ldb));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ } else if (ret == 0) {
+ *error_string = talloc_asprintf(mem_ctx, "User exists in samsync but not in remote LDAP domain! (base: %s, SID: %s)",
+ ldb_dn_get_linearized(state->base_dn[database]),
+ dom_sid_string(mem_ctx, user_sid));
+ return NT_STATUS_NO_SUCH_USER;
+ } else if (ret > 1) {
+ *error_string = talloc_asprintf(mem_ctx, "More than one user in remote LDAP domain with SID: %s",
+ dom_sid_string(mem_ctx, user_sid));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+
+ /* Try to put things in the same location as the remote server */
+ } else if (add) {
+ msg->dn = remote_msgs[0]->dn;
+ talloc_steal(msg, remote_msgs[0]->dn);
+ }
+ }
+
+ cn_name = talloc_strdup(mem_ctx, user->account_name.string);
+ NT_STATUS_HAVE_NO_MEMORY(cn_name);
+ cn_name_len = strlen(cn_name);
+
+#define ADD_OR_DEL(type, attrib, field) do { \
+ if (user->field) { \
+ samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
+ attrib, user->field); \
+ } else if (!add) { \
+ samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
+ attrib); \
+ } \
+ } while (0);
+
+ ADD_OR_DEL(string, "samAccountName", account_name.string);
+ ADD_OR_DEL(string, "displayName", full_name.string);
+
+ if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
+ "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ADD_OR_DEL(uint, "primaryGroupID", primary_gid);
+ ADD_OR_DEL(string, "homeDirectory", home_directory.string);
+ ADD_OR_DEL(string, "homeDrive", home_drive.string);
+ ADD_OR_DEL(string, "scriptPath", logon_script.string);
+ ADD_OR_DEL(string, "description", description.string);
+ ADD_OR_DEL(string, "userWorkstations", workstations.string);
+
+ ADD_OR_DEL(uint64, "lastLogon", last_logon);
+ ADD_OR_DEL(uint64, "lastLogoff", last_logoff);
+
+ if (samdb_msg_add_logon_hours(state->sam_ldb, mem_ctx, msg, "logonHours", &user->logon_hours) != 0) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ADD_OR_DEL(uint, "badPwdCount", bad_password_count);
+ ADD_OR_DEL(uint, "logonCount", logon_count);
+
+ ADD_OR_DEL(uint64, "pwdLastSet", last_password_change);
+ ADD_OR_DEL(uint64, "accountExpires", acct_expiry);
+
+ if (samdb_msg_add_acct_flags(state->sam_ldb, mem_ctx, msg,
+ "userAccountControl", user->acct_flags) != 0) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!add) {
+ /* Passwords. Ensure there is no plaintext stored against
+ * this entry, as we only have hashes */
+ samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
+ "userPassword");
+ }
+ if (user->lm_password_present) {
+ samdb_msg_add_hash(state->sam_ldb, mem_ctx, msg,
+ "dBCSPwd", &user->lmpassword);
+ } else if (!add) {
+ samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
+ "dBCSPwd");
+ }
+ if (user->nt_password_present) {
+ samdb_msg_add_hash(state->sam_ldb, mem_ctx, msg,
+ "unicodePwd", &user->ntpassword);
+ } else if (!add) {
+ samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
+ "unicodePwd");
+ }
+
+ ADD_OR_DEL(string, "comment", comment.string);
+
+ if (samdb_msg_add_parameters(state->sam_ldb, mem_ctx, msg, "userParameters", &user->parameters) != 0) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ADD_OR_DEL(uint, "countryCode", country_code);
+ ADD_OR_DEL(uint, "codePage", code_page);
+
+ ADD_OR_DEL(string, "profilePath", profile_path.string);
+
+#undef ADD_OR_DEL
+
+ for (i=0; remote_attrs[i]; i++) {
+ struct ldb_message_element *el = ldb_msg_find_element(remote_msgs[0], remote_attrs[i]);
+ if (!el) {
+ samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
+ remote_attrs[i]);
+ } else {
+ ldb_msg_add(msg, el, LDB_FLAG_MOD_REPLACE);
+ }
+ }
+
+ acb = user->acct_flags;
+ if (acb & (ACB_WSTRUST)) {
+ cn_name[cn_name_len - 1] = '\0';
+ container = "Computers";
+ obj_class = "computer";
+
+ } else if (acb & ACB_SVRTRUST) {
+ if (cn_name[cn_name_len - 1] != '$') {
+ return NT_STATUS_FOOBAR;
+ }
+ cn_name[cn_name_len - 1] = '\0';
+ container = "Domain Controllers";
+ obj_class = "computer";
+ } else {
+ container = "Users";
+ obj_class = "user";
+ }
+ if (add) {
+ samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
+ "objectClass", obj_class);
+ if (!msg->dn) {
+ msg->dn = ldb_dn_copy(mem_ctx, state->base_dn[database]);
+ ldb_dn_add_child_fmt(msg->dn, "CN=%s,CN=%s", cn_name, container);
+ if (!msg->dn) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ ret = ldb_add(state->sam_ldb, msg);
+ if (ret != 0) {
+ struct ldb_dn *first_try_dn = msg->dn;
+ /* Try again with the default DN */
+ if (!remote_msgs) {
+ *error_string = talloc_asprintf(mem_ctx, "Failed to create user record. Tried %s: %s",
+ ldb_dn_get_linearized(first_try_dn),
+ ldb_errstring(state->sam_ldb));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ } else {
+ msg->dn = talloc_steal(msg, remote_msgs[0]->dn);
+ ret = ldb_add(state->sam_ldb, msg);
+ if (ret != 0) {
+ *error_string = talloc_asprintf(mem_ctx, "Failed to create user record. Tried both %s and %s: %s",
+ ldb_dn_get_linearized(first_try_dn),
+ ldb_dn_get_linearized(msg->dn),
+ ldb_errstring(state->sam_ldb));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ }
+ }
+ } else {
+ ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
+ if (ret != 0) {
+ *error_string = talloc_asprintf(mem_ctx, "Failed to modify user record %s: %s",
+ ldb_dn_get_linearized(msg->dn),
+ ldb_errstring(state->sam_ldb));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS samsync_ldb_delete_user(TALLOC_CTX *mem_ctx,
+ struct samsync_ldb_state *state,
+ enum netr_SamDatabaseID database,
+ struct netr_DELTA_ENUM *delta,
+ char **error_string)
+{
+ uint32_t rid = delta->delta_id_union.rid;
+ struct ldb_message **msgs;
+ int ret;
+ const char *attrs[] = { NULL };
+
+ /* search for the user, by rid */
+ ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database],
+ &msgs, attrs, "(&(objectClass=user)(objectSid=%s))",
+ ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
+
+ if (ret == -1) {
+ *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ } else if (ret == 0) {
+ return NT_STATUS_NO_SUCH_USER;
+ } else if (ret > 1) {
+ *error_string = talloc_asprintf(mem_ctx, "More than one user with SID: %s",
+ dom_sid_string(mem_ctx,
+ dom_sid_add_rid(mem_ctx,
+ state->dom_sid[database],
+ rid)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ ret = ldb_delete(state->sam_ldb, msgs[0]->dn);
+ if (ret != 0) {
+ *error_string = talloc_asprintf(mem_ctx, "Failed to delete user record %s: %s",
+ ldb_dn_get_linearized(msgs[0]->dn),
+ ldb_errstring(state->sam_ldb));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS samsync_ldb_handle_group(TALLOC_CTX *mem_ctx,
+ struct samsync_ldb_state *state,
+ enum netr_SamDatabaseID database,
+ struct netr_DELTA_ENUM *delta,
+ char **error_string)
+{
+ uint32_t rid = delta->delta_id_union.rid;
+ struct netr_DELTA_GROUP *group = delta->delta_union.group;
+ const char *container, *obj_class;
+ const char *cn_name;
+
+ struct ldb_message *msg;
+ struct ldb_message **msgs;
+ int ret;
+ bool add = false;
+ const char *attrs[] = { NULL };
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* search for the group, by rid */
+ ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
+ "(&(objectClass=group)(objectSid=%s))",
+ ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
+
+ if (ret == -1) {
+ *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ } else if (ret == 0) {
+ add = true;
+ } else if (ret > 1) {
+ *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
+ dom_sid_string(mem_ctx,
+ dom_sid_add_rid(mem_ctx,
+ state->dom_sid[database],
+ rid)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ } else {
+ msg->dn = talloc_steal(msg, msgs[0]->dn);
+ }
+
+ cn_name = group->group_name.string;
+
+#define ADD_OR_DEL(type, attrib, field) do { \
+ if (group->field) { \
+ samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
+ attrib, group->field); \
+ } else if (!add) { \
+ samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
+ attrib); \
+ } \
+ } while (0);
+
+ ADD_OR_DEL(string, "samAccountName", group_name.string);
+
+ if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
+ "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ADD_OR_DEL(string, "description", description.string);
+
+#undef ADD_OR_DEL
+
+ container = "Users";
+ obj_class = "group";
+
+ if (add) {
+ samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
+ "objectClass", obj_class);
+ msg->dn = ldb_dn_copy(mem_ctx, state->base_dn[database]);
+ ldb_dn_add_child_fmt(msg->dn, "CN=%s,CN=%s", cn_name, container);
+ if (!msg->dn) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = ldb_add(state->sam_ldb, msg);
+ if (ret != 0) {
+ *error_string = talloc_asprintf(mem_ctx, "Failed to create group record %s: %s",
+ ldb_dn_get_linearized(msg->dn),
+ ldb_errstring(state->sam_ldb));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ } else {
+ ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
+ if (ret != 0) {
+ *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
+ ldb_dn_get_linearized(msg->dn),
+ ldb_errstring(state->sam_ldb));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS samsync_ldb_delete_group(TALLOC_CTX *mem_ctx,
+ struct samsync_ldb_state *state,
+ enum netr_SamDatabaseID database,
+ struct netr_DELTA_ENUM *delta,
+ char **error_string)
+{
+ uint32_t rid = delta->delta_id_union.rid;
+ struct ldb_message **msgs;
+ int ret;
+ const char *attrs[] = { NULL };
+
+ /* search for the group, by rid */
+ ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
+ "(&(objectClass=group)(objectSid=%s))",
+ ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
+
+ if (ret == -1) {
+ *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ } else if (ret == 0) {
+ return NT_STATUS_NO_SUCH_GROUP;
+ } else if (ret > 1) {
+ *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
+ dom_sid_string(mem_ctx,
+ dom_sid_add_rid(mem_ctx,
+ state->dom_sid[database],
+ rid)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ ret = ldb_delete(state->sam_ldb, msgs[0]->dn);
+ if (ret != 0) {
+ *error_string = talloc_asprintf(mem_ctx, "Failed to delete group record %s: %s",
+ ldb_dn_get_linearized(msgs[0]->dn),
+ ldb_errstring(state->sam_ldb));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS samsync_ldb_handle_group_member(TALLOC_CTX *mem_ctx,
+ struct samsync_ldb_state *state,
+ enum netr_SamDatabaseID database,
+ struct netr_DELTA_ENUM *delta,
+ char **error_string)
+{
+ uint32_t rid = delta->delta_id_union.rid;
+ struct netr_DELTA_GROUP_MEMBER *group_member = delta->delta_union.group_member;
+ struct ldb_message *msg;
+ struct ldb_message **msgs;
+ int ret;
+ const char *attrs[] = { NULL };
+ int i;
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* search for the group, by rid */
+ ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
+ "(&(objectClass=group)(objectSid=%s))",
+ ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
+
+ if (ret == -1) {
+ *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ } else if (ret == 0) {
+ return NT_STATUS_NO_SUCH_GROUP;
+ } else if (ret > 1) {
+ *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
+ dom_sid_string(mem_ctx,
+ dom_sid_add_rid(mem_ctx,
+ state->dom_sid[database],
+ rid)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ } else {
+ msg->dn = talloc_steal(msg, msgs[0]->dn);
+ }
+
+ talloc_free(msgs);
+
+ for (i=0; i<group_member->num_rids; i++) {
+ /* search for the group, by rid */
+ ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
+ "(&(objectClass=user)(objectSid=%s))",
+ ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], group_member->rids[i])));
+
+ if (ret == -1) {
+ *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ } else if (ret == 0) {
+ return NT_STATUS_NO_SUCH_USER;
+ } else if (ret > 1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ } else {
+ samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "member", ldb_dn_alloc_linearized(mem_ctx, msgs[0]->dn));
+ }
+
+ talloc_free(msgs);
+ }
+
+ ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
+ if (ret != 0) {
+ *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
+ ldb_dn_get_linearized(msg->dn),
+ ldb_errstring(state->sam_ldb));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS samsync_ldb_handle_alias(TALLOC_CTX *mem_ctx,
+ struct samsync_ldb_state *state,
+ enum netr_SamDatabaseID database,
+ struct netr_DELTA_ENUM *delta,
+ char **error_string)
+{
+ uint32_t rid = delta->delta_id_union.rid;
+ struct netr_DELTA_ALIAS *alias = delta->delta_union.alias;
+ const char *container, *obj_class;
+ const char *cn_name;
+
+ struct ldb_message *msg;
+ struct ldb_message **msgs;
+ int ret;
+ bool add = false;
+ const char *attrs[] = { NULL };
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* search for the alias, by rid */
+ ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
+ "(&(objectClass=group)(objectSid=%s))",
+ ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
+
+ if (ret == -1) {
+ *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ } else if (ret == 0) {
+ add = true;
+ } else if (ret > 1) {
+ *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
+ dom_sid_string(mem_ctx,
+ dom_sid_add_rid(mem_ctx,
+ state->dom_sid[database],
+ rid)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ } else {
+ msg->dn = talloc_steal(mem_ctx, msgs[0]->dn);
+ }
+
+ cn_name = alias->alias_name.string;
+
+#define ADD_OR_DEL(type, attrib, field) do { \
+ if (alias->field) { \
+ samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
+ attrib, alias->field); \
+ } else if (!add) { \
+ samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg, \
+ attrib); \
+ } \
+ } while (0);
+
+ ADD_OR_DEL(string, "samAccountName", alias_name.string);
+
+ if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg,
+ "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ADD_OR_DEL(string, "description", description.string);
+
+#undef ADD_OR_DEL
+
+ samdb_msg_add_uint(state->sam_ldb, mem_ctx, msg, "groupType", 0x80000004);
+
+ container = "Users";
+ obj_class = "group";
+
+ if (add) {
+ samdb_msg_add_string(state->sam_ldb, mem_ctx, msg,
+ "objectClass", obj_class);
+ msg->dn = ldb_dn_copy(mem_ctx, state->base_dn[database]);
+ ldb_dn_add_child_fmt(msg->dn, "CN=%s,CN=%s", cn_name, container);
+ if (!msg->dn) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = ldb_add(state->sam_ldb, msg);
+ if (ret != 0) {
+ *error_string = talloc_asprintf(mem_ctx, "Failed to create alias record %s: %s",
+ ldb_dn_get_linearized(msg->dn),
+ ldb_errstring(state->sam_ldb));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ } else {
+ ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
+ if (ret != 0) {
+ *error_string = talloc_asprintf(mem_ctx, "Failed to modify alias record %s: %s",
+ ldb_dn_get_linearized(msg->dn),
+ ldb_errstring(state->sam_ldb));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS samsync_ldb_delete_alias(TALLOC_CTX *mem_ctx,
+ struct samsync_ldb_state *state,
+ enum netr_SamDatabaseID database,
+ struct netr_DELTA_ENUM *delta,
+ char **error_string)
+{
+ uint32_t rid = delta->delta_id_union.rid;
+ struct ldb_message **msgs;
+ int ret;
+ const char *attrs[] = { NULL };
+
+ /* search for the alias, by rid */
+ ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
+ "(&(objectClass=group)(objectSid=%s))",
+ ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
+
+ if (ret == -1) {
+ *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ } else if (ret == 0) {
+ return NT_STATUS_NO_SUCH_ALIAS;
+ } else if (ret > 1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ ret = ldb_delete(state->sam_ldb, msgs[0]->dn);
+ if (ret != 0) {
+ *error_string = talloc_asprintf(mem_ctx, "Failed to delete alias record %s: %s",
+ ldb_dn_get_linearized(msgs[0]->dn),
+ ldb_errstring(state->sam_ldb));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS samsync_ldb_handle_alias_member(TALLOC_CTX *mem_ctx,
+ struct samsync_ldb_state *state,
+ enum netr_SamDatabaseID database,
+ struct netr_DELTA_ENUM *delta,
+ char **error_string)
+{
+ uint32_t rid = delta->delta_id_union.rid;
+ struct netr_DELTA_ALIAS_MEMBER *alias_member = delta->delta_union.alias_member;
+ struct ldb_message *msg;
+ struct ldb_message **msgs;
+ int ret;
+ const char *attrs[] = { NULL };
+ int i;
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* search for the alias, by rid */
+ ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
+ "(&(objectClass=group)(objectSid=%s))",
+ ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid)));
+
+ if (ret == -1) {
+ *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ } else if (ret == 0) {
+ return NT_STATUS_NO_SUCH_GROUP;
+ } else if (ret > 1) {
+ *error_string = talloc_asprintf(mem_ctx, "More than one group/alias with SID: %s",
+ dom_sid_string(mem_ctx,
+ dom_sid_add_rid(mem_ctx,
+ state->dom_sid[database],
+ rid)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ } else {
+ msg->dn = talloc_steal(msg, msgs[0]->dn);
+ }
+
+ talloc_free(msgs);
+
+ for (i=0; i<alias_member->sids.num_sids; i++) {
+ struct ldb_dn *alias_member_dn;
+ /* search for members, in the top basedn (normal users are builtin aliases) */
+ ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
+ "(objectSid=%s)",
+ ldap_encode_ndr_dom_sid(mem_ctx, alias_member->sids.sids[i].sid));
+
+ if (ret == -1) {
+ *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ } else if (ret == 0) {
+ NTSTATUS nt_status;
+ nt_status = samsync_ldb_add_foreignSecurityPrincipal(mem_ctx, state,
+ alias_member->sids.sids[i].sid,
+ &alias_member_dn,
+ error_string);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+ } else if (ret > 1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ } else {
+ alias_member_dn = msgs[0]->dn;
+ }
+ samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "member", ldb_dn_alloc_linearized(mem_ctx, alias_member_dn));
+
+ talloc_free(msgs);
+ }
+
+ ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
+ if (ret != 0) {
+ *error_string = talloc_asprintf(mem_ctx, "Failed to modify group record %s: %s",
+ ldb_dn_get_linearized(msg->dn),
+ ldb_errstring(state->sam_ldb));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS samsync_ldb_handle_account(TALLOC_CTX *mem_ctx,
+ struct samsync_ldb_state *state,
+ enum netr_SamDatabaseID database,
+ struct netr_DELTA_ENUM *delta,
+ char **error_string)
+{
+ struct dom_sid *sid = delta->delta_id_union.sid;
+ struct netr_DELTA_ACCOUNT *account = delta->delta_union.account;
+
+ struct ldb_message *msg;
+ struct ldb_message **msgs;
+ struct ldb_dn *privilege_dn;
+ int ret;
+ const char *attrs[] = { NULL };
+ int i;
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* search for the account, by sid, in the top basedn */
+ ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
+ "(objectSid=%s)", ldap_encode_ndr_dom_sid(mem_ctx, sid));
+
+ if (ret == -1) {
+ *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ } else if (ret == 0) {
+ NTSTATUS nt_status;
+ nt_status = samsync_ldb_add_foreignSecurityPrincipal(mem_ctx, state,
+ sid,
+ &privilege_dn,
+ error_string);
+ privilege_dn = talloc_steal(msg, privilege_dn);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+ } else if (ret > 1) {
+ *error_string = talloc_asprintf(mem_ctx, "More than one account with SID: %s",
+ dom_sid_string(mem_ctx, sid));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ } else {
+ privilege_dn = talloc_steal(msg, msgs[0]->dn);
+ }
+
+ msg->dn = privilege_dn;
+
+ for (i=0; i< account->privilege_entries; i++) {
+ samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, "privilege",
+ account->privilege_name[i].string);
+ }
+
+ ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
+ if (ret != 0) {
+ *error_string = talloc_asprintf(mem_ctx, "Failed to modify privilege record %s",
+ ldb_dn_get_linearized(msg->dn));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS samsync_ldb_delete_account(TALLOC_CTX *mem_ctx,
+ struct samsync_ldb_state *state,
+ enum netr_SamDatabaseID database,
+ struct netr_DELTA_ENUM *delta,
+ char **error_string)
+{
+ struct dom_sid *sid = delta->delta_id_union.sid;
+
+ struct ldb_message *msg;
+ struct ldb_message **msgs;
+ int ret;
+ const char *attrs[] = { NULL };
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* search for the account, by sid, in the top basedn */
+ ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[SAM_DATABASE_DOMAIN], &msgs, attrs,
+ "(objectSid=%s)",
+ ldap_encode_ndr_dom_sid(mem_ctx, sid));
+
+ if (ret == -1) {
+ *error_string = talloc_asprintf(mem_ctx, "gendb_search failed: %s", ldb_errstring(state->sam_ldb));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ } else if (ret == 0) {
+ return NT_STATUS_NO_SUCH_USER;
+ } else if (ret > 1) {
+ *error_string = talloc_asprintf(mem_ctx, "More than one account with SID: %s",
+ dom_sid_string(mem_ctx, sid));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ } else {
+ msg->dn = talloc_steal(msg, msgs[0]->dn);
+ }
+
+ samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,
+ "privilage");
+
+ ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
+ if (ret != 0) {
+ *error_string = talloc_asprintf(mem_ctx, "Failed to modify privilege record %s",
+ ldb_dn_get_linearized(msg->dn));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS libnet_samsync_ldb_fn(TALLOC_CTX *mem_ctx,
+ void *private_data,
+ enum netr_SamDatabaseID database,
+ struct netr_DELTA_ENUM *delta,
+ char **error_string)
+{
+ NTSTATUS nt_status = NT_STATUS_OK;
+ struct samsync_ldb_state *state = talloc_get_type(private_data, struct samsync_ldb_state);
+
+ *error_string = NULL;
+ switch (delta->delta_type) {
+ case NETR_DELTA_DOMAIN:
+ {
+ nt_status = samsync_ldb_handle_domain(mem_ctx,
+ state,
+ database,
+ delta,
+ error_string);
+ break;
+ }
+ case NETR_DELTA_USER:
+ {
+ nt_status = samsync_ldb_handle_user(mem_ctx,
+ state,
+ database,
+ delta,
+ error_string);
+ break;
+ }
+ case NETR_DELTA_DELETE_USER:
+ {
+ nt_status = samsync_ldb_delete_user(mem_ctx,
+ state,
+ database,
+ delta,
+ error_string);
+ break;
+ }
+ case NETR_DELTA_GROUP:
+ {
+ nt_status = samsync_ldb_handle_group(mem_ctx,
+ state,
+ database,
+ delta,
+ error_string);
+ break;
+ }
+ case NETR_DELTA_DELETE_GROUP:
+ {
+ nt_status = samsync_ldb_delete_group(mem_ctx,
+ state,
+ database,
+ delta,
+ error_string);
+ break;
+ }
+ case NETR_DELTA_GROUP_MEMBER:
+ {
+ nt_status = samsync_ldb_handle_group_member(mem_ctx,
+ state,
+ database,
+ delta,
+ error_string);
+ break;
+ }
+ case NETR_DELTA_ALIAS:
+ {
+ nt_status = samsync_ldb_handle_alias(mem_ctx,
+ state,
+ database,
+ delta,
+ error_string);
+ break;
+ }
+ case NETR_DELTA_DELETE_ALIAS:
+ {
+ nt_status = samsync_ldb_delete_alias(mem_ctx,
+ state,
+ database,
+ delta,
+ error_string);
+ break;
+ }
+ case NETR_DELTA_ALIAS_MEMBER:
+ {
+ nt_status = samsync_ldb_handle_alias_member(mem_ctx,
+ state,
+ database,
+ delta,
+ error_string);
+ break;
+ }
+ case NETR_DELTA_ACCOUNT:
+ {
+ nt_status = samsync_ldb_handle_account(mem_ctx,
+ state,
+ database,
+ delta,
+ error_string);
+ break;
+ }
+ case NETR_DELTA_DELETE_ACCOUNT:
+ {
+ nt_status = samsync_ldb_delete_account(mem_ctx,
+ state,
+ database,
+ delta,
+ error_string);
+ break;
+ }
+ default:
+ /* Can't dump them all right now */
+ break;
+ }
+ if (!NT_STATUS_IS_OK(nt_status) && !*error_string) {
+ *error_string = talloc_asprintf(mem_ctx, "Failed to handle samsync delta: %s", nt_errstr(nt_status));
+ }
+ return nt_status;
+}
+
+static NTSTATUS libnet_samsync_ldb_init(TALLOC_CTX *mem_ctx,
+ void *private_data,
+ struct libnet_SamSync_state *samsync_state,
+ char **error_string)
+{
+ struct samsync_ldb_state *state = talloc_get_type(private_data, struct samsync_ldb_state);
+ const char *server = dcerpc_server_name(samsync_state->netlogon_pipe);
+ char *ldap_url;
+
+ state->samsync_state = samsync_state;
+
+ ZERO_STRUCT(state->dom_sid);
+ if (state->samsync_state->domain_sid) {
+ state->dom_sid[SAM_DATABASE_DOMAIN] = dom_sid_dup(state, state->samsync_state->domain_sid);
+ }
+
+ state->dom_sid[SAM_DATABASE_BUILTIN] = dom_sid_parse_talloc(state, SID_BUILTIN);
+
+ if (state->samsync_state->realm) {
+ if (!server || !*server) {
+ /* huh? how do we not have a server name? */
+ *error_string = talloc_strdup(mem_ctx, "No DCE/RPC server name available. How did we connect?");
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ ldap_url = talloc_asprintf(state, "ldap://%s", server);
+
+ state->remote_ldb = ldb_wrap_connect(mem_ctx,
+ state->samsync_state->machine_net_ctx->event_ctx,
+ state->samsync_state->machine_net_ctx->lp_ctx,
+ ldap_url,
+ NULL, state->samsync_state->machine_net_ctx->cred,
+ 0, NULL);
+ if (!state->remote_ldb) {
+ *error_string = talloc_asprintf(mem_ctx, "Failed to connect to remote LDAP server at %s (used to extract additional data in SamSync replication)", ldap_url);
+ return NT_STATUS_NO_LOGON_SERVERS;
+ }
+ } else {
+ state->remote_ldb = NULL;
+ }
+ return NT_STATUS_OK;
+}
+
+NTSTATUS libnet_samsync_ldb(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_samsync_ldb *r)
+{
+ NTSTATUS nt_status;
+ struct libnet_SamSync r2;
+ struct samsync_ldb_state *state = talloc(mem_ctx, struct samsync_ldb_state);
+
+ if (!state) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ state->secrets = NULL;
+ state->trusted_domains = NULL;
+
+ state->sam_ldb = samdb_connect(mem_ctx,
+ ctx->event_ctx,
+ ctx->lp_ctx,
+ r->in.session_info);
+
+ r2.out.error_string = NULL;
+ r2.in.binding_string = r->in.binding_string;
+ r2.in.rid_crypt = true;
+ r2.in.init_fn = libnet_samsync_ldb_init;
+ r2.in.delta_fn = libnet_samsync_ldb_fn;
+ r2.in.fn_ctx = state;
+ r2.in.machine_account = NULL; /* TODO: Create a machine account, fill this in, and the delete it */
+ nt_status = libnet_SamSync_netlogon(ctx, state, &r2);
+ r->out.error_string = r2.out.error_string;
+ talloc_steal(mem_ctx, r->out.error_string);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(state);
+ return nt_status;
+ }
+ talloc_free(state);
+ return nt_status;
+}
diff --git a/source4/libnet/libnet_share.c b/source4/libnet/libnet_share.c
new file mode 100644
index 0000000000..0bf6749a9c
--- /dev/null
+++ b/source4/libnet/libnet_share.c
@@ -0,0 +1,208 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Grégory LEOCADIE <gleocadie@idealx.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libnet/libnet.h"
+#include "librpc/gen_ndr/ndr_srvsvc_c.h"
+
+
+NTSTATUS libnet_ListShares(struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx, struct libnet_ListShares *r)
+{
+ NTSTATUS status;
+ struct libnet_RpcConnect c;
+ struct srvsvc_NetShareEnumAll s;
+ struct srvsvc_NetShareInfoCtr info_ctr;
+ uint32_t resume_handle = 0;
+ uint32_t totalentries = 0;
+ struct srvsvc_NetShareCtr0 ctr0;
+ struct srvsvc_NetShareCtr1 ctr1;
+ struct srvsvc_NetShareCtr2 ctr2;
+ struct srvsvc_NetShareCtr501 ctr501;
+ struct srvsvc_NetShareCtr502 ctr502;
+
+ c.level = LIBNET_RPC_CONNECT_SERVER;
+ c.in.name = r->in.server_name;
+ c.in.dcerpc_iface = &ndr_table_srvsvc;
+
+ s.in.server_unc = talloc_asprintf(mem_ctx, "\\\\%s", c.in.name);
+
+ status = libnet_RpcConnect(ctx, mem_ctx, &c);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "Connection to SRVSVC pipe of server %s "
+ "failed: %s",
+ r->in.server_name,
+ nt_errstr(status));
+ return status;
+ }
+
+ info_ctr.level = r->in.level;
+ switch (info_ctr.level) {
+ case 0:
+ info_ctr.ctr.ctr0 = &ctr0;
+ ZERO_STRUCT(ctr0);
+ break;
+ case 1:
+ info_ctr.ctr.ctr1 = &ctr1;
+ ZERO_STRUCT(ctr1);
+ break;
+ case 2:
+ info_ctr.ctr.ctr2 = &ctr2;
+ ZERO_STRUCT(ctr2);
+ break;
+ case 501:
+ info_ctr.ctr.ctr501 = &ctr501;
+ ZERO_STRUCT(ctr501);
+ break;
+ case 502:
+ info_ctr.ctr.ctr502 = &ctr502;
+ ZERO_STRUCT(ctr502);
+ break;
+ default:
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "libnet_ListShares: Invalid info level requested: %d",
+ info_ctr.level);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ s.in.max_buffer = ~0;
+ s.in.resume_handle = &resume_handle;
+ s.in.info_ctr = &info_ctr;
+ s.out.info_ctr = &info_ctr;
+ s.out.totalentries = &totalentries;
+
+ status = dcerpc_srvsvc_NetShareEnumAll(c.out.dcerpc_pipe, mem_ctx, &s);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "srvsvc_NetShareEnumAll on server '%s' failed"
+ ": %s",
+ r->in.server_name, nt_errstr(status));
+ goto disconnect;
+ }
+
+ if (!W_ERROR_IS_OK(s.out.result) && !W_ERROR_EQUAL(s.out.result, WERR_MORE_DATA)) {
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "srvsvc_NetShareEnumAll on server '%s' failed: %s",
+ r->in.server_name, win_errstr(s.out.result));
+ goto disconnect;
+ }
+
+ r->out.ctr = s.out.info_ctr->ctr;
+
+disconnect:
+ talloc_free(c.out.dcerpc_pipe);
+
+ return status;
+}
+
+
+NTSTATUS libnet_AddShare(struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx, struct libnet_AddShare *r)
+{
+ NTSTATUS status;
+ struct libnet_RpcConnect c;
+ struct srvsvc_NetShareAdd s;
+ union srvsvc_NetShareInfo info;
+
+ c.level = LIBNET_RPC_CONNECT_SERVER;
+ c.in.name = r->in.server_name;
+ c.in.dcerpc_iface = &ndr_table_srvsvc;
+
+ status = libnet_RpcConnect(ctx, mem_ctx, &c);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "Connection to SRVSVC pipe of server %s "
+ "failed: %s",
+ r->in.server_name, nt_errstr(status));
+ return status;
+ }
+
+ info.info2 = &r->in.share;
+
+ s.in.level = 2;
+ s.in.info = &info;
+ s.in.server_unc = talloc_asprintf(mem_ctx, "\\\\%s", r->in.server_name);
+
+ status = dcerpc_srvsvc_NetShareAdd(c.out.dcerpc_pipe, mem_ctx, &s);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "srvsvc_NetShareAdd '%s' on server '%s' failed"
+ ": %s",
+ r->in.share.name, r->in.server_name,
+ nt_errstr(status));
+ } else if (!W_ERROR_IS_OK(s.out.result)) {
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "srvsvc_NetShareAdd '%s' on server '%s' failed"
+ ": %s",
+ r->in.share.name, r->in.server_name,
+ win_errstr(s.out.result));
+ status = werror_to_ntstatus(s.out.result);
+ }
+
+ talloc_free(c.out.dcerpc_pipe);
+
+ return status;
+}
+
+
+NTSTATUS libnet_DelShare(struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx, struct libnet_DelShare *r)
+{
+ NTSTATUS status;
+ struct libnet_RpcConnect c;
+ struct srvsvc_NetShareDel s;
+
+ c.level = LIBNET_RPC_CONNECT_SERVER;
+ c.in.name = r->in.server_name;
+ c.in.dcerpc_iface = &ndr_table_srvsvc;
+
+ status = libnet_RpcConnect(ctx, mem_ctx, &c);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "Connection to SRVSVC pipe of server %s "
+ "failed: %s",
+ r->in.server_name, nt_errstr(status));
+ return status;
+ }
+
+ s.in.server_unc = talloc_asprintf(mem_ctx, "\\\\%s", r->in.server_name);
+ s.in.share_name = r->in.share_name;
+
+ status = dcerpc_srvsvc_NetShareDel(c.out.dcerpc_pipe, mem_ctx, &s);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "srvsvc_NetShareDel '%s' on server '%s' failed"
+ ": %s",
+ r->in.share_name, r->in.server_name,
+ nt_errstr(status));
+ } else if (!W_ERROR_IS_OK(s.out.result)) {
+ r->out.error_string = talloc_asprintf(mem_ctx,
+ "srvsvc_NetShareDel '%s' on server '%s' failed"
+ ": %s",
+ r->in.share_name, r->in.server_name,
+ win_errstr(s.out.result));
+ status = werror_to_ntstatus(s.out.result);
+ }
+
+ talloc_free(c.out.dcerpc_pipe);
+
+ return status;
+}
diff --git a/source4/libnet/libnet_share.h b/source4/libnet/libnet_share.h
new file mode 100644
index 0000000000..3a9bd7288d
--- /dev/null
+++ b/source4/libnet/libnet_share.h
@@ -0,0 +1,70 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Grégory LEOCADIE <gleocadie@idealx.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "librpc/gen_ndr/srvsvc.h"
+
+enum libnet_ListShares_level {
+ LIBNET_LIST_SHARES_GENERIC,
+ LIBNET_LIST_SHARES_SRVSVC
+};
+
+struct libnet_ListShares {
+ struct {
+ const char *server_name;
+ uint32_t *resume_handle;
+ uint32_t level;
+ } in;
+ struct {
+ const char *error_string;
+ union srvsvc_NetShareCtr ctr;
+ uint32_t *resume_handle;
+ } out;
+};
+
+enum libnet_AddShare_level {
+ LIBNET_ADD_SHARE_GENERIC,
+ LIBNET_ADD_SHARE_SRVSVC
+};
+
+struct libnet_AddShare {
+ enum libnet_AddShare_level level;
+ struct {
+ const char * server_name;
+ struct srvsvc_NetShareInfo2 share;
+ } in;
+ struct {
+ const char* error_string;
+ } out;
+};
+
+enum libnet_DelShare_level {
+ LIBNET_DEL_SHARE_GENERIC,
+ LIBNET_DEL_SHARE_SRVSVC
+};
+
+struct libnet_DelShare {
+ enum libnet_DelShare_level level;
+ struct {
+ const char *server_name;
+ const char *share_name;
+ } in;
+ struct {
+ const char *error_string;
+ } out;
+};
diff --git a/source4/libnet/libnet_site.c b/source4/libnet/libnet_site.c
new file mode 100644
index 0000000000..4a32ab92ed
--- /dev/null
+++ b/source4/libnet/libnet_site.c
@@ -0,0 +1,270 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libnet/libnet.h"
+#include "libcli/cldap/cldap.h"
+#include "lib/ldb/include/ldb.h"
+#include "lib/ldb/include/ldb_errors.h"
+#include "librpc/rpc/dcerpc.h"
+#include "libcli/resolve/resolve.h"
+#include "param/param.h"
+
+/**
+ * 1. Setup a CLDAP socket.
+ * 2. Lookup the default Site-Name.
+ */
+NTSTATUS libnet_FindSite(TALLOC_CTX *ctx, struct libnet_context *lctx, struct libnet_JoinSite *r)
+{
+ NTSTATUS status;
+ TALLOC_CTX *tmp_ctx;
+
+ char *site_name_str;
+ char *config_dn_str;
+ char *server_dn_str;
+
+ struct cldap_socket *cldap = NULL;
+ struct cldap_netlogon search;
+
+ tmp_ctx = talloc_named(ctx, 0, "libnet_FindSite temp context");
+ if (!tmp_ctx) {
+ r->out.error_string = NULL;
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* Resolve the site name. */
+ ZERO_STRUCT(search);
+ search.in.dest_address = r->in.dest_address;
+ search.in.dest_port = r->in.cldap_port;
+ search.in.acct_control = -1;
+ search.in.version = NETLOGON_NT_VERSION_5 | NETLOGON_NT_VERSION_5EX;
+ search.in.map_response = true;
+
+ cldap = cldap_socket_init(tmp_ctx, lctx->event_ctx, lp_iconv_convenience(lctx->lp_ctx));
+ status = cldap_netlogon(cldap, tmp_ctx, &search);
+ if (!NT_STATUS_IS_OK(status)
+ || !search.out.netlogon.data.nt5_ex.client_site) {
+ /*
+ If cldap_netlogon() returns in error,
+ default to using Default-First-Site-Name.
+ */
+ site_name_str = talloc_asprintf(tmp_ctx, "%s",
+ "Default-First-Site-Name");
+ if (!site_name_str) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ } else {
+ site_name_str = talloc_asprintf(tmp_ctx, "%s",
+ search.out.netlogon.data.nt5_ex.client_site);
+ if (!site_name_str) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ /* Generate the CN=Configuration,... DN. */
+/* TODO: look it up! */
+ config_dn_str = talloc_asprintf(tmp_ctx, "CN=Configuration,%s", r->in.domain_dn_str);
+ if (!config_dn_str) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* Generate the CN=Servers,... DN. */
+ server_dn_str = talloc_asprintf(tmp_ctx, "CN=%s,CN=Servers,CN=%s,CN=Sites,%s",
+ r->in.netbios_name, site_name_str, config_dn_str);
+ if (!server_dn_str) {
+ r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ r->out.site_name_str = site_name_str;
+ talloc_steal(r, site_name_str);
+
+ r->out.config_dn_str = config_dn_str;
+ talloc_steal(r, config_dn_str);
+
+ r->out.server_dn_str = server_dn_str;
+ talloc_steal(r, server_dn_str);
+
+ talloc_free(tmp_ctx);
+ return NT_STATUS_OK;
+}
+
+/*
+ * find out Site specific stuff:
+ * 1. Lookup the Site name.
+ * 2. Add entry CN=<netbios name>,CN=Servers,CN=<site name>,CN=Sites,CN=Configuration,<domain dn>.
+ * TODO: 3.) use DsAddEntry() to create CN=NTDS Settings,CN=<netbios name>,CN=Servers,CN=<site name>,...
+ */
+NTSTATUS libnet_JoinSite(struct libnet_context *ctx,
+ struct ldb_context *remote_ldb,
+ struct libnet_JoinDomain *libnet_r)
+{
+ NTSTATUS status;
+ TALLOC_CTX *tmp_ctx;
+
+ struct libnet_JoinSite *r;
+
+ struct ldb_dn *server_dn;
+ struct ldb_message *msg;
+ int rtn;
+
+ const char *server_dn_str;
+ const char *config_dn_str;
+ struct nbt_name name;
+ const char *dest_addr = NULL;
+
+ tmp_ctx = talloc_named(libnet_r, 0, "libnet_JoinSite temp context");
+ if (!tmp_ctx) {
+ libnet_r->out.error_string = NULL;
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ r = talloc(tmp_ctx, struct libnet_JoinSite);
+ if (!r) {
+ libnet_r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ make_nbt_name_client(&name, libnet_r->out.samr_binding->host);
+ status = resolve_name(lp_resolve_context(ctx->lp_ctx), &name, r, &dest_addr, ctx->event_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ libnet_r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ /* Resolve the site name and AD DN's. */
+ r->in.dest_address = dest_addr;
+ r->in.netbios_name = libnet_r->in.netbios_name;
+ r->in.domain_dn_str = libnet_r->out.domain_dn_str;
+ r->in.cldap_port = lp_cldap_port(ctx->lp_ctx);
+
+ status = libnet_FindSite(tmp_ctx, ctx, r);
+ if (!NT_STATUS_IS_OK(status)) {
+ libnet_r->out.error_string =
+ talloc_steal(libnet_r, r->out.error_string);
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ config_dn_str = r->out.config_dn_str;
+ server_dn_str = r->out.server_dn_str;
+
+ /*
+ Add entry CN=<netbios name>,CN=Servers,CN=<site name>,CN=Sites,CN=Configuration,<domain dn>.
+ */
+ msg = ldb_msg_new(tmp_ctx);
+ if (!msg) {
+ libnet_r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rtn = ldb_msg_add_string(msg, "objectClass", "server");
+ if (rtn != 0) {
+ libnet_r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ rtn = ldb_msg_add_string(msg, "systemFlags", "50000000");
+ if (rtn != 0) {
+ libnet_r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ rtn = ldb_msg_add_string(msg, "serverReference", libnet_r->out.account_dn_str);
+ if (rtn != 0) {
+ libnet_r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ server_dn = ldb_dn_new(tmp_ctx, remote_ldb, server_dn_str);
+ if ( ! ldb_dn_validate(server_dn)) {
+ libnet_r->out.error_string = talloc_asprintf(libnet_r,
+ "Invalid server dn: %s",
+ server_dn_str);
+ talloc_free(tmp_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ msg->dn = server_dn;
+
+ rtn = ldb_add(remote_ldb, msg);
+ if (rtn == LDB_ERR_ENTRY_ALREADY_EXISTS) {
+ int i;
+
+ /* make a 'modify' msg, and only for serverReference */
+ msg = ldb_msg_new(tmp_ctx);
+ if (!msg) {
+ libnet_r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ msg->dn = server_dn;
+
+ rtn = ldb_msg_add_string(msg, "serverReference",libnet_r->out.account_dn_str);
+ if (rtn != 0) {
+ libnet_r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* mark all the message elements (should be just one)
+ as LDB_FLAG_MOD_REPLACE */
+ for (i=0;i<msg->num_elements;i++) {
+ msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
+ }
+
+ rtn = ldb_modify(remote_ldb, msg);
+ if (rtn != 0) {
+ libnet_r->out.error_string
+ = talloc_asprintf(libnet_r,
+ "Failed to modify server entry %s: %s: %d",
+ server_dn_str,
+ ldb_errstring(remote_ldb), rtn);
+ talloc_free(tmp_ctx);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ } else if (rtn != 0) {
+ libnet_r->out.error_string
+ = talloc_asprintf(libnet_r,
+ "Failed to add server entry %s: %s: %d",
+ server_dn_str, ldb_errstring(remote_ldb),
+ rtn);
+ talloc_free(tmp_ctx);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ DEBUG(0, ("We still need to perform a DsAddEntry() so that we can create the CN=NTDS Settings container.\n"));
+
+ /* Store the server DN in libnet_r */
+ libnet_r->out.server_dn_str = server_dn_str;
+ talloc_steal(libnet_r, server_dn_str);
+
+ talloc_free(tmp_ctx);
+ return NT_STATUS_OK;
+}
diff --git a/source4/libnet/libnet_site.h b/source4/libnet/libnet_site.h
new file mode 100644
index 0000000000..8e607c5b6a
--- /dev/null
+++ b/source4/libnet/libnet_site.h
@@ -0,0 +1,35 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+struct libnet_JoinSite {
+ struct {
+ const char *dest_address;
+ const char *netbios_name;
+ const char *domain_dn_str;
+ uint16_t cldap_port;
+ } in;
+
+ struct {
+ const char *error_string;
+ const char *site_name_str;
+ const char *config_dn_str;
+ const char *server_dn_str;
+ } out;
+};
+
diff --git a/source4/libnet/libnet_time.c b/source4/libnet/libnet_time.c
new file mode 100644
index 0000000000..61a451d3fd
--- /dev/null
+++ b/source4/libnet/libnet_time.c
@@ -0,0 +1,123 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Stefan Metzmacher 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libnet/libnet.h"
+#include "system/time.h"
+#include "librpc/gen_ndr/ndr_srvsvc_c.h"
+
+/*
+ * get the remote time of a server via srvsvc_NetRemoteTOD
+ */
+static NTSTATUS libnet_RemoteTOD_srvsvc(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_RemoteTOD *r)
+{
+ NTSTATUS status;
+ struct libnet_RpcConnect c;
+ struct srvsvc_NetRemoteTOD tod;
+ struct srvsvc_NetRemoteTODInfo *info = NULL;
+ struct tm tm;
+
+ /* prepare connect to the SRVSVC pipe of a timeserver */
+ c.level = LIBNET_RPC_CONNECT_SERVER;
+ c.in.name = r->srvsvc.in.server_name;
+ c.in.dcerpc_iface = &ndr_table_srvsvc;
+
+ /* 1. connect to the SRVSVC pipe of a timeserver */
+ status = libnet_RpcConnect(ctx, mem_ctx, &c);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->srvsvc.out.error_string = talloc_asprintf(mem_ctx,
+ "Connection to SRVSVC pipe of server '%s' failed: %s",
+ r->srvsvc.in.server_name, nt_errstr(status));
+ return status;
+ }
+
+ /* prepare srvsvc_NetrRemoteTOD */
+ tod.in.server_unc = talloc_asprintf(mem_ctx, "\\%s", c.in.name);
+ tod.out.info = &info;
+
+ /* 2. try srvsvc_NetRemoteTOD */
+ status = dcerpc_srvsvc_NetRemoteTOD(c.out.dcerpc_pipe, mem_ctx, &tod);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->srvsvc.out.error_string = talloc_asprintf(mem_ctx,
+ "srvsvc_NetrRemoteTOD on server '%s' failed: %s",
+ r->srvsvc.in.server_name, nt_errstr(status));
+ goto disconnect;
+ }
+
+ /* check result of srvsvc_NetrRemoteTOD */
+ if (!W_ERROR_IS_OK(tod.out.result)) {
+ r->srvsvc.out.error_string = talloc_asprintf(mem_ctx,
+ "srvsvc_NetrRemoteTOD on server '%s' failed: %s",
+ r->srvsvc.in.server_name, win_errstr(tod.out.result));
+ status = werror_to_ntstatus(tod.out.result);
+ goto disconnect;
+ }
+
+ /* need to set the out parameters */
+ tm.tm_sec = (int)info->secs;
+ tm.tm_min = (int)info->mins;
+ tm.tm_hour = (int)info->hours;
+ tm.tm_mday = (int)info->day;
+ tm.tm_mon = (int)info->month -1;
+ tm.tm_year = (int)info->year - 1900;
+ tm.tm_wday = -1;
+ tm.tm_yday = -1;
+ tm.tm_isdst = -1;
+
+ r->srvsvc.out.time = timegm(&tm);
+ r->srvsvc.out.time_zone = info->timezone * 60;
+
+ goto disconnect;
+
+disconnect:
+ /* close connection */
+ talloc_free(c.out.dcerpc_pipe);
+
+ return status;
+}
+
+static NTSTATUS libnet_RemoteTOD_generic(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_RemoteTOD *r)
+{
+ NTSTATUS status;
+ union libnet_RemoteTOD r2;
+
+ r2.srvsvc.level = LIBNET_REMOTE_TOD_SRVSVC;
+ r2.srvsvc.in.server_name = r->generic.in.server_name;
+
+ status = libnet_RemoteTOD(ctx, mem_ctx, &r2);
+
+ r->generic.out.time = r2.srvsvc.out.time;
+ r->generic.out.time_zone = r2.srvsvc.out.time_zone;
+
+ r->generic.out.error_string = r2.srvsvc.out.error_string;
+
+ return status;
+}
+
+NTSTATUS libnet_RemoteTOD(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_RemoteTOD *r)
+{
+ switch (r->generic.level) {
+ case LIBNET_REMOTE_TOD_GENERIC:
+ return libnet_RemoteTOD_generic(ctx, mem_ctx, r);
+ case LIBNET_REMOTE_TOD_SRVSVC:
+ return libnet_RemoteTOD_srvsvc(ctx, mem_ctx, r);
+ }
+
+ return NT_STATUS_INVALID_LEVEL;
+}
diff --git a/source4/libnet/libnet_time.h b/source4/libnet/libnet_time.h
new file mode 100644
index 0000000000..4382d72775
--- /dev/null
+++ b/source4/libnet/libnet_time.h
@@ -0,0 +1,46 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Stefan Metzmacher 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 <http://www.gnu.org/licenses/>.
+*/
+
+/* struct and enum for getting the time of a remote system */
+enum libnet_RemoteTOD_level {
+ LIBNET_REMOTE_TOD_GENERIC,
+ LIBNET_REMOTE_TOD_SRVSVC
+};
+
+union libnet_RemoteTOD {
+ struct {
+ enum libnet_RemoteTOD_level level;
+
+ struct _libnet_RemoteTOD_in {
+ const char *server_name;
+ } in;
+
+ struct _libnet_RemoteTOD_out {
+ time_t time;
+ int time_zone;
+ const char *error_string;
+ } out;
+ } generic;
+
+ struct {
+ enum libnet_RemoteTOD_level level;
+ struct _libnet_RemoteTOD_in in;
+ struct _libnet_RemoteTOD_out out;
+ } srvsvc;
+};
diff --git a/source4/libnet/libnet_unbecome_dc.c b/source4/libnet/libnet_unbecome_dc.c
new file mode 100644
index 0000000000..3f92daab28
--- /dev/null
+++ b/source4/libnet/libnet_unbecome_dc.c
@@ -0,0 +1,736 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Stefan Metzmacher <metze@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libnet/libnet.h"
+#include "libcli/composite/composite.h"
+#include "libcli/cldap/cldap.h"
+#include "lib/ldb/include/ldb.h"
+#include "lib/ldb/include/ldb_errors.h"
+#include "lib/ldb_wrap.h"
+#include "dsdb/samdb/samdb.h"
+#include "dsdb/common/flags.h"
+#include "librpc/gen_ndr/ndr_drsuapi_c.h"
+#include "param/param.h"
+
+/*****************************************************************************
+ * Windows 2003 (w2k3) does the following steps when changing the server role
+ * from domain controller back to domain member
+ *
+ * We mostly do the same.
+ *****************************************************************************/
+
+/*
+ * lookup DC:
+ * - using nbt name<1C> request and a samlogon mailslot request
+ * or
+ * - using a DNS SRV _ldap._tcp.dc._msdcs. request and a CLDAP netlogon request
+ *
+ * see: unbecomeDC_send_cldap() and unbecomeDC_recv_cldap()
+ */
+
+/*
+ * Open 1st LDAP connection to the DC using admin credentials
+ *
+ * see: unbecomeDC_ldap_connect()
+ */
+
+/*
+ * LDAP search 1st LDAP connection:
+ *
+ * see: unbecomeDC_ldap_rootdse()
+ *
+ * Request:
+ * basedn: ""
+ * scope: base
+ * filter: (objectClass=*)
+ * attrs: defaultNamingContext
+ * configurationNamingContext
+ * Result:
+ * ""
+ * defaultNamingContext: <domain_partition>
+ * configurationNamingContext:CN=Configuration,<domain_partition>
+ */
+
+/*
+ * LDAP search 1st LDAP connection:
+ *
+ * see: unbecomeDC_ldap_computer_object()
+ *
+ * Request:
+ * basedn: <domain_partition>
+ * scope: sub
+ * filter: (&(|(objectClass=user)(objectClass=computer))(sAMAccountName=<new_dc_account_name>))
+ * attrs: distinguishedName
+ * userAccountControl
+ * Result:
+ * CN=<new_dc_netbios_name>,CN=Domain Controllers,<domain_partition>
+ * distinguishedName: CN=<new_dc_netbios_name>,CN=Domain Controllers,<domain_partition>
+ * userAccoountControl: 532480 <0x82000>
+ */
+
+/*
+ * LDAP search 1st LDAP connection:
+ *
+ * see: unbecomeDC_ldap_modify_computer()
+ *
+ * Request:
+ * basedn: CN=<new_dc_netbios_name>,CN=Computers,<domain_partition>
+ * scope: base
+ * filter: (objectClass=*)
+ * attrs: userAccountControl
+ * Result:
+ * CN=<new_dc_netbios_name>,CN=Computers,<domain_partition>
+ * userAccoountControl: 532480 <0x82000>
+ */
+
+/*
+ * LDAP modify 1st LDAP connection:
+ *
+ * see: unbecomeDC_ldap_modify_computer()
+ *
+ * Request (replace):
+ * CN=<new_dc_netbios_name>,CN=Computers,<domain_partition>
+ * userAccoountControl: 4096 <0x1000>
+ * Result:
+ * <success>
+ */
+
+/*
+ * LDAP search 1st LDAP connection:
+ *
+ * see: unbecomeDC_ldap_move_computer()
+ *
+ * Request:
+ * basedn: <WKGUID=aa312825768811d1aded00c04fd8d5cd,<domain_partition>>
+ * scope: base
+ * filter: (objectClass=*)
+ * attrs: 1.1
+ * Result:
+ * CN=Computers,<domain_partition>
+ */
+
+/*
+ * LDAP search 1st LDAP connection:
+ *
+ * not implemented because it doesn't give any new information
+ *
+ * Request:
+ * basedn: CN=Computers,<domain_partition>
+ * scope: base
+ * filter: (objectClass=*)
+ * attrs: distinguishedName
+ * Result:
+ * CN=Computers,<domain_partition>
+ * distinguishedName: CN=Computers,<domain_partition>
+ */
+
+/*
+ * LDAP modifyRDN 1st LDAP connection:
+ *
+ * see: unbecomeDC_ldap_move_computer()
+ *
+ * Request:
+ * entry: CN=<new_dc_netbios_name>,CN=Domain Controllers,<domain_partition>
+ * newrdn: CN=<new_dc_netbios_name>
+ * deleteoldrdn: TRUE
+ * newparent: CN=Computers,<domain_partition>
+ * Result:
+ * <success>
+ */
+
+/*
+ * LDAP unbind on the 1st LDAP connection
+ *
+ * not implemented, because it's not needed...
+ */
+
+/*
+ * Open 1st DRSUAPI connection to the DC using admin credentials
+ * DsBind with DRSUAPI_DS_BIND_GUID ("e24d201a-4fd6-11d1-a3da-0000f875ae0d")
+ *
+ * see: unbecomeDC_drsuapi_connect_send(), unbecomeDC_drsuapi_connect_recv(),
+ * unbecomeDC_drsuapi_bind_send() and unbecomeDC_drsuapi_bind_recv()
+ */
+
+/*
+ * DsRemoveDsServer to remove the
+ * CN=<machine_name>,CN=Servers,CN=<site_name>,CN=Configuration,<domain_partition>
+ * and CN=NTDS Settings,CN=<machine_name>,CN=Servers,CN=<site_name>,CN=Configuration,<domain_partition>
+ * on the 1st DRSUAPI connection
+ *
+ * see: unbecomeDC_drsuapi_remove_ds_server_send() and unbecomeDC_drsuapi_remove_ds_server_recv()
+ */
+
+/*
+ * DsUnbind on the 1st DRSUAPI connection
+ *
+ * not implemented, because it's not needed...
+ */
+
+
+struct libnet_UnbecomeDC_state {
+ struct composite_context *creq;
+
+ struct libnet_context *libnet;
+
+ struct {
+ struct cldap_socket *sock;
+ struct cldap_netlogon io;
+ struct NETLOGON_SAM_LOGON_RESPONSE_EX netlogon;
+ } cldap;
+
+ struct {
+ struct ldb_context *ldb;
+ } ldap;
+
+ struct {
+ struct dcerpc_binding *binding;
+ struct dcerpc_pipe *pipe;
+ struct drsuapi_DsBind bind_r;
+ struct GUID bind_guid;
+ struct drsuapi_DsBindInfoCtr bind_info_ctr;
+ struct drsuapi_DsBindInfo28 local_info28;
+ struct drsuapi_DsBindInfo28 remote_info28;
+ struct policy_handle bind_handle;
+ struct drsuapi_DsRemoveDSServer rm_ds_srv_r;
+ } drsuapi;
+
+ struct {
+ /* input */
+ const char *dns_name;
+ const char *netbios_name;
+
+ /* constructed */
+ struct GUID guid;
+ const char *dn_str;
+ } domain;
+
+ struct {
+ /* constructed */
+ const char *config_dn_str;
+ } forest;
+
+ struct {
+ /* input */
+ const char *address;
+
+ /* constructed */
+ const char *dns_name;
+ const char *netbios_name;
+ const char *site_name;
+ } source_dsa;
+
+ struct {
+ /* input */
+ const char *netbios_name;
+
+ /* constructed */
+ const char *dns_name;
+ const char *site_name;
+ const char *computer_dn_str;
+ const char *server_dn_str;
+ uint32_t user_account_control;
+ } dest_dsa;
+};
+
+static void unbecomeDC_recv_cldap(struct cldap_request *req);
+
+static void unbecomeDC_send_cldap(struct libnet_UnbecomeDC_state *s)
+{
+ struct composite_context *c = s->creq;
+ struct cldap_request *req;
+
+ s->cldap.io.in.dest_address = s->source_dsa.address;
+ s->cldap.io.in.dest_port = lp_cldap_port(s->libnet->lp_ctx);
+ s->cldap.io.in.realm = s->domain.dns_name;
+ s->cldap.io.in.host = s->dest_dsa.netbios_name;
+ s->cldap.io.in.user = NULL;
+ s->cldap.io.in.domain_guid = NULL;
+ s->cldap.io.in.domain_sid = NULL;
+ s->cldap.io.in.acct_control = -1;
+ s->cldap.io.in.version = NETLOGON_NT_VERSION_5 | NETLOGON_NT_VERSION_5EX;
+ s->cldap.io.in.map_response = true;
+
+ s->cldap.sock = cldap_socket_init(s, s->libnet->event_ctx,
+ lp_iconv_convenience(s->libnet->lp_ctx));
+ if (composite_nomem(s->cldap.sock, c)) return;
+
+ req = cldap_netlogon_send(s->cldap.sock, &s->cldap.io);
+ if (composite_nomem(req, c)) return;
+ req->async.fn = unbecomeDC_recv_cldap;
+ req->async.private_data = s;
+}
+
+static void unbecomeDC_connect_ldap(struct libnet_UnbecomeDC_state *s);
+
+static void unbecomeDC_recv_cldap(struct cldap_request *req)
+{
+ struct libnet_UnbecomeDC_state *s = talloc_get_type(req->async.private_data,
+ struct libnet_UnbecomeDC_state);
+ struct composite_context *c = s->creq;
+
+ c->status = cldap_netlogon_recv(req, s, &s->cldap.io);
+ if (!composite_is_ok(c)) return;
+
+ s->cldap.netlogon = s->cldap.io.out.netlogon.data.nt5_ex;
+
+ s->domain.dns_name = s->cldap.netlogon.dns_domain;
+ s->domain.netbios_name = s->cldap.netlogon.domain;
+ s->domain.guid = s->cldap.netlogon.domain_uuid;
+
+ s->source_dsa.dns_name = s->cldap.netlogon.pdc_dns_name;
+ s->source_dsa.netbios_name = s->cldap.netlogon.pdc_name;
+ s->source_dsa.site_name = s->cldap.netlogon.server_site;
+
+ s->dest_dsa.site_name = s->cldap.netlogon.client_site;
+
+ unbecomeDC_connect_ldap(s);
+}
+
+static NTSTATUS unbecomeDC_ldap_connect(struct libnet_UnbecomeDC_state *s)
+{
+ char *url;
+
+ url = talloc_asprintf(s, "ldap://%s/", s->source_dsa.dns_name);
+ NT_STATUS_HAVE_NO_MEMORY(url);
+
+ s->ldap.ldb = ldb_wrap_connect(s, s->libnet->event_ctx, s->libnet->lp_ctx, url,
+ NULL,
+ s->libnet->cred,
+ 0, NULL);
+ talloc_free(url);
+ if (s->ldap.ldb == NULL) {
+ return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS unbecomeDC_ldap_rootdse(struct libnet_UnbecomeDC_state *s)
+{
+ int ret;
+ struct ldb_result *r;
+ struct ldb_dn *basedn;
+ static const char *attrs[] = {
+ "defaultNamingContext",
+ "configurationNamingContext",
+ NULL
+ };
+
+ basedn = ldb_dn_new(s, s->ldap.ldb, NULL);
+ NT_STATUS_HAVE_NO_MEMORY(basedn);
+
+ ret = ldb_search(s->ldap.ldb, s, &r, basedn, LDB_SCOPE_BASE, attrs,
+ "(objectClass=*)");
+ talloc_free(basedn);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_LDAP(ret);
+ } else if (r->count != 1) {
+ talloc_free(r);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ s->domain.dn_str = ldb_msg_find_attr_as_string(r->msgs[0], "defaultNamingContext", NULL);
+ if (!s->domain.dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ talloc_steal(s, s->domain.dn_str);
+
+ s->forest.config_dn_str = ldb_msg_find_attr_as_string(r->msgs[0], "configurationNamingContext", NULL);
+ if (!s->forest.config_dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ talloc_steal(s, s->forest.config_dn_str);
+
+ s->dest_dsa.server_dn_str = talloc_asprintf(s, "CN=%s,CN=Servers,CN=%s,CN=Sites,%s",
+ s->dest_dsa.netbios_name,
+ s->dest_dsa.site_name,
+ s->forest.config_dn_str);
+ NT_STATUS_HAVE_NO_MEMORY(s->dest_dsa.server_dn_str);
+
+ talloc_free(r);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS unbecomeDC_ldap_computer_object(struct libnet_UnbecomeDC_state *s)
+{
+ int ret;
+ struct ldb_result *r;
+ struct ldb_dn *basedn;
+ static const char *attrs[] = {
+ "distinguishedName",
+ "userAccountControl",
+ NULL
+ };
+
+ basedn = ldb_dn_new(s, s->ldap.ldb, s->domain.dn_str);
+ NT_STATUS_HAVE_NO_MEMORY(basedn);
+
+ ret = ldb_search(s->ldap.ldb, s, &r, basedn, LDB_SCOPE_SUBTREE, attrs,
+ "(&(|(objectClass=user)(objectClass=computer))(sAMAccountName=%s$))",
+ s->dest_dsa.netbios_name);
+ talloc_free(basedn);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_LDAP(ret);
+ } else if (r->count != 1) {
+ talloc_free(r);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ s->dest_dsa.computer_dn_str = samdb_result_string(r->msgs[0], "distinguishedName", NULL);
+ if (!s->dest_dsa.computer_dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ talloc_steal(s, s->dest_dsa.computer_dn_str);
+
+ s->dest_dsa.user_account_control = samdb_result_uint(r->msgs[0], "userAccountControl", 0);
+
+ talloc_free(r);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS unbecomeDC_ldap_modify_computer(struct libnet_UnbecomeDC_state *s)
+{
+ int ret;
+ struct ldb_message *msg;
+ uint32_t user_account_control = UF_WORKSTATION_TRUST_ACCOUNT;
+ uint32_t i;
+
+ /* as the value is already as we want it to be, we're done */
+ if (s->dest_dsa.user_account_control == user_account_control) {
+ return NT_STATUS_OK;
+ }
+
+ /* make a 'modify' msg, and only for serverReference */
+ msg = ldb_msg_new(s);
+ NT_STATUS_HAVE_NO_MEMORY(msg);
+ msg->dn = ldb_dn_new(msg, s->ldap.ldb, s->dest_dsa.computer_dn_str);
+ NT_STATUS_HAVE_NO_MEMORY(msg->dn);
+
+ ret = ldb_msg_add_fmt(msg, "userAccountControl", "%u", user_account_control);
+ if (ret != 0) {
+ talloc_free(msg);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* mark all the message elements (should be just one)
+ as LDB_FLAG_MOD_REPLACE */
+ for (i=0;i<msg->num_elements;i++) {
+ msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
+ }
+
+ ret = ldb_modify(s->ldap.ldb, msg);
+ talloc_free(msg);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_LDAP(ret);
+ }
+
+ s->dest_dsa.user_account_control = user_account_control;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS unbecomeDC_ldap_move_computer(struct libnet_UnbecomeDC_state *s)
+{
+ int ret;
+ struct ldb_result *r;
+ struct ldb_dn *basedn;
+ struct ldb_dn *old_dn;
+ struct ldb_dn *new_dn;
+ static const char *_1_1_attrs[] = {
+ "1.1",
+ NULL
+ };
+
+ basedn = ldb_dn_new_fmt(s, s->ldap.ldb, "<WKGUID=aa312825768811d1aded00c04fd8d5cd,%s>",
+ s->domain.dn_str);
+ NT_STATUS_HAVE_NO_MEMORY(basedn);
+
+ ret = ldb_search(s->ldap.ldb, s, &r, basedn, LDB_SCOPE_BASE,
+ _1_1_attrs, "(objectClass=*)");
+ talloc_free(basedn);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_LDAP(ret);
+ } else if (r->count != 1) {
+ talloc_free(r);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ old_dn = ldb_dn_new(r, s->ldap.ldb, s->dest_dsa.computer_dn_str);
+ NT_STATUS_HAVE_NO_MEMORY(old_dn);
+
+ new_dn = r->msgs[0]->dn;
+
+ if (!ldb_dn_add_child_fmt(new_dn, "CN=%s", s->dest_dsa.netbios_name)) {
+ talloc_free(r);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (ldb_dn_compare(old_dn, new_dn) == 0) {
+ /* we don't need to rename if the old and new dn match */
+ talloc_free(r);
+ return NT_STATUS_OK;
+ }
+
+ ret = ldb_rename(s->ldap.ldb, old_dn, new_dn);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(r);
+ return NT_STATUS_LDAP(ret);
+ }
+
+ s->dest_dsa.computer_dn_str = ldb_dn_alloc_linearized(s, new_dn);
+ NT_STATUS_HAVE_NO_MEMORY(s->dest_dsa.computer_dn_str);
+
+ talloc_free(r);
+
+ return NT_STATUS_OK;
+}
+
+static void unbecomeDC_drsuapi_connect_send(struct libnet_UnbecomeDC_state *s);
+
+static void unbecomeDC_connect_ldap(struct libnet_UnbecomeDC_state *s)
+{
+ struct composite_context *c = s->creq;
+
+ c->status = unbecomeDC_ldap_connect(s);
+ if (!composite_is_ok(c)) return;
+
+ c->status = unbecomeDC_ldap_rootdse(s);
+ if (!composite_is_ok(c)) return;
+
+ c->status = unbecomeDC_ldap_computer_object(s);
+ if (!composite_is_ok(c)) return;
+
+ c->status = unbecomeDC_ldap_modify_computer(s);
+ if (!composite_is_ok(c)) return;
+
+ c->status = unbecomeDC_ldap_move_computer(s);
+ if (!composite_is_ok(c)) return;
+
+ unbecomeDC_drsuapi_connect_send(s);
+}
+
+static void unbecomeDC_drsuapi_connect_recv(struct composite_context *creq);
+
+static void unbecomeDC_drsuapi_connect_send(struct libnet_UnbecomeDC_state *s)
+{
+ struct composite_context *c = s->creq;
+ struct composite_context *creq;
+ char *binding_str;
+
+ binding_str = talloc_asprintf(s, "ncacn_ip_tcp:%s[seal]", s->source_dsa.dns_name);
+ if (composite_nomem(binding_str, c)) return;
+
+ c->status = dcerpc_parse_binding(s, binding_str, &s->drsuapi.binding);
+ talloc_free(binding_str);
+ if (!composite_is_ok(c)) return;
+
+ creq = dcerpc_pipe_connect_b_send(s, s->drsuapi.binding, &ndr_table_drsuapi,
+ s->libnet->cred, s->libnet->event_ctx,
+ s->libnet->lp_ctx);
+ composite_continue(c, creq, unbecomeDC_drsuapi_connect_recv, s);
+}
+
+static void unbecomeDC_drsuapi_bind_send(struct libnet_UnbecomeDC_state *s);
+
+static void unbecomeDC_drsuapi_connect_recv(struct composite_context *req)
+{
+ struct libnet_UnbecomeDC_state *s = talloc_get_type(req->async.private_data,
+ struct libnet_UnbecomeDC_state);
+ struct composite_context *c = s->creq;
+
+ c->status = dcerpc_pipe_connect_b_recv(req, s, &s->drsuapi.pipe);
+ if (!composite_is_ok(c)) return;
+
+ unbecomeDC_drsuapi_bind_send(s);
+}
+
+static void unbecomeDC_drsuapi_bind_recv(struct rpc_request *req);
+
+static void unbecomeDC_drsuapi_bind_send(struct libnet_UnbecomeDC_state *s)
+{
+ struct composite_context *c = s->creq;
+ struct rpc_request *req;
+ struct drsuapi_DsBindInfo28 *bind_info28;
+
+ GUID_from_string(DRSUAPI_DS_BIND_GUID, &s->drsuapi.bind_guid);
+
+ bind_info28 = &s->drsuapi.local_info28;
+ bind_info28->supported_extensions = 0;
+ bind_info28->site_guid = GUID_zero();
+ bind_info28->pid = 0;
+ bind_info28->repl_epoch = 0;
+
+ s->drsuapi.bind_info_ctr.length = 28;
+ s->drsuapi.bind_info_ctr.info.info28 = *bind_info28;
+
+ s->drsuapi.bind_r.in.bind_guid = &s->drsuapi.bind_guid;
+ s->drsuapi.bind_r.in.bind_info = &s->drsuapi.bind_info_ctr;
+ s->drsuapi.bind_r.out.bind_handle = &s->drsuapi.bind_handle;
+
+ req = dcerpc_drsuapi_DsBind_send(s->drsuapi.pipe, s, &s->drsuapi.bind_r);
+ composite_continue_rpc(c, req, unbecomeDC_drsuapi_bind_recv, s);
+}
+
+static void unbecomeDC_drsuapi_remove_ds_server_send(struct libnet_UnbecomeDC_state *s);
+
+static void unbecomeDC_drsuapi_bind_recv(struct rpc_request *req)
+{
+ struct libnet_UnbecomeDC_state *s = talloc_get_type(req->async.private_data,
+ struct libnet_UnbecomeDC_state);
+ struct composite_context *c = s->creq;
+
+ c->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(c)) return;
+
+ if (!W_ERROR_IS_OK(s->drsuapi.bind_r.out.result)) {
+ composite_error(c, werror_to_ntstatus(s->drsuapi.bind_r.out.result));
+ return;
+ }
+
+ ZERO_STRUCT(s->drsuapi.remote_info28);
+ if (s->drsuapi.bind_r.out.bind_info) {
+ switch (s->drsuapi.bind_r.out.bind_info->length) {
+ case 24: {
+ struct drsuapi_DsBindInfo24 *info24;
+ info24 = &s->drsuapi.bind_r.out.bind_info->info.info24;
+ s->drsuapi.remote_info28.supported_extensions = info24->supported_extensions;
+ s->drsuapi.remote_info28.site_guid = info24->site_guid;
+ s->drsuapi.remote_info28.pid = info24->pid;
+ s->drsuapi.remote_info28.repl_epoch = 0;
+ break;
+ }
+ case 48: {
+ struct drsuapi_DsBindInfo48 *info48;
+ info48 = &s->drsuapi.bind_r.out.bind_info->info.info48;
+ s->drsuapi.remote_info28.supported_extensions = info48->supported_extensions;
+ s->drsuapi.remote_info28.site_guid = info48->site_guid;
+ s->drsuapi.remote_info28.pid = info48->pid;
+ s->drsuapi.remote_info28.repl_epoch = info48->repl_epoch;
+ break;
+ }
+ case 28:
+ s->drsuapi.remote_info28 = s->drsuapi.bind_r.out.bind_info->info.info28;
+ break;
+ }
+ }
+
+ unbecomeDC_drsuapi_remove_ds_server_send(s);
+}
+
+static void unbecomeDC_drsuapi_remove_ds_server_recv(struct rpc_request *req);
+
+static void unbecomeDC_drsuapi_remove_ds_server_send(struct libnet_UnbecomeDC_state *s)
+{
+ struct composite_context *c = s->creq;
+ struct rpc_request *req;
+ struct drsuapi_DsRemoveDSServer *r = &s->drsuapi.rm_ds_srv_r;
+
+ r->in.bind_handle = &s->drsuapi.bind_handle;
+ r->in.level = 1;
+ r->in.req = talloc(s, union drsuapi_DsRemoveDSServerRequest);
+ r->in.req->req1.server_dn = s->dest_dsa.server_dn_str;
+ r->in.req->req1.domain_dn = s->domain.dn_str;
+ r->in.req->req1.commit = true;
+
+ r->out.level_out = talloc(s, int32_t);
+ r->out.res = talloc(s, union drsuapi_DsRemoveDSServerResult);
+
+ req = dcerpc_drsuapi_DsRemoveDSServer_send(s->drsuapi.pipe, s, r);
+ composite_continue_rpc(c, req, unbecomeDC_drsuapi_remove_ds_server_recv, s);
+}
+
+static void unbecomeDC_drsuapi_remove_ds_server_recv(struct rpc_request *req)
+{
+ struct libnet_UnbecomeDC_state *s = talloc_get_type(req->async.private_data,
+ struct libnet_UnbecomeDC_state);
+ struct composite_context *c = s->creq;
+ struct drsuapi_DsRemoveDSServer *r = &s->drsuapi.rm_ds_srv_r;
+
+ c->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(c)) return;
+
+ if (!W_ERROR_IS_OK(r->out.result)) {
+ composite_error(c, werror_to_ntstatus(r->out.result));
+ return;
+ }
+
+ if (*r->out.level_out != 1) {
+ composite_error(c, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ composite_done(c);
+}
+
+struct composite_context *libnet_UnbecomeDC_send(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_UnbecomeDC *r)
+{
+ struct composite_context *c;
+ struct libnet_UnbecomeDC_state *s;
+ char *tmp_name;
+
+ c = composite_create(mem_ctx, ctx->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct libnet_UnbecomeDC_state);
+ if (composite_nomem(s, c)) return c;
+ c->private_data = s;
+ s->creq = c;
+ s->libnet = ctx;
+
+ /* Domain input */
+ s->domain.dns_name = talloc_strdup(s, r->in.domain_dns_name);
+ if (composite_nomem(s->domain.dns_name, c)) return c;
+ s->domain.netbios_name = talloc_strdup(s, r->in.domain_netbios_name);
+ if (composite_nomem(s->domain.netbios_name, c)) return c;
+
+ /* Source DSA input */
+ s->source_dsa.address = talloc_strdup(s, r->in.source_dsa_address);
+ if (composite_nomem(s->source_dsa.address, c)) return c;
+
+ /* Destination DSA input */
+ s->dest_dsa.netbios_name= talloc_strdup(s, r->in.dest_dsa_netbios_name);
+ if (composite_nomem(s->dest_dsa.netbios_name, c)) return c;
+
+ /* Destination DSA dns_name construction */
+ tmp_name = strlower_talloc(s, s->dest_dsa.netbios_name);
+ if (composite_nomem(tmp_name, c)) return c;
+ s->dest_dsa.dns_name = talloc_asprintf_append_buffer(tmp_name, ".%s",
+ s->domain.dns_name);
+ if (composite_nomem(s->dest_dsa.dns_name, c)) return c;
+
+ unbecomeDC_send_cldap(s);
+ return c;
+}
+
+NTSTATUS libnet_UnbecomeDC_recv(struct composite_context *c, TALLOC_CTX *mem_ctx, struct libnet_UnbecomeDC *r)
+{
+ NTSTATUS status;
+
+ status = composite_wait(c);
+
+ ZERO_STRUCT(r->out);
+
+ talloc_free(c);
+ return status;
+}
+
+NTSTATUS libnet_UnbecomeDC(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_UnbecomeDC *r)
+{
+ NTSTATUS status;
+ struct composite_context *c;
+ c = libnet_UnbecomeDC_send(ctx, mem_ctx, r);
+ status = libnet_UnbecomeDC_recv(c, mem_ctx, r);
+ return status;
+}
diff --git a/source4/libnet/libnet_unbecome_dc.h b/source4/libnet/libnet_unbecome_dc.h
new file mode 100644
index 0000000000..30a412fbdc
--- /dev/null
+++ b/source4/libnet/libnet_unbecome_dc.h
@@ -0,0 +1,31 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Stefan Metzmacher <metze@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+struct libnet_UnbecomeDC {
+ struct {
+ const char *domain_dns_name;
+ const char *domain_netbios_name;
+ const char *source_dsa_address;
+ const char *dest_dsa_netbios_name;
+ } in;
+
+ struct {
+ const char *error_string;
+ } out;
+};
diff --git a/source4/libnet/libnet_user.c b/source4/libnet/libnet_user.c
new file mode 100644
index 0000000000..8606d0856e
--- /dev/null
+++ b/source4/libnet/libnet_user.c
@@ -0,0 +1,1209 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+
+#include "includes.h"
+#include "libnet/libnet.h"
+#include "libcli/composite/composite.h"
+#include "auth/credentials/credentials.h"
+#include "librpc/ndr/libndr.h"
+#include "librpc/gen_ndr/samr.h"
+#include "librpc/gen_ndr/ndr_samr_c.h"
+#include "librpc/gen_ndr/lsa.h"
+#include "librpc/gen_ndr/ndr_lsa_c.h"
+#include "libcli/security/security.h"
+
+
+struct create_user_state {
+ struct libnet_CreateUser r;
+ struct libnet_DomainOpen domain_open;
+ struct libnet_rpc_useradd user_add;
+ struct libnet_context *ctx;
+
+ /* information about the progress */
+ void (*monitor_fn)(struct monitor_msg *);
+};
+
+
+static void continue_rpc_useradd(struct composite_context *ctx);
+static void continue_domain_open_create(struct composite_context *ctx);
+
+
+/**
+ * Sends request to create user account
+ *
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param r pointer to a structure containing arguments and results of this call
+ * @param monitor function pointer for receiving monitor messages
+ * @return compostite context of this request
+ */
+struct composite_context* libnet_CreateUser_send(struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_CreateUser *r,
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *c;
+ struct create_user_state *s;
+ struct composite_context *create_req;
+ bool prereq_met = false;
+
+ /* composite context allocation and setup */
+ c = composite_create(mem_ctx, ctx->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct create_user_state);
+ if (composite_nomem(s, c)) return c;
+
+ c->private_data = s;
+
+ /* store arguments in the state structure */
+ s->ctx = ctx;
+ s->r = *r;
+ ZERO_STRUCT(s->r.out);
+
+ /* prerequisite: make sure the domain is opened */
+ prereq_met = samr_domain_opened(ctx, s->r.in.domain_name, &c, &s->domain_open,
+ continue_domain_open_create, monitor);
+ if (!prereq_met) return c;
+
+ /* prepare arguments for useradd call */
+ s->user_add.in.username = r->in.user_name;
+ s->user_add.in.domain_handle = ctx->samr.handle;
+
+ /* send the request */
+ create_req = libnet_rpc_useradd_send(ctx->samr.pipe, &s->user_add, monitor);
+ if (composite_nomem(create_req, c)) return c;
+
+ /* set the next stage */
+ composite_continue(c, create_req, continue_rpc_useradd, c);
+ return c;
+}
+
+
+/*
+ * Stage 0.5 (optional): receive result of domain open request
+ * and send useradd request
+ */
+static void continue_domain_open_create(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct create_user_state *s;
+ struct composite_context *create_req;
+ struct monitor_msg msg;
+
+ c = talloc_get_type(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct create_user_state);
+
+ /* receive result of DomainOpen call */
+ c->status = libnet_DomainOpen_recv(ctx, s->ctx, c, &s->domain_open);
+ if (!composite_is_ok(c)) return;
+
+ /* send monitor message */
+ if (s->monitor_fn) s->monitor_fn(&msg);
+
+ /* prepare arguments for useradd call */
+ s->user_add.in.username = s->r.in.user_name;
+ s->user_add.in.domain_handle = s->ctx->samr.handle;
+
+ /* send the request */
+ create_req = libnet_rpc_useradd_send(s->ctx->samr.pipe, &s->user_add, s->monitor_fn);
+ if (composite_nomem(create_req, c)) return;
+
+ /* set the next stage */
+ composite_continue(c, create_req, continue_rpc_useradd, c);
+}
+
+
+/*
+ * Stage 1: receive result of useradd call
+ */
+static void continue_rpc_useradd(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct create_user_state *s;
+ struct monitor_msg msg;
+
+ c = talloc_get_type(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct create_user_state);
+
+ /* receive result of the call */
+ c->status = libnet_rpc_useradd_recv(ctx, c, &s->user_add);
+ if (!composite_is_ok(c)) return;
+
+ /* send monitor message */
+ if (s->monitor_fn) s->monitor_fn(&msg);
+
+ /* we're done */
+ composite_done(c);
+}
+
+
+/**
+ * Receive result of CreateUser call
+ *
+ * @param c composite context returned by send request routine
+ * @param mem_ctx memory context of this call
+ * @param r pointer to a structure containing arguments and result of this call
+ * @return nt status
+ */
+NTSTATUS libnet_CreateUser_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
+ struct libnet_CreateUser *r)
+{
+ NTSTATUS status;
+ struct create_user_state *s;
+
+ r->out.error_string = NULL;
+
+ /* wait for result of async request and check status code */
+ status = composite_wait(c);
+ if (!NT_STATUS_IS_OK(status)) {
+ s = talloc_get_type(c->private_data, struct create_user_state);
+ r->out.error_string = talloc_strdup(mem_ctx, nt_errstr(status));
+ }
+
+ return status;
+}
+
+
+/**
+ * Synchronous version of CreateUser call
+ *
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param r pointer to a structure containing arguments and result of this call
+ * @return nt status
+ */
+NTSTATUS libnet_CreateUser(struct libnet_context *ctx, TALLOC_CTX *mem_ctx,
+ struct libnet_CreateUser *r)
+{
+ struct composite_context *c;
+
+ c = libnet_CreateUser_send(ctx, mem_ctx, r, NULL);
+ return libnet_CreateUser_recv(c, mem_ctx, r);
+}
+
+
+struct delete_user_state {
+ struct libnet_DeleteUser r;
+ struct libnet_context *ctx;
+ struct libnet_DomainOpen domain_open;
+ struct libnet_rpc_userdel user_del;
+
+ /* information about the progress */
+ void (*monitor_fn)(struct monitor_msg *);
+};
+
+
+static void continue_rpc_userdel(struct composite_context *ctx);
+static void continue_domain_open_delete(struct composite_context *ctx);
+
+
+/**
+ * Sends request to delete user account
+ *
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param r pointer to structure containing arguments and result of this call
+ * @param monitor function pointer for receiving monitor messages
+ */
+struct composite_context *libnet_DeleteUser_send(struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_DeleteUser *r,
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *c;
+ struct delete_user_state *s;
+ struct composite_context *delete_req;
+ bool prereq_met = false;
+
+ /* composite context allocation and setup */
+ c = composite_create(mem_ctx, ctx->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct delete_user_state);
+ if (composite_nomem(s, c)) return c;
+
+ c->private_data = s;
+
+ /* store arguments in state structure */
+ s->ctx = ctx;
+ s->r = *r;
+ ZERO_STRUCT(s->r.out);
+
+ /* prerequisite: make sure the domain is opened before proceeding */
+ prereq_met = samr_domain_opened(ctx, s->r.in.domain_name, &c, &s->domain_open,
+ continue_domain_open_delete, monitor);
+ if (!prereq_met) return c;
+
+ /* prepare arguments for userdel call */
+ s->user_del.in.username = r->in.user_name;
+ s->user_del.in.domain_handle = ctx->samr.handle;
+
+ /* send request */
+ delete_req = libnet_rpc_userdel_send(ctx->samr.pipe, &s->user_del, monitor);
+ if (composite_nomem(delete_req, c)) return c;
+
+ /* set the next stage */
+ composite_continue(c, delete_req, continue_rpc_userdel, c);
+ return c;
+}
+
+
+/*
+ * Stage 0.5 (optional): receive result of domain open request
+ * and send useradd request
+ */
+static void continue_domain_open_delete(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct delete_user_state *s;
+ struct composite_context *delete_req;
+ struct monitor_msg msg;
+
+ c = talloc_get_type(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct delete_user_state);
+
+ /* receive result of DomainOpen call */
+ c->status = libnet_DomainOpen_recv(ctx, s->ctx, c, &s->domain_open);
+ if (!composite_is_ok(c)) return;
+
+ /* send monitor message */
+ if (s->monitor_fn) s->monitor_fn(&msg);
+
+ /* prepare arguments for userdel call */
+ s->user_del.in.username = s->r.in.user_name;
+ s->user_del.in.domain_handle = s->ctx->samr.handle;
+
+ /* send request */
+ delete_req = libnet_rpc_userdel_send(s->ctx->samr.pipe, &s->user_del, s->monitor_fn);
+ if (composite_nomem(delete_req, c)) return;
+
+ /* set the next stage */
+ composite_continue(c, delete_req, continue_rpc_userdel, c);
+}
+
+
+/*
+ * Stage 1: receive result of userdel call and finish the composite function
+ */
+static void continue_rpc_userdel(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct delete_user_state *s;
+ struct monitor_msg msg;
+
+ c = talloc_get_type(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct delete_user_state);
+
+ /* receive result of userdel call */
+ c->status = libnet_rpc_userdel_recv(ctx, c, &s->user_del);
+ if (!composite_is_ok(c)) return;
+
+ /* send monitor message */
+ if (s->monitor_fn) s->monitor_fn(&msg);
+
+ /* we're done */
+ composite_done(c);
+}
+
+
+/**
+ * Receives result of asynchronous DeleteUser call
+ *
+ * @param c composite context returned by async DeleteUser call
+ * @param mem_ctx memory context of this call
+ * @param r pointer to structure containing arguments and result
+ */
+NTSTATUS libnet_DeleteUser_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
+ struct libnet_DeleteUser *r)
+{
+ NTSTATUS status;
+ struct delete_user_state *s;
+
+ r->out.error_string = NULL;
+
+ /* wait for result of async request and check status code */
+ status = composite_wait(c);
+ if (!NT_STATUS_IS_OK(status)) {
+ s = talloc_get_type(c->private_data, struct delete_user_state);
+ r->out.error_string = talloc_steal(mem_ctx, s->r.out.error_string);
+ }
+
+ return status;
+}
+
+
+/**
+ * Synchronous version of DeleteUser call
+ *
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param r pointer to structure containing arguments and result
+ */
+NTSTATUS libnet_DeleteUser(struct libnet_context *ctx, TALLOC_CTX *mem_ctx,
+ struct libnet_DeleteUser *r)
+{
+ struct composite_context *c;
+
+ c = libnet_DeleteUser_send(ctx, mem_ctx, r, NULL);
+ return libnet_DeleteUser_recv(c, mem_ctx, r);
+}
+
+
+struct modify_user_state {
+ struct libnet_ModifyUser r;
+ struct libnet_context *ctx;
+ struct libnet_DomainOpen domain_open;
+ struct libnet_rpc_userinfo user_info;
+ struct libnet_rpc_usermod user_mod;
+
+ void (*monitor_fn)(struct monitor_msg *);
+};
+
+
+static void continue_rpc_usermod(struct composite_context *ctx);
+static void continue_domain_open_modify(struct composite_context *ctx);
+static NTSTATUS set_user_changes(TALLOC_CTX *mem_ctx, struct usermod_change *mod,
+ struct libnet_rpc_userinfo *info, struct libnet_ModifyUser *r);
+static void continue_rpc_userinfo(struct composite_context *ctx);
+
+
+/**
+ * Sends request to modify user account
+ *
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param r pointer to structure containing arguments and result of this call
+ * @param monitor function pointer for receiving monitor messages
+ */
+struct composite_context *libnet_ModifyUser_send(struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_ModifyUser *r,
+ void (*monitor)(struct monitor_msg*))
+{
+ const uint16_t level = 21;
+ struct composite_context *c;
+ struct modify_user_state *s;
+ struct composite_context *userinfo_req;
+ bool prereq_met = false;
+
+ c = composite_create(mem_ctx, ctx->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct modify_user_state);
+ if (composite_nomem(s, c)) return c;
+
+ c->private_data = s;
+
+ s->ctx = ctx;
+ s->r = *r;
+
+ prereq_met = samr_domain_opened(ctx, s->r.in.domain_name, &c, &s->domain_open,
+ continue_domain_open_modify, monitor);
+ if (!prereq_met) return c;
+
+ s->user_info.in.username = r->in.user_name;
+ s->user_info.in.domain_handle = ctx->samr.handle;
+ s->user_info.in.level = level;
+
+ userinfo_req = libnet_rpc_userinfo_send(ctx->samr.pipe, &s->user_info, monitor);
+ if (composite_nomem(userinfo_req, c)) return c;
+
+ composite_continue(c, userinfo_req, continue_rpc_userinfo, c);
+ return c;
+}
+
+
+/*
+ * Stage 0.5 (optional): receive result of domain open request
+ * and send userinfo request
+ */
+static void continue_domain_open_modify(struct composite_context *ctx)
+{
+ const uint16_t level = 21;
+ struct composite_context *c;
+ struct modify_user_state *s;
+ struct composite_context *userinfo_req;
+ struct monitor_msg msg;
+
+ c = talloc_get_type(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct modify_user_state);
+
+ c->status = libnet_DomainOpen_recv(ctx, s->ctx, c, &s->domain_open);
+ if (!composite_is_ok(c)) return;
+
+ if (s->monitor_fn) s->monitor_fn(&msg);
+
+ s->user_info.in.domain_handle = s->ctx->samr.handle;
+ s->user_info.in.username = s->r.in.user_name;
+ s->user_info.in.level = level;
+
+ userinfo_req = libnet_rpc_userinfo_send(s->ctx->samr.pipe, &s->user_info, s->monitor_fn);
+ if (composite_nomem(userinfo_req, c)) return;
+
+ composite_continue(c, userinfo_req, continue_rpc_userinfo, c);
+}
+
+
+/*
+ * Stage 1: receive result of userinfo call, prepare user changes
+ * (set the fields a caller required to change) and send usermod request
+ */
+static void continue_rpc_userinfo(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct modify_user_state *s;
+ struct composite_context *usermod_req;
+
+ c = talloc_get_type(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct modify_user_state);
+
+ c->status = libnet_rpc_userinfo_recv(ctx, c, &s->user_info);
+ if (!composite_is_ok(c)) return;
+
+ s->user_mod.in.domain_handle = s->ctx->samr.handle;
+ s->user_mod.in.username = s->r.in.user_name;
+
+ c->status = set_user_changes(c, &s->user_mod.in.change, &s->user_info, &s->r);
+
+ usermod_req = libnet_rpc_usermod_send(s->ctx->samr.pipe, &s->user_mod, s->monitor_fn);
+ if (composite_nomem(usermod_req, c)) return;
+
+ composite_continue(c, usermod_req, continue_rpc_usermod, c);
+}
+
+
+/*
+ * Prepare user changes: compare userinfo result to requested changes and
+ * set the field values and flags accordingly for user modify call
+ */
+static NTSTATUS set_user_changes(TALLOC_CTX *mem_ctx, struct usermod_change *mod,
+ struct libnet_rpc_userinfo *info, struct libnet_ModifyUser *r)
+{
+ struct samr_UserInfo21 *user;
+
+ if (mod == NULL || info == NULL || r == NULL || info->in.level != 21) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ user = &info->out.info.info21;
+ mod->fields = 0; /* reset flag field before setting individual flags */
+
+ /* account name change */
+ SET_FIELD_LSA_STRING(r->in, user, mod, account_name, USERMOD_FIELD_ACCOUNT_NAME);
+
+ /* full name change */
+ SET_FIELD_LSA_STRING(r->in, user, mod, full_name, USERMOD_FIELD_FULL_NAME);
+
+ /* description change */
+ SET_FIELD_LSA_STRING(r->in, user, mod, description, USERMOD_FIELD_DESCRIPTION);
+
+ /* comment change */
+ SET_FIELD_LSA_STRING(r->in, user, mod, comment, USERMOD_FIELD_COMMENT);
+
+ /* home directory change */
+ SET_FIELD_LSA_STRING(r->in, user, mod, home_directory, USERMOD_FIELD_HOME_DIRECTORY);
+
+ /* home drive change */
+ SET_FIELD_LSA_STRING(r->in, user, mod, home_drive, USERMOD_FIELD_HOME_DRIVE);
+
+ /* logon script change */
+ SET_FIELD_LSA_STRING(r->in, user, mod, logon_script, USERMOD_FIELD_LOGON_SCRIPT);
+
+ /* profile path change */
+ SET_FIELD_LSA_STRING(r->in, user, mod, profile_path, USERMOD_FIELD_PROFILE_PATH);
+
+ /* account expiry change */
+ SET_FIELD_NTTIME(r->in, user, mod, acct_expiry, USERMOD_FIELD_ACCT_EXPIRY);
+
+ /* account flags change */
+ SET_FIELD_ACCT_FLAGS(r->in, user, mod, acct_flags, USERMOD_FIELD_ACCT_FLAGS);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ * Stage 2: receive result of usermod request and finish the composite function
+ */
+static void continue_rpc_usermod(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct modify_user_state *s;
+ struct monitor_msg msg;
+
+ c = talloc_get_type(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct modify_user_state);
+
+ c->status = libnet_rpc_usermod_recv(ctx, c, &s->user_mod);
+ if (!composite_is_ok(c)) return;
+
+ if (s->monitor_fn) s->monitor_fn(&msg);
+ composite_done(c);
+}
+
+
+/**
+ * Receive result of ModifyUser call
+ *
+ * @param c composite context returned by send request routine
+ * @param mem_ctx memory context of this call
+ * @param r pointer to a structure containing arguments and result of this call
+ * @return nt status
+ */
+NTSTATUS libnet_ModifyUser_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
+ struct libnet_ModifyUser *r)
+{
+ NTSTATUS status = composite_wait(c);
+ return status;
+}
+
+
+/**
+ * Synchronous version of ModifyUser call
+ *
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param r pointer to a structure containing arguments and result of this call
+ * @return nt status
+ */
+NTSTATUS libnet_ModifyUser(struct libnet_context *ctx, TALLOC_CTX *mem_ctx,
+ struct libnet_ModifyUser *r)
+{
+ struct composite_context *c;
+
+ c = libnet_ModifyUser_send(ctx, mem_ctx, r, NULL);
+ return libnet_ModifyUser_recv(c, mem_ctx, r);
+}
+
+
+struct user_info_state {
+ struct libnet_context *ctx;
+ const char *domain_name;
+ enum libnet_UserInfo_level level;
+ const char *user_name;
+ const char *sid_string;
+ struct libnet_LookupName lookup;
+ struct libnet_DomainOpen domopen;
+ struct libnet_rpc_userinfo userinfo;
+
+ /* information about the progress */
+ void (*monitor_fn)(struct monitor_msg *);
+};
+
+
+static void continue_name_found(struct composite_context *ctx);
+static void continue_domain_open_info(struct composite_context *ctx);
+static void continue_info_received(struct composite_context *ctx);
+
+
+/**
+ * Sends request to get user account information
+ *
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param r pointer to a structure containing arguments and results of this call
+ * @param monitor function pointer for receiving monitor messages
+ * @return compostite context of this request
+ */
+struct composite_context* libnet_UserInfo_send(struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_UserInfo *r,
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *c;
+ struct user_info_state *s;
+ struct composite_context *lookup_req, *info_req;
+ bool prereq_met = false;
+
+ /* composite context allocation and setup */
+ c = composite_create(mem_ctx, ctx->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct user_info_state);
+ if (composite_nomem(s, c)) return c;
+
+ c->private_data = s;
+
+ /* store arguments in the state structure */
+ s->monitor_fn = monitor;
+ s->ctx = ctx;
+ s->domain_name = talloc_strdup(c, r->in.domain_name);
+ s->level = r->in.level;
+ switch (s->level) {
+ case USER_INFO_BY_NAME:
+ s->user_name = talloc_strdup(c, r->in.data.user_name);
+ s->sid_string = NULL;
+ break;
+ case USER_INFO_BY_SID:
+ s->user_name = NULL;
+ s->sid_string = dom_sid_string(c, r->in.data.user_sid);
+ break;
+ }
+
+ /* prerequisite: make sure the domain is opened */
+ prereq_met = samr_domain_opened(ctx, s->domain_name, &c, &s->domopen,
+ continue_domain_open_info, monitor);
+ if (!prereq_met) return c;
+
+ switch (s->level) {
+ case USER_INFO_BY_NAME:
+ /* prepare arguments for LookupName call */
+ s->lookup.in.domain_name = s->domain_name;
+ s->lookup.in.name = s->user_name;
+
+ /* send the request */
+ lookup_req = libnet_LookupName_send(ctx, c, &s->lookup,
+ s->monitor_fn);
+ if (composite_nomem(lookup_req, c)) return c;
+
+ /* set the next stage */
+ composite_continue(c, lookup_req, continue_name_found, c);
+ break;
+ case USER_INFO_BY_SID:
+ /* prepare arguments for UserInfo call */
+ s->userinfo.in.domain_handle = s->ctx->samr.handle;
+ s->userinfo.in.sid = s->sid_string;
+ s->userinfo.in.level = 21;
+
+ /* send the request */
+ info_req = libnet_rpc_userinfo_send(s->ctx->samr.pipe,
+ &s->userinfo,
+ s->monitor_fn);
+ if (composite_nomem(info_req, c)) return c;
+
+ /* set the next stage */
+ composite_continue(c, info_req, continue_info_received, c);
+ break;
+ }
+
+ return c;
+}
+
+
+/*
+ * Stage 0.5 (optional): receive result of domain open request
+ * and send LookupName request
+ */
+static void continue_domain_open_info(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct user_info_state *s;
+ struct composite_context *lookup_req, *info_req;
+ struct monitor_msg msg;
+
+ c = talloc_get_type(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct user_info_state);
+
+ /* receive result of DomainOpen call */
+ c->status = libnet_DomainOpen_recv(ctx, s->ctx, c, &s->domopen);
+ if (!composite_is_ok(c)) return;
+
+ /* send monitor message */
+ if (s->monitor_fn) s->monitor_fn(&msg);
+
+ switch (s->level) {
+ case USER_INFO_BY_NAME:
+ /* prepare arguments for LookupName call */
+ s->lookup.in.domain_name = s->domain_name;
+ s->lookup.in.name = s->user_name;
+
+ /* send the request */
+ lookup_req = libnet_LookupName_send(s->ctx, c, &s->lookup, s->monitor_fn);
+ if (composite_nomem(lookup_req, c)) return;
+
+ /* set the next stage */
+ composite_continue(c, lookup_req, continue_name_found, c);
+ break;
+
+ case USER_INFO_BY_SID:
+ /* prepare arguments for UserInfo call */
+ s->userinfo.in.domain_handle = s->ctx->samr.handle;
+ s->userinfo.in.sid = s->sid_string;
+ s->userinfo.in.level = 21;
+
+ /* send the request */
+ info_req = libnet_rpc_userinfo_send(s->ctx->samr.pipe,
+ &s->userinfo,
+ s->monitor_fn);
+ if (composite_nomem(info_req, c)) return;
+
+ /* set the next stage */
+ composite_continue(c, info_req, continue_info_received, c);
+ break;
+ }
+}
+
+
+/*
+ * Stage 1: receive the name (if found) and send userinfo request
+ */
+static void continue_name_found(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct user_info_state *s;
+ struct composite_context *info_req;
+
+ c = talloc_get_type(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct user_info_state);
+
+ /* receive result of LookupName call */
+ c->status = libnet_LookupName_recv(ctx, c, &s->lookup);
+ if (!composite_is_ok(c)) return;
+
+ /* we're only interested in user accounts this time */
+ if (s->lookup.out.sid_type != SID_NAME_USER) {
+ composite_error(c, NT_STATUS_NO_SUCH_USER);
+ return;
+ }
+
+ /* prepare arguments for UserInfo call */
+ s->userinfo.in.domain_handle = s->ctx->samr.handle;
+ s->userinfo.in.sid = s->lookup.out.sidstr;
+ s->userinfo.in.level = 21;
+
+ /* send the request */
+ info_req = libnet_rpc_userinfo_send(s->ctx->samr.pipe, &s->userinfo, s->monitor_fn);
+ if (composite_nomem(info_req, c)) return;
+
+ /* set the next stage */
+ composite_continue(c, info_req, continue_info_received, c);
+}
+
+
+/*
+ * Stage 2: receive user account information and finish the composite function
+ */
+static void continue_info_received(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct user_info_state *s;
+
+ c = talloc_get_type(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct user_info_state);
+
+ /* receive result of userinfo call */
+ c->status = libnet_rpc_userinfo_recv(ctx, c, &s->userinfo);
+ if (!composite_is_ok(c)) return;
+
+ composite_done(c);
+}
+
+
+/**
+ * Receive result of UserInfo call
+ *
+ * @param c composite context returned by send request routine
+ * @param mem_ctx memory context of this call
+ * @param r pointer to a structure containing arguments and result of this call
+ * @return nt status
+ */
+NTSTATUS libnet_UserInfo_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
+ struct libnet_UserInfo *r)
+{
+ NTSTATUS status;
+ struct user_info_state *s;
+
+ status = composite_wait(c);
+
+ if (NT_STATUS_IS_OK(status) && r != NULL) {
+ struct samr_UserInfo21 *info;
+
+ s = talloc_get_type(c->private_data, struct user_info_state);
+ info = &s->userinfo.out.info.info21;
+
+ r->out.user_sid = dom_sid_add_rid(mem_ctx, s->ctx->samr.sid, info->rid);
+ r->out.primary_group_sid = dom_sid_add_rid(mem_ctx, s->ctx->samr.sid, info->primary_gid);
+
+ /* string fields */
+ r->out.account_name = talloc_steal(mem_ctx, info->account_name.string);
+ r->out.full_name = talloc_steal(mem_ctx, info->full_name.string);
+ r->out.description = talloc_steal(mem_ctx, info->description.string);
+ r->out.home_directory = talloc_steal(mem_ctx, info->home_directory.string);
+ r->out.home_drive = talloc_steal(mem_ctx, info->home_drive.string);
+ r->out.comment = talloc_steal(mem_ctx, info->comment.string);
+ r->out.logon_script = talloc_steal(mem_ctx, info->logon_script.string);
+ r->out.profile_path = talloc_steal(mem_ctx, info->profile_path.string);
+
+ /* time fields (allocation) */
+ r->out.acct_expiry = talloc(mem_ctx, struct timeval);
+ r->out.allow_password_change = talloc(mem_ctx, struct timeval);
+ r->out.force_password_change = talloc(mem_ctx, struct timeval);
+ r->out.last_logon = talloc(mem_ctx, struct timeval);
+ r->out.last_logoff = talloc(mem_ctx, struct timeval);
+ r->out.last_password_change = talloc(mem_ctx, struct timeval);
+
+ /* time fields (converting) */
+ nttime_to_timeval(r->out.acct_expiry, info->acct_expiry);
+ nttime_to_timeval(r->out.allow_password_change, info->allow_password_change);
+ nttime_to_timeval(r->out.force_password_change, info->force_password_change);
+ nttime_to_timeval(r->out.last_logon, info->last_logon);
+ nttime_to_timeval(r->out.last_logoff, info->last_logoff);
+ nttime_to_timeval(r->out.last_password_change, info->last_password_change);
+
+ /* flag and number fields */
+ r->out.acct_flags = info->acct_flags;
+
+ r->out.error_string = talloc_strdup(mem_ctx, "Success");
+
+ } else {
+ r->out.error_string = talloc_asprintf(mem_ctx, "Error: %s", nt_errstr(status));
+ }
+
+ talloc_free(c);
+
+ return status;
+}
+
+
+/**
+ * Synchronous version of UserInfo call
+ *
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param r pointer to a structure containing arguments and result of this call
+ * @return nt status
+ */
+NTSTATUS libnet_UserInfo(struct libnet_context *ctx, TALLOC_CTX *mem_ctx,
+ struct libnet_UserInfo *r)
+{
+ struct composite_context *c;
+
+ c = libnet_UserInfo_send(ctx, mem_ctx, r, NULL);
+ return libnet_UserInfo_recv(c, mem_ctx, r);
+}
+
+
+struct userlist_state {
+ struct libnet_context *ctx;
+ const char *domain_name;
+ struct lsa_DomainInfo dominfo;
+ int page_size;
+ uint32_t resume_index;
+ struct userlist *users;
+ uint32_t count;
+
+ struct libnet_DomainOpen domain_open;
+ struct lsa_QueryInfoPolicy query_domain;
+ struct samr_EnumDomainUsers user_list;
+
+ void (*monitor_fn)(struct monitor_msg*);
+};
+
+
+static void continue_lsa_domain_opened(struct composite_context *ctx);
+static void continue_domain_queried(struct rpc_request *req);
+static void continue_samr_domain_opened(struct composite_context *ctx);
+static void continue_users_enumerated(struct rpc_request *req);
+
+
+/**
+ * Sends request to list (enumerate) user accounts
+ *
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param r pointer to structure containing arguments and results of this call
+ * @param monitor function pointer for receiving monitor messages
+ * @return compostite context of this request
+ */
+struct composite_context* libnet_UserList_send(struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_UserList *r,
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *c;
+ struct userlist_state *s;
+ struct rpc_request *query_req;
+ bool prereq_met = false;
+
+ /* composite context allocation and setup */
+ c = composite_create(mem_ctx, ctx->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct userlist_state);
+ if (composite_nomem(s, c)) return c;
+
+ c->private_data = s;
+
+ /* store the arguments in the state structure */
+ s->ctx = ctx;
+ s->page_size = r->in.page_size;
+ s->resume_index = (uint32_t)r->in.resume_index;
+ s->domain_name = talloc_strdup(c, r->in.domain_name);
+ s->monitor_fn = monitor;
+
+ /* make sure we have lsa domain handle before doing anything */
+ prereq_met = lsa_domain_opened(ctx, s->domain_name, &c, &s->domain_open,
+ continue_lsa_domain_opened, monitor);
+ if (!prereq_met) return c;
+
+ /* prepare arguments of QueryDomainInfo call */
+ s->query_domain.in.handle = &ctx->lsa.handle;
+ s->query_domain.in.level = LSA_POLICY_INFO_DOMAIN;
+ s->query_domain.out.info = talloc_zero(c, union lsa_PolicyInformation *);
+ if (composite_nomem(s->query_domain.out.info, c)) return c;
+
+ /* send the request */
+ query_req = dcerpc_lsa_QueryInfoPolicy_send(ctx->lsa.pipe, c, &s->query_domain);
+ if (composite_nomem(query_req, c)) return c;
+
+ composite_continue_rpc(c, query_req, continue_domain_queried, c);
+ return c;
+}
+
+
+/*
+ * Stage 0.5 (optional): receive lsa domain handle and send
+ * request to query domain info
+ */
+static void continue_lsa_domain_opened(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct userlist_state *s;
+ struct rpc_request *query_req;
+
+ c = talloc_get_type(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct userlist_state);
+
+ /* receive lsa domain handle */
+ c->status = libnet_DomainOpen_recv(ctx, s->ctx, c, &s->domain_open);
+ if (!composite_is_ok(c)) return;
+
+ /* prepare arguments of QueryDomainInfo call */
+ s->query_domain.in.handle = &s->ctx->lsa.handle;
+ s->query_domain.in.level = LSA_POLICY_INFO_DOMAIN;
+ s->query_domain.out.info = talloc_zero(c, union lsa_PolicyInformation *);
+ if (composite_nomem(s->query_domain.out.info, c)) return;
+
+ /* send the request */
+ query_req = dcerpc_lsa_QueryInfoPolicy_send(s->ctx->lsa.pipe, c, &s->query_domain);
+ if (composite_nomem(query_req, c)) return;
+
+ composite_continue_rpc(c, query_req, continue_domain_queried, c);
+}
+
+
+/*
+ * Stage 1: receive domain info and request to enum users,
+ * provided a valid samr handle is opened
+ */
+static void continue_domain_queried(struct rpc_request *req)
+{
+ struct composite_context *c;
+ struct userlist_state *s;
+ struct rpc_request *enum_req;
+ bool prereq_met = false;
+
+ c = talloc_get_type(req->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct userlist_state);
+
+ /* receive result of rpc request */
+ c->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(c)) return;
+
+ /* get the returned domain info */
+ s->dominfo = (*s->query_domain.out.info)->domain;
+
+ /* make sure we have samr domain handle before continuing */
+ prereq_met = samr_domain_opened(s->ctx, s->domain_name, &c, &s->domain_open,
+ continue_samr_domain_opened, s->monitor_fn);
+ if (!prereq_met) return;
+
+ /* prepare arguments of EnumDomainUsers call */
+ s->user_list.in.domain_handle = &s->ctx->samr.handle;
+ s->user_list.in.max_size = s->page_size;
+ s->user_list.in.resume_handle = &s->resume_index;
+ s->user_list.in.acct_flags = ACB_NORMAL;
+ s->user_list.out.resume_handle = &s->resume_index;
+ s->user_list.out.num_entries = talloc(s, uint32_t);
+ if (composite_nomem(s->user_list.out.num_entries, c)) return;
+ s->user_list.out.sam = talloc(s, struct samr_SamArray *);
+ if (composite_nomem(s->user_list.out.sam, c)) return;
+
+ /* send the request */
+ enum_req = dcerpc_samr_EnumDomainUsers_send(s->ctx->samr.pipe, c, &s->user_list);
+ if (composite_nomem(enum_req, c)) return;
+
+ composite_continue_rpc(c, enum_req, continue_users_enumerated, c);
+}
+
+
+/*
+ * Stage 1.5 (optional): receive samr domain handle
+ * and request to enumerate accounts
+ */
+static void continue_samr_domain_opened(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct userlist_state *s;
+ struct rpc_request *enum_req;
+
+ c = talloc_get_type(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct userlist_state);
+
+ /* receive samr domain handle */
+ c->status = libnet_DomainOpen_recv(ctx, s->ctx, c, &s->domain_open);
+ if (!composite_is_ok(c)) return;
+
+ /* prepare arguments of EnumDomainUsers call */
+ s->user_list.in.domain_handle = &s->ctx->samr.handle;
+ s->user_list.in.max_size = s->page_size;
+ s->user_list.in.resume_handle = &s->resume_index;
+ s->user_list.in.acct_flags = ACB_NORMAL;
+ s->user_list.out.resume_handle = &s->resume_index;
+ s->user_list.out.sam = talloc(s, struct samr_SamArray *);
+ if (composite_nomem(s->user_list.out.sam, c)) return;
+ s->user_list.out.num_entries = talloc(s, uint32_t);
+ if (composite_nomem(s->user_list.out.num_entries, c)) return;
+
+ /* send the request */
+ enum_req = dcerpc_samr_EnumDomainUsers_send(s->ctx->samr.pipe, c, &s->user_list);
+ if (composite_nomem(enum_req, c)) return;
+
+ composite_continue_rpc(c, enum_req, continue_users_enumerated, c);
+}
+
+
+/*
+ * Stage 2: receive enumerated users and their rids
+ */
+static void continue_users_enumerated(struct rpc_request *req)
+{
+ struct composite_context *c;
+ struct userlist_state *s;
+ int i;
+
+ c = talloc_get_type(req->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct userlist_state);
+
+ /* receive result of rpc request */
+ c->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(c)) return;
+
+ /* get the actual status of the rpc call result
+ (instead of rpc layer status) */
+ c->status = s->user_list.out.result;
+
+ /* we're interested in status "ok" as well as two
+ enum-specific status codes */
+ if (NT_STATUS_IS_OK(c->status) ||
+ NT_STATUS_EQUAL(c->status, STATUS_MORE_ENTRIES) ||
+ NT_STATUS_EQUAL(c->status, NT_STATUS_NO_MORE_ENTRIES)) {
+
+ /* get enumerated accounts counter and resume handle (the latter allows
+ making subsequent call to continue enumeration) */
+ s->resume_index = *s->user_list.out.resume_handle;
+ s->count = *s->user_list.out.num_entries;
+
+ /* prepare returned user accounts array */
+ s->users = talloc_array(c, struct userlist, (*s->user_list.out.sam)->count);
+ if (composite_nomem(s->users, c)) return;
+
+ for (i = 0; i < (*s->user_list.out.sam)->count; i++) {
+ struct dom_sid *user_sid;
+ struct samr_SamEntry *entry = &(*s->user_list.out.sam)->entries[i];
+ struct dom_sid *domain_sid = (*s->query_domain.out.info)->domain.sid;
+
+ /* construct user sid from returned rid and queried domain sid */
+ user_sid = dom_sid_add_rid(c, domain_sid, entry->idx);
+ if (composite_nomem(user_sid, c)) return;
+
+ /* username */
+ s->users[i].username = talloc_strdup(c, entry->name.string);
+ if (composite_nomem(s->users[i].username, c)) return;
+
+ /* sid string */
+ s->users[i].sid = dom_sid_string(c, user_sid);
+ if (composite_nomem(s->users[i].sid, c)) return;
+ }
+
+ /* that's it */
+ composite_done(c);
+
+ } else {
+ /* something went wrong */
+ composite_error(c, c->status);
+ }
+}
+
+
+/**
+ * Receive result of UserList call
+ *
+ * @param c composite context returned by send request routine
+ * @param mem_ctx memory context of this call
+ * @param r pointer to structure containing arguments and result of this call
+ * @return nt status
+ */
+NTSTATUS libnet_UserList_recv(struct composite_context* c, TALLOC_CTX *mem_ctx,
+ struct libnet_UserList *r)
+{
+ NTSTATUS status;
+ struct userlist_state *s;
+
+ if (c == NULL || mem_ctx == NULL || r == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = composite_wait(c);
+ if (NT_STATUS_IS_OK(status) ||
+ NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES)) {
+
+ s = talloc_get_type(c->private_data, struct userlist_state);
+
+ /* get results from composite context */
+ r->out.count = s->count;
+ r->out.resume_index = s->resume_index;
+ r->out.users = talloc_steal(mem_ctx, s->users);
+
+ if (NT_STATUS_IS_OK(status)) {
+ r->out.error_string = talloc_strdup(mem_ctx, "Success");
+ } else {
+ /* success, but we're not done yet */
+ r->out.error_string = talloc_asprintf(mem_ctx, "Success (status: %s)",
+ nt_errstr(status));
+ }
+
+ } else {
+ r->out.error_string = talloc_asprintf(mem_ctx, "Error: %s", nt_errstr(status));
+ }
+
+ return status;
+}
+
+
+/**
+ * Synchronous version of UserList call
+ *
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param r pointer to structure containing arguments and result of this call
+ * @return nt status
+ */
+NTSTATUS libnet_UserList(struct libnet_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_UserList *r)
+{
+ struct composite_context *c;
+
+ c = libnet_UserList_send(ctx, mem_ctx, r, NULL);
+ return libnet_UserList_recv(c, mem_ctx, r);
+}
diff --git a/source4/libnet/libnet_user.h b/source4/libnet/libnet_user.h
new file mode 100644
index 0000000000..4aad654b3b
--- /dev/null
+++ b/source4/libnet/libnet_user.h
@@ -0,0 +1,156 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Rafal Szczesniak <mimir@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+
+struct libnet_CreateUser {
+ struct {
+ const char *user_name;
+ const char *domain_name;
+ } in;
+ struct {
+ const char *error_string;
+ } out;
+};
+
+
+struct libnet_DeleteUser {
+ struct {
+ const char *user_name;
+ const char *domain_name;
+ } in;
+ struct {
+ const char *error_string;
+ } out;
+};
+
+
+struct libnet_ModifyUser {
+ struct {
+ const char *user_name;
+ const char *domain_name;
+
+ const char *account_name;
+ const char *full_name;
+ const char *description;
+ const char *home_directory;
+ const char *home_drive;
+ const char *comment;
+ const char *logon_script;
+ const char *profile_path;
+ struct timeval *acct_expiry;
+ struct timeval *allow_password_change;
+ struct timeval *force_password_change;
+ struct timeval *last_password_change;
+ uint32_t acct_flags;
+ } in;
+ struct {
+ const char *error_string;
+ } out;
+};
+
+
+#define SET_FIELD_LSA_STRING(new, current, mod, field, flag) \
+ if (new.field != NULL && \
+ !strequal_m(current->field.string, new.field)) { \
+ \
+ mod->field = talloc_strdup(mem_ctx, new.field); \
+ if (mod->field == NULL) return NT_STATUS_NO_MEMORY; \
+ \
+ mod->fields |= flag; \
+ }
+
+#define SET_FIELD_NTTIME(new, current, mod, field, flag) \
+ if (new.field != 0) { \
+ NTTIME newval = timeval_to_nttime(new.field); \
+ if (newval != current->field) { \
+ mod->field = talloc_memdup(mem_ctx, new.field, sizeof(*new.field)); \
+ if (mod->field == NULL) return NT_STATUS_NO_MEMORY; \
+ mod->fields |= flag; \
+ } \
+ }
+
+#define SET_FIELD_UINT32(new, current, mod, field, flag) \
+ if (current->field != new.field) { \
+ mod->field = new.field; \
+ mod->fields |= flag; \
+ }
+
+#define SET_FIELD_ACCT_FLAGS(new, current, mod, field, flag) \
+ if (new.field) { \
+ if (current->field != new.field) { \
+ mod->field = new.field; \
+ mod->fields |= flag; \
+ } \
+ }
+
+enum libnet_UserInfo_level {
+ USER_INFO_BY_NAME=0,
+ USER_INFO_BY_SID
+};
+
+struct libnet_UserInfo {
+ struct {
+ const char *domain_name;
+ enum libnet_UserInfo_level level;
+ union {
+ const char *user_name;
+ const struct dom_sid *user_sid;
+ } data;
+ } in;
+ struct {
+ struct dom_sid *user_sid;
+ struct dom_sid *primary_group_sid;
+ const char *account_name;
+ const char *full_name;
+ const char *description;
+ const char *home_directory;
+ const char *home_drive;
+ const char *comment;
+ const char *logon_script;
+ const char *profile_path;
+ struct timeval *acct_expiry;
+ struct timeval *allow_password_change;
+ struct timeval *force_password_change;
+ struct timeval *last_logon;
+ struct timeval *last_logoff;
+ struct timeval *last_password_change;
+ uint32_t acct_flags;
+ const char *error_string;
+ } out;
+};
+
+
+struct libnet_UserList {
+ struct {
+ const char *domain_name;
+ int page_size;
+ uint resume_index;
+ } in;
+ struct {
+ int count;
+ uint resume_index;
+
+ struct userlist {
+ const char *sid;
+ const char *username;
+ } *users;
+
+ const char *error_string;
+ } out;
+};
diff --git a/source4/libnet/libnet_vampire.c b/source4/libnet/libnet_vampire.c
new file mode 100644
index 0000000000..4140de0686
--- /dev/null
+++ b/source4/libnet/libnet_vampire.c
@@ -0,0 +1,720 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Extract the user/system database from a remote server
+
+ Copyright (C) Stefan Metzmacher 2004-2006
+ Copyright (C) Brad Henry 2005
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-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 <http://www.gnu.org/licenses/>.
+*/
+
+
+#include "includes.h"
+#include "libnet/libnet.h"
+#include "lib/events/events.h"
+#include "dsdb/samdb/samdb.h"
+#include "../lib/util/dlinklist.h"
+#include "lib/ldb/include/ldb.h"
+#include "lib/ldb/include/ldb_errors.h"
+#include "librpc/ndr/libndr.h"
+#include "librpc/gen_ndr/ndr_drsuapi.h"
+#include "librpc/gen_ndr/ndr_drsblobs.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "system/time.h"
+#include "lib/ldb_wrap.h"
+#include "auth/auth.h"
+#include "param/param.h"
+#include "param/provision.h"
+
+/*
+List of tasks vampire.py must perform:
+- Domain Join
+ - but don't write the secrets.ldb
+ - results for this should be enough to handle the provision
+- if vampire method is samsync
+ - Provision using these results
+ - do we still want to support this NT4 technology?
+- Start samsync with libnet code
+ - provision in the callback
+- Write out the secrets database, using the code from libnet_Join
+
+*/
+struct vampire_state {
+ const char *netbios_name;
+ struct libnet_JoinDomain *join;
+ struct cli_credentials *machine_account;
+ struct dsdb_schema *self_made_schema;
+ const struct dsdb_schema *schema;
+
+ struct ldb_context *ldb;
+
+ struct {
+ uint32_t object_count;
+ struct drsuapi_DsReplicaObjectListItemEx *first_object;
+ struct drsuapi_DsReplicaObjectListItemEx *last_object;
+ } schema_part;
+
+ const char *targetdir;
+
+ struct loadparm_context *lp_ctx;
+ struct tevent_context *event_ctx;
+};
+
+static NTSTATUS vampire_prepare_db(void *private_data,
+ const struct libnet_BecomeDC_PrepareDB *p)
+{
+ struct vampire_state *s = talloc_get_type(private_data, struct vampire_state);
+ struct provision_settings settings;
+ struct provision_result result;
+ NTSTATUS status;
+
+ settings.site_name = p->dest_dsa->site_name;
+ settings.root_dn_str = p->forest->root_dn_str;
+ settings.domain_dn_str = p->domain->dn_str;
+ settings.config_dn_str = p->forest->config_dn_str;
+ settings.schema_dn_str = p->forest->schema_dn_str;
+ settings.netbios_name = p->dest_dsa->netbios_name;
+ settings.realm = s->join->out.realm;
+ settings.domain = s->join->out.domain_name;
+ settings.server_dn_str = p->dest_dsa->server_dn_str;
+ settings.machine_password = generate_random_str(s, 16);
+ settings.targetdir = s->targetdir;
+
+ status = provision_bare(s, s->lp_ctx, &settings, &result);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ s->ldb = result.samdb;
+ s->lp_ctx = result.lp_ctx;
+
+ return NT_STATUS_OK;
+
+
+}
+
+static NTSTATUS vampire_check_options(void *private_data,
+ const struct libnet_BecomeDC_CheckOptions *o)
+{
+ struct vampire_state *s = talloc_get_type(private_data, struct vampire_state);
+
+ DEBUG(0,("Become DC [%s] of Domain[%s]/[%s]\n",
+ s->netbios_name,
+ o->domain->netbios_name, o->domain->dns_name));
+
+ DEBUG(0,("Promotion Partner is Server[%s] from Site[%s]\n",
+ o->source_dsa->dns_name, o->source_dsa->site_name));
+
+ DEBUG(0,("Options:crossRef behavior_version[%u]\n"
+ "\tschema object_version[%u]\n"
+ "\tdomain behavior_version[%u]\n"
+ "\tdomain w2k3_update_revision[%u]\n",
+ o->forest->crossref_behavior_version,
+ o->forest->schema_object_version,
+ o->domain->behavior_version,
+ o->domain->w2k3_update_revision));
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS vampire_apply_schema(struct vampire_state *s,
+ const struct libnet_BecomeDC_StoreChunk *c)
+{
+ WERROR status;
+ const struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr;
+ uint32_t object_count;
+ struct drsuapi_DsReplicaObjectListItemEx *first_object;
+ struct drsuapi_DsReplicaObjectListItemEx *cur;
+ uint32_t linked_attributes_count;
+ struct drsuapi_DsReplicaLinkedAttribute *linked_attributes;
+ const struct drsuapi_DsReplicaCursor2CtrEx *uptodateness_vector;
+ struct dsdb_extended_replicated_objects *objs;
+ struct repsFromTo1 *s_dsa;
+ char *tmp_dns_name;
+ struct ldb_message *msg;
+ struct ldb_val prefixMap_val;
+ struct ldb_message_element *prefixMap_el;
+ struct ldb_val schemaInfo_val;
+ uint32_t i;
+ int ret;
+ bool ok;
+
+ DEBUG(0,("Analyze and apply schema objects\n"));
+
+ s_dsa = talloc_zero(s, struct repsFromTo1);
+ NT_STATUS_HAVE_NO_MEMORY(s_dsa);
+ s_dsa->other_info = talloc(s_dsa, struct repsFromTo1OtherInfo);
+ NT_STATUS_HAVE_NO_MEMORY(s_dsa->other_info);
+
+ switch (c->ctr_level) {
+ case 1:
+ mapping_ctr = &c->ctr1->mapping_ctr;
+ object_count = s->schema_part.object_count;
+ first_object = s->schema_part.first_object;
+ linked_attributes_count = 0;
+ linked_attributes = NULL;
+ s_dsa->highwatermark = c->ctr1->new_highwatermark;
+ s_dsa->source_dsa_obj_guid = c->ctr1->source_dsa_guid;
+ s_dsa->source_dsa_invocation_id = c->ctr1->source_dsa_invocation_id;
+ uptodateness_vector = NULL; /* TODO: map it */
+ break;
+ case 6:
+ mapping_ctr = &c->ctr6->mapping_ctr;
+ object_count = s->schema_part.object_count;
+ first_object = s->schema_part.first_object;
+ linked_attributes_count = 0; /* TODO: ! */
+ linked_attributes = NULL; /* TODO: ! */;
+ s_dsa->highwatermark = c->ctr6->new_highwatermark;
+ s_dsa->source_dsa_obj_guid = c->ctr6->source_dsa_guid;
+ s_dsa->source_dsa_invocation_id = c->ctr6->source_dsa_invocation_id;
+ uptodateness_vector = c->ctr6->uptodateness_vector;
+ break;
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ s_dsa->replica_flags = DRSUAPI_DS_REPLICA_NEIGHBOUR_WRITEABLE
+ | DRSUAPI_DS_REPLICA_NEIGHBOUR_SYNC_ON_STARTUP
+ | DRSUAPI_DS_REPLICA_NEIGHBOUR_DO_SCHEDULED_SYNCS;
+ memset(s_dsa->schedule, 0x11, sizeof(s_dsa->schedule));
+
+ tmp_dns_name = GUID_string(s_dsa->other_info, &s_dsa->source_dsa_obj_guid);
+ NT_STATUS_HAVE_NO_MEMORY(tmp_dns_name);
+ tmp_dns_name = talloc_asprintf_append_buffer(tmp_dns_name, "._msdcs.%s", c->forest->dns_name);
+ NT_STATUS_HAVE_NO_MEMORY(tmp_dns_name);
+ s_dsa->other_info->dns_name = tmp_dns_name;
+
+ for (cur = first_object; cur; cur = cur->next_object) {
+ bool is_attr = false;
+ bool is_class = false;
+
+ for (i=0; i < cur->object.attribute_ctr.num_attributes; i++) {
+ struct drsuapi_DsReplicaAttribute *a;
+ uint32_t j;
+ const char *oid = NULL;
+
+ a = &cur->object.attribute_ctr.attributes[i];
+ status = dsdb_map_int2oid(s->self_made_schema, a->attid, s, &oid);
+ if (!W_ERROR_IS_OK(status)) {
+ return werror_to_ntstatus(status);
+ }
+
+ switch (a->attid) {
+ case DRSUAPI_ATTRIBUTE_objectClass:
+ for (j=0; j < a->value_ctr.num_values; j++) {
+ uint32_t val = 0xFFFFFFFF;
+
+ if (a->value_ctr.values[i].blob
+ && a->value_ctr.values[i].blob->length == 4) {
+ val = IVAL(a->value_ctr.values[i].blob->data,0);
+ }
+
+ if (val == DRSUAPI_OBJECTCLASS_attributeSchema) {
+ is_attr = true;
+ }
+ if (val == DRSUAPI_OBJECTCLASS_classSchema) {
+ is_class = true;
+ }
+ }
+
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (is_attr) {
+ struct dsdb_attribute *sa;
+
+ sa = talloc_zero(s->self_made_schema, struct dsdb_attribute);
+ NT_STATUS_HAVE_NO_MEMORY(sa);
+
+ status = dsdb_attribute_from_drsuapi(s->self_made_schema, &cur->object, s, sa);
+ if (!W_ERROR_IS_OK(status)) {
+ return werror_to_ntstatus(status);
+ }
+
+ DLIST_ADD_END(s->self_made_schema->attributes, sa, struct dsdb_attribute *);
+ }
+
+ if (is_class) {
+ struct dsdb_class *sc;
+
+ sc = talloc_zero(s->self_made_schema, struct dsdb_class);
+ NT_STATUS_HAVE_NO_MEMORY(sc);
+
+ status = dsdb_class_from_drsuapi(s->self_made_schema, &cur->object, s, sc);
+ if (!W_ERROR_IS_OK(status)) {
+ return werror_to_ntstatus(status);
+ }
+
+ DLIST_ADD_END(s->self_made_schema->classes, sc, struct dsdb_class *);
+ }
+ }
+
+ /* attach the schema to the ldb */
+ ret = dsdb_set_schema(s->ldb, s->self_made_schema);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_FOOBAR;
+ }
+ /* we don't want to access the self made schema anymore */
+ s->self_made_schema = NULL;
+ s->schema = dsdb_get_schema(s->ldb);
+
+ status = dsdb_extended_replicated_objects_commit(s->ldb,
+ c->partition->nc.dn,
+ mapping_ctr,
+ object_count,
+ first_object,
+ linked_attributes_count,
+ linked_attributes,
+ s_dsa,
+ uptodateness_vector,
+ c->gensec_skey,
+ s, &objs);
+ if (!W_ERROR_IS_OK(status)) {
+ DEBUG(0,("Failed to commit objects: %s\n", win_errstr(status)));
+ return werror_to_ntstatus(status);
+ }
+
+ if (lp_parm_bool(s->lp_ctx, NULL, "become dc", "dump objects", false)) {
+ for (i=0; i < objs->num_objects; i++) {
+ struct ldb_ldif ldif;
+ fprintf(stdout, "#\n");
+ ldif.changetype = LDB_CHANGETYPE_NONE;
+ ldif.msg = objs->objects[i].msg;
+ ldb_ldif_write_file(s->ldb, stdout, &ldif);
+ NDR_PRINT_DEBUG(replPropertyMetaDataBlob, objs->objects[i].meta_data);
+ }
+ }
+
+ msg = ldb_msg_new(objs);
+ NT_STATUS_HAVE_NO_MEMORY(msg);
+ msg->dn = objs->partition_dn;
+
+ status = dsdb_get_oid_mappings_ldb(s->schema, msg, &prefixMap_val, &schemaInfo_val);
+ if (!W_ERROR_IS_OK(status)) {
+ DEBUG(0,("Failed dsdb_get_oid_mappings_ldb(%s)\n", win_errstr(status)));
+ return werror_to_ntstatus(status);
+ }
+
+ /* we only add prefixMap here, because schemaInfo is a replicated attribute and already applied */
+ ret = ldb_msg_add_value(msg, "prefixMap", &prefixMap_val, &prefixMap_el);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_FOOBAR;
+ }
+ prefixMap_el->flags = LDB_FLAG_MOD_REPLACE;
+
+ ret = ldb_modify(s->ldb, msg);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0,("Failed to add prefixMap and schemaInfo %s\n", ldb_strerror(ret)));
+ return NT_STATUS_FOOBAR;
+ }
+
+ talloc_free(s_dsa);
+ talloc_free(objs);
+
+ /* reopen the ldb */
+ talloc_free(s->ldb); /* this also free's the s->schema, because dsdb_set_schema() steals it */
+ s->schema = NULL;
+
+ DEBUG(0,("Reopen the SAM LDB with system credentials and a already stored schema\n"));
+ s->ldb = samdb_connect(s, s->event_ctx, s->lp_ctx,
+ system_session(s, s->lp_ctx));
+ if (!s->ldb) {
+ DEBUG(0,("Failed to reopen sam.ldb\n"));
+ return NT_STATUS_INTERNAL_DB_ERROR;
+ }
+
+ /* We must set these up to ensure the replMetaData is written correctly, before our NTDS Settings entry is replicated */
+ ok = samdb_set_ntds_invocation_id(s->ldb, &c->dest_dsa->invocation_id);
+ if (!ok) {
+ DEBUG(0,("Failed to set cached ntds invocationId\n"));
+ return NT_STATUS_FOOBAR;
+ }
+ ok = samdb_set_ntds_objectGUID(s->ldb, &c->dest_dsa->ntds_guid);
+ if (!ok) {
+ DEBUG(0,("Failed to set cached ntds objectGUID\n"));
+ return NT_STATUS_FOOBAR;
+ }
+
+ s->schema = dsdb_get_schema(s->ldb);
+ if (!s->schema) {
+ DEBUG(0,("Failed to get loaded dsdb_schema\n"));
+ return NT_STATUS_FOOBAR;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS vampire_schema_chunk(void *private_data,
+ const struct libnet_BecomeDC_StoreChunk *c)
+{
+ struct vampire_state *s = talloc_get_type(private_data, struct vampire_state);
+ WERROR status;
+ const struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr;
+ uint32_t nc_object_count;
+ uint32_t object_count;
+ struct drsuapi_DsReplicaObjectListItemEx *first_object;
+ struct drsuapi_DsReplicaObjectListItemEx *cur;
+ uint32_t nc_linked_attributes_count;
+ uint32_t linked_attributes_count;
+
+ switch (c->ctr_level) {
+ case 1:
+ mapping_ctr = &c->ctr1->mapping_ctr;
+ nc_object_count = c->ctr1->extended_ret; /* maybe w2k send this unexpected? */
+ object_count = c->ctr1->object_count;
+ first_object = c->ctr1->first_object;
+ nc_linked_attributes_count = 0;
+ linked_attributes_count = 0;
+ break;
+ case 6:
+ mapping_ctr = &c->ctr6->mapping_ctr;
+ nc_object_count = c->ctr6->nc_object_count;
+ object_count = c->ctr6->object_count;
+ first_object = c->ctr6->first_object;
+ nc_linked_attributes_count = c->ctr6->nc_linked_attributes_count;
+ linked_attributes_count = c->ctr6->linked_attributes_count;
+ break;
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (nc_object_count) {
+ DEBUG(0,("Schema-DN[%s] objects[%u/%u] linked_values[%u/%u]\n",
+ c->partition->nc.dn, object_count, nc_object_count,
+ linked_attributes_count, nc_linked_attributes_count));
+ } else {
+ DEBUG(0,("Schema-DN[%s] objects[%u] linked_values[%u\n",
+ c->partition->nc.dn, object_count, linked_attributes_count));
+ }
+
+ if (!s->schema) {
+ s->self_made_schema = dsdb_new_schema(s, lp_iconv_convenience(s->lp_ctx));
+
+ NT_STATUS_HAVE_NO_MEMORY(s->self_made_schema);
+
+ status = dsdb_load_oid_mappings_drsuapi(s->self_made_schema, mapping_ctr);
+ if (!W_ERROR_IS_OK(status)) {
+ return werror_to_ntstatus(status);
+ }
+
+ s->schema = s->self_made_schema;
+ } else {
+ status = dsdb_verify_oid_mappings_drsuapi(s->schema, mapping_ctr);
+ if (!W_ERROR_IS_OK(status)) {
+ return werror_to_ntstatus(status);
+ }
+ }
+
+ if (!s->schema_part.first_object) {
+ s->schema_part.object_count = object_count;
+ s->schema_part.first_object = talloc_steal(s, first_object);
+ } else {
+ s->schema_part.object_count += object_count;
+ s->schema_part.last_object->next_object = talloc_steal(s->schema_part.last_object,
+ first_object);
+ }
+ for (cur = first_object; cur->next_object; cur = cur->next_object) {}
+ s->schema_part.last_object = cur;
+
+ if (!c->partition->more_data) {
+ return vampire_apply_schema(s, c);
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS vampire_store_chunk(void *private_data,
+ const struct libnet_BecomeDC_StoreChunk *c)
+{
+ struct vampire_state *s = talloc_get_type(private_data, struct vampire_state);
+ WERROR status;
+ const struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr;
+ uint32_t nc_object_count;
+ uint32_t object_count;
+ struct drsuapi_DsReplicaObjectListItemEx *first_object;
+ uint32_t nc_linked_attributes_count;
+ uint32_t linked_attributes_count;
+ struct drsuapi_DsReplicaLinkedAttribute *linked_attributes;
+ const struct drsuapi_DsReplicaCursor2CtrEx *uptodateness_vector;
+ struct dsdb_extended_replicated_objects *objs;
+ struct repsFromTo1 *s_dsa;
+ char *tmp_dns_name;
+ uint32_t i;
+
+ s_dsa = talloc_zero(s, struct repsFromTo1);
+ NT_STATUS_HAVE_NO_MEMORY(s_dsa);
+ s_dsa->other_info = talloc(s_dsa, struct repsFromTo1OtherInfo);
+ NT_STATUS_HAVE_NO_MEMORY(s_dsa->other_info);
+
+ switch (c->ctr_level) {
+ case 1:
+ mapping_ctr = &c->ctr1->mapping_ctr;
+ nc_object_count = c->ctr1->extended_ret; /* maybe w2k send this unexpected? */
+ object_count = c->ctr1->object_count;
+ first_object = c->ctr1->first_object;
+ nc_linked_attributes_count = 0;
+ linked_attributes_count = 0;
+ linked_attributes = NULL;
+ s_dsa->highwatermark = c->ctr1->new_highwatermark;
+ s_dsa->source_dsa_obj_guid = c->ctr1->source_dsa_guid;
+ s_dsa->source_dsa_invocation_id = c->ctr1->source_dsa_invocation_id;
+ uptodateness_vector = NULL; /* TODO: map it */
+ break;
+ case 6:
+ mapping_ctr = &c->ctr6->mapping_ctr;
+ nc_object_count = c->ctr6->nc_object_count;
+ object_count = c->ctr6->object_count;
+ first_object = c->ctr6->first_object;
+ nc_linked_attributes_count = c->ctr6->nc_linked_attributes_count;
+ linked_attributes_count = c->ctr6->linked_attributes_count;
+ linked_attributes = c->ctr6->linked_attributes;
+ s_dsa->highwatermark = c->ctr6->new_highwatermark;
+ s_dsa->source_dsa_obj_guid = c->ctr6->source_dsa_guid;
+ s_dsa->source_dsa_invocation_id = c->ctr6->source_dsa_invocation_id;
+ uptodateness_vector = c->ctr6->uptodateness_vector;
+ break;
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ s_dsa->replica_flags = DRSUAPI_DS_REPLICA_NEIGHBOUR_WRITEABLE
+ | DRSUAPI_DS_REPLICA_NEIGHBOUR_SYNC_ON_STARTUP
+ | DRSUAPI_DS_REPLICA_NEIGHBOUR_DO_SCHEDULED_SYNCS;
+ memset(s_dsa->schedule, 0x11, sizeof(s_dsa->schedule));
+
+ tmp_dns_name = GUID_string(s_dsa->other_info, &s_dsa->source_dsa_obj_guid);
+ NT_STATUS_HAVE_NO_MEMORY(tmp_dns_name);
+ tmp_dns_name = talloc_asprintf_append_buffer(tmp_dns_name, "._msdcs.%s", c->forest->dns_name);
+ NT_STATUS_HAVE_NO_MEMORY(tmp_dns_name);
+ s_dsa->other_info->dns_name = tmp_dns_name;
+
+ if (nc_object_count) {
+ DEBUG(0,("Partition[%s] objects[%u/%u] linked_values[%u/%u]\n",
+ c->partition->nc.dn, object_count, nc_object_count,
+ linked_attributes_count, nc_linked_attributes_count));
+ } else {
+ DEBUG(0,("Partition[%s] objects[%u] linked_values[%u\n",
+ c->partition->nc.dn, object_count, linked_attributes_count));
+ }
+
+ status = dsdb_extended_replicated_objects_commit(s->ldb,
+ c->partition->nc.dn,
+ mapping_ctr,
+ object_count,
+ first_object,
+ linked_attributes_count,
+ linked_attributes,
+ s_dsa,
+ uptodateness_vector,
+ c->gensec_skey,
+ s, &objs);
+ if (!W_ERROR_IS_OK(status)) {
+ DEBUG(0,("Failed to commit objects: %s\n", win_errstr(status)));
+ return werror_to_ntstatus(status);
+ }
+
+ if (lp_parm_bool(s->lp_ctx, NULL, "become dc", "dump objects", false)) {
+ for (i=0; i < objs->num_objects; i++) {
+ struct ldb_ldif ldif;
+ fprintf(stdout, "#\n");
+ ldif.changetype = LDB_CHANGETYPE_NONE;
+ ldif.msg = objs->objects[i].msg;
+ ldb_ldif_write_file(s->ldb, stdout, &ldif);
+ NDR_PRINT_DEBUG(replPropertyMetaDataBlob, objs->objects[i].meta_data);
+ }
+ }
+ talloc_free(s_dsa);
+ talloc_free(objs);
+
+ for (i=0; i < linked_attributes_count; i++) {
+ const struct dsdb_attribute *sa;
+
+ if (!linked_attributes[i].identifier) {
+ return NT_STATUS_FOOBAR;
+ }
+
+ if (!linked_attributes[i].value.blob) {
+ return NT_STATUS_FOOBAR;
+ }
+
+ sa = dsdb_attribute_by_attributeID_id(s->schema,
+ linked_attributes[i].attid);
+ if (!sa) {
+ return NT_STATUS_FOOBAR;
+ }
+
+ if (lp_parm_bool(s->lp_ctx, NULL, "become dc", "dump objects", false)) {
+ DEBUG(0,("# %s\n", sa->lDAPDisplayName));
+ NDR_PRINT_DEBUG(drsuapi_DsReplicaLinkedAttribute, &linked_attributes[i]);
+ dump_data(0,
+ linked_attributes[i].value.blob->data,
+ linked_attributes[i].value.blob->length);
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS libnet_Vampire(struct libnet_context *ctx, TALLOC_CTX *mem_ctx,
+ struct libnet_Vampire *r)
+{
+ struct libnet_JoinDomain *join;
+ struct libnet_set_join_secrets *set_secrets;
+ struct libnet_BecomeDC b;
+ struct vampire_state *s;
+ struct ldb_message *msg;
+ int ldb_ret;
+ uint32_t i;
+ NTSTATUS status;
+
+ const char *account_name;
+ const char *netbios_name;
+
+ r->out.error_string = NULL;
+
+ s = talloc_zero(mem_ctx, struct vampire_state);
+ if (!s) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ s->lp_ctx = ctx->lp_ctx;
+ s->event_ctx = ctx->event_ctx;
+
+ join = talloc_zero(s, struct libnet_JoinDomain);
+ if (!join) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (r->in.netbios_name != NULL) {
+ netbios_name = r->in.netbios_name;
+ } else {
+ netbios_name = talloc_reference(join, lp_netbios_name(ctx->lp_ctx));
+ if (!netbios_name) {
+ r->out.error_string = NULL;
+ talloc_free(s);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ account_name = talloc_asprintf(join, "%s$", netbios_name);
+ if (!account_name) {
+ r->out.error_string = NULL;
+ talloc_free(s);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ join->in.domain_name = r->in.domain_name;
+ join->in.account_name = account_name;
+ join->in.netbios_name = netbios_name;
+ join->in.level = LIBNET_JOINDOMAIN_AUTOMATIC;
+ join->in.acct_type = ACB_WSTRUST;
+ join->in.recreate_account = false;
+ status = libnet_JoinDomain(ctx, join, join);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->out.error_string = talloc_steal(mem_ctx, join->out.error_string);
+ talloc_free(s);
+ return status;
+ }
+
+ s->join = join;
+
+ s->targetdir = r->in.targetdir;
+
+ ZERO_STRUCT(b);
+ b.in.domain_dns_name = join->out.realm;
+ b.in.domain_netbios_name = join->out.domain_name;
+ b.in.domain_sid = join->out.domain_sid;
+ b.in.source_dsa_address = join->out.samr_binding->host;
+ b.in.dest_dsa_netbios_name = netbios_name;
+
+ b.in.callbacks.private_data = s;
+ b.in.callbacks.check_options = vampire_check_options;
+ b.in.callbacks.prepare_db = vampire_prepare_db;
+ b.in.callbacks.schema_chunk = vampire_schema_chunk;
+ b.in.callbacks.config_chunk = vampire_store_chunk;
+ b.in.callbacks.domain_chunk = vampire_store_chunk;
+
+ status = libnet_BecomeDC(ctx, s, &b);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("libnet_BecomeDC() failed - %s\n", nt_errstr(status));
+ talloc_free(s);
+ return status;
+ }
+
+ msg = ldb_msg_new(s);
+ if (!msg) {
+ printf("ldb_msg_new() failed\n");
+ talloc_free(s);
+ return NT_STATUS_NO_MEMORY;
+ }
+ msg->dn = ldb_dn_new(msg, s->ldb, "@ROOTDSE");
+ if (!msg->dn) {
+ printf("ldb_msg_new(@ROOTDSE) failed\n");
+ talloc_free(s);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ldb_ret = ldb_msg_add_string(msg, "isSynchronized", "TRUE");
+ if (ldb_ret != LDB_SUCCESS) {
+ printf("ldb_msg_add_string(msg, isSynchronized, TRUE) failed: %d\n", ldb_ret);
+ talloc_free(s);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0; i < msg->num_elements; i++) {
+ msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
+ }
+
+ printf("mark ROOTDSE with isSynchronized=TRUE\n");
+ ldb_ret = ldb_modify(s->ldb, msg);
+ if (ldb_ret != LDB_SUCCESS) {
+ printf("ldb_modify() failed: %d\n", ldb_ret);
+ talloc_free(s);
+ return NT_STATUS_INTERNAL_DB_ERROR;
+ }
+
+ set_secrets = talloc_zero(s, struct libnet_set_join_secrets);
+ if (!set_secrets) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ set_secrets->in.domain_name = join->out.domain_name;
+ set_secrets->in.realm = join->out.realm;
+ set_secrets->in.account_name = account_name;
+ set_secrets->in.netbios_name = netbios_name;
+ set_secrets->in.join_type = SEC_CHAN_BDC;
+ set_secrets->in.join_password = join->out.join_password;
+ set_secrets->in.kvno = join->out.kvno;
+ set_secrets->in.domain_sid = join->out.domain_sid;
+
+ status = libnet_set_join_secrets(ctx, set_secrets, set_secrets);
+ if (!NT_STATUS_IS_OK(status)) {
+ r->out.error_string = talloc_steal(mem_ctx, set_secrets->out.error_string);
+ talloc_free(s);
+ return status;
+ }
+
+ r->out.domain_name = talloc_steal(r, join->out.domain_name);
+ r->out.domain_sid = talloc_steal(r, join->out.domain_sid);
+ talloc_free(s);
+
+ return NT_STATUS_OK;
+
+}
diff --git a/source4/libnet/libnet_vampire.h b/source4/libnet/libnet_vampire.h
new file mode 100644
index 0000000000..5e0c7594b2
--- /dev/null
+++ b/source4/libnet/libnet_vampire.h
@@ -0,0 +1,40 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Stefan Metzmacher 2004
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __LIBNET_VAMPIRE_H__
+#define __LIBNET_VAMPIRE_H__
+
+struct libnet_Vampire {
+ struct {
+ const char *domain_name;
+ const char *netbios_name;
+ const char *targetdir;
+ } in;
+
+ struct {
+ struct dom_sid *domain_sid;
+ const char *domain_name;
+ const char *error_string;
+ } out;
+};
+
+
+#endif /* __LIBNET_VAMPIRE_H__ */
diff --git a/source4/libnet/prereq_domain.c b/source4/libnet/prereq_domain.c
new file mode 100644
index 0000000000..e10e550585
--- /dev/null
+++ b/source4/libnet/prereq_domain.c
@@ -0,0 +1,142 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+
+#include "includes.h"
+#include "libnet/libnet.h"
+#include "libcli/composite/composite.h"
+#include "auth/credentials/credentials.h"
+#include "librpc/ndr/libndr.h"
+#include "librpc/gen_ndr/samr.h"
+#include "librpc/gen_ndr/ndr_samr.h"
+#include "librpc/gen_ndr/lsa.h"
+#include "librpc/gen_ndr/ndr_lsa.h"
+
+
+bool samr_domain_opened(struct libnet_context *ctx, const char *domain_name,
+ struct composite_context **parent_ctx,
+ struct libnet_DomainOpen *domain_open,
+ void (*continue_fn)(struct composite_context*),
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *domopen_req;
+
+ if (parent_ctx == NULL || *parent_ctx == NULL) return false;
+
+ if (domain_name == NULL) {
+ /*
+ * Try to guess the domain name from credentials,
+ * if it's not been explicitly specified.
+ */
+
+ if (policy_handle_empty(&ctx->samr.handle)) {
+ domain_open->in.type = DOMAIN_SAMR;
+ domain_open->in.domain_name = cli_credentials_get_domain(ctx->cred);
+ domain_open->in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+
+ } else {
+ composite_error(*parent_ctx, NT_STATUS_INVALID_PARAMETER);
+ return true;
+ }
+
+ } else {
+ /*
+ * The domain name has been specified, so check whether the same
+ * domain is already opened. If it is - just return NULL. Start
+ * opening a new domain otherwise.
+ */
+
+ if (policy_handle_empty(&ctx->samr.handle) ||
+ !strequal(domain_name, ctx->samr.name)) {
+ domain_open->in.type = DOMAIN_SAMR;
+ domain_open->in.domain_name = domain_name;
+ domain_open->in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+
+ } else {
+ /* domain has already been opened and it's the same domain
+ as requested */
+ return true;
+ }
+ }
+
+ /* send request to open the domain */
+ domopen_req = libnet_DomainOpen_send(ctx, domain_open, monitor);
+ if (composite_nomem(domopen_req, *parent_ctx)) return false;
+
+ composite_continue(*parent_ctx, domopen_req, continue_fn, *parent_ctx);
+ return false;
+}
+
+
+bool lsa_domain_opened(struct libnet_context *ctx, const char *domain_name,
+ struct composite_context **parent_ctx,
+ struct libnet_DomainOpen *domain_open,
+ void (*continue_fn)(struct composite_context*),
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *domopen_req;
+
+ if (parent_ctx == NULL || *parent_ctx == NULL) return false;
+
+ if (domain_name == NULL) {
+ /*
+ * Try to guess the domain name from credentials,
+ * if it's not been explicitly specified.
+ */
+
+ if (policy_handle_empty(&ctx->lsa.handle)) {
+ domain_open->in.type = DOMAIN_LSA;
+ domain_open->in.domain_name = cli_credentials_get_domain(ctx->cred);
+ domain_open->in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+
+ } else {
+ composite_error(*parent_ctx, NT_STATUS_INVALID_PARAMETER);
+ /* this ensures the calling function exits and composite function error
+ gets noticed quickly */
+ return true;
+ }
+
+ } else {
+ /*
+ * The domain name has been specified, so check whether the same
+ * domain is already opened. If it is - just return NULL. Start
+ * opening a new domain otherwise.
+ */
+
+ if (policy_handle_empty(&ctx->lsa.handle) ||
+ !strequal(domain_name, ctx->lsa.name)) {
+ domain_open->in.type = DOMAIN_LSA;
+ domain_open->in.domain_name = domain_name;
+ domain_open->in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+
+ } else {
+ /* domain has already been opened and it's the same domain
+ as requested */
+ return true;
+ }
+ }
+
+ /* send request to open the domain */
+ domopen_req = libnet_DomainOpen_send(ctx, domain_open, monitor);
+ /* see the comment above to find out why true is returned here */
+ if (composite_nomem(domopen_req, *parent_ctx)) return true;
+
+ composite_continue(*parent_ctx, domopen_req, continue_fn, *parent_ctx);
+ return false;
+}
diff --git a/source4/libnet/py_net.c b/source4/libnet/py_net.c
new file mode 100644
index 0000000000..4770cff82d
--- /dev/null
+++ b/source4/libnet/py_net.c
@@ -0,0 +1,88 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Jelmer Vernooij <jelmer@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include <Python.h>
+#include "libnet.h"
+#include "libcli/security/security.h"
+#include "lib/events/events.h"
+#include "param/param.h"
+
+/* FIXME: This prototype should be in param/pyparam.h */
+struct loadparm_context *py_default_loadparm_context(TALLOC_CTX *mem_ctx);
+
+static struct libnet_context *py_net_ctx(PyObject *obj, struct tevent_context *ev)
+{
+ /* FIXME: Use obj */
+ return libnet_context_init(ev, py_default_loadparm_context(NULL));
+}
+
+static PyObject *py_net_join(PyObject *cls, PyObject *args, PyObject *kwargs)
+{
+ struct libnet_Join r;
+ NTSTATUS status;
+ PyObject *result;
+ TALLOC_CTX *mem_ctx;
+ struct tevent_context *ev;
+ struct libnet_context *libnet_ctx;
+ const char *kwnames[] = { "domain_name", "netbios_name", "join_type", "level", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ssii:Join", discard_const_p(char *, kwnames),
+ &r.in.domain_name, &r.in.netbios_name,
+ &r.in.join_type, &r.in.level))
+ return NULL;
+
+ /* FIXME: we really need to get a context from the caller or we may end
+ * up with 2 event contexts */
+ ev = s4_event_context_init(NULL);
+ mem_ctx = talloc_new(ev);
+
+ libnet_ctx = py_net_ctx(cls, ev);
+
+ status = libnet_Join(libnet_ctx, mem_ctx, &r);
+ if (NT_STATUS_IS_ERR(status)) {
+ PyErr_SetString(PyExc_RuntimeError, r.out.error_string);
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ result = Py_BuildValue("sss", r.out.join_password,
+ dom_sid_string(mem_ctx, r.out.domain_sid),
+ r.out.domain_name);
+
+ talloc_free(mem_ctx);
+
+ if (result == NULL)
+ return NULL;
+
+ return result;
+}
+
+static char py_net_join_doc[] = "join(domain_name, netbios_name, join_type, level) -> (join_password, domain_sid, domain_name)\n\n" \
+"Join the domain with the specified name.";
+
+static struct PyMethodDef net_methods[] = {
+ {"Join", (PyCFunction)py_net_join, METH_VARARGS|METH_KEYWORDS, py_net_join_doc},
+ {NULL }
+};
+
+void initnet(void)
+{
+ Py_InitModule("net", net_methods);
+}
diff --git a/source4/libnet/userinfo.c b/source4/libnet/userinfo.c
new file mode 100644
index 0000000000..a718ab9e85
--- /dev/null
+++ b/source4/libnet/userinfo.c
@@ -0,0 +1,363 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ a composite function for getting user information via samr pipe
+*/
+
+#include "includes.h"
+#include "libcli/composite/composite.h"
+#include "librpc/gen_ndr/security.h"
+#include "libcli/security/security.h"
+#include "libnet/libnet.h"
+#include "librpc/gen_ndr/ndr_samr_c.h"
+
+
+struct userinfo_state {
+ struct dcerpc_pipe *pipe;
+ struct policy_handle domain_handle;
+ struct policy_handle user_handle;
+ uint16_t level;
+ struct samr_LookupNames lookup;
+ struct samr_OpenUser openuser;
+ struct samr_QueryUserInfo queryuserinfo;
+ struct samr_Close samrclose;
+ union samr_UserInfo *info;
+
+ /* information about the progress */
+ void (*monitor_fn)(struct monitor_msg *);
+};
+
+
+static void continue_userinfo_lookup(struct rpc_request *req);
+static void continue_userinfo_openuser(struct rpc_request *req);
+static void continue_userinfo_getuser(struct rpc_request *req);
+static void continue_userinfo_closeuser(struct rpc_request *req);
+
+
+/**
+ * Stage 1 (optional): Look for a username in SAM server.
+ */
+static void continue_userinfo_lookup(struct rpc_request *req)
+{
+ struct composite_context *c;
+ struct userinfo_state *s;
+ struct rpc_request *openuser_req;
+ struct monitor_msg msg;
+ struct msg_rpc_lookup_name *msg_lookup;
+
+ c = talloc_get_type(req->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct userinfo_state);
+
+ /* receive samr_Lookup reply */
+ c->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(c)) return;
+
+ /* there could be a problem with name resolving itself */
+ if (!NT_STATUS_IS_OK(s->lookup.out.result)) {
+ composite_error(c, s->lookup.out.result);
+ return;
+ }
+
+ /* issue a monitor message */
+ if (s->monitor_fn) {
+ msg.type = mon_SamrLookupName;
+ msg_lookup = talloc(s, struct msg_rpc_lookup_name);
+ msg_lookup->rid = s->lookup.out.rids->ids;
+ msg_lookup->count = s->lookup.out.rids->count;
+ msg.data = (void*)msg_lookup;
+ msg.data_size = sizeof(*msg_lookup);
+
+ s->monitor_fn(&msg);
+ }
+
+
+ /* have we actually got name resolved
+ - we're looking for only one at the moment */
+ if (s->lookup.out.rids->count == 0) {
+ composite_error(c, NT_STATUS_NO_SUCH_USER);
+ }
+
+ /* TODO: find proper status code for more than one rid found */
+
+ /* prepare parameters for LookupNames */
+ s->openuser.in.domain_handle = &s->domain_handle;
+ s->openuser.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ s->openuser.in.rid = s->lookup.out.rids->ids[0];
+ s->openuser.out.user_handle = &s->user_handle;
+
+ /* send request */
+ openuser_req = dcerpc_samr_OpenUser_send(s->pipe, c, &s->openuser);
+ if (composite_nomem(openuser_req, c)) return;
+
+ composite_continue_rpc(c, openuser_req, continue_userinfo_openuser, c);
+}
+
+
+/**
+ * Stage 2: Open user policy handle.
+ */
+static void continue_userinfo_openuser(struct rpc_request *req)
+{
+ struct composite_context *c;
+ struct userinfo_state *s;
+ struct rpc_request *queryuser_req;
+ struct monitor_msg msg;
+ struct msg_rpc_open_user *msg_open;
+
+ c = talloc_get_type(req->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct userinfo_state);
+
+ /* receive samr_OpenUser reply */
+ c->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(c)) return;
+
+ if (!NT_STATUS_IS_OK(s->queryuserinfo.out.result)) {
+ composite_error(c, s->queryuserinfo.out.result);
+ return;
+ }
+
+ /* issue a monitor message */
+ if (s->monitor_fn) {
+ msg.type = mon_SamrOpenUser;
+ msg_open = talloc(s, struct msg_rpc_open_user);
+ msg_open->rid = s->openuser.in.rid;
+ msg_open->access_mask = s->openuser.in.access_mask;
+ msg.data = (void*)msg_open;
+ msg.data_size = sizeof(*msg_open);
+
+ s->monitor_fn(&msg);
+ }
+
+ /* prepare parameters for QueryUserInfo call */
+ s->queryuserinfo.in.user_handle = &s->user_handle;
+ s->queryuserinfo.in.level = s->level;
+ s->queryuserinfo.out.info = talloc(s, union samr_UserInfo *);
+ if (composite_nomem(s->queryuserinfo.out.info, c)) return;
+
+ /* queue rpc call, set event handling and new state */
+ queryuser_req = dcerpc_samr_QueryUserInfo_send(s->pipe, c, &s->queryuserinfo);
+ if (composite_nomem(queryuser_req, c)) return;
+
+ composite_continue_rpc(c, queryuser_req, continue_userinfo_getuser, c);
+}
+
+
+/**
+ * Stage 3: Get requested user information.
+ */
+static void continue_userinfo_getuser(struct rpc_request *req)
+{
+ struct composite_context *c;
+ struct userinfo_state *s;
+ struct rpc_request *close_req;
+ struct monitor_msg msg;
+ struct msg_rpc_query_user *msg_query;
+
+ c = talloc_get_type(req->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct userinfo_state);
+
+ /* receive samr_QueryUserInfo reply */
+ c->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(c)) return;
+
+ /* check if queryuser itself went ok */
+ if (!NT_STATUS_IS_OK(s->queryuserinfo.out.result)) {
+ composite_error(c, s->queryuserinfo.out.result);
+ return;
+ }
+
+ s->info = talloc_steal(s, *(s->queryuserinfo.out.info));
+
+ /* issue a monitor message */
+ if (s->monitor_fn) {
+ msg.type = mon_SamrQueryUser;
+ msg_query = talloc(s, struct msg_rpc_query_user);
+ msg_query->level = s->queryuserinfo.in.level;
+ msg.data = (void*)msg_query;
+ msg.data_size = sizeof(*msg_query);
+
+ s->monitor_fn(&msg);
+ }
+
+ /* prepare arguments for Close call */
+ s->samrclose.in.handle = &s->user_handle;
+ s->samrclose.out.handle = &s->user_handle;
+
+ /* queue rpc call, set event handling and new state */
+ close_req = dcerpc_samr_Close_send(s->pipe, c, &s->samrclose);
+ if (composite_nomem(close_req, c)) return;
+
+ composite_continue_rpc(c, close_req, continue_userinfo_closeuser, c);
+}
+
+
+/**
+ * Stage 4: Close policy handle associated with opened user.
+ */
+static void continue_userinfo_closeuser(struct rpc_request *req)
+{
+ struct composite_context *c;
+ struct userinfo_state *s;
+ struct monitor_msg msg;
+ struct msg_rpc_close_user *msg_close;
+
+ c = talloc_get_type(req->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct userinfo_state);
+
+ /* receive samr_Close reply */
+ c->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(c)) return;
+
+ if (!NT_STATUS_IS_OK(s->samrclose.out.result)) {
+ composite_error(c, s->samrclose.out.result);
+ return;
+ }
+
+ /* issue a monitor message */
+ if (s->monitor_fn) {
+ msg.type = mon_SamrClose;
+ msg_close = talloc(s, struct msg_rpc_close_user);
+ msg_close->rid = s->openuser.in.rid;
+ msg.data = (void*)msg_close;
+ msg.data_size = sizeof(*msg_close);
+
+ s->monitor_fn(&msg);
+ }
+
+ composite_done(c);
+}
+
+
+/**
+ * Sends asynchronous userinfo request
+ *
+ * @param p dce/rpc call pipe
+ * @param io arguments and results of the call
+ */
+struct composite_context *libnet_rpc_userinfo_send(struct dcerpc_pipe *p,
+ struct libnet_rpc_userinfo *io,
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *c;
+ struct userinfo_state *s;
+ struct dom_sid *sid;
+ struct rpc_request *openuser_req, *lookup_req;
+
+ if (!p || !io) return NULL;
+
+ c = composite_create(p, dcerpc_event_context(p));
+ if (c == NULL) return c;
+
+ s = talloc_zero(c, struct userinfo_state);
+ if (composite_nomem(s, c)) return c;
+
+ c->private_data = s;
+
+ s->level = io->in.level;
+ s->pipe = p;
+ s->domain_handle = io->in.domain_handle;
+ s->monitor_fn = monitor;
+
+ if (io->in.sid) {
+ sid = dom_sid_parse_talloc(s, io->in.sid);
+ if (composite_nomem(sid, c)) return c;
+
+ s->openuser.in.domain_handle = &s->domain_handle;
+ s->openuser.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ s->openuser.in.rid = sid->sub_auths[sid->num_auths - 1];
+ s->openuser.out.user_handle = &s->user_handle;
+
+ /* send request */
+ openuser_req = dcerpc_samr_OpenUser_send(p, c, &s->openuser);
+ if (composite_nomem(openuser_req, c)) return c;
+
+ composite_continue_rpc(c, openuser_req, continue_userinfo_openuser, c);
+
+ } else {
+ /* preparing parameters to send rpc request */
+ s->lookup.in.domain_handle = &s->domain_handle;
+ s->lookup.in.num_names = 1;
+ s->lookup.in.names = talloc_array(s, struct lsa_String, 1);
+ if (composite_nomem(s->lookup.in.names, c)) return c;
+ s->lookup.out.rids = talloc_zero(s, struct samr_Ids);
+ s->lookup.out.types = talloc_zero(s, struct samr_Ids);
+ if (composite_nomem(s->lookup.out.rids, c)) return c;
+ if (composite_nomem(s->lookup.out.types, c)) return c;
+
+ s->lookup.in.names[0].string = talloc_strdup(s, io->in.username);
+ if (composite_nomem(s->lookup.in.names[0].string, c)) return c;
+
+ /* send request */
+ lookup_req = dcerpc_samr_LookupNames_send(p, c, &s->lookup);
+ if (composite_nomem(lookup_req, c)) return c;
+
+ composite_continue_rpc(c, lookup_req, continue_userinfo_lookup, c);
+ }
+
+ return c;
+}
+
+
+/**
+ * Waits for and receives result of asynchronous userinfo call
+ *
+ * @param c composite context returned by asynchronous userinfo call
+ * @param mem_ctx memory context of the call
+ * @param io pointer to results (and arguments) of the call
+ * @return nt status code of execution
+ */
+
+NTSTATUS libnet_rpc_userinfo_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
+ struct libnet_rpc_userinfo *io)
+{
+ NTSTATUS status;
+ struct userinfo_state *s;
+
+ /* wait for results of sending request */
+ status = composite_wait(c);
+
+ if (NT_STATUS_IS_OK(status) && io) {
+ s = talloc_get_type(c->private_data, struct userinfo_state);
+ talloc_steal(mem_ctx, s->info);
+ io->out.info = *s->info;
+ }
+
+ /* memory context associated to composite context is no longer needed */
+ talloc_free(c);
+ return status;
+}
+
+
+/**
+ * Synchronous version of userinfo call
+ *
+ * @param pipe dce/rpc call pipe
+ * @param mem_ctx memory context for the call
+ * @param io arguments and results of the call
+ * @return nt status code of execution
+ */
+
+NTSTATUS libnet_rpc_userinfo(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_rpc_userinfo *io)
+{
+ struct composite_context *c = libnet_rpc_userinfo_send(p, io, NULL);
+ return libnet_rpc_userinfo_recv(c, mem_ctx, io);
+}
diff --git a/source4/libnet/userinfo.h b/source4/libnet/userinfo.h
new file mode 100644
index 0000000000..273ad87fcd
--- /dev/null
+++ b/source4/libnet/userinfo.h
@@ -0,0 +1,54 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+
+#include "librpc/gen_ndr/samr.h"
+
+/*
+ * IO structures for userinfo.c functions
+ */
+
+struct libnet_rpc_userinfo {
+ struct {
+ struct policy_handle domain_handle;
+ const char *username;
+ const char *sid;
+ uint16_t level;
+ } in;
+ struct {
+ union samr_UserInfo info;
+ } out;
+};
+
+
+/*
+ * Monitor messages sent from userinfo.c functions
+ */
+
+struct msg_rpc_open_user {
+ uint32_t rid, access_mask;
+};
+
+struct msg_rpc_query_user {
+ uint16_t level;
+};
+
+struct msg_rpc_close_user {
+ uint32_t rid;
+};
diff --git a/source4/libnet/userman.c b/source4/libnet/userman.c
new file mode 100644
index 0000000000..62d4e0edd2
--- /dev/null
+++ b/source4/libnet/userman.c
@@ -0,0 +1,892 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ a composite functions for user management operations (add/del/chg)
+*/
+
+#include "includes.h"
+#include "libcli/composite/composite.h"
+#include "libnet/libnet.h"
+#include "librpc/gen_ndr/ndr_samr_c.h"
+
+/*
+ * Composite USER ADD functionality
+ */
+
+struct useradd_state {
+ struct dcerpc_pipe *pipe;
+ struct rpc_request *req;
+ struct policy_handle domain_handle;
+ struct samr_CreateUser createuser;
+ struct policy_handle user_handle;
+ uint32_t user_rid;
+
+ /* information about the progress */
+ void (*monitor_fn)(struct monitor_msg *);
+};
+
+
+static void continue_useradd_create(struct rpc_request *req);
+
+
+/**
+ * Stage 1 (and the only one for now): Create user account.
+ */
+static void continue_useradd_create(struct rpc_request *req)
+{
+ struct composite_context *c;
+ struct useradd_state *s;
+
+ c = talloc_get_type(req->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct useradd_state);
+
+ /* check rpc layer status code */
+ c->status = dcerpc_ndr_request_recv(s->req);
+ if (!composite_is_ok(c)) return;
+
+ /* check create user call status code */
+ c->status = s->createuser.out.result;
+
+ /* get created user account data */
+ s->user_handle = *s->createuser.out.user_handle;
+ s->user_rid = *s->createuser.out.rid;
+
+ /* issue a monitor message */
+ if (s->monitor_fn) {
+ struct monitor_msg msg;
+ struct msg_rpc_create_user rpc_create;
+
+ rpc_create.rid = *s->createuser.out.rid;
+
+ msg.type = mon_SamrCreateUser;
+ msg.data = (void*)&rpc_create;
+ msg.data_size = sizeof(rpc_create);
+
+ s->monitor_fn(&msg);
+ }
+
+ composite_done(c);
+}
+
+
+/**
+ * Sends asynchronous useradd request
+ *
+ * @param p dce/rpc call pipe
+ * @param io arguments and results of the call
+ * @param monitor monitor function for providing information about the progress
+ */
+
+struct composite_context *libnet_rpc_useradd_send(struct dcerpc_pipe *p,
+ struct libnet_rpc_useradd *io,
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *c;
+ struct useradd_state *s;
+
+ if (!p || !io) return NULL;
+
+ /* composite allocation and setup */
+ c = composite_create(p, dcerpc_event_context(p));
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct useradd_state);
+ if (composite_nomem(s, c)) return c;
+
+ c->private_data = s;
+
+ /* put passed arguments to the state structure */
+ s->domain_handle = io->in.domain_handle;
+ s->pipe = p;
+ s->monitor_fn = monitor;
+
+ /* preparing parameters to send rpc request */
+ s->createuser.in.domain_handle = &io->in.domain_handle;
+
+ s->createuser.in.account_name = talloc_zero(c, struct lsa_String);
+ if (composite_nomem(s->createuser.in.account_name, c)) return c;
+
+ s->createuser.in.account_name->string = talloc_strdup(c, io->in.username);
+ if (composite_nomem(s->createuser.in.account_name->string, c)) return c;
+
+ s->createuser.out.user_handle = &s->user_handle;
+ s->createuser.out.rid = &s->user_rid;
+
+ /* send the request */
+ s->req = dcerpc_samr_CreateUser_send(p, c, &s->createuser);
+ if (composite_nomem(s->req, c)) return c;
+
+ composite_continue_rpc(c, s->req, continue_useradd_create, c);
+ return c;
+}
+
+
+/**
+ * Waits for and receives result of asynchronous useradd call
+ *
+ * @param c composite context returned by asynchronous useradd call
+ * @param mem_ctx memory context of the call
+ * @param io pointer to results (and arguments) of the call
+ * @return nt status code of execution
+ */
+
+NTSTATUS libnet_rpc_useradd_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
+ struct libnet_rpc_useradd *io)
+{
+ NTSTATUS status;
+ struct useradd_state *s;
+
+ status = composite_wait(c);
+
+ if (NT_STATUS_IS_OK(status) && io) {
+ /* get and return result of the call */
+ s = talloc_get_type(c->private_data, struct useradd_state);
+ io->out.user_handle = s->user_handle;
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+
+/**
+ * Synchronous version of useradd call
+ *
+ * @param pipe dce/rpc call pipe
+ * @param mem_ctx memory context for the call
+ * @param io arguments and results of the call
+ * @return nt status code of execution
+ */
+
+NTSTATUS libnet_rpc_useradd(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_rpc_useradd *io)
+{
+ struct composite_context *c = libnet_rpc_useradd_send(p, io, NULL);
+ return libnet_rpc_useradd_recv(c, mem_ctx, io);
+}
+
+
+
+/*
+ * Composite USER DELETE functionality
+ */
+
+
+struct userdel_state {
+ struct dcerpc_pipe *pipe;
+ struct policy_handle domain_handle;
+ struct policy_handle user_handle;
+ struct samr_LookupNames lookupname;
+ struct samr_OpenUser openuser;
+ struct samr_DeleteUser deleteuser;
+
+ /* information about the progress */
+ void (*monitor_fn)(struct monitor_msg *);
+};
+
+
+static void continue_userdel_name_found(struct rpc_request *req);
+static void continue_userdel_user_opened(struct rpc_request* req);
+static void continue_userdel_deleted(struct rpc_request *req);
+
+
+/**
+ * Stage 1: Lookup the user name and resolve it to rid
+ */
+static void continue_userdel_name_found(struct rpc_request *req)
+{
+ struct composite_context *c;
+ struct userdel_state *s;
+ struct rpc_request *openuser_req;
+ struct monitor_msg msg;
+
+ c = talloc_get_type(req->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct userdel_state);
+
+ /* receive samr_LookupNames result */
+ c->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(c)) return;
+
+ c->status = s->lookupname.out.result;
+ if (!NT_STATUS_IS_OK(c->status)) {
+ composite_error(c, c->status);
+ return;
+ }
+
+ /* what to do when there's no user account to delete
+ and what if there's more than one rid resolved */
+ if (!s->lookupname.out.rids->count) {
+ c->status = NT_STATUS_NO_SUCH_USER;
+ composite_error(c, c->status);
+ return;
+
+ } else if (!s->lookupname.out.rids->count > 1) {
+ c->status = NT_STATUS_INVALID_ACCOUNT_NAME;
+ composite_error(c, c->status);
+ return;
+ }
+
+ /* issue a monitor message */
+ if (s->monitor_fn) {
+ struct msg_rpc_lookup_name msg_lookup;
+
+ msg_lookup.rid = s->lookupname.out.rids->ids;
+ msg_lookup.count = s->lookupname.out.rids->count;
+
+ msg.type = mon_SamrLookupName;
+ msg.data = (void*)&msg_lookup;
+ msg.data_size = sizeof(msg_lookup);
+ s->monitor_fn(&msg);
+ }
+
+ /* prepare the arguments for rpc call */
+ s->openuser.in.domain_handle = &s->domain_handle;
+ s->openuser.in.rid = s->lookupname.out.rids->ids[0];
+ s->openuser.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ s->openuser.out.user_handle = &s->user_handle;
+
+ /* send rpc request */
+ openuser_req = dcerpc_samr_OpenUser_send(s->pipe, c, &s->openuser);
+ if (composite_nomem(openuser_req, c)) return;
+
+ composite_continue_rpc(c, openuser_req, continue_userdel_user_opened, c);
+}
+
+
+/**
+ * Stage 2: Open user account.
+ */
+static void continue_userdel_user_opened(struct rpc_request* req)
+{
+ struct composite_context *c;
+ struct userdel_state *s;
+ struct rpc_request *deluser_req;
+ struct monitor_msg msg;
+
+ c = talloc_get_type(req->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct userdel_state);
+
+ /* receive samr_OpenUser result */
+ c->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(c)) return;
+
+ c->status = s->openuser.out.result;
+ if (!NT_STATUS_IS_OK(c->status)) {
+ composite_error(c, c->status);
+ return;
+ }
+
+ /* issue a monitor message */
+ if (s->monitor_fn) {
+ struct msg_rpc_open_user msg_open;
+
+ msg_open.rid = s->openuser.in.rid;
+ msg_open.access_mask = s->openuser.in.access_mask;
+
+ msg.type = mon_SamrOpenUser;
+ msg.data = (void*)&msg_open;
+ msg.data_size = sizeof(msg_open);
+ s->monitor_fn(&msg);
+ }
+
+ /* prepare the final rpc call arguments */
+ s->deleteuser.in.user_handle = &s->user_handle;
+ s->deleteuser.out.user_handle = &s->user_handle;
+
+ /* send rpc request */
+ deluser_req = dcerpc_samr_DeleteUser_send(s->pipe, c, &s->deleteuser);
+ if (composite_nomem(deluser_req, c)) return;
+
+ /* callback handler setup */
+ composite_continue_rpc(c, deluser_req, continue_userdel_deleted, c);
+}
+
+
+/**
+ * Stage 3: Delete user account
+ */
+static void continue_userdel_deleted(struct rpc_request *req)
+{
+ struct composite_context *c;
+ struct userdel_state *s;
+ struct monitor_msg msg;
+
+ c = talloc_get_type(req->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct userdel_state);
+
+ /* receive samr_DeleteUser result */
+ c->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(c)) return;
+
+ /* return the actual function call status */
+ c->status = s->deleteuser.out.result;
+ if (!NT_STATUS_IS_OK(c->status)) {
+ composite_error(c, c->status);
+ return;
+ }
+
+ /* issue a monitor message */
+ if (s->monitor_fn) {
+ msg.type = mon_SamrDeleteUser;
+ msg.data = NULL;
+ msg.data_size = 0;
+ s->monitor_fn(&msg);
+ }
+
+ composite_done(c);
+}
+
+
+/**
+ * Sends asynchronous userdel request
+ *
+ * @param p dce/rpc call pipe
+ * @param io arguments and results of the call
+ * @param monitor monitor function for providing information about the progress
+ */
+
+struct composite_context *libnet_rpc_userdel_send(struct dcerpc_pipe *p,
+ struct libnet_rpc_userdel *io,
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *c;
+ struct userdel_state *s;
+ struct rpc_request *lookup_req;
+
+ /* composite context allocation and setup */
+ c = composite_create(p, dcerpc_event_context(p));
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct userdel_state);
+ if (composite_nomem(s, c)) return c;
+
+ c->private_data = s;
+
+ /* store function parameters in the state structure */
+ s->pipe = p;
+ s->domain_handle = io->in.domain_handle;
+ s->monitor_fn = monitor;
+
+ /* preparing parameters to send rpc request */
+ s->lookupname.in.domain_handle = &io->in.domain_handle;
+ s->lookupname.in.num_names = 1;
+ s->lookupname.in.names = talloc_zero(s, struct lsa_String);
+ s->lookupname.in.names->string = io->in.username;
+ s->lookupname.out.rids = talloc_zero(s, struct samr_Ids);
+ s->lookupname.out.types = talloc_zero(s, struct samr_Ids);
+ if (composite_nomem(s->lookupname.out.rids, c)) return c;
+ if (composite_nomem(s->lookupname.out.types, c)) return c;
+
+ /* send the request */
+ lookup_req = dcerpc_samr_LookupNames_send(p, c, &s->lookupname);
+ if (composite_nomem(lookup_req, c)) return c;
+
+ /* set the next stage */
+ composite_continue_rpc(c, lookup_req, continue_userdel_name_found, c);
+ return c;
+}
+
+
+/**
+ * Waits for and receives results of asynchronous userdel call
+ *
+ * @param c composite context returned by asynchronous userdel call
+ * @param mem_ctx memory context of the call
+ * @param io pointer to results (and arguments) of the call
+ * @return nt status code of execution
+ */
+
+NTSTATUS libnet_rpc_userdel_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
+ struct libnet_rpc_userdel *io)
+{
+ NTSTATUS status;
+ struct userdel_state *s;
+
+ status = composite_wait(c);
+
+ if (NT_STATUS_IS_OK(status) && io) {
+ s = talloc_get_type(c->private_data, struct userdel_state);
+ io->out.user_handle = s->user_handle;
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+
+/**
+ * Synchronous version of userdel call
+ *
+ * @param pipe dce/rpc call pipe
+ * @param mem_ctx memory context for the call
+ * @param io arguments and results of the call
+ * @return nt status code of execution
+ */
+
+NTSTATUS libnet_rpc_userdel(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_rpc_userdel *io)
+{
+ struct composite_context *c = libnet_rpc_userdel_send(p, io, NULL);
+ return libnet_rpc_userdel_recv(c, mem_ctx, io);
+}
+
+
+/*
+ * USER MODIFY functionality
+ */
+
+static void continue_usermod_name_found(struct rpc_request *req);
+static void continue_usermod_user_opened(struct rpc_request *req);
+static void continue_usermod_user_queried(struct rpc_request *req);
+static void continue_usermod_user_changed(struct rpc_request *req);
+
+
+struct usermod_state {
+ struct dcerpc_pipe *pipe;
+ struct policy_handle domain_handle;
+ struct policy_handle user_handle;
+ struct usermod_change change;
+ union samr_UserInfo info;
+ struct samr_LookupNames lookupname;
+ struct samr_OpenUser openuser;
+ struct samr_SetUserInfo setuser;
+ struct samr_QueryUserInfo queryuser;
+
+ /* information about the progress */
+ void (*monitor_fn)(struct monitor_msg *);
+};
+
+
+/**
+ * Step 1: Lookup user name
+ */
+static void continue_usermod_name_found(struct rpc_request *req)
+{
+ struct composite_context *c;
+ struct usermod_state *s;
+ struct rpc_request *openuser_req;
+ struct monitor_msg msg;
+
+ c = talloc_get_type(req->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct usermod_state);
+
+ /* receive samr_LookupNames result */
+ c->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(c)) return;
+
+ c->status = s->lookupname.out.result;
+ if (!NT_STATUS_IS_OK(c->status)) {
+ composite_error(c, c->status);
+ return;
+ }
+
+ /* what to do when there's no user account to delete
+ and what if there's more than one rid resolved */
+ if (!s->lookupname.out.rids->count) {
+ c->status = NT_STATUS_NO_SUCH_USER;
+ composite_error(c, c->status);
+ return;
+
+ } else if (!s->lookupname.out.rids->count > 1) {
+ c->status = NT_STATUS_INVALID_ACCOUNT_NAME;
+ composite_error(c, c->status);
+ return;
+ }
+
+ /* issue a monitor message */
+ if (s->monitor_fn) {
+ struct msg_rpc_lookup_name msg_lookup;
+
+ msg_lookup.rid = s->lookupname.out.rids->ids;
+ msg_lookup.count = s->lookupname.out.rids->count;
+
+ msg.type = mon_SamrLookupName;
+ msg.data = (void*)&msg_lookup;
+ msg.data_size = sizeof(msg_lookup);
+ s->monitor_fn(&msg);
+ }
+
+ /* prepare the next rpc call */
+ s->openuser.in.domain_handle = &s->domain_handle;
+ s->openuser.in.rid = s->lookupname.out.rids->ids[0];
+ s->openuser.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ s->openuser.out.user_handle = &s->user_handle;
+
+ /* send the rpc request */
+ openuser_req = dcerpc_samr_OpenUser_send(s->pipe, c, &s->openuser);
+ if (composite_nomem(openuser_req, c)) return;
+
+ composite_continue_rpc(c, openuser_req, continue_usermod_user_opened, c);
+}
+
+
+/**
+ * Choose a proper level of samr_UserInfo structure depending on required
+ * change specified by means of flags field. Subsequent calls of this
+ * function are made until there's no flags set meaning that all of the
+ * changes have been made.
+ */
+static bool usermod_setfields(struct usermod_state *s, uint16_t *level,
+ union samr_UserInfo *i, bool queried)
+{
+ if (s->change.fields == 0) return s->change.fields;
+
+ *level = 0;
+
+ if ((s->change.fields & USERMOD_FIELD_ACCOUNT_NAME) &&
+ (*level == 0 || *level == 7)) {
+ *level = 7;
+ i->info7.account_name.string = s->change.account_name;
+
+ s->change.fields ^= USERMOD_FIELD_ACCOUNT_NAME;
+ }
+
+ if ((s->change.fields & USERMOD_FIELD_FULL_NAME) &&
+ (*level == 0 || *level == 8)) {
+ *level = 8;
+ i->info8.full_name.string = s->change.full_name;
+
+ s->change.fields ^= USERMOD_FIELD_FULL_NAME;
+ }
+
+ if ((s->change.fields & USERMOD_FIELD_DESCRIPTION) &&
+ (*level == 0 || *level == 13)) {
+ *level = 13;
+ i->info13.description.string = s->change.description;
+
+ s->change.fields ^= USERMOD_FIELD_DESCRIPTION;
+ }
+
+ if ((s->change.fields & USERMOD_FIELD_COMMENT) &&
+ (*level == 0 || *level == 2)) {
+ *level = 2;
+
+ if (queried) {
+ /* the user info is obtained, so now set the required field */
+ i->info2.comment.string = s->change.comment;
+ s->change.fields ^= USERMOD_FIELD_COMMENT;
+
+ } else {
+ /* we need to query the user info before setting one field in it */
+ return false;
+ }
+ }
+
+ if ((s->change.fields & USERMOD_FIELD_LOGON_SCRIPT) &&
+ (*level == 0 || *level == 11)) {
+ *level = 11;
+ i->info11.logon_script.string = s->change.logon_script;
+
+ s->change.fields ^= USERMOD_FIELD_LOGON_SCRIPT;
+ }
+
+ if ((s->change.fields & USERMOD_FIELD_PROFILE_PATH) &&
+ (*level == 0 || *level == 12)) {
+ *level = 12;
+ i->info12.profile_path.string = s->change.profile_path;
+
+ s->change.fields ^= USERMOD_FIELD_PROFILE_PATH;
+ }
+
+ if ((s->change.fields & USERMOD_FIELD_HOME_DIRECTORY) &&
+ (*level == 0 || *level == 10)) {
+ *level = 10;
+
+ if (queried) {
+ i->info10.home_directory.string = s->change.home_directory;
+ s->change.fields ^= USERMOD_FIELD_HOME_DIRECTORY;
+ } else {
+ return false;
+ }
+ }
+
+ if ((s->change.fields & USERMOD_FIELD_HOME_DRIVE) &&
+ (*level == 0 || *level == 10)) {
+ *level = 10;
+
+ if (queried) {
+ i->info10.home_drive.string = s->change.home_drive;
+ s->change.fields ^= USERMOD_FIELD_HOME_DRIVE;
+ } else {
+ return false;
+ }
+ }
+
+ if ((s->change.fields & USERMOD_FIELD_ACCT_EXPIRY) &&
+ (*level == 0 || *level == 17)) {
+ *level = 17;
+ i->info17.acct_expiry = timeval_to_nttime(s->change.acct_expiry);
+
+ s->change.fields ^= USERMOD_FIELD_ACCT_EXPIRY;
+ }
+
+ if ((s->change.fields & USERMOD_FIELD_ACCT_FLAGS) &&
+ (*level == 0 || *level == 16)) {
+ *level = 16;
+ i->info16.acct_flags = s->change.acct_flags;
+
+ s->change.fields ^= USERMOD_FIELD_ACCT_FLAGS;
+ }
+
+ /* We're going to be here back again soon unless all fields have been set */
+ return true;
+}
+
+
+static NTSTATUS usermod_change(struct composite_context *c,
+ struct usermod_state *s)
+{
+ struct rpc_request *query_req, *setuser_req;
+ bool do_set;
+ union samr_UserInfo *i = &s->info;
+
+ /* set the level to invalid value, so that unless setfields routine
+ gives it a valid value we report the error correctly */
+ uint16_t level = 27;
+
+ /* prepare UserInfo level and data based on bitmask field */
+ do_set = usermod_setfields(s, &level, i, false);
+
+ if (level < 1 || level > 26) {
+ /* apparently there's a field that the setfields routine
+ does not know how to set */
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* If some specific level is used to set user account data and the change
+ itself does not cover all fields then we need to query the user info
+ first, right before changing the data. Otherwise we could set required
+ fields and accidentally reset the others.
+ */
+ if (!do_set) {
+ s->queryuser.in.user_handle = &s->user_handle;
+ s->queryuser.in.level = level;
+ s->queryuser.out.info = talloc(s, union samr_UserInfo *);
+ if (composite_nomem(s->queryuser.out.info, c)) return NT_STATUS_NO_MEMORY;
+
+
+ /* send query user info request to retrieve complete data of
+ a particular info level */
+ query_req = dcerpc_samr_QueryUserInfo_send(s->pipe, c, &s->queryuser);
+ composite_continue_rpc(c, query_req, continue_usermod_user_queried, c);
+
+ } else {
+ s->setuser.in.user_handle = &s->user_handle;
+ s->setuser.in.level = level;
+ s->setuser.in.info = i;
+
+ /* send set user info request after making required change */
+ setuser_req = dcerpc_samr_SetUserInfo_send(s->pipe, c, &s->setuser);
+ composite_continue_rpc(c, setuser_req, continue_usermod_user_changed, c);
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/**
+ * Stage 2: Open user account
+ */
+static void continue_usermod_user_opened(struct rpc_request *req)
+{
+ struct composite_context *c;
+ struct usermod_state *s;
+
+ c = talloc_get_type(req->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct usermod_state);
+
+ c->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(c)) return;
+
+ c->status = s->openuser.out.result;
+ if (!NT_STATUS_IS_OK(c->status)) {
+ composite_error(c, c->status);
+ return;
+ }
+
+ c->status = usermod_change(c, s);
+}
+
+
+/**
+ * Stage 2a (optional): Query the user information
+ */
+static void continue_usermod_user_queried(struct rpc_request *req)
+{
+ struct composite_context *c;
+ struct usermod_state *s;
+ union samr_UserInfo *i;
+ uint16_t level;
+ struct rpc_request *setuser_req;
+
+ c = talloc_get_type(req->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct usermod_state);
+
+ i = &s->info;
+
+ /* receive samr_QueryUserInfo result */
+ c->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(c)) return;
+
+ c->status = s->queryuser.out.result;
+ if (!NT_STATUS_IS_OK(c->status)) {
+ composite_error(c, c->status);
+ return;
+ }
+
+ /* get returned user data and make a change (potentially one
+ of many) */
+ s->info = *(*s->queryuser.out.info);
+
+ usermod_setfields(s, &level, i, true);
+
+ /* prepare rpc call arguments */
+ s->setuser.in.user_handle = &s->user_handle;
+ s->setuser.in.level = level;
+ s->setuser.in.info = i;
+
+ /* send the rpc request */
+ setuser_req = dcerpc_samr_SetUserInfo_send(s->pipe, c, &s->setuser);
+ composite_continue_rpc(c, setuser_req, continue_usermod_user_changed, c);
+}
+
+
+/**
+ * Stage 3: Set new user account data
+ */
+static void continue_usermod_user_changed(struct rpc_request *req)
+{
+ struct composite_context *c;
+ struct usermod_state *s;
+
+ c = talloc_get_type(req->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct usermod_state);
+
+ /* receive samr_SetUserInfo result */
+ c->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(c)) return;
+
+ /* return the actual function call status */
+ c->status = s->setuser.out.result;
+ if (!NT_STATUS_IS_OK(c->status)) {
+ composite_error(c, c->status);
+ return;
+ }
+
+ if (s->change.fields == 0) {
+ /* all fields have been set - we're done */
+ composite_done(c);
+
+ } else {
+ /* something's still not changed - repeat the procedure */
+ c->status = usermod_change(c, s);
+ }
+}
+
+
+/**
+ * Sends asynchronous usermod request
+ *
+ * @param p dce/rpc call pipe
+ * @param io arguments and results of the call
+ * @param monitor monitor function for providing information about the progress
+ */
+
+struct composite_context *libnet_rpc_usermod_send(struct dcerpc_pipe *p,
+ struct libnet_rpc_usermod *io,
+ void (*monitor)(struct monitor_msg*))
+{
+ struct composite_context *c;
+ struct usermod_state *s;
+ struct rpc_request *lookup_req;
+
+ /* composite context allocation and setup */
+ c = composite_create(p, dcerpc_event_context(p));
+ if (c == NULL) return NULL;
+ s = talloc_zero(c, struct usermod_state);
+ if (composite_nomem(s, c)) return c;
+
+ c->private_data = s;
+
+ /* store parameters in the call structure */
+ s->pipe = p;
+ s->domain_handle = io->in.domain_handle;
+ s->change = io->in.change;
+ s->monitor_fn = monitor;
+
+ /* prepare rpc call arguments */
+ s->lookupname.in.domain_handle = &io->in.domain_handle;
+ s->lookupname.in.num_names = 1;
+ s->lookupname.in.names = talloc_zero(s, struct lsa_String);
+ s->lookupname.in.names->string = io->in.username;
+ s->lookupname.out.rids = talloc_zero(s, struct samr_Ids);
+ s->lookupname.out.types = talloc_zero(s, struct samr_Ids);
+ if (composite_nomem(s->lookupname.out.rids, c)) return c;
+ if (composite_nomem(s->lookupname.out.types, c)) return c;
+
+ /* send the rpc request */
+ lookup_req = dcerpc_samr_LookupNames_send(p, c, &s->lookupname);
+ if (composite_nomem(lookup_req, c)) return c;
+
+ /* callback handler setup */
+ composite_continue_rpc(c, lookup_req, continue_usermod_name_found, c);
+ return c;
+}
+
+
+/**
+ * Waits for and receives results of asynchronous usermod call
+ *
+ * @param c composite context returned by asynchronous usermod call
+ * @param mem_ctx memory context of the call
+ * @param io pointer to results (and arguments) of the call
+ * @return nt status code of execution
+ */
+
+NTSTATUS libnet_rpc_usermod_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
+ struct libnet_rpc_usermod *io)
+{
+ NTSTATUS status;
+
+ status = composite_wait(c);
+
+ talloc_free(c);
+ return status;
+}
+
+
+/**
+ * Synchronous version of usermod call
+ *
+ * @param pipe dce/rpc call pipe
+ * @param mem_ctx memory context for the call
+ * @param io arguments and results of the call
+ * @return nt status code of execution
+ */
+
+NTSTATUS libnet_rpc_usermod(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_rpc_usermod *io)
+{
+ struct composite_context *c = libnet_rpc_usermod_send(p, io, NULL);
+ return libnet_rpc_usermod_recv(c, mem_ctx, io);
+}
diff --git a/source4/libnet/userman.h b/source4/libnet/userman.h
new file mode 100644
index 0000000000..b681c25a16
--- /dev/null
+++ b/source4/libnet/userman.h
@@ -0,0 +1,106 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "librpc/gen_ndr/misc.h"
+
+
+/*
+ * IO structures for userman.c functions
+ */
+
+struct libnet_rpc_useradd {
+ struct {
+ struct policy_handle domain_handle;
+ const char *username;
+ } in;
+ struct {
+ struct policy_handle user_handle;
+ } out;
+};
+
+
+struct libnet_rpc_userdel {
+ struct {
+ struct policy_handle domain_handle;
+ const char *username;
+ } in;
+ struct {
+ struct policy_handle user_handle;
+ } out;
+};
+
+
+#define USERMOD_FIELD_ACCOUNT_NAME ( 0x00000001 )
+#define USERMOD_FIELD_FULL_NAME ( 0x00000002 )
+#define USERMOD_FIELD_DESCRIPTION ( 0x00000010 )
+#define USERMOD_FIELD_COMMENT ( 0x00000020 )
+#define USERMOD_FIELD_HOME_DIRECTORY ( 0x00000040 )
+#define USERMOD_FIELD_HOME_DRIVE ( 0x00000080 )
+#define USERMOD_FIELD_LOGON_SCRIPT ( 0x00000100 )
+#define USERMOD_FIELD_PROFILE_PATH ( 0x00000200 )
+#define USERMOD_FIELD_WORKSTATIONS ( 0x00000400 )
+#define USERMOD_FIELD_LOGON_HOURS ( 0x00002000 )
+#define USERMOD_FIELD_ACCT_EXPIRY ( 0x00004000 )
+#define USERMOD_FIELD_ACCT_FLAGS ( 0x00100000 )
+#define USERMOD_FIELD_PARAMETERS ( 0x00200000 )
+#define USERMOD_FIELD_COUNTRY_CODE ( 0x00400000 )
+#define USERMOD_FIELD_CODE_PAGE ( 0x00800000 )
+
+struct libnet_rpc_usermod {
+ struct {
+ struct policy_handle domain_handle;
+ const char *username;
+
+ struct usermod_change {
+ uint32_t fields; /* bitmask field */
+
+ const char *account_name;
+ const char *full_name;
+ const char *description;
+ const char *comment;
+ const char *logon_script;
+ const char *profile_path;
+ const char *home_directory;
+ const char *home_drive;
+ const char *workstations;
+ struct timeval *acct_expiry;
+ struct timeval *allow_password_change;
+ struct timeval *force_password_change;
+ struct timeval *last_logon;
+ struct timeval *last_logoff;
+ struct timeval *last_password_change;
+ uint32_t acct_flags;
+ } change;
+ } in;
+};
+
+
+/*
+ * Monitor messages sent from userman.c functions
+ */
+
+struct msg_rpc_create_user {
+ uint32_t rid;
+};
+
+
+struct msg_rpc_lookup_name {
+ uint32_t *rid;
+ uint32_t count;
+};
diff --git a/source4/librpc/config.mk b/source4/librpc/config.mk
new file mode 100644
index 0000000000..2f1b14dc37
--- /dev/null
+++ b/source4/librpc/config.mk
@@ -0,0 +1,758 @@
+ndrsrcdir = $(librpcsrcdir)/ndr
+gen_ndrsrcdir = $(librpcsrcdir)/gen_ndr
+dcerpcsrcdir = $(librpcsrcdir)/rpc
+
+################################################
+# Start SUBSYSTEM LIBNDR
+[LIBRARY::LIBNDR]
+PUBLIC_DEPENDENCIES = LIBSAMBA-ERRORS LIBTALLOC LIBSAMBA-UTIL CHARSET \
+ LIBSAMBA-HOSTCONFIG
+
+LIBNDR_OBJ_FILES = $(addprefix $(ndrsrcdir)/, ndr_string.o) ../librpc/ndr/ndr_basic.o ../librpc/ndr/uuid.o ../librpc/ndr/ndr.o ../librpc/gen_ndr/ndr_misc.o ../librpc/ndr/ndr_misc.o
+
+PC_FILES += ../librpc/ndr.pc
+LIBNDR_VERSION = 0.0.1
+LIBNDR_SOVERSION = 0
+
+# End SUBSYSTEM LIBNDR
+################################################
+
+PUBLIC_HEADERS += ../librpc/ndr/libndr.h
+PUBLIC_HEADERS += ../librpc/gen_ndr/misc.h ../librpc/gen_ndr/ndr_misc.h
+
+#################################
+# Start BINARY ndrdump
+[BINARY::ndrdump]
+INSTALLDIR = BINDIR
+PRIVATE_DEPENDENCIES = \
+ LIBSAMBA-HOSTCONFIG \
+ LIBSAMBA-UTIL \
+ LIBPOPT \
+ POPT_SAMBA \
+ NDR_TABLE \
+ LIBSAMBA-ERRORS
+# FIXME: ndrdump shouldn't have to depend on RPC...
+# End BINARY ndrdump
+#################################
+
+ndrdump_OBJ_FILES = ../librpc/tools/ndrdump.o
+
+MANPAGES += ../librpc/tools/ndrdump.1
+
+################################################
+# Start SUBSYSTEM NDR_COMPRESSION
+[SUBSYSTEM::NDR_COMPRESSION]
+PRIVATE_DEPENDENCIES = ZLIB LZXPRESS
+PUBLIC_DEPENDENCIES = LIBSAMBA-ERRORS LIBNDR
+# End SUBSYSTEM NDR_COMPRESSION
+################################################
+
+NDR_COMPRESSION_OBJ_FILES = ../librpc/ndr/ndr_compression.o
+
+[SUBSYSTEM::NDR_SECURITY]
+PUBLIC_DEPENDENCIES = LIBNDR LIBSECURITY
+
+NDR_SECURITY_OBJ_FILES = ../librpc/gen_ndr/ndr_security.o \
+ ../librpc/ndr/ndr_sec_helper.o \
+ $(gen_ndrsrcdir)/ndr_server_id.o
+
+PUBLIC_HEADERS += ../librpc/gen_ndr/security.h
+PUBLIC_HEADERS += $(gen_ndrsrcdir)/server_id.h
+
+[SUBSYSTEM::NDR_AUDIOSRV]
+PUBLIC_DEPENDENCIES = LIBNDR
+
+NDR_AUDIOSRV_OBJ_FILES = ../librpc/gen_ndr/ndr_audiosrv.o
+
+[SUBSYSTEM::NDR_NAMED_PIPE_AUTH]
+PUBLIC_DEPENDENCIES = LIBNDR
+
+NDR_NAMED_PIPE_AUTH_OBJ_FILES = ../librpc/gen_ndr/ndr_named_pipe_auth.o
+
+[SUBSYSTEM::NDR_DNSSERVER]
+PUBLIC_DEPENDENCIES = LIBNDR
+
+NDR_DNSSERVER_OBJ_FILES = ../librpc/gen_ndr/ndr_dnsserver.o
+
+[SUBSYSTEM::NDR_WINSTATION]
+PUBLIC_DEPENDENCIES = LIBNDR
+
+NDR_WINSTATION_OBJ_FILES = $(gen_ndrsrcdir)/ndr_winstation.o
+
+[SUBSYSTEM::NDR_ECHO]
+PUBLIC_DEPENDENCIES = LIBNDR
+
+NDR_ECHO_OBJ_FILES = ../librpc/gen_ndr/ndr_echo.o
+
+[SUBSYSTEM::NDR_IRPC]
+PUBLIC_DEPENDENCIES = LIBNDR NDR_SECURITY NDR_NBT
+
+NDR_IRPC_OBJ_FILES = $(gen_ndrsrcdir)/ndr_irpc.o
+
+[SUBSYSTEM::NDR_DCOM]
+PUBLIC_DEPENDENCIES = LIBNDR NDR_SECURITY NDR_ORPC
+
+NDR_DCOM_OBJ_FILES = ../librpc/gen_ndr/ndr_dcom.o
+
+[SUBSYSTEM::NDR_WMI]
+PUBLIC_DEPENDENCIES = LIBNDR NDR_SECURITY NDR_DCOM
+
+NDR_WMI_OBJ_FILES = ../librpc/gen_ndr/ndr_wmi.o ../librpc/ndr/ndr_wmi.o
+
+[SUBSYSTEM::NDR_DSBACKUP]
+PUBLIC_DEPENDENCIES = LIBNDR
+
+NDR_DSBACKUP_OBJ_FILES = ../librpc/gen_ndr/ndr_dsbackup.o
+
+[SUBSYSTEM::NDR_EFS]
+PUBLIC_DEPENDENCIES = LIBNDR NDR_SECURITY
+
+NDR_EFS_OBJ_FILES = ../librpc/gen_ndr/ndr_efs.o
+
+[SUBSYSTEM::NDR_ROT]
+PUBLIC_DEPENDENCIES = LIBNDR NDR_ORPC
+
+NDR_ROT_OBJ_FILES = ../librpc/gen_ndr/ndr_rot.o
+
+[SUBSYSTEM::NDR_LSA]
+PUBLIC_DEPENDENCIES = LIBNDR NDR_SECURITY
+
+NDR_LSA_OBJ_FILES = ../librpc/gen_ndr/ndr_lsa.o
+
+PUBLIC_HEADERS += ../librpc/gen_ndr/lsa.h
+
+[SUBSYSTEM::NDR_DFS]
+PUBLIC_DEPENDENCIES = LIBNDR
+
+NDR_DFS_OBJ_FILES = ../librpc/gen_ndr/ndr_dfs.o
+
+[SUBSYSTEM::NDR_FRSRPC]
+PUBLIC_DEPENDENCIES = LIBNDR
+
+NDR_FRSRPC_OBJ_FILES = ../librpc/gen_ndr/ndr_frsrpc.o
+
+[SUBSYSTEM::NDR_FRSAPI]
+PUBLIC_DEPENDENCIES = LIBNDR
+
+NDR_FRSAPI_OBJ_FILES = ../librpc/gen_ndr/ndr_frsapi.o
+
+[SUBSYSTEM::NDR_DRSUAPI]
+PUBLIC_DEPENDENCIES = LIBNDR NDR_COMPRESSION NDR_SECURITY NDR_SAMR ASN1_UTIL
+
+NDR_DRSUAPI_OBJ_FILES = ../librpc/gen_ndr/ndr_drsuapi.o ../librpc/ndr/ndr_drsuapi.o
+
+[SUBSYSTEM::NDR_DRSBLOBS]
+PUBLIC_DEPENDENCIES = LIBNDR NDR_DRSUAPI
+
+NDR_DRSBLOBS_OBJ_FILES = ../librpc/gen_ndr/ndr_drsblobs.o ../librpc/ndr/ndr_drsblobs.o
+
+[SUBSYSTEM::NDR_SASL_HELPERS]
+PUBLIC_DEPENDENCIES = LIBNDR
+
+NDR_SASL_HELPERS_OBJ_FILES = $(gen_ndrsrcdir)/ndr_sasl_helpers.o
+
+[SUBSYSTEM::NDR_POLICYAGENT]
+PUBLIC_DEPENDENCIES = LIBNDR
+
+NDR_POLICYAGENT_OBJ_FILES = ../librpc/gen_ndr/ndr_policyagent.o
+
+[SUBSYSTEM::NDR_UNIXINFO]
+PUBLIC_DEPENDENCIES = LIBNDR NDR_SECURITY
+
+NDR_UNIXINFO_OBJ_FILES = ../librpc/gen_ndr/ndr_unixinfo.o
+
+[SUBSYSTEM::NDR_SAMR]
+PUBLIC_DEPENDENCIES = LIBNDR NDR_LSA NDR_SECURITY
+
+NDR_SAMR_OBJ_FILES = ../librpc/gen_ndr/ndr_samr.o
+
+PUBLIC_HEADERS += $(addprefix ../librpc/gen_ndr/, samr.h ndr_samr.h ndr_samr_c.h)
+
+[SUBSYSTEM::NDR_NFS4ACL]
+PUBLIC_DEPENDENCIES = LIBNDR NDR_SECURITY
+
+NDR_NFS4ACL_OBJ_FILES = $(gen_ndrsrcdir)/ndr_nfs4acl.o
+
+[SUBSYSTEM::NDR_SPOOLSS]
+PUBLIC_DEPENDENCIES = LIBNDR NDR_SPOOLSS_BUF NDR_SECURITY
+
+NDR_SPOOLSS_OBJ_FILES = ../librpc/gen_ndr/ndr_spoolss.o
+
+[SUBSYSTEM::NDR_SPOOLSS_BUF]
+
+NDR_SPOOLSS_BUF_OBJ_FILES = ../librpc/ndr/ndr_spoolss_buf.o
+
+[SUBSYSTEM::NDR_WKSSVC]
+PUBLIC_DEPENDENCIES = LIBNDR NDR_SRVSVC NDR_SECURITY
+
+NDR_WKSSVC_OBJ_FILES = ../librpc/gen_ndr/ndr_wkssvc.o
+
+[SUBSYSTEM::NDR_SRVSVC]
+PUBLIC_DEPENDENCIES = LIBNDR NDR_SVCCTL NDR_SECURITY
+
+NDR_SRVSVC_OBJ_FILES = ../librpc/gen_ndr/ndr_srvsvc.o
+
+[SUBSYSTEM::NDR_SVCCTL]
+PUBLIC_DEPENDENCIES = LIBNDR
+
+NDR_SVCCTL_OBJ_FILES = ../librpc/gen_ndr/ndr_svcctl.o ../librpc/ndr/ndr_svcctl.o
+
+PUBLIC_HEADERS += $(addprefix ../librpc/gen_ndr/, ndr_svcctl.h svcctl.h)
+
+[SUBSYSTEM::NDR_ATSVC]
+PUBLIC_DEPENDENCIES = LIBNDR
+
+NDR_ATSVC_OBJ_FILES = ../librpc/gen_ndr/ndr_atsvc.o
+
+PUBLIC_HEADERS += $(addprefix ../librpc/gen_ndr/, atsvc.h ndr_atsvc.h)
+
+[SUBSYSTEM::NDR_EVENTLOG]
+PUBLIC_DEPENDENCIES = LIBNDR NDR_LSA
+
+NDR_EVENTLOG_OBJ_FILES = ../librpc/gen_ndr/ndr_eventlog.o
+
+[SUBSYSTEM::NDR_EPMAPPER]
+PUBLIC_DEPENDENCIES = LIBNDR
+
+NDR_EPMAPPER_OBJ_FILES = ../librpc/gen_ndr/ndr_epmapper.o
+
+[SUBSYSTEM::NDR_DBGIDL]
+PUBLIC_DEPENDENCIES = LIBNDR
+
+NDR_DBGIDL_OBJ_FILES = ../librpc/gen_ndr/ndr_dbgidl.o
+
+[SUBSYSTEM::NDR_DSSETUP]
+PUBLIC_DEPENDENCIES = LIBNDR
+
+NDR_DSSETUP_OBJ_FILES = ../librpc/gen_ndr/ndr_dssetup.o
+
+[SUBSYSTEM::NDR_MSGSVC]
+PUBLIC_DEPENDENCIES = LIBNDR
+
+NDR_MSGSVC_OBJ_FILES = ../librpc/gen_ndr/ndr_msgsvc.o
+
+[SUBSYSTEM::NDR_WINS]
+PUBLIC_DEPENDENCIES = LIBNDR
+
+NDR_WINS_OBJ_FILES = $(gen_ndrsrcdir)/ndr_wins.o
+
+[SUBSYSTEM::NDR_WINREG]
+PUBLIC_DEPENDENCIES = LIBNDR NDR_INITSHUTDOWN NDR_SECURITY
+
+NDR_WINREG_OBJ_FILES = ../librpc/gen_ndr/ndr_winreg.o
+
+[SUBSYSTEM::NDR_INITSHUTDOWN]
+PUBLIC_DEPENDENCIES = LIBNDR
+
+NDR_INITSHUTDOWN_OBJ_FILES = ../librpc/gen_ndr/ndr_initshutdown.o
+
+[SUBSYSTEM::NDR_MGMT]
+PUBLIC_DEPENDENCIES = LIBNDR
+
+NDR_MGMT_OBJ_FILES = ../librpc/gen_ndr/ndr_mgmt.o
+
+[SUBSYSTEM::NDR_PROTECTED_STORAGE]
+PUBLIC_DEPENDENCIES = LIBNDR
+
+NDR_PROTECTED_STORAGE_OBJ_FILES = ../librpc/gen_ndr/ndr_protected_storage.o
+
+[SUBSYSTEM::NDR_ORPC]
+PUBLIC_DEPENDENCIES = LIBNDR
+
+NDR_ORPC_OBJ_FILES = ../librpc/gen_ndr/ndr_orpc.o ../librpc/ndr/ndr_orpc.o
+
+[SUBSYSTEM::NDR_OXIDRESOLVER]
+PUBLIC_DEPENDENCIES = LIBNDR NDR_ORPC
+
+NDR_OXIDRESOLVER_OBJ_FILES = ../librpc/gen_ndr/ndr_oxidresolver.o
+
+[SUBSYSTEM::NDR_REMACT]
+PUBLIC_DEPENDENCIES = LIBNDR NDR_ORPC
+
+NDR_REMACT_OBJ_FILES = ../librpc/gen_ndr/ndr_remact.o
+
+[SUBSYSTEM::NDR_WZCSVC]
+PUBLIC_DEPENDENCIES = LIBNDR
+
+NDR_WZCSVC_OBJ_FILES = ../librpc/gen_ndr/ndr_wzcsvc.o
+
+[SUBSYSTEM::NDR_BROWSER]
+PUBLIC_DEPENDENCIES = LIBNDR
+
+NDR_BROWSER_OBJ_FILES = ../librpc/gen_ndr/ndr_browser.o
+
+[SUBSYSTEM::NDR_W32TIME]
+PUBLIC_DEPENDENCIES = LIBNDR
+
+NDR_W32TIME_OBJ_FILES = ../librpc/gen_ndr/ndr_w32time.o
+
+[SUBSYSTEM::NDR_SCERPC]
+PUBLIC_DEPENDENCIES = LIBNDR
+
+NDR_SCERPC_OBJ_FILES = ../librpc/gen_ndr/ndr_scerpc.o
+
+[SUBSYSTEM::NDR_NTSVCS]
+PUBLIC_DEPENDENCIES = LIBNDR
+
+NDR_NTSVCS_OBJ_FILES = ../librpc/gen_ndr/ndr_ntsvcs.o
+
+[SUBSYSTEM::NDR_NETLOGON]
+PUBLIC_DEPENDENCIES = LIBNDR NDR_SAMR NDR_LSA NDR_SECURITY
+
+NDR_NETLOGON_OBJ_FILES = ../librpc/gen_ndr/ndr_netlogon.o ../librpc/ndr/ndr_netlogon.o
+
+PUBLIC_HEADERS += ../librpc/gen_ndr/netlogon.h
+
+[SUBSYSTEM::NDR_TRKWKS]
+PUBLIC_DEPENDENCIES = LIBNDR
+
+NDR_TRKWKS_OBJ_FILES = ../librpc/gen_ndr/ndr_trkwks.o
+
+[SUBSYSTEM::NDR_KEYSVC]
+PUBLIC_DEPENDENCIES = LIBNDR
+
+NDR_KEYSVC_OBJ_FILES = ../librpc/gen_ndr/ndr_keysvc.o
+
+[SUBSYSTEM::NDR_KRB5PAC]
+PUBLIC_DEPENDENCIES = LIBNDR NDR_NETLOGON NDR_SECURITY
+
+NDR_KRB5PAC_OBJ_FILES = ../librpc/gen_ndr/ndr_krb5pac.o ../librpc/ndr/ndr_krb5pac.o
+
+[SUBSYSTEM::NDR_XATTR]
+PUBLIC_DEPENDENCIES = LIBNDR NDR_SECURITY
+
+NDR_XATTR_OBJ_FILES = ../librpc/gen_ndr/ndr_xattr.o
+
+[SUBSYSTEM::NDR_OPENDB]
+PUBLIC_DEPENDENCIES = LIBNDR
+
+NDR_OPENDB_OBJ_FILES = $(gen_ndrsrcdir)/ndr_opendb.o
+
+[SUBSYSTEM::NDR_NOTIFY]
+PUBLIC_DEPENDENCIES = LIBNDR
+
+NDR_NOTIFY_OBJ_FILES = $(gen_ndrsrcdir)/ndr_notify.o
+
+[SUBSYSTEM::NDR_SCHANNEL]
+PUBLIC_DEPENDENCIES = LIBNDR NDR_NBT
+
+NDR_SCHANNEL_OBJ_FILES = $(gen_ndrsrcdir)/ndr_schannel.o
+
+[SUBSYSTEM::NDR_NBT]
+PUBLIC_DEPENDENCIES = LIBNDR NDR_NBT_BUF NDR_SVCCTL NDR_SECURITY NDR_SAMR LIBCLI_NDR_NETLOGON
+
+NDR_NBT_OBJ_FILES = ../librpc/gen_ndr/ndr_nbt.o
+
+PUBLIC_HEADERS += ../librpc/gen_ndr/nbt.h
+
+[SUBSYSTEM::NDR_NTP_SIGND]
+PUBLIC_DEPENDENCIES = LIBNDR
+
+NDR_NTP_SIGND_OBJ_FILES = $(gen_ndrsrcdir)/ndr_ntp_signd.o
+
+[SUBSYSTEM::NDR_WINSREPL]
+PUBLIC_DEPENDENCIES = LIBNDR NDR_NBT
+
+NDR_WINSREPL_OBJ_FILES = $(gen_ndrsrcdir)/ndr_winsrepl.o
+
+[SUBSYSTEM::NDR_WINBIND]
+PUBLIC_DEPENDENCIES = LIBNDR NDR_NETLOGON
+
+NDR_WINBIND_OBJ_FILES = $(gen_ndrsrcdir)/ndr_winbind.o
+#PUBLIC_HEADERS += $(gen_ndrsrcdir)/winbind.h
+
+$(librpcsrcdir)/idl-deps:
+ $(PERL) $(librpcsrcdir)/idl-deps.pl $(wildcard $(librpcsrcdir)/idl/*.idl ../librpc/idl/*.idl) >$@
+
+clean::
+ rm -f $(librpcsrcdir)/idl-deps
+
+-include $(librpcsrcdir)/idl-deps
+
+$(gen_ndrsrcdir)/tables.c: $(IDL_NDR_PARSE_H_FILES)
+ @echo Generating $@
+ @$(PERL) ../librpc/tables.pl --output=$@ $^ > $(gen_ndrsrcdir)/tables.x
+ @mv $(gen_ndrsrcdir)/tables.x $@
+
+[SUBSYSTEM::NDR_TABLE]
+PUBLIC_DEPENDENCIES = \
+ NDR_AUDIOSRV NDR_ECHO NDR_DCERPC \
+ NDR_DSBACKUP NDR_EFS NDR_LSA NDR_DFS NDR_DRSUAPI \
+ NDR_POLICYAGENT NDR_UNIXINFO NDR_SAMR NDR_SPOOLSS NDR_WKSSVC NDR_SRVSVC NDR_ATSVC \
+ NDR_EVENTLOG NDR_EPMAPPER NDR_DBGIDL NDR_DSSETUP NDR_MSGSVC NDR_WINS \
+ NDR_WINREG NDR_MGMT NDR_PROTECTED_STORAGE NDR_OXIDRESOLVER \
+ NDR_REMACT NDR_WZCSVC NDR_BROWSER NDR_W32TIME NDR_SCERPC NDR_NTSVCS \
+ NDR_NETLOGON NDR_TRKWKS NDR_KEYSVC NDR_KRB5PAC NDR_XATTR NDR_SCHANNEL \
+ NDR_ROT NDR_DRSBLOBS NDR_SVCCTL NDR_NBT NDR_WINSREPL NDR_SECURITY \
+ NDR_INITSHUTDOWN NDR_DNSSERVER NDR_WINSTATION NDR_IRPC NDR_OPENDB \
+ NDR_SASL_HELPERS NDR_NOTIFY NDR_WINBIND NDR_FRSRPC NDR_FRSAPI NDR_NFS4ACL NDR_NTP_SIGND \
+ NDR_DCOM NDR_WMI NDR_NAMED_PIPE_AUTH
+
+NDR_TABLE_OBJ_FILES = ../librpc/ndr/ndr_table.o $(gen_ndrsrcdir)/tables.o
+
+[SUBSYSTEM::RPC_NDR_ROT]
+PUBLIC_DEPENDENCIES = NDR_ROT dcerpc
+
+RPC_NDR_ROT_OBJ_FILES = ../librpc/gen_ndr/ndr_rot_c.o
+
+[SUBSYSTEM::RPC_NDR_AUDIOSRV]
+PUBLIC_DEPENDENCIES = NDR_AUDIOSRV dcerpc
+
+RPC_NDR_AUDIOSRV_OBJ_FILES = ../librpc/gen_ndr/ndr_audiosrv_c.o
+
+[SUBSYSTEM::RPC_NDR_ECHO]
+PUBLIC_DEPENDENCIES = dcerpc NDR_ECHO
+
+RPC_NDR_ECHO_OBJ_FILES = ../librpc/gen_ndr/ndr_echo_c.o
+
+[SUBSYSTEM::RPC_NDR_DSBACKUP]
+PUBLIC_DEPENDENCIES = dcerpc NDR_DSBACKUP
+
+RPC_NDR_DSBACKUP_OBJ_FILES = ../librpc/gen_ndr/ndr_dsbackup_c.o
+
+[SUBSYSTEM::RPC_NDR_EFS]
+PUBLIC_DEPENDENCIES = dcerpc NDR_EFS
+
+RPC_NDR_EFS_OBJ_FILES = ../librpc/gen_ndr/ndr_efs_c.o
+
+[SUBSYSTEM::RPC_NDR_LSA]
+PUBLIC_DEPENDENCIES = dcerpc NDR_LSA
+
+RPC_NDR_LSA_OBJ_FILES = ../librpc/gen_ndr/ndr_lsa_c.o
+
+[SUBSYSTEM::RPC_NDR_DFS]
+PUBLIC_DEPENDENCIES = dcerpc NDR_DFS
+
+RPC_NDR_DFS_OBJ_FILES = ../librpc/gen_ndr/ndr_dfs_c.o
+
+[SUBSYSTEM::RPC_NDR_FRSAPI]
+PUBLIC_DEPENDENCIES = dcerpc NDR_FRSAPI
+
+RPC_NDR_FRSAPI_OBJ_FILES = ../librpc/gen_ndr/ndr_frsapi_c.o
+
+[SUBSYSTEM::RPC_NDR_DRSUAPI]
+PUBLIC_DEPENDENCIES = dcerpc NDR_DRSUAPI
+
+RPC_NDR_DRSUAPI_OBJ_FILES = ../librpc/gen_ndr/ndr_drsuapi_c.o
+
+[SUBSYSTEM::RPC_NDR_POLICYAGENT]
+PUBLIC_DEPENDENCIES = dcerpc NDR_POLICYAGENT
+
+RPC_NDR_POLICYAGENT_OBJ_FILES = ../librpc/gen_ndr/ndr_policyagent_c.o
+
+[SUBSYSTEM::RPC_NDR_UNIXINFO]
+PUBLIC_DEPENDENCIES = dcerpc NDR_UNIXINFO
+
+RPC_NDR_UNIXINFO_OBJ_FILES = ../librpc/gen_ndr/ndr_unixinfo_c.o
+
+[SUBSYSTEM::RPC_NDR_BROWSER]
+PUBLIC_DEPENDENCIES = dcerpc NDR_BROWSER
+
+RPC_NDR_BROWSER_OBJ_FILES = ../librpc/gen_ndr/ndr_browser_c.o
+
+[SUBSYSTEM::RPC_NDR_IRPC]
+PUBLIC_DEPENDENCIES = dcerpc NDR_IRPC
+
+RPC_NDR_IRPC_OBJ_FILES = $(gen_ndrsrcdir)/ndr_irpc_c.o
+
+[LIBRARY::dcerpc_samr]
+PUBLIC_DEPENDENCIES = dcerpc NDR_SAMR
+
+PC_FILES += $(librpcsrcdir)/dcerpc_samr.pc
+
+dcerpc_samr_VERSION = 0.0.1
+dcerpc_samr_SOVERSION = 0
+dcerpc_samr_OBJ_FILES = ../librpc/gen_ndr/ndr_samr_c.o
+
+[SUBSYSTEM::RPC_NDR_SPOOLSS]
+PUBLIC_DEPENDENCIES = dcerpc NDR_SPOOLSS
+
+RPC_NDR_SPOOLSS_OBJ_FILES = ../librpc/gen_ndr/ndr_spoolss_c.o
+
+[SUBSYSTEM::RPC_NDR_WKSSVC]
+PUBLIC_DEPENDENCIES = dcerpc NDR_WKSSVC
+
+RPC_NDR_WKSSVC_OBJ_FILES = ../librpc/gen_ndr/ndr_wkssvc_c.o
+
+[SUBSYSTEM::RPC_NDR_SRVSVC]
+PUBLIC_DEPENDENCIES = dcerpc NDR_SRVSVC
+
+RPC_NDR_SRVSVC_OBJ_FILES = ../librpc/gen_ndr/ndr_srvsvc_c.o
+
+[SUBSYSTEM::RPC_NDR_SVCCTL]
+PUBLIC_DEPENDENCIES = dcerpc NDR_SVCCTL
+
+RPC_NDR_SVCCTL_OBJ_FILES = ../librpc/gen_ndr/ndr_svcctl_c.o
+
+PUBLIC_HEADERS += ../librpc/gen_ndr/ndr_svcctl_c.h
+
+[LIBRARY::dcerpc_atsvc]
+PUBLIC_DEPENDENCIES = dcerpc NDR_ATSVC
+
+dcerpc_atsvc_VERSION = 0.0.1
+dcerpc_atsvc_SOVERSION = 0
+
+dcerpc_atsvc_OBJ_FILES = ../librpc/gen_ndr/ndr_atsvc_c.o
+PC_FILES += $(librpcsrcdir)/dcerpc_atsvc.pc
+
+PUBLIC_HEADERS += ../librpc/gen_ndr/ndr_atsvc_c.h
+
+[SUBSYSTEM::RPC_NDR_EVENTLOG]
+PUBLIC_DEPENDENCIES = dcerpc NDR_EVENTLOG
+
+RPC_NDR_EVENTLOG_OBJ_FILES = ../librpc/gen_ndr/ndr_eventlog_c.o
+
+[SUBSYSTEM::RPC_NDR_EPMAPPER]
+PUBLIC_DEPENDENCIES = NDR_EPMAPPER
+
+RPC_NDR_EPMAPPER_OBJ_FILES = ../librpc/gen_ndr/ndr_epmapper_c.o
+
+[SUBSYSTEM::RPC_NDR_DBGIDL]
+PUBLIC_DEPENDENCIES = dcerpc NDR_DBGIDL
+
+RPC_NDR_DBGIDL_OBJ_FILES = ../librpc/gen_ndr/ndr_dbgidl_c.o
+
+[SUBSYSTEM::RPC_NDR_DSSETUP]
+PUBLIC_DEPENDENCIES = dcerpc NDR_DSSETUP
+
+RPC_NDR_DSSETUP_OBJ_FILES = ../librpc/gen_ndr/ndr_dssetup_c.o
+
+[SUBSYSTEM::RPC_NDR_MSGSVC]
+PUBLIC_DEPENDENCIES = dcerpc NDR_MSGSVC
+
+RPC_NDR_MSGSVC_OBJ_FILES = ../librpc/gen_ndr/ndr_msgsvc_c.o
+
+[SUBSYSTEM::RPC_NDR_WINS]
+PUBLIC_DEPENDENCIES = dcerpc NDR_WINS
+
+RPC_NDR_WINS_OBJ_FILES = $(gen_ndrsrcdir)/ndr_wins_c.o
+
+[SUBSYSTEM::RPC_NDR_WINREG]
+PUBLIC_DEPENDENCIES = dcerpc NDR_WINREG
+
+RPC_NDR_WINREG_OBJ_FILES = ../librpc/gen_ndr/ndr_winreg_c.o
+
+[SUBSYSTEM::RPC_NDR_INITSHUTDOWN]
+PUBLIC_DEPENDENCIES = dcerpc NDR_INITSHUTDOWN
+
+RPC_NDR_INITSHUTDOWN_OBJ_FILES = ../librpc/gen_ndr/ndr_initshutdown_c.o
+
+[SUBSYSTEM::RPC_NDR_MGMT]
+PRIVATE_DEPENDENCIES = NDR_MGMT
+
+RPC_NDR_MGMT_OBJ_FILES = ../librpc/gen_ndr/ndr_mgmt_c.o
+
+[SUBSYSTEM::RPC_NDR_PROTECTED_STORAGE]
+PUBLIC_DEPENDENCIES = dcerpc NDR_PROTECTED_STORAGE
+
+RPC_NDR_PROTECTED_STORAGE_OBJ_FILES = ../librpc/gen_ndr/ndr_protected_storage_c.o
+
+[SUBSYSTEM::RPC_NDR_OXIDRESOLVER]
+PUBLIC_DEPENDENCIES = dcerpc NDR_OXIDRESOLVER
+
+RPC_NDR_OXIDRESOLVER_OBJ_FILES = ../librpc/gen_ndr/ndr_oxidresolver_c.o
+
+[SUBSYSTEM::RPC_NDR_REMACT]
+PUBLIC_DEPENDENCIES = dcerpc NDR_REMACT
+
+RPC_NDR_REMACT_OBJ_FILES = ../librpc/gen_ndr/ndr_remact_c.o
+
+[SUBSYSTEM::RPC_NDR_WZCSVC]
+PUBLIC_DEPENDENCIES = dcerpc NDR_WZCSVC
+
+RPC_NDR_WZCSVC_OBJ_FILES = ../librpc/gen_ndr/ndr_wzcsvc_c.o
+
+[SUBSYSTEM::RPC_NDR_W32TIME]
+PUBLIC_DEPENDENCIES = dcerpc NDR_W32TIME
+
+RPC_NDR_W32TIME_OBJ_FILES = ../librpc/gen_ndr/ndr_w32time_c.o
+
+[SUBSYSTEM::RPC_NDR_SCERPC]
+PUBLIC_DEPENDENCIES = dcerpc NDR_SCERPC
+
+RPC_NDR_SCERPC_OBJ_FILES = ../librpc/gen_ndr/ndr_scerpc_c.o
+
+[SUBSYSTEM::RPC_NDR_NTSVCS]
+PUBLIC_DEPENDENCIES = dcerpc NDR_NTSVCS
+
+RPC_NDR_NTSVCS_OBJ_FILES = ../librpc/gen_ndr/ndr_ntsvcs_c.o
+
+[SUBSYSTEM::RPC_NDR_NETLOGON]
+PUBLIC_DEPENDENCIES = NDR_NETLOGON
+
+RPC_NDR_NETLOGON_OBJ_FILES = ../librpc/gen_ndr/ndr_netlogon_c.o
+
+[SUBSYSTEM::RPC_NDR_TRKWKS]
+PUBLIC_DEPENDENCIES = dcerpc NDR_TRKWKS
+
+RPC_NDR_TRKWKS_OBJ_FILES = ../librpc/gen_ndr/ndr_trkwks_c.o
+
+[SUBSYSTEM::RPC_NDR_KEYSVC]
+PUBLIC_DEPENDENCIES = dcerpc NDR_KEYSVC
+
+RPC_NDR_KEYSVC_OBJ_FILES = ../librpc/gen_ndr/ndr_keysvc_c.o
+
+[SUBSYSTEM::NDR_DCERPC]
+PUBLIC_DEPENDENCIES = LIBNDR
+
+NDR_DCERPC_OBJ_FILES = $(gen_ndrsrcdir)/ndr_dcerpc.o
+
+PUBLIC_HEADERS += $(addprefix $(librpcsrcdir)/, gen_ndr/dcerpc.h gen_ndr/ndr_dcerpc.h)
+
+################################################
+# Start SUBSYSTEM dcerpc
+[LIBRARY::dcerpc]
+PRIVATE_DEPENDENCIES = \
+ samba_socket LIBCLI_RESOLVE LIBCLI_SMB LIBCLI_SMB2 \
+ LIBNDR NDR_DCERPC RPC_NDR_EPMAPPER \
+ NDR_SCHANNEL RPC_NDR_NETLOGON \
+ RPC_NDR_MGMT \
+ gensec LIBCLI_AUTH LIBCLI_RAW \
+ LP_RESOLVE
+PUBLIC_DEPENDENCIES = CREDENTIALS
+# End SUBSYSTEM dcerpc
+################################################
+
+PC_FILES += $(librpcsrcdir)/dcerpc.pc
+dcerpc_VERSION = 0.0.1
+dcerpc_SOVERSION = 0
+
+dcerpc_OBJ_FILES = $(addprefix $(dcerpcsrcdir)/, dcerpc.o dcerpc_auth.o dcerpc_schannel.o dcerpc_util.o \
+ dcerpc_smb.o dcerpc_smb2.o dcerpc_sock.o dcerpc_connect.o dcerpc_secondary.o) \
+ ../librpc/rpc/binding.o ../librpc/rpc/dcerpc_error.o
+
+$(eval $(call proto_header_template,$(dcerpcsrcdir)/dcerpc_proto.h,$(dcerpc_OBJ_FILES:.o=.c)))
+
+
+PUBLIC_HEADERS += $(addprefix $(librpcsrcdir)/, rpc/dcerpc.h) \
+ $(addprefix ../librpc/gen_ndr/, mgmt.h ndr_mgmt.h ndr_mgmt_c.h \
+ epmapper.h ndr_epmapper.h ndr_epmapper_c.h)
+
+
+[PYTHON::python_dcerpc]
+LIBRARY_REALNAME = samba/dcerpc/base.$(SHLIBEXT)
+PUBLIC_DEPENDENCIES = LIBCLI_SMB LIBSAMBA-UTIL LIBSAMBA-HOSTCONFIG dcerpc_samr RPC_NDR_LSA DYNCONFIG pycredentials param
+
+python_dcerpc_OBJ_FILES = $(dcerpcsrcdir)/pyrpc.o
+
+$(eval $(call python_py_module_template,samba/dcerpc/__init__.py,$(dcerpcsrcdir)/dcerpc.py))
+
+
+[PYTHON::python_echo]
+LIBRARY_REALNAME = samba/dcerpc/echo.$(SHLIBEXT)
+PRIVATE_DEPENDENCIES = RPC_NDR_ECHO PYTALLOC param pycredentials python_dcerpc
+
+python_echo_OBJ_FILES = ../librpc/gen_ndr/py_echo.o
+
+[PYTHON::python_winreg]
+LIBRARY_REALNAME = samba/dcerpc/winreg.$(SHLIBEXT)
+PRIVATE_DEPENDENCIES = RPC_NDR_WINREG PYTALLOC param pycredentials python_dcerpc
+
+python_winreg_OBJ_FILES = ../librpc/gen_ndr/py_winreg.o
+
+[PYTHON::python_dcerpc_misc]
+LIBRARY_REALNAME = samba/dcerpc/misc.$(SHLIBEXT)
+PRIVATE_DEPENDENCIES = PYTALLOC python_dcerpc
+
+python_dcerpc_misc_OBJ_FILES = ../librpc/gen_ndr/py_misc.o
+
+[PYTHON::python_initshutdown]
+LIBRARY_REALNAME = samba/dcerpc/initshutdown.$(SHLIBEXT)
+PRIVATE_DEPENDENCIES = RPC_NDR_INITSHUTDOWN PYTALLOC param pycredentials python_dcerpc
+
+python_initshutdown_OBJ_FILES = ../librpc/gen_ndr/py_initshutdown.o
+
+[PYTHON::python_epmapper]
+LIBRARY_REALNAME = samba/dcerpc/epmapper.$(SHLIBEXT)
+PRIVATE_DEPENDENCIES = dcerpc PYTALLOC param pycredentials python_dcerpc
+
+python_epmapper_OBJ_FILES = ../librpc/gen_ndr/py_epmapper.o
+
+[PYTHON::python_mgmt]
+LIBRARY_REALNAME = samba/dcerpc/mgmt.$(SHLIBEXT)
+PRIVATE_DEPENDENCIES = PYTALLOC param pycredentials dcerpc python_dcerpc
+
+python_mgmt_OBJ_FILES = ../librpc/gen_ndr/py_mgmt.o
+
+[PYTHON::python_atsvc]
+LIBRARY_REALNAME = samba/dcerpc/atsvc.$(SHLIBEXT)
+PRIVATE_DEPENDENCIES = dcerpc_atsvc PYTALLOC param pycredentials python_dcerpc
+
+python_atsvc_OBJ_FILES = ../librpc/gen_ndr/py_atsvc.o
+
+[PYTHON::python_dcerpc_nbt]
+LIBRARY_REALNAME = samba/dcerpc/nbt.$(SHLIBEXT)
+PRIVATE_DEPENDENCIES = NDR_NBT PYTALLOC param pycredentials python_dcerpc
+
+python_dcerpc_nbt_OBJ_FILES = ../librpc/gen_ndr/py_nbt.o
+
+[PYTHON::python_samr]
+LIBRARY_REALNAME = samba/dcerpc/samr.$(SHLIBEXT)
+PRIVATE_DEPENDENCIES = dcerpc_samr PYTALLOC pycredentials param python_dcerpc
+
+python_samr_OBJ_FILES = ../librpc/gen_ndr/py_samr.o
+
+[PYTHON::python_svcctl]
+LIBRARY_REALNAME = samba/dcerpc/svcctl.$(SHLIBEXT)
+PRIVATE_DEPENDENCIES = RPC_NDR_SVCCTL PYTALLOC param pycredentials python_dcerpc
+
+python_svcctl_OBJ_FILES = ../librpc/gen_ndr/py_svcctl.o
+
+[PYTHON::python_lsa]
+LIBRARY_REALNAME = samba/dcerpc/lsa.$(SHLIBEXT)
+PRIVATE_DEPENDENCIES = RPC_NDR_LSA PYTALLOC param pycredentials python_dcerpc
+
+python_lsa_OBJ_FILES = ../librpc/gen_ndr/py_lsa.o
+
+[PYTHON::python_wkssvc]
+LIBRARY_REALNAME = samba/dcerpc/wkssvc.$(SHLIBEXT)
+PRIVATE_DEPENDENCIES = RPC_NDR_WKSSVC PYTALLOC param pycredentials python_dcerpc
+
+python_wkssvc_OBJ_FILES = ../librpc/gen_ndr/py_wkssvc.o
+
+[PYTHON::python_dfs]
+LIBRARY_REALNAME = samba/dcerpc/dfs.$(SHLIBEXT)
+PRIVATE_DEPENDENCIES = RPC_NDR_DFS PYTALLOC param pycredentials python_dcerpc
+
+python_dfs_OBJ_FILES = ../librpc/gen_ndr/py_dfs.o
+
+[PYTHON::python_unixinfo]
+LIBRARY_REALNAME = samba/dcerpc/unixinfo.$(SHLIBEXT)
+PRIVATE_DEPENDENCIES = RPC_NDR_UNIXINFO PYTALLOC param pycredentials python_dcerpc
+
+python_unixinfo_OBJ_FILES = ../librpc/gen_ndr/py_unixinfo.o
+
+[PYTHON::python_irpc]
+LIBRARY_REALNAME = samba/dcerpc/irpc.$(SHLIBEXT)
+PRIVATE_DEPENDENCIES = RPC_NDR_IRPC PYTALLOC param pycredentials python_dcerpc
+
+python_irpc_OBJ_FILES = $(gen_ndrsrcdir)/py_irpc.o
+
+[PYTHON::python_drsuapi]
+LIBRARY_REALNAME = samba/dcerpc/drsuapi.$(SHLIBEXT)
+PRIVATE_DEPENDENCIES = RPC_NDR_DRSUAPI PYTALLOC param pycredentials python_dcerpc
+
+python_drsuapi_OBJ_FILES = ../librpc/gen_ndr/py_drsuapi.o
+
+[PYTHON::python_dcerpc_security]
+LIBRARY_REALNAME = samba/dcerpc/security.$(SHLIBEXT)
+PRIVATE_DEPENDENCIES = PYTALLOC python_dcerpc_misc python_dcerpc
+
+python_dcerpc_security_OBJ_FILES = ../librpc/gen_ndr/py_security.o
+
+$(IDL_HEADER_FILES) $(IDL_NDR_PARSE_H_FILES) $(IDL_NDR_PARSE_C_FILES) \
+ $(IDL_NDR_CLIENT_C_FILES) $(IDL_NDR_CLIENT_H_FILES) \
+ $(IDL_NDR_SERVER_C_FILES) $(IDL_SWIG_FILES) \
+ $(IDL_NDR_PY_C_FILES) $(IDL_NDR_PY_H_FILES): idl
+
+idl_full:: $(pidldir)/lib/Parse/Pidl/IDL.pm $(pidldir)/lib/Parse/Pidl/Expr.pm
+ @PIDL_OUTPUTDIR="../librpc/gen_ndr" PIDL_ARGS="$(PIDL_ARGS)" CPP="$(CPP)" srcdir="$(srcdir)" PIDL="$(PIDL)" ../librpc/build_idl.sh --full ../librpc/idl/*.idl
+ @CPP="$(CPP)" PIDL="$(PIDL)" $(librpcsrcdir)/scripts/build_idl.sh FULL $(librpcsrcdir)/gen_ndr $(librpcsrcdir)/idl/*.idl
+
+idl:: $(pidldir)/lib/Parse/Pidl/IDL.pm $(pidldir)/lib/Parse/Pidl/Expr.pm
+ @PIDL_OUTPUTDIR="../librpc/gen_ndr" PIDL_ARGS="$(PIDL_ARGS)" CPP="$(CPP)" srcdir="$(srcdir)" PIDL="$(PIDL)" ../librpc/build_idl.sh ../librpc/idl/*.idl
+ @CPP="$(CPP)" PIDL="$(PIDL)" $(librpcsrcdir)/scripts/build_idl.sh PARTIAL $(librpcsrcdir)/gen_ndr $(librpcsrcdir)/idl/*.idl
+
+clean::
+ @echo "Remove ../librpc/gen_ndr files which are not commited to git"
+ @cat ../.gitignore | grep "^librpc/gen_ndr" | xargs rm -f
diff --git a/source4/librpc/dcerpc.pc.in b/source4/librpc/dcerpc.pc.in
new file mode 100644
index 0000000000..3960f2a583
--- /dev/null
+++ b/source4/librpc/dcerpc.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: dcerpc
+Description: DCE/RPC client library
+Requires: ndr
+Version: 0.0.1
+Libs: -L${libdir} -ldcerpc
+Cflags: -I${includedir} -DHAVE_IMMEDIATE_STRUCTURES=1
diff --git a/source4/librpc/dcerpc_atsvc.pc.in b/source4/librpc/dcerpc_atsvc.pc.in
new file mode 100644
index 0000000000..060485c3fc
--- /dev/null
+++ b/source4/librpc/dcerpc_atsvc.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: dcerpc_atsvc
+Description: DCE/RPC client library - ATSVC
+Requires.private: dcerpc ndr
+Version: 0.0.1
+Libs: -L${libdir} -ldcerpc_atsvc
+Cflags: -I${includedir} -DHAVE_IMMEDIATE_STRUCTURES=1
diff --git a/source4/librpc/dcerpc_samr.pc.in b/source4/librpc/dcerpc_samr.pc.in
new file mode 100644
index 0000000000..c4102237d3
--- /dev/null
+++ b/source4/librpc/dcerpc_samr.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: dcerpc_samr
+Description: DCE/RPC client library - SAMR
+Requires.private: dcerpc ndr
+Version: 0.0.1
+Libs: -L${libdir} -ldcerpc_samr
+Cflags: -I${includedir} -DHAVE_IMMEDIATE_STRUCTURES=1
diff --git a/source4/librpc/idl-deps.pl b/source4/librpc/idl-deps.pl
new file mode 100644
index 0000000000..4d97139fc5
--- /dev/null
+++ b/source4/librpc/idl-deps.pl
@@ -0,0 +1,37 @@
+#!/usr/bin/perl
+use strict;
+use File::Basename;
+
+sub add($$)
+{
+ my ($name, $val) = @_;
+
+ print "$name += $val\n";
+}
+
+my %vars = ();
+
+foreach(@ARGV) {
+ my $f = $_;
+ my $b = basename($f);
+
+ $b =~ s/\.idl//;
+
+ my $gen_ndr = dirname($f);
+ $gen_ndr =~ s/\/idl$/\/gen_ndr/;
+
+ print "# $f\n";
+ add("IDL_FILES", $f);
+ add("IDL_HEADER_FILES", "$gen_ndr/$b.h");
+ add("IDL_NDR_PARSE_H_FILES", "$gen_ndr/ndr_$b.h");
+ add("IDL_NDR_PARSE_C_FILES", "$gen_ndr/ndr_$b.c");
+ add("IDL_NDR_CLIENT_C_FILES", "$gen_ndr/ndr_$b\_c.c");
+ add("IDL_NDR_CLIENT_H_FILES", "$gen_ndr/ndr_$b\_c.h");
+ add("IDL_SWIG_FILES", "$gen_ndr/$b.i");
+ add("IDL_NDR_SERVER_C_FILES", "$gen_ndr/ndr_$b\_s.c");
+ add("IDL_NDR_EJS_C_FILES", "$gen_ndr/ndr_$b\_ejs.c");
+ add("IDL_NDR_EJS_H_FILES", "$gen_ndr/ndr_$b\_ejs.h");
+ add("IDL_NDR_PY_C_FILES", "$gen_ndr/py_$b.c");
+ add("IDL_NDR_PY_H_FILES", "$gen_ndr/py_$b.h");
+ print "\n";
+}
diff --git a/source4/librpc/idl/IDL_LICENSE.txt b/source4/librpc/idl/IDL_LICENSE.txt
new file mode 100644
index 0000000000..01ae670b69
--- /dev/null
+++ b/source4/librpc/idl/IDL_LICENSE.txt
@@ -0,0 +1,9 @@
+The IDL files in this directory are made available by the Samba Team
+under the following license:
+
+ Permission to use, copy, modify, and distribute these interface
+ definitions for any purpose is hereby granted without fee.
+
+ This work is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/source4/librpc/idl/dcerpc.idl b/source4/librpc/idl/dcerpc.idl
new file mode 100644
index 0000000000..7c0abe6ab8
--- /dev/null
+++ b/source4/librpc/idl/dcerpc.idl
@@ -0,0 +1,306 @@
+#include "idl_types.h"
+
+/*
+ the base dcerpc packet definitions - not traditionally coded as IDL,
+ but given that pidl can handle it nicely it simplifies things a lot
+ to do it this way
+
+ see http://www.opengroup.org/onlinepubs/9629399/chap12.htm for packet
+ layouts
+*/
+import "misc.idl";
+
+interface dcerpc
+{
+ typedef struct {
+ uint16 context_id;
+ uint8 num_transfer_syntaxes;
+ ndr_syntax_id abstract_syntax;
+ ndr_syntax_id transfer_syntaxes[num_transfer_syntaxes];
+ } dcerpc_ctx_list;
+
+ typedef struct {
+ uint16 max_xmit_frag;
+ uint16 max_recv_frag;
+ uint32 assoc_group_id;
+ uint8 num_contexts;
+ dcerpc_ctx_list ctx_list[num_contexts];
+ [flag(NDR_ALIGN4)] DATA_BLOB _pad;
+ [flag(NDR_REMAINING)] DATA_BLOB auth_info;
+ } dcerpc_bind;
+
+ const uint8 DCERPC_REQUEST_LENGTH = 24;
+
+ typedef struct {
+ } dcerpc_empty;
+
+ typedef [nodiscriminant] union {
+ [default] dcerpc_empty empty;
+ [case(LIBNDR_FLAG_OBJECT_PRESENT)] GUID object;
+ } dcerpc_object;
+
+ typedef struct {
+ uint32 alloc_hint;
+ uint16 context_id;
+ uint16 opnum;
+ [switch_is(ndr->flags & LIBNDR_FLAG_OBJECT_PRESENT)] dcerpc_object object;
+ [flag(NDR_ALIGN8)] DATA_BLOB _pad;
+ [flag(NDR_REMAINING)] DATA_BLOB stub_and_verifier;
+ } dcerpc_request;
+
+ const int DCERPC_BIND_REASON_ASYNTAX = 1;
+ const int DCERPC_BIND_PROVIDER_REJECT = 2;
+ const int DECRPC_BIND_PROTOCOL_VERSION_NOT_SUPPORTED = 4;
+ const int DCERPC_BIND_REASON_INVALID_AUTH_TYPE = 8;
+
+ typedef struct {
+ uint16 result;
+ uint16 reason;
+ ndr_syntax_id syntax;
+ } dcerpc_ack_ctx;
+
+ typedef struct {
+ uint16 max_xmit_frag;
+ uint16 max_recv_frag;
+ uint32 assoc_group_id;
+ [value(strlen(secondary_address)+1)] uint16 secondary_address_size;
+ [charset(DOS)] uint8 secondary_address[secondary_address_size];
+ [flag(NDR_ALIGN4)] DATA_BLOB _pad1;
+ uint8 num_results;
+ dcerpc_ack_ctx ctx_list[num_results];
+ [flag(NDR_REMAINING)] DATA_BLOB auth_info;
+ } dcerpc_bind_ack;
+
+ typedef struct {
+ uint32 num_versions;
+ uint32 versions[num_versions];
+ } dcerpc_bind_nak_versions;
+
+ typedef [nodiscriminant] union {
+ [case(DECRPC_BIND_PROTOCOL_VERSION_NOT_SUPPORTED)] dcerpc_bind_nak_versions v;
+ [default] ;
+ } dcerpc_bind_nak_versions_ctr;
+
+ typedef struct {
+ uint16 reject_reason;
+ [switch_is(reject_reason)] dcerpc_bind_nak_versions_ctr versions;
+ } dcerpc_bind_nak;
+
+ const uint8 DCERPC_RESPONSE_LENGTH = 24;
+
+ typedef struct {
+ uint32 alloc_hint;
+ uint16 context_id;
+ uint8 cancel_count;
+ [flag(NDR_ALIGN8)] DATA_BLOB _pad;
+ [flag(NDR_REMAINING)] DATA_BLOB stub_and_verifier;
+ } dcerpc_response;
+
+
+ const int DCERPC_FAULT_OP_RNG_ERROR = 0x1c010002;
+ const int DCERPC_FAULT_UNK_IF = 0x1c010003;
+ const int DCERPC_FAULT_NDR = 0x000006f7;
+ const int DCERPC_FAULT_INVALID_TAG = 0x1c000006;
+ const int DCERPC_FAULT_CONTEXT_MISMATCH = 0x1c00001a;
+ const int DCERPC_FAULT_OTHER = 0x00000001;
+ const int DCERPC_FAULT_ACCESS_DENIED = 0x00000005;
+ const int DCERPC_FAULT_CANT_PERFORM = 0x000006d8;
+
+ /* we return this fault when we haven't yet run the test
+ to see what fault w2k3 returns in this case */
+ const int DCERPC_FAULT_TODO = 0x00000042;
+
+ typedef struct {
+ uint32 alloc_hint;
+ uint16 context_id;
+ uint8 cancel_count;
+ uint32 status;
+ [flag(NDR_REMAINING)] DATA_BLOB _pad;
+ } dcerpc_fault;
+
+ /* the auth types we know about */
+ typedef [enum8bit] enum {
+ DCERPC_AUTH_TYPE_NONE = 0,
+ /* this seems to be not krb5! */
+ DCERPC_AUTH_TYPE_KRB5_1 = 1,
+ DCERPC_AUTH_TYPE_SPNEGO = 9,
+ DCERPC_AUTH_TYPE_NTLMSSP = 10,
+ DCERPC_AUTH_TYPE_KRB5 = 16,
+ DCERPC_AUTH_TYPE_DPA = 17,
+ DCERPC_AUTH_TYPE_MSN = 18,
+ DCERPC_AUTH_TYPE_DIGEST = 21,
+ DCERPC_AUTH_TYPE_SCHANNEL = 68,
+ DCERPC_AUTH_TYPE_MSMQ = 100
+ } dcerpc_AuthType;
+
+ typedef [enum8bit] enum {
+ DCERPC_AUTH_LEVEL_NONE = 1,
+ DCERPC_AUTH_LEVEL_CONNECT = 2,
+ DCERPC_AUTH_LEVEL_CALL = 3,
+ DCERPC_AUTH_LEVEL_PACKET = 4,
+ DCERPC_AUTH_LEVEL_INTEGRITY = 5,
+ DCERPC_AUTH_LEVEL_PRIVACY = 6
+ } dcerpc_AuthLevel;
+
+ const uint8 DCERPC_AUTH_LEVEL_DEFAULT = DCERPC_AUTH_LEVEL_CONNECT;
+
+ typedef [public] struct {
+ dcerpc_AuthType auth_type;
+ dcerpc_AuthLevel auth_level;
+ uint8 auth_pad_length;
+ uint8 auth_reserved;
+ uint32 auth_context_id;
+ [flag(NDR_REMAINING)] DATA_BLOB credentials;
+ } dcerpc_auth;
+
+ const uint8 DCERPC_AUTH_TRAILER_LENGTH = 8;
+
+ typedef [public] struct {
+ uint32 _pad;
+ [flag(NDR_REMAINING)] DATA_BLOB auth_info;
+ } dcerpc_auth3;
+
+ typedef [public] struct {
+ uint32 _pad;
+ [flag(NDR_REMAINING)] DATA_BLOB auth_info;
+ } dcerpc_orphaned;
+
+ typedef [public] struct {
+ uint32 _pad;
+ [flag(NDR_REMAINING)] DATA_BLOB auth_info;
+ } dcerpc_co_cancel;
+
+ typedef [public] struct {
+ uint32 version;
+ uint32 id;
+ } dcerpc_cl_cancel;
+
+ typedef [public] struct {
+ uint32 version;
+ uint32 id;
+ boolean32 server_is_accepting;
+ } dcerpc_cancel_ack;
+
+ typedef [public] struct {
+ uint32 version;
+ uint8 _pad1;
+ uint16 window_size;
+ uint32 max_tdsu;
+ uint32 max_frag_size;
+ uint16 serial_no;
+ uint16 selack_size;
+ uint32 selack[selack_size];
+ } dcerpc_fack;
+
+ typedef [public] struct {
+ } dcerpc_ack;
+
+ typedef [public] struct {
+ } dcerpc_ping;
+
+ typedef [public] struct {
+ } dcerpc_shutdown;
+
+ typedef [public] struct {
+ } dcerpc_working;
+
+ typedef [enum8bit] enum {
+ DCERPC_PKT_REQUEST = 0, /* Ordinary request. */
+ DCERPC_PKT_PING = 1, /* Connectionless is server alive ? */
+ DCERPC_PKT_RESPONSE = 2, /* Ordinary reply. */
+ DCERPC_PKT_FAULT = 3, /* Fault in processing of call. */
+ DCERPC_PKT_WORKING = 4, /* Connectionless reply to a ping when server busy. */
+ DCERPC_PKT_NOCALL = 5, /* Connectionless reply to a ping when server has lost part of clients call. */
+ DCERPC_PKT_REJECT = 6, /* Refuse a request with a code. */
+ DCERPC_PKT_ACK = 7, /* Connectionless client to server code. */
+ DCERPC_PKT_CL_CANCEL = 8, /* Connectionless cancel. */
+ DCERPC_PKT_FACK = 9, /* Connectionless fragment ack. Both client and server send. */
+ DCERPC_PKT_CANCEL_ACK = 10, /* Server ACK to client cancel request. */
+ DCERPC_PKT_BIND = 11, /* Bind to interface. */
+ DCERPC_PKT_BIND_ACK = 12, /* Server ack of bind. */
+ DCERPC_PKT_BIND_NAK = 13, /* Server nack of bind. */
+ DCERPC_PKT_ALTER = 14, /* Alter auth. */
+ DCERPC_PKT_ALTER_RESP = 15, /* Reply to alter auth. */
+ DCERPC_PKT_AUTH3 = 16, /* not the real name! this is undocumented! */
+ DCERPC_PKT_SHUTDOWN = 17, /* Server to client request to shutdown. */
+ DCERPC_PKT_CO_CANCEL = 18, /* Connection-oriented cancel request. */
+ DCERPC_PKT_ORPHANED = 19 /* Client telling server it's aborting a partially sent request or telling server to stop sending replies. */
+ } dcerpc_pkt_type;
+
+ typedef [nodiscriminant] union {
+ [case(DCERPC_PKT_REQUEST)] dcerpc_request request;
+ [case(DCERPC_PKT_PING)] dcerpc_ping ping;
+ [case(DCERPC_PKT_RESPONSE)] dcerpc_response response;
+ [case(DCERPC_PKT_FAULT)] dcerpc_fault fault;
+ [case(DCERPC_PKT_WORKING)] dcerpc_working working;
+ [case(DCERPC_PKT_NOCALL)] dcerpc_fack nocall;
+ [case(DCERPC_PKT_REJECT)] dcerpc_fault reject;
+ [case(DCERPC_PKT_ACK)] dcerpc_ack ack;
+ [case(DCERPC_PKT_CL_CANCEL)] dcerpc_cl_cancel cl_cancel;
+ [case(DCERPC_PKT_FACK)] dcerpc_fack fack;
+ [case(DCERPC_PKT_CANCEL_ACK)] dcerpc_cancel_ack cancel_ack;
+ [case(DCERPC_PKT_BIND)] dcerpc_bind bind;
+ [case(DCERPC_PKT_BIND_ACK)] dcerpc_bind_ack bind_ack;
+ [case(DCERPC_PKT_BIND_NAK)] dcerpc_bind_nak bind_nak;
+ [case(DCERPC_PKT_ALTER)] dcerpc_bind alter;
+ [case(DCERPC_PKT_ALTER_RESP)] dcerpc_bind_ack alter_resp;
+ [case(DCERPC_PKT_SHUTDOWN)] dcerpc_shutdown shutdown;
+ [case(DCERPC_PKT_CO_CANCEL)] dcerpc_co_cancel co_cancel;
+ [case(DCERPC_PKT_ORPHANED)] dcerpc_orphaned orphaned;
+ [case(DCERPC_PKT_AUTH3)] dcerpc_auth3 auth3;
+ } dcerpc_payload;
+
+ /* pfc_flags values */
+ const uint8 DCERPC_PFC_FLAG_FIRST = 0x01; /* First fragment */
+ const uint8 DCERPC_PFC_FLAG_LAST = 0x02; /* Last fragment */
+ const uint8 DCERPC_PFC_FLAG_PENDING_CANCEL = 0x04; /* Cancel was pending at sender */
+ const uint8 DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN = DCERPC_PFC_FLAG_PENDING_CANCEL; /* depends on the pdu type */
+ const uint8 DCERPC_PFC_FLAG_CONC_MPX = 0x10; /* supports concurrent multiplexing of a single connection. */
+ const uint8 DCERPC_PFC_FLAG_DID_NOT_EXECUTE = 0x20; /* on a fault it means the server hasn't done anything */
+ const uint8 DCERPC_PFC_FLAG_MAYBE = 0x40; /* `maybe' call semantics requested */
+ const uint8 DCERPC_PFC_FLAG_OBJECT_UUID = 0x80; /* on valid guid is in the optional object field */
+
+ /* these offsets are needed by the signing code */
+ const uint8 DCERPC_PFC_OFFSET = 3;
+ const uint8 DCERPC_DREP_OFFSET = 4;
+ const uint8 DCERPC_FRAG_LEN_OFFSET = 8;
+ const uint8 DCERPC_AUTH_LEN_OFFSET = 10;
+
+ /* little-endian flag */
+ const uint8 DCERPC_DREP_LE = 0x10;
+
+ typedef [public] struct {
+ uint8 rpc_vers; /* RPC version */
+ uint8 rpc_vers_minor; /* Minor version */
+ dcerpc_pkt_type ptype; /* Packet type */
+ uint8 pfc_flags; /* Fragmentation flags */
+ uint8 drep[4]; /* NDR data representation */
+ uint16 frag_length; /* Total length of fragment */
+ uint16 auth_length; /* authenticator length */
+ uint32 call_id; /* Call identifier */
+ [switch_is(ptype)] dcerpc_payload u;
+ } ncacn_packet;
+
+ typedef [public] struct {
+ uint8 rpc_vers; /* RPC version (4) */
+ uint8 ptype;
+ uint8 pfc_flags;
+ uint8 ncadg_flags;
+ uint8 drep[3];
+ uint8 serial_high;
+ GUID object;
+ GUID iface;
+ GUID activity;
+ uint32 server_boot; /* Server boot time */
+ uint32 iface_version;
+ uint32 seq_num;
+ uint16 opnum;
+ uint16 ihint;
+ uint16 ahint;
+ uint16 len;
+ uint16 fragnum;
+ uint8 auth_proto;
+ uint8 serial_low;
+ [switch_is(ptype)] dcerpc_payload u;
+ } ncadg_packet;
+}
diff --git a/source4/librpc/idl/irpc.idl b/source4/librpc/idl/irpc.idl
new file mode 100644
index 0000000000..41787355a9
--- /dev/null
+++ b/source4/librpc/idl/irpc.idl
@@ -0,0 +1,144 @@
+#include "idl_types.h"
+
+import "misc.idl", "security.idl", "nbt.idl";
+
+/*
+ definitions for irpc primitives
+*/
+[ uuid("e770c620-0b06-4b5e-8d87-a26e20f28340"),
+ version(1.0),
+ pointer_default(unique)
+] interface irpc
+{
+ typedef bitmap {
+ IRPC_FLAG_REPLY = 0x0001
+ } irpc_flags;
+
+ typedef [public] struct {
+ GUID uuid;
+ uint32 if_version;
+ uint32 callnum;
+ uint32 callid;
+ irpc_flags flags;
+ NTSTATUS status;
+ } irpc_header;
+
+ /******************************************************
+ uptime call - supported by all messaging servers
+ *******************************************************/
+ void irpc_uptime([out,ref] NTTIME *start_time);
+
+ /******************************************************
+ management calls for the nbt server
+ ******************************************************/
+ typedef [v1_enum] enum {
+ NBTD_INFO_STATISTICS
+ } nbtd_info_level;
+
+ typedef struct {
+ hyper total_received;
+ hyper total_sent;
+ hyper query_count;
+ hyper register_count;
+ hyper release_count;
+ } nbtd_statistics;
+
+ typedef [switch_type(nbtd_info_level)] union {
+ [case(NBTD_INFO_STATISTICS)] nbtd_statistics *stats;
+ } nbtd_info;
+
+ void nbtd_information(
+ [in] nbtd_info_level level,
+ [out,switch_is(level)] nbtd_info info
+ );
+
+ /* Send a GetDCName from the privilaged port (owned by nbtd),
+ * and await a reply */
+
+ void nbtd_getdcname(
+ [in] astring domainname,
+ [in] astring ip_address,
+ [in] astring my_computername,
+ [in] astring my_accountname,
+ [in] uint32 account_control,
+ [in] dom_sid *domain_sid,
+ [out,unique] astring *dcname
+ );
+
+ typedef struct {
+ ipv4address addr;
+ } nbtd_proxy_wins_addr;
+
+ void nbtd_proxy_wins_challenge(
+ [in] nbt_name name,
+ [in,out] uint32 num_addrs,
+ [in,out] nbtd_proxy_wins_addr addrs[num_addrs]
+ );
+
+ void nbtd_proxy_wins_release_demand(
+ [in] nbt_name name,
+ [in] uint32 num_addrs,
+ [in] nbtd_proxy_wins_addr addrs[num_addrs]
+ );
+
+ /*
+ Generic Kerberos package call (on the NETLOGON pipe, as a SamLogon)
+
+ The normal use for this call is to check the PAC signature in the KDC
+
+ The KDC has the routines to check this, so it is easier to
+ proxy the request over by IRPC than set up the environment
+ */
+
+ void kdc_check_generic_kerberos(
+ [in] DATA_BLOB generic_request,
+ [out] DATA_BLOB generic_reply
+ );
+
+ /******************************************************
+ management calls for the smb server
+ ******************************************************/
+ typedef [v1_enum] enum {
+ SMBSRV_INFO_SESSIONS,
+ SMBSRV_INFO_TCONS
+ } smbsrv_info_level;
+
+ typedef struct {
+ hyper vuid;
+ astring account_name;
+ astring domain_name;
+ astring client_ip;
+ NTTIME connect_time;
+ NTTIME auth_time;
+ NTTIME last_use_time;
+ } smbsrv_session_info;
+
+ typedef struct {
+ uint32 num_sessions;
+ [size_is(num_sessions)] smbsrv_session_info *sessions;
+ } smbsrv_sessions;
+
+ typedef struct {
+ uint32 tid;
+ astring share_name;
+ astring client_ip;
+ NTTIME connect_time;
+ NTTIME last_use_time;
+ } smbsrv_tcon_info;
+
+ typedef struct {
+ uint32 num_tcons;
+ [size_is(num_tcons)] smbsrv_tcon_info *tcons;
+ } smbsrv_tcons;
+
+ typedef [switch_type(smbsrv_info_level)] union {
+ [case(SMBSRV_INFO_SESSIONS)] smbsrv_sessions sessions;
+ [case(SMBSRV_INFO_TCONS)] smbsrv_tcons tcons;
+ } smbsrv_info;
+
+ void smbsrv_information(
+ [in] smbsrv_info_level level,
+ [out,switch_is(level)] smbsrv_info info
+ );
+
+}
diff --git a/source4/librpc/idl/nfs4acl.idl b/source4/librpc/idl/nfs4acl.idl
new file mode 100644
index 0000000000..3d4379ad5e
--- /dev/null
+++ b/source4/librpc/idl/nfs4acl.idl
@@ -0,0 +1,42 @@
+#include "idl_types.h"
+
+/*
+ NFS4 ACL format on disk
+ see http://www.suse.de/~agruen/nfs4acl/
+*/
+
+import "misc.idl", "security.idl";
+
+[ uuid("18763978-8625-abc3-54ca-9892bacdf321"),
+ version(1.0),
+ pointer_default(unique)
+]
+interface nfs4acl
+{
+ const char *NFS4ACL_XATTR_NAME = "system.nfs4acl";
+
+ /* these structures use the same bit values and other constants as
+ in security.idl */
+ typedef [flag(NDR_BIG_ENDIAN)] struct {
+ uint16 e_type;
+ uint16 e_flags;
+ uint32 e_mask;
+ uint32 e_id;
+ utf8string e_who;
+ [flag(NDR_ALIGN4)] DATA_BLOB _pad;
+ } nfs4ace;
+
+ typedef [public,flag(NDR_BIG_ENDIAN)] struct {
+ uint8 a_version;
+ uint8 a_flags;
+ uint16 a_count;
+ uint32 a_owner_mask;
+ uint32 a_group_mask;
+ uint32 a_other_mask;
+ nfs4ace ace[a_count];
+ } nfs4acl;
+
+ NTSTATUS nfs4acl_test(
+ [in] nfs4acl acl
+ );
+}
diff --git a/source4/librpc/idl/notify.idl b/source4/librpc/idl/notify.idl
new file mode 100644
index 0000000000..a19f737678
--- /dev/null
+++ b/source4/librpc/idl/notify.idl
@@ -0,0 +1,58 @@
+#include "idl_types.h"
+
+/*
+ IDL structures for notify change code
+
+ this defines the structures used in the notify database code, and
+ the change notify buffers
+*/
+
+import "server_id.idl";
+
+[
+ pointer_default(unique)
+]
+interface notify
+{
+
+ /* structure used in the notify database */
+ typedef [public] struct {
+ server_id server;
+ uint32 filter; /* filter to apply in this directory */
+ uint32 subdir_filter; /* filter to apply in child directories */
+ utf8string path;
+ uint32 path_len; /* saves some computation on search */
+ pointer private_data;
+ } notify_entry;
+
+ /*
+ to allow for efficient search for matching entries, we
+ divide them by the directory depth, with a separate array
+ per depth. The entries within each depth are sorted by path,
+ allowing for a bisection search.
+
+ The max_mask and max_mask_subdir at each depth is the
+ bitwise or of the filters and subdir filters for all entries
+ at that depth. This allows a depth to be quickly skipped if
+ no entries will match the target filter
+ */
+ typedef struct {
+ uint32 max_mask;
+ uint32 max_mask_subdir;
+ uint32 num_entries;
+ notify_entry entries[num_entries];
+ } notify_depth;
+
+ typedef [public] struct {
+ uint32 num_depths;
+ notify_depth depth[num_depths];
+ } notify_array;
+
+ /* structure sent between servers in notify messages */
+ typedef [public] struct {
+ uint32 action;
+ utf8string path;
+ pointer private_data;
+ } notify_event;
+
+}
diff --git a/source4/librpc/idl/ntp_signd.idl b/source4/librpc/idl/ntp_signd.idl
new file mode 100644
index 0000000000..2b2fbc7662
--- /dev/null
+++ b/source4/librpc/idl/ntp_signd.idl
@@ -0,0 +1,44 @@
+/*
+ NTP signing IRPC interface
+*/
+
+#include "idl_types.h"
+
+[
+ uuid("0da00951-5b6c-4488-9a89-750cac70920c"),
+ version(1.0),
+ pointer_default(unique)
+]
+interface ntp_signd
+{
+
+ typedef [v1_enum] enum {
+ SIGN_TO_CLIENT = 0,
+ ASK_SERVER_TO_SIGN = 1,
+ CHECK_SERVER_SIGNATURE = 2,
+ SIGNING_SUCCESS = 3,
+ SIGNING_FAILURE = 4
+ } ntp_signd_op;
+
+ typedef [flag(NDR_BIG_ENDIAN),public] struct {
+ uint32 version;
+ ntp_signd_op op;
+ uint16 packet_id;
+ [flag(NDR_LITTLE_ENDIAN)] uint32 key_id;
+ [flag(NDR_REMAINING)] DATA_BLOB packet_to_sign;
+
+ } sign_request;
+
+ typedef [flag(NDR_BIG_ENDIAN),public] struct samba_key_out {
+ uint32 version;
+ ntp_signd_op op;
+ uint32 packet_id;
+ [flag(NDR_REMAINING)] DATA_BLOB signed_packet;
+ } signed_reply;
+
+ void decode_sign_requst(
+ [in] sign_request request
+ );
+
+
+}
diff --git a/source4/librpc/idl/opendb.idl b/source4/librpc/idl/opendb.idl
new file mode 100644
index 0000000000..b76992960a
--- /dev/null
+++ b/source4/librpc/idl/opendb.idl
@@ -0,0 +1,46 @@
+#include "idl_types.h"
+
+/*
+ IDL structures for opendb code
+
+ this defines the structures used in the opendb database code, in
+ ntvfs/common/opendb.c
+*/
+
+import "server_id.idl";
+
+[
+ pointer_default(unique)
+]
+interface opendb
+{
+ typedef struct {
+ server_id server;
+ uint32 stream_id;
+ uint32 share_access;
+ uint32 access_mask;
+ pointer file_handle;
+ pointer fd;
+ /* we need a per-entry delete on close, as well as a per-file
+ one, to cope with strange semantics on open */
+ boolean8 delete_on_close;
+ boolean8 allow_level_II_oplock;
+ uint32 oplock_level;
+ } opendb_entry;
+
+ typedef struct {
+ server_id server;
+ pointer notify_ptr;
+ } opendb_pending;
+
+ typedef [public] struct {
+ boolean8 delete_on_close;
+ NTTIME open_write_time;
+ NTTIME changed_write_time;
+ utf8string path;
+ uint32 num_entries;
+ opendb_entry entries[num_entries];
+ uint32 num_pending;
+ opendb_pending pending[num_pending];
+ } opendb_file;
+}
diff --git a/source4/librpc/idl/sasl_helpers.idl b/source4/librpc/idl/sasl_helpers.idl
new file mode 100644
index 0000000000..8fa4b578b9
--- /dev/null
+++ b/source4/librpc/idl/sasl_helpers.idl
@@ -0,0 +1,22 @@
+#include "idl_types.h"
+
+[
+ pointer_default(unique),
+ helpstring("SASL helpers")
+]
+interface sasl_helpers {
+ typedef [public,flag(NDR_NOALIGN|NDR_BIG_ENDIAN|NDR_PAHEX)] struct {
+ [value(strlen_m(authid))] uint16 authid_length;
+ [charset(UTF8)] uint8 authid[authid_length];
+ uint16 passwd_length;
+ uint8 passwd[passwd_length];
+ [value(strlen_m(service))] uint16 service_length;
+ [charset(UTF8)] uint8 service[service_length];
+ [value(strlen_m(realm))] uint16 realm_length;
+ [charset(UTF8)] uint8 realm[realm_length];
+ } saslauthdRequest;
+
+ void decode_saslauthd(
+ [in] saslauthdRequest req
+ );
+}
diff --git a/source4/librpc/idl/schannel.idl b/source4/librpc/idl/schannel.idl
new file mode 100644
index 0000000000..9cb9e1fb61
--- /dev/null
+++ b/source4/librpc/idl/schannel.idl
@@ -0,0 +1,44 @@
+#include "idl_types.h"
+
+/*
+ schannel structures
+*/
+
+import "netlogon.idl", "nbt.idl";
+
+interface schannel
+{
+ /*
+ a schannel bind blob - used in dcerpc auth_info
+ on a schannel
+ */
+ typedef struct {
+ astring domain;
+ astring workstation;
+ } schannel_bind_3;
+
+ typedef struct {
+ astring domain;
+ astring workstation;
+ nbt_string dnsdomain;
+ nbt_string dnsworkstation;
+ } schannel_bind_23;
+
+ typedef [nodiscriminant] union {
+ [case (3)] schannel_bind_3 info3;
+ [case (23)] schannel_bind_23 info23;
+ } schannel_bind_info;
+
+ typedef [public] struct {
+ uint32 unknown1; /* seems to need to be 0 */
+ uint32 bind_type;
+ [switch_is(bind_type)] schannel_bind_info u;
+ } schannel_bind;
+
+ /* a bind_ack blob */
+ typedef [public] struct {
+ uint32 unknown1; /* 1 */
+ uint32 unknown2; /* 0 */
+ uint32 unknown3; /* 0x006c0000 */
+ } schannel_bind_ack;
+}
diff --git a/source4/librpc/idl/server_id.idl b/source4/librpc/idl/server_id.idl
new file mode 100644
index 0000000000..486143546b
--- /dev/null
+++ b/source4/librpc/idl/server_id.idl
@@ -0,0 +1,12 @@
+[
+ pointer_default(unique)
+]
+interface server_id
+{
+ /* id used to identify a endpoint, possibly in a cluster */
+ typedef [public] struct {
+ hyper id;
+ uint32 id2;
+ uint32 node;
+ } server_id;
+}
diff --git a/source4/librpc/idl/winbind.idl b/source4/librpc/idl/winbind.idl
new file mode 100644
index 0000000000..5cefb38f75
--- /dev/null
+++ b/source4/librpc/idl/winbind.idl
@@ -0,0 +1,68 @@
+/*
+ winbind IRPC interface
+*/
+
+#include "idl_types.h"
+
+import "netlogon.idl", "lsa.idl", "security.idl";
+
+[
+ uuid("245f3e6b-3c5d-6e21-3a2d-2a3d645b7221"),
+ version(1.0),
+ pointer_default(unique)
+]
+interface winbind
+{
+ typedef [switch_type(uint16)] union netr_LogonLevel netr_LogonLevel;
+ typedef [switch_type(uint16)] union netr_Validation netr_Validation;
+
+ typedef enum {
+ ID_TYPE_NOT_SPECIFIED,
+ ID_TYPE_UID,
+ ID_TYPE_GID,
+ ID_TYPE_BOTH
+ } id_type;
+
+ typedef struct {
+ uint32 id;
+ id_type type;
+ } unixid;
+
+ typedef struct {
+ unixid *unixid;
+ dom_sid *sid;
+ NTSTATUS status;
+ } id_mapping;
+
+ /* a call to get runtime informations */
+ void winbind_information(/* TODO */);
+
+ /*
+ * a call to trigger some internal events,
+ * for use in torture tests...
+ */
+ NTSTATUS winbind_remote_control(/* TODO */);
+
+ /*
+ * do a netr_LogonSamLogon() against the right DC
+ */
+ NTSTATUS winbind_SamLogon(
+ [in] uint16 logon_level,
+ [in] [switch_is(logon_level)] netr_LogonLevel logon,
+ [in] uint16 validation_level,
+ [out] [switch_is(validation_level)] netr_Validation validation,
+ [out] uint8 authoritative
+ );
+
+ typedef [v1_enum] enum {
+ WINBIND_IDMAP_LEVEL_SIDS_TO_XIDS = 1,
+ WINBIND_IDMAP_LEVEL_XIDS_TO_SIDS = 2
+ } winbind_get_idmap_level;
+
+ NTSTATUS winbind_get_idmap(
+ [in] winbind_get_idmap_level level,
+ [in] uint32 count,
+ [in,out] [size_is(count)] id_mapping ids[]
+ );
+
+}
diff --git a/source4/librpc/idl/wins.idl b/source4/librpc/idl/wins.idl
new file mode 100644
index 0000000000..dc98cf4259
--- /dev/null
+++ b/source4/librpc/idl/wins.idl
@@ -0,0 +1,27 @@
+[
+ uuid("45f52c28-7f9f-101a-b52b-08002b2efabe"),
+ version(1.0),
+ helpstring("Server-to-Server WINS")
+] interface WinsPipe
+{
+ void WinsRecordAction();
+ void WinsStatus();
+ void WinsTrigger();
+ void WinsDoStaticInit();
+ void WinsDoScavenging();
+ void WinsGetDbRecs();
+ void WinsTerm();
+ void WinsBackup();
+ void WinsDelDbRecs();
+ void WinsPullRange();
+ void WinsSetPriorityClass();
+ void WinsResetCounters();
+ void WinsWorkerThreadUpdate();
+ void WinsGetNameAndAdd();
+ void WinsGetBrowserNames_Old();
+ void WinsDeleteWins();
+ void WinsSetFlags();
+ void WinsGetDbRecsByName();
+ void WinsStatusWHdl();
+ void WinsDoScanvenging2();
+}
diff --git a/source4/librpc/idl/winsrepl.idl b/source4/librpc/idl/winsrepl.idl
new file mode 100644
index 0000000000..0ec05e891c
--- /dev/null
+++ b/source4/librpc/idl/winsrepl.idl
@@ -0,0 +1,175 @@
+#include "idl_types.h"
+
+/*
+ IDL structures for WINS replication protocol (port 42)
+
+ Note that WINS replication is not traditionally encoded using
+ IDL/NDR
+
+ Written by Andrew Tridgell <tridge@osdl.org>
+*/
+
+import "nbt.idl";
+
+interface wrepl
+{
+ const int WINS_REPLICATION_PORT = 42;
+
+ typedef [flag(NDR_BIG_ENDIAN)] struct {
+ ipv4address owner;
+ ipv4address ip;
+ } wrepl_ip;
+
+ typedef [flag(NDR_LITTLE_ENDIAN)] struct {
+ uint32 num_ips;
+ wrepl_ip ips[num_ips];
+ } wrepl_address_list;
+
+ typedef [nodiscriminant] union {
+ [case(0)] ipv4address ip;
+ [case(2)] wrepl_address_list addresses;
+ } wrepl_addresses;
+
+ typedef [enum8bit] enum {
+ WREPL_TYPE_UNIQUE = 0x0,
+ WREPL_TYPE_GROUP = 0x1,
+ WREPL_TYPE_SGROUP = 0x2,
+ WREPL_TYPE_MHOMED = 0x3
+ } wrepl_name_type;
+
+ typedef [enum8bit] enum {
+ WREPL_STATE_ACTIVE = 0x0,
+ WREPL_STATE_RELEASED = 0x1,
+ WREPL_STATE_TOMBSTONE = 0x2,
+ WREPL_STATE_RESERVED = 0x3
+ } wrepl_name_state;
+
+ typedef [enum8bit] enum {
+ WREPL_NODE_B = 0x0,
+ WREPL_NODE_P = 0x1,
+ WREPL_NODE_M = 0x2,
+ WREPL_NODE_H = 0x3
+ } wrepl_name_node;
+
+ typedef [bitmap32bit] bitmap {
+ WREPL_FLAGS_RECORD_TYPE = 0x00000003,
+ WREPL_FLAGS_RECORD_STATE = 0x0000000C,
+ WREPL_FLAGS_REGISTERED_LOCAL = 0x00000010,
+ WREPL_FLAGS_NODE_TYPE = 0x00000060,
+ WREPL_FLAGS_IS_STATIC = 0x00000080
+ } wrepl_flags;
+
+ typedef [v1_enum] enum {
+ WREPL_GROUP_FLAG_NO_GROUP = 0x00000000,
+ WREPL_GROUP_FLAG_IS_GROUP = 0x00000001
+ } wrepl_group_flag;
+
+#define WREPL_IS_GROUP(flags) (\
+ ((((flags) & WREPL_FLAGS_RECORD_TYPE) == WREPL_TYPE_GROUP)|| \
+ (((flags) & WREPL_FLAGS_RECORD_TYPE) == WREPL_TYPE_SGROUP))\
+ ? WREPL_GROUP_FLAG_IS_GROUP : WREPL_GROUP_FLAG_NO_GROUP)
+
+ typedef struct {
+ wrepl_nbt_name name;
+ wrepl_flags flags;
+ [flag(NDR_LITTLE_ENDIAN),value(WREPL_IS_GROUP(flags))] wrepl_group_flag is_group;
+ udlongr id;
+ [switch_is(flags & 2)] wrepl_addresses addresses;
+ ipv4address unknown;
+ } wrepl_wins_name;
+
+ typedef struct {
+ uint32 num_names;
+ wrepl_wins_name names[num_names];
+ } wrepl_send_reply;
+
+ typedef struct {
+ ipv4address address;
+ udlongr max_version;
+ udlongr min_version;
+ uint32 type;
+ } wrepl_wins_owner;
+
+ typedef struct {
+ uint32 partner_count;
+ wrepl_wins_owner partners[partner_count];
+ ipv4address initiator;
+ } wrepl_table;
+
+ typedef [v1_enum] enum {
+ WREPL_REPL_TABLE_QUERY = 0,
+ WREPL_REPL_TABLE_REPLY = 1,
+ WREPL_REPL_SEND_REQUEST = 2,
+ WREPL_REPL_SEND_REPLY = 3,
+ WREPL_REPL_UPDATE = 4,
+ WREPL_REPL_UPDATE2 = 5,
+ WREPL_REPL_INFORM = 8,
+ WREPL_REPL_INFORM2 = 9
+ } wrepl_replication_cmd;
+
+ typedef [nodiscriminant] union {
+ [case(WREPL_REPL_TABLE_QUERY)] ;
+ [case(WREPL_REPL_TABLE_REPLY)] wrepl_table table;
+ [case(WREPL_REPL_SEND_REQUEST)] wrepl_wins_owner owner;
+ [case(WREPL_REPL_SEND_REPLY)] wrepl_send_reply reply;
+ [case(WREPL_REPL_UPDATE)] wrepl_table table;
+ [case(WREPL_REPL_UPDATE2)] wrepl_table table;
+ [case(WREPL_REPL_INFORM)] wrepl_table table;
+ [case(WREPL_REPL_INFORM2)] wrepl_table table;
+ } wrepl_replication_info;
+
+ typedef struct {
+ wrepl_replication_cmd command;
+ [switch_is(command)] wrepl_replication_info info;
+ } wrepl_replication;
+
+ typedef struct {
+ uint32 assoc_ctx;
+ uint16 minor_version;
+ uint16 major_version;
+ } wrepl_start;
+
+ typedef struct {
+ uint32 reason;
+ } wrepl_stop;
+
+ typedef [v1_enum] enum {
+ WREPL_START_ASSOCIATION = 0,
+ WREPL_START_ASSOCIATION_REPLY = 1,
+ WREPL_STOP_ASSOCIATION = 2,
+ WREPL_REPLICATION = 3
+ } wrepl_mess_type;
+
+ typedef [nodiscriminant] union {
+ [case(WREPL_START_ASSOCIATION)] wrepl_start start;
+ [case(WREPL_START_ASSOCIATION_REPLY)] wrepl_start start_reply;
+ [case(WREPL_STOP_ASSOCIATION)] wrepl_stop stop;
+ [case(WREPL_REPLICATION)] wrepl_replication replication;
+ } wrepl_message;
+
+ /*
+ the opcode appears to be a bitfield, but as far as I can tell
+ you must always set the following bits. Additional bits don't
+ seem to matter. Very strange.
+ */
+ const int WREPL_OPCODE_BITS = 0x7800;
+
+
+ typedef [gensize,flag(NDR_BIG_ENDIAN|NDR_PAHEX),public] struct {
+ uint32 opcode;
+ uint32 assoc_ctx;
+ wrepl_mess_type mess_type;
+ [switch_is(mess_type)] wrepl_message message;
+ [flag(NDR_REMAINING)] DATA_BLOB padding;
+ } wrepl_packet;
+
+ typedef [flag(NDR_BIG_ENDIAN|NDR_PAHEX),public] struct {
+ [value(ndr_size_wrepl_packet(&packet, ndr->iconv_convenience, ndr->flags))] uint32 size;
+ wrepl_packet packet;
+ } wrepl_wrap;
+
+ void decode_winsrepl(
+ [in] wrepl_wrap p
+ );
+
+}
diff --git a/source4/librpc/idl/winstation.idl b/source4/librpc/idl/winstation.idl
new file mode 100644
index 0000000000..fb02fa73df
--- /dev/null
+++ b/source4/librpc/idl/winstation.idl
@@ -0,0 +1,13 @@
+/*
+ winstation interface definition
+*/
+
+#include "idl_types.h"
+
+[ uuid("5ca4a760-ebb1-11cf-8611-00a0245420ed"),
+ version(1.0),
+ helpstring("Terminal Services remote management")
+] interface winstation
+{
+ void winstation_foo();
+}
diff --git a/source4/librpc/ndr/ndr_string.c b/source4/librpc/ndr/ndr_string.c
new file mode 100644
index 0000000000..d6d996846e
--- /dev/null
+++ b/source4/librpc/ndr/ndr_string.c
@@ -0,0 +1,711 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ routines for marshalling/unmarshalling string types
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "librpc/ndr/libndr.h"
+
+/**
+ pull a general string from the wire
+*/
+_PUBLIC_ enum ndr_err_code ndr_pull_string(struct ndr_pull *ndr, int ndr_flags, const char **s)
+{
+ char *as=NULL;
+ uint32_t len1, ofs, len2;
+ uint16_t len3;
+ size_t ret;
+ charset_t chset = CH_UTF16;
+ unsigned byte_mul = 2;
+ unsigned flags = ndr->flags;
+ unsigned c_len_term = 0;
+
+ if (!(ndr_flags & NDR_SCALARS)) {
+ return NDR_ERR_SUCCESS;
+ }
+
+ if (NDR_BE(ndr)) {
+ chset = CH_UTF16BE;
+ }
+
+ if (flags & LIBNDR_FLAG_STR_ASCII) {
+ chset = CH_DOS;
+ byte_mul = 1;
+ flags &= ~LIBNDR_FLAG_STR_ASCII;
+ }
+
+ if (flags & LIBNDR_FLAG_STR_UTF8) {
+ chset = CH_UTF8;
+ byte_mul = 1;
+ flags &= ~LIBNDR_FLAG_STR_UTF8;
+ }
+
+ flags &= ~LIBNDR_FLAG_STR_CONFORMANT;
+ if (flags & LIBNDR_FLAG_STR_CHARLEN) {
+ c_len_term = 1;
+ flags &= ~LIBNDR_FLAG_STR_CHARLEN;
+ }
+
+ switch (flags & LIBNDR_STRING_FLAGS) {
+ case LIBNDR_FLAG_STR_LEN4|LIBNDR_FLAG_STR_SIZE4:
+ case LIBNDR_FLAG_STR_LEN4|LIBNDR_FLAG_STR_SIZE4|LIBNDR_FLAG_STR_NOTERM:
+ NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &len1));
+ NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &ofs));
+ if (ofs != 0) {
+ return ndr_pull_error(ndr, NDR_ERR_STRING, "non-zero array offset with string flags 0x%x\n",
+ ndr->flags & LIBNDR_STRING_FLAGS);
+ }
+ NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &len2));
+ if (len2 > len1) {
+ return ndr_pull_error(ndr, NDR_ERR_STRING,
+ "Bad string lengths len1=%u ofs=%u len2=%u\n",
+ len1, ofs, len2);
+ }
+ NDR_PULL_NEED_BYTES(ndr, (len2 + c_len_term)*byte_mul);
+ if (len2 == 0) {
+ as = talloc_strdup(ndr->current_mem_ctx, "");
+ } else {
+ if (!convert_string_talloc_convenience(ndr->current_mem_ctx,
+ ndr->iconv_convenience, chset, CH_UNIX,
+ ndr->data+ndr->offset,
+ (len2 + c_len_term)*byte_mul,
+ (void **)&as, &ret, false)) {
+ return ndr_pull_error(ndr, NDR_ERR_CHARCNV,
+ "Bad character conversion");
+ }
+ }
+ NDR_CHECK(ndr_pull_advance(ndr, (len2 + c_len_term)*byte_mul));
+
+ if (len1 != len2) {
+ DEBUG(6,("len1[%u] != len2[%u] '%s'\n", len1, len2, as));
+ }
+
+ /* this is a way of detecting if a string is sent with the wrong
+ termination */
+ if (ndr->flags & LIBNDR_FLAG_STR_NOTERM) {
+ if (strlen(as) < (len2 + c_len_term)) {
+ DEBUG(6,("short string '%s'\n", as));
+ }
+ } else {
+ if (strlen(as) == (len2 + c_len_term)) {
+ DEBUG(6,("long string '%s'\n", as));
+ }
+ }
+ *s = as;
+ break;
+
+ case LIBNDR_FLAG_STR_SIZE4:
+ case LIBNDR_FLAG_STR_SIZE4|LIBNDR_FLAG_STR_NOTERM:
+ NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &len1));
+ NDR_PULL_NEED_BYTES(ndr, (len1 + c_len_term)*byte_mul);
+ if (len1 == 0) {
+ as = talloc_strdup(ndr->current_mem_ctx, "");
+ } else {
+ if (!convert_string_talloc_convenience(ndr->current_mem_ctx,
+ ndr->iconv_convenience,
+ chset, CH_UNIX,
+ ndr->data+ndr->offset,
+ (len1 + c_len_term)*byte_mul,
+ (void **)&as, &ret, false)) {
+ return ndr_pull_error(ndr, NDR_ERR_CHARCNV,
+ "Bad character conversion");
+ }
+ }
+ NDR_CHECK(ndr_pull_advance(ndr, (len1 + c_len_term)*byte_mul));
+
+ /* this is a way of detecting if a string is sent with the wrong
+ termination */
+ if (ndr->flags & LIBNDR_FLAG_STR_NOTERM) {
+ if (strlen(as) < (len1 + c_len_term)) {
+ DEBUG(6,("short string '%s'\n", as));
+ }
+ } else {
+ if (strlen(as) == (len1 + c_len_term)) {
+ DEBUG(6,("long string '%s'\n", as));
+ }
+ }
+ *s = as;
+ break;
+
+ case LIBNDR_FLAG_STR_LEN4:
+ case LIBNDR_FLAG_STR_LEN4|LIBNDR_FLAG_STR_NOTERM:
+ NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &ofs));
+ if (ofs != 0) {
+ return ndr_pull_error(ndr, NDR_ERR_STRING, "non-zero array offset with string flags 0x%x\n",
+ ndr->flags & LIBNDR_STRING_FLAGS);
+ }
+ NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &len1));
+ NDR_PULL_NEED_BYTES(ndr, (len1 + c_len_term)*byte_mul);
+ if (len1 == 0) {
+ as = talloc_strdup(ndr->current_mem_ctx, "");
+ } else {
+ if (!convert_string_talloc_convenience(ndr->current_mem_ctx,
+ ndr->iconv_convenience,
+ chset, CH_UNIX,
+ ndr->data+ndr->offset,
+ (len1 + c_len_term)*byte_mul,
+ (void **)&as, &ret, false)) {
+ return ndr_pull_error(ndr, NDR_ERR_CHARCNV,
+ "Bad character conversion");
+ }
+ }
+ NDR_CHECK(ndr_pull_advance(ndr, (len1 + c_len_term)*byte_mul));
+
+ /* this is a way of detecting if a string is sent with the wrong
+ termination */
+ if (ndr->flags & LIBNDR_FLAG_STR_NOTERM) {
+ if (strlen(as) < (len1 + c_len_term)) {
+ DEBUG(6,("short string '%s'\n", as));
+ }
+ } else {
+ if (strlen(as) == (len1 + c_len_term)) {
+ DEBUG(6,("long string '%s'\n", as));
+ }
+ }
+ *s = as;
+ break;
+
+
+ case LIBNDR_FLAG_STR_SIZE2:
+ case LIBNDR_FLAG_STR_SIZE2|LIBNDR_FLAG_STR_NOTERM:
+ NDR_CHECK(ndr_pull_uint16(ndr, NDR_SCALARS, &len3));
+ NDR_PULL_NEED_BYTES(ndr, (len3 + c_len_term)*byte_mul);
+ if (len3 == 0) {
+ as = talloc_strdup(ndr->current_mem_ctx, "");
+ } else {
+ if (!convert_string_talloc_convenience(ndr->current_mem_ctx,
+ ndr->iconv_convenience,
+ chset, CH_UNIX,
+ ndr->data+ndr->offset,
+ (len3 + c_len_term)*byte_mul,
+ (void **)&as, &ret, false)) {
+ return ndr_pull_error(ndr, NDR_ERR_CHARCNV,
+ "Bad character conversion");
+ }
+ }
+ NDR_CHECK(ndr_pull_advance(ndr, (len3 + c_len_term)*byte_mul));
+
+ /* this is a way of detecting if a string is sent with the wrong
+ termination */
+ if (ndr->flags & LIBNDR_FLAG_STR_NOTERM) {
+ if (strlen(as) < (len3 + c_len_term)) {
+ DEBUG(6,("short string '%s'\n", as));
+ }
+ } else {
+ if (strlen(as) == (len3 + c_len_term)) {
+ DEBUG(6,("long string '%s'\n", as));
+ }
+ }
+ *s = as;
+ break;
+
+ case LIBNDR_FLAG_STR_SIZE2|LIBNDR_FLAG_STR_NOTERM|LIBNDR_FLAG_STR_BYTESIZE:
+ NDR_CHECK(ndr_pull_uint16(ndr, NDR_SCALARS, &len3));
+ NDR_PULL_NEED_BYTES(ndr, len3);
+ if (len3 == 0) {
+ as = talloc_strdup(ndr->current_mem_ctx, "");
+ } else {
+ if (!convert_string_talloc_convenience(ndr->current_mem_ctx,
+ ndr->iconv_convenience,
+ chset, CH_UNIX,
+ ndr->data+ndr->offset,
+ len3,
+ (void **)&as, &ret, false)) {
+ return ndr_pull_error(ndr, NDR_ERR_CHARCNV,
+ "Bad character conversion");
+ }
+ }
+ NDR_CHECK(ndr_pull_advance(ndr, len3));
+ *s = as;
+ break;
+
+ case LIBNDR_FLAG_STR_NULLTERM:
+ if (byte_mul == 1) {
+ len1 = ascii_len_n((const char *)(ndr->data+ndr->offset), ndr->data_size - ndr->offset);
+ } else {
+ len1 = utf16_len_n(ndr->data+ndr->offset, ndr->data_size - ndr->offset);
+ }
+ if (!convert_string_talloc_convenience(ndr->current_mem_ctx,
+ ndr->iconv_convenience, chset, CH_UNIX,
+ ndr->data+ndr->offset,
+ len1,
+ (void **)&as, &ret, false)) {
+ return ndr_pull_error(ndr, NDR_ERR_CHARCNV,
+ "Bad character conversion");
+ }
+ NDR_CHECK(ndr_pull_advance(ndr, len1));
+ *s = as;
+ break;
+
+ case LIBNDR_FLAG_STR_NOTERM:
+ if (!(ndr->flags & LIBNDR_FLAG_REMAINING)) {
+ return ndr_pull_error(ndr, NDR_ERR_STRING, "Bad string flags 0x%x (missing NDR_REMAINING)\n",
+ ndr->flags & LIBNDR_STRING_FLAGS);
+ }
+
+ len1 = ndr->data_size - ndr->offset;
+
+ NDR_PULL_NEED_BYTES(ndr, len1);
+ if (len1 == 0) {
+ as = talloc_strdup(ndr->current_mem_ctx, "");
+ } else {
+ if (!convert_string_talloc_convenience(ndr->current_mem_ctx,
+ ndr->iconv_convenience,
+ chset, CH_UNIX,
+ ndr->data+ndr->offset,
+ len1,
+ (void **)&as, &ret, false)) {
+ return ndr_pull_error(ndr, NDR_ERR_CHARCNV,
+ "Bad character conversion");
+ }
+ }
+ NDR_CHECK(ndr_pull_advance(ndr, len1));
+
+ *s = as;
+ break;
+
+ default:
+ return ndr_pull_error(ndr, NDR_ERR_STRING, "Bad string flags 0x%x\n",
+ ndr->flags & LIBNDR_STRING_FLAGS);
+ }
+
+ return NDR_ERR_SUCCESS;
+}
+
+
+/**
+ push a general string onto the wire
+*/
+_PUBLIC_ enum ndr_err_code ndr_push_string(struct ndr_push *ndr, int ndr_flags, const char *s)
+{
+ size_t s_len, c_len, d_len;
+ int chset = CH_UTF16;
+ unsigned flags = ndr->flags;
+ unsigned byte_mul = 2;
+ uint8_t *dest = NULL;
+
+ if (!(ndr_flags & NDR_SCALARS)) {
+ return NDR_ERR_SUCCESS;
+ }
+
+ if (NDR_BE(ndr)) {
+ chset = CH_UTF16BE;
+ }
+
+ s_len = s?strlen(s):0;
+
+ if (flags & LIBNDR_FLAG_STR_ASCII) {
+ chset = CH_DOS;
+ byte_mul = 1;
+ flags &= ~LIBNDR_FLAG_STR_ASCII;
+ }
+
+ if (flags & LIBNDR_FLAG_STR_UTF8) {
+ chset = CH_UTF8;
+ byte_mul = 1;
+ flags &= ~LIBNDR_FLAG_STR_UTF8;
+ }
+
+ flags &= ~LIBNDR_FLAG_STR_CONFORMANT;
+
+ if (!(flags & LIBNDR_FLAG_STR_NOTERM)) {
+ s_len++;
+ }
+ if (!convert_string_talloc_convenience(ndr, ndr->iconv_convenience, CH_UNIX, chset, s, s_len, (void **)&dest, &d_len, false)) {
+ return ndr_push_error(ndr, NDR_ERR_CHARCNV,
+ "Bad character conversion");
+ }
+
+ if (flags & LIBNDR_FLAG_STR_BYTESIZE) {
+ c_len = d_len;
+ flags &= ~LIBNDR_FLAG_STR_BYTESIZE;
+ } else if (flags & LIBNDR_FLAG_STR_CHARLEN) {
+ c_len = (d_len / byte_mul)-1;
+ flags &= ~LIBNDR_FLAG_STR_CHARLEN;
+ } else {
+ c_len = d_len / byte_mul;
+ }
+
+ switch ((flags & LIBNDR_STRING_FLAGS) & ~LIBNDR_FLAG_STR_NOTERM) {
+ case LIBNDR_FLAG_STR_LEN4|LIBNDR_FLAG_STR_SIZE4:
+ NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, c_len));
+ NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, 0));
+ NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, c_len));
+ NDR_CHECK(ndr_push_bytes(ndr, dest, d_len));
+ break;
+
+ case LIBNDR_FLAG_STR_LEN4:
+ NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, 0));
+ NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, c_len));
+ NDR_CHECK(ndr_push_bytes(ndr, dest, d_len));
+ break;
+
+ case LIBNDR_FLAG_STR_SIZE4:
+ NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, c_len));
+ NDR_CHECK(ndr_push_bytes(ndr, dest, d_len));
+ break;
+
+ case LIBNDR_FLAG_STR_SIZE2:
+ NDR_CHECK(ndr_push_uint16(ndr, NDR_SCALARS, c_len));
+ NDR_CHECK(ndr_push_bytes(ndr, dest, d_len));
+ break;
+
+ case LIBNDR_FLAG_STR_NULLTERM:
+ NDR_CHECK(ndr_push_bytes(ndr, dest, d_len));
+ break;
+
+ default:
+ if (ndr->flags & LIBNDR_FLAG_REMAINING) {
+ NDR_CHECK(ndr_push_bytes(ndr, dest, d_len));
+ break;
+ }
+
+ return ndr_push_error(ndr, NDR_ERR_STRING, "Bad string flags 0x%x\n",
+ ndr->flags & LIBNDR_STRING_FLAGS);
+ }
+
+ talloc_free(dest);
+
+ return NDR_ERR_SUCCESS;
+}
+
+/**
+ push a general string onto the wire
+*/
+_PUBLIC_ size_t ndr_string_array_size(struct ndr_push *ndr, const char *s)
+{
+ size_t c_len;
+ unsigned flags = ndr->flags;
+ unsigned byte_mul = 2;
+ unsigned c_len_term = 1;
+
+ c_len = s?strlen_m(s):0;
+
+ if (flags & (LIBNDR_FLAG_STR_ASCII|LIBNDR_FLAG_STR_UTF8)) {
+ byte_mul = 1;
+ }
+
+ if (flags & LIBNDR_FLAG_STR_NOTERM) {
+ c_len_term = 0;
+ }
+
+ c_len = c_len + c_len_term;
+
+ if (flags & LIBNDR_FLAG_STR_BYTESIZE) {
+ c_len = c_len * byte_mul;
+ }
+
+ return c_len;
+}
+
+_PUBLIC_ void ndr_print_string(struct ndr_print *ndr, const char *name, const char *s)
+{
+ if (s) {
+ ndr->print(ndr, "%-25s: '%s'", name, s);
+ } else {
+ ndr->print(ndr, "%-25s: NULL", name);
+ }
+}
+
+_PUBLIC_ uint32_t ndr_size_string(int ret, const char * const* string, int flags)
+{
+ /* FIXME: Is this correct for all strings ? */
+ if(!(*string)) return ret;
+ return ret+strlen(*string)+1;
+}
+
+/**
+ pull a general string array from the wire
+*/
+_PUBLIC_ enum ndr_err_code ndr_pull_string_array(struct ndr_pull *ndr, int ndr_flags, const char ***_a)
+{
+ const char **a = NULL;
+ uint32_t count;
+ unsigned flags = ndr->flags;
+ unsigned saved_flags = ndr->flags;
+
+ if (!(ndr_flags & NDR_SCALARS)) {
+ return NDR_ERR_SUCCESS;
+ }
+
+ switch (flags & LIBNDR_STRING_FLAGS) {
+ case LIBNDR_FLAG_STR_NULLTERM:
+ /*
+ * here the strings are null terminated
+ * but also the array is null terminated
+ */
+ for (count = 0;; count++) {
+ TALLOC_CTX *tmp_ctx;
+ const char *s = NULL;
+ a = talloc_realloc(ndr->current_mem_ctx, a, const char *, count + 2);
+ NDR_ERR_HAVE_NO_MEMORY(a);
+ a[count] = NULL;
+ a[count+1] = NULL;
+
+ tmp_ctx = ndr->current_mem_ctx;
+ ndr->current_mem_ctx = a;
+ NDR_CHECK(ndr_pull_string(ndr, ndr_flags, &s));
+ ndr->current_mem_ctx = tmp_ctx;
+ if (strcmp("", s)==0) {
+ a[count] = NULL;
+ break;
+ } else {
+ a[count] = s;
+ }
+ }
+
+ *_a =a;
+ break;
+
+ case LIBNDR_FLAG_STR_NOTERM:
+ if (!(ndr->flags & LIBNDR_FLAG_REMAINING)) {
+ return ndr_pull_error(ndr, NDR_ERR_STRING, "Bad string flags 0x%x (missing NDR_REMAINING)\n",
+ ndr->flags & LIBNDR_STRING_FLAGS);
+ }
+ /*
+ * here the strings are not null terminated
+ * but serarated by a null terminator
+ *
+ * which means the same as:
+ * very string is null terminated exept the last
+ * string is terminated by the end of the buffer
+ *
+ * as LIBNDR_FLAG_STR_NULLTERM also end at the end
+ * of the buffer, we can pull each string with this flag
+ */
+ ndr->flags &= ~(LIBNDR_FLAG_STR_NOTERM|LIBNDR_FLAG_REMAINING);
+ ndr->flags |= LIBNDR_FLAG_STR_NULLTERM;
+
+ for (count = 0; ((ndr->data_size - ndr->offset) > 0); count++) {
+ TALLOC_CTX *tmp_ctx;
+ const char *s = NULL;
+ a = talloc_realloc(ndr->current_mem_ctx, a, const char *, count + 2);
+ NDR_ERR_HAVE_NO_MEMORY(a);
+ a[count] = NULL;
+ a[count+1] = NULL;
+
+ tmp_ctx = ndr->current_mem_ctx;
+ ndr->current_mem_ctx = a;
+ NDR_CHECK(ndr_pull_string(ndr, ndr_flags, &s));
+ ndr->current_mem_ctx = tmp_ctx;
+ a[count] = s;
+ }
+
+ *_a =a;
+ break;
+
+ default:
+ return ndr_pull_error(ndr, NDR_ERR_STRING, "Bad string flags 0x%x\n",
+ ndr->flags & LIBNDR_STRING_FLAGS);
+ }
+
+ ndr->flags = saved_flags;
+ return NDR_ERR_SUCCESS;
+}
+
+/**
+ push a general string array onto the wire
+*/
+_PUBLIC_ enum ndr_err_code ndr_push_string_array(struct ndr_push *ndr, int ndr_flags, const char **a)
+{
+ uint32_t count;
+ unsigned flags = ndr->flags;
+ unsigned saved_flags = ndr->flags;
+
+ if (!(ndr_flags & NDR_SCALARS)) {
+ return NDR_ERR_SUCCESS;
+ }
+
+ switch (flags & LIBNDR_STRING_FLAGS) {
+ case LIBNDR_FLAG_STR_NULLTERM:
+ for (count = 0; a && a[count]; count++) {
+ NDR_CHECK(ndr_push_string(ndr, ndr_flags, a[count]));
+ }
+
+ NDR_CHECK(ndr_push_string(ndr, ndr_flags, ""));
+ break;
+
+ case LIBNDR_FLAG_STR_NOTERM:
+ if (!(ndr->flags & LIBNDR_FLAG_REMAINING)) {
+ return ndr_push_error(ndr, NDR_ERR_STRING, "Bad string flags 0x%x (missing NDR_REMAINING)\n",
+ ndr->flags & LIBNDR_STRING_FLAGS);
+ }
+
+ for (count = 0; a && a[count]; count++) {
+ if (count > 0) {
+ ndr->flags &= ~(LIBNDR_FLAG_STR_NOTERM|LIBNDR_FLAG_REMAINING);
+ ndr->flags |= LIBNDR_FLAG_STR_NULLTERM;
+ NDR_CHECK(ndr_push_string(ndr, ndr_flags, ""));
+ ndr->flags = saved_flags;
+ }
+ NDR_CHECK(ndr_push_string(ndr, ndr_flags, a[count]));
+ }
+
+ break;
+
+ default:
+ return ndr_push_error(ndr, NDR_ERR_STRING, "Bad string flags 0x%x\n",
+ ndr->flags & LIBNDR_STRING_FLAGS);
+ }
+
+ ndr->flags = saved_flags;
+ return NDR_ERR_SUCCESS;
+}
+
+_PUBLIC_ void ndr_print_string_array(struct ndr_print *ndr, const char *name, const char **a)
+{
+ uint32_t count;
+ uint32_t i;
+
+ for (count = 0; a && a[count]; count++) {}
+
+ ndr->print(ndr, "%s: ARRAY(%d)", name, count);
+ ndr->depth++;
+ for (i=0;i<count;i++) {
+ char *idx=NULL;
+ if (asprintf(&idx, "[%d]", i) != -1) {
+ ndr_print_string(ndr, idx, a[i]);
+ free(idx);
+ }
+ }
+ ndr->depth--;
+}
+
+_PUBLIC_ size_t ndr_size_string_array(const char **a, uint32_t count, int flags)
+{
+ uint32_t i;
+ size_t size = 0;
+
+ switch (flags & LIBNDR_STRING_FLAGS) {
+ case LIBNDR_FLAG_STR_NULLTERM:
+ for (i = 0; i < count; i++) {
+ size += strlen_m_term(a[i]);
+ }
+ break;
+ case LIBNDR_FLAG_STR_NOTERM:
+ for (i = 0; i < count; i++) {
+ size += strlen_m(a[i]);
+ }
+ break;
+ default:
+ return 0;
+ }
+
+ return size;
+}
+
+/**
+ * Return number of elements in a string including the last (zeroed) element
+ */
+_PUBLIC_ uint32_t ndr_string_length(const void *_var, uint32_t element_size)
+{
+ uint32_t i;
+ uint8_t zero[4] = {0,0,0,0};
+ const char *var = (const char *)_var;
+
+ for (i = 0; memcmp(var+i*element_size,zero,element_size) != 0; i++);
+
+ return i+1;
+}
+
+_PUBLIC_ enum ndr_err_code ndr_check_string_terminator(struct ndr_pull *ndr, uint32_t count, uint32_t element_size)
+{
+ uint32_t i;
+ uint32_t save_offset;
+
+ save_offset = ndr->offset;
+ ndr_pull_advance(ndr, (count - 1) * element_size);
+ NDR_PULL_NEED_BYTES(ndr, element_size);
+
+ for (i = 0; i < element_size; i++) {
+ if (ndr->data[ndr->offset+i] != 0) {
+ ndr->offset = save_offset;
+
+ return ndr_pull_error(ndr, NDR_ERR_ARRAY_SIZE, "String terminator not present or outside string boundaries");
+ }
+ }
+
+ ndr->offset = save_offset;
+
+ return NDR_ERR_SUCCESS;
+}
+
+_PUBLIC_ enum ndr_err_code ndr_pull_charset(struct ndr_pull *ndr, int ndr_flags, const char **var, uint32_t length, uint8_t byte_mul, charset_t chset)
+{
+ size_t ret;
+ if (length == 0) {
+ *var = talloc_strdup(ndr->current_mem_ctx, "");
+ return NDR_ERR_SUCCESS;
+ }
+
+ if (NDR_BE(ndr) && chset == CH_UTF16) {
+ chset = CH_UTF16BE;
+ }
+
+ NDR_PULL_NEED_BYTES(ndr, length*byte_mul);
+
+ if (!convert_string_talloc_convenience(ndr->current_mem_ctx,
+ ndr->iconv_convenience,
+ chset, CH_UNIX,
+ ndr->data+ndr->offset,
+ length*byte_mul,
+ discard_const_p(void *, var), &ret, false)) {
+ return ndr_pull_error(ndr, NDR_ERR_CHARCNV,
+ "Bad character conversion");
+ }
+ NDR_CHECK(ndr_pull_advance(ndr, length*byte_mul));
+
+ return NDR_ERR_SUCCESS;
+}
+
+_PUBLIC_ enum ndr_err_code ndr_push_charset(struct ndr_push *ndr, int ndr_flags, const char *var, uint32_t length, uint8_t byte_mul, charset_t chset)
+{
+ size_t ret, required;
+
+ if (NDR_BE(ndr) && chset == CH_UTF16) {
+ chset = CH_UTF16BE;
+ }
+
+ required = byte_mul * length;
+
+ NDR_PUSH_NEED_BYTES(ndr, required);
+ if (!convert_string_convenience(ndr->iconv_convenience, CH_UNIX, chset,
+ var, strlen(var),
+ ndr->data+ndr->offset, required, &ret, false)) {
+ return ndr_push_error(ndr, NDR_ERR_CHARCNV,
+ "Bad character conversion");
+ }
+
+ /* Make sure the remaining part of the string is filled with zeroes */
+ if (ret < required) {
+ memset(ndr->data+ndr->offset+ret, 0, required-ret);
+ }
+
+ ndr->offset += required;
+
+ return NDR_ERR_SUCCESS;
+}
+
+/* Return number of elements in a string in the specified charset */
+_PUBLIC_ uint32_t ndr_charset_length(const void *var, charset_t chset)
+{
+ /* FIXME: Treat special chars special here, taking chset into account */
+ /* Also include 0 byte */
+ return strlen((const char *)var)+1;
+}
diff --git a/source4/librpc/ndr/py_security.c b/source4/librpc/ndr/py_security.c
new file mode 100644
index 0000000000..f89263bba3
--- /dev/null
+++ b/source4/librpc/ndr/py_security.c
@@ -0,0 +1,394 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Jelmer Vernooij <jelmer@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+#include <Python.h>
+#include "libcli/security/security.h"
+
+#ifndef Py_RETURN_NONE
+#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
+#endif
+
+static void PyType_AddMethods(PyTypeObject *type, PyMethodDef *methods)
+{
+ PyObject *dict;
+ int i;
+ if (type->tp_dict == NULL)
+ type->tp_dict = PyDict_New();
+ dict = type->tp_dict;
+ for (i = 0; methods[i].ml_name; i++) {
+ PyObject *descr;
+ if (methods[i].ml_flags & METH_CLASS)
+ descr = PyCFunction_New(&methods[i], (PyObject *)type);
+ else
+ descr = PyDescr_NewMethod(type, &methods[i]);
+ PyDict_SetItemString(dict, methods[i].ml_name,
+ descr);
+ }
+}
+
+static int py_dom_sid_cmp(PyObject *py_self, PyObject *py_other)
+{
+ struct dom_sid *self = py_talloc_get_ptr(py_self), *other;
+ other = py_talloc_get_ptr(py_other);
+ if (other == NULL)
+ return -1;
+
+ return dom_sid_compare(self, other);
+}
+
+static PyObject *py_dom_sid_str(PyObject *py_self)
+{
+ struct dom_sid *self = py_talloc_get_ptr(py_self);
+ char *str = dom_sid_string(NULL, self);
+ PyObject *ret = PyString_FromString(str);
+ talloc_free(str);
+ return ret;
+}
+
+static PyObject *py_dom_sid_repr(PyObject *py_self)
+{
+ struct dom_sid *self = py_talloc_get_ptr(py_self);
+ char *str = dom_sid_string(NULL, self);
+ PyObject *ret = PyString_FromFormat("dom_sid('%s')", str);
+ talloc_free(str);
+ return ret;
+}
+
+static int py_dom_sid_init(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ char *str = NULL;
+ struct dom_sid *sid = py_talloc_get_ptr(self);
+ const char *kwnames[] = { "str", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|s", discard_const_p(char *, kwnames), &str))
+ return -1;
+
+ if (str != NULL && !dom_sid_parse(str, sid)) {
+ PyErr_SetString(PyExc_TypeError, "Unable to parse string");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void py_dom_sid_patch(PyTypeObject *type)
+{
+ type->tp_init = py_dom_sid_init;
+ type->tp_str = py_dom_sid_str;
+ type->tp_repr = py_dom_sid_repr;
+ type->tp_compare = py_dom_sid_cmp;
+}
+
+#define PY_DOM_SID_PATCH py_dom_sid_patch
+
+static PyObject *py_descriptor_sacl_add(PyObject *self, PyObject *args)
+{
+ struct security_descriptor *desc = py_talloc_get_ptr(self);
+ NTSTATUS status;
+ struct security_ace *ace;
+ PyObject *py_ace;
+
+ if (!PyArg_ParseTuple(args, "O", &py_ace))
+ return NULL;
+
+ ace = py_talloc_get_ptr(py_ace);
+ status = security_descriptor_sacl_add(desc, ace);
+ PyErr_NTSTATUS_IS_ERR_RAISE(status);
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_descriptor_dacl_add(PyObject *self, PyObject *args)
+{
+ struct security_descriptor *desc = py_talloc_get_ptr(self);
+ NTSTATUS status;
+ struct security_ace *ace;
+ PyObject *py_ace;
+
+ if (!PyArg_ParseTuple(args, "O", &py_ace))
+ return NULL;
+
+ ace = py_talloc_get_ptr(py_ace);
+
+ status = security_descriptor_dacl_add(desc, ace);
+ PyErr_NTSTATUS_IS_ERR_RAISE(status);
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_descriptor_dacl_del(PyObject *self, PyObject *args)
+{
+ struct security_descriptor *desc = py_talloc_get_ptr(self);
+ NTSTATUS status;
+ struct dom_sid *sid;
+ PyObject *py_sid;
+
+ if (!PyArg_ParseTuple(args, "O", &py_sid))
+ return NULL;
+
+ sid = py_talloc_get_ptr(py_sid);
+ status = security_descriptor_dacl_del(desc, sid);
+ PyErr_NTSTATUS_IS_ERR_RAISE(status);
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_descriptor_sacl_del(PyObject *self, PyObject *args)
+{
+ struct security_descriptor *desc = py_talloc_get_ptr(self);
+ NTSTATUS status;
+ struct dom_sid *sid;
+ PyObject *py_sid;
+
+ if (!PyArg_ParseTuple(args, "O", &py_sid))
+ return NULL;
+
+ sid = py_talloc_get_ptr(py_sid);
+ status = security_descriptor_sacl_del(desc, sid);
+ PyErr_NTSTATUS_IS_ERR_RAISE(status);
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_descriptor_new(PyTypeObject *self, PyObject *args, PyObject *kwargs)
+{
+ return py_talloc_import(self, security_descriptor_initialise(NULL));
+}
+
+static PyObject *py_descriptor_from_sddl(PyObject *self, PyObject *args)
+{
+ struct security_descriptor *secdesc;
+ char *sddl;
+ PyObject *py_sid;
+ struct dom_sid *sid;
+
+ if (!PyArg_ParseTuple(args, "sO", &sddl, &py_sid))
+ return NULL;
+
+ sid = py_talloc_get_ptr(py_sid);
+
+ secdesc = sddl_decode(NULL, sddl, sid);
+ if (secdesc == NULL) {
+ PyErr_SetString(PyExc_TypeError, "Unable to parse SDDL");
+ return NULL;
+ }
+
+ return py_talloc_import((PyTypeObject *)self, secdesc);
+}
+
+static PyObject *py_descriptor_as_sddl(PyObject *self, PyObject *py_sid)
+{
+ struct dom_sid *sid = py_talloc_get_ptr(py_sid);
+ struct security_descriptor *desc = py_talloc_get_ptr(self);
+ char *text;
+ PyObject *ret;
+
+ text = sddl_encode(NULL, desc, sid);
+
+ ret = PyString_FromString(text);
+
+ talloc_free(text);
+
+ return ret;
+}
+
+static PyMethodDef py_descriptor_extra_methods[] = {
+ { "sacl_add", (PyCFunction)py_descriptor_sacl_add, METH_VARARGS,
+ "S.sacl_add(ace) -> None\n"
+ "Add a security ace to this security descriptor" },
+ { "dacl_add", (PyCFunction)py_descriptor_dacl_add, METH_VARARGS,
+ NULL },
+ { "dacl_del", (PyCFunction)py_descriptor_dacl_del, METH_VARARGS,
+ NULL },
+ { "sacl_del", (PyCFunction)py_descriptor_sacl_del, METH_VARARGS,
+ NULL },
+ { "from_sddl", (PyCFunction)py_descriptor_from_sddl, METH_VARARGS|METH_CLASS,
+ NULL },
+ { "as_sddl", (PyCFunction)py_descriptor_as_sddl, METH_O,
+ NULL },
+ { NULL }
+};
+
+static void py_descriptor_patch(PyTypeObject *type)
+{
+ type->tp_new = py_descriptor_new;
+ PyType_AddMethods(type, py_descriptor_extra_methods);
+}
+
+#define PY_DESCRIPTOR_PATCH py_descriptor_patch
+
+static PyObject *py_token_is_sid(PyObject *self, PyObject *args)
+{
+ PyObject *py_sid;
+ struct dom_sid *sid;
+ struct security_token *token = py_talloc_get_ptr(self);
+ if (!PyArg_ParseTuple(args, "O", &py_sid))
+ return NULL;
+
+ sid = py_talloc_get_ptr(py_sid);
+
+ return PyBool_FromLong(security_token_is_sid(token, sid));
+}
+
+static PyObject *py_token_has_sid(PyObject *self, PyObject *args)
+{
+ PyObject *py_sid;
+ struct dom_sid *sid;
+ struct security_token *token = py_talloc_get_ptr(self);
+ if (!PyArg_ParseTuple(args, "O", &py_sid))
+ return NULL;
+
+ sid = py_talloc_get_ptr(py_sid);
+
+ return PyBool_FromLong(security_token_has_sid(token, sid));
+}
+
+static PyObject *py_token_is_anonymous(PyObject *self)
+{
+ struct security_token *token = py_talloc_get_ptr(self);
+
+ return PyBool_FromLong(security_token_is_anonymous(token));
+}
+
+static PyObject *py_token_is_system(PyObject *self)
+{
+ struct security_token *token = py_talloc_get_ptr(self);
+
+ return PyBool_FromLong(security_token_is_system(token));
+}
+
+static PyObject *py_token_has_builtin_administrators(PyObject *self)
+{
+ struct security_token *token = py_talloc_get_ptr(self);
+
+ return PyBool_FromLong(security_token_has_builtin_administrators(token));
+}
+
+static PyObject *py_token_has_nt_authenticated_users(PyObject *self)
+{
+ struct security_token *token = py_talloc_get_ptr(self);
+
+ return PyBool_FromLong(security_token_has_nt_authenticated_users(token));
+}
+
+static PyObject *py_token_has_privilege(PyObject *self, PyObject *args)
+{
+ int priv;
+ struct security_token *token = py_talloc_get_ptr(self);
+
+ if (!PyArg_ParseTuple(args, "i", &priv))
+ return NULL;
+
+ return PyBool_FromLong(security_token_has_privilege(token, priv));
+}
+
+static PyObject *py_token_set_privilege(PyObject *self, PyObject *args)
+{
+ int priv;
+ struct security_token *token = py_talloc_get_ptr(self);
+
+ if (!PyArg_ParseTuple(args, "i", &priv))
+ return NULL;
+
+ security_token_set_privilege(token, priv);
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_token_new(PyTypeObject *self, PyObject *args, PyObject *kwargs)
+{
+ return py_talloc_import(self, security_token_initialise(NULL));
+}
+
+static PyMethodDef py_token_extra_methods[] = {
+ { "is_sid", (PyCFunction)py_token_is_sid, METH_VARARGS,
+ "S.is_sid(sid) -> bool\n"
+ "Check whether this token is of the specified SID." },
+ { "has_sid", (PyCFunction)py_token_has_sid, METH_VARARGS,
+ NULL },
+ { "is_anonymous", (PyCFunction)py_token_is_anonymous, METH_NOARGS,
+ "S.is_anonymus() -> bool\n"
+ "Check whether this is an anonymous token." },
+ { "is_system", (PyCFunction)py_token_is_system, METH_NOARGS,
+ NULL },
+ { "has_builtin_administrators", (PyCFunction)py_token_has_builtin_administrators, METH_NOARGS,
+ NULL },
+ { "has_nt_authenticated_users", (PyCFunction)py_token_has_nt_authenticated_users, METH_NOARGS,
+ NULL },
+ { "has_privilege", (PyCFunction)py_token_has_privilege, METH_VARARGS,
+ NULL },
+ { "set_privilege", (PyCFunction)py_token_set_privilege, METH_VARARGS,
+ NULL },
+ { NULL }
+};
+
+#define PY_TOKEN_PATCH py_token_patch
+static void py_token_patch(PyTypeObject *type)
+{
+ type->tp_new = py_token_new;
+ PyType_AddMethods(type, py_token_extra_methods);
+}
+
+static PyObject *py_privilege_name(PyObject *self, PyObject *args)
+{
+ int priv;
+ if (!PyArg_ParseTuple(args, "i", &priv))
+ return NULL;
+
+ return PyString_FromString(sec_privilege_name(priv));
+}
+
+static PyObject *py_privilege_id(PyObject *self, PyObject *args)
+{
+ char *name;
+
+ if (!PyArg_ParseTuple(args, "s", &name))
+ return NULL;
+
+ return PyInt_FromLong(sec_privilege_id(name));
+}
+
+static PyObject *py_random_sid(PyObject *self)
+{
+ struct dom_sid *sid;
+ PyObject *ret;
+ char *str = talloc_asprintf(NULL, "S-1-5-21-%u-%u-%u",
+ (unsigned)generate_random(),
+ (unsigned)generate_random(),
+ (unsigned)generate_random());
+
+ sid = dom_sid_parse_talloc(NULL, str);
+ talloc_free(str);
+ ret = py_talloc_import(&dom_sid_Type, sid);
+ talloc_free(sid);
+ return ret;
+}
+
+static PyMethodDef py_mod_security_extra_methods[] = {
+ { "random_sid", (PyCFunction)py_random_sid, METH_NOARGS, NULL },
+ { "privilege_id", (PyCFunction)py_privilege_id, METH_VARARGS, NULL },
+ { "privilege_name", (PyCFunction)py_privilege_name, METH_VARARGS, NULL },
+ { NULL }
+};
+
+static void py_mod_security_patch(PyObject *m)
+{
+ int i;
+ for (i = 0; py_mod_security_extra_methods[i].ml_name; i++) {
+ PyObject *descr = PyCFunction_New(&py_mod_security_extra_methods[i], NULL);
+ PyModule_AddObject(m, py_mod_security_extra_methods[i].ml_name,
+ descr);
+ }
+}
+
+#define PY_MOD_SECURITY_PATCH py_mod_security_patch
diff --git a/source4/librpc/rpc/dcerpc.c b/source4/librpc/rpc/dcerpc.c
new file mode 100644
index 0000000000..7a568d3c9e
--- /dev/null
+++ b/source4/librpc/rpc/dcerpc.c
@@ -0,0 +1,1697 @@
+/*
+ Unix SMB/CIFS implementation.
+ raw dcerpc operations
+
+ Copyright (C) Tim Potter 2003
+ Copyright (C) Andrew Tridgell 2003-2005
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "../lib/util/dlinklist.h"
+#include "lib/events/events.h"
+#include "librpc/rpc/dcerpc.h"
+#include "librpc/rpc/dcerpc_proto.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "librpc/gen_ndr/ndr_dcerpc.h"
+#include "libcli/composite/composite.h"
+#include "auth/gensec/gensec.h"
+#include "param/param.h"
+
+_PUBLIC_ NTSTATUS dcerpc_init(struct loadparm_context *lp_ctx)
+{
+ return gensec_init(lp_ctx);
+}
+
+static void dcerpc_connection_dead(struct dcerpc_connection *conn, NTSTATUS status);
+static void dcerpc_ship_next_request(struct dcerpc_connection *c);
+
+/* destroy a dcerpc connection */
+static int dcerpc_connection_destructor(struct dcerpc_connection *conn)
+{
+ if (conn->dead) {
+ conn->free_skipped = true;
+ return -1;
+ }
+ dcerpc_connection_dead(conn, NT_STATUS_LOCAL_DISCONNECT);
+ return 0;
+}
+
+
+/* initialise a dcerpc connection.
+ the event context is optional
+*/
+static struct dcerpc_connection *dcerpc_connection_init(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct smb_iconv_convenience *ic)
+{
+ struct dcerpc_connection *c;
+
+ c = talloc_zero(mem_ctx, struct dcerpc_connection);
+ if (!c) {
+ return NULL;
+ }
+
+ c->iconv_convenience = talloc_reference(c, ic);
+
+ c->event_ctx = talloc_reference(c, ev);
+
+ if (c->event_ctx == NULL) {
+ talloc_free(c);
+ return NULL;
+ }
+
+ c->call_id = 1;
+ c->security_state.auth_info = NULL;
+ c->security_state.session_key = dcerpc_generic_session_key;
+ c->security_state.generic_state = NULL;
+ c->binding_string = NULL;
+ c->flags = 0;
+ c->srv_max_xmit_frag = 0;
+ c->srv_max_recv_frag = 0;
+ c->pending = NULL;
+
+ talloc_set_destructor(c, dcerpc_connection_destructor);
+
+ return c;
+}
+
+/* initialise a dcerpc pipe. */
+_PUBLIC_ struct dcerpc_pipe *dcerpc_pipe_init(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+ struct smb_iconv_convenience *ic)
+{
+ struct dcerpc_pipe *p;
+
+ p = talloc(mem_ctx, struct dcerpc_pipe);
+ if (!p) {
+ return NULL;
+ }
+
+ p->conn = dcerpc_connection_init(p, ev, ic);
+ if (p->conn == NULL) {
+ talloc_free(p);
+ return NULL;
+ }
+
+ p->last_fault_code = 0;
+ p->context_id = 0;
+ p->request_timeout = DCERPC_REQUEST_TIMEOUT;
+ p->binding = NULL;
+
+ ZERO_STRUCT(p->syntax);
+ ZERO_STRUCT(p->transfer_syntax);
+
+ return p;
+}
+
+
+/*
+ choose the next call id to use
+*/
+static uint32_t next_call_id(struct dcerpc_connection *c)
+{
+ c->call_id++;
+ if (c->call_id == 0) {
+ c->call_id++;
+ }
+ return c->call_id;
+}
+
+/* we need to be able to get/set the fragment length without doing a full
+ decode */
+void dcerpc_set_frag_length(DATA_BLOB *blob, uint16_t v)
+{
+ if (CVAL(blob->data,DCERPC_DREP_OFFSET) & DCERPC_DREP_LE) {
+ SSVAL(blob->data, DCERPC_FRAG_LEN_OFFSET, v);
+ } else {
+ RSSVAL(blob->data, DCERPC_FRAG_LEN_OFFSET, v);
+ }
+}
+
+uint16_t dcerpc_get_frag_length(const DATA_BLOB *blob)
+{
+ if (CVAL(blob->data,DCERPC_DREP_OFFSET) & DCERPC_DREP_LE) {
+ return SVAL(blob->data, DCERPC_FRAG_LEN_OFFSET);
+ } else {
+ return RSVAL(blob->data, DCERPC_FRAG_LEN_OFFSET);
+ }
+}
+
+void dcerpc_set_auth_length(DATA_BLOB *blob, uint16_t v)
+{
+ if (CVAL(blob->data,DCERPC_DREP_OFFSET) & DCERPC_DREP_LE) {
+ SSVAL(blob->data, DCERPC_AUTH_LEN_OFFSET, v);
+ } else {
+ RSSVAL(blob->data, DCERPC_AUTH_LEN_OFFSET, v);
+ }
+}
+
+
+/**
+ setup for a ndr pull, also setting up any flags from the binding string
+*/
+static struct ndr_pull *ndr_pull_init_flags(struct dcerpc_connection *c,
+ DATA_BLOB *blob, TALLOC_CTX *mem_ctx)
+{
+ struct ndr_pull *ndr = ndr_pull_init_blob(blob, mem_ctx, c->iconv_convenience);
+
+ if (ndr == NULL) return ndr;
+
+ if (c->flags & DCERPC_DEBUG_PAD_CHECK) {
+ ndr->flags |= LIBNDR_FLAG_PAD_CHECK;
+ }
+
+ if (c->flags & DCERPC_NDR_REF_ALLOC) {
+ ndr->flags |= LIBNDR_FLAG_REF_ALLOC;
+ }
+
+ return ndr;
+}
+
+/*
+ parse a data blob into a ncacn_packet structure. This handles both
+ input and output packets
+*/
+static NTSTATUS ncacn_pull(struct dcerpc_connection *c, DATA_BLOB *blob, TALLOC_CTX *mem_ctx,
+ struct ncacn_packet *pkt)
+{
+ struct ndr_pull *ndr;
+ enum ndr_err_code ndr_err;
+
+ ndr = ndr_pull_init_flags(c, blob, mem_ctx);
+ if (!ndr) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (! (CVAL(blob->data, DCERPC_DREP_OFFSET) & DCERPC_DREP_LE)) {
+ ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
+ }
+
+ ndr_err = ndr_pull_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ parse the authentication information on a dcerpc response packet
+*/
+static NTSTATUS ncacn_pull_request_auth(struct dcerpc_connection *c, TALLOC_CTX *mem_ctx,
+ DATA_BLOB *raw_packet,
+ struct ncacn_packet *pkt)
+{
+ struct ndr_pull *ndr;
+ NTSTATUS status;
+ struct dcerpc_auth auth;
+ DATA_BLOB auth_blob;
+ enum ndr_err_code ndr_err;
+
+ if (!c->security_state.auth_info ||
+ !c->security_state.generic_state) {
+ return NT_STATUS_OK;
+ }
+
+ switch (c->security_state.auth_info->auth_level) {
+ case DCERPC_AUTH_LEVEL_PRIVACY:
+ case DCERPC_AUTH_LEVEL_INTEGRITY:
+ break;
+
+ case DCERPC_AUTH_LEVEL_CONNECT:
+ if (pkt->auth_length != 0) {
+ break;
+ }
+ return NT_STATUS_OK;
+ case DCERPC_AUTH_LEVEL_NONE:
+ if (pkt->auth_length != 0) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ return NT_STATUS_OK;
+
+ default:
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ auth_blob.length = 8 + pkt->auth_length;
+
+ /* check for a valid length */
+ if (pkt->u.response.stub_and_verifier.length < auth_blob.length) {
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+
+ auth_blob.data =
+ pkt->u.response.stub_and_verifier.data +
+ pkt->u.response.stub_and_verifier.length - auth_blob.length;
+ pkt->u.response.stub_and_verifier.length -= auth_blob.length;
+
+ /* pull the auth structure */
+ ndr = ndr_pull_init_flags(c, &auth_blob, mem_ctx);
+ if (!ndr) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!(pkt->drep[0] & DCERPC_DREP_LE)) {
+ ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
+ }
+
+ ndr_err = ndr_pull_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, &auth);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+ status = NT_STATUS_OK;
+
+ /* check signature or unseal the packet */
+ switch (c->security_state.auth_info->auth_level) {
+ case DCERPC_AUTH_LEVEL_PRIVACY:
+ status = gensec_unseal_packet(c->security_state.generic_state,
+ mem_ctx,
+ raw_packet->data + DCERPC_REQUEST_LENGTH,
+ pkt->u.response.stub_and_verifier.length,
+ raw_packet->data,
+ raw_packet->length - auth.credentials.length,
+ &auth.credentials);
+ memcpy(pkt->u.response.stub_and_verifier.data,
+ raw_packet->data + DCERPC_REQUEST_LENGTH,
+ pkt->u.response.stub_and_verifier.length);
+ break;
+
+ case DCERPC_AUTH_LEVEL_INTEGRITY:
+ status = gensec_check_packet(c->security_state.generic_state,
+ mem_ctx,
+ pkt->u.response.stub_and_verifier.data,
+ pkt->u.response.stub_and_verifier.length,
+ raw_packet->data,
+ raw_packet->length - auth.credentials.length,
+ &auth.credentials);
+ break;
+
+ case DCERPC_AUTH_LEVEL_CONNECT:
+ /* for now we ignore possible signatures here */
+ status = NT_STATUS_OK;
+ break;
+
+ default:
+ status = NT_STATUS_INVALID_LEVEL;
+ break;
+ }
+
+ /* remove the indicated amount of paddiing */
+ if (pkt->u.response.stub_and_verifier.length < auth.auth_pad_length) {
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+ pkt->u.response.stub_and_verifier.length -= auth.auth_pad_length;
+
+ return status;
+}
+
+
+/*
+ push a dcerpc request packet into a blob, possibly signing it.
+*/
+static NTSTATUS ncacn_push_request_sign(struct dcerpc_connection *c,
+ DATA_BLOB *blob, TALLOC_CTX *mem_ctx,
+ size_t sig_size,
+ struct ncacn_packet *pkt)
+{
+ NTSTATUS status;
+ struct ndr_push *ndr;
+ DATA_BLOB creds2;
+ size_t payload_length;
+ enum ndr_err_code ndr_err;
+ size_t hdr_size = DCERPC_REQUEST_LENGTH;
+
+ /* non-signed packets are simpler */
+ if (sig_size == 0) {
+ return ncacn_push_auth(blob, mem_ctx, c->iconv_convenience, pkt, NULL);
+ }
+
+ switch (c->security_state.auth_info->auth_level) {
+ case DCERPC_AUTH_LEVEL_PRIVACY:
+ case DCERPC_AUTH_LEVEL_INTEGRITY:
+ break;
+
+ case DCERPC_AUTH_LEVEL_CONNECT:
+ /* TODO: let the gensec mech decide if it wants to generate a signature */
+ return ncacn_push_auth(blob, mem_ctx, c->iconv_convenience, pkt, NULL);
+
+ case DCERPC_AUTH_LEVEL_NONE:
+ return ncacn_push_auth(blob, mem_ctx, c->iconv_convenience, pkt, NULL);
+
+ default:
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ ndr = ndr_push_init_ctx(mem_ctx, c->iconv_convenience);
+ if (!ndr) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (c->flags & DCERPC_PUSH_BIGENDIAN) {
+ ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
+ }
+
+ if (pkt->pfc_flags & DCERPC_PFC_FLAG_OBJECT_UUID) {
+ ndr->flags |= LIBNDR_FLAG_OBJECT_PRESENT;
+ hdr_size += 16;
+ }
+
+ ndr_err = ndr_push_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+ status = NT_STATUS_OK;
+
+ /* pad to 16 byte multiple in the payload portion of the
+ packet. This matches what w2k3 does */
+ c->security_state.auth_info->auth_pad_length =
+ (16 - (pkt->u.request.stub_and_verifier.length & 15)) & 15;
+ ndr_err = ndr_push_zero(ndr, c->security_state.auth_info->auth_pad_length);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+ status = NT_STATUS_OK;
+
+ payload_length = pkt->u.request.stub_and_verifier.length +
+ c->security_state.auth_info->auth_pad_length;
+
+ /* we start without signature, it will appended later */
+ c->security_state.auth_info->credentials = data_blob(NULL,0);
+
+ /* add the auth verifier */
+ ndr_err = ndr_push_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, c->security_state.auth_info);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+ status = NT_STATUS_OK;
+
+ /* extract the whole packet as a blob */
+ *blob = ndr_push_blob(ndr);
+
+ /*
+ * Setup the frag and auth length in the packet buffer.
+ * This is needed if the GENSEC mech does AEAD signing
+ * of the packet headers. The signature itself will be
+ * appended later.
+ */
+ dcerpc_set_frag_length(blob, blob->length + sig_size);
+ dcerpc_set_auth_length(blob, sig_size);
+
+ /* sign or seal the packet */
+ switch (c->security_state.auth_info->auth_level) {
+ case DCERPC_AUTH_LEVEL_PRIVACY:
+ status = gensec_seal_packet(c->security_state.generic_state,
+ mem_ctx,
+ blob->data + hdr_size,
+ payload_length,
+ blob->data,
+ blob->length,
+ &creds2);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ break;
+
+ case DCERPC_AUTH_LEVEL_INTEGRITY:
+ status = gensec_sign_packet(c->security_state.generic_state,
+ mem_ctx,
+ blob->data + hdr_size,
+ payload_length,
+ blob->data,
+ blob->length,
+ &creds2);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ break;
+
+ default:
+ status = NT_STATUS_INVALID_LEVEL;
+ break;
+ }
+
+ if (creds2.length != sig_size) {
+ DEBUG(0,("dcesrv_auth_response: creds2.length[%u] != sig_size[%u] pad[%u] stub[%u]\n",
+ creds2.length, (uint32_t)sig_size,
+ c->security_state.auth_info->auth_pad_length,
+ pkt->u.request.stub_and_verifier.length));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ if (!data_blob_append(mem_ctx, blob, creds2.data, creds2.length)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ fill in the fixed values in a dcerpc header
+*/
+static void init_ncacn_hdr(struct dcerpc_connection *c, struct ncacn_packet *pkt)
+{
+ pkt->rpc_vers = 5;
+ pkt->rpc_vers_minor = 0;
+ if (c->flags & DCERPC_PUSH_BIGENDIAN) {
+ pkt->drep[0] = 0;
+ } else {
+ pkt->drep[0] = DCERPC_DREP_LE;
+ }
+ pkt->drep[1] = 0;
+ pkt->drep[2] = 0;
+ pkt->drep[3] = 0;
+}
+
+/*
+ map a bind nak reason to a NTSTATUS
+*/
+static NTSTATUS dcerpc_map_reason(uint16_t reason)
+{
+ switch (reason) {
+ case DCERPC_BIND_REASON_ASYNTAX:
+ return NT_STATUS_RPC_UNSUPPORTED_NAME_SYNTAX;
+ case DCERPC_BIND_REASON_INVALID_AUTH_TYPE:
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+/*
+ a bind or alter context has failed
+*/
+static void dcerpc_composite_fail(struct rpc_request *req)
+{
+ struct composite_context *c = talloc_get_type(req->async.private_data,
+ struct composite_context);
+ composite_error(c, req->status);
+}
+
+/*
+ remove requests from the pending or queued queues
+ */
+static int dcerpc_req_dequeue(struct rpc_request *req)
+{
+ switch (req->state) {
+ case RPC_REQUEST_QUEUED:
+ DLIST_REMOVE(req->p->conn->request_queue, req);
+ break;
+ case RPC_REQUEST_PENDING:
+ DLIST_REMOVE(req->p->conn->pending, req);
+ break;
+ case RPC_REQUEST_DONE:
+ break;
+ }
+ return 0;
+}
+
+
+/*
+ mark the dcerpc connection dead. All outstanding requests get an error
+*/
+static void dcerpc_connection_dead(struct dcerpc_connection *conn, NTSTATUS status)
+{
+ if (conn->dead) return;
+
+ conn->dead = true;
+
+ if (conn->transport.shutdown_pipe) {
+ conn->transport.shutdown_pipe(conn, status);
+ }
+
+ /* all pending requests get the error */
+ while (conn->pending) {
+ struct rpc_request *req = conn->pending;
+ dcerpc_req_dequeue(req);
+ req->state = RPC_REQUEST_DONE;
+ req->status = status;
+ if (req->async.callback) {
+ req->async.callback(req);
+ }
+ }
+
+ talloc_set_destructor(conn, NULL);
+ if (conn->free_skipped) {
+ talloc_free(conn);
+ }
+}
+
+/*
+ forward declarations of the recv_data handlers for the types of
+ packets we need to handle
+*/
+static void dcerpc_request_recv_data(struct dcerpc_connection *c,
+ DATA_BLOB *raw_packet, struct ncacn_packet *pkt);
+
+/*
+ receive a dcerpc reply from the transport. Here we work out what
+ type of reply it is (normal request, bind or alter context) and
+ dispatch to the appropriate handler
+*/
+static void dcerpc_recv_data(struct dcerpc_connection *conn, DATA_BLOB *blob, NTSTATUS status)
+{
+ struct ncacn_packet pkt;
+
+ if (NT_STATUS_IS_OK(status) && blob->length == 0) {
+ status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+
+ /* the transport may be telling us of a severe error, such as
+ a dropped socket */
+ if (!NT_STATUS_IS_OK(status)) {
+ data_blob_free(blob);
+ dcerpc_connection_dead(conn, status);
+ return;
+ }
+
+ /* parse the basic packet to work out what type of response this is */
+ status = ncacn_pull(conn, blob, blob->data, &pkt);
+ if (!NT_STATUS_IS_OK(status)) {
+ data_blob_free(blob);
+ dcerpc_connection_dead(conn, status);
+ }
+
+ dcerpc_request_recv_data(conn, blob, &pkt);
+}
+
+
+/*
+ Receive a bind reply from the transport
+*/
+static void dcerpc_bind_recv_handler(struct rpc_request *req,
+ DATA_BLOB *raw_packet, struct ncacn_packet *pkt)
+{
+ struct composite_context *c;
+ struct dcerpc_connection *conn;
+
+ c = talloc_get_type(req->async.private_data, struct composite_context);
+
+ if (pkt->ptype == DCERPC_PKT_BIND_NAK) {
+ DEBUG(2,("dcerpc: bind_nak reason %d\n",
+ pkt->u.bind_nak.reject_reason));
+ composite_error(c, dcerpc_map_reason(pkt->u.bind_nak.
+ reject_reason));
+ return;
+ }
+
+ if ((pkt->ptype != DCERPC_PKT_BIND_ACK) ||
+ (pkt->u.bind_ack.num_results == 0) ||
+ (pkt->u.bind_ack.ctx_list[0].result != 0)) {
+ composite_error(c, NT_STATUS_NET_WRITE_FAULT);
+ return;
+ }
+
+ conn = req->p->conn;
+
+ conn->srv_max_xmit_frag = pkt->u.bind_ack.max_xmit_frag;
+ conn->srv_max_recv_frag = pkt->u.bind_ack.max_recv_frag;
+
+ if ((req->p->binding->flags & DCERPC_CONCURRENT_MULTIPLEX) &&
+ (pkt->pfc_flags & DCERPC_PFC_FLAG_CONC_MPX)) {
+ conn->flags |= DCERPC_CONCURRENT_MULTIPLEX;
+ }
+
+ if ((req->p->binding->flags & DCERPC_HEADER_SIGNING) &&
+ (pkt->pfc_flags & DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN)) {
+ conn->flags |= DCERPC_HEADER_SIGNING;
+ }
+
+ /* the bind_ack might contain a reply set of credentials */
+ if (conn->security_state.auth_info &&
+ pkt->u.bind_ack.auth_info.length) {
+ enum ndr_err_code ndr_err;
+ ndr_err = ndr_pull_struct_blob(
+ &pkt->u.bind_ack.auth_info, conn,
+ NULL,
+ conn->security_state.auth_info,
+ (ndr_pull_flags_fn_t)ndr_pull_dcerpc_auth);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ c->status = ndr_map_error2ntstatus(ndr_err);
+ if (!composite_is_ok(c)) return;
+ }
+ }
+
+ req->p->assoc_group_id = pkt->u.bind_ack.assoc_group_id;
+
+ composite_done(c);
+}
+
+/*
+ handle timeouts of individual dcerpc requests
+*/
+static void dcerpc_timeout_handler(struct tevent_context *ev, struct tevent_timer *te,
+ struct timeval t, void *private_data)
+{
+ struct rpc_request *req = talloc_get_type(private_data, struct rpc_request);
+
+ if (req->ignore_timeout) {
+ dcerpc_req_dequeue(req);
+ req->state = RPC_REQUEST_DONE;
+ req->status = NT_STATUS_IO_TIMEOUT;
+ if (req->async.callback) {
+ req->async.callback(req);
+ }
+ return;
+ }
+
+ dcerpc_connection_dead(req->p->conn, NT_STATUS_IO_TIMEOUT);
+}
+
+/*
+ send a async dcerpc bind request
+*/
+struct composite_context *dcerpc_bind_send(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ const struct ndr_syntax_id *syntax,
+ const struct ndr_syntax_id *transfer_syntax)
+{
+ struct composite_context *c;
+ struct ncacn_packet pkt;
+ DATA_BLOB blob;
+ struct rpc_request *req;
+
+ c = composite_create(mem_ctx,p->conn->event_ctx);
+ if (c == NULL) return NULL;
+
+ c->private_data = p;
+
+ p->syntax = *syntax;
+ p->transfer_syntax = *transfer_syntax;
+
+ init_ncacn_hdr(p->conn, &pkt);
+
+ pkt.ptype = DCERPC_PKT_BIND;
+ pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
+ pkt.call_id = p->conn->call_id;
+ pkt.auth_length = 0;
+
+ if (p->binding->flags & DCERPC_CONCURRENT_MULTIPLEX) {
+ pkt.pfc_flags |= DCERPC_PFC_FLAG_CONC_MPX;
+ }
+
+ if (p->binding->flags & DCERPC_HEADER_SIGNING) {
+ pkt.pfc_flags |= DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN;
+ }
+
+ pkt.u.bind.max_xmit_frag = 5840;
+ pkt.u.bind.max_recv_frag = 5840;
+ pkt.u.bind.assoc_group_id = p->binding->assoc_group_id;
+ pkt.u.bind.num_contexts = 1;
+ pkt.u.bind.ctx_list = talloc_array(mem_ctx, struct dcerpc_ctx_list, 1);
+ if (composite_nomem(pkt.u.bind.ctx_list, c)) return c;
+ pkt.u.bind.ctx_list[0].context_id = p->context_id;
+ pkt.u.bind.ctx_list[0].num_transfer_syntaxes = 1;
+ pkt.u.bind.ctx_list[0].abstract_syntax = p->syntax;
+ pkt.u.bind.ctx_list[0].transfer_syntaxes = &p->transfer_syntax;
+ pkt.u.bind.auth_info = data_blob(NULL, 0);
+
+ /* construct the NDR form of the packet */
+ c->status = ncacn_push_auth(&blob, c, p->conn->iconv_convenience, &pkt,
+ p->conn->security_state.auth_info);
+ if (!composite_is_ok(c)) return c;
+
+ p->conn->transport.recv_data = dcerpc_recv_data;
+
+ /*
+ * we allocate a dcerpc_request so we can be in the same
+ * request queue as normal requests
+ */
+ req = talloc_zero(c, struct rpc_request);
+ if (composite_nomem(req, c)) return c;
+
+ req->state = RPC_REQUEST_PENDING;
+ req->call_id = pkt.call_id;
+ req->async.private_data = c;
+ req->async.callback = dcerpc_composite_fail;
+ req->p = p;
+ req->recv_handler = dcerpc_bind_recv_handler;
+ DLIST_ADD_END(p->conn->pending, req, struct rpc_request *);
+ talloc_set_destructor(req, dcerpc_req_dequeue);
+
+ c->status = p->conn->transport.send_request(p->conn, &blob,
+ true);
+ if (!composite_is_ok(c)) return c;
+
+ event_add_timed(c->event_ctx, req,
+ timeval_current_ofs(DCERPC_REQUEST_TIMEOUT, 0),
+ dcerpc_timeout_handler, req);
+
+ return c;
+}
+
+/*
+ recv side of async dcerpc bind request
+*/
+NTSTATUS dcerpc_bind_recv(struct composite_context *ctx)
+{
+ NTSTATUS result = composite_wait(ctx);
+ talloc_free(ctx);
+ return result;
+}
+
+/*
+ perform a continued bind (and auth3)
+*/
+NTSTATUS dcerpc_auth3(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx)
+{
+ struct ncacn_packet pkt;
+ NTSTATUS status;
+ DATA_BLOB blob;
+
+ init_ncacn_hdr(p->conn, &pkt);
+
+ pkt.ptype = DCERPC_PKT_AUTH3;
+ pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
+ pkt.call_id = next_call_id(p->conn);
+ pkt.auth_length = 0;
+ pkt.u.auth3._pad = 0;
+ pkt.u.auth3.auth_info = data_blob(NULL, 0);
+
+ if (p->binding->flags & DCERPC_CONCURRENT_MULTIPLEX) {
+ pkt.pfc_flags |= DCERPC_PFC_FLAG_CONC_MPX;
+ }
+
+ if (p->binding->flags & DCERPC_HEADER_SIGNING) {
+ pkt.pfc_flags |= DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN;
+ }
+
+ /* construct the NDR form of the packet */
+ status = ncacn_push_auth(&blob, mem_ctx,
+ p->conn->iconv_convenience,
+ &pkt,
+ p->conn->security_state.auth_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* send it on its way */
+ status = p->conn->transport.send_request(p->conn, &blob, false);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ process a fragment received from the transport layer during a
+ request
+
+ This function frees the data
+*/
+static void dcerpc_request_recv_data(struct dcerpc_connection *c,
+ DATA_BLOB *raw_packet, struct ncacn_packet *pkt)
+{
+ struct rpc_request *req;
+ uint_t length;
+ NTSTATUS status = NT_STATUS_OK;
+
+ /*
+ if this is an authenticated connection then parse and check
+ the auth info. We have to do this before finding the
+ matching packet, as the request structure might have been
+ removed due to a timeout, but if it has been we still need
+ to run the auth routines so that we don't get the sign/seal
+ info out of step with the server
+ */
+ if (c->security_state.auth_info && c->security_state.generic_state &&
+ pkt->ptype == DCERPC_PKT_RESPONSE) {
+ status = ncacn_pull_request_auth(c, raw_packet->data, raw_packet, pkt);
+ }
+
+ /* find the matching request */
+ for (req=c->pending;req;req=req->next) {
+ if (pkt->call_id == req->call_id) break;
+ }
+
+#if 0
+ /* useful for testing certain vendors RPC servers */
+ if (req == NULL && c->pending && pkt->call_id == 0) {
+ DEBUG(0,("HACK FOR INCORRECT CALL ID\n"));
+ req = c->pending;
+ }
+#endif
+
+ if (req == NULL) {
+ DEBUG(2,("dcerpc_request: unmatched call_id %u in response packet\n", pkt->call_id));
+ data_blob_free(raw_packet);
+ return;
+ }
+
+ talloc_steal(req, raw_packet->data);
+
+ if (req->recv_handler != NULL) {
+ dcerpc_req_dequeue(req);
+ req->state = RPC_REQUEST_DONE;
+ req->recv_handler(req, raw_packet, pkt);
+ return;
+ }
+
+ if (pkt->ptype == DCERPC_PKT_FAULT) {
+ DEBUG(5,("rpc fault: %s\n", dcerpc_errstr(c, pkt->u.fault.status)));
+ req->fault_code = pkt->u.fault.status;
+ req->status = NT_STATUS_NET_WRITE_FAULT;
+ goto req_done;
+ }
+
+ if (pkt->ptype != DCERPC_PKT_RESPONSE) {
+ DEBUG(2,("Unexpected packet type %d in dcerpc response\n",
+ (int)pkt->ptype));
+ req->fault_code = DCERPC_FAULT_OTHER;
+ req->status = NT_STATUS_NET_WRITE_FAULT;
+ goto req_done;
+ }
+
+ /* now check the status from the auth routines, and if it failed then fail
+ this request accordingly */
+ if (!NT_STATUS_IS_OK(status)) {
+ req->status = status;
+ goto req_done;
+ }
+
+ length = pkt->u.response.stub_and_verifier.length;
+
+ if (length > 0) {
+ req->payload.data = talloc_realloc(req,
+ req->payload.data,
+ uint8_t,
+ req->payload.length + length);
+ if (!req->payload.data) {
+ req->status = NT_STATUS_NO_MEMORY;
+ goto req_done;
+ }
+ memcpy(req->payload.data+req->payload.length,
+ pkt->u.response.stub_and_verifier.data, length);
+ req->payload.length += length;
+ }
+
+ if (!(pkt->pfc_flags & DCERPC_PFC_FLAG_LAST)) {
+ c->transport.send_read(c);
+ return;
+ }
+
+ if (!(pkt->drep[0] & DCERPC_DREP_LE)) {
+ req->flags |= DCERPC_PULL_BIGENDIAN;
+ } else {
+ req->flags &= ~DCERPC_PULL_BIGENDIAN;
+ }
+
+
+req_done:
+ /* we've got the full payload */
+ req->state = RPC_REQUEST_DONE;
+ DLIST_REMOVE(c->pending, req);
+
+ if (c->request_queue != NULL) {
+ /* We have to look at shipping further requests before calling
+ * the async function, that one might close the pipe */
+ dcerpc_ship_next_request(c);
+ }
+
+ if (req->async.callback) {
+ req->async.callback(req);
+ }
+}
+
+/*
+ perform the send side of a async dcerpc request
+*/
+static struct rpc_request *dcerpc_request_send(struct dcerpc_pipe *p,
+ const struct GUID *object,
+ uint16_t opnum,
+ bool async,
+ DATA_BLOB *stub_data)
+{
+ struct rpc_request *req;
+
+ p->conn->transport.recv_data = dcerpc_recv_data;
+
+ req = talloc(p, struct rpc_request);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ req->p = p;
+ req->call_id = next_call_id(p->conn);
+ req->status = NT_STATUS_OK;
+ req->state = RPC_REQUEST_QUEUED;
+ req->payload = data_blob(NULL, 0);
+ req->flags = 0;
+ req->fault_code = 0;
+ req->async_call = async;
+ req->ignore_timeout = false;
+ req->async.callback = NULL;
+ req->async.private_data = NULL;
+ req->recv_handler = NULL;
+
+ if (object != NULL) {
+ req->object = (struct GUID *)talloc_memdup(req, (const void *)object, sizeof(*object));
+ if (req->object == NULL) {
+ talloc_free(req);
+ return NULL;
+ }
+ } else {
+ req->object = NULL;
+ }
+
+ req->opnum = opnum;
+ req->request_data.length = stub_data->length;
+ req->request_data.data = talloc_reference(req, stub_data->data);
+ if (req->request_data.length && req->request_data.data == NULL) {
+ return NULL;
+ }
+
+ DLIST_ADD_END(p->conn->request_queue, req, struct rpc_request *);
+ talloc_set_destructor(req, dcerpc_req_dequeue);
+
+ dcerpc_ship_next_request(p->conn);
+
+ if (p->request_timeout) {
+ event_add_timed(dcerpc_event_context(p), req,
+ timeval_current_ofs(p->request_timeout, 0),
+ dcerpc_timeout_handler, req);
+ }
+
+ return req;
+}
+
+/*
+ Send a request using the transport
+*/
+
+static void dcerpc_ship_next_request(struct dcerpc_connection *c)
+{
+ struct rpc_request *req;
+ struct dcerpc_pipe *p;
+ DATA_BLOB *stub_data;
+ struct ncacn_packet pkt;
+ DATA_BLOB blob;
+ uint32_t remaining, chunk_size;
+ bool first_packet = true;
+ size_t sig_size = 0;
+
+ req = c->request_queue;
+ if (req == NULL) {
+ return;
+ }
+
+ p = req->p;
+ stub_data = &req->request_data;
+
+ if (!req->async_call && (c->pending != NULL)) {
+ return;
+ }
+
+ DLIST_REMOVE(c->request_queue, req);
+ DLIST_ADD(c->pending, req);
+ req->state = RPC_REQUEST_PENDING;
+
+ init_ncacn_hdr(p->conn, &pkt);
+
+ remaining = stub_data->length;
+
+ /* we can write a full max_recv_frag size, minus the dcerpc
+ request header size */
+ chunk_size = p->conn->srv_max_recv_frag;
+ chunk_size -= DCERPC_REQUEST_LENGTH;
+ if (c->security_state.auth_info &&
+ c->security_state.generic_state) {
+ sig_size = gensec_sig_size(c->security_state.generic_state,
+ p->conn->srv_max_recv_frag);
+ if (sig_size) {
+ chunk_size -= DCERPC_AUTH_TRAILER_LENGTH;
+ chunk_size -= sig_size;
+ }
+ }
+ chunk_size -= (chunk_size % 16);
+
+ pkt.ptype = DCERPC_PKT_REQUEST;
+ pkt.call_id = req->call_id;
+ pkt.auth_length = 0;
+ pkt.pfc_flags = 0;
+ pkt.u.request.alloc_hint = remaining;
+ pkt.u.request.context_id = p->context_id;
+ pkt.u.request.opnum = req->opnum;
+
+ if (req->object) {
+ pkt.u.request.object.object = *req->object;
+ pkt.pfc_flags |= DCERPC_PFC_FLAG_OBJECT_UUID;
+ chunk_size -= ndr_size_GUID(req->object,NULL,0);
+ }
+
+ /* we send a series of pdus without waiting for a reply */
+ while (remaining > 0 || first_packet) {
+ uint32_t chunk = MIN(chunk_size, remaining);
+ bool last_frag = false;
+
+ first_packet = false;
+ pkt.pfc_flags &= ~(DCERPC_PFC_FLAG_FIRST |DCERPC_PFC_FLAG_LAST);
+
+ if (remaining == stub_data->length) {
+ pkt.pfc_flags |= DCERPC_PFC_FLAG_FIRST;
+ }
+ if (chunk == remaining) {
+ pkt.pfc_flags |= DCERPC_PFC_FLAG_LAST;
+ last_frag = true;
+ }
+
+ pkt.u.request.stub_and_verifier.data = stub_data->data +
+ (stub_data->length - remaining);
+ pkt.u.request.stub_and_verifier.length = chunk;
+
+ req->status = ncacn_push_request_sign(p->conn, &blob, req, sig_size, &pkt);
+ if (!NT_STATUS_IS_OK(req->status)) {
+ req->state = RPC_REQUEST_DONE;
+ DLIST_REMOVE(p->conn->pending, req);
+ return;
+ }
+
+ req->status = p->conn->transport.send_request(p->conn, &blob, last_frag);
+ if (!NT_STATUS_IS_OK(req->status)) {
+ req->state = RPC_REQUEST_DONE;
+ DLIST_REMOVE(p->conn->pending, req);
+ return;
+ }
+
+ remaining -= chunk;
+ }
+}
+
+/*
+ return the event context for a dcerpc pipe
+ used by callers who wish to operate asynchronously
+*/
+_PUBLIC_ struct tevent_context *dcerpc_event_context(struct dcerpc_pipe *p)
+{
+ return p->conn->event_ctx;
+}
+
+
+
+/*
+ perform the receive side of a async dcerpc request
+*/
+NTSTATUS dcerpc_request_recv(struct rpc_request *req,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *stub_data)
+{
+ NTSTATUS status;
+
+ while (req->state != RPC_REQUEST_DONE) {
+ struct tevent_context *ctx = dcerpc_event_context(req->p);
+ if (event_loop_once(ctx) != 0) {
+ return NT_STATUS_CONNECTION_DISCONNECTED;
+ }
+ }
+ *stub_data = req->payload;
+ status = req->status;
+ if (stub_data->data) {
+ stub_data->data = talloc_steal(mem_ctx, stub_data->data);
+ }
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ req->p->last_fault_code = req->fault_code;
+ }
+ talloc_free(req);
+ return status;
+}
+
+/*
+ perform a full request/response pair on a dcerpc pipe
+*/
+NTSTATUS dcerpc_request(struct dcerpc_pipe *p,
+ struct GUID *object,
+ uint16_t opnum,
+ bool async,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *stub_data_in,
+ DATA_BLOB *stub_data_out)
+{
+ struct rpc_request *req;
+
+ req = dcerpc_request_send(p, object, opnum, async, stub_data_in);
+ if (req == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ return dcerpc_request_recv(req, mem_ctx, stub_data_out);
+}
+
+
+/*
+ this is a paranoid NDR validator. For every packet we push onto the wire
+ we pull it back again, then push it again. Then we compare the raw NDR data
+ for that to the NDR we initially generated. If they don't match then we know
+ we must have a bug in either the pull or push side of our code
+*/
+static NTSTATUS dcerpc_ndr_validate_in(struct dcerpc_connection *c,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB blob,
+ size_t struct_size,
+ ndr_push_flags_fn_t ndr_push,
+ ndr_pull_flags_fn_t ndr_pull)
+{
+ void *st;
+ struct ndr_pull *pull;
+ struct ndr_push *push;
+ DATA_BLOB blob2;
+ enum ndr_err_code ndr_err;
+
+ st = talloc_size(mem_ctx, struct_size);
+ if (!st) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ pull = ndr_pull_init_flags(c, &blob, mem_ctx);
+ if (!pull) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ pull->flags |= LIBNDR_FLAG_REF_ALLOC;
+
+ ndr_err = ndr_pull(pull, NDR_IN, st);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
+ ndr_err = ndr_pull_error(pull, NDR_ERR_VALIDATE,
+ "failed input validation pull - %s",
+ nt_errstr(status));
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ push = ndr_push_init_ctx(mem_ctx, c->iconv_convenience);
+ if (!push) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ndr_err = ndr_push(push, NDR_IN, st);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
+ ndr_err = ndr_pull_error(pull, NDR_ERR_VALIDATE,
+ "failed input validation push - %s",
+ nt_errstr(status));
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ blob2 = ndr_push_blob(push);
+
+ if (data_blob_cmp(&blob, &blob2) != 0) {
+ DEBUG(3,("original:\n"));
+ dump_data(3, blob.data, blob.length);
+ DEBUG(3,("secondary:\n"));
+ dump_data(3, blob2.data, blob2.length);
+ ndr_err = ndr_pull_error(pull, NDR_ERR_VALIDATE,
+ "failed input validation blobs doesn't match");
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ this is a paranoid NDR input validator. For every packet we pull
+ from the wire we push it back again then pull and push it
+ again. Then we compare the raw NDR data for that to the NDR we
+ initially generated. If they don't match then we know we must have a
+ bug in either the pull or push side of our code
+*/
+static NTSTATUS dcerpc_ndr_validate_out(struct dcerpc_connection *c,
+ struct ndr_pull *pull_in,
+ void *struct_ptr,
+ size_t struct_size,
+ ndr_push_flags_fn_t ndr_push,
+ ndr_pull_flags_fn_t ndr_pull,
+ ndr_print_function_t ndr_print)
+{
+ void *st;
+ struct ndr_pull *pull;
+ struct ndr_push *push;
+ DATA_BLOB blob, blob2;
+ TALLOC_CTX *mem_ctx = pull_in;
+ char *s1, *s2;
+ enum ndr_err_code ndr_err;
+
+ st = talloc_size(mem_ctx, struct_size);
+ if (!st) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ memcpy(st, struct_ptr, struct_size);
+
+ push = ndr_push_init_ctx(mem_ctx, c->iconv_convenience);
+ if (!push) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ndr_err = ndr_push(push, NDR_OUT, struct_ptr);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
+ ndr_err = ndr_push_error(push, NDR_ERR_VALIDATE,
+ "failed output validation push - %s",
+ nt_errstr(status));
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ blob = ndr_push_blob(push);
+
+ pull = ndr_pull_init_flags(c, &blob, mem_ctx);
+ if (!pull) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ pull->flags |= LIBNDR_FLAG_REF_ALLOC;
+ ndr_err = ndr_pull(pull, NDR_OUT, st);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
+ ndr_err = ndr_pull_error(pull, NDR_ERR_VALIDATE,
+ "failed output validation pull - %s",
+ nt_errstr(status));
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ push = ndr_push_init_ctx(mem_ctx, c->iconv_convenience);
+ if (!push) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ndr_err = ndr_push(push, NDR_OUT, st);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
+ ndr_err = ndr_push_error(push, NDR_ERR_VALIDATE,
+ "failed output validation push2 - %s",
+ nt_errstr(status));
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ blob2 = ndr_push_blob(push);
+
+ if (data_blob_cmp(&blob, &blob2) != 0) {
+ DEBUG(3,("original:\n"));
+ dump_data(3, blob.data, blob.length);
+ DEBUG(3,("secondary:\n"));
+ dump_data(3, blob2.data, blob2.length);
+ ndr_err = ndr_push_error(push, NDR_ERR_VALIDATE,
+ "failed output validation blobs doesn't match");
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ /* this checks the printed forms of the two structures, which effectively
+ tests all of the value() attributes */
+ s1 = ndr_print_function_string(mem_ctx, ndr_print, "VALIDATE",
+ NDR_OUT, struct_ptr);
+ s2 = ndr_print_function_string(mem_ctx, ndr_print, "VALIDATE",
+ NDR_OUT, st);
+ if (strcmp(s1, s2) != 0) {
+#if 1
+ DEBUG(3,("VALIDATE ERROR:\nWIRE:\n%s\n GEN:\n%s\n", s1, s2));
+#else
+ /* this is sometimes useful */
+ printf("VALIDATE ERROR\n");
+ file_save("wire.dat", s1, strlen(s1));
+ file_save("gen.dat", s2, strlen(s2));
+ system("diff -u wire.dat gen.dat");
+#endif
+ ndr_err = ndr_push_error(push, NDR_ERR_VALIDATE,
+ "failed output validation strings doesn't match");
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/**
+ send a rpc request given a dcerpc_call structure
+ */
+struct rpc_request *dcerpc_ndr_request_send(struct dcerpc_pipe *p,
+ const struct GUID *object,
+ const struct ndr_interface_table *table,
+ uint32_t opnum,
+ TALLOC_CTX *mem_ctx,
+ void *r)
+{
+ const struct ndr_interface_call *call;
+ struct ndr_push *push;
+ NTSTATUS status;
+ DATA_BLOB request;
+ struct rpc_request *req;
+ enum ndr_err_code ndr_err;
+
+ call = &table->calls[opnum];
+
+ /* setup for a ndr_push_* call */
+ push = ndr_push_init_ctx(mem_ctx, p->conn->iconv_convenience);
+ if (!push) {
+ return NULL;
+ }
+
+ if (p->conn->flags & DCERPC_PUSH_BIGENDIAN) {
+ push->flags |= LIBNDR_FLAG_BIGENDIAN;
+ }
+
+ /* push the structure into a blob */
+ ndr_err = call->ndr_push(push, NDR_IN, r);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ DEBUG(2,("Unable to ndr_push structure in dcerpc_ndr_request_send - %s\n",
+ nt_errstr(status)));
+ talloc_free(push);
+ return NULL;
+ }
+
+ /* retrieve the blob */
+ request = ndr_push_blob(push);
+
+ if (p->conn->flags & DCERPC_DEBUG_VALIDATE_IN) {
+ status = dcerpc_ndr_validate_in(p->conn, push, request, call->struct_size,
+ call->ndr_push, call->ndr_pull);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(2,("Validation failed in dcerpc_ndr_request_send - %s\n",
+ nt_errstr(status)));
+ talloc_free(push);
+ return NULL;
+ }
+ }
+
+ DEBUG(10,("rpc request data:\n"));
+ dump_data(10, request.data, request.length);
+
+ /* make the actual dcerpc request */
+ req = dcerpc_request_send(p, object, opnum, table->calls[opnum].async,
+ &request);
+
+ if (req != NULL) {
+ req->ndr.table = table;
+ req->ndr.opnum = opnum;
+ req->ndr.struct_ptr = r;
+ req->ndr.mem_ctx = mem_ctx;
+ }
+
+ talloc_free(push);
+
+ return req;
+}
+
+/*
+ receive the answer from a dcerpc_ndr_request_send()
+*/
+_PUBLIC_ NTSTATUS dcerpc_ndr_request_recv(struct rpc_request *req)
+{
+ struct dcerpc_pipe *p = req->p;
+ NTSTATUS status;
+ DATA_BLOB response;
+ struct ndr_pull *pull;
+ uint_t flags;
+ TALLOC_CTX *mem_ctx = req->ndr.mem_ctx;
+ void *r = req->ndr.struct_ptr;
+ uint32_t opnum = req->ndr.opnum;
+ const struct ndr_interface_table *table = req->ndr.table;
+ const struct ndr_interface_call *call = &table->calls[opnum];
+ enum ndr_err_code ndr_err;
+
+ /* make sure the recv code doesn't free the request, as we
+ need to grab the flags element before it is freed */
+ if (talloc_reference(p, req) == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = dcerpc_request_recv(req, mem_ctx, &response);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_unlink(p, req);
+ return status;
+ }
+
+ flags = req->flags;
+
+ /* prepare for ndr_pull_* */
+ pull = ndr_pull_init_flags(p->conn, &response, mem_ctx);
+ if (!pull) {
+ talloc_unlink(p, req);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (pull->data) {
+ pull->data = talloc_steal(pull, pull->data);
+ }
+ talloc_unlink(p, req);
+
+ if (flags & DCERPC_PULL_BIGENDIAN) {
+ pull->flags |= LIBNDR_FLAG_BIGENDIAN;
+ }
+
+ DEBUG(10,("rpc reply data:\n"));
+ dump_data(10, pull->data, pull->data_size);
+
+ /* pull the structure from the blob */
+ ndr_err = call->ndr_pull(pull, NDR_OUT, r);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ dcerpc_log_packet(p->conn->packet_log_dir,
+ table, opnum, NDR_OUT,
+ &response);
+ return status;
+ }
+
+ if (p->conn->flags & DCERPC_DEBUG_VALIDATE_OUT) {
+ status = dcerpc_ndr_validate_out(p->conn, pull, r, call->struct_size,
+ call->ndr_push, call->ndr_pull,
+ call->ndr_print);
+ if (!NT_STATUS_IS_OK(status)) {
+ dcerpc_log_packet(p->conn->packet_log_dir,
+ table, opnum, NDR_OUT,
+ &response);
+ return status;
+ }
+ }
+
+ if (pull->offset != pull->data_size) {
+ DEBUG(0,("Warning! ignoring %d unread bytes in rpc packet!\n",
+ pull->data_size - pull->offset));
+ /* we used to return NT_STATUS_INFO_LENGTH_MISMATCH here,
+ but it turns out that early versions of NT
+ (specifically NT3.1) add junk onto the end of rpc
+ packets, so if we want to interoperate at all with
+ those versions then we need to ignore this error */
+ }
+
+ /* TODO: make pull context independent from the output mem_ctx and free the pull context */
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ a useful helper function for synchronous rpc requests
+
+ this can be used when you have ndr push/pull functions in the
+ standard format
+*/
+_PUBLIC_ NTSTATUS dcerpc_ndr_request(struct dcerpc_pipe *p,
+ const struct GUID *object,
+ const struct ndr_interface_table *table,
+ uint32_t opnum,
+ TALLOC_CTX *mem_ctx,
+ void *r)
+{
+ struct rpc_request *req;
+
+ req = dcerpc_ndr_request_send(p, object, table, opnum, mem_ctx, r);
+ if (req == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ return dcerpc_ndr_request_recv(req);
+}
+
+
+/*
+ a useful function for retrieving the server name we connected to
+*/
+_PUBLIC_ const char *dcerpc_server_name(struct dcerpc_pipe *p)
+{
+ if (!p->conn->transport.target_hostname) {
+ if (!p->conn->transport.peer_name) {
+ return "";
+ }
+ return p->conn->transport.peer_name(p->conn);
+ }
+ return p->conn->transport.target_hostname(p->conn);
+}
+
+
+/*
+ get the dcerpc auth_level for a open connection
+*/
+uint32_t dcerpc_auth_level(struct dcerpc_connection *c)
+{
+ uint8_t auth_level;
+
+ if (c->flags & DCERPC_SEAL) {
+ auth_level = DCERPC_AUTH_LEVEL_PRIVACY;
+ } else if (c->flags & DCERPC_SIGN) {
+ auth_level = DCERPC_AUTH_LEVEL_INTEGRITY;
+ } else if (c->flags & DCERPC_CONNECT) {
+ auth_level = DCERPC_AUTH_LEVEL_CONNECT;
+ } else {
+ auth_level = DCERPC_AUTH_LEVEL_NONE;
+ }
+ return auth_level;
+}
+
+/*
+ Receive an alter reply from the transport
+*/
+static void dcerpc_alter_recv_handler(struct rpc_request *req,
+ DATA_BLOB *raw_packet, struct ncacn_packet *pkt)
+{
+ struct composite_context *c;
+ struct dcerpc_pipe *recv_pipe;
+
+ c = talloc_get_type(req->async.private_data, struct composite_context);
+ recv_pipe = talloc_get_type(c->private_data, struct dcerpc_pipe);
+
+ if (pkt->ptype == DCERPC_PKT_ALTER_RESP &&
+ pkt->u.alter_resp.num_results == 1 &&
+ pkt->u.alter_resp.ctx_list[0].result != 0) {
+ DEBUG(2,("dcerpc: alter_resp failed - reason %d\n",
+ pkt->u.alter_resp.ctx_list[0].reason));
+ composite_error(c, dcerpc_map_reason(pkt->u.alter_resp.ctx_list[0].reason));
+ return;
+ }
+
+ if (pkt->ptype != DCERPC_PKT_ALTER_RESP ||
+ pkt->u.alter_resp.num_results == 0 ||
+ pkt->u.alter_resp.ctx_list[0].result != 0) {
+ composite_error(c, NT_STATUS_NET_WRITE_FAULT);
+ return;
+ }
+
+ /* the alter_resp might contain a reply set of credentials */
+ if (recv_pipe->conn->security_state.auth_info &&
+ pkt->u.alter_resp.auth_info.length) {
+ enum ndr_err_code ndr_err;
+ ndr_err = ndr_pull_struct_blob(
+ &pkt->u.alter_resp.auth_info, recv_pipe,
+ NULL,
+ recv_pipe->conn->security_state.auth_info,
+ (ndr_pull_flags_fn_t)ndr_pull_dcerpc_auth);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ c->status = ndr_map_error2ntstatus(ndr_err);
+ if (!composite_is_ok(c)) return;
+ }
+ }
+
+ composite_done(c);
+}
+
+/*
+ send a dcerpc alter_context request
+*/
+struct composite_context *dcerpc_alter_context_send(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ const struct ndr_syntax_id *syntax,
+ const struct ndr_syntax_id *transfer_syntax)
+{
+ struct composite_context *c;
+ struct ncacn_packet pkt;
+ DATA_BLOB blob;
+ struct rpc_request *req;
+
+ c = composite_create(mem_ctx, p->conn->event_ctx);
+ if (c == NULL) return NULL;
+
+ c->private_data = p;
+
+ p->syntax = *syntax;
+ p->transfer_syntax = *transfer_syntax;
+
+ init_ncacn_hdr(p->conn, &pkt);
+
+ pkt.ptype = DCERPC_PKT_ALTER;
+ pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
+ pkt.call_id = p->conn->call_id;
+ pkt.auth_length = 0;
+
+ if (p->binding->flags & DCERPC_CONCURRENT_MULTIPLEX) {
+ pkt.pfc_flags |= DCERPC_PFC_FLAG_CONC_MPX;
+ }
+
+ if (p->binding->flags & DCERPC_HEADER_SIGNING) {
+ pkt.pfc_flags |= DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN;
+ }
+
+ pkt.u.alter.max_xmit_frag = 5840;
+ pkt.u.alter.max_recv_frag = 5840;
+ pkt.u.alter.assoc_group_id = p->binding->assoc_group_id;
+ pkt.u.alter.num_contexts = 1;
+ pkt.u.alter.ctx_list = talloc_array(c, struct dcerpc_ctx_list, 1);
+ if (composite_nomem(pkt.u.alter.ctx_list, c)) return c;
+ pkt.u.alter.ctx_list[0].context_id = p->context_id;
+ pkt.u.alter.ctx_list[0].num_transfer_syntaxes = 1;
+ pkt.u.alter.ctx_list[0].abstract_syntax = p->syntax;
+ pkt.u.alter.ctx_list[0].transfer_syntaxes = &p->transfer_syntax;
+ pkt.u.alter.auth_info = data_blob(NULL, 0);
+
+ /* construct the NDR form of the packet */
+ c->status = ncacn_push_auth(&blob, mem_ctx, p->conn->iconv_convenience, &pkt,
+ p->conn->security_state.auth_info);
+ if (!composite_is_ok(c)) return c;
+
+ p->conn->transport.recv_data = dcerpc_recv_data;
+
+ /*
+ * we allocate a dcerpc_request so we can be in the same
+ * request queue as normal requests
+ */
+ req = talloc_zero(c, struct rpc_request);
+ if (composite_nomem(req, c)) return c;
+
+ req->state = RPC_REQUEST_PENDING;
+ req->call_id = pkt.call_id;
+ req->async.private_data = c;
+ req->async.callback = dcerpc_composite_fail;
+ req->p = p;
+ req->recv_handler = dcerpc_alter_recv_handler;
+ DLIST_ADD_END(p->conn->pending, req, struct rpc_request *);
+ talloc_set_destructor(req, dcerpc_req_dequeue);
+
+ c->status = p->conn->transport.send_request(p->conn, &blob, true);
+ if (!composite_is_ok(c)) return c;
+
+ event_add_timed(c->event_ctx, req,
+ timeval_current_ofs(DCERPC_REQUEST_TIMEOUT, 0),
+ dcerpc_timeout_handler, req);
+
+ return c;
+}
+
+NTSTATUS dcerpc_alter_context_recv(struct composite_context *ctx)
+{
+ NTSTATUS result = composite_wait(ctx);
+ talloc_free(ctx);
+ return result;
+}
+
+/*
+ send a dcerpc alter_context request
+*/
+_PUBLIC_ NTSTATUS dcerpc_alter_context(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ const struct ndr_syntax_id *syntax,
+ const struct ndr_syntax_id *transfer_syntax)
+{
+ struct composite_context *creq;
+ creq = dcerpc_alter_context_send(p, mem_ctx, syntax, transfer_syntax);
+ return dcerpc_alter_context_recv(creq);
+}
diff --git a/source4/librpc/rpc/dcerpc.h b/source4/librpc/rpc/dcerpc.h
new file mode 100644
index 0000000000..d6619ace07
--- /dev/null
+++ b/source4/librpc/rpc/dcerpc.h
@@ -0,0 +1,390 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ DCERPC client side interface structures
+
+ Copyright (C) Tim Potter 2003
+ Copyright (C) Andrew Tridgell 2003-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 <http://www.gnu.org/licenses/>.
+*/
+
+/* This is a public header file that is installed as part of Samba.
+ * If you remove any functions or change their signature, update
+ * the so version number. */
+
+#ifndef __DCERPC_H__
+#define __DCERPC_H__
+
+#include "../lib/util/data_blob.h"
+#include "librpc/gen_ndr/dcerpc.h"
+#include "../librpc/ndr/libndr.h"
+
+enum dcerpc_transport_t {
+ NCA_UNKNOWN, NCACN_NP, NCACN_IP_TCP, NCACN_IP_UDP, NCACN_VNS_IPC,
+ NCACN_VNS_SPP, NCACN_AT_DSP, NCADG_AT_DDP, NCALRPC, NCACN_UNIX_STREAM,
+ NCADG_UNIX_DGRAM, NCACN_HTTP, NCADG_IPX, NCACN_SPX };
+
+/*
+ this defines a generic security context for signed/sealed dcerpc pipes.
+*/
+struct dcerpc_connection;
+struct gensec_settings;
+struct dcerpc_security {
+ struct dcerpc_auth *auth_info;
+ struct gensec_security *generic_state;
+
+ /* get the session key */
+ NTSTATUS (*session_key)(struct dcerpc_connection *, DATA_BLOB *);
+};
+
+/*
+ this holds the information that is not specific to a particular rpc context_id
+*/
+struct dcerpc_connection {
+ uint32_t call_id;
+ uint32_t srv_max_xmit_frag;
+ uint32_t srv_max_recv_frag;
+ uint32_t flags;
+ struct dcerpc_security security_state;
+ const char *binding_string;
+ struct tevent_context *event_ctx;
+ struct smb_iconv_convenience *iconv_convenience;
+
+ /** Directory in which to save ndrdump-parseable files */
+ const char *packet_log_dir;
+
+ bool dead;
+ bool free_skipped;
+
+ struct dcerpc_transport {
+ enum dcerpc_transport_t transport;
+ void *private_data;
+
+ NTSTATUS (*shutdown_pipe)(struct dcerpc_connection *, NTSTATUS status);
+
+ const char *(*peer_name)(struct dcerpc_connection *);
+
+ const char *(*target_hostname)(struct dcerpc_connection *);
+
+ /* send a request to the server */
+ NTSTATUS (*send_request)(struct dcerpc_connection *, DATA_BLOB *, bool trigger_read);
+
+ /* send a read request to the server */
+ NTSTATUS (*send_read)(struct dcerpc_connection *);
+
+ /* a callback to the dcerpc code when a full fragment
+ has been received */
+ void (*recv_data)(struct dcerpc_connection *, DATA_BLOB *, NTSTATUS status);
+ } transport;
+
+ /* Requests that have been sent, waiting for a reply */
+ struct rpc_request *pending;
+
+ /* Sync requests waiting to be shipped */
+ struct rpc_request *request_queue;
+
+ /* the next context_id to be assigned */
+ uint32_t next_context_id;
+};
+
+/*
+ this encapsulates a full dcerpc client side pipe
+*/
+struct dcerpc_pipe {
+ uint32_t context_id;
+
+ uint32_t assoc_group_id;
+
+ struct ndr_syntax_id syntax;
+ struct ndr_syntax_id transfer_syntax;
+
+ struct dcerpc_connection *conn;
+ struct dcerpc_binding *binding;
+
+ /** the last fault code from a DCERPC fault */
+ uint32_t last_fault_code;
+
+ /** timeout for individual rpc requests, in seconds */
+ uint32_t request_timeout;
+};
+
+/* default timeout for all rpc requests, in seconds */
+#define DCERPC_REQUEST_TIMEOUT 60
+
+
+/* dcerpc pipe flags */
+#define DCERPC_DEBUG_PRINT_IN (1<<0)
+#define DCERPC_DEBUG_PRINT_OUT (1<<1)
+#define DCERPC_DEBUG_PRINT_BOTH (DCERPC_DEBUG_PRINT_IN | DCERPC_DEBUG_PRINT_OUT)
+
+#define DCERPC_DEBUG_VALIDATE_IN (1<<2)
+#define DCERPC_DEBUG_VALIDATE_OUT (1<<3)
+#define DCERPC_DEBUG_VALIDATE_BOTH (DCERPC_DEBUG_VALIDATE_IN | DCERPC_DEBUG_VALIDATE_OUT)
+
+#define DCERPC_CONNECT (1<<4)
+#define DCERPC_SIGN (1<<5)
+#define DCERPC_SEAL (1<<6)
+
+#define DCERPC_PUSH_BIGENDIAN (1<<7)
+#define DCERPC_PULL_BIGENDIAN (1<<8)
+
+#define DCERPC_SCHANNEL (1<<9)
+
+/* use a 128 bit session key */
+#define DCERPC_SCHANNEL_128 (1<<12)
+
+/* check incoming pad bytes */
+#define DCERPC_DEBUG_PAD_CHECK (1<<13)
+
+/* set LIBNDR_FLAG_REF_ALLOC flag when decoding NDR */
+#define DCERPC_NDR_REF_ALLOC (1<<14)
+
+#define DCERPC_AUTH_OPTIONS (DCERPC_SEAL|DCERPC_SIGN|DCERPC_SCHANNEL|DCERPC_AUTH_SPNEGO|DCERPC_AUTH_KRB5|DCERPC_AUTH_NTLM)
+
+/* select spnego auth */
+#define DCERPC_AUTH_SPNEGO (1<<15)
+
+/* select krb5 auth */
+#define DCERPC_AUTH_KRB5 (1<<16)
+
+#define DCERPC_SMB2 (1<<17)
+
+/* select NTLM auth */
+#define DCERPC_AUTH_NTLM (1<<18)
+
+/* this triggers the DCERPC_PFC_FLAG_CONC_MPX flag in the bind request */
+#define DCERPC_CONCURRENT_MULTIPLEX (1<<19)
+
+/* this triggers the DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN flag in the bind request */
+#define DCERPC_HEADER_SIGNING (1<<20)
+
+/* this describes a binding to a particular transport/pipe */
+struct dcerpc_binding {
+ enum dcerpc_transport_t transport;
+ struct ndr_syntax_id object;
+ const char *host;
+ const char *target_hostname;
+ const char *endpoint;
+ const char **options;
+ uint32_t flags;
+ uint32_t assoc_group_id;
+};
+
+
+struct dcerpc_pipe_connect {
+ struct dcerpc_pipe *pipe;
+ struct dcerpc_binding *binding;
+ const char *pipe_name;
+ const struct ndr_interface_table *interface;
+ struct cli_credentials *creds;
+ struct resolve_context *resolve_ctx;
+};
+
+
+enum rpc_request_state {
+ RPC_REQUEST_QUEUED,
+ RPC_REQUEST_PENDING,
+ RPC_REQUEST_DONE
+};
+
+/*
+ handle for an async dcerpc request
+*/
+struct rpc_request {
+ struct rpc_request *next, *prev;
+ struct dcerpc_pipe *p;
+ NTSTATUS status;
+ uint32_t call_id;
+ enum rpc_request_state state;
+ DATA_BLOB payload;
+ uint32_t flags;
+ uint32_t fault_code;
+
+ /* this is used to distinguish bind and alter_context requests
+ from normal requests */
+ void (*recv_handler)(struct rpc_request *conn,
+ DATA_BLOB *blob, struct ncacn_packet *pkt);
+
+ const struct GUID *object;
+ uint16_t opnum;
+ DATA_BLOB request_data;
+ bool async_call;
+ bool ignore_timeout;
+
+ /* use by the ndr level async recv call */
+ struct {
+ const struct ndr_interface_table *table;
+ uint32_t opnum;
+ void *struct_ptr;
+ TALLOC_CTX *mem_ctx;
+ } ndr;
+
+ struct {
+ void (*callback)(struct rpc_request *);
+ void *private_data;
+ } async;
+};
+
+struct epm_tower;
+struct epm_floor;
+
+struct smbcli_tree;
+struct smb2_tree;
+struct socket_address;
+
+NTSTATUS dcerpc_pipe_connect(TALLOC_CTX *parent_ctx,
+ struct dcerpc_pipe **pp,
+ const char *binding,
+ const struct ndr_interface_table *table,
+ struct cli_credentials *credentials,
+ struct tevent_context *ev,
+ struct loadparm_context *lp_ctx);
+NTSTATUS dcerpc_ndr_request_recv(struct rpc_request *req);
+struct rpc_request *dcerpc_ndr_request_send(struct dcerpc_pipe *p,
+ const struct GUID *object,
+ const struct ndr_interface_table *table,
+ uint32_t opnum,
+ TALLOC_CTX *mem_ctx,
+ void *r);
+const char *dcerpc_server_name(struct dcerpc_pipe *p);
+struct dcerpc_pipe *dcerpc_pipe_init(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+ struct smb_iconv_convenience *ic);
+NTSTATUS dcerpc_pipe_open_smb(struct dcerpc_pipe *p,
+ struct smbcli_tree *tree,
+ const char *pipe_name);
+NTSTATUS dcerpc_bind_auth_none(struct dcerpc_pipe *p,
+ const struct ndr_interface_table *table);
+NTSTATUS dcerpc_fetch_session_key(struct dcerpc_pipe *p,
+ DATA_BLOB *session_key);
+struct composite_context;
+NTSTATUS dcerpc_secondary_connection_recv(struct composite_context *c,
+ struct dcerpc_pipe **p2);
+NTSTATUS dcerpc_parse_binding(TALLOC_CTX *mem_ctx, const char *s, struct dcerpc_binding **b_out);
+
+struct composite_context* dcerpc_pipe_connect_b_send(TALLOC_CTX *parent_ctx,
+ struct dcerpc_binding *binding,
+ const struct ndr_interface_table *table,
+ struct cli_credentials *credentials,
+ struct tevent_context *ev,
+ struct loadparm_context *lp_ctx);
+
+NTSTATUS dcerpc_pipe_connect_b_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
+ struct dcerpc_pipe **p);
+
+NTSTATUS dcerpc_pipe_connect_b(TALLOC_CTX *parent_ctx,
+ struct dcerpc_pipe **pp,
+ struct dcerpc_binding *binding,
+ const struct ndr_interface_table *table,
+ struct cli_credentials *credentials,
+ struct tevent_context *ev,
+ struct loadparm_context *lp_ctx);
+const char *dcerpc_errstr(TALLOC_CTX *mem_ctx, uint32_t fault_code);
+
+NTSTATUS dcerpc_pipe_auth(TALLOC_CTX *mem_ctx,
+ struct dcerpc_pipe **p,
+ struct dcerpc_binding *binding,
+ const struct ndr_interface_table *table,
+ struct cli_credentials *credentials,
+ struct loadparm_context *lp_ctx);
+char *dcerpc_binding_string(TALLOC_CTX *mem_ctx, const struct dcerpc_binding *b);
+NTSTATUS dcerpc_secondary_connection(struct dcerpc_pipe *p,
+ struct dcerpc_pipe **p2,
+ struct dcerpc_binding *b);
+NTSTATUS dcerpc_bind_auth_schannel(TALLOC_CTX *tmp_ctx,
+ struct dcerpc_pipe *p,
+ const struct ndr_interface_table *table,
+ struct cli_credentials *credentials,
+ struct loadparm_context *lp_ctx,
+ uint8_t auth_level);
+struct tevent_context *dcerpc_event_context(struct dcerpc_pipe *p);
+NTSTATUS dcerpc_init(struct loadparm_context *lp_ctx);
+struct smbcli_tree *dcerpc_smb_tree(struct dcerpc_connection *c);
+uint16_t dcerpc_smb_fnum(struct dcerpc_connection *c);
+NTSTATUS dcerpc_secondary_context(struct dcerpc_pipe *p,
+ struct dcerpc_pipe **pp2,
+ const struct ndr_interface_table *table);
+NTSTATUS dcerpc_alter_context(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ const struct ndr_syntax_id *syntax,
+ const struct ndr_syntax_id *transfer_syntax);
+
+NTSTATUS dcerpc_bind_auth(struct dcerpc_pipe *p,
+ const struct ndr_interface_table *table,
+ struct cli_credentials *credentials,
+ struct gensec_settings *gensec_settings,
+ uint8_t auth_type, uint8_t auth_level,
+ const char *service);
+struct composite_context* dcerpc_pipe_connect_send(TALLOC_CTX *parent_ctx,
+ const char *binding,
+ const struct ndr_interface_table *table,
+ struct cli_credentials *credentials,
+ struct tevent_context *ev, struct loadparm_context *lp_ctx);
+NTSTATUS dcerpc_pipe_connect_recv(struct composite_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct dcerpc_pipe **pp);
+
+NTSTATUS dcerpc_epm_map_binding(TALLOC_CTX *mem_ctx, struct dcerpc_binding *binding,
+ const struct ndr_interface_table *table, struct tevent_context *ev,
+ struct loadparm_context *lp_ctx);
+struct composite_context* dcerpc_secondary_auth_connection_send(struct dcerpc_pipe *p,
+ struct dcerpc_binding *binding,
+ const struct ndr_interface_table *table,
+ struct cli_credentials *credentials,
+ struct loadparm_context *lp_ctx);
+NTSTATUS dcerpc_secondary_auth_connection_recv(struct composite_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct dcerpc_pipe **p);
+
+struct composite_context* dcerpc_secondary_connection_send(struct dcerpc_pipe *p,
+ struct dcerpc_binding *b);
+void dcerpc_log_packet(const char *lockdir,
+ const struct ndr_interface_table *ndr,
+ uint32_t opnum, uint32_t flags,
+ DATA_BLOB *pkt);
+NTSTATUS dcerpc_binding_build_tower(TALLOC_CTX *mem_ctx,
+ const struct dcerpc_binding *binding,
+ struct epm_tower *tower);
+
+NTSTATUS dcerpc_floor_get_lhs_data(const struct epm_floor *epm_floor, struct ndr_syntax_id *syntax);
+
+enum dcerpc_transport_t dcerpc_transport_by_tower(const struct epm_tower *tower);
+const char *derpc_transport_string_by_transport(enum dcerpc_transport_t t);
+
+NTSTATUS dcerpc_ndr_request(struct dcerpc_pipe *p,
+ const struct GUID *object,
+ const struct ndr_interface_table *table,
+ uint32_t opnum,
+ TALLOC_CTX *mem_ctx,
+ void *r);
+
+NTSTATUS dcerpc_binding_from_tower(TALLOC_CTX *mem_ctx,
+ struct epm_tower *tower,
+ struct dcerpc_binding **b_out);
+
+NTSTATUS dcerpc_request(struct dcerpc_pipe *p,
+ struct GUID *object,
+ uint16_t opnum,
+ bool async,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *stub_data_in,
+ DATA_BLOB *stub_data_out);
+
+typedef NTSTATUS (*dcerpc_call_fn) (struct dcerpc_pipe *, TALLOC_CTX *, void *);
+
+enum dcerpc_transport_t dcerpc_transport_by_endpoint_protocol(int prot);
+
+const char *dcerpc_floor_get_rhs_data(TALLOC_CTX *mem_ctx, struct epm_floor *epm_floor);
+
+#endif /* __DCERPC_H__ */
diff --git a/source4/librpc/rpc/dcerpc.py b/source4/librpc/rpc/dcerpc.py
new file mode 100644
index 0000000000..1a76ecca8e
--- /dev/null
+++ b/source4/librpc/rpc/dcerpc.py
@@ -0,0 +1,20 @@
+#!/usr/bin/python
+
+# Unix SMB/CIFS implementation.
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 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 <http://www.gnu.org/licenses/>.
+#
+
+from base import *
diff --git a/source4/librpc/rpc/dcerpc_auth.c b/source4/librpc/rpc/dcerpc_auth.c
new file mode 100644
index 0000000000..14f0f9deb4
--- /dev/null
+++ b/source4/librpc/rpc/dcerpc_auth.c
@@ -0,0 +1,398 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Generic Authentication Interface
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
+ Copyright (C) Stefan Metzmacher 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/composite/composite.h"
+#include "auth/gensec/gensec.h"
+#include "librpc/rpc/dcerpc.h"
+#include "librpc/rpc/dcerpc_proto.h"
+#include "param/param.h"
+
+/*
+ return the rpc syntax and transfer syntax given the pipe uuid and version
+*/
+static NTSTATUS dcerpc_init_syntaxes(const struct ndr_interface_table *table,
+ struct ndr_syntax_id *syntax,
+ struct ndr_syntax_id *transfer_syntax)
+{
+ syntax->uuid = table->syntax_id.uuid;
+ syntax->if_version = table->syntax_id.if_version;
+
+ *transfer_syntax = ndr_transfer_syntax;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ Send request to do a non-authenticated dcerpc bind
+*/
+struct composite_context *dcerpc_bind_auth_none_send(TALLOC_CTX *mem_ctx,
+ struct dcerpc_pipe *p,
+ const struct ndr_interface_table *table)
+{
+ struct ndr_syntax_id syntax;
+ struct ndr_syntax_id transfer_syntax;
+
+ struct composite_context *c;
+
+ c = composite_create(mem_ctx, p->conn->event_ctx);
+ if (c == NULL) return NULL;
+
+ c->status = dcerpc_init_syntaxes(table,
+ &syntax, &transfer_syntax);
+ if (!NT_STATUS_IS_OK(c->status)) {
+ DEBUG(2,("Invalid uuid string in "
+ "dcerpc_bind_auth_none_send\n"));
+ composite_error(c, c->status);
+ return c;
+ }
+
+ /* c was only allocated as a container for a possible error */
+ talloc_free(c);
+
+ return dcerpc_bind_send(p, mem_ctx, &syntax, &transfer_syntax);
+}
+
+
+/*
+ Receive result of a non-authenticated dcerpc bind
+*/
+NTSTATUS dcerpc_bind_auth_none_recv(struct composite_context *ctx)
+{
+ return dcerpc_bind_recv(ctx);
+}
+
+
+/*
+ Perform sync non-authenticated dcerpc bind
+*/
+_PUBLIC_ NTSTATUS dcerpc_bind_auth_none(struct dcerpc_pipe *p,
+ const struct ndr_interface_table *table)
+{
+ struct composite_context *ctx;
+
+ ctx = dcerpc_bind_auth_none_send(p, p, table);
+ return dcerpc_bind_auth_none_recv(ctx);
+}
+
+
+struct bind_auth_state {
+ struct dcerpc_pipe *pipe;
+ DATA_BLOB credentials;
+ bool more_processing; /* Is there anything more to do after the
+ * first bind itself received? */
+};
+
+static void bind_auth_recv_alter(struct composite_context *creq);
+
+static void bind_auth_next_step(struct composite_context *c)
+{
+ struct bind_auth_state *state;
+ struct dcerpc_security *sec;
+ struct composite_context *creq;
+ bool more_processing = false;
+
+ state = talloc_get_type(c->private_data, struct bind_auth_state);
+ sec = &state->pipe->conn->security_state;
+
+ /* The status value here, from GENSEC is vital to the security
+ * of the system. Even if the other end accepts, if GENSEC
+ * claims 'MORE_PROCESSING_REQUIRED' then you must keep
+ * feeding it blobs, or else the remote host/attacker might
+ * avoid mutal authentication requirements.
+ *
+ * Likewise, you must not feed GENSEC too much (after the OK),
+ * it doesn't like that either
+ */
+
+ c->status = gensec_update(sec->generic_state, state,
+ sec->auth_info->credentials,
+ &state->credentials);
+ data_blob_free(&sec->auth_info->credentials);
+
+ if (NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ more_processing = true;
+ c->status = NT_STATUS_OK;
+ }
+
+ if (!composite_is_ok(c)) return;
+
+ if (state->pipe->conn->flags & DCERPC_HEADER_SIGNING) {
+ gensec_want_feature(sec->generic_state, GENSEC_FEATURE_SIGN_PKT_HEADER);
+ }
+
+ if (state->credentials.length == 0) {
+ composite_done(c);
+ return;
+ }
+
+ sec->auth_info->credentials = state->credentials;
+
+ if (!more_processing) {
+ /* NO reply expected, so just send it */
+ c->status = dcerpc_auth3(state->pipe, state);
+ data_blob_free(&state->credentials);
+ sec->auth_info->credentials = data_blob(NULL, 0);
+ if (!composite_is_ok(c)) return;
+
+ composite_done(c);
+ return;
+ }
+
+ /* We are demanding a reply, so use a request that will get us one */
+
+ creq = dcerpc_alter_context_send(state->pipe, state,
+ &state->pipe->syntax,
+ &state->pipe->transfer_syntax);
+ data_blob_free(&state->credentials);
+ sec->auth_info->credentials = data_blob(NULL, 0);
+ if (composite_nomem(creq, c)) return;
+
+ composite_continue(c, creq, bind_auth_recv_alter, c);
+}
+
+
+static void bind_auth_recv_alter(struct composite_context *creq)
+{
+ struct composite_context *c = talloc_get_type(creq->async.private_data,
+ struct composite_context);
+
+ c->status = dcerpc_alter_context_recv(creq);
+ if (!composite_is_ok(c)) return;
+
+ bind_auth_next_step(c);
+}
+
+
+static void bind_auth_recv_bindreply(struct composite_context *creq)
+{
+ struct composite_context *c = talloc_get_type(creq->async.private_data,
+ struct composite_context);
+ struct bind_auth_state *state = talloc_get_type(c->private_data,
+ struct bind_auth_state);
+
+ c->status = dcerpc_bind_recv(creq);
+ if (!composite_is_ok(c)) return;
+
+ if (!state->more_processing) {
+ /* The first gensec_update has not requested a second run, so
+ * we're done here. */
+ composite_done(c);
+ return;
+ }
+
+ bind_auth_next_step(c);
+}
+
+
+/**
+ Bind to a DCE/RPC pipe, send async request
+ @param mem_ctx TALLOC_CTX for the allocation of the composite_context
+ @param p The dcerpc_pipe to bind (must already be connected)
+ @param table The interface table to use (the DCE/RPC bind both selects and interface and authenticates)
+ @param credentials The credentials of the account to connect with
+ @param auth_type Select the authentication scheme to use
+ @param auth_level Chooses between unprotected (connect), signed or sealed
+ @param service The service (used by Kerberos to select the service principal to contact)
+ @retval A composite context describing the partial state of the bind
+*/
+
+struct composite_context *dcerpc_bind_auth_send(TALLOC_CTX *mem_ctx,
+ struct dcerpc_pipe *p,
+ const struct ndr_interface_table *table,
+ struct cli_credentials *credentials,
+ struct gensec_settings *gensec_settings,
+ uint8_t auth_type, uint8_t auth_level,
+ const char *service)
+{
+ struct composite_context *c, *creq;
+ struct bind_auth_state *state;
+ struct dcerpc_security *sec;
+
+ struct ndr_syntax_id syntax, transfer_syntax;
+
+ /* composite context allocation and setup */
+ c = composite_create(mem_ctx, p->conn->event_ctx);
+ if (c == NULL) return NULL;
+
+ state = talloc(c, struct bind_auth_state);
+ if (composite_nomem(state, c)) return c;
+ c->private_data = state;
+
+ state->pipe = p;
+
+ c->status = dcerpc_init_syntaxes(table,
+ &syntax,
+ &transfer_syntax);
+ if (!composite_is_ok(c)) return c;
+
+ sec = &p->conn->security_state;
+
+ c->status = gensec_client_start(p, &sec->generic_state,
+ p->conn->event_ctx,
+ gensec_settings);
+ if (!NT_STATUS_IS_OK(c->status)) {
+ DEBUG(1, ("Failed to start GENSEC client mode: %s\n",
+ nt_errstr(c->status)));
+ composite_error(c, c->status);
+ return c;
+ }
+
+ c->status = gensec_set_credentials(sec->generic_state, credentials);
+ if (!NT_STATUS_IS_OK(c->status)) {
+ DEBUG(1, ("Failed to set GENSEC client credentials: %s\n",
+ nt_errstr(c->status)));
+ composite_error(c, c->status);
+ return c;
+ }
+
+ c->status = gensec_set_target_hostname(sec->generic_state,
+ p->conn->transport.target_hostname(p->conn));
+ if (!NT_STATUS_IS_OK(c->status)) {
+ DEBUG(1, ("Failed to set GENSEC target hostname: %s\n",
+ nt_errstr(c->status)));
+ composite_error(c, c->status);
+ return c;
+ }
+
+ if (service != NULL) {
+ c->status = gensec_set_target_service(sec->generic_state,
+ service);
+ if (!NT_STATUS_IS_OK(c->status)) {
+ DEBUG(1, ("Failed to set GENSEC target service: %s\n",
+ nt_errstr(c->status)));
+ composite_error(c, c->status);
+ return c;
+ }
+ }
+
+ c->status = gensec_start_mech_by_authtype(sec->generic_state,
+ auth_type, auth_level);
+ if (!NT_STATUS_IS_OK(c->status)) {
+ DEBUG(1, ("Failed to start GENSEC client mechanism %s: %s\n",
+ gensec_get_name_by_authtype(sec->generic_state, auth_type),
+ nt_errstr(c->status)));
+ composite_error(c, c->status);
+ return c;
+ }
+
+ sec->auth_info = talloc(p, struct dcerpc_auth);
+ if (composite_nomem(sec->auth_info, c)) return c;
+
+ sec->auth_info->auth_type = auth_type;
+ sec->auth_info->auth_level = auth_level,
+ sec->auth_info->auth_pad_length = 0;
+ sec->auth_info->auth_reserved = 0;
+ sec->auth_info->auth_context_id = random();
+ sec->auth_info->credentials = data_blob(NULL, 0);
+
+ /* The status value here, from GENSEC is vital to the security
+ * of the system. Even if the other end accepts, if GENSEC
+ * claims 'MORE_PROCESSING_REQUIRED' then you must keep
+ * feeding it blobs, or else the remote host/attacker might
+ * avoid mutal authentication requirements.
+ *
+ * Likewise, you must not feed GENSEC too much (after the OK),
+ * it doesn't like that either
+ */
+
+ c->status = gensec_update(sec->generic_state, state,
+ sec->auth_info->credentials,
+ &state->credentials);
+ if (!NT_STATUS_IS_OK(c->status) &&
+ !NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ composite_error(c, c->status);
+ return c;
+ }
+
+ state->more_processing = NT_STATUS_EQUAL(c->status,
+ NT_STATUS_MORE_PROCESSING_REQUIRED);
+
+ if (state->credentials.length == 0) {
+ composite_done(c);
+ return c;
+ }
+
+ sec->auth_info->credentials = state->credentials;
+
+ /* The first request always is a dcerpc_bind. The subsequent ones
+ * depend on gensec results */
+ creq = dcerpc_bind_send(p, state, &syntax, &transfer_syntax);
+ data_blob_free(&state->credentials);
+ sec->auth_info->credentials = data_blob(NULL, 0);
+ if (composite_nomem(creq, c)) return c;
+
+ composite_continue(c, creq, bind_auth_recv_bindreply, c);
+ return c;
+}
+
+
+/**
+ Bind to a DCE/RPC pipe, receive result
+ @param creq A composite context describing state of async call
+ @retval NTSTATUS code
+*/
+
+NTSTATUS dcerpc_bind_auth_recv(struct composite_context *creq)
+{
+ NTSTATUS result = composite_wait(creq);
+ struct bind_auth_state *state = talloc_get_type(creq->private_data,
+ struct bind_auth_state);
+
+ if (NT_STATUS_IS_OK(result)) {
+ /*
+ after a successful authenticated bind the session
+ key reverts to the generic session key
+ */
+ state->pipe->conn->security_state.session_key = dcerpc_generic_session_key;
+ }
+
+ talloc_free(creq);
+ return result;
+}
+
+
+/**
+ Perform a GENSEC authenticated bind to a DCE/RPC pipe, sync
+ @param p The dcerpc_pipe to bind (must already be connected)
+ @param table The interface table to use (the DCE/RPC bind both selects and interface and authenticates)
+ @param credentials The credentials of the account to connect with
+ @param auth_type Select the authentication scheme to use
+ @param auth_level Chooses between unprotected (connect), signed or sealed
+ @param service The service (used by Kerberos to select the service principal to contact)
+ @retval NTSTATUS status code
+*/
+
+_PUBLIC_ NTSTATUS dcerpc_bind_auth(struct dcerpc_pipe *p,
+ const struct ndr_interface_table *table,
+ struct cli_credentials *credentials,
+ struct gensec_settings *gensec_settings,
+ uint8_t auth_type, uint8_t auth_level,
+ const char *service)
+{
+ struct composite_context *creq;
+ creq = dcerpc_bind_auth_send(p, p, table, credentials, gensec_settings,
+ auth_type, auth_level, service);
+ return dcerpc_bind_auth_recv(creq);
+}
diff --git a/source4/librpc/rpc/dcerpc_connect.c b/source4/librpc/rpc/dcerpc_connect.c
new file mode 100644
index 0000000000..0f9fbe0abc
--- /dev/null
+++ b/source4/librpc/rpc/dcerpc_connect.c
@@ -0,0 +1,946 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ dcerpc connect functions
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Jelmer Vernooij 2004
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2007
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+
+#include "includes.h"
+#include "libcli/composite/composite.h"
+#include "libcli/smb_composite/smb_composite.h"
+#include "lib/events/events.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "librpc/rpc/dcerpc.h"
+#include "librpc/rpc/dcerpc_proto.h"
+#include "auth/credentials/credentials.h"
+#include "param/param.h"
+#include "libcli/resolve/resolve.h"
+
+
+struct pipe_np_smb_state {
+ struct smb_composite_connect conn;
+ struct smbcli_tree *tree;
+ struct dcerpc_pipe_connect io;
+};
+
+
+/*
+ Stage 3 of ncacn_np_smb: Named pipe opened (or not)
+*/
+static void continue_pipe_open_smb(struct composite_context *ctx)
+{
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+
+ /* receive result of named pipe open request on smb */
+ c->status = dcerpc_pipe_open_smb_recv(ctx);
+ if (!composite_is_ok(c)) return;
+
+ composite_done(c);
+}
+
+
+/*
+ Stage 2 of ncacn_np_smb: Open a named pipe after successful smb connection
+*/
+static void continue_smb_connect(struct composite_context *ctx)
+{
+ struct composite_context *open_ctx;
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+ struct pipe_np_smb_state *s = talloc_get_type(c->private_data,
+ struct pipe_np_smb_state);
+
+ /* receive result of smb connect request */
+ c->status = smb_composite_connect_recv(ctx, c);
+ if (!composite_is_ok(c)) return;
+
+ /* prepare named pipe open parameters */
+ s->tree = s->conn.out.tree;
+ s->io.pipe_name = s->io.binding->endpoint;
+
+ /* send named pipe open request */
+ open_ctx = dcerpc_pipe_open_smb_send(s->io.pipe, s->tree, s->io.pipe_name);
+ if (composite_nomem(open_ctx, c)) return;
+
+ composite_continue(c, open_ctx, continue_pipe_open_smb, c);
+}
+
+
+/*
+ Initiate async open of a rpc connection to a rpc pipe on SMB using
+ the binding structure to determine the endpoint and options
+*/
+static struct composite_context *dcerpc_pipe_connect_ncacn_np_smb_send(TALLOC_CTX *mem_ctx, struct dcerpc_pipe_connect *io, struct loadparm_context *lp_ctx)
+{
+ struct composite_context *c;
+ struct pipe_np_smb_state *s;
+ struct composite_context *conn_req;
+ struct smb_composite_connect *conn;
+
+ /* composite context allocation and setup */
+ c = composite_create(mem_ctx, io->pipe->conn->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct pipe_np_smb_state);
+ if (composite_nomem(s, c)) return c;
+ c->private_data = s;
+
+ s->io = *io;
+ conn = &s->conn;
+
+ /* prepare smb connection parameters: we're connecting to IPC$ share on
+ remote rpc server */
+ conn->in.dest_host = s->io.binding->host;
+ conn->in.dest_ports = lp_smb_ports(lp_ctx);
+ if (s->io.binding->target_hostname == NULL)
+ conn->in.called_name = "*SMBSERVER"; /* FIXME: This is invalid */
+ else
+ conn->in.called_name = s->io.binding->target_hostname;
+ conn->in.socket_options = lp_socket_options(lp_ctx);
+ conn->in.service = "IPC$";
+ conn->in.service_type = NULL;
+ conn->in.workgroup = lp_workgroup(lp_ctx);
+ conn->in.gensec_settings = lp_gensec_settings(conn, lp_ctx);
+ conn->in.iconv_convenience = lp_iconv_convenience(lp_ctx);
+
+ lp_smbcli_options(lp_ctx, &conn->in.options);
+ lp_smbcli_session_options(lp_ctx, &conn->in.session_options);
+
+ /*
+ * provide proper credentials - user supplied, but allow a
+ * fallback to anonymous if this is an schannel connection
+ * (might be NT4 not allowing machine logins at session
+ * setup).
+ */
+ s->conn.in.credentials = s->io.creds;
+ if (s->io.binding->flags & DCERPC_SCHANNEL) {
+ conn->in.fallback_to_anonymous = true;
+ } else {
+ conn->in.fallback_to_anonymous = false;
+ }
+
+ /* send smb connect request */
+ conn_req = smb_composite_connect_send(conn, s->io.pipe->conn,
+ s->io.resolve_ctx,
+ s->io.pipe->conn->event_ctx);
+ if (composite_nomem(conn_req, c)) return c;
+
+ composite_continue(c, conn_req, continue_smb_connect, c);
+ return c;
+}
+
+
+/*
+ Receive result of a rpc connection to a rpc pipe on SMB
+*/
+static NTSTATUS dcerpc_pipe_connect_ncacn_np_smb_recv(struct composite_context *c)
+{
+ NTSTATUS status = composite_wait(c);
+
+ talloc_free(c);
+ return status;
+}
+
+
+struct pipe_np_smb2_state {
+ struct smb2_tree *tree;
+ struct dcerpc_pipe_connect io;
+};
+
+
+/*
+ Stage 3 of ncacn_np_smb: Named pipe opened (or not)
+*/
+static void continue_pipe_open_smb2(struct composite_context *ctx)
+{
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+
+ /* receive result of named pipe open request on smb2 */
+ c->status = dcerpc_pipe_open_smb2_recv(ctx);
+ if (!composite_is_ok(c)) return;
+
+ composite_done(c);
+}
+
+
+/*
+ Stage 2 of ncacn_np_smb2: Open a named pipe after successful smb2 connection
+*/
+static void continue_smb2_connect(struct composite_context *ctx)
+{
+ struct composite_context *open_req;
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+ struct pipe_np_smb2_state *s = talloc_get_type(c->private_data,
+ struct pipe_np_smb2_state);
+
+ /* receive result of smb2 connect request */
+ c->status = smb2_connect_recv(ctx, c, &s->tree);
+ if (!composite_is_ok(c)) return;
+
+ /* prepare named pipe open parameters */
+ s->io.pipe_name = s->io.binding->endpoint;
+
+ /* send named pipe open request */
+ open_req = dcerpc_pipe_open_smb2_send(s->io.pipe, s->tree, s->io.pipe_name);
+ if (composite_nomem(open_req, c)) return;
+
+ composite_continue(c, open_req, continue_pipe_open_smb2, c);
+}
+
+
+/*
+ Initiate async open of a rpc connection request on SMB2 using
+ the binding structure to determine the endpoint and options
+*/
+static struct composite_context *dcerpc_pipe_connect_ncacn_np_smb2_send(
+ TALLOC_CTX *mem_ctx,
+ struct dcerpc_pipe_connect *io,
+ struct loadparm_context *lp_ctx)
+{
+ struct composite_context *c;
+ struct pipe_np_smb2_state *s;
+ struct composite_context *conn_req;
+ struct smbcli_options options;
+
+ /* composite context allocation and setup */
+ c = composite_create(mem_ctx, io->pipe->conn->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct pipe_np_smb2_state);
+ if (composite_nomem(s, c)) return c;
+ c->private_data = s;
+
+ s->io = *io;
+
+ /*
+ * provide proper credentials - user supplied or anonymous in case this is
+ * schannel connection
+ */
+ if (s->io.binding->flags & DCERPC_SCHANNEL) {
+ s->io.creds = cli_credentials_init(mem_ctx);
+ if (composite_nomem(s->io.creds, c)) return c;
+
+ cli_credentials_guess(s->io.creds, lp_ctx);
+ }
+
+ lp_smbcli_options(lp_ctx, &options);
+
+ /* send smb2 connect request */
+ conn_req = smb2_connect_send(mem_ctx, s->io.binding->host,
+ lp_parm_string_list(mem_ctx, lp_ctx, NULL, "smb2", "ports", NULL),
+ "IPC$",
+ s->io.resolve_ctx,
+ s->io.creds,
+ c->event_ctx,
+ &options,
+ lp_socket_options(lp_ctx),
+ lp_gensec_settings(mem_ctx, lp_ctx)
+ );
+ composite_continue(c, conn_req, continue_smb2_connect, c);
+ return c;
+}
+
+
+/*
+ Receive result of a rpc connection to a rpc pipe on SMB2
+*/
+static NTSTATUS dcerpc_pipe_connect_ncacn_np_smb2_recv(struct composite_context *c)
+{
+ NTSTATUS status = composite_wait(c);
+
+ talloc_free(c);
+ return status;
+}
+
+
+struct pipe_ip_tcp_state {
+ struct dcerpc_pipe_connect io;
+ const char *host;
+ const char *target_hostname;
+ uint32_t port;
+};
+
+
+/*
+ Stage 2 of ncacn_ip_tcp: rpc pipe opened (or not)
+*/
+static void continue_pipe_open_ncacn_ip_tcp(struct composite_context *ctx)
+{
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+
+ /* receive result of named pipe open request on tcp/ip */
+ c->status = dcerpc_pipe_open_tcp_recv(ctx);
+ if (!composite_is_ok(c)) return;
+
+ composite_done(c);
+}
+
+
+/*
+ Initiate async open of a rpc connection to a rpc pipe on TCP/IP using
+ the binding structure to determine the endpoint and options
+*/
+static struct composite_context* dcerpc_pipe_connect_ncacn_ip_tcp_send(TALLOC_CTX *mem_ctx,
+ struct dcerpc_pipe_connect *io)
+{
+ struct composite_context *c;
+ struct pipe_ip_tcp_state *s;
+ struct composite_context *pipe_req;
+
+ /* composite context allocation and setup */
+ c = composite_create(mem_ctx, io->pipe->conn->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct pipe_ip_tcp_state);
+ if (composite_nomem(s, c)) return c;
+ c->private_data = s;
+
+ /* store input parameters in state structure */
+ s->io = *io;
+ s->host = talloc_reference(c, io->binding->host);
+ s->target_hostname = talloc_reference(c, io->binding->target_hostname);
+ /* port number is a binding endpoint here */
+ s->port = atoi(io->binding->endpoint);
+
+ /* send pipe open request on tcp/ip */
+ pipe_req = dcerpc_pipe_open_tcp_send(s->io.pipe->conn, s->host, s->target_hostname,
+ s->port, io->resolve_ctx);
+ composite_continue(c, pipe_req, continue_pipe_open_ncacn_ip_tcp, c);
+ return c;
+}
+
+
+/*
+ Receive result of a rpc connection to a rpc pipe on TCP/IP
+*/
+static NTSTATUS dcerpc_pipe_connect_ncacn_ip_tcp_recv(struct composite_context *c)
+{
+ NTSTATUS status = composite_wait(c);
+
+ talloc_free(c);
+ return status;
+}
+
+
+struct pipe_unix_state {
+ struct dcerpc_pipe_connect io;
+ const char *path;
+};
+
+
+/*
+ Stage 2 of ncacn_unix: rpc pipe opened (or not)
+*/
+static void continue_pipe_open_ncacn_unix_stream(struct composite_context *ctx)
+{
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+
+ /* receive result of pipe open request on unix socket */
+ c->status = dcerpc_pipe_open_unix_stream_recv(ctx);
+ if (!composite_is_ok(c)) return;
+
+ composite_done(c);
+}
+
+
+/*
+ Initiate async open of a rpc connection to a rpc pipe on unix socket using
+ the binding structure to determine the endpoint and options
+*/
+static struct composite_context* dcerpc_pipe_connect_ncacn_unix_stream_send(TALLOC_CTX *mem_ctx,
+ struct dcerpc_pipe_connect *io)
+{
+ struct composite_context *c;
+ struct pipe_unix_state *s;
+ struct composite_context *pipe_req;
+
+ /* composite context allocation and setup */
+ c = composite_create(mem_ctx, io->pipe->conn->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct pipe_unix_state);
+ if (composite_nomem(s, c)) return c;
+ c->private_data = s;
+
+ /* prepare pipe open parameters and store them in state structure
+ also, verify whether biding endpoint is not null */
+ s->io = *io;
+
+ if (!io->binding->endpoint) {
+ DEBUG(0, ("Path to unix socket not specified\n"));
+ composite_error(c, NT_STATUS_INVALID_PARAMETER);
+ return c;
+ }
+
+ s->path = talloc_strdup(c, io->binding->endpoint); /* path is a binding endpoint here */
+ if (composite_nomem(s->path, c)) return c;
+
+ /* send pipe open request on unix socket */
+ pipe_req = dcerpc_pipe_open_unix_stream_send(s->io.pipe->conn, s->path);
+ composite_continue(c, pipe_req, continue_pipe_open_ncacn_unix_stream, c);
+ return c;
+}
+
+
+/*
+ Receive result of a rpc connection to a pipe on unix socket
+*/
+static NTSTATUS dcerpc_pipe_connect_ncacn_unix_stream_recv(struct composite_context *c)
+{
+ NTSTATUS status = composite_wait(c);
+
+ talloc_free(c);
+ return status;
+}
+
+
+struct pipe_ncalrpc_state {
+ struct dcerpc_pipe_connect io;
+};
+
+static NTSTATUS dcerpc_pipe_connect_ncalrpc_recv(struct composite_context *c);
+
+/*
+ Stage 2 of ncalrpc: rpc pipe opened (or not)
+*/
+static void continue_pipe_open_ncalrpc(struct composite_context *ctx)
+{
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+
+ /* receive result of pipe open request on ncalrpc */
+ c->status = dcerpc_pipe_connect_ncalrpc_recv(ctx);
+ if (!composite_is_ok(c)) return;
+
+ composite_done(c);
+}
+
+
+/*
+ Initiate async open of a rpc connection request on NCALRPC using
+ the binding structure to determine the endpoint and options
+*/
+static struct composite_context* dcerpc_pipe_connect_ncalrpc_send(TALLOC_CTX *mem_ctx,
+ struct dcerpc_pipe_connect *io, struct loadparm_context *lp_ctx)
+{
+ struct composite_context *c;
+ struct pipe_ncalrpc_state *s;
+ struct composite_context *pipe_req;
+
+ /* composite context allocation and setup */
+ c = composite_create(mem_ctx, io->pipe->conn->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct pipe_ncalrpc_state);
+ if (composite_nomem(s, c)) return c;
+ c->private_data = s;
+
+ /* store input parameters in state structure */
+ s->io = *io;
+
+ /* send pipe open request */
+ pipe_req = dcerpc_pipe_open_pipe_send(s->io.pipe->conn, lp_ncalrpc_dir(lp_ctx),
+ s->io.binding->endpoint);
+ composite_continue(c, pipe_req, continue_pipe_open_ncalrpc, c);
+ return c;
+}
+
+
+/*
+ Receive result of a rpc connection to a rpc pipe on NCALRPC
+*/
+static NTSTATUS dcerpc_pipe_connect_ncalrpc_recv(struct composite_context *c)
+{
+ NTSTATUS status = composite_wait(c);
+
+ talloc_free(c);
+ return status;
+}
+
+
+struct pipe_connect_state {
+ struct dcerpc_pipe *pipe;
+ struct dcerpc_binding *binding;
+ const struct ndr_interface_table *table;
+ struct cli_credentials *credentials;
+ struct loadparm_context *lp_ctx;
+};
+
+
+static void continue_map_binding(struct composite_context *ctx);
+static void continue_connect(struct composite_context *c, struct pipe_connect_state *s);
+static void continue_pipe_connect_ncacn_np_smb2(struct composite_context *ctx);
+static void continue_pipe_connect_ncacn_np_smb(struct composite_context *ctx);
+static void continue_pipe_connect_ncacn_ip_tcp(struct composite_context *ctx);
+static void continue_pipe_connect_ncacn_unix(struct composite_context *ctx);
+static void continue_pipe_connect_ncalrpc(struct composite_context *ctx);
+static void continue_pipe_connect(struct composite_context *c, struct pipe_connect_state *s);
+static void continue_pipe_auth(struct composite_context *ctx);
+
+
+/*
+ Stage 2 of pipe_connect_b: Receive result of endpoint mapping
+*/
+static void continue_map_binding(struct composite_context *ctx)
+{
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+ struct pipe_connect_state *s = talloc_get_type(c->private_data,
+ struct pipe_connect_state);
+
+ c->status = dcerpc_epm_map_binding_recv(ctx);
+ if (!composite_is_ok(c)) return;
+
+ DEBUG(2,("Mapped to DCERPC endpoint %s\n", s->binding->endpoint));
+
+ continue_connect(c, s);
+}
+
+
+/*
+ Stage 2 of pipe_connect_b: Continue connection after endpoint is known
+*/
+static void continue_connect(struct composite_context *c, struct pipe_connect_state *s)
+{
+ struct dcerpc_pipe_connect pc;
+
+ /* potential exits to another stage by sending an async request */
+ struct composite_context *ncacn_np_smb2_req;
+ struct composite_context *ncacn_np_smb_req;
+ struct composite_context *ncacn_ip_tcp_req;
+ struct composite_context *ncacn_unix_req;
+ struct composite_context *ncalrpc_req;
+
+ /* dcerpc pipe connect input parameters */
+ pc.pipe = s->pipe;
+ pc.binding = s->binding;
+ pc.interface = s->table;
+ pc.creds = s->credentials;
+ pc.resolve_ctx = lp_resolve_context(s->lp_ctx);
+
+ /* connect dcerpc pipe depending on required transport */
+ switch (s->binding->transport) {
+ case NCACN_NP:
+ if (pc.binding->flags & DCERPC_SMB2) {
+ /* new varient of SMB a.k.a. SMB2 */
+ ncacn_np_smb2_req = dcerpc_pipe_connect_ncacn_np_smb2_send(c, &pc, s->lp_ctx);
+ composite_continue(c, ncacn_np_smb2_req, continue_pipe_connect_ncacn_np_smb2, c);
+ return;
+
+ } else {
+ /* good old ordinary SMB */
+ ncacn_np_smb_req = dcerpc_pipe_connect_ncacn_np_smb_send(c, &pc, s->lp_ctx);
+ composite_continue(c, ncacn_np_smb_req, continue_pipe_connect_ncacn_np_smb, c);
+ return;
+ }
+ break;
+
+ case NCACN_IP_TCP:
+ ncacn_ip_tcp_req = dcerpc_pipe_connect_ncacn_ip_tcp_send(c, &pc);
+ composite_continue(c, ncacn_ip_tcp_req, continue_pipe_connect_ncacn_ip_tcp, c);
+ return;
+
+ case NCACN_UNIX_STREAM:
+ ncacn_unix_req = dcerpc_pipe_connect_ncacn_unix_stream_send(c, &pc);
+ composite_continue(c, ncacn_unix_req, continue_pipe_connect_ncacn_unix, c);
+ return;
+
+ case NCALRPC:
+ ncalrpc_req = dcerpc_pipe_connect_ncalrpc_send(c, &pc, s->lp_ctx);
+ composite_continue(c, ncalrpc_req, continue_pipe_connect_ncalrpc, c);
+ return;
+
+ default:
+ /* looks like a transport we don't support now */
+ composite_error(c, NT_STATUS_NOT_SUPPORTED);
+ }
+}
+
+
+/*
+ Stage 3 of pipe_connect_b: Receive result of pipe connect request on
+ named pipe on smb2
+*/
+static void continue_pipe_connect_ncacn_np_smb2(struct composite_context *ctx)
+{
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+ struct pipe_connect_state *s = talloc_get_type(c->private_data,
+ struct pipe_connect_state);
+
+ c->status = dcerpc_pipe_connect_ncacn_np_smb2_recv(ctx);
+ if (!composite_is_ok(c)) return;
+
+ continue_pipe_connect(c, s);
+}
+
+
+/*
+ Stage 3 of pipe_connect_b: Receive result of pipe connect request on
+ named pipe on smb
+*/
+static void continue_pipe_connect_ncacn_np_smb(struct composite_context *ctx)
+{
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+ struct pipe_connect_state *s = talloc_get_type(c->private_data,
+ struct pipe_connect_state);
+
+ c->status = dcerpc_pipe_connect_ncacn_np_smb_recv(ctx);
+ if (!composite_is_ok(c)) return;
+
+ continue_pipe_connect(c, s);
+}
+
+
+/*
+ Stage 3 of pipe_connect_b: Receive result of pipe connect request on tcp/ip
+*/
+static void continue_pipe_connect_ncacn_ip_tcp(struct composite_context *ctx)
+{
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+ struct pipe_connect_state *s = talloc_get_type(c->private_data,
+ struct pipe_connect_state);
+
+ c->status = dcerpc_pipe_connect_ncacn_ip_tcp_recv(ctx);
+ if (!composite_is_ok(c)) return;
+
+ continue_pipe_connect(c, s);
+}
+
+
+/*
+ Stage 3 of pipe_connect_b: Receive result of pipe connect request on unix socket
+*/
+static void continue_pipe_connect_ncacn_unix(struct composite_context *ctx)
+{
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+ struct pipe_connect_state *s = talloc_get_type(c->private_data,
+ struct pipe_connect_state);
+
+ c->status = dcerpc_pipe_connect_ncacn_unix_stream_recv(ctx);
+ if (!composite_is_ok(c)) return;
+
+ continue_pipe_connect(c, s);
+}
+
+
+/*
+ Stage 3 of pipe_connect_b: Receive result of pipe connect request on local rpc
+*/
+static void continue_pipe_connect_ncalrpc(struct composite_context *ctx)
+{
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+ struct pipe_connect_state *s = talloc_get_type(c->private_data,
+ struct pipe_connect_state);
+
+ c->status = dcerpc_pipe_connect_ncalrpc_recv(ctx);
+ if (!composite_is_ok(c)) return;
+
+ continue_pipe_connect(c, s);
+}
+
+
+/*
+ Stage 4 of pipe_connect_b: Start an authentication on connected dcerpc pipe
+ depending on credentials and binding flags passed.
+*/
+static void continue_pipe_connect(struct composite_context *c, struct pipe_connect_state *s)
+{
+ struct composite_context *auth_bind_req;
+
+ s->pipe->binding = s->binding;
+ if (!talloc_reference(s->pipe, s->binding)) {
+ composite_error(c, NT_STATUS_NO_MEMORY);
+ return;
+ }
+
+ auth_bind_req = dcerpc_pipe_auth_send(s->pipe, s->binding, s->table,
+ s->credentials, s->lp_ctx);
+ composite_continue(c, auth_bind_req, continue_pipe_auth, c);
+}
+
+
+/*
+ Stage 5 of pipe_connect_b: Receive result of pipe authentication request
+ and say if all went ok
+*/
+static void continue_pipe_auth(struct composite_context *ctx)
+{
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+ struct pipe_connect_state *s = talloc_get_type(c->private_data, struct pipe_connect_state);
+
+ c->status = dcerpc_pipe_auth_recv(ctx, s, &s->pipe);
+ if (!composite_is_ok(c)) return;
+
+ composite_done(c);
+}
+
+
+/*
+ handle timeouts of a dcerpc connect
+*/
+static void dcerpc_connect_timeout_handler(struct tevent_context *ev, struct tevent_timer *te,
+ struct timeval t, void *private_data)
+{
+ struct composite_context *c = talloc_get_type(private_data, struct composite_context);
+ composite_error(c, NT_STATUS_IO_TIMEOUT);
+}
+
+/*
+ start a request to open a rpc connection to a rpc pipe, using
+ specified binding structure to determine the endpoint and options
+*/
+_PUBLIC_ struct composite_context* dcerpc_pipe_connect_b_send(TALLOC_CTX *parent_ctx,
+ struct dcerpc_binding *binding,
+ const struct ndr_interface_table *table,
+ struct cli_credentials *credentials,
+ struct tevent_context *ev,
+ struct loadparm_context *lp_ctx)
+{
+ struct composite_context *c;
+ struct pipe_connect_state *s;
+ struct tevent_context *new_ev = NULL;
+
+ /* composite context allocation and setup */
+ c = composite_create(parent_ctx, ev);
+ if (c == NULL) {
+ talloc_free(new_ev);
+ return NULL;
+ }
+ talloc_steal(c, new_ev);
+
+ s = talloc_zero(c, struct pipe_connect_state);
+ if (composite_nomem(s, c)) return c;
+ c->private_data = s;
+
+ /* initialise dcerpc pipe structure */
+ s->pipe = dcerpc_pipe_init(c, ev, lp_iconv_convenience(lp_ctx));
+ if (composite_nomem(s->pipe, c)) return c;
+
+ if (DEBUGLEVEL >= 10)
+ s->pipe->conn->packet_log_dir = lp_lockdir(lp_ctx);
+
+ /* store parameters in state structure */
+ s->binding = binding;
+ s->table = table;
+ s->credentials = credentials;
+ s->lp_ctx = lp_ctx;
+
+ event_add_timed(c->event_ctx, c,
+ timeval_current_ofs(DCERPC_REQUEST_TIMEOUT, 0),
+ dcerpc_connect_timeout_handler, c);
+
+ switch (s->binding->transport) {
+ case NCA_UNKNOWN: {
+ struct composite_context *binding_req;
+ binding_req = dcerpc_epm_map_binding_send(c, s->binding, s->table,
+ s->pipe->conn->event_ctx,
+ s->lp_ctx);
+ composite_continue(c, binding_req, continue_map_binding, c);
+ return c;
+ }
+
+ case NCACN_NP:
+ case NCACN_IP_TCP:
+ case NCALRPC:
+ if (!s->binding->endpoint) {
+ struct composite_context *binding_req;
+ binding_req = dcerpc_epm_map_binding_send(c, s->binding, s->table,
+ s->pipe->conn->event_ctx,
+ s->lp_ctx);
+ composite_continue(c, binding_req, continue_map_binding, c);
+ return c;
+ }
+
+ default:
+ break;
+ }
+
+ continue_connect(c, s);
+ return c;
+}
+
+
+/*
+ receive result of a request to open a rpc connection to a rpc pipe
+*/
+_PUBLIC_ NTSTATUS dcerpc_pipe_connect_b_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
+ struct dcerpc_pipe **p)
+{
+ NTSTATUS status;
+ struct pipe_connect_state *s;
+
+ status = composite_wait(c);
+
+ if (NT_STATUS_IS_OK(status)) {
+ s = talloc_get_type(c->private_data, struct pipe_connect_state);
+ talloc_steal(mem_ctx, s->pipe);
+ *p = s->pipe;
+ }
+ talloc_free(c);
+ return status;
+}
+
+
+/*
+ open a rpc connection to a rpc pipe, using the specified
+ binding structure to determine the endpoint and options - sync version
+*/
+_PUBLIC_ NTSTATUS dcerpc_pipe_connect_b(TALLOC_CTX *parent_ctx,
+ struct dcerpc_pipe **pp,
+ struct dcerpc_binding *binding,
+ const struct ndr_interface_table *table,
+ struct cli_credentials *credentials,
+ struct tevent_context *ev,
+ struct loadparm_context *lp_ctx)
+{
+ struct composite_context *c;
+
+ c = dcerpc_pipe_connect_b_send(parent_ctx, binding, table,
+ credentials, ev, lp_ctx);
+ return dcerpc_pipe_connect_b_recv(c, parent_ctx, pp);
+}
+
+
+struct pipe_conn_state {
+ struct dcerpc_pipe *pipe;
+};
+
+
+static void continue_pipe_connect_b(struct composite_context *ctx);
+
+
+/*
+ Initiate rpc connection to a rpc pipe, using the specified string
+ binding to determine the endpoint and options.
+ The string is to be parsed to a binding structure first.
+*/
+_PUBLIC_ struct composite_context* dcerpc_pipe_connect_send(TALLOC_CTX *parent_ctx,
+ const char *binding,
+ const struct ndr_interface_table *table,
+ struct cli_credentials *credentials,
+ struct tevent_context *ev, struct loadparm_context *lp_ctx)
+{
+ struct composite_context *c;
+ struct pipe_conn_state *s;
+ struct dcerpc_binding *b;
+ struct composite_context *pipe_conn_req;
+
+ /* composite context allocation and setup */
+ c = composite_create(parent_ctx, ev);
+ if (c == NULL) {
+ return NULL;
+ }
+
+ s = talloc_zero(c, struct pipe_conn_state);
+ if (composite_nomem(s, c)) return c;
+ c->private_data = s;
+
+ /* parse binding string to the structure */
+ c->status = dcerpc_parse_binding(c, binding, &b);
+ if (!NT_STATUS_IS_OK(c->status)) {
+ DEBUG(0, ("Failed to parse dcerpc binding '%s'\n", binding));
+ composite_error(c, c->status);
+ return c;
+ }
+
+ DEBUG(3, ("Using binding %s\n", dcerpc_binding_string(c, b)));
+
+ /*
+ start connecting to a rpc pipe after binding structure
+ is established
+ */
+ pipe_conn_req = dcerpc_pipe_connect_b_send(c, b, table,
+ credentials, ev, lp_ctx);
+ composite_continue(c, pipe_conn_req, continue_pipe_connect_b, c);
+ return c;
+}
+
+
+/*
+ Stage 2 of pipe_connect: Receive result of actual pipe connect request
+ and say if we're done ok
+*/
+static void continue_pipe_connect_b(struct composite_context *ctx)
+{
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+ struct pipe_conn_state *s = talloc_get_type(c->private_data,
+ struct pipe_conn_state);
+
+ c->status = dcerpc_pipe_connect_b_recv(ctx, c, &s->pipe);
+ talloc_steal(s, s->pipe);
+ if (!composite_is_ok(c)) return;
+
+ composite_done(c);
+}
+
+
+/*
+ Receive result of pipe connect (using binding string) request
+ and return connected pipe structure.
+*/
+NTSTATUS dcerpc_pipe_connect_recv(struct composite_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct dcerpc_pipe **pp)
+{
+ NTSTATUS status;
+ struct pipe_conn_state *s;
+
+ status = composite_wait(c);
+ if (NT_STATUS_IS_OK(status)) {
+ s = talloc_get_type(c->private_data, struct pipe_conn_state);
+ *pp = talloc_steal(mem_ctx, s->pipe);
+ }
+ talloc_free(c);
+ return status;
+}
+
+
+/*
+ Open a rpc connection to a rpc pipe, using the specified string
+ binding to determine the endpoint and options - sync version
+*/
+_PUBLIC_ NTSTATUS dcerpc_pipe_connect(TALLOC_CTX *parent_ctx,
+ struct dcerpc_pipe **pp,
+ const char *binding,
+ const struct ndr_interface_table *table,
+ struct cli_credentials *credentials,
+ struct tevent_context *ev,
+ struct loadparm_context *lp_ctx)
+{
+ struct composite_context *c;
+ c = dcerpc_pipe_connect_send(parent_ctx, binding,
+ table, credentials, ev, lp_ctx);
+ return dcerpc_pipe_connect_recv(c, parent_ctx, pp);
+}
+
diff --git a/source4/librpc/rpc/dcerpc_schannel.c b/source4/librpc/rpc/dcerpc_schannel.c
new file mode 100644
index 0000000000..e3add82bf2
--- /dev/null
+++ b/source4/librpc/rpc/dcerpc_schannel.c
@@ -0,0 +1,413 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ dcerpc schannel operations
+
+ Copyright (C) Andrew Tridgell 2004
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "auth/auth.h"
+#include "libcli/composite/composite.h"
+#include "libcli/auth/libcli_auth.h"
+#include "librpc/gen_ndr/ndr_netlogon.h"
+#include "librpc/gen_ndr/ndr_netlogon_c.h"
+#include "auth/credentials/credentials.h"
+#include "librpc/rpc/dcerpc_proto.h"
+#include "param/param.h"
+
+struct schannel_key_state {
+ struct dcerpc_pipe *pipe;
+ struct dcerpc_pipe *pipe2;
+ struct dcerpc_binding *binding;
+ struct cli_credentials *credentials;
+ struct creds_CredentialState *creds;
+ uint32_t negotiate_flags;
+ struct netr_Credential credentials1;
+ struct netr_Credential credentials2;
+ struct netr_Credential credentials3;
+ struct netr_ServerReqChallenge r;
+ struct netr_ServerAuthenticate2 a;
+ const struct samr_Password *mach_pwd;
+};
+
+
+static void continue_secondary_connection(struct composite_context *ctx);
+static void continue_bind_auth_none(struct composite_context *ctx);
+static void continue_srv_challenge(struct rpc_request *req);
+static void continue_srv_auth2(struct rpc_request *req);
+
+
+/*
+ Stage 2 of schannel_key: Receive endpoint mapping and request secondary
+ rpc connection
+*/
+static void continue_epm_map_binding(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct schannel_key_state *s;
+ struct composite_context *sec_conn_req;
+
+ c = talloc_get_type(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct schannel_key_state);
+
+ /* receive endpoint mapping */
+ c->status = dcerpc_epm_map_binding_recv(ctx);
+ if (!NT_STATUS_IS_OK(c->status)) {
+ DEBUG(0,("Failed to map DCERPC/TCP NCACN_NP pipe for '%s' - %s\n",
+ NDR_NETLOGON_UUID, nt_errstr(c->status)));
+ composite_error(c, c->status);
+ return;
+ }
+
+ /* send a request for secondary rpc connection */
+ sec_conn_req = dcerpc_secondary_connection_send(s->pipe,
+ s->binding);
+ if (composite_nomem(sec_conn_req, c)) return;
+
+ composite_continue(c, sec_conn_req, continue_secondary_connection, c);
+}
+
+
+/*
+ Stage 3 of schannel_key: Receive secondary rpc connection and perform
+ non-authenticated bind request
+*/
+static void continue_secondary_connection(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct schannel_key_state *s;
+ struct composite_context *auth_none_req;
+
+ c = talloc_get_type(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct schannel_key_state);
+
+ /* receive secondary rpc connection */
+ c->status = dcerpc_secondary_connection_recv(ctx, &s->pipe2);
+ if (!composite_is_ok(c)) return;
+
+ talloc_steal(s, s->pipe2);
+
+ /* initiate a non-authenticated bind */
+ auth_none_req = dcerpc_bind_auth_none_send(c, s->pipe2, &ndr_table_netlogon);
+ if (composite_nomem(auth_none_req, c)) return;
+
+ composite_continue(c, auth_none_req, continue_bind_auth_none, c);
+}
+
+
+/*
+ Stage 4 of schannel_key: Receive non-authenticated bind and get
+ a netlogon challenge
+*/
+static void continue_bind_auth_none(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct schannel_key_state *s;
+ struct rpc_request *srv_challenge_req;
+
+ c = talloc_get_type(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct schannel_key_state);
+
+ /* receive result of non-authenticated bind request */
+ c->status = dcerpc_bind_auth_none_recv(ctx);
+ if (!composite_is_ok(c)) return;
+
+ /* prepare a challenge request */
+ s->r.in.server_name = talloc_asprintf(c, "\\\\%s", dcerpc_server_name(s->pipe));
+ if (composite_nomem(s->r.in.server_name, c)) return;
+ s->r.in.computer_name = cli_credentials_get_workstation(s->credentials);
+ s->r.in.credentials = &s->credentials1;
+ s->r.out.return_credentials = &s->credentials2;
+
+ generate_random_buffer(s->credentials1.data, sizeof(s->credentials1.data));
+
+ /*
+ request a netlogon challenge - a rpc request over opened secondary pipe
+ */
+ srv_challenge_req = dcerpc_netr_ServerReqChallenge_send(s->pipe2, c, &s->r);
+ if (composite_nomem(srv_challenge_req, c)) return;
+
+ composite_continue_rpc(c, srv_challenge_req, continue_srv_challenge, c);
+}
+
+
+/*
+ Stage 5 of schannel_key: Receive a challenge and perform authentication
+ on the netlogon pipe
+*/
+static void continue_srv_challenge(struct rpc_request *req)
+{
+ struct composite_context *c;
+ struct schannel_key_state *s;
+ struct rpc_request *srv_auth2_req;
+
+ c = talloc_get_type(req->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct schannel_key_state);
+
+ /* receive rpc request result - netlogon challenge */
+ c->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(c)) return;
+
+ /* prepare credentials for auth2 request */
+ s->mach_pwd = cli_credentials_get_nt_hash(s->credentials, c);
+
+ creds_client_init(s->creds, &s->credentials1, &s->credentials2,
+ s->mach_pwd, &s->credentials3, s->negotiate_flags);
+
+ /* auth2 request arguments */
+ s->a.in.server_name = s->r.in.server_name;
+ s->a.in.account_name = cli_credentials_get_username(s->credentials);
+ s->a.in.secure_channel_type =
+ cli_credentials_get_secure_channel_type(s->credentials);
+ s->a.in.computer_name = cli_credentials_get_workstation(s->credentials);
+ s->a.in.negotiate_flags = &s->negotiate_flags;
+ s->a.in.credentials = &s->credentials3;
+ s->a.out.negotiate_flags = &s->negotiate_flags;
+ s->a.out.return_credentials = &s->credentials3;
+
+ /*
+ authenticate on the netlogon pipe - a rpc request over secondary pipe
+ */
+ srv_auth2_req = dcerpc_netr_ServerAuthenticate2_send(s->pipe2, c, &s->a);
+ if (composite_nomem(srv_auth2_req, c)) return;
+
+ composite_continue_rpc(c, srv_auth2_req, continue_srv_auth2, c);
+}
+
+
+/*
+ Stage 6 of schannel_key: Receive authentication request result and verify
+ received credentials
+*/
+static void continue_srv_auth2(struct rpc_request *req)
+{
+ struct composite_context *c;
+ struct schannel_key_state *s;
+
+ c = talloc_get_type(req->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct schannel_key_state);
+
+ /* receive rpc request result - auth2 credentials */
+ c->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(c)) return;
+
+ /* verify credentials */
+ if (!creds_client_check(s->creds, s->a.out.return_credentials)) {
+ composite_error(c, NT_STATUS_UNSUCCESSFUL);
+ return;
+ }
+
+ /* setup current netlogon credentials */
+ cli_credentials_set_netlogon_creds(s->credentials, s->creds);
+
+ composite_done(c);
+}
+
+
+/*
+ Initiate establishing a schannel key using netlogon challenge
+ on a secondary pipe
+*/
+struct composite_context *dcerpc_schannel_key_send(TALLOC_CTX *mem_ctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *credentials,
+ struct loadparm_context *lp_ctx)
+{
+ struct composite_context *c;
+ struct schannel_key_state *s;
+ struct composite_context *epm_map_req;
+
+ /* composite context allocation and setup */
+ c = composite_create(mem_ctx, p->conn->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct schannel_key_state);
+ if (composite_nomem(s, c)) return c;
+ c->private_data = s;
+
+ /* store parameters in the state structure */
+ s->pipe = p;
+ s->credentials = credentials;
+
+ /* allocate credentials */
+ s->creds = talloc(c, struct creds_CredentialState);
+ if (composite_nomem(s->creds, c)) return c;
+
+ /* type of authentication depends on schannel type */
+ if (s->pipe->conn->flags & DCERPC_SCHANNEL_128) {
+ s->negotiate_flags = NETLOGON_NEG_AUTH2_ADS_FLAGS;
+ } else {
+ s->negotiate_flags = NETLOGON_NEG_AUTH2_FLAGS;
+ }
+
+ /* allocate binding structure */
+ s->binding = talloc(c, struct dcerpc_binding);
+ if (composite_nomem(s->binding, c)) return c;
+
+ *s->binding = *s->pipe->binding;
+
+ /* request the netlogon endpoint mapping */
+ epm_map_req = dcerpc_epm_map_binding_send(c, s->binding,
+ &ndr_table_netlogon,
+ s->pipe->conn->event_ctx,
+ lp_ctx);
+ if (composite_nomem(epm_map_req, c)) return c;
+
+ composite_continue(c, epm_map_req, continue_epm_map_binding, c);
+ return c;
+}
+
+
+/*
+ Receive result of schannel key request
+ */
+NTSTATUS dcerpc_schannel_key_recv(struct composite_context *c)
+{
+ NTSTATUS status = composite_wait(c);
+
+ talloc_free(c);
+ return status;
+}
+
+
+struct auth_schannel_state {
+ struct dcerpc_pipe *pipe;
+ struct cli_credentials *credentials;
+ const struct ndr_interface_table *table;
+ struct loadparm_context *lp_ctx;
+ uint8_t auth_level;
+};
+
+
+static void continue_bind_auth(struct composite_context *ctx);
+
+
+/*
+ Stage 2 of auth_schannel: Receive schannel key and intitiate an
+ authenticated bind using received credentials
+ */
+static void continue_schannel_key(struct composite_context *ctx)
+{
+ struct composite_context *auth_req;
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+ struct auth_schannel_state *s = talloc_get_type(c->private_data,
+ struct auth_schannel_state);
+
+ /* receive schannel key */
+ c->status = dcerpc_schannel_key_recv(ctx);
+ if (!composite_is_ok(c)) {
+ DEBUG(1, ("Failed to setup credentials for account %s: %s\n",
+ cli_credentials_get_username(s->credentials), nt_errstr(c->status)));
+ return;
+ }
+
+ /* send bind auth request with received creds */
+ auth_req = dcerpc_bind_auth_send(c, s->pipe, s->table, s->credentials,
+ lp_gensec_settings(c, s->lp_ctx),
+ DCERPC_AUTH_TYPE_SCHANNEL, s->auth_level,
+ NULL);
+ if (composite_nomem(auth_req, c)) return;
+
+ composite_continue(c, auth_req, continue_bind_auth, c);
+}
+
+
+/*
+ Stage 3 of auth_schannel: Receivce result of authenticated bind
+ and say if we're done ok.
+*/
+static void continue_bind_auth(struct composite_context *ctx)
+{
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+
+ c->status = dcerpc_bind_auth_recv(ctx);
+ if (!composite_is_ok(c)) return;
+
+ composite_done(c);
+}
+
+
+/*
+ Initiate schannel authentication request
+*/
+struct composite_context *dcerpc_bind_auth_schannel_send(TALLOC_CTX *tmp_ctx,
+ struct dcerpc_pipe *p,
+ const struct ndr_interface_table *table,
+ struct cli_credentials *credentials,
+ struct loadparm_context *lp_ctx,
+ uint8_t auth_level)
+{
+ struct composite_context *c;
+ struct auth_schannel_state *s;
+ struct composite_context *schan_key_req;
+
+ /* composite context allocation and setup */
+ c = composite_create(tmp_ctx, p->conn->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct auth_schannel_state);
+ if (composite_nomem(s, c)) return c;
+ c->private_data = s;
+
+ /* store parameters in the state structure */
+ s->pipe = p;
+ s->credentials = credentials;
+ s->table = table;
+ s->auth_level = auth_level;
+ s->lp_ctx = lp_ctx;
+
+ /* start getting schannel key first */
+ schan_key_req = dcerpc_schannel_key_send(c, p, credentials, lp_ctx);
+ if (composite_nomem(schan_key_req, c)) return c;
+
+ composite_continue(c, schan_key_req, continue_schannel_key, c);
+ return c;
+}
+
+
+/*
+ Receive result of schannel authentication request
+*/
+NTSTATUS dcerpc_bind_auth_schannel_recv(struct composite_context *c)
+{
+ NTSTATUS status = composite_wait(c);
+
+ talloc_free(c);
+ return status;
+}
+
+
+/*
+ Perform schannel authenticated bind - sync version
+ */
+_PUBLIC_ NTSTATUS dcerpc_bind_auth_schannel(TALLOC_CTX *tmp_ctx,
+ struct dcerpc_pipe *p,
+ const struct ndr_interface_table *table,
+ struct cli_credentials *credentials,
+ struct loadparm_context *lp_ctx,
+ uint8_t auth_level)
+{
+ struct composite_context *c;
+
+ c = dcerpc_bind_auth_schannel_send(tmp_ctx, p, table, credentials, lp_ctx,
+ auth_level);
+ return dcerpc_bind_auth_schannel_recv(c);
+}
diff --git a/source4/librpc/rpc/dcerpc_secondary.c b/source4/librpc/rpc/dcerpc_secondary.c
new file mode 100644
index 0000000000..1d76c65f40
--- /dev/null
+++ b/source4/librpc/rpc/dcerpc_secondary.c
@@ -0,0 +1,336 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ dcerpc connect functions
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Jelmer Vernooij 2004
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2007
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+
+#include "includes.h"
+#include "libcli/composite/composite.h"
+#include "lib/events/events.h"
+#include "librpc/rpc/dcerpc.h"
+#include "librpc/rpc/dcerpc_proto.h"
+#include "auth/credentials/credentials.h"
+#include "param/param.h"
+#include "libcli/resolve/resolve.h"
+#include "lib/socket/socket.h"
+
+struct sec_conn_state {
+ struct dcerpc_pipe *pipe;
+ struct dcerpc_pipe *pipe2;
+ struct dcerpc_binding *binding;
+ struct smbcli_tree *tree;
+ struct socket_address *peer_addr;
+};
+
+
+static void continue_open_smb(struct composite_context *ctx);
+static void continue_open_tcp(struct composite_context *ctx);
+static void continue_open_pipe(struct composite_context *ctx);
+static void continue_pipe_open(struct composite_context *c);
+
+
+/*
+ Send request to create a secondary dcerpc connection from a primary
+ connection
+*/
+_PUBLIC_ struct composite_context* dcerpc_secondary_connection_send(struct dcerpc_pipe *p,
+ struct dcerpc_binding *b)
+{
+ struct composite_context *c;
+ struct sec_conn_state *s;
+ struct composite_context *pipe_smb_req;
+ struct composite_context *pipe_tcp_req;
+ struct composite_context *pipe_ncalrpc_req;
+
+ /* composite context allocation and setup */
+ c = composite_create(p, p->conn->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct sec_conn_state);
+ if (composite_nomem(s, c)) return c;
+ c->private_data = s;
+
+ s->pipe = p;
+ s->binding = b;
+
+ /* initialise second dcerpc pipe based on primary pipe's event context */
+ s->pipe2 = dcerpc_pipe_init(c, s->pipe->conn->event_ctx, s->pipe->conn->iconv_convenience);
+ if (composite_nomem(s->pipe2, c)) return c;
+
+ if (DEBUGLEVEL >= 10)
+ s->pipe2->conn->packet_log_dir = s->pipe->conn->packet_log_dir;
+
+ /* open second dcerpc pipe using the same transport as for primary pipe */
+ switch (s->pipe->conn->transport.transport) {
+ case NCACN_NP:
+ /* get smb tree of primary dcerpc pipe opened on smb */
+ s->tree = dcerpc_smb_tree(s->pipe->conn);
+ if (!s->tree) {
+ composite_error(c, NT_STATUS_INVALID_PARAMETER);
+ return c;
+ }
+
+ pipe_smb_req = dcerpc_pipe_open_smb_send(s->pipe2, s->tree,
+ s->binding->endpoint);
+ composite_continue(c, pipe_smb_req, continue_open_smb, c);
+ return c;
+
+ case NCACN_IP_TCP:
+ s->peer_addr = dcerpc_socket_peer_addr(s->pipe->conn, s);
+ if (!s->peer_addr) {
+ composite_error(c, NT_STATUS_INVALID_PARAMETER);
+ return c;
+ }
+
+ pipe_tcp_req = dcerpc_pipe_open_tcp_send(s->pipe2->conn,
+ s->peer_addr->addr,
+ s->binding->target_hostname,
+ atoi(s->binding->endpoint),
+ resolve_context_init(s));
+ composite_continue(c, pipe_tcp_req, continue_open_tcp, c);
+ return c;
+
+ case NCALRPC:
+ case NCACN_UNIX_STREAM:
+ pipe_ncalrpc_req = dcerpc_pipe_open_unix_stream_send(s->pipe2->conn,
+ dcerpc_unix_socket_path(s->pipe->conn));
+ composite_continue(c, pipe_ncalrpc_req, continue_open_pipe, c);
+ return c;
+
+ default:
+ /* looks like a transport we don't support */
+ composite_error(c, NT_STATUS_NOT_SUPPORTED);
+ }
+
+ return c;
+}
+
+
+/*
+ Stage 2 of secondary_connection: Receive result of pipe open request on smb
+*/
+static void continue_open_smb(struct composite_context *ctx)
+{
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+
+ c->status = dcerpc_pipe_open_smb_recv(ctx);
+ if (!composite_is_ok(c)) return;
+
+ continue_pipe_open(c);
+}
+
+
+/*
+ Stage 2 of secondary_connection: Receive result of pipe open request on tcp/ip
+*/
+static void continue_open_tcp(struct composite_context *ctx)
+{
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+
+ c->status = dcerpc_pipe_open_tcp_recv(ctx);
+ if (!composite_is_ok(c)) return;
+
+ continue_pipe_open(c);
+}
+
+
+/*
+ Stage 2 of secondary_connection: Receive result of pipe open request on ncalrpc
+*/
+static void continue_open_pipe(struct composite_context *ctx)
+{
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+
+ c->status = dcerpc_pipe_open_pipe_recv(ctx);
+ if (!composite_is_ok(c)) return;
+
+ continue_pipe_open(c);
+}
+
+
+/*
+ Stage 3 of secondary_connection: Get binding data and flags from primary pipe
+ and say if we're done ok.
+*/
+static void continue_pipe_open(struct composite_context *c)
+{
+ struct sec_conn_state *s;
+
+ s = talloc_get_type(c->private_data, struct sec_conn_state);
+
+ s->pipe2->conn->flags = s->pipe->conn->flags;
+ s->pipe2->binding = s->binding;
+ if (!talloc_reference(s->pipe2, s->binding)) {
+ composite_error(c, NT_STATUS_NO_MEMORY);
+ return;
+ }
+
+ composite_done(c);
+}
+
+
+/*
+ Receive result of secondary rpc connection request and return
+ second dcerpc pipe.
+*/
+_PUBLIC_ NTSTATUS dcerpc_secondary_connection_recv(struct composite_context *c,
+ struct dcerpc_pipe **p2)
+{
+ NTSTATUS status = composite_wait(c);
+ struct sec_conn_state *s;
+
+ s = talloc_get_type(c->private_data, struct sec_conn_state);
+
+ if (NT_STATUS_IS_OK(status)) {
+ *p2 = talloc_steal(s->pipe, s->pipe2);
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+/*
+ Create a secondary dcerpc connection from a primary connection
+ - sync version
+
+ If the primary is a SMB connection then the secondary connection
+ will be on the same SMB connection, but using a new fnum
+*/
+_PUBLIC_ NTSTATUS dcerpc_secondary_connection(struct dcerpc_pipe *p,
+ struct dcerpc_pipe **p2,
+ struct dcerpc_binding *b)
+{
+ struct composite_context *c;
+
+ c = dcerpc_secondary_connection_send(p, b);
+ return dcerpc_secondary_connection_recv(c, p2);
+}
+
+/*
+ Create a secondary DCERPC connection, then bind (and possibly
+ authenticate) using the supplied credentials.
+
+ This creates a second connection, to the same host (and on ncacn_np on the same connection) as the first
+*/
+struct sec_auth_conn_state {
+ struct dcerpc_pipe *pipe2;
+ struct dcerpc_binding *binding;
+ const struct ndr_interface_table *table;
+ struct cli_credentials *credentials;
+ struct composite_context *ctx;
+ struct loadparm_context *lp_ctx;
+};
+
+static void dcerpc_secondary_auth_connection_bind(struct composite_context *ctx);
+static void dcerpc_secondary_auth_connection_continue(struct composite_context *ctx);
+
+_PUBLIC_ struct composite_context* dcerpc_secondary_auth_connection_send(struct dcerpc_pipe *p,
+ struct dcerpc_binding *binding,
+ const struct ndr_interface_table *table,
+ struct cli_credentials *credentials,
+ struct loadparm_context *lp_ctx)
+{
+
+ struct composite_context *c, *secondary_conn_ctx;
+ struct sec_auth_conn_state *s;
+
+ /* composite context allocation and setup */
+ c = composite_create(p, p->conn->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct sec_auth_conn_state);
+ if (composite_nomem(s, c)) return c;
+ c->private_data = s;
+ s->ctx = c;
+
+ s->binding = binding;
+ s->table = table;
+ s->credentials = credentials;
+ s->lp_ctx = lp_ctx;
+
+ secondary_conn_ctx = dcerpc_secondary_connection_send(p, binding);
+
+ if (composite_nomem(secondary_conn_ctx, s->ctx)) {
+ talloc_free(c);
+ return NULL;
+ }
+
+ composite_continue(s->ctx, secondary_conn_ctx, dcerpc_secondary_auth_connection_bind,
+ s);
+ return c;
+}
+
+/*
+ Stage 2 of secondary_auth_connection:
+ Having made the secondary connection, we will need to do an (authenticated) bind
+*/
+static void dcerpc_secondary_auth_connection_bind(struct composite_context *ctx)
+{
+ struct composite_context *secondary_auth_ctx;
+ struct sec_auth_conn_state *s = talloc_get_type(ctx->async.private_data,
+ struct sec_auth_conn_state);
+
+ s->ctx->status = dcerpc_secondary_connection_recv(ctx, &s->pipe2);
+ if (!composite_is_ok(s->ctx)) return;
+
+ secondary_auth_ctx = dcerpc_pipe_auth_send(s->pipe2, s->binding, s->table, s->credentials,
+ s->lp_ctx);
+ composite_continue(s->ctx, secondary_auth_ctx, dcerpc_secondary_auth_connection_continue, s);
+
+}
+
+/*
+ Stage 3 of secondary_auth_connection: Receive result of authenticated bind request
+*/
+static void dcerpc_secondary_auth_connection_continue(struct composite_context *ctx)
+{
+ struct sec_auth_conn_state *s = talloc_get_type(ctx->async.private_data,
+ struct sec_auth_conn_state);
+
+ s->ctx->status = dcerpc_pipe_auth_recv(ctx, s, &s->pipe2);
+ if (!composite_is_ok(s->ctx)) return;
+
+ composite_done(s->ctx);
+}
+
+/*
+ Receive an authenticated pipe, created as a secondary connection
+*/
+_PUBLIC_ NTSTATUS dcerpc_secondary_auth_connection_recv(struct composite_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct dcerpc_pipe **p)
+{
+ NTSTATUS status = composite_wait(c);
+ struct sec_auth_conn_state *s;
+
+ s = talloc_get_type(c->private_data, struct sec_auth_conn_state);
+
+ if (NT_STATUS_IS_OK(status)) {
+ *p = talloc_steal(mem_ctx, s->pipe2);
+ }
+
+ talloc_free(c);
+ return status;
+}
diff --git a/source4/librpc/rpc/dcerpc_smb.c b/source4/librpc/rpc/dcerpc_smb.c
new file mode 100644
index 0000000000..013a8578e6
--- /dev/null
+++ b/source4/librpc/rpc/dcerpc_smb.c
@@ -0,0 +1,588 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ dcerpc over SMB transport
+
+ Copyright (C) Tim Potter 2003
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/composite/composite.h"
+#include "librpc/rpc/dcerpc.h"
+#include "librpc/rpc/dcerpc_proto.h"
+
+/* transport private information used by SMB pipe transport */
+struct smb_private {
+ uint16_t fnum;
+ struct smbcli_tree *tree;
+ const char *server_name;
+ bool dead;
+};
+
+
+/*
+ tell the dcerpc layer that the transport is dead
+*/
+static void pipe_dead(struct dcerpc_connection *c, NTSTATUS status)
+{
+ struct smb_private *smb = (struct smb_private *)c->transport.private_data;
+
+ if (smb->dead) {
+ return;
+ }
+
+ smb->dead = true;
+
+ if (NT_STATUS_EQUAL(NT_STATUS_UNSUCCESSFUL, status)) {
+ status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+
+ if (NT_STATUS_EQUAL(NT_STATUS_OK, status)) {
+ status = NT_STATUS_END_OF_FILE;
+ }
+
+ if (c->transport.recv_data) {
+ c->transport.recv_data(c, NULL, status);
+ }
+}
+
+
+/*
+ this holds the state of an in-flight call
+*/
+struct smb_read_state {
+ struct dcerpc_connection *c;
+ struct smbcli_request *req;
+ size_t received;
+ DATA_BLOB data;
+ union smb_read *io;
+};
+
+/*
+ called when a read request has completed
+*/
+static void smb_read_callback(struct smbcli_request *req)
+{
+ struct smb_private *smb;
+ struct smb_read_state *state;
+ union smb_read *io;
+ uint16_t frag_length;
+ NTSTATUS status;
+
+ state = talloc_get_type(req->async.private_data, struct smb_read_state);
+ smb = talloc_get_type(state->c->transport.private_data, struct smb_private);
+ io = state->io;
+
+ status = smb_raw_read_recv(state->req, io);
+ if (NT_STATUS_IS_ERR(status)) {
+ pipe_dead(state->c, status);
+ talloc_free(state);
+ return;
+ }
+
+ state->received += io->readx.out.nread;
+
+ if (state->received < 16) {
+ DEBUG(0,("dcerpc_smb: short packet (length %d) in read callback!\n",
+ (int)state->received));
+ pipe_dead(state->c, NT_STATUS_INFO_LENGTH_MISMATCH);
+ talloc_free(state);
+ return;
+ }
+
+ frag_length = dcerpc_get_frag_length(&state->data);
+
+ if (frag_length <= state->received) {
+ DATA_BLOB data = state->data;
+ struct dcerpc_connection *c = state->c;
+ data.length = state->received;
+ talloc_steal(state->c, data.data);
+ talloc_free(state);
+ c->transport.recv_data(c, &data, NT_STATUS_OK);
+ return;
+ }
+
+ /* initiate another read request, as we only got part of a fragment */
+ state->data.data = talloc_realloc(state, state->data.data, uint8_t, frag_length);
+
+ io->readx.in.mincnt = MIN(state->c->srv_max_xmit_frag,
+ frag_length - state->received);
+ io->readx.in.maxcnt = io->readx.in.mincnt;
+ io->readx.out.data = state->data.data + state->received;
+
+ state->req = smb_raw_read_send(smb->tree, io);
+ if (state->req == NULL) {
+ pipe_dead(state->c, NT_STATUS_NO_MEMORY);
+ talloc_free(state);
+ return;
+ }
+
+ state->req->async.fn = smb_read_callback;
+ state->req->async.private_data = state;
+}
+
+/*
+ trigger a read request from the server, possibly with some initial
+ data in the read buffer
+*/
+static NTSTATUS send_read_request_continue(struct dcerpc_connection *c, DATA_BLOB *blob)
+{
+ struct smb_private *smb = (struct smb_private *)c->transport.private_data;
+ union smb_read *io;
+ struct smb_read_state *state;
+ struct smbcli_request *req;
+
+ state = talloc(smb, struct smb_read_state);
+ if (state == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ state->c = c;
+ if (blob == NULL) {
+ state->received = 0;
+ state->data = data_blob_talloc(state, NULL, 0x2000);
+ } else {
+ uint32_t frag_length = blob->length>=16?
+ dcerpc_get_frag_length(blob):0x2000;
+ state->received = blob->length;
+ state->data = data_blob_talloc(state, NULL, frag_length);
+ if (!state->data.data) {
+ talloc_free(state);
+ return NT_STATUS_NO_MEMORY;
+ }
+ memcpy(state->data.data, blob->data, blob->length);
+ }
+
+ state->io = talloc(state, union smb_read);
+
+ io = state->io;
+ io->generic.level = RAW_READ_READX;
+ io->readx.in.file.fnum = smb->fnum;
+ io->readx.in.mincnt = state->data.length - state->received;
+ io->readx.in.maxcnt = io->readx.in.mincnt;
+ io->readx.in.offset = 0;
+ io->readx.in.remaining = 0;
+ io->readx.in.read_for_execute = false;
+ io->readx.out.data = state->data.data + state->received;
+ req = smb_raw_read_send(smb->tree, io);
+ if (req == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ req->async.fn = smb_read_callback;
+ req->async.private_data = state;
+
+ state->req = req;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ trigger a read request from the server
+*/
+static NTSTATUS send_read_request(struct dcerpc_connection *c)
+{
+ struct smb_private *smb = (struct smb_private *)c->transport.private_data;
+
+ if (smb->dead) {
+ return NT_STATUS_CONNECTION_DISCONNECTED;
+ }
+
+ return send_read_request_continue(c, NULL);
+}
+
+/*
+ this holds the state of an in-flight trans call
+*/
+struct smb_trans_state {
+ struct dcerpc_connection *c;
+ struct smbcli_request *req;
+ struct smb_trans2 *trans;
+};
+
+/*
+ called when a trans request has completed
+*/
+static void smb_trans_callback(struct smbcli_request *req)
+{
+ struct smb_trans_state *state = (struct smb_trans_state *)req->async.private_data;
+ struct dcerpc_connection *c = state->c;
+ NTSTATUS status;
+
+ status = smb_raw_trans_recv(req, state, state->trans);
+
+ if (NT_STATUS_IS_ERR(status)) {
+ pipe_dead(c, status);
+ return;
+ }
+
+ if (!NT_STATUS_EQUAL(status, STATUS_BUFFER_OVERFLOW)) {
+ DATA_BLOB data = state->trans->out.data;
+ talloc_steal(c, data.data);
+ talloc_free(state);
+ c->transport.recv_data(c, &data, NT_STATUS_OK);
+ return;
+ }
+
+ /* there is more to receive - setup a readx */
+ send_read_request_continue(c, &state->trans->out.data);
+ talloc_free(state);
+}
+
+/*
+ send a SMBtrans style request
+*/
+static NTSTATUS smb_send_trans_request(struct dcerpc_connection *c, DATA_BLOB *blob)
+{
+ struct smb_private *smb = (struct smb_private *)c->transport.private_data;
+ struct smb_trans2 *trans;
+ uint16_t setup[2];
+ struct smb_trans_state *state;
+ uint16_t max_data;
+
+ state = talloc(smb, struct smb_trans_state);
+ if (state == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ state->c = c;
+ state->trans = talloc(state, struct smb_trans2);
+ trans = state->trans;
+
+ trans->in.data = *blob;
+ trans->in.params = data_blob(NULL, 0);
+
+ setup[0] = TRANSACT_DCERPCCMD;
+ setup[1] = smb->fnum;
+
+ if (c->srv_max_xmit_frag > 0) {
+ max_data = MIN(UINT16_MAX, c->srv_max_xmit_frag);
+ } else {
+ max_data = UINT16_MAX;
+ }
+
+ trans->in.max_param = 0;
+ trans->in.max_data = max_data;
+ trans->in.max_setup = 0;
+ trans->in.setup_count = 2;
+ trans->in.flags = 0;
+ trans->in.timeout = 0;
+ trans->in.setup = setup;
+ trans->in.trans_name = "\\PIPE\\";
+
+ state->req = smb_raw_trans_send(smb->tree, trans);
+ if (state->req == NULL) {
+ talloc_free(state);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ state->req->async.fn = smb_trans_callback;
+ state->req->async.private_data = state;
+
+ talloc_steal(state, state->req);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ called when a write request has completed
+*/
+static void smb_write_callback(struct smbcli_request *req)
+{
+ struct dcerpc_connection *c = (struct dcerpc_connection *)req->async.private_data;
+
+ if (!NT_STATUS_IS_OK(req->status)) {
+ DEBUG(0,("dcerpc_smb: write callback error\n"));
+ pipe_dead(c, req->status);
+ }
+
+ smbcli_request_destroy(req);
+}
+
+/*
+ send a packet to the server
+*/
+static NTSTATUS smb_send_request(struct dcerpc_connection *c, DATA_BLOB *blob,
+ bool trigger_read)
+{
+ struct smb_private *smb = (struct smb_private *)c->transport.private_data;
+ union smb_write io;
+ struct smbcli_request *req;
+
+ if (smb->dead) {
+ return NT_STATUS_CONNECTION_DISCONNECTED;
+ }
+
+ if (trigger_read) {
+ return smb_send_trans_request(c, blob);
+ }
+
+ io.generic.level = RAW_WRITE_WRITEX;
+ io.writex.in.file.fnum = smb->fnum;
+ io.writex.in.offset = 0;
+ io.writex.in.wmode = PIPE_START_MESSAGE;
+ io.writex.in.remaining = blob->length;
+ io.writex.in.count = blob->length;
+ io.writex.in.data = blob->data;
+
+ /* we must not timeout at the smb level for rpc requests, as otherwise
+ signing/sealing can be messed up */
+ smb->tree->session->transport->options.request_timeout = 0;
+
+ req = smb_raw_write_send(smb->tree, &io);
+ if (req == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ req->async.fn = smb_write_callback;
+ req->async.private_data = c;
+
+ if (trigger_read) {
+ send_read_request(c);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ shutdown SMB pipe connection
+*/
+static NTSTATUS smb_shutdown_pipe(struct dcerpc_connection *c, NTSTATUS status)
+{
+ struct smb_private *smb = (struct smb_private *)c->transport.private_data;
+ union smb_close io;
+ struct smbcli_request *req;
+
+ /* maybe we're still starting up */
+ if (!smb) return status;
+
+ io.close.level = RAW_CLOSE_CLOSE;
+ io.close.in.file.fnum = smb->fnum;
+ io.close.in.write_time = 0;
+ req = smb_raw_close_send(smb->tree, &io);
+ if (req != NULL) {
+ /* we don't care if this fails, so just free it if it succeeds */
+ req->async.fn = (void (*)(struct smbcli_request *))talloc_free;
+ }
+
+ talloc_free(smb);
+
+ return status;
+}
+
+/*
+ return SMB server name (called name)
+*/
+static const char *smb_peer_name(struct dcerpc_connection *c)
+{
+ struct smb_private *smb = (struct smb_private *)c->transport.private_data;
+ return smb->server_name;
+}
+
+/*
+ return remote name we make the actual connection (good for kerberos)
+*/
+static const char *smb_target_hostname(struct dcerpc_connection *c)
+{
+ struct smb_private *smb = talloc_get_type(c->transport.private_data, struct smb_private);
+ return smb->tree->session->transport->socket->hostname;
+}
+
+/*
+ fetch the user session key
+*/
+static NTSTATUS smb_session_key(struct dcerpc_connection *c, DATA_BLOB *session_key)
+{
+ struct smb_private *smb = (struct smb_private *)c->transport.private_data;
+
+ if (smb->tree->session->user_session_key.data) {
+ *session_key = smb->tree->session->user_session_key;
+ return NT_STATUS_OK;
+ }
+ return NT_STATUS_NO_USER_SESSION_KEY;
+}
+
+struct pipe_open_smb_state {
+ union smb_open *open;
+ struct dcerpc_connection *c;
+ struct smbcli_tree *tree;
+ struct composite_context *ctx;
+};
+
+static void pipe_open_recv(struct smbcli_request *req);
+
+struct composite_context *dcerpc_pipe_open_smb_send(struct dcerpc_pipe *p,
+ struct smbcli_tree *tree,
+ const char *pipe_name)
+{
+ struct composite_context *ctx;
+ struct pipe_open_smb_state *state;
+ struct smbcli_request *req;
+ struct dcerpc_connection *c = p->conn;
+
+ /* if we don't have a binding on this pipe yet, then create one */
+ if (p->binding == NULL) {
+ NTSTATUS status;
+ char *s;
+ SMB_ASSERT(tree->session->transport->socket->hostname != NULL);
+ s = talloc_asprintf(p, "ncacn_np:%s", tree->session->transport->socket->hostname);
+ if (s == NULL) return NULL;
+ status = dcerpc_parse_binding(p, s, &p->binding);
+ talloc_free(s);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NULL;
+ }
+ }
+
+ ctx = composite_create(c, c->event_ctx);
+ if (ctx == NULL) return NULL;
+
+ state = talloc(ctx, struct pipe_open_smb_state);
+ if (composite_nomem(state, ctx)) return ctx;
+ ctx->private_data = state;
+
+ state->c = c;
+ state->tree = tree;
+ state->ctx = ctx;
+
+ state->open = talloc(state, union smb_open);
+ if (composite_nomem(state->open, ctx)) return ctx;
+
+ state->open->ntcreatex.level = RAW_OPEN_NTCREATEX;
+ state->open->ntcreatex.in.flags = 0;
+ state->open->ntcreatex.in.root_fid = 0;
+ state->open->ntcreatex.in.access_mask =
+ SEC_STD_READ_CONTROL |
+ SEC_FILE_WRITE_ATTRIBUTE |
+ SEC_FILE_WRITE_EA |
+ SEC_FILE_READ_DATA |
+ SEC_FILE_WRITE_DATA;
+ state->open->ntcreatex.in.file_attr = 0;
+ state->open->ntcreatex.in.alloc_size = 0;
+ state->open->ntcreatex.in.share_access =
+ NTCREATEX_SHARE_ACCESS_READ |
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ state->open->ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
+ state->open->ntcreatex.in.create_options = 0;
+ state->open->ntcreatex.in.impersonation =
+ NTCREATEX_IMPERSONATION_IMPERSONATION;
+ state->open->ntcreatex.in.security_flags = 0;
+
+ if ((strncasecmp(pipe_name, "/pipe/", 6) == 0) ||
+ (strncasecmp(pipe_name, "\\pipe\\", 6) == 0)) {
+ pipe_name += 6;
+ }
+ state->open->ntcreatex.in.fname =
+ (pipe_name[0] == '\\') ?
+ talloc_strdup(state->open, pipe_name) :
+ talloc_asprintf(state->open, "\\%s", pipe_name);
+ if (composite_nomem(state->open->ntcreatex.in.fname, ctx)) return ctx;
+
+ req = smb_raw_open_send(tree, state->open);
+ composite_continue_smb(ctx, req, pipe_open_recv, state);
+ return ctx;
+}
+
+static void pipe_open_recv(struct smbcli_request *req)
+{
+ struct pipe_open_smb_state *state = talloc_get_type(req->async.private_data,
+ struct pipe_open_smb_state);
+ struct composite_context *ctx = state->ctx;
+ struct dcerpc_connection *c = state->c;
+ struct smb_private *smb;
+
+ ctx->status = smb_raw_open_recv(req, state, state->open);
+ if (!composite_is_ok(ctx)) return;
+
+ /*
+ fill in the transport methods
+ */
+ c->transport.transport = NCACN_NP;
+ c->transport.private_data = NULL;
+ c->transport.shutdown_pipe = smb_shutdown_pipe;
+ c->transport.peer_name = smb_peer_name;
+ c->transport.target_hostname = smb_target_hostname;
+
+ c->transport.send_request = smb_send_request;
+ c->transport.send_read = send_read_request;
+ c->transport.recv_data = NULL;
+
+ /* Over-ride the default session key with the SMB session key */
+ c->security_state.session_key = smb_session_key;
+
+ smb = talloc(c, struct smb_private);
+ if (composite_nomem(smb, ctx)) return;
+
+ smb->fnum = state->open->ntcreatex.out.file.fnum;
+ smb->tree = talloc_reference(smb, state->tree);
+ smb->server_name= strupper_talloc(smb,
+ state->tree->session->transport->called.name);
+ if (composite_nomem(smb->server_name, ctx)) return;
+ smb->dead = false;
+
+ c->transport.private_data = smb;
+
+ composite_done(ctx);
+}
+
+NTSTATUS dcerpc_pipe_open_smb_recv(struct composite_context *c)
+{
+ NTSTATUS status = composite_wait(c);
+ talloc_free(c);
+ return status;
+}
+
+_PUBLIC_ NTSTATUS dcerpc_pipe_open_smb(struct dcerpc_pipe *p,
+ struct smbcli_tree *tree,
+ const char *pipe_name)
+{
+ struct composite_context *ctx = dcerpc_pipe_open_smb_send(p, tree,
+ pipe_name);
+ return dcerpc_pipe_open_smb_recv(ctx);
+}
+
+/*
+ return the SMB tree used for a dcerpc over SMB pipe
+*/
+_PUBLIC_ struct smbcli_tree *dcerpc_smb_tree(struct dcerpc_connection *c)
+{
+ struct smb_private *smb;
+
+ if (c->transport.transport != NCACN_NP) return NULL;
+
+ smb = talloc_get_type(c->transport.private_data, struct smb_private);
+ if (!smb) return NULL;
+
+ return smb->tree;
+}
+
+/*
+ return the SMB fnum used for a dcerpc over SMB pipe (hack for torture operations)
+*/
+_PUBLIC_ uint16_t dcerpc_smb_fnum(struct dcerpc_connection *c)
+{
+ struct smb_private *smb;
+
+ if (c->transport.transport != NCACN_NP) return 0;
+
+ smb = talloc_get_type(c->transport.private_data, struct smb_private);
+ if (!smb) return 0;
+
+ return smb->fnum;
+}
diff --git a/source4/librpc/rpc/dcerpc_smb2.c b/source4/librpc/rpc/dcerpc_smb2.c
new file mode 100644
index 0000000000..84ba8114e2
--- /dev/null
+++ b/source4/librpc/rpc/dcerpc_smb2.c
@@ -0,0 +1,512 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ dcerpc over SMB2 transport
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/composite/composite.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "libcli/raw/ioctl.h"
+#include "librpc/rpc/dcerpc.h"
+#include "librpc/rpc/dcerpc_proto.h"
+
+/* transport private information used by SMB2 pipe transport */
+struct smb2_private {
+ struct smb2_handle handle;
+ struct smb2_tree *tree;
+ const char *server_name;
+ bool dead;
+};
+
+
+/*
+ tell the dcerpc layer that the transport is dead
+*/
+static void pipe_dead(struct dcerpc_connection *c, NTSTATUS status)
+{
+ struct smb2_private *smb = (struct smb2_private *)c->transport.private_data;
+
+ if (smb->dead) {
+ return;
+ }
+
+ smb->dead = true;
+
+ if (NT_STATUS_EQUAL(NT_STATUS_UNSUCCESSFUL, status)) {
+ status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+
+ if (NT_STATUS_EQUAL(NT_STATUS_OK, status)) {
+ status = NT_STATUS_END_OF_FILE;
+ }
+
+ if (c->transport.recv_data) {
+ c->transport.recv_data(c, NULL, status);
+ }
+}
+
+
+/*
+ this holds the state of an in-flight call
+*/
+struct smb2_read_state {
+ struct dcerpc_connection *c;
+ DATA_BLOB data;
+};
+
+/*
+ called when a read request has completed
+*/
+static void smb2_read_callback(struct smb2_request *req)
+{
+ struct smb2_private *smb;
+ struct smb2_read_state *state;
+ struct smb2_read io;
+ uint16_t frag_length;
+ NTSTATUS status;
+
+ state = talloc_get_type(req->async.private_data, struct smb2_read_state);
+ smb = talloc_get_type(state->c->transport.private_data, struct smb2_private);
+
+ status = smb2_read_recv(req, state, &io);
+ if (NT_STATUS_IS_ERR(status)) {
+ pipe_dead(state->c, status);
+ talloc_free(state);
+ return;
+ }
+
+ if (!data_blob_append(state, &state->data,
+ io.out.data.data, io.out.data.length)) {
+ pipe_dead(state->c, NT_STATUS_NO_MEMORY);
+ talloc_free(state);
+ return;
+ }
+
+ if (state->data.length < 16) {
+ DEBUG(0,("dcerpc_smb2: short packet (length %d) in read callback!\n",
+ (int)state->data.length));
+ pipe_dead(state->c, NT_STATUS_INFO_LENGTH_MISMATCH);
+ talloc_free(state);
+ return;
+ }
+
+ frag_length = dcerpc_get_frag_length(&state->data);
+
+ if (frag_length <= state->data.length) {
+ DATA_BLOB data = state->data;
+ struct dcerpc_connection *c = state->c;
+ talloc_steal(c, data.data);
+ talloc_free(state);
+ c->transport.recv_data(c, &data, NT_STATUS_OK);
+ return;
+ }
+
+ /* initiate another read request, as we only got part of a fragment */
+ ZERO_STRUCT(io);
+ io.in.file.handle = smb->handle;
+ io.in.length = MIN(state->c->srv_max_xmit_frag,
+ frag_length - state->data.length);
+ if (io.in.length < 16) {
+ io.in.length = 16;
+ }
+
+ req = smb2_read_send(smb->tree, &io);
+ if (req == NULL) {
+ pipe_dead(state->c, NT_STATUS_NO_MEMORY);
+ talloc_free(state);
+ return;
+ }
+
+ req->async.fn = smb2_read_callback;
+ req->async.private_data = state;
+}
+
+
+/*
+ trigger a read request from the server, possibly with some initial
+ data in the read buffer
+*/
+static NTSTATUS send_read_request_continue(struct dcerpc_connection *c, DATA_BLOB *blob)
+{
+ struct smb2_private *smb = (struct smb2_private *)c->transport.private_data;
+ struct smb2_read io;
+ struct smb2_read_state *state;
+ struct smb2_request *req;
+
+ state = talloc(smb, struct smb2_read_state);
+ if (state == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ state->c = c;
+ if (blob == NULL) {
+ state->data = data_blob(NULL, 0);
+ } else {
+ state->data = *blob;
+ talloc_steal(state, state->data.data);
+ }
+
+ ZERO_STRUCT(io);
+ io.in.file.handle = smb->handle;
+
+ if (state->data.length >= 16) {
+ uint16_t frag_length = dcerpc_get_frag_length(&state->data);
+ io.in.length = frag_length - state->data.length;
+ } else {
+ io.in.length = 0x2000;
+ }
+
+ req = smb2_read_send(smb->tree, &io);
+ if (req == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ req->async.fn = smb2_read_callback;
+ req->async.private_data = state;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ trigger a read request from the server
+*/
+static NTSTATUS send_read_request(struct dcerpc_connection *c)
+{
+ struct smb2_private *smb = (struct smb2_private *)c->transport.private_data;
+
+ if (smb->dead) {
+ return NT_STATUS_CONNECTION_DISCONNECTED;
+ }
+
+ return send_read_request_continue(c, NULL);
+}
+
+/*
+ this holds the state of an in-flight trans call
+*/
+struct smb2_trans_state {
+ struct dcerpc_connection *c;
+};
+
+/*
+ called when a trans request has completed
+*/
+static void smb2_trans_callback(struct smb2_request *req)
+{
+ struct smb2_trans_state *state = talloc_get_type(req->async.private_data,
+ struct smb2_trans_state);
+ struct dcerpc_connection *c = state->c;
+ NTSTATUS status;
+ struct smb2_ioctl io;
+
+ status = smb2_ioctl_recv(req, state, &io);
+ if (NT_STATUS_IS_ERR(status)) {
+ pipe_dead(c, status);
+ return;
+ }
+
+ if (!NT_STATUS_EQUAL(status, STATUS_BUFFER_OVERFLOW)) {
+ DATA_BLOB data = io.out.out;
+ talloc_steal(c, data.data);
+ talloc_free(state);
+ c->transport.recv_data(c, &data, NT_STATUS_OK);
+ return;
+ }
+
+ /* there is more to receive - setup a read */
+ send_read_request_continue(c, &io.out.out);
+ talloc_free(state);
+}
+
+/*
+ send a SMBtrans style request, using a named pipe read_write fsctl
+*/
+static NTSTATUS smb2_send_trans_request(struct dcerpc_connection *c, DATA_BLOB *blob)
+{
+ struct smb2_private *smb = talloc_get_type(c->transport.private_data,
+ struct smb2_private);
+ struct smb2_ioctl io;
+ struct smb2_trans_state *state;
+ struct smb2_request *req;
+
+ state = talloc(smb, struct smb2_trans_state);
+ if (state == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ state->c = c;
+
+ ZERO_STRUCT(io);
+ io.in.file.handle = smb->handle;
+ io.in.function = FSCTL_NAMED_PIPE_READ_WRITE;
+ io.in.max_response_size = 0x1000;
+ io.in.flags = 1;
+ io.in.out = *blob;
+
+ req = smb2_ioctl_send(smb->tree, &io);
+ if (req == NULL) {
+ talloc_free(state);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ req->async.fn = smb2_trans_callback;
+ req->async.private_data = state;
+
+ talloc_steal(state, req);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ called when a write request has completed
+*/
+static void smb2_write_callback(struct smb2_request *req)
+{
+ struct dcerpc_connection *c = (struct dcerpc_connection *)req->async.private_data;
+
+ if (!NT_STATUS_IS_OK(req->status)) {
+ DEBUG(0,("dcerpc_smb2: write callback error\n"));
+ pipe_dead(c, req->status);
+ }
+
+ smb2_request_destroy(req);
+}
+
+/*
+ send a packet to the server
+*/
+static NTSTATUS smb2_send_request(struct dcerpc_connection *c, DATA_BLOB *blob,
+ bool trigger_read)
+{
+ struct smb2_private *smb = (struct smb2_private *)c->transport.private_data;
+ struct smb2_write io;
+ struct smb2_request *req;
+
+ if (smb->dead) {
+ return NT_STATUS_CONNECTION_DISCONNECTED;
+ }
+
+ if (trigger_read) {
+ return smb2_send_trans_request(c, blob);
+ }
+
+ ZERO_STRUCT(io);
+ io.in.file.handle = smb->handle;
+ io.in.data = *blob;
+
+ req = smb2_write_send(smb->tree, &io);
+ if (req == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ req->async.fn = smb2_write_callback;
+ req->async.private_data = c;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ shutdown SMB pipe connection
+*/
+static NTSTATUS smb2_shutdown_pipe(struct dcerpc_connection *c, NTSTATUS status)
+{
+ struct smb2_private *smb = (struct smb2_private *)c->transport.private_data;
+ struct smb2_close io;
+ struct smb2_request *req;
+
+ /* maybe we're still starting up */
+ if (!smb) return status;
+
+ ZERO_STRUCT(io);
+ io.in.file.handle = smb->handle;
+ req = smb2_close_send(smb->tree, &io);
+ if (req != NULL) {
+ /* we don't care if this fails, so just free it if it succeeds */
+ req->async.fn = (void (*)(struct smb2_request *))talloc_free;
+ }
+
+ talloc_free(smb);
+
+ return status;
+}
+
+/*
+ return SMB server name
+*/
+static const char *smb2_peer_name(struct dcerpc_connection *c)
+{
+ struct smb2_private *smb = talloc_get_type(c->transport.private_data,
+ struct smb2_private);
+ return smb->server_name;
+}
+
+/*
+ return remote name we make the actual connection (good for kerberos)
+*/
+static const char *smb2_target_hostname(struct dcerpc_connection *c)
+{
+ struct smb2_private *smb = talloc_get_type(c->transport.private_data,
+ struct smb2_private);
+ return smb->tree->session->transport->socket->hostname;
+}
+
+/*
+ fetch the user session key
+*/
+static NTSTATUS smb2_session_key(struct dcerpc_connection *c, DATA_BLOB *session_key)
+{
+ struct smb2_private *smb = talloc_get_type(c->transport.private_data,
+ struct smb2_private);
+ *session_key = smb->tree->session->session_key;
+ if (session_key->data == NULL) {
+ return NT_STATUS_NO_USER_SESSION_KEY;
+ }
+ return NT_STATUS_OK;
+}
+
+struct pipe_open_smb2_state {
+ struct dcerpc_connection *c;
+ struct composite_context *ctx;
+};
+
+static void pipe_open_recv(struct smb2_request *req);
+
+struct composite_context *dcerpc_pipe_open_smb2_send(struct dcerpc_pipe *p,
+ struct smb2_tree *tree,
+ const char *pipe_name)
+{
+ struct composite_context *ctx;
+ struct pipe_open_smb2_state *state;
+ struct smb2_create io;
+ struct smb2_request *req;
+ struct dcerpc_connection *c = p->conn;
+
+ ctx = composite_create(c, c->event_ctx);
+ if (ctx == NULL) return NULL;
+
+ state = talloc(ctx, struct pipe_open_smb2_state);
+ if (composite_nomem(state, ctx)) return ctx;
+ ctx->private_data = state;
+
+ state->c = c;
+ state->ctx = ctx;
+
+ ZERO_STRUCT(io);
+ io.in.desired_access =
+ SEC_STD_READ_CONTROL |
+ SEC_FILE_READ_ATTRIBUTE |
+ SEC_FILE_WRITE_ATTRIBUTE |
+ SEC_STD_SYNCHRONIZE |
+ SEC_FILE_READ_EA |
+ SEC_FILE_WRITE_EA |
+ SEC_FILE_READ_DATA |
+ SEC_FILE_WRITE_DATA |
+ SEC_FILE_APPEND_DATA;
+ io.in.share_access =
+ NTCREATEX_SHARE_ACCESS_READ |
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ io.in.create_disposition = NTCREATEX_DISP_OPEN;
+ io.in.create_options =
+ NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
+ NTCREATEX_OPTIONS_NO_RECALL;
+ io.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
+
+ if ((strncasecmp(pipe_name, "/pipe/", 6) == 0) ||
+ (strncasecmp(pipe_name, "\\pipe\\", 6) == 0)) {
+ pipe_name += 6;
+ }
+ io.in.fname = pipe_name;
+
+ req = smb2_create_send(tree, &io);
+ composite_continue_smb2(ctx, req, pipe_open_recv, state);
+ return ctx;
+}
+
+static void pipe_open_recv(struct smb2_request *req)
+{
+ struct pipe_open_smb2_state *state =
+ talloc_get_type(req->async.private_data,
+ struct pipe_open_smb2_state);
+ struct composite_context *ctx = state->ctx;
+ struct dcerpc_connection *c = state->c;
+ struct smb2_tree *tree = req->tree;
+ struct smb2_private *smb;
+ struct smb2_create io;
+
+ ctx->status = smb2_create_recv(req, state, &io);
+ if (!composite_is_ok(ctx)) return;
+
+ /*
+ fill in the transport methods
+ */
+ c->transport.transport = NCACN_NP;
+ c->transport.private_data = NULL;
+ c->transport.shutdown_pipe = smb2_shutdown_pipe;
+ c->transport.peer_name = smb2_peer_name;
+ c->transport.target_hostname = smb2_target_hostname;
+
+ c->transport.send_request = smb2_send_request;
+ c->transport.send_read = send_read_request;
+ c->transport.recv_data = NULL;
+
+ /* Over-ride the default session key with the SMB session key */
+ c->security_state.session_key = smb2_session_key;
+
+ smb = talloc(c, struct smb2_private);
+ if (composite_nomem(smb, ctx)) return;
+
+ smb->handle = io.out.file.handle;
+ smb->tree = talloc_reference(smb, tree);
+ smb->server_name= strupper_talloc(smb,
+ tree->session->transport->socket->hostname);
+ if (composite_nomem(smb->server_name, ctx)) return;
+ smb->dead = false;
+
+ c->transport.private_data = smb;
+
+ composite_done(ctx);
+}
+
+NTSTATUS dcerpc_pipe_open_smb2_recv(struct composite_context *c)
+{
+ NTSTATUS status = composite_wait(c);
+ talloc_free(c);
+ return status;
+}
+
+NTSTATUS dcerpc_pipe_open_smb2(struct dcerpc_pipe *p,
+ struct smb2_tree *tree,
+ const char *pipe_name)
+{
+ struct composite_context *ctx = dcerpc_pipe_open_smb2_send(p, tree, pipe_name);
+ return dcerpc_pipe_open_smb2_recv(ctx);
+}
+
+/*
+ return the SMB2 tree used for a dcerpc over SMB2 pipe
+*/
+struct smb2_tree *dcerpc_smb2_tree(struct dcerpc_connection *c)
+{
+ struct smb2_private *smb = talloc_get_type(c->transport.private_data,
+ struct smb2_private);
+ return smb->tree;
+}
diff --git a/source4/librpc/rpc/dcerpc_sock.c b/source4/librpc/rpc/dcerpc_sock.c
new file mode 100644
index 0000000000..64a5b92e90
--- /dev/null
+++ b/source4/librpc/rpc/dcerpc_sock.c
@@ -0,0 +1,672 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ dcerpc over standard sockets transport
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Jelmer Vernooij 2004
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/events/events.h"
+#include "lib/socket/socket.h"
+#include "lib/stream/packet.h"
+#include "libcli/composite/composite.h"
+#include "librpc/rpc/dcerpc.h"
+#include "librpc/rpc/dcerpc_proto.h"
+#include "libcli/resolve/resolve.h"
+
+/* transport private information used by general socket pipe transports */
+struct sock_private {
+ struct tevent_fd *fde;
+ struct socket_context *sock;
+ char *server_name;
+
+ struct packet_context *packet;
+ uint32_t pending_reads;
+
+ const char *path; /* For ncacn_unix_sock and ncalrpc */
+};
+
+
+/*
+ mark the socket dead
+*/
+static void sock_dead(struct dcerpc_connection *p, NTSTATUS status)
+{
+ struct sock_private *sock = (struct sock_private *)p->transport.private_data;
+
+ if (!sock) return;
+
+ if (sock->packet) {
+ packet_recv_disable(sock->packet);
+ packet_set_fde(sock->packet, NULL);
+ packet_set_socket(sock->packet, NULL);
+ }
+
+ if (sock->fde) {
+ talloc_free(sock->fde);
+ sock->fde = NULL;
+ }
+
+ if (sock->sock) {
+ talloc_free(sock->sock);
+ sock->sock = NULL;
+ }
+
+ if (NT_STATUS_EQUAL(NT_STATUS_UNSUCCESSFUL, status)) {
+ status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+
+ if (NT_STATUS_EQUAL(NT_STATUS_OK, status)) {
+ status = NT_STATUS_END_OF_FILE;
+ }
+
+ if (p->transport.recv_data) {
+ p->transport.recv_data(p, NULL, status);
+ }
+}
+
+
+/*
+ handle socket recv errors
+*/
+static void sock_error_handler(void *private_data, NTSTATUS status)
+{
+ struct dcerpc_connection *p = talloc_get_type(private_data,
+ struct dcerpc_connection);
+ sock_dead(p, status);
+}
+
+/*
+ check if a blob is a complete packet
+*/
+static NTSTATUS sock_complete_packet(void *private_data, DATA_BLOB blob, size_t *size)
+{
+ if (blob.length < DCERPC_FRAG_LEN_OFFSET+2) {
+ return STATUS_MORE_ENTRIES;
+ }
+ *size = dcerpc_get_frag_length(&blob);
+ if (*size > blob.length) {
+ return STATUS_MORE_ENTRIES;
+ }
+ return NT_STATUS_OK;
+}
+
+/*
+ process recv requests
+*/
+static NTSTATUS sock_process_recv(void *private_data, DATA_BLOB blob)
+{
+ struct dcerpc_connection *p = talloc_get_type(private_data,
+ struct dcerpc_connection);
+ struct sock_private *sock = (struct sock_private *)p->transport.private_data;
+ sock->pending_reads--;
+ if (sock->pending_reads == 0) {
+ packet_recv_disable(sock->packet);
+ }
+ p->transport.recv_data(p, &blob, NT_STATUS_OK);
+ return NT_STATUS_OK;
+}
+
+/*
+ called when a IO is triggered by the events system
+*/
+static void sock_io_handler(struct tevent_context *ev, struct tevent_fd *fde,
+ uint16_t flags, void *private_data)
+{
+ struct dcerpc_connection *p = talloc_get_type(private_data,
+ struct dcerpc_connection);
+ struct sock_private *sock = (struct sock_private *)p->transport.private_data;
+
+ if (flags & EVENT_FD_WRITE) {
+ packet_queue_run(sock->packet);
+ return;
+ }
+
+ if (sock->sock == NULL) {
+ return;
+ }
+
+ if (flags & EVENT_FD_READ) {
+ packet_recv(sock->packet);
+ }
+}
+
+/*
+ initiate a read request - not needed for dcerpc sockets
+*/
+static NTSTATUS sock_send_read(struct dcerpc_connection *p)
+{
+ struct sock_private *sock = (struct sock_private *)p->transport.private_data;
+ sock->pending_reads++;
+ if (sock->pending_reads == 1) {
+ packet_recv_enable(sock->packet);
+ }
+ return NT_STATUS_OK;
+}
+
+/*
+ send an initial pdu in a multi-pdu sequence
+*/
+static NTSTATUS sock_send_request(struct dcerpc_connection *p, DATA_BLOB *data,
+ bool trigger_read)
+{
+ struct sock_private *sock = (struct sock_private *)p->transport.private_data;
+ DATA_BLOB blob;
+ NTSTATUS status;
+
+ if (sock->sock == NULL) {
+ return NT_STATUS_CONNECTION_DISCONNECTED;
+ }
+
+ blob = data_blob_talloc(sock->packet, data->data, data->length);
+ if (blob.data == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = packet_send(sock->packet, blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (trigger_read) {
+ sock_send_read(p);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ shutdown sock pipe connection
+*/
+static NTSTATUS sock_shutdown_pipe(struct dcerpc_connection *p, NTSTATUS status)
+{
+ struct sock_private *sock = (struct sock_private *)p->transport.private_data;
+
+ if (sock && sock->sock) {
+ sock_dead(p, status);
+ }
+
+ return status;
+}
+
+/*
+ return sock server name
+*/
+static const char *sock_peer_name(struct dcerpc_connection *p)
+{
+ struct sock_private *sock = talloc_get_type(p->transport.private_data, struct sock_private);
+ return sock->server_name;
+}
+
+/*
+ return remote name we make the actual connection (good for kerberos)
+*/
+static const char *sock_target_hostname(struct dcerpc_connection *p)
+{
+ struct sock_private *sock = talloc_get_type(p->transport.private_data, struct sock_private);
+ return sock->server_name;
+}
+
+
+struct pipe_open_socket_state {
+ struct dcerpc_connection *conn;
+ struct socket_context *socket_ctx;
+ struct sock_private *sock;
+ struct socket_address *server;
+ const char *target_hostname;
+ enum dcerpc_transport_t transport;
+};
+
+
+static void continue_socket_connect(struct composite_context *ctx)
+{
+ struct dcerpc_connection *conn;
+ struct sock_private *sock;
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+ struct pipe_open_socket_state *s = talloc_get_type(c->private_data,
+ struct pipe_open_socket_state);
+
+ /* make it easier to write a function calls */
+ conn = s->conn;
+ sock = s->sock;
+
+ c->status = socket_connect_recv(ctx);
+ if (!NT_STATUS_IS_OK(c->status)) {
+ DEBUG(0, ("Failed to connect host %s on port %d - %s\n",
+ s->server->addr, s->server->port,
+ nt_errstr(c->status)));
+ composite_error(c, c->status);
+ return;
+ }
+
+ /*
+ fill in the transport methods
+ */
+ conn->transport.transport = s->transport;
+ conn->transport.private_data = NULL;
+
+ conn->transport.send_request = sock_send_request;
+ conn->transport.send_read = sock_send_read;
+ conn->transport.recv_data = NULL;
+
+ conn->transport.shutdown_pipe = sock_shutdown_pipe;
+ conn->transport.peer_name = sock_peer_name;
+ conn->transport.target_hostname = sock_target_hostname;
+
+ sock->sock = s->socket_ctx;
+ sock->pending_reads = 0;
+ sock->server_name = strupper_talloc(sock, s->target_hostname);
+
+ sock->fde = event_add_fd(conn->event_ctx, sock->sock, socket_get_fd(sock->sock),
+ EVENT_FD_READ, sock_io_handler, conn);
+
+ conn->transport.private_data = sock;
+
+ sock->packet = packet_init(sock);
+ if (sock->packet == NULL) {
+ composite_error(c, NT_STATUS_NO_MEMORY);
+ talloc_free(sock);
+ return;
+ }
+
+ packet_set_private(sock->packet, conn);
+ packet_set_socket(sock->packet, sock->sock);
+ packet_set_callback(sock->packet, sock_process_recv);
+ packet_set_full_request(sock->packet, sock_complete_packet);
+ packet_set_error_handler(sock->packet, sock_error_handler);
+ packet_set_event_context(sock->packet, conn->event_ctx);
+ packet_set_fde(sock->packet, sock->fde);
+ packet_set_serialise(sock->packet);
+ packet_set_initial_read(sock->packet, 16);
+
+ /* ensure we don't get SIGPIPE */
+ BlockSignals(true, SIGPIPE);
+
+ composite_done(c);
+}
+
+
+static struct composite_context *dcerpc_pipe_open_socket_send(TALLOC_CTX *mem_ctx,
+ struct dcerpc_connection *cn,
+ struct socket_address *server,
+ const char *target_hostname,
+ const char *full_path,
+ enum dcerpc_transport_t transport)
+{
+ struct composite_context *c;
+ struct pipe_open_socket_state *s;
+ struct composite_context *conn_req;
+
+ c = composite_create(mem_ctx, cn->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct pipe_open_socket_state);
+ if (composite_nomem(s, c)) return c;
+ c->private_data = s;
+
+ s->conn = cn;
+ s->transport = transport;
+ s->server = talloc_reference(c, server);
+ if (composite_nomem(s->server, c)) return c;
+ s->target_hostname = talloc_reference(s, target_hostname);
+
+ s->sock = talloc(cn, struct sock_private);
+ if (composite_nomem(s->sock, c)) return c;
+
+ c->status = socket_create(server->family, SOCKET_TYPE_STREAM, &s->socket_ctx, 0);
+ if (!composite_is_ok(c)) return c;
+
+ talloc_steal(s->sock, s->socket_ctx);
+
+ s->sock->path = talloc_reference(s->sock, full_path);
+
+ conn_req = socket_connect_send(s->socket_ctx, NULL, s->server, 0,
+ c->event_ctx);
+ composite_continue(c, conn_req, continue_socket_connect, c);
+ return c;
+}
+
+
+static NTSTATUS dcerpc_pipe_open_socket_recv(struct composite_context *c)
+{
+ NTSTATUS status = composite_wait(c);
+
+ talloc_free(c);
+ return status;
+}
+
+struct pipe_tcp_state {
+ const char *server;
+ const char *target_hostname;
+ const char *address;
+ uint32_t port;
+ struct socket_address *srvaddr;
+ struct resolve_context *resolve_ctx;
+ struct dcerpc_connection *conn;
+};
+
+
+#if 0 /* disabled till we can resolve names to ipv6 addresses */
+static void continue_ipv6_open_socket(struct composite_context *ctx);
+#endif
+static void continue_ipv4_open_socket(struct composite_context *ctx);
+static void continue_ip_resolve_name(struct composite_context *ctx);
+
+static void continue_ip_resolve_name(struct composite_context *ctx)
+{
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+ struct pipe_tcp_state *s = talloc_get_type(c->private_data,
+ struct pipe_tcp_state);
+ struct composite_context *sock_ipv4_req;
+
+ c->status = resolve_name_recv(ctx, s, &s->address);
+ if (!composite_is_ok(c)) return;
+
+ /* prepare server address using host ip:port and transport name */
+ s->srvaddr = socket_address_from_strings(s->conn, "ipv4", s->address, s->port);
+ if (composite_nomem(s->srvaddr, c)) return;
+
+ /* resolve_nbt_name gives only ipv4 ... - send socket open request */
+ sock_ipv4_req = dcerpc_pipe_open_socket_send(c, s->conn,
+ s->srvaddr, s->target_hostname,
+ NULL,
+ NCACN_IP_TCP);
+ composite_continue(c, sock_ipv4_req, continue_ipv4_open_socket, c);
+}
+
+/*
+ Stage 2 of dcerpc_pipe_open_tcp_send: receive result of pipe open request
+ on IPv6 and send the request on IPv4 unless IPv6 transport succeeded.
+*/
+#if 0 /* disabled till we can resolve names to ipv6 addresses */
+static void continue_ipv6_open_socket(struct composite_context *ctx)
+{
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+ struct pipe_tcp_state *s = talloc_get_type(c->private_data,
+ struct pipe_tcp_state);
+ struct composite_context *sock_ipv4_req;
+
+ /* receive result of socket open request */
+ c->status = dcerpc_pipe_open_socket_recv(ctx);
+ if (NT_STATUS_IS_OK(c->status)) {
+ composite_done(c);
+ return;
+ }
+
+ talloc_free(s->srvaddr);
+
+ /* prepare server address using host:ip and transport name */
+ s->srvaddr = socket_address_from_strings(s->conn, "ipv4", s->address, s->port);
+ if (composite_nomem(s->srvaddr, c)) return;
+
+ /* try IPv4 if IPv6 fails */
+ sock_ipv4_req = dcerpc_pipe_open_socket_send(c, s->conn,
+ s->srvaddr, s->target_hostname,
+ NCACN_IP_TCP);
+ composite_continue(c, sock_ipv4_req, continue_ipv4_open_socket, c);
+}
+#endif
+
+/*
+ Stage 2 of dcerpc_pipe_open_tcp_send: receive result of pipe open request
+ on IPv4 transport.
+*/
+static void continue_ipv4_open_socket(struct composite_context *ctx)
+{
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+ struct pipe_tcp_state *s = talloc_get_type(c->private_data,
+ struct pipe_tcp_state);
+
+ /* receive result socket open request */
+ c->status = dcerpc_pipe_open_socket_recv(ctx);
+ if (!NT_STATUS_IS_OK(c->status)) {
+ /* something went wrong... */
+ DEBUG(0, ("Failed to connect host %s (%s) on port %d - %s.\n",
+ s->address, s->target_hostname,
+ s->port, nt_errstr(c->status)));
+
+ composite_error(c, c->status);
+ return;
+ }
+
+ composite_done(c);
+}
+
+
+/*
+ Send rpc pipe open request to given host:port using
+ tcp/ip transport
+*/
+struct composite_context* dcerpc_pipe_open_tcp_send(struct dcerpc_connection *conn,
+ const char *server,
+ const char *target_hostname,
+ uint32_t port,
+ struct resolve_context *resolve_ctx)
+{
+ struct composite_context *c;
+ struct pipe_tcp_state *s;
+ struct composite_context *resolve_req;
+ struct nbt_name name;
+
+ /* composite context allocation and setup */
+ c = composite_create(conn, conn->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct pipe_tcp_state);
+ if (composite_nomem(s, c)) return c;
+ c->private_data = s;
+
+ /* store input parameters in state structure */
+ s->server = talloc_strdup(c, server);
+ if (composite_nomem(s->server, c)) return c;
+ if (target_hostname) {
+ s->target_hostname = talloc_strdup(c, target_hostname);
+ if (composite_nomem(s->target_hostname, c)) return c;
+ }
+ s->port = port;
+ s->conn = conn;
+ s->resolve_ctx = resolve_ctx;
+
+ make_nbt_name_server(&name, server);
+ resolve_req = resolve_name_send(resolve_ctx, &name, c->event_ctx);
+ composite_continue(c, resolve_req, continue_ip_resolve_name, c);
+ return c;
+}
+
+/*
+ Receive result of pipe open request on tcp/ip
+*/
+NTSTATUS dcerpc_pipe_open_tcp_recv(struct composite_context *c)
+{
+ NTSTATUS status;
+ status = composite_wait(c);
+
+ talloc_free(c);
+ return status;
+}
+
+
+struct pipe_unix_state {
+ const char *path;
+ struct socket_address *srvaddr;
+ struct dcerpc_connection *conn;
+};
+
+
+/*
+ Stage 2 of dcerpc_pipe_open_unix_stream_send: receive result of pipe open
+ request on unix socket.
+*/
+static void continue_unix_open_socket(struct composite_context *ctx)
+{
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+
+ c->status = dcerpc_pipe_open_socket_recv(ctx);
+ if (NT_STATUS_IS_OK(c->status)) {
+ composite_done(c);
+ return;
+ }
+
+ composite_error(c, c->status);
+}
+
+
+/*
+ Send pipe open request on unix socket
+*/
+struct composite_context *dcerpc_pipe_open_unix_stream_send(struct dcerpc_connection *conn,
+ const char *path)
+{
+ struct composite_context *c;
+ struct composite_context *sock_unix_req;
+ struct pipe_unix_state *s;
+
+ /* composite context allocation and setup */
+ c = composite_create(conn, conn->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct pipe_unix_state);
+ if (composite_nomem(s, c)) return c;
+ c->private_data = s;
+
+ /* store parameters in state structure */
+ s->path = talloc_strdup(c, path);
+ if (composite_nomem(s->path, c)) return c;
+ s->conn = conn;
+
+ /* prepare server address using socket path and transport name */
+ s->srvaddr = socket_address_from_strings(conn, "unix", s->path, 0);
+ if (composite_nomem(s->srvaddr, c)) return c;
+
+ /* send socket open request */
+ sock_unix_req = dcerpc_pipe_open_socket_send(c, s->conn,
+ s->srvaddr, NULL,
+ s->path,
+ NCALRPC);
+ composite_continue(c, sock_unix_req, continue_unix_open_socket, c);
+ return c;
+}
+
+
+/*
+ Receive result of pipe open request on unix socket
+*/
+NTSTATUS dcerpc_pipe_open_unix_stream_recv(struct composite_context *c)
+{
+ NTSTATUS status = composite_wait(c);
+
+ talloc_free(c);
+ return status;
+}
+
+
+/*
+ Stage 2 of dcerpc_pipe_open_pipe_send: receive socket open request
+*/
+static void continue_np_open_socket(struct composite_context *ctx)
+{
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+
+ c->status = dcerpc_pipe_open_socket_recv(ctx);
+ if (!composite_is_ok(c)) return;
+
+ composite_done(c);
+}
+
+
+/*
+ Send pipe open request on ncalrpc
+*/
+struct composite_context* dcerpc_pipe_open_pipe_send(struct dcerpc_connection *conn,
+ const char *ncalrpc_dir,
+ const char *identifier)
+{
+ char *canon = NULL;
+
+ struct composite_context *c;
+ struct composite_context *sock_np_req;
+ struct pipe_unix_state *s;
+
+ /* composite context allocation and setup */
+ c = composite_create(conn, conn->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct pipe_unix_state);
+ if (composite_nomem(s, c)) return c;
+ c->private_data = s;
+
+ /* store parameters in state structure */
+ canon = talloc_strdup(s, identifier);
+ if (composite_nomem(canon, c)) return c;
+ s->conn = conn;
+
+ string_replace(canon, '/', '\\');
+ s->path = talloc_asprintf(canon, "%s/%s", ncalrpc_dir, canon);
+ if (composite_nomem(s->path, c)) return c;
+
+ /* prepare server address using path and transport name */
+ s->srvaddr = socket_address_from_strings(conn, "unix", s->path, 0);
+ if (composite_nomem(s->srvaddr, c)) return c;
+
+ /* send socket open request */
+ sock_np_req = dcerpc_pipe_open_socket_send(c, s->conn, s->srvaddr, NULL, s->path, NCALRPC);
+ composite_continue(c, sock_np_req, continue_np_open_socket, c);
+ return c;
+}
+
+
+/*
+ Receive result of pipe open request on ncalrpc
+*/
+NTSTATUS dcerpc_pipe_open_pipe_recv(struct composite_context *c)
+{
+ NTSTATUS status = composite_wait(c);
+
+ talloc_free(c);
+ return status;
+}
+
+
+/*
+ Open a rpc pipe on a named pipe - sync version
+*/
+NTSTATUS dcerpc_pipe_open_pipe(struct dcerpc_connection *conn, const char *ncalrpc_dir, const char *identifier)
+{
+ struct composite_context *c = dcerpc_pipe_open_pipe_send(conn, ncalrpc_dir, identifier);
+ return dcerpc_pipe_open_pipe_recv(c);
+}
+
+const char *dcerpc_unix_socket_path(struct dcerpc_connection *p)
+{
+ struct sock_private *sock = (struct sock_private *)p->transport.private_data;
+ return sock->path;
+}
+
+struct socket_address *dcerpc_socket_peer_addr(struct dcerpc_connection *p, TALLOC_CTX *mem_ctx)
+{
+ struct sock_private *sock = (struct sock_private *)p->transport.private_data;
+ return socket_get_peer_addr(sock->sock, mem_ctx);
+}
+
diff --git a/source4/librpc/rpc/dcerpc_util.c b/source4/librpc/rpc/dcerpc_util.c
new file mode 100644
index 0000000000..d57cc57e47
--- /dev/null
+++ b/source4/librpc/rpc/dcerpc_util.c
@@ -0,0 +1,748 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ dcerpc utility functions
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Jelmer Vernooij 2004
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/events/events.h"
+#include "libcli/composite/composite.h"
+#include "librpc/gen_ndr/ndr_epmapper_c.h"
+#include "librpc/gen_ndr/ndr_dcerpc.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "librpc/rpc/dcerpc_proto.h"
+#include "auth/credentials/credentials.h"
+#include "param/param.h"
+
+/*
+ find a dcerpc call on an interface by name
+*/
+const struct ndr_interface_call *dcerpc_iface_find_call(const struct ndr_interface_table *iface,
+ const char *name)
+{
+ int i;
+ for (i=0;i<iface->num_calls;i++) {
+ if (strcmp(iface->calls[i].name, name) == 0) {
+ return &iface->calls[i];
+ }
+ }
+ return NULL;
+}
+
+/*
+ push a ncacn_packet into a blob, potentially with auth info
+*/
+NTSTATUS ncacn_push_auth(DATA_BLOB *blob, TALLOC_CTX *mem_ctx,
+ struct smb_iconv_convenience *iconv_convenience,
+ struct ncacn_packet *pkt,
+ struct dcerpc_auth *auth_info)
+{
+ struct ndr_push *ndr;
+ enum ndr_err_code ndr_err;
+
+ ndr = ndr_push_init_ctx(mem_ctx, iconv_convenience);
+ if (!ndr) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!(pkt->drep[0] & DCERPC_DREP_LE)) {
+ ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
+ }
+
+ if (pkt->pfc_flags & DCERPC_PFC_FLAG_OBJECT_UUID) {
+ ndr->flags |= LIBNDR_FLAG_OBJECT_PRESENT;
+ }
+
+ if (auth_info) {
+ pkt->auth_length = auth_info->credentials.length;
+ } else {
+ pkt->auth_length = 0;
+ }
+
+ ndr_err = ndr_push_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ if (auth_info) {
+ ndr_err = ndr_push_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, auth_info);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+ }
+
+ *blob = ndr_push_blob(ndr);
+
+ /* fill in the frag length */
+ dcerpc_set_frag_length(blob, blob->length);
+
+ return NT_STATUS_OK;
+}
+
+
+struct epm_map_binding_state {
+ struct dcerpc_binding *binding;
+ const struct ndr_interface_table *table;
+ struct dcerpc_pipe *pipe;
+ struct policy_handle handle;
+ struct GUID guid;
+ struct epm_twr_t twr;
+ struct epm_twr_t *twr_r;
+ struct epm_Map r;
+};
+
+
+static void continue_epm_recv_binding(struct composite_context *ctx);
+static void continue_epm_map(struct rpc_request *req);
+
+
+/*
+ Stage 2 of epm_map_binding: Receive connected rpc pipe and send endpoint
+ mapping rpc request
+*/
+static void continue_epm_recv_binding(struct composite_context *ctx)
+{
+ struct rpc_request *map_req;
+
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+ struct epm_map_binding_state *s = talloc_get_type(c->private_data,
+ struct epm_map_binding_state);
+
+ /* receive result of rpc pipe connect request */
+ c->status = dcerpc_pipe_connect_b_recv(ctx, c, &s->pipe);
+ if (!composite_is_ok(c)) return;
+
+ s->pipe->conn->flags |= DCERPC_NDR_REF_ALLOC;
+
+ /* prepare requested binding parameters */
+ s->binding->object = s->table->syntax_id;
+
+ c->status = dcerpc_binding_build_tower(s->pipe, s->binding, &s->twr.tower);
+ if (!composite_is_ok(c)) return;
+
+ /* with some nice pretty paper around it of course */
+ s->r.in.object = &s->guid;
+ s->r.in.map_tower = &s->twr;
+ s->r.in.entry_handle = &s->handle;
+ s->r.in.max_towers = 1;
+ s->r.out.entry_handle = &s->handle;
+
+ /* send request for an endpoint mapping - a rpc request on connected pipe */
+ map_req = dcerpc_epm_Map_send(s->pipe, c, &s->r);
+ if (composite_nomem(map_req, c)) return;
+
+ composite_continue_rpc(c, map_req, continue_epm_map, c);
+}
+
+
+/*
+ Stage 3 of epm_map_binding: Receive endpoint mapping and provide binding details
+*/
+static void continue_epm_map(struct rpc_request *req)
+{
+ struct composite_context *c = talloc_get_type(req->async.private_data,
+ struct composite_context);
+ struct epm_map_binding_state *s = talloc_get_type(c->private_data,
+ struct epm_map_binding_state);
+
+ /* receive result of a rpc request */
+ c->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(c)) return;
+
+ /* check the details */
+ if (s->r.out.result != 0 || *s->r.out.num_towers != 1) {
+ composite_error(c, NT_STATUS_PORT_UNREACHABLE);
+ return;
+ }
+
+ s->twr_r = s->r.out.towers[0].twr;
+ if (s->twr_r == NULL) {
+ composite_error(c, NT_STATUS_PORT_UNREACHABLE);
+ return;
+ }
+
+ if (s->twr_r->tower.num_floors != s->twr.tower.num_floors ||
+ s->twr_r->tower.floors[3].lhs.protocol != s->twr.tower.floors[3].lhs.protocol) {
+ composite_error(c, NT_STATUS_PORT_UNREACHABLE);
+ return;
+ }
+
+ /* get received endpoint */
+ s->binding->endpoint = talloc_reference(s->binding,
+ dcerpc_floor_get_rhs_data(c, &s->twr_r->tower.floors[3]));
+ if (composite_nomem(s->binding->endpoint, c)) return;
+
+ composite_done(c);
+}
+
+
+/*
+ Request for endpoint mapping of dcerpc binding - try to request for endpoint
+ unless there is default one.
+*/
+struct composite_context *dcerpc_epm_map_binding_send(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding *binding,
+ const struct ndr_interface_table *table,
+ struct tevent_context *ev,
+ struct loadparm_context *lp_ctx)
+{
+ struct composite_context *c;
+ struct epm_map_binding_state *s;
+ struct composite_context *pipe_connect_req;
+ struct cli_credentials *anon_creds;
+
+ NTSTATUS status;
+ struct dcerpc_binding *epmapper_binding;
+ int i;
+
+ if (ev == NULL) {
+ return NULL;
+ }
+
+ /* composite context allocation and setup */
+ c = composite_create(mem_ctx, ev);
+ if (c == NULL) {
+ return NULL;
+ }
+
+ s = talloc_zero(c, struct epm_map_binding_state);
+ if (composite_nomem(s, c)) return c;
+ c->private_data = s;
+
+ s->binding = binding;
+ s->table = table;
+
+ /* anonymous credentials for rpc connection used to get endpoint mapping */
+ anon_creds = cli_credentials_init(mem_ctx);
+ cli_credentials_set_anonymous(anon_creds);
+
+ /*
+ First, check if there is a default endpoint specified in the IDL
+ */
+ if (table != NULL) {
+ struct dcerpc_binding *default_binding;
+
+ /* Find one of the default pipes for this interface */
+ for (i = 0; i < table->endpoints->count; i++) {
+ status = dcerpc_parse_binding(mem_ctx, table->endpoints->names[i], &default_binding);
+
+ if (NT_STATUS_IS_OK(status)) {
+ if (binding->transport == NCA_UNKNOWN)
+ binding->transport = default_binding->transport;
+ if (default_binding->transport == binding->transport &&
+ default_binding->endpoint) {
+ binding->endpoint = talloc_reference(binding, default_binding->endpoint);
+ talloc_free(default_binding);
+
+ composite_done(c);
+ return c;
+
+ } else {
+ talloc_free(default_binding);
+ }
+ }
+ }
+ }
+
+ epmapper_binding = talloc_zero(c, struct dcerpc_binding);
+ if (composite_nomem(epmapper_binding, c)) return c;
+
+ /* basic endpoint mapping data */
+ epmapper_binding->transport = binding->transport;
+ epmapper_binding->host = talloc_reference(epmapper_binding, binding->host);
+ epmapper_binding->target_hostname = epmapper_binding->host;
+ epmapper_binding->options = NULL;
+ epmapper_binding->flags = 0;
+ epmapper_binding->assoc_group_id = 0;
+ epmapper_binding->endpoint = NULL;
+
+ /* initiate rpc pipe connection */
+ pipe_connect_req = dcerpc_pipe_connect_b_send(c, epmapper_binding,
+ &ndr_table_epmapper,
+ anon_creds, c->event_ctx,
+ lp_ctx);
+ if (composite_nomem(pipe_connect_req, c)) return c;
+
+ composite_continue(c, pipe_connect_req, continue_epm_recv_binding, c);
+ return c;
+}
+
+
+/*
+ Receive result of endpoint mapping request
+ */
+NTSTATUS dcerpc_epm_map_binding_recv(struct composite_context *c)
+{
+ NTSTATUS status = composite_wait(c);
+
+ talloc_free(c);
+ return status;
+}
+
+
+/*
+ Get endpoint mapping for rpc connection
+*/
+_PUBLIC_ NTSTATUS dcerpc_epm_map_binding(TALLOC_CTX *mem_ctx, struct dcerpc_binding *binding,
+ const struct ndr_interface_table *table, struct tevent_context *ev,
+ struct loadparm_context *lp_ctx)
+{
+ struct composite_context *c;
+
+ c = dcerpc_epm_map_binding_send(mem_ctx, binding, table, ev, lp_ctx);
+ return dcerpc_epm_map_binding_recv(c);
+}
+
+
+struct pipe_auth_state {
+ struct dcerpc_pipe *pipe;
+ struct dcerpc_binding *binding;
+ const struct ndr_interface_table *table;
+ struct loadparm_context *lp_ctx;
+ struct cli_credentials *credentials;
+};
+
+
+static void continue_auth_schannel(struct composite_context *ctx);
+static void continue_auth(struct composite_context *ctx);
+static void continue_auth_none(struct composite_context *ctx);
+static void continue_ntlmssp_connection(struct composite_context *ctx);
+static void continue_spnego_after_wrong_pass(struct composite_context *ctx);
+
+
+/*
+ Stage 2 of pipe_auth: Receive result of schannel bind request
+*/
+static void continue_auth_schannel(struct composite_context *ctx)
+{
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+
+ c->status = dcerpc_bind_auth_schannel_recv(ctx);
+ if (!composite_is_ok(c)) return;
+
+ composite_done(c);
+}
+
+
+/*
+ Stage 2 of pipe_auth: Receive result of authenticated bind request
+*/
+static void continue_auth(struct composite_context *ctx)
+{
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+
+ c->status = dcerpc_bind_auth_recv(ctx);
+ if (!composite_is_ok(c)) return;
+
+ composite_done(c);
+}
+/*
+ Stage 2 of pipe_auth: Receive result of authenticated bind request, but handle fallbacks:
+ SPNEGO -> NTLMSSP
+*/
+static void continue_auth_auto(struct composite_context *ctx)
+{
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+ struct pipe_auth_state *s = talloc_get_type(c->private_data, struct pipe_auth_state);
+ struct composite_context *sec_conn_req;
+
+ c->status = dcerpc_bind_auth_recv(ctx);
+ if (NT_STATUS_EQUAL(c->status, NT_STATUS_INVALID_PARAMETER)) {
+ /*
+ * Retry with NTLMSSP auth as fallback
+ * send a request for secondary rpc connection
+ */
+ sec_conn_req = dcerpc_secondary_connection_send(s->pipe,
+ s->binding);
+ composite_continue(c, sec_conn_req, continue_ntlmssp_connection, c);
+ return;
+ } else if (NT_STATUS_EQUAL(c->status, NT_STATUS_LOGON_FAILURE)) {
+ if (cli_credentials_wrong_password(s->credentials)) {
+ /*
+ * Retry SPNEGO with a better password
+ * send a request for secondary rpc connection
+ */
+ sec_conn_req = dcerpc_secondary_connection_send(s->pipe,
+ s->binding);
+ composite_continue(c, sec_conn_req, continue_spnego_after_wrong_pass, c);
+ return;
+ }
+ }
+
+ if (!composite_is_ok(c)) return;
+
+ composite_done(c);
+}
+
+/*
+ Stage 3 of pipe_auth (fallback to NTLMSSP case): Receive secondary
+ rpc connection (the first one can't be used any more, due to the
+ bind nak) and perform authenticated bind request
+*/
+static void continue_ntlmssp_connection(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct pipe_auth_state *s;
+ struct composite_context *auth_req;
+ struct dcerpc_pipe *p2;
+
+ c = talloc_get_type(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct pipe_auth_state);
+
+ /* receive secondary rpc connection */
+ c->status = dcerpc_secondary_connection_recv(ctx, &p2);
+ if (!composite_is_ok(c)) return;
+
+ talloc_steal(s, p2);
+ talloc_steal(p2, s->pipe);
+ s->pipe = p2;
+
+ /* initiate a authenticated bind */
+ auth_req = dcerpc_bind_auth_send(c, s->pipe, s->table,
+ s->credentials,
+ lp_gensec_settings(c, s->lp_ctx),
+ DCERPC_AUTH_TYPE_NTLMSSP,
+ dcerpc_auth_level(s->pipe->conn),
+ s->table->authservices->names[0]);
+ composite_continue(c, auth_req, continue_auth, c);
+}
+
+/*
+ Stage 3 of pipe_auth (retry on wrong password): Receive secondary
+ rpc connection (the first one can't be used any more, due to the
+ bind nak) and perform authenticated bind request
+*/
+static void continue_spnego_after_wrong_pass(struct composite_context *ctx)
+{
+ struct composite_context *c;
+ struct pipe_auth_state *s;
+ struct composite_context *auth_req;
+ struct dcerpc_pipe *p2;
+
+ c = talloc_get_type(ctx->async.private_data, struct composite_context);
+ s = talloc_get_type(c->private_data, struct pipe_auth_state);
+
+ /* receive secondary rpc connection */
+ c->status = dcerpc_secondary_connection_recv(ctx, &p2);
+ if (!composite_is_ok(c)) return;
+
+ talloc_steal(s, p2);
+ talloc_steal(p2, s->pipe);
+ s->pipe = p2;
+
+ /* initiate a authenticated bind */
+ auth_req = dcerpc_bind_auth_send(c, s->pipe, s->table,
+ s->credentials,
+ lp_gensec_settings(c, s->lp_ctx),
+ DCERPC_AUTH_TYPE_SPNEGO,
+ dcerpc_auth_level(s->pipe->conn),
+ s->table->authservices->names[0]);
+ composite_continue(c, auth_req, continue_auth, c);
+}
+
+
+/*
+ Stage 2 of pipe_auth: Receive result of non-authenticated bind request
+*/
+static void continue_auth_none(struct composite_context *ctx)
+{
+ struct composite_context *c = talloc_get_type(ctx->async.private_data,
+ struct composite_context);
+
+ c->status = dcerpc_bind_auth_none_recv(ctx);
+ if (!composite_is_ok(c)) return;
+
+ composite_done(c);
+}
+
+
+/*
+ Request to perform an authenticated bind if required. Authentication
+ is determined using credentials passed and binding flags.
+*/
+struct composite_context *dcerpc_pipe_auth_send(struct dcerpc_pipe *p,
+ struct dcerpc_binding *binding,
+ const struct ndr_interface_table *table,
+ struct cli_credentials *credentials,
+ struct loadparm_context *lp_ctx)
+{
+ struct composite_context *c;
+ struct pipe_auth_state *s;
+ struct composite_context *auth_schannel_req;
+ struct composite_context *auth_req;
+ struct composite_context *auth_none_req;
+ struct dcerpc_connection *conn;
+ uint8_t auth_type;
+
+ /* composite context allocation and setup */
+ c = composite_create(p, p->conn->event_ctx);
+ if (c == NULL) return NULL;
+
+ s = talloc_zero(c, struct pipe_auth_state);
+ if (composite_nomem(s, c)) return c;
+ c->private_data = s;
+
+ /* store parameters in state structure */
+ s->binding = binding;
+ s->table = table;
+ s->credentials = credentials;
+ s->pipe = p;
+ s->lp_ctx = lp_ctx;
+
+ conn = s->pipe->conn;
+ conn->flags = binding->flags;
+
+ /* remember the binding string for possible secondary connections */
+ conn->binding_string = dcerpc_binding_string(p, binding);
+
+ if (cli_credentials_is_anonymous(s->credentials)) {
+ auth_none_req = dcerpc_bind_auth_none_send(c, s->pipe, s->table);
+ composite_continue(c, auth_none_req, continue_auth_none, c);
+ return c;
+ }
+
+ if ((binding->flags & DCERPC_SCHANNEL) &&
+ !cli_credentials_get_netlogon_creds(s->credentials)) {
+ /* If we don't already have netlogon credentials for
+ * the schannel bind, then we have to get these
+ * first */
+ auth_schannel_req = dcerpc_bind_auth_schannel_send(c, s->pipe, s->table,
+ s->credentials, s->lp_ctx,
+ dcerpc_auth_level(conn));
+ composite_continue(c, auth_schannel_req, continue_auth_schannel, c);
+ return c;
+ }
+
+ /*
+ * we rely on the already authenticated CIFS connection
+ * if not doing sign or seal
+ */
+ if (conn->transport.transport == NCACN_NP &&
+ !(s->binding->flags & (DCERPC_SIGN|DCERPC_SEAL))) {
+ auth_none_req = dcerpc_bind_auth_none_send(c, s->pipe, s->table);
+ composite_continue(c, auth_none_req, continue_auth_none, c);
+ return c;
+ }
+
+
+ /* Perform an authenticated DCE-RPC bind
+ */
+ if (!(conn->flags & (DCERPC_SIGN|DCERPC_SEAL))) {
+ /*
+ we are doing an authenticated connection,
+ but not using sign or seal. We must force
+ the CONNECT dcerpc auth type as a NONE auth
+ type doesn't allow authentication
+ information to be passed.
+ */
+ conn->flags |= DCERPC_CONNECT;
+ }
+
+ if (s->binding->flags & DCERPC_AUTH_SPNEGO) {
+ auth_type = DCERPC_AUTH_TYPE_SPNEGO;
+
+ } else if (s->binding->flags & DCERPC_AUTH_KRB5) {
+ auth_type = DCERPC_AUTH_TYPE_KRB5;
+
+ } else if (s->binding->flags & DCERPC_SCHANNEL) {
+ auth_type = DCERPC_AUTH_TYPE_SCHANNEL;
+
+ } else if (s->binding->flags & DCERPC_AUTH_NTLM) {
+ auth_type = DCERPC_AUTH_TYPE_NTLMSSP;
+
+ } else {
+ /* try SPNEGO with fallback to NTLMSSP */
+ auth_req = dcerpc_bind_auth_send(c, s->pipe, s->table,
+ s->credentials,
+ lp_gensec_settings(c, s->lp_ctx),
+ DCERPC_AUTH_TYPE_SPNEGO,
+ dcerpc_auth_level(conn),
+ s->table->authservices->names[0]);
+ composite_continue(c, auth_req, continue_auth_auto, c);
+ return c;
+ }
+
+ auth_req = dcerpc_bind_auth_send(c, s->pipe, s->table,
+ s->credentials,
+ lp_gensec_settings(c, s->lp_ctx),
+ auth_type,
+ dcerpc_auth_level(conn),
+ s->table->authservices->names[0]);
+ composite_continue(c, auth_req, continue_auth, c);
+ return c;
+}
+
+
+/*
+ Receive result of authenticated bind request on dcerpc pipe
+
+ This returns *p, which may be different to the one originally
+ supllied, as it rebinds to a new pipe due to authentication fallback
+
+*/
+NTSTATUS dcerpc_pipe_auth_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
+ struct dcerpc_pipe **p)
+{
+ NTSTATUS status;
+
+ struct pipe_auth_state *s = talloc_get_type(c->private_data,
+ struct pipe_auth_state);
+ status = composite_wait(c);
+ if (!NT_STATUS_IS_OK(status)) {
+ char *uuid_str = GUID_string(s->pipe, &s->table->syntax_id.uuid);
+ DEBUG(0, ("Failed to bind to uuid %s - %s\n", uuid_str, nt_errstr(status)));
+ talloc_free(uuid_str);
+ } else {
+ talloc_steal(mem_ctx, s->pipe);
+ *p = s->pipe;
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+
+/*
+ Perform an authenticated bind if needed - sync version
+
+ This may change *p, as it rebinds to a new pipe due to authentication fallback
+*/
+_PUBLIC_ NTSTATUS dcerpc_pipe_auth(TALLOC_CTX *mem_ctx,
+ struct dcerpc_pipe **p,
+ struct dcerpc_binding *binding,
+ const struct ndr_interface_table *table,
+ struct cli_credentials *credentials,
+ struct loadparm_context *lp_ctx)
+{
+ struct composite_context *c;
+
+ c = dcerpc_pipe_auth_send(*p, binding, table, credentials, lp_ctx);
+ return dcerpc_pipe_auth_recv(c, mem_ctx, p);
+}
+
+
+NTSTATUS dcerpc_generic_session_key(struct dcerpc_connection *c,
+ DATA_BLOB *session_key)
+{
+ /* this took quite a few CPU cycles to find ... */
+ session_key->data = discard_const_p(unsigned char, "SystemLibraryDTC");
+ session_key->length = 16;
+ return NT_STATUS_OK;
+}
+
+/*
+ fetch the user session key - may be default (above) or the SMB session key
+
+ The key is always truncated to 16 bytes
+*/
+_PUBLIC_ NTSTATUS dcerpc_fetch_session_key(struct dcerpc_pipe *p,
+ DATA_BLOB *session_key)
+{
+ NTSTATUS status;
+ status = p->conn->security_state.session_key(p->conn, session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ session_key->length = MIN(session_key->length, 16);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ log a rpc packet in a format suitable for ndrdump. This is especially useful
+ for sealed packets, where ethereal cannot easily see the contents
+
+ this triggers on a debug level of >= 10
+*/
+_PUBLIC_ void dcerpc_log_packet(const char *lockdir,
+ const struct ndr_interface_table *ndr,
+ uint32_t opnum, uint32_t flags,
+ DATA_BLOB *pkt)
+{
+ const int num_examples = 20;
+ int i;
+
+ if (lockdir == NULL) return;
+
+ for (i=0;i<num_examples;i++) {
+ char *name=NULL;
+ asprintf(&name, "%s/rpclog/%s-%u.%d.%s",
+ lockdir, ndr->name, opnum, i,
+ (flags&NDR_IN)?"in":"out");
+ if (name == NULL) {
+ return;
+ }
+ if (!file_exist(name)) {
+ if (file_save(name, pkt->data, pkt->length)) {
+ DEBUG(10,("Logged rpc packet to %s\n", name));
+ }
+ free(name);
+ break;
+ }
+ free(name);
+ }
+}
+
+
+
+/*
+ create a secondary context from a primary connection
+
+ this uses dcerpc_alter_context() to create a new dcerpc context_id
+*/
+_PUBLIC_ NTSTATUS dcerpc_secondary_context(struct dcerpc_pipe *p,
+ struct dcerpc_pipe **pp2,
+ const struct ndr_interface_table *table)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p2;
+
+ p2 = talloc_zero(p, struct dcerpc_pipe);
+ if (p2 == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ p2->conn = talloc_reference(p2, p->conn);
+ p2->request_timeout = p->request_timeout;
+
+ p2->context_id = ++p->conn->next_context_id;
+
+ p2->syntax = table->syntax_id;
+
+ p2->transfer_syntax = ndr_transfer_syntax;
+
+ p2->binding = talloc_reference(p2, p->binding);
+
+ status = dcerpc_alter_context(p2, p2, &p2->syntax, &p2->transfer_syntax);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(p2);
+ return status;
+ }
+
+ *pp2 = p2;
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/librpc/rpc/pyrpc.c b/source4/librpc/rpc/pyrpc.c
new file mode 100644
index 0000000000..252883c851
--- /dev/null
+++ b/source4/librpc/rpc/pyrpc.c
@@ -0,0 +1,493 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Jelmer Vernooij <jelmer@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include <Python.h>
+#include <structmember.h>
+#include "librpc/rpc/pyrpc.h"
+#include "librpc/rpc/dcerpc.h"
+#include "lib/events/events.h"
+#include "param/pyparam.h"
+
+#ifndef Py_RETURN_NONE
+#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
+#endif
+
+static PyObject *py_dcerpc_run_function(dcerpc_InterfaceObject *iface,
+ const struct PyNdrRpcMethodDef *md,
+ PyObject *args, PyObject *kwargs)
+{
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+ void *r;
+ PyObject *result = Py_None;
+
+ if (md->pack_in_data == NULL || md->unpack_out_data == NULL) {
+ PyErr_SetString(PyExc_NotImplementedError, "No marshalling code available yet");
+ return NULL;
+ }
+
+ mem_ctx = talloc_new(NULL);
+ if (mem_ctx == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ r = talloc_zero_size(mem_ctx, md->table->calls[md->opnum].struct_size);
+ if (r == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ if (!md->pack_in_data(args, kwargs, r)) {
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ status = md->call(iface->pipe, mem_ctx, r);
+ if (NT_STATUS_IS_ERR(status)) {
+ PyErr_SetDCERPCStatus(iface->pipe, status);
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ result = md->unpack_out_data(r);
+
+ talloc_free(mem_ctx);
+ return result;
+}
+
+static PyObject *py_dcerpc_call_wrapper(PyObject *self, PyObject *args, void *wrapped, PyObject *kwargs)
+{
+ dcerpc_InterfaceObject *iface = (dcerpc_InterfaceObject *)self;
+ const struct PyNdrRpcMethodDef *md = (const struct PyNdrRpcMethodDef *)wrapped;
+
+ return py_dcerpc_run_function(iface, md, args, kwargs);
+}
+
+
+bool PyInterface_AddNdrRpcMethods(PyTypeObject *ifacetype, const struct PyNdrRpcMethodDef *mds)
+{
+ int i;
+ for (i = 0; mds[i].name; i++) {
+ PyObject *ret;
+ struct wrapperbase *wb = (struct wrapperbase *)calloc(sizeof(struct wrapperbase), 1);
+
+ wb->name = discard_const_p(char, mds[i].name);
+ wb->flags = PyWrapperFlag_KEYWORDS;
+ wb->wrapper = (wrapperfunc)py_dcerpc_call_wrapper;
+ wb->doc = discard_const_p(char, mds[i].doc);
+
+ ret = PyDescr_NewWrapper(ifacetype, wb, discard_const_p(void, &mds[i]));
+
+ PyDict_SetItemString(ifacetype->tp_dict, mds[i].name,
+ (PyObject *)ret);
+ }
+
+ return true;
+}
+
+static bool PyString_AsGUID(PyObject *object, struct GUID *uuid)
+{
+ NTSTATUS status;
+ status = GUID_from_string(PyString_AsString(object), uuid);
+ if (NT_STATUS_IS_ERR(status)) {
+ PyErr_SetNTSTATUS(status);
+ return false;
+ }
+ return true;
+}
+
+static bool ndr_syntax_from_py_object(PyObject *object, struct ndr_syntax_id *syntax_id)
+{
+ ZERO_STRUCTP(syntax_id);
+
+ if (PyString_Check(object)) {
+ return PyString_AsGUID(object, &syntax_id->uuid);
+ } else if (PyTuple_Check(object)) {
+ if (PyTuple_Size(object) < 1 || PyTuple_Size(object) > 2) {
+ PyErr_SetString(PyExc_ValueError, "Syntax ID tuple has invalid size");
+ return false;
+ }
+
+ if (!PyString_Check(PyTuple_GetItem(object, 0))) {
+ PyErr_SetString(PyExc_ValueError, "Expected GUID as first element in tuple");
+ return false;
+ }
+
+ if (!PyString_AsGUID(PyTuple_GetItem(object, 0), &syntax_id->uuid))
+ return false;
+
+ if (!PyInt_Check(PyTuple_GetItem(object, 1))) {
+ PyErr_SetString(PyExc_ValueError, "Expected version as second element in tuple");
+ return false;
+ }
+
+ syntax_id->if_version = PyInt_AsLong(PyTuple_GetItem(object, 1));
+ return true;
+ }
+
+ PyErr_SetString(PyExc_TypeError, "Expected UUID or syntax id tuple");
+ return false;
+}
+
+static PyObject *py_iface_server_name(PyObject *obj, void *closure)
+{
+ const char *server_name;
+ dcerpc_InterfaceObject *iface = (dcerpc_InterfaceObject *)obj;
+
+ server_name = dcerpc_server_name(iface->pipe);
+ if (server_name == NULL)
+ Py_RETURN_NONE;
+
+ return PyString_FromString(server_name);
+}
+
+static PyObject *py_ndr_syntax_id(struct ndr_syntax_id *syntax_id)
+{
+ PyObject *ret;
+ char *uuid_str;
+
+ uuid_str = GUID_string(NULL, &syntax_id->uuid);
+ if (uuid_str == NULL)
+ return NULL;
+
+ ret = Py_BuildValue("(s,i)", uuid_str, syntax_id->if_version);
+
+ talloc_free(uuid_str);
+
+ return ret;
+}
+
+static PyObject *py_iface_abstract_syntax(PyObject *obj, void *closure)
+{
+ dcerpc_InterfaceObject *iface = (dcerpc_InterfaceObject *)obj;
+
+ return py_ndr_syntax_id(&iface->pipe->syntax);
+}
+
+static PyObject *py_iface_transfer_syntax(PyObject *obj, void *closure)
+{
+ dcerpc_InterfaceObject *iface = (dcerpc_InterfaceObject *)obj;
+
+ return py_ndr_syntax_id(&iface->pipe->transfer_syntax);
+}
+
+static PyGetSetDef dcerpc_interface_getsetters[] = {
+ { discard_const_p(char, "server_name"), py_iface_server_name, NULL,
+ discard_const_p(char, "name of the server, if connected over SMB") },
+ { discard_const_p(char, "abstract_syntax"), py_iface_abstract_syntax, NULL,
+ discard_const_p(char, "syntax id of the abstract syntax") },
+ { discard_const_p(char, "transfer_syntax"), py_iface_transfer_syntax, NULL,
+ discard_const_p(char, "syntax id of the transfersyntax") },
+ { NULL }
+};
+
+static PyMemberDef dcerpc_interface_members[] = {
+ { discard_const_p(char, "request_timeout"), T_INT,
+ offsetof(struct dcerpc_pipe, request_timeout), 0,
+ discard_const_p(char, "request timeout, in seconds") },
+ { NULL }
+};
+
+void PyErr_SetDCERPCStatus(struct dcerpc_pipe *p, NTSTATUS status)
+{
+ if (p != NULL && NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ const char *errstr = dcerpc_errstr(NULL, p->last_fault_code);
+ PyErr_SetObject(PyExc_RuntimeError,
+ Py_BuildValue("(i,s)", p->last_fault_code,
+ errstr));
+ } else {
+ PyErr_SetNTSTATUS(status);
+ }
+}
+
+static PyObject *py_iface_request(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ dcerpc_InterfaceObject *iface = (dcerpc_InterfaceObject *)self;
+ int opnum;
+ DATA_BLOB data_in, data_out;
+ NTSTATUS status;
+ char *in_data;
+ int in_length;
+ PyObject *ret;
+ PyObject *object = NULL;
+ struct GUID object_guid;
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+ const char *kwnames[] = { "opnum", "data", "object", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "is#|O:request",
+ discard_const_p(char *, kwnames), &opnum, &in_data, &in_length, &object)) {
+ return NULL;
+ }
+
+ data_in.data = (uint8_t *)talloc_memdup(mem_ctx, in_data, in_length);
+ data_in.length = in_length;
+
+ ZERO_STRUCT(data_out);
+
+ if (object != NULL && !PyString_AsGUID(object, &object_guid)) {
+ return NULL;
+ }
+
+ status = dcerpc_request(iface->pipe, object?&object_guid:NULL,
+ opnum, false, mem_ctx, &data_in, &data_out);
+
+ if (NT_STATUS_IS_ERR(status)) {
+ PyErr_SetDCERPCStatus(iface->pipe, status);
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ ret = PyString_FromStringAndSize((char *)data_out.data, data_out.length);
+
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+static PyObject *py_iface_alter_context(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ dcerpc_InterfaceObject *iface = (dcerpc_InterfaceObject *)self;
+ NTSTATUS status;
+ const char *kwnames[] = { "abstract_syntax", "transfer_syntax", NULL };
+ PyObject *py_abstract_syntax = Py_None, *py_transfer_syntax = Py_None;
+ struct ndr_syntax_id abstract_syntax, transfer_syntax;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O:alter_context",
+ discard_const_p(char *, kwnames), &py_abstract_syntax,
+ &py_transfer_syntax)) {
+ return NULL;
+ }
+
+ if (!ndr_syntax_from_py_object(py_abstract_syntax, &abstract_syntax))
+ return NULL;
+
+ if (py_transfer_syntax == Py_None) {
+ transfer_syntax = ndr_transfer_syntax;
+ } else {
+ if (!ndr_syntax_from_py_object(py_transfer_syntax,
+ &transfer_syntax))
+ return NULL;
+ }
+
+ status = dcerpc_alter_context(iface->pipe, iface->pipe, &abstract_syntax,
+ &transfer_syntax);
+
+ if (NT_STATUS_IS_ERR(status)) {
+ PyErr_SetDCERPCStatus(iface->pipe, status);
+ return NULL;
+ }
+
+ Py_RETURN_NONE;
+}
+
+PyObject *py_dcerpc_interface_init_helper(PyTypeObject *type, PyObject *args, PyObject *kwargs, const struct ndr_interface_table *table)
+{
+ dcerpc_InterfaceObject *ret;
+ const char *binding_string;
+ struct cli_credentials *credentials;
+ struct loadparm_context *lp_ctx = NULL;
+ PyObject *py_lp_ctx = Py_None, *py_credentials = Py_None, *py_basis = Py_None;
+ TALLOC_CTX *mem_ctx = NULL;
+ struct tevent_context *event_ctx;
+ NTSTATUS status;
+
+ const char *kwnames[] = {
+ "binding", "lp_ctx", "credentials", "basis_connection", NULL
+ };
+ extern struct cli_credentials *cli_credentials_from_py_object(PyObject *py_obj);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|OOO:samr", discard_const_p(char *, kwnames), &binding_string, &py_lp_ctx, &py_credentials, &py_basis)) {
+ return NULL;
+ }
+
+ lp_ctx = lp_from_py_object(py_lp_ctx);
+ if (lp_ctx == NULL) {
+ PyErr_SetString(PyExc_TypeError, "Expected loadparm context");
+ return NULL;
+ }
+
+ status = dcerpc_init(lp_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ PyErr_SetNTSTATUS(status);
+ return NULL;
+ }
+ credentials = cli_credentials_from_py_object(py_credentials);
+ if (credentials == NULL) {
+ PyErr_SetString(PyExc_TypeError, "Expected credentials");
+ return NULL;
+ }
+ ret = PyObject_New(dcerpc_InterfaceObject, type);
+
+ event_ctx = event_context_init(mem_ctx);
+
+ if (py_basis != Py_None) {
+ struct dcerpc_pipe *base_pipe;
+
+ if (!PyObject_TypeCheck(py_basis, &dcerpc_InterfaceType)) {
+ PyErr_SetString(PyExc_ValueError, "basis_connection must be a DCE/RPC connection");
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ base_pipe = ((dcerpc_InterfaceObject *)py_basis)->pipe;
+
+ status = dcerpc_secondary_context(base_pipe, &ret->pipe, table);
+ } else {
+ status = dcerpc_pipe_connect(NULL, &ret->pipe, binding_string,
+ table, credentials, event_ctx, lp_ctx);
+ }
+ if (NT_STATUS_IS_ERR(status)) {
+ PyErr_SetNTSTATUS(status);
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ ret->pipe->conn->flags |= DCERPC_NDR_REF_ALLOC;
+ return (PyObject *)ret;
+}
+
+static PyMethodDef dcerpc_interface_methods[] = {
+ { "request", (PyCFunction)py_iface_request, METH_VARARGS|METH_KEYWORDS, "S.request(opnum, data, object=None) -> data\nMake a raw request" },
+ { "alter_context", (PyCFunction)py_iface_alter_context, METH_VARARGS|METH_KEYWORDS, "S.alter_context(syntax)\nChange to a different interface" },
+ { NULL, NULL, 0, NULL },
+};
+
+
+static void dcerpc_interface_dealloc(PyObject* self)
+{
+ dcerpc_InterfaceObject *interface = (dcerpc_InterfaceObject *)self;
+ talloc_free(interface->pipe);
+ PyObject_Del(self);
+}
+
+static PyObject *dcerpc_interface_new(PyTypeObject *self, PyObject *args, PyObject *kwargs)
+{
+ dcerpc_InterfaceObject *ret;
+ const char *binding_string;
+ struct cli_credentials *credentials;
+ struct loadparm_context *lp_ctx = NULL;
+ PyObject *py_lp_ctx = Py_None, *py_credentials = Py_None;
+ TALLOC_CTX *mem_ctx = NULL;
+ struct tevent_context *event_ctx;
+ NTSTATUS status;
+
+ PyObject *syntax, *py_basis = Py_None;
+ const char *kwnames[] = {
+ "binding", "syntax", "lp_ctx", "credentials", "basis_connection", NULL
+ };
+ extern struct cli_credentials *cli_credentials_from_py_object(PyObject *py_obj);
+ struct ndr_interface_table *table;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO|OOO:connect", discard_const_p(char *, kwnames), &binding_string, &syntax, &py_lp_ctx, &py_credentials, &py_basis)) {
+ return NULL;
+ }
+
+ lp_ctx = lp_from_py_object(py_lp_ctx);
+ if (lp_ctx == NULL) {
+ PyErr_SetString(PyExc_TypeError, "Expected loadparm context");
+ return NULL;
+ }
+
+ credentials = cli_credentials_from_py_object(py_credentials);
+ if (credentials == NULL) {
+ PyErr_SetString(PyExc_TypeError, "Expected credentials");
+ return NULL;
+ }
+ ret = PyObject_New(dcerpc_InterfaceObject, &dcerpc_InterfaceType);
+
+ event_ctx = s4_event_context_init(mem_ctx);
+
+ /* Create a dummy interface table struct. TODO: In the future, we should rather just allow
+ * connecting without requiring an interface table.
+ */
+
+ table = talloc_zero(mem_ctx, struct ndr_interface_table);
+
+ if (table == NULL) {
+ PyErr_SetString(PyExc_MemoryError, "Allocating interface table");
+ return NULL;
+ }
+
+ if (!ndr_syntax_from_py_object(syntax, &table->syntax_id)) {
+ return NULL;
+ }
+
+ ret->pipe = NULL;
+
+ if (py_basis != Py_None) {
+ struct dcerpc_pipe *base_pipe;
+
+ if (!PyObject_TypeCheck(py_basis, &dcerpc_InterfaceType)) {
+ PyErr_SetString(PyExc_ValueError, "basis_connection must be a DCE/RPC connection");
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ base_pipe = ((dcerpc_InterfaceObject *)py_basis)->pipe;
+
+ status = dcerpc_secondary_context(base_pipe, &ret->pipe,
+ table);
+ ret->pipe = talloc_steal(NULL, ret->pipe);
+ } else {
+ status = dcerpc_pipe_connect(NULL, &ret->pipe, binding_string,
+ table, credentials, event_ctx, lp_ctx);
+ }
+
+ if (NT_STATUS_IS_ERR(status)) {
+ PyErr_SetDCERPCStatus(ret->pipe, status);
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+ ret->pipe->conn->flags |= DCERPC_NDR_REF_ALLOC;
+ return (PyObject *)ret;
+}
+
+PyTypeObject dcerpc_InterfaceType = {
+ PyObject_HEAD_INIT(NULL) 0,
+ .tp_name = "dcerpc.ClientConnection",
+ .tp_basicsize = sizeof(dcerpc_InterfaceObject),
+ .tp_dealloc = dcerpc_interface_dealloc,
+ .tp_getset = dcerpc_interface_getsetters,
+ .tp_members = dcerpc_interface_members,
+ .tp_methods = dcerpc_interface_methods,
+ .tp_doc = "ClientConnection(binding, syntax, lp_ctx=None, credentials=None) -> connection\n"
+"\n"
+"binding should be a DCE/RPC binding string (for example: ncacn_ip_tcp:127.0.0.1)\n"
+"syntax should be a tuple with a GUID and version number of an interface\n"
+"lp_ctx should be a path to a smb.conf file or a param.LoadParm object\n"
+"credentials should be a credentials.Credentials object.\n\n",
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ .tp_new = dcerpc_interface_new,
+};
+
+void initbase(void)
+{
+ PyObject *m;
+
+ if (PyType_Ready(&dcerpc_InterfaceType) < 0)
+ return;
+
+ m = Py_InitModule3("base", NULL, "DCE/RPC protocol implementation");
+ if (m == NULL)
+ return;
+
+ Py_INCREF((PyObject *)&dcerpc_InterfaceType);
+ PyModule_AddObject(m, "ClientConnection", (PyObject *)&dcerpc_InterfaceType);
+}
diff --git a/source4/librpc/rpc/pyrpc.h b/source4/librpc/rpc/pyrpc.h
new file mode 100644
index 0000000000..ae622b562c
--- /dev/null
+++ b/source4/librpc/rpc/pyrpc.h
@@ -0,0 +1,74 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Jelmer Vernooij <jelmer@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _PYRPC_H_
+#define _PYRPC_H_
+
+#include "libcli/util/pyerrors.h"
+#include "librpc/rpc/dcerpc.h"
+
+#define PY_CHECK_TYPE(type, var, fail) \
+ if (!PyObject_TypeCheck(var, type)) {\
+ PyErr_Format(PyExc_TypeError, "Expected type %s", (type)->tp_name); \
+ fail; \
+ }
+
+#define dom_sid0_Type dom_sid_Type
+#define dom_sid2_Type dom_sid_Type
+#define dom_sid28_Type dom_sid_Type
+#define dom_sid0_Check dom_sid_Check
+#define dom_sid2_Check dom_sid_Check
+#define dom_sid28_Check dom_sid_Check
+
+/* This macro is only provided by Python >= 2.3 */
+#ifndef PyAPI_DATA
+# define PyAPI_DATA(RTYPE) extern RTYPE
+#endif
+
+typedef struct {
+ PyObject_HEAD
+ struct dcerpc_pipe *pipe;
+} dcerpc_InterfaceObject;
+
+PyAPI_DATA(PyTypeObject) dcerpc_InterfaceType;
+
+#define PyErr_FromNdrError(err) Py_BuildValue("(is)", err, ndr_map_error2string(err))
+
+#define PyErr_SetNdrError(err) \
+ PyErr_SetObject(PyExc_RuntimeError, PyErr_FromNdrError(err))
+
+void PyErr_SetDCERPCStatus(struct dcerpc_pipe *pipe, NTSTATUS status);
+
+typedef bool (*py_data_pack_fn) (PyObject *args, PyObject *kwargs, void *r);
+typedef PyObject *(*py_data_unpack_fn) (void *r);
+
+struct PyNdrRpcMethodDef {
+ const char *name;
+ const char *doc;
+ dcerpc_call_fn call;
+ py_data_pack_fn pack_in_data;
+ py_data_unpack_fn unpack_out_data;
+ uint32_t opnum;
+ const struct ndr_interface_table *table;
+};
+
+bool PyInterface_AddNdrRpcMethods(PyTypeObject *object, const struct PyNdrRpcMethodDef *mds);
+PyObject *py_dcerpc_interface_init_helper(PyTypeObject *type, PyObject *args, PyObject *kwargs, const struct ndr_interface_table *table);
+
+#endif /* _PYRPC_H_ */
diff --git a/source4/librpc/scripts/build_idl.sh b/source4/librpc/scripts/build_idl.sh
new file mode 100755
index 0000000000..714f6d1f6a
--- /dev/null
+++ b/source4/librpc/scripts/build_idl.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+FULLBUILD=$1
+OUTDIR=$2
+shift 2
+IDL_FILES="$*"
+
+[ -d $OUTDIR ] || mkdir -p $OUTDIR || exit 1
+
+PIDL="$PIDL --outputdir $OUTDIR --header --ndr-parser --server --client --python --dcom-proxy --com-header --includedir ../librpc/idl -- "
+
+if [ x$FULLBUILD = xFULL ]; then
+ echo Rebuilding all idl files in $IDLDIR
+ $PIDL $IDL_FILES || exit 1
+ exit 0
+fi
+
+list=""
+
+for f in $IDL_FILES ; do
+ basename=`basename $f .idl`
+ ndr="$OUTDIR/ndr_$basename.c"
+ # blergh - most shells don't have the -nt function
+ if [ -f $ndr ]; then
+ if [ x`find $f -newer $ndr -print` = x$f ]; then
+ list="$list $f"
+ fi
+ else
+ list="$list $f"
+ fi
+done
+
+if [ "x$list" != x ]; then
+ $PIDL $list || exit 1
+fi
+
+exit 0
diff --git a/source4/librpc/tests/binding_string.c b/source4/librpc/tests/binding_string.c
new file mode 100644
index 0000000000..01cdfae80d
--- /dev/null
+++ b/source4/librpc/tests/binding_string.c
@@ -0,0 +1,172 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ local testing of RPC binding string parsing
+
+ Copyright (C) Jelmer Vernooij 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "librpc/gen_ndr/epmapper.h"
+#include "librpc/rpc/dcerpc.h"
+#include "librpc/rpc/dcerpc_proto.h"
+#include "torture/torture.h"
+
+static bool test_BindingString(struct torture_context *tctx,
+ const void *test_data)
+{
+ const char *binding = test_data;
+ struct dcerpc_binding *b, *b2;
+ const char *s, *s2;
+ struct epm_tower tower;
+ TALLOC_CTX *mem_ctx = tctx;
+
+ /* Parse */
+ torture_assert_ntstatus_ok(tctx, dcerpc_parse_binding(mem_ctx, binding, &b),
+ "Error parsing binding string");
+
+ s = dcerpc_binding_string(mem_ctx, b);
+ torture_assert(tctx, s != NULL, "Error converting binding back to string");
+
+ torture_assert_casestr_equal(tctx, binding, s,
+ "Mismatch while comparing original and regenerated binding strings");
+
+ /* Generate protocol towers */
+ torture_assert_ntstatus_ok(tctx, dcerpc_binding_build_tower(mem_ctx, b, &tower),
+ "Error generating protocol tower");
+
+ /* Convert back to binding and then back to string and compare */
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_binding_from_tower(mem_ctx, &tower, &b2),
+ "Error generating binding from tower for original binding");
+
+ /* Compare to a stripped down version of the binding string because
+ * the protocol tower doesn't contain the extra option data */
+ b->options = NULL;
+
+ b->flags = 0;
+
+ s = dcerpc_binding_string(mem_ctx, b);
+ torture_assert(tctx, s != NULL, "Error converting binding back to string for (stripped down)");
+
+ s2 = dcerpc_binding_string(mem_ctx, b2);
+ torture_assert(tctx, s != NULL, "Error converting binding back to string");
+
+ if (is_ipaddress(b->host))
+ torture_assert_casestr_equal(tctx, s, s2, "Mismatch while comparing original and from protocol tower generated binding strings");
+
+ return true;
+}
+
+static const char *test_strings[] = {
+ "ncacn_np:",
+ "ncalrpc:",
+ "ncalrpc:[,Security=Sane]",
+ "ncacn_np:[rpcecho]",
+ "ncacn_np:127.0.0.1[rpcecho]",
+ "ncacn_ip_tcp:127.0.0.1",
+ "ncacn_ip_tcp:127.0.0.1[20]",
+ "ncacn_ip_tcp:127.0.0.1[20,sign]",
+ "ncacn_ip_tcp:127.0.0.1[20,Security=Foobar,sign]",
+ "ncacn_http:127.0.0.1",
+ "ncacn_http:127.0.0.1[78]",
+ "ncacn_http:127.0.0.1[78,ProxyServer=myproxy:3128]",
+ "ncacn_np:localhost[rpcecho]",
+ "ncacn_np:[/pipe/rpcecho]",
+ "ncacn_np:localhost[/pipe/rpcecho,sign,seal]",
+ "ncacn_np:[,sign]",
+ "ncadg_ip_udp:",
+ "308FB580-1EB2-11CA-923B-08002B1075A7@ncacn_np:localhost",
+ "308FB580-1EB2-11CA-923B-08002B1075A7@ncacn_ip_tcp:127.0.0.1",
+ "ncacn_unix_stream:[/tmp/epmapper]",
+ "ncalrpc:[IDENTIFIER]",
+ "ncacn_unix_stream:[/tmp/epmapper,sign]",
+};
+
+static bool test_parse_check_results(struct torture_context *tctx)
+{
+ struct dcerpc_binding *b;
+ struct GUID uuid;
+
+ torture_assert_ntstatus_ok(tctx,
+ GUID_from_string("308FB580-1EB2-11CA-923B-08002B1075A7", &uuid),
+ "parsing uuid");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_parse_binding(tctx, "ncacn_np:$SERVER", &b), "parse");
+ torture_assert(tctx, b->transport == NCACN_NP, "ncacn_np expected");
+ torture_assert_ntstatus_ok(tctx, dcerpc_parse_binding(tctx, "ncacn_ip_tcp:$SERVER", &b), "parse");
+ torture_assert(tctx, b->transport == NCACN_IP_TCP, "ncacn_ip_tcp expected");
+ torture_assert_ntstatus_ok(tctx, dcerpc_parse_binding(tctx, "ncacn_np:$SERVER[rpcecho]", &b), "parse");
+ torture_assert_str_equal(tctx, b->endpoint, "rpcecho", "endpoint");
+ torture_assert_ntstatus_ok(tctx, dcerpc_parse_binding(tctx, "ncacn_np:$SERVER[/pipe/rpcecho]", &b), "parse");
+ torture_assert_ntstatus_ok(tctx, dcerpc_parse_binding(tctx, "ncacn_np:$SERVER[/pipe/rpcecho,sign,seal]", &b), "parse");
+ torture_assert(tctx, b->flags == DCERPC_SIGN+DCERPC_SEAL, "sign+seal flags");
+ torture_assert_str_equal(tctx, b->endpoint, "/pipe/rpcecho", "endpoint");
+ torture_assert_ntstatus_ok(tctx, dcerpc_parse_binding(tctx, "ncacn_np:$SERVER[,sign]", &b), "parse");
+ torture_assert_ntstatus_ok(tctx, dcerpc_parse_binding(tctx, "ncacn_ip_tcp:$SERVER[,sign]", &b), "parse");
+ torture_assert(tctx, b->endpoint == NULL, "endpoint");
+ torture_assert(tctx, b->flags == DCERPC_SIGN, "sign flag");
+ torture_assert_ntstatus_ok(tctx, dcerpc_parse_binding(tctx, "ncalrpc:", &b), "parse");
+ torture_assert(tctx, b->transport == NCALRPC, "ncalrpc expected");
+ torture_assert_ntstatus_ok(tctx, dcerpc_parse_binding(tctx,
+ "308FB580-1EB2-11CA-923B-08002B1075A7@ncacn_np:$SERVER", &b), "parse");
+ torture_assert(tctx, GUID_equal(&b->object.uuid, &uuid), "object uuid");
+ torture_assert_int_equal(tctx, b->object.if_version, 0, "object version");
+ torture_assert_ntstatus_ok(tctx, dcerpc_parse_binding(tctx,
+ "308FB580-1EB2-11CA-923B-08002B1075A7@ncacn_ip_tcp:$SERVER", &b), "parse");
+
+ return true;
+}
+
+static bool test_no_transport(struct torture_context *tctx)
+{
+ const char *binding = "somehost";
+ struct dcerpc_binding *b;
+ const char *s;
+
+ /* Parse */
+ torture_assert_ntstatus_ok(tctx, dcerpc_parse_binding(tctx, binding, &b),
+ "Error parsing binding string");
+
+ torture_assert(tctx, b->transport == NCA_UNKNOWN, "invalid transport");
+
+ s = dcerpc_binding_string(tctx, b);
+ torture_assert(tctx, s != NULL, "Error converting binding back to string");
+
+ torture_assert_casestr_equal(tctx, binding, s,
+ "Mismatch while comparing original and regenerated binding strings");
+
+ return true;
+}
+
+struct torture_suite *torture_local_binding_string(TALLOC_CTX *mem_ctx)
+{
+ int i;
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "BINDING");
+
+ for (i = 0; i < ARRAY_SIZE(test_strings); i++) {
+ torture_suite_add_simple_tcase_const(suite, test_strings[i],
+ test_BindingString,
+ test_strings[i]);
+ }
+
+ torture_suite_add_simple_test(suite, "no transport",test_no_transport);
+
+ torture_suite_add_simple_test(suite, "parsing results",
+ test_parse_check_results);
+
+ return suite;
+}
diff --git a/source4/librpc/tests/samr-CreateUser-in.dat b/source4/librpc/tests/samr-CreateUser-in.dat
new file mode 100644
index 0000000000..a5840e1f7f
--- /dev/null
+++ b/source4/librpc/tests/samr-CreateUser-in.dat
Binary files differ
diff --git a/source4/librpc/tests/samr-CreateUser-out.dat b/source4/librpc/tests/samr-CreateUser-out.dat
new file mode 100644
index 0000000000..cf9131dba9
--- /dev/null
+++ b/source4/librpc/tests/samr-CreateUser-out.dat
Binary files differ
diff --git a/source4/librpc/tests/test_ndrdump.sh b/source4/librpc/tests/test_ndrdump.sh
new file mode 100755
index 0000000000..dccf86df88
--- /dev/null
+++ b/source4/librpc/tests/test_ndrdump.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+# Blackbox tests for masktest
+# Copyright (C) 2008 Andrew Tridgell
+# Copyright (C) 2008 Andrew Bartlett
+# based on test_smbclient.sh
+
+. `dirname $0`/../../../testprogs/blackbox/subunit.sh
+
+failed=0
+
+samba4bindir="$BUILDDIR/bin"
+ndrdump="$samba4bindir/ndrdump$EXEEXT"
+files=`dirname $0`/
+
+testit "ndrdump with in" $VALGRIND $ndrdump samr samr_CreateUser in $files/samr-CreateUser-in.dat $@ || failed=`expr $failed + 1`
+testit "ndrdump with out" $VALGRIND $ndrdump samr samr_CreateUser out $files/samr-CreateUser-out.dat $@ || failed=`expr $failed + 1`
+testit "ndrdump with --context-file" $VALGRIND $ndrdump --context-file $files/samr-CreateUser-in.dat samr samr_CreateUser out $files/samr-CreateUser-out.dat $@ || failed=`expr $failed + 1`
+testit "ndrdump with validate" $VALGRIND $ndrdump --validate samr samr_CreateUser in $files/samr-CreateUser-in.dat $@ || failed=`expr $failed + 1`
+
+exit $failed
diff --git a/source4/main.mk b/source4/main.mk
new file mode 100644
index 0000000000..ee2018fb69
--- /dev/null
+++ b/source4/main.mk
@@ -0,0 +1,54 @@
+mkinclude dynconfig/config.mk
+mkinclude config.mk
+mkinclude dsdb/config.mk
+mkinclude smbd/config.mk
+mkinclude cluster/config.mk
+mkinclude smbd/process_model.mk
+mkinclude libnet/config.mk
+mkinclude auth/config.mk
+mkinclude ../nsswitch/config.mk
+mkinclude lib/samba3/config.mk
+mkinclude lib/socket/config.mk
+mkinclude ../lib/util/charset/config.mk
+mkinclude lib/ldb-samba/config.mk
+mkinclude lib/tls/config.mk
+mkinclude lib/registry/config.mk
+mkinclude lib/messaging/config.mk
+mkinclude lib/events/config.mk
+mkinclude lib/cmdline/config.mk
+mkinclude ../lib/socket_wrapper/config.mk
+mkinclude ../lib/nss_wrapper/config.mk
+mkinclude lib/stream/config.mk
+mkinclude ../lib/util/config.mk
+mkinclude lib/tdr/config.mk
+mkinclude ../lib/crypto/config.mk
+mkinclude ../lib/torture/config.mk
+mkinclude lib/basic.mk
+mkinclude lib/com/config.mk
+# WMI fails at the moment
+# mkinclude lib/wmi/config.mk
+mkinclude param/config.mk
+mkinclude smb_server/config.mk
+mkinclude rpc_server/config.mk
+mkinclude ldap_server/config.mk
+mkinclude web_server/config.mk
+mkinclude winbind/config.mk
+mkinclude nbt_server/config.mk
+mkinclude wrepl_server/config.mk
+mkinclude cldap_server/config.mk
+mkinclude ntp_signd/config.mk
+mkinclude utils/net/config.mk
+mkinclude utils/config.mk
+mkinclude ntvfs/config.mk
+mkinclude ntptr/config.mk
+mkinclude torture/config.mk
+mkinclude librpc/config.mk
+mkinclude client/config.mk
+mkinclude libcli/config.mk
+mkinclude scripting/python/config.mk
+mkinclude kdc/config.mk
+mkinclude ../lib/smbconf/config.mk
+mkinclude ../lib/async_req/config.mk
+mkinclude ../libcli/security/config.mk
+mkinclude ../libcli/ldap/config.mk
+
diff --git a/source4/min_versions.m4 b/source4/min_versions.m4
new file mode 100644
index 0000000000..eaefbd5148
--- /dev/null
+++ b/source4/min_versions.m4
@@ -0,0 +1,6 @@
+# Minimum and exact required versions for various libraries
+# if we use the ones installed in the system.
+TDB_MIN_VERSION=1.1.3
+TALLOC_MIN_VERSION=1.3.0
+LDB_REQUIRED_VERSION=0.9.3
+TEVENT_REQUIRED_VERSION=0.9.5
diff --git a/source4/nbt_server/config.mk b/source4/nbt_server/config.mk
new file mode 100644
index 0000000000..afd9242188
--- /dev/null
+++ b/source4/nbt_server/config.mk
@@ -0,0 +1,77 @@
+# NBTD server subsystem
+
+#######################
+# Start SUBSYSTEM WINSDB
+[SUBSYSTEM::WINSDB]
+PUBLIC_DEPENDENCIES = \
+ LIBLDB
+# End SUBSYSTEM WINSDB
+#######################
+
+WINSDB_OBJ_FILES = $(addprefix $(nbt_serversrcdir)/wins/, winsdb.o wins_hook.o)
+
+$(eval $(call proto_header_template,$(nbt_serversrcdir)/wins/winsdb_proto.h,$(WINSDB_OBJ_FILES:.o=.c)))
+
+#######################
+# Start MODULE ldb_wins_ldb
+[MODULE::ldb_wins_ldb]
+SUBSYSTEM = LIBLDB
+INIT_FUNCTION = LDB_MODULE(wins_ldb)
+PRIVATE_DEPENDENCIES = \
+ LIBLDB LIBNETIF LIBSAMBA-HOSTCONFIG LIBSAMBA-UTIL
+# End MODULE ldb_wins_ldb
+#######################
+
+ldb_wins_ldb_OBJ_FILES = $(nbt_serversrcdir)/wins/wins_ldb.o
+
+#######################
+# Start SUBSYSTEM NBTD_WINS
+[SUBSYSTEM::NBTD_WINS]
+PRIVATE_DEPENDENCIES = \
+ LIBCLI_NBT WINSDB
+# End SUBSYSTEM NBTD_WINS
+#######################
+
+
+NBTD_WINS_OBJ_FILES = $(addprefix $(nbt_serversrcdir)/wins/, winsserver.o winsclient.o winswack.o wins_dns_proxy.o)
+
+$(eval $(call proto_header_template,$(nbt_serversrcdir)/wins/winsserver_proto.h,$(NBTD_WINS_OBJ_FILES:.o=.c)))
+
+#######################
+# Start SUBSYSTEM NBTD_DGRAM
+[SUBSYSTEM::NBTD_DGRAM]
+PRIVATE_DEPENDENCIES = \
+ LIBCLI_DGRAM
+# End SUBSYSTEM NBTD_DGRAM
+#######################
+
+NBTD_DGRAM_OBJ_FILES = $(addprefix $(nbt_serversrcdir)/dgram/, request.o netlogon.o browse.o)
+
+$(eval $(call proto_header_template,$(nbt_serversrcdir)/dgram/proto.h,$(NBTD_DGRAM_OBJ_FILES:.o=.c)))
+
+#######################
+# Start SUBSYSTEM NBTD
+[SUBSYSTEM::NBT_SERVER]
+PRIVATE_DEPENDENCIES = \
+ LIBCLI_NBT NBTD_WINS NBTD_DGRAM
+# End SUBSYSTEM NBTD
+#######################
+
+NBT_SERVER_OBJ_FILES = $(addprefix $(nbt_serversrcdir)/, \
+ interfaces.o \
+ register.o \
+ query.o \
+ nodestatus.o \
+ defense.o \
+ packet.o \
+ irpc.o)
+
+$(eval $(call proto_header_template,$(nbt_serversrcdir)/nbt_server_proto.h,$(NBT_SERVER_OBJ_FILES:.o=.c)))
+
+[MODULE::service_nbtd]
+INIT_FUNCTION = server_service_nbtd_init
+SUBSYSTEM = service
+PRIVATE_DEPENDENCIES = NBT_SERVER process_model
+
+service_nbtd_OBJ_FILES = \
+ $(nbt_serversrcdir)/nbt_server.o
diff --git a/source4/nbt_server/defense.c b/source4/nbt_server/defense.c
new file mode 100644
index 0000000000..fbe22aa781
--- /dev/null
+++ b/source4/nbt_server/defense.c
@@ -0,0 +1,79 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ defend our names against name registration requests
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "../lib/util/dlinklist.h"
+#include "system/network.h"
+#include "nbt_server/nbt_server.h"
+#include "nbt_server/wins/winsserver.h"
+#include "librpc/gen_ndr/ndr_nbt.h"
+#include "lib/socket/socket.h"
+
+
+/*
+ defend our registered names against registration or name refresh
+ requests
+*/
+void nbtd_request_defense(struct nbt_name_socket *nbtsock,
+ struct nbt_name_packet *packet,
+ struct socket_address *src)
+{
+ struct nbtd_iface_name *iname;
+ struct nbt_name *name;
+ struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private_data,
+ struct nbtd_interface);
+
+ /*
+ * if the packet comes from one of our interfaces
+ * it must be our winsclient trying to reach the winsserver
+ */
+ if (nbtd_self_packet(nbtsock, packet, src)) {
+ nbtd_winsserver_request(nbtsock, packet, src);
+ return;
+ }
+
+ NBTD_ASSERT_PACKET(packet, src, packet->qdcount == 1);
+ NBTD_ASSERT_PACKET(packet, src, packet->arcount == 1);
+ NBTD_ASSERT_PACKET(packet, src,
+ packet->questions[0].question_type == NBT_QTYPE_NETBIOS);
+ NBTD_ASSERT_PACKET(packet, src,
+ packet->questions[0].question_class == NBT_QCLASS_IP);
+ NBTD_ASSERT_PACKET(packet, src,
+ packet->additional[0].rr_type == NBT_QTYPE_NETBIOS);
+ NBTD_ASSERT_PACKET(packet, src,
+ packet->additional[0].rr_class == NBT_QCLASS_IP);
+ NBTD_ASSERT_PACKET(packet, src,
+ packet->additional[0].rdata.netbios.length == 6);
+
+ /* see if we have the requested name on this interface */
+ name = &packet->questions[0].name;
+
+ iname = nbtd_find_iname(iface, name, NBT_NM_ACTIVE);
+ if (iname != NULL &&
+ !(name->type == NBT_NAME_LOGON || iname->nb_flags & NBT_NM_GROUP)) {
+ DEBUG(2,("Defending name %s on %s against %s\n",
+ nbt_name_string(packet, name),
+ iface->bcast_address, src->addr));
+ nbtd_name_registration_reply(nbtsock, packet, src, NBT_RCODE_ACT);
+ } else {
+ nbtd_winsserver_request(nbtsock, packet, src);
+ }
+}
diff --git a/source4/nbt_server/dgram/browse.c b/source4/nbt_server/dgram/browse.c
new file mode 100644
index 0000000000..1ebc88fcd4
--- /dev/null
+++ b/source4/nbt_server/dgram/browse.c
@@ -0,0 +1,85 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ NBT datagram browse server
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "nbt_server/nbt_server.h"
+#include "lib/socket/socket.h"
+#include "librpc/gen_ndr/ndr_nbt.h"
+#include "nbt_server/dgram/proto.h"
+
+static const char *nbt_browse_opcode_string(enum nbt_browse_opcode r)
+{
+ const char *val = NULL;
+
+ switch (r) {
+ case HostAnnouncement: val = "HostAnnouncement"; break;
+ case AnnouncementRequest: val = "AnnouncementRequest"; break;
+ case Election: val = "Election"; break;
+ case GetBackupListReq: val = "GetBackupListReq"; break;
+ case GetBackupListResp: val = "GetBackupListResp"; break;
+ case BecomeBackup: val = "BecomeBackup"; break;
+ case DomainAnnouncement: val = "DomainAnnouncement"; break;
+ case MasterAnnouncement: val = "MasterAnnouncement"; break;
+ case ResetBrowserState: val = "ResetBrowserState"; break;
+ case LocalMasterAnnouncement: val = "LocalMasterAnnouncement"; break;
+ }
+
+ return val;
+}
+
+/*
+ handle incoming browse mailslot requests
+*/
+void nbtd_mailslot_browse_handler(struct dgram_mailslot_handler *dgmslot,
+ struct nbt_dgram_packet *packet,
+ struct socket_address *src)
+{
+ struct nbt_browse_packet *browse = talloc(dgmslot, struct nbt_browse_packet);
+ struct nbt_name *name = &packet->data.msg.dest_name;
+ NTSTATUS status;
+
+ if (browse == NULL) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto failed;
+ }
+
+ status = dgram_mailslot_browse_parse(dgmslot, browse, packet, browse);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+
+ DEBUG(2,("Browse %s (Op %d) on '%s' '%s' from %s:%d\n",
+ nbt_browse_opcode_string(browse->opcode), browse->opcode,
+ nbt_name_string(browse, name), dgmslot->mailslot_name,
+ src->addr, src->port));
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_DEBUG(nbt_browse_packet, browse);
+ }
+
+ talloc_free(browse);
+ return;
+
+failed:
+ DEBUG(2,("nbtd browse handler failed from %s:%d to %s - %s\n",
+ src->addr, src->port, nbt_name_string(browse, name),
+ nt_errstr(status)));
+ talloc_free(browse);
+
+}
diff --git a/source4/nbt_server/dgram/netlogon.c b/source4/nbt_server/dgram/netlogon.c
new file mode 100644
index 0000000000..e5c82280e3
--- /dev/null
+++ b/source4/nbt_server/dgram/netlogon.c
@@ -0,0 +1,210 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ NBT datagram netlogon server
+
+ Copyright (C) Andrew Tridgell 2005
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "nbt_server/nbt_server.h"
+#include "lib/socket/socket.h"
+#include "lib/ldb/include/ldb.h"
+#include "dsdb/samdb/samdb.h"
+#include "auth/auth.h"
+#include "../lib/util/util_ldb.h"
+#include "param/param.h"
+#include "smbd/service_task.h"
+#include "cldap_server/cldap_server.h"
+#include "libcli/security/security.h"
+#include "nbt_server/dgram/proto.h"
+
+/*
+ reply to a GETDC request
+ */
+static void nbtd_netlogon_getdc(struct dgram_mailslot_handler *dgmslot,
+ struct nbtd_interface *iface,
+ struct nbt_dgram_packet *packet,
+ const struct socket_address *src,
+ struct nbt_netlogon_packet *netlogon)
+{
+ struct nbt_name *name = &packet->data.msg.dest_name;
+ struct nbtd_interface *reply_iface = nbtd_find_reply_iface(iface, src->addr, false);
+ struct nbt_netlogon_response_from_pdc *pdc;
+ const char *ref_attrs[] = {"nETBIOSName", NULL};
+ struct ldb_message **ref_res;
+ struct ldb_context *samctx;
+ struct ldb_dn *partitions_basedn;
+ struct nbt_netlogon_response netlogon_response;
+ int ret;
+
+ /* only answer getdc requests on the PDC or LOGON names */
+ if (name->type != NBT_NAME_PDC && name->type != NBT_NAME_LOGON) {
+ return;
+ }
+
+ samctx = iface->nbtsrv->sam_ctx;
+
+ if (!samdb_is_pdc(samctx)) {
+ DEBUG(2, ("Not a PDC, so not processing LOGON_PRIMARY_QUERY\n"));
+ return;
+ }
+
+ partitions_basedn = samdb_partitions_dn(samctx, packet);
+
+ ret = gendb_search(samctx, packet, partitions_basedn, &ref_res, ref_attrs,
+ "(&(&(nETBIOSName=%s)(objectclass=crossRef))(ncName=*))",
+ name->name);
+
+ if (ret != 1) {
+ DEBUG(2,("Unable to find domain reference '%s' in sam\n", name->name));
+ return;
+ }
+
+ /* setup a GETDC reply */
+ ZERO_STRUCT(netlogon_response);
+ netlogon_response.response_type = NETLOGON_GET_PDC;
+ pdc = &netlogon_response.data.get_pdc;
+
+ pdc->command = NETLOGON_RESPONSE_FROM_PDC;
+ pdc->pdc_name = lp_netbios_name(iface->nbtsrv->task->lp_ctx);
+ pdc->unicode_pdc_name = pdc->pdc_name;
+ pdc->domain_name = samdb_result_string(ref_res[0], "nETBIOSName", name->name);;
+ pdc->nt_version = 1;
+ pdc->lmnt_token = 0xFFFF;
+ pdc->lm20_token = 0xFFFF;
+
+ dgram_mailslot_netlogon_reply(reply_iface->dgmsock,
+ packet,
+ lp_netbios_name(iface->nbtsrv->task->lp_ctx),
+ netlogon->req.pdc.mailslot_name,
+ &netlogon_response);
+}
+
+
+/*
+ reply to a ADS style GETDC request
+ */
+static void nbtd_netlogon_samlogon(struct dgram_mailslot_handler *dgmslot,
+ struct nbtd_interface *iface,
+ struct nbt_dgram_packet *packet,
+ const struct socket_address *src,
+ struct nbt_netlogon_packet *netlogon)
+{
+ struct nbt_name *name = &packet->data.msg.dest_name;
+ struct nbtd_interface *reply_iface = nbtd_find_reply_iface(iface, src->addr, false);
+ struct ldb_context *samctx;
+ const char *my_ip = reply_iface->ip_address;
+ struct dom_sid *sid;
+ struct nbt_netlogon_response netlogon_response;
+ NTSTATUS status;
+
+ if (!my_ip) {
+ DEBUG(0, ("Could not obtain own IP address for datagram socket\n"));
+ return;
+ }
+
+ /* only answer getdc requests on the PDC or LOGON names */
+ if (name->type != NBT_NAME_PDC && name->type != NBT_NAME_LOGON) {
+ return;
+ }
+
+ samctx = iface->nbtsrv->sam_ctx;
+
+ if (netlogon->req.logon.sid_size) {
+ sid = &netlogon->req.logon.sid;
+ } else {
+ sid = NULL;
+ }
+
+ status = fill_netlogon_samlogon_response(samctx, packet, NULL, name->name, sid, NULL,
+ netlogon->req.logon.user_name, netlogon->req.logon.acct_control, src->addr,
+ netlogon->req.logon.nt_version, iface->nbtsrv->task->lp_ctx, &netlogon_response.data.samlogon);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(2,("NBT netlogon query failed domain=%s sid=%s version=%d - %s\n",
+ name->name, dom_sid_string(packet, sid), netlogon->req.logon.nt_version, nt_errstr(status)));
+ return;
+ }
+
+ netlogon_response.response_type = NETLOGON_SAMLOGON;
+
+ packet->data.msg.dest_name.type = 0;
+
+ dgram_mailslot_netlogon_reply(reply_iface->dgmsock,
+ packet,
+ lp_netbios_name(iface->nbtsrv->task->lp_ctx),
+ netlogon->req.logon.mailslot_name,
+ &netlogon_response);
+}
+
+
+/*
+ handle incoming netlogon mailslot requests
+*/
+void nbtd_mailslot_netlogon_handler(struct dgram_mailslot_handler *dgmslot,
+ struct nbt_dgram_packet *packet,
+ struct socket_address *src)
+{
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+ struct nbtd_interface *iface =
+ talloc_get_type(dgmslot->private_data, struct nbtd_interface);
+ struct nbt_netlogon_packet *netlogon =
+ talloc(dgmslot, struct nbt_netlogon_packet);
+ struct nbtd_iface_name *iname;
+ struct nbt_name *name = &packet->data.msg.dest_name;
+
+ if (netlogon == NULL) goto failed;
+
+ /*
+ see if the we are listening on the destination netbios name
+ */
+ iname = nbtd_find_iname(iface, name, 0);
+ if (iname == NULL) {
+ status = NT_STATUS_BAD_NETWORK_NAME;
+ goto failed;
+ }
+
+ DEBUG(2,("netlogon request to %s from %s:%d\n",
+ nbt_name_string(netlogon, name), src->addr, src->port));
+ status = dgram_mailslot_netlogon_parse_request(dgmslot, netlogon, packet, netlogon);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+
+ switch (netlogon->command) {
+ case LOGON_PRIMARY_QUERY:
+ nbtd_netlogon_getdc(dgmslot, iface, packet,
+ src, netlogon);
+ break;
+ case LOGON_SAM_LOGON_REQUEST:
+ nbtd_netlogon_samlogon(dgmslot, iface, packet,
+ src, netlogon);
+ break;
+ default:
+ DEBUG(2,("unknown netlogon op %d from %s:%d\n",
+ netlogon->command, src->addr, src->port));
+ NDR_PRINT_DEBUG(nbt_netlogon_packet, netlogon);
+ break;
+ }
+
+ talloc_free(netlogon);
+ return;
+
+failed:
+ DEBUG(2,("nbtd netlogon handler failed from %s:%d to %s - %s\n",
+ src->addr, src->port, nbt_name_string(netlogon, name),
+ nt_errstr(status)));
+ talloc_free(netlogon);
+}
diff --git a/source4/nbt_server/dgram/ntlogon.c b/source4/nbt_server/dgram/ntlogon.c
new file mode 100644
index 0000000000..87e76e08ee
--- /dev/null
+++ b/source4/nbt_server/dgram/ntlogon.c
@@ -0,0 +1,121 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ NBT datagram ntlogon server
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "nbt_server/nbt_server.h"
+#include "smbd/service_task.h"
+#include "lib/socket/socket.h"
+#include "librpc/gen_ndr/ndr_nbt.h"
+#include "param/param.h"
+
+/*
+ reply to a SAM LOGON request
+ */
+static void nbtd_ntlogon_sam_logon(struct dgram_mailslot_handler *dgmslot,
+ struct nbtd_interface *iface,
+ struct nbt_dgram_packet *packet,
+ const struct socket_address *src,
+ struct nbt_ntlogon_packet *ntlogon)
+{
+ struct nbt_name *name = &packet->data.msg.dest_name;
+ struct nbtd_interface *reply_iface = nbtd_find_reply_iface(iface, src->addr, false);
+ struct nbt_ntlogon_packet reply;
+ struct nbt_ntlogon_sam_logon_reply *logon;
+
+ /* only answer sam logon requests on the PDC or LOGON names */
+ if (name->type != NBT_NAME_PDC && name->type != NBT_NAME_LOGON) {
+ return;
+ }
+
+ /* setup a SAM LOGON reply */
+ ZERO_STRUCT(reply);
+ reply.command = NTLOGON_SAM_LOGON_REPLY;
+ logon = &reply.req.reply;
+
+ logon->server = talloc_asprintf(packet, "\\\\%s",
+ lp_netbios_name(iface->nbtsrv->task->lp_ctx));
+ logon->user_name = ntlogon->req.logon.user_name;
+ logon->domain = lp_workgroup(iface->nbtsrv->task->lp_ctx);
+ logon->nt_version = 1;
+ logon->lmnt_token = 0xFFFF;
+ logon->lm20_token = 0xFFFF;
+
+ packet->data.msg.dest_name.type = 0;
+
+ dgram_mailslot_ntlogon_reply(reply_iface->dgmsock,
+ packet,
+ lp_netbios_name(iface->nbtsrv->task->lp_ctx),
+ ntlogon->req.logon.mailslot_name,
+ &reply);
+}
+
+/*
+ handle incoming ntlogon mailslot requests
+*/
+void nbtd_mailslot_ntlogon_handler(struct dgram_mailslot_handler *dgmslot,
+ struct nbt_dgram_packet *packet,
+ struct socket_address *src)
+{
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+ struct nbtd_interface *iface =
+ talloc_get_type(dgmslot->private_data, struct nbtd_interface);
+ struct nbt_ntlogon_packet *ntlogon =
+ talloc(dgmslot, struct nbt_ntlogon_packet);
+ struct nbtd_iface_name *iname;
+ struct nbt_name *name = &packet->data.msg.dest_name;
+
+ if (ntlogon == NULL) goto failed;
+
+ /*
+ see if the we are listening on the destination netbios name
+ */
+ iname = nbtd_find_iname(iface, name, 0);
+ if (iname == NULL) {
+ status = NT_STATUS_BAD_NETWORK_NAME;
+ goto failed;
+ }
+
+ DEBUG(2,("ntlogon request to %s from %s:%d\n",
+ nbt_name_string(ntlogon, name), src->addr, src->port));
+ status = dgram_mailslot_ntlogon_parse(dgmslot, ntlogon, packet, ntlogon);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+
+ NDR_PRINT_DEBUG(nbt_ntlogon_packet, ntlogon);
+
+ switch (ntlogon->command) {
+ case NTLOGON_SAM_LOGON:
+ nbtd_ntlogon_sam_logon(dgmslot, iface, packet, src, ntlogon);
+ break;
+ default:
+ DEBUG(2,("unknown ntlogon op %d from %s:%d\n",
+ ntlogon->command, src->addr, src->port));
+ break;
+ }
+
+ talloc_free(ntlogon);
+ return;
+
+failed:
+ DEBUG(2,("nbtd ntlogon handler failed from %s:%d to %s - %s\n",
+ src->addr, src->port, nbt_name_string(ntlogon, name),
+ nt_errstr(status)));
+ talloc_free(ntlogon);
+}
diff --git a/source4/nbt_server/dgram/request.c b/source4/nbt_server/dgram/request.c
new file mode 100644
index 0000000000..277b64741d
--- /dev/null
+++ b/source4/nbt_server/dgram/request.c
@@ -0,0 +1,153 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ NBT datagram server
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "nbt_server/nbt_server.h"
+#include "smbd/service_task.h"
+#include "lib/socket/socket.h"
+#include "libcli/resolve/resolve.h"
+#include "nbt_server/dgram/proto.h"
+#include "librpc/gen_ndr/ndr_nbt.h"
+#include "param/param.h"
+
+/*
+ a list of mailslots that we have static handlers for
+*/
+static const struct {
+ const char *mailslot_name;
+ dgram_mailslot_handler_t handler;
+} mailslot_handlers[] = {
+ /* Handle both NTLOGON and NETLOGON in the same function, as
+ * they are very similar */
+ { NBT_MAILSLOT_NETLOGON, nbtd_mailslot_netlogon_handler },
+ { NBT_MAILSLOT_NTLOGON, nbtd_mailslot_netlogon_handler },
+ { NBT_MAILSLOT_BROWSE, nbtd_mailslot_browse_handler }
+};
+
+/*
+ receive an incoming dgram request. This is used for general datagram
+ requests. Mailslot requests for our listening mailslots
+ are handled in the specific mailslot handlers
+*/
+void dgram_request_handler(struct nbt_dgram_socket *dgmsock,
+ struct nbt_dgram_packet *packet,
+ struct socket_address *src)
+{
+ DEBUG(0,("General datagram request from %s:%d\n", src->addr, src->port));
+ NDR_PRINT_DEBUG(nbt_dgram_packet, packet);
+}
+
+
+/*
+ setup the port 138 datagram listener for a given interface
+*/
+NTSTATUS nbtd_dgram_setup(struct nbtd_interface *iface, const char *bind_address)
+{
+ struct nbt_dgram_socket *bcast_dgmsock = NULL;
+ struct nbtd_server *nbtsrv = iface->nbtsrv;
+ struct socket_address *bcast_addr, *bind_addr;
+ NTSTATUS status;
+ TALLOC_CTX *tmp_ctx = talloc_new(iface);
+ /* the list of mailslots that we are interested in */
+ int i;
+
+ if (!tmp_ctx) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (strcmp("0.0.0.0", iface->netmask) != 0) {
+ /* listen for broadcasts on port 138 */
+ bcast_dgmsock = nbt_dgram_socket_init(iface,
+ nbtsrv->task->event_ctx,
+ lp_iconv_convenience(nbtsrv->task->lp_ctx));
+ if (!bcast_dgmsock) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ bcast_addr = socket_address_from_strings(tmp_ctx, bcast_dgmsock->sock->backend_name,
+ iface->bcast_address,
+ lp_dgram_port(iface->nbtsrv->task->lp_ctx));
+ if (!bcast_addr) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = socket_listen(bcast_dgmsock->sock, bcast_addr, 0, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ DEBUG(0,("Failed to bind to %s:%d - %s\n",
+ iface->bcast_address, lp_dgram_port(iface->nbtsrv->task->lp_ctx),
+ nt_errstr(status)));
+ return status;
+ }
+
+ dgram_set_incoming_handler(bcast_dgmsock, dgram_request_handler, iface);
+ }
+
+ /* listen for unicasts on port 138 */
+ iface->dgmsock = nbt_dgram_socket_init(iface, nbtsrv->task->event_ctx,
+ lp_iconv_convenience(nbtsrv->task->lp_ctx));
+ if (!iface->dgmsock) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ bind_addr = socket_address_from_strings(tmp_ctx, iface->dgmsock->sock->backend_name,
+ bind_address, lp_dgram_port(iface->nbtsrv->task->lp_ctx));
+ if (!bind_addr) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = socket_listen(iface->dgmsock->sock, bind_addr, 0, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ DEBUG(0,("Failed to bind to %s:%d - %s\n",
+ bind_address, lp_dgram_port(iface->nbtsrv->task->lp_ctx), nt_errstr(status)));
+ return status;
+ }
+
+ dgram_set_incoming_handler(iface->dgmsock, dgram_request_handler, iface);
+
+ talloc_free(tmp_ctx);
+
+ for (i=0;i<ARRAY_SIZE(mailslot_handlers);i++) {
+ /* note that we don't need to keep the pointer
+ to the dgmslot around - the callback is all
+ we need */
+ struct dgram_mailslot_handler *dgmslot;
+
+ if (bcast_dgmsock) {
+ dgmslot = dgram_mailslot_listen(bcast_dgmsock,
+ mailslot_handlers[i].mailslot_name,
+ mailslot_handlers[i].handler, iface);
+ NT_STATUS_HAVE_NO_MEMORY(dgmslot);
+ }
+
+ dgmslot = dgram_mailslot_listen(iface->dgmsock,
+ mailslot_handlers[i].mailslot_name,
+ mailslot_handlers[i].handler, iface);
+ NT_STATUS_HAVE_NO_MEMORY(dgmslot);
+ }
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/nbt_server/interfaces.c b/source4/nbt_server/interfaces.c
new file mode 100644
index 0000000000..0a9196a747
--- /dev/null
+++ b/source4/nbt_server/interfaces.c
@@ -0,0 +1,419 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ NBT interface handling
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "../lib/util/dlinklist.h"
+#include "nbt_server/nbt_server.h"
+#include "smbd/service_task.h"
+#include "lib/socket/socket.h"
+#include "nbt_server/wins/winsserver.h"
+#include "nbt_server/dgram/proto.h"
+#include "system/network.h"
+#include "lib/socket/netif.h"
+#include "param/param.h"
+
+
+/*
+ receive an incoming request and dispatch it to the right place
+*/
+static void nbtd_request_handler(struct nbt_name_socket *nbtsock,
+ struct nbt_name_packet *packet,
+ struct socket_address *src)
+{
+ struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private_data,
+ struct nbtd_interface);
+ struct nbtd_server *nbtsrv = iface->nbtsrv;
+
+ nbtsrv->stats.total_received++;
+
+ /* see if its from one of our own interfaces - if so, then ignore it */
+ if (nbtd_self_packet_and_bcast(nbtsock, packet, src)) {
+ DEBUG(10,("Ignoring bcast self packet from %s:%d\n", src->addr, src->port));
+ return;
+ }
+
+ switch (packet->operation & NBT_OPCODE) {
+ case NBT_OPCODE_QUERY:
+ nbtsrv->stats.query_count++;
+ nbtd_request_query(nbtsock, packet, src);
+ break;
+
+ case NBT_OPCODE_REGISTER:
+ case NBT_OPCODE_REFRESH:
+ case NBT_OPCODE_REFRESH2:
+ nbtsrv->stats.register_count++;
+ nbtd_request_defense(nbtsock, packet, src);
+ break;
+
+ case NBT_OPCODE_RELEASE:
+ case NBT_OPCODE_MULTI_HOME_REG:
+ nbtsrv->stats.release_count++;
+ nbtd_winsserver_request(nbtsock, packet, src);
+ break;
+
+ default:
+ nbtd_bad_packet(packet, src, "Unexpected opcode");
+ break;
+ }
+}
+
+static void nbtd_unexpected_handler(struct nbt_name_socket *nbtsock,
+ struct nbt_name_packet *packet,
+ struct socket_address *src)
+{
+ struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private_data,
+ struct nbtd_interface);
+ struct nbtd_server *nbtsrv = iface->nbtsrv;
+ struct nbtd_interface *i;
+ struct nbt_name_request *req = NULL;
+
+ nbtsrv->stats.total_received++;
+
+ DEBUG(10,("unexpected from src[%s] on interface[%p] %s/%s\n",
+ src->addr, iface, iface->ip_address, iface->netmask));
+
+ /* try the broadcast interface */
+ if (nbtsrv->bcast_interface) {
+ i = nbtsrv->bcast_interface;
+ req = idr_find(i->nbtsock->idr, packet->name_trn_id);
+ }
+
+ /* try the wins server client interface */
+ if (!req && nbtsrv->wins_interface && nbtsrv->wins_interface->nbtsock) {
+ i = nbtsrv->wins_interface;
+ req = idr_find(i->nbtsock->idr, packet->name_trn_id);
+ }
+
+ /* try all other interfaces... */
+ if (!req) {
+ for (i = nbtsrv->interfaces; i; i = i->next) {
+ if (i == iface) {
+ continue;
+ }
+ req = idr_find(i->nbtsock->idr, packet->name_trn_id);
+ if (req) break;
+ }
+ }
+
+ if (!req) {
+ DEBUG(10,("unexpected from src[%s] unable to redirected\n", src->addr));
+ return;
+ }
+
+ DEBUG(10,("unexpected from src[%s] redirected to interface[%p] %s/%s\n",
+ src->addr, i, i->ip_address, i->netmask));
+
+ /*
+ * redirect the incoming response to the socket
+ * we sent the matching request
+ */
+ nbt_name_socket_handle_response_packet(req, packet, src);
+}
+
+/*
+ find a registered name on an interface
+*/
+struct nbtd_iface_name *nbtd_find_iname(struct nbtd_interface *iface,
+ struct nbt_name *name,
+ uint16_t nb_flags)
+{
+ struct nbtd_iface_name *iname;
+ for (iname=iface->names;iname;iname=iname->next) {
+ if (iname->name.type == name->type &&
+ strcmp(name->name, iname->name.name) == 0 &&
+ ((iname->nb_flags & nb_flags) == nb_flags)) {
+ return iname;
+ }
+ }
+ return NULL;
+}
+
+/*
+ start listening on the given address
+*/
+static NTSTATUS nbtd_add_socket(struct nbtd_server *nbtsrv,
+ struct loadparm_context *lp_ctx,
+ const char *bind_address,
+ const char *address,
+ const char *bcast,
+ const char *netmask)
+{
+ struct nbtd_interface *iface;
+ NTSTATUS status;
+ struct socket_address *bcast_address;
+ struct socket_address *unicast_address;
+
+ DEBUG(6,("nbtd_add_socket(%s, %s, %s, %s)\n", bind_address, address, bcast, netmask));
+
+ /*
+ we actually create two sockets. One listens on the broadcast address
+ for the interface, and the other listens on our specific address. This
+ allows us to run with "bind interfaces only" while still receiving
+ broadcast addresses, and also simplifies matching incoming requests
+ to interfaces
+ */
+
+ iface = talloc(nbtsrv, struct nbtd_interface);
+ NT_STATUS_HAVE_NO_MEMORY(iface);
+
+ iface->nbtsrv = nbtsrv;
+ iface->bcast_address = talloc_steal(iface, bcast);
+ iface->ip_address = talloc_steal(iface, address);
+ iface->netmask = talloc_steal(iface, netmask);
+ iface->names = NULL;
+ iface->wack_queue = NULL;
+
+ if (strcmp(netmask, "0.0.0.0") != 0) {
+ struct nbt_name_socket *bcast_nbtsock;
+
+ /* listen for broadcasts on port 137 */
+ bcast_nbtsock = nbt_name_socket_init(iface, nbtsrv->task->event_ctx, lp_iconv_convenience(nbtsrv->task->lp_ctx));
+ if (!bcast_nbtsock) {
+ talloc_free(iface);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ bcast_address = socket_address_from_strings(bcast_nbtsock, bcast_nbtsock->sock->backend_name,
+ bcast, lp_nbt_port(lp_ctx));
+ if (!bcast_address) {
+ talloc_free(iface);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = socket_listen(bcast_nbtsock->sock, bcast_address, 0, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Failed to bind to %s:%d - %s\n",
+ bcast, lp_nbt_port(lp_ctx), nt_errstr(status)));
+ talloc_free(iface);
+ return status;
+ }
+ talloc_free(bcast_address);
+
+ nbt_set_incoming_handler(bcast_nbtsock, nbtd_request_handler, iface);
+ }
+
+ /* listen for unicasts on port 137 */
+ iface->nbtsock = nbt_name_socket_init(iface, nbtsrv->task->event_ctx,
+ lp_iconv_convenience(nbtsrv->task->lp_ctx));
+ if (!iface->nbtsock) {
+ talloc_free(iface);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ unicast_address = socket_address_from_strings(iface->nbtsock,
+ iface->nbtsock->sock->backend_name,
+ bind_address, lp_nbt_port(lp_ctx));
+
+ status = socket_listen(iface->nbtsock->sock, unicast_address, 0, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Failed to bind to %s:%d - %s\n",
+ bind_address, lp_nbt_port(lp_ctx), nt_errstr(status)));
+ talloc_free(iface);
+ return status;
+ }
+ talloc_free(unicast_address);
+
+ nbt_set_incoming_handler(iface->nbtsock, nbtd_request_handler, iface);
+ nbt_set_unexpected_handler(iface->nbtsock, nbtd_unexpected_handler, iface);
+
+ /* also setup the datagram listeners */
+ status = nbtd_dgram_setup(iface, bind_address);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Failed to setup dgram listen on %s - %s\n",
+ bind_address, nt_errstr(status)));
+ talloc_free(iface);
+ return status;
+ }
+
+ if (strcmp(netmask, "0.0.0.0") == 0) {
+ DLIST_ADD(nbtsrv->bcast_interface, iface);
+ } else {
+ DLIST_ADD(nbtsrv->interfaces, iface);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ setup a socket for talking to our WINS servers
+*/
+static NTSTATUS nbtd_add_wins_socket(struct nbtd_server *nbtsrv)
+{
+ struct nbtd_interface *iface;
+
+ iface = talloc_zero(nbtsrv, struct nbtd_interface);
+ NT_STATUS_HAVE_NO_MEMORY(iface);
+
+ iface->nbtsrv = nbtsrv;
+
+ DLIST_ADD(nbtsrv->wins_interface, iface);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ setup our listening sockets on the configured network interfaces
+*/
+NTSTATUS nbtd_startup_interfaces(struct nbtd_server *nbtsrv, struct loadparm_context *lp_ctx,
+ struct interface *ifaces)
+{
+ int num_interfaces = iface_count(ifaces);
+ int i;
+ TALLOC_CTX *tmp_ctx = talloc_new(nbtsrv);
+ NTSTATUS status;
+
+ /* if we are allowing incoming packets from any address, then
+ we also need to bind to the wildcard address */
+ if (!lp_bind_interfaces_only(lp_ctx)) {
+ const char *primary_address;
+
+ /* the primary address is the address we will return
+ for non-WINS queries not made on a specific
+ interface */
+ if (num_interfaces > 0) {
+ primary_address = iface_n_ip(ifaces, 0);
+ } else {
+ primary_address = inet_ntoa(interpret_addr2(
+ lp_netbios_name(lp_ctx)));
+ }
+ primary_address = talloc_strdup(tmp_ctx, primary_address);
+ NT_STATUS_HAVE_NO_MEMORY(primary_address);
+
+ status = nbtd_add_socket(nbtsrv,
+ lp_ctx,
+ "0.0.0.0",
+ primary_address,
+ talloc_strdup(tmp_ctx, "255.255.255.255"),
+ talloc_strdup(tmp_ctx, "0.0.0.0"));
+ NT_STATUS_NOT_OK_RETURN(status);
+ }
+
+ for (i=0; i<num_interfaces; i++) {
+ const char *bcast = iface_n_bcast(ifaces, i);
+ const char *address, *netmask;
+
+ /* we can't assume every interface is broadcast capable */
+ if (bcast == NULL) continue;
+
+ address = talloc_strdup(tmp_ctx, iface_n_ip(ifaces, i));
+ bcast = talloc_strdup(tmp_ctx, bcast);
+ netmask = talloc_strdup(tmp_ctx, iface_n_netmask(ifaces, i));
+
+ status = nbtd_add_socket(nbtsrv, lp_ctx,
+ address, address, bcast, netmask);
+ NT_STATUS_NOT_OK_RETURN(status);
+ }
+
+ if (lp_wins_server_list(lp_ctx)) {
+ status = nbtd_add_wins_socket(nbtsrv);
+ NT_STATUS_NOT_OK_RETURN(status);
+ }
+
+ talloc_free(tmp_ctx);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ form a list of addresses that we should use in name query replies
+ we always place the IP in the given interface first
+*/
+const char **nbtd_address_list(struct nbtd_interface *iface, TALLOC_CTX *mem_ctx)
+{
+ struct nbtd_server *nbtsrv = iface->nbtsrv;
+ const char **ret = NULL;
+ struct nbtd_interface *iface2;
+ bool is_loopback = false;
+
+ if (iface->ip_address) {
+ is_loopback = iface_same_net(iface->ip_address, "127.0.0.1", "255.0.0.0");
+ ret = str_list_add(ret, iface->ip_address);
+ }
+
+ for (iface2=nbtsrv->interfaces;iface2;iface2=iface2->next) {
+ if (iface2 == iface) continue;
+
+ if (!iface2->ip_address) continue;
+
+ if (!is_loopback) {
+ if (iface_same_net(iface2->ip_address, "127.0.0.1", "255.0.0.0")) {
+ continue;
+ }
+ }
+
+ ret = str_list_add(ret, iface2->ip_address);
+ }
+
+ talloc_steal(mem_ctx, ret);
+
+ return ret;
+}
+
+
+/*
+ find the interface to use for sending a outgoing request
+*/
+struct nbtd_interface *nbtd_find_request_iface(struct nbtd_server *nbtd_server,
+ const char *address, bool allow_bcast_iface)
+{
+ struct nbtd_interface *cur;
+
+ /* try to find a exact match */
+ for (cur=nbtd_server->interfaces;cur;cur=cur->next) {
+ if (iface_same_net(address, cur->ip_address, cur->netmask)) {
+ DEBUG(10,("find interface for dst[%s] ip: %s/%s (iface[%p])\n",
+ address, cur->ip_address, cur->netmask, cur));
+ return cur;
+ }
+ }
+
+ /* no exact match, if we have the broadcast interface, use that */
+ if (allow_bcast_iface && nbtd_server->bcast_interface) {
+ cur = nbtd_server->bcast_interface;
+ DEBUG(10,("find interface for dst[%s] ip: %s/%s (bcast iface[%p])\n",
+ address, cur->ip_address, cur->netmask, cur));
+ return cur;
+ }
+
+ /* fallback to first interface */
+ cur = nbtd_server->interfaces;
+ DEBUG(10,("find interface for dst[%s] ip: %s/%s (default iface[%p])\n",
+ address, cur->ip_address, cur->netmask, cur));
+ return cur;
+}
+
+/*
+ * find the interface to use for sending a outgoing reply
+ */
+struct nbtd_interface *nbtd_find_reply_iface(struct nbtd_interface *iface,
+ const char *address, bool allow_bcast_iface)
+{
+ struct nbtd_server *nbtd_server = iface->nbtsrv;
+
+ /* first try to use the given interfacel when it's not the broadcast one */
+ if (iface != nbtd_server->bcast_interface) {
+ return iface;
+ }
+
+ return nbtd_find_request_iface(nbtd_server, address, allow_bcast_iface);
+}
diff --git a/source4/nbt_server/irpc.c b/source4/nbt_server/irpc.c
new file mode 100644
index 0000000000..951f1d296a
--- /dev/null
+++ b/source4/nbt_server/irpc.c
@@ -0,0 +1,210 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ irpc services for the NBT server
+
+ Copyright (C) Andrew Tridgell 2005
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "smbd/service_task.h"
+#include "smbd/service.h"
+#include "nbt_server/nbt_server.h"
+#include "nbt_server/wins/winsserver.h"
+#include "librpc/gen_ndr/ndr_irpc.h"
+#include "lib/socket/socket.h"
+#include "libcli/resolve/resolve.h"
+#include "librpc/gen_ndr/ndr_nbt.h"
+
+/*
+ serve out the nbt statistics
+*/
+static NTSTATUS nbtd_information(struct irpc_message *msg,
+ struct nbtd_information *r)
+{
+ struct nbtd_server *server = talloc_get_type(msg->private_data,
+ struct nbtd_server);
+
+ switch (r->in.level) {
+ case NBTD_INFO_STATISTICS:
+ r->out.info.stats = &server->stats;
+ break;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ winbind needs to be able to do a getdc request, but most (all?) windows
+ servers always send the reply to port 138, regardless of the request
+ port. To cope with this we use a irpc request to the NBT server
+ which has port 138 open, and thus can receive the replies
+*/
+struct getdc_state {
+ struct irpc_message *msg;
+ struct nbtd_getdcname *req;
+};
+
+static void getdc_recv_netlogon_reply(struct dgram_mailslot_handler *dgmslot,
+ struct nbt_dgram_packet *packet,
+ struct socket_address *src)
+{
+ struct getdc_state *s =
+ talloc_get_type(dgmslot->private_data, struct getdc_state);
+ const char *p;
+ struct nbt_netlogon_response netlogon;
+ NTSTATUS status;
+
+ status = dgram_mailslot_netlogon_parse_response(dgmslot, packet, packet,
+ &netlogon);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(5, ("dgram_mailslot_ntlogon_parse failed: %s\n",
+ nt_errstr(status)));
+ goto done;
+ }
+
+ /* We asked for version 1 only */
+ if (netlogon.response_type == NETLOGON_SAMLOGON
+ && netlogon.data.samlogon.ntver != NETLOGON_NT_VERSION_1) {
+ status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto done;
+ }
+
+ p = netlogon.data.samlogon.data.nt4.server;
+
+ DEBUG(10, ("NTLOGON_SAM_LOGON_REPLY: server: %s, user: %s, "
+ "domain: %s\n", p,
+ netlogon.data.samlogon.data.nt4.user_name,
+ netlogon.data.samlogon.data.nt4.domain));
+
+ if (*p == '\\') p += 1;
+ if (*p == '\\') p += 1;
+
+ s->req->out.dcname = talloc_strdup(s->req, p);
+ if (s->req->out.dcname == NULL) {
+ DEBUG(0, ("talloc failed\n"));
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ status = NT_STATUS_OK;
+
+ done:
+ irpc_send_reply(s->msg, status);
+}
+
+static NTSTATUS nbtd_getdcname(struct irpc_message *msg,
+ struct nbtd_getdcname *req)
+{
+ struct nbtd_server *server =
+ talloc_get_type(msg->private_data, struct nbtd_server);
+ struct nbtd_interface *iface = nbtd_find_request_iface(server, req->in.ip_address, true);
+ struct getdc_state *s;
+ struct nbt_netlogon_packet p;
+ struct NETLOGON_SAM_LOGON_REQUEST *r;
+ struct nbt_name src, dst;
+ struct socket_address *dest;
+ struct dgram_mailslot_handler *handler;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(0, ("nbtd_getdcname called\n"));
+
+ s = talloc(msg, struct getdc_state);
+ NT_STATUS_HAVE_NO_MEMORY(s);
+
+ s->msg = msg;
+ s->req = req;
+
+ handler = dgram_mailslot_temp(iface->dgmsock, NBT_MAILSLOT_GETDC,
+ getdc_recv_netlogon_reply, s);
+ NT_STATUS_HAVE_NO_MEMORY(handler);
+
+ ZERO_STRUCT(p);
+ p.command = LOGON_SAM_LOGON_REQUEST;
+ r = &p.req.logon;
+ r->request_count = 0;
+ r->computer_name = req->in.my_computername;
+ r->user_name = req->in.my_accountname;
+ r->mailslot_name = handler->mailslot_name;
+ r->acct_control = req->in.account_control;
+ r->sid = *req->in.domain_sid;
+ r->nt_version = NETLOGON_NT_VERSION_1;
+ r->lmnt_token = 0xffff;
+ r->lm20_token = 0xffff;
+
+ make_nbt_name_client(&src, req->in.my_computername);
+ make_nbt_name(&dst, req->in.domainname, 0x1c);
+
+ dest = socket_address_from_strings(msg, iface->dgmsock->sock->backend_name,
+ req->in.ip_address, 138);
+ NT_STATUS_HAVE_NO_MEMORY(dest);
+
+ status = dgram_mailslot_netlogon_send(iface->dgmsock,
+ &dst, dest,
+ NBT_MAILSLOT_NETLOGON,
+ &src, &p);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("dgram_mailslot_ntlogon_send failed: %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+
+ msg->defer_reply = true;
+ return NT_STATUS_OK;
+}
+
+
+/*
+ register the irpc handlers for the nbt server
+*/
+void nbtd_register_irpc(struct nbtd_server *nbtsrv)
+{
+ NTSTATUS status;
+ struct task_server *task = nbtsrv->task;
+
+ status = IRPC_REGISTER(task->msg_ctx, irpc, NBTD_INFORMATION,
+ nbtd_information, nbtsrv);
+ if (!NT_STATUS_IS_OK(status)) {
+ task_server_terminate(task, "nbtd failed to setup monitoring");
+ return;
+ }
+
+ status = IRPC_REGISTER(task->msg_ctx, irpc, NBTD_GETDCNAME,
+ nbtd_getdcname, nbtsrv);
+ if (!NT_STATUS_IS_OK(status)) {
+ task_server_terminate(task, "nbtd failed to setup getdcname "
+ "handler");
+ return;
+ }
+
+ status = IRPC_REGISTER(task->msg_ctx, irpc, NBTD_PROXY_WINS_CHALLENGE,
+ nbtd_proxy_wins_challenge, nbtsrv);
+ if (!NT_STATUS_IS_OK(status)) {
+ task_server_terminate(task, "nbtd failed to setup wins challenge "
+ "handler");
+ return;
+ }
+
+ status = IRPC_REGISTER(task->msg_ctx, irpc, NBTD_PROXY_WINS_RELEASE_DEMAND,
+ nbtd_proxy_wins_release_demand, nbtsrv);
+ if (!NT_STATUS_IS_OK(status)) {
+ task_server_terminate(task, "nbtd failed to setup wins release demand "
+ "handler");
+ return;
+ }
+}
diff --git a/source4/nbt_server/nbt_server.c b/source4/nbt_server/nbt_server.c
new file mode 100644
index 0000000000..e6ff5003bf
--- /dev/null
+++ b/source4/nbt_server/nbt_server.c
@@ -0,0 +1,97 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ NBT server task
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "smbd/service_task.h"
+#include "smbd/service.h"
+#include "nbt_server/nbt_server.h"
+#include "nbt_server/wins/winsserver.h"
+#include "system/network.h"
+#include "lib/socket/netif.h"
+#include "auth/auth.h"
+#include "dsdb/samdb/samdb.h"
+#include "param/param.h"
+
+/*
+ startup the nbtd task
+*/
+static void nbtd_task_init(struct task_server *task)
+{
+ struct nbtd_server *nbtsrv;
+ NTSTATUS status;
+ struct interface *ifaces;
+
+ load_interfaces(task, lp_interfaces(task->lp_ctx), &ifaces);
+
+ if (iface_count(ifaces) == 0) {
+ task_server_terminate(task, "nbtd: no network interfaces configured");
+ return;
+ }
+
+ task_server_set_title(task, "task[nbtd]");
+
+ nbtsrv = talloc(task, struct nbtd_server);
+ if (nbtsrv == NULL) {
+ task_server_terminate(task, "nbtd: out of memory");
+ return;
+ }
+
+ nbtsrv->task = task;
+ nbtsrv->interfaces = NULL;
+ nbtsrv->bcast_interface = NULL;
+ nbtsrv->wins_interface = NULL;
+
+ /* start listening on the configured network interfaces */
+ status = nbtd_startup_interfaces(nbtsrv, task->lp_ctx, ifaces);
+ if (!NT_STATUS_IS_OK(status)) {
+ task_server_terminate(task, "nbtd failed to setup interfaces");
+ return;
+ }
+
+ nbtsrv->sam_ctx = samdb_connect(nbtsrv, task->event_ctx, task->lp_ctx, system_session(nbtsrv, task->lp_ctx));
+ if (nbtsrv->sam_ctx == NULL) {
+ task_server_terminate(task, "nbtd failed to open samdb");
+ return;
+ }
+
+ /* start the WINS server, if appropriate */
+ status = nbtd_winsserver_init(nbtsrv);
+ if (!NT_STATUS_IS_OK(status)) {
+ task_server_terminate(task, "nbtd failed to start WINS server");
+ return;
+ }
+
+ nbtd_register_irpc(nbtsrv);
+
+ /* start the process of registering our names on all interfaces */
+ nbtd_register_names(nbtsrv);
+
+ irpc_add_name(task->msg_ctx, "nbt_server");
+}
+
+
+/*
+ register ourselves as a available server
+*/
+NTSTATUS server_service_nbtd_init(void)
+{
+ return register_server_service("nbt", nbtd_task_init);
+}
diff --git a/source4/nbt_server/nbt_server.h b/source4/nbt_server/nbt_server.h
new file mode 100644
index 0000000000..c80e5bfca0
--- /dev/null
+++ b/source4/nbt_server/nbt_server.h
@@ -0,0 +1,94 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ NBT server structures
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "../libcli/nbt/libnbt.h"
+#include "libcli/wrepl/winsrepl.h"
+#include "libcli/dgram/libdgram.h"
+#include "librpc/gen_ndr/irpc.h"
+#include "lib/messaging/irpc.h"
+
+/*
+ a list of our registered names on each interface
+*/
+struct nbtd_iface_name {
+ struct nbtd_iface_name *next, *prev;
+ struct nbtd_interface *iface;
+ struct nbt_name name;
+ uint16_t nb_flags;
+ struct timeval registration_time;
+ uint32_t ttl;
+
+ /* if registered with a wins server, then this lists the server being
+ used */
+ const char *wins_server;
+};
+
+struct nbtd_wins_wack_state;
+
+/* a list of network interfaces we are listening on */
+struct nbtd_interface {
+ struct nbtd_interface *next, *prev;
+ struct nbtd_server *nbtsrv;
+ const char *ip_address;
+ const char *bcast_address;
+ const char *netmask;
+ struct nbt_name_socket *nbtsock;
+ struct nbt_dgram_socket *dgmsock;
+ struct nbtd_iface_name *names;
+ struct nbtd_wins_wack_state *wack_queue;
+};
+
+
+/*
+ top level context structure for the nbt server
+*/
+struct nbtd_server {
+ struct task_server *task;
+
+ /* the list of local network interfaces */
+ struct nbtd_interface *interfaces;
+
+ /* broadcast interface used for receiving packets only */
+ struct nbtd_interface *bcast_interface;
+
+ /* wins client interface - used for registering and refreshing
+ our names with a WINS server */
+ struct nbtd_interface *wins_interface;
+
+ struct wins_server *winssrv;
+
+ struct nbtd_statistics stats;
+
+ struct ldb_context *sam_ctx;
+};
+
+
+
+/* check a condition on an incoming packet */
+#define NBTD_ASSERT_PACKET(packet, src, test) do { \
+ if (!(test)) { \
+ nbtd_bad_packet(packet, src, #test); \
+ return; \
+ } \
+} while (0)
+
+struct interface;
+#include "nbt_server/nbt_server_proto.h"
diff --git a/source4/nbt_server/nodestatus.c b/source4/nbt_server/nodestatus.c
new file mode 100644
index 0000000000..8dc1f45053
--- /dev/null
+++ b/source4/nbt_server/nodestatus.c
@@ -0,0 +1,126 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ answer node status queries
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "../lib/util/dlinklist.h"
+#include "system/network.h"
+#include "nbt_server/nbt_server.h"
+#include "lib/socket/socket.h"
+#include "librpc/gen_ndr/ndr_nbt.h"
+
+/*
+ send a name status reply
+*/
+static void nbtd_node_status_reply(struct nbt_name_socket *nbtsock,
+ struct nbt_name_packet *request_packet,
+ struct socket_address *src,
+ struct nbt_name *name,
+ struct nbtd_interface *iface)
+{
+ struct nbt_name_packet *packet;
+ uint32_t name_count;
+ struct nbtd_iface_name *iname;
+ struct nbtd_server *nbtsrv = iface->nbtsrv;
+
+ /* work out how many names to send */
+ name_count = 0;
+ for (iname=iface->names;iname;iname=iname->next) {
+ if ((iname->nb_flags & NBT_NM_ACTIVE) &&
+ strcmp(iname->name.name, "*") != 0) {
+ name_count++;
+ }
+ }
+
+ packet = talloc_zero(nbtsock, struct nbt_name_packet);
+ if (packet == NULL) return;
+
+ packet->name_trn_id = request_packet->name_trn_id;
+ packet->ancount = 1;
+ packet->operation = NBT_OPCODE_QUERY | NBT_FLAG_REPLY | NBT_FLAG_AUTHORITIVE;
+
+ packet->answers = talloc_array(packet, struct nbt_res_rec, 1);
+ if (packet->answers == NULL) goto failed;
+
+ packet->answers[0].name = *name;
+ packet->answers[0].rr_type = NBT_QTYPE_STATUS;
+ packet->answers[0].rr_class = NBT_QCLASS_IP;
+ packet->answers[0].ttl = 0;
+ packet->answers[0].rdata.status.num_names = name_count;
+ packet->answers[0].rdata.status.names = talloc_array(packet->answers,
+ struct nbt_status_name, name_count);
+ if (packet->answers[0].rdata.status.names == NULL) goto failed;
+
+ name_count = 0;
+ for (iname=iface->names;iname;iname=iname->next) {
+ if ((iname->nb_flags & NBT_NM_ACTIVE) &&
+ strcmp(iname->name.name, "*") != 0) {
+ struct nbt_status_name *n = &packet->answers[0].rdata.status.names[name_count];
+ n->name = talloc_asprintf(packet->answers, "%-15s", iname->name.name);
+ if (n->name == NULL) goto failed;
+ n->type = iname->name.type;
+ n->nb_flags = iname->nb_flags;
+ name_count++;
+ }
+ }
+ /* we deliberately don't fill in the statistics structure as
+ it could lead to giving attackers too much information */
+ ZERO_STRUCT(packet->answers[0].rdata.status.statistics);
+
+ DEBUG(7,("Sending node status reply for %s to %s:%d\n",
+ nbt_name_string(packet, name), src->addr, src->port));
+
+ nbtsrv->stats.total_sent++;
+ nbt_name_reply_send(nbtsock, src, packet);
+
+failed:
+ talloc_free(packet);
+}
+
+
+/*
+ answer a node status query
+*/
+void nbtd_query_status(struct nbt_name_socket *nbtsock,
+ struct nbt_name_packet *packet,
+ struct socket_address *src)
+{
+ struct nbt_name *name;
+ struct nbtd_iface_name *iname;
+ struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private_data,
+ struct nbtd_interface);
+
+ NBTD_ASSERT_PACKET(packet, src, packet->qdcount == 1);
+ NBTD_ASSERT_PACKET(packet, src, packet->questions[0].question_type == NBT_QTYPE_STATUS);
+ NBTD_ASSERT_PACKET(packet, src, packet->questions[0].question_class == NBT_QCLASS_IP);
+
+ /* see if we have the requested name on this interface */
+ name = &packet->questions[0].name;
+
+ iname = nbtd_find_iname(iface, name, NBT_NM_ACTIVE);
+ if (iname == NULL) {
+ DEBUG(7,("Node status query for %s from %s - not found on %s\n",
+ nbt_name_string(packet, name), src->addr, iface->ip_address));
+ return;
+ }
+
+ nbtd_node_status_reply(nbtsock, packet, src,
+ &iname->name, iface);
+}
diff --git a/source4/nbt_server/packet.c b/source4/nbt_server/packet.c
new file mode 100644
index 0000000000..ff4e94fef9
--- /dev/null
+++ b/source4/nbt_server/packet.c
@@ -0,0 +1,343 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ packet utility functions
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "nbt_server/nbt_server.h"
+#include "smbd/service_task.h"
+#include "lib/socket/socket.h"
+#include "librpc/gen_ndr/ndr_nbt.h"
+#include "param/param.h"
+
+/*
+ we received a badly formed packet - log it
+*/
+void nbtd_bad_packet(struct nbt_name_packet *packet,
+ const struct socket_address *src, const char *reason)
+{
+ DEBUG(2,("nbtd: bad packet '%s' from %s:%d\n", reason, src->addr, src->port));
+ if (DEBUGLVL(5)) {
+ NDR_PRINT_DEBUG(nbt_name_packet, packet);
+ }
+}
+
+
+/*
+ see if an incoming packet is a broadcast packet from one of our own
+ interfaces
+*/
+bool nbtd_self_packet_and_bcast(struct nbt_name_socket *nbtsock,
+ struct nbt_name_packet *packet,
+ const struct socket_address *src)
+{
+ struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private_data,
+ struct nbtd_interface);
+
+ /* if its not a broadcast then its not considered a self packet */
+ if (!(packet->operation & NBT_FLAG_BROADCAST)) {
+ return false;
+ }
+
+ /*
+ * this uses the fact that iface->nbtsock is the unicast listen address
+ * if the interface isn't the global bcast interface
+ *
+ * so if the request was directed to the unicast address it isn't a broadcast
+ * message
+ */
+ if (iface->nbtsock == nbtsock &&
+ iface != iface->nbtsrv->bcast_interface) {
+ return false;
+ }
+
+ return nbtd_self_packet(nbtsock, packet, src);
+}
+
+bool nbtd_self_packet(struct nbt_name_socket *nbtsock,
+ struct nbt_name_packet *packet,
+ const struct socket_address *src)
+{
+ struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private_data,
+ struct nbtd_interface);
+ struct nbtd_server *nbtsrv = iface->nbtsrv;
+
+ /* if its not from the nbt port, then it wasn't a broadcast from us */
+ if (src->port != lp_nbt_port(iface->nbtsrv->task->lp_ctx)) {
+ return false;
+ }
+
+ /* we have to loop over our interface list, seeing if its from
+ one of our own interfaces */
+ for (iface=nbtsrv->interfaces;iface;iface=iface->next) {
+ if (strcmp(src->addr, iface->ip_address) == 0) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+/*
+ send a name query reply
+*/
+void nbtd_name_query_reply(struct nbt_name_socket *nbtsock,
+ struct nbt_name_packet *request_packet,
+ struct socket_address *src,
+ struct nbt_name *name, uint32_t ttl,
+ uint16_t nb_flags, const char **addresses)
+{
+ struct nbt_name_packet *packet;
+ size_t num_addresses = str_list_length(addresses);
+ struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private_data,
+ struct nbtd_interface);
+ struct nbtd_server *nbtsrv = iface->nbtsrv;
+ int i;
+
+ if (num_addresses == 0) {
+ DEBUG(3,("No addresses in name query reply - failing\n"));
+ return;
+ }
+
+ packet = talloc_zero(nbtsock, struct nbt_name_packet);
+ if (packet == NULL) return;
+
+ packet->name_trn_id = request_packet->name_trn_id;
+ packet->ancount = 1;
+ packet->operation =
+ NBT_FLAG_REPLY |
+ NBT_OPCODE_QUERY |
+ NBT_FLAG_AUTHORITIVE |
+ NBT_FLAG_RECURSION_DESIRED |
+ NBT_FLAG_RECURSION_AVAIL;
+
+ packet->answers = talloc_array(packet, struct nbt_res_rec, 1);
+ if (packet->answers == NULL) goto failed;
+
+ packet->answers[0].name = *name;
+ packet->answers[0].rr_type = NBT_QTYPE_NETBIOS;
+ packet->answers[0].rr_class = NBT_QCLASS_IP;
+ packet->answers[0].ttl = ttl;
+ packet->answers[0].rdata.netbios.length = num_addresses*6;
+ packet->answers[0].rdata.netbios.addresses =
+ talloc_array(packet->answers, struct nbt_rdata_address, num_addresses);
+ if (packet->answers[0].rdata.netbios.addresses == NULL) goto failed;
+
+ for (i=0;i<num_addresses;i++) {
+ struct nbt_rdata_address *addr =
+ &packet->answers[0].rdata.netbios.addresses[i];
+ addr->nb_flags = nb_flags;
+ addr->ipaddr = talloc_strdup(packet->answers, addresses[i]);
+ if (addr->ipaddr == NULL) goto failed;
+ }
+
+ DEBUG(7,("Sending name query reply for %s at %s to %s:%d\n",
+ nbt_name_string(packet, name), addresses[0], src->addr, src->port));
+
+ nbtsrv->stats.total_sent++;
+ nbt_name_reply_send(nbtsock, src, packet);
+
+failed:
+ talloc_free(packet);
+}
+
+
+/*
+ send a negative name query reply
+*/
+void nbtd_negative_name_query_reply(struct nbt_name_socket *nbtsock,
+ struct nbt_name_packet *request_packet,
+ struct socket_address *src)
+{
+ struct nbt_name_packet *packet;
+ struct nbt_name *name = &request_packet->questions[0].name;
+ struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private_data,
+ struct nbtd_interface);
+ struct nbtd_server *nbtsrv = iface->nbtsrv;
+
+ packet = talloc_zero(nbtsock, struct nbt_name_packet);
+ if (packet == NULL) return;
+
+ packet->name_trn_id = request_packet->name_trn_id;
+ packet->ancount = 1;
+ packet->operation =
+ NBT_FLAG_REPLY |
+ NBT_OPCODE_QUERY |
+ NBT_FLAG_AUTHORITIVE |
+ NBT_RCODE_NAM;
+
+ packet->answers = talloc_array(packet, struct nbt_res_rec, 1);
+ if (packet->answers == NULL) goto failed;
+
+ packet->answers[0].name = *name;
+ packet->answers[0].rr_type = NBT_QTYPE_NULL;
+ packet->answers[0].rr_class = NBT_QCLASS_IP;
+ packet->answers[0].ttl = 0;
+ ZERO_STRUCT(packet->answers[0].rdata);
+
+ DEBUG(7,("Sending negative name query reply for %s to %s:%d\n",
+ nbt_name_string(packet, name), src->addr, src->port));
+
+ nbtsrv->stats.total_sent++;
+ nbt_name_reply_send(nbtsock, src, packet);
+
+failed:
+ talloc_free(packet);
+}
+
+/*
+ send a name registration reply
+*/
+void nbtd_name_registration_reply(struct nbt_name_socket *nbtsock,
+ struct nbt_name_packet *request_packet,
+ struct socket_address *src,
+ uint8_t rcode)
+{
+ struct nbt_name_packet *packet;
+ struct nbt_name *name = &request_packet->questions[0].name;
+ struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private_data,
+ struct nbtd_interface);
+ struct nbtd_server *nbtsrv = iface->nbtsrv;
+
+ packet = talloc_zero(nbtsock, struct nbt_name_packet);
+ if (packet == NULL) return;
+
+ packet->name_trn_id = request_packet->name_trn_id;
+ packet->ancount = 1;
+ packet->operation =
+ NBT_FLAG_REPLY |
+ NBT_OPCODE_REGISTER |
+ NBT_FLAG_AUTHORITIVE |
+ NBT_FLAG_RECURSION_DESIRED |
+ NBT_FLAG_RECURSION_AVAIL |
+ rcode;
+
+ packet->answers = talloc_array(packet, struct nbt_res_rec, 1);
+ if (packet->answers == NULL) goto failed;
+
+ packet->answers[0].name = *name;
+ packet->answers[0].rr_type = NBT_QTYPE_NETBIOS;
+ packet->answers[0].rr_class = NBT_QCLASS_IP;
+ packet->answers[0].ttl = request_packet->additional[0].ttl;
+ packet->answers[0].rdata = request_packet->additional[0].rdata;
+
+ DEBUG(7,("Sending %s name registration reply for %s to %s:%d\n",
+ rcode==0?"positive":"negative",
+ nbt_name_string(packet, name), src->addr, src->port));
+
+ nbtsrv->stats.total_sent++;
+ nbt_name_reply_send(nbtsock, src, packet);
+
+failed:
+ talloc_free(packet);
+}
+
+
+/*
+ send a name release reply
+*/
+void nbtd_name_release_reply(struct nbt_name_socket *nbtsock,
+ struct nbt_name_packet *request_packet,
+ struct socket_address *src,
+ uint8_t rcode)
+{
+ struct nbt_name_packet *packet;
+ struct nbt_name *name = &request_packet->questions[0].name;
+ struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private_data,
+ struct nbtd_interface);
+ struct nbtd_server *nbtsrv = iface->nbtsrv;
+
+ packet = talloc_zero(nbtsock, struct nbt_name_packet);
+ if (packet == NULL) return;
+
+ packet->name_trn_id = request_packet->name_trn_id;
+ packet->ancount = 1;
+ packet->operation =
+ NBT_FLAG_REPLY |
+ NBT_OPCODE_RELEASE |
+ NBT_FLAG_AUTHORITIVE |
+ rcode;
+
+ packet->answers = talloc_array(packet, struct nbt_res_rec, 1);
+ if (packet->answers == NULL) goto failed;
+
+ packet->answers[0].name = *name;
+ packet->answers[0].rr_type = NBT_QTYPE_NETBIOS;
+ packet->answers[0].rr_class = NBT_QCLASS_IP;
+ packet->answers[0].ttl = request_packet->additional[0].ttl;
+ packet->answers[0].rdata = request_packet->additional[0].rdata;
+
+ DEBUG(7,("Sending %s name release reply for %s to %s:%d\n",
+ rcode==0?"positive":"negative",
+ nbt_name_string(packet, name), src->addr, src->port));
+
+ nbtsrv->stats.total_sent++;
+ nbt_name_reply_send(nbtsock, src, packet);
+
+failed:
+ talloc_free(packet);
+}
+
+
+/*
+ send a WACK reply
+*/
+void nbtd_wack_reply(struct nbt_name_socket *nbtsock,
+ struct nbt_name_packet *request_packet,
+ struct socket_address *src,
+ uint32_t ttl)
+{
+ struct nbt_name_packet *packet;
+ struct nbt_name *name = &request_packet->questions[0].name;
+ struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private_data,
+ struct nbtd_interface);
+ struct nbtd_server *nbtsrv = iface->nbtsrv;
+
+ packet = talloc_zero(nbtsock, struct nbt_name_packet);
+ if (packet == NULL) return;
+
+ packet->name_trn_id = request_packet->name_trn_id;
+ packet->ancount = 1;
+ packet->operation =
+ NBT_FLAG_REPLY |
+ NBT_OPCODE_WACK |
+ NBT_FLAG_AUTHORITIVE;
+
+ packet->answers = talloc_array(packet, struct nbt_res_rec, 1);
+ if (packet->answers == NULL) goto failed;
+
+ packet->answers[0].name = *name;
+ packet->answers[0].rr_type = NBT_QTYPE_NETBIOS;
+ packet->answers[0].rr_class = NBT_QCLASS_IP;
+ packet->answers[0].ttl = ttl;
+ packet->answers[0].rdata.data.length = 2;
+ packet->answers[0].rdata.data.data = talloc_array(packet, uint8_t, 2);
+ if (packet->answers[0].rdata.data.data == NULL) goto failed;
+ RSSVAL(packet->answers[0].rdata.data.data, 0, request_packet->operation);
+
+ DEBUG(7,("Sending WACK reply for %s to %s:%d\n",
+ nbt_name_string(packet, name), src->addr, src->port));
+
+ nbtsrv->stats.total_sent++;
+ nbt_name_reply_send(nbtsock, src, packet);
+
+failed:
+ talloc_free(packet);
+}
diff --git a/source4/nbt_server/query.c b/source4/nbt_server/query.c
new file mode 100644
index 0000000000..be1492f1cd
--- /dev/null
+++ b/source4/nbt_server/query.c
@@ -0,0 +1,102 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ answer name queries
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "../lib/util/dlinklist.h"
+#include "system/network.h"
+#include "nbt_server/nbt_server.h"
+#include "nbt_server/wins/winsserver.h"
+#include "smbd/service_task.h"
+#include "librpc/gen_ndr/ndr_nbt.h"
+#include "lib/socket/socket.h"
+#include "param/param.h"
+
+/*
+ answer a name query
+*/
+void nbtd_request_query(struct nbt_name_socket *nbtsock,
+ struct nbt_name_packet *packet,
+ struct socket_address *src)
+{
+ struct nbtd_iface_name *iname;
+ struct nbt_name *name;
+ struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private_data,
+ struct nbtd_interface);
+
+ /* see if its a node status query */
+ if (packet->qdcount == 1 &&
+ packet->questions[0].question_type == NBT_QTYPE_STATUS) {
+ nbtd_query_status(nbtsock, packet, src);
+ return;
+ }
+
+ NBTD_ASSERT_PACKET(packet, src, packet->qdcount == 1);
+ NBTD_ASSERT_PACKET(packet, src,
+ packet->questions[0].question_type == NBT_QTYPE_NETBIOS);
+ NBTD_ASSERT_PACKET(packet, src,
+ packet->questions[0].question_class == NBT_QCLASS_IP);
+
+ /* see if we have the requested name on this interface */
+ name = &packet->questions[0].name;
+
+ iname = nbtd_find_iname(iface, name, 0);
+ if (iname == NULL) {
+ /* don't send negative replies to broadcast queries */
+ if (packet->operation & NBT_FLAG_BROADCAST) {
+ return;
+ }
+
+ if (packet->operation & NBT_FLAG_RECURSION_DESIRED) {
+ nbtd_winsserver_request(nbtsock, packet, src);
+ return;
+ }
+
+ /* otherwise send a negative reply */
+ nbtd_negative_name_query_reply(nbtsock, packet, src);
+ return;
+ }
+
+ /*
+ * normally we should forward all queries with the
+ * recursion desired flag to the wins server, but this
+ * breaks are winsclient code, when doing mhomed registrations
+ */
+ if (!(packet->operation & NBT_FLAG_BROADCAST) &&
+ (packet->operation & NBT_FLAG_RECURSION_DESIRED) &&
+ (iname->nb_flags & NBT_NM_GROUP) &&
+ lp_wins_support(iface->nbtsrv->task->lp_ctx)) {
+ nbtd_winsserver_request(nbtsock, packet, src);
+ return;
+ }
+
+ /* if the name is not yet active and its a broadcast query then
+ ignore it for now */
+ if (!(iname->nb_flags & NBT_NM_ACTIVE) &&
+ (packet->operation & NBT_FLAG_BROADCAST)) {
+ DEBUG(7,("Query for %s from %s - name not active yet on %s\n",
+ nbt_name_string(packet, name), src->addr, iface->ip_address));
+ return;
+ }
+
+ nbtd_name_query_reply(nbtsock, packet, src,
+ &iname->name, iname->ttl, iname->nb_flags,
+ nbtd_address_list(iface, packet));
+}
diff --git a/source4/nbt_server/register.c b/source4/nbt_server/register.c
new file mode 100644
index 0000000000..a2840ab5c0
--- /dev/null
+++ b/source4/nbt_server/register.c
@@ -0,0 +1,293 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ register our names
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/events/events.h"
+#include "../lib/util/dlinklist.h"
+#include "nbt_server/nbt_server.h"
+#include "smbd/service_task.h"
+#include "libcli/composite/composite.h"
+#include "librpc/gen_ndr/ndr_samr.h"
+#include "nbt_server/wins/winsserver.h"
+#include "librpc/gen_ndr/ndr_nbt.h"
+#include "dsdb/samdb/samdb.h"
+#include "param/param.h"
+
+static void nbtd_start_refresh_timer(struct nbtd_iface_name *iname);
+
+/*
+ a name refresh request has completed
+*/
+static void refresh_completion_handler(struct nbt_name_request *req)
+{
+ struct nbtd_iface_name *iname = talloc_get_type(req->async.private_data,
+ struct nbtd_iface_name);
+ NTSTATUS status;
+ struct nbt_name_refresh io;
+ TALLOC_CTX *tmp_ctx = talloc_new(iname);
+
+ status = nbt_name_refresh_recv(req, tmp_ctx, &io);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
+ DEBUG(4,("Refreshed name %s with %s on interface %s\n",
+ nbt_name_string(tmp_ctx, &iname->name),
+ iname->iface->ip_address, iname->iface->bcast_address));
+ iname->registration_time = timeval_current();
+ nbtd_start_refresh_timer(iname);
+ talloc_free(tmp_ctx);
+ return;
+ }
+
+ iname->nb_flags |= NBT_NM_CONFLICT;
+ iname->nb_flags &= ~NBT_NM_ACTIVE;
+
+ if (NT_STATUS_IS_OK(status)) {
+ DEBUG(1,("Name conflict from %s refreshing name %s with %s on interface %s - %s\n",
+ io.out.reply_addr, nbt_name_string(tmp_ctx, &iname->name),
+ iname->iface->ip_address, iname->iface->bcast_address,
+ nt_errstr(nbt_rcode_to_ntstatus(io.out.rcode))));
+ } else {
+ DEBUG(1,("Error refreshing name %s with %s on interface %s - %s\n",
+ nbt_name_string(tmp_ctx, &iname->name),
+ iname->iface->ip_address, iname->iface->bcast_address,
+ nt_errstr(status)));
+ }
+
+ talloc_free(tmp_ctx);
+}
+
+
+/*
+ handle name refresh timer events
+*/
+static void name_refresh_handler(struct tevent_context *ev, struct tevent_timer *te,
+ struct timeval t, void *private_data)
+{
+ struct nbtd_iface_name *iname = talloc_get_type(private_data, struct nbtd_iface_name);
+ struct nbtd_interface *iface = iname->iface;
+ struct nbt_name_register io;
+ struct nbt_name_request *req;
+ struct nbtd_server *nbtsrv = iface->nbtsrv;
+
+ /* setup a single name register request. Notice that we don't
+ use a name refresh request, as Windows and Samba3 do not
+ defend against broadcast name refresh packets. So for this
+ to be of any use at all, we need to refresh using name
+ registration packets */
+ io.in.name = iname->name;
+ io.in.dest_addr = iface->bcast_address;
+ io.in.dest_port = lp_nbt_port(iface->nbtsrv->task->lp_ctx);
+ io.in.address = iface->ip_address;
+ io.in.nb_flags = iname->nb_flags;
+ io.in.ttl = iname->ttl;
+ io.in.register_demand = false;
+ io.in.broadcast = true;
+ io.in.multi_homed = false;
+ io.in.timeout = 3;
+ io.in.retries = 0;
+
+ nbtsrv->stats.total_sent++;
+ req = nbt_name_register_send(iface->nbtsock, &io);
+ if (req == NULL) return;
+
+ req->async.fn = refresh_completion_handler;
+ req->async.private_data = iname;
+}
+
+
+/*
+ start a timer to refresh this name
+*/
+static void nbtd_start_refresh_timer(struct nbtd_iface_name *iname)
+{
+ uint32_t refresh_time;
+ uint32_t max_refresh_time = lp_parm_int(iname->iface->nbtsrv->task->lp_ctx, NULL, "nbtd", "max_refresh_time", 7200);
+
+ refresh_time = MIN(max_refresh_time, iname->ttl/2);
+
+ event_add_timed(iname->iface->nbtsrv->task->event_ctx,
+ iname,
+ timeval_add(&iname->registration_time, refresh_time, 0),
+ name_refresh_handler, iname);
+}
+
+
+/*
+ a name registration has completed
+*/
+static void nbtd_register_handler(struct composite_context *creq)
+{
+ struct nbtd_iface_name *iname = talloc_get_type(creq->async.private_data,
+ struct nbtd_iface_name);
+ NTSTATUS status;
+ TALLOC_CTX *tmp_ctx = talloc_new(iname);
+
+ status = nbt_name_register_bcast_recv(creq);
+ if (NT_STATUS_IS_OK(status)) {
+ /* good - nobody complained about our registration */
+ iname->nb_flags |= NBT_NM_ACTIVE;
+ DEBUG(3,("Registered %s with %s on interface %s\n",
+ nbt_name_string(tmp_ctx, &iname->name),
+ iname->iface->ip_address, iname->iface->bcast_address));
+ iname->registration_time = timeval_current();
+ talloc_free(tmp_ctx);
+ nbtd_start_refresh_timer(iname);
+ return;
+ }
+
+ /* someone must have replied with an objection! */
+ iname->nb_flags |= NBT_NM_CONFLICT;
+
+ DEBUG(1,("Error registering %s with %s on interface %s - %s\n",
+ nbt_name_string(tmp_ctx, &iname->name),
+ iname->iface->ip_address, iname->iface->bcast_address,
+ nt_errstr(status)));
+ talloc_free(tmp_ctx);
+}
+
+
+/*
+ register a name on a network interface
+*/
+static void nbtd_register_name_iface(struct nbtd_interface *iface,
+ const char *name, enum nbt_name_type type,
+ uint16_t nb_flags)
+{
+ struct nbtd_iface_name *iname;
+ const char *scope = lp_netbios_scope(iface->nbtsrv->task->lp_ctx);
+ struct nbt_name_register_bcast io;
+ struct composite_context *creq;
+ struct nbtd_server *nbtsrv = iface->nbtsrv;
+
+ iname = talloc(iface, struct nbtd_iface_name);
+ if (!iname) return;
+
+ iname->iface = iface;
+ iname->name.name = strupper_talloc(iname, name);
+ iname->name.type = type;
+ if (scope && *scope) {
+ iname->name.scope = strupper_talloc(iname, scope);
+ } else {
+ iname->name.scope = NULL;
+ }
+ iname->nb_flags = nb_flags;
+ iname->ttl = lp_parm_int(iface->nbtsrv->task->lp_ctx, NULL, "nbtd", "bcast_ttl", 300000);
+ iname->registration_time = timeval_zero();
+ iname->wins_server = NULL;
+
+ DLIST_ADD_END(iface->names, iname, struct nbtd_iface_name *);
+
+ if (nb_flags & NBT_NM_PERMANENT) {
+ /* permanent names are not announced and are immediately active */
+ iname->nb_flags |= NBT_NM_ACTIVE;
+ iname->ttl = 0;
+ return;
+ }
+
+ /* if this is the wins interface, then we need to do a special
+ wins name registration */
+ if (iface == iface->nbtsrv->wins_interface) {
+ nbtd_winsclient_register(iname);
+ return;
+ }
+
+ /* setup a broadcast name registration request */
+ io.in.name = iname->name;
+ io.in.dest_addr = iface->bcast_address;
+ io.in.dest_port = lp_nbt_port(iface->nbtsrv->task->lp_ctx);
+ io.in.address = iface->ip_address;
+ io.in.nb_flags = nb_flags;
+ io.in.ttl = iname->ttl;
+
+ nbtsrv->stats.total_sent++;
+ creq = nbt_name_register_bcast_send(iface->nbtsock, &io);
+ if (creq == NULL) return;
+
+ creq->async.fn = nbtd_register_handler;
+ creq->async.private_data = iname;
+}
+
+
+/*
+ register one name on all our interfaces
+*/
+void nbtd_register_name(struct nbtd_server *nbtsrv,
+ const char *name, enum nbt_name_type type,
+ uint16_t nb_flags)
+{
+ struct nbtd_interface *iface;
+
+ /* register with all the local interfaces */
+ for (iface=nbtsrv->interfaces;iface;iface=iface->next) {
+ nbtd_register_name_iface(iface, name, type, nb_flags);
+ }
+
+ /* register on our general broadcast interface as a permanent name */
+ if (nbtsrv->bcast_interface) {
+ nbtd_register_name_iface(nbtsrv->bcast_interface, name, type,
+ nb_flags | NBT_NM_PERMANENT);
+ }
+
+ /* register with our WINS servers */
+ if (nbtsrv->wins_interface) {
+ nbtd_register_name_iface(nbtsrv->wins_interface, name, type, nb_flags);
+ }
+}
+
+
+/*
+ register our names on all interfaces
+*/
+void nbtd_register_names(struct nbtd_server *nbtsrv)
+{
+ uint16_t nb_flags = NBT_NODE_M;
+ const char **aliases;
+
+ /* note that we don't initially mark the names "ACTIVE". They are
+ marked active once registration is successful */
+ nbtd_register_name(nbtsrv, lp_netbios_name(nbtsrv->task->lp_ctx), NBT_NAME_CLIENT, nb_flags);
+ nbtd_register_name(nbtsrv, lp_netbios_name(nbtsrv->task->lp_ctx), NBT_NAME_USER, nb_flags);
+ nbtd_register_name(nbtsrv, lp_netbios_name(nbtsrv->task->lp_ctx), NBT_NAME_SERVER, nb_flags);
+
+ aliases = lp_netbios_aliases(nbtsrv->task->lp_ctx);
+ while (aliases && aliases[0]) {
+ nbtd_register_name(nbtsrv, aliases[0], NBT_NAME_CLIENT, nb_flags);
+ nbtd_register_name(nbtsrv, aliases[0], NBT_NAME_SERVER, nb_flags);
+ aliases++;
+ }
+
+ if (lp_server_role(nbtsrv->task->lp_ctx) == ROLE_DOMAIN_CONTROLLER) {
+ bool is_pdc = samdb_is_pdc(nbtsrv->sam_ctx);
+ if (is_pdc) {
+ nbtd_register_name(nbtsrv, lp_workgroup(nbtsrv->task->lp_ctx),
+ NBT_NAME_PDC, nb_flags);
+ }
+ nbtd_register_name(nbtsrv, lp_workgroup(nbtsrv->task->lp_ctx),
+ NBT_NAME_LOGON, nb_flags | NBT_NM_GROUP);
+ }
+
+ nb_flags |= NBT_NM_GROUP;
+ nbtd_register_name(nbtsrv, lp_workgroup(nbtsrv->task->lp_ctx), NBT_NAME_CLIENT, nb_flags);
+
+ nb_flags |= NBT_NM_PERMANENT;
+ nbtd_register_name(nbtsrv, "__SAMBA__", NBT_NAME_CLIENT, nb_flags);
+ nbtd_register_name(nbtsrv, "__SAMBA__", NBT_NAME_SERVER, nb_flags);
+ nbtd_register_name(nbtsrv, "*", NBT_NAME_CLIENT, nb_flags);
+}
diff --git a/source4/nbt_server/wins/wins_dns_proxy.c b/source4/nbt_server/wins/wins_dns_proxy.c
new file mode 100644
index 0000000000..cd605907a8
--- /dev/null
+++ b/source4/nbt_server/wins/wins_dns_proxy.c
@@ -0,0 +1,98 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ wins server dns proxy
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "nbt_server/nbt_server.h"
+#include "nbt_server/wins/winsdb.h"
+#include "nbt_server/wins/winsserver.h"
+#include "system/time.h"
+#include "libcli/composite/composite.h"
+#include "smbd/service_task.h"
+#include "libcli/resolve/resolve.h"
+
+struct wins_dns_proxy_state {
+ struct nbt_name_socket *nbtsock;
+ struct nbt_name_packet *packet;
+ struct socket_address *src;
+};
+
+static void nbtd_wins_dns_proxy_handler(struct composite_context *creq)
+{
+ NTSTATUS status;
+ struct wins_dns_proxy_state *s = talloc_get_type(creq->async.private_data,
+ struct wins_dns_proxy_state);
+ struct nbt_name *name = &s->packet->questions[0].name;
+ const char *address;
+ const char **addresses;
+ uint16_t nb_flags = 0; /* TODO: ... */
+
+ status = resolve_name_recv(creq, s->packet, &address);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto notfound;
+ }
+
+ addresses = str_list_add(NULL, address);
+ talloc_steal(s->packet, addresses);
+ if (!addresses) goto notfound;
+
+ nbtd_name_query_reply(s->nbtsock, s->packet, s->src, name,
+ 0, nb_flags, addresses);
+ return;
+notfound:
+ nbtd_negative_name_query_reply(s->nbtsock, s->packet, s->src);
+}
+
+/*
+ dns proxy query a name
+*/
+void nbtd_wins_dns_proxy_query(struct nbt_name_socket *nbtsock,
+ struct nbt_name_packet *packet,
+ struct socket_address *src)
+{
+ struct nbt_name *name = &packet->questions[0].name;
+ struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private_data,
+ struct nbtd_interface);
+ struct wins_dns_proxy_state *s;
+ struct composite_context *creq;
+ struct resolve_context *resolve_ctx;
+
+ s = talloc(nbtsock, struct wins_dns_proxy_state);
+ if (!s) goto failed;
+ s->nbtsock = nbtsock;
+ s->packet = talloc_steal(s, packet);
+ s->src = src;
+ if (!talloc_reference(s, src)) {
+ goto failed;
+ }
+
+ resolve_ctx = resolve_context_init(s);
+ if (resolve_ctx == NULL) goto failed;
+ resolve_context_add_host_method(resolve_ctx);
+
+ creq = resolve_name_send(resolve_ctx, name, iface->nbtsrv->task->event_ctx);
+ if (!creq) goto failed;
+
+ creq->async.fn = nbtd_wins_dns_proxy_handler;
+ creq->async.private_data= s;
+ return;
+failed:
+ nbtd_negative_name_query_reply(nbtsock, packet, src);
+}
diff --git a/source4/nbt_server/wins/wins_hook.c b/source4/nbt_server/wins/wins_hook.c
new file mode 100644
index 0000000000..9753a86670
--- /dev/null
+++ b/source4/nbt_server/wins/wins_hook.c
@@ -0,0 +1,94 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ wins hook feature, we run a specified script
+ which can then do some custom actions
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "nbt_server/nbt_server.h"
+#include "nbt_server/wins/winsdb.h"
+#include "system/filesys.h"
+
+static const char *wins_hook_action_string(enum wins_hook_action action)
+{
+ switch (action) {
+ case WINS_HOOK_ADD: return "add";
+ case WINS_HOOK_MODIFY: return "refresh";
+ case WINS_HOOK_DELETE: return "delete";
+ }
+
+ return "unknown";
+}
+
+void wins_hook(struct winsdb_handle *h, const struct winsdb_record *rec,
+ enum wins_hook_action action, const char *wins_hook_script)
+{
+ uint32_t i, length;
+ int child;
+ char *cmd = NULL;
+ TALLOC_CTX *tmp_mem = NULL;
+
+ if (!wins_hook_script || !wins_hook_script[0]) return;
+
+ tmp_mem = talloc_new(h);
+ if (!tmp_mem) goto failed;
+
+ length = winsdb_addr_list_length(rec->addresses);
+
+ if (action == WINS_HOOK_MODIFY && length < 1) {
+ action = WINS_HOOK_DELETE;
+ }
+
+ cmd = talloc_asprintf(tmp_mem,
+ "%s %s %s %02x %ld",
+ wins_hook_script,
+ wins_hook_action_string(action),
+ rec->name->name,
+ rec->name->type,
+ rec->expire_time);
+ if (!cmd) goto failed;
+
+ for (i=0; rec->addresses[i]; i++) {
+ cmd = talloc_asprintf_append_buffer(cmd, " %s", rec->addresses[i]->address);
+ if (!cmd) goto failed;
+ }
+
+ DEBUG(10,("call wins hook '%s'\n", cmd));
+
+ /* signal handling in posix really sucks - doing this in a library
+ affects the whole app, but what else to do?? */
+ signal(SIGCHLD, SIG_IGN);
+
+ child = fork();
+ if (child == (pid_t)-1) {
+ goto failed;
+ }
+
+ if (child == 0) {
+/* TODO: close file handles */
+ execl("/bin/sh", "sh", "-c", cmd, NULL);
+ _exit(0);
+ }
+
+ talloc_free(tmp_mem);
+ return;
+failed:
+ talloc_free(tmp_mem);
+ DEBUG(0,("FAILED: calling wins hook '%s'\n", wins_hook_script));
+}
diff --git a/source4/nbt_server/wins/wins_ldb.c b/source4/nbt_server/wins/wins_ldb.c
new file mode 100644
index 0000000000..93382d7ddc
--- /dev/null
+++ b/source4/nbt_server/wins/wins_ldb.c
@@ -0,0 +1,121 @@
+/*
+ ldb database module
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb winsdb module
+ *
+ * Description: verify winsdb records before they're written to disk
+ *
+ * Author: Stefan Metzmacher
+ */
+
+#include "includes.h"
+#include "lib/events/events.h"
+#include "nbt_server/nbt_server.h"
+#include "nbt_server/wins/winsdb.h"
+#include "lib/ldb/include/ldb_module.h"
+#include "system/network.h"
+#include "lib/socket/netif.h"
+#include "param/param.h"
+
+static int wins_ldb_verify(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb = ldb_module_get_ctx(module);
+ struct winsdb_handle *h = talloc_get_type(ldb_get_opaque(ldb, "winsdb_handle"),
+ struct winsdb_handle);
+ const struct ldb_message *msg;
+
+ switch (req->operation) {
+ case LDB_ADD:
+ msg = req->op.add.message;
+ break;
+
+ case LDB_MODIFY:
+ msg = req->op.mod.message;
+ break;
+
+ default:
+ return ldb_next_request(module, req);
+ }
+
+ /* do not manipulate our control entries */
+ if (ldb_dn_is_special(msg->dn)) {
+ return ldb_next_request(module, req);
+ }
+
+ if (!h) {
+ ldb_debug_set(ldb, LDB_DEBUG_FATAL, "%s", "WINS_LDB: INTERNAL ERROR: no winsdb_handle present!");
+ return LDB_ERR_OTHER;
+ }
+
+ switch (h->caller) {
+ case WINSDB_HANDLE_CALLER_NBTD:
+ case WINSDB_HANDLE_CALLER_WREPL:
+ /* we trust our nbt and wrepl code ... */
+ return ldb_next_request(module, req);
+
+ case WINSDB_HANDLE_CALLER_ADMIN:
+ ldb_debug(ldb, LDB_DEBUG_WARNING, "%s\n", "WINS_LDB: TODO verify add/modify for WINSDB_HANDLE_CALLER_ADMIN");
+ return ldb_next_request(module, req);
+ }
+
+ return LDB_ERR_OTHER;
+}
+
+static int wins_ldb_init(struct ldb_module *module)
+{
+ struct ldb_context *ldb = ldb_module_get_ctx(module);
+ struct winsdb_handle *h;
+ const char *owner;
+ struct loadparm_context *lp_ctx = ldb_get_opaque(ldb, "loadparm");
+
+ ldb_module_set_private(module, NULL);
+
+ owner = lp_parm_string(lp_ctx, NULL, "winsdb", "local_owner");
+ if (!owner) {
+ struct interface *ifaces;
+ load_interfaces(module, lp_interfaces(lp_ctx), &ifaces);
+ owner = iface_n_ip(ifaces, 0);
+ if (!owner) {
+ owner = "0.0.0.0";
+ }
+ }
+
+ h = talloc_zero(module, struct winsdb_handle);
+ if (!h) goto failed;
+ h->ldb = ldb;
+ h->caller = WINSDB_HANDLE_CALLER_ADMIN;
+ h->local_owner = talloc_strdup(h, owner);
+ if (!h->local_owner) goto failed;
+
+ return ldb_set_opaque(ldb, "winsdb_handle", h);
+
+failed:
+ talloc_free(h);
+ return LDB_ERR_OTHER;
+}
+
+_PUBLIC_ const struct ldb_module_ops ldb_wins_ldb_module_ops = {
+ .name = "wins_ldb",
+ .add = wins_ldb_verify,
+ .modify = wins_ldb_verify,
+ .init_context = wins_ldb_init
+};
diff --git a/source4/nbt_server/wins/winsclient.c b/source4/nbt_server/wins/winsclient.c
new file mode 100644
index 0000000000..2546a26279
--- /dev/null
+++ b/source4/nbt_server/wins/winsclient.c
@@ -0,0 +1,259 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ wins client name registration and refresh
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "nbt_server/nbt_server.h"
+#include "nbt_server/wins/winsserver.h"
+#include "libcli/composite/composite.h"
+#include "lib/events/events.h"
+#include "librpc/gen_ndr/ndr_nbt.h"
+#include "smbd/service_task.h"
+#include "param/param.h"
+
+static void nbtd_wins_refresh_handler(struct composite_context *c);
+
+/* we send WINS client requests using our primary network interface
+*/
+static struct nbt_name_socket *wins_socket(struct nbtd_interface *iface)
+{
+ struct nbtd_server *nbtsrv = iface->nbtsrv;
+ return nbtsrv->interfaces->nbtsock;
+}
+
+
+static void nbtd_wins_refresh(struct tevent_context *ev, struct tevent_timer *te,
+ struct timeval t, void *private_data);
+
+/*
+ retry a WINS name registration
+*/
+static void nbtd_wins_register_retry(struct tevent_context *ev, struct tevent_timer *te,
+ struct timeval t, void *private_data)
+{
+ struct nbtd_iface_name *iname = talloc_get_type(private_data, struct nbtd_iface_name);
+ nbtd_winsclient_register(iname);
+}
+
+/*
+ start a timer to refresh this name
+*/
+static void nbtd_wins_start_refresh_timer(struct nbtd_iface_name *iname)
+{
+ uint32_t refresh_time;
+ uint32_t max_refresh_time = lp_parm_int(iname->iface->nbtsrv->task->lp_ctx, NULL, "nbtd", "max_refresh_time", 7200);
+
+ refresh_time = MIN(max_refresh_time, iname->ttl/2);
+
+ event_add_timed(iname->iface->nbtsrv->task->event_ctx,
+ iname,
+ timeval_add(&iname->registration_time, refresh_time, 0),
+ nbtd_wins_refresh, iname);
+}
+
+/*
+ called when a wins name refresh has completed
+*/
+static void nbtd_wins_refresh_handler(struct composite_context *c)
+{
+ NTSTATUS status;
+ struct nbt_name_refresh_wins io;
+ struct nbtd_iface_name *iname = talloc_get_type(c->async.private_data,
+ struct nbtd_iface_name);
+ TALLOC_CTX *tmp_ctx = talloc_new(iname);
+
+ status = nbt_name_refresh_wins_recv(c, tmp_ctx, &io);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
+ /* our WINS server is dead - start registration over
+ from scratch */
+ DEBUG(2,("Failed to refresh %s with WINS server %s\n",
+ nbt_name_string(tmp_ctx, &iname->name), iname->wins_server));
+ talloc_free(tmp_ctx);
+ nbtd_winsclient_register(iname);
+ return;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1,("Name refresh failure with WINS for %s - %s\n",
+ nbt_name_string(tmp_ctx, &iname->name), nt_errstr(status)));
+ talloc_free(tmp_ctx);
+ return;
+ }
+
+ if (io.out.rcode != 0) {
+ DEBUG(1,("WINS server %s rejected name refresh of %s - %s\n",
+ io.out.wins_server, nbt_name_string(tmp_ctx, &iname->name),
+ nt_errstr(nbt_rcode_to_ntstatus(io.out.rcode))));
+ iname->nb_flags |= NBT_NM_CONFLICT;
+ talloc_free(tmp_ctx);
+ return;
+ }
+
+ DEBUG(4,("Refreshed name %s with WINS server %s\n",
+ nbt_name_string(tmp_ctx, &iname->name), iname->wins_server));
+ /* success - start a periodic name refresh */
+ iname->nb_flags |= NBT_NM_ACTIVE;
+ if (iname->wins_server) {
+ /*
+ * talloc_free() would generate a warning,
+ * so steal it into the tmp context
+ */
+ talloc_steal(tmp_ctx, iname->wins_server);
+ }
+ iname->wins_server = talloc_steal(iname, io.out.wins_server);
+
+ iname->registration_time = timeval_current();
+ nbtd_wins_start_refresh_timer(iname);
+
+ talloc_free(tmp_ctx);
+}
+
+
+/*
+ refresh a WINS name registration
+*/
+static void nbtd_wins_refresh(struct tevent_context *ev, struct tevent_timer *te,
+ struct timeval t, void *private_data)
+{
+ struct nbtd_iface_name *iname = talloc_get_type(private_data, struct nbtd_iface_name);
+ struct nbtd_interface *iface = iname->iface;
+ struct nbt_name_refresh_wins io;
+ struct composite_context *c;
+ TALLOC_CTX *tmp_ctx = talloc_new(iname);
+
+ /* setup a wins name refresh request */
+ io.in.name = iname->name;
+ io.in.wins_servers = (const char **)str_list_make(tmp_ctx, iname->wins_server, NULL);
+ io.in.wins_port = lp_nbt_port(iface->nbtsrv->task->lp_ctx);
+ io.in.addresses = nbtd_address_list(iface, tmp_ctx);
+ io.in.nb_flags = iname->nb_flags;
+ io.in.ttl = iname->ttl;
+
+ if (!io.in.addresses) {
+ talloc_free(tmp_ctx);
+ return;
+ }
+
+ c = nbt_name_refresh_wins_send(wins_socket(iface), &io);
+ if (c == NULL) {
+ talloc_free(tmp_ctx);
+ return;
+ }
+ talloc_steal(c, io.in.addresses);
+
+ c->async.fn = nbtd_wins_refresh_handler;
+ c->async.private_data = iname;
+
+ talloc_free(tmp_ctx);
+}
+
+
+/*
+ called when a wins name register has completed
+*/
+static void nbtd_wins_register_handler(struct composite_context *c)
+{
+ NTSTATUS status;
+ struct nbt_name_register_wins io;
+ struct nbtd_iface_name *iname = talloc_get_type(c->async.private_data,
+ struct nbtd_iface_name);
+ TALLOC_CTX *tmp_ctx = talloc_new(iname);
+
+ status = nbt_name_register_wins_recv(c, tmp_ctx, &io);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
+ /* none of the WINS servers responded - try again
+ periodically */
+ int wins_retry_time = lp_parm_int(iname->iface->nbtsrv->task->lp_ctx, NULL, "nbtd", "wins_retry", 300);
+ event_add_timed(iname->iface->nbtsrv->task->event_ctx,
+ iname,
+ timeval_current_ofs(wins_retry_time, 0),
+ nbtd_wins_register_retry,
+ iname);
+ talloc_free(tmp_ctx);
+ return;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1,("Name register failure with WINS for %s - %s\n",
+ nbt_name_string(tmp_ctx, &iname->name), nt_errstr(status)));
+ talloc_free(tmp_ctx);
+ return;
+ }
+
+ if (io.out.rcode != 0) {
+ DEBUG(1,("WINS server %s rejected name register of %s - %s\n",
+ io.out.wins_server, nbt_name_string(tmp_ctx, &iname->name),
+ nt_errstr(nbt_rcode_to_ntstatus(io.out.rcode))));
+ iname->nb_flags |= NBT_NM_CONFLICT;
+ talloc_free(tmp_ctx);
+ return;
+ }
+
+ /* success - start a periodic name refresh */
+ iname->nb_flags |= NBT_NM_ACTIVE;
+ if (iname->wins_server) {
+ /*
+ * talloc_free() would generate a warning,
+ * so steal it into the tmp context
+ */
+ talloc_steal(tmp_ctx, iname->wins_server);
+ }
+ iname->wins_server = talloc_steal(iname, io.out.wins_server);
+
+ iname->registration_time = timeval_current();
+ nbtd_wins_start_refresh_timer(iname);
+
+ DEBUG(3,("Registered %s with WINS server %s\n",
+ nbt_name_string(tmp_ctx, &iname->name), iname->wins_server));
+
+ talloc_free(tmp_ctx);
+}
+
+/*
+ register a name with our WINS servers
+*/
+void nbtd_winsclient_register(struct nbtd_iface_name *iname)
+{
+ struct nbtd_interface *iface = iname->iface;
+ struct nbt_name_register_wins io;
+ struct composite_context *c;
+
+ /* setup a wins name register request */
+ io.in.name = iname->name;
+ io.in.wins_port = lp_nbt_port(iname->iface->nbtsrv->task->lp_ctx);
+ io.in.wins_servers = lp_wins_server_list(iname->iface->nbtsrv->task->lp_ctx);
+ io.in.addresses = nbtd_address_list(iface, iname);
+ io.in.nb_flags = iname->nb_flags;
+ io.in.ttl = iname->ttl;
+
+ if (!io.in.addresses) {
+ return;
+ }
+
+ c = nbt_name_register_wins_send(wins_socket(iface), &io);
+ if (c == NULL) {
+ talloc_free(io.in.addresses);
+ return;
+ }
+ talloc_steal(c, io.in.addresses);
+
+ c->async.fn = nbtd_wins_register_handler;
+ c->async.private_data = iname;
+}
diff --git a/source4/nbt_server/wins/winsdb.c b/source4/nbt_server/wins/winsdb.c
new file mode 100644
index 0000000000..5c3efe2b83
--- /dev/null
+++ b/source4/nbt_server/wins/winsdb.c
@@ -0,0 +1,1039 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ WINS database routines
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "nbt_server/nbt_server.h"
+#include "nbt_server/wins/winsdb.h"
+#include "lib/ldb/include/ldb.h"
+#include "lib/ldb/include/ldb_errors.h"
+#include "librpc/gen_ndr/ndr_nbt.h"
+#include "system/time.h"
+#include "ldb_wrap.h"
+#include "system/network.h"
+#include "lib/socket/netif.h"
+#include "param/param.h"
+
+uint64_t winsdb_get_maxVersion(struct winsdb_handle *h)
+{
+ int ret;
+ struct ldb_context *ldb = h->ldb;
+ struct ldb_dn *dn;
+ struct ldb_result *res = NULL;
+ TALLOC_CTX *tmp_ctx = talloc_new(ldb);
+ uint64_t maxVersion = 0;
+
+ dn = ldb_dn_new(tmp_ctx, ldb, "CN=VERSION");
+ if (!dn) goto failed;
+
+ /* find the record in the WINS database */
+ ret = ldb_search(ldb, tmp_ctx, &res, dn, LDB_SCOPE_BASE, NULL, NULL);
+ if (ret != LDB_SUCCESS) goto failed;
+ if (res->count > 1) goto failed;
+
+ if (res->count == 1) {
+ maxVersion = ldb_msg_find_attr_as_uint64(res->msgs[0], "maxVersion", 0);
+ }
+
+failed:
+ talloc_free(tmp_ctx);
+ return maxVersion;
+}
+
+/*
+ if newVersion == 0 return the old maxVersion + 1 and save it
+ if newVersion > 0 return MAX(oldMaxVersion, newMaxVersion) and save it
+*/
+uint64_t winsdb_set_maxVersion(struct winsdb_handle *h, uint64_t newMaxVersion)
+{
+ int trans;
+ int ret;
+ struct ldb_dn *dn;
+ struct ldb_result *res = NULL;
+ struct ldb_message *msg = NULL;
+ struct ldb_context *wins_db = h->ldb;
+ TALLOC_CTX *tmp_ctx = talloc_new(wins_db);
+ uint64_t oldMaxVersion = 0;
+
+ trans = ldb_transaction_start(wins_db);
+ if (trans != LDB_SUCCESS) goto failed;
+
+ dn = ldb_dn_new(tmp_ctx, wins_db, "CN=VERSION");
+ if (!dn) goto failed;
+
+ /* find the record in the WINS database */
+ ret = ldb_search(wins_db, tmp_ctx, &res, dn, LDB_SCOPE_BASE, NULL, NULL);
+ if (ret != LDB_SUCCESS) goto failed;
+ if (res->count > 1) goto failed;
+
+ if (res->count == 1) {
+ oldMaxVersion = ldb_msg_find_attr_as_uint64(res->msgs[0], "maxVersion", 0);
+ }
+
+ if (newMaxVersion == 0) {
+ newMaxVersion = oldMaxVersion + 1;
+ } else {
+ newMaxVersion = MAX(oldMaxVersion, newMaxVersion);
+ }
+
+ msg = ldb_msg_new(tmp_ctx);
+ if (!msg) goto failed;
+ msg->dn = dn;
+
+
+ ret = ldb_msg_add_empty(msg, "objectClass", LDB_FLAG_MOD_REPLACE, NULL);
+ if (ret != 0) goto failed;
+ ret = ldb_msg_add_string(msg, "objectClass", "winsMaxVersion");
+ if (ret != 0) goto failed;
+ ret = ldb_msg_add_empty(msg, "maxVersion", LDB_FLAG_MOD_REPLACE, NULL);
+ if (ret != 0) goto failed;
+ ret = ldb_msg_add_fmt(msg, "maxVersion", "%llu", (long long)newMaxVersion);
+ if (ret != 0) goto failed;
+
+ ret = ldb_modify(wins_db, msg);
+ if (ret != 0) ret = ldb_add(wins_db, msg);
+ if (ret != 0) goto failed;
+
+ trans = ldb_transaction_commit(wins_db);
+ if (trans != LDB_SUCCESS) goto failed;
+
+ talloc_free(tmp_ctx);
+ return newMaxVersion;
+
+failed:
+ if (trans == LDB_SUCCESS) ldb_transaction_cancel(wins_db);
+ talloc_free(tmp_ctx);
+ return 0;
+}
+
+uint64_t winsdb_get_seqnumber(struct winsdb_handle *h)
+{
+ int ret;
+ struct ldb_context *ldb = h->ldb;
+ struct ldb_dn *dn;
+ struct ldb_result *res = NULL;
+ TALLOC_CTX *tmp_ctx = talloc_new(ldb);
+ uint64_t seqnumber = 0;
+
+ dn = ldb_dn_new(tmp_ctx, ldb, "@BASEINFO");
+ if (!dn) goto failed;
+
+ /* find the record in the WINS database */
+ ret = ldb_search(ldb, tmp_ctx, &res, dn, LDB_SCOPE_BASE, NULL, NULL);
+ if (ret != LDB_SUCCESS) goto failed;
+ if (res->count > 1) goto failed;
+
+ if (res->count == 1) {
+ seqnumber = ldb_msg_find_attr_as_uint64(res->msgs[0], "sequenceNumber", 0);
+ }
+
+failed:
+ talloc_free(tmp_ctx);
+ return seqnumber;
+}
+
+/*
+ return a DN for a nbt_name
+*/
+static struct ldb_dn *winsdb_dn(TALLOC_CTX *mem_ctx, struct ldb_context *ldb,
+ const struct nbt_name *name)
+{
+ struct ldb_dn *dn;
+
+ dn = ldb_dn_new_fmt(mem_ctx, ldb, "type=0x%02X", name->type);
+ if (ldb_dn_is_valid(dn) && name->name && *name->name) {
+ ldb_dn_add_child_fmt(dn, "name=%s", name->name);
+ }
+ if (ldb_dn_is_valid(dn) && name->scope && *name->scope) {
+ ldb_dn_add_child_fmt(dn, "scope=%s", name->scope);
+ }
+ return dn;
+}
+
+static NTSTATUS winsdb_nbt_name(TALLOC_CTX *mem_ctx, struct ldb_dn *dn, struct nbt_name **_name)
+{
+ NTSTATUS status;
+ struct nbt_name *name;
+ unsigned int comp_num;
+ uint32_t cur = 0;
+
+ name = talloc(mem_ctx, struct nbt_name);
+ if (!name) {
+ status = NT_STATUS_NO_MEMORY;
+ goto failed;
+ }
+
+ comp_num = ldb_dn_get_comp_num(dn);
+
+ if (comp_num > 3) {
+ status = NT_STATUS_INTERNAL_DB_CORRUPTION;
+ goto failed;
+ }
+
+ if (comp_num > cur && strcasecmp("scope", ldb_dn_get_component_name(dn, cur)) == 0) {
+ name->scope = (const char *)talloc_strdup(name, (char *)ldb_dn_get_component_val(dn, cur)->data);
+ cur++;
+ } else {
+ name->scope = NULL;
+ }
+
+ if (comp_num > cur && strcasecmp("name", ldb_dn_get_component_name(dn, cur)) == 0) {
+ name->name = (const char *)talloc_strdup(name, (char *)ldb_dn_get_component_val(dn, cur)->data);
+ cur++;
+ } else {
+ name->name = talloc_strdup(name, "");
+ if (!name->name) {
+ status = NT_STATUS_NO_MEMORY;
+ goto failed;
+ }
+ }
+
+ if (comp_num > cur && strcasecmp("type", ldb_dn_get_component_name(dn, cur)) == 0) {
+ name->type = strtoul((char *)ldb_dn_get_component_val(dn, cur)->data, NULL, 0);
+ cur++;
+ } else {
+ status = NT_STATUS_INTERNAL_DB_CORRUPTION;
+ goto failed;
+ }
+
+ *_name = name;
+ return NT_STATUS_OK;
+failed:
+ talloc_free(name);
+ return status;
+}
+
+/*
+ decode the winsdb_addr("address") attribute:
+ "172.31.1.1" or
+ "172.31.1.1;winsOwner:172.31.9.202;expireTime:20050923032330.0Z;"
+ are valid records
+*/
+static NTSTATUS winsdb_addr_decode(struct winsdb_handle *h, struct winsdb_record *rec, struct ldb_val *val,
+ TALLOC_CTX *mem_ctx, struct winsdb_addr **_addr)
+{
+ NTSTATUS status;
+ struct winsdb_addr *addr;
+ const char *address;
+ const char *wins_owner;
+ const char *expire_time;
+ char *p;
+
+ addr = talloc(mem_ctx, struct winsdb_addr);
+ if (!addr) {
+ status = NT_STATUS_NO_MEMORY;
+ goto failed;
+ }
+
+ address = (char *)val->data;
+
+ p = strchr(address, ';');
+ if (!p) {
+ /* support old entries, with only the address */
+ addr->address = (const char *)talloc_steal(addr, val->data);
+ addr->wins_owner = talloc_reference(addr, rec->wins_owner);
+ if (!addr->wins_owner) {
+ status = NT_STATUS_NO_MEMORY;
+ goto failed;
+ }
+ addr->expire_time = rec->expire_time;
+ *_addr = addr;
+ return NT_STATUS_OK;
+ }
+
+ *p = '\0';p++;
+ addr->address = talloc_strdup(addr, address);
+ if (!addr->address) {
+ status = NT_STATUS_NO_MEMORY;
+ goto failed;
+ }
+
+ if (strncmp("winsOwner:", p, 10) != 0) {
+ status = NT_STATUS_INTERNAL_DB_CORRUPTION;
+ goto failed;
+ }
+ wins_owner = p + 10;
+ p = strchr(wins_owner, ';');
+ if (!p) {
+ status = NT_STATUS_INTERNAL_DB_CORRUPTION;
+ goto failed;
+ }
+
+ *p = '\0';p++;
+ if (strcmp(wins_owner, "0.0.0.0") == 0) {
+ wins_owner = h->local_owner;
+ }
+ addr->wins_owner = talloc_strdup(addr, wins_owner);
+ if (!addr->wins_owner) {
+ status = NT_STATUS_NO_MEMORY;
+ goto failed;
+ }
+
+ if (strncmp("expireTime:", p, 11) != 0) {
+ status = NT_STATUS_INTERNAL_DB_CORRUPTION;
+ goto failed;
+ }
+
+ expire_time = p + 11;
+ p = strchr(expire_time, ';');
+ if (!p) {
+ status = NT_STATUS_INTERNAL_DB_CORRUPTION;
+ goto failed;
+ }
+
+ *p = '\0';p++;
+ addr->expire_time = ldb_string_to_time(expire_time);
+
+ *_addr = addr;
+ return NT_STATUS_OK;
+failed:
+ talloc_free(addr);
+ return status;
+}
+
+/*
+ encode the winsdb_addr("address") attribute like this:
+ non-static record:
+ "172.31.1.1;winsOwner:172.31.9.202;expireTime:20050923032330.0Z;"
+ static record:
+ "172.31.1.1"
+*/
+static int ldb_msg_add_winsdb_addr(struct ldb_message *msg, struct winsdb_record *rec,
+ const char *attr_name, struct winsdb_addr *addr)
+{
+ struct ldb_val val;
+ const char *str;
+
+ if (rec->is_static) {
+ str = talloc_strdup(msg, addr->address);
+ if (!str) return -1;
+ } else {
+ char *expire_time;
+ expire_time = ldb_timestring(msg, addr->expire_time);
+ if (!expire_time) return -1;
+ str = talloc_asprintf(msg, "%s;winsOwner:%s;expireTime:%s;",
+ addr->address, addr->wins_owner,
+ expire_time);
+ talloc_free(expire_time);
+ if (!str) return -1;
+ }
+
+ val.data = discard_const_p(uint8_t, str);
+ val.length = strlen(str);
+
+ return ldb_msg_add_value(msg, attr_name, &val, NULL);
+}
+
+struct winsdb_addr **winsdb_addr_list_make(TALLOC_CTX *mem_ctx)
+{
+ struct winsdb_addr **addresses;
+
+ addresses = talloc_array(mem_ctx, struct winsdb_addr *, 1);
+ if (!addresses) return NULL;
+
+ addresses[0] = NULL;
+
+ return addresses;
+}
+
+static int winsdb_addr_sort_list (struct winsdb_addr **p1, struct winsdb_addr **p2, void *opaque)
+{
+ struct winsdb_addr *a1 = talloc_get_type(*p1, struct winsdb_addr);
+ struct winsdb_addr *a2 = talloc_get_type(*p2, struct winsdb_addr);
+ struct winsdb_handle *h= talloc_get_type(opaque, struct winsdb_handle);
+ bool a1_owned = false;
+ bool a2_owned = false;
+
+ /*
+ * first the owned addresses with the newest to the oldest address
+ * then the replica addresses with the newest to the oldest address
+ */
+ if (a2->expire_time != a1->expire_time) {
+ return a2->expire_time - a1->expire_time;
+ }
+
+ if (strcmp(a2->wins_owner, h->local_owner) == 0) {
+ a2_owned = true;
+ }
+
+ if (strcmp(a1->wins_owner, h->local_owner) == 0) {
+ a1_owned = true;
+ }
+
+ return a2_owned - a1_owned;
+}
+
+struct winsdb_addr **winsdb_addr_list_add(struct winsdb_handle *h, const struct winsdb_record *rec,
+ struct winsdb_addr **addresses, const char *address,
+ const char *wins_owner, time_t expire_time,
+ bool is_name_registration)
+{
+ struct winsdb_addr *old_addr = NULL;
+ size_t len = 0;
+ size_t i;
+ bool found_old_replica = false;
+
+ /*
+ * count the addresses and maybe
+ * find an old entry for the new address
+ */
+ for (i=0; addresses[i]; i++) {
+ if (old_addr) continue;
+ if (strcmp(addresses[i]->address, address) == 0) {
+ old_addr = addresses[i];
+ }
+ }
+ len = i;
+
+ /*
+ * the address is already there
+ * and we can replace it
+ */
+ if (old_addr) {
+ goto remove_old_addr;
+ }
+
+ /*
+ * if we don't have 25 addresses already,
+ * we can just add the new address
+ */
+ if (len < 25) {
+ goto add_new_addr;
+ }
+
+ /*
+ * if we haven't found the address,
+ * and we have already have 25 addresses
+ * if so then we need to do the following:
+ * - if it isn't a name registration, then just ignore the new address
+ * - if it is a name registration, then first search for
+ * the oldest replica and if there's no replica address
+ * search the oldest owned address
+ */
+ if (!is_name_registration) {
+ return addresses;
+ }
+
+ /*
+ * find the oldest replica address, if there's no replica
+ * record at all, find the oldest owned address
+ */
+ for (i=0; addresses[i]; i++) {
+ bool cur_is_replica = false;
+ /* find out if the current address is a replica */
+ if (strcmp(addresses[i]->wins_owner, h->local_owner) != 0) {
+ cur_is_replica = true;
+ }
+
+ /*
+ * if we already found a replica address and the current address
+ * is not a replica, then skip it
+ */
+ if (found_old_replica && !cur_is_replica) continue;
+
+ /*
+ * if we found the first replica address, reset the address
+ * that would be replaced
+ */
+ if (!found_old_replica && cur_is_replica) {
+ found_old_replica = true;
+ old_addr = addresses[i];
+ continue;
+ }
+
+ /*
+ * if the first address isn't a replica, just start with
+ * the first one
+ */
+ if (!old_addr) {
+ old_addr = addresses[i];
+ continue;
+ }
+
+ /*
+ * see if we find an older address
+ */
+ if (addresses[i]->expire_time < old_addr->expire_time) {
+ old_addr = addresses[i];
+ continue;
+ }
+ }
+
+remove_old_addr:
+ winsdb_addr_list_remove(addresses, old_addr->address);
+ len --;
+
+add_new_addr:
+ addresses = talloc_realloc(addresses, addresses, struct winsdb_addr *, len + 2);
+ if (!addresses) return NULL;
+
+ addresses[len] = talloc(addresses, struct winsdb_addr);
+ if (!addresses[len]) {
+ talloc_free(addresses);
+ return NULL;
+ }
+
+ addresses[len]->address = talloc_strdup(addresses[len], address);
+ if (!addresses[len]->address) {
+ talloc_free(addresses);
+ return NULL;
+ }
+
+ addresses[len]->wins_owner = talloc_strdup(addresses[len], wins_owner);
+ if (!addresses[len]->wins_owner) {
+ talloc_free(addresses);
+ return NULL;
+ }
+
+ addresses[len]->expire_time = expire_time;
+
+ addresses[len+1] = NULL;
+
+ ldb_qsort(addresses, len+1 , sizeof(addresses[0]), h, (ldb_qsort_cmp_fn_t)winsdb_addr_sort_list);
+
+ return addresses;
+}
+
+void winsdb_addr_list_remove(struct winsdb_addr **addresses, const char *address)
+{
+ size_t i;
+
+ for (i=0; addresses[i]; i++) {
+ if (strcmp(addresses[i]->address, address) == 0) {
+ break;
+ }
+ }
+
+ for (; addresses[i]; i++) {
+ addresses[i] = addresses[i+1];
+ }
+
+ return;
+}
+
+struct winsdb_addr *winsdb_addr_list_check(struct winsdb_addr **addresses, const char *address)
+{
+ size_t i;
+
+ for (i=0; addresses[i]; i++) {
+ if (strcmp(addresses[i]->address, address) == 0) {
+ return addresses[i];
+ }
+ }
+
+ return NULL;
+}
+
+size_t winsdb_addr_list_length(struct winsdb_addr **addresses)
+{
+ size_t i;
+ for (i=0; addresses[i]; i++);
+ return i;
+}
+
+const char **winsdb_addr_string_list(TALLOC_CTX *mem_ctx, struct winsdb_addr **addresses)
+{
+ size_t len = winsdb_addr_list_length(addresses);
+ const char **str_list=NULL;
+ size_t i;
+
+ for (i=0; i < len; i++) {
+ str_list = str_list_add(str_list, addresses[i]->address);
+ if (!str_list[i]) {
+ return NULL;
+ }
+ }
+ talloc_steal(mem_ctx, str_list);
+ return str_list;
+}
+
+/*
+ load a WINS entry from the database
+*/
+NTSTATUS winsdb_lookup(struct winsdb_handle *h,
+ const struct nbt_name *name,
+ TALLOC_CTX *mem_ctx,
+ struct winsdb_record **_rec)
+{
+ NTSTATUS status;
+ struct ldb_result *res = NULL;
+ int ret;
+ struct winsdb_record *rec;
+ struct ldb_context *wins_db = h->ldb;
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+ time_t now = time(NULL);
+
+ /* find the record in the WINS database */
+ ret = ldb_search(wins_db, tmp_ctx, &res,
+ winsdb_dn(tmp_ctx, wins_db, name),
+ LDB_SCOPE_BASE, NULL, NULL);
+
+ if (ret != LDB_SUCCESS || res->count > 1) {
+ status = NT_STATUS_INTERNAL_DB_CORRUPTION;
+ goto failed;
+ } else if (res->count== 0) {
+ status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ goto failed;
+ }
+
+ status = winsdb_record(h, res->msgs[0], tmp_ctx, now, &rec);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+
+ talloc_steal(mem_ctx, rec);
+ talloc_free(tmp_ctx);
+ *_rec = rec;
+ return NT_STATUS_OK;
+
+failed:
+ talloc_free(tmp_ctx);
+ return status;
+}
+
+NTSTATUS winsdb_record(struct winsdb_handle *h, struct ldb_message *msg, TALLOC_CTX *mem_ctx, time_t now, struct winsdb_record **_rec)
+{
+ NTSTATUS status;
+ struct winsdb_record *rec;
+ struct ldb_message_element *el;
+ struct nbt_name *name;
+ uint32_t i, j, num_values;
+
+ rec = talloc(mem_ctx, struct winsdb_record);
+ if (rec == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto failed;
+ }
+
+ status = winsdb_nbt_name(rec, msg->dn, &name);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+
+ if (strlen(name->name) > 15) {
+ status = NT_STATUS_INTERNAL_DB_CORRUPTION;
+ goto failed;
+ }
+ if (name->scope && strlen(name->scope) > 238) {
+ status = NT_STATUS_INTERNAL_DB_CORRUPTION;
+ goto failed;
+ }
+
+ /* parse it into a more convenient winsdb_record structure */
+ rec->name = name;
+ rec->type = ldb_msg_find_attr_as_int(msg, "recordType", WREPL_TYPE_UNIQUE);
+ rec->state = ldb_msg_find_attr_as_int(msg, "recordState", WREPL_STATE_RELEASED);
+ rec->node = ldb_msg_find_attr_as_int(msg, "nodeType", WREPL_NODE_B);
+ rec->is_static = ldb_msg_find_attr_as_int(msg, "isStatic", 0);
+ rec->expire_time = ldb_string_to_time(ldb_msg_find_attr_as_string(msg, "expireTime", NULL));
+ rec->version = ldb_msg_find_attr_as_uint64(msg, "versionID", 0);
+ rec->wins_owner = ldb_msg_find_attr_as_string(msg, "winsOwner", NULL);
+ rec->registered_by = ldb_msg_find_attr_as_string(msg, "registeredBy", NULL);
+ talloc_steal(rec, rec->wins_owner);
+ talloc_steal(rec, rec->registered_by);
+
+ if (!rec->wins_owner || strcmp(rec->wins_owner, "0.0.0.0") == 0) {
+ rec->wins_owner = h->local_owner;
+ }
+
+ el = ldb_msg_find_element(msg, "address");
+ if (el) {
+ num_values = el->num_values;
+ } else {
+ num_values = 0;
+ }
+
+ if (rec->type == WREPL_TYPE_UNIQUE || rec->type == WREPL_TYPE_GROUP) {
+ if (num_values != 1) {
+ status = NT_STATUS_INTERNAL_DB_CORRUPTION;
+ goto failed;
+ }
+ }
+ if (rec->state == WREPL_STATE_ACTIVE) {
+ if (num_values < 1) {
+ status = NT_STATUS_INTERNAL_DB_CORRUPTION;
+ goto failed;
+ }
+ }
+ if (num_values > 25) {
+ status = NT_STATUS_INTERNAL_DB_CORRUPTION;
+ goto failed;
+ }
+
+ rec->addresses = talloc_array(rec, struct winsdb_addr *, num_values+1);
+ if (rec->addresses == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto failed;
+ }
+
+ for (i=0,j=0;i<num_values;i++) {
+ bool we_are_owner = false;
+
+ status = winsdb_addr_decode(h, rec, &el->values[i], rec->addresses, &rec->addresses[j]);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+
+ if (strcmp(rec->addresses[j]->wins_owner, h->local_owner) == 0) {
+ we_are_owner = true;
+ }
+
+ /*
+ * the record isn't static and is active
+ * then don't add the address if it's expired,
+ * but only if we're the owner of the address
+ *
+ * This is important for SGROUP records,
+ * because each server thinks he's the owner of the
+ * record and the record isn't replicated on a
+ * name_refresh. So addresses owned by another owner
+ * could expire, but we still need to return them
+ * (as windows does).
+ */
+ if (!rec->is_static &&
+ rec->addresses[j]->expire_time <= now &&
+ rec->state == WREPL_STATE_ACTIVE &&
+ we_are_owner) {
+ DEBUG(5,("WINS: expiring name addr %s of %s (expired at %s)\n",
+ rec->addresses[j]->address, nbt_name_string(rec->addresses[j], rec->name),
+ timestring(rec->addresses[j], rec->addresses[j]->expire_time)));
+ talloc_free(rec->addresses[j]);
+ rec->addresses[j] = NULL;
+ continue;
+ }
+ j++;
+ }
+ rec->addresses[j] = NULL;
+ num_values = j;
+
+ if (rec->is_static && rec->state == WREPL_STATE_ACTIVE) {
+ rec->expire_time = get_time_t_max();
+ for (i=0;rec->addresses[i];i++) {
+ rec->addresses[i]->expire_time = rec->expire_time;
+ }
+ }
+
+ if (rec->state == WREPL_STATE_ACTIVE) {
+ if (num_values < 1) {
+ DEBUG(5,("WINS: expiring name %s (because it has no active addresses)\n",
+ nbt_name_string(mem_ctx, rec->name)));
+ rec->state = WREPL_STATE_RELEASED;
+ }
+ }
+
+ *_rec = rec;
+ return NT_STATUS_OK;
+failed:
+ if (NT_STATUS_EQUAL(NT_STATUS_INTERNAL_DB_CORRUPTION, status)) {
+ DEBUG(1,("winsdb_record: corrupted record: %s\n", ldb_dn_get_linearized(msg->dn)));
+ }
+ talloc_free(rec);
+ return status;
+}
+
+/*
+ form a ldb_message from a winsdb_record
+*/
+static struct ldb_message *winsdb_message(struct ldb_context *ldb,
+ struct winsdb_record *rec,
+ TALLOC_CTX *mem_ctx)
+{
+ int i, ret=0;
+ size_t addr_count;
+ const char *expire_time;
+ struct ldb_message *msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) goto failed;
+
+ /* make sure we don't put in corrupted records */
+ addr_count = winsdb_addr_list_length(rec->addresses);
+ if (rec->state == WREPL_STATE_ACTIVE && addr_count == 0) {
+ rec->state = WREPL_STATE_RELEASED;
+ }
+ if (rec->type == WREPL_TYPE_UNIQUE && addr_count > 1) {
+ rec->type = WREPL_TYPE_MHOMED;
+ }
+
+ expire_time = ldb_timestring(msg, rec->expire_time);
+ if (!expire_time) {
+ goto failed;
+ }
+
+ msg->dn = winsdb_dn(msg, ldb, rec->name);
+ if (msg->dn == NULL) goto failed;
+ ret |= ldb_msg_add_fmt(msg, "type", "0x%02X", rec->name->type);
+ if (rec->name->name && *rec->name->name) {
+ ret |= ldb_msg_add_string(msg, "name", rec->name->name);
+ }
+ if (rec->name->scope && *rec->name->scope) {
+ ret |= ldb_msg_add_string(msg, "scope", rec->name->scope);
+ }
+ ret |= ldb_msg_add_fmt(msg, "objectClass", "winsRecord");
+ ret |= ldb_msg_add_fmt(msg, "recordType", "%u", rec->type);
+ ret |= ldb_msg_add_fmt(msg, "recordState", "%u", rec->state);
+ ret |= ldb_msg_add_fmt(msg, "nodeType", "%u", rec->node);
+ ret |= ldb_msg_add_fmt(msg, "isStatic", "%u", rec->is_static);
+ ret |= ldb_msg_add_empty(msg, "expireTime", 0, NULL);
+ if (!(rec->is_static && rec->state == WREPL_STATE_ACTIVE)) {
+ ret |= ldb_msg_add_string(msg, "expireTime", expire_time);
+ }
+ ret |= ldb_msg_add_fmt(msg, "versionID", "%llu", (long long)rec->version);
+ ret |= ldb_msg_add_string(msg, "winsOwner", rec->wins_owner);
+ ret |= ldb_msg_add_empty(msg, "address", 0, NULL);
+ for (i=0;rec->addresses[i];i++) {
+ ret |= ldb_msg_add_winsdb_addr(msg, rec, "address", rec->addresses[i]);
+ }
+ ret |= ldb_msg_add_empty(msg, "registeredBy", 0, NULL);
+ if (rec->registered_by) {
+ ret |= ldb_msg_add_string(msg, "registeredBy", rec->registered_by);
+ if (ret != 0) goto failed;
+ }
+ return msg;
+
+failed:
+ talloc_free(msg);
+ return NULL;
+}
+
+/*
+ save a WINS record into the database
+*/
+uint8_t winsdb_add(struct winsdb_handle *h, struct winsdb_record *rec, uint32_t flags)
+{
+ struct ldb_message *msg;
+ struct ldb_context *wins_db = h->ldb;
+ TALLOC_CTX *tmp_ctx = talloc_new(wins_db);
+ int trans = -1;
+ int ret = 0;
+
+ trans = ldb_transaction_start(wins_db);
+ if (trans != LDB_SUCCESS) goto failed;
+
+ if (flags & WINSDB_FLAG_ALLOC_VERSION) {
+ /* passing '0' means auto-allocate a new one */
+ rec->version = winsdb_set_maxVersion(h, 0);
+ if (rec->version == 0) goto failed;
+ }
+ if (flags & WINSDB_FLAG_TAKE_OWNERSHIP) {
+ rec->wins_owner = h->local_owner;
+ }
+
+ msg = winsdb_message(wins_db, rec, tmp_ctx);
+ if (msg == NULL) goto failed;
+ ret = ldb_add(wins_db, msg);
+ if (ret != 0) goto failed;
+
+ trans = ldb_transaction_commit(wins_db);
+ if (trans != LDB_SUCCESS) goto failed;
+
+ wins_hook(h, rec, WINS_HOOK_ADD, h->hook_script);
+
+ talloc_free(tmp_ctx);
+ return NBT_RCODE_OK;
+
+failed:
+ if (trans == LDB_SUCCESS) ldb_transaction_cancel(wins_db);
+ talloc_free(tmp_ctx);
+ return NBT_RCODE_SVR;
+}
+
+
+/*
+ modify a WINS record in the database
+*/
+uint8_t winsdb_modify(struct winsdb_handle *h, struct winsdb_record *rec, uint32_t flags)
+{
+ struct ldb_message *msg;
+ struct ldb_context *wins_db = h->ldb;
+ TALLOC_CTX *tmp_ctx = talloc_new(wins_db);
+ int trans;
+ int ret;
+ int i;
+
+ trans = ldb_transaction_start(wins_db);
+ if (trans != LDB_SUCCESS) goto failed;
+
+ if (flags & WINSDB_FLAG_ALLOC_VERSION) {
+ /* passing '0' means auto-allocate a new one */
+ rec->version = winsdb_set_maxVersion(h, 0);
+ if (rec->version == 0) goto failed;
+ }
+ if (flags & WINSDB_FLAG_TAKE_OWNERSHIP) {
+ rec->wins_owner = h->local_owner;
+ }
+
+ msg = winsdb_message(wins_db, rec, tmp_ctx);
+ if (msg == NULL) goto failed;
+
+ for (i=0;i<msg->num_elements;i++) {
+ msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
+ }
+
+ ret = ldb_modify(wins_db, msg);
+ if (ret != 0) goto failed;
+
+ trans = ldb_transaction_commit(wins_db);
+ if (trans != LDB_SUCCESS) goto failed;
+
+ wins_hook(h, rec, WINS_HOOK_MODIFY, h->hook_script);
+
+ talloc_free(tmp_ctx);
+ return NBT_RCODE_OK;
+
+failed:
+ if (trans == LDB_SUCCESS) ldb_transaction_cancel(wins_db);
+ talloc_free(tmp_ctx);
+ return NBT_RCODE_SVR;
+}
+
+
+/*
+ delete a WINS record from the database
+*/
+uint8_t winsdb_delete(struct winsdb_handle *h, struct winsdb_record *rec)
+{
+ struct ldb_context *wins_db = h->ldb;
+ TALLOC_CTX *tmp_ctx = talloc_new(wins_db);
+ struct ldb_dn *dn;
+ int trans;
+ int ret;
+
+ trans = ldb_transaction_start(wins_db);
+ if (trans != LDB_SUCCESS) goto failed;
+
+ dn = winsdb_dn(tmp_ctx, wins_db, rec->name);
+ if (dn == NULL) goto failed;
+
+ ret = ldb_delete(wins_db, dn);
+ if (ret != 0) goto failed;
+
+ trans = ldb_transaction_commit(wins_db);
+ if (trans != LDB_SUCCESS) goto failed;
+
+ wins_hook(h, rec, WINS_HOOK_DELETE, h->hook_script);
+
+ talloc_free(tmp_ctx);
+ return NBT_RCODE_OK;
+
+failed:
+ if (trans == LDB_SUCCESS) ldb_transaction_cancel(wins_db);
+ talloc_free(tmp_ctx);
+ return NBT_RCODE_SVR;
+}
+
+static bool winsdb_check_or_add_module_list(struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx, struct winsdb_handle *h)
+{
+ int trans;
+ int ret;
+ struct ldb_dn *dn;
+ struct ldb_result *res = NULL;
+ struct ldb_message *msg = NULL;
+ TALLOC_CTX *tmp_ctx = talloc_new(h);
+ unsigned int flags = 0;
+
+ trans = ldb_transaction_start(h->ldb);
+ if (trans != LDB_SUCCESS) goto failed;
+
+ /* check if we have a special @MODULES record already */
+ dn = ldb_dn_new(tmp_ctx, h->ldb, "@MODULES");
+ if (!dn) goto failed;
+
+ /* find the record in the WINS database */
+ ret = ldb_search(h->ldb, tmp_ctx, &res, dn, LDB_SCOPE_BASE, NULL, NULL);
+ if (ret != LDB_SUCCESS) goto failed;
+
+ if (res->count > 0) goto skip;
+
+ /* if there's no record, add one */
+ msg = ldb_msg_new(tmp_ctx);
+ if (!msg) goto failed;
+ msg->dn = dn;
+
+ ret = ldb_msg_add_string(msg, "@LIST", "wins_ldb");
+ if (ret != 0) goto failed;
+
+ ret = ldb_add(h->ldb, msg);
+ if (ret != 0) goto failed;
+
+ trans = ldb_transaction_commit(h->ldb);
+ if (trans != LDB_SUCCESS) goto failed;
+
+ /* close and reopen the database, with the modules */
+ trans = LDB_ERR_OTHER;
+ talloc_free(h->ldb);
+ h->ldb = NULL;
+
+ if (lp_parm_bool(lp_ctx, NULL,"winsdb", "nosync", false)) {
+ flags |= LDB_FLG_NOSYNC;
+ }
+
+ h->ldb = ldb_wrap_connect(h, ev_ctx, lp_ctx, lock_path(h, lp_ctx, lp_wins_url(lp_ctx)),
+ NULL, NULL, flags, NULL);
+ if (!h->ldb) goto failed;
+
+ talloc_free(tmp_ctx);
+ return true;
+
+skip:
+ if (trans == LDB_SUCCESS) ldb_transaction_cancel(h->ldb);
+ talloc_free(tmp_ctx);
+ return true;
+
+failed:
+ if (trans == LDB_SUCCESS) ldb_transaction_cancel(h->ldb);
+ talloc_free(tmp_ctx);
+ return false;
+}
+
+struct winsdb_handle *winsdb_connect(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx,
+ const char *owner,
+ enum winsdb_handle_caller caller)
+{
+ struct winsdb_handle *h = NULL;
+ unsigned int flags = 0;
+ bool ret;
+ int ldb_err;
+
+ h = talloc_zero(mem_ctx, struct winsdb_handle);
+ if (!h) return NULL;
+
+ if (lp_parm_bool(lp_ctx, NULL,"winsdb", "nosync", false)) {
+ flags |= LDB_FLG_NOSYNC;
+ }
+
+ h->ldb = ldb_wrap_connect(h, ev_ctx, lp_ctx, lock_path(h, lp_ctx, lp_wins_url(lp_ctx)),
+ NULL, NULL, flags, NULL);
+ if (!h->ldb) goto failed;
+
+ h->caller = caller;
+ h->hook_script = lp_wins_hook(lp_ctx);
+
+ h->local_owner = talloc_strdup(h, owner);
+ if (!h->local_owner) goto failed;
+
+ /* make sure the module list is available and used */
+ ret = winsdb_check_or_add_module_list(ev_ctx, lp_ctx, h);
+ if (!ret) goto failed;
+
+ ldb_err = ldb_set_opaque(h->ldb, "winsdb_handle", h);
+ if (ldb_err != LDB_SUCCESS) goto failed;
+
+ return h;
+failed:
+ talloc_free(h);
+ return NULL;
+}
diff --git a/source4/nbt_server/wins/winsdb.h b/source4/nbt_server/wins/winsdb.h
new file mode 100644
index 0000000000..194bcc027e
--- /dev/null
+++ b/source4/nbt_server/wins/winsdb.h
@@ -0,0 +1,81 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ WINS server structures
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#define WINSDB_FLAG_ALLOC_VERSION (1<<0)
+#define WINSDB_FLAG_TAKE_OWNERSHIP (1<<1)
+
+struct winsdb_addr {
+ const char *address;
+ const char *wins_owner;
+ time_t expire_time;
+};
+
+/*
+ each record in the database contains the following information
+*/
+struct winsdb_record {
+ struct nbt_name *name;
+ enum wrepl_name_type type;
+ enum wrepl_name_state state;
+ enum wrepl_name_node node;
+ bool is_static;
+ time_t expire_time;
+ uint64_t version;
+ const char *wins_owner;
+ struct winsdb_addr **addresses;
+
+ /* only needed for debugging problems */
+ const char *registered_by;
+};
+
+enum winsdb_handle_caller {
+ WINSDB_HANDLE_CALLER_ADMIN = 0,
+ WINSDB_HANDLE_CALLER_NBTD = 1,
+ WINSDB_HANDLE_CALLER_WREPL = 2
+};
+
+struct winsdb_handle {
+ /* wins server database handle */
+ struct ldb_context *ldb;
+
+ /*
+ * the type of the caller, as we pass this to the
+ * 'wins_ldb' ldb module can decide if it needs to verify the
+ * the records before they're written to disk
+ */
+ enum winsdb_handle_caller caller;
+
+ /* local owner address */
+ const char *local_owner;
+
+ /* wins hook script */
+ const char *hook_script;
+};
+
+enum wins_hook_action {
+ WINS_HOOK_ADD = 0,
+ WINS_HOOK_MODIFY = 1,
+ WINS_HOOK_DELETE = 2
+};
+
+struct ldb_message;
+struct tevent_context;
+#include "nbt_server/wins/winsdb_proto.h"
diff --git a/source4/nbt_server/wins/winsserver.c b/source4/nbt_server/wins/winsserver.c
new file mode 100644
index 0000000000..798e9c743a
--- /dev/null
+++ b/source4/nbt_server/wins/winsserver.c
@@ -0,0 +1,1065 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ core wins server handling
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "dlinklist.h"
+#include "nbt_server/nbt_server.h"
+#include "nbt_server/wins/winsdb.h"
+#include "nbt_server/wins/winsserver.h"
+#include "librpc/gen_ndr/ndr_nbt.h"
+#include "system/time.h"
+#include "libcli/composite/composite.h"
+#include "smbd/service_task.h"
+#include "system/network.h"
+#include "lib/socket/socket.h"
+#include "lib/socket/netif.h"
+#include "lib/ldb/include/ldb.h"
+#include "param/param.h"
+#include "libcli/resolve/resolve.h"
+
+/*
+ work out the ttl we will use given a client requested ttl
+*/
+uint32_t wins_server_ttl(struct wins_server *winssrv, uint32_t ttl)
+{
+ ttl = MIN(ttl, winssrv->config.max_renew_interval);
+ ttl = MAX(ttl, winssrv->config.min_renew_interval);
+ return ttl;
+}
+
+static enum wrepl_name_type wrepl_type(uint16_t nb_flags, struct nbt_name *name, bool mhomed)
+{
+ /* this copes with the nasty hack that is the type 0x1c name */
+ if (name->type == NBT_NAME_LOGON) {
+ return WREPL_TYPE_SGROUP;
+ }
+ if (nb_flags & NBT_NM_GROUP) {
+ return WREPL_TYPE_GROUP;
+ }
+ if (mhomed) {
+ return WREPL_TYPE_MHOMED;
+ }
+ return WREPL_TYPE_UNIQUE;
+}
+
+/*
+ register a new name with WINS
+*/
+static uint8_t wins_register_new(struct nbt_name_socket *nbtsock,
+ struct nbt_name_packet *packet,
+ const struct socket_address *src,
+ enum wrepl_name_type type)
+{
+ struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private_data,
+ struct nbtd_interface);
+ struct wins_server *winssrv = iface->nbtsrv->winssrv;
+ struct nbt_name *name = &packet->questions[0].name;
+ uint32_t ttl = wins_server_ttl(winssrv, packet->additional[0].ttl);
+ uint16_t nb_flags = packet->additional[0].rdata.netbios.addresses[0].nb_flags;
+ const char *address = packet->additional[0].rdata.netbios.addresses[0].ipaddr;
+ struct winsdb_record rec;
+ enum wrepl_name_node node;
+
+#define WREPL_NODE_NBT_FLAGS(nb_flags) \
+ ((nb_flags & NBT_NM_OWNER_TYPE)>>13)
+
+ node = WREPL_NODE_NBT_FLAGS(nb_flags);
+
+ rec.name = name;
+ rec.type = type;
+ rec.state = WREPL_STATE_ACTIVE;
+ rec.node = node;
+ rec.is_static = false;
+ rec.expire_time = time(NULL) + ttl;
+ rec.version = 0; /* will be allocated later */
+ rec.wins_owner = NULL; /* will be set later */
+ rec.registered_by = src->addr;
+ rec.addresses = winsdb_addr_list_make(packet);
+ if (rec.addresses == NULL) return NBT_RCODE_SVR;
+
+ rec.addresses = winsdb_addr_list_add(winssrv->wins_db,
+ &rec, rec.addresses,
+ address,
+ winssrv->wins_db->local_owner,
+ rec.expire_time,
+ true);
+ if (rec.addresses == NULL) return NBT_RCODE_SVR;
+
+ DEBUG(4,("WINS: accepted registration of %s with address %s\n",
+ nbt_name_string(packet, name), rec.addresses[0]->address));
+
+ return winsdb_add(winssrv->wins_db, &rec, WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP);
+}
+
+
+/*
+ update the ttl on an existing record
+*/
+static uint8_t wins_update_ttl(struct nbt_name_socket *nbtsock,
+ struct nbt_name_packet *packet,
+ struct winsdb_record *rec,
+ struct winsdb_addr *winsdb_addr,
+ const struct socket_address *src)
+{
+ struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private_data,
+ struct nbtd_interface);
+ struct wins_server *winssrv = iface->nbtsrv->winssrv;
+ uint32_t ttl = wins_server_ttl(winssrv, packet->additional[0].ttl);
+ const char *address = packet->additional[0].rdata.netbios.addresses[0].ipaddr;
+ uint32_t modify_flags = 0;
+
+ rec->expire_time = time(NULL) + ttl;
+ rec->registered_by = src->addr;
+
+ if (winsdb_addr) {
+ rec->addresses = winsdb_addr_list_add(winssrv->wins_db,
+ rec, rec->addresses,
+ winsdb_addr->address,
+ winssrv->wins_db->local_owner,
+ rec->expire_time,
+ true);
+ if (rec->addresses == NULL) return NBT_RCODE_SVR;
+ }
+
+ if (strcmp(winssrv->wins_db->local_owner, rec->wins_owner) != 0) {
+ modify_flags = WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP;
+ }
+
+ DEBUG(5,("WINS: refreshed registration of %s at %s\n",
+ nbt_name_string(packet, rec->name), address));
+
+ return winsdb_modify(winssrv->wins_db, rec, modify_flags);
+}
+
+/*
+ do a sgroup merge
+*/
+static uint8_t wins_sgroup_merge(struct nbt_name_socket *nbtsock,
+ struct nbt_name_packet *packet,
+ struct winsdb_record *rec,
+ const char *address,
+ const struct socket_address *src)
+{
+ struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private_data,
+ struct nbtd_interface);
+ struct wins_server *winssrv = iface->nbtsrv->winssrv;
+ uint32_t ttl = wins_server_ttl(winssrv, packet->additional[0].ttl);
+
+ rec->expire_time = time(NULL) + ttl;
+ rec->registered_by = src->addr;
+
+ rec->addresses = winsdb_addr_list_add(winssrv->wins_db,
+ rec, rec->addresses,
+ address,
+ winssrv->wins_db->local_owner,
+ rec->expire_time,
+ true);
+ if (rec->addresses == NULL) return NBT_RCODE_SVR;
+
+ DEBUG(5,("WINS: sgroup merge of %s at %s\n",
+ nbt_name_string(packet, rec->name), address));
+
+ return winsdb_modify(winssrv->wins_db, rec, WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP);
+}
+
+struct nbtd_wins_wack_state {
+ struct nbtd_wins_wack_state *prev, *next;
+ struct wins_server *winssrv;
+ struct nbt_name_socket *nbtsock;
+ struct nbtd_interface *iface;
+ struct nbt_name_packet *request_packet;
+ struct winsdb_record *rec;
+ struct socket_address *src;
+ const char *reg_address;
+ enum wrepl_name_type new_type;
+ struct wins_challenge_io io;
+ NTSTATUS status;
+};
+
+static int nbtd_wins_wack_state_destructor(struct nbtd_wins_wack_state *s)
+{
+ DLIST_REMOVE(s->iface->wack_queue, s);
+ return 0;
+}
+
+static bool wins_check_wack_queue(struct nbtd_interface *iface,
+ struct nbt_name_packet *packet,
+ struct socket_address *src)
+{
+ struct nbtd_wins_wack_state *s;
+
+ for (s= iface->wack_queue; s; s = s->next) {
+ if (packet->name_trn_id != s->request_packet->name_trn_id) {
+ continue;
+ }
+ if (packet->operation != s->request_packet->operation) {
+ continue;
+ }
+ if (src->port != s->src->port) {
+ continue;
+ }
+ if (strcmp(src->addr, s->src->addr) != 0) {
+ continue;
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ deny a registration request
+*/
+static void wins_wack_deny(struct nbtd_wins_wack_state *s)
+{
+ nbtd_name_registration_reply(s->nbtsock, s->request_packet,
+ s->src, NBT_RCODE_ACT);
+ DEBUG(4,("WINS: denied name registration request for %s from %s:%d\n",
+ nbt_name_string(s, s->rec->name), s->src->addr, s->src->port));
+ talloc_free(s);
+}
+
+/*
+ allow a registration request
+*/
+static void wins_wack_allow(struct nbtd_wins_wack_state *s)
+{
+ NTSTATUS status;
+ uint32_t ttl = wins_server_ttl(s->winssrv, s->request_packet->additional[0].ttl);
+ struct winsdb_record *rec = s->rec, *rec2;
+ uint32_t i,j;
+
+ status = winsdb_lookup(s->winssrv->wins_db, rec->name, s, &rec2);
+ if (!NT_STATUS_IS_OK(status) ||
+ rec2->version != rec->version ||
+ strcmp(rec2->wins_owner, rec->wins_owner) != 0) {
+ DEBUG(5,("WINS: record %s changed during WACK - failing registration\n",
+ nbt_name_string(s, rec->name)));
+ wins_wack_deny(s);
+ return;
+ }
+
+ /*
+ * if the old name owner doesn't hold the name anymore
+ * handle the request as new registration for the new name owner
+ */
+ if (!NT_STATUS_IS_OK(s->status)) {
+ uint8_t rcode;
+
+ winsdb_delete(s->winssrv->wins_db, rec);
+ rcode = wins_register_new(s->nbtsock, s->request_packet, s->src, s->new_type);
+ if (rcode != NBT_RCODE_OK) {
+ DEBUG(1,("WINS: record %s failed to register as new during WACK\n",
+ nbt_name_string(s, rec->name)));
+ wins_wack_deny(s);
+ return;
+ }
+ goto done;
+ }
+
+ rec->expire_time = time(NULL) + ttl;
+ rec->registered_by = s->src->addr;
+
+ /*
+ * now remove all addresses that're the client doesn't hold anymore
+ * and update the time stamp and owner for the ownes that are still there
+ */
+ for (i=0; rec->addresses[i]; i++) {
+ bool found = false;
+ for (j=0; j < s->io.out.num_addresses; j++) {
+ if (strcmp(rec->addresses[i]->address, s->io.out.addresses[j]) != 0) continue;
+
+ found = true;
+ break;
+ }
+ if (found) {
+ rec->addresses = winsdb_addr_list_add(s->winssrv->wins_db,
+ rec, rec->addresses,
+ s->reg_address,
+ s->winssrv->wins_db->local_owner,
+ rec->expire_time,
+ true);
+ if (rec->addresses == NULL) goto failed;
+ continue;
+ }
+
+ winsdb_addr_list_remove(rec->addresses, rec->addresses[i]->address);
+ }
+
+ rec->addresses = winsdb_addr_list_add(s->winssrv->wins_db,
+ rec, rec->addresses,
+ s->reg_address,
+ s->winssrv->wins_db->local_owner,
+ rec->expire_time,
+ true);
+ if (rec->addresses == NULL) goto failed;
+
+ /* if we have more than one address, this becomes implicit a MHOMED record */
+ if (winsdb_addr_list_length(rec->addresses) > 1) {
+ rec->type = WREPL_TYPE_MHOMED;
+ }
+
+ winsdb_modify(s->winssrv->wins_db, rec, WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP);
+
+ DEBUG(4,("WINS: accepted registration of %s with address %s\n",
+ nbt_name_string(s, rec->name), s->reg_address));
+
+done:
+ nbtd_name_registration_reply(s->nbtsock, s->request_packet,
+ s->src, NBT_RCODE_OK);
+failed:
+ talloc_free(s);
+}
+
+/*
+ called when a name query to a current owner completes
+*/
+static void wack_wins_challenge_handler(struct composite_context *c_req)
+{
+ struct nbtd_wins_wack_state *s = talloc_get_type(c_req->async.private_data,
+ struct nbtd_wins_wack_state);
+ bool found;
+ uint32_t i;
+
+ s->status = wins_challenge_recv(c_req, s, &s->io);
+
+ /*
+ * if the owner denies it holds the name, then allow
+ * the registration
+ */
+ if (!NT_STATUS_IS_OK(s->status)) {
+ wins_wack_allow(s);
+ return;
+ }
+
+ if (s->new_type == WREPL_TYPE_GROUP || s->new_type == WREPL_TYPE_SGROUP) {
+ DEBUG(1,("WINS: record %s failed to register as group type(%u) during WACK, it's still type(%u)\n",
+ nbt_name_string(s, s->rec->name), s->new_type, s->rec->type));
+ wins_wack_deny(s);
+ return;
+ }
+
+ /*
+ * if the owner still wants the name and doesn't reply
+ * with the address trying to be registered, then deny
+ * the registration
+ */
+ found = false;
+ for (i=0; i < s->io.out.num_addresses; i++) {
+ if (strcmp(s->reg_address, s->io.out.addresses[i]) != 0) continue;
+
+ found = true;
+ break;
+ }
+ if (!found) {
+ wins_wack_deny(s);
+ return;
+ }
+
+ wins_wack_allow(s);
+ return;
+}
+
+
+/*
+ a client has asked to register a unique name that someone else owns. We
+ need to ask each of the current owners if they still want it. If they do
+ then reject the registration, otherwise allow it
+*/
+static void wins_register_wack(struct nbt_name_socket *nbtsock,
+ struct nbt_name_packet *packet,
+ struct winsdb_record *rec,
+ struct socket_address *src,
+ enum wrepl_name_type new_type)
+{
+ struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private_data,
+ struct nbtd_interface);
+ struct wins_server *winssrv = iface->nbtsrv->winssrv;
+ struct nbtd_wins_wack_state *s;
+ struct composite_context *c_req;
+ uint32_t ttl;
+
+ s = talloc_zero(nbtsock, struct nbtd_wins_wack_state);
+ if (s == NULL) goto failed;
+
+ /* package up the state variables for this wack request */
+ s->winssrv = winssrv;
+ s->nbtsock = nbtsock;
+ s->iface = iface;
+ s->request_packet = talloc_steal(s, packet);
+ s->rec = talloc_steal(s, rec);
+ s->reg_address = packet->additional[0].rdata.netbios.addresses[0].ipaddr;
+ s->new_type = new_type;
+ s->src = src;
+ if (talloc_reference(s, src) == NULL) goto failed;
+
+ s->io.in.nbtd_server = iface->nbtsrv;
+ s->io.in.nbt_port = lp_nbt_port(iface->nbtsrv->task->lp_ctx);
+ s->io.in.event_ctx = iface->nbtsrv->task->event_ctx;
+ s->io.in.name = rec->name;
+ s->io.in.num_addresses = winsdb_addr_list_length(rec->addresses);
+ s->io.in.addresses = winsdb_addr_string_list(s, rec->addresses);
+ if (s->io.in.addresses == NULL) goto failed;
+
+ DLIST_ADD_END(iface->wack_queue, s, struct nbtd_wins_wack_state *);
+
+ talloc_set_destructor(s, nbtd_wins_wack_state_destructor);
+
+ /*
+ * send a WACK to the client, specifying the maximum time it could
+ * take to check with the owner, plus some slack
+ */
+ ttl = 5 + 4 * winsdb_addr_list_length(rec->addresses);
+ nbtd_wack_reply(nbtsock, packet, src, ttl);
+
+ /*
+ * send the challenge to the old addresses
+ */
+ c_req = wins_challenge_send(s, &s->io);
+ if (c_req == NULL) goto failed;
+
+ c_req->async.fn = wack_wins_challenge_handler;
+ c_req->async.private_data = s;
+ return;
+
+failed:
+ talloc_free(s);
+ nbtd_name_registration_reply(nbtsock, packet, src, NBT_RCODE_SVR);
+}
+
+/*
+ register a name
+*/
+static void nbtd_winsserver_register(struct nbt_name_socket *nbtsock,
+ struct nbt_name_packet *packet,
+ struct socket_address *src)
+{
+ NTSTATUS status;
+ struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private_data,
+ struct nbtd_interface);
+ struct wins_server *winssrv = iface->nbtsrv->winssrv;
+ struct nbt_name *name = &packet->questions[0].name;
+ struct winsdb_record *rec;
+ uint8_t rcode = NBT_RCODE_OK;
+ uint16_t nb_flags = packet->additional[0].rdata.netbios.addresses[0].nb_flags;
+ const char *address = packet->additional[0].rdata.netbios.addresses[0].ipaddr;
+ bool mhomed = ((packet->operation & NBT_OPCODE) == NBT_OPCODE_MULTI_HOME_REG);
+ enum wrepl_name_type new_type = wrepl_type(nb_flags, name, mhomed);
+ struct winsdb_addr *winsdb_addr = NULL;
+ bool duplicate_packet;
+
+ /*
+ * as a special case, the local master browser name is always accepted
+ * for registration, but never stored, but w2k3 stores it if it's registered
+ * as a group name, (but a query for the 0x1D name still returns not found!)
+ */
+ if (name->type == NBT_NAME_MASTER && !(nb_flags & NBT_NM_GROUP)) {
+ rcode = NBT_RCODE_OK;
+ goto done;
+ }
+
+ /* w2k3 refuses 0x1B names with marked as group */
+ if (name->type == NBT_NAME_PDC && (nb_flags & NBT_NM_GROUP)) {
+ rcode = NBT_RCODE_RFS;
+ goto done;
+ }
+
+ /* w2k3 refuses 0x1C names with out marked as group */
+ if (name->type == NBT_NAME_LOGON && !(nb_flags & NBT_NM_GROUP)) {
+ rcode = NBT_RCODE_RFS;
+ goto done;
+ }
+
+ /* w2k3 refuses 0x1E names with out marked as group */
+ if (name->type == NBT_NAME_BROWSER && !(nb_flags & NBT_NM_GROUP)) {
+ rcode = NBT_RCODE_RFS;
+ goto done;
+ }
+
+ duplicate_packet = wins_check_wack_queue(iface, packet, src);
+ if (duplicate_packet) {
+ /* just ignore the packet */
+ DEBUG(5,("Ignoring duplicate packet while WACK is pending from %s:%d\n",
+ src->addr, src->port));
+ return;
+ }
+
+ status = winsdb_lookup(winssrv->wins_db, name, packet, &rec);
+ if (NT_STATUS_EQUAL(NT_STATUS_OBJECT_NAME_NOT_FOUND, status)) {
+ rcode = wins_register_new(nbtsock, packet, src, new_type);
+ goto done;
+ } else if (!NT_STATUS_IS_OK(status)) {
+ rcode = NBT_RCODE_SVR;
+ goto done;
+ } else if (rec->is_static) {
+ if (rec->type == WREPL_TYPE_GROUP || rec->type == WREPL_TYPE_SGROUP) {
+ rcode = NBT_RCODE_OK;
+ goto done;
+ }
+ rcode = NBT_RCODE_ACT;
+ goto done;
+ }
+
+ if (rec->type == WREPL_TYPE_GROUP) {
+ if (new_type != WREPL_TYPE_GROUP) {
+ DEBUG(2,("WINS: Attempt to register name %s as non normal group(%u)"
+ " while a normal group is already there\n",
+ nbt_name_string(packet, name), new_type));
+ rcode = NBT_RCODE_ACT;
+ goto done;
+ }
+
+ if (rec->state == WREPL_STATE_ACTIVE) {
+ /* TODO: is this correct? */
+ rcode = wins_update_ttl(nbtsock, packet, rec, NULL, src);
+ goto done;
+ }
+
+ /* TODO: is this correct? */
+ winsdb_delete(winssrv->wins_db, rec);
+ rcode = wins_register_new(nbtsock, packet, src, new_type);
+ goto done;
+ }
+
+ if (rec->state != WREPL_STATE_ACTIVE) {
+ winsdb_delete(winssrv->wins_db, rec);
+ rcode = wins_register_new(nbtsock, packet, src, new_type);
+ goto done;
+ }
+
+ switch (rec->type) {
+ case WREPL_TYPE_UNIQUE:
+ case WREPL_TYPE_MHOMED:
+ /*
+ * if its an active unique name, and the registration is for a group, then
+ * see if the unique name owner still wants the name
+ * TODO: is this correct?
+ */
+ if (new_type == WREPL_TYPE_GROUP || new_type == WREPL_TYPE_GROUP) {
+ wins_register_wack(nbtsock, packet, rec, src, new_type);
+ return;
+ }
+
+ /*
+ * if the registration is for an address that is currently active, then
+ * just update the expiry time of the record and the address
+ */
+ winsdb_addr = winsdb_addr_list_check(rec->addresses, address);
+ if (winsdb_addr) {
+ rcode = wins_update_ttl(nbtsock, packet, rec, winsdb_addr, src);
+ goto done;
+ }
+
+ /*
+ * we have to do a WACK to see if the current owner is willing
+ * to give up its claim
+ */
+ wins_register_wack(nbtsock, packet, rec, src, new_type);
+ return;
+
+ case WREPL_TYPE_GROUP:
+ /* this should not be reached as normal groups are handled above */
+ DEBUG(0,("BUG at %s\n",__location__));
+ rcode = NBT_RCODE_ACT;
+ goto done;
+
+ case WREPL_TYPE_SGROUP:
+ /* if the new record isn't also a special group, refuse the registration */
+ if (new_type != WREPL_TYPE_SGROUP) {
+ DEBUG(2,("WINS: Attempt to register name %s as non special group(%u)"
+ " while a special group is already there\n",
+ nbt_name_string(packet, name), new_type));
+ rcode = NBT_RCODE_ACT;
+ goto done;
+ }
+
+ /*
+ * if the registration is for an address that is currently active, then
+ * just update the expiry time of the record and the address
+ */
+ winsdb_addr = winsdb_addr_list_check(rec->addresses, address);
+ if (winsdb_addr) {
+ rcode = wins_update_ttl(nbtsock, packet, rec, winsdb_addr, src);
+ goto done;
+ }
+
+ rcode = wins_sgroup_merge(nbtsock, packet, rec, address, src);
+ goto done;
+ }
+
+done:
+ nbtd_name_registration_reply(nbtsock, packet, src, rcode);
+}
+
+static uint32_t ipv4_match_bits(struct in_addr ip1, struct in_addr ip2)
+{
+ uint32_t i, j, match=0;
+ uint8_t *p1, *p2;
+
+ p1 = (uint8_t *)&ip1.s_addr;
+ p2 = (uint8_t *)&ip2.s_addr;
+
+ for (i=0; i<4; i++) {
+ if (p1[i] != p2[i]) break;
+ match += 8;
+ }
+
+ if (i==4) return match;
+
+ for (j=0; j<8; j++) {
+ if ((p1[i] & (1<<(7-j))) != (p2[i] & (1<<(7-j))))
+ break;
+ match++;
+ }
+
+ return match;
+}
+
+static int nbtd_wins_randomize1Clist_sort(void *p1,/* (const char **) */
+ void *p2,/* (const char **) */
+ struct socket_address *src)
+{
+ const char *a1 = (const char *)*(const char **)p1;
+ const char *a2 = (const char *)*(const char **)p2;
+ uint32_t match_bits1;
+ uint32_t match_bits2;
+
+ match_bits1 = ipv4_match_bits(interpret_addr2(a1), interpret_addr2(src->addr));
+ match_bits2 = ipv4_match_bits(interpret_addr2(a2), interpret_addr2(src->addr));
+
+ return match_bits2 - match_bits1;
+}
+
+static void nbtd_wins_randomize1Clist(struct loadparm_context *lp_ctx,
+ const char **addresses, struct socket_address *src)
+{
+ const char *mask;
+ const char *tmp;
+ uint32_t num_addrs;
+ uint32_t idx, sidx;
+ int r;
+
+ for (num_addrs=0; addresses[num_addrs]; num_addrs++) { /* noop */ }
+
+ if (num_addrs <= 1) return; /* nothing to do */
+
+ /* first sort the addresses depending on the matching to the client */
+ ldb_qsort(addresses, num_addrs , sizeof(addresses[0]),
+ src, (ldb_qsort_cmp_fn_t)nbtd_wins_randomize1Clist_sort);
+
+ mask = lp_parm_string(lp_ctx, NULL, "nbtd", "wins_randomize1Clist_mask");
+ if (!mask) {
+ mask = "255.255.255.0";
+ }
+
+ /*
+ * choose a random address to be the first in the response to the client,
+ * preferr the addresses inside the nbtd:wins_randomize1Clist_mask netmask
+ */
+ r = random();
+ idx = sidx = r % num_addrs;
+
+ while (1) {
+ bool same;
+
+ /* if the current one is in the same subnet, use it */
+ same = iface_same_net(addresses[idx], src->addr, mask);
+ if (same) {
+ sidx = idx;
+ break;
+ }
+
+ /* we need to check for idx == 0, after checking for the same net */
+ if (idx == 0) break;
+ /*
+ * if we haven't found an address in the same subnet, search in ones
+ * which match the client more
+ *
+ * some notes:
+ *
+ * it's not "idx = idx % r" but "idx = r % idx"
+ * because in "a % b" b is the allowed range
+ * and b-1 is the maximum possible result, so it must be decreasing
+ * and the above idx == 0 check breaks the while(1) loop.
+ */
+ idx = r % idx;
+ }
+
+ /* note sidx == 0 is also valid here ... */
+ tmp = addresses[0];
+ addresses[0] = addresses[sidx];
+ addresses[sidx] = tmp;
+}
+
+/*
+ query a name
+*/
+static void nbtd_winsserver_query(struct loadparm_context *lp_ctx,
+ struct nbt_name_socket *nbtsock,
+ struct nbt_name_packet *packet,
+ struct socket_address *src)
+{
+ NTSTATUS status;
+ struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private_data,
+ struct nbtd_interface);
+ struct wins_server *winssrv = iface->nbtsrv->winssrv;
+ struct nbt_name *name = &packet->questions[0].name;
+ struct winsdb_record *rec;
+ struct winsdb_record *rec_1b = NULL;
+ const char **addresses;
+ const char **addresses_1b = NULL;
+ uint16_t nb_flags = 0;
+
+ if (name->type == NBT_NAME_MASTER) {
+ goto notfound;
+ }
+
+ /*
+ * w2k3 returns the first address of the 0x1B record as first address
+ * to a 0x1C query
+ *
+ * since Windows 2000 Service Pack 2 there's on option to trigger this behavior:
+ *
+ * HKEY_LOCAL_MACHINE\System\CurrentControlset\Services\WINS\Parameters\Prepend1BTo1CQueries
+ * Typ: Daten REG_DWORD
+ * Value: 0 = deactivated, 1 = activated
+ */
+ if (name->type == NBT_NAME_LOGON &&
+ lp_parm_bool(lp_ctx, NULL, "nbtd", "wins_prepend1Bto1Cqueries", true)) {
+ struct nbt_name name_1b;
+
+ name_1b = *name;
+ name_1b.type = NBT_NAME_PDC;
+
+ status = winsdb_lookup(winssrv->wins_db, &name_1b, packet, &rec_1b);
+ if (NT_STATUS_IS_OK(status)) {
+ addresses_1b = winsdb_addr_string_list(packet, rec_1b->addresses);
+ }
+ }
+
+ status = winsdb_lookup(winssrv->wins_db, name, packet, &rec);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (!lp_wins_dns_proxy(lp_ctx)) {
+ goto notfound;
+ }
+
+ if (name->type != NBT_NAME_CLIENT && name->type != NBT_NAME_SERVER) {
+ goto notfound;
+ }
+
+ nbtd_wins_dns_proxy_query(nbtsock, packet, src);
+ return;
+ }
+
+ /*
+ * for group's we always reply with
+ * 255.255.255.255 as address, even if
+ * the record is released or tombstoned
+ */
+ if (rec->type == WREPL_TYPE_GROUP) {
+ addresses = str_list_add(NULL, "255.255.255.255");
+ talloc_steal(packet, addresses);
+ if (!addresses) {
+ goto notfound;
+ }
+ nb_flags |= NBT_NM_GROUP;
+ goto found;
+ }
+
+ if (rec->state != WREPL_STATE_ACTIVE) {
+ goto notfound;
+ }
+
+ addresses = winsdb_addr_string_list(packet, rec->addresses);
+ if (!addresses) {
+ goto notfound;
+ }
+
+ /*
+ * if addresses_1b isn't NULL, we have a 0x1C query and need to return the
+ * first 0x1B address as first address
+ */
+ if (addresses_1b && addresses_1b[0]) {
+ const char **addresses_1c = addresses;
+ uint32_t i;
+ uint32_t num_addrs;
+
+ addresses = str_list_add(NULL, addresses_1b[0]);
+ if (!addresses) {
+ goto notfound;
+ }
+ talloc_steal(packet, addresses);
+ num_addrs = 1;
+
+ for (i=0; addresses_1c[i]; i++) {
+ if (strcmp(addresses_1b[0], addresses_1c[i]) == 0) continue;
+
+ /*
+ * stop when we already have 25 addresses
+ */
+ if (num_addrs >= 25) break;
+
+ num_addrs++;
+ addresses = str_list_add(addresses, addresses_1c[i]);
+ if (!addresses) {
+ goto notfound;
+ }
+ }
+ }
+
+ if (rec->type == WREPL_TYPE_SGROUP) {
+ nb_flags |= NBT_NM_GROUP;
+ } else {
+ nb_flags |= (rec->node <<13);
+ }
+
+ /*
+ * since Windows 2000 Service Pack 2 there's on option to trigger this behavior:
+ *
+ * HKEY_LOCAL_MACHINE\System\CurrentControlset\Services\WINS\Parameters\Randomize1CList
+ * Typ: Daten REG_DWORD
+ * Value: 0 = deactivated, 1 = activated
+ */
+ if (name->type == NBT_NAME_LOGON &&
+ lp_parm_bool(lp_ctx, NULL, "nbtd", "wins_randomize1Clist", false)) {
+ nbtd_wins_randomize1Clist(lp_ctx, addresses, src);
+ }
+
+found:
+ nbtd_name_query_reply(nbtsock, packet, src, name,
+ 0, nb_flags, addresses);
+ return;
+
+notfound:
+ nbtd_negative_name_query_reply(nbtsock, packet, src);
+}
+
+/*
+ release a name
+*/
+static void nbtd_winsserver_release(struct nbt_name_socket *nbtsock,
+ struct nbt_name_packet *packet,
+ struct socket_address *src)
+{
+ NTSTATUS status;
+ struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private_data,
+ struct nbtd_interface);
+ struct wins_server *winssrv = iface->nbtsrv->winssrv;
+ struct nbt_name *name = &packet->questions[0].name;
+ struct winsdb_record *rec;
+ uint32_t modify_flags = 0;
+ uint8_t ret;
+
+ if (name->type == NBT_NAME_MASTER) {
+ goto done;
+ }
+
+ status = winsdb_lookup(winssrv->wins_db, name, packet, &rec);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ if (rec->is_static) {
+ if (rec->type == WREPL_TYPE_UNIQUE || rec->type == WREPL_TYPE_MHOMED) {
+ goto done;
+ }
+ nbtd_name_release_reply(nbtsock, packet, src, NBT_RCODE_ACT);
+ return;
+ }
+
+ if (rec->state != WREPL_STATE_ACTIVE) {
+ goto done;
+ }
+
+ /*
+ * TODO: do we need to check if
+ * src->addr matches packet->additional[0].rdata.netbios.addresses[0].ipaddr
+ * here?
+ */
+
+ /*
+ * we only allow releases from an owner - other releases are
+ * silently ignored
+ */
+ if (!winsdb_addr_list_check(rec->addresses, src->addr)) {
+ int i;
+ DEBUG(4,("WINS: silently ignoring attempted name release on %s from %s\n", nbt_name_string(rec, rec->name), src->addr));
+ DEBUGADD(4, ("Registered Addresses: \n"));
+ for (i=0; rec->addresses && rec->addresses[i]; i++) {
+ DEBUGADD(4, ("%s\n", rec->addresses[i]->address));
+ }
+ goto done;
+ }
+
+ DEBUG(4,("WINS: released name %s from %s\n", nbt_name_string(rec, rec->name), src->addr));
+
+ switch (rec->type) {
+ case WREPL_TYPE_UNIQUE:
+ rec->state = WREPL_STATE_RELEASED;
+ break;
+
+ case WREPL_TYPE_GROUP:
+ rec->state = WREPL_STATE_RELEASED;
+ break;
+
+ case WREPL_TYPE_SGROUP:
+ winsdb_addr_list_remove(rec->addresses, src->addr);
+ /* TODO: do we need to take the ownership here? */
+ if (winsdb_addr_list_length(rec->addresses) == 0) {
+ rec->state = WREPL_STATE_RELEASED;
+ }
+ break;
+
+ case WREPL_TYPE_MHOMED:
+ winsdb_addr_list_remove(rec->addresses, src->addr);
+ /* TODO: do we need to take the ownership here? */
+ if (winsdb_addr_list_length(rec->addresses) == 0) {
+ rec->state = WREPL_STATE_RELEASED;
+ }
+ break;
+ }
+
+ if (rec->state == WREPL_STATE_ACTIVE) {
+ /*
+ * If the record is still active, we need to update the
+ * expire_time.
+ *
+ * if we're not the owner, we need to take the ownership.
+ */
+ rec->expire_time= time(NULL) + winssrv->config.max_renew_interval;
+ if (strcmp(rec->wins_owner, winssrv->wins_db->local_owner) != 0) {
+ modify_flags = WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP;
+ }
+ if (lp_parm_bool(iface->nbtsrv->task->lp_ctx, NULL, "wreplsrv", "propagate name releases", false)) {
+ /*
+ * We have an option to propagate every name release,
+ * this is off by default to match windows servers
+ */
+ modify_flags = WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP;
+ }
+ } else if (rec->state == WREPL_STATE_RELEASED) {
+ /*
+ * if we're not the owner, we need to take the owner ship
+ * and make the record tombstone, but expire after
+ * tombstone_interval + tombstone_timeout and not only after tombstone_timeout
+ * like for normal tombstone records.
+ * This is to replicate the record directly to the original owner,
+ * where the record is still active
+ */
+ if (strcmp(rec->wins_owner, winssrv->wins_db->local_owner) == 0) {
+ rec->expire_time= time(NULL) + winssrv->config.tombstone_interval;
+ } else {
+ rec->state = WREPL_STATE_TOMBSTONE;
+ rec->expire_time= time(NULL) +
+ winssrv->config.tombstone_interval +
+ winssrv->config.tombstone_timeout;
+ modify_flags = WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP;
+ }
+ }
+
+ ret = winsdb_modify(winssrv->wins_db, rec, modify_flags);
+ if (ret != NBT_RCODE_OK) {
+ DEBUG(1,("WINS: FAILED: released name %s at %s: error:%u\n",
+ nbt_name_string(rec, rec->name), src->addr, ret));
+ }
+done:
+ /* we match w2k3 by always giving a positive reply to name releases. */
+ nbtd_name_release_reply(nbtsock, packet, src, NBT_RCODE_OK);
+}
+
+
+/*
+ answer a name query
+*/
+void nbtd_winsserver_request(struct nbt_name_socket *nbtsock,
+ struct nbt_name_packet *packet,
+ struct socket_address *src)
+{
+ struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private_data,
+ struct nbtd_interface);
+ struct wins_server *winssrv = iface->nbtsrv->winssrv;
+ if ((packet->operation & NBT_FLAG_BROADCAST) || winssrv == NULL) {
+ return;
+ }
+
+ switch (packet->operation & NBT_OPCODE) {
+ case NBT_OPCODE_QUERY:
+ nbtd_winsserver_query(iface->nbtsrv->task->lp_ctx, nbtsock, packet, src);
+ break;
+
+ case NBT_OPCODE_REGISTER:
+ case NBT_OPCODE_REFRESH:
+ case NBT_OPCODE_REFRESH2:
+ case NBT_OPCODE_MULTI_HOME_REG:
+ nbtd_winsserver_register(nbtsock, packet, src);
+ break;
+
+ case NBT_OPCODE_RELEASE:
+ nbtd_winsserver_release(nbtsock, packet, src);
+ break;
+ }
+
+}
+
+/*
+ startup the WINS server, if configured
+*/
+NTSTATUS nbtd_winsserver_init(struct nbtd_server *nbtsrv)
+{
+ uint32_t tmp;
+ const char *owner;
+
+ if (!lp_wins_support(nbtsrv->task->lp_ctx)) {
+ nbtsrv->winssrv = NULL;
+ return NT_STATUS_OK;
+ }
+
+ nbtsrv->winssrv = talloc_zero(nbtsrv, struct wins_server);
+ NT_STATUS_HAVE_NO_MEMORY(nbtsrv->winssrv);
+
+ nbtsrv->winssrv->config.max_renew_interval = lp_max_wins_ttl(nbtsrv->task->lp_ctx);
+ nbtsrv->winssrv->config.min_renew_interval = lp_min_wins_ttl(nbtsrv->task->lp_ctx);
+ tmp = lp_parm_int(nbtsrv->task->lp_ctx, NULL, "wreplsrv", "tombstone_interval", 6*24*60*60);
+ nbtsrv->winssrv->config.tombstone_interval = tmp;
+ tmp = lp_parm_int(nbtsrv->task->lp_ctx, NULL, "wreplsrv"," tombstone_timeout", 1*24*60*60);
+ nbtsrv->winssrv->config.tombstone_timeout = tmp;
+
+ owner = lp_parm_string(nbtsrv->task->lp_ctx, NULL, "winsdb", "local_owner");
+
+ if (owner == NULL) {
+ struct interface *ifaces;
+ load_interfaces(nbtsrv->task, lp_interfaces(nbtsrv->task->lp_ctx), &ifaces);
+ owner = iface_n_ip(ifaces, 0);
+ }
+
+ nbtsrv->winssrv->wins_db = winsdb_connect(nbtsrv->winssrv, nbtsrv->task->event_ctx,
+ nbtsrv->task->lp_ctx,
+ owner, WINSDB_HANDLE_CALLER_NBTD);
+ if (!nbtsrv->winssrv->wins_db) {
+ return NT_STATUS_INTERNAL_DB_ERROR;
+ }
+
+ irpc_add_name(nbtsrv->task->msg_ctx, "wins_server");
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/nbt_server/wins/winsserver.h b/source4/nbt_server/wins/winsserver.h
new file mode 100644
index 0000000000..9f03557237
--- /dev/null
+++ b/source4/nbt_server/wins/winsserver.h
@@ -0,0 +1,66 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ wins server WACK processing
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+struct wins_server {
+ /* wins server database handle */
+ struct winsdb_handle *wins_db;
+
+ /* some configuration */
+ struct {
+ /*
+ * the interval (in secs) till an active record will be marked as RELEASED
+ */
+ uint32_t min_renew_interval;
+ uint32_t max_renew_interval;
+
+ /*
+ * the interval (in secs) a record remains in RELEASED state,
+ * before it will be marked as TOMBSTONE
+ * (also known as extinction interval)
+ */
+ uint32_t tombstone_interval;
+
+ /*
+ * the interval (in secs) a record remains in TOMBSTONE state,
+ * before it will be removed from the database.
+ * See also 'tombstone_extra_timeout'.
+ * (also known as extinction timeout)
+ */
+ uint32_t tombstone_timeout;
+ } config;
+};
+
+struct wins_challenge_io {
+ struct {
+ struct nbtd_server *nbtd_server;
+ uint16_t nbt_port;
+ struct tevent_context *event_ctx;
+ struct nbt_name *name;
+ uint32_t num_addresses;
+ const char **addresses;
+ } in;
+ struct {
+ uint32_t num_addresses;
+ const char **addresses;
+ } out;
+};
+
+#include "nbt_server/wins/winsserver_proto.h"
diff --git a/source4/nbt_server/wins/winswack.c b/source4/nbt_server/wins/winswack.c
new file mode 100644
index 0000000000..c53fa1d069
--- /dev/null
+++ b/source4/nbt_server/wins/winswack.c
@@ -0,0 +1,387 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ "secure" wins server WACK processing
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "nbt_server/nbt_server.h"
+#include "nbt_server/wins/winsdb.h"
+#include "nbt_server/wins/winsserver.h"
+#include "system/time.h"
+#include "libcli/composite/composite.h"
+#include "param/param.h"
+#include "smbd/service_task.h"
+
+struct wins_challenge_state {
+ struct wins_challenge_io *io;
+ uint32_t current_address;
+ struct nbt_name_query query;
+};
+
+static void wins_challenge_handler(struct nbt_name_request *req)
+{
+ struct composite_context *ctx = talloc_get_type(req->async.private_data, struct composite_context);
+ struct wins_challenge_state *state = talloc_get_type(ctx->private_data, struct wins_challenge_state);
+
+ ctx->status = nbt_name_query_recv(req, state, &state->query);
+
+ /* if we timed out then try the next owner address, if any */
+ if (NT_STATUS_EQUAL(ctx->status, NT_STATUS_IO_TIMEOUT)) {
+ state->current_address++;
+ if (state->current_address < state->io->in.num_addresses) {
+ struct nbtd_interface *iface;
+
+ state->query.in.dest_port = state->io->in.nbt_port;
+ state->query.in.dest_addr = state->io->in.addresses[state->current_address];
+
+ iface = nbtd_find_request_iface(state->io->in.nbtd_server, state->query.in.dest_addr, true);
+ if (!iface) {
+ composite_error(ctx, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
+ ZERO_STRUCT(state->query.out);
+ req = nbt_name_query_send(iface->nbtsock, &state->query);
+ composite_continue_nbt(ctx, req, wins_challenge_handler, ctx);
+ return;
+ }
+ }
+
+ composite_done(ctx);
+}
+
+NTSTATUS wins_challenge_recv(struct composite_context *ctx, TALLOC_CTX *mem_ctx, struct wins_challenge_io *io)
+{
+ NTSTATUS status = ctx->status;
+ struct wins_challenge_state *state = talloc_get_type(ctx->private_data, struct wins_challenge_state);
+
+ if (NT_STATUS_IS_OK(status)) {
+ io->out.num_addresses = state->query.out.num_addrs;
+ io->out.addresses = state->query.out.reply_addrs;
+ talloc_steal(mem_ctx, io->out.addresses);
+ } else {
+ ZERO_STRUCT(io->out);
+ }
+
+ talloc_free(ctx);
+ return status;
+}
+
+struct composite_context *wins_challenge_send(TALLOC_CTX *mem_ctx, struct wins_challenge_io *io)
+{
+ struct composite_context *result;
+ struct wins_challenge_state *state;
+ struct nbt_name_request *req;
+ struct nbtd_interface *iface;
+
+ result = talloc_zero(mem_ctx, struct composite_context);
+ if (result == NULL) return NULL;
+ result->state = COMPOSITE_STATE_IN_PROGRESS;
+ result->event_ctx = talloc_reference(result, io->in.event_ctx);
+
+ state = talloc_zero(result, struct wins_challenge_state);
+ if (state == NULL) goto failed;
+ result->private_data = state;
+
+ /* package up the state variables for this wack request */
+ state->io = io;
+ state->current_address = 0;
+
+ /* setup a name query to the first address */
+ state->query.in.name = *state->io->in.name;
+ state->query.in.dest_port = state->io->in.nbt_port;
+ state->query.in.dest_addr = state->io->in.addresses[state->current_address];
+ state->query.in.broadcast = false;
+ state->query.in.wins_lookup = true;
+ state->query.in.timeout = 1;
+ state->query.in.retries = 2;
+ ZERO_STRUCT(state->query.out);
+
+ iface = nbtd_find_request_iface(state->io->in.nbtd_server, state->query.in.dest_addr, true);
+ if (!iface) {
+ goto failed;
+ }
+
+ req = nbt_name_query_send(iface->nbtsock, &state->query);
+ if (req == NULL) goto failed;
+
+ req->async.fn = wins_challenge_handler;
+ req->async.private_data = result;
+
+ return result;
+failed:
+ talloc_free(result);
+ return NULL;
+}
+
+struct wins_release_demand_io {
+ struct {
+ struct nbtd_server *nbtd_server;
+ struct tevent_context *event_ctx;
+ struct nbt_name *name;
+ uint16_t nb_flags;
+ uint32_t num_addresses;
+ const char **addresses;
+ } in;
+};
+
+struct wins_release_demand_state {
+ struct wins_release_demand_io *io;
+ uint32_t current_address;
+ uint32_t addresses_left;
+ struct nbt_name_release release;
+};
+
+static void wins_release_demand_handler(struct nbt_name_request *req)
+{
+ struct composite_context *ctx = talloc_get_type(req->async.private_data, struct composite_context);
+ struct wins_release_demand_state *state = talloc_get_type(ctx->private_data, struct wins_release_demand_state);
+
+ ctx->status = nbt_name_release_recv(req, state, &state->release);
+
+ /* if we timed out then try the next owner address, if any */
+ if (NT_STATUS_EQUAL(ctx->status, NT_STATUS_IO_TIMEOUT)) {
+ state->current_address++;
+ state->addresses_left--;
+ if (state->current_address < state->io->in.num_addresses) {
+ struct nbtd_interface *iface;
+
+ state->release.in.dest_port = lp_nbt_port(state->io->in.nbtd_server->task->lp_ctx);
+ state->release.in.dest_addr = state->io->in.addresses[state->current_address];
+ state->release.in.address = state->release.in.dest_addr;
+ state->release.in.timeout = (state->addresses_left > 1 ? 2 : 1);
+ state->release.in.retries = (state->addresses_left > 1 ? 0 : 2);
+
+ iface = nbtd_find_request_iface(state->io->in.nbtd_server, state->release.in.dest_addr, true);
+ if (!iface) {
+ composite_error(ctx, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
+ ZERO_STRUCT(state->release.out);
+ req = nbt_name_release_send(iface->nbtsock, &state->release);
+ composite_continue_nbt(ctx, req, wins_release_demand_handler, ctx);
+ return;
+ }
+ }
+
+ composite_done(ctx);
+}
+
+static NTSTATUS wins_release_demand_recv(struct composite_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct wins_release_demand_io *io)
+{
+ NTSTATUS status = ctx->status;
+ talloc_free(ctx);
+ return status;
+}
+
+static struct composite_context *wins_release_demand_send(TALLOC_CTX *mem_ctx, struct wins_release_demand_io *io)
+{
+ struct composite_context *result;
+ struct wins_release_demand_state *state;
+ struct nbt_name_request *req;
+ struct nbtd_interface *iface;
+
+ result = talloc_zero(mem_ctx, struct composite_context);
+ if (result == NULL) return NULL;
+ result->state = COMPOSITE_STATE_IN_PROGRESS;
+ result->event_ctx = talloc_reference(result, io->in.event_ctx);
+
+ state = talloc_zero(result, struct wins_release_demand_state);
+ if (state == NULL) goto failed;
+ result->private_data = state;
+
+ /* package up the state variables for this wack request */
+ state->io = io;
+ state->current_address = 0;
+ state->addresses_left = state->io->in.num_addresses;
+
+ /*
+ * setup a name query to the first address
+ * - if we have more than one address try the first
+ * with 2 secs timeout and no retry
+ * - otherwise use 1 sec timeout (w2k3 uses 0.5 sec here)
+ * with 2 retries
+ */
+ state->release.in.name = *state->io->in.name;
+ state->release.in.dest_port = lp_nbt_port(state->io->in.nbtd_server->task->lp_ctx);
+ state->release.in.dest_addr = state->io->in.addresses[state->current_address];
+ state->release.in.address = state->release.in.dest_addr;
+ state->release.in.broadcast = false;
+ state->release.in.timeout = (state->addresses_left > 1 ? 2 : 1);
+ state->release.in.retries = (state->addresses_left > 1 ? 0 : 2);
+ ZERO_STRUCT(state->release.out);
+
+ iface = nbtd_find_request_iface(state->io->in.nbtd_server, state->release.in.dest_addr, true);
+ if (!iface) {
+ goto failed;
+ }
+
+ req = nbt_name_release_send(iface->nbtsock, &state->release);
+ if (req == NULL) goto failed;
+
+ req->async.fn = wins_release_demand_handler;
+ req->async.private_data = result;
+
+ return result;
+failed:
+ talloc_free(result);
+ return NULL;
+}
+
+/*
+ wrepl_server needs to be able to do a name query request, but some windows
+ servers always send the reply to port 137, regardless of the request
+ port. To cope with this we use a irpc request to the NBT server
+ which has port 137 open, and thus can receive the replies
+*/
+struct proxy_wins_challenge_state {
+ struct irpc_message *msg;
+ struct nbtd_proxy_wins_challenge *req;
+ struct wins_challenge_io io;
+ struct composite_context *c_req;
+};
+
+static void proxy_wins_challenge_handler(struct composite_context *c_req)
+{
+ NTSTATUS status;
+ uint32_t i;
+ struct proxy_wins_challenge_state *s = talloc_get_type(c_req->async.private_data,
+ struct proxy_wins_challenge_state);
+
+ status = wins_challenge_recv(s->c_req, s, &s->io);
+ if (!NT_STATUS_IS_OK(status)) {
+ ZERO_STRUCT(s->req->out);
+ irpc_send_reply(s->msg, status);
+ return;
+ }
+
+ s->req->out.num_addrs = s->io.out.num_addresses;
+ /* TODO: fix pidl to handle inline ipv4address arrays */
+ s->req->out.addrs = talloc_array(s->msg, struct nbtd_proxy_wins_addr,
+ s->io.out.num_addresses);
+ if (!s->req->out.addrs) {
+ ZERO_STRUCT(s->req->out);
+ irpc_send_reply(s->msg, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ for (i=0; i < s->io.out.num_addresses; i++) {
+ s->req->out.addrs[i].addr = talloc_steal(s->req->out.addrs, s->io.out.addresses[i]);
+ }
+
+ irpc_send_reply(s->msg, status);
+}
+
+NTSTATUS nbtd_proxy_wins_challenge(struct irpc_message *msg,
+ struct nbtd_proxy_wins_challenge *req)
+{
+ struct nbtd_server *nbtd_server =
+ talloc_get_type(msg->private_data, struct nbtd_server);
+ struct proxy_wins_challenge_state *s;
+ uint32_t i;
+
+ s = talloc(msg, struct proxy_wins_challenge_state);
+ NT_STATUS_HAVE_NO_MEMORY(s);
+
+ s->msg = msg;
+ s->req = req;
+
+ s->io.in.nbtd_server = nbtd_server;
+ s->io.in.nbt_port = lp_nbt_port(nbtd_server->task->lp_ctx);
+ s->io.in.event_ctx = msg->ev;
+ s->io.in.name = &req->in.name;
+ s->io.in.num_addresses = req->in.num_addrs;
+ s->io.in.addresses = talloc_array(s, const char *, req->in.num_addrs);
+ NT_STATUS_HAVE_NO_MEMORY(s->io.in.addresses);
+ /* TODO: fix pidl to handle inline ipv4address arrays */
+ for (i=0; i < req->in.num_addrs; i++) {
+ s->io.in.addresses[i] = talloc_steal(s->io.in.addresses, req->in.addrs[i].addr);
+ }
+
+ s->c_req = wins_challenge_send(s, &s->io);
+ NT_STATUS_HAVE_NO_MEMORY(s->c_req);
+
+ s->c_req->async.fn = proxy_wins_challenge_handler;
+ s->c_req->async.private_data = s;
+
+ msg->defer_reply = true;
+ return NT_STATUS_OK;
+}
+
+/*
+ wrepl_server needs to be able to do a name release demands, but some windows
+ servers always send the reply to port 137, regardless of the request
+ port. To cope with this we use a irpc request to the NBT server
+ which has port 137 open, and thus can receive the replies
+*/
+struct proxy_wins_release_demand_state {
+ struct irpc_message *msg;
+ struct nbtd_proxy_wins_release_demand *req;
+ struct wins_release_demand_io io;
+ struct composite_context *c_req;
+};
+
+static void proxy_wins_release_demand_handler(struct composite_context *c_req)
+{
+ NTSTATUS status;
+ struct proxy_wins_release_demand_state *s = talloc_get_type(c_req->async.private_data,
+ struct proxy_wins_release_demand_state);
+
+ status = wins_release_demand_recv(s->c_req, s, &s->io);
+
+ irpc_send_reply(s->msg, status);
+}
+
+NTSTATUS nbtd_proxy_wins_release_demand(struct irpc_message *msg,
+ struct nbtd_proxy_wins_release_demand *req)
+{
+ struct nbtd_server *nbtd_server =
+ talloc_get_type(msg->private_data, struct nbtd_server);
+ struct proxy_wins_release_demand_state *s;
+ uint32_t i;
+
+ s = talloc(msg, struct proxy_wins_release_demand_state);
+ NT_STATUS_HAVE_NO_MEMORY(s);
+
+ s->msg = msg;
+ s->req = req;
+
+ s->io.in.nbtd_server = nbtd_server;
+ s->io.in.event_ctx = msg->ev;
+ s->io.in.name = &req->in.name;
+ s->io.in.num_addresses = req->in.num_addrs;
+ s->io.in.addresses = talloc_array(s, const char *, req->in.num_addrs);
+ NT_STATUS_HAVE_NO_MEMORY(s->io.in.addresses);
+ /* TODO: fix pidl to handle inline ipv4address arrays */
+ for (i=0; i < req->in.num_addrs; i++) {
+ s->io.in.addresses[i] = talloc_steal(s->io.in.addresses, req->in.addrs[i].addr);
+ }
+
+ s->c_req = wins_release_demand_send(s, &s->io);
+ NT_STATUS_HAVE_NO_MEMORY(s->c_req);
+
+ s->c_req->async.fn = proxy_wins_release_demand_handler;
+ s->c_req->async.private_data = s;
+
+ msg->defer_reply = true;
+ return NT_STATUS_OK;
+}
diff --git a/source4/ntp_signd/README b/source4/ntp_signd/README
new file mode 100644
index 0000000000..585459b7cb
--- /dev/null
+++ b/source4/ntp_signd/README
@@ -0,0 +1,7 @@
+Here are some pointers to the needed ntp version.
+
+https://support.ntp.org/bugs/show_bug.cgi?id=1028
+
+The patch against ntp-dev-4.2.5p125
+https://support.ntp.org/bugs/attachment.cgi?id=457
+
diff --git a/source4/ntp_signd/config.mk b/source4/ntp_signd/config.mk
new file mode 100644
index 0000000000..07c404d822
--- /dev/null
+++ b/source4/ntp_signd/config.mk
@@ -0,0 +1,14 @@
+# NTP_SIGND server subsystem
+
+#######################
+# Start SUBSYSTEM NTP_signd
+[MODULE::NTP_SIGND]
+INIT_FUNCTION = server_service_ntp_signd_init
+SUBSYSTEM = service
+PRIVATE_DEPENDENCIES = \
+ SAMDB NDR_NTP_SIGND
+# End SUBSYSTEM NTP_SIGND
+#######################
+
+NTP_SIGND_OBJ_FILES = $(addprefix $(ntp_signdsrcdir)/, ntp_signd.o)
+
diff --git a/source4/ntp_signd/ntp-dev-4.2.5p125.diff b/source4/ntp_signd/ntp-dev-4.2.5p125.diff
new file mode 100644
index 0000000000..40669fb3cc
--- /dev/null
+++ b/source4/ntp_signd/ntp-dev-4.2.5p125.diff
@@ -0,0 +1,579 @@
+Only in ntp-samba: autom4te.cache
+Only in ntp-samba: config.h
+Only in ntp-samba: config.log
+Only in ntp-samba: config.status
+Only in ntp-samba/ElectricFence: .deps
+Only in ntp-samba/ElectricFence: Makefile
+Only in ntp-samba: .gcc-warning
+Only in ntp-samba/include/isc: Makefile
+Only in ntp-samba/include: Makefile
+diff -ur ntp-dev-4.2.5p125/include/ntp_config.h ntp-samba/include/ntp_config.h
+--- ntp-dev-4.2.5p125/include/ntp_config.h 2008-07-17 07:20:58.000000000 +1000
++++ ntp-samba/include/ntp_config.h 2008-08-28 21:59:06.000000000 +1000
+@@ -92,6 +92,7 @@
+ int requested_key;
+ int revoke;
+ queue *trusted_key_list;
++ char *ntp_signd_socket;
+ };
+
+ struct filegen_node {
+diff -ur ntp-dev-4.2.5p125/include/ntpd.h ntp-samba/include/ntpd.h
+--- ntp-dev-4.2.5p125/include/ntpd.h 2008-05-18 21:11:28.000000000 +1000
++++ ntp-samba/include/ntpd.h 2008-08-28 21:59:06.000000000 +1000
+@@ -259,6 +259,8 @@
+ extern int config_priority;
+ #endif
+
++extern char const *ntp_signd_socket;
++
+ /* ntp_control.c */
+ extern int num_ctl_traps;
+ extern keyid_t ctl_auth_keyid; /* keyid used for authenticating write requests */
+@@ -471,3 +473,15 @@
+ extern struct refclock *refclock_conf[]; /* refclock configuration table */
+ extern u_char num_refclock_conf;
+ #endif
++
++/* ntp_signd.c */
++#ifdef HAVE_NTP_SIGND
++extern void
++send_via_ntp_signd(
++ struct recvbuf *rbufp, /* receive packet pointer */
++ int xmode,
++ keyid_t xkeyid,
++ int flags,
++ struct pkt *xpkt
++ );
++#endif
+diff -ur ntp-dev-4.2.5p125/include/ntp.h ntp-samba/include/ntp.h
+--- ntp-dev-4.2.5p125/include/ntp.h 2008-08-10 22:37:56.000000000 +1000
++++ ntp-samba/include/ntp.h 2008-08-28 21:59:06.000000000 +1000
+@@ -447,6 +447,7 @@
+ #ifdef OPENSSL
+ #define FLAG_ASSOC 0x4000 /* autokey request */
+ #endif /* OPENSSL */
++#define FLAG_ADKEY 0x00010000 /* Authenticated (or wants reply to be authenticated) using AD authentication */
+
+ /*
+ * Definitions for the clear() routine. We use memset() to clear
+Only in ntp-samba/include: ntp.h.orig
+Only in ntp-samba: libtool
+Only in ntp-samba: Makefile
+diff -ur ntp-dev-4.2.5p125/ntpd/Makefile.am ntp-samba/ntpd/Makefile.am
+--- ntp-dev-4.2.5p125/ntpd/Makefile.am 2008-05-18 21:11:29.000000000 +1000
++++ ntp-samba/ntpd/Makefile.am 2008-08-28 21:59:06.000000000 +1000
+@@ -65,7 +65,7 @@
+ ntp_crypto.c ntp_filegen.c \
+ ntp_intres.c ntp_loopfilter.c ntp_monitor.c ntp_peer.c \
+ ntp_proto.c ntp_refclock.c ntp_request.c \
+- ntp_restrict.c ntp_timer.c ntp_util.c \
++ ntp_restrict.c ntp_timer.c ntp_util.c ntp_signd.c \
+ ppsapi_timepps.h \
+ refclock_acts.c refclock_arbiter.c refclock_arc.c refclock_as2201.c \
+ refclock_atom.c refclock_bancomm.c refclock_chronolog.c \
+diff -ur ntp-dev-4.2.5p125/ntpd/ntp_config.c ntp-samba/ntpd/ntp_config.c
+--- ntp-dev-4.2.5p125/ntpd/ntp_config.c 2008-08-10 22:37:54.000000000 +1000
++++ ntp-samba/ntpd/ntp_config.c 2008-08-28 22:03:52.000000000 +1000
+@@ -148,6 +148,7 @@
+ #endif
+
+ const char *config_file;
++const char *ntp_signd_socket;
+ #ifdef HAVE_NETINFO
+ struct netinfo_config_state *config_netinfo = NULL;
+ int check_netinfo = 1;
+@@ -276,6 +277,11 @@
+ my_config.auth.crypto_cmd_list = NULL;
+ my_config.auth.keys = NULL;
+ my_config.auth.keysdir = NULL;
++#ifdef NTP_SIGND_PATH
++ my_config.auth.ntp_signd_socket = NTP_SIGND_PATH;
++#else
++ my_config.auth.ntp_signd_socket = NULL;
++#endif
+ my_config.auth.requested_key = 0;
+ my_config.auth.revoke = 0;
+ my_config.auth.trusted_key_list = NULL;
+@@ -795,6 +801,7 @@
+ { "crypto", T_Crypto, NO_ARG },
+ { "keys", T_Keys, SINGLE_ARG },
+ { "keysdir", T_Keysdir, SINGLE_ARG },
++ { "ntpsigndsocket", T_NtpSignDsocket, SINGLE_ARG },
+ { "requestkey", T_Requestkey, NO_ARG },
+ { "revoke", T_Revoke, NO_ARG },
+ { "trustedkey", T_Trustedkey, NO_ARG },
+@@ -1000,6 +1007,10 @@
+ if (my_config.auth.keysdir)
+ keysdir = my_config.auth.keysdir;
+
++ /* ntp_signd_socket Command */
++ if (my_config.auth.ntp_signd_socket)
++ ntp_signd_socket = my_config.auth.ntp_signd_socket;
++
+ #ifdef OPENSSL
+ if (cryptosw) {
+ crypto_setup();
+Only in ntp-samba/ntpd: ntp_config.c~
+Only in ntp-samba/ntpd: ntp_config.c.orig
+diff -ur ntp-dev-4.2.5p125/ntpd/ntp_parser.y ntp-samba/ntpd/ntp_parser.y
+--- ntp-dev-4.2.5p125/ntpd/ntp_parser.y 2008-07-17 07:21:06.000000000 +1000
++++ ntp-samba/ntpd/ntp_parser.y 2008-08-28 21:59:06.000000000 +1000
+@@ -155,6 +155,7 @@
+ %token T_Novolley
+ %token T_Ntp
+ %token T_Ntpport
++%token T_NtpSignDsocket
+ %token T_Orphan
+ %token T_Panic
+ %token T_Peer
+@@ -432,6 +433,8 @@
+ { my_config.auth.requested_key = $2; }
+ | T_Trustedkey integer_list
+ { my_config.auth.trusted_key_list = $2; }
++ | T_NtpSignDsocket T_String
++ { my_config.auth.ntp_signd_socket = $2; }
+ ;
+
+ crypto_command_line
+diff -ur ntp-dev-4.2.5p125/ntpd/ntp_proto.c ntp-samba/ntpd/ntp_proto.c
+--- ntp-dev-4.2.5p125/ntpd/ntp_proto.c 2008-07-17 07:21:02.000000000 +1000
++++ ntp-samba/ntpd/ntp_proto.c 2008-08-28 21:59:06.000000000 +1000
+@@ -128,7 +128,7 @@
+ static void clock_combine (struct peer **, int);
+ static void peer_xmit (struct peer *);
+ static void fast_xmit (struct recvbuf *, int, keyid_t,
+- char *);
++ char *, int);
+ static void clock_update (struct peer *);
+ static int default_get_precision (void);
+ static int peer_unfit (struct peer *);
+@@ -311,6 +311,7 @@
+ int authlen; /* offset of MAC field */
+ int is_authentic = 0; /* cryptosum ok */
+ int retcode = AM_NOMATCH; /* match code */
++ int flags = 0; /* flags with details about the authentication */
+ keyid_t skeyid = 0; /* key IDs */
+ u_int32 opcode = 0; /* extension field opcode */
+ struct sockaddr_storage *dstadr_sin; /* active runway */
+@@ -324,6 +325,8 @@
+ keyid_t pkeyid = 0, tkeyid = 0; /* key IDs */
+ #endif /* OPENSSL */
+
++ static unsigned char zero_key[16];
++
+ /*
+ * Monitor the packet and get restrictions. Note that the packet
+ * length for control and private mode packets must be checked
+@@ -480,9 +483,9 @@
+ return; /* rate exceeded */
+
+ if (hismode == MODE_CLIENT)
+- fast_xmit(rbufp, MODE_SERVER, skeyid, "RATE");
++ fast_xmit(rbufp, MODE_SERVER, skeyid, "RATE", 0);
+ else
+- fast_xmit(rbufp, MODE_ACTIVE, skeyid, "RATE");
++ fast_xmit(rbufp, MODE_ACTIVE, skeyid, "RATE", 0);
+ return; /* rate exceeded */
+ }
+
+@@ -535,6 +538,7 @@
+ * is zero, acceptable outcomes of y are NONE and OK. If x is
+ * one, the only acceptable outcome of y is OK.
+ */
++
+ if (has_mac == 0) {
+ is_authentic = AUTH_NONE; /* not required */
+ #ifdef DEBUG
+@@ -555,6 +559,25 @@
+ stoa(&rbufp->recv_srcadr), hismode, skeyid,
+ authlen + has_mac, is_authentic);
+ #endif
++
++ /* If the signature is 20 bytes long, the last 16 of
++ * which are zero, then this is a Microsoft client
++ * wanting AD-style authentication of the server's
++ * reply.
++ *
++ * This is described in Microsoft's WSPP docs, in MS-SNTP:
++ * http://msdn.microsoft.com/en-us/library/cc212930.aspx
++ */
++ } else if (has_mac == MAX_MAC_LEN
++ && (retcode == AM_FXMIT || retcode == AM_NEWPASS)
++ && (memcmp(zero_key, (char *)pkt + authlen + 4, MAX_MAC_LEN - 4) == 0)) {
++
++ /* Don't try to verify the zeros, just set a
++ * flag and otherwise pretend we never saw the signature */
++ is_authentic = AUTH_NONE;
++
++ flags = FLAG_ADKEY;
++
+ } else {
+ #ifdef OPENSSL
+ /*
+@@ -696,9 +719,9 @@
+ if (AUTH(restrict_mask & RES_DONTTRUST,
+ is_authentic)) {
+ fast_xmit(rbufp, MODE_SERVER, skeyid,
+- NULL);
++ NULL, flags);
+ } else if (is_authentic == AUTH_ERROR) {
+- fast_xmit(rbufp, MODE_SERVER, 0, NULL);
++ fast_xmit(rbufp, MODE_SERVER, 0, NULL, 0);
+ sys_badauth++;
+ } else {
+ sys_restricted++;
+@@ -733,7 +756,7 @@
+ * crypto-NAK, as that would not be useful.
+ */
+ if (AUTH(restrict_mask & RES_DONTTRUST, is_authentic))
+- fast_xmit(rbufp, MODE_SERVER, skeyid, NULL);
++ fast_xmit(rbufp, MODE_SERVER, skeyid, NULL, 0);
+ return; /* hooray */
+
+ /*
+@@ -888,7 +911,7 @@
+ is_authentic)) {
+ #ifdef OPENSSL
+ if (crypto_flags && skeyid > NTP_MAXKEY)
+- fast_xmit(rbufp, MODE_ACTIVE, 0, NULL);
++ fast_xmit(rbufp, MODE_ACTIVE, 0, NULL, 0);
+ #endif /* OPENSSL */
+ sys_restricted++;
+ return; /* access denied */
+@@ -904,7 +927,7 @@
+ * This is for drat broken Windows clients. See
+ * Microsoft KB 875424 for preferred workaround.
+ */
+- fast_xmit(rbufp, MODE_PASSIVE, skeyid, NULL);
++ fast_xmit(rbufp, MODE_PASSIVE, skeyid, NULL, flags);
+ #else /* WINTIME */
+ sys_restricted++;
+ #endif /* WINTIME */
+@@ -938,6 +961,7 @@
+ }
+ break;
+
++
+ /*
+ * Process regular packet. Nothing special.
+ */
+@@ -1090,7 +1114,7 @@
+ peer->flash |= TEST5; /* bad auth */
+ peer->badauth++;
+ if (hismode == MODE_ACTIVE || hismode == MODE_PASSIVE)
+- fast_xmit(rbufp, MODE_ACTIVE, 0, NULL);
++ fast_xmit(rbufp, MODE_ACTIVE, 0, NULL, 0);
+ if (peer->flags & FLAG_PREEMPT) {
+ unpeer(peer);
+ return;
+@@ -3159,7 +3183,8 @@
+ struct recvbuf *rbufp, /* receive packet pointer */
+ int xmode, /* receive mode */
+ keyid_t xkeyid, /* transmit key ID */
+- char *mask /* kiss code */
++ char *mask, /* kiss code */
++ int flags /* Flags to indicate signing behaviour */
+ )
+ {
+ struct pkt xpkt; /* transmit packet structure */
+@@ -3220,6 +3245,19 @@
+ HTONL_FP(&rbufp->recv_time, &xpkt.rec);
+ }
+
++ if (flags & FLAG_ADKEY) {
++#ifdef HAVE_NTP_SIGND
++ get_systime(&xmt_tx);
++ if (mask == NULL) {
++ HTONL_FP(&xmt_tx, &xpkt.xmt);
++ }
++ send_via_ntp_signd(rbufp, xmode, xkeyid, flags, &xpkt);
++#endif
++ /* If we don't have the support, drop the packet on the floor.
++ An all zero sig is compleatly bogus anyway */
++ return;
++ }
++
+ /*
+ * If the received packet contains a MAC, the transmitted packet
+ * is authenticated and contains a MAC. If not, the transmitted
+@@ -3252,7 +3290,7 @@
+ * source-destination-key ID combination.
+ */
+ #ifdef OPENSSL
+- if (xkeyid > NTP_MAXKEY) {
++ if (!(flags & FLAG_ADKEY) && (xkeyid > NTP_MAXKEY)) {
+ keyid_t cookie;
+
+ /*
+@@ -3284,8 +3322,10 @@
+ if (mask == NULL) {
+ HTONL_FP(&xmt_tx, &xpkt.xmt);
+ }
++
+ authlen = authencrypt(xkeyid, (u_int32 *)&xpkt, sendlen);
+ sendlen += authlen;
++
+ #ifdef OPENSSL
+ if (xkeyid > NTP_MAXKEY)
+ authtrust(xkeyid, 0);
+Only in ntp-samba/ntpd: ntp_signd.c
+Only in ntp-dev-4.2.5p125/ntpdc: nl.pl
+Only in ntp-samba/scripts: calc_tickadj
+Only in ntp-samba/scripts: checktime
+Only in ntp-samba/scripts: freq_adj
+Only in ntp-samba/scripts: html2man
+Only in ntp-samba/scripts: Makefile
+Only in ntp-samba/scripts: mkver
+Only in ntp-samba/scripts: ntpsweep
+Only in ntp-samba/scripts: ntptrace
+Only in ntp-samba/scripts: ntpver
+Only in ntp-samba/scripts: ntp-wait
+Only in ntp-samba/scripts: plot_summary
+Only in ntp-samba/scripts: summary
+Only in ntp-samba: stamp-h1
+--- /dev/null 2008-08-25 07:28:22.036002925 +1000
++++ ntp-samba/ntpd/ntp_signd.c 2008-08-28 21:59:06.000000000 +1000
+@@ -0,0 +1,242 @@
++/* Copyright 2008, Red Hat, Inc.
++ Copyright 2008, Andrew Tridgell.
++ Licenced under the same terms as NTP itself.
++ */
++#ifdef HAVE_CONFIG_H
++#include <config.h>
++#endif
++
++#ifdef HAVE_NTP_SIGND
++
++#include "ntpd.h"
++#include "ntp_io.h"
++#include "ntp_stdlib.h"
++#include "ntp_unixtime.h"
++#include "ntp_control.h"
++#include "ntp_string.h"
++
++#include <stdio.h>
++#include <stddef.h>
++#ifdef HAVE_LIBSCF_H
++#include <libscf.h>
++#include <unistd.h>
++#endif /* HAVE_LIBSCF_H */
++
++#include <sys/un.h>
++
++/* socket routines by tridge - from junkcode.samba.org */
++
++/*
++ connect to a unix domain socket
++*/
++static int
++ux_socket_connect(const char *name)
++{
++ int fd;
++ struct sockaddr_un addr;
++ if (!name) {
++ return -1;
++ }
++
++ memset(&addr, 0, sizeof(addr));
++ addr.sun_family = AF_UNIX;
++ strncpy(addr.sun_path, name, sizeof(addr.sun_path));
++
++ fd = socket(AF_UNIX, SOCK_STREAM, 0);
++ if (fd == -1) {
++ return -1;
++ }
++
++ if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
++ close(fd);
++ return -1;
++ }
++
++ return fd;
++}
++
++
++/*
++ keep writing until its all sent
++*/
++static int
++write_all(int fd, const void *buf, size_t len)
++{
++ size_t total = 0;
++ while (len) {
++ int n = write(fd, buf, len);
++ if (n <= 0) return total;
++ buf = n + (char *)buf;
++ len -= n;
++ total += n;
++ }
++ return total;
++}
++
++/*
++ keep reading until its all read
++*/
++static int
++read_all(int fd, void *buf, size_t len)
++{
++ size_t total = 0;
++ while (len) {
++ int n = read(fd, buf, len);
++ if (n <= 0) return total;
++ buf = n + (char *)buf;
++ len -= n;
++ total += n;
++ }
++ return total;
++}
++
++/*
++ send a packet in length prefix format
++*/
++static int
++send_packet(int fd, const char *buf, uint32_t len)
++{
++ uint32_t net_len = htonl(len);
++ if (write_all(fd, &net_len, sizeof(net_len)) != sizeof(net_len)) return -1;
++ if (write_all(fd, buf, len) != len) return -1;
++ return 0;
++}
++
++/*
++ receive a packet in length prefix format
++*/
++static int
++recv_packet(int fd, char **buf, uint32_t *len)
++{
++ if (read_all(fd, len, sizeof(*len)) != sizeof(*len)) return -1;
++ *len = ntohl(*len);
++ (*buf) = malloc(*len);
++ if (!*buf) {
++ return -1;
++ }
++ if (read_all(fd, *buf, *len) != *len) {
++ free(*buf);
++ return -1;
++ }
++ return 0;
++}
++
++void
++send_via_ntp_signd(
++ struct recvbuf *rbufp, /* receive packet pointer */
++ int xmode,
++ keyid_t xkeyid,
++ int flags,
++ struct pkt *xpkt
++ )
++{
++
++ /* We are here because it was detected that the client
++ * sent an all-zero signature, and we therefore know
++ * it's windows trying to talk to an AD server
++ *
++ * Because we don't want to dive into Samba's secrets
++ * database just to find the long-term kerberos key
++ * that is re-used as the NTP key, we instead hand the
++ * packet over to Samba to sign, and return to us.
++ *
++ * The signing method Samba will use is described by
++ * Microsoft in MS-SNTP, found here:
++ * http://msdn.microsoft.com/en-us/library/cc212930.aspx
++ */
++
++ int fd, sendlen;
++ struct samba_key_in {
++ uint32_t version;
++ uint32_t op;
++ uint32_t packet_id;
++ uint32_t key_id_le;
++ struct pkt pkt;
++ } samba_pkt;
++
++ struct samba_key_out {
++ uint32_t version;
++ uint32_t op;
++ uint32_t packet_id;
++ struct pkt pkt;
++ } samba_reply;
++
++ char full_socket[256];
++
++ char *reply = NULL;
++ uint32_t reply_len;
++
++ memset(&samba_pkt, 0, sizeof(samba_pkt));
++ samba_pkt.op = 0; /* Sign message */
++ /* This will be echoed into the reply - a different
++ * impelementation might want multiple packets
++ * awaiting signing */
++
++ samba_pkt.packet_id = 1;
++
++ /* Swap the byte order back - it's actually little
++ * endian on the wire, but it was read above as
++ * network byte order */
++ samba_pkt.key_id_le = htonl(xkeyid);
++ samba_pkt.pkt = *xpkt;
++
++ snprintf(full_socket, sizeof(full_socket), "%s/socket", ntp_signd_socket);
++
++ fd = ux_socket_connect(full_socket);
++ /* Only continue with this if we can talk to Samba */
++ if (fd != -1) {
++ /* Send old packet to Samba, expect response */
++ /* Packet to Samba is quite simple:
++ All values BIG endian except key ID as noted
++ [packet size as BE] - 4 bytes
++ [protocol version (0)] - 4 bytes
++ [packet ID] - 4 bytes
++ [operation (sign message=0)] - 4 bytes
++ [key id] - LITTLE endian (as on wire) - 4 bytes
++ [message to sign] - as marshalled, without signature
++ */
++
++ if (send_packet(fd, (char *)&samba_pkt, offsetof(struct samba_key_in, pkt) + LEN_PKT_NOMAC) != 0) {
++ /* Huh? could not talk to Samba... */
++ close(fd);
++ return;
++ }
++
++ if (recv_packet(fd, &reply, &reply_len) != 0) {
++ if (reply) {
++ free(reply);
++ }
++ close(fd);
++ return;
++ }
++ /* Return packet is also simple:
++ [packet size] - network byte order - 4 bytes
++ [protocol version (0)] network byte order - - 4 bytes
++ [operation (signed success=3, failure=4)] network byte order - - 4 byte
++ (optional) [signed message] - as provided before, with signature appended
++ */
++
++ if (reply_len <= sizeof(samba_reply)) {
++ memcpy(&samba_reply, reply, reply_len);
++ if (ntohl(samba_reply.op) == 3 && reply_len > offsetof(struct samba_key_out, pkt)) {
++ sendlen = reply_len - offsetof(struct samba_key_out, pkt);
++ xpkt = &samba_reply.pkt;
++ sendpkt(&rbufp->recv_srcadr, rbufp->dstadr, 0, xpkt, sendlen);
++#ifdef DEBUG
++ if (debug)
++ printf(
++ "transmit ntp_signd packet: at %ld %s->%s mode %d keyid %08x len %d\n",
++ current_time, ntoa(&rbufp->dstadr->sin),
++ ntoa(&rbufp->recv_srcadr), xmode, xkeyid, sendlen);
++#endif
++ }
++ }
++
++ if (reply) {
++ free(reply);
++ }
++ close(fd);
++
++ }
++}
++#endif
diff --git a/source4/ntp_signd/ntp_signd.c b/source4/ntp_signd/ntp_signd.c
new file mode 100644
index 0000000000..4306e5a938
--- /dev/null
+++ b/source4/ntp_signd/ntp_signd.c
@@ -0,0 +1,397 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ NTP packet signing server
+
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "smbd/service_task.h"
+#include "smbd/service.h"
+#include "smbd/service_stream.h"
+#include "smbd/process_model.h"
+#include "lib/stream/packet.h"
+#include "librpc/gen_ndr/ndr_ntp_signd.h"
+#include "param/param.h"
+#include "dsdb/samdb/samdb.h"
+#include "auth/auth.h"
+#include "libcli/security/security.h"
+#include "lib/ldb/include/ldb.h"
+#include "lib/ldb/include/ldb_errors.h"
+#include "../lib/crypto/md5.h"
+#include "system/passwd.h"
+
+/*
+ top level context structure for the ntp_signd server
+*/
+struct ntp_signd_server {
+ struct task_server *task;
+ struct ldb_context *samdb;
+};
+
+/*
+ state of an open connection
+*/
+struct ntp_signd_connection {
+ /* stream connection we belong to */
+ struct stream_connection *conn;
+
+ /* the ntp_signd_server the connection belongs to */
+ struct ntp_signd_server *ntp_signd;
+
+ struct packet_context *packet;
+};
+
+static void ntp_signd_terminate_connection(struct ntp_signd_connection *ntp_signdconn, const char *reason)
+{
+ stream_terminate_connection(ntp_signdconn->conn, reason);
+}
+
+static NTSTATUS signing_failure(struct ntp_signd_connection *ntp_signdconn,
+ uint32_t packet_id)
+{
+ NTSTATUS status;
+ struct signed_reply signed_reply;
+ TALLOC_CTX *tmp_ctx = talloc_new(ntp_signdconn);
+ DATA_BLOB reply, blob;
+ enum ndr_err_code ndr_err;
+
+ NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
+
+ signed_reply.version = 1;
+ signed_reply.op = SIGNING_FAILURE;
+ signed_reply.packet_id = packet_id;
+ signed_reply.signed_packet = data_blob(NULL, 0);
+
+ ndr_err = ndr_push_struct_blob(&reply, tmp_ctx,
+ lp_iconv_convenience(ntp_signdconn->ntp_signd->task->lp_ctx),
+ &signed_reply,
+ (ndr_push_flags_fn_t)ndr_push_signed_reply);
+
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DEBUG(1,("failed to push ntp error reply\n"));
+ talloc_free(tmp_ctx);
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ blob = data_blob_talloc(ntp_signdconn, NULL, reply.length + 4);
+ if (!blob.data) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ RSIVAL(blob.data, 0, reply.length);
+ memcpy(blob.data + 4, reply.data, reply.length);
+
+ status = packet_send(ntp_signdconn->packet, blob);
+
+ /* the call isn't needed any more */
+ talloc_free(tmp_ctx);
+
+ return status;
+}
+
+/*
+ receive a full packet on a NTP_SIGND connection
+*/
+static NTSTATUS ntp_signd_recv(void *private_data, DATA_BLOB wrapped_input)
+{
+ struct ntp_signd_connection *ntp_signdconn = talloc_get_type(private_data,
+ struct ntp_signd_connection);
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ TALLOC_CTX *tmp_ctx = talloc_new(ntp_signdconn);
+ DATA_BLOB input, output, wrapped_output;
+ const struct dom_sid *domain_sid;
+ struct dom_sid *sid;
+ struct sign_request sign_request;
+ struct signed_reply signed_reply;
+ enum ndr_err_code ndr_err;
+ struct ldb_result *res;
+ const char *attrs[] = { "unicodePwd", "userAccountControl", "cn", NULL };
+ struct MD5Context ctx;
+ struct samr_Password *nt_hash;
+ uint32_t user_account_control;
+ int ret;
+
+ NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
+
+ talloc_steal(tmp_ctx, wrapped_input.data);
+
+ input = data_blob_const(wrapped_input.data + 4, wrapped_input.length - 4);
+
+ ndr_err = ndr_pull_struct_blob_all(&input, tmp_ctx,
+ lp_iconv_convenience(ntp_signdconn->ntp_signd->task->lp_ctx),
+ &sign_request,
+ (ndr_pull_flags_fn_t)ndr_pull_sign_request);
+
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DEBUG(1,("failed to parse ntp signing request\n"));
+ dump_data(1, input.data, input.length);
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ /* We need to implement 'check signature' and 'request server
+ * to sign' operations at some point */
+ if (sign_request.op != SIGN_TO_CLIENT) {
+ talloc_free(tmp_ctx);
+ return signing_failure(ntp_signdconn, sign_request.packet_id);
+ }
+
+ domain_sid = samdb_domain_sid(ntp_signdconn->ntp_signd->samdb);
+ if (!domain_sid) {
+ talloc_free(tmp_ctx);
+ return signing_failure(ntp_signdconn, sign_request.packet_id);
+ }
+
+ /* The top bit is a 'key selector' */
+ sid = dom_sid_add_rid(tmp_ctx, domain_sid, sign_request.key_id & 0x7FFFFFFF);
+ if (!sid) {
+ talloc_free(tmp_ctx);
+ return signing_failure(ntp_signdconn, sign_request.packet_id);
+ }
+
+ ret = ldb_search(ntp_signdconn->ntp_signd->samdb, tmp_ctx,
+ &res, samdb_base_dn(ntp_signdconn->ntp_signd->samdb),
+ LDB_SCOPE_SUBTREE, attrs, "(&(objectSid=%s)(objectClass=user))",
+ dom_sid_string(tmp_ctx, sid));
+ if (ret != LDB_SUCCESS) {
+ DEBUG(2, ("Failed to search for SID %s in SAM for NTP signing: %s\n", dom_sid_string(tmp_ctx, sid),
+ ldb_errstring(ntp_signdconn->ntp_signd->samdb)));
+ talloc_free(tmp_ctx);
+ return signing_failure(ntp_signdconn, sign_request.packet_id);
+ }
+
+ if (res->count == 0) {
+ DEBUG(5, ("Failed to find SID %s in SAM for NTP signing\n", dom_sid_string(tmp_ctx, sid)));
+ } else if (res->count != 1) {
+ DEBUG(1, ("Found SID %s %u times in SAM for NTP signing\n", dom_sid_string(tmp_ctx, sid), res->count));
+ talloc_free(tmp_ctx);
+ return signing_failure(ntp_signdconn, sign_request.packet_id);
+ }
+
+ user_account_control = ldb_msg_find_attr_as_uint(res->msgs[0], "userAccountControl", 0);
+
+ if (user_account_control & UF_ACCOUNTDISABLE) {
+ DEBUG(1, ("Account %s for SID [%s] is disabled\n", ldb_dn_get_linearized(res->msgs[0]->dn), dom_sid_string(tmp_ctx, sid)));
+ talloc_free(tmp_ctx);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (!(user_account_control & (UF_INTERDOMAIN_TRUST_ACCOUNT|UF_SERVER_TRUST_ACCOUNT|UF_WORKSTATION_TRUST_ACCOUNT))) {
+ DEBUG(1, ("Account %s for SID [%s] is not a trust account\n", ldb_dn_get_linearized(res->msgs[0]->dn), dom_sid_string(tmp_ctx, sid)));
+ talloc_free(tmp_ctx);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ nt_hash = samdb_result_hash(tmp_ctx, res->msgs[0], "unicodePwd");
+ if (!nt_hash) {
+ DEBUG(1, ("No unicodePwd found on record of SID %s for NTP signing\n", dom_sid_string(tmp_ctx, sid)));
+ talloc_free(tmp_ctx);
+ return signing_failure(ntp_signdconn, sign_request.packet_id);
+ }
+
+ /* Generate the reply packet */
+ signed_reply.version = 1;
+ signed_reply.packet_id = sign_request.packet_id;
+ signed_reply.op = SIGNING_SUCCESS;
+ signed_reply.signed_packet = data_blob_talloc(tmp_ctx,
+ NULL,
+ sign_request.packet_to_sign.length + 20);
+
+ if (!signed_reply.signed_packet.data) {
+ talloc_free(tmp_ctx);
+ return signing_failure(ntp_signdconn, sign_request.packet_id);
+ }
+
+ memcpy(signed_reply.signed_packet.data, sign_request.packet_to_sign.data, sign_request.packet_to_sign.length);
+ SIVAL(signed_reply.signed_packet.data, sign_request.packet_to_sign.length, sign_request.key_id);
+
+ /* Sign the NTP response with the unicodePwd */
+ MD5Init(&ctx);
+ MD5Update(&ctx, nt_hash->hash, sizeof(nt_hash->hash));
+ MD5Update(&ctx, sign_request.packet_to_sign.data, sign_request.packet_to_sign.length);
+ MD5Final(signed_reply.signed_packet.data + sign_request.packet_to_sign.length + 4, &ctx);
+
+
+ /* Place it into the packet for the wire */
+ ndr_err = ndr_push_struct_blob(&output, tmp_ctx,
+ lp_iconv_convenience(ntp_signdconn->ntp_signd->task->lp_ctx),
+ &signed_reply,
+ (ndr_push_flags_fn_t)ndr_push_signed_reply);
+
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DEBUG(1,("failed to push ntp error reply\n"));
+ talloc_free(tmp_ctx);
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ wrapped_output = data_blob_talloc(ntp_signdconn, NULL, output.length + 4);
+ if (!wrapped_output.data) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* The 'wire' transport for this is wrapped with a 4 byte network byte order length */
+ RSIVAL(wrapped_output.data, 0, output.length);
+ memcpy(wrapped_output.data + 4, output.data, output.length);
+
+ status = packet_send(ntp_signdconn->packet, wrapped_output);
+
+ /* the call isn't needed any more */
+ talloc_free(tmp_ctx);
+ return status;
+}
+
+/*
+ receive some data on a NTP_SIGND connection
+*/
+static void ntp_signd_recv_handler(struct stream_connection *conn, uint16_t flags)
+{
+ struct ntp_signd_connection *ntp_signdconn = talloc_get_type(conn->private_data,
+ struct ntp_signd_connection);
+ packet_recv(ntp_signdconn->packet);
+}
+
+/*
+ called on a tcp recv error
+*/
+static void ntp_signd_recv_error(void *private_data, NTSTATUS status)
+{
+ struct ntp_signd_connection *ntp_signdconn = talloc_get_type(private_data, struct ntp_signd_connection);
+ ntp_signd_terminate_connection(ntp_signdconn, nt_errstr(status));
+}
+
+/*
+ called when we can write to a connection
+*/
+static void ntp_signd_send(struct stream_connection *conn, uint16_t flags)
+{
+ struct ntp_signd_connection *ntp_signdconn = talloc_get_type(conn->private_data,
+ struct ntp_signd_connection);
+ packet_queue_run(ntp_signdconn->packet);
+}
+
+/*
+ called when we get a new connection
+*/
+static void ntp_signd_accept(struct stream_connection *conn)
+{
+ struct ntp_signd_server *ntp_signd = talloc_get_type(conn->private_data, struct ntp_signd_server);
+ struct ntp_signd_connection *ntp_signdconn;
+
+ ntp_signdconn = talloc_zero(conn, struct ntp_signd_connection);
+ if (!ntp_signdconn) {
+ stream_terminate_connection(conn, "ntp_signd_accept: out of memory");
+ return;
+ }
+ ntp_signdconn->conn = conn;
+ ntp_signdconn->ntp_signd = ntp_signd;
+ conn->private_data = ntp_signdconn;
+
+ ntp_signdconn->packet = packet_init(ntp_signdconn);
+ if (ntp_signdconn->packet == NULL) {
+ ntp_signd_terminate_connection(ntp_signdconn, "ntp_signd_accept: out of memory");
+ return;
+ }
+ packet_set_private(ntp_signdconn->packet, ntp_signdconn);
+ packet_set_socket(ntp_signdconn->packet, conn->socket);
+ packet_set_callback(ntp_signdconn->packet, ntp_signd_recv);
+ packet_set_full_request(ntp_signdconn->packet, packet_full_request_u32);
+ packet_set_error_handler(ntp_signdconn->packet, ntp_signd_recv_error);
+ packet_set_event_context(ntp_signdconn->packet, conn->event.ctx);
+ packet_set_fde(ntp_signdconn->packet, conn->event.fde);
+ packet_set_serialise(ntp_signdconn->packet);
+}
+
+static const struct stream_server_ops ntp_signd_stream_ops = {
+ .name = "ntp_signd",
+ .accept_connection = ntp_signd_accept,
+ .recv_handler = ntp_signd_recv_handler,
+ .send_handler = ntp_signd_send
+};
+
+/*
+ startup the ntp_signd task
+*/
+static void ntp_signd_task_init(struct task_server *task)
+{
+ struct ntp_signd_server *ntp_signd;
+ NTSTATUS status;
+
+ const struct model_ops *model_ops;
+
+ const char *address;
+
+ if (!directory_create_or_exist(lp_ntp_signd_socket_directory(task->lp_ctx), geteuid(), 0755)) {
+ char *error = talloc_asprintf(task, "Cannot create NTP signd pipe directory: %s",
+ lp_ntp_signd_socket_directory(task->lp_ctx));
+ task_server_terminate(task,
+ error);
+ return;
+ }
+
+ /* within the ntp_signd task we want to be a single process, so
+ ask for the single process model ops and pass these to the
+ stream_setup_socket() call. */
+ model_ops = process_model_startup(task->event_ctx, "single");
+ if (!model_ops) {
+ DEBUG(0,("Can't find 'single' process model_ops\n"));
+ return;
+ }
+
+ task_server_set_title(task, "task[ntp_signd]");
+
+ ntp_signd = talloc(task, struct ntp_signd_server);
+ if (ntp_signd == NULL) {
+ task_server_terminate(task, "ntp_signd: out of memory");
+ return;
+ }
+
+ ntp_signd->task = task;
+
+ /* Must be system to get at the password hashes */
+ ntp_signd->samdb = samdb_connect(ntp_signd, task->event_ctx, task->lp_ctx, system_session(ntp_signd, task->lp_ctx));
+ if (ntp_signd->samdb == NULL) {
+ task_server_terminate(task, "ntp_signd failed to open samdb");
+ return;
+ }
+
+ address = talloc_asprintf(ntp_signd, "%s/socket", lp_ntp_signd_socket_directory(task->lp_ctx));
+
+ status = stream_setup_socket(ntp_signd->task->event_ctx,
+ ntp_signd->task->lp_ctx,
+ model_ops,
+ &ntp_signd_stream_ops,
+ "unix", address, NULL,
+ lp_socket_options(ntp_signd->task->lp_ctx),
+ ntp_signd);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Failed to bind to %s - %s\n",
+ address, nt_errstr(status)));
+ return;
+ }
+
+}
+
+
+/* called at smbd startup - register ourselves as a server service */
+NTSTATUS server_service_ntp_signd_init(void)
+{
+ return register_server_service("ntp_signd", ntp_signd_task_init);
+}
diff --git a/source4/ntptr/config.mk b/source4/ntptr/config.mk
new file mode 100644
index 0000000000..71b3bc05a8
--- /dev/null
+++ b/source4/ntptr/config.mk
@@ -0,0 +1,27 @@
+# ntptr server subsystem
+
+################################################
+# Start MODULE ntptr_simple_ldb
+[MODULE::ntptr_simple_ldb]
+INIT_FUNCTION = ntptr_simple_ldb_init
+SUBSYSTEM = ntptr
+PRIVATE_DEPENDENCIES = \
+ LIBLDB NDR_SPOOLSS DCERPC_COMMON
+# End MODULE ntptr_simple_ldb
+################################################
+
+ntptr_simple_ldb_OBJ_FILES = $(ntptrsrcdir)/simple_ldb/ntptr_simple_ldb.o
+
+################################################
+# Start SUBSYSTEM ntptr
+[SUBSYSTEM::ntptr]
+PUBLIC_DEPENDENCIES = DCERPC_COMMON
+#
+# End SUBSYSTEM ntptr
+################################################
+
+ntptr_OBJ_FILES = \
+ $(ntptrsrcdir)/ntptr_base.o \
+ $(ntptrsrcdir)/ntptr_interface.o
+
+$(eval $(call proto_header_template,$(ntptrsrcdir)/ntptr_proto.h,$(ntptr_OBJ_FILES:.o=.c)))
diff --git a/source4/ntptr/ntptr.h b/source4/ntptr/ntptr.h
new file mode 100644
index 0000000000..91817a202f
--- /dev/null
+++ b/source4/ntptr/ntptr.h
@@ -0,0 +1,240 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ NTPTR structures and defines
+
+ Copyright (C) Stefan (metze) 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 <http://www.gnu.org/licenses/>.
+*/
+
+/* modules can use the following to determine if the interface has changed */
+#define NTPTR_INTERFACE_VERSION 0
+
+struct ntptr_context;
+
+enum ntptr_HandleType {
+ NTPTR_HANDLE_SERVER,
+ NTPTR_HANDLE_PRINTER,
+ NTPTR_HANDLE_PORT,
+ NTPTR_HANDLE_MONITOR
+};
+
+struct ntptr_GenericHandle {
+ enum ntptr_HandleType type;
+ struct ntptr_context *ntptr;
+ const char *object_name;
+ uint32_t access_mask;
+ void *private_data;
+};
+
+struct spoolss_OpenPrinterEx;
+struct spoolss_EnumPrinterData;
+struct spoolss_DeletePrinterData;
+struct spoolss_AddForm;
+struct spoolss_GetForm;
+struct spoolss_SetForm;
+struct spoolss_DeleteForm;
+struct spoolss_AddPrinterDriver;
+struct spoolss_DeletePrinterDriver;
+struct spoolss_GetPrinterDriverDirectory;
+struct spoolss_AddPrinter;
+struct spoolss_GetPrinter;
+struct spoolss_SetPrinter;
+struct spoolss_DeletePrinter;
+struct spoolss_GetPrinterDriver;
+struct spoolss_AddJob;
+struct spoolss_EnumJobs;
+struct spoolss_SetJob;
+struct spoolss_GetJob;
+struct spoolss_ScheduleJob;
+struct spoolss_ReadPrinter;
+struct spoolss_WritePrinter;
+struct spoolss_StartDocPrinter;
+struct spoolss_EndDocPrinter;
+struct spoolss_StartPagePrinter;
+struct spoolss_EndPagePrinter;
+struct spoolss_GetPrinterData;
+struct spoolss_SetPrinterData;
+struct spoolss_EnumPrinterDrivers;
+struct spoolss_EnumMonitors;
+struct spoolss_EnumPrinters;
+struct spoolss_EnumForms;
+struct spoolss_EnumPorts;
+struct spoolss_EnumPrintProcessors;
+struct spoolss_XcvData;
+struct spoolss_GetPrintProcessorDirectory;
+
+/* the ntptr operations structure - contains function pointers to
+ the backend implementations of each operation */
+struct ntptr_ops {
+ const char *name;
+
+ /* initial setup */
+ NTSTATUS (*init_context)(struct ntptr_context *ntptr);
+
+ /* PrintServer functions */
+ WERROR (*OpenPrintServer)(struct ntptr_context *ntptr, TALLOC_CTX *mem_ctx,
+ struct spoolss_OpenPrinterEx *r,
+ const char *printer_name,
+ struct ntptr_GenericHandle **server);
+ WERROR (*XcvDataPrintServer)(struct ntptr_GenericHandle *server, TALLOC_CTX *mem_ctx,
+ struct spoolss_XcvData *r);
+
+ /* PrintServer PrinterData functions */
+ WERROR (*EnumPrintServerData)(struct ntptr_GenericHandle *server, TALLOC_CTX *mem_ctx,
+ struct spoolss_EnumPrinterData *r);
+ WERROR (*GetPrintServerData)(struct ntptr_GenericHandle *server, TALLOC_CTX *mem_ctx,
+ struct spoolss_GetPrinterData *r);
+ WERROR (*SetPrintServerData)(struct ntptr_GenericHandle *server, TALLOC_CTX *mem_ctx,
+ struct spoolss_SetPrinterData *r);
+ WERROR (*DeletePrintServerData)(struct ntptr_GenericHandle *server, TALLOC_CTX *mem_ctx,
+ struct spoolss_DeletePrinterData *r);
+
+ /* PrintServer Form functions */
+ WERROR (*EnumPrintServerForms)(struct ntptr_GenericHandle *server, TALLOC_CTX *mem_ctx,
+ struct spoolss_EnumForms *r);
+ WERROR (*AddPrintServerForm)(struct ntptr_GenericHandle *server, TALLOC_CTX *mem_ctx,
+ struct spoolss_AddForm *r);
+ WERROR (*SetPrintServerForm)(struct ntptr_GenericHandle *server, TALLOC_CTX *mem_ctx,
+ struct spoolss_SetForm *r);
+ WERROR (*DeletePrintServerForm)(struct ntptr_GenericHandle *server, TALLOC_CTX *mem_ctx,
+ struct spoolss_DeleteForm *r);
+
+ /* PrintServer Driver functions */
+ WERROR (*EnumPrinterDrivers)(struct ntptr_context *ntptr, TALLOC_CTX *mem_ctx,
+ struct spoolss_EnumPrinterDrivers *r);
+ WERROR (*AddPrinterDriver)(struct ntptr_context *ntptr, TALLOC_CTX *mem_ctx,
+ struct spoolss_AddPrinterDriver *r);
+ WERROR (*DeletePrinterDriver)(struct ntptr_context *ntptr, TALLOC_CTX *mem_ctx,
+ struct spoolss_DeletePrinterDriver *r);
+ WERROR (*GetPrinterDriverDirectory)(struct ntptr_context *ntptr, TALLOC_CTX *mem_ctx,
+ struct spoolss_GetPrinterDriverDirectory *r);
+
+ /* Port functions */
+ WERROR (*EnumPorts)(struct ntptr_context *ntptr, TALLOC_CTX *mem_ctx,
+ struct spoolss_EnumPorts *r);
+ WERROR (*OpenPort)(struct ntptr_context *ntptr, TALLOC_CTX *mem_ctx,
+ struct spoolss_OpenPrinterEx *r,
+ const char *port_name,
+ struct ntptr_GenericHandle **port);
+ WERROR (*XcvDataPort)(struct ntptr_GenericHandle *port, TALLOC_CTX *mem_ctx,
+ struct spoolss_XcvData *r);
+
+ /* Monitor functions */
+ WERROR (*EnumMonitors)(struct ntptr_context *ntptr, TALLOC_CTX *mem_ctx,
+ struct spoolss_EnumMonitors *r);
+ WERROR (*OpenMonitor)(struct ntptr_context *ntptr, TALLOC_CTX *mem_ctx,
+ struct spoolss_OpenPrinterEx *r,
+ const char *monitor_name,
+ struct ntptr_GenericHandle **monitor);
+ WERROR (*XcvDataMonitor)(struct ntptr_GenericHandle *monitor, TALLOC_CTX *mem_ctx,
+ struct spoolss_XcvData *r);
+
+ /* PrintProcessor functions */
+ WERROR (*EnumPrintProcessors)(struct ntptr_context *ntptr, TALLOC_CTX *mem_ctx,
+ struct spoolss_EnumPrintProcessors *r);
+ WERROR (*GetPrintProcessorDirectory)(struct ntptr_context *ntptr, TALLOC_CTX *mem_ctx,
+ struct spoolss_GetPrintProcessorDirectory *r);
+
+ /* Printer functions */
+ WERROR (*EnumPrinters)(struct ntptr_context *ntptr, TALLOC_CTX *mem_ctx,
+ struct spoolss_EnumPrinters *r);
+ WERROR (*OpenPrinter)(struct ntptr_context *ntptr, TALLOC_CTX *mem_ctx,
+ struct spoolss_OpenPrinterEx *r,
+ const char *printer_name,
+ struct ntptr_GenericHandle **printer);
+ WERROR (*AddPrinter)(struct ntptr_context *ntptr, TALLOC_CTX *mem_ctx,
+ struct spoolss_AddPrinter *r,
+ struct ntptr_GenericHandle **printer);
+ WERROR (*GetPrinter)(struct ntptr_context *ntptr, TALLOC_CTX *mem_ctx,
+ struct spoolss_GetPrinter *r);
+ WERROR (*SetPrinter)(struct ntptr_context *ntptr, TALLOC_CTX *mem_ctx,
+ struct spoolss_SetPrinter *r);
+ WERROR (*DeletePrinter)(struct ntptr_context *ntptr, TALLOC_CTX *mem_ctx,
+ struct spoolss_DeletePrinter *r);
+ WERROR (*XcvDataPrinter)(struct ntptr_GenericHandle *printer, TALLOC_CTX *mem_ctx,
+ struct spoolss_XcvData *r);
+
+ /* Printer Driver functions */
+ WERROR (*GetPrinterDriver)(struct ntptr_context *ntptr, TALLOC_CTX *mem_ctx,
+ struct spoolss_GetPrinterDriver *r);
+
+ /* Printer PrinterData functions */
+ WERROR (*EnumPrinterData)(struct ntptr_GenericHandle *printer, TALLOC_CTX *mem_ctx,
+ struct spoolss_EnumPrinterData *r);
+ WERROR (*GetPrinterData)(struct ntptr_GenericHandle *printer, TALLOC_CTX *mem_ctx,
+ struct spoolss_GetPrinterData *r);
+ WERROR (*SetPrinterData)(struct ntptr_GenericHandle *printer, TALLOC_CTX *mem_ctx,
+ struct spoolss_SetPrinterData *r);
+ WERROR (*DeletePrinterData)(struct ntptr_GenericHandle *printer, TALLOC_CTX *mem_ctx,
+ struct spoolss_DeletePrinterData *r);
+
+ /* Printer Form functions */
+ WERROR (*EnumPrinterForms)(struct ntptr_GenericHandle *printer, TALLOC_CTX *mem_ctx,
+ struct spoolss_EnumForms *r);
+ WERROR (*AddPrinterForm)(struct ntptr_GenericHandle *printer, TALLOC_CTX *mem_ctx,
+ struct spoolss_AddForm *r);
+ WERROR (*GetPrinterForm)(struct ntptr_GenericHandle *printer, TALLOC_CTX *mem_ctx,
+ struct spoolss_GetForm *r);
+ WERROR (*SetPrinterForm)(struct ntptr_GenericHandle *printer, TALLOC_CTX *mem_ctx,
+ struct spoolss_SetForm *r);
+ WERROR (*DeletePrinterForm)(struct ntptr_GenericHandle *printer, TALLOC_CTX *mem_ctx,
+ struct spoolss_DeleteForm *r);
+
+ /* Printer Job functions */
+ WERROR (*EnumJobs)(struct ntptr_GenericHandle *printer, TALLOC_CTX *mem_ctx,
+ struct spoolss_EnumJobs *r);
+ WERROR (*AddJob)(struct ntptr_GenericHandle *printer, TALLOC_CTX *mem_ctx,
+ struct spoolss_AddJob *r);
+ WERROR (*ScheduleJob)(struct ntptr_GenericHandle *printer, TALLOC_CTX *mem_ctx,
+ struct spoolss_ScheduleJob *r);
+ WERROR (*GetJob)(struct ntptr_GenericHandle *printer, TALLOC_CTX *mem_ctx,
+ struct spoolss_GetJob *r);
+ WERROR (*SetJob)(struct ntptr_GenericHandle *printer, TALLOC_CTX *mem_ctx,
+ struct spoolss_SetJob *r);
+
+ /* Printer Printing functions */
+ WERROR (*StartDocPrinter)(struct ntptr_GenericHandle *printer, TALLOC_CTX *mem_ctx,
+ struct spoolss_StartDocPrinter *r);
+ WERROR (*EndDocPrinter)(struct ntptr_GenericHandle *printer, TALLOC_CTX *mem_ctx,
+ struct spoolss_EndDocPrinter *r);
+ WERROR (*StartPagePrinter)(struct ntptr_GenericHandle *printer, TALLOC_CTX *mem_ctx,
+ struct spoolss_StartPagePrinter *r);
+ WERROR (*EndPagePrinter)(struct ntptr_GenericHandle *printer, TALLOC_CTX *mem_ctx,
+ struct spoolss_EndPagePrinter *r);
+ WERROR (*WritePrinter)(struct ntptr_GenericHandle *printer, TALLOC_CTX *mem_ctx,
+ struct spoolss_WritePrinter *r);
+ WERROR (*ReadPrinter)(struct ntptr_GenericHandle *printer, TALLOC_CTX *mem_ctx,
+ struct spoolss_ReadPrinter *r);
+};
+
+struct ntptr_context {
+ const struct ntptr_ops *ops;
+ void *private_data;
+ struct tevent_context *ev_ctx;
+ struct loadparm_context *lp_ctx;
+};
+
+/* this structure is used by backends to determine the size of some critical types */
+struct ntptr_critical_sizes {
+ int interface_version;
+ int sizeof_ntptr_critical_sizes;
+ int sizeof_ntptr_context;
+ int sizeof_ntptr_ops;
+};
+
+struct loadparm_context;
+
+#include "ntptr/ntptr_proto.h"
diff --git a/source4/ntptr/ntptr_base.c b/source4/ntptr/ntptr_base.c
new file mode 100644
index 0000000000..287a4b6777
--- /dev/null
+++ b/source4/ntptr/ntptr_base.c
@@ -0,0 +1,151 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ NTPTR base code
+
+ Copyright (C) Stefan (metze) 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 <http://www.gnu.org/licenses/>.
+*/
+/*
+ this implements the core code for all NTPTR modules. Backends register themselves here.
+*/
+
+#include "includes.h"
+#include "ntptr/ntptr.h"
+#include "param/param.h"
+
+/* the list of currently registered NTPTR backends */
+static struct ntptr_backend {
+ const struct ntptr_ops *ops;
+} *backends = NULL;
+static int num_backends;
+
+/*
+ register a NTPTR backend.
+
+ The 'name' can be later used by other backends to find the operations
+ structure for this backend.
+*/
+NTSTATUS ntptr_register(const void *_ops)
+{
+ const struct ntptr_ops *ops = _ops;
+ struct ntptr_ops *new_ops;
+
+ if (ntptr_backend_byname(ops->name) != NULL) {
+ /* its already registered! */
+ DEBUG(0,("NTPTR backend '%s' already registered\n",
+ ops->name));
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ backends = realloc_p(backends, struct ntptr_backend, num_backends+1);
+ if (!backends) {
+ smb_panic("out of memory in ntptr_register");
+ }
+
+ new_ops = smb_xmemdup(ops, sizeof(*ops));
+ new_ops->name = smb_xstrdup(ops->name);
+
+ backends[num_backends].ops = new_ops;
+
+ num_backends++;
+
+ DEBUG(3,("NTPTR backend '%s'\n",
+ ops->name));
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS ntptr_init(struct loadparm_context *lp_ctx)
+{
+ extern NTSTATUS ntptr_simple_ldb_init(void);
+ init_module_fn static_init[] = { STATIC_ntptr_MODULES };
+ init_module_fn *shared_init = load_samba_modules(NULL, lp_ctx, "ntptr");
+
+ run_init_functions(static_init);
+ run_init_functions(shared_init);
+
+ talloc_free(shared_init);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ return the operations structure for a named backend
+*/
+const struct ntptr_ops *ntptr_backend_byname(const char *name)
+{
+ int i;
+
+ for (i=0;i<num_backends;i++) {
+ if (strcmp(backends[i].ops->name, name) == 0) {
+ return backends[i].ops;
+ }
+ }
+
+ return NULL;
+}
+
+
+/*
+ return the NTPTR interface version, and the size of some critical types
+ This can be used by backends to either detect compilation errors, or provide
+ multiple implementations for different smbd compilation options in one module
+*/
+static const struct ntptr_critical_sizes critical_sizes = {
+ .interface_version = NTPTR_INTERFACE_VERSION,
+ .sizeof_ntptr_critical_sizes = sizeof(struct ntptr_critical_sizes),
+ .sizeof_ntptr_context = sizeof(struct ntptr_context),
+ .sizeof_ntptr_ops = sizeof(struct ntptr_ops),
+};
+const struct ntptr_critical_sizes *ntptr_interface_version(void)
+{
+ return &critical_sizes;
+}
+
+/*
+ create a ntptr_context with a specified NTPTR backend
+*/
+NTSTATUS ntptr_init_context(TALLOC_CTX *mem_ctx, struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx,
+ const char *providor, struct ntptr_context **_ntptr)
+{
+ NTSTATUS status;
+ struct ntptr_context *ntptr;
+
+ if (!providor) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ ntptr = talloc(mem_ctx, struct ntptr_context);
+ NT_STATUS_HAVE_NO_MEMORY(ntptr);
+ ntptr->private_data = NULL;
+ ntptr->ops = ntptr_backend_byname(providor);
+ ntptr->ev_ctx = ev_ctx;
+ ntptr->lp_ctx = lp_ctx;
+
+ if (!ntptr->ops) {
+ DEBUG(1,("ntptr_init_context: failed to find NTPTR providor='%s'\n",
+ providor));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ status = ntptr->ops->init_context(ntptr);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ *_ntptr = ntptr;
+ return NT_STATUS_OK;
+}
diff --git a/source4/ntptr/ntptr_interface.c b/source4/ntptr/ntptr_interface.c
new file mode 100644
index 0000000000..b65a2e32cd
--- /dev/null
+++ b/source4/ntptr/ntptr_interface.c
@@ -0,0 +1,604 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ NTPTR interface functions
+
+ Copyright (C) Stefan (metze) 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "ntptr/ntptr.h"
+
+
+/* PrintServer functions */
+WERROR ntptr_OpenPrintServer(struct ntptr_context *ntptr, TALLOC_CTX *mem_ctx,
+ struct spoolss_OpenPrinterEx *r,
+ const char *printer_name,
+ struct ntptr_GenericHandle **server)
+{
+ if (!ntptr->ops->OpenPrintServer) {
+ return WERR_NOT_SUPPORTED;
+ }
+ return ntptr->ops->OpenPrintServer(ntptr, mem_ctx, r, printer_name, server);
+}
+
+WERROR ntptr_XcvDataPrintServer(struct ntptr_GenericHandle *server, TALLOC_CTX *mem_ctx,
+ struct spoolss_XcvData *r)
+{
+ if (server->type != NTPTR_HANDLE_SERVER) {
+ return WERR_FOOBAR;
+ }
+ if (!server->ntptr->ops->XcvDataPrintServer) {
+ return WERR_NOT_SUPPORTED;
+ }
+ return server->ntptr->ops->XcvDataPrintServer(server, mem_ctx, r);
+}
+
+
+/* PrintServer PrinterData functions */
+WERROR ntptr_EnumPrintServerData(struct ntptr_GenericHandle *server, TALLOC_CTX *mem_ctx,
+ struct spoolss_EnumPrinterData *r)
+{
+ if (server->type != NTPTR_HANDLE_SERVER) {
+ return WERR_FOOBAR;
+ }
+ if (!server->ntptr->ops->EnumPrintServerData) {
+ return WERR_NOT_SUPPORTED;
+ }
+ return server->ntptr->ops->EnumPrintServerData(server, mem_ctx, r);
+}
+
+WERROR ntptr_GetPrintServerData(struct ntptr_GenericHandle *server, TALLOC_CTX *mem_ctx,
+ struct spoolss_GetPrinterData *r)
+{
+ if (server->type != NTPTR_HANDLE_SERVER) {
+ return WERR_FOOBAR;
+ }
+ if (!server->ntptr->ops->GetPrintServerData) {
+ return WERR_NOT_SUPPORTED;
+ }
+ return server->ntptr->ops->GetPrintServerData(server, mem_ctx, r);
+}
+
+WERROR ntptr_SetPrintServerData(struct ntptr_GenericHandle *server, TALLOC_CTX *mem_ctx,
+ struct spoolss_SetPrinterData *r)
+{
+ if (server->type != NTPTR_HANDLE_SERVER) {
+ return WERR_FOOBAR;
+ }
+ if (!server->ntptr->ops->SetPrintServerData) {
+ return WERR_NOT_SUPPORTED;
+ }
+ return server->ntptr->ops->SetPrintServerData(server, mem_ctx, r);
+}
+
+WERROR ntptr_DeletePrintServerData(struct ntptr_GenericHandle *server, TALLOC_CTX *mem_ctx,
+ struct spoolss_DeletePrinterData *r)
+{
+ if (server->type != NTPTR_HANDLE_SERVER) {
+ return WERR_FOOBAR;
+ }
+ if (!server->ntptr->ops->DeletePrintServerData) {
+ return WERR_NOT_SUPPORTED;
+ }
+ return server->ntptr->ops->DeletePrintServerData(server, mem_ctx, r);
+}
+
+
+/* PrintServer Form functions */
+WERROR ntptr_EnumPrintServerForms(struct ntptr_GenericHandle *server, TALLOC_CTX *mem_ctx,
+ struct spoolss_EnumForms *r)
+{
+ if (server->type != NTPTR_HANDLE_SERVER) {
+ return WERR_FOOBAR;
+ }
+ if (!server->ntptr->ops->EnumPrintServerForms) {
+ return WERR_NOT_SUPPORTED;
+ }
+ return server->ntptr->ops->EnumPrintServerForms(server, mem_ctx, r);
+}
+
+WERROR ntptr_AddPrintServerForm(struct ntptr_GenericHandle *server, TALLOC_CTX *mem_ctx,
+ struct spoolss_AddForm *r)
+{
+ if (server->type != NTPTR_HANDLE_SERVER) {
+ return WERR_FOOBAR;
+ }
+ if (!server->ntptr->ops->AddPrintServerForm) {
+ return WERR_NOT_SUPPORTED;
+ }
+ return server->ntptr->ops->AddPrintServerForm(server, mem_ctx, r);
+}
+
+WERROR ntptr_SetPrintServerForm(struct ntptr_GenericHandle *server, TALLOC_CTX *mem_ctx,
+ struct spoolss_SetForm *r)
+{
+ if (server->type != NTPTR_HANDLE_SERVER) {
+ return WERR_FOOBAR;
+ }
+ if (!server->ntptr->ops->SetPrintServerForm) {
+ return WERR_NOT_SUPPORTED;
+ }
+ return server->ntptr->ops->SetPrintServerForm(server, mem_ctx, r);
+}
+
+WERROR ntptr_DeletePrintServerForm(struct ntptr_GenericHandle *server, TALLOC_CTX *mem_ctx,
+ struct spoolss_DeleteForm *r)
+{
+ if (server->type != NTPTR_HANDLE_SERVER) {
+ return WERR_FOOBAR;
+ }
+ if (!server->ntptr->ops->DeletePrintServerForm) {
+ return WERR_NOT_SUPPORTED;
+ }
+ return server->ntptr->ops->DeletePrintServerForm(server, mem_ctx, r);
+}
+
+
+/* PrintServer Driver functions */
+WERROR ntptr_EnumPrinterDrivers(struct ntptr_context *ntptr, TALLOC_CTX *mem_ctx,
+ struct spoolss_EnumPrinterDrivers *r)
+{
+ if (!ntptr->ops->EnumPrinterDrivers) {
+ return WERR_NOT_SUPPORTED;
+ }
+ return ntptr->ops->EnumPrinterDrivers(ntptr, mem_ctx, r);
+}
+
+WERROR ntptr_AddPrinterDriver(struct ntptr_context *ntptr, TALLOC_CTX *mem_ctx,
+ struct spoolss_AddPrinterDriver *r)
+{
+ if (!ntptr->ops->AddPrinterDriver) {
+ return WERR_NOT_SUPPORTED;
+ }
+ return ntptr->ops->AddPrinterDriver(ntptr, mem_ctx, r);
+}
+
+WERROR ntptr_DeletePrinterDriver(struct ntptr_context *ntptr, TALLOC_CTX *mem_ctx,
+ struct spoolss_DeletePrinterDriver *r)
+{
+ if (!ntptr->ops->DeletePrinterDriver) {
+ return WERR_NOT_SUPPORTED;
+ }
+ return ntptr->ops->DeletePrinterDriver(ntptr, mem_ctx, r);
+}
+
+WERROR ntptr_GetPrinterDriverDirectory(struct ntptr_context *ntptr, TALLOC_CTX *mem_ctx,
+ struct spoolss_GetPrinterDriverDirectory *r)
+{
+ if (!ntptr->ops->GetPrinterDriverDirectory) {
+ return WERR_NOT_SUPPORTED;
+ }
+ return ntptr->ops->GetPrinterDriverDirectory(ntptr, mem_ctx, r);
+}
+
+
+/* Port functions */
+WERROR ntptr_EnumPorts(struct ntptr_context *ntptr, TALLOC_CTX *mem_ctx,
+ struct spoolss_EnumPorts *r)
+{
+ if (!ntptr->ops->EnumPorts) {
+ return WERR_NOT_SUPPORTED;
+ }
+ return ntptr->ops->EnumPorts(ntptr, mem_ctx, r);
+}
+
+WERROR ntptr_OpenPort(struct ntptr_context *ntptr, TALLOC_CTX *mem_ctx,
+ struct spoolss_OpenPrinterEx *r,
+ const char *port_name,
+ struct ntptr_GenericHandle **port)
+{
+ if (!ntptr->ops->OpenPort) {
+ return WERR_NOT_SUPPORTED;
+ }
+ return ntptr->ops->OpenPort(ntptr, mem_ctx, r, port_name, port);
+}
+
+WERROR ntptr_XcvDataPort(struct ntptr_GenericHandle *port, TALLOC_CTX *mem_ctx,
+ struct spoolss_XcvData *r)
+{
+ if (port->type != NTPTR_HANDLE_PORT) {
+ return WERR_FOOBAR;
+ }
+ if (!port->ntptr->ops->XcvDataPort) {
+ return WERR_NOT_SUPPORTED;
+ }
+ return port->ntptr->ops->XcvDataPort(port, mem_ctx, r);
+}
+
+/* Monitor functions */
+WERROR ntptr_EnumMonitors(struct ntptr_context *ntptr, TALLOC_CTX *mem_ctx,
+ struct spoolss_EnumMonitors *r)
+{
+ if (!ntptr->ops->EnumMonitors) {
+ return WERR_NOT_SUPPORTED;
+ }
+ return ntptr->ops->EnumMonitors(ntptr, mem_ctx, r);
+}
+
+WERROR ntptr_OpenMonitor(struct ntptr_context *ntptr, TALLOC_CTX *mem_ctx,
+ struct spoolss_OpenPrinterEx *r,
+ const char *monitor_name,
+ struct ntptr_GenericHandle **monitor)
+{
+ if (!ntptr->ops->OpenMonitor) {
+ return WERR_NOT_SUPPORTED;
+ }
+ return ntptr->ops->OpenMonitor(ntptr, mem_ctx, r, monitor_name, monitor);
+}
+
+WERROR ntptr_XcvDataMonitor(struct ntptr_GenericHandle *monitor, TALLOC_CTX *mem_ctx,
+ struct spoolss_XcvData *r)
+{
+ if (monitor->type != NTPTR_HANDLE_MONITOR) {
+ return WERR_FOOBAR;
+ }
+ if (!monitor->ntptr->ops->XcvDataMonitor) {
+ return WERR_NOT_SUPPORTED;
+ }
+ return monitor->ntptr->ops->XcvDataMonitor(monitor, mem_ctx, r);
+}
+
+
+/* PrintProcessor functions */
+WERROR ntptr_EnumPrintProcessors(struct ntptr_context *ntptr, TALLOC_CTX *mem_ctx,
+ struct spoolss_EnumPrintProcessors *r)
+{
+ if (!ntptr->ops->EnumPrintProcessors) {
+ return WERR_NOT_SUPPORTED;
+ }
+ return ntptr->ops->EnumPrintProcessors(ntptr, mem_ctx, r);
+}
+
+WERROR ntptr_GetPrintProcessorDirectory(struct ntptr_context *ntptr, TALLOC_CTX *mem_ctx,
+ struct spoolss_GetPrintProcessorDirectory *r)
+{
+ if (!ntptr->ops->GetPrintProcessorDirectory) {
+ return WERR_NOT_SUPPORTED;
+ }
+ return ntptr->ops->GetPrintProcessorDirectory(ntptr, mem_ctx, r);
+}
+
+
+/* Printer functions */
+WERROR ntptr_EnumPrinters(struct ntptr_context *ntptr, TALLOC_CTX *mem_ctx,
+ struct spoolss_EnumPrinters *r)
+{
+ if (!ntptr->ops->EnumPrinters) {
+ return WERR_NOT_SUPPORTED;
+ }
+ return ntptr->ops->EnumPrinters(ntptr, mem_ctx, r);
+}
+
+WERROR ntptr_OpenPrinter(struct ntptr_context *ntptr, TALLOC_CTX *mem_ctx,
+ struct spoolss_OpenPrinterEx *r,
+ const char *printer_name,
+ struct ntptr_GenericHandle **printer)
+{
+ if (!ntptr->ops->OpenPrinter) {
+ return WERR_NOT_SUPPORTED;
+ }
+ return ntptr->ops->OpenPrinter(ntptr, mem_ctx, r, printer_name, printer);
+}
+
+WERROR ntptr_AddPrinter(struct ntptr_context *ntptr, TALLOC_CTX *mem_ctx,
+ struct spoolss_AddPrinter *r,
+ struct ntptr_GenericHandle **printer)
+{
+ if (!ntptr->ops->AddPrinter) {
+ return WERR_NOT_SUPPORTED;
+ }
+ return ntptr->ops->AddPrinter(ntptr, mem_ctx, r, printer);
+}
+
+WERROR ntptr_GetPrinter(struct ntptr_context *ntptr, TALLOC_CTX *mem_ctx,
+ struct spoolss_GetPrinter *r)
+{
+ if (!ntptr->ops->GetPrinter) {
+ return WERR_NOT_SUPPORTED;
+ }
+ return ntptr->ops->GetPrinter(ntptr, mem_ctx, r);
+}
+
+WERROR ntptr_SetPrinter(struct ntptr_context *ntptr, TALLOC_CTX *mem_ctx,
+ struct spoolss_SetPrinter *r)
+{
+ if (!ntptr->ops->SetPrinter) {
+ return WERR_NOT_SUPPORTED;
+ }
+ return ntptr->ops->SetPrinter(ntptr, mem_ctx, r);
+}
+
+WERROR ntptr_DeletePrinter(struct ntptr_context *ntptr, TALLOC_CTX *mem_ctx,
+ struct spoolss_DeletePrinter *r)
+{
+ if (!ntptr->ops->DeletePrinter) {
+ return WERR_NOT_SUPPORTED;
+ }
+ return ntptr->ops->DeletePrinter(ntptr, mem_ctx, r);
+}
+
+WERROR ntptr_XcvDataPrinter(struct ntptr_GenericHandle *printer, TALLOC_CTX *mem_ctx,
+ struct spoolss_XcvData *r)
+{
+ if (printer->type != NTPTR_HANDLE_PRINTER) {
+ return WERR_FOOBAR;
+ }
+ if (!printer->ntptr->ops->XcvDataPrinter) {
+ return WERR_NOT_SUPPORTED;
+ }
+ return printer->ntptr->ops->XcvDataPrinter(printer, mem_ctx, r);
+}
+
+
+/* Printer Driver functions */
+WERROR ntptr_GetPrinterDriver(struct ntptr_context *ntptr, TALLOC_CTX *mem_ctx,
+ struct spoolss_GetPrinterDriver *r)
+{
+ if (!ntptr->ops->GetPrinterDriver) {
+ return WERR_NOT_SUPPORTED;
+ }
+ return ntptr->ops->GetPrinterDriver(ntptr, mem_ctx, r);
+}
+
+
+/* Printer PrinterData functions */
+WERROR ntptr_EnumPrinterData(struct ntptr_GenericHandle *printer, TALLOC_CTX *mem_ctx,
+ struct spoolss_EnumPrinterData *r)
+{
+ if (printer->type != NTPTR_HANDLE_PRINTER) {
+ return WERR_FOOBAR;
+ }
+ if (!printer->ntptr->ops->EnumPrinterData) {
+ return WERR_NOT_SUPPORTED;
+ }
+ return printer->ntptr->ops->EnumPrinterData(printer, mem_ctx, r);
+}
+
+WERROR ntptr_GetPrinterData(struct ntptr_GenericHandle *printer, TALLOC_CTX *mem_ctx,
+ struct spoolss_GetPrinterData *r)
+{
+ if (printer->type != NTPTR_HANDLE_PRINTER) {
+ return WERR_FOOBAR;
+ }
+ if (!printer->ntptr->ops->GetPrinterData) {
+ return WERR_NOT_SUPPORTED;
+ }
+ return printer->ntptr->ops->GetPrinterData(printer, mem_ctx, r);
+}
+
+WERROR ntptr_SetPrinterData(struct ntptr_GenericHandle *printer, TALLOC_CTX *mem_ctx,
+ struct spoolss_SetPrinterData *r)
+{
+ if (printer->type != NTPTR_HANDLE_PRINTER) {
+ return WERR_FOOBAR;
+ }
+ if (!printer->ntptr->ops->SetPrinterData) {
+ return WERR_NOT_SUPPORTED;
+ }
+ return printer->ntptr->ops->SetPrinterData(printer, mem_ctx, r);
+}
+
+WERROR ntptr_DeletePrinterData(struct ntptr_GenericHandle *printer, TALLOC_CTX *mem_ctx,
+ struct spoolss_DeletePrinterData *r)
+{
+ if (printer->type != NTPTR_HANDLE_PRINTER) {
+ return WERR_FOOBAR;
+ }
+ if (!printer->ntptr->ops->DeletePrinterData) {
+ return WERR_NOT_SUPPORTED;
+ }
+ return printer->ntptr->ops->DeletePrinterData(printer, mem_ctx, r);
+}
+
+
+/* Printer Form functions */
+WERROR ntptr_EnumPrinterForms(struct ntptr_GenericHandle *printer, TALLOC_CTX *mem_ctx,
+ struct spoolss_EnumForms *r)
+{
+ if (printer->type != NTPTR_HANDLE_PRINTER) {
+ return WERR_FOOBAR;
+ }
+ if (!printer->ntptr->ops->EnumPrinterForms) {
+ return WERR_NOT_SUPPORTED;
+ }
+ return printer->ntptr->ops->EnumPrinterForms(printer, mem_ctx, r);
+}
+
+WERROR ntptr_AddPrinterForm(struct ntptr_GenericHandle *printer, TALLOC_CTX *mem_ctx,
+ struct spoolss_AddForm *r)
+{
+ if (printer->type != NTPTR_HANDLE_PRINTER) {
+ return WERR_FOOBAR;
+ }
+ if (!printer->ntptr->ops->AddPrinterForm) {
+ return WERR_NOT_SUPPORTED;
+ }
+ return printer->ntptr->ops->AddPrinterForm(printer, mem_ctx, r);
+}
+
+WERROR ntptr_GetPrinterForm(struct ntptr_GenericHandle *printer, TALLOC_CTX *mem_ctx,
+ struct spoolss_GetForm *r)
+{
+ if (printer->type != NTPTR_HANDLE_PRINTER) {
+ return WERR_FOOBAR;
+ }
+ if (!printer->ntptr->ops->GetPrinterForm) {
+ return WERR_NOT_SUPPORTED;
+ }
+ return printer->ntptr->ops->GetPrinterForm(printer, mem_ctx, r);
+}
+
+WERROR ntptr_SetPrinterForm(struct ntptr_GenericHandle *printer, TALLOC_CTX *mem_ctx,
+ struct spoolss_SetForm *r)
+{
+ if (printer->type != NTPTR_HANDLE_PRINTER) {
+ return WERR_FOOBAR;
+ }
+ if (!printer->ntptr->ops->SetPrinterForm) {
+ return WERR_NOT_SUPPORTED;
+ }
+ return printer->ntptr->ops->SetPrinterForm(printer, mem_ctx, r);
+}
+
+WERROR ntptr_DeletePrinterForm(struct ntptr_GenericHandle *printer, TALLOC_CTX *mem_ctx,
+ struct spoolss_DeleteForm *r)
+{
+ if (printer->type != NTPTR_HANDLE_PRINTER) {
+ return WERR_FOOBAR;
+ }
+ if (!printer->ntptr->ops->DeletePrinterForm) {
+ return WERR_NOT_SUPPORTED;
+ }
+ return printer->ntptr->ops->DeletePrinterForm(printer, mem_ctx, r);
+}
+
+
+/* Printer Job functions */
+WERROR ntptr_EnumJobs(struct ntptr_GenericHandle *printer, TALLOC_CTX *mem_ctx,
+ struct spoolss_EnumJobs *r)
+{
+ if (printer->type != NTPTR_HANDLE_PRINTER) {
+ return WERR_FOOBAR;
+ }
+ if (!printer->ntptr->ops->EnumJobs) {
+ return WERR_NOT_SUPPORTED;
+ }
+ return printer->ntptr->ops->EnumJobs(printer, mem_ctx, r);
+}
+
+WERROR ntptr_AddJob(struct ntptr_GenericHandle *printer, TALLOC_CTX *mem_ctx,
+ struct spoolss_AddJob *r)
+{
+ if (printer->type != NTPTR_HANDLE_PRINTER) {
+ return WERR_FOOBAR;
+ }
+ if (!printer->ntptr->ops->AddJob) {
+ return WERR_NOT_SUPPORTED;
+ }
+ return printer->ntptr->ops->AddJob(printer, mem_ctx, r);
+}
+
+WERROR ntptr_ScheduleJob(struct ntptr_GenericHandle *printer, TALLOC_CTX *mem_ctx,
+ struct spoolss_ScheduleJob *r)
+{
+ if (printer->type != NTPTR_HANDLE_PRINTER) {
+ return WERR_FOOBAR;
+ }
+ if (!printer->ntptr->ops->ScheduleJob) {
+ return WERR_NOT_SUPPORTED;
+ }
+ return printer->ntptr->ops->ScheduleJob(printer, mem_ctx, r);
+}
+
+WERROR ntptr_GetJob(struct ntptr_GenericHandle *printer, TALLOC_CTX *mem_ctx,
+ struct spoolss_GetJob *r)
+{
+ if (printer->type != NTPTR_HANDLE_PRINTER) {
+ return WERR_FOOBAR;
+ }
+ if (!printer->ntptr->ops->GetJob) {
+ return WERR_NOT_SUPPORTED;
+ }
+ return printer->ntptr->ops->GetJob(printer, mem_ctx, r);
+}
+
+WERROR ntptr_SetJob(struct ntptr_GenericHandle *printer, TALLOC_CTX *mem_ctx,
+ struct spoolss_SetJob *r)
+{
+ if (printer->type != NTPTR_HANDLE_PRINTER) {
+ return WERR_FOOBAR;
+ }
+ if (!printer->ntptr->ops->SetJob) {
+ return WERR_NOT_SUPPORTED;
+ }
+ return printer->ntptr->ops->SetJob(printer, mem_ctx, r);
+}
+
+
+/* Printer Printing functions */
+WERROR ntptr_StartDocPrinter(struct ntptr_GenericHandle *printer, TALLOC_CTX *mem_ctx,
+ struct spoolss_StartDocPrinter *r)
+{
+ if (printer->type != NTPTR_HANDLE_PRINTER) {
+ return WERR_FOOBAR;
+ }
+ if (!printer->ntptr->ops->StartDocPrinter) {
+ return WERR_NOT_SUPPORTED;
+ }
+ return printer->ntptr->ops->StartDocPrinter(printer, mem_ctx, r);
+}
+
+WERROR ntptr_EndDocPrinter(struct ntptr_GenericHandle *printer, TALLOC_CTX *mem_ctx,
+ struct spoolss_EndDocPrinter *r)
+{
+ if (printer->type != NTPTR_HANDLE_PRINTER) {
+ return WERR_FOOBAR;
+ }
+ if (!printer->ntptr->ops->EndDocPrinter) {
+ return WERR_NOT_SUPPORTED;
+ }
+ return printer->ntptr->ops->EndDocPrinter(printer, mem_ctx, r);
+}
+
+WERROR ntptr_StartPagePrinter(struct ntptr_GenericHandle *printer, TALLOC_CTX *mem_ctx,
+ struct spoolss_StartPagePrinter *r)
+{
+ if (printer->type != NTPTR_HANDLE_PRINTER) {
+ return WERR_FOOBAR;
+ }
+ if (!printer->ntptr->ops->StartPagePrinter) {
+ return WERR_NOT_SUPPORTED;
+ }
+ return printer->ntptr->ops->StartPagePrinter(printer, mem_ctx, r);
+}
+
+WERROR ntptr_EndPagePrinter(struct ntptr_GenericHandle *printer, TALLOC_CTX *mem_ctx,
+ struct spoolss_EndPagePrinter *r)
+{
+ if (printer->type != NTPTR_HANDLE_PRINTER) {
+ return WERR_FOOBAR;
+ }
+ if (!printer->ntptr->ops->EndPagePrinter) {
+ return WERR_NOT_SUPPORTED;
+ }
+ return printer->ntptr->ops->EndPagePrinter(printer, mem_ctx, r);
+}
+
+WERROR ntptr_WritePrinter(struct ntptr_GenericHandle *printer, TALLOC_CTX *mem_ctx,
+ struct spoolss_WritePrinter *r)
+{
+ if (printer->type != NTPTR_HANDLE_PRINTER) {
+ return WERR_FOOBAR;
+ }
+ if (!printer->ntptr->ops->WritePrinter) {
+ return WERR_NOT_SUPPORTED;
+ }
+ return printer->ntptr->ops->WritePrinter(printer, mem_ctx, r);
+}
+
+WERROR ntptr_ReadPrinter(struct ntptr_GenericHandle *printer, TALLOC_CTX *mem_ctx,
+ struct spoolss_ReadPrinter *r)
+{
+ if (printer->type != NTPTR_HANDLE_PRINTER) {
+ return WERR_FOOBAR;
+ }
+ if (!printer->ntptr->ops->ReadPrinter) {
+ return WERR_NOT_SUPPORTED;
+ }
+ return printer->ntptr->ops->ReadPrinter(printer, mem_ctx, r);
+}
+
diff --git a/source4/ntptr/simple_ldb/ntptr_simple_ldb.c b/source4/ntptr/simple_ldb/ntptr_simple_ldb.c
new file mode 100644
index 0000000000..4ebbaaeffc
--- /dev/null
+++ b/source4/ntptr/simple_ldb/ntptr_simple_ldb.c
@@ -0,0 +1,891 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Simple LDB NTPTR backend
+
+ Copyright (C) Stefan (metze) 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 <http://www.gnu.org/licenses/>.
+*/
+/*
+ This implements a NTPTR backend that store
+ all objects (Printers, Ports, Monitors, PrinterDrivers ...)
+ in a ldb database, but doesn't do real printing.
+
+ This is just used for testing how some of
+ the SPOOLSS protocol details should work
+*/
+
+#include "includes.h"
+#include "ntptr/ntptr.h"
+#include "librpc/gen_ndr/ndr_spoolss.h"
+#include "lib/ldb/include/ldb.h"
+#include "auth/auth.h"
+#include "dsdb/samdb/samdb.h"
+#include "ldb_wrap.h"
+#include "../lib/util/util_ldb.h"
+#include "rpc_server/common/common.h"
+#include "param/param.h"
+
+/*
+ connect to the SPOOLSS database
+ return a ldb_context pointer on success, or NULL on failure
+ */
+static struct ldb_context *sptr_db_connect(TALLOC_CTX *mem_ctx, struct tevent_context *ev_ctx, struct loadparm_context *lp_ctx)
+{
+ return ldb_wrap_connect(mem_ctx, ev_ctx, lp_ctx, lp_spoolss_url(lp_ctx), system_session(mem_ctx, lp_ctx),
+ NULL, 0, NULL);
+}
+
+static int sptr_db_search(struct ldb_context *ldb,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_dn *basedn,
+ struct ldb_message ***res,
+ const char * const *attrs,
+ const char *format, ...) PRINTF_ATTRIBUTE(6,7);
+
+static int sptr_db_search(struct ldb_context *ldb,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_dn *basedn,
+ struct ldb_message ***res,
+ const char * const *attrs,
+ const char *format, ...)
+{
+ va_list ap;
+ int count;
+
+ va_start(ap, format);
+ count = gendb_search_v(ldb, mem_ctx, basedn, res, attrs, format, ap);
+ va_end(ap);
+
+ return count;
+}
+
+#define SET_STRING(ldb, mod, attr, value) do { \
+ if (value == NULL) return WERR_INVALID_PARAM; \
+ if (samdb_msg_add_string(ldb, (TALLOC_CTX *)mod, mod, attr, value) != 0) { \
+ return WERR_NOMEM; \
+ } \
+} while (0)
+
+#define SET_UINT(ldb, mod, attr, value) do { \
+ if (samdb_msg_add_uint(ldb, (TALLOC_CTX *)mod, mod, attr, value) != 0) { \
+ return WERR_NOMEM; \
+ } \
+} while (0)
+
+static NTSTATUS sptr_init_context(struct ntptr_context *ntptr)
+{
+ struct ldb_context *sptr_db = sptr_db_connect(ntptr, ntptr->ev_ctx, ntptr->lp_ctx);
+ NT_STATUS_HAVE_NO_MEMORY(sptr_db);
+
+ ntptr->private_data = sptr_db;
+
+ return NT_STATUS_OK;
+}
+
+/* PrintServer functions */
+static WERROR sptr_OpenPrintServer(struct ntptr_context *ntptr, TALLOC_CTX *mem_ctx,
+ struct spoolss_OpenPrinterEx *r,
+ const char *server_name,
+ struct ntptr_GenericHandle **_server)
+{
+ struct ntptr_GenericHandle *server;
+
+ /* TODO: do access check here! */
+
+ server = talloc(mem_ctx, struct ntptr_GenericHandle);
+ W_ERROR_HAVE_NO_MEMORY(server);
+
+ server->type = NTPTR_HANDLE_SERVER;
+ server->ntptr = ntptr;
+ server->object_name = talloc_strdup(server, server_name);
+ W_ERROR_HAVE_NO_MEMORY(server->object_name);
+ server->access_mask = 0;
+ server->private_data = NULL;
+
+ *_server = server;
+ return WERR_OK;
+}
+
+/*
+ * PrintServer PrinterData functions
+ */
+static WERROR sptr_GetPrintServerData(struct ntptr_GenericHandle *server, TALLOC_CTX *mem_ctx,
+ struct spoolss_GetPrinterData *r)
+{
+ struct dcerpc_server_info *server_info = lp_dcerpc_server_info(mem_ctx, server->ntptr->lp_ctx);
+ if (strcmp("W3SvcInstalled", r->in.value_name) == 0) {
+ *r->out.type = REG_DWORD;
+ r->out.data->value = 0;
+ return WERR_OK;
+ } else if (strcmp("BeepEnabled", r->in.value_name) == 0) {
+ *r->out.type = REG_DWORD;
+ r->out.data->value = 0;
+ return WERR_OK;
+ } else if (strcmp("EventLog", r->in.value_name) == 0) {
+ *r->out.type = REG_DWORD;
+ r->out.data->value = 0;
+ return WERR_OK;
+ } else if (strcmp("NetPopup", r->in.value_name) == 0) {
+ *r->out.type = REG_DWORD;
+ r->out.data->value = 0;
+ return WERR_OK;
+ } else if (strcmp("NetPopupToComputer", r->in.value_name) == 0) {
+ *r->out.type = REG_DWORD;
+ r->out.data->value = 0;
+ return WERR_OK;
+ } else if (strcmp("MajorVersion", r->in.value_name) == 0) {
+ *r->out.type = REG_DWORD;
+ r->out.data->value = 3;
+ return WERR_OK;
+ } else if (strcmp("MinorVersion", r->in.value_name) == 0) {
+ *r->out.type = REG_DWORD;
+ r->out.data->value = 0;
+ return WERR_OK;
+ } else if (strcmp("DefaultSpoolDirectory", r->in.value_name) == 0) {
+ *r->out.type = REG_SZ;
+ r->out.data->string = "C:\\PRINTERS";
+ return WERR_OK;
+ } else if (strcmp("Architecture", r->in.value_name) == 0) {
+ *r->out.type = REG_SZ;
+ r->out.data->string = SPOOLSS_ARCHITECTURE_NT_X86;
+ return WERR_OK;
+ } else if (strcmp("DsPresent", r->in.value_name) == 0) {
+ *r->out.type = REG_DWORD;
+ r->out.data->value = 1;
+ return WERR_OK;
+ } else if (strcmp("OSVersion", r->in.value_name) == 0) {
+ DATA_BLOB blob;
+ enum ndr_err_code ndr_err;
+ struct spoolss_OSVersion os;
+
+ os.major = server_info->version_major;
+ os.minor = server_info->version_minor;
+ os.build = server_info->version_build;
+ os.extra_string = "";
+
+ ndr_err = ndr_push_struct_blob(&blob, mem_ctx, lp_iconv_convenience(server->ntptr->lp_ctx), &os, (ndr_push_flags_fn_t)ndr_push_spoolss_OSVersion);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return WERR_GENERAL_FAILURE;
+ }
+
+ *r->out.type = REG_BINARY;
+ r->out.data->binary = blob;
+ return WERR_OK;
+ } else if (strcmp("OSVersionEx", r->in.value_name) == 0) {
+ DATA_BLOB blob;
+ enum ndr_err_code ndr_err;
+ struct spoolss_OSVersionEx os_ex;
+
+ os_ex.major = server_info->version_major;
+ os_ex.minor = server_info->version_minor;
+ os_ex.build = server_info->version_build;
+ os_ex.extra_string = "";
+ os_ex.unknown2 = 0;
+ os_ex.unknown3 = 0;
+
+ ndr_err = ndr_push_struct_blob(&blob, mem_ctx, lp_iconv_convenience(server->ntptr->lp_ctx), &os_ex, (ndr_push_flags_fn_t)ndr_push_spoolss_OSVersionEx);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return WERR_GENERAL_FAILURE;
+ }
+
+ *r->out.type = REG_BINARY;
+ r->out.data->binary = blob;
+ return WERR_OK;
+ } else if (strcmp("DNSMachineName", r->in.value_name) == 0) {
+ if (!lp_realm(server->ntptr->lp_ctx)) return WERR_INVALID_PARAM;
+
+ *r->out.type = REG_SZ;
+ r->out.data->string = talloc_asprintf(mem_ctx, "%s.%s",
+ lp_netbios_name(server->ntptr->lp_ctx),
+ lp_realm(server->ntptr->lp_ctx));
+ W_ERROR_HAVE_NO_MEMORY(r->out.data->string);
+ return WERR_OK;
+ }
+
+ return WERR_INVALID_PARAM;
+}
+
+/* PrintServer Form functions */
+static WERROR sptr_EnumPrintServerForms(struct ntptr_GenericHandle *server, TALLOC_CTX *mem_ctx,
+ struct spoolss_EnumForms *r)
+{
+ struct ldb_context *sptr_db = talloc_get_type(server->ntptr->private_data, struct ldb_context);
+ struct ldb_message **msgs;
+ int count;
+ int i;
+ union spoolss_FormInfo *info;
+
+ count = sptr_db_search(sptr_db, mem_ctx,
+ ldb_dn_new(mem_ctx, sptr_db, "CN=Forms,CN=PrintServer"),
+ &msgs, NULL, "(&(objectClass=form))");
+
+ if (count == 0) return WERR_OK;
+ if (count < 0) return WERR_GENERAL_FAILURE;
+
+ info = talloc_array(mem_ctx, union spoolss_FormInfo, count);
+ W_ERROR_HAVE_NO_MEMORY(info);
+
+ switch (r->in.level) {
+ case 1:
+ for (i=0; i < count; i++) {
+ info[i].info1.flags = samdb_result_uint(msgs[i], "flags", SPOOLSS_FORM_BUILTIN);
+
+ info[i].info1.form_name = samdb_result_string(msgs[i], "form-name", NULL);
+ W_ERROR_HAVE_NO_MEMORY(info[i].info1.form_name);
+
+ info[i].info1.size.width = samdb_result_uint(msgs[i], "size-width", 0);
+ info[i].info1.size.height = samdb_result_uint(msgs[i], "size-height", 0);
+
+ info[i].info1.area.left = samdb_result_uint(msgs[i], "area-left", 0);
+ info[i].info1.area.top = samdb_result_uint(msgs[i], "area-top", 0);
+ info[i].info1.area.right = samdb_result_uint(msgs[i], "area-right", 0);
+ info[i].info1.area.bottom = samdb_result_uint(msgs[i], "area-bottom", 0);
+ }
+ break;
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ *r->out.info = info;
+ *r->out.count = count;
+ return WERR_OK;
+}
+
+static WERROR sptr_AddPrintServerForm(struct ntptr_GenericHandle *server, TALLOC_CTX *mem_ctx,
+ struct spoolss_AddForm *r)
+{
+ struct ldb_context *sptr_db = talloc_get_type(server->ntptr->private_data, struct ldb_context);
+ struct ldb_message *msg,**msgs;
+ const char * const attrs[] = {"flags", NULL };
+ int count, ret;
+
+ /* TODO: do checks access here
+ * if (!(server->access_mask & desired_access)) {
+ * return WERR_FOOBAR;
+ * }
+ */
+
+ switch (r->in.level) {
+ case 1:
+ if (!r->in.info.info1) {
+ return WERR_FOOBAR;
+ }
+ count = sptr_db_search(sptr_db, mem_ctx,
+ ldb_dn_new(mem_ctx, sptr_db, "CN=Forms,CN=PrintServer"),
+ &msgs, attrs, "(&(form-name=%s)(objectClass=form))",
+ r->in.info.info1->form_name);
+
+ if (count == 1) return WERR_FOOBAR;
+ if (count > 1) return WERR_FOOBAR;
+ if (count < 0) return WERR_GENERAL_FAILURE;
+
+ if (r->in.info.info1->flags != SPOOLSS_FORM_USER) {
+ return WERR_FOOBAR;
+ }
+
+ msg = ldb_msg_new(mem_ctx);
+ W_ERROR_HAVE_NO_MEMORY(msg);
+
+ /* add core elements to the ldb_message for the Form */
+ msg->dn = ldb_dn_new_fmt(msg, sptr_db, "form-name=%s,CN=Forms,CN=PrintServer", r->in.info.info1->form_name);
+ SET_STRING(sptr_db, msg, "objectClass", "form");
+
+ SET_UINT(sptr_db, msg, "flags", r->in.info.info1->flags);
+
+ SET_STRING(sptr_db, msg, "form-name", r->in.info.info1->form_name);
+
+ SET_UINT(sptr_db, msg, "size-width", r->in.info.info1->size.width);
+ SET_UINT(sptr_db, msg, "size-height", r->in.info.info1->size.height);
+
+ SET_UINT(sptr_db, msg, "area-left", r->in.info.info1->area.left);
+ SET_UINT(sptr_db, msg, "area-top", r->in.info.info1->area.top);
+ SET_UINT(sptr_db, msg, "area-right", r->in.info.info1->area.right);
+ SET_UINT(sptr_db, msg, "area-bottom", r->in.info.info1->area.bottom);
+ break;
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ ret = ldb_add(sptr_db, msg);
+ if (ret != 0) {
+ return WERR_FOOBAR;
+ }
+
+ return WERR_OK;
+}
+
+static WERROR sptr_SetPrintServerForm(struct ntptr_GenericHandle *server, TALLOC_CTX *mem_ctx,
+ struct spoolss_SetForm *r)
+{
+ struct ldb_context *sptr_db = talloc_get_type(server->ntptr->private_data, struct ldb_context);
+ struct ldb_message *msg,**msgs;
+ const char * const attrs[] = { "flags", NULL};
+ int count, ret;
+ enum spoolss_FormFlags flags;
+
+ /* TODO: do checks access here
+ * if (!(server->access_mask & desired_access)) {
+ * return WERR_FOOBAR;
+ * }
+ */
+
+ switch (r->in.level) {
+ case 1:
+ if (!r->in.info.info1) {
+ return WERR_FOOBAR;
+ }
+
+ count = sptr_db_search(sptr_db, mem_ctx,
+ ldb_dn_new(mem_ctx, sptr_db, "CN=Forms,CN=PrintServer"),
+ &msgs, attrs, "(&(form-name=%s)(objectClass=form))",
+ r->in.info.info1->form_name);
+
+ if (count == 0) return WERR_FOOBAR;
+ if (count > 1) return WERR_FOOBAR;
+ if (count < 0) return WERR_GENERAL_FAILURE;
+
+ flags = samdb_result_uint(msgs[0], "flags", SPOOLSS_FORM_BUILTIN);
+ if (flags != SPOOLSS_FORM_USER) {
+ return WERR_FOOBAR;
+ }
+
+ msg = ldb_msg_new(mem_ctx);
+ W_ERROR_HAVE_NO_MEMORY(msg);
+
+ /* add core elements to the ldb_message for the user */
+ msg->dn = msgs[0]->dn;
+
+ SET_UINT(sptr_db, msg, "flags", r->in.info.info1->flags);
+
+ SET_STRING(sptr_db, msg, "form-name", r->in.info.info1->form_name);
+
+ SET_UINT(sptr_db, msg, "size-width", r->in.info.info1->size.width);
+ SET_UINT(sptr_db, msg, "size-height", r->in.info.info1->size.height);
+
+ SET_UINT(sptr_db, msg, "area-left", r->in.info.info1->area.left);
+ SET_UINT(sptr_db, msg, "area-top", r->in.info.info1->area.top);
+ SET_UINT(sptr_db, msg, "area-right", r->in.info.info1->area.right);
+ SET_UINT(sptr_db, msg, "area-bottom", r->in.info.info1->area.bottom);
+ break;
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ ret = samdb_replace(sptr_db, mem_ctx, msg);
+ if (ret != 0) {
+ return WERR_FOOBAR;
+ }
+
+ return WERR_OK;
+}
+
+static WERROR sptr_DeletePrintServerForm(struct ntptr_GenericHandle *server, TALLOC_CTX *mem_ctx,
+ struct spoolss_DeleteForm *r)
+{
+ struct ldb_context *sptr_db = talloc_get_type(server->ntptr->private_data, struct ldb_context);
+ struct ldb_message **msgs;
+ const char * const attrs[] = { "flags", NULL};
+ int count, ret;
+ enum spoolss_FormFlags flags;
+
+ /* TODO: do checks access here
+ * if (!(server->access_mask & desired_access)) {
+ * return WERR_FOOBAR;
+ * }
+ */
+
+ if (!r->in.form_name) {
+ return WERR_FOOBAR;
+ }
+
+ count = sptr_db_search(sptr_db, mem_ctx,
+ ldb_dn_new(mem_ctx, sptr_db, "CN=Forms,CN=PrintServer"),
+ &msgs, attrs, "(&(form-name=%s)(objectclass=form))",
+ r->in.form_name);
+
+ if (count == 0) return WERR_FOOBAR;
+ if (count > 1) return WERR_FOOBAR;
+ if (count < 0) return WERR_GENERAL_FAILURE;
+
+ flags = samdb_result_uint(msgs[0], "flags", SPOOLSS_FORM_BUILTIN);
+ if (flags != SPOOLSS_FORM_USER) {
+ return WERR_FOOBAR;
+ }
+
+ ret = ldb_delete(sptr_db, msgs[0]->dn);
+ if (ret != 0) {
+ return WERR_FOOBAR;
+ }
+
+ return WERR_OK;
+}
+
+/* PrintServer Driver functions */
+static WERROR sptr_EnumPrinterDrivers(struct ntptr_context *ntptr, TALLOC_CTX *mem_ctx,
+ struct spoolss_EnumPrinterDrivers *r)
+{
+ return WERR_OK;
+}
+
+static WERROR sptr_GetPrinterDriverDirectory(struct ntptr_context *ntptr, TALLOC_CTX *mem_ctx,
+ struct spoolss_GetPrinterDriverDirectory *r)
+{
+ union spoolss_DriverDirectoryInfo *info;
+ const char *prefix;
+ const char *postfix;
+
+ /*
+ * NOTE: normally r->in.level is 1, but both w2k3 and nt4 sp6a
+ * are ignoring the r->in.level completely, so we do :-)
+ */
+
+ /*
+ * TODO: check the server name is ours
+ * - if it's a invalid UNC then return WERR_INVALID_NAME
+ * - if it's the wrong host name return WERR_INVALID_PARAM
+ * - if it's "" then we need to return a local WINDOWS path
+ */
+ if (!r->in.server || !r->in.server[0]) {
+ prefix = "C:\\DRIVERS";
+ } else {
+ prefix = talloc_asprintf(mem_ctx, "%s\\print$", r->in.server);
+ W_ERROR_HAVE_NO_MEMORY(prefix);
+ }
+
+ if (r->in.environment && strcmp(SPOOLSS_ARCHITECTURE_NT_X86, r->in.environment) == 0) {
+ postfix = "W32X86";
+ } else {
+ return WERR_INVALID_ENVIRONMENT;
+ }
+
+ info = talloc(mem_ctx, union spoolss_DriverDirectoryInfo);
+ W_ERROR_HAVE_NO_MEMORY(info);
+
+ info->info1.directory_name = talloc_asprintf(mem_ctx, "%s\\%s", prefix, postfix);
+ W_ERROR_HAVE_NO_MEMORY(info->info1.directory_name);
+
+ r->out.info = info;
+ return WERR_OK;
+}
+
+/* Printer functions */
+static WERROR sptr_EnumPrinters(struct ntptr_context *ntptr, TALLOC_CTX *mem_ctx,
+ struct spoolss_EnumPrinters *r)
+{
+ struct ldb_context *sptr_db = talloc_get_type(ntptr->private_data, struct ldb_context);
+ struct ldb_message **msgs;
+ int count;
+ int i;
+ union spoolss_PrinterInfo *info;
+
+ count = sptr_db_search(sptr_db, mem_ctx, NULL, &msgs, NULL,
+ "(&(objectclass=printer))");
+
+ if (count == 0) return WERR_OK;
+ if (count < 0) return WERR_GENERAL_FAILURE;
+
+ info = talloc_array(mem_ctx, union spoolss_PrinterInfo, count);
+ W_ERROR_HAVE_NO_MEMORY(info);
+
+ switch(r->in.level) {
+ case 1:
+ for (i = 0; i < count; i++) {
+ info[i].info1.flags = samdb_result_uint(msgs[i], "flags", 0);
+
+ info[i].info1.name = samdb_result_string(msgs[i], "name", "");
+ W_ERROR_HAVE_NO_MEMORY(info[i].info1.name);
+
+ info[i].info1.description = samdb_result_string(msgs[i], "description", "");
+ W_ERROR_HAVE_NO_MEMORY(info[i].info1.description);
+
+ info[i].info1.comment = samdb_result_string(msgs[i], "comment", NULL);
+ }
+ break;
+ case 2:
+ for (i = 0; i < count; i++) {
+ info[i].info2.servername = samdb_result_string(msgs[i], "servername", "");
+ W_ERROR_HAVE_NO_MEMORY(info[i].info2.servername);
+
+ info[i].info2.printername = samdb_result_string(msgs[i], "printername", "");
+ W_ERROR_HAVE_NO_MEMORY(info[i].info2.printername);
+
+ info[i].info2.sharename = samdb_result_string(msgs[i], "sharename", "");
+ W_ERROR_HAVE_NO_MEMORY(info[i].info2.sharename);
+
+ info[i].info2.portname = samdb_result_string(msgs[i], "portname", "");
+ W_ERROR_HAVE_NO_MEMORY(info[i].info2.portname);
+
+ info[i].info2.drivername = samdb_result_string(msgs[i], "drivername", "");
+ W_ERROR_HAVE_NO_MEMORY(info[i].info2.drivername);
+
+ info[i].info2.comment = samdb_result_string(msgs[i], "comment", NULL);
+
+ info[i].info2.location = samdb_result_string(msgs[i], "location", NULL);
+
+ info[i].info2.devmode = NULL;
+
+ info[i].info2.sepfile = samdb_result_string(msgs[i], "sepfile", NULL);
+
+ info[i].info2.printprocessor = samdb_result_string(msgs[i], "printprocessor", "");
+ W_ERROR_HAVE_NO_MEMORY(info[i].info2.printprocessor);
+
+ info[i].info2.datatype = samdb_result_string(msgs[i], "datatype", "");
+ W_ERROR_HAVE_NO_MEMORY(info[i].info2.datatype);
+
+ info[i].info2.parameters = samdb_result_string(msgs[i], "parameters", NULL);
+
+ info[i].info2.secdesc = NULL;
+
+ info[i].info2.attributes = samdb_result_uint(msgs[i], "attributes", 0);
+ info[i].info2.priority = samdb_result_uint(msgs[i], "priority", 0);
+ info[i].info2.defaultpriority = samdb_result_uint(msgs[i], "defaultpriority", 0);
+ info[i].info2.starttime = samdb_result_uint(msgs[i], "starttime", 0);
+ info[i].info2.untiltime = samdb_result_uint(msgs[i], "untiltime", 0);
+ info[i].info2.status = samdb_result_uint(msgs[i], "status", 0);
+ info[i].info2.cjobs = samdb_result_uint(msgs[i], "cjobs", 0);
+ info[i].info2.averageppm = samdb_result_uint(msgs[i], "averageppm", 0);
+ }
+ break;
+ case 4:
+ for (i = 0; i < count; i++) {
+ info[i].info4.printername = samdb_result_string(msgs[i], "printername", "");
+ W_ERROR_HAVE_NO_MEMORY(info[i].info2.printername);
+
+ info[i].info4.servername = samdb_result_string(msgs[i], "servername", "");
+ W_ERROR_HAVE_NO_MEMORY(info[i].info2.servername);
+
+ info[i].info4.attributes = samdb_result_uint(msgs[i], "attributes", 0);
+ }
+ break;
+ case 5:
+ for (i = 0; i < count; i++) {
+ info[i].info5.printername = samdb_result_string(msgs[i], "name", "");
+ W_ERROR_HAVE_NO_MEMORY(info[i].info5.printername);
+
+ info[i].info5.portname = samdb_result_string(msgs[i], "port", "");
+ W_ERROR_HAVE_NO_MEMORY(info[i].info5.portname);
+
+ info[i].info5.attributes = samdb_result_uint(msgs[i], "attributes", 0);
+ info[i].info5.device_not_selected_timeout = samdb_result_uint(msgs[i], "device_not_selected_timeout", 0);
+ info[i].info5.transmission_retry_timeout = samdb_result_uint(msgs[i], "transmission_retry_timeout", 0);
+ }
+ break;
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ *r->out.info = info;
+ *r->out.count = count;
+ return WERR_OK;
+}
+
+static WERROR sptr_OpenPrinter(struct ntptr_context *ntptr, TALLOC_CTX *mem_ctx,
+ struct spoolss_OpenPrinterEx *r,
+ const char *printer_name,
+ struct ntptr_GenericHandle **printer)
+{
+ return WERR_INVALID_PRINTER_NAME;
+}
+
+/* port functions */
+static WERROR sptr_EnumPorts(struct ntptr_context *ntptr, TALLOC_CTX *mem_ctx,
+ struct spoolss_EnumPorts *r)
+{
+ struct ldb_context *sptr_db = talloc_get_type(ntptr->private_data, struct ldb_context);
+ struct ldb_message **msgs;
+ int count;
+ int i;
+ union spoolss_PortInfo *info;
+
+ count = sptr_db_search(sptr_db, mem_ctx, NULL, &msgs, NULL,
+ "(&(objectclass=port))");
+
+ if (count == 0) return WERR_OK;
+ if (count < 0) return WERR_GENERAL_FAILURE;
+
+ info = talloc_array(mem_ctx, union spoolss_PortInfo, count);
+ W_ERROR_HAVE_NO_MEMORY(info);
+
+ switch (r->in.level) {
+ case 1:
+ for (i = 0; i < count; i++) {
+ info[i].info1.port_name = samdb_result_string(msgs[i], "port-name", "");
+ W_ERROR_HAVE_NO_MEMORY(info[i].info1.port_name);
+ }
+ break;
+ case 2:
+ for (i=0; i < count; i++) {
+ info[i].info2.port_name = samdb_result_string(msgs[i], "port-name", "");
+ W_ERROR_HAVE_NO_MEMORY(info[i].info2.port_name);
+
+ info[i].info2.monitor_name = samdb_result_string(msgs[i], "monitor-name", "");
+ W_ERROR_HAVE_NO_MEMORY(info[i].info2.monitor_name);
+
+ info[i].info2.description = samdb_result_string(msgs[i], "description", "");
+ W_ERROR_HAVE_NO_MEMORY(info[i].info2.description);
+
+ info[i].info2.port_type = samdb_result_uint(msgs[i], "port-type", SPOOLSS_PORT_TYPE_WRITE);
+ info[i].info2.reserved = samdb_result_uint(msgs[i], "reserved", 0);
+ }
+ break;
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ *r->out.info = info;
+ *r->out.count = count;
+ return WERR_OK;
+}
+
+/* monitor functions */
+static WERROR sptr_EnumMonitors(struct ntptr_context *ntptr, TALLOC_CTX *mem_ctx,
+ struct spoolss_EnumMonitors *r)
+{
+ struct ldb_context *sptr_db = talloc_get_type(ntptr->private_data, struct ldb_context);
+ struct ldb_message **msgs;
+ int count;
+ int i;
+ union spoolss_MonitorInfo *info;
+
+ count = sptr_db_search(sptr_db, mem_ctx, NULL, &msgs, NULL,
+ "(&(objectclass=monitor))");
+
+ if (count == 0) return WERR_OK;
+ if (count < 0) return WERR_GENERAL_FAILURE;
+
+ info = talloc_array(mem_ctx, union spoolss_MonitorInfo, count);
+ W_ERROR_HAVE_NO_MEMORY(info);
+
+ switch (r->in.level) {
+ case 1:
+ for (i = 0; i < count; i++) {
+ info[i].info1.monitor_name = samdb_result_string(msgs[i], "monitor-name", "");
+ W_ERROR_HAVE_NO_MEMORY(info[i].info1.monitor_name);
+ }
+ break;
+ case 2:
+ for (i=0; i < count; i++) {
+ info[i].info2.monitor_name = samdb_result_string(msgs[i], "monitor-name", "");
+ W_ERROR_HAVE_NO_MEMORY(info[i].info2.monitor_name);
+
+ info[i].info2.environment = samdb_result_string(msgs[i], "environment", "");
+ W_ERROR_HAVE_NO_MEMORY(info[i].info2.environment);
+
+ info[i].info2.dll_name = samdb_result_string(msgs[i], "dll-name", "");
+ W_ERROR_HAVE_NO_MEMORY(info[i].info2.dll_name);
+ }
+ break;
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ *r->out.info = info;
+ *r->out.count = count;
+ return WERR_OK;
+}
+
+/* Printer Form functions */
+static WERROR sptr_GetPrinterForm(struct ntptr_GenericHandle *printer, TALLOC_CTX *mem_ctx,
+ struct spoolss_GetForm *r)
+{
+ struct ldb_context *sptr_db = talloc_get_type(printer->ntptr->private_data, struct ldb_context);
+ struct ldb_message **msgs;
+ struct ldb_dn *base_dn;
+ int count;
+ union spoolss_FormInfo *info;
+
+ /* TODO: do checks access here
+ * if (!(printer->access_mask & desired_access)) {
+ * return WERR_FOOBAR;
+ * }
+ */
+
+ base_dn = ldb_dn_new_fmt(mem_ctx, sptr_db, "CN=Forms,CN=%s,CN=Printers", printer->object_name);
+ W_ERROR_HAVE_NO_MEMORY(base_dn);
+
+ count = sptr_db_search(sptr_db, mem_ctx, base_dn, &msgs, NULL,
+ "(&(form-name=%s)(objectClass=form))",
+ r->in.form_name);
+
+ if (count == 0) return WERR_FOOBAR;
+ if (count > 1) return WERR_FOOBAR;
+ if (count < 0) return WERR_GENERAL_FAILURE;
+
+ info = talloc(mem_ctx, union spoolss_FormInfo);
+ W_ERROR_HAVE_NO_MEMORY(info);
+
+ switch (r->in.level) {
+ case 1:
+ info->info1.flags = samdb_result_uint(msgs[0], "flags", SPOOLSS_FORM_BUILTIN);
+
+ info->info1.form_name = samdb_result_string(msgs[0], "form-name", NULL);
+ W_ERROR_HAVE_NO_MEMORY(info->info1.form_name);
+
+ info->info1.size.width = samdb_result_uint(msgs[0], "size-width", 0);
+ info->info1.size.height = samdb_result_uint(msgs[0], "size-height", 0);
+
+ info->info1.area.left = samdb_result_uint(msgs[0], "area-left", 0);
+ info->info1.area.top = samdb_result_uint(msgs[0], "area-top", 0);
+ info->info1.area.right = samdb_result_uint(msgs[0], "area-right", 0);
+ info->info1.area.bottom = samdb_result_uint(msgs[0], "area-bottom", 0);
+ break;
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ r->out.info = info;
+ return WERR_OK;
+}
+
+static WERROR sptr_GetPrintProcessorDirectory(struct ntptr_context *ntptr, TALLOC_CTX *mem_ctx,
+ struct spoolss_GetPrintProcessorDirectory *r)
+{
+ union spoolss_PrintProcessorDirectoryInfo *info;
+ const char *prefix;
+ const char *postfix;
+
+ /*
+ * NOTE: normally r->in.level is 1, but both w2k3 and nt4 sp6a
+ * are ignoring the r->in.level completely, so we do :-)
+ */
+
+ /*
+ * TODO: check the server name is ours
+ * - if it's a invalid UNC then return WERR_INVALID_NAME
+ * - if it's the wrong host name return WERR_INVALID_PARAM
+ * - if it's "" then we need to return a local WINDOWS path
+ */
+ if (!r->in.server || !r->in.server[0]) {
+ prefix = "C:\\PRTPROCS";
+ } else {
+ prefix = talloc_asprintf(mem_ctx, "%s\\prnproc$", r->in.server);
+ W_ERROR_HAVE_NO_MEMORY(prefix);
+ }
+
+ if (r->in.environment && strcmp(SPOOLSS_ARCHITECTURE_NT_X86, r->in.environment) == 0) {
+ postfix = "W32X86";
+ } else {
+ return WERR_INVALID_ENVIRONMENT;
+ }
+
+ info = talloc(mem_ctx, union spoolss_PrintProcessorDirectoryInfo);
+ W_ERROR_HAVE_NO_MEMORY(info);
+
+ info->info1.directory_name = talloc_asprintf(mem_ctx, "%s\\%s", prefix, postfix);
+ W_ERROR_HAVE_NO_MEMORY(info->info1.directory_name);
+
+ r->out.info = info;
+ return WERR_OK;
+}
+
+
+/*
+ initialialise the simble ldb backend, registering ourselves with the ntptr subsystem
+ */
+static const struct ntptr_ops ntptr_simple_ldb_ops = {
+ .name = "simple_ldb",
+ .init_context = sptr_init_context,
+
+ /* PrintServer functions */
+ .OpenPrintServer = sptr_OpenPrintServer,
+/* .XcvDataPrintServer = sptr_XcvDataPrintServer,
+*/
+ /* PrintServer PrinterData functions */
+/* .EnumPrintServerData = sptr_EnumPrintServerData,
+*/ .GetPrintServerData = sptr_GetPrintServerData,
+/* .SetPrintServerData = sptr_SetPrintServerData,
+ .DeletePrintServerData = sptr_DeletePrintServerData,
+*/
+ /* PrintServer Form functions */
+ .EnumPrintServerForms = sptr_EnumPrintServerForms,
+ .AddPrintServerForm = sptr_AddPrintServerForm,
+ .SetPrintServerForm = sptr_SetPrintServerForm,
+ .DeletePrintServerForm = sptr_DeletePrintServerForm,
+
+ /* PrintServer Driver functions */
+ .EnumPrinterDrivers = sptr_EnumPrinterDrivers,
+/* .AddPrinterDriver = sptr_AddPrinterDriver,
+ .DeletePrinterDriver = sptr_DeletePrinterDriver,
+*/ .GetPrinterDriverDirectory = sptr_GetPrinterDriverDirectory,
+
+ /* Port functions */
+ .EnumPorts = sptr_EnumPorts,
+/* .OpenPort = sptr_OpenPort,
+ .XcvDataPort = sptr_XcvDataPort,
+*/
+ /* Monitor functions */
+ .EnumMonitors = sptr_EnumMonitors,
+/* .OpenMonitor = sptr_OpenMonitor,
+ .XcvDataMonitor = sptr_XcvDataMonitor,
+*/
+ /* PrintProcessor functions */
+/* .EnumPrintProcessors = sptr_EnumPrintProcessors,
+*/
+ .GetPrintProcessorDirectory = sptr_GetPrintProcessorDirectory,
+
+ /* Printer functions */
+ .EnumPrinters = sptr_EnumPrinters,
+ .OpenPrinter = sptr_OpenPrinter,
+/* .AddPrinter = sptr_AddPrinter,
+ .GetPrinter = sptr_GetPrinter,
+ .SetPrinter = sptr_SetPrinter,
+ .DeletePrinter = sptr_DeletePrinter,
+ .XcvDataPrinter = sptr_XcvDataPrinter,
+*/
+ /* Printer Driver functions */
+/* .GetPrinterDriver = sptr_GetPrinterDriver,
+*/
+ /* Printer PrinterData functions */
+/* .EnumPrinterData = sptr_EnumPrinterData,
+ .GetPrinterData = sptr_GetPrinterData,
+ .SetPrinterData = sptr_SetPrinterData,
+ .DeletePrinterData = sptr_DeletePrinterData,
+*/
+ /* Printer Form functions */
+/* .EnumPrinterForms = sptr_EnumPrinterForms,
+ .AddPrinterForm = sptr_AddPrinterForm,
+*/ .GetPrinterForm = sptr_GetPrinterForm,
+/* .SetPrinterForm = sptr_SetPrinterForm,
+ .DeletePrinterForm = sptr_DeletePrinterForm,
+*/
+ /* Printer Job functions */
+/* .EnumJobs = sptr_EnumJobs,
+ .AddJob = sptr_AddJob,
+ .ScheduleJob = sptr_ScheduleJob,
+ .GetJob = sptr_GetJob,
+ .SetJob = sptr_SetJob,
+*/
+ /* Printer Printing functions */
+/* .StartDocPrinter = sptr_StartDocPrinter,
+ .EndDocPrinter = sptr_EndDocPrinter,
+ .StartPagePrinter = sptr_StartPagePrinter,
+ .EndPagePrinter = sptr_EndPagePrinter,
+ .WritePrinter = sptr_WritePrinter,
+ .ReadPrinter = sptr_ReadPrinter,
+*/};
+
+NTSTATUS ntptr_simple_ldb_init(void)
+{
+ NTSTATUS ret;
+
+ ret = ntptr_register(&ntptr_simple_ldb_ops);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("Failed to register NTPTR '%s' backend!\n",
+ ntptr_simple_ldb_ops.name));
+ }
+
+ return ret;
+}
diff --git a/source4/ntvfs/README b/source4/ntvfs/README
new file mode 100644
index 0000000000..c86c9a0050
--- /dev/null
+++ b/source4/ntvfs/README
@@ -0,0 +1,26 @@
+This is the base of the new NTVFS subsystem for Samba. The model for
+NTVFS backends is quite different than for the older style VFS
+backends, in particular:
+
+- the NTVFS backends receive windows style file names, although they
+ are in the unix charset (usually UTF8). This means the backend is
+ responsible for mapping windows filename conventions to unix
+ filename conventions if necessary
+
+- the NTVFS backends are responsible for changing effective UID before
+ calling any OS local filesystem operations (if needed). The
+ become_*() functions are provided to make this easier.
+
+- the NTVFS backends are responsible for resolving DFS paths
+
+- each NTVFS backend handles either disk, printer or IPC$ shares,
+ rather than one backend handling all types
+
+- the entry points of the NTVFS backends correspond closely with basic
+ SMB operations, wheres the old VFS was modelled directly on the
+ POSIX filesystem interface.
+
+- the NTVFS backends are responsible for all semantic mappings, such
+ as mapping dos file attributes, ACLs, file ownership and file times
+
+
diff --git a/source4/ntvfs/cifs/README b/source4/ntvfs/cifs/README
new file mode 100644
index 0000000000..31720612c2
--- /dev/null
+++ b/source4/ntvfs/cifs/README
@@ -0,0 +1,40 @@
+This is the 'CIFS on CIFS' backend for Samba. It provides a NTVFS
+backend that talks to a remote CIFS server. The primary aim of this
+backend is for debugging and development, although some poeple may
+find it useful as a CIFS gateway.
+
+There are two modes of operation: Password specified and delegated
+credentials.
+
+Password specified:
+-------------------
+
+This uses a static username/password in the config file, example:
+
+[myshare]
+ ntvfs handler = cifs
+ cifs:server = myserver
+ cifs:user = tridge
+ cifs:password = mypass
+ cifs:domain = TESTDOM
+ cifs:share = test
+
+
+Delegated credentials:
+----------------------
+
+If your incoming user is authenticated with Kerberos, and the machine
+account for this Samba4 proxy server is 'trusted for delegation', then
+the Samba4 proxy can forward the client's credentials to the target.
+
+You must be joined to the domain (net join <domain> member).
+
+To set 'trusted for delegation' with MMC, see the checkbox in the
+Computer account property page under Users and Computers.
+
+[myshare]
+ ntvfs handler = cifs
+ cifs:server = myserver
+ cifs:share = test
+
+
diff --git a/source4/ntvfs/cifs/vfs_cifs.c b/source4/ntvfs/cifs/vfs_cifs.c
new file mode 100644
index 0000000000..be9096b01f
--- /dev/null
+++ b/source4/ntvfs/cifs/vfs_cifs.c
@@ -0,0 +1,1158 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ CIFS-on-CIFS NTVFS filesystem backend
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) James J Myers 2003 <myersjj@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+/*
+ this implements a CIFS->CIFS NTVFS filesystem backend.
+
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/smb_composite/smb_composite.h"
+#include "auth/auth.h"
+#include "auth/credentials/credentials.h"
+#include "ntvfs/ntvfs.h"
+#include "../lib/util/dlinklist.h"
+#include "param/param.h"
+#include "libcli/resolve/resolve.h"
+
+struct cvfs_file {
+ struct cvfs_file *prev, *next;
+ uint16_t fnum;
+ struct ntvfs_handle *h;
+};
+
+/* this is stored in ntvfs_private */
+struct cvfs_private {
+ struct smbcli_tree *tree;
+ struct smbcli_transport *transport;
+ struct ntvfs_module_context *ntvfs;
+ struct async_info *pending;
+ struct cvfs_file *files;
+ bool map_generic;
+ bool map_trans2;
+};
+
+
+/* a structure used to pass information to an async handler */
+struct async_info {
+ struct async_info *next, *prev;
+ struct cvfs_private *cvfs;
+ struct ntvfs_request *req;
+ struct smbcli_request *c_req;
+ struct cvfs_file *f;
+ void *parms;
+};
+
+#define CHECK_UPSTREAM_OPEN do { \
+ if (! p->transport->socket->sock) { \
+ req->async_states->state|=NTVFS_ASYNC_STATE_CLOSE; \
+ return NT_STATUS_CONNECTION_DISCONNECTED; \
+ } \
+} while(0)
+
+#define SETUP_PID do { \
+ p->tree->session->pid = req->smbpid; \
+ CHECK_UPSTREAM_OPEN; \
+} while(0)
+
+#define SETUP_FILE_HERE(f) do { \
+ f = ntvfs_handle_get_backend_data(io->generic.in.file.ntvfs, ntvfs); \
+ if (!f) return NT_STATUS_INVALID_HANDLE; \
+ io->generic.in.file.fnum = f->fnum; \
+} while (0)
+
+#define SETUP_FILE do { \
+ struct cvfs_file *f; \
+ SETUP_FILE_HERE(f); \
+} while (0)
+
+#define SETUP_PID_AND_FILE do { \
+ SETUP_PID; \
+ SETUP_FILE; \
+} while (0)
+
+#define CIFS_SERVER "cifs:server"
+#define CIFS_USER "cifs:user"
+#define CIFS_PASSWORD "cifs:password"
+#define CIFS_DOMAIN "cifs:domain"
+#define CIFS_SHARE "cifs:share"
+#define CIFS_USE_MACHINE_ACCT "cifs:use-machine-account"
+#define CIFS_MAP_GENERIC "cifs:map-generic"
+#define CIFS_MAP_TRANS2 "cifs:map-trans2"
+
+#define CIFS_USE_MACHINE_ACCT_DEFAULT false
+#define CIFS_MAP_GENERIC_DEFAULT false
+#define CIFS_MAP_TRANS2_DEFAULT true
+
+/*
+ a handler for oplock break events from the server - these need to be passed
+ along to the client
+ */
+static bool oplock_handler(struct smbcli_transport *transport, uint16_t tid, uint16_t fnum, uint8_t level, void *p_private)
+{
+ struct cvfs_private *p = p_private;
+ NTSTATUS status;
+ struct ntvfs_handle *h = NULL;
+ struct cvfs_file *f;
+
+ for (f=p->files; f; f=f->next) {
+ if (f->fnum != fnum) continue;
+ h = f->h;
+ break;
+ }
+
+ if (!h) {
+ DEBUG(5,("vfs_cifs: ignoring oplock break level %d for fnum %d\n", level, fnum));
+ return true;
+ }
+
+ DEBUG(5,("vfs_cifs: sending oplock break level %d for fnum %d\n", level, fnum));
+ status = ntvfs_send_oplock_break(p->ntvfs, h, level);
+ if (!NT_STATUS_IS_OK(status)) return false;
+ return true;
+}
+
+/*
+ connect to a share - used when a tree_connect operation comes in.
+*/
+static NTSTATUS cvfs_connect(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, const char *sharename)
+{
+ NTSTATUS status;
+ struct cvfs_private *p;
+ const char *host, *user, *pass, *domain, *remote_share;
+ struct smb_composite_connect io;
+ struct composite_context *creq;
+ struct share_config *scfg = ntvfs->ctx->config;
+
+ struct cli_credentials *credentials;
+ bool machine_account;
+
+ /* Here we need to determine which server to connect to.
+ * For now we use parametric options, type cifs.
+ * Later we will use security=server and auth_server.c.
+ */
+ host = share_string_option(scfg, CIFS_SERVER, NULL);
+ user = share_string_option(scfg, CIFS_USER, NULL);
+ pass = share_string_option(scfg, CIFS_PASSWORD, NULL);
+ domain = share_string_option(scfg, CIFS_DOMAIN, NULL);
+ remote_share = share_string_option(scfg, CIFS_SHARE, NULL);
+ if (!remote_share) {
+ remote_share = sharename;
+ }
+
+ machine_account = share_bool_option(scfg, CIFS_USE_MACHINE_ACCT, CIFS_USE_MACHINE_ACCT_DEFAULT);
+
+ p = talloc_zero(ntvfs, struct cvfs_private);
+ if (!p) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ntvfs->private_data = p;
+
+ if (!host) {
+ DEBUG(1,("CIFS backend: You must supply server\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (user && pass) {
+ DEBUG(5, ("CIFS backend: Using specified password\n"));
+ credentials = cli_credentials_init(p);
+ if (!credentials) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ cli_credentials_set_conf(credentials, ntvfs->ctx->lp_ctx);
+ cli_credentials_set_username(credentials, user, CRED_SPECIFIED);
+ if (domain) {
+ cli_credentials_set_domain(credentials, domain, CRED_SPECIFIED);
+ }
+ cli_credentials_set_password(credentials, pass, CRED_SPECIFIED);
+ } else if (machine_account) {
+ DEBUG(5, ("CIFS backend: Using machine account\n"));
+ credentials = cli_credentials_init(p);
+ cli_credentials_set_conf(credentials, ntvfs->ctx->lp_ctx);
+ if (domain) {
+ cli_credentials_set_domain(credentials, domain, CRED_SPECIFIED);
+ }
+ status = cli_credentials_set_machine_account(credentials, ntvfs->ctx->lp_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ } else if (req->session_info->credentials) {
+ DEBUG(5, ("CIFS backend: Using delegated credentials\n"));
+ credentials = req->session_info->credentials;
+ } else {
+ DEBUG(1,("CIFS backend: NO delegated credentials found: You must supply server, user and password or the client must supply delegated credentials\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* connect to the server, using the smbd event context */
+ io.in.dest_host = host;
+ io.in.dest_ports = lp_smb_ports(ntvfs->ctx->lp_ctx);
+ io.in.socket_options = lp_socket_options(ntvfs->ctx->lp_ctx);
+ io.in.called_name = host;
+ io.in.credentials = credentials;
+ io.in.fallback_to_anonymous = false;
+ io.in.workgroup = lp_workgroup(ntvfs->ctx->lp_ctx);
+ io.in.service = remote_share;
+ io.in.service_type = "?????";
+ io.in.iconv_convenience = lp_iconv_convenience(ntvfs->ctx->lp_ctx);
+ io.in.gensec_settings = lp_gensec_settings(p, ntvfs->ctx->lp_ctx);
+ lp_smbcli_options(ntvfs->ctx->lp_ctx, &io.in.options);
+ lp_smbcli_session_options(ntvfs->ctx->lp_ctx, &io.in.session_options);
+
+ if (!(ntvfs->ctx->client_caps & NTVFS_CLIENT_CAP_LEVEL_II_OPLOCKS)) {
+ io.in.options.use_level2_oplocks = false;
+ }
+
+ creq = smb_composite_connect_send(&io, p,
+ lp_resolve_context(ntvfs->ctx->lp_ctx),
+ ntvfs->ctx->event_ctx);
+ status = smb_composite_connect_recv(creq, p);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ p->tree = io.out.tree;
+
+ p->transport = p->tree->session->transport;
+ SETUP_PID;
+ p->ntvfs = ntvfs;
+
+ ntvfs->ctx->fs_type = talloc_strdup(ntvfs->ctx, "NTFS");
+ NT_STATUS_HAVE_NO_MEMORY(ntvfs->ctx->fs_type);
+ ntvfs->ctx->dev_type = talloc_strdup(ntvfs->ctx, "A:");
+ NT_STATUS_HAVE_NO_MEMORY(ntvfs->ctx->dev_type);
+
+ /* we need to receive oplock break requests from the server */
+ smbcli_oplock_handler(p->transport, oplock_handler, p);
+
+ p->map_generic = share_bool_option(scfg, CIFS_MAP_GENERIC, CIFS_MAP_GENERIC_DEFAULT);
+
+ p->map_trans2 = share_bool_option(scfg, CIFS_MAP_TRANS2, CIFS_MAP_TRANS2_DEFAULT);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ disconnect from a share
+*/
+static NTSTATUS cvfs_disconnect(struct ntvfs_module_context *ntvfs)
+{
+ struct cvfs_private *p = ntvfs->private_data;
+ struct async_info *a, *an;
+
+ /* first cleanup pending requests */
+ for (a=p->pending; a; a = an) {
+ an = a->next;
+ smbcli_request_destroy(a->c_req);
+ talloc_free(a);
+ }
+
+ talloc_free(p);
+ ntvfs->private_data = NULL;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ destroy an async info structure
+*/
+static int async_info_destructor(struct async_info *async)
+{
+ DLIST_REMOVE(async->cvfs->pending, async);
+ return 0;
+}
+
+/*
+ a handler for simple async replies
+ this handler can only be used for functions that don't return any
+ parameters (those that just return a status code)
+ */
+static void async_simple(struct smbcli_request *c_req)
+{
+ struct async_info *async = c_req->async.private_data;
+ struct ntvfs_request *req = async->req;
+ req->async_states->status = smbcli_request_simple_recv(c_req);
+ talloc_free(async);
+ req->async_states->send_fn(req);
+}
+
+
+/* save some typing for the simple functions */
+#define ASYNC_RECV_TAIL_F(io, async_fn, file) do { \
+ if (!c_req) return NT_STATUS_UNSUCCESSFUL; \
+ { \
+ struct async_info *async; \
+ async = talloc(req, struct async_info); \
+ if (!async) return NT_STATUS_NO_MEMORY; \
+ async->parms = io; \
+ async->req = req; \
+ async->f = file; \
+ async->cvfs = p; \
+ async->c_req = c_req; \
+ DLIST_ADD(p->pending, async); \
+ c_req->async.private_data = async; \
+ talloc_set_destructor(async, async_info_destructor); \
+ } \
+ c_req->async.fn = async_fn; \
+ req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC; \
+ return NT_STATUS_OK; \
+} while (0)
+
+#define ASYNC_RECV_TAIL(io, async_fn) ASYNC_RECV_TAIL_F(io, async_fn, NULL)
+
+#define SIMPLE_ASYNC_TAIL ASYNC_RECV_TAIL(NULL, async_simple)
+
+/*
+ delete a file - the dirtype specifies the file types to include in the search.
+ The name can contain CIFS wildcards, but rarely does (except with OS/2 clients)
+*/
+static NTSTATUS cvfs_unlink(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_unlink *unl)
+{
+ struct cvfs_private *p = ntvfs->private_data;
+ struct smbcli_request *c_req;
+
+ SETUP_PID;
+
+ /* see if the front end will allow us to perform this
+ function asynchronously. */
+ if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ return smb_raw_unlink(p->tree, unl);
+ }
+
+ c_req = smb_raw_unlink_send(p->tree, unl);
+
+ SIMPLE_ASYNC_TAIL;
+}
+
+/*
+ a handler for async ioctl replies
+ */
+static void async_ioctl(struct smbcli_request *c_req)
+{
+ struct async_info *async = c_req->async.private_data;
+ struct ntvfs_request *req = async->req;
+ req->async_states->status = smb_raw_ioctl_recv(c_req, req, async->parms);
+ talloc_free(async);
+ req->async_states->send_fn(req);
+}
+
+/*
+ ioctl interface
+*/
+static NTSTATUS cvfs_ioctl(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_ioctl *io)
+{
+ struct cvfs_private *p = ntvfs->private_data;
+ struct smbcli_request *c_req;
+
+ SETUP_PID_AND_FILE;
+
+ /* see if the front end will allow us to perform this
+ function asynchronously. */
+ if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ return smb_raw_ioctl(p->tree, req, io);
+ }
+
+ c_req = smb_raw_ioctl_send(p->tree, io);
+
+ ASYNC_RECV_TAIL(io, async_ioctl);
+}
+
+/*
+ check if a directory exists
+*/
+static NTSTATUS cvfs_chkpath(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_chkpath *cp)
+{
+ struct cvfs_private *p = ntvfs->private_data;
+ struct smbcli_request *c_req;
+
+ SETUP_PID;
+
+ if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ return smb_raw_chkpath(p->tree, cp);
+ }
+
+ c_req = smb_raw_chkpath_send(p->tree, cp);
+
+ SIMPLE_ASYNC_TAIL;
+}
+
+/*
+ a handler for async qpathinfo replies
+ */
+static void async_qpathinfo(struct smbcli_request *c_req)
+{
+ struct async_info *async = c_req->async.private_data;
+ struct ntvfs_request *req = async->req;
+ req->async_states->status = smb_raw_pathinfo_recv(c_req, req, async->parms);
+ talloc_free(async);
+ req->async_states->send_fn(req);
+}
+
+/*
+ return info on a pathname
+*/
+static NTSTATUS cvfs_qpathinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_fileinfo *info)
+{
+ struct cvfs_private *p = ntvfs->private_data;
+ struct smbcli_request *c_req;
+
+ SETUP_PID;
+
+ if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ return smb_raw_pathinfo(p->tree, req, info);
+ }
+
+ c_req = smb_raw_pathinfo_send(p->tree, info);
+
+ ASYNC_RECV_TAIL(info, async_qpathinfo);
+}
+
+/*
+ a handler for async qfileinfo replies
+ */
+static void async_qfileinfo(struct smbcli_request *c_req)
+{
+ struct async_info *async = c_req->async.private_data;
+ struct ntvfs_request *req = async->req;
+ req->async_states->status = smb_raw_fileinfo_recv(c_req, req, async->parms);
+ talloc_free(async);
+ req->async_states->send_fn(req);
+}
+
+/*
+ query info on a open file
+*/
+static NTSTATUS cvfs_qfileinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_fileinfo *io)
+{
+ struct cvfs_private *p = ntvfs->private_data;
+ struct smbcli_request *c_req;
+
+ SETUP_PID_AND_FILE;
+
+ if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ return smb_raw_fileinfo(p->tree, req, io);
+ }
+
+ c_req = smb_raw_fileinfo_send(p->tree, io);
+
+ ASYNC_RECV_TAIL(io, async_qfileinfo);
+}
+
+
+/*
+ set info on a pathname
+*/
+static NTSTATUS cvfs_setpathinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_setfileinfo *st)
+{
+ struct cvfs_private *p = ntvfs->private_data;
+ struct smbcli_request *c_req;
+
+ SETUP_PID;
+
+ if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ return smb_raw_setpathinfo(p->tree, st);
+ }
+
+ c_req = smb_raw_setpathinfo_send(p->tree, st);
+
+ SIMPLE_ASYNC_TAIL;
+}
+
+
+/*
+ a handler for async open replies
+ */
+static void async_open(struct smbcli_request *c_req)
+{
+ struct async_info *async = c_req->async.private_data;
+ struct cvfs_private *cvfs = async->cvfs;
+ struct ntvfs_request *req = async->req;
+ struct cvfs_file *f = async->f;
+ union smb_open *io = async->parms;
+ union smb_handle *file;
+ talloc_free(async);
+ req->async_states->status = smb_raw_open_recv(c_req, req, io);
+ SMB_OPEN_OUT_FILE(io, file);
+ f->fnum = file->fnum;
+ file->ntvfs = NULL;
+ if (!NT_STATUS_IS_OK(req->async_states->status)) goto failed;
+ req->async_states->status = ntvfs_handle_set_backend_data(f->h, cvfs->ntvfs, f);
+ if (!NT_STATUS_IS_OK(req->async_states->status)) goto failed;
+ file->ntvfs = f->h;
+ DLIST_ADD(cvfs->files, f);
+failed:
+ req->async_states->send_fn(req);
+}
+
+/*
+ open a file
+*/
+static NTSTATUS cvfs_open(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_open *io)
+{
+ struct cvfs_private *p = ntvfs->private_data;
+ struct smbcli_request *c_req;
+ struct ntvfs_handle *h;
+ struct cvfs_file *f;
+ NTSTATUS status;
+
+ SETUP_PID;
+
+ if (io->generic.level != RAW_OPEN_GENERIC &&
+ p->map_generic) {
+ return ntvfs_map_open(ntvfs, req, io);
+ }
+
+ status = ntvfs_handle_new(ntvfs, req, &h);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ f = talloc_zero(h, struct cvfs_file);
+ NT_STATUS_HAVE_NO_MEMORY(f);
+ f->h = h;
+
+ if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ union smb_handle *file;
+
+ status = smb_raw_open(p->tree, req, io);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ SMB_OPEN_OUT_FILE(io, file);
+ f->fnum = file->fnum;
+ file->ntvfs = NULL;
+ status = ntvfs_handle_set_backend_data(f->h, p->ntvfs, f);
+ NT_STATUS_NOT_OK_RETURN(status);
+ file->ntvfs = f->h;
+ DLIST_ADD(p->files, f);
+
+ return NT_STATUS_OK;
+ }
+
+ c_req = smb_raw_open_send(p->tree, io);
+
+ ASYNC_RECV_TAIL_F(io, async_open, f);
+}
+
+/*
+ create a directory
+*/
+static NTSTATUS cvfs_mkdir(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_mkdir *md)
+{
+ struct cvfs_private *p = ntvfs->private_data;
+ struct smbcli_request *c_req;
+
+ SETUP_PID;
+
+ if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ return smb_raw_mkdir(p->tree, md);
+ }
+
+ c_req = smb_raw_mkdir_send(p->tree, md);
+
+ SIMPLE_ASYNC_TAIL;
+}
+
+/*
+ remove a directory
+*/
+static NTSTATUS cvfs_rmdir(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, struct smb_rmdir *rd)
+{
+ struct cvfs_private *p = ntvfs->private_data;
+ struct smbcli_request *c_req;
+
+ SETUP_PID;
+
+ if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ return smb_raw_rmdir(p->tree, rd);
+ }
+ c_req = smb_raw_rmdir_send(p->tree, rd);
+
+ SIMPLE_ASYNC_TAIL;
+}
+
+/*
+ rename a set of files
+*/
+static NTSTATUS cvfs_rename(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_rename *ren)
+{
+ struct cvfs_private *p = ntvfs->private_data;
+ struct smbcli_request *c_req;
+
+ SETUP_PID;
+
+ if (ren->nttrans.level == RAW_RENAME_NTTRANS) {
+ struct cvfs_file *f;
+ f = ntvfs_handle_get_backend_data(ren->nttrans.in.file.ntvfs, ntvfs);
+ if (!f) return NT_STATUS_INVALID_HANDLE;
+ ren->nttrans.in.file.fnum = f->fnum;
+ }
+
+ if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ return smb_raw_rename(p->tree, ren);
+ }
+
+ c_req = smb_raw_rename_send(p->tree, ren);
+
+ SIMPLE_ASYNC_TAIL;
+}
+
+/*
+ copy a set of files
+*/
+static NTSTATUS cvfs_copy(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, struct smb_copy *cp)
+{
+ return NT_STATUS_NOT_SUPPORTED;
+}
+
+/*
+ a handler for async read replies
+ */
+static void async_read(struct smbcli_request *c_req)
+{
+ struct async_info *async = c_req->async.private_data;
+ struct ntvfs_request *req = async->req;
+ req->async_states->status = smb_raw_read_recv(c_req, async->parms);
+ talloc_free(async);
+ req->async_states->send_fn(req);
+}
+
+/*
+ read from a file
+*/
+static NTSTATUS cvfs_read(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_read *io)
+{
+ struct cvfs_private *p = ntvfs->private_data;
+ struct smbcli_request *c_req;
+
+ SETUP_PID;
+
+ if (io->generic.level != RAW_READ_GENERIC &&
+ p->map_generic) {
+ return ntvfs_map_read(ntvfs, req, io);
+ }
+
+ SETUP_FILE;
+
+ if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ return smb_raw_read(p->tree, io);
+ }
+
+ c_req = smb_raw_read_send(p->tree, io);
+
+ ASYNC_RECV_TAIL(io, async_read);
+}
+
+/*
+ a handler for async write replies
+ */
+static void async_write(struct smbcli_request *c_req)
+{
+ struct async_info *async = c_req->async.private_data;
+ struct ntvfs_request *req = async->req;
+ req->async_states->status = smb_raw_write_recv(c_req, async->parms);
+ talloc_free(async);
+ req->async_states->send_fn(req);
+}
+
+/*
+ write to a file
+*/
+static NTSTATUS cvfs_write(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_write *io)
+{
+ struct cvfs_private *p = ntvfs->private_data;
+ struct smbcli_request *c_req;
+
+ SETUP_PID;
+
+ if (io->generic.level != RAW_WRITE_GENERIC &&
+ p->map_generic) {
+ return ntvfs_map_write(ntvfs, req, io);
+ }
+ SETUP_FILE;
+
+ if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ return smb_raw_write(p->tree, io);
+ }
+
+ c_req = smb_raw_write_send(p->tree, io);
+
+ ASYNC_RECV_TAIL(io, async_write);
+}
+
+/*
+ a handler for async seek replies
+ */
+static void async_seek(struct smbcli_request *c_req)
+{
+ struct async_info *async = c_req->async.private_data;
+ struct ntvfs_request *req = async->req;
+ req->async_states->status = smb_raw_seek_recv(c_req, async->parms);
+ talloc_free(async);
+ req->async_states->send_fn(req);
+}
+
+/*
+ seek in a file
+*/
+static NTSTATUS cvfs_seek(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_seek *io)
+{
+ struct cvfs_private *p = ntvfs->private_data;
+ struct smbcli_request *c_req;
+
+ SETUP_PID_AND_FILE;
+
+ if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ return smb_raw_seek(p->tree, io);
+ }
+
+ c_req = smb_raw_seek_send(p->tree, io);
+
+ ASYNC_RECV_TAIL(io, async_seek);
+}
+
+/*
+ flush a file
+*/
+static NTSTATUS cvfs_flush(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_flush *io)
+{
+ struct cvfs_private *p = ntvfs->private_data;
+ struct smbcli_request *c_req;
+
+ SETUP_PID;
+ switch (io->generic.level) {
+ case RAW_FLUSH_FLUSH:
+ SETUP_FILE;
+ break;
+ case RAW_FLUSH_ALL:
+ io->generic.in.file.fnum = 0xFFFF;
+ break;
+ case RAW_FLUSH_SMB2:
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ return smb_raw_flush(p->tree, io);
+ }
+
+ c_req = smb_raw_flush_send(p->tree, io);
+
+ SIMPLE_ASYNC_TAIL;
+}
+
+/*
+ close a file
+*/
+static NTSTATUS cvfs_close(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_close *io)
+{
+ struct cvfs_private *p = ntvfs->private_data;
+ struct smbcli_request *c_req;
+ struct cvfs_file *f;
+ union smb_close io2;
+
+ SETUP_PID;
+
+ if (io->generic.level != RAW_CLOSE_GENERIC &&
+ p->map_generic) {
+ return ntvfs_map_close(ntvfs, req, io);
+ }
+
+ if (io->generic.level == RAW_CLOSE_GENERIC) {
+ ZERO_STRUCT(io2);
+ io2.close.level = RAW_CLOSE_CLOSE;
+ io2.close.in.file = io->generic.in.file;
+ io2.close.in.write_time = io->generic.in.write_time;
+ io = &io2;
+ }
+
+ SETUP_FILE_HERE(f);
+ /* Note, we aren't free-ing f, or it's h here. Should we?
+ even if file-close fails, we'll remove it from the list,
+ what else would we do? Maybe we should not remove until
+ after the proxied call completes? */
+ DLIST_REMOVE(p->files, f);
+
+ if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ return smb_raw_close(p->tree, io);
+ }
+
+ c_req = smb_raw_close_send(p->tree, io);
+
+ SIMPLE_ASYNC_TAIL;
+}
+
+/*
+ exit - closing files open by the pid
+*/
+static NTSTATUS cvfs_exit(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req)
+{
+ struct cvfs_private *p = ntvfs->private_data;
+ struct smbcli_request *c_req;
+
+ SETUP_PID;
+
+ if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ return smb_raw_exit(p->tree->session);
+ }
+
+ c_req = smb_raw_exit_send(p->tree->session);
+
+ SIMPLE_ASYNC_TAIL;
+}
+
+/*
+ logoff - closing files open by the user
+*/
+static NTSTATUS cvfs_logoff(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req)
+{
+ /* we can't do this right in the cifs backend .... */
+ return NT_STATUS_OK;
+}
+
+/*
+ setup for an async call - nothing to do yet
+*/
+static NTSTATUS cvfs_async_setup(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ void *private_data)
+{
+ return NT_STATUS_OK;
+}
+
+/*
+ cancel an async call
+*/
+static NTSTATUS cvfs_cancel(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req)
+{
+ struct cvfs_private *p = ntvfs->private_data;
+ struct async_info *a;
+
+ /* find the matching request */
+ for (a=p->pending;a;a=a->next) {
+ if (a->req == req) {
+ break;
+ }
+ }
+
+ if (a == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ return smb_raw_ntcancel(a->c_req);
+}
+
+/*
+ lock a byte range
+*/
+static NTSTATUS cvfs_lock(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_lock *io)
+{
+ struct cvfs_private *p = ntvfs->private_data;
+ struct smbcli_request *c_req;
+
+ SETUP_PID;
+
+ if (io->generic.level != RAW_LOCK_GENERIC &&
+ p->map_generic) {
+ return ntvfs_map_lock(ntvfs, req, io);
+ }
+ SETUP_FILE;
+
+ if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ return smb_raw_lock(p->tree, io);
+ }
+
+ c_req = smb_raw_lock_send(p->tree, io);
+ SIMPLE_ASYNC_TAIL;
+}
+
+/*
+ set info on a open file
+*/
+static NTSTATUS cvfs_setfileinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_setfileinfo *io)
+{
+ struct cvfs_private *p = ntvfs->private_data;
+ struct smbcli_request *c_req;
+
+ SETUP_PID_AND_FILE;
+
+ if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ return smb_raw_setfileinfo(p->tree, io);
+ }
+ c_req = smb_raw_setfileinfo_send(p->tree, io);
+
+ SIMPLE_ASYNC_TAIL;
+}
+
+
+/*
+ a handler for async fsinfo replies
+ */
+static void async_fsinfo(struct smbcli_request *c_req)
+{
+ struct async_info *async = c_req->async.private_data;
+ struct ntvfs_request *req = async->req;
+ req->async_states->status = smb_raw_fsinfo_recv(c_req, req, async->parms);
+ talloc_free(async);
+ req->async_states->send_fn(req);
+}
+
+/*
+ return filesystem space info
+*/
+static NTSTATUS cvfs_fsinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_fsinfo *fs)
+{
+ struct cvfs_private *p = ntvfs->private_data;
+ struct smbcli_request *c_req;
+
+ SETUP_PID;
+
+ if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ return smb_raw_fsinfo(p->tree, req, fs);
+ }
+
+ c_req = smb_raw_fsinfo_send(p->tree, req, fs);
+
+ ASYNC_RECV_TAIL(fs, async_fsinfo);
+}
+
+/*
+ return print queue info
+*/
+static NTSTATUS cvfs_lpq(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_lpq *lpq)
+{
+ return NT_STATUS_NOT_SUPPORTED;
+}
+
+/*
+ list files in a directory matching a wildcard pattern
+*/
+static NTSTATUS cvfs_search_first(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_search_first *io,
+ void *search_private,
+ bool (*callback)(void *, const union smb_search_data *))
+{
+ struct cvfs_private *p = ntvfs->private_data;
+
+ SETUP_PID;
+
+ return smb_raw_search_first(p->tree, req, io, search_private, callback);
+}
+
+/* continue a search */
+static NTSTATUS cvfs_search_next(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_search_next *io,
+ void *search_private,
+ bool (*callback)(void *, const union smb_search_data *))
+{
+ struct cvfs_private *p = ntvfs->private_data;
+
+ SETUP_PID;
+
+ return smb_raw_search_next(p->tree, req, io, search_private, callback);
+}
+
+/* close a search */
+static NTSTATUS cvfs_search_close(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_search_close *io)
+{
+ struct cvfs_private *p = ntvfs->private_data;
+
+ SETUP_PID;
+
+ return smb_raw_search_close(p->tree, io);
+}
+
+/*
+ a handler for async trans2 replies
+ */
+static void async_trans2(struct smbcli_request *c_req)
+{
+ struct async_info *async = c_req->async.private_data;
+ struct ntvfs_request *req = async->req;
+ req->async_states->status = smb_raw_trans2_recv(c_req, req, async->parms);
+ talloc_free(async);
+ req->async_states->send_fn(req);
+}
+
+/* raw trans2 */
+static NTSTATUS cvfs_trans2(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ struct smb_trans2 *trans2)
+{
+ struct cvfs_private *p = ntvfs->private_data;
+ struct smbcli_request *c_req;
+
+ if (p->map_trans2) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ SETUP_PID;
+
+ if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ return smb_raw_trans2(p->tree, req, trans2);
+ }
+
+ c_req = smb_raw_trans2_send(p->tree, trans2);
+
+ ASYNC_RECV_TAIL(trans2, async_trans2);
+}
+
+
+/* SMBtrans - not used on file shares */
+static NTSTATUS cvfs_trans(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ struct smb_trans2 *trans2)
+{
+ return NT_STATUS_ACCESS_DENIED;
+}
+
+/*
+ a handler for async change notify replies
+ */
+static void async_changenotify(struct smbcli_request *c_req)
+{
+ struct async_info *async = c_req->async.private_data;
+ struct ntvfs_request *req = async->req;
+ req->async_states->status = smb_raw_changenotify_recv(c_req, req, async->parms);
+ talloc_free(async);
+ req->async_states->send_fn(req);
+}
+
+/* change notify request - always async */
+static NTSTATUS cvfs_notify(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_notify *io)
+{
+ struct cvfs_private *p = ntvfs->private_data;
+ struct smbcli_request *c_req;
+ int saved_timeout = p->transport->options.request_timeout;
+ struct cvfs_file *f;
+
+ if (io->nttrans.level != RAW_NOTIFY_NTTRANS) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ SETUP_PID;
+
+ f = ntvfs_handle_get_backend_data(io->nttrans.in.file.ntvfs, ntvfs);
+ if (!f) return NT_STATUS_INVALID_HANDLE;
+ io->nttrans.in.file.fnum = f->fnum;
+
+ /* this request doesn't make sense unless its async */
+ if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* we must not timeout on notify requests - they wait
+ forever */
+ p->transport->options.request_timeout = 0;
+
+ c_req = smb_raw_changenotify_send(p->tree, io);
+
+ p->transport->options.request_timeout = saved_timeout;
+
+ ASYNC_RECV_TAIL(io, async_changenotify);
+}
+
+/*
+ initialise the CIFS->CIFS backend, registering ourselves with the ntvfs subsystem
+ */
+NTSTATUS ntvfs_cifs_init(void)
+{
+ NTSTATUS ret;
+ struct ntvfs_ops ops;
+ NTVFS_CURRENT_CRITICAL_SIZES(vers);
+
+ ZERO_STRUCT(ops);
+
+ /* fill in the name and type */
+ ops.name = "cifs";
+ ops.type = NTVFS_DISK;
+
+ /* fill in all the operations */
+ ops.connect = cvfs_connect;
+ ops.disconnect = cvfs_disconnect;
+ ops.unlink = cvfs_unlink;
+ ops.chkpath = cvfs_chkpath;
+ ops.qpathinfo = cvfs_qpathinfo;
+ ops.setpathinfo = cvfs_setpathinfo;
+ ops.open = cvfs_open;
+ ops.mkdir = cvfs_mkdir;
+ ops.rmdir = cvfs_rmdir;
+ ops.rename = cvfs_rename;
+ ops.copy = cvfs_copy;
+ ops.ioctl = cvfs_ioctl;
+ ops.read = cvfs_read;
+ ops.write = cvfs_write;
+ ops.seek = cvfs_seek;
+ ops.flush = cvfs_flush;
+ ops.close = cvfs_close;
+ ops.exit = cvfs_exit;
+ ops.lock = cvfs_lock;
+ ops.setfileinfo = cvfs_setfileinfo;
+ ops.qfileinfo = cvfs_qfileinfo;
+ ops.fsinfo = cvfs_fsinfo;
+ ops.lpq = cvfs_lpq;
+ ops.search_first = cvfs_search_first;
+ ops.search_next = cvfs_search_next;
+ ops.search_close = cvfs_search_close;
+ ops.trans = cvfs_trans;
+ ops.logoff = cvfs_logoff;
+ ops.async_setup = cvfs_async_setup;
+ ops.cancel = cvfs_cancel;
+ ops.notify = cvfs_notify;
+ ops.trans2 = cvfs_trans2;
+
+ /* register ourselves with the NTVFS subsystem. We register
+ under the name 'cifs'. */
+ ret = ntvfs_register(&ops, &vers);
+
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("Failed to register CIFS backend!\n"));
+ }
+
+ return ret;
+}
diff --git a/source4/ntvfs/cifs_posix_cli/README b/source4/ntvfs/cifs_posix_cli/README
new file mode 100644
index 0000000000..2bc9283418
--- /dev/null
+++ b/source4/ntvfs/cifs_posix_cli/README
@@ -0,0 +1,12 @@
+This module (ntvfs 'cifsposix') provides a simple posix backend which
+provides better semantics for clients which implement the cifs unix
+extensions or the newer cifs posix extensions
+
+WARNING: All file access is done as user 'root'!!!
+ Only use this module for testing, with only test data!!!
+
+For activating this module use:
+
+[testshare]
+ path = /tmp/testshare
+ nfvfs handler = cifsposix
diff --git a/source4/ntvfs/cifs_posix_cli/cifsposix.h b/source4/ntvfs/cifs_posix_cli/cifsposix.h
new file mode 100644
index 0000000000..bf9e588b74
--- /dev/null
+++ b/source4/ntvfs/cifs_posix_cli/cifsposix.h
@@ -0,0 +1,38 @@
+
+struct cifspsx_private {
+ struct ntvfs_module_context *ntvfs;
+
+ /* the base directory */
+ char *connectpath;
+
+ /* a linked list of open searches */
+ struct search_state *search;
+
+ /* next available search handle */
+ uint16_t next_search_handle;
+
+ struct cifspsx_file *open_files;
+};
+
+struct cifspsx_dir {
+ unsigned int count;
+ char *unix_dir;
+ struct cifspsx_dirfile {
+ char *name;
+ struct stat st;
+ } *files;
+};
+
+struct cifspsx_file {
+ struct cifspsx_file *next, *prev;
+ int fd;
+ struct ntvfs_handle *handle;
+ char *name;
+};
+
+struct search_state {
+ struct search_state *next, *prev;
+ uint16_t handle;
+ unsigned int current_index;
+ struct cifspsx_dir *dir;
+};
diff --git a/source4/ntvfs/cifs_posix_cli/svfs_util.c b/source4/ntvfs/cifs_posix_cli/svfs_util.c
new file mode 100644
index 0000000000..03a46205a7
--- /dev/null
+++ b/source4/ntvfs/cifs_posix_cli/svfs_util.c
@@ -0,0 +1,188 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ simpler Samba VFS filesystem backend for clients which support the
+ CIFS Unix Extensions or newer CIFS POSIX protocol extensions
+
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Steve French 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 <http://www.gnu.org/licenses/>.
+*/
+/*
+ utility functions for cifs posix backend
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "cifsposix.h"
+#include "system/time.h"
+#include "system/dir.h"
+#include "ntvfs/ntvfs.h"
+#include "ntvfs/cifs_posix_cli/proto.h"
+
+/*
+ convert a windows path to a unix path - don't do any manging or case sensitive handling
+*/
+char *cifspsx_unix_path(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, const char *name)
+{
+ struct cifspsx_private *p = ntvfs->private_data;
+ char *ret;
+
+ if (*name != '\\') {
+ ret = talloc_asprintf(req, "%s/%s", p->connectpath, name);
+ } else {
+ ret = talloc_asprintf(req, "%s%s", p->connectpath, name);
+ }
+ all_string_sub(ret, "\\", "/", 0);
+
+ strlower(ret + strlen(p->connectpath));
+
+ return ret;
+}
+
+
+/*
+ read a directory and find all matching file names and stat info
+ returned names are separate unix and DOS names. The returned names
+ are relative to the directory
+*/
+struct cifspsx_dir *cifspsx_list_unix(TALLOC_CTX *mem_ctx, struct ntvfs_request *req, const char *unix_path)
+{
+ char *p, *mask;
+ struct cifspsx_dir *dir;
+ DIR *odir;
+ struct dirent *dent;
+ uint_t allocated = 0;
+ char *low_mask;
+
+ dir = talloc(mem_ctx, struct cifspsx_dir);
+ if (!dir) { return NULL; }
+
+ dir->count = 0;
+ dir->files = 0;
+
+ /* find the base directory */
+ p = strrchr(unix_path, '/');
+ if (!p) { return NULL; }
+
+ dir->unix_dir = talloc_strndup(mem_ctx, unix_path, PTR_DIFF(p, unix_path));
+ if (!dir->unix_dir) { return NULL; }
+
+ /* the wildcard pattern is the last part */
+ mask = p+1;
+
+ low_mask = talloc_strdup(mem_ctx, mask);
+ if (!low_mask) { return NULL; }
+ strlower(low_mask);
+
+ odir = opendir(dir->unix_dir);
+ if (!odir) { return NULL; }
+
+ while ((dent = readdir(odir))) {
+ uint_t i = dir->count;
+ char *full_name;
+ char *low_name;
+
+ if (strchr(dent->d_name, ':') && !strchr(unix_path, ':')) {
+ /* don't show streams in dir listing */
+ continue;
+ }
+
+ low_name = talloc_strdup(mem_ctx, dent->d_name);
+ if (!low_name) { continue; }
+ strlower(low_name);
+
+ /* check it matches the wildcard pattern */
+ if (ms_fnmatch(low_mask, low_name, PROTOCOL_NT1) != 0) {
+ continue;
+ }
+
+ if (dir->count >= allocated) {
+ allocated = (allocated + 100) * 1.2;
+ dir->files = talloc_realloc(dir, dir->files, struct cifspsx_dirfile, allocated);
+ if (!dir->files) {
+ closedir(odir);
+ return NULL;
+ }
+ }
+
+ dir->files[i].name = low_name;
+ if (!dir->files[i].name) { continue; }
+
+ asprintf(&full_name, "%s/%s", dir->unix_dir, dir->files[i].name);
+ if (!full_name) { continue; }
+
+ if (stat(full_name, &dir->files[i].st) == 0) {
+ dir->count++;
+ }
+
+ free(full_name);
+ }
+
+ closedir(odir);
+
+ return dir;
+}
+
+/*
+ read a directory and find all matching file names and stat info
+ returned names are separate unix and DOS names. The returned names
+ are relative to the directory
+*/
+struct cifspsx_dir *cifspsx_list(struct ntvfs_module_context *ntvfs, struct ntvfs_request *req, const char *pattern)
+{
+ struct cifspsx_private *p = ntvfs->private_data;
+ char *unix_path;
+
+ unix_path = cifspsx_unix_path(ntvfs, req, pattern);
+ if (!unix_path) { return NULL; }
+
+ return cifspsx_list_unix(p, req, unix_path);
+}
+
+
+/*******************************************************************
+set the time on a file via file descriptor
+*******************************************************************/
+int cifspsx_file_utime(int fd, struct utimbuf *times)
+{
+ char *fd_path = NULL;
+ int ret;
+
+ asprintf(&fd_path, "/proc/self/%d", fd);
+ if (!fd_path) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ ret = utime(fd_path, times);
+ free(fd_path);
+ return ret;
+}
+
+
+/*
+ map a unix file attrib to a DOS attribute
+*/
+uint16_t cifspsx_unix_to_dos_attrib(mode_t mode)
+{
+ uint16_t ret = 0;
+ if (S_ISDIR(mode)) ret |= FILE_ATTRIBUTE_DIRECTORY;
+ if (!(mode & S_IWUSR)) ret |= FILE_ATTRIBUTE_READONLY;
+ return ret;
+}
+
diff --git a/source4/ntvfs/cifs_posix_cli/vfs_cifs_posix.c b/source4/ntvfs/cifs_posix_cli/vfs_cifs_posix.c
new file mode 100644
index 0000000000..02fe9f2264
--- /dev/null
+++ b/source4/ntvfs/cifs_posix_cli/vfs_cifs_posix.c
@@ -0,0 +1,1081 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ simpler Samba VFS filesystem backend for clients which support the
+ CIFS Unix Extensions or newer CIFS POSIX protocol extensions
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Steve French 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 <http://www.gnu.org/licenses/>.
+*/
+/*
+ this implements a very simple NTVFS filesystem backend.
+ this backend largely ignores the POSIX -> CIFS mappings, just doing absolutely
+ minimal work to give a working backend.
+*/
+
+#include "includes.h"
+#include "system/dir.h"
+#include "system/filesys.h"
+#include "cifsposix.h"
+#include "system/time.h"
+#include "../lib/util/dlinklist.h"
+#include "ntvfs/ntvfs.h"
+#include "ntvfs/cifs_posix_cli/proto.h"
+
+#ifndef O_DIRECTORY
+#define O_DIRECTORY 0
+#endif
+
+#define CHECK_READ_ONLY(req) do { if (share_bool_option(ntvfs->ctx->config, SHARE_READONLY, true)) return NT_STATUS_ACCESS_DENIED; } while (0)
+
+/*
+ connect to a share - used when a tree_connect operation comes
+ in. For a disk based backend we needs to ensure that the base
+ directory exists (tho it doesn't need to be accessible by the user,
+ that comes later)
+*/
+static NTSTATUS cifspsx_connect(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, const char *sharename)
+{
+ struct stat st;
+ struct cifspsx_private *p;
+ struct share_config *scfg = ntvfs->ctx->config;
+
+ p = talloc(ntvfs, struct cifspsx_private);
+ NT_STATUS_HAVE_NO_MEMORY(p);
+ p->ntvfs = ntvfs;
+ p->next_search_handle = 0;
+ p->connectpath = talloc_strdup(p, share_string_option(scfg, SHARE_PATH, ""));
+ p->open_files = NULL;
+ p->search = NULL;
+
+ /* the directory must exist */
+ if (stat(p->connectpath, &st) != 0 || !S_ISDIR(st.st_mode)) {
+ DEBUG(0,("'%s' is not a directory, when connecting to [%s]\n",
+ p->connectpath, sharename));
+ return NT_STATUS_BAD_NETWORK_NAME;
+ }
+
+ ntvfs->ctx->fs_type = talloc_strdup(ntvfs->ctx, "NTFS");
+ NT_STATUS_HAVE_NO_MEMORY(ntvfs->ctx->fs_type);
+ ntvfs->ctx->dev_type = talloc_strdup(ntvfs->ctx, "A:");
+ NT_STATUS_HAVE_NO_MEMORY(ntvfs->ctx->dev_type);
+
+ ntvfs->private_data = p;
+
+ DEBUG(0,("WARNING: ntvfs cifs posix: connect to share [%s] with ROOT privileges!!!\n",sharename));
+
+ return NT_STATUS_OK;
+}
+
+/*
+ disconnect from a share
+*/
+static NTSTATUS cifspsx_disconnect(struct ntvfs_module_context *ntvfs)
+{
+ return NT_STATUS_OK;
+}
+
+/*
+ find open file handle given fd
+*/
+static struct cifspsx_file *find_fd(struct cifspsx_private *cp, struct ntvfs_handle *handle)
+{
+ struct cifspsx_file *f;
+ void *p;
+
+ p = ntvfs_handle_get_backend_data(handle, cp->ntvfs);
+ if (!p) return NULL;
+
+ f = talloc_get_type(p, struct cifspsx_file);
+ if (!f) return NULL;
+
+ return f;
+}
+
+/*
+ delete a file - the dirtype specifies the file types to include in the search.
+ The name can contain CIFS wildcards, but rarely does (except with OS/2 clients)
+*/
+static NTSTATUS cifspsx_unlink(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_unlink *unl)
+{
+ char *unix_path;
+
+ CHECK_READ_ONLY(req);
+
+ unix_path = cifspsx_unix_path(ntvfs, req, unl->unlink.in.pattern);
+
+ /* ignoring wildcards ... */
+ if (unlink(unix_path) == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ ioctl interface - we don't do any
+*/
+static NTSTATUS cifspsx_ioctl(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_ioctl *io)
+{
+ return NT_STATUS_INVALID_PARAMETER;
+}
+
+/*
+ check if a directory exists
+*/
+static NTSTATUS cifspsx_chkpath(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_chkpath *cp)
+{
+ char *unix_path;
+ struct stat st;
+
+ unix_path = cifspsx_unix_path(ntvfs, req, cp->chkpath.in.path);
+
+ if (stat(unix_path, &st) == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ if (!S_ISDIR(st.st_mode)) {
+ return NT_STATUS_NOT_A_DIRECTORY;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ build a file_id from a stat struct
+*/
+static uint64_t cifspsx_file_id(struct stat *st)
+{
+ uint64_t ret = st->st_ino;
+ ret <<= 32;
+ ret |= st->st_dev;
+ return ret;
+}
+
+/*
+ approximately map a struct stat to a generic fileinfo struct
+*/
+static NTSTATUS cifspsx_map_fileinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_fileinfo *info,
+ struct stat *st, const char *unix_path)
+{
+ struct cifspsx_dir *dir = NULL;
+ char *pattern = NULL;
+ int i;
+ const char *s, *short_name;
+
+ s = strrchr(unix_path, '/');
+ if (s) {
+ short_name = s+1;
+ } else {
+ short_name = "";
+ }
+
+ asprintf(&pattern, "%s:*", unix_path);
+
+ if (pattern) {
+ dir = cifspsx_list_unix(req, req, pattern);
+ }
+
+ unix_to_nt_time(&info->generic.out.create_time, st->st_ctime);
+ unix_to_nt_time(&info->generic.out.access_time, st->st_atime);
+ unix_to_nt_time(&info->generic.out.write_time, st->st_mtime);
+ unix_to_nt_time(&info->generic.out.change_time, st->st_mtime);
+ info->generic.out.alloc_size = st->st_size;
+ info->generic.out.size = st->st_size;
+ info->generic.out.attrib = cifspsx_unix_to_dos_attrib(st->st_mode);
+ info->generic.out.alloc_size = st->st_blksize * st->st_blocks;
+ info->generic.out.nlink = st->st_nlink;
+ info->generic.out.directory = S_ISDIR(st->st_mode) ? 1 : 0;
+ info->generic.out.file_id = cifspsx_file_id(st);
+ /* REWRITE: TODO stuff in here */
+ info->generic.out.delete_pending = 0;
+ info->generic.out.ea_size = 0;
+ info->generic.out.num_eas = 0;
+ info->generic.out.fname.s = talloc_strdup(req, short_name);
+ info->generic.out.alt_fname.s = talloc_strdup(req, short_name);
+ info->generic.out.compressed_size = 0;
+ info->generic.out.format = 0;
+ info->generic.out.unit_shift = 0;
+ info->generic.out.chunk_shift = 0;
+ info->generic.out.cluster_shift = 0;
+
+ info->generic.out.access_flags = 0;
+ info->generic.out.position = 0;
+ info->generic.out.mode = 0;
+ info->generic.out.alignment_requirement = 0;
+ info->generic.out.reparse_tag = 0;
+ info->generic.out.num_streams = 0;
+ /* setup a single data stream */
+ info->generic.out.num_streams = 1 + (dir?dir->count:0);
+ info->generic.out.streams = talloc_array(req,
+ struct stream_struct,
+ info->generic.out.num_streams);
+ if (!info->generic.out.streams) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ info->generic.out.streams[0].size = st->st_size;
+ info->generic.out.streams[0].alloc_size = st->st_size;
+ info->generic.out.streams[0].stream_name.s = talloc_strdup(req,"::$DATA");
+
+ for (i=0;dir && i<dir->count;i++) {
+ s = strchr(dir->files[i].name, ':');
+ info->generic.out.streams[1+i].size = dir->files[i].st.st_size;
+ info->generic.out.streams[1+i].alloc_size = dir->files[i].st.st_size;
+ info->generic.out.streams[1+i].stream_name.s = s?s:dir->files[i].name;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ return info on a pathname
+*/
+static NTSTATUS cifspsx_qpathinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_fileinfo *info)
+{
+ char *unix_path;
+ struct stat st;
+
+ DEBUG(19,("cifspsx_qpathinfo: file %s level 0x%x\n", info->generic.in.file.path, info->generic.level));
+ if (info->generic.level != RAW_FILEINFO_GENERIC) {
+ return ntvfs_map_qpathinfo(ntvfs, req, info);
+ }
+
+ unix_path = cifspsx_unix_path(ntvfs, req, info->generic.in.file.path);
+ DEBUG(19,("cifspsx_qpathinfo: file %s\n", unix_path));
+ if (stat(unix_path, &st) == -1) {
+ DEBUG(19,("cifspsx_qpathinfo: file %s errno=%d\n", unix_path, errno));
+ return map_nt_error_from_unix(errno);
+ }
+ DEBUG(19,("cifspsx_qpathinfo: file %s, stat done\n", unix_path));
+ return cifspsx_map_fileinfo(ntvfs, req, info, &st, unix_path);
+}
+
+/*
+ query info on a open file
+*/
+static NTSTATUS cifspsx_qfileinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_fileinfo *info)
+{
+ struct cifspsx_private *p = ntvfs->private_data;
+ struct cifspsx_file *f;
+ struct stat st;
+
+ if (info->generic.level != RAW_FILEINFO_GENERIC) {
+ return ntvfs_map_qfileinfo(ntvfs, req, info);
+ }
+
+ f = find_fd(p, info->generic.in.file.ntvfs);
+ if (!f) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ if (fstat(f->fd, &st) == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ return cifspsx_map_fileinfo(ntvfs, req,info, &st, f->name);
+}
+
+
+/*
+ open a file
+*/
+static NTSTATUS cifspsx_open(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_open *io)
+{
+ struct cifspsx_private *p = ntvfs->private_data;
+ char *unix_path;
+ struct stat st;
+ int fd, flags;
+ struct cifspsx_file *f;
+ int create_flags, rdwr_flags;
+ bool readonly;
+ NTSTATUS status;
+ struct ntvfs_handle *handle;
+
+ if (io->generic.level != RAW_OPEN_GENERIC) {
+ return ntvfs_map_open(ntvfs, req, io);
+ }
+
+ readonly = share_bool_option(ntvfs->ctx->config, SHARE_READONLY, SHARE_READONLY_DEFAULT);
+ if (readonly) {
+ create_flags = 0;
+ rdwr_flags = O_RDONLY;
+ } else {
+ create_flags = O_CREAT;
+ rdwr_flags = O_RDWR;
+ }
+
+ unix_path = cifspsx_unix_path(ntvfs, req, io->ntcreatex.in.fname);
+
+ switch (io->generic.in.open_disposition) {
+ case NTCREATEX_DISP_SUPERSEDE:
+ case NTCREATEX_DISP_OVERWRITE_IF:
+ flags = create_flags | O_TRUNC;
+ break;
+ case NTCREATEX_DISP_OPEN:
+ case NTCREATEX_DISP_OVERWRITE:
+ flags = 0;
+ break;
+ case NTCREATEX_DISP_CREATE:
+ flags = create_flags | O_EXCL;
+ break;
+ case NTCREATEX_DISP_OPEN_IF:
+ flags = create_flags;
+ break;
+ default:
+ flags = 0;
+ break;
+ }
+
+ flags |= rdwr_flags;
+
+ if (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY) {
+ flags = O_RDONLY | O_DIRECTORY;
+ if (readonly) {
+ goto do_open;
+ }
+ switch (io->generic.in.open_disposition) {
+ case NTCREATEX_DISP_CREATE:
+ if (mkdir(unix_path, 0755) == -1) {
+ DEBUG(9,("cifspsx_open: mkdir %s errno=%d\n", unix_path, errno));
+ return map_nt_error_from_unix(errno);
+ }
+ break;
+ case NTCREATEX_DISP_OPEN_IF:
+ if (mkdir(unix_path, 0755) == -1 && errno != EEXIST) {
+ DEBUG(9,("cifspsx_open: mkdir %s errno=%d\n", unix_path, errno));
+ return map_nt_error_from_unix(errno);
+ }
+ break;
+ }
+ }
+
+do_open:
+ fd = open(unix_path, flags, 0644);
+ if (fd == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ if (fstat(fd, &st) == -1) {
+ DEBUG(9,("cifspsx_open: fstat errno=%d\n", errno));
+ close(fd);
+ return map_nt_error_from_unix(errno);
+ }
+
+ status = ntvfs_handle_new(ntvfs, req, &handle);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ f = talloc(handle, struct cifspsx_file);
+ NT_STATUS_HAVE_NO_MEMORY(f);
+ f->fd = fd;
+ f->name = talloc_strdup(f, unix_path);
+ NT_STATUS_HAVE_NO_MEMORY(f->name);
+
+ DLIST_ADD(p->open_files, f);
+
+ status = ntvfs_handle_set_backend_data(handle, ntvfs, f);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ ZERO_STRUCT(io->generic.out);
+
+ unix_to_nt_time(&io->generic.out.create_time, st.st_ctime);
+ unix_to_nt_time(&io->generic.out.access_time, st.st_atime);
+ unix_to_nt_time(&io->generic.out.write_time, st.st_mtime);
+ unix_to_nt_time(&io->generic.out.change_time, st.st_mtime);
+ io->generic.out.file.ntvfs = handle;
+ io->generic.out.alloc_size = st.st_size;
+ io->generic.out.size = st.st_size;
+ io->generic.out.attrib = cifspsx_unix_to_dos_attrib(st.st_mode);
+ io->generic.out.is_directory = S_ISDIR(st.st_mode) ? 1 : 0;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ create a directory
+*/
+static NTSTATUS cifspsx_mkdir(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_mkdir *md)
+{
+ char *unix_path;
+
+ CHECK_READ_ONLY(req);
+
+ if (md->generic.level != RAW_MKDIR_MKDIR) {
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ unix_path = cifspsx_unix_path(ntvfs, req, md->mkdir.in.path);
+
+ if (mkdir(unix_path, 0777) == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ remove a directory
+*/
+static NTSTATUS cifspsx_rmdir(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, struct smb_rmdir *rd)
+{
+ char *unix_path;
+
+ CHECK_READ_ONLY(req);
+
+ unix_path = cifspsx_unix_path(ntvfs, req, rd->in.path);
+
+ if (rmdir(unix_path) == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ rename a set of files
+*/
+static NTSTATUS cifspsx_rename(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_rename *ren)
+{
+ char *unix_path1, *unix_path2;
+
+ CHECK_READ_ONLY(req);
+
+ if (ren->generic.level != RAW_RENAME_RENAME) {
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ unix_path1 = cifspsx_unix_path(ntvfs, req, ren->rename.in.pattern1);
+ unix_path2 = cifspsx_unix_path(ntvfs, req, ren->rename.in.pattern2);
+
+ if (rename(unix_path1, unix_path2) == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ copy a set of files
+*/
+static NTSTATUS cifspsx_copy(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, struct smb_copy *cp)
+{
+ return NT_STATUS_NOT_SUPPORTED;
+}
+
+/*
+ read from a file
+*/
+static NTSTATUS cifspsx_read(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_read *rd)
+{
+ struct cifspsx_private *p = ntvfs->private_data;
+ struct cifspsx_file *f;
+ ssize_t ret;
+
+ if (rd->generic.level != RAW_READ_READX) {
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ f = find_fd(p, rd->readx.in.file.ntvfs);
+ if (!f) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ ret = pread(f->fd,
+ rd->readx.out.data,
+ rd->readx.in.maxcnt,
+ rd->readx.in.offset);
+ if (ret == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ rd->readx.out.nread = ret;
+ rd->readx.out.remaining = 0; /* should fill this in? */
+ rd->readx.out.compaction_mode = 0;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ write to a file
+*/
+static NTSTATUS cifspsx_write(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_write *wr)
+{
+ struct cifspsx_private *p = ntvfs->private_data;
+ struct cifspsx_file *f;
+ ssize_t ret;
+
+ if (wr->generic.level != RAW_WRITE_WRITEX) {
+ return ntvfs_map_write(ntvfs, req, wr);
+ }
+
+ CHECK_READ_ONLY(req);
+
+ f = find_fd(p, wr->writex.in.file.ntvfs);
+ if (!f) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ ret = pwrite(f->fd,
+ wr->writex.in.data,
+ wr->writex.in.count,
+ wr->writex.in.offset);
+ if (ret == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ wr->writex.out.nwritten = ret;
+ wr->writex.out.remaining = 0; /* should fill this in? */
+
+ return NT_STATUS_OK;
+}
+
+/*
+ seek in a file
+*/
+static NTSTATUS cifspsx_seek(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_seek *io)
+{
+ return NT_STATUS_NOT_SUPPORTED;
+}
+
+/*
+ flush a file
+*/
+static NTSTATUS cifspsx_flush(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_flush *io)
+{
+ struct cifspsx_private *p = ntvfs->private_data;
+ struct cifspsx_file *f;
+
+ switch (io->generic.level) {
+ case RAW_FLUSH_FLUSH:
+ case RAW_FLUSH_SMB2:
+ /* ignore the additional unknown option in SMB2 */
+ f = find_fd(p, io->generic.in.file.ntvfs);
+ if (!f) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+ fsync(f->fd);
+ return NT_STATUS_OK;
+
+ case RAW_FLUSH_ALL:
+ for (f=p->open_files;f;f=f->next) {
+ fsync(f->fd);
+ }
+ return NT_STATUS_OK;
+ }
+
+ return NT_STATUS_INVALID_LEVEL;
+}
+
+/*
+ close a file
+*/
+static NTSTATUS cifspsx_close(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_close *io)
+{
+ struct cifspsx_private *p = ntvfs->private_data;
+ struct cifspsx_file *f;
+
+ if (io->generic.level != RAW_CLOSE_CLOSE) {
+ /* we need a mapping function */
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ f = find_fd(p, io->close.in.file.ntvfs);
+ if (!f) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ if (close(f->fd) == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ DLIST_REMOVE(p->open_files, f);
+ talloc_free(f->name);
+ talloc_free(f);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ exit - closing files
+*/
+static NTSTATUS cifspsx_exit(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req)
+{
+ return NT_STATUS_NOT_SUPPORTED;
+}
+
+/*
+ logoff - closing files
+*/
+static NTSTATUS cifspsx_logoff(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req)
+{
+ return NT_STATUS_NOT_SUPPORTED;
+}
+
+/*
+ setup for an async call
+*/
+static NTSTATUS cifspsx_async_setup(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ void *private_data)
+{
+ return NT_STATUS_OK;
+}
+
+/*
+ cancel an async call
+*/
+static NTSTATUS cifspsx_cancel(struct ntvfs_module_context *ntvfs, struct ntvfs_request *req)
+{
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+/*
+ lock a byte range
+*/
+static NTSTATUS cifspsx_lock(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_lock *lck)
+{
+ DEBUG(0,("REWRITE: not doing byte range locking!\n"));
+ return NT_STATUS_OK;
+}
+
+/*
+ set info on a pathname
+*/
+static NTSTATUS cifspsx_setpathinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_setfileinfo *st)
+{
+ CHECK_READ_ONLY(req);
+
+ return NT_STATUS_NOT_SUPPORTED;
+}
+
+/*
+ set info on a open file
+*/
+static NTSTATUS cifspsx_setfileinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_setfileinfo *info)
+{
+ struct cifspsx_private *p = ntvfs->private_data;
+ struct cifspsx_file *f;
+ struct utimbuf unix_times;
+
+ CHECK_READ_ONLY(req);
+
+ f = find_fd(p, info->generic.in.file.ntvfs);
+ if (!f) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ switch (info->generic.level) {
+ case RAW_SFILEINFO_END_OF_FILE_INFO:
+ case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
+ if (ftruncate(f->fd,
+ info->end_of_file_info.in.size) == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+ break;
+ case RAW_SFILEINFO_SETATTRE:
+ unix_times.actime = info->setattre.in.access_time;
+ unix_times.modtime = info->setattre.in.write_time;
+
+ if (unix_times.actime == 0 && unix_times.modtime == 0) {
+ break;
+ }
+
+ /* set modify time = to access time if modify time was 0 */
+ if (unix_times.actime != 0 && unix_times.modtime == 0) {
+ unix_times.modtime = unix_times.actime;
+ }
+
+ /* Set the date on this file */
+ if (cifspsx_file_utime(f->fd, &unix_times) != 0) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ break;
+ default:
+ DEBUG(2,("cifspsx_setfileinfo: level %d not implemented\n",
+ info->generic.level));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return NT_STATUS_OK;
+}
+
+
+/*
+ return filesystem space info
+*/
+static NTSTATUS cifspsx_fsinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_fsinfo *fs)
+{
+ struct cifspsx_private *p = ntvfs->private_data;
+ struct stat st;
+
+ if (fs->generic.level != RAW_QFS_GENERIC) {
+ return ntvfs_map_fsinfo(ntvfs, req, fs);
+ }
+
+ if (sys_fsusage(p->connectpath,
+ &fs->generic.out.blocks_free,
+ &fs->generic.out.blocks_total) == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ fs->generic.out.block_size = 512;
+
+ if (stat(p->connectpath, &st) != 0) {
+ return NT_STATUS_DISK_CORRUPT_ERROR;
+ }
+
+ fs->generic.out.fs_id = st.st_ino;
+ unix_to_nt_time(&fs->generic.out.create_time, st.st_ctime);
+ fs->generic.out.serial_number = st.st_ino;
+ fs->generic.out.fs_attr = 0;
+ fs->generic.out.max_file_component_length = 255;
+ fs->generic.out.device_type = 0;
+ fs->generic.out.device_characteristics = 0;
+ fs->generic.out.quota_soft = 0;
+ fs->generic.out.quota_hard = 0;
+ fs->generic.out.quota_flags = 0;
+ fs->generic.out.volume_name = talloc_strdup(req, ntvfs->ctx->config->name);
+ fs->generic.out.fs_type = ntvfs->ctx->fs_type;
+
+ return NT_STATUS_OK;
+}
+
+#if 0
+/*
+ return filesystem attribute info
+*/
+static NTSTATUS cifspsx_fsattr(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_fsattr *fs)
+{
+ struct stat st;
+ struct cifspsx_private *p = ntvfs->private_data;
+
+ if (fs->generic.level != RAW_FSATTR_GENERIC) {
+ return ntvfs_map_fsattr(ntvfs, req, fs);
+ }
+
+ if (stat(p->connectpath, &st) == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ unix_to_nt_time(&fs->generic.out.create_time, st.st_ctime);
+ fs->generic.out.fs_attr =
+ FILE_CASE_PRESERVED_NAMES |
+ FILE_CASE_SENSITIVE_SEARCH |
+ FILE_PERSISTENT_ACLS;
+ fs->generic.out.max_file_component_length = 255;
+ fs->generic.out.serial_number = 1;
+ fs->generic.out.fs_type = talloc_strdup(req, "NTFS");
+ fs->generic.out.volume_name = talloc_strdup(req,
+ lp_servicename(req->tcon->service));
+
+ return NT_STATUS_OK;
+}
+#endif
+
+/*
+ return print queue info
+*/
+static NTSTATUS cifspsx_lpq(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_lpq *lpq)
+{
+ return NT_STATUS_NOT_SUPPORTED;
+}
+
+/*
+ list files in a directory matching a wildcard pattern
+*/
+static NTSTATUS cifspsx_search_first(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_search_first *io,
+ void *search_private,
+ bool (*callback)(void *, const union smb_search_data *))
+{
+ struct cifspsx_dir *dir;
+ int i;
+ struct cifspsx_private *p = ntvfs->private_data;
+ struct search_state *search;
+ union smb_search_data file;
+ uint_t max_count;
+
+ if (io->generic.level != RAW_SEARCH_TRANS2) {
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ if (io->generic.data_level != RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO) {
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ search = talloc_zero(p, struct search_state);
+ if (!search) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ max_count = io->t2ffirst.in.max_count;
+
+ dir = cifspsx_list(ntvfs, req, io->t2ffirst.in.pattern);
+ if (!dir) {
+ return NT_STATUS_FOOBAR;
+ }
+
+ search->handle = p->next_search_handle;
+ search->dir = dir;
+
+ if (dir->count < max_count) {
+ max_count = dir->count;
+ }
+
+ for (i=0; i < max_count;i++) {
+ ZERO_STRUCT(file);
+ unix_to_nt_time(&file.both_directory_info.create_time, dir->files[i].st.st_ctime);
+ unix_to_nt_time(&file.both_directory_info.access_time, dir->files[i].st.st_atime);
+ unix_to_nt_time(&file.both_directory_info.write_time, dir->files[i].st.st_mtime);
+ unix_to_nt_time(&file.both_directory_info.change_time, dir->files[i].st.st_mtime);
+ file.both_directory_info.name.s = dir->files[i].name;
+ file.both_directory_info.short_name.s = dir->files[i].name;
+ file.both_directory_info.size = dir->files[i].st.st_size;
+ file.both_directory_info.attrib = cifspsx_unix_to_dos_attrib(dir->files[i].st.st_mode);
+
+ if (!callback(search_private, &file)) {
+ break;
+ }
+ }
+
+ search->current_index = i;
+
+ io->t2ffirst.out.count = i;
+ io->t2ffirst.out.handle = search->handle;
+ io->t2ffirst.out.end_of_search = (i == dir->count) ? 1 : 0;
+
+ /* work out if we are going to keep the search state */
+ if ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
+ ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) && (i == dir->count))) {
+ talloc_free(search);
+ } else {
+ p->next_search_handle++;
+ DLIST_ADD(p->search, search);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/* continue a search */
+static NTSTATUS cifspsx_search_next(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_search_next *io,
+ void *search_private,
+ bool (*callback)(void *, const union smb_search_data *))
+{
+ struct cifspsx_dir *dir;
+ int i;
+ struct cifspsx_private *p = ntvfs->private_data;
+ struct search_state *search;
+ union smb_search_data file;
+ uint_t max_count;
+
+ if (io->generic.level != RAW_SEARCH_TRANS2) {
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ if (io->generic.data_level != RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO) {
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ for (search=p->search; search; search = search->next) {
+ if (search->handle == io->t2fnext.in.handle) break;
+ }
+
+ if (!search) {
+ /* we didn't find the search handle */
+ return NT_STATUS_FOOBAR;
+ }
+
+ dir = search->dir;
+
+ /* the client might be asking for something other than just continuing
+ with the search */
+ if (!(io->t2fnext.in.flags & FLAG_TRANS2_FIND_CONTINUE) &&
+ (io->t2fnext.in.flags & FLAG_TRANS2_FIND_REQUIRE_RESUME) &&
+ io->t2fnext.in.last_name && *io->t2fnext.in.last_name) {
+ /* look backwards first */
+ for (i=search->current_index; i > 0; i--) {
+ if (strcmp(io->t2fnext.in.last_name, dir->files[i-1].name) == 0) {
+ search->current_index = i;
+ goto found;
+ }
+ }
+
+ /* then look forwards */
+ for (i=search->current_index+1; i <= dir->count; i++) {
+ if (strcmp(io->t2fnext.in.last_name, dir->files[i-1].name) == 0) {
+ search->current_index = i;
+ goto found;
+ }
+ }
+ }
+
+found:
+ max_count = search->current_index + io->t2fnext.in.max_count;
+
+ if (max_count > dir->count) {
+ max_count = dir->count;
+ }
+
+ for (i = search->current_index; i < max_count;i++) {
+ ZERO_STRUCT(file);
+ unix_to_nt_time(&file.both_directory_info.create_time, dir->files[i].st.st_ctime);
+ unix_to_nt_time(&file.both_directory_info.access_time, dir->files[i].st.st_atime);
+ unix_to_nt_time(&file.both_directory_info.write_time, dir->files[i].st.st_mtime);
+ unix_to_nt_time(&file.both_directory_info.change_time, dir->files[i].st.st_mtime);
+ file.both_directory_info.name.s = dir->files[i].name;
+ file.both_directory_info.short_name.s = dir->files[i].name;
+ file.both_directory_info.size = dir->files[i].st.st_size;
+ file.both_directory_info.attrib = cifspsx_unix_to_dos_attrib(dir->files[i].st.st_mode);
+
+ if (!callback(search_private, &file)) {
+ break;
+ }
+ }
+
+ io->t2fnext.out.count = i - search->current_index;
+ io->t2fnext.out.end_of_search = (i == dir->count) ? 1 : 0;
+
+ search->current_index = i;
+
+ /* work out if we are going to keep the search state */
+ if ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
+ ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) && (i == dir->count))) {
+ DLIST_REMOVE(p->search, search);
+ talloc_free(search);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/* close a search */
+static NTSTATUS cifspsx_search_close(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_search_close *io)
+{
+ struct cifspsx_private *p = ntvfs->private_data;
+ struct search_state *search;
+
+ for (search=p->search; search; search = search->next) {
+ if (search->handle == io->findclose.in.handle) break;
+ }
+
+ if (!search) {
+ /* we didn't find the search handle */
+ return NT_STATUS_FOOBAR;
+ }
+
+ DLIST_REMOVE(p->search, search);
+ talloc_free(search);
+
+ return NT_STATUS_OK;
+}
+
+/* SMBtrans - not used on file shares */
+static NTSTATUS cifspsx_trans(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, struct smb_trans2 *trans2)
+{
+ return NT_STATUS_ACCESS_DENIED;
+}
+
+
+/*
+ initialialise the POSIX disk backend, registering ourselves with the ntvfs subsystem
+ */
+NTSTATUS ntvfs_cifs_posix_init(void)
+{
+ NTSTATUS ret;
+ struct ntvfs_ops ops;
+ NTVFS_CURRENT_CRITICAL_SIZES(vers);
+
+ ZERO_STRUCT(ops);
+
+ /* fill in all the operations */
+ ops.connect = cifspsx_connect;
+ ops.disconnect = cifspsx_disconnect;
+ ops.unlink = cifspsx_unlink;
+ ops.chkpath = cifspsx_chkpath;
+ ops.qpathinfo = cifspsx_qpathinfo;
+ ops.setpathinfo = cifspsx_setpathinfo;
+ ops.open = cifspsx_open;
+ ops.mkdir = cifspsx_mkdir;
+ ops.rmdir = cifspsx_rmdir;
+ ops.rename = cifspsx_rename;
+ ops.copy = cifspsx_copy;
+ ops.ioctl = cifspsx_ioctl;
+ ops.read = cifspsx_read;
+ ops.write = cifspsx_write;
+ ops.seek = cifspsx_seek;
+ ops.flush = cifspsx_flush;
+ ops.close = cifspsx_close;
+ ops.exit = cifspsx_exit;
+ ops.lock = cifspsx_lock;
+ ops.setfileinfo = cifspsx_setfileinfo;
+ ops.qfileinfo = cifspsx_qfileinfo;
+ ops.fsinfo = cifspsx_fsinfo;
+ ops.lpq = cifspsx_lpq;
+ ops.search_first = cifspsx_search_first;
+ ops.search_next = cifspsx_search_next;
+ ops.search_close = cifspsx_search_close;
+ ops.trans = cifspsx_trans;
+ ops.logoff = cifspsx_logoff;
+ ops.async_setup = cifspsx_async_setup;
+ ops.cancel = cifspsx_cancel;
+
+ /* register ourselves with the NTVFS subsystem. We register
+ under names 'cifsposix'
+ */
+
+ ops.type = NTVFS_DISK;
+ ops.name = "cifsposix";
+ ret = ntvfs_register(&ops, &vers);
+
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("Failed to register cifs posix backend with name: %s!\n",
+ ops.name));
+ }
+
+ return ret;
+}
diff --git a/source4/ntvfs/common/brlock.c b/source4/ntvfs/common/brlock.c
new file mode 100644
index 0000000000..a5bc5c1db4
--- /dev/null
+++ b/source4/ntvfs/common/brlock.c
@@ -0,0 +1,127 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ generic byte range locking code
+
+ Copyright (C) Andrew Tridgell 1992-2004
+ Copyright (C) Jeremy Allison 1992-2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* This module implements a tdb based byte range locking service,
+ replacing the fcntl() based byte range locking previously
+ used. This allows us to provide the same semantics as NT */
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "../tdb/include/tdb.h"
+#include "messaging/messaging.h"
+#include "lib/messaging/irpc.h"
+#include "libcli/libcli.h"
+#include "cluster/cluster.h"
+#include "ntvfs/common/ntvfs_common.h"
+
+static const struct brlock_ops *ops;
+
+/*
+ set the brl backend ops
+*/
+void brl_set_ops(const struct brlock_ops *new_ops)
+{
+ ops = new_ops;
+}
+
+/*
+ Open up the brlock database. Close it down using talloc_free(). We
+ need the messaging_ctx to allow for pending lock notifications.
+*/
+struct brl_context *brl_init(TALLOC_CTX *mem_ctx, struct server_id server,
+ struct loadparm_context *lp_ctx,
+ struct messaging_context *messaging_ctx)
+{
+ if (ops == NULL) {
+ brl_tdb_init_ops();
+ }
+ return ops->brl_init(mem_ctx, server, lp_ctx, messaging_ctx);
+}
+
+struct brl_handle *brl_create_handle(TALLOC_CTX *mem_ctx, struct ntvfs_handle *ntvfs, DATA_BLOB *file_key)
+{
+ return ops->brl_create_handle(mem_ctx, ntvfs, file_key);
+}
+
+/*
+ Lock a range of bytes. The lock_type can be a PENDING_*_LOCK, in
+ which case a real lock is first tried, and if that fails then a
+ pending lock is created. When the pending lock is triggered (by
+ someone else closing an overlapping lock range) a messaging
+ notification is sent, identified by the notify_ptr
+*/
+NTSTATUS brl_lock(struct brl_context *brl,
+ struct brl_handle *brlh,
+ uint32_t smbpid,
+ uint64_t start, uint64_t size,
+ enum brl_type lock_type,
+ void *notify_ptr)
+{
+ return ops->brl_lock(brl, brlh, smbpid, start, size, lock_type, notify_ptr);
+}
+
+
+/*
+ Unlock a range of bytes.
+*/
+NTSTATUS brl_unlock(struct brl_context *brl,
+ struct brl_handle *brlh,
+ uint32_t smbpid,
+ uint64_t start, uint64_t size)
+{
+ return ops->brl_unlock(brl, brlh, smbpid, start, size);
+}
+
+/*
+ remove a pending lock. This is called when the caller has either
+ given up trying to establish a lock or when they have succeeded in
+ getting it. In either case they no longer need to be notified.
+*/
+NTSTATUS brl_remove_pending(struct brl_context *brl,
+ struct brl_handle *brlh,
+ void *notify_ptr)
+{
+ return ops->brl_remove_pending(brl, brlh, notify_ptr);
+}
+
+
+/*
+ Test if we are allowed to perform IO on a region of an open file
+*/
+NTSTATUS brl_locktest(struct brl_context *brl,
+ struct brl_handle *brlh,
+ uint32_t smbpid,
+ uint64_t start, uint64_t size,
+ enum brl_type lock_type)
+{
+ return ops->brl_locktest(brl, brlh, smbpid, start, size, lock_type);
+}
+
+
+/*
+ Remove any locks associated with a open file.
+*/
+NTSTATUS brl_close(struct brl_context *brl,
+ struct brl_handle *brlh)
+{
+ return ops->brl_close(brl, brlh);
+}
diff --git a/source4/ntvfs/common/brlock.h b/source4/ntvfs/common/brlock.h
new file mode 100644
index 0000000000..75f142b6f6
--- /dev/null
+++ b/source4/ntvfs/common/brlock.h
@@ -0,0 +1,55 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ generic byte range locking code - common include
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "libcli/libcli.h"
+
+struct brlock_ops {
+ struct brl_context *(*brl_init)(TALLOC_CTX *, struct server_id ,
+ struct loadparm_context *lp_ctx,
+ struct messaging_context *);
+ struct brl_handle *(*brl_create_handle)(TALLOC_CTX *, struct ntvfs_handle *, DATA_BLOB *);
+ NTSTATUS (*brl_lock)(struct brl_context *,
+ struct brl_handle *,
+ uint32_t ,
+ uint64_t , uint64_t ,
+ enum brl_type ,
+ void *);
+ NTSTATUS (*brl_unlock)(struct brl_context *,
+ struct brl_handle *,
+ uint32_t ,
+ uint64_t , uint64_t );
+ NTSTATUS (*brl_remove_pending)(struct brl_context *,
+ struct brl_handle *,
+ void *);
+ NTSTATUS (*brl_locktest)(struct brl_context *,
+ struct brl_handle *,
+ uint32_t ,
+ uint64_t , uint64_t ,
+ enum brl_type );
+ NTSTATUS (*brl_close)(struct brl_context *,
+ struct brl_handle *);
+};
+
+
+void brl_set_ops(const struct brlock_ops *new_ops);
+void brl_tdb_init_ops(void);
+void brl_ctdb_init_ops(void);
+
diff --git a/source4/ntvfs/common/brlock_tdb.c b/source4/ntvfs/common/brlock_tdb.c
new file mode 100644
index 0000000000..299400b96c
--- /dev/null
+++ b/source4/ntvfs/common/brlock_tdb.c
@@ -0,0 +1,737 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ generic byte range locking code - tdb backend
+
+ Copyright (C) Andrew Tridgell 1992-2006
+ Copyright (C) Jeremy Allison 1992-2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* This module implements a tdb based byte range locking service,
+ replacing the fcntl() based byte range locking previously
+ used. This allows us to provide the same semantics as NT */
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "../tdb/include/tdb.h"
+#include "messaging/messaging.h"
+#include "tdb_wrap.h"
+#include "lib/messaging/irpc.h"
+#include "libcli/libcli.h"
+#include "cluster/cluster.h"
+#include "ntvfs/common/brlock.h"
+#include "ntvfs/ntvfs.h"
+#include "param/param.h"
+
+/*
+ in this module a "DATA_BLOB *file_key" is a blob that uniquely identifies
+ a file. For a local posix filesystem this will usually be a combination
+ of the device and inode numbers of the file, but it can be anything
+ that uniquely idetifies a file for locking purposes, as long
+ as it is applied consistently.
+*/
+
+/* this struct is typicaly attached to tcon */
+struct brl_context {
+ struct tdb_wrap *w;
+ struct server_id server;
+ struct messaging_context *messaging_ctx;
+};
+
+/*
+ the lock context contains the elements that define whether one
+ lock is the same as another lock
+*/
+struct lock_context {
+ struct server_id server;
+ uint32_t smbpid;
+ struct brl_context *ctx;
+};
+
+/* The data in brlock records is an unsorted linear array of these
+ records. It is unnecessary to store the count as tdb provides the
+ size of the record */
+struct lock_struct {
+ struct lock_context context;
+ struct ntvfs_handle *ntvfs;
+ uint64_t start;
+ uint64_t size;
+ enum brl_type lock_type;
+ void *notify_ptr;
+};
+
+/* this struct is attached to on oprn file handle */
+struct brl_handle {
+ DATA_BLOB key;
+ struct ntvfs_handle *ntvfs;
+ struct lock_struct last_lock;
+};
+
+/*
+ Open up the brlock.tdb database. Close it down using
+ talloc_free(). We need the messaging_ctx to allow for
+ pending lock notifications.
+*/
+static struct brl_context *brl_tdb_init(TALLOC_CTX *mem_ctx, struct server_id server,
+ struct loadparm_context *lp_ctx,
+ struct messaging_context *messaging_ctx)
+{
+ struct brl_context *brl;
+
+ brl = talloc(mem_ctx, struct brl_context);
+ if (brl == NULL) {
+ return NULL;
+ }
+
+ brl->w = cluster_tdb_tmp_open(brl, lp_ctx, "brlock.tdb", TDB_DEFAULT);
+ if (brl->w == NULL) {
+ talloc_free(brl);
+ return NULL;
+ }
+
+ brl->server = server;
+ brl->messaging_ctx = messaging_ctx;
+
+ return brl;
+}
+
+static struct brl_handle *brl_tdb_create_handle(TALLOC_CTX *mem_ctx, struct ntvfs_handle *ntvfs,
+ DATA_BLOB *file_key)
+{
+ struct brl_handle *brlh;
+
+ brlh = talloc(mem_ctx, struct brl_handle);
+ if (brlh == NULL) {
+ return NULL;
+ }
+
+ brlh->key = *file_key;
+ brlh->ntvfs = ntvfs;
+ ZERO_STRUCT(brlh->last_lock);
+
+ return brlh;
+}
+
+/*
+ see if two locking contexts are equal
+*/
+static bool brl_tdb_same_context(struct lock_context *ctx1, struct lock_context *ctx2)
+{
+ return (cluster_id_equal(&ctx1->server, &ctx2->server) &&
+ ctx1->smbpid == ctx2->smbpid &&
+ ctx1->ctx == ctx2->ctx);
+}
+
+/*
+ see if lck1 and lck2 overlap
+
+ lck1 is the existing lock. lck2 is the new lock we are
+ looking at adding
+*/
+static bool brl_tdb_overlap(struct lock_struct *lck1,
+ struct lock_struct *lck2)
+{
+ /* this extra check is not redundent - it copes with locks
+ that go beyond the end of 64 bit file space */
+ if (lck1->size != 0 &&
+ lck1->start == lck2->start &&
+ lck1->size == lck2->size) {
+ return true;
+ }
+
+ if (lck1->start >= (lck2->start+lck2->size) ||
+ lck2->start >= (lck1->start+lck1->size)) {
+ return false;
+ }
+
+ /* we have a conflict. Now check to see if lck1 really still
+ * exists, which involves checking if the process still
+ * exists. We leave this test to last as its the most
+ * expensive test, especially when we are clustered */
+ /* TODO: need to do this via a server_id_exists() call, which
+ * hasn't been written yet. When clustered this will need to
+ * call into ctdb */
+
+ return true;
+}
+
+/*
+ See if lock2 can be added when lock1 is in place.
+*/
+static bool brl_tdb_conflict(struct lock_struct *lck1,
+ struct lock_struct *lck2)
+{
+ /* pending locks don't conflict with anything */
+ if (lck1->lock_type >= PENDING_READ_LOCK ||
+ lck2->lock_type >= PENDING_READ_LOCK) {
+ return false;
+ }
+
+ if (lck1->lock_type == READ_LOCK && lck2->lock_type == READ_LOCK) {
+ return false;
+ }
+
+ if (brl_tdb_same_context(&lck1->context, &lck2->context) &&
+ lck2->lock_type == READ_LOCK && lck1->ntvfs == lck2->ntvfs) {
+ return false;
+ }
+
+ return brl_tdb_overlap(lck1, lck2);
+}
+
+
+/*
+ Check to see if this lock conflicts, but ignore our own locks on the
+ same fnum only.
+*/
+static bool brl_tdb_conflict_other(struct lock_struct *lck1, struct lock_struct *lck2)
+{
+ /* pending locks don't conflict with anything */
+ if (lck1->lock_type >= PENDING_READ_LOCK ||
+ lck2->lock_type >= PENDING_READ_LOCK) {
+ return false;
+ }
+
+ if (lck1->lock_type == READ_LOCK && lck2->lock_type == READ_LOCK)
+ return false;
+
+ /*
+ * note that incoming write calls conflict with existing READ
+ * locks even if the context is the same. JRA. See LOCKTEST7
+ * in smbtorture.
+ */
+ if (brl_tdb_same_context(&lck1->context, &lck2->context) &&
+ lck1->ntvfs == lck2->ntvfs &&
+ (lck2->lock_type == READ_LOCK || lck1->lock_type == WRITE_LOCK)) {
+ return false;
+ }
+
+ return brl_tdb_overlap(lck1, lck2);
+}
+
+
+/*
+ amazingly enough, w2k3 "remembers" whether the last lock failure
+ is the same as this one and changes its error code. I wonder if any
+ app depends on this?
+*/
+static NTSTATUS brl_tdb_lock_failed(struct brl_handle *brlh, struct lock_struct *lock)
+{
+ /*
+ * this function is only called for non pending lock!
+ */
+
+ /* in SMB2 mode always return NT_STATUS_LOCK_NOT_GRANTED! */
+ if (lock->ntvfs->ctx->protocol == PROTOCOL_SMB2) {
+ return NT_STATUS_LOCK_NOT_GRANTED;
+ }
+
+ /*
+ * if the notify_ptr is non NULL,
+ * it means that we're at the end of a pending lock
+ * and the real lock is requested after the timout went by
+ * In this case we need to remember the last_lock and always
+ * give FILE_LOCK_CONFLICT
+ */
+ if (lock->notify_ptr) {
+ brlh->last_lock = *lock;
+ return NT_STATUS_FILE_LOCK_CONFLICT;
+ }
+
+ /*
+ * amazing the little things you learn with a test
+ * suite. Locks beyond this offset (as a 64 bit
+ * number!) always generate the conflict error code,
+ * unless the top bit is set
+ */
+ if (lock->start >= 0xEF000000 && (lock->start >> 63) == 0) {
+ brlh->last_lock = *lock;
+ return NT_STATUS_FILE_LOCK_CONFLICT;
+ }
+
+ /*
+ * if the current lock matches the last failed lock on the file handle
+ * and starts at the same offset, then FILE_LOCK_CONFLICT should be returned
+ */
+ if (cluster_id_equal(&lock->context.server, &brlh->last_lock.context.server) &&
+ lock->context.ctx == brlh->last_lock.context.ctx &&
+ lock->ntvfs == brlh->last_lock.ntvfs &&
+ lock->start == brlh->last_lock.start) {
+ return NT_STATUS_FILE_LOCK_CONFLICT;
+ }
+
+ brlh->last_lock = *lock;
+ return NT_STATUS_LOCK_NOT_GRANTED;
+}
+
+/*
+ Lock a range of bytes. The lock_type can be a PENDING_*_LOCK, in
+ which case a real lock is first tried, and if that fails then a
+ pending lock is created. When the pending lock is triggered (by
+ someone else closing an overlapping lock range) a messaging
+ notification is sent, identified by the notify_ptr
+*/
+static NTSTATUS brl_tdb_lock(struct brl_context *brl,
+ struct brl_handle *brlh,
+ uint32_t smbpid,
+ uint64_t start, uint64_t size,
+ enum brl_type lock_type,
+ void *notify_ptr)
+{
+ TDB_DATA kbuf, dbuf;
+ int count=0, i;
+ struct lock_struct lock, *locks=NULL;
+ NTSTATUS status;
+
+ kbuf.dptr = brlh->key.data;
+ kbuf.dsize = brlh->key.length;
+
+ if (tdb_chainlock(brl->w->tdb, kbuf) != 0) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ /* if this is a pending lock, then with the chainlock held we
+ try to get the real lock. If we succeed then we don't need
+ to make it pending. This prevents a possible race condition
+ where the pending lock gets created after the lock that is
+ preventing the real lock gets removed */
+ if (lock_type >= PENDING_READ_LOCK) {
+ enum brl_type rw = (lock_type==PENDING_READ_LOCK? READ_LOCK : WRITE_LOCK);
+
+ /* here we need to force that the last_lock isn't overwritten */
+ lock = brlh->last_lock;
+ status = brl_tdb_lock(brl, brlh, smbpid, start, size, rw, NULL);
+ brlh->last_lock = lock;
+
+ if (NT_STATUS_IS_OK(status)) {
+ tdb_chainunlock(brl->w->tdb, kbuf);
+ return NT_STATUS_OK;
+ }
+ }
+
+ dbuf = tdb_fetch(brl->w->tdb, kbuf);
+
+ lock.context.smbpid = smbpid;
+ lock.context.server = brl->server;
+ lock.context.ctx = brl;
+ lock.ntvfs = brlh->ntvfs;
+ lock.context.ctx = brl;
+ lock.start = start;
+ lock.size = size;
+ lock.lock_type = lock_type;
+ lock.notify_ptr = notify_ptr;
+
+ if (dbuf.dptr) {
+ /* there are existing locks - make sure they don't conflict */
+ locks = (struct lock_struct *)dbuf.dptr;
+ count = dbuf.dsize / sizeof(*locks);
+ for (i=0; i<count; i++) {
+ if (brl_tdb_conflict(&locks[i], &lock)) {
+ status = brl_tdb_lock_failed(brlh, &lock);
+ goto fail;
+ }
+ }
+ }
+
+ /* no conflicts - add it to the list of locks */
+ locks = realloc_p(locks, struct lock_struct, count+1);
+ if (!locks) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ } else {
+ dbuf.dptr = (uint8_t *)locks;
+ }
+ locks[count] = lock;
+ dbuf.dsize += sizeof(lock);
+
+ if (tdb_store(brl->w->tdb, kbuf, dbuf, TDB_REPLACE) != 0) {
+ status = NT_STATUS_INTERNAL_DB_CORRUPTION;
+ goto fail;
+ }
+
+ free(dbuf.dptr);
+ tdb_chainunlock(brl->w->tdb, kbuf);
+
+ /* the caller needs to know if the real lock was granted. If
+ we have reached here then it must be a pending lock that
+ was granted, so tell them the lock failed */
+ if (lock_type >= PENDING_READ_LOCK) {
+ return NT_STATUS_LOCK_NOT_GRANTED;
+ }
+
+ return NT_STATUS_OK;
+
+ fail:
+
+ free(dbuf.dptr);
+ tdb_chainunlock(brl->w->tdb, kbuf);
+ return status;
+}
+
+
+/*
+ we are removing a lock that might be holding up a pending lock. Scan for pending
+ locks that cover this range and if we find any then notify the server that it should
+ retry the lock
+*/
+static void brl_tdb_notify_unlock(struct brl_context *brl,
+ struct lock_struct *locks, int count,
+ struct lock_struct *removed_lock)
+{
+ int i, last_notice;
+
+ /* the last_notice logic is to prevent stampeding on a lock
+ range. It prevents us sending hundreds of notifies on the
+ same range of bytes. It doesn't prevent all possible
+ stampedes, but it does prevent the most common problem */
+ last_notice = -1;
+
+ for (i=0;i<count;i++) {
+ if (locks[i].lock_type >= PENDING_READ_LOCK &&
+ brl_tdb_overlap(&locks[i], removed_lock)) {
+ if (last_notice != -1 && brl_tdb_overlap(&locks[i], &locks[last_notice])) {
+ continue;
+ }
+ if (locks[i].lock_type == PENDING_WRITE_LOCK) {
+ last_notice = i;
+ }
+ messaging_send_ptr(brl->messaging_ctx, locks[i].context.server,
+ MSG_BRL_RETRY, locks[i].notify_ptr);
+ }
+ }
+}
+
+
+/*
+ send notifications for all pending locks - the file is being closed by this
+ user
+*/
+static void brl_tdb_notify_all(struct brl_context *brl,
+ struct lock_struct *locks, int count)
+{
+ int i;
+ for (i=0;i<count;i++) {
+ if (locks->lock_type >= PENDING_READ_LOCK) {
+ brl_tdb_notify_unlock(brl, locks, count, &locks[i]);
+ }
+ }
+}
+
+
+
+/*
+ Unlock a range of bytes.
+*/
+static NTSTATUS brl_tdb_unlock(struct brl_context *brl,
+ struct brl_handle *brlh,
+ uint32_t smbpid,
+ uint64_t start, uint64_t size)
+{
+ TDB_DATA kbuf, dbuf;
+ int count, i;
+ struct lock_struct *locks, *lock;
+ struct lock_context context;
+ NTSTATUS status;
+
+ kbuf.dptr = brlh->key.data;
+ kbuf.dsize = brlh->key.length;
+
+ if (tdb_chainlock(brl->w->tdb, kbuf) != 0) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ dbuf = tdb_fetch(brl->w->tdb, kbuf);
+ if (!dbuf.dptr) {
+ tdb_chainunlock(brl->w->tdb, kbuf);
+ return NT_STATUS_RANGE_NOT_LOCKED;
+ }
+
+ context.smbpid = smbpid;
+ context.server = brl->server;
+ context.ctx = brl;
+
+ /* there are existing locks - find a match */
+ locks = (struct lock_struct *)dbuf.dptr;
+ count = dbuf.dsize / sizeof(*locks);
+
+ for (i=0; i<count; i++) {
+ lock = &locks[i];
+ if (brl_tdb_same_context(&lock->context, &context) &&
+ lock->ntvfs == brlh->ntvfs &&
+ lock->start == start &&
+ lock->size == size &&
+ lock->lock_type == WRITE_LOCK) {
+ break;
+ }
+ }
+ if (i < count) goto found;
+
+ for (i=0; i<count; i++) {
+ lock = &locks[i];
+ if (brl_tdb_same_context(&lock->context, &context) &&
+ lock->ntvfs == brlh->ntvfs &&
+ lock->start == start &&
+ lock->size == size &&
+ lock->lock_type < PENDING_READ_LOCK) {
+ break;
+ }
+ }
+
+found:
+ if (i < count) {
+ /* found it - delete it */
+ if (count == 1) {
+ if (tdb_delete(brl->w->tdb, kbuf) != 0) {
+ status = NT_STATUS_INTERNAL_DB_CORRUPTION;
+ goto fail;
+ }
+ } else {
+ struct lock_struct removed_lock = *lock;
+ if (i < count-1) {
+ memmove(&locks[i], &locks[i+1],
+ sizeof(*locks)*((count-1) - i));
+ }
+ count--;
+
+ /* send notifications for any relevant pending locks */
+ brl_tdb_notify_unlock(brl, locks, count, &removed_lock);
+
+ dbuf.dsize = count * sizeof(*locks);
+
+ if (tdb_store(brl->w->tdb, kbuf, dbuf, TDB_REPLACE) != 0) {
+ status = NT_STATUS_INTERNAL_DB_CORRUPTION;
+ goto fail;
+ }
+ }
+
+ free(dbuf.dptr);
+ tdb_chainunlock(brl->w->tdb, kbuf);
+ return NT_STATUS_OK;
+ }
+
+ /* we didn't find it */
+ status = NT_STATUS_RANGE_NOT_LOCKED;
+
+ fail:
+ free(dbuf.dptr);
+ tdb_chainunlock(brl->w->tdb, kbuf);
+ return status;
+}
+
+
+/*
+ remove a pending lock. This is called when the caller has either
+ given up trying to establish a lock or when they have succeeded in
+ getting it. In either case they no longer need to be notified.
+*/
+static NTSTATUS brl_tdb_remove_pending(struct brl_context *brl,
+ struct brl_handle *brlh,
+ void *notify_ptr)
+{
+ TDB_DATA kbuf, dbuf;
+ int count, i;
+ struct lock_struct *locks;
+ NTSTATUS status;
+
+ kbuf.dptr = brlh->key.data;
+ kbuf.dsize = brlh->key.length;
+
+ if (tdb_chainlock(brl->w->tdb, kbuf) != 0) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ dbuf = tdb_fetch(brl->w->tdb, kbuf);
+ if (!dbuf.dptr) {
+ tdb_chainunlock(brl->w->tdb, kbuf);
+ return NT_STATUS_RANGE_NOT_LOCKED;
+ }
+
+ /* there are existing locks - find a match */
+ locks = (struct lock_struct *)dbuf.dptr;
+ count = dbuf.dsize / sizeof(*locks);
+
+ for (i=0; i<count; i++) {
+ struct lock_struct *lock = &locks[i];
+
+ if (lock->lock_type >= PENDING_READ_LOCK &&
+ lock->notify_ptr == notify_ptr &&
+ cluster_id_equal(&lock->context.server, &brl->server)) {
+ /* found it - delete it */
+ if (count == 1) {
+ if (tdb_delete(brl->w->tdb, kbuf) != 0) {
+ status = NT_STATUS_INTERNAL_DB_CORRUPTION;
+ goto fail;
+ }
+ } else {
+ if (i < count-1) {
+ memmove(&locks[i], &locks[i+1],
+ sizeof(*locks)*((count-1) - i));
+ }
+ count--;
+ dbuf.dsize = count * sizeof(*locks);
+ if (tdb_store(brl->w->tdb, kbuf, dbuf, TDB_REPLACE) != 0) {
+ status = NT_STATUS_INTERNAL_DB_CORRUPTION;
+ goto fail;
+ }
+ }
+
+ free(dbuf.dptr);
+ tdb_chainunlock(brl->w->tdb, kbuf);
+ return NT_STATUS_OK;
+ }
+ }
+
+ /* we didn't find it */
+ status = NT_STATUS_RANGE_NOT_LOCKED;
+
+ fail:
+ free(dbuf.dptr);
+ tdb_chainunlock(brl->w->tdb, kbuf);
+ return status;
+}
+
+
+/*
+ Test if we are allowed to perform IO on a region of an open file
+*/
+static NTSTATUS brl_tdb_locktest(struct brl_context *brl,
+ struct brl_handle *brlh,
+ uint32_t smbpid,
+ uint64_t start, uint64_t size,
+ enum brl_type lock_type)
+{
+ TDB_DATA kbuf, dbuf;
+ int count, i;
+ struct lock_struct lock, *locks;
+
+ kbuf.dptr = brlh->key.data;
+ kbuf.dsize = brlh->key.length;
+
+ dbuf = tdb_fetch(brl->w->tdb, kbuf);
+ if (dbuf.dptr == NULL) {
+ return NT_STATUS_OK;
+ }
+
+ lock.context.smbpid = smbpid;
+ lock.context.server = brl->server;
+ lock.context.ctx = brl;
+ lock.ntvfs = brlh->ntvfs;
+ lock.start = start;
+ lock.size = size;
+ lock.lock_type = lock_type;
+
+ /* there are existing locks - make sure they don't conflict */
+ locks = (struct lock_struct *)dbuf.dptr;
+ count = dbuf.dsize / sizeof(*locks);
+
+ for (i=0; i<count; i++) {
+ if (brl_tdb_conflict_other(&locks[i], &lock)) {
+ free(dbuf.dptr);
+ return NT_STATUS_FILE_LOCK_CONFLICT;
+ }
+ }
+
+ free(dbuf.dptr);
+ return NT_STATUS_OK;
+}
+
+
+/*
+ Remove any locks associated with a open file.
+*/
+static NTSTATUS brl_tdb_close(struct brl_context *brl,
+ struct brl_handle *brlh)
+{
+ TDB_DATA kbuf, dbuf;
+ int count, i, dcount=0;
+ struct lock_struct *locks;
+ NTSTATUS status;
+
+ kbuf.dptr = brlh->key.data;
+ kbuf.dsize = brlh->key.length;
+
+ if (tdb_chainlock(brl->w->tdb, kbuf) != 0) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ dbuf = tdb_fetch(brl->w->tdb, kbuf);
+ if (!dbuf.dptr) {
+ tdb_chainunlock(brl->w->tdb, kbuf);
+ return NT_STATUS_OK;
+ }
+
+ /* there are existing locks - remove any for this fnum */
+ locks = (struct lock_struct *)dbuf.dptr;
+ count = dbuf.dsize / sizeof(*locks);
+
+ for (i=0; i<count; i++) {
+ struct lock_struct *lock = &locks[i];
+
+ if (lock->context.ctx == brl &&
+ cluster_id_equal(&lock->context.server, &brl->server) &&
+ lock->ntvfs == brlh->ntvfs) {
+ /* found it - delete it */
+ if (count > 1 && i < count-1) {
+ memmove(&locks[i], &locks[i+1],
+ sizeof(*locks)*((count-1) - i));
+ }
+ count--;
+ i--;
+ dcount++;
+ }
+ }
+
+ status = NT_STATUS_OK;
+
+ if (count == 0) {
+ if (tdb_delete(brl->w->tdb, kbuf) != 0) {
+ status = NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ } else if (dcount != 0) {
+ /* tell all pending lock holders for this file that
+ they have a chance now. This is a bit indiscriminant,
+ but works OK */
+ brl_tdb_notify_all(brl, locks, count);
+
+ dbuf.dsize = count * sizeof(*locks);
+
+ if (tdb_store(brl->w->tdb, kbuf, dbuf, TDB_REPLACE) != 0) {
+ status = NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ }
+
+ free(dbuf.dptr);
+ tdb_chainunlock(brl->w->tdb, kbuf);
+
+ return status;
+}
+
+
+static const struct brlock_ops brlock_tdb_ops = {
+ .brl_init = brl_tdb_init,
+ .brl_create_handle = brl_tdb_create_handle,
+ .brl_lock = brl_tdb_lock,
+ .brl_unlock = brl_tdb_unlock,
+ .brl_remove_pending = brl_tdb_remove_pending,
+ .brl_locktest = brl_tdb_locktest,
+ .brl_close = brl_tdb_close
+};
+
+
+void brl_tdb_init_ops(void)
+{
+ brl_set_ops(&brlock_tdb_ops);
+}
diff --git a/source4/ntvfs/common/config.mk b/source4/ntvfs/common/config.mk
new file mode 100644
index 0000000000..a6957ace2f
--- /dev/null
+++ b/source4/ntvfs/common/config.mk
@@ -0,0 +1,11 @@
+################################################
+# Start LIBRARY ntvfs_common
+[SUBSYSTEM::ntvfs_common]
+PUBLIC_DEPENDENCIES = NDR_OPENDB NDR_NOTIFY sys_notify sys_lease share
+# End LIBRARY ntvfs_common
+################################################
+
+ntvfs_common_OBJ_FILES = $(addprefix $(ntvfssrcdir)/common/, init.o brlock.o brlock_tdb.o opendb.o opendb_tdb.o notify.o)
+
+$(eval $(call proto_header_template,$(ntvfssrcdir)/common/proto.h,$(ntvfs_common_OBJ_FILES:.o=.c)))
+
diff --git a/source4/ntvfs/common/init.c b/source4/ntvfs/common/init.c
new file mode 100644
index 0000000000..e0f5a9d2aa
--- /dev/null
+++ b/source4/ntvfs/common/init.c
@@ -0,0 +1,32 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ this is the change notify database. It implements mechanisms for
+ storing current change notify waiters in a tdb, and checking if a
+ given event matches any of the stored notify waiiters.
+*/
+
+#include "includes.h"
+#include "ntvfs/sysdep/sys_notify.h"
+
+NTSTATUS ntvfs_common_init(void)
+{
+ return sys_notify_init();
+}
diff --git a/source4/ntvfs/common/notify.c b/source4/ntvfs/common/notify.c
new file mode 100644
index 0000000000..135458a00f
--- /dev/null
+++ b/source4/ntvfs/common/notify.c
@@ -0,0 +1,672 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ this is the change notify database. It implements mechanisms for
+ storing current change notify waiters in a tdb, and checking if a
+ given event matches any of the stored notify waiiters.
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "../tdb/include/tdb.h"
+#include "../lib/util/util_tdb.h"
+#include "messaging/messaging.h"
+#include "tdb_wrap.h"
+#include "lib/messaging/irpc.h"
+#include "librpc/gen_ndr/ndr_notify.h"
+#include "../lib/util/dlinklist.h"
+#include "ntvfs/common/ntvfs_common.h"
+#include "ntvfs/sysdep/sys_notify.h"
+#include "cluster/cluster.h"
+#include "param/param.h"
+
+struct notify_context {
+ struct tdb_wrap *w;
+ struct server_id server;
+ struct messaging_context *messaging_ctx;
+ struct notify_list *list;
+ struct notify_array *array;
+ int seqnum;
+ struct sys_notify_context *sys_notify_ctx;
+ struct smb_iconv_convenience *iconv_convenience;
+};
+
+
+struct notify_list {
+ struct notify_list *next, *prev;
+ void *private_data;
+ void (*callback)(void *, const struct notify_event *);
+ void *sys_notify_handle;
+ int depth;
+};
+
+#define NOTIFY_KEY "notify array"
+
+#define NOTIFY_ENABLE "notify:enable"
+#define NOTIFY_ENABLE_DEFAULT true
+
+static NTSTATUS notify_remove_all(struct notify_context *notify);
+static void notify_handler(struct messaging_context *msg_ctx, void *private_data,
+ uint32_t msg_type, struct server_id server_id, DATA_BLOB *data);
+
+/*
+ destroy the notify context
+*/
+static int notify_destructor(struct notify_context *notify)
+{
+ messaging_deregister(notify->messaging_ctx, MSG_PVFS_NOTIFY, notify);
+ notify_remove_all(notify);
+ return 0;
+}
+
+/*
+ Open up the notify.tdb database. You should close it down using
+ talloc_free(). We need the messaging_ctx to allow for notifications
+ via internal messages
+*/
+struct notify_context *notify_init(TALLOC_CTX *mem_ctx, struct server_id server,
+ struct messaging_context *messaging_ctx,
+ struct loadparm_context *lp_ctx,
+ struct tevent_context *ev,
+ struct share_config *scfg)
+{
+ struct notify_context *notify;
+
+ if (share_bool_option(scfg, NOTIFY_ENABLE, NOTIFY_ENABLE_DEFAULT) != true) {
+ return NULL;
+ }
+
+ if (ev == NULL) {
+ return NULL;
+ }
+
+ notify = talloc(mem_ctx, struct notify_context);
+ if (notify == NULL) {
+ return NULL;
+ }
+
+ notify->w = cluster_tdb_tmp_open(notify, lp_ctx, "notify.tdb", TDB_SEQNUM);
+ if (notify->w == NULL) {
+ talloc_free(notify);
+ return NULL;
+ }
+
+ notify->server = server;
+ notify->messaging_ctx = messaging_ctx;
+ notify->list = NULL;
+ notify->array = NULL;
+ notify->iconv_convenience = lp_iconv_convenience(lp_ctx);
+ notify->seqnum = tdb_get_seqnum(notify->w->tdb);
+
+ talloc_set_destructor(notify, notify_destructor);
+
+ /* register with the messaging subsystem for the notify
+ message type */
+ messaging_register(notify->messaging_ctx, notify,
+ MSG_PVFS_NOTIFY, notify_handler);
+
+ notify->sys_notify_ctx = sys_notify_context_create(scfg, notify, ev);
+
+ return notify;
+}
+
+
+/*
+ lock the notify db
+*/
+static NTSTATUS notify_lock(struct notify_context *notify)
+{
+ if (tdb_lock_bystring(notify->w->tdb, NOTIFY_KEY) != 0) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ return NT_STATUS_OK;
+}
+
+/*
+ unlock the notify db
+*/
+static void notify_unlock(struct notify_context *notify)
+{
+ tdb_unlock_bystring(notify->w->tdb, NOTIFY_KEY);
+}
+
+/*
+ load the notify array
+*/
+static NTSTATUS notify_load(struct notify_context *notify)
+{
+ TDB_DATA dbuf;
+ DATA_BLOB blob;
+ enum ndr_err_code ndr_err;
+ int seqnum;
+
+ seqnum = tdb_get_seqnum(notify->w->tdb);
+
+ if (seqnum == notify->seqnum && notify->array != NULL) {
+ return NT_STATUS_OK;
+ }
+
+ notify->seqnum = seqnum;
+
+ talloc_free(notify->array);
+ notify->array = talloc_zero(notify, struct notify_array);
+ NT_STATUS_HAVE_NO_MEMORY(notify->array);
+
+ dbuf = tdb_fetch_bystring(notify->w->tdb, NOTIFY_KEY);
+ if (dbuf.dptr == NULL) {
+ return NT_STATUS_OK;
+ }
+
+ blob.data = dbuf.dptr;
+ blob.length = dbuf.dsize;
+
+ ndr_err = ndr_pull_struct_blob(&blob, notify->array, notify->iconv_convenience,
+ notify->array,
+ (ndr_pull_flags_fn_t)ndr_pull_notify_array);
+ free(dbuf.dptr);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ compare notify entries for sorting
+*/
+static int notify_compare(const void *p1, const void *p2)
+{
+ const struct notify_entry *e1 = p1, *e2 = p2;
+ return strcmp(e1->path, e2->path);
+}
+
+/*
+ save the notify array
+*/
+static NTSTATUS notify_save(struct notify_context *notify)
+{
+ TDB_DATA dbuf;
+ DATA_BLOB blob;
+ enum ndr_err_code ndr_err;
+ int ret;
+ TALLOC_CTX *tmp_ctx;
+
+ /* if possible, remove some depth arrays */
+ while (notify->array->num_depths > 0 &&
+ notify->array->depth[notify->array->num_depths-1].num_entries == 0) {
+ notify->array->num_depths--;
+ }
+
+ /* we might just be able to delete the record */
+ if (notify->array->num_depths == 0) {
+ ret = tdb_delete_bystring(notify->w->tdb, NOTIFY_KEY);
+ if (ret != 0) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ return NT_STATUS_OK;
+ }
+
+ tmp_ctx = talloc_new(notify);
+ NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
+
+ ndr_err = ndr_push_struct_blob(&blob, tmp_ctx, notify->iconv_convenience, notify->array,
+ (ndr_push_flags_fn_t)ndr_push_notify_array);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ talloc_free(tmp_ctx);
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ dbuf.dptr = blob.data;
+ dbuf.dsize = blob.length;
+
+ ret = tdb_store_bystring(notify->w->tdb, NOTIFY_KEY, dbuf, TDB_REPLACE);
+ talloc_free(tmp_ctx);
+ if (ret != 0) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ handle incoming notify messages
+*/
+static void notify_handler(struct messaging_context *msg_ctx, void *private_data,
+ uint32_t msg_type, struct server_id server_id, DATA_BLOB *data)
+{
+ struct notify_context *notify = talloc_get_type(private_data, struct notify_context);
+ enum ndr_err_code ndr_err;
+ struct notify_event ev;
+ TALLOC_CTX *tmp_ctx = talloc_new(notify);
+ struct notify_list *listel;
+
+ if (tmp_ctx == NULL) {
+ return;
+ }
+
+ ndr_err = ndr_pull_struct_blob(data, tmp_ctx, notify->iconv_convenience, &ev,
+ (ndr_pull_flags_fn_t)ndr_pull_notify_event);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ talloc_free(tmp_ctx);
+ return;
+ }
+
+ for (listel=notify->list;listel;listel=listel->next) {
+ if (listel->private_data == ev.private_data) {
+ listel->callback(listel->private_data, &ev);
+ break;
+ }
+ }
+
+ talloc_free(tmp_ctx);
+}
+
+/*
+ callback from sys_notify telling us about changes from the OS
+*/
+static void sys_notify_callback(struct sys_notify_context *ctx,
+ void *ptr, struct notify_event *ev)
+{
+ struct notify_list *listel = talloc_get_type(ptr, struct notify_list);
+ ev->private_data = listel;
+ listel->callback(listel->private_data, ev);
+}
+
+/*
+ add an entry to the notify array
+*/
+static NTSTATUS notify_add_array(struct notify_context *notify, struct notify_entry *e,
+ void *private_data, int depth)
+{
+ int i;
+ struct notify_depth *d;
+ struct notify_entry *ee;
+
+ /* possibly expand the depths array */
+ if (depth >= notify->array->num_depths) {
+ d = talloc_realloc(notify->array, notify->array->depth,
+ struct notify_depth, depth+1);
+ NT_STATUS_HAVE_NO_MEMORY(d);
+ for (i=notify->array->num_depths;i<=depth;i++) {
+ ZERO_STRUCT(d[i]);
+ }
+ notify->array->depth = d;
+ notify->array->num_depths = depth+1;
+ }
+ d = &notify->array->depth[depth];
+
+ /* expand the entries array */
+ ee = talloc_realloc(notify->array->depth, d->entries, struct notify_entry,
+ d->num_entries+1);
+ NT_STATUS_HAVE_NO_MEMORY(ee);
+ d->entries = ee;
+
+ d->entries[d->num_entries] = *e;
+ d->entries[d->num_entries].private_data = private_data;
+ d->entries[d->num_entries].server = notify->server;
+ d->entries[d->num_entries].path_len = strlen(e->path);
+ d->num_entries++;
+
+ d->max_mask |= e->filter;
+ d->max_mask_subdir |= e->subdir_filter;
+
+ if (d->num_entries > 1) {
+ qsort(d->entries, d->num_entries, sizeof(d->entries[0]), notify_compare);
+ }
+
+ /* recalculate the maximum masks */
+ d->max_mask = 0;
+ d->max_mask_subdir = 0;
+
+ for (i=0;i<d->num_entries;i++) {
+ d->max_mask |= d->entries[i].filter;
+ d->max_mask_subdir |= d->entries[i].subdir_filter;
+ }
+
+ return notify_save(notify);
+}
+
+/*
+ add a notify watch. This is called when a notify is first setup on a open
+ directory handle.
+*/
+NTSTATUS notify_add(struct notify_context *notify, struct notify_entry *e0,
+ void (*callback)(void *, const struct notify_event *),
+ void *private_data)
+{
+ struct notify_entry e = *e0;
+ NTSTATUS status;
+ char *tmp_path = NULL;
+ struct notify_list *listel;
+ size_t len;
+ int depth;
+
+ /* see if change notify is enabled at all */
+ if (notify == NULL) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ status = notify_lock(notify);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ status = notify_load(notify);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ /* cope with /. on the end of the path */
+ len = strlen(e.path);
+ if (len > 1 && e.path[len-1] == '.' && e.path[len-2] == '/') {
+ tmp_path = talloc_strndup(notify, e.path, len-2);
+ if (tmp_path == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ e.path = tmp_path;
+ }
+
+ depth = count_chars(e.path, '/');
+
+ listel = talloc_zero(notify, struct notify_list);
+ if (listel == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ listel->private_data = private_data;
+ listel->callback = callback;
+ listel->depth = depth;
+ DLIST_ADD(notify->list, listel);
+
+ /* ignore failures from sys_notify */
+ if (notify->sys_notify_ctx != NULL) {
+ /*
+ this call will modify e.filter and e.subdir_filter
+ to remove bits handled by the backend
+ */
+ status = sys_notify_watch(notify->sys_notify_ctx, &e,
+ sys_notify_callback, listel,
+ &listel->sys_notify_handle);
+ if (NT_STATUS_IS_OK(status)) {
+ talloc_steal(listel, listel->sys_notify_handle);
+ }
+ }
+
+ /* if the system notify handler couldn't handle some of the
+ filter bits, or couldn't handle a request for recursion
+ then we need to install it in the array used for the
+ intra-samba notify handling */
+ if (e.filter != 0 || e.subdir_filter != 0) {
+ status = notify_add_array(notify, &e, private_data, depth);
+ }
+
+done:
+ notify_unlock(notify);
+ talloc_free(tmp_path);
+
+ return status;
+}
+
+/*
+ remove a notify watch. Called when the directory handle is closed
+*/
+NTSTATUS notify_remove(struct notify_context *notify, void *private_data)
+{
+ NTSTATUS status;
+ struct notify_list *listel;
+ int i, depth;
+ struct notify_depth *d;
+
+ /* see if change notify is enabled at all */
+ if (notify == NULL) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ for (listel=notify->list;listel;listel=listel->next) {
+ if (listel->private_data == private_data) {
+ DLIST_REMOVE(notify->list, listel);
+ break;
+ }
+ }
+ if (listel == NULL) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ depth = listel->depth;
+
+ talloc_free(listel);
+
+ status = notify_lock(notify);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ status = notify_load(notify);
+ if (!NT_STATUS_IS_OK(status)) {
+ notify_unlock(notify);
+ return status;
+ }
+
+ if (depth >= notify->array->num_depths) {
+ notify_unlock(notify);
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ /* we only have to search at the depth of this element */
+ d = &notify->array->depth[depth];
+
+ for (i=0;i<d->num_entries;i++) {
+ if (private_data == d->entries[i].private_data &&
+ cluster_id_equal(&notify->server, &d->entries[i].server)) {
+ break;
+ }
+ }
+ if (i == d->num_entries) {
+ notify_unlock(notify);
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ if (i < d->num_entries-1) {
+ memmove(&d->entries[i], &d->entries[i+1],
+ sizeof(d->entries[i])*(d->num_entries-(i+1)));
+ }
+ d->num_entries--;
+
+ status = notify_save(notify);
+
+ notify_unlock(notify);
+
+ return status;
+}
+
+/*
+ remove all notify watches for this messaging server
+*/
+static NTSTATUS notify_remove_all(struct notify_context *notify)
+{
+ NTSTATUS status;
+ int i, depth, del_count=0;
+
+ if (notify->list == NULL) {
+ return NT_STATUS_OK;
+ }
+
+ status = notify_lock(notify);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ status = notify_load(notify);
+ if (!NT_STATUS_IS_OK(status)) {
+ notify_unlock(notify);
+ return status;
+ }
+
+ /* we have to search for all entries across all depths, looking for matches
+ for our server id */
+ for (depth=0;depth<notify->array->num_depths;depth++) {
+ struct notify_depth *d = &notify->array->depth[depth];
+ for (i=0;i<d->num_entries;i++) {
+ if (cluster_id_equal(&notify->server, &d->entries[i].server)) {
+ if (i < d->num_entries-1) {
+ memmove(&d->entries[i], &d->entries[i+1],
+ sizeof(d->entries[i])*(d->num_entries-(i+1)));
+ }
+ i--;
+ d->num_entries--;
+ del_count++;
+ }
+ }
+ }
+
+ if (del_count > 0) {
+ status = notify_save(notify);
+ }
+
+ notify_unlock(notify);
+
+ return status;
+}
+
+
+/*
+ send a notify message to another messaging server
+*/
+static void notify_send(struct notify_context *notify, struct notify_entry *e,
+ const char *path, uint32_t action)
+{
+ struct notify_event ev;
+ DATA_BLOB data;
+ NTSTATUS status;
+ enum ndr_err_code ndr_err;
+ TALLOC_CTX *tmp_ctx;
+
+ ev.action = action;
+ ev.path = path;
+ ev.private_data = e->private_data;
+
+ tmp_ctx = talloc_new(notify);
+
+ ndr_err = ndr_push_struct_blob(&data, tmp_ctx, notify->iconv_convenience, &ev, (ndr_push_flags_fn_t)ndr_push_notify_event);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ talloc_free(tmp_ctx);
+ return;
+ }
+
+ status = messaging_send(notify->messaging_ctx, e->server,
+ MSG_PVFS_NOTIFY, &data);
+ talloc_free(tmp_ctx);
+}
+
+
+/*
+ trigger a notify message for anyone waiting on a matching event
+
+ This function is called a lot, and needs to be very fast. The unusual data structure
+ and traversal is designed to be fast in the average case, even for large numbers of
+ notifies
+*/
+void notify_trigger(struct notify_context *notify,
+ uint32_t action, uint32_t filter, const char *path)
+{
+ NTSTATUS status;
+ int depth;
+ const char *p, *next_p;
+
+ /* see if change notify is enabled at all */
+ if (notify == NULL) {
+ return;
+ }
+
+ status = notify_load(notify);
+ if (!NT_STATUS_IS_OK(status)) {
+ return;
+ }
+
+ /* loop along the given path, working with each directory depth separately */
+ for (depth=0,p=path;
+ p && depth < notify->array->num_depths;
+ p=next_p,depth++) {
+ int p_len = p - path;
+ int min_i, max_i, i;
+ struct notify_depth *d = &notify->array->depth[depth];
+ next_p = strchr(p+1, '/');
+
+ /* see if there are any entries at this depth */
+ if (d->num_entries == 0) continue;
+
+ /* try to skip based on the maximum mask. If next_p is
+ NULL then we know it will be a 'this directory'
+ match, otherwise it must be a subdir match */
+ if (next_p != NULL) {
+ if (0 == (filter & d->max_mask_subdir)) {
+ continue;
+ }
+ } else {
+ if (0 == (filter & d->max_mask)) {
+ continue;
+ }
+ }
+
+ /* we know there is an entry here worth looking
+ for. Use a bisection search to find the first entry
+ with a matching path */
+ min_i = 0;
+ max_i = d->num_entries-1;
+
+ while (min_i < max_i) {
+ struct notify_entry *e;
+ int cmp;
+ i = (min_i+max_i)/2;
+ e = &d->entries[i];
+ cmp = strncmp(path, e->path, p_len);
+ if (cmp == 0) {
+ if (p_len == e->path_len) {
+ max_i = i;
+ } else {
+ max_i = i-1;
+ }
+ } else if (cmp < 0) {
+ max_i = i-1;
+ } else {
+ min_i = i+1;
+ }
+ }
+
+ if (min_i != max_i) {
+ /* none match */
+ continue;
+ }
+
+ /* we now know that the entries start at min_i */
+ for (i=min_i;i<d->num_entries;i++) {
+ struct notify_entry *e = &d->entries[i];
+ if (p_len != e->path_len ||
+ strncmp(path, e->path, p_len) != 0) break;
+ if (next_p != NULL) {
+ if (0 == (filter & e->subdir_filter)) {
+ continue;
+ }
+ } else {
+ if (0 == (filter & e->filter)) {
+ continue;
+ }
+ }
+ notify_send(notify, e, path + e->path_len + 1, action);
+ }
+ }
+}
diff --git a/source4/ntvfs/common/ntvfs_common.h b/source4/ntvfs/common/ntvfs_common.h
new file mode 100644
index 0000000000..37dd553a4e
--- /dev/null
+++ b/source4/ntvfs/common/ntvfs_common.h
@@ -0,0 +1,32 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _NTVFS_COMMON_H_
+#define _NTVFS_COMMON_H_
+
+#include "ntvfs/ntvfs.h"
+
+struct notify_event;
+struct notify_entry;
+
+#include "ntvfs/common/brlock.h"
+#include "ntvfs/common/opendb.h"
+#include "ntvfs/common/proto.h"
+
+#endif /* _NTVFS_COMMON_H_ */
diff --git a/source4/ntvfs/common/opendb.c b/source4/ntvfs/common/opendb.c
new file mode 100644
index 0000000000..12fe7015a7
--- /dev/null
+++ b/source4/ntvfs/common/opendb.c
@@ -0,0 +1,200 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ this is the open files database. It implements shared storage of
+ what files are open between server instances, and implements the rules
+ of shared access to files.
+
+ The caller needs to provide a file_key, which specifies what file
+ they are talking about. This needs to be a unique key across all
+ filesystems, and is usually implemented in terms of a device/inode
+ pair.
+
+ Before any operations can be performed the caller needs to establish
+ a lock on the record associated with file_key. That is done by
+ calling odb_lock(). The caller releases this lock by calling
+ talloc_free() on the returned handle.
+
+ All other operations on a record are done by passing the odb_lock()
+ handle back to this module. The handle contains internal
+ information about what file_key is being operated on.
+*/
+
+#include "includes.h"
+#include "ntvfs/ntvfs.h"
+#include "ntvfs/common/ntvfs_common.h"
+#include "cluster/cluster.h"
+#include "param/param.h"
+
+static const struct opendb_ops *ops;
+
+/*
+ set the odb backend ops
+*/
+void odb_set_ops(const struct opendb_ops *new_ops)
+{
+ ops = new_ops;
+}
+
+/*
+ Open up the openfiles.tdb database. Close it down using
+ talloc_free(). We need the messaging_ctx to allow for pending open
+ notifications.
+*/
+struct odb_context *odb_init(TALLOC_CTX *mem_ctx,
+ struct ntvfs_context *ntvfs_ctx)
+{
+ if (ops == NULL) {
+ odb_tdb_init_ops();
+ }
+ return ops->odb_init(mem_ctx, ntvfs_ctx);
+}
+
+/*
+ get a lock on a entry in the odb. This call returns a lock handle,
+ which the caller should unlock using talloc_free().
+*/
+struct odb_lock *odb_lock(TALLOC_CTX *mem_ctx,
+ struct odb_context *odb, DATA_BLOB *file_key)
+{
+ return ops->odb_lock(mem_ctx, odb, file_key);
+}
+
+DATA_BLOB odb_get_key(TALLOC_CTX *mem_ctx, struct odb_lock *lck)
+{
+ return ops->odb_get_key(mem_ctx, lck);
+}
+
+/*
+ register an open file in the open files database.
+ The share_access rules are implemented by odb_can_open()
+ and it's needed to call odb_can_open() before
+ odb_open_file() otherwise NT_STATUS_INTERNAL_ERROR is returned
+
+ Note that the path is only used by the delete on close logic, not
+ for comparing with other filenames
+*/
+NTSTATUS odb_open_file(struct odb_lock *lck,
+ void *file_handle, const char *path,
+ int *fd, NTTIME open_write_time,
+ bool allow_level_II_oplock,
+ uint32_t oplock_level, uint32_t *oplock_granted)
+{
+ return ops->odb_open_file(lck, file_handle, path,
+ fd, open_write_time,
+ allow_level_II_oplock,
+ oplock_level, oplock_granted);
+}
+
+
+/*
+ register a pending open file in the open files database
+*/
+NTSTATUS odb_open_file_pending(struct odb_lock *lck, void *private_data)
+{
+ return ops->odb_open_file_pending(lck, private_data);
+}
+
+
+/*
+ remove a opendb entry
+*/
+NTSTATUS odb_close_file(struct odb_lock *lck, void *file_handle,
+ const char **delete_path)
+{
+ return ops->odb_close_file(lck, file_handle, delete_path);
+}
+
+
+/*
+ remove a pending opendb entry
+*/
+NTSTATUS odb_remove_pending(struct odb_lock *lck, void *private_data)
+{
+ return ops->odb_remove_pending(lck, private_data);
+}
+
+
+/*
+ rename the path in a open file
+*/
+NTSTATUS odb_rename(struct odb_lock *lck, const char *path)
+{
+ return ops->odb_rename(lck, path);
+}
+
+/*
+ get back the path of an open file
+*/
+NTSTATUS odb_get_path(struct odb_lock *lck, const char **path)
+{
+ return ops->odb_get_path(lck, path);
+}
+
+/*
+ update delete on close flag on an open file
+*/
+NTSTATUS odb_set_delete_on_close(struct odb_lock *lck, bool del_on_close)
+{
+ return ops->odb_set_delete_on_close(lck, del_on_close);
+}
+
+/*
+ update the write time on an open file
+*/
+NTSTATUS odb_set_write_time(struct odb_lock *lck,
+ NTTIME write_time, bool force)
+{
+ return ops->odb_set_write_time(lck, write_time, force);
+}
+
+/*
+ return the current value of the delete_on_close bit,
+ and the current write time.
+*/
+NTSTATUS odb_get_file_infos(struct odb_context *odb, DATA_BLOB *key,
+ bool *del_on_close, NTTIME *write_time)
+{
+ return ops->odb_get_file_infos(odb, key, del_on_close, write_time);
+}
+
+/*
+ determine if a file can be opened with the given share_access,
+ create_options and access_mask
+*/
+NTSTATUS odb_can_open(struct odb_lock *lck,
+ uint32_t stream_id, uint32_t share_access,
+ uint32_t access_mask, bool delete_on_close,
+ uint32_t open_disposition, bool break_to_none)
+{
+ return ops->odb_can_open(lck, stream_id, share_access, access_mask,
+ delete_on_close, open_disposition, break_to_none);
+}
+
+NTSTATUS odb_update_oplock(struct odb_lock *lck, void *file_handle,
+ uint32_t oplock_level)
+{
+ return ops->odb_update_oplock(lck, file_handle, oplock_level);
+}
+
+NTSTATUS odb_break_oplocks(struct odb_lock *lck)
+{
+ return ops->odb_break_oplocks(lck);
+}
diff --git a/source4/ntvfs/common/opendb.h b/source4/ntvfs/common/opendb.h
new file mode 100644
index 0000000000..446df1777f
--- /dev/null
+++ b/source4/ntvfs/common/opendb.h
@@ -0,0 +1,60 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ open database code - common include
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+struct opendb_ops {
+ struct odb_context *(*odb_init)(TALLOC_CTX *mem_ctx,
+ struct ntvfs_context *ntvfs_ctx);
+ struct odb_lock *(*odb_lock)(TALLOC_CTX *mem_ctx,
+ struct odb_context *odb, DATA_BLOB *file_key);
+ DATA_BLOB (*odb_get_key)(TALLOC_CTX *mem_ctx, struct odb_lock *lck);
+ NTSTATUS (*odb_open_file)(struct odb_lock *lck,
+ void *file_handle, const char *path,
+ int *fd, NTTIME open_write_time,
+ bool allow_level_II_oplock,
+ uint32_t oplock_level, uint32_t *oplock_granted);
+ NTSTATUS (*odb_open_file_pending)(struct odb_lock *lck, void *private_data);
+ NTSTATUS (*odb_close_file)(struct odb_lock *lck, void *file_handle,
+ const char **delete_path);
+ NTSTATUS (*odb_remove_pending)(struct odb_lock *lck, void *private_data);
+ NTSTATUS (*odb_rename)(struct odb_lock *lck, const char *path);
+ NTSTATUS (*odb_get_path)(struct odb_lock *lck, const char **path);
+ NTSTATUS (*odb_set_delete_on_close)(struct odb_lock *lck, bool del_on_close);
+ NTSTATUS (*odb_set_write_time)(struct odb_lock *lck,
+ NTTIME write_time, bool force);
+ NTSTATUS (*odb_get_file_infos)(struct odb_context *odb, DATA_BLOB *key,
+ bool *del_on_close, NTTIME *write_time);
+ NTSTATUS (*odb_can_open)(struct odb_lock *lck,
+ uint32_t stream_id, uint32_t share_access,
+ uint32_t access_mask, bool delete_on_close,
+ uint32_t open_disposition, bool break_to_none);
+ NTSTATUS (*odb_update_oplock)(struct odb_lock *lck, void *file_handle,
+ uint32_t oplock_level);
+ NTSTATUS (*odb_break_oplocks)(struct odb_lock *lck);
+};
+
+struct opendb_oplock_break {
+ void *file_handle;
+ uint8_t level;
+};
+
+void odb_set_ops(const struct opendb_ops *new_ops);
+void odb_tdb_init_ops(void);
+void odb_ctdb_init_ops(void);
diff --git a/source4/ntvfs/common/opendb_tdb.c b/source4/ntvfs/common/opendb_tdb.c
new file mode 100644
index 0000000000..8f5d10f902
--- /dev/null
+++ b/source4/ntvfs/common/opendb_tdb.c
@@ -0,0 +1,905 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Andrew Tridgell 2004
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ this is the open files database, tdb backend. It implements shared
+ storage of what files are open between server instances, and
+ implements the rules of shared access to files.
+
+ The caller needs to provide a file_key, which specifies what file
+ they are talking about. This needs to be a unique key across all
+ filesystems, and is usually implemented in terms of a device/inode
+ pair.
+
+ Before any operations can be performed the caller needs to establish
+ a lock on the record associated with file_key. That is done by
+ calling odb_lock(). The caller releases this lock by calling
+ talloc_free() on the returned handle.
+
+ All other operations on a record are done by passing the odb_lock()
+ handle back to this module. The handle contains internal
+ information about what file_key is being operated on.
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "../tdb/include/tdb.h"
+#include "messaging/messaging.h"
+#include "tdb_wrap.h"
+#include "lib/messaging/irpc.h"
+#include "librpc/gen_ndr/ndr_opendb.h"
+#include "ntvfs/ntvfs.h"
+#include "ntvfs/common/ntvfs_common.h"
+#include "cluster/cluster.h"
+#include "param/param.h"
+#include "ntvfs/sysdep/sys_lease.h"
+
+struct odb_context {
+ struct tdb_wrap *w;
+ struct ntvfs_context *ntvfs_ctx;
+ bool oplocks;
+ struct sys_lease_context *lease_ctx;
+};
+
+/*
+ an odb lock handle. You must obtain one of these using odb_lock() before doing
+ any other operations.
+*/
+struct odb_lock {
+ struct odb_context *odb;
+ TDB_DATA key;
+
+ struct opendb_file file;
+
+ struct {
+ struct opendb_entry *e;
+ bool attrs_only;
+ } can_open;
+};
+
+static NTSTATUS odb_oplock_break_send(struct messaging_context *msg_ctx,
+ struct opendb_entry *e,
+ uint8_t level);
+
+/*
+ Open up the openfiles.tdb database. Close it down using
+ talloc_free(). We need the messaging_ctx to allow for pending open
+ notifications.
+*/
+static struct odb_context *odb_tdb_init(TALLOC_CTX *mem_ctx,
+ struct ntvfs_context *ntvfs_ctx)
+{
+ struct odb_context *odb;
+
+ odb = talloc(mem_ctx, struct odb_context);
+ if (odb == NULL) {
+ return NULL;
+ }
+
+ odb->w = cluster_tdb_tmp_open(odb, ntvfs_ctx->lp_ctx, "openfiles.tdb", TDB_DEFAULT);
+ if (odb->w == NULL) {
+ talloc_free(odb);
+ return NULL;
+ }
+
+ odb->ntvfs_ctx = ntvfs_ctx;
+
+ odb->oplocks = share_bool_option(ntvfs_ctx->config, SHARE_OPLOCKS, SHARE_OPLOCKS_DEFAULT);
+
+ odb->lease_ctx = sys_lease_context_create(ntvfs_ctx->config, odb,
+ ntvfs_ctx->event_ctx,
+ ntvfs_ctx->msg_ctx,
+ odb_oplock_break_send);
+
+ return odb;
+}
+
+/*
+ destroy a lock on the database
+*/
+static int odb_lock_destructor(struct odb_lock *lck)
+{
+ tdb_chainunlock(lck->odb->w->tdb, lck->key);
+ return 0;
+}
+
+static NTSTATUS odb_pull_record(struct odb_lock *lck, struct opendb_file *file);
+
+/*
+ get a lock on a entry in the odb. This call returns a lock handle,
+ which the caller should unlock using talloc_free().
+*/
+static struct odb_lock *odb_tdb_lock(TALLOC_CTX *mem_ctx,
+ struct odb_context *odb, DATA_BLOB *file_key)
+{
+ struct odb_lock *lck;
+ NTSTATUS status;
+
+ lck = talloc(mem_ctx, struct odb_lock);
+ if (lck == NULL) {
+ return NULL;
+ }
+
+ lck->odb = talloc_reference(lck, odb);
+ lck->key.dptr = talloc_memdup(lck, file_key->data, file_key->length);
+ lck->key.dsize = file_key->length;
+ if (lck->key.dptr == NULL) {
+ talloc_free(lck);
+ return NULL;
+ }
+
+ if (tdb_chainlock(odb->w->tdb, lck->key) != 0) {
+ talloc_free(lck);
+ return NULL;
+ }
+
+ ZERO_STRUCT(lck->can_open);
+
+ talloc_set_destructor(lck, odb_lock_destructor);
+
+ status = odb_pull_record(lck, &lck->file);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ /* initialise a blank structure */
+ ZERO_STRUCT(lck->file);
+ } else if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lck);
+ return NULL;
+ }
+
+ return lck;
+}
+
+static DATA_BLOB odb_tdb_get_key(TALLOC_CTX *mem_ctx, struct odb_lock *lck)
+{
+ return data_blob_talloc(mem_ctx, lck->key.dptr, lck->key.dsize);
+}
+
+
+/*
+ determine if two odb_entry structures conflict
+
+ return NT_STATUS_OK on no conflict
+*/
+static NTSTATUS share_conflict(struct opendb_entry *e1,
+ uint32_t stream_id,
+ uint32_t share_access,
+ uint32_t access_mask)
+{
+ /* if either open involves no read.write or delete access then
+ it can't conflict */
+ if (!(e1->access_mask & (SEC_FILE_WRITE_DATA |
+ SEC_FILE_APPEND_DATA |
+ SEC_FILE_READ_DATA |
+ SEC_FILE_EXECUTE |
+ SEC_STD_DELETE))) {
+ return NT_STATUS_OK;
+ }
+ if (!(access_mask & (SEC_FILE_WRITE_DATA |
+ SEC_FILE_APPEND_DATA |
+ SEC_FILE_READ_DATA |
+ SEC_FILE_EXECUTE |
+ SEC_STD_DELETE))) {
+ return NT_STATUS_OK;
+ }
+
+ /* data IO access masks. This is skipped if the two open handles
+ are on different streams (as in that case the masks don't
+ interact) */
+ if (e1->stream_id != stream_id) {
+ return NT_STATUS_OK;
+ }
+
+#define CHECK_MASK(am, right, sa, share) \
+ if (((am) & (right)) && !((sa) & (share))) return NT_STATUS_SHARING_VIOLATION
+
+ CHECK_MASK(e1->access_mask, SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA,
+ share_access, NTCREATEX_SHARE_ACCESS_WRITE);
+ CHECK_MASK(access_mask, SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA,
+ e1->share_access, NTCREATEX_SHARE_ACCESS_WRITE);
+
+ CHECK_MASK(e1->access_mask, SEC_FILE_READ_DATA | SEC_FILE_EXECUTE,
+ share_access, NTCREATEX_SHARE_ACCESS_READ);
+ CHECK_MASK(access_mask, SEC_FILE_READ_DATA | SEC_FILE_EXECUTE,
+ e1->share_access, NTCREATEX_SHARE_ACCESS_READ);
+
+ CHECK_MASK(e1->access_mask, SEC_STD_DELETE,
+ share_access, NTCREATEX_SHARE_ACCESS_DELETE);
+ CHECK_MASK(access_mask, SEC_STD_DELETE,
+ e1->share_access, NTCREATEX_SHARE_ACCESS_DELETE);
+#undef CHECK_MASK
+ return NT_STATUS_OK;
+}
+
+/*
+ pull a record, translating from the db format to the opendb_file structure defined
+ in opendb.idl
+*/
+static NTSTATUS odb_pull_record(struct odb_lock *lck, struct opendb_file *file)
+{
+ struct odb_context *odb = lck->odb;
+ TDB_DATA dbuf;
+ DATA_BLOB blob;
+ enum ndr_err_code ndr_err;
+
+ dbuf = tdb_fetch(odb->w->tdb, lck->key);
+ if (dbuf.dptr == NULL) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ blob.data = dbuf.dptr;
+ blob.length = dbuf.dsize;
+
+ ndr_err = ndr_pull_struct_blob(&blob, lck, lp_iconv_convenience(lck->odb->ntvfs_ctx->lp_ctx), file, (ndr_pull_flags_fn_t)ndr_pull_opendb_file);
+ free(dbuf.dptr);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ push a record, translating from the opendb_file structure defined in opendb.idl
+*/
+static NTSTATUS odb_push_record(struct odb_lock *lck, struct opendb_file *file)
+{
+ struct odb_context *odb = lck->odb;
+ TDB_DATA dbuf;
+ DATA_BLOB blob;
+ enum ndr_err_code ndr_err;
+ int ret;
+
+ if (file->num_entries == 0) {
+ ret = tdb_delete(odb->w->tdb, lck->key);
+ if (ret != 0) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ return NT_STATUS_OK;
+ }
+
+ ndr_err = ndr_push_struct_blob(&blob, lck, lp_iconv_convenience(lck->odb->ntvfs_ctx->lp_ctx), file, (ndr_push_flags_fn_t)ndr_push_opendb_file);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ dbuf.dptr = blob.data;
+ dbuf.dsize = blob.length;
+
+ ret = tdb_store(odb->w->tdb, lck->key, dbuf, TDB_REPLACE);
+ data_blob_free(&blob);
+ if (ret != 0) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ send an oplock break to a client
+*/
+static NTSTATUS odb_oplock_break_send(struct messaging_context *msg_ctx,
+ struct opendb_entry *e,
+ uint8_t level)
+{
+ NTSTATUS status;
+ struct opendb_oplock_break op_break;
+ DATA_BLOB blob;
+
+ ZERO_STRUCT(op_break);
+
+ /* tell the server handling this open file about the need to send the client
+ a break */
+ op_break.file_handle = e->file_handle;
+ op_break.level = level;
+
+ blob = data_blob_const(&op_break, sizeof(op_break));
+
+ status = messaging_send(msg_ctx, e->server,
+ MSG_NTVFS_OPLOCK_BREAK, &blob);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ return NT_STATUS_OK;
+}
+
+static bool access_attributes_only(uint32_t access_mask,
+ uint32_t open_disposition,
+ bool break_to_none)
+{
+ switch (open_disposition) {
+ case NTCREATEX_DISP_SUPERSEDE:
+ case NTCREATEX_DISP_OVERWRITE_IF:
+ case NTCREATEX_DISP_OVERWRITE:
+ return false;
+ default:
+ break;
+ }
+
+ if (break_to_none) {
+ return false;
+ }
+
+#define CHECK_MASK(m,g) ((m) && (((m) & ~(g))==0) && (((m) & (g)) != 0))
+ return CHECK_MASK(access_mask,
+ SEC_STD_SYNCHRONIZE |
+ SEC_FILE_READ_ATTRIBUTE |
+ SEC_FILE_WRITE_ATTRIBUTE);
+#undef CHECK_MASK
+}
+
+static NTSTATUS odb_tdb_open_can_internal(struct odb_context *odb,
+ const struct opendb_file *file,
+ uint32_t stream_id, uint32_t share_access,
+ uint32_t access_mask, bool delete_on_close,
+ uint32_t open_disposition, bool break_to_none,
+ bool *_attrs_only)
+{
+ NTSTATUS status;
+ uint32_t i;
+ bool attrs_only = false;
+
+ /* see if anyone has an oplock, which we need to break */
+ for (i=0;i<file->num_entries;i++) {
+ if (file->entries[i].oplock_level == OPLOCK_BATCH) {
+ bool oplock_return = OPLOCK_BREAK_TO_LEVEL_II;
+ /* if this is an attribute only access
+ * it doesn't conflict with a BACTCH oplock
+ * but we'll not grant the oplock below
+ */
+ attrs_only = access_attributes_only(access_mask,
+ open_disposition,
+ break_to_none);
+ if (attrs_only) {
+ break;
+ }
+ /* a batch oplock caches close calls, which
+ means the client application might have
+ already closed the file. We have to allow
+ this close to propogate by sending a oplock
+ break request and suspending this call
+ until the break is acknowledged or the file
+ is closed */
+ if (break_to_none ||
+ !file->entries[i].allow_level_II_oplock) {
+ oplock_return = OPLOCK_BREAK_TO_NONE;
+ }
+ odb_oplock_break_send(odb->ntvfs_ctx->msg_ctx,
+ &file->entries[i],
+ oplock_return);
+ return NT_STATUS_OPLOCK_NOT_GRANTED;
+ }
+ }
+
+ if (file->delete_on_close) {
+ /* while delete on close is set, no new opens are allowed */
+ return NT_STATUS_DELETE_PENDING;
+ }
+
+ if (file->num_entries != 0 && delete_on_close) {
+ return NT_STATUS_SHARING_VIOLATION;
+ }
+
+ /* check for sharing violations */
+ for (i=0;i<file->num_entries;i++) {
+ status = share_conflict(&file->entries[i], stream_id,
+ share_access, access_mask);
+ NT_STATUS_NOT_OK_RETURN(status);
+ }
+
+ /* we now know the open could succeed, but we need to check
+ for any exclusive oplocks. We can't grant a second open
+ till these are broken. Note that we check for batch oplocks
+ before checking for sharing violations, and check for
+ exclusive oplocks afterwards. */
+ for (i=0;i<file->num_entries;i++) {
+ if (file->entries[i].oplock_level == OPLOCK_EXCLUSIVE) {
+ bool oplock_return = OPLOCK_BREAK_TO_LEVEL_II;
+ /* if this is an attribute only access
+ * it doesn't conflict with an EXCLUSIVE oplock
+ * but we'll not grant the oplock below
+ */
+ attrs_only = access_attributes_only(access_mask,
+ open_disposition,
+ break_to_none);
+ if (attrs_only) {
+ break;
+ }
+ /*
+ * send an oplock break to the holder of the
+ * oplock and tell caller to retry later
+ */
+ if (break_to_none ||
+ !file->entries[i].allow_level_II_oplock) {
+ oplock_return = OPLOCK_BREAK_TO_NONE;
+ }
+ odb_oplock_break_send(odb->ntvfs_ctx->msg_ctx,
+ &file->entries[i],
+ oplock_return);
+ return NT_STATUS_OPLOCK_NOT_GRANTED;
+ }
+ }
+
+ if (_attrs_only) {
+ *_attrs_only = attrs_only;
+ }
+ return NT_STATUS_OK;
+}
+
+/*
+ register an open file in the open files database.
+ The share_access rules are implemented by odb_can_open()
+ and it's needed to call odb_can_open() before
+ odb_open_file() otherwise NT_STATUS_INTERNAL_ERROR is returned
+
+ Note that the path is only used by the delete on close logic, not
+ for comparing with other filenames
+*/
+static NTSTATUS odb_tdb_open_file(struct odb_lock *lck,
+ void *file_handle, const char *path,
+ int *fd, NTTIME open_write_time,
+ bool allow_level_II_oplock,
+ uint32_t oplock_level, uint32_t *oplock_granted)
+{
+ struct odb_context *odb = lck->odb;
+
+ if (!lck->can_open.e) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ if (odb->oplocks == false) {
+ oplock_level = OPLOCK_NONE;
+ }
+
+ if (!oplock_granted) {
+ oplock_level = OPLOCK_NONE;
+ }
+
+ if (lck->file.path == NULL) {
+ lck->file.path = talloc_strdup(lck, path);
+ NT_STATUS_HAVE_NO_MEMORY(lck->file.path);
+ }
+
+ if (lck->file.open_write_time == 0) {
+ lck->file.open_write_time = open_write_time;
+ }
+
+ /*
+ possibly grant an exclusive, batch or level2 oplock
+ */
+ if (lck->can_open.attrs_only) {
+ oplock_level = OPLOCK_NONE;
+ } else if (oplock_level == OPLOCK_EXCLUSIVE) {
+ if (lck->file.num_entries == 0) {
+ oplock_level = OPLOCK_EXCLUSIVE;
+ } else if (allow_level_II_oplock) {
+ oplock_level = OPLOCK_LEVEL_II;
+ } else {
+ oplock_level = OPLOCK_NONE;
+ }
+ } else if (oplock_level == OPLOCK_BATCH) {
+ if (lck->file.num_entries == 0) {
+ oplock_level = OPLOCK_BATCH;
+ } else if (allow_level_II_oplock) {
+ oplock_level = OPLOCK_LEVEL_II;
+ } else {
+ oplock_level = OPLOCK_NONE;
+ }
+ } else if (oplock_level == OPLOCK_LEVEL_II) {
+ oplock_level = OPLOCK_LEVEL_II;
+ } else {
+ oplock_level = OPLOCK_NONE;
+ }
+
+ lck->can_open.e->file_handle = file_handle;
+ lck->can_open.e->fd = fd;
+ lck->can_open.e->allow_level_II_oplock = allow_level_II_oplock;
+ lck->can_open.e->oplock_level = oplock_level;
+
+ if (odb->lease_ctx && fd) {
+ NTSTATUS status;
+ status = sys_lease_setup(odb->lease_ctx, lck->can_open.e);
+ NT_STATUS_NOT_OK_RETURN(status);
+ }
+
+ if (oplock_granted) {
+ if (lck->can_open.e->oplock_level == OPLOCK_EXCLUSIVE) {
+ *oplock_granted = EXCLUSIVE_OPLOCK_RETURN;
+ } else if (lck->can_open.e->oplock_level == OPLOCK_BATCH) {
+ *oplock_granted = BATCH_OPLOCK_RETURN;
+ } else if (lck->can_open.e->oplock_level == OPLOCK_LEVEL_II) {
+ *oplock_granted = LEVEL_II_OPLOCK_RETURN;
+ } else {
+ *oplock_granted = NO_OPLOCK_RETURN;
+ }
+ }
+
+ /* it doesn't conflict, so add it to the end */
+ lck->file.entries = talloc_realloc(lck, lck->file.entries,
+ struct opendb_entry,
+ lck->file.num_entries+1);
+ NT_STATUS_HAVE_NO_MEMORY(lck->file.entries);
+
+ lck->file.entries[lck->file.num_entries] = *lck->can_open.e;
+ lck->file.num_entries++;
+
+ talloc_free(lck->can_open.e);
+ lck->can_open.e = NULL;
+
+ return odb_push_record(lck, &lck->file);
+}
+
+
+/*
+ register a pending open file in the open files database
+*/
+static NTSTATUS odb_tdb_open_file_pending(struct odb_lock *lck, void *private_data)
+{
+ struct odb_context *odb = lck->odb;
+
+ if (lck->file.path == NULL) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ lck->file.pending = talloc_realloc(lck, lck->file.pending,
+ struct opendb_pending,
+ lck->file.num_pending+1);
+ NT_STATUS_HAVE_NO_MEMORY(lck->file.pending);
+
+ lck->file.pending[lck->file.num_pending].server = odb->ntvfs_ctx->server_id;
+ lck->file.pending[lck->file.num_pending].notify_ptr = private_data;
+
+ lck->file.num_pending++;
+
+ return odb_push_record(lck, &lck->file);
+}
+
+
+/*
+ remove a opendb entry
+*/
+static NTSTATUS odb_tdb_close_file(struct odb_lock *lck, void *file_handle,
+ const char **_delete_path)
+{
+ struct odb_context *odb = lck->odb;
+ const char *delete_path = NULL;
+ int i;
+
+ if (lck->file.path == NULL) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ /* find the entry, and delete it */
+ for (i=0;i<lck->file.num_entries;i++) {
+ if (file_handle == lck->file.entries[i].file_handle &&
+ cluster_id_equal(&odb->ntvfs_ctx->server_id, &lck->file.entries[i].server)) {
+ if (lck->file.entries[i].delete_on_close) {
+ lck->file.delete_on_close = true;
+ }
+ if (odb->lease_ctx && lck->file.entries[i].fd) {
+ NTSTATUS status;
+ status = sys_lease_remove(odb->lease_ctx, &lck->file.entries[i]);
+ NT_STATUS_NOT_OK_RETURN(status);
+ }
+ if (i < lck->file.num_entries-1) {
+ memmove(lck->file.entries+i, lck->file.entries+i+1,
+ (lck->file.num_entries - (i+1)) *
+ sizeof(struct opendb_entry));
+ }
+ break;
+ }
+ }
+
+ if (i == lck->file.num_entries) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* send any pending notifications, removing them once sent */
+ for (i=0;i<lck->file.num_pending;i++) {
+ messaging_send_ptr(odb->ntvfs_ctx->msg_ctx,
+ lck->file.pending[i].server,
+ MSG_PVFS_RETRY_OPEN,
+ lck->file.pending[i].notify_ptr);
+ }
+ lck->file.num_pending = 0;
+
+ lck->file.num_entries--;
+
+ if (lck->file.num_entries == 0 && lck->file.delete_on_close) {
+ delete_path = lck->file.path;
+ }
+
+ if (_delete_path) {
+ *_delete_path = delete_path;
+ }
+
+ return odb_push_record(lck, &lck->file);
+}
+
+/*
+ update the oplock level of the client
+*/
+static NTSTATUS odb_tdb_update_oplock(struct odb_lock *lck, void *file_handle,
+ uint32_t oplock_level)
+{
+ struct odb_context *odb = lck->odb;
+ int i;
+
+ if (lck->file.path == NULL) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ /* find the entry, and update it */
+ for (i=0;i<lck->file.num_entries;i++) {
+ if (file_handle == lck->file.entries[i].file_handle &&
+ cluster_id_equal(&odb->ntvfs_ctx->server_id, &lck->file.entries[i].server)) {
+ lck->file.entries[i].oplock_level = oplock_level;
+
+ if (odb->lease_ctx && lck->file.entries[i].fd) {
+ NTSTATUS status;
+ status = sys_lease_update(odb->lease_ctx, &lck->file.entries[i]);
+ NT_STATUS_NOT_OK_RETURN(status);
+ }
+
+ break;
+ }
+ }
+
+ if (i == lck->file.num_entries) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* send any pending notifications, removing them once sent */
+ for (i=0;i<lck->file.num_pending;i++) {
+ messaging_send_ptr(odb->ntvfs_ctx->msg_ctx,
+ lck->file.pending[i].server,
+ MSG_PVFS_RETRY_OPEN,
+ lck->file.pending[i].notify_ptr);
+ }
+ lck->file.num_pending = 0;
+
+ return odb_push_record(lck, &lck->file);
+}
+
+/*
+ send oplocks breaks to none to all level2 holders
+*/
+static NTSTATUS odb_tdb_break_oplocks(struct odb_lock *lck)
+{
+ struct odb_context *odb = lck->odb;
+ int i;
+ bool modified = false;
+
+ /* see if anyone has an oplock, which we need to break */
+ for (i=0;i<lck->file.num_entries;i++) {
+ if (lck->file.entries[i].oplock_level == OPLOCK_LEVEL_II) {
+ /*
+ * there could be multiple level2 oplocks
+ * and we just send a break to none to all of them
+ * without waiting for a release
+ */
+ odb_oplock_break_send(odb->ntvfs_ctx->msg_ctx,
+ &lck->file.entries[i],
+ OPLOCK_BREAK_TO_NONE);
+ lck->file.entries[i].oplock_level = OPLOCK_NONE;
+ modified = true;
+ }
+ }
+
+ if (modified) {
+ return odb_push_record(lck, &lck->file);
+ }
+ return NT_STATUS_OK;
+}
+
+/*
+ remove a pending opendb entry
+*/
+static NTSTATUS odb_tdb_remove_pending(struct odb_lock *lck, void *private_data)
+{
+ struct odb_context *odb = lck->odb;
+ int i;
+
+ if (lck->file.path == NULL) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ /* find the entry, and delete it */
+ for (i=0;i<lck->file.num_pending;i++) {
+ if (private_data == lck->file.pending[i].notify_ptr &&
+ cluster_id_equal(&odb->ntvfs_ctx->server_id, &lck->file.pending[i].server)) {
+ if (i < lck->file.num_pending-1) {
+ memmove(lck->file.pending+i, lck->file.pending+i+1,
+ (lck->file.num_pending - (i+1)) *
+ sizeof(struct opendb_pending));
+ }
+ break;
+ }
+ }
+
+ if (i == lck->file.num_pending) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ lck->file.num_pending--;
+
+ return odb_push_record(lck, &lck->file);
+}
+
+
+/*
+ rename the path in a open file
+*/
+static NTSTATUS odb_tdb_rename(struct odb_lock *lck, const char *path)
+{
+ if (lck->file.path == NULL) {
+ /* not having the record at all is OK */
+ return NT_STATUS_OK;
+ }
+
+ lck->file.path = talloc_strdup(lck, path);
+ NT_STATUS_HAVE_NO_MEMORY(lck->file.path);
+
+ return odb_push_record(lck, &lck->file);
+}
+
+/*
+ get the path of an open file
+*/
+static NTSTATUS odb_tdb_get_path(struct odb_lock *lck, const char **path)
+{
+ *path = NULL;
+
+ /* we don't ignore NT_STATUS_OBJECT_NAME_NOT_FOUND here */
+ if (lck->file.path == NULL) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ *path = lck->file.path;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ update delete on close flag on an open file
+*/
+static NTSTATUS odb_tdb_set_delete_on_close(struct odb_lock *lck, bool del_on_close)
+{
+ if (lck->file.path == NULL) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ lck->file.delete_on_close = del_on_close;
+
+ return odb_push_record(lck, &lck->file);
+}
+
+/*
+ update the write time on an open file
+*/
+static NTSTATUS odb_tdb_set_write_time(struct odb_lock *lck,
+ NTTIME write_time, bool force)
+{
+ if (lck->file.path == NULL) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ if (lck->file.changed_write_time != 0 && !force) {
+ return NT_STATUS_OK;
+ }
+
+ lck->file.changed_write_time = write_time;
+
+ return odb_push_record(lck, &lck->file);
+}
+
+/*
+ return the current value of the delete_on_close bit, and how many
+ people still have the file open
+*/
+static NTSTATUS odb_tdb_get_file_infos(struct odb_context *odb, DATA_BLOB *key,
+ bool *del_on_close, NTTIME *write_time)
+{
+ struct odb_lock *lck;
+
+ if (del_on_close) {
+ *del_on_close = false;
+ }
+ if (write_time) {
+ *write_time = 0;
+ }
+
+ lck = odb_lock(odb, odb, key);
+ NT_STATUS_HAVE_NO_MEMORY(lck);
+
+ if (del_on_close) {
+ *del_on_close = lck->file.delete_on_close;
+ }
+ if (write_time) {
+ if (lck->file.changed_write_time == 0) {
+ *write_time = lck->file.open_write_time;
+ } else {
+ *write_time = lck->file.changed_write_time;
+ }
+ }
+
+ talloc_free(lck);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ determine if a file can be opened with the given share_access,
+ create_options and access_mask
+*/
+static NTSTATUS odb_tdb_can_open(struct odb_lock *lck,
+ uint32_t stream_id, uint32_t share_access,
+ uint32_t access_mask, bool delete_on_close,
+ uint32_t open_disposition, bool break_to_none)
+{
+ struct odb_context *odb = lck->odb;
+ NTSTATUS status;
+
+ status = odb_tdb_open_can_internal(odb, &lck->file, stream_id,
+ share_access, access_mask,
+ delete_on_close, open_disposition,
+ break_to_none, &lck->can_open.attrs_only);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ lck->can_open.e = talloc(lck, struct opendb_entry);
+ NT_STATUS_HAVE_NO_MEMORY(lck->can_open.e);
+
+ lck->can_open.e->server = odb->ntvfs_ctx->server_id;
+ lck->can_open.e->file_handle = NULL;
+ lck->can_open.e->fd = NULL;
+ lck->can_open.e->stream_id = stream_id;
+ lck->can_open.e->share_access = share_access;
+ lck->can_open.e->access_mask = access_mask;
+ lck->can_open.e->delete_on_close = delete_on_close;
+ lck->can_open.e->allow_level_II_oplock = false;
+ lck->can_open.e->oplock_level = OPLOCK_NONE;
+
+ return NT_STATUS_OK;
+}
+
+
+static const struct opendb_ops opendb_tdb_ops = {
+ .odb_init = odb_tdb_init,
+ .odb_lock = odb_tdb_lock,
+ .odb_get_key = odb_tdb_get_key,
+ .odb_open_file = odb_tdb_open_file,
+ .odb_open_file_pending = odb_tdb_open_file_pending,
+ .odb_close_file = odb_tdb_close_file,
+ .odb_remove_pending = odb_tdb_remove_pending,
+ .odb_rename = odb_tdb_rename,
+ .odb_get_path = odb_tdb_get_path,
+ .odb_set_delete_on_close = odb_tdb_set_delete_on_close,
+ .odb_set_write_time = odb_tdb_set_write_time,
+ .odb_get_file_infos = odb_tdb_get_file_infos,
+ .odb_can_open = odb_tdb_can_open,
+ .odb_update_oplock = odb_tdb_update_oplock,
+ .odb_break_oplocks = odb_tdb_break_oplocks
+};
+
+
+void odb_tdb_init_ops(void)
+{
+ sys_lease_init();
+ odb_set_ops(&opendb_tdb_ops);
+}
diff --git a/source4/ntvfs/config.mk b/source4/ntvfs/config.mk
new file mode 100644
index 0000000000..593c526edb
--- /dev/null
+++ b/source4/ntvfs/config.mk
@@ -0,0 +1,103 @@
+# NTVFS Server subsystem
+mkinclude posix/config.mk
+mkinclude common/config.mk
+mkinclude unixuid/config.mk
+mkinclude sysdep/config.mk
+
+################################################
+# Start MODULE ntvfs_cifs
+[MODULE::ntvfs_cifs]
+INIT_FUNCTION = ntvfs_cifs_init
+SUBSYSTEM = ntvfs
+PRIVATE_DEPENDENCIES = \
+ LIBCLI_SMB LIBCLI_RAW
+# End MODULE ntvfs_cifs
+################################################
+
+ntvfs_cifs_OBJ_FILES = $(ntvfssrcdir)/cifs/vfs_cifs.o
+
+
+################################################
+# Start MODULE ntvfs_smb2
+[MODULE::ntvfs_smb2]
+INIT_FUNCTION = ntvfs_smb2_init
+SUBSYSTEM = ntvfs
+PRIVATE_DEPENDENCIES = \
+ LIBCLI_SMB LIBCLI_RAW
+# End MODULE ntvfs_smb2
+################################################
+
+ntvfs_smb2_OBJ_FILES = $(ntvfssrcdir)/smb2/vfs_smb2.o
+
+
+################################################
+# Start MODULE ntvfs_simple
+[MODULE::ntvfs_simple]
+INIT_FUNCTION = ntvfs_simple_init
+SUBSYSTEM = ntvfs
+# End MODULE ntvfs_simple
+################################################
+
+ntvfs_simple_OBJ_FILES = $(addprefix $(ntvfssrcdir)/simple/, vfs_simple.o svfs_util.o)
+
+$(eval $(call proto_header_template,$(ntvfssrcdir)/simple/proto.h,$(ntvfs_simple_OBJ_FILES:.o=.c)))
+
+################################################
+# Start MODULE ntvfs_cifsposix
+[MODULE::ntvfs_cifsposix]
+#ENABLE = NO
+INIT_FUNCTION = ntvfs_cifs_posix_init
+SUBSYSTEM = ntvfs
+# End MODULE ntvfs_cifsposix
+################################################
+
+ntvfs_cifsposix_OBJ_FILES = \
+ $(addprefix $(ntvfssrcdir)/cifs_posix_cli/, vfs_cifs_posix.o svfs_util.o)
+
+$(eval $(call proto_header_template,$(ntvfssrcdir)/cifs_posix_cli/proto.h,$(ntvfs_cifsposix_OBJ_FILES:.o=.c)))
+
+################################################
+# Start MODULE ntvfs_print
+[MODULE::ntvfs_print]
+INIT_FUNCTION = ntvfs_print_init
+SUBSYSTEM = ntvfs
+# End MODULE ntvfs_print
+################################################
+
+ntvfs_print_OBJ_FILES = $(ntvfssrcdir)/print/vfs_print.o
+
+################################################
+# Start MODULE ntvfs_ipc
+[MODULE::ntvfs_ipc]
+SUBSYSTEM = ntvfs
+INIT_FUNCTION = ntvfs_ipc_init
+PRIVATE_DEPENDENCIES = dcerpc_server DCERPC_COMMON
+# End MODULE ntvfs_ipc
+################################################
+
+ntvfs_ipc_OBJ_FILES = $(addprefix $(ntvfssrcdir)/ipc/, vfs_ipc.o ipc_rap.o rap_server.o)
+
+$(eval $(call proto_header_template,$(ntvfssrcdir)/ipc/proto.h,$(ntvfs_ipc_OBJ_FILES:.o=.c)))
+
+################################################
+# Start MODULE ntvfs_nbench
+[MODULE::ntvfs_nbench]
+SUBSYSTEM = ntvfs
+INIT_FUNCTION = ntvfs_nbench_init
+# End MODULE ntvfs_nbench
+################################################
+
+ntvfs_nbench_OBJ_FILES = $(ntvfssrcdir)/nbench/vfs_nbench.o
+
+################################################
+# Start SUBSYSTEM NTVFS
+[SUBSYSTEM::ntvfs]
+
+ntvfs_OBJ_FILES = $(addprefix $(ntvfssrcdir)/, ntvfs_base.o ntvfs_generic.o ntvfs_interface.o ntvfs_util.o)
+
+$(eval $(call proto_header_template,$(ntvfssrcdir)/ntvfs_proto.h,$(ntvfs_OBJ_FILES:.o=.c)))
+
+# PUBLIC_HEADERS += $(ntvfssrcdir)/ntvfs.h
+#
+# End SUBSYSTEM NTVFS
+################################################
diff --git a/source4/ntvfs/ipc/README b/source4/ntvfs/ipc/README
new file mode 100644
index 0000000000..059a7146e5
--- /dev/null
+++ b/source4/ntvfs/ipc/README
@@ -0,0 +1,5 @@
+This is the IPC$ backend for Samba. NTVFS operations that are made on
+IPC$ shares are directed here by default. Most file operations
+are not supported on IPC$ shares.
+
+
diff --git a/source4/ntvfs/ipc/ipc.h b/source4/ntvfs/ipc/ipc.h
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/source4/ntvfs/ipc/ipc.h
diff --git a/source4/ntvfs/ipc/ipc_rap.c b/source4/ntvfs/ipc/ipc_rap.c
new file mode 100644
index 0000000000..04ea70030e
--- /dev/null
+++ b/source4/ntvfs/ipc/ipc_rap.c
@@ -0,0 +1,511 @@
+/*
+ Unix SMB/CIFS implementation.
+ RAP handlers
+
+ Copyright (C) Volker Lendecke 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/raw/interfaces.h"
+#include "libcli/rap/rap.h"
+#include "events/events.h"
+#include "ntvfs/ipc/proto.h"
+#include "librpc/ndr/libndr.h"
+#include "param/param.h"
+
+#define NDR_RETURN(call) do { \
+ enum ndr_err_code _ndr_err; \
+ _ndr_err = call; \
+ if (!NDR_ERR_CODE_IS_SUCCESS(_ndr_err)) { \
+ return ndr_map_error2ntstatus(_ndr_err); \
+ } \
+} while (0)
+
+#define RAP_GOTO(call) do { \
+ result = call; \
+ if (NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL)) {\
+ goto buffer_overflow; \
+ } \
+ if (!NT_STATUS_IS_OK(result)) { \
+ goto done; \
+ } \
+} while (0)
+
+#define NDR_GOTO(call) do { \
+ enum ndr_err_code _ndr_err; \
+ _ndr_err = call; \
+ if (!NDR_ERR_CODE_IS_SUCCESS(_ndr_err)) { \
+ RAP_GOTO(ndr_map_error2ntstatus(_ndr_err)); \
+ } \
+} while (0)
+
+
+#define NERR_notsupported 50
+
+struct rap_string_heap {
+ TALLOC_CTX *mem_ctx;
+ int offset;
+ int num_strings;
+ const char **strings;
+};
+
+struct rap_heap_save {
+ int offset, num_strings;
+};
+
+static void rap_heap_save(struct rap_string_heap *heap,
+ struct rap_heap_save *save)
+{
+ save->offset = heap->offset;
+ save->num_strings = heap->num_strings;
+}
+
+static void rap_heap_restore(struct rap_string_heap *heap,
+ struct rap_heap_save *save)
+{
+ heap->offset = save->offset;
+ heap->num_strings = save->num_strings;
+}
+
+struct rap_call {
+ struct loadparm_context *lp_ctx;
+
+ TALLOC_CTX *mem_ctx;
+ uint16_t callno;
+ const char *paramdesc;
+ const char *datadesc;
+
+ uint16_t status;
+ uint16_t convert;
+
+ uint16_t rcv_paramlen, rcv_datalen;
+
+ struct ndr_push *ndr_push_param;
+ struct ndr_push *ndr_push_data;
+ struct rap_string_heap *heap;
+
+ struct ndr_pull *ndr_pull_param;
+ struct ndr_pull *ndr_pull_data;
+
+ struct tevent_context *event_ctx;
+};
+
+#define RAPNDR_FLAGS (LIBNDR_FLAG_NOALIGN|LIBNDR_FLAG_STR_ASCII|LIBNDR_FLAG_STR_NULLTERM);
+
+static struct rap_call *new_rap_srv_call(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx,
+ struct smb_trans2 *trans)
+{
+ struct rap_call *call;
+
+ call = talloc(mem_ctx, struct rap_call);
+
+ if (call == NULL)
+ return NULL;
+
+ ZERO_STRUCTP(call);
+
+ call->lp_ctx = talloc_reference(call, lp_ctx);
+ call->event_ctx = ev_ctx;
+
+ call->mem_ctx = mem_ctx;
+
+ call->ndr_pull_param = ndr_pull_init_blob(&trans->in.params, mem_ctx, lp_iconv_convenience(lp_ctx));
+ call->ndr_pull_param->flags = RAPNDR_FLAGS;
+
+ call->ndr_pull_data = ndr_pull_init_blob(&trans->in.data, mem_ctx, lp_iconv_convenience(lp_ctx));
+ call->ndr_pull_data->flags = RAPNDR_FLAGS;
+
+ call->heap = talloc(mem_ctx, struct rap_string_heap);
+
+ if (call->heap == NULL)
+ return NULL;
+
+ ZERO_STRUCTP(call->heap);
+
+ call->heap->mem_ctx = mem_ctx;
+
+ return call;
+}
+
+static NTSTATUS rap_srv_pull_word(struct rap_call *call, uint16_t *result)
+{
+ enum ndr_err_code ndr_err;
+
+ if (*call->paramdesc++ != 'W')
+ return NT_STATUS_INVALID_PARAMETER;
+
+ ndr_err = ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, result);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS rap_srv_pull_dword(struct rap_call *call, uint32_t *result)
+{
+ enum ndr_err_code ndr_err;
+
+ if (*call->paramdesc++ != 'D')
+ return NT_STATUS_INVALID_PARAMETER;
+
+ ndr_err = ndr_pull_uint32(call->ndr_pull_param, NDR_SCALARS, result);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS rap_srv_pull_string(struct rap_call *call, const char **result)
+{
+ enum ndr_err_code ndr_err;
+ char paramdesc = *call->paramdesc++;
+
+ if (paramdesc == 'O') {
+ *result = NULL;
+ return NT_STATUS_OK;
+ }
+
+ if (paramdesc != 'z')
+ return NT_STATUS_INVALID_PARAMETER;
+
+ ndr_err = ndr_pull_string(call->ndr_pull_param, NDR_SCALARS, result);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS rap_srv_pull_bufsize(struct rap_call *call, uint16_t *bufsize)
+{
+ enum ndr_err_code ndr_err;
+
+ if ( (*call->paramdesc++ != 'r') || (*call->paramdesc++ != 'L') )
+ return NT_STATUS_INVALID_PARAMETER;
+
+ ndr_err = ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, bufsize);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ call->heap->offset = *bufsize;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS rap_srv_pull_expect_multiple(struct rap_call *call)
+{
+ if ( (*call->paramdesc++ != 'e') || (*call->paramdesc++ != 'h') )
+ return NT_STATUS_INVALID_PARAMETER;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS rap_push_string(struct ndr_push *data_push,
+ struct rap_string_heap *heap,
+ const char *str)
+{
+ size_t space;
+
+ if (str == NULL)
+ str = "";
+
+ space = strlen(str)+1;
+
+ if (heap->offset < space)
+ return NT_STATUS_BUFFER_TOO_SMALL;
+
+ heap->offset -= space;
+
+ NDR_RETURN(ndr_push_uint16(data_push, NDR_SCALARS, heap->offset));
+ NDR_RETURN(ndr_push_uint16(data_push, NDR_SCALARS, 0));
+
+ heap->strings = talloc_realloc(heap->mem_ctx,
+ heap->strings,
+ const char *,
+ heap->num_strings + 1);
+
+ if (heap->strings == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ heap->strings[heap->num_strings] = str;
+ heap->num_strings += 1;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS _rap_netshareenum(struct rap_call *call)
+{
+ struct rap_NetShareEnum r;
+ NTSTATUS result;
+
+ RAP_GOTO(rap_srv_pull_word(call, &r.in.level));
+ RAP_GOTO(rap_srv_pull_bufsize(call, &r.in.bufsize));
+ RAP_GOTO(rap_srv_pull_expect_multiple(call));
+
+ switch(r.in.level) {
+ case 0:
+ if (strcmp(call->datadesc, "B13") != 0)
+ return NT_STATUS_INVALID_PARAMETER;
+ break;
+ case 1:
+ if (strcmp(call->datadesc, "B13BWz") != 0)
+ return NT_STATUS_INVALID_PARAMETER;
+ break;
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ result = rap_netshareenum(call, call->event_ctx, call->lp_ctx, &r);
+
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ for (r.out.count = 0; r.out.count < r.out.available; r.out.count++) {
+
+ int i = r.out.count;
+ uint32_t offset_save;
+ struct rap_heap_save heap_save;
+
+ offset_save = call->ndr_push_data->offset;
+ rap_heap_save(call->heap, &heap_save);
+
+ switch(r.in.level) {
+ case 0:
+ NDR_GOTO(ndr_push_bytes(call->ndr_push_data,
+ (const uint8_t *)r.out.info[i].info0.name,
+ sizeof(r.out.info[i].info0.name)));
+ break;
+ case 1:
+ NDR_GOTO(ndr_push_bytes(call->ndr_push_data,
+ (const uint8_t *)r.out.info[i].info1.name,
+ sizeof(r.out.info[i].info1.name)));
+ NDR_GOTO(ndr_push_uint8(call->ndr_push_data,
+ NDR_SCALARS, r.out.info[i].info1.pad));
+ NDR_GOTO(ndr_push_uint16(call->ndr_push_data,
+ NDR_SCALARS, r.out.info[i].info1.type));
+
+ RAP_GOTO(rap_push_string(call->ndr_push_data,
+ call->heap,
+ r.out.info[i].info1.comment));
+
+ break;
+ }
+
+ if (call->ndr_push_data->offset > call->heap->offset) {
+
+ buffer_overflow:
+
+ call->ndr_push_data->offset = offset_save;
+ rap_heap_restore(call->heap, &heap_save);
+ break;
+ }
+ }
+
+ call->status = r.out.status;
+
+ NDR_RETURN(ndr_push_uint16(call->ndr_push_param, NDR_SCALARS, r.out.count));
+ NDR_RETURN(ndr_push_uint16(call->ndr_push_param, NDR_SCALARS, r.out.available));
+
+ result = NT_STATUS_OK;
+
+ done:
+ return result;
+}
+
+static NTSTATUS _rap_netserverenum2(struct rap_call *call)
+{
+ struct rap_NetServerEnum2 r;
+ NTSTATUS result;
+
+ RAP_GOTO(rap_srv_pull_word(call, &r.in.level));
+ RAP_GOTO(rap_srv_pull_bufsize(call, &r.in.bufsize));
+ RAP_GOTO(rap_srv_pull_expect_multiple(call));
+ RAP_GOTO(rap_srv_pull_dword(call, &r.in.servertype));
+ RAP_GOTO(rap_srv_pull_string(call, &r.in.domain));
+
+ switch(r.in.level) {
+ case 0:
+ if (strcmp(call->datadesc, "B16") != 0)
+ return NT_STATUS_INVALID_PARAMETER;
+ break;
+ case 1:
+ if (strcmp(call->datadesc, "B16BBDz") != 0)
+ return NT_STATUS_INVALID_PARAMETER;
+ break;
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ result = rap_netserverenum2(call, call->lp_ctx, &r);
+
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ for (r.out.count = 0; r.out.count < r.out.available; r.out.count++) {
+
+ int i = r.out.count;
+ uint32_t offset_save;
+ struct rap_heap_save heap_save;
+
+ offset_save = call->ndr_push_data->offset;
+ rap_heap_save(call->heap, &heap_save);
+
+ switch(r.in.level) {
+ case 0:
+ NDR_GOTO(ndr_push_bytes(call->ndr_push_data,
+ (const uint8_t *)r.out.info[i].info0.name,
+ sizeof(r.out.info[i].info0.name)));
+ break;
+ case 1:
+ NDR_GOTO(ndr_push_bytes(call->ndr_push_data,
+ (const uint8_t *)r.out.info[i].info1.name,
+ sizeof(r.out.info[i].info1.name)));
+ NDR_GOTO(ndr_push_uint8(call->ndr_push_data,
+ NDR_SCALARS, r.out.info[i].info1.version_major));
+ NDR_GOTO(ndr_push_uint8(call->ndr_push_data,
+ NDR_SCALARS, r.out.info[i].info1.version_minor));
+ NDR_GOTO(ndr_push_uint32(call->ndr_push_data,
+ NDR_SCALARS, r.out.info[i].info1.servertype));
+
+ RAP_GOTO(rap_push_string(call->ndr_push_data,
+ call->heap,
+ r.out.info[i].info1.comment));
+
+ break;
+ }
+
+ if (call->ndr_push_data->offset > call->heap->offset) {
+
+ buffer_overflow:
+
+ call->ndr_push_data->offset = offset_save;
+ rap_heap_restore(call->heap, &heap_save);
+ break;
+ }
+ }
+
+ call->status = r.out.status;
+
+ NDR_RETURN(ndr_push_uint16(call->ndr_push_param, NDR_SCALARS, r.out.count));
+ NDR_RETURN(ndr_push_uint16(call->ndr_push_param, NDR_SCALARS, r.out.available));
+
+ result = NT_STATUS_OK;
+
+ done:
+ return result;
+}
+
+static NTSTATUS api_Unsupported(struct rap_call *call)
+{
+ call->status = NERR_notsupported;
+ call->convert = 0;
+ return NT_STATUS_OK;
+}
+
+static const struct
+{
+ const char *name;
+ int id;
+ NTSTATUS (*fn)(struct rap_call *call);
+} api_commands[] = {
+ {"NetShareEnum", RAP_WshareEnum, _rap_netshareenum },
+ {"NetServerEnum2", RAP_NetServerEnum2, _rap_netserverenum2 },
+ {NULL, -1, api_Unsupported}
+};
+
+NTSTATUS ipc_rap_call(TALLOC_CTX *mem_ctx, struct tevent_context *event_ctx, struct loadparm_context *lp_ctx,
+ struct smb_trans2 *trans)
+{
+ int i;
+ NTSTATUS result;
+ struct rap_call *call;
+ DATA_BLOB result_param, result_data;
+ struct ndr_push *final_param;
+ struct ndr_push *final_data;
+
+ call = new_rap_srv_call(mem_ctx, event_ctx, lp_ctx, trans);
+
+ if (call == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ NDR_RETURN(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &call->callno));
+ NDR_RETURN(ndr_pull_string(call->ndr_pull_param, NDR_SCALARS,
+ &call->paramdesc));
+ NDR_RETURN(ndr_pull_string(call->ndr_pull_param, NDR_SCALARS,
+ &call->datadesc));
+
+ call->ndr_push_param = ndr_push_init_ctx(call, lp_iconv_convenience(lp_ctx));
+ call->ndr_push_data = ndr_push_init_ctx(call, lp_iconv_convenience(lp_ctx));
+
+ if ((call->ndr_push_param == NULL) || (call->ndr_push_data == NULL))
+ return NT_STATUS_NO_MEMORY;
+
+ call->ndr_push_param->flags = RAPNDR_FLAGS;
+ call->ndr_push_data->flags = RAPNDR_FLAGS;
+
+ result = NT_STATUS_INVALID_SYSTEM_SERVICE;
+
+ for (i=0; api_commands[i].name != NULL; i++) {
+ if (api_commands[i].id == call->callno) {
+ DEBUG(5, ("Running RAP call %s\n",
+ api_commands[i].name));
+ result = api_commands[i].fn(call);
+ break;
+ }
+ }
+
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ result_param = ndr_push_blob(call->ndr_push_param);
+ result_data = ndr_push_blob(call->ndr_push_data);
+
+ final_param = ndr_push_init_ctx(call, lp_iconv_convenience(lp_ctx));
+ final_data = ndr_push_init_ctx(call, lp_iconv_convenience(lp_ctx));
+
+ if ((final_param == NULL) || (final_data == NULL))
+ return NT_STATUS_NO_MEMORY;
+
+ final_param->flags = RAPNDR_FLAGS;
+ final_data->flags = RAPNDR_FLAGS;
+
+ NDR_RETURN(ndr_push_uint16(final_param, NDR_SCALARS, call->status));
+ NDR_RETURN(ndr_push_uint16(final_param,
+ NDR_SCALARS, call->heap->offset - result_data.length));
+ NDR_RETURN(ndr_push_bytes(final_param, result_param.data,
+ result_param.length));
+
+ NDR_RETURN(ndr_push_bytes(final_data, result_data.data,
+ result_data.length));
+
+ for (i=call->heap->num_strings-1; i>=0; i--)
+ NDR_RETURN(ndr_push_string(final_data, NDR_SCALARS,
+ call->heap->strings[i]));
+
+ trans->out.setup_count = 0;
+ trans->out.setup = NULL;
+ trans->out.params = ndr_push_blob(final_param);
+ trans->out.data = ndr_push_blob(final_data);
+
+ return result;
+}
diff --git a/source4/ntvfs/ipc/np_echo.c b/source4/ntvfs/ipc/np_echo.c
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/source4/ntvfs/ipc/np_echo.c
diff --git a/source4/ntvfs/ipc/rap_server.c b/source4/ntvfs/ipc/rap_server.c
new file mode 100644
index 0000000000..2bc07c3e7b
--- /dev/null
+++ b/source4/ntvfs/ipc/rap_server.c
@@ -0,0 +1,89 @@
+/*
+ Unix SMB/CIFS implementation.
+ RAP handlers
+
+ Copyright (C) Volker Lendecke 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "param/share.h"
+#include "libcli/rap/rap.h"
+#include "libcli/raw/interfaces.h"
+#include "librpc/gen_ndr/srvsvc.h"
+#include "rpc_server/common/common.h"
+#include "param/param.h"
+#include "ntvfs/ipc/ipc.h"
+#include "ntvfs/ipc/proto.h"
+
+/* At this moment these are just dummy functions, but you might get the
+ * idea. */
+
+NTSTATUS rap_netshareenum(TALLOC_CTX *mem_ctx,
+ struct tevent_context *event_ctx,
+ struct loadparm_context *lp_ctx,
+ struct rap_NetShareEnum *r)
+{
+ NTSTATUS nterr;
+ const char **snames;
+ struct share_context *sctx;
+ struct share_config *scfg;
+ int i, j, count;
+
+ r->out.status = 0;
+ r->out.available = 0;
+ r->out.info = NULL;
+
+ nterr = share_get_context_by_name(mem_ctx, lp_share_backend(lp_ctx), event_ctx, lp_ctx, &sctx);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return nterr;
+ }
+
+ nterr = share_list_all(mem_ctx, sctx, &count, &snames);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return nterr;
+ }
+
+ r->out.available = count;
+ r->out.info = talloc_array(mem_ctx,
+ union rap_shareenum_info, r->out.available);
+
+ for (i = 0, j = 0; i < r->out.available; i++) {
+ if (!NT_STATUS_IS_OK(share_get_config(mem_ctx, sctx, snames[i], &scfg))) {
+ DEBUG(3, ("WARNING: Service [%s] disappeared after enumeration!\n", snames[i]));
+ continue;
+ }
+ strncpy(r->out.info[j].info1.name,
+ snames[i],
+ sizeof(r->out.info[0].info1.name));
+ r->out.info[i].info1.pad = 0;
+ r->out.info[i].info1.type = dcesrv_common_get_share_type(mem_ctx, NULL, scfg);
+ r->out.info[i].info1.comment = talloc_strdup(mem_ctx, share_string_option(scfg, SHARE_COMMENT, ""));
+ talloc_free(scfg);
+ j++;
+ }
+ r->out.available = j;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS rap_netserverenum2(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ struct rap_NetServerEnum2 *r)
+{
+ r->out.status = 0;
+ r->out.available = 0;
+ return NT_STATUS_OK;
+}
diff --git a/source4/ntvfs/ipc/vfs_ipc.c b/source4/ntvfs/ipc/vfs_ipc.c
new file mode 100644
index 0000000000..2f05a86dfa
--- /dev/null
+++ b/source4/ntvfs/ipc/vfs_ipc.c
@@ -0,0 +1,953 @@
+/*
+ Unix SMB/CIFS implementation.
+ default IPC$ NTVFS backend
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Stefan (metze) Metzmacher 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 <http://www.gnu.org/licenses/>.
+*/
+/*
+ this implements the IPC$ backend, called by the NTVFS subsystem to
+ handle requests on IPC$ shares
+*/
+
+
+#include "includes.h"
+#include "../lib/util/dlinklist.h"
+#include "ntvfs/ntvfs.h"
+#include "libcli/rap/rap.h"
+#include "ntvfs/ipc/proto.h"
+#include "rpc_server/dcerpc_server.h"
+#include "libcli/raw/ioctl.h"
+#include "param/param.h"
+
+/* this is the private structure used to keep the state of an open
+ ipc$ connection. It needs to keep information about all open
+ pipes */
+struct ipc_private {
+ struct ntvfs_module_context *ntvfs;
+
+ struct dcesrv_context *dcesrv;
+
+ /* a list of open pipes */
+ struct pipe_state {
+ struct pipe_state *next, *prev;
+ struct ipc_private *ipriv;
+ const char *pipe_name;
+ struct ntvfs_handle *handle;
+ struct dcesrv_connection *dce_conn;
+ uint16_t ipc_state;
+ } *pipe_list;
+};
+
+
+/*
+ find a open pipe give a file handle
+*/
+static struct pipe_state *pipe_state_find(struct ipc_private *ipriv, struct ntvfs_handle *handle)
+{
+ struct pipe_state *s;
+ void *p;
+
+ p = ntvfs_handle_get_backend_data(handle, ipriv->ntvfs);
+ if (!p) return NULL;
+
+ s = talloc_get_type(p, struct pipe_state);
+ if (!s) return NULL;
+
+ return s;
+}
+
+/*
+ find a open pipe give a wire fnum
+*/
+static struct pipe_state *pipe_state_find_key(struct ipc_private *ipriv, struct ntvfs_request *req, const DATA_BLOB *key)
+{
+ struct ntvfs_handle *h;
+
+ h = ntvfs_handle_search_by_wire_key(ipriv->ntvfs, req, key);
+ if (!h) return NULL;
+
+ return pipe_state_find(ipriv, h);
+}
+
+
+/*
+ connect to a share - always works
+*/
+static NTSTATUS ipc_connect(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, const char *sharename)
+{
+ NTSTATUS status;
+ struct ipc_private *ipriv;
+
+ ntvfs->ctx->fs_type = talloc_strdup(ntvfs->ctx, "IPC");
+ NT_STATUS_HAVE_NO_MEMORY(ntvfs->ctx->fs_type);
+
+ ntvfs->ctx->dev_type = talloc_strdup(ntvfs->ctx, "IPC");
+ NT_STATUS_HAVE_NO_MEMORY(ntvfs->ctx->dev_type);
+
+ /* prepare the private state for this connection */
+ ipriv = talloc(ntvfs, struct ipc_private);
+ NT_STATUS_HAVE_NO_MEMORY(ipriv);
+
+ ntvfs->private_data = ipriv;
+
+ ipriv->ntvfs = ntvfs;
+ ipriv->pipe_list = NULL;
+
+ /* setup the DCERPC server subsystem */
+ status = dcesrv_init_ipc_context(ipriv, ntvfs->ctx->lp_ctx, &ipriv->dcesrv);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ disconnect from a share
+*/
+static NTSTATUS ipc_disconnect(struct ntvfs_module_context *ntvfs)
+{
+ return NT_STATUS_OK;
+}
+
+/*
+ delete a file
+*/
+static NTSTATUS ipc_unlink(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_unlink *unl)
+{
+ return NT_STATUS_ACCESS_DENIED;
+}
+
+/*
+ check if a directory exists
+*/
+static NTSTATUS ipc_chkpath(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_chkpath *cp)
+{
+ return NT_STATUS_ACCESS_DENIED;
+}
+
+/*
+ return info on a pathname
+*/
+static NTSTATUS ipc_qpathinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_fileinfo *info)
+{
+ switch (info->generic.level) {
+ case RAW_FILEINFO_GENERIC:
+ return NT_STATUS_INVALID_DEVICE_REQUEST;
+ case RAW_FILEINFO_GETATTR:
+ return NT_STATUS_ACCESS_DENIED;
+ default:
+ return ntvfs_map_qpathinfo(ntvfs, req, info);
+ }
+}
+
+/*
+ set info on a pathname
+*/
+static NTSTATUS ipc_setpathinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_setfileinfo *st)
+{
+ return NT_STATUS_ACCESS_DENIED;
+}
+
+
+/*
+ destroy a open pipe structure
+*/
+static int ipc_fd_destructor(struct pipe_state *p)
+{
+ DLIST_REMOVE(p->ipriv->pipe_list, p);
+ ntvfs_handle_remove_backend_data(p->handle, p->ipriv->ntvfs);
+ return 0;
+}
+
+static struct socket_address *ipc_get_my_addr(struct dcesrv_connection *dce_conn, TALLOC_CTX *mem_ctx)
+{
+ struct ipc_private *ipriv = dce_conn->transport.private_data;
+
+ return ntvfs_get_my_addr(ipriv->ntvfs, mem_ctx);
+}
+
+static struct socket_address *ipc_get_peer_addr(struct dcesrv_connection *dce_conn, TALLOC_CTX *mem_ctx)
+{
+ struct ipc_private *ipriv = dce_conn->transport.private_data;
+
+ return ntvfs_get_peer_addr(ipriv->ntvfs, mem_ctx);
+}
+
+/*
+ open a file backend - used for MSRPC pipes
+*/
+static NTSTATUS ipc_open_generic(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, const char *fname,
+ struct pipe_state **ps)
+{
+ struct pipe_state *p;
+ NTSTATUS status;
+ struct dcerpc_binding *ep_description;
+ struct ipc_private *ipriv = ntvfs->private_data;
+ struct ntvfs_handle *h;
+
+ status = ntvfs_handle_new(ntvfs, req, &h);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ p = talloc(h, struct pipe_state);
+ NT_STATUS_HAVE_NO_MEMORY(p);
+
+ ep_description = talloc(req, struct dcerpc_binding);
+ NT_STATUS_HAVE_NO_MEMORY(ep_description);
+
+ while (fname[0] == '\\') fname++;
+
+ p->pipe_name = talloc_asprintf(p, "\\pipe\\%s", fname);
+ NT_STATUS_HAVE_NO_MEMORY(p->pipe_name);
+
+ p->handle = h;
+ p->ipc_state = 0x5ff;
+
+ /*
+ we're all set, now ask the dcerpc server subsystem to open the
+ endpoint. At this stage the pipe isn't bound, so we don't
+ know what interface the user actually wants, just that they want
+ one of the interfaces attached to this pipe endpoint.
+ */
+ ep_description->transport = NCACN_NP;
+ ep_description->endpoint = talloc_reference(ep_description, p->pipe_name);
+
+ /* The session info is refcount-increased in the
+ * dcesrv_endpoint_search_connect() function
+ */
+ status = dcesrv_endpoint_search_connect(ipriv->dcesrv,
+ p,
+ ep_description,
+ h->session_info,
+ ntvfs->ctx->event_ctx,
+ ntvfs->ctx->msg_ctx,
+ ntvfs->ctx->server_id,
+ 0,
+ &p->dce_conn);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ p->dce_conn->transport.private_data = ipriv;
+ p->dce_conn->transport.report_output_data = NULL;
+ p->dce_conn->transport.get_my_addr = ipc_get_my_addr;
+ p->dce_conn->transport.get_peer_addr = ipc_get_peer_addr;
+
+ DLIST_ADD(ipriv->pipe_list, p);
+
+ p->ipriv = ipriv;
+
+ talloc_set_destructor(p, ipc_fd_destructor);
+
+ status = ntvfs_handle_set_backend_data(h, ipriv->ntvfs, p);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ *ps = p;
+ return NT_STATUS_OK;
+}
+
+/*
+ open a file with ntcreatex - used for MSRPC pipes
+*/
+static NTSTATUS ipc_open_ntcreatex(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_open *oi)
+{
+ struct pipe_state *p;
+ NTSTATUS status;
+
+ status = ipc_open_generic(ntvfs, req, oi->ntcreatex.in.fname, &p);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ ZERO_STRUCT(oi->ntcreatex.out);
+ oi->ntcreatex.out.file.ntvfs= p->handle;
+ oi->ntcreatex.out.ipc_state = p->ipc_state;
+ oi->ntcreatex.out.file_type = FILE_TYPE_MESSAGE_MODE_PIPE;
+
+ return status;
+}
+
+/*
+ open a file with openx - used for MSRPC pipes
+*/
+static NTSTATUS ipc_open_openx(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_open *oi)
+{
+ struct pipe_state *p;
+ NTSTATUS status;
+ const char *fname = oi->openx.in.fname;
+
+ status = ipc_open_generic(ntvfs, req, fname, &p);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ ZERO_STRUCT(oi->openx.out);
+ oi->openx.out.file.ntvfs= p->handle;
+ oi->openx.out.ftype = 2;
+ oi->openx.out.devstate = p->ipc_state;
+
+ return status;
+}
+
+/*
+ open a file with SMB2 Create - used for MSRPC pipes
+*/
+static NTSTATUS ipc_open_smb2(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_open *oi)
+{
+ struct pipe_state *p;
+ NTSTATUS status;
+
+ status = ipc_open_generic(ntvfs, req, oi->smb2.in.fname, &p);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ ZERO_STRUCT(oi->smb2.out);
+ oi->smb2.out.file.ntvfs = p->handle;
+ oi->smb2.out.oplock_level = oi->smb2.in.oplock_level;
+ oi->smb2.out.create_action = NTCREATEX_ACTION_EXISTED;
+ oi->smb2.out.create_time = 0;
+ oi->smb2.out.access_time = 0;
+ oi->smb2.out.write_time = 0;
+ oi->smb2.out.change_time = 0;
+ oi->smb2.out.alloc_size = 4096;
+ oi->smb2.out.size = 0;
+ oi->smb2.out.file_attr = FILE_ATTRIBUTE_NORMAL;
+ oi->smb2.out.reserved2 = 0;
+
+ return status;
+}
+
+/*
+ open a file - used for MSRPC pipes
+*/
+static NTSTATUS ipc_open(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_open *oi)
+{
+ NTSTATUS status;
+
+ switch (oi->generic.level) {
+ case RAW_OPEN_NTCREATEX:
+ status = ipc_open_ntcreatex(ntvfs, req, oi);
+ break;
+ case RAW_OPEN_OPENX:
+ status = ipc_open_openx(ntvfs, req, oi);
+ break;
+ case RAW_OPEN_SMB2:
+ status = ipc_open_smb2(ntvfs, req, oi);
+ break;
+ default:
+ status = NT_STATUS_NOT_SUPPORTED;
+ break;
+ }
+
+ return status;
+}
+
+/*
+ create a directory
+*/
+static NTSTATUS ipc_mkdir(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_mkdir *md)
+{
+ return NT_STATUS_ACCESS_DENIED;
+}
+
+/*
+ remove a directory
+*/
+static NTSTATUS ipc_rmdir(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, struct smb_rmdir *rd)
+{
+ return NT_STATUS_ACCESS_DENIED;
+}
+
+/*
+ rename a set of files
+*/
+static NTSTATUS ipc_rename(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_rename *ren)
+{
+ return NT_STATUS_ACCESS_DENIED;
+}
+
+/*
+ copy a set of files
+*/
+static NTSTATUS ipc_copy(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, struct smb_copy *cp)
+{
+ return NT_STATUS_ACCESS_DENIED;
+}
+
+static NTSTATUS ipc_readx_dcesrv_output(void *private_data, DATA_BLOB *out, size_t *nwritten)
+{
+ DATA_BLOB *blob = private_data;
+
+ if (out->length < blob->length) {
+ blob->length = out->length;
+ }
+ memcpy(blob->data, out->data, blob->length);
+ *nwritten = blob->length;
+ return NT_STATUS_OK;
+}
+
+/*
+ read from a file
+*/
+static NTSTATUS ipc_read(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_read *rd)
+{
+ struct ipc_private *ipriv = ntvfs->private_data;
+ DATA_BLOB data;
+ struct pipe_state *p;
+ NTSTATUS status = NT_STATUS_OK;
+
+ if (rd->generic.level != RAW_READ_GENERIC) {
+ return ntvfs_map_read(ntvfs, req, rd);
+ }
+
+ p = pipe_state_find(ipriv, rd->readx.in.file.ntvfs);
+ if (!p) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ data.length = rd->readx.in.maxcnt;
+ data.data = rd->readx.out.data;
+ if (data.length > UINT16_MAX) {
+ data.length = UINT16_MAX;
+ }
+
+ if (data.length != 0) {
+ status = dcesrv_output(p->dce_conn, &data, ipc_readx_dcesrv_output);
+ if (NT_STATUS_IS_ERR(status)) {
+ return status;
+ }
+ }
+
+ rd->readx.out.remaining = 0;
+ rd->readx.out.compaction_mode = 0;
+ rd->readx.out.nread = data.length;
+
+ return status;
+}
+
+/*
+ write to a file
+*/
+static NTSTATUS ipc_write(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_write *wr)
+{
+ struct ipc_private *ipriv = ntvfs->private_data;
+ DATA_BLOB data;
+ struct pipe_state *p;
+ NTSTATUS status;
+
+ if (wr->generic.level != RAW_WRITE_GENERIC) {
+ return ntvfs_map_write(ntvfs, req, wr);
+ }
+
+ data.data = discard_const_p(void, wr->writex.in.data);
+ data.length = wr->writex.in.count;
+
+ p = pipe_state_find(ipriv, wr->writex.in.file.ntvfs);
+ if (!p) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ status = dcesrv_input(p->dce_conn, &data);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ wr->writex.out.nwritten = data.length;
+ wr->writex.out.remaining = 0;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ seek in a file
+*/
+static NTSTATUS ipc_seek(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_seek *io)
+{
+ return NT_STATUS_ACCESS_DENIED;
+}
+
+/*
+ flush a file
+*/
+static NTSTATUS ipc_flush(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_flush *io)
+{
+ return NT_STATUS_ACCESS_DENIED;
+}
+
+/*
+ close a file
+*/
+static NTSTATUS ipc_close(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_close *io)
+{
+ struct ipc_private *ipriv = ntvfs->private_data;
+ struct pipe_state *p;
+
+ if (io->generic.level != RAW_CLOSE_CLOSE) {
+ return ntvfs_map_close(ntvfs, req, io);
+ }
+
+ p = pipe_state_find(ipriv, io->close.in.file.ntvfs);
+ if (!p) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ talloc_free(p);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ exit - closing files
+*/
+static NTSTATUS ipc_exit(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req)
+{
+ struct ipc_private *ipriv = ntvfs->private_data;
+ struct pipe_state *p, *next;
+
+ for (p=ipriv->pipe_list; p; p=next) {
+ next = p->next;
+ if (p->handle->session_info == req->session_info &&
+ p->handle->smbpid == req->smbpid) {
+ talloc_free(p);
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ logoff - closing files open by the user
+*/
+static NTSTATUS ipc_logoff(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req)
+{
+ struct ipc_private *ipriv = ntvfs->private_data;
+ struct pipe_state *p, *next;
+
+ for (p=ipriv->pipe_list; p; p=next) {
+ next = p->next;
+ if (p->handle->session_info == req->session_info) {
+ talloc_free(p);
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ setup for an async call
+*/
+static NTSTATUS ipc_async_setup(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ void *private_data)
+{
+ return NT_STATUS_OK;
+}
+
+/*
+ cancel an async call
+*/
+static NTSTATUS ipc_cancel(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req)
+{
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+/*
+ lock a byte range
+*/
+static NTSTATUS ipc_lock(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_lock *lck)
+{
+ return NT_STATUS_ACCESS_DENIED;
+}
+
+/*
+ set info on a open file
+*/
+static NTSTATUS ipc_setfileinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_setfileinfo *info)
+{
+ return NT_STATUS_ACCESS_DENIED;
+}
+
+/*
+ query info on a open file
+*/
+static NTSTATUS ipc_qfileinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_fileinfo *info)
+{
+ struct ipc_private *ipriv = ntvfs->private_data;
+ struct pipe_state *p = pipe_state_find(ipriv, info->generic.in.file.ntvfs);
+ if (!p) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+ switch (info->generic.level) {
+ case RAW_FILEINFO_GENERIC:
+ {
+ ZERO_STRUCT(info->generic.out);
+ info->generic.out.attrib = FILE_ATTRIBUTE_NORMAL;
+ info->generic.out.fname.s = strrchr(p->pipe_name, '\\');
+ info->generic.out.alloc_size = 4096;
+ info->generic.out.nlink = 1;
+ /* What the heck? Match Win2k3: IPC$ pipes are delete pending */
+ info->generic.out.delete_pending = 1;
+ return NT_STATUS_OK;
+ }
+ case RAW_FILEINFO_ALT_NAME_INFO:
+ case RAW_FILEINFO_ALT_NAME_INFORMATION:
+ case RAW_FILEINFO_STREAM_INFO:
+ case RAW_FILEINFO_STREAM_INFORMATION:
+ case RAW_FILEINFO_COMPRESSION_INFO:
+ case RAW_FILEINFO_COMPRESSION_INFORMATION:
+ case RAW_FILEINFO_NETWORK_OPEN_INFORMATION:
+ case RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION:
+ return NT_STATUS_INVALID_PARAMETER;
+ case RAW_FILEINFO_ALL_EAS:
+ return NT_STATUS_ACCESS_DENIED;
+ default:
+ return ntvfs_map_qfileinfo(ntvfs, req, info);
+ }
+
+ return NT_STATUS_ACCESS_DENIED;
+}
+
+
+/*
+ return filesystem info
+*/
+static NTSTATUS ipc_fsinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_fsinfo *fs)
+{
+ return NT_STATUS_ACCESS_DENIED;
+}
+
+/*
+ return print queue info
+*/
+static NTSTATUS ipc_lpq(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_lpq *lpq)
+{
+ return NT_STATUS_ACCESS_DENIED;
+}
+
+/*
+ list files in a directory matching a wildcard pattern
+*/
+static NTSTATUS ipc_search_first(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_search_first *io,
+ void *search_private,
+ bool (*callback)(void *, const union smb_search_data *))
+{
+ return NT_STATUS_ACCESS_DENIED;
+}
+
+/*
+ continue listing files in a directory
+*/
+static NTSTATUS ipc_search_next(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_search_next *io,
+ void *search_private,
+ bool (*callback)(void *, const union smb_search_data *))
+{
+ return NT_STATUS_ACCESS_DENIED;
+}
+
+/*
+ end listing files in a directory
+*/
+static NTSTATUS ipc_search_close(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_search_close *io)
+{
+ return NT_STATUS_ACCESS_DENIED;
+}
+
+static NTSTATUS ipc_trans_dcesrv_output(void *private_data, DATA_BLOB *out, size_t *nwritten)
+{
+ NTSTATUS status = NT_STATUS_OK;
+ DATA_BLOB *blob = private_data;
+
+ if (out->length > blob->length) {
+ status = STATUS_BUFFER_OVERFLOW;
+ }
+
+ if (out->length < blob->length) {
+ blob->length = out->length;
+ }
+ memcpy(blob->data, out->data, blob->length);
+ *nwritten = blob->length;
+ return status;
+}
+
+/* SMBtrans - handle a DCERPC command */
+static NTSTATUS ipc_dcerpc_cmd(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, struct smb_trans2 *trans)
+{
+ struct pipe_state *p;
+ struct ipc_private *ipriv = ntvfs->private_data;
+ NTSTATUS status;
+ DATA_BLOB fnum_key;
+ uint16_t fnum;
+
+ /*
+ * the fnum is in setup[1], a 16 bit value
+ * the setup[*] values are already in host byteorder
+ * but ntvfs_handle_search_by_wire_key() expects
+ * network byteorder
+ */
+ SSVAL(&fnum, 0, trans->in.setup[1]);
+ fnum_key = data_blob_const(&fnum, 2);
+
+ p = pipe_state_find_key(ipriv, req, &fnum_key);
+ if (!p) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ trans->out.data = data_blob_talloc(req, NULL, trans->in.max_data);
+ if (!trans->out.data.data) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* pass the data to the dcerpc server. Note that we don't
+ expect this to fail, and things like NDR faults are not
+ reported at this stage. Those sorts of errors happen in the
+ dcesrv_output stage */
+ status = dcesrv_input(p->dce_conn, &trans->in.data);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /*
+ now ask the dcerpc system for some output. This doesn't yet handle
+ async calls. Again, we only expect NT_STATUS_OK. If the call fails then
+ the error is encoded at the dcerpc level
+ */
+ status = dcesrv_output(p->dce_conn, &trans->out.data, ipc_trans_dcesrv_output);
+ if (NT_STATUS_IS_ERR(status)) {
+ return status;
+ }
+
+ trans->out.setup_count = 0;
+ trans->out.setup = NULL;
+ trans->out.params = data_blob(NULL, 0);
+
+ return status;
+}
+
+
+/* SMBtrans - set named pipe state */
+static NTSTATUS ipc_set_nm_pipe_state(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, struct smb_trans2 *trans)
+{
+ struct ipc_private *ipriv = ntvfs->private_data;
+ struct pipe_state *p;
+ DATA_BLOB fnum_key;
+
+ /* the fnum is in setup[1] */
+ fnum_key = data_blob_const(&trans->in.setup[1], sizeof(trans->in.setup[1]));
+
+ p = pipe_state_find_key(ipriv, req, &fnum_key);
+ if (!p) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ if (trans->in.params.length != 2) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ p->ipc_state = SVAL(trans->in.params.data, 0);
+
+ trans->out.setup_count = 0;
+ trans->out.setup = NULL;
+ trans->out.params = data_blob(NULL, 0);
+ trans->out.data = data_blob(NULL, 0);
+
+ return NT_STATUS_OK;
+}
+
+
+/* SMBtrans - used to provide access to SMB pipes */
+static NTSTATUS ipc_trans(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, struct smb_trans2 *trans)
+{
+ NTSTATUS status;
+
+ if (strequal(trans->in.trans_name, "\\PIPE\\LANMAN"))
+ return ipc_rap_call(req, ntvfs->ctx->event_ctx, ntvfs->ctx->lp_ctx, trans);
+
+ if (trans->in.setup_count != 2) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ switch (trans->in.setup[0]) {
+ case TRANSACT_SETNAMEDPIPEHANDLESTATE:
+ status = ipc_set_nm_pipe_state(ntvfs, req, trans);
+ break;
+ case TRANSACT_DCERPCCMD:
+ status = ipc_dcerpc_cmd(ntvfs, req, trans);
+ break;
+ default:
+ status = NT_STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ return status;
+}
+
+static NTSTATUS ipc_ioctl_smb2(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_ioctl *io)
+{
+ struct pipe_state *p;
+ struct ipc_private *ipriv = ntvfs->private_data;
+ NTSTATUS status;
+
+ switch (io->smb2.in.function) {
+ case FSCTL_NAMED_PIPE_READ_WRITE:
+ break;
+
+ default:
+ return NT_STATUS_FS_DRIVER_REQUIRED;
+ }
+
+ p = pipe_state_find(ipriv, io->smb2.in.file.ntvfs);
+ if (!p) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ io->smb2.out.out = data_blob_talloc(req, NULL, io->smb2.in.max_response_size);
+ NT_STATUS_HAVE_NO_MEMORY(io->smb2.out.out.data);
+
+ /* pass the data to the dcerpc server. Note that we don't
+ expect this to fail, and things like NDR faults are not
+ reported at this stage. Those sorts of errors happen in the
+ dcesrv_output stage */
+ status = dcesrv_input(p->dce_conn, &io->smb2.in.out);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ /*
+ now ask the dcerpc system for some output. This doesn't yet handle
+ async calls. Again, we only expect NT_STATUS_OK. If the call fails then
+ the error is encoded at the dcerpc level
+ */
+ status = dcesrv_output(p->dce_conn, &io->smb2.out.out, ipc_trans_dcesrv_output);
+ NT_STATUS_IS_ERR_RETURN(status);
+
+ io->smb2.out._pad = 0;
+ io->smb2.out.function = io->smb2.in.function;
+ io->smb2.out.unknown2 = 0;
+ io->smb2.out.unknown3 = 0;
+ io->smb2.out.in = io->smb2.in.out;
+
+ return status;
+}
+
+/*
+ ioctl interface
+*/
+static NTSTATUS ipc_ioctl(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_ioctl *io)
+{
+ switch (io->generic.level) {
+ case RAW_IOCTL_SMB2:
+ return ipc_ioctl_smb2(ntvfs, req, io);
+
+ case RAW_IOCTL_SMB2_NO_HANDLE:
+ return NT_STATUS_FS_DRIVER_REQUIRED;
+
+ default:
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ return NT_STATUS_ACCESS_DENIED;
+}
+
+
+/*
+ initialialise the IPC backend, registering ourselves with the ntvfs subsystem
+ */
+NTSTATUS ntvfs_ipc_init(void)
+{
+ NTSTATUS ret;
+ struct ntvfs_ops ops;
+ NTVFS_CURRENT_CRITICAL_SIZES(vers);
+
+ ZERO_STRUCT(ops);
+
+ /* fill in the name and type */
+ ops.name = "default";
+ ops.type = NTVFS_IPC;
+
+ /* fill in all the operations */
+ ops.connect = ipc_connect;
+ ops.disconnect = ipc_disconnect;
+ ops.unlink = ipc_unlink;
+ ops.chkpath = ipc_chkpath;
+ ops.qpathinfo = ipc_qpathinfo;
+ ops.setpathinfo = ipc_setpathinfo;
+ ops.open = ipc_open;
+ ops.mkdir = ipc_mkdir;
+ ops.rmdir = ipc_rmdir;
+ ops.rename = ipc_rename;
+ ops.copy = ipc_copy;
+ ops.ioctl = ipc_ioctl;
+ ops.read = ipc_read;
+ ops.write = ipc_write;
+ ops.seek = ipc_seek;
+ ops.flush = ipc_flush;
+ ops.close = ipc_close;
+ ops.exit = ipc_exit;
+ ops.lock = ipc_lock;
+ ops.setfileinfo = ipc_setfileinfo;
+ ops.qfileinfo = ipc_qfileinfo;
+ ops.fsinfo = ipc_fsinfo;
+ ops.lpq = ipc_lpq;
+ ops.search_first = ipc_search_first;
+ ops.search_next = ipc_search_next;
+ ops.search_close = ipc_search_close;
+ ops.trans = ipc_trans;
+ ops.logoff = ipc_logoff;
+ ops.async_setup = ipc_async_setup;
+ ops.cancel = ipc_cancel;
+
+ /* register ourselves with the NTVFS subsystem. */
+ ret = ntvfs_register(&ops, &vers);
+
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("Failed to register IPC backend!\n"));
+ return ret;
+ }
+
+ return ret;
+}
diff --git a/source4/ntvfs/nbench/README b/source4/ntvfs/nbench/README
new file mode 100644
index 0000000000..e1601e4440
--- /dev/null
+++ b/source4/ntvfs/nbench/README
@@ -0,0 +1,13 @@
+This module provides a way to capture file sharing loads for the
+NBENCH benchmark client. It also servers as an example of a NTVFS
+filter module.
+
+Here is an example config that passes through to the CIFS NTVFS backend.
+
+[bench]
+ ntvfs handler = nbench cifs
+ cifs:server = myserver
+ cifs:user = myuser
+ cifs:password = mypass
+ cifs:domain = MYDOMAIN
+ cifs:share = bench
diff --git a/source4/ntvfs/nbench/vfs_nbench.c b/source4/ntvfs/nbench/vfs_nbench.c
new file mode 100644
index 0000000000..7ba2e7c649
--- /dev/null
+++ b/source4/ntvfs/nbench/vfs_nbench.c
@@ -0,0 +1,972 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ a pass-thru NTVFS module to record a NBENCH load file
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ "passthru" in this module refers to the next level of NTVFS being used
+*/
+
+#include "includes.h"
+#include "ntvfs/ntvfs.h"
+#include "system/filesys.h"
+
+/* this is stored in ntvfs_private */
+struct nbench_private {
+ int log_fd;
+};
+
+/*
+ log one request to the nbench log
+*/
+static void nbench_log(struct ntvfs_request *req,
+ const char *format, ...) PRINTF_ATTRIBUTE(2, 3);
+
+static void nbench_log(struct ntvfs_request *req,
+ const char *format, ...)
+{
+ struct nbench_private *nprivates = req->async_states->ntvfs->private_data;
+ va_list ap;
+ char *s = NULL;
+
+ va_start(ap, format);
+ vasprintf(&s, format, ap);
+ va_end(ap);
+
+ write(nprivates->log_fd, s, strlen(s));
+ free(s);
+}
+
+static char *nbench_ntvfs_handle_string(struct ntvfs_request *req, struct ntvfs_handle *h)
+{
+ DATA_BLOB key;
+ uint16_t fnum = 0;
+
+ key = ntvfs_handle_get_wire_key(h, req);
+
+ switch (key.length) {
+ case 2: /* SMB fnum */
+ fnum = SVAL(key.data, 0);
+ break;
+ default:
+ DEBUG(0,("%s: invalid wire handle size: %u\n",
+ __FUNCTION__, (unsigned)key.length));
+ break;
+ }
+
+ return talloc_asprintf(req, "%u", fnum);
+}
+
+/*
+ this pass through macro operates on request contexts, and disables
+ async calls.
+
+ async calls are a pain for the nbench module as it makes pulling the
+ status code and any result parameters much harder.
+*/
+#define PASS_THRU_REQ_PRE_ASYNC(ntvfs, req, op, par1) do { \
+ status = ntvfs_async_state_push(ntvfs, req, par1, nbench_##op##_send); \
+ if (!NT_STATUS_IS_OK(status)) { \
+ return status; \
+ } \
+} while (0)
+
+#define PASS_THRU_REQ_POST_ASYNC(req) do { \
+ req->async_states->status = status; \
+ if (!(req->async_states->state & NTVFS_ASYNC_STATE_ASYNC)) { \
+ req->async_states->send_fn(req); \
+ } \
+} while (0)
+
+#define PASS_THRU_REQ(ntvfs, req, op, par1, args) do { \
+ PASS_THRU_REQ_PRE_ASYNC(ntvfs, req, op, par1); \
+ status = ntvfs_next_##op args; \
+ PASS_THRU_REQ_POST_ASYNC(req); \
+} while (0)
+
+#define PASS_THRU_REP_POST(req) do { \
+ ntvfs_async_state_pop(req); \
+ if (req->async_states->state & NTVFS_ASYNC_STATE_ASYNC) { \
+ req->async_states->send_fn(req); \
+ } \
+} while (0)
+
+/*
+ connect to a share - used when a tree_connect operation comes in.
+*/
+static NTSTATUS nbench_connect(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, const char *sharename)
+{
+ struct nbench_private *nprivates;
+ NTSTATUS status;
+ char *logname = NULL;
+
+ nprivates = talloc(ntvfs, struct nbench_private);
+ if (!nprivates) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ asprintf(&logname, "/tmp/nbenchlog%d.%u", ntvfs->depth, getpid());
+ nprivates->log_fd = open(logname, O_WRONLY|O_CREAT|O_APPEND, 0644);
+ free(logname);
+
+ if (nprivates->log_fd == -1) {
+ DEBUG(0,("Failed to open nbench log\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ ntvfs->private_data = nprivates;
+
+ status = ntvfs_next_connect(ntvfs, req, sharename);
+
+ return status;
+}
+
+/*
+ disconnect from a share
+*/
+static NTSTATUS nbench_disconnect(struct ntvfs_module_context *ntvfs)
+{
+ struct nbench_private *nprivates = ntvfs->private_data;
+ NTSTATUS status;
+
+ close(nprivates->log_fd);
+
+ status = ntvfs_next_disconnect(ntvfs);
+
+ return status;
+}
+
+/*
+ delete a file - the dirtype specifies the file types to include in the search.
+ The name can contain CIFS wildcards, but rarely does (except with OS/2 clients)
+*/
+static void nbench_unlink_send(struct ntvfs_request *req)
+{
+ union smb_unlink *unl = req->async_states->private_data;
+
+ nbench_log(req, "Unlink \"%s\" 0x%x %s\n",
+ unl->unlink.in.pattern, unl->unlink.in.attrib,
+ get_nt_error_c_code(req->async_states->status));
+
+ PASS_THRU_REP_POST(req);
+}
+
+static NTSTATUS nbench_unlink(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_unlink *unl)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, unlink, unl, (ntvfs, req, unl));
+
+ return status;
+}
+
+/*
+ ioctl interface
+*/
+static void nbench_ioctl_send(struct ntvfs_request *req)
+{
+ nbench_log(req, "Ioctl - NOT HANDLED\n");
+
+ PASS_THRU_REP_POST(req);
+}
+
+static NTSTATUS nbench_ioctl(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_ioctl *io)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, ioctl, io, (ntvfs, req, io));
+
+ return status;
+}
+
+/*
+ check if a directory exists
+*/
+static void nbench_chkpath_send(struct ntvfs_request *req)
+{
+ union smb_chkpath *cp = req->async_states->private_data;
+
+ nbench_log(req, "Chkpath \"%s\" %s\n",
+ cp->chkpath.in.path,
+ get_nt_error_c_code(req->async_states->status));
+
+ PASS_THRU_REP_POST(req);
+}
+
+static NTSTATUS nbench_chkpath(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_chkpath *cp)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, chkpath, cp, (ntvfs, req, cp));
+
+ return status;
+}
+
+/*
+ return info on a pathname
+*/
+static void nbench_qpathinfo_send(struct ntvfs_request *req)
+{
+ union smb_fileinfo *info = req->async_states->private_data;
+
+ nbench_log(req, "QUERY_PATH_INFORMATION \"%s\" %d %s\n",
+ info->generic.in.file.path,
+ info->generic.level,
+ get_nt_error_c_code(req->async_states->status));
+
+ PASS_THRU_REP_POST(req);
+}
+
+static NTSTATUS nbench_qpathinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_fileinfo *info)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, qpathinfo, info, (ntvfs, req, info));
+
+ return status;
+}
+
+/*
+ query info on a open file
+*/
+static void nbench_qfileinfo_send(struct ntvfs_request *req)
+{
+ union smb_fileinfo *info = req->async_states->private_data;
+
+ nbench_log(req, "QUERY_FILE_INFORMATION %s %d %s\n",
+ nbench_ntvfs_handle_string(req, info->generic.in.file.ntvfs),
+ info->generic.level,
+ get_nt_error_c_code(req->async_states->status));
+
+ PASS_THRU_REP_POST(req);
+}
+
+static NTSTATUS nbench_qfileinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_fileinfo *info)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, qfileinfo, info, (ntvfs, req, info));
+
+ return status;
+}
+
+/*
+ set info on a pathname
+*/
+static void nbench_setpathinfo_send(struct ntvfs_request *req)
+{
+ union smb_setfileinfo *st = req->async_states->private_data;
+
+ nbench_log(req, "SET_PATH_INFORMATION \"%s\" %d %s\n",
+ st->generic.in.file.path,
+ st->generic.level,
+ get_nt_error_c_code(req->async_states->status));
+
+ PASS_THRU_REP_POST(req);
+}
+
+static NTSTATUS nbench_setpathinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_setfileinfo *st)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, setpathinfo, st, (ntvfs, req, st));
+
+ return status;
+}
+
+/*
+ open a file
+*/
+static void nbench_open_send(struct ntvfs_request *req)
+{
+ union smb_open *io = req->async_states->private_data;
+
+ switch (io->generic.level) {
+ case RAW_OPEN_NTCREATEX:
+ if (!NT_STATUS_IS_OK(req->async_states->status)) {
+ ZERO_STRUCT(io->ntcreatex.out);
+ }
+ nbench_log(req, "NTCreateX \"%s\" 0x%x 0x%x %s %s\n",
+ io->ntcreatex.in.fname,
+ io->ntcreatex.in.create_options,
+ io->ntcreatex.in.open_disposition,
+ nbench_ntvfs_handle_string(req, io->ntcreatex.out.file.ntvfs),
+ get_nt_error_c_code(req->async_states->status));
+ break;
+
+ default:
+ nbench_log(req, "Open-%d - NOT HANDLED\n",
+ io->generic.level);
+ break;
+ }
+
+ PASS_THRU_REP_POST(req);
+}
+
+static NTSTATUS nbench_open(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_open *io)
+{
+ NTSTATUS status;
+
+#undef open /* AIX defines open to be open64 */
+ PASS_THRU_REQ(ntvfs, req, open, io, (ntvfs, req, io));
+
+ return status;
+}
+
+/*
+ create a directory
+*/
+static void nbench_mkdir_send(struct ntvfs_request *req)
+{
+ nbench_log(req, "Mkdir - NOT HANDLED\n");
+
+ PASS_THRU_REP_POST(req);
+}
+
+static NTSTATUS nbench_mkdir(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_mkdir *md)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, mkdir, md, (ntvfs, req, md));
+
+ return status;
+}
+
+/*
+ remove a directory
+*/
+static void nbench_rmdir_send(struct ntvfs_request *req)
+{
+ struct smb_rmdir *rd = req->async_states->private_data;
+
+ nbench_log(req, "Rmdir \"%s\" %s\n",
+ rd->in.path,
+ get_nt_error_c_code(req->async_states->status));
+
+ PASS_THRU_REP_POST(req);
+}
+
+static NTSTATUS nbench_rmdir(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, struct smb_rmdir *rd)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, rmdir, rd, (ntvfs, req, rd));
+
+ return status;
+}
+
+/*
+ rename a set of files
+*/
+static void nbench_rename_send(struct ntvfs_request *req)
+{
+ union smb_rename *ren = req->async_states->private_data;
+
+ switch (ren->generic.level) {
+ case RAW_RENAME_RENAME:
+ nbench_log(req, "Rename \"%s\" \"%s\" %s\n",
+ ren->rename.in.pattern1,
+ ren->rename.in.pattern2,
+ get_nt_error_c_code(req->async_states->status));
+ break;
+
+ default:
+ nbench_log(req, "Rename-%d - NOT HANDLED\n",
+ ren->generic.level);
+ break;
+ }
+
+ PASS_THRU_REP_POST(req);
+}
+
+static NTSTATUS nbench_rename(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_rename *ren)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, rename, ren, (ntvfs, req, ren));
+
+ return status;
+}
+
+/*
+ copy a set of files
+*/
+static void nbench_copy_send(struct ntvfs_request *req)
+{
+ nbench_log(req, "Copy - NOT HANDLED\n");
+
+ PASS_THRU_REP_POST(req);
+}
+
+static NTSTATUS nbench_copy(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, struct smb_copy *cp)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, copy, cp, (ntvfs, req, cp));
+
+ return status;
+}
+
+/*
+ read from a file
+*/
+static void nbench_read_send(struct ntvfs_request *req)
+{
+ union smb_read *rd = req->async_states->private_data;
+
+ switch (rd->generic.level) {
+ case RAW_READ_READX:
+ if (!NT_STATUS_IS_OK(req->async_states->status)) {
+ ZERO_STRUCT(rd->readx.out);
+ }
+ nbench_log(req, "ReadX %s %d %d %d %s\n",
+ nbench_ntvfs_handle_string(req, rd->readx.in.file.ntvfs),
+ (int)rd->readx.in.offset,
+ rd->readx.in.maxcnt,
+ rd->readx.out.nread,
+ get_nt_error_c_code(req->async_states->status));
+ break;
+ default:
+ nbench_log(req, "Read-%d - NOT HANDLED\n",
+ rd->generic.level);
+ break;
+ }
+
+ PASS_THRU_REP_POST(req);
+}
+
+static NTSTATUS nbench_read(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_read *rd)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, read, rd, (ntvfs, req, rd));
+
+ return status;
+}
+
+/*
+ write to a file
+*/
+static void nbench_write_send(struct ntvfs_request *req)
+{
+ union smb_write *wr = req->async_states->private_data;
+
+ switch (wr->generic.level) {
+ case RAW_WRITE_WRITEX:
+ if (!NT_STATUS_IS_OK(req->async_states->status)) {
+ ZERO_STRUCT(wr->writex.out);
+ }
+ nbench_log(req, "WriteX %s %d %d %d %s\n",
+ nbench_ntvfs_handle_string(req, wr->writex.in.file.ntvfs),
+ (int)wr->writex.in.offset,
+ wr->writex.in.count,
+ wr->writex.out.nwritten,
+ get_nt_error_c_code(req->async_states->status));
+ break;
+
+ case RAW_WRITE_WRITE:
+ if (!NT_STATUS_IS_OK(req->async_states->status)) {
+ ZERO_STRUCT(wr->write.out);
+ }
+ nbench_log(req, "Write %s %d %d %d %s\n",
+ nbench_ntvfs_handle_string(req, wr->write.in.file.ntvfs),
+ wr->write.in.offset,
+ wr->write.in.count,
+ wr->write.out.nwritten,
+ get_nt_error_c_code(req->async_states->status));
+ break;
+
+ default:
+ nbench_log(req, "Write-%d - NOT HANDLED\n",
+ wr->generic.level);
+ break;
+ }
+
+ PASS_THRU_REP_POST(req);
+}
+
+static NTSTATUS nbench_write(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_write *wr)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, write, wr, (ntvfs, req, wr));
+
+ return status;
+}
+
+/*
+ seek in a file
+*/
+static void nbench_seek_send(struct ntvfs_request *req)
+{
+ nbench_log(req, "Seek - NOT HANDLED\n");
+
+ PASS_THRU_REP_POST(req);
+}
+
+static NTSTATUS nbench_seek(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_seek *io)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, seek, io, (ntvfs, req, io));
+
+ return status;
+}
+
+/*
+ flush a file
+*/
+static void nbench_flush_send(struct ntvfs_request *req)
+{
+ union smb_flush *io = req->async_states->private_data;
+
+ switch (io->generic.level) {
+ case RAW_FLUSH_FLUSH:
+ nbench_log(req, "Flush %s %s\n",
+ nbench_ntvfs_handle_string(req, io->flush.in.file.ntvfs),
+ get_nt_error_c_code(req->async_states->status));
+ break;
+ case RAW_FLUSH_ALL:
+ nbench_log(req, "Flush %d %s\n",
+ 0xFFFF,
+ get_nt_error_c_code(req->async_states->status));
+ break;
+ default:
+ nbench_log(req, "Flush-%d - NOT HANDLED\n",
+ io->generic.level);
+ break;
+ }
+
+ PASS_THRU_REP_POST(req);
+}
+
+static NTSTATUS nbench_flush(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_flush *io)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, flush, io, (ntvfs, req, io));
+
+ return status;
+}
+
+/*
+ close a file
+*/
+static void nbench_close_send(struct ntvfs_request *req)
+{
+ union smb_close *io = req->async_states->private_data;
+
+ switch (io->generic.level) {
+ case RAW_CLOSE_CLOSE:
+ nbench_log(req, "Close %s %s\n",
+ nbench_ntvfs_handle_string(req, io->close.in.file.ntvfs),
+ get_nt_error_c_code(req->async_states->status));
+ break;
+
+ default:
+ nbench_log(req, "Close-%d - NOT HANDLED\n",
+ io->generic.level);
+ break;
+ }
+
+ PASS_THRU_REP_POST(req);
+}
+
+static NTSTATUS nbench_close(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_close *io)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, close, io, (ntvfs, req, io));
+
+ return status;
+}
+
+/*
+ exit - closing files
+*/
+static void nbench_exit_send(struct ntvfs_request *req)
+{
+ nbench_log(req, "Exit - NOT HANDLED\n");
+
+ PASS_THRU_REP_POST(req);
+}
+
+static NTSTATUS nbench_exit(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, exit, NULL, (ntvfs, req));
+
+ return status;
+}
+
+/*
+ logoff - closing files
+*/
+static void nbench_logoff_send(struct ntvfs_request *req)
+{
+ nbench_log(req, "Logoff - NOT HANDLED\n");
+
+ PASS_THRU_REP_POST(req);
+}
+
+static NTSTATUS nbench_logoff(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, logoff, NULL, (ntvfs, req));
+
+ return status;
+}
+
+/*
+ async_setup - send fn
+*/
+static void nbench_async_setup_send(struct ntvfs_request *req)
+{
+ PASS_THRU_REP_POST(req);
+}
+
+/*
+ async setup
+*/
+static NTSTATUS nbench_async_setup(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ void *private_data)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, async_setup, NULL, (ntvfs, req, private_data));
+
+ return status;
+}
+
+
+static void nbench_cancel_send(struct ntvfs_request *req)
+{
+ PASS_THRU_REP_POST(req);
+}
+
+/*
+ cancel an existing async request
+*/
+static NTSTATUS nbench_cancel(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, cancel, NULL, (ntvfs, req));
+
+ return status;
+}
+
+/*
+ lock a byte range
+*/
+static void nbench_lock_send(struct ntvfs_request *req)
+{
+ union smb_lock *lck = req->async_states->private_data;
+
+ if (lck->generic.level == RAW_LOCK_LOCKX &&
+ lck->lockx.in.lock_cnt == 1 &&
+ lck->lockx.in.ulock_cnt == 0) {
+ nbench_log(req, "LockX %s %d %d %s\n",
+ nbench_ntvfs_handle_string(req, lck->lockx.in.file.ntvfs),
+ (int)lck->lockx.in.locks[0].offset,
+ (int)lck->lockx.in.locks[0].count,
+ get_nt_error_c_code(req->async_states->status));
+ } else if (lck->generic.level == RAW_LOCK_LOCKX &&
+ lck->lockx.in.ulock_cnt == 1) {
+ nbench_log(req, "UnlockX %s %d %d %s\n",
+ nbench_ntvfs_handle_string(req, lck->lockx.in.file.ntvfs),
+ (int)lck->lockx.in.locks[0].offset,
+ (int)lck->lockx.in.locks[0].count,
+ get_nt_error_c_code(req->async_states->status));
+ } else {
+ nbench_log(req, "Lock-%d - NOT HANDLED\n", lck->generic.level);
+ }
+
+ PASS_THRU_REP_POST(req);
+}
+
+static NTSTATUS nbench_lock(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_lock *lck)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, lock, lck, (ntvfs, req, lck));
+
+ return status;
+}
+
+/*
+ set info on a open file
+*/
+static void nbench_setfileinfo_send(struct ntvfs_request *req)
+{
+ union smb_setfileinfo *info = req->async_states->private_data;
+
+ nbench_log(req, "SET_FILE_INFORMATION %s %d %s\n",
+ nbench_ntvfs_handle_string(req, info->generic.in.file.ntvfs),
+ info->generic.level,
+ get_nt_error_c_code(req->async_states->status));
+
+ PASS_THRU_REP_POST(req);
+}
+
+static NTSTATUS nbench_setfileinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_setfileinfo *info)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, setfileinfo, info, (ntvfs, req, info));
+
+ return status;
+}
+
+/*
+ return filesystem space info
+*/
+static void nbench_fsinfo_send(struct ntvfs_request *req)
+{
+ union smb_fsinfo *fs = req->async_states->private_data;
+
+ nbench_log(req, "QUERY_FS_INFORMATION %d %s\n",
+ fs->generic.level,
+ get_nt_error_c_code(req->async_states->status));
+
+ PASS_THRU_REP_POST(req);
+}
+
+static NTSTATUS nbench_fsinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_fsinfo *fs)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, fsinfo, fs, (ntvfs, req, fs));
+
+ return status;
+}
+
+/*
+ return print queue info
+*/
+static void nbench_lpq_send(struct ntvfs_request *req)
+{
+ union smb_lpq *lpq = req->async_states->private_data;
+
+ nbench_log(req, "Lpq-%d - NOT HANDLED\n", lpq->generic.level);
+
+ PASS_THRU_REP_POST(req);
+}
+
+static NTSTATUS nbench_lpq(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_lpq *lpq)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, lpq, lpq, (ntvfs, req, lpq));
+
+ return status;
+}
+
+/*
+ list files in a directory matching a wildcard pattern
+*/
+static void nbench_search_first_send(struct ntvfs_request *req)
+{
+ union smb_search_first *io = req->async_states->private_data;
+
+ switch (io->generic.level) {
+ case RAW_SEARCH_TRANS2:
+ if (NT_STATUS_IS_ERR(req->async_states->status)) {
+ ZERO_STRUCT(io->t2ffirst.out);
+ }
+ nbench_log(req, "FIND_FIRST \"%s\" %d %d %d %s\n",
+ io->t2ffirst.in.pattern,
+ io->t2ffirst.data_level,
+ io->t2ffirst.in.max_count,
+ io->t2ffirst.out.count,
+ get_nt_error_c_code(req->async_states->status));
+ break;
+
+ default:
+ nbench_log(req, "Search-%d - NOT HANDLED\n", io->generic.level);
+ break;
+ }
+
+ PASS_THRU_REP_POST(req);
+}
+
+static NTSTATUS nbench_search_first(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_search_first *io,
+ void *search_private,
+ bool (*callback)(void *, const union smb_search_data *))
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, search_first, io, (ntvfs, req, io, search_private, callback));
+
+ return status;
+}
+
+/* continue a search */
+static void nbench_search_next_send(struct ntvfs_request *req)
+{
+ union smb_search_next *io = req->async_states->private_data;
+
+ nbench_log(req, "Searchnext-%d - NOT HANDLED\n", io->generic.level);
+
+ PASS_THRU_REP_POST(req);
+}
+
+static NTSTATUS nbench_search_next(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_search_next *io,
+ void *search_private,
+ bool (*callback)(void *, const union smb_search_data *))
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, search_next, io, (ntvfs, req, io, search_private, callback));
+
+ return status;
+}
+
+/* close a search */
+static void nbench_search_close_send(struct ntvfs_request *req)
+{
+ union smb_search_close *io = req->async_states->private_data;
+
+ nbench_log(req, "Searchclose-%d - NOT HANDLED\n", io->generic.level);
+
+ PASS_THRU_REP_POST(req);
+}
+
+static NTSTATUS nbench_search_close(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_search_close *io)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, search_close, io, (ntvfs, req, io));
+
+ return status;
+}
+
+/* SMBtrans - not used on file shares */
+static void nbench_trans_send(struct ntvfs_request *req)
+{
+ nbench_log(req, "Trans - NOT HANDLED\n");
+
+ PASS_THRU_REP_POST(req);
+}
+
+static NTSTATUS nbench_trans(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, struct smb_trans2 *trans2)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, trans, trans2, (ntvfs, req, trans2));
+
+ return status;
+}
+
+/*
+ initialise the nbench backend, registering ourselves with the ntvfs subsystem
+ */
+NTSTATUS ntvfs_nbench_init(void)
+{
+ NTSTATUS ret;
+ struct ntvfs_ops ops;
+ NTVFS_CURRENT_CRITICAL_SIZES(vers);
+
+ ZERO_STRUCT(ops);
+
+ /* fill in the name and type */
+ ops.name = "nbench";
+ ops.type = NTVFS_DISK;
+
+ /* fill in all the operations */
+ ops.connect = nbench_connect;
+ ops.disconnect = nbench_disconnect;
+ ops.unlink = nbench_unlink;
+ ops.chkpath = nbench_chkpath;
+ ops.qpathinfo = nbench_qpathinfo;
+ ops.setpathinfo = nbench_setpathinfo;
+ ops.open = nbench_open;
+ ops.mkdir = nbench_mkdir;
+ ops.rmdir = nbench_rmdir;
+ ops.rename = nbench_rename;
+ ops.copy = nbench_copy;
+ ops.ioctl = nbench_ioctl;
+ ops.read = nbench_read;
+ ops.write = nbench_write;
+ ops.seek = nbench_seek;
+ ops.flush = nbench_flush;
+ ops.close = nbench_close;
+ ops.exit = nbench_exit;
+ ops.lock = nbench_lock;
+ ops.setfileinfo = nbench_setfileinfo;
+ ops.qfileinfo = nbench_qfileinfo;
+ ops.fsinfo = nbench_fsinfo;
+ ops.lpq = nbench_lpq;
+ ops.search_first = nbench_search_first;
+ ops.search_next = nbench_search_next;
+ ops.search_close = nbench_search_close;
+ ops.trans = nbench_trans;
+ ops.logoff = nbench_logoff;
+ ops.async_setup = nbench_async_setup;
+ ops.cancel = nbench_cancel;
+
+ /* we don't register a trans2 handler as we want to be able to
+ log individual trans2 requests */
+ ops.trans2 = NULL;
+
+ /* register ourselves with the NTVFS subsystem. */
+ ret = ntvfs_register(&ops, &vers);
+
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("Failed to register nbench backend!\n"));
+ }
+
+ return ret;
+}
diff --git a/source4/ntvfs/ntvfs.h b/source4/ntvfs/ntvfs.h
new file mode 100644
index 0000000000..b62595967f
--- /dev/null
+++ b/source4/ntvfs/ntvfs.h
@@ -0,0 +1,339 @@
+/*
+ Unix SMB/CIFS implementation.
+ NTVFS structures and defines
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Stefan Metzmacher 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _NTVFS_H_
+#define _NTVFS_H_
+
+#include "libcli/raw/interfaces.h"
+#include "param/share.h"
+#include "librpc/gen_ndr/security.h"
+#include "librpc/gen_ndr/server_id.h"
+
+/* modules can use the following to determine if the interface has changed */
+/* version 1 -> 0 - make module stacking easier -- metze */
+#define NTVFS_INTERFACE_VERSION 0
+
+struct ntvfs_module_context;
+struct ntvfs_request;
+
+/* each backend has to be one one of the following 3 basic types. In
+ earlier versions of Samba backends needed to handle all types, now
+ we implement them separately.
+ The values 1..3 match the SMB2 SMB2_SHARE_TYPE_* values
+ */
+enum ntvfs_type {NTVFS_DISK=1, NTVFS_IPC=2, NTVFS_PRINT=3};
+
+/* the ntvfs operations structure - contains function pointers to
+ the backend implementations of each operation */
+struct ntvfs_ops {
+ const char *name;
+ enum ntvfs_type type;
+
+ /* initial setup */
+ NTSTATUS (*connect)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ const char *sharename);
+ NTSTATUS (*disconnect)(struct ntvfs_module_context *ntvfs);
+
+ /* async_setup - called when a backend is processing a async request */
+ NTSTATUS (*async_setup)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ void *private_data);
+
+ /* filesystem operations */
+ NTSTATUS (*fsinfo)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_fsinfo *fs);
+
+ /* path operations */
+ NTSTATUS (*unlink)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_unlink *unl);
+ NTSTATUS (*chkpath)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_chkpath *cp);
+ NTSTATUS (*qpathinfo)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_fileinfo *st);
+ NTSTATUS (*setpathinfo)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_setfileinfo *st);
+ NTSTATUS (*mkdir)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_mkdir *md);
+ NTSTATUS (*rmdir)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ struct smb_rmdir *rd);
+ NTSTATUS (*rename)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_rename *ren);
+ NTSTATUS (*copy)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ struct smb_copy *cp);
+ NTSTATUS (*open)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_open *oi);
+
+ /* directory search */
+ NTSTATUS (*search_first)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_search_first *io, void *private_data,
+ bool (*callback)(void *private_data, const union smb_search_data *file));
+ NTSTATUS (*search_next)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_search_next *io, void *private_data,
+ bool (*callback)(void *private_data, const union smb_search_data *file));
+ NTSTATUS (*search_close)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_search_close *io);
+
+ /* operations on open files */
+ NTSTATUS (*ioctl)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_ioctl *io);
+ NTSTATUS (*read)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_read *io);
+ NTSTATUS (*write)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_write *io);
+ NTSTATUS (*seek)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_seek *io);
+ NTSTATUS (*flush)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_flush *flush);
+ NTSTATUS (*lock)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_lock *lck);
+ NTSTATUS (*qfileinfo)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_fileinfo *info);
+ NTSTATUS (*setfileinfo)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_setfileinfo *info);
+ NTSTATUS (*close)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_close *io);
+
+ /* trans interface - used by IPC backend for pipes and RAP calls */
+ NTSTATUS (*trans)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ struct smb_trans2 *trans);
+
+ /* trans2 interface - only used by CIFS backend to prover complete passthru for testing */
+ NTSTATUS (*trans2)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ struct smb_trans2 *trans2);
+
+ /* change notify request */
+ NTSTATUS (*notify)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_notify *info);
+
+ /* cancel - cancels any pending async request */
+ NTSTATUS (*cancel)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req);
+
+ /* printing specific operations */
+ NTSTATUS (*lpq)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_lpq *lpq);
+
+ /* logoff - called when a vuid is closed */
+ NTSTATUS (*logoff)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req);
+ NTSTATUS (*exit)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req);
+};
+
+struct ntvfs_module_context {
+ struct ntvfs_module_context *prev, *next;
+ struct ntvfs_context *ctx;
+ int depth;
+ const struct ntvfs_ops *ops;
+ void *private_data;
+};
+
+struct ntvfs_context {
+ enum ntvfs_type type;
+
+ /* the reported filesystem type */
+ char *fs_type;
+
+ /* the reported device type */
+ char *dev_type;
+
+ enum protocol_types protocol;
+
+ /*
+ * client capabilities
+ * this field doesn't use protocol specific
+ * values!
+ */
+#define NTVFS_CLIENT_CAP_LEVEL_II_OPLOCKS 0x0000000000000001LLU
+ uint64_t client_caps;
+
+ /*
+ * linked list of module contexts
+ */
+ struct ntvfs_module_context *modules;
+
+ struct share_config *config;
+
+ struct server_id server_id;
+ struct loadparm_context *lp_ctx;
+ struct tevent_context *event_ctx;
+ struct messaging_context *msg_ctx;
+
+ struct {
+ void *private_data;
+ NTSTATUS (*handler)(void *private_data, struct ntvfs_handle *handle, uint8_t level);
+ } oplock;
+
+ struct {
+ void *private_data;
+ struct socket_address *(*get_my_addr)(void *private_data, TALLOC_CTX *mem_ctx);
+ struct socket_address *(*get_peer_addr)(void *private_data, TALLOC_CTX *mem_ctx);
+ } client;
+
+ struct {
+ void *private_data;
+ NTSTATUS (*create_new)(void *private_data, struct ntvfs_request *req, struct ntvfs_handle **h);
+ NTSTATUS (*make_valid)(void *private_data, struct ntvfs_handle *h);
+ void (*destroy)(void *private_data, struct ntvfs_handle *h);
+ struct ntvfs_handle *(*search_by_wire_key)(void *private_data, struct ntvfs_request *req, const DATA_BLOB *key);
+ DATA_BLOB (*get_wire_key)(void *private_data, struct ntvfs_handle *handle, TALLOC_CTX *mem_ctx);
+ } handles;
+};
+
+/* a set of flags to control handling of request structures */
+#define NTVFS_ASYNC_STATE_ASYNC (1<<1) /* the backend will answer this one later */
+#define NTVFS_ASYNC_STATE_MAY_ASYNC (1<<2) /* the backend is allowed to answer async */
+#define NTVFS_ASYNC_STATE_CLOSE (1<<3) /* the backend session should be closed */
+
+/* the ntvfs_async_state structure allows backend functions to
+ delay replying to requests. To use this, the front end must
+ set send_fn to a function to be called by the backend
+ when the reply is finally ready to be sent. The backend
+ must set status to the status it wants in the
+ reply. The backend must set the NTVFS_ASYNC_STATE_ASYNC
+ control_flag on the request to indicate that it wishes to
+ delay the reply
+
+ If NTVFS_ASYNC_STATE_MAY_ASYNC is not set then the backend cannot
+ ask for a delayed reply for this request
+
+ note that the private_data pointer is private to the layer which alloced this struct
+*/
+struct ntvfs_async_state {
+ struct ntvfs_async_state *prev, *next;
+ /* the async handling infos */
+ unsigned int state;
+ void *private_data;
+ void (*send_fn)(struct ntvfs_request *);
+ NTSTATUS status;
+
+ /* the passthru module's per session private data */
+ struct ntvfs_module_context *ntvfs;
+};
+
+struct ntvfs_request {
+ /* the ntvfs_context this requests belongs to */
+ struct ntvfs_context *ctx;
+
+ /* ntvfs per request async states */
+ struct ntvfs_async_state *async_states;
+
+ /* the session_info, with security_token and maybe delegated credentials */
+ struct auth_session_info *session_info;
+
+ /* the smb pid is needed for locking contexts */
+ uint32_t smbpid;
+
+ /*
+ * client capabilities
+ * this field doesn't use protocol specific
+ * values!
+ * see NTVFS_CLIENT_CAP_*
+ */
+ uint64_t client_caps;
+
+ /* some statictics for the management tools */
+ struct {
+ /* the system time when the request arrived */
+ struct timeval request_time;
+ } statistics;
+
+ struct {
+ void *private_data;
+ } frontend_data;
+};
+
+struct ntvfs_handle {
+ struct ntvfs_context *ctx;
+
+ struct auth_session_info *session_info;
+
+ uint16_t smbpid;
+
+ struct ntvfs_handle_data {
+ struct ntvfs_handle_data *prev, *next;
+ struct ntvfs_module_context *owner;
+ void *private_data;/* this must be a valid talloc pointer */
+ } *backend_data;
+
+ struct {
+ void *private_data;
+ } frontend_data;
+};
+
+/* this structure is used by backends to determine the size of some critical types */
+struct ntvfs_critical_sizes {
+ int interface_version;
+ int sizeof_ntvfs_critical_sizes;
+ int sizeof_ntvfs_context;
+ int sizeof_ntvfs_module_context;
+ int sizeof_ntvfs_ops;
+ int sizeof_ntvfs_async_state;
+ int sizeof_ntvfs_request;
+ int sizeof_ntvfs_handle;
+ int sizeof_ntvfs_handle_data;
+};
+
+#define NTVFS_CURRENT_CRITICAL_SIZES(c) \
+ struct ntvfs_critical_sizes c = { \
+ .interface_version = NTVFS_INTERFACE_VERSION, \
+ .sizeof_ntvfs_critical_sizes = sizeof(struct ntvfs_critical_sizes), \
+ .sizeof_ntvfs_context = sizeof(struct ntvfs_context), \
+ .sizeof_ntvfs_module_context = sizeof(struct ntvfs_module_context), \
+ .sizeof_ntvfs_ops = sizeof(struct ntvfs_ops), \
+ .sizeof_ntvfs_async_state = sizeof(struct ntvfs_async_state), \
+ .sizeof_ntvfs_request = sizeof(struct ntvfs_request), \
+ .sizeof_ntvfs_handle = sizeof(struct ntvfs_handle), \
+ .sizeof_ntvfs_handle_data = sizeof(struct ntvfs_handle_data), \
+ }
+
+struct messaging_context;
+#include "librpc/gen_ndr/security.h"
+#include "librpc/gen_ndr/notify.h"
+#include "ntvfs/ntvfs_proto.h"
+
+#endif /* _NTVFS_H_ */
diff --git a/source4/ntvfs/ntvfs_base.c b/source4/ntvfs/ntvfs_base.c
new file mode 100644
index 0000000000..b581588d9d
--- /dev/null
+++ b/source4/ntvfs/ntvfs_base.c
@@ -0,0 +1,229 @@
+/*
+ Unix SMB/CIFS implementation.
+ NTVFS base code
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Stefan (metze) Metzmacher 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 <http://www.gnu.org/licenses/>.
+*/
+/*
+ this implements the core code for all NTVFS modules. Backends register themselves here.
+*/
+
+#include "includes.h"
+#include "../lib/util/dlinklist.h"
+#include "ntvfs/ntvfs.h"
+#include "param/param.h"
+
+/* the list of currently registered NTVFS backends, note that there
+ * can be more than one backend with the same name, as long as they
+ * have different typesx */
+static struct ntvfs_backend {
+ const struct ntvfs_ops *ops;
+} *backends = NULL;
+static int num_backends;
+
+/*
+ register a NTVFS backend.
+
+ The 'name' can be later used by other backends to find the operations
+ structure for this backend.
+
+ The 'type' is used to specify whether this is for a disk, printer or IPC$ share
+*/
+NTSTATUS ntvfs_register(const struct ntvfs_ops *ops,
+ const struct ntvfs_critical_sizes *const sizes)
+{
+ struct ntvfs_ops *new_ops;
+
+ if (ntvfs_interface_differs(sizes)) {
+ DEBUG(0, ("NTVFS backend '%s' for type %d "
+ "failed version check\n",
+ ops->name, (int)ops->type));
+ return NT_STATUS_BAD_FUNCTION_TABLE;
+ }
+
+ if (ntvfs_backend_byname(ops->name, ops->type) != NULL) {
+ /* its already registered! */
+ DEBUG(0,("NTVFS backend '%s' for type %d already registered\n",
+ ops->name, (int)ops->type));
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ backends = realloc_p(backends, struct ntvfs_backend, num_backends+1);
+ if (!backends) {
+ smb_panic("out of memory in ntvfs_register");
+ }
+
+ new_ops = smb_xmemdup(ops, sizeof(*ops));
+ new_ops->name = smb_xstrdup(ops->name);
+
+ backends[num_backends].ops = new_ops;
+
+ num_backends++;
+
+ DEBUG(3,("NTVFS backend '%s' for type %d registered\n",
+ ops->name,ops->type));
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ return the operations structure for a named backend of the specified type
+*/
+const struct ntvfs_ops *ntvfs_backend_byname(const char *name, enum ntvfs_type type)
+{
+ int i;
+
+ for (i=0;i<num_backends;i++) {
+ if (backends[i].ops->type == type &&
+ strcmp(backends[i].ops->name, name) == 0) {
+ return backends[i].ops;
+ }
+ }
+
+ return NULL;
+}
+
+
+/*
+ return the NTVFS interface version, and the size of some critical types
+ This can be used by backends to either detect compilation errors, or provide
+ multiple implementations for different smbd compilation options in one module
+*/
+
+static const NTVFS_CURRENT_CRITICAL_SIZES(critical_sizes);
+
+const struct ntvfs_critical_sizes *ntvfs_interface_version(void)
+{
+ return &critical_sizes;
+}
+
+bool ntvfs_interface_differs(const struct ntvfs_critical_sizes *const iface)
+{
+ /* The comparison would be easier with memcmp, but compiler-interset
+ * alignment padding is not guaranteed to be zeroed.
+ */
+
+#define FIELD_DIFFERS(field) (iface->field != critical_sizes.field)
+
+ if (FIELD_DIFFERS(interface_version))
+ return true;
+
+ if (FIELD_DIFFERS(sizeof_ntvfs_critical_sizes))
+ return true;
+
+ if (FIELD_DIFFERS(sizeof_ntvfs_context))
+ return true;
+
+ if (FIELD_DIFFERS(sizeof_ntvfs_module_context))
+ return true;
+
+ if (FIELD_DIFFERS(sizeof_ntvfs_ops))
+ return true;
+
+ if (FIELD_DIFFERS(sizeof_ntvfs_async_state))
+ return true;
+
+ if (FIELD_DIFFERS(sizeof_ntvfs_request))
+ return true;
+
+ /* Versions match. */
+ return false;
+
+#undef FIELD_DIFFERS
+}
+
+/*
+ initialise a connection structure to point at a NTVFS backend
+*/
+NTSTATUS ntvfs_init_connection(TALLOC_CTX *mem_ctx, struct share_config *scfg, enum ntvfs_type type,
+ enum protocol_types protocol,
+ uint64_t ntvfs_client_caps,
+ struct tevent_context *ev, struct messaging_context *msg,
+ struct loadparm_context *lp_ctx,
+ struct server_id server_id, struct ntvfs_context **_ctx)
+{
+ const char **handlers = share_string_list_option(mem_ctx, scfg, SHARE_NTVFS_HANDLER);
+ int i;
+ struct ntvfs_context *ctx;
+
+ if (!handlers) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ ctx = talloc_zero(mem_ctx, struct ntvfs_context);
+ NT_STATUS_HAVE_NO_MEMORY(ctx);
+ ctx->protocol = protocol;
+ ctx->client_caps = ntvfs_client_caps;
+ ctx->type = type;
+ ctx->config = talloc_steal(ctx, scfg);
+ ctx->event_ctx = ev;
+ ctx->msg_ctx = msg;
+ ctx->server_id = server_id;
+ ctx->lp_ctx = lp_ctx;
+
+ for (i=0; handlers[i]; i++) {
+ struct ntvfs_module_context *ntvfs;
+
+ ntvfs = talloc_zero(ctx, struct ntvfs_module_context);
+ NT_STATUS_HAVE_NO_MEMORY(ntvfs);
+ ntvfs->ctx = ctx;
+ ntvfs->ops = ntvfs_backend_byname(handlers[i], ctx->type);
+ if (!ntvfs->ops) {
+ DEBUG(1,("ntvfs_init_connection: failed to find backend=%s, type=%d\n",
+ handlers[i], ctx->type));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ ntvfs->depth = i;
+ DLIST_ADD_END(ctx->modules, ntvfs, struct ntvfs_module_context *);
+ }
+
+ if (!ctx->modules) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ *_ctx = ctx;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS ntvfs_init(struct loadparm_context *lp_ctx)
+{
+ static bool initialized = false;
+ extern NTSTATUS ntvfs_posix_init(void);
+ extern NTSTATUS ntvfs_cifs_init(void);
+ extern NTSTATUS ntvfs_smb2_init(void);
+ extern NTSTATUS ntvfs_nbench_init(void);
+ extern NTSTATUS ntvfs_unixuid_init(void);
+ extern NTSTATUS ntvfs_ipc_init(void);
+ extern NTSTATUS ntvfs_print_init(void);
+ extern NTSTATUS ntvfs_simple_init(void);
+ extern NTSTATUS ntvfs_cifs_posix_init(void);
+ init_module_fn static_init[] = { STATIC_ntvfs_MODULES };
+ init_module_fn *shared_init;
+
+ if (initialized) return NT_STATUS_OK;
+ initialized = true;
+
+ shared_init = load_samba_modules(NULL, lp_ctx, "ntvfs");
+
+ run_init_functions(static_init);
+ run_init_functions(shared_init);
+
+ talloc_free(shared_init);
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/ntvfs/ntvfs_generic.c b/source4/ntvfs/ntvfs_generic.c
new file mode 100644
index 0000000000..ad13f36c8a
--- /dev/null
+++ b/source4/ntvfs/ntvfs_generic.c
@@ -0,0 +1,1553 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ NTVFS generic level mapping code
+
+ Copyright (C) Andrew Tridgell 2003-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 <http://www.gnu.org/licenses/>.
+*/
+/*
+ this implements mappings between info levels for NTVFS backend calls
+
+ the idea is that each of these functions implements one of the NTVFS
+ backend calls in terms of the 'generic' call. All backends that use
+ these functions must supply the generic call, but can if it wants to
+ also implement other levels if the need arises
+
+ this allows backend writers to only implement one variant of each
+ call unless they need fine grained control of the calls.
+*/
+
+#include "includes.h"
+#include "ntvfs/ntvfs.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/* a second stage function converts from the out parameters of the generic
+ call onto the out parameters of the specific call made */
+typedef NTSTATUS (*second_stage_t)(struct ntvfs_module_context *,
+ struct ntvfs_request *,
+ void *, void *, NTSTATUS);
+
+/*
+ this structure holds the async state for pending mapped async calls
+*/
+struct ntvfs_map_async {
+ struct ntvfs_module_context *ntvfs;
+ void *io, *io2;
+ second_stage_t fn;
+};
+
+/*
+ this is a async wrapper, called from the backend when it has completed
+ a function that it has decided to reply to in an async fashion
+*/
+static void ntvfs_map_async_send(struct ntvfs_request *req)
+{
+ struct ntvfs_map_async *m = talloc_get_type(req->async_states->private_data,
+ struct ntvfs_map_async);
+
+ ntvfs_async_state_pop(req);
+
+ /* call the _finish function setup in ntvfs_map_async_setup() */
+ req->async_states->status = m->fn(m->ntvfs, req, m->io, m->io2, req->async_states->status);
+
+ /* call the send function from the next module up */
+ req->async_states->send_fn(req);
+}
+
+/*
+ prepare for calling a ntvfs backend with async support
+ io is the original call structure
+ io2 is the new call structure for the mapped call
+ fn is a second stage function for processing the out arguments
+*/
+static NTSTATUS ntvfs_map_async_setup(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ void *io, void *io2,
+ second_stage_t fn)
+{
+ struct ntvfs_map_async *m;
+ m = talloc(req, struct ntvfs_map_async);
+ if (m == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ m->ntvfs = ntvfs;
+ m->io = io;
+ m->io2 = io2;
+ m->fn = fn;
+ return ntvfs_async_state_push(ntvfs, req, m, ntvfs_map_async_send);
+}
+
+/*
+ called when first stage processing is complete.
+*/
+static NTSTATUS ntvfs_map_async_finish(struct ntvfs_request *req, NTSTATUS status)
+{
+ struct ntvfs_map_async *m;
+
+ /* if the backend has decided to reply in an async fashion then
+ we don't need to do any work here */
+ if (req->async_states->state & NTVFS_ASYNC_STATE_ASYNC) {
+ return status;
+ }
+
+ /* the backend is replying immediately. call the 2nd stage function after popping our local
+ async state */
+ m = talloc_get_type(req->async_states->private_data,
+ struct ntvfs_map_async);
+
+ ntvfs_async_state_pop(req);
+
+ return m->fn(m->ntvfs, req, m->io, m->io2, status);
+}
+
+/*
+ see if a filename ends in EXE COM DLL or SYM. This is needed for the
+ DENY_DOS mapping for OpenX
+*/
+bool is_exe_filename(const char *fname)
+{
+ char *p;
+ p = strrchr(fname, '.');
+ if (!p) {
+ return false;
+ }
+ p++;
+ if (strcasecmp(p, "EXE") == 0 ||
+ strcasecmp(p, "COM") == 0 ||
+ strcasecmp(p, "DLL") == 0 ||
+ strcasecmp(p, "SYM") == 0) {
+ return true;
+ }
+ return false;
+}
+
+
+/*
+ NTVFS openx to ntcreatex mapper
+*/
+static NTSTATUS ntvfs_map_open_finish(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_open *io,
+ union smb_open *io2,
+ NTSTATUS status)
+{
+ time_t write_time = 0;
+ uint32_t set_size = 0;
+ union smb_setfileinfo *sf;
+ uint_t state;
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ switch (io->generic.level) {
+ case RAW_OPEN_OPEN:
+ io->openold.out.file.ntvfs = io2->generic.out.file.ntvfs;
+ io->openold.out.attrib = io2->generic.out.attrib;
+ io->openold.out.write_time = nt_time_to_unix(io2->generic.out.write_time);
+ io->openold.out.size = io2->generic.out.size;
+ io->openold.out.rmode = io->openold.in.open_mode;
+ break;
+
+ case RAW_OPEN_OPENX:
+ io->openx.out.file.ntvfs = io2->generic.out.file.ntvfs;
+ io->openx.out.attrib = io2->generic.out.attrib;
+ io->openx.out.write_time = nt_time_to_unix(io2->generic.out.write_time);
+ io->openx.out.size = io2->generic.out.size;
+ io->openx.out.access = (io->openx.in.open_mode & OPENX_MODE_ACCESS_MASK);
+ io->openx.out.ftype = 0;
+ io->openx.out.devstate = 0;
+ io->openx.out.action = io2->generic.out.create_action;
+ io->openx.out.unique_fid = 0;
+ io->openx.out.access_mask = SEC_STD_ALL;
+ io->openx.out.unknown = 0;
+
+ /* we need to extend the file to the requested size if
+ it was newly created */
+ if (io2->generic.out.create_action == NTCREATEX_ACTION_CREATED) {
+ set_size = io->openx.in.size;
+ }
+ break;
+
+ case RAW_OPEN_T2OPEN:
+ io->t2open.out.file.ntvfs = io2->generic.out.file.ntvfs;
+ io->t2open.out.attrib = io2->generic.out.attrib;
+ io->t2open.out.write_time = nt_time_to_unix(io2->generic.out.write_time);
+ io->t2open.out.size = io2->generic.out.size;
+ io->t2open.out.access = io->t2open.in.open_mode;
+ io->t2open.out.ftype = 0;
+ io->t2open.out.devstate = 0;
+ io->t2open.out.action = io2->generic.out.create_action;
+ io->t2open.out.file_id = 0;
+ break;
+
+ case RAW_OPEN_MKNEW:
+ case RAW_OPEN_CREATE:
+ io->mknew.out.file.ntvfs= io2->generic.out.file.ntvfs;
+ write_time = io->mknew.in.write_time;
+ break;
+
+ case RAW_OPEN_CTEMP:
+ io->ctemp.out.file.ntvfs= io2->generic.out.file.ntvfs;
+ io->ctemp.out.name = talloc_strdup(req, io2->generic.in.fname +
+ strlen(io->ctemp.in.directory) + 1);
+ NT_STATUS_HAVE_NO_MEMORY(io->ctemp.out.name);
+ break;
+
+ case RAW_OPEN_SMB2:
+ ZERO_STRUCT(io->smb2.out);
+ io->smb2.out.file.ntvfs = io2->generic.out.file.ntvfs;
+ switch (io2->generic.out.oplock_level) {
+ case BATCH_OPLOCK_RETURN:
+ io->smb2.out.oplock_level = SMB2_OPLOCK_LEVEL_BATCH;
+ break;
+ case EXCLUSIVE_OPLOCK_RETURN:
+ io->smb2.out.oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE;
+ break;
+ case LEVEL_II_OPLOCK_RETURN:
+ io->smb2.out.oplock_level = SMB2_OPLOCK_LEVEL_II;
+ break;
+ default:
+ io->smb2.out.oplock_level = SMB2_OPLOCK_LEVEL_NONE;
+ break;
+ }
+ io->smb2.out.reserved = 0;
+ io->smb2.out.create_action = io2->generic.out.create_action;
+ io->smb2.out.create_time = io2->generic.out.create_time;
+ io->smb2.out.access_time = io2->generic.out.access_time;
+ io->smb2.out.write_time = io2->generic.out.write_time;
+ io->smb2.out.change_time = io2->generic.out.change_time;
+ io->smb2.out.alloc_size = io2->generic.out.alloc_size;
+ io->smb2.out.size = io2->generic.out.size;
+ io->smb2.out.file_attr = io2->generic.out.attrib;
+ io->smb2.out.reserved2 = 0;
+ io->smb2.out.maximal_access = io2->generic.out.maximal_access;
+ break;
+
+ default:
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ /* doing a secondary request async is more trouble than its
+ worth */
+ state = req->async_states->state;
+ req->async_states->state &= ~NTVFS_ASYNC_STATE_MAY_ASYNC;
+
+ if (write_time != 0) {
+ sf = talloc(req, union smb_setfileinfo);
+ NT_STATUS_HAVE_NO_MEMORY(sf);
+ sf->generic.level = RAW_SFILEINFO_STANDARD;
+ sf->generic.in.file.ntvfs = io2->generic.out.file.ntvfs;
+ sf->standard.in.create_time = 0;
+ sf->standard.in.write_time = write_time;
+ sf->standard.in.access_time = 0;
+ status = ntvfs->ops->setfileinfo(ntvfs, req, sf);
+ }
+
+ if (set_size != 0) {
+ sf = talloc(req, union smb_setfileinfo);
+ NT_STATUS_HAVE_NO_MEMORY(sf);
+ sf->generic.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
+ sf->generic.in.file.ntvfs = io2->generic.out.file.ntvfs;
+ sf->end_of_file_info.in.size = set_size;
+ status = ntvfs->ops->setfileinfo(ntvfs, req, sf);
+ if (NT_STATUS_IS_OK(status)) {
+ io->openx.out.size = io->openx.in.size;
+ }
+ }
+
+ req->async_states->state = state;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ the core of the mapping between openx style parameters and ntcreatex
+ parameters
+*/
+static NTSTATUS map_openx_open(uint16_t flags, uint16_t open_mode,
+ uint16_t open_func, const char *fname,
+ union smb_open *io2)
+{
+ io2->generic.in.create_options = NTCREATEX_OPTIONS_NON_DIRECTORY_FILE;
+
+ if (flags & OPENX_FLAGS_REQUEST_OPLOCK) {
+ io2->generic.in.flags |= NTCREATEX_FLAGS_REQUEST_OPLOCK;
+ }
+ if (flags & OPENX_FLAGS_REQUEST_BATCH_OPLOCK) {
+ io2->generic.in.flags |= NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
+ }
+
+ switch (open_mode & OPENX_MODE_ACCESS_MASK) {
+ case OPENX_MODE_ACCESS_READ:
+ case OPENX_MODE_ACCESS_EXEC:
+ io2->generic.in.access_mask = SEC_RIGHTS_FILE_READ;
+ break;
+ case OPENX_MODE_ACCESS_WRITE:
+ io2->generic.in.access_mask = SEC_RIGHTS_FILE_WRITE;
+ break;
+ case OPENX_MODE_ACCESS_RDWR:
+ case OPENX_MODE_ACCESS_FCB:
+ io2->generic.in.access_mask =
+ SEC_RIGHTS_FILE_READ |
+ SEC_RIGHTS_FILE_WRITE;
+ break;
+ default:
+ return NT_STATUS_DOS(ERRDOS, ERRbadaccess);
+ }
+
+ switch (open_mode & OPENX_MODE_DENY_MASK) {
+ case OPENX_MODE_DENY_READ:
+ io2->generic.in.share_access = NTCREATEX_SHARE_ACCESS_WRITE;
+ break;
+ case OPENX_MODE_DENY_WRITE:
+ io2->generic.in.share_access = NTCREATEX_SHARE_ACCESS_READ;
+ break;
+ case OPENX_MODE_DENY_ALL:
+ io2->generic.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ break;
+ case OPENX_MODE_DENY_NONE:
+ io2->generic.in.share_access =
+ NTCREATEX_SHARE_ACCESS_READ |
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ break;
+ case OPENX_MODE_DENY_DOS:
+ /* DENY_DOS is quite strange - it depends on the filename! */
+ io2->generic.in.create_options |=
+ NTCREATEX_OPTIONS_PRIVATE_DENY_DOS;
+ if (is_exe_filename(fname)) {
+ io2->generic.in.share_access =
+ NTCREATEX_SHARE_ACCESS_READ |
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ } else {
+ if ((open_mode & OPENX_MODE_ACCESS_MASK) == OPENX_MODE_ACCESS_READ) {
+ io2->generic.in.share_access = NTCREATEX_SHARE_ACCESS_READ;
+ } else {
+ io2->generic.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ }
+ }
+ break;
+ case OPENX_MODE_DENY_FCB:
+ io2->generic.in.create_options |= NTCREATEX_OPTIONS_PRIVATE_DENY_FCB;
+ io2->generic.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ break;
+ default:
+ return NT_STATUS_DOS(ERRDOS, ERRbadaccess);
+ }
+
+ switch (open_func) {
+ case (OPENX_OPEN_FUNC_OPEN):
+ io2->generic.in.open_disposition = NTCREATEX_DISP_OPEN;
+ break;
+ case (OPENX_OPEN_FUNC_TRUNC):
+ io2->generic.in.open_disposition = NTCREATEX_DISP_OVERWRITE;
+ break;
+ case (OPENX_OPEN_FUNC_FAIL | OPENX_OPEN_FUNC_CREATE):
+ io2->generic.in.open_disposition = NTCREATEX_DISP_CREATE;
+ break;
+ case (OPENX_OPEN_FUNC_OPEN | OPENX_OPEN_FUNC_CREATE):
+ io2->generic.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+ break;
+ case (OPENX_OPEN_FUNC_TRUNC | OPENX_OPEN_FUNC_CREATE):
+ io2->generic.in.open_disposition = NTCREATEX_DISP_OVERWRITE_IF;
+ break;
+ default:
+ /* this one is very strange */
+ if ((open_mode & OPENX_MODE_ACCESS_MASK) == OPENX_MODE_ACCESS_EXEC) {
+ io2->generic.in.open_disposition = NTCREATEX_DISP_CREATE;
+ break;
+ }
+ return NT_STATUS_DOS(ERRDOS, ERRbadaccess);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ NTVFS open generic to any mapper
+*/
+NTSTATUS ntvfs_map_open(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_open *io)
+{
+ NTSTATUS status;
+ union smb_open *io2;
+
+ io2 = talloc_zero(req, union smb_open);
+ if (io2 == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = ntvfs_map_async_setup(ntvfs, req,
+ io, io2,
+ (second_stage_t)ntvfs_map_open_finish);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ io2->generic.level = RAW_OPEN_GENERIC;
+
+ switch (io->generic.level) {
+ case RAW_OPEN_OPENX:
+ status = map_openx_open(io->openx.in.flags,
+ io->openx.in.open_mode,
+ io->openx.in.open_func,
+ io->openx.in.fname,
+ io2);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ io2->generic.in.file_attr = io->openx.in.file_attrs;
+ io2->generic.in.fname = io->openx.in.fname;
+
+ status = ntvfs->ops->open(ntvfs, req, io2);
+ break;
+
+
+ case RAW_OPEN_OPEN:
+ status = map_openx_open(0,
+ io->openold.in.open_mode,
+ OPENX_OPEN_FUNC_OPEN,
+ io->openold.in.fname,
+ io2);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ io2->generic.in.file_attr = io->openold.in.search_attrs;
+ io2->generic.in.fname = io->openold.in.fname;
+
+ status = ntvfs->ops->open(ntvfs, req, io2);
+ break;
+
+ case RAW_OPEN_T2OPEN:
+ io2->generic.level = RAW_OPEN_NTTRANS_CREATE;
+
+ if (io->t2open.in.open_func == 0) {
+ status = NT_STATUS_OBJECT_NAME_COLLISION;
+ goto done;
+ }
+
+ status = map_openx_open(io->t2open.in.flags,
+ io->t2open.in.open_mode,
+ io->t2open.in.open_func,
+ io->t2open.in.fname,
+ io2);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ io2->generic.in.file_attr = io->t2open.in.file_attrs;
+ io2->generic.in.fname = io->t2open.in.fname;
+ io2->generic.in.ea_list = talloc(io2, struct smb_ea_list);
+ io2->generic.in.ea_list->num_eas = io->t2open.in.num_eas;
+ io2->generic.in.ea_list->eas = io->t2open.in.eas;
+
+ status = ntvfs->ops->open(ntvfs, req, io2);
+ break;
+
+ case RAW_OPEN_MKNEW:
+ io2->generic.in.file_attr = io->mknew.in.attrib;
+ io2->generic.in.fname = io->mknew.in.fname;
+ io2->generic.in.open_disposition = NTCREATEX_DISP_CREATE;
+ io2->generic.in.access_mask =
+ SEC_RIGHTS_FILE_READ |
+ SEC_RIGHTS_FILE_WRITE;
+ io2->generic.in.share_access =
+ NTCREATEX_SHARE_ACCESS_READ |
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ status = ntvfs->ops->open(ntvfs, req, io2);
+ break;
+
+ case RAW_OPEN_CREATE:
+ io2->generic.in.file_attr = io->mknew.in.attrib;
+ io2->generic.in.fname = io->mknew.in.fname;
+ io2->generic.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+ io2->generic.in.access_mask =
+ SEC_RIGHTS_FILE_READ |
+ SEC_RIGHTS_FILE_WRITE;
+ io2->generic.in.share_access =
+ NTCREATEX_SHARE_ACCESS_READ |
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ status = ntvfs->ops->open(ntvfs, req, io2);
+ break;
+
+ case RAW_OPEN_CTEMP:
+ io2->generic.in.file_attr = io->ctemp.in.attrib;
+ io2->generic.in.fname =
+ talloc_asprintf(io2, "%s\\SRV%s",
+ io->ctemp.in.directory,
+ generate_random_str_list(io2, 5, "0123456789"));
+ io2->generic.in.open_disposition = NTCREATEX_DISP_CREATE;
+ io2->generic.in.access_mask =
+ SEC_RIGHTS_FILE_READ |
+ SEC_RIGHTS_FILE_WRITE;
+ io2->generic.in.share_access =
+ NTCREATEX_SHARE_ACCESS_READ |
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ status = ntvfs->ops->open(ntvfs, req, io2);
+ break;
+ case RAW_OPEN_SMB2:
+ switch (io->smb2.in.oplock_level) {
+ case SMB2_OPLOCK_LEVEL_BATCH:
+ io2->generic.in.flags = NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK |
+ NTCREATEX_FLAGS_REQUEST_OPLOCK;
+ break;
+ case SMB2_OPLOCK_LEVEL_EXCLUSIVE:
+ io2->generic.in.flags = NTCREATEX_FLAGS_REQUEST_OPLOCK;
+ break;
+ default:
+ io2->generic.in.flags = 0;
+ break;
+ }
+ io2->generic.in.root_fid = 0;
+ io2->generic.in.access_mask = io->smb2.in.desired_access;
+ io2->generic.in.alloc_size = io->smb2.in.alloc_size;
+ io2->generic.in.file_attr = io->smb2.in.file_attributes;
+ io2->generic.in.share_access = io->smb2.in.share_access;
+ io2->generic.in.open_disposition= io->smb2.in.create_disposition;
+ io2->generic.in.create_options = io->smb2.in.create_options;
+ io2->generic.in.impersonation = io->smb2.in.impersonation_level;
+ io2->generic.in.security_flags = 0;
+ io2->generic.in.fname = io->smb2.in.fname;
+ io2->generic.in.sec_desc = io->smb2.in.sec_desc;
+ io2->generic.in.ea_list = &io->smb2.in.eas;
+ io2->generic.in.query_maximal_access = io->smb2.in.query_maximal_access;
+
+ /* we don't support timewarp yet */
+ if (io->smb2.in.timewarp != 0) {
+ status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ break;
+ }
+
+ /* we need to check these bits before we check the private mask */
+ if (io2->generic.in.create_options & SMB2_CREATE_OPTIONS_NOT_SUPPORTED_MASK) {
+ DEBUG(2,(__location__ " create_options 0x%x not supported\n",
+ io2->generic.in.create_options));
+ status = NT_STATUS_NOT_SUPPORTED;
+ break;
+ }
+
+ /* TODO: find out why only SMB2 ignores these */
+ io2->generic.in.create_options &= ~NTCREATEX_OPTIONS_SYNC_ALERT;
+ io2->generic.in.create_options &= ~NTCREATEX_OPTIONS_ASYNC_ALERT;
+
+ status = ntvfs->ops->open(ntvfs, req, io2);
+ break;
+
+ default:
+ status = NT_STATUS_INVALID_LEVEL;
+ break;
+ }
+done:
+ return ntvfs_map_async_finish(req, status);
+}
+
+
+/*
+ NTVFS fsinfo generic to any mapper
+*/
+NTSTATUS ntvfs_map_fsinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_fsinfo *fs)
+{
+ NTSTATUS status;
+ union smb_fsinfo *fs2;
+
+ fs2 = talloc(req, union smb_fsinfo);
+ if (fs2 == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (fs->generic.level == RAW_QFS_GENERIC) {
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ /* only used by the simple backend, which doesn't do async */
+ req->async_states->state &= ~NTVFS_ASYNC_STATE_MAY_ASYNC;
+
+ /* ask the backend for the generic info */
+ fs2->generic.level = RAW_QFS_GENERIC;
+
+ status = ntvfs->ops->fsinfo(ntvfs, req, fs2);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* and convert it to the required level */
+ switch (fs->generic.level) {
+ case RAW_QFS_GENERIC:
+ return NT_STATUS_INVALID_LEVEL;
+
+ case RAW_QFS_DSKATTR: {
+ /* map from generic to DSKATTR */
+ uint_t bpunit = 64;
+
+ /* we need to scale the sizes to fit */
+ for (bpunit=64; bpunit<0x10000; bpunit *= 2) {
+ if (fs2->generic.out.blocks_total * (double)fs2->generic.out.block_size < bpunit * 512 * 65535.0) {
+ break;
+ }
+ }
+
+ fs->dskattr.out.blocks_per_unit = bpunit;
+ fs->dskattr.out.block_size = 512;
+ fs->dskattr.out.units_total =
+ (fs2->generic.out.blocks_total * (double)fs2->generic.out.block_size) / (bpunit * 512);
+ fs->dskattr.out.units_free =
+ (fs2->generic.out.blocks_free * (double)fs2->generic.out.block_size) / (bpunit * 512);
+
+ /* we must return a maximum of 2G to old DOS systems, or they get very confused */
+ if (bpunit > 64 && req->ctx->protocol <= PROTOCOL_LANMAN2) {
+ fs->dskattr.out.blocks_per_unit = 64;
+ fs->dskattr.out.units_total = 0xFFFF;
+ fs->dskattr.out.units_free = 0xFFFF;
+ }
+ return NT_STATUS_OK;
+ }
+
+ case RAW_QFS_ALLOCATION:
+ fs->allocation.out.fs_id = fs2->generic.out.fs_id;
+ fs->allocation.out.total_alloc_units = fs2->generic.out.blocks_total;
+ fs->allocation.out.avail_alloc_units = fs2->generic.out.blocks_free;
+ fs->allocation.out.sectors_per_unit = 1;
+ fs->allocation.out.bytes_per_sector = fs2->generic.out.block_size;
+ return NT_STATUS_OK;
+
+ case RAW_QFS_VOLUME:
+ fs->volume.out.serial_number = fs2->generic.out.serial_number;
+ fs->volume.out.volume_name.s = fs2->generic.out.volume_name;
+ return NT_STATUS_OK;
+
+ case RAW_QFS_VOLUME_INFO:
+ case RAW_QFS_VOLUME_INFORMATION:
+ fs->volume_info.out.create_time = fs2->generic.out.create_time;
+ fs->volume_info.out.serial_number = fs2->generic.out.serial_number;
+ fs->volume_info.out.volume_name.s = fs2->generic.out.volume_name;
+ return NT_STATUS_OK;
+
+ case RAW_QFS_SIZE_INFO:
+ case RAW_QFS_SIZE_INFORMATION:
+ fs->size_info.out.total_alloc_units = fs2->generic.out.blocks_total;
+ fs->size_info.out.avail_alloc_units = fs2->generic.out.blocks_free;
+ fs->size_info.out.sectors_per_unit = 1;
+ fs->size_info.out.bytes_per_sector = fs2->generic.out.block_size;
+ return NT_STATUS_OK;
+
+ case RAW_QFS_DEVICE_INFO:
+ case RAW_QFS_DEVICE_INFORMATION:
+ fs->device_info.out.device_type = fs2->generic.out.device_type;
+ fs->device_info.out.characteristics = fs2->generic.out.device_characteristics;
+ return NT_STATUS_OK;
+
+ case RAW_QFS_ATTRIBUTE_INFO:
+ case RAW_QFS_ATTRIBUTE_INFORMATION:
+ fs->attribute_info.out.fs_attr = fs2->generic.out.fs_attr;
+ fs->attribute_info.out.max_file_component_length = fs2->generic.out.max_file_component_length;
+ fs->attribute_info.out.fs_type.s = fs2->generic.out.fs_type;
+ return NT_STATUS_OK;
+
+ case RAW_QFS_QUOTA_INFORMATION:
+ ZERO_STRUCT(fs->quota_information.out.unknown);
+ fs->quota_information.out.quota_soft = fs2->generic.out.quota_soft;
+ fs->quota_information.out.quota_hard = fs2->generic.out.quota_hard;
+ fs->quota_information.out.quota_flags = fs2->generic.out.quota_flags;
+ return NT_STATUS_OK;
+
+ case RAW_QFS_FULL_SIZE_INFORMATION:
+ fs->full_size_information.out.total_alloc_units = fs2->generic.out.blocks_total;
+ fs->full_size_information.out.call_avail_alloc_units = fs2->generic.out.blocks_free;
+ fs->full_size_information.out.actual_avail_alloc_units = fs2->generic.out.blocks_free;
+ fs->full_size_information.out.sectors_per_unit = 1;
+ fs->full_size_information.out.bytes_per_sector = fs2->generic.out.block_size;
+ return NT_STATUS_OK;
+
+ case RAW_QFS_OBJECTID_INFORMATION:
+ fs->objectid_information.out.guid = fs2->generic.out.guid;
+ ZERO_STRUCT(fs->objectid_information.out.unknown);
+ return NT_STATUS_OK;
+ }
+
+
+ return NT_STATUS_INVALID_LEVEL;
+}
+
+
+/*
+ NTVFS fileinfo generic to any mapper
+*/
+NTSTATUS ntvfs_map_fileinfo(TALLOC_CTX *mem_ctx,
+ union smb_fileinfo *info,
+ union smb_fileinfo *info2)
+{
+ int i;
+ /* and convert it to the required level using results in info2 */
+ switch (info->generic.level) {
+ case RAW_FILEINFO_GENERIC:
+ return NT_STATUS_INVALID_LEVEL;
+ case RAW_FILEINFO_GETATTR:
+ info->getattr.out.attrib = info2->generic.out.attrib & 0xff;
+ info->getattr.out.size = info2->generic.out.size;
+ info->getattr.out.write_time = nt_time_to_unix(info2->generic.out.write_time);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_GETATTRE:
+ info->getattre.out.attrib = info2->generic.out.attrib;
+ info->getattre.out.size = info2->generic.out.size;
+ info->getattre.out.write_time = nt_time_to_unix(info2->generic.out.write_time);
+ info->getattre.out.create_time = nt_time_to_unix(info2->generic.out.create_time);
+ info->getattre.out.access_time = nt_time_to_unix(info2->generic.out.access_time);
+ info->getattre.out.alloc_size = info2->generic.out.alloc_size;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_NETWORK_OPEN_INFORMATION:
+ info->network_open_information.out.create_time = info2->generic.out.create_time;
+ info->network_open_information.out.access_time = info2->generic.out.access_time;
+ info->network_open_information.out.write_time = info2->generic.out.write_time;
+ info->network_open_information.out.change_time = info2->generic.out.change_time;
+ info->network_open_information.out.alloc_size = info2->generic.out.alloc_size;
+ info->network_open_information.out.size = info2->generic.out.size;
+ info->network_open_information.out.attrib = info2->generic.out.attrib;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_ALL_INFO:
+ case RAW_FILEINFO_ALL_INFORMATION:
+ info->all_info.out.create_time = info2->generic.out.create_time;
+ info->all_info.out.access_time = info2->generic.out.access_time;
+ info->all_info.out.write_time = info2->generic.out.write_time;
+ info->all_info.out.change_time = info2->generic.out.change_time;
+ info->all_info.out.attrib = info2->generic.out.attrib;
+ info->all_info.out.alloc_size = info2->generic.out.alloc_size;
+ info->all_info.out.size = info2->generic.out.size;
+ info->all_info.out.nlink = info2->generic.out.nlink;
+ info->all_info.out.delete_pending = info2->generic.out.delete_pending;
+ info->all_info.out.directory = info2->generic.out.directory;
+ info->all_info.out.ea_size = info2->generic.out.ea_size;
+ info->all_info.out.fname.s = info2->generic.out.fname.s;
+ info->all_info.out.fname.private_length = info2->generic.out.fname.private_length;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_BASIC_INFO:
+ case RAW_FILEINFO_BASIC_INFORMATION:
+ info->basic_info.out.create_time = info2->generic.out.create_time;
+ info->basic_info.out.access_time = info2->generic.out.access_time;
+ info->basic_info.out.write_time = info2->generic.out.write_time;
+ info->basic_info.out.change_time = info2->generic.out.change_time;
+ info->basic_info.out.attrib = info2->generic.out.attrib;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_STANDARD:
+ info->standard.out.create_time = nt_time_to_unix(info2->generic.out.create_time);
+ info->standard.out.access_time = nt_time_to_unix(info2->generic.out.access_time);
+ info->standard.out.write_time = nt_time_to_unix(info2->generic.out.write_time);
+ info->standard.out.size = info2->generic.out.size;
+ info->standard.out.alloc_size = info2->generic.out.alloc_size;
+ info->standard.out.attrib = info2->generic.out.attrib;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_EA_SIZE:
+ info->ea_size.out.create_time = nt_time_to_unix(info2->generic.out.create_time);
+ info->ea_size.out.access_time = nt_time_to_unix(info2->generic.out.access_time);
+ info->ea_size.out.write_time = nt_time_to_unix(info2->generic.out.write_time);
+ info->ea_size.out.size = info2->generic.out.size;
+ info->ea_size.out.alloc_size = info2->generic.out.alloc_size;
+ info->ea_size.out.attrib = info2->generic.out.attrib;
+ info->ea_size.out.ea_size = info2->generic.out.ea_size;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_STANDARD_INFO:
+ case RAW_FILEINFO_STANDARD_INFORMATION:
+ info->standard_info.out.alloc_size = info2->generic.out.alloc_size;
+ info->standard_info.out.size = info2->generic.out.size;
+ info->standard_info.out.nlink = info2->generic.out.nlink;
+ info->standard_info.out.delete_pending = info2->generic.out.delete_pending;
+ info->standard_info.out.directory = info2->generic.out.directory;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_INTERNAL_INFORMATION:
+ info->internal_information.out.file_id = info2->generic.out.file_id;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_EA_INFO:
+ case RAW_FILEINFO_EA_INFORMATION:
+ info->ea_info.out.ea_size = info2->generic.out.ea_size;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION:
+ info->attribute_tag_information.out.attrib = info2->generic.out.attrib;
+ info->attribute_tag_information.out.reparse_tag = info2->generic.out.reparse_tag;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_STREAM_INFO:
+ case RAW_FILEINFO_STREAM_INFORMATION:
+ info->stream_info.out.num_streams = info2->generic.out.num_streams;
+ if (info->stream_info.out.num_streams > 0) {
+ info->stream_info.out.streams =
+ talloc_array(mem_ctx,
+ struct stream_struct,
+ info->stream_info.out.num_streams);
+ if (!info->stream_info.out.streams) {
+ DEBUG(2,("ntvfs_map_fileinfo: no memory for %d streams\n",
+ info->stream_info.out.num_streams));
+ return NT_STATUS_NO_MEMORY;
+ }
+ for (i=0; i < info->stream_info.out.num_streams; i++) {
+ info->stream_info.out.streams[i] = info2->generic.out.streams[i];
+ info->stream_info.out.streams[i].stream_name.s =
+ talloc_strdup(info->stream_info.out.streams,
+ info2->generic.out.streams[i].stream_name.s);
+ if (!info->stream_info.out.streams[i].stream_name.s) {
+ DEBUG(2,("ntvfs_map_fileinfo: no memory for stream_name\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+ }
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_NAME_INFO:
+ case RAW_FILEINFO_NAME_INFORMATION:
+ info->name_info.out.fname.s = talloc_strdup(mem_ctx, info2->generic.out.fname.s);
+ NT_STATUS_HAVE_NO_MEMORY(info->name_info.out.fname.s);
+ info->name_info.out.fname.private_length = info2->generic.out.fname.private_length;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_ALT_NAME_INFO:
+ case RAW_FILEINFO_ALT_NAME_INFORMATION:
+ info->alt_name_info.out.fname.s = talloc_strdup(mem_ctx, info2->generic.out.alt_fname.s);
+ NT_STATUS_HAVE_NO_MEMORY(info->alt_name_info.out.fname.s);
+ info->alt_name_info.out.fname.private_length = info2->generic.out.alt_fname.private_length;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_POSITION_INFORMATION:
+ info->position_information.out.position = info2->generic.out.position;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_ALL_EAS:
+ info->all_eas.out.num_eas = info2->generic.out.num_eas;
+ if (info->all_eas.out.num_eas > 0) {
+ info->all_eas.out.eas = talloc_array(mem_ctx,
+ struct ea_struct,
+ info->all_eas.out.num_eas);
+ if (!info->all_eas.out.eas) {
+ DEBUG(2,("ntvfs_map_fileinfo: no memory for %d eas\n",
+ info->all_eas.out.num_eas));
+ return NT_STATUS_NO_MEMORY;
+ }
+ for (i = 0; i < info->all_eas.out.num_eas; i++) {
+ info->all_eas.out.eas[i] = info2->generic.out.eas[i];
+ info->all_eas.out.eas[i].name.s =
+ talloc_strdup(info->all_eas.out.eas,
+ info2->generic.out.eas[i].name.s);
+ if (!info->all_eas.out.eas[i].name.s) {
+ DEBUG(2,("ntvfs_map_fileinfo: no memory for stream_name\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+ info->all_eas.out.eas[i].value.data =
+ (uint8_t *)talloc_memdup(info->all_eas.out.eas,
+ info2->generic.out.eas[i].value.data,
+ info2->generic.out.eas[i].value.length);
+ if (!info->all_eas.out.eas[i].value.data) {
+ DEBUG(2,("ntvfs_map_fileinfo: no memory for stream_name\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+ }
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_IS_NAME_VALID:
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_COMPRESSION_INFO:
+ case RAW_FILEINFO_COMPRESSION_INFORMATION:
+ info->compression_info.out.compressed_size = info2->generic.out.compressed_size;
+ info->compression_info.out.format = info2->generic.out.format;
+ info->compression_info.out.unit_shift = info2->generic.out.unit_shift;
+ info->compression_info.out.chunk_shift = info2->generic.out.chunk_shift;
+ info->compression_info.out.cluster_shift = info2->generic.out.cluster_shift;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_ACCESS_INFORMATION:
+ info->access_information.out.access_flags = info2->generic.out.access_flags;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_MODE_INFORMATION:
+ info->mode_information.out.mode = info2->generic.out.mode;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_ALIGNMENT_INFORMATION:
+ info->alignment_information.out.alignment_requirement =
+ info2->generic.out.alignment_requirement;
+ return NT_STATUS_OK;
+#if 0
+ case RAW_FILEINFO_UNIX_BASIC:
+ info->unix_basic_info.out.end_of_file = info2->generic.out.end_of_file;
+ info->unix_basic_info.out.num_bytes = info2->generic.out.size;
+ info->unix_basic_info.out.status_change_time = info2->generic.out.change_time;
+ info->unix_basic_info.out.access_time = info2->generic.out.access_time;
+ info->unix_basic_info.out.change_time = info2->generic.out.change_time;
+ info->unix_basic_info.out.uid = info2->generic.out.uid;
+ info->unix_basic_info.out.gid = info2->generic.out.gid;
+ info->unix_basic_info.out.file_type = info2->generic.out.file_type;
+ info->unix_basic_info.out.dev_major = info2->generic.out.device;
+ info->unix_basic_info.out.dev_minor = info2->generic.out.device;
+ info->unix_basic_info.out.unique_id = info2->generic.out.inode;
+ info->unix_basic_info.out.permissions = info2->generic.out.permissions;
+ info->unix_basic_info.out.nlink = info2->generic.out.nlink;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_UNIX_LINK:
+ info->unix_link_info.out.link_dest = info2->generic.out.link_dest;
+ return NT_STATUS_OK;
+#endif
+ }
+
+ return NT_STATUS_INVALID_LEVEL;
+}
+
+/*
+ NTVFS fileinfo generic to any mapper
+*/
+NTSTATUS ntvfs_map_qfileinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_fileinfo *info)
+{
+ NTSTATUS status;
+ union smb_fileinfo *info2;
+
+ info2 = talloc(req, union smb_fileinfo);
+ if (info2 == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (info->generic.level == RAW_FILEINFO_GENERIC) {
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ /* ask the backend for the generic info */
+ info2->generic.level = RAW_FILEINFO_GENERIC;
+ info2->generic.in.file.ntvfs= info->generic.in.file.ntvfs;
+
+ /* only used by the simple backend, which doesn't do async */
+ req->async_states->state &= ~NTVFS_ASYNC_STATE_MAY_ASYNC;
+
+ status = ntvfs->ops->qfileinfo(ntvfs, req, info2);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ return ntvfs_map_fileinfo(req, info, info2);
+}
+
+/*
+ NTVFS pathinfo generic to any mapper
+*/
+NTSTATUS ntvfs_map_qpathinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_fileinfo *info)
+{
+ NTSTATUS status;
+ union smb_fileinfo *info2;
+
+ info2 = talloc(req, union smb_fileinfo);
+ if (info2 == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (info->generic.level == RAW_FILEINFO_GENERIC) {
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ /* ask the backend for the generic info */
+ info2->generic.level = RAW_FILEINFO_GENERIC;
+ info2->generic.in.file.path = info->generic.in.file.path;
+
+ /* only used by the simple backend, which doesn't do async */
+ req->async_states->state &= ~NTVFS_ASYNC_STATE_MAY_ASYNC;
+
+ status = ntvfs->ops->qpathinfo(ntvfs, req, info2);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ return ntvfs_map_fileinfo(req, info, info2);
+}
+
+
+/*
+ NTVFS lock generic to any mapper
+*/
+NTSTATUS ntvfs_map_lock(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_lock *lck)
+{
+ union smb_lock *lck2;
+ struct smb_lock_entry *locks;
+
+ lck2 = talloc(req, union smb_lock);
+ if (lck2 == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ locks = talloc_array(lck2, struct smb_lock_entry, 1);
+ if (locks == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ switch (lck->generic.level) {
+ case RAW_LOCK_LOCKX:
+ return NT_STATUS_INVALID_LEVEL;
+
+ case RAW_LOCK_LOCK:
+ lck2->generic.level = RAW_LOCK_GENERIC;
+ lck2->generic.in.file.ntvfs= lck->lock.in.file.ntvfs;
+ lck2->generic.in.mode = 0;
+ lck2->generic.in.timeout = 0;
+ lck2->generic.in.ulock_cnt = 0;
+ lck2->generic.in.lock_cnt = 1;
+ lck2->generic.in.locks = locks;
+ locks->pid = req->smbpid;
+ locks->offset = lck->lock.in.offset;
+ locks->count = lck->lock.in.count;
+ break;
+
+ case RAW_LOCK_UNLOCK:
+ lck2->generic.level = RAW_LOCK_GENERIC;
+ lck2->generic.in.file.ntvfs= lck->unlock.in.file.ntvfs;
+ lck2->generic.in.mode = 0;
+ lck2->generic.in.timeout = 0;
+ lck2->generic.in.ulock_cnt = 1;
+ lck2->generic.in.lock_cnt = 0;
+ lck2->generic.in.locks = locks;
+ locks->pid = req->smbpid;
+ locks->offset = lck->unlock.in.offset;
+ locks->count = lck->unlock.in.count;
+ break;
+
+ case RAW_LOCK_SMB2: {
+ /* this is only approximate! We need to change the
+ generic structure to fix this properly */
+ int i;
+ bool isunlock;
+ if (lck->smb2.in.lock_count < 1) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ lck2->generic.level = RAW_LOCK_GENERIC;
+ lck2->generic.in.file.ntvfs= lck->smb2.in.file.ntvfs;
+ lck2->generic.in.timeout = UINT32_MAX;
+ lck2->generic.in.mode = 0;
+ lck2->generic.in.lock_cnt = 0;
+ lck2->generic.in.ulock_cnt = 0;
+ lck2->generic.in.locks = talloc_zero_array(lck2, struct smb_lock_entry,
+ lck->smb2.in.lock_count);
+ if (lck2->generic.in.locks == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ /* only the first lock gives the UNLOCK bit - see
+ MS-SMB2 3.3.5.14 */
+ if (lck->smb2.in.locks[0].flags & SMB2_LOCK_FLAG_UNLOCK) {
+ lck2->generic.in.ulock_cnt = lck->smb2.in.lock_count;
+ isunlock = true;
+ } else {
+ lck2->generic.in.lock_cnt = lck->smb2.in.lock_count;
+ isunlock = false;
+ }
+ for (i=0;i<lck->smb2.in.lock_count;i++) {
+ if (isunlock &&
+ (lck->smb2.in.locks[i].flags &
+ (SMB2_LOCK_FLAG_SHARED|SMB2_LOCK_FLAG_EXCLUSIVE))) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ if (!isunlock &&
+ (lck->smb2.in.locks[i].flags & SMB2_LOCK_FLAG_UNLOCK)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ lck2->generic.in.locks[i].pid = req->smbpid;
+ lck2->generic.in.locks[i].offset = lck->smb2.in.locks[i].offset;
+ lck2->generic.in.locks[i].count = lck->smb2.in.locks[i].length;
+ if (!(lck->smb2.in.locks[i].flags & SMB2_LOCK_FLAG_EXCLUSIVE)) {
+ lck2->generic.in.mode = LOCKING_ANDX_SHARED_LOCK;
+ }
+ if (lck->smb2.in.locks[i].flags & SMB2_LOCK_FLAG_FAIL_IMMEDIATELY) {
+ lck2->generic.in.timeout = 0;
+ }
+ }
+ /* initialize output value */
+ lck->smb2.out.reserved = 0;
+ break;
+ }
+
+ case RAW_LOCK_SMB2_BREAK:
+ lck2->generic.level = RAW_LOCK_GENERIC;
+ lck2->generic.in.file.ntvfs = lck->smb2_break.in.file.ntvfs;
+ lck2->generic.in.mode = LOCKING_ANDX_OPLOCK_RELEASE |
+ ((lck->smb2_break.in.oplock_level << 8) & 0xFF00);
+ lck2->generic.in.timeout = 0;
+ lck2->generic.in.ulock_cnt = 0;
+ lck2->generic.in.lock_cnt = 0;
+ lck2->generic.in.locks = NULL;
+
+ /* initialize output value */
+ lck->smb2_break.out.oplock_level= lck->smb2_break.in.oplock_level;
+ lck->smb2_break.out.reserved = lck->smb2_break.in.reserved;
+ lck->smb2_break.out.reserved2 = lck->smb2_break.in.reserved2;
+ lck->smb2_break.out.file = lck->smb2_break.in.file;
+ break;
+ }
+
+ /*
+ * we don't need to call ntvfs_map_async_setup() here,
+ * as lock() doesn't have any output fields
+ */
+
+ return ntvfs->ops->lock(ntvfs, req, lck2);
+}
+
+
+/*
+ NTVFS write generic to any mapper
+*/
+static NTSTATUS ntvfs_map_write_finish(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_write *wr,
+ union smb_write *wr2,
+ NTSTATUS status)
+{
+ union smb_lock *lck;
+ union smb_close *cl;
+ uint_t state;
+
+ if (NT_STATUS_IS_ERR(status)) {
+ return status;
+ }
+
+ switch (wr->generic.level) {
+ case RAW_WRITE_WRITE:
+ wr->write.out.nwritten = wr2->generic.out.nwritten;
+ break;
+
+ case RAW_WRITE_WRITEUNLOCK:
+ wr->writeunlock.out.nwritten = wr2->generic.out.nwritten;
+
+ lck = talloc(wr2, union smb_lock);
+ if (lck == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ lck->unlock.level = RAW_LOCK_UNLOCK;
+ lck->unlock.in.file.ntvfs = wr->writeunlock.in.file.ntvfs;
+ lck->unlock.in.count = wr->writeunlock.in.count;
+ lck->unlock.in.offset = wr->writeunlock.in.offset;
+
+ if (lck->unlock.in.count != 0) {
+ /* do the lock sync for now */
+ state = req->async_states->state;
+ req->async_states->state &= ~NTVFS_ASYNC_STATE_MAY_ASYNC;
+ status = ntvfs->ops->lock(ntvfs, req, lck);
+ req->async_states->state = state;
+ }
+ break;
+
+ case RAW_WRITE_WRITECLOSE:
+ wr->writeclose.out.nwritten = wr2->generic.out.nwritten;
+
+ cl = talloc(wr2, union smb_close);
+ if (cl == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ cl->close.level = RAW_CLOSE_CLOSE;
+ cl->close.in.file.ntvfs = wr->writeclose.in.file.ntvfs;
+ cl->close.in.write_time = wr->writeclose.in.mtime;
+
+ if (wr2->generic.in.count != 0) {
+ /* do the close sync for now */
+ state = req->async_states->state;
+ req->async_states->state &= ~NTVFS_ASYNC_STATE_MAY_ASYNC;
+ status = ntvfs->ops->close(ntvfs, req, cl);
+ req->async_states->state = state;
+ }
+ break;
+
+ case RAW_WRITE_SPLWRITE:
+ break;
+
+ case RAW_WRITE_SMB2:
+ wr->smb2.out._pad = 0;
+ wr->smb2.out.nwritten = wr2->generic.out.nwritten;
+ wr->smb2.out.unknown1 = 0;
+ break;
+
+ default:
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ return status;
+}
+
+
+/*
+ NTVFS write generic to any mapper
+*/
+NTSTATUS ntvfs_map_write(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_write *wr)
+{
+ union smb_write *wr2;
+ NTSTATUS status;
+
+ wr2 = talloc(req, union smb_write);
+ if (wr2 == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = ntvfs_map_async_setup(ntvfs, req, wr, wr2,
+ (second_stage_t)ntvfs_map_write_finish);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ wr2->writex.level = RAW_WRITE_GENERIC;
+
+ switch (wr->generic.level) {
+ case RAW_WRITE_WRITEX:
+ status = NT_STATUS_INVALID_LEVEL;
+ break;
+
+ case RAW_WRITE_WRITE:
+ wr2->writex.in.file.ntvfs= wr->write.in.file.ntvfs;
+ wr2->writex.in.offset = wr->write.in.offset;
+ wr2->writex.in.wmode = 0;
+ wr2->writex.in.remaining = wr->write.in.remaining;
+ wr2->writex.in.count = wr->write.in.count;
+ wr2->writex.in.data = wr->write.in.data;
+ status = ntvfs->ops->write(ntvfs, req, wr2);
+ break;
+
+ case RAW_WRITE_WRITEUNLOCK:
+ wr2->writex.in.file.ntvfs= wr->writeunlock.in.file.ntvfs;
+ wr2->writex.in.offset = wr->writeunlock.in.offset;
+ wr2->writex.in.wmode = 0;
+ wr2->writex.in.remaining = wr->writeunlock.in.remaining;
+ wr2->writex.in.count = wr->writeunlock.in.count;
+ wr2->writex.in.data = wr->writeunlock.in.data;
+ status = ntvfs->ops->write(ntvfs, req, wr2);
+ break;
+
+ case RAW_WRITE_WRITECLOSE:
+ wr2->writex.in.file.ntvfs= wr->writeclose.in.file.ntvfs;
+ wr2->writex.in.offset = wr->writeclose.in.offset;
+ wr2->writex.in.wmode = 0;
+ wr2->writex.in.remaining = 0;
+ wr2->writex.in.count = wr->writeclose.in.count;
+ wr2->writex.in.data = wr->writeclose.in.data;
+ status = ntvfs->ops->write(ntvfs, req, wr2);
+ break;
+
+ case RAW_WRITE_SPLWRITE:
+ wr2->writex.in.file.ntvfs= wr->splwrite.in.file.ntvfs;
+ wr2->writex.in.offset = 0;
+ wr2->writex.in.wmode = 0;
+ wr2->writex.in.remaining = 0;
+ wr2->writex.in.count = wr->splwrite.in.count;
+ wr2->writex.in.data = wr->splwrite.in.data;
+ status = ntvfs->ops->write(ntvfs, req, wr2);
+ break;
+
+ case RAW_WRITE_SMB2:
+ wr2->writex.in.file.ntvfs= wr->smb2.in.file.ntvfs;
+ wr2->writex.in.offset = wr->smb2.in.offset;
+ wr2->writex.in.wmode = 0;
+ wr2->writex.in.remaining = 0;
+ wr2->writex.in.count = wr->smb2.in.data.length;
+ wr2->writex.in.data = wr->smb2.in.data.data;
+ status = ntvfs->ops->write(ntvfs, req, wr2);
+ }
+
+ return ntvfs_map_async_finish(req, status);
+}
+
+
+/*
+ NTVFS read generic to any mapper - finish the out mapping
+*/
+static NTSTATUS ntvfs_map_read_finish(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_read *rd,
+ union smb_read *rd2,
+ NTSTATUS status)
+{
+ switch (rd->generic.level) {
+ case RAW_READ_READ:
+ rd->read.out.nread = rd2->generic.out.nread;
+ break;
+ case RAW_READ_READBRAW:
+ rd->readbraw.out.nread = rd2->generic.out.nread;
+ break;
+ case RAW_READ_LOCKREAD:
+ rd->lockread.out.nread = rd2->generic.out.nread;
+ break;
+ case RAW_READ_SMB2:
+ rd->smb2.out.data.length= rd2->generic.out.nread;
+ rd->smb2.out.remaining = 0;
+ rd->smb2.out.reserved = 0;
+ break;
+ default:
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ return status;
+}
+
+/*
+ NTVFS read* to readx mapper
+*/
+NTSTATUS ntvfs_map_read(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_read *rd)
+{
+ union smb_read *rd2;
+ union smb_lock *lck;
+ NTSTATUS status;
+ uint_t state;
+
+ rd2 = talloc(req, union smb_read);
+ if (rd2 == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = ntvfs_map_async_setup(ntvfs, req, rd, rd2,
+ (second_stage_t)ntvfs_map_read_finish);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ rd2->readx.level = RAW_READ_READX;
+ rd2->readx.in.read_for_execute = false;
+
+ switch (rd->generic.level) {
+ case RAW_READ_READX:
+ status = NT_STATUS_INVALID_LEVEL;
+ break;
+
+ case RAW_READ_READ:
+ rd2->readx.in.file.ntvfs= rd->read.in.file.ntvfs;
+ rd2->readx.in.offset = rd->read.in.offset;
+ rd2->readx.in.mincnt = rd->read.in.count;
+ rd2->readx.in.maxcnt = rd->read.in.count;
+ rd2->readx.in.remaining = rd->read.in.remaining;
+ rd2->readx.out.data = rd->read.out.data;
+ status = ntvfs->ops->read(ntvfs, req, rd2);
+ break;
+
+ case RAW_READ_READBRAW:
+ rd2->readx.in.file.ntvfs= rd->readbraw.in.file.ntvfs;
+ rd2->readx.in.offset = rd->readbraw.in.offset;
+ rd2->readx.in.mincnt = rd->readbraw.in.mincnt;
+ rd2->readx.in.maxcnt = rd->readbraw.in.maxcnt;
+ rd2->readx.in.remaining = 0;
+ rd2->readx.out.data = rd->readbraw.out.data;
+ status = ntvfs->ops->read(ntvfs, req, rd2);
+ break;
+
+ case RAW_READ_LOCKREAD:
+ /* do the initial lock sync for now */
+ state = req->async_states->state;
+ req->async_states->state &= ~NTVFS_ASYNC_STATE_MAY_ASYNC;
+
+ lck = talloc(rd2, union smb_lock);
+ if (lck == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ lck->lock.level = RAW_LOCK_LOCK;
+ lck->lock.in.file.ntvfs = rd->lockread.in.file.ntvfs;
+ lck->lock.in.count = rd->lockread.in.count;
+ lck->lock.in.offset = rd->lockread.in.offset;
+ status = ntvfs->ops->lock(ntvfs, req, lck);
+ req->async_states->state = state;
+
+ rd2->readx.in.file.ntvfs= rd->lockread.in.file.ntvfs;
+ rd2->readx.in.offset = rd->lockread.in.offset;
+ rd2->readx.in.mincnt = rd->lockread.in.count;
+ rd2->readx.in.maxcnt = rd->lockread.in.count;
+ rd2->readx.in.remaining = rd->lockread.in.remaining;
+ rd2->readx.out.data = rd->lockread.out.data;
+
+ if (NT_STATUS_IS_OK(status)) {
+ status = ntvfs->ops->read(ntvfs, req, rd2);
+ }
+ break;
+
+ case RAW_READ_SMB2:
+ rd2->readx.in.file.ntvfs= rd->smb2.in.file.ntvfs;
+ rd2->readx.in.offset = rd->smb2.in.offset;
+ rd2->readx.in.mincnt = rd->smb2.in.min_count;
+ rd2->readx.in.maxcnt = rd->smb2.in.length;
+ rd2->readx.in.remaining = 0;
+ rd2->readx.out.data = rd->smb2.out.data.data;
+ status = ntvfs->ops->read(ntvfs, req, rd2);
+ break;
+ }
+
+done:
+ return ntvfs_map_async_finish(req, status);
+}
+
+
+/*
+ NTVFS close generic to any mapper
+*/
+static NTSTATUS ntvfs_map_close_finish(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_close *cl,
+ union smb_close *cl2,
+ NTSTATUS status)
+{
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ switch (cl->generic.level) {
+ case RAW_CLOSE_SMB2:
+ cl->smb2.out.flags = cl2->generic.out.flags;
+ cl->smb2.out._pad = 0;
+ cl->smb2.out.create_time = cl2->generic.out.create_time;
+ cl->smb2.out.access_time = cl2->generic.out.access_time;
+ cl->smb2.out.write_time = cl2->generic.out.write_time;
+ cl->smb2.out.change_time = cl2->generic.out.change_time;
+ cl->smb2.out.alloc_size = cl2->generic.out.alloc_size;
+ cl->smb2.out.size = cl2->generic.out.size;
+ cl->smb2.out.file_attr = cl2->generic.out.file_attr;
+ break;
+ default:
+ break;
+ }
+
+ return status;
+}
+
+/*
+ NTVFS close generic to any mapper
+*/
+NTSTATUS ntvfs_map_close(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_close *cl)
+{
+ union smb_close *cl2;
+ NTSTATUS status;
+
+ cl2 = talloc(req, union smb_close);
+ if (cl2 == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ switch (cl->generic.level) {
+ case RAW_CLOSE_GENERIC:
+ return NT_STATUS_INVALID_LEVEL;
+
+ case RAW_CLOSE_CLOSE:
+ cl2->generic.level = RAW_CLOSE_GENERIC;
+ cl2->generic.in.file = cl->close.in.file;
+ cl2->generic.in.write_time = cl->close.in.write_time;
+ cl2->generic.in.flags = 0;
+ break;
+
+ case RAW_CLOSE_SPLCLOSE:
+ cl2->generic.level = RAW_CLOSE_GENERIC;
+ cl2->generic.in.file = cl->splclose.in.file;
+ cl2->generic.in.write_time = 0;
+ cl2->generic.in.flags = 0;
+ break;
+
+ case RAW_CLOSE_SMB2:
+ cl2->generic.level = RAW_CLOSE_GENERIC;
+ cl2->generic.in.file = cl->smb2.in.file;
+ cl2->generic.in.write_time = 0;
+ cl2->generic.in.flags = cl->smb2.in.flags;
+ break;
+ }
+
+ status = ntvfs_map_async_setup(ntvfs, req, cl, cl2,
+ (second_stage_t)ntvfs_map_close_finish);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ status = ntvfs->ops->close(ntvfs, req, cl2);
+
+ return ntvfs_map_async_finish(req, status);
+}
+
+/*
+ NTVFS notify generic to any mapper
+*/
+static NTSTATUS ntvfs_map_notify_finish(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_notify *nt,
+ union smb_notify *nt2,
+ NTSTATUS status)
+{
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ switch (nt->nttrans.level) {
+ case RAW_NOTIFY_SMB2:
+ if (nt2->nttrans.out.num_changes == 0) {
+ return STATUS_NOTIFY_ENUM_DIR;
+ }
+ nt->smb2.out.num_changes = nt2->nttrans.out.num_changes;
+ nt->smb2.out.changes = talloc_steal(req, nt2->nttrans.out.changes);
+ break;
+
+ default:
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ return status;
+}
+
+
+/*
+ NTVFS notify generic to any mapper
+*/
+NTSTATUS ntvfs_map_notify(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_notify *nt)
+{
+ union smb_notify *nt2;
+ NTSTATUS status;
+
+ nt2 = talloc(req, union smb_notify);
+ NT_STATUS_HAVE_NO_MEMORY(nt2);
+
+ status = ntvfs_map_async_setup(ntvfs, req, nt, nt2,
+ (second_stage_t)ntvfs_map_notify_finish);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ nt2->nttrans.level = RAW_NOTIFY_NTTRANS;
+
+ switch (nt->nttrans.level) {
+ case RAW_NOTIFY_NTTRANS:
+ status = NT_STATUS_INVALID_LEVEL;
+ break;
+
+ case RAW_NOTIFY_SMB2:
+ nt2->nttrans.in.file.ntvfs = nt->smb2.in.file.ntvfs;
+ nt2->nttrans.in.buffer_size = nt->smb2.in.buffer_size;
+ nt2->nttrans.in.completion_filter = nt->smb2.in.completion_filter;
+ nt2->nttrans.in.recursive = nt->smb2.in.recursive;
+ status = ntvfs->ops->notify(ntvfs, req, nt2);
+ break;
+ }
+
+ return ntvfs_map_async_finish(req, status);
+}
diff --git a/source4/ntvfs/ntvfs_interface.c b/source4/ntvfs/ntvfs_interface.c
new file mode 100644
index 0000000000..6d3fe55c06
--- /dev/null
+++ b/source4/ntvfs/ntvfs_interface.c
@@ -0,0 +1,716 @@
+/*
+ Unix SMB/CIFS implementation.
+ NTVFS interface functions
+
+ Copyright (C) Stefan (metze) Metzmacher 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "ntvfs/ntvfs.h"
+
+/* connect/disconnect */
+NTSTATUS ntvfs_connect(struct ntvfs_request *req, const char *sharename)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->connect) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->connect(ntvfs, req, sharename);
+}
+
+NTSTATUS ntvfs_disconnect(struct ntvfs_context *ntvfs_ctx)
+{
+ struct ntvfs_module_context *ntvfs;
+ if (ntvfs_ctx == NULL) {
+ return NT_STATUS_INVALID_CONNECTION;
+ }
+ ntvfs = ntvfs_ctx->modules;
+ if (!ntvfs->ops->disconnect) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->disconnect(ntvfs);
+}
+
+/* async setup - called by a backend that wants to setup any state for
+ a async request */
+NTSTATUS ntvfs_async_setup(struct ntvfs_request *req, void *private_data)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->async_setup) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->async_setup(ntvfs, req, private_data);
+}
+
+/* filesystem operations */
+NTSTATUS ntvfs_fsinfo(struct ntvfs_request *req, union smb_fsinfo *fs)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->fsinfo) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->fsinfo(ntvfs, req, fs);
+}
+
+/* path operations */
+NTSTATUS ntvfs_unlink(struct ntvfs_request *req, union smb_unlink *unl)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->unlink) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->unlink(ntvfs, req, unl);
+}
+
+NTSTATUS ntvfs_chkpath(struct ntvfs_request *req, union smb_chkpath *cp)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->chkpath) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->chkpath(ntvfs, req, cp);
+}
+
+NTSTATUS ntvfs_qpathinfo(struct ntvfs_request *req, union smb_fileinfo *st)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->qpathinfo) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->qpathinfo(ntvfs, req, st);
+}
+
+NTSTATUS ntvfs_setpathinfo(struct ntvfs_request *req, union smb_setfileinfo *st)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->setpathinfo) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->setpathinfo(ntvfs, req, st);
+}
+
+NTSTATUS ntvfs_open(struct ntvfs_request *req, union smb_open *oi)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->open) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->open(ntvfs, req, oi);
+}
+
+NTSTATUS ntvfs_mkdir(struct ntvfs_request *req, union smb_mkdir *md)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->mkdir) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->mkdir(ntvfs, req, md);
+}
+
+NTSTATUS ntvfs_rmdir(struct ntvfs_request *req, struct smb_rmdir *rd)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->rmdir) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->rmdir(ntvfs, req, rd);
+}
+
+NTSTATUS ntvfs_rename(struct ntvfs_request *req, union smb_rename *ren)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->rename) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->rename(ntvfs, req, ren);
+}
+
+NTSTATUS ntvfs_copy(struct ntvfs_request *req, struct smb_copy *cp)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->copy) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->copy(ntvfs, req, cp);
+}
+
+/* directory search */
+NTSTATUS ntvfs_search_first(struct ntvfs_request *req, union smb_search_first *io, void *private_data,
+ bool ntvfs_callback(void *private_data, const union smb_search_data *file))
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->search_first) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->search_first(ntvfs, req, io, private_data, ntvfs_callback);
+}
+
+NTSTATUS ntvfs_search_next(struct ntvfs_request *req, union smb_search_next *io, void *private_data,
+ bool ntvfs_callback(void *private_data, const union smb_search_data *file))
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->search_next) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->search_next(ntvfs, req, io, private_data, ntvfs_callback);
+}
+
+NTSTATUS ntvfs_search_close(struct ntvfs_request *req, union smb_search_close *io)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->search_close) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->search_close(ntvfs, req, io);
+}
+
+/* operations on open files */
+NTSTATUS ntvfs_ioctl(struct ntvfs_request *req, union smb_ioctl *io)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->ioctl) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->ioctl(ntvfs, req, io);
+}
+
+NTSTATUS ntvfs_read(struct ntvfs_request *req, union smb_read *io)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->read) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->read(ntvfs, req, io);
+}
+
+NTSTATUS ntvfs_write(struct ntvfs_request *req, union smb_write *io)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->write) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->write(ntvfs, req, io);
+}
+
+NTSTATUS ntvfs_seek(struct ntvfs_request *req, union smb_seek *io)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->seek) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->seek(ntvfs, req, io);
+}
+
+NTSTATUS ntvfs_flush(struct ntvfs_request *req,
+ union smb_flush *flush)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->flush) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->flush(ntvfs, req, flush);
+}
+
+NTSTATUS ntvfs_lock(struct ntvfs_request *req, union smb_lock *lck)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->lock) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->lock(ntvfs, req, lck);
+}
+
+NTSTATUS ntvfs_qfileinfo(struct ntvfs_request *req, union smb_fileinfo *info)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->qfileinfo) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->qfileinfo(ntvfs, req, info);
+}
+
+NTSTATUS ntvfs_setfileinfo(struct ntvfs_request *req, union smb_setfileinfo *info)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->setfileinfo) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->setfileinfo(ntvfs, req, info);
+}
+
+NTSTATUS ntvfs_close(struct ntvfs_request *req, union smb_close *io)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->close) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->close(ntvfs, req, io);
+}
+
+/* trans interface - used by IPC backend for pipes and RAP calls */
+NTSTATUS ntvfs_trans(struct ntvfs_request *req, struct smb_trans2 *trans)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->trans) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->trans(ntvfs, req, trans);
+}
+
+/* trans2 interface - only used by CIFS backend to prover complete passthru for testing */
+NTSTATUS ntvfs_trans2(struct ntvfs_request *req, struct smb_trans2 *trans2)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->trans2) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->trans2(ntvfs, req, trans2);
+}
+
+/* printing specific operations */
+NTSTATUS ntvfs_lpq(struct ntvfs_request *req, union smb_lpq *lpq)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->lpq) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->lpq(ntvfs, req, lpq);
+}
+
+/* logoff - called when a vuid is closed */
+NTSTATUS ntvfs_logoff(struct ntvfs_request *req)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->logoff) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->logoff(ntvfs, req);
+}
+
+NTSTATUS ntvfs_exit(struct ntvfs_request *req)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->exit) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->exit(ntvfs, req);
+}
+
+/*
+ change notify request
+*/
+NTSTATUS ntvfs_notify(struct ntvfs_request *req, union smb_notify *info)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->notify) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->notify(ntvfs, req, info);
+}
+
+/*
+ cancel an outstanding async request
+*/
+NTSTATUS ntvfs_cancel(struct ntvfs_request *req)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->cancel) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->cancel(ntvfs, req);
+}
+
+/* initial setup */
+NTSTATUS ntvfs_next_connect(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, const char *sharename)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->connect) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->connect(ntvfs->next, req, sharename);
+}
+
+NTSTATUS ntvfs_next_disconnect(struct ntvfs_module_context *ntvfs)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->disconnect) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->disconnect(ntvfs->next);
+}
+
+/* async_setup - called when setting up for a async request */
+NTSTATUS ntvfs_next_async_setup(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ void *private_data)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->async_setup) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->async_setup(ntvfs->next, req, private_data);
+}
+
+/* filesystem operations */
+NTSTATUS ntvfs_next_fsinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_fsinfo *fs)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->fsinfo) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->fsinfo(ntvfs->next, req, fs);
+}
+
+/* path operations */
+NTSTATUS ntvfs_next_unlink(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_unlink *unl)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->unlink) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->unlink(ntvfs->next, req, unl);
+}
+
+NTSTATUS ntvfs_next_chkpath(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_chkpath *cp)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->chkpath) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->chkpath(ntvfs->next, req, cp);
+}
+
+NTSTATUS ntvfs_next_qpathinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_fileinfo *st)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->qpathinfo) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->qpathinfo(ntvfs->next, req, st);
+}
+
+NTSTATUS ntvfs_next_setpathinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_setfileinfo *st)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->setpathinfo) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->setpathinfo(ntvfs->next, req, st);
+}
+
+NTSTATUS ntvfs_next_mkdir(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_mkdir *md)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->mkdir) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->mkdir(ntvfs->next, req, md);
+}
+
+NTSTATUS ntvfs_next_rmdir(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ struct smb_rmdir *rd)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->rmdir) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->rmdir(ntvfs->next, req, rd);
+}
+
+NTSTATUS ntvfs_next_rename(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_rename *ren)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->rename) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->rename(ntvfs->next, req, ren);
+}
+
+NTSTATUS ntvfs_next_copy(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ struct smb_copy *cp)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->copy) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->copy(ntvfs->next, req, cp);
+}
+
+NTSTATUS ntvfs_next_open(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_open *oi)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->open) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->open(ntvfs->next, req, oi);
+}
+
+
+/* directory search */
+NTSTATUS ntvfs_next_search_first(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_search_first *io, void *private_data,
+ bool (*callback)(void *private_data, const union smb_search_data *file))
+{
+ if (!ntvfs->next || !ntvfs->next->ops->search_first) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->search_first(ntvfs->next, req, io, private_data, callback);
+}
+
+NTSTATUS ntvfs_next_search_next(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_search_next *io, void *private_data,
+ bool (*callback)(void *private_data, const union smb_search_data *file))
+{
+ if (!ntvfs->next || !ntvfs->next->ops->search_next) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->search_next(ntvfs->next, req, io, private_data, callback);
+}
+
+NTSTATUS ntvfs_next_search_close(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_search_close *io)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->search_close) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->search_close(ntvfs->next, req, io);
+}
+
+/* operations on open files */
+NTSTATUS ntvfs_next_ioctl(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_ioctl *io)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->ioctl) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->ioctl(ntvfs->next, req, io);
+}
+
+NTSTATUS ntvfs_next_read(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_read *io)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->read) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->read(ntvfs->next, req, io);
+}
+
+NTSTATUS ntvfs_next_write(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_write *io)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->write) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->write(ntvfs->next, req, io);
+}
+
+NTSTATUS ntvfs_next_seek(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_seek *io)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->seek) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->seek(ntvfs->next, req, io);
+}
+
+NTSTATUS ntvfs_next_flush(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_flush *flush)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->flush) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->flush(ntvfs->next, req, flush);
+}
+
+NTSTATUS ntvfs_next_lock(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_lock *lck)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->lock) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->lock(ntvfs->next, req, lck);
+}
+
+NTSTATUS ntvfs_next_qfileinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_fileinfo *info)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->qfileinfo) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->qfileinfo(ntvfs->next, req, info);
+}
+
+NTSTATUS ntvfs_next_setfileinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_setfileinfo *info)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->setfileinfo) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->setfileinfo(ntvfs->next, req, info);
+}
+
+NTSTATUS ntvfs_next_close(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_close *io)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->close) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->close(ntvfs->next, req, io);
+}
+
+/* trans interface - used by IPC backend for pipes and RAP calls */
+NTSTATUS ntvfs_next_trans(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ struct smb_trans2 *trans)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->trans) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->trans(ntvfs->next, req, trans);
+}
+
+/* trans2 interface - only used by CIFS backend to prover complete passthru for testing */
+NTSTATUS ntvfs_next_trans2(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ struct smb_trans2 *trans2)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->trans2) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->trans2(ntvfs->next, req, trans2);
+}
+
+/*
+ change notify request
+*/
+NTSTATUS ntvfs_next_notify(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_notify *info)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->notify) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->notify(ntvfs->next, req, info);
+}
+
+/* cancel - called to cancel an outstanding async request */
+NTSTATUS ntvfs_next_cancel(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->cancel) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->cancel(ntvfs->next, req);
+}
+
+/* printing specific operations */
+NTSTATUS ntvfs_next_lpq(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_lpq *lpq)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->lpq) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->lpq(ntvfs->next, req, lpq);
+}
+
+
+/* logoff - called when a vuid is closed */
+NTSTATUS ntvfs_next_logoff(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->logoff) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->logoff(ntvfs->next, req);
+}
+
+NTSTATUS ntvfs_next_exit(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->exit) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->exit(ntvfs->next, req);
+}
+
+/* oplock helpers */
+NTSTATUS ntvfs_set_oplock_handler(struct ntvfs_context *ntvfs,
+ NTSTATUS (*handler)(void *private_data, struct ntvfs_handle *handle, uint8_t level),
+ void *private_data)
+{
+ ntvfs->oplock.handler = handler;
+ ntvfs->oplock.private_data = private_data;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS ntvfs_send_oplock_break(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_handle *handle, uint8_t level)
+{
+ if (!ntvfs->ctx->oplock.handler) {
+ return NT_STATUS_OK;
+ }
+
+ return ntvfs->ctx->oplock.handler(ntvfs->ctx->oplock.private_data, handle, level);
+}
+
+/* client connection callback */
+NTSTATUS ntvfs_set_addr_callbacks(struct ntvfs_context *ntvfs,
+ struct socket_address *(*my_addr)(void *private_data, TALLOC_CTX *mem_ctx),
+ struct socket_address *(*peer_addr)(void *private_data, TALLOC_CTX *mem_ctx),
+ void *private_data)
+{
+ ntvfs->client.get_peer_addr = my_addr;
+ ntvfs->client.get_my_addr = peer_addr;
+ ntvfs->client.private_data = private_data;
+ return NT_STATUS_OK;
+}
+
+struct socket_address *ntvfs_get_my_addr(struct ntvfs_module_context *ntvfs, TALLOC_CTX *mem_ctx)
+{
+ if (!ntvfs->ctx->client.get_my_addr) {
+ return NULL;
+ }
+
+ return ntvfs->ctx->client.get_my_addr(ntvfs->ctx->client.private_data, mem_ctx);
+}
+
+struct socket_address *ntvfs_get_peer_addr(struct ntvfs_module_context *ntvfs, TALLOC_CTX *mem_ctx)
+{
+ if (!ntvfs->ctx->client.get_peer_addr) {
+ return NULL;
+ }
+
+ return ntvfs->ctx->client.get_peer_addr(ntvfs->ctx->client.private_data, mem_ctx);
+}
diff --git a/source4/ntvfs/ntvfs_util.c b/source4/ntvfs/ntvfs_util.c
new file mode 100644
index 0000000000..1163139255
--- /dev/null
+++ b/source4/ntvfs/ntvfs_util.c
@@ -0,0 +1,202 @@
+/*
+ Unix SMB/CIFS implementation.
+ NTVFS utility code
+ Copyright (C) Stefan Metzmacher 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 <http://www.gnu.org/licenses/>.
+*/
+/*
+ this implements common utility functions that many NTVFS backends may wish to use
+*/
+
+#include "includes.h"
+#include "../lib/util/dlinklist.h"
+#include "ntvfs/ntvfs.h"
+
+
+struct ntvfs_request *ntvfs_request_create(struct ntvfs_context *ctx, TALLOC_CTX *mem_ctx,
+ struct auth_session_info *session_info,
+ uint16_t smbpid,
+ struct timeval request_time,
+ void *private_data,
+ void (*send_fn)(struct ntvfs_request *),
+ uint32_t state)
+{
+ struct ntvfs_request *req;
+ struct ntvfs_async_state *async;
+
+ req = talloc(mem_ctx, struct ntvfs_request);
+ if (!req) return NULL;
+ req->ctx = ctx;
+ req->async_states = NULL;
+ req->session_info = session_info;
+ req->smbpid = smbpid;
+ req->client_caps = ctx->client_caps;
+ req->statistics.request_time = request_time;
+
+ async = talloc(req, struct ntvfs_async_state);
+ if (!async) goto failed;
+
+ async->state = state;
+ async->private_data = private_data;
+ async->send_fn = send_fn;
+ async->status = NT_STATUS_INTERNAL_ERROR;
+ async->ntvfs = NULL;
+
+ DLIST_ADD(req->async_states, async);
+
+ return req;
+failed:
+ talloc_free(req);
+ return NULL;
+}
+
+NTSTATUS ntvfs_async_state_push(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ void *private_data,
+ void (*send_fn)(struct ntvfs_request *))
+{
+ struct ntvfs_async_state *async;
+
+ async = talloc(req, struct ntvfs_async_state);
+ NT_STATUS_HAVE_NO_MEMORY(async);
+
+ async->state = req->async_states->state;
+ async->private_data = private_data;
+ async->send_fn = send_fn;
+ async->status = NT_STATUS_INTERNAL_ERROR;
+
+ async->ntvfs = ntvfs;
+
+ DLIST_ADD(req->async_states, async);
+
+ return NT_STATUS_OK;
+}
+
+void ntvfs_async_state_pop(struct ntvfs_request *req)
+{
+ struct ntvfs_async_state *async;
+
+ async = req->async_states;
+
+ DLIST_REMOVE(req->async_states, async);
+
+ req->async_states->state = async->state;
+ req->async_states->status = async->status;
+
+ talloc_free(async);
+}
+
+NTSTATUS ntvfs_handle_new(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ struct ntvfs_handle **h)
+{
+ if (!ntvfs->ctx->handles.create_new) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ctx->handles.create_new(ntvfs->ctx->handles.private_data, req, h);
+}
+
+NTSTATUS ntvfs_handle_set_backend_data(struct ntvfs_handle *h,
+ struct ntvfs_module_context *ntvfs,
+ TALLOC_CTX *private_data)
+{
+ struct ntvfs_handle_data *d;
+ bool first_time = h->backend_data?false:true;
+
+ for (d=h->backend_data; d; d = d->next) {
+ if (d->owner != ntvfs) continue;
+ d->private_data = talloc_steal(d, private_data);
+ return NT_STATUS_OK;
+ }
+
+ d = talloc(h, struct ntvfs_handle_data);
+ NT_STATUS_HAVE_NO_MEMORY(d);
+ d->owner = ntvfs;
+ d->private_data = talloc_steal(d, private_data);
+
+ DLIST_ADD(h->backend_data, d);
+
+ if (first_time) {
+ NTSTATUS status;
+ status = h->ctx->handles.make_valid(h->ctx->handles.private_data, h);
+ NT_STATUS_NOT_OK_RETURN(status);
+ }
+
+ return NT_STATUS_OK;
+}
+
+void *ntvfs_handle_get_backend_data(struct ntvfs_handle *h,
+ struct ntvfs_module_context *ntvfs)
+{
+ struct ntvfs_handle_data *d;
+
+ for (d=h->backend_data; d; d = d->next) {
+ if (d->owner != ntvfs) continue;
+ return d->private_data;
+ }
+
+ return NULL;
+}
+
+void ntvfs_handle_remove_backend_data(struct ntvfs_handle *h,
+ struct ntvfs_module_context *ntvfs)
+{
+ struct ntvfs_handle_data *d,*n;
+
+ for (d=h->backend_data; d; d = n) {
+ n = d->next;
+ if (d->owner != ntvfs) continue;
+ DLIST_REMOVE(h->backend_data, d);
+ talloc_free(d);
+ d = NULL;
+ }
+
+ if (h->backend_data) return;
+
+ /* if there's no backend_data anymore, destroy the handle */
+ h->ctx->handles.destroy(h->ctx->handles.private_data, h);
+}
+
+struct ntvfs_handle *ntvfs_handle_search_by_wire_key(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ const DATA_BLOB *key)
+{
+ if (!ntvfs->ctx->handles.search_by_wire_key) {
+ return NULL;
+ }
+ return ntvfs->ctx->handles.search_by_wire_key(ntvfs->ctx->handles.private_data, req, key);
+}
+
+DATA_BLOB ntvfs_handle_get_wire_key(struct ntvfs_handle *h, TALLOC_CTX *mem_ctx)
+{
+ return h->ctx->handles.get_wire_key(h->ctx->handles.private_data, h, mem_ctx);
+}
+
+NTSTATUS ntvfs_set_handle_callbacks(struct ntvfs_context *ntvfs,
+ NTSTATUS (*create_new)(void *private_data, struct ntvfs_request *req, struct ntvfs_handle **h),
+ NTSTATUS (*make_valid)(void *private_data, struct ntvfs_handle *h),
+ void (*destroy)(void *private_data, struct ntvfs_handle *h),
+ struct ntvfs_handle *(*search_by_wire_key)(void *private_data, struct ntvfs_request *req, const DATA_BLOB *key),
+ DATA_BLOB (*get_wire_key)(void *private_data, struct ntvfs_handle *handle, TALLOC_CTX *mem_ctx),
+ void *private_data)
+{
+ ntvfs->handles.create_new = create_new;
+ ntvfs->handles.make_valid = make_valid;
+ ntvfs->handles.destroy = destroy;
+ ntvfs->handles.search_by_wire_key = search_by_wire_key;
+ ntvfs->handles.get_wire_key = get_wire_key;
+ ntvfs->handles.private_data = private_data;
+ return NT_STATUS_OK;
+}
diff --git a/source4/ntvfs/posix/config.m4 b/source4/ntvfs/posix/config.m4
new file mode 100644
index 0000000000..a6f79dfbb3
--- /dev/null
+++ b/source4/ntvfs/posix/config.m4
@@ -0,0 +1,36 @@
+
+
+dnl #############################################
+dnl see if we have nanosecond resolution for stat
+AC_CACHE_CHECK([for tv_nsec nanosecond fields in struct stat],ac_cv_have_stat_tv_nsec,[
+AC_TRY_COMPILE(
+[
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+],
+[struct stat st;
+ st.st_mtim.tv_nsec;
+ st.st_atim.tv_nsec;
+ st.st_ctim.tv_nsec;
+],
+ac_cv_decl_have_stat_tv_nsec=yes,
+ac_cv_decl_have_stat_tv_nsec=no)
+])
+if test x"$ac_cv_decl_have_stat_tv_nsec" = x"yes"; then
+ AC_DEFINE(HAVE_STAT_TV_NSEC,1,[Whether stat has tv_nsec nanosecond fields])
+fi
+
+AC_CHECK_HEADERS(blkid/blkid.h)
+AC_SEARCH_LIBS_EXT(blkid_get_cache, [blkid], BLKID_LIBS)
+AC_CHECK_FUNC_EXT(blkid_get_cache, $BLKID_LIBS)
+SMB_EXT_LIB(BLKID,[${BLKID_LIBS}],[${BLKID_CFLAGS}],[${BLKID_CPPFLAGS}],[${BLKID_LDFLAGS}])
+if test x"$ac_cv_func_ext_blkid_get_cache" = x"yes"; then
+ AC_DEFINE(HAVE_LIBBLKID,1,[Whether we have blkid support (e2fsprogs)])
+ SMB_ENABLE(BLKID,YES)
+fi
+
+SMB_ENABLE(pvfs_aio,NO)
+if test x"$tevent_cv_aio_support" = x"yes"; then
+ SMB_ENABLE(pvfs_aio,YES)
+fi
diff --git a/source4/ntvfs/posix/config.mk b/source4/ntvfs/posix/config.mk
new file mode 100644
index 0000000000..1d7949214a
--- /dev/null
+++ b/source4/ntvfs/posix/config.mk
@@ -0,0 +1,79 @@
+
+[SUBSYSTEM::pvfs_acl]
+
+pvfs_acl_OBJ_FILES = $(ntvfssrcdir)/posix/pvfs_acl.o
+
+$(eval $(call proto_header_template,$(ntvfssrcdir)/posix/vfs_acl_proto.h,$(pvfs_acl_OBJ_FILES:.o=.c)))
+
+################################################
+# Start MODULE pvfs_acl_xattr
+[MODULE::pvfs_acl_xattr]
+INIT_FUNCTION = pvfs_acl_xattr_init
+SUBSYSTEM = pvfs_acl
+PRIVATE_DEPENDENCIES = NDR_XATTR
+# End MODULE pvfs_acl_xattr
+################################################
+
+pvfs_acl_xattr_OBJ_FILES = $(ntvfssrcdir)/posix/pvfs_acl_xattr.o
+
+################################################
+# Start MODULE pvfs_acl_nfs4
+[MODULE::pvfs_acl_nfs4]
+INIT_FUNCTION = pvfs_acl_nfs4_init
+SUBSYSTEM = pvfs_acl
+PRIVATE_DEPENDENCIES = NDR_NFS4ACL SAMDB
+# End MODULE pvfs_acl_nfs4
+################################################
+
+pvfs_acl_nfs4_OBJ_FILES = $(ntvfssrcdir)/posix/pvfs_acl_nfs4.o
+
+################################################
+[SUBSYSTEM::pvfs_aio]
+PRIVATE_DEPENDENCIES = LIBTEVENT LIBTEVENT_EXT
+################################################
+
+pvfs_aio_OBJ_FILES = $(ntvfssrcdir)/posix/pvfs_aio.o
+
+################################################
+# Start MODULE ntvfs_posix
+[MODULE::ntvfs_posix]
+SUBSYSTEM = ntvfs
+OUTPUT_TYPE = MERGED_OBJ
+INIT_FUNCTION = ntvfs_posix_init
+#PRIVATE_DEPENDENCIES = pvfs_acl_xattr pvfs_acl_nfs4
+PRIVATE_DEPENDENCIES = NDR_XATTR WRAP_XATTR BLKID ntvfs_common MESSAGING \
+ LIBWBCLIENT pvfs_acl pvfs_aio
+# End MODULE ntvfs_posix
+################################################
+
+ntvfs_posix_OBJ_FILES = $(addprefix $(ntvfssrcdir)/posix/, \
+ vfs_posix.o \
+ pvfs_util.o \
+ pvfs_search.o \
+ pvfs_dirlist.o \
+ pvfs_fileinfo.o \
+ pvfs_unlink.o \
+ pvfs_mkdir.o \
+ pvfs_open.o \
+ pvfs_read.o \
+ pvfs_flush.o \
+ pvfs_write.o \
+ pvfs_fsinfo.o \
+ pvfs_qfileinfo.o \
+ pvfs_setfileinfo.o \
+ pvfs_rename.o \
+ pvfs_resolve.o \
+ pvfs_shortname.o \
+ pvfs_lock.o \
+ pvfs_oplock.o \
+ pvfs_wait.o \
+ pvfs_seek.o \
+ pvfs_ioctl.o \
+ pvfs_xattr.o \
+ pvfs_streams.o \
+ pvfs_notify.o \
+ xattr_system.o \
+ xattr_tdb.o)
+
+$(eval $(call proto_header_template,$(ntvfssrcdir)/posix/vfs_posix_proto.h,$(ntvfs_posix_OBJ_FILES:.o=.c)))
+
diff --git a/source4/ntvfs/posix/pvfs_acl.c b/source4/ntvfs/posix/pvfs_acl.c
new file mode 100644
index 0000000000..1adced44aa
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_acl.c
@@ -0,0 +1,851 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - ACL support
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "auth/auth.h"
+#include "vfs_posix.h"
+#include "librpc/gen_ndr/xattr.h"
+#include "libcli/security/security.h"
+#include "param/param.h"
+
+
+/* the list of currently registered ACL backends */
+static struct pvfs_acl_backend {
+ const struct pvfs_acl_ops *ops;
+} *backends = NULL;
+static int num_backends;
+
+/*
+ register a pvfs acl backend.
+
+ The 'name' can be later used by other backends to find the operations
+ structure for this backend.
+*/
+NTSTATUS pvfs_acl_register(const struct pvfs_acl_ops *ops)
+{
+ struct pvfs_acl_ops *new_ops;
+
+ if (pvfs_acl_backend_byname(ops->name) != NULL) {
+ DEBUG(0,("pvfs acl backend '%s' already registered\n", ops->name));
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ backends = talloc_realloc(talloc_autofree_context(), backends, struct pvfs_acl_backend, num_backends+1);
+ NT_STATUS_HAVE_NO_MEMORY(backends);
+
+ new_ops = (struct pvfs_acl_ops *)talloc_memdup(backends, ops, sizeof(*ops));
+ new_ops->name = talloc_strdup(new_ops, ops->name);
+
+ backends[num_backends].ops = new_ops;
+
+ num_backends++;
+
+ DEBUG(3,("NTVFS backend '%s' registered\n", ops->name));
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ return the operations structure for a named backend
+*/
+const struct pvfs_acl_ops *pvfs_acl_backend_byname(const char *name)
+{
+ int i;
+
+ for (i=0;i<num_backends;i++) {
+ if (strcmp(backends[i].ops->name, name) == 0) {
+ return backends[i].ops;
+ }
+ }
+
+ return NULL;
+}
+
+NTSTATUS pvfs_acl_init(struct loadparm_context *lp_ctx)
+{
+ static bool initialized = false;
+ extern NTSTATUS pvfs_acl_nfs4_init(void);
+ extern NTSTATUS pvfs_acl_xattr_init(void);
+ init_module_fn static_init[] = { STATIC_pvfs_acl_MODULES };
+ init_module_fn *shared_init;
+
+ if (initialized) return NT_STATUS_OK;
+ initialized = true;
+
+ shared_init = load_samba_modules(NULL, lp_ctx, "pvfs_acl");
+
+ run_init_functions(static_init);
+ run_init_functions(shared_init);
+
+ talloc_free(shared_init);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ map a single access_mask from generic to specific bits for files/dirs
+*/
+static uint32_t pvfs_translate_mask(uint32_t access_mask)
+{
+ if (access_mask & SEC_MASK_GENERIC) {
+ if (access_mask & SEC_GENERIC_READ) access_mask |= SEC_RIGHTS_FILE_READ;
+ if (access_mask & SEC_GENERIC_WRITE) access_mask |= SEC_RIGHTS_FILE_WRITE;
+ if (access_mask & SEC_GENERIC_EXECUTE) access_mask |= SEC_RIGHTS_FILE_EXECUTE;
+ if (access_mask & SEC_GENERIC_ALL) access_mask |= SEC_RIGHTS_FILE_ALL;
+ access_mask &= ~SEC_MASK_GENERIC;
+ }
+ return access_mask;
+}
+
+
+/*
+ map any generic access bits in the given acl
+ this relies on the fact that the mappings for files and directories
+ are the same
+*/
+static void pvfs_translate_generic_bits(struct security_acl *acl)
+{
+ unsigned i;
+
+ if (!acl) return;
+
+ for (i=0;i<acl->num_aces;i++) {
+ struct security_ace *ace = &acl->aces[i];
+ ace->access_mask = pvfs_translate_mask(ace->access_mask);
+ }
+}
+
+
+/*
+ setup a default ACL for a file
+*/
+static NTSTATUS pvfs_default_acl(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
+ struct pvfs_filename *name, int fd,
+ struct security_descriptor **psd)
+{
+ struct security_descriptor *sd;
+ NTSTATUS status;
+ struct security_ace ace;
+ mode_t mode;
+ struct id_mapping *ids;
+ struct composite_context *ctx;
+
+ *psd = security_descriptor_initialise(req);
+ if (*psd == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ sd = *psd;
+
+ ids = talloc_zero_array(sd, struct id_mapping, 2);
+ NT_STATUS_HAVE_NO_MEMORY(ids);
+
+ ids[0].unixid = talloc(ids, struct unixid);
+ NT_STATUS_HAVE_NO_MEMORY(ids[0].unixid);
+
+ ids[0].unixid->id = name->st.st_uid;
+ ids[0].unixid->type = ID_TYPE_UID;
+ ids[0].sid = NULL;
+
+ ids[1].unixid = talloc(ids, struct unixid);
+ NT_STATUS_HAVE_NO_MEMORY(ids[1].unixid);
+
+ ids[1].unixid->id = name->st.st_gid;
+ ids[1].unixid->type = ID_TYPE_GID;
+ ids[1].sid = NULL;
+
+ ctx = wbc_xids_to_sids_send(pvfs->wbc_ctx, ids, 2, ids);
+ NT_STATUS_HAVE_NO_MEMORY(ctx);
+
+ status = wbc_xids_to_sids_recv(ctx, &ids);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ sd->owner_sid = talloc_steal(sd, ids[0].sid);
+ sd->group_sid = talloc_steal(sd, ids[1].sid);
+
+ talloc_free(ids);
+ sd->type |= SEC_DESC_DACL_PRESENT;
+
+ mode = name->st.st_mode;
+
+ /*
+ we provide up to 4 ACEs
+ - Owner
+ - Group
+ - Everyone
+ - Administrator
+ */
+
+
+ /* setup owner ACE */
+ ace.type = SEC_ACE_TYPE_ACCESS_ALLOWED;
+ ace.flags = 0;
+ ace.trustee = *sd->owner_sid;
+ ace.access_mask = 0;
+
+ if (mode & S_IRUSR) {
+ if (mode & S_IWUSR) {
+ ace.access_mask |= SEC_RIGHTS_FILE_ALL;
+ } else {
+ ace.access_mask |= SEC_RIGHTS_FILE_READ | SEC_FILE_EXECUTE;
+ }
+ }
+ if (mode & S_IWUSR) {
+ ace.access_mask |= SEC_RIGHTS_FILE_WRITE | SEC_STD_DELETE;
+ }
+ if (ace.access_mask) {
+ security_descriptor_dacl_add(sd, &ace);
+ }
+
+
+ /* setup group ACE */
+ ace.trustee = *sd->group_sid;
+ ace.access_mask = 0;
+ if (mode & S_IRGRP) {
+ ace.access_mask |= SEC_RIGHTS_FILE_READ | SEC_FILE_EXECUTE;
+ }
+ if (mode & S_IWGRP) {
+ /* note that delete is not granted - this matches posix behaviour */
+ ace.access_mask |= SEC_RIGHTS_FILE_WRITE;
+ }
+ if (ace.access_mask) {
+ security_descriptor_dacl_add(sd, &ace);
+ }
+
+ /* setup other ACE */
+ ace.trustee = *dom_sid_parse_talloc(req, SID_WORLD);
+ ace.access_mask = 0;
+ if (mode & S_IROTH) {
+ ace.access_mask |= SEC_RIGHTS_FILE_READ | SEC_FILE_EXECUTE;
+ }
+ if (mode & S_IWOTH) {
+ ace.access_mask |= SEC_RIGHTS_FILE_WRITE;
+ }
+ if (ace.access_mask) {
+ security_descriptor_dacl_add(sd, &ace);
+ }
+
+ /* setup system ACE */
+ ace.trustee = *dom_sid_parse_talloc(req, SID_NT_SYSTEM);
+ ace.access_mask = SEC_RIGHTS_FILE_ALL;
+ security_descriptor_dacl_add(sd, &ace);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ omit any security_descriptor elements not specified in the given
+ secinfo flags
+*/
+static void normalise_sd_flags(struct security_descriptor *sd, uint32_t secinfo_flags)
+{
+ if (!(secinfo_flags & SECINFO_OWNER)) {
+ sd->owner_sid = NULL;
+ }
+ if (!(secinfo_flags & SECINFO_GROUP)) {
+ sd->group_sid = NULL;
+ }
+ if (!(secinfo_flags & SECINFO_DACL)) {
+ sd->dacl = NULL;
+ }
+ if (!(secinfo_flags & SECINFO_SACL)) {
+ sd->sacl = NULL;
+ }
+}
+
+/*
+ answer a setfileinfo for an ACL
+*/
+NTSTATUS pvfs_acl_set(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
+ struct pvfs_filename *name, int fd,
+ uint32_t access_mask,
+ union smb_setfileinfo *info)
+{
+ uint32_t secinfo_flags = info->set_secdesc.in.secinfo_flags;
+ struct security_descriptor *new_sd, *sd, orig_sd;
+ NTSTATUS status = NT_STATUS_NOT_FOUND;
+ uid_t old_uid = -1;
+ gid_t old_gid = -1;
+ uid_t new_uid = -1;
+ gid_t new_gid = -1;
+ struct id_mapping *ids;
+ struct composite_context *ctx;
+
+ if (pvfs->acl_ops != NULL) {
+ status = pvfs->acl_ops->acl_load(pvfs, name, fd, req, &sd);
+ }
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ status = pvfs_default_acl(pvfs, req, name, fd, &sd);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ ids = talloc(req, struct id_mapping);
+ NT_STATUS_HAVE_NO_MEMORY(ids);
+ ids->unixid = NULL;
+ ids->sid = NULL;
+ ids->status = NT_STATUS_NONE_MAPPED;
+
+ new_sd = info->set_secdesc.in.sd;
+ orig_sd = *sd;
+
+ old_uid = name->st.st_uid;
+ old_gid = name->st.st_gid;
+
+ /* only set the elements that have been specified */
+ if (secinfo_flags & SECINFO_OWNER) {
+ if (!(access_mask & SEC_STD_WRITE_OWNER)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ if (!dom_sid_equal(sd->owner_sid, new_sd->owner_sid)) {
+ ids->sid = new_sd->owner_sid;
+ ctx = wbc_sids_to_xids_send(pvfs->wbc_ctx, ids, 1, ids);
+ NT_STATUS_HAVE_NO_MEMORY(ctx);
+ status = wbc_sids_to_xids_recv(ctx, &ids);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ if (ids->unixid->type == ID_TYPE_BOTH ||
+ ids->unixid->type == ID_TYPE_UID) {
+ new_uid = ids->unixid->id;
+ }
+ }
+ sd->owner_sid = new_sd->owner_sid;
+ }
+ if (secinfo_flags & SECINFO_GROUP) {
+ if (!(access_mask & SEC_STD_WRITE_OWNER)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ if (!dom_sid_equal(sd->group_sid, new_sd->group_sid)) {
+ ids->sid = new_sd->group_sid;
+ ctx = wbc_sids_to_xids_send(pvfs->wbc_ctx, ids, 1, ids);
+ NT_STATUS_HAVE_NO_MEMORY(ctx);
+ status = wbc_sids_to_xids_recv(ctx, &ids);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ if (ids->unixid->type == ID_TYPE_BOTH ||
+ ids->unixid->type == ID_TYPE_GID) {
+ new_gid = ids->unixid->id;
+ }
+
+ }
+ sd->group_sid = new_sd->group_sid;
+ }
+ if (secinfo_flags & SECINFO_DACL) {
+ if (!(access_mask & SEC_STD_WRITE_DAC)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ sd->dacl = new_sd->dacl;
+ pvfs_translate_generic_bits(sd->dacl);
+ }
+ if (secinfo_flags & SECINFO_SACL) {
+ if (!(access_mask & SEC_FLAG_SYSTEM_SECURITY)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ sd->sacl = new_sd->sacl;
+ pvfs_translate_generic_bits(sd->sacl);
+ }
+
+ if (new_uid == old_uid) {
+ new_uid = -1;
+ }
+
+ if (new_gid == old_gid) {
+ new_gid = -1;
+ }
+
+ /* if there's something to change try it */
+ if (new_uid != -1 || new_gid != -1) {
+ int ret;
+ if (fd == -1) {
+ ret = chown(name->full_name, new_uid, new_gid);
+ } else {
+ ret = fchown(fd, new_uid, new_gid);
+ }
+ if (ret == -1) {
+ return pvfs_map_errno(pvfs, errno);
+ }
+ }
+
+ /* we avoid saving if the sd is the same. This means when clients
+ copy files and end up copying the default sd that we don't
+ needlessly use xattrs */
+ if (!security_descriptor_equal(sd, &orig_sd) && pvfs->acl_ops) {
+ status = pvfs->acl_ops->acl_save(pvfs, name, fd, sd);
+ }
+
+ return status;
+}
+
+
+/*
+ answer a fileinfo query for the ACL
+*/
+NTSTATUS pvfs_acl_query(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
+ struct pvfs_filename *name, int fd,
+ union smb_fileinfo *info)
+{
+ NTSTATUS status = NT_STATUS_NOT_FOUND;
+ struct security_descriptor *sd;
+
+ if (pvfs->acl_ops) {
+ status = pvfs->acl_ops->acl_load(pvfs, name, fd, req, &sd);
+ }
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ status = pvfs_default_acl(pvfs, req, name, fd, &sd);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ normalise_sd_flags(sd, info->query_secdesc.in.secinfo_flags);
+
+ info->query_secdesc.out.sd = sd;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ check the read only bit against any of the write access bits
+*/
+static bool pvfs_read_only(struct pvfs_state *pvfs, uint32_t access_mask)
+{
+ if ((pvfs->flags & PVFS_FLAG_READONLY) &&
+ (access_mask & (SEC_FILE_WRITE_DATA |
+ SEC_FILE_APPEND_DATA |
+ SEC_FILE_WRITE_EA |
+ SEC_FILE_WRITE_ATTRIBUTE |
+ SEC_STD_DELETE |
+ SEC_STD_WRITE_DAC |
+ SEC_STD_WRITE_OWNER |
+ SEC_DIR_DELETE_CHILD))) {
+ return true;
+ }
+ return false;
+}
+
+/*
+ default access check function based on unix permissions
+ doing this saves on building a full security descriptor
+ for the common case of access check on files with no
+ specific NT ACL
+*/
+NTSTATUS pvfs_access_check_unix(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
+ struct pvfs_filename *name,
+ uint32_t *access_mask)
+{
+ uid_t uid = geteuid();
+ uint32_t max_bits = SEC_RIGHTS_FILE_READ | SEC_FILE_ALL;
+
+ if (pvfs_read_only(pvfs, *access_mask)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* owner and root get extra permissions */
+ if (uid == 0) {
+ max_bits |= SEC_STD_ALL | SEC_FLAG_SYSTEM_SECURITY;
+ } else if (uid == name->st.st_uid) {
+ max_bits |= SEC_STD_ALL;
+ }
+
+ if (*access_mask == SEC_FLAG_MAXIMUM_ALLOWED) {
+ *access_mask = max_bits;
+ return NT_STATUS_OK;
+ }
+
+ if (uid != 0 && (*access_mask & SEC_FLAG_SYSTEM_SECURITY)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (*access_mask & ~max_bits) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (pvfs->ntvfs->ctx->protocol != PROTOCOL_SMB2) {
+ /* on SMB, this bit is always granted, even if not
+ asked for */
+ *access_mask |= SEC_FILE_READ_ATTRIBUTE;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ check the security descriptor on a file, if any
+
+ *access_mask is modified with the access actually granted
+*/
+NTSTATUS pvfs_access_check(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
+ struct pvfs_filename *name,
+ uint32_t *access_mask)
+{
+ struct security_token *token = req->session_info->security_token;
+ struct xattr_NTACL *acl;
+ NTSTATUS status;
+ struct security_descriptor *sd;
+
+ /* on SMB2 a blank access mask is always denied */
+ if (pvfs->ntvfs->ctx->protocol == PROTOCOL_SMB2 &&
+ *access_mask == 0) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (pvfs_read_only(pvfs, *access_mask)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ acl = talloc(req, struct xattr_NTACL);
+ if (acl == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* expand the generic access bits to file specific bits */
+ *access_mask = pvfs_translate_mask(*access_mask);
+ if (pvfs->ntvfs->ctx->protocol != PROTOCOL_SMB2) {
+ *access_mask &= ~SEC_FILE_READ_ATTRIBUTE;
+ }
+
+ status = pvfs_acl_load(pvfs, name, -1, acl);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ talloc_free(acl);
+ return pvfs_access_check_unix(pvfs, req, name, access_mask);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ switch (acl->version) {
+ case 1:
+ sd = acl->info.sd;
+ break;
+ default:
+ return NT_STATUS_INVALID_ACL;
+ }
+
+ /* check the acl against the required access mask */
+ status = sec_access_check(sd, token, *access_mask, access_mask);
+
+ if (pvfs->ntvfs->ctx->protocol != PROTOCOL_SMB2) {
+ /* on SMB, this bit is always granted, even if not
+ asked for */
+ *access_mask |= SEC_FILE_READ_ATTRIBUTE;
+ }
+
+ talloc_free(acl);
+
+ return status;
+}
+
+
+/*
+ a simplified interface to access check, designed for calls that
+ do not take or return an access check mask
+*/
+NTSTATUS pvfs_access_check_simple(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
+ struct pvfs_filename *name,
+ uint32_t access_needed)
+{
+ if (access_needed == 0) {
+ return NT_STATUS_OK;
+ }
+ return pvfs_access_check(pvfs, req, name, &access_needed);
+}
+
+/*
+ access check for creating a new file/directory
+*/
+NTSTATUS pvfs_access_check_create(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
+ struct pvfs_filename *name,
+ uint32_t *access_mask)
+{
+ struct pvfs_filename *parent;
+ NTSTATUS status;
+
+ status = pvfs_resolve_parent(pvfs, req, name, &parent);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = pvfs_access_check(pvfs, req, parent, access_mask);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (! ((*access_mask) & SEC_DIR_ADD_FILE)) {
+ return pvfs_access_check_simple(pvfs, req, parent, SEC_DIR_ADD_FILE);
+ }
+
+ return status;
+}
+
+/*
+ access check for creating a new file/directory - no access mask supplied
+*/
+NTSTATUS pvfs_access_check_parent(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
+ struct pvfs_filename *name,
+ uint32_t access_mask)
+{
+ struct pvfs_filename *parent;
+ NTSTATUS status;
+
+ status = pvfs_resolve_parent(pvfs, req, name, &parent);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return pvfs_access_check_simple(pvfs, req, parent, access_mask);
+}
+
+
+/*
+ determine if an ACE is inheritable
+*/
+static bool pvfs_inheritable_ace(struct pvfs_state *pvfs,
+ const struct security_ace *ace,
+ bool container)
+{
+ if (!container) {
+ return (ace->flags & SEC_ACE_FLAG_OBJECT_INHERIT) != 0;
+ }
+
+ if (ace->flags & SEC_ACE_FLAG_CONTAINER_INHERIT) {
+ return true;
+ }
+
+ if ((ace->flags & SEC_ACE_FLAG_OBJECT_INHERIT) &&
+ !(ace->flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT)) {
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ this is the core of ACL inheritance. It copies any inheritable
+ aces from the parent SD to the child SD. Note that the algorithm
+ depends on whether the child is a container or not
+*/
+static NTSTATUS pvfs_acl_inherit_aces(struct pvfs_state *pvfs,
+ struct security_descriptor *parent_sd,
+ struct security_descriptor *sd,
+ bool container)
+{
+ int i;
+
+ for (i=0;i<parent_sd->dacl->num_aces;i++) {
+ struct security_ace ace = parent_sd->dacl->aces[i];
+ NTSTATUS status;
+ const struct dom_sid *creator = NULL, *new_id = NULL;
+ uint32_t orig_flags;
+
+ if (!pvfs_inheritable_ace(pvfs, &ace, container)) {
+ continue;
+ }
+
+ orig_flags = ace.flags;
+
+ /* see the RAW-ACLS inheritance test for details on these rules */
+ if (!container) {
+ ace.flags = 0;
+ } else {
+ ace.flags &= ~SEC_ACE_FLAG_INHERIT_ONLY;
+
+ if (!(ace.flags & SEC_ACE_FLAG_CONTAINER_INHERIT)) {
+ ace.flags |= SEC_ACE_FLAG_INHERIT_ONLY;
+ }
+ if (ace.flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT) {
+ ace.flags = 0;
+ }
+ }
+
+ /* the CREATOR sids are special when inherited */
+ if (dom_sid_equal(&ace.trustee, pvfs->sid_cache.creator_owner)) {
+ creator = pvfs->sid_cache.creator_owner;
+ new_id = sd->owner_sid;
+ } else if (dom_sid_equal(&ace.trustee, pvfs->sid_cache.creator_group)) {
+ creator = pvfs->sid_cache.creator_group;
+ new_id = sd->group_sid;
+ } else {
+ new_id = &ace.trustee;
+ }
+
+ if (creator && container &&
+ (ace.flags & SEC_ACE_FLAG_CONTAINER_INHERIT)) {
+ uint32_t flags = ace.flags;
+
+ ace.trustee = *new_id;
+ ace.flags = 0;
+ status = security_descriptor_dacl_add(sd, &ace);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ ace.trustee = *creator;
+ ace.flags = flags | SEC_ACE_FLAG_INHERIT_ONLY;
+ status = security_descriptor_dacl_add(sd, &ace);
+ } else if (container &&
+ !(orig_flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT)) {
+ status = security_descriptor_dacl_add(sd, &ace);
+ } else {
+ ace.trustee = *new_id;
+ status = security_descriptor_dacl_add(sd, &ace);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+
+/*
+ setup an ACL on a new file/directory based on the inherited ACL from
+ the parent. If there is no inherited ACL then we don't set anything,
+ as the default ACL applies anyway
+*/
+NTSTATUS pvfs_acl_inherit(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
+ struct pvfs_filename *name,
+ int fd)
+{
+ struct xattr_NTACL *acl;
+ NTSTATUS status;
+ struct pvfs_filename *parent;
+ struct security_descriptor *parent_sd, *sd;
+ bool container;
+ struct id_mapping *ids;
+ struct composite_context *ctx;
+
+ /* form the parents path */
+ status = pvfs_resolve_parent(pvfs, req, name, &parent);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ acl = talloc(req, struct xattr_NTACL);
+ if (acl == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = pvfs_acl_load(pvfs, parent, -1, acl);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ return NT_STATUS_OK;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ switch (acl->version) {
+ case 1:
+ parent_sd = acl->info.sd;
+ break;
+ default:
+ return NT_STATUS_INVALID_ACL;
+ }
+
+ if (parent_sd == NULL ||
+ parent_sd->dacl == NULL ||
+ parent_sd->dacl->num_aces == 0) {
+ /* go with the default ACL */
+ return NT_STATUS_OK;
+ }
+
+ /* create the new sd */
+ sd = security_descriptor_initialise(req);
+ if (sd == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ids = talloc_array(sd, struct id_mapping, 2);
+ NT_STATUS_HAVE_NO_MEMORY(ids);
+
+ ids[0].unixid = talloc(ids, struct unixid);
+ NT_STATUS_HAVE_NO_MEMORY(ids[0].unixid);
+ ids[0].unixid->id = name->st.st_uid;
+ ids[0].unixid->type = ID_TYPE_UID;
+ ids[0].sid = NULL;
+ ids[0].status = NT_STATUS_NONE_MAPPED;
+
+ ids[1].unixid = talloc(ids, struct unixid);
+ NT_STATUS_HAVE_NO_MEMORY(ids[1].unixid);
+ ids[1].unixid->id = name->st.st_gid;
+ ids[1].unixid->type = ID_TYPE_GID;
+ ids[1].sid = NULL;
+ ids[1].status = NT_STATUS_NONE_MAPPED;
+
+ ctx = wbc_xids_to_sids_send(pvfs->wbc_ctx, ids, 2, ids);
+ NT_STATUS_HAVE_NO_MEMORY(ctx);
+
+ status = wbc_xids_to_sids_recv(ctx, &ids);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ sd->owner_sid = talloc_steal(sd, ids[0].sid);
+ sd->group_sid = talloc_steal(sd, ids[1].sid);
+
+ sd->type |= SEC_DESC_DACL_PRESENT;
+
+ container = (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) ? true:false;
+
+ /* fill in the aces from the parent */
+ status = pvfs_acl_inherit_aces(pvfs, parent_sd, sd, container);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* if there is nothing to inherit then we fallback to the
+ default acl */
+ if (sd->dacl == NULL || sd->dacl->num_aces == 0) {
+ return NT_STATUS_OK;
+ }
+
+ acl->info.sd = sd;
+
+ status = pvfs_acl_save(pvfs, name, fd, acl);
+
+ return status;
+}
+
+/*
+ return the maximum allowed access mask
+*/
+NTSTATUS pvfs_access_maximal_allowed(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
+ struct pvfs_filename *name,
+ uint32_t *maximal_access)
+{
+ *maximal_access = SEC_FLAG_MAXIMUM_ALLOWED;
+ return pvfs_access_check(pvfs, req, name, maximal_access);
+}
diff --git a/source4/ntvfs/posix/pvfs_acl_nfs4.c b/source4/ntvfs/posix/pvfs_acl_nfs4.c
new file mode 100644
index 0000000000..02ed058af7
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_acl_nfs4.c
@@ -0,0 +1,212 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - NT ACLs mapped to NFS4 ACLs, as per
+ http://www.suse.de/~agruen/nfs4acl/
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "vfs_posix.h"
+#include "../lib/util/unix_privs.h"
+#include "librpc/gen_ndr/ndr_nfs4acl.h"
+#include "libcli/security/security.h"
+
+#define ACE4_IDENTIFIER_GROUP 0x40
+
+/*
+ load the current ACL from system.nfs4acl
+*/
+static NTSTATUS pvfs_acl_load_nfs4(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
+ TALLOC_CTX *mem_ctx,
+ struct security_descriptor **psd)
+{
+ NTSTATUS status;
+ struct nfs4acl *acl;
+ struct security_descriptor *sd;
+ int i, num_ids;
+ struct id_mapping *ids;
+ struct composite_context *ctx;
+
+ acl = talloc_zero(mem_ctx, struct nfs4acl);
+ NT_STATUS_HAVE_NO_MEMORY(acl);
+
+ status = pvfs_xattr_ndr_load(pvfs, mem_ctx, name->full_name, fd,
+ NFS4ACL_XATTR_NAME,
+ acl, ndr_pull_nfs4acl);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(acl);
+ return status;
+ }
+
+ *psd = security_descriptor_initialise(mem_ctx);
+ NT_STATUS_HAVE_NO_MEMORY(*psd);
+
+ sd = *psd;
+
+ sd->type |= acl->a_flags;
+
+ /* the number of ids to map is the acl count plus uid and gid */
+ num_ids = acl->a_count +2;
+ ids = talloc_array(sd, struct id_mapping, num_ids);
+ NT_STATUS_HAVE_NO_MEMORY(ids);
+
+ ids[0].unixid = talloc(ids, struct unixid);
+ NT_STATUS_HAVE_NO_MEMORY(ids[0].unixid);
+ ids[0].unixid->id = name->st.st_uid;
+ ids[0].unixid->type = ID_TYPE_UID;
+ ids[0].sid = NULL;
+ ids[0].status = NT_STATUS_NONE_MAPPED;
+
+ ids[1].unixid = talloc(ids, struct unixid);
+ NT_STATUS_HAVE_NO_MEMORY(ids[1].unixid);
+ ids[1].unixid->id = name->st.st_gid;
+ ids[1].unixid->type = ID_TYPE_GID;
+ ids[1].sid = NULL;
+ ids[1].status = NT_STATUS_NONE_MAPPED;
+
+ for (i=0;i<acl->a_count;i++) {
+ struct nfs4ace *a = &acl->ace[i];
+ ids[i+2].unixid = talloc(ids, struct unixid);
+ NT_STATUS_HAVE_NO_MEMORY(ids[i+2].unixid);
+ ids[i+2].unixid->id = a->e_id;
+ if (a->e_flags & ACE4_IDENTIFIER_GROUP) {
+ ids[i+2].unixid->type = ID_TYPE_GID;
+ } else {
+ ids[i+2].unixid->type = ID_TYPE_UID;
+ }
+ ids[i+2].sid = NULL;
+ ids[i+2].status = NT_STATUS_NONE_MAPPED;
+ }
+
+ /* Allocate memory for the sids from the security descriptor to be on
+ * the safe side. */
+ ctx = wbc_xids_to_sids_send(pvfs->wbc_ctx, sd, num_ids, ids);
+ NT_STATUS_HAVE_NO_MEMORY(ctx);
+ status = wbc_xids_to_sids_recv(ctx, &ids);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ sd->owner_sid = talloc_steal(sd, ids[0].sid);
+ sd->group_sid = talloc_steal(sd, ids[1].sid);
+
+ for (i=0;i<acl->a_count;i++) {
+ struct nfs4ace *a = &acl->ace[i];
+ struct security_ace ace;
+ ace.type = a->e_type;
+ ace.flags = a->e_flags;
+ ace.access_mask = a->e_mask;
+ ace.trustee = *ids[i+2].sid;
+ security_descriptor_dacl_add(sd, &ace);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ save the acl for a file into system.nfs4acl
+*/
+static NTSTATUS pvfs_acl_save_nfs4(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
+ struct security_descriptor *sd)
+{
+ NTSTATUS status;
+ void *privs;
+ struct nfs4acl acl;
+ int i;
+ TALLOC_CTX *tmp_ctx;
+ struct id_mapping *ids;
+ struct composite_context *ctx;
+
+ tmp_ctx = talloc_new(pvfs);
+ NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
+
+ acl.a_version = 0;
+ acl.a_flags = sd->type;
+ acl.a_count = sd->dacl?sd->dacl->num_aces:0;
+ acl.a_owner_mask = 0;
+ acl.a_group_mask = 0;
+ acl.a_other_mask = 0;
+
+ acl.ace = talloc_array(tmp_ctx, struct nfs4ace, acl.a_count);
+ if (!acl.ace) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ids = talloc_array(tmp_ctx, struct id_mapping, acl.a_count);
+ if (ids == NULL) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0;i<acl.a_count;i++) {
+ struct security_ace *ace = &sd->dacl->aces[i];
+ ids[i].unixid = NULL;
+ ids[i].sid = dom_sid_dup(ids, &ace->trustee);
+ if (ids[i].sid == NULL) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ ids[i].status = NT_STATUS_NONE_MAPPED;
+ }
+
+ ctx = wbc_sids_to_xids_send(pvfs->wbc_ctx,ids, acl.a_count, ids);
+ if (ctx == NULL) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ status = wbc_sids_to_xids_recv(ctx, &ids);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ for (i=0;i<acl.a_count;i++) {
+ struct nfs4ace *a = &acl.ace[i];
+ struct security_ace *ace = &sd->dacl->aces[i];
+ a->e_type = ace->type;
+ a->e_flags = ace->flags;
+ a->e_mask = ace->access_mask;
+ if (ids[i].unixid->type != ID_TYPE_UID) {
+ a->e_flags |= ACE4_IDENTIFIER_GROUP;
+ }
+ a->e_id = ids[i].unixid->id;
+ a->e_who = "";
+ }
+
+ privs = root_privileges();
+ status = pvfs_xattr_ndr_save(pvfs, name->full_name, fd,
+ NFS4ACL_XATTR_NAME,
+ &acl, ndr_push_nfs4acl);
+ talloc_free(privs);
+
+ talloc_free(tmp_ctx);
+ return status;
+}
+
+
+/*
+ initialise pvfs acl NFS4 backend
+*/
+NTSTATUS pvfs_acl_nfs4_init(void)
+{
+ struct pvfs_acl_ops ops = {
+ .name = "nfs4acl",
+ .acl_load = pvfs_acl_load_nfs4,
+ .acl_save = pvfs_acl_save_nfs4
+ };
+ return pvfs_acl_register(&ops);
+}
diff --git a/source4/ntvfs/posix/pvfs_acl_xattr.c b/source4/ntvfs/posix/pvfs_acl_xattr.c
new file mode 100644
index 0000000000..87bb6bcb94
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_acl_xattr.c
@@ -0,0 +1,104 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - NT ACLs in xattrs
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "vfs_posix.h"
+#include "../lib/util/unix_privs.h"
+#include "librpc/gen_ndr/ndr_xattr.h"
+
+/*
+ load the current ACL from extended attributes
+*/
+static NTSTATUS pvfs_acl_load_xattr(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
+ TALLOC_CTX *mem_ctx,
+ struct security_descriptor **sd)
+{
+ NTSTATUS status;
+ struct xattr_NTACL *acl;
+
+ if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
+ return NT_STATUS_NOT_FOUND;
+ }
+
+ acl = talloc_zero(mem_ctx, struct xattr_NTACL);
+ NT_STATUS_HAVE_NO_MEMORY(acl);
+
+ status = pvfs_xattr_ndr_load(pvfs, mem_ctx, name->full_name, fd,
+ XATTR_NTACL_NAME,
+ acl,
+ (ndr_pull_flags_fn_t)ndr_pull_xattr_NTACL);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(acl);
+ return status;
+ }
+
+ if (acl->version != 1) {
+ talloc_free(acl);
+ return NT_STATUS_INVALID_ACL;
+ }
+
+ *sd = talloc_steal(mem_ctx, acl->info.sd);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ save the acl for a file into filesystem xattr
+*/
+static NTSTATUS pvfs_acl_save_xattr(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
+ struct security_descriptor *sd)
+{
+ NTSTATUS status;
+ void *privs;
+ struct xattr_NTACL acl;
+
+ if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
+ return NT_STATUS_OK;
+ }
+
+ acl.version = 1;
+ acl.info.sd = sd;
+
+ /* this xattr is in the "system" namespace, so we need
+ admin privileges to set it */
+ privs = root_privileges();
+ status = pvfs_xattr_ndr_save(pvfs, name->full_name, fd,
+ XATTR_NTACL_NAME,
+ &acl,
+ (ndr_push_flags_fn_t)ndr_push_xattr_NTACL);
+ talloc_free(privs);
+ return status;
+}
+
+
+/*
+ initialise pvfs acl xattr backend
+*/
+NTSTATUS pvfs_acl_xattr_init(void)
+{
+ struct pvfs_acl_ops ops = {
+ .name = "xattr",
+ .acl_load = pvfs_acl_load_xattr,
+ .acl_save = pvfs_acl_save_xattr
+ };
+ return pvfs_acl_register(&ops);
+}
diff --git a/source4/ntvfs/posix/pvfs_aio.c b/source4/ntvfs/posix/pvfs_aio.c
new file mode 100644
index 0000000000..e2028d0017
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_aio.c
@@ -0,0 +1,166 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - Linux AIO calls
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include <tevent.h>
+#include "vfs_posix.h"
+#include "system/aio.h"
+
+struct pvfs_aio_read_state {
+ struct ntvfs_request *req;
+ union smb_read *rd;
+ struct pvfs_file *f;
+ struct tevent_aio *ae;
+};
+
+struct pvfs_aio_write_state {
+ struct ntvfs_request *req;
+ union smb_write *wr;
+ struct pvfs_file *f;
+ struct tevent_aio *ae;
+};
+
+/*
+ called when an aio read has finished
+*/
+static void pvfs_aio_read_handler(struct tevent_context *ev, struct tevent_aio *ae,
+ int ret, void *private_data)
+{
+ struct pvfs_aio_read_state *state = talloc_get_type(private_data,
+ struct pvfs_aio_read_state);
+ struct pvfs_file *f = state->f;
+ union smb_read *rd = state->rd;
+
+ if (ret < 0) {
+ /* errno is -ret on error */
+ state->req->async_states->status = pvfs_map_errno(f->pvfs, -ret);
+ state->req->async_states->send_fn(state->req);
+ return;
+ }
+
+ f->handle->position = f->handle->seek_offset = rd->readx.in.offset + ret;
+
+ rd->readx.out.nread = ret;
+ rd->readx.out.remaining = 0xFFFF;
+ rd->readx.out.compaction_mode = 0;
+
+ talloc_steal(ev, state->ae);
+
+ state->req->async_states->status = NT_STATUS_OK;
+ state->req->async_states->send_fn(state->req);
+}
+
+
+/*
+ read from a file
+*/
+NTSTATUS pvfs_aio_pread(struct ntvfs_request *req, union smb_read *rd,
+ struct pvfs_file *f, uint32_t maxcnt)
+{
+ struct iocb iocb;
+ struct pvfs_aio_read_state *state;
+
+ state = talloc(req, struct pvfs_aio_read_state);
+ NT_STATUS_HAVE_NO_MEMORY(state);
+
+ io_prep_pread(&iocb, f->handle->fd, rd->readx.out.data,
+ maxcnt, rd->readx.in.offset);
+ state->ae = tevent_add_aio(req->ctx->event_ctx, req->ctx->event_ctx, &iocb,
+ pvfs_aio_read_handler, state);
+ if (state->ae == NULL) {
+ DEBUG(0,("Failed tevent_add_aio\n"));
+ talloc_free(state);
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ state->req = req;
+ state->rd = rd;
+ state->f = f;
+
+ req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
+
+ return NT_STATUS_OK;
+}
+
+
+
+
+/*
+ called when an aio write has finished
+*/
+static void pvfs_aio_write_handler(struct tevent_context *ev, struct tevent_aio *ae,
+ int ret, void *private_data)
+{
+ struct pvfs_aio_write_state *state = talloc_get_type(private_data,
+ struct pvfs_aio_write_state);
+ struct pvfs_file *f = state->f;
+ union smb_write *wr = state->wr;
+
+ if (ret < 0) {
+ /* errno is -ret on error */
+ state->req->async_states->status = pvfs_map_errno(f->pvfs, -ret);
+ state->req->async_states->send_fn(state->req);
+ return;
+ }
+
+ f->handle->seek_offset = wr->writex.in.offset + ret;
+
+ wr->writex.out.nwritten = ret;
+ wr->writex.out.remaining = 0;
+
+ talloc_steal(ev, state->ae);
+
+ state->req->async_states->status = NT_STATUS_OK;
+ state->req->async_states->send_fn(state->req);
+}
+
+
+/*
+ write to a file
+*/
+NTSTATUS pvfs_aio_pwrite(struct ntvfs_request *req, union smb_write *wr,
+ struct pvfs_file *f)
+{
+ struct iocb iocb;
+ struct pvfs_aio_write_state *state;
+
+ state = talloc(req, struct pvfs_aio_write_state);
+ NT_STATUS_HAVE_NO_MEMORY(state);
+
+ io_prep_pwrite(&iocb, f->handle->fd, discard_const(wr->writex.in.data),
+ wr->writex.in.count, wr->writex.in.offset);
+ state->ae = tevent_add_aio(req->ctx->event_ctx, req->ctx->event_ctx, &iocb,
+ pvfs_aio_write_handler, state);
+ if (state->ae == NULL) {
+ DEBUG(0,("Failed tevent_add_aio\n"));
+ talloc_free(state);
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ state->req = req;
+ state->wr = wr;
+ state->f = f;
+
+ req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
+
+ return NT_STATUS_OK;
+}
+
diff --git a/source4/ntvfs/posix/pvfs_dirlist.c b/source4/ntvfs/posix/pvfs_dirlist.c
new file mode 100644
index 0000000000..77f19c3585
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_dirlist.c
@@ -0,0 +1,403 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+/*
+ directory listing functions for posix backend
+*/
+
+#include "includes.h"
+#include "vfs_posix.h"
+#include "system/dir.h"
+
+#define NAME_CACHE_SIZE 100
+
+struct name_cache_entry {
+ char *name;
+ off_t offset;
+};
+
+struct pvfs_dir {
+ struct pvfs_state *pvfs;
+ bool no_wildcard;
+ char *single_name;
+ const char *pattern;
+ off_t offset;
+ DIR *dir;
+ const char *unix_path;
+ bool end_of_search;
+ struct name_cache_entry *name_cache;
+ uint32_t name_cache_index;
+};
+
+/* these three numbers are chosen to minimise the chances of a bad
+ interaction with the OS value for 'end of directory'. On IRIX
+ telldir() returns 0xFFFFFFFF at the end of a directory, and that
+ caused an infinite loop with the original values of 0,1,2
+
+ On XFS on linux telldir returns 0x7FFFFFFF at the end of a
+ directory. Thus the change from 0x80000002, as otherwise
+ 0x7FFFFFFF+0x80000002==1==DIR_OFFSET_DOTDOT
+*/
+#define DIR_OFFSET_DOT 0
+#define DIR_OFFSET_DOTDOT 1
+#define DIR_OFFSET_BASE 0x80000022
+
+/*
+ a special directory listing case where the pattern has no wildcard. We can just do a single stat()
+ thus avoiding the more expensive directory scan
+*/
+static NTSTATUS pvfs_list_no_wildcard(struct pvfs_state *pvfs, struct pvfs_filename *name,
+ const char *pattern, struct pvfs_dir *dir)
+{
+ if (!name->exists) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ dir->pvfs = pvfs;
+ dir->no_wildcard = true;
+ dir->end_of_search = false;
+ dir->unix_path = talloc_strdup(dir, name->full_name);
+ if (!dir->unix_path) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ dir->single_name = talloc_strdup(dir, pattern);
+ if (!dir->single_name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ dir->dir = NULL;
+ dir->offset = 0;
+ dir->pattern = NULL;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ destroy an open search
+*/
+static int pvfs_dirlist_destructor(struct pvfs_dir *dir)
+{
+ if (dir->dir) closedir(dir->dir);
+ return 0;
+}
+
+/*
+ start to read a directory
+
+ if the pattern matches no files then we return NT_STATUS_OK, with dir->count = 0
+*/
+NTSTATUS pvfs_list_start(struct pvfs_state *pvfs, struct pvfs_filename *name,
+ TALLOC_CTX *mem_ctx, struct pvfs_dir **dirp)
+{
+ char *pattern;
+ struct pvfs_dir *dir;
+
+ (*dirp) = talloc_zero(mem_ctx, struct pvfs_dir);
+ if (*dirp == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ dir = *dirp;
+
+ /* split the unix path into a directory + pattern */
+ pattern = strrchr(name->full_name, '/');
+ if (!pattern) {
+ /* this should not happen, as pvfs_unix_path is supposed to
+ return an absolute path */
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ *pattern++ = 0;
+
+ if (!name->has_wildcard) {
+ return pvfs_list_no_wildcard(pvfs, name, pattern, dir);
+ }
+
+ dir->unix_path = talloc_strdup(dir, name->full_name);
+ if (!dir->unix_path) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ dir->pattern = talloc_strdup(dir, pattern);
+ if (dir->pattern == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ dir->dir = opendir(name->full_name);
+ if (!dir->dir) {
+ return pvfs_map_errno(pvfs, errno);
+ }
+
+ dir->pvfs = pvfs;
+ dir->no_wildcard = false;
+ dir->end_of_search = false;
+ dir->offset = DIR_OFFSET_DOT;
+ dir->name_cache = talloc_zero_array(dir,
+ struct name_cache_entry,
+ NAME_CACHE_SIZE);
+ if (dir->name_cache == NULL) {
+ talloc_free(dir);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ talloc_set_destructor(dir, pvfs_dirlist_destructor);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ add an entry to the local cache
+*/
+static void dcache_add(struct pvfs_dir *dir, const char *name)
+{
+ struct name_cache_entry *e;
+
+ dir->name_cache_index = (dir->name_cache_index+1) % NAME_CACHE_SIZE;
+ e = &dir->name_cache[dir->name_cache_index];
+
+ if (e->name) talloc_free(e->name);
+
+ e->name = talloc_strdup(dir->name_cache, name);
+ e->offset = dir->offset;
+}
+
+/*
+ return the next entry
+*/
+const char *pvfs_list_next(struct pvfs_dir *dir, off_t *ofs)
+{
+ struct dirent *de;
+ enum protocol_types protocol = dir->pvfs->ntvfs->ctx->protocol;
+
+ /* non-wildcard searches are easy */
+ if (dir->no_wildcard) {
+ dir->end_of_search = true;
+ if (*ofs != 0) return NULL;
+ (*ofs)++;
+ return dir->single_name;
+ }
+
+ /* . and .. are handled separately as some unix systems will
+ not return them first in a directory, but windows client
+ may assume that these entries always appear first */
+ if (*ofs == DIR_OFFSET_DOT) {
+ (*ofs) = DIR_OFFSET_DOTDOT;
+ dir->offset = *ofs;
+ if (ms_fnmatch(dir->pattern, ".", protocol) == 0) {
+ dcache_add(dir, ".");
+ return ".";
+ }
+ }
+
+ if (*ofs == DIR_OFFSET_DOTDOT) {
+ (*ofs) = DIR_OFFSET_BASE;
+ dir->offset = *ofs;
+ if (ms_fnmatch(dir->pattern, "..", protocol) == 0) {
+ dcache_add(dir, "..");
+ return "..";
+ }
+ }
+
+ if (*ofs == DIR_OFFSET_BASE) {
+ rewinddir(dir->dir);
+ } else if (*ofs != dir->offset) {
+ seekdir(dir->dir, (*ofs) - DIR_OFFSET_BASE);
+ }
+ dir->offset = *ofs;
+
+ while ((de = readdir(dir->dir))) {
+ const char *dname = de->d_name;
+
+ if (ISDOT(dname) || ISDOTDOT(dname)) {
+ continue;
+ }
+
+ if (ms_fnmatch(dir->pattern, dname, protocol) != 0) {
+ char *short_name = pvfs_short_name_component(dir->pvfs, dname);
+ if (short_name == NULL ||
+ ms_fnmatch(dir->pattern, short_name, protocol) != 0) {
+ talloc_free(short_name);
+ continue;
+ }
+ talloc_free(short_name);
+ }
+
+ dir->offset = telldir(dir->dir) + DIR_OFFSET_BASE;
+ (*ofs) = dir->offset;
+
+ dcache_add(dir, dname);
+
+ return dname;
+ }
+
+ dir->end_of_search = true;
+ return NULL;
+}
+
+/*
+ return unix directory of an open search
+*/
+const char *pvfs_list_unix_path(struct pvfs_dir *dir)
+{
+ return dir->unix_path;
+}
+
+/*
+ return true if end of search has been reached
+*/
+bool pvfs_list_eos(struct pvfs_dir *dir, off_t ofs)
+{
+ return dir->end_of_search;
+}
+
+/*
+ seek to the given name
+*/
+NTSTATUS pvfs_list_seek(struct pvfs_dir *dir, const char *name, off_t *ofs)
+{
+ struct dirent *de;
+ int i;
+
+ dir->end_of_search = false;
+
+ if (ISDOT(name)) {
+ dir->offset = DIR_OFFSET_DOTDOT;
+ *ofs = dir->offset;
+ return NT_STATUS_OK;
+ }
+
+ if (ISDOTDOT(name)) {
+ dir->offset = DIR_OFFSET_BASE;
+ *ofs = dir->offset;
+ return NT_STATUS_OK;
+ }
+
+ for (i=dir->name_cache_index;i>=0;i--) {
+ struct name_cache_entry *e = &dir->name_cache[i];
+ if (e->name && strcasecmp_m(name, e->name) == 0) {
+ *ofs = e->offset;
+ return NT_STATUS_OK;
+ }
+ }
+ for (i=NAME_CACHE_SIZE-1;i>dir->name_cache_index;i--) {
+ struct name_cache_entry *e = &dir->name_cache[i];
+ if (e->name && strcasecmp_m(name, e->name) == 0) {
+ *ofs = e->offset;
+ return NT_STATUS_OK;
+ }
+ }
+
+ rewinddir(dir->dir);
+
+ while ((de = readdir(dir->dir))) {
+ if (strcasecmp_m(name, de->d_name) == 0) {
+ dir->offset = telldir(dir->dir) + DIR_OFFSET_BASE;
+ *ofs = dir->offset;
+ return NT_STATUS_OK;
+ }
+ }
+
+ dir->end_of_search = true;
+
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+}
+
+/*
+ seek to the given offset
+*/
+NTSTATUS pvfs_list_seek_ofs(struct pvfs_dir *dir, uint32_t resume_key, off_t *ofs)
+{
+ struct dirent *de;
+ int i;
+
+ dir->end_of_search = false;
+
+ if (resume_key == DIR_OFFSET_DOT) {
+ *ofs = DIR_OFFSET_DOTDOT;
+ return NT_STATUS_OK;
+ }
+
+ if (resume_key == DIR_OFFSET_DOTDOT) {
+ *ofs = DIR_OFFSET_BASE;
+ return NT_STATUS_OK;
+ }
+
+ if (resume_key == DIR_OFFSET_BASE) {
+ rewinddir(dir->dir);
+ if ((de=readdir(dir->dir)) == NULL) {
+ dir->end_of_search = true;
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+ *ofs = telldir(dir->dir) + DIR_OFFSET_BASE;
+ dir->offset = *ofs;
+ return NT_STATUS_OK;
+ }
+
+ for (i=dir->name_cache_index;i>=0;i--) {
+ struct name_cache_entry *e = &dir->name_cache[i];
+ if (resume_key == (uint32_t)e->offset) {
+ *ofs = e->offset;
+ return NT_STATUS_OK;
+ }
+ }
+ for (i=NAME_CACHE_SIZE-1;i>dir->name_cache_index;i--) {
+ struct name_cache_entry *e = &dir->name_cache[i];
+ if (resume_key == (uint32_t)e->offset) {
+ *ofs = e->offset;
+ return NT_STATUS_OK;
+ }
+ }
+
+ rewinddir(dir->dir);
+
+ while ((de = readdir(dir->dir))) {
+ dir->offset = telldir(dir->dir) + DIR_OFFSET_BASE;
+ if (resume_key == (uint32_t)dir->offset) {
+ *ofs = dir->offset;
+ return NT_STATUS_OK;
+ }
+ }
+
+ dir->end_of_search = true;
+
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+}
+
+
+/*
+ see if a directory is empty
+*/
+bool pvfs_directory_empty(struct pvfs_state *pvfs, struct pvfs_filename *name)
+{
+ struct dirent *de;
+ DIR *dir = opendir(name->full_name);
+ if (dir == NULL) {
+ return true;
+ }
+
+ while ((de = readdir(dir))) {
+ if (!ISDOT(de->d_name) && !ISDOTDOT(de->d_name)) {
+ closedir(dir);
+ return false;
+ }
+ }
+
+ closedir(dir);
+ return true;
+}
diff --git a/source4/ntvfs/posix/pvfs_fileinfo.c b/source4/ntvfs/posix/pvfs_fileinfo.c
new file mode 100644
index 0000000000..a14c8f64ae
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_fileinfo.c
@@ -0,0 +1,159 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend -
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "vfs_posix.h"
+
+/****************************************************************************
+ Change a unix mode to a dos mode.
+****************************************************************************/
+static uint32_t dos_mode_from_stat(struct pvfs_state *pvfs, struct stat *st)
+{
+ int result = 0;
+
+ if ((st->st_mode & S_IWUSR) == 0)
+ result |= FILE_ATTRIBUTE_READONLY;
+
+ if ((pvfs->flags & PVFS_FLAG_MAP_ARCHIVE) && ((st->st_mode & S_IXUSR) != 0))
+ result |= FILE_ATTRIBUTE_ARCHIVE;
+
+ if ((pvfs->flags & PVFS_FLAG_MAP_SYSTEM) && ((st->st_mode & S_IXGRP) != 0))
+ result |= FILE_ATTRIBUTE_SYSTEM;
+
+ if ((pvfs->flags & PVFS_FLAG_MAP_HIDDEN) && ((st->st_mode & S_IXOTH) != 0))
+ result |= FILE_ATTRIBUTE_HIDDEN;
+
+ if (S_ISDIR(st->st_mode))
+ result = FILE_ATTRIBUTE_DIRECTORY | (result & FILE_ATTRIBUTE_READONLY);
+
+ return result;
+}
+
+
+
+/*
+ fill in the dos file attributes for a file
+*/
+NTSTATUS pvfs_fill_dos_info(struct pvfs_state *pvfs, struct pvfs_filename *name,
+ uint_t flags, int fd)
+{
+ NTSTATUS status;
+ DATA_BLOB lkey;
+ NTTIME write_time;
+
+ /* make directories appear as size 0 with 1 link */
+ if (S_ISDIR(name->st.st_mode)) {
+ name->st.st_size = 0;
+ name->st.st_nlink = 1;
+ } else if (name->stream_id == 0) {
+ name->stream_name = NULL;
+ }
+
+ /* for now just use the simple samba mapping */
+ unix_to_nt_time(&name->dos.create_time, name->st.st_ctime);
+ unix_to_nt_time(&name->dos.access_time, name->st.st_atime);
+ unix_to_nt_time(&name->dos.write_time, name->st.st_mtime);
+ unix_to_nt_time(&name->dos.change_time, name->st.st_ctime);
+#ifdef HAVE_STAT_TV_NSEC
+ name->dos.create_time += name->st.st_ctim.tv_nsec / 100;
+ name->dos.access_time += name->st.st_atim.tv_nsec / 100;
+ name->dos.write_time += name->st.st_mtim.tv_nsec / 100;
+ name->dos.change_time += name->st.st_ctim.tv_nsec / 100;
+#endif
+ name->dos.attrib = dos_mode_from_stat(pvfs, &name->st);
+ name->dos.alloc_size = pvfs_round_alloc_size(pvfs, name->st.st_size);
+ name->dos.nlink = name->st.st_nlink;
+ name->dos.ea_size = 4;
+ if (pvfs->ntvfs->ctx->protocol == PROTOCOL_SMB2) {
+ /* SMB2 represents a null EA with zero bytes */
+ name->dos.ea_size = 0;
+ }
+
+ name->dos.file_id = (((uint64_t)name->st.st_dev)<<32) | name->st.st_ino;
+ name->dos.flags = 0;
+
+ status = pvfs_dosattrib_load(pvfs, name, fd);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ if (flags & PVFS_RESOLVE_NO_OPENDB) {
+ return NT_STATUS_OK;
+ }
+
+ status = pvfs_locking_key(name, name, &lkey);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ status = odb_get_file_infos(pvfs->odb_context, &lkey,
+ NULL, &write_time);
+ data_blob_free(&lkey);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1,("WARNING: odb_get_file_infos: %s\n", nt_errstr(status)));
+ return status;
+ }
+
+ if (!null_time(write_time)) {
+ name->dos.write_time = write_time;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ return a set of unix file permissions for a new file or directory
+*/
+mode_t pvfs_fileperms(struct pvfs_state *pvfs, uint32_t attrib)
+{
+ mode_t mode = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
+
+ if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE) &&
+ (attrib & FILE_ATTRIBUTE_READONLY)) {
+ mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
+ }
+
+ if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
+ if ((attrib & FILE_ATTRIBUTE_ARCHIVE) &&
+ (pvfs->flags & PVFS_FLAG_MAP_ARCHIVE)) {
+ mode |= S_IXUSR;
+ }
+ if ((attrib & FILE_ATTRIBUTE_SYSTEM) &&
+ (pvfs->flags & PVFS_FLAG_MAP_SYSTEM)) {
+ mode |= S_IXGRP;
+ }
+ if ((attrib & FILE_ATTRIBUTE_HIDDEN) &&
+ (pvfs->flags & PVFS_FLAG_MAP_HIDDEN)) {
+ mode |= S_IXOTH;
+ }
+ }
+
+ if (attrib & FILE_ATTRIBUTE_DIRECTORY) {
+ mode |= (S_IFDIR | S_IWUSR);
+ mode |= (S_IXUSR | S_IXGRP | S_IXOTH);
+ mode &= pvfs->options.dir_mask;
+ mode |= pvfs->options.force_dir_mode;
+ } else {
+ mode &= pvfs->options.create_mask;
+ mode |= pvfs->options.force_create_mode;
+ }
+
+ return mode;
+}
+
+
diff --git a/source4/ntvfs/posix/pvfs_flush.c b/source4/ntvfs/posix/pvfs_flush.c
new file mode 100644
index 0000000000..f425fccec7
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_flush.c
@@ -0,0 +1,80 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - flush
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "vfs_posix.h"
+
+/*
+ flush a single open file
+*/
+static void pvfs_flush_file(struct pvfs_state *pvfs, struct pvfs_file *f)
+{
+ if (f->handle->fd == -1) {
+ return;
+ }
+ if (pvfs->flags & PVFS_FLAG_STRICT_SYNC) {
+ fsync(f->handle->fd);
+ }
+}
+
+/*
+ flush a fnum
+*/
+NTSTATUS pvfs_flush(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_flush *io)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ struct pvfs_file *f;
+
+ switch (io->generic.level) {
+ case RAW_FLUSH_FLUSH:
+ case RAW_FLUSH_SMB2:
+ /* TODO: take care of io->smb2.in.unknown */
+ f = pvfs_find_fd(pvfs, req, io->generic.in.file.ntvfs);
+ if (!f) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+ pvfs_flush_file(pvfs, f);
+ io->smb2.out.reserved = 0;
+ return NT_STATUS_OK;
+
+ case RAW_FLUSH_ALL:
+ if (!(pvfs->flags & PVFS_FLAG_STRICT_SYNC)) {
+ return NT_STATUS_OK;
+ }
+
+ /*
+ * they are asking to flush all open files
+ * for the given SMBPID
+ */
+ for (f=pvfs->files.list;f;f=f->next) {
+ if (f->ntvfs->smbpid != req->smbpid) continue;
+
+ pvfs_flush_file(pvfs, f);
+ }
+
+ return NT_STATUS_OK;
+ }
+
+ return NT_STATUS_INVALID_LEVEL;
+}
diff --git a/source4/ntvfs/posix/pvfs_fsinfo.c b/source4/ntvfs/posix/pvfs_fsinfo.c
new file mode 100644
index 0000000000..10fb7cf707
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_fsinfo.c
@@ -0,0 +1,208 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - fsinfo
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "vfs_posix.h"
+#include "librpc/gen_ndr/xattr.h"
+#include "librpc/ndr/libndr.h"
+
+/* We use libblkid out of e2fsprogs to identify UUID of a volume */
+#ifdef HAVE_LIBBLKID
+#include <blkid/blkid.h>
+#endif
+
+static NTSTATUS pvfs_blkid_fs_uuid(struct pvfs_state *pvfs, struct stat *st, struct GUID *uuid)
+{
+#ifdef HAVE_LIBBLKID
+ NTSTATUS status;
+ char *uuid_value = NULL;
+ char *devname = NULL;
+
+ devname = blkid_devno_to_devname(st->st_dev);
+ if (!devname) {
+ ZERO_STRUCTP(uuid);
+ return NT_STATUS_OK;
+ }
+
+ uuid_value = blkid_get_tag_value(NULL, "UUID", devname);
+ free(devname);
+ if (!uuid_value) {
+ ZERO_STRUCTP(uuid);
+ return NT_STATUS_OK;
+ }
+
+ status = GUID_from_string(uuid_value, uuid);
+ free(uuid_value);
+ if (!NT_STATUS_IS_OK(status)) {
+ ZERO_STRUCTP(uuid);
+ return NT_STATUS_OK;
+ }
+ return NT_STATUS_OK;
+#else
+ ZERO_STRUCTP(uuid);
+ return NT_STATUS_OK;
+#endif
+}
+
+static NTSTATUS pvfs_cache_base_fs_uuid(struct pvfs_state *pvfs, struct stat *st)
+{
+ NTSTATUS status;
+ struct GUID uuid;
+
+ if (pvfs->base_fs_uuid) return NT_STATUS_OK;
+
+ status = pvfs_blkid_fs_uuid(pvfs, st, &uuid);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ pvfs->base_fs_uuid = talloc(pvfs, struct GUID);
+ NT_STATUS_HAVE_NO_MEMORY(pvfs->base_fs_uuid);
+ *pvfs->base_fs_uuid = uuid;
+
+ return NT_STATUS_OK;
+}
+/*
+ return filesystem space info
+*/
+NTSTATUS pvfs_fsinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_fsinfo *fs)
+{
+ NTSTATUS status;
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ uint64_t blocks_free, blocks_total;
+ uint_t bpunit;
+ struct stat st;
+ const uint16_t block_size = 512;
+
+ /* only some levels need the expensive sys_fsusage() call */
+ switch (fs->generic.level) {
+ case RAW_QFS_DSKATTR:
+ case RAW_QFS_ALLOCATION:
+ case RAW_QFS_SIZE_INFO:
+ case RAW_QFS_SIZE_INFORMATION:
+ case RAW_QFS_FULL_SIZE_INFORMATION:
+ if (sys_fsusage(pvfs->base_directory, &blocks_free, &blocks_total) == -1) {
+ return pvfs_map_errno(pvfs, errno);
+ }
+ default:
+ break;
+ }
+
+ if (stat(pvfs->base_directory, &st) != 0) {
+ return NT_STATUS_DISK_CORRUPT_ERROR;
+ }
+
+ /* now fill in the out fields */
+ switch (fs->generic.level) {
+ case RAW_QFS_GENERIC:
+ return NT_STATUS_INVALID_LEVEL;
+
+ case RAW_QFS_DSKATTR:
+ /* we need to scale the sizes to fit */
+ for (bpunit=64; bpunit<0x10000; bpunit *= 2) {
+ if (blocks_total * (double)block_size < bpunit * 512 * 65535.0) {
+ break;
+ }
+ }
+ fs->dskattr.out.blocks_per_unit = bpunit;
+ fs->dskattr.out.block_size = block_size;
+ fs->dskattr.out.units_total = (blocks_total * (double)block_size) / (bpunit * 512);
+ fs->dskattr.out.units_free = (blocks_free * (double)block_size) / (bpunit * 512);
+
+ /* we must return a maximum of 2G to old DOS systems, or they get very confused */
+ if (bpunit > 64 && req->ctx->protocol <= PROTOCOL_LANMAN2) {
+ fs->dskattr.out.blocks_per_unit = 64;
+ fs->dskattr.out.units_total = 0xFFFF;
+ fs->dskattr.out.units_free = 0xFFFF;
+ }
+ return NT_STATUS_OK;
+
+ case RAW_QFS_ALLOCATION:
+ fs->allocation.out.fs_id = st.st_dev;
+ fs->allocation.out.total_alloc_units = blocks_total;
+ fs->allocation.out.avail_alloc_units = blocks_free;
+ fs->allocation.out.sectors_per_unit = 1;
+ fs->allocation.out.bytes_per_sector = block_size;
+ return NT_STATUS_OK;
+
+ case RAW_QFS_VOLUME:
+ fs->volume.out.serial_number = st.st_ino;
+ fs->volume.out.volume_name.s = pvfs->share_name;
+ return NT_STATUS_OK;
+
+ case RAW_QFS_VOLUME_INFO:
+ case RAW_QFS_VOLUME_INFORMATION:
+ unix_to_nt_time(&fs->volume_info.out.create_time, st.st_ctime);
+ fs->volume_info.out.serial_number = st.st_ino;
+ fs->volume_info.out.volume_name.s = pvfs->share_name;
+ return NT_STATUS_OK;
+
+ case RAW_QFS_SIZE_INFO:
+ case RAW_QFS_SIZE_INFORMATION:
+ fs->size_info.out.total_alloc_units = blocks_total;
+ fs->size_info.out.avail_alloc_units = blocks_free;
+ fs->size_info.out.sectors_per_unit = 1;
+ fs->size_info.out.bytes_per_sector = block_size;
+ return NT_STATUS_OK;
+
+ case RAW_QFS_DEVICE_INFO:
+ case RAW_QFS_DEVICE_INFORMATION:
+ fs->device_info.out.device_type = 0;
+ fs->device_info.out.characteristics = 0;
+ return NT_STATUS_OK;
+
+ case RAW_QFS_ATTRIBUTE_INFO:
+ case RAW_QFS_ATTRIBUTE_INFORMATION:
+ fs->attribute_info.out.fs_attr = pvfs->fs_attribs;
+ fs->attribute_info.out.max_file_component_length = 255;
+ fs->attribute_info.out.fs_type.s = ntvfs->ctx->fs_type;
+ return NT_STATUS_OK;
+
+ case RAW_QFS_QUOTA_INFORMATION:
+ ZERO_STRUCT(fs->quota_information.out.unknown);
+ fs->quota_information.out.quota_soft = 0;
+ fs->quota_information.out.quota_hard = 0;
+ fs->quota_information.out.quota_flags = 0;
+ return NT_STATUS_OK;
+
+ case RAW_QFS_FULL_SIZE_INFORMATION:
+ fs->full_size_information.out.total_alloc_units = blocks_total;
+ fs->full_size_information.out.call_avail_alloc_units = blocks_free;
+ fs->full_size_information.out.actual_avail_alloc_units = blocks_free;
+ fs->full_size_information.out.sectors_per_unit = 1;
+ fs->full_size_information.out.bytes_per_sector = block_size;
+ return NT_STATUS_OK;
+
+ case RAW_QFS_OBJECTID_INFORMATION:
+ ZERO_STRUCT(fs->objectid_information.out.guid);
+ ZERO_STRUCT(fs->objectid_information.out.unknown);
+
+ status = pvfs_cache_base_fs_uuid(pvfs, &st);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ fs->objectid_information.out.guid = *pvfs->base_fs_uuid;
+ return NT_STATUS_OK;
+
+ default:
+ break;
+ }
+ return NT_STATUS_INVALID_LEVEL;
+}
diff --git a/source4/ntvfs/posix/pvfs_ioctl.c b/source4/ntvfs/posix/pvfs_ioctl.c
new file mode 100644
index 0000000000..f263e9852b
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_ioctl.c
@@ -0,0 +1,82 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - open and close
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "vfs_posix.h"
+#include "libcli/raw/ioctl.h"
+
+/*
+ old ioctl interface
+*/
+static NTSTATUS pvfs_ioctl_old(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_ioctl *io)
+{
+ return NT_STATUS_DOS(ERRSRV, ERRerror);
+}
+
+/*
+ nt ioctl interface
+*/
+static NTSTATUS pvfs_ntioctl(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_ioctl *io)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ struct pvfs_file *f;
+
+ f = pvfs_find_fd(pvfs, req, io->ntioctl.in.file.ntvfs);
+ if (!f) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ switch (io->ntioctl.in.function) {
+ case FSCTL_SET_SPARSE:
+ /* maybe some posix systems have a way of marking
+ a file non-sparse? */
+ io->ntioctl.out.blob = data_blob(NULL, 0);
+ return NT_STATUS_OK;
+ }
+
+ return NT_STATUS_NOT_SUPPORTED;
+}
+
+/*
+ ioctl interface
+*/
+NTSTATUS pvfs_ioctl(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_ioctl *io)
+{
+ switch (io->generic.level) {
+ case RAW_IOCTL_IOCTL:
+ return pvfs_ioctl_old(ntvfs, req, io);
+
+ case RAW_IOCTL_NTIOCTL:
+ return pvfs_ntioctl(ntvfs, req, io);
+
+ case RAW_IOCTL_SMB2:
+ case RAW_IOCTL_SMB2_NO_HANDLE:
+ /* see WSPP SMB2 test 46 */
+ return NT_STATUS_INVALID_DEVICE_REQUEST;
+ }
+
+ return NT_STATUS_INVALID_LEVEL;
+}
diff --git a/source4/ntvfs/posix/pvfs_lock.c b/source4/ntvfs/posix/pvfs_lock.c
new file mode 100644
index 0000000000..711c924ae3
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_lock.c
@@ -0,0 +1,408 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - locking
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "vfs_posix.h"
+#include "system/time.h"
+#include "../lib/util/dlinklist.h"
+#include "messaging/messaging.h"
+
+
+/*
+ check if we can perform IO on a range that might be locked
+*/
+NTSTATUS pvfs_check_lock(struct pvfs_state *pvfs,
+ struct pvfs_file *f,
+ uint32_t smbpid,
+ uint64_t offset, uint64_t count,
+ enum brl_type rw)
+{
+ if (!(pvfs->flags & PVFS_FLAG_STRICT_LOCKING)) {
+ return NT_STATUS_OK;
+ }
+
+ return brl_locktest(pvfs->brl_context,
+ f->brl_handle,
+ smbpid,
+ offset, count, rw);
+}
+
+/* this state structure holds information about a lock we are waiting on */
+struct pvfs_pending_lock {
+ struct pvfs_pending_lock *next, *prev;
+ struct pvfs_state *pvfs;
+ union smb_lock *lck;
+ struct pvfs_file *f;
+ struct ntvfs_request *req;
+ int pending_lock;
+ struct pvfs_wait *wait_handle;
+ struct timeval end_time;
+};
+
+/*
+ a secondary attempt to setup a lock has failed - back out
+ the locks we did get and send an error
+*/
+static void pvfs_lock_async_failed(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
+ struct pvfs_file *f,
+ struct smb_lock_entry *locks,
+ int i,
+ NTSTATUS status)
+{
+ /* undo the locks we just did */
+ for (i--;i>=0;i--) {
+ brl_unlock(pvfs->brl_context,
+ f->brl_handle,
+ locks[i].pid,
+ locks[i].offset,
+ locks[i].count);
+ f->lock_count--;
+ }
+ req->async_states->status = status;
+ req->async_states->send_fn(req);
+}
+
+
+/*
+ called when we receive a pending lock notification. It means that
+ either our lock timed out or someone else has unlocked a overlapping
+ range, so we should try the lock again. Note that on timeout we
+ do retry the lock, giving it a last chance.
+*/
+static void pvfs_pending_lock_continue(void *private_data, enum pvfs_wait_notice reason)
+{
+ struct pvfs_pending_lock *pending = talloc_get_type(private_data,
+ struct pvfs_pending_lock);
+ struct pvfs_state *pvfs = pending->pvfs;
+ struct pvfs_file *f = pending->f;
+ struct ntvfs_request *req = pending->req;
+ union smb_lock *lck = pending->lck;
+ struct smb_lock_entry *locks;
+ enum brl_type rw;
+ NTSTATUS status;
+ int i;
+ bool timed_out;
+
+ timed_out = (reason != PVFS_WAIT_EVENT);
+
+ locks = lck->lockx.in.locks + lck->lockx.in.ulock_cnt;
+
+ if (lck->lockx.in.mode & LOCKING_ANDX_SHARED_LOCK) {
+ rw = READ_LOCK;
+ } else {
+ rw = WRITE_LOCK;
+ }
+
+ DLIST_REMOVE(f->pending_list, pending);
+
+ /* we don't retry on a cancel */
+ if (reason == PVFS_WAIT_CANCEL) {
+ status = NT_STATUS_FILE_LOCK_CONFLICT;
+ } else {
+ /*
+ * here it's important to pass the pending pointer
+ * because with this we'll get the correct error code
+ * FILE_LOCK_CONFLICT in the error case
+ */
+ status = brl_lock(pvfs->brl_context,
+ f->brl_handle,
+ locks[pending->pending_lock].pid,
+ locks[pending->pending_lock].offset,
+ locks[pending->pending_lock].count,
+ rw, pending);
+ }
+ if (NT_STATUS_IS_OK(status)) {
+ f->lock_count++;
+ timed_out = false;
+ }
+
+ /* if we have failed and timed out, or succeeded, then we
+ don't need the pending lock any more */
+ if (NT_STATUS_IS_OK(status) || timed_out) {
+ NTSTATUS status2;
+ status2 = brl_remove_pending(pvfs->brl_context,
+ f->brl_handle, pending);
+ if (!NT_STATUS_IS_OK(status2)) {
+ DEBUG(0,("pvfs_lock: failed to remove pending lock - %s\n", nt_errstr(status2)));
+ }
+ talloc_free(pending->wait_handle);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ if (timed_out) {
+ /* no more chances */
+ pvfs_lock_async_failed(pvfs, req, f, locks, pending->pending_lock, status);
+ talloc_free(pending);
+ } else {
+ /* we can try again */
+ DLIST_ADD(f->pending_list, pending);
+ }
+ return;
+ }
+
+ /* if we haven't timed out yet, then we can do more pending locks */
+ if (rw == READ_LOCK) {
+ rw = PENDING_READ_LOCK;
+ } else {
+ rw = PENDING_WRITE_LOCK;
+ }
+
+ /* we've now got the pending lock. try and get the rest, which might
+ lead to more pending locks */
+ for (i=pending->pending_lock+1;i<lck->lockx.in.lock_cnt;i++) {
+ if (pending) {
+ pending->pending_lock = i;
+ }
+
+ status = brl_lock(pvfs->brl_context,
+ f->brl_handle,
+ locks[i].pid,
+ locks[i].offset,
+ locks[i].count,
+ rw, pending);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (pending) {
+ /* a timed lock failed - setup a wait message to handle
+ the pending lock notification or a timeout */
+ pending->wait_handle = pvfs_wait_message(pvfs, req, MSG_BRL_RETRY,
+ pending->end_time,
+ pvfs_pending_lock_continue,
+ pending);
+ if (pending->wait_handle == NULL) {
+ pvfs_lock_async_failed(pvfs, req, f, locks, i, NT_STATUS_NO_MEMORY);
+ talloc_free(pending);
+ } else {
+ talloc_steal(pending, pending->wait_handle);
+ DLIST_ADD(f->pending_list, pending);
+ }
+ return;
+ }
+ pvfs_lock_async_failed(pvfs, req, f, locks, i, status);
+ talloc_free(pending);
+ return;
+ }
+
+ f->lock_count++;
+ }
+
+ /* we've managed to get all the locks. Tell the client */
+ req->async_states->status = NT_STATUS_OK;
+ req->async_states->send_fn(req);
+ talloc_free(pending);
+}
+
+
+/*
+ called when we close a file that might have locks
+*/
+void pvfs_lock_close(struct pvfs_state *pvfs, struct pvfs_file *f)
+{
+ struct pvfs_pending_lock *p, *next;
+
+ if (f->lock_count || f->pending_list) {
+ DEBUG(5,("pvfs_lock: removing %.0f locks on close\n",
+ (double)f->lock_count));
+ brl_close(f->pvfs->brl_context, f->brl_handle);
+ f->lock_count = 0;
+ }
+
+ /* reply to all the pending lock requests, telling them the
+ lock failed */
+ for (p=f->pending_list;p;p=next) {
+ next = p->next;
+ DLIST_REMOVE(f->pending_list, p);
+ p->req->async_states->status = NT_STATUS_RANGE_NOT_LOCKED;
+ p->req->async_states->send_fn(p->req);
+ }
+}
+
+
+/*
+ cancel a set of locks
+*/
+static NTSTATUS pvfs_lock_cancel(struct pvfs_state *pvfs, struct ntvfs_request *req, union smb_lock *lck,
+ struct pvfs_file *f)
+{
+ struct pvfs_pending_lock *p;
+
+ for (p=f->pending_list;p;p=p->next) {
+ /* check if the lock request matches exactly - you can only cancel with exact matches */
+ if (p->lck->lockx.in.ulock_cnt == lck->lockx.in.ulock_cnt &&
+ p->lck->lockx.in.lock_cnt == lck->lockx.in.lock_cnt &&
+ p->lck->lockx.in.file.ntvfs== lck->lockx.in.file.ntvfs &&
+ p->lck->lockx.in.mode == (lck->lockx.in.mode & ~LOCKING_ANDX_CANCEL_LOCK)) {
+ int i;
+
+ for (i=0;i<lck->lockx.in.ulock_cnt + lck->lockx.in.lock_cnt;i++) {
+ if (p->lck->lockx.in.locks[i].pid != lck->lockx.in.locks[i].pid ||
+ p->lck->lockx.in.locks[i].offset != lck->lockx.in.locks[i].offset ||
+ p->lck->lockx.in.locks[i].count != lck->lockx.in.locks[i].count) {
+ break;
+ }
+ }
+ if (i < lck->lockx.in.ulock_cnt) continue;
+
+ /* an exact match! we can cancel it, which is equivalent
+ to triggering the timeout early */
+ pvfs_pending_lock_continue(p, PVFS_WAIT_TIMEOUT);
+ return NT_STATUS_OK;
+ }
+ }
+
+ return NT_STATUS_DOS(ERRDOS, ERRcancelviolation);
+}
+
+
+/*
+ lock or unlock a byte range
+*/
+NTSTATUS pvfs_lock(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_lock *lck)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ struct pvfs_file *f;
+ struct smb_lock_entry *locks;
+ int i;
+ enum brl_type rw;
+ struct pvfs_pending_lock *pending = NULL;
+ NTSTATUS status;
+
+ if (lck->generic.level != RAW_LOCK_GENERIC) {
+ return ntvfs_map_lock(ntvfs, req, lck);
+ }
+
+ if (lck->lockx.in.mode & LOCKING_ANDX_OPLOCK_RELEASE) {
+ return pvfs_oplock_release(ntvfs, req, lck);
+ }
+
+ f = pvfs_find_fd(pvfs, req, lck->lockx.in.file.ntvfs);
+ if (!f) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ if (f->handle->fd == -1) {
+ return NT_STATUS_FILE_IS_A_DIRECTORY;
+ }
+
+ status = pvfs_break_level2_oplocks(f);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ if (lck->lockx.in.timeout != 0 &&
+ (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ pending = talloc(f, struct pvfs_pending_lock);
+ if (pending == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ pending->pvfs = pvfs;
+ pending->lck = lck;
+ pending->f = f;
+ pending->req = req;
+
+ pending->end_time =
+ timeval_current_ofs(lck->lockx.in.timeout/1000,
+ 1000*(lck->lockx.in.timeout%1000));
+ }
+
+ if (lck->lockx.in.mode & LOCKING_ANDX_SHARED_LOCK) {
+ rw = pending? PENDING_READ_LOCK : READ_LOCK;
+ } else {
+ rw = pending? PENDING_WRITE_LOCK : WRITE_LOCK;
+ }
+
+ if (lck->lockx.in.mode & LOCKING_ANDX_CANCEL_LOCK) {
+ talloc_free(pending);
+ return pvfs_lock_cancel(pvfs, req, lck, f);
+ }
+
+ if (lck->lockx.in.mode & LOCKING_ANDX_CHANGE_LOCKTYPE) {
+ /* this seems to not be supported by any windows server,
+ or used by any clients */
+ talloc_free(pending);
+ return NT_STATUS_DOS(ERRDOS, ERRnoatomiclocks);
+ }
+
+ /* the unlocks happen first */
+ locks = lck->lockx.in.locks;
+
+ for (i=0;i<lck->lockx.in.ulock_cnt;i++) {
+ status = brl_unlock(pvfs->brl_context,
+ f->brl_handle,
+ locks[i].pid,
+ locks[i].offset,
+ locks[i].count);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(pending);
+ return status;
+ }
+ f->lock_count--;
+ }
+
+ locks += i;
+
+ for (i=0;i<lck->lockx.in.lock_cnt;i++) {
+ if (pending) {
+ pending->pending_lock = i;
+ }
+
+ status = brl_lock(pvfs->brl_context,
+ f->brl_handle,
+ locks[i].pid,
+ locks[i].offset,
+ locks[i].count,
+ rw, pending);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (pending) {
+ /* a timed lock failed - setup a wait message to handle
+ the pending lock notification or a timeout */
+ pending->wait_handle = pvfs_wait_message(pvfs, req, MSG_BRL_RETRY,
+ pending->end_time,
+ pvfs_pending_lock_continue,
+ pending);
+ if (pending->wait_handle == NULL) {
+ talloc_free(pending);
+ return NT_STATUS_NO_MEMORY;
+ }
+ talloc_steal(pending, pending->wait_handle);
+ DLIST_ADD(f->pending_list, pending);
+ return NT_STATUS_OK;
+ }
+
+ /* undo the locks we just did */
+ for (i--;i>=0;i--) {
+ brl_unlock(pvfs->brl_context,
+ f->brl_handle,
+ locks[i].pid,
+ locks[i].offset,
+ locks[i].count);
+ f->lock_count--;
+ }
+ talloc_free(pending);
+ return status;
+ }
+ f->lock_count++;
+ }
+
+ talloc_free(pending);
+ return NT_STATUS_OK;
+}
diff --git a/source4/ntvfs/posix/pvfs_mkdir.c b/source4/ntvfs/posix/pvfs_mkdir.c
new file mode 100644
index 0000000000..6ec9e60955
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_mkdir.c
@@ -0,0 +1,196 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - mkdir and rmdir
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/dir.h"
+#include "vfs_posix.h"
+#include "librpc/gen_ndr/security.h"
+
+/*
+ create a directory with EAs
+*/
+static NTSTATUS pvfs_t2mkdir(struct pvfs_state *pvfs,
+ struct ntvfs_request *req, union smb_mkdir *md)
+{
+ NTSTATUS status;
+ struct pvfs_filename *name;
+ mode_t mode;
+
+ /* resolve the cifs name to a posix name */
+ status = pvfs_resolve_name(pvfs, req, md->t2mkdir.in.path, 0, &name);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (name->exists) {
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ status = pvfs_access_check_parent(pvfs, req, name, SEC_DIR_ADD_FILE);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ mode = pvfs_fileperms(pvfs, FILE_ATTRIBUTE_DIRECTORY);
+
+ if (mkdir(name->full_name, mode) == -1) {
+ return pvfs_map_errno(pvfs, errno);
+ }
+
+ pvfs_xattr_unlink_hook(pvfs, name->full_name);
+
+ status = pvfs_resolve_name(pvfs, req, md->t2mkdir.in.path, 0, &name);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (!name->exists ||
+ !(name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ /* setup an inherited acl from the parent */
+ status = pvfs_acl_inherit(pvfs, req, name, -1);
+ if (!NT_STATUS_IS_OK(status)) {
+ rmdir(name->full_name);
+ return status;
+ }
+
+ /* setup any EAs that were asked for */
+ status = pvfs_setfileinfo_ea_set(pvfs, name, -1,
+ md->t2mkdir.in.num_eas,
+ md->t2mkdir.in.eas);
+ if (!NT_STATUS_IS_OK(status)) {
+ rmdir(name->full_name);
+ return status;
+ }
+
+ notify_trigger(pvfs->notify_context,
+ NOTIFY_ACTION_ADDED,
+ FILE_NOTIFY_CHANGE_DIR_NAME,
+ name->full_name);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ create a directory
+*/
+NTSTATUS pvfs_mkdir(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_mkdir *md)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ NTSTATUS status;
+ struct pvfs_filename *name;
+ mode_t mode;
+
+ if (md->generic.level == RAW_MKDIR_T2MKDIR) {
+ return pvfs_t2mkdir(pvfs, req, md);
+ }
+
+ if (md->generic.level != RAW_MKDIR_MKDIR) {
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ /* resolve the cifs name to a posix name */
+ status = pvfs_resolve_name(pvfs, req, md->mkdir.in.path, 0, &name);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (name->exists) {
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ status = pvfs_access_check_parent(pvfs, req, name, SEC_DIR_ADD_FILE);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ mode = pvfs_fileperms(pvfs, FILE_ATTRIBUTE_DIRECTORY);
+
+ if (mkdir(name->full_name, mode) == -1) {
+ return pvfs_map_errno(pvfs, errno);
+ }
+
+ pvfs_xattr_unlink_hook(pvfs, name->full_name);
+
+ /* setup an inherited acl from the parent */
+ status = pvfs_acl_inherit(pvfs, req, name, -1);
+ if (!NT_STATUS_IS_OK(status)) {
+ rmdir(name->full_name);
+ return status;
+ }
+
+ notify_trigger(pvfs->notify_context,
+ NOTIFY_ACTION_ADDED,
+ FILE_NOTIFY_CHANGE_DIR_NAME,
+ name->full_name);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ remove a directory
+*/
+NTSTATUS pvfs_rmdir(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, struct smb_rmdir *rd)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ NTSTATUS status;
+ struct pvfs_filename *name;
+
+ /* resolve the cifs name to a posix name */
+ status = pvfs_resolve_name(pvfs, req, rd->in.path, 0, &name);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (!name->exists) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ status = pvfs_access_check_simple(pvfs, req, name, SEC_STD_DELETE);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = pvfs_xattr_unlink_hook(pvfs, name->full_name);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (rmdir(name->full_name) == -1) {
+ /* some olders systems don't return ENOTEMPTY to rmdir() */
+ if (errno == EEXIST) {
+ return NT_STATUS_DIRECTORY_NOT_EMPTY;
+ }
+ return pvfs_map_errno(pvfs, errno);
+ }
+
+ notify_trigger(pvfs->notify_context,
+ NOTIFY_ACTION_REMOVED,
+ FILE_NOTIFY_CHANGE_DIR_NAME,
+ name->full_name);
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/ntvfs/posix/pvfs_notify.c b/source4/ntvfs/posix/pvfs_notify.c
new file mode 100644
index 0000000000..09aa0f64e6
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_notify.c
@@ -0,0 +1,285 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - notify
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "vfs_posix.h"
+#include "lib/messaging/irpc.h"
+#include "messaging/messaging.h"
+#include "../lib/util/dlinklist.h"
+#include "lib/events/events.h"
+
+/* pending notifies buffer, hung off struct pvfs_file for open directories
+ that have used change notify */
+struct pvfs_notify_buffer {
+ struct pvfs_file *f;
+ uint32_t num_changes;
+ struct notify_changes *changes;
+ uint32_t max_buffer_size;
+ uint32_t current_buffer_size;
+
+ /* a list of requests waiting for events on this handle */
+ struct notify_pending {
+ struct notify_pending *next, *prev;
+ struct ntvfs_request *req;
+ union smb_notify *info;
+ } *pending;
+};
+
+/*
+ send a notify on the next event run.
+*/
+static void pvfs_notify_send_next(struct tevent_context *ev, struct tevent_timer *te,
+ struct timeval t, void *ptr)
+{
+ struct ntvfs_request *req = talloc_get_type(ptr, struct ntvfs_request);
+ req->async_states->send_fn(req);
+}
+
+
+/*
+ send a reply to a pending notify request
+*/
+static void pvfs_notify_send(struct pvfs_notify_buffer *notify_buffer,
+ NTSTATUS status, bool immediate)
+{
+ struct notify_pending *pending = notify_buffer->pending;
+ struct ntvfs_request *req;
+ union smb_notify *info;
+
+ if (notify_buffer->current_buffer_size > notify_buffer->max_buffer_size &&
+ notify_buffer->num_changes != 0) {
+ /* on buffer overflow return no changes and destroys the notify buffer */
+ notify_buffer->num_changes = 0;
+ while (notify_buffer->pending) {
+ pvfs_notify_send(notify_buffer, NT_STATUS_OK, immediate);
+ }
+ talloc_free(notify_buffer);
+ return;
+ }
+
+ /* see if there is anyone waiting */
+ if (notify_buffer->pending == NULL) {
+ return;
+ }
+
+ DLIST_REMOVE(notify_buffer->pending, pending);
+
+ req = pending->req;
+ info = pending->info;
+
+ info->nttrans.out.num_changes = notify_buffer->num_changes;
+ info->nttrans.out.changes = talloc_steal(req, notify_buffer->changes);
+ notify_buffer->num_changes = 0;
+ notify_buffer->changes = NULL;
+ notify_buffer->current_buffer_size = 0;
+
+ talloc_free(pending);
+
+ if (info->nttrans.out.num_changes != 0) {
+ status = NT_STATUS_OK;
+ }
+
+ req->async_states->status = status;
+
+ if (immediate) {
+ req->async_states->send_fn(req);
+ return;
+ }
+
+ /* we can't call pvfs_notify_send() directly here, as that
+ would free the request, and the ntvfs modules above us
+ could use it, so call it on the next event */
+ event_add_timed(req->ctx->event_ctx,
+ req, timeval_zero(), pvfs_notify_send_next, req);
+}
+
+/*
+ destroy a notify buffer. Called when the handle is closed
+ */
+static int pvfs_notify_destructor(struct pvfs_notify_buffer *n)
+{
+ notify_remove(n->f->pvfs->notify_context, n);
+ n->f->notify_buffer = NULL;
+ pvfs_notify_send(n, NT_STATUS_OK, true);
+ return 0;
+}
+
+
+/*
+ called when a async notify event comes in
+*/
+static void pvfs_notify_callback(void *private_data, const struct notify_event *ev)
+{
+ struct pvfs_notify_buffer *n = talloc_get_type(private_data, struct pvfs_notify_buffer);
+ size_t len;
+ struct notify_changes *n2;
+ char *new_path;
+
+ n2 = talloc_realloc(n, n->changes, struct notify_changes, n->num_changes+1);
+ if (n2 == NULL) {
+ /* nothing much we can do for this */
+ return;
+ }
+ n->changes = n2;
+
+ new_path = talloc_strdup(n->changes, ev->path);
+ if (new_path == NULL) {
+ return;
+ }
+ string_replace(new_path, '/', '\\');
+
+ n->changes[n->num_changes].action = ev->action;
+ n->changes[n->num_changes].name.s = new_path;
+ n->num_changes++;
+
+ /*
+ work out how much room this will take in the buffer
+ */
+ len = 12 + strlen_m(ev->path)*2;
+ if (len & 3) {
+ len += 4 - (len & 3);
+ }
+ n->current_buffer_size += len;
+
+ /* send what we have, unless its the first part of a rename */
+ if (ev->action != NOTIFY_ACTION_OLD_NAME) {
+ pvfs_notify_send(n, NT_STATUS_OK, true);
+ }
+}
+
+/*
+ setup a notify buffer on a directory handle
+*/
+static NTSTATUS pvfs_notify_setup(struct pvfs_state *pvfs, struct pvfs_file *f,
+ uint32_t buffer_size, uint32_t filter, bool recursive)
+{
+ NTSTATUS status;
+ struct notify_entry e;
+
+ f->notify_buffer = talloc_zero(f, struct pvfs_notify_buffer);
+ NT_STATUS_HAVE_NO_MEMORY(f->notify_buffer);
+
+ f->notify_buffer->max_buffer_size = buffer_size;
+ f->notify_buffer->f = f;
+
+ e.filter = filter;
+ e.path = f->handle->name->full_name;
+ if (recursive) {
+ e.subdir_filter = filter;
+ } else {
+ e.subdir_filter = 0;
+ }
+
+ status = notify_add(pvfs->notify_context, &e,
+ pvfs_notify_callback, f->notify_buffer);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ talloc_set_destructor(f->notify_buffer, pvfs_notify_destructor);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ called from the pvfs_wait code when either an event has come in, or
+ the notify request has been cancelled
+*/
+static void pvfs_notify_end(void *private_data, enum pvfs_wait_notice reason)
+{
+ struct pvfs_notify_buffer *notify_buffer = talloc_get_type(private_data,
+ struct pvfs_notify_buffer);
+ if (reason == PVFS_WAIT_CANCEL) {
+ pvfs_notify_send(notify_buffer, NT_STATUS_CANCELLED, false);
+ } else {
+ pvfs_notify_send(notify_buffer, NT_STATUS_OK, true);
+ }
+}
+
+/* change notify request - always async. This request blocks until the
+ event buffer is non-empty */
+NTSTATUS pvfs_notify(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_notify *info)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ struct pvfs_file *f;
+ NTSTATUS status;
+ struct notify_pending *pending;
+
+ if (info->nttrans.level != RAW_NOTIFY_NTTRANS) {
+ return ntvfs_map_notify(ntvfs, req, info);
+ }
+
+ f = pvfs_find_fd(pvfs, req, info->nttrans.in.file.ntvfs);
+ if (!f) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ /* this request doesn't make sense unless its async */
+ if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* its only valid for directories */
+ if (f->handle->fd != -1) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* if the handle doesn't currently have a notify buffer then
+ create one */
+ if (f->notify_buffer == NULL) {
+ status = pvfs_notify_setup(pvfs, f,
+ info->nttrans.in.buffer_size,
+ info->nttrans.in.completion_filter,
+ info->nttrans.in.recursive);
+ NT_STATUS_NOT_OK_RETURN(status);
+ }
+
+ /* we update the max_buffer_size on each call, but we do not
+ update the recursive flag or filter */
+ f->notify_buffer->max_buffer_size = info->nttrans.in.buffer_size;
+
+ pending = talloc(f->notify_buffer, struct notify_pending);
+ NT_STATUS_HAVE_NO_MEMORY(pending);
+
+ pending->req = talloc_reference(pending, req);
+ NT_STATUS_HAVE_NO_MEMORY(pending->req);
+ pending->info = info;
+
+ DLIST_ADD_END(f->notify_buffer->pending, pending, struct notify_pending *);
+
+ /* if the buffer is empty then start waiting */
+ if (f->notify_buffer->num_changes == 0) {
+ struct pvfs_wait *wait_handle;
+ wait_handle = pvfs_wait_message(pvfs, req, -1,
+ timeval_zero(),
+ pvfs_notify_end,
+ f->notify_buffer);
+ NT_STATUS_HAVE_NO_MEMORY(wait_handle);
+ talloc_steal(req, wait_handle);
+ return NT_STATUS_OK;
+ }
+
+ req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
+ pvfs_notify_send(f->notify_buffer, NT_STATUS_OK, false);
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/ntvfs/posix/pvfs_open.c b/source4/ntvfs/posix/pvfs_open.c
new file mode 100644
index 0000000000..12f50fcc97
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_open.c
@@ -0,0 +1,2024 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - open and close
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "vfs_posix.h"
+#include "system/dir.h"
+#include "system/time.h"
+#include "../lib/util/dlinklist.h"
+#include "messaging/messaging.h"
+#include "librpc/gen_ndr/xattr.h"
+
+/*
+ find open file handle given fnum
+*/
+struct pvfs_file *pvfs_find_fd(struct pvfs_state *pvfs,
+ struct ntvfs_request *req, struct ntvfs_handle *h)
+{
+ void *p;
+ struct pvfs_file *f;
+
+ p = ntvfs_handle_get_backend_data(h, pvfs->ntvfs);
+ if (!p) return NULL;
+
+ f = talloc_get_type(p, struct pvfs_file);
+ if (!f) return NULL;
+
+ return f;
+}
+
+/*
+ cleanup a open directory handle
+*/
+static int pvfs_dir_handle_destructor(struct pvfs_file_handle *h)
+{
+ if (h->have_opendb_entry) {
+ struct odb_lock *lck;
+ NTSTATUS status;
+ const char *delete_path = NULL;
+
+ lck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
+ if (lck == NULL) {
+ DEBUG(0,("Unable to lock opendb for close\n"));
+ return 0;
+ }
+
+ status = odb_close_file(lck, h, &delete_path);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Unable to remove opendb entry for '%s' - %s\n",
+ h->name->full_name, nt_errstr(status)));
+ }
+
+ if (h->name->stream_name == NULL && delete_path) {
+ status = pvfs_xattr_unlink_hook(h->pvfs, delete_path);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Warning: xattr unlink hook failed for '%s' - %s\n",
+ delete_path, nt_errstr(status)));
+ }
+ if (rmdir(delete_path) != 0) {
+ DEBUG(0,("pvfs_dir_handle_destructor: failed to rmdir '%s' - %s\n",
+ delete_path, strerror(errno)));
+ }
+ }
+
+ talloc_free(lck);
+ }
+
+ return 0;
+}
+
+/*
+ cleanup a open directory fnum
+*/
+static int pvfs_dir_fnum_destructor(struct pvfs_file *f)
+{
+ DLIST_REMOVE(f->pvfs->files.list, f);
+ ntvfs_handle_remove_backend_data(f->ntvfs, f->pvfs->ntvfs);
+
+ return 0;
+}
+
+/*
+ setup any EAs and the ACL on newly created files/directories
+*/
+static NTSTATUS pvfs_open_setup_eas_acl(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
+ struct pvfs_filename *name,
+ int fd, struct pvfs_file *f,
+ union smb_open *io)
+{
+ NTSTATUS status;
+ struct security_descriptor *sd;
+
+ /* setup any EAs that were asked for */
+ if (io->ntcreatex.in.ea_list) {
+ status = pvfs_setfileinfo_ea_set(pvfs, name, fd,
+ io->ntcreatex.in.ea_list->num_eas,
+ io->ntcreatex.in.ea_list->eas);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ sd = io->ntcreatex.in.sec_desc;
+ /* setup an initial sec_desc if requested */
+ if (sd && (sd->type & SEC_DESC_DACL_PRESENT)) {
+ union smb_setfileinfo set;
+/*
+ * TODO: set the full ACL!
+ * - vista denies the creation of the file with NT_STATUS_PRIVILEGE_NOT_HELD,
+ * when a SACL is present on the sd,
+ * but the user doesn't have SeSecurityPrivilege
+ * - w2k3 allows it
+ */
+ set.set_secdesc.in.file.ntvfs = f->ntvfs;
+ set.set_secdesc.in.secinfo_flags = SECINFO_DACL;
+ set.set_secdesc.in.sd = sd;
+
+ status = pvfs_acl_set(pvfs, req, name, fd, SEC_STD_WRITE_DAC, &set);
+ } else {
+ /* otherwise setup an inherited acl from the parent */
+ status = pvfs_acl_inherit(pvfs, req, name, fd);
+ }
+
+ return status;
+}
+
+/*
+ form the lock context used for opendb locking. Note that we must
+ zero here to take account of possible padding on some architectures
+*/
+NTSTATUS pvfs_locking_key(struct pvfs_filename *name,
+ TALLOC_CTX *mem_ctx, DATA_BLOB *key)
+{
+ struct {
+ dev_t device;
+ ino_t inode;
+ } lock_context;
+ ZERO_STRUCT(lock_context);
+
+ lock_context.device = name->st.st_dev;
+ lock_context.inode = name->st.st_ino;
+
+ *key = data_blob_talloc(mem_ctx, &lock_context, sizeof(lock_context));
+ if (key->data == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ open a directory
+*/
+static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
+ struct pvfs_filename *name,
+ union smb_open *io)
+{
+ struct pvfs_file *f;
+ struct ntvfs_handle *h;
+ NTSTATUS status;
+ uint32_t create_action;
+ uint32_t access_mask = io->generic.in.access_mask;
+ struct odb_lock *lck;
+ bool del_on_close;
+ uint32_t create_options;
+ uint32_t share_access;
+ bool forced;
+
+ create_options = io->generic.in.create_options;
+ share_access = io->generic.in.share_access;
+
+ forced = (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY)?true:false;
+
+ if (name->stream_name) {
+ if (forced) {
+ return NT_STATUS_NOT_A_DIRECTORY;
+ } else {
+ return NT_STATUS_FILE_IS_A_DIRECTORY;
+ }
+ }
+
+ /* if the client says it must be a directory, and it isn't,
+ then fail */
+ if (name->exists && !(name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
+ return NT_STATUS_NOT_A_DIRECTORY;
+ }
+
+ /* found with gentest */
+ if (io->ntcreatex.in.access_mask == SEC_FLAG_MAXIMUM_ALLOWED &&
+ (io->ntcreatex.in.create_options & NTCREATEX_OPTIONS_DIRECTORY) &&
+ (io->ntcreatex.in.create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ switch (io->generic.in.open_disposition) {
+ case NTCREATEX_DISP_OPEN_IF:
+ break;
+
+ case NTCREATEX_DISP_OPEN:
+ if (!name->exists) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+ break;
+
+ case NTCREATEX_DISP_CREATE:
+ if (name->exists) {
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+ break;
+
+ case NTCREATEX_DISP_OVERWRITE_IF:
+ case NTCREATEX_DISP_OVERWRITE:
+ case NTCREATEX_DISP_SUPERSEDE:
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = ntvfs_handle_new(pvfs->ntvfs, req, &h);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ f = talloc(h, struct pvfs_file);
+ if (f == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ f->handle = talloc(f, struct pvfs_file_handle);
+ if (f->handle == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (name->exists) {
+ /* check the security descriptor */
+ status = pvfs_access_check(pvfs, req, name, &access_mask);
+ } else {
+ status = pvfs_access_check_create(pvfs, req, name, &access_mask);
+ }
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ if (io->generic.in.query_maximal_access) {
+ status = pvfs_access_maximal_allowed(pvfs, req, name,
+ &io->generic.out.maximal_access);
+ NT_STATUS_NOT_OK_RETURN(status);
+ }
+
+ f->ntvfs = h;
+ f->pvfs = pvfs;
+ f->pending_list = NULL;
+ f->lock_count = 0;
+ f->share_access = io->generic.in.share_access;
+ f->impersonation = io->generic.in.impersonation;
+ f->access_mask = access_mask;
+ f->brl_handle = NULL;
+ f->notify_buffer = NULL;
+ f->search = NULL;
+
+ f->handle->pvfs = pvfs;
+ f->handle->name = talloc_steal(f->handle, name);
+ f->handle->fd = -1;
+ f->handle->odb_locking_key = data_blob(NULL, 0);
+ f->handle->create_options = io->generic.in.create_options;
+ f->handle->seek_offset = 0;
+ f->handle->position = 0;
+ f->handle->mode = 0;
+ f->handle->oplock = NULL;
+ ZERO_STRUCT(f->handle->write_time);
+ f->handle->open_completed = false;
+
+ if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
+ pvfs_directory_empty(pvfs, f->handle->name)) {
+ del_on_close = true;
+ } else {
+ del_on_close = false;
+ }
+
+ if (name->exists) {
+ /* form the lock context used for opendb locking */
+ status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* get a lock on this file before the actual open */
+ lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
+ if (lck == NULL) {
+ DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
+ name->full_name));
+ /* we were supposed to do a blocking lock, so something
+ is badly wrong! */
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ /* see if we are allowed to open at the same time as existing opens */
+ status = odb_can_open(lck, name->stream_id,
+ share_access, access_mask, del_on_close,
+ io->generic.in.open_disposition, false);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lck);
+ return status;
+ }
+
+ /* now really mark the file as open */
+ status = odb_open_file(lck, f->handle, name->full_name,
+ NULL, name->dos.write_time,
+ false, OPLOCK_NONE, NULL);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lck);
+ return status;
+ }
+
+ f->handle->have_opendb_entry = true;
+ }
+
+ DLIST_ADD(pvfs->files.list, f);
+
+ /* setup destructors to avoid leaks on abnormal termination */
+ talloc_set_destructor(f->handle, pvfs_dir_handle_destructor);
+ talloc_set_destructor(f, pvfs_dir_fnum_destructor);
+
+ if (!name->exists) {
+ uint32_t attrib = io->generic.in.file_attr | FILE_ATTRIBUTE_DIRECTORY;
+ mode_t mode = pvfs_fileperms(pvfs, attrib);
+
+ if (mkdir(name->full_name, mode) == -1) {
+ return pvfs_map_errno(pvfs,errno);
+ }
+
+ pvfs_xattr_unlink_hook(pvfs, name->full_name);
+
+ status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, 0, &name);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto cleanup_delete;
+ }
+
+ status = pvfs_open_setup_eas_acl(pvfs, req, name, -1, f, io);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto cleanup_delete;
+ }
+
+ /* form the lock context used for opendb locking */
+ status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
+ if (lck == NULL) {
+ DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
+ name->full_name));
+ /* we were supposed to do a blocking lock, so something
+ is badly wrong! */
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ status = odb_can_open(lck, name->stream_id,
+ share_access, access_mask, del_on_close,
+ io->generic.in.open_disposition, false);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto cleanup_delete;
+ }
+
+ status = odb_open_file(lck, f->handle, name->full_name,
+ NULL, name->dos.write_time,
+ false, OPLOCK_NONE, NULL);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto cleanup_delete;
+ }
+
+ f->handle->have_opendb_entry = true;
+
+ create_action = NTCREATEX_ACTION_CREATED;
+
+ notify_trigger(pvfs->notify_context,
+ NOTIFY_ACTION_ADDED,
+ FILE_NOTIFY_CHANGE_DIR_NAME,
+ name->full_name);
+ } else {
+ create_action = NTCREATEX_ACTION_EXISTED;
+ }
+
+ if (!name->exists) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ /* the open succeeded, keep this handle permanently */
+ status = ntvfs_handle_set_backend_data(h, pvfs->ntvfs, f);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto cleanup_delete;
+ }
+
+ f->handle->open_completed = true;
+
+ io->generic.out.oplock_level = OPLOCK_NONE;
+ io->generic.out.file.ntvfs = h;
+ io->generic.out.create_action = create_action;
+ io->generic.out.create_time = name->dos.create_time;
+ io->generic.out.access_time = name->dos.access_time;
+ io->generic.out.write_time = name->dos.write_time;
+ io->generic.out.change_time = name->dos.change_time;
+ io->generic.out.attrib = name->dos.attrib;
+ io->generic.out.alloc_size = name->dos.alloc_size;
+ io->generic.out.size = name->st.st_size;
+ io->generic.out.file_type = FILE_TYPE_DISK;
+ io->generic.out.ipc_state = 0;
+ io->generic.out.is_directory = 1;
+
+ return NT_STATUS_OK;
+
+cleanup_delete:
+ rmdir(name->full_name);
+ return status;
+}
+
+/*
+ destroy a struct pvfs_file_handle
+*/
+static int pvfs_handle_destructor(struct pvfs_file_handle *h)
+{
+ talloc_free(h->write_time.update_event);
+ h->write_time.update_event = NULL;
+
+ if ((h->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
+ h->name->stream_name) {
+ NTSTATUS status;
+ status = pvfs_stream_delete(h->pvfs, h->name, h->fd);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Failed to delete stream '%s' on close of '%s'\n",
+ h->name->stream_name, h->name->full_name));
+ }
+ }
+
+ if (h->fd != -1) {
+ if (close(h->fd) != 0) {
+ DEBUG(0,("pvfs_handle_destructor: close(%d) failed for %s - %s\n",
+ h->fd, h->name->full_name, strerror(errno)));
+ }
+ h->fd = -1;
+ }
+
+ if (!h->write_time.update_forced &&
+ h->write_time.update_on_close &&
+ h->write_time.close_time == 0) {
+ struct timeval tv;
+ tv = timeval_current();
+ h->write_time.close_time = timeval_to_nttime(&tv);
+ }
+
+ if (h->have_opendb_entry) {
+ struct odb_lock *lck;
+ NTSTATUS status;
+ const char *delete_path = NULL;
+
+ lck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
+ if (lck == NULL) {
+ DEBUG(0,("Unable to lock opendb for close\n"));
+ return 0;
+ }
+
+ if (h->write_time.update_forced) {
+ status = odb_get_file_infos(h->pvfs->odb_context,
+ &h->odb_locking_key,
+ NULL,
+ &h->write_time.close_time);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Unable get write time for '%s' - %s\n",
+ h->name->full_name, nt_errstr(status)));
+ }
+
+ h->write_time.update_forced = false;
+ h->write_time.update_on_close = true;
+ } else if (h->write_time.update_on_close) {
+ status = odb_set_write_time(lck, h->write_time.close_time, true);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Unable set write time for '%s' - %s\n",
+ h->name->full_name, nt_errstr(status)));
+ }
+ }
+
+ status = odb_close_file(lck, h, &delete_path);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Unable to remove opendb entry for '%s' - %s\n",
+ h->name->full_name, nt_errstr(status)));
+ }
+
+ if (h->name->stream_name == NULL &&
+ h->open_completed && delete_path) {
+ status = pvfs_xattr_unlink_hook(h->pvfs, delete_path);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Warning: xattr unlink hook failed for '%s' - %s\n",
+ delete_path, nt_errstr(status)));
+ }
+ if (unlink(delete_path) != 0) {
+ DEBUG(0,("pvfs_close: failed to delete '%s' - %s\n",
+ delete_path, strerror(errno)));
+ } else {
+ notify_trigger(h->pvfs->notify_context,
+ NOTIFY_ACTION_REMOVED,
+ FILE_NOTIFY_CHANGE_FILE_NAME,
+ delete_path);
+ }
+ h->write_time.update_on_close = false;
+ }
+
+ talloc_free(lck);
+ }
+
+ if (h->write_time.update_on_close) {
+ struct timeval tv[2];
+
+ nttime_to_timeval(&tv[0], h->name->dos.access_time);
+ nttime_to_timeval(&tv[1], h->write_time.close_time);
+
+ if (!timeval_is_zero(&tv[0]) || !timeval_is_zero(&tv[1])) {
+ if (utimes(h->name->full_name, tv) == -1) {
+ DEBUG(0,("pvfs_handle_destructor: utimes() failed '%s' - %s\n",
+ h->name->full_name, strerror(errno)));
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+/*
+ destroy a struct pvfs_file
+*/
+static int pvfs_fnum_destructor(struct pvfs_file *f)
+{
+ DLIST_REMOVE(f->pvfs->files.list, f);
+ pvfs_lock_close(f->pvfs, f);
+ ntvfs_handle_remove_backend_data(f->ntvfs, f->pvfs->ntvfs);
+
+ return 0;
+}
+
+
+/*
+ form the lock context used for byte range locking. This is separate
+ from the locking key used for opendb locking as it needs to take
+ account of file streams (each stream is a separate byte range
+ locking space)
+*/
+static NTSTATUS pvfs_brl_locking_handle(TALLOC_CTX *mem_ctx,
+ struct pvfs_filename *name,
+ struct ntvfs_handle *ntvfs,
+ struct brl_handle **_h)
+{
+ DATA_BLOB odb_key, key;
+ NTSTATUS status;
+ struct brl_handle *h;
+
+ status = pvfs_locking_key(name, mem_ctx, &odb_key);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ if (name->stream_name == NULL) {
+ key = odb_key;
+ } else {
+ key = data_blob_talloc(mem_ctx, NULL,
+ odb_key.length + strlen(name->stream_name) + 1);
+ NT_STATUS_HAVE_NO_MEMORY(key.data);
+ memcpy(key.data, odb_key.data, odb_key.length);
+ memcpy(key.data + odb_key.length,
+ name->stream_name, strlen(name->stream_name) + 1);
+ data_blob_free(&odb_key);
+ }
+
+ h = brl_create_handle(mem_ctx, ntvfs, &key);
+ NT_STATUS_HAVE_NO_MEMORY(h);
+
+ *_h = h;
+ return NT_STATUS_OK;
+}
+
+/*
+ create a new file
+*/
+static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
+ struct pvfs_filename *name,
+ union smb_open *io)
+{
+ struct pvfs_file *f;
+ NTSTATUS status;
+ struct ntvfs_handle *h;
+ int flags, fd;
+ struct odb_lock *lck;
+ uint32_t create_options = io->generic.in.create_options;
+ uint32_t share_access = io->generic.in.share_access;
+ uint32_t access_mask = io->generic.in.access_mask;
+ mode_t mode;
+ uint32_t attrib;
+ bool del_on_close;
+ struct pvfs_filename *parent;
+ uint32_t oplock_level = OPLOCK_NONE, oplock_granted;
+ bool allow_level_II_oplock = false;
+
+ if (io->ntcreatex.in.file_attr & ~FILE_ATTRIBUTE_ALL_MASK) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (io->ntcreatex.in.file_attr & FILE_ATTRIBUTE_ENCRYPTED) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if ((io->ntcreatex.in.file_attr & FILE_ATTRIBUTE_READONLY) &&
+ (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
+ return NT_STATUS_CANNOT_DELETE;
+ }
+
+ status = pvfs_access_check_create(pvfs, req, name, &access_mask);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ /* check that the parent isn't opened with delete on close set */
+ status = pvfs_resolve_parent(pvfs, req, name, &parent);
+ if (NT_STATUS_IS_OK(status)) {
+ DATA_BLOB locking_key;
+ status = pvfs_locking_key(parent, req, &locking_key);
+ NT_STATUS_NOT_OK_RETURN(status);
+ status = odb_get_file_infos(pvfs->odb_context, &locking_key,
+ &del_on_close, NULL);
+ NT_STATUS_NOT_OK_RETURN(status);
+ if (del_on_close) {
+ return NT_STATUS_DELETE_PENDING;
+ }
+ }
+
+ if (access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)) {
+ flags = O_RDWR;
+ } else {
+ flags = O_RDONLY;
+ }
+
+ status = ntvfs_handle_new(pvfs->ntvfs, req, &h);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ f = talloc(h, struct pvfs_file);
+ NT_STATUS_HAVE_NO_MEMORY(f);
+
+ f->handle = talloc(f, struct pvfs_file_handle);
+ NT_STATUS_HAVE_NO_MEMORY(f->handle);
+
+ attrib = io->ntcreatex.in.file_attr | FILE_ATTRIBUTE_ARCHIVE;
+ mode = pvfs_fileperms(pvfs, attrib);
+
+ /* create the file */
+ fd = open(name->full_name, flags | O_CREAT | O_EXCL| O_NONBLOCK, mode);
+ if (fd == -1) {
+ return pvfs_map_errno(pvfs, errno);
+ }
+
+ pvfs_xattr_unlink_hook(pvfs, name->full_name);
+
+ /* if this was a stream create then create the stream as well */
+ if (name->stream_name) {
+ status = pvfs_stream_create(pvfs, name, fd);
+ if (!NT_STATUS_IS_OK(status)) {
+ close(fd);
+ return status;
+ }
+ }
+
+ /* re-resolve the open fd */
+ status = pvfs_resolve_name_fd(pvfs, fd, name, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ close(fd);
+ return status;
+ }
+
+ /* support initial alloc sizes */
+ name->dos.alloc_size = io->ntcreatex.in.alloc_size;
+ name->dos.attrib = attrib;
+ status = pvfs_dosattrib_save(pvfs, name, fd);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto cleanup_delete;
+ }
+
+
+ status = pvfs_open_setup_eas_acl(pvfs, req, name, fd, f, io);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto cleanup_delete;
+ }
+
+ if (io->generic.in.query_maximal_access) {
+ status = pvfs_access_maximal_allowed(pvfs, req, name,
+ &io->generic.out.maximal_access);
+ NT_STATUS_NOT_OK_RETURN(status);
+ }
+
+ /* form the lock context used for byte range locking and
+ opendb locking */
+ status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto cleanup_delete;
+ }
+
+ status = pvfs_brl_locking_handle(f, name, h, &f->brl_handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto cleanup_delete;
+ }
+
+ /* grab a lock on the open file record */
+ lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
+ if (lck == NULL) {
+ DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
+ name->full_name));
+ /* we were supposed to do a blocking lock, so something
+ is badly wrong! */
+ status = NT_STATUS_INTERNAL_DB_CORRUPTION;
+ goto cleanup_delete;
+ }
+
+ if (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) {
+ del_on_close = true;
+ } else {
+ del_on_close = false;
+ }
+
+ if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
+ oplock_level = OPLOCK_NONE;
+ } else if (io->ntcreatex.in.flags & NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK) {
+ oplock_level = OPLOCK_BATCH;
+ } else if (io->ntcreatex.in.flags & NTCREATEX_FLAGS_REQUEST_OPLOCK) {
+ oplock_level = OPLOCK_EXCLUSIVE;
+ }
+
+ if (req->client_caps & NTVFS_CLIENT_CAP_LEVEL_II_OPLOCKS) {
+ allow_level_II_oplock = true;
+ }
+
+ status = odb_can_open(lck, name->stream_id,
+ share_access, access_mask, del_on_close,
+ io->generic.in.open_disposition, false);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lck);
+ /* bad news, we must have hit a race - we don't delete the file
+ here as the most likely scenario is that someone else created
+ the file at the same time */
+ close(fd);
+ return status;
+ }
+
+ f->ntvfs = h;
+ f->pvfs = pvfs;
+ f->pending_list = NULL;
+ f->lock_count = 0;
+ f->share_access = io->generic.in.share_access;
+ f->access_mask = access_mask;
+ f->impersonation = io->generic.in.impersonation;
+ f->notify_buffer = NULL;
+ f->search = NULL;
+
+ f->handle->pvfs = pvfs;
+ f->handle->name = talloc_steal(f->handle, name);
+ f->handle->fd = fd;
+ f->handle->create_options = io->generic.in.create_options;
+ f->handle->seek_offset = 0;
+ f->handle->position = 0;
+ f->handle->mode = 0;
+ f->handle->oplock = NULL;
+ f->handle->have_opendb_entry = true;
+ ZERO_STRUCT(f->handle->write_time);
+ f->handle->open_completed = false;
+
+ status = odb_open_file(lck, f->handle, name->full_name,
+ &f->handle->fd, name->dos.write_time,
+ allow_level_II_oplock,
+ oplock_level, &oplock_granted);
+ talloc_free(lck);
+ if (!NT_STATUS_IS_OK(status)) {
+ /* bad news, we must have hit a race - we don't delete the file
+ here as the most likely scenario is that someone else created
+ the file at the same time */
+ close(fd);
+ return status;
+ }
+
+ DLIST_ADD(pvfs->files.list, f);
+
+ /* setup a destructor to avoid file descriptor leaks on
+ abnormal termination */
+ talloc_set_destructor(f, pvfs_fnum_destructor);
+ talloc_set_destructor(f->handle, pvfs_handle_destructor);
+
+ if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
+ oplock_granted = OPLOCK_BATCH;
+ } else if (oplock_granted != OPLOCK_NONE) {
+ status = pvfs_setup_oplock(f, oplock_granted);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ io->generic.out.oplock_level = oplock_granted;
+ io->generic.out.file.ntvfs = f->ntvfs;
+ io->generic.out.create_action = NTCREATEX_ACTION_CREATED;
+ io->generic.out.create_time = name->dos.create_time;
+ io->generic.out.access_time = name->dos.access_time;
+ io->generic.out.write_time = name->dos.write_time;
+ io->generic.out.change_time = name->dos.change_time;
+ io->generic.out.attrib = name->dos.attrib;
+ io->generic.out.alloc_size = name->dos.alloc_size;
+ io->generic.out.size = name->st.st_size;
+ io->generic.out.file_type = FILE_TYPE_DISK;
+ io->generic.out.ipc_state = 0;
+ io->generic.out.is_directory = 0;
+
+ /* success - keep the file handle */
+ status = ntvfs_handle_set_backend_data(h, pvfs->ntvfs, f);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto cleanup_delete;
+ }
+
+ f->handle->open_completed = true;
+
+ notify_trigger(pvfs->notify_context,
+ NOTIFY_ACTION_ADDED,
+ FILE_NOTIFY_CHANGE_FILE_NAME,
+ name->full_name);
+
+ return NT_STATUS_OK;
+
+cleanup_delete:
+ close(fd);
+ unlink(name->full_name);
+ return status;
+}
+
+/*
+ state of a pending retry
+*/
+struct pvfs_odb_retry {
+ struct ntvfs_module_context *ntvfs;
+ struct ntvfs_request *req;
+ DATA_BLOB odb_locking_key;
+ void *io;
+ void *private_data;
+ void (*callback)(struct pvfs_odb_retry *r,
+ struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ void *io,
+ void *private_data,
+ enum pvfs_wait_notice reason);
+};
+
+/* destroy a pending request */
+static int pvfs_odb_retry_destructor(struct pvfs_odb_retry *r)
+{
+ struct pvfs_state *pvfs = talloc_get_type(r->ntvfs->private_data,
+ struct pvfs_state);
+ if (r->odb_locking_key.data) {
+ struct odb_lock *lck;
+ lck = odb_lock(r->req, pvfs->odb_context, &r->odb_locking_key);
+ if (lck != NULL) {
+ odb_remove_pending(lck, r);
+ }
+ talloc_free(lck);
+ }
+ return 0;
+}
+
+static void pvfs_odb_retry_callback(void *_r, enum pvfs_wait_notice reason)
+{
+ struct pvfs_odb_retry *r = talloc_get_type(_r, struct pvfs_odb_retry);
+
+ if (reason == PVFS_WAIT_EVENT) {
+ /*
+ * The pending odb entry is already removed.
+ * We use a null locking key to indicate this
+ * to the destructor.
+ */
+ data_blob_free(&r->odb_locking_key);
+ }
+
+ r->callback(r, r->ntvfs, r->req, r->io, r->private_data, reason);
+}
+
+/*
+ setup for a retry of a request that was rejected
+ by odb_can_open()
+*/
+NTSTATUS pvfs_odb_retry_setup(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ struct odb_lock *lck,
+ struct timeval end_time,
+ void *io,
+ void *private_data,
+ void (*callback)(struct pvfs_odb_retry *r,
+ struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ void *io,
+ void *private_data,
+ enum pvfs_wait_notice reason))
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ struct pvfs_odb_retry *r;
+ struct pvfs_wait *wait_handle;
+ NTSTATUS status;
+
+ r = talloc(req, struct pvfs_odb_retry);
+ NT_STATUS_HAVE_NO_MEMORY(r);
+
+ r->ntvfs = ntvfs;
+ r->req = req;
+ r->io = io;
+ r->private_data = private_data;
+ r->callback = callback;
+ r->odb_locking_key = odb_get_key(r, lck);
+ if (r->odb_locking_key.data == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* setup a pending lock */
+ status = odb_open_file_pending(lck, r);
+ if (NT_STATUS_EQUAL(NT_STATUS_OBJECT_NAME_NOT_FOUND,status)) {
+ /*
+ * maybe only a unix application
+ * has the file open
+ */
+ data_blob_free(&r->odb_locking_key);
+ } else if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ talloc_free(lck);
+
+ talloc_set_destructor(r, pvfs_odb_retry_destructor);
+
+ wait_handle = pvfs_wait_message(pvfs, req,
+ MSG_PVFS_RETRY_OPEN, end_time,
+ pvfs_odb_retry_callback, r);
+ if (wait_handle == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ talloc_steal(r, wait_handle);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ retry an open after a sharing violation
+*/
+static void pvfs_retry_open_sharing(struct pvfs_odb_retry *r,
+ struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ void *_io,
+ void *private_data,
+ enum pvfs_wait_notice reason)
+{
+ union smb_open *io = talloc_get_type(_io, union smb_open);
+ struct timeval *final_timeout = NULL;
+ NTSTATUS status;
+
+ if (private_data) {
+ final_timeout = talloc_get_type(private_data,
+ struct timeval);
+ }
+
+ /* w2k3 ignores SMBntcancel for outstanding open requests. It's probably
+ just a bug in their server, but we better do the same */
+ if (reason == PVFS_WAIT_CANCEL) {
+ return;
+ }
+
+ if (reason == PVFS_WAIT_TIMEOUT) {
+ if (final_timeout &&
+ !timeval_expired(final_timeout)) {
+ /*
+ * we need to retry periodictly
+ * after an EAGAIN as there's
+ * no way the kernel tell us
+ * an oplock is released.
+ */
+ goto retry;
+ }
+ /* if it timed out, then give the failure
+ immediately */
+ talloc_free(r);
+ req->async_states->status = NT_STATUS_SHARING_VIOLATION;
+ req->async_states->send_fn(req);
+ return;
+ }
+
+retry:
+ talloc_free(r);
+
+ /* try the open again, which could trigger another retry setup
+ if it wants to, so we have to unmark the async flag so we
+ will know if it does a second async reply */
+ req->async_states->state &= ~NTVFS_ASYNC_STATE_ASYNC;
+
+ status = pvfs_open(ntvfs, req, io);
+ if (req->async_states->state & NTVFS_ASYNC_STATE_ASYNC) {
+ /* the 2nd try also replied async, so we don't send
+ the reply yet */
+ return;
+ }
+
+ /* re-mark it async, just in case someone up the chain does
+ paranoid checking */
+ req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
+
+ /* send the reply up the chain */
+ req->async_states->status = status;
+ req->async_states->send_fn(req);
+}
+
+
+/*
+ special handling for openx DENY_DOS semantics
+
+ This function attempts a reference open using an existing handle. If its allowed,
+ then it returns NT_STATUS_OK, otherwise it returns any other code and normal
+ open processing continues.
+*/
+static NTSTATUS pvfs_open_deny_dos(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_open *io,
+ struct pvfs_file *f, struct odb_lock *lck)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ struct pvfs_file *f2;
+ struct pvfs_filename *name;
+ NTSTATUS status;
+
+ /* search for an existing open with the right parameters. Note
+ the magic ntcreatex options flag, which is set in the
+ generic mapping code. This might look ugly, but its
+ actually pretty much now w2k does it internally as well.
+
+ If you look at the BASE-DENYDOS test you will see that a
+ DENY_DOS is a very special case, and in the right
+ circumstances you actually get the _same_ handle back
+ twice, rather than a new handle.
+ */
+ for (f2=pvfs->files.list;f2;f2=f2->next) {
+ if (f2 != f &&
+ f2->ntvfs->session_info == req->session_info &&
+ f2->ntvfs->smbpid == req->smbpid &&
+ (f2->handle->create_options &
+ (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS |
+ NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) &&
+ (f2->access_mask & SEC_FILE_WRITE_DATA) &&
+ strcasecmp_m(f2->handle->name->original_name,
+ io->generic.in.fname)==0) {
+ break;
+ }
+ }
+
+ if (!f2) {
+ return NT_STATUS_SHARING_VIOLATION;
+ }
+
+ /* quite an insane set of semantics ... */
+ if (is_exe_filename(io->generic.in.fname) &&
+ (f2->handle->create_options & NTCREATEX_OPTIONS_PRIVATE_DENY_DOS)) {
+ return NT_STATUS_SHARING_VIOLATION;
+ }
+
+ /*
+ setup a reference to the existing handle
+ */
+ talloc_free(f->handle);
+ f->handle = talloc_reference(f, f2->handle);
+
+ talloc_free(lck);
+
+ name = f->handle->name;
+
+ io->generic.out.oplock_level = OPLOCK_NONE;
+ io->generic.out.file.ntvfs = f->ntvfs;
+ io->generic.out.create_action = NTCREATEX_ACTION_EXISTED;
+ io->generic.out.create_time = name->dos.create_time;
+ io->generic.out.access_time = name->dos.access_time;
+ io->generic.out.write_time = name->dos.write_time;
+ io->generic.out.change_time = name->dos.change_time;
+ io->generic.out.attrib = name->dos.attrib;
+ io->generic.out.alloc_size = name->dos.alloc_size;
+ io->generic.out.size = name->st.st_size;
+ io->generic.out.file_type = FILE_TYPE_DISK;
+ io->generic.out.ipc_state = 0;
+ io->generic.out.is_directory = 0;
+
+ status = ntvfs_handle_set_backend_data(f->ntvfs, ntvfs, f);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ return NT_STATUS_OK;
+}
+
+
+
+/*
+ setup for a open retry after a sharing violation
+*/
+static NTSTATUS pvfs_open_setup_retry(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_open *io,
+ struct pvfs_file *f,
+ struct odb_lock *lck,
+ NTSTATUS parent_status)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ NTSTATUS status;
+ struct timeval end_time;
+ struct timeval *final_timeout = NULL;
+
+ if (io->generic.in.create_options &
+ (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS | NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) {
+ /* see if we can satisfy the request using the special DENY_DOS
+ code */
+ status = pvfs_open_deny_dos(ntvfs, req, io, f, lck);
+ if (NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ /* the retry should allocate a new file handle */
+ talloc_free(f);
+
+ if (NT_STATUS_EQUAL(parent_status, NT_STATUS_SHARING_VIOLATION)) {
+ end_time = timeval_add(&req->statistics.request_time,
+ 0, pvfs->sharing_violation_delay);
+ } else if (NT_STATUS_EQUAL(parent_status, NT_STATUS_OPLOCK_NOT_GRANTED)) {
+ end_time = timeval_add(&req->statistics.request_time,
+ pvfs->oplock_break_timeout, 0);
+ } else if (NT_STATUS_EQUAL(parent_status, STATUS_MORE_ENTRIES)) {
+ /*
+ * we got EAGAIN which means a unix application
+ * has an oplock or share mode
+ *
+ * we retry every 4/5 of the sharing violation delay
+ * to see if the unix application
+ * has released the oplock or share mode.
+ */
+ final_timeout = talloc(req, struct timeval);
+ NT_STATUS_HAVE_NO_MEMORY(final_timeout);
+ *final_timeout = timeval_add(&req->statistics.request_time,
+ pvfs->oplock_break_timeout,
+ 0);
+ end_time = timeval_current_ofs(0, (pvfs->sharing_violation_delay*4)/5);
+ end_time = timeval_min(final_timeout, &end_time);
+ } else {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ return pvfs_odb_retry_setup(ntvfs, req, lck, end_time, io,
+ final_timeout, pvfs_retry_open_sharing);
+}
+
+/*
+ open a file
+*/
+NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_open *io)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ int flags = 0;
+ struct pvfs_filename *name;
+ struct pvfs_file *f;
+ struct ntvfs_handle *h;
+ NTSTATUS status;
+ int fd;
+ struct odb_lock *lck;
+ uint32_t create_options;
+ uint32_t create_options_must_ignore_mask;
+ uint32_t share_access;
+ uint32_t access_mask;
+ uint32_t create_action = NTCREATEX_ACTION_EXISTED;
+ bool del_on_close;
+ bool stream_existed, stream_truncate=false;
+ uint32_t oplock_level = OPLOCK_NONE, oplock_granted;
+ bool allow_level_II_oplock = false;
+
+ /* use the generic mapping code to avoid implementing all the
+ different open calls. */
+ if (io->generic.level != RAW_OPEN_GENERIC &&
+ io->generic.level != RAW_OPEN_NTTRANS_CREATE) {
+ return ntvfs_map_open(ntvfs, req, io);
+ }
+
+ ZERO_STRUCT(io->generic.out);
+
+ create_options = io->generic.in.create_options;
+ share_access = io->generic.in.share_access;
+ access_mask = io->generic.in.access_mask;
+
+ if (share_access & ~NTCREATEX_SHARE_ACCESS_MASK) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /*
+ * These options are ignored,
+ * but we reuse some of them as private values for the generic mapping
+ */
+ create_options_must_ignore_mask = NTCREATEX_OPTIONS_MUST_IGNORE_MASK;
+ create_options_must_ignore_mask &= ~NTCREATEX_OPTIONS_PRIVATE_MASK;
+ create_options &= ~create_options_must_ignore_mask;
+
+ if (create_options & NTCREATEX_OPTIONS_NOT_SUPPORTED_MASK) {
+ DEBUG(2,(__location__ " create_options 0x%x not supported\n",
+ create_options));
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ if (create_options & NTCREATEX_OPTIONS_INVALID_PARAM_MASK) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* TODO: When we implement HSM, add a hook here not to pull
+ * the actual file off tape, when this option is passed from
+ * the client */
+ if (create_options & NTCREATEX_OPTIONS_NO_RECALL) {
+ /* no-op */
+ }
+
+ /* TODO: If (unlikely) Linux does a good compressed
+ * filesystem, we might need an ioctl call for this */
+ if (create_options & NTCREATEX_OPTIONS_NO_COMPRESSION) {
+ /* no-op */
+ }
+
+ if (create_options & NTCREATEX_OPTIONS_NO_INTERMEDIATE_BUFFERING) {
+ create_options |= NTCREATEX_OPTIONS_WRITE_THROUGH;
+ }
+
+ /* Open the file with sync, if they asked for it, but
+ 'strict sync = no' turns this client request into a no-op */
+ if (create_options & (NTCREATEX_OPTIONS_WRITE_THROUGH) && !(pvfs->flags | PVFS_FLAG_STRICT_SYNC)) {
+ flags |= O_SYNC;
+ }
+
+
+ /* other create options are not allowed */
+ if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
+ !(access_mask & SEC_STD_DELETE)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (access_mask & SEC_MASK_INVALID) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* what does this bit really mean?? */
+ if (req->ctx->protocol == PROTOCOL_SMB2 &&
+ access_mask == SEC_STD_SYNCHRONIZE) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (io->ntcreatex.in.file_attr & (FILE_ATTRIBUTE_DEVICE|
+ FILE_ATTRIBUTE_VOLUME|
+ (~FILE_ATTRIBUTE_ALL_MASK))) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* we ignore some file_attr bits */
+ io->ntcreatex.in.file_attr &= ~(FILE_ATTRIBUTE_NONINDEXED |
+ FILE_ATTRIBUTE_COMPRESSED |
+ FILE_ATTRIBUTE_REPARSE_POINT |
+ FILE_ATTRIBUTE_SPARSE |
+ FILE_ATTRIBUTE_NORMAL);
+
+ /* resolve the cifs name to a posix name */
+ status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname,
+ PVFS_RESOLVE_STREAMS, &name);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* if the client specified that it must not be a directory then
+ check that it isn't */
+ if (name->exists && (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) &&
+ (io->generic.in.create_options & NTCREATEX_OPTIONS_NON_DIRECTORY_FILE)) {
+ return NT_STATUS_FILE_IS_A_DIRECTORY;
+ }
+
+ /* if the client specified that it must be a directory then
+ check that it is */
+ if (name->exists && !(name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) &&
+ (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY)) {
+ return NT_STATUS_NOT_A_DIRECTORY;
+ }
+
+ /* directory opens are handled separately */
+ if ((name->exists && (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) ||
+ (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY)) {
+ return pvfs_open_directory(pvfs, req, name, io);
+ }
+
+ /* FILE_ATTRIBUTE_DIRECTORY is ignored if the above test for directory
+ open doesn't match */
+ io->generic.in.file_attr &= ~FILE_ATTRIBUTE_DIRECTORY;
+
+ switch (io->generic.in.open_disposition) {
+ case NTCREATEX_DISP_SUPERSEDE:
+ case NTCREATEX_DISP_OVERWRITE_IF:
+ if (name->stream_name == NULL) {
+ flags = O_TRUNC;
+ } else {
+ stream_truncate = true;
+ }
+ create_action = NTCREATEX_ACTION_TRUNCATED;
+ break;
+
+ case NTCREATEX_DISP_OPEN:
+ if (!name->stream_exists) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+ flags = 0;
+ break;
+
+ case NTCREATEX_DISP_OVERWRITE:
+ if (!name->stream_exists) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+ if (name->stream_name == NULL) {
+ flags = O_TRUNC;
+ } else {
+ stream_truncate = true;
+ }
+ create_action = NTCREATEX_ACTION_TRUNCATED;
+ break;
+
+ case NTCREATEX_DISP_CREATE:
+ if (name->stream_exists) {
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+ flags = 0;
+ break;
+
+ case NTCREATEX_DISP_OPEN_IF:
+ flags = 0;
+ break;
+
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* handle creating a new file separately */
+ if (!name->exists) {
+ status = pvfs_create_file(pvfs, req, name, io);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
+ return status;
+ }
+
+ /* we've hit a race - the file was created during this call */
+ if (io->generic.in.open_disposition == NTCREATEX_DISP_CREATE) {
+ return status;
+ }
+
+ /* try re-resolving the name */
+ status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, 0, &name);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ /* fall through to a normal open */
+ }
+
+ if ((name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
+ (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
+ return NT_STATUS_CANNOT_DELETE;
+ }
+
+ /* check the security descriptor */
+ status = pvfs_access_check(pvfs, req, name, &access_mask);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ if (io->generic.in.query_maximal_access) {
+ status = pvfs_access_maximal_allowed(pvfs, req, name,
+ &io->generic.out.maximal_access);
+ NT_STATUS_NOT_OK_RETURN(status);
+ }
+
+ status = ntvfs_handle_new(pvfs->ntvfs, req, &h);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ f = talloc(h, struct pvfs_file);
+ if (f == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ f->handle = talloc(f, struct pvfs_file_handle);
+ if (f->handle == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ f->ntvfs = h;
+ f->pvfs = pvfs;
+ f->pending_list = NULL;
+ f->lock_count = 0;
+ f->share_access = io->generic.in.share_access;
+ f->access_mask = access_mask;
+ f->impersonation = io->generic.in.impersonation;
+ f->notify_buffer = NULL;
+ f->search = NULL;
+
+ f->handle->pvfs = pvfs;
+ f->handle->fd = -1;
+ f->handle->name = talloc_steal(f->handle, name);
+ f->handle->create_options = io->generic.in.create_options;
+ f->handle->seek_offset = 0;
+ f->handle->position = 0;
+ f->handle->mode = 0;
+ f->handle->oplock = NULL;
+ f->handle->have_opendb_entry = false;
+ ZERO_STRUCT(f->handle->write_time);
+ f->handle->open_completed = false;
+
+ /* form the lock context used for byte range locking and
+ opendb locking */
+ status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = pvfs_brl_locking_handle(f, name, h, &f->brl_handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* get a lock on this file before the actual open */
+ lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
+ if (lck == NULL) {
+ DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
+ name->full_name));
+ /* we were supposed to do a blocking lock, so something
+ is badly wrong! */
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ DLIST_ADD(pvfs->files.list, f);
+
+ /* setup a destructor to avoid file descriptor leaks on
+ abnormal termination */
+ talloc_set_destructor(f, pvfs_fnum_destructor);
+ talloc_set_destructor(f->handle, pvfs_handle_destructor);
+
+ /*
+ * Only SMB2 takes care of the delete_on_close,
+ * on existing files
+ */
+ if (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE &&
+ req->ctx->protocol == PROTOCOL_SMB2) {
+ del_on_close = true;
+ } else {
+ del_on_close = false;
+ }
+
+ if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
+ oplock_level = OPLOCK_NONE;
+ } else if (io->ntcreatex.in.flags & NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK) {
+ oplock_level = OPLOCK_BATCH;
+ } else if (io->ntcreatex.in.flags & NTCREATEX_FLAGS_REQUEST_OPLOCK) {
+ oplock_level = OPLOCK_EXCLUSIVE;
+ }
+
+ if (req->client_caps & NTVFS_CLIENT_CAP_LEVEL_II_OPLOCKS) {
+ allow_level_II_oplock = true;
+ }
+
+ /* see if we are allowed to open at the same time as existing opens */
+ status = odb_can_open(lck, name->stream_id,
+ share_access, access_mask, del_on_close,
+ io->generic.in.open_disposition, false);
+
+ /*
+ * on a sharing violation we need to retry when the file is closed by
+ * the other user, or after 1 second
+ * on a non granted oplock we need to retry when the file is closed by
+ * the other user, or after 30 seconds
+ */
+ if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) &&
+ (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ return pvfs_open_setup_retry(ntvfs, req, io, f, lck, status);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lck);
+ return status;
+ }
+
+ if (access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)) {
+ flags |= O_RDWR;
+ } else {
+ flags |= O_RDONLY;
+ }
+
+ /* do the actual open */
+ fd = open(f->handle->name->full_name, flags | O_NONBLOCK);
+ if (fd == -1) {
+ status = pvfs_map_errno(f->pvfs, errno);
+
+ /*
+ * STATUS_MORE_ENTRIES is EAGAIN or EWOULDBLOCK
+ */
+ if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES) &&
+ (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ return pvfs_open_setup_retry(ntvfs, req, io, f, lck, status);
+ }
+
+ talloc_free(lck);
+ return status;
+ }
+
+ f->handle->fd = fd;
+
+ /* now really mark the file as open */
+ status = odb_open_file(lck, f->handle, name->full_name,
+ &f->handle->fd, name->dos.write_time,
+ allow_level_II_oplock,
+ oplock_level, &oplock_granted);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lck);
+ return status;
+ }
+
+ f->handle->have_opendb_entry = true;
+
+ if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
+ oplock_granted = OPLOCK_BATCH;
+ } else if (oplock_granted != OPLOCK_NONE) {
+ status = pvfs_setup_oplock(f, oplock_granted);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lck);
+ return status;
+ }
+ }
+
+ stream_existed = name->stream_exists;
+
+ /* if this was a stream create then create the stream as well */
+ if (!name->stream_exists) {
+ status = pvfs_stream_create(pvfs, f->handle->name, fd);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lck);
+ return status;
+ }
+ if (stream_truncate) {
+ status = pvfs_stream_truncate(pvfs, f->handle->name, fd, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lck);
+ return status;
+ }
+ }
+ }
+
+ /* re-resolve the open fd */
+ status = pvfs_resolve_name_fd(f->pvfs, fd, f->handle->name, PVFS_RESOLVE_NO_OPENDB);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lck);
+ return status;
+ }
+
+ if (f->handle->name->stream_id == 0 &&
+ (io->generic.in.open_disposition == NTCREATEX_DISP_OVERWRITE ||
+ io->generic.in.open_disposition == NTCREATEX_DISP_OVERWRITE_IF)) {
+ /* for overwrite we need to replace file permissions */
+ uint32_t attrib = io->ntcreatex.in.file_attr | FILE_ATTRIBUTE_ARCHIVE;
+ mode_t mode = pvfs_fileperms(pvfs, attrib);
+ if (fchmod(fd, mode) == -1) {
+ talloc_free(lck);
+ return pvfs_map_errno(pvfs, errno);
+ }
+ name->dos.alloc_size = io->ntcreatex.in.alloc_size;
+ name->dos.attrib = attrib;
+ status = pvfs_dosattrib_save(pvfs, name, fd);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lck);
+ return status;
+ }
+ }
+
+ talloc_free(lck);
+
+ status = ntvfs_handle_set_backend_data(h, ntvfs, f);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ /* mark the open as having completed fully, so delete on close
+ can now be used */
+ f->handle->open_completed = true;
+
+ io->generic.out.oplock_level = oplock_granted;
+ io->generic.out.file.ntvfs = h;
+ io->generic.out.create_action = stream_existed?
+ create_action:NTCREATEX_ACTION_CREATED;
+
+ io->generic.out.create_time = name->dos.create_time;
+ io->generic.out.access_time = name->dos.access_time;
+ io->generic.out.write_time = name->dos.write_time;
+ io->generic.out.change_time = name->dos.change_time;
+ io->generic.out.attrib = name->dos.attrib;
+ io->generic.out.alloc_size = name->dos.alloc_size;
+ io->generic.out.size = name->st.st_size;
+ io->generic.out.file_type = FILE_TYPE_DISK;
+ io->generic.out.ipc_state = 0;
+ io->generic.out.is_directory = 0;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ close a file
+*/
+NTSTATUS pvfs_close(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_close *io)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ struct pvfs_file *f;
+
+ if (io->generic.level == RAW_CLOSE_SPLCLOSE) {
+ return NT_STATUS_DOS(ERRSRV, ERRerror);
+ }
+
+ if (io->generic.level != RAW_CLOSE_GENERIC) {
+ return ntvfs_map_close(ntvfs, req, io);
+ }
+
+ f = pvfs_find_fd(pvfs, req, io->generic.in.file.ntvfs);
+ if (!f) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ if (!null_time(io->generic.in.write_time)) {
+ f->handle->write_time.update_forced = false;
+ f->handle->write_time.update_on_close = true;
+ unix_to_nt_time(&f->handle->write_time.close_time, io->generic.in.write_time);
+ }
+
+ if (io->generic.in.flags & SMB2_CLOSE_FLAGS_FULL_INFORMATION) {
+ struct pvfs_filename *name;
+ NTSTATUS status;
+ struct pvfs_file_handle *h = f->handle;
+
+ status = pvfs_resolve_name_handle(pvfs, h);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ name = h->name;
+
+ io->generic.out.flags = SMB2_CLOSE_FLAGS_FULL_INFORMATION;
+ io->generic.out.create_time = name->dos.create_time;
+ io->generic.out.access_time = name->dos.access_time;
+ io->generic.out.write_time = name->dos.write_time;
+ io->generic.out.change_time = name->dos.change_time;
+ io->generic.out.alloc_size = name->dos.alloc_size;
+ io->generic.out.size = name->st.st_size;
+ io->generic.out.file_attr = name->dos.attrib;
+ } else {
+ ZERO_STRUCT(io->generic.out);
+ }
+
+ talloc_free(f);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ logoff - close all file descriptors open by a vuid
+*/
+NTSTATUS pvfs_logoff(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ struct pvfs_file *f, *next;
+
+ for (f=pvfs->files.list;f;f=next) {
+ next = f->next;
+ if (f->ntvfs->session_info == req->session_info) {
+ talloc_free(f);
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ exit - close files for the current pid
+*/
+NTSTATUS pvfs_exit(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ struct pvfs_file *f, *next;
+
+ for (f=pvfs->files.list;f;f=next) {
+ next = f->next;
+ if (f->ntvfs->session_info == req->session_info &&
+ f->ntvfs->smbpid == req->smbpid) {
+ talloc_free(f);
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ change the delete on close flag on an already open file
+*/
+NTSTATUS pvfs_set_delete_on_close(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
+ struct pvfs_file *f, bool del_on_close)
+{
+ struct odb_lock *lck;
+ NTSTATUS status;
+
+ if ((f->handle->name->dos.attrib & FILE_ATTRIBUTE_READONLY) && del_on_close) {
+ return NT_STATUS_CANNOT_DELETE;
+ }
+
+ if ((f->handle->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) &&
+ !pvfs_directory_empty(pvfs, f->handle->name)) {
+ return NT_STATUS_DIRECTORY_NOT_EMPTY;
+ }
+
+ if (del_on_close) {
+ f->handle->create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
+ } else {
+ f->handle->create_options &= ~NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
+ }
+
+ lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
+ if (lck == NULL) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ status = odb_set_delete_on_close(lck, del_on_close);
+
+ talloc_free(lck);
+
+ return status;
+}
+
+
+/*
+ determine if a file can be deleted, or if it is prevented by an
+ already open file
+*/
+NTSTATUS pvfs_can_delete(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
+ struct pvfs_filename *name,
+ struct odb_lock **lckp)
+{
+ NTSTATUS status;
+ DATA_BLOB key;
+ struct odb_lock *lck;
+ uint32_t share_access;
+ uint32_t access_mask;
+ bool delete_on_close;
+
+ status = pvfs_locking_key(name, name, &key);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ lck = odb_lock(req, pvfs->odb_context, &key);
+ if (lck == NULL) {
+ DEBUG(0,("Unable to lock opendb for can_delete\n"));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ share_access = NTCREATEX_SHARE_ACCESS_READ |
+ NTCREATEX_SHARE_ACCESS_WRITE |
+ NTCREATEX_SHARE_ACCESS_DELETE;
+ access_mask = SEC_STD_DELETE;
+ delete_on_close = true;
+
+ status = odb_can_open(lck, name->stream_id,
+ share_access, access_mask, delete_on_close,
+ NTCREATEX_DISP_OPEN, false);
+
+ if (NT_STATUS_IS_OK(status)) {
+ status = pvfs_access_check_simple(pvfs, req, name, access_mask);
+ }
+
+ /*
+ * if it's a sharing violation or we got no oplock
+ * only keep the lock if the caller requested access
+ * to the lock
+ */
+ if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) {
+ if (lckp) {
+ *lckp = lck;
+ } else {
+ talloc_free(lck);
+ }
+ } else if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lck);
+ if (lckp) {
+ *lckp = NULL;
+ }
+ } else if (lckp) {
+ *lckp = lck;
+ }
+
+ return status;
+}
+
+/*
+ determine if a file can be renamed, or if it is prevented by an
+ already open file
+*/
+NTSTATUS pvfs_can_rename(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
+ struct pvfs_filename *name,
+ struct odb_lock **lckp)
+{
+ NTSTATUS status;
+ DATA_BLOB key;
+ struct odb_lock *lck;
+ uint32_t share_access;
+ uint32_t access_mask;
+ bool delete_on_close;
+
+ status = pvfs_locking_key(name, name, &key);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ lck = odb_lock(req, pvfs->odb_context, &key);
+ if (lck == NULL) {
+ DEBUG(0,("Unable to lock opendb for can_stat\n"));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ share_access = NTCREATEX_SHARE_ACCESS_READ |
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ access_mask = SEC_STD_DELETE;
+ delete_on_close = false;
+
+ status = odb_can_open(lck, name->stream_id,
+ share_access, access_mask, delete_on_close,
+ NTCREATEX_DISP_OPEN, false);
+
+ /*
+ * if it's a sharing violation or we got no oplock
+ * only keep the lock if the caller requested access
+ * to the lock
+ */
+ if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) {
+ if (lckp) {
+ *lckp = lck;
+ } else {
+ talloc_free(lck);
+ }
+ } else if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lck);
+ if (lckp) {
+ *lckp = NULL;
+ }
+ } else if (lckp) {
+ *lckp = lck;
+ }
+
+ return status;
+}
+
+/*
+ determine if the file size of a file can be changed,
+ or if it is prevented by an already open file
+*/
+NTSTATUS pvfs_can_update_file_size(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
+ struct pvfs_filename *name,
+ struct odb_lock **lckp)
+{
+ NTSTATUS status;
+ DATA_BLOB key;
+ struct odb_lock *lck;
+ uint32_t share_access;
+ uint32_t access_mask;
+ bool break_to_none;
+ bool delete_on_close;
+
+ status = pvfs_locking_key(name, name, &key);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ lck = odb_lock(req, pvfs->odb_context, &key);
+ if (lck == NULL) {
+ DEBUG(0,("Unable to lock opendb for can_stat\n"));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ share_access = NTCREATEX_SHARE_ACCESS_READ |
+ NTCREATEX_SHARE_ACCESS_WRITE |
+ NTCREATEX_SHARE_ACCESS_DELETE;
+ /*
+ * I would have thought that we would need to pass
+ * SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA here too
+ *
+ * But you only need SEC_FILE_WRITE_ATTRIBUTE permissions
+ * to set the filesize.
+ *
+ * --metze
+ */
+ access_mask = SEC_FILE_WRITE_ATTRIBUTE;
+ delete_on_close = false;
+ break_to_none = true;
+
+ status = odb_can_open(lck, name->stream_id,
+ share_access, access_mask, delete_on_close,
+ NTCREATEX_DISP_OPEN, break_to_none);
+
+ /*
+ * if it's a sharing violation or we got no oplock
+ * only keep the lock if the caller requested access
+ * to the lock
+ */
+ if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) {
+ if (lckp) {
+ *lckp = lck;
+ } else {
+ talloc_free(lck);
+ }
+ } else if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lck);
+ if (lckp) {
+ *lckp = NULL;
+ }
+ } else if (lckp) {
+ *lckp = lck;
+ }
+
+ return status;
+}
+
+/*
+ determine if file meta data can be accessed, or if it is prevented by an
+ already open file
+*/
+NTSTATUS pvfs_can_stat(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
+ struct pvfs_filename *name)
+{
+ NTSTATUS status;
+ DATA_BLOB key;
+ struct odb_lock *lck;
+ uint32_t share_access;
+ uint32_t access_mask;
+ bool delete_on_close;
+
+ status = pvfs_locking_key(name, name, &key);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ lck = odb_lock(req, pvfs->odb_context, &key);
+ if (lck == NULL) {
+ DEBUG(0,("Unable to lock opendb for can_stat\n"));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ share_access = NTCREATEX_SHARE_ACCESS_READ |
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ access_mask = SEC_FILE_READ_ATTRIBUTE;
+ delete_on_close = false;
+
+ status = odb_can_open(lck, name->stream_id,
+ share_access, access_mask, delete_on_close,
+ NTCREATEX_DISP_OPEN, false);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lck);
+ }
+
+ return status;
+}
+
+
+/*
+ determine if delete on close is set on
+*/
+bool pvfs_delete_on_close_set(struct pvfs_state *pvfs, struct pvfs_file_handle *h)
+{
+ NTSTATUS status;
+ bool del_on_close;
+
+ status = odb_get_file_infos(pvfs->odb_context, &h->odb_locking_key,
+ &del_on_close, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1,("WARNING: unable to determine delete on close status for open file\n"));
+ return false;
+ }
+
+ return del_on_close;
+}
diff --git a/source4/ntvfs/posix/pvfs_oplock.c b/source4/ntvfs/posix/pvfs_oplock.c
new file mode 100644
index 0000000000..e5a069addc
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_oplock.c
@@ -0,0 +1,298 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - oplock handling
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/messaging/messaging.h"
+#include "lib/messaging/irpc.h"
+#include "system/time.h"
+#include "vfs_posix.h"
+
+
+struct pvfs_oplock {
+ struct pvfs_file_handle *handle;
+ struct pvfs_file *file;
+ uint32_t level;
+ struct timeval break_to_level_II;
+ struct timeval break_to_none;
+ struct messaging_context *msg_ctx;
+};
+
+static NTSTATUS pvfs_oplock_release_internal(struct pvfs_file_handle *h,
+ uint8_t oplock_break)
+{
+ struct odb_lock *olck;
+ NTSTATUS status;
+
+ if (h->fd == -1) {
+ return NT_STATUS_FILE_IS_A_DIRECTORY;
+ }
+
+ if (!h->have_opendb_entry) {
+ return NT_STATUS_FOOBAR;
+ }
+
+ if (!h->oplock) {
+ return NT_STATUS_FOOBAR;
+ }
+
+ olck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
+ if (olck == NULL) {
+ DEBUG(0,("Unable to lock opendb for oplock update\n"));
+ return NT_STATUS_FOOBAR;
+ }
+
+ if (oplock_break == OPLOCK_BREAK_TO_NONE) {
+ h->oplock->level = OPLOCK_NONE;
+ } else if (oplock_break == OPLOCK_BREAK_TO_LEVEL_II) {
+ h->oplock->level = OPLOCK_LEVEL_II;
+ } else {
+ /* fallback to level II in case of a invalid value */
+ DEBUG(1,("unexpected oplock break level[0x%02X]\n", oplock_break));
+ h->oplock->level = OPLOCK_LEVEL_II;
+ }
+ status = odb_update_oplock(olck, h, h->oplock->level);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Unable to update oplock level for '%s' - %s\n",
+ h->name->full_name, nt_errstr(status)));
+ talloc_free(olck);
+ return status;
+ }
+
+ talloc_free(olck);
+
+ /* after a break to none, we no longer have an oplock attached */
+ if (h->oplock->level == OPLOCK_NONE) {
+ talloc_free(h->oplock);
+ h->oplock = NULL;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ receive oplock breaks and forward them to the client
+*/
+static void pvfs_oplock_break(struct pvfs_oplock *opl, uint8_t level)
+{
+ NTSTATUS status;
+ struct pvfs_file *f = opl->file;
+ struct pvfs_file_handle *h = opl->handle;
+ struct pvfs_state *pvfs = h->pvfs;
+ struct timeval cur = timeval_current();
+ struct timeval *last = NULL;
+ struct timeval end;
+
+ switch (level) {
+ case OPLOCK_BREAK_TO_LEVEL_II:
+ last = &opl->break_to_level_II;
+ break;
+ case OPLOCK_BREAK_TO_NONE:
+ last = &opl->break_to_none;
+ break;
+ }
+
+ if (!last) {
+ DEBUG(0,("%s: got unexpected level[0x%02X]\n",
+ __FUNCTION__, level));
+ return;
+ }
+
+ if (timeval_is_zero(last)) {
+ /*
+ * this is the first break we for this level
+ * remember the time
+ */
+ *last = cur;
+
+ DEBUG(0,("%s: sending oplock break level %d for '%s' %p\n",
+ __FUNCTION__, level, h->name->original_name, h));
+ status = ntvfs_send_oplock_break(pvfs->ntvfs, f->ntvfs, level);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("%s: sending oplock break failed: %s\n",
+ __FUNCTION__, nt_errstr(status)));
+ }
+ return;
+ }
+
+ end = timeval_add(last, pvfs->oplock_break_timeout, 0);
+
+ if (timeval_compare(&cur, &end) < 0) {
+ /*
+ * If it's not expired just ignore the break
+ * as we already sent the break request to the client
+ */
+ DEBUG(0,("%s: do not resend oplock break level %d for '%s' %p\n",
+ __FUNCTION__, level, h->name->original_name, h));
+ return;
+ }
+
+ /*
+ * If the client did not send a release within the
+ * oplock break timeout time frame we auto release
+ * the oplock
+ */
+ DEBUG(0,("%s: auto release oplock level %d for '%s' %p\n",
+ __FUNCTION__, level, h->name->original_name, h));
+ status = pvfs_oplock_release_internal(h, level);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("%s: failed to auto release the oplock[0x%02X]: %s\n",
+ __FUNCTION__, level, nt_errstr(status)));
+ }
+}
+
+static void pvfs_oplock_break_dispatch(struct messaging_context *msg,
+ void *private_data, uint32_t msg_type,
+ struct server_id src, DATA_BLOB *data)
+{
+ struct pvfs_oplock *opl = talloc_get_type(private_data,
+ struct pvfs_oplock);
+ struct opendb_oplock_break opb;
+
+ ZERO_STRUCT(opb);
+
+ /* we need to check that this one is for us. See
+ messaging_send_ptr() for the other side of this.
+ */
+ if (data->length == sizeof(struct opendb_oplock_break)) {
+ struct opendb_oplock_break *p;
+ p = (struct opendb_oplock_break *)data->data;
+ opb = *p;
+ } else {
+ DEBUG(0,("%s: ignore oplock break with length[%u]\n",
+ __location__, (unsigned)data->length));
+ return;
+ }
+ if (opb.file_handle != opl->handle) {
+ return;
+ }
+
+ /*
+ * maybe we should use ntvfs_setup_async()
+ */
+ pvfs_oplock_break(opl, opb.level);
+}
+
+static int pvfs_oplock_destructor(struct pvfs_oplock *opl)
+{
+ messaging_deregister(opl->msg_ctx, MSG_NTVFS_OPLOCK_BREAK, opl);
+ return 0;
+}
+
+NTSTATUS pvfs_setup_oplock(struct pvfs_file *f, uint32_t oplock_granted)
+{
+ NTSTATUS status;
+ struct pvfs_oplock *opl;
+ uint32_t level = OPLOCK_NONE;
+
+ f->handle->oplock = NULL;
+
+ switch (oplock_granted) {
+ case EXCLUSIVE_OPLOCK_RETURN:
+ level = OPLOCK_EXCLUSIVE;
+ break;
+ case BATCH_OPLOCK_RETURN:
+ level = OPLOCK_BATCH;
+ break;
+ case LEVEL_II_OPLOCK_RETURN:
+ level = OPLOCK_LEVEL_II;
+ break;
+ }
+
+ if (level == OPLOCK_NONE) {
+ return NT_STATUS_OK;
+ }
+
+ opl = talloc_zero(f->handle, struct pvfs_oplock);
+ NT_STATUS_HAVE_NO_MEMORY(opl);
+
+ opl->handle = f->handle;
+ opl->file = f;
+ opl->level = level;
+ opl->msg_ctx = f->pvfs->ntvfs->ctx->msg_ctx;
+
+ status = messaging_register(opl->msg_ctx,
+ opl,
+ MSG_NTVFS_OPLOCK_BREAK,
+ pvfs_oplock_break_dispatch);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ /* destructor */
+ talloc_set_destructor(opl, pvfs_oplock_destructor);
+
+ f->handle->oplock = opl;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS pvfs_oplock_release(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_lock *lck)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ struct pvfs_file *f;
+ uint8_t oplock_break;
+ NTSTATUS status;
+
+ f = pvfs_find_fd(pvfs, req, lck->lockx.in.file.ntvfs);
+ if (!f) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ oplock_break = (lck->lockx.in.mode >> 8) & 0xFF;
+
+ status = pvfs_oplock_release_internal(f->handle, oplock_break);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("%s: failed to release the oplock[0x%02X]: %s\n",
+ __FUNCTION__, oplock_break, nt_errstr(status)));
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS pvfs_break_level2_oplocks(struct pvfs_file *f)
+{
+ struct pvfs_file_handle *h = f->handle;
+ struct odb_lock *olck;
+ NTSTATUS status;
+
+ if (h->oplock && h->oplock->level != OPLOCK_LEVEL_II) {
+ return NT_STATUS_OK;
+ }
+
+ olck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
+ if (olck == NULL) {
+ DEBUG(0,("Unable to lock opendb for oplock update\n"));
+ return NT_STATUS_FOOBAR;
+ }
+
+ status = odb_break_oplocks(olck);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Unable to break level2 oplocks to none for '%s' - %s\n",
+ h->name->full_name, nt_errstr(status)));
+ talloc_free(olck);
+ return status;
+ }
+
+ talloc_free(olck);
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/ntvfs/posix/pvfs_qfileinfo.c b/source4/ntvfs/posix/pvfs_qfileinfo.c
new file mode 100644
index 0000000000..bfc9a8441c
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_qfileinfo.c
@@ -0,0 +1,450 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - read
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "vfs_posix.h"
+#include "librpc/gen_ndr/xattr.h"
+
+
+/*
+ determine what access bits are needed for a call
+*/
+static uint32_t pvfs_fileinfo_access(union smb_fileinfo *info)
+{
+ uint32_t needed;
+
+ switch (info->generic.level) {
+ case RAW_FILEINFO_EA_LIST:
+ case RAW_FILEINFO_ALL_EAS:
+ needed = SEC_FILE_READ_EA;
+ break;
+
+ case RAW_FILEINFO_IS_NAME_VALID:
+ needed = 0;
+ break;
+
+ case RAW_FILEINFO_ACCESS_INFORMATION:
+ needed = 0;
+ break;
+
+ case RAW_FILEINFO_SEC_DESC:
+ needed = 0;
+ if (info->query_secdesc.in.secinfo_flags & (SECINFO_OWNER|SECINFO_GROUP)) {
+ needed |= SEC_STD_READ_CONTROL;
+ }
+ if (info->query_secdesc.in.secinfo_flags & SECINFO_DACL) {
+ needed |= SEC_STD_READ_CONTROL;
+ }
+ if (info->query_secdesc.in.secinfo_flags & SECINFO_SACL) {
+ needed |= SEC_FLAG_SYSTEM_SECURITY;
+ }
+ break;
+
+ default:
+ needed = SEC_FILE_READ_ATTRIBUTE;
+ break;
+ }
+
+ return needed;
+}
+
+/*
+ reply to a RAW_FILEINFO_EA_LIST call
+*/
+NTSTATUS pvfs_query_ea_list(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
+ struct pvfs_filename *name, int fd,
+ uint_t num_names,
+ struct ea_name *names,
+ struct smb_ea_list *eas)
+{
+ NTSTATUS status;
+ int i;
+ struct xattr_DosEAs *ealist = talloc(mem_ctx, struct xattr_DosEAs);
+
+ ZERO_STRUCTP(eas);
+ status = pvfs_doseas_load(pvfs, name, fd, ealist);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ eas->eas = talloc_array(mem_ctx, struct ea_struct, num_names);
+ if (eas->eas == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ eas->num_eas = num_names;
+ for (i=0;i<num_names;i++) {
+ int j;
+ eas->eas[i].flags = 0;
+ eas->eas[i].name.s = names[i].name.s;
+ eas->eas[i].value = data_blob(NULL, 0);
+ for (j=0;j<ealist->num_eas;j++) {
+ if (strcasecmp_m(eas->eas[i].name.s,
+ ealist->eas[j].name) == 0) {
+ eas->eas[i].value = ealist->eas[j].value;
+ break;
+ }
+ }
+ }
+ return NT_STATUS_OK;
+}
+
+/*
+ reply to a RAW_FILEINFO_ALL_EAS call
+*/
+static NTSTATUS pvfs_query_all_eas(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
+ struct pvfs_filename *name, int fd,
+ struct smb_ea_list *eas)
+{
+ NTSTATUS status;
+ int i;
+ struct xattr_DosEAs *ealist = talloc(mem_ctx, struct xattr_DosEAs);
+
+ ZERO_STRUCTP(eas);
+ status = pvfs_doseas_load(pvfs, name, fd, ealist);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ eas->eas = talloc_array(mem_ctx, struct ea_struct, ealist->num_eas);
+ if (eas->eas == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ eas->num_eas = 0;
+ for (i=0;i<ealist->num_eas;i++) {
+ eas->eas[eas->num_eas].flags = 0;
+ eas->eas[eas->num_eas].name.s = ealist->eas[i].name;
+ eas->eas[eas->num_eas].value = ealist->eas[i].value;
+ eas->num_eas++;
+ }
+ return NT_STATUS_OK;
+}
+
+/*
+ approximately map a struct pvfs_filename to a generic fileinfo struct
+*/
+static NTSTATUS pvfs_map_fileinfo(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
+ struct pvfs_filename *name, union smb_fileinfo *info,
+ int fd)
+{
+ switch (info->generic.level) {
+ case RAW_FILEINFO_GENERIC:
+ return NT_STATUS_INVALID_LEVEL;
+
+ case RAW_FILEINFO_GETATTR:
+ info->getattr.out.attrib = name->dos.attrib;
+ info->getattr.out.size = name->st.st_size;
+ info->getattr.out.write_time = nt_time_to_unix(name->dos.write_time);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_GETATTRE:
+ case RAW_FILEINFO_STANDARD:
+ info->standard.out.create_time = nt_time_to_unix(name->dos.create_time);
+ info->standard.out.access_time = nt_time_to_unix(name->dos.access_time);
+ info->standard.out.write_time = nt_time_to_unix(name->dos.write_time);
+ info->standard.out.size = name->st.st_size;
+ info->standard.out.alloc_size = name->dos.alloc_size;
+ info->standard.out.attrib = name->dos.attrib;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_EA_SIZE:
+ info->ea_size.out.create_time = nt_time_to_unix(name->dos.create_time);
+ info->ea_size.out.access_time = nt_time_to_unix(name->dos.access_time);
+ info->ea_size.out.write_time = nt_time_to_unix(name->dos.write_time);
+ info->ea_size.out.size = name->st.st_size;
+ info->ea_size.out.alloc_size = name->dos.alloc_size;
+ info->ea_size.out.attrib = name->dos.attrib;
+ info->ea_size.out.ea_size = name->dos.ea_size;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_EA_LIST:
+ return pvfs_query_ea_list(pvfs, req, name, fd,
+ info->ea_list.in.num_names,
+ info->ea_list.in.ea_names,
+ &info->ea_list.out);
+
+ case RAW_FILEINFO_ALL_EAS:
+ return pvfs_query_all_eas(pvfs, req, name, fd, &info->all_eas.out);
+
+ case RAW_FILEINFO_SMB2_ALL_EAS: {
+ NTSTATUS status = pvfs_query_all_eas(pvfs, req, name, fd, &info->all_eas.out);
+ if (NT_STATUS_IS_OK(status) &&
+ info->all_eas.out.num_eas == 0) {
+ return NT_STATUS_NO_EAS_ON_FILE;
+ }
+ return status;
+ }
+
+ case RAW_FILEINFO_IS_NAME_VALID:
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_BASIC_INFO:
+ case RAW_FILEINFO_BASIC_INFORMATION:
+ info->basic_info.out.create_time = name->dos.create_time;
+ info->basic_info.out.access_time = name->dos.access_time;
+ info->basic_info.out.write_time = name->dos.write_time;
+ info->basic_info.out.change_time = name->dos.change_time;
+ info->basic_info.out.attrib = name->dos.attrib;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_STANDARD_INFO:
+ case RAW_FILEINFO_STANDARD_INFORMATION:
+ info->standard_info.out.alloc_size = name->dos.alloc_size;
+ info->standard_info.out.size = name->st.st_size;
+ info->standard_info.out.nlink = name->dos.nlink;
+ info->standard_info.out.delete_pending = 0; /* only for qfileinfo */
+ info->standard_info.out.directory =
+ (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)? 1 : 0;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_EA_INFO:
+ case RAW_FILEINFO_EA_INFORMATION:
+ info->ea_info.out.ea_size = name->dos.ea_size;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_NAME_INFO:
+ case RAW_FILEINFO_NAME_INFORMATION:
+ if (req->ctx->protocol == PROTOCOL_SMB2) {
+ /* strange that SMB2 doesn't have this */
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+ info->name_info.out.fname.s = name->original_name;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_ALL_INFO:
+ case RAW_FILEINFO_ALL_INFORMATION:
+ info->all_info.out.create_time = name->dos.create_time;
+ info->all_info.out.access_time = name->dos.access_time;
+ info->all_info.out.write_time = name->dos.write_time;
+ info->all_info.out.change_time = name->dos.change_time;
+ info->all_info.out.attrib = name->dos.attrib;
+ info->all_info.out.alloc_size = name->dos.alloc_size;
+ info->all_info.out.size = name->st.st_size;
+ info->all_info.out.nlink = name->dos.nlink;
+ info->all_info.out.delete_pending = 0; /* only set by qfileinfo */
+ info->all_info.out.directory =
+ (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)? 1 : 0;
+ info->all_info.out.ea_size = name->dos.ea_size;
+ info->all_info.out.fname.s = name->original_name;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_ALT_NAME_INFO:
+ case RAW_FILEINFO_ALT_NAME_INFORMATION:
+ info->name_info.out.fname.s = pvfs_short_name(pvfs, name, name);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_STREAM_INFO:
+ case RAW_FILEINFO_STREAM_INFORMATION:
+ return pvfs_stream_information(pvfs, req, name, fd, &info->stream_info.out);
+
+ case RAW_FILEINFO_COMPRESSION_INFO:
+ case RAW_FILEINFO_COMPRESSION_INFORMATION:
+ info->compression_info.out.compressed_size = name->st.st_size;
+ info->compression_info.out.format = 0;
+ info->compression_info.out.unit_shift = 0;
+ info->compression_info.out.chunk_shift = 0;
+ info->compression_info.out.cluster_shift = 0;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_INTERNAL_INFORMATION:
+ info->internal_information.out.file_id = name->dos.file_id;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_ACCESS_INFORMATION:
+ info->access_information.out.access_flags = 0; /* only set by qfileinfo */
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_POSITION_INFORMATION:
+ info->position_information.out.position = 0; /* only set by qfileinfo */
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_MODE_INFORMATION:
+ info->mode_information.out.mode = 0; /* only set by qfileinfo */
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_ALIGNMENT_INFORMATION:
+ info->alignment_information.out.alignment_requirement = 0;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_NETWORK_OPEN_INFORMATION:
+ info->network_open_information.out.create_time = name->dos.create_time;
+ info->network_open_information.out.access_time = name->dos.access_time;
+ info->network_open_information.out.write_time = name->dos.write_time;
+ info->network_open_information.out.change_time = name->dos.change_time;
+ info->network_open_information.out.alloc_size = name->dos.alloc_size;
+ info->network_open_information.out.size = name->st.st_size;
+ info->network_open_information.out.attrib = name->dos.attrib;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION:
+ info->attribute_tag_information.out.attrib = name->dos.attrib;
+ info->attribute_tag_information.out.reparse_tag = 0;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_SEC_DESC:
+ return pvfs_acl_query(pvfs, req, name, fd, info);
+
+ case RAW_FILEINFO_SMB2_ALL_INFORMATION:
+ info->all_info2.out.create_time = name->dos.create_time;
+ info->all_info2.out.access_time = name->dos.access_time;
+ info->all_info2.out.write_time = name->dos.write_time;
+ info->all_info2.out.change_time = name->dos.change_time;
+ info->all_info2.out.attrib = name->dos.attrib;
+ info->all_info2.out.unknown1 = 0;
+ info->all_info2.out.alloc_size = name->dos.alloc_size;
+ info->all_info2.out.size = name->st.st_size;
+ info->all_info2.out.nlink = name->dos.nlink;
+ info->all_info2.out.delete_pending = 0; /* only set by qfileinfo */
+ info->all_info2.out.directory =
+ (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)? 1 : 0;
+ info->all_info2.out.file_id = name->dos.file_id;
+ info->all_info2.out.ea_size = name->dos.ea_size;
+ info->all_info2.out.access_mask = 0; /* only set by qfileinfo */
+ info->all_info2.out.position = 0; /* only set by qfileinfo */
+ info->all_info2.out.mode = 0; /* only set by qfileinfo */
+ /* windows wants the full path on disk for this
+ result, but I really don't want to expose that on
+ the wire, so I'll give the path with a share
+ prefix, which is a good approximation */
+ info->all_info2.out.fname.s = talloc_asprintf(req, "\\%s\\%s",
+ pvfs->share_name,
+ name->original_name);
+ NT_STATUS_HAVE_NO_MEMORY(info->all_info2.out.fname.s);
+ return NT_STATUS_OK;
+ }
+
+ return NT_STATUS_INVALID_LEVEL;
+}
+
+/*
+ return info on a pathname
+*/
+NTSTATUS pvfs_qpathinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_fileinfo *info)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ struct pvfs_filename *name;
+ NTSTATUS status;
+
+ /* resolve the cifs name to a posix name */
+ status = pvfs_resolve_name(pvfs, req, info->generic.in.file.path, PVFS_RESOLVE_STREAMS, &name);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (!name->stream_exists) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ status = pvfs_can_stat(pvfs, req, name);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = pvfs_access_check_simple(pvfs, req, name,
+ pvfs_fileinfo_access(info));
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = pvfs_map_fileinfo(pvfs, req, name, info, -1);
+
+ return status;
+}
+
+/*
+ query info on a open file
+*/
+NTSTATUS pvfs_qfileinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_fileinfo *info)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ struct pvfs_file *f;
+ struct pvfs_file_handle *h;
+ NTSTATUS status;
+ uint32_t access_needed;
+
+ f = pvfs_find_fd(pvfs, req, info->generic.in.file.ntvfs);
+ if (!f) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+ h = f->handle;
+
+ access_needed = pvfs_fileinfo_access(info);
+ if ((f->access_mask & access_needed) != access_needed) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* update the file information */
+ status = pvfs_resolve_name_handle(pvfs, h);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = pvfs_map_fileinfo(pvfs, req, h->name, info, h->fd);
+
+ /* a qfileinfo can fill in a bit more info than a qpathinfo -
+ now modify the levels that need to be fixed up */
+ switch (info->generic.level) {
+ case RAW_FILEINFO_STANDARD_INFO:
+ case RAW_FILEINFO_STANDARD_INFORMATION:
+ if (pvfs_delete_on_close_set(pvfs, h)) {
+ info->standard_info.out.delete_pending = 1;
+ info->standard_info.out.nlink--;
+ }
+ break;
+
+ case RAW_FILEINFO_ALL_INFO:
+ case RAW_FILEINFO_ALL_INFORMATION:
+ if (pvfs_delete_on_close_set(pvfs, h)) {
+ info->all_info.out.delete_pending = 1;
+ info->all_info.out.nlink--;
+ }
+ break;
+
+ case RAW_FILEINFO_POSITION_INFORMATION:
+ info->position_information.out.position = h->position;
+ break;
+
+ case RAW_FILEINFO_ACCESS_INFORMATION:
+ info->access_information.out.access_flags = f->access_mask;
+ break;
+
+ case RAW_FILEINFO_MODE_INFORMATION:
+ info->mode_information.out.mode = h->mode;
+ break;
+
+ case RAW_FILEINFO_SMB2_ALL_INFORMATION:
+ if (pvfs_delete_on_close_set(pvfs, h)) {
+ info->all_info2.out.delete_pending = 1;
+ info->all_info2.out.nlink--;
+ }
+ info->all_info2.out.position = h->position;
+ info->all_info2.out.access_mask = f->access_mask;
+ info->all_info2.out.mode = h->mode;
+ break;
+
+ default:
+ break;
+ }
+
+ return status;
+}
diff --git a/source4/ntvfs/posix/pvfs_read.c b/source4/ntvfs/posix/pvfs_read.c
new file mode 100644
index 0000000000..d9080d632d
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_read.c
@@ -0,0 +1,112 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - read
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "vfs_posix.h"
+#include "lib/events/events.h"
+
+/*
+ read from a file
+*/
+NTSTATUS pvfs_read(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_read *rd)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ ssize_t ret;
+ struct pvfs_file *f;
+ NTSTATUS status;
+ uint32_t maxcnt;
+ uint32_t mask;
+
+ if (rd->generic.level != RAW_READ_READX) {
+ return ntvfs_map_read(ntvfs, req, rd);
+ }
+
+ f = pvfs_find_fd(pvfs, req, rd->readx.in.file.ntvfs);
+ if (!f) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ if (f->handle->fd == -1) {
+ return NT_STATUS_INVALID_DEVICE_REQUEST;
+ }
+
+ mask = SEC_FILE_READ_DATA;
+ if (rd->readx.in.read_for_execute) {
+ mask |= SEC_FILE_EXECUTE;
+ }
+ if (!(f->access_mask & mask)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ maxcnt = rd->readx.in.maxcnt;
+ if (maxcnt > UINT16_MAX && req->ctx->protocol < PROTOCOL_SMB2) {
+ maxcnt = 0;
+ }
+
+ status = pvfs_check_lock(pvfs, f, req->smbpid,
+ rd->readx.in.offset,
+ maxcnt,
+ READ_LOCK);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (f->handle->name->stream_name) {
+ ret = pvfs_stream_read(pvfs, f->handle,
+ rd->readx.out.data, maxcnt, rd->readx.in.offset);
+ } else {
+#if HAVE_LINUX_AIO
+ /* possibly try an aio read */
+ if ((req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC) &&
+ (pvfs->flags & PVFS_FLAG_LINUX_AIO)) {
+ status = pvfs_aio_pread(req, rd, f, maxcnt);
+ if (NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_OK;
+ }
+ }
+#endif
+ ret = pread(f->handle->fd,
+ rd->readx.out.data,
+ maxcnt,
+ rd->readx.in.offset);
+ }
+ if (ret == -1) {
+ return pvfs_map_errno(pvfs, errno);
+ }
+
+ /* only SMB2 honors mincnt */
+ if (req->ctx->protocol == PROTOCOL_SMB2) {
+ if (rd->readx.in.mincnt > ret ||
+ (ret == 0 && maxcnt > 0)) {
+ return NT_STATUS_END_OF_FILE;
+ }
+ }
+
+ f->handle->position = f->handle->seek_offset = rd->readx.in.offset + ret;
+
+ rd->readx.out.nread = ret;
+ rd->readx.out.remaining = 0xFFFF;
+ rd->readx.out.compaction_mode = 0;
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/ntvfs/posix/pvfs_rename.c b/source4/ntvfs/posix/pvfs_rename.c
new file mode 100644
index 0000000000..7f8eab5aa0
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_rename.c
@@ -0,0 +1,665 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - rename
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "vfs_posix.h"
+#include "librpc/gen_ndr/security.h"
+#include "param/param.h"
+
+
+/*
+ do a file rename, and send any notify triggers
+*/
+NTSTATUS pvfs_do_rename(struct pvfs_state *pvfs,
+ struct odb_lock *lck,
+ const struct pvfs_filename *name1,
+ const char *name2)
+{
+ const char *r1, *r2;
+ uint32_t mask;
+ NTSTATUS status;
+
+ if (rename(name1->full_name, name2) == -1) {
+ return pvfs_map_errno(pvfs, errno);
+ }
+
+ status = odb_rename(lck, name2);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ if (name1->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) {
+ mask = FILE_NOTIFY_CHANGE_DIR_NAME;
+ } else {
+ mask = FILE_NOTIFY_CHANGE_FILE_NAME;
+ }
+ /*
+ renames to the same directory cause a OLD_NAME->NEW_NAME notify.
+ renames to a different directory are considered a remove/add
+ */
+ r1 = strrchr_m(name1->full_name, '/');
+ r2 = strrchr_m(name2, '/');
+
+ if ((r1-name1->full_name) != (r2-name2) ||
+ strncmp(name1->full_name, name2, r1-name1->full_name) != 0) {
+ notify_trigger(pvfs->notify_context,
+ NOTIFY_ACTION_REMOVED,
+ mask,
+ name1->full_name);
+ notify_trigger(pvfs->notify_context,
+ NOTIFY_ACTION_ADDED,
+ mask,
+ name2);
+ } else {
+ notify_trigger(pvfs->notify_context,
+ NOTIFY_ACTION_OLD_NAME,
+ mask,
+ name1->full_name);
+ notify_trigger(pvfs->notify_context,
+ NOTIFY_ACTION_NEW_NAME,
+ mask,
+ name2);
+ }
+
+ /* this is a strange one. w2k3 gives an additional event for CHANGE_ATTRIBUTES
+ and CHANGE_CREATION on the new file when renaming files, but not
+ directories */
+ if ((name1->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) == 0) {
+ notify_trigger(pvfs->notify_context,
+ NOTIFY_ACTION_MODIFIED,
+ FILE_NOTIFY_CHANGE_ATTRIBUTES|FILE_NOTIFY_CHANGE_CREATION,
+ name2);
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ resolve a wildcard rename pattern. This works on one component of the name
+*/
+static const char *pvfs_resolve_wildcard_component(TALLOC_CTX *mem_ctx,
+ struct smb_iconv_convenience *iconv_convenience,
+ const char *fname,
+ const char *pattern)
+{
+ const char *p1, *p2;
+ char *dest, *d;
+
+ /* the length is bounded by the length of the two strings combined */
+ dest = talloc_array(mem_ctx, char, strlen(fname) + strlen(pattern) + 1);
+ if (dest == NULL) {
+ return NULL;
+ }
+
+ p1 = fname;
+ p2 = pattern;
+ d = dest;
+
+ while (*p2) {
+ codepoint_t c1, c2;
+ size_t c_size1, c_size2;
+ c1 = next_codepoint_convenience(iconv_convenience, p1, &c_size1);
+ c2 = next_codepoint_convenience(iconv_convenience, p2, &c_size2);
+ if (c2 == '?') {
+ d += push_codepoint(iconv_convenience, d, c1);
+ } else if (c2 == '*') {
+ memcpy(d, p1, strlen(p1));
+ d += strlen(p1);
+ break;
+ } else {
+ d += push_codepoint(iconv_convenience, d, c2);
+ }
+
+ p1 += c_size1;
+ p2 += c_size2;
+ }
+
+ *d = 0;
+
+ talloc_set_name_const(dest, dest);
+
+ return dest;
+}
+
+/*
+ resolve a wildcard rename pattern.
+*/
+static const char *pvfs_resolve_wildcard(TALLOC_CTX *mem_ctx,
+ struct smb_iconv_convenience *iconv_convenience,
+ const char *fname,
+ const char *pattern)
+{
+ const char *base1, *base2;
+ const char *ext1, *ext2;
+ char *p;
+
+ /* break into base part plus extension */
+ p = strrchr_m(fname, '.');
+ if (p == NULL) {
+ ext1 = "";
+ base1 = fname;
+ } else {
+ ext1 = talloc_strdup(mem_ctx, p+1);
+ base1 = talloc_strndup(mem_ctx, fname, p-fname);
+ }
+ if (ext1 == NULL || base1 == NULL) {
+ return NULL;
+ }
+
+ p = strrchr_m(pattern, '.');
+ if (p == NULL) {
+ ext2 = "";
+ base2 = fname;
+ } else {
+ ext2 = talloc_strdup(mem_ctx, p+1);
+ base2 = talloc_strndup(mem_ctx, pattern, p-pattern);
+ }
+ if (ext2 == NULL || base2 == NULL) {
+ return NULL;
+ }
+
+ base1 = pvfs_resolve_wildcard_component(mem_ctx, iconv_convenience, base1, base2);
+ ext1 = pvfs_resolve_wildcard_component(mem_ctx, iconv_convenience, ext1, ext2);
+ if (base1 == NULL || ext1 == NULL) {
+ return NULL;
+ }
+
+ if (*ext1 == 0) {
+ return base1;
+ }
+
+ return talloc_asprintf(mem_ctx, "%s.%s", base1, ext1);
+}
+
+/*
+ retry an rename after a sharing violation
+*/
+static void pvfs_retry_rename(struct pvfs_odb_retry *r,
+ struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ void *_io,
+ void *private_data,
+ enum pvfs_wait_notice reason)
+{
+ union smb_rename *io = talloc_get_type(_io, union smb_rename);
+ NTSTATUS status = NT_STATUS_INTERNAL_ERROR;
+
+ talloc_free(r);
+
+ switch (reason) {
+ case PVFS_WAIT_CANCEL:
+/*TODO*/
+ status = NT_STATUS_CANCELLED;
+ break;
+ case PVFS_WAIT_TIMEOUT:
+ /* if it timed out, then give the failure
+ immediately */
+/*TODO*/
+ status = NT_STATUS_SHARING_VIOLATION;
+ break;
+ case PVFS_WAIT_EVENT:
+
+ /* try the open again, which could trigger another retry setup
+ if it wants to, so we have to unmark the async flag so we
+ will know if it does a second async reply */
+ req->async_states->state &= ~NTVFS_ASYNC_STATE_ASYNC;
+
+ status = pvfs_rename(ntvfs, req, io);
+ if (req->async_states->state & NTVFS_ASYNC_STATE_ASYNC) {
+ /* the 2nd try also replied async, so we don't send
+ the reply yet */
+ return;
+ }
+
+ /* re-mark it async, just in case someone up the chain does
+ paranoid checking */
+ req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
+ break;
+ }
+
+ /* send the reply up the chain */
+ req->async_states->status = status;
+ req->async_states->send_fn(req);
+}
+
+/*
+ setup for a rename retry after a sharing violation
+ or a non granted oplock
+*/
+static NTSTATUS pvfs_rename_setup_retry(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_rename *io,
+ struct odb_lock *lck,
+ NTSTATUS status)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ struct timeval end_time;
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) {
+ end_time = timeval_add(&req->statistics.request_time,
+ 0, pvfs->sharing_violation_delay);
+ } else if (NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) {
+ end_time = timeval_add(&req->statistics.request_time,
+ pvfs->oplock_break_timeout, 0);
+ } else {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ return pvfs_odb_retry_setup(ntvfs, req, lck, end_time, io, NULL,
+ pvfs_retry_rename);
+}
+
+/*
+ rename one file from a wildcard set
+*/
+static NTSTATUS pvfs_rename_one(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
+ const char *dir_path,
+ const char *fname1,
+ const char *fname2,
+ uint16_t attrib)
+{
+ struct pvfs_filename *name1, *name2;
+ TALLOC_CTX *mem_ctx = talloc_new(req);
+ struct odb_lock *lck = NULL;
+ NTSTATUS status;
+
+ /* resolve the wildcard pattern for this name */
+ fname2 = pvfs_resolve_wildcard(mem_ctx, lp_iconv_convenience(pvfs->ntvfs->ctx->lp_ctx), fname1, fname2);
+ if (fname2 == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* get a pvfs_filename source object */
+ status = pvfs_resolve_partial(pvfs, mem_ctx,
+ dir_path, fname1,
+ PVFS_RESOLVE_NO_OPENDB,
+ &name1);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+
+ /* make sure its matches the given attributes */
+ status = pvfs_match_attrib(pvfs, name1, attrib, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+
+ status = pvfs_can_rename(pvfs, req, name1, &lck);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lck);
+ goto failed;
+ }
+
+ /* get a pvfs_filename dest object */
+ status = pvfs_resolve_partial(pvfs, mem_ctx,
+ dir_path, fname2,
+ PVFS_RESOLVE_NO_OPENDB,
+ &name2);
+ if (NT_STATUS_IS_OK(status)) {
+ status = pvfs_can_delete(pvfs, req, name2, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+ }
+
+ status = NT_STATUS_OK;
+
+ fname2 = talloc_asprintf(mem_ctx, "%s/%s", dir_path, fname2);
+ if (fname2 == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = pvfs_do_rename(pvfs, lck, name1, fname2);
+
+failed:
+ talloc_free(mem_ctx);
+ return status;
+}
+
+
+/*
+ rename a set of files with wildcards
+*/
+static NTSTATUS pvfs_rename_wildcard(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
+ union smb_rename *ren,
+ struct pvfs_filename *name1,
+ struct pvfs_filename *name2)
+{
+ struct pvfs_dir *dir;
+ NTSTATUS status;
+ off_t ofs = 0;
+ const char *fname, *fname2, *dir_path;
+ uint16_t attrib = ren->rename.in.attrib;
+ int total_renamed = 0;
+
+ /* get list of matching files */
+ status = pvfs_list_start(pvfs, name1, req, &dir);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = NT_STATUS_NO_SUCH_FILE;
+
+ dir_path = pvfs_list_unix_path(dir);
+
+ /* only allow wildcard renames within a directory */
+ if (strncmp(dir_path, name2->full_name, strlen(dir_path)) != 0 ||
+ name2->full_name[strlen(dir_path)] != '/' ||
+ strchr(name2->full_name + strlen(dir_path) + 1, '/')) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ fname2 = talloc_strdup(name2, name2->full_name + strlen(dir_path) + 1);
+ if (fname2 == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ while ((fname = pvfs_list_next(dir, &ofs))) {
+ status = pvfs_rename_one(pvfs, req,
+ dir_path,
+ fname, fname2, attrib);
+ if (NT_STATUS_IS_OK(status)) {
+ total_renamed++;
+ }
+ }
+
+ if (total_renamed == 0) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ rename a set of files - SMBmv interface
+*/
+static NTSTATUS pvfs_rename_mv(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_rename *ren)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ NTSTATUS status;
+ struct pvfs_filename *name1, *name2;
+ struct odb_lock *lck = NULL;
+
+ /* resolve the cifs name to a posix name */
+ status = pvfs_resolve_name(pvfs, req, ren->rename.in.pattern1,
+ PVFS_RESOLVE_WILDCARD, &name1);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = pvfs_resolve_name(pvfs, req, ren->rename.in.pattern2,
+ PVFS_RESOLVE_WILDCARD, &name2);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (name1->has_wildcard || name2->has_wildcard) {
+ return pvfs_rename_wildcard(pvfs, req, ren, name1, name2);
+ }
+
+ if (!name1->exists) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ if (strcmp(name1->full_name, name2->full_name) == 0) {
+ return NT_STATUS_OK;
+ }
+
+ if (name2->exists) {
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ status = pvfs_match_attrib(pvfs, name1, ren->rename.in.attrib, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = pvfs_can_rename(pvfs, req, name1, &lck);
+ /*
+ * on a sharing violation we need to retry when the file is closed by
+ * the other user, or after 1 second
+ * on a non granted oplock we need to retry when the file is closed by
+ * the other user, or after 30 seconds
+ */
+ if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) &&
+ (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ return pvfs_rename_setup_retry(pvfs->ntvfs, req, ren, lck, status);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = pvfs_do_rename(pvfs, lck, name1, name2->full_name);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ rename a stream
+*/
+static NTSTATUS pvfs_rename_stream(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_rename *ren,
+ struct pvfs_filename *name1)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ NTSTATUS status;
+ struct odb_lock *lck = NULL;
+
+ if (name1->has_wildcard) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (ren->ntrename.in.new_name[0] != ':') {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!name1->exists) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ if (ren->ntrename.in.flags != RENAME_FLAG_RENAME) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = pvfs_can_rename(pvfs, req, name1, &lck);
+ /*
+ * on a sharing violation we need to retry when the file is closed by
+ * the other user, or after 1 second
+ * on a non granted oplock we need to retry when the file is closed by
+ * the other user, or after 30 seconds
+ */
+ if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) &&
+ (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ return pvfs_rename_setup_retry(pvfs->ntvfs, req, ren, lck, status);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = pvfs_access_check_simple(pvfs, req, name1, SEC_FILE_WRITE_ATTRIBUTE);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ status = pvfs_stream_rename(pvfs, name1, -1,
+ ren->ntrename.in.new_name+1);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ rename a set of files - ntrename interface
+*/
+static NTSTATUS pvfs_rename_nt(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_rename *ren)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ NTSTATUS status;
+ struct pvfs_filename *name1, *name2;
+ struct odb_lock *lck = NULL;
+
+ switch (ren->ntrename.in.flags) {
+ case RENAME_FLAG_RENAME:
+ case RENAME_FLAG_HARD_LINK:
+ case RENAME_FLAG_COPY:
+ case RENAME_FLAG_MOVE_CLUSTER_INFORMATION:
+ break;
+ default:
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* resolve the cifs name to a posix name */
+ status = pvfs_resolve_name(pvfs, req, ren->ntrename.in.old_name,
+ PVFS_RESOLVE_WILDCARD | PVFS_RESOLVE_STREAMS, &name1);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (name1->stream_name) {
+ /* stream renames need to be handled separately */
+ return pvfs_rename_stream(ntvfs, req, ren, name1);
+ }
+
+ status = pvfs_resolve_name(pvfs, req, ren->ntrename.in.new_name,
+ PVFS_RESOLVE_WILDCARD, &name2);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (name1->has_wildcard || name2->has_wildcard) {
+ return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
+ }
+
+ if (!name1->exists) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ if (strcmp(name1->full_name, name2->full_name) == 0) {
+ return NT_STATUS_OK;
+ }
+
+ if (name2->exists) {
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ status = pvfs_match_attrib(pvfs, name1, ren->ntrename.in.attrib, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = pvfs_can_rename(pvfs, req, name1, &lck);
+ /*
+ * on a sharing violation we need to retry when the file is closed by
+ * the other user, or after 1 second
+ * on a non granted oplock we need to retry when the file is closed by
+ * the other user, or after 30 seconds
+ */
+ if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) &&
+ (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ return pvfs_rename_setup_retry(pvfs->ntvfs, req, ren, lck, status);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ switch (ren->ntrename.in.flags) {
+ case RENAME_FLAG_RENAME:
+ status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
+ NT_STATUS_NOT_OK_RETURN(status);
+ status = pvfs_do_rename(pvfs, lck, name1, name2->full_name);
+ NT_STATUS_NOT_OK_RETURN(status);
+ break;
+
+ case RENAME_FLAG_HARD_LINK:
+ status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
+ NT_STATUS_NOT_OK_RETURN(status);
+ if (link(name1->full_name, name2->full_name) == -1) {
+ return pvfs_map_errno(pvfs, errno);
+ }
+ break;
+
+ case RENAME_FLAG_COPY:
+ status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
+ NT_STATUS_NOT_OK_RETURN(status);
+ return pvfs_copy_file(pvfs, name1, name2);
+
+ case RENAME_FLAG_MOVE_CLUSTER_INFORMATION:
+ return NT_STATUS_INVALID_PARAMETER;
+
+ default:
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+
+ return NT_STATUS_OK;
+}
+
+/*
+ rename a set of files - ntrename interface
+*/
+NTSTATUS pvfs_rename(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_rename *ren)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ struct pvfs_file *f;
+
+ switch (ren->generic.level) {
+ case RAW_RENAME_RENAME:
+ return pvfs_rename_mv(ntvfs, req, ren);
+
+ case RAW_RENAME_NTRENAME:
+ return pvfs_rename_nt(ntvfs, req, ren);
+
+ case RAW_RENAME_NTTRANS:
+ f = pvfs_find_fd(pvfs, req, ren->nttrans.in.file.ntvfs);
+ if (!f) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ /* wk23 ignores the request */
+ return NT_STATUS_OK;
+
+ default:
+ break;
+ }
+
+ return NT_STATUS_INVALID_LEVEL;
+}
+
diff --git a/source4/ntvfs/posix/pvfs_resolve.c b/source4/ntvfs/posix/pvfs_resolve.c
new file mode 100644
index 0000000000..c33323350e
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_resolve.c
@@ -0,0 +1,806 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - filename resolution
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ this is the core code for converting a filename from the format as
+ given by a client to a posix filename, including any case-matching
+ required, and checks for legal characters
+*/
+
+
+#include "includes.h"
+#include "vfs_posix.h"
+#include "system/dir.h"
+#include "param/param.h"
+
+/**
+ compare two filename components. This is where the name mangling hook will go
+*/
+static int component_compare(struct pvfs_state *pvfs, const char *comp, const char *name)
+{
+ int ret;
+
+ ret = strcasecmp_m(comp, name);
+
+ if (ret != 0) {
+ char *shortname = pvfs_short_name_component(pvfs, name);
+ if (shortname) {
+ ret = strcasecmp_m(comp, shortname);
+ talloc_free(shortname);
+ }
+ }
+
+ return ret;
+}
+
+/*
+ search for a filename in a case insensitive fashion
+
+ TODO: add a cache for previously resolved case-insensitive names
+ TODO: add mangled name support
+*/
+static NTSTATUS pvfs_case_search(struct pvfs_state *pvfs,
+ struct pvfs_filename *name,
+ uint_t flags)
+{
+ /* break into a series of components */
+ int num_components;
+ char **components;
+ char *p, *partial_name;
+ int i;
+
+ /* break up the full name info pathname components */
+ num_components=2;
+ p = name->full_name + strlen(pvfs->base_directory) + 1;
+
+ for (;*p;p++) {
+ if (*p == '/') {
+ num_components++;
+ }
+ }
+
+ components = talloc_array(name, char *, num_components);
+ p = name->full_name + strlen(pvfs->base_directory);
+ *p++ = 0;
+
+ components[0] = name->full_name;
+
+ for (i=1;i<num_components;i++) {
+ components[i] = p;
+ p = strchr(p, '/');
+ if (p) *p++ = 0;
+ if (pvfs_is_reserved_name(pvfs, components[i])) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ }
+
+ partial_name = talloc_strdup(name, components[0]);
+ if (!partial_name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* for each component, check if it exists as-is, and if not then
+ do a directory scan */
+ for (i=1;i<num_components;i++) {
+ char *test_name;
+ DIR *dir;
+ struct dirent *de;
+ char *long_component;
+
+ /* possibly remap from the short name cache */
+ long_component = pvfs_mangled_lookup(pvfs, name, components[i]);
+ if (long_component) {
+ components[i] = long_component;
+ }
+
+ test_name = talloc_asprintf(name, "%s/%s", partial_name, components[i]);
+ if (!test_name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* check if this component exists as-is */
+ if (stat(test_name, &name->st) == 0) {
+ if (i<num_components-1 && !S_ISDIR(name->st.st_mode)) {
+ return NT_STATUS_OBJECT_PATH_NOT_FOUND;
+ }
+ talloc_free(partial_name);
+ partial_name = test_name;
+ if (i == num_components - 1) {
+ name->exists = true;
+ }
+ continue;
+ }
+
+ /* the filesystem might be case insensitive, in which
+ case a search is pointless unless the name is
+ mangled */
+ if ((pvfs->flags & PVFS_FLAG_CI_FILESYSTEM) &&
+ !pvfs_is_mangled_component(pvfs, components[i])) {
+ if (i < num_components-1) {
+ return NT_STATUS_OBJECT_PATH_NOT_FOUND;
+ }
+ partial_name = test_name;
+ continue;
+ }
+
+ dir = opendir(partial_name);
+ if (!dir) {
+ return pvfs_map_errno(pvfs, errno);
+ }
+
+ while ((de = readdir(dir))) {
+ if (component_compare(pvfs, components[i], de->d_name) == 0) {
+ break;
+ }
+ }
+
+ if (!de) {
+ if (i < num_components-1) {
+ closedir(dir);
+ return NT_STATUS_OBJECT_PATH_NOT_FOUND;
+ }
+ } else {
+ components[i] = talloc_strdup(name, de->d_name);
+ }
+ test_name = talloc_asprintf(name, "%s/%s", partial_name, components[i]);
+ talloc_free(partial_name);
+ partial_name = test_name;
+
+ closedir(dir);
+ }
+
+ if (!name->exists) {
+ if (stat(partial_name, &name->st) == 0) {
+ name->exists = true;
+ }
+ }
+
+ talloc_free(name->full_name);
+ name->full_name = partial_name;
+
+ if (name->exists) {
+ return pvfs_fill_dos_info(pvfs, name, flags, -1);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ parse a alternate data stream name
+*/
+static NTSTATUS parse_stream_name(struct smb_iconv_convenience *ic,
+ struct pvfs_filename *name,
+ const char *s)
+{
+ char *p, *stream_name;
+ if (s[1] == '\0') {
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ }
+ name->stream_name = stream_name = talloc_strdup(name, s+1);
+ if (name->stream_name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ p = stream_name;
+
+ while (*p) {
+ size_t c_size;
+ codepoint_t c = next_codepoint_convenience(ic, p, &c_size);
+
+ switch (c) {
+ case '/':
+ case '\\':
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ case ':':
+ *p= 0;
+ p++;
+ if (*p == '\0') {
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ }
+ if (strcasecmp_m(p, "$DATA") != 0) {
+ if (strchr_m(p, ':')) {
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ }
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ c_size = 0;
+ p--;
+ break;
+ }
+
+ p += c_size;
+ }
+
+ if (strcmp(name->stream_name, "") == 0) {
+ /*
+ * we don't set stream_name to NULL, here
+ * as this would be wrong for directories
+ *
+ * pvfs_fill_dos_info() will set it to NULL
+ * if it's not a directory.
+ */
+ name->stream_id = 0;
+ } else {
+ name->stream_id = pvfs_name_hash(name->stream_name,
+ strlen(name->stream_name));
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ convert a CIFS pathname to a unix pathname. Note that this does NOT
+ take into account case insensitivity, and in fact does not access
+ the filesystem at all. It is merely a reformatting and charset
+ checking routine.
+
+ errors are returned if the filename is illegal given the flags
+*/
+static NTSTATUS pvfs_unix_path(struct pvfs_state *pvfs, const char *cifs_name,
+ uint_t flags, struct pvfs_filename *name)
+{
+ char *ret, *p, *p_start;
+ struct smb_iconv_convenience *ic = NULL;
+ NTSTATUS status;
+
+ name->original_name = talloc_strdup(name, cifs_name);
+ name->stream_name = NULL;
+ name->stream_id = 0;
+ name->has_wildcard = false;
+
+ while (*cifs_name == '\\') {
+ cifs_name++;
+ }
+
+ if (*cifs_name == 0) {
+ name->full_name = talloc_asprintf(name, "%s/.", pvfs->base_directory);
+ if (name->full_name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ return NT_STATUS_OK;
+ }
+
+ ret = talloc_asprintf(name, "%s/%s", pvfs->base_directory, cifs_name);
+ if (ret == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ p = ret + strlen(pvfs->base_directory) + 1;
+
+ /* now do an in-place conversion of '\' to '/', checking
+ for legal characters */
+ p_start = p;
+
+ ic = lp_iconv_convenience(pvfs->ntvfs->ctx->lp_ctx);
+ while (*p) {
+ size_t c_size;
+ codepoint_t c = next_codepoint_convenience(ic, p, &c_size);
+
+ if (c <= 0x1F) {
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ }
+
+ switch (c) {
+ case '\\':
+ if (name->has_wildcard) {
+ /* wildcards are only allowed in the last part
+ of a name */
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ }
+ if (p > p_start && (p[1] == '\\' || p[1] == '\0')) {
+ /* see if it is definately a "\\" or
+ * a trailing "\". If it is then fail here,
+ * and let the next layer up try again after
+ * pvfs_reduce_name() if it wants to. This is
+ * much more efficient on average than always
+ * scanning for these separately
+ */
+ return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
+ } else {
+ *p = '/';
+ }
+ break;
+ case ':':
+ if (!(flags & PVFS_RESOLVE_STREAMS)) {
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ }
+ if (name->has_wildcard) {
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ }
+ status = parse_stream_name(ic, name, p);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ *p-- = 0;
+ break;
+ case '*':
+ case '>':
+ case '<':
+ case '?':
+ case '"':
+ if (!(flags & PVFS_RESOLVE_WILDCARD)) {
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ }
+ name->has_wildcard = true;
+ break;
+ case '/':
+ case '|':
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ case '.':
+ /* see if it is definately a .. or
+ . component. If it is then fail here, and
+ let the next layer up try again after
+ pvfs_reduce_name() if it wants to. This is
+ much more efficient on average than always
+ scanning for these separately */
+ if (p[1] == '.' &&
+ (p[2] == 0 || p[2] == '\\') &&
+ (p == p_start || p[-1] == '/')) {
+ return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
+ }
+ if ((p[1] == 0 || p[1] == '\\') &&
+ (p == p_start || p[-1] == '/')) {
+ return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
+ }
+ break;
+ }
+
+ p += c_size;
+ }
+
+ name->full_name = ret;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ reduce a name that contains .. components or repeated \ separators
+ return NULL if it can't be reduced
+*/
+static NTSTATUS pvfs_reduce_name(TALLOC_CTX *mem_ctx,
+ struct smb_iconv_convenience *iconv_convenience,
+ const char **fname, uint_t flags)
+{
+ codepoint_t c;
+ size_t c_size, len;
+ int i, num_components, err_count;
+ char **components;
+ char *p, *s, *ret;
+
+ s = talloc_strdup(mem_ctx, *fname);
+ if (s == NULL) return NT_STATUS_NO_MEMORY;
+
+ for (num_components=1, p=s; *p; p += c_size) {
+ c = next_codepoint_convenience(iconv_convenience, p, &c_size);
+ if (c == '\\') num_components++;
+ }
+
+ components = talloc_array(s, char *, num_components+1);
+ if (components == NULL) {
+ talloc_free(s);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ components[0] = s;
+ for (i=0, p=s; *p; p += c_size) {
+ c = next_codepoint_convenience(iconv_convenience, p, &c_size);
+ if (c == '\\') {
+ *p = 0;
+ components[++i] = p+1;
+ }
+ }
+ components[i+1] = NULL;
+
+ /*
+ rather bizarre!
+
+ '.' components are not allowed, but the rules for what error
+ code to give don't seem to make sense. This is a close
+ approximation.
+ */
+ for (err_count=i=0;components[i];i++) {
+ if (strcmp(components[i], "") == 0) {
+ continue;
+ }
+ if (ISDOT(components[i]) || err_count) {
+ err_count++;
+ }
+ }
+ if (err_count) {
+ if (flags & PVFS_RESOLVE_WILDCARD) err_count--;
+
+ if (err_count==1) {
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ } else {
+ return NT_STATUS_OBJECT_PATH_NOT_FOUND;
+ }
+ }
+
+ /* remove any null components */
+ for (i=0;components[i];i++) {
+ if (strcmp(components[i], "") == 0) {
+ memmove(&components[i], &components[i+1],
+ sizeof(char *)*(num_components-i));
+ i--;
+ continue;
+ }
+ if (ISDOTDOT(components[i])) {
+ if (i < 1) return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
+ memmove(&components[i-1], &components[i+1],
+ sizeof(char *)*(num_components-i));
+ i -= 2;
+ continue;
+ }
+ }
+
+ if (components[0] == NULL) {
+ talloc_free(s);
+ *fname = talloc_strdup(mem_ctx, "\\");
+ return NT_STATUS_OK;
+ }
+
+ for (len=i=0;components[i];i++) {
+ len += strlen(components[i]) + 1;
+ }
+
+ /* rebuild the name */
+ ret = talloc_array(mem_ctx, char, len+1);
+ if (ret == NULL) {
+ talloc_free(s);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (len=0,i=0;components[i];i++) {
+ size_t len1 = strlen(components[i]);
+ ret[len] = '\\';
+ memcpy(ret+len+1, components[i], len1);
+ len += len1 + 1;
+ }
+ ret[len] = 0;
+
+ talloc_set_name_const(ret, ret);
+
+ talloc_free(s);
+
+ *fname = ret;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ resolve a name from relative client format to a struct pvfs_filename
+ the memory for the filename is made as a talloc child of 'name'
+
+ flags include:
+ PVFS_RESOLVE_NO_WILDCARD = wildcards are considered illegal characters
+ PVFS_RESOLVE_STREAMS = stream names are allowed
+
+ TODO: ../ collapsing, and outside share checking
+*/
+NTSTATUS pvfs_resolve_name(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
+ const char *cifs_name,
+ uint_t flags, struct pvfs_filename **name)
+{
+ NTSTATUS status;
+
+ *name = talloc(mem_ctx, struct pvfs_filename);
+ if (*name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*name)->exists = false;
+ (*name)->stream_exists = false;
+
+ if (!(pvfs->fs_attribs & FS_ATTR_NAMED_STREAMS)) {
+ flags &= ~PVFS_RESOLVE_STREAMS;
+ }
+
+ /* do the basic conversion to a unix formatted path,
+ also checking for allowable characters */
+ status = pvfs_unix_path(pvfs, cifs_name, flags, *name);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD)) {
+ /* it might contain .. components which need to be reduced */
+ status = pvfs_reduce_name(*name, lp_iconv_convenience(pvfs->ntvfs->ctx->lp_ctx), &cifs_name, flags);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ status = pvfs_unix_path(pvfs, cifs_name, flags, *name);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* if it has a wildcard then no point doing a stat() of the
+ full name. Instead We need check if the directory exists
+ */
+ if ((*name)->has_wildcard) {
+ const char *p;
+ char *dir_name, *saved_name;
+ p = strrchr((*name)->full_name, '/');
+ if (p == NULL) {
+ /* root directory wildcard is OK */
+ return NT_STATUS_OK;
+ }
+ dir_name = talloc_strndup(*name, (*name)->full_name, (p-(*name)->full_name));
+ if (stat(dir_name, &(*name)->st) == 0) {
+ talloc_free(dir_name);
+ return NT_STATUS_OK;
+ }
+ /* we need to search for a matching name */
+ saved_name = (*name)->full_name;
+ (*name)->full_name = dir_name;
+ status = pvfs_case_search(pvfs, *name, flags);
+ if (!NT_STATUS_IS_OK(status)) {
+ /* the directory doesn't exist */
+ (*name)->full_name = saved_name;
+ return status;
+ }
+ /* it does exist, but might need a case change */
+ if (dir_name != (*name)->full_name) {
+ (*name)->full_name = talloc_asprintf(*name, "%s%s",
+ (*name)->full_name, p);
+ NT_STATUS_HAVE_NO_MEMORY((*name)->full_name);
+ } else {
+ (*name)->full_name = saved_name;
+ talloc_free(dir_name);
+ }
+ return NT_STATUS_OK;
+ }
+
+ /* if we can stat() the full name now then we are done */
+ if (stat((*name)->full_name, &(*name)->st) == 0) {
+ (*name)->exists = true;
+ return pvfs_fill_dos_info(pvfs, *name, flags, -1);
+ }
+
+ /* search for a matching filename */
+ status = pvfs_case_search(pvfs, *name, flags);
+
+ return status;
+}
+
+
+/*
+ do a partial resolve, returning a pvfs_filename structure given a
+ base path and a relative component. It is an error if the file does
+ not exist. No case-insensitive matching is done.
+
+ this is used in places like directory searching where we need a pvfs_filename
+ to pass to a function, but already know the unix base directory and component
+*/
+NTSTATUS pvfs_resolve_partial(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
+ const char *unix_dir, const char *fname,
+ uint_t flags, struct pvfs_filename **name)
+{
+ NTSTATUS status;
+
+ *name = talloc(mem_ctx, struct pvfs_filename);
+ if (*name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*name)->full_name = talloc_asprintf(*name, "%s/%s", unix_dir, fname);
+ if ((*name)->full_name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (stat((*name)->full_name, &(*name)->st) == -1) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ (*name)->exists = true;
+ (*name)->stream_exists = true;
+ (*name)->has_wildcard = false;
+ (*name)->original_name = talloc_strdup(*name, fname);
+ (*name)->stream_name = NULL;
+ (*name)->stream_id = 0;
+
+ status = pvfs_fill_dos_info(pvfs, *name, flags, -1);
+
+ return status;
+}
+
+
+/*
+ fill in the pvfs_filename info for an open file, given the current
+ info for a (possibly) non-open file. This is used by places that need
+ to update the pvfs_filename stat information, and by pvfs_open()
+*/
+NTSTATUS pvfs_resolve_name_fd(struct pvfs_state *pvfs, int fd,
+ struct pvfs_filename *name, uint_t flags)
+{
+ dev_t device = (dev_t)0;
+ ino_t inode = 0;
+
+ if (name->exists) {
+ device = name->st.st_dev;
+ inode = name->st.st_ino;
+ }
+
+ if (fd == -1) {
+ if (stat(name->full_name, &name->st) == -1) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+ } else {
+ if (fstat(fd, &name->st) == -1) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+ }
+
+ if (name->exists &&
+ (device != name->st.st_dev || inode != name->st.st_ino)) {
+ /* the file we are looking at has changed! this could
+ be someone trying to exploit a race
+ condition. Certainly we don't want to continue
+ operating on this file */
+ DEBUG(0,("pvfs: WARNING: file '%s' changed during resolve - failing\n",
+ name->full_name));
+ return NT_STATUS_UNEXPECTED_IO_ERROR;
+ }
+
+ name->exists = true;
+
+ return pvfs_fill_dos_info(pvfs, name, flags, fd);
+}
+
+/*
+ fill in the pvfs_filename info for an open file, given the current
+ info for a (possibly) non-open file. This is used by places that need
+ to update the pvfs_filename stat information, and the path
+ after a possible rename on a different handle.
+*/
+NTSTATUS pvfs_resolve_name_handle(struct pvfs_state *pvfs,
+ struct pvfs_file_handle *h)
+{
+ NTSTATUS status;
+
+ if (h->have_opendb_entry) {
+ struct odb_lock *lck;
+ const char *name = NULL;
+
+ lck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
+ if (lck == NULL) {
+ DEBUG(0,("%s: failed to lock file '%s' in opendb\n",
+ __FUNCTION__, h->name->full_name));
+ /* we were supposed to do a blocking lock, so something
+ is badly wrong! */
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ status = odb_get_path(lck, &name);
+ if (NT_STATUS_IS_OK(status)) {
+ /*
+ * This relies an the fact that
+ * renames of open files are only
+ * allowed by setpathinfo() and setfileinfo()
+ * and there're only renames within the same
+ * directory supported
+ */
+ if (strcmp(h->name->full_name, name) != 0) {
+ const char *orig_dir;
+ const char *new_file;
+ const char *new_orig;
+ char *delim;
+
+ delim = strrchr(name, '/');
+ if (!delim) {
+ talloc_free(lck);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ new_file = delim + 1;
+ delim = strrchr(h->name->original_name, '\\');
+ if (delim) {
+ delim[0] = '\0';
+ orig_dir = h->name->original_name;
+ new_orig = talloc_asprintf(h->name, "%s\\%s",
+ orig_dir, new_file);
+ if (!new_orig) {
+ talloc_free(lck);
+ return NT_STATUS_NO_MEMORY;
+ }
+ } else {
+ new_orig = talloc_strdup(h->name, new_file);
+ if (!new_orig) {
+ talloc_free(lck);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ talloc_free(h->name->original_name);
+ talloc_free(h->name->full_name);
+ h->name->full_name = talloc_steal(h->name, name);
+ h->name->original_name = new_orig;
+ }
+ }
+
+ talloc_free(lck);
+ }
+
+ /*
+ * TODO: pass PVFS_RESOLVE_NO_OPENDB and get
+ * the write time from odb_lock() above.
+ */
+ status = pvfs_resolve_name_fd(pvfs, h->fd, h->name, 0);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ if (!null_nttime(h->write_time.close_time)) {
+ h->name->dos.write_time = h->write_time.close_time;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ resolve the parent of a given name
+*/
+NTSTATUS pvfs_resolve_parent(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
+ const struct pvfs_filename *child,
+ struct pvfs_filename **name)
+{
+ NTSTATUS status;
+ char *p;
+
+ *name = talloc(mem_ctx, struct pvfs_filename);
+ if (*name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*name)->full_name = talloc_strdup(*name, child->full_name);
+ if ((*name)->full_name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ p = strrchr_m((*name)->full_name, '/');
+ if (p == NULL) {
+ return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
+ }
+
+ /* this handles the root directory */
+ if (p == (*name)->full_name) {
+ p[1] = 0;
+ } else {
+ p[0] = 0;
+ }
+
+ if (stat((*name)->full_name, &(*name)->st) == -1) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ (*name)->exists = true;
+ (*name)->stream_exists = true;
+ (*name)->has_wildcard = false;
+ /* we can't get the correct 'original_name', but for the purposes
+ of this call this is close enough */
+ (*name)->original_name = talloc_reference(*name, child->original_name);
+ (*name)->stream_name = NULL;
+ (*name)->stream_id = 0;
+
+ status = pvfs_fill_dos_info(pvfs, *name, PVFS_RESOLVE_NO_OPENDB, -1);
+
+ return status;
+}
diff --git a/source4/ntvfs/posix/pvfs_search.c b/source4/ntvfs/posix/pvfs_search.c
new file mode 100644
index 0000000000..dc4f86b4d2
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_search.c
@@ -0,0 +1,862 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - directory search functions
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "vfs_posix.h"
+#include "system/time.h"
+#include "librpc/gen_ndr/security.h"
+#include "smbd/service_stream.h"
+#include "lib/events/events.h"
+#include "../lib/util/dlinklist.h"
+
+/* place a reasonable limit on old-style searches as clients tend to
+ not send search close requests */
+#define MAX_OLD_SEARCHES 2000
+#define MAX_SEARCH_HANDLES (UINT16_MAX - 1)
+#define INVALID_SEARCH_HANDLE UINT16_MAX
+
+/*
+ destroy an open search
+*/
+static int pvfs_search_destructor(struct pvfs_search_state *search)
+{
+ DLIST_REMOVE(search->pvfs->search.list, search);
+ idr_remove(search->pvfs->search.idtree, search->handle);
+ return 0;
+}
+
+/*
+ called when a search timer goes off
+*/
+static void pvfs_search_timer(struct tevent_context *ev, struct tevent_timer *te,
+ struct timeval t, void *ptr)
+{
+ struct pvfs_search_state *search = talloc_get_type(ptr, struct pvfs_search_state);
+ talloc_free(search);
+}
+
+/*
+ setup a timer to destroy a open search after a inactivity period
+*/
+static void pvfs_search_setup_timer(struct pvfs_search_state *search)
+{
+ struct tevent_context *ev = search->pvfs->ntvfs->ctx->event_ctx;
+ if (search->handle == INVALID_SEARCH_HANDLE) return;
+ talloc_free(search->te);
+ search->te = event_add_timed(ev, search,
+ timeval_current_ofs(search->pvfs->search.inactivity_time, 0),
+ pvfs_search_timer, search);
+}
+
+/*
+ fill in a single search result for a given info level
+*/
+static NTSTATUS fill_search_info(struct pvfs_state *pvfs,
+ enum smb_search_data_level level,
+ const char *unix_path,
+ const char *fname,
+ struct pvfs_search_state *search,
+ off_t dir_offset,
+ union smb_search_data *file)
+{
+ struct pvfs_filename *name;
+ NTSTATUS status;
+ const char *shortname;
+ uint32_t dir_index = (uint32_t)dir_offset; /* truncated - see the code
+ in pvfs_list_seek_ofs() for
+ how we cope with this */
+
+ status = pvfs_resolve_partial(pvfs, file, unix_path, fname, 0, &name);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = pvfs_match_attrib(pvfs, name, search->search_attrib, search->must_attrib);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ switch (level) {
+ case RAW_SEARCH_DATA_SEARCH:
+ shortname = pvfs_short_name(pvfs, name, name);
+ file->search.attrib = name->dos.attrib;
+ file->search.write_time = nt_time_to_unix(name->dos.write_time);
+ file->search.size = name->st.st_size;
+ file->search.name = shortname;
+ file->search.id.reserved = search->handle >> 8;
+ memset(file->search.id.name, ' ', sizeof(file->search.id.name));
+ memcpy(file->search.id.name, shortname,
+ MIN(strlen(shortname)+1, sizeof(file->search.id.name)));
+ file->search.id.handle = search->handle & 0xFF;
+ file->search.id.server_cookie = dir_index;
+ file->search.id.client_cookie = 0;
+ return NT_STATUS_OK;
+
+ case RAW_SEARCH_DATA_STANDARD:
+ file->standard.resume_key = dir_index;
+ file->standard.create_time = nt_time_to_unix(name->dos.create_time);
+ file->standard.access_time = nt_time_to_unix(name->dos.access_time);
+ file->standard.write_time = nt_time_to_unix(name->dos.write_time);
+ file->standard.size = name->st.st_size;
+ file->standard.alloc_size = name->dos.alloc_size;
+ file->standard.attrib = name->dos.attrib;
+ file->standard.name.s = fname;
+ return NT_STATUS_OK;
+
+ case RAW_SEARCH_DATA_EA_SIZE:
+ file->ea_size.resume_key = dir_index;
+ file->ea_size.create_time = nt_time_to_unix(name->dos.create_time);
+ file->ea_size.access_time = nt_time_to_unix(name->dos.access_time);
+ file->ea_size.write_time = nt_time_to_unix(name->dos.write_time);
+ file->ea_size.size = name->st.st_size;
+ file->ea_size.alloc_size = name->dos.alloc_size;
+ file->ea_size.attrib = name->dos.attrib;
+ file->ea_size.ea_size = name->dos.ea_size;
+ file->ea_size.name.s = fname;
+ return NT_STATUS_OK;
+
+ case RAW_SEARCH_DATA_EA_LIST:
+ file->ea_list.resume_key = dir_index;
+ file->ea_list.create_time = nt_time_to_unix(name->dos.create_time);
+ file->ea_list.access_time = nt_time_to_unix(name->dos.access_time);
+ file->ea_list.write_time = nt_time_to_unix(name->dos.write_time);
+ file->ea_list.size = name->st.st_size;
+ file->ea_list.alloc_size = name->dos.alloc_size;
+ file->ea_list.attrib = name->dos.attrib;
+ file->ea_list.name.s = fname;
+ return pvfs_query_ea_list(pvfs, file, name, -1,
+ search->num_ea_names,
+ search->ea_names,
+ &file->ea_list.eas);
+
+ case RAW_SEARCH_DATA_DIRECTORY_INFO:
+ file->directory_info.file_index = dir_index;
+ file->directory_info.create_time = name->dos.create_time;
+ file->directory_info.access_time = name->dos.access_time;
+ file->directory_info.write_time = name->dos.write_time;
+ file->directory_info.change_time = name->dos.change_time;
+ file->directory_info.size = name->st.st_size;
+ file->directory_info.alloc_size = name->dos.alloc_size;
+ file->directory_info.attrib = name->dos.attrib;
+ file->directory_info.name.s = fname;
+ return NT_STATUS_OK;
+
+ case RAW_SEARCH_DATA_FULL_DIRECTORY_INFO:
+ file->full_directory_info.file_index = dir_index;
+ file->full_directory_info.create_time = name->dos.create_time;
+ file->full_directory_info.access_time = name->dos.access_time;
+ file->full_directory_info.write_time = name->dos.write_time;
+ file->full_directory_info.change_time = name->dos.change_time;
+ file->full_directory_info.size = name->st.st_size;
+ file->full_directory_info.alloc_size = name->dos.alloc_size;
+ file->full_directory_info.attrib = name->dos.attrib;
+ file->full_directory_info.ea_size = name->dos.ea_size;
+ file->full_directory_info.name.s = fname;
+ return NT_STATUS_OK;
+
+ case RAW_SEARCH_DATA_NAME_INFO:
+ file->name_info.file_index = dir_index;
+ file->name_info.name.s = fname;
+ return NT_STATUS_OK;
+
+ case RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO:
+ file->both_directory_info.file_index = dir_index;
+ file->both_directory_info.create_time = name->dos.create_time;
+ file->both_directory_info.access_time = name->dos.access_time;
+ file->both_directory_info.write_time = name->dos.write_time;
+ file->both_directory_info.change_time = name->dos.change_time;
+ file->both_directory_info.size = name->st.st_size;
+ file->both_directory_info.alloc_size = name->dos.alloc_size;
+ file->both_directory_info.attrib = name->dos.attrib;
+ file->both_directory_info.ea_size = name->dos.ea_size;
+ file->both_directory_info.short_name.s = pvfs_short_name(pvfs, file, name);
+ file->both_directory_info.name.s = fname;
+ return NT_STATUS_OK;
+
+ case RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO:
+ file->id_full_directory_info.file_index = dir_index;
+ file->id_full_directory_info.create_time = name->dos.create_time;
+ file->id_full_directory_info.access_time = name->dos.access_time;
+ file->id_full_directory_info.write_time = name->dos.write_time;
+ file->id_full_directory_info.change_time = name->dos.change_time;
+ file->id_full_directory_info.size = name->st.st_size;
+ file->id_full_directory_info.alloc_size = name->dos.alloc_size;
+ file->id_full_directory_info.attrib = name->dos.attrib;
+ file->id_full_directory_info.ea_size = name->dos.ea_size;
+ file->id_full_directory_info.file_id = name->dos.file_id;
+ file->id_full_directory_info.name.s = fname;
+ return NT_STATUS_OK;
+
+ case RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO:
+ file->id_both_directory_info.file_index = dir_index;
+ file->id_both_directory_info.create_time = name->dos.create_time;
+ file->id_both_directory_info.access_time = name->dos.access_time;
+ file->id_both_directory_info.write_time = name->dos.write_time;
+ file->id_both_directory_info.change_time = name->dos.change_time;
+ file->id_both_directory_info.size = name->st.st_size;
+ file->id_both_directory_info.alloc_size = name->dos.alloc_size;
+ file->id_both_directory_info.attrib = name->dos.attrib;
+ file->id_both_directory_info.ea_size = name->dos.ea_size;
+ file->id_both_directory_info.file_id = name->dos.file_id;
+ file->id_both_directory_info.short_name.s = pvfs_short_name(pvfs, file, name);
+ file->id_both_directory_info.name.s = fname;
+ return NT_STATUS_OK;
+
+ case RAW_SEARCH_DATA_GENERIC:
+ break;
+ }
+
+ return NT_STATUS_INVALID_LEVEL;
+}
+
+
+/*
+ the search fill loop
+*/
+static NTSTATUS pvfs_search_fill(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
+ uint_t max_count,
+ struct pvfs_search_state *search,
+ enum smb_search_data_level level,
+ uint_t *reply_count,
+ void *search_private,
+ bool (*callback)(void *, const union smb_search_data *))
+{
+ struct pvfs_dir *dir = search->dir;
+ NTSTATUS status;
+
+ *reply_count = 0;
+
+ if (max_count == 0) {
+ max_count = 1;
+ }
+
+ while ((*reply_count) < max_count) {
+ union smb_search_data *file;
+ const char *name;
+ off_t ofs = search->current_index;
+
+ name = pvfs_list_next(dir, &search->current_index);
+ if (name == NULL) break;
+
+ file = talloc(mem_ctx, union smb_search_data);
+ if (!file) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = fill_search_info(pvfs, level,
+ pvfs_list_unix_path(dir), name,
+ search, search->current_index, file);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(file);
+ continue;
+ }
+
+ if (!callback(search_private, file)) {
+ talloc_free(file);
+ search->current_index = ofs;
+ break;
+ }
+
+ (*reply_count)++;
+ talloc_free(file);
+ }
+
+ pvfs_search_setup_timer(search);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ we've run out of search handles - cleanup those that the client forgot
+ to close
+*/
+static void pvfs_search_cleanup(struct pvfs_state *pvfs)
+{
+ int i;
+ time_t t = time(NULL);
+
+ for (i=0;i<MAX_OLD_SEARCHES;i++) {
+ struct pvfs_search_state *search;
+ void *p = idr_find(pvfs->search.idtree, i);
+
+ if (p == NULL) return;
+
+ search = talloc_get_type(p, struct pvfs_search_state);
+ if (pvfs_list_eos(search->dir, search->current_index) &&
+ search->last_used != 0 &&
+ t > search->last_used + 30) {
+ /* its almost certainly been forgotten
+ about */
+ talloc_free(search);
+ }
+ }
+}
+
+
+/*
+ list files in a directory matching a wildcard pattern - old SMBsearch interface
+*/
+static NTSTATUS pvfs_search_first_old(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_search_first *io,
+ void *search_private,
+ bool (*callback)(void *, const union smb_search_data *))
+{
+ struct pvfs_dir *dir;
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ struct pvfs_search_state *search;
+ uint_t reply_count;
+ uint16_t search_attrib;
+ const char *pattern;
+ NTSTATUS status;
+ struct pvfs_filename *name;
+ int id;
+
+ search_attrib = io->search_first.in.search_attrib;
+ pattern = io->search_first.in.pattern;
+
+ /* resolve the cifs name to a posix name */
+ status = pvfs_resolve_name(pvfs, req, pattern, PVFS_RESOLVE_WILDCARD, &name);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (!name->has_wildcard && !name->exists) {
+ return STATUS_NO_MORE_FILES;
+ }
+
+ status = pvfs_access_check_parent(pvfs, req, name, SEC_DIR_TRAVERSE | SEC_DIR_LIST);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* we initially make search a child of the request, then if we
+ need to keep it long term we steal it for the private
+ structure */
+ search = talloc(req, struct pvfs_search_state);
+ if (!search) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* do the actual directory listing */
+ status = pvfs_list_start(pvfs, name, search, &dir);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* we need to give a handle back to the client so it
+ can continue a search */
+ id = idr_get_new(pvfs->search.idtree, search, MAX_OLD_SEARCHES);
+ if (id == -1) {
+ pvfs_search_cleanup(pvfs);
+ id = idr_get_new(pvfs->search.idtree, search, MAX_OLD_SEARCHES);
+ }
+ if (id == -1) {
+ return NT_STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ search->pvfs = pvfs;
+ search->handle = id;
+ search->dir = dir;
+ search->current_index = 0;
+ search->search_attrib = search_attrib & 0xFF;
+ search->must_attrib = (search_attrib>>8) & 0xFF;
+ search->last_used = time(NULL);
+ search->te = NULL;
+
+ DLIST_ADD(pvfs->search.list, search);
+
+ talloc_set_destructor(search, pvfs_search_destructor);
+
+ status = pvfs_search_fill(pvfs, req, io->search_first.in.max_count, search, io->generic.data_level,
+ &reply_count, search_private, callback);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ io->search_first.out.count = reply_count;
+
+ /* not matching any entries is an error */
+ if (reply_count == 0) {
+ return STATUS_NO_MORE_FILES;
+ }
+
+ talloc_steal(pvfs, search);
+
+ return NT_STATUS_OK;
+}
+
+/* continue a old style search */
+static NTSTATUS pvfs_search_next_old(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_search_next *io,
+ void *search_private,
+ bool (*callback)(void *, const union smb_search_data *))
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ void *p;
+ struct pvfs_search_state *search;
+ struct pvfs_dir *dir;
+ uint_t reply_count, max_count;
+ uint16_t handle;
+ NTSTATUS status;
+
+ handle = io->search_next.in.id.handle | (io->search_next.in.id.reserved<<8);
+ max_count = io->search_next.in.max_count;
+
+ p = idr_find(pvfs->search.idtree, handle);
+ if (p == NULL) {
+ /* we didn't find the search handle */
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ search = talloc_get_type(p, struct pvfs_search_state);
+
+ dir = search->dir;
+
+ status = pvfs_list_seek_ofs(dir, io->search_next.in.id.server_cookie,
+ &search->current_index);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ search->last_used = time(NULL);
+
+ status = pvfs_search_fill(pvfs, req, max_count, search, io->generic.data_level,
+ &reply_count, search_private, callback);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ io->search_next.out.count = reply_count;
+
+ /* not matching any entries means end of search */
+ if (reply_count == 0) {
+ talloc_free(search);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ list files in a directory matching a wildcard pattern
+*/
+static NTSTATUS pvfs_search_first_trans2(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_search_first *io,
+ void *search_private,
+ bool (*callback)(void *, const union smb_search_data *))
+{
+ struct pvfs_dir *dir;
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ struct pvfs_search_state *search;
+ uint_t reply_count;
+ uint16_t search_attrib, max_count;
+ const char *pattern;
+ NTSTATUS status;
+ struct pvfs_filename *name;
+ int id;
+
+ search_attrib = io->t2ffirst.in.search_attrib;
+ pattern = io->t2ffirst.in.pattern;
+ max_count = io->t2ffirst.in.max_count;
+
+ /* resolve the cifs name to a posix name */
+ status = pvfs_resolve_name(pvfs, req, pattern, PVFS_RESOLVE_WILDCARD, &name);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (!name->has_wildcard && !name->exists) {
+ return NT_STATUS_NO_SUCH_FILE;
+ }
+
+ status = pvfs_access_check_parent(pvfs, req, name, SEC_DIR_TRAVERSE | SEC_DIR_LIST);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* we initially make search a child of the request, then if we
+ need to keep it long term we steal it for the private
+ structure */
+ search = talloc(req, struct pvfs_search_state);
+ if (!search) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* do the actual directory listing */
+ status = pvfs_list_start(pvfs, name, search, &dir);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ id = idr_get_new(pvfs->search.idtree, search, MAX_SEARCH_HANDLES);
+ if (id == -1) {
+ return NT_STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ search->pvfs = pvfs;
+ search->handle = id;
+ search->dir = dir;
+ search->current_index = 0;
+ search->search_attrib = search_attrib;
+ search->must_attrib = 0;
+ search->last_used = 0;
+ search->num_ea_names = io->t2ffirst.in.num_names;
+ search->ea_names = io->t2ffirst.in.ea_names;
+ search->te = NULL;
+
+ DLIST_ADD(pvfs->search.list, search);
+ talloc_set_destructor(search, pvfs_search_destructor);
+
+ status = pvfs_search_fill(pvfs, req, max_count, search, io->generic.data_level,
+ &reply_count, search_private, callback);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* not matching any entries is an error */
+ if (reply_count == 0) {
+ return NT_STATUS_NO_SUCH_FILE;
+ }
+
+ io->t2ffirst.out.count = reply_count;
+ io->t2ffirst.out.handle = search->handle;
+ io->t2ffirst.out.end_of_search = pvfs_list_eos(dir, search->current_index) ? 1 : 0;
+
+ /* work out if we are going to keep the search state
+ and allow for a search continue */
+ if ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
+ ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) &&
+ io->t2ffirst.out.end_of_search)) {
+ talloc_free(search);
+ } else {
+ talloc_steal(pvfs, search);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/* continue a search */
+static NTSTATUS pvfs_search_next_trans2(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_search_next *io,
+ void *search_private,
+ bool (*callback)(void *, const union smb_search_data *))
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ void *p;
+ struct pvfs_search_state *search;
+ struct pvfs_dir *dir;
+ uint_t reply_count;
+ uint16_t handle;
+ NTSTATUS status;
+
+ handle = io->t2fnext.in.handle;
+
+ p = idr_find(pvfs->search.idtree, handle);
+ if (p == NULL) {
+ /* we didn't find the search handle */
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ search = talloc_get_type(p, struct pvfs_search_state);
+
+ dir = search->dir;
+
+ status = NT_STATUS_OK;
+
+ /* work out what type of continuation is being used */
+ if (io->t2fnext.in.last_name && *io->t2fnext.in.last_name) {
+ status = pvfs_list_seek(dir, io->t2fnext.in.last_name, &search->current_index);
+ if (!NT_STATUS_IS_OK(status) && io->t2fnext.in.resume_key) {
+ status = pvfs_list_seek_ofs(dir, io->t2fnext.in.resume_key,
+ &search->current_index);
+ }
+ } else if (!(io->t2fnext.in.flags & FLAG_TRANS2_FIND_CONTINUE)) {
+ status = pvfs_list_seek_ofs(dir, io->t2fnext.in.resume_key,
+ &search->current_index);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ search->num_ea_names = io->t2fnext.in.num_names;
+ search->ea_names = io->t2fnext.in.ea_names;
+
+ status = pvfs_search_fill(pvfs, req, io->t2fnext.in.max_count, search, io->generic.data_level,
+ &reply_count, search_private, callback);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ io->t2fnext.out.count = reply_count;
+ io->t2fnext.out.end_of_search = pvfs_list_eos(dir, search->current_index) ? 1 : 0;
+
+ /* work out if we are going to keep the search state */
+ if ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
+ ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) &&
+ io->t2fnext.out.end_of_search)) {
+ talloc_free(search);
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS pvfs_search_first_smb2(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, const struct smb2_find *io,
+ void *search_private,
+ bool (*callback)(void *, const union smb_search_data *))
+{
+ struct pvfs_dir *dir;
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ struct pvfs_search_state *search;
+ uint_t reply_count;
+ uint16_t max_count;
+ const char *pattern;
+ NTSTATUS status;
+ struct pvfs_filename *name;
+ struct pvfs_file *f;
+
+ f = pvfs_find_fd(pvfs, req, io->in.file.ntvfs);
+ if (!f) {
+ return NT_STATUS_FILE_CLOSED;
+ }
+
+ /* its only valid for directories */
+ if (f->handle->fd != -1) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!(f->access_mask & SEC_DIR_LIST)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (f->search) {
+ talloc_free(f->search);
+ f->search = NULL;
+ }
+
+ if (strequal(io->in.pattern, "")) {
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ }
+ if (strchr_m(io->in.pattern, '\\')) {
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ }
+ if (strchr_m(io->in.pattern, '/')) {
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ }
+
+ if (strequal("", f->handle->name->original_name)) {
+ pattern = talloc_asprintf(req, "\\%s", io->in.pattern);
+ NT_STATUS_HAVE_NO_MEMORY(pattern);
+ } else {
+ pattern = talloc_asprintf(req, "\\%s\\%s",
+ f->handle->name->original_name,
+ io->in.pattern);
+ NT_STATUS_HAVE_NO_MEMORY(pattern);
+ }
+
+ /* resolve the cifs name to a posix name */
+ status = pvfs_resolve_name(pvfs, req, pattern, PVFS_RESOLVE_WILDCARD, &name);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ if (!name->has_wildcard && !name->exists) {
+ return NT_STATUS_NO_SUCH_FILE;
+ }
+
+ /* we initially make search a child of the request, then if we
+ need to keep it long term we steal it for the private
+ structure */
+ search = talloc(req, struct pvfs_search_state);
+ NT_STATUS_HAVE_NO_MEMORY(search);
+
+ /* do the actual directory listing */
+ status = pvfs_list_start(pvfs, name, search, &dir);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ search->pvfs = pvfs;
+ search->handle = INVALID_SEARCH_HANDLE;
+ search->dir = dir;
+ search->current_index = 0;
+ search->search_attrib = 0x0000FFFF;
+ search->must_attrib = 0;
+ search->last_used = 0;
+ search->num_ea_names = 0;
+ search->ea_names = NULL;
+ search->te = NULL;
+
+ if (io->in.continue_flags & SMB2_CONTINUE_FLAG_SINGLE) {
+ max_count = 1;
+ } else {
+ max_count = UINT16_MAX;
+ }
+
+ status = pvfs_search_fill(pvfs, req, max_count, search, io->data_level,
+ &reply_count, search_private, callback);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ /* not matching any entries is an error */
+ if (reply_count == 0) {
+ return NT_STATUS_NO_SUCH_FILE;
+ }
+
+ f->search = talloc_steal(f, search);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS pvfs_search_next_smb2(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, const struct smb2_find *io,
+ void *search_private,
+ bool (*callback)(void *, const union smb_search_data *))
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ struct pvfs_search_state *search;
+ uint_t reply_count;
+ uint16_t max_count;
+ NTSTATUS status;
+ struct pvfs_file *f;
+
+ f = pvfs_find_fd(pvfs, req, io->in.file.ntvfs);
+ if (!f) {
+ return NT_STATUS_FILE_CLOSED;
+ }
+
+ /* its only valid for directories */
+ if (f->handle->fd != -1) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* if there's no search started on the dir handle, it's like a search_first */
+ search = f->search;
+ if (!search) {
+ return pvfs_search_first_smb2(ntvfs, req, io, search_private, callback);
+ }
+
+ if (io->in.continue_flags & SMB2_CONTINUE_FLAG_RESTART) {
+ search->current_index = 0;
+ }
+
+ if (io->in.continue_flags & SMB2_CONTINUE_FLAG_SINGLE) {
+ max_count = 1;
+ } else {
+ max_count = UINT16_MAX;
+ }
+
+ status = pvfs_search_fill(pvfs, req, max_count, search, io->data_level,
+ &reply_count, search_private, callback);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ /* not matching any entries is an error */
+ if (reply_count == 0) {
+ return STATUS_NO_MORE_FILES;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ list files in a directory matching a wildcard pattern
+*/
+NTSTATUS pvfs_search_first(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_search_first *io,
+ void *search_private,
+ bool (*callback)(void *, const union smb_search_data *))
+{
+ switch (io->generic.level) {
+ case RAW_SEARCH_SEARCH:
+ case RAW_SEARCH_FFIRST:
+ case RAW_SEARCH_FUNIQUE:
+ return pvfs_search_first_old(ntvfs, req, io, search_private, callback);
+
+ case RAW_SEARCH_TRANS2:
+ return pvfs_search_first_trans2(ntvfs, req, io, search_private, callback);
+
+ case RAW_SEARCH_SMB2:
+ return pvfs_search_first_smb2(ntvfs, req, &io->smb2, search_private, callback);
+ }
+
+ return NT_STATUS_INVALID_LEVEL;
+}
+
+/* continue a search */
+NTSTATUS pvfs_search_next(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_search_next *io,
+ void *search_private,
+ bool (*callback)(void *, const union smb_search_data *))
+{
+ switch (io->generic.level) {
+ case RAW_SEARCH_SEARCH:
+ case RAW_SEARCH_FFIRST:
+ return pvfs_search_next_old(ntvfs, req, io, search_private, callback);
+
+ case RAW_SEARCH_FUNIQUE:
+ return NT_STATUS_INVALID_LEVEL;
+
+ case RAW_SEARCH_TRANS2:
+ return pvfs_search_next_trans2(ntvfs, req, io, search_private, callback);
+
+ case RAW_SEARCH_SMB2:
+ return pvfs_search_next_smb2(ntvfs, req, &io->smb2, search_private, callback);
+ }
+
+ return NT_STATUS_INVALID_LEVEL;
+}
+
+
+/* close a search */
+NTSTATUS pvfs_search_close(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_search_close *io)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ void *p;
+ struct pvfs_search_state *search;
+ uint16_t handle = INVALID_SEARCH_HANDLE;
+
+ switch (io->generic.level) {
+ case RAW_FINDCLOSE_GENERIC:
+ return NT_STATUS_INVALID_LEVEL;
+
+ case RAW_FINDCLOSE_FCLOSE:
+ handle = io->fclose.in.id.handle;
+ break;
+
+ case RAW_FINDCLOSE_FINDCLOSE:
+ handle = io->findclose.in.handle;
+ break;
+ }
+
+ p = idr_find(pvfs->search.idtree, handle);
+ if (p == NULL) {
+ /* we didn't find the search handle */
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ search = talloc_get_type(p, struct pvfs_search_state);
+
+ talloc_free(search);
+
+ return NT_STATUS_OK;
+}
+
diff --git a/source4/ntvfs/posix/pvfs_seek.c b/source4/ntvfs/posix/pvfs_seek.c
new file mode 100644
index 0000000000..2cd3410876
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_seek.c
@@ -0,0 +1,65 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - seek
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "vfs_posix.h"
+
+/*
+ seek in a file
+*/
+NTSTATUS pvfs_seek(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_seek *io)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ struct pvfs_file *f;
+ struct pvfs_file_handle *h;
+ NTSTATUS status;
+
+ f = pvfs_find_fd(pvfs, req, io->lseek.in.file.ntvfs);
+ if (!f) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+ h = f->handle;
+
+ status = NT_STATUS_OK;
+
+ switch (io->lseek.in.mode) {
+ case SEEK_MODE_START:
+ h->seek_offset = io->lseek.in.offset;
+ break;
+
+ case SEEK_MODE_CURRENT:
+ h->seek_offset += io->lseek.in.offset;
+ break;
+
+ case SEEK_MODE_END:
+ status = pvfs_resolve_name_fd(pvfs, h->fd, h->name, PVFS_RESOLVE_NO_OPENDB);
+ h->seek_offset = h->name->st.st_size + io->lseek.in.offset;
+ break;
+ }
+
+ io->lseek.out.offset = h->seek_offset;
+
+ return status;
+}
+
diff --git a/source4/ntvfs/posix/pvfs_setfileinfo.c b/source4/ntvfs/posix/pvfs_setfileinfo.c
new file mode 100644
index 0000000000..9fe02a8e17
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_setfileinfo.c
@@ -0,0 +1,876 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - setfileinfo
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "vfs_posix.h"
+#include "system/time.h"
+#include "librpc/gen_ndr/xattr.h"
+
+
+/*
+ determine what access bits are needed for a call
+*/
+static uint32_t pvfs_setfileinfo_access(union smb_setfileinfo *info)
+{
+ uint32_t needed;
+
+ switch (info->generic.level) {
+ case RAW_SFILEINFO_EA_SET:
+ needed = SEC_FILE_WRITE_EA;
+ break;
+
+ case RAW_SFILEINFO_DISPOSITION_INFO:
+ case RAW_SFILEINFO_DISPOSITION_INFORMATION:
+ needed = SEC_STD_DELETE;
+ break;
+
+ case RAW_SFILEINFO_END_OF_FILE_INFO:
+ needed = SEC_FILE_WRITE_DATA;
+ break;
+
+ case RAW_SFILEINFO_POSITION_INFORMATION:
+ needed = 0;
+ break;
+
+ case RAW_SFILEINFO_SEC_DESC:
+ needed = 0;
+ if (info->set_secdesc.in.secinfo_flags & (SECINFO_OWNER|SECINFO_GROUP)) {
+ needed |= SEC_STD_WRITE_OWNER;
+ }
+ if (info->set_secdesc.in.secinfo_flags & SECINFO_DACL) {
+ needed |= SEC_STD_WRITE_DAC;
+ }
+ if (info->set_secdesc.in.secinfo_flags & SECINFO_SACL) {
+ needed |= SEC_FLAG_SYSTEM_SECURITY;
+ }
+ break;
+
+ case RAW_SFILEINFO_RENAME_INFORMATION:
+ case RAW_SFILEINFO_RENAME_INFORMATION_SMB2:
+ needed = SEC_STD_DELETE;
+ break;
+
+ default:
+ needed = SEC_FILE_WRITE_ATTRIBUTE;
+ break;
+ }
+
+ return needed;
+}
+
+/*
+ rename_information level for streams
+*/
+static NTSTATUS pvfs_setfileinfo_rename_stream(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
+ struct pvfs_filename *name,
+ int fd,
+ DATA_BLOB *odb_locking_key,
+ union smb_setfileinfo *info)
+{
+ NTSTATUS status;
+ struct odb_lock *lck = NULL;
+
+ if (info->rename_information.in.new_name[0] != ':') {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = pvfs_access_check_simple(pvfs, req, name, SEC_FILE_WRITE_ATTRIBUTE);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ lck = odb_lock(req, pvfs->odb_context, odb_locking_key);
+ if (lck == NULL) {
+ DEBUG(0,("Unable to lock opendb for can_stat\n"));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+
+ status = pvfs_stream_rename(pvfs, name, fd,
+ info->rename_information.in.new_name+1);
+ return status;
+}
+
+/*
+ rename_information level
+*/
+static NTSTATUS pvfs_setfileinfo_rename(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
+ struct pvfs_filename *name,
+ int fd,
+ DATA_BLOB *odb_locking_key,
+ union smb_setfileinfo *info)
+{
+ NTSTATUS status;
+ struct pvfs_filename *name2;
+ char *new_name, *p;
+ struct odb_lock *lck = NULL;
+
+ /* renames are only allowed within a directory */
+ if (strchr_m(info->rename_information.in.new_name, '\\') &&
+ (req->ctx->protocol != PROTOCOL_SMB2)) {
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ /* handle stream renames specially */
+ if (name->stream_name) {
+ return pvfs_setfileinfo_rename_stream(pvfs, req, name, fd,
+ odb_locking_key, info);
+ }
+
+ /* w2k3 does not appear to allow relative rename. On SMB2, vista sends it sometimes,
+ but I suspect it is just uninitialised memory */
+ if (info->rename_information.in.root_fid != 0 &&
+ (req->ctx->protocol != PROTOCOL_SMB2)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* construct the fully qualified windows name for the new file name */
+ if (req->ctx->protocol == PROTOCOL_SMB2) {
+ /* SMB2 sends the full path of the new name */
+ new_name = talloc_asprintf(req, "\\%s", info->rename_information.in.new_name);
+ } else {
+ new_name = talloc_strdup(req, name->original_name);
+ if (new_name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ p = strrchr_m(new_name, '\\');
+ if (p == NULL) {
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ } else {
+ *p = 0;
+ }
+
+ new_name = talloc_asprintf(req, "%s\\%s", new_name,
+ info->rename_information.in.new_name);
+ }
+ if (new_name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* resolve the new name */
+ status = pvfs_resolve_name(pvfs, name, new_name, 0, &name2);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* if the destination exists, then check the rename is allowed */
+ if (name2->exists) {
+ if (strcmp(name2->full_name, name->full_name) == 0) {
+ /* rename to same name is null-op */
+ return NT_STATUS_OK;
+ }
+
+ if (!info->rename_information.in.overwrite) {
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ status = pvfs_can_delete(pvfs, req, name2, NULL);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_DELETE_PENDING)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ lck = odb_lock(req, pvfs->odb_context, odb_locking_key);
+ if (lck == NULL) {
+ DEBUG(0,("Unable to lock opendb for can_stat\n"));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ status = pvfs_do_rename(pvfs, lck, name, name2->full_name);
+ talloc_free(lck);
+ NT_STATUS_NOT_OK_RETURN(status);
+ if (NT_STATUS_IS_OK(status)) {
+ name->full_name = talloc_steal(name, name2->full_name);
+ name->original_name = talloc_steal(name, name2->original_name);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ add a single DOS EA
+*/
+NTSTATUS pvfs_setfileinfo_ea_set(struct pvfs_state *pvfs,
+ struct pvfs_filename *name,
+ int fd, uint16_t num_eas,
+ struct ea_struct *eas)
+{
+ struct xattr_DosEAs *ealist;
+ int i, j;
+ NTSTATUS status;
+
+ if (num_eas == 0) {
+ return NT_STATUS_OK;
+ }
+
+ if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ ealist = talloc(name, struct xattr_DosEAs);
+
+ /* load the current list */
+ status = pvfs_doseas_load(pvfs, name, fd, ealist);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ for (j=0;j<num_eas;j++) {
+ struct ea_struct *ea = &eas[j];
+ /* see if its already there */
+ for (i=0;i<ealist->num_eas;i++) {
+ if (strcasecmp_m(ealist->eas[i].name, ea->name.s) == 0) {
+ ealist->eas[i].value = ea->value;
+ break;
+ }
+ }
+
+ if (i==ealist->num_eas) {
+ /* add it */
+ ealist->eas = talloc_realloc(ealist, ealist->eas,
+ struct xattr_EA,
+ ealist->num_eas+1);
+ if (ealist->eas == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ ealist->eas[i].name = ea->name.s;
+ ealist->eas[i].value = ea->value;
+ ealist->num_eas++;
+ }
+ }
+
+ /* pull out any null EAs */
+ for (i=0;i<ealist->num_eas;i++) {
+ if (ealist->eas[i].value.length == 0) {
+ memmove(&ealist->eas[i],
+ &ealist->eas[i+1],
+ (ealist->num_eas-(i+1)) * sizeof(ealist->eas[i]));
+ ealist->num_eas--;
+ i--;
+ }
+ }
+
+ status = pvfs_doseas_save(pvfs, name, fd, ealist);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ notify_trigger(pvfs->notify_context,
+ NOTIFY_ACTION_MODIFIED,
+ FILE_NOTIFY_CHANGE_EA,
+ name->full_name);
+
+ name->dos.ea_size = 4;
+ for (i=0;i<ealist->num_eas;i++) {
+ name->dos.ea_size += 4 + strlen(ealist->eas[i].name)+1 +
+ ealist->eas[i].value.length;
+ }
+
+ /* update the ea_size attrib */
+ return pvfs_dosattrib_save(pvfs, name, fd);
+}
+
+/*
+ set info on a open file
+*/
+NTSTATUS pvfs_setfileinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_setfileinfo *info)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ struct pvfs_file *f;
+ struct pvfs_file_handle *h;
+ struct pvfs_filename newstats;
+ NTSTATUS status;
+ uint32_t access_needed;
+ uint32_t change_mask = 0;
+
+ f = pvfs_find_fd(pvfs, req, info->generic.in.file.ntvfs);
+ if (!f) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ h = f->handle;
+
+ access_needed = pvfs_setfileinfo_access(info);
+ if ((f->access_mask & access_needed) != access_needed) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* update the file information */
+ status = pvfs_resolve_name_handle(pvfs, h);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* we take a copy of the current file stats, then update
+ newstats in each of the elements below. At the end we
+ compare, and make any changes needed */
+ newstats = *h->name;
+
+ switch (info->generic.level) {
+ case RAW_SFILEINFO_SETATTR:
+ if (!null_time(info->setattr.in.write_time)) {
+ unix_to_nt_time(&newstats.dos.write_time, info->setattr.in.write_time);
+ }
+ if (info->setattr.in.attrib != FILE_ATTRIBUTE_NORMAL) {
+ newstats.dos.attrib = info->setattr.in.attrib;
+ }
+ break;
+
+ case RAW_SFILEINFO_SETATTRE:
+ case RAW_SFILEINFO_STANDARD:
+ if (!null_time(info->setattre.in.create_time)) {
+ unix_to_nt_time(&newstats.dos.create_time, info->setattre.in.create_time);
+ }
+ if (!null_time(info->setattre.in.access_time)) {
+ unix_to_nt_time(&newstats.dos.access_time, info->setattre.in.access_time);
+ }
+ if (!null_time(info->setattre.in.write_time)) {
+ unix_to_nt_time(&newstats.dos.write_time, info->setattre.in.write_time);
+ }
+ break;
+
+ case RAW_SFILEINFO_EA_SET:
+ return pvfs_setfileinfo_ea_set(pvfs, h->name, h->fd,
+ info->ea_set.in.num_eas,
+ info->ea_set.in.eas);
+
+ case RAW_SFILEINFO_BASIC_INFO:
+ case RAW_SFILEINFO_BASIC_INFORMATION:
+ if (!null_nttime(info->basic_info.in.create_time)) {
+ newstats.dos.create_time = info->basic_info.in.create_time;
+ }
+ if (!null_nttime(info->basic_info.in.access_time)) {
+ newstats.dos.access_time = info->basic_info.in.access_time;
+ }
+ if (!null_nttime(info->basic_info.in.write_time)) {
+ newstats.dos.write_time = info->basic_info.in.write_time;
+ }
+ if (!null_nttime(info->basic_info.in.change_time)) {
+ newstats.dos.change_time = info->basic_info.in.change_time;
+ }
+ if (info->basic_info.in.attrib != 0) {
+ newstats.dos.attrib = info->basic_info.in.attrib;
+ }
+ break;
+
+ case RAW_SFILEINFO_DISPOSITION_INFO:
+ case RAW_SFILEINFO_DISPOSITION_INFORMATION:
+ return pvfs_set_delete_on_close(pvfs, req, f,
+ info->disposition_info.in.delete_on_close);
+
+ case RAW_SFILEINFO_ALLOCATION_INFO:
+ case RAW_SFILEINFO_ALLOCATION_INFORMATION:
+ status = pvfs_break_level2_oplocks(f);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ newstats.dos.alloc_size = info->allocation_info.in.alloc_size;
+ if (newstats.dos.alloc_size < newstats.st.st_size) {
+ newstats.st.st_size = newstats.dos.alloc_size;
+ }
+ newstats.dos.alloc_size = pvfs_round_alloc_size(pvfs,
+ newstats.dos.alloc_size);
+ break;
+
+ case RAW_SFILEINFO_END_OF_FILE_INFO:
+ case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
+ status = pvfs_break_level2_oplocks(f);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ newstats.st.st_size = info->end_of_file_info.in.size;
+ break;
+
+ case RAW_SFILEINFO_POSITION_INFORMATION:
+ h->position = info->position_information.in.position;
+ break;
+
+ case RAW_SFILEINFO_MODE_INFORMATION:
+ /* this one is a puzzle */
+ if (info->mode_information.in.mode != 0 &&
+ info->mode_information.in.mode != 2 &&
+ info->mode_information.in.mode != 4 &&
+ info->mode_information.in.mode != 6) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ h->mode = info->mode_information.in.mode;
+ break;
+
+ case RAW_SFILEINFO_RENAME_INFORMATION:
+ case RAW_SFILEINFO_RENAME_INFORMATION_SMB2:
+ return pvfs_setfileinfo_rename(pvfs, req, h->name, f->handle->fd,
+ &h->odb_locking_key,
+ info);
+
+ case RAW_SFILEINFO_SEC_DESC:
+ notify_trigger(pvfs->notify_context,
+ NOTIFY_ACTION_MODIFIED,
+ FILE_NOTIFY_CHANGE_SECURITY,
+ h->name->full_name);
+ return pvfs_acl_set(pvfs, req, h->name, h->fd, f->access_mask, info);
+
+ default:
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ /* possibly change the file size */
+ if (newstats.st.st_size != h->name->st.st_size) {
+ if (h->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) {
+ return NT_STATUS_FILE_IS_A_DIRECTORY;
+ }
+ if (h->name->stream_name) {
+ status = pvfs_stream_truncate(pvfs, h->name, h->fd, newstats.st.st_size);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ change_mask |= FILE_NOTIFY_CHANGE_STREAM_SIZE;
+ } else {
+ int ret;
+ if (f->access_mask &
+ (SEC_FILE_WRITE_DATA|SEC_FILE_APPEND_DATA)) {
+ ret = ftruncate(h->fd, newstats.st.st_size);
+ } else {
+ ret = truncate(h->name->full_name, newstats.st.st_size);
+ }
+ if (ret == -1) {
+ return pvfs_map_errno(pvfs, errno);
+ }
+ change_mask |= FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_ATTRIBUTES;
+ }
+ }
+
+ /* possibly change the file timestamps */
+ if (newstats.dos.create_time != h->name->dos.create_time) {
+ change_mask |= FILE_NOTIFY_CHANGE_CREATION;
+ }
+ if (newstats.dos.access_time != h->name->dos.access_time) {
+ change_mask |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
+ }
+ if (newstats.dos.write_time != h->name->dos.write_time) {
+ change_mask |= FILE_NOTIFY_CHANGE_LAST_WRITE;
+ }
+ if ((change_mask & FILE_NOTIFY_CHANGE_LAST_ACCESS) ||
+ (change_mask & FILE_NOTIFY_CHANGE_LAST_WRITE)) {
+ struct timeval tv[2];
+
+ nttime_to_timeval(&tv[0], newstats.dos.access_time);
+ nttime_to_timeval(&tv[1], newstats.dos.write_time);
+
+ if (!timeval_is_zero(&tv[0]) || !timeval_is_zero(&tv[1])) {
+ if (utimes(h->name->full_name, tv) == -1) {
+ DEBUG(0,("pvfs_setfileinfo: utimes() failed '%s' - %s\n",
+ h->name->full_name, strerror(errno)));
+ return pvfs_map_errno(pvfs, errno);
+ }
+ }
+ }
+ if (change_mask & FILE_NOTIFY_CHANGE_LAST_WRITE) {
+ struct odb_lock *lck;
+
+ lck = odb_lock(req, h->pvfs->odb_context, &h->odb_locking_key);
+ if (lck == NULL) {
+ DEBUG(0,("Unable to lock opendb for write time update\n"));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ status = odb_set_write_time(lck, newstats.dos.write_time, true);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Unable to update write time: %s\n",
+ nt_errstr(status)));
+ talloc_free(lck);
+ return status;
+ }
+
+ talloc_free(lck);
+
+ h->write_time.update_forced = true;
+ h->write_time.update_on_close = false;
+ talloc_free(h->write_time.update_event);
+ h->write_time.update_event = NULL;
+ }
+
+ /* possibly change the attribute */
+ if (newstats.dos.attrib != h->name->dos.attrib) {
+ mode_t mode;
+ if ((newstats.dos.attrib & FILE_ATTRIBUTE_DIRECTORY) &&
+ !(h->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ mode = pvfs_fileperms(pvfs, newstats.dos.attrib);
+ if (!(h->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
+ if (fchmod(h->fd, mode) == -1) {
+ return pvfs_map_errno(pvfs, errno);
+ }
+ }
+ change_mask |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
+ }
+
+ *h->name = newstats;
+
+ notify_trigger(pvfs->notify_context,
+ NOTIFY_ACTION_MODIFIED,
+ change_mask,
+ h->name->full_name);
+
+ return pvfs_dosattrib_save(pvfs, h->name, h->fd);
+}
+
+/*
+ retry an open after a sharing violation
+*/
+static void pvfs_retry_setpathinfo(struct pvfs_odb_retry *r,
+ struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ void *_info,
+ void *private_data,
+ enum pvfs_wait_notice reason)
+{
+ union smb_setfileinfo *info = talloc_get_type(_info,
+ union smb_setfileinfo);
+ NTSTATUS status = NT_STATUS_INTERNAL_ERROR;
+
+ talloc_free(r);
+
+ switch (reason) {
+ case PVFS_WAIT_CANCEL:
+/*TODO*/
+ status = NT_STATUS_CANCELLED;
+ break;
+ case PVFS_WAIT_TIMEOUT:
+ /* if it timed out, then give the failure
+ immediately */
+/*TODO*/
+ status = NT_STATUS_SHARING_VIOLATION;
+ break;
+ case PVFS_WAIT_EVENT:
+
+ /* try the open again, which could trigger another retry setup
+ if it wants to, so we have to unmark the async flag so we
+ will know if it does a second async reply */
+ req->async_states->state &= ~NTVFS_ASYNC_STATE_ASYNC;
+
+ status = pvfs_setpathinfo(ntvfs, req, info);
+ if (req->async_states->state & NTVFS_ASYNC_STATE_ASYNC) {
+ /* the 2nd try also replied async, so we don't send
+ the reply yet */
+ return;
+ }
+
+ /* re-mark it async, just in case someone up the chain does
+ paranoid checking */
+ req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
+ break;
+ }
+
+ /* send the reply up the chain */
+ req->async_states->status = status;
+ req->async_states->send_fn(req);
+}
+
+/*
+ setup for a unlink retry after a sharing violation
+ or a non granted oplock
+*/
+static NTSTATUS pvfs_setpathinfo_setup_retry(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_setfileinfo *info,
+ struct odb_lock *lck,
+ NTSTATUS status)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ struct timeval end_time;
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) {
+ end_time = timeval_add(&req->statistics.request_time,
+ 0, pvfs->sharing_violation_delay);
+ } else if (NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) {
+ end_time = timeval_add(&req->statistics.request_time,
+ pvfs->oplock_break_timeout, 0);
+ } else {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ return pvfs_odb_retry_setup(ntvfs, req, lck, end_time, info, NULL,
+ pvfs_retry_setpathinfo);
+}
+
+/*
+ set info on a pathname
+*/
+NTSTATUS pvfs_setpathinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_setfileinfo *info)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ struct pvfs_filename *name;
+ struct pvfs_filename newstats;
+ NTSTATUS status;
+ uint32_t access_needed;
+ uint32_t change_mask = 0;
+ struct odb_lock *lck = NULL;
+ DATA_BLOB odb_locking_key;
+
+ /* resolve the cifs name to a posix name */
+ status = pvfs_resolve_name(pvfs, req, info->generic.in.file.path,
+ PVFS_RESOLVE_STREAMS, &name);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (!name->exists) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ access_needed = pvfs_setfileinfo_access(info);
+ status = pvfs_access_check_simple(pvfs, req, name, access_needed);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* we take a copy of the current file stats, then update
+ newstats in each of the elements below. At the end we
+ compare, and make any changes needed */
+ newstats = *name;
+
+ switch (info->generic.level) {
+ case RAW_SFILEINFO_SETATTR:
+ if (!null_time(info->setattr.in.write_time)) {
+ unix_to_nt_time(&newstats.dos.write_time, info->setattr.in.write_time);
+ }
+ if (info->setattr.in.attrib == 0) {
+ newstats.dos.attrib = FILE_ATTRIBUTE_NORMAL;
+ } else if (info->setattr.in.attrib != FILE_ATTRIBUTE_NORMAL) {
+ newstats.dos.attrib = info->setattr.in.attrib;
+ }
+ break;
+
+ case RAW_SFILEINFO_SETATTRE:
+ case RAW_SFILEINFO_STANDARD:
+ if (!null_time(info->setattre.in.create_time)) {
+ unix_to_nt_time(&newstats.dos.create_time, info->setattre.in.create_time);
+ }
+ if (!null_time(info->setattre.in.access_time)) {
+ unix_to_nt_time(&newstats.dos.access_time, info->setattre.in.access_time);
+ }
+ if (!null_time(info->setattre.in.write_time)) {
+ unix_to_nt_time(&newstats.dos.write_time, info->setattre.in.write_time);
+ }
+ break;
+
+ case RAW_SFILEINFO_EA_SET:
+ return pvfs_setfileinfo_ea_set(pvfs, name, -1,
+ info->ea_set.in.num_eas,
+ info->ea_set.in.eas);
+
+ case RAW_SFILEINFO_BASIC_INFO:
+ case RAW_SFILEINFO_BASIC_INFORMATION:
+ if (!null_nttime(info->basic_info.in.create_time)) {
+ newstats.dos.create_time = info->basic_info.in.create_time;
+ }
+ if (!null_nttime(info->basic_info.in.access_time)) {
+ newstats.dos.access_time = info->basic_info.in.access_time;
+ }
+ if (!null_nttime(info->basic_info.in.write_time)) {
+ newstats.dos.write_time = info->basic_info.in.write_time;
+ }
+ if (!null_nttime(info->basic_info.in.change_time)) {
+ newstats.dos.change_time = info->basic_info.in.change_time;
+ }
+ if (info->basic_info.in.attrib != 0) {
+ newstats.dos.attrib = info->basic_info.in.attrib;
+ }
+ break;
+
+ case RAW_SFILEINFO_ALLOCATION_INFO:
+ case RAW_SFILEINFO_ALLOCATION_INFORMATION:
+ status = pvfs_can_update_file_size(pvfs, req, name, &lck);
+ /*
+ * on a sharing violation we need to retry when the file is closed by
+ * the other user, or after 1 second
+ * on a non granted oplock we need to retry when the file is closed by
+ * the other user, or after 30 seconds
+ */
+ if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) &&
+ (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ return pvfs_setpathinfo_setup_retry(pvfs->ntvfs, req, info, lck, status);
+ }
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ if (info->allocation_info.in.alloc_size > newstats.dos.alloc_size) {
+ /* strange. Increasing the allocation size via setpathinfo
+ should be silently ignored */
+ break;
+ }
+ newstats.dos.alloc_size = info->allocation_info.in.alloc_size;
+ if (newstats.dos.alloc_size < newstats.st.st_size) {
+ newstats.st.st_size = newstats.dos.alloc_size;
+ }
+ newstats.dos.alloc_size = pvfs_round_alloc_size(pvfs,
+ newstats.dos.alloc_size);
+ break;
+
+ case RAW_SFILEINFO_END_OF_FILE_INFO:
+ case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
+ status = pvfs_can_update_file_size(pvfs, req, name, &lck);
+ /*
+ * on a sharing violation we need to retry when the file is closed by
+ * the other user, or after 1 second
+ * on a non granted oplock we need to retry when the file is closed by
+ * the other user, or after 30 seconds
+ */
+ if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) &&
+ (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ return pvfs_setpathinfo_setup_retry(pvfs->ntvfs, req, info, lck, status);
+ }
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ newstats.st.st_size = info->end_of_file_info.in.size;
+ break;
+
+ case RAW_SFILEINFO_MODE_INFORMATION:
+ if (info->mode_information.in.mode != 0 &&
+ info->mode_information.in.mode != 2 &&
+ info->mode_information.in.mode != 4 &&
+ info->mode_information.in.mode != 6) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ return NT_STATUS_OK;
+
+ case RAW_SFILEINFO_RENAME_INFORMATION:
+ case RAW_SFILEINFO_RENAME_INFORMATION_SMB2:
+ status = pvfs_locking_key(name, name, &odb_locking_key);
+ NT_STATUS_NOT_OK_RETURN(status);
+ status = pvfs_setfileinfo_rename(pvfs, req, name, -1,
+ &odb_locking_key, info);
+ NT_STATUS_NOT_OK_RETURN(status);
+ return NT_STATUS_OK;
+
+ case RAW_SFILEINFO_DISPOSITION_INFO:
+ case RAW_SFILEINFO_DISPOSITION_INFORMATION:
+ case RAW_SFILEINFO_POSITION_INFORMATION:
+ return NT_STATUS_OK;
+
+ default:
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ /* possibly change the file size */
+ if (newstats.st.st_size != name->st.st_size) {
+ if (name->stream_name) {
+ status = pvfs_stream_truncate(pvfs, name, -1, newstats.st.st_size);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ } else if (truncate(name->full_name, newstats.st.st_size) == -1) {
+ return pvfs_map_errno(pvfs, errno);
+ }
+ change_mask |= FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_ATTRIBUTES;
+ }
+
+ /* possibly change the file timestamps */
+ if (newstats.dos.create_time != name->dos.create_time) {
+ change_mask |= FILE_NOTIFY_CHANGE_CREATION;
+ }
+ if (newstats.dos.access_time != name->dos.access_time) {
+ change_mask |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
+ }
+ if (newstats.dos.write_time != name->dos.write_time) {
+ change_mask |= FILE_NOTIFY_CHANGE_LAST_WRITE;
+ }
+ if ((change_mask & FILE_NOTIFY_CHANGE_LAST_ACCESS) ||
+ (change_mask & FILE_NOTIFY_CHANGE_LAST_WRITE)) {
+ struct timeval tv[2];
+
+ nttime_to_timeval(&tv[0], newstats.dos.access_time);
+ nttime_to_timeval(&tv[1], newstats.dos.write_time);
+
+ if (!timeval_is_zero(&tv[0]) || !timeval_is_zero(&tv[1])) {
+ if (utimes(name->full_name, tv) == -1) {
+ DEBUG(0,("pvfs_setpathinfo: utimes() failed '%s' - %s\n",
+ name->full_name, strerror(errno)));
+ return pvfs_map_errno(pvfs, errno);
+ }
+ }
+ }
+ if (change_mask & FILE_NOTIFY_CHANGE_LAST_WRITE) {
+ if (lck == NULL) {
+ DATA_BLOB lkey;
+ status = pvfs_locking_key(name, name, &lkey);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ lck = odb_lock(req, pvfs->odb_context, &lkey);
+ data_blob_free(&lkey);
+ if (lck == NULL) {
+ DEBUG(0,("Unable to lock opendb for write time update\n"));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ }
+
+ status = odb_set_write_time(lck, newstats.dos.write_time, true);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ /* it could be that nobody has opened the file */
+ } else if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Unable to update write time: %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+ }
+
+ /* possibly change the attribute */
+ newstats.dos.attrib |= (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY);
+ if (newstats.dos.attrib != name->dos.attrib) {
+ mode_t mode = pvfs_fileperms(pvfs, newstats.dos.attrib);
+ if (chmod(name->full_name, mode) == -1) {
+ return pvfs_map_errno(pvfs, errno);
+ }
+ change_mask |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
+ }
+
+ *name = newstats;
+
+ if (change_mask != 0) {
+ notify_trigger(pvfs->notify_context,
+ NOTIFY_ACTION_MODIFIED,
+ change_mask,
+ name->full_name);
+ }
+
+ return pvfs_dosattrib_save(pvfs, name, -1);
+}
+
diff --git a/source4/ntvfs/posix/pvfs_shortname.c b/source4/ntvfs/posix/pvfs_shortname.c
new file mode 100644
index 0000000000..530def9163
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_shortname.c
@@ -0,0 +1,699 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - 8.3 name routines
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/locale.h"
+#include "vfs_posix.h"
+#include "param/param.h"
+
+/*
+ this mangling scheme uses the following format
+
+ Annnn~n.AAA
+
+ where nnnnn is a base 36 hash, and A represents characters from the original string
+
+ The hash is taken of the leading part of the long filename, in uppercase
+
+ for simplicity, we only allow ascii characters in 8.3 names
+*/
+
+/*
+ ===============================================================================
+ NOTE NOTE NOTE!!!
+
+ This file deliberately uses non-multibyte string functions in many places. This
+ is *not* a mistake. This code is multi-byte safe, but it gets this property
+ through some very subtle knowledge of the way multi-byte strings are encoded
+ and the fact that this mangling algorithm only supports ascii characters in
+ 8.3 names.
+
+ please don't convert this file to use the *_m() functions!!
+ ===============================================================================
+*/
+
+
+#if 1
+#define M_DEBUG(level, x) DEBUG(level, x)
+#else
+#define M_DEBUG(level, x)
+#endif
+
+/* these flags are used to mark characters in as having particular
+ properties */
+#define FLAG_BASECHAR 1
+#define FLAG_ASCII 2
+#define FLAG_ILLEGAL 4
+#define FLAG_WILDCARD 8
+
+/* the "possible" flags are used as a fast way to find possible DOS
+ reserved filenames */
+#define FLAG_POSSIBLE1 16
+#define FLAG_POSSIBLE2 32
+#define FLAG_POSSIBLE3 64
+#define FLAG_POSSIBLE4 128
+
+#define DEFAULT_MANGLE_PREFIX 4
+
+#define MANGLE_BASECHARS "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+
+#define FLAG_CHECK(c, flag) (ctx->char_flags[(uint8_t)(c)] & (flag))
+
+static const char *reserved_names[] =
+{ "AUX", "CON", "COM1", "COM2", "COM3", "COM4",
+ "LPT1", "LPT2", "LPT3", "NUL", "PRN", NULL };
+
+
+struct pvfs_mangle_context {
+ uint8_t char_flags[256];
+ /*
+ this determines how many characters are used from the original
+ filename in the 8.3 mangled name. A larger value leads to a weaker
+ hash and more collisions. The largest possible value is 6.
+ */
+ int mangle_prefix;
+ uint32_t mangle_modulus;
+
+ /* we will use a very simple direct mapped prefix cache. The big
+ advantage of this cache structure is speed and low memory usage
+
+ The cache is indexed by the low-order bits of the hash, and confirmed by
+ hashing the resulting cache entry to match the known hash
+ */
+ uint32_t cache_size;
+ char **prefix_cache;
+ uint32_t *prefix_cache_hashes;
+
+ /* this is used to reverse the base 36 mapping */
+ unsigned char base_reverse[256];
+
+ struct smb_iconv_convenience *iconv_convenience;
+};
+
+
+/*
+ hash a string of the specified length. The string does not need to be
+ null terminated
+
+ this hash needs to be fast with a low collision rate (what hash doesn't?)
+*/
+static uint32_t mangle_hash(struct pvfs_mangle_context *ctx,
+ const char *key, size_t length)
+{
+ return pvfs_name_hash(key, length) % ctx->mangle_modulus;
+}
+
+/*
+ insert an entry into the prefix cache. The string might not be null
+ terminated */
+static void cache_insert(struct pvfs_mangle_context *ctx,
+ const char *prefix, int length, uint32_t hash)
+{
+ int i = hash % ctx->cache_size;
+
+ if (ctx->prefix_cache[i]) {
+ talloc_free(ctx->prefix_cache[i]);
+ }
+
+ ctx->prefix_cache[i] = talloc_strndup(ctx->prefix_cache, prefix, length);
+ ctx->prefix_cache_hashes[i] = hash;
+}
+
+/*
+ lookup an entry in the prefix cache. Return NULL if not found.
+*/
+static const char *cache_lookup(struct pvfs_mangle_context *ctx, uint32_t hash)
+{
+ int i = hash % ctx->cache_size;
+
+
+ if (!ctx->prefix_cache[i] || hash != ctx->prefix_cache_hashes[i]) {
+ return NULL;
+ }
+
+ /* yep, it matched */
+ return ctx->prefix_cache[i];
+}
+
+
+/*
+ determine if a string is possibly in a mangled format, ignoring
+ case
+
+ In this algorithm, mangled names use only pure ascii characters (no
+ multi-byte) so we can avoid doing a UCS2 conversion
+ */
+static bool is_mangled_component(struct pvfs_mangle_context *ctx,
+ const char *name, size_t len)
+{
+ unsigned int i;
+
+ M_DEBUG(10,("is_mangled_component %s (len %u) ?\n", name, (unsigned int)len));
+
+ /* check the length */
+ if (len > 12 || len < 8)
+ return false;
+
+ /* the best distinguishing characteristic is the ~ */
+ if (name[6] != '~')
+ return false;
+
+ /* check extension */
+ if (len > 8) {
+ if (name[8] != '.')
+ return false;
+ for (i=9; name[i] && i < len; i++) {
+ if (! FLAG_CHECK(name[i], FLAG_ASCII)) {
+ return false;
+ }
+ }
+ }
+
+ /* check lead characters */
+ for (i=0;i<ctx->mangle_prefix;i++) {
+ if (! FLAG_CHECK(name[i], FLAG_ASCII)) {
+ return false;
+ }
+ }
+
+ /* check rest of hash */
+ if (! FLAG_CHECK(name[7], FLAG_BASECHAR)) {
+ return false;
+ }
+ for (i=ctx->mangle_prefix;i<6;i++) {
+ if (! FLAG_CHECK(name[i], FLAG_BASECHAR)) {
+ return false;
+ }
+ }
+
+ M_DEBUG(10,("is_mangled_component %s (len %u) -> yes\n", name, (unsigned int)len));
+
+ return true;
+}
+
+
+
+/*
+ determine if a string is possibly in a mangled format, ignoring
+ case
+
+ In this algorithm, mangled names use only pure ascii characters (no
+ multi-byte) so we can avoid doing a UCS2 conversion
+
+ NOTE! This interface must be able to handle a path with unix
+ directory separators. It should return true if any component is
+ mangled
+ */
+static bool is_mangled(struct pvfs_mangle_context *ctx, const char *name)
+{
+ const char *p;
+ const char *s;
+
+ M_DEBUG(10,("is_mangled %s ?\n", name));
+
+ for (s=name; (p=strchr(s, '/')); s=p+1) {
+ if (is_mangled_component(ctx, s, PTR_DIFF(p, s))) {
+ return true;
+ }
+ }
+
+ /* and the last part ... */
+ return is_mangled_component(ctx, s, strlen(s));
+}
+
+
+/*
+ see if a filename is an allowable 8.3 name.
+
+ we are only going to allow ascii characters in 8.3 names, as this
+ simplifies things greatly (it means that we know the string won't
+ get larger when converted from UNIX to DOS formats)
+*/
+static bool is_8_3(struct pvfs_mangle_context *ctx,
+ const char *name, bool check_case, bool allow_wildcards)
+{
+ int len, i;
+ char *dot_p;
+
+ /* as a special case, the names '.' and '..' are allowable 8.3 names */
+ if (name[0] == '.') {
+ if (!name[1] || (name[1] == '.' && !name[2])) {
+ return true;
+ }
+ }
+
+ /* the simplest test is on the overall length of the
+ filename. Note that we deliberately use the ascii string
+ length (not the multi-byte one) as it is faster, and gives us
+ the result we need in this case. Using strlen_m would not
+ only be slower, it would be incorrect */
+ len = strlen(name);
+ if (len > 12)
+ return false;
+
+ /* find the '.'. Note that once again we use the non-multibyte
+ function */
+ dot_p = strchr(name, '.');
+
+ if (!dot_p) {
+ /* if the name doesn't contain a '.' then its length
+ must be less than 8 */
+ if (len > 8) {
+ return false;
+ }
+ } else {
+ int prefix_len, suffix_len;
+
+ /* if it does contain a dot then the prefix must be <=
+ 8 and the suffix <= 3 in length */
+ prefix_len = PTR_DIFF(dot_p, name);
+ suffix_len = len - (prefix_len+1);
+
+ if (prefix_len > 8 || suffix_len > 3 || suffix_len == 0) {
+ return false;
+ }
+
+ /* a 8.3 name cannot contain more than 1 '.' */
+ if (strchr(dot_p+1, '.')) {
+ return false;
+ }
+ }
+
+ /* the length are all OK. Now check to see if the characters themselves are OK */
+ for (i=0; name[i]; i++) {
+ /* note that we may allow wildcard petterns! */
+ if (!FLAG_CHECK(name[i], FLAG_ASCII|(allow_wildcards ? FLAG_WILDCARD : 0)) &&
+ name[i] != '.') {
+ return false;
+ }
+ }
+
+ /* it is a good 8.3 name */
+ return true;
+}
+
+
+/*
+ try to find a 8.3 name in the cache, and if found then
+ return the original long name.
+*/
+static char *check_cache(struct pvfs_mangle_context *ctx,
+ TALLOC_CTX *mem_ctx, const char *name)
+{
+ uint32_t hash, multiplier;
+ unsigned int i;
+ const char *prefix;
+ char extension[4];
+
+ /* make sure that this is a mangled name from this cache */
+ if (!is_mangled(ctx, name)) {
+ M_DEBUG(10,("check_cache: %s -> not mangled\n", name));
+ return NULL;
+ }
+
+ /* we need to extract the hash from the 8.3 name */
+ hash = ctx->base_reverse[(unsigned char)name[7]];
+ for (multiplier=36, i=5;i>=ctx->mangle_prefix;i--) {
+ uint32_t v = ctx->base_reverse[(unsigned char)name[i]];
+ hash += multiplier * v;
+ multiplier *= 36;
+ }
+
+ /* now look in the prefix cache for that hash */
+ prefix = cache_lookup(ctx, hash);
+ if (!prefix) {
+ M_DEBUG(10,("check_cache: %s -> %08X -> not found\n", name, hash));
+ return NULL;
+ }
+
+ /* we found it - construct the full name */
+ if (name[8] == '.') {
+ strncpy(extension, name+9, 3);
+ extension[3] = 0;
+ } else {
+ extension[0] = 0;
+ }
+
+ if (extension[0]) {
+ return talloc_asprintf(mem_ctx, "%s.%s", prefix, extension);
+ }
+
+ return talloc_strdup(mem_ctx, prefix);
+}
+
+
+/*
+ look for a DOS reserved name
+*/
+static bool is_reserved_name(struct pvfs_mangle_context *ctx, const char *name)
+{
+ if (FLAG_CHECK(name[0], FLAG_POSSIBLE1) &&
+ FLAG_CHECK(name[1], FLAG_POSSIBLE2) &&
+ FLAG_CHECK(name[2], FLAG_POSSIBLE3) &&
+ FLAG_CHECK(name[3], FLAG_POSSIBLE4)) {
+ /* a likely match, scan the lot */
+ int i;
+ for (i=0; reserved_names[i]; i++) {
+ if (strcasecmp(name, reserved_names[i]) == 0) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+
+/*
+ See if a filename is a legal long filename.
+ A filename ending in a '.' is not legal unless it's "." or "..". JRA.
+*/
+static bool is_legal_name(struct pvfs_mangle_context *ctx, const char *name)
+{
+ while (*name) {
+ size_t c_size;
+ codepoint_t c = next_codepoint_convenience(ctx->iconv_convenience, name, &c_size);
+ if (c == INVALID_CODEPOINT) {
+ return false;
+ }
+ /* all high chars are OK */
+ if (c >= 128) {
+ name += c_size;
+ continue;
+ }
+ if (FLAG_CHECK(c, FLAG_ILLEGAL)) {
+ return false;
+ }
+ name += c_size;
+ }
+
+ return true;
+}
+
+/*
+ the main forward mapping function, which converts a long filename to
+ a 8.3 name
+
+ if need83 is not set then we only do the mangling if the name is illegal
+ as a long name
+
+ if cache83 is not set then we don't cache the result
+
+ return NULL if we don't need to do any conversion
+*/
+static char *name_map(struct pvfs_mangle_context *ctx,
+ const char *name, bool need83, bool cache83)
+{
+ char *dot_p;
+ char lead_chars[7];
+ char extension[4];
+ unsigned int extension_length, i;
+ unsigned int prefix_len;
+ uint32_t hash, v;
+ char *new_name;
+ const char *basechars = MANGLE_BASECHARS;
+
+ /* reserved names are handled specially */
+ if (!is_reserved_name(ctx, name)) {
+ /* if the name is already a valid 8.3 name then we don't need to
+ do anything */
+ if (is_8_3(ctx, name, false, false)) {
+ return NULL;
+ }
+
+ /* if the caller doesn't strictly need 8.3 then just check for illegal
+ filenames */
+ if (!need83 && is_legal_name(ctx, name)) {
+ return NULL;
+ }
+ }
+
+ /* find the '.' if any */
+ dot_p = strrchr(name, '.');
+
+ if (dot_p) {
+ /* if the extension contains any illegal characters or
+ is too long or zero length then we treat it as part
+ of the prefix */
+ for (i=0; i<4 && dot_p[i+1]; i++) {
+ if (! FLAG_CHECK(dot_p[i+1], FLAG_ASCII)) {
+ dot_p = NULL;
+ break;
+ }
+ }
+ if (i == 0 || i == 4) dot_p = NULL;
+ }
+
+ /* the leading characters in the mangled name is taken from
+ the first characters of the name, if they are ascii otherwise
+ '_' is used
+ */
+ for (i=0;i<ctx->mangle_prefix && name[i];i++) {
+ lead_chars[i] = name[i];
+ if (! FLAG_CHECK(lead_chars[i], FLAG_ASCII)) {
+ lead_chars[i] = '_';
+ }
+ lead_chars[i] = toupper((unsigned char)lead_chars[i]);
+ }
+ for (;i<ctx->mangle_prefix;i++) {
+ lead_chars[i] = '_';
+ }
+
+ /* the prefix is anything up to the first dot */
+ if (dot_p) {
+ prefix_len = PTR_DIFF(dot_p, name);
+ } else {
+ prefix_len = strlen(name);
+ }
+
+ /* the extension of the mangled name is taken from the first 3
+ ascii chars after the dot */
+ extension_length = 0;
+ if (dot_p) {
+ for (i=1; extension_length < 3 && dot_p[i]; i++) {
+ unsigned char c = dot_p[i];
+ if (FLAG_CHECK(c, FLAG_ASCII)) {
+ extension[extension_length++] = toupper(c);
+ }
+ }
+ }
+
+ /* find the hash for this prefix */
+ v = hash = mangle_hash(ctx, name, prefix_len);
+
+ new_name = talloc_array(ctx, char, 13);
+ if (new_name == NULL) {
+ return NULL;
+ }
+
+ /* now form the mangled name. */
+ for (i=0;i<ctx->mangle_prefix;i++) {
+ new_name[i] = lead_chars[i];
+ }
+ new_name[7] = basechars[v % 36];
+ new_name[6] = '~';
+ for (i=5; i>=ctx->mangle_prefix; i--) {
+ v = v / 36;
+ new_name[i] = basechars[v % 36];
+ }
+
+ /* add the extension */
+ if (extension_length) {
+ new_name[8] = '.';
+ memcpy(&new_name[9], extension, extension_length);
+ new_name[9+extension_length] = 0;
+ } else {
+ new_name[8] = 0;
+ }
+
+ if (cache83) {
+ /* put it in the cache */
+ cache_insert(ctx, name, prefix_len, hash);
+ }
+
+ M_DEBUG(10,("name_map: %s -> %08X -> %s (cache=%d)\n",
+ name, hash, new_name, cache83));
+
+ return new_name;
+}
+
+
+/* initialise the flags table
+
+ we allow only a very restricted set of characters as 'ascii' in this
+ mangling backend. This isn't a significant problem as modern clients
+ use the 'long' filenames anyway, and those don't have these
+ restrictions.
+*/
+static void init_tables(struct pvfs_mangle_context *ctx)
+{
+ const char *basechars = MANGLE_BASECHARS;
+ int i;
+ /* the list of reserved dos names - all of these are illegal */
+
+ ZERO_STRUCT(ctx->char_flags);
+
+ for (i=1;i<128;i++) {
+ if ((i >= '0' && i <= '9') ||
+ (i >= 'a' && i <= 'z') ||
+ (i >= 'A' && i <= 'Z')) {
+ ctx->char_flags[i] |= (FLAG_ASCII | FLAG_BASECHAR);
+ }
+ if (strchr("_-$~", i)) {
+ ctx->char_flags[i] |= FLAG_ASCII;
+ }
+
+ if (strchr("*\\/?<>|\":", i)) {
+ ctx->char_flags[i] |= FLAG_ILLEGAL;
+ }
+
+ if (strchr("*?\"<>", i)) {
+ ctx->char_flags[i] |= FLAG_WILDCARD;
+ }
+ }
+
+ ZERO_STRUCT(ctx->base_reverse);
+ for (i=0;i<36;i++) {
+ ctx->base_reverse[(uint8_t)basechars[i]] = i;
+ }
+
+ /* fill in the reserved names flags. These are used as a very
+ fast filter for finding possible DOS reserved filenames */
+ for (i=0; reserved_names[i]; i++) {
+ unsigned char c1, c2, c3, c4;
+
+ c1 = (unsigned char)reserved_names[i][0];
+ c2 = (unsigned char)reserved_names[i][1];
+ c3 = (unsigned char)reserved_names[i][2];
+ c4 = (unsigned char)reserved_names[i][3];
+
+ ctx->char_flags[c1] |= FLAG_POSSIBLE1;
+ ctx->char_flags[c2] |= FLAG_POSSIBLE2;
+ ctx->char_flags[c3] |= FLAG_POSSIBLE3;
+ ctx->char_flags[c4] |= FLAG_POSSIBLE4;
+ ctx->char_flags[tolower(c1)] |= FLAG_POSSIBLE1;
+ ctx->char_flags[tolower(c2)] |= FLAG_POSSIBLE2;
+ ctx->char_flags[tolower(c3)] |= FLAG_POSSIBLE3;
+ ctx->char_flags[tolower(c4)] |= FLAG_POSSIBLE4;
+
+ ctx->char_flags[(unsigned char)'.'] |= FLAG_POSSIBLE4;
+ }
+
+ ctx->mangle_modulus = 1;
+ for (i=0;i<(7-ctx->mangle_prefix);i++) {
+ ctx->mangle_modulus *= 36;
+ }
+}
+
+/*
+ initialise the mangling code
+ */
+NTSTATUS pvfs_mangle_init(struct pvfs_state *pvfs)
+{
+ struct pvfs_mangle_context *ctx;
+
+ ctx = talloc(pvfs, struct pvfs_mangle_context);
+ if (ctx == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ctx->iconv_convenience = lp_iconv_convenience(pvfs->ntvfs->ctx->lp_ctx);
+
+ /* by default have a max of 512 entries in the cache. */
+ ctx->cache_size = lp_parm_int(pvfs->ntvfs->ctx->lp_ctx, NULL, "mangle", "cachesize", 512);
+
+ ctx->prefix_cache = talloc_array(ctx, char *, ctx->cache_size);
+ if (ctx->prefix_cache == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ ctx->prefix_cache_hashes = talloc_array(ctx, uint32_t, ctx->cache_size);
+ if (ctx->prefix_cache_hashes == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ memset(ctx->prefix_cache, 0, sizeof(char *) * ctx->cache_size);
+ memset(ctx->prefix_cache_hashes, 0, sizeof(uint32_t) * ctx->cache_size);
+
+ ctx->mangle_prefix = lp_parm_int(pvfs->ntvfs->ctx->lp_ctx, NULL, "mangle", "prefix", -1);
+ if (ctx->mangle_prefix < 0 || ctx->mangle_prefix > 6) {
+ ctx->mangle_prefix = DEFAULT_MANGLE_PREFIX;
+ }
+
+ init_tables(ctx);
+
+ pvfs->mangle_ctx = ctx;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ return the short name for a component of a full name
+*/
+char *pvfs_short_name_component(struct pvfs_state *pvfs, const char *name)
+{
+ return name_map(pvfs->mangle_ctx, name, true, true);
+}
+
+
+/*
+ return the short name for a given entry in a directory
+*/
+const char *pvfs_short_name(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
+ struct pvfs_filename *name)
+{
+ char *p = strrchr(name->full_name, '/');
+ char *ret = pvfs_short_name_component(pvfs, p+1);
+ if (ret == NULL) {
+ return p+1;
+ }
+ talloc_steal(mem_ctx, ret);
+ return ret;
+}
+
+/*
+ lookup a mangled name, returning the original long name if present
+ in the cache
+*/
+char *pvfs_mangled_lookup(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
+ const char *name)
+{
+ return check_cache(pvfs->mangle_ctx, mem_ctx, name);
+}
+
+
+/*
+ look for a DOS reserved name
+*/
+bool pvfs_is_reserved_name(struct pvfs_state *pvfs, const char *name)
+{
+ return is_reserved_name(pvfs->mangle_ctx, name);
+}
+
+
+/*
+ see if a component of a filename could be a mangled name from our
+ mangling code
+*/
+bool pvfs_is_mangled_component(struct pvfs_state *pvfs, const char *name)
+{
+ return is_mangled_component(pvfs->mangle_ctx, name, strlen(name));
+}
diff --git a/source4/ntvfs/posix/pvfs_streams.c b/source4/ntvfs/posix/pvfs_streams.c
new file mode 100644
index 0000000000..381d2033b0
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_streams.c
@@ -0,0 +1,542 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "vfs_posix.h"
+#include "librpc/gen_ndr/xattr.h"
+
+/*
+ normalise a stream name, removing a :$DATA suffix if there is one
+ Note: this returns the existing pointer to the name if the name does
+ not need normalising
+ */
+static const char *stream_name_normalise(TALLOC_CTX *ctx, const char *name)
+{
+ const char *c = strchr_m(name, ':');
+ if (c == NULL || strcasecmp_m(c, ":$DATA") != 0) {
+ return name;
+ }
+ return talloc_strndup(ctx, name, c-name);
+}
+
+/*
+ compare two stream names, taking account of the default $DATA extension
+ */
+static int stream_name_cmp(const char *name1, const char *name2)
+{
+ const char *c1, *c2;
+ int l1, l2, ret;
+ c1 = strchr_m(name1, ':');
+ c2 = strchr_m(name2, ':');
+
+ /* check the first part is the same */
+ l1 = c1?(c1 - name1):strlen(name1);
+ l2 = c2?(c2 - name2):strlen(name2);
+ if (l1 != l2) {
+ return l1 - l2;
+ }
+ ret = strncasecmp_m(name1, name2, l1);
+ if (ret != 0) {
+ return ret;
+ }
+
+ /* the first parts are the same, check the suffix */
+ if (c1 && c2) {
+ return strcasecmp_m(c1, c2);
+ }
+
+ if (c1) {
+ return strcasecmp_m(c1, ":$DATA");
+ }
+ if (c2) {
+ return strcasecmp_m(c2, ":$DATA");
+ }
+
+ /* neither names have a suffix */
+ return 0;
+}
+
+
+/*
+ return the list of file streams for RAW_FILEINFO_STREAM_INFORMATION
+*/
+NTSTATUS pvfs_stream_information(struct pvfs_state *pvfs,
+ TALLOC_CTX *mem_ctx,
+ struct pvfs_filename *name, int fd,
+ struct stream_information *info)
+{
+ struct xattr_DosStreams *streams;
+ int i;
+ NTSTATUS status;
+
+ /* directories don't have streams */
+ if (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) {
+ info->num_streams = 0;
+ info->streams = NULL;
+ return NT_STATUS_OK;
+ }
+
+ streams = talloc(mem_ctx, struct xattr_DosStreams);
+ if (streams == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = pvfs_streams_load(pvfs, name, fd, streams);
+ if (!NT_STATUS_IS_OK(status)) {
+ ZERO_STRUCTP(streams);
+ }
+
+ info->num_streams = streams->num_streams+1;
+ info->streams = talloc_array(mem_ctx, struct stream_struct, info->num_streams);
+ if (!info->streams) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ info->streams[0].size = name->st.st_size;
+ info->streams[0].alloc_size = name->dos.alloc_size;
+ info->streams[0].stream_name.s = talloc_strdup(info->streams, "::$DATA");
+
+ for (i=0;i<streams->num_streams;i++) {
+ info->streams[i+1].size = streams->streams[i].size;
+ info->streams[i+1].alloc_size = streams->streams[i].alloc_size;
+ if (strchr(streams->streams[i].name, ':') == NULL) {
+ info->streams[i+1].stream_name.s = talloc_asprintf(streams->streams,
+ ":%s:$DATA",
+ streams->streams[i].name);
+ } else {
+ info->streams[i+1].stream_name.s = talloc_strdup(streams->streams,
+ streams->streams[i].name);
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ fill in the stream information for a name
+*/
+NTSTATUS pvfs_stream_info(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd)
+{
+ struct xattr_DosStreams *streams;
+ int i;
+ NTSTATUS status;
+
+ /* the NULL stream always exists */
+ if (name->stream_name == NULL) {
+ name->stream_exists = true;
+ return NT_STATUS_OK;
+ }
+
+ streams = talloc(name, struct xattr_DosStreams);
+ if (streams == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = pvfs_streams_load(pvfs, name, fd, streams);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(streams);
+ return status;
+ }
+
+ for (i=0;i<streams->num_streams;i++) {
+ struct xattr_DosStream *s = &streams->streams[i];
+ if (stream_name_cmp(s->name, name->stream_name) == 0) {
+ name->dos.alloc_size = pvfs_round_alloc_size(pvfs, s->alloc_size);
+ name->st.st_size = s->size;
+ name->stream_exists = true;
+ talloc_free(streams);
+ return NT_STATUS_OK;
+ }
+ }
+
+ talloc_free(streams);
+
+ name->dos.alloc_size = 0;
+ name->st.st_size = 0;
+ name->stream_exists = false;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ update size information for a stream
+*/
+static NTSTATUS pvfs_stream_update_size(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
+ off_t size)
+{
+ struct xattr_DosStreams *streams;
+ int i;
+ NTSTATUS status;
+
+ streams = talloc(name, struct xattr_DosStreams);
+ if (streams == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = pvfs_streams_load(pvfs, name, fd, streams);
+ if (!NT_STATUS_IS_OK(status)) {
+ ZERO_STRUCTP(streams);
+ }
+
+ for (i=0;i<streams->num_streams;i++) {
+ struct xattr_DosStream *s = &streams->streams[i];
+ if (stream_name_cmp(s->name, name->stream_name) == 0) {
+ s->size = size;
+ s->alloc_size = pvfs_round_alloc_size(pvfs, size);
+ break;
+ }
+ }
+
+ if (i == streams->num_streams) {
+ struct xattr_DosStream *s;
+ streams->streams = talloc_realloc(streams, streams->streams,
+ struct xattr_DosStream,
+ streams->num_streams+1);
+ if (streams->streams == NULL) {
+ talloc_free(streams);
+ return NT_STATUS_NO_MEMORY;
+ }
+ streams->num_streams++;
+ s = &streams->streams[i];
+
+ s->flags = XATTR_STREAM_FLAG_INTERNAL;
+ s->size = size;
+ s->alloc_size = pvfs_round_alloc_size(pvfs, size);
+ s->name = stream_name_normalise(streams, name->stream_name);
+ if (s->name == NULL) {
+ talloc_free(streams);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ status = pvfs_streams_save(pvfs, name, fd, streams);
+ talloc_free(streams);
+
+ return status;
+}
+
+
+/*
+ rename a stream
+*/
+NTSTATUS pvfs_stream_rename(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
+ const char *new_name)
+{
+ struct xattr_DosStreams *streams;
+ int i, found_old, found_new;
+ NTSTATUS status;
+
+ streams = talloc(name, struct xattr_DosStreams);
+ if (streams == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ new_name = stream_name_normalise(streams, new_name);
+ if (new_name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = pvfs_streams_load(pvfs, name, fd, streams);
+ if (!NT_STATUS_IS_OK(status)) {
+ ZERO_STRUCTP(streams);
+ }
+
+ /* the default stream always exists */
+ if (strcmp(new_name, "") == 0 ||
+ strcasecmp_m(new_name, ":$DATA") == 0) {
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ /* try to find the old/new names in the list */
+ found_old = found_new = -1;
+ for (i=0;i<streams->num_streams;i++) {
+ struct xattr_DosStream *s = &streams->streams[i];
+ if (stream_name_cmp(s->name, new_name) == 0) {
+ found_new = i;
+ }
+ if (stream_name_cmp(s->name, name->stream_name) == 0) {
+ found_old = i;
+ }
+ }
+
+ if (found_old == -1) {
+ talloc_free(streams);
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ if (found_new == -1) {
+ /* a simple rename */
+ struct xattr_DosStream *s = &streams->streams[found_old];
+ s->name = new_name;
+ } else {
+ /* remove the old one and replace with the new one */
+ streams->streams[found_old].name = new_name;
+ memmove(&streams->streams[found_new],
+ &streams->streams[found_new+1],
+ sizeof(streams->streams[0]) *
+ (streams->num_streams - (found_new+1)));
+ }
+
+ status = pvfs_streams_save(pvfs, name, fd, streams);
+ talloc_free(streams);
+
+ return status;
+}
+
+
+/*
+ create the xattr for a alternate data stream
+*/
+NTSTATUS pvfs_stream_create(struct pvfs_state *pvfs,
+ struct pvfs_filename *name,
+ int fd)
+{
+ NTSTATUS status;
+ status = pvfs_xattr_create(pvfs, name->full_name, fd,
+ XATTR_DOSSTREAM_PREFIX, name->stream_name);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ return pvfs_stream_update_size(pvfs, name, fd, 0);
+}
+
+/*
+ delete the xattr for a alternate data stream
+*/
+NTSTATUS pvfs_stream_delete(struct pvfs_state *pvfs,
+ struct pvfs_filename *name,
+ int fd)
+{
+ NTSTATUS status;
+ struct xattr_DosStreams *streams;
+ int i;
+
+ status = pvfs_xattr_delete(pvfs, name->full_name, fd,
+ XATTR_DOSSTREAM_PREFIX, name->stream_name);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ streams = talloc(name, struct xattr_DosStreams);
+ if (streams == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = pvfs_streams_load(pvfs, name, fd, streams);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(streams);
+ return status;
+ }
+
+ for (i=0;i<streams->num_streams;i++) {
+ struct xattr_DosStream *s = &streams->streams[i];
+ if (stream_name_cmp(s->name, name->stream_name) == 0) {
+ memmove(s, s+1, (streams->num_streams - (i+1)) * sizeof(*s));
+ streams->num_streams--;
+ break;
+ }
+ }
+
+ status = pvfs_streams_save(pvfs, name, fd, streams);
+ talloc_free(streams);
+
+ return status;
+}
+
+/*
+ load a stream into a blob
+*/
+static NTSTATUS pvfs_stream_load(struct pvfs_state *pvfs,
+ TALLOC_CTX *mem_ctx,
+ struct pvfs_filename *name,
+ int fd,
+ size_t estimated_size,
+ DATA_BLOB *blob)
+{
+ NTSTATUS status;
+
+ status = pvfs_xattr_load(pvfs, mem_ctx, name->full_name, fd,
+ XATTR_DOSSTREAM_PREFIX,
+ name->stream_name, estimated_size, blob);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ /* try with a case insensitive match */
+ struct xattr_DosStreams *streams;
+ int i;
+
+ streams = talloc(mem_ctx, struct xattr_DosStreams);
+ if (streams == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = pvfs_streams_load(pvfs, name, fd, streams);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(streams);
+ return NT_STATUS_NOT_FOUND;
+ }
+ for (i=0;i<streams->num_streams;i++) {
+ struct xattr_DosStream *s = &streams->streams[i];
+ if (stream_name_cmp(s->name, name->stream_name) == 0) {
+ status = pvfs_xattr_load(pvfs, mem_ctx, name->full_name, fd,
+ XATTR_DOSSTREAM_PREFIX,
+ s->name, estimated_size, blob);
+ talloc_free(streams);
+ return status;
+ }
+ }
+ talloc_free(streams);
+ return NT_STATUS_NOT_FOUND;
+ }
+
+ return status;
+}
+
+/*
+ the equvalent of pread() on a stream
+*/
+ssize_t pvfs_stream_read(struct pvfs_state *pvfs,
+ struct pvfs_file_handle *h, void *data, size_t count, off_t offset)
+{
+ NTSTATUS status;
+ DATA_BLOB blob;
+ if (count == 0) {
+ return 0;
+ }
+ status = pvfs_stream_load(pvfs, h, h->name, h->fd, offset+count, &blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ errno = EIO;
+ return -1;
+ }
+ if (offset >= blob.length) {
+ data_blob_free(&blob);
+ return 0;
+ }
+ if (count > blob.length - offset) {
+ count = blob.length - offset;
+ }
+ memcpy(data, blob.data + offset, count);
+ data_blob_free(&blob);
+ return count;
+}
+
+
+/*
+ the equvalent of pwrite() on a stream
+*/
+ssize_t pvfs_stream_write(struct pvfs_state *pvfs,
+ struct pvfs_file_handle *h, const void *data, size_t count, off_t offset)
+{
+ NTSTATUS status;
+ DATA_BLOB blob;
+ if (count == 0) {
+ return 0;
+ }
+
+ if (count+offset > XATTR_MAX_STREAM_SIZE) {
+ if (!pvfs->ea_db || count+offset > XATTR_MAX_STREAM_SIZE_TDB) {
+ errno = ENOSPC;
+ return -1;
+ }
+ }
+
+ /* we have to load the existing stream, then modify, then save */
+ status = pvfs_stream_load(pvfs, h, h->name, h->fd, offset+count, &blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ blob = data_blob(NULL, 0);
+ }
+ if (count+offset > blob.length) {
+ blob.data = talloc_realloc(blob.data, blob.data, uint8_t, count+offset);
+ if (blob.data == NULL) {
+ errno = ENOMEM;
+ return -1;
+ }
+ if (offset > blob.length) {
+ memset(blob.data+blob.length, 0, offset - blob.length);
+ }
+ blob.length = count+offset;
+ }
+ memcpy(blob.data + offset, data, count);
+
+ status = pvfs_xattr_save(pvfs, h->name->full_name, h->fd, XATTR_DOSSTREAM_PREFIX,
+ h->name->stream_name, &blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ data_blob_free(&blob);
+ /* getting this error mapping right is probably
+ not worth it */
+ errno = ENOSPC;
+ return -1;
+ }
+
+ status = pvfs_stream_update_size(pvfs, h->name, h->fd, blob.length);
+
+ data_blob_free(&blob);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ errno = EIO;
+ return -1;
+ }
+
+ return count;
+}
+
+/*
+ the equvalent of truncate() on a stream
+*/
+NTSTATUS pvfs_stream_truncate(struct pvfs_state *pvfs,
+ struct pvfs_filename *name, int fd, off_t length)
+{
+ NTSTATUS status;
+ DATA_BLOB blob;
+
+ if (length > XATTR_MAX_STREAM_SIZE) {
+ if (!pvfs->ea_db || length > XATTR_MAX_STREAM_SIZE_TDB) {
+ return NT_STATUS_DISK_FULL;
+ }
+ }
+
+ /* we have to load the existing stream, then modify, then save */
+ status = pvfs_stream_load(pvfs, name, name, fd, length, &blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (length <= blob.length) {
+ blob.length = length;
+ } else if (length > blob.length) {
+ blob.data = talloc_realloc(blob.data, blob.data, uint8_t, length);
+ if (blob.data == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ memset(blob.data+blob.length, 0, length - blob.length);
+ blob.length = length;
+ }
+
+ status = pvfs_xattr_save(pvfs, name->full_name, fd, XATTR_DOSSTREAM_PREFIX,
+ name->stream_name, &blob);
+ data_blob_free(&blob);
+
+ if (NT_STATUS_IS_OK(status)) {
+ status = pvfs_stream_update_size(pvfs, name, fd, blob.length);
+ }
+
+ return status;
+}
diff --git a/source4/ntvfs/posix/pvfs_unlink.c b/source4/ntvfs/posix/pvfs_unlink.c
new file mode 100644
index 0000000000..e10b2e3eef
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_unlink.c
@@ -0,0 +1,276 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - unlink
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "vfs_posix.h"
+#include "system/dir.h"
+
+/*
+ retry an open after a sharing violation
+*/
+static void pvfs_retry_unlink(struct pvfs_odb_retry *r,
+ struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ void *_io,
+ void *private_data,
+ enum pvfs_wait_notice reason)
+{
+ union smb_unlink *io = talloc_get_type(_io, union smb_unlink);
+ NTSTATUS status = NT_STATUS_INTERNAL_ERROR;
+
+ talloc_free(r);
+
+ switch (reason) {
+ case PVFS_WAIT_CANCEL:
+/*TODO*/
+ status = NT_STATUS_CANCELLED;
+ break;
+ case PVFS_WAIT_TIMEOUT:
+ /* if it timed out, then give the failure
+ immediately */
+/*TODO*/
+ status = NT_STATUS_SHARING_VIOLATION;
+ break;
+ case PVFS_WAIT_EVENT:
+
+ /* try the open again, which could trigger another retry setup
+ if it wants to, so we have to unmark the async flag so we
+ will know if it does a second async reply */
+ req->async_states->state &= ~NTVFS_ASYNC_STATE_ASYNC;
+
+ status = pvfs_unlink(ntvfs, req, io);
+ if (req->async_states->state & NTVFS_ASYNC_STATE_ASYNC) {
+ /* the 2nd try also replied async, so we don't send
+ the reply yet */
+ return;
+ }
+
+ /* re-mark it async, just in case someone up the chain does
+ paranoid checking */
+ req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
+ break;
+ }
+
+ /* send the reply up the chain */
+ req->async_states->status = status;
+ req->async_states->send_fn(req);
+}
+
+/*
+ setup for a unlink retry after a sharing violation
+ or a non granted oplock
+*/
+static NTSTATUS pvfs_unlink_setup_retry(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_unlink *io,
+ struct odb_lock *lck,
+ NTSTATUS status)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ struct timeval end_time;
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) {
+ end_time = timeval_add(&req->statistics.request_time,
+ 0, pvfs->sharing_violation_delay);
+ } else if (NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) {
+ end_time = timeval_add(&req->statistics.request_time,
+ pvfs->oplock_break_timeout, 0);
+ } else {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ return pvfs_odb_retry_setup(ntvfs, req, lck, end_time, io, NULL,
+ pvfs_retry_unlink);
+}
+
+
+/*
+ unlink a file
+*/
+static NTSTATUS pvfs_unlink_file(struct pvfs_state *pvfs,
+ struct pvfs_filename *name)
+{
+ NTSTATUS status;
+
+ if (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) {
+ return NT_STATUS_FILE_IS_A_DIRECTORY;
+ }
+
+ if (name->st.st_nlink == 1) {
+ status = pvfs_xattr_unlink_hook(pvfs, name->full_name);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ /* finally try the actual unlink */
+ if (unlink(name->full_name) == -1) {
+ status = pvfs_map_errno(pvfs, errno);
+ }
+
+ if (NT_STATUS_IS_OK(status)) {
+ notify_trigger(pvfs->notify_context,
+ NOTIFY_ACTION_REMOVED,
+ FILE_NOTIFY_CHANGE_FILE_NAME,
+ name->full_name);
+ }
+
+ return status;
+}
+
+/*
+ unlink one file
+*/
+static NTSTATUS pvfs_unlink_one(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
+ union smb_unlink *unl,
+ struct pvfs_filename *name)
+{
+ NTSTATUS status;
+ struct odb_lock *lck = NULL;
+
+ /* make sure its matches the given attributes */
+ status = pvfs_match_attrib(pvfs, name,
+ unl->unlink.in.attrib, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = pvfs_can_delete(pvfs, req, name, &lck);
+
+ /*
+ * on a sharing violation we need to retry when the file is closed by
+ * the other user, or after 1 second
+ * on a non granted oplock we need to retry when the file is closed by
+ * the other user, or after 30 seconds
+ */
+ if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) &&
+ (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ return pvfs_unlink_setup_retry(pvfs->ntvfs, req, unl, lck, status);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (name->stream_name) {
+ if (!name->stream_exists) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ return pvfs_stream_delete(pvfs, name, -1);
+ }
+
+ return pvfs_unlink_file(pvfs, name);
+}
+
+/*
+ delete a file - the dirtype specifies the file types to include in the search.
+ The name can contain CIFS wildcards, but rarely does (except with OS/2 clients)
+*/
+NTSTATUS pvfs_unlink(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_unlink *unl)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ struct pvfs_dir *dir;
+ NTSTATUS status;
+ uint32_t total_deleted=0;
+ struct pvfs_filename *name;
+ const char *fname;
+ off_t ofs;
+
+ /* resolve the cifs name to a posix name */
+ status = pvfs_resolve_name(pvfs, req, unl->unlink.in.pattern,
+ PVFS_RESOLVE_WILDCARD |
+ PVFS_RESOLVE_STREAMS |
+ PVFS_RESOLVE_NO_OPENDB,
+ &name);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (!name->exists && !name->has_wildcard) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ if (name->exists &&
+ (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
+ return NT_STATUS_FILE_IS_A_DIRECTORY;
+ }
+
+ if (!name->has_wildcard) {
+ return pvfs_unlink_one(pvfs, req, unl, name);
+ }
+
+ /*
+ * disable async requests in the wildcard case
+ * untill we have proper tests for this
+ */
+ req->async_states->state &= ~NTVFS_ASYNC_STATE_MAY_ASYNC;
+
+ /* get list of matching files */
+ status = pvfs_list_start(pvfs, name, req, &dir);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = NT_STATUS_NO_SUCH_FILE;
+ talloc_free(name);
+
+ ofs = 0;
+
+ while ((fname = pvfs_list_next(dir, &ofs))) {
+ /* this seems to be a special case */
+ if ((unl->unlink.in.attrib & FILE_ATTRIBUTE_DIRECTORY) &&
+ (ISDOT(fname) || ISDOTDOT(fname))) {
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ }
+
+ /* get a pvfs_filename object */
+ status = pvfs_resolve_partial(pvfs, req,
+ pvfs_list_unix_path(dir),
+ fname,
+ PVFS_RESOLVE_NO_OPENDB,
+ &name);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = pvfs_unlink_one(pvfs, req, unl, name);
+ if (NT_STATUS_IS_OK(status)) {
+ total_deleted++;
+ }
+
+ talloc_free(name);
+ }
+
+ if (total_deleted > 0) {
+ status = NT_STATUS_OK;
+ }
+
+ return status;
+}
+
+
diff --git a/source4/ntvfs/posix/pvfs_util.c b/source4/ntvfs/posix/pvfs_util.c
new file mode 100644
index 0000000000..81ff20a608
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_util.c
@@ -0,0 +1,202 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+/*
+ utility functions for posix backend
+*/
+
+#include "includes.h"
+#include "vfs_posix.h"
+
+/*
+ return true if a string contains one of the CIFS wildcard characters
+*/
+bool pvfs_has_wildcard(const char *str)
+{
+ if (strpbrk(str, "*?<>\"")) {
+ return true;
+ }
+ return false;
+}
+
+/*
+ map a unix errno to a NTSTATUS
+*/
+NTSTATUS pvfs_map_errno(struct pvfs_state *pvfs, int unix_errno)
+{
+ return map_nt_error_from_unix(unix_errno);
+}
+
+
+/*
+ check if a filename has an attribute matching the given attribute search value
+ this is used by calls like unlink and search which take an attribute
+ and only include special files if they match the given attribute
+*/
+NTSTATUS pvfs_match_attrib(struct pvfs_state *pvfs, struct pvfs_filename *name,
+ uint32_t attrib, uint32_t must_attrib)
+{
+ if ((name->dos.attrib & ~attrib) & FILE_ATTRIBUTE_DIRECTORY) {
+ return NT_STATUS_FILE_IS_A_DIRECTORY;
+ }
+ if ((name->dos.attrib & ~attrib) & (FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM)) {
+ return NT_STATUS_NO_SUCH_FILE;
+ }
+ if (must_attrib & ~name->dos.attrib) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+ return NT_STATUS_OK;
+}
+
+
+/*
+ normalise a file attribute
+*/
+uint32_t pvfs_attrib_normalise(uint32_t attrib, mode_t mode)
+{
+ if (attrib != FILE_ATTRIBUTE_NORMAL) {
+ attrib &= ~FILE_ATTRIBUTE_NORMAL;
+ }
+ if (S_ISDIR(mode)) {
+ attrib |= FILE_ATTRIBUTE_DIRECTORY;
+ } else {
+ attrib &= ~FILE_ATTRIBUTE_DIRECTORY;
+ }
+ return attrib;
+}
+
+
+/*
+ copy a file. Caller is supposed to have already ensured that the
+ operation is allowed. The destination file must not exist.
+*/
+NTSTATUS pvfs_copy_file(struct pvfs_state *pvfs,
+ struct pvfs_filename *name1,
+ struct pvfs_filename *name2)
+{
+ int fd1, fd2;
+ mode_t mode;
+ NTSTATUS status;
+ size_t buf_size = 0x10000;
+ uint8_t *buf = talloc_array(name2, uint8_t, buf_size);
+
+ if (buf == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ fd1 = open(name1->full_name, O_RDONLY);
+ if (fd1 == -1) {
+ talloc_free(buf);
+ return pvfs_map_errno(pvfs, errno);
+ }
+
+ fd2 = open(name2->full_name, O_CREAT|O_EXCL|O_WRONLY, 0);
+ if (fd2 == -1) {
+ close(fd1);
+ talloc_free(buf);
+ return pvfs_map_errno(pvfs, errno);
+ }
+
+ while (1) {
+ ssize_t ret2, ret = read(fd1, buf, buf_size);
+ if (ret == -1 &&
+ (errno == EINTR || errno == EAGAIN)) {
+ continue;
+ }
+ if (ret <= 0) break;
+
+ ret2 = write(fd2, buf, ret);
+ if (ret2 == -1 &&
+ (errno == EINTR || errno == EAGAIN)) {
+ continue;
+ }
+
+ if (ret2 != ret) {
+ close(fd1);
+ close(fd2);
+ talloc_free(buf);
+ unlink(name2->full_name);
+ if (ret2 == -1) {
+ return pvfs_map_errno(pvfs, errno);
+ }
+ return NT_STATUS_DISK_FULL;
+ }
+ }
+
+ talloc_free(buf);
+ close(fd1);
+
+ mode = pvfs_fileperms(pvfs, name1->dos.attrib);
+ if (fchmod(fd2, mode) == -1) {
+ status = pvfs_map_errno(pvfs, errno);
+ close(fd2);
+ unlink(name2->full_name);
+ return status;
+ }
+
+ name2->st.st_mode = mode;
+ name2->dos = name1->dos;
+
+ status = pvfs_dosattrib_save(pvfs, name2, fd2);
+ if (!NT_STATUS_IS_OK(status)) {
+ close(fd2);
+ unlink(name2->full_name);
+ return status;
+ }
+
+ close(fd2);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ hash a string of the specified length. The string does not need to be
+ null terminated
+
+ hash alghorithm changed to FNV1 by idra@samba.org (Simo Sorce).
+ see http://www.isthe.com/chongo/tech/comp/fnv/index.html for a
+ discussion on Fowler / Noll / Vo (FNV) Hash by one of it's authors
+*/
+uint32_t pvfs_name_hash(const char *key, size_t length)
+{
+ const uint32_t fnv1_prime = 0x01000193;
+ const uint32_t fnv1_init = 0xa6b93095;
+ uint32_t value = fnv1_init;
+
+ while (*key && length--) {
+ size_t c_size;
+ codepoint_t c = next_codepoint(key, &c_size);
+ c = toupper_m(c);
+ value *= fnv1_prime;
+ value ^= (uint32_t)c;
+ key += c_size;
+ }
+
+ return value;
+}
+
+
+/*
+ file allocation size rounding. This is required to pass ifstest
+*/
+uint64_t pvfs_round_alloc_size(struct pvfs_state *pvfs, uint64_t size)
+{
+ const uint32_t round_value = pvfs->alloc_size_rounding;
+ return round_value * ((size + round_value - 1)/round_value);
+}
diff --git a/source4/ntvfs/posix/pvfs_wait.c b/source4/ntvfs/posix/pvfs_wait.c
new file mode 100644
index 0000000000..5552ab0d1b
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_wait.c
@@ -0,0 +1,201 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - async request wait routines
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/events/events.h"
+#include "../lib/util/dlinklist.h"
+#include "vfs_posix.h"
+#include "smbd/service_stream.h"
+#include "lib/messaging/irpc.h"
+
+/* the context for a single wait instance */
+struct pvfs_wait {
+ struct pvfs_wait *next, *prev;
+ struct pvfs_state *pvfs;
+ void (*handler)(void *, enum pvfs_wait_notice);
+ void *private_data;
+ int msg_type;
+ struct messaging_context *msg_ctx;
+ struct tevent_context *ev;
+ struct ntvfs_request *req;
+ enum pvfs_wait_notice reason;
+};
+
+/*
+ called from the ntvfs layer when we have requested setup of an async
+ call. this ensures that async calls runs with the right state of
+ previous ntvfs handlers in the chain (such as security context)
+*/
+NTSTATUS pvfs_async_setup(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, void *private_data)
+{
+ struct pvfs_wait *pwait = talloc_get_type(private_data,
+ struct pvfs_wait);
+ pwait->handler(pwait->private_data, pwait->reason);
+ return NT_STATUS_OK;
+}
+
+/*
+ receive a completion message for a wait
+*/
+static void pvfs_wait_dispatch(struct messaging_context *msg,
+ void *private_data, uint32_t msg_type,
+ struct server_id src, DATA_BLOB *data)
+{
+ struct pvfs_wait *pwait = talloc_get_type(private_data,
+ struct pvfs_wait);
+ struct ntvfs_request *req;
+ void *p = NULL;
+
+ /* we need to check that this one is for us. See
+ messaging_send_ptr() for the other side of this.
+ */
+ if (data->length == sizeof(void *)) {
+ void **pp;
+ pp = (void **)data->data;
+ p = *pp;
+ }
+ if (p == NULL || p != pwait->private_data) {
+ return;
+ }
+
+ pwait->reason = PVFS_WAIT_EVENT;
+
+ /* the extra reference here is to ensure that the req
+ structure is not destroyed when the async request reply is
+ sent, which would cause problems with the other ntvfs
+ modules above us */
+ req = talloc_reference(msg, pwait->req);
+ ntvfs_async_setup(pwait->req, pwait);
+ talloc_unlink(msg, req);
+}
+
+
+/*
+ receive a timeout on a message wait
+*/
+static void pvfs_wait_timeout(struct tevent_context *ev,
+ struct tevent_timer *te, struct timeval t,
+ void *private_data)
+{
+ struct pvfs_wait *pwait = talloc_get_type(private_data,
+ struct pvfs_wait);
+ struct ntvfs_request *req = pwait->req;
+
+ pwait->reason = PVFS_WAIT_TIMEOUT;
+
+ talloc_increase_ref_count(req);
+ ntvfs_async_setup(pwait->req, pwait);
+ talloc_free(req);
+}
+
+
+/*
+ destroy a pending wait
+ */
+static int pvfs_wait_destructor(struct pvfs_wait *pwait)
+{
+ if (pwait->msg_type != -1) {
+ messaging_deregister(pwait->msg_ctx, pwait->msg_type, pwait);
+ }
+ DLIST_REMOVE(pwait->pvfs->wait_list, pwait);
+ return 0;
+}
+
+/*
+ setup a request to wait on a message of type msg_type, with a
+ timeout (given as an expiry time)
+
+ the return value is a handle. To stop waiting talloc_free this
+ handle.
+
+ if msg_type == -1 then no message is registered, and it is assumed
+ that the caller handles any messaging setup needed
+*/
+struct pvfs_wait *pvfs_wait_message(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
+ int msg_type,
+ struct timeval end_time,
+ void (*fn)(void *, enum pvfs_wait_notice),
+ void *private_data)
+{
+ struct pvfs_wait *pwait;
+
+ pwait = talloc(pvfs, struct pvfs_wait);
+ if (pwait == NULL) {
+ return NULL;
+ }
+
+ pwait->private_data = private_data;
+ pwait->handler = fn;
+ pwait->msg_ctx = pvfs->ntvfs->ctx->msg_ctx;
+ pwait->ev = pvfs->ntvfs->ctx->event_ctx;
+ pwait->msg_type = msg_type;
+ pwait->req = talloc_reference(pwait, req);
+ pwait->pvfs = pvfs;
+
+ if (!timeval_is_zero(&end_time)) {
+ /* setup a timer */
+ event_add_timed(pwait->ev, pwait, end_time, pvfs_wait_timeout, pwait);
+ }
+
+ /* register with the messaging subsystem for this message
+ type */
+ if (msg_type != -1) {
+ messaging_register(pwait->msg_ctx,
+ pwait,
+ msg_type,
+ pvfs_wait_dispatch);
+ }
+
+ /* tell the main smb server layer that we will be replying
+ asynchronously */
+ req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
+
+ DLIST_ADD(pvfs->wait_list, pwait);
+
+ /* make sure we cleanup the timer and message handler */
+ talloc_set_destructor(pwait, pvfs_wait_destructor);
+
+ return pwait;
+}
+
+
+/*
+ cancel an outstanding async request
+*/
+NTSTATUS pvfs_cancel(struct ntvfs_module_context *ntvfs, struct ntvfs_request *req)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ struct pvfs_wait *pwait;
+
+ for (pwait=pvfs->wait_list;pwait;pwait=pwait->next) {
+ if (pwait->req == req) {
+ /* trigger a cancel on the request */
+ pwait->reason = PVFS_WAIT_CANCEL;
+ ntvfs_async_setup(pwait->req, pwait);
+ return NT_STATUS_OK;
+ }
+ }
+
+ return NT_STATUS_DOS(ERRDOS, ERRcancelviolation);
+}
diff --git a/source4/ntvfs/posix/pvfs_write.c b/source4/ntvfs/posix/pvfs_write.c
new file mode 100644
index 0000000000..fb629a87fb
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_write.c
@@ -0,0 +1,155 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - write
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "vfs_posix.h"
+#include "librpc/gen_ndr/security.h"
+#include "lib/events/events.h"
+
+static void pvfs_write_time_update_handler(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data)
+{
+ struct pvfs_file_handle *h = talloc_get_type(private_data,
+ struct pvfs_file_handle);
+ struct odb_lock *lck;
+ NTSTATUS status;
+ NTTIME write_time;
+
+ lck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
+ if (lck == NULL) {
+ DEBUG(0,("Unable to lock opendb for write time update\n"));
+ return;
+ }
+
+ write_time = timeval_to_nttime(&tv);
+
+ status = odb_set_write_time(lck, write_time, false);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Unable to update write time: %s\n",
+ nt_errstr(status)));
+ return;
+ }
+
+ talloc_free(lck);
+
+ h->write_time.update_event = NULL;
+}
+
+static void pvfs_trigger_write_time_update(struct pvfs_file_handle *h)
+{
+ struct pvfs_state *pvfs = h->pvfs;
+ struct timeval tv;
+
+ if (h->write_time.update_triggered) {
+ return;
+ }
+
+ tv = timeval_current_ofs(0, pvfs->writetime_delay);
+
+ h->write_time.update_triggered = true;
+ h->write_time.update_on_close = true;
+ h->write_time.update_event = event_add_timed(pvfs->ntvfs->ctx->event_ctx,
+ h, tv,
+ pvfs_write_time_update_handler,
+ h);
+ if (!h->write_time.update_event) {
+ DEBUG(0,("Failed event_add_timed\n"));
+ }
+}
+
+/*
+ write to a file
+*/
+NTSTATUS pvfs_write(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_write *wr)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ ssize_t ret;
+ struct pvfs_file *f;
+ NTSTATUS status;
+
+ if (wr->generic.level != RAW_WRITE_WRITEX) {
+ return ntvfs_map_write(ntvfs, req, wr);
+ }
+
+ f = pvfs_find_fd(pvfs, req, wr->writex.in.file.ntvfs);
+ if (!f) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ if (f->handle->fd == -1) {
+ return NT_STATUS_INVALID_DEVICE_REQUEST;
+ }
+
+ if (!(f->access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA))) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ status = pvfs_check_lock(pvfs, f, req->smbpid,
+ wr->writex.in.offset,
+ wr->writex.in.count,
+ WRITE_LOCK);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ status = pvfs_break_level2_oplocks(f);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ pvfs_trigger_write_time_update(f->handle);
+
+ if (f->handle->name->stream_name) {
+ ret = pvfs_stream_write(pvfs,
+ f->handle,
+ wr->writex.in.data,
+ wr->writex.in.count,
+ wr->writex.in.offset);
+ } else {
+#if HAVE_LINUX_AIO
+ /* possibly try an aio write */
+ if ((req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC) &&
+ (pvfs->flags & PVFS_FLAG_LINUX_AIO)) {
+ status = pvfs_aio_pwrite(req, wr, f);
+ if (NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_OK;
+ }
+ }
+#endif
+ ret = pwrite(f->handle->fd,
+ wr->writex.in.data,
+ wr->writex.in.count,
+ wr->writex.in.offset);
+ }
+ if (ret == -1) {
+ if (errno == EFBIG) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ return pvfs_map_errno(pvfs, errno);
+ }
+
+ f->handle->seek_offset = wr->writex.in.offset + ret;
+
+ wr->writex.out.nwritten = ret;
+ wr->writex.out.remaining = 0; /* should fill this in? */
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/ntvfs/posix/pvfs_xattr.c b/source4/ntvfs/posix/pvfs_xattr.c
new file mode 100644
index 0000000000..1ca09402a3
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_xattr.c
@@ -0,0 +1,481 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - xattr support
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "vfs_posix.h"
+#include "../lib/util/unix_privs.h"
+#include "librpc/gen_ndr/ndr_xattr.h"
+#include "param/param.h"
+
+/*
+ pull a xattr as a blob
+*/
+static NTSTATUS pull_xattr_blob(struct pvfs_state *pvfs,
+ TALLOC_CTX *mem_ctx,
+ const char *attr_name,
+ const char *fname,
+ int fd,
+ size_t estimated_size,
+ DATA_BLOB *blob)
+{
+ NTSTATUS status;
+
+ if (pvfs->ea_db) {
+ return pull_xattr_blob_tdb(pvfs, mem_ctx, attr_name, fname,
+ fd, estimated_size, blob);
+ }
+
+ status = pull_xattr_blob_system(pvfs, mem_ctx, attr_name, fname,
+ fd, estimated_size, blob);
+
+ /* if the filesystem doesn't support them, then tell pvfs not to try again */
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)||
+ NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)||
+ NT_STATUS_EQUAL(status, NT_STATUS_INVALID_SYSTEM_SERVICE)) {
+ DEBUG(2,("pvfs_xattr: xattr not supported in filesystem: %s\n", nt_errstr(status)));
+ pvfs->flags &= ~PVFS_FLAG_XATTR_ENABLE;
+ status = NT_STATUS_NOT_FOUND;
+ }
+
+ return status;
+}
+
+/*
+ push a xattr as a blob
+*/
+static NTSTATUS push_xattr_blob(struct pvfs_state *pvfs,
+ const char *attr_name,
+ const char *fname,
+ int fd,
+ const DATA_BLOB *blob)
+{
+ if (pvfs->ea_db) {
+ return push_xattr_blob_tdb(pvfs, attr_name, fname, fd, blob);
+ }
+ return push_xattr_blob_system(pvfs, attr_name, fname, fd, blob);
+}
+
+
+/*
+ delete a xattr
+*/
+static NTSTATUS delete_xattr(struct pvfs_state *pvfs, const char *attr_name,
+ const char *fname, int fd)
+{
+ if (pvfs->ea_db) {
+ return delete_xattr_tdb(pvfs, attr_name, fname, fd);
+ }
+ return delete_xattr_system(pvfs, attr_name, fname, fd);
+}
+
+/*
+ a hook called on unlink - allows the tdb xattr backend to cleanup
+*/
+NTSTATUS pvfs_xattr_unlink_hook(struct pvfs_state *pvfs, const char *fname)
+{
+ if (pvfs->ea_db) {
+ return unlink_xattr_tdb(pvfs, fname);
+ }
+ return unlink_xattr_system(pvfs, fname);
+}
+
+
+/*
+ load a NDR structure from a xattr
+*/
+NTSTATUS pvfs_xattr_ndr_load(struct pvfs_state *pvfs,
+ TALLOC_CTX *mem_ctx,
+ const char *fname, int fd, const char *attr_name,
+ void *p, void *pull_fn)
+{
+ NTSTATUS status;
+ DATA_BLOB blob;
+ enum ndr_err_code ndr_err;
+
+ status = pull_xattr_blob(pvfs, mem_ctx, attr_name, fname,
+ fd, XATTR_DOSATTRIB_ESTIMATED_SIZE, &blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* pull the blob */
+ ndr_err = ndr_pull_struct_blob(&blob, mem_ctx, lp_iconv_convenience(pvfs->ntvfs->ctx->lp_ctx),
+ p, (ndr_pull_flags_fn_t)pull_fn);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ data_blob_free(&blob);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ save a NDR structure into a xattr
+*/
+NTSTATUS pvfs_xattr_ndr_save(struct pvfs_state *pvfs,
+ const char *fname, int fd, const char *attr_name,
+ void *p, void *push_fn)
+{
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+ DATA_BLOB blob;
+ NTSTATUS status;
+ enum ndr_err_code ndr_err;
+
+ ndr_err = ndr_push_struct_blob(&blob, mem_ctx, lp_iconv_convenience(pvfs->ntvfs->ctx->lp_ctx), p, (ndr_push_flags_fn_t)push_fn);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ talloc_free(mem_ctx);
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ status = push_xattr_blob(pvfs, attr_name, fname, fd, &blob);
+ talloc_free(mem_ctx);
+
+ return status;
+}
+
+
+/*
+ fill in file attributes from extended attributes
+*/
+NTSTATUS pvfs_dosattrib_load(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd)
+{
+ NTSTATUS status;
+ struct xattr_DosAttrib attrib;
+ TALLOC_CTX *mem_ctx = talloc_new(name);
+ struct xattr_DosInfo1 *info1;
+ struct xattr_DosInfo2Old *info2;
+
+ if (name->stream_name != NULL) {
+ name->stream_exists = false;
+ } else {
+ name->stream_exists = true;
+ }
+
+ if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
+ return NT_STATUS_OK;
+ }
+
+ status = pvfs_xattr_ndr_load(pvfs, mem_ctx, name->full_name,
+ fd, XATTR_DOSATTRIB_NAME,
+ &attrib,
+ (ndr_pull_flags_fn_t)ndr_pull_xattr_DosAttrib);
+
+ /* not having a DosAttrib is not an error */
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ talloc_free(mem_ctx);
+ return pvfs_stream_info(pvfs, name, fd);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(mem_ctx);
+ return status;
+ }
+
+ switch (attrib.version) {
+ case 1:
+ info1 = &attrib.info.info1;
+ name->dos.attrib = pvfs_attrib_normalise(info1->attrib,
+ name->st.st_mode);
+ name->dos.ea_size = info1->ea_size;
+ if (name->st.st_size == info1->size) {
+ name->dos.alloc_size =
+ pvfs_round_alloc_size(pvfs, info1->alloc_size);
+ }
+ if (!null_nttime(info1->create_time)) {
+ name->dos.create_time = info1->create_time;
+ }
+ if (!null_nttime(info1->change_time)) {
+ name->dos.change_time = info1->change_time;
+ }
+ name->dos.flags = 0;
+ break;
+
+ case 2:
+ /*
+ * Note: This is only used to parse existing values from disk
+ * We use xattr_DosInfo1 again for storing new values
+ */
+ info2 = &attrib.info.oldinfo2;
+ name->dos.attrib = pvfs_attrib_normalise(info2->attrib,
+ name->st.st_mode);
+ name->dos.ea_size = info2->ea_size;
+ if (name->st.st_size == info2->size) {
+ name->dos.alloc_size =
+ pvfs_round_alloc_size(pvfs, info2->alloc_size);
+ }
+ if (!null_nttime(info2->create_time)) {
+ name->dos.create_time = info2->create_time;
+ }
+ if (!null_nttime(info2->change_time)) {
+ name->dos.change_time = info2->change_time;
+ }
+ name->dos.flags = info2->flags;
+ break;
+
+ default:
+ DEBUG(0,("ERROR: Unsupported xattr DosAttrib version %d on '%s'\n",
+ attrib.version, name->full_name));
+ talloc_free(mem_ctx);
+ return NT_STATUS_INVALID_LEVEL;
+ }
+ talloc_free(mem_ctx);
+
+ status = pvfs_stream_info(pvfs, name, fd);
+
+ return status;
+}
+
+
+/*
+ save the file attribute into the xattr
+*/
+NTSTATUS pvfs_dosattrib_save(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd)
+{
+ struct xattr_DosAttrib attrib;
+ struct xattr_DosInfo1 *info1;
+
+ if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
+ return NT_STATUS_OK;
+ }
+
+ attrib.version = 1;
+ info1 = &attrib.info.info1;
+
+ name->dos.attrib = pvfs_attrib_normalise(name->dos.attrib, name->st.st_mode);
+
+ info1->attrib = name->dos.attrib;
+ info1->ea_size = name->dos.ea_size;
+ info1->size = name->st.st_size;
+ info1->alloc_size = name->dos.alloc_size;
+ info1->create_time = name->dos.create_time;
+ info1->change_time = name->dos.change_time;
+
+ return pvfs_xattr_ndr_save(pvfs, name->full_name, fd,
+ XATTR_DOSATTRIB_NAME, &attrib,
+ (ndr_push_flags_fn_t)ndr_push_xattr_DosAttrib);
+}
+
+
+/*
+ load the set of DOS EAs
+*/
+NTSTATUS pvfs_doseas_load(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
+ struct xattr_DosEAs *eas)
+{
+ NTSTATUS status;
+ ZERO_STRUCTP(eas);
+ if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
+ return NT_STATUS_OK;
+ }
+ status = pvfs_xattr_ndr_load(pvfs, eas, name->full_name, fd, XATTR_DOSEAS_NAME,
+ eas, (ndr_pull_flags_fn_t)ndr_pull_xattr_DosEAs);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ return NT_STATUS_OK;
+ }
+ return status;
+}
+
+/*
+ save the set of DOS EAs
+*/
+NTSTATUS pvfs_doseas_save(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
+ struct xattr_DosEAs *eas)
+{
+ if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
+ return NT_STATUS_OK;
+ }
+ return pvfs_xattr_ndr_save(pvfs, name->full_name, fd, XATTR_DOSEAS_NAME, eas,
+ (ndr_push_flags_fn_t)ndr_push_xattr_DosEAs);
+}
+
+
+/*
+ load the set of streams from extended attributes
+*/
+NTSTATUS pvfs_streams_load(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
+ struct xattr_DosStreams *streams)
+{
+ NTSTATUS status;
+ ZERO_STRUCTP(streams);
+ if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
+ return NT_STATUS_OK;
+ }
+ status = pvfs_xattr_ndr_load(pvfs, streams, name->full_name, fd,
+ XATTR_DOSSTREAMS_NAME,
+ streams,
+ (ndr_pull_flags_fn_t)ndr_pull_xattr_DosStreams);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ return NT_STATUS_OK;
+ }
+ return status;
+}
+
+/*
+ save the set of streams into filesystem xattr
+*/
+NTSTATUS pvfs_streams_save(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
+ struct xattr_DosStreams *streams)
+{
+ if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
+ return NT_STATUS_OK;
+ }
+ return pvfs_xattr_ndr_save(pvfs, name->full_name, fd,
+ XATTR_DOSSTREAMS_NAME,
+ streams,
+ (ndr_push_flags_fn_t)ndr_push_xattr_DosStreams);
+}
+
+
+/*
+ load the current ACL from extended attributes
+*/
+NTSTATUS pvfs_acl_load(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
+ struct xattr_NTACL *acl)
+{
+ NTSTATUS status;
+ ZERO_STRUCTP(acl);
+ if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
+ return NT_STATUS_NOT_FOUND;
+ }
+ status = pvfs_xattr_ndr_load(pvfs, acl, name->full_name, fd,
+ XATTR_NTACL_NAME,
+ acl,
+ (ndr_pull_flags_fn_t)ndr_pull_xattr_NTACL);
+ return status;
+}
+
+/*
+ save the acl for a file into filesystem xattr
+*/
+NTSTATUS pvfs_acl_save(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
+ struct xattr_NTACL *acl)
+{
+ NTSTATUS status;
+ void *privs;
+
+ if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
+ return NT_STATUS_OK;
+ }
+
+ /* this xattr is in the "system" namespace, so we need
+ admin privileges to set it */
+ privs = root_privileges();
+ status = pvfs_xattr_ndr_save(pvfs, name->full_name, fd,
+ XATTR_NTACL_NAME,
+ acl,
+ (ndr_push_flags_fn_t)ndr_push_xattr_NTACL);
+ talloc_free(privs);
+ return status;
+}
+
+/*
+ create a zero length xattr with the given name
+*/
+NTSTATUS pvfs_xattr_create(struct pvfs_state *pvfs,
+ const char *fname, int fd,
+ const char *attr_prefix,
+ const char *attr_name)
+{
+ NTSTATUS status;
+ DATA_BLOB blob = data_blob(NULL, 0);
+ char *aname = talloc_asprintf(NULL, "%s%s", attr_prefix, attr_name);
+ if (aname == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ status = push_xattr_blob(pvfs, aname, fname, fd, &blob);
+ talloc_free(aname);
+ return status;
+}
+
+
+/*
+ delete a xattr with the given name
+*/
+NTSTATUS pvfs_xattr_delete(struct pvfs_state *pvfs,
+ const char *fname, int fd,
+ const char *attr_prefix,
+ const char *attr_name)
+{
+ NTSTATUS status;
+ char *aname = talloc_asprintf(NULL, "%s%s", attr_prefix, attr_name);
+ if (aname == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ status = delete_xattr(pvfs, aname, fname, fd);
+ talloc_free(aname);
+ return status;
+}
+
+/*
+ load a xattr with the given name
+*/
+NTSTATUS pvfs_xattr_load(struct pvfs_state *pvfs,
+ TALLOC_CTX *mem_ctx,
+ const char *fname, int fd,
+ const char *attr_prefix,
+ const char *attr_name,
+ size_t estimated_size,
+ DATA_BLOB *blob)
+{
+ NTSTATUS status;
+ char *aname = talloc_asprintf(mem_ctx, "%s%s", attr_prefix, attr_name);
+ if (aname == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ status = pull_xattr_blob(pvfs, mem_ctx, aname, fname, fd, estimated_size, blob);
+ talloc_free(aname);
+ return status;
+}
+
+/*
+ save a xattr with the given name
+*/
+NTSTATUS pvfs_xattr_save(struct pvfs_state *pvfs,
+ const char *fname, int fd,
+ const char *attr_prefix,
+ const char *attr_name,
+ const DATA_BLOB *blob)
+{
+ NTSTATUS status;
+ char *aname = talloc_asprintf(NULL, "%s%s", attr_prefix, attr_name);
+ if (aname == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ status = push_xattr_blob(pvfs, aname, fname, fd, blob);
+ talloc_free(aname);
+ return status;
+}
+
+
+/*
+ probe for system support for xattrs
+*/
+void pvfs_xattr_probe(struct pvfs_state *pvfs)
+{
+ TALLOC_CTX *tmp_ctx = talloc_new(pvfs);
+ DATA_BLOB blob;
+ pull_xattr_blob(pvfs, tmp_ctx, "user.XattrProbe", pvfs->base_directory,
+ -1, 1, &blob);
+ pull_xattr_blob(pvfs, tmp_ctx, "security.XattrProbe", pvfs->base_directory,
+ -1, 1, &blob);
+ talloc_free(tmp_ctx);
+}
diff --git a/source4/ntvfs/posix/vfs_posix.c b/source4/ntvfs/posix/vfs_posix.c
new file mode 100644
index 0000000000..29ef701dee
--- /dev/null
+++ b/source4/ntvfs/posix/vfs_posix.c
@@ -0,0 +1,391 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+/*
+ this implements most of the POSIX NTVFS backend
+ This is the default backend
+*/
+
+#include "includes.h"
+#include "vfs_posix.h"
+#include "librpc/gen_ndr/security.h"
+#include "../tdb/include/tdb.h"
+#include "tdb_wrap.h"
+#include "libcli/security/security.h"
+#include "lib/events/events.h"
+#include "param/param.h"
+
+/*
+ setup config options for a posix share
+*/
+static void pvfs_setup_options(struct pvfs_state *pvfs)
+{
+ struct share_config *scfg = pvfs->ntvfs->ctx->config;
+ const char *eadb;
+
+ if (share_bool_option(scfg, SHARE_MAP_HIDDEN, SHARE_MAP_HIDDEN_DEFAULT))
+ pvfs->flags |= PVFS_FLAG_MAP_HIDDEN;
+ if (share_bool_option(scfg, SHARE_MAP_ARCHIVE, SHARE_MAP_ARCHIVE_DEFAULT))
+ pvfs->flags |= PVFS_FLAG_MAP_ARCHIVE;
+ if (share_bool_option(scfg, SHARE_MAP_SYSTEM, SHARE_MAP_SYSTEM_DEFAULT))
+ pvfs->flags |= PVFS_FLAG_MAP_SYSTEM;
+ if (share_bool_option(scfg, SHARE_READONLY, SHARE_READONLY_DEFAULT))
+ pvfs->flags |= PVFS_FLAG_READONLY;
+ if (share_bool_option(scfg, SHARE_STRICT_SYNC, SHARE_STRICT_SYNC_DEFAULT))
+ pvfs->flags |= PVFS_FLAG_STRICT_SYNC;
+ if (share_bool_option(scfg, SHARE_STRICT_LOCKING, SHARE_STRICT_LOCKING_DEFAULT))
+ pvfs->flags |= PVFS_FLAG_STRICT_LOCKING;
+ if (share_bool_option(scfg, SHARE_CI_FILESYSTEM, SHARE_CI_FILESYSTEM_DEFAULT))
+ pvfs->flags |= PVFS_FLAG_CI_FILESYSTEM;
+ if (share_bool_option(scfg, PVFS_FAKE_OPLOCKS, PVFS_FAKE_OPLOCKS_DEFAULT))
+ pvfs->flags |= PVFS_FLAG_FAKE_OPLOCKS;
+ if (share_bool_option(scfg, PVFS_AIO, false))
+ pvfs->flags |= PVFS_FLAG_LINUX_AIO;
+
+ /* file perm options */
+ pvfs->options.create_mask = share_int_option(scfg,
+ SHARE_CREATE_MASK,
+ SHARE_CREATE_MASK_DEFAULT);
+ pvfs->options.dir_mask = share_int_option(scfg,
+ SHARE_DIR_MASK,
+ SHARE_DIR_MASK_DEFAULT);
+ pvfs->options.force_dir_mode = share_int_option(scfg,
+ SHARE_FORCE_DIR_MODE,
+ SHARE_FORCE_DIR_MODE_DEFAULT);
+ pvfs->options.force_create_mode = share_int_option(scfg,
+ SHARE_FORCE_CREATE_MODE,
+ SHARE_FORCE_CREATE_MODE_DEFAULT);
+ /* this must be a power of 2 */
+ pvfs->alloc_size_rounding = share_int_option(scfg,
+ PVFS_ALLOCATION_ROUNDING,
+ PVFS_ALLOCATION_ROUNDING_DEFAULT);
+
+ pvfs->search.inactivity_time = share_int_option(scfg,
+ PVFS_SEARCH_INACTIVITY,
+ PVFS_SEARCH_INACTIVITY_DEFAULT);
+
+#if HAVE_XATTR_SUPPORT
+ if (share_bool_option(scfg, PVFS_XATTR, PVFS_XATTR_DEFAULT))
+ pvfs->flags |= PVFS_FLAG_XATTR_ENABLE;
+#endif
+
+ pvfs->sharing_violation_delay = share_int_option(scfg,
+ PVFS_SHARE_DELAY,
+ PVFS_SHARE_DELAY_DEFAULT);
+
+ pvfs->oplock_break_timeout = share_int_option(scfg,
+ PVFS_OPLOCK_TIMEOUT,
+ PVFS_OPLOCK_TIMEOUT_DEFAULT);
+
+ pvfs->writetime_delay = share_int_option(scfg,
+ PVFS_WRITETIME_DELAY,
+ PVFS_WRITETIME_DELAY_DEFAULT);
+
+ pvfs->share_name = talloc_strdup(pvfs, scfg->name);
+
+ pvfs->fs_attribs =
+ FS_ATTR_CASE_SENSITIVE_SEARCH |
+ FS_ATTR_CASE_PRESERVED_NAMES |
+ FS_ATTR_UNICODE_ON_DISK |
+ FS_ATTR_SPARSE_FILES;
+
+ /* allow xattrs to be stored in a external tdb */
+ eadb = share_string_option(scfg, PVFS_EADB, NULL);
+ if (eadb != NULL) {
+ pvfs->ea_db = tdb_wrap_open(pvfs, eadb, 50000,
+ TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
+ if (pvfs->ea_db != NULL) {
+ pvfs->flags |= PVFS_FLAG_XATTR_ENABLE;
+ } else {
+ DEBUG(0,("Failed to open eadb '%s' - %s\n",
+ eadb, strerror(errno)));
+ pvfs->flags &= ~PVFS_FLAG_XATTR_ENABLE;
+ }
+ }
+
+ if (pvfs->flags & PVFS_FLAG_XATTR_ENABLE) {
+ pvfs->fs_attribs |= FS_ATTR_NAMED_STREAMS;
+ }
+ if (pvfs->flags & PVFS_FLAG_XATTR_ENABLE) {
+ pvfs->fs_attribs |= FS_ATTR_PERSISTANT_ACLS;
+ }
+
+ pvfs->sid_cache.creator_owner = dom_sid_parse_talloc(pvfs, SID_CREATOR_OWNER);
+ pvfs->sid_cache.creator_group = dom_sid_parse_talloc(pvfs, SID_CREATOR_GROUP);
+
+ /* check if the system really supports xattrs */
+ if (pvfs->flags & PVFS_FLAG_XATTR_ENABLE) {
+ pvfs_xattr_probe(pvfs);
+ }
+
+ /* enable an ACL backend */
+ pvfs->acl_ops = pvfs_acl_backend_byname(share_string_option(scfg, PVFS_ACL, "xattr"));
+}
+
+static int pvfs_state_destructor(struct pvfs_state *pvfs)
+{
+ struct pvfs_file *f, *fn;
+ struct pvfs_search_state *s, *sn;
+
+ /*
+ * make sure we cleanup files and searches before anything else
+ * because there destructors need to acess the pvfs_state struct
+ */
+ for (f=pvfs->files.list; f; f=fn) {
+ fn = f->next;
+ talloc_free(f);
+ }
+
+ for (s=pvfs->search.list; s; s=sn) {
+ sn = s->next;
+ talloc_free(s);
+ }
+
+ return 0;
+}
+
+/*
+ connect to a share - used when a tree_connect operation comes
+ in. For a disk based backend we needs to ensure that the base
+ directory exists (tho it doesn't need to be accessible by the user,
+ that comes later)
+*/
+static NTSTATUS pvfs_connect(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, const char *sharename)
+{
+ struct pvfs_state *pvfs;
+ struct stat st;
+ char *base_directory;
+ NTSTATUS status;
+
+ /*
+ * TODO: call this from ntvfs_posix_init()
+ * but currently we don't have a lp_ctx there
+ */
+ status = pvfs_acl_init(ntvfs->ctx->lp_ctx);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ pvfs = talloc_zero(ntvfs, struct pvfs_state);
+ NT_STATUS_HAVE_NO_MEMORY(pvfs);
+
+ /* for simplicity of path construction, remove any trailing slash now */
+ base_directory = talloc_strdup(pvfs, share_string_option(ntvfs->ctx->config, SHARE_PATH, ""));
+ NT_STATUS_HAVE_NO_MEMORY(base_directory);
+ if (strcmp(base_directory, "/") != 0) {
+ trim_string(base_directory, NULL, "/");
+ }
+
+ pvfs->ntvfs = ntvfs;
+ pvfs->base_directory = base_directory;
+
+ /* the directory must exist. Note that we deliberately don't
+ check that it is readable */
+ if (stat(pvfs->base_directory, &st) != 0 || !S_ISDIR(st.st_mode)) {
+ DEBUG(0,("pvfs_connect: '%s' is not a directory, when connecting to [%s]\n",
+ pvfs->base_directory, sharename));
+ return NT_STATUS_BAD_NETWORK_NAME;
+ }
+
+ ntvfs->ctx->fs_type = talloc_strdup(ntvfs->ctx, "NTFS");
+ NT_STATUS_HAVE_NO_MEMORY(ntvfs->ctx->fs_type);
+
+ ntvfs->ctx->dev_type = talloc_strdup(ntvfs->ctx, "A:");
+ NT_STATUS_HAVE_NO_MEMORY(ntvfs->ctx->dev_type);
+
+ ntvfs->private_data = pvfs;
+
+ pvfs->brl_context = brl_init(pvfs,
+ pvfs->ntvfs->ctx->server_id,
+ pvfs->ntvfs->ctx->lp_ctx,
+ pvfs->ntvfs->ctx->msg_ctx);
+ if (pvfs->brl_context == NULL) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ pvfs->odb_context = odb_init(pvfs, pvfs->ntvfs->ctx);
+ if (pvfs->odb_context == NULL) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ /* allow this to be NULL - we just disable change notify */
+ pvfs->notify_context = notify_init(pvfs,
+ pvfs->ntvfs->ctx->server_id,
+ pvfs->ntvfs->ctx->msg_ctx,
+ pvfs->ntvfs->ctx->lp_ctx,
+ pvfs->ntvfs->ctx->event_ctx,
+ pvfs->ntvfs->ctx->config);
+
+ pvfs->wbc_ctx = wbc_init(pvfs,
+ pvfs->ntvfs->ctx->msg_ctx,
+ pvfs->ntvfs->ctx->event_ctx);
+ if (pvfs->wbc_ctx == NULL) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ /* allocate the search handle -> ptr tree */
+ pvfs->search.idtree = idr_init(pvfs);
+ NT_STATUS_HAVE_NO_MEMORY(pvfs->search.idtree);
+
+ status = pvfs_mangle_init(pvfs);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ pvfs_setup_options(pvfs);
+
+ talloc_set_destructor(pvfs, pvfs_state_destructor);
+
+#ifdef SIGXFSZ
+ /* who had the stupid idea to generate a signal on a large
+ file write instead of just failing it!? */
+ BlockSignals(true, SIGXFSZ);
+#endif
+
+ return NT_STATUS_OK;
+}
+
+/*
+ disconnect from a share
+*/
+static NTSTATUS pvfs_disconnect(struct ntvfs_module_context *ntvfs)
+{
+ return NT_STATUS_OK;
+}
+
+/*
+ check if a directory exists
+*/
+static NTSTATUS pvfs_chkpath(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_chkpath *cp)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ struct pvfs_filename *name;
+ NTSTATUS status;
+
+ /* resolve the cifs name to a posix name */
+ status = pvfs_resolve_name(pvfs, req, cp->chkpath.in.path, 0, &name);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ if (!name->exists) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ if (!S_ISDIR(name->st.st_mode)) {
+ return NT_STATUS_NOT_A_DIRECTORY;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ copy a set of files
+*/
+static NTSTATUS pvfs_copy(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, struct smb_copy *cp)
+{
+ DEBUG(0,("pvfs_copy not implemented\n"));
+ return NT_STATUS_NOT_SUPPORTED;
+}
+
+/*
+ return print queue info
+*/
+static NTSTATUS pvfs_lpq(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_lpq *lpq)
+{
+ return NT_STATUS_NOT_SUPPORTED;
+}
+
+/* SMBtrans - not used on file shares */
+static NTSTATUS pvfs_trans(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, struct smb_trans2 *trans2)
+{
+ return NT_STATUS_ACCESS_DENIED;
+}
+
+/*
+ initialialise the POSIX disk backend, registering ourselves with the ntvfs subsystem
+ */
+NTSTATUS ntvfs_posix_init(void)
+{
+ NTSTATUS ret;
+ struct ntvfs_ops ops;
+ NTVFS_CURRENT_CRITICAL_SIZES(vers);
+
+ ZERO_STRUCT(ops);
+
+ ops.type = NTVFS_DISK;
+
+ /* fill in all the operations */
+ ops.connect = pvfs_connect;
+ ops.disconnect = pvfs_disconnect;
+ ops.unlink = pvfs_unlink;
+ ops.chkpath = pvfs_chkpath;
+ ops.qpathinfo = pvfs_qpathinfo;
+ ops.setpathinfo = pvfs_setpathinfo;
+ ops.open = pvfs_open;
+ ops.mkdir = pvfs_mkdir;
+ ops.rmdir = pvfs_rmdir;
+ ops.rename = pvfs_rename;
+ ops.copy = pvfs_copy;
+ ops.ioctl = pvfs_ioctl;
+ ops.read = pvfs_read;
+ ops.write = pvfs_write;
+ ops.seek = pvfs_seek;
+ ops.flush = pvfs_flush;
+ ops.close = pvfs_close;
+ ops.exit = pvfs_exit;
+ ops.lock = pvfs_lock;
+ ops.setfileinfo = pvfs_setfileinfo;
+ ops.qfileinfo = pvfs_qfileinfo;
+ ops.fsinfo = pvfs_fsinfo;
+ ops.lpq = pvfs_lpq;
+ ops.search_first = pvfs_search_first;
+ ops.search_next = pvfs_search_next;
+ ops.search_close = pvfs_search_close;
+ ops.trans = pvfs_trans;
+ ops.logoff = pvfs_logoff;
+ ops.async_setup = pvfs_async_setup;
+ ops.cancel = pvfs_cancel;
+ ops.notify = pvfs_notify;
+
+ /* register ourselves with the NTVFS subsystem. We register
+ under the name 'default' as we wish to be the default
+ backend, and also register as 'posix' */
+ ops.name = "default";
+ ret = ntvfs_register(&ops, &vers);
+
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("Failed to register POSIX backend as '%s'!\n", ops.name));
+ }
+
+ ops.name = "posix";
+ ret = ntvfs_register(&ops, &vers);
+
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("Failed to register POSIX backend as '%s'!\n", ops.name));
+ }
+
+ if (NT_STATUS_IS_OK(ret)) {
+ ret = ntvfs_common_init();
+ }
+
+ return ret;
+}
diff --git a/source4/ntvfs/posix/vfs_posix.h b/source4/ntvfs/posix/vfs_posix.h
new file mode 100644
index 0000000000..b032ab3f93
--- /dev/null
+++ b/source4/ntvfs/posix/vfs_posix.h
@@ -0,0 +1,293 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - structure definitions
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _VFS_POSIX_H_
+#define _VFS_POSIX_H_
+
+#include "librpc/gen_ndr/xattr.h"
+#include "system/filesys.h"
+#include "ntvfs/ntvfs.h"
+#include "ntvfs/common/ntvfs_common.h"
+#include "libcli/wbclient/wbclient.h"
+#include "lib/events/events.h"
+
+struct pvfs_wait;
+struct pvfs_oplock;
+
+/* this is the private structure for the posix vfs backend. It is used
+ to hold per-connection (per tree connect) state information */
+struct pvfs_state {
+ struct ntvfs_module_context *ntvfs;
+ const char *base_directory;
+ struct GUID *base_fs_uuid;
+
+ const char *share_name;
+ uint_t flags;
+
+ struct pvfs_mangle_context *mangle_ctx;
+
+ struct brl_context *brl_context;
+ struct odb_context *odb_context;
+ struct notify_context *notify_context;
+ struct wbc_context *wbc_ctx;
+
+ /* a list of pending async requests. Needed to support
+ ntcancel */
+ struct pvfs_wait *wait_list;
+
+ /* the sharing violation timeout (nsecs) */
+ uint_t sharing_violation_delay;
+
+ /* the oplock break timeout (secs) */
+ uint_t oplock_break_timeout;
+
+ /* the write time update delay (nsecs) */
+ uint_t writetime_delay;
+
+ /* filesystem attributes (see FS_ATTR_*) */
+ uint32_t fs_attribs;
+
+ /* if posix:eadb is set, then this gets setup */
+ struct tdb_wrap *ea_db;
+
+ /* the allocation size rounding */
+ uint32_t alloc_size_rounding;
+
+ struct {
+ /* the open files as DLINKLIST */
+ struct pvfs_file *list;
+ } files;
+
+ struct {
+ /* an id tree mapping open search ID to a pvfs_search_state structure */
+ struct idr_context *idtree;
+
+ /* the open searches as DLINKLIST */
+ struct pvfs_search_state *list;
+
+ /* how long to keep inactive searches around for */
+ uint_t inactivity_time;
+ } search;
+
+ /* used to accelerate acl mapping */
+ struct {
+ const struct dom_sid *creator_owner;
+ const struct dom_sid *creator_group;
+ } sid_cache;
+
+ /* the acl backend */
+ const struct pvfs_acl_ops *acl_ops;
+
+ /* non-flag share options */
+ struct {
+ mode_t dir_mask;
+ mode_t force_dir_mode;
+ mode_t create_mask;
+ mode_t force_create_mode;
+ } options;
+};
+
+/* this is the basic information needed about a file from the filesystem */
+struct pvfs_dos_fileinfo {
+ NTTIME create_time;
+ NTTIME access_time;
+ NTTIME write_time;
+ NTTIME change_time;
+ uint32_t attrib;
+ uint64_t alloc_size;
+ uint32_t nlink;
+ uint32_t ea_size;
+ uint64_t file_id;
+ uint32_t flags;
+};
+
+/*
+ this is the structure returned by pvfs_resolve_name(). It holds the posix details of
+ a filename passed by the client to any function
+*/
+struct pvfs_filename {
+ const char *original_name;
+ char *full_name;
+ const char *stream_name; /* does not include :$DATA suffix */
+ uint32_t stream_id; /* this uses a hash, so is probabilistic */
+ bool has_wildcard;
+ bool exists; /* true if the base filename exists */
+ bool stream_exists; /* true if the stream exists */
+ struct stat st;
+ struct pvfs_dos_fileinfo dos;
+};
+
+
+/* open file handle state - encapsulates the posix fd
+
+ Note that this is separated from the pvfs_file structure in order
+ to cope with the openx DENY_DOS semantics where a 2nd DENY_DOS open
+ on the same connection gets the same low level filesystem handle,
+ rather than a new handle
+*/
+struct pvfs_file_handle {
+ int fd;
+
+ struct pvfs_filename *name;
+
+ /* a unique file key to be used for open file locking */
+ DATA_BLOB odb_locking_key;
+
+ uint32_t create_options;
+
+ /* this is set by the mode_information level. What does it do? */
+ uint32_t mode;
+
+ /* yes, we need 2 independent positions ... */
+ uint64_t seek_offset;
+ uint64_t position;
+
+ bool have_opendb_entry;
+
+ /*
+ * we need to wait for oplock break requests from other processes,
+ * and we need to remember the pvfs_file so we can correctly
+ * forward the oplock break to the client
+ */
+ struct pvfs_oplock *oplock;
+
+ /* we need this hook back to our parent for lock destruction */
+ struct pvfs_state *pvfs;
+
+ struct {
+ bool update_triggered;
+ struct tevent_timer *update_event;
+ bool update_on_close;
+ NTTIME close_time;
+ bool update_forced;
+ } write_time;
+
+ /* the open went through to completion */
+ bool open_completed;
+};
+
+/* open file state */
+struct pvfs_file {
+ struct pvfs_file *next, *prev;
+ struct pvfs_file_handle *handle;
+ struct ntvfs_handle *ntvfs;
+
+ struct pvfs_state *pvfs;
+
+ uint32_t impersonation;
+ uint32_t share_access;
+ uint32_t access_mask;
+
+ /* a list of pending locks - used for locking cancel operations */
+ struct pvfs_pending_lock *pending_list;
+
+ /* a file handle to be used for byte range locking */
+ struct brl_handle *brl_handle;
+
+ /* a count of active locks - used to avoid calling brl_close on
+ file close */
+ uint64_t lock_count;
+
+ /* for directories, a buffer of pending notify events */
+ struct pvfs_notify_buffer *notify_buffer;
+
+ /* for directories, the state of an incomplete SMB2 Find */
+ struct pvfs_search_state *search;
+};
+
+/* the state of a search started with pvfs_search_first() */
+struct pvfs_search_state {
+ struct pvfs_search_state *prev, *next;
+ struct pvfs_state *pvfs;
+ uint16_t handle;
+ off_t current_index;
+ uint16_t search_attrib;
+ uint16_t must_attrib;
+ struct pvfs_dir *dir;
+ time_t last_used;
+ uint_t num_ea_names;
+ struct ea_name *ea_names;
+ struct tevent_timer *te;
+};
+
+/* flags to pvfs_resolve_name() */
+#define PVFS_RESOLVE_WILDCARD (1<<0)
+#define PVFS_RESOLVE_STREAMS (1<<1)
+#define PVFS_RESOLVE_NO_OPENDB (1<<2)
+
+/* flags in pvfs->flags */
+#define PVFS_FLAG_CI_FILESYSTEM (1<<0) /* the filesystem is case insensitive */
+#define PVFS_FLAG_MAP_ARCHIVE (1<<1)
+#define PVFS_FLAG_MAP_SYSTEM (1<<2)
+#define PVFS_FLAG_MAP_HIDDEN (1<<3)
+#define PVFS_FLAG_READONLY (1<<4)
+#define PVFS_FLAG_STRICT_SYNC (1<<5)
+#define PVFS_FLAG_STRICT_LOCKING (1<<6)
+#define PVFS_FLAG_XATTR_ENABLE (1<<7)
+#define PVFS_FLAG_FAKE_OPLOCKS (1<<8)
+#define PVFS_FLAG_LINUX_AIO (1<<9)
+
+/* forward declare some anonymous structures */
+struct pvfs_dir;
+
+/* types of notification for pvfs wait events */
+enum pvfs_wait_notice {PVFS_WAIT_EVENT, PVFS_WAIT_TIMEOUT, PVFS_WAIT_CANCEL};
+
+/*
+ state of a pending retry
+*/
+struct pvfs_odb_retry;
+
+#define PVFS_EADB "posix:eadb"
+#define PVFS_XATTR "posix:xattr"
+#define PVFS_FAKE_OPLOCKS "posix:fakeoplocks"
+#define PVFS_SHARE_DELAY "posix:sharedelay"
+#define PVFS_OPLOCK_TIMEOUT "posix:oplocktimeout"
+#define PVFS_WRITETIME_DELAY "posix:writetimeupdatedelay"
+#define PVFS_ALLOCATION_ROUNDING "posix:allocationrounding"
+#define PVFS_SEARCH_INACTIVITY "posix:searchinactivity"
+#define PVFS_ACL "posix:acl"
+#define PVFS_AIO "posix:aio"
+
+#define PVFS_XATTR_DEFAULT true
+#define PVFS_FAKE_OPLOCKS_DEFAULT false
+#define PVFS_SHARE_DELAY_DEFAULT 1000000 /* nsecs */
+#define PVFS_OPLOCK_TIMEOUT_DEFAULT 30 /* secs */
+#define PVFS_WRITETIME_DELAY_DEFAULT 2000000 /* nsecs */
+#define PVFS_ALLOCATION_ROUNDING_DEFAULT 512
+#define PVFS_SEARCH_INACTIVITY_DEFAULT 300
+
+struct pvfs_acl_ops {
+ const char *name;
+ NTSTATUS (*acl_load)(struct pvfs_state *, struct pvfs_filename *, int , TALLOC_CTX *,
+ struct security_descriptor **);
+ NTSTATUS (*acl_save)(struct pvfs_state *, struct pvfs_filename *, int , struct security_descriptor *);
+};
+
+#include "ntvfs/posix/vfs_posix_proto.h"
+#include "ntvfs/posix/vfs_acl_proto.h"
+
+NTSTATUS pvfs_aio_pread(struct ntvfs_request *req, union smb_read *rd,
+ struct pvfs_file *f, uint32_t maxcnt);
+NTSTATUS pvfs_aio_pwrite(struct ntvfs_request *req, union smb_write *wr,
+ struct pvfs_file *f);
+
+#endif /* _VFS_POSIX_H_ */
diff --git a/source4/ntvfs/posix/xattr_system.c b/source4/ntvfs/posix/xattr_system.c
new file mode 100644
index 0000000000..f22c0e9ea4
--- /dev/null
+++ b/source4/ntvfs/posix/xattr_system.c
@@ -0,0 +1,146 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - xattr support using filesystem xattrs
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "vfs_posix.h"
+#include "../lib/util/wrap_xattr.h"
+
+/*
+ pull a xattr as a blob, from either a file or a file descriptor
+*/
+NTSTATUS pull_xattr_blob_system(struct pvfs_state *pvfs,
+ TALLOC_CTX *mem_ctx,
+ const char *attr_name,
+ const char *fname,
+ int fd,
+ size_t estimated_size,
+ DATA_BLOB *blob)
+{
+ int ret;
+
+ *blob = data_blob_talloc(mem_ctx, NULL, estimated_size+16);
+ if (blob->data == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+again:
+ if (fd != -1) {
+ ret = wrap_fgetxattr(fd, attr_name, blob->data, estimated_size);
+ } else {
+ ret = wrap_getxattr(fname, attr_name, blob->data, estimated_size);
+ }
+ if (ret == -1 && errno == ERANGE) {
+ estimated_size *= 2;
+ blob->data = talloc_realloc(mem_ctx, blob->data,
+ uint8_t, estimated_size);
+ if (blob->data == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ blob->length = estimated_size;
+ goto again;
+ }
+ if (ret == -1 && errno == EPERM) {
+ struct stat statbuf;
+
+ if (fd != -1) {
+ ret = fstat(fd, &statbuf);
+ } else {
+ ret = stat(fname, &statbuf);
+ }
+ if (ret == 0) {
+ /* check if this is a directory and the sticky bit is set */
+ if (S_ISDIR(statbuf.st_mode) && (statbuf.st_mode & S_ISVTX)) {
+ /* pretend we could not find the xattr */
+
+ data_blob_free(blob);
+ return NT_STATUS_NOT_FOUND;
+
+ } else {
+ /* if not this was probably a legitimate error
+ * reset ret and errno to the correct values */
+ errno = EPERM;
+ ret = -1;
+ }
+ }
+ }
+
+ if (ret == -1) {
+ data_blob_free(blob);
+ return pvfs_map_errno(pvfs, errno);
+ }
+
+ blob->length = ret;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ push a xattr as a blob, from either a file or a file descriptor
+*/
+NTSTATUS push_xattr_blob_system(struct pvfs_state *pvfs,
+ const char *attr_name,
+ const char *fname,
+ int fd,
+ const DATA_BLOB *blob)
+{
+ int ret;
+
+ if (fd != -1) {
+ ret = wrap_fsetxattr(fd, attr_name, blob->data, blob->length, 0);
+ } else {
+ ret = wrap_setxattr(fname, attr_name, blob->data, blob->length, 0);
+ }
+ if (ret == -1) {
+ return pvfs_map_errno(pvfs, errno);
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ delete a xattr
+*/
+NTSTATUS delete_xattr_system(struct pvfs_state *pvfs, const char *attr_name,
+ const char *fname, int fd)
+{
+ int ret;
+
+ if (fd != -1) {
+ ret = wrap_fremovexattr(fd, attr_name);
+ } else {
+ ret = wrap_removexattr(fname, attr_name);
+ }
+ if (ret == -1) {
+ return pvfs_map_errno(pvfs, errno);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ unlink a file - cleanup any xattrs
+*/
+NTSTATUS unlink_xattr_system(struct pvfs_state *pvfs, const char *fname)
+{
+ /* nothing needs to be done for filesystem based xattrs */
+ return NT_STATUS_OK;
+}
diff --git a/source4/ntvfs/posix/xattr_tdb.c b/source4/ntvfs/posix/xattr_tdb.c
new file mode 100644
index 0000000000..aa13fee4c8
--- /dev/null
+++ b/source4/ntvfs/posix/xattr_tdb.c
@@ -0,0 +1,234 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - xattr support using a tdb
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "vfs_posix.h"
+#include "../tdb/include/tdb.h"
+#include "tdb_wrap.h"
+
+#define XATTR_LIST_ATTR ".xattr_list"
+
+/*
+ we need to maintain a list of attributes on each file, so that unlink
+ can automatically clean them up
+*/
+static NTSTATUS xattr_tdb_add_list(struct pvfs_state *pvfs, const char *attr_name,
+ const char *fname, int fd)
+{
+ DATA_BLOB blob;
+ TALLOC_CTX *mem_ctx;
+ const char *s;
+ NTSTATUS status;
+ size_t len;
+
+ if (strcmp(attr_name, XATTR_LIST_ATTR) == 0) {
+ return NT_STATUS_OK;
+ }
+
+ mem_ctx = talloc_new(pvfs);
+
+ status = pull_xattr_blob_tdb(pvfs, mem_ctx, XATTR_LIST_ATTR,
+ fname, fd, 100, &blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ blob = data_blob(NULL, 0);
+ }
+
+ for (s=(const char *)blob.data; s < (const char *)(blob.data+blob.length); s += strlen(s) + 1) {
+ if (strcmp(attr_name, s) == 0) {
+ talloc_free(mem_ctx);
+ return NT_STATUS_OK;
+ }
+ }
+
+ len = strlen(attr_name) + 1;
+
+ blob.data = talloc_realloc(mem_ctx, blob.data, uint8_t, blob.length + len);
+ if (blob.data == NULL) {
+ talloc_free(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ memcpy(blob.data + blob.length, attr_name, len);
+ blob.length += len;
+
+ status = push_xattr_blob_tdb(pvfs, XATTR_LIST_ATTR, fname, fd, &blob);
+ talloc_free(mem_ctx);
+
+ return status;
+}
+
+/*
+ form a key for using in the ea_db
+*/
+static NTSTATUS get_ea_db_key(TALLOC_CTX *mem_ctx,
+ const char *attr_name,
+ const char *fname, int fd,
+ TDB_DATA *key)
+{
+ struct stat st;
+ size_t len = strlen(attr_name);
+
+ if (fd == -1) {
+ if (stat(fname, &st) == -1) {
+ return NT_STATUS_NOT_FOUND;
+ }
+ } else {
+ if (fstat(fd, &st) == -1) {
+ return NT_STATUS_NOT_FOUND;
+ }
+ }
+
+ key->dptr = talloc_array(mem_ctx, uint8_t, 16 + len);
+ if (key->dptr == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ key->dsize = 16 + len;
+
+ SBVAL(key->dptr, 0, st.st_dev);
+ SBVAL(key->dptr, 8, st.st_ino);
+ memcpy(key->dptr+16, attr_name, len);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ pull a xattr as a blob, using the ea_db tdb
+*/
+NTSTATUS pull_xattr_blob_tdb(struct pvfs_state *pvfs,
+ TALLOC_CTX *mem_ctx,
+ const char *attr_name,
+ const char *fname,
+ int fd,
+ size_t estimated_size,
+ DATA_BLOB *blob)
+{
+ TDB_DATA tkey, tdata;
+ NTSTATUS status;
+
+ status = get_ea_db_key(mem_ctx, attr_name, fname, fd, &tkey);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ tdata = tdb_fetch(pvfs->ea_db->tdb, tkey);
+ if (tdata.dptr == NULL) {
+ return NT_STATUS_NOT_FOUND;
+ }
+
+ *blob = data_blob_talloc(mem_ctx, tdata.dptr, tdata.dsize);
+ free(tdata.dptr);
+ if (blob->data == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ push a xattr as a blob, using ea_db
+*/
+NTSTATUS push_xattr_blob_tdb(struct pvfs_state *pvfs,
+ const char *attr_name,
+ const char *fname,
+ int fd,
+ const DATA_BLOB *blob)
+{
+ TDB_DATA tkey, tdata;
+ NTSTATUS status;
+
+ status = get_ea_db_key(pvfs, attr_name, fname, fd, &tkey);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ tdata.dptr = blob->data;
+ tdata.dsize = blob->length;
+
+ if (tdb_chainlock(pvfs->ea_db->tdb, tkey) != 0) {
+ talloc_free(tkey.dptr);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ status = xattr_tdb_add_list(pvfs, attr_name, fname, fd);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ if (tdb_store(pvfs->ea_db->tdb, tkey, tdata, TDB_REPLACE) == -1) {
+ status = NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+done:
+ tdb_chainunlock(pvfs->ea_db->tdb, tkey);
+ talloc_free(tkey.dptr);
+ return status;
+}
+
+
+/*
+ delete a xattr
+*/
+NTSTATUS delete_xattr_tdb(struct pvfs_state *pvfs, const char *attr_name,
+ const char *fname, int fd)
+{
+ TDB_DATA tkey;
+ NTSTATUS status;
+
+ status = get_ea_db_key(NULL, attr_name, fname, fd, &tkey);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (tdb_delete(pvfs->ea_db->tdb, tkey) == -1) {
+ talloc_free(tkey.dptr);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ talloc_free(tkey.dptr);
+ return NT_STATUS_OK;
+}
+
+
+
+/*
+ delete all xattrs for a file
+*/
+NTSTATUS unlink_xattr_tdb(struct pvfs_state *pvfs, const char *fname)
+{
+ TALLOC_CTX *mem_ctx = talloc_new(pvfs);
+ DATA_BLOB blob;
+ const char *s;
+ NTSTATUS status;
+
+ status = pull_xattr_blob_tdb(pvfs, mem_ctx, XATTR_LIST_ATTR,
+ fname, -1, 100, &blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(mem_ctx);
+ return NT_STATUS_OK;
+ }
+
+ for (s=(const char *)blob.data; s < (const char *)(blob.data+blob.length); s += strlen(s) + 1) {
+ delete_xattr_tdb(pvfs, s, fname, -1);
+ }
+
+ status = delete_xattr_tdb(pvfs, XATTR_LIST_ATTR, fname, -1);
+ talloc_free(mem_ctx);
+ return status;
+}
diff --git a/source4/ntvfs/print/README b/source4/ntvfs/print/README
new file mode 100644
index 0000000000..441c82dddd
--- /dev/null
+++ b/source4/ntvfs/print/README
@@ -0,0 +1,3 @@
+This is the print NTVFS backend for Samba. NTVFS operations that are
+made on print shares are directed here by default. Most directory
+operations and many file operations are not supported on print shares.
diff --git a/source4/ntvfs/print/vfs_print.c b/source4/ntvfs/print/vfs_print.c
new file mode 100644
index 0000000000..540e51a43b
--- /dev/null
+++ b/source4/ntvfs/print/vfs_print.c
@@ -0,0 +1,125 @@
+/*
+ Unix SMB/CIFS implementation.
+ default print NTVFS backend
+ 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 <http://www.gnu.org/licenses/>.
+*/
+/*
+ this implements the print backend, called by the NTVFS subsystem to
+ handle requests on printing shares
+*/
+
+#include "includes.h"
+#include "libcli/raw/ioctl.h"
+#include "ntvfs/ntvfs.h"
+#include "param/param.h"
+
+/*
+ connect to a share - used when a tree_connect operation comes
+ in. For printing shares this should check that the spool directory
+ is available
+*/
+static NTSTATUS print_connect(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, const char *sharename)
+{
+ ntvfs->ctx->fs_type = talloc_strdup(ntvfs->ctx, "NTFS");
+ NT_STATUS_HAVE_NO_MEMORY(ntvfs->ctx->fs_type);
+
+ ntvfs->ctx->dev_type = talloc_strdup(ntvfs->ctx, "LPT1:");
+ NT_STATUS_HAVE_NO_MEMORY(ntvfs->ctx->dev_type);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ disconnect from a share
+*/
+static NTSTATUS print_disconnect(struct ntvfs_module_context *ntvfs)
+{
+ return NT_STATUS_OK;
+}
+
+/*
+ lots of operations are not allowed on printing shares - mostly return NT_STATUS_ACCESS_DENIED
+*/
+static NTSTATUS print_unlink(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_unlink *unl)
+{
+ return NT_STATUS_ACCESS_DENIED;
+}
+
+
+/*
+ ioctl - used for job query
+*/
+static NTSTATUS print_ioctl(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_ioctl *io)
+{
+ char *p;
+
+ if (io->generic.level != RAW_IOCTL_IOCTL) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (io->ioctl.in.request == IOCTL_QUERY_JOB_INFO) {
+
+ /* a request for the print job id of an open print job */
+ io->ioctl.out.blob = data_blob_talloc(req, NULL, 32);
+
+ data_blob_clear(&io->ioctl.out.blob);
+
+ p = (char *)io->ioctl.out.blob.data;
+ SSVAL(p,0, 1 /* REWRITE: fsp->rap_print_jobid */);
+ push_string(p+2, lp_netbios_name(ntvfs->ctx->lp_ctx), 15, STR_TERMINATE|STR_ASCII);
+ push_string(p+18, ntvfs->ctx->config->name, 13, STR_TERMINATE|STR_ASCII);
+ return NT_STATUS_OK;
+ }
+
+ return NT_STATUS_INVALID_PARAMETER;
+}
+
+
+/*
+ initialialise the print backend, registering ourselves with the ntvfs subsystem
+ */
+NTSTATUS ntvfs_print_init(void)
+{
+ NTSTATUS ret;
+ struct ntvfs_ops ops;
+ NTVFS_CURRENT_CRITICAL_SIZES(vers);
+
+ ZERO_STRUCT(ops);
+
+ /* fill in the name and type */
+ ops.name = "default";
+ ops.type = NTVFS_PRINT;
+
+ /* fill in all the operations */
+ ops.connect = print_connect;
+ ops.disconnect = print_disconnect;
+ ops.unlink = print_unlink;
+ ops.ioctl = print_ioctl;
+
+ /* register ourselves with the NTVFS subsystem. We register under the name 'default'
+ as we wish to be the default backend */
+ ret = ntvfs_register(&ops, &vers);
+
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("Failed to register PRINT backend!\n"));
+ }
+
+ return ret;
+}
diff --git a/source4/ntvfs/simple/README b/source4/ntvfs/simple/README
new file mode 100644
index 0000000000..f1de5d9f80
--- /dev/null
+++ b/source4/ntvfs/simple/README
@@ -0,0 +1,10 @@
+This module (ntvfs 'simple') provides a very, very simple posix backend.
+
+WARNING: All file access is done as user 'root'!!!
+ Only use this module for testing, with only test data!!!
+
+For activating this module use:
+
+[testshare]
+ path = /tmp/testshare
+ nfvfs handler = simple
diff --git a/source4/ntvfs/simple/svfs.h b/source4/ntvfs/simple/svfs.h
new file mode 100644
index 0000000000..e5ad3b95d9
--- /dev/null
+++ b/source4/ntvfs/simple/svfs.h
@@ -0,0 +1,38 @@
+
+struct svfs_private {
+ struct ntvfs_module_context *ntvfs;
+
+ /* the base directory */
+ char *connectpath;
+
+ /* a linked list of open searches */
+ struct search_state *search;
+
+ /* next available search handle */
+ uint16_t next_search_handle;
+
+ struct svfs_file *open_files;
+};
+
+struct svfs_dir {
+ unsigned int count;
+ char *unix_dir;
+ struct svfs_dirfile {
+ char *name;
+ struct stat st;
+ } *files;
+};
+
+struct svfs_file {
+ struct svfs_file *next, *prev;
+ int fd;
+ struct ntvfs_handle *handle;
+ char *name;
+};
+
+struct search_state {
+ struct search_state *next, *prev;
+ uint16_t handle;
+ unsigned int current_index;
+ struct svfs_dir *dir;
+};
diff --git a/source4/ntvfs/simple/svfs_util.c b/source4/ntvfs/simple/svfs_util.c
new file mode 100644
index 0000000000..f7f011572a
--- /dev/null
+++ b/source4/ntvfs/simple/svfs_util.c
@@ -0,0 +1,185 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ simple NTVFS filesystem backend
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+/*
+ utility functions for simple backend
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "svfs.h"
+#include "system/time.h"
+#include "system/dir.h"
+#include "ntvfs/ntvfs.h"
+#include "ntvfs/simple/proto.h"
+
+/*
+ convert a windows path to a unix path - don't do any manging or case sensitive handling
+*/
+char *svfs_unix_path(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, const char *name)
+{
+ struct svfs_private *p = ntvfs->private_data;
+ char *ret;
+
+ if (*name != '\\') {
+ ret = talloc_asprintf(req, "%s/%s", p->connectpath, name);
+ } else {
+ ret = talloc_asprintf(req, "%s%s", p->connectpath, name);
+ }
+ all_string_sub(ret, "\\", "/", 0);
+
+ strlower(ret + strlen(p->connectpath));
+
+ return ret;
+}
+
+
+/*
+ read a directory and find all matching file names and stat info
+ returned names are separate unix and DOS names. The returned names
+ are relative to the directory
+*/
+struct svfs_dir *svfs_list_unix(TALLOC_CTX *mem_ctx, struct ntvfs_request *req, const char *unix_path)
+{
+ char *p, *mask;
+ struct svfs_dir *dir;
+ DIR *odir;
+ struct dirent *dent;
+ uint_t allocated = 0;
+ char *low_mask;
+
+ dir = talloc(mem_ctx, struct svfs_dir);
+ if (!dir) { return NULL; }
+
+ dir->count = 0;
+ dir->files = 0;
+
+ /* find the base directory */
+ p = strrchr(unix_path, '/');
+ if (!p) { return NULL; }
+
+ dir->unix_dir = talloc_strndup(mem_ctx, unix_path, PTR_DIFF(p, unix_path));
+ if (!dir->unix_dir) { return NULL; }
+
+ /* the wildcard pattern is the last part */
+ mask = p+1;
+
+ low_mask = talloc_strdup(mem_ctx, mask);
+ if (!low_mask) { return NULL; }
+ strlower(low_mask);
+
+ odir = opendir(dir->unix_dir);
+ if (!odir) { return NULL; }
+
+ while ((dent = readdir(odir))) {
+ uint_t i = dir->count;
+ char *full_name;
+ char *low_name;
+
+ if (strchr(dent->d_name, ':') && !strchr(unix_path, ':')) {
+ /* don't show streams in dir listing */
+ continue;
+ }
+
+ low_name = talloc_strdup(mem_ctx, dent->d_name);
+ if (!low_name) { continue; }
+ strlower(low_name);
+
+ /* check it matches the wildcard pattern */
+ if (ms_fnmatch(low_mask, low_name, PROTOCOL_NT1) != 0) {
+ continue;
+ }
+
+ if (dir->count >= allocated) {
+ allocated = (allocated + 100) * 1.2;
+ dir->files = talloc_realloc(dir, dir->files, struct svfs_dirfile, allocated);
+ if (!dir->files) {
+ closedir(odir);
+ return NULL;
+ }
+ }
+
+ dir->files[i].name = low_name;
+ if (!dir->files[i].name) { continue; }
+
+ asprintf(&full_name, "%s/%s", dir->unix_dir, dir->files[i].name);
+ if (!full_name) { continue; }
+
+ if (stat(full_name, &dir->files[i].st) == 0) {
+ dir->count++;
+ }
+
+ free(full_name);
+ }
+
+ closedir(odir);
+
+ return dir;
+}
+
+/*
+ read a directory and find all matching file names and stat info
+ returned names are separate unix and DOS names. The returned names
+ are relative to the directory
+*/
+struct svfs_dir *svfs_list(struct ntvfs_module_context *ntvfs, struct ntvfs_request *req, const char *pattern)
+{
+ struct svfs_private *p = ntvfs->private_data;
+ char *unix_path;
+
+ unix_path = svfs_unix_path(ntvfs, req, pattern);
+ if (!unix_path) { return NULL; }
+
+ return svfs_list_unix(p, req, unix_path);
+}
+
+
+/*******************************************************************
+set the time on a file via file descriptor
+*******************************************************************/
+int svfs_file_utime(int fd, struct utimbuf *times)
+{
+ char *fd_path = NULL;
+ int ret;
+
+ asprintf(&fd_path, "/proc/self/%d", fd);
+ if (!fd_path) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ ret = utime(fd_path, times);
+ free(fd_path);
+ return ret;
+}
+
+
+/*
+ map a unix file attrib to a DOS attribute
+*/
+uint16_t svfs_unix_to_dos_attrib(mode_t mode)
+{
+ uint16_t ret = 0;
+ if (S_ISDIR(mode)) ret |= FILE_ATTRIBUTE_DIRECTORY;
+ if (!(mode & S_IWUSR)) ret |= FILE_ATTRIBUTE_READONLY;
+ return ret;
+}
+
diff --git a/source4/ntvfs/simple/vfs_simple.c b/source4/ntvfs/simple/vfs_simple.c
new file mode 100644
index 0000000000..bf0afcec0a
--- /dev/null
+++ b/source4/ntvfs/simple/vfs_simple.c
@@ -0,0 +1,1078 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ simple NTVFS filesystem backend
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+/*
+ this implements a very simple NTVFS filesystem backend.
+
+ this backend largely ignores the POSIX -> CIFS mappings, just doing absolutely
+ minimal work to give a working backend.
+*/
+
+#include "includes.h"
+#include "system/dir.h"
+#include "system/filesys.h"
+#include "svfs.h"
+#include "system/time.h"
+#include "../lib/util/dlinklist.h"
+#include "ntvfs/ntvfs.h"
+#include "ntvfs/simple/proto.h"
+
+#ifndef O_DIRECTORY
+#define O_DIRECTORY 0
+#endif
+
+#define CHECK_READ_ONLY(req) do { if (share_bool_option(ntvfs->ctx->config, SHARE_READONLY, true)) return NT_STATUS_ACCESS_DENIED; } while (0)
+
+/*
+ connect to a share - used when a tree_connect operation comes
+ in. For a disk based backend we needs to ensure that the base
+ directory exists (tho it doesn't need to be accessible by the user,
+ that comes later)
+*/
+static NTSTATUS svfs_connect(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, const char *sharename)
+{
+ struct stat st;
+ struct svfs_private *p;
+ struct share_config *scfg = ntvfs->ctx->config;
+
+ p = talloc(ntvfs, struct svfs_private);
+ NT_STATUS_HAVE_NO_MEMORY(p);
+ p->ntvfs = ntvfs;
+ p->next_search_handle = 0;
+ p->connectpath = talloc_strdup(p, share_string_option(scfg, SHARE_PATH, ""));
+ p->open_files = NULL;
+ p->search = NULL;
+
+ /* the directory must exist */
+ if (stat(p->connectpath, &st) != 0 || !S_ISDIR(st.st_mode)) {
+ DEBUG(0,("'%s' is not a directory, when connecting to [%s]\n",
+ p->connectpath, sharename));
+ return NT_STATUS_BAD_NETWORK_NAME;
+ }
+
+ ntvfs->ctx->fs_type = talloc_strdup(ntvfs->ctx, "NTFS");
+ NT_STATUS_HAVE_NO_MEMORY(ntvfs->ctx->fs_type);
+ ntvfs->ctx->dev_type = talloc_strdup(ntvfs->ctx, "A:");
+ NT_STATUS_HAVE_NO_MEMORY(ntvfs->ctx->dev_type);
+
+ ntvfs->private_data = p;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ disconnect from a share
+*/
+static NTSTATUS svfs_disconnect(struct ntvfs_module_context *ntvfs)
+{
+ return NT_STATUS_OK;
+}
+
+/*
+ find open file handle given fd
+*/
+static struct svfs_file *find_fd(struct svfs_private *sp, struct ntvfs_handle *handle)
+{
+ struct svfs_file *f;
+ void *p;
+
+ p = ntvfs_handle_get_backend_data(handle, sp->ntvfs);
+ if (!p) return NULL;
+
+ f = talloc_get_type(p, struct svfs_file);
+ if (!f) return NULL;
+
+ return f;
+}
+
+/*
+ delete a file - the dirtype specifies the file types to include in the search.
+ The name can contain CIFS wildcards, but rarely does (except with OS/2 clients)
+*/
+static NTSTATUS svfs_unlink(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_unlink *unl)
+{
+ char *unix_path;
+
+ CHECK_READ_ONLY(req);
+
+ unix_path = svfs_unix_path(ntvfs, req, unl->unlink.in.pattern);
+
+ /* ignoring wildcards ... */
+ if (unlink(unix_path) == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ ioctl interface - we don't do any
+*/
+static NTSTATUS svfs_ioctl(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_ioctl *io)
+{
+ return NT_STATUS_INVALID_PARAMETER;
+}
+
+/*
+ check if a directory exists
+*/
+static NTSTATUS svfs_chkpath(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_chkpath *cp)
+{
+ char *unix_path;
+ struct stat st;
+
+ unix_path = svfs_unix_path(ntvfs, req, cp->chkpath.in.path);
+
+ if (stat(unix_path, &st) == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ if (!S_ISDIR(st.st_mode)) {
+ return NT_STATUS_NOT_A_DIRECTORY;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ build a file_id from a stat struct
+*/
+static uint64_t svfs_file_id(struct stat *st)
+{
+ uint64_t ret = st->st_ino;
+ ret <<= 32;
+ ret |= st->st_dev;
+ return ret;
+}
+
+/*
+ approximately map a struct stat to a generic fileinfo struct
+*/
+static NTSTATUS svfs_map_fileinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_fileinfo *info,
+ struct stat *st, const char *unix_path)
+{
+ struct svfs_dir *dir = NULL;
+ char *pattern = NULL;
+ int i;
+ const char *s, *short_name;
+
+ s = strrchr(unix_path, '/');
+ if (s) {
+ short_name = s+1;
+ } else {
+ short_name = "";
+ }
+
+ asprintf(&pattern, "%s:*", unix_path);
+
+ if (pattern) {
+ dir = svfs_list_unix(req, req, pattern);
+ }
+
+ unix_to_nt_time(&info->generic.out.create_time, st->st_ctime);
+ unix_to_nt_time(&info->generic.out.access_time, st->st_atime);
+ unix_to_nt_time(&info->generic.out.write_time, st->st_mtime);
+ unix_to_nt_time(&info->generic.out.change_time, st->st_mtime);
+ info->generic.out.alloc_size = st->st_size;
+ info->generic.out.size = st->st_size;
+ info->generic.out.attrib = svfs_unix_to_dos_attrib(st->st_mode);
+ info->generic.out.alloc_size = st->st_blksize * st->st_blocks;
+ info->generic.out.nlink = st->st_nlink;
+ info->generic.out.directory = S_ISDIR(st->st_mode) ? 1 : 0;
+ info->generic.out.file_id = svfs_file_id(st);
+ /* REWRITE: TODO stuff in here */
+ info->generic.out.delete_pending = 0;
+ info->generic.out.ea_size = 0;
+ info->generic.out.num_eas = 0;
+ info->generic.out.fname.s = talloc_strdup(req, short_name);
+ info->generic.out.alt_fname.s = talloc_strdup(req, short_name);
+ info->generic.out.compressed_size = 0;
+ info->generic.out.format = 0;
+ info->generic.out.unit_shift = 0;
+ info->generic.out.chunk_shift = 0;
+ info->generic.out.cluster_shift = 0;
+
+ info->generic.out.access_flags = 0;
+ info->generic.out.position = 0;
+ info->generic.out.mode = 0;
+ info->generic.out.alignment_requirement = 0;
+ info->generic.out.reparse_tag = 0;
+ info->generic.out.num_streams = 0;
+ /* setup a single data stream */
+ info->generic.out.num_streams = 1 + (dir?dir->count:0);
+ info->generic.out.streams = talloc_array(req,
+ struct stream_struct,
+ info->generic.out.num_streams);
+ if (!info->generic.out.streams) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ info->generic.out.streams[0].size = st->st_size;
+ info->generic.out.streams[0].alloc_size = st->st_size;
+ info->generic.out.streams[0].stream_name.s = talloc_strdup(req,"::$DATA");
+
+ for (i=0;dir && i<dir->count;i++) {
+ s = strchr(dir->files[i].name, ':');
+ info->generic.out.streams[1+i].size = dir->files[i].st.st_size;
+ info->generic.out.streams[1+i].alloc_size = dir->files[i].st.st_size;
+ info->generic.out.streams[1+i].stream_name.s = s?s:dir->files[i].name;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ return info on a pathname
+*/
+static NTSTATUS svfs_qpathinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_fileinfo *info)
+{
+ char *unix_path;
+ struct stat st;
+
+ DEBUG(19,("svfs_qpathinfo: file %s level 0x%x\n", info->generic.in.file.path, info->generic.level));
+ if (info->generic.level != RAW_FILEINFO_GENERIC) {
+ return ntvfs_map_qpathinfo(ntvfs, req, info);
+ }
+
+ unix_path = svfs_unix_path(ntvfs, req, info->generic.in.file.path);
+ DEBUG(19,("svfs_qpathinfo: file %s\n", unix_path));
+ if (stat(unix_path, &st) == -1) {
+ DEBUG(19,("svfs_qpathinfo: file %s errno=%d\n", unix_path, errno));
+ return map_nt_error_from_unix(errno);
+ }
+ DEBUG(19,("svfs_qpathinfo: file %s, stat done\n", unix_path));
+ return svfs_map_fileinfo(ntvfs, req, info, &st, unix_path);
+}
+
+/*
+ query info on a open file
+*/
+static NTSTATUS svfs_qfileinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_fileinfo *info)
+{
+ struct svfs_private *p = ntvfs->private_data;
+ struct svfs_file *f;
+ struct stat st;
+
+ if (info->generic.level != RAW_FILEINFO_GENERIC) {
+ return ntvfs_map_qfileinfo(ntvfs, req, info);
+ }
+
+ f = find_fd(p, info->generic.in.file.ntvfs);
+ if (!f) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ if (fstat(f->fd, &st) == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ return svfs_map_fileinfo(ntvfs, req,info, &st, f->name);
+}
+
+
+/*
+ open a file
+*/
+static NTSTATUS svfs_open(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_open *io)
+{
+ struct svfs_private *p = ntvfs->private_data;
+ char *unix_path;
+ struct stat st;
+ int fd, flags;
+ struct svfs_file *f;
+ int create_flags, rdwr_flags;
+ bool readonly;
+ NTSTATUS status;
+ struct ntvfs_handle *handle;
+
+ if (io->generic.level != RAW_OPEN_GENERIC) {
+ return ntvfs_map_open(ntvfs, req, io);
+ }
+
+ readonly = share_bool_option(ntvfs->ctx->config, SHARE_READONLY, SHARE_READONLY_DEFAULT);
+ if (readonly) {
+ create_flags = 0;
+ rdwr_flags = O_RDONLY;
+ } else {
+ create_flags = O_CREAT;
+ rdwr_flags = O_RDWR;
+ }
+
+ unix_path = svfs_unix_path(ntvfs, req, io->ntcreatex.in.fname);
+
+ switch (io->generic.in.open_disposition) {
+ case NTCREATEX_DISP_SUPERSEDE:
+ case NTCREATEX_DISP_OVERWRITE_IF:
+ flags = create_flags | O_TRUNC;
+ break;
+ case NTCREATEX_DISP_OPEN:
+ case NTCREATEX_DISP_OVERWRITE:
+ flags = 0;
+ break;
+ case NTCREATEX_DISP_CREATE:
+ flags = create_flags | O_EXCL;
+ break;
+ case NTCREATEX_DISP_OPEN_IF:
+ flags = create_flags;
+ break;
+ default:
+ flags = 0;
+ break;
+ }
+
+ flags |= rdwr_flags;
+
+ if (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY) {
+ flags = O_RDONLY | O_DIRECTORY;
+ if (readonly) {
+ goto do_open;
+ }
+ switch (io->generic.in.open_disposition) {
+ case NTCREATEX_DISP_CREATE:
+ if (mkdir(unix_path, 0755) == -1) {
+ DEBUG(9,("svfs_open: mkdir %s errno=%d\n", unix_path, errno));
+ return map_nt_error_from_unix(errno);
+ }
+ break;
+ case NTCREATEX_DISP_OPEN_IF:
+ if (mkdir(unix_path, 0755) == -1 && errno != EEXIST) {
+ DEBUG(9,("svfs_open: mkdir %s errno=%d\n", unix_path, errno));
+ return map_nt_error_from_unix(errno);
+ }
+ break;
+ }
+ }
+
+do_open:
+ fd = open(unix_path, flags, 0644);
+ if (fd == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ if (fstat(fd, &st) == -1) {
+ DEBUG(9,("svfs_open: fstat errno=%d\n", errno));
+ close(fd);
+ return map_nt_error_from_unix(errno);
+ }
+
+ status = ntvfs_handle_new(ntvfs, req, &handle);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ f = talloc(handle, struct svfs_file);
+ NT_STATUS_HAVE_NO_MEMORY(f);
+ f->fd = fd;
+ f->name = talloc_strdup(f, unix_path);
+ NT_STATUS_HAVE_NO_MEMORY(f->name);
+
+ DLIST_ADD(p->open_files, f);
+
+ status = ntvfs_handle_set_backend_data(handle, ntvfs, f);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ ZERO_STRUCT(io->generic.out);
+
+ unix_to_nt_time(&io->generic.out.create_time, st.st_ctime);
+ unix_to_nt_time(&io->generic.out.access_time, st.st_atime);
+ unix_to_nt_time(&io->generic.out.write_time, st.st_mtime);
+ unix_to_nt_time(&io->generic.out.change_time, st.st_mtime);
+ io->generic.out.file.ntvfs = handle;
+ io->generic.out.alloc_size = st.st_size;
+ io->generic.out.size = st.st_size;
+ io->generic.out.attrib = svfs_unix_to_dos_attrib(st.st_mode);
+ io->generic.out.is_directory = S_ISDIR(st.st_mode) ? 1 : 0;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ create a directory
+*/
+static NTSTATUS svfs_mkdir(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_mkdir *md)
+{
+ char *unix_path;
+
+ CHECK_READ_ONLY(req);
+
+ if (md->generic.level != RAW_MKDIR_MKDIR) {
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ unix_path = svfs_unix_path(ntvfs, req, md->mkdir.in.path);
+
+ if (mkdir(unix_path, 0777) == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ remove a directory
+*/
+static NTSTATUS svfs_rmdir(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, struct smb_rmdir *rd)
+{
+ char *unix_path;
+
+ CHECK_READ_ONLY(req);
+
+ unix_path = svfs_unix_path(ntvfs, req, rd->in.path);
+
+ if (rmdir(unix_path) == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ rename a set of files
+*/
+static NTSTATUS svfs_rename(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_rename *ren)
+{
+ char *unix_path1, *unix_path2;
+
+ CHECK_READ_ONLY(req);
+
+ if (ren->generic.level != RAW_RENAME_RENAME) {
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ unix_path1 = svfs_unix_path(ntvfs, req, ren->rename.in.pattern1);
+ unix_path2 = svfs_unix_path(ntvfs, req, ren->rename.in.pattern2);
+
+ if (rename(unix_path1, unix_path2) == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ copy a set of files
+*/
+static NTSTATUS svfs_copy(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, struct smb_copy *cp)
+{
+ return NT_STATUS_NOT_SUPPORTED;
+}
+
+/*
+ read from a file
+*/
+static NTSTATUS svfs_read(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_read *rd)
+{
+ struct svfs_private *p = ntvfs->private_data;
+ struct svfs_file *f;
+ ssize_t ret;
+
+ if (rd->generic.level != RAW_READ_READX) {
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ f = find_fd(p, rd->readx.in.file.ntvfs);
+ if (!f) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ ret = pread(f->fd,
+ rd->readx.out.data,
+ rd->readx.in.maxcnt,
+ rd->readx.in.offset);
+ if (ret == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ rd->readx.out.nread = ret;
+ rd->readx.out.remaining = 0; /* should fill this in? */
+ rd->readx.out.compaction_mode = 0;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ write to a file
+*/
+static NTSTATUS svfs_write(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_write *wr)
+{
+ struct svfs_private *p = ntvfs->private_data;
+ struct svfs_file *f;
+ ssize_t ret;
+
+ if (wr->generic.level != RAW_WRITE_WRITEX) {
+ return ntvfs_map_write(ntvfs, req, wr);
+ }
+
+ CHECK_READ_ONLY(req);
+
+ f = find_fd(p, wr->writex.in.file.ntvfs);
+ if (!f) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ ret = pwrite(f->fd,
+ wr->writex.in.data,
+ wr->writex.in.count,
+ wr->writex.in.offset);
+ if (ret == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ wr->writex.out.nwritten = ret;
+ wr->writex.out.remaining = 0; /* should fill this in? */
+
+ return NT_STATUS_OK;
+}
+
+/*
+ seek in a file
+*/
+static NTSTATUS svfs_seek(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_seek *io)
+{
+ return NT_STATUS_NOT_SUPPORTED;
+}
+
+/*
+ flush a file
+*/
+static NTSTATUS svfs_flush(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_flush *io)
+{
+ struct svfs_private *p = ntvfs->private_data;
+ struct svfs_file *f;
+
+ switch (io->generic.level) {
+ case RAW_FLUSH_FLUSH:
+ case RAW_FLUSH_SMB2:
+ /* ignore the additional unknown option in SMB2 */
+ f = find_fd(p, io->generic.in.file.ntvfs);
+ if (!f) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+ fsync(f->fd);
+ return NT_STATUS_OK;
+
+ case RAW_FLUSH_ALL:
+ for (f=p->open_files;f;f=f->next) {
+ fsync(f->fd);
+ }
+ return NT_STATUS_OK;
+ }
+
+ return NT_STATUS_INVALID_LEVEL;
+}
+
+/*
+ close a file
+*/
+static NTSTATUS svfs_close(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_close *io)
+{
+ struct svfs_private *p = ntvfs->private_data;
+ struct svfs_file *f;
+
+ if (io->generic.level != RAW_CLOSE_CLOSE) {
+ /* we need a mapping function */
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ f = find_fd(p, io->close.in.file.ntvfs);
+ if (!f) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ if (close(f->fd) == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ DLIST_REMOVE(p->open_files, f);
+ talloc_free(f->name);
+ talloc_free(f);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ exit - closing files
+*/
+static NTSTATUS svfs_exit(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req)
+{
+ return NT_STATUS_NOT_SUPPORTED;
+}
+
+/*
+ logoff - closing files
+*/
+static NTSTATUS svfs_logoff(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req)
+{
+ return NT_STATUS_NOT_SUPPORTED;
+}
+
+/*
+ setup for an async call
+*/
+static NTSTATUS svfs_async_setup(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ void *private_data)
+{
+ return NT_STATUS_OK;
+}
+
+/*
+ cancel an async call
+*/
+static NTSTATUS svfs_cancel(struct ntvfs_module_context *ntvfs, struct ntvfs_request *req)
+{
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+/*
+ lock a byte range
+*/
+static NTSTATUS svfs_lock(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_lock *lck)
+{
+ DEBUG(0,("REWRITE: not doing byte range locking!\n"));
+ return NT_STATUS_OK;
+}
+
+/*
+ set info on a pathname
+*/
+static NTSTATUS svfs_setpathinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_setfileinfo *st)
+{
+ CHECK_READ_ONLY(req);
+
+ return NT_STATUS_NOT_SUPPORTED;
+}
+
+/*
+ set info on a open file
+*/
+static NTSTATUS svfs_setfileinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_setfileinfo *info)
+{
+ struct svfs_private *p = ntvfs->private_data;
+ struct svfs_file *f;
+ struct utimbuf unix_times;
+
+ CHECK_READ_ONLY(req);
+
+ f = find_fd(p, info->generic.in.file.ntvfs);
+ if (!f) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ switch (info->generic.level) {
+ case RAW_SFILEINFO_END_OF_FILE_INFO:
+ case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
+ if (ftruncate(f->fd,
+ info->end_of_file_info.in.size) == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+ break;
+ case RAW_SFILEINFO_SETATTRE:
+ unix_times.actime = info->setattre.in.access_time;
+ unix_times.modtime = info->setattre.in.write_time;
+
+ if (unix_times.actime == 0 && unix_times.modtime == 0) {
+ break;
+ }
+
+ /* set modify time = to access time if modify time was 0 */
+ if (unix_times.actime != 0 && unix_times.modtime == 0) {
+ unix_times.modtime = unix_times.actime;
+ }
+
+ /* Set the date on this file */
+ if (svfs_file_utime(f->fd, &unix_times) != 0) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ break;
+ default:
+ DEBUG(2,("svfs_setfileinfo: level %d not implemented\n",
+ info->generic.level));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return NT_STATUS_OK;
+}
+
+
+/*
+ return filesystem space info
+*/
+static NTSTATUS svfs_fsinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_fsinfo *fs)
+{
+ struct svfs_private *p = ntvfs->private_data;
+ struct stat st;
+
+ if (fs->generic.level != RAW_QFS_GENERIC) {
+ return ntvfs_map_fsinfo(ntvfs, req, fs);
+ }
+
+ if (sys_fsusage(p->connectpath,
+ &fs->generic.out.blocks_free,
+ &fs->generic.out.blocks_total) == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ fs->generic.out.block_size = 512;
+
+ if (stat(p->connectpath, &st) != 0) {
+ return NT_STATUS_DISK_CORRUPT_ERROR;
+ }
+
+ fs->generic.out.fs_id = st.st_ino;
+ unix_to_nt_time(&fs->generic.out.create_time, st.st_ctime);
+ fs->generic.out.serial_number = st.st_ino;
+ fs->generic.out.fs_attr = 0;
+ fs->generic.out.max_file_component_length = 255;
+ fs->generic.out.device_type = 0;
+ fs->generic.out.device_characteristics = 0;
+ fs->generic.out.quota_soft = 0;
+ fs->generic.out.quota_hard = 0;
+ fs->generic.out.quota_flags = 0;
+ fs->generic.out.volume_name = talloc_strdup(req, ntvfs->ctx->config->name);
+ fs->generic.out.fs_type = ntvfs->ctx->fs_type;
+
+ return NT_STATUS_OK;
+}
+
+#if 0
+/*
+ return filesystem attribute info
+*/
+static NTSTATUS svfs_fsattr(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_fsattr *fs)
+{
+ struct stat st;
+ struct svfs_private *p = ntvfs->private_data;
+
+ if (fs->generic.level != RAW_FSATTR_GENERIC) {
+ return ntvfs_map_fsattr(ntvfs, req, fs);
+ }
+
+ if (stat(p->connectpath, &st) == -1) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ unix_to_nt_time(&fs->generic.out.create_time, st.st_ctime);
+ fs->generic.out.fs_attr =
+ FILE_CASE_PRESERVED_NAMES |
+ FILE_CASE_SENSITIVE_SEARCH |
+ FILE_PERSISTENT_ACLS;
+ fs->generic.out.max_file_component_length = 255;
+ fs->generic.out.serial_number = 1;
+ fs->generic.out.fs_type = talloc_strdup(req, "NTFS");
+ fs->generic.out.volume_name = talloc_strdup(req,
+ lp_servicename(req->tcon->service));
+
+ return NT_STATUS_OK;
+}
+#endif
+
+/*
+ return print queue info
+*/
+static NTSTATUS svfs_lpq(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_lpq *lpq)
+{
+ return NT_STATUS_NOT_SUPPORTED;
+}
+
+/*
+ list files in a directory matching a wildcard pattern
+*/
+static NTSTATUS svfs_search_first(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_search_first *io,
+ void *search_private,
+ bool (*callback)(void *, const union smb_search_data *))
+{
+ struct svfs_dir *dir;
+ int i;
+ struct svfs_private *p = ntvfs->private_data;
+ struct search_state *search;
+ union smb_search_data file;
+ uint_t max_count;
+
+ if (io->generic.level != RAW_SEARCH_TRANS2) {
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ if (io->generic.data_level != RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO) {
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ search = talloc_zero(p, struct search_state);
+ if (!search) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ max_count = io->t2ffirst.in.max_count;
+
+ dir = svfs_list(ntvfs, req, io->t2ffirst.in.pattern);
+ if (!dir) {
+ return NT_STATUS_FOOBAR;
+ }
+
+ search->handle = p->next_search_handle;
+ search->dir = dir;
+
+ if (dir->count < max_count) {
+ max_count = dir->count;
+ }
+
+ for (i=0; i < max_count;i++) {
+ ZERO_STRUCT(file);
+ unix_to_nt_time(&file.both_directory_info.create_time, dir->files[i].st.st_ctime);
+ unix_to_nt_time(&file.both_directory_info.access_time, dir->files[i].st.st_atime);
+ unix_to_nt_time(&file.both_directory_info.write_time, dir->files[i].st.st_mtime);
+ unix_to_nt_time(&file.both_directory_info.change_time, dir->files[i].st.st_mtime);
+ file.both_directory_info.name.s = dir->files[i].name;
+ file.both_directory_info.short_name.s = dir->files[i].name;
+ file.both_directory_info.size = dir->files[i].st.st_size;
+ file.both_directory_info.attrib = svfs_unix_to_dos_attrib(dir->files[i].st.st_mode);
+
+ if (!callback(search_private, &file)) {
+ break;
+ }
+ }
+
+ search->current_index = i;
+
+ io->t2ffirst.out.count = i;
+ io->t2ffirst.out.handle = search->handle;
+ io->t2ffirst.out.end_of_search = (i == dir->count) ? 1 : 0;
+
+ /* work out if we are going to keep the search state */
+ if ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
+ ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) && (i == dir->count))) {
+ talloc_free(search);
+ } else {
+ p->next_search_handle++;
+ DLIST_ADD(p->search, search);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/* continue a search */
+static NTSTATUS svfs_search_next(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_search_next *io,
+ void *search_private,
+ bool (*callback)(void *, const union smb_search_data *))
+{
+ struct svfs_dir *dir;
+ int i;
+ struct svfs_private *p = ntvfs->private_data;
+ struct search_state *search;
+ union smb_search_data file;
+ uint_t max_count;
+
+ if (io->generic.level != RAW_SEARCH_TRANS2) {
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ if (io->generic.data_level != RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO) {
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ for (search=p->search; search; search = search->next) {
+ if (search->handle == io->t2fnext.in.handle) break;
+ }
+
+ if (!search) {
+ /* we didn't find the search handle */
+ return NT_STATUS_FOOBAR;
+ }
+
+ dir = search->dir;
+
+ /* the client might be asking for something other than just continuing
+ with the search */
+ if (!(io->t2fnext.in.flags & FLAG_TRANS2_FIND_CONTINUE) &&
+ (io->t2fnext.in.flags & FLAG_TRANS2_FIND_REQUIRE_RESUME) &&
+ io->t2fnext.in.last_name && *io->t2fnext.in.last_name) {
+ /* look backwards first */
+ for (i=search->current_index; i > 0; i--) {
+ if (strcmp(io->t2fnext.in.last_name, dir->files[i-1].name) == 0) {
+ search->current_index = i;
+ goto found;
+ }
+ }
+
+ /* then look forwards */
+ for (i=search->current_index+1; i <= dir->count; i++) {
+ if (strcmp(io->t2fnext.in.last_name, dir->files[i-1].name) == 0) {
+ search->current_index = i;
+ goto found;
+ }
+ }
+ }
+
+found:
+ max_count = search->current_index + io->t2fnext.in.max_count;
+
+ if (max_count > dir->count) {
+ max_count = dir->count;
+ }
+
+ for (i = search->current_index; i < max_count;i++) {
+ ZERO_STRUCT(file);
+ unix_to_nt_time(&file.both_directory_info.create_time, dir->files[i].st.st_ctime);
+ unix_to_nt_time(&file.both_directory_info.access_time, dir->files[i].st.st_atime);
+ unix_to_nt_time(&file.both_directory_info.write_time, dir->files[i].st.st_mtime);
+ unix_to_nt_time(&file.both_directory_info.change_time, dir->files[i].st.st_mtime);
+ file.both_directory_info.name.s = dir->files[i].name;
+ file.both_directory_info.short_name.s = dir->files[i].name;
+ file.both_directory_info.size = dir->files[i].st.st_size;
+ file.both_directory_info.attrib = svfs_unix_to_dos_attrib(dir->files[i].st.st_mode);
+
+ if (!callback(search_private, &file)) {
+ break;
+ }
+ }
+
+ io->t2fnext.out.count = i - search->current_index;
+ io->t2fnext.out.end_of_search = (i == dir->count) ? 1 : 0;
+
+ search->current_index = i;
+
+ /* work out if we are going to keep the search state */
+ if ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
+ ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) && (i == dir->count))) {
+ DLIST_REMOVE(p->search, search);
+ talloc_free(search);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/* close a search */
+static NTSTATUS svfs_search_close(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_search_close *io)
+{
+ struct svfs_private *p = ntvfs->private_data;
+ struct search_state *search;
+
+ for (search=p->search; search; search = search->next) {
+ if (search->handle == io->findclose.in.handle) break;
+ }
+
+ if (!search) {
+ /* we didn't find the search handle */
+ return NT_STATUS_FOOBAR;
+ }
+
+ DLIST_REMOVE(p->search, search);
+ talloc_free(search);
+
+ return NT_STATUS_OK;
+}
+
+/* SMBtrans - not used on file shares */
+static NTSTATUS svfs_trans(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, struct smb_trans2 *trans2)
+{
+ return NT_STATUS_ACCESS_DENIED;
+}
+
+
+/*
+ initialialise the POSIX disk backend, registering ourselves with the ntvfs subsystem
+ */
+NTSTATUS ntvfs_simple_init(void)
+{
+ NTSTATUS ret;
+ struct ntvfs_ops ops;
+ NTVFS_CURRENT_CRITICAL_SIZES(vers);
+
+ ZERO_STRUCT(ops);
+
+ /* fill in all the operations */
+ ops.connect = svfs_connect;
+ ops.disconnect = svfs_disconnect;
+ ops.unlink = svfs_unlink;
+ ops.chkpath = svfs_chkpath;
+ ops.qpathinfo = svfs_qpathinfo;
+ ops.setpathinfo = svfs_setpathinfo;
+ ops.open = svfs_open;
+ ops.mkdir = svfs_mkdir;
+ ops.rmdir = svfs_rmdir;
+ ops.rename = svfs_rename;
+ ops.copy = svfs_copy;
+ ops.ioctl = svfs_ioctl;
+ ops.read = svfs_read;
+ ops.write = svfs_write;
+ ops.seek = svfs_seek;
+ ops.flush = svfs_flush;
+ ops.close = svfs_close;
+ ops.exit = svfs_exit;
+ ops.lock = svfs_lock;
+ ops.setfileinfo = svfs_setfileinfo;
+ ops.qfileinfo = svfs_qfileinfo;
+ ops.fsinfo = svfs_fsinfo;
+ ops.lpq = svfs_lpq;
+ ops.search_first = svfs_search_first;
+ ops.search_next = svfs_search_next;
+ ops.search_close = svfs_search_close;
+ ops.trans = svfs_trans;
+ ops.logoff = svfs_logoff;
+ ops.async_setup = svfs_async_setup;
+ ops.cancel = svfs_cancel;
+
+ /* register ourselves with the NTVFS subsystem. We register
+ under names 'simple'
+ */
+
+ ops.type = NTVFS_DISK;
+ ops.name = "simple";
+ ret = ntvfs_register(&ops, &vers);
+
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("Failed to register simple backend with name: %s!\n",
+ ops.name));
+ }
+
+ return ret;
+}
diff --git a/source4/ntvfs/smb2/vfs_smb2.c b/source4/ntvfs/smb2/vfs_smb2.c
new file mode 100644
index 0000000000..d1e194f638
--- /dev/null
+++ b/source4/ntvfs/smb2/vfs_smb2.c
@@ -0,0 +1,851 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ CIFS-to-SMB2 NTVFS filesystem backend
+
+ Copyright (C) Andrew Tridgell 2008
+
+ largely based on vfs_cifs.c which was
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) James J Myers 2003 <myersjj@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+/*
+ this implements a CIFS->CIFS NTVFS filesystem backend.
+
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/composite/composite.h"
+#include "libcli/smb_composite/smb_composite.h"
+#include "auth/auth.h"
+#include "auth/credentials/credentials.h"
+#include "ntvfs/ntvfs.h"
+#include "../lib/util/dlinklist.h"
+#include "param/param.h"
+#include "libcli/resolve/resolve.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+struct cvfs_file {
+ struct cvfs_file *prev, *next;
+ uint16_t fnum;
+ struct ntvfs_handle *h;
+};
+
+/* this is stored in ntvfs_private */
+struct cvfs_private {
+ struct smb2_tree *tree;
+ struct smb2_transport *transport;
+ struct ntvfs_module_context *ntvfs;
+ struct async_info *pending;
+ struct cvfs_file *files;
+
+ /* a handle on the root of the share */
+ /* TODO: leaving this handle open could prevent other users
+ from opening the share with exclusive access. We probably
+ need to open it on demand */
+ struct smb2_handle roothandle;
+};
+
+
+/* a structure used to pass information to an async handler */
+struct async_info {
+ struct async_info *next, *prev;
+ struct cvfs_private *cvfs;
+ struct ntvfs_request *req;
+ void *c_req;
+ struct composite_context *c_comp;
+ struct cvfs_file *f;
+ void *parms;
+};
+
+#define SETUP_FILE_HERE(f) do { \
+ f = ntvfs_handle_get_backend_data(io->generic.in.file.ntvfs, ntvfs); \
+ if (!f) return NT_STATUS_INVALID_HANDLE; \
+ io->generic.in.file.fnum = f->fnum; \
+} while (0)
+
+#define SETUP_FILE do { \
+ struct cvfs_file *f; \
+ SETUP_FILE_HERE(f); \
+} while (0)
+
+#define SMB2_SERVER "smb2:server"
+#define SMB2_USER "smb2:user"
+#define SMB2_PASSWORD "smb2:password"
+#define SMB2_DOMAIN "smb2:domain"
+#define SMB2_SHARE "smb2:share"
+#define SMB2_USE_MACHINE_ACCT "smb2:use-machine-account"
+
+#define SMB2_USE_MACHINE_ACCT_DEFAULT false
+
+/*
+ a handler for oplock break events from the server - these need to be passed
+ along to the client
+ */
+static bool oplock_handler(struct smbcli_transport *transport, uint16_t tid, uint16_t fnum, uint8_t level, void *p_private)
+{
+ struct cvfs_private *p = p_private;
+ NTSTATUS status;
+ struct ntvfs_handle *h = NULL;
+ struct cvfs_file *f;
+
+ for (f=p->files; f; f=f->next) {
+ if (f->fnum != fnum) continue;
+ h = f->h;
+ break;
+ }
+
+ if (!h) {
+ DEBUG(5,("vfs_smb2: ignoring oplock break level %d for fnum %d\n", level, fnum));
+ return true;
+ }
+
+ DEBUG(5,("vfs_smb2: sending oplock break level %d for fnum %d\n", level, fnum));
+ status = ntvfs_send_oplock_break(p->ntvfs, h, level);
+ if (!NT_STATUS_IS_OK(status)) return false;
+ return true;
+}
+
+/*
+ return a handle to the root of the share
+*/
+static NTSTATUS smb2_get_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_WRITE|
+ NTCREATEX_SHARE_ACCESS_DELETE;
+ io.in.create_options = 0;
+ io.in.fname = NULL;
+
+ status = smb2_create(tree, tree, &io);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ *handle = io.out.file.handle;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ connect to a share - used when a tree_connect operation comes in.
+*/
+static NTSTATUS cvfs_connect(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, const char *sharename)
+{
+ NTSTATUS status;
+ struct cvfs_private *p;
+ const char *host, *user, *pass, *domain, *remote_share;
+ struct composite_context *creq;
+ struct share_config *scfg = ntvfs->ctx->config;
+ struct smb2_tree *tree;
+ struct cli_credentials *credentials;
+ bool machine_account;
+ struct smbcli_options options;
+
+ /* Here we need to determine which server to connect to.
+ * For now we use parametric options, type cifs.
+ * Later we will use security=server and auth_server.c.
+ */
+ host = share_string_option(scfg, SMB2_SERVER, NULL);
+ user = share_string_option(scfg, SMB2_USER, NULL);
+ pass = share_string_option(scfg, SMB2_PASSWORD, NULL);
+ domain = share_string_option(scfg, SMB2_DOMAIN, NULL);
+ remote_share = share_string_option(scfg, SMB2_SHARE, NULL);
+ if (!remote_share) {
+ remote_share = sharename;
+ }
+
+ machine_account = share_bool_option(scfg, SMB2_USE_MACHINE_ACCT, SMB2_USE_MACHINE_ACCT_DEFAULT);
+
+ p = talloc_zero(ntvfs, struct cvfs_private);
+ if (!p) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ntvfs->private_data = p;
+
+ if (!host) {
+ DEBUG(1,("CIFS backend: You must supply server\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (user && pass) {
+ DEBUG(5, ("CIFS backend: Using specified password\n"));
+ credentials = cli_credentials_init(p);
+ if (!credentials) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ cli_credentials_set_conf(credentials, ntvfs->ctx->lp_ctx);
+ cli_credentials_set_username(credentials, user, CRED_SPECIFIED);
+ if (domain) {
+ cli_credentials_set_domain(credentials, domain, CRED_SPECIFIED);
+ }
+ cli_credentials_set_password(credentials, pass, CRED_SPECIFIED);
+ } else if (machine_account) {
+ DEBUG(5, ("CIFS backend: Using machine account\n"));
+ credentials = cli_credentials_init(p);
+ cli_credentials_set_conf(credentials, ntvfs->ctx->lp_ctx);
+ if (domain) {
+ cli_credentials_set_domain(credentials, domain, CRED_SPECIFIED);
+ }
+ status = cli_credentials_set_machine_account(credentials, ntvfs->ctx->lp_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ } else if (req->session_info->credentials) {
+ DEBUG(5, ("CIFS backend: Using delegated credentials\n"));
+ credentials = req->session_info->credentials;
+ } else {
+ DEBUG(1,("CIFS backend: NO delegated credentials found: You must supply server, user and password or the client must supply delegated credentials\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ lp_smbcli_options(ntvfs->ctx->lp_ctx, &options);
+
+ creq = smb2_connect_send(p, host,
+ lp_parm_string_list(p, ntvfs->ctx->lp_ctx, NULL, "smb2", "ports", NULL),
+ remote_share,
+ lp_resolve_context(ntvfs->ctx->lp_ctx),
+ credentials,
+ ntvfs->ctx->event_ctx, &options,
+ lp_socket_options(ntvfs->ctx->lp_ctx),
+ lp_gensec_settings(p, ntvfs->ctx->lp_ctx)
+ );
+
+ status = smb2_connect_recv(creq, p, &tree);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ status = smb2_get_roothandle(tree, &p->roothandle);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ p->tree = tree;
+ p->transport = p->tree->session->transport;
+ p->ntvfs = ntvfs;
+
+ ntvfs->ctx->fs_type = talloc_strdup(ntvfs->ctx, "NTFS");
+ NT_STATUS_HAVE_NO_MEMORY(ntvfs->ctx->fs_type);
+ ntvfs->ctx->dev_type = talloc_strdup(ntvfs->ctx, "A:");
+ NT_STATUS_HAVE_NO_MEMORY(ntvfs->ctx->dev_type);
+
+ /* we need to receive oplock break requests from the server */
+ /* TODO: enable oplocks
+ smbcli_oplock_handler(p->transport, oplock_handler, p);
+ */
+ return NT_STATUS_OK;
+}
+
+/*
+ disconnect from a share
+*/
+static NTSTATUS cvfs_disconnect(struct ntvfs_module_context *ntvfs)
+{
+ struct cvfs_private *p = ntvfs->private_data;
+ struct async_info *a, *an;
+
+ /* first cleanup pending requests */
+ for (a=p->pending; a; a = an) {
+ an = a->next;
+ talloc_free(a->c_req);
+ talloc_free(a);
+ }
+
+ talloc_free(p);
+ ntvfs->private_data = NULL;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ destroy an async info structure
+*/
+static int async_info_destructor(struct async_info *async)
+{
+ DLIST_REMOVE(async->cvfs->pending, async);
+ return 0;
+}
+
+/*
+ a handler for simple async SMB2 replies
+ this handler can only be used for functions that don't return any
+ parameters (those that just return a status code)
+ */
+static void async_simple_smb2(struct smb2_request *c_req)
+{
+ struct async_info *async = c_req->async.private_data;
+ struct ntvfs_request *req = async->req;
+
+ smb2_request_receive(c_req);
+ req->async_states->status = smb2_request_destroy(c_req);
+ talloc_free(async);
+ req->async_states->send_fn(req);
+}
+
+/*
+ a handler for simple async composite replies
+ this handler can only be used for functions that don't return any
+ parameters (those that just return a status code)
+ */
+static void async_simple_composite(struct composite_context *c_req)
+{
+ struct async_info *async = c_req->async.private_data;
+ struct ntvfs_request *req = async->req;
+
+ req->async_states->status = composite_wait_free(c_req);
+ talloc_free(async);
+ req->async_states->send_fn(req);
+}
+
+
+/* save some typing for the simple functions */
+#define ASYNC_RECV_TAIL_F(io, async_fn, file) do { \
+ if (!c_req) return NT_STATUS_UNSUCCESSFUL; \
+ { \
+ struct async_info *async; \
+ async = talloc(req, struct async_info); \
+ if (!async) return NT_STATUS_NO_MEMORY; \
+ async->parms = io; \
+ async->req = req; \
+ async->f = file; \
+ async->cvfs = p; \
+ async->c_req = c_req; \
+ DLIST_ADD(p->pending, async); \
+ c_req->async.private_data = async; \
+ talloc_set_destructor(async, async_info_destructor); \
+ } \
+ c_req->async.fn = async_fn; \
+ req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC; \
+ return NT_STATUS_OK; \
+} while (0)
+
+#define ASYNC_RECV_TAIL(io, async_fn) ASYNC_RECV_TAIL_F(io, async_fn, NULL)
+
+#define SIMPLE_ASYNC_TAIL ASYNC_RECV_TAIL(NULL, async_simple_smb2)
+#define SIMPLE_COMPOSITE_TAIL ASYNC_RECV_TAIL(NULL, async_simple_composite)
+
+#define CHECK_ASYNC(req) do { \
+ if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) { \
+ DEBUG(0,("SMB2 proxy backend does not support sync operation at %s\n", \
+ __location__)); \
+ return NT_STATUS_NOT_IMPLEMENTED; \
+ }} while (0)
+
+/*
+ delete a file - the dirtype specifies the file types to include in the search.
+ The name can contain CIFS wildcards, but rarely does (except with OS/2 clients)
+
+ BUGS:
+ - doesn't handle wildcards
+ - doesn't obey attrib restrictions
+*/
+static NTSTATUS cvfs_unlink(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_unlink *unl)
+{
+ struct cvfs_private *p = ntvfs->private_data;
+ struct composite_context *c_req;
+
+ CHECK_ASYNC(req);
+
+ c_req = smb2_composite_unlink_send(p->tree, unl);
+
+ SIMPLE_COMPOSITE_TAIL;
+}
+
+/*
+ ioctl interface
+*/
+static NTSTATUS cvfs_ioctl(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_ioctl *io)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/*
+ check if a directory exists
+*/
+static NTSTATUS cvfs_chkpath(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_chkpath *cp)
+{
+ struct cvfs_private *p = ntvfs->private_data;
+ struct smb2_request *c_req;
+ struct smb2_find f;
+
+ CHECK_ASYNC(req);
+
+ /* SMB2 doesn't have a chkpath operation, and also doesn't
+ have a query path info call, so the best seems to be to do a
+ find call, using the roothandle we established at connect
+ time */
+ ZERO_STRUCT(f);
+ f.in.file.handle = p->roothandle;
+ f.in.level = SMB2_FIND_DIRECTORY_INFO;
+ f.in.pattern = cp->chkpath.in.path;
+ /* SMB2 find doesn't accept \ or the empty string - this is the best
+ approximation */
+ if (strcmp(f.in.pattern, "\\") == 0 ||
+ strcmp(f.in.pattern, "") == 0) {
+ f.in.pattern = "?";
+ }
+ f.in.continue_flags = SMB2_CONTINUE_FLAG_SINGLE | SMB2_CONTINUE_FLAG_RESTART;
+ f.in.max_response_size = 0x1000;
+
+ c_req = smb2_find_send(p->tree, &f);
+
+ SIMPLE_ASYNC_TAIL;
+}
+
+/*
+ return info on a pathname
+*/
+static NTSTATUS cvfs_qpathinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_fileinfo *info)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/*
+ query info on a open file
+*/
+static NTSTATUS cvfs_qfileinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_fileinfo *io)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+
+/*
+ set info on a pathname
+*/
+static NTSTATUS cvfs_setpathinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_setfileinfo *st)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+
+/*
+ open a file
+*/
+static NTSTATUS cvfs_open(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_open *io)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/*
+ create a directory
+*/
+static NTSTATUS cvfs_mkdir(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_mkdir *md)
+{
+ struct cvfs_private *p = ntvfs->private_data;
+ struct composite_context *c_req;
+
+ CHECK_ASYNC(req);
+
+ c_req = smb2_composite_mkdir_send(p->tree, md);
+
+ SIMPLE_COMPOSITE_TAIL;
+}
+
+/*
+ remove a directory
+*/
+static NTSTATUS cvfs_rmdir(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, struct smb_rmdir *rd)
+{
+ struct cvfs_private *p = ntvfs->private_data;
+ struct composite_context *c_req;
+
+ CHECK_ASYNC(req);
+
+ c_req = smb2_composite_rmdir_send(p->tree, rd);
+
+ SIMPLE_COMPOSITE_TAIL;
+}
+
+/*
+ rename a set of files
+*/
+static NTSTATUS cvfs_rename(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_rename *ren)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/*
+ copy a set of files
+*/
+static NTSTATUS cvfs_copy(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, struct smb_copy *cp)
+{
+ return NT_STATUS_NOT_SUPPORTED;
+}
+
+/*
+ read from a file
+*/
+static NTSTATUS cvfs_read(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_read *io)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/*
+ write to a file
+*/
+static NTSTATUS cvfs_write(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_write *io)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/*
+ seek in a file
+*/
+static NTSTATUS cvfs_seek(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_seek *io)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/*
+ flush a file
+*/
+static NTSTATUS cvfs_flush(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_flush *io)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/*
+ close a file
+*/
+static NTSTATUS cvfs_close(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_close *io)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/*
+ exit - closing files open by the pid
+*/
+static NTSTATUS cvfs_exit(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/*
+ logoff - closing files open by the user
+*/
+static NTSTATUS cvfs_logoff(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req)
+{
+ /* we can't do this right in the cifs backend .... */
+ return NT_STATUS_OK;
+}
+
+/*
+ setup for an async call - nothing to do yet
+*/
+static NTSTATUS cvfs_async_setup(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ void *private_data)
+{
+ return NT_STATUS_OK;
+}
+
+/*
+ cancel an async call
+*/
+static NTSTATUS cvfs_cancel(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/*
+ lock a byte range
+*/
+static NTSTATUS cvfs_lock(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_lock *io)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/*
+ set info on a open file
+*/
+static NTSTATUS cvfs_setfileinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_setfileinfo *io)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+
+/*
+ a handler for async fsinfo replies
+ */
+static void async_fsinfo(struct smb2_request *c_req)
+{
+ struct async_info *async = c_req->async.private_data;
+ struct ntvfs_request *req = async->req;
+ req->async_states->status = smb2_getinfo_fs_recv(c_req, req, async->parms);
+ talloc_free(async);
+ req->async_states->send_fn(req);
+}
+
+/*
+ return filesystem space info
+*/
+static NTSTATUS cvfs_fsinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_fsinfo *fs)
+{
+ struct cvfs_private *p = ntvfs->private_data;
+ struct smb2_request *c_req;
+ enum smb_fsinfo_level level = fs->generic.level;
+
+ CHECK_ASYNC(req);
+
+ switch (level) {
+ /* some levels go straight through */
+ case RAW_QFS_VOLUME_INFORMATION:
+ case RAW_QFS_SIZE_INFORMATION:
+ case RAW_QFS_DEVICE_INFORMATION:
+ case RAW_QFS_ATTRIBUTE_INFORMATION:
+ case RAW_QFS_QUOTA_INFORMATION:
+ case RAW_QFS_FULL_SIZE_INFORMATION:
+ case RAW_QFS_OBJECTID_INFORMATION:
+ break;
+
+ /* some get mapped */
+ case RAW_QFS_VOLUME_INFO:
+ level = RAW_QFS_VOLUME_INFORMATION;
+ break;
+ case RAW_QFS_SIZE_INFO:
+ level = RAW_QFS_SIZE_INFORMATION;
+ break;
+ case RAW_QFS_DEVICE_INFO:
+ level = RAW_QFS_DEVICE_INFORMATION;
+ break;
+ case RAW_QFS_ATTRIBUTE_INFO:
+ level = RAW_QFS_ATTRIBUTE_INFO;
+ break;
+
+ default:
+ /* the rest get refused for now */
+ DEBUG(0,("fsinfo level %u not possible on SMB2\n",
+ (unsigned)fs->generic.level));
+ break;
+ }
+
+ fs->generic.level = level;
+ fs->generic.handle = p->roothandle;
+
+ c_req = smb2_getinfo_fs_send(p->tree, fs);
+
+ ASYNC_RECV_TAIL(fs, async_fsinfo);
+}
+
+/*
+ return print queue info
+*/
+static NTSTATUS cvfs_lpq(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_lpq *lpq)
+{
+ return NT_STATUS_NOT_SUPPORTED;
+}
+
+/*
+ list files in a directory matching a wildcard pattern
+*/
+static NTSTATUS cvfs_search_first(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_search_first *io,
+ void *search_private,
+ bool (*callback)(void *, const union smb_search_data *))
+{
+ struct cvfs_private *p = ntvfs->private_data;
+ struct smb2_find f;
+ enum smb_search_data_level smb2_level;
+ uint_t count, i;
+ union smb_search_data *data;
+ NTSTATUS status;
+
+ if (io->generic.level != RAW_SEARCH_TRANS2) {
+ DEBUG(0,("We only support trans2 search in smb2 backend\n"));
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ switch (io->generic.data_level) {
+ case RAW_SEARCH_DATA_DIRECTORY_INFO:
+ smb2_level = SMB2_FIND_DIRECTORY_INFO;
+ break;
+ case RAW_SEARCH_DATA_FULL_DIRECTORY_INFO:
+ smb2_level = SMB2_FIND_FULL_DIRECTORY_INFO;
+ break;
+ case RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO:
+ smb2_level = SMB2_FIND_BOTH_DIRECTORY_INFO;
+ break;
+ case RAW_SEARCH_DATA_NAME_INFO:
+ smb2_level = SMB2_FIND_NAME_INFO;
+ break;
+ case RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO:
+ smb2_level = SMB2_FIND_ID_FULL_DIRECTORY_INFO;
+ break;
+ case RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO:
+ smb2_level = SMB2_FIND_ID_BOTH_DIRECTORY_INFO;
+ break;
+ default:
+ DEBUG(0,("Unsupported search level %u for smb2 backend\n",
+ (unsigned)io->generic.data_level));
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ /* we do the search on the roothandle. This only works because
+ search is synchronous, otherwise we'd have no way to
+ distinguish multiple searches happening at once
+ */
+ ZERO_STRUCT(f);
+ f.in.file.handle = p->roothandle;
+ f.in.level = smb2_level;
+ f.in.pattern = io->t2ffirst.in.pattern;
+ while (f.in.pattern[0] == '\\') {
+ f.in.pattern++;
+ }
+ f.in.continue_flags = 0;
+ f.in.max_response_size = 0x10000;
+
+ status = smb2_find_level(p->tree, req, &f, &count, &data);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ for (i=0;i<count;i++) {
+ if (!callback(search_private, &data[i])) break;
+ }
+
+ io->t2ffirst.out.handle = 0;
+ io->t2ffirst.out.count = i;
+ /* TODO: fix end_of_file */
+ io->t2ffirst.out.end_of_search = 1;
+
+ talloc_free(data);
+
+ return NT_STATUS_OK;
+}
+
+/* continue a search */
+static NTSTATUS cvfs_search_next(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_search_next *io,
+ void *search_private,
+ bool (*callback)(void *, const union smb_search_data *))
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/* close a search */
+static NTSTATUS cvfs_search_close(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_search_close *io)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/* SMBtrans - not used on file shares */
+static NTSTATUS cvfs_trans(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ struct smb_trans2 *trans2)
+{
+ return NT_STATUS_ACCESS_DENIED;
+}
+
+/* change notify request - always async */
+static NTSTATUS cvfs_notify(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_notify *io)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/*
+ initialise the CIFS->CIFS backend, registering ourselves with the ntvfs subsystem
+ */
+NTSTATUS ntvfs_smb2_init(void)
+{
+ NTSTATUS ret;
+ struct ntvfs_ops ops;
+ NTVFS_CURRENT_CRITICAL_SIZES(vers);
+
+ ZERO_STRUCT(ops);
+
+ /* fill in the name and type */
+ ops.name = "smb2";
+ ops.type = NTVFS_DISK;
+
+ /* fill in all the operations */
+ ops.connect = cvfs_connect;
+ ops.disconnect = cvfs_disconnect;
+ ops.unlink = cvfs_unlink;
+ ops.chkpath = cvfs_chkpath;
+ ops.qpathinfo = cvfs_qpathinfo;
+ ops.setpathinfo = cvfs_setpathinfo;
+ ops.open = cvfs_open;
+ ops.mkdir = cvfs_mkdir;
+ ops.rmdir = cvfs_rmdir;
+ ops.rename = cvfs_rename;
+ ops.copy = cvfs_copy;
+ ops.ioctl = cvfs_ioctl;
+ ops.read = cvfs_read;
+ ops.write = cvfs_write;
+ ops.seek = cvfs_seek;
+ ops.flush = cvfs_flush;
+ ops.close = cvfs_close;
+ ops.exit = cvfs_exit;
+ ops.lock = cvfs_lock;
+ ops.setfileinfo = cvfs_setfileinfo;
+ ops.qfileinfo = cvfs_qfileinfo;
+ ops.fsinfo = cvfs_fsinfo;
+ ops.lpq = cvfs_lpq;
+ ops.search_first = cvfs_search_first;
+ ops.search_next = cvfs_search_next;
+ ops.search_close = cvfs_search_close;
+ ops.trans = cvfs_trans;
+ ops.logoff = cvfs_logoff;
+ ops.async_setup = cvfs_async_setup;
+ ops.cancel = cvfs_cancel;
+ ops.notify = cvfs_notify;
+
+ /* register ourselves with the NTVFS subsystem. We register
+ under the name 'smb2'. */
+ ret = ntvfs_register(&ops, &vers);
+
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("Failed to register SMB2 backend\n"));
+ }
+
+ return ret;
+}
diff --git a/source4/ntvfs/sysdep/README b/source4/ntvfs/sysdep/README
new file mode 100644
index 0000000000..a2a8f57c28
--- /dev/null
+++ b/source4/ntvfs/sysdep/README
@@ -0,0 +1,5 @@
+This directory contains OS depdendent interfaces to facilities that
+are only available on a few of our target systems, and require
+substantial code to abstract.
+
+
diff --git a/source4/ntvfs/sysdep/config.m4 b/source4/ntvfs/sysdep/config.m4
new file mode 100644
index 0000000000..f54f65d08d
--- /dev/null
+++ b/source4/ntvfs/sysdep/config.m4
@@ -0,0 +1,27 @@
+AC_CHECK_HEADERS(linux/inotify.h asm/unistd.h sys/inotify.h)
+AC_CHECK_FUNC(inotify_init)
+AC_HAVE_DECL(__NR_inotify_init, [#include <asm/unistd.h>])
+
+SMB_ENABLE(sys_notify_inotify, NO)
+
+if test x"$ac_cv_func_inotify_init" = x"yes" -a x"$ac_cv_header_sys_inotify_h" = x"yes"; then
+ SMB_ENABLE(sys_notify_inotify, YES)
+fi
+
+if test x"$ac_cv_func_inotify_init" = x"yes" -a x"$ac_cv_header_linux_inotify_h" = x"yes"; then
+ SMB_ENABLE(sys_notify_inotify, YES)
+fi
+
+if test x"$ac_cv_header_linux_inotify_h" = x"yes" -a x"$ac_cv_have___NR_inotify_init_decl" = x"yes"; then
+ SMB_ENABLE(sys_notify_inotify, YES)
+fi
+
+AC_HAVE_DECL(F_SETLEASE, [#include <fcntl.h>])
+AC_HAVE_DECL(SA_SIGINFO, [#include <signal.h>])
+
+SMB_ENABLE(sys_lease_linux, NO)
+
+if test x"$ac_cv_have_F_SETLEASE_decl" = x"yes" \
+ -a x"$ac_cv_have_SA_SIGINFO_decl" = x"yes"; then
+ SMB_ENABLE(sys_lease_linux, YES)
+fi
diff --git a/source4/ntvfs/sysdep/config.mk b/source4/ntvfs/sysdep/config.mk
new file mode 100644
index 0000000000..5feca6143a
--- /dev/null
+++ b/source4/ntvfs/sysdep/config.mk
@@ -0,0 +1,27 @@
+################################################
+# Start MODULE sys_notify_inotify
+[MODULE::sys_notify_inotify]
+SUBSYSTEM = sys_notify
+INIT_FUNCTION = sys_notify_inotify_init
+PRIVATE_DEPENDENCIES = LIBEVENTS
+# End MODULE sys_notify_inotify
+################################################
+
+sys_notify_inotify_OBJ_FILES = $(ntvfssrcdir)/sysdep/inotify.o
+
+################################################
+# Start SUBSYSTEM sys_notify
+[SUBSYSTEM::sys_notify]
+# End SUBSYSTEM sys_notify
+################################################
+
+sys_notify_OBJ_FILES = $(ntvfssrcdir)/sysdep/sys_notify.o
+
+[SUBSYSTEM::sys_lease_linux]
+PRIVATE_DEPENDENCIES = LIBTEVENT
+
+sys_lease_linux_OBJ_FILES = $(ntvfssrcdir)/sysdep/sys_lease_linux.o
+
+[SUBSYSTEM::sys_lease]
+
+sys_lease_OBJ_FILES = $(ntvfssrcdir)/sysdep/sys_lease.o
diff --git a/source4/ntvfs/sysdep/inotify.c b/source4/ntvfs/sysdep/inotify.c
new file mode 100644
index 0000000000..42aac0b097
--- /dev/null
+++ b/source4/ntvfs/sysdep/inotify.c
@@ -0,0 +1,422 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ notify implementation using inotify
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include <tevent.h>
+#include "ntvfs/sysdep/sys_notify.h"
+#include "../lib/util/dlinklist.h"
+#include "libcli/raw/smb.h"
+#include "param/param.h"
+
+#if HAVE_SYS_INOTIFY_H
+#include <sys/inotify.h>
+#else
+/* for older glibc varients - we can remove this eventually */
+#include <linux/inotify.h>
+#include <asm/unistd.h>
+
+#ifndef HAVE_INOTIFY_INIT
+/*
+ glibc doesn't define these functions yet (as of March 2006)
+*/
+static int inotify_init(void)
+{
+ return syscall(__NR_inotify_init);
+}
+
+static int inotify_add_watch(int fd, const char *path, __u32 mask)
+{
+ return syscall(__NR_inotify_add_watch, fd, path, mask);
+}
+
+static int inotify_rm_watch(int fd, int wd)
+{
+ return syscall(__NR_inotify_rm_watch, fd, wd);
+}
+#endif
+#endif
+
+
+/* older glibc headers don't have these defines either */
+#ifndef IN_ONLYDIR
+#define IN_ONLYDIR 0x01000000
+#endif
+#ifndef IN_MASK_ADD
+#define IN_MASK_ADD 0x20000000
+#endif
+
+struct inotify_private {
+ struct sys_notify_context *ctx;
+ int fd;
+ struct inotify_watch_context *watches;
+};
+
+struct inotify_watch_context {
+ struct inotify_watch_context *next, *prev;
+ struct inotify_private *in;
+ int wd;
+ sys_notify_callback_t callback;
+ void *private_data;
+ uint32_t mask; /* the inotify mask */
+ uint32_t filter; /* the windows completion filter */
+ const char *path;
+};
+
+
+/*
+ see if a particular event from inotify really does match a requested
+ notify event in SMB
+*/
+static bool filter_match(struct inotify_watch_context *w,
+ struct inotify_event *e)
+{
+ if ((e->mask & w->mask) == 0) {
+ /* this happens because inotify_add_watch() coalesces watches on the same
+ path, oring their masks together */
+ return false;
+ }
+
+ /* SMB separates the filters for files and directories */
+ if (e->mask & IN_ISDIR) {
+ if ((w->filter & FILE_NOTIFY_CHANGE_DIR_NAME) == 0) {
+ return false;
+ }
+ } else {
+ if ((e->mask & IN_ATTRIB) &&
+ (w->filter & (FILE_NOTIFY_CHANGE_ATTRIBUTES|
+ FILE_NOTIFY_CHANGE_LAST_WRITE|
+ FILE_NOTIFY_CHANGE_LAST_ACCESS|
+ FILE_NOTIFY_CHANGE_EA|
+ FILE_NOTIFY_CHANGE_SECURITY))) {
+ return true;
+ }
+ if ((e->mask & IN_MODIFY) &&
+ (w->filter & FILE_NOTIFY_CHANGE_ATTRIBUTES)) {
+ return true;
+ }
+ if ((w->filter & FILE_NOTIFY_CHANGE_FILE_NAME) == 0) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+
+/*
+ dispatch one inotify event
+
+ the cookies are used to correctly handle renames
+*/
+static void inotify_dispatch(struct inotify_private *in,
+ struct inotify_event *e,
+ uint32_t prev_cookie,
+ struct inotify_event *e2)
+{
+ struct inotify_watch_context *w, *next;
+ struct notify_event ne;
+
+ /* ignore extraneous events, such as unmount and IN_IGNORED events */
+ if ((e->mask & (IN_ATTRIB|IN_MODIFY|IN_CREATE|IN_DELETE|
+ IN_MOVED_FROM|IN_MOVED_TO)) == 0) {
+ return;
+ }
+
+ /* map the inotify mask to a action. This gets complicated for
+ renames */
+ if (e->mask & IN_CREATE) {
+ ne.action = NOTIFY_ACTION_ADDED;
+ } else if (e->mask & IN_DELETE) {
+ ne.action = NOTIFY_ACTION_REMOVED;
+ } else if (e->mask & IN_MOVED_FROM) {
+ if (e2 != NULL && e2->cookie == e->cookie) {
+ ne.action = NOTIFY_ACTION_OLD_NAME;
+ } else {
+ ne.action = NOTIFY_ACTION_REMOVED;
+ }
+ } else if (e->mask & IN_MOVED_TO) {
+ if (e->cookie == prev_cookie) {
+ ne.action = NOTIFY_ACTION_NEW_NAME;
+ } else {
+ ne.action = NOTIFY_ACTION_ADDED;
+ }
+ } else {
+ ne.action = NOTIFY_ACTION_MODIFIED;
+ }
+ ne.path = e->name;
+
+ /* find any watches that have this watch descriptor */
+ for (w=in->watches;w;w=next) {
+ next = w->next;
+ if (w->wd == e->wd && filter_match(w, e)) {
+ w->callback(in->ctx, w->private_data, &ne);
+ }
+ }
+
+ /* SMB expects a file rename to generate three events, two for
+ the rename and the other for a modify of the
+ destination. Strange! */
+ if (ne.action != NOTIFY_ACTION_NEW_NAME ||
+ (e->mask & IN_ISDIR) != 0) {
+ return;
+ }
+
+ ne.action = NOTIFY_ACTION_MODIFIED;
+ e->mask = IN_ATTRIB;
+
+ for (w=in->watches;w;w=next) {
+ next = w->next;
+ if (w->wd == e->wd && filter_match(w, e) &&
+ !(w->filter & FILE_NOTIFY_CHANGE_CREATION)) {
+ w->callback(in->ctx, w->private_data, &ne);
+ }
+ }
+}
+
+/*
+ called when the kernel has some events for us
+*/
+static void inotify_handler(struct tevent_context *ev, struct tevent_fd *fde,
+ uint16_t flags, void *private_data)
+{
+ struct inotify_private *in = talloc_get_type(private_data,
+ struct inotify_private);
+ int bufsize = 0;
+ struct inotify_event *e0, *e;
+ uint32_t prev_cookie=0;
+
+ /*
+ we must use FIONREAD as we cannot predict the length of the
+ filenames, and thus can't know how much to allocate
+ otherwise
+ */
+ if (ioctl(in->fd, FIONREAD, &bufsize) != 0 ||
+ bufsize == 0) {
+ DEBUG(0,("No data on inotify fd?!\n"));
+ return;
+ }
+
+ e0 = e = talloc_size(in, bufsize);
+ if (e == NULL) return;
+
+ if (read(in->fd, e0, bufsize) != bufsize) {
+ DEBUG(0,("Failed to read all inotify data\n"));
+ talloc_free(e0);
+ return;
+ }
+
+ /* we can get more than one event in the buffer */
+ while (bufsize >= sizeof(*e)) {
+ struct inotify_event *e2 = NULL;
+ bufsize -= e->len + sizeof(*e);
+ if (bufsize >= sizeof(*e)) {
+ e2 = (struct inotify_event *)(e->len + sizeof(*e) + (char *)e);
+ }
+ inotify_dispatch(in, e, prev_cookie, e2);
+ prev_cookie = e->cookie;
+ e = e2;
+ }
+
+ talloc_free(e0);
+}
+
+/*
+ setup the inotify handle - called the first time a watch is added on
+ this context
+*/
+static NTSTATUS inotify_setup(struct sys_notify_context *ctx)
+{
+ struct inotify_private *in;
+ struct tevent_fd *fde;
+
+ in = talloc(ctx, struct inotify_private);
+ NT_STATUS_HAVE_NO_MEMORY(in);
+
+ in->fd = inotify_init();
+ if (in->fd == -1) {
+ DEBUG(0,("Failed to init inotify - %s\n", strerror(errno)));
+ talloc_free(in);
+ return map_nt_error_from_unix(errno);
+ }
+ in->ctx = ctx;
+ in->watches = NULL;
+
+ ctx->private_data = in;
+
+ /* add a event waiting for the inotify fd to be readable */
+ fde = tevent_add_fd(ctx->ev, in, in->fd,
+ TEVENT_FD_READ, inotify_handler, in);
+ if (!fde) {
+ if (errno == 0) {
+ errno = ENOMEM;
+ }
+ DEBUG(0,("Failed to tevent_add_fd() - %s\n", strerror(errno)));
+ talloc_free(in);
+ return map_nt_error_from_unix(errno);
+ }
+
+ tevent_fd_set_auto_close(fde);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ map from a change notify mask to a inotify mask. Remove any bits
+ which we can handle
+*/
+static const struct {
+ uint32_t notify_mask;
+ uint32_t inotify_mask;
+} inotify_mapping[] = {
+ {FILE_NOTIFY_CHANGE_FILE_NAME, IN_CREATE|IN_DELETE|IN_MOVED_FROM|IN_MOVED_TO},
+ {FILE_NOTIFY_CHANGE_DIR_NAME, IN_CREATE|IN_DELETE|IN_MOVED_FROM|IN_MOVED_TO},
+ {FILE_NOTIFY_CHANGE_ATTRIBUTES, IN_ATTRIB|IN_MOVED_TO|IN_MOVED_FROM|IN_MODIFY},
+ {FILE_NOTIFY_CHANGE_LAST_WRITE, IN_ATTRIB},
+ {FILE_NOTIFY_CHANGE_LAST_ACCESS, IN_ATTRIB},
+ {FILE_NOTIFY_CHANGE_EA, IN_ATTRIB},
+ {FILE_NOTIFY_CHANGE_SECURITY, IN_ATTRIB}
+};
+
+static uint32_t inotify_map(struct notify_entry *e)
+{
+ int i;
+ uint32_t out=0;
+ for (i=0;i<ARRAY_SIZE(inotify_mapping);i++) {
+ if (inotify_mapping[i].notify_mask & e->filter) {
+ out |= inotify_mapping[i].inotify_mask;
+ e->filter &= ~inotify_mapping[i].notify_mask;
+ }
+ }
+ return out;
+}
+
+/*
+ destroy a watch
+*/
+static int watch_destructor(struct inotify_watch_context *w)
+{
+ struct inotify_private *in = w->in;
+ int wd = w->wd;
+ DLIST_REMOVE(w->in->watches, w);
+
+ /* only rm the watch if its the last one with this wd */
+ for (w=in->watches;w;w=w->next) {
+ if (w->wd == wd) break;
+ }
+ if (w == NULL) {
+ inotify_rm_watch(in->fd, wd);
+ }
+ return 0;
+}
+
+
+/*
+ add a watch. The watch is removed when the caller calls
+ talloc_free() on *handle
+*/
+static NTSTATUS inotify_watch(struct sys_notify_context *ctx,
+ struct notify_entry *e,
+ sys_notify_callback_t callback,
+ void *private_data,
+ void *handle_p)
+{
+ struct inotify_private *in;
+ int wd;
+ uint32_t mask;
+ struct inotify_watch_context *w;
+ uint32_t filter = e->filter;
+ void **handle = (void **)handle_p;
+
+ /* maybe setup the inotify fd */
+ if (ctx->private_data == NULL) {
+ NTSTATUS status;
+ status = inotify_setup(ctx);
+ NT_STATUS_NOT_OK_RETURN(status);
+ }
+
+ in = talloc_get_type(ctx->private_data, struct inotify_private);
+
+ mask = inotify_map(e);
+ if (mask == 0) {
+ /* this filter can't be handled by inotify */
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* using IN_MASK_ADD allows us to cope with inotify() returning the same
+ watch descriptor for muliple watches on the same path */
+ mask |= (IN_MASK_ADD | IN_ONLYDIR);
+
+ /* get a new watch descriptor for this path */
+ wd = inotify_add_watch(in->fd, e->path, mask);
+ if (wd == -1) {
+ e->filter = filter;
+ return map_nt_error_from_unix(errno);
+ }
+
+ w = talloc(in, struct inotify_watch_context);
+ if (w == NULL) {
+ inotify_rm_watch(in->fd, wd);
+ e->filter = filter;
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ w->in = in;
+ w->wd = wd;
+ w->callback = callback;
+ w->private_data = private_data;
+ w->mask = mask;
+ w->filter = filter;
+ w->path = talloc_strdup(w, e->path);
+ if (w->path == NULL) {
+ inotify_rm_watch(in->fd, wd);
+ e->filter = filter;
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*handle) = w;
+
+ DLIST_ADD(in->watches, w);
+
+ /* the caller frees the handle to stop watching */
+ talloc_set_destructor(w, watch_destructor);
+
+ return NT_STATUS_OK;
+}
+
+
+static struct sys_notify_backend inotify = {
+ .name = "inotify",
+ .notify_watch = inotify_watch
+};
+
+/*
+ initialialise the inotify module
+ */
+NTSTATUS sys_notify_inotify_init(void)
+{
+ /* register ourselves as a system inotify module */
+ return sys_notify_register(&inotify);
+}
diff --git a/source4/ntvfs/sysdep/sys_lease.c b/source4/ntvfs/sysdep/sys_lease.c
new file mode 100644
index 0000000000..12fd83e8e0
--- /dev/null
+++ b/source4/ntvfs/sysdep/sys_lease.c
@@ -0,0 +1,145 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ abstract the various kernel interfaces to leases (oplocks) into a
+ single Samba friendly interface
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "ntvfs/sysdep/sys_lease.h"
+#include "../lib/util/dlinklist.h"
+#include "param/param.h"
+
+/* list of registered backends */
+static struct sys_lease_ops *backends;
+static uint32_t num_backends;
+
+#define LEASE_BACKEND "lease:backend"
+
+/*
+ initialise a system change notify backend
+*/
+_PUBLIC_ struct sys_lease_context *sys_lease_context_create(struct share_config *scfg,
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct messaging_context *msg,
+ sys_lease_send_break_fn break_send)
+{
+ struct sys_lease_context *ctx;
+ const char *bname;
+ int i;
+ NTSTATUS status;
+
+ if (num_backends == 0) {
+ return NULL;
+ }
+
+ if (ev == NULL) {
+ return NULL;
+ }
+
+ ctx = talloc_zero(mem_ctx, struct sys_lease_context);
+ if (ctx == NULL) {
+ return NULL;
+ }
+
+ ctx->event_ctx = ev;
+ ctx->msg_ctx = msg;
+ ctx->break_send = break_send;
+
+ bname = share_string_option(scfg, LEASE_BACKEND, NULL);
+ if (!bname) {
+ talloc_free(ctx);
+ return NULL;
+ }
+
+ for (i=0;i<num_backends;i++) {
+ if (strcasecmp(backends[i].name, bname) == 0) {
+ ctx->ops = &backends[i];
+ break;
+ }
+ }
+
+ if (!ctx->ops) {
+ talloc_free(ctx);
+ return NULL;
+ }
+
+ status = ctx->ops->init(ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(ctx);
+ return NULL;
+ }
+
+ return ctx;
+}
+
+/*
+ register a lease backend
+*/
+_PUBLIC_ NTSTATUS sys_lease_register(const struct sys_lease_ops *backend)
+{
+ struct sys_lease_ops *b;
+ b = talloc_realloc(talloc_autofree_context(), backends,
+ struct sys_lease_ops, num_backends+1);
+ NT_STATUS_HAVE_NO_MEMORY(b);
+ backends = b;
+ backends[num_backends] = *backend;
+ num_backends++;
+ return NT_STATUS_OK;
+}
+
+#ifndef STATIC_sys_lease_MODULES
+#define STATIC_sys_lease_MODULES NULL
+#endif
+
+_PUBLIC_ NTSTATUS sys_lease_init(void)
+{
+ static bool initialized = false;
+ extern NTSTATUS sys_lease_linux_init(void);
+
+ init_module_fn static_init[] = { STATIC_sys_lease_MODULES };
+
+ if (initialized) return NT_STATUS_OK;
+ initialized = true;
+
+ run_init_functions(static_init);
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sys_lease_setup(struct sys_lease_context *ctx,
+ struct opendb_entry *e)
+{
+ return ctx->ops->setup(ctx, e);
+}
+
+NTSTATUS sys_lease_update(struct sys_lease_context *ctx,
+ struct opendb_entry *e)
+{
+ return ctx->ops->update(ctx, e);
+}
+
+NTSTATUS sys_lease_remove(struct sys_lease_context *ctx,
+ struct opendb_entry *e)
+{
+ return ctx->ops->remove(ctx, e);
+}
diff --git a/source4/ntvfs/sysdep/sys_lease.h b/source4/ntvfs/sysdep/sys_lease.h
new file mode 100644
index 0000000000..422797b0b8
--- /dev/null
+++ b/source4/ntvfs/sysdep/sys_lease.h
@@ -0,0 +1,66 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "param/share.h"
+
+struct sys_lease_context;
+struct opendb_entry;
+struct messaging_context;
+struct tevent_context;
+
+typedef NTSTATUS (*sys_lease_send_break_fn)(struct messaging_context *,
+ struct opendb_entry *,
+ uint8_t level);
+
+struct sys_lease_ops {
+ const char *name;
+ NTSTATUS (*init)(struct sys_lease_context *ctx);
+ NTSTATUS (*setup)(struct sys_lease_context *ctx,
+ struct opendb_entry *e);
+ NTSTATUS (*update)(struct sys_lease_context *ctx,
+ struct opendb_entry *e);
+ NTSTATUS (*remove)(struct sys_lease_context *ctx,
+ struct opendb_entry *e);
+};
+
+struct sys_lease_context {
+ struct tevent_context *event_ctx;
+ struct messaging_context *msg_ctx;
+ sys_lease_send_break_fn break_send;
+ void *private_data; /* for use of backend */
+ const struct sys_lease_ops *ops;
+};
+
+NTSTATUS sys_lease_register(const struct sys_lease_ops *ops);
+NTSTATUS sys_lease_init(void);
+
+struct sys_lease_context *sys_lease_context_create(struct share_config *scfg,
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct messaging_context *msg_ctx,
+ sys_lease_send_break_fn break_send);
+
+NTSTATUS sys_lease_setup(struct sys_lease_context *ctx,
+ struct opendb_entry *e);
+
+NTSTATUS sys_lease_update(struct sys_lease_context *ctx,
+ struct opendb_entry *e);
+
+NTSTATUS sys_lease_remove(struct sys_lease_context *ctx,
+ struct opendb_entry *e);
diff --git a/source4/ntvfs/sysdep/sys_lease_linux.c b/source4/ntvfs/sysdep/sys_lease_linux.c
new file mode 100644
index 0000000000..d1473627a7
--- /dev/null
+++ b/source4/ntvfs/sysdep/sys_lease_linux.c
@@ -0,0 +1,213 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ lease (oplock) implementation using fcntl F_SETLEASE on linux
+*/
+
+#include "includes.h"
+#include <tevent.h>
+#include "system/filesys.h"
+#include "ntvfs/sysdep/sys_lease.h"
+#include "ntvfs/ntvfs.h"
+#include "librpc/gen_ndr/ndr_opendb.h"
+#include "../lib/util/dlinklist.h"
+#include "cluster/cluster.h"
+
+#define LINUX_LEASE_RT_SIGNAL (SIGRTMIN+1)
+
+struct linux_lease_pending {
+ struct linux_lease_pending *prev, *next;
+ struct sys_lease_context *ctx;
+ struct opendb_entry e;
+};
+
+/* the global linked list of pending leases */
+static struct linux_lease_pending *leases;
+
+static void linux_lease_signal_handler(struct tevent_context *ev_ctx,
+ struct tevent_signal *se,
+ int signum, int count,
+ void *_info, void *private_data)
+{
+ struct sys_lease_context *ctx = talloc_get_type(private_data,
+ struct sys_lease_context);
+ siginfo_t *info = (siginfo_t *)_info;
+ struct linux_lease_pending *c;
+ int got_fd = info->si_fd;
+
+ for (c = leases; c; c = c->next) {
+ int *fd = (int *)c->e.fd;
+
+ if (got_fd == *fd) {
+ break;
+ }
+ }
+
+ if (!c) {
+ return;
+ }
+
+ ctx->break_send(ctx->msg_ctx, &c->e, OPLOCK_BREAK_TO_NONE);
+}
+
+static int linux_lease_pending_destructor(struct linux_lease_pending *p)
+{
+ int ret;
+ int *fd = (int *)p->e.fd;
+
+ DLIST_REMOVE(leases, p);
+
+ if (*fd == -1) {
+ return 0;
+ }
+
+ ret = fcntl(*fd, F_SETLEASE, F_UNLCK);
+ if (ret == -1) {
+ DEBUG(0,("%s: failed to remove oplock: %s\n",
+ __FUNCTION__, strerror(errno)));
+ }
+
+ return 0;
+}
+
+static NTSTATUS linux_lease_init(struct sys_lease_context *ctx)
+{
+ struct tevent_signal *se;
+
+ se = tevent_add_signal(ctx->event_ctx, ctx,
+ LINUX_LEASE_RT_SIGNAL, SA_SIGINFO,
+ linux_lease_signal_handler, ctx);
+ NT_STATUS_HAVE_NO_MEMORY(se);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS linux_lease_setup(struct sys_lease_context *ctx,
+ struct opendb_entry *e)
+{
+ int ret;
+ int *fd = (int *)e->fd;
+ struct linux_lease_pending *p;
+
+ if (e->oplock_level == OPLOCK_NONE) {
+ e->fd = NULL;
+ return NT_STATUS_OK;
+ } else if (e->oplock_level == OPLOCK_LEVEL_II) {
+ /*
+ * the linux kernel doesn't support level2 oplocks
+ * so fix up the granted oplock level
+ */
+ e->oplock_level = OPLOCK_NONE;
+ e->allow_level_II_oplock = false;
+ e->fd = NULL;
+ return NT_STATUS_OK;
+ }
+
+ p = talloc(ctx, struct linux_lease_pending);
+ NT_STATUS_HAVE_NO_MEMORY(p);
+
+ p->ctx = ctx;
+ p->e = *e;
+
+ ret = fcntl(*fd, F_SETSIG, LINUX_LEASE_RT_SIGNAL);
+ if (ret == -1) {
+ talloc_free(p);
+ return map_nt_error_from_unix(errno);
+ }
+
+ ret = fcntl(*fd, F_SETLEASE, F_WRLCK);
+ if (ret == -1) {
+ talloc_free(p);
+ return map_nt_error_from_unix(errno);
+ }
+
+ DLIST_ADD(leases, p);
+
+ talloc_set_destructor(p, linux_lease_pending_destructor);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS linux_lease_remove(struct sys_lease_context *ctx,
+ struct opendb_entry *e);
+
+static NTSTATUS linux_lease_update(struct sys_lease_context *ctx,
+ struct opendb_entry *e)
+{
+ struct linux_lease_pending *c;
+
+ for (c = leases; c; c = c->next) {
+ if (c->e.fd == e->fd) {
+ break;
+ }
+ }
+
+ if (!c) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ /*
+ * set the fd pointer to NULL so that the caller
+ * will not call the remove function as the oplock
+ * is already removed
+ */
+ e->fd = NULL;
+
+ talloc_free(c);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS linux_lease_remove(struct sys_lease_context *ctx,
+ struct opendb_entry *e)
+{
+ struct linux_lease_pending *c;
+
+ for (c = leases; c; c = c->next) {
+ if (c->e.fd == e->fd) {
+ break;
+ }
+ }
+
+ if (!c) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ talloc_free(c);
+
+ return NT_STATUS_OK;
+}
+
+static struct sys_lease_ops linux_lease_ops = {
+ .name = "linux",
+ .init = linux_lease_init,
+ .setup = linux_lease_setup,
+ .update = linux_lease_update,
+ .remove = linux_lease_remove
+};
+
+/*
+ initialialise the linux lease module
+ */
+NTSTATUS sys_lease_linux_init(void)
+{
+ /* register ourselves as a system lease module */
+ return sys_lease_register(&linux_lease_ops);
+}
diff --git a/source4/ntvfs/sysdep/sys_notify.c b/source4/ntvfs/sysdep/sys_notify.c
new file mode 100644
index 0000000000..57e921b206
--- /dev/null
+++ b/source4/ntvfs/sysdep/sys_notify.c
@@ -0,0 +1,147 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ abstract the various kernel interfaces to change notify into a
+ single Samba friendly interface
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "ntvfs/sysdep/sys_notify.h"
+#include "../lib/tevent/tevent.h"
+#include "../lib/util/dlinklist.h"
+#include "param/param.h"
+
+/* list of registered backends */
+static struct sys_notify_backend *backends;
+static uint32_t num_backends;
+
+#define NOTIFY_BACKEND "notify:backend"
+
+/*
+ initialise a system change notify backend
+*/
+_PUBLIC_ struct sys_notify_context *sys_notify_context_create(struct share_config *scfg,
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev)
+{
+ struct sys_notify_context *ctx;
+ const char *bname;
+ int i;
+
+ if (num_backends == 0) {
+ return NULL;
+ }
+
+ if (ev == NULL) {
+ return NULL;
+ }
+
+ ctx = talloc_zero(mem_ctx, struct sys_notify_context);
+ if (ctx == NULL) {
+ return NULL;
+ }
+
+ ctx->ev = ev;
+
+ bname = share_string_option(scfg, NOTIFY_BACKEND, NULL);
+ if (!bname) {
+ if (num_backends) {
+ bname = backends[0].name;
+ } else {
+ bname = "__unknown__";
+ }
+ }
+
+ for (i=0;i<num_backends;i++) {
+ char *enable_opt_name;
+ bool enabled;
+
+ enable_opt_name = talloc_asprintf(mem_ctx, "notify:%s",
+ backends[i].name);
+ enabled = share_bool_option(scfg, enable_opt_name, true);
+ talloc_free(enable_opt_name);
+
+ if (!enabled)
+ continue;
+
+ if (strcasecmp(backends[i].name, bname) == 0) {
+ bname = backends[i].name;
+ break;
+ }
+ }
+
+ ctx->name = bname;
+ ctx->notify_watch = NULL;
+
+ if (i < num_backends) {
+ ctx->notify_watch = backends[i].notify_watch;
+ }
+
+ return ctx;
+}
+
+/*
+ add a watch
+
+ note that this call must modify the e->filter and e->subdir_filter
+ bits to remove ones handled by this backend. Any remaining bits will
+ be handled by the generic notify layer
+*/
+_PUBLIC_ NTSTATUS sys_notify_watch(struct sys_notify_context *ctx,
+ struct notify_entry *e,
+ sys_notify_callback_t callback,
+ void *private_data, void *handle)
+{
+ if (!ctx->notify_watch) {
+ return NT_STATUS_INVALID_SYSTEM_SERVICE;
+ }
+ return ctx->notify_watch(ctx, e, callback, private_data, handle);
+}
+
+/*
+ register a notify backend
+*/
+_PUBLIC_ NTSTATUS sys_notify_register(struct sys_notify_backend *backend)
+{
+ struct sys_notify_backend *b;
+ b = talloc_realloc(talloc_autofree_context(), backends,
+ struct sys_notify_backend, num_backends+1);
+ NT_STATUS_HAVE_NO_MEMORY(b);
+ backends = b;
+ backends[num_backends] = *backend;
+ num_backends++;
+ return NT_STATUS_OK;
+}
+
+_PUBLIC_ NTSTATUS sys_notify_init(void)
+{
+ static bool initialized = false;
+ extern NTSTATUS sys_notify_inotify_init(void);
+
+ init_module_fn static_init[] = { STATIC_sys_notify_MODULES };
+
+ if (initialized) return NT_STATUS_OK;
+ initialized = true;
+
+ run_init_functions(static_init);
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/ntvfs/sysdep/sys_notify.h b/source4/ntvfs/sysdep/sys_notify.h
new file mode 100644
index 0000000000..d912a9bdaf
--- /dev/null
+++ b/source4/ntvfs/sysdep/sys_notify.h
@@ -0,0 +1,53 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "librpc/gen_ndr/notify.h"
+#include "param/share.h"
+
+struct sys_notify_context;
+
+typedef void (*sys_notify_callback_t)(struct sys_notify_context *,
+ void *, struct notify_event *ev);
+
+typedef NTSTATUS (*notify_watch_t)(struct sys_notify_context *ctx,
+ struct notify_entry *e,
+ sys_notify_callback_t callback,
+ void *private_data,
+ void *handle_p);
+
+struct sys_notify_context {
+ struct tevent_context *ev;
+ void *private_data; /* for use of backend */
+ const char *name;
+ notify_watch_t notify_watch;
+};
+
+struct sys_notify_backend {
+ const char *name;
+ notify_watch_t notify_watch;
+};
+
+NTSTATUS sys_notify_register(struct sys_notify_backend *backend);
+struct sys_notify_context *sys_notify_context_create(struct share_config *scfg,
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev);
+NTSTATUS sys_notify_watch(struct sys_notify_context *ctx, struct notify_entry *e,
+ sys_notify_callback_t callback, void *private_data,
+ void *handle);
+NTSTATUS sys_notify_init(void);
diff --git a/source4/ntvfs/unixuid/config.m4 b/source4/ntvfs/unixuid/config.m4
new file mode 100644
index 0000000000..cb870d9e16
--- /dev/null
+++ b/source4/ntvfs/unixuid/config.m4
@@ -0,0 +1 @@
+AC_CHECK_FUNCS(setgroups)
diff --git a/source4/ntvfs/unixuid/config.mk b/source4/ntvfs/unixuid/config.mk
new file mode 100644
index 0000000000..6377657cec
--- /dev/null
+++ b/source4/ntvfs/unixuid/config.mk
@@ -0,0 +1,10 @@
+################################################
+# Start MODULE ntvfs_unixuid
+[MODULE::ntvfs_unixuid]
+INIT_FUNCTION = ntvfs_unixuid_init
+SUBSYSTEM = ntvfs
+PRIVATE_DEPENDENCIES = SAMDB NSS_WRAPPER
+# End MODULE ntvfs_unixuid
+################################################
+
+ntvfs_unixuid_OBJ_FILES = $(ntvfssrcdir)/unixuid/vfs_unixuid.o
diff --git a/source4/ntvfs/unixuid/vfs_unixuid.c b/source4/ntvfs/unixuid/vfs_unixuid.c
new file mode 100644
index 0000000000..db22a85492
--- /dev/null
+++ b/source4/ntvfs/unixuid/vfs_unixuid.c
@@ -0,0 +1,727 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ a pass-thru NTVFS module to setup a security context using unix
+ uid/gid
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "system/passwd.h"
+#include "auth/auth.h"
+#include "ntvfs/ntvfs.h"
+#include "libcli/wbclient/wbclient.h"
+
+struct unixuid_private {
+ struct wbc_context *wbc_ctx;
+ struct unix_sec_ctx *last_sec_ctx;
+ struct security_token *last_token;
+};
+
+
+
+struct unix_sec_ctx {
+ uid_t uid;
+ gid_t gid;
+ uint_t ngroups;
+ gid_t *groups;
+};
+
+/*
+ pull the current security context into a unix_sec_ctx
+*/
+static struct unix_sec_ctx *save_unix_security(TALLOC_CTX *mem_ctx)
+{
+ struct unix_sec_ctx *sec = talloc(mem_ctx, struct unix_sec_ctx);
+ if (sec == NULL) {
+ return NULL;
+ }
+ sec->uid = geteuid();
+ sec->gid = getegid();
+ sec->ngroups = getgroups(0, NULL);
+ if (sec->ngroups == -1) {
+ talloc_free(sec);
+ return NULL;
+ }
+ sec->groups = talloc_array(sec, gid_t, sec->ngroups);
+ if (sec->groups == NULL) {
+ talloc_free(sec);
+ return NULL;
+ }
+
+ if (getgroups(sec->ngroups, sec->groups) != sec->ngroups) {
+ talloc_free(sec);
+ return NULL;
+ }
+
+ return sec;
+}
+
+/*
+ set the current security context from a unix_sec_ctx
+*/
+static NTSTATUS set_unix_security(struct unix_sec_ctx *sec)
+{
+ seteuid(0);
+
+ if (setgroups(sec->ngroups, sec->groups) != 0) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ if (setegid(sec->gid) != 0) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ if (seteuid(sec->uid) != 0) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ return NT_STATUS_OK;
+}
+
+/*
+ form a unix_sec_ctx from the current security_token
+*/
+static NTSTATUS nt_token_to_unix_security(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ struct security_token *token,
+ struct unix_sec_ctx **sec)
+{
+ struct unixuid_private *priv = ntvfs->private_data;
+ int i;
+ NTSTATUS status;
+ struct id_mapping *ids;
+ struct composite_context *ctx;
+ *sec = talloc(req, struct unix_sec_ctx);
+
+ /* we can't do unix security without a user and group */
+ if (token->num_sids < 2) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ ids = talloc_array(req, struct id_mapping, token->num_sids);
+ NT_STATUS_HAVE_NO_MEMORY(ids);
+
+ ids[0].unixid = NULL;
+ ids[0].sid = token->user_sid;
+ ids[0].status = NT_STATUS_NONE_MAPPED;
+
+ ids[1].unixid = NULL;
+ ids[1].sid = token->group_sid;
+ ids[1].status = NT_STATUS_NONE_MAPPED;
+
+ (*sec)->ngroups = token->num_sids - 2;
+ (*sec)->groups = talloc_array(*sec, gid_t, (*sec)->ngroups);
+ NT_STATUS_HAVE_NO_MEMORY((*sec)->groups);
+
+ for (i=0;i<(*sec)->ngroups;i++) {
+ ids[i+2].unixid = NULL;
+ ids[i+2].sid = token->sids[i+2];
+ ids[i+2].status = NT_STATUS_NONE_MAPPED;
+ }
+
+ ctx = wbc_sids_to_xids_send(priv->wbc_ctx, ids, token->num_sids, ids);
+ NT_STATUS_HAVE_NO_MEMORY(ctx);
+
+ status = wbc_sids_to_xids_recv(ctx, &ids);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ if (ids[0].unixid->type == ID_TYPE_BOTH ||
+ ids[0].unixid->type == ID_TYPE_UID) {
+ (*sec)->uid = ids[0].unixid->id;
+ } else {
+ return NT_STATUS_INVALID_SID;
+ }
+
+ if (ids[1].unixid->type == ID_TYPE_BOTH ||
+ ids[1].unixid->type == ID_TYPE_GID) {
+ (*sec)->gid = ids[1].unixid->id;
+ } else {
+ return NT_STATUS_INVALID_SID;
+ }
+
+ for (i=0;i<(*sec)->ngroups;i++) {
+ if (ids[i+2].unixid->type == ID_TYPE_BOTH ||
+ ids[i+2].unixid->type == ID_TYPE_GID) {
+ (*sec)->groups[i] = ids[i+2].unixid->id;
+ } else {
+ return NT_STATUS_INVALID_SID;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ setup our unix security context according to the session authentication info
+*/
+static NTSTATUS unixuid_setup_security(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, struct unix_sec_ctx **sec)
+{
+ struct unixuid_private *priv = ntvfs->private_data;
+ struct security_token *token;
+ struct unix_sec_ctx *newsec;
+ NTSTATUS status;
+
+ if (req->session_info == NULL) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ token = req->session_info->security_token;
+
+ *sec = save_unix_security(ntvfs);
+ if (*sec == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (token == priv->last_token) {
+ newsec = priv->last_sec_ctx;
+ } else {
+ status = nt_token_to_unix_security(ntvfs, req, token, &newsec);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(*sec);
+ return status;
+ }
+ if (priv->last_sec_ctx) {
+ talloc_free(priv->last_sec_ctx);
+ }
+ priv->last_sec_ctx = newsec;
+ priv->last_token = token;
+ talloc_steal(priv, newsec);
+ }
+
+ status = set_unix_security(newsec);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(*sec);
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ this pass through macro operates on request contexts
+*/
+#define PASS_THRU_REQ(ntvfs, req, op, args) do { \
+ NTSTATUS status2; \
+ struct unix_sec_ctx *sec; \
+ status = unixuid_setup_security(ntvfs, req, &sec); \
+ NT_STATUS_NOT_OK_RETURN(status); \
+ status = ntvfs_next_##op args; \
+ status2 = set_unix_security(sec); \
+ talloc_free(sec); \
+ if (!NT_STATUS_IS_OK(status2)) smb_panic("Unable to reset security context"); \
+} while (0)
+
+
+
+/*
+ connect to a share - used when a tree_connect operation comes in.
+*/
+static NTSTATUS unixuid_connect(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, const char *sharename)
+{
+ struct unixuid_private *priv;
+ NTSTATUS status;
+
+ priv = talloc(ntvfs, struct unixuid_private);
+ if (!priv) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ priv->wbc_ctx = wbc_init(priv, ntvfs->ctx->msg_ctx,
+ ntvfs->ctx->event_ctx);
+ if (priv->wbc_ctx == NULL) {
+ talloc_free(priv);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ ntvfs->private_data = priv;
+ priv->last_sec_ctx = NULL;
+ priv->last_token = NULL;
+
+ /* we don't use PASS_THRU_REQ here, as the connect operation runs with
+ root privileges. This allows the backends to setup any database
+ links they might need during the connect. */
+ status = ntvfs_next_connect(ntvfs, req, sharename);
+
+ return status;
+}
+
+/*
+ disconnect from a share
+*/
+static NTSTATUS unixuid_disconnect(struct ntvfs_module_context *ntvfs)
+{
+ struct unixuid_private *priv = ntvfs->private_data;
+ NTSTATUS status;
+
+ talloc_free(priv);
+ ntvfs->private_data = NULL;
+
+ status = ntvfs_next_disconnect(ntvfs);
+
+ return status;
+}
+
+
+/*
+ delete a file
+*/
+static NTSTATUS unixuid_unlink(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_unlink *unl)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, unlink, (ntvfs, req, unl));
+
+ return status;
+}
+
+/*
+ ioctl interface
+*/
+static NTSTATUS unixuid_ioctl(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_ioctl *io)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, ioctl, (ntvfs, req, io));
+
+ return status;
+}
+
+/*
+ check if a directory exists
+*/
+static NTSTATUS unixuid_chkpath(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_chkpath *cp)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, chkpath, (ntvfs, req, cp));
+
+ return status;
+}
+
+/*
+ return info on a pathname
+*/
+static NTSTATUS unixuid_qpathinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_fileinfo *info)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, qpathinfo, (ntvfs, req, info));
+
+ return status;
+}
+
+/*
+ query info on a open file
+*/
+static NTSTATUS unixuid_qfileinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_fileinfo *info)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, qfileinfo, (ntvfs, req, info));
+
+ return status;
+}
+
+
+/*
+ set info on a pathname
+*/
+static NTSTATUS unixuid_setpathinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_setfileinfo *st)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, setpathinfo, (ntvfs, req, st));
+
+ return status;
+}
+
+/*
+ open a file
+*/
+static NTSTATUS unixuid_open(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_open *io)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, open, (ntvfs, req, io));
+
+ return status;
+}
+
+/*
+ create a directory
+*/
+static NTSTATUS unixuid_mkdir(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_mkdir *md)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, mkdir, (ntvfs, req, md));
+
+ return status;
+}
+
+/*
+ remove a directory
+*/
+static NTSTATUS unixuid_rmdir(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, struct smb_rmdir *rd)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, rmdir, (ntvfs, req, rd));
+
+ return status;
+}
+
+/*
+ rename a set of files
+*/
+static NTSTATUS unixuid_rename(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_rename *ren)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, rename, (ntvfs, req, ren));
+
+ return status;
+}
+
+/*
+ copy a set of files
+*/
+static NTSTATUS unixuid_copy(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, struct smb_copy *cp)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, copy, (ntvfs, req, cp));
+
+ return status;
+}
+
+/*
+ read from a file
+*/
+static NTSTATUS unixuid_read(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_read *rd)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, read, (ntvfs, req, rd));
+
+ return status;
+}
+
+/*
+ write to a file
+*/
+static NTSTATUS unixuid_write(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_write *wr)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, write, (ntvfs, req, wr));
+
+ return status;
+}
+
+/*
+ seek in a file
+*/
+static NTSTATUS unixuid_seek(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_seek *io)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, seek, (ntvfs, req, io));
+
+ return status;
+}
+
+/*
+ flush a file
+*/
+static NTSTATUS unixuid_flush(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_flush *io)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, flush, (ntvfs, req, io));
+
+ return status;
+}
+
+/*
+ close a file
+*/
+static NTSTATUS unixuid_close(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_close *io)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, close, (ntvfs, req, io));
+
+ return status;
+}
+
+/*
+ exit - closing files
+*/
+static NTSTATUS unixuid_exit(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, exit, (ntvfs, req));
+
+ return status;
+}
+
+/*
+ logoff - closing files
+*/
+static NTSTATUS unixuid_logoff(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req)
+{
+ struct unixuid_private *priv = ntvfs->private_data;
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, logoff, (ntvfs, req));
+
+ priv->last_token = NULL;
+
+ return status;
+}
+
+/*
+ async setup
+*/
+static NTSTATUS unixuid_async_setup(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ void *private_data)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, async_setup, (ntvfs, req, private_data));
+
+ return status;
+}
+
+/*
+ cancel an async request
+*/
+static NTSTATUS unixuid_cancel(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, cancel, (ntvfs, req));
+
+ return status;
+}
+
+/*
+ change notify
+*/
+static NTSTATUS unixuid_notify(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_notify *info)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, notify, (ntvfs, req, info));
+
+ return status;
+}
+
+/*
+ lock a byte range
+*/
+static NTSTATUS unixuid_lock(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_lock *lck)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, lock, (ntvfs, req, lck));
+
+ return status;
+}
+
+/*
+ set info on a open file
+*/
+static NTSTATUS unixuid_setfileinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_setfileinfo *info)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, setfileinfo, (ntvfs, req, info));
+
+ return status;
+}
+
+
+/*
+ return filesystem space info
+*/
+static NTSTATUS unixuid_fsinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_fsinfo *fs)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, fsinfo, (ntvfs, req, fs));
+
+ return status;
+}
+
+/*
+ return print queue info
+*/
+static NTSTATUS unixuid_lpq(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_lpq *lpq)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, lpq, (ntvfs, req, lpq));
+
+ return status;
+}
+
+/*
+ list files in a directory matching a wildcard pattern
+*/
+static NTSTATUS unixuid_search_first(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_search_first *io,
+ void *search_private,
+ bool (*callback)(void *, const union smb_search_data *))
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, search_first, (ntvfs, req, io, search_private, callback));
+
+ return status;
+}
+
+/* continue a search */
+static NTSTATUS unixuid_search_next(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_search_next *io,
+ void *search_private,
+ bool (*callback)(void *, const union smb_search_data *))
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, search_next, (ntvfs, req, io, search_private, callback));
+
+ return status;
+}
+
+/* close a search */
+static NTSTATUS unixuid_search_close(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_search_close *io)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, search_close, (ntvfs, req, io));
+
+ return status;
+}
+
+/* SMBtrans - not used on file shares */
+static NTSTATUS unixuid_trans(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, struct smb_trans2 *trans2)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, trans, (ntvfs, req, trans2));
+
+ return status;
+}
+
+/*
+ initialise the unixuid backend, registering ourselves with the ntvfs subsystem
+ */
+NTSTATUS ntvfs_unixuid_init(void)
+{
+ NTSTATUS ret;
+ struct ntvfs_ops ops;
+ NTVFS_CURRENT_CRITICAL_SIZES(vers);
+
+ ZERO_STRUCT(ops);
+
+ /* fill in all the operations */
+ ops.connect = unixuid_connect;
+ ops.disconnect = unixuid_disconnect;
+ ops.unlink = unixuid_unlink;
+ ops.chkpath = unixuid_chkpath;
+ ops.qpathinfo = unixuid_qpathinfo;
+ ops.setpathinfo = unixuid_setpathinfo;
+ ops.open = unixuid_open;
+ ops.mkdir = unixuid_mkdir;
+ ops.rmdir = unixuid_rmdir;
+ ops.rename = unixuid_rename;
+ ops.copy = unixuid_copy;
+ ops.ioctl = unixuid_ioctl;
+ ops.read = unixuid_read;
+ ops.write = unixuid_write;
+ ops.seek = unixuid_seek;
+ ops.flush = unixuid_flush;
+ ops.close = unixuid_close;
+ ops.exit = unixuid_exit;
+ ops.lock = unixuid_lock;
+ ops.setfileinfo = unixuid_setfileinfo;
+ ops.qfileinfo = unixuid_qfileinfo;
+ ops.fsinfo = unixuid_fsinfo;
+ ops.lpq = unixuid_lpq;
+ ops.search_first = unixuid_search_first;
+ ops.search_next = unixuid_search_next;
+ ops.search_close = unixuid_search_close;
+ ops.trans = unixuid_trans;
+ ops.logoff = unixuid_logoff;
+ ops.async_setup = unixuid_async_setup;
+ ops.cancel = unixuid_cancel;
+ ops.notify = unixuid_notify;
+
+ ops.name = "unixuid";
+
+ /* we register under all 3 backend types, as we are not type specific */
+ ops.type = NTVFS_DISK;
+ ret = ntvfs_register(&ops, &vers);
+ if (!NT_STATUS_IS_OK(ret)) goto failed;
+
+ ops.type = NTVFS_PRINT;
+ ret = ntvfs_register(&ops, &vers);
+ if (!NT_STATUS_IS_OK(ret)) goto failed;
+
+ ops.type = NTVFS_IPC;
+ ret = ntvfs_register(&ops, &vers);
+ if (!NT_STATUS_IS_OK(ret)) goto failed;
+
+failed:
+ return ret;
+}
diff --git a/source4/param/README b/source4/param/README
new file mode 100644
index 0000000000..403a217588
--- /dev/null
+++ b/source4/param/README
@@ -0,0 +1,4 @@
+This directory contains "libsamba-hostconfig".
+
+The libsamba-hostconfig library provides access to all host-wide configuration
+such as the configured shares, default parameter values and host secret keys.
diff --git a/source4/param/config.mk b/source4/param/config.mk
new file mode 100644
index 0000000000..d420a3883c
--- /dev/null
+++ b/source4/param/config.mk
@@ -0,0 +1,64 @@
+[LIBRARY::LIBSAMBA-HOSTCONFIG]
+PUBLIC_DEPENDENCIES = LIBSAMBA-UTIL
+PRIVATE_DEPENDENCIES = DYNCONFIG LIBREPLACE_EXT CHARSET
+
+LIBSAMBA-HOSTCONFIG_VERSION = 0.0.1
+LIBSAMBA-HOSTCONFIG_SOVERSION = 0
+
+LIBSAMBA-HOSTCONFIG_OBJ_FILES = $(addprefix $(paramsrcdir)/, \
+ loadparm.o generic.o util.o)
+
+PUBLIC_HEADERS += param/param.h
+
+PC_FILES += $(paramsrcdir)/samba-hostconfig.pc
+
+[SUBSYSTEM::PROVISION]
+PRIVATE_DEPENDENCIES = LIBPYTHON pyldb
+
+PROVISION_OBJ_FILES = $(paramsrcdir)/provision.o $(param_OBJ_FILES)
+
+#################################
+# Start SUBSYSTEM share
+[SUBSYSTEM::share]
+PRIVATE_DEPENDENCIES = LIBSAMBA-UTIL
+# End SUBSYSTEM share
+#################################
+
+share_OBJ_FILES = $(paramsrcdir)/share.o
+
+$(eval $(call proto_header_template,$(paramsrcdir)/share_proto.h,$(share_OBJ_FILES:.o=.c)))
+
+PUBLIC_HEADERS += param/share.h
+
+################################################
+# Start MODULE share_classic
+[MODULE::share_classic]
+SUBSYSTEM = share
+INIT_FUNCTION = share_classic_init
+PRIVATE_DEPENDENCIES = LIBSAMBA-UTIL
+# End MODULE share_classic
+################################################
+
+share_classic_OBJ_FILES = $(paramsrcdir)/share_classic.o
+
+################################################
+# Start MODULE share_ldb
+[MODULE::share_ldb]
+SUBSYSTEM = share
+INIT_FUNCTION = share_ldb_init
+PRIVATE_DEPENDENCIES = LIBLDB LDB_WRAP
+# End MODULE share_ldb
+################################################
+
+share_ldb_OBJ_FILES = $(paramsrcdir)/share_ldb.o
+
+[SUBSYSTEM::SECRETS]
+PRIVATE_DEPENDENCIES = LIBLDB TDB_WRAP UTIL_TDB NDR_SECURITY
+
+SECRETS_OBJ_FILES = $(paramsrcdir)/secrets.o
+
+[PYTHON::param]
+LIBRARY_REALNAME = samba/param.$(SHLIBEXT)
+PRIVATE_DEPENDENCIES = LIBSAMBA-HOSTCONFIG PYTALLOC
+
+param_OBJ_FILES = $(paramsrcdir)/pyparam.o
diff --git a/source4/param/generic.c b/source4/param/generic.c
new file mode 100644
index 0000000000..ba5464a0f0
--- /dev/null
+++ b/source4/param/generic.c
@@ -0,0 +1,294 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "../lib/util/dlinklist.h"
+#include "param/param.h"
+#include "param/loadparm.h"
+#include "system/filesys.h"
+
+struct param_section *param_get_section(struct param_context *ctx, const char *name)
+{
+ struct param_section *sect;
+
+ if (name == NULL)
+ name = GLOBAL_NAME;
+
+ for (sect = ctx->sections; sect; sect = sect->next) {
+ if (!strcasecmp_m(sect->name, name))
+ return sect;
+ }
+
+ return NULL;
+}
+
+struct param_opt *param_section_get(struct param_section *section,
+ const char *name)
+{
+ struct param_opt *p;
+
+ for (p = section->parameters; p; p = p->next) {
+ if (strcasecmp_m(p->key, name) == 0)
+ return p;
+ }
+
+ return NULL;
+}
+
+struct param_opt *param_get (struct param_context *ctx, const char *name, const char *section_name)
+{
+ struct param_section *section = param_get_section(ctx, section_name);
+ if (section == NULL)
+ return NULL;
+
+ return param_section_get(section, name);
+}
+
+struct param_section *param_add_section(struct param_context *ctx, const char *section_name)
+{
+ struct param_section *section;
+ section = talloc_zero(ctx, struct param_section);
+ if (section == NULL)
+ return NULL;
+
+ section->name = talloc_strdup(section, section_name);
+ DLIST_ADD_END(ctx->sections, section, struct param_section *);
+ return section;
+}
+
+/* Look up parameter. If it is not found, add it */
+struct param_opt *param_get_add(struct param_context *ctx, const char *name, const char *section_name)
+{
+ struct param_section *section;
+ struct param_opt *p;
+
+ SMB_ASSERT(section_name != NULL);
+ SMB_ASSERT(name != NULL);
+
+ section = param_get_section(ctx, section_name);
+
+ if (section == NULL) {
+ section = param_add_section(ctx, section_name);
+ }
+
+ p = param_section_get(section, name);
+ if (p == NULL) {
+ p = talloc_zero(section, struct param_opt);
+ if (p == NULL)
+ return NULL;
+
+ p->key = talloc_strdup(p, name);
+ DLIST_ADD_END(section->parameters, p, struct param_opt *);
+ }
+
+ return p;
+}
+
+const char *param_get_string(struct param_context *ctx, const char *param, const char *section)
+{
+ struct param_opt *p = param_get(ctx, param, section);
+
+ if (p == NULL)
+ return NULL;
+
+ return p->value;
+}
+
+int param_set_string(struct param_context *ctx, const char *param, const char *value, const char *section)
+{
+ struct param_opt *p = param_get_add(ctx, param, section);
+
+ if (p == NULL)
+ return -1;
+
+ p->value = talloc_strdup(p, value);
+
+ return 0;
+}
+
+const char **param_get_string_list(struct param_context *ctx, const char *param, const char *separator, const char *section)
+{
+ struct param_opt *p = param_get(ctx, param, section);
+
+ if (p == NULL)
+ return NULL;
+
+ return (const char **)str_list_make(ctx, p->value, separator);
+}
+
+int param_set_string_list(struct param_context *ctx, const char *param, const char **list, const char *section)
+{
+ struct param_opt *p = param_get_add(ctx, param, section);
+
+ p->value = str_list_join(p, list, ' ');
+
+ return 0;
+}
+
+int param_get_int(struct param_context *ctx, const char *param, int default_v, const char *section)
+{
+ const char *value = param_get_string(ctx, param, section);
+
+ if (value)
+ return strtol(value, NULL, 0);
+
+ return default_v;
+}
+
+void param_set_int(struct param_context *ctx, const char *param, int value, const char *section)
+{
+ struct param_opt *p = param_get_add(ctx, section, param);
+
+ if (!p)
+ return;
+
+ p->value = talloc_asprintf(p, "%d", value);
+}
+
+unsigned long param_get_ulong(struct param_context *ctx, const char *param, unsigned long default_v, const char *section)
+{
+ const char *value = param_get_string(ctx, param, section);
+
+ if (value)
+ return strtoul(value, NULL, 0);
+
+ return default_v;
+}
+
+void param_set_ulong(struct param_context *ctx, const char *name, unsigned long value, const char *section)
+{
+ struct param_opt *p = param_get_add(ctx, name, section);
+
+ if (!p)
+ return;
+
+ p->value = talloc_asprintf(p, "%lu", value);
+}
+
+static bool param_sfunc (const char *name, void *_ctx)
+{
+ struct param_context *ctx = (struct param_context *)_ctx;
+ struct param_section *section = param_get_section(ctx, name);
+
+ if (section == NULL) {
+ section = talloc_zero(ctx, struct param_section);
+ if (section == NULL)
+ return false;
+
+ section->name = talloc_strdup(section, name);
+
+ DLIST_ADD_END(ctx->sections, section, struct param_section *);
+ }
+
+ /* Make sure this section is on top of the list for param_pfunc */
+ DLIST_PROMOTE(ctx->sections, section);
+
+ return true;
+}
+
+static bool param_pfunc (const char *name, const char *value, void *_ctx)
+{
+ struct param_context *ctx = (struct param_context *)_ctx;
+ struct param_opt *p = param_section_get(ctx->sections, name);
+
+ if (!p) {
+ p = talloc_zero(ctx->sections, struct param_opt);
+ if (p == NULL)
+ return false;
+
+ p->key = talloc_strdup(p, name);
+ p->value = talloc_strdup(p, value);
+ DLIST_ADD(ctx->sections->parameters, p);
+ } else { /* Replace current value */
+ talloc_free(p->value);
+ p->value = talloc_strdup(p, value);
+ }
+
+ return true;
+}
+
+struct param_context *param_init(TALLOC_CTX *mem_ctx)
+{
+ return talloc_zero(mem_ctx, struct param_context);
+}
+
+
+int param_read(struct param_context *ctx, const char *fn)
+{
+ ctx->sections = talloc_zero(ctx, struct param_section);
+ if (ctx->sections == NULL)
+ return -1;
+
+ ctx->sections->name = talloc_strdup(ctx->sections, "global");
+ if (!pm_process( fn, param_sfunc, param_pfunc, ctx)) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int param_use(struct loadparm_context *lp_ctx, struct param_context *ctx)
+{
+ struct param_section *section;
+
+ for (section = ctx->sections; section; section = section->next) {
+ struct param_opt *param;
+ bool isglobal = strcmp(section->name, "global") == 0;
+ for (param = section->parameters; param; param = param->next) {
+ if (isglobal)
+ lp_do_global_parameter(lp_ctx, param->key,
+ param->value);
+ else {
+ struct loadparm_service *service =
+ lp_service(lp_ctx, section->name);
+ if (service == NULL)
+ service = lp_add_service(lp_ctx, lp_default_service(lp_ctx), section->name);
+ lp_do_service_parameter(lp_ctx, service, param->key, param->value);
+ }
+ }
+ }
+ return 0;
+}
+
+int param_write(struct param_context *ctx, const char *fn)
+{
+ int file;
+ struct param_section *section;
+
+ if (fn == NULL || ctx == NULL)
+ return -1;
+
+ file = open(fn, O_WRONLY|O_CREAT, 0755);
+
+ if (file == -1)
+ return -1;
+
+ for (section = ctx->sections; section; section = section->next) {
+ struct param_opt *param;
+
+ fdprintf(file, "[%s]\n", section->name);
+ for (param = section->parameters; param; param = param->next) {
+ fdprintf(file, "\t%s = %s\n", param->key, param->value);
+ }
+ fdprintf(file, "\n");
+ }
+
+ close(file);
+
+ return 0;
+}
diff --git a/source4/param/loadparm.c b/source4/param/loadparm.c
new file mode 100644
index 0000000000..6789aa1ac2
--- /dev/null
+++ b/source4/param/loadparm.c
@@ -0,0 +1,2724 @@
+/*
+ Unix SMB/CIFS implementation.
+ Parameter loading functions
+ Copyright (C) Karl Auer 1993-1998
+
+ Largely re-written by Andrew Tridgell, September 1994
+
+ Copyright (C) Simo Sorce 2001
+ Copyright (C) Alexander Bokovoy 2002
+ Copyright (C) Stefan (metze) Metzmacher 2002
+ Copyright (C) Jim McDonough (jmcd@us.ibm.com) 2003.
+ Copyright (C) James Myers 2003 <myersjj@samba.org>
+ Copyright (C) Jelmer Vernooij <jelmer@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Load parameters.
+ *
+ * This module provides suitable callback functions for the params
+ * module. It builds the internal table of service details which is
+ * then used by the rest of the server.
+ *
+ * To add a parameter:
+ *
+ * 1) add it to the global or service structure definition
+ * 2) add it to the parm_table
+ * 3) add it to the list of available functions (eg: using FN_GLOBAL_STRING())
+ * 4) If it's a global then initialise it in init_globals. If a local
+ * (ie. service) parameter then initialise it in the sDefault structure
+ *
+ *
+ * Notes:
+ * The configuration file is processed sequentially for speed. It is NOT
+ * accessed randomly as happens in 'real' Windows. For this reason, there
+ * is a fair bit of sequence-dependent code here - ie., code which assumes
+ * that certain things happen before others. In particular, the code which
+ * happens at the boundary between sections is delicately poised, so be
+ * careful!
+ *
+ */
+
+#include "includes.h"
+#include "version.h"
+#include "dynconfig/dynconfig.h"
+#include "system/time.h"
+#include "system/locale.h"
+#include "system/network.h" /* needed for TCP_NODELAY */
+#include "smb_server/smb_server.h"
+#include "libcli/raw/signing.h"
+#include "../lib/util/dlinklist.h"
+#include "param/param.h"
+#include "param/loadparm.h"
+#include "libcli/raw/libcliraw.h"
+#include "rpc_server/common/common.h"
+#include "lib/socket/socket.h"
+#include "auth/gensec/gensec.h"
+
+#define standard_sub_basic talloc_strdup
+
+static bool do_parameter(const char *, const char *, void *);
+static bool defaults_saved = false;
+
+/**
+ * This structure describes global (ie., server-wide) parameters.
+ */
+struct loadparm_global
+{
+ enum server_role server_role;
+
+ const char **smb_ports;
+ char *ncalrpc_dir;
+ char *dos_charset;
+ char *unix_charset;
+ char *display_charset;
+ char *szLockDir;
+ char *szModulesDir;
+ char *szPidDir;
+ char *szSetupDir;
+ char *szServerString;
+ char *szAutoServices;
+ char *szPasswdChat;
+ char *szShareBackend;
+ char *szSAM_URL;
+ char *szIDMAP_URL;
+ char *szSECRETS_URL;
+ char *szSPOOLSS_URL;
+ char *szWINS_CONFIG_URL;
+ char *szWINS_URL;
+ char *szPrivateDir;
+ const char **szPasswordServers;
+ char *szSocketOptions;
+ char *szRealm;
+ const char **szWINSservers;
+ const char **szInterfaces;
+ char *szSocketAddress;
+ char *szAnnounceVersion; /* This is initialised in init_globals */
+ char *szWorkgroup;
+ char *szNetbiosName;
+ const char **szNetbiosAliases;
+ char *szNetbiosScope;
+ char *szDomainOtherSIDs;
+ const char **szNameResolveOrder;
+ const char **dcerpc_ep_servers;
+ const char **server_services;
+ char *ntptr_providor;
+ char *szWinbindSeparator;
+ char *szWinbinddPrivilegedSocketDirectory;
+ char *szWinbinddSocketDirectory;
+ char *szTemplateShell;
+ char *szTemplateHomedir;
+ int bWinbindSealedPipes;
+ int bIdmapTrustedOnly;
+ char *swat_directory;
+ int tls_enabled;
+ char *tls_keyfile;
+ char *tls_certfile;
+ char *tls_cafile;
+ char *tls_crlfile;
+ char *tls_dhpfile;
+ char *logfile;
+ char *panic_action;
+ int max_mux;
+ int debuglevel;
+ int max_xmit;
+ int pwordlevel;
+ int srv_maxprotocol;
+ int srv_minprotocol;
+ int cli_maxprotocol;
+ int cli_minprotocol;
+ int security;
+ int paranoid_server_security;
+ int max_wins_ttl;
+ int min_wins_ttl;
+ int announce_as; /* This is initialised in init_globals */
+ int nbt_port;
+ int dgram_port;
+ int cldap_port;
+ int krb5_port;
+ int kpasswd_port;
+ int web_port;
+ char *socket_options;
+ int bWINSsupport;
+ int bWINSdnsProxy;
+ char *szWINSHook;
+ int bLocalMaster;
+ int bPreferredMaster;
+ int bEncryptPasswords;
+ int bNullPasswords;
+ int bObeyPamRestrictions;
+ int bLargeReadwrite;
+ int bReadRaw;
+ int bWriteRaw;
+ int bTimeServer;
+ int bBindInterfacesOnly;
+ int bNTSmbSupport;
+ int bNTStatusSupport;
+ int bLanmanAuth;
+ int bNTLMAuth;
+ int bUseSpnego;
+ int server_signing;
+ int client_signing;
+ int bClientPlaintextAuth;
+ int bClientLanManAuth;
+ int bClientNTLMv2Auth;
+ int client_use_spnego_principal;
+ int bHostMSDfs;
+ int bUnicode;
+ int bUnixExtensions;
+ int bDisableNetbios;
+ int bRpcBigEndian;
+ char *szNTPSignDSocketDirectory;
+ struct param_opt *param_opt;
+};
+
+
+/**
+ * This structure describes a single service.
+ */
+struct loadparm_service
+{
+ char *szService;
+ char *szPath;
+ char *szCopy;
+ char *szInclude;
+ char *szPrintername;
+ char **szHostsallow;
+ char **szHostsdeny;
+ char *comment;
+ char *volume;
+ char *fstype;
+ char **ntvfs_handler;
+ int iMaxPrintJobs;
+ int iMaxConnections;
+ int iCSCPolicy;
+ int bAvailable;
+ int bBrowseable;
+ int bRead_only;
+ int bPrint_ok;
+ int bMap_system;
+ int bMap_hidden;
+ int bMap_archive;
+ int bStrictLocking;
+ int bOplocks;
+ int iCreate_mask;
+ int iCreate_force_mode;
+ int iDir_mask;
+ int iDir_force_mode;
+ int *copymap;
+ int bMSDfsRoot;
+ int bStrictSync;
+ int bCIFileSystem;
+ struct param_opt *param_opt;
+
+ char dummy[3]; /* for alignment */
+};
+
+
+#define NUMPARAMETERS (sizeof(parm_table) / sizeof(struct parm_struct))
+
+
+/* prototypes for the special type handlers */
+static bool handle_include(struct loadparm_context *lp_ctx,
+ const char *pszParmValue, char **ptr);
+static bool handle_copy(struct loadparm_context *lp_ctx,
+ const char *pszParmValue, char **ptr);
+static bool handle_debuglevel(struct loadparm_context *lp_ctx,
+ const char *pszParmValue, char **ptr);
+static bool handle_logfile(struct loadparm_context *lp_ctx,
+ const char *pszParmValue, char **ptr);
+
+static const struct enum_list enum_protocol[] = {
+ {PROTOCOL_SMB2, "SMB2"},
+ {PROTOCOL_NT1, "NT1"},
+ {PROTOCOL_LANMAN2, "LANMAN2"},
+ {PROTOCOL_LANMAN1, "LANMAN1"},
+ {PROTOCOL_CORE, "CORE"},
+ {PROTOCOL_COREPLUS, "COREPLUS"},
+ {PROTOCOL_COREPLUS, "CORE+"},
+ {-1, NULL}
+};
+
+static const struct enum_list enum_security[] = {
+ {SEC_SHARE, "SHARE"},
+ {SEC_USER, "USER"},
+ {-1, NULL}
+};
+
+static const struct enum_list enum_announce_as[] = {
+ {ANNOUNCE_AS_NT_SERVER, "NT"},
+ {ANNOUNCE_AS_NT_SERVER, "NT Server"},
+ {ANNOUNCE_AS_NT_WORKSTATION, "NT Workstation"},
+ {ANNOUNCE_AS_WIN95, "win95"},
+ {ANNOUNCE_AS_WFW, "WfW"},
+ {-1, NULL}
+};
+
+static const struct enum_list enum_bool_auto[] = {
+ {false, "No"},
+ {false, "False"},
+ {false, "0"},
+ {true, "Yes"},
+ {true, "True"},
+ {true, "1"},
+ {Auto, "Auto"},
+ {-1, NULL}
+};
+
+/* Client-side offline caching policy types */
+enum csc_policy {
+ CSC_POLICY_MANUAL=0,
+ CSC_POLICY_DOCUMENTS=1,
+ CSC_POLICY_PROGRAMS=2,
+ CSC_POLICY_DISABLE=3
+};
+
+static const struct enum_list enum_csc_policy[] = {
+ {CSC_POLICY_MANUAL, "manual"},
+ {CSC_POLICY_DOCUMENTS, "documents"},
+ {CSC_POLICY_PROGRAMS, "programs"},
+ {CSC_POLICY_DISABLE, "disable"},
+ {-1, NULL}
+};
+
+/* SMB signing types. */
+static const struct enum_list enum_smb_signing_vals[] = {
+ {SMB_SIGNING_OFF, "No"},
+ {SMB_SIGNING_OFF, "False"},
+ {SMB_SIGNING_OFF, "0"},
+ {SMB_SIGNING_OFF, "Off"},
+ {SMB_SIGNING_OFF, "disabled"},
+ {SMB_SIGNING_SUPPORTED, "Yes"},
+ {SMB_SIGNING_SUPPORTED, "True"},
+ {SMB_SIGNING_SUPPORTED, "1"},
+ {SMB_SIGNING_SUPPORTED, "On"},
+ {SMB_SIGNING_SUPPORTED, "enabled"},
+ {SMB_SIGNING_REQUIRED, "required"},
+ {SMB_SIGNING_REQUIRED, "mandatory"},
+ {SMB_SIGNING_REQUIRED, "force"},
+ {SMB_SIGNING_REQUIRED, "forced"},
+ {SMB_SIGNING_REQUIRED, "enforced"},
+ {SMB_SIGNING_AUTO, "auto"},
+ {-1, NULL}
+};
+
+static const struct enum_list enum_server_role[] = {
+ {ROLE_STANDALONE, "standalone"},
+ {ROLE_DOMAIN_MEMBER, "member server"},
+ {ROLE_DOMAIN_MEMBER, "member"},
+ {ROLE_DOMAIN_CONTROLLER, "domain controller"},
+ {ROLE_DOMAIN_CONTROLLER, "dc"},
+ {-1, NULL}
+};
+
+
+#define GLOBAL_VAR(name) offsetof(struct loadparm_global, name)
+#define LOCAL_VAR(name) offsetof(struct loadparm_service, name)
+
+static struct parm_struct parm_table[] = {
+ {"server role", P_ENUM, P_GLOBAL, GLOBAL_VAR(server_role), NULL, enum_server_role},
+
+ {"dos charset", P_STRING, P_GLOBAL, GLOBAL_VAR(dos_charset), NULL, NULL},
+ {"unix charset", P_STRING, P_GLOBAL, GLOBAL_VAR(unix_charset), NULL, NULL},
+ {"ncalrpc dir", P_STRING, P_GLOBAL, GLOBAL_VAR(ncalrpc_dir), NULL, NULL},
+ {"display charset", P_STRING, P_GLOBAL, GLOBAL_VAR(display_charset), NULL, NULL},
+ {"comment", P_STRING, P_LOCAL, LOCAL_VAR(comment), NULL, NULL},
+ {"path", P_STRING, P_LOCAL, LOCAL_VAR(szPath), NULL, NULL},
+ {"directory", P_STRING, P_LOCAL, LOCAL_VAR(szPath), NULL, NULL},
+ {"workgroup", P_USTRING, P_GLOBAL, GLOBAL_VAR(szWorkgroup), NULL, NULL},
+ {"realm", P_STRING, P_GLOBAL, GLOBAL_VAR(szRealm), NULL, NULL},
+ {"netbios name", P_USTRING, P_GLOBAL, GLOBAL_VAR(szNetbiosName), NULL, NULL},
+ {"netbios aliases", P_LIST, P_GLOBAL, GLOBAL_VAR(szNetbiosAliases), NULL, NULL},
+ {"netbios scope", P_USTRING, P_GLOBAL, GLOBAL_VAR(szNetbiosScope), NULL, NULL},
+ {"server string", P_STRING, P_GLOBAL, GLOBAL_VAR(szServerString), NULL, NULL},
+ {"interfaces", P_LIST, P_GLOBAL, GLOBAL_VAR(szInterfaces), NULL, NULL},
+ {"bind interfaces only", P_BOOL, P_GLOBAL, GLOBAL_VAR(bBindInterfacesOnly), NULL, NULL},
+ {"ntvfs handler", P_LIST, P_LOCAL, LOCAL_VAR(ntvfs_handler), NULL, NULL},
+ {"ntptr providor", P_STRING, P_GLOBAL, GLOBAL_VAR(ntptr_providor), NULL, NULL},
+ {"dcerpc endpoint servers", P_LIST, P_GLOBAL, GLOBAL_VAR(dcerpc_ep_servers), NULL, NULL},
+ {"server services", P_LIST, P_GLOBAL, GLOBAL_VAR(server_services), NULL, NULL},
+
+ {"security", P_ENUM, P_GLOBAL, GLOBAL_VAR(security), NULL, enum_security},
+ {"encrypt passwords", P_BOOL, P_GLOBAL, GLOBAL_VAR(bEncryptPasswords), NULL, NULL},
+ {"null passwords", P_BOOL, P_GLOBAL, GLOBAL_VAR(bNullPasswords), NULL, NULL},
+ {"obey pam restrictions", P_BOOL, P_GLOBAL, GLOBAL_VAR(bObeyPamRestrictions), NULL, NULL},
+ {"password server", P_LIST, P_GLOBAL, GLOBAL_VAR(szPasswordServers), NULL, NULL},
+ {"sam database", P_STRING, P_GLOBAL, GLOBAL_VAR(szSAM_URL), NULL, NULL},
+ {"idmap database", P_STRING, P_GLOBAL, GLOBAL_VAR(szIDMAP_URL), NULL, NULL},
+ {"secrets database", P_STRING, P_GLOBAL, GLOBAL_VAR(szSECRETS_URL), NULL, NULL},
+ {"spoolss database", P_STRING, P_GLOBAL, GLOBAL_VAR(szSPOOLSS_URL), NULL, NULL},
+ {"wins config database", P_STRING, P_GLOBAL, GLOBAL_VAR(szWINS_CONFIG_URL), NULL, NULL},
+ {"wins database", P_STRING, P_GLOBAL, GLOBAL_VAR(szWINS_URL), NULL, NULL},
+ {"private dir", P_STRING, P_GLOBAL, GLOBAL_VAR(szPrivateDir), NULL, NULL},
+ {"passwd chat", P_STRING, P_GLOBAL, GLOBAL_VAR(szPasswdChat), NULL, NULL},
+ {"password level", P_INTEGER, P_GLOBAL, GLOBAL_VAR(pwordlevel), NULL, NULL},
+ {"lanman auth", P_BOOL, P_GLOBAL, GLOBAL_VAR(bLanmanAuth), NULL, NULL},
+ {"ntlm auth", P_BOOL, P_GLOBAL, GLOBAL_VAR(bNTLMAuth), NULL, NULL},
+ {"client NTLMv2 auth", P_BOOL, P_GLOBAL, GLOBAL_VAR(bClientNTLMv2Auth), NULL, NULL},
+ {"client lanman auth", P_BOOL, P_GLOBAL, GLOBAL_VAR(bClientLanManAuth), NULL, NULL},
+ {"client plaintext auth", P_BOOL, P_GLOBAL, GLOBAL_VAR(bClientPlaintextAuth), NULL, NULL},
+ {"client use spnego principal", P_BOOL, P_GLOBAL, GLOBAL_VAR(client_use_spnego_principal), NULL, NULL},
+
+ {"read only", P_BOOL, P_LOCAL, LOCAL_VAR(bRead_only), NULL, NULL},
+
+ {"create mask", P_OCTAL, P_LOCAL, LOCAL_VAR(iCreate_mask), NULL, NULL},
+ {"force create mode", P_OCTAL, P_LOCAL, LOCAL_VAR(iCreate_force_mode), NULL, NULL},
+ {"directory mask", P_OCTAL, P_LOCAL, LOCAL_VAR(iDir_mask), NULL, NULL},
+ {"force directory mode", P_OCTAL, P_LOCAL, LOCAL_VAR(iDir_force_mode), NULL, NULL},
+
+ {"hosts allow", P_LIST, P_LOCAL, LOCAL_VAR(szHostsallow), NULL, NULL},
+ {"hosts deny", P_LIST, P_LOCAL, LOCAL_VAR(szHostsdeny), NULL, NULL},
+
+ {"log level", P_INTEGER, P_GLOBAL, GLOBAL_VAR(debuglevel), handle_debuglevel, NULL},
+ {"debuglevel", P_INTEGER, P_GLOBAL, GLOBAL_VAR(debuglevel), handle_debuglevel, NULL},
+ {"log file", P_STRING, P_GLOBAL, GLOBAL_VAR(logfile), handle_logfile, NULL},
+
+ {"smb ports", P_LIST, P_GLOBAL, GLOBAL_VAR(smb_ports), NULL, NULL},
+ {"nbt port", P_INTEGER, P_GLOBAL, GLOBAL_VAR(nbt_port), NULL, NULL},
+ {"dgram port", P_INTEGER, P_GLOBAL, GLOBAL_VAR(dgram_port), NULL, NULL},
+ {"cldap port", P_INTEGER, P_GLOBAL, GLOBAL_VAR(cldap_port), NULL, NULL},
+ {"krb5 port", P_INTEGER, P_GLOBAL, GLOBAL_VAR(krb5_port), NULL, NULL},
+ {"kpasswd port", P_INTEGER, P_GLOBAL, GLOBAL_VAR(kpasswd_port), NULL, NULL},
+ {"web port", P_INTEGER, P_GLOBAL, GLOBAL_VAR(web_port), NULL, NULL},
+ {"tls enabled", P_BOOL, P_GLOBAL, GLOBAL_VAR(tls_enabled), NULL, NULL},
+ {"tls keyfile", P_STRING, P_GLOBAL, GLOBAL_VAR(tls_keyfile), NULL, NULL},
+ {"tls certfile", P_STRING, P_GLOBAL, GLOBAL_VAR(tls_certfile), NULL, NULL},
+ {"tls cafile", P_STRING, P_GLOBAL, GLOBAL_VAR(tls_cafile), NULL, NULL},
+ {"tls crlfile", P_STRING, P_GLOBAL, GLOBAL_VAR(tls_crlfile), NULL, NULL},
+ {"tls dh params file", P_STRING, P_GLOBAL, GLOBAL_VAR(tls_dhpfile), NULL, NULL},
+ {"swat directory", P_STRING, P_GLOBAL, GLOBAL_VAR(swat_directory), NULL, NULL},
+ {"large readwrite", P_BOOL, P_GLOBAL, GLOBAL_VAR(bLargeReadwrite), NULL, NULL},
+ {"server max protocol", P_ENUM, P_GLOBAL, GLOBAL_VAR(srv_maxprotocol), NULL, enum_protocol},
+ {"server min protocol", P_ENUM, P_GLOBAL, GLOBAL_VAR(srv_minprotocol), NULL, enum_protocol},
+ {"client max protocol", P_ENUM, P_GLOBAL, GLOBAL_VAR(cli_maxprotocol), NULL, enum_protocol},
+ {"client min protocol", P_ENUM, P_GLOBAL, GLOBAL_VAR(cli_minprotocol), NULL, enum_protocol},
+ {"unicode", P_BOOL, P_GLOBAL, GLOBAL_VAR(bUnicode), NULL, NULL},
+ {"read raw", P_BOOL, P_GLOBAL, GLOBAL_VAR(bReadRaw), NULL, NULL},
+ {"write raw", P_BOOL, P_GLOBAL, GLOBAL_VAR(bWriteRaw), NULL, NULL},
+ {"disable netbios", P_BOOL, P_GLOBAL, GLOBAL_VAR(bDisableNetbios), NULL, NULL},
+
+ {"nt status support", P_BOOL, P_GLOBAL, GLOBAL_VAR(bNTStatusSupport), NULL, NULL},
+
+ {"announce version", P_STRING, P_GLOBAL, GLOBAL_VAR(szAnnounceVersion), NULL, NULL},
+ {"announce as", P_ENUM, P_GLOBAL, GLOBAL_VAR(announce_as), NULL, enum_announce_as},
+ {"max mux", P_INTEGER, P_GLOBAL, GLOBAL_VAR(max_mux), NULL, NULL},
+ {"max xmit", P_BYTES, P_GLOBAL, GLOBAL_VAR(max_xmit), NULL, NULL},
+
+ {"name resolve order", P_LIST, P_GLOBAL, GLOBAL_VAR(szNameResolveOrder), NULL, NULL},
+ {"max wins ttl", P_INTEGER, P_GLOBAL, GLOBAL_VAR(max_wins_ttl), NULL, NULL},
+ {"min wins ttl", P_INTEGER, P_GLOBAL, GLOBAL_VAR(min_wins_ttl), NULL, NULL},
+ {"time server", P_BOOL, P_GLOBAL, GLOBAL_VAR(bTimeServer), NULL, NULL},
+ {"unix extensions", P_BOOL, P_GLOBAL, GLOBAL_VAR(bUnixExtensions), NULL, NULL},
+ {"use spnego", P_BOOL, P_GLOBAL, GLOBAL_VAR(bUseSpnego), NULL, NULL},
+ {"server signing", P_ENUM, P_GLOBAL, GLOBAL_VAR(server_signing), NULL, enum_smb_signing_vals},
+ {"client signing", P_ENUM, P_GLOBAL, GLOBAL_VAR(client_signing), NULL, enum_smb_signing_vals},
+ {"rpc big endian", P_BOOL, P_GLOBAL, GLOBAL_VAR(bRpcBigEndian), NULL, NULL},
+
+ {"max connections", P_INTEGER, P_LOCAL, LOCAL_VAR(iMaxConnections), NULL, NULL},
+ {"paranoid server security", P_BOOL, P_GLOBAL, GLOBAL_VAR(paranoid_server_security), NULL, NULL},
+ {"socket options", P_STRING, P_GLOBAL, GLOBAL_VAR(socket_options), NULL, NULL},
+
+ {"strict sync", P_BOOL, P_LOCAL, LOCAL_VAR(bStrictSync), NULL, NULL},
+ {"case insensitive filesystem", P_BOOL, P_LOCAL, LOCAL_VAR(bCIFileSystem), NULL, NULL},
+
+ {"max print jobs", P_INTEGER, P_LOCAL, LOCAL_VAR(iMaxPrintJobs), NULL, NULL},
+ {"printable", P_BOOL, P_LOCAL, LOCAL_VAR(bPrint_ok), NULL, NULL},
+ {"print ok", P_BOOL, P_LOCAL, LOCAL_VAR(bPrint_ok), NULL, NULL},
+
+ {"printer name", P_STRING, P_LOCAL, LOCAL_VAR(szPrintername), NULL, NULL},
+ {"printer", P_STRING, P_LOCAL, LOCAL_VAR(szPrintername), NULL, NULL},
+
+ {"map system", P_BOOL, P_LOCAL, LOCAL_VAR(bMap_system), NULL, NULL},
+ {"map hidden", P_BOOL, P_LOCAL, LOCAL_VAR(bMap_hidden), NULL, NULL},
+ {"map archive", P_BOOL, P_LOCAL, LOCAL_VAR(bMap_archive), NULL, NULL},
+
+ {"preferred master", P_ENUM, P_GLOBAL, GLOBAL_VAR(bPreferredMaster), NULL, enum_bool_auto},
+ {"prefered master", P_ENUM, P_GLOBAL, GLOBAL_VAR(bPreferredMaster), NULL, enum_bool_auto},
+ {"local master", P_BOOL, P_GLOBAL, GLOBAL_VAR(bLocalMaster), NULL, NULL},
+ {"browseable", P_BOOL, P_LOCAL, LOCAL_VAR(bBrowseable), NULL, NULL},
+ {"browsable", P_BOOL, P_LOCAL, LOCAL_VAR(bBrowseable), NULL, NULL},
+
+ {"wins server", P_LIST, P_GLOBAL, GLOBAL_VAR(szWINSservers), NULL, NULL},
+ {"wins support", P_BOOL, P_GLOBAL, GLOBAL_VAR(bWINSsupport), NULL, NULL},
+ {"dns proxy", P_BOOL, P_GLOBAL, GLOBAL_VAR(bWINSdnsProxy), NULL, NULL},
+ {"wins hook", P_STRING, P_GLOBAL, GLOBAL_VAR(szWINSHook), NULL, NULL},
+
+ {"csc policy", P_ENUM, P_LOCAL, LOCAL_VAR(iCSCPolicy), NULL, enum_csc_policy},
+
+ {"strict locking", P_BOOL, P_LOCAL, LOCAL_VAR(bStrictLocking), NULL, NULL},
+ {"oplocks", P_BOOL, P_LOCAL, LOCAL_VAR(bOplocks), NULL, NULL},
+
+ {"share backend", P_STRING, P_GLOBAL, GLOBAL_VAR(szShareBackend), NULL, NULL},
+ {"preload", P_STRING, P_GLOBAL, GLOBAL_VAR(szAutoServices), NULL, NULL},
+ {"auto services", P_STRING, P_GLOBAL, GLOBAL_VAR(szAutoServices), NULL, NULL},
+ {"lock dir", P_STRING, P_GLOBAL, GLOBAL_VAR(szLockDir), NULL, NULL},
+ {"lock directory", P_STRING, P_GLOBAL, GLOBAL_VAR(szLockDir), NULL, NULL},
+ {"modules dir", P_STRING, P_GLOBAL, GLOBAL_VAR(szModulesDir), NULL, NULL},
+ {"pid directory", P_STRING, P_GLOBAL, GLOBAL_VAR(szPidDir), NULL, NULL},
+ {"setup directory", P_STRING, P_GLOBAL, GLOBAL_VAR(szSetupDir), NULL, NULL},
+
+ {"socket address", P_STRING, P_GLOBAL, GLOBAL_VAR(szSocketAddress), NULL, NULL},
+ {"copy", P_STRING, P_LOCAL, LOCAL_VAR(szCopy), handle_copy, NULL},
+ {"include", P_STRING, P_LOCAL, LOCAL_VAR(szInclude), handle_include, NULL},
+
+ {"available", P_BOOL, P_LOCAL, LOCAL_VAR(bAvailable), NULL, NULL},
+ {"volume", P_STRING, P_LOCAL, LOCAL_VAR(volume), NULL, NULL },
+ {"fstype", P_STRING, P_LOCAL, LOCAL_VAR(fstype), NULL, NULL},
+
+ {"panic action", P_STRING, P_GLOBAL, GLOBAL_VAR(panic_action), NULL, NULL},
+
+ {"msdfs root", P_BOOL, P_LOCAL, LOCAL_VAR(bMSDfsRoot), NULL, NULL},
+ {"host msdfs", P_BOOL, P_GLOBAL, GLOBAL_VAR(bHostMSDfs), NULL, NULL},
+ {"winbind separator", P_STRING, P_GLOBAL, GLOBAL_VAR(szWinbindSeparator), NULL, NULL },
+ {"winbindd socket directory", P_STRING, P_GLOBAL, GLOBAL_VAR(szWinbinddSocketDirectory), NULL, NULL },
+ {"winbindd privileged socket directory", P_STRING, P_GLOBAL, GLOBAL_VAR(szWinbinddPrivilegedSocketDirectory), NULL, NULL },
+ {"winbind sealed pipes", P_BOOL, P_GLOBAL, GLOBAL_VAR(bWinbindSealedPipes), NULL, NULL },
+ {"template shell", P_STRING, P_GLOBAL, GLOBAL_VAR(szTemplateShell), NULL, NULL },
+ {"template homedir", P_STRING, P_GLOBAL, GLOBAL_VAR(szTemplateHomedir), NULL, NULL },
+ {"idmap trusted only", P_BOOL, P_GLOBAL, GLOBAL_VAR(bIdmapTrustedOnly), NULL, NULL},
+
+ {"ntp signd socket directory", P_STRING, P_GLOBAL, GLOBAL_VAR(szNTPSignDSocketDirectory), NULL, NULL },
+
+ {NULL, P_BOOL, P_NONE, 0, NULL, NULL}
+};
+
+
+/* local variables */
+struct loadparm_context {
+ const char *szConfigFile;
+ struct loadparm_global *globals;
+ struct loadparm_service **services;
+ struct loadparm_service *sDefault;
+ int iNumServices;
+ struct loadparm_service *currentService;
+ bool bInGlobalSection;
+ struct file_lists {
+ struct file_lists *next;
+ char *name;
+ char *subfname;
+ time_t modtime;
+ } *file_lists;
+ unsigned int flags[NUMPARAMETERS];
+ struct smb_iconv_convenience *iconv_convenience;
+};
+
+
+struct loadparm_service *lp_default_service(struct loadparm_context *lp_ctx)
+{
+ return lp_ctx->sDefault;
+}
+
+/*
+ return the parameter table
+*/
+struct parm_struct *lp_parm_table(void)
+{
+ return parm_table;
+}
+
+/**
+ * Convenience routine to grab string parameters into temporary memory
+ * and run standard_sub_basic on them.
+ *
+ * The buffers can be written to by
+ * callers without affecting the source string.
+ */
+
+static const char *lp_string(const char *s)
+{
+#if 0 /* until REWRITE done to make thread-safe */
+ size_t len = s ? strlen(s) : 0;
+ char *ret;
+#endif
+
+ /* The follow debug is useful for tracking down memory problems
+ especially if you have an inner loop that is calling a lp_*()
+ function that returns a string. Perhaps this debug should be
+ present all the time? */
+
+#if 0
+ DEBUG(10, ("lp_string(%s)\n", s));
+#endif
+
+#if 0 /* until REWRITE done to make thread-safe */
+ if (!lp_talloc)
+ lp_talloc = talloc_init("lp_talloc");
+
+ ret = talloc_array(lp_talloc, char, len + 100); /* leave room for substitution */
+
+ if (!ret)
+ return NULL;
+
+ if (!s)
+ *ret = 0;
+ else
+ strlcpy(ret, s, len);
+
+ if (trim_string(ret, "\"", "\"")) {
+ if (strchr(ret,'"') != NULL)
+ strlcpy(ret, s, len);
+ }
+
+ standard_sub_basic(ret,len+100);
+ return (ret);
+#endif
+ return s;
+}
+
+/*
+ In this section all the functions that are used to access the
+ parameters from the rest of the program are defined
+*/
+
+#define FN_GLOBAL_STRING(fn_name,var_name) \
+ const char *fn_name(struct loadparm_context *lp_ctx) {if (lp_ctx == NULL) return NULL; return lp_ctx->globals->var_name ? lp_string(lp_ctx->globals->var_name) : "";}
+#define FN_GLOBAL_CONST_STRING(fn_name,var_name) \
+ const char *fn_name(struct loadparm_context *lp_ctx) {if (lp_ctx == NULL) return NULL; return lp_ctx->globals->var_name ? lp_ctx->globals->var_name : "";}
+#define FN_GLOBAL_LIST(fn_name,var_name) \
+ const char **fn_name(struct loadparm_context *lp_ctx) {if (lp_ctx == NULL) return NULL; return lp_ctx->globals->var_name;}
+#define FN_GLOBAL_BOOL(fn_name,var_name) \
+ bool fn_name(struct loadparm_context *lp_ctx) {if (lp_ctx == NULL) return false; return lp_ctx->globals->var_name;}
+#if 0 /* unused */
+#define FN_GLOBAL_CHAR(fn_name,ptr) \
+ char fn_name(void) {return(*(char *)(ptr));}
+#endif
+#define FN_GLOBAL_INTEGER(fn_name,var_name) \
+ int fn_name(struct loadparm_context *lp_ctx) {return lp_ctx->globals->var_name;}
+
+#define FN_LOCAL_STRING(fn_name,val) \
+ const char *fn_name(struct loadparm_service *service, struct loadparm_service *sDefault) {return(lp_string((const char *)((service != NULL && service->val != NULL) ? service->val : sDefault->val)));}
+#define FN_LOCAL_LIST(fn_name,val) \
+ const char **fn_name(struct loadparm_service *service, struct loadparm_service *sDefault) {return(const char **)(service != NULL && service->val != NULL? service->val : sDefault->val);}
+#define FN_LOCAL_BOOL(fn_name,val) \
+ bool fn_name(struct loadparm_service *service, struct loadparm_service *sDefault) {return((service != NULL)? service->val : sDefault->val);}
+#define FN_LOCAL_INTEGER(fn_name,val) \
+ int fn_name(struct loadparm_service *service, struct loadparm_service *sDefault) {return((service != NULL)? service->val : sDefault->val);}
+
+_PUBLIC_ FN_GLOBAL_INTEGER(lp_server_role, server_role)
+_PUBLIC_ FN_GLOBAL_LIST(lp_smb_ports, smb_ports)
+_PUBLIC_ FN_GLOBAL_INTEGER(lp_nbt_port, nbt_port)
+_PUBLIC_ FN_GLOBAL_INTEGER(lp_dgram_port, dgram_port)
+_PUBLIC_ FN_GLOBAL_INTEGER(lp_cldap_port, cldap_port)
+_PUBLIC_ FN_GLOBAL_INTEGER(lp_krb5_port, krb5_port)
+_PUBLIC_ FN_GLOBAL_INTEGER(lp_kpasswd_port, kpasswd_port)
+_PUBLIC_ FN_GLOBAL_INTEGER(lp_web_port, web_port)
+_PUBLIC_ FN_GLOBAL_STRING(lp_swat_directory, swat_directory)
+_PUBLIC_ FN_GLOBAL_BOOL(lp_tls_enabled, tls_enabled)
+_PUBLIC_ FN_GLOBAL_STRING(lp_share_backend, szShareBackend)
+_PUBLIC_ FN_GLOBAL_STRING(lp_sam_url, szSAM_URL)
+_PUBLIC_ FN_GLOBAL_STRING(lp_idmap_url, szIDMAP_URL)
+_PUBLIC_ FN_GLOBAL_STRING(lp_secrets_url, szSECRETS_URL)
+_PUBLIC_ FN_GLOBAL_STRING(lp_spoolss_url, szSPOOLSS_URL)
+_PUBLIC_ FN_GLOBAL_STRING(lp_wins_config_url, szWINS_CONFIG_URL)
+_PUBLIC_ FN_GLOBAL_STRING(lp_wins_url, szWINS_URL)
+_PUBLIC_ FN_GLOBAL_CONST_STRING(lp_winbind_separator, szWinbindSeparator)
+_PUBLIC_ FN_GLOBAL_CONST_STRING(lp_winbindd_socket_directory, szWinbinddSocketDirectory)
+_PUBLIC_ FN_GLOBAL_CONST_STRING(lp_winbindd_privileged_socket_directory, szWinbinddPrivilegedSocketDirectory)
+_PUBLIC_ FN_GLOBAL_CONST_STRING(lp_template_shell, szTemplateShell)
+_PUBLIC_ FN_GLOBAL_CONST_STRING(lp_template_homedir, szTemplateHomedir)
+_PUBLIC_ FN_GLOBAL_BOOL(lp_winbind_sealed_pipes, bWinbindSealedPipes)
+_PUBLIC_ FN_GLOBAL_BOOL(lp_idmap_trusted_only, bIdmapTrustedOnly)
+_PUBLIC_ FN_GLOBAL_STRING(lp_private_dir, szPrivateDir)
+_PUBLIC_ FN_GLOBAL_STRING(lp_serverstring, szServerString)
+_PUBLIC_ FN_GLOBAL_STRING(lp_lockdir, szLockDir)
+_PUBLIC_ FN_GLOBAL_STRING(lp_modulesdir, szModulesDir)
+_PUBLIC_ FN_GLOBAL_STRING(lp_setupdir, szSetupDir)
+_PUBLIC_ FN_GLOBAL_STRING(lp_ncalrpc_dir, ncalrpc_dir)
+_PUBLIC_ FN_GLOBAL_STRING(lp_dos_charset, dos_charset)
+_PUBLIC_ FN_GLOBAL_STRING(lp_unix_charset, unix_charset)
+_PUBLIC_ FN_GLOBAL_STRING(lp_display_charset, display_charset)
+_PUBLIC_ FN_GLOBAL_STRING(lp_piddir, szPidDir)
+_PUBLIC_ FN_GLOBAL_LIST(lp_dcerpc_endpoint_servers, dcerpc_ep_servers)
+_PUBLIC_ FN_GLOBAL_LIST(lp_server_services, server_services)
+_PUBLIC_ FN_GLOBAL_STRING(lp_ntptr_providor, ntptr_providor)
+_PUBLIC_ FN_GLOBAL_STRING(lp_auto_services, szAutoServices)
+_PUBLIC_ FN_GLOBAL_STRING(lp_passwd_chat, szPasswdChat)
+_PUBLIC_ FN_GLOBAL_LIST(lp_passwordserver, szPasswordServers)
+_PUBLIC_ FN_GLOBAL_LIST(lp_name_resolve_order, szNameResolveOrder)
+_PUBLIC_ FN_GLOBAL_STRING(lp_realm, szRealm)
+_PUBLIC_ FN_GLOBAL_STRING(lp_socket_options, socket_options)
+_PUBLIC_ FN_GLOBAL_STRING(lp_workgroup, szWorkgroup)
+_PUBLIC_ FN_GLOBAL_STRING(lp_netbios_name, szNetbiosName)
+_PUBLIC_ FN_GLOBAL_STRING(lp_netbios_scope, szNetbiosScope)
+_PUBLIC_ FN_GLOBAL_LIST(lp_wins_server_list, szWINSservers)
+_PUBLIC_ FN_GLOBAL_LIST(lp_interfaces, szInterfaces)
+_PUBLIC_ FN_GLOBAL_STRING(lp_socket_address, szSocketAddress)
+_PUBLIC_ FN_GLOBAL_LIST(lp_netbios_aliases, szNetbiosAliases)
+
+_PUBLIC_ FN_GLOBAL_BOOL(lp_disable_netbios, bDisableNetbios)
+_PUBLIC_ FN_GLOBAL_BOOL(lp_wins_support, bWINSsupport)
+_PUBLIC_ FN_GLOBAL_BOOL(lp_wins_dns_proxy, bWINSdnsProxy)
+_PUBLIC_ FN_GLOBAL_STRING(lp_wins_hook, szWINSHook)
+_PUBLIC_ FN_GLOBAL_BOOL(lp_local_master, bLocalMaster)
+_PUBLIC_ FN_GLOBAL_BOOL(lp_readraw, bReadRaw)
+_PUBLIC_ FN_GLOBAL_BOOL(lp_large_readwrite, bLargeReadwrite)
+_PUBLIC_ FN_GLOBAL_BOOL(lp_writeraw, bWriteRaw)
+_PUBLIC_ FN_GLOBAL_BOOL(lp_null_passwords, bNullPasswords)
+_PUBLIC_ FN_GLOBAL_BOOL(lp_obey_pam_restrictions, bObeyPamRestrictions)
+_PUBLIC_ FN_GLOBAL_BOOL(lp_encrypted_passwords, bEncryptPasswords)
+_PUBLIC_ FN_GLOBAL_BOOL(lp_time_server, bTimeServer)
+_PUBLIC_ FN_GLOBAL_BOOL(lp_bind_interfaces_only, bBindInterfacesOnly)
+_PUBLIC_ FN_GLOBAL_BOOL(lp_unicode, bUnicode)
+_PUBLIC_ FN_GLOBAL_BOOL(lp_nt_status_support, bNTStatusSupport)
+_PUBLIC_ FN_GLOBAL_BOOL(lp_lanman_auth, bLanmanAuth)
+_PUBLIC_ FN_GLOBAL_BOOL(lp_ntlm_auth, bNTLMAuth)
+_PUBLIC_ FN_GLOBAL_BOOL(lp_client_plaintext_auth, bClientPlaintextAuth)
+_PUBLIC_ FN_GLOBAL_BOOL(lp_client_lanman_auth, bClientLanManAuth)
+_PUBLIC_ FN_GLOBAL_BOOL(lp_client_ntlmv2_auth, bClientNTLMv2Auth)
+_PUBLIC_ FN_GLOBAL_BOOL(lp_client_use_spnego_principal, client_use_spnego_principal)
+_PUBLIC_ FN_GLOBAL_BOOL(lp_host_msdfs, bHostMSDfs)
+_PUBLIC_ FN_GLOBAL_BOOL(lp_unix_extensions, bUnixExtensions)
+_PUBLIC_ FN_GLOBAL_BOOL(lp_use_spnego, bUseSpnego)
+_PUBLIC_ FN_GLOBAL_BOOL(lp_rpc_big_endian, bRpcBigEndian)
+_PUBLIC_ FN_GLOBAL_INTEGER(lp_max_wins_ttl, max_wins_ttl)
+_PUBLIC_ FN_GLOBAL_INTEGER(lp_min_wins_ttl, min_wins_ttl)
+_PUBLIC_ FN_GLOBAL_INTEGER(lp_maxmux, max_mux)
+_PUBLIC_ FN_GLOBAL_INTEGER(lp_max_xmit, max_xmit)
+_PUBLIC_ FN_GLOBAL_INTEGER(lp_passwordlevel, pwordlevel)
+_PUBLIC_ FN_GLOBAL_INTEGER(lp_srv_maxprotocol, srv_maxprotocol)
+_PUBLIC_ FN_GLOBAL_INTEGER(lp_srv_minprotocol, srv_minprotocol)
+_PUBLIC_ FN_GLOBAL_INTEGER(lp_cli_maxprotocol, cli_maxprotocol)
+_PUBLIC_ FN_GLOBAL_INTEGER(lp_cli_minprotocol, cli_minprotocol)
+_PUBLIC_ FN_GLOBAL_INTEGER(lp_security, security)
+_PUBLIC_ FN_GLOBAL_BOOL(lp_paranoid_server_security, paranoid_server_security)
+_PUBLIC_ FN_GLOBAL_INTEGER(lp_announce_as, announce_as)
+const char *lp_servicename(const struct loadparm_service *service)
+{
+ return lp_string((const char *)service->szService);
+}
+
+_PUBLIC_ FN_LOCAL_STRING(lp_pathname, szPath)
+static FN_LOCAL_STRING(_lp_printername, szPrintername)
+_PUBLIC_ FN_LOCAL_LIST(lp_hostsallow, szHostsallow)
+_PUBLIC_ FN_LOCAL_LIST(lp_hostsdeny, szHostsdeny)
+_PUBLIC_ FN_LOCAL_STRING(lp_comment, comment)
+_PUBLIC_ FN_LOCAL_STRING(lp_fstype, fstype)
+static FN_LOCAL_STRING(lp_volume, volume)
+_PUBLIC_ FN_LOCAL_LIST(lp_ntvfs_handler, ntvfs_handler)
+_PUBLIC_ FN_LOCAL_BOOL(lp_msdfs_root, bMSDfsRoot)
+_PUBLIC_ FN_LOCAL_BOOL(lp_browseable, bBrowseable)
+_PUBLIC_ FN_LOCAL_BOOL(lp_readonly, bRead_only)
+_PUBLIC_ FN_LOCAL_BOOL(lp_print_ok, bPrint_ok)
+_PUBLIC_ FN_LOCAL_BOOL(lp_map_hidden, bMap_hidden)
+_PUBLIC_ FN_LOCAL_BOOL(lp_map_archive, bMap_archive)
+_PUBLIC_ FN_LOCAL_BOOL(lp_strict_locking, bStrictLocking)
+_PUBLIC_ FN_LOCAL_BOOL(lp_oplocks, bOplocks)
+_PUBLIC_ FN_LOCAL_BOOL(lp_strict_sync, bStrictSync)
+_PUBLIC_ FN_LOCAL_BOOL(lp_ci_filesystem, bCIFileSystem)
+_PUBLIC_ FN_LOCAL_BOOL(lp_map_system, bMap_system)
+_PUBLIC_ FN_LOCAL_INTEGER(lp_max_connections, iMaxConnections)
+_PUBLIC_ FN_LOCAL_INTEGER(lp_csc_policy, iCSCPolicy)
+_PUBLIC_ FN_LOCAL_INTEGER(lp_create_mask, iCreate_mask)
+_PUBLIC_ FN_LOCAL_INTEGER(lp_force_create_mode, iCreate_force_mode)
+_PUBLIC_ FN_LOCAL_INTEGER(lp_dir_mask, iDir_mask)
+_PUBLIC_ FN_LOCAL_INTEGER(lp_force_dir_mode, iDir_force_mode)
+_PUBLIC_ FN_GLOBAL_INTEGER(lp_server_signing, server_signing)
+_PUBLIC_ FN_GLOBAL_INTEGER(lp_client_signing, client_signing)
+
+_PUBLIC_ FN_GLOBAL_CONST_STRING(lp_ntp_signd_socket_directory, szNTPSignDSocketDirectory)
+
+/* local prototypes */
+static int map_parameter(const char *pszParmName);
+static struct loadparm_service *getservicebyname(struct loadparm_context *lp_ctx,
+ const char *pszServiceName);
+static void copy_service(struct loadparm_service *pserviceDest,
+ struct loadparm_service *pserviceSource,
+ int *pcopymapDest);
+static bool service_ok(struct loadparm_service *service);
+static bool do_section(const char *pszSectionName, void *);
+static void init_copymap(struct loadparm_service *pservice);
+
+/* This is a helper function for parametrical options support. */
+/* It returns a pointer to parametrical option value if it exists or NULL otherwise */
+/* Actual parametrical functions are quite simple */
+const char *lp_get_parametric(struct loadparm_context *lp_ctx,
+ struct loadparm_service *service,
+ const char *type, const char *option)
+{
+ char *vfskey;
+ struct param_opt *data;
+
+ if (lp_ctx == NULL)
+ return NULL;
+
+ data = (service == NULL ? lp_ctx->globals->param_opt : service->param_opt);
+
+ asprintf(&vfskey, "%s:%s", type, option);
+ strlower(vfskey);
+
+ while (data) {
+ if (strcmp(data->key, vfskey) == 0) {
+ free(vfskey);
+ return data->value;
+ }
+ data = data->next;
+ }
+
+ if (service != NULL) {
+ /* Try to fetch the same option but from globals */
+ /* but only if we are not already working with globals */
+ for (data = lp_ctx->globals->param_opt; data;
+ data = data->next) {
+ if (strcmp(data->key, vfskey) == 0) {
+ free(vfskey);
+ return data->value;
+ }
+ }
+ }
+
+ free(vfskey);
+
+ return NULL;
+}
+
+
+/**
+ * convenience routine to return int parameters.
+ */
+static int lp_int(const char *s)
+{
+
+ if (!s) {
+ DEBUG(0,("lp_int(%s): is called with NULL!\n",s));
+ return -1;
+ }
+
+ return strtol(s, NULL, 0);
+}
+
+/**
+ * convenience routine to return unsigned long parameters.
+ */
+static int lp_ulong(const char *s)
+{
+
+ if (!s) {
+ DEBUG(0,("lp_int(%s): is called with NULL!\n",s));
+ return -1;
+ }
+
+ return strtoul(s, NULL, 0);
+}
+
+/**
+ * convenience routine to return unsigned long parameters.
+ */
+static double lp_double(const char *s)
+{
+
+ if (!s) {
+ DEBUG(0,("lp_double(%s): is called with NULL!\n",s));
+ return -1;
+ }
+
+ return strtod(s, NULL);
+}
+
+/**
+ * convenience routine to return boolean parameters.
+ */
+static bool lp_bool(const char *s)
+{
+ bool ret = false;
+
+ if (!s) {
+ DEBUG(0,("lp_bool(%s): is called with NULL!\n",s));
+ return false;
+ }
+
+ if (!set_boolean(s, &ret)) {
+ DEBUG(0,("lp_bool(%s): value is not boolean!\n",s));
+ return false;
+ }
+
+ return ret;
+}
+
+
+/**
+ * Return parametric option from a given service. Type is a part of option before ':'
+ * Parametric option has following syntax: 'Type: option = value'
+ * Returned value is allocated in 'lp_talloc' context
+ */
+
+const char *lp_parm_string(struct loadparm_context *lp_ctx,
+ struct loadparm_service *service, const char *type,
+ const char *option)
+{
+ const char *value = lp_get_parametric(lp_ctx, service, type, option);
+
+ if (value)
+ return lp_string(value);
+
+ return NULL;
+}
+
+/**
+ * Return parametric option from a given service. Type is a part of option before ':'
+ * Parametric option has following syntax: 'Type: option = value'
+ * Returned value is allocated in 'lp_talloc' context
+ */
+
+const char **lp_parm_string_list(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ struct loadparm_service *service,
+ const char *type,
+ const char *option, const char *separator)
+{
+ const char *value = lp_get_parametric(lp_ctx, service, type, option);
+
+ if (value != NULL)
+ return (const char **)str_list_make(mem_ctx, value, separator);
+
+ return NULL;
+}
+
+/**
+ * Return parametric option from a given service. Type is a part of option before ':'
+ * Parametric option has following syntax: 'Type: option = value'
+ */
+
+int lp_parm_int(struct loadparm_context *lp_ctx,
+ struct loadparm_service *service, const char *type,
+ const char *option, int default_v)
+{
+ const char *value = lp_get_parametric(lp_ctx, service, type, option);
+
+ if (value)
+ return lp_int(value);
+
+ return default_v;
+}
+
+/**
+ * Return parametric option from a given service. Type is a part of
+ * option before ':'.
+ * Parametric option has following syntax: 'Type: option = value'.
+ */
+
+int lp_parm_bytes(struct loadparm_context *lp_ctx,
+ struct loadparm_service *service, const char *type,
+ const char *option, int default_v)
+{
+ uint64_t bval;
+
+ const char *value = lp_get_parametric(lp_ctx, service, type, option);
+
+ if (value && conv_str_size(value, &bval)) {
+ if (bval <= INT_MAX) {
+ return (int)bval;
+ }
+ }
+
+ return default_v;
+}
+
+/**
+ * Return parametric option from a given service.
+ * Type is a part of option before ':'
+ * Parametric option has following syntax: 'Type: option = value'
+ */
+unsigned long lp_parm_ulong(struct loadparm_context *lp_ctx,
+ struct loadparm_service *service, const char *type,
+ const char *option, unsigned long default_v)
+{
+ const char *value = lp_get_parametric(lp_ctx, service, type, option);
+
+ if (value)
+ return lp_ulong(value);
+
+ return default_v;
+}
+
+
+double lp_parm_double(struct loadparm_context *lp_ctx,
+ struct loadparm_service *service, const char *type,
+ const char *option, double default_v)
+{
+ const char *value = lp_get_parametric(lp_ctx, service, type, option);
+
+ if (value != NULL)
+ return lp_double(value);
+
+ return default_v;
+}
+
+/**
+ * Return parametric option from a given service. Type is a part of option before ':'
+ * Parametric option has following syntax: 'Type: option = value'
+ */
+
+bool lp_parm_bool(struct loadparm_context *lp_ctx,
+ struct loadparm_service *service, const char *type,
+ const char *option, bool default_v)
+{
+ const char *value = lp_get_parametric(lp_ctx, service, type, option);
+
+ if (value != NULL)
+ return lp_bool(value);
+
+ return default_v;
+}
+
+
+/**
+ * Initialise a service to the defaults.
+ */
+
+static struct loadparm_service *init_service(TALLOC_CTX *mem_ctx, struct loadparm_service *sDefault)
+{
+ struct loadparm_service *pservice =
+ talloc_zero(mem_ctx, struct loadparm_service);
+ copy_service(pservice, sDefault, NULL);
+ return pservice;
+}
+
+/**
+ * Set a string value, deallocating any existing space, and allocing the space
+ * for the string
+ */
+static bool string_set(TALLOC_CTX *mem_ctx, char **dest, const char *src)
+{
+ talloc_free(*dest);
+
+ if (src == NULL)
+ src = "";
+
+ *dest = talloc_strdup(mem_ctx, src);
+ if ((*dest) == NULL) {
+ DEBUG(0,("Out of memory in string_init\n"));
+ return false;
+ }
+
+ return true;
+}
+
+
+
+/**
+ * Add a new service to the services array initialising it with the given
+ * service.
+ */
+
+struct loadparm_service *lp_add_service(struct loadparm_context *lp_ctx,
+ const struct loadparm_service *pservice,
+ const char *name)
+{
+ int i;
+ struct loadparm_service tservice;
+ int num_to_alloc = lp_ctx->iNumServices + 1;
+ struct param_opt *data, *pdata;
+
+ tservice = *pservice;
+
+ /* it might already exist */
+ if (name) {
+ struct loadparm_service *service = getservicebyname(lp_ctx,
+ name);
+ if (service != NULL) {
+ /* Clean all parametric options for service */
+ /* They will be added during parsing again */
+ data = service->param_opt;
+ while (data) {
+ pdata = data->next;
+ talloc_free(data);
+ data = pdata;
+ }
+ service->param_opt = NULL;
+ return service;
+ }
+ }
+
+ /* find an invalid one */
+ for (i = 0; i < lp_ctx->iNumServices; i++)
+ if (lp_ctx->services[i] == NULL)
+ break;
+
+ /* if not, then create one */
+ if (i == lp_ctx->iNumServices) {
+ struct loadparm_service **tsp;
+
+ tsp = talloc_realloc(lp_ctx, lp_ctx->services, struct loadparm_service *, num_to_alloc);
+
+ if (!tsp) {
+ DEBUG(0,("lp_add_service: failed to enlarge services!\n"));
+ return NULL;
+ } else {
+ lp_ctx->services = tsp;
+ lp_ctx->services[lp_ctx->iNumServices] = NULL;
+ }
+
+ lp_ctx->iNumServices++;
+ }
+
+ lp_ctx->services[i] = init_service(lp_ctx->services, lp_ctx->sDefault);
+ if (lp_ctx->services[i] == NULL) {
+ DEBUG(0,("lp_add_service: out of memory!\n"));
+ return NULL;
+ }
+ copy_service(lp_ctx->services[i], &tservice, NULL);
+ if (name != NULL)
+ string_set(lp_ctx->services[i], &lp_ctx->services[i]->szService, name);
+ return lp_ctx->services[i];
+}
+
+/**
+ * Add a new home service, with the specified home directory, defaults coming
+ * from service ifrom.
+ */
+
+bool lp_add_home(struct loadparm_context *lp_ctx,
+ const char *pszHomename,
+ struct loadparm_service *default_service,
+ const char *user, const char *pszHomedir)
+{
+ struct loadparm_service *service;
+
+ service = lp_add_service(lp_ctx, default_service, pszHomename);
+
+ if (service == NULL)
+ return false;
+
+ if (!(*(default_service->szPath))
+ || strequal(default_service->szPath, lp_ctx->sDefault->szPath)) {
+ service->szPath = talloc_strdup(service, pszHomedir);
+ } else {
+ service->szPath = string_sub_talloc(service, lp_pathname(default_service, lp_ctx->sDefault), "%H", pszHomedir);
+ }
+
+ if (!(*(service->comment))) {
+ service->comment = talloc_asprintf(service, "Home directory of %s", user);
+ }
+ service->bAvailable = default_service->bAvailable;
+ service->bBrowseable = default_service->bBrowseable;
+
+ DEBUG(3, ("adding home's share [%s] for user '%s' at '%s'\n",
+ pszHomename, user, service->szPath));
+
+ return true;
+}
+
+/**
+ * Add the IPC service.
+ */
+
+static bool lp_add_hidden(struct loadparm_context *lp_ctx, const char *name,
+ const char *fstype)
+{
+ struct loadparm_service *service = lp_add_service(lp_ctx, lp_ctx->sDefault, name);
+
+ if (service == NULL)
+ return false;
+
+ string_set(service, &service->szPath, tmpdir());
+
+ service->comment = talloc_asprintf(service, "%s Service (%s)",
+ fstype, lp_ctx->globals->szServerString);
+ string_set(service, &service->fstype, fstype);
+ service->iMaxConnections = -1;
+ service->bAvailable = true;
+ service->bRead_only = true;
+ service->bPrint_ok = false;
+ service->bBrowseable = false;
+
+ if (strcasecmp(fstype, "IPC") == 0) {
+ lp_do_service_parameter(lp_ctx, service, "ntvfs handler",
+ "default");
+ }
+
+ DEBUG(3, ("adding hidden service %s\n", name));
+
+ return true;
+}
+
+/**
+ * Add a new printer service, with defaults coming from service iFrom.
+ */
+
+bool lp_add_printer(struct loadparm_context *lp_ctx,
+ const char *pszPrintername,
+ struct loadparm_service *default_service)
+{
+ const char *comment = "From Printcap";
+ struct loadparm_service *service;
+ service = lp_add_service(lp_ctx, default_service, pszPrintername);
+
+ if (service == NULL)
+ return false;
+
+ /* note that we do NOT default the availability flag to True - */
+ /* we take it from the default service passed. This allows all */
+ /* dynamic printers to be disabled by disabling the [printers] */
+ /* entry (if/when the 'available' keyword is implemented!). */
+
+ /* the printer name is set to the service name. */
+ string_set(service, &service->szPrintername, pszPrintername);
+ string_set(service, &service->comment, comment);
+ service->bBrowseable = default_service->bBrowseable;
+ /* Printers cannot be read_only. */
+ service->bRead_only = false;
+ /* Printer services must be printable. */
+ service->bPrint_ok = true;
+
+ DEBUG(3, ("adding printer service %s\n", pszPrintername));
+
+ return true;
+}
+
+/**
+ * Map a parameter's string representation to something we can use.
+ * Returns False if the parameter string is not recognised, else TRUE.
+ */
+
+static int map_parameter(const char *pszParmName)
+{
+ int iIndex;
+
+ if (*pszParmName == '-')
+ return -1;
+
+ for (iIndex = 0; parm_table[iIndex].label; iIndex++)
+ if (strwicmp(parm_table[iIndex].label, pszParmName) == 0)
+ return iIndex;
+
+ /* Warn only if it isn't parametric option */
+ if (strchr(pszParmName, ':') == NULL)
+ DEBUG(0, ("Unknown parameter encountered: \"%s\"\n", pszParmName));
+ /* We do return 'fail' for parametric options as well because they are
+ stored in different storage
+ */
+ return -1;
+}
+
+
+/**
+ return the parameter structure for a parameter
+*/
+struct parm_struct *lp_parm_struct(const char *name)
+{
+ int parmnum = map_parameter(name);
+ if (parmnum == -1) return NULL;
+ return &parm_table[parmnum];
+}
+
+/**
+ return the parameter pointer for a parameter
+*/
+void *lp_parm_ptr(struct loadparm_context *lp_ctx,
+ struct loadparm_service *service, struct parm_struct *parm)
+{
+ if (service == NULL) {
+ if (parm->pclass == P_LOCAL)
+ return ((char *)lp_ctx->sDefault)+parm->offset;
+ else if (parm->pclass == P_GLOBAL)
+ return ((char *)lp_ctx->globals)+parm->offset;
+ else return NULL;
+ } else {
+ return ((char *)service) + parm->offset;
+ }
+}
+
+/**
+ * Find a service by name. Otherwise works like get_service.
+ */
+
+static struct loadparm_service *getservicebyname(struct loadparm_context *lp_ctx,
+ const char *pszServiceName)
+{
+ int iService;
+
+ for (iService = lp_ctx->iNumServices - 1; iService >= 0; iService--)
+ if (lp_ctx->services[iService] != NULL &&
+ strwicmp(lp_ctx->services[iService]->szService, pszServiceName) == 0) {
+ return lp_ctx->services[iService];
+ }
+
+ return NULL;
+}
+
+/**
+ * Copy a service structure to another.
+ * If pcopymapDest is NULL then copy all fields
+ */
+
+static void copy_service(struct loadparm_service *pserviceDest,
+ struct loadparm_service *pserviceSource,
+ int *pcopymapDest)
+{
+ int i;
+ bool bcopyall = (pcopymapDest == NULL);
+ struct param_opt *data, *pdata, *paramo;
+ bool not_added;
+
+ for (i = 0; parm_table[i].label; i++)
+ if (parm_table[i].offset != -1 && parm_table[i].pclass == P_LOCAL &&
+ (bcopyall || pcopymapDest[i])) {
+ void *src_ptr =
+ ((char *)pserviceSource) + parm_table[i].offset;
+ void *dest_ptr =
+ ((char *)pserviceDest) + parm_table[i].offset;
+
+ switch (parm_table[i].type) {
+ case P_BOOL:
+ *(int *)dest_ptr = *(int *)src_ptr;
+ break;
+
+ case P_INTEGER:
+ case P_OCTAL:
+ case P_ENUM:
+ *(int *)dest_ptr = *(int *)src_ptr;
+ break;
+
+ case P_STRING:
+ string_set(pserviceDest,
+ (char **)dest_ptr,
+ *(char **)src_ptr);
+ break;
+
+ case P_USTRING:
+ string_set(pserviceDest,
+ (char **)dest_ptr,
+ *(char **)src_ptr);
+ strupper(*(char **)dest_ptr);
+ break;
+ case P_LIST:
+ *(const char ***)dest_ptr = (const char **)str_list_copy(pserviceDest,
+ *(const char ***)src_ptr);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (bcopyall) {
+ init_copymap(pserviceDest);
+ if (pserviceSource->copymap)
+ memcpy((void *)pserviceDest->copymap,
+ (void *)pserviceSource->copymap,
+ sizeof(int) * NUMPARAMETERS);
+ }
+
+ data = pserviceSource->param_opt;
+ while (data) {
+ not_added = true;
+ pdata = pserviceDest->param_opt;
+ /* Traverse destination */
+ while (pdata) {
+ /* If we already have same option, override it */
+ if (strcmp(pdata->key, data->key) == 0) {
+ talloc_free(pdata->value);
+ pdata->value = talloc_reference(pdata,
+ data->value);
+ not_added = false;
+ break;
+ }
+ pdata = pdata->next;
+ }
+ if (not_added) {
+ paramo = talloc(pserviceDest, struct param_opt);
+ if (paramo == NULL)
+ smb_panic("OOM");
+ paramo->key = talloc_reference(paramo, data->key);
+ paramo->value = talloc_reference(paramo, data->value);
+ DLIST_ADD(pserviceDest->param_opt, paramo);
+ }
+ data = data->next;
+ }
+}
+
+/**
+ * Check a service for consistency. Return False if the service is in any way
+ * incomplete or faulty, else True.
+ */
+static bool service_ok(struct loadparm_service *service)
+{
+ bool bRetval;
+
+ bRetval = true;
+ if (service->szService[0] == '\0') {
+ DEBUG(0, ("The following message indicates an internal error:\n"));
+ DEBUG(0, ("No service name in service entry.\n"));
+ bRetval = false;
+ }
+
+ /* The [printers] entry MUST be printable. I'm all for flexibility, but */
+ /* I can't see why you'd want a non-printable printer service... */
+ if (strwicmp(service->szService, PRINTERS_NAME) == 0) {
+ if (!service->bPrint_ok) {
+ DEBUG(0, ("WARNING: [%s] service MUST be printable!\n",
+ service->szService));
+ service->bPrint_ok = true;
+ }
+ /* [printers] service must also be non-browsable. */
+ if (service->bBrowseable)
+ service->bBrowseable = false;
+ }
+
+ /* If a service is flagged unavailable, log the fact at level 0. */
+ if (!service->bAvailable)
+ DEBUG(1, ("NOTE: Service %s is flagged unavailable.\n",
+ service->szService));
+
+ return bRetval;
+}
+
+
+/*******************************************************************
+ Keep a linked list of all config files so we know when one has changed
+ it's date and needs to be reloaded.
+********************************************************************/
+
+static void add_to_file_list(struct loadparm_context *lp_ctx,
+ const char *fname, const char *subfname)
+{
+ struct file_lists *f = lp_ctx->file_lists;
+
+ while (f) {
+ if (f->name && !strcmp(f->name, fname))
+ break;
+ f = f->next;
+ }
+
+ if (!f) {
+ f = talloc(lp_ctx, struct file_lists);
+ if (!f)
+ return;
+ f->next = lp_ctx->file_lists;
+ f->name = talloc_strdup(f, fname);
+ if (!f->name) {
+ talloc_free(f);
+ return;
+ }
+ f->subfname = talloc_strdup(f, subfname);
+ if (!f->subfname) {
+ talloc_free(f);
+ return;
+ }
+ lp_ctx->file_lists = f;
+ f->modtime = file_modtime(subfname);
+ } else {
+ time_t t = file_modtime(subfname);
+ if (t)
+ f->modtime = t;
+ }
+}
+
+/*******************************************************************
+ Check if a config file has changed date.
+********************************************************************/
+bool lp_file_list_changed(struct loadparm_context *lp_ctx)
+{
+ struct file_lists *f;
+ DEBUG(6, ("lp_file_list_changed()\n"));
+
+ for (f = lp_ctx->file_lists; f != NULL; f = f->next) {
+ char *n2;
+ time_t mod_time;
+
+ n2 = standard_sub_basic(lp_ctx, f->name);
+
+ DEBUGADD(6, ("file %s -> %s last mod_time: %s\n",
+ f->name, n2, ctime(&f->modtime)));
+
+ mod_time = file_modtime(n2);
+
+ if (mod_time && ((f->modtime != mod_time) || (f->subfname == NULL) || (strcmp(n2, f->subfname) != 0))) {
+ DEBUGADD(6, ("file %s modified: %s\n", n2,
+ ctime(&mod_time)));
+ f->modtime = mod_time;
+ talloc_free(f->subfname);
+ f->subfname = talloc_strdup(f, n2);
+ return true;
+ }
+ }
+ return false;
+}
+
+/***************************************************************************
+ Handle the include operation.
+***************************************************************************/
+
+static bool handle_include(struct loadparm_context *lp_ctx,
+ const char *pszParmValue, char **ptr)
+{
+ char *fname = standard_sub_basic(lp_ctx, pszParmValue);
+
+ add_to_file_list(lp_ctx, pszParmValue, fname);
+
+ string_set(lp_ctx, ptr, fname);
+
+ if (file_exist(fname))
+ return pm_process(fname, do_section, do_parameter, lp_ctx);
+
+ DEBUG(2, ("Can't find include file %s\n", fname));
+
+ return false;
+}
+
+/***************************************************************************
+ Handle the interpretation of the copy parameter.
+***************************************************************************/
+
+static bool handle_copy(struct loadparm_context *lp_ctx,
+ const char *pszParmValue, char **ptr)
+{
+ bool bRetval;
+ struct loadparm_service *serviceTemp;
+
+ string_set(lp_ctx, ptr, pszParmValue);
+
+ bRetval = false;
+
+ DEBUG(3, ("Copying service from service %s\n", pszParmValue));
+
+ if ((serviceTemp = getservicebyname(lp_ctx, pszParmValue)) != NULL) {
+ if (serviceTemp == lp_ctx->currentService) {
+ DEBUG(0, ("Can't copy service %s - unable to copy self!\n", pszParmValue));
+ } else {
+ copy_service(lp_ctx->currentService,
+ serviceTemp,
+ lp_ctx->currentService->copymap);
+ bRetval = true;
+ }
+ } else {
+ DEBUG(0, ("Unable to copy service - source not found: %s\n",
+ pszParmValue));
+ bRetval = false;
+ }
+
+ return bRetval;
+}
+
+static bool handle_debuglevel(struct loadparm_context *lp_ctx,
+ const char *pszParmValue, char **ptr)
+{
+ DEBUGLEVEL = atoi(pszParmValue);
+
+ return true;
+}
+
+static bool handle_logfile(struct loadparm_context *lp_ctx,
+ const char *pszParmValue, char **ptr)
+{
+ logfile = pszParmValue;
+ return true;
+}
+
+/***************************************************************************
+ Initialise a copymap.
+***************************************************************************/
+
+static void init_copymap(struct loadparm_service *pservice)
+{
+ int i;
+ talloc_free(pservice->copymap);
+ pservice->copymap = talloc_array(pservice, int, NUMPARAMETERS);
+ if (pservice->copymap == NULL) {
+ DEBUG(0,
+ ("Couldn't allocate copymap!! (size %d)\n",
+ (int)NUMPARAMETERS));
+ return;
+ }
+ for (i = 0; i < NUMPARAMETERS; i++)
+ pservice->copymap[i] = true;
+}
+
+/**
+ * Process a parametric option
+ */
+static bool lp_do_parameter_parametric(struct loadparm_context *lp_ctx,
+ struct loadparm_service *service,
+ const char *pszParmName,
+ const char *pszParmValue, int flags)
+{
+ struct param_opt *paramo, *data;
+ char *name;
+ TALLOC_CTX *mem_ctx;
+
+ while (isspace((unsigned char)*pszParmName)) {
+ pszParmName++;
+ }
+
+ name = strdup(pszParmName);
+ if (!name) return false;
+
+ strlower(name);
+
+ if (service == NULL) {
+ data = lp_ctx->globals->param_opt;
+ mem_ctx = lp_ctx->globals;
+ } else {
+ data = service->param_opt;
+ mem_ctx = service;
+ }
+
+ /* Traverse destination */
+ for (paramo=data; paramo; paramo=paramo->next) {
+ /* If we already have the option set, override it unless
+ it was a command line option and the new one isn't */
+ if (strcmp(paramo->key, name) == 0) {
+ if ((paramo->priority & FLAG_CMDLINE) &&
+ !(flags & FLAG_CMDLINE)) {
+ return true;
+ }
+
+ talloc_free(paramo->value);
+ paramo->value = talloc_strdup(paramo, pszParmValue);
+ paramo->priority = flags;
+ free(name);
+ return true;
+ }
+ }
+
+ paramo = talloc(mem_ctx, struct param_opt);
+ if (!paramo)
+ smb_panic("OOM");
+ paramo->key = talloc_strdup(paramo, name);
+ paramo->value = talloc_strdup(paramo, pszParmValue);
+ paramo->priority = flags;
+ if (service == NULL) {
+ DLIST_ADD(lp_ctx->globals->param_opt, paramo);
+ } else {
+ DLIST_ADD(service->param_opt, paramo);
+ }
+
+ free(name);
+
+ return true;
+}
+
+static bool set_variable(TALLOC_CTX *mem_ctx, int parmnum, void *parm_ptr,
+ const char *pszParmName, const char *pszParmValue,
+ struct loadparm_context *lp_ctx)
+{
+ int i;
+ /* if it is a special case then go ahead */
+ if (parm_table[parmnum].special) {
+ parm_table[parmnum].special(lp_ctx, pszParmValue,
+ (char **)parm_ptr);
+ return true;
+ }
+
+ /* now switch on the type of variable it is */
+ switch (parm_table[parmnum].type)
+ {
+ case P_BOOL: {
+ bool b;
+ if (!set_boolean(pszParmValue, &b)) {
+ DEBUG(0,("lp_do_parameter(%s): value is not boolean!\n", pszParmValue));
+ return false;
+ }
+ *(int *)parm_ptr = b;
+ }
+ break;
+
+ case P_INTEGER:
+ *(int *)parm_ptr = atoi(pszParmValue);
+ break;
+
+ case P_OCTAL:
+ *(int *)parm_ptr = strtol(pszParmValue, NULL, 8);
+ break;
+
+ case P_BYTES:
+ {
+ uint64_t val;
+ if (conv_str_size(pszParmValue, &val)) {
+ if (val <= INT_MAX) {
+ *(int *)parm_ptr = (int)val;
+ break;
+ }
+ }
+
+ DEBUG(0,("lp_do_parameter(%s): value is not "
+ "a valid size specifier!\n", pszParmValue));
+ return false;
+ }
+
+ case P_LIST:
+ *(const char ***)parm_ptr = (const char **)str_list_make(mem_ctx,
+ pszParmValue, NULL);
+ break;
+
+ case P_STRING:
+ string_set(mem_ctx, (char **)parm_ptr, pszParmValue);
+ break;
+
+ case P_USTRING:
+ string_set(mem_ctx, (char **)parm_ptr, pszParmValue);
+ strupper(*(char **)parm_ptr);
+ break;
+
+ case P_ENUM:
+ for (i = 0; parm_table[parmnum].enum_list[i].name; i++) {
+ if (strequal
+ (pszParmValue,
+ parm_table[parmnum].enum_list[i].name)) {
+ *(int *)parm_ptr =
+ parm_table[parmnum].
+ enum_list[i].value;
+ break;
+ }
+ }
+ if (!parm_table[parmnum].enum_list[i].name) {
+ DEBUG(0,("Unknown enumerated value '%s' for '%s'\n",
+ pszParmValue, pszParmName));
+ return false;
+ }
+ break;
+ }
+
+ if (lp_ctx->flags[parmnum] & FLAG_DEFAULT) {
+ lp_ctx->flags[parmnum] &= ~FLAG_DEFAULT;
+ /* we have to also unset FLAG_DEFAULT on aliases */
+ for (i=parmnum-1;i>=0 && parm_table[i].offset == parm_table[parmnum].offset;i--) {
+ lp_ctx->flags[i] &= ~FLAG_DEFAULT;
+ }
+ for (i=parmnum+1;i<NUMPARAMETERS && parm_table[i].offset == parm_table[parmnum].offset;i++) {
+ lp_ctx->flags[i] &= ~FLAG_DEFAULT;
+ }
+ }
+ return true;
+}
+
+
+bool lp_do_global_parameter(struct loadparm_context *lp_ctx,
+ const char *pszParmName, const char *pszParmValue)
+{
+ int parmnum = map_parameter(pszParmName);
+ void *parm_ptr;
+
+ if (parmnum < 0) {
+ if (strchr(pszParmName, ':')) {
+ return lp_do_parameter_parametric(lp_ctx, NULL, pszParmName, pszParmValue, 0);
+ }
+ DEBUG(0, ("Ignoring unknown parameter \"%s\"\n", pszParmName));
+ return true;
+ }
+
+ /* if the flag has been set on the command line, then don't allow override,
+ but don't report an error */
+ if (lp_ctx->flags[parmnum] & FLAG_CMDLINE) {
+ return true;
+ }
+
+ parm_ptr = lp_parm_ptr(lp_ctx, NULL, &parm_table[parmnum]);
+
+ return set_variable(lp_ctx, parmnum, parm_ptr,
+ pszParmName, pszParmValue, lp_ctx);
+}
+
+bool lp_do_service_parameter(struct loadparm_context *lp_ctx,
+ struct loadparm_service *service,
+ const char *pszParmName, const char *pszParmValue)
+{
+ void *parm_ptr;
+ int i;
+ int parmnum = map_parameter(pszParmName);
+
+ if (parmnum < 0) {
+ if (strchr(pszParmName, ':')) {
+ return lp_do_parameter_parametric(lp_ctx, service, pszParmName, pszParmValue, 0);
+ }
+ DEBUG(0, ("Ignoring unknown parameter \"%s\"\n", pszParmName));
+ return true;
+ }
+
+ /* if the flag has been set on the command line, then don't allow override,
+ but don't report an error */
+ if (lp_ctx->flags[parmnum] & FLAG_CMDLINE) {
+ return true;
+ }
+
+ if (parm_table[parmnum].pclass == P_GLOBAL) {
+ DEBUG(0,
+ ("Global parameter %s found in service section!\n",
+ pszParmName));
+ return true;
+ }
+ parm_ptr = ((char *)service) + parm_table[parmnum].offset;
+
+ if (!service->copymap)
+ init_copymap(service);
+
+ /* this handles the aliases - set the copymap for other
+ * entries with the same data pointer */
+ for (i = 0; parm_table[i].label; i++)
+ if (parm_table[i].offset == parm_table[parmnum].offset &&
+ parm_table[i].pclass == parm_table[parmnum].pclass)
+ service->copymap[i] = false;
+
+ return set_variable(service, parmnum, parm_ptr, pszParmName,
+ pszParmValue, lp_ctx);
+}
+
+/**
+ * Process a parameter.
+ */
+
+static bool do_parameter(const char *pszParmName, const char *pszParmValue,
+ void *userdata)
+{
+ struct loadparm_context *lp_ctx = (struct loadparm_context *)userdata;
+
+ if (lp_ctx->bInGlobalSection)
+ return lp_do_global_parameter(lp_ctx, pszParmName,
+ pszParmValue);
+ else
+ return lp_do_service_parameter(lp_ctx, lp_ctx->currentService,
+ pszParmName, pszParmValue);
+}
+
+/*
+ variable argument do parameter
+*/
+bool lp_do_global_parameter_var(struct loadparm_context *lp_ctx, const char *pszParmName, const char *fmt, ...) PRINTF_ATTRIBUTE(3, 4);
+bool lp_do_global_parameter_var(struct loadparm_context *lp_ctx,
+ const char *pszParmName, const char *fmt, ...)
+{
+ char *s;
+ bool ret;
+ va_list ap;
+
+ va_start(ap, fmt);
+ s = talloc_vasprintf(NULL, fmt, ap);
+ va_end(ap);
+ ret = lp_do_global_parameter(lp_ctx, pszParmName, s);
+ talloc_free(s);
+ return ret;
+}
+
+
+/*
+ set a parameter from the commandline - this is called from command line parameter
+ parsing code. It sets the parameter then marks the parameter as unable to be modified
+ by smb.conf processing
+*/
+bool lp_set_cmdline(struct loadparm_context *lp_ctx, const char *pszParmName,
+ const char *pszParmValue)
+{
+ int parmnum = map_parameter(pszParmName);
+ int i;
+
+ while (isspace((unsigned char)*pszParmValue)) pszParmValue++;
+
+
+ if (parmnum < 0 && strchr(pszParmName, ':')) {
+ /* set a parametric option */
+ return lp_do_parameter_parametric(lp_ctx, NULL, pszParmName,
+ pszParmValue, FLAG_CMDLINE);
+ }
+
+ if (parmnum < 0) {
+ DEBUG(0,("Unknown option '%s'\n", pszParmName));
+ return false;
+ }
+
+ /* reset the CMDLINE flag in case this has been called before */
+ lp_ctx->flags[parmnum] &= ~FLAG_CMDLINE;
+
+ if (!lp_do_global_parameter(lp_ctx, pszParmName, pszParmValue)) {
+ return false;
+ }
+
+ lp_ctx->flags[parmnum] |= FLAG_CMDLINE;
+
+ /* we have to also set FLAG_CMDLINE on aliases */
+ for (i=parmnum-1;i>=0 && parm_table[i].offset == parm_table[parmnum].offset;i--) {
+ lp_ctx->flags[i] |= FLAG_CMDLINE;
+ }
+ for (i=parmnum+1;i<NUMPARAMETERS && parm_table[i].offset == parm_table[parmnum].offset;i++) {
+ lp_ctx->flags[i] |= FLAG_CMDLINE;
+ }
+
+ return true;
+}
+
+/*
+ set a option from the commandline in 'a=b' format. Use to support --option
+*/
+bool lp_set_option(struct loadparm_context *lp_ctx, const char *option)
+{
+ char *p, *s;
+ bool ret;
+
+ s = strdup(option);
+ if (!s) {
+ return false;
+ }
+
+ p = strchr(s, '=');
+ if (!p) {
+ free(s);
+ return false;
+ }
+
+ *p = 0;
+
+ ret = lp_set_cmdline(lp_ctx, s, p+1);
+ free(s);
+ return ret;
+}
+
+
+#define BOOLSTR(b) ((b) ? "Yes" : "No")
+
+/**
+ * Print a parameter of the specified type.
+ */
+
+static void print_parameter(struct parm_struct *p, void *ptr, FILE * f)
+{
+ int i;
+ switch (p->type)
+ {
+ case P_ENUM:
+ for (i = 0; p->enum_list[i].name; i++) {
+ if (*(int *)ptr == p->enum_list[i].value) {
+ fprintf(f, "%s",
+ p->enum_list[i].name);
+ break;
+ }
+ }
+ break;
+
+ case P_BOOL:
+ fprintf(f, "%s", BOOLSTR((bool)*(int *)ptr));
+ break;
+
+ case P_INTEGER:
+ case P_BYTES:
+ fprintf(f, "%d", *(int *)ptr);
+ break;
+
+ case P_OCTAL:
+ fprintf(f, "0%o", *(int *)ptr);
+ break;
+
+ case P_LIST:
+ if ((char ***)ptr && *(char ***)ptr) {
+ char **list = *(char ***)ptr;
+
+ for (; *list; list++)
+ fprintf(f, "%s%s", *list,
+ ((*(list+1))?", ":""));
+ }
+ break;
+
+ case P_STRING:
+ case P_USTRING:
+ if (*(char **)ptr) {
+ fprintf(f, "%s", *(char **)ptr);
+ }
+ break;
+ }
+}
+
+/**
+ * Check if two parameters are equal.
+ */
+
+static bool equal_parameter(parm_type type, void *ptr1, void *ptr2)
+{
+ switch (type) {
+ case P_BOOL:
+ return (*((int *)ptr1) == *((int *)ptr2));
+
+ case P_INTEGER:
+ case P_OCTAL:
+ case P_BYTES:
+ case P_ENUM:
+ return (*((int *)ptr1) == *((int *)ptr2));
+
+ case P_LIST:
+ return str_list_equal((const char **)(*(char ***)ptr1),
+ (const char **)(*(char ***)ptr2));
+
+ case P_STRING:
+ case P_USTRING:
+ {
+ char *p1 = *(char **)ptr1, *p2 = *(char **)ptr2;
+ if (p1 && !*p1)
+ p1 = NULL;
+ if (p2 && !*p2)
+ p2 = NULL;
+ return (p1 == p2 || strequal(p1, p2));
+ }
+ }
+ return false;
+}
+
+/**
+ * Process a new section (service).
+ *
+ * At this stage all sections are services.
+ * Later we'll have special sections that permit server parameters to be set.
+ * Returns True on success, False on failure.
+ */
+
+static bool do_section(const char *pszSectionName, void *userdata)
+{
+ struct loadparm_context *lp_ctx = (struct loadparm_context *)userdata;
+ bool bRetval;
+ bool isglobal = ((strwicmp(pszSectionName, GLOBAL_NAME) == 0) ||
+ (strwicmp(pszSectionName, GLOBAL_NAME2) == 0));
+ bRetval = false;
+
+ /* if we've just struck a global section, note the fact. */
+ lp_ctx->bInGlobalSection = isglobal;
+
+ /* check for multiple global sections */
+ if (lp_ctx->bInGlobalSection) {
+ DEBUG(3, ("Processing section \"[%s]\"\n", pszSectionName));
+ return true;
+ }
+
+ /* if we have a current service, tidy it up before moving on */
+ bRetval = true;
+
+ if (lp_ctx->currentService != NULL)
+ bRetval = service_ok(lp_ctx->currentService);
+
+ /* if all is still well, move to the next record in the services array */
+ if (bRetval) {
+ /* We put this here to avoid an odd message order if messages are */
+ /* issued by the post-processing of a previous section. */
+ DEBUG(2, ("Processing section \"[%s]\"\n", pszSectionName));
+
+ if ((lp_ctx->currentService = lp_add_service(lp_ctx, lp_ctx->sDefault,
+ pszSectionName))
+ == NULL) {
+ DEBUG(0, ("Failed to add a new service\n"));
+ return false;
+ }
+ }
+
+ return bRetval;
+}
+
+
+/**
+ * Determine if a particular base parameter is currently set to the default value.
+ */
+
+static bool is_default(struct loadparm_service *sDefault, int i)
+{
+ void *def_ptr = ((char *)sDefault) + parm_table[i].offset;
+ if (!defaults_saved)
+ return false;
+ switch (parm_table[i].type) {
+ case P_LIST:
+ return str_list_equal((const char **)parm_table[i].def.lvalue,
+ (const char **)def_ptr);
+ case P_STRING:
+ case P_USTRING:
+ return strequal(parm_table[i].def.svalue,
+ *(char **)def_ptr);
+ case P_BOOL:
+ return parm_table[i].def.bvalue ==
+ *(int *)def_ptr;
+ case P_INTEGER:
+ case P_OCTAL:
+ case P_BYTES:
+ case P_ENUM:
+ return parm_table[i].def.ivalue ==
+ *(int *)def_ptr;
+ }
+ return false;
+}
+
+/**
+ *Display the contents of the global structure.
+ */
+
+static void dump_globals(struct loadparm_context *lp_ctx, FILE *f,
+ bool show_defaults)
+{
+ int i;
+ struct param_opt *data;
+
+ fprintf(f, "# Global parameters\n[global]\n");
+
+ for (i = 0; parm_table[i].label; i++)
+ if (parm_table[i].pclass == P_GLOBAL &&
+ parm_table[i].offset != -1 &&
+ (i == 0 || (parm_table[i].offset != parm_table[i - 1].offset))) {
+ if (!show_defaults && (lp_ctx->flags[i] & FLAG_DEFAULT))
+ continue;
+ fprintf(f, "\t%s = ", parm_table[i].label);
+ print_parameter(&parm_table[i], lp_parm_ptr(lp_ctx, NULL, &parm_table[i]), f);
+ fprintf(f, "\n");
+ }
+ if (lp_ctx->globals->param_opt != NULL) {
+ for (data = lp_ctx->globals->param_opt; data;
+ data = data->next) {
+ fprintf(f, "\t%s = %s\n", data->key, data->value);
+ }
+ }
+
+}
+
+/**
+ * Display the contents of a single services record.
+ */
+
+static void dump_a_service(struct loadparm_service * pService, struct loadparm_service *sDefault, FILE * f)
+{
+ int i;
+ struct param_opt *data;
+
+ if (pService != sDefault)
+ fprintf(f, "\n[%s]\n", pService->szService);
+
+ for (i = 0; parm_table[i].label; i++)
+ if (parm_table[i].pclass == P_LOCAL &&
+ parm_table[i].offset != -1 &&
+ (*parm_table[i].label != '-') &&
+ (i == 0 || (parm_table[i].offset != parm_table[i - 1].offset))) {
+ if (pService == sDefault) {
+ if (defaults_saved && is_default(sDefault, i))
+ continue;
+ } else {
+ if (equal_parameter(parm_table[i].type,
+ ((char *)pService) +
+ parm_table[i].offset,
+ ((char *)sDefault) +
+ parm_table[i].offset))
+ continue;
+ }
+
+ fprintf(f, "\t%s = ", parm_table[i].label);
+ print_parameter(&parm_table[i],
+ ((char *)pService) + parm_table[i].offset, f);
+ fprintf(f, "\n");
+ }
+ if (pService->param_opt != NULL) {
+ for (data = pService->param_opt; data; data = data->next) {
+ fprintf(f, "\t%s = %s\n", data->key, data->value);
+ }
+ }
+}
+
+bool lp_dump_a_parameter(struct loadparm_context *lp_ctx,
+ struct loadparm_service *service,
+ const char *parm_name, FILE * f)
+{
+ struct parm_struct *parm;
+ void *ptr;
+
+ parm = lp_parm_struct(parm_name);
+ if (!parm) {
+ return false;
+ }
+
+ ptr = lp_parm_ptr(lp_ctx, service,parm);
+
+ print_parameter(parm, ptr, f);
+ fprintf(f, "\n");
+ return true;
+}
+
+/**
+ * Return info about the next service in a service. snum==-1 gives the globals.
+ * Return NULL when out of parameters.
+ */
+
+struct parm_struct *lp_next_parameter(struct loadparm_context *lp_ctx, int snum, int *i,
+ int allparameters)
+{
+ if (snum == -1) {
+ /* do the globals */
+ for (; parm_table[*i].label; (*i)++) {
+ if (parm_table[*i].offset == -1
+ || (*parm_table[*i].label == '-'))
+ continue;
+
+ if ((*i) > 0
+ && (parm_table[*i].offset ==
+ parm_table[(*i) - 1].offset))
+ continue;
+
+ return &parm_table[(*i)++];
+ }
+ } else {
+ struct loadparm_service *pService = lp_ctx->services[snum];
+
+ for (; parm_table[*i].label; (*i)++) {
+ if (parm_table[*i].pclass == P_LOCAL &&
+ parm_table[*i].offset != -1 &&
+ (*parm_table[*i].label != '-') &&
+ ((*i) == 0 ||
+ (parm_table[*i].offset !=
+ parm_table[(*i) - 1].offset)))
+ {
+ if (allparameters ||
+ !equal_parameter(parm_table[*i].type,
+ ((char *)pService) +
+ parm_table[*i].offset,
+ ((char *)lp_ctx->sDefault) +
+ parm_table[*i].offset))
+ {
+ return &parm_table[(*i)++];
+ }
+ }
+ }
+ }
+
+ return NULL;
+}
+
+
+/**
+ * Auto-load some home services.
+ */
+static void lp_add_auto_services(struct loadparm_context *lp_ctx,
+ const char *str)
+{
+ return;
+}
+
+
+/**
+ * Unload unused services.
+ */
+
+void lp_killunused(struct loadparm_context *lp_ctx,
+ struct smbsrv_connection *smb,
+ bool (*snumused) (struct smbsrv_connection *, int))
+{
+ int i;
+ for (i = 0; i < lp_ctx->iNumServices; i++) {
+ if (lp_ctx->services[i] == NULL)
+ continue;
+
+ if (!snumused || !snumused(smb, i)) {
+ talloc_free(lp_ctx->services[i]);
+ lp_ctx->services[i] = NULL;
+ }
+ }
+}
+
+
+static int lp_destructor(struct loadparm_context *lp_ctx)
+{
+ struct param_opt *data;
+
+ if (lp_ctx->globals->param_opt != NULL) {
+ struct param_opt *next;
+ for (data = lp_ctx->globals->param_opt; data; data=next) {
+ next = data->next;
+ if (data->priority & FLAG_CMDLINE) continue;
+ DLIST_REMOVE(lp_ctx->globals->param_opt, data);
+ talloc_free(data);
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Initialise the global parameter structure.
+ */
+struct loadparm_context *loadparm_init(TALLOC_CTX *mem_ctx)
+{
+ int i;
+ char *myname;
+ struct loadparm_context *lp_ctx;
+
+ lp_ctx = talloc_zero(mem_ctx, struct loadparm_context);
+ if (lp_ctx == NULL)
+ return NULL;
+
+ talloc_set_destructor(lp_ctx, lp_destructor);
+ lp_ctx->bInGlobalSection = true;
+ lp_ctx->globals = talloc_zero(lp_ctx, struct loadparm_global);
+ lp_ctx->sDefault = talloc_zero(lp_ctx, struct loadparm_service);
+
+ lp_ctx->sDefault->iMaxPrintJobs = 1000;
+ lp_ctx->sDefault->bAvailable = true;
+ lp_ctx->sDefault->bBrowseable = true;
+ lp_ctx->sDefault->bRead_only = true;
+ lp_ctx->sDefault->bMap_archive = true;
+ lp_ctx->sDefault->bStrictLocking = true;
+ lp_ctx->sDefault->bOplocks = true;
+ lp_ctx->sDefault->iCreate_mask = 0744;
+ lp_ctx->sDefault->iCreate_force_mode = 0000;
+ lp_ctx->sDefault->iDir_mask = 0755;
+ lp_ctx->sDefault->iDir_force_mode = 0000;
+
+ DEBUG(3, ("Initialising global parameters\n"));
+
+ for (i = 0; parm_table[i].label; i++) {
+ if ((parm_table[i].type == P_STRING ||
+ parm_table[i].type == P_USTRING) &&
+ parm_table[i].offset != -1 &&
+ !(lp_ctx->flags[i] & FLAG_CMDLINE)) {
+ char **r;
+ if (parm_table[i].pclass == P_LOCAL) {
+ r = (char **)(((char *)lp_ctx->sDefault) + parm_table[i].offset);
+ } else {
+ r = (char **)(((char *)lp_ctx->globals) + parm_table[i].offset);
+ }
+ *r = talloc_strdup(lp_ctx, "");
+ }
+ }
+
+ lp_do_global_parameter(lp_ctx, "share backend", "classic");
+
+ lp_do_global_parameter(lp_ctx, "server role", "standalone");
+
+ /* options that can be set on the command line must be initialised via
+ the slower lp_do_global_parameter() to ensure that FLAG_CMDLINE is obeyed */
+#ifdef TCP_NODELAY
+ lp_do_global_parameter(lp_ctx, "socket options", "TCP_NODELAY");
+#endif
+ lp_do_global_parameter(lp_ctx, "workgroup", DEFAULT_WORKGROUP);
+ myname = get_myname(lp_ctx);
+ lp_do_global_parameter(lp_ctx, "netbios name", myname);
+ talloc_free(myname);
+ lp_do_global_parameter(lp_ctx, "name resolve order", "wins host bcast");
+
+ lp_do_global_parameter(lp_ctx, "fstype", "NTFS");
+
+ lp_do_global_parameter(lp_ctx, "ntvfs handler", "unixuid default");
+ lp_do_global_parameter(lp_ctx, "max connections", "-1");
+
+ lp_do_global_parameter(lp_ctx, "dcerpc endpoint servers", "epmapper srvsvc wkssvc rpcecho samr netlogon lsarpc spoolss drsuapi winreg dssetup unixinfo browser");
+ lp_do_global_parameter(lp_ctx, "server services", "smb rpc nbt wrepl ldap cldap kdc drepl winbind ntp_signd");
+ lp_do_global_parameter(lp_ctx, "ntptr providor", "simple_ldb");
+ lp_do_global_parameter(lp_ctx, "auth methods:domain controller", "anonymous sam_ignoredomain");
+ lp_do_global_parameter(lp_ctx, "auth methods:member server", "anonymous sam winbind");
+ lp_do_global_parameter(lp_ctx, "auth methods:standalone", "anonymous sam_ignoredomain");
+ lp_do_global_parameter(lp_ctx, "private dir", dyn_PRIVATE_DIR);
+ lp_do_global_parameter(lp_ctx, "sam database", "sam.ldb");
+ lp_do_global_parameter(lp_ctx, "idmap database", "idmap.ldb");
+ lp_do_global_parameter(lp_ctx, "secrets database", "secrets.ldb");
+ lp_do_global_parameter(lp_ctx, "spoolss database", "spoolss.ldb");
+ lp_do_global_parameter(lp_ctx, "wins config database", "wins_config.ldb");
+ lp_do_global_parameter(lp_ctx, "wins database", "wins.ldb");
+ lp_do_global_parameter(lp_ctx, "registry:HKEY_LOCAL_MACHINE", "hklm.ldb");
+
+ /* This hive should be dynamically generated by Samba using
+ data from the sam, but for the moment leave it in a tdb to
+ keep regedt32 from popping up an annoying dialog. */
+ lp_do_global_parameter(lp_ctx, "registry:HKEY_USERS", "hku.ldb");
+
+ /* using UTF8 by default allows us to support all chars */
+ lp_do_global_parameter(lp_ctx, "unix charset", "UTF8");
+
+ /* Use codepage 850 as a default for the dos character set */
+ lp_do_global_parameter(lp_ctx, "dos charset", "CP850");
+
+ /*
+ * Allow the default PASSWD_CHAT to be overridden in local.h.
+ */
+ lp_do_global_parameter(lp_ctx, "passwd chat", DEFAULT_PASSWD_CHAT);
+
+ lp_do_global_parameter(lp_ctx, "pid directory", dyn_PIDDIR);
+ lp_do_global_parameter(lp_ctx, "lock dir", dyn_LOCKDIR);
+ lp_do_global_parameter(lp_ctx, "modules dir", dyn_MODULESDIR);
+ lp_do_global_parameter(lp_ctx, "ncalrpc dir", dyn_NCALRPCDIR);
+
+ lp_do_global_parameter(lp_ctx, "socket address", "0.0.0.0");
+ lp_do_global_parameter_var(lp_ctx, "server string",
+ "Samba %s", SAMBA_VERSION_STRING);
+
+ lp_do_global_parameter_var(lp_ctx, "announce version", "%d.%d",
+ DEFAULT_MAJOR_VERSION,
+ DEFAULT_MINOR_VERSION);
+
+ lp_do_global_parameter(lp_ctx, "password server", "*");
+
+ lp_do_global_parameter(lp_ctx, "max mux", "50");
+ lp_do_global_parameter(lp_ctx, "max xmit", "12288");
+ lp_do_global_parameter(lp_ctx, "password level", "0");
+ lp_do_global_parameter(lp_ctx, "LargeReadwrite", "True");
+ lp_do_global_parameter(lp_ctx, "server min protocol", "CORE");
+ lp_do_global_parameter(lp_ctx, "server max protocol", "NT1");
+ lp_do_global_parameter(lp_ctx, "client min protocol", "CORE");
+ lp_do_global_parameter(lp_ctx, "client max protocol", "NT1");
+ lp_do_global_parameter(lp_ctx, "security", "USER");
+ lp_do_global_parameter(lp_ctx, "paranoid server security", "True");
+ lp_do_global_parameter(lp_ctx, "EncryptPasswords", "True");
+ lp_do_global_parameter(lp_ctx, "ReadRaw", "True");
+ lp_do_global_parameter(lp_ctx, "WriteRaw", "True");
+ lp_do_global_parameter(lp_ctx, "NullPasswords", "False");
+ lp_do_global_parameter(lp_ctx, "ObeyPamRestrictions", "False");
+ lp_do_global_parameter(lp_ctx, "announce as", "NT SERVER");
+
+ lp_do_global_parameter(lp_ctx, "TimeServer", "False");
+ lp_do_global_parameter(lp_ctx, "BindInterfacesOnly", "False");
+ lp_do_global_parameter(lp_ctx, "Unicode", "True");
+ lp_do_global_parameter(lp_ctx, "ClientLanManAuth", "False");
+ lp_do_global_parameter(lp_ctx, "LanmanAuth", "False");
+ lp_do_global_parameter(lp_ctx, "NTLMAuth", "True");
+ lp_do_global_parameter(lp_ctx, "client use spnego principal", "False");
+
+ lp_do_global_parameter(lp_ctx, "UnixExtensions", "False");
+
+ lp_do_global_parameter(lp_ctx, "PreferredMaster", "Auto");
+ lp_do_global_parameter(lp_ctx, "LocalMaster", "True");
+
+ lp_do_global_parameter(lp_ctx, "wins support", "False");
+ lp_do_global_parameter(lp_ctx, "dns proxy", "True");
+
+ lp_do_global_parameter(lp_ctx, "winbind separator", "\\");
+ lp_do_global_parameter(lp_ctx, "winbind sealed pipes", "True");
+ lp_do_global_parameter(lp_ctx, "winbindd socket directory", dyn_WINBINDD_SOCKET_DIR);
+ lp_do_global_parameter(lp_ctx, "winbindd privileged socket directory", dyn_WINBINDD_PRIVILEGED_SOCKET_DIR);
+ lp_do_global_parameter(lp_ctx, "template shell", "/bin/false");
+ lp_do_global_parameter(lp_ctx, "template homedir", "/home/%WORKGROUP%/%ACCOUNTNAME%");
+ lp_do_global_parameter(lp_ctx, "idmap trusted only", "False");
+
+ lp_do_global_parameter(lp_ctx, "client signing", "Yes");
+ lp_do_global_parameter(lp_ctx, "server signing", "auto");
+
+ lp_do_global_parameter(lp_ctx, "use spnego", "True");
+
+ lp_do_global_parameter(lp_ctx, "smb ports", "445 139");
+ lp_do_global_parameter(lp_ctx, "nbt port", "137");
+ lp_do_global_parameter(lp_ctx, "dgram port", "138");
+ lp_do_global_parameter(lp_ctx, "cldap port", "389");
+ lp_do_global_parameter(lp_ctx, "krb5 port", "88");
+ lp_do_global_parameter(lp_ctx, "kpasswd port", "464");
+ lp_do_global_parameter(lp_ctx, "web port", "901");
+ lp_do_global_parameter(lp_ctx, "swat directory", dyn_SWATDIR);
+
+ lp_do_global_parameter(lp_ctx, "nt status support", "True");
+
+ lp_do_global_parameter(lp_ctx, "max wins ttl", "518400"); /* 6 days */
+ lp_do_global_parameter(lp_ctx, "min wins ttl", "10");
+
+ lp_do_global_parameter(lp_ctx, "tls enabled", "True");
+ lp_do_global_parameter(lp_ctx, "tls keyfile", "tls/key.pem");
+ lp_do_global_parameter(lp_ctx, "tls certfile", "tls/cert.pem");
+ lp_do_global_parameter(lp_ctx, "tls cafile", "tls/ca.pem");
+ lp_do_global_parameter_var(lp_ctx, "setup directory", "%s",
+ dyn_SETUPDIR);
+
+ lp_do_global_parameter(lp_ctx, "prefork children:smb", "4");
+
+ lp_do_global_parameter(lp_ctx, "ntp signd socket directory", dyn_NTP_SIGND_SOCKET_DIR);
+
+ for (i = 0; parm_table[i].label; i++) {
+ if (!(lp_ctx->flags[i] & FLAG_CMDLINE)) {
+ lp_ctx->flags[i] |= FLAG_DEFAULT;
+ }
+ }
+
+ return lp_ctx;
+}
+
+const char *lp_configfile(struct loadparm_context *lp_ctx)
+{
+ return lp_ctx->szConfigFile;
+}
+
+const char *lp_default_path(void)
+{
+ if (getenv("SMB_CONF_PATH"))
+ return getenv("SMB_CONF_PATH");
+ else
+ return dyn_CONFIGFILE;
+}
+
+/**
+ * Update the internal state of a loadparm context after settings
+ * have changed.
+ */
+static bool lp_update(struct loadparm_context *lp_ctx)
+{
+ lp_add_auto_services(lp_ctx, lp_auto_services(lp_ctx));
+
+ lp_add_hidden(lp_ctx, "IPC$", "IPC");
+ lp_add_hidden(lp_ctx, "ADMIN$", "DISK");
+
+ if (!lp_ctx->globals->szWINSservers && lp_ctx->globals->bWINSsupport) {
+ lp_do_global_parameter(lp_ctx, "wins server", "127.0.0.1");
+ }
+
+ panic_action = lp_ctx->globals->panic_action;
+
+ reload_charcnv(lp_ctx);
+
+ /* FIXME: ntstatus_check_dos_mapping = lp_nt_status_support(lp_ctx); */
+
+ /* FIXME: This is a bit of a hack, but we can't use a global, since
+ * not everything that uses lp also uses the socket library */
+ if (lp_parm_bool(lp_ctx, NULL, "socket", "testnonblock", false)) {
+ setenv("SOCKET_TESTNONBLOCK", "1", 1);
+ } else {
+ unsetenv("SOCKET_TESTNONBLOCK");
+ }
+
+ /* FIXME: Check locale in environment for this: */
+ if (strcmp(lp_display_charset(lp_ctx), lp_unix_charset(lp_ctx)) != 0)
+ d_set_iconv(smb_iconv_open(lp_display_charset(lp_ctx), lp_unix_charset(lp_ctx)));
+ else
+ d_set_iconv((smb_iconv_t)-1);
+
+ return true;
+}
+
+bool lp_load_default(struct loadparm_context *lp_ctx)
+{
+ const char *path;
+
+ path = lp_default_path();
+
+ if (!file_exist(path)) {
+ /* We allow the default smb.conf file to not exist,
+ * basically the equivalent of an empty file. */
+ return lp_update(lp_ctx);
+ }
+
+ return lp_load(lp_ctx, path);
+}
+
+/**
+ * Load the services array from the services file.
+ *
+ * Return True on success, False on failure.
+ */
+bool lp_load(struct loadparm_context *lp_ctx, const char *filename)
+{
+ char *n2;
+ bool bRetval;
+
+ filename = talloc_strdup(lp_ctx, filename);
+
+ lp_ctx->szConfigFile = filename;
+
+ lp_ctx->bInGlobalSection = true;
+ n2 = standard_sub_basic(lp_ctx, lp_ctx->szConfigFile);
+ DEBUG(2, ("lp_load: refreshing parameters from %s\n", n2));
+
+ add_to_file_list(lp_ctx, lp_ctx->szConfigFile, n2);
+
+ /* We get sections first, so have to start 'behind' to make up */
+ lp_ctx->currentService = NULL;
+ bRetval = pm_process(n2, do_section, do_parameter, lp_ctx);
+
+ /* finish up the last section */
+ DEBUG(4, ("pm_process() returned %s\n", BOOLSTR(bRetval)));
+ if (bRetval)
+ if (lp_ctx->currentService != NULL)
+ bRetval = service_ok(lp_ctx->currentService);
+
+ bRetval = bRetval && lp_update(lp_ctx);
+
+ return bRetval;
+}
+
+/**
+ * Return the max number of services.
+ */
+
+int lp_numservices(struct loadparm_context *lp_ctx)
+{
+ return lp_ctx->iNumServices;
+}
+
+/**
+ * Display the contents of the services array in human-readable form.
+ */
+
+void lp_dump(struct loadparm_context *lp_ctx, FILE *f, bool show_defaults,
+ int maxtoprint)
+{
+ int iService;
+
+ if (show_defaults)
+ defaults_saved = false;
+
+ dump_globals(lp_ctx, f, show_defaults);
+
+ dump_a_service(lp_ctx->sDefault, lp_ctx->sDefault, f);
+
+ for (iService = 0; iService < maxtoprint; iService++)
+ lp_dump_one(f, show_defaults, lp_ctx->services[iService], lp_ctx->sDefault);
+}
+
+/**
+ * Display the contents of one service in human-readable form.
+ */
+void lp_dump_one(FILE *f, bool show_defaults, struct loadparm_service *service, struct loadparm_service *sDefault)
+{
+ if (service != NULL) {
+ if (service->szService[0] == '\0')
+ return;
+ dump_a_service(service, sDefault, f);
+ }
+}
+
+struct loadparm_service *lp_servicebynum(struct loadparm_context *lp_ctx,
+ int snum)
+{
+ return lp_ctx->services[snum];
+}
+
+struct loadparm_service *lp_service(struct loadparm_context *lp_ctx,
+ const char *service_name)
+{
+ int iService;
+ char *serviceName;
+
+ for (iService = lp_ctx->iNumServices - 1; iService >= 0; iService--) {
+ if (lp_ctx->services[iService] &&
+ lp_ctx->services[iService]->szService) {
+ /*
+ * The substitution here is used to support %U is
+ * service names
+ */
+ serviceName = standard_sub_basic(
+ lp_ctx->services[iService],
+ lp_ctx->services[iService]->szService);
+ if (strequal(serviceName, service_name))
+ return lp_ctx->services[iService];
+ }
+ }
+
+ DEBUG(7,("lp_servicenumber: couldn't find %s\n", service_name));
+ return NULL;
+}
+
+
+/**
+ * A useful volume label function.
+ */
+const char *volume_label(struct loadparm_service *service, struct loadparm_service *sDefault)
+{
+ const char *ret = lp_volume(service, sDefault);
+ if (!*ret)
+ return lp_servicename(service);
+ return ret;
+}
+
+
+/**
+ * If we are PDC then prefer us as DMB
+ */
+const char *lp_printername(struct loadparm_service *service, struct loadparm_service *sDefault)
+{
+ const char *ret = _lp_printername(service, sDefault);
+ if (ret == NULL || (ret != NULL && *ret == '\0'))
+ ret = lp_servicename(service);
+
+ return ret;
+}
+
+
+/**
+ * Return the max print jobs per queue.
+ */
+int lp_maxprintjobs(struct loadparm_service *service, struct loadparm_service *sDefault)
+{
+ int maxjobs = (service != NULL) ? service->iMaxPrintJobs : sDefault->iMaxPrintJobs;
+ if (maxjobs <= 0 || maxjobs >= PRINT_MAX_JOBID)
+ maxjobs = PRINT_MAX_JOBID - 1;
+
+ return maxjobs;
+}
+
+struct smb_iconv_convenience *lp_iconv_convenience(struct loadparm_context *lp_ctx)
+{
+ if (lp_ctx == NULL) {
+ static struct smb_iconv_convenience *fallback_ic = NULL;
+ if (fallback_ic == NULL)
+ fallback_ic = smb_iconv_convenience_init(talloc_autofree_context(),
+ "CP850", "UTF8", true);
+ return fallback_ic;
+ }
+ return lp_ctx->iconv_convenience;
+}
+
+_PUBLIC_ void reload_charcnv(struct loadparm_context *lp_ctx)
+{
+ talloc_free(lp_ctx->iconv_convenience);
+ global_iconv_convenience = lp_ctx->iconv_convenience = smb_iconv_convenience_init_lp(lp_ctx, lp_ctx);
+}
+
+void lp_smbcli_options(struct loadparm_context *lp_ctx,
+ struct smbcli_options *options)
+{
+ options->max_xmit = lp_max_xmit(lp_ctx);
+ options->max_mux = lp_maxmux(lp_ctx);
+ options->use_spnego = lp_nt_status_support(lp_ctx) && lp_use_spnego(lp_ctx);
+ options->signing = lp_client_signing(lp_ctx);
+ options->request_timeout = SMB_REQUEST_TIMEOUT;
+ options->ntstatus_support = lp_nt_status_support(lp_ctx);
+ options->max_protocol = lp_cli_maxprotocol(lp_ctx);
+ options->unicode = lp_unicode(lp_ctx);
+ options->use_oplocks = true;
+ options->use_level2_oplocks = true;
+}
+
+void lp_smbcli_session_options(struct loadparm_context *lp_ctx,
+ struct smbcli_session_options *options)
+{
+ options->lanman_auth = lp_client_lanman_auth(lp_ctx);
+ options->ntlmv2_auth = lp_client_ntlmv2_auth(lp_ctx);
+ options->plaintext_auth = lp_client_plaintext_auth(lp_ctx);
+}
+
+_PUBLIC_ char *lp_tls_keyfile(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx)
+{
+ return private_path(mem_ctx, lp_ctx, lp_ctx->globals->tls_keyfile);
+}
+
+_PUBLIC_ char *lp_tls_certfile(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx)
+{
+ return private_path(mem_ctx, lp_ctx, lp_ctx->globals->tls_certfile);
+}
+
+_PUBLIC_ char *lp_tls_cafile(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx)
+{
+ return private_path(mem_ctx, lp_ctx, lp_ctx->globals->tls_cafile);
+}
+
+_PUBLIC_ char *lp_tls_crlfile(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx)
+{
+ return private_path(mem_ctx, lp_ctx, lp_ctx->globals->tls_crlfile);
+}
+
+_PUBLIC_ char *lp_tls_dhpfile(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx)
+{
+ return private_path(mem_ctx, lp_ctx, lp_ctx->globals->tls_dhpfile);
+}
+
+_PUBLIC_ struct dcerpc_server_info *lp_dcerpc_server_info(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx)
+{
+ struct dcerpc_server_info *ret = talloc_zero(mem_ctx, struct dcerpc_server_info);
+
+ ret->domain_name = talloc_reference(mem_ctx, lp_workgroup(lp_ctx));
+ ret->version_major = lp_parm_int(lp_ctx, NULL, "server_info", "version_major", 5);
+ ret->version_minor = lp_parm_int(lp_ctx, NULL, "server_info", "version_minor", 2);
+ ret->version_build = lp_parm_int(lp_ctx, NULL, "server_info", "version_build", 3790);
+
+ return ret;
+}
+
+struct gensec_settings *lp_gensec_settings(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx)
+{
+ struct gensec_settings *settings = talloc(mem_ctx, struct gensec_settings);
+ if (settings == NULL)
+ return NULL;
+ SMB_ASSERT(lp_ctx != NULL);
+ settings->lp_ctx = talloc_reference(settings, lp_ctx);
+ settings->iconv_convenience = lp_iconv_convenience(lp_ctx);
+ settings->target_hostname = lp_parm_string(lp_ctx, NULL, "gensec", "target_hostname");
+ return settings;
+}
diff --git a/source4/param/loadparm.h b/source4/param/loadparm.h
new file mode 100644
index 0000000000..454d3f8853
--- /dev/null
+++ b/source4/param/loadparm.h
@@ -0,0 +1,99 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ type definitions for loadparm
+
+ Copyright (C) Karl Auer 1993-1998
+
+ Largely re-written by Andrew Tridgell, September 1994
+
+ Copyright (C) Simo Sorce 2001
+ Copyright (C) Alexander Bokovoy 2002
+ Copyright (C) Stefan (metze) Metzmacher 2002
+ Copyright (C) Jim McDonough (jmcd@us.ibm.com) 2003.
+ Copyright (C) James Myers 2003 <myersjj@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* the following are used by loadparm for option lists */
+typedef enum {
+ P_BOOL,P_INTEGER,P_OCTAL,P_BYTES,P_LIST,P_STRING,P_USTRING,P_ENUM
+} parm_type;
+
+typedef enum {
+ P_LOCAL,P_GLOBAL,P_NONE
+} parm_class;
+
+struct enum_list {
+ int value;
+ const char *name;
+};
+
+struct loadparm_context;
+
+struct parm_struct {
+ const char *label;
+ parm_type type;
+ parm_class pclass;
+ int offset;
+ bool (*special)(struct loadparm_context *, const char *, char **);
+ const struct enum_list *enum_list;
+ union {
+ int bvalue;
+ int ivalue;
+ char *svalue;
+ char cvalue;
+ const char **lvalue;
+ } def;
+};
+
+#define FLAG_DEFAULT 0x0001 /* this option was a default */
+#define FLAG_CMDLINE 0x0002 /* this option was set from the command line */
+
+#ifndef PRINTERS_NAME
+#define PRINTERS_NAME "printers"
+#endif
+
+#ifndef HOMES_NAME
+#define HOMES_NAME "homes"
+#endif
+
+/* This defines the section name in the configuration file that will contain */
+/* global parameters - that is, parameters relating to the whole server, not */
+/* just services. This name is then reserved, and may not be used as a */
+/* a service name. It will default to "global" if not defined here. */
+#ifndef GLOBAL_NAME
+#define GLOBAL_NAME "global"
+#define GLOBAL_NAME2 "globals"
+#endif
+
+/* The default workgroup - usually overridden in smb.conf */
+#ifndef DEFAULT_WORKGROUP
+#define DEFAULT_WORKGROUP "WORKGROUP"
+#endif
+
+/*
+ * Default passwd chat script.
+ */
+#ifndef DEFAULT_PASSWD_CHAT
+#define DEFAULT_PASSWD_CHAT "*new*password* %n\\n *new*password* %n\\n *changed*"
+#endif
+
+/* Max number of jobs per print queue. */
+#ifndef PRINT_MAX_JOBID
+#define PRINT_MAX_JOBID 10000
+#endif
+
+
diff --git a/source4/param/param.h b/source4/param/param.h
new file mode 100644
index 0000000000..3d257be062
--- /dev/null
+++ b/source4/param/param.h
@@ -0,0 +1,442 @@
+/*
+ Unix SMB/CIFS implementation.
+ Generic parameter parsing interface
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _PARAM_H /* _PARAM_H */
+#define _PARAM_H
+
+struct param_opt {
+ struct param_opt *prev, *next;
+ char *key;
+ char *value;
+ int priority;
+};
+
+struct param_context {
+ struct param_section *sections;
+};
+
+struct param_section {
+ const char *name;
+ struct param_section *prev, *next;
+ struct param_opt *parameters;
+};
+
+struct param_context;
+struct smbsrv_connection;
+
+#define Auto (2)
+
+typedef NTSTATUS (*init_module_fn) (void);
+
+/* this needs to be a string which is not in the C library. We
+ previously used "init_module", but that meant that modules which
+ did not define this function ended up calling the C library
+ function init_module() which makes a system call */
+#define SAMBA_INIT_MODULE "samba_init_module"
+
+enum server_role {
+ ROLE_STANDALONE=0,
+ ROLE_DOMAIN_MEMBER=1,
+ ROLE_DOMAIN_CONTROLLER=2,
+};
+
+enum announce_as {/* Types of machine we can announce as. */
+ ANNOUNCE_AS_NT_SERVER=1,
+ ANNOUNCE_AS_WIN95=2,
+ ANNOUNCE_AS_WFW=3,
+ ANNOUNCE_AS_NT_WORKSTATION=4
+};
+
+struct loadparm_context;
+struct loadparm_service;
+struct smbcli_options;
+struct smbcli_session_options;
+struct gensec_settings;
+
+void reload_charcnv(struct loadparm_context *lp_ctx);
+
+struct loadparm_service *lp_default_service(struct loadparm_context *lp_ctx);
+struct parm_struct *lp_parm_table(void);
+int lp_server_role(struct loadparm_context *);
+const char **lp_smb_ports(struct loadparm_context *);
+int lp_nbt_port(struct loadparm_context *);
+int lp_dgram_port(struct loadparm_context *);
+int lp_cldap_port(struct loadparm_context *);
+int lp_krb5_port(struct loadparm_context *);
+int lp_kpasswd_port(struct loadparm_context *);
+int lp_web_port(struct loadparm_context *);
+const char *lp_swat_directory(struct loadparm_context *);
+bool lp_tls_enabled(struct loadparm_context *);
+char *lp_tls_keyfile(TALLOC_CTX *mem_ctx, struct loadparm_context *);
+char *lp_tls_certfile(TALLOC_CTX *mem_ctx, struct loadparm_context *);
+char *lp_tls_cafile(TALLOC_CTX *mem_ctx, struct loadparm_context *);
+char *lp_tls_crlfile(TALLOC_CTX *mem_ctx, struct loadparm_context *);
+char *lp_tls_dhpfile(TALLOC_CTX *mem_ctx, struct loadparm_context *);
+const char *lp_share_backend(struct loadparm_context *);
+const char *lp_sam_url(struct loadparm_context *);
+const char *lp_idmap_url(struct loadparm_context *);
+const char *lp_secrets_url(struct loadparm_context *);
+const char *lp_spoolss_url(struct loadparm_context *);
+const char *lp_wins_config_url(struct loadparm_context *);
+const char *lp_wins_url(struct loadparm_context *);
+const char *lp_winbind_separator(struct loadparm_context *);
+const char *lp_winbindd_socket_directory(struct loadparm_context *);
+const char *lp_winbindd_privileged_socket_directory(struct loadparm_context *);
+const char *lp_template_shell(struct loadparm_context *);
+const char *lp_template_homedir(struct loadparm_context *);
+bool lp_winbind_sealed_pipes(struct loadparm_context *);
+bool lp_idmap_trusted_only(struct loadparm_context *);
+const char *lp_private_dir(struct loadparm_context *);
+const char *lp_serverstring(struct loadparm_context *);
+const char *lp_lockdir(struct loadparm_context *);
+const char *lp_modulesdir(struct loadparm_context *);
+const char *lp_setupdir(struct loadparm_context *);
+const char *lp_ncalrpc_dir(struct loadparm_context *);
+const char *lp_dos_charset(struct loadparm_context *);
+const char *lp_unix_charset(struct loadparm_context *);
+const char *lp_display_charset(struct loadparm_context *);
+const char *lp_piddir(struct loadparm_context *);
+const char **lp_dcerpc_endpoint_servers(struct loadparm_context *);
+const char **lp_server_services(struct loadparm_context *);
+const char *lp_ntptr_providor(struct loadparm_context *);
+const char *lp_auto_services(struct loadparm_context *);
+const char *lp_passwd_chat(struct loadparm_context *);
+const char **lp_passwordserver(struct loadparm_context *);
+const char **lp_name_resolve_order(struct loadparm_context *);
+const char *lp_realm(struct loadparm_context *);
+const char *lp_socket_options(struct loadparm_context *);
+const char *lp_workgroup(struct loadparm_context *);
+const char *lp_netbios_name(struct loadparm_context *);
+const char *lp_netbios_scope(struct loadparm_context *);
+const char **lp_wins_server_list(struct loadparm_context *);
+const char **lp_interfaces(struct loadparm_context *);
+const char *lp_socket_address(struct loadparm_context *);
+const char **lp_netbios_aliases(struct loadparm_context *);
+bool lp_disable_netbios(struct loadparm_context *);
+bool lp_wins_support(struct loadparm_context *);
+bool lp_wins_dns_proxy(struct loadparm_context *);
+const char *lp_wins_hook(struct loadparm_context *);
+bool lp_local_master(struct loadparm_context *);
+bool lp_readraw(struct loadparm_context *);
+bool lp_large_readwrite(struct loadparm_context *);
+bool lp_writeraw(struct loadparm_context *);
+bool lp_null_passwords(struct loadparm_context *);
+bool lp_obey_pam_restrictions(struct loadparm_context *);
+bool lp_encrypted_passwords(struct loadparm_context *);
+bool lp_time_server(struct loadparm_context *);
+bool lp_bind_interfaces_only(struct loadparm_context *);
+bool lp_unicode(struct loadparm_context *);
+bool lp_nt_status_support(struct loadparm_context *);
+bool lp_lanman_auth(struct loadparm_context *);
+bool lp_ntlm_auth(struct loadparm_context *);
+bool lp_client_plaintext_auth(struct loadparm_context *);
+bool lp_client_lanman_auth(struct loadparm_context *);
+bool lp_client_ntlmv2_auth(struct loadparm_context *);
+bool lp_client_use_spnego_principal(struct loadparm_context *);
+bool lp_host_msdfs(struct loadparm_context *);
+bool lp_unix_extensions(struct loadparm_context *);
+bool lp_use_spnego(struct loadparm_context *);
+bool lp_rpc_big_endian(struct loadparm_context *);
+int lp_max_wins_ttl(struct loadparm_context *);
+int lp_min_wins_ttl(struct loadparm_context *);
+int lp_maxmux(struct loadparm_context *);
+int lp_max_xmit(struct loadparm_context *);
+int lp_passwordlevel(struct loadparm_context *);
+int lp_srv_maxprotocol(struct loadparm_context *);
+int lp_srv_minprotocol(struct loadparm_context *);
+int lp_cli_maxprotocol(struct loadparm_context *);
+int lp_cli_minprotocol(struct loadparm_context *);
+int lp_security(struct loadparm_context *);
+bool lp_paranoid_server_security(struct loadparm_context *);
+int lp_announce_as(struct loadparm_context *);
+
+const char *lp_servicename(const struct loadparm_service *service);
+const char *lp_pathname(struct loadparm_service *, struct loadparm_service *);
+const char **lp_hostsallow(struct loadparm_service *, struct loadparm_service *);
+const char **lp_hostsdeny(struct loadparm_service *, struct loadparm_service *);
+const char *lp_comment(struct loadparm_service *, struct loadparm_service *);
+const char *lp_fstype(struct loadparm_service *, struct loadparm_service *);
+const char **lp_ntvfs_handler(struct loadparm_service *, struct loadparm_service *);
+bool lp_msdfs_root(struct loadparm_service *, struct loadparm_service *);
+bool lp_browseable(struct loadparm_service *, struct loadparm_service *);
+bool lp_readonly(struct loadparm_service *, struct loadparm_service *);
+bool lp_print_ok(struct loadparm_service *, struct loadparm_service *);
+bool lp_map_hidden(struct loadparm_service *, struct loadparm_service *);
+bool lp_map_archive(struct loadparm_service *, struct loadparm_service *);
+bool lp_strict_locking(struct loadparm_service *, struct loadparm_service *);
+bool lp_oplocks(struct loadparm_service *, struct loadparm_service *);
+bool lp_strict_sync(struct loadparm_service *, struct loadparm_service *);
+bool lp_ci_filesystem(struct loadparm_service *, struct loadparm_service *);
+bool lp_map_system(struct loadparm_service *, struct loadparm_service *);
+int lp_max_connections(struct loadparm_service *, struct loadparm_service *);
+int lp_csc_policy(struct loadparm_service *, struct loadparm_service *);
+int lp_create_mask(struct loadparm_service *, struct loadparm_service *);
+int lp_force_create_mode(struct loadparm_service *, struct loadparm_service *);
+int lp_dir_mask(struct loadparm_service *, struct loadparm_service *);
+int lp_force_dir_mode(struct loadparm_service *, struct loadparm_service *);
+int lp_server_signing(struct loadparm_context *);
+int lp_client_signing(struct loadparm_context *);
+const char *lp_ntp_signd_socket_directory(struct loadparm_context *);
+
+
+const char *lp_get_parametric(struct loadparm_context *lp_ctx,
+ struct loadparm_service *service,
+ const char *type, const char *option);
+
+const char *lp_parm_string(struct loadparm_context *lp_ctx,
+ struct loadparm_service *service, const char *type,
+ const char *option);
+const char **lp_parm_string_list(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ struct loadparm_service *service,
+ const char *type,
+ const char *option, const char *separator);
+int lp_parm_int(struct loadparm_context *lp_ctx,
+ struct loadparm_service *service, const char *type,
+ const char *option, int default_v);
+int lp_parm_bytes(struct loadparm_context *lp_ctx,
+ struct loadparm_service *service, const char *type,
+ const char *option, int default_v);
+unsigned long lp_parm_ulong(struct loadparm_context *lp_ctx,
+ struct loadparm_service *service, const char *type,
+ const char *option, unsigned long default_v);
+double lp_parm_double(struct loadparm_context *lp_ctx,
+ struct loadparm_service *service, const char *type,
+ const char *option, double default_v);
+bool lp_parm_bool(struct loadparm_context *lp_ctx,
+ struct loadparm_service *service, const char *type,
+ const char *option, bool default_v);
+struct loadparm_service *lp_add_service(struct loadparm_context *lp_ctx,
+ const struct loadparm_service *pservice,
+ const char *name);
+bool lp_add_home(struct loadparm_context *lp_ctx,
+ const char *pszHomename,
+ struct loadparm_service *default_service,
+ const char *user, const char *pszHomedir);
+bool lp_add_printer(struct loadparm_context *lp_ctx,
+ const char *pszPrintername,
+ struct loadparm_service *default_service);
+struct parm_struct *lp_parm_struct(const char *name);
+void *lp_parm_ptr(struct loadparm_context *lp_ctx,
+ struct loadparm_service *service, struct parm_struct *parm);
+bool lp_file_list_changed(struct loadparm_context *lp_ctx);
+
+bool lp_do_global_parameter(struct loadparm_context *lp_ctx,
+ const char *pszParmName, const char *pszParmValue);
+bool lp_do_service_parameter(struct loadparm_context *lp_ctx,
+ struct loadparm_service *service,
+ const char *pszParmName, const char *pszParmValue);
+
+/**
+ * Process a parameter.
+ */
+bool lp_do_global_parameter_var(struct loadparm_context *lp_ctx,
+ const char *pszParmName, const char *fmt, ...);
+bool lp_set_cmdline(struct loadparm_context *lp_ctx, const char *pszParmName,
+ const char *pszParmValue);
+bool lp_set_option(struct loadparm_context *lp_ctx, const char *option);
+
+/**
+ * Display the contents of a single services record.
+ */
+bool lp_dump_a_parameter(struct loadparm_context *lp_ctx,
+ struct loadparm_service *service,
+ const char *parm_name, FILE * f);
+
+/**
+ * Return info about the next service in a service. snum==-1 gives the globals.
+ * Return NULL when out of parameters.
+ */
+struct parm_struct *lp_next_parameter(struct loadparm_context *lp_ctx, int snum, int *i,
+ int allparameters);
+
+/**
+ * Unload unused services.
+ */
+void lp_killunused(struct loadparm_context *lp_ctx,
+ struct smbsrv_connection *smb,
+ bool (*snumused) (struct smbsrv_connection *, int));
+
+/**
+ * Initialise the global parameter structure.
+ */
+struct loadparm_context *loadparm_init(TALLOC_CTX *mem_ctx);
+const char *lp_configfile(struct loadparm_context *lp_ctx);
+bool lp_load_default(struct loadparm_context *lp_ctx);
+const char *lp_default_path(void);
+
+/**
+ * Load the services array from the services file.
+ *
+ * Return True on success, False on failure.
+ */
+bool lp_load(struct loadparm_context *lp_ctx, const char *filename);
+
+/**
+ * Return the max number of services.
+ */
+int lp_numservices(struct loadparm_context *lp_ctx);
+
+/**
+ * Display the contents of the services array in human-readable form.
+ */
+void lp_dump(struct loadparm_context *lp_ctx, FILE *f, bool show_defaults,
+ int maxtoprint);
+
+/**
+ * Display the contents of one service in human-readable form.
+ */
+void lp_dump_one(FILE *f, bool show_defaults, struct loadparm_service *service, struct loadparm_service *sDefault);
+struct loadparm_service *lp_servicebynum(struct loadparm_context *lp_ctx,
+ int snum);
+struct loadparm_service *lp_service(struct loadparm_context *lp_ctx,
+ const char *service_name);
+
+/**
+ * A useful volume label function.
+ */
+const char *volume_label(struct loadparm_service *service, struct loadparm_service *sDefault);
+
+/**
+ * If we are PDC then prefer us as DMB
+ */
+const char *lp_printername(struct loadparm_service *service, struct loadparm_service *sDefault);
+
+/**
+ * Return the max print jobs per queue.
+ */
+int lp_maxprintjobs(struct loadparm_service *service, struct loadparm_service *sDefault);
+struct smb_iconv_convenience *lp_iconv_convenience(struct loadparm_context *lp_ctx);
+void lp_smbcli_options(struct loadparm_context *lp_ctx,
+ struct smbcli_options *options);
+void lp_smbcli_session_options(struct loadparm_context *lp_ctx,
+ struct smbcli_session_options *options);
+struct dcerpc_server_info *lp_dcerpc_server_info(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx);
+struct gensec_settings *lp_gensec_settings(TALLOC_CTX *, struct loadparm_context *);
+
+
+/* The following definitions come from param/generic.c */
+
+struct param_section *param_get_section(struct param_context *ctx, const char *name);
+struct param_opt *param_section_get(struct param_section *section,
+ const char *name);
+struct param_opt *param_get (struct param_context *ctx, const char *name, const char *section_name);
+struct param_section *param_add_section(struct param_context *ctx, const char *section_name);
+struct param_opt *param_get_add(struct param_context *ctx, const char *name, const char *section_name);
+const char *param_get_string(struct param_context *ctx, const char *param, const char *section);
+int param_set_string(struct param_context *ctx, const char *param, const char *value, const char *section);
+const char **param_get_string_list(struct param_context *ctx, const char *param, const char *separator, const char *section);
+int param_set_string_list(struct param_context *ctx, const char *param, const char **list, const char *section);
+int param_get_int(struct param_context *ctx, const char *param, int default_v, const char *section);
+void param_set_int(struct param_context *ctx, const char *param, int value, const char *section);
+unsigned long param_get_ulong(struct param_context *ctx, const char *param, unsigned long default_v, const char *section);
+void param_set_ulong(struct param_context *ctx, const char *name, unsigned long value, const char *section);
+struct param_context *param_init(TALLOC_CTX *mem_ctx);
+int param_read(struct param_context *ctx, const char *fn);
+int param_use(struct loadparm_context *lp_ctx, struct param_context *ctx);
+int param_write(struct param_context *ctx, const char *fn);
+
+/* The following definitions come from param/util.c */
+
+
+/**
+ * @file
+ * @brief Misc utility functions
+ */
+bool lp_is_mydomain(struct loadparm_context *lp_ctx,
+ const char *domain);
+
+/**
+ see if a string matches either our primary or one of our secondary
+ netbios aliases. do a case insensitive match
+*/
+bool lp_is_myname(struct loadparm_context *lp_ctx, const char *name);
+
+/**
+ A useful function for returning a path in the Samba lock directory.
+**/
+char *lock_path(TALLOC_CTX* mem_ctx, struct loadparm_context *lp_ctx,
+ const char *name);
+
+/**
+ * @brief Returns an absolute path to a file in the directory containing the current config file
+ *
+ * @param name File to find, relative to the config file directory.
+ *
+ * @retval Pointer to a talloc'ed string containing the full path.
+ **/
+char *config_path(TALLOC_CTX* mem_ctx, struct loadparm_context *lp_ctx,
+ const char *name);
+
+/**
+ * @brief Returns an absolute path to a file in the Samba private directory.
+ *
+ * @param name File to find, relative to PRIVATEDIR.
+ * if name is not relative, then use it as-is
+ *
+ * @retval Pointer to a talloc'ed string containing the full path.
+ **/
+char *private_path(TALLOC_CTX* mem_ctx,
+ struct loadparm_context *lp_ctx,
+ const char *name);
+
+/**
+ return a path in the smbd.tmp directory, where all temporary file
+ for smbd go. If NULL is passed for name then return the directory
+ path itself
+*/
+char *smbd_tmp_path(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ const char *name);
+
+/**
+ * Obtain the init function from a shared library file
+ */
+init_module_fn load_module(TALLOC_CTX *mem_ctx, const char *path);
+
+/**
+ * Obtain list of init functions from the modules in the specified
+ * directory
+ */
+init_module_fn *load_modules(TALLOC_CTX *mem_ctx, const char *path);
+
+/**
+ * Run the specified init functions.
+ *
+ * @return true if all functions ran successfully, false otherwise
+ */
+bool run_init_functions(init_module_fn *fns);
+
+/**
+ * Load the initialization functions from DSO files for a specific subsystem.
+ *
+ * Will return an array of function pointers to initialization functions
+ */
+init_module_fn *load_samba_modules(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx, const char *subsystem);
+const char *lp_messaging_path(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx);
+struct smb_iconv_convenience *smb_iconv_convenience_init_lp(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx);
+
+/* The following definitions come from lib/version.c */
+
+const char *samba_version_string(void);
+
+
+#endif /* _PARAM_H */
diff --git a/source4/param/provision.c b/source4/param/provision.c
new file mode 100644
index 0000000000..c8bff59deb
--- /dev/null
+++ b/source4/param/provision.c
@@ -0,0 +1,143 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Jelmer Vernooij <jelmer@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "auth/auth.h"
+#include "lib/ldb_wrap.h"
+#include "libcli/raw/libcliraw.h"
+#include "librpc/ndr/libndr.h"
+
+#include "param/param.h"
+#include "param/provision.h"
+#include <Python.h>
+#include "scripting/python/modules.h"
+#include "lib/ldb/pyldb.h"
+#include "param/pyparam.h"
+
+NTSTATUS provision_bare(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx,
+ struct provision_settings *settings,
+ struct provision_result *result)
+{
+ char *configfile;
+ PyObject *provision_mod, *provision_dict, *provision_fn, *py_result, *parameters;
+
+ DEBUG(0,("Provision for Become-DC test using python\n"));
+
+ py_load_samba_modules();
+ Py_Initialize();
+ py_update_path("bin"); /* FIXME: Can't assume this is always the case */
+
+ provision_mod = PyImport_Import(PyString_FromString("samba.provision"));
+
+ if (provision_mod == NULL) {
+ PyErr_Print();
+ DEBUG(0, ("Unable to import provision Python module.\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ provision_dict = PyModule_GetDict(provision_mod);
+
+ if (provision_dict == NULL) {
+ DEBUG(0, ("Unable to get dictionary for provision module\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ provision_fn = PyDict_GetItemString(provision_dict, "provision_become_dc");
+ if (provision_fn == NULL) {
+ PyErr_Print();
+ DEBUG(0, ("Unable to get provision_become_dc function\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ DEBUG(0,("New Server in Site[%s]\n",
+ settings->site_name));
+
+ DEBUG(0,("DSA Instance [%s]\n"
+ "\tinvocationId[%s]\n",
+ settings->ntds_dn_str,
+ settings->invocation_id == NULL?"None":GUID_string(mem_ctx, settings->invocation_id)));
+
+ DEBUG(0,("Pathes under targetdir[%s]\n",
+ settings->targetdir));
+ parameters = PyDict_New();
+
+ configfile = lp_configfile(lp_ctx);
+ if (configfile != NULL) {
+ PyDict_SetItemString(parameters, "smbconf",
+ PyString_FromString(configfile));
+ }
+
+ PyDict_SetItemString(parameters, "rootdn",
+ PyString_FromString(settings->root_dn_str));
+ if (settings->targetdir != NULL)
+ PyDict_SetItemString(parameters, "targetdir",
+ PyString_FromString(settings->targetdir));
+ PyDict_SetItemString(parameters, "setup_dir",
+ PyString_FromString("setup"));
+ PyDict_SetItemString(parameters, "hostname",
+ PyString_FromString(settings->netbios_name));
+ PyDict_SetItemString(parameters, "domain",
+ PyString_FromString(settings->domain));
+ PyDict_SetItemString(parameters, "realm",
+ PyString_FromString(settings->realm));
+ if (settings->root_dn_str)
+ PyDict_SetItemString(parameters, "rootdn",
+ PyString_FromString(settings->root_dn_str));
+
+ if (settings->domain_dn_str)
+ PyDict_SetItemString(parameters, "domaindn",
+ PyString_FromString(settings->domain_dn_str));
+
+ if (settings->schema_dn_str)
+ PyDict_SetItemString(parameters, "schemadn",
+ PyString_FromString(settings->schema_dn_str));
+
+ if (settings->config_dn_str)
+ PyDict_SetItemString(parameters, "configdn",
+ PyString_FromString(settings->config_dn_str));
+
+ if (settings->server_dn_str)
+ PyDict_SetItemString(parameters, "serverdn",
+ PyString_FromString(settings->server_dn_str));
+
+ if (settings->site_name)
+ PyDict_SetItemString(parameters, "sitename",
+ PyString_FromString(settings->site_name));
+
+ PyDict_SetItemString(parameters, "machinepass",
+ PyString_FromString(settings->machine_password));
+
+ py_result = PyEval_CallObjectWithKeywords(provision_fn, NULL, parameters);
+
+ Py_DECREF(parameters);
+
+ if (py_result == NULL) {
+ PyErr_Print();
+ PyErr_Clear();
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ result->domaindn = talloc_strdup(mem_ctx, PyString_AsString(PyObject_GetAttrString(py_result, "domaindn")));
+
+ /* FIXME paths */
+ result->lp_ctx = lp_from_py_object(PyObject_GetAttrString(py_result, "lp"));
+ result->samdb = PyLdb_AsLdbContext(PyObject_GetAttrString(py_result, "samdb"));
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/param/provision.h b/source4/param/provision.h
new file mode 100644
index 0000000000..af9685d292
--- /dev/null
+++ b/source4/param/provision.h
@@ -0,0 +1,51 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Jelmer Vernooij <jelmer@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _PROVISION_H_
+#define _PROVISION_H_
+
+struct provision_settings {
+ const char *site_name;
+ const char *root_dn_str;
+ const char *domain_dn_str;
+ const char *config_dn_str;
+ const char *schema_dn_str;
+ const char *server_dn_str;
+ const struct GUID *invocation_id;
+ const char *netbios_name;
+ const char *host_ip;
+ const char *realm;
+ const char *domain;
+ const char *ntds_dn_str;
+ const char *machine_password;
+ const char *targetdir;
+};
+
+/* FIXME: Rename this to hostconfig ? */
+struct provision_result {
+ const char *domaindn;
+ struct ldb_context *samdb;
+ struct loadparm_context *lp_ctx;
+};
+
+NTSTATUS provision_bare(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx,
+ struct provision_settings *settings,
+ struct provision_result *result);
+
+#endif /* _PROVISION_H_ */
diff --git a/source4/param/pyparam.c b/source4/param/pyparam.c
new file mode 100644
index 0000000000..efaedf7b41
--- /dev/null
+++ b/source4/param/pyparam.c
@@ -0,0 +1,385 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "includes.h"
+#include "param/param.h"
+#include "param/loadparm.h"
+#include <Python.h>
+#include "pytalloc.h"
+
+/* There's no Py_ssize_t in 2.4, apparently */
+#if PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION < 5
+typedef int Py_ssize_t;
+typedef inquiry lenfunc;
+#endif
+
+#ifndef Py_RETURN_NONE
+#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
+#endif
+
+#define PyLoadparmContext_AsLoadparmContext(obj) py_talloc_get_type(obj, struct loadparm_context)
+
+PyAPI_DATA(PyTypeObject) PyLoadparmContext;
+PyAPI_DATA(PyTypeObject) PyLoadparmService;
+
+PyObject *PyLoadparmService_FromService(struct loadparm_service *service)
+{
+ return py_talloc_import(&PyLoadparmService, service);
+}
+
+static PyObject *py_lp_ctx_get_helper(struct loadparm_context *lp_ctx, const char *service_name, const char *param_name)
+{
+ struct parm_struct *parm = NULL;
+ void *parm_ptr = NULL;
+ int i;
+
+ if (service_name != NULL) {
+ struct loadparm_service *service;
+ /* its a share parameter */
+ service = lp_service(lp_ctx, service_name);
+ if (service == NULL) {
+ return NULL;
+ }
+ if (strchr(param_name, ':')) {
+ /* its a parametric option on a share */
+ const char *type = talloc_strndup(lp_ctx,
+ param_name,
+ strcspn(param_name, ":"));
+ const char *option = strchr(param_name, ':') + 1;
+ const char *value;
+ if (type == NULL || option == NULL) {
+ return NULL;
+ }
+ value = lp_get_parametric(lp_ctx, service, type, option);
+ if (value == NULL) {
+ return NULL;
+ }
+ return PyString_FromString(value);
+ }
+
+ parm = lp_parm_struct(param_name);
+ if (parm == NULL || parm->pclass == P_GLOBAL) {
+ return NULL;
+ }
+ parm_ptr = lp_parm_ptr(lp_ctx, service, parm);
+ } else if (strchr(param_name, ':')) {
+ /* its a global parametric option */
+ const char *type = talloc_strndup(lp_ctx,
+ param_name, strcspn(param_name, ":"));
+ const char *option = strchr(param_name, ':') + 1;
+ const char *value;
+ if (type == NULL || option == NULL) {
+ return NULL;
+ }
+ value = lp_get_parametric(lp_ctx, NULL, type, option);
+ if (value == NULL)
+ return NULL;
+ return PyString_FromString(value);
+ } else {
+ /* its a global parameter */
+ parm = lp_parm_struct(param_name);
+ if (parm == NULL) {
+ return NULL;
+ }
+ parm_ptr = lp_parm_ptr(lp_ctx, NULL, parm);
+ }
+
+ if (parm == NULL || parm_ptr == NULL) {
+ return NULL;
+ }
+
+ /* construct and return the right type of python object */
+ switch (parm->type) {
+ case P_STRING:
+ case P_USTRING:
+ return PyString_FromString(*(char **)parm_ptr);
+ case P_BOOL:
+ return PyBool_FromLong(*(bool *)parm_ptr);
+ case P_INTEGER:
+ case P_OCTAL:
+ case P_BYTES:
+ return PyLong_FromLong(*(int *)parm_ptr);
+ case P_ENUM:
+ for (i=0; parm->enum_list[i].name; i++) {
+ if (*(int *)parm_ptr == parm->enum_list[i].value) {
+ return PyString_FromString(parm->enum_list[i].name);
+ }
+ }
+ return NULL;
+ case P_LIST:
+ {
+ int j;
+ const char **strlist = *(const char ***)parm_ptr;
+ PyObject *pylist = PyList_New(str_list_length(strlist));
+ for (j = 0; strlist[j]; j++)
+ PyList_SetItem(pylist, j,
+ PyString_FromString(strlist[j]));
+ return pylist;
+ }
+
+ break;
+ }
+ return NULL;
+
+}
+
+static PyObject *py_lp_ctx_load(py_talloc_Object *self, PyObject *args)
+{
+ char *filename;
+ bool ret;
+ if (!PyArg_ParseTuple(args, "s", &filename))
+ return NULL;
+
+ ret = lp_load(PyLoadparmContext_AsLoadparmContext(self), filename);
+
+ if (!ret) {
+ PyErr_Format(PyExc_RuntimeError, "Unable to load file %s", filename);
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_lp_ctx_load_default(py_talloc_Object *self)
+{
+ bool ret;
+ ret = lp_load_default(PyLoadparmContext_AsLoadparmContext(self));
+
+ if (!ret) {
+ PyErr_SetString(PyExc_RuntimeError, "Unable to load default file");
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_lp_ctx_get(py_talloc_Object *self, PyObject *args)
+{
+ char *param_name;
+ char *section_name = NULL;
+ PyObject *ret;
+ if (!PyArg_ParseTuple(args, "s|s", &param_name, &section_name))
+ return NULL;
+
+ ret = py_lp_ctx_get_helper(PyLoadparmContext_AsLoadparmContext(self), section_name, param_name);
+ if (ret == NULL)
+ Py_RETURN_NONE;
+ return ret;
+}
+
+static PyObject *py_lp_ctx_is_myname(py_talloc_Object *self, PyObject *args)
+{
+ char *name;
+ if (!PyArg_ParseTuple(args, "s", &name))
+ return NULL;
+
+ return PyBool_FromLong(lp_is_myname(PyLoadparmContext_AsLoadparmContext(self), name));
+}
+
+static PyObject *py_lp_ctx_is_mydomain(py_talloc_Object *self, PyObject *args)
+{
+ char *name;
+ if (!PyArg_ParseTuple(args, "s", &name))
+ return NULL;
+
+ return PyBool_FromLong(lp_is_mydomain(PyLoadparmContext_AsLoadparmContext(self), name));
+}
+
+static PyObject *py_lp_ctx_set(py_talloc_Object *self, PyObject *args)
+{
+ char *name, *value;
+ bool ret;
+ if (!PyArg_ParseTuple(args, "ss", &name, &value))
+ return NULL;
+
+ ret = lp_set_cmdline(PyLoadparmContext_AsLoadparmContext(self), name, value);
+ if (!ret) {
+ PyErr_SetString(PyExc_RuntimeError, "Unable to set parameter");
+ return NULL;
+ }
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_lp_ctx_private_path(py_talloc_Object *self, PyObject *args)
+{
+ char *name, *path;
+ PyObject *ret;
+ if (!PyArg_ParseTuple(args, "s", &name))
+ return NULL;
+
+ path = private_path(NULL, PyLoadparmContext_AsLoadparmContext(self), name);
+ ret = PyString_FromString(path);
+ talloc_free(path);
+
+ return ret;
+}
+
+static PyMethodDef py_lp_ctx_methods[] = {
+ { "load", (PyCFunction)py_lp_ctx_load, METH_VARARGS,
+ "S.load(filename) -> None\n"
+ "Load specified file." },
+ { "load_default", (PyCFunction)py_lp_ctx_load_default, METH_NOARGS,
+ "S.load_default() -> None\n"
+ "Load default smb.conf file." },
+ { "is_myname", (PyCFunction)py_lp_ctx_is_myname, METH_VARARGS,
+ "S.is_myname(name) -> bool\n"
+ "Check whether the specified name matches one of our netbios names." },
+ { "is_mydomain", (PyCFunction)py_lp_ctx_is_mydomain, METH_VARARGS,
+ "S.is_mydomain(name) -> bool\n"
+ "Check whether the specified name matches our domain name." },
+ { "get", (PyCFunction)py_lp_ctx_get, METH_VARARGS,
+ "S.get(name, service_name) -> value\n"
+ "Find specified parameter." },
+ { "set", (PyCFunction)py_lp_ctx_set, METH_VARARGS,
+ "S.set(name, value) -> bool\n"
+ "Change a parameter." },
+ { "private_path", (PyCFunction)py_lp_ctx_private_path, METH_VARARGS,
+ "S.private_path(name) -> path\n" },
+ { NULL }
+};
+
+static PyObject *py_lp_ctx_default_service(py_talloc_Object *self, void *closure)
+{
+ return PyLoadparmService_FromService(lp_default_service(PyLoadparmContext_AsLoadparmContext(self)));
+}
+
+static PyObject *py_lp_ctx_config_file(py_talloc_Object *self, void *closure)
+{
+ const char *configfile = lp_configfile(PyLoadparmContext_AsLoadparmContext(self));
+ if (configfile == NULL)
+ Py_RETURN_NONE;
+ else
+ return PyString_FromString(configfile);
+}
+
+static PyGetSetDef py_lp_ctx_getset[] = {
+ { discard_const_p(char, "default_service"), (getter)py_lp_ctx_default_service, NULL, NULL },
+ { discard_const_p(char, "configfile"), (getter)py_lp_ctx_config_file, NULL,
+ discard_const_p(char, "Name of last config file that was loaded.") },
+ { NULL }
+};
+
+static PyObject *py_lp_ctx_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
+{
+ return py_talloc_import(type, loadparm_init(NULL));
+}
+
+static Py_ssize_t py_lp_ctx_len(py_talloc_Object *self)
+{
+ return lp_numservices(PyLoadparmContext_AsLoadparmContext(self));
+}
+
+static PyObject *py_lp_ctx_getitem(py_talloc_Object *self, PyObject *name)
+{
+ struct loadparm_service *service;
+ if (!PyString_Check(name)) {
+ PyErr_SetString(PyExc_TypeError, "Only string subscripts are supported");
+ return NULL;
+ }
+ service = lp_service(PyLoadparmContext_AsLoadparmContext(self), PyString_AsString(name));
+ if (service == NULL) {
+ PyErr_SetString(PyExc_KeyError, "No such section");
+ return NULL;
+ }
+ return PyLoadparmService_FromService(service);
+}
+
+static PyMappingMethods py_lp_ctx_mapping = {
+ .mp_length = (lenfunc)py_lp_ctx_len,
+ .mp_subscript = (binaryfunc)py_lp_ctx_getitem,
+};
+
+PyTypeObject PyLoadparmContext = {
+ .tp_name = "LoadParm",
+ .tp_basicsize = sizeof(py_talloc_Object),
+ .tp_dealloc = py_talloc_dealloc,
+ .tp_getset = py_lp_ctx_getset,
+ .tp_methods = py_lp_ctx_methods,
+ .tp_new = py_lp_ctx_new,
+ .tp_as_mapping = &py_lp_ctx_mapping,
+ .tp_flags = Py_TPFLAGS_DEFAULT,
+};
+
+PyTypeObject PyLoadparmService = {
+ .tp_name = "LoadparmService",
+ .tp_dealloc = py_talloc_dealloc,
+ .tp_basicsize = sizeof(py_talloc_Object),
+ .tp_flags = Py_TPFLAGS_DEFAULT,
+};
+
+_PUBLIC_ struct loadparm_context *lp_from_py_object(PyObject *py_obj)
+{
+ struct loadparm_context *lp_ctx;
+ if (PyString_Check(py_obj)) {
+ lp_ctx = loadparm_init(NULL);
+ if (!lp_load(lp_ctx, PyString_AsString(py_obj))) {
+ talloc_free(lp_ctx);
+ PyErr_Format(PyExc_RuntimeError,
+ "Unable to load %s", PyString_AsString(py_obj));
+ return NULL;
+ }
+ return lp_ctx;
+ }
+
+ if (py_obj == Py_None) {
+ lp_ctx = loadparm_init(NULL);
+ /* We're not checking that loading the file succeeded *on purpose */
+ lp_load_default(lp_ctx);
+ return lp_ctx;
+ }
+
+ return PyLoadparmContext_AsLoadparmContext(py_obj);
+}
+
+struct loadparm_context *py_default_loadparm_context(TALLOC_CTX *mem_ctx)
+{
+ struct loadparm_context *ret;
+ ret = loadparm_init(mem_ctx);
+ if (!lp_load_default(ret))
+ return NULL;
+ return ret;
+}
+
+static PyObject *py_default_path(PyObject *self)
+{
+ return PyString_FromString(lp_default_path());
+}
+
+static PyMethodDef pyparam_methods[] = {
+ { "default_path", (PyCFunction)py_default_path, METH_NOARGS,
+ "Returns the default smb.conf path." },
+ { NULL }
+};
+
+void initparam(void)
+{
+ PyObject *m;
+
+ if (PyType_Ready(&PyLoadparmContext) < 0)
+ return;
+
+ m = Py_InitModule3("param", pyparam_methods, "Parsing and writing Samba configuration files.");
+ if (m == NULL)
+ return;
+
+ Py_INCREF(&PyLoadparmContext);
+ PyModule_AddObject(m, "LoadParm", (PyObject *)&PyLoadparmContext);
+}
diff --git a/source4/param/pyparam.h b/source4/param/pyparam.h
new file mode 100644
index 0000000000..1cda8f1dba
--- /dev/null
+++ b/source4/param/pyparam.h
@@ -0,0 +1,25 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Jelmer Vernooij <jelmer@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _PYPARAM_H_
+#define _PYPARAM_H_
+
+_PUBLIC_ struct loadparm_context *lp_from_py_object(PyObject *py_obj);
+
+#endif /* _PYPARAM_H_ */
diff --git a/source4/param/samba-hostconfig.pc.in b/source4/param/samba-hostconfig.pc.in
new file mode 100644
index 0000000000..b8ba24096d
--- /dev/null
+++ b/source4/param/samba-hostconfig.pc.in
@@ -0,0 +1,10 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: samba-hostconfig
+Description: Host-wide Samba configuration
+Version: 0.0.1
+Libs: -L${libdir} -lsamba-hostconfig
+Cflags: -I${includedir} -DHAVE_IMMEDIATE_STRUCTURES=1
diff --git a/source4/param/secrets.c b/source4/param/secrets.c
new file mode 100644
index 0000000000..6c6f7c28f0
--- /dev/null
+++ b/source4/param/secrets.c
@@ -0,0 +1,196 @@
+/*
+ Unix SMB/CIFS implementation.
+ Copyright (C) Andrew Tridgell 1992-2001
+ Copyright (C) Andrew Bartlett 2002
+ Copyright (C) Rafal Szczesniak 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 <http://www.gnu.org/licenses/>.
+*/
+
+/* the Samba secrets database stores any generated, private information
+ such as the local SID and machine trust password */
+
+#include "includes.h"
+#include "secrets.h"
+#include "param/param.h"
+#include "system/filesys.h"
+#include "tdb_wrap.h"
+#include "lib/ldb/include/ldb.h"
+#include "../tdb/include/tdb.h"
+#include "../lib/util/util_tdb.h"
+#include "../lib/util/util_ldb.h"
+#include "librpc/gen_ndr/ndr_security.h"
+
+/**
+ * Use a TDB to store an incrementing random seed.
+ *
+ * Initialised to the current pid, the very first time Samba starts,
+ * and incremented by one each time it is needed.
+ *
+ * @note Not called by systems with a working /dev/urandom.
+ */
+static void get_rand_seed(struct tdb_wrap *secretsdb, int *new_seed)
+{
+ *new_seed = getpid();
+ if (secretsdb != NULL) {
+ tdb_change_int32_atomic(secretsdb->tdb, "INFO/random_seed", new_seed, 1);
+ }
+}
+
+/**
+ * open up the secrets database
+ */
+struct tdb_wrap *secrets_init(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx)
+{
+ char *fname;
+ uint8_t dummy;
+ struct tdb_wrap *tdb;
+
+ fname = private_path(mem_ctx, lp_ctx, "secrets.tdb");
+
+ tdb = tdb_wrap_open(mem_ctx, fname, 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
+
+ if (!tdb) {
+ DEBUG(0,("Failed to open %s\n", fname));
+ talloc_free(fname);
+ return NULL;
+ }
+ talloc_free(fname);
+
+ /**
+ * Set a reseed function for the crypto random generator
+ *
+ * This avoids a problem where systems without /dev/urandom
+ * could send the same challenge to multiple clients
+ */
+ set_rand_reseed_callback((void (*) (void *, int *))get_rand_seed, tdb);
+
+ /* Ensure that the reseed is done now, while we are root, etc */
+ generate_random_buffer(&dummy, sizeof(dummy));
+
+ return tdb;
+}
+
+/**
+ connect to the secrets ldb
+*/
+struct ldb_context *secrets_db_connect(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx)
+{
+ char *path;
+ const char *url;
+ struct ldb_context *ldb;
+
+ url = lp_secrets_url(lp_ctx);
+ if (!url || !url[0]) {
+ return NULL;
+ }
+
+ path = private_path(mem_ctx, lp_ctx, url);
+ if (!path) {
+ return NULL;
+ }
+
+ /* Secrets.ldb *must* always be local. If we call for a
+ * system_session() we will recurse */
+ ldb = ldb_init(mem_ctx, ev_ctx);
+ if (!ldb) {
+ talloc_free(path);
+ return NULL;
+ }
+
+ ldb_set_modules_dir(ldb,
+ talloc_asprintf(ldb, "%s/ldb", lp_modulesdir(lp_ctx)));
+
+ if (ldb_connect(ldb, path, 0, NULL) != 0) {
+ talloc_free(path);
+ return NULL;
+ }
+
+ talloc_free(path);
+
+ return ldb;
+}
+
+/**
+ * Retrieve the domain SID from the secrets database.
+ * @return pointer to a SID object if the SID could be obtained, NULL otherwise
+ */
+struct dom_sid *secrets_get_domain_sid(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx,
+ const char *domain)
+{
+ struct ldb_context *ldb;
+ struct ldb_message **msgs;
+ int ldb_ret;
+ const char *attrs[] = { "objectSid", NULL };
+ struct dom_sid *result = NULL;
+ const struct ldb_val *v;
+ enum ndr_err_code ndr_err;
+
+ ldb = secrets_db_connect(mem_ctx, ev_ctx, lp_ctx);
+ if (ldb == NULL) {
+ DEBUG(5, ("secrets_db_connect failed\n"));
+ return NULL;
+ }
+
+ ldb_ret = gendb_search(ldb, ldb,
+ ldb_dn_new(mem_ctx, ldb, SECRETS_PRIMARY_DOMAIN_DN),
+ &msgs, attrs,
+ SECRETS_PRIMARY_DOMAIN_FILTER, domain);
+
+ if (ldb_ret == -1) {
+ DEBUG(5, ("Error searching for domain SID for %s: %s",
+ domain, ldb_errstring(ldb)));
+ talloc_free(ldb);
+ return NULL;
+ }
+
+ if (ldb_ret == 0) {
+ DEBUG(5, ("Did not find domain record for %s\n", domain));
+ talloc_free(ldb);
+ return NULL;
+ }
+
+ if (ldb_ret > 1) {
+ DEBUG(5, ("Found more than one (%d) domain records for %s\n",
+ ldb_ret, domain));
+ talloc_free(ldb);
+ return NULL;
+ }
+
+ v = ldb_msg_find_ldb_val(msgs[0], "objectSid");
+ if (v == NULL) {
+ DEBUG(0, ("Domain object for %s does not contain a SID!\n",
+ domain));
+ return NULL;
+ }
+ result = talloc(mem_ctx, struct dom_sid);
+ if (result == NULL) {
+ talloc_free(ldb);
+ return NULL;
+ }
+
+ ndr_err = ndr_pull_struct_blob(v, result, NULL, result,
+ (ndr_pull_flags_fn_t)ndr_pull_dom_sid);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ talloc_free(result);
+ talloc_free(ldb);
+ return NULL;
+ }
+
+ return result;
+}
diff --git a/source4/param/secrets.h b/source4/param/secrets.h
new file mode 100644
index 0000000000..743cf684a9
--- /dev/null
+++ b/source4/param/secrets.h
@@ -0,0 +1,46 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * secrets.tdb file format info
+ * Copyright (C) Andrew Tridgell 2000
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _SECRETS_H
+#define _SECRETS_H
+
+#define SECRETS_PRIMARY_DOMAIN_DN "cn=Primary Domains"
+#define SECRETS_PRINCIPALS_DN "cn=Principals"
+#define SECRETS_PRIMARY_DOMAIN_FILTER "(&(flatname=%s)(objectclass=primaryDomain))"
+#define SECRETS_PRIMARY_REALM_FILTER "(&(realm=%s)(objectclass=primaryDomain))"
+#define SECRETS_KRBTGT_SEARCH "(&((|(realm=%s)(flatname=%s))(samAccountName=krbtgt)))"
+#define SECRETS_PRINCIPAL_SEARCH "(&(|(realm=%s)(flatname=%s))(servicePrincipalName=%s))"
+#define SECRETS_LDAP_FILTER "(objectclass=ldapSecret)"
+
+/**
+ * Use a TDB to store an incrementing random seed.
+ *
+ * Initialised to the current pid, the very first time Samba starts,
+ * and incremented by one each time it is needed.
+ *
+ * @note Not called by systems with a working /dev/urandom.
+ */
+struct loadparm_context;
+struct tevent_context;
+struct tdb_wrap *secrets_init(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx);
+struct ldb_context *secrets_db_connect(TALLOC_CTX *mem_ctx, struct tevent_context *ev_ctx, struct loadparm_context *lp_ctx);
+struct dom_sid *secrets_get_domain_sid(TALLOC_CTX *mem_ctx, struct tevent_context *ev_ctx, struct loadparm_context *lp_ctx, const char *domain);
+
+
+#endif /* _SECRETS_H */
diff --git a/source4/param/share.c b/source4/param/share.c
new file mode 100644
index 0000000000..13d591ec1b
--- /dev/null
+++ b/source4/param/share.c
@@ -0,0 +1,156 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Modular shares configuration system
+
+ Copyright (C) Simo Sorce 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "param/share.h"
+#include "param/param.h"
+
+const char *share_string_option(struct share_config *scfg, const char *opt_name, const char *defval)
+{
+ return scfg->ctx->ops->string_option(scfg, opt_name, defval);
+}
+
+int share_int_option(struct share_config *scfg, const char *opt_name, int defval)
+{
+ return scfg->ctx->ops->int_option(scfg, opt_name, defval);
+}
+
+bool share_bool_option(struct share_config *scfg, const char *opt_name, bool defval)
+{
+ return scfg->ctx->ops->bool_option(scfg, opt_name, defval);
+}
+
+const char **share_string_list_option(TALLOC_CTX *mem_ctx, struct share_config *scfg, const char *opt_name)
+{
+ return scfg->ctx->ops->string_list_option(mem_ctx, scfg, opt_name);
+}
+
+NTSTATUS share_list_all(TALLOC_CTX *mem_ctx, struct share_context *sctx, int *count, const char ***names)
+{
+ return sctx->ops->list_all(mem_ctx, sctx, count, names);
+}
+
+NTSTATUS share_get_config(TALLOC_CTX *mem_ctx, struct share_context *sctx, const char *name, struct share_config **scfg)
+{
+ return sctx->ops->get_config(mem_ctx, sctx, name, scfg);
+}
+
+NTSTATUS share_create(struct share_context *sctx, const char *name, struct share_info *info, int count)
+{
+ if (sctx->ops->create) {
+ return sctx->ops->create(sctx, name, info, count);
+ }
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS share_set(struct share_context *sctx, const char *name, struct share_info *info, int count)
+{
+ if (sctx->ops->set) {
+ return sctx->ops->set(sctx, name, info, count);
+ }
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS share_remove(struct share_context *sctx, const char *name)
+{
+ if (sctx->ops->remove) {
+ return sctx->ops->remove(sctx, name);
+ }
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/* List of currently available share backends */
+static struct share_ops **backends = NULL;
+
+static const struct share_ops *share_backend_by_name(const char *name)
+{
+ int i;
+
+ for (i = 0; backends && backends[i]; i++) {
+ if (strcmp(backends[i]->name, name) == 0) {
+ return backends[i];
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ Register the share backend
+*/
+NTSTATUS share_register(const struct share_ops *ops)
+{
+ int i;
+
+ if (share_backend_by_name(ops->name) != NULL) {
+ DEBUG(0,("SHARE backend [%s] already registered\n", ops->name));
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ i = 0;
+ while (backends && backends[i]) {
+ i++;
+ }
+
+ backends = realloc_p(backends, struct share_ops *, i + 2);
+ if (!backends) {
+ smb_panic("out of memory in share_register");
+ }
+
+ backends[i] = (struct share_ops *)smb_xmemdup(ops, sizeof(*ops));
+ backends[i]->name = smb_xstrdup(ops->name);
+
+ backends[i + 1] = NULL;
+
+ DEBUG(3, ("SHARE backend [%s] registered.\n", ops->name));
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS share_get_context_by_name(TALLOC_CTX *mem_ctx, const char *backend_name,
+ struct tevent_context *event_ctx,
+ struct loadparm_context *lp_ctx,
+ struct share_context **ctx)
+{
+ const struct share_ops *ops;
+
+ ops = share_backend_by_name(backend_name);
+ if (!ops) {
+ DEBUG(0, ("share_init_connection: share backend [%s] not found!\n", backend_name));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ return ops->init(mem_ctx, ops, event_ctx, lp_ctx, ctx);
+}
+
+/*
+ initialise the SHARE subsystem
+*/
+NTSTATUS share_init(void)
+{
+ extern NTSTATUS share_ldb_init(void);
+ extern NTSTATUS share_classic_init(void);
+ init_module_fn static_init[] = { STATIC_share_MODULES };
+
+ run_init_functions(static_init);
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/param/share.h b/source4/param/share.h
new file mode 100644
index 0000000000..755d19e337
--- /dev/null
+++ b/source4/param/share.h
@@ -0,0 +1,137 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Modular services configuration
+
+ Copyright (C) Simo Sorce 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _SHARE_H
+#define _SHARE_H
+
+struct share_ops;
+
+struct share_context {
+ const struct share_ops *ops;
+ void *priv_data;
+};
+
+struct share_config {
+ const char *name;
+ struct share_context *ctx;
+ void *opaque;
+};
+
+enum share_info_type {
+ SHARE_INFO_STRING,
+ SHARE_INFO_INT,
+ SHARE_INFO_BLOB
+};
+
+struct share_info {
+ enum share_info_type type;
+ const char *name;
+ void *value;
+};
+
+struct tevent_context;
+
+struct share_ops {
+ const char *name;
+ NTSTATUS (*init)(TALLOC_CTX *, const struct share_ops*, struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx,
+ struct share_context **);
+ const char *(*string_option)(struct share_config *, const char *, const char *);
+ int (*int_option)(struct share_config *, const char *, int);
+ bool (*bool_option)(struct share_config *, const char *, bool);
+ const char **(*string_list_option)(TALLOC_CTX *, struct share_config *, const char *);
+ NTSTATUS (*list_all)(TALLOC_CTX *, struct share_context *, int *, const char ***);
+ NTSTATUS (*get_config)(TALLOC_CTX *, struct share_context *, const char *, struct share_config **);
+ NTSTATUS (*create)(struct share_context *, const char *, struct share_info *, int);
+ NTSTATUS (*set)(struct share_context *, const char *, struct share_info *, int);
+ NTSTATUS (*remove)(struct share_context *, const char *);
+};
+
+struct loadparm_context;
+
+#include "param/share_proto.h"
+
+/* list of shares options */
+
+#define SHARE_NAME "name"
+#define SHARE_PATH "path"
+#define SHARE_COMMENT "comment"
+#define SHARE_PASSWORD "password"
+#define SHARE_HOSTS_ALLOW "hosts-allow"
+#define SHARE_HOSTS_DENY "hosts-deny"
+#define SHARE_NTVFS_HANDLER "ntvfs-handler"
+#define SHARE_TYPE "type"
+#define SHARE_VOLUME "volume"
+#define SHARE_CSC_POLICY "csc-policy"
+#define SHARE_AVAILABLE "available"
+#define SHARE_BROWSEABLE "browseable"
+#define SHARE_MAX_CONNECTIONS "max-connections"
+
+/* I'd like to see the following options go away
+ * and always use EAs and SECDESCs */
+#define SHARE_READONLY "readonly"
+#define SHARE_MAP_SYSTEM "map-system"
+#define SHARE_MAP_HIDDEN "map-hidden"
+#define SHARE_MAP_ARCHIVE "map-archive"
+
+#define SHARE_STRICT_LOCKING "strict-locking"
+#define SHARE_OPLOCKS "oplocks"
+#define SHARE_STRICT_SYNC "strict-sync"
+#define SHARE_MSDFS_ROOT "msdfs-root"
+#define SHARE_CI_FILESYSTEM "ci-filesystem"
+
+#define SHARE_DIR_MASK "directory mask"
+#define SHARE_CREATE_MASK "create mask"
+#define SHARE_FORCE_CREATE_MODE "force create mode"
+#define SHARE_FORCE_DIR_MODE "force directory mode"
+
+/* defaults */
+
+#define SHARE_HOST_ALLOW_DEFAULT NULL
+#define SHARE_HOST_DENY_DEFAULT NULL
+#define SHARE_VOLUME_DEFAULT NULL
+#define SHARE_TYPE_DEFAULT "DISK"
+#define SHARE_CSC_POLICY_DEFAULT 0
+#define SHARE_AVAILABLE_DEFAULT true
+#define SHARE_BROWSEABLE_DEFAULT true
+#define SHARE_MAX_CONNECTIONS_DEFAULT 0
+
+#define SHARE_DIR_MASK_DEFAULT 0755
+#define SHARE_CREATE_MASK_DEFAULT 0744
+#define SHARE_FORCE_CREATE_MODE_DEFAULT 0000
+#define SHARE_FORCE_DIR_MODE_DEFAULT 0000
+
+
+
+/* I'd like to see the following options go away
+ * and always use EAs and SECDESCs */
+#define SHARE_READONLY_DEFAULT true
+#define SHARE_MAP_SYSTEM_DEFAULT false
+#define SHARE_MAP_HIDDEN_DEFAULT false
+#define SHARE_MAP_ARCHIVE_DEFAULT true
+
+#define SHARE_STRICT_LOCKING_DEFAULT true
+#define SHARE_OPLOCKS_DEFAULT true
+#define SHARE_STRICT_SYNC_DEFAULT false
+#define SHARE_MSDFS_ROOT_DEFAULT false
+#define SHARE_CI_FILESYSTEM_DEFAULT false
+
+#endif /* _SHARE_H */
diff --git a/source4/param/share_classic.c b/source4/param/share_classic.c
new file mode 100644
index 0000000000..d732372f45
--- /dev/null
+++ b/source4/param/share_classic.c
@@ -0,0 +1,362 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Classic file based shares configuration
+
+ Copyright (C) Simo Sorce 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "param/share.h"
+#include "param/param.h"
+
+static NTSTATUS sclassic_init(TALLOC_CTX *mem_ctx,
+ const struct share_ops *ops,
+ struct tevent_context *event_ctx,
+ struct loadparm_context *lp_ctx,
+ struct share_context **ctx)
+{
+ *ctx = talloc(mem_ctx, struct share_context);
+ if (!*ctx) {
+ DEBUG(0, ("ERROR: Out of memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*ctx)->ops = ops;
+ (*ctx)->priv_data = lp_ctx;
+
+ return NT_STATUS_OK;
+}
+
+static const char *sclassic_string_option(struct share_config *scfg,
+ const char *opt_name,
+ const char *defval)
+{
+ struct loadparm_service *s = talloc_get_type(scfg->opaque,
+ struct loadparm_service);
+ struct loadparm_context *lp_ctx = talloc_get_type(scfg->ctx->priv_data,
+ struct loadparm_context);
+ char *parm, *val;
+ const char *ret;
+
+ if (strchr(opt_name, ':')) {
+ parm = talloc_strdup(scfg, opt_name);
+ if (!parm) {
+ return NULL;
+ }
+ val = strchr(parm, ':');
+ *val = '\0';
+ val++;
+
+ ret = lp_parm_string(lp_ctx, s, parm, val);
+ if (!ret) {
+ ret = defval;
+ }
+ talloc_free(parm);
+ return ret;
+ }
+
+ if (strcmp(opt_name, SHARE_NAME) == 0) {
+ return scfg->name;
+ }
+
+ if (strcmp(opt_name, SHARE_PATH) == 0) {
+ return lp_pathname(s, lp_default_service(lp_ctx));
+ }
+
+ if (strcmp(opt_name, SHARE_COMMENT) == 0) {
+ return lp_comment(s, lp_default_service(lp_ctx));
+ }
+
+ if (strcmp(opt_name, SHARE_VOLUME) == 0) {
+ return volume_label(s, lp_default_service(lp_ctx));
+ }
+
+ if (strcmp(opt_name, SHARE_TYPE) == 0) {
+ if (lp_print_ok(s, lp_default_service(lp_ctx))) {
+ return "PRINTER";
+ }
+ if (strcmp("NTFS", lp_fstype(s, lp_default_service(lp_ctx))) == 0) {
+ return "DISK";
+ }
+ return lp_fstype(s, lp_default_service(lp_ctx));
+ }
+
+ if (strcmp(opt_name, SHARE_PASSWORD) == 0) {
+ return defval;
+ }
+
+ DEBUG(0,("request for unknown share string option '%s'\n",
+ opt_name));
+
+ return defval;
+}
+
+static int sclassic_int_option(struct share_config *scfg, const char *opt_name, int defval)
+{
+ struct loadparm_service *s = talloc_get_type(scfg->opaque,
+ struct loadparm_service);
+ struct loadparm_context *lp_ctx = talloc_get_type(scfg->ctx->priv_data,
+ struct loadparm_context);
+ char *parm, *val;
+ int ret;
+
+ if (strchr(opt_name, ':')) {
+ parm = talloc_strdup(scfg, opt_name);
+ if (!parm) {
+ return -1;
+ }
+ val = strchr(parm, ':');
+ *val = '\0';
+ val++;
+
+ ret = lp_parm_int(lp_ctx, s, parm, val, defval);
+ if (!ret) {
+ ret = defval;
+ }
+ talloc_free(parm);
+ return ret;
+ }
+
+ if (strcmp(opt_name, SHARE_CSC_POLICY) == 0) {
+ return lp_csc_policy(s, lp_default_service(lp_ctx));
+ }
+
+ if (strcmp(opt_name, SHARE_MAX_CONNECTIONS) == 0) {
+ return lp_max_connections(s, lp_default_service(lp_ctx));
+ }
+
+ if (strcmp(opt_name, SHARE_CREATE_MASK) == 0) {
+ return lp_create_mask(s, lp_default_service(lp_ctx));
+ }
+
+ if (strcmp(opt_name, SHARE_DIR_MASK) == 0) {
+ return lp_dir_mask(s, lp_default_service(lp_ctx));
+ }
+
+ if (strcmp(opt_name, SHARE_FORCE_DIR_MODE) == 0) {
+ return lp_force_dir_mode(s, lp_default_service(lp_ctx));
+ }
+
+ if (strcmp(opt_name, SHARE_FORCE_CREATE_MODE) == 0) {
+ return lp_force_create_mode(s, lp_default_service(lp_ctx));
+ }
+
+
+ DEBUG(0,("request for unknown share int option '%s'\n",
+ opt_name));
+
+ return defval;
+}
+
+static bool sclassic_bool_option(struct share_config *scfg, const char *opt_name,
+ bool defval)
+{
+ struct loadparm_service *s = talloc_get_type(scfg->opaque,
+ struct loadparm_service);
+ struct loadparm_context *lp_ctx = talloc_get_type(scfg->ctx->priv_data,
+ struct loadparm_context);
+ char *parm, *val;
+ bool ret;
+
+ if (strchr(opt_name, ':')) {
+ parm = talloc_strdup(scfg, opt_name);
+ if(!parm) {
+ return false;
+ }
+ val = strchr(parm, ':');
+ *val = '\0';
+ val++;
+
+ ret = lp_parm_bool(lp_ctx, s, parm, val, defval);
+ talloc_free(parm);
+ return ret;
+ }
+
+ if (strcmp(opt_name, SHARE_AVAILABLE) == 0) {
+ return s != NULL;
+ }
+
+ if (strcmp(opt_name, SHARE_BROWSEABLE) == 0) {
+ return lp_browseable(s, lp_default_service(lp_ctx));
+ }
+
+ if (strcmp(opt_name, SHARE_READONLY) == 0) {
+ return lp_readonly(s, lp_default_service(lp_ctx));
+ }
+
+ if (strcmp(opt_name, SHARE_MAP_SYSTEM) == 0) {
+ return lp_map_system(s, lp_default_service(lp_ctx));
+ }
+
+ if (strcmp(opt_name, SHARE_MAP_HIDDEN) == 0) {
+ return lp_map_hidden(s, lp_default_service(lp_ctx));
+ }
+
+ if (strcmp(opt_name, SHARE_MAP_ARCHIVE) == 0) {
+ return lp_map_archive(s, lp_default_service(lp_ctx));
+ }
+
+ if (strcmp(opt_name, SHARE_STRICT_LOCKING) == 0) {
+ return lp_strict_locking(s, lp_default_service(lp_ctx));
+ }
+
+ if (strcmp(opt_name, SHARE_OPLOCKS) == 0) {
+ return lp_oplocks(s, lp_default_service(lp_ctx));
+ }
+
+ if (strcmp(opt_name, SHARE_STRICT_SYNC) == 0) {
+ return lp_strict_sync(s, lp_default_service(lp_ctx));
+ }
+
+ if (strcmp(opt_name, SHARE_MSDFS_ROOT) == 0) {
+ return lp_msdfs_root(s, lp_default_service(lp_ctx));
+ }
+
+ if (strcmp(opt_name, SHARE_CI_FILESYSTEM) == 0) {
+ return lp_ci_filesystem(s, lp_default_service(lp_ctx));
+ }
+
+ DEBUG(0,("request for unknown share bool option '%s'\n",
+ opt_name));
+
+ return defval;
+}
+
+static const char **sclassic_string_list_option(TALLOC_CTX *mem_ctx, struct share_config *scfg, const char *opt_name)
+{
+ struct loadparm_service *s = talloc_get_type(scfg->opaque,
+ struct loadparm_service);
+ struct loadparm_context *lp_ctx = talloc_get_type(scfg->ctx->priv_data,
+ struct loadparm_context);
+ char *parm, *val;
+ const char **ret;
+
+ if (strchr(opt_name, ':')) {
+ parm = talloc_strdup(scfg, opt_name);
+ if (!parm) {
+ return NULL;
+ }
+ val = strchr(parm, ':');
+ *val = '\0';
+ val++;
+
+ ret = lp_parm_string_list(mem_ctx, lp_ctx, s, parm, val, ",;");
+ talloc_free(parm);
+ return ret;
+ }
+
+ if (strcmp(opt_name, SHARE_HOSTS_ALLOW) == 0) {
+ return lp_hostsallow(s, lp_default_service(lp_ctx));
+ }
+
+ if (strcmp(opt_name, SHARE_HOSTS_DENY) == 0) {
+ return lp_hostsdeny(s, lp_default_service(lp_ctx));
+ }
+
+ if (strcmp(opt_name, SHARE_NTVFS_HANDLER) == 0) {
+ return lp_ntvfs_handler(s, lp_default_service(lp_ctx));
+ }
+
+ DEBUG(0,("request for unknown share list option '%s'\n",
+ opt_name));
+
+ return NULL;
+}
+
+static NTSTATUS sclassic_list_all(TALLOC_CTX *mem_ctx,
+ struct share_context *ctx,
+ int *count,
+ const char ***names)
+{
+ int i;
+ int num_services;
+ const char **n;
+
+ num_services = lp_numservices((struct loadparm_context *)ctx->priv_data);
+
+ n = talloc_array(mem_ctx, const char *, num_services);
+ if (!n) {
+ DEBUG(0,("ERROR: Out of memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i = 0; i < num_services; i++) {
+ n[i] = talloc_strdup(n, lp_servicename(lp_servicebynum((struct loadparm_context *)ctx->priv_data, i)));
+ if (!n[i]) {
+ DEBUG(0,("ERROR: Out of memory!\n"));
+ talloc_free(n);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ *names = n;
+ *count = num_services;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS sclassic_get_config(TALLOC_CTX *mem_ctx,
+ struct share_context *ctx,
+ const char *name,
+ struct share_config **scfg)
+{
+ struct share_config *s;
+ struct loadparm_service *service;
+
+ service = lp_service((struct loadparm_context *)ctx->priv_data, name);
+
+ if (service == NULL) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ s = talloc(mem_ctx, struct share_config);
+ if (!s) {
+ DEBUG(0,("ERROR: Out of memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ s->name = talloc_strdup(s, lp_servicename(service));
+ if (!s->name) {
+ DEBUG(0,("ERROR: Out of memory!\n"));
+ talloc_free(s);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ s->opaque = (void *)service;
+ s->ctx = ctx;
+
+ *scfg = s;
+
+ return NT_STATUS_OK;
+}
+
+static const struct share_ops ops = {
+ .name = "classic",
+ .init = sclassic_init,
+ .string_option = sclassic_string_option,
+ .int_option = sclassic_int_option,
+ .bool_option = sclassic_bool_option,
+ .string_list_option = sclassic_string_list_option,
+ .list_all = sclassic_list_all,
+ .get_config = sclassic_get_config
+};
+
+NTSTATUS share_classic_init(void)
+{
+ return share_register(&ops);
+}
+
diff --git a/source4/param/share_ldb.c b/source4/param/share_ldb.c
new file mode 100644
index 0000000000..0944ba30fc
--- /dev/null
+++ b/source4/param/share_ldb.c
@@ -0,0 +1,592 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ LDB based shares configuration
+
+ Copyright (C) Simo Sorce 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "ldb/include/ldb.h"
+#include "ldb/include/ldb_errors.h"
+#include "auth/auth.h"
+#include "ldb_wrap.h"
+#include "param/share.h"
+#include "param/param.h"
+
+static NTSTATUS sldb_init(TALLOC_CTX *mem_ctx, const struct share_ops *ops,
+ struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx,
+ struct share_context **ctx)
+{
+ struct ldb_context *sdb;
+
+ *ctx = talloc(mem_ctx, struct share_context);
+ if (!*ctx) {
+ DEBUG(0, ("ERROR: Out of memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ sdb = ldb_wrap_connect(*ctx, ev_ctx, lp_ctx,
+ private_path(*ctx, lp_ctx, "share.ldb"),
+ system_session(*ctx, lp_ctx),
+ NULL, 0, NULL);
+
+ if (!sdb) {
+ talloc_free(*ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ (*ctx)->ops = ops;
+ (*ctx)->priv_data = (void *)sdb;
+
+ return NT_STATUS_OK;
+}
+
+static const char *sldb_string_option(struct share_config *scfg, const char *opt_name, const char *defval)
+{
+ struct ldb_message *msg;
+ struct ldb_message_element *el;
+
+ if (scfg == NULL) return defval;
+
+ msg = talloc_get_type(scfg->opaque, struct ldb_message);
+
+ if (strchr(opt_name, ':')) {
+ char *name, *p;
+
+ name = talloc_strdup(scfg, opt_name);
+ if (!name) {
+ return NULL;
+ }
+ p = strchr(name, ':');
+ *p = '-';
+
+ el = ldb_msg_find_element(msg, name);
+ } else {
+ el = ldb_msg_find_element(msg, opt_name);
+ }
+
+ if (el == NULL) {
+ return defval;
+ }
+
+ return (const char *)(el->values[0].data);
+}
+
+static int sldb_int_option(struct share_config *scfg, const char *opt_name, int defval)
+{
+ const char *val;
+ int ret;
+
+ val = sldb_string_option(scfg, opt_name, NULL);
+ if (val == NULL) return defval;
+
+ errno = 0;
+ ret = (int)strtol(val, NULL, 10);
+ if (errno) return -1;
+
+ return ret;
+}
+
+static bool sldb_bool_option(struct share_config *scfg, const char *opt_name, bool defval)
+{
+ const char *val;
+
+ val = sldb_string_option(scfg, opt_name, NULL);
+ if (val == NULL) return defval;
+
+ if (strcasecmp(val, "true") == 0) return true;
+
+ return false;
+}
+
+static const char **sldb_string_list_option(TALLOC_CTX *mem_ctx, struct share_config *scfg, const char *opt_name)
+{
+ struct ldb_message *msg;
+ struct ldb_message_element *el;
+ const char **list;
+ int i;
+
+ if (scfg == NULL) return NULL;
+
+ msg = talloc_get_type(scfg->opaque, struct ldb_message);
+
+ if (strchr(opt_name, ':')) {
+ char *name, *p;
+
+ name = talloc_strdup(scfg, opt_name);
+ if (!name) {
+ return NULL;
+ }
+ p = strchr(name, ':');
+ *p = '-';
+
+ el = ldb_msg_find_element(msg, name);
+ } else {
+ el = ldb_msg_find_element(msg, opt_name);
+ }
+
+ if (el == NULL) {
+ return NULL;
+ }
+
+ list = talloc_array(mem_ctx, const char *, el->num_values + 1);
+ if (!list) return NULL;
+
+ for (i = 0; i < el->num_values; i++) {
+ list[i] = (const char *)(el->values[i].data);
+ }
+ list[i] = NULL;
+
+ return list;
+}
+
+static NTSTATUS sldb_list_all(TALLOC_CTX *mem_ctx,
+ struct share_context *ctx,
+ int *count,
+ const char ***names)
+{
+ int ret, i, j;
+ const char **n;
+ struct ldb_context *ldb;
+ struct ldb_result *res;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (!tmp_ctx) {
+ DEBUG(0,("ERROR: Out of memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ldb = talloc_get_type(ctx->priv_data, struct ldb_context);
+
+ ret = ldb_search(ldb, tmp_ctx, &res, ldb_dn_new(tmp_ctx, ldb, "CN=SHARES"),
+ LDB_SCOPE_SUBTREE, NULL, "(name=*)");
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ n = talloc_array(mem_ctx, const char *, res->count);
+ if (!n) {
+ DEBUG(0,("ERROR: Out of memory!\n"));
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i = 0, j = 0; i < res->count; i++) {
+ n[j] = talloc_strdup(n, ldb_msg_find_attr_as_string(res->msgs[i], "name", NULL));
+ if (!n[j]) {
+ DEBUG(0,("WARNING: Malformed share object in share database\n!"));
+ continue;
+ }
+ j++;
+ }
+
+ *names = n;
+ *count = j;
+ talloc_free(tmp_ctx);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS sldb_get_config(TALLOC_CTX *mem_ctx,
+ struct share_context *ctx,
+ const char *name,
+ struct share_config **scfg)
+{
+ int ret;
+ struct share_config *s;
+ struct ldb_context *ldb;
+ struct ldb_result *res;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (!tmp_ctx) {
+ DEBUG(0,("ERROR: Out of memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ldb = talloc_get_type(ctx->priv_data, struct ldb_context);
+
+ ret = ldb_search(ldb, tmp_ctx, &res,
+ ldb_dn_new(tmp_ctx, ldb, "CN=SHARES"), LDB_SCOPE_SUBTREE, NULL,
+ "(name=%s)", name);
+ if (ret != LDB_SUCCESS || res->count > 1) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ } else if (res->count != 1) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ s = talloc(tmp_ctx, struct share_config);
+ if (!s) {
+ DEBUG(0,("ERROR: Out of memory!\n"));
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ s->name = talloc_strdup(s, ldb_msg_find_attr_as_string(res->msgs[0], "name", NULL));
+ if (!s->name) {
+ DEBUG(0,("ERROR: Invalid share object!\n"));
+ talloc_free(tmp_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ s->opaque = talloc_steal(s, res->msgs[0]);
+ if (!s->opaque) {
+ DEBUG(0,("ERROR: Invalid share object!\n"));
+ talloc_free(tmp_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ s->ctx = ctx;
+
+ *scfg = talloc_steal(mem_ctx, s);
+
+ talloc_free(tmp_ctx);
+ return NT_STATUS_OK;
+}
+
+#define SHARE_ADD_STRING(name, value) do { \
+ err = ldb_msg_add_string(msg, name, value); \
+ if (err != LDB_SUCCESS) { \
+ DEBUG(2,("ERROR: unable to add string share option %s to ldb msg\n", name)); \
+ ret = NT_STATUS_UNSUCCESSFUL; \
+ goto done; \
+ } } while(0)
+
+#define SHARE_ADD_INT(name, value) do { \
+ err = ldb_msg_add_fmt(msg, name, "%d", value); \
+ if (err != LDB_SUCCESS) { \
+ DEBUG(2,("ERROR: unable to add integer share option %s to ldb msg\n", name)); \
+ ret = NT_STATUS_UNSUCCESSFUL; \
+ goto done; \
+ } } while(0)
+
+#define SHARE_ADD_BLOB(name, value) do { \
+ err = ldb_msg_add_value(msg, name, value, NULL); \
+ if (err != LDB_SUCCESS) { \
+ DEBUG(2,("ERROR: unable to add blob share option %s to ldb msg\n", name)); \
+ ret = NT_STATUS_UNSUCCESSFUL; \
+ goto done; \
+ } } while(0)
+
+static NTSTATUS sldb_create(struct share_context *ctx, const char *name, struct share_info *info, int count)
+{
+ struct ldb_context *ldb;
+ struct ldb_message *msg;
+ TALLOC_CTX *tmp_ctx;
+ NTSTATUS ret;
+ int err, i, j;
+
+ for (i = 0, j = 0; i < count && j != 0x03; i++) {
+ if (strcasecmp(info[i].name, SHARE_TYPE) == 0) j |= 0x02;
+ if (strcasecmp(info[i].name, SHARE_PATH) == 0) j |= 0x01;
+ if (strcasecmp(info[i].name, SHARE_NAME) == 0) {
+ if (strcasecmp(name, (char *)info[i].value) != 0) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ }
+ }
+ if (!name || j != 0x03) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ DEBUG(0,("ERROR: Out of memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ldb = talloc_get_type(ctx->priv_data, struct ldb_context);
+
+ msg = ldb_msg_new(tmp_ctx);
+ if (!msg) {
+ DEBUG(0,("ERROR: Out of memory!\n"));
+ ret = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ /* TODO: escape info->name */
+ msg->dn = ldb_dn_new_fmt(tmp_ctx, ldb, "CN=%s,CN=SHARES", name);
+ if (!msg->dn) {
+ DEBUG(0,("ERROR: Out of memory!\n"));
+ ret = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ SHARE_ADD_STRING("objectClass", "top");
+ SHARE_ADD_STRING("objectClass", "share");
+ SHARE_ADD_STRING("cn", name);
+ SHARE_ADD_STRING(SHARE_NAME, name);
+
+ for (i = 0; i < count; i++) {
+ if (strcasecmp(info[i].name, SHARE_NAME) == 0) continue;
+
+ switch (info[i].type) {
+ case SHARE_INFO_STRING:
+ SHARE_ADD_STRING(info[i].name, (char *)info[i].value);
+ break;
+ case SHARE_INFO_INT:
+ SHARE_ADD_INT(info[i].name, *((int *)info[i].value));
+ break;
+ case SHARE_INFO_BLOB:
+ SHARE_ADD_BLOB(info[i].name, (DATA_BLOB *)info[i].value);
+ break;
+ default:
+ DEBUG(2,("ERROR: Invalid share info type for %s\n", info[i].name));
+ ret = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+ }
+
+ /* TODO: Security Descriptor */
+
+ SHARE_ADD_STRING(SHARE_AVAILABLE, "true");
+ SHARE_ADD_STRING(SHARE_BROWSEABLE, "true");
+ SHARE_ADD_STRING(SHARE_READONLY, "false");
+ SHARE_ADD_STRING(SHARE_NTVFS_HANDLER, "unixuid");
+ SHARE_ADD_STRING(SHARE_NTVFS_HANDLER, "posix");
+
+ err = ldb_add(ldb, msg);
+ if (err != LDB_SUCCESS) {
+ DEBUG(2,("ERROR: unable to add share %s to share.ldb\n"
+ " err=%d [%s]\n", name, err, ldb_errstring(ldb)));
+ if (err == LDB_ERR_NO_SUCH_OBJECT) {
+ ret = NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ } else if (err == LDB_ERR_ENTRY_ALREADY_EXISTS) {
+ ret = NT_STATUS_OBJECT_NAME_COLLISION;
+ } else {
+ ret = NT_STATUS_UNSUCCESSFUL;
+ }
+ goto done;
+ }
+
+ ret = NT_STATUS_OK;
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+#define SHARE_MOD_STRING(name, value) do { \
+ err = ldb_msg_add_empty(msg, name, LDB_FLAG_MOD_REPLACE, NULL); \
+ if (err != LDB_SUCCESS) { \
+ DEBUG(2,("ERROR: unable to add string share option %s to ldb msg\n", name)); \
+ ret = NT_STATUS_UNSUCCESSFUL; \
+ goto done; \
+ } \
+ err = ldb_msg_add_string(msg, name, value); \
+ if (err != LDB_SUCCESS) { \
+ DEBUG(2,("ERROR: unable to add string share option %s to ldb msg\n", name)); \
+ ret = NT_STATUS_UNSUCCESSFUL; \
+ goto done; \
+ } } while(0)
+
+#define SHARE_MOD_INT(name, value) do { \
+ err = ldb_msg_add_empty(msg, name, LDB_FLAG_MOD_REPLACE, NULL); \
+ if (err != LDB_SUCCESS) { \
+ DEBUG(2,("ERROR: unable to add string share option %s to ldb msg\n", name)); \
+ ret = NT_STATUS_UNSUCCESSFUL; \
+ goto done; \
+ } \
+ err = ldb_msg_add_fmt(msg, name, "%d", value); \
+ if (err != LDB_SUCCESS) { \
+ DEBUG(2,("ERROR: unable to add integer share option %s to ldb msg\n", name)); \
+ ret = NT_STATUS_UNSUCCESSFUL; \
+ goto done; \
+ } } while(0)
+
+#define SHARE_MOD_BLOB(name, value) do { \
+ err = ldb_msg_add_empty(msg, name, LDB_FLAG_MOD_REPLACE, NULL); \
+ if (err != LDB_SUCCESS) { \
+ DEBUG(2,("ERROR: unable to add string share option %s to ldb msg\n", name)); \
+ ret = NT_STATUS_UNSUCCESSFUL; \
+ goto done; \
+ } \
+ err = ldb_msg_add_value(msg, name, value, NULL); \
+ if (err != LDB_SUCCESS) { \
+ DEBUG(2,("ERROR: unable to add blob share option %s to ldb msg\n", name)); \
+ ret = NT_STATUS_UNSUCCESSFUL; \
+ goto done; \
+ } } while(0)
+
+static NTSTATUS sldb_set(struct share_context *ctx, const char *name, struct share_info *info, int count)
+{
+ struct ldb_context *ldb;
+ struct ldb_message *msg;
+ TALLOC_CTX *tmp_ctx;
+ NTSTATUS ret;
+ bool do_rename = false;
+ char *newname;
+ int err, i;
+
+ if (!name) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ DEBUG(0,("ERROR: Out of memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ldb = talloc_get_type(ctx->priv_data, struct ldb_context);
+
+ msg = ldb_msg_new(tmp_ctx);
+ if (!msg) {
+ DEBUG(0,("ERROR: Out of memory!\n"));
+ ret = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ /* TODO: escape name */
+ msg->dn = ldb_dn_new_fmt(tmp_ctx, ldb, "CN=%s,CN=SHARES", name);
+ if (!msg->dn) {
+ DEBUG(0,("ERROR: Out of memory!\n"));
+ ret = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ for (i = 0; i < count; i++) {
+ if (strcasecmp(info[i].name, SHARE_NAME) == 0) {
+ if (strcasecmp(name, (char *)info[i].value) != 0) {
+ do_rename = true;
+ newname = (char *)info[i].value;
+ SHARE_MOD_STRING("cn", (char *)info[i].value);
+ }
+ }
+
+ switch (info[i].type) {
+ case SHARE_INFO_STRING:
+ SHARE_MOD_STRING(info[i].name, (char *)info[i].value);
+ break;
+ case SHARE_INFO_INT:
+ SHARE_MOD_INT(info[i].name, *((int *)info[i].value));
+ break;
+ case SHARE_INFO_BLOB:
+ SHARE_MOD_BLOB(info[i].name, (DATA_BLOB *)info[i].value);
+ break;
+ default:
+ DEBUG(2,("ERROR: Invalid share info type for %s\n", info[i].name));
+ ret = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+ }
+
+ if (do_rename) {
+ struct ldb_dn *olddn, *newdn;
+
+ olddn = msg->dn;
+
+ /* TODO: escape newname */
+ newdn = ldb_dn_new_fmt(tmp_ctx, ldb, "CN=%s,CN=SHARES", newname);
+ if (!newdn) {
+ DEBUG(0,("ERROR: Out of memory!\n"));
+ ret = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ err = ldb_rename(ldb, olddn, newdn);
+ if (err != LDB_SUCCESS) {
+ DEBUG(2,("ERROR: unable to rename share %s (to %s)\n"
+ " err=%d [%s]\n", name, newname, err, ldb_errstring(ldb)));
+ if (err == LDB_ERR_NO_SUCH_OBJECT) {
+ ret = NT_STATUS_OBJECT_NAME_COLLISION;
+ } else {
+ ret = NT_STATUS_UNSUCCESSFUL;
+ }
+ goto done;
+ }
+
+ msg->dn = newdn;
+ }
+
+ err = ldb_modify(ldb, msg);
+ if (err != LDB_SUCCESS) {
+ DEBUG(2,("ERROR: unable to add share %s to share.ldb\n"
+ " err=%d [%s]\n", name, err, ldb_errstring(ldb)));
+ if (err == LDB_ERR_NO_SUCH_OBJECT) {
+ ret = NT_STATUS_OBJECT_NAME_COLLISION;
+ } else {
+ ret = NT_STATUS_UNSUCCESSFUL;
+ }
+ goto done;
+ }
+
+ ret = NT_STATUS_OK;
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static NTSTATUS sldb_remove(struct share_context *ctx, const char *name)
+{
+ struct ldb_context *ldb;
+ struct ldb_dn *dn;
+ TALLOC_CTX *tmp_ctx;
+ NTSTATUS ret;
+ int err;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ DEBUG(0,("ERROR: Out of memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ldb = talloc_get_type(ctx->priv_data, struct ldb_context);
+
+ dn = ldb_dn_new_fmt(tmp_ctx, ldb, "CN=%s,CN=SHARES", name);
+ if (!dn) {
+ DEBUG(0,("ERROR: Out of memory!\n"));
+ ret = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ err = ldb_delete(ldb, dn);
+ if (err != LDB_SUCCESS) {
+ DEBUG(2,("ERROR: unable to remove share %s from share.ldb\n"
+ " err=%d [%s]\n", name, err, ldb_errstring(ldb)));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ ret = NT_STATUS_OK;
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static const struct share_ops ops = {
+ .name = "ldb",
+ .init = sldb_init,
+ .string_option = sldb_string_option,
+ .int_option = sldb_int_option,
+ .bool_option = sldb_bool_option,
+ .string_list_option = sldb_string_list_option,
+ .list_all = sldb_list_all,
+ .get_config = sldb_get_config,
+ .create = sldb_create,
+ .set = sldb_set,
+ .remove = sldb_remove
+};
+
+NTSTATUS share_ldb_init(void)
+{
+ return share_register(&ops);
+}
diff --git a/source4/param/tests/bindings.py b/source4/param/tests/bindings.py
new file mode 100644
index 0000000000..41a67f93fc
--- /dev/null
+++ b/source4/param/tests/bindings.py
@@ -0,0 +1,52 @@
+#!/usr/bin/python
+
+# Unix SMB/CIFS implementation.
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 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 <http://www.gnu.org/licenses/>.
+#
+
+from samba import param
+import unittest
+
+class LoadParmTestCase(unittest.TestCase):
+ def test_init(self):
+ file = param.LoadParm()
+ self.assertTrue(file is not None)
+
+ def test_length(self):
+ file = param.LoadParm()
+ self.assertEquals(0, len(file))
+
+ def test_set_workgroup(self):
+ file = param.LoadParm()
+ file.set("workgroup", "bla")
+ self.assertEquals("BLA", file.get("workgroup"))
+
+ def test_is_mydomain(self):
+ file = param.LoadParm()
+ file.set("workgroup", "bla")
+ self.assertTrue(file.is_mydomain("BLA"))
+ self.assertFalse(file.is_mydomain("FOOBAR"))
+
+ def test_is_myname(self):
+ file = param.LoadParm()
+ file.set("netbios name", "bla")
+ self.assertTrue(file.is_myname("BLA"))
+ self.assertFalse(file.is_myname("FOOBAR"))
+
+ def test_load_default(self):
+ file = param.LoadParm()
+ file.load_default()
+
diff --git a/source4/param/tests/loadparm.c b/source4/param/tests/loadparm.c
new file mode 100644
index 0000000000..49fcdf7249
--- /dev/null
+++ b/source4/param/tests/loadparm.c
@@ -0,0 +1,167 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Jelmer Vernooij <jelmer@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "param/share.h"
+#include "param/param.h"
+#include "torture/torture.h"
+
+static bool test_create(struct torture_context *tctx)
+{
+ struct loadparm_context *lp_ctx = loadparm_init(tctx);
+ torture_assert(tctx, lp_ctx != NULL, "lp_ctx");
+ return true;
+}
+
+static bool test_set_option(struct torture_context *tctx)
+{
+ struct loadparm_context *lp_ctx = loadparm_init(tctx);
+ torture_assert(tctx, lp_set_option(lp_ctx, "workgroup=werkgroep"), "lp_set_option failed");
+ torture_assert_str_equal(tctx, "WERKGROEP", lp_workgroup(lp_ctx), "workgroup");
+ return true;
+}
+
+static bool test_set_cmdline(struct torture_context *tctx)
+{
+ struct loadparm_context *lp_ctx = loadparm_init(tctx);
+ torture_assert(tctx, lp_set_cmdline(lp_ctx, "workgroup", "werkgroep"), "lp_set_cmdline failed");
+ torture_assert(tctx, lp_do_global_parameter(lp_ctx, "workgroup", "barbla"), "lp_set_option failed");
+ torture_assert_str_equal(tctx, "WERKGROEP", lp_workgroup(lp_ctx), "workgroup");
+ return true;
+}
+
+static bool test_do_global_parameter(struct torture_context *tctx)
+{
+ struct loadparm_context *lp_ctx = loadparm_init(tctx);
+ torture_assert(tctx, lp_do_global_parameter(lp_ctx, "workgroup", "werkgroep42"),
+ "lp_set_cmdline failed");
+ torture_assert_str_equal(tctx, lp_workgroup(lp_ctx), "WERKGROEP42", "workgroup");
+ return true;
+}
+
+
+static bool test_do_global_parameter_var(struct torture_context *tctx)
+{
+ struct loadparm_context *lp_ctx = loadparm_init(tctx);
+ torture_assert(tctx, lp_do_global_parameter_var(lp_ctx, "workgroup", "werk%s%d", "groep", 42),
+ "lp_set_cmdline failed");
+ torture_assert_str_equal(tctx, lp_workgroup(lp_ctx), "WERKGROEP42", "workgroup");
+ return true;
+}
+
+
+static bool test_set_option_invalid(struct torture_context *tctx)
+{
+ struct loadparm_context *lp_ctx = loadparm_init(tctx);
+ torture_assert(tctx, !lp_set_option(lp_ctx, "workgroup"), "lp_set_option succeeded");
+ return true;
+}
+
+static bool test_set_option_parametric(struct torture_context *tctx)
+{
+ struct loadparm_context *lp_ctx = loadparm_init(tctx);
+ torture_assert(tctx, lp_set_option(lp_ctx, "some:thing=blaat"), "lp_set_option failed");
+ torture_assert_str_equal(tctx, lp_parm_string(lp_ctx, NULL, "some", "thing"), "blaat",
+ "invalid parametric option");
+ return true;
+}
+
+static bool test_lp_parm_double(struct torture_context *tctx)
+{
+ struct loadparm_context *lp_ctx = loadparm_init(tctx);
+ torture_assert(tctx, lp_set_option(lp_ctx, "some:thing=3.4"), "lp_set_option failed");
+ torture_assert(tctx, lp_parm_double(lp_ctx, NULL, "some", "thing", 2.0) == 3.4,
+ "invalid parametric option");
+ torture_assert(tctx, lp_parm_double(lp_ctx, NULL, "some", "bla", 2.0) == 2.0,
+ "invalid parametric option");
+ return true;
+}
+
+static bool test_lp_parm_bool(struct torture_context *tctx)
+{
+ struct loadparm_context *lp_ctx = loadparm_init(tctx);
+ torture_assert(tctx, lp_set_option(lp_ctx, "some:thing=true"), "lp_set_option failed");
+ torture_assert(tctx, lp_parm_bool(lp_ctx, NULL, "some", "thing", false) == true,
+ "invalid parametric option");
+ torture_assert(tctx, lp_parm_bool(lp_ctx, NULL, "some", "bla", true) == true,
+ "invalid parametric option");
+ return true;
+}
+
+static bool test_lp_parm_int(struct torture_context *tctx)
+{
+ struct loadparm_context *lp_ctx = loadparm_init(tctx);
+ torture_assert(tctx, lp_set_option(lp_ctx, "some:thing=34"), "lp_set_option failed");
+ torture_assert_int_equal(tctx, lp_parm_int(lp_ctx, NULL, "some", "thing", 20), 34,
+ "invalid parametric option");
+ torture_assert_int_equal(tctx, lp_parm_int(lp_ctx, NULL, "some", "bla", 42), 42,
+ "invalid parametric option");
+ return true;
+}
+
+static bool test_lp_parm_bytes(struct torture_context *tctx)
+{
+ struct loadparm_context *lp_ctx = loadparm_init(tctx);
+ torture_assert(tctx, lp_set_option(lp_ctx, "some:thing=16K"), "lp_set_option failed");
+ torture_assert_int_equal(tctx, lp_parm_bytes(lp_ctx, NULL, "some", "thing", 20), 16 * 1024,
+ "invalid parametric option");
+ torture_assert_int_equal(tctx, lp_parm_bytes(lp_ctx, NULL, "some", "bla", 42), 42,
+ "invalid parametric option");
+ return true;
+}
+
+static bool test_lp_do_service_parameter(struct torture_context *tctx)
+{
+ struct loadparm_context *lp_ctx = loadparm_init(tctx);
+ struct loadparm_service *service = lp_add_service(lp_ctx, lp_default_service(lp_ctx), "foo");
+ torture_assert(tctx, lp_do_service_parameter(lp_ctx, service,
+ "some:thing", "foo"), "lp_set_option failed");
+ torture_assert_str_equal(tctx, lp_parm_string(lp_ctx, service, "some", "thing"), "foo",
+ "invalid parametric option");
+ return true;
+}
+
+static bool test_lp_service(struct torture_context *tctx)
+{
+ struct loadparm_context *lp_ctx = loadparm_init(tctx);
+ struct loadparm_service *service = lp_add_service(lp_ctx, lp_default_service(lp_ctx), "foo");
+ torture_assert(tctx, service == lp_service(lp_ctx, "foo"), "invalid service");
+ return true;
+}
+
+struct torture_suite *torture_local_loadparm(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "LOADPARM");
+
+ torture_suite_add_simple_test(suite, "create", test_create);
+ torture_suite_add_simple_test(suite, "set_option", test_set_option);
+ torture_suite_add_simple_test(suite, "set_cmdline", test_set_cmdline);
+ torture_suite_add_simple_test(suite, "set_option_invalid", test_set_option_invalid);
+ torture_suite_add_simple_test(suite, "set_option_parametric", test_set_option_parametric);
+ torture_suite_add_simple_test(suite, "set_lp_parm_double", test_lp_parm_double);
+ torture_suite_add_simple_test(suite, "set_lp_parm_bool", test_lp_parm_bool);
+ torture_suite_add_simple_test(suite, "set_lp_parm_int", test_lp_parm_int);
+ torture_suite_add_simple_test(suite, "set_lp_parm_bytes", test_lp_parm_bytes);
+ torture_suite_add_simple_test(suite, "service_parameter", test_lp_do_service_parameter);
+ torture_suite_add_simple_test(suite, "lp_service", test_lp_service);
+ torture_suite_add_simple_test(suite, "do_global_parameter_var", test_do_global_parameter_var);
+ torture_suite_add_simple_test(suite, "do_global_parameter", test_do_global_parameter);
+
+ return suite;
+}
diff --git a/source4/param/tests/share.c b/source4/param/tests/share.c
new file mode 100644
index 0000000000..c64b5c607a
--- /dev/null
+++ b/source4/param/tests/share.c
@@ -0,0 +1,215 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ local testing of share code
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "param/share.h"
+#include "param/param.h"
+#include "torture/torture.h"
+
+static bool test_list_empty(struct torture_context *tctx,
+ const void *tcase_data,
+ const void *test_data)
+{
+ struct share_context *ctx = (struct share_context *)discard_const(tcase_data);
+ int count;
+ const char **names;
+
+ torture_assert_ntstatus_ok(tctx, share_list_all(tctx, ctx, &count, &names),
+ "share_list_all failed");
+
+ return true;
+}
+
+static bool test_create(struct torture_context *tctx,
+ const void *tcase_data,
+ const void *test_data)
+{
+ struct share_context *ctx = (struct share_context *)discard_const(tcase_data);
+ int count;
+ const char **names;
+ int i;
+ bool found = false;
+ struct share_info inf[] = {
+ { SHARE_INFO_STRING, SHARE_TYPE, discard_const_p(void *, "IPC$") },
+ { SHARE_INFO_STRING, SHARE_PATH, discard_const_p(void *, "/tmp/bla") }
+ };
+ NTSTATUS status;
+
+ status = share_create(ctx, "bloe", inf, 2);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED))
+ torture_skip(tctx, "Not supported by backend");
+
+ torture_assert_ntstatus_ok(tctx, status, "create_share failed");
+
+ torture_assert_ntstatus_ok(tctx, share_list_all(tctx, ctx, &count, &names),
+ "share_list_all failed");
+
+ torture_assert(tctx, count >= 1, "creating share failed");
+
+
+ for (i = 0; i < count; i++) {
+ found |= strcmp(names[i], "bloe") == 0;
+ }
+
+ torture_assert(tctx, found, "created share found");
+
+ return true;
+}
+
+
+static bool test_create_invalid(struct torture_context *tctx,
+ const void *tcase_data,
+ const void *test_data)
+{
+ struct share_context *ctx = (struct share_context *)discard_const(tcase_data);
+ NTSTATUS status;
+
+ status = share_create(ctx, "bla", NULL, 0);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED))
+ torture_skip(tctx, "Not supported by backend");
+
+ torture_assert_ntstatus_equal(tctx, NT_STATUS_INVALID_PARAMETER,
+ status,
+ "create_share failed");
+
+ torture_assert_ntstatus_equal(tctx, NT_STATUS_INVALID_PARAMETER,
+ share_create(ctx, NULL, NULL, 0),
+ "create_share failed");
+
+ return true;
+}
+
+static bool test_share_remove_invalid(struct torture_context *tctx,
+ const void *tcase_data,
+ const void *test_data)
+{
+ struct share_context *ctx = (struct share_context *)discard_const(tcase_data);
+ NTSTATUS status;
+
+ status = share_remove(ctx, "nonexistant");
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED))
+ torture_skip(tctx, "Not supported by backend");
+
+ torture_assert_ntstatus_equal(tctx, status, NT_STATUS_UNSUCCESSFUL, "remove fails");
+
+ return true;
+}
+
+
+
+static bool test_share_remove(struct torture_context *tctx,
+ const void *tcase_data,
+ const void *test_data)
+{
+ struct share_context *ctx = (struct share_context *)discard_const(tcase_data);
+ struct share_info inf[] = {
+ { SHARE_INFO_STRING, SHARE_TYPE, discard_const_p(void *, "IPC$") },
+ { SHARE_INFO_STRING, SHARE_PATH, discard_const_p(void *, "/tmp/bla") }
+ };
+ NTSTATUS status;
+
+ status = share_create(ctx, "blie", inf, 2);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED))
+ torture_skip(tctx, "Not supported by backend");
+
+ torture_assert_ntstatus_ok(tctx, status, "create_share failed");
+
+ torture_assert_ntstatus_ok(tctx, share_remove(ctx, "blie"), "remove failed");
+
+ return true;
+}
+
+static bool test_double_create(struct torture_context *tctx,
+ const void *tcase_data,
+ const void *test_data)
+{
+ struct share_context *ctx = (struct share_context *)discard_const(tcase_data);
+ struct share_info inf[] = {
+ { SHARE_INFO_STRING, SHARE_TYPE, discard_const_p(void *, "IPC$") },
+ { SHARE_INFO_STRING, SHARE_PATH, discard_const_p(void *, "/tmp/bla") }
+ };
+ NTSTATUS status;
+
+ status = share_create(ctx, "bla", inf, 2);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED))
+ torture_skip(tctx, "Not supported by backend");
+
+ torture_assert_ntstatus_ok(tctx, status, "create_share failed");
+
+ torture_assert_ntstatus_equal(tctx, NT_STATUS_OBJECT_NAME_COLLISION,
+ share_create(ctx, "bla", inf, 2),
+ "create_share failed");
+
+ return true;
+}
+
+static void tcase_add_share_tests(struct torture_tcase *tcase)
+{
+ torture_tcase_add_test_const(tcase, "list_empty", test_list_empty,NULL);
+ torture_tcase_add_test_const(tcase, "share_create", test_create, NULL);
+ torture_tcase_add_test_const(tcase, "share_remove", test_share_remove,
+ NULL);
+ torture_tcase_add_test_const(tcase, "share_remove_invalid",
+ test_share_remove_invalid, NULL);
+ torture_tcase_add_test_const(tcase, "share_create_invalid",
+ test_create_invalid, NULL);
+ torture_tcase_add_test_const(tcase, "share_double_create",
+ test_double_create, NULL);
+}
+
+static bool setup_ldb(struct torture_context *tctx, void **data)
+{
+ return NT_STATUS_IS_OK(share_get_context_by_name(tctx, "ldb", tctx->ev, tctx->lp_ctx, (struct share_context **)data));
+}
+
+static bool setup_classic(struct torture_context *tctx, void **data)
+{
+ return NT_STATUS_IS_OK(share_get_context_by_name(tctx, "classic", tctx->ev, tctx->lp_ctx, (struct share_context **)data));
+}
+
+static bool teardown(struct torture_context *tctx, void *data)
+{
+ talloc_free(data);
+ return true;
+}
+
+struct torture_suite *torture_local_share(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "SHARE");
+ struct torture_tcase *tcase;
+
+ share_init();
+
+ tcase = torture_suite_add_tcase(suite, "ldb");
+ torture_tcase_set_fixture(tcase, setup_ldb, teardown);
+ tcase_add_share_tests(tcase);
+
+ tcase = torture_suite_add_tcase(suite, "classic");
+ torture_tcase_set_fixture(tcase, setup_classic, teardown);
+ tcase_add_share_tests(tcase);
+
+ return suite;
+}
diff --git a/source4/param/util.c b/source4/param/util.c
new file mode 100644
index 0000000000..3881107cbc
--- /dev/null
+++ b/source4/param/util.c
@@ -0,0 +1,298 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Jeremy Allison 2001-2002
+ Copyright (C) Simo Sorce 2001
+ Copyright (C) Jim McDonough (jmcd@us.ibm.com) 2003.
+ Copyright (C) James J Myers 2003
+ Copyright (C) Jelmer Vernooij 2005-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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "dynconfig/dynconfig.h"
+#include "system/network.h"
+#include "system/filesys.h"
+#include "system/dir.h"
+#include "param/param.h"
+
+/**
+ * @file
+ * @brief Misc utility functions
+ */
+
+
+bool lp_is_mydomain(struct loadparm_context *lp_ctx,
+ const char *domain)
+{
+ return strequal(lp_workgroup(lp_ctx), domain);
+}
+
+/**
+ see if a string matches either our primary or one of our secondary
+ netbios aliases. do a case insensitive match
+*/
+bool lp_is_myname(struct loadparm_context *lp_ctx, const char *name)
+{
+ const char **aliases;
+ int i;
+
+ if (strcasecmp(name, lp_netbios_name(lp_ctx)) == 0) {
+ return true;
+ }
+
+ aliases = lp_netbios_aliases(lp_ctx);
+ for (i=0; aliases && aliases[i]; i++) {
+ if (strcasecmp(name, aliases[i]) == 0) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+/**
+ A useful function for returning a path in the Samba lock directory.
+**/
+char *lock_path(TALLOC_CTX* mem_ctx, struct loadparm_context *lp_ctx,
+ const char *name)
+{
+ char *fname, *dname;
+ if (name == NULL) {
+ return NULL;
+ }
+ if (name[0] == 0 || name[0] == '/' || strstr(name, ":/")) {
+ return talloc_strdup(mem_ctx, name);
+ }
+
+ dname = talloc_strdup(mem_ctx, lp_lockdir(lp_ctx));
+ trim_string(dname,"","/");
+
+ if (!directory_exist(dname)) {
+ mkdir(dname,0755);
+ }
+
+ fname = talloc_asprintf(mem_ctx, "%s/%s", dname, name);
+
+ talloc_free(dname);
+
+ return fname;
+}
+
+/**
+ * @brief Returns an absolute path to a file in the directory containing the current config file
+ *
+ * @param name File to find, relative to the config file directory.
+ *
+ * @retval Pointer to a talloc'ed string containing the full path.
+ **/
+
+char *config_path(TALLOC_CTX* mem_ctx, struct loadparm_context *lp_ctx,
+ const char *name)
+{
+ char *fname, *config_dir, *p;
+ config_dir = talloc_strdup(mem_ctx, lp_configfile(lp_ctx));
+ if (config_dir == NULL) {
+ config_dir = talloc_strdup(mem_ctx, lp_default_path());
+ }
+ p = strrchr(config_dir, '/');
+ if (p == NULL) {
+ return NULL;
+ }
+ p[0] = '\0';
+ fname = talloc_asprintf(mem_ctx, "%s/%s", config_dir, name);
+ talloc_free(config_dir);
+ return fname;
+}
+
+/**
+ * @brief Returns an absolute path to a file in the Samba private directory.
+ *
+ * @param name File to find, relative to PRIVATEDIR.
+ * if name is not relative, then use it as-is
+ *
+ * @retval Pointer to a talloc'ed string containing the full path.
+ **/
+char *private_path(TALLOC_CTX* mem_ctx,
+ struct loadparm_context *lp_ctx,
+ const char *name)
+{
+ char *fname;
+ if (name == NULL) {
+ return NULL;
+ }
+ if (name[0] == 0 || name[0] == '/' || strstr(name, ":/")) {
+ return talloc_strdup(mem_ctx, name);
+ }
+ fname = talloc_asprintf(mem_ctx, "%s/%s", lp_private_dir(lp_ctx), name);
+ return fname;
+}
+
+/**
+ return a path in the smbd.tmp directory, where all temporary file
+ for smbd go. If NULL is passed for name then return the directory
+ path itself
+*/
+char *smbd_tmp_path(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ const char *name)
+{
+ char *fname, *dname;
+
+ dname = private_path(mem_ctx, lp_ctx, "smbd.tmp");
+ if (!directory_exist(dname)) {
+ mkdir(dname,0755);
+ }
+
+ if (name == NULL) {
+ return dname;
+ }
+
+ fname = talloc_asprintf(mem_ctx, "%s/%s", dname, name);
+ talloc_free(dname);
+
+ return fname;
+}
+
+/**
+ * Obtain the init function from a shared library file
+ */
+init_module_fn load_module(TALLOC_CTX *mem_ctx, const char *path)
+{
+ void *handle;
+ void *init_fn;
+
+ handle = dlopen(path, RTLD_NOW);
+ if (handle == NULL) {
+ DEBUG(0, ("Unable to open %s: %s\n", path, dlerror()));
+ return NULL;
+ }
+
+ init_fn = dlsym(handle, SAMBA_INIT_MODULE);
+
+ if (init_fn == NULL) {
+ DEBUG(0, ("Unable to find %s() in %s: %s\n",
+ SAMBA_INIT_MODULE, path, dlerror()));
+ DEBUG(1, ("Loading module '%s' failed\n", path));
+ dlclose(handle);
+ return NULL;
+ }
+
+ return (init_module_fn)init_fn;
+}
+
+/**
+ * Obtain list of init functions from the modules in the specified
+ * directory
+ */
+init_module_fn *load_modules(TALLOC_CTX *mem_ctx, const char *path)
+{
+ DIR *dir;
+ struct dirent *entry;
+ char *filename;
+ int success = 0;
+ init_module_fn *ret = talloc_array(mem_ctx, init_module_fn, 2);
+
+ ret[0] = NULL;
+
+ dir = opendir(path);
+ if (dir == NULL) {
+ talloc_free(ret);
+ return NULL;
+ }
+
+ while((entry = readdir(dir))) {
+ if (ISDOT(entry->d_name) || ISDOTDOT(entry->d_name))
+ continue;
+
+ filename = talloc_asprintf(mem_ctx, "%s/%s", path, entry->d_name);
+
+ ret[success] = load_module(mem_ctx, filename);
+ if (ret[success]) {
+ ret = talloc_realloc(mem_ctx, ret, init_module_fn, success+2);
+ success++;
+ ret[success] = NULL;
+ }
+
+ talloc_free(filename);
+ }
+
+ closedir(dir);
+
+ return ret;
+}
+
+/**
+ * Run the specified init functions.
+ *
+ * @return true if all functions ran successfully, false otherwise
+ */
+bool run_init_functions(init_module_fn *fns)
+{
+ int i;
+ bool ret = true;
+
+ if (fns == NULL)
+ return true;
+
+ for (i = 0; fns[i]; i++) { ret &= (bool)NT_STATUS_IS_OK(fns[i]()); }
+
+ return ret;
+}
+
+static char *modules_path(TALLOC_CTX* mem_ctx, struct loadparm_context *lp_ctx,
+ const char *name)
+{
+ const char *env_moduledir = getenv("LD_SAMBA_MODULE_PATH");
+ return talloc_asprintf(mem_ctx, "%s/%s",
+ env_moduledir?env_moduledir:lp_modulesdir(lp_ctx),
+ name);
+}
+
+/**
+ * Load the initialization functions from DSO files for a specific subsystem.
+ *
+ * Will return an array of function pointers to initialization functions
+ */
+
+init_module_fn *load_samba_modules(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx, const char *subsystem)
+{
+ char *path = modules_path(mem_ctx, lp_ctx, subsystem);
+ init_module_fn *ret;
+
+ ret = load_modules(mem_ctx, path);
+
+ talloc_free(path);
+
+ return ret;
+}
+
+const char *lp_messaging_path(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx)
+{
+ return smbd_tmp_path(mem_ctx, lp_ctx, "messaging");
+}
+
+struct smb_iconv_convenience *smb_iconv_convenience_init_lp(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx)
+{
+ return smb_iconv_convenience_init(mem_ctx, lp_dos_charset(lp_ctx),
+ lp_unix_charset(lp_ctx),
+ lp_parm_bool(lp_ctx, NULL, "iconv", "native", true));
+}
+
+
diff --git a/source4/rpc_server/browser/dcesrv_browser.c b/source4/rpc_server/browser/dcesrv_browser.c
new file mode 100644
index 0000000000..33fed50857
--- /dev/null
+++ b/source4/rpc_server/browser/dcesrv_browser.c
@@ -0,0 +1,172 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the browser pipe
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "rpc_server/dcerpc_server.h"
+#include "librpc/gen_ndr/ndr_browser.h"
+#include "rpc_server/common/common.h"
+
+
+/*
+ BrowserrServerEnum
+*/
+static void dcesrv_BrowserrServerEnum(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct BrowserrServerEnum *r)
+{
+ DCESRV_FAULT_VOID(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ BrowserrDebugCall
+*/
+static void dcesrv_BrowserrDebugCall(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct BrowserrDebugCall *r)
+{
+ DCESRV_FAULT_VOID(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ BrowserrQueryOtherDomains
+*/
+static WERROR dcesrv_BrowserrQueryOtherDomains(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct BrowserrQueryOtherDomains *r)
+{
+ struct BrowserrSrvInfo100Ctr *ctr100;
+
+ switch (r->in.info->level) {
+ case 100:
+ if (!r->in.info->info.info100) {
+ return WERR_INVALID_PARAM;
+ }
+
+ ctr100 = talloc(mem_ctx, struct BrowserrSrvInfo100Ctr);
+ W_ERROR_HAVE_NO_MEMORY(ctr100);
+
+ ctr100->entries_read = 0;
+ ctr100->entries = talloc_zero_array(ctr100, struct srvsvc_NetSrvInfo100,
+ ctr100->entries_read);
+ W_ERROR_HAVE_NO_MEMORY(ctr100->entries);
+
+ r->out.info->info.info100 = ctr100;
+ *r->out.total_entries = ctr100->entries_read;
+ return WERR_OK;
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ return WERR_INVALID_PARAM;
+}
+
+
+/*
+ BrowserrResetNetlogonState
+*/
+static void dcesrv_BrowserrResetNetlogonState(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct BrowserrResetNetlogonState *r)
+{
+ DCESRV_FAULT_VOID(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ BrowserrDebugTrace
+*/
+static void dcesrv_BrowserrDebugTrace(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct BrowserrDebugTrace *r)
+{
+ DCESRV_FAULT_VOID(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ BrowserrQueryStatistics
+*/
+static void dcesrv_BrowserrQueryStatistics(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct BrowserrQueryStatistics *r)
+{
+ DCESRV_FAULT_VOID(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ BrowserResetStatistics
+*/
+static void dcesrv_BrowserResetStatistics(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct BrowserResetStatistics *r)
+{
+ DCESRV_FAULT_VOID(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ NetrBrowserStatisticsClear
+*/
+static void dcesrv_NetrBrowserStatisticsClear(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct NetrBrowserStatisticsClear *r)
+{
+ DCESRV_FAULT_VOID(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ NetrBrowserStatisticsGet
+*/
+static void dcesrv_NetrBrowserStatisticsGet(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct NetrBrowserStatisticsGet *r)
+{
+ DCESRV_FAULT_VOID(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ BrowserrSetNetlogonState
+*/
+static void dcesrv_BrowserrSetNetlogonState(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct BrowserrSetNetlogonState *r)
+{
+ DCESRV_FAULT_VOID(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ BrowserrQueryEmulatedDomains
+*/
+static void dcesrv_BrowserrQueryEmulatedDomains(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct BrowserrQueryEmulatedDomains *r)
+{
+ DCESRV_FAULT_VOID(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ BrowserrServerEnumEx
+*/
+static void dcesrv_BrowserrServerEnumEx(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct BrowserrServerEnumEx *r)
+{
+ DCESRV_FAULT_VOID(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_browser_s.c"
diff --git a/source4/rpc_server/common/common.h b/source4/rpc_server/common/common.h
new file mode 100644
index 0000000000..aacd460388
--- /dev/null
+++ b/source4/rpc_server/common/common.h
@@ -0,0 +1,45 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ common macros for the dcerpc server interfaces
+
+ Copyright (C) Stefan (metze) Metzmacher 2004
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _DCERPC_SERVER_COMMON_H_
+#define _DCERPC_SERVER_COMMON_H_
+
+struct share_config;
+struct dcesrv_context;
+enum srvsvc_ShareType dcesrv_common_get_share_type(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx, struct share_config *scfg);
+enum srvsvc_PlatformId dcesrv_common_get_platform_id(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx);
+const char *dcesrv_common_get_lan_root(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx);
+const char *dcesrv_common_get_server_name(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx, const char *server_unc);
+uint32_t dcesrv_common_get_share_permissions(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx, struct share_config *scfg);
+uint32_t dcesrv_common_get_share_current_users(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx, struct share_config *scfg);
+const char *dcesrv_common_get_share_path(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx, struct share_config *scfg);
+
+struct dcesrv_context;
+
+struct dcerpc_server_info {
+ const char *domain_name;
+ uint32_t version_major;
+ uint32_t version_minor;
+ uint32_t version_build;
+};
+
+#endif /* _DCERPC_SERVER_COMMON_H_ */
diff --git a/source4/rpc_server/common/server_info.c b/source4/rpc_server/common/server_info.c
new file mode 100644
index 0000000000..a7384f117d
--- /dev/null
+++ b/source4/rpc_server/common/server_info.c
@@ -0,0 +1,197 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ common server info functions
+
+ Copyright (C) Stefan (metze) Metzmacher 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "librpc/gen_ndr/ndr_srvsvc.h"
+#include "librpc/gen_ndr/svcctl.h"
+#include "rpc_server/dcerpc_server.h"
+#include "dsdb/samdb/samdb.h"
+#include "auth/auth.h"
+#include "param/param.h"
+#include "rpc_server/common/common.h"
+#include "rpc_server/common/proto.h"
+
+/*
+ Here are common server info functions used by some dcerpc server interfaces
+*/
+
+/* This hardcoded value should go into a ldb database! */
+enum srvsvc_PlatformId dcesrv_common_get_platform_id(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx)
+{
+ enum srvsvc_PlatformId id;
+
+ id = lp_parm_int(dce_ctx->lp_ctx, NULL, "server_info", "platform_id", PLATFORM_ID_NT);
+
+ return id;
+}
+
+const char *dcesrv_common_get_server_name(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx, const char *server_unc)
+{
+ const char *p = server_unc;
+
+ /* if there's no string return our NETBIOS name */
+ if (!p) {
+ return talloc_strdup(mem_ctx, lp_netbios_name(dce_ctx->lp_ctx));
+ }
+
+ /* if there're '\\\\' in front remove them otherwise just pass the string */
+ if (p[0] == '\\' && p[1] == '\\') {
+ p += 2;
+ }
+
+ return talloc_strdup(mem_ctx, p);
+}
+
+
+/* This hardcoded value should go into a ldb database! */
+uint32_t dcesrv_common_get_server_type(TALLOC_CTX *mem_ctx, struct tevent_context *event_ctx, struct dcesrv_context *dce_ctx)
+{
+ int default_server_announce = 0;
+ default_server_announce |= SV_TYPE_WORKSTATION;
+ default_server_announce |= SV_TYPE_SERVER;
+ default_server_announce |= SV_TYPE_SERVER_UNIX;
+
+ switch (lp_announce_as(dce_ctx->lp_ctx)) {
+ case ANNOUNCE_AS_NT_SERVER:
+ default_server_announce |= SV_TYPE_SERVER_NT;
+ /* fall through... */
+ case ANNOUNCE_AS_NT_WORKSTATION:
+ default_server_announce |= SV_TYPE_NT;
+ break;
+ case ANNOUNCE_AS_WIN95:
+ default_server_announce |= SV_TYPE_WIN95_PLUS;
+ break;
+ case ANNOUNCE_AS_WFW:
+ default_server_announce |= SV_TYPE_WFW;
+ break;
+ default:
+ break;
+ }
+
+ switch (lp_server_role(dce_ctx->lp_ctx)) {
+ case ROLE_DOMAIN_MEMBER:
+ default_server_announce |= SV_TYPE_DOMAIN_MEMBER;
+ break;
+ case ROLE_DOMAIN_CONTROLLER:
+ {
+ struct ldb_context *samctx;
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+ if (!tmp_ctx) {
+ break;
+ }
+ /* open main ldb */
+ samctx = samdb_connect(tmp_ctx, event_ctx, dce_ctx->lp_ctx, anonymous_session(tmp_ctx, event_ctx, dce_ctx->lp_ctx));
+ if (samctx == NULL) {
+ DEBUG(2,("Unable to open samdb in determining server announce flags\n"));
+ } else {
+ /* Determine if we are the pdc */
+ bool is_pdc = samdb_is_pdc(samctx);
+ if (is_pdc) {
+ default_server_announce |= SV_TYPE_DOMAIN_CTRL;
+ } else {
+ default_server_announce |= SV_TYPE_DOMAIN_BAKCTRL;
+ }
+ }
+ /* Close it */
+ talloc_free(tmp_ctx);
+ break;
+ }
+ case ROLE_STANDALONE:
+ default:
+ break;
+ }
+ if (lp_time_server(dce_ctx->lp_ctx))
+ default_server_announce |= SV_TYPE_TIME_SOURCE;
+
+ if (lp_host_msdfs(dce_ctx->lp_ctx))
+ default_server_announce |= SV_TYPE_DFS_SERVER;
+
+
+#if 0
+ {
+ /* TODO: announce us as print server when we are a print server */
+ bool is_print_server = false;
+ if (is_print_server) {
+ default_server_announce |= SV_TYPE_PRINTQ_SERVER;
+ }
+ }
+#endif
+ return default_server_announce;
+}
+
+/* This hardcoded value should go into a ldb database! */
+const char *dcesrv_common_get_lan_root(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx)
+{
+ return talloc_strdup(mem_ctx, "");
+}
+
+/* This hardcoded value should go into a ldb database! */
+uint32_t dcesrv_common_get_users(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx)
+{
+ return -1;
+}
+
+/* This hardcoded value should go into a ldb database! */
+uint32_t dcesrv_common_get_disc(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx)
+{
+ return 15;
+}
+
+/* This hardcoded value should go into a ldb database! */
+uint32_t dcesrv_common_get_hidden(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx)
+{
+ return 0;
+}
+
+/* This hardcoded value should go into a ldb database! */
+uint32_t dcesrv_common_get_announce(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx)
+{
+ return 240;
+}
+
+/* This hardcoded value should go into a ldb database! */
+uint32_t dcesrv_common_get_anndelta(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx)
+{
+ return 3000;
+}
+
+/* This hardcoded value should go into a ldb database! */
+uint32_t dcesrv_common_get_licenses(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx)
+{
+ return 0;
+}
+
+/* This hardcoded value should go into a ldb database! */
+const char *dcesrv_common_get_userpath(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx)
+{
+ return talloc_strdup(mem_ctx, "c:\\");
+}
+
+#define INVALID_SHARE_NAME_CHARS " \"*+,./:;<=>?[\\]|"
+
+bool dcesrv_common_validate_share_name(TALLOC_CTX *mem_ctx, const char *share_name)
+{
+ if (strpbrk(share_name, INVALID_SHARE_NAME_CHARS)) {
+ return false;
+ }
+
+ return true;
+}
diff --git a/source4/rpc_server/common/share_info.c b/source4/rpc_server/common/share_info.c
new file mode 100644
index 0000000000..48870e602f
--- /dev/null
+++ b/source4/rpc_server/common/share_info.c
@@ -0,0 +1,113 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ common share info functions
+
+ Copyright (C) Stefan (metze) Metzmacher 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "param/share.h"
+#include "librpc/gen_ndr/srvsvc.h"
+#include "rpc_server/dcerpc_server.h"
+#include "rpc_server/common/common.h"
+#include "rpc_server/common/proto.h"
+
+/*
+ Here are common server info functions used by some dcerpc server interfaces
+*/
+
+/* This hardcoded value should go into a ldb database! */
+uint32_t dcesrv_common_get_share_permissions(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx, struct share_config *scfg)
+{
+ return 0;
+}
+
+/* This hardcoded value should go into a ldb database! */
+uint32_t dcesrv_common_get_share_current_users(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx, struct share_config *scfg)
+{
+ return 1;
+}
+
+/* This hardcoded value should go into a ldb database! */
+enum srvsvc_ShareType dcesrv_common_get_share_type(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx, struct share_config *scfg)
+{
+ /* for disk share 0x00000000
+ * for print share 0x00000001
+ * for IPC$ share 0x00000003
+ *
+ * administrative shares:
+ * ADMIN$, IPC$, C$, D$, E$ ... are type |= 0x80000000
+ * this ones are hidden in NetShareEnum, but shown in NetShareEnumAll
+ */
+ enum srvsvc_ShareType share_type = 0;
+ const char *sharetype;
+
+ if (!share_bool_option(scfg, SHARE_BROWSEABLE, SHARE_BROWSEABLE_DEFAULT)) {
+ share_type |= STYPE_HIDDEN;
+ }
+
+ sharetype = share_string_option(scfg, SHARE_TYPE, SHARE_TYPE_DEFAULT);
+ if (sharetype && strcasecmp(sharetype, "IPC") == 0) {
+ share_type |= STYPE_IPC;
+ return share_type;
+ }
+
+ if (sharetype && strcasecmp(sharetype, "PRINTER") == 0) {
+ share_type |= STYPE_PRINTQ;
+ return share_type;
+ }
+
+ share_type |= STYPE_DISKTREE;
+
+ return share_type;
+}
+
+/* This hardcoded value should go into a ldb database! */
+const char *dcesrv_common_get_share_path(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx, struct share_config *scfg)
+{
+ const char *sharetype;
+ char *p;
+
+ sharetype = share_string_option(scfg, SHARE_TYPE, SHARE_TYPE_DEFAULT);
+
+ if (sharetype && strcasecmp(sharetype, "IPC") == 0) {
+ return talloc_strdup(mem_ctx, "");
+ }
+
+ p = talloc_strdup(mem_ctx, share_string_option(scfg, SHARE_PATH, ""));
+ if (!p) {
+ return NULL;
+ }
+ if (p[0] == '\0') {
+ return p;
+ }
+ all_string_sub(p, "/", "\\", 0);
+
+ return talloc_asprintf(mem_ctx, "C:%s", p);
+}
+
+/* This hardcoded value should go into a ldb database! */
+uint32_t dcesrv_common_get_share_dfs_flags(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx, struct share_config *scfg)
+{
+ return 0;
+}
+
+/* This hardcoded value should go into a ldb database! */
+struct security_descriptor *dcesrv_common_get_security_descriptor(TALLOC_CTX *mem_ctx, struct dcesrv_context *dce_ctx, struct share_config *scfg)
+{
+ return NULL;
+}
diff --git a/source4/rpc_server/config.mk b/source4/rpc_server/config.mk
new file mode 100644
index 0000000000..d05b0a0c0d
--- /dev/null
+++ b/source4/rpc_server/config.mk
@@ -0,0 +1,226 @@
+# DCERPC Server subsystem
+
+################################################
+# Start SUBSYSTEM DCERPC_COMMON
+[SUBSYSTEM::DCERPC_COMMON]
+PRIVATE_DEPENDENCIES = LIBLDB
+#
+# End SUBSYSTEM DCERPC_COMMON
+################################################
+
+DCERPC_COMMON_OBJ_FILES = $(addprefix $(rpc_serversrcdir)/common/, server_info.o share_info.o)
+
+$(eval $(call proto_header_template,$(rpc_serversrcdir)/common/proto.h,$(DCERPC_COMMON_OBJ_FILES:.o=.c)))
+
+PUBLIC_HEADERS += $(rpc_serversrcdir)/common/common.h
+
+################################################
+# Start MODULE dcerpc_rpcecho
+[MODULE::dcerpc_rpcecho]
+INIT_FUNCTION = dcerpc_server_rpcecho_init
+SUBSYSTEM = dcerpc_server
+PRIVATE_DEPENDENCIES = NDR_ECHO LIBEVENTS
+# End MODULE dcerpc_rpcecho
+################################################
+
+dcerpc_rpcecho_OBJ_FILES = $(rpc_serversrcdir)/echo/rpc_echo.o
+
+################################################
+# Start MODULE dcerpc_epmapper
+[MODULE::dcerpc_epmapper]
+INIT_FUNCTION = dcerpc_server_epmapper_init
+SUBSYSTEM = dcerpc_server
+PRIVATE_DEPENDENCIES = NDR_EPMAPPER
+# End MODULE dcerpc_epmapper
+################################################
+
+dcerpc_epmapper_OBJ_FILES = $(rpc_serversrcdir)/epmapper/rpc_epmapper.o
+
+################################################
+# Start MODULE dcerpc_remote
+[MODULE::dcerpc_remote]
+INIT_FUNCTION = dcerpc_server_remote_init
+SUBSYSTEM = dcerpc_server
+PRIVATE_DEPENDENCIES = \
+ LIBCLI_SMB NDR_TABLE
+# End MODULE dcerpc_remote
+################################################
+
+dcerpc_remote_OBJ_FILES = $(rpc_serversrcdir)/remote/dcesrv_remote.o
+
+################################################
+# Start MODULE dcerpc_srvsvc
+[MODULE::dcerpc_srvsvc]
+INIT_FUNCTION = dcerpc_server_srvsvc_init
+SUBSYSTEM = dcerpc_server
+PRIVATE_DEPENDENCIES = \
+ DCERPC_COMMON NDR_SRVSVC share
+# End MODULE dcerpc_srvsvc
+################################################
+
+
+dcerpc_srvsvc_OBJ_FILES = $(addprefix $(rpc_serversrcdir)/srvsvc/, dcesrv_srvsvc.o srvsvc_ntvfs.o)
+
+$(eval $(call proto_header_template,$(rpc_serversrcdir)/srvsvc/proto.h,$(dcerpc_srvsvc_OBJ_FILES:.o=.c)))
+
+################################################
+# Start MODULE dcerpc_wkssvc
+[MODULE::dcerpc_wkssvc]
+INIT_FUNCTION = dcerpc_server_wkssvc_init
+SUBSYSTEM = dcerpc_server
+PRIVATE_DEPENDENCIES = \
+ DCERPC_COMMON NDR_WKSSVC
+# End MODULE dcerpc_wkssvc
+################################################
+
+dcerpc_wkssvc_OBJ_FILES = $(rpc_serversrcdir)/wkssvc/dcesrv_wkssvc.o
+
+################################################
+# Start MODULE dcerpc_unixinfo
+[MODULE::dcerpc_unixinfo]
+INIT_FUNCTION = dcerpc_server_unixinfo_init
+SUBSYSTEM = dcerpc_server
+PRIVATE_DEPENDENCIES = \
+ DCERPC_COMMON \
+ SAMDB \
+ NDR_UNIXINFO \
+ NSS_WRAPPER \
+ LIBWBCLIENT
+# End MODULE dcerpc_unixinfo
+################################################
+
+dcerpc_unixinfo_OBJ_FILES = $(rpc_serversrcdir)/unixinfo/dcesrv_unixinfo.o
+
+################################################
+# Start MODULE dcesrv_samr
+[MODULE::dcesrv_samr]
+INIT_FUNCTION = dcerpc_server_samr_init
+SUBSYSTEM = dcerpc_server
+PRIVATE_DEPENDENCIES = \
+ SAMDB \
+ DCERPC_COMMON \
+ NDR_SAMR
+# End MODULE dcesrv_samr
+################################################
+
+dcesrv_samr_OBJ_FILES = $(addprefix $(rpc_serversrcdir)/samr/, dcesrv_samr.o samr_password.o)
+
+$(eval $(call proto_header_template,$(rpc_serversrcdir)/samr/proto.h,$(dcesrv_samr_OBJ_FILES:.o=.c)))
+
+################################################
+# Start MODULE dcerpc_winreg
+[MODULE::dcerpc_winreg]
+INIT_FUNCTION = dcerpc_server_winreg_init
+SUBSYSTEM = dcerpc_server
+OUTPUT_TYPE = MERGED_OBJ
+PRIVATE_DEPENDENCIES = \
+ registry NDR_WINREG
+# End MODULE dcerpc_winreg
+################################################
+
+dcerpc_winreg_OBJ_FILES = $(rpc_serversrcdir)/winreg/rpc_winreg.o
+
+################################################
+# Start MODULE dcerpc_netlogon
+[MODULE::dcerpc_netlogon]
+INIT_FUNCTION = dcerpc_server_netlogon_init
+SUBSYSTEM = dcerpc_server
+PRIVATE_DEPENDENCIES = \
+ DCERPC_COMMON \
+ SCHANNELDB \
+ NDR_NETLOGON \
+ auth_sam
+# End MODULE dcerpc_netlogon
+################################################
+
+dcerpc_netlogon_OBJ_FILES = $(rpc_serversrcdir)/netlogon/dcerpc_netlogon.o
+
+################################################
+# Start MODULE dcerpc_lsa
+[MODULE::dcerpc_lsarpc]
+INIT_FUNCTION = dcerpc_server_lsa_init
+SUBSYSTEM = dcerpc_server
+PRIVATE_DEPENDENCIES = \
+ SAMDB \
+ DCERPC_COMMON \
+ NDR_LSA \
+ LIBCLI_AUTH \
+ NDR_DSSETUP
+# End MODULE dcerpc_lsa
+################################################
+
+dcerpc_lsarpc_OBJ_FILES = $(addprefix $(rpc_serversrcdir)/lsa/, dcesrv_lsa.o lsa_init.o lsa_lookup.o)
+
+$(eval $(call proto_header_template,$(rpc_serversrcdir)/lsa/proto.h,$(dcerpc_lsarpc_OBJ_FILES:.o=.c)))
+
+
+################################################
+# Start MODULE dcerpc_spoolss
+[MODULE::dcerpc_spoolss]
+INIT_FUNCTION = dcerpc_server_spoolss_init
+SUBSYSTEM = dcerpc_server
+OUTPUT_TYPE = MERGED_OBJ
+PRIVATE_DEPENDENCIES = \
+ DCERPC_COMMON \
+ NDR_SPOOLSS \
+ ntptr \
+ RPC_NDR_SPOOLSS
+# End MODULE dcerpc_spoolss
+################################################
+
+dcerpc_spoolss_OBJ_FILES = $(rpc_serversrcdir)/spoolss/dcesrv_spoolss.o
+
+################################################
+# Start MODULE dcerpc_drsuapi
+[MODULE::dcerpc_drsuapi]
+INIT_FUNCTION = dcerpc_server_drsuapi_init
+SUBSYSTEM = dcerpc_server
+PRIVATE_DEPENDENCIES = \
+ SAMDB \
+ DCERPC_COMMON \
+ NDR_DRSUAPI
+# End MODULE dcerpc_drsuapi
+################################################
+
+dcerpc_drsuapi_OBJ_FILES = $(rpc_serversrcdir)/drsuapi/dcesrv_drsuapi.o
+
+################################################
+# Start MODULE dcerpc_browser
+[MODULE::dcerpc_browser]
+INIT_FUNCTION = dcerpc_server_browser_init
+SUBSYSTEM = dcerpc_server
+PRIVATE_DEPENDENCIES = \
+ DCERPC_COMMON \
+ NDR_BROWSER
+# End MODULE dcerpc_browser
+################################################
+
+dcerpc_browser_OBJ_FILES = $(rpc_serversrcdir)/browser/dcesrv_browser.o
+
+################################################
+# Start SUBSYSTEM dcerpc_server
+[SUBSYSTEM::dcerpc_server]
+PRIVATE_DEPENDENCIES = \
+ LIBCLI_AUTH \
+ LIBNDR \
+ dcerpc samba_server_gensec
+
+dcerpc_server_OBJ_FILES = $(addprefix $(rpc_serversrcdir)/, \
+ dcerpc_server.o \
+ dcesrv_auth.o \
+ dcesrv_mgmt.o \
+ handles.o)
+
+$(eval $(call proto_header_template,$(rpc_serversrcdir)/dcerpc_server_proto.h,$(dcerpc_server_OBJ_FILES:.o=.c)))
+
+# End SUBSYSTEM DCERPC
+################################################
+
+PUBLIC_HEADERS += $(rpc_serversrcdir)/dcerpc_server.h
+
+[MODULE::DCESRV]
+INIT_FUNCTION = server_service_rpc_init
+SUBSYSTEM = service
+PRIVATE_DEPENDENCIES = dcerpc_server
+
+DCESRV_OBJ_FILES = $(rpc_serversrcdir)/service_rpc.o
diff --git a/source4/rpc_server/dcerpc_server.c b/source4/rpc_server/dcerpc_server.c
new file mode 100644
index 0000000000..d7bd7601f7
--- /dev/null
+++ b/source4/rpc_server/dcerpc_server.c
@@ -0,0 +1,1512 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ server side dcerpc core code
+
+ Copyright (C) Andrew Tridgell 2003-2005
+ Copyright (C) Stefan (metze) Metzmacher 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "librpc/gen_ndr/ndr_dcerpc.h"
+#include "auth/auth.h"
+#include "auth/gensec/gensec.h"
+#include "../lib/util/dlinklist.h"
+#include "rpc_server/dcerpc_server.h"
+#include "rpc_server/dcerpc_server_proto.h"
+#include "librpc/rpc/dcerpc_proto.h"
+#include "lib/events/events.h"
+#include "smbd/service_task.h"
+#include "smbd/service_stream.h"
+#include "smbd/service.h"
+#include "system/filesys.h"
+#include "libcli/security/security.h"
+#include "param/param.h"
+
+#define SAMBA_ASSOC_GROUP 0x12345678
+
+extern const struct dcesrv_interface dcesrv_mgmt_interface;
+
+/*
+ see if two endpoints match
+*/
+static bool endpoints_match(const struct dcerpc_binding *ep1,
+ const struct dcerpc_binding *ep2)
+{
+ if (ep1->transport != ep2->transport) {
+ return false;
+ }
+
+ if (!ep1->endpoint || !ep2->endpoint) {
+ return ep1->endpoint == ep2->endpoint;
+ }
+
+ if (strcasecmp(ep1->endpoint, ep2->endpoint) != 0)
+ return false;
+
+ return true;
+}
+
+/*
+ find an endpoint in the dcesrv_context
+*/
+static struct dcesrv_endpoint *find_endpoint(struct dcesrv_context *dce_ctx,
+ const struct dcerpc_binding *ep_description)
+{
+ struct dcesrv_endpoint *ep;
+ for (ep=dce_ctx->endpoint_list; ep; ep=ep->next) {
+ if (endpoints_match(ep->ep_description, ep_description)) {
+ return ep;
+ }
+ }
+ return NULL;
+}
+
+/*
+ find a registered context_id from a bind or alter_context
+*/
+static struct dcesrv_connection_context *dcesrv_find_context(struct dcesrv_connection *conn,
+ uint32_t context_id)
+{
+ struct dcesrv_connection_context *c;
+ for (c=conn->contexts;c;c=c->next) {
+ if (c->context_id == context_id) return c;
+ }
+ return NULL;
+}
+
+/*
+ see if a uuid and if_version match to an interface
+*/
+static bool interface_match(const struct dcesrv_interface *if1,
+ const struct dcesrv_interface *if2)
+{
+ return (if1->syntax_id.if_version == if2->syntax_id.if_version &&
+ GUID_equal(&if1->syntax_id.uuid, &if2->syntax_id.uuid));
+}
+
+/*
+ find the interface operations on an endpoint
+*/
+static const struct dcesrv_interface *find_interface(const struct dcesrv_endpoint *endpoint,
+ const struct dcesrv_interface *iface)
+{
+ struct dcesrv_if_list *ifl;
+ for (ifl=endpoint->interface_list; ifl; ifl=ifl->next) {
+ if (interface_match(&(ifl->iface), iface)) {
+ return &(ifl->iface);
+ }
+ }
+ return NULL;
+}
+
+/*
+ see if a uuid and if_version match to an interface
+*/
+static bool interface_match_by_uuid(const struct dcesrv_interface *iface,
+ const struct GUID *uuid, uint32_t if_version)
+{
+ return (iface->syntax_id.if_version == if_version &&
+ GUID_equal(&iface->syntax_id.uuid, uuid));
+}
+
+/*
+ find the interface operations on an endpoint by uuid
+*/
+static const struct dcesrv_interface *find_interface_by_uuid(const struct dcesrv_endpoint *endpoint,
+ const struct GUID *uuid, uint32_t if_version)
+{
+ struct dcesrv_if_list *ifl;
+ for (ifl=endpoint->interface_list; ifl; ifl=ifl->next) {
+ if (interface_match_by_uuid(&(ifl->iface), uuid, if_version)) {
+ return &(ifl->iface);
+ }
+ }
+ return NULL;
+}
+
+/*
+ find the earlier parts of a fragmented call awaiting reassembily
+*/
+static struct dcesrv_call_state *dcesrv_find_fragmented_call(struct dcesrv_connection *dce_conn, uint16_t call_id)
+{
+ struct dcesrv_call_state *c;
+ for (c=dce_conn->incoming_fragmented_call_list;c;c=c->next) {
+ if (c->pkt.call_id == call_id) {
+ return c;
+ }
+ }
+ return NULL;
+}
+
+/*
+ register an interface on an endpoint
+*/
+_PUBLIC_ NTSTATUS dcesrv_interface_register(struct dcesrv_context *dce_ctx,
+ const char *ep_name,
+ const struct dcesrv_interface *iface,
+ const struct security_descriptor *sd)
+{
+ struct dcesrv_endpoint *ep;
+ struct dcesrv_if_list *ifl;
+ struct dcerpc_binding *binding;
+ bool add_ep = false;
+ NTSTATUS status;
+
+ status = dcerpc_parse_binding(dce_ctx, ep_name, &binding);
+
+ if (NT_STATUS_IS_ERR(status)) {
+ DEBUG(0, ("Trouble parsing binding string '%s'\n", ep_name));
+ return status;
+ }
+
+ /* check if this endpoint exists
+ */
+ if ((ep=find_endpoint(dce_ctx, binding))==NULL) {
+ ep = talloc(dce_ctx, struct dcesrv_endpoint);
+ if (!ep) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ ZERO_STRUCTP(ep);
+ ep->ep_description = talloc_reference(ep, binding);
+ add_ep = true;
+
+ /* add mgmt interface */
+ ifl = talloc(dce_ctx, struct dcesrv_if_list);
+ if (!ifl) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ memcpy(&(ifl->iface), &dcesrv_mgmt_interface,
+ sizeof(struct dcesrv_interface));
+
+ DLIST_ADD(ep->interface_list, ifl);
+ }
+
+ /* see if the interface is already registered on te endpoint */
+ if (find_interface(ep, iface)!=NULL) {
+ DEBUG(0,("dcesrv_interface_register: interface '%s' already registered on endpoint '%s'\n",
+ iface->name, ep_name));
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ /* talloc a new interface list element */
+ ifl = talloc(dce_ctx, struct dcesrv_if_list);
+ if (!ifl) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* copy the given interface struct to the one on the endpoints interface list */
+ memcpy(&(ifl->iface),iface, sizeof(struct dcesrv_interface));
+
+ /* if we have a security descriptor given,
+ * we should see if we can set it up on the endpoint
+ */
+ if (sd != NULL) {
+ /* if there's currently no security descriptor given on the endpoint
+ * we try to set it
+ */
+ if (ep->sd == NULL) {
+ ep->sd = security_descriptor_copy(dce_ctx, sd);
+ }
+
+ /* if now there's no security descriptor given on the endpoint
+ * something goes wrong, either we failed to copy the security descriptor
+ * or there was already one on the endpoint
+ */
+ if (ep->sd != NULL) {
+ DEBUG(0,("dcesrv_interface_register: interface '%s' failed to setup a security descriptor\n"
+ " on endpoint '%s'\n",
+ iface->name, ep_name));
+ if (add_ep) free(ep);
+ free(ifl);
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+ }
+
+ /* finally add the interface on the endpoint */
+ DLIST_ADD(ep->interface_list, ifl);
+
+ /* if it's a new endpoint add it to the dcesrv_context */
+ if (add_ep) {
+ DLIST_ADD(dce_ctx->endpoint_list, ep);
+ }
+
+ DEBUG(4,("dcesrv_interface_register: interface '%s' registered on endpoint '%s'\n",
+ iface->name, ep_name));
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS dcesrv_inherited_session_key(struct dcesrv_connection *p,
+ DATA_BLOB *session_key)
+{
+ if (p->auth_state.session_info->session_key.length) {
+ *session_key = p->auth_state.session_info->session_key;
+ return NT_STATUS_OK;
+ }
+ return NT_STATUS_NO_USER_SESSION_KEY;
+}
+
+NTSTATUS dcesrv_generic_session_key(struct dcesrv_connection *p,
+ DATA_BLOB *session_key)
+{
+ /* this took quite a few CPU cycles to find ... */
+ session_key->data = discard_const_p(uint8_t, "SystemLibraryDTC");
+ session_key->length = 16;
+ return NT_STATUS_OK;
+}
+
+/*
+ fetch the user session key - may be default (above) or the SMB session key
+
+ The key is always truncated to 16 bytes
+*/
+_PUBLIC_ NTSTATUS dcesrv_fetch_session_key(struct dcesrv_connection *p,
+ DATA_BLOB *session_key)
+{
+ NTSTATUS status = p->auth_state.session_key(p, session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ session_key->length = MIN(session_key->length, 16);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ connect to a dcerpc endpoint
+*/
+_PUBLIC_ NTSTATUS dcesrv_endpoint_connect(struct dcesrv_context *dce_ctx,
+ TALLOC_CTX *mem_ctx,
+ const struct dcesrv_endpoint *ep,
+ struct auth_session_info *session_info,
+ struct tevent_context *event_ctx,
+ struct messaging_context *msg_ctx,
+ struct server_id server_id,
+ uint32_t state_flags,
+ struct dcesrv_connection **_p)
+{
+ struct dcesrv_connection *p;
+
+ if (!session_info) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ p = talloc(mem_ctx, struct dcesrv_connection);
+ NT_STATUS_HAVE_NO_MEMORY(p);
+
+ if (!talloc_reference(p, session_info)) {
+ talloc_free(p);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ p->dce_ctx = dce_ctx;
+ p->endpoint = ep;
+ p->contexts = NULL;
+ p->call_list = NULL;
+ p->packet_log_dir = lp_lockdir(dce_ctx->lp_ctx);
+ p->incoming_fragmented_call_list = NULL;
+ p->pending_call_list = NULL;
+ p->cli_max_recv_frag = 0;
+ p->partial_input = data_blob(NULL, 0);
+ p->auth_state.auth_info = NULL;
+ p->auth_state.gensec_security = NULL;
+ p->auth_state.session_info = session_info;
+ p->auth_state.session_key = dcesrv_generic_session_key;
+ p->event_ctx = event_ctx;
+ p->msg_ctx = msg_ctx;
+ p->server_id = server_id;
+ p->processing = false;
+ p->state_flags = state_flags;
+ ZERO_STRUCT(p->transport);
+
+ *_p = p;
+ return NT_STATUS_OK;
+}
+
+/*
+ search and connect to a dcerpc endpoint
+*/
+_PUBLIC_ NTSTATUS dcesrv_endpoint_search_connect(struct dcesrv_context *dce_ctx,
+ TALLOC_CTX *mem_ctx,
+ const struct dcerpc_binding *ep_description,
+ struct auth_session_info *session_info,
+ struct tevent_context *event_ctx,
+ struct messaging_context *msg_ctx,
+ struct server_id server_id,
+ uint32_t state_flags,
+ struct dcesrv_connection **dce_conn_p)
+{
+ NTSTATUS status;
+ const struct dcesrv_endpoint *ep;
+
+ /* make sure this endpoint exists */
+ ep = find_endpoint(dce_ctx, ep_description);
+ if (!ep) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ status = dcesrv_endpoint_connect(dce_ctx, mem_ctx, ep, session_info,
+ event_ctx, msg_ctx, server_id,
+ state_flags, dce_conn_p);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ (*dce_conn_p)->auth_state.session_key = dcesrv_inherited_session_key;
+
+ /* TODO: check security descriptor of the endpoint here
+ * if it's a smb named pipe
+ * if it's failed free dce_conn_p
+ */
+
+ return NT_STATUS_OK;
+}
+
+
+static void dcesrv_init_hdr(struct ncacn_packet *pkt, bool bigendian)
+{
+ pkt->rpc_vers = 5;
+ pkt->rpc_vers_minor = 0;
+ if (bigendian) {
+ pkt->drep[0] = 0;
+ } else {
+ pkt->drep[0] = DCERPC_DREP_LE;
+ }
+ pkt->drep[1] = 0;
+ pkt->drep[2] = 0;
+ pkt->drep[3] = 0;
+}
+
+/*
+ move a call from an existing linked list to the specified list. This
+ prevents bugs where we forget to remove the call from a previous
+ list when moving it.
+ */
+static void dcesrv_call_set_list(struct dcesrv_call_state *call,
+ enum dcesrv_call_list list)
+{
+ switch (call->list) {
+ case DCESRV_LIST_NONE:
+ break;
+ case DCESRV_LIST_CALL_LIST:
+ DLIST_REMOVE(call->conn->call_list, call);
+ break;
+ case DCESRV_LIST_FRAGMENTED_CALL_LIST:
+ DLIST_REMOVE(call->conn->incoming_fragmented_call_list, call);
+ break;
+ case DCESRV_LIST_PENDING_CALL_LIST:
+ DLIST_REMOVE(call->conn->pending_call_list, call);
+ break;
+ }
+ call->list = list;
+ switch (list) {
+ case DCESRV_LIST_NONE:
+ break;
+ case DCESRV_LIST_CALL_LIST:
+ DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
+ break;
+ case DCESRV_LIST_FRAGMENTED_CALL_LIST:
+ DLIST_ADD_END(call->conn->incoming_fragmented_call_list, call, struct dcesrv_call_state *);
+ break;
+ case DCESRV_LIST_PENDING_CALL_LIST:
+ DLIST_ADD_END(call->conn->pending_call_list, call, struct dcesrv_call_state *);
+ break;
+ }
+}
+
+/*
+ return a dcerpc fault
+*/
+static NTSTATUS dcesrv_fault(struct dcesrv_call_state *call, uint32_t fault_code)
+{
+ struct ncacn_packet pkt;
+ struct data_blob_list_item *rep;
+ uint8_t zeros[4];
+ NTSTATUS status;
+
+ /* setup a bind_ack */
+ dcesrv_init_hdr(&pkt, lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
+ pkt.auth_length = 0;
+ pkt.call_id = call->pkt.call_id;
+ pkt.ptype = DCERPC_PKT_FAULT;
+ pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
+ pkt.u.fault.alloc_hint = 0;
+ pkt.u.fault.context_id = 0;
+ pkt.u.fault.cancel_count = 0;
+ pkt.u.fault.status = fault_code;
+
+ ZERO_STRUCT(zeros);
+ pkt.u.fault._pad = data_blob_const(zeros, sizeof(zeros));
+
+ rep = talloc(call, struct data_blob_list_item);
+ if (!rep) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = ncacn_push_auth(&rep->blob, call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx), &pkt, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ dcerpc_set_frag_length(&rep->blob, rep->blob.length);
+
+ DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
+ dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ return a dcerpc bind_nak
+*/
+static NTSTATUS dcesrv_bind_nak(struct dcesrv_call_state *call, uint32_t reason)
+{
+ struct ncacn_packet pkt;
+ struct data_blob_list_item *rep;
+ NTSTATUS status;
+
+ /* setup a bind_nak */
+ dcesrv_init_hdr(&pkt, lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
+ pkt.auth_length = 0;
+ pkt.call_id = call->pkt.call_id;
+ pkt.ptype = DCERPC_PKT_BIND_NAK;
+ pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
+ pkt.u.bind_nak.reject_reason = reason;
+ if (pkt.u.bind_nak.reject_reason == DECRPC_BIND_PROTOCOL_VERSION_NOT_SUPPORTED) {
+ pkt.u.bind_nak.versions.v.num_versions = 0;
+ }
+
+ rep = talloc(call, struct data_blob_list_item);
+ if (!rep) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = ncacn_push_auth(&rep->blob, call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx), &pkt, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ dcerpc_set_frag_length(&rep->blob, rep->blob.length);
+
+ DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
+ dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
+
+ return NT_STATUS_OK;
+}
+
+static int dcesrv_connection_context_destructor(struct dcesrv_connection_context *c)
+{
+ DLIST_REMOVE(c->conn->contexts, c);
+
+ if (c->iface) {
+ c->iface->unbind(c, c->iface);
+ }
+
+ return 0;
+}
+
+/*
+ handle a bind request
+*/
+static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call)
+{
+ uint32_t if_version, transfer_syntax_version;
+ struct GUID uuid, *transfer_syntax_uuid;
+ struct ncacn_packet pkt;
+ struct data_blob_list_item *rep;
+ NTSTATUS status;
+ uint32_t result=0, reason=0;
+ uint32_t context_id;
+ const struct dcesrv_interface *iface;
+ uint32_t extra_flags = 0;
+
+ /*
+ * Association groups allow policy handles to be shared across
+ * multiple client connections. We don't implement this yet.
+ *
+ * So we just allow 0 if the client wants to create a new
+ * association group.
+ *
+ * And we allow the 0x12345678 value, we give away as
+ * assoc_group_id back to the clients
+ */
+ if (call->pkt.u.bind.assoc_group_id != 0 &&
+ lp_parm_bool(call->conn->dce_ctx->lp_ctx, NULL, "dcesrv","assoc group checking", true) &&
+ call->pkt.u.bind.assoc_group_id != SAMBA_ASSOC_GROUP) {
+ return dcesrv_bind_nak(call, 0);
+ }
+
+ if (call->pkt.u.bind.num_contexts < 1 ||
+ call->pkt.u.bind.ctx_list[0].num_transfer_syntaxes < 1) {
+ return dcesrv_bind_nak(call, 0);
+ }
+
+ context_id = call->pkt.u.bind.ctx_list[0].context_id;
+
+ /* you can't bind twice on one context */
+ if (dcesrv_find_context(call->conn, context_id) != NULL) {
+ return dcesrv_bind_nak(call, 0);
+ }
+
+ if_version = call->pkt.u.bind.ctx_list[0].abstract_syntax.if_version;
+ uuid = call->pkt.u.bind.ctx_list[0].abstract_syntax.uuid;
+
+ transfer_syntax_version = call->pkt.u.bind.ctx_list[0].transfer_syntaxes[0].if_version;
+ transfer_syntax_uuid = &call->pkt.u.bind.ctx_list[0].transfer_syntaxes[0].uuid;
+ if (!GUID_equal(&ndr_transfer_syntax.uuid, transfer_syntax_uuid) != 0 ||
+ ndr_transfer_syntax.if_version != transfer_syntax_version) {
+ char *uuid_str = GUID_string(call, transfer_syntax_uuid);
+ /* we only do NDR encoded dcerpc */
+ DEBUG(0,("Non NDR transfer syntax requested - %s\n", uuid_str));
+ talloc_free(uuid_str);
+ return dcesrv_bind_nak(call, 0);
+ }
+
+ iface = find_interface_by_uuid(call->conn->endpoint, &uuid, if_version);
+ if (iface == NULL) {
+ char *uuid_str = GUID_string(call, &uuid);
+ DEBUG(2,("Request for unknown dcerpc interface %s/%d\n", uuid_str, if_version));
+ talloc_free(uuid_str);
+
+ /* we don't know about that interface */
+ result = DCERPC_BIND_PROVIDER_REJECT;
+ reason = DCERPC_BIND_REASON_ASYNTAX;
+ }
+
+ if (iface) {
+ /* add this context to the list of available context_ids */
+ struct dcesrv_connection_context *context = talloc(call->conn,
+ struct dcesrv_connection_context);
+ if (context == NULL) {
+ return dcesrv_bind_nak(call, 0);
+ }
+ context->conn = call->conn;
+ context->iface = iface;
+ context->context_id = context_id;
+ /*
+ * we need to send a non zero assoc_group_id here to make longhorn happy,
+ * it also matches samba3
+ */
+ context->assoc_group_id = SAMBA_ASSOC_GROUP;
+ context->private_data = NULL;
+ context->handles = NULL;
+ DLIST_ADD(call->conn->contexts, context);
+ call->context = context;
+ talloc_set_destructor(context, dcesrv_connection_context_destructor);
+
+ status = iface->bind(call, iface);
+ if (!NT_STATUS_IS_OK(status)) {
+ char *uuid_str = GUID_string(call, &uuid);
+ DEBUG(2,("Request for dcerpc interface %s/%d rejected: %s\n",
+ uuid_str, if_version, nt_errstr(status)));
+ talloc_free(uuid_str);
+ /* we don't want to trigger the iface->unbind() hook */
+ context->iface = NULL;
+ talloc_free(call->context);
+ call->context = NULL;
+ return dcesrv_bind_nak(call, 0);
+ }
+ }
+
+ if (call->conn->cli_max_recv_frag == 0) {
+ call->conn->cli_max_recv_frag = call->pkt.u.bind.max_recv_frag;
+ }
+
+ if ((call->pkt.pfc_flags & DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN) &&
+ lp_parm_bool(call->conn->dce_ctx->lp_ctx, NULL, "dcesrv","header signing", false)) {
+ call->conn->state_flags |= DCESRV_CALL_STATE_FLAG_HEADER_SIGNING;
+ extra_flags |= DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN;
+ }
+
+ /* handle any authentication that is being requested */
+ if (!dcesrv_auth_bind(call)) {
+ talloc_free(call->context);
+ call->context = NULL;
+ return dcesrv_bind_nak(call, DCERPC_BIND_REASON_INVALID_AUTH_TYPE);
+ }
+
+ /* setup a bind_ack */
+ dcesrv_init_hdr(&pkt, lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
+ pkt.auth_length = 0;
+ pkt.call_id = call->pkt.call_id;
+ pkt.ptype = DCERPC_PKT_BIND_ACK;
+ pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST | extra_flags;
+ pkt.u.bind_ack.max_xmit_frag = 0x2000;
+ pkt.u.bind_ack.max_recv_frag = 0x2000;
+
+ /*
+ make it possible for iface->bind() to specify the assoc_group_id
+ This helps the openchange mapiproxy plugin to work correctly.
+
+ metze
+ */
+ if (call->context) {
+ pkt.u.bind_ack.assoc_group_id = call->context->assoc_group_id;
+ } else {
+ /* we better pick something - this chosen so as to send a non zero assoc_group_id (matching windows), it also matches samba3 */
+ pkt.u.bind_ack.assoc_group_id = SAMBA_ASSOC_GROUP;
+ }
+
+ if (iface) {
+ /* FIXME: Use pipe name as specified by endpoint instead of interface name */
+ pkt.u.bind_ack.secondary_address = talloc_asprintf(call, "\\PIPE\\%s", iface->name);
+ } else {
+ pkt.u.bind_ack.secondary_address = "";
+ }
+ pkt.u.bind_ack.num_results = 1;
+ pkt.u.bind_ack.ctx_list = talloc(call, struct dcerpc_ack_ctx);
+ if (!pkt.u.bind_ack.ctx_list) {
+ talloc_free(call->context);
+ call->context = NULL;
+ return NT_STATUS_NO_MEMORY;
+ }
+ pkt.u.bind_ack.ctx_list[0].result = result;
+ pkt.u.bind_ack.ctx_list[0].reason = reason;
+ pkt.u.bind_ack.ctx_list[0].syntax = ndr_transfer_syntax;
+ pkt.u.bind_ack.auth_info = data_blob(NULL, 0);
+
+ status = dcesrv_auth_bind_ack(call, &pkt);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(call->context);
+ call->context = NULL;
+ return dcesrv_bind_nak(call, 0);
+ }
+
+ rep = talloc(call, struct data_blob_list_item);
+ if (!rep) {
+ talloc_free(call->context);
+ call->context = NULL;
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = ncacn_push_auth(&rep->blob, call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx), &pkt, call->conn->auth_state.auth_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(call->context);
+ call->context = NULL;
+ return status;
+ }
+
+ dcerpc_set_frag_length(&rep->blob, rep->blob.length);
+
+ DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
+ dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ handle a auth3 request
+*/
+static NTSTATUS dcesrv_auth3(struct dcesrv_call_state *call)
+{
+ /* handle the auth3 in the auth code */
+ if (!dcesrv_auth_auth3(call)) {
+ return dcesrv_fault(call, DCERPC_FAULT_OTHER);
+ }
+
+ talloc_free(call);
+
+ /* we don't send a reply to a auth3 request, except by a
+ fault */
+ return NT_STATUS_OK;
+}
+
+
+/*
+ handle a bind request
+*/
+static NTSTATUS dcesrv_alter_new_context(struct dcesrv_call_state *call, uint32_t context_id)
+{
+ uint32_t if_version, transfer_syntax_version;
+ struct dcesrv_connection_context *context;
+ const struct dcesrv_interface *iface;
+ struct GUID uuid, *transfer_syntax_uuid;
+ NTSTATUS status;
+
+ if_version = call->pkt.u.alter.ctx_list[0].abstract_syntax.if_version;
+ uuid = call->pkt.u.alter.ctx_list[0].abstract_syntax.uuid;
+
+ transfer_syntax_version = call->pkt.u.alter.ctx_list[0].transfer_syntaxes[0].if_version;
+ transfer_syntax_uuid = &call->pkt.u.alter.ctx_list[0].transfer_syntaxes[0].uuid;
+ if (!GUID_equal(transfer_syntax_uuid, &ndr_transfer_syntax.uuid) ||
+ ndr_transfer_syntax.if_version != transfer_syntax_version) {
+ /* we only do NDR encoded dcerpc */
+ return NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED;
+ }
+
+ iface = find_interface_by_uuid(call->conn->endpoint, &uuid, if_version);
+ if (iface == NULL) {
+ char *uuid_str = GUID_string(call, &uuid);
+ DEBUG(2,("Request for unknown dcerpc interface %s/%d\n", uuid_str, if_version));
+ talloc_free(uuid_str);
+ return NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED;
+ }
+
+ /* add this context to the list of available context_ids */
+ context = talloc(call->conn, struct dcesrv_connection_context);
+ if (context == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ context->conn = call->conn;
+ context->iface = iface;
+ context->context_id = context_id;
+ context->assoc_group_id = SAMBA_ASSOC_GROUP;
+ context->private_data = NULL;
+ context->handles = NULL;
+ DLIST_ADD(call->conn->contexts, context);
+ call->context = context;
+ talloc_set_destructor(context, dcesrv_connection_context_destructor);
+
+ status = iface->bind(call, iface);
+ if (!NT_STATUS_IS_OK(status)) {
+ /* we don't want to trigger the iface->unbind() hook */
+ context->iface = NULL;
+ talloc_free(context);
+ call->context = NULL;
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ handle a alter context request
+*/
+static NTSTATUS dcesrv_alter(struct dcesrv_call_state *call)
+{
+ struct ncacn_packet pkt;
+ struct data_blob_list_item *rep;
+ NTSTATUS status;
+ uint32_t result=0, reason=0;
+ uint32_t context_id;
+
+ /* handle any authentication that is being requested */
+ if (!dcesrv_auth_alter(call)) {
+ /* TODO: work out the right reject code */
+ result = DCERPC_BIND_PROVIDER_REJECT;
+ reason = DCERPC_BIND_REASON_ASYNTAX;
+ }
+
+ context_id = call->pkt.u.alter.ctx_list[0].context_id;
+
+ /* see if they are asking for a new interface */
+ if (result == 0) {
+ call->context = dcesrv_find_context(call->conn, context_id);
+ if (!call->context) {
+ status = dcesrv_alter_new_context(call, context_id);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = DCERPC_BIND_PROVIDER_REJECT;
+ reason = DCERPC_BIND_REASON_ASYNTAX;
+ }
+ }
+ }
+
+ if (result == 0 &&
+ call->pkt.u.alter.assoc_group_id != 0 &&
+ lp_parm_bool(call->conn->dce_ctx->lp_ctx, NULL, "dcesrv","assoc group checking", true) &&
+ call->pkt.u.alter.assoc_group_id != call->context->assoc_group_id) {
+ /* TODO: work out what to return here */
+ result = DCERPC_BIND_PROVIDER_REJECT;
+ reason = DCERPC_BIND_REASON_ASYNTAX;
+ }
+
+ /* setup a alter_resp */
+ dcesrv_init_hdr(&pkt, lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
+ pkt.auth_length = 0;
+ pkt.call_id = call->pkt.call_id;
+ pkt.ptype = DCERPC_PKT_ALTER_RESP;
+ pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
+ pkt.u.alter_resp.max_xmit_frag = 0x2000;
+ pkt.u.alter_resp.max_recv_frag = 0x2000;
+ if (result == 0) {
+ pkt.u.alter_resp.assoc_group_id = call->context->assoc_group_id;
+ } else {
+ pkt.u.alter_resp.assoc_group_id = 0;
+ }
+ pkt.u.alter_resp.num_results = 1;
+ pkt.u.alter_resp.ctx_list = talloc_array(call, struct dcerpc_ack_ctx, 1);
+ if (!pkt.u.alter_resp.ctx_list) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ pkt.u.alter_resp.ctx_list[0].result = result;
+ pkt.u.alter_resp.ctx_list[0].reason = reason;
+ pkt.u.alter_resp.ctx_list[0].syntax = ndr_transfer_syntax;
+ pkt.u.alter_resp.auth_info = data_blob(NULL, 0);
+ pkt.u.alter_resp.secondary_address = "";
+
+ status = dcesrv_auth_alter_ack(call, &pkt);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)
+ || NT_STATUS_EQUAL(status, NT_STATUS_LOGON_FAILURE)
+ || NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)
+ || NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
+ return dcesrv_fault(call, DCERPC_FAULT_ACCESS_DENIED);
+ }
+ return dcesrv_fault(call, 0);
+ }
+
+ rep = talloc(call, struct data_blob_list_item);
+ if (!rep) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = ncacn_push_auth(&rep->blob, call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx), &pkt, call->conn->auth_state.auth_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ dcerpc_set_frag_length(&rep->blob, rep->blob.length);
+
+ DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
+ dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ handle a dcerpc request packet
+*/
+static NTSTATUS dcesrv_request(struct dcesrv_call_state *call)
+{
+ struct ndr_pull *pull;
+ NTSTATUS status;
+ struct dcesrv_connection_context *context;
+
+ /* if authenticated, and the mech we use can't do async replies, don't use them... */
+ if (call->conn->auth_state.gensec_security &&
+ !gensec_have_feature(call->conn->auth_state.gensec_security, GENSEC_FEATURE_ASYNC_REPLIES)) {
+ call->state_flags &= ~DCESRV_CALL_STATE_FLAG_MAY_ASYNC;
+ }
+
+ context = dcesrv_find_context(call->conn, call->pkt.u.request.context_id);
+ if (context == NULL) {
+ return dcesrv_fault(call, DCERPC_FAULT_UNK_IF);
+ }
+
+ pull = ndr_pull_init_blob(&call->pkt.u.request.stub_and_verifier, call,
+ lp_iconv_convenience(call->conn->dce_ctx->lp_ctx));
+ NT_STATUS_HAVE_NO_MEMORY(pull);
+
+ pull->flags |= LIBNDR_FLAG_REF_ALLOC;
+
+ call->context = context;
+ call->ndr_pull = pull;
+
+ if (!(call->pkt.drep[0] & DCERPC_DREP_LE)) {
+ pull->flags |= LIBNDR_FLAG_BIGENDIAN;
+ }
+
+ /* unravel the NDR for the packet */
+ status = context->iface->ndr_pull(call, call, pull, &call->r);
+ if (!NT_STATUS_IS_OK(status)) {
+ return dcesrv_fault(call, call->fault_code);
+ }
+
+ if (pull->offset != pull->data_size) {
+ DEBUG(3,("Warning: %d extra bytes in incoming RPC request\n",
+ pull->data_size - pull->offset));
+ dump_data(10, pull->data+pull->offset, pull->data_size - pull->offset);
+ }
+
+ /* call the dispatch function */
+ status = context->iface->dispatch(call, call, call->r);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(5,("dcerpc fault in call %s:%02x - %s\n",
+ context->iface->name,
+ call->pkt.u.request.opnum,
+ dcerpc_errstr(pull, call->fault_code)));
+ return dcesrv_fault(call, call->fault_code);
+ }
+
+ /* add the call to the pending list */
+ dcesrv_call_set_list(call, DCESRV_LIST_PENDING_CALL_LIST);
+
+ if (call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
+ return NT_STATUS_OK;
+ }
+
+ return dcesrv_reply(call);
+}
+
+_PUBLIC_ NTSTATUS dcesrv_reply(struct dcesrv_call_state *call)
+{
+ struct ndr_push *push;
+ NTSTATUS status;
+ DATA_BLOB stub;
+ uint32_t total_length, chunk_size;
+ struct dcesrv_connection_context *context = call->context;
+ size_t sig_size = 0;
+
+ /* call the reply function */
+ status = context->iface->reply(call, call, call->r);
+ if (!NT_STATUS_IS_OK(status)) {
+ return dcesrv_fault(call, call->fault_code);
+ }
+
+ /* form the reply NDR */
+ push = ndr_push_init_ctx(call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx));
+ NT_STATUS_HAVE_NO_MEMORY(push);
+
+ /* carry over the pointer count to the reply in case we are
+ using full pointer. See NDR specification for full
+ pointers */
+ push->ptr_count = call->ndr_pull->ptr_count;
+
+ if (lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx)) {
+ push->flags |= LIBNDR_FLAG_BIGENDIAN;
+ }
+
+ status = context->iface->ndr_push(call, call, push, call->r);
+ if (!NT_STATUS_IS_OK(status)) {
+ return dcesrv_fault(call, call->fault_code);
+ }
+
+ stub = ndr_push_blob(push);
+
+ total_length = stub.length;
+
+ /* we can write a full max_recv_frag size, minus the dcerpc
+ request header size */
+ chunk_size = call->conn->cli_max_recv_frag;
+ chunk_size -= DCERPC_REQUEST_LENGTH;
+ if (call->conn->auth_state.auth_info &&
+ call->conn->auth_state.gensec_security) {
+ sig_size = gensec_sig_size(call->conn->auth_state.gensec_security,
+ call->conn->cli_max_recv_frag);
+ if (sig_size) {
+ chunk_size -= DCERPC_AUTH_TRAILER_LENGTH;
+ chunk_size -= sig_size;
+ }
+ }
+ chunk_size -= (chunk_size % 16);
+
+ do {
+ uint32_t length;
+ struct data_blob_list_item *rep;
+ struct ncacn_packet pkt;
+
+ rep = talloc(call, struct data_blob_list_item);
+ NT_STATUS_HAVE_NO_MEMORY(rep);
+
+ length = MIN(chunk_size, stub.length);
+
+ /* form the dcerpc response packet */
+ dcesrv_init_hdr(&pkt, lp_rpc_big_endian(call->conn->dce_ctx->lp_ctx));
+ pkt.auth_length = 0;
+ pkt.call_id = call->pkt.call_id;
+ pkt.ptype = DCERPC_PKT_RESPONSE;
+ pkt.pfc_flags = 0;
+ if (stub.length == total_length) {
+ pkt.pfc_flags |= DCERPC_PFC_FLAG_FIRST;
+ }
+ if (length == stub.length) {
+ pkt.pfc_flags |= DCERPC_PFC_FLAG_LAST;
+ }
+ pkt.u.response.alloc_hint = stub.length;
+ pkt.u.response.context_id = call->pkt.u.request.context_id;
+ pkt.u.response.cancel_count = 0;
+ pkt.u.response.stub_and_verifier.data = stub.data;
+ pkt.u.response.stub_and_verifier.length = length;
+
+ if (!dcesrv_auth_response(call, &rep->blob, sig_size, &pkt)) {
+ return dcesrv_fault(call, DCERPC_FAULT_OTHER);
+ }
+
+ dcerpc_set_frag_length(&rep->blob, rep->blob.length);
+
+ DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
+
+ stub.data += length;
+ stub.length -= length;
+ } while (stub.length != 0);
+
+ /* move the call from the pending to the finished calls list */
+ dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
+
+ if (call->conn->call_list && call->conn->call_list->replies) {
+ if (call->conn->transport.report_output_data) {
+ call->conn->transport.report_output_data(call->conn);
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+_PUBLIC_ struct socket_address *dcesrv_connection_get_my_addr(struct dcesrv_connection *conn, TALLOC_CTX *mem_ctx)
+{
+ if (!conn->transport.get_my_addr) {
+ return NULL;
+ }
+
+ return conn->transport.get_my_addr(conn, mem_ctx);
+}
+
+_PUBLIC_ struct socket_address *dcesrv_connection_get_peer_addr(struct dcesrv_connection *conn, TALLOC_CTX *mem_ctx)
+{
+ if (!conn->transport.get_peer_addr) {
+ return NULL;
+ }
+
+ return conn->transport.get_peer_addr(conn, mem_ctx);
+}
+
+/*
+ work out if we have a full packet yet
+*/
+static bool dce_full_packet(const DATA_BLOB *data)
+{
+ if (data->length < DCERPC_FRAG_LEN_OFFSET+2) {
+ return false;
+ }
+ if (dcerpc_get_frag_length(data) > data->length) {
+ return false;
+ }
+ return true;
+}
+
+/*
+ we might have consumed only part of our input - advance past that part
+*/
+static void dce_partial_advance(struct dcesrv_connection *dce_conn, uint32_t offset)
+{
+ DATA_BLOB blob;
+
+ if (dce_conn->partial_input.length == offset) {
+ data_blob_free(&dce_conn->partial_input);
+ return;
+ }
+
+ blob = dce_conn->partial_input;
+ dce_conn->partial_input = data_blob(blob.data + offset,
+ blob.length - offset);
+ data_blob_free(&blob);
+}
+
+/*
+ remove the call from the right list when freed
+ */
+static int dcesrv_call_dequeue(struct dcesrv_call_state *call)
+{
+ dcesrv_call_set_list(call, DCESRV_LIST_NONE);
+ return 0;
+}
+
+/*
+ process some input to a dcerpc endpoint server.
+*/
+NTSTATUS dcesrv_input_process(struct dcesrv_connection *dce_conn)
+{
+ struct ndr_pull *ndr;
+ enum ndr_err_code ndr_err;
+ NTSTATUS status;
+ struct dcesrv_call_state *call;
+ DATA_BLOB blob;
+
+ call = talloc_zero(dce_conn, struct dcesrv_call_state);
+ if (!call) {
+ talloc_free(dce_conn->partial_input.data);
+ return NT_STATUS_NO_MEMORY;
+ }
+ call->conn = dce_conn;
+ call->event_ctx = dce_conn->event_ctx;
+ call->msg_ctx = dce_conn->msg_ctx;
+ call->state_flags = call->conn->state_flags;
+ call->time = timeval_current();
+ call->list = DCESRV_LIST_NONE;
+
+ talloc_set_destructor(call, dcesrv_call_dequeue);
+
+ blob = dce_conn->partial_input;
+ blob.length = dcerpc_get_frag_length(&blob);
+
+ ndr = ndr_pull_init_blob(&blob, call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx));
+ if (!ndr) {
+ talloc_free(dce_conn->partial_input.data);
+ talloc_free(call);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!(CVAL(blob.data, DCERPC_DREP_OFFSET) & DCERPC_DREP_LE)) {
+ ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
+ }
+
+ if (CVAL(blob.data, DCERPC_PFC_OFFSET) & DCERPC_PFC_FLAG_OBJECT_UUID) {
+ ndr->flags |= LIBNDR_FLAG_OBJECT_PRESENT;
+ }
+
+ ndr_err = ndr_pull_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, &call->pkt);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ talloc_free(dce_conn->partial_input.data);
+ talloc_free(call);
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ /* we have to check the signing here, before combining the
+ pdus */
+ if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
+ !dcesrv_auth_request(call, &blob)) {
+ dce_partial_advance(dce_conn, blob.length);
+ return dcesrv_fault(call, DCERPC_FAULT_ACCESS_DENIED);
+ }
+
+ dce_partial_advance(dce_conn, blob.length);
+
+ /* see if this is a continued packet */
+ if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
+ !(call->pkt.pfc_flags & DCERPC_PFC_FLAG_FIRST)) {
+ struct dcesrv_call_state *call2 = call;
+ uint32_t alloc_size;
+
+ /* we only allow fragmented requests, no other packet types */
+ if (call->pkt.ptype != DCERPC_PKT_REQUEST) {
+ return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
+ }
+
+ /* this is a continuation of an existing call - find the call then
+ tack it on the end */
+ call = dcesrv_find_fragmented_call(dce_conn, call2->pkt.call_id);
+ if (!call) {
+ return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
+ }
+
+ if (call->pkt.ptype != call2->pkt.ptype) {
+ /* trying to play silly buggers are we? */
+ return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
+ }
+
+ alloc_size = call->pkt.u.request.stub_and_verifier.length +
+ call2->pkt.u.request.stub_and_verifier.length;
+ if (call->pkt.u.request.alloc_hint > alloc_size) {
+ alloc_size = call->pkt.u.request.alloc_hint;
+ }
+
+ call->pkt.u.request.stub_and_verifier.data =
+ talloc_realloc(call,
+ call->pkt.u.request.stub_and_verifier.data,
+ uint8_t, alloc_size);
+ if (!call->pkt.u.request.stub_and_verifier.data) {
+ return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
+ }
+ memcpy(call->pkt.u.request.stub_and_verifier.data +
+ call->pkt.u.request.stub_and_verifier.length,
+ call2->pkt.u.request.stub_and_verifier.data,
+ call2->pkt.u.request.stub_and_verifier.length);
+ call->pkt.u.request.stub_and_verifier.length +=
+ call2->pkt.u.request.stub_and_verifier.length;
+
+ call->pkt.pfc_flags |= (call2->pkt.pfc_flags & DCERPC_PFC_FLAG_LAST);
+
+ talloc_free(call2);
+ }
+
+ /* this may not be the last pdu in the chain - if its isn't then
+ just put it on the incoming_fragmented_call_list and wait for the rest */
+ if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
+ !(call->pkt.pfc_flags & DCERPC_PFC_FLAG_LAST)) {
+ dcesrv_call_set_list(call, DCESRV_LIST_FRAGMENTED_CALL_LIST);
+ return NT_STATUS_OK;
+ }
+
+ /* This removes any fragments we may have had stashed away */
+ dcesrv_call_set_list(call, DCESRV_LIST_NONE);
+
+ switch (call->pkt.ptype) {
+ case DCERPC_PKT_BIND:
+ status = dcesrv_bind(call);
+ break;
+ case DCERPC_PKT_AUTH3:
+ status = dcesrv_auth3(call);
+ break;
+ case DCERPC_PKT_ALTER:
+ status = dcesrv_alter(call);
+ break;
+ case DCERPC_PKT_REQUEST:
+ status = dcesrv_request(call);
+ break;
+ default:
+ status = NT_STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ /* if we are going to be sending a reply then add
+ it to the list of pending calls. We add it to the end to keep the call
+ list in the order we will answer */
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(call);
+ }
+
+ return status;
+}
+
+
+/*
+ provide some input to a dcerpc endpoint server. This passes data
+ from a dcerpc client into the server
+*/
+_PUBLIC_ NTSTATUS dcesrv_input(struct dcesrv_connection *dce_conn, const DATA_BLOB *data)
+{
+ NTSTATUS status;
+
+ dce_conn->partial_input.data = talloc_realloc(dce_conn,
+ dce_conn->partial_input.data,
+ uint8_t,
+ dce_conn->partial_input.length + data->length);
+ if (!dce_conn->partial_input.data) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ memcpy(dce_conn->partial_input.data + dce_conn->partial_input.length,
+ data->data, data->length);
+ dce_conn->partial_input.length += data->length;
+
+ while (dce_full_packet(&dce_conn->partial_input)) {
+ status = dcesrv_input_process(dce_conn);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ retrieve some output from a dcerpc server
+ The caller supplies a function that will be called to do the
+ actual output.
+
+ The first argument to write_fn() will be 'private', the second will
+ be a pointer to a buffer containing the data to be sent and the 3rd
+ will be a pointer to a size_t variable that will be set to the
+ number of bytes that are consumed from the output.
+
+ from the current fragment
+*/
+_PUBLIC_ NTSTATUS dcesrv_output(struct dcesrv_connection *dce_conn,
+ void *private_data,
+ NTSTATUS (*write_fn)(void *private_data, DATA_BLOB *output, size_t *nwritten))
+{
+ NTSTATUS status;
+ struct dcesrv_call_state *call;
+ struct data_blob_list_item *rep;
+ size_t nwritten;
+
+ call = dce_conn->call_list;
+ if (!call || !call->replies) {
+ if (dce_conn->pending_call_list) {
+ /* TODO: we need to say act async here
+ * as we know we have pending requests
+ * which will be finished at a time
+ */
+ return NT_STATUS_FOOBAR;
+ }
+ return NT_STATUS_FOOBAR;
+ }
+ rep = call->replies;
+
+ status = write_fn(private_data, &rep->blob, &nwritten);
+ NT_STATUS_IS_ERR_RETURN(status);
+
+ rep->blob.length -= nwritten;
+ rep->blob.data += nwritten;
+
+ if (rep->blob.length == 0) {
+ /* we're done with this section of the call */
+ DLIST_REMOVE(call->replies, rep);
+ }
+
+ if (call->replies == NULL) {
+ /* we're done with the whole call */
+ dcesrv_call_set_list(call, DCESRV_LIST_NONE);
+ talloc_free(call);
+ }
+
+ return status;
+}
+
+_PUBLIC_ NTSTATUS dcesrv_init_context(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ const char **endpoint_servers, struct dcesrv_context **_dce_ctx)
+{
+ NTSTATUS status;
+ struct dcesrv_context *dce_ctx;
+ int i;
+
+ if (!endpoint_servers) {
+ DEBUG(0,("dcesrv_init_context: no endpoint servers configured\n"));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ dce_ctx = talloc(mem_ctx, struct dcesrv_context);
+ NT_STATUS_HAVE_NO_MEMORY(dce_ctx);
+ dce_ctx->endpoint_list = NULL;
+ dce_ctx->lp_ctx = lp_ctx;
+
+ for (i=0;endpoint_servers[i];i++) {
+ const struct dcesrv_endpoint_server *ep_server;
+
+ ep_server = dcesrv_ep_server_byname(endpoint_servers[i]);
+ if (!ep_server) {
+ DEBUG(0,("dcesrv_init_context: failed to find endpoint server = '%s'\n", endpoint_servers[i]));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ status = ep_server->init_server(dce_ctx, ep_server);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("dcesrv_init_context: failed to init endpoint server = '%s': %s\n", endpoint_servers[i],
+ nt_errstr(status)));
+ return status;
+ }
+ }
+
+ *_dce_ctx = dce_ctx;
+ return NT_STATUS_OK;
+}
+
+/* the list of currently registered DCERPC endpoint servers.
+ */
+static struct ep_server {
+ struct dcesrv_endpoint_server *ep_server;
+} *ep_servers = NULL;
+static int num_ep_servers;
+
+/*
+ register a DCERPC endpoint server.
+
+ The 'name' can be later used by other backends to find the operations
+ structure for this backend.
+
+ The 'type' is used to specify whether this is for a disk, printer or IPC$ share
+*/
+_PUBLIC_ NTSTATUS dcerpc_register_ep_server(const void *_ep_server)
+{
+ const struct dcesrv_endpoint_server *ep_server = _ep_server;
+
+ if (dcesrv_ep_server_byname(ep_server->name) != NULL) {
+ /* its already registered! */
+ DEBUG(0,("DCERPC endpoint server '%s' already registered\n",
+ ep_server->name));
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ ep_servers = realloc_p(ep_servers, struct ep_server, num_ep_servers+1);
+ if (!ep_servers) {
+ smb_panic("out of memory in dcerpc_register");
+ }
+
+ ep_servers[num_ep_servers].ep_server = smb_xmemdup(ep_server, sizeof(*ep_server));
+ ep_servers[num_ep_servers].ep_server->name = smb_xstrdup(ep_server->name);
+
+ num_ep_servers++;
+
+ DEBUG(3,("DCERPC endpoint server '%s' registered\n",
+ ep_server->name));
+
+ return NT_STATUS_OK;
+}
+
+/*
+ return the operations structure for a named backend of the specified type
+*/
+const struct dcesrv_endpoint_server *dcesrv_ep_server_byname(const char *name)
+{
+ int i;
+
+ for (i=0;i<num_ep_servers;i++) {
+ if (strcmp(ep_servers[i].ep_server->name, name) == 0) {
+ return ep_servers[i].ep_server;
+ }
+ }
+
+ return NULL;
+}
+
+void dcerpc_server_init(struct loadparm_context *lp_ctx)
+{
+ static bool initialized;
+ extern NTSTATUS dcerpc_server_wkssvc_init(void);
+ extern NTSTATUS dcerpc_server_drsuapi_init(void);
+ extern NTSTATUS dcerpc_server_winreg_init(void);
+ extern NTSTATUS dcerpc_server_spoolss_init(void);
+ extern NTSTATUS dcerpc_server_epmapper_init(void);
+ extern NTSTATUS dcerpc_server_srvsvc_init(void);
+ extern NTSTATUS dcerpc_server_netlogon_init(void);
+ extern NTSTATUS dcerpc_server_rpcecho_init(void);
+ extern NTSTATUS dcerpc_server_unixinfo_init(void);
+ extern NTSTATUS dcerpc_server_samr_init(void);
+ extern NTSTATUS dcerpc_server_remote_init(void);
+ extern NTSTATUS dcerpc_server_lsa_init(void);
+ extern NTSTATUS dcerpc_server_browser_init(void);
+ init_module_fn static_init[] = { STATIC_dcerpc_server_MODULES };
+ init_module_fn *shared_init;
+
+ if (initialized) {
+ return;
+ }
+ initialized = true;
+
+ shared_init = load_samba_modules(NULL, lp_ctx, "dcerpc_server");
+
+ run_init_functions(static_init);
+ run_init_functions(shared_init);
+
+ talloc_free(shared_init);
+}
+
+/*
+ return the DCERPC module version, and the size of some critical types
+ This can be used by endpoint server modules to either detect compilation errors, or provide
+ multiple implementations for different smbd compilation options in one module
+*/
+const struct dcesrv_critical_sizes *dcerpc_module_version(void)
+{
+ static const struct dcesrv_critical_sizes critical_sizes = {
+ DCERPC_MODULE_VERSION,
+ sizeof(struct dcesrv_context),
+ sizeof(struct dcesrv_endpoint),
+ sizeof(struct dcesrv_endpoint_server),
+ sizeof(struct dcesrv_interface),
+ sizeof(struct dcesrv_if_list),
+ sizeof(struct dcesrv_connection),
+ sizeof(struct dcesrv_call_state),
+ sizeof(struct dcesrv_auth),
+ sizeof(struct dcesrv_handle)
+ };
+
+ return &critical_sizes;
+}
+
+/*
+ initialise the dcerpc server context for ncacn_np based services
+*/
+_PUBLIC_ NTSTATUS dcesrv_init_ipc_context(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx,
+ struct dcesrv_context **_dce_ctx)
+{
+ NTSTATUS status;
+ struct dcesrv_context *dce_ctx;
+
+ dcerpc_server_init(lp_ctx);
+
+ status = dcesrv_init_context(mem_ctx, lp_ctx, lp_dcerpc_endpoint_servers(lp_ctx), &dce_ctx);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ *_dce_ctx = dce_ctx;
+ return NT_STATUS_OK;
+}
+
+
diff --git a/source4/rpc_server/dcerpc_server.h b/source4/rpc_server/dcerpc_server.h
new file mode 100644
index 0000000000..7e12a3840b
--- /dev/null
+++ b/source4/rpc_server/dcerpc_server.h
@@ -0,0 +1,376 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ server side dcerpc defines
+
+ Copyright (C) Andrew Tridgell 2003-2005
+ Copyright (C) Stefan (metze) Metzmacher 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef SAMBA_DCERPC_SERVER_H
+#define SAMBA_DCERPC_SERVER_H
+
+#include "librpc/gen_ndr/server_id.h"
+#include "librpc/rpc/dcerpc.h"
+#include "librpc/ndr/libndr.h"
+
+/* modules can use the following to determine if the interface has changed
+ * please increment the version number after each interface change
+ * with a comment and maybe update struct dcesrv_critical_sizes.
+ */
+/* version 1 - initial version - metze */
+#define DCERPC_MODULE_VERSION 1
+
+struct dcesrv_connection;
+struct dcesrv_call_state;
+struct dcesrv_auth;
+struct dcesrv_connection_context;
+
+struct dcesrv_interface {
+ const char *name;
+ struct ndr_syntax_id syntax_id;
+
+ /* this function is called when the client binds to this interface */
+ NTSTATUS (*bind)(struct dcesrv_call_state *, const struct dcesrv_interface *);
+
+ /* this function is called when the client disconnects the endpoint */
+ void (*unbind)(struct dcesrv_connection_context *, const struct dcesrv_interface *);
+
+ /* the ndr_pull function for the chosen interface.
+ */
+ NTSTATUS (*ndr_pull)(struct dcesrv_call_state *, TALLOC_CTX *, struct ndr_pull *, void **);
+
+ /* the dispatch function for the chosen interface.
+ */
+ NTSTATUS (*dispatch)(struct dcesrv_call_state *, TALLOC_CTX *, void *);
+
+ /* the reply function for the chosen interface.
+ */
+ NTSTATUS (*reply)(struct dcesrv_call_state *, TALLOC_CTX *, void *);
+
+ /* the ndr_push function for the chosen interface.
+ */
+ NTSTATUS (*ndr_push)(struct dcesrv_call_state *, TALLOC_CTX *, struct ndr_push *, const void *);
+
+ /* for any private use by the interface code */
+ const void *private_data;
+};
+
+enum dcesrv_call_list {
+ DCESRV_LIST_NONE,
+ DCESRV_LIST_CALL_LIST,
+ DCESRV_LIST_FRAGMENTED_CALL_LIST,
+ DCESRV_LIST_PENDING_CALL_LIST
+};
+
+/* the state of an ongoing dcerpc call */
+struct dcesrv_call_state {
+ struct dcesrv_call_state *next, *prev;
+ struct dcesrv_connection *conn;
+ struct dcesrv_connection_context *context;
+ struct ncacn_packet pkt;
+
+ /*
+ which list this request is in, if any
+ */
+ enum dcesrv_call_list list;
+
+ /* the backend can mark the call
+ * with DCESRV_CALL_STATE_FLAG_ASYNC
+ * that will cause the frontend to not touch r->out
+ * and skip the reply
+ *
+ * this is only allowed to the backend when DCESRV_CALL_STATE_FLAG_MAY_ASYNC
+ * is alerady set by the frontend
+ *
+ * the backend then needs to call dcesrv_reply() when it's
+ * ready to send the reply
+ */
+#define DCESRV_CALL_STATE_FLAG_ASYNC (1<<0)
+#define DCESRV_CALL_STATE_FLAG_MAY_ASYNC (1<<1)
+#define DCESRV_CALL_STATE_FLAG_HEADER_SIGNING (1<<2)
+ uint32_t state_flags;
+
+ /* the time the request arrived in the server */
+ struct timeval time;
+
+ /* the backend can use this event context for async replies */
+ struct tevent_context *event_ctx;
+
+ /* the message_context that will be used for async replies */
+ struct messaging_context *msg_ctx;
+
+ /* this is the pointer to the allocated function struct */
+ void *r;
+
+ /*
+ * that's the ndr pull context used in dcesrv_request()
+ * needed by dcesrv_reply() to carry over information
+ * for full pointer support.
+ */
+ struct ndr_pull *ndr_pull;
+
+ DATA_BLOB input;
+
+ struct data_blob_list_item *replies;
+
+ /* this is used by the boilerplate code to generate DCERPC faults */
+ uint32_t fault_code;
+};
+
+#define DCESRV_HANDLE_ANY 255
+
+/* a dcerpc handle in internal format */
+struct dcesrv_handle {
+ struct dcesrv_handle *next, *prev;
+ struct dcesrv_connection_context *context;
+ struct policy_handle wire_handle;
+ void *data;
+};
+
+/* hold the authentication state information */
+struct dcesrv_auth {
+ struct dcerpc_auth *auth_info;
+ struct gensec_security *gensec_security;
+ struct auth_session_info *session_info;
+ NTSTATUS (*session_key)(struct dcesrv_connection *, DATA_BLOB *session_key);
+};
+
+struct dcesrv_connection_context {
+ struct dcesrv_connection_context *next, *prev;
+ uint32_t context_id;
+
+ uint32_t assoc_group_id;
+
+ /* the connection this is on */
+ struct dcesrv_connection *conn;
+
+ /* the ndr function table for the chosen interface */
+ const struct dcesrv_interface *iface;
+
+ /* private data for the interface implementation */
+ void *private_data;
+
+ /* current rpc handles - this is really the wrong scope for
+ them, but it will do for now */
+ struct dcesrv_handle *handles;
+};
+
+
+/* the state associated with a dcerpc server connection */
+struct dcesrv_connection {
+ /* the top level context for this server */
+ struct dcesrv_context *dce_ctx;
+
+ /* the endpoint that was opened */
+ const struct dcesrv_endpoint *endpoint;
+
+ /* a list of established context_ids */
+ struct dcesrv_connection_context *contexts;
+
+ /* the state of the current incoming call fragments */
+ struct dcesrv_call_state *incoming_fragmented_call_list;
+
+ /* the state of the async pending calls */
+ struct dcesrv_call_state *pending_call_list;
+
+ /* the state of the current outgoing calls */
+ struct dcesrv_call_state *call_list;
+
+ /* the maximum size the client wants to receive */
+ uint32_t cli_max_recv_frag;
+
+ DATA_BLOB partial_input;
+
+ /* the current authentication state */
+ struct dcesrv_auth auth_state;
+
+ /* the event_context that will be used for this connection */
+ struct tevent_context *event_ctx;
+
+ /* the message_context that will be used for this connection */
+ struct messaging_context *msg_ctx;
+
+ /* the server_id that will be used for this connection */
+ struct server_id server_id;
+
+ /* the transport level session key */
+ DATA_BLOB transport_session_key;
+
+ bool processing;
+
+ const char *packet_log_dir;
+
+ /* this is the default state_flags for dcesrv_call_state structs */
+ uint32_t state_flags;
+
+ struct {
+ void *private_data;
+ void (*report_output_data)(struct dcesrv_connection *);
+ struct socket_address *(*get_my_addr)(struct dcesrv_connection *, TALLOC_CTX *mem_ctx);
+ struct socket_address *(*get_peer_addr)(struct dcesrv_connection *, TALLOC_CTX *mem_ctx);
+ } transport;
+};
+
+
+struct dcesrv_endpoint_server {
+ /* this is the name of the endpoint server */
+ const char *name;
+
+ /* this function should register endpoints and some other setup stuff,
+ * it is called when the dcesrv_context gets initialized.
+ */
+ NTSTATUS (*init_server)(struct dcesrv_context *, const struct dcesrv_endpoint_server *);
+
+ /* this function can be used by other endpoint servers to
+ * ask for a dcesrv_interface implementation
+ * - iface must be reference to an already existing struct !
+ */
+ bool (*interface_by_uuid)(struct dcesrv_interface *iface, const struct GUID *, uint32_t);
+
+ /* this function can be used by other endpoint servers to
+ * ask for a dcesrv_interface implementation
+ * - iface must be reference to an already existeng struct !
+ */
+ bool (*interface_by_name)(struct dcesrv_interface *iface, const char *);
+};
+
+
+/* server-wide context information for the dcerpc server */
+struct dcesrv_context {
+ /* the list of endpoints that have registered
+ * by the configured endpoint servers
+ */
+ struct dcesrv_endpoint {
+ struct dcesrv_endpoint *next, *prev;
+ /* the type and location of the endpoint */
+ struct dcerpc_binding *ep_description;
+ /* the security descriptor for smb named pipes */
+ struct security_descriptor *sd;
+ /* the list of interfaces available on this endpoint */
+ struct dcesrv_if_list {
+ struct dcesrv_if_list *next, *prev;
+ struct dcesrv_interface iface;
+ } *interface_list;
+ } *endpoint_list;
+
+ /* loadparm context to use for this connection */
+ struct loadparm_context *lp_ctx;
+};
+
+/* this structure is used by modules to determine the size of some critical types */
+struct dcesrv_critical_sizes {
+ int interface_version;
+ int sizeof_dcesrv_context;
+ int sizeof_dcesrv_endpoint;
+ int sizeof_dcesrv_endpoint_server;
+ int sizeof_dcesrv_interface;
+ int sizeof_dcesrv_if_list;
+ int sizeof_dcesrv_connection;
+ int sizeof_dcesrv_call_state;
+ int sizeof_dcesrv_auth;
+ int sizeof_dcesrv_handle;
+};
+
+struct model_ops;
+
+NTSTATUS dcesrv_interface_register(struct dcesrv_context *dce_ctx,
+ const char *ep_name,
+ const struct dcesrv_interface *iface,
+ const struct security_descriptor *sd);
+NTSTATUS dcerpc_register_ep_server(const void *_ep_server);
+NTSTATUS dcesrv_init_context(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ const char **endpoint_servers, struct dcesrv_context **_dce_ctx);
+NTSTATUS dcesrv_init_ipc_context(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx,
+ struct dcesrv_context **_dce_ctx);
+NTSTATUS dcesrv_endpoint_search_connect(struct dcesrv_context *dce_ctx,
+ TALLOC_CTX *mem_ctx,
+ const struct dcerpc_binding *ep_description,
+ struct auth_session_info *session_info,
+ struct tevent_context *event_ctx,
+ struct messaging_context *msg_ctx,
+ struct server_id server_id,
+ uint32_t state_flags,
+ struct dcesrv_connection **dce_conn_p);
+NTSTATUS dcesrv_output(struct dcesrv_connection *dce_conn,
+ void *private_data,
+ NTSTATUS (*write_fn)(void *private_data, DATA_BLOB *output, size_t *nwritten));
+NTSTATUS dcesrv_input(struct dcesrv_connection *dce_conn, const DATA_BLOB *data);
+NTSTATUS dcesrv_endpoint_connect(struct dcesrv_context *dce_ctx,
+ TALLOC_CTX *mem_ctx,
+ const struct dcesrv_endpoint *ep,
+ struct auth_session_info *session_info,
+ struct tevent_context *event_ctx,
+ struct messaging_context *msg_ctx,
+ struct server_id server_id,
+ uint32_t state_flags,
+ struct dcesrv_connection **_p);
+
+NTSTATUS dcesrv_reply(struct dcesrv_call_state *call);
+struct dcesrv_handle *dcesrv_handle_new(struct dcesrv_connection_context *context,
+ uint8_t handle_type);
+
+struct dcesrv_handle *dcesrv_handle_fetch(
+ struct dcesrv_connection_context *context,
+ struct policy_handle *p,
+ uint8_t handle_type);
+struct socket_address *dcesrv_connection_get_my_addr(struct dcesrv_connection *conn, TALLOC_CTX *mem_ctx);
+
+struct socket_address *dcesrv_connection_get_peer_addr(struct dcesrv_connection *conn, TALLOC_CTX *mem_ctx);
+
+NTSTATUS dcesrv_fetch_session_key(struct dcesrv_connection *p, DATA_BLOB *session_key);
+
+/* a useful macro for generating a RPC fault in the backend code */
+#define DCESRV_FAULT(code) do { \
+ dce_call->fault_code = code; \
+ return r->out.result; \
+} while(0)
+
+/* a useful macro for generating a RPC fault in the backend code */
+#define DCESRV_FAULT_VOID(code) do { \
+ dce_call->fault_code = code; \
+ return; \
+} while(0)
+
+/* a useful macro for checking the validity of a dcerpc policy handle
+ and giving the right fault code if invalid */
+#define DCESRV_CHECK_HANDLE(h) do {if (!(h)) DCESRV_FAULT(DCERPC_FAULT_CONTEXT_MISMATCH); } while (0)
+
+/* this checks for a valid policy handle, and gives a fault if an
+ invalid handle or retval if the handle is of the
+ wrong type */
+#define DCESRV_PULL_HANDLE_RETVAL(h, inhandle, t, retval) do { \
+ (h) = dcesrv_handle_fetch(dce_call->context, (inhandle), DCESRV_HANDLE_ANY); \
+ DCESRV_CHECK_HANDLE(h); \
+ if ((t) != DCESRV_HANDLE_ANY && (h)->wire_handle.handle_type != (t)) { \
+ return retval; \
+ } \
+} while (0)
+
+/* this checks for a valid policy handle and gives a dcerpc fault
+ if its the wrong type of handle */
+#define DCESRV_PULL_HANDLE_FAULT(h, inhandle, t) do { \
+ (h) = dcesrv_handle_fetch(dce_call->context, (inhandle), t); \
+ DCESRV_CHECK_HANDLE(h); \
+} while (0)
+
+#define DCESRV_PULL_HANDLE(h, inhandle, t) DCESRV_PULL_HANDLE_RETVAL(h, inhandle, t, NT_STATUS_INVALID_HANDLE)
+#define DCESRV_PULL_HANDLE_WERR(h, inhandle, t) DCESRV_PULL_HANDLE_RETVAL(h, inhandle, t, WERR_BADFID)
+
+
+
+#endif /* SAMBA_DCERPC_SERVER_H */
diff --git a/source4/rpc_server/dcesrv_auth.c b/source4/rpc_server/dcesrv_auth.c
new file mode 100644
index 0000000000..e2e3b77948
--- /dev/null
+++ b/source4/rpc_server/dcesrv_auth.c
@@ -0,0 +1,539 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ server side dcerpc authentication code
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Stefan (metze) Metzmacher 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "rpc_server/dcerpc_server.h"
+#include "rpc_server/dcerpc_server_proto.h"
+#include "librpc/rpc/dcerpc_proto.h"
+#include "librpc/gen_ndr/ndr_dcerpc.h"
+#include "auth/credentials/credentials.h"
+#include "auth/gensec/gensec.h"
+#include "auth/auth.h"
+#include "param/param.h"
+
+/*
+ parse any auth information from a dcerpc bind request
+ return false if we can't handle the auth request for some
+ reason (in which case we send a bind_nak)
+*/
+bool dcesrv_auth_bind(struct dcesrv_call_state *call)
+{
+ struct cli_credentials *server_credentials;
+ struct ncacn_packet *pkt = &call->pkt;
+ struct dcesrv_connection *dce_conn = call->conn;
+ struct dcesrv_auth *auth = &dce_conn->auth_state;
+ NTSTATUS status;
+ enum ndr_err_code ndr_err;
+
+ if (pkt->u.bind.auth_info.length == 0) {
+ dce_conn->auth_state.auth_info = NULL;
+ return true;
+ }
+
+ dce_conn->auth_state.auth_info = talloc(dce_conn, struct dcerpc_auth);
+ if (!dce_conn->auth_state.auth_info) {
+ return false;
+ }
+
+ ndr_err = ndr_pull_struct_blob(&pkt->u.bind.auth_info,
+ call, NULL,
+ dce_conn->auth_state.auth_info,
+ (ndr_pull_flags_fn_t)ndr_pull_dcerpc_auth);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return false;
+ }
+
+ server_credentials
+ = cli_credentials_init(call);
+ if (!server_credentials) {
+ DEBUG(1, ("Failed to init server credentials\n"));
+ return false;
+ }
+
+ cli_credentials_set_conf(server_credentials, call->conn->dce_ctx->lp_ctx);
+ status = cli_credentials_set_machine_account(server_credentials, call->conn->dce_ctx->lp_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10, ("Failed to obtain server credentials, perhaps a standalone server?: %s\n", nt_errstr(status)));
+ talloc_free(server_credentials);
+ server_credentials = NULL;
+ }
+
+ status = samba_server_gensec_start(dce_conn, call->event_ctx,
+ call->msg_ctx,
+ call->conn->dce_ctx->lp_ctx,
+ server_credentials,
+ NULL,
+ &auth->gensec_security);
+
+ status = gensec_start_mech_by_authtype(auth->gensec_security, auth->auth_info->auth_type,
+ auth->auth_info->auth_level);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to start GENSEC mechanism for DCERPC server: auth_type=%d, auth_level=%d: %s\n",
+ (int)auth->auth_info->auth_type,
+ (int)auth->auth_info->auth_level,
+ nt_errstr(status)));
+ return false;
+ }
+
+ if (call->conn->state_flags & DCESRV_CALL_STATE_FLAG_HEADER_SIGNING) {
+ gensec_want_feature(auth->gensec_security, GENSEC_FEATURE_SIGN_PKT_HEADER);
+ }
+
+ return true;
+}
+
+/*
+ add any auth information needed in a bind ack, and process the authentication
+ information found in the bind.
+*/
+NTSTATUS dcesrv_auth_bind_ack(struct dcesrv_call_state *call, struct ncacn_packet *pkt)
+{
+ struct dcesrv_connection *dce_conn = call->conn;
+ NTSTATUS status;
+
+ if (!call->conn->auth_state.gensec_security) {
+ return NT_STATUS_OK;
+ }
+
+ status = gensec_update(dce_conn->auth_state.gensec_security,
+ call,
+ dce_conn->auth_state.auth_info->credentials,
+ &dce_conn->auth_state.auth_info->credentials);
+
+ if (NT_STATUS_IS_OK(status)) {
+ status = gensec_session_info(dce_conn->auth_state.gensec_security,
+ &dce_conn->auth_state.session_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to establish session_info: %s\n", nt_errstr(status)));
+ return status;
+ }
+
+ if (dce_conn->state_flags & DCESRV_CALL_STATE_FLAG_HEADER_SIGNING) {
+ gensec_want_feature(dce_conn->auth_state.gensec_security,
+ GENSEC_FEATURE_SIGN_PKT_HEADER);
+ }
+
+ /* Now that we are authenticated, go back to the generic session key... */
+ dce_conn->auth_state.session_key = dcesrv_generic_session_key;
+ return NT_STATUS_OK;
+ } else if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ dce_conn->auth_state.auth_info->auth_pad_length = 0;
+ dce_conn->auth_state.auth_info->auth_reserved = 0;
+ return NT_STATUS_OK;
+ } else {
+ DEBUG(2, ("Failed to start dcesrv auth negotiate: %s\n", nt_errstr(status)));
+ return status;
+ }
+}
+
+
+/*
+ process the final stage of a auth request
+*/
+bool dcesrv_auth_auth3(struct dcesrv_call_state *call)
+{
+ struct ncacn_packet *pkt = &call->pkt;
+ struct dcesrv_connection *dce_conn = call->conn;
+ NTSTATUS status;
+ enum ndr_err_code ndr_err;
+
+ /* We can't work without an existing gensec state, and an new blob to feed it */
+ if (!dce_conn->auth_state.auth_info ||
+ !dce_conn->auth_state.gensec_security ||
+ pkt->u.auth3.auth_info.length == 0) {
+ return false;
+ }
+
+ ndr_err = ndr_pull_struct_blob(&pkt->u.auth3.auth_info,
+ call, NULL,
+ dce_conn->auth_state.auth_info,
+ (ndr_pull_flags_fn_t)ndr_pull_dcerpc_auth);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return false;
+ }
+
+ /* Pass the extra data we got from the client down to gensec for processing */
+ status = gensec_update(dce_conn->auth_state.gensec_security,
+ call,
+ dce_conn->auth_state.auth_info->credentials,
+ &dce_conn->auth_state.auth_info->credentials);
+ if (NT_STATUS_IS_OK(status)) {
+ status = gensec_session_info(dce_conn->auth_state.gensec_security,
+ &dce_conn->auth_state.session_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to establish session_info: %s\n", nt_errstr(status)));
+ return false;
+ }
+ /* Now that we are authenticated, go back to the generic session key... */
+ dce_conn->auth_state.session_key = dcesrv_generic_session_key;
+ return true;
+ } else {
+ DEBUG(4, ("dcesrv_auth_auth3: failed to authenticate: %s\n",
+ nt_errstr(status)));
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ parse any auth information from a dcerpc alter request
+ return false if we can't handle the auth request for some
+ reason (in which case we send a bind_nak (is this true for here?))
+*/
+bool dcesrv_auth_alter(struct dcesrv_call_state *call)
+{
+ struct ncacn_packet *pkt = &call->pkt;
+ struct dcesrv_connection *dce_conn = call->conn;
+ enum ndr_err_code ndr_err;
+
+ /* on a pure interface change there is no auth blob */
+ if (pkt->u.alter.auth_info.length == 0) {
+ return true;
+ }
+
+ /* We can't work without an existing gensec state */
+ if (!dce_conn->auth_state.gensec_security) {
+ return false;
+ }
+
+ dce_conn->auth_state.auth_info = talloc(dce_conn, struct dcerpc_auth);
+ if (!dce_conn->auth_state.auth_info) {
+ return false;
+ }
+
+ ndr_err = ndr_pull_struct_blob(&pkt->u.alter.auth_info,
+ call, NULL,
+ dce_conn->auth_state.auth_info,
+ (ndr_pull_flags_fn_t)ndr_pull_dcerpc_auth);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ add any auth information needed in a alter ack, and process the authentication
+ information found in the alter.
+*/
+NTSTATUS dcesrv_auth_alter_ack(struct dcesrv_call_state *call, struct ncacn_packet *pkt)
+{
+ struct dcesrv_connection *dce_conn = call->conn;
+ NTSTATUS status;
+
+ /* on a pure interface change there is no auth_info structure
+ setup */
+ if (!call->conn->auth_state.auth_info ||
+ dce_conn->auth_state.auth_info->credentials.length == 0) {
+ return NT_STATUS_OK;
+ }
+
+ if (!call->conn->auth_state.gensec_security) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = gensec_update(dce_conn->auth_state.gensec_security,
+ call,
+ dce_conn->auth_state.auth_info->credentials,
+ &dce_conn->auth_state.auth_info->credentials);
+
+ if (NT_STATUS_IS_OK(status)) {
+ status = gensec_session_info(dce_conn->auth_state.gensec_security,
+ &dce_conn->auth_state.session_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to establish session_info: %s\n", nt_errstr(status)));
+ return status;
+ }
+
+ /* Now that we are authenticated, got back to the generic session key... */
+ dce_conn->auth_state.session_key = dcesrv_generic_session_key;
+ return NT_STATUS_OK;
+ } else if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ dce_conn->auth_state.auth_info->auth_pad_length = 0;
+ dce_conn->auth_state.auth_info->auth_reserved = 0;
+ return NT_STATUS_OK;
+ }
+
+ DEBUG(2, ("Failed to finish dcesrv auth alter_ack: %s\n", nt_errstr(status)));
+ return status;
+}
+
+/*
+ check credentials on a request
+*/
+bool dcesrv_auth_request(struct dcesrv_call_state *call, DATA_BLOB *full_packet)
+{
+ struct ncacn_packet *pkt = &call->pkt;
+ struct dcesrv_connection *dce_conn = call->conn;
+ DATA_BLOB auth_blob;
+ struct dcerpc_auth auth;
+ struct ndr_pull *ndr;
+ NTSTATUS status;
+ enum ndr_err_code ndr_err;
+ size_t hdr_size = DCERPC_REQUEST_LENGTH;
+
+ if (!dce_conn->auth_state.auth_info ||
+ !dce_conn->auth_state.gensec_security) {
+ return true;
+ }
+
+ switch (dce_conn->auth_state.auth_info->auth_level) {
+ case DCERPC_AUTH_LEVEL_PRIVACY:
+ case DCERPC_AUTH_LEVEL_INTEGRITY:
+ break;
+
+ case DCERPC_AUTH_LEVEL_CONNECT:
+ if (pkt->auth_length != 0) {
+ break;
+ }
+ return true;
+ case DCERPC_AUTH_LEVEL_NONE:
+ if (pkt->auth_length != 0) {
+ return false;
+ }
+ return true;
+
+ default:
+ return false;
+ }
+
+ auth_blob.length = 8 + pkt->auth_length;
+
+ /* check for a valid length */
+ if (pkt->u.request.stub_and_verifier.length < auth_blob.length) {
+ return false;
+ }
+
+ auth_blob.data =
+ pkt->u.request.stub_and_verifier.data +
+ pkt->u.request.stub_and_verifier.length - auth_blob.length;
+ pkt->u.request.stub_and_verifier.length -= auth_blob.length;
+
+ /* pull the auth structure */
+ ndr = ndr_pull_init_blob(&auth_blob, call, lp_iconv_convenience(call->conn->dce_ctx->lp_ctx));
+ if (!ndr) {
+ return false;
+ }
+
+ if (!(pkt->drep[0] & DCERPC_DREP_LE)) {
+ ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
+ }
+
+ if (pkt->pfc_flags & DCERPC_PFC_FLAG_OBJECT_UUID) {
+ ndr->flags |= LIBNDR_FLAG_OBJECT_PRESENT;
+ hdr_size += 16;
+ }
+
+ ndr_err = ndr_pull_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, &auth);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ talloc_free(ndr);
+ return false;
+ }
+
+ /* check signature or unseal the packet */
+ switch (dce_conn->auth_state.auth_info->auth_level) {
+ case DCERPC_AUTH_LEVEL_PRIVACY:
+ status = gensec_unseal_packet(dce_conn->auth_state.gensec_security,
+ call,
+ full_packet->data + hdr_size,
+ pkt->u.request.stub_and_verifier.length,
+ full_packet->data,
+ full_packet->length-auth.credentials.length,
+ &auth.credentials);
+ memcpy(pkt->u.request.stub_and_verifier.data,
+ full_packet->data + hdr_size,
+ pkt->u.request.stub_and_verifier.length);
+ break;
+
+ case DCERPC_AUTH_LEVEL_INTEGRITY:
+ status = gensec_check_packet(dce_conn->auth_state.gensec_security,
+ call,
+ pkt->u.request.stub_and_verifier.data,
+ pkt->u.request.stub_and_verifier.length,
+ full_packet->data,
+ full_packet->length-auth.credentials.length,
+ &auth.credentials);
+ break;
+
+ case DCERPC_AUTH_LEVEL_CONNECT:
+ /* for now we ignore possible signatures here */
+ status = NT_STATUS_OK;
+ break;
+
+ default:
+ status = NT_STATUS_INVALID_LEVEL;
+ break;
+ }
+
+ /* remove the indicated amount of padding */
+ if (pkt->u.request.stub_and_verifier.length < auth.auth_pad_length) {
+ talloc_free(ndr);
+ return false;
+ }
+ pkt->u.request.stub_and_verifier.length -= auth.auth_pad_length;
+ talloc_free(ndr);
+
+ return NT_STATUS_IS_OK(status);
+}
+
+
+/*
+ push a signed or sealed dcerpc request packet into a blob
+*/
+bool dcesrv_auth_response(struct dcesrv_call_state *call,
+ DATA_BLOB *blob, size_t sig_size,
+ struct ncacn_packet *pkt)
+{
+ struct dcesrv_connection *dce_conn = call->conn;
+ NTSTATUS status;
+ enum ndr_err_code ndr_err;
+ struct ndr_push *ndr;
+ uint32_t payload_length;
+ DATA_BLOB creds2;
+
+ /* non-signed packets are simple */
+ if (sig_size == 0) {
+ status = ncacn_push_auth(blob, call, lp_iconv_convenience(dce_conn->dce_ctx->lp_ctx), pkt, NULL);
+ return NT_STATUS_IS_OK(status);
+ }
+
+ switch (dce_conn->auth_state.auth_info->auth_level) {
+ case DCERPC_AUTH_LEVEL_PRIVACY:
+ case DCERPC_AUTH_LEVEL_INTEGRITY:
+ break;
+
+ case DCERPC_AUTH_LEVEL_CONNECT:
+ /*
+ * TODO: let the gensec mech decide if it wants to generate a signature
+ * that might be needed for schannel...
+ */
+ status = ncacn_push_auth(blob, call, lp_iconv_convenience(dce_conn->dce_ctx->lp_ctx), pkt, NULL);
+ return NT_STATUS_IS_OK(status);
+
+ case DCERPC_AUTH_LEVEL_NONE:
+ status = ncacn_push_auth(blob, call, lp_iconv_convenience(dce_conn->dce_ctx->lp_ctx), pkt, NULL);
+ return NT_STATUS_IS_OK(status);
+
+ default:
+ return false;
+ }
+
+ ndr = ndr_push_init_ctx(call, lp_iconv_convenience(dce_conn->dce_ctx->lp_ctx));
+ if (!ndr) {
+ return false;
+ }
+
+ if (!(pkt->drep[0] & DCERPC_DREP_LE)) {
+ ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
+ }
+
+ ndr_err = ndr_push_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return false;
+ }
+
+ /* pad to 16 byte multiple, match win2k3 */
+ dce_conn->auth_state.auth_info->auth_pad_length =
+ (16 - (pkt->u.response.stub_and_verifier.length & 15)) & 15;
+ ndr_err = ndr_push_zero(ndr, dce_conn->auth_state.auth_info->auth_pad_length);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return false;
+ }
+
+ payload_length = pkt->u.response.stub_and_verifier.length +
+ dce_conn->auth_state.auth_info->auth_pad_length;
+
+ /* we start without signature, it will appended later */
+ dce_conn->auth_state.auth_info->credentials = data_blob(NULL, 0);
+
+ /* add the auth verifier */
+ ndr_err = ndr_push_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS,
+ dce_conn->auth_state.auth_info);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return false;
+ }
+
+ /* extract the whole packet as a blob */
+ *blob = ndr_push_blob(ndr);
+
+ /*
+ * Setup the frag and auth length in the packet buffer.
+ * This is needed if the GENSEC mech does AEAD signing
+ * of the packet headers. The signature itself will be
+ * appended later.
+ */
+ dcerpc_set_frag_length(blob, blob->length + sig_size);
+ dcerpc_set_auth_length(blob, sig_size);
+
+ /* sign or seal the packet */
+ switch (dce_conn->auth_state.auth_info->auth_level) {
+ case DCERPC_AUTH_LEVEL_PRIVACY:
+ status = gensec_seal_packet(dce_conn->auth_state.gensec_security,
+ call,
+ ndr->data + DCERPC_REQUEST_LENGTH,
+ payload_length,
+ blob->data,
+ blob->length,
+ &creds2);
+ break;
+
+ case DCERPC_AUTH_LEVEL_INTEGRITY:
+ status = gensec_sign_packet(dce_conn->auth_state.gensec_security,
+ call,
+ ndr->data + DCERPC_REQUEST_LENGTH,
+ payload_length,
+ blob->data,
+ blob->length,
+ &creds2);
+ break;
+
+ default:
+ status = NT_STATUS_INVALID_LEVEL;
+ break;
+ }
+
+ if (NT_STATUS_IS_OK(status)) {
+ if (creds2.length != sig_size) {
+ DEBUG(0,("dcesrv_auth_response: creds2.length[%u] != sig_size[%u] pad[%u] stub[%u]\n",
+ creds2.length, (uint32_t)sig_size,
+ dce_conn->auth_state.auth_info->auth_pad_length,
+ pkt->u.response.stub_and_verifier.length));
+ data_blob_free(&creds2);
+ status = NT_STATUS_INTERNAL_ERROR;
+ }
+ }
+
+ if (NT_STATUS_IS_OK(status)) {
+ if (!data_blob_append(call, blob, creds2.data, creds2.length)) {
+ status = NT_STATUS_NO_MEMORY;
+ }
+ data_blob_free(&creds2);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ return true;
+}
diff --git a/source4/rpc_server/dcesrv_mgmt.c b/source4/rpc_server/dcesrv_mgmt.c
new file mode 100644
index 0000000000..3a8f9956c2
--- /dev/null
+++ b/source4/rpc_server/dcesrv_mgmt.c
@@ -0,0 +1,102 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the mgmt pipe
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "rpc_server/dcerpc_server.h"
+#include "librpc/gen_ndr/ndr_mgmt.h"
+#include "rpc_server/common/common.h"
+
+/*
+ mgmt_inq_if_ids
+*/
+static WERROR dcesrv_mgmt_inq_if_ids(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct mgmt_inq_if_ids *r)
+{
+ const struct dcesrv_endpoint *ep = dce_call->conn->endpoint;
+ struct dcesrv_if_list *l;
+ struct rpc_if_id_vector_t *vector;
+
+ vector = *r->out.if_id_vector = talloc(mem_ctx, struct rpc_if_id_vector_t);
+ vector->count = 0;
+ vector->if_id = NULL;
+ for (l = ep->interface_list; l; l = l->next) {
+ vector->count++;
+ vector->if_id = talloc_realloc(mem_ctx, vector->if_id, struct ndr_syntax_id_p, vector->count);
+ vector->if_id[vector->count-1].id = &l->iface.syntax_id;
+ }
+ return WERR_OK;
+}
+
+
+/*
+ mgmt_inq_stats
+*/
+static WERROR dcesrv_mgmt_inq_stats(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct mgmt_inq_stats *r)
+{
+ if (r->in.max_count != MGMT_STATS_ARRAY_MAX_SIZE)
+ return WERR_NOT_SUPPORTED;
+
+ r->out.statistics->count = r->in.max_count;
+ r->out.statistics->statistics = talloc_array(mem_ctx, uint32_t, r->in.max_count);
+ /* FIXME */
+ r->out.statistics->statistics[MGMT_STATS_CALLS_IN] = 0;
+ r->out.statistics->statistics[MGMT_STATS_CALLS_OUT] = 0;
+ r->out.statistics->statistics[MGMT_STATS_PKTS_IN] = 0;
+ r->out.statistics->statistics[MGMT_STATS_PKTS_OUT] = 0;
+
+ return WERR_OK;
+}
+
+
+/*
+ mgmt_is_server_listening
+*/
+static uint32_t dcesrv_mgmt_is_server_listening(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct mgmt_is_server_listening *r)
+{
+ *r->out.status = 0;
+ return 1;
+}
+
+
+/*
+ mgmt_stop_server_listening
+*/
+static WERROR dcesrv_mgmt_stop_server_listening(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct mgmt_stop_server_listening *r)
+{
+ return WERR_ACCESS_DENIED;
+}
+
+
+/*
+ mgmt_inq_princ_name
+*/
+static WERROR dcesrv_mgmt_inq_princ_name(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct mgmt_inq_princ_name *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_mgmt_s.c"
diff --git a/source4/rpc_server/drsuapi/dcesrv_drsuapi.c b/source4/rpc_server/drsuapi/dcesrv_drsuapi.c
new file mode 100644
index 0000000000..6af8ea50b5
--- /dev/null
+++ b/source4/rpc_server/drsuapi/dcesrv_drsuapi.c
@@ -0,0 +1,825 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the drsuapi pipe
+
+ Copyright (C) Stefan Metzmacher 2004
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "librpc/gen_ndr/ndr_drsuapi.h"
+#include "rpc_server/dcerpc_server.h"
+#include "rpc_server/common/common.h"
+#include "rpc_server/drsuapi/dcesrv_drsuapi.h"
+#include "dsdb/samdb/samdb.h"
+#include "lib/ldb/include/ldb_errors.h"
+#include "param/param.h"
+
+/*
+ drsuapi_DsBind
+*/
+static WERROR dcesrv_drsuapi_DsBind(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsBind *r)
+{
+ struct drsuapi_bind_state *b_state;
+ struct dcesrv_handle *handle;
+ struct drsuapi_DsBindInfoCtr *bind_info;
+ struct GUID site_guid;
+ struct ldb_result *site_res;
+ struct ldb_dn *server_site_dn;
+ static const char *site_attrs[] = { "objectGUID", NULL };
+ struct ldb_result *ntds_res;
+ struct ldb_dn *ntds_dn;
+ static const char *ntds_attrs[] = { "ms-DS-ReplicationEpoch", NULL };
+ uint32_t pid;
+ uint32_t repl_epoch;
+ int ret;
+
+ r->out.bind_info = NULL;
+ ZERO_STRUCTP(r->out.bind_handle);
+
+ b_state = talloc_zero(mem_ctx, struct drsuapi_bind_state);
+ W_ERROR_HAVE_NO_MEMORY(b_state);
+
+ /*
+ * connect to the samdb
+ */
+ b_state->sam_ctx = samdb_connect(b_state, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, dce_call->conn->auth_state.session_info);
+ if (!b_state->sam_ctx) {
+ return WERR_FOOBAR;
+ }
+
+ /*
+ * find out the guid of our own site
+ */
+ server_site_dn = samdb_server_site_dn(b_state->sam_ctx, mem_ctx);
+ W_ERROR_HAVE_NO_MEMORY(server_site_dn);
+
+ ret = ldb_search(b_state->sam_ctx, mem_ctx, &site_res,
+ server_site_dn, LDB_SCOPE_BASE, site_attrs,
+ "(objectClass=*)");
+ if (ret != LDB_SUCCESS) {
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+ if (site_res->count != 1) {
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+ site_guid = samdb_result_guid(site_res->msgs[0], "objectGUID");
+
+ /*
+ * lookup the local servers Replication Epoch
+ */
+ ntds_dn = samdb_ntds_settings_dn(b_state->sam_ctx);
+ W_ERROR_HAVE_NO_MEMORY(ntds_dn);
+
+ ret = ldb_search(b_state->sam_ctx, mem_ctx, &ntds_res,
+ ntds_dn, LDB_SCOPE_BASE, ntds_attrs,
+ "(objectClass=*)");
+ if (ret != LDB_SUCCESS) {
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+ if (ntds_res->count != 1) {
+ return WERR_DS_DRA_INTERNAL_ERROR;
+ }
+ repl_epoch = samdb_result_uint(ntds_res->msgs[0], "ms-DS-ReplicationEpoch", 0);
+
+ /*
+ * The "process identifier" of the client.
+ * According to the WSPP docs, sectin 5.35, this is
+ * for informational and debugging purposes only.
+ * The assignment is implementation specific.
+ */
+ pid = 0;
+
+ /*
+ * store the clients bind_guid
+ */
+ if (r->in.bind_guid) {
+ b_state->remote_bind_guid = *r->in.bind_guid;
+ }
+
+ /*
+ * store the clients bind_info
+ */
+ if (r->in.bind_info) {
+ switch (r->in.bind_info->length) {
+ case 24: {
+ struct drsuapi_DsBindInfo24 *info24;
+ info24 = &r->in.bind_info->info.info24;
+ b_state->remote_info28.supported_extensions = info24->supported_extensions;
+ b_state->remote_info28.site_guid = info24->site_guid;
+ b_state->remote_info28.pid = info24->pid;
+ b_state->remote_info28.repl_epoch = 0;
+ break;
+ }
+ case 28:
+ b_state->remote_info28 = r->in.bind_info->info.info28;
+ break;
+ }
+ }
+
+ /*
+ * fill in our local bind info 28
+ */
+ b_state->local_info28.supported_extensions = 0;
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_BASE;
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ASYNC_REPLICATION;
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_REMOVEAPI;
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_MOVEREQ_V2;
+#if 0 /* we don't support MSZIP compression (only decompression) */
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHG_COMPRESS;
+#endif
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V1;
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_RESTORE_USN_OPTIMIZATION;
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_KCC_EXECUTE;
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRY_V2;
+ if (0 /*domain.behavior_version == 2*/) {
+ /* TODO: find out how this is really triggered! */
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_LINKED_VALUE_REPLICATION;
+ }
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V2;
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_INSTANCE_TYPE_NOT_REQ_ON_MOD;
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_CRYPTO_BIND;
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GET_REPL_INFO;
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_STRONG_ENCRYPTION;
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V01;
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_TRANSITIVE_MEMBERSHIP;
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ADD_SID_HISTORY;
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_POST_BETA3;
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_00100000;
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GET_MEMBERSHIPS2;
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V6;
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_NONDOMAIN_NCS;
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8;
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V5;
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V6;
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRYREPLY_V3;
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V7;
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_VERIFY_OBJECT;
+#if 0 /* we don't support XPRESS compression yet */
+ b_state->local_info28.supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_XPRESS_COMPRESS;
+#endif
+ b_state->local_info28.site_guid = site_guid;
+ b_state->local_info28.pid = pid;
+ b_state->local_info28.repl_epoch = repl_epoch;
+
+ /*
+ * allocate the return bind_info
+ */
+ bind_info = talloc(mem_ctx, struct drsuapi_DsBindInfoCtr);
+ W_ERROR_HAVE_NO_MEMORY(bind_info);
+
+ bind_info->length = 28;
+ bind_info->info.info28 = b_state->local_info28;
+
+ /*
+ * allocate a bind handle
+ */
+ handle = dcesrv_handle_new(dce_call->context, DRSUAPI_BIND_HANDLE);
+ W_ERROR_HAVE_NO_MEMORY(handle);
+ handle->data = talloc_steal(handle, b_state);
+
+ /*
+ * prepare reply
+ */
+ r->out.bind_info = bind_info;
+ *r->out.bind_handle = handle->wire_handle;
+
+ return WERR_OK;
+}
+
+
+/*
+ drsuapi_DsUnbind
+*/
+static WERROR dcesrv_drsuapi_DsUnbind(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsUnbind *r)
+{
+ struct dcesrv_handle *h;
+
+ *r->out.bind_handle = *r->in.bind_handle;
+
+ DCESRV_PULL_HANDLE_WERR(h, r->in.bind_handle, DRSUAPI_BIND_HANDLE);
+
+ talloc_free(h);
+
+ ZERO_STRUCTP(r->out.bind_handle);
+
+ return WERR_OK;
+}
+
+
+/*
+ drsuapi_DsReplicaSync
+*/
+static WERROR dcesrv_drsuapi_DsReplicaSync(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsReplicaSync *r)
+{
+ /* TODO: implement this call correct!
+ * for now we just say yes,
+ * because we have no output parameter
+ */
+ return WERR_OK;
+}
+
+
+/*
+ drsuapi_DsGetNCChanges
+*/
+static WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsGetNCChanges *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ drsuapi_DsReplicaUpdateRefs
+*/
+static WERROR dcesrv_drsuapi_DsReplicaUpdateRefs(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsReplicaUpdateRefs *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ DRSUAPI_REPLICA_ADD
+*/
+static WERROR dcesrv_DRSUAPI_REPLICA_ADD(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct DRSUAPI_REPLICA_ADD *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ DRSUAPI_REPLICA_DEL
+*/
+static WERROR dcesrv_DRSUAPI_REPLICA_DEL(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct DRSUAPI_REPLICA_DEL *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ DRSUAPI_REPLICA_MODIFY
+*/
+static WERROR dcesrv_DRSUAPI_REPLICA_MODIFY(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct DRSUAPI_REPLICA_MODIFY *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ DRSUAPI_VERIFY_NAMES
+*/
+static WERROR dcesrv_DRSUAPI_VERIFY_NAMES(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct DRSUAPI_VERIFY_NAMES *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ drsuapi_DsGetMemberships
+*/
+static WERROR dcesrv_drsuapi_DsGetMemberships(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsGetMemberships *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ DRSUAPI_INTER_DOMAIN_MOVE
+*/
+static WERROR dcesrv_DRSUAPI_INTER_DOMAIN_MOVE(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct DRSUAPI_INTER_DOMAIN_MOVE *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ drsuapi_DsGetNT4ChangeLog
+*/
+static WERROR dcesrv_drsuapi_DsGetNT4ChangeLog(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsGetNT4ChangeLog *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ drsuapi_DsCrackNames
+*/
+static WERROR dcesrv_drsuapi_DsCrackNames(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsCrackNames *r)
+{
+ WERROR status;
+ struct drsuapi_bind_state *b_state;
+ struct dcesrv_handle *h;
+
+ *r->out.level_out = r->in.level;
+
+ DCESRV_PULL_HANDLE_WERR(h, r->in.bind_handle, DRSUAPI_BIND_HANDLE);
+ b_state = h->data;
+
+ r->out.ctr = talloc_zero(mem_ctx, union drsuapi_DsNameCtr);
+ W_ERROR_HAVE_NO_MEMORY(r->out.ctr);
+
+ switch (r->in.level) {
+ case 1: {
+ struct drsuapi_DsNameCtr1 *ctr1;
+ struct drsuapi_DsNameInfo1 *names;
+ int count;
+ int i;
+
+ ctr1 = talloc(mem_ctx, struct drsuapi_DsNameCtr1);
+ W_ERROR_HAVE_NO_MEMORY(ctr1);
+
+ count = r->in.req->req1.count;
+ names = talloc_array(mem_ctx, struct drsuapi_DsNameInfo1, count);
+ W_ERROR_HAVE_NO_MEMORY(names);
+
+ for (i=0; i < count; i++) {
+ status = DsCrackNameOneName(b_state->sam_ctx, mem_ctx,
+ r->in.req->req1.format_flags,
+ r->in.req->req1.format_offered,
+ r->in.req->req1.format_desired,
+ r->in.req->req1.names[i].str,
+ &names[i]);
+ if (!W_ERROR_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ ctr1->count = count;
+ ctr1->array = names;
+ r->out.ctr->ctr1 = ctr1;
+
+ return WERR_OK;
+ }
+ }
+
+ return WERR_UNKNOWN_LEVEL;
+}
+
+/*
+ drsuapi_DsWriteAccountSpn
+*/
+static WERROR dcesrv_drsuapi_DsWriteAccountSpn(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsWriteAccountSpn *r)
+{
+ struct drsuapi_bind_state *b_state;
+ struct dcesrv_handle *h;
+
+ *r->out.level_out = r->in.level;
+
+ DCESRV_PULL_HANDLE_WERR(h, r->in.bind_handle, DRSUAPI_BIND_HANDLE);
+ b_state = h->data;
+
+ r->out.res = talloc(mem_ctx, union drsuapi_DsWriteAccountSpnResult);
+ W_ERROR_HAVE_NO_MEMORY(r->out.res);
+
+ switch (r->in.level) {
+ case 1: {
+ struct drsuapi_DsWriteAccountSpnRequest1 *req;
+ struct ldb_message *msg;
+ int count, i, ret;
+ req = &r->in.req->req1;
+ count = req->count;
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ return WERR_NOMEM;
+ }
+
+ msg->dn = ldb_dn_new(msg, b_state->sam_ctx, req->object_dn);
+ if ( ! ldb_dn_validate(msg->dn)) {
+ r->out.res->res1.status = WERR_OK;
+ return WERR_OK;
+ }
+
+ /* construct mods */
+ for (i = 0; i < count; i++) {
+ samdb_msg_add_string(b_state->sam_ctx,
+ msg, msg, "servicePrincipalName",
+ req->spn_names[i].str);
+ }
+ for (i=0;i<msg->num_elements;i++) {
+ switch (req->operation) {
+ case DRSUAPI_DS_SPN_OPERATION_ADD:
+ msg->elements[i].flags = LDB_FLAG_MOD_ADD;
+ break;
+ case DRSUAPI_DS_SPN_OPERATION_REPLACE:
+ msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
+ break;
+ case DRSUAPI_DS_SPN_OPERATION_DELETE:
+ msg->elements[i].flags = LDB_FLAG_MOD_DELETE;
+ break;
+ }
+ }
+
+ /* Apply to database */
+
+ ret = ldb_modify(b_state->sam_ctx, msg);
+ if (ret != 0) {
+ DEBUG(0,("Failed to modify SPNs on %s: %s\n",
+ ldb_dn_get_linearized(msg->dn),
+ ldb_errstring(b_state->sam_ctx)));
+ r->out.res->res1.status = WERR_ACCESS_DENIED;
+ } else {
+ r->out.res->res1.status = WERR_OK;
+ }
+
+ return WERR_OK;
+ }
+ }
+
+ return WERR_UNKNOWN_LEVEL;
+}
+
+
+/*
+ drsuapi_DsRemoveDSServer
+*/
+static WERROR dcesrv_drsuapi_DsRemoveDSServer(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsRemoveDSServer *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ DRSUAPI_REMOVE_DS_DOMAIN
+*/
+static WERROR dcesrv_DRSUAPI_REMOVE_DS_DOMAIN(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct DRSUAPI_REMOVE_DS_DOMAIN *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+/* Obtain the site name from a server DN */
+static const char *result_site_name(struct ldb_dn *site_dn)
+{
+ /* Format is cn=<NETBIOS name>,cn=Servers,cn=<site>,cn=sites.... */
+ const struct ldb_val *val = ldb_dn_get_component_val(site_dn, 2);
+ const char *name = ldb_dn_get_component_name(site_dn, 2);
+
+ if (!name || (ldb_attr_cmp(name, "cn") != 0)) {
+ /* Ensure this matches the format. This gives us a
+ * bit more confidence that a 'cn' value will be a
+ * ascii string */
+ return NULL;
+ }
+ if (val) {
+ return (char *)val->data;
+ }
+ return NULL;
+}
+
+/*
+ drsuapi_DsGetDomainControllerInfo
+*/
+static WERROR dcesrv_drsuapi_DsGetDomainControllerInfo_1(struct drsuapi_bind_state *b_state,
+ TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsGetDomainControllerInfo *r)
+{
+ struct ldb_dn *sites_dn;
+ struct ldb_result *res;
+
+ const char *attrs_account_1[] = { "cn", "dnsHostName", NULL };
+ const char *attrs_account_2[] = { "cn", "dnsHostName", "objectGUID", NULL };
+
+ const char *attrs_none[] = { NULL };
+
+ const char *attrs_site[] = { "objectGUID", NULL };
+
+ const char *attrs_ntds[] = { "options", "objectGUID", NULL };
+
+ const char *attrs_1[] = { "serverReference", "cn", "dnsHostName", NULL };
+ const char *attrs_2[] = { "serverReference", "cn", "dnsHostName", "objectGUID", NULL };
+ const char **attrs;
+
+ struct drsuapi_DsGetDCInfoCtr1 *ctr1;
+ struct drsuapi_DsGetDCInfoCtr2 *ctr2;
+
+ int ret, i;
+
+ *r->out.level_out = r->in.req->req1.level;
+ r->out.ctr = talloc(mem_ctx, union drsuapi_DsGetDCInfoCtr);
+ W_ERROR_HAVE_NO_MEMORY(r->out.ctr);
+
+ sites_dn = samdb_sites_dn(b_state->sam_ctx, mem_ctx);
+ if (!sites_dn) {
+ return WERR_DS_OBJ_NOT_FOUND;
+ }
+
+ switch (*r->out.level_out) {
+ case -1:
+ /* this level is not like the others */
+ return WERR_UNKNOWN_LEVEL;
+ case 1:
+ attrs = attrs_1;
+ break;
+ case 2:
+ attrs = attrs_2;
+ break;
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ ret = ldb_search(b_state->sam_ctx, mem_ctx, &res, sites_dn, LDB_SCOPE_SUBTREE, attrs,
+ "objectClass=server");
+
+ if (ret) {
+ DEBUG(1, ("searching for servers in sites DN %s failed: %s\n",
+ ldb_dn_get_linearized(sites_dn), ldb_errstring(b_state->sam_ctx)));
+ return WERR_GENERAL_FAILURE;
+ }
+
+ switch (*r->out.level_out) {
+ case 1:
+ ctr1 = &r->out.ctr->ctr1;
+ ctr1->count = res->count;
+ ctr1->array = talloc_zero_array(mem_ctx,
+ struct drsuapi_DsGetDCInfo1,
+ res->count);
+ for (i=0; i < res->count; i++) {
+ struct ldb_dn *domain_dn;
+ struct ldb_result *res_domain;
+ struct ldb_result *res_account;
+ struct ldb_dn *ntds_dn = ldb_dn_copy(mem_ctx, res->msgs[i]->dn);
+
+ struct ldb_dn *ref_dn
+ = ldb_msg_find_attr_as_dn(b_state->sam_ctx,
+ mem_ctx, res->msgs[i],
+ "serverReference");
+
+ if (!ntds_dn || !ldb_dn_add_child_fmt(ntds_dn, "CN=NTDS Settings")) {
+ return WERR_NOMEM;
+ }
+
+ ret = ldb_search(b_state->sam_ctx, mem_ctx, &res_account, ref_dn,
+ LDB_SCOPE_BASE, attrs_account_1, "objectClass=computer");
+ if (ret == LDB_SUCCESS && res_account->count == 1) {
+ const char *errstr;
+ ctr1->array[i].dns_name
+ = ldb_msg_find_attr_as_string(res_account->msgs[0], "dNSHostName", NULL);
+ ctr1->array[i].netbios_name
+ = ldb_msg_find_attr_as_string(res_account->msgs[0], "cn", NULL);
+ ctr1->array[i].computer_dn
+ = ldb_dn_get_linearized(res_account->msgs[0]->dn);
+
+ /* Determine if this is the PDC */
+ ret = samdb_search_for_parent_domain(b_state->sam_ctx,
+ mem_ctx, res_account->msgs[0]->dn,
+ &domain_dn, &errstr);
+
+ if (ret == LDB_SUCCESS) {
+ ret = ldb_search(b_state->sam_ctx, mem_ctx, &res_domain, domain_dn,
+ LDB_SCOPE_BASE, attrs_none, "fSMORoleOwner=%s",
+ ldb_dn_get_linearized(ntds_dn));
+ if (ret) {
+ return WERR_GENERAL_FAILURE;
+ }
+ if (res_domain->count == 1) {
+ ctr1->array[i].is_pdc = true;
+ }
+ }
+ }
+ if ((ret != LDB_SUCCESS) && (ret != LDB_ERR_NO_SUCH_OBJECT)) {
+ DEBUG(5, ("warning: searching for computer DN %s failed: %s\n",
+ ldb_dn_get_linearized(ref_dn), ldb_errstring(b_state->sam_ctx)));
+ }
+
+ /* Look at server DN and extract site component */
+ ctr1->array[i].site_name = result_site_name(res->msgs[i]->dn);
+ ctr1->array[i].server_dn = ldb_dn_get_linearized(res->msgs[i]->dn);
+
+
+ ctr1->array[i].is_enabled = true;
+
+ }
+ break;
+ case 2:
+ ctr2 = &r->out.ctr->ctr2;
+ ctr2->count = res->count;
+ ctr2->array = talloc_zero_array(mem_ctx,
+ struct drsuapi_DsGetDCInfo2,
+ res->count);
+ for (i=0; i < res->count; i++) {
+ struct ldb_dn *domain_dn;
+ struct ldb_result *res_domain;
+ struct ldb_result *res_account;
+ struct ldb_dn *ntds_dn = ldb_dn_copy(mem_ctx, res->msgs[i]->dn);
+ struct ldb_result *res_ntds;
+ struct ldb_dn *site_dn = ldb_dn_copy(mem_ctx, res->msgs[i]->dn);
+ struct ldb_result *res_site;
+ struct ldb_dn *ref_dn
+ = ldb_msg_find_attr_as_dn(b_state->sam_ctx,
+ mem_ctx, res->msgs[i],
+ "serverReference");
+
+ if (!ntds_dn || !ldb_dn_add_child_fmt(ntds_dn, "CN=NTDS Settings")) {
+ return WERR_NOMEM;
+ }
+
+ /* Format is cn=<NETBIOS name>,cn=Servers,cn=<site>,cn=sites.... */
+ if (!site_dn || !ldb_dn_remove_child_components(site_dn, 2)) {
+ return WERR_NOMEM;
+ }
+
+ ret = ldb_search(b_state->sam_ctx, mem_ctx, &res_ntds, ntds_dn,
+ LDB_SCOPE_BASE, attrs_ntds, "objectClass=nTDSDSA");
+ if (ret == LDB_SUCCESS && res_ntds->count == 1) {
+ ctr2->array[i].is_gc
+ = (ldb_msg_find_attr_as_int(res_ntds->msgs[0], "options", 0) == 1);
+ ctr2->array[i].ntds_guid
+ = samdb_result_guid(res_ntds->msgs[0], "objectGUID");
+ ctr2->array[i].ntds_dn = ldb_dn_get_linearized(res_ntds->msgs[0]->dn);
+ }
+ if ((ret != LDB_SUCCESS) && (ret != LDB_ERR_NO_SUCH_OBJECT)) {
+ DEBUG(5, ("warning: searching for NTDS DN %s failed: %s\n",
+ ldb_dn_get_linearized(ntds_dn), ldb_errstring(b_state->sam_ctx)));
+ }
+
+ ret = ldb_search(b_state->sam_ctx, mem_ctx, &res_site, site_dn,
+ LDB_SCOPE_BASE, attrs_site, "objectClass=site");
+ if (ret == LDB_SUCCESS && res_site->count == 1) {
+ ctr2->array[i].site_guid
+ = samdb_result_guid(res_site->msgs[0], "objectGUID");
+ ctr2->array[i].site_dn = ldb_dn_get_linearized(res_site->msgs[0]->dn);
+ }
+ if ((ret != LDB_SUCCESS) && (ret != LDB_ERR_NO_SUCH_OBJECT)) {
+ DEBUG(5, ("warning: searching for site DN %s failed: %s\n",
+ ldb_dn_get_linearized(site_dn), ldb_errstring(b_state->sam_ctx)));
+ }
+
+ ret = ldb_search(b_state->sam_ctx, mem_ctx, &res_account, ref_dn,
+ LDB_SCOPE_BASE, attrs_account_2, "objectClass=computer");
+ if (ret == LDB_SUCCESS && res_account->count == 1) {
+ const char *errstr;
+ ctr2->array[i].dns_name
+ = ldb_msg_find_attr_as_string(res_account->msgs[0], "dNSHostName", NULL);
+ ctr2->array[i].netbios_name
+ = ldb_msg_find_attr_as_string(res_account->msgs[0], "cn", NULL);
+ ctr2->array[i].computer_dn = ldb_dn_get_linearized(res_account->msgs[0]->dn);
+ ctr2->array[i].computer_guid
+ = samdb_result_guid(res_account->msgs[0], "objectGUID");
+
+ /* Determine if this is the PDC */
+ ret = samdb_search_for_parent_domain(b_state->sam_ctx,
+ mem_ctx, res_account->msgs[0]->dn,
+ &domain_dn, &errstr);
+
+ if (ret == LDB_SUCCESS) {
+ ret = ldb_search(b_state->sam_ctx, mem_ctx, &res_domain, domain_dn,
+ LDB_SCOPE_BASE, attrs_none, "fSMORoleOwner=%s",
+ ldb_dn_get_linearized(ntds_dn));
+ if (ret == LDB_SUCCESS && res_domain->count == 1) {
+ ctr2->array[i].is_pdc = true;
+ }
+ if ((ret != LDB_SUCCESS) && (ret != LDB_ERR_NO_SUCH_OBJECT)) {
+ DEBUG(5, ("warning: searching for domain DN %s failed: %s\n",
+ ldb_dn_get_linearized(domain_dn), ldb_errstring(b_state->sam_ctx)));
+ }
+ }
+ }
+ if ((ret != LDB_SUCCESS) && (ret != LDB_ERR_NO_SUCH_OBJECT)) {
+ DEBUG(5, ("warning: searching for computer account DN %s failed: %s\n",
+ ldb_dn_get_linearized(ref_dn), ldb_errstring(b_state->sam_ctx)));
+ }
+
+ /* Look at server DN and extract site component */
+ ctr2->array[i].site_name = result_site_name(res->msgs[i]->dn);
+ ctr2->array[i].server_dn = ldb_dn_get_linearized(res->msgs[i]->dn);
+ ctr2->array[i].server_guid
+ = samdb_result_guid(res->msgs[i], "objectGUID");
+
+ ctr2->array[i].is_enabled = true;
+
+ }
+ break;
+ }
+ return WERR_OK;
+}
+
+/*
+ drsuapi_DsGetDomainControllerInfo
+*/
+static WERROR dcesrv_drsuapi_DsGetDomainControllerInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsGetDomainControllerInfo *r)
+{
+ struct dcesrv_handle *h;
+ struct drsuapi_bind_state *b_state;
+ DCESRV_PULL_HANDLE_WERR(h, r->in.bind_handle, DRSUAPI_BIND_HANDLE);
+ b_state = h->data;
+
+ switch (r->in.level) {
+ case 1:
+ return dcesrv_drsuapi_DsGetDomainControllerInfo_1(b_state, mem_ctx, r);
+ }
+
+ return WERR_UNKNOWN_LEVEL;
+}
+
+
+/*
+ drsuapi_DsAddEntry
+*/
+static WERROR dcesrv_drsuapi_DsAddEntry(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsAddEntry *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ DRSUAPI_EXECUTE_KCC
+*/
+static WERROR dcesrv_DRSUAPI_EXECUTE_KCC(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct DRSUAPI_EXECUTE_KCC *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ drsuapi_DsReplicaGetInfo
+*/
+static WERROR dcesrv_drsuapi_DsReplicaGetInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsReplicaGetInfo *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ DRSUAPI_ADD_SID_HISTORY
+*/
+static WERROR dcesrv_DRSUAPI_ADD_SID_HISTORY(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct DRSUAPI_ADD_SID_HISTORY *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+/*
+ drsuapi_DsGetMemberships2
+*/
+static WERROR dcesrv_drsuapi_DsGetMemberships2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct drsuapi_DsGetMemberships2 *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+/*
+ DRSUAPI_REPLICA_VERIFY_OBJECTS
+*/
+static WERROR dcesrv_DRSUAPI_REPLICA_VERIFY_OBJECTS(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct DRSUAPI_REPLICA_VERIFY_OBJECTS *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ DRSUAPI_GET_OBJECT_EXISTENCE
+*/
+static WERROR dcesrv_DRSUAPI_GET_OBJECT_EXISTENCE(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct DRSUAPI_GET_OBJECT_EXISTENCE *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ drsuapi_QuerySitesByCost
+*/
+static WERROR dcesrv_drsuapi_QuerySitesByCost(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct drsuapi_QuerySitesByCost *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_drsuapi_s.c"
diff --git a/source4/rpc_server/drsuapi/dcesrv_drsuapi.h b/source4/rpc_server/drsuapi/dcesrv_drsuapi.h
new file mode 100644
index 0000000000..7412865449
--- /dev/null
+++ b/source4/rpc_server/drsuapi/dcesrv_drsuapi.h
@@ -0,0 +1,37 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the drsuapi pipe
+
+ Copyright (C) Stefan Metzmacher 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 <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ this type allows us to distinguish handle types
+*/
+enum drsuapi_handle {
+ DRSUAPI_BIND_HANDLE,
+};
+
+/*
+ state asscoiated with a drsuapi_DsBind*() operation
+*/
+struct drsuapi_bind_state {
+ struct ldb_context *sam_ctx;
+ struct GUID remote_bind_guid;
+ struct drsuapi_DsBindInfo28 remote_info28;
+ struct drsuapi_DsBindInfo28 local_info28;
+};
diff --git a/source4/rpc_server/echo/rpc_echo.c b/source4/rpc_server/echo/rpc_echo.c
new file mode 100644
index 0000000000..8bcee7d925
--- /dev/null
+++ b/source4/rpc_server/echo/rpc_echo.c
@@ -0,0 +1,204 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the echo pipe
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Stefan (metze) 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "rpc_server/dcerpc_server.h"
+#include "librpc/gen_ndr/ndr_echo.h"
+#include "lib/events/events.h"
+
+
+static NTSTATUS dcesrv_echo_AddOne(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct echo_AddOne *r)
+{
+ *r->out.out_data = r->in.in_data + 1;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS dcesrv_echo_EchoData(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct echo_EchoData *r)
+{
+ if (!r->in.len) {
+ return NT_STATUS_OK;
+ }
+
+ r->out.out_data = talloc_memdup(mem_ctx, r->in.in_data, r->in.len);
+ if (!r->out.out_data) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS dcesrv_echo_SinkData(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct echo_SinkData *r)
+{
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS dcesrv_echo_SourceData(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct echo_SourceData *r)
+{
+ int i;
+
+ r->out.data = talloc_array(mem_ctx, uint8_t, r->in.len);
+ if (!r->out.data) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0;i<r->in.len;i++) {
+ r->out.data[i] = i;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS dcesrv_echo_TestCall(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct echo_TestCall *r)
+{
+ *r->out.s2 = talloc_strdup(mem_ctx, r->in.s1);
+ if (r->in.s1 && !*r->out.s2) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS dcesrv_echo_TestCall2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct echo_TestCall2 *r)
+{
+ r->out.info = talloc(mem_ctx, union echo_Info);
+ if (!r->out.info) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ switch (r->in.level) {
+ case 1:
+ r->out.info->info1.v = 10;
+ break;
+ case 2:
+ r->out.info->info2.v = 20;
+ break;
+ case 3:
+ r->out.info->info3.v = 30;
+ break;
+ case 4:
+ r->out.info->info4.v = 40;
+ break;
+ case 5:
+ r->out.info->info5.v1 = 50;
+ r->out.info->info5.v2 = 60;
+ break;
+ case 6:
+ r->out.info->info6.v1 = 70;
+ r->out.info->info6.info1.v= 80;
+ break;
+ case 7:
+ r->out.info->info7.v1 = 80;
+ r->out.info->info7.info4.v = 90;
+ break;
+ default:
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS dcesrv_echo_TestEnum(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct echo_TestEnum *r)
+{
+ r->out.foo2->e1 = ECHO_ENUM2;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS dcesrv_echo_TestSurrounding(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct echo_TestSurrounding *r)
+{
+ if (!r->in.data) {
+ r->out.data = NULL;
+ return NT_STATUS_OK;
+ }
+
+ r->out.data = talloc(mem_ctx, struct echo_Surrounding);
+ if (!r->out.data) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ r->out.data->x = 2 * r->in.data->x;
+ r->out.data->surrounding = talloc_zero_array(mem_ctx, uint16_t, r->out.data->x);
+ if (!r->out.data->surrounding) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static uint16_t dcesrv_echo_TestDoublePointer(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct echo_TestDoublePointer *r)
+{
+ if (!*r->in.data)
+ return 0;
+ if (!**r->in.data)
+ return 0;
+ return ***r->in.data;
+}
+
+struct echo_TestSleep_private {
+ struct dcesrv_call_state *dce_call;
+ struct echo_TestSleep *r;
+};
+
+static void echo_TestSleep_handler(struct tevent_context *ev, struct tevent_timer *te,
+ struct timeval t, void *private_data)
+{
+ struct echo_TestSleep_private *p = talloc_get_type(private_data,
+ struct echo_TestSleep_private);
+ struct echo_TestSleep *r = p->r;
+ NTSTATUS status;
+
+ r->out.result = r->in.seconds;
+
+ status = dcesrv_reply(p->dce_call);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("echo_TestSleep_handler: dcesrv_reply() failed - %s\n",
+ nt_errstr(status)));
+ }
+}
+
+static long dcesrv_echo_TestSleep(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct echo_TestSleep *r)
+{
+ struct echo_TestSleep_private *p;
+
+ if (!(dce_call->state_flags & DCESRV_CALL_STATE_FLAG_MAY_ASYNC)) {
+ /* we're not allowed to reply async */
+ sleep(r->in.seconds);
+ return r->in.seconds;
+ }
+
+ /* we're allowed to reply async */
+ p = talloc(mem_ctx, struct echo_TestSleep_private);
+ if (!p) {
+ return 0;
+ }
+
+ p->dce_call = dce_call;
+ p->r = r;
+
+ event_add_timed(dce_call->event_ctx, p,
+ timeval_add(&dce_call->time, r->in.seconds, 0),
+ echo_TestSleep_handler, p);
+
+ dce_call->state_flags |= DCESRV_CALL_STATE_FLAG_ASYNC;
+ return 0;
+}
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_echo_s.c"
diff --git a/source4/rpc_server/epmapper/rpc_epmapper.c b/source4/rpc_server/epmapper/rpc_epmapper.c
new file mode 100644
index 0000000000..b9b02962be
--- /dev/null
+++ b/source4/rpc_server/epmapper/rpc_epmapper.c
@@ -0,0 +1,267 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the epmapper pipe
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Jelmer Vernooij 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "librpc/gen_ndr/ndr_epmapper.h"
+#include "rpc_server/dcerpc_server.h"
+#include "rpc_server/common/common.h"
+
+typedef uint32_t error_status_t;
+
+/* handle types for this module */
+enum handle_types {HTYPE_LOOKUP};
+
+/* a endpoint combined with an interface description */
+struct dcesrv_ep_iface {
+ const char *name;
+ struct epm_tower ep;
+};
+
+/*
+ build a list of all interfaces handled by all endpoint servers
+*/
+static uint32_t build_ep_list(TALLOC_CTX *mem_ctx,
+ struct dcesrv_endpoint *endpoint_list,
+ struct dcesrv_ep_iface **eps)
+{
+ struct dcesrv_endpoint *d;
+ uint32_t total = 0;
+ NTSTATUS status;
+
+ *eps = NULL;
+
+ for (d=endpoint_list; d; d=d->next) {
+ struct dcesrv_if_list *iface;
+ struct dcerpc_binding *description;
+
+ for (iface=d->interface_list;iface;iface=iface->next) {
+ (*eps) = talloc_realloc(mem_ctx,
+ *eps,
+ struct dcesrv_ep_iface,
+ total + 1);
+ if (!*eps) {
+ return 0;
+ }
+ (*eps)[total].name = iface->iface.name;
+
+ description = d->ep_description;
+ description->object = iface->iface.syntax_id;
+
+ status = dcerpc_binding_build_tower(mem_ctx, description, &(*eps)[total].ep);
+ if (NT_STATUS_IS_ERR(status)) {
+ DEBUG(1, ("Unable to build tower for %s\n", iface->iface.name));
+ continue;
+ }
+ total++;
+ }
+ }
+
+ return total;
+}
+
+
+static error_status_t dcesrv_epm_Insert(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct epm_Insert *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+static error_status_t dcesrv_epm_Delete(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct epm_Delete *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ implement epm_Lookup. This call is used to enumerate the interfaces
+ available on a rpc server
+*/
+static error_status_t dcesrv_epm_Lookup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct epm_Lookup *r)
+{
+ struct dcesrv_handle *h;
+ struct rpc_eps {
+ uint32_t count;
+ struct dcesrv_ep_iface *e;
+ } *eps;
+ uint32_t num_ents;
+ int i;
+
+ DCESRV_PULL_HANDLE_FAULT(h, r->in.entry_handle, HTYPE_LOOKUP);
+
+ eps = h->data;
+
+ if (!eps) {
+ /* this is the first call - fill the list. Subsequent calls
+ will feed from this list, stored in the handle */
+ eps = talloc(h, struct rpc_eps);
+ if (!eps) {
+ return EPMAPPER_STATUS_NO_MEMORY;
+ }
+ h->data = eps;
+
+ eps->count = build_ep_list(h, dce_call->conn->dce_ctx->endpoint_list, &eps->e);
+ }
+
+ /* return the next N elements */
+ num_ents = r->in.max_ents;
+ if (num_ents > eps->count) {
+ num_ents = eps->count;
+ }
+
+ *r->out.entry_handle = h->wire_handle;
+ r->out.num_ents = talloc(mem_ctx, uint32_t);
+ *r->out.num_ents = num_ents;
+
+ if (num_ents == 0) {
+ r->out.entries = NULL;
+ ZERO_STRUCTP(r->out.entry_handle);
+ talloc_free(h);
+ return EPMAPPER_STATUS_NO_MORE_ENTRIES;
+ }
+
+ r->out.entries = talloc_array(mem_ctx, struct epm_entry_t, num_ents);
+ if (!r->out.entries) {
+ return EPMAPPER_STATUS_NO_MEMORY;
+ }
+
+ for (i=0;i<num_ents;i++) {
+ ZERO_STRUCT(r->out.entries[i].object);
+ r->out.entries[i].annotation = eps->e[i].name;
+ r->out.entries[i].tower = talloc(mem_ctx, struct epm_twr_t);
+ if (!r->out.entries[i].tower) {
+ return EPMAPPER_STATUS_NO_MEMORY;
+ }
+ r->out.entries[i].tower->tower = eps->e[i].ep;
+ }
+
+ eps->count -= num_ents;
+ eps->e += num_ents;
+
+ return EPMAPPER_STATUS_OK;
+}
+
+
+/*
+ implement epm_Map. This is used to find the specific endpoint to talk to given
+ a generic protocol tower
+*/
+static error_status_t dcesrv_epm_Map(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct epm_Map *r)
+{
+ uint32_t count;
+ int i;
+ struct dcesrv_ep_iface *eps;
+ struct epm_floor *floors;
+ enum dcerpc_transport_t transport;
+ struct ndr_syntax_id ndr_syntax;
+
+ count = build_ep_list(mem_ctx, dce_call->conn->dce_ctx->endpoint_list, &eps);
+
+ ZERO_STRUCT(*r->out.entry_handle);
+ r->out.num_towers = talloc(mem_ctx, uint32_t);
+ if (!r->out.num_towers) {
+ return EPMAPPER_STATUS_NO_MEMORY;
+ }
+ *r->out.num_towers = 1;
+ r->out.towers = talloc(mem_ctx, struct epm_twr_p_t);
+ if (!r->out.towers) {
+ return EPMAPPER_STATUS_NO_MEMORY;
+ }
+ r->out.towers->twr = talloc(mem_ctx, struct epm_twr_t);
+ if (!r->out.towers->twr) {
+ return EPMAPPER_STATUS_NO_MEMORY;
+ }
+
+ if (!r->in.map_tower || r->in.max_towers == 0 ||
+ r->in.map_tower->tower.num_floors < 3) {
+ goto failed;
+ }
+
+ floors = r->in.map_tower->tower.floors;
+
+ dcerpc_floor_get_lhs_data(&r->in.map_tower->tower.floors[1], &ndr_syntax);
+
+ if (floors[1].lhs.protocol != EPM_PROTOCOL_UUID ||
+ !GUID_equal(&ndr_syntax.uuid, &ndr_transfer_syntax.uuid) ||
+ ndr_syntax.if_version != ndr_transfer_syntax.if_version) {
+ goto failed;
+ }
+
+ transport = dcerpc_transport_by_tower(&r->in.map_tower->tower);
+
+ if (transport == -1) {
+ DEBUG(2, ("Client requested unknown transport with levels: "));
+ for (i = 2; i < r->in.map_tower->tower.num_floors; i++) {
+ DEBUG(2, ("%d, ", r->in.map_tower->tower.floors[i].lhs.protocol));
+ }
+ DEBUG(2, ("\n"));
+ goto failed;
+ }
+
+ for (i=0;i<count;i++) {
+ if (
+ data_blob_cmp(&r->in.map_tower->tower.floors[0].lhs.lhs_data,
+ &eps[i].ep.floors[0].lhs.lhs_data) != 0
+ || transport != dcerpc_transport_by_tower(&eps[i].ep)) {
+ continue;
+ }
+
+ r->out.towers->twr->tower = eps[i].ep;
+ r->out.towers->twr->tower_length = 0;
+ return EPMAPPER_STATUS_OK;
+ }
+
+
+failed:
+ *r->out.num_towers = 0;
+ r->out.towers->twr = NULL;
+
+ return EPMAPPER_STATUS_NO_MORE_ENTRIES;
+}
+
+static error_status_t dcesrv_epm_LookupHandleFree(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct epm_LookupHandleFree *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+static error_status_t dcesrv_epm_InqObject(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct epm_InqObject *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+static error_status_t dcesrv_epm_MgmtDelete(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct epm_MgmtDelete *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+static error_status_t dcesrv_epm_MapAuth(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct epm_MapAuth *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_epmapper_s.c"
diff --git a/source4/rpc_server/handles.c b/source4/rpc_server/handles.c
new file mode 100644
index 0000000000..284354feb4
--- /dev/null
+++ b/source4/rpc_server/handles.c
@@ -0,0 +1,90 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ server side dcerpc handle code
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "../lib/util/dlinklist.h"
+#include "rpc_server/dcerpc_server.h"
+
+/*
+ destroy a rpc handle
+*/
+static int dcesrv_handle_destructor(struct dcesrv_handle *h)
+{
+ DLIST_REMOVE(h->context->handles, h);
+ return 0;
+}
+
+
+/*
+ allocate a new rpc handle
+*/
+_PUBLIC_ struct dcesrv_handle *dcesrv_handle_new(struct dcesrv_connection_context *context,
+ uint8_t handle_type)
+{
+ struct dcesrv_handle *h;
+
+ h = talloc(context, struct dcesrv_handle);
+ if (!h) {
+ return NULL;
+ }
+ h->data = NULL;
+ h->context = context;
+
+ h->wire_handle.handle_type = handle_type;
+ h->wire_handle.uuid = GUID_random();
+
+ DLIST_ADD(context->handles, h);
+
+ talloc_set_destructor(h, dcesrv_handle_destructor);
+
+ return h;
+}
+
+/**
+ find an internal handle given a wire handle. If the wire handle is NULL then
+ allocate a new handle
+*/
+_PUBLIC_ struct dcesrv_handle *dcesrv_handle_fetch(
+ struct dcesrv_connection_context *context,
+ struct policy_handle *p,
+ uint8_t handle_type)
+{
+ struct dcesrv_handle *h;
+
+ if (policy_handle_empty(p)) {
+ return dcesrv_handle_new(context, handle_type);
+ }
+
+ for (h=context->handles; h; h=h->next) {
+ if (h->wire_handle.handle_type == p->handle_type &&
+ GUID_equal(&p->uuid, &h->wire_handle.uuid)) {
+ if (handle_type != DCESRV_HANDLE_ANY &&
+ p->handle_type != handle_type) {
+ DEBUG(0,("client gave us the wrong handle type (%d should be %d)\n",
+ p->handle_type, handle_type));
+ return NULL;
+ }
+ return h;
+ }
+ }
+
+ return NULL;
+}
diff --git a/source4/rpc_server/lsa/dcesrv_lsa.c b/source4/rpc_server/lsa/dcesrv_lsa.c
new file mode 100644
index 0000000000..cbadf654fb
--- /dev/null
+++ b/source4/rpc_server/lsa/dcesrv_lsa.c
@@ -0,0 +1,3345 @@
+/* need access mask/acl implementation */
+
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the lsarpc pipe
+
+ Copyright (C) Andrew Tridgell 2004
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "rpc_server/lsa/lsa.h"
+#include "../lib/util/util_ldb.h"
+#include "libcli/ldap/ldap_ndr.h"
+#include "system/kerberos.h"
+#include "auth/kerberos/kerberos.h"
+#include "librpc/gen_ndr/ndr_drsblobs.h"
+#include "librpc/gen_ndr/ndr_lsa.h"
+#include "../lib/crypto/crypto.h"
+
+/*
+ this type allows us to distinguish handle types
+*/
+
+/*
+ state associated with a lsa_OpenAccount() operation
+*/
+struct lsa_account_state {
+ struct lsa_policy_state *policy;
+ uint32_t access_mask;
+ struct dom_sid *account_sid;
+};
+
+
+/*
+ state associated with a lsa_OpenSecret() operation
+*/
+struct lsa_secret_state {
+ struct lsa_policy_state *policy;
+ uint32_t access_mask;
+ struct ldb_dn *secret_dn;
+ struct ldb_context *sam_ldb;
+ bool global;
+};
+
+/*
+ state associated with a lsa_OpenTrustedDomain() operation
+*/
+struct lsa_trusted_domain_state {
+ struct lsa_policy_state *policy;
+ uint32_t access_mask;
+ struct ldb_dn *trusted_domain_dn;
+ struct ldb_dn *trusted_domain_user_dn;
+};
+
+static NTSTATUS dcesrv_lsa_EnumAccountRights(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_EnumAccountRights *r);
+
+static NTSTATUS dcesrv_lsa_AddRemoveAccountRights(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_policy_state *state,
+ int ldb_flag,
+ struct dom_sid *sid,
+ const struct lsa_RightSet *rights);
+
+/*
+ lsa_Close
+*/
+static NTSTATUS dcesrv_lsa_Close(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_Close *r)
+{
+ struct dcesrv_handle *h;
+
+ *r->out.handle = *r->in.handle;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, DCESRV_HANDLE_ANY);
+
+ talloc_free(h);
+
+ ZERO_STRUCTP(r->out.handle);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ lsa_Delete
+*/
+static NTSTATUS dcesrv_lsa_Delete(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_Delete *r)
+{
+ return NT_STATUS_NOT_SUPPORTED;
+}
+
+
+/*
+ lsa_DeleteObject
+*/
+static NTSTATUS dcesrv_lsa_DeleteObject(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_DeleteObject *r)
+{
+ struct dcesrv_handle *h;
+ int ret;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, DCESRV_HANDLE_ANY);
+
+ if (h->wire_handle.handle_type == LSA_HANDLE_SECRET) {
+ struct lsa_secret_state *secret_state = h->data;
+
+ /* Ensure user is permitted to delete this... */
+ switch (security_session_user_level(dce_call->conn->auth_state.session_info))
+ {
+ case SECURITY_SYSTEM:
+ case SECURITY_ADMINISTRATOR:
+ break;
+ default:
+ /* Users and annonymous are not allowed delete things */
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ ret = ldb_delete(secret_state->sam_ldb,
+ secret_state->secret_dn);
+ talloc_free(h);
+ if (ret != 0) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ ZERO_STRUCTP(r->out.handle);
+
+ return NT_STATUS_OK;
+ } else if (h->wire_handle.handle_type == LSA_HANDLE_TRUSTED_DOMAIN) {
+ struct lsa_trusted_domain_state *trusted_domain_state =
+ talloc_get_type(h->data, struct lsa_trusted_domain_state);
+ ret = ldb_transaction_start(trusted_domain_state->policy->sam_ldb);
+ if (ret != 0) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ ret = ldb_delete(trusted_domain_state->policy->sam_ldb,
+ trusted_domain_state->trusted_domain_dn);
+ if (ret != 0) {
+ ldb_transaction_cancel(trusted_domain_state->policy->sam_ldb);
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ if (trusted_domain_state->trusted_domain_user_dn) {
+ ret = ldb_delete(trusted_domain_state->policy->sam_ldb,
+ trusted_domain_state->trusted_domain_user_dn);
+ if (ret != 0) {
+ ldb_transaction_cancel(trusted_domain_state->policy->sam_ldb);
+ return NT_STATUS_INVALID_HANDLE;
+ }
+ }
+
+ ret = ldb_transaction_commit(trusted_domain_state->policy->sam_ldb);
+ if (ret != 0) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ talloc_free(h);
+ ZERO_STRUCTP(r->out.handle);
+
+ return NT_STATUS_OK;
+ } else if (h->wire_handle.handle_type == LSA_HANDLE_ACCOUNT) {
+ struct lsa_RightSet *rights;
+ struct lsa_account_state *astate;
+ struct lsa_EnumAccountRights r2;
+ NTSTATUS status;
+
+ rights = talloc(mem_ctx, struct lsa_RightSet);
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_ACCOUNT);
+
+ astate = h->data;
+
+ r2.in.handle = &astate->policy->handle->wire_handle;
+ r2.in.sid = astate->account_sid;
+ r2.out.rights = rights;
+
+ /* dcesrv_lsa_EnumAccountRights takes a LSA_HANDLE_POLICY,
+ but we have a LSA_HANDLE_ACCOUNT here, so this call
+ will always fail */
+ status = dcesrv_lsa_EnumAccountRights(dce_call, mem_ctx, &r2);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ return NT_STATUS_OK;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = dcesrv_lsa_AddRemoveAccountRights(dce_call, mem_ctx, astate->policy,
+ LDB_FLAG_MOD_DELETE, astate->account_sid,
+ r2.out.rights);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ return NT_STATUS_OK;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ ZERO_STRUCTP(r->out.handle);
+ }
+
+ return NT_STATUS_INVALID_HANDLE;
+}
+
+
+/*
+ lsa_EnumPrivs
+*/
+static NTSTATUS dcesrv_lsa_EnumPrivs(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_EnumPrivs *r)
+{
+ struct dcesrv_handle *h;
+ struct lsa_policy_state *state;
+ int i;
+ const char *privname;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_POLICY);
+
+ state = h->data;
+
+ i = *r->in.resume_handle;
+ if (i == 0) i = 1;
+
+ while ((privname = sec_privilege_name(i)) &&
+ r->out.privs->count < r->in.max_count) {
+ struct lsa_PrivEntry *e;
+
+ r->out.privs->privs = talloc_realloc(r->out.privs,
+ r->out.privs->privs,
+ struct lsa_PrivEntry,
+ r->out.privs->count+1);
+ if (r->out.privs->privs == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ e = &r->out.privs->privs[r->out.privs->count];
+ e->luid.low = i;
+ e->luid.high = 0;
+ e->name.string = privname;
+ r->out.privs->count++;
+ i++;
+ }
+
+ *r->out.resume_handle = i;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ lsa_QuerySecObj
+*/
+static NTSTATUS dcesrv_lsa_QuerySecurity(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_QuerySecurity *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_SetSecObj
+*/
+static NTSTATUS dcesrv_lsa_SetSecObj(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_SetSecObj *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_ChangePassword
+*/
+static NTSTATUS dcesrv_lsa_ChangePassword(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_ChangePassword *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+/*
+ dssetup_DsRoleGetPrimaryDomainInformation
+
+ This is not an LSA call, but is the only call left on the DSSETUP
+ pipe (after the pipe was truncated), and needs lsa_get_policy_state
+*/
+static WERROR dcesrv_dssetup_DsRoleGetPrimaryDomainInformation(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct dssetup_DsRoleGetPrimaryDomainInformation *r)
+{
+ union dssetup_DsRoleInfo *info;
+
+ info = talloc(mem_ctx, union dssetup_DsRoleInfo);
+ W_ERROR_HAVE_NO_MEMORY(info);
+
+ switch (r->in.level) {
+ case DS_ROLE_BASIC_INFORMATION:
+ {
+ enum dssetup_DsRole role = DS_ROLE_STANDALONE_SERVER;
+ uint32_t flags = 0;
+ const char *domain = NULL;
+ const char *dns_domain = NULL;
+ const char *forest = NULL;
+ struct GUID domain_guid;
+ struct lsa_policy_state *state;
+
+ NTSTATUS status = dcesrv_lsa_get_policy_state(dce_call, mem_ctx, &state);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ntstatus_to_werror(status);
+ }
+
+ ZERO_STRUCT(domain_guid);
+
+ switch (lp_server_role(dce_call->conn->dce_ctx->lp_ctx)) {
+ case ROLE_STANDALONE:
+ role = DS_ROLE_STANDALONE_SERVER;
+ break;
+ case ROLE_DOMAIN_MEMBER:
+ role = DS_ROLE_MEMBER_SERVER;
+ break;
+ case ROLE_DOMAIN_CONTROLLER:
+ if (samdb_is_pdc(state->sam_ldb)) {
+ role = DS_ROLE_PRIMARY_DC;
+ } else {
+ role = DS_ROLE_BACKUP_DC;
+ }
+ break;
+ }
+
+ switch (lp_server_role(dce_call->conn->dce_ctx->lp_ctx)) {
+ case ROLE_STANDALONE:
+ domain = talloc_strdup(mem_ctx, lp_workgroup(dce_call->conn->dce_ctx->lp_ctx));
+ W_ERROR_HAVE_NO_MEMORY(domain);
+ break;
+ case ROLE_DOMAIN_MEMBER:
+ domain = talloc_strdup(mem_ctx, lp_workgroup(dce_call->conn->dce_ctx->lp_ctx));
+ W_ERROR_HAVE_NO_MEMORY(domain);
+ /* TODO: what is with dns_domain and forest and guid? */
+ break;
+ case ROLE_DOMAIN_CONTROLLER:
+ flags = DS_ROLE_PRIMARY_DS_RUNNING;
+
+ if (state->mixed_domain == 1) {
+ flags |= DS_ROLE_PRIMARY_DS_MIXED_MODE;
+ }
+
+ domain = state->domain_name;
+ dns_domain = state->domain_dns;
+ forest = state->forest_dns;
+
+ domain_guid = state->domain_guid;
+ flags |= DS_ROLE_PRIMARY_DOMAIN_GUID_PRESENT;
+ break;
+ }
+
+ info->basic.role = role;
+ info->basic.flags = flags;
+ info->basic.domain = domain;
+ info->basic.dns_domain = dns_domain;
+ info->basic.forest = forest;
+ info->basic.domain_guid = domain_guid;
+
+ r->out.info = info;
+ return WERR_OK;
+ }
+ case DS_ROLE_UPGRADE_STATUS:
+ {
+ info->upgrade.upgrading = DS_ROLE_NOT_UPGRADING;
+ info->upgrade.previous_role = DS_ROLE_PREVIOUS_UNKNOWN;
+
+ r->out.info = info;
+ return WERR_OK;
+ }
+ case DS_ROLE_OP_STATUS:
+ {
+ info->opstatus.status = DS_ROLE_OP_IDLE;
+
+ r->out.info = info;
+ return WERR_OK;
+ }
+ default:
+ return WERR_INVALID_PARAM;
+ }
+
+ return WERR_INVALID_PARAM;
+}
+
+/*
+ fill in the AccountDomain info
+*/
+static NTSTATUS dcesrv_lsa_info_AccountDomain(struct lsa_policy_state *state, TALLOC_CTX *mem_ctx,
+ struct lsa_DomainInfo *info)
+{
+ info->name.string = state->domain_name;
+ info->sid = state->domain_sid;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ fill in the DNS domain info
+*/
+static NTSTATUS dcesrv_lsa_info_DNS(struct lsa_policy_state *state, TALLOC_CTX *mem_ctx,
+ struct lsa_DnsDomainInfo *info)
+{
+ info->name.string = state->domain_name;
+ info->sid = state->domain_sid;
+ info->dns_domain.string = state->domain_dns;
+ info->dns_forest.string = state->forest_dns;
+ info->domain_guid = state->domain_guid;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ lsa_QueryInfoPolicy2
+*/
+static NTSTATUS dcesrv_lsa_QueryInfoPolicy2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_QueryInfoPolicy2 *r)
+{
+ struct lsa_policy_state *state;
+ struct dcesrv_handle *h;
+ union lsa_PolicyInformation *info;
+
+ *r->out.info = NULL;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_POLICY);
+
+ state = h->data;
+
+ info = talloc_zero(mem_ctx, union lsa_PolicyInformation);
+ if (!info) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ *r->out.info = info;
+
+ switch (r->in.level) {
+ case LSA_POLICY_INFO_AUDIT_LOG:
+ /* we don't need to fill in any of this */
+ ZERO_STRUCT(info->audit_log);
+ return NT_STATUS_OK;
+ case LSA_POLICY_INFO_AUDIT_EVENTS:
+ /* we don't need to fill in any of this */
+ ZERO_STRUCT(info->audit_events);
+ return NT_STATUS_OK;
+ case LSA_POLICY_INFO_PD:
+ /* we don't need to fill in any of this */
+ ZERO_STRUCT(info->pd);
+ return NT_STATUS_OK;
+
+ case LSA_POLICY_INFO_DOMAIN:
+ return dcesrv_lsa_info_AccountDomain(state, mem_ctx, &info->domain);
+ case LSA_POLICY_INFO_ACCOUNT_DOMAIN:
+ return dcesrv_lsa_info_AccountDomain(state, mem_ctx, &info->account_domain);
+ case LSA_POLICY_INFO_L_ACCOUNT_DOMAIN:
+ return dcesrv_lsa_info_AccountDomain(state, mem_ctx, &info->l_account_domain);
+
+
+ case LSA_POLICY_INFO_ROLE:
+ info->role.role = LSA_ROLE_PRIMARY;
+ return NT_STATUS_OK;
+
+ case LSA_POLICY_INFO_DNS:
+ case LSA_POLICY_INFO_DNS_INT:
+ return dcesrv_lsa_info_DNS(state, mem_ctx, &info->dns);
+
+ case LSA_POLICY_INFO_REPLICA:
+ ZERO_STRUCT(info->replica);
+ return NT_STATUS_OK;
+
+ case LSA_POLICY_INFO_QUOTA:
+ ZERO_STRUCT(info->quota);
+ return NT_STATUS_OK;
+
+ case LSA_POLICY_INFO_MOD:
+ case LSA_POLICY_INFO_AUDIT_FULL_SET:
+ case LSA_POLICY_INFO_AUDIT_FULL_QUERY:
+ /* windows gives INVALID_PARAMETER */
+ *r->out.info = NULL;
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ *r->out.info = NULL;
+ return NT_STATUS_INVALID_INFO_CLASS;
+}
+
+/*
+ lsa_QueryInfoPolicy
+*/
+static NTSTATUS dcesrv_lsa_QueryInfoPolicy(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_QueryInfoPolicy *r)
+{
+ struct lsa_QueryInfoPolicy2 r2;
+ NTSTATUS status;
+
+ ZERO_STRUCT(r2);
+
+ r2.in.handle = r->in.handle;
+ r2.in.level = r->in.level;
+ r2.out.info = r->out.info;
+
+ status = dcesrv_lsa_QueryInfoPolicy2(dce_call, mem_ctx, &r2);
+
+ return status;
+}
+
+/*
+ lsa_SetInfoPolicy
+*/
+static NTSTATUS dcesrv_lsa_SetInfoPolicy(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_SetInfoPolicy *r)
+{
+ /* need to support this */
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_ClearAuditLog
+*/
+static NTSTATUS dcesrv_lsa_ClearAuditLog(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_ClearAuditLog *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_CreateAccount
+
+ This call does not seem to have any long-term effects, hence no database operations
+
+ we need to talk to the MS product group to find out what this account database means!
+
+ answer is that the lsa database is totally separate from the SAM and
+ ldap databases. We are going to need a separate ldb to store these
+ accounts. The SIDs on this account bear no relation to the SIDs in
+ AD
+*/
+static NTSTATUS dcesrv_lsa_CreateAccount(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_CreateAccount *r)
+{
+ struct lsa_account_state *astate;
+
+ struct lsa_policy_state *state;
+ struct dcesrv_handle *h, *ah;
+
+ ZERO_STRUCTP(r->out.acct_handle);
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_POLICY);
+
+ state = h->data;
+
+ astate = talloc(dce_call->conn, struct lsa_account_state);
+ if (astate == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ astate->account_sid = dom_sid_dup(astate, r->in.sid);
+ if (astate->account_sid == NULL) {
+ talloc_free(astate);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ astate->policy = talloc_reference(astate, state);
+ astate->access_mask = r->in.access_mask;
+
+ ah = dcesrv_handle_new(dce_call->context, LSA_HANDLE_ACCOUNT);
+ if (!ah) {
+ talloc_free(astate);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ah->data = talloc_steal(ah, astate);
+
+ *r->out.acct_handle = ah->wire_handle;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ lsa_EnumAccounts
+*/
+static NTSTATUS dcesrv_lsa_EnumAccounts(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_EnumAccounts *r)
+{
+ struct dcesrv_handle *h;
+ struct lsa_policy_state *state;
+ int ret, i;
+ struct ldb_message **res;
+ const char * const attrs[] = { "objectSid", NULL};
+ uint32_t count;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_POLICY);
+
+ state = h->data;
+
+ /* NOTE: This call must only return accounts that have at least
+ one privilege set
+ */
+ ret = gendb_search(state->sam_ldb, mem_ctx, NULL, &res, attrs,
+ "(&(objectSid=*)(privilege=*))");
+ if (ret < 0) {
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ if (*r->in.resume_handle >= ret) {
+ return NT_STATUS_NO_MORE_ENTRIES;
+ }
+
+ count = ret - *r->in.resume_handle;
+ if (count > r->in.num_entries) {
+ count = r->in.num_entries;
+ }
+
+ if (count == 0) {
+ return NT_STATUS_NO_MORE_ENTRIES;
+ }
+
+ r->out.sids->sids = talloc_array(r->out.sids, struct lsa_SidPtr, count);
+ if (r->out.sids->sids == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0;i<count;i++) {
+ r->out.sids->sids[i].sid =
+ samdb_result_dom_sid(r->out.sids->sids,
+ res[i + *r->in.resume_handle],
+ "objectSid");
+ NT_STATUS_HAVE_NO_MEMORY(r->out.sids->sids[i].sid);
+ }
+
+ r->out.sids->num_sids = count;
+ *r->out.resume_handle = count + *r->in.resume_handle;
+
+ return NT_STATUS_OK;
+
+}
+
+
+/*
+ lsa_CreateTrustedDomainEx2
+*/
+static NTSTATUS dcesrv_lsa_CreateTrustedDomain_base(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_CreateTrustedDomainEx2 *r,
+ int op)
+{
+ struct dcesrv_handle *policy_handle;
+ struct lsa_policy_state *policy_state;
+ struct lsa_trusted_domain_state *trusted_domain_state;
+ struct dcesrv_handle *handle;
+ struct ldb_message **msgs, *msg, *msg_user;
+ const char *attrs[] = {
+ NULL
+ };
+ const char *netbios_name;
+ const char *dns_name;
+ const char *name;
+ DATA_BLOB session_key = data_blob(NULL, 0);
+ DATA_BLOB trustAuthIncoming, trustAuthOutgoing, auth_blob;
+ struct trustDomainPasswords auth_struct;
+ int ret;
+ NTSTATUS nt_status;
+ enum ndr_err_code ndr_err;
+
+ DCESRV_PULL_HANDLE(policy_handle, r->in.policy_handle, LSA_HANDLE_POLICY);
+ ZERO_STRUCTP(r->out.trustdom_handle);
+
+ policy_state = policy_handle->data;
+
+ nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ netbios_name = r->in.info->netbios_name.string;
+ if (!netbios_name) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ dns_name = r->in.info->domain_name.string;
+
+ trusted_domain_state = talloc_zero(mem_ctx, struct lsa_trusted_domain_state);
+ if (!trusted_domain_state) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ trusted_domain_state->policy = policy_state;
+
+ if (strcasecmp(netbios_name, "BUILTIN") == 0
+ || (dns_name && strcasecmp(dns_name, "BUILTIN") == 0)
+ || (dom_sid_in_domain(policy_state->builtin_sid, r->in.info->sid))) {
+ return NT_STATUS_INVALID_PARAMETER;;
+ }
+
+ if (strcasecmp(netbios_name, policy_state->domain_name) == 0
+ || strcasecmp(netbios_name, policy_state->domain_dns) == 0
+ || (dns_name && strcasecmp(dns_name, policy_state->domain_dns) == 0)
+ || (dns_name && strcasecmp(dns_name, policy_state->domain_name) == 0)
+ || (dom_sid_equal(policy_state->domain_sid, r->in.info->sid))) {
+ return NT_STATUS_CURRENT_DOMAIN_NOT_ALLOWED;
+ }
+
+ /* While this is a REF pointer, some of the functions that wrap this don't provide this */
+ if (op == NDR_LSA_CREATETRUSTEDDOMAIN) {
+ /* No secrets are created at this time, for this function */
+ auth_struct.outgoing.count = 0;
+ auth_struct.incoming.count = 0;
+ } else {
+ auth_blob = data_blob_const(r->in.auth_info->auth_blob.data, r->in.auth_info->auth_blob.size);
+ arcfour_crypt_blob(auth_blob.data, auth_blob.length, &session_key);
+ ndr_err = ndr_pull_struct_blob(&auth_blob, mem_ctx,
+ lp_iconv_convenience(dce_call->conn->dce_ctx->lp_ctx),
+ &auth_struct,
+ (ndr_pull_flags_fn_t)ndr_pull_trustDomainPasswords);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (op == NDR_LSA_CREATETRUSTEDDOMAINEX) {
+ if (auth_struct.incoming.count > 1) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ }
+ }
+
+ if (auth_struct.incoming.count) {
+ int i;
+ struct trustAuthInOutBlob incoming;
+
+ incoming.count = auth_struct.incoming.count;
+ incoming.current = talloc(mem_ctx, struct AuthenticationInformationArray);
+ if (!incoming.current) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ incoming.current->array = *auth_struct.incoming.current;
+ if (!incoming.current->array) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ incoming.previous = talloc(mem_ctx, struct AuthenticationInformationArray);
+ if (!incoming.previous) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ incoming.previous->array = talloc_array(mem_ctx, struct AuthenticationInformation, incoming.count);
+ if (!incoming.previous->array) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i = 0; i < incoming.count; i++) {
+ incoming.previous->array[i].LastUpdateTime = 0;
+ incoming.previous->array[i].AuthType = 0;
+ }
+ ndr_err = ndr_push_struct_blob(&trustAuthIncoming, mem_ctx,
+ lp_iconv_convenience(dce_call->conn->dce_ctx->lp_ctx),
+ &incoming,
+ (ndr_push_flags_fn_t)ndr_push_trustAuthInOutBlob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ } else {
+ trustAuthIncoming = data_blob(NULL, 0);
+ }
+
+ if (auth_struct.outgoing.count) {
+ int i;
+ struct trustAuthInOutBlob outgoing;
+
+ outgoing.count = auth_struct.outgoing.count;
+ outgoing.current = talloc(mem_ctx, struct AuthenticationInformationArray);
+ if (!outgoing.current) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ outgoing.current->array = *auth_struct.outgoing.current;
+ if (!outgoing.current->array) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ outgoing.previous = talloc(mem_ctx, struct AuthenticationInformationArray);
+ if (!outgoing.previous) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ outgoing.previous->array = talloc_array(mem_ctx, struct AuthenticationInformation, outgoing.count);
+ if (!outgoing.previous->array) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i = 0; i < outgoing.count; i++) {
+ outgoing.previous->array[i].LastUpdateTime = 0;
+ outgoing.previous->array[i].AuthType = 0;
+ }
+ ndr_err = ndr_push_struct_blob(&trustAuthOutgoing, mem_ctx,
+ lp_iconv_convenience(dce_call->conn->dce_ctx->lp_ctx),
+ &outgoing,
+ (ndr_push_flags_fn_t)ndr_push_trustAuthInOutBlob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ } else {
+ trustAuthOutgoing = data_blob(NULL, 0);
+ }
+
+ ret = ldb_transaction_start(policy_state->sam_ldb);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ if (dns_name) {
+ char *dns_encoded = ldb_binary_encode_string(mem_ctx, netbios_name);
+ char *netbios_encoded = ldb_binary_encode_string(mem_ctx, netbios_name);
+ /* search for the trusted_domain record */
+ ret = gendb_search(policy_state->sam_ldb,
+ mem_ctx, policy_state->system_dn, &msgs, attrs,
+ "(&(|(flatname=%s)(cn=%s)(trustPartner=%s)(flatname=%s)(cn=%s)(trustPartner=%s))(objectclass=trustedDomain))",
+ dns_encoded, dns_encoded, dns_encoded, netbios_encoded, netbios_encoded, netbios_encoded);
+ if (ret > 0) {
+ ldb_transaction_cancel(policy_state->sam_ldb);
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+ } else {
+ char *netbios_encoded = ldb_binary_encode_string(mem_ctx, netbios_name);
+ /* search for the trusted_domain record */
+ ret = gendb_search(policy_state->sam_ldb,
+ mem_ctx, policy_state->system_dn, &msgs, attrs,
+ "(&(|(flatname=%s)(cn=%s)(trustPartner=%s))(objectclass=trustedDomain))",
+ netbios_encoded, netbios_encoded, netbios_encoded);
+ if (ret > 0) {
+ ldb_transaction_cancel(policy_state->sam_ldb);
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+ }
+
+ if (ret < 0 ) {
+ ldb_transaction_cancel(policy_state->sam_ldb);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ name = dns_name ? dns_name : netbios_name;
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ msg->dn = ldb_dn_copy(mem_ctx, policy_state->system_dn);
+ if ( ! ldb_dn_add_child_fmt(msg->dn, "cn=%s", name)) {
+ ldb_transaction_cancel(policy_state->sam_ldb);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ samdb_msg_add_string(trusted_domain_state->policy->sam_ldb, mem_ctx, msg, "flatname", netbios_name);
+
+ if (r->in.info->sid) {
+ const char *sid_string = dom_sid_string(mem_ctx, r->in.info->sid);
+ if (!sid_string) {
+ ldb_transaction_cancel(policy_state->sam_ldb);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ samdb_msg_add_string(trusted_domain_state->policy->sam_ldb, mem_ctx, msg, "securityIdentifier", sid_string);
+ }
+
+ samdb_msg_add_string(trusted_domain_state->policy->sam_ldb, mem_ctx, msg, "objectClass", "trustedDomain");
+
+ samdb_msg_add_int(trusted_domain_state->policy->sam_ldb, mem_ctx, msg, "trustType", r->in.info->trust_type);
+
+ samdb_msg_add_int(trusted_domain_state->policy->sam_ldb, mem_ctx, msg, "trustAttributes", r->in.info->trust_attributes);
+
+ samdb_msg_add_int(trusted_domain_state->policy->sam_ldb, mem_ctx, msg, "trustDirection", r->in.info->trust_direction);
+
+ if (dns_name) {
+ samdb_msg_add_string(trusted_domain_state->policy->sam_ldb, mem_ctx, msg, "trustPartner", dns_name);
+ }
+
+ if (trustAuthIncoming.data) {
+ ret = ldb_msg_add_value(msg, "trustAuthIncoming", &trustAuthIncoming, NULL);
+ if (ret != LDB_SUCCESS) {
+ ldb_transaction_cancel(policy_state->sam_ldb);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+ if (trustAuthOutgoing.data) {
+ ret = ldb_msg_add_value(msg, "trustAuthOutgoing", &trustAuthOutgoing, NULL);
+ if (ret != LDB_SUCCESS) {
+ ldb_transaction_cancel(policy_state->sam_ldb);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ trusted_domain_state->trusted_domain_dn = talloc_reference(trusted_domain_state, msg->dn);
+
+ /* create the trusted_domain */
+ ret = ldb_add(trusted_domain_state->policy->sam_ldb, msg);
+ switch (ret) {
+ case LDB_SUCCESS:
+ break;
+ case LDB_ERR_ENTRY_ALREADY_EXISTS:
+ ldb_transaction_cancel(trusted_domain_state->policy->sam_ldb);
+ DEBUG(0,("Failed to create trusted domain record %s: %s\n",
+ ldb_dn_get_linearized(msg->dn),
+ ldb_errstring(trusted_domain_state->policy->sam_ldb)));
+ return NT_STATUS_DOMAIN_EXISTS;
+ case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
+ ldb_transaction_cancel(trusted_domain_state->policy->sam_ldb);
+ DEBUG(0,("Failed to create trusted domain record %s: %s\n",
+ ldb_dn_get_linearized(msg->dn),
+ ldb_errstring(trusted_domain_state->policy->sam_ldb)));
+ return NT_STATUS_ACCESS_DENIED;
+ default:
+ ldb_transaction_cancel(trusted_domain_state->policy->sam_ldb);
+ DEBUG(0,("Failed to create user record %s: %s\n",
+ ldb_dn_get_linearized(msg->dn),
+ ldb_errstring(trusted_domain_state->policy->sam_ldb)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ if (r->in.info->trust_direction & LSA_TRUST_DIRECTION_INBOUND) {
+ msg_user = ldb_msg_new(mem_ctx);
+ if (msg_user == NULL) {
+ ldb_transaction_cancel(trusted_domain_state->policy->sam_ldb);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* Inbound trusts must also create a cn=users object to match */
+
+ trusted_domain_state->trusted_domain_user_dn = msg_user->dn
+ = ldb_dn_copy(trusted_domain_state, policy_state->domain_dn);
+ if ( ! ldb_dn_add_child_fmt(msg_user->dn, "cn=users")) {
+ ldb_transaction_cancel(policy_state->sam_ldb);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if ( ! ldb_dn_add_child_fmt(msg_user->dn, "cn=%s", netbios_name)) {
+ ldb_transaction_cancel(policy_state->sam_ldb);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ldb_msg_add_string(msg_user, "objectClass", "user");
+
+ ldb_msg_add_steal_string(msg_user, "samAccountName",
+ talloc_asprintf(mem_ctx, "%s$", netbios_name));
+
+ if (samdb_msg_add_uint(trusted_domain_state->policy->sam_ldb, mem_ctx, msg_user,
+ "userAccountControl",
+ UF_INTERDOMAIN_TRUST_ACCOUNT) != 0) {
+ ldb_transaction_cancel(policy_state->sam_ldb);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (auth_struct.incoming.count) {
+ int i;
+ for (i=0; i < auth_struct.incoming.count; i++ ) {
+ if (auth_struct.incoming.current[i]->AuthType == TRUST_AUTH_TYPE_NT4OWF) {
+ samdb_msg_add_hash(trusted_domain_state->policy->sam_ldb,
+ mem_ctx, msg_user, "unicodePwd",
+ &auth_struct.incoming.current[i]->AuthInfo.nt4owf.password);
+ } else if (auth_struct.incoming.current[i]->AuthType == TRUST_AUTH_TYPE_CLEAR) {
+ DATA_BLOB new_password = data_blob_const(auth_struct.incoming.current[i]->AuthInfo.clear.password,
+ auth_struct.incoming.current[i]->AuthInfo.clear.size);
+ ret = ldb_msg_add_value(msg_user, "clearTextPassword", &new_password, NULL);
+ if (ret != LDB_SUCCESS) {
+ ldb_transaction_cancel(policy_state->sam_ldb);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+ }
+ }
+
+ /* create the cn=users trusted_domain account */
+ ret = ldb_add(trusted_domain_state->policy->sam_ldb, msg_user);
+ switch (ret) {
+ case LDB_SUCCESS:
+ break;
+ case LDB_ERR_ENTRY_ALREADY_EXISTS:
+ ldb_transaction_cancel(trusted_domain_state->policy->sam_ldb);
+ DEBUG(0,("Failed to create trusted domain record %s: %s\n",
+ ldb_dn_get_linearized(msg_user->dn),
+ ldb_errstring(trusted_domain_state->policy->sam_ldb)));
+ return NT_STATUS_DOMAIN_EXISTS;
+ case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
+ ldb_transaction_cancel(trusted_domain_state->policy->sam_ldb);
+ DEBUG(0,("Failed to create trusted domain record %s: %s\n",
+ ldb_dn_get_linearized(msg_user->dn),
+ ldb_errstring(trusted_domain_state->policy->sam_ldb)));
+ return NT_STATUS_ACCESS_DENIED;
+ default:
+ ldb_transaction_cancel(trusted_domain_state->policy->sam_ldb);
+ DEBUG(0,("Failed to create user record %s: %s\n",
+ ldb_dn_get_linearized(msg_user->dn),
+ ldb_errstring(trusted_domain_state->policy->sam_ldb)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ }
+
+ ret = ldb_transaction_commit(policy_state->sam_ldb);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ handle = dcesrv_handle_new(dce_call->context, LSA_HANDLE_TRUSTED_DOMAIN);
+ if (!handle) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ handle->data = talloc_steal(handle, trusted_domain_state);
+
+ trusted_domain_state->access_mask = r->in.access_mask;
+ trusted_domain_state->policy = talloc_reference(trusted_domain_state, policy_state);
+
+ *r->out.trustdom_handle = handle->wire_handle;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ lsa_CreateTrustedDomainEx2
+*/
+static NTSTATUS dcesrv_lsa_CreateTrustedDomainEx2(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_CreateTrustedDomainEx2 *r)
+{
+ return dcesrv_lsa_CreateTrustedDomain_base(dce_call, mem_ctx, r, NDR_LSA_CREATETRUSTEDDOMAINEX2);
+}
+/*
+ lsa_CreateTrustedDomainEx
+*/
+static NTSTATUS dcesrv_lsa_CreateTrustedDomainEx(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_CreateTrustedDomainEx *r)
+{
+ struct lsa_CreateTrustedDomainEx2 r2;
+
+ r2.in.policy_handle = r->in.policy_handle;
+ r2.in.info = r->in.info;
+ r2.in.auth_info = r->in.auth_info;
+ r2.out.trustdom_handle = r->out.trustdom_handle;
+ return dcesrv_lsa_CreateTrustedDomain_base(dce_call, mem_ctx, &r2, NDR_LSA_CREATETRUSTEDDOMAINEX);
+}
+
+/*
+ lsa_CreateTrustedDomain
+*/
+static NTSTATUS dcesrv_lsa_CreateTrustedDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_CreateTrustedDomain *r)
+{
+ struct lsa_CreateTrustedDomainEx2 r2;
+
+ r2.in.policy_handle = r->in.policy_handle;
+ r2.in.info = talloc(mem_ctx, struct lsa_TrustDomainInfoInfoEx);
+ if (!r2.in.info) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ r2.in.info->domain_name.string = NULL;
+ r2.in.info->netbios_name = r->in.info->name;
+ r2.in.info->sid = r->in.info->sid;
+ r2.in.info->trust_direction = LSA_TRUST_DIRECTION_OUTBOUND;
+ r2.in.info->trust_type = LSA_TRUST_TYPE_DOWNLEVEL;
+ r2.in.info->trust_attributes = 0;
+
+ r2.in.access_mask = r->in.access_mask;
+ r2.out.trustdom_handle = r->out.trustdom_handle;
+
+ return dcesrv_lsa_CreateTrustedDomain_base(dce_call, mem_ctx, &r2, NDR_LSA_CREATETRUSTEDDOMAIN);
+
+}
+
+/*
+ lsa_OpenTrustedDomain
+*/
+static NTSTATUS dcesrv_lsa_OpenTrustedDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_OpenTrustedDomain *r)
+{
+ struct dcesrv_handle *policy_handle;
+
+ struct lsa_policy_state *policy_state;
+ struct lsa_trusted_domain_state *trusted_domain_state;
+ struct dcesrv_handle *handle;
+ struct ldb_message **msgs;
+ const char *attrs[] = {
+ "trustDirection",
+ "flatname",
+ NULL
+ };
+
+ const char *sid_string;
+ int ret;
+
+ DCESRV_PULL_HANDLE(policy_handle, r->in.handle, LSA_HANDLE_POLICY);
+ ZERO_STRUCTP(r->out.trustdom_handle);
+ policy_state = policy_handle->data;
+
+ trusted_domain_state = talloc_zero(mem_ctx, struct lsa_trusted_domain_state);
+ if (!trusted_domain_state) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ trusted_domain_state->policy = policy_state;
+
+ sid_string = dom_sid_string(mem_ctx, r->in.sid);
+ if (!sid_string) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* search for the trusted_domain record */
+ ret = gendb_search(trusted_domain_state->policy->sam_ldb,
+ mem_ctx, policy_state->system_dn, &msgs, attrs,
+ "(&(securityIdentifier=%s)(objectclass=trustedDomain))",
+ sid_string);
+ if (ret == 0) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ if (ret != 1) {
+ DEBUG(0,("Found %d records matching DN %s\n", ret,
+ ldb_dn_get_linearized(policy_state->system_dn)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ trusted_domain_state->trusted_domain_dn = talloc_reference(trusted_domain_state, msgs[0]->dn);
+
+ trusted_domain_state->trusted_domain_user_dn = NULL;
+
+ if (ldb_msg_find_attr_as_int(msgs[0], "trustDirection", 0) & LSA_TRUST_DIRECTION_INBOUND) {
+ const char *flatname = ldb_binary_encode_string(mem_ctx, ldb_msg_find_attr_as_string(msgs[0], "flatname", NULL));
+ /* search for the trusted_domain record */
+ ret = gendb_search(trusted_domain_state->policy->sam_ldb,
+ mem_ctx, policy_state->domain_dn, &msgs, attrs,
+ "(&(samaccountname=%s$)(objectclass=user)(userAccountControl:1.2.840.113556.1.4.803:=%d))",
+ flatname, UF_INTERDOMAIN_TRUST_ACCOUNT);
+ if (ret == 1) {
+ trusted_domain_state->trusted_domain_user_dn = talloc_steal(trusted_domain_state, msgs[0]->dn);
+ }
+ }
+ handle = dcesrv_handle_new(dce_call->context, LSA_HANDLE_TRUSTED_DOMAIN);
+ if (!handle) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ handle->data = talloc_steal(handle, trusted_domain_state);
+
+ trusted_domain_state->access_mask = r->in.access_mask;
+ trusted_domain_state->policy = talloc_reference(trusted_domain_state, policy_state);
+
+ *r->out.trustdom_handle = handle->wire_handle;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ lsa_OpenTrustedDomainByName
+*/
+static NTSTATUS dcesrv_lsa_OpenTrustedDomainByName(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_OpenTrustedDomainByName *r)
+{
+ struct dcesrv_handle *policy_handle;
+
+ struct lsa_policy_state *policy_state;
+ struct lsa_trusted_domain_state *trusted_domain_state;
+ struct dcesrv_handle *handle;
+ struct ldb_message **msgs;
+ const char *attrs[] = {
+ NULL
+ };
+
+ int ret;
+
+ DCESRV_PULL_HANDLE(policy_handle, r->in.handle, LSA_HANDLE_POLICY);
+ ZERO_STRUCTP(r->out.trustdom_handle);
+ policy_state = policy_handle->data;
+
+ if (!r->in.name.string) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ trusted_domain_state = talloc_zero(mem_ctx, struct lsa_trusted_domain_state);
+ if (!trusted_domain_state) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ trusted_domain_state->policy = policy_state;
+
+ /* search for the trusted_domain record */
+ ret = gendb_search(trusted_domain_state->policy->sam_ldb,
+ mem_ctx, policy_state->system_dn, &msgs, attrs,
+ "(&(flatname=%s)(objectclass=trustedDomain))",
+ ldb_binary_encode_string(mem_ctx, r->in.name.string));
+ if (ret == 0) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ if (ret != 1) {
+ DEBUG(0,("Found %d records matching DN %s\n", ret,
+ ldb_dn_get_linearized(policy_state->system_dn)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ trusted_domain_state->trusted_domain_dn = talloc_reference(trusted_domain_state, msgs[0]->dn);
+
+ handle = dcesrv_handle_new(dce_call->context, LSA_HANDLE_TRUSTED_DOMAIN);
+ if (!handle) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ handle->data = talloc_steal(handle, trusted_domain_state);
+
+ trusted_domain_state->access_mask = r->in.access_mask;
+ trusted_domain_state->policy = talloc_reference(trusted_domain_state, policy_state);
+
+ *r->out.trustdom_handle = handle->wire_handle;
+
+ return NT_STATUS_OK;
+}
+
+
+
+/*
+ lsa_SetTrustedDomainInfo
+*/
+static NTSTATUS dcesrv_lsa_SetTrustedDomainInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_SetTrustedDomainInfo *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+
+/*
+ lsa_SetInfomrationTrustedDomain
+*/
+static NTSTATUS dcesrv_lsa_SetInformationTrustedDomain(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_SetInformationTrustedDomain *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_DeleteTrustedDomain
+*/
+static NTSTATUS dcesrv_lsa_DeleteTrustedDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_DeleteTrustedDomain *r)
+{
+ NTSTATUS status;
+ struct lsa_OpenTrustedDomain opn;
+ struct lsa_DeleteObject del;
+ struct dcesrv_handle *h;
+
+ opn.in.handle = r->in.handle;
+ opn.in.sid = r->in.dom_sid;
+ opn.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ opn.out.trustdom_handle = talloc(mem_ctx, struct policy_handle);
+ if (!opn.out.trustdom_handle) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ status = dcesrv_lsa_OpenTrustedDomain(dce_call, mem_ctx, &opn);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ DCESRV_PULL_HANDLE(h, opn.out.trustdom_handle, DCESRV_HANDLE_ANY);
+ talloc_steal(mem_ctx, h);
+
+ del.in.handle = opn.out.trustdom_handle;
+ del.out.handle = opn.out.trustdom_handle;
+ status = dcesrv_lsa_DeleteObject(dce_call, mem_ctx, &del);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS fill_trust_domain_ex(TALLOC_CTX *mem_ctx,
+ struct ldb_message *msg,
+ struct lsa_TrustDomainInfoInfoEx *info_ex)
+{
+ info_ex->domain_name.string
+ = ldb_msg_find_attr_as_string(msg, "trustPartner", NULL);
+ info_ex->netbios_name.string
+ = ldb_msg_find_attr_as_string(msg, "flatname", NULL);
+ info_ex->sid
+ = samdb_result_dom_sid(mem_ctx, msg, "securityIdentifier");
+ info_ex->trust_direction
+ = ldb_msg_find_attr_as_int(msg, "trustDirection", 0);
+ info_ex->trust_type
+ = ldb_msg_find_attr_as_int(msg, "trustType", 0);
+ info_ex->trust_attributes
+ = ldb_msg_find_attr_as_int(msg, "trustAttributes", 0);
+ return NT_STATUS_OK;
+}
+
+/*
+ lsa_QueryTrustedDomainInfo
+*/
+static NTSTATUS dcesrv_lsa_QueryTrustedDomainInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_QueryTrustedDomainInfo *r)
+{
+ union lsa_TrustedDomainInfo *info = NULL;
+ struct dcesrv_handle *h;
+ struct lsa_trusted_domain_state *trusted_domain_state;
+ struct ldb_message *msg;
+ int ret;
+ struct ldb_message **res;
+ const char *attrs[] = {
+ "flatname",
+ "trustPartner",
+ "securityIdentifier",
+ "trustDirection",
+ "trustType",
+ "trustAttributes",
+ "msDs-supportedEncryptionTypes",
+ NULL
+ };
+
+ DCESRV_PULL_HANDLE(h, r->in.trustdom_handle, LSA_HANDLE_TRUSTED_DOMAIN);
+
+ trusted_domain_state = talloc_get_type(h->data, struct lsa_trusted_domain_state);
+
+ /* pull all the user attributes */
+ ret = gendb_search_dn(trusted_domain_state->policy->sam_ldb, mem_ctx,
+ trusted_domain_state->trusted_domain_dn, &res, attrs);
+ if (ret != 1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ msg = res[0];
+
+ info = talloc_zero(mem_ctx, union lsa_TrustedDomainInfo);
+ if (!info) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ *r->out.info = info;
+
+ switch (r->in.level) {
+ case LSA_TRUSTED_DOMAIN_INFO_NAME:
+ info->name.netbios_name.string
+ = samdb_result_string(msg, "flatname", NULL);
+ break;
+ case LSA_TRUSTED_DOMAIN_INFO_POSIX_OFFSET:
+ info->posix_offset.posix_offset
+ = samdb_result_uint(msg, "posixOffset", 0);
+ break;
+#if 0 /* Win2k3 doesn't implement this */
+ case LSA_TRUSTED_DOMAIN_INFO_BASIC:
+ r->out.info->info_basic.netbios_name.string
+ = ldb_msg_find_attr_as_string(msg, "flatname", NULL);
+ r->out.info->info_basic.sid
+ = samdb_result_dom_sid(mem_ctx, msg, "securityIdentifier");
+ break;
+#endif
+ case LSA_TRUSTED_DOMAIN_INFO_INFO_EX:
+ return fill_trust_domain_ex(mem_ctx, msg, &info->info_ex);
+
+ case LSA_TRUSTED_DOMAIN_INFO_FULL_INFO:
+ ZERO_STRUCT(info->full_info);
+ return fill_trust_domain_ex(mem_ctx, msg, &info->full_info.info_ex);
+
+ case LSA_TRUSTED_DOMAIN_INFO_FULL_INFO_2_INTERNAL:
+ ZERO_STRUCT(info->full_info2_internal);
+ info->full_info2_internal.posix_offset.posix_offset
+ = samdb_result_uint(msg, "posixOffset", 0);
+ return fill_trust_domain_ex(mem_ctx, msg, &info->full_info2_internal.info.info_ex);
+
+ case LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRTYPION_TYPES:
+ info->enc_types.enc_types
+ = samdb_result_uint(msg, "msDs-supportedEncryptionTypes", KERB_ENCTYPE_RC4_HMAC_MD5);
+ break;
+
+ case LSA_TRUSTED_DOMAIN_INFO_CONTROLLERS:
+ case LSA_TRUSTED_DOMAIN_INFO_INFO_EX2_INTERNAL:
+ /* oops, we don't want to return the info after all */
+ talloc_free(info);
+ *r->out.info = NULL;
+ return NT_STATUS_INVALID_PARAMETER;
+ default:
+ /* oops, we don't want to return the info after all */
+ talloc_free(info);
+ *r->out.info = NULL;
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ lsa_QueryTrustedDomainInfoBySid
+*/
+static NTSTATUS dcesrv_lsa_QueryTrustedDomainInfoBySid(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_QueryTrustedDomainInfoBySid *r)
+{
+ NTSTATUS status;
+ struct lsa_OpenTrustedDomain opn;
+ struct lsa_QueryTrustedDomainInfo query;
+ struct dcesrv_handle *h;
+
+ opn.in.handle = r->in.handle;
+ opn.in.sid = r->in.dom_sid;
+ opn.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ opn.out.trustdom_handle = talloc(mem_ctx, struct policy_handle);
+ if (!opn.out.trustdom_handle) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ status = dcesrv_lsa_OpenTrustedDomain(dce_call, mem_ctx, &opn);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* Ensure this handle goes away at the end of this call */
+ DCESRV_PULL_HANDLE(h, opn.out.trustdom_handle, DCESRV_HANDLE_ANY);
+ talloc_steal(mem_ctx, h);
+
+ query.in.trustdom_handle = opn.out.trustdom_handle;
+ query.in.level = r->in.level;
+ query.out.info = r->out.info;
+ status = dcesrv_lsa_QueryTrustedDomainInfo(dce_call, mem_ctx, &query);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ lsa_SetTrustedDomainInfoByName
+*/
+static NTSTATUS dcesrv_lsa_SetTrustedDomainInfoByName(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_SetTrustedDomainInfoByName *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+/*
+ lsa_QueryTrustedDomainInfoByName
+*/
+static NTSTATUS dcesrv_lsa_QueryTrustedDomainInfoByName(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_QueryTrustedDomainInfoByName *r)
+{
+ NTSTATUS status;
+ struct lsa_OpenTrustedDomainByName opn;
+ struct lsa_QueryTrustedDomainInfo query;
+ struct dcesrv_handle *h;
+
+ opn.in.handle = r->in.handle;
+ opn.in.name = *r->in.trusted_domain;
+ opn.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ opn.out.trustdom_handle = talloc(mem_ctx, struct policy_handle);
+ if (!opn.out.trustdom_handle) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ status = dcesrv_lsa_OpenTrustedDomainByName(dce_call, mem_ctx, &opn);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* Ensure this handle goes away at the end of this call */
+ DCESRV_PULL_HANDLE(h, opn.out.trustdom_handle, DCESRV_HANDLE_ANY);
+ talloc_steal(mem_ctx, h);
+
+ query.in.trustdom_handle = opn.out.trustdom_handle;
+ query.in.level = r->in.level;
+ query.out.info = r->out.info;
+ status = dcesrv_lsa_QueryTrustedDomainInfo(dce_call, mem_ctx, &query);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ lsa_CloseTrustedDomainEx
+*/
+static NTSTATUS dcesrv_lsa_CloseTrustedDomainEx(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_CloseTrustedDomainEx *r)
+{
+ /* The result of a bad hair day from an IDL programmer? Not
+ * implmented in Win2k3. You should always just lsa_Close
+ * anyway. */
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+
+/*
+ comparison function for sorting lsa_DomainInformation array
+*/
+static int compare_DomainInfo(struct lsa_DomainInfo *e1, struct lsa_DomainInfo *e2)
+{
+ return strcasecmp_m(e1->name.string, e2->name.string);
+}
+
+/*
+ lsa_EnumTrustDom
+*/
+static NTSTATUS dcesrv_lsa_EnumTrustDom(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_EnumTrustDom *r)
+{
+ struct dcesrv_handle *policy_handle;
+ struct lsa_DomainInfo *entries;
+ struct lsa_policy_state *policy_state;
+ struct ldb_message **domains;
+ const char *attrs[] = {
+ "flatname",
+ "securityIdentifier",
+ NULL
+ };
+
+
+ int count, i;
+
+ *r->out.resume_handle = 0;
+
+ r->out.domains->domains = NULL;
+ r->out.domains->count = 0;
+
+ DCESRV_PULL_HANDLE(policy_handle, r->in.handle, LSA_HANDLE_POLICY);
+
+ policy_state = policy_handle->data;
+
+ /* search for all users in this domain. This could possibly be cached and
+ resumed based on resume_key */
+ count = gendb_search(policy_state->sam_ldb, mem_ctx, policy_state->system_dn, &domains, attrs,
+ "objectclass=trustedDomain");
+ if (count == -1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ /* convert to lsa_TrustInformation format */
+ entries = talloc_array(mem_ctx, struct lsa_DomainInfo, count);
+ if (!entries) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ for (i=0;i<count;i++) {
+ entries[i].sid = samdb_result_dom_sid(mem_ctx, domains[i], "securityIdentifier");
+ entries[i].name.string = samdb_result_string(domains[i], "flatname", NULL);
+ }
+
+ /* sort the results by name */
+ qsort(entries, count, sizeof(*entries),
+ (comparison_fn_t)compare_DomainInfo);
+
+ if (*r->in.resume_handle >= count) {
+ *r->out.resume_handle = -1;
+
+ return NT_STATUS_NO_MORE_ENTRIES;
+ }
+
+ /* return the rest, limit by max_size. Note that we
+ use the w2k3 element size value of 60 */
+ r->out.domains->count = count - *r->in.resume_handle;
+ r->out.domains->count = MIN(r->out.domains->count,
+ 1+(r->in.max_size/LSA_ENUM_TRUST_DOMAIN_MULTIPLIER));
+
+ r->out.domains->domains = entries + *r->in.resume_handle;
+ r->out.domains->count = r->out.domains->count;
+
+ if (r->out.domains->count < count - *r->in.resume_handle) {
+ *r->out.resume_handle = *r->in.resume_handle + r->out.domains->count;
+ return STATUS_MORE_ENTRIES;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ comparison function for sorting lsa_DomainInformation array
+*/
+static int compare_TrustDomainInfoInfoEx(struct lsa_TrustDomainInfoInfoEx *e1, struct lsa_TrustDomainInfoInfoEx *e2)
+{
+ return strcasecmp_m(e1->netbios_name.string, e2->netbios_name.string);
+}
+
+/*
+ lsa_EnumTrustedDomainsEx
+*/
+static NTSTATUS dcesrv_lsa_EnumTrustedDomainsEx(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_EnumTrustedDomainsEx *r)
+{
+ struct dcesrv_handle *policy_handle;
+ struct lsa_TrustDomainInfoInfoEx *entries;
+ struct lsa_policy_state *policy_state;
+ struct ldb_message **domains;
+ const char *attrs[] = {
+ "flatname",
+ "trustPartner",
+ "securityIdentifier",
+ "trustDirection",
+ "trustType",
+ "trustAttributes",
+ NULL
+ };
+ NTSTATUS nt_status;
+
+ int count, i;
+
+ *r->out.resume_handle = 0;
+
+ r->out.domains->domains = NULL;
+ r->out.domains->count = 0;
+
+ DCESRV_PULL_HANDLE(policy_handle, r->in.handle, LSA_HANDLE_POLICY);
+
+ policy_state = policy_handle->data;
+
+ /* search for all users in this domain. This could possibly be cached and
+ resumed based on resume_key */
+ count = gendb_search(policy_state->sam_ldb, mem_ctx, policy_state->system_dn, &domains, attrs,
+ "objectclass=trustedDomain");
+ if (count == -1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ /* convert to lsa_DomainInformation format */
+ entries = talloc_array(mem_ctx, struct lsa_TrustDomainInfoInfoEx, count);
+ if (!entries) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ for (i=0;i<count;i++) {
+ nt_status = fill_trust_domain_ex(mem_ctx, domains[i], &entries[i]);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+ }
+
+ /* sort the results by name */
+ qsort(entries, count, sizeof(*entries),
+ (comparison_fn_t)compare_TrustDomainInfoInfoEx);
+
+ if (*r->in.resume_handle >= count) {
+ *r->out.resume_handle = -1;
+
+ return NT_STATUS_NO_MORE_ENTRIES;
+ }
+
+ /* return the rest, limit by max_size. Note that we
+ use the w2k3 element size value of 60 */
+ r->out.domains->count = count - *r->in.resume_handle;
+ r->out.domains->count = MIN(r->out.domains->count,
+ 1+(r->in.max_size/LSA_ENUM_TRUST_DOMAIN_EX_MULTIPLIER));
+
+ r->out.domains->domains = entries + *r->in.resume_handle;
+ r->out.domains->count = r->out.domains->count;
+
+ if (r->out.domains->count < count - *r->in.resume_handle) {
+ *r->out.resume_handle = *r->in.resume_handle + r->out.domains->count;
+ return STATUS_MORE_ENTRIES;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ lsa_OpenAccount
+*/
+static NTSTATUS dcesrv_lsa_OpenAccount(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_OpenAccount *r)
+{
+ struct dcesrv_handle *h, *ah;
+ struct lsa_policy_state *state;
+ struct lsa_account_state *astate;
+
+ ZERO_STRUCTP(r->out.acct_handle);
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_POLICY);
+
+ state = h->data;
+
+ astate = talloc(dce_call->conn, struct lsa_account_state);
+ if (astate == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ astate->account_sid = dom_sid_dup(astate, r->in.sid);
+ if (astate->account_sid == NULL) {
+ talloc_free(astate);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ astate->policy = talloc_reference(astate, state);
+ astate->access_mask = r->in.access_mask;
+
+ ah = dcesrv_handle_new(dce_call->context, LSA_HANDLE_ACCOUNT);
+ if (!ah) {
+ talloc_free(astate);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ah->data = talloc_steal(ah, astate);
+
+ *r->out.acct_handle = ah->wire_handle;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ lsa_EnumPrivsAccount
+*/
+static NTSTATUS dcesrv_lsa_EnumPrivsAccount(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_EnumPrivsAccount *r)
+{
+ struct dcesrv_handle *h;
+ struct lsa_account_state *astate;
+ int ret, i;
+ struct ldb_message **res;
+ const char * const attrs[] = { "privilege", NULL};
+ struct ldb_message_element *el;
+ const char *sidstr;
+ struct lsa_PrivilegeSet *privs;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_ACCOUNT);
+
+ astate = h->data;
+
+ privs = talloc(mem_ctx, struct lsa_PrivilegeSet);
+ if (privs == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ privs->count = 0;
+ privs->unknown = 0;
+ privs->set = NULL;
+
+ *r->out.privs = privs;
+
+ sidstr = ldap_encode_ndr_dom_sid(mem_ctx, astate->account_sid);
+ if (sidstr == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = gendb_search(astate->policy->sam_ldb, mem_ctx, NULL, &res, attrs,
+ "objectSid=%s", sidstr);
+ if (ret != 1) {
+ return NT_STATUS_OK;
+ }
+
+ el = ldb_msg_find_element(res[0], "privilege");
+ if (el == NULL || el->num_values == 0) {
+ return NT_STATUS_OK;
+ }
+
+ privs->set = talloc_array(privs,
+ struct lsa_LUIDAttribute, el->num_values);
+ if (privs->set == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0;i<el->num_values;i++) {
+ int id = sec_privilege_id((const char *)el->values[i].data);
+ if (id == -1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ privs->set[i].attribute = 0;
+ privs->set[i].luid.low = id;
+ privs->set[i].luid.high = 0;
+ }
+
+ privs->count = el->num_values;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ lsa_EnumAccountRights
+*/
+static NTSTATUS dcesrv_lsa_EnumAccountRights(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_EnumAccountRights *r)
+{
+ struct dcesrv_handle *h;
+ struct lsa_policy_state *state;
+ int ret, i;
+ struct ldb_message **res;
+ const char * const attrs[] = { "privilege", NULL};
+ const char *sidstr;
+ struct ldb_message_element *el;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_POLICY);
+
+ state = h->data;
+
+ sidstr = ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid);
+ if (sidstr == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = gendb_search(state->sam_ldb, mem_ctx, NULL, &res, attrs,
+ "(&(objectSid=%s)(privilege=*))", sidstr);
+ if (ret == 0) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+ if (ret > 1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ if (ret == -1) {
+ DEBUG(3, ("searching for account rights for SID: %s failed: %s",
+ dom_sid_string(mem_ctx, r->in.sid),
+ ldb_errstring(state->sam_ldb)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ el = ldb_msg_find_element(res[0], "privilege");
+ if (el == NULL || el->num_values == 0) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ r->out.rights->count = el->num_values;
+ r->out.rights->names = talloc_array(r->out.rights,
+ struct lsa_StringLarge, r->out.rights->count);
+ if (r->out.rights->names == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0;i<el->num_values;i++) {
+ r->out.rights->names[i].string = (const char *)el->values[i].data;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+
+/*
+ helper for lsa_AddAccountRights and lsa_RemoveAccountRights
+*/
+static NTSTATUS dcesrv_lsa_AddRemoveAccountRights(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_policy_state *state,
+ int ldb_flag,
+ struct dom_sid *sid,
+ const struct lsa_RightSet *rights)
+{
+ const char *sidstr;
+ struct ldb_message *msg;
+ struct ldb_message_element *el;
+ int i, ret;
+ struct lsa_EnumAccountRights r2;
+
+ sidstr = ldap_encode_ndr_dom_sid(mem_ctx, sid);
+ if (sidstr == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ msg->dn = samdb_search_dn(state->sam_ldb, mem_ctx,
+ NULL, "objectSid=%s", sidstr);
+ if (msg->dn == NULL) {
+ NTSTATUS status;
+ if (ldb_flag == LDB_FLAG_MOD_DELETE) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+ status = samdb_create_foreign_security_principal(state->sam_ldb, mem_ctx,
+ sid, &msg->dn);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ if (ldb_msg_add_empty(msg, "privilege", ldb_flag, NULL)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (ldb_flag == LDB_FLAG_MOD_ADD) {
+ NTSTATUS status;
+
+ r2.in.handle = &state->handle->wire_handle;
+ r2.in.sid = sid;
+ r2.out.rights = talloc(mem_ctx, struct lsa_RightSet);
+
+ status = dcesrv_lsa_EnumAccountRights(dce_call, mem_ctx, &r2);
+ if (!NT_STATUS_IS_OK(status)) {
+ ZERO_STRUCTP(r2.out.rights);
+ }
+ }
+
+ for (i=0;i<rights->count;i++) {
+ if (sec_privilege_id(rights->names[i].string) == -1) {
+ return NT_STATUS_NO_SUCH_PRIVILEGE;
+ }
+
+ if (ldb_flag == LDB_FLAG_MOD_ADD) {
+ int j;
+ for (j=0;j<r2.out.rights->count;j++) {
+ if (strcasecmp_m(r2.out.rights->names[j].string,
+ rights->names[i].string) == 0) {
+ break;
+ }
+ }
+ if (j != r2.out.rights->count) continue;
+ }
+
+ ret = ldb_msg_add_string(msg, "privilege", rights->names[i].string);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ el = ldb_msg_find_element(msg, "privilege");
+ if (!el) {
+ return NT_STATUS_OK;
+ }
+
+ ret = ldb_modify(state->sam_ldb, msg);
+ if (ret != 0) {
+ if (ldb_flag == LDB_FLAG_MOD_DELETE && ret == LDB_ERR_NO_SUCH_ATTRIBUTE) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+ DEBUG(3, ("Could not %s attributes from %s: %s",
+ ldb_flag == LDB_FLAG_MOD_DELETE ? "delete" : "add",
+ ldb_dn_get_linearized(msg->dn), ldb_errstring(state->sam_ldb)));
+ return NT_STATUS_UNEXPECTED_IO_ERROR;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ lsa_AddPrivilegesToAccount
+*/
+static NTSTATUS dcesrv_lsa_AddPrivilegesToAccount(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_AddPrivilegesToAccount *r)
+{
+ struct lsa_RightSet rights;
+ struct dcesrv_handle *h;
+ struct lsa_account_state *astate;
+ int i;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_ACCOUNT);
+
+ astate = h->data;
+
+ rights.count = r->in.privs->count;
+ rights.names = talloc_array(mem_ctx, struct lsa_StringLarge, rights.count);
+ if (rights.names == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ for (i=0;i<rights.count;i++) {
+ int id = r->in.privs->set[i].luid.low;
+ if (r->in.privs->set[i].luid.high) {
+ return NT_STATUS_NO_SUCH_PRIVILEGE;
+ }
+ rights.names[i].string = sec_privilege_name(id);
+ if (rights.names[i].string == NULL) {
+ return NT_STATUS_NO_SUCH_PRIVILEGE;
+ }
+ }
+
+ return dcesrv_lsa_AddRemoveAccountRights(dce_call, mem_ctx, astate->policy,
+ LDB_FLAG_MOD_ADD, astate->account_sid,
+ &rights);
+}
+
+
+/*
+ lsa_RemovePrivilegesFromAccount
+*/
+static NTSTATUS dcesrv_lsa_RemovePrivilegesFromAccount(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_RemovePrivilegesFromAccount *r)
+{
+ struct lsa_RightSet *rights;
+ struct dcesrv_handle *h;
+ struct lsa_account_state *astate;
+ int i;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_ACCOUNT);
+
+ astate = h->data;
+
+ rights = talloc(mem_ctx, struct lsa_RightSet);
+
+ if (r->in.remove_all == 1 &&
+ r->in.privs == NULL) {
+ struct lsa_EnumAccountRights r2;
+ NTSTATUS status;
+
+ r2.in.handle = &astate->policy->handle->wire_handle;
+ r2.in.sid = astate->account_sid;
+ r2.out.rights = rights;
+
+ status = dcesrv_lsa_EnumAccountRights(dce_call, mem_ctx, &r2);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return dcesrv_lsa_AddRemoveAccountRights(dce_call, mem_ctx, astate->policy,
+ LDB_FLAG_MOD_DELETE, astate->account_sid,
+ r2.out.rights);
+ }
+
+ if (r->in.remove_all != 0) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ rights->count = r->in.privs->count;
+ rights->names = talloc_array(mem_ctx, struct lsa_StringLarge, rights->count);
+ if (rights->names == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ for (i=0;i<rights->count;i++) {
+ int id = r->in.privs->set[i].luid.low;
+ if (r->in.privs->set[i].luid.high) {
+ return NT_STATUS_NO_SUCH_PRIVILEGE;
+ }
+ rights->names[i].string = sec_privilege_name(id);
+ if (rights->names[i].string == NULL) {
+ return NT_STATUS_NO_SUCH_PRIVILEGE;
+ }
+ }
+
+ return dcesrv_lsa_AddRemoveAccountRights(dce_call, mem_ctx, astate->policy,
+ LDB_FLAG_MOD_DELETE, astate->account_sid,
+ rights);
+}
+
+
+/*
+ lsa_GetQuotasForAccount
+*/
+static NTSTATUS dcesrv_lsa_GetQuotasForAccount(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_GetQuotasForAccount *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_SetQuotasForAccount
+*/
+static NTSTATUS dcesrv_lsa_SetQuotasForAccount(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_SetQuotasForAccount *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_GetSystemAccessAccount
+*/
+static NTSTATUS dcesrv_lsa_GetSystemAccessAccount(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_GetSystemAccessAccount *r)
+{
+ int i;
+ NTSTATUS status;
+ struct lsa_EnumPrivsAccount enumPrivs;
+ struct lsa_PrivilegeSet *privs;
+
+ privs = talloc(mem_ctx, struct lsa_PrivilegeSet);
+ if (!privs) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ privs->count = 0;
+ privs->unknown = 0;
+ privs->set = NULL;
+
+ enumPrivs.in.handle = r->in.handle;
+ enumPrivs.out.privs = &privs;
+
+ status = dcesrv_lsa_EnumPrivsAccount(dce_call, mem_ctx, &enumPrivs);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ *(r->out.access_mask) = 0x00000000;
+
+ for (i = 0; i < privs->count; i++) {
+ int priv = privs->set[i].luid.low;
+
+ switch (priv) {
+ case SEC_PRIV_INTERACTIVE_LOGON:
+ *(r->out.access_mask) |= LSA_POLICY_MODE_INTERACTIVE;
+ break;
+ case SEC_PRIV_NETWORK_LOGON:
+ *(r->out.access_mask) |= LSA_POLICY_MODE_NETWORK;
+ break;
+ case SEC_PRIV_REMOTE_INTERACTIVE_LOGON:
+ *(r->out.access_mask) |= LSA_POLICY_MODE_REMOTE_INTERACTIVE;
+ break;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ lsa_SetSystemAccessAccount
+*/
+static NTSTATUS dcesrv_lsa_SetSystemAccessAccount(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_SetSystemAccessAccount *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_CreateSecret
+*/
+static NTSTATUS dcesrv_lsa_CreateSecret(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_CreateSecret *r)
+{
+ struct dcesrv_handle *policy_handle;
+ struct lsa_policy_state *policy_state;
+ struct lsa_secret_state *secret_state;
+ struct dcesrv_handle *handle;
+ struct ldb_message **msgs, *msg;
+ const char *errstr;
+ const char *attrs[] = {
+ NULL
+ };
+
+ const char *name;
+
+ int ret;
+
+ DCESRV_PULL_HANDLE(policy_handle, r->in.handle, LSA_HANDLE_POLICY);
+ ZERO_STRUCTP(r->out.sec_handle);
+
+ switch (security_session_user_level(dce_call->conn->auth_state.session_info))
+ {
+ case SECURITY_SYSTEM:
+ case SECURITY_ADMINISTRATOR:
+ break;
+ default:
+ /* Users and annonymous are not allowed create secrets */
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ policy_state = policy_handle->data;
+
+ if (!r->in.name.string) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ secret_state = talloc(mem_ctx, struct lsa_secret_state);
+ if (!secret_state) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ secret_state->policy = policy_state;
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (strncmp("G$", r->in.name.string, 2) == 0) {
+ const char *name2;
+ name = &r->in.name.string[2];
+ /* We need to connect to the database as system, as this is one of the rare RPC calls that must read the secrets (and this is denied otherwise) */
+ secret_state->sam_ldb = talloc_reference(secret_state,
+ samdb_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, system_session(secret_state, dce_call->conn->dce_ctx->lp_ctx)));
+ secret_state->global = true;
+
+ if (strlen(name) < 1) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ name2 = talloc_asprintf(mem_ctx, "%s Secret", ldb_binary_encode_string(mem_ctx, name));
+ /* search for the secret record */
+ ret = gendb_search(secret_state->sam_ldb,
+ mem_ctx, policy_state->system_dn, &msgs, attrs,
+ "(&(cn=%s)(objectclass=secret))",
+ name2);
+ if (ret > 0) {
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ if (ret == -1) {
+ DEBUG(0,("Failure searching for CN=%s: %s\n",
+ name2, ldb_errstring(secret_state->sam_ldb)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ msg->dn = ldb_dn_copy(mem_ctx, policy_state->system_dn);
+ if (!name2 || ! ldb_dn_add_child_fmt(msg->dn, "cn=%s", name2)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ samdb_msg_add_string(secret_state->sam_ldb, mem_ctx, msg, "cn", name2);
+
+ } else {
+ secret_state->global = false;
+
+ name = r->in.name.string;
+ if (strlen(name) < 1) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ secret_state->sam_ldb = talloc_reference(secret_state,
+ secrets_db_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx));
+ /* search for the secret record */
+ ret = gendb_search(secret_state->sam_ldb, mem_ctx,
+ ldb_dn_new(mem_ctx, secret_state->sam_ldb, "cn=LSA Secrets"),
+ &msgs, attrs,
+ "(&(cn=%s)(objectclass=secret))",
+ ldb_binary_encode_string(mem_ctx, name));
+ if (ret > 0) {
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ if (ret == -1) {
+ DEBUG(0,("Failure searching for CN=%s: %s\n",
+ name, ldb_errstring(secret_state->sam_ldb)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ msg->dn = ldb_dn_new_fmt(mem_ctx, secret_state->sam_ldb, "cn=%s,cn=LSA Secrets", name);
+ samdb_msg_add_string(secret_state->sam_ldb, mem_ctx, msg, "cn", name);
+ }
+
+ /* pull in all the template attributes. Note this is always from the global samdb */
+ ret = samdb_copy_template(secret_state->policy->sam_ldb, msg,
+ "secret", &errstr);
+ if (ret != 0) {
+ DEBUG(0,("Failed to load TemplateSecret from samdb: %s\n",
+ errstr));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ samdb_msg_add_string(secret_state->sam_ldb, mem_ctx, msg, "objectClass", "secret");
+
+ secret_state->secret_dn = talloc_reference(secret_state, msg->dn);
+
+ /* create the secret */
+ ret = ldb_add(secret_state->sam_ldb, msg);
+ if (ret != 0) {
+ DEBUG(0,("Failed to create secret record %s: %s\n",
+ ldb_dn_get_linearized(msg->dn),
+ ldb_errstring(secret_state->sam_ldb)));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ handle = dcesrv_handle_new(dce_call->context, LSA_HANDLE_SECRET);
+ if (!handle) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ handle->data = talloc_steal(handle, secret_state);
+
+ secret_state->access_mask = r->in.access_mask;
+ secret_state->policy = talloc_reference(secret_state, policy_state);
+
+ *r->out.sec_handle = handle->wire_handle;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ lsa_OpenSecret
+*/
+static NTSTATUS dcesrv_lsa_OpenSecret(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_OpenSecret *r)
+{
+ struct dcesrv_handle *policy_handle;
+
+ struct lsa_policy_state *policy_state;
+ struct lsa_secret_state *secret_state;
+ struct dcesrv_handle *handle;
+ struct ldb_message **msgs;
+ const char *attrs[] = {
+ NULL
+ };
+
+ const char *name;
+
+ int ret;
+
+ DCESRV_PULL_HANDLE(policy_handle, r->in.handle, LSA_HANDLE_POLICY);
+ ZERO_STRUCTP(r->out.sec_handle);
+ policy_state = policy_handle->data;
+
+ if (!r->in.name.string) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ switch (security_session_user_level(dce_call->conn->auth_state.session_info))
+ {
+ case SECURITY_SYSTEM:
+ case SECURITY_ADMINISTRATOR:
+ break;
+ default:
+ /* Users and annonymous are not allowed to access secrets */
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ secret_state = talloc(mem_ctx, struct lsa_secret_state);
+ if (!secret_state) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ secret_state->policy = policy_state;
+
+ if (strncmp("G$", r->in.name.string, 2) == 0) {
+ name = &r->in.name.string[2];
+ /* We need to connect to the database as system, as this is one of the rare RPC calls that must read the secrets (and this is denied otherwise) */
+ secret_state->sam_ldb = talloc_reference(secret_state,
+ samdb_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, system_session(secret_state, dce_call->conn->dce_ctx->lp_ctx)));
+ secret_state->global = true;
+
+ if (strlen(name) < 1) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* search for the secret record */
+ ret = gendb_search(secret_state->sam_ldb,
+ mem_ctx, policy_state->system_dn, &msgs, attrs,
+ "(&(cn=%s Secret)(objectclass=secret))",
+ ldb_binary_encode_string(mem_ctx, name));
+ if (ret == 0) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ if (ret != 1) {
+ DEBUG(0,("Found %d records matching DN %s\n", ret,
+ ldb_dn_get_linearized(policy_state->system_dn)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ } else {
+ secret_state->global = false;
+ secret_state->sam_ldb = talloc_reference(secret_state,
+ secrets_db_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx));
+
+ name = r->in.name.string;
+ if (strlen(name) < 1) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* search for the secret record */
+ ret = gendb_search(secret_state->sam_ldb, mem_ctx,
+ ldb_dn_new(mem_ctx, secret_state->sam_ldb, "cn=LSA Secrets"),
+ &msgs, attrs,
+ "(&(cn=%s)(objectclass=secret))",
+ ldb_binary_encode_string(mem_ctx, name));
+ if (ret == 0) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ if (ret != 1) {
+ DEBUG(0,("Found %d records matching CN=%s\n",
+ ret, ldb_binary_encode_string(mem_ctx, name)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ }
+
+ secret_state->secret_dn = talloc_reference(secret_state, msgs[0]->dn);
+
+ handle = dcesrv_handle_new(dce_call->context, LSA_HANDLE_SECRET);
+ if (!handle) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ handle->data = talloc_steal(handle, secret_state);
+
+ secret_state->access_mask = r->in.access_mask;
+ secret_state->policy = talloc_reference(secret_state, policy_state);
+
+ *r->out.sec_handle = handle->wire_handle;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ lsa_SetSecret
+*/
+static NTSTATUS dcesrv_lsa_SetSecret(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_SetSecret *r)
+{
+
+ struct dcesrv_handle *h;
+ struct lsa_secret_state *secret_state;
+ struct ldb_message *msg;
+ DATA_BLOB session_key;
+ DATA_BLOB crypt_secret, secret;
+ struct ldb_val val;
+ int ret;
+ NTSTATUS status = NT_STATUS_OK;
+
+ struct timeval now = timeval_current();
+ NTTIME nt_now = timeval_to_nttime(&now);
+
+ DCESRV_PULL_HANDLE(h, r->in.sec_handle, LSA_HANDLE_SECRET);
+
+ secret_state = h->data;
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ msg->dn = talloc_reference(mem_ctx, secret_state->secret_dn);
+ if (!msg->dn) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (r->in.old_val) {
+ /* Decrypt */
+ crypt_secret.data = r->in.old_val->data;
+ crypt_secret.length = r->in.old_val->size;
+
+ status = sess_decrypt_blob(mem_ctx, &crypt_secret, &session_key, &secret);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ val.data = secret.data;
+ val.length = secret.length;
+
+ /* set value */
+ if (samdb_msg_add_value(secret_state->sam_ldb,
+ mem_ctx, msg, "priorValue", &val) != 0) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* set old value mtime */
+ if (samdb_msg_add_uint64(secret_state->sam_ldb,
+ mem_ctx, msg, "priorSetTime", nt_now) != 0) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ } else {
+ /* If the old value is not set, then migrate the
+ * current value to the old value */
+ const struct ldb_val *old_val;
+ NTTIME last_set_time;
+ struct ldb_message **res;
+ const char *attrs[] = {
+ "currentValue",
+ "lastSetTime",
+ NULL
+ };
+
+ /* search for the secret record */
+ ret = gendb_search_dn(secret_state->sam_ldb,mem_ctx,
+ secret_state->secret_dn, &res, attrs);
+ if (ret == 0) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ if (ret != 1) {
+ DEBUG(0,("Found %d records matching dn=%s\n", ret,
+ ldb_dn_get_linearized(secret_state->secret_dn)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ old_val = ldb_msg_find_ldb_val(res[0], "currentValue");
+ last_set_time = ldb_msg_find_attr_as_uint64(res[0], "lastSetTime", 0);
+
+ if (old_val) {
+ /* set old value */
+ if (samdb_msg_add_value(secret_state->sam_ldb,
+ mem_ctx, msg, "priorValue",
+ old_val) != 0) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ } else {
+ if (samdb_msg_add_delete(secret_state->sam_ldb,
+ mem_ctx, msg, "priorValue")) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ }
+
+ /* set old value mtime */
+ if (ldb_msg_find_ldb_val(res[0], "lastSetTime")) {
+ if (samdb_msg_add_uint64(secret_state->sam_ldb,
+ mem_ctx, msg, "priorSetTime", last_set_time) != 0) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ } else {
+ if (samdb_msg_add_uint64(secret_state->sam_ldb,
+ mem_ctx, msg, "priorSetTime", nt_now) != 0) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+ }
+
+ if (r->in.new_val) {
+ /* Decrypt */
+ crypt_secret.data = r->in.new_val->data;
+ crypt_secret.length = r->in.new_val->size;
+
+ status = sess_decrypt_blob(mem_ctx, &crypt_secret, &session_key, &secret);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ val.data = secret.data;
+ val.length = secret.length;
+
+ /* set value */
+ if (samdb_msg_add_value(secret_state->sam_ldb,
+ mem_ctx, msg, "currentValue", &val) != 0) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* set new value mtime */
+ if (samdb_msg_add_uint64(secret_state->sam_ldb,
+ mem_ctx, msg, "lastSetTime", nt_now) != 0) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ } else {
+ /* NULL out the NEW value */
+ if (samdb_msg_add_uint64(secret_state->sam_ldb,
+ mem_ctx, msg, "lastSetTime", nt_now) != 0) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ if (samdb_msg_add_delete(secret_state->sam_ldb,
+ mem_ctx, msg, "currentValue")) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ /* modify the samdb record */
+ ret = samdb_replace(secret_state->sam_ldb, mem_ctx, msg);
+ if (ret != 0) {
+ /* we really need samdb.c to return NTSTATUS */
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ lsa_QuerySecret
+*/
+static NTSTATUS dcesrv_lsa_QuerySecret(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_QuerySecret *r)
+{
+ struct dcesrv_handle *h;
+ struct lsa_secret_state *secret_state;
+ struct ldb_message *msg;
+ DATA_BLOB session_key;
+ DATA_BLOB crypt_secret, secret;
+ int ret;
+ struct ldb_message **res;
+ const char *attrs[] = {
+ "currentValue",
+ "priorValue",
+ "lastSetTime",
+ "priorSetTime",
+ NULL
+ };
+
+ NTSTATUS nt_status;
+
+ DCESRV_PULL_HANDLE(h, r->in.sec_handle, LSA_HANDLE_SECRET);
+
+ /* Ensure user is permitted to read this... */
+ switch (security_session_user_level(dce_call->conn->auth_state.session_info))
+ {
+ case SECURITY_SYSTEM:
+ case SECURITY_ADMINISTRATOR:
+ break;
+ default:
+ /* Users and annonymous are not allowed to read secrets */
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ secret_state = h->data;
+
+ /* pull all the user attributes */
+ ret = gendb_search_dn(secret_state->sam_ldb, mem_ctx,
+ secret_state->secret_dn, &res, attrs);
+ if (ret != 1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ msg = res[0];
+
+ nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ if (r->in.old_val) {
+ const struct ldb_val *prior_val;
+ r->out.old_val = talloc_zero(mem_ctx, struct lsa_DATA_BUF_PTR);
+ if (!r->out.old_val) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ prior_val = ldb_msg_find_ldb_val(res[0], "priorValue");
+
+ if (prior_val && prior_val->length) {
+ secret.data = prior_val->data;
+ secret.length = prior_val->length;
+
+ /* Encrypt */
+ crypt_secret = sess_encrypt_blob(mem_ctx, &secret, &session_key);
+ if (!crypt_secret.length) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ r->out.old_val->buf = talloc(mem_ctx, struct lsa_DATA_BUF);
+ if (!r->out.old_val->buf) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ r->out.old_val->buf->size = crypt_secret.length;
+ r->out.old_val->buf->length = crypt_secret.length;
+ r->out.old_val->buf->data = crypt_secret.data;
+ }
+ }
+
+ if (r->in.old_mtime) {
+ r->out.old_mtime = talloc(mem_ctx, NTTIME);
+ if (!r->out.old_mtime) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ *r->out.old_mtime = ldb_msg_find_attr_as_uint64(res[0], "priorSetTime", 0);
+ }
+
+ if (r->in.new_val) {
+ const struct ldb_val *new_val;
+ r->out.new_val = talloc_zero(mem_ctx, struct lsa_DATA_BUF_PTR);
+ if (!r->out.new_val) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ new_val = ldb_msg_find_ldb_val(res[0], "currentValue");
+
+ if (new_val && new_val->length) {
+ secret.data = new_val->data;
+ secret.length = new_val->length;
+
+ /* Encrypt */
+ crypt_secret = sess_encrypt_blob(mem_ctx, &secret, &session_key);
+ if (!crypt_secret.length) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ r->out.new_val->buf = talloc(mem_ctx, struct lsa_DATA_BUF);
+ if (!r->out.new_val->buf) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ r->out.new_val->buf->length = crypt_secret.length;
+ r->out.new_val->buf->size = crypt_secret.length;
+ r->out.new_val->buf->data = crypt_secret.data;
+ }
+ }
+
+ if (r->in.new_mtime) {
+ r->out.new_mtime = talloc(mem_ctx, NTTIME);
+ if (!r->out.new_mtime) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ *r->out.new_mtime = ldb_msg_find_attr_as_uint64(res[0], "lastSetTime", 0);
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ lsa_LookupPrivValue
+*/
+static NTSTATUS dcesrv_lsa_LookupPrivValue(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_LookupPrivValue *r)
+{
+ struct dcesrv_handle *h;
+ struct lsa_policy_state *state;
+ int id;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_POLICY);
+
+ state = h->data;
+
+ id = sec_privilege_id(r->in.name->string);
+ if (id == -1) {
+ return NT_STATUS_NO_SUCH_PRIVILEGE;
+ }
+
+ r->out.luid->low = id;
+ r->out.luid->high = 0;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ lsa_LookupPrivName
+*/
+static NTSTATUS dcesrv_lsa_LookupPrivName(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_LookupPrivName *r)
+{
+ struct dcesrv_handle *h;
+ struct lsa_policy_state *state;
+ struct lsa_StringLarge *name;
+ const char *privname;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_POLICY);
+
+ state = h->data;
+
+ if (r->in.luid->high != 0) {
+ return NT_STATUS_NO_SUCH_PRIVILEGE;
+ }
+
+ privname = sec_privilege_name(r->in.luid->low);
+ if (privname == NULL) {
+ return NT_STATUS_NO_SUCH_PRIVILEGE;
+ }
+
+ name = talloc(mem_ctx, struct lsa_StringLarge);
+ if (name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ name->string = privname;
+
+ *r->out.name = name;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ lsa_LookupPrivDisplayName
+*/
+static NTSTATUS dcesrv_lsa_LookupPrivDisplayName(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_LookupPrivDisplayName *r)
+{
+ struct dcesrv_handle *h;
+ struct lsa_policy_state *state;
+ struct lsa_StringLarge *disp_name = NULL;
+ int id;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_POLICY);
+
+ state = h->data;
+
+ id = sec_privilege_id(r->in.name->string);
+ if (id == -1) {
+ return NT_STATUS_NO_SUCH_PRIVILEGE;
+ }
+
+ disp_name = talloc(mem_ctx, struct lsa_StringLarge);
+ if (disp_name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ disp_name->string = sec_privilege_display_name(id, &r->in.language_id);
+ if (disp_name->string == NULL) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ *r->out.disp_name = disp_name;
+ *r->out.returned_language_id = 0;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ lsa_EnumAccountsWithUserRight
+*/
+static NTSTATUS dcesrv_lsa_EnumAccountsWithUserRight(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_EnumAccountsWithUserRight *r)
+{
+ struct dcesrv_handle *h;
+ struct lsa_policy_state *state;
+ int ret, i;
+ struct ldb_message **res;
+ const char * const attrs[] = { "objectSid", NULL};
+ const char *privname;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_POLICY);
+
+ state = h->data;
+
+ if (r->in.name == NULL) {
+ return NT_STATUS_NO_SUCH_PRIVILEGE;
+ }
+
+ privname = r->in.name->string;
+ if (sec_privilege_id(privname) == -1) {
+ return NT_STATUS_NO_SUCH_PRIVILEGE;
+ }
+
+ ret = gendb_search(state->sam_ldb, mem_ctx, NULL, &res, attrs,
+ "privilege=%s", privname);
+ if (ret == -1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ if (ret == 0) {
+ return NT_STATUS_NO_MORE_ENTRIES;
+ }
+
+ r->out.sids->sids = talloc_array(r->out.sids, struct lsa_SidPtr, ret);
+ if (r->out.sids->sids == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ for (i=0;i<ret;i++) {
+ r->out.sids->sids[i].sid = samdb_result_dom_sid(r->out.sids->sids,
+ res[i], "objectSid");
+ NT_STATUS_HAVE_NO_MEMORY(r->out.sids->sids[i].sid);
+ }
+ r->out.sids->num_sids = ret;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ lsa_AddAccountRights
+*/
+static NTSTATUS dcesrv_lsa_AddAccountRights(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_AddAccountRights *r)
+{
+ struct dcesrv_handle *h;
+ struct lsa_policy_state *state;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_POLICY);
+
+ state = h->data;
+
+ return dcesrv_lsa_AddRemoveAccountRights(dce_call, mem_ctx, state,
+ LDB_FLAG_MOD_ADD,
+ r->in.sid, r->in.rights);
+}
+
+
+/*
+ lsa_RemoveAccountRights
+*/
+static NTSTATUS dcesrv_lsa_RemoveAccountRights(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_RemoveAccountRights *r)
+{
+ struct dcesrv_handle *h;
+ struct lsa_policy_state *state;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_POLICY);
+
+ state = h->data;
+
+ return dcesrv_lsa_AddRemoveAccountRights(dce_call, mem_ctx, state,
+ LDB_FLAG_MOD_DELETE,
+ r->in.sid, r->in.rights);
+}
+
+
+/*
+ lsa_StorePrivateData
+*/
+static NTSTATUS dcesrv_lsa_StorePrivateData(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_StorePrivateData *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_RetrievePrivateData
+*/
+static NTSTATUS dcesrv_lsa_RetrievePrivateData(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_RetrievePrivateData *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_GetUserName
+*/
+static NTSTATUS dcesrv_lsa_GetUserName(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_GetUserName *r)
+{
+ NTSTATUS status = NT_STATUS_OK;
+ const char *account_name;
+ const char *authority_name;
+ struct lsa_String *_account_name;
+ struct lsa_String *_authority_name = NULL;
+
+ /* this is what w2k3 does */
+ r->out.account_name = r->in.account_name;
+ r->out.authority_name = r->in.authority_name;
+
+ if (r->in.account_name
+ && *r->in.account_name
+ /* && *(*r->in.account_name)->string */
+ ) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (r->in.authority_name
+ && *r->in.authority_name
+ /* && *(*r->in.authority_name)->string */
+ ) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ account_name = talloc_reference(mem_ctx, dce_call->conn->auth_state.session_info->server_info->account_name);
+ authority_name = talloc_reference(mem_ctx, dce_call->conn->auth_state.session_info->server_info->domain_name);
+
+ _account_name = talloc(mem_ctx, struct lsa_String);
+ NT_STATUS_HAVE_NO_MEMORY(_account_name);
+ _account_name->string = account_name;
+
+ if (r->in.authority_name) {
+ _authority_name = talloc(mem_ctx, struct lsa_String);
+ NT_STATUS_HAVE_NO_MEMORY(_authority_name);
+ _authority_name->string = authority_name;
+ }
+
+ *r->out.account_name = _account_name;
+ if (r->out.authority_name) {
+ *r->out.authority_name = _authority_name;
+ }
+
+ return status;
+}
+
+/*
+ lsa_SetInfoPolicy2
+*/
+static NTSTATUS dcesrv_lsa_SetInfoPolicy2(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_SetInfoPolicy2 *r)
+{
+ /* need to support these */
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+/*
+ lsa_QueryDomainInformationPolicy
+*/
+static NTSTATUS dcesrv_lsa_QueryDomainInformationPolicy(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_QueryDomainInformationPolicy *r)
+{
+ union lsa_DomainInformationPolicy *info;
+
+ info = talloc(r->out.info, union lsa_DomainInformationPolicy);
+ if (!info) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ switch (r->in.level) {
+ case LSA_DOMAIN_INFO_POLICY_EFS:
+ talloc_free(info);
+ *r->out.info = NULL;
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ case LSA_DOMAIN_INFO_POLICY_KERBEROS:
+ {
+ struct lsa_DomainInfoKerberos *k = &info->kerberos_info;
+ struct smb_krb5_context *smb_krb5_context;
+ int ret = smb_krb5_init_context(mem_ctx,
+ dce_call->event_ctx,
+ dce_call->conn->dce_ctx->lp_ctx,
+ &smb_krb5_context);
+ if (ret != 0) {
+ talloc_free(info);
+ *r->out.info = NULL;
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ k->enforce_restrictions = 0; /* FIXME, details missing from MS-LSAD 2.2.53 */
+ k->service_tkt_lifetime = 0; /* Need to find somewhere to store this, and query in KDC too */
+ k->user_tkt_lifetime = 0; /* Need to find somewhere to store this, and query in KDC too */
+ k->user_tkt_renewaltime = 0; /* Need to find somewhere to store this, and query in KDC too */
+ k->clock_skew = krb5_get_max_time_skew(smb_krb5_context->krb5_context);
+ talloc_free(smb_krb5_context);
+ *r->out.info = info;
+ return NT_STATUS_OK;
+ }
+ default:
+ talloc_free(info);
+ *r->out.info = NULL;
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+}
+
+/*
+ lsa_SetDomInfoPolicy
+*/
+static NTSTATUS dcesrv_lsa_SetDomainInformationPolicy(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_SetDomainInformationPolicy *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+/*
+ lsa_TestCall
+*/
+static NTSTATUS dcesrv_lsa_TestCall(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_TestCall *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+/*
+ lsa_CREDRWRITE
+*/
+static NTSTATUS dcesrv_lsa_CREDRWRITE(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_CREDRWRITE *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_CREDRREAD
+*/
+static NTSTATUS dcesrv_lsa_CREDRREAD(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_CREDRREAD *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_CREDRENUMERATE
+*/
+static NTSTATUS dcesrv_lsa_CREDRENUMERATE(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_CREDRENUMERATE *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_CREDRWRITEDOMAINCREDENTIALS
+*/
+static NTSTATUS dcesrv_lsa_CREDRWRITEDOMAINCREDENTIALS(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_CREDRWRITEDOMAINCREDENTIALS *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_CREDRREADDOMAINCREDENTIALS
+*/
+static NTSTATUS dcesrv_lsa_CREDRREADDOMAINCREDENTIALS(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_CREDRREADDOMAINCREDENTIALS *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_CREDRDELETE
+*/
+static NTSTATUS dcesrv_lsa_CREDRDELETE(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_CREDRDELETE *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_CREDRGETTARGETINFO
+*/
+static NTSTATUS dcesrv_lsa_CREDRGETTARGETINFO(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_CREDRGETTARGETINFO *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_CREDRPROFILELOADED
+*/
+static NTSTATUS dcesrv_lsa_CREDRPROFILELOADED(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_CREDRPROFILELOADED *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_CREDRGETSESSIONTYPES
+*/
+static NTSTATUS dcesrv_lsa_CREDRGETSESSIONTYPES(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_CREDRGETSESSIONTYPES *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_LSARREGISTERAUDITEVENT
+*/
+static NTSTATUS dcesrv_lsa_LSARREGISTERAUDITEVENT(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_LSARREGISTERAUDITEVENT *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_LSARGENAUDITEVENT
+*/
+static NTSTATUS dcesrv_lsa_LSARGENAUDITEVENT(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_LSARGENAUDITEVENT *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_LSARUNREGISTERAUDITEVENT
+*/
+static NTSTATUS dcesrv_lsa_LSARUNREGISTERAUDITEVENT(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_LSARUNREGISTERAUDITEVENT *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_lsaRQueryForestTrustInformation
+*/
+static NTSTATUS dcesrv_lsa_lsaRQueryForestTrustInformation(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_lsaRQueryForestTrustInformation *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_LSARSETFORESTTRUSTINFORMATION
+*/
+static NTSTATUS dcesrv_lsa_LSARSETFORESTTRUSTINFORMATION(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_LSARSETFORESTTRUSTINFORMATION *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_CREDRRENAME
+*/
+static NTSTATUS dcesrv_lsa_CREDRRENAME(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_CREDRRENAME *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+
+/*
+ lsa_LSAROPENPOLICYSCE
+*/
+static NTSTATUS dcesrv_lsa_LSAROPENPOLICYSCE(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_LSAROPENPOLICYSCE *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_LSARADTREGISTERSECURITYEVENTSOURCE
+*/
+static NTSTATUS dcesrv_lsa_LSARADTREGISTERSECURITYEVENTSOURCE(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_LSARADTREGISTERSECURITYEVENTSOURCE *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_LSARADTUNREGISTERSECURITYEVENTSOURCE
+*/
+static NTSTATUS dcesrv_lsa_LSARADTUNREGISTERSECURITYEVENTSOURCE(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_LSARADTUNREGISTERSECURITYEVENTSOURCE *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ lsa_LSARADTREPORTSECURITYEVENT
+*/
+static NTSTATUS dcesrv_lsa_LSARADTREPORTSECURITYEVENT(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_LSARADTREPORTSECURITYEVENT *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_lsa_s.c"
+
+
+
+/*****************************************
+NOTE! The remaining calls below were
+removed in w2k3, so the DCESRV_FAULT()
+replies are the correct implementation. Do
+not try and fill these in with anything else
+******************************************/
+
+/*
+ dssetup_DsRoleDnsNameToFlatName
+*/
+static WERROR dcesrv_dssetup_DsRoleDnsNameToFlatName(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct dssetup_DsRoleDnsNameToFlatName *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ dssetup_DsRoleDcAsDc
+*/
+static WERROR dcesrv_dssetup_DsRoleDcAsDc(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct dssetup_DsRoleDcAsDc *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ dssetup_DsRoleDcAsReplica
+*/
+static WERROR dcesrv_dssetup_DsRoleDcAsReplica(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct dssetup_DsRoleDcAsReplica *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ dssetup_DsRoleDemoteDc
+*/
+static WERROR dcesrv_dssetup_DsRoleDemoteDc(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct dssetup_DsRoleDemoteDc *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ dssetup_DsRoleGetDcOperationProgress
+*/
+static WERROR dcesrv_dssetup_DsRoleGetDcOperationProgress(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct dssetup_DsRoleGetDcOperationProgress *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ dssetup_DsRoleGetDcOperationResults
+*/
+static WERROR dcesrv_dssetup_DsRoleGetDcOperationResults(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct dssetup_DsRoleGetDcOperationResults *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ dssetup_DsRoleCancel
+*/
+static WERROR dcesrv_dssetup_DsRoleCancel(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct dssetup_DsRoleCancel *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ dssetup_DsRoleServerSaveStateForUpgrade
+*/
+static WERROR dcesrv_dssetup_DsRoleServerSaveStateForUpgrade(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct dssetup_DsRoleServerSaveStateForUpgrade *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ dssetup_DsRoleUpgradeDownlevelServer
+*/
+static WERROR dcesrv_dssetup_DsRoleUpgradeDownlevelServer(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct dssetup_DsRoleUpgradeDownlevelServer *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ dssetup_DsRoleAbortDownlevelServerUpgrade
+*/
+static WERROR dcesrv_dssetup_DsRoleAbortDownlevelServerUpgrade(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct dssetup_DsRoleAbortDownlevelServerUpgrade *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_dssetup_s.c"
+
+NTSTATUS dcerpc_server_lsa_init(void)
+{
+ NTSTATUS ret;
+
+ ret = dcerpc_server_dssetup_init();
+ if (!NT_STATUS_IS_OK(ret)) {
+ return ret;
+ }
+ ret = dcerpc_server_lsarpc_init();
+ if (!NT_STATUS_IS_OK(ret)) {
+ return ret;
+ }
+ return ret;
+}
diff --git a/source4/rpc_server/lsa/lsa.h b/source4/rpc_server/lsa/lsa.h
new file mode 100644
index 0000000000..ffdf96d091
--- /dev/null
+++ b/source4/rpc_server/lsa/lsa.h
@@ -0,0 +1,68 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the lsarpc pipe
+
+ Copyright (C) Andrew Tridgell 2004
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "rpc_server/dcerpc_server.h"
+#include "rpc_server/common/common.h"
+#include "auth/auth.h"
+#include "dsdb/samdb/samdb.h"
+#include "libcli/ldap/ldap_ndr.h"
+#include "lib/ldb/include/ldb_errors.h"
+#include "libcli/security/security.h"
+#include "libcli/auth/libcli_auth.h"
+#include "param/secrets.h"
+#include "../lib/util/util_ldb.h"
+#include "librpc/gen_ndr/ndr_dssetup.h"
+#include "param/param.h"
+
+/*
+ state associated with a lsa_OpenPolicy() operation
+*/
+struct lsa_policy_state {
+ struct dcesrv_handle *handle;
+ struct ldb_context *sam_ldb;
+ uint32_t access_mask;
+ struct ldb_dn *domain_dn;
+ struct ldb_dn *forest_dn;
+ struct ldb_dn *builtin_dn;
+ struct ldb_dn *system_dn;
+ const char *domain_name;
+ const char *domain_dns;
+ const char *forest_dns;
+ struct dom_sid *domain_sid;
+ struct GUID domain_guid;
+ struct dom_sid *builtin_sid;
+ struct dom_sid *nt_authority_sid;
+ struct dom_sid *creator_owner_domain_sid;
+ struct dom_sid *world_domain_sid;
+ int mixed_domain;
+};
+
+enum lsa_handle {
+ LSA_HANDLE_POLICY,
+ LSA_HANDLE_ACCOUNT,
+ LSA_HANDLE_SECRET,
+ LSA_HANDLE_TRUSTED_DOMAIN
+};
+
+#include "rpc_server/lsa/proto.h"
+
diff --git a/source4/rpc_server/lsa/lsa_init.c b/source4/rpc_server/lsa/lsa_init.c
new file mode 100644
index 0000000000..8d8417109f
--- /dev/null
+++ b/source4/rpc_server/lsa/lsa_init.c
@@ -0,0 +1,250 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the lsarpc pipe
+
+ Copyright (C) Andrew Tridgell 2004
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "rpc_server/lsa/lsa.h"
+
+NTSTATUS dcesrv_lsa_get_policy_state(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_policy_state **_state)
+{
+ struct lsa_policy_state *state;
+ struct ldb_dn *partitions_basedn;
+ struct ldb_result *dom_res;
+ const char *dom_attrs[] = {
+ "objectSid",
+ "objectGUID",
+ "nTMixedDomain",
+ "fSMORoleOwner",
+ NULL
+ };
+ struct ldb_result *ref_res;
+ struct ldb_result *forest_ref_res;
+ const char *ref_attrs[] = {
+ "nETBIOSName",
+ "dnsRoot",
+ NULL
+ };
+ int ret;
+
+ state = talloc(mem_ctx, struct lsa_policy_state);
+ if (!state) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* make sure the sam database is accessible */
+ state->sam_ldb = samdb_connect(state, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, dce_call->conn->auth_state.session_info);
+ if (state->sam_ldb == NULL) {
+ return NT_STATUS_INVALID_SYSTEM_SERVICE;
+ }
+
+ partitions_basedn = samdb_partitions_dn(state->sam_ldb, mem_ctx);
+
+ /* work out the domain_dn - useful for so many calls its worth
+ fetching here */
+ state->domain_dn = samdb_base_dn(state->sam_ldb);
+ if (!state->domain_dn) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* work out the forest root_dn - useful for so many calls its worth
+ fetching here */
+ state->forest_dn = samdb_root_dn(state->sam_ldb);
+ if (!state->forest_dn) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = ldb_search(state->sam_ldb, mem_ctx, &dom_res,
+ state->domain_dn, LDB_SCOPE_BASE, dom_attrs, NULL);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_INVALID_SYSTEM_SERVICE;
+ }
+ if (dom_res->count != 1) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ state->domain_sid = samdb_result_dom_sid(state, dom_res->msgs[0], "objectSid");
+ if (!state->domain_sid) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ state->domain_guid = samdb_result_guid(dom_res->msgs[0], "objectGUID");
+ if (!state->domain_sid) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ state->mixed_domain = ldb_msg_find_attr_as_uint(dom_res->msgs[0], "nTMixedDomain", 0);
+
+ talloc_free(dom_res);
+
+ ret = ldb_search(state->sam_ldb, state, &ref_res,
+ partitions_basedn, LDB_SCOPE_SUBTREE, ref_attrs,
+ "(&(objectclass=crossRef)(ncName=%s))",
+ ldb_dn_get_linearized(state->domain_dn));
+
+ if (ret != LDB_SUCCESS) {
+ talloc_free(ref_res);
+ return NT_STATUS_INVALID_SYSTEM_SERVICE;
+ }
+ if (ref_res->count != 1) {
+ talloc_free(ref_res);
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ state->domain_name = ldb_msg_find_attr_as_string(ref_res->msgs[0], "nETBIOSName", NULL);
+ if (!state->domain_name) {
+ talloc_free(ref_res);
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+ talloc_steal(state, state->domain_name);
+
+ state->domain_dns = ldb_msg_find_attr_as_string(ref_res->msgs[0], "dnsRoot", NULL);
+ if (!state->domain_dns) {
+ talloc_free(ref_res);
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+ talloc_steal(state, state->domain_dns);
+
+ talloc_free(ref_res);
+
+ ret = ldb_search(state->sam_ldb, state, &forest_ref_res,
+ partitions_basedn, LDB_SCOPE_SUBTREE, ref_attrs,
+ "(&(objectclass=crossRef)(ncName=%s))",
+ ldb_dn_get_linearized(state->forest_dn));
+
+ if (ret != LDB_SUCCESS) {
+ talloc_free(forest_ref_res);
+ return NT_STATUS_INVALID_SYSTEM_SERVICE;
+ }
+ if (forest_ref_res->count != 1) {
+ talloc_free(forest_ref_res);
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ state->forest_dns = ldb_msg_find_attr_as_string(forest_ref_res->msgs[0], "dnsRoot", NULL);
+ if (!state->forest_dns) {
+ talloc_free(forest_ref_res);
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+ talloc_steal(state, state->forest_dns);
+
+ talloc_free(forest_ref_res);
+
+ /* work out the builtin_dn - useful for so many calls its worth
+ fetching here */
+ state->builtin_dn = samdb_search_dn(state->sam_ldb, state, state->domain_dn, "(objectClass=builtinDomain)");
+ if (!state->builtin_dn) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ /* work out the system_dn - useful for so many calls its worth
+ fetching here */
+ state->system_dn = samdb_search_dn(state->sam_ldb, state,
+ state->domain_dn, "(&(objectClass=container)(cn=System))");
+ if (!state->system_dn) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ state->builtin_sid = dom_sid_parse_talloc(state, SID_BUILTIN);
+ if (!state->builtin_sid) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ state->nt_authority_sid = dom_sid_parse_talloc(state, SID_NT_AUTHORITY);
+ if (!state->nt_authority_sid) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ state->creator_owner_domain_sid = dom_sid_parse_talloc(state, SID_CREATOR_OWNER_DOMAIN);
+ if (!state->creator_owner_domain_sid) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ state->world_domain_sid = dom_sid_parse_talloc(state, SID_WORLD_DOMAIN);
+ if (!state->world_domain_sid) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ *_state = state;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ lsa_OpenPolicy2
+*/
+NTSTATUS dcesrv_lsa_OpenPolicy2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_OpenPolicy2 *r)
+{
+ NTSTATUS status;
+ struct lsa_policy_state *state;
+ struct dcesrv_handle *handle;
+
+ ZERO_STRUCTP(r->out.handle);
+
+ if (r->in.attr != NULL &&
+ r->in.attr->root_dir != NULL) {
+ /* MS-LSAD 3.1.4.4.1 */
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = dcesrv_lsa_get_policy_state(dce_call, mem_ctx, &state);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ handle = dcesrv_handle_new(dce_call->context, LSA_HANDLE_POLICY);
+ if (!handle) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ handle->data = talloc_steal(handle, state);
+
+ /* need to check the access mask against - need ACLs - fails
+ WSPP test */
+ state->access_mask = r->in.access_mask;
+ state->handle = handle;
+ *r->out.handle = handle->wire_handle;
+
+ /* note that we have completely ignored the attr element of
+ the OpenPolicy. As far as I can tell, this is what w2k3
+ does */
+
+ return NT_STATUS_OK;
+}
+
+/*
+ lsa_OpenPolicy
+ a wrapper around lsa_OpenPolicy2
+*/
+NTSTATUS dcesrv_lsa_OpenPolicy(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_OpenPolicy *r)
+{
+ struct lsa_OpenPolicy2 r2;
+
+ r2.in.system_name = NULL;
+ r2.in.attr = r->in.attr;
+ r2.in.access_mask = r->in.access_mask;
+ r2.out.handle = r->out.handle;
+
+ return dcesrv_lsa_OpenPolicy2(dce_call, mem_ctx, &r2);
+}
+
+
diff --git a/source4/rpc_server/lsa/lsa_lookup.c b/source4/rpc_server/lsa/lsa_lookup.c
new file mode 100644
index 0000000000..dc47d3783a
--- /dev/null
+++ b/source4/rpc_server/lsa/lsa_lookup.c
@@ -0,0 +1,1007 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the lsarpc pipe
+
+ Copyright (C) Andrew Tridgell 2004
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "rpc_server/lsa/lsa.h"
+
+static const struct {
+ const char *domain;
+ const char *name;
+ const char *sid;
+ int rtype;
+} well_known[] = {
+ {
+ .name = "EVERYONE",
+ .sid = SID_WORLD,
+ .rtype = SID_NAME_WKN_GRP,
+ },
+ {
+ .name = "CREATOR OWNER",
+ .sid = SID_CREATOR_OWNER,
+ .rtype = SID_NAME_WKN_GRP,
+ },
+ {
+ .name = "CREATOR GROUP",
+ .sid = SID_CREATOR_GROUP,
+ .rtype = SID_NAME_WKN_GRP,
+ },
+ {
+ .name = "Owner Rights",
+ .sid = SID_OWNER_RIGHTS,
+ .rtype = SID_NAME_WKN_GRP,
+ },
+ {
+ .domain = "NT AUTHORITY",
+ .name = "Dialup",
+ .sid = SID_NT_DIALUP,
+ .rtype = SID_NAME_WKN_GRP,
+ },
+ {
+ .domain = "NT AUTHORITY",
+ .name = "Network",
+ .sid = SID_NT_NETWORK,
+ .rtype = SID_NAME_WKN_GRP,
+ },
+ {
+ .domain = "NT AUTHORITY",
+ .name = "Batch",
+ .sid = SID_NT_BATCH,
+ .rtype = SID_NAME_WKN_GRP,
+ },
+ {
+ .domain = "NT AUTHORITY",
+ .name = "Interactive",
+ .sid = SID_NT_INTERACTIVE,
+ .rtype = SID_NAME_WKN_GRP,
+ },
+ {
+ .domain = "NT AUTHORITY",
+ .name = "Service",
+ .sid = SID_NT_SERVICE,
+ .rtype = SID_NAME_WKN_GRP,
+ },
+ {
+ .domain = "NT AUTHORITY",
+ .name = "ANONYMOUS LOGON",
+ .sid = SID_NT_ANONYMOUS,
+ .rtype = SID_NAME_WKN_GRP,
+ },
+ {
+ .domain = "NT AUTHORITY",
+ .name = "Proxy",
+ .sid = SID_NT_PROXY,
+ .rtype = SID_NAME_WKN_GRP,
+ },
+ {
+ .domain = "NT AUTHORITY",
+ .name = "ServerLogon",
+ .sid = SID_NT_ENTERPRISE_DCS,
+ .rtype = SID_NAME_WKN_GRP,
+ },
+ {
+ .domain = "NT AUTHORITY",
+ .name = "Self",
+ .sid = SID_NT_SELF,
+ .rtype = SID_NAME_WKN_GRP,
+ },
+ {
+ .domain = "NT AUTHORITY",
+ .name = "Authenticated Users",
+ .sid = SID_NT_AUTHENTICATED_USERS,
+ .rtype = SID_NAME_WKN_GRP,
+ },
+ {
+ .domain = "NT AUTHORITY",
+ .name = "Restricted",
+ .sid = SID_NT_RESTRICTED,
+ .rtype = SID_NAME_WKN_GRP,
+ },
+ {
+ .domain = "NT AUTHORITY",
+ .name = "Terminal Server User",
+ .sid = SID_NT_TERMINAL_SERVER_USERS,
+ .rtype = SID_NAME_WKN_GRP,
+ },
+ {
+ .domain = "NT AUTHORITY",
+ .name = "Remote Interactive Logon",
+ .sid = SID_NT_REMOTE_INTERACTIVE,
+ .rtype = SID_NAME_WKN_GRP,
+ },
+ {
+ .domain = "NT AUTHORITY",
+ .name = "This Organization",
+ .sid = SID_NT_THIS_ORGANISATION,
+ .rtype = SID_NAME_WKN_GRP,
+ },
+ {
+ .domain = "NT AUTHORITY",
+ .name = "SYSTEM",
+ .sid = SID_NT_SYSTEM,
+ .rtype = SID_NAME_WKN_GRP,
+ },
+ {
+ .domain = "NT AUTHORITY",
+ .name = "Local Service",
+ .sid = SID_NT_LOCAL_SERVICE,
+ .rtype = SID_NAME_WKN_GRP,
+ },
+ {
+ .domain = "NT AUTHORITY",
+ .name = "Network Service",
+ .sid = SID_NT_NETWORK_SERVICE,
+ .rtype = SID_NAME_WKN_GRP,
+ },
+ {
+ .domain = "NT AUTHORITY",
+ .name = "Digest Authentication",
+ .sid = SID_NT_DIGEST_AUTHENTICATION,
+ .rtype = SID_NAME_WKN_GRP,
+ },
+ {
+ .domain = "NT AUTHORITY",
+ .name = "Enterprise Domain Controllers",
+ .sid = SID_NT_ENTERPRISE_DCS,
+ .rtype = SID_NAME_WKN_GRP,
+ },
+ {
+ .domain = "NT AUTHORITY",
+ .name = "NTLM Authentication",
+ .sid = SID_NT_NTLM_AUTHENTICATION,
+ .rtype = SID_NAME_WKN_GRP,
+ },
+ {
+ .domain = "NT AUTHORITY",
+ .name = "Other Organization",
+ .sid = SID_NT_OTHER_ORGANISATION,
+ .rtype = SID_NAME_WKN_GRP,
+ },
+ {
+ .domain = "NT AUTHORITY",
+ .name = "SChannel Authentication",
+ .sid = SID_NT_SCHANNEL_AUTHENTICATION,
+ .rtype = SID_NAME_WKN_GRP,
+ },
+ {
+ .domain = "NT AUTHORITY",
+ .name = "IUSR",
+ .sid = SID_NT_IUSR,
+ .rtype = SID_NAME_WKN_GRP,
+ },
+ {
+ .sid = NULL,
+ }
+};
+
+static NTSTATUS lookup_well_known_names(TALLOC_CTX *mem_ctx, const char *domain,
+ const char *name, const char **authority_name,
+ struct dom_sid **sid, uint32_t *rtype)
+{
+ int i;
+ for (i=0; well_known[i].sid; i++) {
+ if (domain) {
+ if (strcasecmp_m(domain, well_known[i].domain) == 0
+ && strcasecmp_m(name, well_known[i].name) == 0) {
+ *authority_name = well_known[i].domain;
+ *sid = dom_sid_parse_talloc(mem_ctx, well_known[i].sid);
+ *rtype = well_known[i].rtype;
+ return NT_STATUS_OK;
+ }
+ } else {
+ if (strcasecmp_m(name, well_known[i].name) == 0) {
+ *authority_name = well_known[i].domain;
+ *sid = dom_sid_parse_talloc(mem_ctx, well_known[i].sid);
+ *rtype = well_known[i].rtype;
+ return NT_STATUS_OK;
+ }
+ }
+ }
+ return NT_STATUS_NOT_FOUND;
+}
+
+static NTSTATUS lookup_well_known_sids(TALLOC_CTX *mem_ctx,
+ const char *sid_str, const char **authority_name,
+ const char **name, uint32_t *rtype)
+{
+ int i;
+ for (i=0; well_known[i].sid; i++) {
+ if (strcasecmp_m(sid_str, well_known[i].sid) == 0) {
+ *authority_name = well_known[i].domain;
+ *name = well_known[i].name;
+ *rtype = well_known[i].rtype;
+ return NT_STATUS_OK;
+ }
+ }
+ return NT_STATUS_NOT_FOUND;
+}
+
+/*
+ lookup a SID for 1 name
+*/
+static NTSTATUS dcesrv_lsa_lookup_name(struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx,
+ struct lsa_policy_state *state, TALLOC_CTX *mem_ctx,
+ const char *name, const char **authority_name,
+ struct dom_sid **sid, enum lsa_SidType *rtype)
+{
+ int ret, atype, i;
+ struct ldb_message **res;
+ const char * const attrs[] = { "objectSid", "sAMAccountType", NULL};
+ const char *p;
+ const char *domain;
+ const char *username;
+ struct ldb_dn *domain_dn;
+ struct dom_sid *domain_sid;
+ NTSTATUS status;
+
+ p = strchr_m(name, '\\');
+ if (p != NULL) {
+ domain = talloc_strndup(mem_ctx, name, p-name);
+ if (!domain) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ username = p + 1;
+ } else if (strchr_m(name, '@')) {
+ status = crack_name_to_nt4_name(mem_ctx, ev_ctx, lp_ctx, DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL, name, &domain, &username);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(3, ("Failed to crack name %s into an NT4 name: %s\n", name, nt_errstr(status)));
+ return status;
+ }
+ } else {
+ domain = NULL;
+ username = name;
+ }
+
+ if (!domain) {
+ /* Look up table of well known names */
+ status = lookup_well_known_names(mem_ctx, NULL, username, authority_name, sid, rtype);
+ if (NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_OK;
+ }
+
+ if (strcasecmp_m(username, NAME_NT_AUTHORITY) == 0) {
+ *authority_name = NAME_NT_AUTHORITY;
+ *sid = dom_sid_parse_talloc(mem_ctx, SID_NT_AUTHORITY);
+ *rtype = SID_NAME_DOMAIN;
+ return NT_STATUS_OK;
+ }
+ if (strcasecmp_m(username, NAME_BUILTIN) == 0) {
+ *authority_name = NAME_BUILTIN;
+ *sid = dom_sid_parse_talloc(mem_ctx, SID_BUILTIN);
+ *rtype = SID_NAME_DOMAIN;
+ return NT_STATUS_OK;
+ }
+ if (strcasecmp_m(username, state->domain_dns) == 0) {
+ *authority_name = state->domain_name;
+ *sid = state->domain_sid;
+ *rtype = SID_NAME_DOMAIN;
+ return NT_STATUS_OK;
+ }
+ if (strcasecmp_m(username, state->domain_name) == 0) {
+ *authority_name = state->domain_name;
+ *sid = state->domain_sid;
+ *rtype = SID_NAME_DOMAIN;
+ return NT_STATUS_OK;
+ }
+
+ /* Perhaps this is a well known user? */
+ name = talloc_asprintf(mem_ctx, "%s\\%s", NAME_NT_AUTHORITY, username);
+ if (!name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ status = dcesrv_lsa_lookup_name(ev_ctx, lp_ctx, state, mem_ctx, name, authority_name, sid, rtype);
+ if (NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* Perhaps this is a BUILTIN user? */
+ name = talloc_asprintf(mem_ctx, "%s\\%s", NAME_BUILTIN, username);
+ if (!name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ status = dcesrv_lsa_lookup_name(ev_ctx, lp_ctx, state, mem_ctx, name, authority_name, sid, rtype);
+ if (NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* OK, I give up - perhaps we need to assume the user is in our domain? */
+ name = talloc_asprintf(mem_ctx, "%s\\%s", state->domain_name, username);
+ if (!name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ status = dcesrv_lsa_lookup_name(ev_ctx, lp_ctx, state, mem_ctx, name, authority_name, sid, rtype);
+ if (NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return STATUS_SOME_UNMAPPED;
+ } else if (strcasecmp_m(domain, NAME_NT_AUTHORITY) == 0) {
+ if (!*username) {
+ *authority_name = NAME_NT_AUTHORITY;
+ *sid = dom_sid_parse_talloc(mem_ctx, SID_NT_AUTHORITY);
+ *rtype = SID_NAME_DOMAIN;
+ return NT_STATUS_OK;
+ }
+
+ /* Look up table of well known names */
+ return lookup_well_known_names(mem_ctx, domain, username, authority_name,
+ sid, rtype);
+ } else if (strcasecmp_m(domain, NAME_BUILTIN) == 0) {
+ *authority_name = NAME_BUILTIN;
+ domain_dn = state->builtin_dn;
+ } else if (strcasecmp_m(domain, state->domain_dns) == 0) {
+ *authority_name = state->domain_name;
+ domain_dn = state->domain_dn;
+ } else if (strcasecmp_m(domain, state->domain_name) == 0) {
+ *authority_name = state->domain_name;
+ domain_dn = state->domain_dn;
+ } else {
+ /* Not local, need to ask winbind in future */
+ return STATUS_SOME_UNMAPPED;
+ }
+
+ ret = gendb_search_dn(state->sam_ldb, mem_ctx, domain_dn, &res, attrs);
+ if (ret == 1) {
+ domain_sid = samdb_result_dom_sid(mem_ctx, res[0], "objectSid");
+ if (domain_sid == NULL) {
+ return NT_STATUS_INVALID_SID;
+ }
+ } else {
+ return NT_STATUS_INVALID_SID;
+ }
+
+ if (!*username) {
+ *sid = domain_sid;
+ *rtype = SID_NAME_DOMAIN;
+ return NT_STATUS_OK;
+ }
+
+ ret = gendb_search(state->sam_ldb, mem_ctx, domain_dn, &res, attrs,
+ "(&(sAMAccountName=%s)(objectSid=*))",
+ ldb_binary_encode_string(mem_ctx, username));
+ if (ret == -1) {
+ return NT_STATUS_INVALID_SID;
+ }
+
+ for (i=0; i < ret; i++) {
+ *sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
+ if (*sid == NULL) {
+ return NT_STATUS_INVALID_SID;
+ }
+
+ /* Check that this is in the domain */
+ if (!dom_sid_in_domain(domain_sid, *sid)) {
+ continue;
+ }
+
+ atype = samdb_result_uint(res[i], "sAMAccountType", 0);
+
+ *rtype = samdb_atype_map(atype);
+ if (*rtype == SID_NAME_UNKNOWN) {
+ return STATUS_SOME_UNMAPPED;
+ }
+
+ return NT_STATUS_OK;
+ }
+
+ /* need to check for an allocated sid */
+
+ return NT_STATUS_INVALID_SID;
+}
+
+
+/*
+ add to the lsa_RefDomainList for LookupSids and LookupNames
+*/
+static NTSTATUS dcesrv_lsa_authority_list(struct lsa_policy_state *state, TALLOC_CTX *mem_ctx,
+ enum lsa_SidType rtype,
+ const char *authority_name,
+ struct dom_sid *sid,
+ struct lsa_RefDomainList *domains,
+ uint32_t *sid_index)
+{
+ struct dom_sid *authority_sid;
+ int i;
+
+ if (rtype != SID_NAME_DOMAIN) {
+ authority_sid = dom_sid_dup(mem_ctx, sid);
+ if (authority_sid == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ authority_sid->num_auths--;
+ } else {
+ authority_sid = sid;
+ }
+
+ /* see if we've already done this authority name */
+ for (i=0;i<domains->count;i++) {
+ if (strcasecmp_m(authority_name, domains->domains[i].name.string) == 0) {
+ *sid_index = i;
+ return NT_STATUS_OK;
+ }
+ }
+
+ domains->domains = talloc_realloc(domains,
+ domains->domains,
+ struct lsa_DomainInfo,
+ domains->count+1);
+ if (domains->domains == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ domains->domains[i].name.string = authority_name;
+ domains->domains[i].sid = authority_sid;
+ domains->count++;
+ domains->max_size = LSA_REF_DOMAIN_LIST_MULTIPLIER * domains->count;
+ *sid_index = i;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ lookup a name for 1 SID
+*/
+static NTSTATUS dcesrv_lsa_lookup_sid(struct lsa_policy_state *state, TALLOC_CTX *mem_ctx,
+ struct dom_sid *sid, const char *sid_str,
+ const char **authority_name,
+ const char **name, enum lsa_SidType *rtype)
+{
+ NTSTATUS status;
+ int ret;
+ uint32_t atype;
+ struct ldb_message **res;
+ struct ldb_dn *domain_dn;
+ const char * const attrs[] = { "sAMAccountName", "sAMAccountType", "cn", NULL};
+
+ status = lookup_well_known_sids(mem_ctx, sid_str, authority_name, name, rtype);
+ if (NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (dom_sid_in_domain(state->domain_sid, sid)) {
+ *authority_name = state->domain_name;
+ domain_dn = state->domain_dn;
+ } else if (dom_sid_in_domain(state->builtin_sid, sid)) {
+ *authority_name = NAME_BUILTIN;
+ domain_dn = state->builtin_dn;
+ } else {
+ /* Not well known, our domain or built in */
+
+ /* In future, we must look at SID histories, and at trusted domains via winbind */
+
+ return NT_STATUS_NOT_FOUND;
+ }
+
+ ret = gendb_search(state->sam_ldb, mem_ctx, domain_dn, &res, attrs,
+ "objectSid=%s", ldap_encode_ndr_dom_sid(mem_ctx, sid));
+ if (ret == 1) {
+ *name = ldb_msg_find_attr_as_string(res[0], "sAMAccountName", NULL);
+ if (!*name) {
+ *name = ldb_msg_find_attr_as_string(res[0], "cn", NULL);
+ if (!*name) {
+ *name = talloc_strdup(mem_ctx, sid_str);
+ NT_STATUS_HAVE_NO_MEMORY(*name);
+ }
+ }
+
+ atype = samdb_result_uint(res[0], "sAMAccountType", 0);
+
+ *rtype = samdb_atype_map(atype);
+
+ return NT_STATUS_OK;
+ }
+
+ /* need to re-add a check for an allocated sid */
+
+ return NT_STATUS_NOT_FOUND;
+}
+
+
+/*
+ lsa_LookupSids2
+*/
+NTSTATUS dcesrv_lsa_LookupSids2(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_LookupSids2 *r)
+{
+ struct lsa_policy_state *state;
+ struct lsa_RefDomainList *domains = NULL;
+ int i;
+ NTSTATUS status = NT_STATUS_OK;
+
+ if (r->in.level < LSA_LOOKUP_NAMES_ALL ||
+ r->in.level > LSA_LOOKUP_NAMES_RODC_REFERRAL_TO_FULL_DC) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ *r->out.domains = NULL;
+
+ /* NOTE: the WSPP test suite tries SIDs with invalid revision numbers,
+ and expects NT_STATUS_INVALID_PARAMETER back - we just treat it as
+ an unknown SID. We could add a SID validator here. (tridge)
+ MS-DTYP 2.4.2
+ */
+
+ status = dcesrv_lsa_get_policy_state(dce_call, mem_ctx, &state);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ domains = talloc_zero(r->out.domains, struct lsa_RefDomainList);
+ if (domains == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ *r->out.domains = domains;
+
+ r->out.names = talloc_zero(mem_ctx, struct lsa_TransNameArray2);
+ if (r->out.names == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ *r->out.count = 0;
+
+ r->out.names->names = talloc_array(r->out.names, struct lsa_TranslatedName2,
+ r->in.sids->num_sids);
+ if (r->out.names->names == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0;i<r->in.sids->num_sids;i++) {
+ struct dom_sid *sid = r->in.sids->sids[i].sid;
+ char *sid_str = dom_sid_string(mem_ctx, sid);
+ const char *name, *authority_name;
+ enum lsa_SidType rtype;
+ uint32_t sid_index;
+ NTSTATUS status2;
+
+ r->out.names->count++;
+
+ r->out.names->names[i].sid_type = SID_NAME_UNKNOWN;
+ r->out.names->names[i].name.string = sid_str;
+ r->out.names->names[i].sid_index = 0xFFFFFFFF;
+ r->out.names->names[i].unknown = 0;
+
+ if (sid_str == NULL) {
+ r->out.names->names[i].name.string = "(SIDERROR)";
+ status = STATUS_SOME_UNMAPPED;
+ continue;
+ }
+
+ status2 = dcesrv_lsa_lookup_sid(state, mem_ctx, sid, sid_str,
+ &authority_name, &name, &rtype);
+ if (!NT_STATUS_IS_OK(status2)) {
+ status = STATUS_SOME_UNMAPPED;
+ continue;
+ }
+
+ /* set up the authority table */
+ status2 = dcesrv_lsa_authority_list(state, mem_ctx, rtype,
+ authority_name, sid,
+ domains, &sid_index);
+ if (!NT_STATUS_IS_OK(status2)) {
+ continue;
+ }
+
+ r->out.names->names[i].sid_type = rtype;
+ r->out.names->names[i].name.string = name;
+ r->out.names->names[i].sid_index = sid_index;
+ r->out.names->names[i].unknown = 0;
+
+ (*r->out.count)++;
+ }
+
+ if (*r->out.count == 0) {
+ return NT_STATUS_NONE_MAPPED;
+ }
+ if (*r->out.count != r->in.sids->num_sids) {
+ return STATUS_SOME_UNMAPPED;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ lsa_LookupSids3
+
+ Identical to LookupSids2, but doesn't take a policy handle
+
+*/
+NTSTATUS dcesrv_lsa_LookupSids3(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_LookupSids3 *r)
+{
+ struct lsa_LookupSids2 r2;
+ struct lsa_OpenPolicy2 pol;
+ NTSTATUS status;
+ struct dcesrv_handle *h;
+
+ ZERO_STRUCT(r2);
+
+ /* No policy handle on the wire, so make one up here */
+ r2.in.handle = talloc(mem_ctx, struct policy_handle);
+ if (!r2.in.handle) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ pol.out.handle = r2.in.handle;
+ pol.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ pol.in.attr = NULL;
+ pol.in.system_name = NULL;
+ status = dcesrv_lsa_OpenPolicy2(dce_call, mem_ctx, &pol);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* ensure this handle goes away at the end of this call */
+ DCESRV_PULL_HANDLE(h, r2.in.handle, LSA_HANDLE_POLICY);
+ talloc_steal(mem_ctx, h);
+
+ r2.in.sids = r->in.sids;
+ r2.in.names = r->in.names;
+ r2.in.level = r->in.level;
+ r2.in.count = r->in.count;
+ r2.in.unknown1 = r->in.unknown1;
+ r2.in.unknown2 = r->in.unknown2;
+ r2.out.count = r->out.count;
+ r2.out.names = r->out.names;
+ r2.out.domains = r->out.domains;
+
+ status = dcesrv_lsa_LookupSids2(dce_call, mem_ctx, &r2);
+
+ r->out.domains = r2.out.domains;
+ r->out.names = r2.out.names;
+ r->out.count = r2.out.count;
+
+ return status;
+}
+
+
+/*
+ lsa_LookupSids
+*/
+NTSTATUS dcesrv_lsa_LookupSids(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_LookupSids *r)
+{
+ struct lsa_LookupSids2 r2;
+ NTSTATUS status;
+ int i;
+
+ ZERO_STRUCT(r2);
+
+ r2.in.handle = r->in.handle;
+ r2.in.sids = r->in.sids;
+ r2.in.names = NULL;
+ r2.in.level = r->in.level;
+ r2.in.count = r->in.count;
+ r2.in.unknown1 = 0;
+ r2.in.unknown2 = 0;
+ r2.out.count = r->out.count;
+ r2.out.names = NULL;
+ r2.out.domains = r->out.domains;
+
+ status = dcesrv_lsa_LookupSids2(dce_call, mem_ctx, &r2);
+ /* we deliberately don't check for error from the above,
+ as even on error we are supposed to return the names */
+
+ r->out.domains = r2.out.domains;
+ if (!r2.out.names) {
+ r->out.names = NULL;
+ return status;
+ }
+
+ r->out.names = talloc(mem_ctx, struct lsa_TransNameArray);
+ if (r->out.names == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ r->out.names->count = r2.out.names->count;
+ r->out.names->names = talloc_array(r->out.names, struct lsa_TranslatedName,
+ r->out.names->count);
+ if (r->out.names->names == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ for (i=0;i<r->out.names->count;i++) {
+ r->out.names->names[i].sid_type = r2.out.names->names[i].sid_type;
+ r->out.names->names[i].name.string = r2.out.names->names[i].name.string;
+ r->out.names->names[i].sid_index = r2.out.names->names[i].sid_index;
+ }
+
+ return status;
+}
+
+
+/*
+ lsa_LookupNames3
+*/
+NTSTATUS dcesrv_lsa_LookupNames3(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_LookupNames3 *r)
+{
+ struct lsa_policy_state *policy_state;
+ struct dcesrv_handle *policy_handle;
+ int i;
+ struct loadparm_context *lp_ctx = dce_call->conn->dce_ctx->lp_ctx;
+ struct lsa_RefDomainList *domains;
+
+ DCESRV_PULL_HANDLE(policy_handle, r->in.handle, LSA_HANDLE_POLICY);
+
+ if (r->in.level < LSA_LOOKUP_NAMES_ALL ||
+ r->in.level > LSA_LOOKUP_NAMES_RODC_REFERRAL_TO_FULL_DC) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ policy_state = policy_handle->data;
+
+ *r->out.domains = NULL;
+
+ domains = talloc_zero(mem_ctx, struct lsa_RefDomainList);
+ if (domains == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ *r->out.domains = domains;
+
+ r->out.sids = talloc_zero(mem_ctx, struct lsa_TransSidArray3);
+ if (r->out.sids == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ *r->out.count = 0;
+
+ r->out.sids->sids = talloc_array(r->out.sids, struct lsa_TranslatedSid3,
+ r->in.num_names);
+ if (r->out.sids->sids == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0;i<r->in.num_names;i++) {
+ const char *name = r->in.names[i].string;
+ const char *authority_name;
+ struct dom_sid *sid;
+ uint32_t sid_index;
+ enum lsa_SidType rtype;
+ NTSTATUS status2;
+
+ r->out.sids->count++;
+
+ r->out.sids->sids[i].sid_type = SID_NAME_UNKNOWN;
+ r->out.sids->sids[i].sid = NULL;
+ r->out.sids->sids[i].sid_index = 0xFFFFFFFF;
+ r->out.sids->sids[i].flags = 0;
+
+ status2 = dcesrv_lsa_lookup_name(dce_call->event_ctx, lp_ctx, policy_state, mem_ctx, name, &authority_name, &sid, &rtype);
+ if (!NT_STATUS_IS_OK(status2) || sid->num_auths == 0) {
+ continue;
+ }
+
+ status2 = dcesrv_lsa_authority_list(policy_state, mem_ctx, rtype, authority_name,
+ sid, domains, &sid_index);
+ if (!NT_STATUS_IS_OK(status2)) {
+ continue;
+ }
+
+ r->out.sids->sids[i].sid_type = rtype;
+ r->out.sids->sids[i].sid = sid;
+ r->out.sids->sids[i].sid_index = sid_index;
+ r->out.sids->sids[i].flags = 0;
+
+ (*r->out.count)++;
+ }
+
+ if (*r->out.count == 0) {
+ return NT_STATUS_NONE_MAPPED;
+ }
+ if (*r->out.count != r->in.num_names) {
+ return STATUS_SOME_UNMAPPED;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ lsa_LookupNames4
+
+ Identical to LookupNames3, but doesn't take a policy handle
+
+*/
+NTSTATUS dcesrv_lsa_LookupNames4(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_LookupNames4 *r)
+{
+ struct lsa_LookupNames3 r2;
+ struct lsa_OpenPolicy2 pol;
+ NTSTATUS status;
+ struct dcesrv_handle *h;
+
+ ZERO_STRUCT(r2);
+
+ /* No policy handle on the wire, so make one up here */
+ r2.in.handle = talloc(mem_ctx, struct policy_handle);
+ if (!r2.in.handle) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ pol.out.handle = r2.in.handle;
+ pol.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ pol.in.attr = NULL;
+ pol.in.system_name = NULL;
+ status = dcesrv_lsa_OpenPolicy2(dce_call, mem_ctx, &pol);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* ensure this handle goes away at the end of this call */
+ DCESRV_PULL_HANDLE(h, r2.in.handle, LSA_HANDLE_POLICY);
+ talloc_steal(mem_ctx, h);
+
+ r2.in.num_names = r->in.num_names;
+ r2.in.names = r->in.names;
+ r2.in.level = r->in.level;
+ r2.in.sids = r->in.sids;
+ r2.in.count = r->in.count;
+ r2.in.lookup_options = r->in.lookup_options;
+ r2.in.client_revision = r->in.client_revision;
+ r2.out.domains = r->out.domains;
+ r2.out.sids = r->out.sids;
+ r2.out.count = r->out.count;
+
+ status = dcesrv_lsa_LookupNames3(dce_call, mem_ctx, &r2);
+
+ r->out.domains = r2.out.domains;
+ r->out.sids = r2.out.sids;
+ r->out.count = r2.out.count;
+ return status;
+}
+
+/*
+ lsa_LookupNames2
+*/
+NTSTATUS dcesrv_lsa_LookupNames2(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_LookupNames2 *r)
+{
+ struct lsa_policy_state *state;
+ struct dcesrv_handle *h;
+ int i;
+ struct loadparm_context *lp_ctx = dce_call->conn->dce_ctx->lp_ctx;
+ struct lsa_RefDomainList *domains;
+
+ *r->out.domains = NULL;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, LSA_HANDLE_POLICY);
+
+ if (r->in.level < LSA_LOOKUP_NAMES_ALL ||
+ r->in.level > LSA_LOOKUP_NAMES_RODC_REFERRAL_TO_FULL_DC) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ state = h->data;
+
+ domains = talloc_zero(mem_ctx, struct lsa_RefDomainList);
+ if (domains == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ *r->out.domains = domains;
+
+ r->out.sids = talloc_zero(mem_ctx, struct lsa_TransSidArray2);
+ if (r->out.sids == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ *r->out.count = 0;
+
+ r->out.sids->sids = talloc_array(r->out.sids, struct lsa_TranslatedSid2,
+ r->in.num_names);
+ if (r->out.sids->sids == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0;i<r->in.num_names;i++) {
+ const char *name = r->in.names[i].string;
+ const char *authority_name;
+ struct dom_sid *sid;
+ uint32_t rtype, sid_index;
+ NTSTATUS status2;
+
+ r->out.sids->count++;
+
+ r->out.sids->sids[i].sid_type = SID_NAME_UNKNOWN;
+ /* MS-LSAT 3.1.4.7 - rid zero is considered equivalent
+ to sid NULL - so we should return 0 rid for
+ unmapped entries */
+ r->out.sids->sids[i].rid = 0;
+ r->out.sids->sids[i].sid_index = 0xFFFFFFFF;
+ r->out.sids->sids[i].unknown = 0;
+
+ status2 = dcesrv_lsa_lookup_name(dce_call->event_ctx, lp_ctx, state, mem_ctx, name,
+ &authority_name, &sid, &rtype);
+ if (!NT_STATUS_IS_OK(status2)) {
+ continue;
+ }
+
+ status2 = dcesrv_lsa_authority_list(state, mem_ctx, rtype, authority_name,
+ sid, domains, &sid_index);
+ if (!NT_STATUS_IS_OK(status2)) {
+ continue;
+ }
+
+ r->out.sids->sids[i].sid_type = rtype;
+ r->out.sids->sids[i].rid = sid->sub_auths[sid->num_auths-1];
+ r->out.sids->sids[i].sid_index = sid_index;
+ r->out.sids->sids[i].unknown = 0;
+
+ (*r->out.count)++;
+ }
+
+ if (*r->out.count == 0) {
+ return NT_STATUS_NONE_MAPPED;
+ }
+ if (*r->out.count != r->in.num_names) {
+ return STATUS_SOME_UNMAPPED;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ lsa_LookupNames
+*/
+NTSTATUS dcesrv_lsa_LookupNames(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct lsa_LookupNames *r)
+{
+ struct lsa_LookupNames2 r2;
+ NTSTATUS status;
+ int i;
+
+ ZERO_STRUCT(r2);
+
+ r2.in.handle = r->in.handle;
+ r2.in.num_names = r->in.num_names;
+ r2.in.names = r->in.names;
+ r2.in.sids = NULL;
+ r2.in.level = r->in.level;
+ r2.in.count = r->in.count;
+ r2.in.lookup_options = 0;
+ r2.in.client_revision = 0;
+ r2.out.count = r->out.count;
+ r2.out.domains = r->out.domains;
+
+ status = dcesrv_lsa_LookupNames2(dce_call, mem_ctx, &r2);
+ if (r2.out.sids == NULL) {
+ return status;
+ }
+
+ r->out.sids = talloc(mem_ctx, struct lsa_TransSidArray);
+ if (r->out.sids == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ r->out.sids->count = r2.out.sids->count;
+ r->out.sids->sids = talloc_array(r->out.sids, struct lsa_TranslatedSid,
+ r->out.sids->count);
+ if (r->out.sids->sids == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ for (i=0;i<r->out.sids->count;i++) {
+ r->out.sids->sids[i].sid_type = r2.out.sids->sids[i].sid_type;
+ r->out.sids->sids[i].rid = r2.out.sids->sids[i].rid;
+ r->out.sids->sids[i].sid_index = r2.out.sids->sids[i].sid_index;
+ }
+
+ return status;
+}
+
diff --git a/source4/rpc_server/netlogon/dcerpc_netlogon.c b/source4/rpc_server/netlogon/dcerpc_netlogon.c
new file mode 100644
index 0000000000..2efddc74fc
--- /dev/null
+++ b/source4/rpc_server/netlogon/dcerpc_netlogon.c
@@ -0,0 +1,1490 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the netlogon pipe
+
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2008
+ Copyright (C) Stefan Metzmacher <metze@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "rpc_server/dcerpc_server.h"
+#include "rpc_server/common/common.h"
+#include "lib/ldb/include/ldb.h"
+#include "auth/auth.h"
+#include "auth/auth_sam_reply.h"
+#include "dsdb/samdb/samdb.h"
+#include "dsdb/common/flags.h"
+#include "rpc_server/samr/proto.h"
+#include "../lib/util/util_ldb.h"
+#include "libcli/auth/libcli_auth.h"
+#include "auth/gensec/schannel_state.h"
+#include "libcli/security/security.h"
+#include "param/param.h"
+#include "lib/messaging/irpc.h"
+#include "librpc/gen_ndr/ndr_irpc.h"
+#include "librpc/gen_ndr/ndr_netlogon.h"
+
+struct server_pipe_state {
+ struct netr_Credential client_challenge;
+ struct netr_Credential server_challenge;
+};
+
+
+static NTSTATUS dcesrv_netr_ServerReqChallenge(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_ServerReqChallenge *r)
+{
+ struct server_pipe_state *pipe_state =
+ (struct server_pipe_state *)dce_call->context->private_data;
+
+ ZERO_STRUCTP(r->out.return_credentials);
+
+ /* destroyed on pipe shutdown */
+
+ if (pipe_state) {
+ talloc_free(pipe_state);
+ dce_call->context->private_data = NULL;
+ }
+
+ pipe_state = talloc(dce_call->context, struct server_pipe_state);
+ NT_STATUS_HAVE_NO_MEMORY(pipe_state);
+
+ pipe_state->client_challenge = *r->in.credentials;
+
+ generate_random_buffer(pipe_state->server_challenge.data,
+ sizeof(pipe_state->server_challenge.data));
+
+ *r->out.return_credentials = pipe_state->server_challenge;
+
+ dce_call->context->private_data = pipe_state;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS dcesrv_netr_ServerAuthenticate3(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_ServerAuthenticate3 *r)
+{
+ struct server_pipe_state *pipe_state =
+ (struct server_pipe_state *)dce_call->context->private_data;
+ struct creds_CredentialState *creds;
+ void *sam_ctx;
+ struct samr_Password *mach_pwd;
+ uint32_t user_account_control;
+ int num_records;
+ struct ldb_message **msgs;
+ NTSTATUS nt_status;
+ const char *attrs[] = {"unicodePwd", "userAccountControl",
+ "objectSid", NULL};
+
+ const char *trust_dom_attrs[] = {"flatname", NULL};
+ const char *account_name;
+
+ ZERO_STRUCTP(r->out.return_credentials);
+ *r->out.rid = 0;
+
+ /*
+ * According to Microsoft (see bugid #6099)
+ * Windows 7 looks at the negotiate_flags
+ * returned in this structure *even if the
+ * call fails with access denied!
+ */
+ *r->out.negotiate_flags = NETLOGON_NEG_ACCOUNT_LOCKOUT |
+ NETLOGON_NEG_PERSISTENT_SAMREPL |
+ NETLOGON_NEG_ARCFOUR |
+ NETLOGON_NEG_PROMOTION_COUNT |
+ NETLOGON_NEG_CHANGELOG_BDC |
+ NETLOGON_NEG_FULL_SYNC_REPL |
+ NETLOGON_NEG_MULTIPLE_SIDS |
+ NETLOGON_NEG_REDO |
+ NETLOGON_NEG_PASSWORD_CHANGE_REFUSAL |
+ NETLOGON_NEG_SEND_PASSWORD_INFO_PDC |
+ NETLOGON_NEG_GENERIC_PASSTHROUGH |
+ NETLOGON_NEG_CONCURRENT_RPC |
+ NETLOGON_NEG_AVOID_ACCOUNT_DB_REPL |
+ NETLOGON_NEG_AVOID_SECURITYAUTH_DB_REPL |
+ NETLOGON_NEG_STRONG_KEYS |
+ NETLOGON_NEG_TRANSITIVE_TRUSTS |
+ NETLOGON_NEG_DNS_DOMAIN_TRUSTS |
+ NETLOGON_NEG_PASSWORD_SET2 |
+ NETLOGON_NEG_GETDOMAININFO |
+ NETLOGON_NEG_CROSS_FOREST_TRUSTS |
+ NETLOGON_NEG_NEUTRALIZE_NT4_EMULATION |
+ NETLOGON_NEG_RODC_PASSTHROUGH |
+ NETLOGON_NEG_AUTHENTICATED_RPC_LSASS |
+ NETLOGON_NEG_AUTHENTICATED_RPC;
+
+ if (!pipe_state) {
+ DEBUG(1, ("No challenge requested by client, cannot authenticate\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx,
+ system_session(mem_ctx, dce_call->conn->dce_ctx->lp_ctx));
+ if (sam_ctx == NULL) {
+ return NT_STATUS_INVALID_SYSTEM_SERVICE;
+ }
+
+ if (r->in.secure_channel_type == SEC_CHAN_DNS_DOMAIN) {
+ char *encoded_account = ldb_binary_encode_string(mem_ctx, r->in.account_name);
+ const char *flatname;
+ if (!encoded_account) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* Kill the trailing dot */
+ if (encoded_account[strlen(encoded_account)-1] == '.') {
+ encoded_account[strlen(encoded_account)-1] = '\0';
+ }
+
+ /* pull the user attributes */
+ num_records = gendb_search((struct ldb_context *)sam_ctx,
+ mem_ctx, NULL, &msgs,
+ trust_dom_attrs,
+ "(&(trustPartner=%s)(objectclass=trustedDomain))",
+ encoded_account);
+
+ if (num_records == 0) {
+ DEBUG(3,("Couldn't find trust [%s] in samdb.\n",
+ encoded_account));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (num_records > 1) {
+ DEBUG(0,("Found %d records matching user [%s]\n", num_records, r->in.account_name));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ flatname = ldb_msg_find_attr_as_string(msgs[0], "flatname", NULL);
+ if (!flatname) {
+ /* No flatname for this trust - we can't proceed */
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ account_name = talloc_asprintf(mem_ctx, "%s$", flatname);
+
+ if (!account_name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ } else {
+ account_name = r->in.account_name;
+ }
+
+ /* pull the user attributes */
+ num_records = gendb_search((struct ldb_context *)sam_ctx, mem_ctx,
+ NULL, &msgs, attrs,
+ "(&(sAMAccountName=%s)(objectclass=user))",
+ ldb_binary_encode_string(mem_ctx, account_name));
+
+ if (num_records == 0) {
+ DEBUG(3,("Couldn't find user [%s] in samdb.\n",
+ r->in.account_name));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (num_records > 1) {
+ DEBUG(0,("Found %d records matching user [%s]\n", num_records, r->in.account_name));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+
+ user_account_control = ldb_msg_find_attr_as_uint(msgs[0], "userAccountControl", 0);
+
+ if (user_account_control & UF_ACCOUNTDISABLE) {
+ DEBUG(1, ("Account [%s] is disabled\n", r->in.account_name));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (r->in.secure_channel_type == SEC_CHAN_WKSTA) {
+ if (!(user_account_control & UF_WORKSTATION_TRUST_ACCOUNT)) {
+ DEBUG(1, ("Client asked for a workstation secure channel, but is not a workstation (member server) acb flags: 0x%x\n", user_account_control));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ } else if (r->in.secure_channel_type == SEC_CHAN_DOMAIN ||
+ r->in.secure_channel_type == SEC_CHAN_DNS_DOMAIN) {
+ if (!(user_account_control & UF_INTERDOMAIN_TRUST_ACCOUNT)) {
+ DEBUG(1, ("Client asked for a trusted domain secure channel, but is not a trusted domain: acb flags: 0x%x\n", user_account_control));
+
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ } else if (r->in.secure_channel_type == SEC_CHAN_BDC) {
+ if (!(user_account_control & UF_SERVER_TRUST_ACCOUNT)) {
+ DEBUG(1, ("Client asked for a server secure channel, but is not a server (domain controller): acb flags: 0x%x\n", user_account_control));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ } else {
+ DEBUG(1, ("Client asked for an invalid secure channel type: %d\n",
+ r->in.secure_channel_type));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ *r->out.rid = samdb_result_rid_from_sid(mem_ctx, msgs[0],
+ "objectSid", 0);
+
+ mach_pwd = samdb_result_hash(mem_ctx, msgs[0], "unicodePwd");
+ if (mach_pwd == NULL) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ creds = talloc(mem_ctx, struct creds_CredentialState);
+ NT_STATUS_HAVE_NO_MEMORY(creds);
+
+ creds_server_init(creds, &pipe_state->client_challenge,
+ &pipe_state->server_challenge, mach_pwd,
+ r->out.return_credentials,
+ *r->in.negotiate_flags);
+
+ if (!creds_server_check(creds, r->in.credentials)) {
+ talloc_free(creds);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ creds->account_name = talloc_steal(creds, r->in.account_name);
+
+ creds->computer_name = talloc_steal(creds, r->in.computer_name);
+ creds->domain = talloc_strdup(creds, lp_workgroup(dce_call->conn->dce_ctx->lp_ctx));
+
+ creds->secure_channel_type = r->in.secure_channel_type;
+
+ creds->sid = samdb_result_dom_sid(creds, msgs[0], "objectSid");
+
+
+ /* remember this session key state */
+ nt_status = schannel_store_session_key(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, creds);
+
+ return nt_status;
+}
+
+static NTSTATUS dcesrv_netr_ServerAuthenticate(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_ServerAuthenticate *r)
+{
+ struct netr_ServerAuthenticate3 r3;
+ uint32_t rid = 0;
+ /* TODO:
+ * negotiate_flags is used as an [in] parameter
+ * so it need to be initialised.
+ *
+ * (I think ... = 0; seems wrong here --metze)
+ */
+ uint32_t negotiate_flags_in = 0;
+ uint32_t negotiate_flags_out = 0;
+
+ r3.in.server_name = r->in.server_name;
+ r3.in.account_name = r->in.account_name;
+ r3.in.secure_channel_type = r->in.secure_channel_type;
+ r3.in.computer_name = r->in.computer_name;
+ r3.in.credentials = r->in.credentials;
+ r3.out.return_credentials = r->out.return_credentials;
+ r3.in.negotiate_flags = &negotiate_flags_in;
+ r3.out.negotiate_flags = &negotiate_flags_out;
+ r3.out.rid = &rid;
+
+ return dcesrv_netr_ServerAuthenticate3(dce_call, mem_ctx, &r3);
+}
+
+static NTSTATUS dcesrv_netr_ServerAuthenticate2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_ServerAuthenticate2 *r)
+{
+ struct netr_ServerAuthenticate3 r3;
+ uint32_t rid = 0;
+
+ r3.in.server_name = r->in.server_name;
+ r3.in.account_name = r->in.account_name;
+ r3.in.secure_channel_type = r->in.secure_channel_type;
+ r3.in.computer_name = r->in.computer_name;
+ r3.in.credentials = r->in.credentials;
+ r3.out.return_credentials = r->out.return_credentials;
+ r3.in.negotiate_flags = r->in.negotiate_flags;
+ r3.out.negotiate_flags = r->out.negotiate_flags;
+ r3.out.rid = &rid;
+
+ return dcesrv_netr_ServerAuthenticate3(dce_call, mem_ctx, &r3);
+}
+
+/*
+ Validate an incoming authenticator against the credentials for the remote machine.
+
+ The credentials are (re)read and from the schannel database, and
+ written back after the caclulations are performed.
+
+ The creds_out parameter (if not NULL) returns the credentials, if
+ the caller needs some of that information.
+
+*/
+static NTSTATUS dcesrv_netr_creds_server_step_check(struct tevent_context *event_ctx,
+ struct loadparm_context *lp_ctx,
+ const char *computer_name,
+ TALLOC_CTX *mem_ctx,
+ struct netr_Authenticator *received_authenticator,
+ struct netr_Authenticator *return_authenticator,
+ struct creds_CredentialState **creds_out)
+{
+ struct creds_CredentialState *creds;
+ NTSTATUS nt_status;
+ struct ldb_context *ldb;
+ int ret;
+
+ ldb = schannel_db_connect(mem_ctx, event_ctx, lp_ctx);
+ if (!ldb) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ ret = ldb_transaction_start(ldb);
+ if (ret != 0) {
+ talloc_free(ldb);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ /* Because this is a shared structure (even across
+ * disconnects) we must update the database every time we
+ * update the structure */
+
+ nt_status = schannel_fetch_session_key_ldb(ldb, ldb, computer_name,
+ lp_workgroup(lp_ctx),
+ &creds);
+ if (NT_STATUS_IS_OK(nt_status)) {
+ nt_status = creds_server_step_check(creds,
+ received_authenticator,
+ return_authenticator);
+ }
+ if (NT_STATUS_IS_OK(nt_status)) {
+ nt_status = schannel_store_session_key_ldb(ldb, ldb, creds);
+ }
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ ldb_transaction_commit(ldb);
+ if (creds_out) {
+ *creds_out = creds;
+ talloc_steal(mem_ctx, creds);
+ }
+ } else {
+ ldb_transaction_cancel(ldb);
+ }
+ talloc_free(ldb);
+ return nt_status;
+}
+
+/*
+ Change the machine account password for the currently connected
+ client. Supplies only the NT#.
+*/
+
+static NTSTATUS dcesrv_netr_ServerPasswordSet(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_ServerPasswordSet *r)
+{
+ struct creds_CredentialState *creds;
+ struct ldb_context *sam_ctx;
+ NTSTATUS nt_status;
+
+ nt_status = dcesrv_netr_creds_server_step_check(dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx,
+ r->in.computer_name, mem_ctx,
+ r->in.credential, r->out.return_authenticator,
+ &creds);
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+
+ sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, system_session(mem_ctx, dce_call->conn->dce_ctx->lp_ctx));
+ if (sam_ctx == NULL) {
+ return NT_STATUS_INVALID_SYSTEM_SERVICE;
+ }
+
+ creds_des_decrypt(creds, r->in.new_password);
+
+ /* Using the sid for the account as the key, set the password */
+ nt_status = samdb_set_password_sid(sam_ctx, mem_ctx,
+ creds->sid,
+ NULL, /* Don't have plaintext */
+ NULL, r->in.new_password,
+ true, /* Password change */
+ NULL, NULL);
+ return nt_status;
+}
+
+/*
+ Change the machine account password for the currently connected
+ client. Supplies new plaintext.
+*/
+static NTSTATUS dcesrv_netr_ServerPasswordSet2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_ServerPasswordSet2 *r)
+{
+ struct creds_CredentialState *creds;
+ struct ldb_context *sam_ctx;
+ NTSTATUS nt_status;
+ DATA_BLOB new_password;
+
+ struct samr_CryptPassword password_buf;
+
+ nt_status = dcesrv_netr_creds_server_step_check(dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx,
+ r->in.computer_name, mem_ctx,
+ r->in.credential, r->out.return_authenticator,
+ &creds);
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+
+ sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, system_session(mem_ctx, dce_call->conn->dce_ctx->lp_ctx));
+ if (sam_ctx == NULL) {
+ return NT_STATUS_INVALID_SYSTEM_SERVICE;
+ }
+
+ memcpy(password_buf.data, r->in.new_password->data, 512);
+ SIVAL(password_buf.data, 512, r->in.new_password->length);
+ creds_arcfour_crypt(creds, password_buf.data, 516);
+
+ if (!extract_pw_from_buffer(mem_ctx, password_buf.data, &new_password)) {
+ DEBUG(3,("samr: failed to decode password buffer\n"));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /* Using the sid for the account as the key, set the password */
+ nt_status = samdb_set_password_sid(sam_ctx, mem_ctx,
+ creds->sid,
+ &new_password, /* we have plaintext */
+ NULL, NULL,
+ true, /* Password change */
+ NULL, NULL);
+ return nt_status;
+}
+
+
+/*
+ netr_LogonUasLogon
+*/
+static WERROR dcesrv_netr_LogonUasLogon(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_LogonUasLogon *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_LogonUasLogoff
+*/
+static WERROR dcesrv_netr_LogonUasLogoff(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_LogonUasLogoff *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_LogonSamLogon_base
+
+ This version of the function allows other wrappers to say 'do not check the credentials'
+
+ We can't do the traditional 'wrapping' format completly, as this function must only run under schannel
+*/
+static NTSTATUS dcesrv_netr_LogonSamLogon_base(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_LogonSamLogonEx *r, struct creds_CredentialState *creds)
+{
+ struct auth_context *auth_context;
+ struct auth_usersupplied_info *user_info;
+ struct auth_serversupplied_info *server_info;
+ NTSTATUS nt_status;
+ static const char zeros[16];
+ struct netr_SamBaseInfo *sam;
+ struct netr_SamInfo2 *sam2;
+ struct netr_SamInfo3 *sam3;
+ struct netr_SamInfo6 *sam6;
+
+ user_info = talloc(mem_ctx, struct auth_usersupplied_info);
+ NT_STATUS_HAVE_NO_MEMORY(user_info);
+
+ user_info->flags = 0;
+ user_info->mapped_state = false;
+ user_info->remote_host = NULL;
+
+ switch (r->in.logon_level) {
+ case NetlogonInteractiveInformation:
+ case NetlogonServiceInformation:
+ case NetlogonInteractiveTransitiveInformation:
+ case NetlogonServiceTransitiveInformation:
+ if (creds->negotiate_flags & NETLOGON_NEG_ARCFOUR) {
+ creds_arcfour_crypt(creds,
+ r->in.logon->password->lmpassword.hash,
+ sizeof(r->in.logon->password->lmpassword.hash));
+ creds_arcfour_crypt(creds,
+ r->in.logon->password->ntpassword.hash,
+ sizeof(r->in.logon->password->ntpassword.hash));
+ } else {
+ creds_des_decrypt(creds, &r->in.logon->password->lmpassword);
+ creds_des_decrypt(creds, &r->in.logon->password->ntpassword);
+ }
+
+ /* TODO: we need to deny anonymous access here */
+ nt_status = auth_context_create(mem_ctx,
+ dce_call->event_ctx, dce_call->msg_ctx,
+ dce_call->conn->dce_ctx->lp_ctx,
+ &auth_context);
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+
+ user_info->logon_parameters = r->in.logon->password->identity_info.parameter_control;
+ user_info->client.account_name = r->in.logon->password->identity_info.account_name.string;
+ user_info->client.domain_name = r->in.logon->password->identity_info.domain_name.string;
+ user_info->workstation_name = r->in.logon->password->identity_info.workstation.string;
+
+ user_info->flags |= USER_INFO_INTERACTIVE_LOGON;
+ user_info->password_state = AUTH_PASSWORD_HASH;
+
+ user_info->password.hash.lanman = talloc(user_info, struct samr_Password);
+ NT_STATUS_HAVE_NO_MEMORY(user_info->password.hash.lanman);
+ *user_info->password.hash.lanman = r->in.logon->password->lmpassword;
+
+ user_info->password.hash.nt = talloc(user_info, struct samr_Password);
+ NT_STATUS_HAVE_NO_MEMORY(user_info->password.hash.nt);
+ *user_info->password.hash.nt = r->in.logon->password->ntpassword;
+
+ break;
+ case NetlogonNetworkInformation:
+ case NetlogonNetworkTransitiveInformation:
+
+ /* TODO: we need to deny anonymous access here */
+ nt_status = auth_context_create(mem_ctx,
+ dce_call->event_ctx, dce_call->msg_ctx,
+ dce_call->conn->dce_ctx->lp_ctx,
+ &auth_context);
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+
+ nt_status = auth_context_set_challenge(auth_context, r->in.logon->network->challenge, "netr_LogonSamLogonWithFlags");
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+
+ user_info->logon_parameters = r->in.logon->network->identity_info.parameter_control;
+ user_info->client.account_name = r->in.logon->network->identity_info.account_name.string;
+ user_info->client.domain_name = r->in.logon->network->identity_info.domain_name.string;
+ user_info->workstation_name = r->in.logon->network->identity_info.workstation.string;
+
+ user_info->password_state = AUTH_PASSWORD_RESPONSE;
+ user_info->password.response.lanman = data_blob_talloc(mem_ctx, r->in.logon->network->lm.data, r->in.logon->network->lm.length);
+ user_info->password.response.nt = data_blob_talloc(mem_ctx, r->in.logon->network->nt.data, r->in.logon->network->nt.length);
+
+ break;
+
+
+ case NetlogonGenericInformation:
+ {
+ if (creds->negotiate_flags & NETLOGON_NEG_ARCFOUR) {
+ creds_arcfour_crypt(creds,
+ r->in.logon->generic->data, r->in.logon->generic->length);
+ } else {
+ /* Using DES to verify kerberos tickets makes no sense */
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (strcmp(r->in.logon->generic->package_name.string, "Kerberos") == 0) {
+ NTSTATUS status;
+ struct server_id *kdc;
+ struct kdc_check_generic_kerberos check;
+ struct netr_GenericInfo2 *generic = talloc_zero(mem_ctx, struct netr_GenericInfo2);
+ NT_STATUS_HAVE_NO_MEMORY(generic);
+ *r->out.authoritative = 1;
+
+ /* TODO: Describe and deal with these flags */
+ *r->out.flags = 0;
+
+ r->out.validation->generic = generic;
+
+ kdc = irpc_servers_byname(dce_call->msg_ctx, mem_ctx, "kdc_server");
+ if ((kdc == NULL) || (kdc[0].id == 0)) {
+ return NT_STATUS_NO_LOGON_SERVERS;
+ }
+
+ check.in.generic_request =
+ data_blob_const(r->in.logon->generic->data,
+ r->in.logon->generic->length);
+
+ status = irpc_call(dce_call->msg_ctx, kdc[0],
+ &ndr_table_irpc, NDR_KDC_CHECK_GENERIC_KERBEROS,
+ &check, mem_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ generic->length = check.out.generic_reply.length;
+ generic->data = check.out.generic_reply.data;
+ return NT_STATUS_OK;
+ }
+
+ /* Until we get an implemetnation of these other packages */
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ nt_status = auth_check_password(auth_context, mem_ctx, user_info, &server_info);
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+
+ nt_status = auth_convert_server_info_sambaseinfo(mem_ctx, server_info, &sam);
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+
+ /* Don't crypt an all-zero key, it would give away the NETLOGON pipe session key */
+ /* It appears that level 6 is not individually encrypted */
+ if ((r->in.validation_level != 6) &&
+ memcmp(sam->key.key, zeros, sizeof(sam->key.key)) != 0) {
+ /* This key is sent unencrypted without the ARCFOUR flag set */
+ if (creds->negotiate_flags & NETLOGON_NEG_ARCFOUR) {
+ creds_arcfour_crypt(creds,
+ sam->key.key,
+ sizeof(sam->key.key));
+ }
+ }
+
+ /* Don't crypt an all-zero key, it would give away the NETLOGON pipe session key */
+ /* It appears that level 6 is not individually encrypted */
+ if ((r->in.validation_level != 6) &&
+ memcmp(sam->LMSessKey.key, zeros, sizeof(sam->LMSessKey.key)) != 0) {
+ if (creds->negotiate_flags & NETLOGON_NEG_ARCFOUR) {
+ creds_arcfour_crypt(creds,
+ sam->LMSessKey.key,
+ sizeof(sam->LMSessKey.key));
+ } else {
+ creds_des_encrypt_LMKey(creds,
+ &sam->LMSessKey);
+ }
+ }
+
+ switch (r->in.validation_level) {
+ case 2:
+ sam2 = talloc_zero(mem_ctx, struct netr_SamInfo2);
+ NT_STATUS_HAVE_NO_MEMORY(sam2);
+ sam2->base = *sam;
+ r->out.validation->sam2 = sam2;
+ break;
+
+ case 3:
+ sam3 = talloc_zero(mem_ctx, struct netr_SamInfo3);
+ NT_STATUS_HAVE_NO_MEMORY(sam3);
+ sam3->base = *sam;
+ r->out.validation->sam3 = sam3;
+ break;
+
+ case 6:
+ sam6 = talloc_zero(mem_ctx, struct netr_SamInfo6);
+ NT_STATUS_HAVE_NO_MEMORY(sam6);
+ sam6->base = *sam;
+ sam6->forest.string = lp_realm(dce_call->conn->dce_ctx->lp_ctx);
+ sam6->principle.string = talloc_asprintf(mem_ctx, "%s@%s",
+ sam->account_name.string, sam6->forest.string);
+ NT_STATUS_HAVE_NO_MEMORY(sam6->principle.string);
+ r->out.validation->sam6 = sam6;
+ break;
+
+ default:
+ break;
+ }
+
+ *r->out.authoritative = 1;
+
+ /* TODO: Describe and deal with these flags */
+ *r->out.flags = 0;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS dcesrv_netr_LogonSamLogonEx(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_LogonSamLogonEx *r)
+{
+ NTSTATUS nt_status;
+ struct creds_CredentialState *creds;
+ nt_status = schannel_fetch_session_key(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, r->in.computer_name, lp_workgroup(dce_call->conn->dce_ctx->lp_ctx), &creds);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ if (!dce_call->conn->auth_state.auth_info ||
+ dce_call->conn->auth_state.auth_info->auth_type != DCERPC_AUTH_TYPE_SCHANNEL) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ return dcesrv_netr_LogonSamLogon_base(dce_call, mem_ctx, r, creds);
+}
+
+/*
+ netr_LogonSamLogonWithFlags
+
+*/
+static NTSTATUS dcesrv_netr_LogonSamLogonWithFlags(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_LogonSamLogonWithFlags *r)
+{
+ NTSTATUS nt_status;
+ struct creds_CredentialState *creds;
+ struct netr_LogonSamLogonEx r2;
+
+ struct netr_Authenticator *return_authenticator;
+
+ return_authenticator = talloc(mem_ctx, struct netr_Authenticator);
+ NT_STATUS_HAVE_NO_MEMORY(return_authenticator);
+
+ nt_status = dcesrv_netr_creds_server_step_check(dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx,
+ r->in.computer_name, mem_ctx,
+ r->in.credential, return_authenticator,
+ &creds);
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+
+ ZERO_STRUCT(r2);
+
+ r2.in.server_name = r->in.server_name;
+ r2.in.computer_name = r->in.computer_name;
+ r2.in.logon_level = r->in.logon_level;
+ r2.in.logon = r->in.logon;
+ r2.in.validation_level = r->in.validation_level;
+ r2.in.flags = r->in.flags;
+ r2.out.validation = r->out.validation;
+ r2.out.authoritative = r->out.authoritative;
+ r2.out.flags = r->out.flags;
+
+ nt_status = dcesrv_netr_LogonSamLogon_base(dce_call, mem_ctx, &r2, creds);
+
+ r->out.return_authenticator = return_authenticator;
+
+ return nt_status;
+}
+
+/*
+ netr_LogonSamLogon
+*/
+static NTSTATUS dcesrv_netr_LogonSamLogon(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_LogonSamLogon *r)
+{
+ struct netr_LogonSamLogonWithFlags r2;
+ uint32_t flags = 0;
+ NTSTATUS status;
+
+ ZERO_STRUCT(r2);
+
+ r2.in.server_name = r->in.server_name;
+ r2.in.computer_name = r->in.computer_name;
+ r2.in.credential = r->in.credential;
+ r2.in.return_authenticator = r->in.return_authenticator;
+ r2.in.logon_level = r->in.logon_level;
+ r2.in.logon = r->in.logon;
+ r2.in.validation_level = r->in.validation_level;
+ r2.in.flags = &flags;
+ r2.out.validation = r->out.validation;
+ r2.out.authoritative = r->out.authoritative;
+ r2.out.flags = &flags;
+
+ status = dcesrv_netr_LogonSamLogonWithFlags(dce_call, mem_ctx, &r2);
+
+ r->out.return_authenticator = r2.out.return_authenticator;
+
+ return status;
+}
+
+
+/*
+ netr_LogonSamLogoff
+*/
+static NTSTATUS dcesrv_netr_LogonSamLogoff(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_LogonSamLogoff *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+
+/*
+ netr_DatabaseDeltas
+*/
+static NTSTATUS dcesrv_netr_DatabaseDeltas(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_DatabaseDeltas *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_DatabaseSync
+*/
+static NTSTATUS dcesrv_netr_DatabaseSync(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_DatabaseSync *r)
+{
+ /* win2k3 native mode returns "NOT IMPLEMENTED" for this call */
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+
+/*
+ netr_AccountDeltas
+*/
+static NTSTATUS dcesrv_netr_AccountDeltas(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_AccountDeltas *r)
+{
+ /* w2k3 returns "NOT IMPLEMENTED" for this call */
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+
+/*
+ netr_AccountSync
+*/
+static NTSTATUS dcesrv_netr_AccountSync(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_AccountSync *r)
+{
+ /* w2k3 returns "NOT IMPLEMENTED" for this call */
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+
+/*
+ netr_GetDcName
+*/
+static WERROR dcesrv_netr_GetDcName(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_GetDcName *r)
+{
+ const char * const attrs[] = { NULL };
+ void *sam_ctx;
+ struct ldb_message **res;
+ struct ldb_dn *domain_dn;
+ int ret;
+ const char *dcname;
+
+ sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx,
+ dce_call->conn->dce_ctx->lp_ctx,
+ dce_call->conn->auth_state.session_info);
+ if (sam_ctx == NULL) {
+ return WERR_DS_SERVICE_UNAVAILABLE;
+ }
+
+ domain_dn = samdb_domain_to_dn((struct ldb_context *)sam_ctx, mem_ctx,
+ r->in.domainname);
+ if (domain_dn == NULL) {
+ return WERR_DS_SERVICE_UNAVAILABLE;
+ }
+
+ ret = gendb_search_dn((struct ldb_context *)sam_ctx, mem_ctx,
+ domain_dn, &res, attrs);
+ if (ret != 1) {
+ return WERR_NO_SUCH_DOMAIN;
+ }
+
+ /* TODO: - return real IP address
+ * - check all r->in.* parameters (server_unc is ignored by w2k3!)
+ */
+ dcname = talloc_asprintf(mem_ctx, "\\\\%s",
+ lp_netbios_name(dce_call->conn->dce_ctx->lp_ctx));
+ W_ERROR_HAVE_NO_MEMORY(dcname);
+
+ *r->out.dcname = dcname;
+ return WERR_OK;
+}
+
+
+/*
+ netr_LogonControl
+*/
+static WERROR dcesrv_netr_LogonControl(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_LogonControl *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_GetAnyDCName
+*/
+static WERROR dcesrv_netr_GetAnyDCName(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_GetAnyDCName *r)
+{
+ struct netr_GetDcName r2;
+ WERROR werr;
+
+ ZERO_STRUCT(r2);
+
+ r2.in.logon_server = r->in.logon_server;
+ r2.in.domainname = r->in.domainname;
+ r2.out.dcname = r->out.dcname;
+
+ werr = dcesrv_netr_GetDcName(dce_call, mem_ctx, &r2);
+
+ return werr;
+}
+
+
+/*
+ netr_LogonControl2
+*/
+static WERROR dcesrv_netr_LogonControl2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_LogonControl2 *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_DatabaseSync2
+*/
+static NTSTATUS dcesrv_netr_DatabaseSync2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_DatabaseSync2 *r)
+{
+ /* win2k3 native mode returns "NOT IMPLEMENTED" for this call */
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+
+/*
+ netr_DatabaseRedo
+*/
+static NTSTATUS dcesrv_netr_DatabaseRedo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_DatabaseRedo *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_LogonControl2Ex
+*/
+static WERROR dcesrv_netr_LogonControl2Ex(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_LogonControl2Ex *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_NetrEnumerateTurstedDomains
+*/
+static WERROR dcesrv_netr_NetrEnumerateTrustedDomains(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_NetrEnumerateTrustedDomains *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_LogonGetCapabilities
+*/
+static NTSTATUS dcesrv_netr_LogonGetCapabilities(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_LogonGetCapabilities *r)
+{
+ /* we don't support AES yet */
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+
+/*
+ netr_NETRLOGONSETSERVICEBITS
+*/
+static WERROR dcesrv_netr_NETRLOGONSETSERVICEBITS(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_NETRLOGONSETSERVICEBITS *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_LogonGetTrustRid
+*/
+static WERROR dcesrv_netr_LogonGetTrustRid(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_LogonGetTrustRid *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_NETRLOGONCOMPUTESERVERDIGEST
+*/
+static WERROR dcesrv_netr_NETRLOGONCOMPUTESERVERDIGEST(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_NETRLOGONCOMPUTESERVERDIGEST *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_NETRLOGONCOMPUTECLIENTDIGEST
+*/
+static WERROR dcesrv_netr_NETRLOGONCOMPUTECLIENTDIGEST(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_NETRLOGONCOMPUTECLIENTDIGEST *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+
+/*
+ netr_DsRGetSiteName
+*/
+static WERROR dcesrv_netr_DsRGetSiteName(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_DsRGetSiteName *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ fill in a netr_DomainTrustInfo from a ldb search result
+*/
+static NTSTATUS fill_domain_trust_info(TALLOC_CTX *mem_ctx,
+ struct ldb_message *res,
+ struct ldb_message *ref_res,
+ struct netr_DomainTrustInfo *info,
+ bool is_local, bool is_trust_list)
+{
+ ZERO_STRUCTP(info);
+
+ info->trust_extension.info = talloc_zero(mem_ctx, struct netr_trust_extension);
+ info->trust_extension.length = 16;
+ info->trust_extension.info->flags =
+ NETR_TRUST_FLAG_TREEROOT |
+ NETR_TRUST_FLAG_IN_FOREST |
+ NETR_TRUST_FLAG_PRIMARY;
+ info->trust_extension.info->parent_index = 0; /* should be index into array
+ of parent */
+ info->trust_extension.info->trust_type = LSA_TRUST_TYPE_UPLEVEL; /* should be based on ldb search for trusts */
+ info->trust_extension.info->trust_attributes = LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE; /* needs to be based on ldb search */
+
+ if (is_trust_list) {
+ /* MS-NRPC 3.5.4.3.9 - must be set to NULL for trust list */
+ info->forest.string = NULL;
+ } else {
+ /* TODO: we need a common function for pulling the forest */
+ info->forest.string = samdb_result_string(ref_res, "dnsRoot", NULL);
+ }
+
+ if (is_local) {
+ info->domainname.string = samdb_result_string(ref_res, "nETBIOSName", NULL);
+ info->fulldomainname.string = samdb_result_string(ref_res, "dnsRoot", NULL);
+ info->guid = samdb_result_guid(res, "objectGUID");
+ info->sid = samdb_result_dom_sid(mem_ctx, res, "objectSid");
+ } else {
+ info->domainname.string = samdb_result_string(res, "flatName", NULL);
+ info->fulldomainname.string = samdb_result_string(res, "trustPartner", NULL);
+ info->guid = samdb_result_guid(res, "objectGUID");
+ info->sid = samdb_result_dom_sid(mem_ctx, res, "securityIdentifier");
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ netr_LogonGetDomainInfo
+ this is called as part of the ADS domain logon procedure.
+
+ It has an important role in convaying details about the client, such
+ as Operating System, Version, Service Pack etc.
+*/
+static NTSTATUS dcesrv_netr_LogonGetDomainInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_LogonGetDomainInfo *r)
+{
+ const char * const attrs[] = { "objectSid",
+ "objectGUID", "flatName", "securityIdentifier",
+ "trustPartner", NULL };
+ const char * const ref_attrs[] = { "nETBIOSName", "dnsRoot", NULL };
+ struct ldb_context *sam_ctx;
+ struct ldb_message **res1, **res2, **ref_res;
+ struct netr_DomainInfo1 *info1;
+ int ret, ret1, ret2, i;
+ NTSTATUS status;
+ struct ldb_dn *partitions_basedn;
+
+ const char *local_domain;
+
+ status = dcesrv_netr_creds_server_step_check(dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx,
+ r->in.computer_name, mem_ctx,
+ r->in.credential,
+ r->out.return_authenticator,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,(__location__ " Bad credentials - error\n"));
+ }
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, dce_call->conn->auth_state.session_info);
+ if (sam_ctx == NULL) {
+ return NT_STATUS_INVALID_SYSTEM_SERVICE;
+ }
+
+ partitions_basedn = samdb_partitions_dn(sam_ctx, mem_ctx);
+
+ /* we need to do two searches. The first will pull our primary
+ domain and the second will pull any trusted domains. Our
+ primary domain is also a "trusted" domain, so we need to
+ put the primary domain into the lists of returned trusts as
+ well */
+ ret1 = gendb_search_dn(sam_ctx, mem_ctx, samdb_base_dn(sam_ctx), &res1, attrs);
+ if (ret1 != 1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ /* try and find the domain */
+ ret = gendb_search(sam_ctx, mem_ctx, partitions_basedn,
+ &ref_res, ref_attrs,
+ "(&(objectClass=crossRef)(ncName=%s))",
+ ldb_dn_get_linearized(res1[0]->dn));
+ if (ret != 1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ local_domain = samdb_result_string(ref_res[0], "nETBIOSName", NULL);
+
+ ret2 = gendb_search(sam_ctx, mem_ctx, NULL, &res2, attrs, "(objectClass=trustedDomain)");
+ if (ret2 == -1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ info1 = talloc(mem_ctx, struct netr_DomainInfo1);
+ NT_STATUS_HAVE_NO_MEMORY(info1);
+
+ ZERO_STRUCTP(info1);
+
+ info1->num_trusts = ret2 + 1;
+ info1->trusts = talloc_array(mem_ctx, struct netr_DomainTrustInfo,
+ info1->num_trusts);
+ NT_STATUS_HAVE_NO_MEMORY(info1->trusts);
+
+ status = fill_domain_trust_info(mem_ctx, res1[0], ref_res[0], &info1->domaininfo,
+ true, false);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ for (i=0;i<ret2;i++) {
+ status = fill_domain_trust_info(mem_ctx, res2[i], NULL, &info1->trusts[i],
+ false, true);
+ NT_STATUS_NOT_OK_RETURN(status);
+ }
+
+ status = fill_domain_trust_info(mem_ctx, res1[0], ref_res[0], &info1->trusts[i],
+ true, true);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ info1->dns_hostname.string = samdb_result_string(ref_res[0], "dnsRoot", NULL);
+ info1->workstation_flags =
+ NETR_WS_FLAG_HANDLES_INBOUND_TRUSTS | NETR_WS_FLAG_HANDLES_SPN_UPDATE;
+ info1->supported_enc_types = 0; /* w2008 gives this 0 */
+
+ r->out.info->info1 = info1;
+
+ return NT_STATUS_OK;
+}
+
+
+
+/*
+ netr_ServerPasswordGet
+*/
+static WERROR dcesrv_netr_ServerPasswordGet(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_ServerPasswordGet *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_NETRLOGONSENDTOSAM
+*/
+static WERROR dcesrv_netr_NETRLOGONSENDTOSAM(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_NETRLOGONSENDTOSAM *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_DsRAddressToSitenamesW
+*/
+static WERROR dcesrv_netr_DsRAddressToSitenamesW(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_DsRAddressToSitenamesW *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_DsRGetDCNameEx2
+*/
+static WERROR dcesrv_netr_DsRGetDCNameEx2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_DsRGetDCNameEx2 *r)
+{
+ const char * const attrs[] = { "objectGUID", NULL };
+ void *sam_ctx;
+ struct ldb_message **res;
+ struct ldb_dn *domain_dn;
+ int ret;
+ struct netr_DsRGetDCNameInfo *info;
+
+ ZERO_STRUCTP(r->out.info);
+
+ sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, dce_call->conn->auth_state.session_info);
+ if (sam_ctx == NULL) {
+ return WERR_DS_SERVICE_UNAVAILABLE;
+ }
+
+ /* Win7-beta will send the domain name in the form the user typed, so we have to cope
+ with both the short and long form here */
+ if (r->in.domain_name == NULL || strcasecmp(r->in.domain_name, lp_workgroup(dce_call->conn->dce_ctx->lp_ctx)) == 0) {
+ r->in.domain_name = lp_realm(dce_call->conn->dce_ctx->lp_ctx);
+ }
+
+ domain_dn = samdb_dns_domain_to_dn((struct ldb_context *)sam_ctx,
+ mem_ctx,
+ r->in.domain_name);
+ if (domain_dn == NULL) {
+ return WERR_DS_SERVICE_UNAVAILABLE;
+ }
+
+ ret = gendb_search_dn((struct ldb_context *)sam_ctx, mem_ctx,
+ domain_dn, &res, attrs);
+ if (ret != 1) {
+ return WERR_NO_SUCH_DOMAIN;
+ }
+
+ info = talloc(mem_ctx, struct netr_DsRGetDCNameInfo);
+ W_ERROR_HAVE_NO_MEMORY(info);
+
+ /* TODO: - return real IP address
+ * - check all r->in.* parameters (server_unc is ignored by w2k3!)
+ */
+ info->dc_unc = talloc_asprintf(mem_ctx, "\\\\%s.%s",
+ lp_netbios_name(dce_call->conn->dce_ctx->lp_ctx),
+ lp_realm(dce_call->conn->dce_ctx->lp_ctx));
+ W_ERROR_HAVE_NO_MEMORY(info->dc_unc);
+ info->dc_address = talloc_strdup(mem_ctx, "\\\\0.0.0.0");
+ W_ERROR_HAVE_NO_MEMORY(info->dc_address);
+ info->dc_address_type = DS_ADDRESS_TYPE_INET;
+ info->domain_guid = samdb_result_guid(res[0], "objectGUID");
+ info->domain_name = lp_realm(dce_call->conn->dce_ctx->lp_ctx);
+ info->forest_name = lp_realm(dce_call->conn->dce_ctx->lp_ctx);
+ info->dc_flags = DS_DNS_FOREST |
+ DS_DNS_DOMAIN |
+ DS_DNS_CONTROLLER |
+ DS_SERVER_WRITABLE |
+ DS_SERVER_CLOSEST |
+ DS_SERVER_TIMESERV |
+ DS_SERVER_KDC |
+ DS_SERVER_DS |
+ DS_SERVER_LDAP |
+ DS_SERVER_GC |
+ DS_SERVER_PDC;
+ info->dc_site_name = talloc_strdup(mem_ctx, "Default-First-Site-Name");
+ W_ERROR_HAVE_NO_MEMORY(info->dc_site_name);
+ info->client_site_name = talloc_strdup(mem_ctx, "Default-First-Site-Name");
+ W_ERROR_HAVE_NO_MEMORY(info->client_site_name);
+
+ *r->out.info = info;
+
+ return WERR_OK;
+}
+
+/*
+ netr_DsRGetDCNameEx
+*/
+static WERROR dcesrv_netr_DsRGetDCNameEx(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_DsRGetDCNameEx *r)
+{
+ struct netr_DsRGetDCNameEx2 r2;
+ WERROR werr;
+
+ ZERO_STRUCT(r2);
+
+ r2.in.server_unc = r->in.server_unc;
+ r2.in.client_account = NULL;
+ r2.in.mask = 0;
+ r2.in.domain_guid = r->in.domain_guid;
+ r2.in.domain_name = r->in.domain_name;
+ r2.in.site_name = r->in.site_name;
+ r2.in.flags = r->in.flags;
+ r2.out.info = r->out.info;
+
+ werr = dcesrv_netr_DsRGetDCNameEx2(dce_call, mem_ctx, &r2);
+
+ return werr;
+}
+
+/*
+ netr_DsRGetDCName
+*/
+static WERROR dcesrv_netr_DsRGetDCName(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_DsRGetDCName *r)
+{
+ struct netr_DsRGetDCNameEx2 r2;
+ WERROR werr;
+
+ ZERO_STRUCT(r2);
+
+ r2.in.server_unc = r->in.server_unc;
+ r2.in.client_account = NULL;
+ r2.in.mask = 0;
+ r2.in.domain_name = r->in.domain_name;
+ r2.in.domain_guid = r->in.domain_guid;
+
+ r2.in.site_name = NULL; /* should fill in from site GUID */
+ r2.in.flags = r->in.flags;
+ r2.out.info = r->out.info;
+
+ werr = dcesrv_netr_DsRGetDCNameEx2(dce_call, mem_ctx, &r2);
+
+ return werr;
+}
+/*
+ netr_NETRLOGONGETTIMESERVICEPARENTDOMAIN
+*/
+static WERROR dcesrv_netr_NETRLOGONGETTIMESERVICEPARENTDOMAIN(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_NETRLOGONGETTIMESERVICEPARENTDOMAIN *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_NetrEnumerateTrustedDomainsEx
+*/
+static WERROR dcesrv_netr_NetrEnumerateTrustedDomainsEx(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_NetrEnumerateTrustedDomainsEx *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_DsRAddressToSitenamesExW
+*/
+static WERROR dcesrv_netr_DsRAddressToSitenamesExW(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_DsRAddressToSitenamesExW *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_DsrGetDcSiteCoverageW
+*/
+static WERROR dcesrv_netr_DsrGetDcSiteCoverageW(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_DsrGetDcSiteCoverageW *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_DsrEnumerateDomainTrusts
+*/
+static WERROR dcesrv_netr_DsrEnumerateDomainTrusts(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_DsrEnumerateDomainTrusts *r)
+{
+ struct netr_DomainTrustList *trusts;
+ void *sam_ctx;
+ int ret;
+ struct ldb_message **dom_res, **ref_res;
+ const char * const dom_attrs[] = { "objectSid", "objectGUID", NULL };
+ const char * const ref_attrs[] = { "nETBIOSName", "dnsRoot", NULL };
+ struct ldb_dn *partitions_basedn;
+
+ ZERO_STRUCT(r->out);
+
+ sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, dce_call->conn->auth_state.session_info);
+ if (sam_ctx == NULL) {
+ return WERR_GENERAL_FAILURE;
+ }
+
+ partitions_basedn = samdb_partitions_dn((struct ldb_context *)sam_ctx,
+ mem_ctx);
+
+ ret = gendb_search_dn((struct ldb_context *)sam_ctx, mem_ctx, NULL,
+ &dom_res, dom_attrs);
+ if (ret == -1) {
+ return WERR_GENERAL_FAILURE;
+ }
+ if (ret != 1) {
+ return WERR_GENERAL_FAILURE;
+ }
+
+ ret = gendb_search((struct ldb_context *)sam_ctx, mem_ctx,
+ partitions_basedn, &ref_res, ref_attrs,
+ "(&(objectClass=crossRef)(ncName=%s))",
+ ldb_dn_get_linearized(dom_res[0]->dn));
+ if (ret == -1) {
+ return WERR_GENERAL_FAILURE;
+ }
+ if (ret != 1) {
+ return WERR_GENERAL_FAILURE;
+ }
+
+ trusts = talloc(mem_ctx, struct netr_DomainTrustList);
+ W_ERROR_HAVE_NO_MEMORY(trusts);
+
+ trusts->array = talloc_array(trusts, struct netr_DomainTrust, ret);
+ W_ERROR_HAVE_NO_MEMORY(trusts->array);
+
+ trusts->count = 1; /* ?? */
+
+ r->out.trusts = trusts;
+
+ /* TODO: add filtering by trust_flags, and correct trust_type
+ and attributes */
+ trusts->array[0].netbios_name = samdb_result_string(ref_res[0], "nETBIOSName", NULL);
+ trusts->array[0].dns_name = samdb_result_string(ref_res[0], "dnsRoot", NULL);
+ trusts->array[0].trust_flags =
+ NETR_TRUST_FLAG_TREEROOT |
+ NETR_TRUST_FLAG_IN_FOREST |
+ NETR_TRUST_FLAG_PRIMARY;
+ trusts->array[0].parent_index = 0;
+ trusts->array[0].trust_type = 2;
+ trusts->array[0].trust_attributes = 0;
+ trusts->array[0].sid = samdb_result_dom_sid(mem_ctx, dom_res[0], "objectSid");
+ trusts->array[0].guid = samdb_result_guid(dom_res[0], "objectGUID");
+
+ return WERR_OK;
+}
+
+
+/*
+ netr_DsrDeregisterDNSHostRecords
+*/
+static WERROR dcesrv_netr_DsrDeregisterDNSHostRecords(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_DsrDeregisterDNSHostRecords *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_ServerTrustPasswordsGet
+*/
+static NTSTATUS dcesrv_netr_ServerTrustPasswordsGet(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_ServerTrustPasswordsGet *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_DsRGetForestTrustInformation
+*/
+static WERROR dcesrv_netr_DsRGetForestTrustInformation(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_DsRGetForestTrustInformation *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_GetForestTrustInformation
+*/
+static WERROR dcesrv_netr_GetForestTrustInformation(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_GetForestTrustInformation *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ netr_ServerGetTrustInfo
+*/
+static NTSTATUS dcesrv_netr_ServerGetTrustInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct netr_ServerGetTrustInfo *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_netlogon_s.c"
diff --git a/source4/rpc_server/remote/README b/source4/rpc_server/remote/README
new file mode 100644
index 0000000000..0d235a9901
--- /dev/null
+++ b/source4/rpc_server/remote/README
@@ -0,0 +1,38 @@
+This is an RPC backend that implements all operations in terms of
+remote RPC operations. This may be useful in certain debugging
+situations, where the traffic is encrypted, or you wish to validate
+that IDL is correct before implementing full test clients, or with
+windows clients.
+
+There are two modes of operation: Password specified and delegated
+credentials.
+
+Password specified:
+-------------------
+
+This uses a static username/password in the config file, example:
+
+[global]
+ dcerpc endpoint servers = remote
+ dcerpc_remote:binding = ncacn_np:win2003
+ dcerpc_remote:username = administrator
+ dcerpc_remote:password = PASSWORD
+ dcerpc_remote:interfaces = samr, lsarpc, netlogon
+
+Delegated credentials:
+----------------------
+
+If your incoming user is authenticated with Kerberos, and the machine
+account for this Samba4 proxy server is 'trusted for delegation', then
+the Samba4 proxy can forward the client's credentials to the target.
+
+You must be joined to the domain (net join <domain> member).
+
+To set 'trusted for delegation' with MMC, see the checkbox in the
+Computer account property page under Users and Computers.
+
+[global]
+ dcerpc endpoint servers = remote
+ dcerpc_remote:binding = ncacn_np:win2003
+ dcerpc_remote:interfaces = samr, lsarpc, netlogon
+
diff --git a/source4/rpc_server/remote/dcesrv_remote.c b/source4/rpc_server/remote/dcesrv_remote.c
new file mode 100644
index 0000000000..e20e87b326
--- /dev/null
+++ b/source4/rpc_server/remote/dcesrv_remote.c
@@ -0,0 +1,327 @@
+/*
+ Unix SMB/CIFS implementation.
+ remote dcerpc operations
+
+ Copyright (C) Stefan (metze) Metzmacher 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "rpc_server/dcerpc_server.h"
+#include "auth/auth.h"
+#include "auth/credentials/credentials.h"
+#include "librpc/ndr/ndr_table.h"
+#include "param/param.h"
+
+
+struct dcesrv_remote_private {
+ struct dcerpc_pipe *c_pipe;
+};
+
+static NTSTATUS remote_op_reply(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, void *r)
+{
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS remote_op_bind(struct dcesrv_call_state *dce_call, const struct dcesrv_interface *iface)
+{
+ NTSTATUS status;
+ const struct ndr_interface_table *table;
+ struct dcesrv_remote_private *priv;
+ const char *binding = lp_parm_string(dce_call->conn->dce_ctx->lp_ctx, NULL, "dcerpc_remote", "binding");
+ const char *user, *pass, *domain;
+ struct cli_credentials *credentials;
+ bool machine_account;
+
+ machine_account = lp_parm_bool(dce_call->conn->dce_ctx->lp_ctx, NULL, "dcerpc_remote", "use_machine_account", false);
+
+ priv = talloc(dce_call->conn, struct dcesrv_remote_private);
+ if (!priv) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ priv->c_pipe = NULL;
+ dce_call->context->private_data = priv;
+
+ if (!binding) {
+ DEBUG(0,("You must specify a DCE/RPC binding string\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ user = lp_parm_string(dce_call->conn->dce_ctx->lp_ctx, NULL, "dcerpc_remote", "user");
+ pass = lp_parm_string(dce_call->conn->dce_ctx->lp_ctx, NULL, "dcerpc_remote", "password");
+ domain = lp_parm_string(dce_call->conn->dce_ctx->lp_ctx, NULL, "dceprc_remote", "domain");
+
+ table = ndr_table_by_uuid(&iface->syntax_id.uuid); /* FIXME: What about if_version ? */
+ if (!table) {
+ dce_call->fault_code = DCERPC_FAULT_UNK_IF;
+ return NT_STATUS_NET_WRITE_FAULT;
+ }
+
+ if (user && pass) {
+ DEBUG(5, ("dcerpc_remote: RPC Proxy: Using specified account\n"));
+ credentials = cli_credentials_init(priv);
+ if (!credentials) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ cli_credentials_set_conf(credentials, dce_call->conn->dce_ctx->lp_ctx);
+ cli_credentials_set_username(credentials, user, CRED_SPECIFIED);
+ if (domain) {
+ cli_credentials_set_domain(credentials, domain, CRED_SPECIFIED);
+ }
+ cli_credentials_set_password(credentials, pass, CRED_SPECIFIED);
+ } else if (machine_account) {
+ DEBUG(5, ("dcerpc_remote: RPC Proxy: Using machine account\n"));
+ credentials = cli_credentials_init(priv);
+ cli_credentials_set_conf(credentials, dce_call->conn->dce_ctx->lp_ctx);
+ if (domain) {
+ cli_credentials_set_domain(credentials, domain, CRED_SPECIFIED);
+ }
+ status = cli_credentials_set_machine_account(credentials, dce_call->conn->dce_ctx->lp_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ } else if (dce_call->conn->auth_state.session_info->credentials) {
+ DEBUG(5, ("dcerpc_remote: RPC Proxy: Using delegated credentials\n"));
+ credentials = dce_call->conn->auth_state.session_info->credentials;
+ } else {
+ DEBUG(1,("dcerpc_remote: RPC Proxy: You must supply binding, user and password or have delegated credentials\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = dcerpc_pipe_connect(priv,
+ &(priv->c_pipe), binding, table,
+ credentials, dce_call->event_ctx,
+ dce_call->conn->dce_ctx->lp_ctx);
+
+ talloc_free(credentials);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static void remote_op_unbind(struct dcesrv_connection_context *context, const struct dcesrv_interface *iface)
+{
+ struct dcesrv_remote_private *priv = (struct dcesrv_remote_private *)context->private_data;
+
+ talloc_free(priv->c_pipe);
+
+ return;
+}
+
+static NTSTATUS remote_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;
+ const struct ndr_interface_table *table = (const struct ndr_interface_table *)dce_call->context->iface->private_data;
+ uint16_t opnum = dce_call->pkt.u.request.opnum;
+
+ dce_call->fault_code = 0;
+
+ if (opnum >= table->num_calls) {
+ dce_call->fault_code = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NET_WRITE_FAULT;
+ }
+
+ *r = talloc_size(mem_ctx, table->calls[opnum].struct_size);
+ if (!*r) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* unravel the NDR for the packet */
+ ndr_err = table->calls[opnum].ndr_pull(pull, NDR_IN, *r);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ dcerpc_log_packet(dce_call->conn->packet_log_dir,
+ table, opnum, NDR_IN,
+ &dce_call->pkt.u.request.stub_and_verifier);
+ dce_call->fault_code = DCERPC_FAULT_NDR;
+ return NT_STATUS_NET_WRITE_FAULT;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS remote_op_dispatch(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, void *r)
+{
+ struct dcesrv_remote_private *priv = dce_call->context->private_data;
+ uint16_t opnum = dce_call->pkt.u.request.opnum;
+ const struct ndr_interface_table *table = dce_call->context->iface->private_data;
+ const struct ndr_interface_call *call;
+ const char *name;
+
+ name = table->calls[opnum].name;
+ call = &table->calls[opnum];
+
+ if (priv->c_pipe->conn->flags & DCERPC_DEBUG_PRINT_IN) {
+ ndr_print_function_debug(call->ndr_print, name, NDR_IN | NDR_SET_VALUES, r);
+ }
+
+ priv->c_pipe->conn->flags |= DCERPC_NDR_REF_ALLOC;
+
+ /* we didn't use the return code of this function as we only check the last_fault_code */
+ dcerpc_ndr_request(priv->c_pipe, NULL, table, opnum, mem_ctx,r);
+
+ dce_call->fault_code = priv->c_pipe->last_fault_code;
+ if (dce_call->fault_code != 0) {
+ DEBUG(0,("dcesrv_remote: call[%s] failed with: %s!\n",name, dcerpc_errstr(mem_ctx, dce_call->fault_code)));
+ return NT_STATUS_NET_WRITE_FAULT;
+ }
+
+ if ((dce_call->fault_code == 0) &&
+ (priv->c_pipe->conn->flags & DCERPC_DEBUG_PRINT_OUT)) {
+ ndr_print_function_debug(call->ndr_print, name, NDR_OUT, r);
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS remote_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;
+ const struct ndr_interface_table *table = dce_call->context->iface->private_data;
+ uint16_t opnum = dce_call->pkt.u.request.opnum;
+
+ /* unravel the NDR for the packet */
+ ndr_err = table->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 NTSTATUS remote_register_one_iface(struct dcesrv_context *dce_ctx, const struct dcesrv_interface *iface)
+{
+ int i;
+ const struct ndr_interface_table *table = iface->private_data;
+
+ for (i=0;i<table->endpoints->count;i++) {
+ NTSTATUS ret;
+ const char *name = table->endpoints->names[i];
+
+ ret = dcesrv_interface_register(dce_ctx, name, iface, NULL);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(1,("remote_op_init_server: failed to register endpoint '%s'\n",name));
+ return ret;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS remote_op_init_server(struct dcesrv_context *dce_ctx, const struct dcesrv_endpoint_server *ep_server)
+{
+ int i;
+ const char **ifaces = (const char **)str_list_make(dce_ctx, lp_parm_string(dce_ctx->lp_ctx, NULL, "dcerpc_remote", "interfaces"),NULL);
+
+ if (!ifaces) {
+ DEBUG(3,("remote_op_init_server: no interfaces configured\n"));
+ return NT_STATUS_OK;
+ }
+
+ for (i=0;ifaces[i];i++) {
+ NTSTATUS ret;
+ struct dcesrv_interface iface;
+
+ if (!ep_server->interface_by_name(&iface, ifaces[i])) {
+ DEBUG(0,("remote_op_init_server: failed to find interface = '%s'\n", ifaces[i]));
+ talloc_free(ifaces);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ ret = remote_register_one_iface(dce_ctx, &iface);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("remote_op_init_server: failed to register interface = '%s'\n", ifaces[i]));
+ talloc_free(ifaces);
+ return ret;
+ }
+ }
+
+ talloc_free(ifaces);
+ return NT_STATUS_OK;
+}
+
+static bool remote_fill_interface(struct dcesrv_interface *iface, const struct ndr_interface_table *if_tabl)
+{
+ iface->name = if_tabl->name;
+ iface->syntax_id = if_tabl->syntax_id;
+
+ iface->bind = remote_op_bind;
+ iface->unbind = remote_op_unbind;
+
+ iface->ndr_pull = remote_op_ndr_pull;
+ iface->dispatch = remote_op_dispatch;
+ iface->reply = remote_op_reply;
+ iface->ndr_push = remote_op_ndr_push;
+
+ iface->private_data = if_tabl;
+
+ return true;
+}
+
+static bool remote_op_interface_by_uuid(struct dcesrv_interface *iface, const struct GUID *uuid, uint32_t if_version)
+{
+ const struct ndr_interface_list *l;
+
+ for (l=ndr_table_list();l;l=l->next) {
+ if (l->table->syntax_id.if_version == if_version &&
+ GUID_equal(&l->table->syntax_id.uuid, uuid)==0) {
+ return remote_fill_interface(iface, l->table);
+ }
+ }
+
+ return false;
+}
+
+static bool remote_op_interface_by_name(struct dcesrv_interface *iface, const char *name)
+{
+ const struct ndr_interface_table *tbl = ndr_table_by_name(name);
+
+ if (tbl)
+ return remote_fill_interface(iface, tbl);
+
+ return false;
+}
+
+NTSTATUS dcerpc_server_remote_init(void)
+{
+ NTSTATUS ret;
+ struct dcesrv_endpoint_server ep_server;
+
+ ZERO_STRUCT(ep_server);
+
+ /* fill in our name */
+ ep_server.name = "remote";
+
+ /* fill in all the operations */
+ ep_server.init_server = remote_op_init_server;
+
+ ep_server.interface_by_uuid = remote_op_interface_by_uuid;
+ ep_server.interface_by_name = remote_op_interface_by_name;
+
+ /* register ourselves with the DCERPC subsystem. */
+ ret = dcerpc_register_ep_server(&ep_server);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("Failed to register 'remote' endpoint server!\n"));
+ return ret;
+ }
+
+ /* We need the full DCE/RPC interface table */
+ ndr_table_init();
+
+ return ret;
+}
diff --git a/source4/rpc_server/samr/dcesrv_samr.c b/source4/rpc_server/samr/dcesrv_samr.c
new file mode 100644
index 0000000000..df23e11a67
--- /dev/null
+++ b/source4/rpc_server/samr/dcesrv_samr.c
@@ -0,0 +1,4346 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the samr pipe
+
+ Copyright (C) Andrew Tridgell 2004
+ Copyright (C) Volker Lendecke 2004
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "librpc/gen_ndr/ndr_samr.h"
+#include "rpc_server/dcerpc_server.h"
+#include "rpc_server/common/common.h"
+#include "rpc_server/samr/dcesrv_samr.h"
+#include "system/time.h"
+#include "lib/ldb/include/ldb.h"
+#include "lib/ldb/include/ldb_errors.h"
+#include "dsdb/common/flags.h"
+#include "dsdb/samdb/samdb.h"
+#include "libcli/ldap/ldap_ndr.h"
+#include "libcli/security/security.h"
+#include "rpc_server/samr/proto.h"
+#include "../lib/util/util_ldb.h"
+#include "param/param.h"
+
+/* these query macros make samr_Query[User|Group|Alias]Info a bit easier to read */
+
+#define QUERY_STRING(msg, field, attr) \
+ info->field.string = samdb_result_string(msg, attr, "");
+#define QUERY_UINT(msg, field, attr) \
+ info->field = samdb_result_uint(msg, attr, 0);
+#define QUERY_RID(msg, field, attr) \
+ info->field = samdb_result_rid_from_sid(mem_ctx, msg, attr, 0);
+#define QUERY_UINT64(msg, field, attr) \
+ info->field = samdb_result_uint64(msg, attr, 0);
+#define QUERY_APASSC(msg, field, attr) \
+ info->field = samdb_result_allow_password_change(sam_ctx, mem_ctx, \
+ a_state->domain_state->domain_dn, msg, attr);
+#define QUERY_FPASSC(msg, field, attr) \
+ info->field = samdb_result_force_password_change(sam_ctx, mem_ctx, \
+ a_state->domain_state->domain_dn, msg);
+#define QUERY_LHOURS(msg, field, attr) \
+ info->field = samdb_result_logon_hours(mem_ctx, msg, attr);
+#define QUERY_AFLAGS(msg, field, attr) \
+ info->field = samdb_result_acct_flags(sam_ctx, mem_ctx, msg, a_state->domain_state->domain_dn);
+#define QUERY_PARAMETERS(msg, field, attr) \
+ info->field = samdb_result_parameters(mem_ctx, msg, attr);
+
+
+/* these are used to make the Set[User|Group]Info code easier to follow */
+
+#define SET_STRING(msg, field, attr) do { \
+ struct ldb_message_element *set_el; \
+ if (r->in.info->field.string == NULL) return NT_STATUS_INVALID_PARAMETER; \
+ if (r->in.info->field.string[0] == '\0') { \
+ if (ldb_msg_add_empty(msg, attr, LDB_FLAG_MOD_DELETE, NULL)) { \
+ return NT_STATUS_NO_MEMORY; \
+ } \
+ } \
+ if (ldb_msg_add_string(msg, attr, r->in.info->field.string) != 0) { \
+ return NT_STATUS_NO_MEMORY; \
+ } \
+ set_el = ldb_msg_find_element(msg, attr); \
+ set_el->flags = LDB_FLAG_MOD_REPLACE; \
+} while (0)
+
+#define SET_UINT(msg, field, attr) do { \
+ struct ldb_message_element *set_el; \
+ if (samdb_msg_add_uint(sam_ctx, mem_ctx, msg, attr, r->in.info->field) != 0) { \
+ return NT_STATUS_NO_MEMORY; \
+ } \
+ set_el = ldb_msg_find_element(msg, attr); \
+ set_el->flags = LDB_FLAG_MOD_REPLACE; \
+} while (0)
+
+#define SET_INT64(msg, field, attr) do { \
+ struct ldb_message_element *set_el; \
+ if (samdb_msg_add_int64(sam_ctx, mem_ctx, msg, attr, r->in.info->field) != 0) { \
+ return NT_STATUS_NO_MEMORY; \
+ } \
+ set_el = ldb_msg_find_element(msg, attr); \
+ set_el->flags = LDB_FLAG_MOD_REPLACE; \
+} while (0)
+
+#define SET_UINT64(msg, field, attr) do { \
+ struct ldb_message_element *set_el; \
+ if (samdb_msg_add_uint64(sam_ctx, mem_ctx, msg, attr, r->in.info->field) != 0) { \
+ return NT_STATUS_NO_MEMORY; \
+ } \
+ set_el = ldb_msg_find_element(msg, attr); \
+ set_el->flags = LDB_FLAG_MOD_REPLACE; \
+} while (0)
+
+#define CHECK_FOR_MULTIPLES(value, flag, poss_flags) \
+ do { \
+ if ((value & flag) && ((value & flag) != (value & (poss_flags)))) { \
+ return NT_STATUS_INVALID_PARAMETER; \
+ } \
+ } while (0) \
+
+/* Set account flags, discarding flags that cannot be set with SAMR */
+#define SET_AFLAGS(msg, field, attr) do { \
+ struct ldb_message_element *set_el; \
+ if ((r->in.info->field & (ACB_NORMAL | ACB_DOMTRUST | ACB_WSTRUST | ACB_SVRTRUST)) == 0) { \
+ return NT_STATUS_INVALID_PARAMETER; \
+ } \
+ CHECK_FOR_MULTIPLES(r->in.info->field, ACB_NORMAL, ACB_NORMAL | ACB_DOMTRUST | ACB_WSTRUST | ACB_SVRTRUST); \
+ CHECK_FOR_MULTIPLES(r->in.info->field, ACB_DOMTRUST, ACB_NORMAL | ACB_DOMTRUST | ACB_WSTRUST | ACB_SVRTRUST); \
+ CHECK_FOR_MULTIPLES(r->in.info->field, ACB_WSTRUST, ACB_NORMAL | ACB_DOMTRUST | ACB_WSTRUST | ACB_SVRTRUST); \
+ CHECK_FOR_MULTIPLES(r->in.info->field, ACB_SVRTRUST, ACB_NORMAL | ACB_DOMTRUST | ACB_WSTRUST | ACB_SVRTRUST); \
+ if (samdb_msg_add_acct_flags(sam_ctx, mem_ctx, msg, attr, (r->in.info->field & ~(ACB_AUTOLOCK|ACB_PW_EXPIRED))) != 0) { \
+ return NT_STATUS_NO_MEMORY; \
+ } \
+ set_el = ldb_msg_find_element(msg, attr); \
+ set_el->flags = LDB_FLAG_MOD_REPLACE; \
+} while (0)
+
+#define SET_LHOURS(msg, field, attr) do { \
+ struct ldb_message_element *set_el; \
+ if (samdb_msg_add_logon_hours(sam_ctx, mem_ctx, msg, attr, &r->in.info->field) != 0) { \
+ return NT_STATUS_NO_MEMORY; \
+ } \
+ set_el = ldb_msg_find_element(msg, attr); \
+ set_el->flags = LDB_FLAG_MOD_REPLACE; \
+} while (0)
+
+#define SET_PARAMETERS(msg, field, attr) do { \
+ struct ldb_message_element *set_el; \
+ if (r->in.info->field.length != 0) { \
+ if (samdb_msg_add_parameters(sam_ctx, mem_ctx, msg, attr, &r->in.info->field) != 0) { \
+ return NT_STATUS_NO_MEMORY; \
+ } \
+ set_el = ldb_msg_find_element(msg, attr); \
+ set_el->flags = LDB_FLAG_MOD_REPLACE; \
+ } \
+} while (0)
+
+
+
+/*
+ samr_Connect
+
+ create a connection to the SAM database
+*/
+static NTSTATUS dcesrv_samr_Connect(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_Connect *r)
+{
+ struct samr_connect_state *c_state;
+ struct dcesrv_handle *handle;
+
+ ZERO_STRUCTP(r->out.connect_handle);
+
+ c_state = talloc(dce_call->conn, struct samr_connect_state);
+ if (!c_state) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* make sure the sam database is accessible */
+ c_state->sam_ctx = samdb_connect(c_state, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, dce_call->conn->auth_state.session_info);
+ if (c_state->sam_ctx == NULL) {
+ talloc_free(c_state);
+ return NT_STATUS_INVALID_SYSTEM_SERVICE;
+ }
+
+
+ handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_CONNECT);
+ if (!handle) {
+ talloc_free(c_state);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ handle->data = talloc_steal(handle, c_state);
+
+ c_state->access_mask = r->in.access_mask;
+ *r->out.connect_handle = handle->wire_handle;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_Close
+*/
+static NTSTATUS dcesrv_samr_Close(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_Close *r)
+{
+ struct dcesrv_handle *h;
+
+ *r->out.handle = *r->in.handle;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, DCESRV_HANDLE_ANY);
+
+ talloc_free(h);
+
+ ZERO_STRUCTP(r->out.handle);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_SetSecurity
+*/
+static NTSTATUS dcesrv_samr_SetSecurity(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_SetSecurity *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ samr_QuerySecurity
+*/
+static NTSTATUS dcesrv_samr_QuerySecurity(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_QuerySecurity *r)
+{
+ struct dcesrv_handle *h;
+ struct sec_desc_buf *sd;
+
+ *r->out.sdbuf = NULL;
+
+ DCESRV_PULL_HANDLE(h, r->in.handle, DCESRV_HANDLE_ANY);
+
+ sd = talloc(mem_ctx, struct sec_desc_buf);
+ if (sd == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ sd->sd = samdb_default_security_descriptor(mem_ctx);
+
+ *r->out.sdbuf = sd;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_Shutdown
+
+ we refuse this operation completely. If a admin wants to shutdown samr
+ in Samba then they should use the samba admin tools to disable the samr pipe
+*/
+static NTSTATUS dcesrv_samr_Shutdown(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_Shutdown *r)
+{
+ return NT_STATUS_ACCESS_DENIED;
+}
+
+
+/*
+ samr_LookupDomain
+
+ this maps from a domain name to a SID
+*/
+static NTSTATUS dcesrv_samr_LookupDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_LookupDomain *r)
+{
+ struct samr_connect_state *c_state;
+ struct dcesrv_handle *h;
+ struct dom_sid *sid;
+ const char * const dom_attrs[] = { "objectSid", NULL};
+ const char * const ref_attrs[] = { "ncName", NULL};
+ struct ldb_message **dom_msgs;
+ struct ldb_message **ref_msgs;
+ int ret;
+ struct ldb_dn *partitions_basedn;
+
+ *r->out.sid = NULL;
+
+ DCESRV_PULL_HANDLE(h, r->in.connect_handle, SAMR_HANDLE_CONNECT);
+
+ c_state = h->data;
+
+ if (r->in.domain_name->string == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ partitions_basedn = samdb_partitions_dn(c_state->sam_ctx, mem_ctx);
+
+ if (strcasecmp(r->in.domain_name->string, "BUILTIN") == 0) {
+ ret = gendb_search(c_state->sam_ctx,
+ mem_ctx, NULL, &dom_msgs, dom_attrs,
+ "(objectClass=builtinDomain)");
+ } else {
+ ret = gendb_search(c_state->sam_ctx,
+ mem_ctx, partitions_basedn, &ref_msgs, ref_attrs,
+ "(&(&(nETBIOSName=%s)(objectclass=crossRef))(ncName=*))",
+ ldb_binary_encode_string(mem_ctx, r->in.domain_name->string));
+ if (ret != 1) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ ret = gendb_search_dn(c_state->sam_ctx, mem_ctx,
+ samdb_result_dn(c_state->sam_ctx, mem_ctx,
+ ref_msgs[0], "ncName", NULL),
+ &dom_msgs, dom_attrs);
+ }
+
+ if (ret != 1) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ sid = samdb_result_dom_sid(mem_ctx, dom_msgs[0],
+ "objectSid");
+
+ if (sid == NULL) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ *r->out.sid = sid;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_EnumDomains
+
+ list the domains in the SAM
+*/
+static NTSTATUS dcesrv_samr_EnumDomains(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_EnumDomains *r)
+{
+ struct samr_connect_state *c_state;
+ struct dcesrv_handle *h;
+ struct samr_SamArray *array;
+ int i, start_i, ret;
+ const char * const dom_attrs[] = { "cn", NULL};
+ const char * const ref_attrs[] = { "nETBIOSName", NULL};
+ struct ldb_result *dom_res;
+ struct ldb_result *ref_res;
+ struct ldb_dn *partitions_basedn;
+
+ *r->out.resume_handle = 0;
+ *r->out.sam = NULL;
+ *r->out.num_entries = 0;
+
+ DCESRV_PULL_HANDLE(h, r->in.connect_handle, SAMR_HANDLE_CONNECT);
+
+ c_state = h->data;
+
+ partitions_basedn = samdb_partitions_dn(c_state->sam_ctx, mem_ctx);
+
+ ret = ldb_search(c_state->sam_ctx, mem_ctx, &dom_res, ldb_get_default_basedn(c_state->sam_ctx),
+ LDB_SCOPE_SUBTREE, dom_attrs, "(|(|(objectClass=domain)(objectClass=builtinDomain))(objectClass=samba4LocalDomain))");
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0,("samdb: unable to find domains: %s\n", ldb_errstring(c_state->sam_ctx)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ *r->out.resume_handle = dom_res->count;
+
+ start_i = *r->in.resume_handle;
+
+ if (start_i >= dom_res->count) {
+ /* search past end of list is not an error for this call */
+ return NT_STATUS_OK;
+ }
+
+ array = talloc(mem_ctx, struct samr_SamArray);
+ if (array == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ array->count = 0;
+ array->entries = NULL;
+
+ array->entries = talloc_array(mem_ctx, struct samr_SamEntry, dom_res->count - start_i);
+ if (array->entries == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0;i<dom_res->count-start_i;i++) {
+ array->entries[i].idx = start_i + i;
+ /* try and find the domain */
+ ret = ldb_search(c_state->sam_ctx, mem_ctx, &ref_res, partitions_basedn,
+ LDB_SCOPE_SUBTREE, ref_attrs, "(&(objectClass=crossRef)(ncName=%s))",
+ ldb_dn_get_linearized(dom_res->msgs[i]->dn));
+
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0,("samdb: unable to find domains: %s\n", ldb_errstring(c_state->sam_ctx)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ if (ref_res->count == 1) {
+ array->entries[i].name.string = samdb_result_string(ref_res->msgs[0], "nETBIOSName", NULL);
+ } else {
+ array->entries[i].name.string = samdb_result_string(dom_res->msgs[i], "cn", NULL);
+ }
+ }
+
+ *r->out.sam = array;
+ *r->out.num_entries = i;
+ array->count = *r->out.num_entries;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_OpenDomain
+*/
+static NTSTATUS dcesrv_samr_OpenDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_OpenDomain *r)
+{
+ struct dcesrv_handle *h_conn, *h_domain;
+ const char *domain_name;
+ struct samr_connect_state *c_state;
+ struct samr_domain_state *d_state;
+ const char * const dom_attrs[] = { "cn", NULL};
+ const char * const ref_attrs[] = { "nETBIOSName", NULL};
+ struct ldb_message **dom_msgs;
+ struct ldb_message **ref_msgs;
+ int ret;
+ struct ldb_dn *partitions_basedn;
+
+ ZERO_STRUCTP(r->out.domain_handle);
+
+ DCESRV_PULL_HANDLE(h_conn, r->in.connect_handle, SAMR_HANDLE_CONNECT);
+
+ c_state = h_conn->data;
+
+ if (r->in.sid == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ partitions_basedn = samdb_partitions_dn(c_state->sam_ctx, mem_ctx);
+
+ ret = gendb_search(c_state->sam_ctx,
+ mem_ctx, NULL, &dom_msgs, dom_attrs,
+ "(&(objectSid=%s)(|(|(objectClass=domain)(objectClass=builtinDomain))(objectClass=samba4LocalDomain)))",
+ ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
+ if (ret == 0) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ } else if (ret > 1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ } else if (ret == -1) {
+ DEBUG(1, ("Failed to open domain %s: %s\n", dom_sid_string(mem_ctx, r->in.sid), ldb_errstring(c_state->sam_ctx)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ } else {
+ ret = gendb_search(c_state->sam_ctx,
+ mem_ctx, partitions_basedn, &ref_msgs, ref_attrs,
+ "(&(&(nETBIOSName=*)(objectclass=crossRef))(ncName=%s))",
+ ldb_dn_get_linearized(dom_msgs[0]->dn));
+ if (ret == 0) {
+ domain_name = ldb_msg_find_attr_as_string(dom_msgs[0], "cn", NULL);
+ if (domain_name == NULL) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+ } else if (ret == 1) {
+
+ domain_name = ldb_msg_find_attr_as_string(ref_msgs[0], "nETBIOSName", NULL);
+ if (domain_name == NULL) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+ } else {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+ }
+
+ d_state = talloc(c_state, struct samr_domain_state);
+ if (!d_state) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ d_state->role = lp_server_role(dce_call->conn->dce_ctx->lp_ctx);
+ d_state->connect_state = talloc_reference(d_state, c_state);
+ d_state->sam_ctx = c_state->sam_ctx;
+ d_state->domain_sid = dom_sid_dup(d_state, r->in.sid);
+ d_state->domain_name = talloc_strdup(d_state, domain_name);
+ d_state->domain_dn = ldb_dn_copy(d_state, dom_msgs[0]->dn);
+ if (!d_state->domain_sid || !d_state->domain_name || !d_state->domain_dn) {
+ talloc_free(d_state);
+ return NT_STATUS_NO_MEMORY;
+ }
+ d_state->access_mask = r->in.access_mask;
+
+ if (dom_sid_equal(d_state->domain_sid, dom_sid_parse_talloc(mem_ctx, SID_BUILTIN))) {
+ d_state->builtin = true;
+ } else {
+ d_state->builtin = false;
+ }
+
+ d_state->lp_ctx = dce_call->conn->dce_ctx->lp_ctx;
+
+ h_domain = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_DOMAIN);
+ if (!h_domain) {
+ talloc_free(d_state);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ h_domain->data = talloc_steal(h_domain, d_state);
+
+ *r->out.domain_handle = h_domain->wire_handle;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ return DomInfo1
+*/
+static NTSTATUS dcesrv_samr_info_DomInfo1(struct samr_domain_state *state,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message **dom_msgs,
+ struct samr_DomInfo1 *info)
+{
+ info->min_password_length =
+ samdb_result_uint(dom_msgs[0], "minPwdLength", 0);
+ info->password_history_length =
+ samdb_result_uint(dom_msgs[0], "pwdHistoryLength", 0);
+ info->password_properties =
+ samdb_result_uint(dom_msgs[0], "pwdProperties", 0);
+ info->max_password_age =
+ samdb_result_int64(dom_msgs[0], "maxPwdAge", 0);
+ info->min_password_age =
+ samdb_result_int64(dom_msgs[0], "minPwdAge", 0);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ return DomInfo2
+*/
+static NTSTATUS dcesrv_samr_info_DomGeneralInformation(struct samr_domain_state *state,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message **dom_msgs,
+ struct samr_DomGeneralInformation *info)
+{
+ /* This pulls the NetBIOS name from the
+ cn=NTDS Settings,cn=<NETBIOS name of PDC>,....
+ string */
+ info->primary.string = samdb_result_fsmo_name(state->sam_ctx, mem_ctx, dom_msgs[0], "fSMORoleOwner");
+
+ if (!info->primary.string) {
+ info->primary.string = lp_netbios_name(state->lp_ctx);
+ }
+
+ info->force_logoff_time = ldb_msg_find_attr_as_uint64(dom_msgs[0], "forceLogoff",
+ 0x8000000000000000LL);
+
+ info->oem_information.string = samdb_result_string(dom_msgs[0], "oEMInformation", NULL);
+ info->domain_name.string = state->domain_name;
+
+ info->sequence_num = ldb_msg_find_attr_as_uint64(dom_msgs[0], "modifiedCount",
+ 0);
+ switch (state->role) {
+ case ROLE_DOMAIN_CONTROLLER:
+ /* This pulls the NetBIOS name from the
+ cn=NTDS Settings,cn=<NETBIOS name of PDC>,....
+ string */
+ if (samdb_is_pdc(state->sam_ctx)) {
+ info->role = SAMR_ROLE_DOMAIN_PDC;
+ } else {
+ info->role = SAMR_ROLE_DOMAIN_BDC;
+ }
+ break;
+ case ROLE_DOMAIN_MEMBER:
+ info->role = SAMR_ROLE_DOMAIN_MEMBER;
+ break;
+ case ROLE_STANDALONE:
+ info->role = SAMR_ROLE_STANDALONE;
+ break;
+ }
+
+ /* No users in BUILTIN, and the LOCAL group types are only in builtin, and the global group type is never in BUILTIN */
+ info->num_users = samdb_search_count(state->sam_ctx, mem_ctx, state->domain_dn,
+ "(objectClass=user)");
+ info->num_groups = samdb_search_count(state->sam_ctx, mem_ctx, state->domain_dn,
+ "(&(objectClass=group)(sAMAccountType=%u))",
+ ATYPE_GLOBAL_GROUP);
+ info->num_aliases = samdb_search_count(state->sam_ctx, mem_ctx, state->domain_dn,
+ "(&(objectClass=group)(sAMAccountType=%u))",
+ ATYPE_LOCAL_GROUP);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ return DomInfo3
+*/
+static NTSTATUS dcesrv_samr_info_DomInfo3(struct samr_domain_state *state,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message **dom_msgs,
+ struct samr_DomInfo3 *info)
+{
+ info->force_logoff_time = ldb_msg_find_attr_as_uint64(dom_msgs[0], "forceLogoff",
+ 0x8000000000000000LL);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ return DomInfo4
+*/
+static NTSTATUS dcesrv_samr_info_DomOEMInformation(struct samr_domain_state *state,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message **dom_msgs,
+ struct samr_DomOEMInformation *info)
+{
+ info->oem_information.string = samdb_result_string(dom_msgs[0], "oEMInformation", NULL);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ return DomInfo5
+*/
+static NTSTATUS dcesrv_samr_info_DomInfo5(struct samr_domain_state *state,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message **dom_msgs,
+ struct samr_DomInfo5 *info)
+{
+ info->domain_name.string = state->domain_name;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ return DomInfo6
+*/
+static NTSTATUS dcesrv_samr_info_DomInfo6(struct samr_domain_state *state,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message **dom_msgs,
+ struct samr_DomInfo6 *info)
+{
+ /* This pulls the NetBIOS name from the
+ cn=NTDS Settings,cn=<NETBIOS name of PDC>,....
+ string */
+ info->primary.string = samdb_result_fsmo_name(state->sam_ctx, mem_ctx,
+ dom_msgs[0], "fSMORoleOwner");
+
+ if (!info->primary.string) {
+ info->primary.string = lp_netbios_name(state->lp_ctx);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ return DomInfo7
+*/
+static NTSTATUS dcesrv_samr_info_DomInfo7(struct samr_domain_state *state,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message **dom_msgs,
+ struct samr_DomInfo7 *info)
+{
+
+ switch (state->role) {
+ case ROLE_DOMAIN_CONTROLLER:
+ /* This pulls the NetBIOS name from the
+ cn=NTDS Settings,cn=<NETBIOS name of PDC>,....
+ string */
+ if (samdb_is_pdc(state->sam_ctx)) {
+ info->role = SAMR_ROLE_DOMAIN_PDC;
+ } else {
+ info->role = SAMR_ROLE_DOMAIN_BDC;
+ }
+ break;
+ case ROLE_DOMAIN_MEMBER:
+ info->role = SAMR_ROLE_DOMAIN_MEMBER;
+ break;
+ case ROLE_STANDALONE:
+ info->role = SAMR_ROLE_STANDALONE;
+ break;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ return DomInfo8
+*/
+static NTSTATUS dcesrv_samr_info_DomInfo8(struct samr_domain_state *state,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message **dom_msgs,
+ struct samr_DomInfo8 *info)
+{
+ info->sequence_num = ldb_msg_find_attr_as_uint64(dom_msgs[0], "modifiedCount",
+ time(NULL));
+
+ info->domain_create_time = ldb_msg_find_attr_as_uint(dom_msgs[0], "creationTime",
+ 0x0LL);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ return DomInfo9
+*/
+static NTSTATUS dcesrv_samr_info_DomInfo9(struct samr_domain_state *state,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message **dom_msgs,
+ struct samr_DomInfo9 *info)
+{
+ info->domain_server_state = DOMAIN_SERVER_ENABLED;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ return DomInfo11
+*/
+static NTSTATUS dcesrv_samr_info_DomGeneralInformation2(struct samr_domain_state *state,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message **dom_msgs,
+ struct samr_DomGeneralInformation2 *info)
+{
+ NTSTATUS status;
+ status = dcesrv_samr_info_DomGeneralInformation(state, mem_ctx, dom_msgs, &info->general);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ info->lockout_duration = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutDuration",
+ -18000000000LL);
+ info->lockout_window = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockOutObservationWindow",
+ -18000000000LL);
+ info->lockout_threshold = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutThreshold", 0);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ return DomInfo12
+*/
+static NTSTATUS dcesrv_samr_info_DomInfo12(struct samr_domain_state *state,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message **dom_msgs,
+ struct samr_DomInfo12 *info)
+{
+ info->lockout_duration = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutDuration",
+ -18000000000LL);
+ info->lockout_window = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockOutObservationWindow",
+ -18000000000LL);
+ info->lockout_threshold = ldb_msg_find_attr_as_int64(dom_msgs[0], "lockoutThreshold", 0);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ return DomInfo13
+*/
+static NTSTATUS dcesrv_samr_info_DomInfo13(struct samr_domain_state *state,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message **dom_msgs,
+ struct samr_DomInfo13 *info)
+{
+ info->sequence_num = ldb_msg_find_attr_as_uint64(dom_msgs[0], "modifiedCount",
+ time(NULL));
+
+ info->domain_create_time = ldb_msg_find_attr_as_uint(dom_msgs[0], "creationTime",
+ 0x0LL);
+
+ info->modified_count_at_last_promotion = 0;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ samr_QueryDomainInfo
+*/
+static NTSTATUS dcesrv_samr_QueryDomainInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_QueryDomainInfo *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_domain_state *d_state;
+ union samr_DomainInfo *info;
+
+ struct ldb_message **dom_msgs;
+ const char * const *attrs = NULL;
+
+ *r->out.info = NULL;
+
+ DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
+
+ d_state = h->data;
+
+ info = talloc(mem_ctx, union samr_DomainInfo);
+ if (!info) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ switch (r->in.level) {
+ case 1:
+ {
+ static const char * const attrs2[] = { "minPwdLength", "pwdHistoryLength",
+ "pwdProperties", "maxPwdAge",
+ "minPwdAge", NULL };
+ attrs = attrs2;
+ break;
+ }
+ case 2:
+ {
+ static const char * const attrs2[] = {"forceLogoff",
+ "oEMInformation",
+ "modifiedCount",
+ "fSMORoleOwner",
+ NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 3:
+ {
+ static const char * const attrs2[] = {"forceLogoff",
+ NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 4:
+ {
+ static const char * const attrs2[] = {"oEMInformation",
+ NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 5:
+ {
+ attrs = NULL;
+ break;
+ }
+ case 6:
+ {
+ static const char * const attrs2[] = {"fSMORoleOwner",
+ NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 7:
+ {
+ attrs = NULL;
+ break;
+ }
+ case 8:
+ {
+ static const char * const attrs2[] = { "modifiedCount",
+ "creationTime",
+ NULL };
+ attrs = attrs2;
+ break;
+ }
+ case 9:
+ attrs = NULL;
+ break;
+ case 11:
+ {
+ static const char * const attrs2[] = { "oEMInformation", "forceLogoff",
+ "modifiedCount",
+ "lockoutDuration",
+ "lockOutObservationWindow",
+ "lockoutThreshold",
+ NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 12:
+ {
+ static const char * const attrs2[] = { "lockoutDuration",
+ "lockOutObservationWindow",
+ "lockoutThreshold",
+ NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 13:
+ {
+ static const char * const attrs2[] = { "modifiedCount",
+ "creationTime",
+ NULL };
+ attrs = attrs2;
+ break;
+ }
+ }
+
+ /* some levels don't need a search */
+ if (attrs) {
+ int ret;
+ ret = gendb_search_dn(d_state->sam_ctx, mem_ctx,
+ d_state->domain_dn, &dom_msgs, attrs);
+ if (ret != 1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ }
+
+ *r->out.info = info;
+
+ ZERO_STRUCTP(info);
+
+ switch (r->in.level) {
+ case 1:
+ return dcesrv_samr_info_DomInfo1(d_state, mem_ctx, dom_msgs,
+ &info->info1);
+ case 2:
+ return dcesrv_samr_info_DomGeneralInformation(d_state, mem_ctx, dom_msgs,
+ &info->general);
+ case 3:
+ return dcesrv_samr_info_DomInfo3(d_state, mem_ctx, dom_msgs,
+ &info->info3);
+ case 4:
+ return dcesrv_samr_info_DomOEMInformation(d_state, mem_ctx, dom_msgs,
+ &info->oem);
+ case 5:
+ return dcesrv_samr_info_DomInfo5(d_state, mem_ctx, dom_msgs,
+ &info->info5);
+ case 6:
+ return dcesrv_samr_info_DomInfo6(d_state, mem_ctx, dom_msgs,
+ &info->info6);
+ case 7:
+ return dcesrv_samr_info_DomInfo7(d_state, mem_ctx, dom_msgs,
+ &info->info7);
+ case 8:
+ return dcesrv_samr_info_DomInfo8(d_state, mem_ctx, dom_msgs,
+ &info->info8);
+ case 9:
+ return dcesrv_samr_info_DomInfo9(d_state, mem_ctx, dom_msgs,
+ &info->info9);
+ case 11:
+ return dcesrv_samr_info_DomGeneralInformation2(d_state, mem_ctx, dom_msgs,
+ &info->general2);
+ case 12:
+ return dcesrv_samr_info_DomInfo12(d_state, mem_ctx, dom_msgs,
+ &info->info12);
+ case 13:
+ return dcesrv_samr_info_DomInfo13(d_state, mem_ctx, dom_msgs,
+ &info->info13);
+ }
+
+ return NT_STATUS_INVALID_INFO_CLASS;
+}
+
+
+/*
+ samr_SetDomainInfo
+*/
+static NTSTATUS dcesrv_samr_SetDomainInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_SetDomainInfo *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_domain_state *d_state;
+ struct ldb_message *msg;
+ int ret;
+ struct ldb_context *sam_ctx;
+
+ DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
+
+ d_state = h->data;
+ sam_ctx = d_state->sam_ctx;
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ msg->dn = talloc_reference(mem_ctx, d_state->domain_dn);
+ if (!msg->dn) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ switch (r->in.level) {
+ case 1:
+ SET_UINT (msg, info1.min_password_length, "minPwdLength");
+ SET_UINT (msg, info1.password_history_length, "pwdHistoryLength");
+ SET_UINT (msg, info1.password_properties, "pwdProperties");
+ SET_INT64 (msg, info1.max_password_age, "maxPwdAge");
+ SET_INT64 (msg, info1.min_password_age, "minPwdAge");
+ break;
+ case 3:
+ SET_UINT64 (msg, info3.force_logoff_time, "forceLogoff");
+ break;
+ case 4:
+ SET_STRING(msg, oem.oem_information, "oEMInformation");
+ break;
+
+ case 6:
+ case 7:
+ case 9:
+ /* No op, we don't know where to set these */
+ return NT_STATUS_OK;
+
+ case 12:
+
+ SET_INT64 (msg, info12.lockout_duration, "lockoutDuration");
+ SET_INT64 (msg, info12.lockout_window, "lockOutObservationWindow");
+ SET_INT64 (msg, info12.lockout_threshold, "lockoutThreshold");
+ break;
+
+ default:
+ /* many info classes are not valid for SetDomainInfo */
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ /* modify the samdb record */
+ ret = ldb_modify(sam_ctx, msg);
+ if (ret != 0) {
+ DEBUG(1,("Failed to modify record %s: %s\n",
+ ldb_dn_get_linearized(d_state->domain_dn),
+ ldb_errstring(sam_ctx)));
+
+ /* we really need samdb.c to return NTSTATUS */
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ samr_CreateDomainGroup
+*/
+static NTSTATUS dcesrv_samr_CreateDomainGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_CreateDomainGroup *r)
+{
+ struct samr_domain_state *d_state;
+ struct samr_account_state *a_state;
+ struct dcesrv_handle *h;
+ const char *name;
+ struct ldb_message *msg;
+ struct dom_sid *sid;
+ const char *groupname;
+ struct dcesrv_handle *g_handle;
+ int ret;
+
+ ZERO_STRUCTP(r->out.group_handle);
+ *r->out.rid = 0;
+
+ DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
+
+ d_state = h->data;
+
+ if (d_state->builtin) {
+ DEBUG(5, ("Cannot create a domain group in the BUILTIN domain"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ groupname = r->in.name->string;
+
+ if (groupname == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* check if the group already exists */
+ name = samdb_search_string(d_state->sam_ctx, mem_ctx, NULL,
+ "sAMAccountName",
+ "(&(sAMAccountName=%s)(objectclass=group))",
+ ldb_binary_encode_string(mem_ctx, groupname));
+ if (name != NULL) {
+ return NT_STATUS_GROUP_EXISTS;
+ }
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* add core elements to the ldb_message for the user */
+ msg->dn = ldb_dn_copy(mem_ctx, d_state->domain_dn);
+ ldb_dn_add_child_fmt(msg->dn, "CN=%s,CN=Users", groupname);
+ if (!msg->dn) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ samdb_msg_add_string(d_state->sam_ctx, mem_ctx, msg, "sAMAccountName", groupname);
+ samdb_msg_add_string(d_state->sam_ctx, mem_ctx, msg, "objectClass", "group");
+
+ /* create the group */
+ ret = ldb_add(d_state->sam_ctx, msg);
+ switch (ret) {
+ case LDB_SUCCESS:
+ break;
+ case LDB_ERR_ENTRY_ALREADY_EXISTS:
+ DEBUG(0,("Failed to create group record %s: %s\n",
+ ldb_dn_get_linearized(msg->dn),
+ ldb_errstring(d_state->sam_ctx)));
+ return NT_STATUS_GROUP_EXISTS;
+ case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
+ DEBUG(0,("Failed to create group record %s: %s\n",
+ ldb_dn_get_linearized(msg->dn),
+ ldb_errstring(d_state->sam_ctx)));
+ return NT_STATUS_ACCESS_DENIED;
+ default:
+ DEBUG(0,("Failed to create group record %s: %s\n",
+ ldb_dn_get_linearized(msg->dn),
+ ldb_errstring(d_state->sam_ctx)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ a_state = talloc(d_state, struct samr_account_state);
+ if (!a_state) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ a_state->sam_ctx = d_state->sam_ctx;
+ a_state->access_mask = r->in.access_mask;
+ a_state->domain_state = talloc_reference(a_state, d_state);
+ a_state->account_dn = talloc_steal(a_state, msg->dn);
+
+ /* retrieve the sid for the group just created */
+ sid = samdb_search_dom_sid(d_state->sam_ctx, a_state,
+ msg->dn, "objectSid", NULL);
+ if (sid == NULL) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ a_state->account_name = talloc_strdup(a_state, groupname);
+ if (!a_state->account_name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* create the policy handle */
+ g_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_GROUP);
+ if (!g_handle) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ g_handle->data = talloc_steal(g_handle, a_state);
+
+ *r->out.group_handle = g_handle->wire_handle;
+ *r->out.rid = sid->sub_auths[sid->num_auths-1];
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ comparison function for sorting SamEntry array
+*/
+static int compare_SamEntry(struct samr_SamEntry *e1, struct samr_SamEntry *e2)
+{
+ return e1->idx - e2->idx;
+}
+
+/*
+ samr_EnumDomainGroups
+*/
+static NTSTATUS dcesrv_samr_EnumDomainGroups(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_EnumDomainGroups *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_domain_state *d_state;
+ struct ldb_message **res;
+ int ldb_cnt, count, i, first;
+ struct samr_SamEntry *entries;
+ const char * const attrs[3] = { "objectSid", "sAMAccountName", NULL };
+ struct samr_SamArray *sam;
+
+ *r->out.resume_handle = 0;
+ *r->out.sam = NULL;
+ *r->out.num_entries = 0;
+
+ DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
+
+ d_state = h->data;
+
+ /* search for all domain groups in this domain. This could possibly be
+ cached and resumed based on resume_key */
+ ldb_cnt = samdb_search_domain(d_state->sam_ctx, mem_ctx,
+ d_state->domain_dn, &res, attrs,
+ d_state->domain_sid,
+ "(&(grouptype=%d)(objectclass=group))",
+ GTYPE_SECURITY_GLOBAL_GROUP);
+ if (ldb_cnt == -1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ /* convert to SamEntry format */
+ entries = talloc_array(mem_ctx, struct samr_SamEntry, ldb_cnt);
+ if (!entries) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ count = 0;
+
+ for (i=0;i<ldb_cnt;i++) {
+ struct dom_sid *group_sid;
+
+ group_sid = samdb_result_dom_sid(mem_ctx, res[i],
+ "objectSid");
+ if (group_sid == NULL)
+ continue;
+
+ entries[count].idx =
+ group_sid->sub_auths[group_sid->num_auths-1];
+ entries[count].name.string =
+ samdb_result_string(res[i], "sAMAccountName", "");
+ count += 1;
+ }
+
+ /* sort the results by rid */
+ qsort(entries, count, sizeof(struct samr_SamEntry),
+ (comparison_fn_t)compare_SamEntry);
+
+ /* find the first entry to return */
+ for (first=0;
+ first<count && entries[first].idx <= *r->in.resume_handle;
+ first++) ;
+
+ /* return the rest, limit by max_size. Note that we
+ use the w2k3 element size value of 54 */
+ *r->out.num_entries = count - first;
+ *r->out.num_entries = MIN(*r->out.num_entries,
+ 1+(r->in.max_size/SAMR_ENUM_USERS_MULTIPLIER));
+
+ sam = talloc(mem_ctx, struct samr_SamArray);
+ if (!sam) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ sam->entries = entries+first;
+ sam->count = *r->out.num_entries;
+
+ *r->out.sam = sam;
+
+ if (*r->out.num_entries < count - first) {
+ *r->out.resume_handle = entries[first+*r->out.num_entries-1].idx;
+ return STATUS_MORE_ENTRIES;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_CreateUser2
+
+ This call uses transactions to ensure we don't get a new conflicting
+ user while we are processing this, and to ensure the user either
+ completly exists, or does not.
+*/
+static NTSTATUS dcesrv_samr_CreateUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_CreateUser2 *r)
+{
+ struct samr_domain_state *d_state;
+ struct samr_account_state *a_state;
+ struct dcesrv_handle *h;
+ const char *name;
+ struct ldb_message *msg;
+ struct dom_sid *sid;
+ const char *account_name;
+ struct dcesrv_handle *u_handle;
+ int ret;
+ const char *container, *obj_class=NULL;
+ char *cn_name;
+ int cn_name_len;
+
+ const char *attrs[] = {
+ "objectSid",
+ "userAccountControl",
+ NULL
+ };
+
+ uint32_t user_account_control;
+
+ struct ldb_message **msgs;
+
+ ZERO_STRUCTP(r->out.user_handle);
+ *r->out.access_granted = 0;
+ *r->out.rid = 0;
+
+ DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
+
+ d_state = h->data;
+
+ if (d_state->builtin) {
+ DEBUG(5, ("Cannot create a user in the BUILTIN domain"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ account_name = r->in.account_name->string;
+
+ if (account_name == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ ret = ldb_transaction_start(d_state->sam_ctx);
+ if (ret != 0) {
+ DEBUG(0,("Failed to start a transaction for user creation: %s\n",
+ ldb_errstring(d_state->sam_ctx)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ /* check if the user already exists */
+ name = samdb_search_string(d_state->sam_ctx, mem_ctx, NULL,
+ "sAMAccountName",
+ "(&(sAMAccountName=%s)(objectclass=user))",
+ ldb_binary_encode_string(mem_ctx, account_name));
+ if (name != NULL) {
+ ldb_transaction_cancel(d_state->sam_ctx);
+ return NT_STATUS_USER_EXISTS;
+ }
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ ldb_transaction_cancel(d_state->sam_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ cn_name = talloc_strdup(mem_ctx, account_name);
+ if (!cn_name) {
+ ldb_transaction_cancel(d_state->sam_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ cn_name_len = strlen(cn_name);
+
+ /* This must be one of these values *only* */
+ if (r->in.acct_flags == ACB_NORMAL) {
+ container = "CN=Users";
+ obj_class = "user";
+
+ } else if (r->in.acct_flags == ACB_WSTRUST) {
+ if (cn_name[cn_name_len - 1] != '$') {
+ return NT_STATUS_FOOBAR;
+ }
+ cn_name[cn_name_len - 1] = '\0';
+ container = "CN=Computers";
+ obj_class = "computer";
+ samdb_msg_add_int(d_state->sam_ctx, mem_ctx, msg, "primaryGroupID", DOMAIN_RID_DOMAIN_MEMBERS);
+
+ } else if (r->in.acct_flags == ACB_SVRTRUST) {
+ if (cn_name[cn_name_len - 1] != '$') {
+ return NT_STATUS_FOOBAR;
+ }
+ cn_name[cn_name_len - 1] = '\0';
+ container = "OU=Domain Controllers";
+ obj_class = "computer";
+ samdb_msg_add_int(d_state->sam_ctx, mem_ctx, msg, "primaryGroupID", DOMAIN_RID_DCS);
+
+ } else if (r->in.acct_flags == ACB_DOMTRUST) {
+ container = "CN=Users";
+ obj_class = "user";
+
+ } else {
+ ldb_transaction_cancel(d_state->sam_ctx);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* add core elements to the ldb_message for the user */
+ msg->dn = ldb_dn_copy(mem_ctx, d_state->domain_dn);
+ if ( ! ldb_dn_add_child_fmt(msg->dn, "CN=%s,%s", cn_name, container)) {
+ ldb_transaction_cancel(d_state->sam_ctx);
+ return NT_STATUS_FOOBAR;
+ }
+
+ samdb_msg_add_string(d_state->sam_ctx, mem_ctx, msg, "sAMAccountName", account_name);
+ samdb_msg_add_string(d_state->sam_ctx, mem_ctx, msg, "objectClass", obj_class);
+
+ /* Start a transaction, so we can query and do a subsequent atomic modify */
+
+ /* create the user */
+ ret = ldb_add(d_state->sam_ctx, msg);
+ switch (ret) {
+ case LDB_SUCCESS:
+ break;
+ case LDB_ERR_ENTRY_ALREADY_EXISTS:
+ ldb_transaction_cancel(d_state->sam_ctx);
+ DEBUG(0,("Failed to create user record %s: %s\n",
+ ldb_dn_get_linearized(msg->dn),
+ ldb_errstring(d_state->sam_ctx)));
+ return NT_STATUS_USER_EXISTS;
+ case LDB_ERR_UNWILLING_TO_PERFORM:
+ case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
+ ldb_transaction_cancel(d_state->sam_ctx);
+ DEBUG(0,("Failed to create user record %s: %s\n",
+ ldb_dn_get_linearized(msg->dn),
+ ldb_errstring(d_state->sam_ctx)));
+ return NT_STATUS_ACCESS_DENIED;
+ default:
+ ldb_transaction_cancel(d_state->sam_ctx);
+ DEBUG(0,("Failed to create user record %s: %s\n",
+ ldb_dn_get_linearized(msg->dn),
+ ldb_errstring(d_state->sam_ctx)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ a_state = talloc(d_state, struct samr_account_state);
+ if (!a_state) {
+ ldb_transaction_cancel(d_state->sam_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ a_state->sam_ctx = d_state->sam_ctx;
+ a_state->access_mask = r->in.access_mask;
+ a_state->domain_state = talloc_reference(a_state, d_state);
+ a_state->account_dn = talloc_steal(a_state, msg->dn);
+
+ /* retrieve the sid and account control bits for the user just created */
+ ret = gendb_search_dn(d_state->sam_ctx, a_state,
+ msg->dn, &msgs, attrs);
+
+ if (ret != 1) {
+ ldb_transaction_cancel(d_state->sam_ctx);
+ DEBUG(0,("Apparently we failed to create an account record, as %s now doesn't exist\n",
+ ldb_dn_get_linearized(msg->dn)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ sid = samdb_result_dom_sid(mem_ctx, msgs[0], "objectSid");
+ if (sid == NULL) {
+ ldb_transaction_cancel(d_state->sam_ctx);
+ DEBUG(0,("Apparently we failed to get the objectSid of the just created account record %s\n",
+ ldb_dn_get_linearized(msg->dn)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ /* Change the account control to be the correct account type.
+ * The default is for a workstation account */
+ user_account_control = samdb_result_uint(msgs[0], "userAccountControl", 0);
+ user_account_control = (user_account_control &
+ ~(UF_NORMAL_ACCOUNT |
+ UF_INTERDOMAIN_TRUST_ACCOUNT |
+ UF_WORKSTATION_TRUST_ACCOUNT |
+ UF_SERVER_TRUST_ACCOUNT));
+ user_account_control |= samdb_acb2uf(r->in.acct_flags);
+
+ talloc_free(msg);
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ ldb_transaction_cancel(d_state->sam_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ msg->dn = ldb_dn_copy(msg, a_state->account_dn);
+
+ if (samdb_msg_add_uint(a_state->sam_ctx, mem_ctx, msg,
+ "userAccountControl",
+ user_account_control) != 0) {
+ ldb_transaction_cancel(d_state->sam_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* modify the samdb record */
+ ret = samdb_replace(a_state->sam_ctx, mem_ctx, msg);
+ if (ret != 0) {
+ DEBUG(0,("Failed to modify account record %s to set userAccountControl: %s\n",
+ ldb_dn_get_linearized(msg->dn),
+ ldb_errstring(d_state->sam_ctx)));
+ ldb_transaction_cancel(d_state->sam_ctx);
+
+ /* we really need samdb.c to return NTSTATUS */
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ ret = ldb_transaction_commit(d_state->sam_ctx);
+ if (ret != 0) {
+ DEBUG(0,("Failed to commit transaction to add and modify account record %s: %s\n",
+ ldb_dn_get_linearized(msg->dn),
+ ldb_errstring(d_state->sam_ctx)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ a_state->account_name = talloc_steal(a_state, account_name);
+ if (!a_state->account_name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* create the policy handle */
+ u_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_USER);
+ if (!u_handle) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ u_handle->data = talloc_steal(u_handle, a_state);
+
+ *r->out.user_handle = u_handle->wire_handle;
+ *r->out.access_granted = 0xf07ff; /* TODO: fix access mask calculations */
+
+ *r->out.rid = sid->sub_auths[sid->num_auths-1];
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_CreateUser
+*/
+static NTSTATUS dcesrv_samr_CreateUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_CreateUser *r)
+{
+ struct samr_CreateUser2 r2;
+ uint32_t access_granted = 0;
+
+
+ /* a simple wrapper around samr_CreateUser2 works nicely */
+ r2.in.domain_handle = r->in.domain_handle;
+ r2.in.account_name = r->in.account_name;
+ r2.in.acct_flags = ACB_NORMAL;
+ r2.in.access_mask = r->in.access_mask;
+ r2.out.user_handle = r->out.user_handle;
+ r2.out.access_granted = &access_granted;
+ r2.out.rid = r->out.rid;
+
+ return dcesrv_samr_CreateUser2(dce_call, mem_ctx, &r2);
+}
+
+/*
+ samr_EnumDomainUsers
+*/
+static NTSTATUS dcesrv_samr_EnumDomainUsers(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_EnumDomainUsers *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_domain_state *d_state;
+ struct ldb_result *res;
+ int ret, num_filtered_entries, i, first;
+ struct samr_SamEntry *entries;
+ const char * const attrs[] = { "objectSid", "sAMAccountName", "userAccountControl", NULL };
+ struct samr_SamArray *sam;
+
+ *r->out.resume_handle = 0;
+ *r->out.sam = NULL;
+ *r->out.num_entries = 0;
+
+ DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
+
+ d_state = h->data;
+
+ /* don't have to worry about users in the builtin domain, as there are none */
+ ret = ldb_search(d_state->sam_ctx, mem_ctx, &res, d_state->domain_dn, LDB_SCOPE_SUBTREE, attrs, "objectClass=user");
+
+ if (ret != LDB_SUCCESS) {
+ DEBUG(3, ("Failed to search for Domain Users in %s: %s\n",
+ ldb_dn_get_linearized(d_state->domain_dn), ldb_errstring(d_state->sam_ctx)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ /* convert to SamEntry format */
+ entries = talloc_array(mem_ctx, struct samr_SamEntry, res->count);
+ if (!entries) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ num_filtered_entries = 0;
+ for (i=0;i<res->count;i++) {
+ /* Check if a mask has been requested */
+ if (r->in.acct_flags
+ && ((samdb_result_acct_flags(d_state->sam_ctx, mem_ctx, res->msgs[i],
+ d_state->domain_dn) & r->in.acct_flags) == 0)) {
+ continue;
+ }
+ entries[num_filtered_entries].idx = samdb_result_rid_from_sid(mem_ctx, res->msgs[i], "objectSid", 0);
+ entries[num_filtered_entries].name.string = samdb_result_string(res->msgs[i], "sAMAccountName", "");
+ num_filtered_entries++;
+ }
+
+ /* sort the results by rid */
+ qsort(entries, num_filtered_entries, sizeof(struct samr_SamEntry),
+ (comparison_fn_t)compare_SamEntry);
+
+ /* find the first entry to return */
+ for (first=0;
+ first<num_filtered_entries && entries[first].idx <= *r->in.resume_handle;
+ first++) ;
+
+ /* return the rest, limit by max_size. Note that we
+ use the w2k3 element size value of 54 */
+ *r->out.num_entries = num_filtered_entries - first;
+ *r->out.num_entries = MIN(*r->out.num_entries,
+ 1+(r->in.max_size/SAMR_ENUM_USERS_MULTIPLIER));
+
+ sam = talloc(mem_ctx, struct samr_SamArray);
+ if (!sam) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ sam->entries = entries+first;
+ sam->count = *r->out.num_entries;
+
+ *r->out.sam = sam;
+
+ if (first == num_filtered_entries) {
+ return NT_STATUS_OK;
+ }
+
+ if (*r->out.num_entries < num_filtered_entries - first) {
+ *r->out.resume_handle = entries[first+*r->out.num_entries-1].idx;
+ return STATUS_MORE_ENTRIES;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_CreateDomAlias
+*/
+static NTSTATUS dcesrv_samr_CreateDomAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_CreateDomAlias *r)
+{
+ struct samr_domain_state *d_state;
+ struct samr_account_state *a_state;
+ struct dcesrv_handle *h;
+ const char *alias_name, *name;
+ struct ldb_message *msg;
+ struct dom_sid *sid;
+ struct dcesrv_handle *a_handle;
+ int ret;
+
+ ZERO_STRUCTP(r->out.alias_handle);
+ *r->out.rid = 0;
+
+ DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
+
+ d_state = h->data;
+
+ if (d_state->builtin) {
+ DEBUG(5, ("Cannot create a domain alias in the BUILTIN domain"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ alias_name = r->in.alias_name->string;
+
+ if (alias_name == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* Check if alias already exists */
+ name = samdb_search_string(d_state->sam_ctx, mem_ctx, NULL,
+ "sAMAccountName",
+ "(sAMAccountName=%s)(objectclass=group))",
+ ldb_binary_encode_string(mem_ctx, alias_name));
+
+ if (name != NULL) {
+ return NT_STATUS_ALIAS_EXISTS;
+ }
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* add core elements to the ldb_message for the alias */
+ msg->dn = ldb_dn_copy(mem_ctx, d_state->domain_dn);
+ ldb_dn_add_child_fmt(msg->dn, "CN=%s,CN=Users", alias_name);
+ if (!msg->dn) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ samdb_msg_add_string(d_state->sam_ctx, mem_ctx, msg, "sAMAccountName", alias_name);
+ samdb_msg_add_string(d_state->sam_ctx, mem_ctx, msg, "objectClass", "group");
+ samdb_msg_add_int(d_state->sam_ctx, mem_ctx, msg, "groupType", GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
+
+ /* create the alias */
+ ret = ldb_add(d_state->sam_ctx, msg);
+ switch (ret) {
+ case LDB_SUCCESS:
+ break;
+ case LDB_ERR_ENTRY_ALREADY_EXISTS:
+ return NT_STATUS_ALIAS_EXISTS;
+ case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
+ return NT_STATUS_ACCESS_DENIED;
+ default:
+ DEBUG(0,("Failed to create alias record %s: %s\n",
+ ldb_dn_get_linearized(msg->dn),
+ ldb_errstring(d_state->sam_ctx)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ a_state = talloc(d_state, struct samr_account_state);
+ if (!a_state) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ a_state->sam_ctx = d_state->sam_ctx;
+ a_state->access_mask = r->in.access_mask;
+ a_state->domain_state = talloc_reference(a_state, d_state);
+ a_state->account_dn = talloc_steal(a_state, msg->dn);
+
+ /* retrieve the sid for the alias just created */
+ sid = samdb_search_dom_sid(d_state->sam_ctx, a_state,
+ msg->dn, "objectSid", NULL);
+
+ a_state->account_name = talloc_strdup(a_state, alias_name);
+ if (!a_state->account_name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* create the policy handle */
+ a_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_ALIAS);
+ if (a_handle == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ a_handle->data = talloc_steal(a_handle, a_state);
+
+ *r->out.alias_handle = a_handle->wire_handle;
+
+ *r->out.rid = sid->sub_auths[sid->num_auths-1];
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_EnumDomainAliases
+*/
+static NTSTATUS dcesrv_samr_EnumDomainAliases(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_EnumDomainAliases *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_domain_state *d_state;
+ struct ldb_message **res;
+ int ldb_cnt, count, i, first;
+ struct samr_SamEntry *entries;
+ const char * const attrs[3] = { "objectSid", "sAMAccountName", NULL };
+ struct samr_SamArray *sam;
+
+ *r->out.resume_handle = 0;
+ *r->out.sam = NULL;
+ *r->out.num_entries = 0;
+
+ DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
+
+ d_state = h->data;
+
+ /* search for all domain groups in this domain. This could possibly be
+ cached and resumed based on resume_key */
+ ldb_cnt = samdb_search_domain(d_state->sam_ctx, mem_ctx,
+ d_state->domain_dn,
+ &res, attrs,
+ d_state->domain_sid,
+ "(&(|(grouptype=%d)(grouptype=%d)))"
+ "(objectclass=group))",
+ GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
+ GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
+ if (ldb_cnt == -1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ if (ldb_cnt == 0) {
+ return NT_STATUS_OK;
+ }
+
+ /* convert to SamEntry format */
+ entries = talloc_array(mem_ctx, struct samr_SamEntry, ldb_cnt);
+ if (!entries) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ count = 0;
+
+ for (i=0;i<ldb_cnt;i++) {
+ struct dom_sid *alias_sid;
+
+ alias_sid = samdb_result_dom_sid(mem_ctx, res[i],
+ "objectSid");
+
+ if (alias_sid == NULL)
+ continue;
+
+ entries[count].idx =
+ alias_sid->sub_auths[alias_sid->num_auths-1];
+ entries[count].name.string =
+ samdb_result_string(res[i], "sAMAccountName", "");
+ count += 1;
+ }
+
+ /* sort the results by rid */
+ qsort(entries, count, sizeof(struct samr_SamEntry),
+ (comparison_fn_t)compare_SamEntry);
+
+ /* find the first entry to return */
+ for (first=0;
+ first<count && entries[first].idx <= *r->in.resume_handle;
+ first++) ;
+
+ if (first == count) {
+ return NT_STATUS_OK;
+ }
+
+ *r->out.num_entries = count - first;
+ *r->out.num_entries = MIN(*r->out.num_entries, 1000);
+
+ sam = talloc(mem_ctx, struct samr_SamArray);
+ if (!sam) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ sam->entries = entries+first;
+ sam->count = *r->out.num_entries;
+
+ *r->out.sam = sam;
+
+ if (*r->out.num_entries < count - first) {
+ *r->out.resume_handle =
+ entries[first+*r->out.num_entries-1].idx;
+ return STATUS_MORE_ENTRIES;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_GetAliasMembership
+*/
+static NTSTATUS dcesrv_samr_GetAliasMembership(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_GetAliasMembership *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_domain_state *d_state;
+ struct ldb_message **res;
+ int i, count = 0;
+
+ DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
+
+ d_state = h->data;
+
+ if (r->in.sids->num_sids > 0) {
+ const char *filter;
+ const char * const attrs[2] = { "objectSid", NULL };
+
+ filter = talloc_asprintf(mem_ctx,
+ "(&(|(grouptype=%d)(grouptype=%d))"
+ "(objectclass=group)(|",
+ GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
+ GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
+ if (filter == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ for (i=0; i<r->in.sids->num_sids; i++) {
+ const char *memberdn;
+
+ memberdn =
+ samdb_search_string(d_state->sam_ctx,
+ mem_ctx, NULL, "distinguishedName",
+ "(objectSid=%s)",
+ ldap_encode_ndr_dom_sid(mem_ctx,
+ r->in.sids->sids[i].sid));
+
+ if (memberdn == NULL)
+ continue;
+
+ filter = talloc_asprintf(mem_ctx, "%s(member=%s)",
+ filter, memberdn);
+ if (filter == NULL)
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ count = samdb_search_domain(d_state->sam_ctx, mem_ctx,
+ d_state->domain_dn, &res, attrs,
+ d_state->domain_sid, "%s))", filter);
+ if (count < 0)
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ r->out.rids->count = 0;
+ r->out.rids->ids = talloc_array(mem_ctx, uint32_t, count);
+ if (r->out.rids->ids == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ for (i=0; i<count; i++) {
+ struct dom_sid *alias_sid;
+
+ alias_sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
+
+ if (alias_sid == NULL) {
+ DEBUG(0, ("Could not find objectSid\n"));
+ continue;
+ }
+
+ r->out.rids->ids[r->out.rids->count] =
+ alias_sid->sub_auths[alias_sid->num_auths-1];
+ r->out.rids->count += 1;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_LookupNames
+*/
+static NTSTATUS dcesrv_samr_LookupNames(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_LookupNames *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_domain_state *d_state;
+ int i, num_mapped;
+ NTSTATUS status = NT_STATUS_OK;
+ const char * const attrs[] = { "sAMAccountType", "objectSid", NULL };
+ int count;
+
+ ZERO_STRUCTP(r->out.rids);
+ ZERO_STRUCTP(r->out.types);
+
+ DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
+
+ d_state = h->data;
+
+ if (r->in.num_names == 0) {
+ return NT_STATUS_OK;
+ }
+
+ r->out.rids->ids = talloc_array(mem_ctx, uint32_t, r->in.num_names);
+ r->out.types->ids = talloc_array(mem_ctx, uint32_t, r->in.num_names);
+ if (!r->out.rids->ids || !r->out.types->ids) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ r->out.rids->count = r->in.num_names;
+ r->out.types->count = r->in.num_names;
+
+ num_mapped = 0;
+
+ for (i=0;i<r->in.num_names;i++) {
+ struct ldb_message **res;
+ struct dom_sid *sid;
+ uint32_t atype, rtype;
+
+ r->out.rids->ids[i] = 0;
+ r->out.types->ids[i] = SID_NAME_UNKNOWN;
+
+ count = gendb_search(d_state->sam_ctx, mem_ctx, d_state->domain_dn, &res, attrs,
+ "sAMAccountName=%s",
+ ldb_binary_encode_string(mem_ctx, r->in.names[i].string));
+ if (count != 1) {
+ status = STATUS_SOME_UNMAPPED;
+ continue;
+ }
+
+ sid = samdb_result_dom_sid(mem_ctx, res[0], "objectSid");
+ if (sid == NULL) {
+ status = STATUS_SOME_UNMAPPED;
+ continue;
+ }
+
+ atype = samdb_result_uint(res[0], "sAMAccountType", 0);
+ if (atype == 0) {
+ status = STATUS_SOME_UNMAPPED;
+ continue;
+ }
+
+ rtype = samdb_atype_map(atype);
+
+ if (rtype == SID_NAME_UNKNOWN) {
+ status = STATUS_SOME_UNMAPPED;
+ continue;
+ }
+
+ r->out.rids->ids[i] = sid->sub_auths[sid->num_auths-1];
+ r->out.types->ids[i] = rtype;
+ num_mapped++;
+ }
+
+ if (num_mapped == 0) {
+ return NT_STATUS_NONE_MAPPED;
+ }
+ return status;
+}
+
+
+/*
+ samr_LookupRids
+*/
+static NTSTATUS dcesrv_samr_LookupRids(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_LookupRids *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_domain_state *d_state;
+ int i, total;
+ NTSTATUS status = NT_STATUS_OK;
+ struct lsa_String *names;
+ uint32_t *ids;
+
+ ZERO_STRUCTP(r->out.names);
+ ZERO_STRUCTP(r->out.types);
+
+ DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
+
+ d_state = h->data;
+
+ if (r->in.num_rids == 0)
+ return NT_STATUS_OK;
+
+ names = talloc_array(mem_ctx, struct lsa_String, r->in.num_rids);
+ ids = talloc_array(mem_ctx, uint32_t, r->in.num_rids);
+
+ if ((names == NULL) || (ids == NULL))
+ return NT_STATUS_NO_MEMORY;
+
+ total = 0;
+
+ for (i=0; i<r->in.num_rids; i++) {
+ struct ldb_message **res;
+ int count;
+ const char * const attrs[] = { "sAMAccountType",
+ "sAMAccountName", NULL };
+ uint32_t atype;
+ struct dom_sid *sid;
+
+ ids[i] = SID_NAME_UNKNOWN;
+
+ sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rids[i]);
+ if (sid == NULL) {
+ names[i].string = NULL;
+ status = STATUS_SOME_UNMAPPED;
+ continue;
+ }
+
+ count = gendb_search(d_state->sam_ctx, mem_ctx,
+ d_state->domain_dn, &res, attrs,
+ "(objectSid=%s)",
+ ldap_encode_ndr_dom_sid(mem_ctx, sid));
+ if (count != 1) {
+ names[i].string = NULL;
+ status = STATUS_SOME_UNMAPPED;
+ continue;
+ }
+
+ names[i].string = samdb_result_string(res[0], "sAMAccountName",
+ NULL);
+
+ atype = samdb_result_uint(res[0], "sAMAccountType", 0);
+ if (atype == 0) {
+ status = STATUS_SOME_UNMAPPED;
+ continue;
+ }
+
+ ids[i] = samdb_atype_map(atype);
+
+ if (ids[i] == SID_NAME_UNKNOWN) {
+ status = STATUS_SOME_UNMAPPED;
+ continue;
+ }
+ }
+
+ r->out.names->names = names;
+ r->out.names->count = r->in.num_rids;
+
+ r->out.types->ids = ids;
+ r->out.types->count = r->in.num_rids;
+
+ return status;
+}
+
+
+/*
+ samr_OpenGroup
+*/
+static NTSTATUS dcesrv_samr_OpenGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_OpenGroup *r)
+{
+ struct samr_domain_state *d_state;
+ struct samr_account_state *a_state;
+ struct dcesrv_handle *h;
+ const char *groupname;
+ struct dom_sid *sid;
+ struct ldb_message **msgs;
+ struct dcesrv_handle *g_handle;
+ const char * const attrs[2] = { "sAMAccountName", NULL };
+ int ret;
+
+ ZERO_STRUCTP(r->out.group_handle);
+
+ DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
+
+ d_state = h->data;
+
+ /* form the group SID */
+ sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
+ if (!sid) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* search for the group record */
+ ret = gendb_search(d_state->sam_ctx,
+ mem_ctx, d_state->domain_dn, &msgs, attrs,
+ "(&(objectSid=%s)(objectclass=group)"
+ "(grouptype=%d))",
+ ldap_encode_ndr_dom_sid(mem_ctx, sid),
+ GTYPE_SECURITY_GLOBAL_GROUP);
+ if (ret == 0) {
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+ if (ret != 1) {
+ DEBUG(0,("Found %d records matching sid %s\n",
+ ret, dom_sid_string(mem_ctx, sid)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ groupname = samdb_result_string(msgs[0], "sAMAccountName", NULL);
+ if (groupname == NULL) {
+ DEBUG(0,("sAMAccountName field missing for sid %s\n",
+ dom_sid_string(mem_ctx, sid)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ a_state = talloc(d_state, struct samr_account_state);
+ if (!a_state) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ a_state->sam_ctx = d_state->sam_ctx;
+ a_state->access_mask = r->in.access_mask;
+ a_state->domain_state = talloc_reference(a_state, d_state);
+ a_state->account_dn = talloc_steal(a_state, msgs[0]->dn);
+ a_state->account_sid = talloc_steal(a_state, sid);
+ a_state->account_name = talloc_strdup(a_state, groupname);
+ if (!a_state->account_name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* create the policy handle */
+ g_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_GROUP);
+ if (!g_handle) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ g_handle->data = talloc_steal(g_handle, a_state);
+
+ *r->out.group_handle = g_handle->wire_handle;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ samr_QueryGroupInfo
+*/
+static NTSTATUS dcesrv_samr_QueryGroupInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_QueryGroupInfo *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_account_state *a_state;
+ struct ldb_message *msg;
+ struct ldb_result *res;
+ const char * const attrs[4] = { "sAMAccountName", "description",
+ "numMembers", NULL };
+ int ret;
+ union samr_GroupInfo *info;
+
+ *r->out.info = NULL;
+
+ DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
+
+ a_state = h->data;
+
+ ret = ldb_search(a_state->sam_ctx, mem_ctx, &res, a_state->account_dn, LDB_SCOPE_SUBTREE, attrs, "objectClass=*");
+
+ if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+ return NT_STATUS_NO_SUCH_GROUP;
+ } else if (ret != LDB_SUCCESS) {
+ DEBUG(2, ("Error reading group info: %s\n", ldb_errstring(a_state->sam_ctx)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ if (res->count != 1) {
+ DEBUG(2, ("Error finding group info, got %d entries\n", res->count));
+
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ msg = res->msgs[0];
+
+ /* allocate the info structure */
+ info = talloc_zero(mem_ctx, union samr_GroupInfo);
+ if (info == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* Fill in the level */
+ switch (r->in.level) {
+ case GROUPINFOALL:
+ QUERY_STRING(msg, all.name, "sAMAccountName");
+ info->all.attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED; /* Do like w2k3 */
+ QUERY_UINT (msg, all.num_members, "numMembers")
+ QUERY_STRING(msg, all.description, "description");
+ break;
+ case GROUPINFONAME:
+ QUERY_STRING(msg, name, "sAMAccountName");
+ break;
+ case GROUPINFOATTRIBUTES:
+ info->attributes.attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED; /* Do like w2k3 */
+ break;
+ case GROUPINFODESCRIPTION:
+ QUERY_STRING(msg, description, "description");
+ break;
+ case GROUPINFOALL2:
+ QUERY_STRING(msg, all2.name, "sAMAccountName");
+ info->all.attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED; /* Do like w2k3 */
+ QUERY_UINT (msg, all2.num_members, "numMembers")
+ QUERY_STRING(msg, all2.description, "description");
+ break;
+ default:
+ talloc_free(info);
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ *r->out.info = info;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_SetGroupInfo
+*/
+static NTSTATUS dcesrv_samr_SetGroupInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_SetGroupInfo *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_account_state *g_state;
+ struct ldb_message *msg;
+ struct ldb_context *sam_ctx;
+ int ret;
+
+ DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
+
+ g_state = h->data;
+ sam_ctx = g_state->sam_ctx;
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ msg->dn = ldb_dn_copy(mem_ctx, g_state->account_dn);
+ if (!msg->dn) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ switch (r->in.level) {
+ case GROUPINFODESCRIPTION:
+ SET_STRING(msg, description, "description");
+ break;
+ case GROUPINFONAME:
+ /* On W2k3 this does not change the name, it changes the
+ * sAMAccountName attribute */
+ SET_STRING(msg, name, "sAMAccountName");
+ break;
+ case GROUPINFOATTRIBUTES:
+ /* This does not do anything obviously visible in W2k3 LDAP */
+ return NT_STATUS_OK;
+ default:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ /* modify the samdb record */
+ ret = ldb_modify(g_state->sam_ctx, msg);
+ if (ret != 0) {
+ /* we really need samdb.c to return NTSTATUS */
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_AddGroupMember
+*/
+static NTSTATUS dcesrv_samr_AddGroupMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_AddGroupMember *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_account_state *a_state;
+ struct samr_domain_state *d_state;
+ struct ldb_message *mod;
+ struct dom_sid *membersid;
+ const char *memberdn;
+ struct ldb_result *res;
+ const char * const attrs[] = { NULL };
+ int ret;
+
+ DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
+
+ a_state = h->data;
+ d_state = a_state->domain_state;
+
+ membersid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
+ if (membersid == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ /* In native mode, AD can also nest domain groups. Not sure yet
+ * whether this is also available via RPC. */
+ ret = ldb_search(d_state->sam_ctx, mem_ctx, &res,
+ d_state->domain_dn, LDB_SCOPE_SUBTREE, attrs,
+ "(&(objectSid=%s)(objectclass=user))",
+ ldap_encode_ndr_dom_sid(mem_ctx, membersid));
+
+ if (ret != 0) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ if (res->count == 0) {
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ if (res->count > 1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ memberdn = ldb_dn_alloc_linearized(mem_ctx, res->msgs[0]->dn);
+
+ if (memberdn == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ mod = ldb_msg_new(mem_ctx);
+ if (mod == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
+
+ if (samdb_msg_add_addval(d_state->sam_ctx, mem_ctx, mod, "member",
+ memberdn) != 0)
+ return NT_STATUS_UNSUCCESSFUL;
+
+ ret = ldb_modify(a_state->sam_ctx, mod);
+ switch (ret) {
+ case LDB_SUCCESS:
+ return NT_STATUS_OK;
+ case LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS:
+ return NT_STATUS_MEMBER_IN_GROUP;
+ case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
+ return NT_STATUS_ACCESS_DENIED;
+ default:
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+}
+
+
+/*
+ samr_DeleteDomainGroup
+*/
+static NTSTATUS dcesrv_samr_DeleteDomainGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_DeleteDomainGroup *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_account_state *a_state;
+ int ret;
+
+ *r->out.group_handle = *r->in.group_handle;
+
+ DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
+
+ a_state = h->data;
+
+ ret = ldb_delete(a_state->sam_ctx, a_state->account_dn);
+ if (ret != 0) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ ZERO_STRUCTP(r->out.group_handle);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_DeleteGroupMember
+*/
+static NTSTATUS dcesrv_samr_DeleteGroupMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_DeleteGroupMember *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_account_state *a_state;
+ struct samr_domain_state *d_state;
+ struct ldb_message *mod;
+ struct dom_sid *membersid;
+ const char *memberdn;
+ struct ldb_result *res;
+ const char * const attrs[] = { NULL };
+ int ret;
+
+ DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
+
+ a_state = h->data;
+ d_state = a_state->domain_state;
+
+ membersid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
+ if (membersid == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ /* In native mode, AD can also nest domain groups. Not sure yet
+ * whether this is also available via RPC. */
+ ret = ldb_search(d_state->sam_ctx, mem_ctx, &res,
+ d_state->domain_dn, LDB_SCOPE_SUBTREE, attrs,
+ "(&(objectSid=%s)(objectclass=user))",
+ ldap_encode_ndr_dom_sid(mem_ctx, membersid));
+
+ if (ret != 0) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ if (res->count == 0) {
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ if (res->count > 1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ memberdn = ldb_dn_alloc_linearized(mem_ctx, res->msgs[0]->dn);
+
+ if (memberdn == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ mod = ldb_msg_new(mem_ctx);
+ if (mod == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
+
+ if (samdb_msg_add_delval(d_state->sam_ctx, mem_ctx, mod, "member",
+ memberdn) != 0) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = ldb_modify(a_state->sam_ctx, mod);
+ switch (ret) {
+ case LDB_SUCCESS:
+ return NT_STATUS_OK;
+ case LDB_ERR_NO_SUCH_ATTRIBUTE:
+ return NT_STATUS_MEMBER_NOT_IN_GROUP;
+ case LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS:
+ return NT_STATUS_ACCESS_DENIED;
+ default:
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+}
+
+
+/*
+ samr_QueryGroupMember
+*/
+static NTSTATUS dcesrv_samr_QueryGroupMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_QueryGroupMember *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_account_state *a_state;
+ struct ldb_message **res;
+ struct ldb_message_element *el;
+ struct samr_RidTypeArray *array;
+ const char * const attrs[2] = { "member", NULL };
+ int ret;
+
+ DCESRV_PULL_HANDLE(h, r->in.group_handle, SAMR_HANDLE_GROUP);
+
+ a_state = h->data;
+
+ /* pull the member attribute */
+ ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
+ a_state->account_dn, &res, attrs);
+
+ if (ret != 1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ array = talloc(mem_ctx, struct samr_RidTypeArray);
+
+ if (array == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ ZERO_STRUCTP(array);
+
+ el = ldb_msg_find_element(res[0], "member");
+
+ if (el != NULL) {
+ int i;
+
+ array->count = el->num_values;
+
+ array->rids = talloc_array(mem_ctx, uint32_t,
+ el->num_values);
+ if (array->rids == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ array->types = talloc_array(mem_ctx, uint32_t,
+ el->num_values);
+ if (array->types == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ for (i=0; i<el->num_values; i++) {
+ struct ldb_message **res2;
+ const char * const attrs2[2] = { "objectSid", NULL };
+ ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
+ ldb_dn_from_ldb_val(mem_ctx, a_state->sam_ctx, &el->values[i]),
+ &res2, attrs2);
+ if (ret != 1)
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+
+ array->rids[i] =
+ samdb_result_rid_from_sid(mem_ctx, res2[0],
+ "objectSid", 0);
+
+ if (array->rids[i] == 0)
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+
+ array->types[i] = 7; /* RID type of some kind, not sure what the value means. */
+ }
+ }
+
+ *r->out.rids = array;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_SetMemberAttributesOfGroup
+*/
+static NTSTATUS dcesrv_samr_SetMemberAttributesOfGroup(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_SetMemberAttributesOfGroup *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ samr_OpenAlias
+*/
+static NTSTATUS dcesrv_samr_OpenAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_OpenAlias *r)
+{
+ struct samr_domain_state *d_state;
+ struct samr_account_state *a_state;
+ struct dcesrv_handle *h;
+ const char *alias_name;
+ struct dom_sid *sid;
+ struct ldb_message **msgs;
+ struct dcesrv_handle *g_handle;
+ const char * const attrs[2] = { "sAMAccountName", NULL };
+ int ret;
+
+ ZERO_STRUCTP(r->out.alias_handle);
+
+ DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
+
+ d_state = h->data;
+
+ /* form the alias SID */
+ sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
+ if (sid == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ /* search for the group record */
+ ret = gendb_search(d_state->sam_ctx,
+ mem_ctx, d_state->domain_dn, &msgs, attrs,
+ "(&(objectSid=%s)(objectclass=group)"
+ "(|(grouptype=%d)(grouptype=%d)))",
+ ldap_encode_ndr_dom_sid(mem_ctx, sid),
+ GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
+ GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
+ if (ret == 0) {
+ return NT_STATUS_NO_SUCH_ALIAS;
+ }
+ if (ret != 1) {
+ DEBUG(0,("Found %d records matching sid %s\n",
+ ret, dom_sid_string(mem_ctx, sid)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ alias_name = samdb_result_string(msgs[0], "sAMAccountName", NULL);
+ if (alias_name == NULL) {
+ DEBUG(0,("sAMAccountName field missing for sid %s\n",
+ dom_sid_string(mem_ctx, sid)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ a_state = talloc(d_state, struct samr_account_state);
+ if (!a_state) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ a_state->sam_ctx = d_state->sam_ctx;
+ a_state->access_mask = r->in.access_mask;
+ a_state->domain_state = talloc_reference(a_state, d_state);
+ a_state->account_dn = talloc_steal(a_state, msgs[0]->dn);
+ a_state->account_sid = talloc_steal(a_state, sid);
+ a_state->account_name = talloc_strdup(a_state, alias_name);
+ if (!a_state->account_name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* create the policy handle */
+ g_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_ALIAS);
+ if (!g_handle) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ g_handle->data = talloc_steal(g_handle, a_state);
+
+ *r->out.alias_handle = g_handle->wire_handle;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_QueryAliasInfo
+*/
+static NTSTATUS dcesrv_samr_QueryAliasInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_QueryAliasInfo *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_account_state *a_state;
+ struct ldb_message *msg, **res;
+ const char * const attrs[4] = { "sAMAccountName", "description",
+ "numMembers", NULL };
+ int ret;
+ union samr_AliasInfo *info;
+
+ *r->out.info = NULL;
+
+ DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
+
+ a_state = h->data;
+
+ /* pull all the alias attributes */
+ ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
+ a_state->account_dn ,&res, attrs);
+ if (ret != 1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ msg = res[0];
+
+ /* allocate the info structure */
+ info = talloc_zero(mem_ctx, union samr_AliasInfo);
+ if (info == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ switch(r->in.level) {
+ case ALIASINFOALL:
+ QUERY_STRING(msg, all.name, "sAMAccountName");
+ QUERY_UINT (msg, all.num_members, "numMembers");
+ QUERY_STRING(msg, all.description, "description");
+ break;
+ case ALIASINFONAME:
+ QUERY_STRING(msg, name, "sAMAccountName");
+ break;
+ case ALIASINFODESCRIPTION:
+ QUERY_STRING(msg, description, "description");
+ break;
+ default:
+ talloc_free(info);
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ *r->out.info = info;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_SetAliasInfo
+*/
+static NTSTATUS dcesrv_samr_SetAliasInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_SetAliasInfo *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_account_state *a_state;
+ struct ldb_message *msg;
+ struct ldb_context *sam_ctx;
+ int ret;
+
+ DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
+
+ a_state = h->data;
+ sam_ctx = a_state->sam_ctx;
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ msg->dn = ldb_dn_copy(mem_ctx, a_state->account_dn);
+ if (!msg->dn) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ switch (r->in.level) {
+ case ALIASINFODESCRIPTION:
+ SET_STRING(msg, description, "description");
+ break;
+ case ALIASINFONAME:
+ /* On W2k3 this does not change the name, it changes the
+ * sAMAccountName attribute */
+ SET_STRING(msg, name, "sAMAccountName");
+ break;
+ default:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ /* modify the samdb record */
+ ret = ldb_modify(a_state->sam_ctx, msg);
+ if (ret != 0) {
+ /* we really need samdb.c to return NTSTATUS */
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_DeleteDomAlias
+*/
+static NTSTATUS dcesrv_samr_DeleteDomAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_DeleteDomAlias *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_account_state *a_state;
+ int ret;
+
+ *r->out.alias_handle = *r->in.alias_handle;
+
+ DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
+
+ a_state = h->data;
+
+ ret = ldb_delete(a_state->sam_ctx, a_state->account_dn);
+ if (ret != 0) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ ZERO_STRUCTP(r->out.alias_handle);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_AddAliasMember
+*/
+static NTSTATUS dcesrv_samr_AddAliasMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_AddAliasMember *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_account_state *a_state;
+ struct samr_domain_state *d_state;
+ struct ldb_message *mod;
+ struct ldb_message **msgs;
+ const char * const attrs[] = { NULL };
+ struct ldb_dn *memberdn = NULL;
+ int ret;
+ NTSTATUS status;
+
+ DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
+
+ a_state = h->data;
+ d_state = a_state->domain_state;
+
+ ret = gendb_search(d_state->sam_ctx, mem_ctx, NULL,
+ &msgs, attrs, "(objectsid=%s)",
+ ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
+
+ if (ret == 1) {
+ memberdn = msgs[0]->dn;
+ } else if (ret > 1) {
+ DEBUG(0,("Found %d records matching sid %s\n",
+ ret, dom_sid_string(mem_ctx, r->in.sid)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ } else if (ret == 0) {
+ status = samdb_create_foreign_security_principal(d_state->sam_ctx, mem_ctx,
+ r->in.sid, &memberdn);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ } else {
+ DEBUG(0, ("samdb_search returned %d: %s\n", ret, ldb_errstring(d_state->sam_ctx)));
+ }
+
+ if (memberdn == NULL) {
+ DEBUG(0, ("Could not find memberdn\n"));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ mod = ldb_msg_new(mem_ctx);
+ if (mod == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
+
+ if (samdb_msg_add_addval(d_state->sam_ctx, mem_ctx, mod, "member",
+ ldb_dn_alloc_linearized(mem_ctx, memberdn)) != 0)
+ return NT_STATUS_UNSUCCESSFUL;
+
+ if (ldb_modify(a_state->sam_ctx, mod) != 0)
+ return NT_STATUS_UNSUCCESSFUL;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_DeleteAliasMember
+*/
+static NTSTATUS dcesrv_samr_DeleteAliasMember(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_DeleteAliasMember *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_account_state *a_state;
+ struct samr_domain_state *d_state;
+ struct ldb_message *mod;
+ const char *memberdn;
+
+ DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
+
+ a_state = h->data;
+ d_state = a_state->domain_state;
+
+ memberdn = samdb_search_string(d_state->sam_ctx, mem_ctx, NULL,
+ "distinguishedName", "(objectSid=%s)",
+ ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
+
+ if (memberdn == NULL)
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+ mod = ldb_msg_new(mem_ctx);
+ if (mod == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ mod->dn = talloc_reference(mem_ctx, a_state->account_dn);
+
+ if (samdb_msg_add_delval(d_state->sam_ctx, mem_ctx, mod, "member",
+ memberdn) != 0)
+ return NT_STATUS_UNSUCCESSFUL;
+
+ if (ldb_modify(a_state->sam_ctx, mod) != 0)
+ return NT_STATUS_UNSUCCESSFUL;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_GetMembersInAlias
+*/
+static NTSTATUS dcesrv_samr_GetMembersInAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_GetMembersInAlias *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_account_state *a_state;
+ struct samr_domain_state *d_state;
+ struct ldb_message **msgs;
+ struct lsa_SidPtr *sids;
+ struct ldb_message_element *el;
+ const char * const attrs[2] = { "member", NULL};
+ int ret;
+
+ DCESRV_PULL_HANDLE(h, r->in.alias_handle, SAMR_HANDLE_ALIAS);
+
+ a_state = h->data;
+ d_state = a_state->domain_state;
+
+ ret = gendb_search_dn(d_state->sam_ctx, mem_ctx,
+ a_state->account_dn, &msgs, attrs);
+
+ if (ret == -1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ } else if (ret == 0) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ } else if (ret != 1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ r->out.sids->num_sids = 0;
+ r->out.sids->sids = NULL;
+
+ el = ldb_msg_find_element(msgs[0], "member");
+
+ if (el != NULL) {
+ int i;
+
+ sids = talloc_array(mem_ctx, struct lsa_SidPtr,
+ el->num_values);
+
+ if (sids == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ for (i=0; i<el->num_values; i++) {
+ struct ldb_message **msgs2;
+ const char * const attrs2[2] = { "objectSid", NULL };
+ ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
+ ldb_dn_from_ldb_val(mem_ctx, a_state->sam_ctx, &el->values[i]),
+ &msgs2, attrs2);
+ if (ret != 1)
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+
+ sids[i].sid = samdb_result_dom_sid(mem_ctx, msgs2[0],
+ "objectSid");
+
+ if (sids[i].sid == NULL)
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ r->out.sids->num_sids = el->num_values;
+ r->out.sids->sids = sids;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ samr_OpenUser
+*/
+static NTSTATUS dcesrv_samr_OpenUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_OpenUser *r)
+{
+ struct samr_domain_state *d_state;
+ struct samr_account_state *a_state;
+ struct dcesrv_handle *h;
+ const char *account_name;
+ struct dom_sid *sid;
+ struct ldb_message **msgs;
+ struct dcesrv_handle *u_handle;
+ const char * const attrs[2] = { "sAMAccountName", NULL };
+ int ret;
+
+ ZERO_STRUCTP(r->out.user_handle);
+
+ DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
+
+ d_state = h->data;
+
+ /* form the users SID */
+ sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
+ if (!sid) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* search for the user record */
+ ret = gendb_search(d_state->sam_ctx,
+ mem_ctx, d_state->domain_dn, &msgs, attrs,
+ "(&(objectSid=%s)(objectclass=user))",
+ ldap_encode_ndr_dom_sid(mem_ctx, sid));
+ if (ret == 0) {
+ return NT_STATUS_NO_SUCH_USER;
+ }
+ if (ret != 1) {
+ DEBUG(0,("Found %d records matching sid %s\n", ret,
+ dom_sid_string(mem_ctx, sid)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ account_name = samdb_result_string(msgs[0], "sAMAccountName", NULL);
+ if (account_name == NULL) {
+ DEBUG(0,("sAMAccountName field missing for sid %s\n",
+ dom_sid_string(mem_ctx, sid)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ a_state = talloc(mem_ctx, struct samr_account_state);
+ if (!a_state) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ a_state->sam_ctx = d_state->sam_ctx;
+ a_state->access_mask = r->in.access_mask;
+ a_state->domain_state = talloc_reference(a_state, d_state);
+ a_state->account_dn = talloc_steal(a_state, msgs[0]->dn);
+ a_state->account_sid = talloc_steal(a_state, sid);
+ a_state->account_name = talloc_strdup(a_state, account_name);
+ if (!a_state->account_name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* create the policy handle */
+ u_handle = dcesrv_handle_new(dce_call->context, SAMR_HANDLE_USER);
+ if (!u_handle) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ u_handle->data = talloc_steal(u_handle, a_state);
+
+ *r->out.user_handle = u_handle->wire_handle;
+
+ return NT_STATUS_OK;
+
+}
+
+
+/*
+ samr_DeleteUser
+*/
+static NTSTATUS dcesrv_samr_DeleteUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_DeleteUser *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_account_state *a_state;
+ int ret;
+
+ *r->out.user_handle = *r->in.user_handle;
+
+ DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
+
+ a_state = h->data;
+
+ ret = ldb_delete(a_state->sam_ctx, a_state->account_dn);
+ if (ret != 0) {
+ DEBUG(1, ("Failed to delete user: %s: %s\n",
+ ldb_dn_get_linearized(a_state->account_dn),
+ ldb_errstring(a_state->sam_ctx)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ ZERO_STRUCTP(r->out.user_handle);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_QueryUserInfo
+*/
+static NTSTATUS dcesrv_samr_QueryUserInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_QueryUserInfo *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_account_state *a_state;
+ struct ldb_message *msg, **res;
+ int ret;
+ struct ldb_context *sam_ctx;
+
+ const char * const *attrs = NULL;
+ union samr_UserInfo *info;
+
+ *r->out.info = NULL;
+
+ DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
+
+ a_state = h->data;
+ sam_ctx = a_state->sam_ctx;
+
+ /* fill in the reply */
+ switch (r->in.level) {
+ case 1:
+ {
+ static const char * const attrs2[] = {"sAMAccountName", "displayName",
+ "primaryroupID", "description",
+ "comment", NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 2:
+ {
+ static const char * const attrs2[] = {"comment", "countryCode", "codePage", NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 3:
+ {
+ static const char * const attrs2[] = {"sAMAccountName",
+ "displayName",
+ "objectSid",
+ "primaryGroupID",
+ "homeDirectory",
+ "homeDrive",
+ "scriptPath",
+ "profilePath",
+ "userWorkstations",
+ "lastLogon",
+ "lastLogoff",
+ "pwdLastSet",
+ "logonHours",
+ "badPwdCount",
+ "logonCount",
+ "userAccountControl", NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 4:
+ {
+ static const char * const attrs2[] = {"logonHours", NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 5:
+ {
+ static const char * const attrs2[] = {"sAMAccountName",
+ "displayName",
+ "objectSid",
+ "primaryGroupID",
+ "homeDirectory",
+ "homeDrive",
+ "scriptPath",
+ "profilePath",
+ "description",
+ "userWorkstations",
+ "lastLogon",
+ "lastLogoff",
+ "logonHours",
+ "badPwdCount",
+ "logonCount",
+ "pwdLastSet",
+ "accountExpires",
+ "userAccountControl",
+ NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 6:
+ {
+ static const char * const attrs2[] = {"sAMAccountName", "displayName", NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 7:
+ {
+ static const char * const attrs2[] = {"sAMAccountName", NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 8:
+ {
+ static const char * const attrs2[] = {"displayName", NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 9:
+ {
+ static const char * const attrs2[] = {"primaryGroupID", NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 10:
+ {
+ static const char * const attrs2[] = {"homeDirectory", "homeDrive", NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 11:
+ {
+ static const char * const attrs2[] = {"scriptPath", NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 12:
+ {
+ static const char * const attrs2[] = {"profilePath", NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 13:
+ {
+ static const char * const attrs2[] = {"description", NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 14:
+ {
+ static const char * const attrs2[] = {"userWorkstations", NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 16:
+ {
+ static const char * const attrs2[] = {"userAccountControl", "pwdLastSet", NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 17:
+ {
+ static const char * const attrs2[] = {"accountExpires", NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 20:
+ {
+ static const char * const attrs2[] = {"userParameters", NULL};
+ attrs = attrs2;
+ break;
+ }
+ case 21:
+ {
+ static const char * const attrs2[] = {"lastLogon",
+ "lastLogoff",
+ "pwdLastSet",
+ "accountExpires",
+ "sAMAccountName",
+ "displayName",
+ "homeDirectory",
+ "homeDrive",
+ "scriptPath",
+ "profilePath",
+ "description",
+ "userWorkstations",
+ "comment",
+ "userParameters",
+ "objectSid",
+ "primaryGroupID",
+ "userAccountControl",
+ "logonHours",
+ "badPwdCount",
+ "logonCount",
+ "countryCode",
+ "codePage",
+ NULL};
+ attrs = attrs2;
+ break;
+ }
+ }
+
+ /* pull all the user attributes */
+ ret = gendb_search_dn(a_state->sam_ctx, mem_ctx,
+ a_state->account_dn ,&res, attrs);
+ if (ret != 1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ msg = res[0];
+
+ /* allocate the info structure */
+ info = talloc_zero(mem_ctx, union samr_UserInfo);
+ if (info == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* fill in the reply */
+ switch (r->in.level) {
+ case 1:
+ QUERY_STRING(msg, info1.account_name, "sAMAccountName");
+ QUERY_STRING(msg, info1.full_name, "displayName");
+ QUERY_UINT (msg, info1.primary_gid, "primaryGroupID");
+ QUERY_STRING(msg, info1.description, "description");
+ QUERY_STRING(msg, info1.comment, "comment");
+ break;
+
+ case 2:
+ QUERY_STRING(msg, info2.comment, "comment");
+ QUERY_UINT (msg, info2.country_code, "countryCode");
+ QUERY_UINT (msg, info2.code_page, "codePage");
+ break;
+
+ case 3:
+ QUERY_STRING(msg, info3.account_name, "sAMAccountName");
+ QUERY_STRING(msg, info3.full_name, "displayName");
+ QUERY_RID (msg, info3.rid, "objectSid");
+ QUERY_UINT (msg, info3.primary_gid, "primaryGroupID");
+ QUERY_STRING(msg, info3.home_directory, "homeDirectory");
+ QUERY_STRING(msg, info3.home_drive, "homeDrive");
+ QUERY_STRING(msg, info3.logon_script, "scriptPath");
+ QUERY_STRING(msg, info3.profile_path, "profilePath");
+ QUERY_STRING(msg, info3.workstations, "userWorkstations");
+ QUERY_UINT64(msg, info3.last_logon, "lastLogon");
+ QUERY_UINT64(msg, info3.last_logoff, "lastLogoff");
+ QUERY_UINT64(msg, info3.last_password_change, "pwdLastSet");
+ QUERY_APASSC(msg, info3.allow_password_change, "pwdLastSet");
+ QUERY_FPASSC(msg, info3.force_password_change, "pwdLastSet");
+ QUERY_LHOURS(msg, info3.logon_hours, "logonHours");
+ QUERY_UINT (msg, info3.bad_password_count, "badPwdCount");
+ QUERY_UINT (msg, info3.logon_count, "logonCount");
+ QUERY_AFLAGS(msg, info3.acct_flags, "userAccountControl");
+ break;
+
+ case 4:
+ QUERY_LHOURS(msg, info4.logon_hours, "logonHours");
+ break;
+
+ case 5:
+ QUERY_STRING(msg, info5.account_name, "sAMAccountName");
+ QUERY_STRING(msg, info5.full_name, "displayName");
+ QUERY_RID (msg, info5.rid, "objectSid");
+ QUERY_UINT (msg, info5.primary_gid, "primaryGroupID");
+ QUERY_STRING(msg, info5.home_directory, "homeDirectory");
+ QUERY_STRING(msg, info5.home_drive, "homeDrive");
+ QUERY_STRING(msg, info5.logon_script, "scriptPath");
+ QUERY_STRING(msg, info5.profile_path, "profilePath");
+ QUERY_STRING(msg, info5.description, "description");
+ QUERY_STRING(msg, info5.workstations, "userWorkstations");
+ QUERY_UINT64(msg, info5.last_logon, "lastLogon");
+ QUERY_UINT64(msg, info5.last_logoff, "lastLogoff");
+ QUERY_LHOURS(msg, info5.logon_hours, "logonHours");
+ QUERY_UINT (msg, info5.bad_password_count, "badPwdCount");
+ QUERY_UINT (msg, info5.logon_count, "logonCount");
+ QUERY_UINT64(msg, info5.last_password_change, "pwdLastSet");
+ QUERY_UINT64(msg, info5.acct_expiry, "accountExpires");
+ QUERY_AFLAGS(msg, info5.acct_flags, "userAccountControl");
+ break;
+
+ case 6:
+ QUERY_STRING(msg, info6.account_name, "sAMAccountName");
+ QUERY_STRING(msg, info6.full_name, "displayName");
+ break;
+
+ case 7:
+ QUERY_STRING(msg, info7.account_name, "sAMAccountName");
+ break;
+
+ case 8:
+ QUERY_STRING(msg, info8.full_name, "displayName");
+ break;
+
+ case 9:
+ QUERY_UINT (msg, info9.primary_gid, "primaryGroupID");
+ break;
+
+ case 10:
+ QUERY_STRING(msg, info10.home_directory,"homeDirectory");
+ QUERY_STRING(msg, info10.home_drive, "homeDrive");
+ break;
+
+ case 11:
+ QUERY_STRING(msg, info11.logon_script, "scriptPath");
+ break;
+
+ case 12:
+ QUERY_STRING(msg, info12.profile_path, "profilePath");
+ break;
+
+ case 13:
+ QUERY_STRING(msg, info13.description, "description");
+ break;
+
+ case 14:
+ QUERY_STRING(msg, info14.workstations, "userWorkstations");
+ break;
+
+ case 16:
+ QUERY_AFLAGS(msg, info16.acct_flags, "userAccountControl");
+ break;
+
+ case 17:
+ QUERY_UINT64(msg, info17.acct_expiry, "accountExpires");
+ break;
+
+ case 20:
+ QUERY_PARAMETERS(msg, info20.parameters, "userParameters");
+ break;
+
+ case 21:
+ QUERY_UINT64(msg, info21.last_logon, "lastLogon");
+ QUERY_UINT64(msg, info21.last_logoff, "lastLogoff");
+ QUERY_UINT64(msg, info21.last_password_change, "pwdLastSet");
+ QUERY_UINT64(msg, info21.acct_expiry, "accountExpires");
+ QUERY_APASSC(msg, info21.allow_password_change,"pwdLastSet");
+ QUERY_FPASSC(msg, info21.force_password_change,"pwdLastSet");
+ QUERY_STRING(msg, info21.account_name, "sAMAccountName");
+ QUERY_STRING(msg, info21.full_name, "displayName");
+ QUERY_STRING(msg, info21.home_directory, "homeDirectory");
+ QUERY_STRING(msg, info21.home_drive, "homeDrive");
+ QUERY_STRING(msg, info21.logon_script, "scriptPath");
+ QUERY_STRING(msg, info21.profile_path, "profilePath");
+ QUERY_STRING(msg, info21.description, "description");
+ QUERY_STRING(msg, info21.workstations, "userWorkstations");
+ QUERY_STRING(msg, info21.comment, "comment");
+ QUERY_PARAMETERS(msg, info21.parameters, "userParameters");
+ QUERY_RID (msg, info21.rid, "objectSid");
+ QUERY_UINT (msg, info21.primary_gid, "primaryGroupID");
+ QUERY_AFLAGS(msg, info21.acct_flags, "userAccountControl");
+ info->info21.fields_present = 0x00FFFFFF;
+ QUERY_LHOURS(msg, info21.logon_hours, "logonHours");
+ QUERY_UINT (msg, info21.bad_password_count, "badPwdCount");
+ QUERY_UINT (msg, info21.logon_count, "logonCount");
+ QUERY_UINT (msg, info21.country_code, "countryCode");
+ QUERY_UINT (msg, info21.code_page, "codePage");
+ break;
+
+
+ default:
+ talloc_free(info);
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ *r->out.info = info;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_SetUserInfo
+*/
+static NTSTATUS dcesrv_samr_SetUserInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_SetUserInfo *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_account_state *a_state;
+ struct ldb_message *msg;
+ int ret;
+ NTSTATUS status = NT_STATUS_OK;
+ struct ldb_context *sam_ctx;
+
+ DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
+
+ a_state = h->data;
+ sam_ctx = a_state->sam_ctx;
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ msg->dn = talloc_reference(mem_ctx, a_state->account_dn);
+ if (!msg->dn) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ switch (r->in.level) {
+ case 2:
+ SET_STRING(msg, info2.comment, "comment");
+ SET_UINT (msg, info2.country_code, "countryCode");
+ SET_UINT (msg, info2.code_page, "codePage");
+ break;
+
+ case 4:
+ SET_LHOURS(msg, info4.logon_hours, "logonHours");
+ break;
+
+ case 6:
+ SET_STRING(msg, info6.full_name, "displayName");
+ break;
+
+ case 7:
+ SET_STRING(msg, info7.account_name, "samAccountName");
+ break;
+
+ case 8:
+ SET_STRING(msg, info8.full_name, "displayName");
+ break;
+
+ case 9:
+ SET_UINT(msg, info9.primary_gid, "primaryGroupID");
+ break;
+
+ case 10:
+ SET_STRING(msg, info10.home_directory, "homeDirectory");
+ SET_STRING(msg, info10.home_drive, "homeDrive");
+ break;
+
+ case 11:
+ SET_STRING(msg, info11.logon_script, "scriptPath");
+ break;
+
+ case 12:
+ SET_STRING(msg, info12.profile_path, "profilePath");
+ break;
+
+ case 13:
+ SET_STRING(msg, info13.description, "description");
+ break;
+
+ case 14:
+ SET_STRING(msg, info14.workstations, "userWorkstations");
+ break;
+
+ case 16:
+ SET_AFLAGS(msg, info16.acct_flags, "userAccountControl");
+ break;
+
+ case 17:
+ SET_UINT64(msg, info17.acct_expiry, "accountExpires");
+ break;
+
+ case 20:
+ SET_PARAMETERS(msg, info20.parameters, "userParameters");
+ break;
+
+ case 21:
+#define IFSET(bit) if (bit & r->in.info->info21.fields_present)
+ IFSET(SAMR_FIELD_ACCT_EXPIRY)
+ SET_UINT64(msg, info21.acct_expiry, "accountExpires");
+ IFSET(SAMR_FIELD_ACCOUNT_NAME)
+ SET_STRING(msg, info21.account_name, "samAccountName");
+ IFSET(SAMR_FIELD_FULL_NAME)
+ SET_STRING(msg, info21.full_name, "displayName");
+ IFSET(SAMR_FIELD_DESCRIPTION)
+ SET_STRING(msg, info21.description, "description");
+ IFSET(SAMR_FIELD_COMMENT)
+ SET_STRING(msg, info21.comment, "comment");
+ IFSET(SAMR_FIELD_LOGON_SCRIPT)
+ SET_STRING(msg, info21.logon_script, "scriptPath");
+ IFSET(SAMR_FIELD_PROFILE_PATH)
+ SET_STRING(msg, info21.profile_path, "profilePath");
+ IFSET(SAMR_FIELD_HOME_DIRECTORY)
+ SET_STRING(msg, info21.home_directory, "homeDirectory");
+ IFSET(SAMR_FIELD_HOME_DRIVE)
+ SET_STRING(msg, info21.home_drive, "homeDrive");
+ IFSET(SAMR_FIELD_WORKSTATIONS)
+ SET_STRING(msg, info21.workstations, "userWorkstations");
+ IFSET(SAMR_FIELD_LOGON_HOURS)
+ SET_LHOURS(msg, info21.logon_hours, "logonHours");
+ IFSET(SAMR_FIELD_ACCT_FLAGS)
+ SET_AFLAGS(msg, info21.acct_flags, "userAccountControl");
+ IFSET(SAMR_FIELD_PARAMETERS)
+ SET_PARAMETERS(msg, info21.parameters, "userParameters");
+ IFSET(SAMR_FIELD_COUNTRY_CODE)
+ SET_UINT (msg, info21.country_code, "countryCode");
+ IFSET(SAMR_FIELD_CODE_PAGE)
+ SET_UINT (msg, info21.code_page, "codePage");
+#undef IFSET
+ break;
+
+ case 23:
+#define IFSET(bit) if (bit & r->in.info->info23.info.fields_present)
+ IFSET(SAMR_FIELD_ACCT_EXPIRY)
+ SET_UINT64(msg, info23.info.acct_expiry, "accountExpires");
+ IFSET(SAMR_FIELD_ACCOUNT_NAME)
+ SET_STRING(msg, info23.info.account_name, "samAccountName");
+ IFSET(SAMR_FIELD_FULL_NAME)
+ SET_STRING(msg, info23.info.full_name, "displayName");
+ IFSET(SAMR_FIELD_DESCRIPTION)
+ SET_STRING(msg, info23.info.description, "description");
+ IFSET(SAMR_FIELD_COMMENT)
+ SET_STRING(msg, info23.info.comment, "comment");
+ IFSET(SAMR_FIELD_LOGON_SCRIPT)
+ SET_STRING(msg, info23.info.logon_script, "scriptPath");
+ IFSET(SAMR_FIELD_PROFILE_PATH)
+ SET_STRING(msg, info23.info.profile_path, "profilePath");
+ IFSET(SAMR_FIELD_WORKSTATIONS)
+ SET_STRING(msg, info23.info.workstations, "userWorkstations");
+ IFSET(SAMR_FIELD_LOGON_HOURS)
+ SET_LHOURS(msg, info23.info.logon_hours, "logonHours");
+ IFSET(SAMR_FIELD_ACCT_FLAGS)
+ SET_AFLAGS(msg, info23.info.acct_flags, "userAccountControl");
+ IFSET(SAMR_FIELD_PARAMETERS)
+ SET_PARAMETERS(msg, info23.info.parameters, "userParameters");
+ IFSET(SAMR_FIELD_COUNTRY_CODE)
+ SET_UINT (msg, info23.info.country_code, "countryCode");
+ IFSET(SAMR_FIELD_CODE_PAGE)
+ SET_UINT (msg, info23.info.code_page, "codePage");
+ IFSET(SAMR_FIELD_NT_PASSWORD_PRESENT) {
+ status = samr_set_password(dce_call,
+ a_state->sam_ctx,
+ a_state->account_dn,
+ a_state->domain_state->domain_dn,
+ mem_ctx, msg,
+ &r->in.info->info23.password);
+ } else IFSET(SAMR_FIELD_LM_PASSWORD_PRESENT) {
+ status = samr_set_password(dce_call,
+ a_state->sam_ctx,
+ a_state->account_dn,
+ a_state->domain_state->domain_dn,
+ mem_ctx, msg,
+ &r->in.info->info23.password);
+ }
+#undef IFSET
+ break;
+
+ /* the set password levels are handled separately */
+ case 24:
+ status = samr_set_password(dce_call,
+ a_state->sam_ctx,
+ a_state->account_dn,
+ a_state->domain_state->domain_dn,
+ mem_ctx, msg,
+ &r->in.info->info24.password);
+ break;
+
+ case 25:
+#define IFSET(bit) if (bit & r->in.info->info25.info.fields_present)
+ IFSET(SAMR_FIELD_ACCT_EXPIRY)
+ SET_UINT64(msg, info25.info.acct_expiry, "accountExpires");
+ IFSET(SAMR_FIELD_ACCOUNT_NAME)
+ SET_STRING(msg, info25.info.account_name, "samAccountName");
+ IFSET(SAMR_FIELD_FULL_NAME)
+ SET_STRING(msg, info25.info.full_name, "displayName");
+ IFSET(SAMR_FIELD_DESCRIPTION)
+ SET_STRING(msg, info25.info.description, "description");
+ IFSET(SAMR_FIELD_COMMENT)
+ SET_STRING(msg, info25.info.comment, "comment");
+ IFSET(SAMR_FIELD_LOGON_SCRIPT)
+ SET_STRING(msg, info25.info.logon_script, "scriptPath");
+ IFSET(SAMR_FIELD_PROFILE_PATH)
+ SET_STRING(msg, info25.info.profile_path, "profilePath");
+ IFSET(SAMR_FIELD_WORKSTATIONS)
+ SET_STRING(msg, info25.info.workstations, "userWorkstations");
+ IFSET(SAMR_FIELD_LOGON_HOURS)
+ SET_LHOURS(msg, info25.info.logon_hours, "logonHours");
+ IFSET(SAMR_FIELD_ACCT_FLAGS)
+ SET_AFLAGS(msg, info25.info.acct_flags, "userAccountControl");
+ IFSET(SAMR_FIELD_PARAMETERS)
+ SET_PARAMETERS(msg, info25.info.parameters, "userParameters");
+ IFSET(SAMR_FIELD_COUNTRY_CODE)
+ SET_UINT (msg, info25.info.country_code, "countryCode");
+ IFSET(SAMR_FIELD_CODE_PAGE)
+ SET_UINT (msg, info25.info.code_page, "codePage");
+ IFSET(SAMR_FIELD_NT_PASSWORD_PRESENT) {
+ status = samr_set_password_ex(dce_call,
+ a_state->sam_ctx,
+ a_state->account_dn,
+ a_state->domain_state->domain_dn,
+ mem_ctx, msg,
+ &r->in.info->info25.password);
+ } else IFSET(SAMR_FIELD_LM_PASSWORD_PRESENT) {
+ status = samr_set_password_ex(dce_call,
+ a_state->sam_ctx,
+ a_state->account_dn,
+ a_state->domain_state->domain_dn,
+ mem_ctx, msg,
+ &r->in.info->info25.password);
+ }
+#undef IFSET
+ break;
+
+ /* the set password levels are handled separately */
+ case 26:
+ status = samr_set_password_ex(dce_call,
+ a_state->sam_ctx,
+ a_state->account_dn,
+ a_state->domain_state->domain_dn,
+ mem_ctx, msg,
+ &r->in.info->info26.password);
+ break;
+
+
+ default:
+ /* many info classes are not valid for SetUserInfo */
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* modify the samdb record */
+ ret = ldb_modify(a_state->sam_ctx, msg);
+ if (ret != 0) {
+ DEBUG(1,("Failed to modify record %s: %s\n",
+ ldb_dn_get_linearized(a_state->account_dn),
+ ldb_errstring(a_state->sam_ctx)));
+
+ /* we really need samdb.c to return NTSTATUS */
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_GetGroupsForUser
+*/
+static NTSTATUS dcesrv_samr_GetGroupsForUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_GetGroupsForUser *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_account_state *a_state;
+ struct samr_domain_state *d_state;
+ struct ldb_message **res;
+ const char * const attrs[2] = { "objectSid", NULL };
+ struct samr_RidWithAttributeArray *array;
+ int count;
+
+ DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
+
+ a_state = h->data;
+ d_state = a_state->domain_state;
+
+ count = samdb_search_domain(a_state->sam_ctx, mem_ctx, d_state->domain_dn, &res,
+ attrs, d_state->domain_sid,
+ "(&(member=%s)(grouptype=%d)(objectclass=group))",
+ ldb_dn_get_linearized(a_state->account_dn),
+ GTYPE_SECURITY_GLOBAL_GROUP);
+ if (count < 0)
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+
+ array = talloc(mem_ctx, struct samr_RidWithAttributeArray);
+ if (array == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ array->count = 0;
+ array->rids = NULL;
+
+ if (count > 0) {
+ int i;
+ array->rids = talloc_array(mem_ctx, struct samr_RidWithAttribute,
+ count);
+
+ if (array->rids == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ for (i=0; i<count; i++) {
+ struct dom_sid *group_sid;
+
+ group_sid = samdb_result_dom_sid(mem_ctx, res[i],
+ "objectSid");
+ if (group_sid == NULL) {
+ DEBUG(0, ("Couldn't find objectSid attrib\n"));
+ continue;
+ }
+
+ array->rids[array->count].rid =
+ group_sid->sub_auths[group_sid->num_auths-1];
+ array->rids[array->count].attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED;
+ array->count += 1;
+ }
+ }
+
+ *r->out.rids = array;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_QueryDisplayInfo
+*/
+static NTSTATUS dcesrv_samr_QueryDisplayInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_QueryDisplayInfo *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_domain_state *d_state;
+ struct ldb_message **res;
+ int ldb_cnt, count, i;
+ const char * const attrs[] = { "objectSid", "sAMAccountName", "displayName",
+ "description", "userAccountControl", "pwdLastSet", NULL };
+ struct samr_DispEntryFull *entriesFull = NULL;
+ struct samr_DispEntryFullGroup *entriesFullGroup = NULL;
+ struct samr_DispEntryAscii *entriesAscii = NULL;
+ struct samr_DispEntryGeneral * entriesGeneral = NULL;
+ const char *filter;
+
+ DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
+
+ d_state = h->data;
+
+ switch (r->in.level) {
+ case 1:
+ case 4:
+ filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)"
+ "(sAMAccountType=%u))",
+ ATYPE_NORMAL_ACCOUNT);
+ break;
+ case 2:
+ filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)"
+ "(sAMAccountType=%u))",
+ ATYPE_WORKSTATION_TRUST);
+ break;
+ case 3:
+ case 5:
+ filter = talloc_asprintf(mem_ctx, "(&(grouptype=%d)"
+ "(objectclass=group))",
+ GTYPE_SECURITY_GLOBAL_GROUP);
+ break;
+ default:
+ return NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ /* search for all requested objects in this domain. This could
+ possibly be cached and resumed based on resume_key */
+ ldb_cnt = samdb_search_domain(d_state->sam_ctx, mem_ctx,
+ d_state->domain_dn, &res, attrs,
+ d_state->domain_sid, "%s", filter);
+ if (ldb_cnt == -1) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ if (ldb_cnt == 0 || r->in.max_entries == 0) {
+ return NT_STATUS_OK;
+ }
+
+ switch (r->in.level) {
+ case 1:
+ entriesGeneral = talloc_array(mem_ctx,
+ struct samr_DispEntryGeneral,
+ ldb_cnt);
+ break;
+ case 2:
+ entriesFull = talloc_array(mem_ctx,
+ struct samr_DispEntryFull,
+ ldb_cnt);
+ break;
+ case 3:
+ entriesFullGroup = talloc_array(mem_ctx,
+ struct samr_DispEntryFullGroup,
+ ldb_cnt);
+ break;
+ case 4:
+ case 5:
+ entriesAscii = talloc_array(mem_ctx,
+ struct samr_DispEntryAscii,
+ ldb_cnt);
+ break;
+ }
+
+ if ((entriesGeneral == NULL) && (entriesFull == NULL) &&
+ (entriesAscii == NULL) && (entriesFullGroup == NULL))
+ return NT_STATUS_NO_MEMORY;
+
+ count = 0;
+
+ for (i=0; i<ldb_cnt; i++) {
+ struct dom_sid *objectsid;
+
+ objectsid = samdb_result_dom_sid(mem_ctx, res[i],
+ "objectSid");
+ if (objectsid == NULL)
+ continue;
+
+ switch(r->in.level) {
+ case 1:
+ entriesGeneral[count].idx = count + 1;
+ entriesGeneral[count].rid =
+ objectsid->sub_auths[objectsid->num_auths-1];
+ entriesGeneral[count].acct_flags =
+ samdb_result_acct_flags(d_state->sam_ctx, mem_ctx,
+ res[i],
+ d_state->domain_dn);
+ entriesGeneral[count].account_name.string =
+ samdb_result_string(res[i],
+ "sAMAccountName", "");
+ entriesGeneral[count].full_name.string =
+ samdb_result_string(res[i], "displayName", "");
+ entriesGeneral[count].description.string =
+ samdb_result_string(res[i], "description", "");
+ break;
+ case 2:
+ entriesFull[count].idx = count + 1;
+ entriesFull[count].rid =
+ objectsid->sub_auths[objectsid->num_auths-1];
+
+ /* No idea why we need to or in ACB_NORMAL here, but this is what Win2k3 seems to do... */
+ entriesFull[count].acct_flags =
+ samdb_result_acct_flags(d_state->sam_ctx, mem_ctx,
+ res[i],
+ d_state->domain_dn) | ACB_NORMAL;
+ entriesFull[count].account_name.string =
+ samdb_result_string(res[i], "sAMAccountName",
+ "");
+ entriesFull[count].description.string =
+ samdb_result_string(res[i], "description", "");
+ break;
+ case 3:
+ entriesFullGroup[count].idx = count + 1;
+ entriesFullGroup[count].rid =
+ objectsid->sub_auths[objectsid->num_auths-1];
+ /* We get a "7" here for groups */
+ entriesFullGroup[count].acct_flags
+ = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED;
+ entriesFullGroup[count].account_name.string =
+ samdb_result_string(res[i], "sAMAccountName",
+ "");
+ entriesFullGroup[count].description.string =
+ samdb_result_string(res[i], "description", "");
+ break;
+ case 4:
+ case 5:
+ entriesAscii[count].idx = count + 1;
+ entriesAscii[count].account_name.string =
+ samdb_result_string(res[i], "sAMAccountName",
+ "");
+ break;
+ }
+
+ count += 1;
+ }
+
+ *r->out.total_size = count;
+
+ if (r->in.start_idx >= count) {
+ *r->out.returned_size = 0;
+ switch(r->in.level) {
+ case 1:
+ r->out.info->info1.count = *r->out.returned_size;
+ r->out.info->info1.entries = NULL;
+ break;
+ case 2:
+ r->out.info->info2.count = *r->out.returned_size;
+ r->out.info->info2.entries = NULL;
+ break;
+ case 3:
+ r->out.info->info3.count = *r->out.returned_size;
+ r->out.info->info3.entries = NULL;
+ break;
+ case 4:
+ r->out.info->info4.count = *r->out.returned_size;
+ r->out.info->info4.entries = NULL;
+ break;
+ case 5:
+ r->out.info->info5.count = *r->out.returned_size;
+ r->out.info->info5.entries = NULL;
+ break;
+ }
+ } else {
+ *r->out.returned_size = MIN(count - r->in.start_idx,
+ r->in.max_entries);
+ switch(r->in.level) {
+ case 1:
+ r->out.info->info1.count = *r->out.returned_size;
+ r->out.info->info1.entries =
+ &(entriesGeneral[r->in.start_idx]);
+ break;
+ case 2:
+ r->out.info->info2.count = *r->out.returned_size;
+ r->out.info->info2.entries =
+ &(entriesFull[r->in.start_idx]);
+ break;
+ case 3:
+ r->out.info->info3.count = *r->out.returned_size;
+ r->out.info->info3.entries =
+ &(entriesFullGroup[r->in.start_idx]);
+ break;
+ case 4:
+ r->out.info->info4.count = *r->out.returned_size;
+ r->out.info->info4.entries =
+ &(entriesAscii[r->in.start_idx]);
+ break;
+ case 5:
+ r->out.info->info5.count = *r->out.returned_size;
+ r->out.info->info5.entries =
+ &(entriesAscii[r->in.start_idx]);
+ break;
+ }
+ }
+
+ return (*r->out.returned_size < (count - r->in.start_idx)) ?
+ STATUS_MORE_ENTRIES : NT_STATUS_OK;
+}
+
+
+/*
+ samr_GetDisplayEnumerationIndex
+*/
+static NTSTATUS dcesrv_samr_GetDisplayEnumerationIndex(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_GetDisplayEnumerationIndex *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ samr_TestPrivateFunctionsDomain
+*/
+static NTSTATUS dcesrv_samr_TestPrivateFunctionsDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_TestPrivateFunctionsDomain *r)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+
+/*
+ samr_TestPrivateFunctionsUser
+*/
+static NTSTATUS dcesrv_samr_TestPrivateFunctionsUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_TestPrivateFunctionsUser *r)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+
+/*
+ samr_GetUserPwInfo
+*/
+static NTSTATUS dcesrv_samr_GetUserPwInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_GetUserPwInfo *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_account_state *a_state;
+
+ ZERO_STRUCTP(r->out.info);
+
+ DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
+
+ a_state = h->data;
+
+ r->out.info->min_password_length = samdb_search_uint(a_state->sam_ctx, mem_ctx, 0,
+ a_state->domain_state->domain_dn, "minPwdLength",
+ NULL);
+ r->out.info->password_properties = samdb_search_uint(a_state->sam_ctx, mem_ctx, 0,
+ a_state->account_dn,
+ "pwdProperties", NULL);
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_RemoveMemberFromForeignDomain
+*/
+static NTSTATUS dcesrv_samr_RemoveMemberFromForeignDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_RemoveMemberFromForeignDomain *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_domain_state *d_state;
+ const char *memberdn;
+ struct ldb_message **res;
+ const char * const attrs[3] = { "distinguishedName", "objectSid", NULL };
+ int i, count;
+
+ DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
+
+ d_state = h->data;
+
+ memberdn = samdb_search_string(d_state->sam_ctx, mem_ctx, NULL,
+ "distinguishedName", "(objectSid=%s)",
+ ldap_encode_ndr_dom_sid(mem_ctx, r->in.sid));
+ /* Nothing to do */
+ if (memberdn == NULL) {
+ return NT_STATUS_OK;
+ }
+
+ /* TODO: Does this call only remove alias members, or does it do this
+ * for domain groups as well? */
+
+ count = samdb_search_domain(d_state->sam_ctx, mem_ctx,
+ d_state->domain_dn, &res, attrs,
+ d_state->domain_sid,
+ "(&(member=%s)(objectClass=group)"
+ "(|(groupType=%d)(groupType=%d)))",
+ memberdn,
+ GTYPE_SECURITY_BUILTIN_LOCAL_GROUP,
+ GTYPE_SECURITY_DOMAIN_LOCAL_GROUP);
+
+ if (count < 0)
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+
+ for (i=0; i<count; i++) {
+ struct ldb_message *mod;
+
+ mod = ldb_msg_new(mem_ctx);
+ if (mod == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ mod->dn = samdb_result_dn(d_state->sam_ctx, mod, res[i], "distinguishedName", NULL);
+ if (mod->dn == NULL) {
+ talloc_free(mod);
+ continue;
+ }
+
+ if (samdb_msg_add_delval(d_state->sam_ctx, mem_ctx, mod,
+ "member", memberdn) != 0)
+ return NT_STATUS_NO_MEMORY;
+
+ if (ldb_modify(d_state->sam_ctx, mod) != 0)
+ return NT_STATUS_UNSUCCESSFUL;
+
+ talloc_free(mod);
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_QueryDomainInfo2
+
+ just an alias for samr_QueryDomainInfo
+*/
+static NTSTATUS dcesrv_samr_QueryDomainInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_QueryDomainInfo2 *r)
+{
+ struct samr_QueryDomainInfo r1;
+ NTSTATUS status;
+
+ ZERO_STRUCT(r1.out);
+ r1.in.domain_handle = r->in.domain_handle;
+ r1.in.level = r->in.level;
+ r1.out.info = r->out.info;
+
+ status = dcesrv_samr_QueryDomainInfo(dce_call, mem_ctx, &r1);
+
+ return status;
+}
+
+
+/*
+ samr_QueryUserInfo2
+
+ just an alias for samr_QueryUserInfo
+*/
+static NTSTATUS dcesrv_samr_QueryUserInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_QueryUserInfo2 *r)
+{
+ struct samr_QueryUserInfo r1;
+ NTSTATUS status;
+
+ r1.in.user_handle = r->in.user_handle;
+ r1.in.level = r->in.level;
+ r1.out.info = r->out.info;
+
+ status = dcesrv_samr_QueryUserInfo(dce_call, mem_ctx, &r1);
+
+ return status;
+}
+
+
+/*
+ samr_QueryDisplayInfo2
+*/
+static NTSTATUS dcesrv_samr_QueryDisplayInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_QueryDisplayInfo2 *r)
+{
+ struct samr_QueryDisplayInfo q;
+ NTSTATUS result;
+
+ q.in.domain_handle = r->in.domain_handle;
+ q.in.level = r->in.level;
+ q.in.start_idx = r->in.start_idx;
+ q.in.max_entries = r->in.max_entries;
+ q.in.buf_size = r->in.buf_size;
+ q.out.total_size = r->out.total_size;
+ q.out.returned_size = r->out.returned_size;
+ q.out.info = r->out.info;
+
+ result = dcesrv_samr_QueryDisplayInfo(dce_call, mem_ctx, &q);
+
+ return result;
+}
+
+
+/*
+ samr_GetDisplayEnumerationIndex2
+*/
+static NTSTATUS dcesrv_samr_GetDisplayEnumerationIndex2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_GetDisplayEnumerationIndex2 *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ samr_QueryDisplayInfo3
+*/
+static NTSTATUS dcesrv_samr_QueryDisplayInfo3(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_QueryDisplayInfo3 *r)
+{
+ struct samr_QueryDisplayInfo q;
+ NTSTATUS result;
+
+ q.in.domain_handle = r->in.domain_handle;
+ q.in.level = r->in.level;
+ q.in.start_idx = r->in.start_idx;
+ q.in.max_entries = r->in.max_entries;
+ q.in.buf_size = r->in.buf_size;
+ q.out.total_size = r->out.total_size;
+ q.out.returned_size = r->out.returned_size;
+ q.out.info = r->out.info;
+
+ result = dcesrv_samr_QueryDisplayInfo(dce_call, mem_ctx, &q);
+
+ return result;
+}
+
+
+/*
+ samr_AddMultipleMembersToAlias
+*/
+static NTSTATUS dcesrv_samr_AddMultipleMembersToAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_AddMultipleMembersToAlias *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ samr_RemoveMultipleMembersFromAlias
+*/
+static NTSTATUS dcesrv_samr_RemoveMultipleMembersFromAlias(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_RemoveMultipleMembersFromAlias *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ samr_GetDomPwInfo
+
+ this fetches the default password properties for a domain
+
+ note that w2k3 completely ignores the domain name in this call, and
+ always returns the information for the servers primary domain
+*/
+static NTSTATUS dcesrv_samr_GetDomPwInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_GetDomPwInfo *r)
+{
+ struct ldb_message **msgs;
+ int ret;
+ const char * const attrs[] = {"minPwdLength", "pwdProperties", NULL };
+ struct ldb_context *sam_ctx;
+
+ ZERO_STRUCTP(r->out.info);
+
+ sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, dce_call->conn->auth_state.session_info);
+ if (sam_ctx == NULL) {
+ return NT_STATUS_INVALID_SYSTEM_SERVICE;
+ }
+
+ /* The domain name in this call is ignored */
+ ret = gendb_search_dn(sam_ctx,
+ mem_ctx, NULL, &msgs, attrs);
+ if (ret <= 0) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+ if (ret > 1) {
+ talloc_free(msgs);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ r->out.info->min_password_length = samdb_result_uint(msgs[0], "minPwdLength", 0);
+ r->out.info->password_properties = samdb_result_uint(msgs[0], "pwdProperties", 1);
+
+ talloc_free(msgs);
+
+ talloc_free(sam_ctx);
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_Connect2
+*/
+static NTSTATUS dcesrv_samr_Connect2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_Connect2 *r)
+{
+ struct samr_Connect c;
+
+ c.in.system_name = NULL;
+ c.in.access_mask = r->in.access_mask;
+ c.out.connect_handle = r->out.connect_handle;
+
+ return dcesrv_samr_Connect(dce_call, mem_ctx, &c);
+}
+
+
+/*
+ samr_SetUserInfo2
+
+ just an alias for samr_SetUserInfo
+*/
+static NTSTATUS dcesrv_samr_SetUserInfo2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_SetUserInfo2 *r)
+{
+ struct samr_SetUserInfo r2;
+
+ r2.in.user_handle = r->in.user_handle;
+ r2.in.level = r->in.level;
+ r2.in.info = r->in.info;
+
+ return dcesrv_samr_SetUserInfo(dce_call, mem_ctx, &r2);
+}
+
+
+/*
+ samr_SetBootKeyInformation
+*/
+static NTSTATUS dcesrv_samr_SetBootKeyInformation(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_SetBootKeyInformation *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ samr_GetBootKeyInformation
+*/
+static NTSTATUS dcesrv_samr_GetBootKeyInformation(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_GetBootKeyInformation *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ samr_Connect3
+*/
+static NTSTATUS dcesrv_samr_Connect3(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_Connect3 *r)
+{
+ struct samr_Connect c;
+
+ c.in.system_name = NULL;
+ c.in.access_mask = r->in.access_mask;
+ c.out.connect_handle = r->out.connect_handle;
+
+ return dcesrv_samr_Connect(dce_call, mem_ctx, &c);
+}
+
+
+/*
+ samr_Connect4
+*/
+static NTSTATUS dcesrv_samr_Connect4(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_Connect4 *r)
+{
+ struct samr_Connect c;
+
+ c.in.system_name = NULL;
+ c.in.access_mask = r->in.access_mask;
+ c.out.connect_handle = r->out.connect_handle;
+
+ return dcesrv_samr_Connect(dce_call, mem_ctx, &c);
+}
+
+
+/*
+ samr_Connect5
+*/
+static NTSTATUS dcesrv_samr_Connect5(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_Connect5 *r)
+{
+ struct samr_Connect c;
+ NTSTATUS status;
+
+ c.in.system_name = NULL;
+ c.in.access_mask = r->in.access_mask;
+ c.out.connect_handle = r->out.connect_handle;
+
+ status = dcesrv_samr_Connect(dce_call, mem_ctx, &c);
+
+ r->out.info_out->info1.client_version = SAMR_CONNECT_AFTER_W2K;
+ r->out.info_out->info1.unknown2 = 0;
+ *r->out.level_out = r->in.level_in;
+
+ return status;
+}
+
+
+/*
+ samr_RidToSid
+*/
+static NTSTATUS dcesrv_samr_RidToSid(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_RidToSid *r)
+{
+ struct samr_domain_state *d_state;
+ struct dcesrv_handle *h;
+
+ DCESRV_PULL_HANDLE(h, r->in.domain_handle, SAMR_HANDLE_DOMAIN);
+
+ d_state = h->data;
+
+ /* form the users SID */
+ *r->out.sid = dom_sid_add_rid(mem_ctx, d_state->domain_sid, r->in.rid);
+ if (!*r->out.sid) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_SetDsrmPassword
+*/
+static NTSTATUS dcesrv_samr_SetDsrmPassword(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_SetDsrmPassword *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ samr_ValidatePassword
+*/
+static NTSTATUS dcesrv_samr_ValidatePassword(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_ValidatePassword *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_samr_s.c"
diff --git a/source4/rpc_server/samr/dcesrv_samr.h b/source4/rpc_server/samr/dcesrv_samr.h
new file mode 100644
index 0000000000..a28a4bec43
--- /dev/null
+++ b/source4/rpc_server/samr/dcesrv_samr.h
@@ -0,0 +1,69 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the samr pipe - definitions
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "param/param.h"
+
+/*
+ this type allows us to distinguish handle types
+*/
+enum samr_handle {
+ SAMR_HANDLE_CONNECT,
+ SAMR_HANDLE_DOMAIN,
+ SAMR_HANDLE_USER,
+ SAMR_HANDLE_GROUP,
+ SAMR_HANDLE_ALIAS
+};
+
+
+/*
+ state asscoiated with a samr_Connect*() operation
+*/
+struct samr_connect_state {
+ void *sam_ctx;
+ uint32_t access_mask;
+};
+
+/*
+ state associated with a samr_OpenDomain() operation
+*/
+struct samr_domain_state {
+ struct samr_connect_state *connect_state;
+ void *sam_ctx;
+ uint32_t access_mask;
+ struct dom_sid *domain_sid;
+ const char *domain_name;
+ struct ldb_dn *domain_dn;
+ enum server_role role;
+ bool builtin;
+ struct loadparm_context *lp_ctx;
+};
+
+/*
+ state associated with a open account handle
+*/
+struct samr_account_state {
+ struct samr_domain_state *domain_state;
+ void *sam_ctx;
+ uint32_t access_mask;
+ struct dom_sid *account_sid;
+ const char *account_name;
+ struct ldb_dn *account_dn;
+};
diff --git a/source4/rpc_server/samr/samr_password.c b/source4/rpc_server/samr/samr_password.c
new file mode 100644
index 0000000000..f334eeb8f3
--- /dev/null
+++ b/source4/rpc_server/samr/samr_password.c
@@ -0,0 +1,628 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ samr server password set/change handling
+
+ Copyright (C) Andrew Tridgell 2004
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "rpc_server/dcerpc_server.h"
+#include "rpc_server/common/common.h"
+#include "rpc_server/samr/dcesrv_samr.h"
+#include "system/time.h"
+#include "../lib/crypto/crypto.h"
+#include "dsdb/common/flags.h"
+#include "libcli/ldap/ldap.h"
+#include "dsdb/samdb/samdb.h"
+#include "auth/auth.h"
+#include "rpc_server/samr/proto.h"
+#include "libcli/auth/libcli_auth.h"
+#include "../lib/util/util_ldb.h"
+#include "param/param.h"
+
+/*
+ samr_ChangePasswordUser
+*/
+NTSTATUS dcesrv_samr_ChangePasswordUser(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct samr_ChangePasswordUser *r)
+{
+ struct dcesrv_handle *h;
+ struct samr_account_state *a_state;
+ struct ldb_context *sam_ctx;
+ struct ldb_message **res, *msg;
+ int ret;
+ struct samr_Password new_lmPwdHash, new_ntPwdHash, checkHash;
+ struct samr_Password *lm_pwd, *nt_pwd;
+ NTSTATUS status = NT_STATUS_OK;
+ const char * const attrs[] = { "dBCSPwd", "unicodePwd" , NULL };
+
+ DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
+
+ a_state = h->data;
+
+ /* basic sanity checking on parameters. Do this before any database ops */
+ if (!r->in.lm_present || !r->in.nt_present ||
+ !r->in.old_lm_crypted || !r->in.new_lm_crypted ||
+ !r->in.old_nt_crypted || !r->in.new_nt_crypted) {
+ /* we should really handle a change with lm not
+ present */
+ return NT_STATUS_INVALID_PARAMETER_MIX;
+ }
+
+ /* To change a password we need to open as system */
+ sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, system_session(mem_ctx, dce_call->conn->dce_ctx->lp_ctx));
+ if (sam_ctx == NULL) {
+ return NT_STATUS_INVALID_SYSTEM_SERVICE;
+ }
+
+ ret = ldb_transaction_start(sam_ctx);
+ if (ret) {
+ DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
+ return NT_STATUS_TRANSACTION_ABORTED;
+ }
+
+ /* fetch the old hashes */
+ ret = gendb_search_dn(sam_ctx, mem_ctx,
+ a_state->account_dn, &res, attrs);
+ if (ret != 1) {
+ ldb_transaction_cancel(sam_ctx);
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+ msg = res[0];
+
+ status = samdb_result_passwords(mem_ctx, dce_call->conn->dce_ctx->lp_ctx,
+ msg, &lm_pwd, &nt_pwd);
+ if (!NT_STATUS_IS_OK(status) || !lm_pwd || !nt_pwd) {
+ ldb_transaction_cancel(sam_ctx);
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /* decrypt and check the new lm hash */
+ D_P16(lm_pwd->hash, r->in.new_lm_crypted->hash, new_lmPwdHash.hash);
+ D_P16(new_lmPwdHash.hash, r->in.old_lm_crypted->hash, checkHash.hash);
+ if (memcmp(checkHash.hash, lm_pwd, 16) != 0) {
+ ldb_transaction_cancel(sam_ctx);
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /* decrypt and check the new nt hash */
+ D_P16(nt_pwd->hash, r->in.new_nt_crypted->hash, new_ntPwdHash.hash);
+ D_P16(new_ntPwdHash.hash, r->in.old_nt_crypted->hash, checkHash.hash);
+ if (memcmp(checkHash.hash, nt_pwd, 16) != 0) {
+ ldb_transaction_cancel(sam_ctx);
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /* The NT Cross is not required by Win2k3 R2, but if present
+ check the nt cross hash */
+ if (r->in.cross1_present && r->in.nt_cross) {
+ D_P16(lm_pwd->hash, r->in.nt_cross->hash, checkHash.hash);
+ if (memcmp(checkHash.hash, new_ntPwdHash.hash, 16) != 0) {
+ ldb_transaction_cancel(sam_ctx);
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+ }
+
+ /* The LM Cross is not required by Win2k3 R2, but if present
+ check the lm cross hash */
+ if (r->in.cross2_present && r->in.lm_cross) {
+ D_P16(nt_pwd->hash, r->in.lm_cross->hash, checkHash.hash);
+ if (memcmp(checkHash.hash, new_lmPwdHash.hash, 16) != 0) {
+ ldb_transaction_cancel(sam_ctx);
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+ }
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ ldb_transaction_cancel(sam_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ msg->dn = ldb_dn_copy(msg, a_state->account_dn);
+ if (!msg->dn) {
+ ldb_transaction_cancel(sam_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* setup password modify mods on the user DN specified. This may fail
+ * due to password policies. */
+ status = samdb_set_password(sam_ctx, mem_ctx,
+ a_state->account_dn, a_state->domain_state->domain_dn,
+ msg, NULL, &new_lmPwdHash, &new_ntPwdHash,
+ true, /* this is a user password change */
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ ldb_transaction_cancel(sam_ctx);
+ return status;
+ }
+
+ /* The above call only setup the modifications, this actually
+ * makes the write to the database. */
+ ret = samdb_replace(sam_ctx, mem_ctx, msg);
+ if (ret != 0) {
+ DEBUG(2,("Failed to modify record to change password on %s: %s\n",
+ ldb_dn_get_linearized(a_state->account_dn),
+ ldb_errstring(sam_ctx)));
+ ldb_transaction_cancel(sam_ctx);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ /* And this confirms it in a transaction commit */
+ ret = ldb_transaction_commit(sam_ctx);
+ if (ret != 0) {
+ DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
+ ldb_dn_get_linearized(a_state->account_dn),
+ ldb_errstring(sam_ctx)));
+ return NT_STATUS_TRANSACTION_ABORTED;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ samr_OemChangePasswordUser2
+*/
+NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_OemChangePasswordUser2 *r)
+{
+ NTSTATUS status;
+ DATA_BLOB new_password, new_unicode_password;
+ char *new_pass;
+ struct samr_CryptPassword *pwbuf = r->in.password;
+ struct ldb_context *sam_ctx;
+ struct ldb_dn *user_dn;
+ int ret;
+ struct ldb_message **res, *mod;
+ const char * const attrs[] = { "objectSid", "dBCSPwd", NULL };
+ struct samr_Password *lm_pwd;
+ DATA_BLOB lm_pwd_blob;
+ uint8_t new_lm_hash[16];
+ struct samr_Password lm_verifier;
+ size_t unicode_pw_len;
+
+ if (pwbuf == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (r->in.hash == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* To change a password we need to open as system */
+ sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, system_session(mem_ctx, dce_call->conn->dce_ctx->lp_ctx));
+ if (sam_ctx == NULL) {
+ return NT_STATUS_INVALID_SYSTEM_SERVICE;
+ }
+
+ ret = ldb_transaction_start(sam_ctx);
+ if (ret) {
+ DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
+ return NT_STATUS_TRANSACTION_ABORTED;
+ }
+
+ /* we need the users dn and the domain dn (derived from the
+ user SID). We also need the current lm password hash in
+ order to decrypt the incoming password */
+ ret = gendb_search(sam_ctx,
+ mem_ctx, NULL, &res, attrs,
+ "(&(sAMAccountName=%s)(objectclass=user))",
+ r->in.account->string);
+ if (ret != 1) {
+ ldb_transaction_cancel(sam_ctx);
+ /* Don't give the game away: (don't allow anonymous users to prove the existance of usernames) */
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ user_dn = res[0]->dn;
+
+ status = samdb_result_passwords(mem_ctx, dce_call->conn->dce_ctx->lp_ctx,
+ res[0], &lm_pwd, NULL);
+ if (!NT_STATUS_IS_OK(status) || !lm_pwd) {
+ ldb_transaction_cancel(sam_ctx);
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /* decrypt the password we have been given */
+ lm_pwd_blob = data_blob(lm_pwd->hash, sizeof(lm_pwd->hash));
+ arcfour_crypt_blob(pwbuf->data, 516, &lm_pwd_blob);
+ data_blob_free(&lm_pwd_blob);
+
+ if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
+ ldb_transaction_cancel(sam_ctx);
+ DEBUG(3,("samr: failed to decode password buffer\n"));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ if (!convert_string_talloc_convenience(mem_ctx, lp_iconv_convenience(dce_call->conn->dce_ctx->lp_ctx),
+ CH_DOS, CH_UNIX,
+ (const char *)new_password.data,
+ new_password.length,
+ (void **)&new_pass, NULL, false)) {
+ DEBUG(3,("samr: failed to convert incoming password buffer to unix charset\n"));
+ ldb_transaction_cancel(sam_ctx);
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ if (!convert_string_talloc_convenience(mem_ctx, lp_iconv_convenience(dce_call->conn->dce_ctx->lp_ctx),
+ CH_DOS, CH_UTF16,
+ (const char *)new_password.data,
+ new_password.length,
+ (void **)&new_unicode_password.data, &unicode_pw_len, false)) {
+ DEBUG(3,("samr: failed to convert incoming password buffer to UTF16 charset\n"));
+ ldb_transaction_cancel(sam_ctx);
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+ new_unicode_password.length = unicode_pw_len;
+
+ E_deshash(new_pass, new_lm_hash);
+ E_old_pw_hash(new_lm_hash, lm_pwd->hash, lm_verifier.hash);
+ if (memcmp(lm_verifier.hash, r->in.hash->hash, 16) != 0) {
+ ldb_transaction_cancel(sam_ctx);
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ mod = ldb_msg_new(mem_ctx);
+ if (mod == NULL) {
+ ldb_transaction_cancel(sam_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ mod->dn = ldb_dn_copy(mod, user_dn);
+ if (!mod->dn) {
+ ldb_transaction_cancel(sam_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* set the password on the user DN specified. This may fail
+ * due to password policies */
+ status = samdb_set_password(sam_ctx, mem_ctx,
+ user_dn, NULL,
+ mod, &new_unicode_password,
+ NULL, NULL,
+ true, /* this is a user password change */
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ ldb_transaction_cancel(sam_ctx);
+ return status;
+ }
+
+ /* The above call only setup the modifications, this actually
+ * makes the write to the database. */
+ ret = samdb_replace(sam_ctx, mem_ctx, mod);
+ if (ret != 0) {
+ DEBUG(2,("Failed to modify record to change password on %s: %s\n",
+ ldb_dn_get_linearized(user_dn),
+ ldb_errstring(sam_ctx)));
+ ldb_transaction_cancel(sam_ctx);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ /* And this confirms it in a transaction commit */
+ ret = ldb_transaction_commit(sam_ctx);
+ if (ret != 0) {
+ DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
+ ldb_dn_get_linearized(user_dn),
+ ldb_errstring(sam_ctx)));
+ return NT_STATUS_TRANSACTION_ABORTED;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ samr_ChangePasswordUser3
+*/
+NTSTATUS dcesrv_samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct samr_ChangePasswordUser3 *r)
+{
+ NTSTATUS status;
+ DATA_BLOB new_password;
+ struct ldb_context *sam_ctx = NULL;
+ struct ldb_dn *user_dn;
+ int ret;
+ struct ldb_message **res, *mod;
+ const char * const attrs[] = { "unicodePwd", "dBCSPwd", NULL };
+ struct samr_Password *nt_pwd, *lm_pwd;
+ DATA_BLOB nt_pwd_blob;
+ struct samr_DomInfo1 *dominfo = NULL;
+ struct samr_ChangeReject *reject = NULL;
+ enum samr_RejectReason reason = SAMR_REJECT_OTHER;
+ uint8_t new_nt_hash[16], new_lm_hash[16];
+ struct samr_Password nt_verifier, lm_verifier;
+
+ *r->out.dominfo = NULL;
+ *r->out.reject = NULL;
+
+ if (r->in.nt_password == NULL ||
+ r->in.nt_verifier == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* To change a password we need to open as system */
+ sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, system_session(mem_ctx, dce_call->conn->dce_ctx->lp_ctx));
+ if (sam_ctx == NULL) {
+ return NT_STATUS_INVALID_SYSTEM_SERVICE;
+ }
+
+ ret = ldb_transaction_start(sam_ctx);
+ if (ret) {
+ talloc_free(sam_ctx);
+ DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
+ return NT_STATUS_TRANSACTION_ABORTED;
+ }
+
+ /* we need the users dn and the domain dn (derived from the
+ user SID). We also need the current lm and nt password hashes
+ in order to decrypt the incoming passwords */
+ ret = gendb_search(sam_ctx,
+ mem_ctx, NULL, &res, attrs,
+ "(&(sAMAccountName=%s)(objectclass=user))",
+ r->in.account->string);
+ if (ret != 1) {
+ /* Don't give the game away: (don't allow anonymous users to prove the existance of usernames) */
+ status = NT_STATUS_WRONG_PASSWORD;
+ goto failed;
+ }
+
+ user_dn = res[0]->dn;
+
+ status = samdb_result_passwords(mem_ctx, dce_call->conn->dce_ctx->lp_ctx,
+ res[0], &lm_pwd, &nt_pwd);
+ if (!NT_STATUS_IS_OK(status) ) {
+ goto failed;
+ }
+
+ if (!nt_pwd) {
+ status = NT_STATUS_WRONG_PASSWORD;
+ goto failed;
+ }
+
+ /* decrypt the password we have been given */
+ nt_pwd_blob = data_blob(nt_pwd->hash, sizeof(nt_pwd->hash));
+ arcfour_crypt_blob(r->in.nt_password->data, 516, &nt_pwd_blob);
+ data_blob_free(&nt_pwd_blob);
+
+ if (!extract_pw_from_buffer(mem_ctx, r->in.nt_password->data, &new_password)) {
+ ldb_transaction_cancel(sam_ctx);
+ DEBUG(3,("samr: failed to decode password buffer\n"));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ if (r->in.nt_verifier == NULL) {
+ status = NT_STATUS_WRONG_PASSWORD;
+ goto failed;
+ }
+
+ /* check NT verifier */
+ mdfour(new_nt_hash, new_password.data, new_password.length);
+
+ E_old_pw_hash(new_nt_hash, nt_pwd->hash, nt_verifier.hash);
+ if (memcmp(nt_verifier.hash, r->in.nt_verifier->hash, 16) != 0) {
+ status = NT_STATUS_WRONG_PASSWORD;
+ goto failed;
+ }
+
+ /* check LM verifier (really not needed as we just checked the
+ * much stronger NT hash, but the RPC-SAMR test checks for
+ * this) */
+ if (lm_pwd && r->in.lm_verifier != NULL) {
+ char *new_pass;
+ if (!convert_string_talloc_convenience(mem_ctx, lp_iconv_convenience(dce_call->conn->dce_ctx->lp_ctx),
+ CH_UTF16, CH_UNIX,
+ (const char *)new_password.data,
+ new_password.length,
+ (void **)&new_pass, NULL, false)) {
+ E_deshash(new_pass, new_lm_hash);
+ E_old_pw_hash(new_nt_hash, lm_pwd->hash, lm_verifier.hash);
+ if (memcmp(lm_verifier.hash, r->in.lm_verifier->hash, 16) != 0) {
+ status = NT_STATUS_WRONG_PASSWORD;
+ goto failed;
+ }
+ }
+ }
+
+ mod = ldb_msg_new(mem_ctx);
+ if (mod == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto failed;
+ }
+
+ mod->dn = ldb_dn_copy(mod, user_dn);
+ if (!mod->dn) {
+ status = NT_STATUS_NO_MEMORY;
+ goto failed;
+ }
+
+ /* set the password on the user DN specified. This may fail
+ * due to password policies */
+ status = samdb_set_password(sam_ctx, mem_ctx,
+ user_dn, NULL,
+ mod, &new_password,
+ NULL, NULL,
+ true, /* this is a user password change */
+ &reason,
+ &dominfo);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+
+ /* The above call only setup the modifications, this actually
+ * makes the write to the database. */
+ ret = samdb_replace(sam_ctx, mem_ctx, mod);
+ if (ret != 0) {
+ DEBUG(2,("samdb_replace failed to change password for %s: %s\n",
+ ldb_dn_get_linearized(user_dn),
+ ldb_errstring(sam_ctx)));
+ status = NT_STATUS_UNSUCCESSFUL;
+ goto failed;
+ }
+
+ /* And this confirms it in a transaction commit */
+ ret = ldb_transaction_commit(sam_ctx);
+ if (ret != 0) {
+ DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
+ ldb_dn_get_linearized(user_dn),
+ ldb_errstring(sam_ctx)));
+ status = NT_STATUS_TRANSACTION_ABORTED;
+ goto failed;
+ }
+
+ return NT_STATUS_OK;
+
+failed:
+ ldb_transaction_cancel(sam_ctx);
+ talloc_free(sam_ctx);
+
+ reject = talloc(mem_ctx, struct samr_ChangeReject);
+ *r->out.dominfo = dominfo;
+ *r->out.reject = reject;
+
+ if (reject == NULL) {
+ return status;
+ }
+ ZERO_STRUCTP(reject);
+
+ reject->reason = reason;
+
+ return status;
+}
+
+
+/*
+ samr_ChangePasswordUser2
+
+ easy - just a subset of samr_ChangePasswordUser3
+*/
+NTSTATUS dcesrv_samr_ChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct samr_ChangePasswordUser2 *r)
+{
+ struct samr_ChangePasswordUser3 r2;
+ struct samr_DomInfo1 *dominfo = NULL;
+ struct samr_ChangeReject *reject = NULL;
+
+ r2.in.server = r->in.server;
+ r2.in.account = r->in.account;
+ r2.in.nt_password = r->in.nt_password;
+ r2.in.nt_verifier = r->in.nt_verifier;
+ r2.in.lm_change = r->in.lm_change;
+ r2.in.lm_password = r->in.lm_password;
+ r2.in.lm_verifier = r->in.lm_verifier;
+ r2.in.password3 = NULL;
+ r2.out.dominfo = &dominfo;
+ r2.out.reject = &reject;
+
+ return dcesrv_samr_ChangePasswordUser3(dce_call, mem_ctx, &r2);
+}
+
+
+/*
+ set password via a samr_CryptPassword buffer
+ this will in the 'msg' with modify operations that will update the user
+ password when applied
+*/
+NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
+ void *sam_ctx,
+ struct ldb_dn *account_dn, struct ldb_dn *domain_dn,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message *msg,
+ struct samr_CryptPassword *pwbuf)
+{
+ NTSTATUS nt_status;
+ DATA_BLOB new_password;
+ DATA_BLOB session_key = data_blob(NULL, 0);
+
+ nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ arcfour_crypt_blob(pwbuf->data, 516, &session_key);
+
+ if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
+ DEBUG(3,("samr: failed to decode password buffer\n"));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /* set the password - samdb needs to know both the domain and user DNs,
+ so the domain password policy can be used */
+ return samdb_set_password(sam_ctx, mem_ctx,
+ account_dn, domain_dn,
+ msg, &new_password,
+ NULL, NULL,
+ false, /* This is a password set, not change */
+ NULL, NULL);
+}
+
+
+/*
+ set password via a samr_CryptPasswordEx buffer
+ this will in the 'msg' with modify operations that will update the user
+ password when applied
+*/
+NTSTATUS samr_set_password_ex(struct dcesrv_call_state *dce_call,
+ struct ldb_context *sam_ctx,
+ struct ldb_dn *account_dn, struct ldb_dn *domain_dn,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message *msg,
+ struct samr_CryptPasswordEx *pwbuf)
+{
+ NTSTATUS nt_status;
+ DATA_BLOB new_password;
+ DATA_BLOB co_session_key;
+ DATA_BLOB session_key = data_blob(NULL, 0);
+ struct MD5Context ctx;
+
+ nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ co_session_key = data_blob_talloc(mem_ctx, NULL, 16);
+ if (!co_session_key.data) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ MD5Init(&ctx);
+ MD5Update(&ctx, &pwbuf->data[516], 16);
+ MD5Update(&ctx, session_key.data, session_key.length);
+ MD5Final(co_session_key.data, &ctx);
+
+ arcfour_crypt_blob(pwbuf->data, 516, &co_session_key);
+
+ if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
+ DEBUG(3,("samr: failed to decode password buffer\n"));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /* set the password - samdb needs to know both the domain and user DNs,
+ so the domain password policy can be used */
+ return samdb_set_password(sam_ctx, mem_ctx,
+ account_dn, domain_dn,
+ msg, &new_password,
+ NULL, NULL,
+ false, /* This is a password set, not change */
+ NULL, NULL);
+}
+
+
diff --git a/source4/rpc_server/service_rpc.c b/source4/rpc_server/service_rpc.c
new file mode 100644
index 0000000000..5596944bd8
--- /dev/null
+++ b/source4/rpc_server/service_rpc.c
@@ -0,0 +1,458 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ smbd-specific dcerpc server code
+
+ Copyright (C) Andrew Tridgell 2003-2005
+ Copyright (C) Stefan (metze) Metzmacher 2004-2005
+ Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2004,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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "librpc/gen_ndr/ndr_dcerpc.h"
+#include "auth/auth.h"
+#include "auth/gensec/gensec.h"
+#include "../lib/util/dlinklist.h"
+#include "rpc_server/dcerpc_server.h"
+#include "rpc_server/dcerpc_server_proto.h"
+#include "lib/events/events.h"
+#include "smbd/service_task.h"
+#include "smbd/service_stream.h"
+#include "smbd/service.h"
+#include "system/filesys.h"
+#include "libcli/security/security.h"
+#include "lib/socket/socket.h"
+#include "lib/messaging/irpc.h"
+#include "system/network.h"
+#include "lib/socket/netif.h"
+#include "param/param.h"
+
+struct dcesrv_socket_context {
+ const struct dcesrv_endpoint *endpoint;
+ struct dcesrv_context *dcesrv_ctx;
+};
+
+/*
+ write_fn callback for dcesrv_output()
+*/
+static NTSTATUS dcerpc_write_fn(void *private_data, DATA_BLOB *out, size_t *nwritten)
+{
+ NTSTATUS status;
+ struct socket_context *sock = talloc_get_type(private_data, struct socket_context);
+ size_t sendlen;
+
+ status = socket_send(sock, out, &sendlen);
+ NT_STATUS_IS_ERR_RETURN(status);
+
+ *nwritten = sendlen;
+ return status;
+}
+
+static void dcesrv_terminate_connection(struct dcesrv_connection *dce_conn, const char *reason)
+{
+ struct stream_connection *srv_conn;
+ srv_conn = talloc_get_type(dce_conn->transport.private_data,
+ struct stream_connection);
+
+ stream_terminate_connection(srv_conn, reason);
+}
+
+static void dcesrv_sock_report_output_data(struct dcesrv_connection *dcesrv_conn)
+{
+ struct stream_connection *srv_conn;
+ srv_conn = talloc_get_type(dcesrv_conn->transport.private_data,
+ struct stream_connection);
+
+ if (srv_conn && srv_conn->event.fde) {
+ EVENT_FD_WRITEABLE(srv_conn->event.fde);
+ }
+}
+
+static struct socket_address *dcesrv_sock_get_my_addr(struct dcesrv_connection *dcesrv_conn, TALLOC_CTX *mem_ctx)
+{
+ struct stream_connection *srv_conn;
+ srv_conn = talloc_get_type(dcesrv_conn->transport.private_data,
+ struct stream_connection);
+
+ return socket_get_my_addr(srv_conn->socket, mem_ctx);
+}
+
+static struct socket_address *dcesrv_sock_get_peer_addr(struct dcesrv_connection *dcesrv_conn, TALLOC_CTX *mem_ctx)
+{
+ struct stream_connection *srv_conn;
+ srv_conn = talloc_get_type(dcesrv_conn->transport.private_data,
+ struct stream_connection);
+
+ return socket_get_peer_addr(srv_conn->socket, mem_ctx);
+}
+
+static void dcesrv_sock_accept(struct stream_connection *srv_conn)
+{
+ NTSTATUS status;
+ struct dcesrv_socket_context *dcesrv_sock =
+ talloc_get_type(srv_conn->private_data, struct dcesrv_socket_context);
+ struct dcesrv_connection *dcesrv_conn = NULL;
+
+ if (!srv_conn->session_info) {
+ status = auth_anonymous_session_info(srv_conn,
+ srv_conn->event.ctx,
+ srv_conn->lp_ctx,
+ &srv_conn->session_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("dcesrv_sock_accept: auth_anonymous_session_info failed: %s\n",
+ nt_errstr(status)));
+ stream_terminate_connection(srv_conn, nt_errstr(status));
+ return;
+ }
+ }
+
+ status = dcesrv_endpoint_connect(dcesrv_sock->dcesrv_ctx,
+ srv_conn,
+ dcesrv_sock->endpoint,
+ srv_conn->session_info,
+ srv_conn->event.ctx,
+ srv_conn->msg_ctx,
+ srv_conn->server_id,
+ DCESRV_CALL_STATE_FLAG_MAY_ASYNC,
+ &dcesrv_conn);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("dcesrv_sock_accept: dcesrv_endpoint_connect failed: %s\n",
+ nt_errstr(status)));
+ stream_terminate_connection(srv_conn, nt_errstr(status));
+ return;
+ }
+
+ dcesrv_conn->transport.private_data = srv_conn;
+ dcesrv_conn->transport.report_output_data = dcesrv_sock_report_output_data;
+ dcesrv_conn->transport.get_my_addr = dcesrv_sock_get_my_addr;
+ dcesrv_conn->transport.get_peer_addr = dcesrv_sock_get_peer_addr;
+
+ if (dcesrv_sock->endpoint->ep_description->transport == NCACN_NP) {
+ dcesrv_conn->auth_state.session_key = dcesrv_inherited_session_key;
+ }
+
+ srv_conn->private_data = dcesrv_conn;
+
+ irpc_add_name(srv_conn->msg_ctx, "rpc_server");
+
+ return;
+}
+
+static void dcesrv_sock_recv(struct stream_connection *conn, uint16_t flags)
+{
+ NTSTATUS status;
+ struct dcesrv_connection *dce_conn = talloc_get_type(conn->private_data, struct dcesrv_connection);
+ DATA_BLOB tmp_blob;
+ size_t nread;
+
+ if (dce_conn->processing) {
+ EVENT_FD_NOT_READABLE(conn->event.fde);
+ return;
+ }
+
+ tmp_blob = data_blob_talloc(conn->socket, NULL, 0x1000);
+ if (tmp_blob.data == NULL) {
+ dcesrv_terminate_connection(dce_conn, "out of memory");
+ return;
+ }
+
+ status = socket_recv(conn->socket, tmp_blob.data, tmp_blob.length, &nread);
+ if (NT_STATUS_IS_ERR(status)) {
+ dcesrv_terminate_connection(dce_conn, nt_errstr(status));
+ return;
+ }
+ if (nread == 0) {
+ talloc_free(tmp_blob.data);
+ return;
+ }
+
+ tmp_blob.length = nread;
+
+ dce_conn->processing = true;
+ status = dcesrv_input(dce_conn, &tmp_blob);
+ dce_conn->processing = false;
+ talloc_free(tmp_blob.data);
+
+ EVENT_FD_READABLE(conn->event.fde);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ dcesrv_terminate_connection(dce_conn, nt_errstr(status));
+ return;
+ }
+
+ if (dce_conn->call_list && dce_conn->call_list->replies) {
+ EVENT_FD_WRITEABLE(conn->event.fde);
+ }
+}
+
+static void dcesrv_sock_send(struct stream_connection *conn, uint16_t flags)
+{
+ struct dcesrv_connection *dce_conn = talloc_get_type(conn->private_data, struct dcesrv_connection);
+ NTSTATUS status;
+
+ status = dcesrv_output(dce_conn, conn->socket, dcerpc_write_fn);
+ if (NT_STATUS_IS_ERR(status)) {
+ dcesrv_terminate_connection(dce_conn, "eof on socket");
+ return;
+ }
+
+ if (!dce_conn->call_list || !dce_conn->call_list->replies) {
+ EVENT_FD_NOT_WRITEABLE(conn->event.fde);
+ }
+}
+
+
+static const struct stream_server_ops dcesrv_stream_ops = {
+ .name = "rpc",
+ .accept_connection = dcesrv_sock_accept,
+ .recv_handler = dcesrv_sock_recv,
+ .send_handler = dcesrv_sock_send,
+};
+
+
+
+static NTSTATUS dcesrv_add_ep_unix(struct dcesrv_context *dce_ctx,
+ struct loadparm_context *lp_ctx,
+ struct dcesrv_endpoint *e,
+ struct tevent_context *event_ctx, const struct model_ops *model_ops)
+{
+ struct dcesrv_socket_context *dcesrv_sock;
+ uint16_t port = 1;
+ NTSTATUS status;
+
+ dcesrv_sock = talloc(event_ctx, struct dcesrv_socket_context);
+ NT_STATUS_HAVE_NO_MEMORY(dcesrv_sock);
+
+ /* remember the endpoint of this socket */
+ dcesrv_sock->endpoint = e;
+ dcesrv_sock->dcesrv_ctx = talloc_reference(dcesrv_sock, dce_ctx);
+
+ status = stream_setup_socket(event_ctx, lp_ctx,
+ model_ops, &dcesrv_stream_ops,
+ "unix", e->ep_description->endpoint, &port,
+ lp_socket_options(lp_ctx),
+ dcesrv_sock);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("service_setup_stream_socket(path=%s) failed - %s\n",
+ e->ep_description->endpoint, nt_errstr(status)));
+ }
+
+ return status;
+}
+
+static NTSTATUS dcesrv_add_ep_ncalrpc(struct dcesrv_context *dce_ctx,
+ struct loadparm_context *lp_ctx,
+ struct dcesrv_endpoint *e,
+ struct tevent_context *event_ctx, const struct model_ops *model_ops)
+{
+ struct dcesrv_socket_context *dcesrv_sock;
+ uint16_t port = 1;
+ char *full_path;
+ NTSTATUS status;
+
+ if (!e->ep_description->endpoint) {
+ /* No identifier specified: use DEFAULT.
+ * DO NOT hardcode this value anywhere else. Rather, specify
+ * no endpoint and let the epmapper worry about it. */
+ e->ep_description->endpoint = talloc_strdup(dce_ctx, "DEFAULT");
+ }
+
+ full_path = talloc_asprintf(dce_ctx, "%s/%s", lp_ncalrpc_dir(lp_ctx),
+ e->ep_description->endpoint);
+
+ dcesrv_sock = talloc(event_ctx, struct dcesrv_socket_context);
+ NT_STATUS_HAVE_NO_MEMORY(dcesrv_sock);
+
+ /* remember the endpoint of this socket */
+ dcesrv_sock->endpoint = e;
+ dcesrv_sock->dcesrv_ctx = talloc_reference(dcesrv_sock, dce_ctx);
+
+ status = stream_setup_socket(event_ctx, lp_ctx,
+ model_ops, &dcesrv_stream_ops,
+ "unix", full_path, &port,
+ lp_socket_options(lp_ctx),
+ dcesrv_sock);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("service_setup_stream_socket(identifier=%s,path=%s) failed - %s\n",
+ e->ep_description->endpoint, full_path, nt_errstr(status)));
+ }
+ return status;
+}
+
+static NTSTATUS dcesrv_add_ep_np(struct dcesrv_context *dce_ctx,
+ struct loadparm_context *lp_ctx,
+ struct dcesrv_endpoint *e,
+ struct tevent_context *event_ctx, const struct model_ops *model_ops)
+{
+ struct dcesrv_socket_context *dcesrv_sock;
+ NTSTATUS status;
+
+ if (e->ep_description->endpoint == NULL) {
+ DEBUG(0, ("Endpoint mandatory for named pipes\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ dcesrv_sock = talloc(event_ctx, struct dcesrv_socket_context);
+ NT_STATUS_HAVE_NO_MEMORY(dcesrv_sock);
+
+ /* remember the endpoint of this socket */
+ dcesrv_sock->endpoint = e;
+ dcesrv_sock->dcesrv_ctx = talloc_reference(dcesrv_sock, dce_ctx);
+
+ status = stream_setup_named_pipe(event_ctx, lp_ctx,
+ model_ops, &dcesrv_stream_ops,
+ e->ep_description->endpoint, dcesrv_sock);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("stream_setup_named_pipe(pipe=%s) failed - %s\n",
+ e->ep_description->endpoint, nt_errstr(status)));
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ add a socket address to the list of events, one event per dcerpc endpoint
+*/
+static NTSTATUS add_socket_rpc_tcp_iface(struct dcesrv_context *dce_ctx, struct dcesrv_endpoint *e,
+ struct tevent_context *event_ctx, const struct model_ops *model_ops,
+ const char *address)
+{
+ struct dcesrv_socket_context *dcesrv_sock;
+ uint16_t port = 0;
+ NTSTATUS status;
+
+ if (e->ep_description->endpoint) {
+ port = atoi(e->ep_description->endpoint);
+ }
+
+ dcesrv_sock = talloc(event_ctx, struct dcesrv_socket_context);
+ NT_STATUS_HAVE_NO_MEMORY(dcesrv_sock);
+
+ /* remember the endpoint of this socket */
+ dcesrv_sock->endpoint = e;
+ dcesrv_sock->dcesrv_ctx = talloc_reference(dcesrv_sock, dce_ctx);
+
+ status = stream_setup_socket(event_ctx, dce_ctx->lp_ctx,
+ model_ops, &dcesrv_stream_ops,
+ "ipv4", address, &port,
+ lp_socket_options(dce_ctx->lp_ctx),
+ dcesrv_sock);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("service_setup_stream_socket(address=%s,port=%u) failed - %s\n",
+ address, port, nt_errstr(status)));
+ }
+
+ if (e->ep_description->endpoint == NULL) {
+ e->ep_description->endpoint = talloc_asprintf(dce_ctx, "%d", port);
+ }
+
+ return status;
+}
+
+static NTSTATUS dcesrv_add_ep_tcp(struct dcesrv_context *dce_ctx,
+ struct loadparm_context *lp_ctx,
+ struct dcesrv_endpoint *e,
+ struct tevent_context *event_ctx, const struct model_ops *model_ops)
+{
+ NTSTATUS status;
+
+ /* Add TCP/IP sockets */
+ if (lp_interfaces(lp_ctx) && lp_bind_interfaces_only(lp_ctx)) {
+ int num_interfaces;
+ int i;
+ struct interface *ifaces;
+
+ load_interfaces(dce_ctx, lp_interfaces(lp_ctx), &ifaces);
+
+ num_interfaces = iface_count(ifaces);
+ for(i = 0; i < num_interfaces; i++) {
+ const char *address = iface_n_ip(ifaces, i);
+ status = add_socket_rpc_tcp_iface(dce_ctx, e, event_ctx, model_ops, address);
+ NT_STATUS_NOT_OK_RETURN(status);
+ }
+ } else {
+ status = add_socket_rpc_tcp_iface(dce_ctx, e, event_ctx, model_ops,
+ lp_socket_address(lp_ctx));
+ NT_STATUS_NOT_OK_RETURN(status);
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS dcesrv_add_ep(struct dcesrv_context *dce_ctx,
+ struct loadparm_context *lp_ctx,
+ struct dcesrv_endpoint *e,
+ struct tevent_context *event_ctx, const struct model_ops *model_ops)
+{
+ switch (e->ep_description->transport) {
+ case NCACN_UNIX_STREAM:
+ return dcesrv_add_ep_unix(dce_ctx, lp_ctx, e, event_ctx, model_ops);
+
+ case NCALRPC:
+ return dcesrv_add_ep_ncalrpc(dce_ctx, lp_ctx, e, event_ctx, model_ops);
+
+ case NCACN_IP_TCP:
+ return dcesrv_add_ep_tcp(dce_ctx, lp_ctx, e, event_ctx, model_ops);
+
+ case NCACN_NP:
+ return dcesrv_add_ep_np(dce_ctx, lp_ctx, e, event_ctx, model_ops);
+
+ default:
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+}
+
+/*
+ open the dcerpc server sockets
+*/
+static void dcesrv_task_init(struct task_server *task)
+{
+ NTSTATUS status;
+ struct dcesrv_context *dce_ctx;
+ struct dcesrv_endpoint *e;
+
+ dcerpc_server_init(task->lp_ctx);
+
+ task_server_set_title(task, "task[dcesrv]");
+
+ status = dcesrv_init_context(task->event_ctx,
+ task->lp_ctx,
+ lp_dcerpc_endpoint_servers(task->lp_ctx),
+ &dce_ctx);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+
+ /* Make sure the directory for NCALRPC exists */
+ if (!directory_exist(lp_ncalrpc_dir(task->lp_ctx))) {
+ mkdir(lp_ncalrpc_dir(task->lp_ctx), 0755);
+ }
+
+ for (e=dce_ctx->endpoint_list;e;e=e->next) {
+ status = dcesrv_add_ep(dce_ctx, task->lp_ctx, e, task->event_ctx, task->model_ops);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+ }
+
+ return;
+failed:
+ task_server_terminate(task, "Failed to startup dcerpc server task");
+}
+
+NTSTATUS server_service_rpc_init(void)
+{
+
+ return register_server_service("rpc", dcesrv_task_init);
+}
diff --git a/source4/rpc_server/spoolss/dcesrv_spoolss.c b/source4/rpc_server/spoolss/dcesrv_spoolss.c
new file mode 100644
index 0000000000..7d14c0e502
--- /dev/null
+++ b/source4/rpc_server/spoolss/dcesrv_spoolss.c
@@ -0,0 +1,1594 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the spoolss pipe
+
+ Copyright (C) Tim Potter 2004
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "rpc_server/dcerpc_server.h"
+#include "librpc/gen_ndr/ndr_spoolss.h"
+#include "rpc_server/common/common.h"
+#include "ntptr/ntptr.h"
+#include "lib/socket/socket.h"
+#include "smbd/service_stream.h"
+#include "librpc/gen_ndr/ndr_spoolss_c.h"
+#include "auth/credentials/credentials.h"
+#include "param/param.h"
+
+enum spoolss_handle {
+ SPOOLSS_NOTIFY
+};
+
+#define SPOOLSS_BUFFER_UNION(fn,ic,info,level) \
+ ((info)?ndr_size_##fn(info, level, ic, 0):0)
+
+#define SPOOLSS_BUFFER_UNION_ARRAY(fn,ic,info,level,count) \
+ ((info)?ndr_size_##fn##_info(dce_call, ic, level, count, info):0)
+
+#define SPOOLSS_BUFFER_OK(val_true,val_false) ((r->in.offered >= *r->out.needed)?val_true:val_false)
+
+static WERROR dcesrv_spoolss_parse_printer_name(TALLOC_CTX *mem_ctx, const char *name,
+ const char **_server_name,
+ const char **_object_name,
+ enum ntptr_HandleType *_object_type)
+{
+ char *p;
+ char *server = NULL;
+ char *server_unc = NULL;
+ const char *object = name;
+
+ /* no printername is there it's like open server */
+ if (!name) {
+ *_server_name = NULL;
+ *_object_name = NULL;
+ *_object_type = NTPTR_HANDLE_SERVER;
+ return WERR_OK;
+ }
+
+ /* just "\\" is invalid */
+ if (strequal("\\\\", name)) {
+ return WERR_INVALID_PRINTER_NAME;
+ }
+
+ if (strncmp("\\\\", name, 2) == 0) {
+ server_unc = talloc_strdup(mem_ctx, name);
+ W_ERROR_HAVE_NO_MEMORY(server_unc);
+ server = server_unc + 2;
+
+ /* here we know we have "\\" in front not followed
+ * by '\0', now see if we have another "\" in the string
+ */
+ p = strchr_m(server, '\\');
+ if (!p) {
+ /* there's no other "\", so it's ("\\%s",server)
+ */
+ *_server_name = server_unc;
+ *_object_name = NULL;
+ *_object_type = NTPTR_HANDLE_SERVER;
+ return WERR_OK;
+ }
+ /* here we know that we have ("\\%s\",server),
+ * if we have '\0' as next then it's an invalid name
+ * otherwise the printer_name
+ */
+ p[0] = '\0';
+ /* everything that follows is the printer name */
+ p++;
+ object = p;
+
+ /* just "" as server is invalid */
+ if (strequal(server, "")) {
+ return WERR_INVALID_PRINTER_NAME;
+ }
+ }
+
+ /* just "" is invalid */
+ if (strequal(object, "")) {
+ return WERR_INVALID_PRINTER_NAME;
+ }
+
+#define XCV_PORT ",XcvPort "
+#define XCV_MONITOR ",XcvMonitor "
+ if (strncmp(object, XCV_PORT, strlen(XCV_PORT)) == 0) {
+ object += strlen(XCV_PORT);
+
+ /* just "" is invalid */
+ if (strequal(object, "")) {
+ return WERR_INVALID_PRINTER_NAME;
+ }
+
+ *_server_name = server_unc;
+ *_object_name = object;
+ *_object_type = NTPTR_HANDLE_PORT;
+ return WERR_OK;
+ } else if (strncmp(object, XCV_MONITOR, strlen(XCV_MONITOR)) == 0) {
+ object += strlen(XCV_MONITOR);
+
+ /* just "" is invalid */
+ if (strequal(object, "")) {
+ return WERR_INVALID_PRINTER_NAME;
+ }
+
+ *_server_name = server_unc;
+ *_object_name = object;
+ *_object_type = NTPTR_HANDLE_MONITOR;
+ return WERR_OK;
+ }
+
+ *_server_name = server_unc;
+ *_object_name = object;
+ *_object_type = NTPTR_HANDLE_PRINTER;
+ return WERR_OK;
+}
+
+/*
+ * Check server_name is:
+ * - "" , functions that don't allow "",
+ * should check that on their own, before calling this function
+ * - our name (only netbios yet, TODO: need to test dns name!)
+ * - our ip address of the current use socket
+ * otherwise return WERR_INVALID_PRINTER_NAME
+ */
+static WERROR dcesrv_spoolss_check_server_name(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ const char *server_name)
+{
+ bool ret;
+ struct socket_address *myaddr;
+ const char **aliases;
+ int i;
+
+ /* NULL is ok */
+ if (!server_name) return WERR_OK;
+
+ /* "" is ok */
+ ret = strequal("",server_name);
+ if (ret) return WERR_OK;
+
+ /* just "\\" is invalid */
+ if (strequal("\\\\", server_name)) {
+ return WERR_INVALID_PRINTER_NAME;
+ }
+
+ /* then we need "\\" */
+ if (strncmp("\\\\", server_name, 2) != 0) {
+ return WERR_INVALID_PRINTER_NAME;
+ }
+
+ server_name += 2;
+
+ /* NETBIOS NAME is ok */
+ ret = strequal(lp_netbios_name(dce_call->conn->dce_ctx->lp_ctx), server_name);
+ if (ret) return WERR_OK;
+
+ aliases = lp_netbios_aliases(dce_call->conn->dce_ctx->lp_ctx);
+
+ for (i=0; aliases && aliases[i]; i++) {
+ if (strequal(aliases[i], server_name)) {
+ return WERR_OK;
+ }
+ }
+
+ /* DNS NAME is ok
+ * TODO: we need to check if aliases are also ok
+ */
+ if (lp_realm(dce_call->conn->dce_ctx->lp_ctx)) {
+ char *str;
+
+ str = talloc_asprintf(mem_ctx, "%s.%s",
+ lp_netbios_name(dce_call->conn->dce_ctx->lp_ctx),
+ lp_realm(dce_call->conn->dce_ctx->lp_ctx));
+ W_ERROR_HAVE_NO_MEMORY(str);
+
+ ret = strequal(str, server_name);
+ talloc_free(str);
+ if (ret) return WERR_OK;
+ }
+
+ myaddr = dcesrv_connection_get_my_addr(dce_call->conn, mem_ctx);
+ W_ERROR_HAVE_NO_MEMORY(myaddr);
+
+ ret = strequal(myaddr->addr, server_name);
+ talloc_free(myaddr);
+ if (ret) return WERR_OK;
+
+ return WERR_INVALID_PRINTER_NAME;
+}
+
+static NTSTATUS dcerpc_spoolss_bind(struct dcesrv_call_state *dce_call, const struct dcesrv_interface *iface)
+{
+ NTSTATUS status;
+ struct ntptr_context *ntptr;
+
+ status = ntptr_init_context(dce_call->context, dce_call->conn->event_ctx, dce_call->conn->dce_ctx->lp_ctx,
+ lp_ntptr_providor(dce_call->conn->dce_ctx->lp_ctx), &ntptr);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ dce_call->context->private_data = ntptr;
+
+ return NT_STATUS_OK;
+}
+
+#define DCESRV_INTERFACE_SPOOLSS_BIND dcerpc_spoolss_bind
+
+/*
+ spoolss_EnumPrinters
+*/
+static WERROR dcesrv_spoolss_EnumPrinters(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_EnumPrinters *r)
+{
+ struct ntptr_context *ntptr = talloc_get_type(dce_call->context->private_data, struct ntptr_context);
+ WERROR status;
+ struct smb_iconv_convenience *ic = lp_iconv_convenience(ntptr->lp_ctx);
+
+ status = dcesrv_spoolss_check_server_name(dce_call, mem_ctx, r->in.server);
+ W_ERROR_NOT_OK_RETURN(status);
+
+ status = ntptr_EnumPrinters(ntptr, mem_ctx, r);
+ W_ERROR_NOT_OK_RETURN(status);
+
+ *r->out.needed = SPOOLSS_BUFFER_UNION_ARRAY(spoolss_EnumPrinters, ic, *r->out.info, r->in.level, *r->out.count);
+ *r->out.info = SPOOLSS_BUFFER_OK(*r->out.info, NULL);
+ *r->out.count = SPOOLSS_BUFFER_OK(*r->out.count, 0);
+ return SPOOLSS_BUFFER_OK(WERR_OK, WERR_INSUFFICIENT_BUFFER);
+}
+
+static WERROR dcesrv_spoolss_OpenPrinterEx(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_OpenPrinterEx *r);
+/*
+ spoolss_OpenPrinter
+*/
+static WERROR dcesrv_spoolss_OpenPrinter(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_OpenPrinter *r)
+{
+ WERROR status;
+ struct spoolss_OpenPrinterEx *r2;
+
+ r2 = talloc(mem_ctx, struct spoolss_OpenPrinterEx);
+ W_ERROR_HAVE_NO_MEMORY(r2);
+
+ r2->in.printername = r->in.printername;
+ r2->in.datatype = r->in.datatype;
+ r2->in.devmode_ctr = r->in.devmode_ctr;
+ r2->in.access_mask = r->in.access_mask;
+ r2->in.level = 1;
+ r2->in.userlevel.level1 = NULL;
+
+ r2->out.handle = r->out.handle;
+
+ /* TODO: we should take care about async replies here,
+ if spoolss_OpenPrinterEx() would be async!
+ */
+ status = dcesrv_spoolss_OpenPrinterEx(dce_call, mem_ctx, r2);
+
+ r->out.handle = r2->out.handle;
+
+ return status;
+}
+
+
+/*
+ spoolss_SetJob
+*/
+static WERROR dcesrv_spoolss_SetJob(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_SetJob *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_GetJob
+*/
+static WERROR dcesrv_spoolss_GetJob(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_GetJob *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_EnumJobs
+*/
+static WERROR dcesrv_spoolss_EnumJobs(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_EnumJobs *r)
+{
+ return WERR_OK;
+}
+
+
+/*
+ spoolss_AddPrinter
+*/
+static WERROR dcesrv_spoolss_AddPrinter(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_AddPrinter *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_DeletePrinter
+*/
+static WERROR dcesrv_spoolss_DeletePrinter(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_DeletePrinter *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_SetPrinter
+*/
+static WERROR dcesrv_spoolss_SetPrinter(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_SetPrinter *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_GetPrinter
+*/
+static WERROR dcesrv_spoolss_GetPrinter(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_GetPrinter *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_AddPrinterDriver
+*/
+static WERROR dcesrv_spoolss_AddPrinterDriver(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_AddPrinterDriver *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_EnumPrinterDrivers
+*/
+static WERROR dcesrv_spoolss_EnumPrinterDrivers(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_EnumPrinterDrivers *r)
+{
+ struct ntptr_context *ntptr = talloc_get_type(dce_call->context->private_data, struct ntptr_context);
+ WERROR status;
+ struct smb_iconv_convenience *ic = lp_iconv_convenience(ntptr->lp_ctx);
+
+ status = dcesrv_spoolss_check_server_name(dce_call, mem_ctx, r->in.server);
+ W_ERROR_NOT_OK_RETURN(status);
+
+ status = ntptr_EnumPrinterDrivers(ntptr, mem_ctx, r);
+ W_ERROR_NOT_OK_RETURN(status);
+
+ *r->out.needed = SPOOLSS_BUFFER_UNION_ARRAY(spoolss_EnumPrinterDrivers, ic, *r->out.info, r->in.level, *r->out.count);
+ *r->out.info = SPOOLSS_BUFFER_OK(*r->out.info, NULL);
+ *r->out.count = SPOOLSS_BUFFER_OK(*r->out.count, 0);
+ return SPOOLSS_BUFFER_OK(WERR_OK, WERR_INSUFFICIENT_BUFFER);
+}
+
+
+/*
+ spoolss_GetPrinterDriver
+*/
+static WERROR dcesrv_spoolss_GetPrinterDriver(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_GetPrinterDriver *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_GetPrinterDriverDirectory
+*/
+static WERROR dcesrv_spoolss_GetPrinterDriverDirectory(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_GetPrinterDriverDirectory *r)
+{
+ struct ntptr_context *ntptr = talloc_get_type(dce_call->context->private_data, struct ntptr_context);
+ WERROR status;
+ struct smb_iconv_convenience *ic = lp_iconv_convenience(ntptr->lp_ctx);
+
+ status = dcesrv_spoolss_check_server_name(dce_call, mem_ctx, r->in.server);
+ W_ERROR_NOT_OK_RETURN(status);
+
+ status = ntptr_GetPrinterDriverDirectory(ntptr, mem_ctx, r);
+ W_ERROR_NOT_OK_RETURN(status);
+
+ *r->out.needed = SPOOLSS_BUFFER_UNION(spoolss_DriverDirectoryInfo, ic, r->out.info, r->in.level);
+ r->out.info = SPOOLSS_BUFFER_OK(r->out.info, NULL);
+ return SPOOLSS_BUFFER_OK(WERR_OK, WERR_INSUFFICIENT_BUFFER);
+}
+
+
+/*
+ spoolss_DeletePrinterDriver
+*/
+static WERROR dcesrv_spoolss_DeletePrinterDriver(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_DeletePrinterDriver *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_AddPrintProcessor
+*/
+static WERROR dcesrv_spoolss_AddPrintProcessor(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_AddPrintProcessor *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_EnumPrintProcessors
+*/
+static WERROR dcesrv_spoolss_EnumPrintProcessors(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_EnumPrintProcessors *r)
+{
+ return WERR_OK;
+}
+
+
+/*
+ spoolss_GetPrintProcessorDirectory
+*/
+static WERROR dcesrv_spoolss_GetPrintProcessorDirectory(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_GetPrintProcessorDirectory *r)
+{
+ struct ntptr_context *ntptr = talloc_get_type(dce_call->context->private_data, struct ntptr_context);
+ WERROR status;
+ struct smb_iconv_convenience *ic = lp_iconv_convenience(ntptr->lp_ctx);
+
+ status = dcesrv_spoolss_check_server_name(dce_call, mem_ctx, r->in.server);
+ W_ERROR_NOT_OK_RETURN(status);
+
+ status = ntptr_GetPrintProcessorDirectory(ntptr, mem_ctx, r);
+ W_ERROR_NOT_OK_RETURN(status);
+
+ *r->out.needed = SPOOLSS_BUFFER_UNION(spoolss_PrintProcessorDirectoryInfo, ic, r->out.info, r->in.level);
+ r->out.info = SPOOLSS_BUFFER_OK(r->out.info, NULL);
+ return SPOOLSS_BUFFER_OK(WERR_OK, WERR_INSUFFICIENT_BUFFER);
+}
+
+
+/*
+ spoolss_StartDocPrinter
+*/
+static WERROR dcesrv_spoolss_StartDocPrinter(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_StartDocPrinter *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_StartPagePrinter
+*/
+static WERROR dcesrv_spoolss_StartPagePrinter(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_StartPagePrinter *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_WritePrinter
+*/
+static WERROR dcesrv_spoolss_WritePrinter(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_WritePrinter *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_EndPagePrinter
+*/
+static WERROR dcesrv_spoolss_EndPagePrinter(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_EndPagePrinter *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_AbortPrinter
+*/
+static WERROR dcesrv_spoolss_AbortPrinter(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_AbortPrinter *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_ReadPrinter
+*/
+static WERROR dcesrv_spoolss_ReadPrinter(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_ReadPrinter *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_EndDocPrinter
+*/
+static WERROR dcesrv_spoolss_EndDocPrinter(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_EndDocPrinter *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_AddJob
+*/
+static WERROR dcesrv_spoolss_AddJob(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_AddJob *r)
+{
+ if (r->in.level != 1) {
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ return WERR_INVALID_PARAM;
+}
+
+
+/*
+ spoolss_ScheduleJob
+*/
+static WERROR dcesrv_spoolss_ScheduleJob(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_ScheduleJob *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_GetPrinterData
+*/
+static WERROR dcesrv_spoolss_GetPrinterData(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_GetPrinterData *r)
+{
+ struct ntptr_GenericHandle *handle;
+ struct dcesrv_handle *h;
+ WERROR status;
+ struct smb_iconv_convenience *ic = lp_iconv_convenience(dce_call->conn->dce_ctx->lp_ctx);
+
+ DCESRV_PULL_HANDLE_WERR(h, r->in.handle, DCESRV_HANDLE_ANY);
+ handle = talloc_get_type(h->data, struct ntptr_GenericHandle);
+ if (!handle)
+ return WERR_BADFID;
+
+ r->out.type = talloc_zero(mem_ctx, enum winreg_Type);
+ W_ERROR_HAVE_NO_MEMORY(r->out.type);
+
+ r->out.needed = talloc_zero(mem_ctx, uint32_t);
+ W_ERROR_HAVE_NO_MEMORY(r->out.needed);
+
+ r->out.data = talloc_zero(mem_ctx, union spoolss_PrinterData);
+ W_ERROR_HAVE_NO_MEMORY(r->out.data);
+
+ switch (handle->type) {
+ case NTPTR_HANDLE_SERVER:
+ status = ntptr_GetPrintServerData(handle, mem_ctx, r);
+ break;
+ default:
+ status = WERR_FOOBAR;
+ break;
+ }
+
+ W_ERROR_NOT_OK_RETURN(status);
+
+ *r->out.needed = ndr_size_spoolss_PrinterData(r->out.data, *r->out.type, ic, 0);
+ *r->out.type = SPOOLSS_BUFFER_OK(*r->out.type, REG_NONE);
+ r->out.data = SPOOLSS_BUFFER_OK(r->out.data, r->out.data);
+ return SPOOLSS_BUFFER_OK(WERR_OK, WERR_MORE_DATA);
+}
+
+
+/*
+ spoolss_SetPrinterData
+*/
+static WERROR dcesrv_spoolss_SetPrinterData(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_SetPrinterData *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_WaitForPrinterChange
+*/
+static WERROR dcesrv_spoolss_WaitForPrinterChange(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_WaitForPrinterChange *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_ClosePrinter
+*/
+static WERROR dcesrv_spoolss_ClosePrinter(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_ClosePrinter *r)
+{
+ struct dcesrv_handle *h;
+
+ *r->out.handle = *r->in.handle;
+
+ DCESRV_PULL_HANDLE_WERR(h, r->in.handle, DCESRV_HANDLE_ANY);
+
+ talloc_free(h);
+
+ ZERO_STRUCTP(r->out.handle);
+
+ return WERR_OK;
+}
+
+
+/*
+ spoolss_AddForm
+*/
+static WERROR dcesrv_spoolss_AddForm(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_AddForm *r)
+{
+ struct ntptr_GenericHandle *handle;
+ struct dcesrv_handle *h;
+ WERROR status;
+
+ DCESRV_PULL_HANDLE_WERR(h, r->in.handle, DCESRV_HANDLE_ANY);
+ handle = talloc_get_type(h->data, struct ntptr_GenericHandle);
+ if (!handle)
+ return WERR_BADFID;
+
+ switch (handle->type) {
+ case NTPTR_HANDLE_SERVER:
+ status = ntptr_AddPrintServerForm(handle, mem_ctx, r);
+ W_ERROR_NOT_OK_RETURN(status);
+ break;
+ case NTPTR_HANDLE_PRINTER:
+ status = ntptr_AddPrinterForm(handle, mem_ctx, r);
+ W_ERROR_NOT_OK_RETURN(status);
+ break;
+ default:
+ return WERR_FOOBAR;
+ }
+
+ return WERR_OK;
+}
+
+
+/*
+ spoolss_DeleteForm
+*/
+static WERROR dcesrv_spoolss_DeleteForm(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_DeleteForm *r)
+{
+ struct ntptr_GenericHandle *handle;
+ struct dcesrv_handle *h;
+ WERROR status;
+
+ DCESRV_PULL_HANDLE_WERR(h, r->in.handle, DCESRV_HANDLE_ANY);
+ handle = talloc_get_type(h->data, struct ntptr_GenericHandle);
+ if (!handle)
+ return WERR_BADFID;
+
+ switch (handle->type) {
+ case NTPTR_HANDLE_SERVER:
+ status = ntptr_DeletePrintServerForm(handle, mem_ctx, r);
+ W_ERROR_NOT_OK_RETURN(status);
+ break;
+ case NTPTR_HANDLE_PRINTER:
+ status = ntptr_DeletePrinterForm(handle, mem_ctx, r);
+ W_ERROR_NOT_OK_RETURN(status);
+ break;
+ default:
+ return WERR_FOOBAR;
+ }
+
+ return WERR_OK;
+}
+
+
+/*
+ spoolss_GetForm
+*/
+static WERROR dcesrv_spoolss_GetForm(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_GetForm *r)
+{
+ struct ntptr_GenericHandle *handle;
+ struct dcesrv_handle *h;
+ WERROR status;
+ struct smb_iconv_convenience *ic = lp_iconv_convenience(dce_call->conn->dce_ctx->lp_ctx);
+
+ DCESRV_PULL_HANDLE_WERR(h, r->in.handle, DCESRV_HANDLE_ANY);
+ handle = talloc_get_type(h->data, struct ntptr_GenericHandle);
+ if (!handle)
+ return WERR_BADFID;
+
+ switch (handle->type) {
+ case NTPTR_HANDLE_SERVER:
+ /*
+ * stupid, but w2k3 returns WERR_BADFID here?
+ */
+ return WERR_BADFID;
+ case NTPTR_HANDLE_PRINTER:
+ status = ntptr_GetPrinterForm(handle, mem_ctx, r);
+ W_ERROR_NOT_OK_RETURN(status);
+ break;
+ default:
+ return WERR_FOOBAR;
+ }
+
+ *r->out.needed = SPOOLSS_BUFFER_UNION(spoolss_FormInfo, ic, r->out.info, r->in.level);
+ r->out.info = SPOOLSS_BUFFER_OK(r->out.info, NULL);
+ return SPOOLSS_BUFFER_OK(WERR_OK, WERR_INSUFFICIENT_BUFFER);
+}
+
+
+/*
+ spoolss_SetForm
+*/
+static WERROR dcesrv_spoolss_SetForm(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_SetForm *r)
+{
+ struct ntptr_GenericHandle *handle;
+ struct dcesrv_handle *h;
+ WERROR status;
+
+ DCESRV_PULL_HANDLE_WERR(h, r->in.handle, DCESRV_HANDLE_ANY);
+ handle = talloc_get_type(h->data, struct ntptr_GenericHandle);
+ if (!handle)
+ return WERR_BADFID;
+
+ switch (handle->type) {
+ case NTPTR_HANDLE_SERVER:
+ status = ntptr_SetPrintServerForm(handle, mem_ctx, r);
+ W_ERROR_NOT_OK_RETURN(status);
+ break;
+ case NTPTR_HANDLE_PRINTER:
+ status = ntptr_SetPrinterForm(handle, mem_ctx, r);
+ W_ERROR_NOT_OK_RETURN(status);
+ break;
+ default:
+ return WERR_FOOBAR;
+ }
+
+ return WERR_OK;
+}
+
+
+/*
+ spoolss_EnumForms
+*/
+static WERROR dcesrv_spoolss_EnumForms(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_EnumForms *r)
+{
+ struct ntptr_GenericHandle *handle;
+ struct dcesrv_handle *h;
+ WERROR status;
+ struct smb_iconv_convenience *ic = lp_iconv_convenience(dce_call->conn->dce_ctx->lp_ctx);
+
+ DCESRV_PULL_HANDLE_WERR(h, r->in.handle, DCESRV_HANDLE_ANY);
+ handle = talloc_get_type(h->data, struct ntptr_GenericHandle);
+ if (!handle)
+ return WERR_BADFID;
+
+ switch (handle->type) {
+ case NTPTR_HANDLE_SERVER:
+ status = ntptr_EnumPrintServerForms(handle, mem_ctx, r);
+ W_ERROR_NOT_OK_RETURN(status);
+ break;
+ case NTPTR_HANDLE_PRINTER:
+ status = ntptr_EnumPrinterForms(handle, mem_ctx, r);
+ W_ERROR_NOT_OK_RETURN(status);
+ break;
+ default:
+ return WERR_FOOBAR;
+ }
+
+ *r->out.needed = SPOOLSS_BUFFER_UNION_ARRAY(spoolss_EnumForms, ic, *r->out.info, r->in.level, *r->out.count);
+ *r->out.info = SPOOLSS_BUFFER_OK(*r->out.info, NULL);
+ *r->out.count = SPOOLSS_BUFFER_OK(*r->out.count, 0);
+ return SPOOLSS_BUFFER_OK(WERR_OK, WERR_INSUFFICIENT_BUFFER);
+}
+
+
+/*
+ spoolss_EnumPorts
+*/
+static WERROR dcesrv_spoolss_EnumPorts(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_EnumPorts *r)
+{
+ struct ntptr_context *ntptr = talloc_get_type(dce_call->context->private_data, struct ntptr_context);
+ WERROR status;
+ struct smb_iconv_convenience *ic = lp_iconv_convenience(ntptr->lp_ctx);
+
+ status = dcesrv_spoolss_check_server_name(dce_call, mem_ctx, r->in.servername);
+ W_ERROR_NOT_OK_RETURN(status);
+
+ status = ntptr_EnumPorts(ntptr, mem_ctx, r);
+ W_ERROR_NOT_OK_RETURN(status);
+
+ *r->out.needed = SPOOLSS_BUFFER_UNION_ARRAY(spoolss_EnumPorts, ic, *r->out.info, r->in.level, *r->out.count);
+ *r->out.info = SPOOLSS_BUFFER_OK(*r->out.info, NULL);
+ *r->out.count = SPOOLSS_BUFFER_OK(*r->out.count, 0);
+ return SPOOLSS_BUFFER_OK(WERR_OK, WERR_INSUFFICIENT_BUFFER);
+}
+
+
+/*
+ spoolss_EnumMonitors
+*/
+static WERROR dcesrv_spoolss_EnumMonitors(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_EnumMonitors *r)
+{
+ struct ntptr_context *ntptr = talloc_get_type(dce_call->context->private_data, struct ntptr_context);
+ WERROR status;
+ struct smb_iconv_convenience *ic = lp_iconv_convenience(ntptr->lp_ctx);
+
+ status = dcesrv_spoolss_check_server_name(dce_call, mem_ctx, r->in.servername);
+ W_ERROR_NOT_OK_RETURN(status);
+
+ status = ntptr_EnumMonitors(ntptr, mem_ctx, r);
+ W_ERROR_NOT_OK_RETURN(status);
+
+ *r->out.needed = SPOOLSS_BUFFER_UNION_ARRAY(spoolss_EnumMonitors, ic, *r->out.info, r->in.level, *r->out.count);
+ *r->out.info = SPOOLSS_BUFFER_OK(*r->out.info, NULL);
+ *r->out.count = SPOOLSS_BUFFER_OK(*r->out.count, 0);
+ return SPOOLSS_BUFFER_OK(WERR_OK, WERR_INSUFFICIENT_BUFFER);
+}
+
+
+/*
+ spoolss_AddPort
+*/
+static WERROR dcesrv_spoolss_AddPort(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_AddPort *r)
+{
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/*
+ spoolss_ConfigurePort
+*/
+static WERROR dcesrv_spoolss_ConfigurePort(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_ConfigurePort *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_DeletePort
+*/
+static WERROR dcesrv_spoolss_DeletePort(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_DeletePort *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_CreatePrinterIC
+*/
+static WERROR dcesrv_spoolss_CreatePrinterIC(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_CreatePrinterIC *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_PlayGDIScriptOnPrinterIC
+*/
+static WERROR dcesrv_spoolss_PlayGDIScriptOnPrinterIC(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_PlayGDIScriptOnPrinterIC *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_DeletePrinterIC
+*/
+static WERROR dcesrv_spoolss_DeletePrinterIC(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_DeletePrinterIC *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_AddPrinterConnection
+*/
+static WERROR dcesrv_spoolss_AddPrinterConnection(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_AddPrinterConnection *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_DeletePrinterConnection
+*/
+static WERROR dcesrv_spoolss_DeletePrinterConnection(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_DeletePrinterConnection *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_PrinterMessageBox
+*/
+static WERROR dcesrv_spoolss_PrinterMessageBox(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_PrinterMessageBox *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_AddMonitor
+*/
+static WERROR dcesrv_spoolss_AddMonitor(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_AddMonitor *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_DeleteMonitor
+*/
+static WERROR dcesrv_spoolss_DeleteMonitor(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_DeleteMonitor *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_DeletePrintProcessor
+*/
+static WERROR dcesrv_spoolss_DeletePrintProcessor(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_DeletePrintProcessor *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_AddPrintProvidor
+*/
+static WERROR dcesrv_spoolss_AddPrintProvidor(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_AddPrintProvidor *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_DeletePrintProvidor
+*/
+static WERROR dcesrv_spoolss_DeletePrintProvidor(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_DeletePrintProvidor *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_EnumPrintProcDataTypes
+*/
+static WERROR dcesrv_spoolss_EnumPrintProcDataTypes(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_EnumPrintProcDataTypes *r)
+{
+ return WERR_OK;
+}
+
+
+/*
+ spoolss_ResetPrinter
+*/
+static WERROR dcesrv_spoolss_ResetPrinter(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_ResetPrinter *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_GetPrinterDriver2
+*/
+static WERROR dcesrv_spoolss_GetPrinterDriver2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_GetPrinterDriver2 *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_FindFirstPrinterChangeNotification
+*/
+static WERROR dcesrv_spoolss_FindFirstPrinterChangeNotification(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_FindFirstPrinterChangeNotification *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_FindNextPrinterChangeNotification
+*/
+static WERROR dcesrv_spoolss_FindNextPrinterChangeNotification(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_FindNextPrinterChangeNotification *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_FindClosePrinterNotify
+*/
+static WERROR dcesrv_spoolss_FindClosePrinterNotify(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_FindClosePrinterNotify *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_RouterFindFirstPrinterChangeNotificationOld
+*/
+static WERROR dcesrv_spoolss_RouterFindFirstPrinterChangeNotificationOld(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_RouterFindFirstPrinterChangeNotificationOld *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_ReplyOpenPrinter
+*/
+static WERROR dcesrv_spoolss_ReplyOpenPrinter(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_ReplyOpenPrinter *r)
+{
+ struct dcesrv_handle *handle;
+
+ handle = dcesrv_handle_new(dce_call->context, SPOOLSS_NOTIFY);
+ W_ERROR_HAVE_NO_MEMORY(handle);
+
+ /* For now, just return a handle */
+
+ *r->out.handle = handle->wire_handle;
+
+ return WERR_OK;
+}
+
+
+/*
+ spoolss_RouterReplyPrinter
+*/
+static WERROR dcesrv_spoolss_RouterReplyPrinter(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_RouterReplyPrinter *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_ReplyClosePrinter
+*/
+static WERROR dcesrv_spoolss_ReplyClosePrinter(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_ReplyClosePrinter *r)
+{
+ struct dcesrv_handle *handle;
+
+ DCESRV_PULL_HANDLE_WERR(handle, r->in.handle, SPOOLSS_NOTIFY);
+
+ talloc_free(handle);
+
+ ZERO_STRUCTP(r->out.handle);
+
+ return WERR_OK;
+}
+
+/*
+ spoolss_AddPortEx
+*/
+static WERROR dcesrv_spoolss_AddPortEx(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_AddPortEx *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_RouterFindFirstPrinterChangeNotification
+*/
+static WERROR dcesrv_spoolss_RouterFindFirstPrinterChangeNotification(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_RouterFindFirstPrinterChangeNotification *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_SpoolerInit
+*/
+static WERROR dcesrv_spoolss_SpoolerInit(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_SpoolerInit *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_ResetPrinterEx
+*/
+static WERROR dcesrv_spoolss_ResetPrinterEx(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_ResetPrinterEx *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_RemoteFindFirstPrinterChangeNotifyEx
+*/
+static WERROR dcesrv_spoolss_RemoteFindFirstPrinterChangeNotifyEx(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_RemoteFindFirstPrinterChangeNotifyEx *r)
+{
+ struct dcerpc_pipe *p;
+ struct dcerpc_binding *binding;
+ NTSTATUS status;
+ struct spoolss_ReplyOpenPrinter rop;
+ struct cli_credentials *creds;
+ struct policy_handle notify_handle;
+
+ DEBUG(2, ("Received RFFPCNex from %s\n", r->in.local_machine));
+
+ /*
+ * TODO: for now just open a connection to the client and drop it again
+ * to keep the w2k3 PrintServer
+ * happy to allow to open the Add Printer GUI
+ * and the torture suite passing
+ */
+
+ binding = talloc_zero(mem_ctx, struct dcerpc_binding);
+
+ binding->transport = NCACN_NP;
+ if (strncmp(r->in.local_machine, "\\\\", 2))
+ return WERR_INVALID_COMPUTERNAME;
+ binding->host = r->in.local_machine+2;
+
+ creds = cli_credentials_init_anon(mem_ctx); /* FIXME: Use machine credentials instead ? */
+
+ status = dcerpc_pipe_connect_b(mem_ctx, &p, binding, &ndr_table_spoolss,
+ creds, dce_call->event_ctx,
+ dce_call->conn->dce_ctx->lp_ctx);
+
+ if (NT_STATUS_IS_ERR(status)) {
+ DEBUG(0, ("unable to call back to %s\n", r->in.local_machine));
+ return WERR_SERVER_UNAVAILABLE;
+ }
+
+ ZERO_STRUCT(rop);
+ rop.in.server_name = lp_netbios_name(dce_call->conn->dce_ctx->lp_ctx);
+ W_ERROR_HAVE_NO_MEMORY(rop.in.server_name);
+ rop.in.printer_local = 0;
+ rop.in.type = REG_NONE;
+ rop.in.bufsize = 0;
+ rop.in.buffer = NULL;
+ rop.out.handle = &notify_handle;
+
+ status = dcerpc_spoolss_ReplyOpenPrinter(p, mem_ctx, &rop);
+ if (NT_STATUS_IS_ERR(status)) {
+ DEBUG(0, ("unable to open remote printer %s\n",
+ r->in.local_machine));
+ return WERR_SERVER_UNAVAILABLE;
+ }
+
+ talloc_free(p);
+
+ return WERR_OK;
+}
+
+
+/*
+ spoolss_RouterReplyPrinterEx
+*/
+static WERROR dcesrv_spoolss_RouterReplyPrinterEx(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_RouterReplyPrinterEx *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_RouterRefreshPrinterChangeNotify
+*/
+static WERROR dcesrv_spoolss_RouterRefreshPrinterChangeNotify(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_RouterRefreshPrinterChangeNotify *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_44
+*/
+static WERROR dcesrv_spoolss_44(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_44 *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+/*
+ spoolss_OpenPrinterEx
+*/
+static WERROR dcesrv_spoolss_OpenPrinterEx(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_OpenPrinterEx *r)
+{
+ struct ntptr_context *ntptr = talloc_get_type(dce_call->context->private_data, struct ntptr_context);
+ struct ntptr_GenericHandle *handle;
+ struct dcesrv_handle *h;
+ const char *server;
+ const char *object;
+ enum ntptr_HandleType type;
+ WERROR status;
+
+ ZERO_STRUCTP(r->out.handle);
+
+ status = dcesrv_spoolss_parse_printer_name(mem_ctx, r->in.printername, &server, &object, &type);
+ W_ERROR_NOT_OK_RETURN(status);
+
+ status = dcesrv_spoolss_check_server_name(dce_call, mem_ctx, server);
+ W_ERROR_NOT_OK_RETURN(status);
+
+ switch (type) {
+ case NTPTR_HANDLE_SERVER:
+ status = ntptr_OpenPrintServer(ntptr, mem_ctx, r, server, &handle);
+ W_ERROR_NOT_OK_RETURN(status);
+ break;
+ case NTPTR_HANDLE_PORT:
+ status = ntptr_OpenPort(ntptr, mem_ctx, r, object, &handle);
+ W_ERROR_NOT_OK_RETURN(status);
+ break;
+ case NTPTR_HANDLE_MONITOR:
+ status = ntptr_OpenMonitor(ntptr, mem_ctx, r, object, &handle);
+ W_ERROR_NOT_OK_RETURN(status);
+ break;
+ case NTPTR_HANDLE_PRINTER:
+ status = ntptr_OpenPrinter(ntptr, mem_ctx, r, object, &handle);
+ W_ERROR_NOT_OK_RETURN(status);
+ break;
+ default:
+ return WERR_FOOBAR;
+ }
+
+ h = dcesrv_handle_new(dce_call->context, handle->type);
+ W_ERROR_HAVE_NO_MEMORY(h);
+
+ h->data = talloc_steal(h, handle);
+
+ *r->out.handle = h->wire_handle;
+
+ return WERR_OK;
+}
+
+/*
+ spoolss_AddPrinterEx
+*/
+static WERROR dcesrv_spoolss_AddPrinterEx(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_AddPrinterEx *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_47
+*/
+static WERROR dcesrv_spoolss_47(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_47 *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_EnumPrinterData
+*/
+static WERROR dcesrv_spoolss_EnumPrinterData(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_EnumPrinterData *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_DeletePrinterData
+*/
+static WERROR dcesrv_spoolss_DeletePrinterData(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_DeletePrinterData *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_4a
+*/
+static WERROR dcesrv_spoolss_4a(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_4a *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_4b
+*/
+static WERROR dcesrv_spoolss_4b(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_4b *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_4c
+*/
+static WERROR dcesrv_spoolss_4c(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_4c *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_SetPrinterDataEx
+*/
+static WERROR dcesrv_spoolss_SetPrinterDataEx(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_SetPrinterDataEx *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_GetPrinterDataEx
+*/
+static WERROR dcesrv_spoolss_GetPrinterDataEx(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_GetPrinterDataEx *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_EnumPrinterDataEx
+*/
+static WERROR dcesrv_spoolss_EnumPrinterDataEx(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_EnumPrinterDataEx *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_EnumPrinterKey
+*/
+static WERROR dcesrv_spoolss_EnumPrinterKey(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_EnumPrinterKey *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_DeletePrinterDataEx
+*/
+static WERROR dcesrv_spoolss_DeletePrinterDataEx(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_DeletePrinterDataEx *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_DeletePrinterKey
+*/
+static WERROR dcesrv_spoolss_DeletePrinterKey(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_DeletePrinterKey *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_53
+*/
+static WERROR dcesrv_spoolss_53(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_53 *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_DeletePrinterDriverEx
+*/
+static WERROR dcesrv_spoolss_DeletePrinterDriverEx(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_DeletePrinterDriverEx *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_55
+*/
+static WERROR dcesrv_spoolss_55(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_55 *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_56
+*/
+static WERROR dcesrv_spoolss_56(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_56 *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_57
+*/
+static WERROR dcesrv_spoolss_57(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_57 *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_XcvData
+*/
+static WERROR dcesrv_spoolss_XcvData(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_XcvData *r)
+{
+ struct ntptr_GenericHandle *handle;
+ struct dcesrv_handle *h;
+ WERROR status;
+
+ DCESRV_PULL_HANDLE_WERR(h, r->in.handle, DCESRV_HANDLE_ANY);
+ handle = talloc_get_type(h->data, struct ntptr_GenericHandle);
+
+ switch (handle->type) {
+ case NTPTR_HANDLE_SERVER:
+ status = ntptr_XcvDataPrintServer(handle, mem_ctx, r);
+ W_ERROR_NOT_OK_RETURN(status);
+ break;
+ case NTPTR_HANDLE_PRINTER:
+ status = ntptr_XcvDataPrinter(handle, mem_ctx, r);
+ W_ERROR_NOT_OK_RETURN(status);
+ break;
+ case NTPTR_HANDLE_PORT:
+ status = ntptr_XcvDataPort(handle, mem_ctx, r);
+ W_ERROR_NOT_OK_RETURN(status);
+ break;
+ case NTPTR_HANDLE_MONITOR:
+ status = ntptr_XcvDataMonitor(handle, mem_ctx, r);
+ W_ERROR_NOT_OK_RETURN(status);
+ break;
+ default:
+ return WERR_FOOBAR;
+ }
+
+ /* TODO: handle the buffer sizes here! */
+ return WERR_OK;
+}
+
+
+/*
+ spoolss_AddPrinterDriverEx
+*/
+static WERROR dcesrv_spoolss_AddPrinterDriverEx(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_AddPrinterDriverEx *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_5a
+*/
+static WERROR dcesrv_spoolss_5a(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_5a *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_5b
+*/
+static WERROR dcesrv_spoolss_5b(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_5b *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_5c
+*/
+static WERROR dcesrv_spoolss_5c(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_5c *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_5d
+*/
+static WERROR dcesrv_spoolss_5d(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_5d *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_5e
+*/
+static WERROR dcesrv_spoolss_5e(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_5e *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ spoolss_5f
+*/
+static WERROR dcesrv_spoolss_5f(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct spoolss_5f *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_spoolss_s.c"
diff --git a/source4/rpc_server/srvsvc/dcesrv_srvsvc.c b/source4/rpc_server/srvsvc/dcesrv_srvsvc.c
new file mode 100644
index 0000000000..f33c49aa4e
--- /dev/null
+++ b/source4/rpc_server/srvsvc/dcesrv_srvsvc.c
@@ -0,0 +1,2323 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the srvsvc pipe
+
+ Copyright (C) Stefan (metze) Metzmacher 2004-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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "ntvfs/ntvfs.h"
+#include "rpc_server/dcerpc_server.h"
+#include "librpc/gen_ndr/ndr_srvsvc.h"
+#include "rpc_server/common/common.h"
+#include "rpc_server/common/proto.h"
+#include "auth/auth.h"
+#include "libcli/security/security.h"
+#include "system/time.h"
+#include "rpc_server/srvsvc/proto.h"
+#include "param/param.h"
+
+#define SRVSVC_CHECK_ADMIN_ACCESS do { \
+ struct security_token *t = dce_call->conn->auth_state.session_info->security_token; \
+ if (!security_token_has_builtin_administrators(t) && \
+ !security_token_has_sid_string(t, SID_BUILTIN_SERVER_OPERATORS)) { \
+ return WERR_ACCESS_DENIED; \
+ } \
+} while (0)
+
+/*
+ srvsvc_NetCharDevEnum
+*/
+static WERROR dcesrv_srvsvc_NetCharDevEnum(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetCharDevEnum *r)
+{
+ *r->out.totalentries = 0;
+
+ switch (r->in.info_ctr->level) {
+ case 0:
+ r->out.info_ctr->ctr.ctr0 = talloc(mem_ctx, struct srvsvc_NetCharDevCtr0);
+ W_ERROR_HAVE_NO_MEMORY(r->out.info_ctr->ctr.ctr0);
+
+ r->out.info_ctr->ctr.ctr0->count = 0;
+ r->out.info_ctr->ctr.ctr0->array = NULL;
+
+ return WERR_NOT_SUPPORTED;
+
+ case 1:
+ r->out.info_ctr->ctr.ctr1 = talloc(mem_ctx, struct srvsvc_NetCharDevCtr1);
+ W_ERROR_HAVE_NO_MEMORY(r->out.info_ctr->ctr.ctr1);
+
+ r->out.info_ctr->ctr.ctr1->count = 0;
+ r->out.info_ctr->ctr.ctr1->array = NULL;
+
+ return WERR_NOT_SUPPORTED;
+
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ return WERR_OK;
+}
+
+
+/*
+ srvsvc_NetCharDevGetInfo
+*/
+static WERROR dcesrv_srvsvc_NetCharDevGetInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetCharDevGetInfo *r)
+{
+ ZERO_STRUCTP(r->out.info);
+
+ switch (r->in.level) {
+ case 0:
+ {
+ return WERR_NOT_SUPPORTED;
+ }
+ case 1:
+ {
+ return WERR_NOT_SUPPORTED;
+ }
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ return WERR_UNKNOWN_LEVEL;
+}
+
+
+/*
+ srvsvc_NetCharDevControl
+*/
+static WERROR dcesrv_srvsvc_NetCharDevControl(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetCharDevControl *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NetCharDevQEnum
+*/
+static WERROR dcesrv_srvsvc_NetCharDevQEnum(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetCharDevQEnum *r)
+{
+ *r->out.totalentries = 0;
+
+ switch (r->in.info_ctr->level) {
+ case 0:
+ {
+ r->out.info_ctr->ctr.ctr0 = talloc(mem_ctx, struct srvsvc_NetCharDevQCtr0);
+ W_ERROR_HAVE_NO_MEMORY(r->out.info_ctr->ctr.ctr0);
+
+ r->out.info_ctr->ctr.ctr0->count = 0;
+ r->out.info_ctr->ctr.ctr0->array = NULL;
+
+ return WERR_NOT_SUPPORTED;
+ }
+ case 1:
+ {
+ r->out.info_ctr->ctr.ctr1 = talloc(mem_ctx, struct srvsvc_NetCharDevQCtr1);
+ W_ERROR_HAVE_NO_MEMORY(r->out.info_ctr->ctr.ctr1);
+
+ r->out.info_ctr->ctr.ctr1->count = 0;
+ r->out.info_ctr->ctr.ctr1->array = NULL;
+
+ return WERR_NOT_SUPPORTED;
+ }
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ return WERR_UNKNOWN_LEVEL;
+}
+
+
+/*
+ srvsvc_NetCharDevQGetInfo
+*/
+static WERROR dcesrv_srvsvc_NetCharDevQGetInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetCharDevQGetInfo *r)
+{
+ ZERO_STRUCTP(r->out.info);
+
+ switch (r->in.level) {
+ case 0:
+ {
+ return WERR_NOT_SUPPORTED;
+ }
+ case 1:
+ {
+ return WERR_NOT_SUPPORTED;
+ }
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ return WERR_UNKNOWN_LEVEL;
+}
+
+
+/*
+ srvsvc_NetCharDevQSetInfo
+*/
+static WERROR dcesrv_srvsvc_NetCharDevQSetInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetCharDevQSetInfo *r)
+{
+ switch (r->in.level) {
+ case 0:
+ {
+ if (r->in.parm_error) {
+ r->out.parm_error = r->in.parm_error;
+ }
+ return WERR_NOT_SUPPORTED;
+ }
+ case 1:
+ {
+ if (r->in.parm_error) {
+ r->out.parm_error = r->in.parm_error;
+ }
+ return WERR_NOT_SUPPORTED;
+ }
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ return WERR_UNKNOWN_LEVEL;
+}
+
+
+/*
+ srvsvc_NetCharDevQPurge
+*/
+static WERROR dcesrv_srvsvc_NetCharDevQPurge(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetCharDevQPurge *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NetCharDevQPurgeSelf
+*/
+static WERROR dcesrv_srvsvc_NetCharDevQPurgeSelf(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetCharDevQPurgeSelf *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NetConnEnum
+*/
+static WERROR dcesrv_srvsvc_NetConnEnum(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetConnEnum *r)
+{
+ *r->out.totalentries = 0;
+
+ switch (r->in.info_ctr->level) {
+ case 0:
+ {
+ r->out.info_ctr->ctr.ctr0 = talloc(mem_ctx, struct srvsvc_NetConnCtr0);
+ W_ERROR_HAVE_NO_MEMORY(r->out.info_ctr->ctr.ctr0);
+
+ r->out.info_ctr->ctr.ctr0->count = 0;
+ r->out.info_ctr->ctr.ctr0->array = NULL;
+
+ return WERR_NOT_SUPPORTED;
+ }
+ case 1:
+ {
+ r->out.info_ctr->ctr.ctr1 = talloc(mem_ctx, struct srvsvc_NetConnCtr1);
+ W_ERROR_HAVE_NO_MEMORY(r->out.info_ctr->ctr.ctr1);
+
+ r->out.info_ctr->ctr.ctr1->count = 0;
+ r->out.info_ctr->ctr.ctr1->array = NULL;
+
+ return WERR_NOT_SUPPORTED;
+ }
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ return WERR_UNKNOWN_LEVEL;
+}
+
+
+/*
+ srvsvc_NetFileEnum
+*/
+static WERROR dcesrv_srvsvc_NetFileEnum(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetFileEnum *r)
+{
+ *r->out.totalentries = 0;
+
+ switch (r->in.info_ctr->level) {
+ case 2:
+ {
+ r->out.info_ctr->ctr.ctr2 = talloc(mem_ctx, struct srvsvc_NetFileCtr2);
+ W_ERROR_HAVE_NO_MEMORY(r->out.info_ctr->ctr.ctr2);
+
+ r->out.info_ctr->ctr.ctr2->count = 0;
+ r->out.info_ctr->ctr.ctr2->array = NULL;
+
+ return WERR_NOT_SUPPORTED;
+ }
+ case 3:
+ {
+ r->out.info_ctr->ctr.ctr3 = talloc(mem_ctx, struct srvsvc_NetFileCtr3);
+ W_ERROR_HAVE_NO_MEMORY(r->out.info_ctr->ctr.ctr3);
+
+ r->out.info_ctr->ctr.ctr3->count = 0;
+ r->out.info_ctr->ctr.ctr3->array = NULL;
+
+ return WERR_NOT_SUPPORTED;
+ }
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ return WERR_UNKNOWN_LEVEL;
+}
+
+
+/*
+ srvsvc_NetFileGetInfo
+*/
+static WERROR dcesrv_srvsvc_NetFileGetInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetFileGetInfo *r)
+{
+ ZERO_STRUCTP(r->out.info);
+
+ switch (r->in.level) {
+ case 2:
+ {
+ return WERR_NOT_SUPPORTED;
+ }
+ case 3:
+ {
+ return WERR_NOT_SUPPORTED;
+ }
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ return WERR_UNKNOWN_LEVEL;
+}
+
+
+/*
+ srvsvc_NetFileClose
+*/
+static WERROR dcesrv_srvsvc_NetFileClose(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetFileClose *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NetSessEnum
+*/
+static WERROR dcesrv_srvsvc_NetSessEnum(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetSessEnum *r)
+{
+ *r->out.totalentries = 0;
+
+ switch (r->in.info_ctr->level) {
+ case 0:
+ {
+ r->out.info_ctr->ctr.ctr0 = talloc(mem_ctx, struct srvsvc_NetSessCtr0);
+ W_ERROR_HAVE_NO_MEMORY(r->out.info_ctr->ctr.ctr0);
+
+ r->out.info_ctr->ctr.ctr0->count = 0;
+ r->out.info_ctr->ctr.ctr0->array = NULL;
+
+ return WERR_NOT_SUPPORTED;
+ }
+ case 1:
+ {
+ r->out.info_ctr->ctr.ctr1 = talloc(mem_ctx, struct srvsvc_NetSessCtr1);
+ W_ERROR_HAVE_NO_MEMORY(r->out.info_ctr->ctr.ctr1);
+
+ r->out.info_ctr->ctr.ctr1->count = 0;
+ r->out.info_ctr->ctr.ctr1->array = NULL;
+
+ return WERR_NOT_SUPPORTED;
+ }
+ case 2:
+ {
+ r->out.info_ctr->ctr.ctr2 = talloc(mem_ctx, struct srvsvc_NetSessCtr2);
+ W_ERROR_HAVE_NO_MEMORY(r->out.info_ctr->ctr.ctr2);
+
+ r->out.info_ctr->ctr.ctr2->count = 0;
+ r->out.info_ctr->ctr.ctr2->array = NULL;
+
+ return WERR_NOT_SUPPORTED;
+ }
+ case 10:
+ {
+ r->out.info_ctr->ctr.ctr10 = talloc(mem_ctx, struct srvsvc_NetSessCtr10);
+ W_ERROR_HAVE_NO_MEMORY(r->out.info_ctr->ctr.ctr10);
+
+ r->out.info_ctr->ctr.ctr10->count = 0;
+ r->out.info_ctr->ctr.ctr10->array = NULL;
+
+ return WERR_NOT_SUPPORTED;
+ }
+ case 502:
+ {
+ r->out.info_ctr->ctr.ctr502 = talloc(mem_ctx, struct srvsvc_NetSessCtr502);
+ W_ERROR_HAVE_NO_MEMORY(r->out.info_ctr->ctr.ctr502);
+
+ r->out.info_ctr->ctr.ctr502->count = 0;
+ r->out.info_ctr->ctr.ctr502->array = NULL;
+
+ return WERR_NOT_SUPPORTED;
+ }
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ return WERR_UNKNOWN_LEVEL;
+}
+
+
+/*
+ srvsvc_NetSessDel
+*/
+static WERROR dcesrv_srvsvc_NetSessDel(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetSessDel *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NetShareAdd
+*/
+static WERROR dcesrv_srvsvc_NetShareAdd(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetShareAdd *r)
+{
+ switch (r->in.level) {
+ case 0:
+ {
+ if (r->in.parm_error) {
+ r->out.parm_error = r->in.parm_error;
+ }
+ return WERR_NOT_SUPPORTED;
+ }
+ case 1:
+ {
+ if (r->in.parm_error) {
+ r->out.parm_error = r->in.parm_error;
+ }
+ return WERR_NOT_SUPPORTED;
+ }
+ case 2:
+ {
+ NTSTATUS nterr;
+ struct share_info *info;
+ struct share_context *sctx;
+ int count = 8;
+ int i;
+
+ nterr = share_get_context_by_name(mem_ctx, lp_share_backend(dce_call->conn->dce_ctx->lp_ctx), dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, &sctx);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return ntstatus_to_werror(nterr);
+ }
+
+ /* there are no more than 8 options in struct srvsvc_NetShareInfo2 */
+ info = talloc_array(mem_ctx, struct share_info, count);
+ W_ERROR_HAVE_NO_MEMORY(info);
+
+ i = 0;
+
+ info[i].name = SHARE_TYPE;
+ info[i].type = SHARE_INFO_STRING;
+ switch (r->in.info->info2->type) {
+ case 0x00:
+ info[i].value = talloc_strdup(info, "DISK");
+ break;
+ case 0x01:
+ info[i].value = talloc_strdup(info, "PRINTER");
+ break;
+ case 0x03:
+ info[i].value = talloc_strdup(info, "IPC");
+ break;
+ default:
+ return WERR_INVALID_PARAM;
+ }
+ W_ERROR_HAVE_NO_MEMORY(info[i].value);
+ i++;
+
+ if (r->in.info->info2->path && r->in.info->info2->path[0]) {
+ info[i].name = SHARE_PATH;
+ info[i].type = SHARE_INFO_STRING;
+
+ /* Windows will send a path in a form of C:\example\path */
+ if (r->in.info->info2->path[1] == ':') {
+ info[i].value = talloc_strdup(info, &r->in.info->info2->path[2]);
+ } else {
+ /* very strange let's try to set as is */
+ info[i].value = talloc_strdup(info, r->in.info->info2->path);
+ }
+ W_ERROR_HAVE_NO_MEMORY(info[i].value);
+ all_string_sub((char *)info[i].value, "\\", "/", 0);
+
+ i++;
+ }
+
+ if (r->in.info->info2->comment && r->in.info->info2->comment[0]) {
+ info[i].name = SHARE_COMMENT;
+ info[i].type = SHARE_INFO_STRING;
+ info[i].value = talloc_strdup(info, r->in.info->info2->comment);
+ W_ERROR_HAVE_NO_MEMORY(info[i].value);
+
+ i++;
+ }
+
+ if (r->in.info->info2->password && r->in.info->info2->password[0]) {
+ info[i].name = SHARE_PASSWORD;
+ info[i].type = SHARE_INFO_STRING;
+ info[i].value = talloc_strdup(info, r->in.info->info2->password);
+ W_ERROR_HAVE_NO_MEMORY(info[i].value);
+
+ i++;
+ }
+
+ info[i].name = SHARE_MAX_CONNECTIONS;
+ info[i].type = SHARE_INFO_INT;
+ info[i].value = talloc(info, int);
+ *((int *)info[i].value) = r->in.info->info2->max_users;
+ i++;
+
+ /* TODO: security descriptor */
+
+ nterr = share_create(sctx, r->in.info->info2->name, info, i);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return ntstatus_to_werror(nterr);
+ }
+
+ if (r->in.parm_error) {
+ r->out.parm_error = r->in.parm_error;
+ }
+
+ return WERR_OK;
+ }
+ case 501:
+ {
+ if (r->in.parm_error) {
+ r->out.parm_error = r->in.parm_error;
+ }
+ return WERR_NOT_SUPPORTED;
+ }
+ case 502:
+ {
+ NTSTATUS nterr;
+ struct share_info *info;
+ struct share_context *sctx;
+ int count = 10;
+ int i;
+
+ nterr = share_get_context_by_name(mem_ctx, lp_share_backend(dce_call->conn->dce_ctx->lp_ctx), dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, &sctx);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return ntstatus_to_werror(nterr);
+ }
+
+ /* there are no more than 10 options in struct srvsvc_NetShareInfo502 */
+ info = talloc_array(mem_ctx, struct share_info, count);
+ W_ERROR_HAVE_NO_MEMORY(info);
+
+ i = 0;
+
+ info[i].name = SHARE_TYPE;
+ info[i].type = SHARE_INFO_STRING;
+ switch (r->in.info->info502->type) {
+ case 0x00:
+ info[i].value = talloc_strdup(info, "DISK");
+ break;
+ case 0x01:
+ info[i].value = talloc_strdup(info, "PRINTER");
+ break;
+ case 0x03:
+ info[i].value = talloc_strdup(info, "IPC");
+ break;
+ default:
+ return WERR_INVALID_PARAM;
+ }
+ W_ERROR_HAVE_NO_MEMORY(info[i].value);
+ i++;
+
+ if (r->in.info->info502->path && r->in.info->info502->path[0]) {
+ info[i].name = SHARE_PATH;
+ info[i].type = SHARE_INFO_STRING;
+
+ /* Windows will send a path in a form of C:\example\path */
+ if (r->in.info->info502->path[1] == ':') {
+ info[i].value = talloc_strdup(info, &r->in.info->info502->path[2]);
+ } else {
+ /* very strange let's try to set as is */
+ info[i].value = talloc_strdup(info, r->in.info->info502->path);
+ }
+ W_ERROR_HAVE_NO_MEMORY(info[i].value);
+ all_string_sub((char *)info[i].value, "\\", "/", 0);
+
+ i++;
+ }
+
+ if (r->in.info->info502->comment && r->in.info->info502->comment[0]) {
+ info[i].name = SHARE_COMMENT;
+ info[i].type = SHARE_INFO_STRING;
+ info[i].value = talloc_strdup(info, r->in.info->info502->comment);
+ W_ERROR_HAVE_NO_MEMORY(info[i].value);
+
+ i++;
+ }
+
+ if (r->in.info->info502->password && r->in.info->info502->password[0]) {
+ info[i].name = SHARE_PASSWORD;
+ info[i].type = SHARE_INFO_STRING;
+ info[i].value = talloc_strdup(info, r->in.info->info502->password);
+ W_ERROR_HAVE_NO_MEMORY(info[i].value);
+
+ i++;
+ }
+
+ info[i].name = SHARE_MAX_CONNECTIONS;
+ info[i].type = SHARE_INFO_INT;
+ info[i].value = talloc(info, int);
+ *((int *)info[i].value) = r->in.info->info502->max_users;
+ i++;
+
+ /* TODO: security descriptor */
+
+ nterr = share_create(sctx, r->in.info->info502->name, info, i);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return ntstatus_to_werror(nterr);
+ }
+
+ if (r->in.parm_error) {
+ r->out.parm_error = r->in.parm_error;
+ }
+
+ return WERR_OK;
+ }
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ return WERR_UNKNOWN_LEVEL;
+}
+
+static WERROR dcesrv_srvsvc_fiel_ShareInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct share_config *scfg, uint32_t level,
+ union srvsvc_NetShareInfo *info)
+{
+ struct dcesrv_context *dce_ctx = dce_call->conn->dce_ctx;
+
+ switch (level) {
+ case 0:
+ {
+ info->info0->name = talloc_strdup(mem_ctx, scfg->name);
+ W_ERROR_HAVE_NO_MEMORY(info->info0->name);
+
+ return WERR_OK;
+ }
+ case 1:
+ {
+ info->info1->name = talloc_strdup(mem_ctx, scfg->name);
+ W_ERROR_HAVE_NO_MEMORY(info->info1->name);
+ info->info1->type = dcesrv_common_get_share_type(mem_ctx, dce_ctx, scfg);
+ info->info1->comment = talloc_strdup(mem_ctx, share_string_option(scfg, SHARE_COMMENT, ""));
+ W_ERROR_HAVE_NO_MEMORY(info->info1->comment);
+
+ return WERR_OK;
+ }
+ case 2:
+ {
+ info->info2->name = talloc_strdup(mem_ctx, scfg->name);
+ W_ERROR_HAVE_NO_MEMORY(info->info2->name);
+ info->info2->type = dcesrv_common_get_share_type(mem_ctx, dce_ctx, scfg);
+ info->info2->comment = talloc_strdup(mem_ctx, share_string_option(scfg, SHARE_COMMENT, ""));
+ W_ERROR_HAVE_NO_MEMORY(info->info2->comment);
+ info->info2->permissions = dcesrv_common_get_share_permissions(mem_ctx, dce_ctx, scfg);
+ info->info2->max_users = share_int_option(scfg, SHARE_MAX_CONNECTIONS, SHARE_MAX_CONNECTIONS_DEFAULT);
+ info->info2->current_users = dcesrv_common_get_share_current_users(mem_ctx, dce_ctx, scfg);
+ info->info2->path = dcesrv_common_get_share_path(mem_ctx, dce_ctx, scfg);
+ W_ERROR_HAVE_NO_MEMORY(info->info2->path);
+ info->info2->password = talloc_strdup(mem_ctx, share_string_option(scfg, SHARE_PASSWORD, NULL));
+
+ return WERR_OK;
+ }
+ case 501:
+ {
+ info->info501->name = talloc_strdup(mem_ctx, scfg->name);
+ W_ERROR_HAVE_NO_MEMORY(info->info501->name);
+ info->info501->type = dcesrv_common_get_share_type(mem_ctx, dce_ctx, scfg);
+ info->info501->comment = talloc_strdup(mem_ctx, share_string_option(scfg, SHARE_COMMENT, ""));
+ W_ERROR_HAVE_NO_MEMORY(info->info501->comment);
+ info->info501->csc_policy = share_int_option(scfg, SHARE_CSC_POLICY, SHARE_CSC_POLICY_DEFAULT);
+
+ return WERR_OK;
+ }
+ case 502:
+ {
+ info->info502->name = talloc_strdup(mem_ctx, scfg->name);
+ W_ERROR_HAVE_NO_MEMORY(info->info502->name);
+ info->info502->type = dcesrv_common_get_share_type(mem_ctx, dce_ctx, scfg);
+ info->info502->comment = talloc_strdup(mem_ctx, share_string_option(scfg, SHARE_COMMENT, ""));
+ W_ERROR_HAVE_NO_MEMORY(info->info502->comment);
+ info->info502->permissions = dcesrv_common_get_share_permissions(mem_ctx, dce_ctx, scfg);
+ info->info502->max_users = share_int_option(scfg, SHARE_MAX_CONNECTIONS, SHARE_MAX_CONNECTIONS_DEFAULT);
+ info->info502->current_users = dcesrv_common_get_share_current_users(mem_ctx, dce_ctx, scfg);
+ info->info502->path = dcesrv_common_get_share_path(mem_ctx, dce_ctx, scfg);
+ W_ERROR_HAVE_NO_MEMORY(info->info502->path);
+ info->info502->password = talloc_strdup(mem_ctx, share_string_option(scfg, SHARE_PASSWORD, NULL));
+ info->info502->sd_buf.sd = dcesrv_common_get_security_descriptor(mem_ctx, dce_ctx, scfg);
+
+ return WERR_OK;
+ }
+ case 1005:
+ {
+ info->info1005->dfs_flags = dcesrv_common_get_share_dfs_flags(mem_ctx, dce_ctx, scfg);
+
+ return WERR_OK;
+ }
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ return WERR_UNKNOWN_LEVEL;
+}
+
+/*
+ srvsvc_NetShareEnumAll
+*/
+static WERROR dcesrv_srvsvc_NetShareEnumAll(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetShareEnumAll *r)
+{
+ NTSTATUS nterr;
+ int numshares = 0;
+ const char **snames;
+ struct share_context *sctx;
+ struct share_config *scfg;
+
+ *r->out.totalentries = 0;
+
+ /* TODO: - paging of results
+ */
+
+ nterr = share_get_context_by_name(mem_ctx, lp_share_backend(dce_call->conn->dce_ctx->lp_ctx), dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, &sctx);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return ntstatus_to_werror(nterr);
+ }
+
+ nterr = share_list_all(mem_ctx, sctx, &numshares, &snames);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return ntstatus_to_werror(nterr);
+ }
+
+ switch (r->in.info_ctr->level) {
+ case 0:
+ {
+ int i;
+ struct srvsvc_NetShareCtr0 *ctr0;
+
+ ctr0 = talloc(mem_ctx, struct srvsvc_NetShareCtr0);
+ W_ERROR_HAVE_NO_MEMORY(ctr0);
+
+ ctr0->count = numshares;
+ ctr0->array = NULL;
+
+ if (ctr0->count == 0) {
+ r->out.info_ctr->ctr.ctr0 = ctr0;
+ return WERR_OK;
+ }
+
+ ctr0->array = talloc_array(mem_ctx, struct srvsvc_NetShareInfo0, ctr0->count);
+ W_ERROR_HAVE_NO_MEMORY(ctr0->array);
+
+ for (i = 0; i < ctr0->count; i++) {
+ WERROR status;
+ union srvsvc_NetShareInfo info;
+
+ nterr = share_get_config(mem_ctx, sctx, snames[i], &scfg);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ DEBUG(1, ("ERROR: Service [%s] disappeared after enumeration", snames[i]));
+ return WERR_GENERAL_FAILURE;
+ }
+ info.info0 = &ctr0->array[i];
+ status = dcesrv_srvsvc_fiel_ShareInfo(dce_call, mem_ctx, scfg, r->in.info_ctr->level, &info);
+ if (!W_ERROR_IS_OK(status)) {
+ return status;
+ }
+ talloc_free(scfg);
+ }
+ talloc_free(snames);
+
+ r->out.info_ctr->ctr.ctr0 = ctr0;
+ *r->out.totalentries = r->out.info_ctr->ctr.ctr0->count;
+ return WERR_OK;
+ }
+ case 1:
+ {
+ int i;
+ struct srvsvc_NetShareCtr1 *ctr1;
+
+ ctr1 = talloc(mem_ctx, struct srvsvc_NetShareCtr1);
+ W_ERROR_HAVE_NO_MEMORY(ctr1);
+
+ ctr1->count = numshares;
+ ctr1->array = NULL;
+
+ if (ctr1->count == 0) {
+ r->out.info_ctr->ctr.ctr1 = ctr1;
+ return WERR_OK;
+ }
+
+ ctr1->array = talloc_array(mem_ctx, struct srvsvc_NetShareInfo1, ctr1->count);
+ W_ERROR_HAVE_NO_MEMORY(ctr1->array);
+
+ for (i=0; i < ctr1->count; i++) {
+ WERROR status;
+ union srvsvc_NetShareInfo info;
+
+ nterr = share_get_config(mem_ctx, sctx, snames[i], &scfg);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ DEBUG(1, ("ERROR: Service [%s] disappeared after enumeration", snames[i]));
+ return WERR_GENERAL_FAILURE;
+ }
+ info.info1 = &ctr1->array[i];
+ status = dcesrv_srvsvc_fiel_ShareInfo(dce_call, mem_ctx, scfg, r->in.info_ctr->level, &info);
+ if (!W_ERROR_IS_OK(status)) {
+ return status;
+ }
+ talloc_free(scfg);
+ }
+ talloc_free(snames);
+
+ r->out.info_ctr->ctr.ctr1 = ctr1;
+ *r->out.totalentries = r->out.info_ctr->ctr.ctr1->count;
+
+ return WERR_OK;
+ }
+ case 2:
+ {
+ int i;
+ struct srvsvc_NetShareCtr2 *ctr2;
+
+ SRVSVC_CHECK_ADMIN_ACCESS;
+
+ ctr2 = talloc(mem_ctx, struct srvsvc_NetShareCtr2);
+ W_ERROR_HAVE_NO_MEMORY(ctr2);
+
+ ctr2->count = numshares;
+ ctr2->array = NULL;
+
+ if (ctr2->count == 0) {
+ r->out.info_ctr->ctr.ctr2 = ctr2;
+ return WERR_OK;
+ }
+
+ ctr2->array = talloc_array(mem_ctx, struct srvsvc_NetShareInfo2, ctr2->count);
+ W_ERROR_HAVE_NO_MEMORY(ctr2->array);
+
+ for (i=0; i < ctr2->count; i++) {
+ WERROR status;
+ union srvsvc_NetShareInfo info;
+
+ nterr = share_get_config(mem_ctx, sctx, snames[i], &scfg);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ DEBUG(1, ("ERROR: Service [%s] disappeared after enumeration", snames[i]));
+ return WERR_GENERAL_FAILURE;
+ }
+ info.info2 = &ctr2->array[i];
+ status = dcesrv_srvsvc_fiel_ShareInfo(dce_call, mem_ctx, scfg, r->in.info_ctr->level, &info);
+ if (!W_ERROR_IS_OK(status)) {
+ return status;
+ }
+ talloc_free(scfg);
+ }
+ talloc_free(snames);
+
+ r->out.info_ctr->ctr.ctr2 = ctr2;
+ *r->out.totalentries = r->out.info_ctr->ctr.ctr2->count;
+
+ return WERR_OK;
+ }
+ case 501:
+ {
+ int i;
+ struct srvsvc_NetShareCtr501 *ctr501;
+
+ SRVSVC_CHECK_ADMIN_ACCESS;
+
+ ctr501 = talloc(mem_ctx, struct srvsvc_NetShareCtr501);
+ W_ERROR_HAVE_NO_MEMORY(ctr501);
+
+ ctr501->count = numshares;
+ ctr501->array = NULL;
+
+ if (ctr501->count == 0) {
+ r->out.info_ctr->ctr.ctr501 = ctr501;
+ return WERR_OK;
+ }
+
+ ctr501->array = talloc_array(mem_ctx, struct srvsvc_NetShareInfo501, ctr501->count);
+ W_ERROR_HAVE_NO_MEMORY(ctr501->array);
+
+ for (i=0; i < ctr501->count; i++) {
+ WERROR status;
+ union srvsvc_NetShareInfo info;
+
+ nterr = share_get_config(mem_ctx, sctx, snames[i], &scfg);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ DEBUG(1, ("ERROR: Service [%s] disappeared after enumeration", snames[i]));
+ return WERR_GENERAL_FAILURE;
+ }
+ info.info501 = &ctr501->array[i];
+ status = dcesrv_srvsvc_fiel_ShareInfo(dce_call, mem_ctx, scfg, r->in.info_ctr->level, &info);
+ if (!W_ERROR_IS_OK(status)) {
+ return status;
+ }
+ talloc_free(scfg);
+ }
+ talloc_free(snames);
+
+ r->out.info_ctr->ctr.ctr501 = ctr501;
+ *r->out.totalentries = r->out.info_ctr->ctr.ctr501->count;
+
+ return WERR_OK;
+ }
+ case 502:
+ {
+ int i;
+ struct srvsvc_NetShareCtr502 *ctr502;
+
+ SRVSVC_CHECK_ADMIN_ACCESS;
+
+ ctr502 = talloc(mem_ctx, struct srvsvc_NetShareCtr502);
+ W_ERROR_HAVE_NO_MEMORY(ctr502);
+
+ ctr502->count = numshares;
+ ctr502->array = NULL;
+
+ if (ctr502->count == 0) {
+ r->out.info_ctr->ctr.ctr502 = ctr502;
+ return WERR_OK;
+ }
+
+ ctr502->array = talloc_array(mem_ctx, struct srvsvc_NetShareInfo502, ctr502->count);
+ W_ERROR_HAVE_NO_MEMORY(ctr502->array);
+
+ for (i=0; i < ctr502->count; i++) {
+ WERROR status;
+ union srvsvc_NetShareInfo info;
+
+ nterr = share_get_config(mem_ctx, sctx, snames[i], &scfg);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ DEBUG(1, ("ERROR: Service [%s] disappeared after enumeration", snames[i]));
+ return WERR_GENERAL_FAILURE;
+ }
+ info.info502 = &ctr502->array[i];
+ status = dcesrv_srvsvc_fiel_ShareInfo(dce_call, mem_ctx, scfg, r->in.info_ctr->level, &info);
+ if (!W_ERROR_IS_OK(status)) {
+ return status;
+ }
+ talloc_free(scfg);
+ }
+ talloc_free(snames);
+
+ r->out.info_ctr->ctr.ctr502 = ctr502;
+ *r->out.totalentries = r->out.info_ctr->ctr.ctr502->count;
+
+ return WERR_OK;
+ }
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ return WERR_UNKNOWN_LEVEL;
+}
+
+
+/*
+ srvsvc_NetShareGetInfo
+*/
+static WERROR dcesrv_srvsvc_NetShareGetInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetShareGetInfo *r)
+{
+ NTSTATUS nterr;
+ struct share_context *sctx = NULL;
+ struct share_config *scfg = NULL;
+
+ ZERO_STRUCTP(r->out.info);
+
+ /* TODO: - access check
+ */
+
+ if (strcmp("", r->in.share_name) == 0) {
+ return WERR_INVALID_PARAM;
+ }
+
+ nterr = share_get_context_by_name(mem_ctx, lp_share_backend(dce_call->conn->dce_ctx->lp_ctx), dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, &sctx);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return ntstatus_to_werror(nterr);
+ }
+
+ nterr = share_get_config(mem_ctx, sctx, r->in.share_name, &scfg);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return ntstatus_to_werror(nterr);
+ }
+
+ switch (r->in.level) {
+ case 0:
+ {
+ WERROR status;
+ union srvsvc_NetShareInfo info;
+
+ info.info0 = talloc(mem_ctx, struct srvsvc_NetShareInfo0);
+ W_ERROR_HAVE_NO_MEMORY(info.info0);
+
+ status = dcesrv_srvsvc_fiel_ShareInfo(dce_call, mem_ctx, scfg, r->in.level, &info);
+ if (!W_ERROR_IS_OK(status)) {
+ return status;
+ }
+
+ r->out.info->info0 = info.info0;
+ return WERR_OK;
+ }
+ case 1:
+ {
+ WERROR status;
+ union srvsvc_NetShareInfo info;
+
+ info.info1 = talloc(mem_ctx, struct srvsvc_NetShareInfo1);
+ W_ERROR_HAVE_NO_MEMORY(info.info1);
+
+ status = dcesrv_srvsvc_fiel_ShareInfo(dce_call, mem_ctx, scfg, r->in.level, &info);
+ if (!W_ERROR_IS_OK(status)) {
+ return status;
+ }
+
+ r->out.info->info1 = info.info1;
+ return WERR_OK;
+ }
+ case 2:
+ {
+ WERROR status;
+ union srvsvc_NetShareInfo info;
+
+ SRVSVC_CHECK_ADMIN_ACCESS;
+
+ info.info2 = talloc(mem_ctx, struct srvsvc_NetShareInfo2);
+ W_ERROR_HAVE_NO_MEMORY(info.info2);
+
+ status = dcesrv_srvsvc_fiel_ShareInfo(dce_call, mem_ctx, scfg, r->in.level, &info);
+ if (!W_ERROR_IS_OK(status)) {
+ return status;
+ }
+
+ r->out.info->info2 = info.info2;
+ return WERR_OK;
+ }
+ case 501:
+ {
+ WERROR status;
+ union srvsvc_NetShareInfo info;
+
+ info.info501 = talloc(mem_ctx, struct srvsvc_NetShareInfo501);
+ W_ERROR_HAVE_NO_MEMORY(info.info501);
+
+ status = dcesrv_srvsvc_fiel_ShareInfo(dce_call, mem_ctx, scfg, r->in.level, &info);
+ if (!W_ERROR_IS_OK(status)) {
+ return status;
+ }
+
+ r->out.info->info501 = info.info501;
+ return WERR_OK;
+ }
+ case 502:
+ {
+ WERROR status;
+ union srvsvc_NetShareInfo info;
+
+ SRVSVC_CHECK_ADMIN_ACCESS;
+
+ info.info502 = talloc(mem_ctx, struct srvsvc_NetShareInfo502);
+ W_ERROR_HAVE_NO_MEMORY(info.info502);
+
+ status = dcesrv_srvsvc_fiel_ShareInfo(dce_call, mem_ctx, scfg, r->in.level, &info);
+ if (!W_ERROR_IS_OK(status)) {
+ return status;
+ }
+
+ r->out.info->info502 = info.info502;
+ return WERR_OK;
+ }
+ case 1005:
+ {
+ WERROR status;
+ union srvsvc_NetShareInfo info;
+
+ info.info1005 = talloc(mem_ctx, struct srvsvc_NetShareInfo1005);
+ W_ERROR_HAVE_NO_MEMORY(info.info1005);
+
+ status = dcesrv_srvsvc_fiel_ShareInfo(dce_call, mem_ctx, scfg, r->in.level, &info);
+ if (!W_ERROR_IS_OK(status)) {
+ return status;
+ }
+
+ r->out.info->info1005 = info.info1005;
+ return WERR_OK;
+ }
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ return WERR_UNKNOWN_LEVEL;
+}
+
+static WERROR dcesrv_srvsvc_fill_share_info(struct share_info *info, int *count,
+ const char *share_name, int level,
+ const char *name,
+ const char *path,
+ const char *comment,
+ const char *password,
+ enum srvsvc_ShareType type,
+ int32_t max_users,
+ uint32_t csc_policy,
+ struct security_descriptor *sd)
+{
+ int i = 0;
+
+ if (level == 501) {
+ info[i].name = SHARE_CSC_POLICY;
+ info[i].type = SHARE_INFO_INT;
+ info[i].value = talloc(info, int);
+ *((int *)info[i].value) = csc_policy;
+ i++;
+ }
+
+ switch(level) {
+
+ case 502:
+ /* TODO: check if unknown is csc_policy */
+
+ /* TODO: security descriptor */
+
+ case 2:
+ if (path && path[0]) {
+ info[i].name = SHARE_PATH;
+ info[i].type = SHARE_INFO_STRING;
+
+ /* Windows will send a path in a form of C:\example\path */
+ if (path[1] == ':') {
+ info[i].value = talloc_strdup(info, &path[2]);
+ } else {
+ /* very strange let's try to set as is */
+ info[i].value = talloc_strdup(info, path);
+ }
+ W_ERROR_HAVE_NO_MEMORY(info[i].value);
+ all_string_sub((char *)info[i].value, "\\", "/", 0);
+
+ i++;
+ }
+
+ if (password && password[0]) {
+ info[i].name = SHARE_PASSWORD;
+ info[i].type = SHARE_INFO_STRING;
+ info[i].value = talloc_strdup(info, password);
+ W_ERROR_HAVE_NO_MEMORY(info[i].value);
+
+ i++;
+ }
+
+ info[i].name = SHARE_MAX_CONNECTIONS;
+ info[i].type = SHARE_INFO_INT;
+ info[i].value = talloc(info, int);
+ *((int *)info[i].value) = max_users;
+ i++;
+
+ case 501:
+ case 1:
+ info[i].name = SHARE_TYPE;
+ info[i].type = SHARE_INFO_STRING;
+ switch (type) {
+ case 0x00:
+ info[i].value = talloc_strdup(info, "DISK");
+ break;
+ case 0x01:
+ info[i].value = talloc_strdup(info, "PRINTER");
+ break;
+ case 0x03:
+ info[i].value = talloc_strdup(info, "IPC");
+ break;
+ default:
+ return WERR_INVALID_PARAM;
+ }
+ W_ERROR_HAVE_NO_MEMORY(info[i].value);
+ i++;
+
+ case 1004:
+ if (comment) {
+ info[i].name = SHARE_COMMENT;
+ info[i].type = SHARE_INFO_STRING;
+ info[i].value = talloc_strdup(info, comment);
+ W_ERROR_HAVE_NO_MEMORY(info[i].value);
+
+ i++;
+ }
+ case 0:
+ if (name &&
+ strcasecmp(share_name, name) != 0) {
+ info[i].name = SHARE_NAME;
+ info[i].type = SHARE_INFO_STRING;
+ info[i].value = talloc_strdup(info, name);
+ W_ERROR_HAVE_NO_MEMORY(info[i].value);
+ i++;
+ }
+
+ break;
+
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ *count = i;
+
+ return WERR_OK;
+}
+
+/*
+ srvsvc_NetShareSetInfo
+*/
+static WERROR dcesrv_srvsvc_NetShareSetInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetShareSetInfo *r)
+{
+ NTSTATUS nterr;
+ WERROR status;
+ struct share_context *sctx = NULL;
+ struct share_info *info;
+ int count;
+
+ /* TODO: - access check
+ */
+
+ /* there are no more than 10 options in all struct srvsvc_NetShareInfoXXX */
+ info = talloc_array(mem_ctx, struct share_info, 10);
+ W_ERROR_HAVE_NO_MEMORY(info);
+
+ ZERO_STRUCT(r->out);
+
+ if (strcmp("", r->in.share_name) == 0) {
+ return WERR_INVALID_PARAM;
+ }
+
+ nterr = share_get_context_by_name(mem_ctx, lp_share_backend(dce_call->conn->dce_ctx->lp_ctx), dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, &sctx);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return ntstatus_to_werror(nterr);
+ }
+
+ switch (r->in.level) {
+ case 0:
+ {
+ status = dcesrv_srvsvc_fill_share_info(info, &count,
+ r->in.share_name, r->in.level,
+ r->in.info->info0->name,
+ NULL,
+ NULL,
+ NULL,
+ 0,
+ 0,
+ 0,
+ NULL);
+ if (W_ERROR_EQUAL(status, WERR_OK)) {
+ return status;
+ }
+ break;
+ }
+ case 1:
+ {
+ status = dcesrv_srvsvc_fill_share_info(info, &count,
+ r->in.share_name, r->in.level,
+ r->in.info->info1->name,
+ NULL,
+ r->in.info->info1->comment,
+ NULL,
+ r->in.info->info1->type,
+ 0,
+ 0,
+ NULL);
+ if (W_ERROR_EQUAL(status, WERR_OK)) {
+ return status;
+ }
+ break;
+ }
+ case 2:
+ {
+ status = dcesrv_srvsvc_fill_share_info(info, &count,
+ r->in.share_name, r->in.level,
+ r->in.info->info2->name,
+ r->in.info->info2->path,
+ r->in.info->info2->comment,
+ r->in.info->info2->password,
+ r->in.info->info2->type,
+ r->in.info->info2->max_users,
+ 0,
+ NULL);
+ if (W_ERROR_EQUAL(status, WERR_OK)) {
+ return status;
+ }
+ break;
+ }
+ case 501:
+ {
+ status = dcesrv_srvsvc_fill_share_info(info, &count,
+ r->in.share_name, r->in.level,
+ r->in.info->info501->name,
+ NULL,
+ r->in.info->info501->comment,
+ NULL,
+ r->in.info->info501->type,
+ 0,
+ r->in.info->info501->csc_policy,
+ NULL);
+ if (W_ERROR_EQUAL(status, WERR_OK)) {
+ return status;
+ }
+ break;
+ }
+ case 502:
+ {
+ status = dcesrv_srvsvc_fill_share_info(info, &count,
+ r->in.share_name, r->in.level,
+ r->in.info->info502->name,
+ r->in.info->info502->path,
+ r->in.info->info502->comment,
+ r->in.info->info502->password,
+ r->in.info->info502->type,
+ r->in.info->info502->max_users,
+ 0,
+ r->in.info->info502->sd_buf.sd);
+ if (W_ERROR_EQUAL(status, WERR_OK)) {
+ return status;
+ }
+ break;
+ }
+ case 1004:
+ {
+ status = dcesrv_srvsvc_fill_share_info(info, &count,
+ r->in.share_name, r->in.level,
+ NULL,
+ NULL,
+ r->in.info->info1004->comment,
+ NULL,
+ 0,
+ 0,
+ 0,
+ NULL);
+ if (W_ERROR_EQUAL(status, WERR_OK)) {
+ return status;
+ }
+ break;
+ }
+ case 1005:
+ {
+ /* r->in.info.dfs_flags; */
+
+ if (r->in.parm_error) {
+ r->out.parm_error = r->in.parm_error;
+ }
+
+ return WERR_OK;
+ }
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ nterr = share_set(sctx, r->in.share_name, info, count);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return ntstatus_to_werror(nterr);
+ }
+
+ if (r->in.parm_error) {
+ r->out.parm_error = r->in.parm_error;
+ }
+
+ return WERR_OK;
+}
+
+
+/*
+ srvsvc_NetShareDelSticky
+*/
+static WERROR dcesrv_srvsvc_NetShareDelSticky(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetShareDelSticky *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NetShareCheck
+*/
+static WERROR dcesrv_srvsvc_NetShareCheck(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetShareCheck *r)
+{
+ NTSTATUS nterr;
+ struct share_context *sctx = NULL;
+ struct share_config *scfg = NULL;
+ char *device;
+ const char **names;
+ int count, i;
+
+ *r->out.type = 0;
+
+ /* TODO: - access check
+ */
+
+ if (strcmp("", r->in.device_name) == 0) {
+ *r->out.type = STYPE_IPC;
+ return WERR_OK;
+ }
+
+ /* copy the path skipping C:\ */
+ if (strncasecmp(r->in.device_name, "C:", 2) == 0) {
+ device = talloc_strdup(mem_ctx, &r->in.device_name[2]);
+ } else {
+ /* no chance we have a share that doesn't start with C:\ */
+ return WERR_DEVICE_NOT_SHARED;
+ }
+ all_string_sub(device, "\\", "/", 0);
+
+ nterr = share_get_context_by_name(mem_ctx, lp_share_backend(dce_call->conn->dce_ctx->lp_ctx), dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, &sctx);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return ntstatus_to_werror(nterr);
+ }
+
+ nterr = share_list_all(mem_ctx, sctx, &count, &names);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return ntstatus_to_werror(nterr);
+ }
+
+ for (i = 0; i < count; i++) {
+ const char *path;
+ const char *type;
+
+ nterr = share_get_config(mem_ctx, sctx, names[i], &scfg);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return ntstatus_to_werror(nterr);
+ }
+ path = share_string_option(scfg, SHARE_PATH, NULL);
+ if (!path) continue;
+
+ if (strcmp(device, path) == 0) {
+ type = share_string_option(scfg, SHARE_TYPE, NULL);
+ if (!type) continue;
+
+ if (strcmp(type, "DISK") == 0) {
+ *r->out.type = STYPE_DISKTREE;
+ return WERR_OK;
+ }
+
+ if (strcmp(type, "IPC") == 0) {
+ *r->out.type = STYPE_IPC;
+ return WERR_OK;
+ }
+
+ if (strcmp(type, "PRINTER") == 0) {
+ *r->out.type = STYPE_PRINTQ;
+ return WERR_OK;
+ }
+ }
+ }
+
+ return WERR_DEVICE_NOT_SHARED;
+}
+
+
+/*
+ srvsvc_NetSrvGetInfo
+*/
+static WERROR dcesrv_srvsvc_NetSrvGetInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetSrvGetInfo *r)
+{
+ struct dcesrv_context *dce_ctx = dce_call->conn->dce_ctx;
+ struct dcerpc_server_info *server_info = lp_dcerpc_server_info(mem_ctx, dce_ctx->lp_ctx);
+
+ ZERO_STRUCTP(r->out.info);
+
+ switch (r->in.level) {
+ case 100:
+ {
+ struct srvsvc_NetSrvInfo100 *info100;
+
+ info100 = talloc(mem_ctx, struct srvsvc_NetSrvInfo100);
+ W_ERROR_HAVE_NO_MEMORY(info100);
+
+ info100->platform_id = dcesrv_common_get_platform_id(mem_ctx, dce_ctx);
+ info100->server_name = dcesrv_common_get_server_name(mem_ctx, dce_ctx, r->in.server_unc);
+ W_ERROR_HAVE_NO_MEMORY(info100->server_name);
+
+ r->out.info->info100 = info100;
+ return WERR_OK;
+ }
+ case 101:
+ {
+ struct srvsvc_NetSrvInfo101 *info101;
+
+ info101 = talloc(mem_ctx, struct srvsvc_NetSrvInfo101);
+ W_ERROR_HAVE_NO_MEMORY(info101);
+
+ info101->platform_id = dcesrv_common_get_platform_id(mem_ctx, dce_ctx);
+ info101->server_name = dcesrv_common_get_server_name(mem_ctx, dce_ctx, r->in.server_unc);
+ W_ERROR_HAVE_NO_MEMORY(info101->server_name);
+
+ info101->version_major = server_info->version_major;
+ info101->version_minor = server_info->version_minor;
+ info101->server_type = dcesrv_common_get_server_type(mem_ctx, dce_call->event_ctx, dce_ctx);
+ info101->comment = talloc_strdup(mem_ctx, lp_serverstring(dce_ctx->lp_ctx));
+ W_ERROR_HAVE_NO_MEMORY(info101->comment);
+
+ r->out.info->info101 = info101;
+ return WERR_OK;
+ }
+ case 102:
+ {
+ struct srvsvc_NetSrvInfo102 *info102;
+
+ info102 = talloc(mem_ctx, struct srvsvc_NetSrvInfo102);
+ W_ERROR_HAVE_NO_MEMORY(info102);
+
+ info102->platform_id = dcesrv_common_get_platform_id(mem_ctx, dce_ctx);
+ info102->server_name = dcesrv_common_get_server_name(mem_ctx, dce_ctx, r->in.server_unc);
+ W_ERROR_HAVE_NO_MEMORY(info102->server_name);
+
+ info102->version_major = server_info->version_major;
+ info102->version_minor = server_info->version_minor;
+ info102->server_type = dcesrv_common_get_server_type(mem_ctx, dce_call->event_ctx, dce_ctx);
+ info102->comment = talloc_strdup(mem_ctx, lp_serverstring(dce_ctx->lp_ctx));
+ W_ERROR_HAVE_NO_MEMORY(info102->comment);
+
+ info102->users = dcesrv_common_get_users(mem_ctx, dce_ctx);
+ info102->disc = dcesrv_common_get_disc(mem_ctx, dce_ctx);
+ info102->hidden = dcesrv_common_get_hidden(mem_ctx, dce_ctx);
+ info102->announce = dcesrv_common_get_announce(mem_ctx, dce_ctx);
+ info102->anndelta = dcesrv_common_get_anndelta(mem_ctx, dce_ctx);
+ info102->licenses = dcesrv_common_get_licenses(mem_ctx, dce_ctx);
+ info102->userpath = dcesrv_common_get_userpath(mem_ctx, dce_ctx);
+ W_ERROR_HAVE_NO_MEMORY(info102->userpath);
+
+ r->out.info->info102 = info102;
+ return WERR_OK;
+ }
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ return WERR_UNKNOWN_LEVEL;
+}
+
+
+/*
+ srvsvc_NetSrvSetInfo
+*/
+static WERROR dcesrv_srvsvc_NetSrvSetInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetSrvSetInfo *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NetDiskEnum
+*/
+static WERROR dcesrv_srvsvc_NetDiskEnum(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetDiskEnum *r)
+{
+ r->out.info->disks = NULL;
+ r->out.info->count = 0;
+ *r->out.totalentries = 0;
+
+ switch (r->in.level) {
+ case 0:
+ {
+ /* we can safely hardcode the reply and report we have only one disk (C:) */
+ /* for some reason Windows wants 2 entries with the second being empty */
+ r->out.info->disks = talloc_array(mem_ctx, struct srvsvc_NetDiskInfo0, 2);
+ W_ERROR_HAVE_NO_MEMORY(r->out.info->disks);
+ r->out.info->count = 2;
+
+ r->out.info->disks[0].disk = talloc_strdup(mem_ctx, "C:");
+ W_ERROR_HAVE_NO_MEMORY(r->out.info->disks[0].disk);
+
+ r->out.info->disks[1].disk = talloc_strdup(mem_ctx, "");
+ W_ERROR_HAVE_NO_MEMORY(r->out.info->disks[1].disk);
+
+ *r->out.totalentries = 1;
+ r->out.resume_handle = r->in.resume_handle;
+
+ return WERR_OK;
+ }
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ return WERR_UNKNOWN_LEVEL;
+}
+
+
+/*
+ srvsvc_NetServerStatisticsGet
+*/
+static WERROR dcesrv_srvsvc_NetServerStatisticsGet(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetServerStatisticsGet *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NetTransportAdd
+*/
+static WERROR dcesrv_srvsvc_NetTransportAdd(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetTransportAdd *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NetTransportEnum
+*/
+static WERROR dcesrv_srvsvc_NetTransportEnum(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetTransportEnum *r)
+{
+ r->out.transports->level = r->in.transports->level;
+ *r->out.totalentries = 0;
+ if (r->out.resume_handle) {
+ *r->out.resume_handle = 0;
+ }
+
+ switch (r->in.transports->level) {
+ case 0:
+ {
+ r->out.transports->ctr.ctr0 = talloc(mem_ctx, struct srvsvc_NetTransportCtr0);
+ W_ERROR_HAVE_NO_MEMORY(r->out.transports->ctr.ctr0);
+
+ r->out.transports->ctr.ctr0->count = 0;
+ r->out.transports->ctr.ctr0->array = NULL;
+
+ return WERR_NOT_SUPPORTED;
+ }
+ case 1:
+ {
+ r->out.transports->ctr.ctr1 = talloc(mem_ctx, struct srvsvc_NetTransportCtr1);
+ W_ERROR_HAVE_NO_MEMORY(r->out.transports->ctr.ctr1);
+
+ r->out.transports->ctr.ctr1->count = 0;
+ r->out.transports->ctr.ctr1->array = NULL;
+
+ return WERR_NOT_SUPPORTED;
+ }
+ case 2:
+ {
+ r->out.transports->ctr.ctr2 = talloc(mem_ctx, struct srvsvc_NetTransportCtr2);
+ W_ERROR_HAVE_NO_MEMORY(r->out.transports->ctr.ctr2);
+
+ r->out.transports->ctr.ctr2->count = 0;
+ r->out.transports->ctr.ctr2->array = NULL;
+
+ return WERR_NOT_SUPPORTED;
+ }
+ case 3:
+ {
+ r->out.transports->ctr.ctr3 = talloc(mem_ctx, struct srvsvc_NetTransportCtr3);
+ W_ERROR_HAVE_NO_MEMORY(r->out.transports->ctr.ctr3);
+
+ r->out.transports->ctr.ctr3->count = 0;
+ r->out.transports->ctr.ctr3->array = NULL;
+
+ return WERR_NOT_SUPPORTED;
+ }
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ return WERR_UNKNOWN_LEVEL;
+}
+
+/*
+ srvsvc_NetTransportDel
+*/
+static WERROR dcesrv_srvsvc_NetTransportDel(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetTransportDel *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NetRemoteTOD
+*/
+static WERROR dcesrv_srvsvc_NetRemoteTOD(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetRemoteTOD *r)
+{
+ struct timeval tval;
+ time_t t;
+ struct tm tm;
+ struct srvsvc_NetRemoteTODInfo *info;
+
+ info = talloc(mem_ctx, struct srvsvc_NetRemoteTODInfo);
+ W_ERROR_HAVE_NO_MEMORY(info);
+
+ GetTimeOfDay(&tval);
+ t = tval.tv_sec;
+
+ gmtime_r(&t, &tm);
+
+ info->elapsed = t;
+ /* TODO: fake the uptime: just return the milliseconds till 0:00:00 today */
+ info->msecs = (tm.tm_hour*60*60*1000)
+ + (tm.tm_min*60*1000)
+ + (tm.tm_sec*1000)
+ + (tval.tv_usec/1000);
+ info->hours = tm.tm_hour;
+ info->mins = tm.tm_min;
+ info->secs = tm.tm_sec;
+ info->hunds = tval.tv_usec/10000;
+ info->timezone = get_time_zone(t)/60;
+ info->tinterval = 310; /* just return the same as windows */
+ info->day = tm.tm_mday;
+ info->month = tm.tm_mon + 1;
+ info->year = tm.tm_year + 1900;
+ info->weekday = tm.tm_wday;
+
+ *r->out.info = info;
+
+ return WERR_OK;
+}
+
+/*
+ srvsvc_NetPathType
+*/
+static WERROR dcesrv_srvsvc_NetPathType(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetPathType *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NetPathCanonicalize
+*/
+static WERROR dcesrv_srvsvc_NetPathCanonicalize(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetPathCanonicalize *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NetPathCompare
+*/
+static WERROR dcesrv_srvsvc_NetPathCompare(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetPathCompare *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NetNameValidate
+*/
+static WERROR dcesrv_srvsvc_NetNameValidate(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetNameValidate *r)
+{
+ int len;
+
+ if ((r->in.flags != 0x0) && (r->in.flags != 0x80000000)) {
+ return WERR_INVALID_NAME;
+ }
+
+ switch (r->in.name_type) {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ return WERR_NOT_SUPPORTED;
+
+ case 9: /* validate share name */
+
+ len = strlen_m(r->in.name);
+ if ((r->in.flags == 0x0) && (len > 81)) {
+ return WERR_INVALID_NAME;
+ }
+ if ((r->in.flags == 0x80000000) && (len > 13)) {
+ return WERR_INVALID_NAME;
+ }
+ if (! dcesrv_common_validate_share_name(mem_ctx, r->in.name)) {
+ return WERR_INVALID_NAME;
+ }
+ return WERR_OK;
+
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ return WERR_NOT_SUPPORTED;
+ default:
+ return WERR_INVALID_PARAM;
+ }
+
+ return WERR_INVALID_PARAM;
+}
+
+
+/*
+ srvsvc_NetPRNameCompare
+*/
+static WERROR dcesrv_srvsvc_NetPRNameCompare(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetPRNameCompare *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NetShareEnum
+*/
+static WERROR dcesrv_srvsvc_NetShareEnum(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetShareEnum *r)
+{
+ NTSTATUS nterr;
+ int numshares = 0;
+ const char **snames;
+ struct share_context *sctx;
+ struct share_config *scfg;
+ struct dcesrv_context *dce_ctx = dce_call->conn->dce_ctx;
+
+ *r->out.totalentries = 0;
+
+ /* TODO: - paging of results
+ */
+
+ nterr = share_get_context_by_name(mem_ctx, lp_share_backend(dce_call->conn->dce_ctx->lp_ctx), dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, &sctx);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return ntstatus_to_werror(nterr);
+ }
+
+ nterr = share_list_all(mem_ctx, sctx, &numshares, &snames);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return ntstatus_to_werror(nterr);
+ }
+
+ switch (r->in.info_ctr->level) {
+ case 0:
+ {
+ int i, y = 0;
+ int count;
+ struct srvsvc_NetShareCtr0 *ctr0;
+
+ ctr0 = talloc(mem_ctx, struct srvsvc_NetShareCtr0);
+ W_ERROR_HAVE_NO_MEMORY(ctr0);
+
+ count = numshares;
+ ctr0->count = count;
+ ctr0->array = NULL;
+
+ if (ctr0->count == 0) {
+ r->out.info_ctr->ctr.ctr0 = ctr0;
+ return WERR_OK;
+ }
+
+ ctr0->array = talloc_array(mem_ctx, struct srvsvc_NetShareInfo0, count);
+ W_ERROR_HAVE_NO_MEMORY(ctr0->array);
+
+ for (i=0; i < count; i++) {
+ WERROR status;
+ union srvsvc_NetShareInfo info;
+ enum srvsvc_ShareType type;
+
+ nterr = share_get_config(mem_ctx, sctx, snames[i], &scfg);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ DEBUG(1, ("ERROR: Service [%s] disappeared after enumeration", snames[i]));
+ return WERR_GENERAL_FAILURE;
+ }
+
+ type = dcesrv_common_get_share_type(mem_ctx, dce_ctx, scfg);
+ if (type & STYPE_HIDDEN) {
+ ctr0->count--;
+ talloc_free(scfg);
+ continue;
+ }
+
+ info.info0 = &ctr0->array[y];
+ status = dcesrv_srvsvc_fiel_ShareInfo(dce_call, mem_ctx, scfg, r->in.info_ctr->level, &info);
+ W_ERROR_NOT_OK_RETURN(status);
+ talloc_free(scfg);
+ y++;
+ }
+ talloc_free(snames);
+
+ r->out.info_ctr->ctr.ctr0 = ctr0;
+ *r->out.totalentries = r->out.info_ctr->ctr.ctr0->count;
+
+ return WERR_OK;
+ }
+ case 1:
+ {
+ int i, y = 0;
+ int count;
+ struct srvsvc_NetShareCtr1 *ctr1;
+
+ ctr1 = talloc(mem_ctx, struct srvsvc_NetShareCtr1);
+ W_ERROR_HAVE_NO_MEMORY(ctr1);
+
+ count = numshares;
+ ctr1->count = count;
+ ctr1->array = NULL;
+
+ if (ctr1->count == 0) {
+ r->out.info_ctr->ctr.ctr1 = ctr1;
+ return WERR_OK;
+ }
+
+ ctr1->array = talloc_array(mem_ctx, struct srvsvc_NetShareInfo1, count);
+ W_ERROR_HAVE_NO_MEMORY(ctr1->array);
+
+ for (i=0; i < count; i++) {
+ WERROR status;
+ union srvsvc_NetShareInfo info;
+ enum srvsvc_ShareType type;
+
+ nterr = share_get_config(mem_ctx, sctx, snames[i], &scfg);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ DEBUG(1, ("ERROR: Service [%s] disappeared after enumeration", snames[i]));
+ return WERR_GENERAL_FAILURE;
+ }
+
+ type = dcesrv_common_get_share_type(mem_ctx, dce_ctx, scfg);
+ if (type & STYPE_HIDDEN) {
+ ctr1->count--;
+ talloc_free(scfg);
+ continue;
+ }
+
+ info.info1 = &ctr1->array[y];
+ status = dcesrv_srvsvc_fiel_ShareInfo(dce_call, mem_ctx, scfg, r->in.info_ctr->level, &info);
+ W_ERROR_NOT_OK_RETURN(status);
+ talloc_free(scfg);
+ y++;
+ }
+ talloc_free(snames);
+
+ r->out.info_ctr->ctr.ctr1 = ctr1;
+ *r->out.totalentries = r->out.info_ctr->ctr.ctr1->count;
+
+ return WERR_OK;
+ }
+ case 2:
+ {
+ int i, y = 0;
+ int count;
+ struct srvsvc_NetShareCtr2 *ctr2;
+
+ SRVSVC_CHECK_ADMIN_ACCESS;
+
+ ctr2 = talloc(mem_ctx, struct srvsvc_NetShareCtr2);
+ W_ERROR_HAVE_NO_MEMORY(ctr2);
+
+ count = numshares;
+ ctr2->count = count;
+ ctr2->array = NULL;
+
+ if (ctr2->count == 0) {
+ r->out.info_ctr->ctr.ctr2 = ctr2;
+ return WERR_OK;
+ }
+
+ ctr2->array = talloc_array(mem_ctx, struct srvsvc_NetShareInfo2, count);
+ W_ERROR_HAVE_NO_MEMORY(ctr2->array);
+
+ for (i=0; i < count; i++) {
+ WERROR status;
+ union srvsvc_NetShareInfo info;
+ enum srvsvc_ShareType type;
+
+ nterr = share_get_config(mem_ctx, sctx, snames[i], &scfg);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ DEBUG(1, ("ERROR: Service [%s] disappeared after enumeration", snames[i]));
+ return WERR_GENERAL_FAILURE;
+ }
+
+ type = dcesrv_common_get_share_type(mem_ctx, dce_ctx, scfg);
+ if (type & STYPE_HIDDEN) {
+ ctr2->count--;
+ talloc_free(scfg);
+ continue;
+ }
+
+ info.info2 = &ctr2->array[y];
+ status = dcesrv_srvsvc_fiel_ShareInfo(dce_call, mem_ctx, scfg, r->in.info_ctr->level, &info);
+ W_ERROR_NOT_OK_RETURN(status);
+ talloc_free(scfg);
+ y++;
+ }
+ talloc_free(snames);
+
+ r->out.info_ctr->ctr.ctr2 = ctr2;
+ *r->out.totalentries = r->out.info_ctr->ctr.ctr2->count;
+
+ return WERR_OK;
+ }
+ case 502:
+ {
+ int i, y = 0;
+ int count;
+ struct srvsvc_NetShareCtr502 *ctr502;
+
+ SRVSVC_CHECK_ADMIN_ACCESS;
+
+ ctr502 = talloc(mem_ctx, struct srvsvc_NetShareCtr502);
+ W_ERROR_HAVE_NO_MEMORY(ctr502);
+
+ count = numshares;
+ ctr502->count = count;
+ ctr502->array = NULL;
+
+ if (ctr502->count == 0) {
+ r->out.info_ctr->ctr.ctr502 = ctr502;
+ return WERR_OK;
+ }
+
+ ctr502->array = talloc_array(mem_ctx, struct srvsvc_NetShareInfo502, count);
+ W_ERROR_HAVE_NO_MEMORY(ctr502->array);
+
+ for (i=0; i < count; i++) {
+ WERROR status;
+ union srvsvc_NetShareInfo info;
+ enum srvsvc_ShareType type;
+
+ nterr = share_get_config(mem_ctx, sctx, snames[i], &scfg);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ DEBUG(1, ("ERROR: Service [%s] disappeared after enumeration", snames[i]));
+ return WERR_GENERAL_FAILURE;
+ }
+
+ type = dcesrv_common_get_share_type(mem_ctx, dce_ctx, scfg);
+ if (type & STYPE_HIDDEN) {
+ ctr502->count--;
+ talloc_free(scfg);
+ continue;
+ }
+
+ info.info502 = &ctr502->array[y];
+ status = dcesrv_srvsvc_fiel_ShareInfo(dce_call, mem_ctx, scfg, r->in.info_ctr->level, &info);
+ W_ERROR_NOT_OK_RETURN(status);
+ talloc_free(scfg);
+ y++;
+ }
+ talloc_free(snames);
+
+ r->out.info_ctr->ctr.ctr502 = ctr502;
+ *r->out.totalentries = r->out.info_ctr->ctr.ctr502->count;
+
+ return WERR_OK;
+ }
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ return WERR_UNKNOWN_LEVEL;
+}
+
+
+/*
+ srvsvc_NetShareDelStart
+*/
+static WERROR dcesrv_srvsvc_NetShareDelStart(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetShareDelStart *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NetShareDelCommit
+*/
+static WERROR dcesrv_srvsvc_NetShareDelCommit(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetShareDelCommit *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NetGetFileSecurity
+*/
+static WERROR dcesrv_srvsvc_NetGetFileSecurity(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetGetFileSecurity *r)
+{
+ struct sec_desc_buf *sd_buf;
+ struct ntvfs_context *ntvfs_ctx = NULL;
+ struct ntvfs_request *ntvfs_req;
+ union smb_fileinfo *io;
+ NTSTATUS nt_status;
+
+ nt_status = srvsvc_create_ntvfs_context(dce_call, mem_ctx, r->in.share, &ntvfs_ctx);
+ if (!NT_STATUS_IS_OK(nt_status)) return ntstatus_to_werror(nt_status);
+
+ ntvfs_req = ntvfs_request_create(ntvfs_ctx, mem_ctx,
+ dce_call->conn->auth_state.session_info,
+ 0,
+ dce_call->time,
+ NULL, NULL, 0);
+ W_ERROR_HAVE_NO_MEMORY(ntvfs_req);
+
+ sd_buf = talloc(mem_ctx, struct sec_desc_buf);
+ W_ERROR_HAVE_NO_MEMORY(sd_buf);
+
+ io = talloc(mem_ctx, union smb_fileinfo);
+ W_ERROR_HAVE_NO_MEMORY(io);
+
+ io->query_secdesc.level = RAW_FILEINFO_SEC_DESC;
+ io->query_secdesc.in.file.path = r->in.file;
+ io->query_secdesc.in.secinfo_flags = r->in.securityinformation;
+
+ nt_status = ntvfs_qpathinfo(ntvfs_req, io);
+ if (!NT_STATUS_IS_OK(nt_status)) return ntstatus_to_werror(nt_status);
+
+ sd_buf->sd = io->query_secdesc.out.sd;
+
+ *r->out.sd_buf = sd_buf;
+ return WERR_OK;
+}
+
+
+/*
+ srvsvc_NetSetFileSecurity
+*/
+static WERROR dcesrv_srvsvc_NetSetFileSecurity(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetSetFileSecurity *r)
+{
+ struct ntvfs_context *ntvfs_ctx;
+ struct ntvfs_request *ntvfs_req;
+ union smb_setfileinfo *io;
+ NTSTATUS nt_status;
+
+ nt_status = srvsvc_create_ntvfs_context(dce_call, mem_ctx, r->in.share, &ntvfs_ctx);
+ if (!NT_STATUS_IS_OK(nt_status)) return ntstatus_to_werror(nt_status);
+
+ ntvfs_req = ntvfs_request_create(ntvfs_ctx, mem_ctx,
+ dce_call->conn->auth_state.session_info,
+ 0,
+ dce_call->time,
+ NULL, NULL, 0);
+ W_ERROR_HAVE_NO_MEMORY(ntvfs_req);
+
+ io = talloc(mem_ctx, union smb_setfileinfo);
+ W_ERROR_HAVE_NO_MEMORY(io);
+
+ io->set_secdesc.level = RAW_FILEINFO_SEC_DESC;
+ io->set_secdesc.in.file.path = r->in.file;
+ io->set_secdesc.in.secinfo_flags = r->in.securityinformation;
+ io->set_secdesc.in.sd = r->in.sd_buf->sd;
+
+ nt_status = ntvfs_setpathinfo(ntvfs_req, io);
+ if (!NT_STATUS_IS_OK(nt_status)) return ntstatus_to_werror(nt_status);
+
+ return WERR_OK;
+}
+
+
+/*
+ srvsvc_NetServerTransportAddEx
+*/
+static WERROR dcesrv_srvsvc_NetServerTransportAddEx(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetServerTransportAddEx *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NetServerSetServiceBitsEx
+*/
+static WERROR dcesrv_srvsvc_NetServerSetServiceBitsEx(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetServerSetServiceBitsEx *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NETRDFSGETVERSION
+*/
+static WERROR dcesrv_srvsvc_NETRDFSGETVERSION(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NETRDFSGETVERSION *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NETRDFSCREATELOCALPARTITION
+*/
+static WERROR dcesrv_srvsvc_NETRDFSCREATELOCALPARTITION(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NETRDFSCREATELOCALPARTITION *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NETRDFSDELETELOCALPARTITION
+*/
+static WERROR dcesrv_srvsvc_NETRDFSDELETELOCALPARTITION(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NETRDFSDELETELOCALPARTITION *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NETRDFSSETLOCALVOLUMESTATE
+*/
+static WERROR dcesrv_srvsvc_NETRDFSSETLOCALVOLUMESTATE(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NETRDFSSETLOCALVOLUMESTATE *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NETRDFSSETSERVERINFO
+*/
+static WERROR dcesrv_srvsvc_NETRDFSSETSERVERINFO(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NETRDFSSETSERVERINFO *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NETRDFSCREATEEXITPOINT
+*/
+static WERROR dcesrv_srvsvc_NETRDFSCREATEEXITPOINT(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NETRDFSCREATEEXITPOINT *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NETRDFSDELETEEXITPOINT
+*/
+static WERROR dcesrv_srvsvc_NETRDFSDELETEEXITPOINT(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NETRDFSDELETEEXITPOINT *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NETRDFSMODIFYPREFIX
+*/
+static WERROR dcesrv_srvsvc_NETRDFSMODIFYPREFIX(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NETRDFSMODIFYPREFIX *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NETRDFSFIXLOCALVOLUME
+*/
+static WERROR dcesrv_srvsvc_NETRDFSFIXLOCALVOLUME(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NETRDFSFIXLOCALVOLUME *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NETRDFSMANAGERREPORTSITEINFO
+*/
+static WERROR dcesrv_srvsvc_NETRDFSMANAGERREPORTSITEINFO(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NETRDFSMANAGERREPORTSITEINFO *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ srvsvc_NETRSERVERTRANSPORTDELEX
+*/
+static WERROR dcesrv_srvsvc_NETRSERVERTRANSPORTDELEX(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NETRSERVERTRANSPORTDELEX *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+/*
+ srvsvc_NetShareDel
+*/
+static WERROR dcesrv_srvsvc_NetShareDel(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetShareDel *r)
+{
+ NTSTATUS nterr;
+ struct share_context *sctx;
+
+ nterr = share_get_context_by_name(mem_ctx, lp_share_backend(dce_call->conn->dce_ctx->lp_ctx), dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, &sctx);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return ntstatus_to_werror(nterr);
+ }
+
+ nterr = share_remove(sctx, r->in.share_name);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return ntstatus_to_werror(nterr);
+ }
+
+ return WERR_OK;
+}
+
+/*
+ srvsvc_NetSetServiceBits
+*/
+static WERROR dcesrv_srvsvc_NetSetServiceBits(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NetSetServiceBits *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+/*
+ srvsvc_NETRPRNAMECANONICALIZE
+*/
+static WERROR dcesrv_srvsvc_NETRPRNAMECANONICALIZE(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct srvsvc_NETRPRNAMECANONICALIZE *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_srvsvc_s.c"
diff --git a/source4/rpc_server/srvsvc/srvsvc_ntvfs.c b/source4/rpc_server/srvsvc/srvsvc_ntvfs.c
new file mode 100644
index 0000000000..f1cb35bdd8
--- /dev/null
+++ b/source4/rpc_server/srvsvc/srvsvc_ntvfs.c
@@ -0,0 +1,139 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ srvsvc pipe ntvfs helper functions
+
+ Copyright (C) Stefan (metze) 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 <http://www.gnu.org/licenses/>.
+*/
+#include "includes.h"
+#include "ntvfs/ntvfs.h"
+#include "rpc_server/dcerpc_server.h"
+#include "librpc/gen_ndr/ndr_srvsvc.h"
+#include "rpc_server/common/common.h"
+#include "rpc_server/srvsvc/proto.h"
+#include "lib/socket/socket.h"
+#include "param/param.h"
+
+struct socket_address *srvsvc_get_my_addr(void *p, TALLOC_CTX *mem_ctx)
+{
+ struct dcesrv_connection *conn = talloc_get_type(p, struct dcesrv_connection);
+ return dcesrv_connection_get_my_addr(conn, mem_ctx);
+}
+
+struct socket_address *srvsvc_get_peer_addr(void *p, TALLOC_CTX *mem_ctx)
+{
+ struct dcesrv_connection *conn = talloc_get_type(p, struct dcesrv_connection);
+ return dcesrv_connection_get_peer_addr(conn, mem_ctx);
+}
+
+struct srvsvc_ntvfs_ctx {
+ struct ntvfs_context *ntvfs;
+};
+
+static int srvsvc_ntvfs_ctx_destructor(struct srvsvc_ntvfs_ctx *c)
+{
+ ntvfs_disconnect(c->ntvfs);
+ return 0;
+}
+
+NTSTATUS srvsvc_create_ntvfs_context(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ const char *share,
+ struct ntvfs_context **_ntvfs)
+{
+ NTSTATUS status;
+ struct srvsvc_ntvfs_ctx *c;
+ struct ntvfs_request *ntvfs_req;
+ enum ntvfs_type type;
+ struct share_context *sctx;
+ struct share_config *scfg;
+ const char *sharetype;
+
+ status = share_get_context_by_name(mem_ctx, lp_share_backend(dce_call->conn->dce_ctx->lp_ctx), dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, &sctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = share_get_config(mem_ctx, sctx, share, &scfg);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("srvsvc_create_ntvfs_context: couldn't find service %s\n", share));
+ return status;
+ }
+
+#if 0 /* TODO: fix access cecking */
+ if (!socket_check_access(dce_call->connection->socket,
+ scfg->name,
+ share_string_list_option(scfg, SHARE_HOSTS_ALLOW),
+ share_string_list_option(scfg, SHARE_HOSTS_DENY))) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+#endif
+
+ /* work out what sort of connection this is */
+ sharetype = share_string_option(scfg, SHARE_TYPE, SHARE_TYPE_DEFAULT);
+ if (sharetype && strcmp(sharetype, "IPC") == 0) {
+ type = NTVFS_IPC;
+ } else if (sharetype && strcmp(sharetype, "PRINTER")) {
+ type = NTVFS_PRINT;
+ } else {
+ type = NTVFS_DISK;
+ }
+
+ c = talloc(mem_ctx, struct srvsvc_ntvfs_ctx);
+ NT_STATUS_HAVE_NO_MEMORY(c);
+
+ /* init ntvfs function pointers */
+ status = ntvfs_init_connection(c, scfg, type,
+ PROTOCOL_NT1,
+ 0,/* ntvfs_client_caps */
+ dce_call->event_ctx,
+ dce_call->conn->msg_ctx,
+ dce_call->conn->dce_ctx->lp_ctx,
+ dce_call->conn->server_id,
+ &c->ntvfs);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("srvsvc_create_ntvfs_context: ntvfs_init_connection failed for service %s\n",
+ scfg->name));
+ return status;
+ }
+ talloc_set_destructor(c, srvsvc_ntvfs_ctx_destructor);
+
+ /*
+ * NOTE: we only set the addr callbacks as we're not interesseted in oplocks or in getting file handles
+ */
+ status = ntvfs_set_addr_callbacks(c->ntvfs, srvsvc_get_my_addr, srvsvc_get_peer_addr, dce_call->conn);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("srvsvc_create_ntvfs_context: NTVFS failed to set the addr callbacks!\n"));
+ return status;
+ }
+
+ ntvfs_req = ntvfs_request_create(c->ntvfs, mem_ctx,
+ dce_call->conn->auth_state.session_info,
+ 0, /* TODO: fill in PID */
+ dce_call->time,
+ NULL, NULL, 0);
+ NT_STATUS_HAVE_NO_MEMORY(ntvfs_req);
+
+ /* Invoke NTVFS connection hook */
+ status = ntvfs_connect(ntvfs_req, scfg->name);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("srvsvc_create_ntvfs_context: NTVFS ntvfs_connect() failed!\n"));
+ return status;
+ }
+
+ *_ntvfs = c->ntvfs;
+ return NT_STATUS_OK;
+}
diff --git a/source4/rpc_server/unixinfo/dcesrv_unixinfo.c b/source4/rpc_server/unixinfo/dcesrv_unixinfo.c
new file mode 100644
index 0000000000..a1d76ef811
--- /dev/null
+++ b/source4/rpc_server/unixinfo/dcesrv_unixinfo.c
@@ -0,0 +1,241 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the unixinfo pipe
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "rpc_server/dcerpc_server.h"
+#include "rpc_server/common/common.h"
+#include "librpc/gen_ndr/ndr_unixinfo.h"
+#include "libcli/wbclient/wbclient.h"
+#include "lib/events/events.h"
+#include "system/passwd.h"
+
+static NTSTATUS dcerpc_unixinfo_bind(struct dcesrv_call_state *dce_call,
+ const struct dcesrv_interface *iface)
+{
+ struct wbc_context *wbc_ctx;
+
+ wbc_ctx = wbc_init(dce_call->context, dce_call->msg_ctx,
+ dce_call->event_ctx);
+ NT_STATUS_HAVE_NO_MEMORY(wbc_ctx);
+
+ dce_call->context->private_data = wbc_ctx;
+
+ return NT_STATUS_OK;
+}
+
+#define DCESRV_INTERFACE_UNIXINFO_BIND dcerpc_unixinfo_bind
+
+static NTSTATUS dcesrv_unixinfo_SidToUid(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct unixinfo_SidToUid *r)
+{
+ NTSTATUS status;
+ struct wbc_context *wbc_ctx = talloc_get_type_abort(
+ dce_call->context->private_data,
+ struct wbc_context);
+ struct id_mapping *ids;
+ struct composite_context *ctx;
+
+ DEBUG(5, ("dcesrv_unixinfo_SidToUid called\n"));
+
+ ids = talloc(mem_ctx, struct id_mapping);
+ NT_STATUS_HAVE_NO_MEMORY(ids);
+
+ ids->sid = &r->in.sid;
+ ids->status = NT_STATUS_NONE_MAPPED;
+ ids->unixid = NULL;
+ ctx = wbc_sids_to_xids_send(wbc_ctx, ids, 1, ids);
+ NT_STATUS_HAVE_NO_MEMORY(ctx);
+
+ status = wbc_sids_to_xids_recv(ctx, &ids);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ if (ids->unixid->type == ID_TYPE_BOTH ||
+ ids->unixid->type == ID_TYPE_UID) {
+ *r->out.uid = ids->unixid->id;
+ return NT_STATUS_OK;
+ } else {
+ return NT_STATUS_INVALID_SID;
+ }
+}
+
+static NTSTATUS dcesrv_unixinfo_UidToSid(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct unixinfo_UidToSid *r)
+{
+ struct wbc_context *wbc_ctx = talloc_get_type_abort(
+ dce_call->context->private_data,
+ struct wbc_context);
+ struct id_mapping *ids;
+ struct composite_context *ctx;
+ uint32_t uid;
+ NTSTATUS status;
+
+ DEBUG(5, ("dcesrv_unixinfo_UidToSid called\n"));
+
+ uid = r->in.uid; /* This cuts uid to 32 bit */
+ if ((uint64_t)uid != r->in.uid) {
+ DEBUG(10, ("uid out of range\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ ids = talloc(mem_ctx, struct id_mapping);
+ NT_STATUS_HAVE_NO_MEMORY(ids);
+
+ ids->sid = NULL;
+ ids->status = NT_STATUS_NONE_MAPPED;
+ ids->unixid = talloc(ids, struct unixid);
+ NT_STATUS_HAVE_NO_MEMORY(ids->unixid);
+
+ ids->unixid->id = uid;
+ ids->unixid->type = ID_TYPE_UID;
+
+ ctx = wbc_xids_to_sids_send(wbc_ctx, ids, 1, ids);
+ NT_STATUS_HAVE_NO_MEMORY(ctx);
+
+ status = wbc_xids_to_sids_recv(ctx, &ids);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ r->out.sid = ids->sid;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS dcesrv_unixinfo_SidToGid(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct unixinfo_SidToGid *r)
+{
+ NTSTATUS status;
+ struct wbc_context *wbc_ctx = talloc_get_type_abort(
+ dce_call->context->private_data,
+ struct wbc_context);
+ struct id_mapping *ids;
+ struct composite_context *ctx;
+
+ DEBUG(5, ("dcesrv_unixinfo_SidToGid called\n"));
+
+ ids = talloc(mem_ctx, struct id_mapping);
+ NT_STATUS_HAVE_NO_MEMORY(ids);
+
+ ids->sid = &r->in.sid;
+ ids->status = NT_STATUS_NONE_MAPPED;
+ ids->unixid = NULL;
+ ctx = wbc_sids_to_xids_send(wbc_ctx, ids, 1, ids);
+ NT_STATUS_HAVE_NO_MEMORY(ctx);
+
+ status = wbc_sids_to_xids_recv(ctx, &ids);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ if (ids->unixid->type == ID_TYPE_BOTH ||
+ ids->unixid->type == ID_TYPE_GID) {
+ *r->out.gid = ids->unixid->id;
+ return NT_STATUS_OK;
+ } else {
+ return NT_STATUS_INVALID_SID;
+ }
+}
+
+static NTSTATUS dcesrv_unixinfo_GidToSid(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct unixinfo_GidToSid *r)
+{
+ struct wbc_context *wbc_ctx = talloc_get_type_abort(
+ dce_call->context->private_data,
+ struct wbc_context);
+ struct id_mapping *ids;
+ struct composite_context *ctx;
+ uint32_t gid;
+ NTSTATUS status;
+
+ DEBUG(5, ("dcesrv_unixinfo_GidToSid called\n"));
+
+ gid = r->in.gid; /* This cuts gid to 32 bit */
+ if ((uint64_t)gid != r->in.gid) {
+ DEBUG(10, ("gid out of range\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ ids = talloc(mem_ctx, struct id_mapping);
+ NT_STATUS_HAVE_NO_MEMORY(ids);
+
+ ids->sid = NULL;
+ ids->status = NT_STATUS_NONE_MAPPED;
+ ids->unixid = talloc(ids, struct unixid);
+ NT_STATUS_HAVE_NO_MEMORY(ids->unixid);
+
+ ids->unixid->id = gid;
+ ids->unixid->type = ID_TYPE_GID;
+
+ ctx = wbc_xids_to_sids_send(wbc_ctx, ids, 1, ids);
+ NT_STATUS_HAVE_NO_MEMORY(ctx);
+
+ status = wbc_xids_to_sids_recv(ctx, &ids);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ r->out.sid = ids->sid;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS dcesrv_unixinfo_GetPWUid(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct unixinfo_GetPWUid *r)
+{
+ int i;
+
+ *r->out.count = 0;
+
+ r->out.infos = talloc_zero_array(mem_ctx, struct unixinfo_GetPWUidInfo,
+ *r->in.count);
+ NT_STATUS_HAVE_NO_MEMORY(r->out.infos);
+ *r->out.count = *r->in.count;
+
+ for (i=0; i < *r->in.count; i++) {
+ uid_t uid;
+ struct passwd *pwd;
+
+ uid = r->in.uids[i];
+ pwd = getpwuid(uid);
+ if (pwd == NULL) {
+ DEBUG(10, ("uid %d not found\n", uid));
+ r->out.infos[i].homedir = "";
+ r->out.infos[i].shell = "";
+ r->out.infos[i].status = NT_STATUS_NO_SUCH_USER;
+ continue;
+ }
+
+ r->out.infos[i].homedir = talloc_strdup(mem_ctx, pwd->pw_dir);
+ r->out.infos[i].shell = talloc_strdup(mem_ctx, pwd->pw_shell);
+
+ if ((r->out.infos[i].homedir == NULL) ||
+ (r->out.infos[i].shell == NULL)) {
+ r->out.infos[i].homedir = "";
+ r->out.infos[i].shell = "";
+ r->out.infos[i].status = NT_STATUS_NO_MEMORY;
+ continue;
+ }
+
+ r->out.infos[i].status = NT_STATUS_OK;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_unixinfo_s.c"
diff --git a/source4/rpc_server/winreg/README b/source4/rpc_server/winreg/README
new file mode 100644
index 0000000000..03a81e776b
--- /dev/null
+++ b/source4/rpc_server/winreg/README
@@ -0,0 +1,3 @@
+This is the RPC server for the registry for Samba. It is basically a
+front-end for the registry library in lib/registry. See
+lib/registry/README for more details.
diff --git a/source4/rpc_server/winreg/rpc_winreg.c b/source4/rpc_server/winreg/rpc_winreg.c
new file mode 100644
index 0000000000..6158730759
--- /dev/null
+++ b/source4/rpc_server/winreg/rpc_winreg.c
@@ -0,0 +1,697 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the winreg pipe
+
+ Copyright (C) 2004 Jelmer Vernooij, jelmer@samba.org
+ Copyright (C) 2008 Matthias Dieter Wallnöfer, mwallnoefer@yahoo.de
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "rpc_server/dcerpc_server.h"
+#include "lib/registry/registry.h"
+#include "librpc/gen_ndr/ndr_winreg.h"
+#include "rpc_server/common/common.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "param/param.h"
+#include "libcli/security/security.h"
+
+enum handle_types { HTYPE_REGVAL, HTYPE_REGKEY };
+
+static NTSTATUS dcerpc_winreg_bind(struct dcesrv_call_state *dce_call,
+ const struct dcesrv_interface *iface)
+{
+ struct registry_context *ctx;
+ WERROR err;
+
+ err = reg_open_samba(dce_call->context,
+ &ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, dce_call->conn->auth_state.session_info,
+ NULL);
+
+ if (!W_ERROR_IS_OK(err)) {
+ DEBUG(0, ("Error opening registry: %s\n", win_errstr(err)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ dce_call->context->private_data = ctx;
+
+ return NT_STATUS_OK;
+}
+
+#define DCESRV_INTERFACE_WINREG_BIND dcerpc_winreg_bind
+
+static WERROR dcesrv_winreg_openhive(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx, uint32_t hkey,
+ struct policy_handle **outh)
+{
+ struct registry_context *ctx = dce_call->context->private_data;
+ struct dcesrv_handle *h;
+ WERROR result;
+
+ h = dcesrv_handle_new(dce_call->context, HTYPE_REGKEY);
+
+ result = reg_get_predefined_key(ctx, hkey,
+ (struct registry_key **)&h->data);
+ if (!W_ERROR_IS_OK(result)) {
+ return result;
+ }
+ *outh = &h->wire_handle;
+
+ return result;
+}
+
+#define func_winreg_OpenHive(k,n) static WERROR dcesrv_winreg_Open ## k (struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct winreg_Open ## k *r) \
+{ \
+ return dcesrv_winreg_openhive (dce_call, mem_ctx, n, &r->out.handle);\
+}
+
+func_winreg_OpenHive(HKCR,HKEY_CLASSES_ROOT)
+func_winreg_OpenHive(HKCU,HKEY_CURRENT_USER)
+func_winreg_OpenHive(HKLM,HKEY_LOCAL_MACHINE)
+func_winreg_OpenHive(HKPD,HKEY_PERFORMANCE_DATA)
+func_winreg_OpenHive(HKU,HKEY_USERS)
+func_winreg_OpenHive(HKCC,HKEY_CURRENT_CONFIG)
+func_winreg_OpenHive(HKDD,HKEY_DYN_DATA)
+func_winreg_OpenHive(HKPT,HKEY_PERFORMANCE_TEXT)
+func_winreg_OpenHive(HKPN,HKEY_PERFORMANCE_NLSTEXT)
+
+/*
+ winreg_CloseKey
+*/
+static WERROR dcesrv_winreg_CloseKey(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_CloseKey *r)
+{
+ struct dcesrv_handle *h;
+
+ DCESRV_PULL_HANDLE_FAULT(h, r->in.handle, HTYPE_REGKEY);
+
+ talloc_free(h);
+
+ ZERO_STRUCTP(r->out.handle);
+
+ return WERR_OK;
+}
+
+/*
+ winreg_CreateKey
+*/
+static WERROR dcesrv_winreg_CreateKey(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_CreateKey *r)
+{
+ struct dcesrv_handle *h, *newh;
+ struct security_descriptor sd;
+ struct registry_key *key;
+ WERROR result;
+
+ DCESRV_PULL_HANDLE_FAULT(h, r->in.handle, HTYPE_REGKEY);
+ key = h->data;
+
+ newh = dcesrv_handle_new(dce_call->context, HTYPE_REGKEY);
+
+ switch (security_session_user_level(dce_call->conn->auth_state.session_info))
+ {
+ case SECURITY_SYSTEM:
+ case SECURITY_ADMINISTRATOR:
+ /* the security descriptor is optional */
+ if (r->in.secdesc != NULL) {
+ DATA_BLOB sdblob;
+ enum ndr_err_code ndr_err;
+ sdblob.data = r->in.secdesc->sd.data;
+ sdblob.length = r->in.secdesc->sd.len;
+ if (sdblob.data == NULL) {
+ return WERR_INVALID_PARAM;
+ }
+ ndr_err = ndr_pull_struct_blob_all(&sdblob, mem_ctx, NULL, &sd,
+ (ndr_pull_flags_fn_t)ndr_pull_security_descriptor);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return WERR_INVALID_PARAM;
+ }
+ }
+
+ result = reg_key_add_name(newh, key, r->in.name.name, NULL,
+ r->in.secdesc?&sd:NULL, (struct registry_key **)&newh->data);
+ if (W_ERROR_IS_OK(result)) {
+ r->out.new_handle = &newh->wire_handle;
+ } else {
+ talloc_free(newh);
+ }
+
+ return result;
+ default:
+ return WERR_ACCESS_DENIED;
+ }
+}
+
+
+/*
+ winreg_DeleteKey
+*/
+static WERROR dcesrv_winreg_DeleteKey(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_DeleteKey *r)
+{
+ struct dcesrv_handle *h;
+ struct registry_key *key;
+
+ DCESRV_PULL_HANDLE_FAULT(h, r->in.handle, HTYPE_REGKEY);
+ key = h->data;
+
+ switch (security_session_user_level(dce_call->conn->auth_state.session_info))
+ {
+ case SECURITY_SYSTEM:
+ case SECURITY_ADMINISTRATOR:
+ return reg_key_del(key, r->in.key.name);
+ default:
+ return WERR_ACCESS_DENIED;
+ }
+}
+
+
+/*
+ winreg_DeleteValue
+*/
+static WERROR dcesrv_winreg_DeleteValue(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_DeleteValue *r)
+{
+ struct dcesrv_handle *h;
+ struct registry_key *key;
+
+ DCESRV_PULL_HANDLE_FAULT(h, r->in.handle, HTYPE_REGKEY);
+ key = h->data;
+
+ switch (security_session_user_level(dce_call->conn->auth_state.session_info))
+ {
+ case SECURITY_SYSTEM:
+ case SECURITY_ADMINISTRATOR:
+ return reg_del_value(key, r->in.value.name);
+ default:
+ return WERR_ACCESS_DENIED;
+ }
+}
+
+
+/*
+ winreg_EnumKey
+*/
+static WERROR dcesrv_winreg_EnumKey(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_EnumKey *r)
+{
+ struct dcesrv_handle *h;
+ struct registry_key *key;
+ const char *name, *classname;
+ NTTIME last_mod;
+ WERROR result;
+
+ DCESRV_PULL_HANDLE_FAULT(h, r->in.handle, HTYPE_REGKEY);
+ key = h->data;
+
+ result = reg_key_get_subkey_by_index(mem_ctx,
+ key, r->in.enum_index, &name, &classname, &last_mod);
+
+ if (2*strlen_m_term(name) > r->in.name->size) {
+ return WERR_MORE_DATA;
+ }
+
+ if (name != NULL) {
+ r->out.name->name = name;
+ r->out.name->length = 2*strlen_m_term(name);
+ } else {
+ r->out.name->name = r->in.name->name;
+ r->out.name->length = r->in.name->length;
+ }
+ r->out.name->size = r->in.name->size;
+
+ r->out.keyclass = r->in.keyclass;
+ if (classname != NULL) {
+ r->out.keyclass->name = classname;
+ r->out.keyclass->length = 2*strlen_m_term(classname);
+ } else {
+ r->out.keyclass->name = r->in.keyclass->name;
+ r->out.keyclass->length = r->in.keyclass->length;
+ }
+ r->out.keyclass->size = r->in.keyclass->size;
+
+ if (r->in.last_changed_time != NULL)
+ r->out.last_changed_time = &last_mod;
+
+ return result;
+}
+
+
+/*
+ winreg_EnumValue
+*/
+static WERROR dcesrv_winreg_EnumValue(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_EnumValue *r)
+{
+ struct dcesrv_handle *h;
+ struct registry_key *key;
+ const char *data_name;
+ uint32_t data_type;
+ DATA_BLOB data;
+ WERROR result;
+
+ DCESRV_PULL_HANDLE_FAULT(h, r->in.handle, HTYPE_REGKEY);
+ key = h->data;
+
+ result = reg_key_get_value_by_index(mem_ctx, key,
+ r->in.enum_index, &data_name, &data_type, &data);
+
+ if (!W_ERROR_IS_OK(result)) {
+ /* if the lookup wasn't successful, send client query back */
+ data_name = r->in.name->name;
+ data_type = *r->in.type;
+ data.data = r->in.value;
+ data.length = *r->in.length;
+ }
+
+ /* check if there is enough room for the name */
+ if (r->in.name->size < 2*strlen_m_term(data_name)) {
+ return WERR_MORE_DATA;
+ }
+
+ /* "data_name" is NULL when we query the default attribute */
+ if (data_name != NULL) {
+ r->out.name->name = data_name;
+ r->out.name->length = 2*strlen_m_term(data_name);
+ } else {
+ r->out.name->name = r->in.name->name;
+ r->out.name->length = r->in.name->length;
+ }
+ r->out.name->size = r->in.name->size;
+
+ r->out.type = talloc(mem_ctx, uint32_t);
+ if (!r->out.type) {
+ return WERR_NOMEM;
+ }
+ *r->out.type = data_type;
+
+ /* check the client has enough room for the value */
+ if (r->in.value != NULL &&
+ r->in.size != NULL &&
+ data.length > *r->in.size) {
+ return WERR_MORE_DATA;
+ }
+
+ if (r->in.value != NULL) {
+ r->out.value = data.data;
+ }
+
+ if (r->in.size != NULL) {
+ r->out.size = talloc(mem_ctx, uint32_t);
+ *r->out.size = data.length;
+ r->out.length = r->out.size;
+ }
+
+ return result;
+}
+
+
+/*
+ winreg_FlushKey
+*/
+static WERROR dcesrv_winreg_FlushKey(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_FlushKey *r)
+{
+ struct dcesrv_handle *h;
+ struct registry_key *key;
+
+ DCESRV_PULL_HANDLE_FAULT(h, r->in.handle, HTYPE_REGKEY);
+ key = h->data;
+
+ switch (security_session_user_level(dce_call->conn->auth_state.session_info))
+ {
+ case SECURITY_SYSTEM:
+ case SECURITY_ADMINISTRATOR:
+ return reg_key_flush(key);
+ default:
+ return WERR_ACCESS_DENIED;
+ }
+}
+
+
+/*
+ winreg_GetKeySecurity
+*/
+static WERROR dcesrv_winreg_GetKeySecurity(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_GetKeySecurity *r)
+{
+ struct dcesrv_handle *h;
+
+ DCESRV_PULL_HANDLE_FAULT(h, r->in.handle, HTYPE_REGKEY);
+
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/*
+ winreg_LoadKey
+*/
+static WERROR dcesrv_winreg_LoadKey(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_LoadKey *r)
+{
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/*
+ winreg_NotifyChangeKeyValue
+*/
+static WERROR dcesrv_winreg_NotifyChangeKeyValue(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_NotifyChangeKeyValue *r)
+{
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/*
+ winreg_OpenKey
+*/
+static WERROR dcesrv_winreg_OpenKey(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_OpenKey *r)
+{
+ struct dcesrv_handle *h, *newh;
+ struct registry_key *key;
+ WERROR result;
+
+ DCESRV_PULL_HANDLE_FAULT(h, r->in.parent_handle, HTYPE_REGKEY);
+ key = h->data;
+
+ switch (security_session_user_level(dce_call->conn->auth_state.session_info))
+ {
+ case SECURITY_SYSTEM:
+ case SECURITY_ADMINISTRATOR:
+ case SECURITY_USER:
+ if (r->in.keyname.name && strcmp(r->in.keyname.name, "") == 0) {
+ newh = talloc_reference(dce_call->context, h);
+ result = WERR_OK;
+ } else {
+ newh = dcesrv_handle_new(dce_call->context, HTYPE_REGKEY);
+ result = reg_open_key(newh, key, r->in.keyname.name,
+ (struct registry_key **)&newh->data);
+ }
+
+ if (W_ERROR_IS_OK(result)) {
+ r->out.handle = &newh->wire_handle;
+ } else {
+ talloc_free(newh);
+ }
+ return result;
+ default:
+ return WERR_ACCESS_DENIED;
+ }
+
+}
+
+
+/*
+ winreg_QueryInfoKey
+*/
+static WERROR dcesrv_winreg_QueryInfoKey(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_QueryInfoKey *r)
+{
+ struct dcesrv_handle *h;
+ struct registry_key *key;
+ const char *classname = NULL;
+ WERROR result;
+
+ DCESRV_PULL_HANDLE_FAULT(h, r->in.handle, HTYPE_REGKEY);
+ key = h->data;
+
+ switch (security_session_user_level(dce_call->conn->auth_state.session_info))
+ {
+ case SECURITY_SYSTEM:
+ case SECURITY_ADMINISTRATOR:
+ case SECURITY_USER:
+ result = reg_key_get_info(mem_ctx, key, &classname,
+ r->out.num_subkeys, r->out.num_values,
+ r->out.last_changed_time, r->out.max_subkeylen,
+ r->out.max_valnamelen, r->out.max_valbufsize);
+
+ if (classname != NULL) {
+ r->out.classname->name = classname;
+ r->out.classname->name_len = 2*strlen_m_term(classname);
+ } else {
+ r->out.classname->name = r->in.classname->name;
+ r->out.classname->name_len = r->in.classname->name_len;
+ }
+ r->out.classname->name_size = r->in.classname->name_size;
+
+ return result;
+ default:
+ return WERR_ACCESS_DENIED;
+ }
+}
+
+
+/*
+ winreg_QueryValue
+*/
+static WERROR dcesrv_winreg_QueryValue(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_QueryValue *r)
+{
+ struct dcesrv_handle *h;
+ struct registry_key *key;
+ uint32_t value_type;
+ DATA_BLOB value_data;
+ WERROR result;
+
+ DCESRV_PULL_HANDLE_FAULT(h, r->in.handle, HTYPE_REGKEY);
+ key = h->data;
+
+ switch (security_session_user_level(dce_call->conn->auth_state.session_info))
+ {
+ case SECURITY_SYSTEM:
+ case SECURITY_ADMINISTRATOR:
+ case SECURITY_USER:
+ result = reg_key_get_value_by_name(mem_ctx, key,
+ r->in.value_name->name, &value_type, &value_data);
+
+ if (!W_ERROR_IS_OK(result)) {
+ /* if the lookup wasn't successful, send client query back */
+ value_type = *r->in.type;
+ value_data.data = r->in.data;
+ value_data.length = *r->in.data_length;
+ }
+
+ r->out.type = talloc(mem_ctx, uint32_t);
+ if (!r->out.type) {
+ return WERR_NOMEM;
+ }
+ *r->out.type = value_type;
+ r->out.data_length = talloc(mem_ctx, uint32_t);
+ if (!r->out.data_length) {
+ return WERR_NOMEM;
+ }
+ *r->out.data_length = value_data.length;
+ r->out.data_size = talloc(mem_ctx, uint32_t);
+ if (!r->out.data_size) {
+ return WERR_NOMEM;
+ }
+ *r->out.data_size = value_data.length;
+ r->out.data = value_data.data;
+
+ return result;
+ default:
+ return WERR_ACCESS_DENIED;
+ }
+}
+
+
+/*
+ winreg_ReplaceKey
+*/
+static WERROR dcesrv_winreg_ReplaceKey(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_ReplaceKey *r)
+{
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/*
+ winreg_RestoreKey
+*/
+static WERROR dcesrv_winreg_RestoreKey(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_RestoreKey *r)
+{
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/*
+ winreg_SaveKey
+*/
+static WERROR dcesrv_winreg_SaveKey(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_SaveKey *r)
+{
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/*
+ winreg_SetKeySecurity
+*/
+static WERROR dcesrv_winreg_SetKeySecurity(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_SetKeySecurity *r)
+{
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/*
+ winreg_SetValue
+*/
+static WERROR dcesrv_winreg_SetValue(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_SetValue *r)
+{
+ struct dcesrv_handle *h;
+ struct registry_key *key;
+ DATA_BLOB data;
+ WERROR result;
+
+ DCESRV_PULL_HANDLE_FAULT(h, r->in.handle, HTYPE_REGKEY);
+ key = h->data;
+
+ switch (security_session_user_level(dce_call->conn->auth_state.session_info))
+ {
+ case SECURITY_SYSTEM:
+ case SECURITY_ADMINISTRATOR:
+ data.data = r->in.data;
+ data.length = r->in.size;
+ result = reg_val_set(key, r->in.name.name, r->in.type, data);
+ return result;
+ default:
+ return WERR_ACCESS_DENIED;
+ }
+}
+
+
+/*
+ winreg_UnLoadKey
+*/
+static WERROR dcesrv_winreg_UnLoadKey(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_UnLoadKey *r)
+{
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/*
+ winreg_InitiateSystemShutdown
+*/
+static WERROR dcesrv_winreg_InitiateSystemShutdown(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_InitiateSystemShutdown *r)
+{
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/*
+ winreg_AbortSystemShutdown
+*/
+static WERROR dcesrv_winreg_AbortSystemShutdown(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_AbortSystemShutdown *r)
+{
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/*
+ winreg_GetVersion
+*/
+static WERROR dcesrv_winreg_GetVersion(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_GetVersion *r)
+{
+ struct dcesrv_handle *h;
+
+ DCESRV_PULL_HANDLE_FAULT(h, r->in.handle, HTYPE_REGKEY);
+
+ r->out.version = talloc(mem_ctx, uint32_t);
+ W_ERROR_HAVE_NO_MEMORY(r->out.version);
+
+ *r->out.version = 5;
+
+ return WERR_OK;
+}
+
+
+/*
+ winreg_QueryMultipleValues
+*/
+static WERROR dcesrv_winreg_QueryMultipleValues(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_QueryMultipleValues *r)
+{
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/*
+ winreg_InitiateSystemShutdownEx
+*/
+static WERROR dcesrv_winreg_InitiateSystemShutdownEx(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_InitiateSystemShutdownEx *r)
+{
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/*
+ winreg_SaveKeyEx
+*/
+static WERROR dcesrv_winreg_SaveKeyEx(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_SaveKeyEx *r)
+{
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/*
+ winreg_QueryMultipleValues2
+*/
+static WERROR dcesrv_winreg_QueryMultipleValues2(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct winreg_QueryMultipleValues2 *r)
+{
+ return WERR_NOT_SUPPORTED;
+}
+
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_winreg_s.c"
diff --git a/source4/rpc_server/wkssvc/dcesrv_wkssvc.c b/source4/rpc_server/wkssvc/dcesrv_wkssvc.c
new file mode 100644
index 0000000000..e23485aea9
--- /dev/null
+++ b/source4/rpc_server/wkssvc/dcesrv_wkssvc.c
@@ -0,0 +1,418 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ endpoint server for the wkssvc pipe
+
+ Copyright (C) Stefan (metze) Metzmacher 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "rpc_server/dcerpc_server.h"
+#include "librpc/gen_ndr/ndr_wkssvc.h"
+#include "rpc_server/common/common.h"
+#include "param/param.h"
+
+/*
+ wkssvc_NetWkstaGetInfo
+*/
+static WERROR dcesrv_wkssvc_NetWkstaGetInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetWkstaGetInfo *r)
+{
+ struct dcesrv_context *dce_ctx = dce_call->conn->dce_ctx;
+ struct dcerpc_server_info *server_info = lp_dcerpc_server_info(mem_ctx, dce_ctx->lp_ctx);
+
+ ZERO_STRUCT(r->out);
+ r->out.info = talloc_zero(mem_ctx, union wkssvc_NetWkstaInfo);
+ W_ERROR_HAVE_NO_MEMORY(r->out.info);
+
+ /* NOTE: win2k3 ignores r->in.server_name completly so we do --metze */
+
+ switch(r->in.level) {
+ case 100:
+ {
+ struct wkssvc_NetWkstaInfo100 *info100;
+
+ info100 = talloc(mem_ctx, struct wkssvc_NetWkstaInfo100);
+ W_ERROR_HAVE_NO_MEMORY(info100);
+
+ info100->platform_id = dcesrv_common_get_platform_id(mem_ctx, dce_ctx);
+ info100->server_name = dcesrv_common_get_server_name(mem_ctx, dce_ctx, NULL);
+ W_ERROR_HAVE_NO_MEMORY(info100->server_name);
+ info100->domain_name = talloc_reference(mem_ctx, server_info->domain_name);
+ W_ERROR_HAVE_NO_MEMORY(info100->domain_name);
+ info100->version_major = server_info->version_major;
+ info100->version_minor = server_info->version_minor;
+
+ r->out.info->info100 = info100;
+ return WERR_OK;
+ }
+ case 101:
+ {
+ struct wkssvc_NetWkstaInfo101 *info101;
+
+ info101 = talloc(mem_ctx, struct wkssvc_NetWkstaInfo101);
+ W_ERROR_HAVE_NO_MEMORY(info101);
+
+ info101->platform_id = dcesrv_common_get_platform_id(mem_ctx, dce_ctx);
+ info101->server_name = dcesrv_common_get_server_name(mem_ctx, dce_ctx, NULL);
+ W_ERROR_HAVE_NO_MEMORY(info101->server_name);
+ info101->domain_name = talloc_reference(mem_ctx, server_info->domain_name);
+ W_ERROR_HAVE_NO_MEMORY(info101->domain_name);
+ info101->version_major = server_info->version_major;
+ info101->version_minor = server_info->version_minor;
+ info101->lan_root = dcesrv_common_get_lan_root(mem_ctx, dce_ctx);
+
+ r->out.info->info101 = info101;
+ return WERR_OK;
+ }
+ case 102:
+ {
+ return WERR_ACCESS_DENIED;
+ }
+ case 502:
+ {
+ return WERR_ACCESS_DENIED;
+ }
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ return WERR_UNKNOWN_LEVEL;
+}
+
+
+/*
+ wkssvc_NetWkstaSetInfo
+*/
+static WERROR dcesrv_wkssvc_NetWkstaSetInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetWkstaSetInfo *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetWkstaEnumUsers
+*/
+static WERROR dcesrv_wkssvc_NetWkstaEnumUsers(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetWkstaEnumUsers *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrWkstaUserGetInfo
+*/
+static WERROR dcesrv_wkssvc_NetrWkstaUserGetInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrWkstaUserGetInfo *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrWkstaUserSetInfo
+*/
+static WERROR dcesrv_wkssvc_NetrWkstaUserSetInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrWkstaUserSetInfo *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetWkstaTransportEnum
+*/
+static WERROR dcesrv_wkssvc_NetWkstaTransportEnum(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetWkstaTransportEnum *r)
+{
+ r->out.total_entries = 0;
+ r->out.resume_handle = NULL;
+
+ switch (r->in.info->level) {
+ case 0:
+ r->out.info = talloc(mem_ctx, struct wkssvc_NetWkstaTransportInfo);
+ W_ERROR_HAVE_NO_MEMORY(r->out.info);
+ r->out.info->level = r->in.info->level;
+ r->out.info->ctr.ctr0 = talloc(mem_ctx, struct wkssvc_NetWkstaTransportCtr0);
+ W_ERROR_HAVE_NO_MEMORY(r->out.info->ctr.ctr0);
+
+ r->out.info->ctr.ctr0->count = 0;
+ r->out.info->ctr.ctr0->array = NULL;
+
+ return WERR_NOT_SUPPORTED;
+
+ default:
+ return WERR_UNKNOWN_LEVEL;
+ }
+
+ return WERR_UNKNOWN_LEVEL;
+}
+
+
+/*
+ wkssvc_NetrWkstaTransportAdd
+*/
+static WERROR dcesrv_wkssvc_NetrWkstaTransportAdd(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrWkstaTransportAdd *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrWkstaTransportDel
+*/
+static WERROR dcesrv_wkssvc_NetrWkstaTransportDel(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrWkstaTransportDel *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrUseAdd
+*/
+static WERROR dcesrv_wkssvc_NetrUseAdd(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrUseAdd *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrUseGetInfo
+*/
+static WERROR dcesrv_wkssvc_NetrUseGetInfo(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrUseGetInfo *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrUseDel
+*/
+static WERROR dcesrv_wkssvc_NetrUseDel(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrUseDel *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrUseEnum
+*/
+static WERROR dcesrv_wkssvc_NetrUseEnum(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrUseEnum *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrMessageBufferSend
+*/
+static WERROR dcesrv_wkssvc_NetrMessageBufferSend(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrMessageBufferSend *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrWorkstationStatisticsGet
+*/
+static WERROR dcesrv_wkssvc_NetrWorkstationStatisticsGet(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrWorkstationStatisticsGet *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrLogonDomainNameAdd
+*/
+static WERROR dcesrv_wkssvc_NetrLogonDomainNameAdd(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrLogonDomainNameAdd *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrLogonDomainNameDel
+*/
+static WERROR dcesrv_wkssvc_NetrLogonDomainNameDel(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrLogonDomainNameDel *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrJoinDomain
+*/
+static WERROR dcesrv_wkssvc_NetrJoinDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrJoinDomain *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrUnjoinDomain
+*/
+static WERROR dcesrv_wkssvc_NetrUnjoinDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrUnjoinDomain *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrRenameMachineInDomain
+*/
+static WERROR dcesrv_wkssvc_NetrRenameMachineInDomain(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrRenameMachineInDomain *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrValidateName
+*/
+static WERROR dcesrv_wkssvc_NetrValidateName(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrValidateName *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrGetJoinInformation
+*/
+static WERROR dcesrv_wkssvc_NetrGetJoinInformation(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrGetJoinInformation *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrGetJoinableOus
+*/
+static WERROR dcesrv_wkssvc_NetrGetJoinableOus(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrGetJoinableOus *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ WKSSVC_NETRJOINDOMAIN2
+*/
+static WERROR dcesrv_wkssvc_NetrJoinDomain2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrJoinDomain2 *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ WKSSVC_NETRUNJOINDOMAIN2
+*/
+static WERROR dcesrv_wkssvc_NetrUnjoinDomain2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrUnjoinDomain2 *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrRenameMachineInDomain2
+*/
+static WERROR dcesrv_wkssvc_NetrRenameMachineInDomain2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrRenameMachineInDomain2 *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrValidateName2
+*/
+static WERROR dcesrv_wkssvc_NetrValidateName2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrValidateName2 *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrGetJoinableOus2
+*/
+static WERROR dcesrv_wkssvc_NetrGetJoinableOus2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrGetJoinableOus2 *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrAddAlternateComputername
+*/
+static WERROR dcesrv_wkssvc_NetrAddAlternateComputerName(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrAddAlternateComputerName *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrRemoveAlternateComputername
+*/
+static WERROR dcesrv_wkssvc_NetrRemoveAlternateComputerName(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrRemoveAlternateComputerName *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrSetPrimaryComputername
+*/
+static WERROR dcesrv_wkssvc_NetrSetPrimaryComputername(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrSetPrimaryComputername *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/*
+ wkssvc_NetrEnumerateComputerNames
+*/
+static WERROR dcesrv_wkssvc_NetrEnumerateComputerNames(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
+ struct wkssvc_NetrEnumerateComputerNames *r)
+{
+ DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+}
+
+
+/* include the generated boilerplate */
+#include "librpc/gen_ndr/ndr_wkssvc_s.c"
diff --git a/source4/script/buildtree.pl b/source4/script/buildtree.pl
new file mode 100755
index 0000000000..a40036aa55
--- /dev/null
+++ b/source4/script/buildtree.pl
@@ -0,0 +1,40 @@
+#! /usr/bin/env perl -w
+ eval 'exec /usr/bin/env perl -S $0 ${1+"$@"}'
+ if 0; #$running_under_some_shell
+
+use strict;
+use File::Find ();
+use File::Path qw(mkpath);
+use Cwd 'abs_path';
+
+# Set the variable $File::Find::dont_use_nlink if you're using AFS,
+# since AFS cheats.
+
+# for the convenience of &wanted calls, including -eval statements:
+use vars qw/*name *dir *prune/;
+*name = *File::Find::name;
+*dir = *File::Find::dir;
+*prune = *File::Find::prune;
+my $builddir = abs_path($ENV{builddir});
+my $srcdir = abs_path($ENV{srcdir});
+sub wanted;
+
+
+
+# Traverse desired filesystems
+File::Find::find({wanted => \&wanted, no_chdir => 1}, $srcdir);
+exit;
+
+
+sub wanted {
+ my ($dev,$ino,$mode,$nlink,$uid,$gid,$newdir);
+
+ if ((($dev,$ino,$mode,$nlink,$uid,$gid) = lstat($_)) &&
+ (-d _) && (($newdir = abs_path($_)) !~ /$builddir/))
+ {
+ $newdir =~ s!$srcdir!$builddir!;
+ mkpath($newdir);
+ print("Creating $newdir\n");
+ }
+}
+
diff --git a/source4/script/configure_check_unused.pl b/source4/script/configure_check_unused.pl
new file mode 100755
index 0000000000..52d8deeb27
--- /dev/null
+++ b/source4/script/configure_check_unused.pl
@@ -0,0 +1,124 @@
+#!/usr/bin/perl
+# Script that finds macros in a configure script that are not
+# used in a set of C files.
+# Copyright Jelmer Vernooij <jelmer@samba.org>, GPL
+#
+# Usage: ./$ARGV[0] configure.in [c-files...]
+
+use strict;
+
+sub autoconf_parse($$$$)
+{
+ my $in = shift;
+ my $defines = shift;
+ my $functions = shift;
+ my $headers = shift;
+
+ open(IN, $in) or die("Can't open $in");
+
+ my $ln = 0;
+
+ foreach(<IN>) {
+ $ln++;
+
+ if(/AC_DEFINE\(([^,]+),/) {
+ $defines->{$1} = "$in:$ln";
+ }
+
+ if(/AC_CHECK_FUNCS\(\[*(.[^],)]+)/) {
+ foreach(split / /, $1) {
+ $functions->{$_} = "$in:$ln";
+ }
+ }
+
+ if(/AC_CHECK_FUNC\(([^,)]+)/) {
+ $functions->{$1} = "$in:$ln";
+ }
+
+ if(/AC_CHECK_HEADERS\(\[*([^],)]+)/) {
+ foreach(split / /, $1) {
+ $headers->{$_} = "$in:$ln";
+ }
+ }
+
+ if(/AC_CHECK_HEADER\(([^,)]+)/) {
+ $headers->{$1} = "$in:$ln";
+ }
+
+ if(/sinclude\(([^,]+)\)/) {
+ autoconf_parse($1, $defines, $functions, $headers);
+ }
+ }
+
+ close IN;
+}
+
+# Return the symbols and headers used by a C file
+sub cfile_parse($$$)
+{
+ my $in = shift;
+ my $symbols = shift;
+ my $headers = shift;
+
+ open(FI, $in) or die("Can't open $in");
+ my $ln = 0;
+ my $line;
+ while($line = <FI>) {
+ $ln++;
+ $_ = $line;
+ if (/\#([ \t]*)include ["<]([^">]+)/) {
+ $headers->{$2} = "$in:$ln";
+ }
+
+ $_ = $line;
+ while(/([A-Za-z0-9_]+)/g) {
+ $symbols->{$1} = "$in:$ln";
+ }
+ }
+ close FI;
+}
+
+my %ac_defines = ();
+my %ac_func_checks = ();
+my %ac_headers = ();
+my %symbols = ();
+my %headers = ();
+
+if (scalar(@ARGV) <= 1) {
+ print("Usage: configure_find_unused.pl configure.in [CFILE...]\n");
+ exit 0;
+}
+
+autoconf_parse(shift(@ARGV), \%ac_defines, \%ac_func_checks, \%ac_headers);
+cfile_parse($_, \%symbols, \%headers) foreach(@ARGV);
+
+(keys %ac_defines) or warn("No defines found in configure.in file, parse error?");
+
+foreach (keys %ac_defines) {
+ if (not defined($symbols{$_})) {
+ print "$ac_defines{$_}: Autoconf-defined $_ is unused\n";
+ }
+}
+
+(keys %ac_func_checks) or warn("No function checks found in configure.in file, parse error?");
+
+foreach (keys %ac_func_checks) {
+ my $def = "HAVE_".uc($_);
+ if (not defined($symbols{$_})) {
+ print "$ac_func_checks{$_}: Autoconf-checked function `$_' is unused\n";
+ } elsif (not defined($symbols{$def})) {
+ print "$ac_func_checks{$_}: Autoconf-define `$def' for function `$_' is unused\n";
+ }
+}
+
+(keys %ac_headers) or warn("No headers found in configure.in file, parse error?");
+
+foreach (keys %ac_headers) {
+ my $def = "HAVE_".uc($_);
+ $def =~ s/[\/\.]/_/g;
+ if (not defined($headers{$_})) {
+ print "$ac_headers{$_}: Autoconf-checked header `$_' is unused\n";
+ } elsif (not defined($symbols{$def})) {
+ print "$ac_headers{$_}: Autoconf-define `$def' for header `$_' is unused\n";
+ }
+}
diff --git a/source4/script/depfilter.py b/source4/script/depfilter.py
new file mode 100755
index 0000000000..74d1d179c7
--- /dev/null
+++ b/source4/script/depfilter.py
@@ -0,0 +1,50 @@
+#!/usr/bin/python
+#
+# Filter out arcs in a dotty graph that are at or below a certain
+# node. This is useful for visualising parts of the dependency graph.
+#
+
+# Command line stuff
+
+import sys, sre
+
+if len(sys.argv) != 2:
+ print 'Usage: depfilter.py NODE'
+ sys.exit(1)
+
+top = sys.argv[1]
+
+# Read in dot file
+
+lines = sys.stdin.readlines()
+
+graph = {}
+
+for arc in lines[1:-1]:
+ match = sre.search('"(.*)" -> "(.*)"', arc)
+ n1, n2 = match.group(1), match.group(2)
+ if not graph.has_key(n1):
+ graph[n1] = []
+ graph[n1].append(n2)
+
+# Create subset of 'graph' rooted at 'top'
+
+subgraph = {}
+
+def add_deps(node):
+ if graph.has_key(node) and not subgraph.has_key(node):
+ subgraph[node] = graph[node]
+ for n in graph[node]:
+ add_deps(n)
+
+add_deps(top)
+
+# Generate output
+
+print lines[0],
+
+for key, value in subgraph.items():
+ for n in value:
+ print '\t"%s" -> "%s"' % (key, n)
+
+print lines[-1],
diff --git a/source4/script/extract_allparms.sh b/source4/script/extract_allparms.sh
new file mode 100755
index 0000000000..f16068b3fd
--- /dev/null
+++ b/source4/script/extract_allparms.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+grep '{".*P_[GL]' param/loadparm.c | sed -e 's/&.*$//g' -e 's/",.*P_LOCAL.*$/ S/' -e 's/",.*P_GLOBAL.*$/ G/' -e 's/^ .*{"//g' | sort -f
diff --git a/source4/script/find_unused_macros.pl b/source4/script/find_unused_macros.pl
new file mode 100755
index 0000000000..8886835fa3
--- /dev/null
+++ b/source4/script/find_unused_macros.pl
@@ -0,0 +1,38 @@
+#!/usr/bin/perl
+# Script that reads in C files and prints defines that are used nowhere in the
+# code
+
+# Arguments: C and H files
+# Copyright Jelmer Vernooij <jelmer@samba.org>, GPL
+
+use strict;
+
+my %defined;
+my %used;
+my %files;
+
+my $tmp;
+while($tmp = shift) {
+ $files{$tmp} = $tmp;
+ open(FI, $tmp);
+ my $ln = 0;
+ while(<FI>) {
+ $ln++;
+ my $line = $_;
+ my $cur = "";
+ if(/^#define ([A-Za-z0-9_]+)/) {
+ $defined{$1} = "$tmp:$ln";
+ $cur = $1;
+ }
+
+ $_ = $line;
+ while(/([A-Za-z0-9_]+)/sgm) {
+ if($cur ne $1) { $used{$1} = "$tmp:$ln"; }
+ }
+ }
+ close FI;
+}
+
+foreach(keys %defined) {
+ if(!$used{$_}) { print "$defined{$_}: Macro `$_' is unused\n"; }
+}
diff --git a/source4/script/find_unused_makefilevars.pl b/source4/script/find_unused_makefilevars.pl
new file mode 100755
index 0000000000..23fc36ef6a
--- /dev/null
+++ b/source4/script/find_unused_makefilevars.pl
@@ -0,0 +1,55 @@
+#!/usr/bin/perl
+# Script that reads in Makefile.in and outputs the names of all
+# used but undefined vars and all defined but unused vars
+# Copyright Jelmer Vernooij <jelmer@samba.org>
+
+# Arguments:
+# 1: Makefile.in
+#
+
+my %references;
+my %defines;
+
+# First, make a list of defines in configure
+$in = shift;
+
+sub process_file($)
+{
+ my ($fn) = @_;
+ open(IN, $fn);
+ while(<IN>) {
+ my $line = $_;
+ while($line =~ /^\b([a-zA-Z0-9_][a-zA-Z0-9_]*)\b[ \t]*=.*/sgm) {
+ $defines{$1} = 1;
+ }
+ while($line =~ /\$\(([a-zA-Z0-9_][a-zA-Z0-9_]*)\)/sgm) {
+ $references{$1} = 1;
+ }
+ while ($line =~ /^include (.*)/sgm) {
+ process_file($1);
+ }
+ }
+ close IN;
+}
+
+process_file($in);
+
+print "##### DEFINED BUT UNUSED: #####\n";
+foreach(%defines) {
+# print $_." defined\n";
+
+ if ($_ != 1) {
+ if ($references{$_} != 1) {
+ print $_."\n";
+ }
+ }
+}
+
+print "##### USED BUT UNDEFINED: #####\n";
+foreach(%references) {
+ if ($_ != 1) {
+ if ($defines{$_} != 1) {
+ print $_."\n";
+ }
+ }
+}
diff --git a/source4/script/find_unused_options.sh b/source4/script/find_unused_options.sh
new file mode 100755
index 0000000000..d554580959
--- /dev/null
+++ b/source4/script/find_unused_options.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+#
+# this script finds unused lp_*() functions
+#
+# use it like this:
+#
+# user@host:~/samba/source>./script/find_unused_options.sh
+#
+
+LIST_GLOBAL=`grep '^FN_GLOBAL' param/loadparm.c |sed -e's/^FN_GLOBAL.*(\(.*\).*,.*\(&Globals\..*\)).*/\1:\2/'`
+
+LIST_LOCAL=`grep '^FN_LOCAL' param/loadparm.c |sed -e's/^FN_LOCAL.*(\(.*\).*,[ ]*\(.*\)).*/\1:\2/'`
+
+CFILES=`find . -name "*.c"`
+
+for i in $LIST_GLOBAL;do
+ key=`echo $i|cut -d ':' -f1`
+ val=`echo $i|cut -d ':' -f2`
+
+ found=`grep "$key[ ]*()" $CFILES`
+ if test -z "$found"; then
+ echo "Not Used Global: $key() -> $val"
+ fi
+done
+
+for i in $LIST_LOCAL;do
+ key=`echo $i|cut -d ':' -f1`
+ val=`echo $i|cut -d ':' -f2`
+
+ found=`grep "$key[ ]*(" $CFILES`
+
+ if test -z "$found"; then
+ echo "Not Used LOCAL: $key() -> $val"
+ fi
+done
+
+echo "# do a 'make clean;make everything' before removing anything!"
diff --git a/source4/script/findstatic.pl b/source4/script/findstatic.pl
new file mode 100755
index 0000000000..43a4916435
--- /dev/null
+++ b/source4/script/findstatic.pl
@@ -0,0 +1,70 @@
+#!/usr/bin/perl -w
+# find a list of fns and variables in the code that could be static
+# usually called with something like this:
+# findstatic.pl `find . -name "*.o"`
+# Andrew Tridgell <tridge@samba.org>
+
+use strict;
+
+# use nm to find the symbols
+my($saved_delim) = $/;
+undef $/;
+my($syms) = `nm -o @ARGV`;
+$/ = $saved_delim;
+
+my(@lines) = split(/\n/s, $syms);
+
+my(%def);
+my(%undef);
+my(%stype);
+
+my(%typemap) = (
+ "T" => "function",
+ "C" => "uninitialised variable",
+ "D" => "initialised variable"
+ );
+
+
+# parse the symbols into defined and undefined
+for (my($i)=0; $i <= $#{@lines}; $i++) {
+ my($line) = $lines[$i];
+ if ($line =~ /(.*):[a-f0-9]* ([TCD]) (.*)/) {
+ my($fname) = $1;
+ my($symbol) = $3;
+ push(@{$def{$fname}}, $symbol);
+ $stype{$symbol} = $2;
+ }
+ if ($line =~ /(.*):\s* U (.*)/) {
+ my($fname) = $1;
+ my($symbol) = $2;
+ push(@{$undef{$fname}}, $symbol);
+ }
+}
+
+# look for defined symbols that are never referenced outside the place they
+# are defined
+foreach my $f (keys %def) {
+ print "Checking $f\n";
+ my($found_one) = 0;
+ foreach my $s (@{$def{$f}}) {
+ my($found) = 0;
+ foreach my $f2 (keys %undef) {
+ if ($f2 ne $f) {
+ foreach my $s2 (@{$undef{$f2}}) {
+ if ($s2 eq $s) {
+ $found = 1;
+ $found_one = 1;
+ }
+ }
+ }
+ }
+ if ($found == 0) {
+ my($t) = $typemap{$stype{$s}};
+ print " '$s' is unique to $f ($t)\n";
+ }
+ }
+ if ($found_one == 0) {
+ print " all symbols in '$f' are unused (main program?)\n";
+ }
+}
+
diff --git a/source4/script/installdat.sh b/source4/script/installdat.sh
new file mode 100755
index 0000000000..bea8ad891a
--- /dev/null
+++ b/source4/script/installdat.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+#fist version March 2002, Herb Lewis
+
+DATDIR=$1
+SRCDIR=$2/
+
+echo Installing dat files in $DATDIR
+
+for f in $SRCDIR/../codepages/*.dat; do
+ FNAME=$DATDIR/`basename $f`
+ echo $FNAME
+ cp $f $FNAME || echo Cannot install $FNAME. Does $USER have privileges?
+ chmod 0644 $FNAME
+done
+
+cat << EOF
+======================================================================
+The dat files have been installed.
+======================================================================
+EOF
+
+exit 0
+
diff --git a/source4/script/installdirs.sh b/source4/script/installdirs.sh
new file mode 100755
index 0000000000..9557b86d3b
--- /dev/null
+++ b/source4/script/installdirs.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+while ( test -n "$1" ); do
+ if [ ! -d $1 ]; then
+ mkdir -p $1
+ fi
+
+ if [ ! -d $1 ]; then
+ echo Failed to make directory $1
+ exit 1
+ fi
+
+ shift;
+done
+
+
+
diff --git a/source4/script/installheader.pl b/source4/script/installheader.pl
new file mode 100755
index 0000000000..5be3434a5c
--- /dev/null
+++ b/source4/script/installheader.pl
@@ -0,0 +1,109 @@
+#!/usr/bin/perl
+# Copyright (C) 2006 Jelmer Vernooij
+use strict;
+use File::Basename;
+
+my $includedir = shift;
+
+
+sub read_headermap($)
+{
+ my ($fn) = @_;
+ my %map = ();
+ my $ln = 0;
+ open(MAP, "<headermap.txt");
+ while(<MAP>) {
+ $ln++;
+ s/#.*$//g;
+ next if (/^\s*$/);
+ if (! /^(.*): (.*)$/) {
+ print STDERR "headermap.txt:$ln: Malformed line\n";
+ next;
+ }
+ $map{$1} = $2;
+ }
+
+ close(MAP);
+
+ return %map;
+}
+
+my %map = read_headermap("headermap.txt");
+
+sub findmap($)
+{
+ $_ = shift;
+ s/^\.\///g;
+
+ if (! -f $_ && -f "lib/$_") { $_ = "lib/$_"; }
+
+ return $map{$_};
+}
+
+sub rewrite_include($$)
+{
+ my ($pos,$d) = @_;
+
+ my $n = findmap($d);
+ return $n if $n;
+ return $d;
+}
+
+sub install_header($$)
+{
+ my ($src,$dst) = @_;
+
+ my $lineno = 0;
+
+ open(IN, "<$src");
+ open(OUT, ">$dst");
+
+ while (<IN>) {
+ $lineno++;
+ die("Will not install autogenerated header $src") if (/This file was automatically generated by mkproto.pl. DO NOT EDIT/);
+
+ if (/^#include \"(.*)\"/) {
+ print OUT "#include <" . rewrite_include("$src:$lineno", $1) . ">\n";
+ } elsif (/^#if _SAMBA_BUILD_ == 4/) {
+ print OUT "#if 1\n";
+ } else {
+ print OUT $_;
+ }
+ }
+
+ close(OUT);
+ close(IN);
+}
+
+foreach my $p (@ARGV)
+{
+ my $p2 = findmap($p);
+ unless ($p2) {
+ die("Unable to map $p");
+ }
+ print "Installing $p as $includedir/$p2\n";
+
+ my $dirname = dirname($p2);
+
+ if (! -d "$includedir/$dirname") {
+ mkdir("$includedir/$dirname", 0777);
+ }
+
+ if ( -f "$includedir/$p2" ) {
+ unlink("$includedir/$p2.old");
+ rename("$includedir/$p2", "$includedir/$p2.old");
+ }
+
+ install_header($p,"$includedir/$p2");
+}
+
+print <<EOF;
+======================================================================
+The headers are installed. You may restore the old headers (if there
+were any) using the command "make revert". You may uninstall the headers
+using the command "make uninstallheader" or "make uninstall" to uninstall
+binaries, man pages and shell scripts.
+======================================================================
+EOF
+
+exit 0;
diff --git a/source4/script/installlib.sh b/source4/script/installlib.sh
new file mode 100755
index 0000000000..cc9ff0b9ea
--- /dev/null
+++ b/source4/script/installlib.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+LIBDIR=$1
+SHLIBEXT=$2
+
+shift
+shift
+
+for p in $*; do
+ p2=`basename $p`
+ lnname=`echo $p2 | sed -e "s/\.$SHLIBEXT.*/.$SHLIBEXT/"`
+ echo Installing $p as $LIBDIR/$p2
+ if [ -f $LIBDIR/$p2 ]; then
+ rm -f $LIBDIR/$p2.old
+ mv $LIBDIR/$p2 $LIBDIR/$p2.old
+ fi
+ cp $p $LIBDIR/
+ if [ $p2 != $lnname ]; then
+ ln -sf $p2 $LIBDIR/$lnname
+ fi
+done
+
+cat << EOF
+======================================================================
+The shared libraries are installed. You may restore the old libraries (if there
+were any) using the command "make revert". You may uninstall the libraries
+using the command "make uninstalllib" or "make uninstall" to uninstall
+binaries, man pages and shell scripts.
+======================================================================
+EOF
+
+exit 0
diff --git a/source4/script/installman.sh b/source4/script/installman.sh
new file mode 100755
index 0000000000..a3b6ec0d93
--- /dev/null
+++ b/source4/script/installman.sh
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+MANDIR=$1
+shift 1
+MANPAGES=$*
+
+for I in $MANPAGES
+do
+ SECTION=`echo -n $I | sed "s/.*\(.\)$/\1/"
+ DIR="$MANDIR/man$SECTION"
+ if [ ! -d "$DIR" ]
+ then
+ mkdir "$DIR"
+ fi
+
+ BASE=`basename $I`
+
+ echo "Installing manpage \"$BASE\" in $DIR"
+ cp $I $DIR
+done
+
+cat << EOF
+======================================================================
+The man pages have been installed. You may uninstall them using the command
+the command "make uninstallman" or make "uninstall" to uninstall binaries,
+man pages and shell scripts.
+======================================================================
+EOF
+
+exit 0
diff --git a/source4/script/installmisc.sh b/source4/script/installmisc.sh
new file mode 100755
index 0000000000..2bd34b119f
--- /dev/null
+++ b/source4/script/installmisc.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+# install miscellaneous files
+
+SRCDIR="$1"
+SETUPDIR="$2"
+
+cd $SRCDIR || exit 1
+
+echo "Installing setup templates"
+mkdir -p $SETUPDIR || exit 1
+cp setup/schema-map-* $SETUPDIR || exit 1
+cp setup/DB_CONFIG $SETUPDIR || exit 1
+cp setup/provision-backend $SETUPDIR || exit 1
+cp setup/provision $SETUPDIR || exit 1
+cp setup/newuser $SETUPDIR || exit 1
+cp setup/*.inf $SETUPDIR || exit 1
+cp setup/*.ldif $SETUPDIR || exit 1
+cp setup/*.reg $SETUPDIR || exit 1
+cp setup/*.zone $SETUPDIR || exit 1
+cp setup/*.conf $SETUPDIR || exit 1
+cp setup/*.php $SETUPDIR || exit 1
+cp setup/*.txt $SETUPDIR || exit 1
+cp setup/provision.smb.conf.dc $SETUPDIR || exit 1
+cp setup/provision.smb.conf.member $SETUPDIR || exit 1
+cp setup/provision.smb.conf.standalone $SETUPDIR || exit 1
+
+exit 0
diff --git a/source4/script/installpc.sh b/source4/script/installpc.sh
new file mode 100755
index 0000000000..81ca2f8145
--- /dev/null
+++ b/source4/script/installpc.sh
@@ -0,0 +1,16 @@
+#!/bin/sh
+# install miscellaneous files
+
+SRCDIR="$1"
+PKGCONFIGDIR="$2"
+shift
+shift
+
+cd $SRCDIR || exit 1
+
+for I in $*
+do
+ cp $I $PKGCONFIGDIR
+done
+
+exit 0
diff --git a/source4/script/installswat.sh b/source4/script/installswat.sh
new file mode 100755
index 0000000000..4304e3e490
--- /dev/null
+++ b/source4/script/installswat.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+SWATDIR=$1
+SRCDIR=$2
+
+echo Installing swat files in $SWATDIR
+
+cd $SRCDIR/../swat || exit 1
+
+mkdir -p $SWATDIR || exit 1
+
+installdir() {
+ for f in $*; do
+ dname=`dirname $f`
+ echo "Installing $f in $dname"
+ test -d $SWATDIR/$dname || mkdir -p $SWATDIR/$dname || exit 1
+ cp $f $SWATDIR/$dname/ || exit 1
+ chmod 0644 $SWATDIR/$f || exit 1
+ done
+}
+
+installdir `find . -name '*.js'`
+installdir `find . -name '*.esp'`
+installdir `find . -name '*.css'`
+installdir `find . -name '*.png'`
+installdir `find . -name '*.ico'`
+installdir `find . -name '*.gif'`
+installdir `find . -name '*.ejs'`
+
+cat << EOF
+======================================================================
+The swat files have been installed.
+======================================================================
+EOF
+
+exit 0
+
diff --git a/source4/script/minimal_includes.pl b/source4/script/minimal_includes.pl
new file mode 100755
index 0000000000..963dc10f8b
--- /dev/null
+++ b/source4/script/minimal_includes.pl
@@ -0,0 +1,150 @@
+#!/usr/bin/perl -w
+# find a list of #include lines in C code that might not be needed
+# usually called with something like this:
+# minimal_includes.pl `find . -name "*.c"`
+# Andrew Tridgell <tridge@samba.org>
+
+use strict;
+use Data::Dumper;
+use Getopt::Long;
+
+my $opt_help = 0;
+my $opt_remove = 0;
+
+#####################################################################
+# write a string into a file
+sub FileSave($$)
+{
+ my($filename) = shift;
+ my($v) = shift;
+ local(*FILE);
+ open(FILE, ">$filename") || die "can't open $filename";
+ print FILE $v;
+ close(FILE);
+}
+
+sub load_lines($)
+{
+ my $fname = shift;
+ my @lines = split(/^/m, `cat $fname`);
+ return @lines;
+}
+
+sub save_lines($$)
+{
+ my $fname = shift;
+ my $lines = shift;
+ my $data = join('', @{$lines});
+ FileSave($fname, $data);
+}
+
+sub test_compile($)
+{
+ my $fname = shift;
+ my $obj;
+ if ($fname =~ s/(.*)\.c$/$1.o/) {
+ $obj = "$1.o";
+ } else {
+ return "NOT A C FILE";
+ }
+ unlink($obj);
+ my $ret = `make $obj 2>&1`;
+ if (!unlink("$obj")) {
+ return "COMPILE FAILED";
+ }
+ return $ret;
+}
+
+sub test_include($$$$)
+{
+ my $fname = shift;
+ my $lines = shift;
+ my $i = shift;
+ my $original = shift;
+ my $line = $lines->[$i];
+
+ $lines->[$i] = "";
+ save_lines("_testcompile.c", $lines);
+
+ my $out = test_compile("_testcompile.c");
+ $out =~ s/_testcompile.c/$fname/g;
+
+ if ($out eq $original) {
+ if ($opt_remove) {
+ print "$fname: removing $line\n";
+ save_lines($fname, $lines);
+ return;
+ }
+ print "$fname: might be able to remove $line\n";
+ }
+
+ $lines->[$i] = $line;
+ unlink("_testcompile.c");
+}
+
+sub process_file($)
+{
+ my $fname = shift;
+ my @lines = load_lines($fname);
+ my $num_lines = $#lines;
+
+ my $original = test_compile($fname);
+
+ if ($original eq "COMPILE FAILED") {
+ print "Failed to compile $fname\n";
+ return;
+ }
+
+ print "Processing $fname (with $num_lines lines)\n";
+
+ my $if_level = 0;
+
+ for (my $i=0;$i<=$num_lines;$i++) {
+ my $line = $lines[$i];
+ if ($line =~ /^\#\s*if/) {
+ $if_level++;
+ }
+ if ($line =~ /^\#\s*endif/) {
+ $if_level--;
+ }
+ if ($if_level == 0 &&
+ $line =~ /^\#\s*include/ &&
+ !($line =~ /needed/)) {
+ test_include($fname, \@lines, $i, $original);
+ }
+ }
+}
+
+
+#########################################
+# display help text
+sub ShowHelp()
+{
+ print "
+ minimise includes
+ Copyright (C) tridge\@samba.org
+
+ Usage: minimal_includes.pl [options] <C files....>
+
+ Options:
+ --help show help
+ --remove remove includes, don't just list them
+";
+}
+
+
+# main program
+GetOptions (
+ 'h|help|?' => \$opt_help,
+ 'remove' => \$opt_remove,
+ );
+
+if ($opt_help) {
+ ShowHelp();
+ exit(0);
+}
+
+for (my $i=0;$i<=$#ARGV;$i++) {
+ my $fname = $ARGV[$i];
+ process_file($fname);
+}
diff --git a/source4/script/mkinstalldirs b/source4/script/mkinstalldirs
new file mode 100755
index 0000000000..f945dbf2bc
--- /dev/null
+++ b/source4/script/mkinstalldirs
@@ -0,0 +1,38 @@
+#! /bin/sh
+# mkinstalldirs --- make directory hierarchy
+# Author: Noah Friedman <friedman@prep.ai.mit.edu>
+# Created: 1993-05-16
+# Public domain
+
+errstatus=0
+
+for file
+do
+ set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'`
+ shift
+
+ pathcomp=
+ for d
+ do
+ pathcomp="$pathcomp$d"
+ case "$pathcomp" in
+ -* ) pathcomp=./$pathcomp ;;
+ esac
+
+ if test ! -d "$pathcomp"; then
+ echo "mkdir $pathcomp" 1>&2
+
+ mkdir "$pathcomp" || lasterr=$?
+
+ if test ! -d "$pathcomp"; then
+ errstatus=$lasterr
+ fi
+ fi
+
+ pathcomp="$pathcomp/"
+ done
+done
+
+exit $errstatus
+
+# mkinstalldirs ends here
diff --git a/source4/script/mkproto.pl b/source4/script/mkproto.pl
new file mode 100755
index 0000000000..e1b790d41d
--- /dev/null
+++ b/source4/script/mkproto.pl
@@ -0,0 +1,281 @@
+#!/usr/bin/perl
+# Simple script for generating prototypes for C functions
+# Written by Jelmer Vernooij
+# based on the original mkproto.sh by Andrew Tridgell
+
+use strict;
+
+# don't use warnings module as it is not portable enough
+# use warnings;
+
+use Getopt::Long;
+use File::Basename;
+use File::Path;
+
+#####################################################################
+# read a file into a string
+
+my $public_file = undef;
+my $private_file = undef;
+my $all_file = undef;
+my $public_define = undef;
+my $private_define = undef;
+my $_public = "";
+my $_private = "";
+my $public_data = \$_public;
+my $private_data = \$_private;
+my $builddir = ".";
+my $srcdir = ".";
+
+sub public($)
+{
+ my ($d) = @_;
+ $$public_data .= $d;
+}
+
+sub private($)
+{
+ my ($d) = @_;
+ $$private_data .= $d;
+}
+
+sub usage()
+{
+ print "Usage: mkproto.pl [options] [c files]\n";
+ print "OPTIONS:\n";
+ print " --public=FILE Write prototypes for public functions to FILE\n";
+ print " --private=FILE Write prototypes for private functions to FILE\n";
+ print " --define=DEF Use DEF to check whether header was already included\n";
+ print " --public-define=DEF Same as --define, but just for public header\n";
+ print " --private-define=DEF Same as --define, but just for private header\n";
+ print " --srcdir=path Read files relative to this directory\n";
+ print " --builddir=path Write file relative to this directory\n";
+ print " --help Print this help message\n\n";
+ exit 0;
+}
+
+GetOptions(
+ 'public=s' => sub { my ($f,$v) = @_; $public_file = $v; },
+ 'all=s' => sub { my ($f,$v) = @_; $public_file = $v; $private_file = $v; },
+ 'private=s' => sub { my ($f,$v) = @_; $private_file = $v; },
+ 'define=s' => sub {
+ my ($f,$v) = @_;
+ $public_define = $v;
+ $private_define = "$v\_PRIVATE";
+ },
+ 'public-define=s' => \$public_define,
+ 'private-define=s' => \$private_define,
+ 'srcdir=s' => sub { my ($f,$v) = @_; $srcdir = $v; },
+ 'builddir=s' => sub { my ($f,$v) = @_; $builddir = $v; },
+ 'help' => \&usage
+) or exit(1);
+
+sub normalize_define($$)
+{
+ my ($define, $file) = @_;
+
+ if (not defined($define) and defined($file)) {
+ $define = "__" . uc($file) . "__";
+ $define =~ tr{./}{__};
+ $define =~ tr{\-}{_};
+ } elsif (not defined($define)) {
+ $define = '_PROTO_H_';
+ }
+
+ return $define;
+}
+
+$public_define = normalize_define($public_define, $public_file);
+$private_define = normalize_define($private_define, $private_file);
+
+if ((defined($private_file) and defined($public_file) and ($private_file eq $public_file)) or
+ (not defined($private_file) and not defined($public_file))) {
+ $private_data = $public_data;
+}
+
+sub file_load($)
+{
+ my($filename) = @_;
+ local(*INPUTFILE);
+ open(INPUTFILE, $filename) or return undef;
+ my($saved_delim) = $/;
+ undef $/;
+ my($data) = <INPUTFILE>;
+ close(INPUTFILE);
+ $/ = $saved_delim;
+ return $data;
+}
+
+sub print_header($$)
+{
+ my ($file, $header_name) = @_;
+ $file->("#ifndef $header_name\n");
+ $file->("#define $header_name\n\n");
+ $file->("#undef _PRINTF_ATTRIBUTE\n");
+ $file->("#define _PRINTF_ATTRIBUTE(a1, a2) PRINTF_ATTRIBUTE(a1, a2)\n");
+ $file->("/* This file was automatically generated by mkproto.pl. DO NOT EDIT */\n\n");
+}
+
+sub print_footer($$)
+{
+ my ($file, $header_name) = @_;
+ $file->("#undef _PRINTF_ATTRIBUTE\n");
+ $file->("#define _PRINTF_ATTRIBUTE(a1, a2)\n");
+ $file->("\n#endif /* $header_name */\n\n");
+}
+
+sub handle_loadparm($$)
+{
+ my ($file,$line) = @_;
+
+ if ($line =~ /^_PUBLIC_ FN_(GLOBAL|LOCAL)_(CONST_STRING|STRING|BOOL|bool|CHAR|INTEGER|LIST)\((\w+),.*\)/o) {
+ my $scope = $1;
+ my $type = $2;
+ my $name = $3;
+
+ my %tmap = (
+ "BOOL" => "bool ",
+ "CONST_STRING" => "const char *",
+ "STRING" => "const char *",
+ "INTEGER" => "int ",
+ "CHAR" => "char ",
+ "LIST" => "const char **",
+ );
+
+ my %smap = (
+ "GLOBAL" => "struct loadparm_context *",
+ "LOCAL" => "struct loadparm_service *, struct loadparm_service *"
+ );
+
+ $file->("$tmap{$type}$name($smap{$scope});\n");
+ }
+}
+
+sub process_file($$$)
+{
+ my ($public_file, $private_file, $filename) = @_;
+
+ $filename =~ s/\.o$/\.c/g;
+
+ if ($filename =~ /^\//) {
+ open(FH, "<$filename") or die("Failed to open $filename");
+ } elsif (!open(FH, "< $builddir/$filename")) {
+ open(FH, "< $srcdir/$filename") || die "Failed to open $filename";
+ }
+
+ $private_file->("\n/* The following definitions come from $filename */\n\n");
+
+ my $comment = undef;
+ my $incomment = 0;
+ while (my $line = <FH>) {
+ my $target = \&private;
+ my $is_public = 0;
+
+ if ($line =~ /^\/\*\*/) {
+ $comment = "";
+ $incomment = 1;
+ }
+
+ if ($incomment) {
+ $comment .= $line;
+ if ($line =~ /\*\//) {
+ $incomment = 0;
+ }
+ }
+
+ # these are ordered for maximum speed
+ next if ($line =~ /^\s/);
+
+ next unless ($line =~ /\(/);
+
+ next if ($line =~ /^\/|[;]/);
+
+ if ($line =~ /^_PUBLIC_ FN_/) {
+ handle_loadparm($public_file, $line);
+ handle_loadparm($private_file, $line);
+ next;
+ }
+
+ if ($line =~ /^_PUBLIC_[\t ]/) {
+ $target = \&public;
+ $is_public = 1;
+ }
+
+ next unless ( $is_public || $line =~ /
+ ^(_DEPRECATED_ |_NORETURN_ |_WARN_UNUSED_RESULT_ |_PURE_ )*(
+ void|bool|int|struct|char|const|\w+_[tT]\s|uint|unsigned|long|NTSTATUS|
+ ADS_STATUS|enum\s.*\(|DATA_BLOB|WERROR|XFILE|FILE|DIR|
+ double|TDB_CONTEXT|TDB_DATA|TALLOC_CTX|NTTIME|FN_|init_module|
+ GtkWidget|GType|smb_ucs2_t|krb5_error_code)
+ /xo);
+
+ next if ($line =~ /^int\s*main/);
+
+ $target->("\n$comment") if (defined($comment)); $comment = undef;
+
+ if ( $line =~ /\(.*\)\s*$/o ) {
+ chomp $line;
+ $target->("$line;\n");
+ next;
+ }
+
+ $target->($line);
+
+ while ($line = <FH>) {
+ if ($line =~ /\)\s*$/o) {
+ chomp $line;
+ $target->("$line;\n");
+ last;
+ }
+ $target->($line);
+ }
+ }
+
+ close(FH);
+}
+
+
+print_header(\&public, $public_define);
+if (defined($private_file) and defined($public_file) and $public_file ne $private_file) {
+ print_header(\&private, $private_define);
+
+ private("/* this file contains prototypes for functions that " .
+ "are private \n * to this subsystem or library. These functions " .
+ "should not be \n * used outside this particular subsystem! */\n\n");
+
+ public("/* this file contains prototypes for functions that " .
+ "are part of \n * the public API of this subsystem or library. */\n\n");
+
+}
+
+public("#ifndef _PUBLIC_\n#define _PUBLIC_\n#endif\n\n");
+public("#ifndef _PURE_\n#define _PURE_\n#endif\n\n");
+public("#ifndef _NORETURN_\n#define _NORETURN_\n#endif\n\n");
+public("#ifndef _DEPRECATED_\n#define _DEPRECATED_\n#endif\n\n");
+public("#ifndef _WARN_UNUSED_RESULT_\n#define _WARN_UNUSED_RESULT_\n#endif\n\n");
+
+process_file(\&public, \&private, $_) foreach (@ARGV);
+print_footer(\&public, $public_define);
+if (defined($private_file) and $public_file ne $private_file) {
+ print_footer(\&private, $private_define);
+}
+
+if (not defined($public_file)) {
+ print STDOUT $$public_data;
+}
+
+if (not defined($private_file) and defined($public_file)) {
+ print STDOUT $$private_data;
+}
+
+mkpath(dirname($public_file), 0, 0755);
+open(PUBLIC, ">$public_file") or die("Can't open `$public_file': $!");
+print PUBLIC "$$public_data";
+close(PUBLIC);
+
+if (defined($private_file) and $public_file ne $private_file) {
+ mkpath(dirname($private_file), 0, 0755);
+ open(PRIVATE, ">$private_file") or die("Can't open `$private_file': $!");
+ print PRIVATE "$$private_data";
+ close(PRIVATE);
+}
diff --git a/source4/script/mkrelease.sh b/source4/script/mkrelease.sh
new file mode 100755
index 0000000000..303dfe6d95
--- /dev/null
+++ b/source4/script/mkrelease.sh
@@ -0,0 +1,43 @@
+#!/bin/sh
+
+if [ ! -d ".git" -o `dirname $0` != "./source4/script" ]; then
+ echo "Run this script from the top-level directory in the"
+ echo "repository as: ./source4/script/mkrelease.sh"
+ exit 1
+fi
+
+OUTDIR=`mktemp -d samba-XXXXX`
+(git archive --format=tar HEAD | (cd $OUTDIR/ && tar xf -))
+
+echo SAMBA_VERSION_IS_GIT_SNAPSHOT=no >> $OUTDIR/source4/VERSION
+
+#Prepare the tarball for a Samba4 release, with some generated files,
+#but without Samba3 stuff (to avoid confusion)
+( cd $OUTDIR/ || exit 1
+ rm -rf README Manifest Read-Manifest-Now Roadmap source3 packaging docs-xml examples swat WHATSNEW.txt MAINTAINERS || exit 1
+ cd source4 || exit 1
+ ./autogen.sh || exit 1
+ ./configure || exit 1
+ make dist || exit 1
+) || exit 1
+
+VERSION_FILE=$OUTDIR/source4/version.h
+if [ ! -f $VERSION_FILE ]; then
+ echo "Cannot find version.h at $VERSION_FILE"
+ exit 1;
+fi
+
+VERSION=`sed -n 's/^SAMBA_VERSION_STRING=//p' $VERSION_FILE`
+echo "Version: $VERSION"
+mv $OUTDIR samba-$VERSION || exit 1
+tar -cf samba-$VERSION.tar samba-$VERSION || (rm -rf samba-$VERSION; exit 1)
+rm -rf samba-$VERSION || exit 1
+echo "Now run: "
+echo "gpg --detach-sign --armor samba-$VERSION.tar"
+echo "gzip samba-$VERSION.tar"
+echo "And then upload "
+echo "samba-$VERSION.tar.gz samba-$VERSION.tar.asc"
+echo "to pub/samba/samba4/ on samba.org"
+
+
+
diff --git a/source4/script/mkversion.sh b/source4/script/mkversion.sh
new file mode 100755
index 0000000000..da912ac092
--- /dev/null
+++ b/source4/script/mkversion.sh
@@ -0,0 +1,133 @@
+#!/bin/sh
+
+VERSION_FILE=$1
+OUTPUT_FILE=$2
+
+if test -z "$VERSION_FILE";then
+ VERSION_FILE="VERSION"
+fi
+
+if test -z "$OUTPUT_FILE";then
+ OUTPUT_FILE="version.h"
+fi
+
+SOURCE_DIR=$3
+
+SAMBA_VERSION_MAJOR=`sed -n 's/^SAMBA_VERSION_MAJOR=//p' $SOURCE_DIR$VERSION_FILE`
+SAMBA_VERSION_MINOR=`sed -n 's/^SAMBA_VERSION_MINOR=//p' $SOURCE_DIR$VERSION_FILE`
+SAMBA_VERSION_RELEASE=`sed -n 's/^SAMBA_VERSION_RELEASE=//p' $SOURCE_DIR$VERSION_FILE`
+
+SAMBA_VERSION_REVISION=`sed -n 's/^SAMBA_VERSION_REVISION=//p' $SOURCE_DIR$VERSION_FILE`
+
+SAMBA_VERSION_TP_RELEASE=`sed -n 's/^SAMBA_VERSION_TP_RELEASE=//p' $SOURCE_DIR$VERSION_FILE`
+SAMBA_VERSION_ALPHA_RELEASE=`sed -n 's/^SAMBA_VERSION_ALPHA_RELEASE=//p' $SOURCE_DIR$VERSION_FILE`
+SAMBA_VERSION_PRE_RELEASE=`sed -n 's/^SAMBA_VERSION_PRE_RELEASE=//p' $SOURCE_DIR$VERSION_FILE`
+SAMBA_VERSION_RC_RELEASE=`sed -n 's/^SAMBA_VERSION_RC_RELEASE=//p' $SOURCE_DIR$VERSION_FILE`
+
+SAMBA_VERSION_IS_GIT_SNAPSHOT=`sed -n 's/^SAMBA_VERSION_IS_GIT_SNAPSHOT=//p' $SOURCE_DIR$VERSION_FILE`
+
+SAMBA_VERSION_RELEASE_NICKNAME=`sed -n 's/^SAMBA_VERSION_RELEASE_NICKNAME=//p' $SOURCE_DIR$VERSION_FILE`
+
+SAMBA_VERSION_VENDOR_SUFFIX=`sed -n 's/^SAMBA_VERSION_VENDOR_SUFFIX=//p' $SOURCE_DIR$VERSION_FILE`
+SAMBA_VERSION_VENDOR_PATCH=`sed -n 's/^SAMBA_VERSION_VENDOR_PATCH=//p' $SOURCE_DIR$VERSION_FILE`
+
+echo "/* Autogenerated by script/mkversion.sh */" > $OUTPUT_FILE
+
+echo "#define SAMBA_VERSION_MAJOR ${SAMBA_VERSION_MAJOR}" >> $OUTPUT_FILE
+echo "#define SAMBA_VERSION_MINOR ${SAMBA_VERSION_MINOR}" >> $OUTPUT_FILE
+echo "#define SAMBA_VERSION_RELEASE ${SAMBA_VERSION_RELEASE}" >> $OUTPUT_FILE
+
+
+##
+## start with "3.0.22"
+##
+SAMBA_VERSION_STRING="${SAMBA_VERSION_MAJOR}.${SAMBA_VERSION_MINOR}.${SAMBA_VERSION_RELEASE}"
+
+
+##
+## maybe add "3.0.22a" or "4.0.0tp11" or "4.0.0alpha1" or "3.0.22pre1" or "3.0.22rc1"
+## We do not do pre or rc version on patch/letter releases
+##
+if test -n "${SAMBA_VERSION_REVISION}";then
+ SAMBA_VERSION_STRING="${SAMBA_VERSION_STRING}${SAMBA_VERSION_REVISION}"
+ echo "#define SAMBA_VERSION_REVISION \"${SAMBA_VERSION_REVISION}\"" >> $OUTPUT_FILE
+elif test -n "${SAMBA_VERSION_TP_RELEASE}";then
+ SAMBA_VERSION_STRING="${SAMBA_VERSION_STRING}tp${SAMBA_VERSION_TP_RELEASE}"
+ echo "#define SAMBA_VERSION_TP_RELEASE ${SAMBA_VERSION_TP_RELEASE}" >> $OUTPUT_FILE
+elif test -n "${SAMBA_VERSION_ALPHA_RELEASE}";then
+ SAMBA_VERSION_STRING="${SAMBA_VERSION_STRING}alpha${SAMBA_VERSION_ALPHA_RELEASE}"
+ echo "#define SAMBA_VERSION_ALPHA_RELEASE ${SAMBA_VERSION_ALPHA_RELEASE}" >> $OUTPUT_FILE
+elif test -n "${SAMBA_VERSION_PRE_RELEASE}";then
+ ## maybe add "3.0.22pre2"
+ SAMBA_VERSION_STRING="${SAMBA_VERSION_STRING}pre${SAMBA_VERSION_PRE_RELEASE}"
+ echo "#define SAMBA_VERSION_PRE_RELEASE ${SAMBA_VERSION_PRE_RELEASE}" >> $OUTPUT_FILE
+elif test -n "${SAMBA_VERSION_RC_RELEASE}";then
+ SAMBA_VERSION_STRING="${SAMBA_VERSION_STRING}rc${SAMBA_VERSION_RC_RELEASE}"
+ echo "#define SAMBA_VERSION_RC_RELEASE ${SAMBA_VERSION_RC_RELEASE}" >> $OUTPUT_FILE
+fi
+
+##
+## GIT commit details
+##
+if test x"${SAMBA_VERSION_IS_GIT_SNAPSHOT}" = x"yes";then
+ _SAVE_LANG=${LANG}
+ LANG="C"
+ HAVEVER="no"
+
+ if test x"${HAVEVER}" != x"yes" -a -d "${SOURCE_DIR}../.git";then
+ HAVEGIT=no
+ GIT_INFO=`git show --pretty=format:"%h%n%ct%n%H%n%cd" --stat HEAD 2>/dev/null`
+ GIT_COMMIT_ABBREV=`printf %s "${GIT_INFO}" | sed -n 1p`
+ GIT_COMMIT_TIME=`printf %s "${GIT_INFO}" | sed -n 2p`
+ GIT_COMMIT_FULLREV=`printf %s "${GIT_INFO}" | sed -n 3p`
+ GIT_COMMIT_DATE=`printf %s "${GIT_INFO}" | sed -n 4p`
+ if test -n "${GIT_COMMIT_ABBREV}";then
+ HAVEGIT=yes
+ HAVEVER=yes
+ fi
+ fi
+
+ if test x"${HAVEGIT}" = x"yes";then
+ SAMBA_VERSION_STRING="${SAMBA_VERSION_STRING}-GIT-${GIT_COMMIT_ABBREV}"
+
+ echo "#define SAMBA_VERSION_GIT_COMMIT_ABBREV \"${GIT_COMMIT_ABBREV}\"" >> $OUTPUT_FILE
+ echo "#define SAMBA_VERSION_GIT_COMMIT_TIME ${GIT_COMMIT_TIME}" >> $OUTPUT_FILE
+ echo "#define SAMBA_VERSION_GIT_COMMIT_FULLREV \"${GIT_COMMIT_FULLREV}\"" >> $OUTPUT_FILE
+ echo "#define SAMBA_VERSION_GIT_COMMIT_DATE \"${GIT_COMMIT_DATE}\"" >> $OUTPUT_FILE
+ else
+ SAMBA_VERSION_STRING="${SAMBA_VERSION_STRING}-GIT-UNKNOWN"
+ fi
+ LANG=${_SAVE_LANG}
+fi
+
+echo "#define SAMBA_VERSION_OFFICIAL_STRING \"${SAMBA_VERSION_STRING}\"" >> $OUTPUT_FILE
+
+##
+## Add the vendor string if present
+##
+if test -n "${SAMBA_VERSION_VENDOR_SUFFIX}";then
+ echo "#define SAMBA_VERSION_VENDOR_SUFFIX ${SAMBA_VERSION_VENDOR_SUFFIX}" >> $OUTPUT_FILE
+ SAMBA_VERSION_STRING="${SAMBA_VERSION_STRING}-${SAMBA_VERSION_VENDOR_SUFFIX}"
+ if test -n "${SAMBA_VERSION_VENDOR_PATCH}";then
+ echo "#define SAMBA_VERSION_VENDOR_PATCH ${SAMBA_VERSION_VENDOR_PATCH}" >> $OUTPUT_FILE
+ SAMBA_VERSION_STRING="${SAMBA_VERSION_STRING}-${SAMBA_VERSION_VENDOR_PATCH}"
+ fi
+fi
+
+echo "/* Version for mkrelease.sh: " >> $OUTPUT_FILE
+echo "SAMBA_VERSION_STRING=$SAMBA_VERSION_STRING" >> $OUTPUT_FILE
+echo "*/" >> $OUTPUT_FILE
+
+##
+## Add a release nickname
+##
+if test -n "${SAMBA_VERSION_RELEASE_NICKNAME}";then
+ echo "#define SAMBA_VERSION_RELEASE_NICKNAME ${SAMBA_VERSION_RELEASE_NICKNAME}" >> $OUTPUT_FILE
+ SAMBA_VERSION_STRING="${SAMBA_VERSION_STRING} (${SAMBA_VERSION_RELEASE_NICKNAME})"
+fi
+
+echo "#define SAMBA_VERSION_STRING \"${SAMBA_VERSION_STRING}\"" >> $OUTPUT_FILE
+
+echo "$0: '$OUTPUT_FILE' created for Samba(\"${SAMBA_VERSION_STRING}\")"
+
+exit 0
diff --git a/source4/script/pkg-config b/source4/script/pkg-config
new file mode 100755
index 0000000000..458cac6be2
--- /dev/null
+++ b/source4/script/pkg-config
@@ -0,0 +1,145 @@
+#!/usr/bin/perl
+# Simple pkg-config implementation in perl
+# jelmer@samba.org, November 2006
+
+use strict;
+use Getopt::Long;
+
+my @dirs = split(/:/, $ENV{PKG_CONFIG_PATH});
+
+my $opt_help = 0;
+my $opt_libs = 0;
+my $opt_cflags = 0;
+my $opt_static = 0;
+
+my $result = GetOptions (
+ 'help|h|?' => \$opt_help,
+ 'static' => \$opt_static,
+ 'libs' => \$opt_libs,
+ 'cflags' => \$opt_cflags
+ );
+
+if (not $result) {
+ exit(1);
+}
+
+if ($opt_help) {
+ print "pkg-config replacement in perl\n";
+ print "Copyright (C) 2006 Jelmer Vernooij <jelmer\@samba.org>\n";
+ print "\n";
+ print "Usage: pkg-config [OPTIONS] PACKAGE...\n";
+ print " --help Print this help message\n";
+ print " --static Print flags for static libraries\n";
+ print " --libs Print linker flags\n";
+ print " --cflags Print C compiler flags\n";
+ print "\n";
+ exit(0);
+}
+
+sub find_path($)
+{
+ my $name = shift;
+ foreach my $dir (@dirs) {
+ if (-f "$dir/$name-uninstalled.pc") {
+ return "$dir/$name-uninstalled.pc";
+ }
+ }
+ foreach my $dir (@dirs) {
+ if (-f "$dir/$name.pc" ) {
+ return "$dir/$name.pc";
+ }
+ }
+ die("No such package `$name'");
+}
+
+sub ReplaceVars($$)
+{
+ my ($expr, $vars) = @_;
+
+ $_ = $expr;
+
+ while (/(.*)\${([^}]+)}(.*)/) {
+ $_ = "$1$vars->{$2}$3";
+ }
+
+ return $_;
+}
+
+sub Parse($)
+{
+ my $name = shift;
+ my $path = find_path($name);
+ my %variables = ();
+ my %fields = ();
+ my $lineno = 0;
+ open(IN, "<$path") or die("Unable to open $path: $!");
+ foreach (<IN>) {
+ $lineno+=1;
+ next if (/^#.*/);
+ if (/^([A-Za-z.]+): (.*)$/) {
+ $fields{$1} = ReplaceVars($2, \%variables);
+ } elsif (/^([A-Za-z_]+)=(.*)$/) {
+ $variables{$1} = ReplaceVars($2, \%variables);
+ } elsif (/^[ \t]*$/) {
+ } else {
+ warn("$path:$lineno: parse error");
+ }
+ }
+ close(IN);
+ return \%fields;
+}
+
+sub Cflags($)
+{
+ my $name = shift;
+ my $fields = Parse($name);
+ my @cflags = split(/ /, $fields->{Cflags});
+ foreach (split(/[, ]/, $fields->{Requires})) {
+ push (@cflags, Cflags($_));
+ }
+ return @cflags;
+}
+
+sub Libs($)
+{
+ my $name = shift;
+ my $fields = Parse($name);
+ my @libs = split(/ /, $fields->{Libs});
+ foreach (split(/[, ]/, $fields->{Requires})) {
+ push (@libs, Libs($_));
+ }
+ if ($opt_static) {
+ foreach (split(/[ ,]/, $fields->{"Requires.private"})) {
+ push (@libs, Libs($_));
+ }
+ }
+ return @libs;
+}
+
+my @out = ();
+
+foreach my $pkg (@ARGV)
+{
+ push (@out, Libs($pkg)) if ($opt_libs);
+ push (@out, Cflags($pkg)) if ($opt_cflags);
+}
+
+sub nub
+{
+ my @list = @_;
+ my @ret = ();
+ my %seen = ();
+ foreach (@list) {
+ next if (defined($seen{$_}));
+ push (@ret, $_);
+ $seen{$_} = 1;
+ }
+ return @ret;
+}
+
+if ($#out >= 0) {
+ @out = nub(@out);
+ print join(' ', @out) . "\n";
+}
+
+exit 0;
diff --git a/source4/script/revert.sh b/source4/script/revert.sh
new file mode 100755
index 0000000000..8df5fd2fbd
--- /dev/null
+++ b/source4/script/revert.sh
@@ -0,0 +1,18 @@
+#!/bin/sh
+BINDIR=$1
+shift
+
+for p in $*; do
+ p2=`basename $p`
+ if [ -f $BINDIR/$p2.old ]; then
+ echo Restoring $BINDIR/$p2.old
+ mv $BINDIR/$p2 $BINDIR/$p2.new
+ mv $BINDIR/$p2.old $BINDIR/$p2
+ rm -f $BINDIR/$p2.new
+ else
+ echo Not restoring $p
+ fi
+done
+
+exit 0
+
diff --git a/source4/script/uninstalllib.sh b/source4/script/uninstalllib.sh
new file mode 100755
index 0000000000..9c45b2c941
--- /dev/null
+++ b/source4/script/uninstalllib.sh
@@ -0,0 +1,35 @@
+#!/bin/sh
+# based on uninstallbin.sh
+# 4 July 96 Dan.Shearer@UniSA.edu.au
+
+LIBDIR=$1
+shift
+
+if [ ! -d $LIBDIR ]; then
+ echo Directory $LIBDIR does not exist!
+ echo Do a "make installbin" or "make install" first.
+ exit 1
+fi
+
+for p in $*; do
+ p2=`basename $p`
+ if [ -f $LIBDIR/$p2 ]; then
+ echo Removing $LIBDIR/$p2
+ rm -f $LIBDIR/$p2
+ if [ -f $LIBDIR/$p2 ]; then
+ echo Cannot remove $LIBDIR/$p2 ... does $USER have privileges?
+ fi
+ fi
+done
+
+
+cat << EOF
+======================================================================
+The shared libraries have been uninstalled. You may restore the libraries using
+the command "make installlib" or "make install" to install binaries,
+man pages, modules and shell scripts. You can restore a previous
+version of the libraries (if there were any) using "make revert".
+======================================================================
+EOF
+
+exit 0
diff --git a/source4/script/uninstallman.sh b/source4/script/uninstallman.sh
new file mode 100755
index 0000000000..9b087c68bb
--- /dev/null
+++ b/source4/script/uninstallman.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+# 4 July 96 Dan.Shearer@UniSA.edu.au
+# Updated for Samba4 by Jelmer Vernooij
+
+MANDIR=$1
+shift 1
+MANPAGES=$*
+
+for I in $MANPAGES
+do
+ SECTION=`echo -n $I | sed "s/.*\(.\)$/\1/"
+ FNAME=$MANDIR/man$SECTION/$I
+ if test -f $FNAME; then
+ echo Deleting $FNAME
+ rm -f $FNAME
+ test -f $FNAME && echo Cannot remove $FNAME... does $USER have privileges?
+ fi
+done
+
+cat << EOF
+======================================================================
+The man pages have been uninstalled. You may install them again using
+the command "make installman" or make "install" to install binaries,
+man pages and shell scripts.
+======================================================================
+EOF
+exit 0
diff --git a/source4/script/update-proto.pl b/source4/script/update-proto.pl
new file mode 100755
index 0000000000..c130650fb8
--- /dev/null
+++ b/source4/script/update-proto.pl
@@ -0,0 +1,242 @@
+#!/usr/bin/perl
+# Simple script for updating the prototypes in a C header file
+#
+# Copyright (C) 2006 Jelmer Vernooij <jelmer@samba.org>
+# Published under the GNU GPL
+
+use strict;
+use warnings;
+use Getopt::Long;
+
+=head1 NAME
+
+update-proto - automatically update prototypes in header files
+
+=head1 SYNOPSIS
+
+update-proto [OPTIONS] <HEADER> <C-FILE>...
+
+update-proto [OPTIONS] <HEADER>
+
+=head1 DESCRIPTION
+
+Update-proto makes sure the prototypes in a C header file are current
+by comparing the existing prototypes in a header with the function definition
+in the source file. It aims to keep the diff between the original header
+and generated one as small as possible.
+
+New prototypes are inserted before any line that contains the following comment:
+
+/* New prototypes are inserted above this line */
+
+It will automatically parse C files after it encounters a line that contains:
+
+/* The following definitions come from FILE */
+
+When two or more prototypes exist for a function, only the first one
+will be kept.
+
+=head1 OPTIONS
+
+=over 4
+
+=item I<--verbose|-v>
+
+Increase verbosity. Currently only two levels of verbosity are used.
+
+=item I<--help>
+
+Show list of options
+
+=back
+
+=head1 BUGS
+
+Strange complex functions are not recognized. In particular those
+created by macros or returning (without typedef) function pointers.
+
+=head1 LICENSE
+
+update-proto is licensed under the GNU General Public License L<http://www.gnu.org/licenses/gpl.html>.
+
+=head1 AUTHOR
+
+update-proto was written by Jelmer Vernooij L<jelmer@samba.org>.
+
+=cut
+
+sub Usage()
+{
+ print "Usage: update-proto.pl [OPTIONS] <HEADER> <C-FILE>...\n";
+ exit 1;
+}
+
+sub Help()
+{
+ print "Usage: update-proto.pl [OPTIONS] <HEADER> <C-FILE>...\n";
+ print "Options:\n";
+ print " --help Show this help message\n";
+ print " --verbose Write changes made to standard error\n\n";
+ exit 0;
+}
+
+my %new_protos = ();
+
+my $verbose = 0;
+
+GetOptions(
+ 'help|h' => \&Help,
+ 'v|verbose' => sub { $verbose += 1; }
+) or Usage();
+
+sub count($$)
+{
+ my ($t, $s) = @_;
+ my $count = 0;
+ while($s =~ s/^(.)//) { $count++ if $1 eq $t; }
+ return $count;
+}
+
+my $header = shift @ARGV;
+
+sub process_file($)
+{
+ my $file = shift;
+ open (IN, "<$file");
+ while (my $line = <IN>) {
+ $_ = $line;
+ next if /^\s/;
+ next unless /\(/;
+ next if /^\/|[;]|^#|}|^\s*static/;
+ s/\/\*(.*?)\*\///g;
+ my $public = s/_PUBLIC_//g;
+ s/_PRINTF_ATTRIBUTE\([^)]+\)//g;
+ next unless /^(struct\s+\w+|union\s+\w+|\w+)\s+\**\s*(\w+)\s*\((.*)$/;
+
+ my $name = $2;
+
+ next if ($name eq "main");
+
+ # Read continuation lines if any
+ my $prn = 1 + count("(", $3) - count(")", $3);
+
+ while ($prn) {
+ my $l = <IN>;
+ $l or die("EOF while parsing function prototype");
+ $line .= $l;
+ $prn += count("(", $l) - count(")", $l);
+ }
+
+ $line =~ s/\n$//;
+
+ # Strip off possible start of function
+ $line =~ s/{\s*$//g;
+
+ $new_protos{$name} = "$line;";
+ }
+ close(IN);
+}
+
+process_file($_) foreach (@ARGV);
+
+my $added = 0;
+my $modified = 0;
+my $deleted = 0;
+my $kept = 0;
+
+sub insert_new_protos()
+{
+ foreach (keys %new_protos) {
+ print "$new_protos{$_}\n";
+ print STDERR "Inserted prototype for `$_'\n" if ($verbose);
+ $added+=1;
+ }
+ %new_protos = ();
+}
+
+my $blankline_due = 0;
+
+open (HDR, "<$header");
+while (my $line = <HDR>) {
+ if ($line eq "\n") {
+ $blankline_due = 1;
+ $line = <HDR>;
+ }
+
+ # Recognize C files that prototypes came from
+ if ($line =~ /\/\* The following definitions come from (.*) \*\//) {
+ insert_new_protos();
+ if ($blankline_due) {
+ print "\n";
+ $blankline_due = 0;
+ }
+ process_file($1);
+ print "$line";
+ next;
+ }
+
+ if ($blankline_due) {
+ print "\n";
+ $blankline_due = 0;
+ }
+
+ # Insert prototypes that weren't in the header before
+ if ($line =~ /\/\* New prototypes are inserted above this line.*\*\/\s*/) {
+ insert_new_protos();
+ print "$line\n";
+ next;
+ }
+
+ if ($line =~ /^\s*typedef |^\#|^\s*static/) {
+ print "$line";
+ next;
+ }
+
+ $_ = $line;
+ s/\/\*(.*?)\*\///g;
+ my $public = s/_PUBLIC_//g;
+ s/_PRINTF_ATTRIBUTE\([^)]+\)//g;
+ unless (/^(struct\s+\w+|union\s+\w+|\w+)\s+\**\s*(\w+)\s*\((.*)$/) {
+ print "$line";
+ next;
+ }
+
+ # Read continuation lines if any
+ my $prn = 1 + count("(", $3) - count(")", $3);
+
+ while ($prn) {
+ my $l = <HDR>;
+ $l or die("EOF while parsing function prototype");
+ $line .= $l;
+ $prn += count("(", $l) - count(")", $l);
+ }
+
+ my $name = $2;
+
+ # This prototype is for a function that was removed
+ unless (defined($new_protos{$name})) {
+ $deleted+=1;
+ print STDERR "Removed prototype for `$name'\n" if ($verbose);
+ next;
+ }
+
+ my $nline = $line;
+ chop($nline);
+
+ if ($new_protos{$name} ne $nline) {
+ $modified+=1;
+ print STDERR "Updated prototype for `$name'\n" if ($verbose);
+ print "$new_protos{$name}\n";
+ } else {
+ $kept+=1;
+ print STDERR "Prototype for `$name' didn't change\n" if ($verbose > 1);
+ print "$line";
+ }
+
+ delete $new_protos{$name};
+}
+close(HDR);
+
+print STDERR "$added added, $modified modified, $deleted deleted, $kept unchanged.\n";
+
+1;
diff --git a/source4/scripting/bin/autoidl b/source4/scripting/bin/autoidl
new file mode 100755
index 0000000000..eed4ba3a80
--- /dev/null
+++ b/source4/scripting/bin/autoidl
@@ -0,0 +1,161 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Unix SMB/CIFS implementation.
+# Copyright © Jelmer Vernooij <jelmer@samba.org> 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 <http://www.gnu.org/licenses/>.
+#
+
+import sys
+
+MAX_OPNUM = 1000
+MAX_BASE_SIZE = 0x1000
+MAX_IFACE_VERSION = 1000
+
+DCERPC_FAULT_OP_RNG_ERROR = 0x1c010002
+DCERPC_FAULT_NDR = 0x6f7
+NT_STATUS_NET_WRITE_FAULT = -1073741614
+
+
+sys.path.insert(0, "bin/python")
+
+from samba.dcerpc import ClientConnection
+
+def find_num_funcs(conn):
+ for i in xrange(MAX_OPNUM):
+ try:
+ conn.request(i, "")
+ except RuntimeError, (num, msg):
+ if num == DCERPC_FAULT_OP_RNG_ERROR:
+ return i
+ raise Exception("More than %d functions" % MAX_OPNUM)
+
+class Function:
+ def __init__(self, conn, opnum):
+ self.conn = conn
+ self.opnum = opnum
+
+ def request(self, data):
+ assert isinstance(data, str)
+ self.conn.request(self.opnum, data)
+
+ def valid_ndr(self, data):
+ try:
+ self.request(data)
+ except RuntimeError, (num, msg):
+ if num == DCERPC_FAULT_NDR:
+ return False
+ return True
+
+ def find_base_request(self, start=""):
+ """Find the smallest possible request that we can do that is
+ valid.
+
+ This generally means sending all zeroes so we get NULL pointers where
+ possible."""
+ # TODO: Don't try with just 0's as there may be switch_is() variables
+ # for which 0 is not a valid value or variables with range() set
+ # See how much input bytes we need
+ for i in range(MAX_BASE_SIZE):
+ data = start + chr(0) * i
+ if self.valid_ndr(data):
+ return data
+ return None
+
+ def check_decision_byte(self, base_request, i):
+ """Check whether the specified byte is a possible "decision" byte,
+ e.g. a byte that is part of a pointer, array size variable or
+ discriminant.
+
+ Note that this function only checks if a byte is definitely a decision
+ byte. It may return False in some cases while the byte is actually
+ a decision byte."""
+ data = list(base_request)
+ data[i] = chr(1)
+ return not self.valid_ndr("".join(data))
+
+ def find_deferrant_data(self, base_request, idx):
+ data = list(base_request)
+ data[idx*4] = chr(1)
+ # Just check that this is a pointer to something non-empty:
+ assert not self.valid_ndr("".join(data))
+
+ newdata = self.find_base_request("".join(data))
+
+ if newdata is None:
+ return None
+
+ assert newdata.startswith(data)
+
+ return newdata[len(data):]
+
+ def find_idl(self):
+ base_request = self.find_base_request()
+
+ if base_request is None:
+ raise Exception("Unable to determine base size for opnum %d" % self.opnum)
+
+ print "\tBase request is %r" % base_request
+
+ decision_byte_map = map(lambda x: self.check_decision_byte(base_request, x), range(len(base_request)))
+
+ print decision_byte_map
+
+ # find pointers
+ possible_pointers = map(all,
+ [decision_byte_map[i*4:(i+1)*4] for i in range(int(len(base_request)/4))])
+ print possible_pointers
+
+ pointer_deferrant_bases = map(
+ lambda x: self.find_deferrant_data(base_request, x) if possible_pointers[x] else None, range(len(possible_pointers)))
+
+ print pointer_deferrant_bases
+
+
+if len(sys.argv) < 3:
+ print "Usage: autoidl <binding> <UUID> [<version>]"
+ sys.exit(1)
+
+(binding, uuid) = sys.argv[1:3]
+if len(sys.argv) == 4:
+ version = sys.argv[3]
+else:
+ version = None
+
+if version is None:
+ for i in range(MAX_IFACE_VERSION):
+ try:
+ conn = ClientConnection(binding, (uuid, i))
+ except RuntimeError, (num, msg):
+ if num == NT_STATUS_NET_WRITE_FAULT:
+ continue
+ raise
+ else:
+ break
+else:
+ conn = ClientConnection(binding, (uuid, version))
+
+print "Figuring out number of connections...",
+num_funcs = find_num_funcs(conn)
+print "%d" % num_funcs
+
+# Figure out the syntax for each one
+for i in range(num_funcs):
+ print "Function %d" % i
+ data = Function(conn, i)
+ try:
+ data.find_idl()
+ except Exception, e:
+ print "Error: %r" % e
diff --git a/source4/scripting/bin/epdump b/source4/scripting/bin/epdump
new file mode 100755
index 0000000000..15dee33774
--- /dev/null
+++ b/source4/scripting/bin/epdump
@@ -0,0 +1,24 @@
+#!/usr/bin/python
+
+# Unix SMB/CIFS implementation.
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 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 <http://www.gnu.org/licenses/>.
+#
+
+import sys
+
+if len(sys.argv) < 2:
+ print "Usage: %s <binding-string>" % sys.argv[0]
+ sys.exit(1)
diff --git a/source4/scripting/bin/minschema b/source4/scripting/bin/minschema
new file mode 100755
index 0000000000..e7d7ed4979
--- /dev/null
+++ b/source4/scripting/bin/minschema
@@ -0,0 +1,581 @@
+#!/usr/bin/python
+#
+# work out the minimal schema for a set of objectclasses
+#
+
+import optparse
+
+import os, sys
+
+# Find right directory when running from source tree
+sys.path.insert(0, "bin/python")
+
+import samba
+from samba import getopt as options, Ldb
+from ldb import SCOPE_SUBTREE, SCOPE_BASE, LdbError
+import sys
+
+parser = optparse.OptionParser("minschema <URL> <classfile>")
+sambaopts = options.SambaOptions(parser)
+parser.add_option_group(sambaopts)
+credopts = options.CredentialsOptions(parser)
+parser.add_option_group(credopts)
+parser.add_option_group(options.VersionOptions(parser))
+parser.add_option("--verbose", help="Be verbose", action="store_true")
+parser.add_option("--dump-classes", action="store_true")
+parser.add_option("--dump-attributes", action="store_true")
+parser.add_option("--dump-subschema", action="store_true")
+parser.add_option("--dump-subschema-auto", action="store_true")
+
+opts, args = parser.parse_args()
+opts.dump_all = True
+
+if opts.dump_classes:
+ opts.dump_all = False
+if opts.dump_attributes:
+ opts.dump_all = False
+if opts.dump_subschema:
+ opts.dump_all = False
+if opts.dump_subschema_auto:
+ opts.dump_all = False
+ opts.dump_subschema = True
+if opts.dump_all:
+ opts.dump_classes = True
+ opts.dump_attributes = True
+ opts.dump_subschema = True
+ opts.dump_subschema_auto = True
+
+if len(args) != 2:
+ parser.print_usage()
+ sys.exit(1)
+
+(url, classfile) = args
+
+lp_ctx = sambaopts.get_loadparm()
+
+creds = credopts.get_credentials(lp_ctx)
+ldb = Ldb(url, credentials=creds)
+
+objectclasses = []
+attributes = []
+
+objectclasses_expanded = set()
+
+# the attributes we need for objectclasses
+class_attrs = ["objectClass",
+ "subClassOf",
+ "governsID",
+ "possSuperiors",
+ "possibleInferiors",
+ "mayContain",
+ "mustContain",
+ "auxiliaryClass",
+ "rDNAttID",
+ "showInAdvancedViewOnly",
+ "adminDisplayName",
+ "adminDescription",
+ "objectClassCategory",
+ "lDAPDisplayName",
+ "schemaIDGUID",
+ "systemOnly",
+ "systemPossSuperiors",
+ "systemMayContain",
+ "systemMustContain",
+ "systemAuxiliaryClass",
+ "defaultSecurityDescriptor",
+ "systemFlags",
+ "defaultHidingValue",
+ "objectCategory",
+ "defaultObjectCategory",
+
+ # this attributes are not used by w2k3
+ "schemaFlagsEx",
+ "msDs-IntId",
+ "msDs-Schema-Extensions",
+ "classDisplayName",
+ "isDefunct"]
+
+attrib_attrs = ["objectClass",
+ "attributeID",
+ "attributeSyntax",
+ "isSingleValued",
+ "rangeLower",
+ "rangeUpper",
+ "mAPIID",
+ "linkID",
+ "showInAdvancedViewOnly",
+ "adminDisplayName",
+ "oMObjectClass",
+ "adminDescription",
+ "oMSyntax",
+ "searchFlags",
+ "extendedCharsAllowed",
+ "lDAPDisplayName",
+ "schemaIDGUID",
+ "attributeSecurityGUID",
+ "systemOnly",
+ "systemFlags",
+ "isMemberOfPartialAttributeSet",
+ "objectCategory",
+
+ # this attributes are not used by w2k3
+ "schemaFlagsEx",
+ "msDs-IntId",
+ "msDs-Schema-Extensions",
+ "classDisplayName",
+ "isEphemeral",
+ "isDefunct"]
+
+#
+# notes:
+#
+# objectClassCategory
+# 1: structural
+# 2: abstract
+# 3: auxiliary
+
+def get_object_cn(ldb, name):
+ attrs = ["cn"]
+
+ res = ldb.search("(ldapDisplayName=%s)" % name, rootDse["schemaNamingContext"], SCOPE_SUBTREE, attrs)
+ assert len(res) == 1
+
+ return res[0]["cn"]
+
+class Objectclass:
+ def __init__(self, ldb, name):
+ """create an objectclass object"""
+ self.name = name
+ self.cn = get_object_cn(ldb, name)
+
+
+class Attribute:
+ def __init__(self, ldb, name):
+ """create an attribute object"""
+ self.name = name
+ self.cn = get_object_cn(ldb, name)
+
+
+syntaxmap = dict()
+
+syntaxmap['2.5.5.1'] = '1.3.6.1.4.1.1466.115.121.1.12'
+syntaxmap['2.5.5.2'] = '1.3.6.1.4.1.1466.115.121.1.38'
+syntaxmap['2.5.5.3'] = '1.2.840.113556.1.4.1362'
+syntaxmap['2.5.5.4'] = '1.2.840.113556.1.4.905'
+syntaxmap['2.5.5.5'] = '1.3.6.1.4.1.1466.115.121.1.26'
+syntaxmap['2.5.5.6'] = '1.3.6.1.4.1.1466.115.121.1.36'
+syntaxmap['2.5.5.7'] = '1.2.840.113556.1.4.903'
+syntaxmap['2.5.5.8'] = '1.3.6.1.4.1.1466.115.121.1.7'
+syntaxmap['2.5.5.9'] = '1.3.6.1.4.1.1466.115.121.1.27'
+syntaxmap['2.5.5.10'] = '1.3.6.1.4.1.1466.115.121.1.40'
+syntaxmap['2.5.5.11'] = '1.3.6.1.4.1.1466.115.121.1.24'
+syntaxmap['2.5.5.12'] = '1.3.6.1.4.1.1466.115.121.1.15'
+syntaxmap['2.5.5.13'] = '1.3.6.1.4.1.1466.115.121.1.43'
+syntaxmap['2.5.5.14'] = '1.2.840.113556.1.4.904'
+syntaxmap['2.5.5.15'] = '1.2.840.113556.1.4.907'
+syntaxmap['2.5.5.16'] = '1.2.840.113556.1.4.906'
+syntaxmap['2.5.5.17'] = '1.3.6.1.4.1.1466.115.121.1.40'
+
+
+def map_attribute_syntax(s):
+ """map some attribute syntaxes from some apparently MS specific
+ syntaxes to the standard syntaxes"""
+ if syntaxmap.has_key(s):
+ return syntaxmap[s]
+ return s
+
+
+def fix_dn(dn):
+ """fix a string DN to use ${SCHEMADN}"""
+ return dn.replace(rootDse["schemaNamingContext"], "${SCHEMADN}")
+
+
+def write_ldif_one(o, attrs):
+ """dump an object as ldif"""
+ print "dn: CN=%s,${SCHEMADN}\n" % o["cn"]
+ for a in attrs:
+ if not o.has_key(a):
+ continue
+ # special case for oMObjectClass, which is a binary object
+ if a == "oMObjectClass":
+ print "%s:: %s\n" % (a, o[a])
+ continue
+ v = o[a]
+ if isinstance(v, str):
+ v = [v]
+ for j in v:
+ print "%s: %s\n" % (a, fix_dn(j))
+ print "\n"
+
+def write_ldif(o, attrs):
+ """dump an array of objects as ldif"""
+ for i in o:
+ write_ldif_one(i, attrs)
+
+
+def create_testdn(exampleDN):
+ """create a testDN based an an example DN
+ the idea is to ensure we obey any structural rules"""
+ a = exampleDN.split(",")
+ a[0] = "CN=TestDN"
+ return ",".join(a)
+
+
+def find_objectclass_properties(ldb, o):
+ """the properties of an objectclass"""
+ res = ldb.search(
+ expression="(ldapDisplayName=%s)" % o.name,
+ base=rootDse["schemaNamingContext"], scope=SCOPE_SUBTREE, attrs=class_attrs)
+ assert(len(res) == 1)
+ msg = res[0]
+ for a in msg:
+ o[a] = msg[a]
+
+def find_attribute_properties(ldb, o):
+ """find the properties of an attribute"""
+ res = ldb.search(
+ expression="(ldapDisplayName=%s)" % o.name,
+ base=rootDse["schemaNamingContext"], scope=SCOPE_SUBTREE,
+ attrs=attrib_attrs)
+ assert(len(res) == 1)
+ msg = res[0]
+ for a in msg:
+ # special case for oMObjectClass, which is a binary object
+ if a == "oMObjectClass":
+ o[a] = ldb.encode(msg[a])
+ continue
+ o[a] = msg[a]
+
+
+def find_objectclass_auto(ldb, o):
+ """find the auto-created properties of an objectclass. Only works for
+ classes that can be created using just a DN and the objectclass"""
+ if not o.has_key("exampleDN"):
+ return
+ testdn = create_testdn(o.exampleDN)
+
+ print "testdn is '%s'\n" % testdn
+
+ ldif = "dn: " + testdn
+ ldif += "\nobjectClass: " + o.name
+ try:
+ ldb.add(ldif)
+ except LdbError, e:
+ print "error adding %s: %s\n" % (o.name, e)
+ print "%s\n" % ldif
+ return
+
+ res = ldb.search(base=testdn, scope=ldb.SCOPE_BASE)
+ ldb.delete(testdn)
+
+ for a in res.msgs[0]:
+ attributes[a].autocreate = True
+
+
+def expand_objectclass(ldb, o):
+ """look at auxiliary information from a class to intuit the existance of
+ more classes needed for a minimal schema"""
+ attrs = ["auxiliaryClass", "systemAuxiliaryClass",
+ "possSuperiors", "systemPossSuperiors",
+ "subClassOf"]
+ res = ldb.search(
+ expression="(&(objectClass=classSchema)(ldapDisplayName=%s))" % o.name,
+ base=rootDse["schemaNamingContext"], scope=SCOPE_SUBTREE,
+ attrs=attrs)
+ print "Expanding class %s\n" % o.name
+ assert(len(res) == 1)
+ msg = res[0]
+ for a in attrs:
+ if not msg.has_key(aname):
+ continue
+ list = msg[aname]
+ if isinstance(list, str):
+ list = [msg[aname]]
+ for name in list:
+ if not objectclasses.has_key(name):
+ print "Found new objectclass '%s'\n" % name
+ objectclasses[name] = Objectclass(ldb, name)
+
+
+def add_objectclass_attributes(ldb, objectclass):
+ """add the must and may attributes from an objectclass to the full list
+ of attributes"""
+ attrs = ["mustContain", "systemMustContain",
+ "mayContain", "systemMayContain"]
+ for aname in attrs:
+ if not objectclass.has_key(aname):
+ continue
+ alist = objectclass[aname]
+ if isinstance(alist, str):
+ alist = [alist]
+ for a in alist:
+ if not attributes.has_key(a):
+ attributes[a] = Attribute(ldb, a)
+
+
+def walk_dn(ldb, dn):
+ """process an individual record, working out what attributes it has"""
+ # get a list of all possible attributes for this object
+ attrs = ["allowedAttributes"]
+ try:
+ res = ldb.search("objectClass=*", dn, SCOPE_BASE, attrs)
+ except LdbError, e:
+ print "Unable to fetch allowedAttributes for '%s' - %r\n" % (dn, e)
+ return
+ allattrs = res[0]["allowedAttributes"]
+ try:
+ res = ldb.search("objectClass=*", dn, SCOPE_BASE, allattrs)
+ except LdbError, e:
+ print "Unable to fetch all attributes for '%s' - %s\n" % (dn, e)
+ return
+ msg = res[0]
+ for a in msg:
+ if not attributes.has_key(a):
+ attributes[a] = Attribute(ldb, a)
+
+def walk_naming_context(ldb, namingContext):
+ """walk a naming context, looking for all records"""
+ try:
+ res = ldb.search("objectClass=*", namingContext, SCOPE_DEFAULT,
+ ["objectClass"])
+ except LdbError, e:
+ print "Unable to fetch objectClasses for '%s' - %s\n" % (namingContext, e)
+ return
+ for msg in res:
+ msg = res.msgs[r]["objectClass"]
+ for objectClass in msg:
+ if not objectclasses.has_key(objectClass):
+ objectclasses[objectClass] = Objectclass(ldb, objectClass)
+ objectclasses[objectClass].exampleDN = res.msgs[r]["dn"]
+ walk_dn(ldb, res.msgs[r].dn)
+
+def trim_objectclass_attributes(ldb, objectclass):
+ """trim the may attributes for an objectClass"""
+ # trim possibleInferiors,
+ # include only the classes we extracted
+ if objectclass.has_key("possibleInferiors"):
+ possinf = objectclass["possibleInferiors"]
+ newpossinf = []
+ if isinstance(possinf, str):
+ possinf = [possinf]
+ for x in possinf:
+ if objectclasses.has_key(x):
+ newpossinf[n] = x
+ n+=1
+ objectclass["possibleInferiors"] = newpossinf
+
+ # trim systemMayContain,
+ # remove duplicates
+ if objectclass.has_key("systemMayContain"):
+ sysmay = objectclass["systemMayContain"]
+ newsysmay = []
+ if isinstance(sysmay, str):
+ sysmay = [sysmay]
+ for x in sysmay:
+ if not x in newsysmay:
+ newsysmay.append(x)
+ objectclass["systemMayContain"] = newsysmay
+
+ # trim mayContain,
+ # remove duplicates
+ if not objectclass.has_key("mayContain"):
+ may = objectclass["mayContain"]
+ newmay = []
+ if isinstance(may, str):
+ may = [may]
+ for x in may:
+ if not x in newmay:
+ newmay.append(x)
+ objectclass["mayContain"] = newmay
+
+def build_objectclass(ldb, name):
+ """load the basic attributes of an objectClass"""
+ attrs = ["name"]
+ try:
+ res = ldb.search(
+ expression="(&(objectClass=classSchema)(ldapDisplayName=%s))" % name,
+ base=rootDse["schemaNamingContext"], scope=SCOPE_SUBTREE,
+ attrs=attrs)
+ except LdbError, e:
+ print "unknown class '%s'\n" % name
+ return None
+ if len(res) == 0:
+ print "unknown class '%s'\n" % name
+ return None
+ return Objectclass(ldb, name)
+
+def attribute_list(objectclass, attr1, attr2):
+ """form a coalesced attribute list"""
+ a1 = objectclass[attr1]
+ a2 = objectclass[attr2]
+ if isinstance(a1, str):
+ a1 = [a1]
+ if isinstance(a2, str):
+ a2 = [a2]
+ return a1 + a2
+
+def aggregate_list(name, list):
+ """write out a list in aggregate form"""
+ if list is None:
+ return
+ print "%s ( %s )" % (name, "$ ".join(list))
+
+def write_aggregate_objectclass(objectclass):
+ """write the aggregate record for an objectclass"""
+ print "objectClasses: ( %s NAME '%s' " % (objectclass.governsID, objectclass.name)
+ if not objectclass.has_key('subClassOf'):
+ print "SUP %s " % objectclass['subClassOf']
+ if objectclass.objectClassCategory == 1:
+ print "STRUCTURAL "
+ elif objectclass.objectClassCategory == 2:
+ print "ABSTRACT "
+ elif objectclass.objectClassCategory == 3:
+ print "AUXILIARY "
+
+ list = attribute_list(objectclass, "systemMustContain", "mustContain")
+ aggregate_list("MUST", list)
+
+ list = attribute_list(objectclass, "systemMayContain", "mayContain")
+ aggregate_list("MAY", list)
+
+ print ")\n"
+
+
+def write_aggregate_ditcontentrule(objectclass):
+ """write the aggregate record for an ditcontentrule"""
+ list = attribute_list(objectclass, "auxiliaryClass", "systemAuxiliaryClass")
+ if list is None:
+ return
+
+ print "dITContentRules: ( %s NAME '%s' " % (objectclass.governsID, objectclass.name)
+
+ aggregate_list("AUX", list)
+
+ may_list = None
+ must_list = None
+
+ for c in list:
+ list2 = attribute_list(objectclasses[c],
+ "mayContain", "systemMayContain")
+ may_list = may_list + list2
+ list2 = attribute_list(objectclasses[c],
+ "mustContain", "systemMustContain")
+ must_list = must_list + list2
+
+ aggregate_list("MUST", must_list)
+ aggregate_list("MAY", may_list)
+
+ print ")\n"
+
+def write_aggregate_attribute(attrib):
+ """write the aggregate record for an attribute"""
+ print "attributeTypes: ( %s NAME '%s' SYNTAX '%s' " % (
+ attrib.attributeID, attrib.name,
+ map_attribute_syntax(attrib.attributeSyntax))
+ if attrib['isSingleValued'] == "TRUE":
+ print "SINGLE-VALUE "
+ if attrib['systemOnly'] == "TRUE":
+ print "NO-USER-MODIFICATION "
+
+ print ")\n"
+
+
+def write_aggregate():
+ """write the aggregate record"""
+ print "dn: CN=Aggregate,${SCHEMADN}\n"
+ print """objectClass: top
+objectClass: subSchema
+objectCategory: CN=SubSchema,${SCHEMADN}
+"""
+ if not opts.dump_subschema_auto:
+ return
+
+ for objectclass in objectclasses:
+ write_aggregate_objectclass(objectclass)
+ for attr in attributes:
+ write_aggregate_attribute(attr)
+ for objectclass in objectclasses:
+ write_aggregate_ditcontentrule(objectclass)
+
+def load_list(file):
+ """load a list from a file"""
+ return open(file, 'r').readlines()
+
+# get the rootDSE
+res = ldb.search(base="", expression="", scope=SCOPE_BASE, attrs=["schemaNamingContext"])
+rootDse = res[0]
+
+# load the list of classes we are interested in
+classes = load_list(classfile)
+for classname in classes:
+ objectclass = build_objectclass(ldb, classname)
+ if objectclass is not None:
+ objectclasses[classname] = objectclass
+
+
+#
+# expand the objectclass list as needed
+#
+expanded = 0
+
+# so EJS do not have while nor the break statement
+# cannot find any other way than doing more loops
+# than necessary to recursively expand all classes
+#
+for inf in range(500):
+ for n in objectclasses:
+ if not n in objectclasses_expanded:
+ expand_objectclass(ldb, objectclasses[i])
+ objectclasses_expanded.add(n)
+
+#
+# find objectclass properties
+#
+for objectclass in objectclasses:
+ find_objectclass_properties(ldb, objectclass)
+
+
+#
+# form the full list of attributes
+#
+for objectclass in objectclasses:
+ add_objectclass_attributes(ldb, objectclass)
+
+# and attribute properties
+for attr in attributes:
+ find_attribute_properties(ldb, attr)
+
+#
+# trim the 'may' attribute lists to those really needed
+#
+for objectclass in objectclasses:
+ trim_objectclass_attributes(ldb, objectclass)
+
+#
+# dump an ldif form of the attributes and objectclasses
+#
+if opts.dump_attributes:
+ write_ldif(attributes, attrib_attrs)
+if opts.dump_classes:
+ write_ldif(objectclasses, class_attrs)
+if opts.dump_subschema:
+ write_aggregate()
+
+if not opts.verbose:
+ sys.exit(0)
+
+#
+# dump list of objectclasses
+#
+print "objectClasses:\n"
+for objectclass in objectclasses:
+ print "\t%s\n" % objectclass
+
+print "attributes:\n"
+for attr in attributes:
+ print "\t%s\n" % attr
+
+print "autocreated attributes:\n"
+for attr in attributes:
+ if attr.autocreate:
+ print "\t%s\n" % i
diff --git a/source4/scripting/bin/mymachinepw b/source4/scripting/bin/mymachinepw
new file mode 100755
index 0000000000..3a843b5947
--- /dev/null
+++ b/source4/scripting/bin/mymachinepw
@@ -0,0 +1,59 @@
+#!/usr/bin/env python
+
+# Unix SMB/CIFS implementation.
+# Copyright (C) Volker Lendecke 2008
+# Copyright (C) Stefan Metzmacher 2008
+#
+# Extract our own machine pw from secrets.ldb
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+import samba.param as param, ldb, sys, getopt
+
+optlist, args = getopt.getopt(sys.argv[1:], "s:")
+
+conf = param.LoadParm()
+loaded = False
+
+for o, v in optlist:
+ if o == "-s":
+ if not conf.load(v):
+ print(v + " not found")
+ exit(1)
+ loaded = True
+
+if not loaded:
+ conf.load_default()
+
+path=conf.get("private dir") + "/secrets.ldb"
+netbios=conf.get("netbios name")
+
+secrets = ldb.Ldb(path)
+
+search = "(&(objectclass=primaryDomain)(samaccountname=" + \
+ netbios + "$))"
+
+msg = secrets.search(expression=search, attrs=['secret'])
+
+if not msg:
+ print "Error:"
+ print "Password for host[%s] not found in path[%s]." % (netbios, path)
+ print "You may want to pass the smb.conf location via the -s option."
+ exit(1)
+
+password=msg[0]['secret'][0]
+
+print(password)
+exit(0)
diff --git a/source4/scripting/bin/rpcclient b/source4/scripting/bin/rpcclient
new file mode 100755
index 0000000000..aba4f9ddb3
--- /dev/null
+++ b/source4/scripting/bin/rpcclient
@@ -0,0 +1,305 @@
+#!/usr/bin/python
+
+import sys, os, string
+
+# Find right directory when running from source tree
+sys.path.insert(0, "bin/python")
+
+from cmd import Cmd
+from optparse import OptionParser
+from pprint import pprint
+
+import dcerpc, samr
+
+def swig2dict(obj):
+ """Convert a swig object to a dictionary."""
+
+ result = {}
+
+ for attr in filter(lambda x: type(x) == str, dir(obj)):
+
+ if attr[:2] == '__' and attr[-2:] == '__':
+ continue
+
+ if attr == 'this' or attr == 'thisown':
+ continue
+
+ result[attr] = getattr(obj, attr)
+
+ return result
+
+class rpcclient(Cmd):
+
+ prompt = 'rpcclient$ '
+
+ def __init__(self, server, cred):
+ Cmd.__init__(self)
+ self.server = server
+ self.cred = cred
+
+ def emptyline(self):
+
+ # Default for empty line is to repeat last command - yuck
+
+ pass
+
+ def onecmd(self, line):
+
+ # Override the onecmd() method so we can trap error returns
+
+ try:
+ Cmd.onecmd(self, line)
+ except dcerpc.NTSTATUS, arg:
+ print 'The command returned an error: %s' % arg[1]
+
+ # Command handlers
+
+ def do_help(self, line):
+ """Displays on-line help for rpcclient commands."""
+ Cmd.do_help(self, line)
+
+ def do_shell(self, line):
+
+ status = os.system(line)
+
+ if os.WIFEXITED(status):
+ if os.WEXITSTATUS(status) != 0:
+ print 'Command exited with code %d' % os.WEXITSTATUS(status)
+ else:
+ print 'Command exited with signal %d' % os.WTERMSIG(status)
+
+ def do_EOF(self, line):
+ """Exits rpcclient."""
+ print
+ sys.exit(0)
+
+ # SAMR pipe commands
+
+ def do_SamrEnumDomains(self, line):
+ """Enumerate domain names."""
+
+ usage = 'usage: SamrEnumDomains'
+
+ if line != '':
+ print usage
+ return
+
+ pipe = dcerpc.pipe_connect(
+ 'ncacn_np:%s' % self.server,
+ dcerpc.DCERPC_SAMR_UUID, int(dcerpc.DCERPC_SAMR_VERSION),
+ self.cred)
+
+ connect_handle = samr.Connect(pipe)
+
+ for i in connect_handle.EnumDomains():
+ print i
+
+ def do_SamrLookupDomain(self, line):
+ """Return the SID for a domain."""
+
+ usage = 'SamrLookupDomain DOMAIN'
+
+ parser = OptionParser(usage)
+ options, args = parser.parse_args(string.split(line))
+
+ if len(args) != 1:
+ print 'usage:', usage
+ return
+
+ pipe = dcerpc.pipe_connect(
+ 'ncacn_np:%s' % self.server,
+ dcerpc.DCERPC_SAMR_UUID, int(dcerpc.DCERPC_SAMR_VERSION),
+ self.cred)
+
+ connect_handle = samr.Connect(pipe)
+
+ print connect_handle.LookupDomain(args[0])
+
+ def do_SamrQueryDomInfo(self, line):
+ """Return information about a domain designated by its SID."""
+
+ usage = 'SamrQueryDomInfo DOMAIN_SID [info_level]'
+
+ parser = OptionParser(usage)
+ options, args = parser.parse_args(string.split(line))
+
+ if (len(args) == 0) or (len(args) > 2):
+ print 'usage:', usage
+ return
+
+ pipe = dcerpc.pipe_connect(
+ 'ncacn_np:%s' % self.server,
+ dcerpc.DCERPC_SAMR_UUID, int(dcerpc.DCERPC_SAMR_VERSION),
+ self.cred)
+
+ connect_handle = samr.Connect(pipe)
+ domain_handle = connect_handle.OpenDomain(args[0])
+
+ if (len(args) == 2):
+ result = domain_handle.QueryDomainInfo(int(args[1]))
+ else:
+ result = domain_handle.QueryDomainInfo()
+
+ pprint(swig2dict(result))
+
+ def do_SamrQueryDomInfo2(self, line):
+ """Return information about a domain designated by its SID.
+ (Windows 2000 and >)"""
+
+ usage = 'SamrQueryDomInfo2 DOMAIN_SID [info_level] (Windows 2000 and >)'
+ parser = OptionParser(usage)
+ options, args = parser.parse_args(string.split(line))
+
+ if len(args) == 0 or len(args) > 2:
+ print 'usage:', usage
+ return
+
+ pipe = dcerpc.pipe_connect(
+ 'ncacn_np:%s' % self.server,
+ dcerpc.DCERPC_SAMR_UUID, int(dcerpc.DCERPC_SAMR_VERSION),
+ self.cred)
+
+ connect_handle = samr.Connect(pipe)
+ domain_handle = connect_handle.OpenDomain(args[0])
+
+ if (len(args) == 2):
+ result = domain_handle.QueryDomainInfo2(int(args[1]))
+ else:
+ result = domain_handle.QueryDomainInfo2()
+
+ pprint(swig2dict(result))
+
+ def do_SamrEnumDomainGroups(self, line):
+ """Return the list of groups of a domain designated by its SID."""
+
+ usage = 'SamrEnumDomainGroups DOMAIN_SID'
+
+ parser = OptionParser(usage)
+ options, args = parser.parse_args(string.split(line))
+
+ if len(args) != 1:
+ print 'usage:', usage
+ return
+
+ pipe = dcerpc.pipe_connect(
+ 'ncacn_np:%s' % self.server,
+ dcerpc.DCERPC_SAMR_UUID, int(dcerpc.DCERPC_SAMR_VERSION),
+ self.cred)
+
+ connect_handle = samr.Connect(pipe)
+ domain_handle = connect_handle.OpenDomain(args[0])
+
+ result = domain_handle.EnumDomainGroups()
+
+ pprint(result)
+
+ def do_SamrEnumDomainAliases(self, line):
+ """Return the list of aliases (local groups) of a domain designated
+ by its SID."""
+
+ usage = 'SamrEnumDomainAliases DOMAIN_SID'
+
+ parser = OptionParser(usage)
+ options, args = parser.parse_args(string.split(line))
+
+ if len(args) != 1:
+ print 'usage:', usage
+ return
+
+ pipe = dcerpc.pipe_connect(
+ 'ncacn_np:%s' % self.server,
+ dcerpc.DCERPC_SAMR_UUID, int(dcerpc.DCERPC_SAMR_VERSION),
+ self.cred)
+
+ connect_handle = samr.Connect(pipe)
+ domain_handle = connect_handle.OpenDomain(args[0])
+
+ result = domain_handle.EnumDomainAliases()
+
+ pprint(result)
+
+ def do_SamrEnumDomainUsers(self, line):
+ """Return the list of users of a domain designated by its SID."""
+
+ usage = 'SamrEnumDomainUsers DOMAIN_SID [user_account_flags]'
+
+ parser = OptionParser(usage)
+ options, args = parser.parse_args(string.split(line))
+
+ if (len(args) == 0) or (len(args) > 2):
+ print 'usage:', usage
+ return
+
+ pipe = dcerpc.pipe_connect(
+ 'ncacn_np:%s' % self.server,
+ dcerpc.DCERPC_SAMR_UUID, int(dcerpc.DCERPC_SAMR_VERSION),
+ self.cred)
+
+ connect_handle = samr.Connect(pipe)
+ domain_handle = connect_handle.OpenDomain(args[0])
+
+ if (len(args) == 2):
+ result = domain_handle.EnumDomainUsers(int(args[1]))
+ else:
+ result = domain_handle.EnumDomainUsers()
+
+ pprint(result)
+
+if __name__ == '__main__':
+
+ # Parse command line
+
+ usage = 'rpcclient SERVER [options]'
+
+ if len(sys.argv) == 1:
+ print usage
+ sys.exit(1)
+
+ server = sys.argv[1]
+ del(sys.argv[1])
+
+ parser = OptionParser(usage)
+
+ parser.add_option('-U', '--username', action='store', type='string',
+ help='Use given credentials when connecting',
+ metavar='DOMAIN\\username%password',
+ dest='username')
+
+ parser.add_option('-c', '--command', action='store', type='string',
+ help='Execute COMMAND', dest='command')
+
+ options, args = parser.parse_args()
+
+ # Break --username up into domain, username and password
+
+ cred = None
+
+ if not options.username:
+ options.username = '%'
+
+ domain = ''
+ if string.find(options.username, '\\') != -1:
+ domain, options.username = string.split(options.username, '\\')
+
+ password = ''
+ if string.find(options.username, '%') != -1:
+ options.username, password = string.split(options.username, '%')
+
+ username = options.username
+
+ if username != '':
+ cred = (domain, username, password)
+
+ # Run command loop
+
+ c = rpcclient(server, cred)
+
+ if options.command:
+ c.onecmd(options.command)
+ sys.exit(0)
+
+ while 1:
+ try:
+ c.cmdloop()
+ except KeyboardInterrupt:
+ print 'KeyboardInterrupt'
diff --git a/source4/scripting/bin/samba3dump b/source4/scripting/bin/samba3dump
new file mode 100755
index 0000000000..c11f8dbd0d
--- /dev/null
+++ b/source4/scripting/bin/samba3dump
@@ -0,0 +1,167 @@
+#!/usr/bin/python
+#
+# Dump Samba3 data
+# Copyright Jelmer Vernooij 2005-2007
+# Released under the GNU GPL v3 or later
+#
+
+import optparse
+import os, sys
+
+# Find right directory when running from source tree
+sys.path.insert(0, "bin/python")
+
+import samba
+import samba.samba3
+
+parser = optparse.OptionParser("samba3dump <libdir> [<smb.conf>]")
+parser.add_option("--format", type="choice", metavar="FORMAT",
+ choices=["full", "summary"])
+
+opts, args = parser.parse_args()
+
+if opts.format is None:
+ opts.format = "summary"
+
+def print_header(txt):
+ print "\n%s" % txt
+ print "=" * len(txt)
+
+def print_samba3_policy(pol):
+ print_header("Account Policies")
+ print "Min password length: %d" % pol.min_password_length
+ print "Password history length: %d" % pol.password_history
+ if pol.user_must_logon_to_change_password:
+ print "User must logon to change password: %d" % pol.user_must_logon_to_change_password
+ if pol.maximum_password_age:
+ print "Maximum password age: %d" % pol.maximum_password_age
+ if pol.minimum_password_age:
+ print "Minimum password age: %d" % pol.minimum_password_age
+ if pol.lockout_duration:
+ print "Lockout duration: %d" % pol.lockout_duration
+ if pol.reset_count_minutes:
+ print "Reset Count Minutes: %d" % pol.reset_count_minutes
+ if pol.bad_lockout_minutes:
+ print "Bad Lockout Minutes: %d" % pol.bad_lockout_minutes
+ if pol.disconnect_time:
+ print "Disconnect Time: %d" % pol.disconnect_time
+ if pol.refuse_machine_password_change:
+ print "Refuse Machine Password Change: %d" % pol.refuse_machine_password_change
+
+def print_samba3_sam(samdb):
+ print_header("SAM Database")
+ for user in samdb:
+ print "%s" % user
+
+def print_samba3_shares(shares):
+ print_header("Configured shares")
+ for s in shares:
+ print "--- %s ---" % s.name
+ for p in s:
+ print "\t%s = %s" % (p.key, p.value)
+ print ""
+
+def print_samba3_secrets(secrets):
+ print_header("Secrets")
+
+ if secrets.get_auth_user():
+ print "IPC Credentials:"
+ if secrets.get_auth_user():
+ print " User: %s\n" % secrets.get_auth_user()
+ if secrets.get_auth_password():
+ print " Password: %s\n" % secrets.get_auth_password()
+ if secrets.get_auth_domain():
+ print " Domain: %s\n" % secrets.get_auth_domain()
+
+ if len(list(secrets.ldap_dns())) > 0:
+ print "LDAP passwords:"
+ for dn in secrets.ldap_dns():
+ print "\t%s -> %s" % (dn, secrets.get_ldap_bind_pw(dn))
+ print ""
+
+ print "Domains:"
+ for domain in secrets.domains():
+ print "\t--- %s ---" % domain
+ print "\tSID: %s" % secrets.get_sid(domain)
+ print "\tGUID: %s" % secrets.get_domain_guid(domain)
+ print "\tPlaintext pwd: %s" % secrets.get_machine_password(domain)
+ if secrets.get_machine_last_change_time(domain):
+ print "\tLast Changed: %lu" % secrets.get_machine_last_change_time(domain)
+ if secrets.get_machine_sec_channel_type(domain):
+ print "\tSecure Channel Type: %d\n" % secrets.get_machine_sec_channel_type(domain)
+
+ print "Trusted domains:"
+ for td in secrets.trusted_domains():
+ print td
+
+def print_samba3_regdb(regdb):
+ print_header("Registry")
+ from samba.registry import str_regtype
+
+ for k in regdb.keys():
+ print "[%s]" % k
+ for (value_name, (type, value)) in regdb.values(k).items():
+ print "\"%s\"=%s:%s" % (value_name, str_regtype(type), value)
+
+def print_samba3_winsdb(winsdb):
+ print_header("WINS Database")
+
+ for name in winsdb:
+ (ttl, ips, nb_flags) = winsdb[name]
+ print "%s, nb_flags: %s, ttl: %lu, %d ips, fst: %s" % (name, nb_flags, ttl, len(ips), ips[0])
+
+def print_samba3_groupmappings(groupdb):
+ print_header("Group Mappings")
+
+ for sid in groupdb.groupsids():
+ print "\t--- Group: %s ---" % sid
+
+def print_samba3_aliases(groupdb):
+ for sid in groupdb.aliases():
+ print "\t--- Alias: %s ---" % sid
+
+def print_samba3_idmapdb(idmapdb):
+ print_header("Winbindd SID<->GID/UID mappings")
+
+ print "User High Water Mark: %d" % idmapdb.get_user_hwm()
+ print "Group High Water Mark: %d\n" % idmapdb.get_group_hwm()
+
+ for uid in idmapdb.uids():
+ print "%s -> UID %d" % (idmapdb.get_user_sid(uid), uid)
+
+ for gid in idmapdb.gids():
+ print "%s -> GID %d" % (idmapdb.get_group_sid(gid), gid)
+
+def print_samba3(samba3):
+ print_samba3_policy(samba3.get_policy_db())
+ print_samba3_winsdb(samba3.get_wins_db())
+ print_samba3_regdb(samba3.get_registry())
+ print_samba3_secrets(samba3.get_secrets_db())
+ print_samba3_idmapdb(samba3.get_idmap_db())
+ print_samba3_sam(samba3.get_sam_db())
+ groupdb = samba3.get_groupmapping_db()
+ print_samba3_groupmappings(groupdb)
+ print_samba3_aliases(groupdb)
+ print_samba3_shares(samba3.get_shares())
+
+def print_samba3_summary(samba3):
+ print "WINS db entries: %d" % len(samba3.get_wins_db())
+ print "Registry key count: %d" % len(samba3.get_registry())
+ groupdb = samba3.get_groupmapping_db()
+ print "Groupmap count: %d" % len(list(groupdb.groupsids()))
+ print "Alias count: %d" % len(list(groupdb.aliases()))
+ idmapdb = samba3.get_idmap_db()
+ print "Idmap count: %d" % (len(list(idmapdb.uids())) + len(list(idmapdb.gids())))
+
+libdir = args[0]
+if len(args) > 1:
+ smbconf = args[1]
+else:
+ smbconf = os.path.join(libdir, "smb.conf")
+
+samba3 = samba.samba3.Samba3(libdir, smbconf)
+
+if opts.format == "summary":
+ print_samba3_summary(samba3)
+elif opts.format == "full":
+ print_samba3(samba3)
diff --git a/source4/scripting/bin/smbstatus b/source4/scripting/bin/smbstatus
new file mode 100755
index 0000000000..a9265ead6a
--- /dev/null
+++ b/source4/scripting/bin/smbstatus
@@ -0,0 +1,83 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# provide information on connected users and open files
+# Copyright Ç’ Jelmer Vernooij 2008
+#
+# Based on the original in EJS:
+# Copyright Andrew Tridgell 2005
+# Released under the GNU GPL version 3 or later
+#
+
+import os, sys
+
+sys.path.insert(0, "bin/python")
+
+import optparse
+import samba.getopt as options
+from samba import irpc, messaging
+
+def show_sessions(conn):
+ """show open sessions"""
+
+ sessions = conn.smbsrv_information(irpc.SMBSRV_INFO_SESSIONS).next()
+ print "User Client Connected at"
+ print "-" * 79
+ for session in sessions:
+ fulluser = "%s/%s" % (session.account_name, session.domain_name)
+ print "%-30s %16s %s" % (fulluser, session.client_ip, sys.httptime(session.connect_time))
+ print ""
+
+def show_tcons(open_connection):
+ """show open tree connects"""
+ conn = open_connection("smb_server")
+ tcons = conn.smbsrv_information(irpc.SMBSRV_INFO_TCONS).next()
+ print "Share Client Connected at"
+ print "-" * 79
+ for tcon in tcons:
+ print "%-30s %16s %s" % (tcon.share_name, tcon.client_ip, sys.httptime(tcon.connect_time))
+
+
+def show_nbt(open_connection):
+ """show nbtd information"""
+ conn = open_connection("nbt_server")
+ stats = conn.nbtd_information(irpc.NBTD_INFO_STATISTICS).next()
+ print "NBT server statistics:"
+ fields = [("total_received", "Total received"),
+ ("total_sent", "Total sent"),
+ ("query_count", "Query count"),
+ ("register_count", "Register count"),
+ ("release_count", "Release count")]
+ for (field, description) in fields:
+ print "\t%s:\t%s" % (description, getattr(stats, field))
+ print
+
+parser = optparse.OptionParser("%s [options]" % sys.argv[0])
+sambaopts = options.SambaOptions(parser)
+parser.add_option_group(sambaopts)
+parser.add_option("--messaging-path", type="string", metavar="PATH",
+ help="messaging path")
+parser.add_option("--nbt", help="show NetBIOS status", action="store_true")
+
+opts, args = parser.parse_args()
+
+lp = sambaopts.get_loadparm()
+
+print "%s" % lp.get("server string")
+
+messaging_path = (opts.messaging_path or os.path.join(lp.get("private dir"), "smbd.tmp", "messaging"))
+
+def open_connection(name):
+ return messaging.ClientConnection(name, messaging_path=messaging_path)
+
+if opts.nbt:
+ show_nbt(open_connection)
+else:
+ try:
+ conn = open_connection("smb_server")
+ except RuntimeError, (num, msg):
+ if msg == 'NT_STATUS_OBJECT_NAME_NOT_FOUND':
+ print "No active connections"
+ else:
+ show_sessions(conn)
+ show_tcons(conn)
diff --git a/source4/scripting/bin/subunitrun b/source4/scripting/bin/subunitrun
new file mode 100755
index 0000000000..62717a78a9
--- /dev/null
+++ b/source4/scripting/bin/subunitrun
@@ -0,0 +1,47 @@
+#!/usr/bin/python
+
+# Simple subunit testrunner for python
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 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 <http://www.gnu.org/licenses/>.
+#
+
+import sys
+
+# Find right directory when running from source tree
+sys.path.insert(0, "bin/python")
+sys.path.insert(1, "../lib/subunit/python")
+
+from subunit import SubunitTestRunner
+from unittest import TestProgram
+import optparse
+import os
+from samba import param
+import samba.getopt as options
+import samba.tests
+
+parser = optparse.OptionParser("subunitrun [options] <tests>")
+credopts = options.CredentialsOptions(parser)
+parser.add_option_group(credopts)
+sambaopts = options.SambaOptions(parser)
+parser.add_option_group(sambaopts)
+parser.add_option_group(options.VersionOptions(parser))
+
+args = parser.parse_args()[1]
+
+samba.tests.cmdline_loadparm = sambaopts.get_loadparm()
+samba.tests.cmdline_credentials = credopts.get_credentials(samba.tests.cmdline_loadparm)
+
+runner = SubunitTestRunner()
+program = TestProgram(module=None, argv=[sys.argv[0]] + args, testRunner=runner)
diff --git a/source4/scripting/python/STATUS b/source4/scripting/python/STATUS
new file mode 100644
index 0000000000..ee67b8bc7a
--- /dev/null
+++ b/source4/scripting/python/STATUS
@@ -0,0 +1,14 @@
+dsdb/samdb/ldb_modules/tests/samba3sam.py: Fix remaining failing tests
+lib/ldb/tests/python/ldap.py: Fix remaining 3 FIXME's
+command-line vampire
+provisioning: combine some of the python dictionaries
+finish scripting/bin/smbstatus.py
+
+not important before making Python the default:
+- hierarchy (rename samr -> dcerpc.samr, misc -> samba.misc, etc)
+- scripting/python/samba/upgrade.py
+- install python modules into system
+- SWAT
+- __ndr_pack__/__ndr_unpack__ members for the NDR struct bindings
+- generate docstrings in DCE/RPC bindings
+- eliminate some variables from the python interface because they can be induced
diff --git a/source4/scripting/python/config.mk b/source4/scripting/python/config.mk
new file mode 100644
index 0000000000..ba624ee163
--- /dev/null
+++ b/source4/scripting/python/config.mk
@@ -0,0 +1,35 @@
+[SUBSYSTEM::LIBPYTHON]
+PUBLIC_DEPENDENCIES = EXT_LIB_PYTHON
+PRIVATE_DEPENDENCIES = PYTALLOC
+INIT_FUNCTION_SENTINEL = { NULL, NULL }
+
+LIBPYTHON_OBJ_FILES = $(addprefix $(pyscriptsrcdir)/, modules.o)
+
+[SUBSYSTEM::PYTALLOC]
+PUBLIC_DEPENDENCIES = EXT_LIB_PYTHON LIBTALLOC
+
+PYTALLOC_OBJ_FILES = ../lib/talloc/pytalloc.o
+
+[PYTHON::python_uuid]
+PRIVATE_DEPENDENCIES = LIBNDR
+
+python_uuid_OBJ_FILES = $(pyscriptsrcdir)/uuidmodule.o
+
+[PYTHON::python_glue]
+LIBRARY_REALNAME = samba/glue.$(SHLIBEXT)
+PRIVATE_DEPENDENCIES = LIBNDR LIBLDB SAMDB CREDENTIALS pyldb python_dcerpc_misc python_dcerpc_security pyauth
+
+python_glue_OBJ_FILES = $(pyscriptsrcdir)/pyglue.o
+
+$(python_glue_OBJ_FILES): CFLAGS+=-I$(ldbsrcdir)
+
+_PY_FILES = $(shell find $(pyscriptsrcdir)/samba ../lib/subunit/python -name "*.py")
+
+$(eval $(foreach pyfile, $(_PY_FILES),$(call python_py_module_template,$(patsubst $(pyscriptsrcdir)/%,%,$(pyfile)),$(pyfile))))
+
+EPYDOC_OPTIONS = --no-private --url http://www.samba.org/ --no-sourcecode
+
+epydoc:: pythonmods
+ PYTHONPATH=$(pythonbuilddir):../lib/subunit/python epydoc $(EPYDOC_OPTIONS) samba tdb ldb subunit
+
+install:: installpython
diff --git a/source4/scripting/python/examples/netbios.py b/source4/scripting/python/examples/netbios.py
new file mode 100644
index 0000000000..3671076a59
--- /dev/null
+++ b/source4/scripting/python/examples/netbios.py
@@ -0,0 +1,28 @@
+#!/usr/bin/python
+
+# Unix SMB/CIFS implementation.
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 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 <http://www.gnu.org/licenses/>.
+#
+
+from samba.netbios import Node
+
+n = Node()
+(reply_from, names, addresses) = n.query_name("GANIEDA", "192.168.4.0",
+ timeout=4)
+
+print "Received reply from %s:" % (reply_from, )
+print "Names: %r" % (names, )
+print "Addresses: %r" % (addresses, )
diff --git a/source4/scripting/python/examples/samr.py b/source4/scripting/python/examples/samr.py
new file mode 100755
index 0000000000..b3ea117b40
--- /dev/null
+++ b/source4/scripting/python/examples/samr.py
@@ -0,0 +1,117 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Unix SMB/CIFS implementation.
+# Copyright © Jelmer Vernooij <jelmer@samba.org> 2008
+#
+# Based on samr.js © Andrew Tridgell <tridge@samba.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+import sys
+
+sys.path.insert(0, "bin/python")
+
+from samba.dcerpc import samr, security, lsa
+
+def display_lsa_string(str):
+ return str.string
+
+def FillUserInfo(samr, dom_handle, users, level):
+ """fill a user array with user information from samrQueryUserInfo"""
+ for i in range(len(users)):
+ user_handle = samr.OpenUser(handle, security.SEC_FLAG_MAXIMUM_ALLOWED, users[i].idx)
+ info = samr.QueryUserInfo(user_handle, level)
+ info.name = users[i].name
+ info.idx = users[i].idx
+ users[i] = info
+ samr.Close(user_handle)
+
+def toArray((handle, array, num_entries)):
+ ret = []
+ for x in range(num_entries):
+ ret.append((array.entries[x].idx, array.entries[x].name))
+ return ret
+
+
+def test_Connect(samr):
+ """test the samr_Connect interface"""
+ print "Testing samr_Connect"
+ return samr.Connect2(None, security.SEC_FLAG_MAXIMUM_ALLOWED)
+
+def test_LookupDomain(samr, handle, domain):
+ """test the samr_LookupDomain interface"""
+ print "Testing samr_LookupDomain"
+ return samr.LookupDomain(handle, domain)
+
+def test_OpenDomain(samr, handle, sid):
+ """test the samr_OpenDomain interface"""
+ print "Testing samr_OpenDomain"
+ return samr.OpenDomain(handle, security.SEC_FLAG_MAXIMUM_ALLOWED, sid)
+
+def test_EnumDomainUsers(samr, dom_handle):
+ """test the samr_EnumDomainUsers interface"""
+ print "Testing samr_EnumDomainUsers"
+ users = toArray(samr.EnumDomainUsers(dom_handle, 0, 0, -1))
+ print "Found %d users" % len(users)
+ for idx, user in users:
+ print "\t%s\t(%d)" % (user, idx)
+
+def test_EnumDomainGroups(samr, dom_handle):
+ """test the samr_EnumDomainGroups interface"""
+ print "Testing samr_EnumDomainGroups"
+ groups = toArray(samr.EnumDomainGroups(dom_handle, 0, 0))
+ print "Found %d groups" % len(groups)
+ for idx, group in groups:
+ print "\t%s\t(%d)" % (group, idx)
+
+def test_domain_ops(samr, dom_handle):
+ """test domain specific ops"""
+ test_EnumDomainUsers(samr, dom_handle)
+ test_EnumDomainGroups(samr, dom_handle)
+
+def test_EnumDomains(samr, handle):
+ """test the samr_EnumDomains interface"""
+ print "Testing samr_EnumDomains"
+
+ domains = toArray(samr.EnumDomains(handle, 0, -1))
+ print "Found %d domains" % len(domains)
+ for idx, domain in domains:
+ print "\t%s (%d)" % (display_lsa_string(domain), idx)
+ for idx, domain in domains:
+ print "Testing domain %s" % display_lsa_string(domain)
+ sid = samr.LookupDomain(handle, domain)
+ dom_handle = test_OpenDomain(samr, handle, sid)
+ test_domain_ops(samr, dom_handle)
+ samr.Close(dom_handle)
+
+if len(sys.argv) != 2:
+ print "Usage: samr.js <BINDING>"
+ sys.exit(1)
+
+binding = sys.argv[1]
+
+print "Connecting to %s" % binding
+try:
+ samr = samr.samr(binding)
+except Exception, e:
+ print "Failed to connect to %s: %s" % (binding, e.message)
+ sys.exit(1)
+
+handle = test_Connect(samr)
+test_EnumDomains(samr, handle)
+samr.Close(handle)
+
+print "All OK"
diff --git a/source4/scripting/python/examples/winreg.py b/source4/scripting/python/examples/winreg.py
new file mode 100755
index 0000000000..80b48ecfd7
--- /dev/null
+++ b/source4/scripting/python/examples/winreg.py
@@ -0,0 +1,87 @@
+#!/usr/bin/python
+#
+# tool to manipulate a remote registry
+# Copyright Andrew Tridgell 2005
+# Copyright Jelmer Vernooij 2007
+# Released under the GNU GPL v3 or later
+#
+
+import sys
+
+# Find right directory when running from source tree
+sys.path.insert(0, "bin/python")
+
+from samba.dcerpc import winreg
+import optparse
+import samba.getopt as options
+
+parser = optparse.OptionParser("%s <BINDING> [path]" % sys.argv[0])
+sambaopts = options.SambaOptions(parser)
+parser.add_option_group(sambaopts)
+parser.add_option("--createkey", type="string", metavar="KEYNAME",
+ help="create a key")
+
+opts, args = parser.parse_args()
+
+if len(args) < 1:
+ parser.print_usage()
+ sys.exit(-1)
+
+binding = args[0]
+
+print "Connecting to " + binding
+conn = winreg.winreg(binding, sambaopts.get_loadparm())
+
+def list_values(key):
+ (num_values, max_valnamelen, max_valbufsize) = conn.QueryInfoKey(key, winreg.String())[4:8]
+ for i in range(num_values):
+ name = winreg.StringBuf()
+ name.size = max_valnamelen
+ (name, type, data, _, data_len) = conn.EnumValue(key, i, name, 0, "", max_valbufsize, 0)
+ print "\ttype=%-30s size=%4d '%s'" % type, len, name
+ if type in (winreg.REG_SZ, winreg.REG_EXPAND_SZ):
+ print "\t\t'%s'" % data
+# if (v.type == reg.REG_MULTI_SZ) {
+# for (j in v.value) {
+# printf("\t\t'%s'\n", v.value[j])
+# }
+# }
+# if (v.type == reg.REG_DWORD || v.type == reg.REG_DWORD_BIG_ENDIAN) {
+# printf("\t\t0x%08x (%d)\n", v.value, v.value)
+# }
+# if (v.type == reg.REG_QWORD) {
+# printf("\t\t0x%llx (%lld)\n", v.value, v.value)
+# }
+
+def list_path(key, path):
+ count = 0
+ (num_subkeys, max_subkeylen, max_subkeysize) = conn.QueryInfoKey(key, winreg.String())[1:4]
+ for i in range(num_subkeys):
+ name = winreg.StringBuf()
+ name.size = max_subkeysize
+ keyclass = winreg.StringBuf()
+ keyclass.size = max_subkeysize
+ (name, _, _) = conn.EnumKey(key, i, name, keyclass=keyclass, last_changed_time=None)[0]
+ subkey = conn.OpenKey(key, name, 0, winreg.KEY_QUERY_VALUE | winreg.KEY_ENUMERATE_SUB_KEYS)
+ count += list_path(subkey, "%s\\%s" % (path, name))
+ list_values(subkey)
+ return count
+
+if len(args) > 1:
+ root = args[1]
+else:
+ root = "HKLM"
+
+if opts.createkey:
+ reg.create_key("HKLM\\SOFTWARE", opt.createkey)
+else:
+ print "Listing registry tree '%s'" % root
+ try:
+ root_key = getattr(conn, "Open%s" % root)(None, winreg.KEY_QUERY_VALUE | winreg.KEY_ENUMERATE_SUB_KEYS)
+ except AttributeError:
+ print "Unknown root key name %s" % root
+ sys.exit(1)
+ count = list_path(root_key, root)
+ if count == 0:
+ print "No entries found"
+ sys.exit(1)
diff --git a/source4/scripting/python/modules.c b/source4/scripting/python/modules.c
new file mode 100644
index 0000000000..e53f4cfaf2
--- /dev/null
+++ b/source4/scripting/python/modules.c
@@ -0,0 +1,70 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Jelmer Vernooij <jelmer@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "scripting/python/modules.h"
+#include <Python.h>
+
+extern void init_ldb(void);
+extern void init_security(void);
+extern void init_registry(void);
+extern void init_param(void);
+extern void init_misc(void);
+extern void init_ldb(void);
+extern void init_auth(void);
+extern void init_credentials(void);
+extern void init_tdb(void);
+extern void init_dcerpc(void);
+extern void init_events(void);
+extern void inituuid(void);
+extern void init_net(void);
+extern void initecho(void);
+extern void initdfs(void);
+extern void initdrsuapi(void);
+extern void initwinreg(void);
+extern void initepmapper(void);
+extern void initinitshutdown(void);
+extern void initmgmt(void);
+extern void initnet(void);
+extern void initatsvc(void);
+extern void initsamr(void);
+extern void initlsa(void);
+extern void initsvcctl(void);
+extern void initwkssvc(void);
+extern void initunixinfo(void);
+extern void init_libcli_nbt(void);
+extern void init_libcli_smb(void);
+
+static struct _inittab py_modules[] = { STATIC_LIBPYTHON_MODULES };
+
+void py_load_samba_modules(void)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(py_modules); i++) {
+ PyImport_ExtendInittab(&py_modules[i]);
+ }
+}
+
+void py_update_path(const char *bindir)
+{
+ char *newpath;
+ asprintf(&newpath, "%s/python:%s/../scripting/python:%s", bindir, bindir, Py_GetPath());
+ PySys_SetPath(newpath);
+ free(newpath);
+}
diff --git a/source4/scripting/python/modules.h b/source4/scripting/python/modules.h
new file mode 100644
index 0000000000..6b242ee257
--- /dev/null
+++ b/source4/scripting/python/modules.h
@@ -0,0 +1,28 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Jelmer Vernooij <jelmer@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __SAMBA_PYTHON_MODULES_H__
+#define __SAMBA_PYTHON_MODULES_H__
+
+void py_load_samba_modules(void);
+void py_update_path(const char *bindir);
+
+#define py_iconv_convenience(mem_ctx) smb_iconv_convenience_init(mem_ctx, "ASCII", PyUnicode_GetDefaultEncoding(), true)
+
+#endif /* __SAMBA_PYTHON_MODULES_H__ */
diff --git a/source4/scripting/python/pyglue.c b/source4/scripting/python/pyglue.c
new file mode 100644
index 0000000000..a2c4790611
--- /dev/null
+++ b/source4/scripting/python/pyglue.c
@@ -0,0 +1,274 @@
+/*
+ Unix SMB/CIFS implementation.
+ Copyright (C) Jelmer Vernooij <jelmer@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "ldb.h"
+#include "ldb_errors.h"
+#include "param/param.h"
+#include "auth/credentials/credentials.h"
+#include "dsdb/samdb/samdb.h"
+#include "lib/ldb-samba/ldif_handlers.h"
+#include "librpc/ndr/libndr.h"
+#include "version.h"
+#include <Python.h>
+#include "lib/ldb/pyldb.h"
+#include "libcli/util/pyerrors.h"
+#include "libcli/security/security.h"
+#include "auth/pyauth.h"
+#include "param/pyparam.h"
+
+#ifndef Py_RETURN_NONE
+#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
+#endif
+
+/* FIXME: These should be in a header file somewhere, once we finish moving
+ * away from SWIG .. */
+extern struct cli_credentials *cli_credentials_from_py_object(PyObject *py_obj);
+
+#define PyErr_LDB_OR_RAISE(py_ldb, ldb) \
+ if (!PyLdb_Check(py_ldb)) { \
+ /*PyErr_SetString(PyExc_TypeError, "Ldb connection object required"); \
+ return NULL; \ */ \
+ } \
+ ldb = PyLdb_AsLdbContext(py_ldb);
+
+
+static PyObject *py_generate_random_str(PyObject *self, PyObject *args)
+{
+ int len;
+ PyObject *ret;
+ char *retstr;
+ if (!PyArg_ParseTuple(args, "i", &len))
+ return NULL;
+
+ retstr = generate_random_str(NULL, len);
+ ret = PyString_FromString(retstr);
+ talloc_free(retstr);
+ return ret;
+}
+
+static PyObject *py_unix2nttime(PyObject *self, PyObject *args)
+{
+ time_t t;
+ NTTIME nt;
+ if (!PyArg_ParseTuple(args, "I", &t))
+ return NULL;
+
+ unix_to_nt_time(&nt, t);
+
+ return PyInt_FromLong((uint64_t)nt);
+}
+
+static PyObject *py_ldb_set_credentials(PyObject *self, PyObject *args)
+{
+ PyObject *py_creds, *py_ldb;
+ struct cli_credentials *creds;
+ struct ldb_context *ldb;
+ if (!PyArg_ParseTuple(args, "OO", &py_ldb, &py_creds))
+ return NULL;
+
+ PyErr_LDB_OR_RAISE(py_ldb, ldb);
+
+ creds = cli_credentials_from_py_object(py_creds);
+ if (creds == NULL) {
+ PyErr_SetString(PyExc_TypeError, "Expected credentials object");
+ return NULL;
+ }
+
+ ldb_set_opaque(ldb, "credentials", creds);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_ldb_set_loadparm(PyObject *self, PyObject *args)
+{
+ PyObject *py_lp_ctx, *py_ldb;
+ struct loadparm_context *lp_ctx;
+ struct ldb_context *ldb;
+ if (!PyArg_ParseTuple(args, "OO", &py_ldb, &py_lp_ctx))
+ return NULL;
+
+ PyErr_LDB_OR_RAISE(py_ldb, ldb);
+
+ lp_ctx = lp_from_py_object(py_lp_ctx);
+ if (lp_ctx == NULL) {
+ PyErr_SetString(PyExc_TypeError, "Expected loadparm object");
+ return NULL;
+ }
+
+ ldb_set_opaque(ldb, "loadparm", lp_ctx);
+
+ Py_RETURN_NONE;
+}
+
+
+static PyObject *py_ldb_set_session_info(PyObject *self, PyObject *args)
+{
+ PyObject *py_session_info, *py_ldb;
+ struct auth_session_info *info;
+ struct ldb_context *ldb;
+ if (!PyArg_ParseTuple(args, "OO", &py_ldb, &py_session_info))
+ return NULL;
+
+ PyErr_LDB_OR_RAISE(py_ldb, ldb);
+ /*if (!PyAuthSession_Check(py_session_info)) {
+ PyErr_SetString(PyExc_TypeError, "Expected session info object");
+ return NULL;
+ }*/
+
+ info = PyAuthSession_AsSession(py_session_info);
+
+ ldb_set_opaque(ldb, "sessionInfo", info);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_samdb_set_domain_sid(PyLdbObject *self, PyObject *args)
+{
+ PyObject *py_ldb, *py_sid;
+ struct ldb_context *ldb;
+ struct dom_sid *sid;
+ bool ret;
+
+ if (!PyArg_ParseTuple(args, "OO", &py_ldb, &py_sid))
+ return NULL;
+
+ PyErr_LDB_OR_RAISE(py_ldb, ldb);
+
+ sid = dom_sid_parse_talloc(NULL, PyString_AsString(py_sid));
+
+ ret = samdb_set_domain_sid(ldb, sid);
+ if (!ret) {
+ PyErr_SetString(PyExc_RuntimeError, "set_domain_sid failed");
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_ldb_register_samba_handlers(PyObject *self, PyObject *args)
+{
+ PyObject *py_ldb;
+ struct ldb_context *ldb;
+ int ret;
+
+ if (!PyArg_ParseTuple(args, "O", &py_ldb))
+ return NULL;
+
+ PyErr_LDB_OR_RAISE(py_ldb, ldb);
+ ret = ldb_register_samba_handlers(ldb);
+
+ PyErr_LDB_ERROR_IS_ERR_RAISE(ret, ldb);
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_dsdb_set_ntds_invocation_id(PyObject *self, PyObject *args)
+{
+ PyObject *py_ldb, *py_guid;
+ bool ret;
+ struct GUID guid;
+ struct ldb_context *ldb;
+ if (!PyArg_ParseTuple(args, "OO", &py_ldb, &py_guid))
+ return NULL;
+
+ PyErr_LDB_OR_RAISE(py_ldb, ldb);
+ GUID_from_string(PyString_AsString(py_guid), &guid);
+
+ ret = samdb_set_ntds_invocation_id(ldb, &guid);
+ if (!ret) {
+ PyErr_SetString(PyExc_RuntimeError, "set_ntds_invocation_id failed");
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_dsdb_set_global_schema(PyObject *self, PyObject *args)
+{
+ PyObject *py_ldb;
+ struct ldb_context *ldb;
+ int ret;
+ if (!PyArg_ParseTuple(args, "O", &py_ldb))
+ return NULL;
+
+ PyErr_LDB_OR_RAISE(py_ldb, ldb);
+
+ ret = dsdb_set_global_schema(ldb);
+ PyErr_LDB_ERROR_IS_ERR_RAISE(ret, ldb);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_dsdb_attach_schema_from_ldif_file(PyObject *self, PyObject *args)
+{
+ WERROR result;
+ char *pf, *df;
+ PyObject *py_ldb;
+ struct ldb_context *ldb;
+
+ if (!PyArg_ParseTuple(args, "Oss", &py_ldb, &pf, &df))
+ return NULL;
+
+ PyErr_LDB_OR_RAISE(py_ldb, ldb);
+
+ result = dsdb_attach_schema_from_ldif_file(ldb, pf, df);
+ PyErr_WERROR_IS_ERR_RAISE(result);
+
+ Py_RETURN_NONE;
+}
+
+static PyMethodDef py_misc_methods[] = {
+ { "generate_random_str", (PyCFunction)py_generate_random_str, METH_VARARGS,
+ "random_password(len) -> string\n"
+ "Generate random password with specified length." },
+ { "unix2nttime", (PyCFunction)py_unix2nttime, METH_VARARGS,
+ "unix2nttime(timestamp) -> nttime" },
+ { "ldb_set_credentials", (PyCFunction)py_ldb_set_credentials, METH_VARARGS,
+ "ldb_set_credentials(ldb, credentials) -> None\n"
+ "Set credentials to use when connecting." },
+ { "ldb_set_session_info", (PyCFunction)py_ldb_set_session_info, METH_VARARGS,
+ "ldb_set_session_info(ldb, session_info)\n"
+ "Set session info to use when connecting." },
+ { "ldb_set_loadparm", (PyCFunction)py_ldb_set_loadparm, METH_VARARGS,
+ "ldb_set_loadparm(ldb, session_info)\n"
+ "Set loadparm context to use when connecting." },
+ { "samdb_set_domain_sid", (PyCFunction)py_samdb_set_domain_sid, METH_VARARGS,
+ "samdb_set_domain_sid(samdb, sid)\n"
+ "Set SID of domain to use." },
+ { "ldb_register_samba_handlers", (PyCFunction)py_ldb_register_samba_handlers, METH_VARARGS,
+ "ldb_register_samba_handlers(ldb)\n"
+ "Register Samba-specific LDB modules and schemas." },
+ { "dsdb_set_ntds_invocation_id", (PyCFunction)py_dsdb_set_ntds_invocation_id, METH_VARARGS,
+ NULL },
+ { "dsdb_set_global_schema", (PyCFunction)py_dsdb_set_global_schema, METH_VARARGS,
+ NULL },
+ { "dsdb_attach_schema_from_ldif_file", (PyCFunction)py_dsdb_attach_schema_from_ldif_file, METH_VARARGS,
+ NULL },
+ { NULL }
+};
+
+void initglue(void)
+{
+ PyObject *m;
+
+ m = Py_InitModule3("glue", py_misc_methods,
+ "Python bindings for miscellaneous Samba functions.");
+ if (m == NULL)
+ return;
+
+ PyModule_AddObject(m, "version", PyString_FromString(SAMBA_VERSION_STRING));
+}
+
diff --git a/source4/scripting/python/samba/__init__.py b/source4/scripting/python/samba/__init__.py
new file mode 100644
index 0000000000..a49e6e1ead
--- /dev/null
+++ b/source4/scripting/python/samba/__init__.py
@@ -0,0 +1,242 @@
+#!/usr/bin/python
+
+# Unix SMB/CIFS implementation.
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008
+#
+# Based on the original in EJS:
+# Copyright (C) Andrew Tridgell <tridge@samba.org> 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 <http://www.gnu.org/licenses/>.
+#
+
+"""Samba 4."""
+
+__docformat__ = "restructuredText"
+
+import os
+
+def _in_source_tree():
+ """Check whether the script is being run from the source dir. """
+ return os.path.exists("%s/../../../samba4-skip" % os.path.dirname(__file__))
+
+
+# When running, in-tree, make sure bin/python is in the PYTHONPATH
+if _in_source_tree():
+ import sys
+ srcdir = "%s/../../.." % os.path.dirname(__file__)
+ sys.path.append("%s/bin/python" % srcdir)
+ default_ldb_modules_dir = "%s/bin/modules/ldb" % srcdir
+else:
+ default_ldb_modules_dir = None
+
+
+import ldb
+import credentials
+import glue
+
+class Ldb(ldb.Ldb):
+ """Simple Samba-specific LDB subclass that takes care
+ of setting up the modules dir, credentials pointers, etc.
+
+ Please note that this is intended to be for all Samba LDB files,
+ not necessarily the Sam database. For Sam-specific helper
+ functions see samdb.py.
+ """
+ def __init__(self, url=None, session_info=None, credentials=None,
+ modules_dir=None, lp=None):
+ """Open a Samba Ldb file.
+
+ :param url: Optional LDB URL to open
+ :param session_info: Optional session information
+ :param credentials: Optional credentials, defaults to anonymous.
+ :param modules_dir: Modules directory, if not the default.
+ :param lp: Loadparm object, optional.
+
+ This is different from a regular Ldb file in that the Samba-specific
+ modules-dir is used by default and that credentials and session_info
+ can be passed through (required by some modules).
+ """
+ super(Ldb, self).__init__()
+
+ if modules_dir is not None:
+ self.set_modules_dir(modules_dir)
+ elif default_ldb_modules_dir is not None:
+ self.set_modules_dir(default_ldb_modules_dir)
+
+ if credentials is not None:
+ self.set_credentials(credentials)
+
+ if session_info is not None:
+ self.set_session_info(session_info)
+
+ glue.ldb_register_samba_handlers(self)
+
+ if lp is not None:
+ self.set_loadparm(lp)
+
+ def msg(l,text):
+ print text
+ #self.set_debug(msg)
+
+ if url is not None:
+ self.connect(url)
+
+ def set_credentials(self, credentials):
+ glue.ldb_set_credentials(self, credentials)
+
+ def set_session_info(self, session_info):
+ glue.ldb_set_session_info(self, session_info)
+
+ def set_loadparm(self, lp_ctx):
+ glue.ldb_set_loadparm(self, lp_ctx)
+
+ def searchone(self, attribute, basedn=None, expression=None,
+ scope=ldb.SCOPE_BASE):
+ """Search for one attribute as a string.
+
+ :param basedn: BaseDN for the search.
+ :param attribute: Name of the attribute
+ :param expression: Optional search expression.
+ :param scope: Search scope (defaults to base).
+ :return: Value of attribute as a string or None if it wasn't found.
+ """
+ res = self.search(basedn, scope, expression, [attribute])
+ if len(res) != 1 or res[0][attribute] is None:
+ return None
+ values = set(res[0][attribute])
+ assert len(values) == 1
+ return self.schema_format_value(attribute, values.pop())
+
+ def erase(self):
+ """Erase this ldb, removing all records."""
+ # delete the specials
+ for attr in ["@INDEXLIST", "@ATTRIBUTES", "@SUBCLASSES", "@MODULES",
+ "@OPTIONS", "@PARTITION", "@KLUDGEACL"]:
+ try:
+ self.delete(attr)
+ except ldb.LdbError, (LDB_ERR_NO_SUCH_OBJECT, _):
+ # Ignore missing dn errors
+ pass
+
+ basedn = ""
+ # and the rest
+ for msg in self.search(basedn, ldb.SCOPE_SUBTREE,
+ "(&(|(objectclass=*)(distinguishedName=*))(!(distinguishedName=@BASEINFO)))",
+ ["distinguishedName"]):
+ try:
+ self.delete(msg.dn)
+ except ldb.LdbError, (LDB_ERR_NO_SUCH_OBJECT, _):
+ # Ignore no such object errors
+ pass
+
+ res = self.search(basedn, ldb.SCOPE_SUBTREE, "(&(|(objectclass=*)(distinguishedName=*))(!(distinguishedName=@BASEINFO)))", ["distinguishedName"])
+ assert len(res) == 0
+
+ def erase_partitions(self):
+ """Erase an ldb, removing all records."""
+ res = self.search("", ldb.SCOPE_BASE, "(objectClass=*)",
+ ["namingContexts"])
+ assert len(res) == 1
+ if not "namingContexts" in res[0]:
+ return
+ for basedn in res[0]["namingContexts"]:
+ previous_remaining = 1
+ current_remaining = 0
+
+ k = 0
+ while ++k < 10 and (previous_remaining != current_remaining):
+ # and the rest
+ try:
+ res2 = self.search(basedn, ldb.SCOPE_SUBTREE, "(|(objectclass=*)(distinguishedName=*))", ["distinguishedName"])
+ except ldb.LdbError, (LDB_ERR_NO_SUCH_OBJECT, _):
+ # Ignore missing dn errors
+ return
+
+ previous_remaining = current_remaining
+ current_remaining = len(res2)
+ for msg in res2:
+ try:
+ self.delete(msg.dn)
+ # Ignore no such object errors
+ except ldb.LdbError, (LDB_ERR_NO_SUCH_OBJECT, _):
+ pass
+ # Ignore not allowed on non leaf errors
+ except ldb.LdbError, (LDB_ERR_NOT_ALLOWED_ON_NON_LEAF, _):
+ pass
+
+ def load_ldif_file_add(self, ldif_path):
+ """Load a LDIF file.
+
+ :param ldif_path: Path to LDIF file.
+ """
+ self.add_ldif(open(ldif_path, 'r').read())
+
+ def add_ldif(self, ldif):
+ """Add data based on a LDIF string.
+
+ :param ldif: LDIF text.
+ """
+ for changetype, msg in self.parse_ldif(ldif):
+ assert changetype == ldb.CHANGETYPE_NONE
+ self.add(msg)
+
+ def modify_ldif(self, ldif):
+ """Modify database based on a LDIF string.
+
+ :param ldif: LDIF text.
+ """
+ for changetype, msg in self.parse_ldif(ldif):
+ self.modify(msg)
+
+
+def substitute_var(text, values):
+ """substitute strings of the form ${NAME} in str, replacing
+ with substitutions from subobj.
+
+ :param text: Text in which to subsitute.
+ :param values: Dictionary with keys and values.
+ """
+
+ for (name, value) in values.items():
+ assert isinstance(name, str), "%r is not a string" % name
+ assert isinstance(value, str), "Value %r for %s is not a string" % (value, name)
+ text = text.replace("${%s}" % name, value)
+
+ return text
+
+
+def check_all_substituted(text):
+ """Make sure that all substitution variables in a string have been replaced.
+ If not, raise an exception.
+
+ :param text: The text to search for substitution variables
+ """
+ if not "${" in text:
+ return
+
+ var_start = text.find("${")
+ var_end = text.find("}", var_start)
+
+ raise Exception("Not all variables substituted: %s" % text[var_start:var_end+1])
+
+
+def valid_netbios_name(name):
+ """Check whether a name is valid as a NetBIOS name. """
+ # FIXME: There are probably more constraints here.
+ # crh has a paragraph on this in his book (1.4.1.1)
+ if len(name) > 15:
+ return False
+ return True
+
+version = glue.version
diff --git a/source4/scripting/python/samba/getopt.py b/source4/scripting/python/samba/getopt.py
new file mode 100644
index 0000000000..c12245f6c5
--- /dev/null
+++ b/source4/scripting/python/samba/getopt.py
@@ -0,0 +1,116 @@
+#!/usr/bin/python
+
+# Samba-specific bits for optparse
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 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 <http://www.gnu.org/licenses/>.
+#
+
+"""Support for parsing Samba-related command-line options."""
+
+import optparse
+from credentials import Credentials, AUTO_USE_KERBEROS, DONT_USE_KERBEROS, MUST_USE_KERBEROS
+from hostconfig import Hostconfig
+
+__docformat__ = "restructuredText"
+
+class SambaOptions(optparse.OptionGroup):
+ """General Samba-related command line options."""
+ def __init__(self, parser):
+ optparse.OptionGroup.__init__(self, parser, "Samba Common Options")
+ self.add_option("-s", "--configfile", action="callback",
+ type=str, metavar="FILE", help="Configuration file",
+ callback=self._load_configfile)
+ self._configfile = None
+
+ def get_loadparm_path(self):
+ """Return the path to the smb.conf file specified on the command line. """
+ return self._configfile
+
+ def _load_configfile(self, option, opt_str, arg, parser):
+ self._configfile = arg
+
+ def get_loadparm(self):
+ """Return a loadparm object with data specified on the command line. """
+ import os, param
+ lp = param.LoadParm()
+ if self._configfile is not None:
+ lp.load(self._configfile)
+ elif os.getenv("SMB_CONF_PATH") is not None:
+ lp.load(os.getenv("SMB_CONF_PATH"))
+ else:
+ lp.load_default()
+ return lp
+
+ def get_hostconfig(self):
+ return Hostconfig(self.get_loadparm())
+
+
+class VersionOptions(optparse.OptionGroup):
+ """Command line option for printing Samba version."""
+ def __init__(self, parser):
+ optparse.OptionGroup.__init__(self, parser, "Version Options")
+
+
+class CredentialsOptions(optparse.OptionGroup):
+ """Command line options for specifying credentials."""
+ def __init__(self, parser):
+ self.no_pass = False
+ optparse.OptionGroup.__init__(self, parser, "Credentials Options")
+ self.add_option("--simple-bind-dn", metavar="DN", action="callback",
+ callback=self._set_simple_bind_dn, type=str,
+ help="DN to use for a simple bind")
+ self.add_option("--password", metavar="PASSWORD", action="callback",
+ help="Password", type=str, callback=self._set_password)
+ self.add_option("-U", "--username", metavar="USERNAME",
+ action="callback", type=str,
+ help="Username", callback=self._parse_username)
+ self.add_option("-W", "--workgroup", metavar="WORKGROUP",
+ action="callback", type=str,
+ help="Workgroup", callback=self._parse_workgroup)
+ self.add_option("-N", "--no-pass", action="store_true",
+ help="Don't ask for a password")
+ self.add_option("-k", "--kerberos", metavar="KERBEROS",
+ action="callback", type=str,
+ help="Use Kerberos", callback=self._set_kerberos)
+ self.creds = Credentials()
+
+ def _parse_username(self, option, opt_str, arg, parser):
+ self.creds.parse_string(arg)
+
+ def _parse_workgroup(self, option, opt_str, arg, parser):
+ self.creds.set_domain(arg)
+
+ def _set_password(self, option, opt_str, arg, parser):
+ self.creds.set_password(arg)
+
+ def _set_kerberos(self, option, opt_str, arg, parser):
+ if bool(arg) or arg.lower() == "yes":
+ self.creds.set_kerberos_state(MUST_USE_KERBEROS)
+ else:
+ self.creds.set_kerberos_state(DONT_USE_KERBEROS)
+
+ def _set_simple_bind_dn(self, option, opt_str, arg, parser):
+ self.creds.set_bind_dn(arg)
+
+ def get_credentials(self, lp):
+ """Obtain the credentials set on the command-line.
+
+ :param lp: Loadparm object to use.
+ :return: Credentials object
+ """
+ self.creds.guess(lp)
+ if not self.no_pass:
+ self.creds.set_cmdline_callbacks()
+ return self.creds
diff --git a/source4/scripting/python/samba/hostconfig.py b/source4/scripting/python/samba/hostconfig.py
new file mode 100644
index 0000000000..313e3420b0
--- /dev/null
+++ b/source4/scripting/python/samba/hostconfig.py
@@ -0,0 +1,33 @@
+#!/usr/bin/python
+
+# Unix SMB/CIFS implementation.
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 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 <http://www.gnu.org/licenses/>.
+#
+
+from samdb import SamDB
+
+class Hostconfig(object):
+ """Aggregate object that contains all information about the configuration
+ of a Samba host."""
+
+ def __init__(self, lp):
+ self.lp = lp
+
+ def get_samdb(self, session_info, credentials):
+ return SamDB(url=self.lp.get("sam database"),
+ session_info=session_info, credentials=credentials,
+ lp=self.lp)
+
diff --git a/source4/scripting/python/samba/idmap.py b/source4/scripting/python/samba/idmap.py
new file mode 100644
index 0000000000..f8eeb18925
--- /dev/null
+++ b/source4/scripting/python/samba/idmap.py
@@ -0,0 +1,82 @@
+#!/usr/bin/python
+
+# Unix SMB/CIFS implementation.
+# Copyright (C) 2008 Kai Blin <kai@samba.org>
+#
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+"""Convenience functions for using the idmap database."""
+
+__docformat__ = "restructuredText"
+
+import samba
+import glue
+import ldb
+
+class IDmapDB(samba.Ldb):
+ """The IDmap database."""
+
+ # Mappings for ID_TYPE_UID, ID_TYPE_GID and ID_TYPE_BOTH
+ TYPE_UID = 1
+ TYPE_GID = 2
+ TYPE_BOTH = 3
+
+ def __init__(self, url=None, session_info=None, credentials=None,
+ modules_dir=None, lp=None):
+ """Open the IDmap Database.
+
+ :param url: URL of the database.
+ """
+ self.lp = lp
+
+ super(IDmapDB, self).__init__(session_info=session_info, credentials=credentials,
+ modules_dir=modules_dir, lp=lp)
+ if url:
+ self.connect(url)
+ else:
+ self.connect(lp.get("idmap database"))
+
+ def connect(self, url):
+ super(IDmapDB, self).connect(self.lp.private_path(url))
+
+ def setup_name_mapping(self, sid, type, unixid):
+ """Setup a mapping between a sam name and a unix name.
+
+ :param sid: SID of the NT-side of the mapping.
+ :param unixname: Unix name to map to.
+ """
+ type_string = ""
+ if type == self.TYPE_UID:
+ type_string = "ID_TYPE_UID"
+ elif type == self.TYPE_GID:
+ type_string = "ID_TYPE_GID"
+ elif type == self.TYPE_BOTH:
+ type_string = "ID_TYPE_BOTH"
+ else:
+ return
+
+ mod = """
+dn: CN=%s
+xidNumber: %s
+objectSid: %s
+objectClass: sidMap
+type: %s
+cn: %s
+
+""" % (sid, unixid, sid, type_string, sid)
+ self.add(self.parse_ldif(mod).next()[1])
+
+
diff --git a/source4/scripting/python/samba/ndr.py b/source4/scripting/python/samba/ndr.py
new file mode 100644
index 0000000000..e718ff3422
--- /dev/null
+++ b/source4/scripting/python/samba/ndr.py
@@ -0,0 +1,28 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Unix SMB/CIFS implementation.
+# Copyright © Jelmer Vernooij <jelmer@samba.org> 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 <http://www.gnu.org/licenses/>.
+#
+
+def ndr_pack(object):
+ return object.__ndr_pack__()
+
+
+def ndr_unpack(cls, data):
+ object = cls()
+ object.__ndr_unpack__(data)
+ return object
diff --git a/source4/scripting/python/samba/provision.py b/source4/scripting/python/samba/provision.py
new file mode 100644
index 0000000000..d96857661e
--- /dev/null
+++ b/source4/scripting/python/samba/provision.py
@@ -0,0 +1,1682 @@
+#
+# Unix SMB/CIFS implementation.
+# backend code for provisioning a Samba4 server
+
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008
+# Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008
+# Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
+#
+# Based on the original in EJS:
+# Copyright (C) Andrew Tridgell <tridge@samba.org> 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 <http://www.gnu.org/licenses/>.
+#
+
+"""Functions for setting up a Samba configuration."""
+
+from base64 import b64encode
+import os
+import sys
+import pwd
+import grp
+import time
+import uuid, glue
+import socket
+import param
+import registry
+import samba
+from auth import system_session
+from samba import Ldb, substitute_var, valid_netbios_name, check_all_substituted
+from samba.samdb import SamDB
+from samba.idmap import IDmapDB
+from samba.dcerpc import security
+import urllib
+from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError, \
+ timestring, CHANGETYPE_MODIFY, CHANGETYPE_NONE
+
+__docformat__ = "restructuredText"
+
+
+def find_setup_dir():
+ """Find the setup directory used by provision."""
+ dirname = os.path.dirname(__file__)
+ if "/site-packages/" in dirname:
+ prefix = "/".join(dirname[:dirname.index("/site-packages/")].split("/")[:-2])
+ for suffix in ["share/setup", "share/samba/setup", "setup"]:
+ ret = os.path.join(prefix, suffix)
+ if os.path.isdir(ret):
+ return ret
+ # In source tree
+ ret = os.path.join(dirname, "../../../setup")
+ if os.path.isdir(ret):
+ return ret
+ raise Exception("Unable to find setup directory.")
+
+
+DEFAULTSITE = "Default-First-Site-Name"
+
+class InvalidNetbiosName(Exception):
+ """A specified name was not a valid NetBIOS name."""
+ def __init__(self, name):
+ super(InvalidNetbiosName, self).__init__("The name '%r' is not a valid NetBIOS name" % name)
+
+
+class ProvisionPaths(object):
+ def __init__(self):
+ self.shareconf = None
+ self.hklm = None
+ self.hkcu = None
+ self.hkcr = None
+ self.hku = None
+ self.hkpd = None
+ self.hkpt = None
+ self.samdb = None
+ self.idmapdb = None
+ self.secrets = None
+ self.keytab = None
+ self.dns_keytab = None
+ self.dns = None
+ self.winsdb = None
+ self.private_dir = None
+ self.ldapdir = None
+ self.slapdconf = None
+ self.modulesconf = None
+ self.memberofconf = None
+ self.fedoradsinf = None
+ self.fedoradspartitions = None
+ self.olmmron = None
+ self.olmmrserveridsconf = None
+ self.olmmrsyncreplconf = None
+ self.olcdir = None
+ self.olslaptest = None
+ self.olcseedldif = None
+
+
+class ProvisionNames(object):
+ def __init__(self):
+ self.rootdn = None
+ self.domaindn = None
+ self.configdn = None
+ self.schemadn = None
+ self.ldapmanagerdn = None
+ self.dnsdomain = None
+ self.realm = None
+ self.netbiosname = None
+ self.domain = None
+ self.hostname = None
+ self.sitename = None
+ self.smbconf = None
+
+
+class ProvisionResult(object):
+ def __init__(self):
+ self.paths = None
+ self.domaindn = None
+ self.lp = None
+ self.samdb = None
+
+def check_install(lp, session_info, credentials):
+ """Check whether the current install seems ok.
+
+ :param lp: Loadparm context
+ :param session_info: Session information
+ :param credentials: Credentials
+ """
+ if lp.get("realm") == "":
+ raise Exception("Realm empty")
+ ldb = Ldb(lp.get("sam database"), session_info=session_info,
+ credentials=credentials, lp=lp)
+ if len(ldb.search("(cn=Administrator)")) != 1:
+ raise "No administrator account found"
+
+
+def findnss(nssfn, names):
+ """Find a user or group from a list of possibilities.
+
+ :param nssfn: NSS Function to try (should raise KeyError if not found)
+ :param names: Names to check.
+ :return: Value return by first names list.
+ """
+ for name in names:
+ try:
+ return nssfn(name)
+ except KeyError:
+ pass
+ raise KeyError("Unable to find user/group %r" % names)
+
+
+findnss_uid = lambda names: findnss(pwd.getpwnam, names)[2]
+findnss_gid = lambda names: findnss(grp.getgrnam, names)[2]
+
+
+def read_and_sub_file(file, subst_vars):
+ """Read a file and sub in variables found in it
+
+ :param file: File to be read (typically from setup directory)
+ param subst_vars: Optional variables to subsitute in the file.
+ """
+ data = open(file, 'r').read()
+ if subst_vars is not None:
+ data = substitute_var(data, subst_vars)
+ check_all_substituted(data)
+ return data
+
+
+def setup_add_ldif(ldb, ldif_path, subst_vars=None):
+ """Setup a ldb in the private dir.
+
+ :param ldb: LDB file to import data into
+ :param ldif_path: Path of the LDIF file to load
+ :param subst_vars: Optional variables to subsitute in LDIF.
+ """
+ assert isinstance(ldif_path, str)
+
+ data = read_and_sub_file(ldif_path, subst_vars)
+ ldb.add_ldif(data)
+
+
+def setup_modify_ldif(ldb, ldif_path, subst_vars=None):
+ """Modify a ldb in the private dir.
+
+ :param ldb: LDB object.
+ :param ldif_path: LDIF file path.
+ :param subst_vars: Optional dictionary with substitution variables.
+ """
+ data = read_and_sub_file(ldif_path, subst_vars)
+
+ ldb.modify_ldif(data)
+
+
+def setup_ldb(ldb, ldif_path, subst_vars):
+ """Import a LDIF a file into a LDB handle, optionally substituting variables.
+
+ :note: Either all LDIF data will be added or none (using transactions).
+
+ :param ldb: LDB file to import into.
+ :param ldif_path: Path to the LDIF file.
+ :param subst_vars: Dictionary with substitution variables.
+ """
+ assert ldb is not None
+ ldb.transaction_start()
+ try:
+ setup_add_ldif(ldb, ldif_path, subst_vars)
+ except:
+ ldb.transaction_cancel()
+ raise
+ ldb.transaction_commit()
+
+
+def setup_file(template, fname, subst_vars):
+ """Setup a file in the private dir.
+
+ :param template: Path of the template file.
+ :param fname: Path of the file to create.
+ :param subst_vars: Substitution variables.
+ """
+ f = fname
+
+ if os.path.exists(f):
+ os.unlink(f)
+
+ data = read_and_sub_file(template, subst_vars)
+ open(f, 'w').write(data)
+
+
+def provision_paths_from_lp(lp, dnsdomain):
+ """Set the default paths for provisioning.
+
+ :param lp: Loadparm context.
+ :param dnsdomain: DNS Domain name
+ """
+ paths = ProvisionPaths()
+ paths.private_dir = lp.get("private dir")
+ paths.keytab = "secrets.keytab"
+ paths.dns_keytab = "dns.keytab"
+
+ paths.shareconf = os.path.join(paths.private_dir, "share.ldb")
+ paths.samdb = os.path.join(paths.private_dir, lp.get("sam database") or "samdb.ldb")
+ paths.idmapdb = os.path.join(paths.private_dir, lp.get("idmap database") or "idmap.ldb")
+ paths.secrets = os.path.join(paths.private_dir, lp.get("secrets database") or "secrets.ldb")
+ paths.templates = os.path.join(paths.private_dir, "templates.ldb")
+ paths.dns = os.path.join(paths.private_dir, dnsdomain + ".zone")
+ paths.namedconf = os.path.join(paths.private_dir, "named.conf")
+ paths.namedtxt = os.path.join(paths.private_dir, "named.txt")
+ paths.krb5conf = os.path.join(paths.private_dir, "krb5.conf")
+ paths.winsdb = os.path.join(paths.private_dir, "wins.ldb")
+ paths.s4_ldapi_path = os.path.join(paths.private_dir, "ldapi")
+ paths.phpldapadminconfig = os.path.join(paths.private_dir,
+ "phpldapadmin-config.php")
+ paths.ldapdir = os.path.join(paths.private_dir,
+ "ldap")
+ paths.slapdconf = os.path.join(paths.ldapdir,
+ "slapd.conf")
+ paths.modulesconf = os.path.join(paths.ldapdir,
+ "modules.conf")
+ paths.memberofconf = os.path.join(paths.ldapdir,
+ "memberof.conf")
+ paths.fedoradsinf = os.path.join(paths.ldapdir,
+ "fedorads.inf")
+ paths.fedoradspartitions = os.path.join(paths.ldapdir,
+ "fedorads-partitions.ldif")
+ paths.olmmrserveridsconf = os.path.join(paths.ldapdir,
+ "mmr_serverids.conf")
+ paths.olmmrsyncreplconf = os.path.join(paths.ldapdir,
+ "mmr_syncrepl.conf")
+ paths.olcdir = os.path.join(paths.ldapdir,
+ "slapd.d")
+ paths.olcseedldif = os.path.join(paths.ldapdir,
+ "olc_seed.ldif")
+ paths.hklm = "hklm.ldb"
+ paths.hkcr = "hkcr.ldb"
+ paths.hkcu = "hkcu.ldb"
+ paths.hku = "hku.ldb"
+ paths.hkpd = "hkpd.ldb"
+ paths.hkpt = "hkpt.ldb"
+
+ paths.sysvol = lp.get("path", "sysvol")
+
+ paths.netlogon = lp.get("path", "netlogon")
+
+ paths.smbconf = lp.configfile
+
+ return paths
+
+
+def guess_names(lp=None, hostname=None, domain=None, dnsdomain=None, serverrole=None,
+ rootdn=None, domaindn=None, configdn=None, schemadn=None, serverdn=None,
+ sitename=None):
+ """Guess configuration settings to use."""
+
+ if hostname is None:
+ hostname = socket.gethostname().split(".")[0].lower()
+
+ netbiosname = hostname.upper()
+ if not valid_netbios_name(netbiosname):
+ raise InvalidNetbiosName(netbiosname)
+
+ hostname = hostname.lower()
+
+ if dnsdomain is None:
+ dnsdomain = lp.get("realm")
+
+ if serverrole is None:
+ serverrole = lp.get("server role")
+
+ assert dnsdomain is not None
+ realm = dnsdomain.upper()
+
+ if lp.get("realm").upper() != realm:
+ raise Exception("realm '%s' in %s must match chosen realm '%s'" %
+ (lp.get("realm"), lp.configfile, realm))
+
+ dnsdomain = dnsdomain.lower()
+
+ if serverrole == "domain controller":
+ if domain is None:
+ domain = lp.get("workgroup")
+ if domaindn is None:
+ domaindn = "DC=" + dnsdomain.replace(".", ",DC=")
+ if lp.get("workgroup").upper() != domain.upper():
+ raise Exception("workgroup '%s' in smb.conf must match chosen domain '%s'",
+ lp.get("workgroup"), domain)
+ else:
+ domain = netbiosname
+ if domaindn is None:
+ domaindn = "CN=" + netbiosname
+
+ assert domain is not None
+ domain = domain.upper()
+ if not valid_netbios_name(domain):
+ raise InvalidNetbiosName(domain)
+
+ if rootdn is None:
+ rootdn = domaindn
+
+ if configdn is None:
+ configdn = "CN=Configuration," + rootdn
+ if schemadn is None:
+ schemadn = "CN=Schema," + configdn
+
+ if sitename is None:
+ sitename=DEFAULTSITE
+
+ names = ProvisionNames()
+ names.rootdn = rootdn
+ names.domaindn = domaindn
+ names.configdn = configdn
+ names.schemadn = schemadn
+ names.ldapmanagerdn = "CN=Manager," + rootdn
+ names.dnsdomain = dnsdomain
+ names.domain = domain
+ names.realm = realm
+ names.netbiosname = netbiosname
+ names.hostname = hostname
+ names.sitename = sitename
+ names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
+
+ return names
+
+
+def make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
+ targetdir):
+ """Create a new smb.conf file based on a couple of basic settings.
+ """
+ assert smbconf is not None
+ if hostname is None:
+ hostname = socket.gethostname().split(".")[0].lower()
+
+ if serverrole is None:
+ serverrole = "standalone"
+
+ assert serverrole in ("domain controller", "member server", "standalone")
+ if serverrole == "domain controller":
+ smbconfsuffix = "dc"
+ elif serverrole == "member server":
+ smbconfsuffix = "member"
+ elif serverrole == "standalone":
+ smbconfsuffix = "standalone"
+
+ assert domain is not None
+ assert realm is not None
+
+ default_lp = param.LoadParm()
+ #Load non-existant file
+ if os.path.exists(smbconf):
+ default_lp.load(smbconf)
+
+ if targetdir is not None:
+ privatedir_line = "private dir = " + os.path.abspath(os.path.join(targetdir, "private"))
+ lockdir_line = "lock dir = " + os.path.abspath(targetdir)
+
+ default_lp.set("lock dir", os.path.abspath(targetdir))
+ else:
+ privatedir_line = ""
+ lockdir_line = ""
+
+ sysvol = os.path.join(default_lp.get("lock dir"), "sysvol")
+ netlogon = os.path.join(sysvol, realm.lower(), "scripts")
+
+ setup_file(setup_path("provision.smb.conf.%s" % smbconfsuffix),
+ smbconf, {
+ "HOSTNAME": hostname,
+ "DOMAIN": domain,
+ "REALM": realm,
+ "SERVERROLE": serverrole,
+ "NETLOGONPATH": netlogon,
+ "SYSVOLPATH": sysvol,
+ "PRIVATEDIR_LINE": privatedir_line,
+ "LOCKDIR_LINE": lockdir_line
+ })
+
+
+def setup_name_mappings(samdb, idmap, sid, domaindn, root_uid, nobody_uid,
+ users_gid, wheel_gid):
+ """setup reasonable name mappings for sam names to unix names.
+
+ :param samdb: SamDB object.
+ :param idmap: IDmap db object.
+ :param sid: The domain sid.
+ :param domaindn: The domain DN.
+ :param root_uid: uid of the UNIX root user.
+ :param nobody_uid: uid of the UNIX nobody user.
+ :param users_gid: gid of the UNIX users group.
+ :param wheel_gid: gid of the UNIX wheel group."""
+ # add some foreign sids if they are not present already
+ samdb.add_stock_foreign_sids()
+
+ idmap.setup_name_mapping("S-1-5-7", idmap.TYPE_UID, nobody_uid)
+ idmap.setup_name_mapping("S-1-5-32-544", idmap.TYPE_GID, wheel_gid)
+
+ idmap.setup_name_mapping(sid + "-500", idmap.TYPE_UID, root_uid)
+ idmap.setup_name_mapping(sid + "-513", idmap.TYPE_GID, users_gid)
+
+
+def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
+ credentials, names,
+ serverrole, ldap_backend=None,
+ ldap_backend_type=None, erase=False):
+ """Setup the partitions for the SAM database.
+
+ Alternatively, provision() may call this, and then populate the database.
+
+ :note: This will wipe the Sam Database!
+
+ :note: This function always removes the local SAM LDB file. The erase
+ parameter controls whether to erase the existing data, which
+ may not be stored locally but in LDAP.
+ """
+ assert session_info is not None
+
+ try:
+ samdb = SamDB(samdb_path, session_info=session_info,
+ credentials=credentials, lp=lp)
+ # Wipes the database
+ samdb.erase()
+ except LdbError:
+ os.unlink(samdb_path)
+ samdb = SamDB(samdb_path, session_info=session_info,
+ credentials=credentials, lp=lp)
+ # Wipes the database
+ samdb.erase()
+
+
+ #Add modules to the list to activate them by default
+ #beware often order is important
+ #
+ # Some Known ordering constraints:
+ # - rootdse must be first, as it makes redirects from "" -> cn=rootdse
+ # - objectclass must be before password_hash, because password_hash checks
+ # that the objectclass is of type person (filled in by objectclass
+ # module when expanding the objectclass list)
+ # - partition must be last
+ # - each partition has its own module list then
+ modules_list = ["rootdse",
+ "paged_results",
+ "ranged_results",
+ "anr",
+ "server_sort",
+ "asq",
+ "extended_dn_store",
+ "extended_dn_in",
+ "rdn_name",
+ "objectclass",
+ "samldb",
+ "kludge_acl",
+ "password_hash",
+ "operational"]
+ tdb_modules_list = [
+ "subtree_rename",
+ "subtree_delete",
+ "linked_attributes",
+ "extended_dn_out_ldb"]
+ modules_list2 = ["show_deleted",
+ "partition"]
+
+ domaindn_ldb = "users.ldb"
+ if ldap_backend is not None:
+ domaindn_ldb = ldap_backend
+ configdn_ldb = "configuration.ldb"
+ if ldap_backend is not None:
+ configdn_ldb = ldap_backend
+ schemadn_ldb = "schema.ldb"
+ if ldap_backend is not None:
+ schema_ldb = ldap_backend
+ schemadn_ldb = ldap_backend
+
+ if ldap_backend_type == "fedora-ds":
+ backend_modules = ["nsuniqueid", "paged_searches"]
+ # We can handle linked attributes here, as we don't have directory-side subtree operations
+ tdb_modules_list = ["linked_attributes", "extended_dn_out_dereference"]
+ elif ldap_backend_type == "openldap":
+ backend_modules = ["entryuuid", "paged_searches"]
+ # OpenLDAP handles subtree renames, so we don't want to do any of these things
+ tdb_modules_list = ["extended_dn_out_dereference"]
+ elif ldap_backend is not None:
+ raise "LDAP Backend specified, but LDAP Backend Type not specified"
+ elif serverrole == "domain controller":
+ backend_modules = ["repl_meta_data"]
+ else:
+ backend_modules = ["objectguid"]
+
+ if tdb_modules_list is None:
+ tdb_modules_list_as_string = ""
+ else:
+ tdb_modules_list_as_string = ","+",".join(tdb_modules_list)
+
+ samdb.transaction_start()
+ try:
+ setup_add_ldif(samdb, setup_path("provision_partitions.ldif"), {
+ "SCHEMADN": names.schemadn,
+ "SCHEMADN_LDB": schemadn_ldb,
+ "SCHEMADN_MOD2": ",objectguid",
+ "CONFIGDN": names.configdn,
+ "CONFIGDN_LDB": configdn_ldb,
+ "DOMAINDN": names.domaindn,
+ "DOMAINDN_LDB": domaindn_ldb,
+ "SCHEMADN_MOD": "schema_fsmo,instancetype",
+ "CONFIGDN_MOD": "naming_fsmo,instancetype",
+ "DOMAINDN_MOD": "pdc_fsmo,instancetype",
+ "MODULES_LIST": ",".join(modules_list),
+ "TDB_MODULES_LIST": tdb_modules_list_as_string,
+ "MODULES_LIST2": ",".join(modules_list2),
+ "BACKEND_MOD": ",".join(backend_modules),
+ })
+
+ except:
+ samdb.transaction_cancel()
+ raise
+
+ samdb.transaction_commit()
+
+ samdb = SamDB(samdb_path, session_info=session_info,
+ credentials=credentials, lp=lp)
+
+ samdb.transaction_start()
+ try:
+ message("Setting up sam.ldb attributes")
+ samdb.load_ldif_file_add(setup_path("provision_init.ldif"))
+
+ message("Setting up sam.ldb rootDSE")
+ setup_samdb_rootdse(samdb, setup_path, names)
+
+ if erase:
+ message("Erasing data from partitions")
+ samdb.erase_partitions()
+
+ except:
+ samdb.transaction_cancel()
+ raise
+
+ samdb.transaction_commit()
+
+ return samdb
+
+
+def secretsdb_become_dc(secretsdb, setup_path, domain, realm, dnsdomain,
+ netbiosname, domainsid, keytab_path, samdb_url,
+ dns_keytab_path, dnspass, machinepass):
+ """Add DC-specific bits to a secrets database.
+
+ :param secretsdb: Ldb Handle to the secrets database
+ :param setup_path: Setup path function
+ :param machinepass: Machine password
+ """
+ setup_ldb(secretsdb, setup_path("secrets_dc.ldif"), {
+ "MACHINEPASS_B64": b64encode(machinepass),
+ "DOMAIN": domain,
+ "REALM": realm,
+ "DNSDOMAIN": dnsdomain,
+ "DOMAINSID": str(domainsid),
+ "SECRETS_KEYTAB": keytab_path,
+ "NETBIOSNAME": netbiosname,
+ "SAM_LDB": samdb_url,
+ "DNS_KEYTAB": dns_keytab_path,
+ "DNSPASS_B64": b64encode(dnspass),
+ })
+
+
+def setup_secretsdb(path, setup_path, session_info, credentials, lp):
+ """Setup the secrets database.
+
+ :param path: Path to the secrets database.
+ :param setup_path: Get the path to a setup file.
+ :param session_info: Session info.
+ :param credentials: Credentials
+ :param lp: Loadparm context
+ :return: LDB handle for the created secrets database
+ """
+ if os.path.exists(path):
+ os.unlink(path)
+ secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
+ lp=lp)
+ secrets_ldb.erase()
+ secrets_ldb.load_ldif_file_add(setup_path("secrets_init.ldif"))
+ secrets_ldb = Ldb(path, session_info=session_info, credentials=credentials,
+ lp=lp)
+ secrets_ldb.load_ldif_file_add(setup_path("secrets.ldif"))
+
+ if credentials is not None and credentials.authentication_requested():
+ if credentials.get_bind_dn() is not None:
+ setup_add_ldif(secrets_ldb, setup_path("secrets_simple_ldap.ldif"), {
+ "LDAPMANAGERDN": credentials.get_bind_dn(),
+ "LDAPMANAGERPASS_B64": b64encode(credentials.get_password())
+ })
+ else:
+ setup_add_ldif(secrets_ldb, setup_path("secrets_sasl_ldap.ldif"), {
+ "LDAPADMINUSER": credentials.get_username(),
+ "LDAPADMINREALM": credentials.get_realm(),
+ "LDAPADMINPASS_B64": b64encode(credentials.get_password())
+ })
+
+ return secrets_ldb
+
+
+def setup_templatesdb(path, setup_path, session_info, credentials, lp):
+ """Setup the templates database.
+
+ :param path: Path to the database.
+ :param setup_path: Function for obtaining the path to setup files.
+ :param session_info: Session info
+ :param credentials: Credentials
+ :param lp: Loadparm context
+ """
+ templates_ldb = SamDB(path, session_info=session_info,
+ credentials=credentials, lp=lp)
+ # Wipes the database
+ try:
+ templates_ldb.erase()
+ # This should be 'except LdbError', but on a re-provision the assert in ldb.erase fires, and we need to catch that too
+ except:
+ os.unlink(path)
+
+ templates_ldb.load_ldif_file_add(setup_path("provision_templates_init.ldif"))
+
+ templates_ldb = SamDB(path, session_info=session_info,
+ credentials=credentials, lp=lp)
+
+ templates_ldb.load_ldif_file_add(setup_path("provision_templates.ldif"))
+
+
+def setup_registry(path, setup_path, session_info, credentials, lp):
+ """Setup the registry.
+
+ :param path: Path to the registry database
+ :param setup_path: Function that returns the path to a setup.
+ :param session_info: Session information
+ :param credentials: Credentials
+ :param lp: Loadparm context
+ """
+ reg = registry.Registry()
+ hive = registry.open_ldb(path, session_info=session_info,
+ credentials=credentials, lp_ctx=lp)
+ reg.mount_hive(hive, registry.HKEY_LOCAL_MACHINE)
+ provision_reg = setup_path("provision.reg")
+ assert os.path.exists(provision_reg)
+ reg.diff_apply(provision_reg)
+
+
+def setup_idmapdb(path, setup_path, session_info, credentials, lp):
+ """Setup the idmap database.
+
+ :param path: path to the idmap database
+ :param setup_path: Function that returns a path to a setup file
+ :param session_info: Session information
+ :param credentials: Credentials
+ :param lp: Loadparm context
+ """
+ if os.path.exists(path):
+ os.unlink(path)
+
+ idmap_ldb = IDmapDB(path, session_info=session_info,
+ credentials=credentials, lp=lp)
+
+ idmap_ldb.erase()
+ idmap_ldb.load_ldif_file_add(setup_path("idmap_init.ldif"))
+ return idmap_ldb
+
+
+def setup_samdb_rootdse(samdb, setup_path, names):
+ """Setup the SamDB rootdse.
+
+ :param samdb: Sam Database handle
+ :param setup_path: Obtain setup path
+ """
+ setup_add_ldif(samdb, setup_path("provision_rootdse_add.ldif"), {
+ "SCHEMADN": names.schemadn,
+ "NETBIOSNAME": names.netbiosname,
+ "DNSDOMAIN": names.dnsdomain,
+ "REALM": names.realm,
+ "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
+ "DOMAINDN": names.domaindn,
+ "ROOTDN": names.rootdn,
+ "CONFIGDN": names.configdn,
+ "SERVERDN": names.serverdn,
+ })
+
+
+def setup_self_join(samdb, names,
+ machinepass, dnspass,
+ domainsid, invocationid, setup_path,
+ policyguid):
+ """Join a host to its own domain."""
+ assert isinstance(invocationid, str)
+ setup_add_ldif(samdb, setup_path("provision_self_join.ldif"), {
+ "CONFIGDN": names.configdn,
+ "SCHEMADN": names.schemadn,
+ "DOMAINDN": names.domaindn,
+ "SERVERDN": names.serverdn,
+ "INVOCATIONID": invocationid,
+ "NETBIOSNAME": names.netbiosname,
+ "DEFAULTSITE": names.sitename,
+ "DNSNAME": "%s.%s" % (names.hostname, names.dnsdomain),
+ "MACHINEPASS_B64": b64encode(machinepass),
+ "DNSPASS_B64": b64encode(dnspass),
+ "REALM": names.realm,
+ "DOMAIN": names.domain,
+ "DNSDOMAIN": names.dnsdomain})
+ setup_add_ldif(samdb, setup_path("provision_group_policy.ldif"), {
+ "POLICYGUID": policyguid,
+ "DNSDOMAIN": names.dnsdomain,
+ "DOMAINSID": str(domainsid),
+ "DOMAINDN": names.domaindn})
+
+
+def setup_samdb(path, setup_path, session_info, credentials, lp,
+ names, message,
+ domainsid, aci, domainguid, policyguid,
+ fill, adminpass, krbtgtpass,
+ machinepass, invocationid, dnspass,
+ serverrole, ldap_backend=None,
+ ldap_backend_type=None):
+ """Setup a complete SAM Database.
+
+ :note: This will wipe the main SAM database file!
+ """
+
+ erase = (fill != FILL_DRS)
+
+ # Also wipes the database
+ setup_samdb_partitions(path, setup_path, message=message, lp=lp,
+ credentials=credentials, session_info=session_info,
+ names=names,
+ ldap_backend=ldap_backend, serverrole=serverrole,
+ ldap_backend_type=ldap_backend_type, erase=erase)
+
+ samdb = SamDB(path, session_info=session_info,
+ credentials=credentials, lp=lp)
+ if fill == FILL_DRS:
+ return samdb
+
+ message("Pre-loading the Samba 4 and AD schema")
+ samdb.set_domain_sid(str(domainsid))
+ if serverrole == "domain controller":
+ samdb.set_invocation_id(invocationid)
+
+ load_schema(setup_path, samdb, names.schemadn, names.netbiosname,
+ names.configdn, names.sitename, names.serverdn,
+ names.hostname)
+
+ samdb.transaction_start()
+
+ try:
+ message("Adding DomainDN: %s (permitted to fail)" % names.domaindn)
+ if serverrole == "domain controller":
+ domain_oc = "domainDNS"
+ else:
+ domain_oc = "samba4LocalDomain"
+
+ setup_add_ldif(samdb, setup_path("provision_basedn.ldif"), {
+ "DOMAINDN": names.domaindn,
+ "ACI": aci,
+ "DOMAIN_OC": domain_oc
+ })
+
+ message("Modifying DomainDN: " + names.domaindn + "")
+ if domainguid is not None:
+ domainguid_mod = "replace: objectGUID\nobjectGUID: %s\n-" % domainguid
+ else:
+ domainguid_mod = ""
+
+ setup_modify_ldif(samdb, setup_path("provision_basedn_modify.ldif"), {
+ "LDAPTIME": timestring(int(time.time())),
+ "DOMAINSID": str(domainsid),
+ "SCHEMADN": names.schemadn,
+ "NETBIOSNAME": names.netbiosname,
+ "DEFAULTSITE": names.sitename,
+ "CONFIGDN": names.configdn,
+ "SERVERDN": names.serverdn,
+ "POLICYGUID": policyguid,
+ "DOMAINDN": names.domaindn,
+ "DOMAINGUID_MOD": domainguid_mod,
+ })
+
+ message("Adding configuration container (permitted to fail)")
+ setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
+ "CONFIGDN": names.configdn,
+ "ACI": aci,
+ })
+ message("Modifying configuration container")
+ setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
+ "CONFIGDN": names.configdn,
+ "SCHEMADN": names.schemadn,
+ })
+
+ message("Adding schema container (permitted to fail)")
+ setup_add_ldif(samdb, setup_path("provision_schema_basedn.ldif"), {
+ "SCHEMADN": names.schemadn,
+ "ACI": aci,
+ })
+ message("Modifying schema container")
+
+ prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
+
+ setup_modify_ldif(samdb,
+ setup_path("provision_schema_basedn_modify.ldif"), {
+ "SCHEMADN": names.schemadn,
+ "NETBIOSNAME": names.netbiosname,
+ "DEFAULTSITE": names.sitename,
+ "CONFIGDN": names.configdn,
+ "SERVERDN": names.serverdn,
+ "PREFIXMAP_B64": b64encode(prefixmap)
+ })
+
+ message("Setting up sam.ldb Samba4 schema")
+ setup_add_ldif(samdb, setup_path("schema_samba4.ldif"),
+ {"SCHEMADN": names.schemadn })
+ message("Setting up sam.ldb AD schema")
+ setup_add_ldif(samdb, setup_path("schema.ldif"),
+ {"SCHEMADN": names.schemadn})
+ setup_add_ldif(samdb, setup_path("aggregate_schema.ldif"),
+ {"SCHEMADN": names.schemadn})
+
+ message("Setting up sam.ldb configuration data")
+ setup_add_ldif(samdb, setup_path("provision_configuration.ldif"), {
+ "CONFIGDN": names.configdn,
+ "NETBIOSNAME": names.netbiosname,
+ "DEFAULTSITE": names.sitename,
+ "DNSDOMAIN": names.dnsdomain,
+ "DOMAIN": names.domain,
+ "SCHEMADN": names.schemadn,
+ "DOMAINDN": names.domaindn,
+ "SERVERDN": names.serverdn
+ })
+
+ message("Setting up display specifiers")
+ setup_add_ldif(samdb, setup_path("display_specifiers.ldif"),
+ {"CONFIGDN": names.configdn})
+
+ message("Adding users container (permitted to fail)")
+ setup_add_ldif(samdb, setup_path("provision_users_add.ldif"), {
+ "DOMAINDN": names.domaindn})
+ message("Modifying users container")
+ setup_modify_ldif(samdb, setup_path("provision_users_modify.ldif"), {
+ "DOMAINDN": names.domaindn})
+ message("Adding computers container (permitted to fail)")
+ setup_add_ldif(samdb, setup_path("provision_computers_add.ldif"), {
+ "DOMAINDN": names.domaindn})
+ message("Modifying computers container")
+ setup_modify_ldif(samdb, setup_path("provision_computers_modify.ldif"), {
+ "DOMAINDN": names.domaindn})
+ message("Setting up sam.ldb data")
+ setup_add_ldif(samdb, setup_path("provision.ldif"), {
+ "DOMAINDN": names.domaindn,
+ "NETBIOSNAME": names.netbiosname,
+ "DEFAULTSITE": names.sitename,
+ "CONFIGDN": names.configdn,
+ "SERVERDN": names.serverdn
+ })
+
+ if fill == FILL_FULL:
+ message("Setting up sam.ldb users and groups")
+ setup_add_ldif(samdb, setup_path("provision_users.ldif"), {
+ "DOMAINDN": names.domaindn,
+ "DOMAINSID": str(domainsid),
+ "CONFIGDN": names.configdn,
+ "ADMINPASS_B64": b64encode(adminpass),
+ "KRBTGTPASS_B64": b64encode(krbtgtpass),
+ })
+
+ if serverrole == "domain controller":
+ message("Setting up self join")
+ setup_self_join(samdb, names=names, invocationid=invocationid,
+ dnspass=dnspass,
+ machinepass=machinepass,
+ domainsid=domainsid, policyguid=policyguid,
+ setup_path=setup_path)
+
+ except:
+ samdb.transaction_cancel()
+ raise
+
+ samdb.transaction_commit()
+ return samdb
+
+
+FILL_FULL = "FULL"
+FILL_NT4SYNC = "NT4SYNC"
+FILL_DRS = "DRS"
+
+def provision(setup_dir, message, session_info,
+ credentials, smbconf=None, targetdir=None, samdb_fill=FILL_FULL, realm=None,
+ rootdn=None, domaindn=None, schemadn=None, configdn=None,
+ serverdn=None,
+ domain=None, hostname=None, hostip=None, hostip6=None,
+ domainsid=None, adminpass=None, krbtgtpass=None, domainguid=None,
+ policyguid=None, invocationid=None, machinepass=None,
+ dnspass=None, root=None, nobody=None, nogroup=None, users=None,
+ wheel=None, backup=None, aci=None, serverrole=None,
+ ldap_backend=None, ldap_backend_type=None, sitename=None):
+ """Provision samba4
+
+ :note: caution, this wipes all existing data!
+ """
+
+ def setup_path(file):
+ return os.path.join(setup_dir, file)
+
+ if domainsid is None:
+ domainsid = security.random_sid()
+
+ if policyguid is None:
+ policyguid = str(uuid.uuid4())
+ if adminpass is None:
+ adminpass = glue.generate_random_str(12)
+ if krbtgtpass is None:
+ krbtgtpass = glue.generate_random_str(12)
+ if machinepass is None:
+ machinepass = glue.generate_random_str(12)
+ if dnspass is None:
+ dnspass = glue.generate_random_str(12)
+ root_uid = findnss_uid([root or "root"])
+ nobody_uid = findnss_uid([nobody or "nobody"])
+ users_gid = findnss_gid([users or "users"])
+ if wheel is None:
+ wheel_gid = findnss_gid(["wheel", "adm"])
+ else:
+ wheel_gid = findnss_gid([wheel])
+ if aci is None:
+ aci = "# no aci for local ldb"
+
+ if targetdir is not None:
+ if (not os.path.exists(os.path.join(targetdir, "etc"))):
+ os.makedirs(os.path.join(targetdir, "etc"))
+ smbconf = os.path.join(targetdir, "etc", "smb.conf")
+ elif smbconf is None:
+ smbconf = param.default_path()
+
+ # only install a new smb.conf if there isn't one there already
+ if not os.path.exists(smbconf):
+ make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
+ targetdir)
+
+ lp = param.LoadParm()
+ lp.load(smbconf)
+
+ names = guess_names(lp=lp, hostname=hostname, domain=domain,
+ dnsdomain=realm, serverrole=serverrole, sitename=sitename,
+ rootdn=rootdn, domaindn=domaindn, configdn=configdn, schemadn=schemadn,
+ serverdn=serverdn)
+
+ paths = provision_paths_from_lp(lp, names.dnsdomain)
+
+ if hostip is None:
+ try:
+ hostip = socket.getaddrinfo(names.hostname, None, socket.AF_INET, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
+ except socket.gaierror, (socket.EAI_NODATA, msg):
+ hostip = None
+
+ if hostip6 is None:
+ try:
+ hostip6 = socket.getaddrinfo(names.hostname, None, socket.AF_INET6, socket.AI_CANONNAME, socket.IPPROTO_IP)[0][-1][0]
+ except socket.gaierror, (socket.EAI_NODATA, msg):
+ hostip6 = None
+
+ if serverrole is None:
+ serverrole = lp.get("server role")
+
+ assert serverrole in ("domain controller", "member server", "standalone")
+ if invocationid is None and serverrole == "domain controller":
+ invocationid = str(uuid.uuid4())
+
+ if not os.path.exists(paths.private_dir):
+ os.mkdir(paths.private_dir)
+
+ ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
+
+ if ldap_backend is not None:
+ if ldap_backend == "ldapi":
+ # provision-backend will set this path suggested slapd command line / fedorads.inf
+ ldap_backend = "ldapi://%s" % urllib.quote(os.path.join(paths.private_dir, "ldap", "ldapi"), safe="")
+
+ # only install a new shares config db if there is none
+ if not os.path.exists(paths.shareconf):
+ message("Setting up share.ldb")
+ share_ldb = Ldb(paths.shareconf, session_info=session_info,
+ credentials=credentials, lp=lp)
+ share_ldb.load_ldif_file_add(setup_path("share.ldif"))
+
+
+ message("Setting up secrets.ldb")
+ secrets_ldb = setup_secretsdb(paths.secrets, setup_path,
+ session_info=session_info,
+ credentials=credentials, lp=lp)
+
+ message("Setting up the registry")
+ setup_registry(paths.hklm, setup_path, session_info,
+ credentials=credentials, lp=lp)
+
+ message("Setting up templates db")
+ setup_templatesdb(paths.templates, setup_path, session_info=session_info,
+ credentials=credentials, lp=lp)
+
+ message("Setting up idmap db")
+ idmap = setup_idmapdb(paths.idmapdb, setup_path, session_info=session_info,
+ credentials=credentials, lp=lp)
+
+ samdb = setup_samdb(paths.samdb, setup_path, session_info=session_info,
+ credentials=credentials, lp=lp, names=names,
+ message=message,
+ domainsid=domainsid,
+ aci=aci, domainguid=domainguid, policyguid=policyguid,
+ fill=samdb_fill,
+ adminpass=adminpass, krbtgtpass=krbtgtpass,
+ invocationid=invocationid,
+ machinepass=machinepass, dnspass=dnspass,
+ serverrole=serverrole, ldap_backend=ldap_backend,
+ ldap_backend_type=ldap_backend_type)
+
+ if lp.get("server role") == "domain controller":
+ if paths.netlogon is None:
+ message("Existing smb.conf does not have a [netlogon] share, but you are configuring a DC.")
+ message("Please either remove %s or see the template at %s" %
+ ( paths.smbconf, setup_path("provision.smb.conf.dc")))
+ assert(paths.netlogon is not None)
+
+ if paths.sysvol is None:
+ message("Existing smb.conf does not have a [sysvol] share, but you are configuring a DC.")
+ message("Please either remove %s or see the template at %s" %
+ (paths.smbconf, setup_path("provision.smb.conf.dc")))
+ assert(paths.sysvol is not None)
+
+ policy_path = os.path.join(paths.sysvol, names.dnsdomain, "Policies",
+ "{" + policyguid + "}")
+ os.makedirs(policy_path, 0755)
+ open(os.path.join(policy_path, "GPT.INI"), 'w').write("")
+ os.makedirs(os.path.join(policy_path, "Machine"), 0755)
+ os.makedirs(os.path.join(policy_path, "User"), 0755)
+ if not os.path.isdir(paths.netlogon):
+ os.makedirs(paths.netlogon, 0755)
+
+ if samdb_fill == FILL_FULL:
+ setup_name_mappings(samdb, idmap, str(domainsid), names.domaindn,
+ root_uid=root_uid, nobody_uid=nobody_uid,
+ users_gid=users_gid, wheel_gid=wheel_gid)
+
+ message("Setting up sam.ldb rootDSE marking as synchronized")
+ setup_modify_ldif(samdb, setup_path("provision_rootdse_modify.ldif"))
+
+ # Only make a zone file on the first DC, it should be replicated with DNS replication
+ if serverrole == "domain controller":
+ secrets_ldb = Ldb(paths.secrets, session_info=session_info,
+ credentials=credentials, lp=lp)
+ secretsdb_become_dc(secrets_ldb, setup_path, domain=domain, realm=names.realm,
+ netbiosname=names.netbiosname, domainsid=domainsid,
+ keytab_path=paths.keytab, samdb_url=paths.samdb,
+ dns_keytab_path=paths.dns_keytab, dnspass=dnspass,
+ machinepass=machinepass, dnsdomain=names.dnsdomain)
+
+ samdb = SamDB(paths.samdb, session_info=session_info,
+ credentials=credentials, lp=lp)
+
+ domainguid = samdb.searchone(basedn=domaindn, attribute="objectGUID")
+ assert isinstance(domainguid, str)
+ hostguid = samdb.searchone(basedn=domaindn, attribute="objectGUID",
+ expression="(&(objectClass=computer)(cn=%s))" % names.hostname,
+ scope=SCOPE_SUBTREE)
+ assert isinstance(hostguid, str)
+
+ create_zone_file(paths.dns, setup_path, dnsdomain=names.dnsdomain,
+ domaindn=names.domaindn, hostip=hostip,
+ hostip6=hostip6, hostname=names.hostname,
+ dnspass=dnspass, realm=names.realm,
+ domainguid=domainguid, hostguid=hostguid)
+
+ create_named_conf(paths.namedconf, setup_path, realm=names.realm,
+ dnsdomain=names.dnsdomain, private_dir=paths.private_dir)
+
+ create_named_txt(paths.namedtxt, setup_path, realm=names.realm,
+ dnsdomain=names.dnsdomain, private_dir=paths.private_dir,
+ keytab_name=paths.dns_keytab)
+ message("See %s for an example configuration include file for BIND" % paths.namedconf)
+ message("and %s for further documentation required for secure DNS updates" % paths.namedtxt)
+
+ create_krb5_conf(paths.krb5conf, setup_path, dnsdomain=names.dnsdomain,
+ hostname=names.hostname, realm=names.realm)
+ message("A Kerberos configuration suitable for Samba 4 has been generated at %s" % paths.krb5conf)
+
+ create_phpldapadmin_config(paths.phpldapadminconfig, setup_path,
+ ldapi_url)
+
+ message("Please install the phpLDAPadmin configuration located at %s into /etc/phpldapadmin/config.php" % paths.phpldapadminconfig)
+
+ message("Once the above files are installed, your Samba4 server will be ready to use")
+ message("Server Role: %s" % serverrole)
+ message("Hostname: %s" % names.hostname)
+ message("NetBIOS Domain: %s" % names.domain)
+ message("DNS Domain: %s" % names.dnsdomain)
+ message("DOMAIN SID: %s" % str(domainsid))
+ if samdb_fill == FILL_FULL:
+ message("Admin password: %s" % adminpass)
+
+ result = ProvisionResult()
+ result.domaindn = domaindn
+ result.paths = paths
+ result.lp = lp
+ result.samdb = samdb
+ return result
+
+
+def provision_become_dc(setup_dir=None,
+ smbconf=None, targetdir=None, realm=None,
+ rootdn=None, domaindn=None, schemadn=None, configdn=None,
+ serverdn=None,
+ domain=None, hostname=None, domainsid=None,
+ adminpass=None, krbtgtpass=None, domainguid=None,
+ policyguid=None, invocationid=None, machinepass=None,
+ dnspass=None, root=None, nobody=None, nogroup=None, users=None,
+ wheel=None, backup=None, aci=None, serverrole=None,
+ ldap_backend=None, ldap_backend_type=None, sitename=None):
+
+ def message(text):
+ """print a message if quiet is not set."""
+ print text
+
+ return provision(setup_dir, message, system_session(), None,
+ smbconf=smbconf, targetdir=targetdir, samdb_fill=FILL_DRS, realm=realm,
+ rootdn=rootdn, domaindn=domaindn, schemadn=schemadn, configdn=configdn, serverdn=serverdn,
+ domain=domain, hostname=hostname, hostip="127.0.0.1", domainsid=domainsid, machinepass=machinepass, serverrole="domain controller", sitename=sitename)
+
+
+def setup_db_config(setup_path, dbdir):
+ """Setup a Berkeley database.
+
+ :param setup_path: Setup path function.
+ :param dbdir: Database directory."""
+ if not os.path.isdir(os.path.join(dbdir, "bdb-logs")):
+ os.makedirs(os.path.join(dbdir, "bdb-logs"), 0700)
+ if not os.path.isdir(os.path.join(dbdir, "tmp")):
+ os.makedirs(os.path.join(dbdir, "tmp"), 0700)
+
+ setup_file(setup_path("DB_CONFIG"), os.path.join(dbdir, "DB_CONFIG"),
+ {"LDAPDBDIR": dbdir})
+
+
+
+def provision_backend(setup_dir=None, message=None,
+ smbconf=None, targetdir=None, realm=None,
+ rootdn=None, domaindn=None, schemadn=None, configdn=None,
+ domain=None, hostname=None, adminpass=None, root=None, serverrole=None,
+ ldap_backend_type=None, ldap_backend_port=None,
+ ol_mmr_urls=None,ol_olc=None,ol_slaptest=None):
+
+ def setup_path(file):
+ return os.path.join(setup_dir, file)
+
+ if hostname is None:
+ hostname = socket.gethostname().split(".")[0].lower()
+
+ if root is None:
+ root = findnss(pwd.getpwnam, ["root"])[0]
+
+ if adminpass is None:
+ adminpass = glue.generate_random_str(12)
+
+ if targetdir is not None:
+ if (not os.path.exists(os.path.join(targetdir, "etc"))):
+ os.makedirs(os.path.join(targetdir, "etc"))
+ smbconf = os.path.join(targetdir, "etc", "smb.conf")
+ elif smbconf is None:
+ smbconf = param.default_path()
+ assert smbconf is not None
+
+ # only install a new smb.conf if there isn't one there already
+ if not os.path.exists(smbconf):
+ make_smbconf(smbconf, setup_path, hostname, domain, realm, serverrole,
+ targetdir)
+
+ # openldap-online-configuration: validation of olc and slaptest
+ if ol_olc == "yes" and ol_slaptest is None:
+ sys.exit("Warning: OpenLDAP-Online-Configuration cant be setup without path to slaptest-Binary!")
+
+ if ol_olc == "yes" and ol_slaptest is not None:
+ ol_slaptest = ol_slaptest + "/slaptest"
+ if not os.path.exists(ol_slaptest):
+ message (ol_slaptest)
+ sys.exit("Warning: Given Path to slaptest-Binary does not exist!")
+ ###
+
+
+
+ lp = param.LoadParm()
+ lp.load(smbconf)
+
+ if serverrole is None:
+ serverrole = lp.get("server role")
+
+ names = guess_names(lp=lp, hostname=hostname, domain=domain,
+ dnsdomain=realm, serverrole=serverrole,
+ rootdn=rootdn, domaindn=domaindn, configdn=configdn,
+ schemadn=schemadn)
+
+ paths = provision_paths_from_lp(lp, names.dnsdomain)
+
+ if not os.path.isdir(paths.ldapdir):
+ os.makedirs(paths.ldapdir, 0700)
+ schemadb_path = os.path.join(paths.ldapdir, "schema-tmp.ldb")
+ try:
+ os.unlink(schemadb_path)
+ except OSError:
+ pass
+
+ schemadb = Ldb(schemadb_path, lp=lp)
+
+ prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
+
+ setup_add_ldif(schemadb, setup_path("provision_schema_basedn.ldif"),
+ {"SCHEMADN": names.schemadn,
+ "ACI": "#",
+ })
+ setup_modify_ldif(schemadb,
+ setup_path("provision_schema_basedn_modify.ldif"), \
+ {"SCHEMADN": names.schemadn,
+ "NETBIOSNAME": names.netbiosname,
+ "DEFAULTSITE": DEFAULTSITE,
+ "CONFIGDN": names.configdn,
+ "SERVERDN": names.serverdn,
+ "PREFIXMAP_B64": b64encode(prefixmap)
+ })
+
+ setup_add_ldif(schemadb, setup_path("schema_samba4.ldif"),
+ {"SCHEMADN": names.schemadn })
+ setup_add_ldif(schemadb, setup_path("schema.ldif"),
+ {"SCHEMADN": names.schemadn})
+
+ if ldap_backend_type == "fedora-ds":
+ if ldap_backend_port is not None:
+ serverport = "ServerPort=%d" % ldap_backend_port
+ else:
+ serverport = ""
+
+ setup_file(setup_path("fedorads.inf"), paths.fedoradsinf,
+ {"ROOT": root,
+ "HOSTNAME": hostname,
+ "DNSDOMAIN": names.dnsdomain,
+ "LDAPDIR": paths.ldapdir,
+ "DOMAINDN": names.domaindn,
+ "LDAPMANAGERDN": names.ldapmanagerdn,
+ "LDAPMANAGERPASS": adminpass,
+ "SERVERPORT": serverport})
+
+ setup_file(setup_path("fedorads-partitions.ldif"), paths.fedoradspartitions,
+ {"CONFIGDN": names.configdn,
+ "SCHEMADN": names.schemadn,
+ })
+
+ mapping = "schema-map-fedora-ds-1.0"
+ backend_schema = "99_ad.ldif"
+
+ slapdcommand="Initialise Fedora DS with: setup-ds.pl --file=%s" % paths.fedoradsinf
+
+ ldapuser = "--simple-bind-dn=" + names.ldapmanagerdn
+
+ elif ldap_backend_type == "openldap":
+ attrs = ["linkID", "lDAPDisplayName"]
+ res = schemadb.search(expression="(&(linkID=*)(!(linkID:1.2.840.113556.1.4.803:=1))(objectclass=attributeSchema)(attributeSyntax=2.5.5.1))", base=names.schemadn, scope=SCOPE_SUBTREE, attrs=attrs)
+
+ memberof_config = "# Generated from schema in %s\n" % schemadb_path
+ refint_attributes = ""
+ for i in range (0, len(res)):
+ expression = "(&(objectclass=attributeSchema)(linkID=%d)(attributeSyntax=2.5.5.1))" % (int(res[i]["linkID"][0])+1)
+ target = schemadb.searchone(basedn=names.schemadn,
+ expression=expression,
+ attribute="lDAPDisplayName",
+ scope=SCOPE_SUBTREE)
+ if target is not None:
+ refint_attributes = refint_attributes + " " + target + " " + res[i]["lDAPDisplayName"][0]
+
+ memberof_config += read_and_sub_file(setup_path("memberof.conf"),
+ { "MEMBER_ATTR" : str(res[i]["lDAPDisplayName"][0]),
+ "MEMBEROF_ATTR" : str(target) })
+
+ refint_config = read_and_sub_file(setup_path("refint.conf"),
+ { "LINK_ATTRS" : refint_attributes})
+
+# generate serverids, ldap-urls and syncrepl-blocks for mmr hosts
+ mmr_on_config = ""
+ mmr_replicator_acl = ""
+ mmr_serverids_config = ""
+ mmr_syncrepl_schema_config = ""
+ mmr_syncrepl_config_config = ""
+ mmr_syncrepl_user_config = ""
+
+
+ if ol_mmr_urls is not None:
+ # For now, make these equal
+ mmr_pass = adminpass
+
+ url_list=filter(None,ol_mmr_urls.split(' '))
+ if (len(url_list) == 1):
+ url_list=filter(None,ol_mmr_urls.split(','))
+
+
+ mmr_on_config = "MirrorMode On"
+ mmr_replicator_acl = " by dn=cn=replicator,cn=samba read"
+ serverid=0
+ for url in url_list:
+ serverid=serverid+1
+ mmr_serverids_config += read_and_sub_file(setup_path("mmr_serverids.conf"),
+ { "SERVERID" : str(serverid),
+ "LDAPSERVER" : url })
+ rid=serverid*10
+ rid=rid+1
+ mmr_syncrepl_schema_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
+ { "RID" : str(rid),
+ "MMRDN": names.schemadn,
+ "LDAPSERVER" : url,
+ "MMR_PASSWORD": mmr_pass})
+
+ rid=rid+1
+ mmr_syncrepl_config_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
+ { "RID" : str(rid),
+ "MMRDN": names.configdn,
+ "LDAPSERVER" : url,
+ "MMR_PASSWORD": mmr_pass})
+
+ rid=rid+1
+ mmr_syncrepl_user_config += read_and_sub_file(setup_path("mmr_syncrepl.conf"),
+ { "RID" : str(rid),
+ "MMRDN": names.domaindn,
+ "LDAPSERVER" : url,
+ "MMR_PASSWORD": mmr_pass })
+ # olc = yes?
+ olc_config_pass = ""
+ olc_config_acl = ""
+ olc_syncrepl_config = ""
+ olc_mmr_config = ""
+ if ol_olc == "yes":
+ olc_config_pass += read_and_sub_file(setup_path("olc_pass.conf"),
+ { "OLC_PW": adminpass })
+ olc_config_acl += read_and_sub_file(setup_path("olc_acl.conf"),{})
+
+ # if olc = yes + mmr = yes, generate cn=config-replication directives
+ # and olc_seed.lif for the other mmr-servers
+ if ol_olc == "yes" and ol_mmr_urls is not None:
+ serverid=0
+ olc_serverids_config = ""
+ olc_syncrepl_config = ""
+ olc_syncrepl_seed_config = ""
+ olc_mmr_config = ""
+ olc_mmr_config += read_and_sub_file(setup_path("olc_mmr.conf"),{})
+ rid=1000
+ for url in url_list:
+ serverid=serverid+1
+ olc_serverids_config += read_and_sub_file(setup_path("olc_serverid.conf"),
+ { "SERVERID" : str(serverid),
+ "LDAPSERVER" : url })
+
+ rid=rid+1
+ olc_syncrepl_config += read_and_sub_file(setup_path("olc_syncrepl.conf"),
+ { "RID" : str(rid),
+ "LDAPSERVER" : url,
+ "MMR_PASSWORD": adminpass})
+
+ olc_syncrepl_seed_config += read_and_sub_file(setup_path("olc_syncrepl_seed.conf"),
+ { "RID" : str(rid),
+ "LDAPSERVER" : url})
+
+ setup_file(setup_path("olc_seed.ldif"), paths.olcseedldif,
+ {"OLC_SERVER_ID_CONF": olc_serverids_config,
+ "OLC_PW": adminpass,
+ "OLC_SYNCREPL_CONF": olc_syncrepl_seed_config})
+
+
+ # end olc
+
+ setup_file(setup_path("slapd.conf"), paths.slapdconf,
+ {"DNSDOMAIN": names.dnsdomain,
+ "LDAPDIR": paths.ldapdir,
+ "DOMAINDN": names.domaindn,
+ "CONFIGDN": names.configdn,
+ "SCHEMADN": names.schemadn,
+ "MEMBEROF_CONFIG": memberof_config,
+ "MIRRORMODE": mmr_on_config,
+ "REPLICATOR_ACL": mmr_replicator_acl,
+ "MMR_SERVERIDS_CONFIG": mmr_serverids_config,
+ "MMR_SYNCREPL_SCHEMA_CONFIG": mmr_syncrepl_schema_config,
+ "MMR_SYNCREPL_CONFIG_CONFIG": mmr_syncrepl_config_config,
+ "MMR_SYNCREPL_USER_CONFIG": mmr_syncrepl_user_config,
+ "OLC_CONFIG_PASS": olc_config_pass,
+ "OLC_SYNCREPL_CONFIG": olc_syncrepl_config,
+ "OLC_CONFIG_ACL": olc_config_acl,
+ "OLC_MMR_CONFIG": olc_mmr_config,
+ "REFINT_CONFIG": refint_config})
+ setup_file(setup_path("modules.conf"), paths.modulesconf,
+ {"REALM": names.realm})
+
+ setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "user"))
+ setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "config"))
+ setup_db_config(setup_path, os.path.join(paths.ldapdir, "db", "schema"))
+
+ if not os.path.exists(os.path.join(paths.ldapdir, "db", "samba", "cn=samba")):
+ os.makedirs(os.path.join(paths.ldapdir, "db", "samba", "cn=samba"), 0700)
+
+ setup_file(setup_path("cn=samba.ldif"),
+ os.path.join(paths.ldapdir, "db", "samba", "cn=samba.ldif"),
+ { "UUID": str(uuid.uuid4()),
+ "LDAPTIME": timestring(int(time.time()))} )
+ setup_file(setup_path("cn=samba-admin.ldif"),
+ os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=samba-admin.ldif"),
+ {"LDAPADMINPASS_B64": b64encode(adminpass),
+ "UUID": str(uuid.uuid4()),
+ "LDAPTIME": timestring(int(time.time()))} )
+
+ if ol_mmr_urls is not None:
+ setup_file(setup_path("cn=replicator.ldif"),
+ os.path.join(paths.ldapdir, "db", "samba", "cn=samba", "cn=replicator.ldif"),
+ {"MMR_PASSWORD_B64": b64encode(mmr_pass),
+ "UUID": str(uuid.uuid4()),
+ "LDAPTIME": timestring(int(time.time()))} )
+
+
+ mapping = "schema-map-openldap-2.3"
+ backend_schema = "backend-schema.schema"
+
+ ldapi_uri = "ldapi://" + urllib.quote(os.path.join(paths.private_dir, "ldap", "ldapi"), safe="")
+ if ldap_backend_port is not None:
+ server_port_string = " -h ldap://0.0.0.0:%d" % ldap_backend_port
+ else:
+ server_port_string = ""
+
+ if ol_olc != "yes" and ol_mmr_urls is None:
+ slapdcommand="Start slapd with: slapd -f " + paths.ldapdir + "/slapd.conf -h " + ldapi_uri + server_port_string
+
+ if ol_olc == "yes" and ol_mmr_urls is None:
+ slapdcommand="Start slapd with: slapd -F " + paths.olcdir + " -h \"" + ldapi_uri + " ldap://<FQHN>:<PORT>\""
+
+ if ol_olc != "yes" and ol_mmr_urls is not None:
+ slapdcommand="Start slapd with: slapd -f " + paths.ldapdir + "/slapd.conf -h \"" + ldapi_uri + " ldap://<FQHN>:<PORT>\""
+
+ if ol_olc == "yes" and ol_mmr_urls is not None:
+ slapdcommand="Start slapd with: slapd -F " + paths.olcdir + " -h \"" + ldapi_uri + " ldap://<FQHN>:<PORT>\""
+
+
+ ldapuser = "--username=samba-admin"
+
+
+ schema_command = "bin/ad2oLschema --option=convert:target=" + ldap_backend_type + " -I " + setup_path(mapping) + " -H tdb://" + schemadb_path + " -O " + os.path.join(paths.ldapdir, backend_schema)
+
+ os.system(schema_command)
+
+ message("Your %s Backend for Samba4 is now configured, and is ready to be started" % ldap_backend_type)
+ message("Server Role: %s" % serverrole)
+ message("Hostname: %s" % names.hostname)
+ message("DNS Domain: %s" % names.dnsdomain)
+ message("Base DN: %s" % names.domaindn)
+
+ if ldap_backend_type == "openldap":
+ message("LDAP admin user: samba-admin")
+ else:
+ message("LDAP admin DN: %s" % names.ldapmanagerdn)
+
+ message("LDAP admin password: %s" % adminpass)
+ message(slapdcommand)
+ if ol_olc == "yes" or ol_mmr_urls is not None:
+ message("Attention to slapd-Port: <PORT> must be different than 389!")
+ assert isinstance(ldap_backend_type, str)
+ assert isinstance(ldapuser, str)
+ assert isinstance(adminpass, str)
+ assert isinstance(names.dnsdomain, str)
+ assert isinstance(names.domain, str)
+ assert isinstance(serverrole, str)
+ args = ["--ldap-backend=ldapi",
+ "--ldap-backend-type=" + ldap_backend_type,
+ "--password=" + adminpass,
+ ldapuser,
+ "--realm=" + names.dnsdomain,
+ "--domain=" + names.domain,
+ "--server-role='" + serverrole + "'"]
+ message("Run provision with: " + " ".join(args))
+
+
+ # if --ol-olc=yes, generate online-configuration in ../private/ldap/slapd.d
+ if ol_olc == "yes":
+ if not os.path.isdir(paths.olcdir):
+ os.makedirs(paths.olcdir, 0770)
+ paths.olslaptest = str(ol_slaptest)
+ olc_command = paths.olslaptest + " -f" + paths.slapdconf + " -F" + paths.olcdir + " >/dev/null 2>&1"
+ os.system(olc_command)
+ os.remove(paths.slapdconf)
+ # use line below for debugging during olc-conversion with slaptest, instead of olc_command above
+ #olc_command = paths.olslaptest + " -f" + paths.slapdconf + " -F" + paths.olcdir"
+
+
+def create_phpldapadmin_config(path, setup_path, ldapi_uri):
+ """Create a PHP LDAP admin configuration file.
+
+ :param path: Path to write the configuration to.
+ :param setup_path: Function to generate setup paths.
+ """
+ setup_file(setup_path("phpldapadmin-config.php"), path,
+ {"S4_LDAPI_URI": ldapi_uri})
+
+
+def create_zone_file(path, setup_path, dnsdomain, domaindn,
+ hostip, hostip6, hostname, dnspass, realm, domainguid, hostguid):
+ """Write out a DNS zone file, from the info in the current database.
+
+ :param path: Path of the new zone file.
+ :param setup_path: Setup path function.
+ :param dnsdomain: DNS Domain name
+ :param domaindn: DN of the Domain
+ :param hostip: Local IPv4 IP
+ :param hostip6: Local IPv6 IP
+ :param hostname: Local hostname
+ :param dnspass: Password for DNS
+ :param realm: Realm name
+ :param domainguid: GUID of the domain.
+ :param hostguid: GUID of the host.
+ """
+ assert isinstance(domainguid, str)
+
+ if hostip6 is not None:
+ hostip6_base_line = " IN AAAA " + hostip6
+ hostip6_host_line = hostname + " IN AAAA " + hostip6
+ else:
+ hostip6_base_line = ""
+ hostip6_host_line = ""
+
+ if hostip is not None:
+ hostip_base_line = " IN A " + hostip
+ hostip_host_line = hostname + " IN A " + hostip
+ else:
+ hostip_base_line = ""
+ hostip_host_line = ""
+
+ setup_file(setup_path("provision.zone"), path, {
+ "DNSPASS_B64": b64encode(dnspass),
+ "HOSTNAME": hostname,
+ "DNSDOMAIN": dnsdomain,
+ "REALM": realm,
+ "HOSTIP_BASE_LINE": hostip_base_line,
+ "HOSTIP_HOST_LINE": hostip_host_line,
+ "DOMAINGUID": domainguid,
+ "DATESTRING": time.strftime("%Y%m%d%H"),
+ "DEFAULTSITE": DEFAULTSITE,
+ "HOSTGUID": hostguid,
+ "HOSTIP6_BASE_LINE": hostip6_base_line,
+ "HOSTIP6_HOST_LINE": hostip6_host_line,
+ })
+
+
+def create_named_conf(path, setup_path, realm, dnsdomain,
+ private_dir):
+ """Write out a file containing zone statements suitable for inclusion in a
+ named.conf file (including GSS-TSIG configuration).
+
+ :param path: Path of the new named.conf file.
+ :param setup_path: Setup path function.
+ :param realm: Realm name
+ :param dnsdomain: DNS Domain name
+ :param private_dir: Path to private directory
+ :param keytab_name: File name of DNS keytab file
+ """
+
+ setup_file(setup_path("named.conf"), path, {
+ "DNSDOMAIN": dnsdomain,
+ "REALM": realm,
+ "REALM_WC": "*." + ".".join(realm.split(".")[1:]),
+ "PRIVATE_DIR": private_dir
+ })
+
+def create_named_txt(path, setup_path, realm, dnsdomain,
+ private_dir, keytab_name):
+ """Write out a file containing zone statements suitable for inclusion in a
+ named.conf file (including GSS-TSIG configuration).
+
+ :param path: Path of the new named.conf file.
+ :param setup_path: Setup path function.
+ :param realm: Realm name
+ :param dnsdomain: DNS Domain name
+ :param private_dir: Path to private directory
+ :param keytab_name: File name of DNS keytab file
+ """
+
+ setup_file(setup_path("named.txt"), path, {
+ "DNSDOMAIN": dnsdomain,
+ "REALM": realm,
+ "DNS_KEYTAB": keytab_name,
+ "DNS_KEYTAB_ABS": os.path.join(private_dir, keytab_name),
+ "PRIVATE_DIR": private_dir
+ })
+
+def create_krb5_conf(path, setup_path, dnsdomain, hostname, realm):
+ """Write out a file containing zone statements suitable for inclusion in a
+ named.conf file (including GSS-TSIG configuration).
+
+ :param path: Path of the new named.conf file.
+ :param setup_path: Setup path function.
+ :param dnsdomain: DNS Domain name
+ :param hostname: Local hostname
+ :param realm: Realm name
+ """
+
+ setup_file(setup_path("krb5.conf"), path, {
+ "DNSDOMAIN": dnsdomain,
+ "HOSTNAME": hostname,
+ "REALM": realm,
+ })
+
+
+def load_schema(setup_path, samdb, schemadn, netbiosname, configdn, sitename,
+ serverdn, servername):
+ """Load schema for the SamDB.
+
+ :param samdb: Load a schema into a SamDB.
+ :param setup_path: Setup path function.
+ :param schemadn: DN of the schema
+ :param netbiosname: NetBIOS name of the host.
+ :param configdn: DN of the configuration
+ :param serverdn: DN of the server
+ :param servername: Host name of the server
+ """
+ schema_data = open(setup_path("schema.ldif"), 'r').read()
+ schema_data += open(setup_path("schema_samba4.ldif"), 'r').read()
+ schema_data = substitute_var(schema_data, {"SCHEMADN": schemadn})
+ check_all_substituted(schema_data)
+ prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
+ prefixmap = b64encode(prefixmap)
+
+ head_data = open(setup_path("provision_schema_basedn_modify.ldif"), 'r').read()
+ head_data = substitute_var(head_data, {
+ "SCHEMADN": schemadn,
+ "NETBIOSNAME": netbiosname,
+ "CONFIGDN": configdn,
+ "DEFAULTSITE": sitename,
+ "PREFIXMAP_B64": prefixmap,
+ "SERVERDN": serverdn,
+ "SERVERNAME": servername,
+ })
+ check_all_substituted(head_data)
+ samdb.attach_schema_from_ldif(head_data, schema_data)
+
diff --git a/source4/scripting/python/samba/samba3.py b/source4/scripting/python/samba/samba3.py
new file mode 100644
index 0000000000..c8ddbc8864
--- /dev/null
+++ b/source4/scripting/python/samba/samba3.py
@@ -0,0 +1,792 @@
+#!/usr/bin/python
+
+# Unix SMB/CIFS implementation.
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 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 <http://www.gnu.org/licenses/>.
+#
+
+"""Support for reading Samba 3 data files."""
+
+__docformat__ = "restructuredText"
+
+REGISTRY_VALUE_PREFIX = "SAMBA_REGVAL"
+REGISTRY_DB_VERSION = 1
+
+import os
+import struct
+import tdb
+
+
+def fetch_uint32(tdb, key):
+ try:
+ data = tdb[key]
+ except KeyError:
+ return None
+ assert len(data) == 4
+ return struct.unpack("<L", data)[0]
+
+
+def fetch_int32(tdb, key):
+ try:
+ data = tdb[key]
+ except KeyError:
+ return None
+ assert len(data) == 4
+ return struct.unpack("<l", data)[0]
+
+
+class TdbDatabase(object):
+ """Simple Samba 3 TDB database reader."""
+ def __init__(self, file):
+ """Open a file.
+
+ :param file: Path of the file to open.
+ """
+ self.tdb = tdb.Tdb(file, flags=os.O_RDONLY)
+ self._check_version()
+
+ def _check_version(self):
+ pass
+
+ def close(self):
+ """Close resources associated with this object."""
+ self.tdb.close()
+
+
+class Registry(TdbDatabase):
+ """Simple read-only support for reading the Samba3 registry.
+
+ :note: This object uses the same syntax for registry key paths as
+ Samba 3. This particular format uses forward slashes for key path
+ separators and abbreviations for the predefined key names.
+ e.g.: HKLM/Software/Bar.
+ """
+ def __len__(self):
+ """Return the number of keys."""
+ return len(self.keys())
+
+ def keys(self):
+ """Return list with all the keys."""
+ return [k.rstrip("\x00") for k in self.tdb.iterkeys() if not k.startswith(REGISTRY_VALUE_PREFIX)]
+
+ def subkeys(self, key):
+ """Retrieve the subkeys for the specified key.
+
+ :param key: Key path.
+ :return: list with key names
+ """
+ data = self.tdb.get("%s\x00" % key)
+ if data is None:
+ return []
+ (num, ) = struct.unpack("<L", data[0:4])
+ keys = data[4:].split("\0")
+ assert keys[-1] == ""
+ keys.pop()
+ assert len(keys) == num
+ return keys
+
+ def values(self, key):
+ """Return a dictionary with the values set for a specific key.
+
+ :param key: Key to retrieve values for.
+ :return: Dictionary with value names as key, tuple with type and
+ data as value."""
+ data = self.tdb.get("%s/%s\x00" % (REGISTRY_VALUE_PREFIX, key))
+ if data is None:
+ return {}
+ ret = {}
+ (num, ) = struct.unpack("<L", data[0:4])
+ data = data[4:]
+ for i in range(num):
+ # Value name
+ (name, data) = data.split("\0", 1)
+
+ (type, ) = struct.unpack("<L", data[0:4])
+ data = data[4:]
+ (value_len, ) = struct.unpack("<L", data[0:4])
+ data = data[4:]
+
+ ret[name] = (type, data[:value_len])
+ data = data[value_len:]
+
+ return ret
+
+
+class PolicyDatabase(TdbDatabase):
+ """Samba 3 Account Policy database reader."""
+ def __init__(self, file):
+ """Open a policy database
+
+ :param file: Path to the file to open.
+ """
+ super(PolicyDatabase, self).__init__(file)
+ self.min_password_length = fetch_uint32(self.tdb, "min password length\x00")
+ self.password_history = fetch_uint32(self.tdb, "password history\x00")
+ self.user_must_logon_to_change_password = fetch_uint32(self.tdb, "user must logon to change pasword\x00")
+ self.maximum_password_age = fetch_uint32(self.tdb, "maximum password age\x00")
+ self.minimum_password_age = fetch_uint32(self.tdb, "minimum password age\x00")
+ self.lockout_duration = fetch_uint32(self.tdb, "lockout duration\x00")
+ self.reset_count_minutes = fetch_uint32(self.tdb, "reset count minutes\x00")
+ self.bad_lockout_minutes = fetch_uint32(self.tdb, "bad lockout minutes\x00")
+ self.disconnect_time = fetch_int32(self.tdb, "disconnect time\x00")
+ self.refuse_machine_password_change = fetch_uint32(self.tdb, "refuse machine password change\x00")
+
+ # FIXME: Read privileges as well
+
+
+GROUPDB_DATABASE_VERSION_V1 = 1 # native byte format.
+GROUPDB_DATABASE_VERSION_V2 = 2 # le format.
+
+GROUP_PREFIX = "UNIXGROUP/"
+
+# Alias memberships are stored reverse, as memberships. The performance
+# critical operation is to determine the aliases a SID is member of, not
+# listing alias members. So we store a list of alias SIDs a SID is member of
+# hanging of the member as key.
+MEMBEROF_PREFIX = "MEMBEROF/"
+
+class GroupMappingDatabase(TdbDatabase):
+ """Samba 3 group mapping database reader."""
+ def _check_version(self):
+ assert fetch_int32(self.tdb, "INFO/version\x00") in (GROUPDB_DATABASE_VERSION_V1, GROUPDB_DATABASE_VERSION_V2)
+
+ def groupsids(self):
+ """Retrieve the SIDs for the groups in this database.
+
+ :return: List with sids as strings.
+ """
+ for k in self.tdb.iterkeys():
+ if k.startswith(GROUP_PREFIX):
+ yield k[len(GROUP_PREFIX):].rstrip("\0")
+
+ def get_group(self, sid):
+ """Retrieve the group mapping information for a particular group.
+
+ :param sid: SID of the group
+ :return: None if the group can not be found, otherwise
+ a tuple with gid, sid_name_use, the NT name and comment.
+ """
+ data = self.tdb.get("%s%s\0" % (GROUP_PREFIX, sid))
+ if data is None:
+ return data
+ (gid, sid_name_use) = struct.unpack("<lL", data[0:8])
+ (nt_name, comment, _) = data[8:].split("\0")
+ return (gid, sid_name_use, nt_name, comment)
+
+ def aliases(self):
+ """Retrieve the aliases in this database."""
+ for k in self.tdb.iterkeys():
+ if k.startswith(MEMBEROF_PREFIX):
+ yield k[len(MEMBEROF_PREFIX):].rstrip("\0")
+
+
+# High water mark keys
+IDMAP_HWM_GROUP = "GROUP HWM\0"
+IDMAP_HWM_USER = "USER HWM\0"
+
+IDMAP_GROUP_PREFIX = "GID "
+IDMAP_USER_PREFIX = "UID "
+
+# idmap version determines auto-conversion
+IDMAP_VERSION_V2 = 2
+
+class IdmapDatabase(TdbDatabase):
+ """Samba 3 ID map database reader."""
+ def _check_version(self):
+ assert fetch_int32(self.tdb, "IDMAP_VERSION\0") == IDMAP_VERSION_V2
+
+ def uids(self):
+ """Retrieve a list of all uids in this database."""
+ for k in self.tdb.iterkeys():
+ if k.startswith(IDMAP_USER_PREFIX):
+ yield int(k[len(IDMAP_USER_PREFIX):].rstrip("\0"))
+
+ def gids(self):
+ """Retrieve a list of all gids in this database."""
+ for k in self.tdb.iterkeys():
+ if k.startswith(IDMAP_GROUP_PREFIX):
+ yield int(k[len(IDMAP_GROUP_PREFIX):].rstrip("\0"))
+
+ def get_user_sid(self, uid):
+ """Retrieve the SID associated with a particular uid.
+
+ :param uid: UID to retrieve SID for.
+ :return: A SID or None if no mapping was found.
+ """
+ data = self.tdb.get("%s%d\0" % (IDMAP_USER_PREFIX, uid))
+ if data is None:
+ return data
+ return data.rstrip("\0")
+
+ def get_group_sid(self, gid):
+ data = self.tdb.get("%s%d\0" % (IDMAP_GROUP_PREFIX, gid))
+ if data is None:
+ return data
+ return data.rstrip("\0")
+
+ def get_user_hwm(self):
+ """Obtain the user high-water mark."""
+ return fetch_uint32(self.tdb, IDMAP_HWM_USER)
+
+ def get_group_hwm(self):
+ """Obtain the group high-water mark."""
+ return fetch_uint32(self.tdb, IDMAP_HWM_GROUP)
+
+
+class SecretsDatabase(TdbDatabase):
+ """Samba 3 Secrets database reader."""
+ def get_auth_password(self):
+ return self.tdb.get("SECRETS/AUTH_PASSWORD")
+
+ def get_auth_domain(self):
+ return self.tdb.get("SECRETS/AUTH_DOMAIN")
+
+ def get_auth_user(self):
+ return self.tdb.get("SECRETS/AUTH_USER")
+
+ def get_domain_guid(self, host):
+ return self.tdb.get("SECRETS/DOMGUID/%s" % host)
+
+ def ldap_dns(self):
+ for k in self.tdb.iterkeys():
+ if k.startswith("SECRETS/LDAP_BIND_PW/"):
+ yield k[len("SECRETS/LDAP_BIND_PW/"):].rstrip("\0")
+
+ def domains(self):
+ """Iterate over domains in this database.
+
+ :return: Iterator over the names of domains in this database.
+ """
+ for k in self.tdb.iterkeys():
+ if k.startswith("SECRETS/SID/"):
+ yield k[len("SECRETS/SID/"):].rstrip("\0")
+
+ def get_ldap_bind_pw(self, host):
+ return self.tdb.get("SECRETS/LDAP_BIND_PW/%s" % host)
+
+ def get_afs_keyfile(self, host):
+ return self.tdb.get("SECRETS/AFS_KEYFILE/%s" % host)
+
+ def get_machine_sec_channel_type(self, host):
+ return fetch_uint32(self.tdb, "SECRETS/MACHINE_SEC_CHANNEL_TYPE/%s" % host)
+
+ def get_machine_last_change_time(self, host):
+ return fetch_uint32(self.tdb, "SECRETS/MACHINE_LAST_CHANGE_TIME/%s" % host)
+
+ def get_machine_password(self, host):
+ return self.tdb.get("SECRETS/MACHINE_PASSWORD/%s" % host)
+
+ def get_machine_acc(self, host):
+ return self.tdb.get("SECRETS/$MACHINE.ACC/%s" % host)
+
+ def get_domtrust_acc(self, host):
+ return self.tdb.get("SECRETS/$DOMTRUST.ACC/%s" % host)
+
+ def trusted_domains(self):
+ for k in self.tdb.iterkeys():
+ if k.startswith("SECRETS/$DOMTRUST.ACC/"):
+ yield k[len("SECRETS/$DOMTRUST.ACC/"):].rstrip("\0")
+
+ def get_random_seed(self):
+ return self.tdb.get("INFO/random_seed")
+
+ def get_sid(self, host):
+ return self.tdb.get("SECRETS/SID/%s" % host.upper())
+
+
+SHARE_DATABASE_VERSION_V1 = 1
+SHARE_DATABASE_VERSION_V2 = 2
+
+class ShareInfoDatabase(TdbDatabase):
+ """Samba 3 Share Info database reader."""
+ def _check_version(self):
+ assert fetch_int32(self.tdb, "INFO/version\0") in (SHARE_DATABASE_VERSION_V1, SHARE_DATABASE_VERSION_V2)
+
+ def get_secdesc(self, name):
+ """Obtain the security descriptor on a particular share.
+
+ :param name: Name of the share
+ """
+ secdesc = self.tdb.get("SECDESC/%s" % name)
+ # FIXME: Run ndr_pull_security_descriptor
+ return secdesc
+
+
+class Shares(object):
+ """Container for share objects."""
+ def __init__(self, lp, shareinfo):
+ self.lp = lp
+ self.shareinfo = shareinfo
+
+ def __len__(self):
+ """Number of shares."""
+ return len(self.lp) - 1
+
+ def __iter__(self):
+ """Iterate over the share names."""
+ return self.lp.__iter__()
+
+
+ACB_DISABLED = 0x00000001
+ACB_HOMDIRREQ = 0x00000002
+ACB_PWNOTREQ = 0x00000004
+ACB_TEMPDUP = 0x00000008
+ACB_NORMAL = 0x00000010
+ACB_MNS = 0x00000020
+ACB_DOMTRUST = 0x00000040
+ACB_WSTRUST = 0x00000080
+ACB_SVRTRUST = 0x00000100
+ACB_PWNOEXP = 0x00000200
+ACB_AUTOLOCK = 0x00000400
+ACB_ENC_TXT_PWD_ALLOWED = 0x00000800
+ACB_SMARTCARD_REQUIRED = 0x00001000
+ACB_TRUSTED_FOR_DELEGATION = 0x00002000
+ACB_NOT_DELEGATED = 0x00004000
+ACB_USE_DES_KEY_ONLY = 0x00008000
+ACB_DONT_REQUIRE_PREAUTH = 0x00010000
+ACB_PW_EXPIRED = 0x00020000
+ACB_NO_AUTH_DATA_REQD = 0x00080000
+
+acb_info_mapping = {
+ 'N': ACB_PWNOTREQ, # 'N'o password.
+ 'D': ACB_DISABLED, # 'D'isabled.
+ 'H': ACB_HOMDIRREQ, # 'H'omedir required.
+ 'T': ACB_TEMPDUP, # 'T'emp account.
+ 'U': ACB_NORMAL, # 'U'ser account (normal).
+ 'M': ACB_MNS, # 'M'NS logon user account. What is this ?
+ 'W': ACB_WSTRUST, # 'W'orkstation account.
+ 'S': ACB_SVRTRUST, # 'S'erver account.
+ 'L': ACB_AUTOLOCK, # 'L'ocked account.
+ 'X': ACB_PWNOEXP, # No 'X'piry on password
+ 'I': ACB_DOMTRUST, # 'I'nterdomain trust account.
+ ' ': 0
+ }
+
+def decode_acb(text):
+ """Decode a ACB field.
+
+ :param text: ACB text
+ :return: integer with flags set.
+ """
+ assert not "[" in text and not "]" in text
+ ret = 0
+ for x in text:
+ ret |= acb_info_mapping[x]
+ return ret
+
+
+class SAMUser(object):
+ """Samba 3 SAM User.
+
+ :note: Unknown or unset fields are set to None.
+ """
+ def __init__(self, name, uid=None, lm_password=None, nt_password=None, acct_ctrl=None,
+ last_change_time=None, nt_username=None, fullname=None, logon_time=None, logoff_time=None,
+ acct_desc=None, group_rid=None, bad_password_count=None, logon_count=None,
+ domain=None, dir_drive=None, munged_dial=None, homedir=None, logon_script=None,
+ profile_path=None, workstations=None, kickoff_time=None, bad_password_time=None,
+ pass_last_set_time=None, pass_can_change_time=None, pass_must_change_time=None,
+ user_rid=None, unknown_6=None, nt_password_history=None,
+ unknown_str=None, hours=None, logon_divs=None):
+ self.username = name
+ self.uid = uid
+ self.lm_password = lm_password
+ self.nt_password = nt_password
+ self.acct_ctrl = acct_ctrl
+ self.pass_last_set_time = last_change_time
+ self.nt_username = nt_username
+ self.fullname = fullname
+ self.logon_time = logon_time
+ self.logoff_time = logoff_time
+ self.acct_desc = acct_desc
+ self.group_rid = group_rid
+ self.bad_password_count = bad_password_count
+ self.logon_count = logon_count
+ self.domain = domain
+ self.dir_drive = dir_drive
+ self.munged_dial = munged_dial
+ self.homedir = homedir
+ self.logon_script = logon_script
+ self.profile_path = profile_path
+ self.workstations = workstations
+ self.kickoff_time = kickoff_time
+ self.bad_password_time = bad_password_time
+ self.pass_can_change_time = pass_can_change_time
+ self.pass_must_change_time = pass_must_change_time
+ self.user_rid = user_rid
+ self.unknown_6 = unknown_6
+ self.nt_password_history = nt_password_history
+ self.unknown_str = unknown_str
+ self.hours = hours
+ self.logon_divs = logon_divs
+
+ def __eq__(self, other):
+ if not isinstance(other, SAMUser):
+ return False
+ return self.__dict__ == other.__dict__
+
+
+class SmbpasswdFile(object):
+ """Samba 3 smbpasswd file reader."""
+ def __init__(self, file):
+ self.users = {}
+ f = open(file, 'r')
+ for l in f.readlines():
+ if len(l) == 0 or l[0] == "#":
+ continue # Skip comments and blank lines
+ parts = l.split(":")
+ username = parts[0]
+ uid = int(parts[1])
+ acct_ctrl = 0
+ last_change_time = None
+ if parts[2] == "NO PASSWORD":
+ acct_ctrl |= ACB_PWNOTREQ
+ lm_password = None
+ elif parts[2][0] in ("*", "X"):
+ # No password set
+ lm_password = None
+ else:
+ lm_password = parts[2]
+
+ if parts[3][0] in ("*", "X"):
+ # No password set
+ nt_password = None
+ else:
+ nt_password = parts[3]
+
+ if parts[4][0] == '[':
+ assert "]" in parts[4]
+ acct_ctrl |= decode_acb(parts[4][1:-1])
+ if parts[5].startswith("LCT-"):
+ last_change_time = int(parts[5][len("LCT-"):], 16)
+ else: # old style file
+ if username[-1] == "$":
+ acct_ctrl &= ~ACB_NORMAL
+ acct_ctrl |= ACB_WSTRUST
+
+ self.users[username] = SAMUser(username, uid, lm_password, nt_password, acct_ctrl, last_change_time)
+
+ f.close()
+
+ def __len__(self):
+ return len(self.users)
+
+ def __getitem__(self, name):
+ return self.users[name]
+
+ def __iter__(self):
+ return iter(self.users)
+
+ def close(self): # For consistency
+ pass
+
+
+TDBSAM_FORMAT_STRING_V0 = "ddddddBBBBBBBBBBBBddBBwdwdBwwd"
+TDBSAM_FORMAT_STRING_V1 = "dddddddBBBBBBBBBBBBddBBwdwdBwwd"
+TDBSAM_FORMAT_STRING_V2 = "dddddddBBBBBBBBBBBBddBBBwwdBwwd"
+TDBSAM_USER_PREFIX = "USER_"
+
+
+class LdapSam(object):
+ """Samba 3 LDAP passdb backend reader."""
+ def __init__(self, url):
+ self.ldap_url = ldap_url
+
+
+class TdbSam(TdbDatabase):
+ """Samba 3 TDB passdb backend reader."""
+ def _check_version(self):
+ self.version = fetch_uint32(self.tdb, "INFO/version\0") or 0
+ assert self.version in (0, 1, 2)
+
+ def usernames(self):
+ """Iterate over the usernames in this Tdb database."""
+ for k in self.tdb.iterkeys():
+ if k.startswith(TDBSAM_USER_PREFIX):
+ yield k[len(TDBSAM_USER_PREFIX):].rstrip("\0")
+
+ __iter__ = usernames
+
+ def __getitem__(self, name):
+ data = self.tdb["%s%s\0" % (TDBSAM_USER_PREFIX, name)]
+ user = SAMUser(name)
+
+ def unpack_string(data):
+ (length, ) = struct.unpack("<L", data[:4])
+ data = data[4:]
+ if length == 0:
+ return (None, data)
+ return (data[:length].rstrip("\0"), data[length:])
+
+ def unpack_int32(data):
+ (value, ) = struct.unpack("<l", data[:4])
+ return (value, data[4:])
+
+ def unpack_uint32(data):
+ (value, ) = struct.unpack("<L", data[:4])
+ return (value, data[4:])
+
+ def unpack_uint16(data):
+ (value, ) = struct.unpack("<H", data[:2])
+ return (value, data[2:])
+
+ (logon_time, data) = unpack_int32(data)
+ (logoff_time, data) = unpack_int32(data)
+ (kickoff_time, data) = unpack_int32(data)
+
+ if self.version > 0:
+ (bad_password_time, data) = unpack_int32(data)
+ if bad_password_time != 0:
+ user.bad_password_time = bad_password_time
+ (pass_last_set_time, data) = unpack_int32(data)
+ (pass_can_change_time, data) = unpack_int32(data)
+ (pass_must_change_time, data) = unpack_int32(data)
+
+ if logon_time != 0:
+ user.logon_time = logon_time
+ user.logoff_time = logoff_time
+ user.kickoff_time = kickoff_time
+ if pass_last_set_time != 0:
+ user.pass_last_set_time = pass_last_set_time
+ user.pass_can_change_time = pass_can_change_time
+
+ (user.username, data) = unpack_string(data)
+ (user.domain, data) = unpack_string(data)
+ (user.nt_username, data) = unpack_string(data)
+ (user.fullname, data) = unpack_string(data)
+ (user.homedir, data) = unpack_string(data)
+ (user.dir_drive, data) = unpack_string(data)
+ (user.logon_script, data) = unpack_string(data)
+ (user.profile_path, data) = unpack_string(data)
+ (user.acct_desc, data) = unpack_string(data)
+ (user.workstations, data) = unpack_string(data)
+ (user.unknown_str, data) = unpack_string(data)
+ (user.munged_dial, data) = unpack_string(data)
+
+ (user.user_rid, data) = unpack_int32(data)
+ (user.group_rid, data) = unpack_int32(data)
+
+ (user.lm_password, data) = unpack_string(data)
+ (user.nt_password, data) = unpack_string(data)
+
+ if self.version > 1:
+ (user.nt_password_history, data) = unpack_string(data)
+
+ (user.acct_ctrl, data) = unpack_uint16(data)
+ (_, data) = unpack_uint32(data) # remove_me field
+ (user.logon_divs, data) = unpack_uint16(data)
+ (hours, data) = unpack_string(data)
+ user.hours = []
+ for entry in hours:
+ for i in range(8):
+ user.hours.append(ord(entry) & (2 ** i) == (2 ** i))
+ (user.bad_password_count, data) = unpack_uint16(data)
+ (user.logon_count, data) = unpack_uint16(data)
+ (user.unknown_6, data) = unpack_uint32(data)
+ assert len(data) == 0
+ return user
+
+
+def shellsplit(text):
+ """Very simple shell-like line splitting.
+
+ :param text: Text to split.
+ :return: List with parts of the line as strings.
+ """
+ ret = list()
+ inquotes = False
+ current = ""
+ for c in text:
+ if c == "\"":
+ inquotes = not inquotes
+ elif c in ("\t", "\n", " ") and not inquotes:
+ ret.append(current)
+ current = ""
+ else:
+ current += c
+ if current != "":
+ ret.append(current)
+ return ret
+
+
+class WinsDatabase(object):
+ """Samba 3 WINS database reader."""
+ def __init__(self, file):
+ self.entries = {}
+ f = open(file, 'r')
+ assert f.readline().rstrip("\n") == "VERSION 1 0"
+ for l in f.readlines():
+ if l[0] == "#": # skip comments
+ continue
+ entries = shellsplit(l.rstrip("\n"))
+ name = entries[0]
+ ttl = int(entries[1])
+ i = 2
+ ips = []
+ while "." in entries[i]:
+ ips.append(entries[i])
+ i+=1
+ nb_flags = int(entries[i][:-1], 16)
+ assert not name in self.entries, "Name %s exists twice" % name
+ self.entries[name] = (ttl, ips, nb_flags)
+ f.close()
+
+ def __getitem__(self, name):
+ return self.entries[name]
+
+ def __len__(self):
+ return len(self.entries)
+
+ def __iter__(self):
+ return iter(self.entries)
+
+ def items(self):
+ """Return the entries in this WINS database."""
+ return self.entries.items()
+
+ def close(self): # for consistency
+ pass
+
+
+class ParamFile(object):
+ """Simple smb.conf-compatible file parser
+
+ Does not use a parameter table, unlike the "normal".
+ """
+
+ def __init__(self, sections=None):
+ self._sections = sections or {}
+
+ def _sanitize_name(self, name):
+ return name.strip().lower().replace(" ","")
+
+ def __repr__(self):
+ return "ParamFile(%r)" % self._sections
+
+ def read(self, filename):
+ """Read a file.
+
+ :param filename: Path to the file
+ """
+ section = None
+ for i, l in enumerate(open(filename, 'r').xreadlines()):
+ l = l.strip()
+ if not l:
+ continue
+ if l[0] == "[" and l[-1] == "]":
+ section = self._sanitize_name(l[1:-1])
+ self._sections.setdefault(section, {})
+ elif "=" in l:
+ (k, v) = l.split("=", 1)
+ self._sections[section][self._sanitize_name(k)] = v
+ else:
+ raise Error("Unable to parser line %d: %r" % (i+1,l))
+
+ def get(self, param, section=None):
+ """Return the value of a parameter.
+
+ :param param: Parameter name
+ :param section: Section name, defaults to "global"
+ :return: parameter value as string if found, None otherwise.
+ """
+ if section is None:
+ section = "global"
+ section = self._sanitize_name(section)
+ if not section in self._sections:
+ return None
+ param = self._sanitize_name(param)
+ if not param in self._sections[section]:
+ return None
+ return self._sections[section][param].strip()
+
+ def __getitem__(self, section):
+ return self._sections[section]
+
+ def get_section(self, section):
+ return self._sections.get(section)
+
+ def add_section(self, section):
+ self._sections[self._sanitize_name(section)] = {}
+
+ def set_string(self, name, value):
+ self._sections["global"][name] = value
+
+ def get_string(self, name):
+ return self._sections["global"].get(name)
+
+
+class Samba3(object):
+ """Samba 3 configuration and state data reader."""
+ def __init__(self, libdir, smbconfpath):
+ """Open the configuration and data for a Samba 3 installation.
+
+ :param libdir: Library directory
+ :param smbconfpath: Path to the smb.conf file.
+ """
+ self.smbconfpath = smbconfpath
+ self.libdir = libdir
+ self.lp = ParamFile()
+ self.lp.read(self.smbconfpath)
+
+ def libdir_path(self, path):
+ if path[0] == "/" or path[0] == ".":
+ return path
+ return os.path.join(self.libdir, path)
+
+ def get_conf(self):
+ return self.lp
+
+ def get_sam_db(self):
+ lp = self.get_conf()
+ backends = (lp.get("passdb backend") or "").split(" ")
+ if ":" in backends[0]:
+ (name, location) = backends[0].split(":", 2)
+ else:
+ name = backends[0]
+ location = None
+ if name == "smbpasswd":
+ return SmbpasswdFile(self.libdir_path(location or "smbpasswd"))
+ elif name == "tdbsam":
+ return TdbSam(self.libdir_path(location or "passdb.tdb"))
+ elif name == "ldapsam":
+ if location is not None:
+ return LdapSam("ldap:%s" % location)
+ return LdapSam(lp.get("ldap server"))
+ else:
+ raise NotImplementedError("unsupported passdb backend %s" % backends[0])
+
+ def get_policy_db(self):
+ return PolicyDatabase(self.libdir_path("account_policy.tdb"))
+
+ def get_registry(self):
+ return Registry(self.libdir_path("registry.tdb"))
+
+ def get_secrets_db(self):
+ return SecretsDatabase(self.libdir_path("secrets.tdb"))
+
+ def get_shareinfo_db(self):
+ return ShareInfoDatabase(self.libdir_path("share_info.tdb"))
+
+ def get_idmap_db(self):
+ return IdmapDatabase(self.libdir_path("winbindd_idmap.tdb"))
+
+ def get_wins_db(self):
+ return WinsDatabase(self.libdir_path("wins.dat"))
+
+ def get_shares(self):
+ return Shares(self.get_conf(), self.get_shareinfo_db())
+
+ def get_groupmapping_db(self):
+ return GroupMappingDatabase(self.libdir_path("group_mapping.tdb"))
diff --git a/source4/scripting/python/samba/samdb.py b/source4/scripting/python/samba/samdb.py
new file mode 100644
index 0000000000..b92a91e2ef
--- /dev/null
+++ b/source4/scripting/python/samba/samdb.py
@@ -0,0 +1,245 @@
+#!/usr/bin/python
+
+# Unix SMB/CIFS implementation.
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008
+#
+# Based on the original in EJS:
+# Copyright (C) Andrew Tridgell <tridge@samba.org> 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 <http://www.gnu.org/licenses/>.
+#
+
+"""Convenience functions for using the SAM."""
+
+import samba
+import glue
+import ldb
+from samba.idmap import IDmapDB
+import pwd
+import time
+import base64
+
+__docformat__ = "restructuredText"
+
+class SamDB(samba.Ldb):
+ """The SAM database."""
+
+ def __init__(self, url=None, session_info=None, credentials=None,
+ modules_dir=None, lp=None):
+ """Open the Sam Database.
+
+ :param url: URL of the database.
+ """
+ self.lp = lp
+ super(SamDB, self).__init__(session_info=session_info, credentials=credentials,
+ modules_dir=modules_dir, lp=lp)
+ glue.dsdb_set_global_schema(self)
+ if url:
+ self.connect(url)
+ else:
+ self.connect(lp.get("sam database"))
+
+ def connect(self, url):
+ super(SamDB, self).connect(self.lp.private_path(url))
+
+ def add_foreign(self, domaindn, sid, desc):
+ """Add a foreign security principle."""
+ add = """
+dn: CN=%s,CN=ForeignSecurityPrincipals,%s
+objectClass: top
+objectClass: foreignSecurityPrincipal
+description: %s
+""" % (sid, domaindn, desc)
+ # deliberately ignore errors from this, as the records may
+ # already exist
+ for msg in self.parse_ldif(add):
+ self.add(msg[1])
+
+ def add_stock_foreign_sids(self):
+ domaindn = self.domain_dn()
+ self.add_foreign(domaindn, "S-1-5-7", "Anonymous")
+ self.add_foreign(domaindn, "S-1-1-0", "World")
+ self.add_foreign(domaindn, "S-1-5-2", "Network")
+ self.add_foreign(domaindn, "S-1-5-18", "System")
+ self.add_foreign(domaindn, "S-1-5-11", "Authenticated Users")
+
+ def enable_account(self, user_dn):
+ """Enable an account.
+
+ :param user_dn: Dn of the account to enable.
+ """
+ res = self.search(user_dn, ldb.SCOPE_BASE, None, ["userAccountControl"])
+ assert len(res) == 1
+ userAccountControl = res[0]["userAccountControl"][0]
+ userAccountControl = int(userAccountControl)
+ if (userAccountControl & 0x2):
+ userAccountControl = userAccountControl & ~0x2 # remove disabled bit
+ if (userAccountControl & 0x20):
+ userAccountControl = userAccountControl & ~0x20 # remove 'no password required' bit
+
+ mod = """
+dn: %s
+changetype: modify
+replace: userAccountControl
+userAccountControl: %u
+""" % (user_dn, userAccountControl)
+ self.modify_ldif(mod)
+
+ def domain_dn(self):
+ # find the DNs for the domain and the domain users group
+ res = self.search("", scope=ldb.SCOPE_BASE,
+ expression="(defaultNamingContext=*)",
+ attrs=["defaultNamingContext"])
+ assert(len(res) == 1 and res[0]["defaultNamingContext"] is not None)
+ return res[0]["defaultNamingContext"][0]
+
+ def newuser(self, username, unixname, password):
+ """add a new user record.
+
+ :param username: Name of the new user.
+ :param unixname: Name of the unix user to map to.
+ :param password: Password for the new user
+ """
+ # connect to the sam
+ self.transaction_start()
+ try:
+ domain_dn = self.domain_dn()
+ assert(domain_dn is not None)
+ user_dn = "CN=%s,CN=Users,%s" % (username, domain_dn)
+
+ #
+ # the new user record. note the reliance on the samdb module to
+ # fill in a sid, guid etc
+ #
+ # now the real work
+ self.add({"dn": user_dn,
+ "sAMAccountName": username,
+ "userPassword": password,
+ "objectClass": "user"})
+
+ res = self.search(user_dn, scope=ldb.SCOPE_BASE,
+ expression="objectclass=*",
+ attrs=["objectSid"])
+ assert len(res) == 1
+ user_sid = self.schema_format_value("objectSid", res[0]["objectSid"][0])
+
+ try:
+ idmap = IDmapDB(lp=self.lp)
+
+ user = pwd.getpwnam(unixname)
+ # setup ID mapping for this UID
+
+ idmap.setup_name_mapping(user_sid, idmap.TYPE_UID, user[2])
+
+ except KeyError:
+ pass
+
+ # modify the userAccountControl to remove the disabled bit
+ self.enable_account(user_dn)
+ except:
+ self.transaction_cancel()
+ raise
+ self.transaction_commit()
+
+ def setpassword(self, filter, password):
+ """Set a password on a user record
+
+ :param filter: LDAP filter to find the user (eg samccountname=name)
+ :param password: Password for the user
+ """
+ # connect to the sam
+ self.transaction_start()
+ try:
+ # find the DNs for the domain
+ res = self.search("", scope=ldb.SCOPE_BASE,
+ expression="(defaultNamingContext=*)",
+ attrs=["defaultNamingContext"])
+ assert(len(res) == 1 and res[0]["defaultNamingContext"] is not None)
+ domain_dn = res[0]["defaultNamingContext"][0]
+ assert(domain_dn is not None)
+
+ res = self.search(domain_dn, scope=ldb.SCOPE_SUBTREE,
+ expression=filter,
+ attrs=[])
+ assert(len(res) == 1)
+ user_dn = res[0].dn
+
+ setpw = """
+dn: %s
+changetype: modify
+replace: userPassword
+userPassword:: %s
+""" % (user_dn, base64.b64encode(password))
+
+ self.modify_ldif(setpw)
+
+ # modify the userAccountControl to remove the disabled bit
+ self.enable_account(user_dn)
+ except:
+ self.transaction_cancel()
+ raise
+ self.transaction_commit()
+
+ def set_domain_sid(self, sid):
+ """Change the domain SID used by this SamDB.
+
+ :param sid: The new domain sid to use.
+ """
+ glue.samdb_set_domain_sid(self, sid)
+
+ def attach_schema_from_ldif(self, pf, df):
+ glue.dsdb_attach_schema_from_ldif_file(self, pf, df)
+
+ def set_invocation_id(self, invocation_id):
+ """Set the invocation id for this SamDB handle.
+
+ :param invocation_id: GUID of the invocation id.
+ """
+ glue.dsdb_set_ntds_invocation_id(self, invocation_id)
+
+ def setexpiry(self, user, expiry_seconds, noexpiry):
+ """Set the password expiry for a user
+
+ :param expiry_seconds: expiry time from now in seconds
+ :param noexpiry: if set, then don't expire password
+ """
+ self.transaction_start()
+ try:
+ res = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE,
+ expression=("(samAccountName=%s)" % user),
+ attrs=["userAccountControl", "accountExpires"])
+ assert len(res) == 1
+ userAccountControl = int(res[0]["userAccountControl"][0])
+ accountExpires = int(res[0]["accountExpires"][0])
+ if noexpiry:
+ userAccountControl = userAccountControl | 0x10000
+ accountExpires = 0
+ else:
+ userAccountControl = userAccountControl & ~0x10000
+ accountExpires = glue.unix2nttime(expiry_seconds + int(time.time()))
+
+ mod = """
+dn: %s
+changetype: modify
+replace: userAccountControl
+userAccountControl: %u
+replace: accountExpires
+accountExpires: %u
+""" % (res[0].dn, userAccountControl, accountExpires)
+ # now change the database
+ self.modify_ldif(mod)
+ except:
+ self.transaction_cancel()
+ raise
+ self.transaction_commit();
diff --git a/source4/scripting/python/samba/tests/__init__.py b/source4/scripting/python/samba/tests/__init__.py
new file mode 100644
index 0000000000..b342b93c49
--- /dev/null
+++ b/source4/scripting/python/samba/tests/__init__.py
@@ -0,0 +1,98 @@
+#!/usr/bin/python
+
+# Unix SMB/CIFS implementation.
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-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 <http://www.gnu.org/licenses/>.
+#
+
+"""Samba Python tests."""
+
+import os
+import ldb
+import samba
+import tempfile
+import unittest
+
+class LdbTestCase(unittest.TestCase):
+ """Trivial test case for running tests against a LDB."""
+ def setUp(self):
+ self.filename = os.tempnam()
+ self.ldb = samba.Ldb(self.filename)
+
+ def set_modules(self, modules=[]):
+ """Change the modules for this Ldb."""
+ m = ldb.Message()
+ m.dn = ldb.Dn(self.ldb, "@MODULES")
+ m["@LIST"] = ",".join(modules)
+ self.ldb.add(m)
+ self.ldb = samba.Ldb(self.filename)
+
+
+class TestCaseInTempDir(unittest.TestCase):
+ def setUp(self):
+ super(TestCaseInTempDir, self).setUp()
+ self.tempdir = tempfile.mkdtemp()
+
+ def tearDown(self):
+ super(TestCaseInTempDir, self).tearDown()
+ self.assertEquals([], os.listdir(self.tempdir))
+ os.rmdir(self.tempdir)
+
+
+class SubstituteVarTestCase(unittest.TestCase):
+ def test_empty(self):
+ self.assertEquals("", samba.substitute_var("", {}))
+
+ def test_nothing(self):
+ self.assertEquals("foo bar", samba.substitute_var("foo bar", {"bar": "bla"}))
+
+ def test_replace(self):
+ self.assertEquals("foo bla", samba.substitute_var("foo ${bar}", {"bar": "bla"}))
+
+ def test_broken(self):
+ self.assertEquals("foo ${bdkjfhsdkfh sdkfh ",
+ samba.substitute_var("foo ${bdkjfhsdkfh sdkfh ", {"bar": "bla"}))
+
+ def test_unknown_var(self):
+ self.assertEquals("foo ${bla} gsff",
+ samba.substitute_var("foo ${bla} gsff", {"bar": "bla"}))
+
+ def test_check_all_substituted(self):
+ samba.check_all_substituted("nothing to see here")
+ self.assertRaises(Exception, samba.check_all_substituted, "Not subsituted: ${FOOBAR}")
+
+
+class LdbExtensionTests(TestCaseInTempDir):
+ def test_searchone(self):
+ path = self.tempdir + "/searchone.ldb"
+ l = samba.Ldb(path)
+ try:
+ l.add({"dn": "foo=dc", "bar": "bla"})
+ self.assertEquals("bla", l.searchone(basedn=ldb.Dn(l, "foo=dc"), attribute="bar"))
+ finally:
+ del l
+ os.unlink(path)
+
+
+cmdline_loadparm = None
+cmdline_credentials = None
+
+class RpcInterfaceTestCase(unittest.TestCase):
+ def get_loadparm(self):
+ assert cmdline_loadparm is not None
+ return cmdline_loadparm
+
+ def get_credentials(self):
+ return cmdline_credentials
diff --git a/source4/scripting/python/samba/tests/dcerpc/__init__.py b/source4/scripting/python/samba/tests/dcerpc/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/source4/scripting/python/samba/tests/dcerpc/__init__.py
diff --git a/source4/scripting/python/samba/tests/dcerpc/bare.py b/source4/scripting/python/samba/tests/dcerpc/bare.py
new file mode 100644
index 0000000000..cd939b8098
--- /dev/null
+++ b/source4/scripting/python/samba/tests/dcerpc/bare.py
@@ -0,0 +1,48 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Unix SMB/CIFS implementation.
+# Copyright © Jelmer Vernooij <jelmer@samba.org> 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 <http://www.gnu.org/licenses/>.
+#
+
+from samba.dcerpc import ClientConnection
+from unittest import TestCase
+from samba.tests import cmdline_loadparm
+
+
+class BareTestCase(TestCase):
+ def test_bare(self):
+ # Connect to the echo pipe
+ x = ClientConnection("ncalrpc:localhost[DEFAULT]",
+ ("60a15ec5-4de8-11d7-a637-005056a20182", 1), lp_ctx=cmdline_loadparm)
+ self.assertEquals("\x01\x00\x00\x00", x.request(0, chr(0) * 4))
+
+ def test_alter_context(self):
+ x = ClientConnection("ncalrpc:localhost[DEFAULT]",
+ ("12345778-1234-abcd-ef00-0123456789ac", 1), lp_ctx=cmdline_loadparm)
+ y = ClientConnection("ncalrpc:localhost",
+ ("60a15ec5-4de8-11d7-a637-005056a20182", 1),
+ basis_connection=x, lp_ctx=cmdline_loadparm)
+ x.alter_context(("60a15ec5-4de8-11d7-a637-005056a20182", 1))
+ # FIXME: self.assertEquals("\x01\x00\x00\x00", x.request(0, chr(0) * 4))
+
+ def test_two_connections(self):
+ x = ClientConnection("ncalrpc:localhost[DEFAULT]",
+ ("60a15ec5-4de8-11d7-a637-005056a20182", 1), lp_ctx=cmdline_loadparm)
+ y = ClientConnection("ncalrpc:localhost",
+ ("60a15ec5-4de8-11d7-a637-005056a20182", 1),
+ basis_connection=x, lp_ctx=cmdline_loadparm)
+ self.assertEquals("\x01\x00\x00\x00", y.request(0, chr(0) * 4))
diff --git a/source4/scripting/python/samba/tests/dcerpc/registry.py b/source4/scripting/python/samba/tests/dcerpc/registry.py
new file mode 100644
index 0000000000..526b2340cc
--- /dev/null
+++ b/source4/scripting/python/samba/tests/dcerpc/registry.py
@@ -0,0 +1,50 @@
+#!/usr/bin/python
+
+# Unix SMB/CIFS implementation.
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 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 <http://www.gnu.org/licenses/>.
+#
+
+from samba.dcerpc import winreg
+import unittest
+from samba.tests import RpcInterfaceTestCase
+
+
+class WinregTests(RpcInterfaceTestCase):
+ def setUp(self):
+ self.conn = winreg.winreg("ncalrpc:", self.get_loadparm(),
+ self.get_credentials())
+
+ def get_hklm(self):
+ return self.conn.OpenHKLM(None,
+ winreg.KEY_QUERY_VALUE | winreg.KEY_ENUMERATE_SUB_KEYS)
+
+ def test_hklm(self):
+ handle = self.conn.OpenHKLM(None,
+ winreg.KEY_QUERY_VALUE | winreg.KEY_ENUMERATE_SUB_KEYS)
+ self.conn.CloseKey(handle)
+
+ def test_getversion(self):
+ handle = self.get_hklm()
+ version = self.conn.GetVersion(handle)
+ self.assertEquals(int, version.__class__)
+ self.conn.CloseKey(handle)
+
+ def test_getkeyinfo(self):
+ handle = self.conn.OpenHKLM(None,
+ winreg.KEY_QUERY_VALUE | winreg.KEY_ENUMERATE_SUB_KEYS)
+ x = self.conn.QueryInfoKey(handle, winreg.String())
+ self.assertEquals(9, len(x)) # should return a 9-tuple
+ self.conn.CloseKey(handle)
diff --git a/source4/scripting/python/samba/tests/dcerpc/rpcecho.py b/source4/scripting/python/samba/tests/dcerpc/rpcecho.py
new file mode 100644
index 0000000000..62268005c2
--- /dev/null
+++ b/source4/scripting/python/samba/tests/dcerpc/rpcecho.py
@@ -0,0 +1,69 @@
+#!/usr/bin/python
+
+# Unix SMB/CIFS implementation.
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 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 <http://www.gnu.org/licenses/>.
+#
+
+from samba.dcerpc import echo
+from samba.ndr import ndr_pack, ndr_unpack
+import unittest
+from samba.tests import RpcInterfaceTestCase
+
+
+class RpcEchoTests(RpcInterfaceTestCase):
+ def setUp(self):
+ self.conn = echo.rpcecho("ncalrpc:", self.get_loadparm())
+
+ def test_two_contexts(self):
+ self.conn2 = echo.rpcecho("ncalrpc:", self.get_loadparm(), basis_connection=self.conn)
+ self.assertEquals(3, self.conn2.AddOne(2))
+
+ def test_abstract_syntax(self):
+ self.assertEquals(("60a15ec5-4de8-11d7-a637-005056a20182", 1),
+ self.conn.abstract_syntax)
+
+ def test_addone(self):
+ self.assertEquals(2, self.conn.AddOne(1))
+
+ def test_echodata(self):
+ self.assertEquals([1,2,3], self.conn.EchoData([1, 2, 3]))
+
+ def test_call(self):
+ self.assertEquals(u"foobar", self.conn.TestCall(u"foobar"))
+
+ def test_surrounding(self):
+ surrounding_struct = echo.Surrounding()
+ surrounding_struct.x = 4
+ surrounding_struct.surrounding = [1,2,3,4]
+ y = self.conn.TestSurrounding(surrounding_struct)
+ self.assertEquals(4 * [0], y.surrounding)
+
+ def test_manual_request(self):
+ self.assertEquals("\x01\x00\x00\x00", self.conn.request(0, chr(0) * 4))
+
+ def test_server_name(self):
+ self.assertEquals(None, self.conn.server_name)
+
+
+class NdrEchoTests(unittest.TestCase):
+ def test_info1_push(self):
+ x = echo.info1()
+ x.v = 42
+ self.assertEquals("\x2a", ndr_pack(x))
+
+ def test_info1_pull(self):
+ x = ndr_unpack(echo.info1, "\x42")
+ self.assertEquals(x.v, 66)
diff --git a/source4/scripting/python/samba/tests/dcerpc/sam.py b/source4/scripting/python/samba/tests/dcerpc/sam.py
new file mode 100644
index 0000000000..50e00a3f9e
--- /dev/null
+++ b/source4/scripting/python/samba/tests/dcerpc/sam.py
@@ -0,0 +1,46 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Unix SMB/CIFS implementation.
+# Copyright © Jelmer Vernooij <jelmer@samba.org> 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 <http://www.gnu.org/licenses/>.
+#
+
+from samba.dcerpc import samr, security
+from samba.tests import RpcInterfaceTestCase
+
+# FIXME: Pidl should be doing this for us
+def toArray((handle, array, num_entries)):
+ ret = []
+ for x in range(num_entries):
+ ret.append((array.entries[x].idx, array.entries[x].name))
+ return ret
+
+
+class SamrTests(RpcInterfaceTestCase):
+ def setUp(self):
+ self.conn = samr.samr("ncalrpc:", self.get_loadparm())
+
+ def test_connect5(self):
+ (level, info, handle) = self.conn.Connect5(None, 0, 1, samr.ConnectInfo1())
+
+ def test_connect2(self):
+ handle = self.conn.Connect2(None, security.SEC_FLAG_MAXIMUM_ALLOWED)
+
+ def test_EnumDomains(self):
+ handle = self.conn.Connect2(None, security.SEC_FLAG_MAXIMUM_ALLOWED)
+ domains = toArray(self.conn.EnumDomains(handle, 0, -1))
+ self.conn.Close(handle)
+
diff --git a/source4/scripting/python/samba/tests/dcerpc/unix.py b/source4/scripting/python/samba/tests/dcerpc/unix.py
new file mode 100644
index 0000000000..aa47b71b16
--- /dev/null
+++ b/source4/scripting/python/samba/tests/dcerpc/unix.py
@@ -0,0 +1,37 @@
+#!/usr/bin/python
+
+# Unix SMB/CIFS implementation.
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 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 <http://www.gnu.org/licenses/>.
+#
+
+from samba.dcerpc import unixinfo
+from samba.tests import RpcInterfaceTestCase
+
+class UnixinfoTests(RpcInterfaceTestCase):
+ def setUp(self):
+ self.conn = unixinfo.unixinfo("ncalrpc:", self.get_loadparm())
+
+ def test_getpwuid(self):
+ infos = self.conn.GetPWUid(range(512))
+ self.assertEquals(512, len(infos))
+ self.assertEquals("/bin/false", infos[0].shell)
+ self.assertTrue(isinstance(infos[0].homedir, unicode))
+
+ def test_gidtosid(self):
+ self.conn.GidToSid(1000)
+
+ def test_uidtosid(self):
+ self.conn.UidToSid(1000)
diff --git a/source4/scripting/python/samba/tests/provision.py b/source4/scripting/python/samba/tests/provision.py
new file mode 100644
index 0000000000..fdac9d4ea2
--- /dev/null
+++ b/source4/scripting/python/samba/tests/provision.py
@@ -0,0 +1,124 @@
+#!/usr/bin/python
+
+# Unix SMB/CIFS implementation.
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-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 <http://www.gnu.org/licenses/>.
+#
+
+import os
+from samba.provision import setup_secretsdb, secretsdb_become_dc, findnss
+import samba.tests
+from ldb import Dn
+from samba import param
+import unittest
+
+lp = samba.tests.cmdline_loadparm
+
+setup_dir = "setup"
+def setup_path(file):
+ return os.path.join(setup_dir, file)
+
+
+class ProvisionTestCase(samba.tests.TestCaseInTempDir):
+ """Some simple tests for individual functions in the provisioning code.
+ """
+ def test_setup_secretsdb(self):
+ path = os.path.join(self.tempdir, "secrets.ldb")
+ ldb = setup_secretsdb(path, setup_path, None, None, lp=lp)
+ try:
+ self.assertEquals("LSA Secrets",
+ ldb.searchone(basedn="CN=LSA Secrets", attribute="CN"))
+ finally:
+ del ldb
+ os.unlink(path)
+
+ def test_become_dc(self):
+ path = os.path.join(self.tempdir, "secrets.ldb")
+ secrets_ldb = setup_secretsdb(path, setup_path, None, None, lp=lp)
+ try:
+ secretsdb_become_dc(secrets_ldb, setup_path, domain="EXAMPLE",
+ realm="example", netbiosname="myhost",
+ domainsid="S-5-22", keytab_path="keytab.path",
+ samdb_url="ldap://url/",
+ dns_keytab_path="dns.keytab", dnspass="bla",
+ machinepass="machinepass", dnsdomain="example.com")
+ self.assertEquals(1,
+ len(secrets_ldb.search("samAccountName=krbtgt,flatname=EXAMPLE,CN=Principals")))
+ self.assertEquals("keytab.path",
+ secrets_ldb.searchone(basedn="flatname=EXAMPLE,CN=primary domains",
+ expression="(privateKeytab=*)",
+ attribute="privateKeytab"))
+ self.assertEquals("S-5-22",
+ secrets_ldb.searchone(basedn="flatname=EXAMPLE,CN=primary domains",
+ expression="objectSid=*", attribute="objectSid"))
+
+ finally:
+ del secrets_ldb
+ os.unlink(path)
+
+
+class FindNssTests(unittest.TestCase):
+ """Test findnss() function."""
+ def test_nothing(self):
+ def x(y):
+ raise KeyError
+ self.assertRaises(KeyError, findnss, x, [])
+
+ def test_first(self):
+ self.assertEquals("bla", findnss(lambda x: "bla", ["bla"]))
+
+ def test_skip_first(self):
+ def x(y):
+ if y != "bla":
+ raise KeyError
+ return "ha"
+ self.assertEquals("ha", findnss(x, ["bloe", "bla"]))
+
+
+class Disabled(object):
+ def test_setup_templatesdb(self):
+ raise NotImplementedError(self.test_setup_templatesdb)
+
+ def test_setup_registry(self):
+ raise NotImplementedError(self.test_setup_registry)
+
+ def test_setup_samdb_rootdse(self):
+ raise NotImplementedError(self.test_setup_samdb_rootdse)
+
+ def test_setup_samdb_partitions(self):
+ raise NotImplementedError(self.test_setup_samdb_partitions)
+
+ def test_create_phpldapadmin_config(self):
+ raise NotImplementedError(self.test_create_phpldapadmin_config)
+
+ def test_provision_dns(self):
+ raise NotImplementedError(self.test_provision_dns)
+
+ def test_provision_ldapbase(self):
+ raise NotImplementedError(self.test_provision_ldapbase)
+
+ def test_provision_guess(self):
+ raise NotImplementedError(self.test_provision_guess)
+
+ def test_join_domain(self):
+ raise NotImplementedError(self.test_join_domain)
+
+ def test_vampire(self):
+ raise NotImplementedError(self.test_vampire)
+
+ def test_erase_partitions(self):
+ raise NotImplementedError(self.test_erase_partitions)
+
+
diff --git a/source4/scripting/python/samba/tests/samba3.py b/source4/scripting/python/samba/tests/samba3.py
new file mode 100644
index 0000000000..71e08bdd7f
--- /dev/null
+++ b/source4/scripting/python/samba/tests/samba3.py
@@ -0,0 +1,232 @@
+#!/usr/bin/python
+
+# Unix SMB/CIFS implementation.
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 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 <http://www.gnu.org/licenses/>.
+#
+
+import unittest
+from samba.samba3 import GroupMappingDatabase, Registry, PolicyDatabase, SecretsDatabase, TdbSam
+from samba.samba3 import WinsDatabase, SmbpasswdFile, ACB_NORMAL, IdmapDatabase, SAMUser, ParamFile
+import os
+
+DATADIR=os.path.join(os.path.dirname(__file__), "../../../../../testdata/samba3")
+print "Samba 3 data dir: %s" % DATADIR
+
+class RegistryTestCase(unittest.TestCase):
+ def setUp(self):
+ self.registry = Registry(os.path.join(DATADIR, "registry.tdb"))
+
+ def tearDown(self):
+ self.registry.close()
+
+ def test_length(self):
+ self.assertEquals(28, len(self.registry))
+
+ def test_keys(self):
+ self.assertTrue("HKLM" in self.registry.keys())
+
+ def test_subkeys(self):
+ self.assertEquals(["SOFTWARE", "SYSTEM"], self.registry.subkeys("HKLM"))
+
+ def test_values(self):
+ self.assertEquals({'DisplayName': (1L, 'E\x00v\x00e\x00n\x00t\x00 \x00L\x00o\x00g\x00\x00\x00'),
+ 'ErrorControl': (4L, '\x01\x00\x00\x00')},
+ self.registry.values("HKLM/SYSTEM/CURRENTCONTROLSET/SERVICES/EVENTLOG"))
+
+
+class PolicyTestCase(unittest.TestCase):
+ def setUp(self):
+ self.policy = PolicyDatabase(os.path.join(DATADIR, "account_policy.tdb"))
+
+ def test_policy(self):
+ self.assertEquals(self.policy.min_password_length, 5)
+ self.assertEquals(self.policy.minimum_password_age, 0)
+ self.assertEquals(self.policy.maximum_password_age, 999999999)
+ self.assertEquals(self.policy.refuse_machine_password_change, 0)
+ self.assertEquals(self.policy.reset_count_minutes, 0)
+ self.assertEquals(self.policy.disconnect_time, -1)
+ self.assertEquals(self.policy.user_must_logon_to_change_password, None)
+ self.assertEquals(self.policy.password_history, 0)
+ self.assertEquals(self.policy.lockout_duration, 0)
+ self.assertEquals(self.policy.bad_lockout_minutes, None)
+
+
+class GroupsTestCase(unittest.TestCase):
+ def setUp(self):
+ self.groupdb = GroupMappingDatabase(os.path.join(DATADIR, "group_mapping.tdb"))
+
+ def tearDown(self):
+ self.groupdb.close()
+
+ def test_group_length(self):
+ self.assertEquals(13, len(list(self.groupdb.groupsids())))
+
+ def test_get_group(self):
+ self.assertEquals((-1, 5L, 'Administrators', ''), self.groupdb.get_group("S-1-5-32-544"))
+
+ def test_groupsids(self):
+ sids = list(self.groupdb.groupsids())
+ self.assertTrue("S-1-5-32-544" in sids)
+
+ def test_alias_length(self):
+ self.assertEquals(0, len(list(self.groupdb.aliases())))
+
+
+class SecretsDbTestCase(unittest.TestCase):
+ def setUp(self):
+ self.secretsdb = SecretsDatabase(os.path.join(DATADIR, "secrets.tdb"))
+
+ def tearDown(self):
+ self.secretsdb.close()
+
+ def test_get_sid(self):
+ self.assertTrue(self.secretsdb.get_sid("BEDWYR") is not None)
+
+
+class TdbSamTestCase(unittest.TestCase):
+ def setUp(self):
+ self.samdb = TdbSam(os.path.join(DATADIR, "passdb.tdb"))
+
+ def tearDown(self):
+ self.samdb.close()
+
+ def test_usernames(self):
+ self.assertEquals(3, len(list(self.samdb.usernames())))
+
+ def test_getuser(self):
+ user = SAMUser("root")
+ user.logoff_time = 2147483647
+ user.kickoff_time = 2147483647
+ user.pass_can_change_time = 1125418267
+ user.username = "root"
+ user.uid = None
+ user.lm_password = 'U)\x02\x03\x1b\xed\xe9\xef\xaa\xd3\xb45\xb5\x14\x04\xee'
+ user.nt_password = '\x87\x8d\x80\x14`l\xda)gzD\xef\xa15?\xc7'
+ user.acct_ctrl = 16
+ user.pass_last_set_time = 1125418267
+ user.fullname = "root"
+ user.nt_username = ""
+ user.logoff_time = 2147483647
+ user.acct_desc = ""
+ user.group_rid = 1001
+ user.logon_count = 0
+ user.bad_password_count = 0
+ user.domain = "BEDWYR"
+ user.munged_dial = ""
+ user.workstations = ""
+ user.user_rid = 1000
+ user.kickoff_time = 2147483647
+ user.logoff_time = 2147483647
+ user.unknown_6 = 1260L
+ user.logon_divs = 0
+ user.hours = [True for i in range(168)]
+ other = self.samdb["root"]
+ for name in other.__dict__:
+ if other.__dict__[name] != user.__dict__[name]:
+ print "%s: %r != %r" % (name, other.__dict__[name], user.__dict__[name])
+ self.assertEquals(user, other)
+
+
+class WinsDatabaseTestCase(unittest.TestCase):
+ def setUp(self):
+ self.winsdb = WinsDatabase(os.path.join(DATADIR, "wins.dat"))
+
+ def test_length(self):
+ self.assertEquals(22, len(self.winsdb))
+
+ def test_first_entry(self):
+ self.assertEqual((1124185120, ["192.168.1.5"], 0x64), self.winsdb["ADMINISTRATOR#03"])
+
+ def tearDown(self):
+ self.winsdb.close()
+
+
+class SmbpasswdTestCase(unittest.TestCase):
+ def setUp(self):
+ self.samdb = SmbpasswdFile(os.path.join(DATADIR, "smbpasswd"))
+
+ def test_length(self):
+ self.assertEquals(3, len(self.samdb))
+
+ def test_get_user(self):
+ user = SAMUser("rootpw")
+ user.lm_password = "552902031BEDE9EFAAD3B435B51404EE"
+ user.nt_password = "878D8014606CDA29677A44EFA1353FC7"
+ user.acct_ctrl = ACB_NORMAL
+ user.pass_last_set_time = int(1125418267)
+ user.uid = 0
+ self.assertEquals(user, self.samdb["rootpw"])
+
+ def tearDown(self):
+ self.samdb.close()
+
+
+class IdmapDbTestCase(unittest.TestCase):
+ def setUp(self):
+ self.idmapdb = IdmapDatabase(os.path.join(DATADIR, "winbindd_idmap.tdb"))
+
+ def test_user_hwm(self):
+ self.assertEquals(10000, self.idmapdb.get_user_hwm())
+
+ def test_group_hwm(self):
+ self.assertEquals(10002, self.idmapdb.get_group_hwm())
+
+ def test_uids(self):
+ self.assertEquals(1, len(list(self.idmapdb.uids())))
+
+ def test_gids(self):
+ self.assertEquals(3, len(list(self.idmapdb.gids())))
+
+ def test_get_user_sid(self):
+ self.assertEquals("S-1-5-21-58189338-3053988021-627566699-501", self.idmapdb.get_user_sid(65534))
+
+ def test_get_group_sid(self):
+ self.assertEquals("S-1-5-21-2447931902-1787058256-3961074038-3007", self.idmapdb.get_group_sid(10001))
+
+ def tearDown(self):
+ self.idmapdb.close()
+
+
+class ShareInfoTestCase(unittest.TestCase):
+ def setUp(self):
+ self.shareinfodb = ShareInfoDatabase(os.path.join(DATADIR, "share_info.tdb"))
+
+ # FIXME: needs proper data so it can be tested
+
+ def tearDown(self):
+ self.shareinfodb.close()
+
+
+class ParamTestCase(unittest.TestCase):
+ def test_init(self):
+ file = ParamFile()
+ self.assertTrue(file is not None)
+
+ def test_add_section(self):
+ file = ParamFile()
+ file.add_section("global")
+ self.assertTrue(file["global"] is not None)
+
+ def test_set_param_string(self):
+ file = ParamFile()
+ file.add_section("global")
+ file.set_string("data", "bar")
+ self.assertEquals("bar", file.get_string("data"))
+
+ def test_get_section(self):
+ file = ParamFile()
+ self.assertEquals(None, file.get_section("unknown"))
+ self.assertRaises(KeyError, lambda: file["unknown"])
diff --git a/source4/scripting/python/samba/tests/samdb.py b/source4/scripting/python/samba/tests/samdb.py
new file mode 100644
index 0000000000..d0b95cf542
--- /dev/null
+++ b/source4/scripting/python/samba/tests/samdb.py
@@ -0,0 +1,97 @@
+#!/usr/bin/python
+
+# Unix SMB/CIFS implementation. Tests for SamDB
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 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 <http://www.gnu.org/licenses/>.
+#
+from samba.auth import system_session
+from samba.credentials import Credentials
+import os
+from samba.provision import setup_samdb, guess_names, setup_templatesdb, make_smbconf, find_setup_dir
+from samba.samdb import SamDB
+from samba.tests import TestCaseInTempDir
+from samba.dcerpc import security
+from unittest import TestCase
+import uuid
+from samba import param
+
+
+class SamDBTestCase(TestCaseInTempDir):
+ """Base-class for tests with a Sam Database.
+
+ This is used by the Samba SamDB-tests, but e.g. also by the OpenChange
+ provisioning tests (which need a Sam).
+ """
+
+ def setup_path(self, relpath):
+ return os.path.join(find_setup_dir(), relpath)
+
+ def setUp(self):
+ super(SamDBTestCase, self).setUp()
+ invocationid = str(uuid.uuid4())
+ domaindn = "DC=COM,DC=EXAMPLE"
+ self.domaindn = domaindn
+ configdn = "CN=Configuration," + domaindn
+ schemadn = "CN=Schema," + configdn
+ domainguid = str(uuid.uuid4())
+ policyguid = str(uuid.uuid4())
+ creds = Credentials()
+ creds.set_anonymous()
+ domainsid = security.random_sid()
+ hostguid = str(uuid.uuid4())
+ path = os.path.join(self.tempdir, "samdb.ldb")
+ session_info = system_session()
+
+ hostname="foo"
+ domain="EXAMPLE"
+ dnsdomain="example.com"
+ serverrole="domain controller"
+
+ smbconf = os.path.join(self.tempdir, "smb.conf")
+ make_smbconf(smbconf, self.setup_path, hostname, domain, dnsdomain,
+ serverrole, self.tempdir)
+
+ self.lp = param.LoadParm()
+ self.lp.load(smbconf)
+
+ names = guess_names(lp=self.lp, hostname=hostname,
+ domain=domain, dnsdomain=dnsdomain,
+ serverrole=serverrole,
+ domaindn=self.domaindn, configdn=configdn,
+ schemadn=schemadn)
+ setup_templatesdb(os.path.join(self.tempdir, "templates.ldb"),
+ self.setup_path, session_info=session_info,
+ credentials=creds, lp=self.lp)
+ self.samdb = setup_samdb(path, self.setup_path, session_info, creds,
+ self.lp, names,
+ lambda x: None, domainsid,
+ "# no aci", domainguid,
+ policyguid, False, "secret",
+ "secret", "secret", invocationid,
+ "secret", "domain controller")
+
+ def tearDown(self):
+ for f in ['templates.ldb', 'schema.ldb', 'configuration.ldb',
+ 'users.ldb', 'samdb.ldb', 'smb.conf']:
+ os.remove(os.path.join(self.tempdir, f))
+ super(SamDBTestCase, self).tearDown()
+
+
+class SamDBTests(SamDBTestCase):
+ """Tests for the SamDB implementation."""
+
+ def test_add_foreign(self):
+ self.samdb.add_foreign(self.domaindn, "S-1-5-7", "Somedescription")
+
diff --git a/source4/scripting/python/samba/tests/upgrade.py b/source4/scripting/python/samba/tests/upgrade.py
new file mode 100644
index 0000000000..17ebfa7bf5
--- /dev/null
+++ b/source4/scripting/python/samba/tests/upgrade.py
@@ -0,0 +1,37 @@
+#!/usr/bin/python
+
+# Unix SMB/CIFS implementation.
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 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 <http://www.gnu.org/licenses/>.
+#
+
+from samba import Ldb
+from samba.upgrade import import_wins
+from samba.tests import LdbTestCase
+
+class WinsUpgradeTests(LdbTestCase):
+ def test_upgrade(self):
+ winsdb = {
+ "FOO#20": (200, ["127.0.0.1", "127.0.0.2"], 0x60)
+ }
+ import_wins(self.ldb, winsdb)
+
+ self.assertEquals(['name=FOO,type=0x20'],
+ [str(m.dn) for m in self.ldb.search(expression="(objectClass=winsRecord)")])
+
+ def test_version(self):
+ import_wins(self.ldb, {})
+ self.assertEquals("VERSION",
+ str(self.ldb.search(expression="(objectClass=winsMaxVersion)")[0]["cn"]))
diff --git a/source4/scripting/python/samba/torture/pytorture b/source4/scripting/python/samba/torture/pytorture
new file mode 100755
index 0000000000..e0123447e8
--- /dev/null
+++ b/source4/scripting/python/samba/torture/pytorture
@@ -0,0 +1,51 @@
+#!/usr/bin/python
+
+import sys
+from optparse import OptionParser
+
+# Parse command line
+
+parser = OptionParser()
+
+parser.add_option("-b", "--binding", action="store", type="string",
+ dest="binding")
+
+parser.add_option("-d", "--domain", action="store", type="string",
+ dest="domain")
+
+parser.add_option("-u", "--username", action="store", type="string",
+ dest="username")
+
+parser.add_option("-p", "--password", action="store", type="string",
+ dest="password")
+
+(options, args) = parser.parse_args()
+
+if not options.binding:
+ parser.error('You must supply a binding string')
+
+if not options.username or not options.password or not options.domain:
+ parser.error('You must supply a domain, username and password')
+
+binding = options.binding
+domain = options.domain
+username = options.username
+password = options.password
+
+if len(args) == 0:
+ parser.error('You must supply the name of a module to test')
+
+# Import and test
+
+for test in args:
+
+ try:
+ module = __import__('torture_%s' % test)
+ except ImportError:
+ print 'No such module "%s"' % test
+ sys.exit(1)
+
+ if not hasattr(module, 'runtests'):
+ print 'Module "%s" does not have a runtests function' % test
+
+ module.runtests(binding, (domain, username, password))
diff --git a/source4/scripting/python/samba/torture/spoolss.py b/source4/scripting/python/samba/torture/spoolss.py
new file mode 100644
index 0000000000..a75385e079
--- /dev/null
+++ b/source4/scripting/python/samba/torture/spoolss.py
@@ -0,0 +1,437 @@
+import sys, string
+import dcerpc
+
+
+def ResizeBufferCall(fn, pipe, r):
+
+ r['buffer'] = None
+ r['buf_size'] = 0
+
+ result = fn(pipe, r)
+
+ if result['result'] == dcerpc.WERR_INSUFFICIENT_BUFFER or \
+ result['result'] == dcerpc.WERR_MORE_DATA:
+ r['buffer'] = result['buf_size'] * '\x00'
+ r['buf_size'] = result['buf_size']
+
+ result = fn(pipe, r)
+
+ return result
+
+
+def test_OpenPrinterEx(pipe, printer):
+
+ print 'spoolss_OpenPrinterEx(%s)' % printer
+
+ printername = '\\\\%s' % dcerpc.dcerpc_server_name(pipe)
+
+ if printer is not None:
+ printername = printername + '\\%s' % printer
+
+ r = {}
+ r['printername'] = printername
+ r['datatype'] = None
+ r['devmode_ctr'] = {}
+ r['devmode_ctr']['size'] = 0
+ r['devmode_ctr']['devmode'] = None
+ r['access_mask'] = 0x02000000
+ r['level'] = 1
+ r['userlevel'] = {}
+ r['userlevel']['level1'] = {}
+ r['userlevel']['level1']['size'] = 0
+ r['userlevel']['level1']['client'] = None
+ r['userlevel']['level1']['user'] = None
+ r['userlevel']['level1']['build'] = 1381
+ r['userlevel']['level1']['major'] = 2
+ r['userlevel']['level1']['minor'] = 0
+ r['userlevel']['level1']['processor'] = 0
+
+ result = dcerpc.spoolss_OpenPrinterEx(pipe, r)
+
+ return result['handle']
+
+
+def test_ClosePrinter(pipe, handle):
+
+ r = {}
+ r['handle'] = handle
+
+ dcerpc.spoolss_ClosePrinter(pipe, r)
+
+
+def test_GetPrinter(pipe, handle):
+
+ r = {}
+ r['handle'] = handle
+
+ for level in [0, 1, 2, 3, 4, 5, 6, 7]:
+
+ print 'spoolss_GetPrinter(level = %d)' % level
+
+ r['level'] = level
+ r['buffer'] = None
+ r['buf_size'] = 0
+
+ result = ResizeBufferCall(dcerpc.spoolss_GetPrinter, pipe, r)
+
+
+def test_EnumForms(pipe, handle):
+
+ print 'spoolss_EnumForms()'
+
+ r = {}
+ r['handle'] = handle
+ r['level'] = 1
+ r['buffer'] = None
+ r['buf_size'] = 0
+
+ result = ResizeBufferCall(dcerpc.spoolss_EnumForms, pipe, r)
+
+ forms = dcerpc.unmarshall_spoolss_FormInfo_array(
+ result['buffer'], r['level'], result['count'])
+
+ for form in forms:
+
+ r = {}
+ r['handle'] = handle
+ r['formname'] = form['info1']['formname']
+ r['level'] = 1
+
+ result = ResizeBufferCall(dcerpc.spoolss_GetForm, pipe, r)
+
+
+def test_EnumPorts(pipe, handle):
+
+ print 'spoolss_EnumPorts()'
+
+ for level in [1, 2]:
+
+ r = {}
+ r['handle'] = handle
+ r['servername'] = None
+ r['level'] = level
+
+ result = ResizeBufferCall(dcerpc.spoolss_EnumPorts, pipe, r)
+
+ ports = dcerpc.unmarshall_spoolss_PortInfo_array(
+ result['buffer'], r['level'], result['count'])
+
+ if level == 1:
+ port_names = map(lambda x: x['info1']['port_name'], ports)
+
+
+def test_DeleteForm(pipe, handle, formname):
+
+ r = {}
+ r['handle'] = handle
+ r['formname'] = formname
+
+ dcerpc.spoolss_DeleteForm(pipe, r)
+
+
+def test_GetForm(pipe, handle, formname):
+
+ r = {}
+ r['handle'] = handle
+ r['formname'] = formname
+ r['level'] = 1
+
+ result = ResizeBufferCall(dcerpc.spoolss_GetForm, pipe, r)
+
+ return result['info']['info1']
+
+
+def test_SetForm(pipe, handle, form):
+
+ print 'spoolss_SetForm()'
+
+ r = {}
+ r['handle'] = handle
+ r['level'] = 1
+ r['formname'] = form['info1']['formname']
+ r['info'] = form
+
+ dcerpc.spoolss_SetForm(pipe, r)
+
+ newform = test_GetForm(pipe, handle, r['formname'])
+
+ if form['info1'] != newform:
+ print 'SetForm: mismatch: %s != %s' % \
+ (r['info']['info1'], f)
+ sys.exit(1)
+
+
+def test_AddForm(pipe, handle):
+
+ print 'spoolss_AddForm()'
+
+ formname = '__testform__'
+
+ r = {}
+ r['handle'] = handle
+ r['level'] = 1
+ r['info'] = {}
+ r['info']['info1'] = {}
+ r['info']['info1']['formname'] = formname
+ r['info']['info1']['flags'] = 0x0002
+ r['info']['info1']['width'] = 100
+ r['info']['info1']['length'] = 100
+ r['info']['info1']['left'] = 0
+ r['info']['info1']['top'] = 1000
+ r['info']['info1']['right'] = 2000
+ r['info']['info1']['bottom'] = 3000
+
+ try:
+ result = dcerpc.spoolss_AddForm(pipe, r)
+ except dcerpc.WERROR, arg:
+ if arg[0] == dcerpc.WERR_ALREADY_EXISTS:
+ test_DeleteForm(pipe, handle, formname)
+ result = dcerpc.spoolss_AddForm(pipe, r)
+
+ f = test_GetForm(pipe, handle, formname)
+
+ if r['info']['info1'] != f:
+ print 'AddForm: mismatch: %s != %s' % \
+ (r['info']['info1'], f)
+ sys.exit(1)
+
+ r['formname'] = formname
+
+ test_SetForm(pipe, handle, r['info'])
+
+ test_DeleteForm(pipe, handle, formname)
+
+
+def test_EnumJobs(pipe, handle):
+
+ print 'spoolss_EnumJobs()'
+
+ r = {}
+ r['handle'] = handle
+ r['firstjob'] = 0
+ r['numjobs'] = 0xffffffff
+ r['level'] = 1
+
+ result = ResizeBufferCall(dcerpc.spoolss_EnumJobs, pipe, r)
+
+ if result['buffer'] is None:
+ return
+
+ jobs = dcerpc.unmarshall_spoolss_JobInfo_array(
+ result['buffer'], r['level'], result['count'])
+
+ for job in jobs:
+
+ s = {}
+ s['handle'] = handle
+ s['job_id'] = job['info1']['job_id']
+ s['level'] = 1
+
+ result = ResizeBufferCall(dcerpc.spoolss_GetJob, pipe, s)
+
+ if result['info'] != job:
+ print 'EnumJobs: mismatch: %s != %s' % (result['info'], job)
+ sys.exit(1)
+
+
+ # TODO: AddJob, DeleteJob, ScheduleJob
+
+
+def test_EnumPrinterData(pipe, handle):
+
+ print 'test_EnumPrinterData()'
+
+ enum_index = 0
+
+ while 1:
+
+ r = {}
+ r['handle'] = handle
+ r['enum_index'] = enum_index
+
+ r['value_offered'] = 0
+ r['data_size'] = 0
+
+ result = dcerpc.spoolss_EnumPrinterData(pipe, r)
+
+ r['value_offered'] = result['value_needed']
+ r['data_size'] = result['data_size']
+
+ result = dcerpc.spoolss_EnumPrinterData(pipe, r)
+
+ if result['result'] == dcerpc.WERR_NO_MORE_ITEMS:
+ break
+
+ s = {}
+ s['handle'] = handle
+ s['value_name'] = result['value_name']
+
+ result2 = ResizeBufferCall(dcerpc.spoolss_GetPrinterData, pipe, s)
+
+ if result['buffer'][:result2['buf_size']] != result2['buffer']:
+ print 'EnumPrinterData/GetPrinterData mismatch'
+ sys.exit(1)
+
+ enum_index += 1
+
+
+def test_SetPrinterDataEx(pipe, handle):
+
+ valuename = '__printerdataextest__'
+ data = '12345'
+
+ r = {}
+ r['handle'] = handle
+ r['key_name'] = 'DsSpooler'
+ r['value_name'] = valuename
+ r['type'] = 3
+ r['buffer'] = data
+ r['buf_size'] = len(data)
+
+ result = dcerpc.spoolss_SetPrinterDataEx(pipe, r)
+
+
+def test_EnumPrinterDataEx(pipe, handle):
+
+ r = {}
+ r['handle'] = handle
+ r['key_name'] = 'DsSpooler'
+ r['buf_size'] = 0
+
+ result = dcerpc.spoolss_EnumPrinterDataEx(pipe, r)
+
+ if result['result'] == dcerpc.WERR_MORE_DATA:
+ r['buf_size'] = result['buf_size']
+
+ result = dcerpc.spoolss_EnumPrinterDataEx(pipe, r)
+
+ # TODO: test spoolss_GetPrinterDataEx()
+
+
+def test_SetPrinterData(pipe, handle):
+
+ print 'testing spoolss_SetPrinterData()'
+
+ valuename = '__printerdatatest__'
+ data = '12345'
+
+ r = {}
+ r['handle'] = handle
+ r['value_name'] = valuename
+ r['type'] = 3 # REG_BINARY
+ r['buffer'] = data
+ r['real_len'] = 5
+
+ dcerpc.spoolss_SetPrinterData(pipe, r)
+
+ s = {}
+ s['handle'] = handle
+ s['value_name'] = valuename
+
+ result = ResizeBufferCall(dcerpc.spoolss_GetPrinterData, pipe, r)
+
+ if result['buffer'] != data:
+ print 'SetPrinterData: mismatch'
+ sys.exit(1)
+
+ dcerpc.spoolss_DeletePrinterData(pipe, r)
+
+
+def test_EnumPrinters(pipe):
+
+ print 'testing spoolss_EnumPrinters()'
+
+ printer_names = None
+
+ r = {}
+ r['flags'] = 0x02
+ r['server'] = None
+
+ for level in [0, 1, 2, 4, 5]:
+
+ print 'test_EnumPrinters(level = %d)' % level
+
+ r['level'] = level
+
+ result = ResizeBufferCall(dcerpc.spoolss_EnumPrinters, pipe, r)
+
+ printers = dcerpc.unmarshall_spoolss_PrinterInfo_array(
+ result['buffer'], r['level'], result['count'])
+
+ if level == 2:
+ for p in printers:
+
+ # A nice check is for the specversion in the
+ # devicemode. This has always been observed to be
+ # 1025.
+
+ if p['info2']['devmode']['specversion'] != 1025:
+ print 'test_EnumPrinters: specversion != 1025'
+ sys.exit(1)
+
+ r['level'] = 1
+ result = ResizeBufferCall(dcerpc.spoolss_EnumPrinters, pipe, r)
+
+ for printer in dcerpc.unmarshall_spoolss_PrinterInfo_array(
+ result['buffer'], r['level'], result['count']):
+
+ if string.find(printer['info1']['name'], '\\\\') == 0:
+ print 'Skipping remote printer %s' % printer['info1']['name']
+ continue
+
+ printername = string.split(printer['info1']['name'], ',')[0]
+
+ handle = test_OpenPrinterEx(pipe, printername)
+
+ test_GetPrinter(pipe, handle)
+ test_EnumPorts(pipe, handle)
+ test_EnumForms(pipe, handle)
+ test_AddForm(pipe, handle)
+ test_EnumJobs(pipe, handle)
+ test_EnumPrinterData(pipe, handle)
+ test_EnumPrinterDataEx(pipe, handle)
+ test_SetPrinterData(pipe, handle)
+# test_SetPrinterDataEx(pipe, handle)
+ test_ClosePrinter(pipe, handle)
+
+
+def test_EnumPrinterDrivers(pipe):
+
+ print 'test spoolss_EnumPrinterDrivers()'
+
+ for level in [1, 2, 3]:
+
+ r = {}
+ r['server'] = None
+ r['environment'] = None
+ r['level'] = level
+
+ result = ResizeBufferCall(dcerpc.spoolss_EnumPrinterDrivers, pipe, r)
+
+ drivers = dcerpc.unmarshall_spoolss_DriverInfo_array(
+ result['buffer'], r['level'], result['count'])
+
+ if level == 1:
+ driver_names = map(lambda x: x['info1']['driver_name'], drivers)
+
+
+def test_PrintServer(pipe):
+
+ handle = test_OpenPrinterEx(pipe, None)
+
+ # EnumForms and AddForm tests return WERR_BADFID here (??)
+
+ test_ClosePrinter(pipe, handle)
+
+
+def runtests(binding, domain, username, password):
+
+ print 'Testing SPOOLSS pipe'
+
+ pipe = dcerpc.pipe_connect(binding,
+ dcerpc.DCERPC_SPOOLSS_UUID, dcerpc.DCERPC_SPOOLSS_VERSION,
+ domain, username, password)
+
+ test_EnumPrinters(pipe)
+ test_EnumPrinterDrivers(pipe)
+ test_PrintServer(pipe)
diff --git a/source4/scripting/python/samba/torture/torture_samr.py b/source4/scripting/python/samba/torture/torture_samr.py
new file mode 100755
index 0000000000..15c6dc1a76
--- /dev/null
+++ b/source4/scripting/python/samba/torture/torture_samr.py
@@ -0,0 +1,221 @@
+#!/usr/bin/python
+
+import sys
+import dcerpc, samr
+
+def test_Connect(pipe):
+
+ handle = samr.Connect(pipe)
+ handle = samr.Connect2(pipe)
+ handle = samr.Connect3(pipe)
+ handle = samr.Connect4(pipe)
+
+ # WIN2K3 only?
+
+ try:
+ handle = samr.Connect5(pipe)
+ except dcerpc.NTSTATUS, arg:
+ if arg[0] != 0xc00000d2L: # NT_STATUS_NET_WRITE_FAULT
+ raise
+
+ return handle
+
+def test_UserHandle(user_handle):
+
+ # QuerySecurity()/SetSecurity()
+
+ user_handle.SetSecurity(user_handle.QuerySecurity())
+
+ # GetUserPwInfo()
+
+ user_handle.GetUserPwInfo()
+
+ # GetUserInfo()
+
+ for level in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 20,
+ 21, 23, 24, 25, 26]:
+
+ try:
+ user_handle.QueryUserInfo(level)
+ user_handle.QueryUserInfo2(level)
+ except dcerpc.NTSTATUS, arg:
+ if arg[0] != 0xc0000003L: # NT_STATUS_INVALID_INFO_CLASS
+ raise
+
+ # GetGroupsForUser()
+
+ user_handle.GetGroupsForUser()
+
+ # TestPrivateFunctionsUser()
+
+ try:
+ user_handle.TestPrivateFunctionsUser()
+ except dcerpc.NTSTATUS, arg:
+ if arg[0] != 0xC0000002L:
+ raise
+
+def test_GroupHandle(group_handle):
+
+ # QuerySecurity()/SetSecurity()
+
+ group_handle.SetSecurity(group_handle.QuerySecurity())
+
+ # QueryGroupInfo()
+
+ for level in [1, 2, 3, 4, 5]:
+ info = group_handle.QueryGroupInfo(level)
+
+ # TODO: SetGroupinfo()
+
+ # QueryGroupMember()
+
+ group_handle.QueryGroupMember()
+
+def test_AliasHandle(alias_handle):
+
+ # QuerySecurity()/SetSecurity()
+
+ alias_handle.SetSecurity(alias_handle.QuerySecurity())
+
+ print alias_handle.GetMembersInAlias()
+
+def test_DomainHandle(name, sid, domain_handle):
+
+ print 'testing %s (%s)' % (name, sid)
+
+ # QuerySecurity()/SetSecurity()
+
+ domain_handle.SetSecurity(domain_handle.QuerySecurity())
+
+ # LookupNames(), none mapped
+
+ try:
+ domain_handle.LookupNames(['xxNONAMExx'])
+ except dcerpc.NTSTATUS, arg:
+ if arg[0] != 0xc0000073L:
+ raise dcerpc.NTSTATUS(arg)
+
+ # LookupNames(), some mapped
+
+ if name != 'Builtin':
+ domain_handle.LookupNames(['Administrator', 'xxNONAMExx'])
+
+ # QueryDomainInfo()/SetDomainInfo()
+
+ levels = [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13]
+ set_ok = [1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0]
+
+ for i in range(len(levels)):
+
+ info = domain_handle.QueryDomainInfo(level = levels[i])
+
+ try:
+ domain_handle.SetDomainInfo(levels[i], info)
+ except dcerpc.NTSTATUS, arg:
+ if not (arg[0] == 0xc0000003L and not set_ok[i]):
+ raise
+
+ # QueryDomainInfo2()
+
+ levels = [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13]
+
+ for i in range(len(levels)):
+ domain_handle.QueryDomainInfo2(level = levels[i])
+
+ # EnumDomainUsers
+
+ print 'testing users'
+
+ users = domain_handle.EnumDomainUsers()
+ rids = domain_handle.LookupNames(users)
+
+ for i in range(len(users)):
+ test_UserHandle(domain_handle.OpenUser(rids[0][i]))
+
+ # QueryDisplayInfo
+
+ for i in [1, 2, 3, 4, 5]:
+ domain_handle.QueryDisplayInfo(level = i)
+ domain_handle.QueryDisplayInfo2(level = i)
+ domain_handle.QueryDisplayInfo3(level = i)
+
+ # EnumDomainGroups
+
+ print 'testing groups'
+
+ groups = domain_handle.EnumDomainGroups()
+ rids = domain_handle.LookupNames(groups)
+
+ for i in range(len(groups)):
+ test_GroupHandle(domain_handle.OpenGroup(rids[0][i]))
+
+ # EnumDomainAliases
+
+ print 'testing aliases'
+
+ aliases = domain_handle.EnumDomainAliases()
+ rids = domain_handle.LookupNames(aliases)
+
+ for i in range(len(aliases)):
+ test_AliasHandle(domain_handle.OpenAlias(rids[0][i]))
+
+ # CreateUser
+ # CreateUser2
+ # CreateDomAlias
+ # RidToSid
+ # RemoveMemberFromForeignDomain
+ # CreateDomainGroup
+ # GetAliasMembership
+
+ # GetBootKeyInformation()
+
+ try:
+ domain_handle.GetBootKeyInformation()
+ except dcerpc.NTSTATUS, arg:
+ pass
+
+ # TestPrivateFunctionsDomain()
+
+ try:
+ domain_handle.TestPrivateFunctionsDomain()
+ except dcerpc.NTSTATUS, arg:
+ if arg[0] != 0xC0000002L:
+ raise
+
+def test_ConnectHandle(connect_handle):
+
+ print 'testing connect handle'
+
+ # QuerySecurity/SetSecurity
+
+ connect_handle.SetSecurity(connect_handle.QuerySecurity())
+
+ # Lookup bogus domain
+
+ try:
+ connect_handle.LookupDomain('xxNODOMAINxx')
+ except dcerpc.NTSTATUS, arg:
+ if arg[0] != 0xC00000DFL: # NT_STATUS_NO_SUCH_DOMAIN
+ raise
+
+ # Test all domains
+
+ for domain_name in connect_handle.EnumDomains():
+
+ connect_handle.GetDomPwInfo(domain_name)
+ sid = connect_handle.LookupDomain(domain_name)
+ domain_handle = connect_handle.OpenDomain(sid)
+
+ test_DomainHandle(domain_name, sid, domain_handle)
+
+ # TODO: Test Shutdown() function
+
+def runtests(binding, creds):
+
+ print 'Testing SAMR pipe'
+
+ pipe = dcerpc.pipe_connect(binding,
+ dcerpc.DCERPC_SAMR_UUID, int(dcerpc.DCERPC_SAMR_VERSION), creds)
+
+ handle = test_Connect(pipe)
+ test_ConnectHandle(handle)
diff --git a/source4/scripting/python/samba/torture/torture_tdb.py b/source4/scripting/python/samba/torture/torture_tdb.py
new file mode 100755
index 0000000000..7f97caf6cb
--- /dev/null
+++ b/source4/scripting/python/samba/torture/torture_tdb.py
@@ -0,0 +1,90 @@
+#!/usr/bin/python
+
+import sys, os
+import Tdb
+
+def fail(msg):
+ print 'FAILED:', msg
+ sys.exit(1)
+
+tdb_file = '/tmp/torture_tdb.tdb'
+
+# Create temporary tdb file
+
+t = Tdb.Tdb(tdb_file, flags = Tdb.CLEAR_IF_FIRST)
+
+# Check non-existent key throws KeyError exception
+
+try:
+ t['__none__']
+except KeyError:
+ pass
+else:
+ fail('non-existent key did not throw KeyError')
+
+# Check storing key
+
+t['bar'] = '1234'
+if t['bar'] != '1234':
+ fail('store key failed')
+
+# Check key exists
+
+if not t.has_key('bar'):
+ fail('has_key() failed for existing key')
+
+if t.has_key('__none__'):
+ fail('has_key() succeeded for non-existent key')
+
+# Delete key
+
+try:
+ del(t['__none__'])
+except KeyError:
+ pass
+else:
+ fail('delete of non-existent key did not throw KeyError')
+
+del t['bar']
+if t.has_key('bar'):
+ fail('delete of existing key did not delete key')
+
+# Clear all keys
+
+t.clear()
+if len(t) != 0:
+ fail('clear failed to remove all keys')
+
+# Other dict functions
+
+t['a'] = '1'
+t['ab'] = '12'
+t['abc'] = '123'
+
+if len(t) != 3:
+ fail('len method produced wrong value')
+
+keys = t.keys()
+values = t.values()
+items = t.items()
+
+if set(keys) != set(['a', 'ab', 'abc']):
+ fail('keys method produced wrong values')
+
+if set(values) != set(['1', '12', '123']):
+ fail('values method produced wrong values')
+
+if set(items) != set([('a', '1'), ('ab', '12'), ('abc', '123')]):
+ fail('values method produced wrong values')
+
+t.close()
+
+# Re-open read-only
+
+t = Tdb.Tdb(tdb_file, open_flags = os.O_RDONLY)
+t.keys()
+t.close()
+
+# Clean up
+
+os.unlink(tdb_file)
diff --git a/source4/scripting/python/samba/torture/winreg.py b/source4/scripting/python/samba/torture/winreg.py
new file mode 100755
index 0000000000..eb60b9847e
--- /dev/null
+++ b/source4/scripting/python/samba/torture/winreg.py
@@ -0,0 +1,165 @@
+#!/usr/bin/python
+
+import sys, dcerpc
+
+def test_OpenHKLM(pipe):
+
+ r = {}
+ r['unknown'] = {}
+ r['unknown']['unknown0'] = 0x9038
+ r['unknown']['unknown1'] = 0x0000
+ r['access_required'] = 0x02000000
+
+ result = dcerpc.winreg_OpenHKLM(pipe, r)
+
+ return result['handle']
+
+def test_QueryInfoKey(pipe, handle):
+
+ r = {}
+ r['handle'] = handle
+ r['class'] = {}
+ r['class']['name'] = None
+
+ return dcerpc.winreg_QueryInfoKey(pipe, r)
+
+def test_CloseKey(pipe, handle):
+
+ r = {}
+ r['handle'] = handle
+
+ dcerpc.winreg_CloseKey(pipe, r)
+
+def test_FlushKey(pipe, handle):
+
+ r = {}
+ r['handle'] = handle
+
+ dcerpc.winreg_FlushKey(pipe, r)
+
+def test_GetVersion(pipe, handle):
+
+ r = {}
+ r['handle'] = handle
+
+ dcerpc.winreg_GetVersion(pipe, r)
+
+def test_GetKeySecurity(pipe, handle):
+
+ r = {}
+ r['handle'] = handle
+ r['unknown'] = 4
+ r['size'] = None
+ r['data'] = {}
+ r['data']['max_len'] = 0
+ r['data']['data'] = ''
+
+ result = dcerpc.winreg_GetKeySecurity(pipe, r)
+
+ print result
+
+ if result['result'] == dcerpc.WERR_INSUFFICIENT_BUFFER:
+ r['size'] = {}
+ r['size']['max_len'] = result['data']['max_len']
+ r['size']['offset'] = 0
+ r['size']['len'] = result['data']['max_len']
+
+ result = dcerpc.winreg_GetKeySecurity(pipe, r)
+
+ print result
+
+ sys.exit(1)
+
+def test_Key(pipe, handle, name, depth = 0):
+
+ # Don't descend too far. Registries can be very deep.
+
+ if depth > 2:
+ return
+
+ try:
+ keyinfo = test_QueryInfoKey(pipe, handle)
+ except dcerpc.WERROR, arg:
+ if arg[0] == dcerpc.WERR_ACCESS_DENIED:
+ return
+
+ test_GetVersion(pipe, handle)
+
+ test_FlushKey(pipe, handle)
+
+ test_GetKeySecurity(pipe, handle)
+
+ # Enumerate values in this key
+
+ r = {}
+ r['handle'] = handle
+ r['name_in'] = {}
+ r['name_in']['len'] = 0
+ r['name_in']['max_len'] = (keyinfo['max_valnamelen'] + 1) * 2
+ r['name_in']['buffer'] = {}
+ r['name_in']['buffer']['max_len'] = keyinfo['max_valnamelen'] + 1
+ r['name_in']['buffer']['offset'] = 0
+ r['name_in']['buffer']['len'] = 0
+ r['type'] = 0
+ r['value_in'] = {}
+ r['value_in']['max_len'] = keyinfo['max_valbufsize']
+ r['value_in']['offset'] = 0
+ r['value_in']['len'] = 0
+ r['value_len1'] = keyinfo['max_valbufsize']
+ r['value_len2'] = 0
+
+ for i in range(0, keyinfo['num_values']):
+
+ r['enum_index'] = i
+
+ dcerpc.winreg_EnumValue(pipe, r)
+
+ # Recursively test subkeys of this key
+
+ r = {}
+ r['handle'] = handle
+ r['key_name_len'] = 0
+ r['unknown'] = 0x0414
+ r['in_name'] = {}
+ r['in_name']['unknown'] = 0x20a
+ r['in_name']['key_name'] = {}
+ r['in_name']['key_name']['name'] = None
+ r['class'] = {}
+ r['class']['name'] = None
+ r['last_changed_time'] = {}
+ r['last_changed_time']['low'] = 0
+ r['last_changed_time']['high'] = 0
+
+ for i in range(0, keyinfo['num_subkeys']):
+
+ r['enum_index'] = i
+
+ subkey = dcerpc.winreg_EnumKey(pipe, r)
+
+ s = {}
+ s['handle'] = handle
+ s['keyname'] = {}
+ s['keyname']['name'] = subkey['out_name']['name']
+ s['unknown'] = 0
+ s['access_mask'] = 0x02000000
+
+ result = dcerpc.winreg_OpenKey(pipe, s)
+
+ test_Key(pipe, result['handle'], name + '/' + s['keyname']['name'],
+ depth + 1)
+
+ test_CloseKey(pipe, result['handle'])
+
+ # Enumerate values
+
+def runtests(binding, domain, username, password):
+
+ print 'Testing WINREG pipe'
+
+ pipe = dcerpc.pipe_connect(binding,
+ dcerpc.DCERPC_WINREG_UUID, dcerpc.DCERPC_WINREG_VERSION,
+ domain, username, password)
+
+ handle = test_OpenHKLM(pipe)
+
+ test_Key(pipe, handle, 'HKLM')
diff --git a/source4/scripting/python/samba/upgrade.py b/source4/scripting/python/samba/upgrade.py
new file mode 100644
index 0000000000..0c83604e82
--- /dev/null
+++ b/source4/scripting/python/samba/upgrade.py
@@ -0,0 +1,437 @@
+#!/usr/bin/python
+#
+# backend code for upgrading from Samba3
+# Copyright Jelmer Vernooij 2005-2007
+# Released under the GNU GPL v3 or later
+#
+
+"""Support code for upgrading from Samba 3 to Samba 4."""
+
+__docformat__ = "restructuredText"
+
+from provision import findnss, provision, FILL_DRS
+import grp
+import ldb
+import time
+import pwd
+import uuid
+import registry
+from samba import Ldb
+from samba.samdb import SamDB
+
+def import_sam_policy(samldb, samba3_policy, domaindn):
+ """Import a Samba 3 policy database."""
+ samldb.modify_ldif("""
+dn: %s
+changetype: modify
+replace: minPwdLength
+minPwdLength: %d
+pwdHistoryLength: %d
+minPwdAge: %d
+maxPwdAge: %d
+lockoutDuration: %d
+samba3ResetCountMinutes: %d
+samba3UserMustLogonToChangePassword: %d
+samba3BadLockoutMinutes: %d
+samba3DisconnectTime: %d
+
+""" % (dn, policy.min_password_length,
+ policy.password_history, policy.minimum_password_age,
+ policy.maximum_password_age, policy.lockout_duration,
+ policy.reset_count_minutes, policy.user_must_logon_to_change_password,
+ policy.bad_lockout_minutes, policy.disconnect_time))
+
+
+def import_sam_account(samldb,acc,domaindn,domainsid):
+ """Import a Samba 3 SAM account.
+
+ :param samldb: Samba 4 SAM Database handle
+ :param acc: Samba 3 account
+ :param domaindn: Domain DN
+ :param domainsid: Domain SID."""
+ if acc.nt_username is None or acc.nt_username == "":
+ acc.nt_username = acc.username
+
+ if acc.fullname is None:
+ try:
+ acc.fullname = pwd.getpwnam(acc.username)[4].split(",")[0]
+ except KeyError:
+ pass
+
+ if acc.fullname is None:
+ acc.fullname = acc.username
+
+ assert acc.fullname is not None
+ assert acc.nt_username is not None
+
+ samldb.add({
+ "dn": "cn=%s,%s" % (acc.fullname, domaindn),
+ "objectClass": ["top", "user"],
+ "lastLogon": str(acc.logon_time),
+ "lastLogoff": str(acc.logoff_time),
+ "unixName": acc.username,
+ "sAMAccountName": acc.nt_username,
+ "cn": acc.nt_username,
+ "description": acc.acct_desc,
+ "primaryGroupID": str(acc.group_rid),
+ "badPwdcount": str(acc.bad_password_count),
+ "logonCount": str(acc.logon_count),
+ "samba3Domain": acc.domain,
+ "samba3DirDrive": acc.dir_drive,
+ "samba3MungedDial": acc.munged_dial,
+ "samba3Homedir": acc.homedir,
+ "samba3LogonScript": acc.logon_script,
+ "samba3ProfilePath": acc.profile_path,
+ "samba3Workstations": acc.workstations,
+ "samba3KickOffTime": str(acc.kickoff_time),
+ "samba3BadPwdTime": str(acc.bad_password_time),
+ "samba3PassLastSetTime": str(acc.pass_last_set_time),
+ "samba3PassCanChangeTime": str(acc.pass_can_change_time),
+ "samba3PassMustChangeTime": str(acc.pass_must_change_time),
+ "objectSid": "%s-%d" % (domainsid, acc.user_rid),
+ "lmPwdHash:": acc.lm_password,
+ "ntPwdHash:": acc.nt_password,
+ })
+
+
+def import_sam_group(samldb, sid, gid, sid_name_use, nt_name, comment, domaindn):
+ """Upgrade a SAM group.
+
+ :param samldb: SAM database.
+ :param gid: Group GID
+ :param sid_name_use: SID name use
+ :param nt_name: NT Group Name
+ :param comment: NT Group Comment
+ :param domaindn: Domain DN
+ """
+
+ if sid_name_use == 5: # Well-known group
+ return None
+
+ if nt_name in ("Domain Guests", "Domain Users", "Domain Admins"):
+ return None
+
+ if gid == -1:
+ gr = grp.getgrnam(nt_name)
+ else:
+ gr = grp.getgrgid(gid)
+
+ if gr is None:
+ unixname = "UNKNOWN"
+ else:
+ unixname = gr.gr_name
+
+ assert unixname is not None
+
+ samldb.add({
+ "dn": "cn=%s,%s" % (nt_name, domaindn),
+ "objectClass": ["top", "group"],
+ "description": comment,
+ "cn": nt_name,
+ "objectSid": sid,
+ "unixName": unixname,
+ "samba3SidNameUse": str(sid_name_use)
+ })
+
+
+def import_idmap(samdb,samba3_idmap,domaindn):
+ """Import idmap data.
+
+ :param samdb: SamDB handle.
+ :param samba3_idmap: Samba 3 IDMAP database to import from
+ :param domaindn: Domain DN.
+ """
+ samdb.add({
+ "dn": domaindn,
+ "userHwm": str(samba3_idmap.get_user_hwm()),
+ "groupHwm": str(samba3_idmap.get_group_hwm())})
+
+ for uid in samba3_idmap.uids():
+ samdb.add({"dn": "SID=%s,%s" % (samba3_idmap.get_user_sid(uid), domaindn),
+ "SID": samba3_idmap.get_user_sid(uid),
+ "type": "user",
+ "unixID": str(uid)})
+
+ for gid in samba3_idmap.uids():
+ samdb.add({"dn": "SID=%s,%s" % (samba3_idmap.get_group_sid(gid), domaindn),
+ "SID": samba3_idmap.get_group_sid(gid),
+ "type": "group",
+ "unixID": str(gid)})
+
+
+def import_wins(samba4_winsdb, samba3_winsdb):
+ """Import settings from a Samba3 WINS database.
+
+ :param samba4_winsdb: WINS database to import to
+ :param samba3_winsdb: WINS database to import from
+ """
+ version_id = 0
+
+ for (name, (ttl, ips, nb_flags)) in samba3_winsdb.items():
+ version_id+=1
+
+ type = int(name.split("#", 1)[1], 16)
+
+ if type == 0x1C:
+ rType = 0x2
+ elif type & 0x80:
+ if len(ips) > 1:
+ rType = 0x2
+ else:
+ rType = 0x1
+ else:
+ if len(ips) > 1:
+ rType = 0x3
+ else:
+ rType = 0x0
+
+ if ttl > time.time():
+ rState = 0x0 # active
+ else:
+ rState = 0x1 # released
+
+ nType = ((nb_flags & 0x60)>>5)
+
+ samba4_winsdb.add({"dn": "name=%s,type=0x%s" % tuple(name.split("#")),
+ "type": name.split("#")[1],
+ "name": name.split("#")[0],
+ "objectClass": "winsRecord",
+ "recordType": str(rType),
+ "recordState": str(rState),
+ "nodeType": str(nType),
+ "expireTime": ldb.timestring(ttl),
+ "isStatic": "0",
+ "versionID": str(version_id),
+ "address": ips})
+
+ samba4_winsdb.add({"dn": "cn=VERSION",
+ "cn": "VERSION",
+ "objectClass": "winsMaxVersion",
+ "maxVersion": str(version_id)})
+
+def upgrade_provision(samba3, setup_dir, message, credentials, session_info, smbconf, targetdir):
+ oldconf = samba3.get_conf()
+
+ if oldconf.get("domain logons") == "True":
+ serverrole = "domain controller"
+ else:
+ if oldconf.get("security") == "user":
+ serverrole = "standalone"
+ else:
+ serverrole = "member server"
+
+ domainname = oldconf.get("workgroup")
+ if domainname:
+ domainname = str(domainname)
+ realm = oldconf.get("realm")
+ netbiosname = oldconf.get("netbios name")
+
+ secrets_db = samba3.get_secrets_db()
+
+ if domainname is None:
+ domainname = secrets_db.domains()[0]
+ message("No domain specified in smb.conf file, assuming '%s'" % domainname)
+
+ if realm is None:
+ realm = domainname.lower()
+ message("No realm specified in smb.conf file, assuming '%s'\n" % realm)
+
+ domainguid = secrets_db.get_domain_guid(domainname)
+ domainsid = secrets_db.get_sid(domainname)
+ if domainsid is None:
+ message("Can't find domain secrets for '%s'; using random SID\n" % domainname)
+
+ if netbiosname is not None:
+ machinepass = secrets_db.get_machine_password(netbiosname)
+ else:
+ machinepass = None
+
+ result = provision(setup_dir=setup_dir, message=message,
+ samdb_fill=FILL_DRS, smbconf=smbconf, session_info=session_info,
+ credentials=credentials, realm=realm,
+ domain=domainname, domainsid=domainsid, domainguid=domainguid,
+ machinepass=machinepass, serverrole=serverrole, targetdir=targetdir)
+
+ import_wins(Ldb(result.paths.winsdb), samba3.get_wins_db())
+
+ # FIXME: import_registry(registry.Registry(), samba3.get_registry())
+
+ # FIXME: import_idmap(samdb,samba3.get_idmap_db(),domaindn)
+
+ groupdb = samba3.get_groupmapping_db()
+ for sid in groupdb.groupsids():
+ (gid, sid_name_use, nt_name, comment) = groupdb.get_group(sid)
+ # FIXME: import_sam_group(samdb, sid, gid, sid_name_use, nt_name, comment, domaindn)
+
+ # FIXME: Aliases
+
+ passdb = samba3.get_sam_db()
+ for name in passdb:
+ user = passdb[name]
+ #FIXME: import_sam_account(result.samdb, user, domaindn, domainsid)
+
+ if hasattr(passdb, 'ldap_url'):
+ message("Enabling Samba3 LDAP mappings for SAM database")
+
+ enable_samba3sam(result.samdb, passdb.ldap_url)
+
+
+def enable_samba3sam(samdb, ldapurl):
+ """Enable Samba 3 LDAP URL database.
+
+ :param samdb: SAM Database.
+ :param ldapurl: Samba 3 LDAP URL
+ """
+ samdb.modify_ldif("""
+dn: @MODULES
+changetype: modify
+replace: @LIST
+@LIST: samldb,operational,objectguid,rdn_name,samba3sam
+""")
+
+ samdb.add({"dn": "@MAP=samba3sam", "@MAP_URL": ldapurl})
+
+
+smbconf_keep = [
+ "dos charset",
+ "unix charset",
+ "display charset",
+ "comment",
+ "path",
+ "directory",
+ "workgroup",
+ "realm",
+ "netbios name",
+ "netbios aliases",
+ "netbios scope",
+ "server string",
+ "interfaces",
+ "bind interfaces only",
+ "security",
+ "auth methods",
+ "encrypt passwords",
+ "null passwords",
+ "obey pam restrictions",
+ "password server",
+ "smb passwd file",
+ "private dir",
+ "passwd chat",
+ "password level",
+ "lanman auth",
+ "ntlm auth",
+ "client NTLMv2 auth",
+ "client lanman auth",
+ "client plaintext auth",
+ "read only",
+ "hosts allow",
+ "hosts deny",
+ "log level",
+ "debuglevel",
+ "log file",
+ "smb ports",
+ "large readwrite",
+ "max protocol",
+ "min protocol",
+ "unicode",
+ "read raw",
+ "write raw",
+ "disable netbios",
+ "nt status support",
+ "announce version",
+ "announce as",
+ "max mux",
+ "max xmit",
+ "name resolve order",
+ "max wins ttl",
+ "min wins ttl",
+ "time server",
+ "unix extensions",
+ "use spnego",
+ "server signing",
+ "client signing",
+ "max connections",
+ "paranoid server security",
+ "socket options",
+ "strict sync",
+ "max print jobs",
+ "printable",
+ "print ok",
+ "printer name",
+ "printer",
+ "map system",
+ "map hidden",
+ "map archive",
+ "preferred master",
+ "prefered master",
+ "local master",
+ "browseable",
+ "browsable",
+ "wins server",
+ "wins support",
+ "csc policy",
+ "strict locking",
+ "preload",
+ "auto services",
+ "lock dir",
+ "lock directory",
+ "pid directory",
+ "socket address",
+ "copy",
+ "include",
+ "available",
+ "volume",
+ "fstype",
+ "panic action",
+ "msdfs root",
+ "host msdfs",
+ "winbind separator"]
+
+def upgrade_smbconf(oldconf,mark):
+ """Remove configuration variables not present in Samba4
+
+ :param oldconf: Old configuration structure
+ :param mark: Whether removed configuration variables should be
+ kept in the new configuration as "samba3:<name>"
+ """
+ data = oldconf.data()
+ newconf = param_init()
+
+ for s in data:
+ for p in data[s]:
+ keep = False
+ for k in smbconf_keep:
+ if smbconf_keep[k] == p:
+ keep = True
+ break
+
+ if keep:
+ newconf.set(s, p, oldconf.get(s, p))
+ elif mark:
+ newconf.set(s, "samba3:"+p, oldconf.get(s,p))
+
+ return newconf
+
+SAMBA3_PREDEF_NAMES = {
+ 'HKLM': registry.HKEY_LOCAL_MACHINE,
+}
+
+def import_registry(samba4_registry, samba3_regdb):
+ """Import a Samba 3 registry database into the Samba 4 registry.
+
+ :param samba4_registry: Samba 4 registry handle.
+ :param samba3_regdb: Samba 3 registry database handle.
+ """
+ def ensure_key_exists(keypath):
+ (predef_name, keypath) = keypath.split("/", 1)
+ predef_id = SAMBA3_PREDEF_NAMES[predef_name]
+ keypath = keypath.replace("/", "\\")
+ return samba4_registry.create_key(predef_id, keypath)
+
+ for key in samba3_regdb.keys():
+ key_handle = ensure_key_exists(key)
+ for subkey in samba3_regdb.subkeys(key):
+ ensure_key_exists(subkey)
+ for (value_name, (value_type, value_data)) in samba3_regdb.values(key).items():
+ key_handle.set_value(value_name, value_type, value_data)
+
+
diff --git a/source4/scripting/python/uuidmodule.c b/source4/scripting/python/uuidmodule.c
new file mode 100644
index 0000000000..98ef9adaa9
--- /dev/null
+++ b/source4/scripting/python/uuidmodule.c
@@ -0,0 +1,58 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Jelmer Vernooij <jelmer@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include <Python.h>
+#include "librpc/ndr/libndr.h"
+
+static PyObject *uuid_random(PyObject *self, PyObject *args)
+{
+ struct GUID guid;
+ PyObject *pyobj;
+ char *str;
+
+ if (!PyArg_ParseTuple(args, ""))
+ return NULL;
+
+ guid = GUID_random();
+
+ str = GUID_string(NULL, &guid);
+ if (str == NULL) {
+ PyErr_SetString(PyExc_TypeError, "can't convert uuid to string");
+ return NULL;
+ }
+
+ pyobj = PyString_FromString(str);
+
+ talloc_free(str);
+
+ return pyobj;
+}
+
+static PyMethodDef methods[] = {
+ { "uuid4", (PyCFunction)uuid_random, METH_VARARGS, NULL},
+ { NULL, NULL }
+};
+
+void inituuid(void)
+{
+ PyObject *mod = Py_InitModule3("uuid", methods, "UUID helper routines");
+ if (mod == NULL)
+ return;
+}
diff --git a/source4/selftest/config.mk b/source4/selftest/config.mk
new file mode 100644
index 0000000000..324532c22a
--- /dev/null
+++ b/source4/selftest/config.mk
@@ -0,0 +1,89 @@
+TEST_FORMAT = plain
+
+SELFTEST = $(LD_LIBPATH_OVERRIDE) PYTHON=$(PYTHON) \
+ $(PERL) $(selftestdir)/selftest.pl --prefix=${selftest_prefix} \
+ --builddir=$(builddir) --srcdir=$(srcdir) \
+ --expected-failures=$(srcdir)/selftest/knownfail \
+ --format=$(TEST_FORMAT) \
+ --exclude=$(srcdir)/selftest/skip --testlist="./selftest/tests.sh|" \
+ $(TEST_OPTIONS)
+
+SELFTEST_NOSLOW_OPTS = --exclude=$(srcdir)/selftest/slow
+SELFTEST_QUICK_OPTS = $(SELFTEST_NOSLOW_OPTS) --quick --include=$(srcdir)/selftest/quick
+
+slowtest:: everything
+ $(SELFTEST) $(DEFAULT_TEST_OPTIONS) --immediate $(TESTS)
+
+test:: everything
+ $(SELFTEST) $(SELFTEST_NOSLOW_OPTS) $(DEFAULT_TEST_OPTIONS) --immediate \
+ $(TESTS)
+
+kvmtest:: everything
+ $(SELFTEST) $(SELFTEST_NOSLOW_OPTS) $(DEFAULT_TEST_OPTIONS) --immediate \
+ --target=kvm --image=$(KVM_IMAGE)
+
+kvmquicktest:: everything
+ $(SELFTEST) $(DEFAULT_TEST_OPTIONS) --immediate \
+ $(SELFTEST_QUICK_OPTS) --target=kvm --image=$(KVM_IMAGE)
+
+testone:: everything
+ $(SELFTEST) $(SELFTEST_NOSLOW_OPTS) $(DEFAULT_TEST_OPTIONS) --one $(TESTS)
+
+test-swrap:: everything
+ $(SELFTEST) $(SELFTEST_NOSLOW_OPTS) --socket-wrapper --immediate $(TESTS)
+
+test-swrap-pcap:: everything
+ $(SELFTEST) $(SELFTEST_NOSLOW_OPTS) --socket-wrapper-pcap --immediate $(TESTS)
+
+test-swrap-keep-pcap:: everything
+ $(SELFTEST) $(SELFTEST_NOSLOW_OPTS) --socket-wrapper-keep-pcap --immediate $(TESTS)
+
+test-noswrap:: everything
+ $(SELFTEST) $(SELFTEST_NOSLOW_OPTS) --immediate $(TESTS)
+
+quicktest:: all
+ $(SELFTEST) $(SELFTEST_QUICK_OPTS) --socket-wrapper --immediate $(TESTS)
+
+quicktestone:: all
+ $(SELFTEST) $(SELFTEST_QUICK_OPTS) --socket-wrapper --one $(TESTS)
+
+testenv:: everything
+ $(SELFTEST) $(SELFTEST_NOSLOW_OPTS) --socket-wrapper --testenv
+
+testenv-%:: everything
+ SELFTEST_TESTENV=$* $(SELFTEST) $(SELFTEST_NOSLOW_OPTS) --socket-wrapper --testenv
+
+test-%::
+ $(MAKE) test TESTS=$*
+
+valgrindtest:: valgrindtest-all
+
+valgrindtest-quick:: all
+ SMBD_VALGRIND="xterm -n server -e $(selftestdir)/valgrind_run $(LD_LIBPATH_OVERRIDE)" \
+ VALGRIND="valgrind -q --num-callers=30 --log-file=${selftest_prefix}/valgrind.log" \
+ $(SELFTEST) $(SELFTEST_QUICK_OPTS) --immediate --socket-wrapper $(TESTS)
+
+valgrindtest-all:: everything
+ SMBD_VALGRIND="xterm -n server -e $(selftestdir)/valgrind_run $(LD_LIBPATH_OVERRIDE)" \
+ VALGRIND="valgrind -q --num-callers=30 --log-file=${selftest_prefix}/valgrind.log" \
+ $(SELFTEST) $(SELFTEST_NOSLOW_OPTS) --immediate --socket-wrapper $(TESTS)
+
+valgrindtest-env:: everything
+ SMBD_VALGRIND="xterm -n server -e $(selftestdir)/valgrind_run $(LD_LIBPATH_OVERRIDE)" \
+ VALGRIND="valgrind -q --num-callers=30 --log-file=${selftest_prefix}/valgrind.log" \
+ $(SELFTEST) $(SELFTEST_NOSLOW_OPTS) --socket-wrapper --testenv
+
+gdbtest:: gdbtest-all
+
+gdbtest-quick:: all
+ SMBD_VALGRIND="xterm -n server -e $(selftestdir)/gdb_run $(LD_LIBPATH_OVERRIDE)" \
+ $(SELFTEST) $(SELFTEST_QUICK_OPTS) --immediate --socket-wrapper $(TESTS)
+
+gdbtest-all:: everything
+ SMBD_VALGRIND="xterm -n server -e $(selftestdir)/gdb_run $(LD_LIBPATH_OVERRIDE)" \
+ $(SELFTEST) $(SELFTEST_NOSLOW_OPTS) --immediate --socket-wrapper $(TESTS)
+
+gdbtest-env:: everything
+ SMBD_VALGRIND="xterm -n server -e $(selftestdir)/gdb_run $(LD_LIBPATH_OVERRIDE)" \
+ $(SELFTEST) $(SELFTEST_NOSLOW_OPTS) --socket-wrapper --testenv
+
diff --git a/source4/selftest/knownfail b/source4/selftest/knownfail
new file mode 100644
index 0000000000..89fccdfaa5
--- /dev/null
+++ b/source4/selftest/knownfail
@@ -0,0 +1,53 @@
+# This file contains a list of regular expressions matching the names of
+# tests that are expected to fail.
+#
+# "make test" will not report failures for tests listed here and will consider
+# a successful run for any of these tests an error.
+local.resolve.*.async
+local.iconv.*.next_codepoint()
+base.delete.*.deltest17
+base.delete.*.deltest20a
+base.delete.*.deltest20b
+rpc.winreg.*security
+samba4.local.registry.(dir|ldb).check hive security
+samba4.local.registry.local.security
+rpc.wkssvc
+rpc.handles.*.lsarpc-shared
+rpc.handles.*.mixed-shared
+rpc.epmapper.*.Insert
+rpc.epmapper.*.InqObject
+rpc.dfs.*
+rpc.drsuapi.*
+rpc.lsalookup
+rpc.cracknames
+rpc.netlogon.*.LogonUasLogon
+rpc.netlogon.*.LogonUasLogoff
+rpc.netlogon.*.DatabaseSync
+rpc.netlogon.*.DatabaseSync2
+rpc.netlogon.*.LogonControl
+rpc.netlogon.*.LogonControl2
+rpc.netlogon.*.DsrEnumerateDomainTrusts
+rpc.netlogon.*.NetrEnumerateTrustedDomains
+rpc.netlogon.*.NetrEnumerateTrustedDomainsEx
+rpc.netlogon.*.DsrGetDcSiteCoverageW
+rpc.netlogon.*.DsRAddressToSitenamesW
+rpc.netlogon.*.DsRAddressToSitenamesExW
+rpc.netlogon.*.GetPassword
+rpc.netlogon.*.GetTrustPasswords
+rpc.netlogon.*.DatabaseRedo
+rpc.netlogon.*.ServerGetTrustInfo
+samba4.rpc.samr.passwords.pwdlastset # Not provided by Samba 4 yet
+base.charset.*.Testing partial surrogate
+.*net.api.delshare.* # DelShare isn't implemented yet
+rap.*netservergetinfo
+smb2.persistent.handles1
+samba4.winbind.struct.*.SHOW_SEQUENCE # Not yet working in winbind
+samba4.winbind.struct.*.GETPWENT # Not yet working in winbind
+samba4.winbind.struct.*.SETPWENT # Not yet working in winbind
+samba4.winbind.struct.*.LOOKUP_NAME_SID # Not yet working in winbind
+^samba4.*base.delaywrite.*update of write time and SMBwrite truncate$
+^samba4.*base.delaywrite.*update of write time and SMBwrite truncate expand$
+^samba4.*base.delaywrite.*delayed update of write time 3a$
+^samba4.*base.delaywrite.*delayed update of write time 3c$
+^samba4.*base.delaywrite.*update of write time using SET_END_OF_FILE$
+^samba4.*base.delaywrite.*update of write time using SET_ALLOCATION_SIZE$
diff --git a/source4/selftest/quick b/source4/selftest/quick
new file mode 100644
index 0000000000..7bea619f18
--- /dev/null
+++ b/source4/selftest/quick
@@ -0,0 +1,38 @@
+# This file contains regexes matching the tests that should be run
+# when doing a "quicktest" - verifying whether the build is working
+# rather than trying to see what exactly is broken.
+#
+# This should be as quick as possible but cover as much code as possible.
+base.unlink
+base.attr
+base.delete
+base.tcon
+base.open
+base.chkpath
+raw.qfsinfo
+raw.qfileinfo
+raw.sfileinfo
+raw.mkdir
+raw.seek
+raw.open
+raw.write
+raw.unlink
+raw.read
+raw.close
+raw.ioctl
+raw.rename
+raw.eas
+raw.streams
+base.open
+rpc.altercontext
+rpc.join
+rpc.echo
+rpc.schannel
+rpc.netlogon
+rpc.unixinfo
+rpc.handles
+rpc.altercontext
+rpc.join
+rpc.handles
+rpc.echo
+smb.signing
diff --git a/source4/selftest/skip b/source4/selftest/skip
new file mode 100644
index 0000000000..3c360908d1
--- /dev/null
+++ b/source4/selftest/skip
@@ -0,0 +1,61 @@
+# This file contains a list of regular expressions matching testsuites that
+# should be skipped during "make test".
+#
+# Possible reasons for adding a testsuite here:
+# * Testsuite functionality not implemented on the server side
+# * Testsuite crashes during run
+# * Testsuite crashes server
+# * Testsuite contains "flapping" tests (sometimes success, sometimes failure)
+# * Testsuite hangs indefinitely
+#
+# If a testsuite is partially succeeding, please list the failing bits
+# in the samba4-knownfail file rather than disabling the testsuite completely.
+#
+# If a testsuite is very slow, please add it to samba4-slow instead.
+#
+# Please add a comment for each testsuite you disable explaining why
+# it is being skipped.
+raw.composite
+base.iometer
+base.casetable
+base.nttrans
+base.scan.maxfid
+raw.hold.oplock # Not a test, but a way to block other clients for a test
+raw.ping.pong # Needs second server to test
+rpc.samr.accessmask
+raw.scan.eamax
+samba4.ntvfs.cifs.raw.qfileinfo.ipc
+smb2.notify
+smb2.scan
+ntvfs.cifs.base.charset
+ntvfs.cifs.base.iometer
+ntvfs.cifs.base.casetable
+ntvfs.cifs.base.nttrans
+ntvfs.cifs.base.scan-maxfid
+ntvfs.cifs.base.utable
+ntvfs.cifs.base.smb
+ntvfs.cifs.raw.composite
+ntvfs.cifs.raw.notify
+ntvfs.cifs.raw.scan-eamax
+ntvfs.cifs.raw.context
+ntvfs.cifs.raw.qfileinfo.ipc
+rpc.dssync
+rpc.samsync
+rpc.remact # Not provided by Samba 4
+rpc.oxidresolve # Not provided by Samba 4
+rpc.eventlog # Not provided by Samba 4
+rpc.initshutdown # Not provided by Samba 4
+rpc.svcctl # Not provided by Samba 4
+rpc.atsvc # Not provided by Samba 4
+rpc.frsapi # Not provided by Samba 4
+rpc.ntsvcs # Not provided by Samba 4
+^samba4.base.samba3.* # Samba3-specific test
+^samba4.ntvfs.cifs.base.samba3.* # Samba3-specific test
+^samba4.raw.samba3.* # Samba3-specific test
+^samba4.ntvfs.cifs.raw.samba3.* # Samba3-specific test
+samba4.ntvfs.cifs.raw.
+^samba4.rpc..*samba3.* # Samba3-specific test
+^samba4.net.domopen.*$ # Hangs for some reason
+^samba4.net.api.become.dc.*$ # Fails
+nss.test # Fails
+raw.offline # Samba 4 doesn't have much offline support yet
diff --git a/source4/selftest/slow b/source4/selftest/slow
new file mode 100644
index 0000000000..d414368de8
--- /dev/null
+++ b/source4/selftest/slow
@@ -0,0 +1,7 @@
+# This file contains regexes matching tests that are very slow and
+# should be skipped during a normal test run.
+.*base.bench.holdcon.* # Slow
+raw.bench.lookup # Slow
+base.utable # Slow
+base.smb # Slow
+rpc.scanner # Slow
diff --git a/source4/selftest/test_w2k3.sh b/source4/selftest/test_w2k3.sh
new file mode 100755
index 0000000000..3cd034d000
--- /dev/null
+++ b/source4/selftest/test_w2k3.sh
@@ -0,0 +1,48 @@
+#!/bin/sh
+
+# tests that should pass against a w2k3 DC, as administrator
+
+# add tests to this list as they start passing, so we test
+# that they stay passing
+ncacn_np_tests="RPC-SCHANNEL RPC-DSSETUP RPC-EPMAPPER RPC-SAMR RPC-WKSSVC RPC-SRVSVC RPC-EVENTLOG RPC-NETLOGON RPC-LSA RPC-SAMLOGON RPC-SAMSYNC RPC-MULTIBIND RPC-WINREG RPC-SPOOLSS RPC-SPOOLSS-WIN"
+ncacn_ip_tcp_tests="RPC-SCHANNEL RPC-EPMAPPER RPC-SAMR RPC-NETLOGON RPC-LSA RPC-SAMLOGON RPC-SAMSYNC RPC-MULTIBIND"
+
+if [ $# -lt 4 ]; then
+cat <<EOF
+Usage: test_w2k3.sh SERVER USERNAME PASSWORD DOMAIN REALM
+EOF
+exit 1;
+fi
+
+server="$1"
+username="$2"
+password="$3"
+domain="$4"
+realm="$5"
+shift 5
+
+incdir=`dirname $0`
+. $incdir/test_functions.sh
+
+OPTIONS="-U$username%$password -W $domain --option realm=$realm"
+
+name="RPC-SPOOLSS on ncacn_np"
+testit "$name" rpc bin/smbtorture $TORTURE_OPTIONS ncacn_np:"$server" $OPTIONS RPC-SPOOLSS "$*"
+
+for bindoptions in padcheck connect sign seal ntlm,sign ntlm,seal $VALIDATE bigendian; do
+ for transport in ncacn_ip_tcp ncacn_np; do
+ case $transport in
+ ncacn_np) tests=$ncacn_np_tests ;;
+ ncacn_ip_tcp) tests=$ncacn_ip_tcp_tests ;;
+ esac
+ for t in $tests; do
+ name="$t on $transport with $bindoptions"
+ testit "$name" rpc bin/smbtorture $TORTURE_OPTIONS $transport:"$server[$bindoptions]" $OPTIONS $t "$*"
+ done
+ done
+done
+
+name="RPC-DRSUAPI on ncacn_ip_tcp with seal"
+testit "$name" rpc bin/smbtorture $TORTURE_OPTIONS ncacn_ip_tcp:"$server[seal]" $OPTIONS RPC-DRSUAPI "$*"
+name="RPC-DRSUAPI on ncacn_ip_tcp with seal,bigendian"
+testit "$name" rpc bin/smbtorture $TORTURE_OPTIONS ncacn_ip_tcp:"$server[seal,bigendian]" $OPTIONS RPC-DRSUAPI "$*"
diff --git a/source4/selftest/test_w2k3_file.sh b/source4/selftest/test_w2k3_file.sh
new file mode 100755
index 0000000000..d59d2b49ef
--- /dev/null
+++ b/source4/selftest/test_w2k3_file.sh
@@ -0,0 +1,44 @@
+#!/bin/sh
+
+# this runs the file serving tests that are expected to pass with win2003
+
+if [ $# -lt 3 ]; then
+cat <<EOF
+Usage: test_w2k3_file.sh UNC USERNAME PASSWORD <first> <smbtorture args>
+EOF
+exit 1;
+fi
+
+unc="$1"
+username="$2"
+password="$3"
+start="$4"
+shift 4
+ADDARGS="$*"
+
+incdir=`dirname $0`
+. $incdir/test_functions.sh
+
+tests="BASE-FDPASS BASE-LOCK "
+tests="$tests BASE-UNLINK BASE-ATTR"
+tests="$tests BASE-DIR1 BASE-DIR2 BASE-VUID"
+tests="$tests BASE-TCON BASE-TCONDEV BASE-RW1"
+tests="$tests BASE-DENY3 BASE-XCOPY BASE-OPEN BASE-DENYDOS"
+tests="$tests BASE-DELETE BASE-PROPERTIES BASE-MANGLE"
+tests="$tests BASE-CHKPATH BASE-SECLEAK BASE-TRANS2"
+tests="$tests BASE-NTDENY1 BASE-NTDENY2 BASE-RENAME BASE-OPENATTR"
+tests="$tests RAW-QFILEINFO RAW-SFILEINFO-BUG RAW-SFILEINFO"
+tests="$tests RAW-LOCK RAW-MKDIR RAW-SEEK RAW-CONTEXT RAW-MUX RAW-OPEN RAW-WRITE"
+tests="$tests RAW-UNLINK RAW-READ RAW-CLOSE RAW-IOCTL RAW-CHKPATH RAW-RENAME"
+tests="$tests RAW-EAS RAW-STREAMS RAW-OPLOCK RAW-NOTIFY BASE-DELAYWRITE"
+# slowest tests last
+tests="$tests BASE-DENY1 BASE-DENY2"
+
+# these tests are known to fail against windows
+fail="RAW-SEARCH RAW-ACLS RAW-QFSINFO"
+
+echo "Skipping tests expected to fail: $fail"
+
+for t in $tests; do
+ testit "$t" smb $VALGRIND bin/smbtorture $TORTURE_OPTIONS $ADDARGS $unc -U"$username"%"$password" $t
+done
diff --git a/source4/selftest/test_win.sh b/source4/selftest/test_win.sh
new file mode 100755
index 0000000000..17c09e6b26
--- /dev/null
+++ b/source4/selftest/test_win.sh
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+# A shell script to connect to a windows host over telnet,
+# setup for a smbtorture test,
+# run the test,
+# and remove the previously configured directory and share.
+# Copyright Brad Henry <brad@samba.org> 2006
+# Released under the GNU GPL version 3 or later.
+
+. selftest/test_functions.sh
+
+export SMBTORTURE_REMOTE_HOST=`perl -I$WINTEST_DIR $WINTEST_DIR/vm_get_ip.pl VM_CFG_PATH`
+if [ -z $SMBTORTURE_REMOTE_HOST ]; then
+ # Restore snapshot to ensure VM is in a known state, then exit.
+ restore_snapshot "Test failed to get the IP address of the windows host." "$VM_CFG_PATH"
+ exit 1
+fi
+
+name="BASE against Windows 2003"
+testit "$name" smb $WINTEST_DIR/wintest_base.sh $SMBTORTURE_REMOTE_HOST \
+ $SMBTORTURE_USERNAME $SMBTORTURE_PASSWORD $SMBTORTURE_WORKGROUP
+
+name="RAW against Windows 2003"
+testit "$name" smb $WINTEST_DIR/wintest_raw.sh $SMBTORTURE_REMOTE_HOST \
+ $SMBTORTURE_USERNAME $SMBTORTURE_PASSWORD $SMBTORTURE_WORKGROUP
+
+name="RPC against Windows 2003"
+testit "$name" smb $WINTEST_DIR/wintest_rpc.sh $SMBTORTURE_REMOTE_HOST \
+ $SMBTORTURE_USERNAME $SMBTORTURE_PASSWORD $SMBTORTURE_WORKGROUP
+
+name="NET against Windows 2003"
+testit "$name" smb $WINTEST_DIR/wintest_net.sh $SMBTORTURE_REMOTE_HOST \
+ $SMBTORTURE_USERNAME $SMBTORTURE_PASSWORD $SMBTORTURE_WORKGROUP
+
+name="Windows 2003 against samba"
+testit "$name" smb $WINTEST_DIR/wintest_client.sh $SMBTORTURE_REMOTE_HOST
+
+dc_tests="RPC-DRSUAPI ncacn_np ncacn_ip_tcp"
+for name in $dc_tests; do
+ testit "$name against Windows 2003 DC" rpc $WINTEST_DIR/wintest_2k3_dc.sh \
+ "$name"
+done
diff --git a/source4/selftest/tests.sh b/source4/selftest/tests.sh
new file mode 100755
index 0000000000..821db06414
--- /dev/null
+++ b/source4/selftest/tests.sh
@@ -0,0 +1,433 @@
+#!/bin/sh
+# This script generates a list of testsuites that should be run as part of
+# the Samba 4 test suite.
+
+# The output of this script is parsed by selftest.pl, which then decides
+# which of the tests to actually run. It will, for example, skip all tests
+# listed in selftest/skip or only run a subset during "make quicktest".
+
+# The idea is that this script outputs all of the tests of Samba 4, not
+# just those that are known to pass, and list those that should be skipped
+# or are known to fail in selftest/skip or selftest/knownfail. This makes it
+# very easy to see what functionality is still missing in Samba 4 and makes
+# it possible to run the testsuite against other servers, such as Samba 3 or
+# Windows that have a different set of features.
+
+# The syntax for a testsuite is "-- TEST --" on a single line, followed
+# by the name of the test, the environment it needs and the command to run, all
+# three separated by newlines. All other lines in the output are considered
+# comments.
+
+if [ ! -n "$PERL" ]
+then
+ PERL=perl
+fi
+
+if [ ! -n "$PYTHON" ]
+then
+ PYTHON=python
+fi
+
+plantest() {
+ name=$1
+ env=$2
+ shift 2
+ cmdline="$*"
+ echo "-- TEST --"
+ if [ "$env" = "none" ]; then
+ echo "samba4.$name"
+ else
+ echo "samba4.$name ($env)"
+ fi
+ echo $env
+ echo $cmdline
+}
+
+normalize_testname() {
+ name=$1
+ shift 1
+ echo $name | tr "A-Z-" "a-z."
+}
+
+plansmbtorturetest() {
+ name=$1
+ env=$2
+ shift 2
+ other_args="$*"
+ modname=`normalize_testname $name`
+ cmdline="$VALGRIND $smb4torture $other_args $name"
+ plantest "$modname" "$env" $cmdline
+}
+
+samba4srcdir="`dirname $0`/.."
+samba4bindir="$BUILDDIR/bin"
+smb4torture="$samba4bindir/smbtorture${EXEEXT}"
+$smb4torture -V
+
+bbdir=../testprogs/blackbox
+
+prefix_abs="$SELFTEST_PREFIX/s4client"
+CONFIGURATION="--configfile=\$SMB_CONF_PATH"
+
+test -d "$prefix_abs" || mkdir "$prefix_abs"
+
+TORTURE_OPTIONS=""
+TORTURE_OPTIONS="$TORTURE_OPTIONS $CONFIGURATION"
+TORTURE_OPTIONS="$TORTURE_OPTIONS --maximum-runtime=$SELFTEST_MAXTIME"
+TORTURE_OPTIONS="$TORTURE_OPTIONS --target=$SELFTEST_TARGET"
+TORTURE_OPTIONS="$TORTURE_OPTIONS --basedir=$prefix_abs"
+if [ -n "$SELFTEST_VERBOSE" ]; then
+ TORTURE_OPTIONS="$TORTURE_OPTIONS --option=torture:progress=no"
+fi
+TORTURE_OPTIONS="$TORTURE_OPTIONS --format=subunit"
+if [ -n "$SELFTEST_QUICK" ]; then
+ TORTURE_OPTIONS="$TORTURE_OPTIONS --option=torture:quick=yes"
+fi
+smb4torture="$smb4torture $TORTURE_OPTIONS"
+
+echo "OPTIONS $TORTURE_OPTIONS"
+
+# Simple tests for LDAP and CLDAP
+
+for options in "" "--option=socket:testnonblock=true" "-U\$USERNAME%\$PASSWORD --option=socket:testnonblock=true" "-U\$USERNAME%\$PASSWORD"; do
+ plantest "ldb.ldap with options $options" dc $bbdir/test_ldb.sh ldap \$SERVER_IP $options
+done
+# see if we support ldaps
+if grep ENABLE_GNUTLS.1 include/config.h > /dev/null; then
+ for options in "" "-U\$USERNAME%\$PASSWORD"; do
+ plantest "ldb.ldaps with options $options" dc $bbdir/test_ldb.sh ldaps \$SERVER_IP $options
+ done
+fi
+plantest "ldb.ldapi with options $options" dc $bbdir/test_ldb.sh ldapi \$PREFIX_ABS/dc/private/ldapi $options
+for t in LDAP-CLDAP LDAP-BASIC LDAP-SCHEMA LDAP-UPTODATEVECTOR
+do
+ plansmbtorturetest "$t" dc "-U\$USERNAME%\$PASSWORD" //\$SERVER_IP/_none_
+done
+
+# only do the ldb tests when not in quick mode - they are quite slow, and ldb
+# is now pretty well tested by the rest of the quick tests anyway
+LDBDIR=$samba4srcdir/lib/ldb
+export LDBDIR
+plantest "ldb" none TEST_DATA_PREFIX=\$PREFIX $LDBDIR/tests/test-tdb.sh
+
+# Tests for RPC
+
+# add tests to this list as they start passing, so we test
+# that they stay passing
+ncacn_np_tests="RPC-SCHANNEL RPC-JOIN RPC-LSA RPC-DSSETUP RPC-ALTERCONTEXT RPC-MULTIBIND RPC-NETLOGON RPC-HANDLES RPC-SAMSYNC RPC-SAMBA3SESSIONKEY RPC-SAMBA3-GETUSERNAME RPC-SAMBA3-LSA RPC-BINDSAMBA3 RPC-NETLOGSAMBA3 RPC-ASYNCBIND RPC-LSALOOKUP RPC-LSA-GETUSER RPC-SCHANNEL2 RPC-AUTHCONTEXT"
+ncalrpc_tests="RPC-SCHANNEL RPC-JOIN RPC-LSA RPC-DSSETUP RPC-ALTERCONTEXT RPC-MULTIBIND RPC-NETLOGON RPC-DRSUAPI RPC-ASYNCBIND RPC-LSALOOKUP RPC-LSA-GETUSER RPC-SCHANNEL2 RPC-AUTHCONTEXT"
+ncacn_ip_tcp_tests="RPC-SCHANNEL RPC-JOIN RPC-LSA RPC-DSSETUP RPC-ALTERCONTEXT RPC-MULTIBIND RPC-NETLOGON RPC-HANDLES RPC-DSSYNC RPC-ASYNCBIND RPC-LSALOOKUP RPC-LSA-GETUSER RPC-SCHANNEL2 RPC-AUTHCONTEXT RPC-OBJECTUUID"
+slow_ncacn_np_tests="RPC-SAMLOGON RPC-SAMR RPC-SAMR-USERS RPC-SAMR-PASSWORDS"
+slow_ncalrpc_tests="RPC-SAMR RPC-SAMR-PASSWORDS"
+slow_ncacn_ip_tcp_tests="RPC-SAMR RPC-SAMR-PASSWORDS RPC-CRACKNAMES"
+
+all_tests="$ncalrpc_tests $ncacn_np_tests $ncacn_ip_tcp_tests $slow_ncalrpc_tests $slow_ncacn_np_tests $slow_ncacn_ip_tcp_tests RPC-SECRETS RPC-SAMBA3-SHARESEC RPC-COUNTCALLS"
+
+# Make sure all tests get run
+for t in `$smb4torture --list | grep "^RPC-"`
+do
+ echo $all_tests | grep "$t" > /dev/null
+ if [ $? -ne 0 ]
+ then
+ auto_rpc_tests="$auto_rpc_tests $t"
+ fi
+done
+
+for bindoptions in seal,padcheck $VALIDATE bigendian; do
+ for transport in ncalrpc ncacn_np ncacn_ip_tcp; do
+ env="dc"
+ case $transport in
+ ncalrpc) tests=$ncalrpc_tests;env="dc:local" ;;
+ ncacn_np) tests=$ncacn_np_tests ;;
+ ncacn_ip_tcp) tests=$ncacn_ip_tcp_tests ;;
+ esac
+ for t in $tests; do
+ plantest "`normalize_testname $t` on $transport with $bindoptions" $env $VALGRIND $smb4torture $transport:"\$SERVER[$bindoptions]" -U"\$USERNAME"%"\$PASSWORD" -W \$DOMAIN $t "$*"
+ done
+ plantest "rpc.samba3.sharesec on $transport with $bindoptions" $env $VALGRIND $smb4torture $transport:"\$SERVER[$bindoptions]" -U"\$USERNAME"%"\$PASSWORD" -W \$DOMAIN --option=torture:share=tmp RPC-SAMBA3-SHARESEC "$*"
+ done
+done
+
+for bindoptions in "" $VALIDATE bigendian; do
+ for t in $auto_rpc_tests; do
+ plantest "`normalize_testname $t` with $bindoptions" dc $VALGRIND $smb4torture "\$SERVER[$bindoptions]" -U"\$USERNAME"%"\$PASSWORD" -W \$DOMAIN $t "$*"
+ done
+done
+
+t="RPC-COUNTCALLS"
+plantest "`normalize_testname $t`" dc:local $VALGRIND $smb4torture "\$SERVER[$bindoptions]" -U"\$USERNAME"%"\$PASSWORD" -W \$DOMAIN $t "$*"
+
+for bindoptions in connect $VALIDATE ; do
+ for transport in ncalrpc ncacn_np ncacn_ip_tcp; do
+ env="dc"
+ case $transport in
+ ncalrpc) tests=$slow_ncalrpc_tests; env="dc:local" ;;
+ ncacn_np) tests=$slow_ncacn_np_tests ;;
+ ncacn_ip_tcp) tests=$slow_ncacn_ip_tcp_tests ;;
+ esac
+ for t in $tests; do
+ plantest "`normalize_testname $t` on $transport with $bindoptions" $env $VALGRIND $smb4torture $transport:"\$SERVER[$bindoptions]" -U"\$USERNAME"%"\$PASSWORD" -W \$DOMAIN $t "$*"
+ done
+ done
+done
+
+
+# Tests for the NET API
+
+net=`$smb4torture --list | grep "^NET-"`
+
+for t in $net; do
+ plansmbtorturetest "$t" dc "\$SERVER[$VALIDATE]" -U"\$USERNAME"%"\$PASSWORD" -W "\$DOMAIN" "$*"
+done
+
+# Tests for session keys
+# FIXME: Integrate these into a single smbtorture test
+
+bindoptions=""
+transport="ncacn_np"
+for ntlmoptions in \
+ "-k no --option=usespnego=yes" \
+ "-k no --option=usespnego=yes --option=ntlmssp_client:128bit=no" \
+ "-k no --option=usespnego=yes --option=ntlmssp_client:56bit=yes" \
+ "-k no --option=usespnego=yes --option=ntlmssp_client:56bit=no" \
+ "-k no --option=usespnego=yes --option=ntlmssp_client:128bit=no --option=ntlmssp_client:56bit=yes" \
+ "-k no --option=usespnego=yes --option=ntlmssp_client:128bit=no --option=ntlmssp_client:56bit=no" \
+ "-k no --option=usespnego=yes --option=clientntlmv2auth=yes" \
+ "-k no --option=usespnego=yes --option=clientntlmv2auth=yes --option=ntlmssp_client:128bit=no" \
+ "-k no --option=usespnego=yes --option=clientntlmv2auth=yes --option=ntlmssp_client:128bit=no --option=ntlmssp_client:56bit=yes" \
+ "-k no --option=usespnego=no --option=clientntlmv2auth=yes" \
+ "-k no --option=gensec:spnego=no --option=clientntlmv2auth=yes" \
+ "-k no --option=usespnego=no"; do
+ name="rpc.secrets on $transport with $bindoptions with $ntlmoptions"
+ plantest "$name" dc $smb4torture $transport:"\$SERVER[$bindoptions]" $ntlmoptions -U"\$USERNAME"%"\$PASSWORD" -W \$DOMAIN --option=gensec:target_hostname=\$NETBIOSNAME RPC-SECRETS "$*"
+done
+plantest "rpc.secrets on $transport with $bindoptions with Kerberos" dc $smb4torture $transport:"\$SERVER[$bindoptions]" -k yes -U"\$USERNAME"%"\$PASSWORD" -W \$DOMAIN "--option=gensec:target_hostname=\$NETBIOSNAME" RPC-SECRETS "$*"
+plantest "rpc.secrets on $transport with $bindoptions with Kerberos - use target principal" dc $smb4torture $transport:"\$SERVER[$bindoptions]" -k yes -U"\$USERNAME"%"\$PASSWORD" -W \$DOMAIN "--option=clientusespnegoprincipal=yes" "--option=gensec:target_hostname=\$NETBIOSNAME" RPC-SECRETS "$*"
+plantest "rpc.secrets on $transport with Kerberos - use Samba3 style login" dc $smb4torture $transport:"\$SERVER" -k yes -U"\$USERNAME"%"\$PASSWORD" -W "\$DOMAIN" "--option=gensec:fake_gssapi_krb5=yes" "--option=gensec:gssapi_krb5=no" "--option=gensec:target_hostname=\$NETBIOSNAME" "RPC-SECRETS-none*" "$*"
+plantest "rpc.secrets on $transport with Kerberos - use Samba3 style login, use target principal" dc $smb4torture $transport:"\$SERVER" -k yes -U"\$USERNAME"%"\$PASSWORD" -W "\$DOMAIN" "--option=clientusespnegoprincipal=yes" "--option=gensec:fake_gssapi_krb5=yes" "--option=gensec:gssapi_krb5=no" "--option=gensec:target_hostname=\$NETBIOSNAME" "RPC-SECRETS-none*" "$*"
+
+# Echo tests
+transports="ncacn_np ncacn_ip_tcp ncalrpc"
+
+for transport in $transports; do
+ for bindoptions in connect spnego spnego,sign spnego,seal $VALIDATE padcheck bigendian bigendian,seal; do
+ for ntlmoptions in \
+ "--option=socket:testnonblock=True --option=torture:quick=yes"; do
+ env="dc"
+ if test x"$transport" = x"ncalrpc"; then
+ env="dc:local"
+ fi
+ plantest "rpc.echo on $transport with $bindoptions and $ntlmoptions" $env $smb4torture $transport:"\$SERVER[$bindoptions]" $ntlmoptions -U"\$USERNAME"%"\$PASSWORD" -W "\$DOMAIN" RPC-ECHO "$*"
+ done
+ done
+done
+
+for transport in $transports; do
+ for bindoptions in sign seal; do
+ for ntlmoptions in \
+ "--option=ntlmssp_client:ntlm2=yes --option=torture:quick=yes" \
+ "--option=ntlmssp_client:ntlm2=no --option=torture:quick=yes" \
+ "--option=ntlmssp_client:ntlm2=yes --option=ntlmssp_client:128bit=no --option=torture:quick=yes" \
+ "--option=ntlmssp_client:ntlm2=no --option=ntlmssp_client:128bit=no --option=torture:quick=yes" \
+ "--option=ntlmssp_client:ntlm2=yes --option=ntlmssp_client:keyexchange=no --option=torture:quick=yes" \
+ "--option=ntlmssp_client:ntlm2=no --option=ntlmssp_client:keyexchange=no --option=torture:quick=yes" \
+ "--option=clientntlmv2auth=yes --option=ntlmssp_client:keyexchange=no --option=torture:quick=yes" \
+ "--option=clientntlmv2auth=yes --option=ntlmssp_client:128bit=no --option=ntlmssp_client:keyexchange=yes --option=torture:quick=yes" \
+ "--option=clientntlmv2auth=yes --option=ntlmssp_client:128bit=no --option=ntlmssp_client:keyexchange=no --option=torture:quick=yes" \
+ ; do
+ env="dc"
+ if test x"$transport" = x"ncalrpc"; then
+ env="dc:local"
+ fi
+ plantest "rpc.echo on $transport with $bindoptions and $ntlmoptions" $env $smb4torture $transport:"\$SERVER[$bindoptions]" $ntlmoptions -U"\$USERNAME"%"\$PASSWORD" -W \$DOMAIN RPC-ECHO "$*"
+ done
+ done
+done
+
+plantest "rpc.echo on ncacn_np over smb2" dc $smb4torture ncacn_np:"\$SERVER[smb2]" -U"\$USERNAME"%"\$PASSWORD" -W \$DOMAIN RPC-ECHO "$*"
+
+# Tests against the NTVFS POSIX backend
+NTVFSARGS=""
+NTVFSARGS="${NTVFSARGS} --option=torture:sharedelay=100000"
+NTVFSARGS="${NTVFSARGS} --option=torture:oplocktimeout=3"
+NTVFSARGS="${NTVFSARGS} --option=torture:writetimeupdatedelay=500000"
+
+smb2=`$smb4torture --list | grep "^SMB2-" | xargs`
+#The QFILEINFO-IPC test needs to be on ipc$
+raw=`$smb4torture --list | grep "^RAW-" | grep -v "RAW-QFILEINFO-IPC"| xargs`
+base=`$smb4torture --list | grep "^BASE-" | xargs`
+
+for t in $base $raw $smb2; do
+ plansmbtorturetest "$t" dc $ADDARGS //\$SERVER/tmp -U"\$USERNAME"%"\$PASSWORD" $NTVFSARGS
+done
+
+plansmbtorturetest "RAW-QFILEINFO-IPC" dc $ADDARGS //\$SERVER/ipc$ -U"\$USERNAME"%"\$PASSWORD"
+
+rap=`$smb4torture --list | grep "^RAP-" | xargs`
+for t in $rap; do
+ plansmbtorturetest "$t" dc $ADDARGS //\$SERVER/IPC\\\$ -U"\$USERNAME"%"\$PASSWORD"
+done
+
+# Tests against the NTVFS CIFS backend
+for t in $base $raw; do
+ plantest "ntvfs.cifs.`normalize_testname $t`" dc $VALGRIND $smb4torture //\$NETBIOSNAME/cifs -U"\$USERNAME"%"\$PASSWORD" $NTVFSARGS $t
+done
+
+# Local tests
+
+for t in `$smb4torture --list | grep "^LOCAL-" | xargs`; do
+ plansmbtorturetest "$t" none ncalrpc: "$*"
+done
+
+tdbtorture4="$samba4bindir/tdbtorture${EXEEXT}"
+if test -f $tdbtorture4
+then
+ plantest "tdb.stress" none $VALGRIND $tdbtorture4
+fi
+
+# Pidl tests
+
+if test x"${PIDL_TESTS_SKIP}" = x"yes"; then
+ echo "Skipping pidl tests - PIDL_TESTS_SKIP=yes"
+elif $PERL -e 'eval require Test::More;' > /dev/null 2>&1; then
+ for f in $samba4srcdir/../pidl/tests/*.pl; do
+ plantest "pidl.`basename $f .pl`" none $PERL $f "|" $PERL $samba4srcdir/../lib/subunit/harness2subunit.pl
+ done
+else
+ echo "Skipping pidl tests - Test::More not installed"
+fi
+
+# Blackbox Tests:
+# tests that interact directly with the command-line tools rather than using
+# the API. These mainly test that the various command-line options of commands
+# work correctly.
+
+plantest "blackbox.ndrdump" none $samba4srcdir/librpc/tests/test_ndrdump.sh
+plantest "blackbox.net" dc $samba4srcdir/utils/tests/test_net.sh "\$SERVER" "\$USERNAME" "\$PASSWORD" "\$DOMAIN"
+plantest "blackbox.kinit" dc $bbdir/test_kinit.sh "\$SERVER" "\$USERNAME" "\$PASSWORD" "\$REALM" "\$DOMAIN" "$PREFIX" $CONFIGURATION
+plantest "blackbox.cifsdd" dc $samba4srcdir/client/tests/test_cifsdd.sh "\$SERVER" "\$USERNAME" "\$PASSWORD" "\$DOMAIN"
+plantest "blackbox.nmblookup" dc $samba4srcdir/utils/tests/test_nmblookup.sh "\$NETBIOSNAME" "\$NETBIOSALIAS" "\$SERVER" "\$SERVER_IP"
+plantest "blackbox.nmblookup" member $samba4srcdir/utils/tests/test_nmblookup.sh "\$NETBIOSNAME" "\$NETBIOSALIAS" "\$SERVER" "\$SERVER_IP"
+plantest "blackbox.locktest" dc $samba4srcdir/torture/tests/test_locktest.sh "\$SERVER" "\$USERNAME" "\$PASSWORD" "\$DOMAIN" "$PREFIX"
+plantest "blackbox.masktest" dc $samba4srcdir/torture/tests/test_masktest.sh "\$SERVER" "\$USERNAME" "\$PASSWORD" "\$DOMAIN" "$PREFIX"
+plantest "blackbox.gentest" dc $samba4srcdir/torture/tests/test_gentest.sh "\$SERVER" "\$USERNAME" "\$PASSWORD" "\$DOMAIN" "$PREFIX"
+plantest "blackbox.wbinfo" dc:local $samba4srcdir/../nsswitch/tests/test_wbinfo.sh "\$DOMAIN" "\$USERNAME" "\$PASSWORD" "dc"
+plantest "blackbox.wbinfo" member:local $samba4srcdir/../nsswitch/tests/test_wbinfo.sh "\$DOMAIN" "\$DC_USERNAME" "\$DC_PASSWORD" "member"
+
+# Tests using the "Simple" NTVFS backend
+
+for t in "BASE-RW1"; do
+ plantest "ntvfs.simple.`normalize_testname $t`" dc $VALGRIND $smb4torture $ADDARGS //\$SERVER/simple -U"\$USERNAME"%"\$PASSWORD" $t
+done
+
+# Domain Member Tests
+
+plantest "rpc.echo against member server with local creds" member $VALGRIND $smb4torture ncacn_np:"\$NETBIOSNAME" -U"\$NETBIOSNAME/\$USERNAME"%"\$PASSWORD" RPC-ECHO "$*"
+plantest "rpc.echo against member server with domain creds" member $VALGRIND $smb4torture ncacn_np:"\$NETBIOSNAME" -U"\$DOMAIN/\$DC_USERNAME"%"\$DC_PASSWORD" RPC-ECHO "$*"
+plantest "rpc.samr against member server with local creds" member $VALGRIND $smb4torture ncacn_np:"\$NETBIOSNAME" -U"\$NETBIOSNAME/\$USERNAME"%"\$PASSWORD" "RPC-SAMR" "$*"
+plantest "rpc.samr.users against member server with local creds" member $VALGRIND $smb4torture ncacn_np:"\$NETBIOSNAME" -U"\$NETBIOSNAME/\$USERNAME"%"\$PASSWORD" "RPC-SAMR-USERS" "$*"
+plantest "rpc.samr.passwords against member server with local creds" member $VALGRIND $smb4torture ncacn_np:"\$NETBIOSNAME" -U"\$NETBIOSNAME/\$USERNAME"%"\$PASSWORD" "RPC-SAMR-PASSWORDS" "$*"
+plantest "blackbox.smbclient against member server with local creds" member $samba4srcdir/client/tests/test_smbclient.sh "\$NETBIOSNAME" "\$USERNAME" "\$PASSWORD" "\$NETBIOSNAME" "$PREFIX"
+
+# Tests SMB signing
+
+for mech in \
+ "-k no" \
+ "-k no --option=usespnego=no" \
+ "-k no --option=gensec:spengo=no" \
+ "-k yes" \
+ "-k yes --option=gensec:fake_gssapi_krb5=yes --option=gensec:gssapi_krb5=no"; do
+ for signing in \
+ "--signing=on" \
+ "--signing=required"; do
+
+ signoptions="$mech $signing"
+ name="smb.signing on with $signoptions"
+ plantest "$name" dc $VALGRIND $smb4torture //"\$NETBIOSNAME"/tmp $signoptions -U"\$USERNAME"%"\$PASSWORD" BASE-XCOPY "$*"
+ done
+done
+
+for mech in \
+ "-k no" \
+ "-k no --option=usespnego=no" \
+ "-k no --option=gensec:spengo=no" \
+ "-k yes" \
+ "-k yes --option=gensec:fake_gssapi_krb5=yes --option=gensec:gssapi_krb5=no"; do
+ signoptions="$mech --signing=off"
+ name="smb.signing on with $signoptions"
+ plantest "$name domain-creds" member $VALGRIND $smb4torture //"\$NETBIOSNAME"/tmp $signoptions -U"\$DC_USERNAME"%"\$DC_PASSWORD" BASE-XCOPY "$*"
+done
+for mech in \
+ "-k no" \
+ "-k no --option=usespnego=no" \
+ "-k no --option=gensec:spengo=no"; do
+ signoptions="$mech --signing=off"
+ name="smb.signing on with $signoptions"
+ plantest "$name local-creds" member $VALGRIND $smb4torture //"\$NETBIOSNAME"/tmp $signoptions -U"\$NETBIOSNAME/\$USERNAME"%"\$PASSWORD" BASE-XCOPY "$*"
+done
+plantest "smb.signing --signing=yes anon" dc $VALGRIND $smb4torture //"\$NETBIOSNAME"/tmp -k no --signing=yes -U% BASE-XCOPY "$*"
+plantest "smb.signing --signing=required anon" dc $VALGRIND $smb4torture //"\$NETBIOSNAME"/tmp -k no --signing=required -U% BASE-XCOPY "$*"
+plantest "smb.signing --signing=no anon" member $VALGRIND $smb4torture //"\$NETBIOSNAME"/tmp -k no --signing=no -U% BASE-XCOPY "$*"
+
+NBT_TESTS=`$smb4torture --list | grep "^NBT-" | xargs`
+
+for t in $NBT_TESTS; do
+ plansmbtorturetest "$t" dc //\$SERVER/_none_ -U\$USERNAME%\$PASSWORD
+done
+
+WB_OPTS="--option=\"torture:strict mode=no\""
+WB_OPTS="${WB_OPTS} --option=\"torture:timelimit=1\""
+WB_OPTS="${WB_OPTS} --option=\"torture:winbindd separator=/\""
+WB_OPTS="${WB_OPTS} --option=\"torture:winbindd netbios name=\$SERVER\""
+WB_OPTS="${WB_OPTS} --option=\"torture:winbindd netbios domain=\$DOMAIN\""
+
+WINBIND_STRUCT_TESTS=`$smb4torture --list | grep "^WINBIND-STRUCT" | xargs`
+WINBIND_NDR_TESTS=`$smb4torture --list | grep "^WINBIND-NDR" | xargs`
+for env in dc member; do
+ for t in $WINBIND_STRUCT_TESTS; do
+ plansmbtorturetest $t $env $WB_OPTS //_none_/_none_
+ done
+
+ for t in $WINBIND_NDR_TESTS; do
+ plansmbtorturetest $t $env $WB_OPTS //_none_/_none_
+ done
+done
+
+nsstest4="$samba4bindir/nsstest${EXEEXT}"
+if test -f $nsstest4
+then
+ plantest "nss.test using winbind" member $VALGRIND $nsstest4 $samba4bindir/shared/libnss_winbind.so
+fi
+
+SUBUNITRUN="$VALGRIND $PYTHON $samba4srcdir/scripting/bin/subunitrun"
+plantest "ldb.python" none PYTHONPATH="$PYTHONPATH:$samba4srcdir/lib/ldb/tests/python/" $SUBUNITRUN api
+plantest "credentials.python" none PYTHONPATH="$PYTHONPATH:$samba4srcdir/auth/credentials/tests" $SUBUNITRUN bindings
+plantest "registry.python" none PYTHONPATH="$PYTHONPATH:$samba4srcdir/lib/registry/tests/" $SUBUNITRUN bindings
+plantest "tdb.python" none PYTHONPATH="$PYTHONPATH:../lib/tdb/python/tests" $SUBUNITRUN simple
+plantest "auth.python" none PYTHONPATH="$PYTHONPATH:$samba4srcdir/auth/tests/" $SUBUNITRUN bindings
+plantest "security.python" none PYTHONPATH="$PYTHONPATH:$samba4srcdir/libcli/security/tests" $SUBUNITRUN bindings
+plantest "param.python" none PYTHONPATH="$PYTHONPATH:$samba4srcdir/param/tests" $SUBUNITRUN bindings
+plantest "upgrade.python" none $SUBUNITRUN samba.tests.upgrade
+plantest "samba.python" none $SUBUNITRUN samba.tests
+plantest "provision.python" none $SUBUNITRUN samba.tests.provision
+plantest "samba3.python" none $SUBUNITRUN samba.tests.samba3
+plantest "samr.python" dc:local $SUBUNITRUN samba.tests.dcerpc.sam
+plantest "dcerpc.bare.python" dc:local $SUBUNITRUN samba.tests.dcerpc.bare
+plantest "unixinfo.python" dc:local $SUBUNITRUN samba.tests.dcerpc.unix
+plantest "samdb.python" none $SUBUNITRUN samba.tests.samdb
+plantest "tevent.python" none PYTHONPATH="$PYTHONPATH:../lib/tevent" $SUBUNITRUN tests
+plantest "messaging.python" none PYTHONPATH="$PYTHONPATH:$samba4srcdir/lib/messaging/tests" $SUBUNITRUN bindings
+plantest "samba3sam.python" none PYTHONPATH="$PYTHONPATH:$samba4srcdir/dsdb/samdb/ldb_modules/tests" $SUBUNITRUN samba3sam
+plantest "subunit.python" none $SUBUNITRUN subunit
+plantest "rpcecho.python" dc:local $SUBUNITRUN samba.tests.dcerpc.rpcecho
+plantest "winreg.python" dc:local $SUBUNITRUN -U\$USERNAME%\$PASSWORD samba.tests.dcerpc.registry
+plantest "ldap.python" dc $PYTHON $samba4srcdir/lib/ldb/tests/python/ldap.py $CONFIGURATION \$SERVER -U\$USERNAME%\$PASSWORD -W \$DOMAIN
+plantest "blackbox.samba3dump" none $PYTHON $samba4srcdir/scripting/bin/samba3dump $samba4srcdir/../testdata/samba3
+rm -rf $PREFIX/upgrade
+plantest "blackbox.upgrade" none $PYTHON $samba4srcdir/setup/upgrade $CONFIGURATION --targetdir=$PREFIX/upgrade $samba4srcdir/../testdata/samba3 ../testdata/samba3/smb.conf
+rm -rf $PREFIX/provision
+mkdir $PREFIX/provision
+plantest "blackbox.provision.py" none PYTHON="$PYTHON" $samba4srcdir/setup/tests/blackbox_provision.sh "$PREFIX/provision"
+plantest "blackbox.provision-backend.py" none PYTHON="$PYTHON" $samba4srcdir/setup/tests/blackbox_provision-backend.sh "$PREFIX/provision"
+plantest "blackbox.setpassword.py" none PYTHON="$PYTHON" $samba4srcdir/setup/tests/blackbox_setpassword.sh "$PREFIX/provision"
+plantest "blackbox.newuser.py" none PYTHON="$PYTHON" $samba4srcdir/setup/tests/blackbox_newuser.sh "$PREFIX/provision"
diff --git a/source4/selftest/tests_win.sh b/source4/selftest/tests_win.sh
new file mode 100755
index 0000000000..19460eee0e
--- /dev/null
+++ b/source4/selftest/tests_win.sh
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+ if [ `whoami` != "root" ]; then
+ echo "Windows tests will not run without root privileges."
+ exit 1
+ fi
+
+ if [ "$DO_SOCKET_WRAPPER" = SOCKET_WRAPPER ]; then
+ echo "Windows tests will not run with socket wrapper enabled."
+ exit 1
+ fi
+
+ if [ ! $WINTESTCONF ]; then
+ echo "Environment variable WINTESTCONF has not been defined."
+ echo "Windows tests will not run unconfigured."
+ exit 1
+ fi
+
+ if [ ! -r $WINTESTCONF ]; then
+ echo "$WINTESTCONF could not be read."
+ exit 1
+ fi
+
+ export WINTEST_DIR=$SRCDIR/selftest/win
+ export TMPDIR=$TMPDIR
+ export NETBIOSNAME=$NETBIOSNAME
+
+ . $WINTESTCONF
+
+ $SRCDIR/selftest/test_win.sh
diff --git a/source4/selftest/tests_win2k3_dc.sh b/source4/selftest/tests_win2k3_dc.sh
new file mode 100755
index 0000000000..290a4ef666
--- /dev/null
+++ b/source4/selftest/tests_win2k3_dc.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+if [ ! $WINTESTCONF ]; then
+ echo "Environment variable WINTESTCONF has not been defined."
+ echo "Windows tests will not run unconfigured."
+ exit 1
+fi
+
+if [ ! -r $WINTESTCONF ]; then
+ echo "$WINTESTCONF could not be read."
+ exit 1
+fi
+
+. selftest/test_functions.sh
+
+export SRCDIR=$SRCDIR
+
+tests="RPC-DRSUAPI RPC-SPOOLSS ncacn_np ncacn_ip_tcp"
+
+for name in $tests; do
+ testit $name rpc $SRCDIR/selftest/win/wintest_2k3_dc.sh $name
+done
diff --git a/source4/selftest/win/README b/source4/selftest/win/README
new file mode 100644
index 0000000000..fc934385c4
--- /dev/null
+++ b/source4/selftest/win/README
@@ -0,0 +1,121 @@
+This framework uses a VMware Server hosted Windows guest VM to test the
+behaviour of Windows -> Samba and Samba -> Windows interactions. To setup a
+Windows host for testing, vm_setup.tar.gz contain some scripts which create
+an administrative user account, and enable and start the installed telnet
+service on the Windows host. Optionally, the hostname and workgroup name can
+also be set. vm_setup.tar.gz is currently located in the SOC/bnh branch of
+Samba's SVN repository.
+
+PREREQUISITES
+
+To use these scripts, VMware Server needs to be running with a Windows guest
+VM installed, IP addressed, and VMware tools needs to be installed and running
+on the guest VM. The Windows OS I used to test with was Windows Server 2003,
+but I think this should work with any version of Windows that has the
+Microsoft telnet service installed. The VMware Server versions I used for
+testing was 1.0.0 build-27828, and 1.0.0 build-28343.
+
+PLEASE NOTE: Due to problems with my original revert_snapshot() code, the initial
+setup now requires that the VM configuration setting 'When Powering Off' is
+manually set to 'Revert to snapshot' (snapshot.action="autoRevert" in the
+guest's .vmx file). This should not be a permanent change, but the original
+revert_snapshot() code I wrote no longer works and i'm not sure why.
+
+On the machine that these scripts are running on (this need not be the same
+machine as the VMware host), the VMware perl scripting api needs to be
+installed, as well as the vix-perl api. These come with the VMware Server
+console package.
+
+After unzipping this file, the libraries are installed by extracting the
+VMware-vix-e.x.p-<revision number>.tar.gz and
+VMware-VmPerlAPI-e.x.p-<revision number>.tar.gz archives, and running the
+vmware-install.pl scripts inside their respective directories.
+
+On Slackware 10.2, I encountered a problem in that when I tried to use the vix
+api libraries, I would get the following error:
+
+SSLLoadSharedLibrary: Failed to load library /<client program directory>/libcrypto.so.0.9.7:/<client program directory>/libcrypto.so.0.9.7: cannot open a shared object file: No such file or directory.
+
+The fix found on the VMware knowledge base (search http://kb.vmware.com for
+Doc ID: 1837104) states that it's a known problem with the scripting libraries,
+and can be resolved by installing VMware Server on the host, which properly
+sets up the SSL module loader. This is what I would suggest if you encounter
+this, as it solved the problem for me (I don't have VMware Server actually
+running on that host though).
+
+INSTALLATION
+
+To use these scripts, modify initial_setup.conf to match your environment. The
+GUEST_HOSTNAME, GUEST_WORKGROUP, HOST_SERVER_NAME, HOST_SERVER_PORT,
+HOST_USERNAME, and HOST_PASSWORD variables are optional, and are commented out
+in this release.
+
+Running initial_setup.sh will:
+* Get the IP address of the Windows guest VM.
+* Take a snapshot of the pristine Windows guest.
+* Copy the windows scripts from the windows-scripts directory on the unix host
+ to the directory on the Windows guest specified by the
+ GUEST_SCRIPT_PATH option. This path will be created on the guest if
+ it does not already exist.
+* Execute win_setup.wsf on the Windows guest in order to create the
+ administrator account specified by GUEST_USERNAME and GUEST_PASSWORD,
+ enable and start the telnet service, and set the GUEST_HOSTNAME and
+ GUEST_WORKGROUP if configured.
+* If these operations are successful so far, another snapshot is taken at this
+ point. This is the snapshot which is restored if the tests encounter
+ problems they are unable to recover from.
+
+These operations leave the Windows guest in a state such that it can be
+remotely administered with telnet. Specifically, this will allow us to use
+'make wintest' in Samba 4 to perform smbtorture tests against a Windows host,
+and perform tests from a Windows client to a Samba server.
+
+INTEGRATING WITH THE BUILD FARM
+
+Follow the standard steps to add a host to the build farm. The major
+difference is that we will need to run these tests as root. To run the
+Windows tests in the build farm, a .fns file will need to be created for
+your new host that exports a WINTESTCONF environment variable pointing to a
+config file used by 'make wintest'. An example of this config file can be
+found at source/selftest/win/test_win.conf in the Samba 4 source tree.
+
+I've also included the bnhtest.fns file that I'm using for my build farm host
+below, as an example. It was modified from generic.fns.
+
+action_test_windows() {
+ do_make wintest
+ w_status=$?
+ echo "WINTEST STATUS: $w_status"
+ return $w_status;
+}
+
+per_run_hook
+
+system=`uname`
+
+export WINTESTCONF="/home/build/win/test_win.conf"
+
+for compiler in gcc cc icc; do
+
+ # arrgh, "which" gives no err code on solaris
+ path=`which $compiler`
+ if [ -x "$path" ]; then
+
+ if $compiler -v 2>&1 | grep gcc.version > /dev/null; then
+ isgcc=1
+ CFLAGS="-Wall"
+ export CFLAGS
+ else
+ CFLAGS=""
+ export CFLAGS
+ isgcc=0
+ fi
+ if [ $compiler = gcc -o $isgcc = 0 ]; then
+
+ # only attempt samba4 if we have perl
+ if which perl > /dev/null; then
+ test_tree samba4 source $compiler configure build install test_windows test
+ fi
+ fi
+ fi
+done
diff --git a/source4/selftest/win/VMHost.pm b/source4/selftest/win/VMHost.pm
new file mode 100644
index 0000000000..1f02f21c28
--- /dev/null
+++ b/source4/selftest/win/VMHost.pm
@@ -0,0 +1,359 @@
+#!/usr/bin/perl -w
+
+# A perl object to provide a simple, unified method of handling some
+# VMware Server VM management functions using the perl and VIX API's.
+# Copyright Brad Henry <brad@samba.org> 2006
+# Released under the GNU GPL version 3 or later.
+
+# VMware Perl API
+use VMware::VmPerl;
+use VMware::VmPerl::VM;
+use VMware::VmPerl::ConnectParams;
+
+# VMware C bindings
+use VMware::Vix::Simple;
+use VMware::Vix::API::Constants;
+
+# Create a class to abstract from the Vix and VMPerl APIs.
+{ package VMHost;
+ my $perl_vm = VMware::VmPerl::VM::new();
+ my $perl_vm_credentials;
+ my $vix_vm;
+ my $vix_vm_host;
+
+ my $err_code = 0;
+ my $err_str = "";
+
+ my $hostname;
+ my $port;
+ my $username;
+ my $password;
+ my $vm_cfg_path;
+ my $guest_admin_username;
+ my $guest_admin_password;
+
+ sub error {
+ my $old_err_code = $err_code;
+ my $old_err_str = $err_str;
+ $err_code = 0;
+ $err_str = "";
+ return ($old_err_code, $old_err_str);
+ }
+
+ # Power on the guest if it isn't already running.
+ # Returns 0 when the guest is already running, and
+ # if not, it waits until it is started.
+ sub start_guest {
+ my $vm_power_state = $perl_vm->get_execution_state();
+ if (!defined($vm_power_state)) {
+ ($err_code, $err_str) = $perl_vm->get_last_error();
+ return ($err_code);
+ }
+ if ($vm_power_state == VMware::VmPerl::VM_EXECUTION_STATE_OFF
+ || $vm_power_state ==
+ VMware::VmPerl::VM_EXECUTION_STATE_SUSPENDED)
+ {
+ if (!$perl_vm->start()) {
+ ($err_code, $err_str) =
+ $perl_vm->get_last_error();
+ return ($err_code);
+ }
+ while ($perl_vm->get_tools_last_active() == 0) {
+ sleep(60);
+ }
+ }
+ return ($err_code);
+ }
+
+ sub host_connect {
+ # When called as a method, the first parameter passed is the
+ # name of the method. Called locally, this function will lose
+ # the first parameter.
+ shift @_;
+ ($hostname, $port, $username, $password, $vm_cfg_path,
+ $guest_admin_username, $guest_admin_password) = @_;
+
+ # Connect to host using vmperl api.
+ $perl_vm_credentials =
+ VMware::VmPerl::ConnectParams::new($hostname, $port,
+ $username, $password);
+ if (!$perl_vm->connect($perl_vm_credentials, $vm_cfg_path)) {
+ ($err_code, $err_str) = $perl_vm->get_last_error();
+ undef $perl_vm;
+ return ($err_code);
+ }
+
+ # Connect to host using vix api.
+ ($err_code, $vix_vm_host) =
+ VMware::Vix::Simple::HostConnect(
+ VMware::Vix::Simple::VIX_API_VERSION,
+ VMware::Vix::Simple::VIX_SERVICEPROVIDER_VMWARE_SERVER,
+ $hostname, $port, $username, $password,
+ 0, VMware::Vix::Simple::VIX_INVALID_HANDLE);
+ if ($err_code != VMware::Vix::Simple::VIX_OK) {
+ $err_str =
+ VMware::Vix::Simple::GetErrorText($err_code);
+ undef $perl_vm;
+ undef $vix_vm;
+ undef $vix_vm_host;
+ return ($err_code);
+ }
+
+ # Power on our guest os if it isn't already running.
+ $err_code = start_guest();
+ if ($err_code != 0) {
+ my $old_err_str = $err_str;
+ $err_str = "Starting guest power after connect " .
+ "failed: " . $old_err_str;
+ undef $perl_vm;
+ undef $vix_vm;
+ undef $vix_vm_host;
+ return ($err_code);
+ }
+
+ # Open VM.
+ ($err_code, $vix_vm) =
+ VMware::Vix::Simple::VMOpen($vix_vm_host, $vm_cfg_path);
+ if ($err_code != VMware::Vix::Simple::VIX_OK) {
+ $err_str =
+ VMware::Vix::Simple::GetErrorText($err_code);
+ undef $perl_vm;
+ undef $vix_vm;
+ undef $vix_vm_host;
+ return ($err_code);
+ }
+
+ # Login to $vix_vm guest OS.
+ $err_code = VMware::Vix::Simple::VMLoginInGuest($vix_vm,
+ $guest_admin_username, $guest_admin_password,
+ 0);
+ if ($err_code != VMware::Vix::Simple::VIX_OK) {
+ $err_str =
+ VMware::Vix::Simple::GetErrorText($err_code);
+ undef $perl_vm;
+ undef $vix_vm;
+ undef $vix_vm_host;
+ return ($err_code);
+ }
+ return ($err_code);
+ }
+
+ sub host_disconnect {
+ undef $perl_vm;
+
+ $perl_vm = VMware::VmPerl::VM::new();
+ if (!$perl_vm) {
+ $err_code = 1;
+ $err_str = "Error creating new VmPerl object";
+ }
+
+ undef $vix_vm;
+ VMware::Vix::Simple::HostDisconnect($vix_vm_host);
+ VMware::Vix::Simple::ReleaseHandle($vix_vm_host);
+ return ($err_code);
+ }
+
+ sub host_reconnect {
+ $err_code = host_disconnect();
+ if ($err_code != 0) {
+ my $old_err_str = $err_str;
+ $err_str = "Disconnecting from host failed: " .
+ $old_err_str;
+ return ($err_code);
+ }
+
+ $err_code = host_connect(NULL, $hostname, $port, $username,
+ $password, $vm_cfg_path, $guest_admin_username,
+ $guest_admin_password);
+ if ($err_code != 0) {
+ my $old_err_str = $err_str;
+ $err_str = "Re-connecting to host failed: " .
+ $old_err_str;
+ return ($err_code);
+ }
+ return ($err_code);
+ }
+
+ sub create_snapshot {
+ my $snapshot;
+
+ ($err_code, $snapshot) =
+ VMware::Vix::Simple::VMCreateSnapshot($vix_vm,
+ "Snapshot", "Created by vm_setup.pl", 0,
+ VMware::Vix::Simple::VIX_INVALID_HANDLE);
+
+ VMware::Vix::Simple::ReleaseHandle($snapshot);
+
+ if ($err_code != VMware::Vix::Simple::VIX_OK) {
+ $err_str =
+ VMware::Vix::Simple::GetErrorText($err_code);
+ return $err_code;
+ }
+
+ $err_code = host_reconnect();
+ if ($err_code != 0) {
+ my $old_err_str = $err_str;
+ $err_str = "Reconnecting to host after creating " .
+ "snapshot: " . $old_err_str;
+ return ($err_code);
+ }
+ return ($err_code);
+ }
+
+ sub revert_snapshot {
+ # Because of problems with VMRevertToSnapshot(), we have to
+ # rely on the guest having set 'Revert to Snapshot' following
+ # a power-off event.
+ $err_code = VMware::Vix::Simple::VMPowerOff($vix_vm, 0);
+ if ($err_code != VMware::Vix::Simple::VIX_OK) {
+ $err_str =
+ VMware::Vix::Simple::GetErrorText($err_code);
+ return $err_code;
+ }
+
+ # host_reconnect() will power-on a guest in a non-running state.
+ $err_code = host_reconnect();
+ if ($err_code != 0) {
+ my $old_err_str = $err_str;
+ $err_str = "Reconnecting to host after reverting " .
+ "snapshot: " . $old_err_str;
+ return ($err_code);
+ }
+ return ($err_code);
+ }
+
+ # $dest_path must exist. It doesn't get created.
+ sub copy_files_to_guest {
+ shift @_;
+ my (%files) = @_;
+
+ my $src_file;
+ my $dest_file;
+
+ foreach $src_file (keys(%files)) {
+ $dest_file = $files{$src_file};
+ $err_code =
+ VMware::Vix::Simple::VMCopyFileFromHostToGuest(
+ $vix_vm, $src_file, $dest_file, 0,
+ VMware::Vix::Simple::VIX_INVALID_HANDLE);
+ if ($err_code != VMware::Vix::Simple::VIX_OK) {
+ $err_str = "Copying $src_file: " .
+ VMware::Vix::Simple::GetErrorText(
+ $err_code);
+ return $err_code;
+ }
+ }
+ return $err_code;
+ }
+
+ sub copy_to_guest {
+ # Read parameters $src_path, $dest_path.
+ shift @_;
+ my ($src_path, $dest_dir) = @_;
+
+ my $len = length($dest_dir);
+ my $idx = rindex($dest_dir, '\\');
+ if ($idx != ($len - 1)) {
+ $err_code = -1;
+ $err_str = "Destination $dest_dir must be a " .
+ "directory path";
+ return ($err_code);
+ }
+
+ # Create the directory $dest_path on the guest VM filesystem.
+ my $cmd = "cmd.exe ";
+ my $cmd_args = "/C MKDIR " . $dest_dir;
+ $err_code = run_on_guest(NULL, $cmd, $cmd_args);
+ if ( $err_code != 0) {
+ my $old_err_str = $err_str;
+ $err_str = "Creating directory $dest_dir on host: " .
+ $old_err_str;
+ return ($err_code);
+ }
+
+ # If $src_filepath specifies a file, create it in $dest_path
+ # and keep the same name.
+ # If $src_path is a directory, create the files it contains in
+ # $dest_path, keeping the same names.
+ $len = length($src_path);
+ my %files;
+ $idx = rindex($src_path, '/');
+ if ($idx == ($len - 1)) {
+ # $src_path is a directory.
+ if (!opendir (DIR_HANDLE, $src_path)) {
+ $err_code = -1;
+ $err_str = "Error opening directory $src_path";
+ return $err_code;
+ }
+
+ foreach $file (readdir DIR_HANDLE) {
+ my $src_file = $src_path . $file;
+
+ if (!opendir(DIR_HANDLE2, $src_file)) {
+ # We aren't interested in subdirs.
+ my $dest_path = $dest_dir . $file;
+ $files{$src_file} = $dest_path;
+ } else {
+ closedir(DIR_HANDLE2);
+ }
+ }
+ } else {
+ # Strip if preceeding path from $src_path.
+ my $src_file = substr($src_path, ($idx + 1), $len);
+ my $dest_path = $dest_dir . $src_file;
+
+ # Add $src_path => $dest_path to %files.
+ $files{$src_path} = $dest_path;
+ }
+
+ $err_code = copy_files_to_guest(NULL, %files);
+ if ($err_code != 0) {
+ my $old_err_str = $err_str;
+ $err_str = "Copying files to host after " .
+ "populating %files: " . $old_err_str;
+ return ($err_code);
+ }
+ return ($err_code);
+ }
+
+ sub run_on_guest {
+ # Read parameters $cmd, $cmd_args.
+ shift @_;
+ my ($cmd, $cmd_args) = @_;
+
+ $err_code = VMware::Vix::Simple::VMRunProgramInGuest($vix_vm,
+ $cmd, $cmd_args, 0,
+ VMware::Vix::Simple::VIX_INVALID_HANDLE);
+ if ($err_code != VMware::Vix::Simple::VIX_OK) {
+ $err_str = VMware::Vix::Simple::GetErrorText(
+ $err_code);
+ return ($err_code);
+ }
+
+ return ($err_code);
+ }
+
+ sub get_guest_ip {
+ my $guest_ip = $perl_vm->get_guest_info('ip');
+
+ if (!defined($guest_ip)) {
+ ($err_code, $err_str) = $perl_vm->get_last_error();
+ return NULL;
+ }
+
+ if (!($guest_ip)) {
+ $err_code = 1;
+ $err_str = "Guest did not set the 'ip' variable";
+ return NULL;
+ }
+ return $guest_ip;
+ }
+
+ sub DESTROY {
+ host_disconnect();
+ undef $perl_vm;
+ undef $vix_vm_host;
+ }
+}
+
+return TRUE;
diff --git a/source4/selftest/win/common.exp b/source4/selftest/win/common.exp
new file mode 100644
index 0000000000..28a3d7a6a1
--- /dev/null
+++ b/source4/selftest/win/common.exp
@@ -0,0 +1,521 @@
+# A library of commonly used functions written in expect.
+# Copyright Brad Henry <brad@samba.org> 2006
+# Released under the GNU GPL version 3 or later.
+
+# This function maps a drive letter to a share point.
+proc map_share { remote_prompt share_drive sharepoint username domain password } {
+ set default_err_str "Unknown error in function map_share"
+ set err_str $default_err_str
+
+ set cmd "net use $share_drive $sharepoint $password /USER:$username@$domain\r\n"
+ send $cmd
+
+ expect {
+ "The command completed successfully." {
+ expect_prompt $remote_prompt
+ set err_str "OK"
+ } \
+ "The local device name is already in use." {
+ expect_prompt $remote_prompt
+ set err_str "The device name $share_drive is already in use"
+ } \
+ "The network name cannot be found." {
+ expect_prompt $remote_prompt
+ set err_str "Sharepoint $sharepoint could not be found"
+ } \
+ timeout {
+ set err_str "Function map_share timed out while mapping $share_drive to $sharepoint"
+ }
+ }
+ return $err_str
+}
+
+# This function unmaps a drive letter from a share point.
+proc unmap_share { remote_prompt share_drive } {
+ set default_err_str "Unknown error in function unmap_share"
+ set err_str $default_err_str
+
+ set cmd "net use $share_drive /DELETE\r\n"
+ send $cmd
+
+ expect {
+ "was deleted successfully." {
+ expect_prompt $remote_prompt
+ set err_str "OK"
+ } \
+ "NET HELPMSG 2250" {
+ expect_prompt $remote_prompt
+ set err_str "The network connection could not be found while unmapping $share_drive"
+ } \
+ timeout {
+ set err_str "Function unmap_share timed out while unmapping $share_drive"
+ }
+ }
+ return $err_str
+}
+
+# This function uses xcopy to copy a text file from one location on the
+# remote windows host to another.
+proc xcopy_file { remote_prompt in_filename out_filename xcopy_options } {
+ set default_err_str "Unknown error in function xcopy_file"
+ set err_str $default_err_str
+
+ set cmd "xcopy $in_filename $out_filename $xcopy_options\r\n"
+ send $cmd
+
+ expect {
+ "(F = file, D = directory)? " {
+ set cmd "F\r\n"
+ send $cmd
+ expect {
+ "1 File(s) copied\r\n\r\n" {
+ expect_prompt $remote_prompt
+ set err_str "OK"
+ } \
+ "0 File(s) copied\r\n\r\n" {
+ expect_prompt $remote_prompt
+ set err_str $default_err_str
+ } \
+ timeout {
+ set err_str "Function xcopy_file has timed out while copying $in_filename"
+ }
+ }
+ } \
+ "1 File(s) copied\r\n\r\n" {
+ expect_prompt $remote_prompt
+ set err_str "OK"
+ } \
+ "0 File(s) copied\r\n\r\n" {
+ expect_prompt $remote_prompt
+ set err_str $default_err_str
+ } \
+ timeout {
+ set err_str "Function xcopy_file timed out while copying $in_filename"
+ }
+ }
+ return $err_str
+}
+
+# This function creates a temporary file on the remote windows host.
+# The file contents are populated by a recursive directory listing of
+# the windows %HOMEDRIVE%.
+proc create_tmp_file { remote_prompt filename } {
+ set default_err_str "Unknown error in function create_tmp_file"
+ set err_str $default_err_str
+
+ set cmd "dir %HOMEDRIVE%\\ /S > $filename\r\n"
+ send $cmd
+ expect {
+ $remote_prompt {
+ set err_str "OK"
+ } \
+ timeout {
+ set err_str "Function create_tmp_file timed out while creating $filename"
+ }
+ }
+ return $err_str
+}
+
+# This function compares two files on the remote windows host.
+proc compare_files { remote_prompt file1 file2 } {
+ set default_err_str "Unknown error in function compare_files"
+ set err_str $default_err_str
+
+ set cmd "fc $file1 $file2\r\n"
+ send $cmd
+ expect {
+ "FC: no differences encountered\r\n\r\n\r\n" {
+ expect_prompt $remote_prompt
+ set err_str "OK"
+ } \
+ "\*\*\*\*\* $file1" {
+ expect_prompt $remote_prompt
+ set err_str "Files $file1 and $file2 differ"
+ } \
+ "\*\*\*\*\* $file2" {
+ expect_prompt $remote_prompt
+ set err_str "Files $file1 and $file2 differ"
+ } \
+ timeout {
+ set err_str "Function compare_files timed out while comparing files $file1 and $file2"
+ }
+ }
+ return $err_str
+}
+
+# This function deletes a file on the remote windows host.
+proc delete_file { remote_prompt filename } {
+ set default_err_str "Unknown error in function delete_file"
+ set err_str $default_err_str
+
+ set cmd "del $filename\r\n"
+ send $cmd
+ expect {
+ "Could Not" {
+ expect_prompt $remote_prompt
+ set err_str $default_err_str
+ } \
+ $remote_prompt {
+ set err_str "OK"
+ } \
+ timeout {
+ set err_str "Function delete_file timed oout while deleting $filename"
+ }
+ }
+ return $err_str
+}
+
+# This function copies a text file over telnet from the local unix host
+# to the remote windows host.
+proc copy_file { remote_prompt in_filename out_filename } {
+ set default_err_str "Unknown error in function copy_file"
+ set err_str $default_err_str
+
+ # The octal ASCII code for Control-Z is 032.
+ set CTRLZ \032
+
+ # Open local file and read contents.
+ set in_file [open $in_filename r]
+ set in_data [read $in_file]
+
+ # Initiate copy on remote host.
+ set cmd "copy con $out_filename\r\n"
+ send $cmd
+
+ # Separate $in_data into lines and send to remote host.
+ set out_data [split $in_data "\n"]
+ foreach out_line $out_data {
+ send $out_line
+ # We might as well do a unix -> windows line conversion.
+ send "\r\n"
+ # Are we overwriting an existing file?
+ # If so, exit so we can handle it.
+ expect {
+ "(Yes/No/All)" {
+ send "NO\r\n"
+ expect_prompt $remote_prompt
+ set err_str "File exists"
+ } \
+ $out_line {
+ set err_str "OK"
+ } \
+ timeout {
+ set err_str "Function copy_file timed out while copying $in_filename"
+ }
+ }
+ if { $err_str != "OK" } {
+ return $err_str
+ } else {
+ set err_str $default_err_str
+ }
+ }
+
+ # ^Z\r to complete the transfer.
+ send $CTRLZ
+ send "\r"
+ expect {
+ "file(s) copied." {
+ set err_str [expect_prompt $remote_prompt]
+ } \
+ $remote_prompt {
+ set err_str $default_err_str
+ } \
+ timeout {
+ expect_prompt $remote_prompt
+ set err_str "Function copy_file timed out while finishing copy of $in_filename"
+ }
+ }
+ return $err_str
+}
+
+# This function waits for the command prompt and reports an error on
+# timeout.
+proc expect_prompt { remote_prompt } {
+ set default_err_str "Unknown error occurred while waiting for the command prompt"
+ set err_str $default_err_str
+
+ expect {
+ $remote_prompt {
+ set err_str "OK"
+ } \
+ timeout {
+ set err_str "Timeout occurred while waiting for the command prompt"
+ }
+ }
+ return $err_str
+}
+
+# This function will create a telnet login shell to $remote_host as $username.
+# If expected dialogue is not recieved, return with a specific error if one
+# is recognized. Otherwise return a generic error indicating the function
+# name.
+proc telnet_login { remote_prompt remote_host username password } {
+
+ set default_err_str "Unknown error in function telnet_login"
+ set err_str $default_err_str
+
+ set cmd "telnet $remote_host\r"
+ send $cmd
+ expect {
+ "login: " {
+ set err_str "OK"
+ } \
+ "Connection refused" {
+ set err_str "Connection refused"
+ } \
+ "No route to host" {
+ set err_str "No route to host"
+ } \
+ timeout {
+ set err_str "Function telnet_login timed out while waiting for the login prompt"
+ }
+ }
+ if { $err_str != "OK" } {
+ # Return because something unexpected happened.
+ return $err_str
+ } else {
+ # Reset err_str
+ set err_str $default_err_str
+ }
+
+ set cmd "$username\r"
+ send $cmd
+ expect {
+ "password: " {
+ set err_str "OK"
+ } \
+ timeout {
+ set err_str "Function telnet_login timed out while waiting for the password prompt"
+ }
+ }
+ if { $err_str != "OK" } {
+ return $err_str
+ } else {
+ set err_str $default_err_str
+ }
+
+ set cmd "$password\r"
+ send $cmd
+ expect {
+ $remote_prompt {
+ set err_str "OK"
+ } \
+ "Login Failed" {
+ set err_str "Telnet login failed"
+ } \
+ timeout {
+ set err_str "Function telnet_login timed out while waiting for the command prompt"
+ }
+ }
+ return $err_str
+}
+
+proc create_directory { remote_prompt sharepath } {
+
+ set default_err_str "Unknown error in function create_directory"
+ set err_str $default_err_str
+
+ set cmd "mkdir $sharepath\r\n"
+ send $cmd
+ expect {
+ "already exists" {
+ expect_prompt $remote_prompt
+ set err_str "Directory already exists"
+ } \
+ $remote_prompt {
+ set err_str "OK"
+ } \
+ timeout {
+ expect_prompt $remote_prompt
+ set err_str "Timeout reached starting create_directory."
+ }
+ }
+ return $err_str
+}
+
+proc delete_directory { remote_prompt sharepath } {
+
+ set default_err_str "Unknown error in function delete_directory"
+ set err_str $default_err_str
+
+ set cmd "rmdir /S /Q $sharepath\r\n"
+ send $cmd
+ expect {
+ "Access is denied." {
+ expect_prompt $remote_prompt
+ set err_str "Directory access is denied"
+ } \
+ $remote_prompt {
+ set err_str "OK"
+ } \
+ timeout {
+ expect_prompt $remote_prompt
+ set err_str "Timeout reached in delete_directory"
+ }
+ }
+ return $err_str
+}
+
+proc create_share { remote_prompt username sharepath sharename } {
+
+ set default_err_str "Unknown error in function create_share"
+ set err_str $default_err_str
+
+ set cmd "net share $sharename=$sharepath /GRANT:$username,FULL\r\n"
+ send $cmd
+ expect {
+ "was shared successfully." {
+ set err_str [expect_prompt $remote_prompt]
+ } \
+ "NET HELPMSG 2118." {
+ expect_prompt $remote_prompt
+ set err_str "The name has already been shared"
+ } \
+ $remote_prompt {
+ set err_str $default_err_str
+ } \
+ timeout {
+ expect_prompt $remote_prompt
+ set err_str "Timeout reached in create_share"
+ }
+ }
+ return $err_str
+}
+
+proc delete_share { remote_prompt sharename } {
+
+ set default_err_str "Unknown error in function delete_share"
+ set err_str $default_err_str
+
+ set cmd "net share $sharename /DELETE\r\n"
+ send $cmd
+ expect {
+ "was deleted successfully." {
+ set err_str [expect_prompt $remote_prompt]
+ } \
+ "does not exist." {
+ expect_prompt $remote_prompt
+ set err_str "The share does not exist"
+ } \
+ $remote_prompt {
+ set err_str $default_err_str
+ } \
+ timeout {
+ expect_prompt $remote_prompt
+ set err_str "Timeout reached in delete_share"
+ }
+ }
+ return $err_str
+}
+
+proc delete_hosts_entry { remote_prompt hosts_file_path backup_hosts_filename } {
+
+ set default_err_str "Unknown error in function delete_hosts_entry"
+ set err_str $default_err_str
+
+ set cmd "cd $hosts_file_path\r\n"
+ send $cmd
+ expect {
+ "." {
+ expect_prompt $remote_prompt
+ set err_str $default_err_str
+ } \
+ $remote_prompt {
+ set err_str "OK"
+ } \
+ timeout {
+ expect_prompt $remote_prompt
+ set err_str "Timeout reached in delete_hosts_entry"
+ }
+ }
+ if { $err_str != "OK" } {
+ return $err_str
+ } else {
+ set err_str $default_err_str
+ }
+
+ set cmd "move /Y $backup_hosts_filename hosts\r\n"
+ send $cmd
+ expect {
+ "1 file(s) moved." {
+ set err_str [expect_prompt $remote_prompt]
+ } \
+ "cannot find the file specified." {
+ expect_prompt $remote_prompt
+ set err_str "File not found"
+ } \
+ $remote_prompt {
+ set err_str $default_err_str
+ } \
+ timeout {
+ expect_prompt $remote_prompt
+ set err_str "Function delete_hosts_entry timed out while renaming $backup_hosts_filename"
+ }
+ }
+ return $err_str
+}
+
+proc create_hosts_entry { remote_prompt hosts_file_path hostname ip \
+ backup_hosts_filename } {
+
+ set default_err_str "Unknown error in function create_hosts_entry"
+ set err_str $default_err_str
+
+ set cmd "cd $hosts_file_path\r\n"
+ send $cmd
+ expect {
+ "." {
+ expect_prompt $remote_prompt
+ set err_str $default_err_str
+ } \
+ $remote_prompt {
+ set err_str "OK"
+ } \
+ timeout {
+ expect_prompt $remote_prompt
+ set err_str "Timeout reached in create_hosts_entry"
+ }
+ }
+ if { $err_str != "OK" } {
+ return $err_str
+ } else {
+ set err_str $default_err_str
+ }
+
+ set cmd "copy /Y hosts $backup_hosts_filename\r\n"
+ send $cmd
+ expect {
+ "1 file(s) copied." {
+ set err_str [expect_prompt $remote_prompt]
+ } \
+ "cannot find the file specified." {
+ expect_prompt $remote_prompt
+ set err_str "File not found."
+ } \
+ $remote_prompt {
+ set err_str $default_err_str
+ } \
+ timeout {
+ expect_prompt $remote_prompt
+ set err_str "Function create_hosts_entry timed out while copying hosts file"
+ }
+ }
+ if { $err_str != "OK" } {
+ return $err_str
+ } else {
+ set err_str $default_err_str
+ }
+
+ set cmd "echo $ip $hostname #smbtorture host. >> hosts\r\n"
+ send $cmd
+ expect {
+ $remote_prompt {
+ set err_str "OK"
+ } \
+ timeout {
+ expect_prompt $remote_prompt
+ set err_str "Function create_hosts timed out while updating hosts file"
+ }
+ }
+ return $err_str
+}
diff --git a/source4/selftest/win/test_win.conf b/source4/selftest/win/test_win.conf
new file mode 100644
index 0000000000..ed52be999e
--- /dev/null
+++ b/source4/selftest/win/test_win.conf
@@ -0,0 +1,83 @@
+
+# perl needs to know to look in $WINTEST_DIR for VMHost.pm.
+export PERLLIB=$WINTEST_DIR
+
+# Command prompt that we are expecting on the windows host.
+export SMBTORTURE_REMOTE_PROMPT=">"
+
+##
+## The variables in this section apply to the 'make wintest_dc' set of tests.
+##
+
+# A username and password with admin rights to the DC we're testing against.
+export WIN2K3_DC_USERNAME="tortureuser"
+export WIN2K3_DC_PASSWORD="torturepass"
+
+# The domain and realm that the DC is configured for.
+export WIN2K3_DC_DOMAIN="WINTESTDC"
+export WIN2K3_DC_REALM="wintest.dc"
+
+# The path to the DC vmware image config file, local to the vmware server.
+export WIN2K3_DC_VM_CFG_PATH="/var/lib/vmware/Virtual Machines/Windows Server 2003 DC BuildFarm/Windows 2003 DC BuildFarm.vmx"
+
+##
+## The parameters in this section apply to the 'make wintest' set of tests.
+##
+
+# The username and password we will be testing with.
+# This user will need admin rights on the remote windows host.
+export SMBTORTURE_USERNAME="tortureuser"
+export SMBTORTURE_PASSWORD="torturepass"
+
+# The name of the workgroup we will be using on the remote windows host.
+export SMBTORTURE_WORKGROUP="SMBTEST"
+
+# The name of and path to the windows share we'll be testing against.
+export SMBTORTURE_REMOTE_SHARE_NAME="smbtorture_share"
+export SMBTORTURE_REMOTE_SHARE_PATH="%HOMEDRIVE%\smbtorture_shared_dir"
+
+# Default timeout for the expect scripts to wait for a response from the remote.
+export SMBTORTURE_EXPECT_TIMEOUT=30
+
+# Path to the local smbtorture binary.
+export SMBTORTURE_BIN_PATH="bin/smbtorture"
+
+# Local system hostname and ip address we'll be adding to the remote's
+# hosts file.
+export SMBTORTURE_LOCAL_HOSTNAME=$NETBIOSNAME
+export SMBTORTURE_LOCAL_IP="192.168.100.12"
+
+# Filename of the windows hosts' unedited hosts file.
+export REMOTE_BACKUP_HOSTS_FILENAME="hosts.smbtorture"
+export REMOTE_HOSTS_FILE_PATH="%SYSTEMROOT%\\system32\\drivers\\etc"
+
+# These coincide with the parameters mktestsetup.sh uses to setup smbd.
+export SMBTORTURE_LOCAL_USERNAME="administrator"
+export SMBTORTURE_LOCAL_PASSWORD="penguin"
+export SMBTORTURE_LOCAL_DOMAIN="SAMBADOMAIN"
+
+# This is the name of the samba share the windows vm will connect to.
+export SMBTORTURE_LOCAL_SHARE_NAME="TMP"
+
+# This is the drive letter which will be used to mount a share on the windows vm.
+export SMBTORTURE_REMOTE_DRIVE_LETTER="X:"
+
+# This is the name of the file which will be created on the windows vm
+# and used for samba server tests.
+export SMBTORTURE_TMP_FILENAME="smbtorture.tmp"
+
+# The path to the vmware image config file local to the vmware server.
+export VM_CFG_PATH="/var/lib/vmware/Virtual Machines/Win2k3-BuildFarm/Win2k3-BuildFarm.vmx"
+
+# In order to copy files and execute programs on the guest vm,
+# we need administrator-level credentials to log in with.
+export GUEST_ADMIN_USERNAME="administrator"
+export GUEST_ADMIN_PASSWORD="adminpass"
+
+# These parameters are optional. If not specified, the script tries to access
+# a local vmware server as the executing user.
+# logged-in user running the script are used.
+export HOST_SERVER_NAME="vmhost"
+export HOST_SERVER_PORT=902
+export HOST_USERNAME="vmuser"
+export HOST_PASSWORD="vmpass"
diff --git a/source4/selftest/win/vm_get_ip.pl b/source4/selftest/win/vm_get_ip.pl
new file mode 100644
index 0000000000..9657a34790
--- /dev/null
+++ b/source4/selftest/win/vm_get_ip.pl
@@ -0,0 +1,48 @@
+#!/usr/bin/perl -w
+
+# A perl script to connect to a VMware server and get the IP address of a VM.
+# Copyright Brad Henry <brad@samba.org> 2006
+# Released under the GNU GPL version 3 or later.
+
+use VMHost;
+
+sub check_error {
+ my $vm = VMHost;
+ my $custom_err_str = "";
+ ($vm, $custom_err_str) = @_;
+
+ my ($err_code, $err_str) = $vm->error;
+ if ($err_code != 0) {
+ undef $vm;
+ die $custom_err_str . "Returned $err_code: $err_str.\n";
+ }
+}
+
+# Read in parameters from environment.
+my $vm_cfg_path = $ENV{"$ARGV[0]"};
+my $host_server_name = $ENV{'HOST_SERVER_NAME'};
+my $host_server_port = $ENV{'HOST_SERVER_PORT'};
+if (!defined($host_server_port)) {
+ $host_server_port = 902;
+}
+
+my $host_username = $ENV{'HOST_USERNAME'};
+my $host_password = $ENV{'HOST_PASSWORD'};
+my $guest_admin_username = $ENV{'GUEST_ADMIN_USERNAME'};
+my $guest_admin_password = $ENV{'GUEST_ADMIN_PASSWORD'};
+
+my $vm = VMHost;
+
+$vm->host_connect($host_server_name, $host_server_port, $host_username,
+ $host_password, $vm_cfg_path, $guest_admin_username,
+ $guest_admin_password);
+check_error($vm, "Error in \$vm->host_connect().\n");
+
+my $guest_ip = $vm->get_guest_ip();
+check_error($vm, "Error in \$vm->get_guest_ip().\n");
+
+print $guest_ip;
+
+undef $vm;
+
+exit 0;
diff --git a/source4/selftest/win/vm_load_snapshot.pl b/source4/selftest/win/vm_load_snapshot.pl
new file mode 100644
index 0000000000..35e80badce
--- /dev/null
+++ b/source4/selftest/win/vm_load_snapshot.pl
@@ -0,0 +1,46 @@
+#!/usr/bin/perl -w
+
+# A perl script to connect to a VMware server and revert a VM snapshot.
+# Copyright Brad Henry <brad@samba.org> 2006
+# Released under the GNU GPL version 3 or later.
+
+use VMHost;
+
+sub check_error {
+my $vm = VMHost;
+ my $custom_err_str = "";
+ ($vm, $custom_err_str) = @_;
+
+ my ($err_code, $err_str) = $vm->error;
+ if ($err_code != 0) {
+ undef $vm;
+ die $custom_err_str . "Returned $err_code: $err_str.\n";
+ }
+}
+
+# Read in parameters from environment.
+my $vm_cfg_path = $ENV{'VM_CFG_PATH'};
+my $host_server_name = $ENV{'HOST_SERVER_NAME'};
+my $host_server_port = $ENV{'HOST_SERVER_PORT'};
+if (!defined($host_server_port)) {
+ $host_server_port = 902;
+}
+
+my $host_username = $ENV{'HOST_USERNAME'};
+my $host_password = $ENV{'HOST_PASSWORD'};
+my $guest_admin_username = $ENV{'GUEST_ADMIN_USERNAME'};
+my $guest_admin_password = $ENV{'GUEST_ADMIN_PASSWORD'};
+
+my $vm = VMHost;
+
+$vm->host_connect($host_server_name, $host_server_port, $host_username,
+ $host_password, $vm_cfg_path, $guest_admin_username,
+ $guest_admin_password);
+check_error($vm, "Error in \$vm->host_connect().\n");
+
+$vm->revert_snapshot();
+check_error($vm, "Error in \$vm->revert_snapshot().\n");
+
+undef $vm;
+
+exit 0;
diff --git a/source4/selftest/win/wintest_2k3_dc.sh b/source4/selftest/win/wintest_2k3_dc.sh
new file mode 100755
index 0000000000..ed964a274a
--- /dev/null
+++ b/source4/selftest/win/wintest_2k3_dc.sh
@@ -0,0 +1,114 @@
+#!/bin/sh
+
+if [ $# -lt 1 ]; then
+cat <<EOF
+Usage: wintest_2k3_dc.sh TESTGROUP
+EOF
+exit 1;
+fi
+
+TESTGROUP=$1
+
+if [ -z $WINTEST_DIR ]; then
+ echo "Environment variable WINTEST_DIR not found."
+ exit 1;
+fi
+
+# This variable is defined in the per-hosts .fns file for build-farm hosts that run windows tests.
+if [ -z $WINTESTCONF ]; then
+ echo "Please point environment variable WINTESTCONF to your test_win.conf file."
+ exit 1;
+fi
+
+. $WINTESTCONF
+. $WINTEST_DIR/wintest_functions.sh
+
+export WIN2K3_DC_REMOTE_HOST=`perl -I$WINTEST_DIR $WINTEST_DIR/vm_get_ip.pl WIN2K3_DC_VM_CFG_PATH`
+
+if [ -z $WIN2K3_DC_REMOTE_HOST ]; then
+ # Restore snapshot to ensure VM is in a known state, then exit.
+ restore_snapshot "Test failed to get the IP address of the windows 2003 DC." "$WIN2K3_DC_VM_CFG_PATH"
+ exit 1;
+fi
+
+server=$WIN2K3_DC_REMOTE_HOST
+username=$WIN2K3_DC_USERNAME
+password=$WIN2K3_DC_PASSWORD
+domain=$WIN2K3_DC_DOMAIN
+realm=$WIN2K3_DC_REALM
+
+OPTIONS="-U$username%$password -W $domain --option realm=$realm"
+
+all_errs=0
+
+on_error() {
+ name=$1
+
+ all_errs=`expr $all_errs + 1`
+ restore_snapshot "$name test failed." "$WIN2K3_DC_VM_CFG_PATH"
+}
+
+drsuapi_tests() {
+
+ name="RPC-DRSUAPI on ncacn_ip_tcp with seal"
+ bin/smbtorture \
+ ncacn_ip_tcp:$server[seal] $OPTIONS \
+ RPC-DRSUAPI || on_error "$name"
+
+ name="RPC-DRSUAPI on ncacn_ip_tcp with seal,bigendian"
+ bin/smbtorture \
+ ncacn_ip_tcp:$server[seal,bigendian] $OPTIONS \
+ RPC-DRSUAPI || on_error "$name"
+}
+
+spoolss_tests() {
+
+ name="RPC-SPOOLSS on ncacn_np"
+ bin/smbtorture \
+ ncacn_np:$server $OPTIONS \
+ RPC-SPOOLSS || on_error "$name"
+}
+
+ncacn_ip_tcp_tests() {
+ bindopt=$1
+ transport="ncacn_ip_tcp"
+ tests="RPC-SCHANNEL RPC-EPMAPPER RPC-SAMR RPC-NETLOGON RPC-LSA RPC-SAMLOGON RPC-SAMSYNC RPC-MULTIBIND"
+
+ for bindoptions in $bindopt; do
+ for t in $tests; do
+ name="$t on $transport with $bindoptions"
+ bin/smbtorture $TORTURE_OPTIONS \
+ $transport:$server[$bindoptions] \
+ $OPTIONS $t || on_error "$name"
+ done
+ done
+}
+
+ncacn_np_tests() {
+ bindopt=$1
+ transport="ncacn_np"
+ tests="RPC-SCHANNEL RPC-DSSETUP RPC-EPMAPPER RPC-SAMR RPC-WKSSVC RPC-SRVSVC RPC-EVENTLOG RPC-NETLOGON RPC-LSA RPC-SAMLOGON RPC-SAMSYNC RPC-MULTIBIND RPC-WINREG"
+
+ for bindoptions in $bindopt; do
+ for t in $tests; do
+ name="$t on $transport with $bindoptions"
+ bin/smbtorture $TORTURE_OPTIONS \
+ $transport:$server[$bindoptions] \
+ $OPTIONS $t || on_error "$name"
+ done
+ done
+}
+
+bindoptions="padcheck connect sign seal ntlm,sign ntml,seal $VALIDATE bigendian"
+
+case $TESTGROUP in
+ RPC-DRSUAPI) drsuapi_tests ;;
+ RPC-SPOOLSS) spoolss_tests ;;
+ ncacn_ip_tcp) ncacn_ip_tcp_tests $bindoptions ;;
+ ncacn_np) ncacn_np_tests $bindoptions ;;
+ *) echo "$TESTGROUP is not a known set of tests."
+ exit 1;
+ ;;
+esac
+
+exit $all_errs
diff --git a/source4/selftest/win/wintest_base.sh b/source4/selftest/win/wintest_base.sh
new file mode 100755
index 0000000000..b78da4f7c9
--- /dev/null
+++ b/source4/selftest/win/wintest_base.sh
@@ -0,0 +1,67 @@
+#!/bin/sh
+
+. selftest/test_functions.sh
+
+. selftest/win/wintest_functions.sh
+
+# This variable is defined in the per-hosts .fns file.
+. $WINTESTCONF
+
+if [ $# -lt 4 ]; then
+cat <<EOF
+Usage: test_net.sh SERVER USERNAME PASSWORD DOMAIN
+EOF
+exit 1;
+fi
+
+server="$1"
+username="$2"
+password="$3"
+domain="$4"
+shift 4
+
+export SMBTORTURE_REMOTE_HOST=$server
+
+base_tests="BASE-UNLINK BASE-ATTR BASE-DELETE BASE-TCON BASE-OPEN BASE-CHKPATH"
+
+all_errs=0
+err=0
+
+on_error() {
+ errstr=$1
+
+ all_errs=`expr $all_errs + 1`
+ restore_snapshot $errstr "$VM_CFG_PATH"
+}
+
+for t in $base_tests; do
+ test_name="$t / WINDOWS SERVER"
+ echo -e "\n$test_name SETUP PHASE"
+
+ setup_share_test
+
+ if [ $err_rtn -ne 0 ]; then
+ # If test setup fails, load VM snapshot and skip test.
+ on_error "\n$test_name setup failed, skipping test."
+ else
+ echo -e "\n$test_name setup completed successfully."
+
+ $SMBTORTURE_BIN_PATH -U $username%$password \
+ -W $domain //$server/$SMBTORTURE_REMOTE_SHARE_NAME \
+ $t || err=1
+ if [ $err -ne 0 ]; then
+ on_error "\n$test_name failed."
+ else
+ echo -e "\n$test_name CLEANUP PHASE"
+ remove_share_test
+ if [ $err_rtn -ne 0 ]; then
+ # If cleanup fails, restore VM snapshot.
+ on_error "\n$test_name removal failed."
+ else
+ echo -e "\n$test_name removal completed successfully."
+ fi
+ fi
+ fi
+done
+
+exit $all_errs
diff --git a/source4/selftest/win/wintest_client.exp b/source4/selftest/win/wintest_client.exp
new file mode 100644
index 0000000000..ccf5d06ea9
--- /dev/null
+++ b/source4/selftest/win/wintest_client.exp
@@ -0,0 +1,95 @@
+# An expect script to create a temporary file, map a share, copy the file to the share,
+# and compare the contents of the two files.
+# Copyright Brad Henry <brad@samba.org> 2006
+# Released under the GNU GPL version 3 or later.
+
+proc run_test { remote_prompt tmp_filename share_drive host_drive buildhost_ip buildhost_share username domain password } {
+
+ # Create the temp file on the windows host and connect to the samba share.
+ set host_tmpfile "$host_drive\\$tmp_filename"
+ set err_str [create_tmp_file $remote_prompt $host_tmpfile]
+ if { $err_str != "OK" } {
+ return $err_str
+ }
+
+ set buildhost_sharepoint "\\\\$buildhost_ip\\$buildhost_share"
+ set err_str [map_share $remote_prompt $share_drive $buildhost_sharepoint $username $domain $password]
+ if { $err_str != "OK" } {
+ return $err_str
+ }
+
+ # Copy the temp file to the share and compare its contents with the original.
+ set share_tmpfile "$share_drive\\$tmp_filename"
+ set xcopy_options ""
+ set err_str [xcopy_file $remote_prompt $host_tmpfile $share_tmpfile $xcopy_options]
+ if { $err_str != "OK" } {
+ return $err_str
+ }
+
+ set err_str [compare_files $remote_prompt $host_tmpfile $share_tmpfile]
+ if { $err_str != "OK" } {
+ return $err_str
+ }
+
+ # Remove files and unmap share.
+ set err_str [delete_file $remote_prompt $share_tmpfile]
+ if { $err_str != "OK" } {
+ return $err_str
+ }
+ set err_str [delete_file $remote_prompt $host_tmpfile]
+ if { $err_str != "OK" } {
+ return $err_str
+ }
+
+ set err_str [unmap_share $remote_prompt $share_drive]
+ if {$err_str != "OK" } {
+ return $err_str
+ }
+
+ return $err_str
+}
+
+# Read parameters.
+set remote_prompt $env(SMBTORTURE_REMOTE_PROMPT)
+set remote_host $env(SMBTORTURE_REMOTE_HOST)
+set username $env(SMBTORTURE_USERNAME)
+set password $env(SMBTORTURE_PASSWORD)
+set timeout $env(SMBTORTURE_EXPECT_TIMEOUT)
+
+set tmp_filename $env(SMBTORTURE_TMP_FILENAME)
+
+set share_drive $env(SMBTORTURE_REMOTE_DRIVE_LETTER)
+set host_drive "%HOMEDRIVE%"
+
+set buildhost_ip $env(SMBTORTURE_LOCAL_IP)
+set buildhost_share $env(SMBTORTURE_LOCAL_SHARE_NAME)
+set buildhost_username $env(SMBTORTURE_LOCAL_USERNAME)
+set buildhost_domain $env(SMBTORTURE_LOCAL_DOMAIN)
+set buildhost_password $env(SMBTORTURE_LOCAL_PASSWORD)
+
+set err_val [spawn $env(SHELL)]
+if {$err_val == 0} {
+ puts stderr "Expect failed while spawning a shell process."
+ exit $err_val
+}
+
+set err_str [telnet_login $remote_prompt $remote_host $username $password]
+if {$err_str != "OK"} {
+ puts stderr "\nFunction telnet_login failed during Samba server testing."
+ puts stderr "Error was: $err_str."
+ exit 1
+}
+
+set err_str [run_test $remote_prompt $tmp_filename $share_drive $host_drive $buildhost_ip $buildhost_share $buildhost_username $buildhost_domain $buildhost_password]
+if {$err_str != "OK"} {
+ puts stderr "\nFunction run_test failed during Samba server testing."
+ puts stderr "Error was: $err_str."
+
+ # Log off from the telnet server.
+ send "exit\r\n"
+ exit 1
+}
+
+# Log off from the telnet server.
+send "exit\r\n"
+exit 0
diff --git a/source4/selftest/win/wintest_client.sh b/source4/selftest/win/wintest_client.sh
new file mode 100755
index 0000000000..6b76ae36f7
--- /dev/null
+++ b/source4/selftest/win/wintest_client.sh
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+. selftest/test_functions.sh
+
+. selftest/win/wintest_functions.sh
+
+# This variable is defined in the per-hosts .fns file.
+. $WINTESTCONF
+
+export SMBTORTURE_REMOTE_HOST=$1
+
+test_name="WINDOWS CLIENT / SAMBA SERVER SHARE"
+
+cat $WINTEST_DIR/common.exp > $TMPDIR/client_test.exp
+cat $WINTEST_DIR/wintest_client.exp >> $TMPDIR/client_test.exp
+
+expect $TMPDIR/client_test.exp || all_errs=`expr $all_errs + 1`
+
+if [ $all_errs > 0 ]; then
+ # Restore snapshot to ensure VM is in a known state.
+ restore_snapshot "\n$test_name failed." "$VM_CFG_PATH"
+fi
+
+rm -f $TMPDIR/client_test.exp
+
+exit $all_errs
diff --git a/source4/selftest/win/wintest_functions.sh b/source4/selftest/win/wintest_functions.sh
new file mode 100755
index 0000000000..3c0a1dccac
--- /dev/null
+++ b/source4/selftest/win/wintest_functions.sh
@@ -0,0 +1,54 @@
+#!/bin/sh
+
+# Setup the windows environment.
+# This was the best way I could figure out including library files
+# for the moment.
+# I was finding that "cat common.exp wintest_setup.exp | expect -f -"
+# fails to run, but exits with 0 status something like 1% of the time.
+
+setup_share_test()
+{
+ echo -e "\nSetting up windows environment."
+ cat $WINTEST_DIR/common.exp > $TMPDIR/setup.exp
+ cat $WINTEST_DIR/wintest_setup.exp >> $TMPDIR/setup.exp
+ expect $TMPDIR/setup.exp
+ err_rtn=$?
+ rm -f $TMPDIR/setup.exp
+}
+
+# Clean up the windows environment after the test has run or failed.
+remove_share_test()
+{
+ echo -e "\nCleaning up windows environment."
+ cat $WINTEST_DIR/common.exp > $TMPDIR/remove.exp
+ cat $WINTEST_DIR/wintest_remove.exp >> $TMPDIR/remove.exp
+ expect $TMPDIR/remove.exp
+ err_rtn=$?
+ rm -f $TMPDIR/remove.exp
+}
+
+restore_snapshot()
+{
+ err_str=$1
+ VMX_PATH=$2
+
+ # Display the error that caused us to restore the snapshot.
+ echo -e $err_str
+
+ if [ -z $HOST_SERVER_NAME ]; then
+ # The vmware server is running locally.
+ vmrun revertToSnapshot "$VMX_PATH"
+ err_rtn=$?
+ else
+ vmrun -h $HOST_SERVER_NAME -P $HOST_SERVER_PORT \
+ -u $HOST_USERNAME -p $HOST_PASSWORD \
+ revertToSnapshot "$VMX_PATH"
+ err_rtn=$?
+ fi
+
+ if [ $err_rtn -eq 0 ]; then
+ echo "Snapshot restored."
+ else
+ echo "Error $err_rtn restoring snapshot!"
+ fi
+}
diff --git a/source4/selftest/win/wintest_net.sh b/source4/selftest/win/wintest_net.sh
new file mode 100755
index 0000000000..88cec1f3a7
--- /dev/null
+++ b/source4/selftest/win/wintest_net.sh
@@ -0,0 +1,62 @@
+#!/bin/sh
+
+. selftest/test_functions.sh
+
+. selftest/win/wintest_functions.sh
+
+# This variable is defined in the per-hosts .fns file.
+. $WINTESTCONF
+
+if [ $# -lt 4 ]; then
+cat <<EOF
+Usage: test_net.sh SERVER USERNAME PASSWORD DOMAIN
+EOF
+exit 1;
+fi
+
+server="$1"
+username="$2"
+password="$3"
+domain="$4"
+shift 4
+
+ncacn_np_tests="NET-API-LOOKUP NET-API-LOOKUPHOST NET-API-RPCCONN-BIND NET-API-RPCCONN-SRV NET-API-RPCCONN-DC NET-API-RPCCONN-DCINFO NET-API-LISTSHARES"
+#These tests fail on ncacn_np: NET-API-LOOKUPPDC NET-API-CREATEUSER NET-API-DELETEUSER
+
+ncalrpc_tests="NET-API-RPCCONN-SRV NET-API-RPCCONN-DC NET-API-RPCCONN-DCINFO NET-API-LISTSHARES"
+#These tests fail on ncalrpc: NET-API-CREATEUSER NET-API-DELETEUSER
+
+ncacn_ip_tcp_tests="NET-API-LOOKUP NET-API-LOOKUPHOST NET-API-RPCCONN-SRV NET-API-RPCCONN-DC NET-API-RPCCONN-DCINFO NET-API-LISTSHARES"
+#These tests fail on ncacn_ip_tcp: NET-API-LOOKUPPDC NET-API-CREATEUSER NET-API-DELETEUSER
+
+bind_options="seal,padcheck bigendian"
+
+test_type="ncalrpc ncacn_np ncacn_ip_tcp"
+
+all_errs=0
+
+on_error() {
+ errstr=$1
+
+ all_errs=`expr $all_errs + 1`
+ restore_snapshot "$errstr" "$VM_CFG_PATH"
+}
+
+for o in $bind_options; do
+ for transport in $test_type; do
+ case $transport in
+ ncalrpc) net_test=$ncalrpc_tests ;;
+ ncacn_np) net_test=$ncacn_np_tests ;;
+ ncacn_ip_tcp) net_test=$ncacn_ip_tcp_tests ;;
+ esac
+
+ for t in $net_test; do
+ test_name="$t on $transport with $o"
+ $SMBTORTURE_BIN_PATH -U $username%$password \
+ -W $domain $transport:$server[$o] \
+ $t || on_error "\n$test_name failed."
+ done
+ done
+done
+
+exit $all_errs
diff --git a/source4/selftest/win/wintest_raw.sh b/source4/selftest/win/wintest_raw.sh
new file mode 100755
index 0000000000..541e19829a
--- /dev/null
+++ b/source4/selftest/win/wintest_raw.sh
@@ -0,0 +1,68 @@
+#!/bin/sh
+
+. selftest/test_functions.sh
+
+. selftest/win/wintest_functions.sh
+
+# This variable is defined in the per-hosts .fns file.
+. $WINTESTCONF
+
+if [ $# -lt 4 ]; then
+cat <<EOF
+Usage: test_net.sh SERVER USERNAME PASSWORD DOMAIN
+EOF
+exit 1;
+fi
+
+server="$1"
+username="$2"
+password="$3"
+domain="$4"
+shift 4
+
+export SMBTORTURE_REMOTE_HOST=$server
+
+raw_tests="RAW-QFILEINFO RAW-SFILEINFO RAW-MKDIR RAW-SEEK RAW-OPEN RAW-WRITE RAW-UNLINK RAW-READ RAW-CLOSE RAW-IOCTL RAW-RENAME RAW-EAS RAW-STREAMS"
+# This test fails: RAW-QFSINFO
+
+all_errs=0
+err=0
+
+on_error() {
+ errstr=$1
+ all_errs=`expr $all_errs + 1`
+
+ restore_snapshot "$errstr" "$VM_CFG_PATH"
+}
+
+for t in $raw_tests; do
+ test_name="$t / WINDOWS SERVER"
+ echo -e "\n$test_name SETUP PHASE"
+
+ setup_share_test
+
+ if [ $err_rtn -ne 0 ]; then
+ # If test setup fails, load VM snapshot and skip test.
+ on_error "\n$test_name setup failed, skipping test."
+ else
+ echo -e "\n$test_name setup completed successfully."
+
+ $SMBTORTURE_BIN_PATH -U $username%$password -W $domain \
+ //$server/$SMBTORTURE_REMOTE_SHARE_NAME \
+ $t || err=1
+ if [ $err -ne 0 ]; then
+ on_error "\n$test_name failed."
+ else
+ echo -e "\n$test_name CLEANUP PHASE"
+ remove_share_test
+ if [ $err_rtn -ne 0 ]; then
+ # If cleanup fails, restore VM snapshot.
+ on_error "\n$test_name removal failed."
+ else
+ echo -e "\n$test_name removal completed successfully."
+ fi
+ fi
+ fi
+done
+
+exit $all_errs
diff --git a/source4/selftest/win/wintest_remove.exp b/source4/selftest/win/wintest_remove.exp
new file mode 100644
index 0000000000..36dc4a7d24
--- /dev/null
+++ b/source4/selftest/win/wintest_remove.exp
@@ -0,0 +1,71 @@
+# An expect script to remove a directory and share which was
+# previously setup for an smbtorture test.
+# Copyright Brad Henry <brad@samba.org> 2006
+# Released under the GNU GPL version 3 or later.
+
+proc remove_test { remote_prompt sharepath sharename hosts_file_path \
+ backup_hosts_filename } {
+
+ set err_str [delete_share $remote_prompt $sharename]
+ if { $err_str != "OK" } {
+ puts stderr "Error in function delete_share: $err_str."
+ puts stderr "Function remove_test will continue."
+ }
+
+ set err_str [delete_directory $remote_prompt $sharepath]
+ if { $err_str != "OK" } {
+ puts stderr "Error in function delete_directory: $err_str."
+ puts stderr "Function remove_test will continue."
+ }
+
+ # Overwrite the current hosts file with the backup we made during setup.
+ set err_str [delete_hosts_entry $remote_prompt $hosts_file_path \
+ $backup_hosts_filename]
+ if { $err_str != "OK" } {
+ puts stderr "Error in function delete_hosts_entry: $err_str."
+ puts stderr "Function remove_test will continue."
+ }
+ return $err_str
+}
+
+# read parameters
+set remote_host $env(SMBTORTURE_REMOTE_HOST)
+set remote_prompt $env(SMBTORTURE_REMOTE_PROMPT)
+
+set username $env(SMBTORTURE_USERNAME)
+set password $env(SMBTORTURE_PASSWORD)
+
+set timeout $env(SMBTORTURE_EXPECT_TIMEOUT)
+
+set sharepath $env(SMBTORTURE_REMOTE_SHARE_PATH)
+set sharename $env(SMBTORTURE_REMOTE_SHARE_NAME)
+
+set backup_hosts_filename $env(REMOTE_BACKUP_HOSTS_FILENAME)
+set hosts_file_path $env(REMOTE_HOSTS_FILE_PATH)
+
+set err_val [spawn $env(SHELL)]
+if {$err_val == 0} {
+ puts stderr "Expect failed while spawning a shell process."
+ exit $err_val
+}
+
+set err_str [telnet_login $remote_prompt $remote_host $username $password]
+if {$err_str != "OK"} {
+ puts stderr "\nFunction telnet_login failed during cleanup."
+ puts stderr "Error was: $err_str."
+ exit 1
+}
+
+set err_str [remove_test $remote_prompt $sharepath $sharename \
+ $hosts_file_path $backup_hosts_filename]
+if {$err_str != "OK"} {
+ puts stderr "\nFunction remove_test failed."
+ puts stderr "Error was: $err_str."
+ # Log off from the telnet server.
+ send "exit\r\n"
+ exit 1
+}
+
+# Log off from the telnet server.
+send "exit\r\n"
+exit 0
diff --git a/source4/selftest/win/wintest_rpc.sh b/source4/selftest/win/wintest_rpc.sh
new file mode 100755
index 0000000000..d0a0783c2e
--- /dev/null
+++ b/source4/selftest/win/wintest_rpc.sh
@@ -0,0 +1,66 @@
+#!/bin/sh
+
+. selftest/test_functions.sh
+
+. selftest/win/wintest_functions.sh
+
+# This variable is defined in the per-hosts .fns file.
+. $WINTESTCONF
+
+if [ $# -lt 4 ]; then
+cat <<EOF
+Usage: test_rpc.sh SERVER USERNAME PASSWORD DOMAIN
+EOF
+exit 1;
+fi
+
+server="$1"
+username="$2"
+password="$3"
+domain="$4"
+shift 4
+
+ncacn_np_tests="RPC-SRVSVC RPC-UNIXINFO RPC-ECHO RPC-DSSETUP RPC-ALTERCONTEXT RPC-MULTIBIND"
+# These tests fail on ncacn_np: RPC-SPOOLSS RPC-SCHANNEL RPC-JOIN RPC-LSA
+# RPC-NETLOGON
+
+ncalrpc_tests="RPC-UNIXINFO RPC-ECHO"
+# These tests fail on ncalrpc: RPC-SCHANNEL RPC-JOIN RPC-LSA RPC-DSSETUP
+# RPC-ALTERCONTEXT RPC-MULTIBIND RPC-NETLOGON
+
+ncacn_ip_tcp_tests="RPC-UNIXINFO RPC-ECHO"
+# These tests fail on ncacn_ip_tcp: RPC-SCHANNEL RPC-JOIN RPC-LSA RPC-DSSETUP
+# RPC-ALTERCONTEXT RPC-MULTIBIND RPC-NETLOGON
+
+bind_options="seal,padcheck bigendian"
+
+test_type="ncalrpc ncacn_np ncacn_ip_tcp"
+
+all_errs=0
+
+on_error() {
+ errstr=$1
+ all_errs=`expr $all_errs + 1`
+
+ restore_snapshot "$errstr" "$VM_CFG_PATH"
+}
+
+for o in $bind_options; do
+ for transport in $test_type; do
+ case $transport in
+ ncalrpc) rpc_test=$ncalrpc_tests ;;
+ ncacn_np) rpc_test=$ncacn_np_tests ;;
+ ncacn_ip_tcp) rpc_test=$ncacn_ip_tcp_tests ;;
+ esac
+
+ for t in $rpc_test; do
+ test_name="$t on $transport with $o"
+
+ $SMBTORTURE_BIN_PATH -U $username%$password \
+ -W $domain $transport:$server[$o] \
+ $t || on_error "\n$test_name failed."
+ done
+ done
+done
+
+exit $all_errs
diff --git a/source4/selftest/win/wintest_setup.exp b/source4/selftest/win/wintest_setup.exp
new file mode 100644
index 0000000000..f55732ac00
--- /dev/null
+++ b/source4/selftest/win/wintest_setup.exp
@@ -0,0 +1,104 @@
+# An expect script to setup a directory and share for an smbtorture test.
+# Copyright Brad Henry <brad@samba.org> 2006
+# Released under the GNU GPL version 3 or later.
+
+proc setup_test { remote_prompt sharepath sharename username local_hostname \
+ local_ip hosts_file_path backup_hosts_filename } {
+
+ # If creating the directory fails, remove, then
+ # re-create the directory.
+ set err_str [create_directory $remote_prompt $sharepath]
+ if { $err_str != "OK" } {
+ if { $err_str != "Directory already exists" } {
+ puts stderr "\nUnexpected error occured in setup_test.\n"
+ puts stderr "Function create_directory returned $err_str."
+ } else {
+ puts stdout "\nDirectory $sharepath exists."
+ }
+ puts stdout "Re-creating directory $sharepath."
+
+ set err_str [delete_directory $remote_prompt $sharepath]
+ if { $err_str != "OK" } {
+ return $err_str
+ }
+ set err_str [create_directory $remote_prompt $sharepath]
+ if { $err_str != "OK" } {
+ return $err_str
+ }
+ }
+
+ # If creating the share fails, remove, then
+ # re-create the share.
+ set err_str [create_share $remote_prompt $username $sharepath \
+ $sharename]
+ if { $err_str != "OK" } {
+ if { $err_str != "The name has already been shared" } {
+ puts stderr "\nUnexpected error occured in setup_test."
+ puts stderr "Function create_share returned $err_str."
+ } else {
+ puts stdout "\nShare $sharename exists."
+ }
+ puts stdout "Re-creating share $sharename."
+
+ set err_str [delete_share $remote_prompt $sharename]
+ if { $err_str != "OK" } {
+ return $err_str
+ }
+ set err_str [create_share $remote_prompt $username $sharepath \
+ $sharename]
+ if { $err_str != "OK" } {
+ return $err_str
+ }
+ }
+
+ # Add a hosts file entry on the windows machine for the smbtorture host.
+ set err_str [create_hosts_entry $remote_prompt $hosts_file_path \
+ $local_hostname $local_ip $backup_hosts_filename]
+ return $err_str
+}
+
+# Read parameters.
+set remote_host $env(SMBTORTURE_REMOTE_HOST)
+set remote_prompt $env(SMBTORTURE_REMOTE_PROMPT)
+
+set username $env(SMBTORTURE_USERNAME)
+set password $env(SMBTORTURE_PASSWORD)
+
+set timeout $env(SMBTORTURE_EXPECT_TIMEOUT)
+
+set sharepath $env(SMBTORTURE_REMOTE_SHARE_PATH)
+set sharename $env(SMBTORTURE_REMOTE_SHARE_NAME)
+
+set local_hostname $env(SMBTORTURE_LOCAL_HOSTNAME)
+set local_ip $env(SMBTORTURE_LOCAL_IP)
+
+set backup_hosts_filename $env(REMOTE_BACKUP_HOSTS_FILENAME)
+set hosts_file_path $env(REMOTE_HOSTS_FILE_PATH)
+
+set err_val [spawn $env(SHELL)]
+if {$err_val == 0} {
+ puts stderr "Expect failed while spawning a shell process."
+ exit $err_val
+}
+
+set err_str [telnet_login $remote_prompt $remote_host $username $password]
+if {$err_str != "OK"} {
+ puts stderr "\nFunction telnet_login failed during setup."
+ puts stderr "Error was: $err_str."
+ exit 1
+}
+
+set err_str [setup_test $remote_prompt $sharepath $sharename $username \
+ $local_hostname $local_ip $hosts_file_path \
+ $backup_hosts_filename]
+if {$err_str != "OK"} {
+ puts stderr "\nFunction setup_test failed during setup."
+ puts stderr "Error was: $err_str."
+ # Log off from the telnet server.
+ send "exit\r\n"
+ exit 1
+}
+
+# Log off from the telnet server.
+send "exit\r\n"
+exit 0
diff --git a/source4/setup/DB_CONFIG b/source4/setup/DB_CONFIG
new file mode 100644
index 0000000000..74bb09d800
--- /dev/null
+++ b/source4/setup/DB_CONFIG
@@ -0,0 +1,6 @@
+set_cachesize 0 524288 0
+set_lg_regionmax 104857
+set_lg_max 1048576
+set_lg_bsize 209715
+set_lg_dir ${LDAPDBDIR}/bdb-logs
+set_tmp_dir ${LDAPDBDIR}/tmp
diff --git a/source4/setup/ad-schema/MS-AD_Schema_Attributes_v20080618.txt b/source4/setup/ad-schema/MS-AD_Schema_Attributes_v20080618.txt
new file mode 100644
index 0000000000..ab4f3999bd
--- /dev/null
+++ b/source4/setup/ad-schema/MS-AD_Schema_Attributes_v20080618.txt
@@ -0,0 +1,15681 @@
+# © 2008 Microsoft Corporation. All rights reserved
+#
+# Intellectual Property Rights Notice for Protocol Documentation
+#
+# Copyrights.
+# This protocol documentation is covered by Microsoft
+# copyrights. Regardless of any other terms that are contained in the
+# terms of use for the Microsoft website that hosts this documentation,
+# you may make copies of it in order to develop implementations of the
+# protocols, and may distribute portions of it in your implementations
+# of the protocols or your documentation as necessary to properly
+# document the implementation. You may also distribute in your
+# implementation, with or without modification, any schema, IDL's, or
+# code samples that are included in the documentation. This permission
+# also applies to any documents that are referenced in the protocol
+# documentation.
+#
+# No Trade Secrets.
+# Microsoft does not claim any trade secret rights in this documentation.
+#
+# Patents.
+# Microsoft has patents that may cover your implementations of the
+# protocols. Neither this notice nor Microsoft's delivery of the
+# documentation grants any licenses under those or any other Microsoft
+# patents. However, the protocols may be covered by MicrosoftÂ’s Open
+# Specification Promise (available here:
+# http://www.microsoft.com/interop/osp). If you would prefer a written
+# license, or if the protocols are not covered by the OSP, patent
+# licenses are available by contacting protocol@microsoft.com.
+#
+# Trademarks.
+# The names of companies and products contained in this documentation
+# may be covered by trademarks or similar intellectual property
+# rights. This notice does not grant any licenses under those
+# rights.Reservation of Rights. All other rights are reserved, and this
+# notice does not grant any rights other than specifically described
+# above, whether by implication, estoppel, or otherwise.
+#
+# Tools.
+# This protocol documentation is intended for use in conjunction with
+# publicly available standard specifications and network programming
+# art, and assumes that the reader either is familiar with the
+# aforementioned material or has immediate access to it. A protocol
+# specification does not require the use of Microsoft programming tools
+# or programming environments in order for you to develop an
+# implementation. If you have access to Microsoft programming tools and
+# environments you are free to take advantage of them.
+
+cn: Account-Expires
+ldapDisplayName: accountExpires
+attributeId: 1.2.840.113556.1.4.159
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: bf967915-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: fCOPY
+attributeSecurityGuid: 4c164200-20c0-11d0-a768-00aa006e0529
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ACS-Enable-ACS-Service
+ldapDisplayName: aCSEnableACSService
+attributeId: 1.2.840.113556.1.4.770
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: 7f561287-5301-11d1-a9c5-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Catalogs
+ldapDisplayName: catalogs
+attributeId: 1.2.840.113556.1.4.675
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 7bfdcb81-4807-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Categories
+ldapDisplayName: categories
+attributeId: 1.2.840.113556.1.4.672
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 7bfdcb7e-4807-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 36
+rangeUpper: 36
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Category-Id
+ldapDisplayName: categoryId
+attributeId: 1.2.840.113556.1.4.322
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 7d6c0e94-7e20-11d0-afd6-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 16
+rangeUpper: 16
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: CA-Usages
+ldapDisplayName: cAUsages
+attributeId: 1.2.840.113556.1.4.690
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 963d2738-48be-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: CA-WEB-URL
+ldapDisplayName: cAWEBURL
+attributeId: 1.2.840.113556.1.4.688
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 963d2736-48be-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Certificate-Authority-Object
+ldapDisplayName: certificateAuthorityObject
+attributeId: 1.2.840.113556.1.4.684
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: TRUE
+schemaIdGuid: 963d2732-48be-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Certificate-Revocation-List
+ldapDisplayName: certificateRevocationList
+attributeId: 2.5.4.39
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 1677579f-47f3-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 10485760
+mapiID: 32790
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Certificate-Templates
+ldapDisplayName: certificateTemplates
+attributeId: 1.2.840.113556.1.4.823
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 2a39c5b1-8960-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Class-Display-Name
+ldapDisplayName: classDisplayName
+attributeId: 1.2.840.113556.1.4.610
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 548e1c22-dea6-11d0-b010-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Common-Name
+ldapDisplayName: cn
+attributeId: 2.5.4.3
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: bf96793f-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: fATTINDEX
+rangeLower: 1
+rangeUpper: 64
+attributeSecurityGuid: e48d0154-bcf8-11d1-8702-00c04fb96050
+mapiID: 14863
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT |FLAG_ATTR_REQ_PARTIAL_SET_MEMBER
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ACS-Enable-RSVP-Accounting
+ldapDisplayName: aCSEnableRSVPAccounting
+attributeId: 1.2.840.113556.1.4.899
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: f072230e-aef5-11d1-bdcf-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Text-Country
+ldapDisplayName: co
+attributeId: 1.2.840.113556.1.2.131
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: f0f8ffa7-1191-11d0-a060-00aa006c33ed
+systemOnly: FALSE
+searchFlags: fCOPY
+rangeLower: 1
+rangeUpper: 128
+attributeSecurityGuid: e48d0154-bcf8-11d1-8702-00c04fb96050
+mapiID: 14886
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Code-Page
+ldapDisplayName: codePage
+attributeId: 1.2.840.113556.1.4.16
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: bf967938-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: fCOPY
+rangeLower: 0
+rangeUpper: 65535
+attributeSecurityGuid: 59ba2f42-79a2-11d0-9020-00c04fc2d3cf
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: COM-ClassID
+ldapDisplayName: cOMClassID
+attributeId: 1.2.840.113556.1.4.19
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: bf96793b-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: fATTINDEX
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: COM-CLSID
+ldapDisplayName: cOMCLSID
+attributeId: 1.2.840.113556.1.4.249
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 281416d9-1968-11d0-a28f-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 36
+rangeUpper: 36
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: COM-InterfaceID
+ldapDisplayName: cOMInterfaceID
+attributeId: 1.2.840.113556.1.4.20
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: bf96793c-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 36
+rangeUpper: 36
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: User-Comment
+ldapDisplayName: comment
+attributeId: 1.2.840.113556.1.4.156
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: bf967a6a-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+attributeSecurityGuid: 59ba2f42-79a2-11d0-9020-00c04fc2d3cf
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: COM-Other-Prog-Id
+ldapDisplayName: cOMOtherProgId
+attributeId: 1.2.840.113556.1.4.253
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 281416dd-1968-11d0-a28f-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Company
+ldapDisplayName: company
+attributeId: 1.2.840.113556.1.2.146
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: f0f8ff88-1191-11d0-a060-00aa006c33ed
+systemOnly: FALSE
+searchFlags: fCOPY
+rangeLower: 1
+rangeUpper: 64
+attributeSecurityGuid: e48d0154-bcf8-11d1-8702-00c04fb96050
+mapiID: 14870
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: COM-ProgID
+ldapDisplayName: cOMProgID
+attributeId: 1.2.840.113556.1.4.21
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: bf96793d-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: COM-Treat-As-Class-Id
+ldapDisplayName: cOMTreatAsClassId
+attributeId: 1.2.840.113556.1.4.251
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 281416db-1968-11d0-a28f-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 36
+rangeUpper: 36
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ACS-Enable-RSVP-Message-Logging
+ldapDisplayName: aCSEnableRSVPMessageLogging
+attributeId: 1.2.840.113556.1.4.768
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: 7f561285-5301-11d1-a9c5-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: COM-Typelib-Id
+ldapDisplayName: cOMTypelibId
+attributeId: 1.2.840.113556.1.4.254
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 281416de-1968-11d0-a28f-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 36
+rangeUpper: 36
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: COM-Unique-LIBID
+ldapDisplayName: cOMUniqueLIBID
+attributeId: 1.2.840.113556.1.4.250
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 281416da-1968-11d0-a28f-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 36
+rangeUpper: 36
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Content-Indexing-Allowed
+ldapDisplayName: contentIndexingAllowed
+attributeId: 1.2.840.113556.1.4.24
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: bf967943-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Context-Menu
+ldapDisplayName: contextMenu
+attributeId: 1.2.840.113556.1.4.499
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 4d8601ee-ac85-11d0-afe3-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Control-Access-Rights
+ldapDisplayName: controlAccessRights
+attributeId: 1.2.840.113556.1.4.200
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: FALSE
+schemaIdGuid: 6da8a4fc-0e52-11d0-a286-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 16
+rangeUpper: 16
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Cost
+ldapDisplayName: cost
+attributeId: 1.2.840.113556.1.2.135
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: bf967944-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+mapiID: 32872
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Country-Code
+ldapDisplayName: countryCode
+attributeId: 1.2.840.113556.1.4.25
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 5fd42471-1262-11d0-a060-00aa006c33ed
+systemOnly: FALSE
+searchFlags: fCOPY
+rangeLower: 0
+rangeUpper: 65535
+attributeSecurityGuid: 59ba2f42-79a2-11d0-9020-00c04fc2d3cf
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Create-Dialog
+ldapDisplayName: createDialog
+attributeId: 1.2.840.113556.1.4.810
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 2b09958a-8931-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Create-Time-Stamp
+ldapDisplayName: createTimeStamp
+attributeId: 2.5.18.1
+attributeSyntax: 2.5.5.11
+omSyntax: 24
+isSingleValued: TRUE
+schemaIdGuid: 2df90d73-009f-11d2-aa4c-00c04fd7d83a
+systemOnly: TRUE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_IS_CONSTRUCTED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Create-Wizard-Ext
+ldapDisplayName: createWizardExt
+attributeId: 1.2.840.113556.1.4.812
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 2b09958b-8931-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ACS-Event-Log-Level
+ldapDisplayName: aCSEventLogLevel
+attributeId: 1.2.840.113556.1.4.769
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 7f561286-5301-11d1-a9c5-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Creation-Time
+ldapDisplayName: creationTime
+attributeId: 1.2.840.113556.1.4.26
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: bf967946-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Creation-Wizard
+ldapDisplayName: creationWizard
+attributeId: 1.2.840.113556.1.4.498
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 4d8601ed-ac85-11d0-afe3-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Creator
+ldapDisplayName: creator
+attributeId: 1.2.840.113556.1.4.679
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 7bfdcb85-4807-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: CRL-Object
+ldapDisplayName: cRLObject
+attributeId: 1.2.840.113556.1.4.689
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: TRUE
+schemaIdGuid: 963d2737-48be-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: CRL-Partitioned-Revocation-List
+ldapDisplayName: cRLPartitionedRevocationList
+attributeId: 1.2.840.113556.1.4.683
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 963d2731-48be-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 10485760
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Cross-Certificate-Pair
+ldapDisplayName: crossCertificatePair
+attributeId: 2.5.4.40
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: FALSE
+schemaIdGuid: 167757b2-47f3-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 32768
+mapiID: 32805
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Current-Location
+ldapDisplayName: currentLocation
+attributeId: 1.2.840.113556.1.4.335
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 1f0075fc-7e40-11d0-afd6-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 32
+rangeUpper: 32
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Current-Parent-CA
+ldapDisplayName: currentParentCA
+attributeId: 1.2.840.113556.1.4.696
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: 963d273f-48be-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Current-Value
+ldapDisplayName: currentValue
+attributeId: 1.2.840.113556.1.4.27
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: bf967947-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Curr-Machine-Id
+ldapDisplayName: currMachineId
+attributeId: 1.2.840.113556.1.4.337
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 1f0075fe-7e40-11d0-afd6-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 16
+rangeUpper: 16
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ACS-Identity-Name
+ldapDisplayName: aCSIdentityName
+attributeId: 1.2.840.113556.1.4.784
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: dab029b6-ddf7-11d1-90a5-00c04fd91ab1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: DBCS-Pwd
+ldapDisplayName: dBCSPwd
+attributeId: 1.2.840.113556.1.4.55
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: bf96799c-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Domain-Component
+ldapDisplayName: dc
+attributeId: 0.9.2342.19200300.100.1.25
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 19195a55-6da0-11d0-afd3-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 255
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT |FLAG_ATTR_REQ_PARTIAL_SET_MEMBER
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Default-Class-Store
+ldapDisplayName: defaultClassStore
+attributeId: 1.2.840.113556.1.4.213
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: bf967948-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Default-Group
+ldapDisplayName: defaultGroup
+attributeId: 1.2.840.113556.1.4.480
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: TRUE
+schemaIdGuid: 720bc4e2-a54a-11d0-afdf-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Default-Hiding-Value
+ldapDisplayName: defaultHidingValue
+attributeId: 1.2.840.113556.1.4.518
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: b7b13116-b82e-11d0-afee-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Default-Local-Policy-Object
+ldapDisplayName: defaultLocalPolicyObject
+attributeId: 1.2.840.113556.1.4.57
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: TRUE
+schemaIdGuid: bf96799f-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Default-Object-Category
+ldapDisplayName: defaultObjectCategory
+attributeId: 1.2.840.113556.1.4.783
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: TRUE
+schemaIdGuid: 26d97367-6070-11d1-a9c6-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Default-Priority
+ldapDisplayName: defaultPriority
+attributeId: 1.2.840.113556.1.4.232
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 281416c8-1968-11d0-a28f-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Default-Security-Descriptor
+ldapDisplayName: defaultSecurityDescriptor
+attributeId: 1.2.840.113556.1.4.224
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 807a6d30-1669-11d0-a064-00aa006c33ed
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 32767
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Delta-Revocation-List
+ldapDisplayName: deltaRevocationList
+attributeId: 2.5.4.53
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: FALSE
+schemaIdGuid: 167757b5-47f3-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 10485760
+mapiID: 35910
+
+cn: ACS-Max-Aggregate-Peak-Rate-Per-User
+ldapDisplayName: aCSMaxAggregatePeakRatePerUser
+attributeId: 1.2.840.113556.1.4.897
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: f072230c-aef5-11d1-bdcf-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Department
+ldapDisplayName: department
+attributeId: 1.2.840.113556.1.2.141
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: bf96794f-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: fCOPY
+rangeLower: 1
+rangeUpper: 64
+attributeSecurityGuid: e48d0154-bcf8-11d1-8702-00c04fb96050
+mapiID: 14872
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: departmentNumber
+ldapDisplayName: departmentNumber
+attributeId: 2.16.840.1.113730.3.1.2
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: be9ef6ee-cbc7-4f22-b27b-96967e7ee585
+systemOnly: FALSE
+searchFlags: 0
+showInAdvancedViewOnly: FALSE
+
+cn: Description
+ldapDisplayName: description
+attributeId: 2.5.4.13
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: bf967950-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 1024
+attributeSecurityGuid: e48d0154-bcf8-11d1-8702-00c04fb96050
+mapiID: 32879
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Desktop-Profile
+ldapDisplayName: desktopProfile
+attributeId: 1.2.840.113556.1.4.346
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: eea65906-8ac6-11d0-afda-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Destination-Indicator
+ldapDisplayName: destinationIndicator
+attributeId: 2.5.4.27
+attributeSyntax: 2.5.5.5
+omSyntax: 19
+isSingleValued: FALSE
+schemaIdGuid: bf967951-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 128
+mapiID: 32880
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: dhcp-Classes
+ldapDisplayName: dhcpClasses
+attributeId: 1.2.840.113556.1.4.715
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: FALSE
+schemaIdGuid: 963d2750-48be-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: dhcp-Flags
+ldapDisplayName: dhcpFlags
+attributeId: 1.2.840.113556.1.4.700
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: 963d2741-48be-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: dhcp-Identification
+ldapDisplayName: dhcpIdentification
+attributeId: 1.2.840.113556.1.4.701
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 963d2742-48be-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: dhcp-Mask
+ldapDisplayName: dhcpMask
+attributeId: 1.2.840.113556.1.4.706
+attributeSyntax: 2.5.5.5
+omSyntax: 19
+isSingleValued: FALSE
+schemaIdGuid: 963d2747-48be-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: dhcp-MaxKey
+ldapDisplayName: dhcpMaxKey
+attributeId: 1.2.840.113556.1.4.719
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: 963d2754-48be-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ACS-Max-Duration-Per-Flow
+ldapDisplayName: aCSMaxDurationPerFlow
+attributeId: 1.2.840.113556.1.4.761
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 7f56127e-5301-11d1-a9c5-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: dhcp-Obj-Description
+ldapDisplayName: dhcpObjDescription
+attributeId: 1.2.840.113556.1.4.703
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 963d2744-48be-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: dhcp-Obj-Name
+ldapDisplayName: dhcpObjName
+attributeId: 1.2.840.113556.1.4.702
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 963d2743-48be-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: dhcp-Options
+ldapDisplayName: dhcpOptions
+attributeId: 1.2.840.113556.1.4.714
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: FALSE
+schemaIdGuid: 963d274f-48be-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: dhcp-Properties
+ldapDisplayName: dhcpProperties
+attributeId: 1.2.840.113556.1.4.718
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: FALSE
+schemaIdGuid: 963d2753-48be-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: dhcp-Ranges
+ldapDisplayName: dhcpRanges
+attributeId: 1.2.840.113556.1.4.707
+attributeSyntax: 2.5.5.5
+omSyntax: 19
+isSingleValued: FALSE
+schemaIdGuid: 963d2748-48be-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: dhcp-Reservations
+ldapDisplayName: dhcpReservations
+attributeId: 1.2.840.113556.1.4.709
+attributeSyntax: 2.5.5.5
+omSyntax: 19
+isSingleValued: FALSE
+schemaIdGuid: 963d274a-48be-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: dhcp-Servers
+ldapDisplayName: dhcpServers
+attributeId: 1.2.840.113556.1.4.704
+attributeSyntax: 2.5.5.5
+omSyntax: 19
+isSingleValued: FALSE
+schemaIdGuid: 963d2745-48be-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+extendedCharsAllowed: TRUE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: dhcp-Sites
+ldapDisplayName: dhcpSites
+attributeId: 1.2.840.113556.1.4.708
+attributeSyntax: 2.5.5.5
+omSyntax: 19
+isSingleValued: FALSE
+schemaIdGuid: 963d2749-48be-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: dhcp-State
+ldapDisplayName: dhcpState
+attributeId: 1.2.840.113556.1.4.717
+attributeSyntax: 2.5.5.5
+omSyntax: 19
+isSingleValued: FALSE
+schemaIdGuid: 963d2752-48be-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: dhcp-Subnets
+ldapDisplayName: dhcpSubnets
+attributeId: 1.2.840.113556.1.4.705
+attributeSyntax: 2.5.5.5
+omSyntax: 19
+isSingleValued: FALSE
+schemaIdGuid: 963d2746-48be-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ACS-Maximum-SDU-Size
+ldapDisplayName: aCSMaximumSDUSize
+attributeId: 1.2.840.113556.1.4.1314
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: 87a2d8f9-3b90-11d2-90cc-00c04fd91ab1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: dhcp-Type
+ldapDisplayName: dhcpType
+attributeId: 1.2.840.113556.1.4.699
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 963d273b-48be-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: fATTINDEX
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: dhcp-Unique-Key
+ldapDisplayName: dhcpUniqueKey
+attributeId: 1.2.840.113556.1.4.698
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: 963d273a-48be-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: dhcp-Update-Time
+ldapDisplayName: dhcpUpdateTime
+attributeId: 1.2.840.113556.1.4.720
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: 963d2755-48be-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Reports
+ldapDisplayName: directReports
+attributeId: 1.2.840.113556.1.2.436
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: bf967a1c-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+searchFlags: 0
+attributeSecurityGuid: e48d0154-bcf8-11d1-8702-00c04fb96050
+linkID: 43
+mapiID: 32782
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_NOT_REPLICATED
+
+cn: Display-Name
+ldapDisplayName: displayName
+attributeId: 1.2.840.113556.1.2.13
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: bf967953-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: fANR | fATTINDEX
+rangeLower: 0
+rangeUpper: 256
+attributeSecurityGuid: 59ba2f42-79a2-11d0-9020-00c04fc2d3cf
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Display-Name-Printable
+ldapDisplayName: displayNamePrintable
+attributeId: 1.2.840.113556.1.2.353
+attributeSyntax: 2.5.5.5
+omSyntax: 19
+isSingleValued: TRUE
+schemaIdGuid: bf967954-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 256
+attributeSecurityGuid: e48d0154-bcf8-11d1-8702-00c04fb96050
+mapiID: 14847
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Obj-Dist-Name
+ldapDisplayName: distinguishedName
+attributeId: 2.5.4.49
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: TRUE
+schemaIdGuid: bf9679e4-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+searchFlags:fPRESERVEONDELETE
+attributeSecurityGuid: e48d0154-bcf8-11d1-8702-00c04fb96050
+mapiID: 32828
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT |FLAG_ATTR_REQ_PARTIAL_SET_MEMBER | FLAG_ATTR_NOT_REPLICATED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: DIT-Content-Rules
+ldapDisplayName: dITContentRules
+attributeId: 2.5.21.2
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 9a7ad946-ca53-11d1-bbd0-0080c76670c0
+systemOnly: TRUE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_IS_CONSTRUCTED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Division
+ldapDisplayName: division
+attributeId: 1.2.840.113556.1.4.261
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: fe6136a0-2073-11d0-a9c2-00aa006c33ed
+systemOnly: FALSE
+searchFlags: fCOPY
+rangeLower: 0
+rangeUpper: 256
+attributeSecurityGuid: e48d0154-bcf8-11d1-8702-00c04fb96050
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: DMD-Location
+ldapDisplayName: dMDLocation
+attributeId: 1.2.840.113556.1.2.36
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: TRUE
+schemaIdGuid: f0f8ff8b-1191-11d0-a060-00aa006c33ed
+systemOnly: TRUE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ACS-Max-No-Of-Account-Files
+ldapDisplayName: aCSMaxNoOfAccountFiles
+attributeId: 1.2.840.113556.1.4.901
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: f0722310-aef5-11d1-bdcf-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: DMD-Name
+ldapDisplayName: dmdName
+attributeId: 1.2.840.113556.1.2.598
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 167757b9-47f3-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 1024
+mapiID: 35926
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: DN-Reference-Update
+ldapDisplayName: dNReferenceUpdate
+attributeId: 1.2.840.113556.1.4.1242
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: 2df90d86-009f-11d2-aa4c-00c04fd7d83a
+systemOnly: TRUE
+searchFlags:fPRESERVEONDELETE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Dns-Allow-Dynamic
+ldapDisplayName: dnsAllowDynamic
+attributeId: 1.2.840.113556.1.4.378
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: e0fa1e65-9b45-11d0-afdd-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Dns-Allow-XFR
+ldapDisplayName: dnsAllowXFR
+attributeId: 1.2.840.113556.1.4.379
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: e0fa1e66-9b45-11d0-afdd-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: DNS-Host-Name
+ldapDisplayName: dNSHostName
+attributeId: 1.2.840.113556.1.4.619
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 72e39547-7b18-11d1-adef-00c04fd8d5cd
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 2048
+attributeSecurityGuid: 72e39547-7b18-11d1-adef-00c04fd8d5cd
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Dns-Notify-Secondaries
+ldapDisplayName: dnsNotifySecondaries
+attributeId: 1.2.840.113556.1.4.381
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: FALSE
+schemaIdGuid: e0fa1e68-9b45-11d0-afdd-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: DNS-Property
+ldapDisplayName: dNSProperty
+attributeId: 1.2.840.113556.1.4.1306
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: FALSE
+schemaIdGuid: 675a15fe-3b70-11d2-90cc-00c04fd91ab1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Dns-Record
+ldapDisplayName: dnsRecord
+attributeId: 1.2.840.113556.1.4.382
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: FALSE
+schemaIdGuid: e0fa1e69-9b45-11d0-afdd-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Dns-Root
+ldapDisplayName: dnsRoot
+attributeId: 1.2.840.113556.1.4.28
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: bf967959-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: fATTINDEX
+rangeLower: 1
+rangeUpper: 255
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Dns-Secure-Secondaries
+ldapDisplayName: dnsSecureSecondaries
+attributeId: 1.2.840.113556.1.4.380
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: FALSE
+schemaIdGuid: e0fa1e67-9b45-11d0-afdd-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ACS-Max-No-Of-Log-Files
+ldapDisplayName: aCSMaxNoOfLogFiles
+attributeId: 1.2.840.113556.1.4.774
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 1cb3559c-56d0-11d1-a9c6-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: DNS-Tombstoned
+ldapDisplayName: dNSTombstoned
+attributeId: 1.2.840.113556.1.4.1414
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: d5eb2eb7-be4e-463b-a214-634a44d7392e
+systemOnly: FALSE
+searchFlags: fATTINDEX
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: documentAuthor
+ldapDisplayName: documentAuthor
+attributeId: 0.9.2342.19200300.100.1.14
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: f18a8e19-af5f-4478-b096-6f35c27eb83f
+systemOnly: FALSE
+searchFlags: 0
+
+cn: documentIdentifier
+ldapDisplayName: documentIdentifier
+attributeId: 0.9.2342.19200300.100.1.11
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 0b21ce82-ff63-46d9-90fb-c8b9f24e97b9
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 256
+
+cn: documentLocation
+ldapDisplayName: documentLocation
+attributeId: 0.9.2342.19200300.100.1.15
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: b958b14e-ac6d-4ec4-8892-be70b69f7281
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 256
+
+cn: documentPublisher
+ldapDisplayName: documentPublisher
+attributeId: 0.9.2342.19200300.100.1.56
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 170f09d7-eb69-448a-9a30-f1afecfd32d7
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 256
+
+cn: documentTitle
+ldapDisplayName: documentTitle
+attributeId: 0.9.2342.19200300.100.1.12
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: de265a9c-ff2c-47b9-91dc-6e6fe2c43062
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 256
+
+cn: documentVersion
+ldapDisplayName: documentVersion
+attributeId: 0.9.2342.19200300.100.1.13
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 94b3a8a9-d613-4cec-9aad-5fbcc1046b43
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 256
+
+cn: Domain-Certificate-Authorities
+ldapDisplayName: domainCAs
+attributeId: 1.2.840.113556.1.4.668
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: 7bfdcb7a-4807-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Domain-Cross-Ref
+ldapDisplayName: domainCrossRef
+attributeId: 1.2.840.113556.1.4.472
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: TRUE
+schemaIdGuid: b000ea7b-a086-11d0-afdd-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Domain-ID
+ldapDisplayName: domainID
+attributeId: 1.2.840.113556.1.4.686
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: TRUE
+schemaIdGuid: 963d2734-48be-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Account-Name-History
+ldapDisplayName: accountNameHistory
+attributeId: 1.2.840.113556.1.4.1307
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 031952ec-3b72-11d2-90cc-00c04fd91ab1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ACS-Max-Peak-Bandwidth
+ldapDisplayName: aCSMaxPeakBandwidth
+attributeId: 1.2.840.113556.1.4.767
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: 7f561284-5301-11d1-a9c5-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Domain-Identifier
+ldapDisplayName: domainIdentifier
+attributeId: 1.2.840.113556.1.4.755
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 7f561278-5301-11d1-a9c5-0000f80367c1
+systemOnly: TRUE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Domain-Policy-Object
+ldapDisplayName: domainPolicyObject
+attributeId: 1.2.840.113556.1.4.32
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: TRUE
+schemaIdGuid: bf96795d-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Domain-Policy-Reference
+ldapDisplayName: domainPolicyReference
+attributeId: 1.2.840.113556.1.4.422
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: TRUE
+schemaIdGuid: 80a67e2a-9f22-11d0-afdd-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+attributeSecurityGuid: a29b89fe-c7e8-11d0-9bae-00c04fd92ef5
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Domain-Replica
+ldapDisplayName: domainReplica
+attributeId: 1.2.840.113556.1.4.158
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: bf96795e-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 32767
+attributeSecurityGuid: b8119fd0-04f6-4762-ab7a-4986c76b3f9a
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Domain-Wide-Policy
+ldapDisplayName: domainWidePolicy
+attributeId: 1.2.840.113556.1.4.421
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: FALSE
+schemaIdGuid: 80a67e29-9f22-11d0-afdd-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+attributeSecurityGuid: a29b89fd-c7e8-11d0-9bae-00c04fd92ef5
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: drink
+ldapDisplayName: drink
+attributeId: 0.9.2342.19200300.100.1.5
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 1a1aa5b5-262e-4df6-af04-2cf6b0d80048
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 256
+
+cn: Driver-Name
+ldapDisplayName: driverName
+attributeId: 1.2.840.113556.1.4.229
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 281416c5-1968-11d0-a28f-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Driver-Version
+ldapDisplayName: driverVersion
+attributeId: 1.2.840.113556.1.4.276
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: ba305f6e-47e3-11d0-a1a6-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: DSA-Signature
+ldapDisplayName: dSASignature
+attributeId: 1.2.840.113556.1.2.74
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 167757bc-47f3-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+mapiID: 32887
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: DS-Core-Propagation-Data
+ldapDisplayName: dSCorePropagationData
+attributeId: 1.2.840.113556.1.4.1357
+attributeSyntax: 2.5.5.11
+omSyntax: 24
+isSingleValued: FALSE
+schemaIdGuid: d167aa4b-8b08-11d2-9939-0000f87a57d4
+systemOnly: TRUE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT |FLAG_ATTR_REQ_PARTIAL_SET_MEMBER | FLAG_ATTR_NOT_REPLICATED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ACS-Max-Peak-Bandwidth-Per-Flow
+ldapDisplayName: aCSMaxPeakBandwidthPerFlow
+attributeId: 1.2.840.113556.1.4.759
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: 7f56127c-5301-11d1-a9c5-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: DS-Heuristics
+ldapDisplayName: dSHeuristics
+attributeId: 1.2.840.113556.1.2.212
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: f0f8ff86-1191-11d0-a060-00aa006c33ed
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: DS-UI-Admin-Maximum
+ldapDisplayName: dSUIAdminMaximum
+attributeId: 1.2.840.113556.1.4.1344
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: ee8d0ae0-6f91-11d2-9905-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: DS-UI-Admin-Notification
+ldapDisplayName: dSUIAdminNotification
+attributeId: 1.2.840.113556.1.4.1343
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: f6ea0a94-6f91-11d2-9905-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: DS-UI-Shell-Maximum
+ldapDisplayName: dSUIShellMaximum
+attributeId: 1.2.840.113556.1.4.1345
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: fcca766a-6f91-11d2-9905-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Dynamic-LDAP-Server
+ldapDisplayName: dynamicLDAPServer
+attributeId: 1.2.840.113556.1.4.537
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: TRUE
+schemaIdGuid: 52458021-ca6a-11d0-afff-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: EFSPolicy
+ldapDisplayName: eFSPolicy
+attributeId: 1.2.840.113556.1.4.268
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: FALSE
+schemaIdGuid: 8e4eb2ec-4712-11d0-a1a0-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+attributeSecurityGuid: a29b89fd-c7e8-11d0-9bae-00c04fd92ef5
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Employee-ID
+ldapDisplayName: employeeID
+attributeId: 1.2.840.113556.1.4.35
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: bf967962-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 16
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Employee-Number
+ldapDisplayName: employeeNumber
+attributeId: 1.2.840.113556.1.2.610
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: a8df73ef-c5ea-11d1-bbcb-0080c76670c0
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 512
+mapiID: 35943
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Employee-Type
+ldapDisplayName: employeeType
+attributeId: 1.2.840.113556.1.2.613
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: a8df73f0-c5ea-11d1-bbcb-0080c76670c0
+systemOnly: FALSE
+searchFlags: fCOPY
+rangeLower: 1
+rangeUpper: 256
+mapiID: 35945
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Enabled
+ldapDisplayName: Enabled
+attributeId: 1.2.840.113556.1.2.557
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: a8df73f2-c5ea-11d1-bbcb-0080c76670c0
+systemOnly: FALSE
+searchFlags: 0
+mapiID: 35873
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ACS-Max-Size-Of-RSVP-Account-File
+ldapDisplayName: aCSMaxSizeOfRSVPAccountFile
+attributeId: 1.2.840.113556.1.4.902
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: f0722311-aef5-11d1-bdcf-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Enabled-Connection
+ldapDisplayName: enabledConnection
+attributeId: 1.2.840.113556.1.4.36
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: bf967963-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Enrollment-Providers
+ldapDisplayName: enrollmentProviders
+attributeId: 1.2.840.113556.1.4.825
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 2a39c5b3-8960-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Entry-TTL
+ldapDisplayName: entryTTL
+attributeId: 1.3.6.1.4.1.1466.101.119.3
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: d213decc-d81a-4384-aac2-dcfcfd631cf8
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 31557600
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_IS_CONSTRUCTED
+
+cn: Extended-Attribute-Info
+ldapDisplayName: extendedAttributeInfo
+attributeId: 1.2.840.113556.1.4.909
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 9a7ad947-ca53-11d1-bbd0-0080c76670c0
+systemOnly: TRUE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_IS_CONSTRUCTED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Extended-Chars-Allowed
+ldapDisplayName: extendedCharsAllowed
+attributeId: 1.2.840.113556.1.2.380
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: bf967966-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+mapiID: 32935
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+systemOnly: TRUE
+
+cn: Extended-Class-Info
+ldapDisplayName: extendedClassInfo
+attributeId: 1.2.840.113556.1.4.908
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 9a7ad948-ca53-11d1-bbd0-0080c76670c0
+systemOnly: TRUE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_IS_CONSTRUCTED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Extension-Name
+ldapDisplayName: extensionName
+attributeId: 1.2.840.113556.1.2.227
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: bf967972-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 255
+mapiID: 32937
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Extra-Columns
+ldapDisplayName: extraColumns
+attributeId: 1.2.840.113556.1.4.1687
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: d24e2846-1dd9-4bcf-99d7-a6227cc86da7
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Facsimile-Telephone-Number
+ldapDisplayName: facsimileTelephoneNumber
+attributeId: 2.5.4.23
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: bf967974-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 64
+attributeSecurityGuid: 77b5b886-944a-11d1-aebd-0000f80367c1
+mapiID: 14883
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: File-Ext-Priority
+ldapDisplayName: fileExtPriority
+attributeId: 1.2.840.113556.1.4.816
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: d9e18315-8939-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+searchFlags: fATTINDEX
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ACS-Max-Size-Of-RSVP-Log-File
+ldapDisplayName: aCSMaxSizeOfRSVPLogFile
+attributeId: 1.2.840.113556.1.4.775
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 1cb3559d-56d0-11d1-a9c6-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Flags
+ldapDisplayName: flags
+attributeId: 1.2.840.113556.1.4.38
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: bf967976-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Flat-Name
+ldapDisplayName: flatName
+attributeId: 1.2.840.113556.1.4.511
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: b7b13117-b82e-11d0-afee-0000f80367c1
+systemOnly: FALSE
+searchFlags: fATTINDEX
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Force-Logoff
+ldapDisplayName: forceLogoff
+attributeId: 1.2.840.113556.1.4.39
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: bf967977-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+attributeSecurityGuid: b8119fd0-04f6-4762-ab7a-4986c76b3f9a
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Foreign-Identifier
+ldapDisplayName: foreignIdentifier
+attributeId: 1.2.840.113556.1.4.356
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 3e97891e-8c01-11d0-afda-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Friendly-Names
+ldapDisplayName: friendlyNames
+attributeId: 1.2.840.113556.1.4.682
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 7bfdcb88-4807-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: From-Entry
+ldapDisplayName: fromEntry
+attributeId: 1.2.840.113556.1.4.910
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: FALSE
+schemaIdGuid: 9a7ad949-ca53-11d1-bbd0-0080c76670c0
+systemOnly: TRUE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_IS_CONSTRUCTED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: From-Server
+ldapDisplayName: fromServer
+attributeId: 1.2.840.113556.1.4.40
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: TRUE
+schemaIdGuid: bf967979-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: fATTINDEX
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Frs-Computer-Reference
+ldapDisplayName: frsComputerReference
+attributeId: 1.2.840.113556.1.4.869
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: TRUE
+schemaIdGuid: 2a132578-9373-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+linkID: 102
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT |FLAG_ATTR_REQ_PARTIAL_SET_MEMBER
+
+cn: Frs-Computer-Reference-BL
+ldapDisplayName: frsComputerReferenceBL
+attributeId: 1.2.840.113556.1.4.870
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: 2a132579-9373-11d1-aebc-0000f80367c1
+systemOnly: TRUE
+searchFlags: 0
+linkID: 103
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_NOT_REPLICATED
+
+cn: FRS-Control-Data-Creation
+ldapDisplayName: fRSControlDataCreation
+attributeId: 1.2.840.113556.1.4.871
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 2a13257a-9373-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 32
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ACS-Max-Token-Bucket-Per-Flow
+ldapDisplayName: aCSMaxTokenBucketPerFlow
+attributeId: 1.2.840.113556.1.4.1313
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: 81f6e0df-3b90-11d2-90cc-00c04fd91ab1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: FRS-Control-Inbound-Backlog
+ldapDisplayName: fRSControlInboundBacklog
+attributeId: 1.2.840.113556.1.4.872
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 2a13257b-9373-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 32
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: FRS-Control-Outbound-Backlog
+ldapDisplayName: fRSControlOutboundBacklog
+attributeId: 1.2.840.113556.1.4.873
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 2a13257c-9373-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 32
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: FRS-Directory-Filter
+ldapDisplayName: fRSDirectoryFilter
+attributeId: 1.2.840.113556.1.4.484
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 1be8f171-a9ff-11d0-afe2-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 2048
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: FRS-DS-Poll
+ldapDisplayName: fRSDSPoll
+attributeId: 1.2.840.113556.1.4.490
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 1be8f177-a9ff-11d0-afe2-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: FRS-Extensions
+ldapDisplayName: fRSExtensions
+attributeId: 1.2.840.113556.1.4.536
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 52458020-ca6a-11d0-afff-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 65536
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: FRS-Fault-Condition
+ldapDisplayName: fRSFaultCondition
+attributeId: 1.2.840.113556.1.4.491
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 1be8f178-a9ff-11d0-afe2-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 16
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: FRS-File-Filter
+ldapDisplayName: fRSFileFilter
+attributeId: 1.2.840.113556.1.4.483
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 1be8f170-a9ff-11d0-afe2-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 2048
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: FRS-Flags
+ldapDisplayName: fRSFlags
+attributeId: 1.2.840.113556.1.4.874
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 2a13257d-9373-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: FRS-Level-Limit
+ldapDisplayName: fRSLevelLimit
+attributeId: 1.2.840.113556.1.4.534
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 5245801e-ca6a-11d0-afff-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: FRS-Member-Reference
+ldapDisplayName: fRSMemberReference
+attributeId: 1.2.840.113556.1.4.875
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: TRUE
+schemaIdGuid: 2a13257e-9373-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+linkID: 104
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT |FLAG_ATTR_REQ_PARTIAL_SET_MEMBER
+
+cn: ACS-Max-Token-Rate-Per-Flow
+ldapDisplayName: aCSMaxTokenRatePerFlow
+attributeId: 1.2.840.113556.1.4.758
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: 7f56127b-5301-11d1-a9c5-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: FRS-Member-Reference-BL
+ldapDisplayName: fRSMemberReferenceBL
+attributeId: 1.2.840.113556.1.4.876
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: 2a13257f-9373-11d1-aebc-0000f80367c1
+systemOnly: TRUE
+searchFlags: 0
+linkID: 105
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_NOT_REPLICATED
+
+cn: FRS-Partner-Auth-Level
+ldapDisplayName: fRSPartnerAuthLevel
+attributeId: 1.2.840.113556.1.4.877
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 2a132580-9373-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: FRS-Primary-Member
+ldapDisplayName: fRSPrimaryMember
+attributeId: 1.2.840.113556.1.4.878
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: TRUE
+schemaIdGuid: 2a132581-9373-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+linkID: 106
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: FRS-Replica-Set-GUID
+ldapDisplayName: fRSReplicaSetGUID
+attributeId: 1.2.840.113556.1.4.533
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 5245801a-ca6a-11d0-afff-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 16
+rangeUpper: 16
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: FRS-Replica-Set-Type
+ldapDisplayName: fRSReplicaSetType
+attributeId: 1.2.840.113556.1.4.31
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 26d9736b-6070-11d1-a9c6-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: FRS-Root-Path
+ldapDisplayName: fRSRootPath
+attributeId: 1.2.840.113556.1.4.487
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 1be8f174-a9ff-11d0-afe2-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 2048
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: FRS-Root-Security
+ldapDisplayName: fRSRootSecurity
+attributeId: 1.2.840.113556.1.4.535
+attributeSyntax: 2.5.5.15
+omSyntax: 66
+isSingleValued: TRUE
+schemaIdGuid: 5245801f-ca6a-11d0-afff-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 65535
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: FRS-Service-Command
+ldapDisplayName: fRSServiceCommand
+attributeId: 1.2.840.113556.1.4.500
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: ddac0cee-af8f-11d0-afeb-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 512
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: FRS-Service-Command-Status
+ldapDisplayName: fRSServiceCommandStatus
+attributeId: 1.2.840.113556.1.4.879
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 2a132582-9373-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 512
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: FRS-Staging-Path
+ldapDisplayName: fRSStagingPath
+attributeId: 1.2.840.113556.1.4.488
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 1be8f175-a9ff-11d0-afe2-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 2048
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ACS-Minimum-Delay-Variation
+ldapDisplayName: aCSMinimumDelayVariation
+attributeId: 1.2.840.113556.1.4.1317
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: 9c65329b-3b90-11d2-90cc-00c04fd91ab1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: FRS-Time-Last-Command
+ldapDisplayName: fRSTimeLastCommand
+attributeId: 1.2.840.113556.1.4.880
+attributeSyntax: 2.5.5.11
+omSyntax: 23
+isSingleValued: TRUE
+schemaIdGuid: 2a132583-9373-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: FRS-Time-Last-Config-Change
+ldapDisplayName: fRSTimeLastConfigChange
+attributeId: 1.2.840.113556.1.4.881
+attributeSyntax: 2.5.5.11
+omSyntax: 23
+isSingleValued: TRUE
+schemaIdGuid: 2a132584-9373-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: FRS-Update-Timeout
+ldapDisplayName: fRSUpdateTimeout
+attributeId: 1.2.840.113556.1.4.485
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 1be8f172-a9ff-11d0-afe2-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: FRS-Version
+ldapDisplayName: fRSVersion
+attributeId: 1.2.840.113556.1.4.882
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 2a132585-9373-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 32
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: FRS-Version-GUID
+ldapDisplayName: fRSVersionGUID
+attributeId: 1.2.840.113556.1.4.43
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 26d9736c-6070-11d1-a9c6-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 16
+rangeUpper: 16
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: FRS-Working-Path
+ldapDisplayName: fRSWorkingPath
+attributeId: 1.2.840.113556.1.4.486
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 1be8f173-a9ff-11d0-afe2-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 2048
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: FSMO-Role-Owner
+ldapDisplayName: fSMORoleOwner
+attributeId: 1.2.840.113556.1.4.369
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: TRUE
+schemaIdGuid: 66171887-8f3c-11d0-afda-00c04fd930c9
+systemOnly: FALSE
+searchFlags: fATTINDEX
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Garbage-Coll-Period
+ldapDisplayName: garbageCollPeriod
+attributeId: 1.2.840.113556.1.2.301
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 5fd424a1-1262-11d0-a060-00aa006c33ed
+systemOnly: FALSE
+searchFlags: 0
+mapiID: 32943
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Gecos
+ldapDisplayName: gecos
+attributeId: 1.3.6.1.1.1.1.2
+attributeSyntax: 2.5.5.5
+omSyntax: 22
+isSingleValued: TRUE
+schemaIdGuid: a3e03f1f-1d55-4253-a0af-30c2a784e46e
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 10240
+
+cn: Generated-Connection
+ldapDisplayName: generatedConnection
+attributeId: 1.2.840.113556.1.4.41
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: bf96797a-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ACS-Minimum-Latency
+ldapDisplayName: aCSMinimumLatency
+attributeId: 1.2.840.113556.1.4.1316
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: 9517fefb-3b90-11d2-90cc-00c04fd91ab1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Generation-Qualifier
+ldapDisplayName: generationQualifier
+attributeId: 2.5.4.44
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 16775804-47f3-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 64
+mapiID: 35923
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: GidNumber
+ldapDisplayName: gidNumber
+attributeId: 1.3.6.1.1.1.1.1
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: c5b95f0c-ec9e-41c4-849c-b46597ed6696
+systemOnly: FALSE
+searchFlags: fATTINDEX
+
+cn: Given-Name
+ldapDisplayName: givenName
+attributeId: 2.5.4.42
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: f0f8ff8e-1191-11d0-a060-00aa006c33ed
+systemOnly: FALSE
+searchFlags: fANR | fATTINDEX
+rangeLower: 1
+rangeUpper: 64
+attributeSecurityGuid: e48d0154-bcf8-11d1-8702-00c04fb96050
+mapiID: 14854
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Global-Address-List
+ldapDisplayName: globalAddressList
+attributeId: 1.2.840.113556.1.4.1245
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: f754c748-06f4-11d2-aa53-00c04fd7d83a
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Global-Address-List2
+ldapDisplayName: globalAddressList2
+attributeId: 1.2.840.113556.1.4.2047
+attributeSyntax: 2.5.5.1
+linkID: 2124
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: 4898f63d-4112-477c-8826-3ca00bd8277d
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Governs-ID
+ldapDisplayName: governsID
+attributeId: 1.2.840.113556.1.2.22
+attributeSyntax: 2.5.5.2
+omSyntax: 6
+isSingleValued: TRUE
+schemaIdGuid: bf96797d-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+searchFlags:fPRESERVEONDELETE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: GPC-File-Sys-Path
+ldapDisplayName: gPCFileSysPath
+attributeId: 1.2.840.113556.1.4.894
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: f30e3bc1-9ff0-11d1-b603-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: GPC-Functionality-Version
+ldapDisplayName: gPCFunctionalityVersion
+attributeId: 1.2.840.113556.1.4.893
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: f30e3bc0-9ff0-11d1-b603-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: GPC-Machine-Extension-Names
+ldapDisplayName: gPCMachineExtensionNames
+attributeId: 1.2.840.113556.1.4.1348
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 32ff8ecc-783f-11d2-9916-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: GPC-User-Extension-Names
+ldapDisplayName: gPCUserExtensionNames
+attributeId: 1.2.840.113556.1.4.1349
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 42a75fc6-783f-11d2-9916-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: GPC-WQL-Filter
+ldapDisplayName: gPCWQLFilter
+attributeId: 1.2.840.113556.1.4.1694
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 7bd4c7a6-1add-4436-8c04-3999a880154c
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ACS-Minimum-Policed-Size
+ldapDisplayName: aCSMinimumPolicedSize
+attributeId: 1.2.840.113556.1.4.1315
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: 8d0e7195-3b90-11d2-90cc-00c04fd91ab1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: GP-Link
+ldapDisplayName: gPLink
+attributeId: 1.2.840.113556.1.4.891
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: f30e3bbe-9ff0-11d1-b603-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: GP-Options
+ldapDisplayName: gPOptions
+attributeId: 1.2.840.113556.1.4.892
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: f30e3bbf-9ff0-11d1-b603-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Group-Attributes
+ldapDisplayName: groupAttributes
+attributeId: 1.2.840.113556.1.4.152
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: bf96797e-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: fATTINDEX
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Group-Membership-SAM
+ldapDisplayName: groupMembershipSAM
+attributeId: 1.2.840.113556.1.4.166
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: bf967980-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Group-Priority
+ldapDisplayName: groupPriority
+attributeId: 1.2.840.113556.1.4.345
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: eea65905-8ac6-11d0-afda-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Groups-to-Ignore
+ldapDisplayName: groupsToIgnore
+attributeId: 1.2.840.113556.1.4.344
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: eea65904-8ac6-11d0-afda-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Group-Type
+ldapDisplayName: groupType
+attributeId: 1.2.840.113556.1.4.750
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 9a9a021e-4a5b-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags:fPRESERVEONDELETE | fATTINDEX
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT |FLAG_ATTR_REQ_PARTIAL_SET_MEMBER
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Has-Master-NCs
+ldapDisplayName: hasMasterNCs
+attributeId: 1.2.840.113556.1.2.14
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: bf967982-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+searchFlags: 0
+linkID: 76
+mapiID: 32950
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Has-Partial-Replica-NCs
+ldapDisplayName: hasPartialReplicaNCs
+attributeId: 1.2.840.113556.1.2.15
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: bf967981-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+searchFlags: 0
+linkID: 74
+mapiID: 32949
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Help-Data16
+ldapDisplayName: helpData16
+attributeId: 1.2.840.113556.1.2.402
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 5fd424a7-1262-11d0-a060-00aa006c33ed
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 32768
+mapiID: 32826
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ACS-Non-Reserved-Max-SDU-Size
+ldapDisplayName: aCSNonReservedMaxSDUSize
+attributeId: 1.2.840.113556.1.4.1320
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: aec2cfe3-3b90-11d2-90cc-00c04fd91ab1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Help-Data32
+ldapDisplayName: helpData32
+attributeId: 1.2.840.113556.1.2.9
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 5fd424a8-1262-11d0-a060-00aa006c33ed
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 32768
+mapiID: 32784
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Help-File-Name
+ldapDisplayName: helpFileName
+attributeId: 1.2.840.113556.1.2.327
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 5fd424a9-1262-11d0-a060-00aa006c33ed
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 13
+mapiID: 32827
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Hide-From-AB
+ldapDisplayName: hideFromAB
+attributeId: 1.2.840.113556.1.4.1780
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: ec05b750-a977-4efe-8e8d-ba6c1a6e33a8
+systemOnly: FALSE
+searchFlags: 0
+
+cn: Home-Directory
+ldapDisplayName: homeDirectory
+attributeId: 1.2.840.113556.1.4.44
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: bf967985-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: fCOPY
+attributeSecurityGuid: 5f202010-79a5-11d0-9020-00c04fc2d4cf
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Home-Drive
+ldapDisplayName: homeDrive
+attributeId: 1.2.840.113556.1.4.45
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: bf967986-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: fCOPY
+attributeSecurityGuid: 5f202010-79a5-11d0-9020-00c04fc2d4cf
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Phone-Home-Primary
+ldapDisplayName: homePhone
+attributeId: 0.9.2342.19200300.100.1.20
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: f0f8ffa1-1191-11d0-a060-00aa006c33ed
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 64
+attributeSecurityGuid: 77b5b886-944a-11d1-aebd-0000f80367c1
+mapiID: 14857
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Address-Home
+ldapDisplayName: homePostalAddress
+attributeId: 1.2.840.113556.1.2.617
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 16775781-47f3-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 4096
+attributeSecurityGuid: 77b5b886-944a-11d1-aebd-0000f80367c1
+mapiID: 14941
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: host
+ldapDisplayName: host
+attributeId: 0.9.2342.19200300.100.1.9
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 6043df71-fa48-46cf-ab7c-cbd54644b22d
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 256
+
+cn: houseIdentifier
+ldapDisplayName: houseIdentifier
+attributeId: 2.5.4.51
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: a45398b7-c44a-4eb6-82d3-13c10946dbfe
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 32768
+
+cn: Icon-Path
+ldapDisplayName: iconPath
+attributeId: 1.2.840.113556.1.4.219
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: f0f8ff83-1191-11d0-a060-00aa006c33ed
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 2048
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ACS-Aggregate-Token-Rate-Per-User
+ldapDisplayName: aCSAggregateTokenRatePerUser
+attributeId: 1.2.840.113556.1.4.760
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: 7f56127d-5301-11d1-a9c5-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ACS-Non-Reserved-Min-Policed-Size
+ldapDisplayName: aCSNonReservedMinPolicedSize
+attributeId: 1.2.840.113556.1.4.1321
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: b6873917-3b90-11d2-90cc-00c04fd91ab1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Implemented-Categories
+ldapDisplayName: implementedCategories
+attributeId: 1.2.840.113556.1.4.320
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: FALSE
+schemaIdGuid: 7d6c0e92-7e20-11d0-afd6-00c04fd930c9
+systemOnly: FALSE
+searchFlags: fATTINDEX
+rangeLower: 16
+rangeUpper: 16
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: IndexedScopes
+ldapDisplayName: indexedScopes
+attributeId: 1.2.840.113556.1.4.681
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 7bfdcb87-4807-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Comment
+ldapDisplayName: info
+attributeId: 1.2.840.113556.1.2.81
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: bf96793e-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 1024
+attributeSecurityGuid: 77b5b886-944a-11d1-aebd-0000f80367c1
+mapiID: 12292
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Initial-Auth-Incoming
+ldapDisplayName: initialAuthIncoming
+attributeId: 1.2.840.113556.1.4.539
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 52458023-ca6a-11d0-afff-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Initial-Auth-Outgoing
+ldapDisplayName: initialAuthOutgoing
+attributeId: 1.2.840.113556.1.4.540
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 52458024-ca6a-11d0-afff-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Initials
+ldapDisplayName: initials
+attributeId: 2.5.4.43
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: f0f8ff90-1191-11d0-a060-00aa006c33ed
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 6
+attributeSecurityGuid: e48d0154-bcf8-11d1-8702-00c04fb96050
+mapiID: 14858
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Install-Ui-Level
+ldapDisplayName: installUiLevel
+attributeId: 1.2.840.113556.1.4.847
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 96a7dd64-9118-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Instance-Type
+ldapDisplayName: instanceType
+attributeId: 1.2.840.113556.1.2.1
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: bf96798c-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+searchFlags:fPRESERVEONDELETE
+mapiID: 32957
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT |FLAG_ATTR_REQ_PARTIAL_SET_MEMBER
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: International-ISDN-Number
+ldapDisplayName: internationalISDNNumber
+attributeId: 2.5.4.25
+attributeSyntax: 2.5.5.6
+omSyntax: 18
+isSingleValued: FALSE
+schemaIdGuid: bf96798d-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 16
+attributeSecurityGuid: 77b5b886-944a-11d1-aebd-0000f80367c1
+mapiID: 32958
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Inter-Site-Topology-Failover
+ldapDisplayName: interSiteTopologyFailover
+attributeId: 1.2.840.113556.1.4.1248
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: b7c69e60-2cc7-11d2-854e-00a0c983f608
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ACS-Non-Reserved-Peak-Rate
+ldapDisplayName: aCSNonReservedPeakRate
+attributeId: 1.2.840.113556.1.4.1318
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: a331a73f-3b90-11d2-90cc-00c04fd91ab1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Inter-Site-Topology-Generator
+ldapDisplayName: interSiteTopologyGenerator
+attributeId: 1.2.840.113556.1.4.1246
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: TRUE
+schemaIdGuid: b7c69e5e-2cc7-11d2-854e-00a0c983f608
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Inter-Site-Topology-Renew
+ldapDisplayName: interSiteTopologyRenew
+attributeId: 1.2.840.113556.1.4.1247
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: b7c69e5f-2cc7-11d2-854e-00a0c983f608
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Invocation-Id
+ldapDisplayName: invocationId
+attributeId: 1.2.840.113556.1.2.115
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: bf96798e-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+searchFlags: fATTINDEX
+mapiID: 32959
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: IpHostNumber
+ldapDisplayName: ipHostNumber
+attributeId: 1.3.6.1.1.1.1.19
+attributeSyntax: 2.5.5.5
+omSyntax: 22
+isSingleValued: FALSE
+schemaIdGuid: de8bb721-85dc-4fde-b687-9657688e667e
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 128
+
+cn: IpNetmaskNumber
+ldapDisplayName: ipNetmaskNumber
+attributeId: 1.3.6.1.1.1.1.21
+attributeSyntax: 2.5.5.5
+omSyntax: 22
+isSingleValued: TRUE
+schemaIdGuid: 6ff64fcd-462e-4f62-b44a-9a5347659eb9
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 128
+
+cn: IpNetworkNumber
+ldapDisplayName: ipNetworkNumber
+attributeId: 1.3.6.1.1.1.1.20
+attributeSyntax: 2.5.5.5
+omSyntax: 22
+isSingleValued: TRUE
+schemaIdGuid: 4e3854f4-3087-42a4-a813-bb0c528958d3
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 128
+
+cn: Phone-Ip-Primary
+ldapDisplayName: ipPhone
+attributeId: 1.2.840.113556.1.4.721
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 4d146e4a-48d4-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 64
+attributeSecurityGuid: 77b5b886-944a-11d1-aebd-0000f80367c1
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: IpProtocolNumber
+ldapDisplayName: ipProtocolNumber
+attributeId: 1.3.6.1.1.1.1.17
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: ebf5c6eb-0e2d-4415-9670-1081993b4211
+systemOnly: FALSE
+searchFlags: 0
+
+cn: Ipsec-Data
+ldapDisplayName: ipsecData
+attributeId: 1.2.840.113556.1.4.623
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: b40ff81f-427a-11d1-a9c2-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Ipsec-Data-Type
+ldapDisplayName: ipsecDataType
+attributeId: 1.2.840.113556.1.4.622
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: b40ff81e-427a-11d1-a9c2-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ACS-Non-Reserved-Token-Size
+ldapDisplayName: aCSNonReservedTokenSize
+attributeId: 1.2.840.113556.1.4.1319
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: a916d7c9-3b90-11d2-90cc-00c04fd91ab1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Ipsec-Filter-Reference
+ldapDisplayName: ipsecFilterReference
+attributeId: 1.2.840.113556.1.4.629
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: b40ff823-427a-11d1-a9c2-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Ipsec-ID
+ldapDisplayName: ipsecID
+attributeId: 1.2.840.113556.1.4.621
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: b40ff81d-427a-11d1-a9c2-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Ipsec-ISAKMP-Reference
+ldapDisplayName: ipsecISAKMPReference
+attributeId: 1.2.840.113556.1.4.626
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: TRUE
+schemaIdGuid: b40ff820-427a-11d1-a9c2-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Ipsec-Name
+ldapDisplayName: ipsecName
+attributeId: 1.2.840.113556.1.4.620
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: b40ff81c-427a-11d1-a9c2-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: IPSEC-Negotiation-Policy-Action
+ldapDisplayName: iPSECNegotiationPolicyAction
+attributeId: 1.2.840.113556.1.4.888
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 07383075-91df-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Ipsec-Negotiation-Policy-Reference
+ldapDisplayName: ipsecNegotiationPolicyReference
+attributeId: 1.2.840.113556.1.4.628
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: TRUE
+schemaIdGuid: b40ff822-427a-11d1-a9c2-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: IPSEC-Negotiation-Policy-Type
+ldapDisplayName: iPSECNegotiationPolicyType
+attributeId: 1.2.840.113556.1.4.887
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 07383074-91df-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Ipsec-NFA-Reference
+ldapDisplayName: ipsecNFAReference
+attributeId: 1.2.840.113556.1.4.627
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: b40ff821-427a-11d1-a9c2-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Ipsec-Owners-Reference
+ldapDisplayName: ipsecOwnersReference
+attributeId: 1.2.840.113556.1.4.624
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: b40ff824-427a-11d1-a9c2-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Ipsec-Policy-Reference
+ldapDisplayName: ipsecPolicyReference
+attributeId: 1.2.840.113556.1.4.517
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: TRUE
+schemaIdGuid: b7b13118-b82e-11d0-afee-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ACS-Non-Reserved-Tx-Limit
+ldapDisplayName: aCSNonReservedTxLimit
+attributeId: 1.2.840.113556.1.4.780
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: 1cb355a2-56d0-11d1-a9c6-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: IpServicePort
+ldapDisplayName: ipServicePort
+attributeId: 1.3.6.1.1.1.1.15
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: ff2daebf-f463-495a-8405-3e483641eaa2
+systemOnly: FALSE
+searchFlags: 0
+
+cn: IpServiceProtocol
+ldapDisplayName: ipServiceProtocol
+attributeId: 1.3.6.1.1.1.1.16
+attributeSyntax: 2.5.5.5
+omSyntax: 22
+isSingleValued: FALSE
+schemaIdGuid: cd96ec0b-1ed6-43b4-b26b-f170b645883f
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 1024
+
+cn: Is-Critical-System-Object
+ldapDisplayName: isCriticalSystemObject
+attributeId: 1.2.840.113556.1.4.868
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: 00fbf30d-91fe-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Is-Defunct
+ldapDisplayName: isDefunct
+attributeId: 1.2.840.113556.1.4.661
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: 28630ebe-41d5-11d1-a9c1-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Is-Deleted
+ldapDisplayName: isDeleted
+attributeId: 1.2.840.113556.1.2.48
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: bf96798f-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+searchFlags: 0
+mapiID: 32960
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT |FLAG_ATTR_REQ_PARTIAL_SET_MEMBER
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Is-Ephemeral
+ldapDisplayName: isEphemeral
+attributeId: 1.2.840.113556.1.4.1212
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: f4c453f0-c5f1-11d1-bbcb-0080c76670c0
+systemOnly: TRUE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Is-Member-Of-Partial-Attribute-Set
+ldapDisplayName: isMemberOfPartialAttributeSet
+attributeId: 1.2.840.113556.1.4.639
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: 19405b9d-3cfa-11d1-a9c0-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Is-Privilege-Holder
+ldapDisplayName: isPrivilegeHolder
+attributeId: 1.2.840.113556.1.4.638
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: 19405b9c-3cfa-11d1-a9c0-0000f80367c1
+systemOnly: TRUE
+searchFlags: 0
+linkID: 71
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_NOT_REPLICATED
+
+cn: Is-Single-Valued
+ldapDisplayName: isSingleValued
+attributeId: 1.2.840.113556.1.2.33
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: bf967992-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+searchFlags: 0
+mapiID: 32961
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: jpegPhoto
+ldapDisplayName: jpegPhoto
+attributeId: 0.9.2342.19200300.100.1.60
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: FALSE
+schemaIdGuid: bac80572-09c4-4fa9-9ae6-7628d7adbe0e
+systemOnly: FALSE
+searchFlags: 0
+showInAdvancedViewOnly: FALSE
+
+cn: ACS-Non-Reserved-Tx-Size
+ldapDisplayName: aCSNonReservedTxSize
+attributeId: 1.2.840.113556.1.4.898
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: f072230d-aef5-11d1-bdcf-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Keywords
+ldapDisplayName: keywords
+attributeId: 1.2.840.113556.1.4.48
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: bf967993-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: fATTINDEX
+rangeLower: 1
+rangeUpper: 256
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Knowledge-Information
+ldapDisplayName: knowledgeInformation
+attributeId: 2.5.4.2
+attributeSyntax: 2.5.5.4
+omSyntax: 20
+isSingleValued: FALSE
+schemaIdGuid: 1677581f-47f3-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+mapiID: 32963
+
+cn: Locality-Name
+ldapDisplayName: l
+attributeId: 2.5.4.7
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: bf9679a2-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: fCOPY | fATTINDEX
+rangeLower: 1
+rangeUpper: 128
+attributeSecurityGuid: 77b5b886-944a-11d1-aebd-0000f80367c1
+mapiID: 14887
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT |FLAG_ATTR_REQ_PARTIAL_SET_MEMBER
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: labeledURI
+ldapDisplayName: labeledURI
+attributeId: 1.3.6.1.4.1.250.1.57
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: c569bb46-c680-44bc-a273-e6c227d71b45
+systemOnly: FALSE
+searchFlags: 0
+showInAdvancedViewOnly: FALSE
+
+cn: Last-Backup-Restoration-Time
+ldapDisplayName: lastBackupRestorationTime
+attributeId: 1.2.840.113556.1.4.519
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: 1fbb0be8-ba63-11d0-afef-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Last-Content-Indexed
+ldapDisplayName: lastContentIndexed
+attributeId: 1.2.840.113556.1.4.50
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: bf967995-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Last-Known-Parent
+ldapDisplayName: lastKnownParent
+attributeId: 1.2.840.113556.1.4.781
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: TRUE
+schemaIdGuid: 52ab8670-5709-11d1-a9c6-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Last-Logoff
+ldapDisplayName: lastLogoff
+attributeId: 1.2.840.113556.1.4.51
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: bf967996-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+attributeSecurityGuid: 5f202010-79a5-11d0-9020-00c04fc2d4cf
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_NOT_REPLICATED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Last-Logon
+ldapDisplayName: lastLogon
+attributeId: 1.2.840.113556.1.4.52
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: bf967997-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+attributeSecurityGuid: 5f202010-79a5-11d0-9020-00c04fc2d4cf
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_NOT_REPLICATED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Last-Logon-Timestamp
+ldapDisplayName: lastLogonTimestamp
+attributeId: 1.2.840.113556.1.4.1696
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: c0e20a04-0e5a-4ff3-9482-5efeaecd7060
+systemOnly: FALSE
+searchFlags: 0
+attributeSecurityGuid: 5f202010-79a5-11d0-9020-00c04fc2d4cf
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ACS-Permission-Bits
+ldapDisplayName: aCSPermissionBits
+attributeId: 1.2.840.113556.1.4.765
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: 7f561282-5301-11d1-a9c5-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Last-Set-Time
+ldapDisplayName: lastSetTime
+attributeId: 1.2.840.113556.1.4.53
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: bf967998-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Last-Update-Sequence
+ldapDisplayName: lastUpdateSequence
+attributeId: 1.2.840.113556.1.4.330
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 7d6c0e9c-7e20-11d0-afd6-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: LDAP-Admin-Limits
+ldapDisplayName: lDAPAdminLimits
+attributeId: 1.2.840.113556.1.4.843
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 7359a352-90f7-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: LDAP-Display-Name
+ldapDisplayName: lDAPDisplayName
+attributeId: 1.2.840.113556.1.2.460
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: bf96799a-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags:fPRESERVEONDELETE | fATTINDEX
+rangeLower: 1
+rangeUpper: 256
+mapiID: 33137
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: LDAP-IPDeny-List
+ldapDisplayName: lDAPIPDenyList
+attributeId: 1.2.840.113556.1.4.844
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: FALSE
+schemaIdGuid: 7359a353-90f7-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: LSA-Creation-Time
+ldapDisplayName: lSACreationTime
+attributeId: 1.2.840.113556.1.4.66
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: bf9679ad-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: LSA-Modified-Count
+ldapDisplayName: lSAModifiedCount
+attributeId: 1.2.840.113556.1.4.67
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: bf9679ae-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Legacy-Exchange-DN
+ldapDisplayName: legacyExchangeDN
+attributeId: 1.2.840.113556.1.4.655
+attributeSyntax: 2.5.5.4
+omSyntax: 20
+isSingleValued: TRUE
+schemaIdGuid: 28630ebc-41d5-11d1-a9c1-0000f80367c1
+systemOnly: FALSE
+searchFlags:fPRESERVEONDELETE| fANR | fATTINDEX
+attributeSecurityGuid: e48d0154-bcf8-11d1-8702-00c04fb96050
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Link-ID
+ldapDisplayName: linkID
+attributeId: 1.2.840.113556.1.2.50
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: bf96799b-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+searchFlags: 0
+mapiID: 32965
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Link-Track-Secret
+ldapDisplayName: linkTrackSecret
+attributeId: 1.2.840.113556.1.4.269
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 2ae80fe2-47b4-11d0-a1a4-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 16
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ACS-Policy-Name
+ldapDisplayName: aCSPolicyName
+attributeId: 1.2.840.113556.1.4.772
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 1cb3559a-56d0-11d1-a9c6-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Lm-Pwd-History
+ldapDisplayName: lmPwdHistory
+attributeId: 1.2.840.113556.1.4.160
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: FALSE
+schemaIdGuid: bf96799d-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Locale-ID
+ldapDisplayName: localeID
+attributeId: 1.2.840.113556.1.4.58
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: FALSE
+schemaIdGuid: bf9679a1-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: fCOPY
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Localization-Display-Id
+ldapDisplayName: localizationDisplayId
+attributeId: 1.2.840.113556.1.4.1353
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: a746f0d1-78d0-11d2-9916-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Localized-Description
+ldapDisplayName: localizedDescription
+attributeId: 1.2.840.113556.1.4.817
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: d9e18316-8939-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Local-Policy-Flags
+ldapDisplayName: localPolicyFlags
+attributeId: 1.2.840.113556.1.4.56
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: bf96799e-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Local-Policy-Reference
+ldapDisplayName: localPolicyReference
+attributeId: 1.2.840.113556.1.4.457
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: TRUE
+schemaIdGuid: 80a67e4d-9f22-11d0-afdd-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+attributeSecurityGuid: a29b8a01-c7e8-11d0-9bae-00c04fd92ef5
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Location
+ldapDisplayName: location
+attributeId: 1.2.840.113556.1.4.222
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 09dcb79f-165f-11d0-a064-00aa006c33ed
+systemOnly: FALSE
+searchFlags: fATTINDEX
+rangeLower: 0
+rangeUpper: 1024
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Lockout-Duration
+ldapDisplayName: lockoutDuration
+attributeId: 1.2.840.113556.1.4.60
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: bf9679a5-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+attributeSecurityGuid: c7407360-20bf-11d0-a768-00aa006e0529
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Lock-Out-Observation-Window
+ldapDisplayName: lockOutObservationWindow
+attributeId: 1.2.840.113556.1.4.61
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: bf9679a4-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+attributeSecurityGuid: c7407360-20bf-11d0-a768-00aa006e0529
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Lockout-Threshold
+ldapDisplayName: lockoutThreshold
+attributeId: 1.2.840.113556.1.4.73
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: bf9679a6-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 65535
+attributeSecurityGuid: c7407360-20bf-11d0-a768-00aa006e0529
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ACS-Priority
+ldapDisplayName: aCSPriority
+attributeId: 1.2.840.113556.1.4.764
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 7f561281-5301-11d1-a9c5-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Lockout-Time
+ldapDisplayName: lockoutTime
+attributeId: 1.2.840.113556.1.4.662
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: 28630ebf-41d5-11d1-a9c1-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: LoginShell
+ldapDisplayName: loginShell
+attributeId: 1.3.6.1.1.1.1.4
+attributeSyntax: 2.5.5.5
+omSyntax: 22
+isSingleValued: TRUE
+schemaIdGuid: a553d12c-3231-4c5e-8adf-8d189697721e
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 1024
+
+cn: Logon-Count
+ldapDisplayName: logonCount
+attributeId: 1.2.840.113556.1.4.169
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: bf9679aa-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+attributeSecurityGuid: 5f202010-79a5-11d0-9020-00c04fc2d4cf
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_NOT_REPLICATED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Logon-Hours
+ldapDisplayName: logonHours
+attributeId: 1.2.840.113556.1.4.64
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: bf9679ab-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: fCOPY
+attributeSecurityGuid: 5f202010-79a5-11d0-9020-00c04fc2d4cf
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Logon-Workstation
+ldapDisplayName: logonWorkstation
+attributeId: 1.2.840.113556.1.4.65
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: bf9679ac-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: fCOPY
+attributeSecurityGuid: 5f202010-79a5-11d0-9020-00c04fc2d4cf
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ACS-RSVP-Account-Files-Location
+ldapDisplayName: aCSRSVPAccountFilesLocation
+attributeId: 1.2.840.113556.1.4.900
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: f072230f-aef5-11d1-bdcf-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ACS-RSVP-Log-Files-Location
+ldapDisplayName: aCSRSVPLogFilesLocation
+attributeId: 1.2.840.113556.1.4.773
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 1cb3559b-56d0-11d1-a9c6-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ACS-Allocable-RSVP-Bandwidth
+ldapDisplayName: aCSAllocableRSVPBandwidth
+attributeId: 1.2.840.113556.1.4.766
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: 7f561283-5301-11d1-a9c5-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ACS-Server-List
+ldapDisplayName: aCSServerList
+attributeId: 1.2.840.113556.1.4.1312
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 7cbd59a5-3b90-11d2-90cc-00c04fd91ab1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ACS-Service-Type
+ldapDisplayName: aCSServiceType
+attributeId: 1.2.840.113556.1.4.762
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 7f56127f-5301-11d1-a9c5-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ACS-Time-Of-Day
+ldapDisplayName: aCSTimeOfDay
+attributeId: 1.2.840.113556.1.4.756
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 7f561279-5301-11d1-a9c5-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ACS-Total-No-Of-Flows
+ldapDisplayName: aCSTotalNoOfFlows
+attributeId: 1.2.840.113556.1.4.763
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 7f561280-5301-11d1-a9c5-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Additional-Trusted-Service-Names
+ldapDisplayName: additionalTrustedServiceNames
+attributeId: 1.2.840.113556.1.4.889
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 032160be-9824-11d1-aec0-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Address-Book-Roots
+ldapDisplayName: addressBookRoots
+attributeId: 1.2.840.113556.1.4.1244
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: f70b6e48-06f4-11d2-aa53-00c04fd7d83a
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Address-Book-Roots2
+ldapDisplayName: addressBookRoots2
+attributeId: 1.2.840.113556.1.4.2046
+attributeSyntax: 2.5.5.1
+linkID: 2122
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: 508ca374-a511-4e4e-9f4f-856f61a6b7e4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Address-Entry-Display-Table
+ldapDisplayName: addressEntryDisplayTable
+attributeId: 1.2.840.113556.1.2.324
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 5fd42461-1262-11d0-a060-00aa006c33ed
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 32768
+mapiID: 32791
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Address-Entry-Display-Table-MSDOS
+ldapDisplayName: addressEntryDisplayTableMSDOS
+attributeId: 1.2.840.113556.1.2.400
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 5fd42462-1262-11d0-a060-00aa006c33ed
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 32768
+mapiID: 32839
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Address-Syntax
+ldapDisplayName: addressSyntax
+attributeId: 1.2.840.113556.1.2.255
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 5fd42463-1262-11d0-a060-00aa006c33ed
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 4096
+mapiID: 32792
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Address-Type
+ldapDisplayName: addressType
+attributeId: 1.2.840.113556.1.2.350
+attributeSyntax: 2.5.5.4
+omSyntax: 20
+isSingleValued: TRUE
+schemaIdGuid: 5fd42464-1262-11d0-a060-00aa006c33ed
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 32
+mapiID: 32840
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ACS-Cache-Timeout
+ldapDisplayName: aCSCacheTimeout
+attributeId: 1.2.840.113556.1.4.779
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 1cb355a1-56d0-11d1-a9c6-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Admin-Context-Menu
+ldapDisplayName: adminContextMenu
+attributeId: 1.2.840.113556.1.4.614
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 553fd038-f32e-11d0-b0bc-00c04fd8dca6
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Admin-Count
+ldapDisplayName: adminCount
+attributeId: 1.2.840.113556.1.4.150
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: bf967918-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Admin-Description
+ldapDisplayName: adminDescription
+attributeId: 1.2.840.113556.1.2.226
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: bf967919-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 1024
+attributeSecurityGuid: 59ba2f42-79a2-11d0-9020-00c04fc2d3cf
+mapiID: 32842
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Admin-Display-Name
+ldapDisplayName: adminDisplayName
+attributeId: 1.2.840.113556.1.2.194
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: bf96791a-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 256
+mapiID: 32843
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Admin-Multiselect-Property-Pages
+ldapDisplayName: adminMultiselectPropertyPages
+attributeId: 1.2.840.113556.1.4.1690
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 18f9b67d-5ac6-4b3b-97db-d0a406afb7ba
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Admin-Property-Pages
+ldapDisplayName: adminPropertyPages
+attributeId: 1.2.840.113556.1.4.562
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 52458038-ca6a-11d0-afff-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Allowed-Attributes
+ldapDisplayName: allowedAttributes
+attributeId: 1.2.840.113556.1.4.913
+attributeSyntax: 2.5.5.2
+omSyntax: 6
+isSingleValued: FALSE
+schemaIdGuid: 9a7ad940-ca53-11d1-bbd0-0080c76670c0
+systemOnly: TRUE
+searchFlags: 0
+attributeSecurityGuid: e48d0154-bcf8-11d1-8702-00c04fb96050
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_IS_CONSTRUCTED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Allowed-Attributes-Effective
+ldapDisplayName: allowedAttributesEffective
+attributeId: 1.2.840.113556.1.4.914
+attributeSyntax: 2.5.5.2
+omSyntax: 6
+isSingleValued: FALSE
+schemaIdGuid: 9a7ad941-ca53-11d1-bbd0-0080c76670c0
+systemOnly: TRUE
+searchFlags: 0
+attributeSecurityGuid: e48d0154-bcf8-11d1-8702-00c04fb96050
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_IS_CONSTRUCTED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Allowed-Child-Classes
+ldapDisplayName: allowedChildClasses
+attributeId: 1.2.840.113556.1.4.911
+attributeSyntax: 2.5.5.2
+omSyntax: 6
+isSingleValued: FALSE
+schemaIdGuid: 9a7ad942-ca53-11d1-bbd0-0080c76670c0
+systemOnly: TRUE
+searchFlags: 0
+attributeSecurityGuid: e48d0154-bcf8-11d1-8702-00c04fb96050
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_IS_CONSTRUCTED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Allowed-Child-Classes-Effective
+ldapDisplayName: allowedChildClassesEffective
+attributeId: 1.2.840.113556.1.4.912
+attributeSyntax: 2.5.5.2
+omSyntax: 6
+isSingleValued: FALSE
+schemaIdGuid: 9a7ad943-ca53-11d1-bbd0-0080c76670c0
+systemOnly: TRUE
+searchFlags: 0
+attributeSecurityGuid: e48d0154-bcf8-11d1-8702-00c04fb96050
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_IS_CONSTRUCTED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ACS-Direction
+ldapDisplayName: aCSDirection
+attributeId: 1.2.840.113556.1.4.757
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 7f56127a-5301-11d1-a9c5-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Alt-Security-Identities
+ldapDisplayName: altSecurityIdentities
+attributeId: 1.2.840.113556.1.4.867
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 00fbf30c-91fe-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+searchFlags: fATTINDEX
+attributeSecurityGuid: e48d0154-bcf8-11d1-8702-00c04fb96050
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT |FLAG_ATTR_REQ_PARTIAL_SET_MEMBER
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ANR
+ldapDisplayName: aNR
+attributeId: 1.2.840.113556.1.4.1208
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 45b01500-c419-11d1-bbc9-0080c76670c0
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_IS_CONSTRUCTED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Application-Name
+ldapDisplayName: applicationName
+attributeId: 1.2.840.113556.1.4.218
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: dd712226-10e4-11d0-a05f-00aa006c33ed
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 64
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Applies-To
+ldapDisplayName: appliesTo
+attributeId: 1.2.840.113556.1.4.341
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 8297931d-86d3-11d0-afda-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 36
+rangeUpper: 36
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: App-Schema-Version
+ldapDisplayName: appSchemaVersion
+attributeId: 1.2.840.113556.1.4.848
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 96a7dd65-9118-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Asset-Number
+ldapDisplayName: assetNumber
+attributeId: 1.2.840.113556.1.4.283
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: ba305f75-47e3-11d0-a1a6-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Assistant
+ldapDisplayName: assistant
+attributeId: 1.2.840.113556.1.4.652
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: TRUE
+schemaIdGuid: 0296c11c-40da-11d1-a9c0-0000f80367c1
+systemOnly: FALSE
+searchFlags: fCOPY
+attributeSecurityGuid: 77b5b886-944a-11d1-aebd-0000f80367c1
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: associatedDomain
+ldapDisplayName: associatedDomain
+attributeId: 0.9.2342.19200300.100.1.37
+attributeSyntax: 2.5.5.5
+omSyntax: 22
+isSingleValued: FALSE
+schemaIdGuid: 3320fc38-c379-4c17-a510-1bdf6133c5da
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 256
+
+cn: associatedName
+ldapDisplayName: associatedName
+attributeId: 0.9.2342.19200300.100.1.38
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: f7fbfc45-85ab-42a4-a435-780e62f7858b
+systemOnly: FALSE
+searchFlags: 0
+
+cn: Assoc-NT-Account
+ldapDisplayName: assocNTAccount
+attributeId: 1.2.840.113556.1.4.1213
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 398f63c0-ca60-11d1-bbd1-0000f81f10c0
+systemOnly: FALSE
+searchFlags: 0
+
+cn: ACS-DSBM-DeadTime
+ldapDisplayName: aCSDSBMDeadTime
+attributeId: 1.2.840.113556.1.4.778
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 1cb355a0-56d0-11d1-a9c6-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: attributeCertificateAttribute
+ldapDisplayName: attributeCertificateAttribute
+attributeId: 2.5.4.58
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: FALSE
+schemaIdGuid: fa4693bb-7bc2-4cb9-81a8-c99c43b7905e
+systemOnly: FALSE
+searchFlags: 0
+
+cn: Attribute-Display-Names
+ldapDisplayName: attributeDisplayNames
+attributeId: 1.2.840.113556.1.4.748
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: cb843f80-48d9-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Attribute-ID
+ldapDisplayName: attributeID
+attributeId: 1.2.840.113556.1.2.30
+attributeSyntax: 2.5.5.2
+omSyntax: 6
+isSingleValued: TRUE
+schemaIdGuid: bf967922-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+searchFlags:fPRESERVEONDELETE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Attribute-Security-GUID
+ldapDisplayName: attributeSecurityGUID
+attributeId: 1.2.840.113556.1.4.149
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: bf967924-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 16
+rangeUpper: 16
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Attribute-Syntax
+ldapDisplayName: attributeSyntax
+attributeId: 1.2.840.113556.1.2.32
+attributeSyntax: 2.5.5.2
+omSyntax: 6
+isSingleValued: TRUE
+schemaIdGuid: bf967925-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+searchFlags:fPRESERVEONDELETE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Attribute-Types
+ldapDisplayName: attributeTypes
+attributeId: 2.5.21.5
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 9a7ad944-ca53-11d1-bbd0-0080c76670c0
+systemOnly: TRUE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_IS_CONSTRUCTED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: audio
+ldapDisplayName: audio
+attributeId: 0.9.2342.19200300.100.1.55
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: FALSE
+schemaIdGuid: d0e1d224-e1a0-42ce-a2da-793ba5244f35
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 250000
+showInAdvancedViewOnly: FALSE
+
+cn: Auditing-Policy
+ldapDisplayName: auditingPolicy
+attributeId: 1.2.840.113556.1.4.202
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 6da8a4fe-0e52-11d0-a286-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Authentication-Options
+ldapDisplayName: authenticationOptions
+attributeId: 1.2.840.113556.1.4.11
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: bf967928-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Authority-Revocation-List
+ldapDisplayName: authorityRevocationList
+attributeId: 2.5.4.38
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: FALSE
+schemaIdGuid: 1677578d-47f3-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 10485760
+mapiID: 32806
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ACS-DSBM-Priority
+ldapDisplayName: aCSDSBMPriority
+attributeId: 1.2.840.113556.1.4.776
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 1cb3559e-56d0-11d1-a9c6-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Auxiliary-Class
+ldapDisplayName: auxiliaryClass
+attributeId: 1.2.840.113556.1.2.351
+attributeSyntax: 2.5.5.2
+omSyntax: 6
+isSingleValued: FALSE
+schemaIdGuid: bf96792c-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Bad-Password-Time
+ldapDisplayName: badPasswordTime
+attributeId: 1.2.840.113556.1.4.49
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: bf96792d-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_NOT_REPLICATED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Bad-Pwd-Count
+ldapDisplayName: badPwdCount
+attributeId: 1.2.840.113556.1.4.12
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: bf96792e-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+attributeSecurityGuid: 5f202010-79a5-11d0-9020-00c04fc2d4cf
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_NOT_REPLICATED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Birth-Location
+ldapDisplayName: birthLocation
+attributeId: 1.2.840.113556.1.4.332
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 1f0075f9-7e40-11d0-afd6-00c04fd930c9
+systemOnly: FALSE
+searchFlags: fATTINDEX
+rangeLower: 32
+rangeUpper: 32
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: BootFile
+ldapDisplayName: bootFile
+attributeId: 1.3.6.1.1.1.1.24
+attributeSyntax: 2.5.5.5
+omSyntax: 22
+isSingleValued: FALSE
+schemaIdGuid: e3f3cb4e-0f20-42eb-9703-d2ff26e52667
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 10240
+
+cn: BootParameter
+ldapDisplayName: bootParameter
+attributeId: 1.3.6.1.1.1.1.23
+attributeSyntax: 2.5.5.5
+omSyntax: 22
+isSingleValued: FALSE
+schemaIdGuid: d72a0750-8c7c-416e-8714-e65f11e908be
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 10240
+
+cn: Bridgehead-Server-List-BL
+ldapDisplayName: bridgeheadServerListBL
+attributeId: 1.2.840.113556.1.4.820
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: d50c2cdb-8951-11d1-aebc-0000f80367c1
+systemOnly: TRUE
+searchFlags: 0
+linkID: 99
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_NOT_REPLICATED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Bridgehead-Transport-List
+ldapDisplayName: bridgeheadTransportList
+attributeId: 1.2.840.113556.1.4.819
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: d50c2cda-8951-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+linkID: 98
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: buildingName
+ldapDisplayName: buildingName
+attributeId: 0.9.2342.19200300.100.1.48
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: f87fa54b-b2c5-4fd7-88c0-daccb21d93c5
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 256
+
+cn: Builtin-Creation-Time
+ldapDisplayName: builtinCreationTime
+attributeId: 1.2.840.113556.1.4.13
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: bf96792f-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ACS-DSBM-Refresh
+ldapDisplayName: aCSDSBMRefresh
+attributeId: 1.2.840.113556.1.4.777
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 1cb3559f-56d0-11d1-a9c6-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Builtin-Modified-Count
+ldapDisplayName: builtinModifiedCount
+attributeId: 1.2.840.113556.1.4.14
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: bf967930-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Business-Category
+ldapDisplayName: businessCategory
+attributeId: 2.5.4.15
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: bf967931-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 128
+mapiID: 32855
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Bytes-Per-Minute
+ldapDisplayName: bytesPerMinute
+attributeId: 1.2.840.113556.1.4.284
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: ba305f76-47e3-11d0-a1a6-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Country-Name
+ldapDisplayName: c
+attributeId: 2.5.4.6
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: bf967945-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: fCOPY
+rangeLower: 1
+rangeUpper: 3
+attributeSecurityGuid: 77b5b886-944a-11d1-aebd-0000f80367c1
+mapiID: 32873
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT |FLAG_ATTR_REQ_PARTIAL_SET_MEMBER
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: CA-Certificate
+ldapDisplayName: cACertificate
+attributeId: 2.5.4.37
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: FALSE
+schemaIdGuid: bf967932-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 32768
+mapiID: 32771
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: CA-Certificate-DN
+ldapDisplayName: cACertificateDN
+attributeId: 1.2.840.113556.1.4.697
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 963d2740-48be-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: CA-Connect
+ldapDisplayName: cAConnect
+attributeId: 1.2.840.113556.1.4.687
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 963d2735-48be-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Canonical-Name
+ldapDisplayName: canonicalName
+attributeId: 1.2.840.113556.1.4.916
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 9a7ad945-ca53-11d1-bbd0-0080c76670c0
+systemOnly: TRUE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_IS_CONSTRUCTED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Can-Upgrade-Script
+ldapDisplayName: canUpgradeScript
+attributeId: 1.2.840.113556.1.4.815
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: d9e18314-8939-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: carLicense
+ldapDisplayName: carLicense
+attributeId: 2.16.840.1.113730.3.1.1
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: d4159c92-957d-4a87-8a67-8d2934e01649
+systemOnly: FALSE
+searchFlags: 0
+showInAdvancedViewOnly: FALSE
+
+cn: MacAddress
+ldapDisplayName: macAddress
+attributeId: 1.3.6.1.1.1.1.22
+attributeSyntax: 2.5.5.5
+omSyntax: 22
+isSingleValued: FALSE
+schemaIdGuid: e6a522dd-9770-43e1-89de-1de5044328f7
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 128
+
+cn: Manager
+ldapDisplayName: manager
+attributeId: 0.9.2342.19200300.100.1.10
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: TRUE
+schemaIdGuid: bf9679b5-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: fCOPY
+attributeSecurityGuid: e48d0154-bcf8-11d1-8702-00c04fb96050
+linkID: 42
+mapiID: 32773
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-SQL-NamedPipe
+ldapDisplayName: mS-SQL-NamedPipe
+attributeId: 1.2.840.113556.1.4.1374
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 7b91c840-ccee-11d2-9993-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-SQL-PublicationURL
+ldapDisplayName: mS-SQL-PublicationURL
+attributeId: 1.2.840.113556.1.4.1384
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: ae0c11b8-ccee-11d2-9993-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-SQL-Publisher
+ldapDisplayName: mS-SQL-Publisher
+attributeId: 1.2.840.113556.1.4.1402
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: c1676858-d34b-11d2-999a-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-SQL-RegisteredOwner
+ldapDisplayName: mS-SQL-RegisteredOwner
+attributeId: 1.2.840.113556.1.4.1364
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 48fd44ea-ccee-11d2-9993-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-SQL-ServiceAccount
+ldapDisplayName: mS-SQL-ServiceAccount
+attributeId: 1.2.840.113556.1.4.1369
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 64933a3e-ccee-11d2-9993-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-SQL-Size
+ldapDisplayName: mS-SQL-Size
+attributeId: 1.2.840.113556.1.4.1396
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: e9098084-ccee-11d2-9993-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-SQL-SortOrder
+ldapDisplayName: mS-SQL-SortOrder
+attributeId: 1.2.840.113556.1.4.1371
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 6ddc42c0-ccee-11d2-9993-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-SQL-SPX
+ldapDisplayName: mS-SQL-SPX
+attributeId: 1.2.840.113556.1.4.1376
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 86b08004-ccee-11d2-9993-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-SQL-Status
+ldapDisplayName: mS-SQL-Status
+attributeId: 1.2.840.113556.1.4.1380
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: 9a7d4770-ccee-11d2-9993-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-SQL-TCPIP
+ldapDisplayName: mS-SQL-TCPIP
+attributeId: 1.2.840.113556.1.4.1377
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 8ac263a6-ccee-11d2-9993-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MAPI-ID
+ldapDisplayName: mAPIID
+attributeId: 1.2.840.113556.1.2.49
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: bf9679b7-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+searchFlags: 0
+mapiID: 32974
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: MS-SQL-ThirdParty
+ldapDisplayName: mS-SQL-ThirdParty
+attributeId: 1.2.840.113556.1.4.1407
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: c4e311fc-d34b-11d2-999a-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-SQL-Type
+ldapDisplayName: mS-SQL-Type
+attributeId: 1.2.840.113556.1.4.1391
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: ca48eba8-ccee-11d2-9993-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-SQL-UnicodeSortOrder
+ldapDisplayName: mS-SQL-UnicodeSortOrder
+attributeId: 1.2.840.113556.1.4.1372
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 72dc918a-ccee-11d2-9993-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-SQL-Version
+ldapDisplayName: mS-SQL-Version
+attributeId: 1.2.840.113556.1.4.1388
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: c07cc1d0-ccee-11d2-9993-0000f87a57d4
+systemOnly: FALSE
+searchFlags: fATTINDEX
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-SQL-Vines
+ldapDisplayName: mS-SQL-Vines
+attributeId: 1.2.840.113556.1.4.1379
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 94c56394-ccee-11d2-9993-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-COM-DefaultPartitionLink
+ldapDisplayName: msCOM-DefaultPartitionLink
+attributeId: 1.2.840.113556.1.4.1427
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: TRUE
+schemaIdGuid: 998b10f7-aa1a-4364-b867-753d197fe670
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-COM-ObjectId
+ldapDisplayName: msCOM-ObjectId
+attributeId: 1.2.840.113556.1.4.1428
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 430f678b-889f-41f2-9843-203b5a65572f
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-COM-PartitionLink
+ldapDisplayName: msCOM-PartitionLink
+attributeId: 1.2.840.113556.1.4.1423
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: 09abac62-043f-4702-ac2b-6ca15eee5754
+systemOnly: FALSE
+searchFlags: 0
+linkID: 1040
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-COM-PartitionSetLink
+ldapDisplayName: msCOM-PartitionSetLink
+attributeId: 1.2.840.113556.1.4.1424
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: 67f121dc-7d02-4c7d-82f5-9ad4c950ac34
+systemOnly: TRUE
+searchFlags: 0
+linkID: 1041
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_NOT_REPLICATED
+
+cn: ms-COM-UserLink
+ldapDisplayName: msCOM-UserLink
+attributeId: 1.2.840.113556.1.4.1425
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: 9e6f3a4d-242c-4f37-b068-36b57f9fc852
+systemOnly: TRUE
+searchFlags: 0
+linkID: 1049
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_NOT_REPLICATED
+
+cn: Marshalled-Interface
+ldapDisplayName: marshalledInterface
+attributeId: 1.2.840.113556.1.4.72
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: FALSE
+schemaIdGuid: bf9679b9-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-COM-UserPartitionSetLink
+ldapDisplayName: msCOM-UserPartitionSetLink
+attributeId: 1.2.840.113556.1.4.1426
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: TRUE
+schemaIdGuid: 8e940c8a-e477-4367-b08d-ff2ff942dcd7
+systemOnly: FALSE
+searchFlags: 0
+linkID: 1048
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Mscope-Id
+ldapDisplayName: mscopeId
+attributeId: 1.2.840.113556.1.4.716
+attributeSyntax: 2.5.5.5
+omSyntax: 19
+isSingleValued: TRUE
+schemaIdGuid: 963d2751-48be-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-DFSR-CachePolicy
+ldapDisplayName: msDFSR-CachePolicy
+attributeId: 1.2.840.113556.1.6.13.3.29
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: db7a08e7-fc76-4569-a45f-f5ecb66a88b5
+searchFlags: 0
+
+cn: ms-DFSR-CommonStagingPath
+ldapDisplayName: msDFSR-CommonStagingPath
+attributeId: 1.2.840.113556.1.6.13.3.38
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 936eac41-d257-4bb9-bd55-f310a3cf09ad
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 32767
+
+cn: ms-DFSR-CommonStagingSizeInMb
+ldapDisplayName: msDFSR-CommonStagingSizeInMb
+attributeId: 1.2.840.113556.1.6.13.3.39
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: 135eb00e-4846-458b-8ea2-a37559afd405
+searchFlags: 0
+rangeLower: 0
+rangeUpper: -1
+
+cn: ms-DFSR-ComputerReference
+ldapDisplayName: msDFSR-ComputerReference
+attributeId: 1.2.840.113556.1.6.13.3.101
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: TRUE
+schemaIdGuid: 6c7b5785-3d21-41bf-8a8a-627941544d5a
+searchFlags: 0
+linkID: 2050
+
+cn: ms-DFSR-ComputerReferenceBL
+ldapDisplayName: msDFSR-ComputerReferenceBL
+attributeId: 1.2.840.113556.1.6.13.3.103
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: 5eb526d7-d71b-44ae-8cc6-95460052e6ac
+searchFlags: 0
+linkID: 2051
+systemFlags: FLAG_ATTR_NOT_REPLICATED
+
+cn: ms-DFSR-ConflictPath
+ldapDisplayName: msDFSR-ConflictPath
+attributeId: 1.2.840.113556.1.6.13.3.7
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 5cf0bcc8-60f7-4bff-bda6-aea0344eb151
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 32767
+
+cn: ms-DFSR-ConflictSizeInMb
+ldapDisplayName: msDFSR-ConflictSizeInMb
+attributeId: 1.2.840.113556.1.6.13.3.8
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: 9ad33fc9-aacf-4299-bb3e-d1fc6ea88e49
+searchFlags: 0
+rangeLower: 0
+rangeUpper: -1
+
+cn: ms-DFSR-ContentSetGuid
+ldapDisplayName: msDFSR-ContentSetGuid
+attributeId: 1.2.840.113556.1.6.13.3.18
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 1035a8e1-67a8-4c21-b7bb-031cdf99d7a0
+searchFlags: 0
+rangeLower: 16
+rangeUpper: 16
+
+cn: Mastered-By
+ldapDisplayName: masteredBy
+attributeId: 1.2.840.113556.1.4.1409
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: e48e64e0-12c9-11d3-9102-00c04fd91ab1
+systemOnly: TRUE
+searchFlags: 0
+linkID: 77
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_NOT_REPLICATED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DFSR-DefaultCompressionExclusionFilter
+ldapDisplayName: msDFSR-DefaultCompressionExclusionFilter
+attributeId: 1.2.840.113556.1.6.13.3.34
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 87811bd5-cd8b-45cb-9f5d-980f3a9e0c97
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 32767
+
+cn: ms-DFSR-DeletedPath
+ldapDisplayName: msDFSR-DeletedPath
+attributeId: 1.2.840.113556.1.6.13.3.26
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 817cf0b8-db95-4914-b833-5a079ef65764
+searchFlags: 0
+rangeUpper: 32767
+
+cn: ms-DFSR-DeletedSizeInMb
+ldapDisplayName: msDFSR-DeletedSizeInMb
+attributeId: 1.2.840.113556.1.6.13.3.27
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: 53ed9ad1-9975-41f4-83f5-0c061a12553a
+searchFlags: 0
+rangeUpper: -1
+
+cn: ms-DFSR-DfsLinkTarget
+ldapDisplayName: msDFSR-DfsLinkTarget
+attributeId: 1.2.840.113556.1.6.13.3.24
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: f7b85ba9-3bf9-428f-aab4-2eee6d56f063
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 32767
+
+cn: ms-DFSR-DfsPath
+ldapDisplayName: msDFSR-DfsPath
+attributeId: 1.2.840.113556.1.6.13.3.21
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 2cc903e2-398c-443b-ac86-ff6b01eac7ba
+searchFlags: fATTINDEX
+rangeLower: 0
+rangeUpper: 32767
+
+cn: ms-DFSR-DirectoryFilter
+ldapDisplayName: msDFSR-DirectoryFilter
+attributeId: 1.2.840.113556.1.6.13.3.13
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 93c7b477-1f2e-4b40-b7bf-007e8d038ccf
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 32767
+
+cn: ms-DFSR-DisablePacketPrivacy
+ldapDisplayName: msDFSR-DisablePacketPrivacy
+attributeId: 1.2.840.113556.1.6.13.3.32
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: 6a84ede5-741e-43fd-9dd6-aa0f61578621
+searchFlags: 0
+
+cn: ms-DFSR-Enabled
+ldapDisplayName: msDFSR-Enabled
+attributeId: 1.2.840.113556.1.6.13.3.9
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: 03726ae7-8e7d-4446-8aae-a91657c00993
+searchFlags: 0
+
+cn: ms-DFSR-Extension
+ldapDisplayName: msDFSR-Extension
+attributeId: 1.2.840.113556.1.6.13.3.2
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 78f011ec-a766-4b19-adcf-7b81ed781a4d
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 65536
+
+cn: ms-DFSR-FileFilter
+ldapDisplayName: msDFSR-FileFilter
+attributeId: 1.2.840.113556.1.6.13.3.12
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: d68270ac-a5dc-4841-a6ac-cd68be38c181
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 32767
+
+cn: Max-Pwd-Age
+ldapDisplayName: maxPwdAge
+attributeId: 1.2.840.113556.1.4.74
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: bf9679bb-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+attributeSecurityGuid: c7407360-20bf-11d0-a768-00aa006e0529
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DFSR-Flags
+ldapDisplayName: msDFSR-Flags
+attributeId: 1.2.840.113556.1.6.13.3.16
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: fe515695-3f61-45c8-9bfa-19c148c57b09
+searchFlags: 0
+
+cn: ms-DFSR-Keywords
+ldapDisplayName: msDFSR-Keywords
+attributeId: 1.2.840.113556.1.6.13.3.15
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 048b4692-6227-4b67-a074-c4437083e14b
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 32767
+
+cn: ms-DFSR-MaxAgeInCacheInMin
+ldapDisplayName: msDFSR-MaxAgeInCacheInMin
+attributeId: 1.2.840.113556.1.6.13.3.31
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 2ab0e48d-ac4e-4afc-83e5-a34240db6198
+searchFlags: 0
+rangeUpper: 2147483647
+
+cn: ms-DFSR-MemberReference
+ldapDisplayName: msDFSR-MemberReference
+attributeId: 1.2.840.113556.1.6.13.3.100
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: TRUE
+schemaIdGuid: 261337aa-f1c3-44b2-bbea-c88d49e6f0c7
+searchFlags: 0
+linkID: 2052
+
+cn: ms-DFSR-MemberReferenceBL
+ldapDisplayName: msDFSR-MemberReferenceBL
+attributeId: 1.2.840.113556.1.6.13.3.102
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: adde62c6-1880-41ed-bd3c-30b7d25e14f0
+searchFlags: 0
+linkID: 2053
+systemFlags: FLAG_ATTR_NOT_REPLICATED
+
+cn: ms-DFSR-MinDurationCacheInMin
+ldapDisplayName: msDFSR-MinDurationCacheInMin
+attributeId: 1.2.840.113556.1.6.13.3.30
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 4c5d607a-ce49-444a-9862-82a95f5d1fcc
+searchFlags: 0
+rangeUpper: 2147483647
+
+cn: ms-DFSR-OnDemandExclusionDirectoryFilter
+ldapDisplayName: msDFSR-OnDemandExclusionDirectoryFilter
+attributeId: 1.2.840.113556.1.6.13.3.36
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 7d523aff-9012-49b2-9925-f922a0018656
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 32767
+
+cn: ms-DFSR-OnDemandExclusionFileFilter
+ldapDisplayName: msDFSR-OnDemandExclusionFileFilter
+attributeId: 1.2.840.113556.1.6.13.3.35
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: a68359dc-a581-4ee6-9015-5382c60f0fb4
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 32767
+
+cn: ms-DFSR-Options
+ldapDisplayName: msDFSR-Options
+attributeId: 1.2.840.113556.1.6.13.3.17
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: d6d67084-c720-417d-8647-b696237a114c
+searchFlags: 0
+
+cn: ms-DFSR-Options2
+ldapDisplayName: msDFSR-Options2
+attributeId: 1.2.840.113556.1.6.13.3.37
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 11e24318-4ca6-4f49-9afe-e5eb1afa3473
+searchFlags: 0
+
+cn: Max-Renew-Age
+ldapDisplayName: maxRenewAge
+attributeId: 1.2.840.113556.1.4.75
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: bf9679bc-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DFSR-Priority
+ldapDisplayName: msDFSR-Priority
+attributeId: 1.2.840.113556.1.6.13.3.25
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: eb20e7d6-32ad-42de-b141-16ad2631b01b
+searchFlags: 0
+
+cn: ms-DFSR-RdcEnabled
+ldapDisplayName: msDFSR-RdcEnabled
+attributeId: 1.2.840.113556.1.6.13.3.19
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: e3b44e05-f4a7-4078-a730-f48670a743f8
+searchFlags: 0
+
+cn: ms-DFSR-RdcMinFileSizeInKb
+ldapDisplayName: msDFSR-RdcMinFileSizeInKb
+attributeId: 1.2.840.113556.1.6.13.3.20
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: f402a330-ace5-4dc1-8cc9-74d900bf8ae0
+searchFlags: 0
+rangeLower: 0
+rangeUpper: -1
+
+cn: ms-DFSR-ReadOnly
+ldapDisplayName: msDFSR-ReadOnly
+attributeId: 1.2.840.113556.1.6.13.3.28
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: 5ac48021-e447-46e7-9d23-92c0c6a90dfb
+searchFlags: 0
+
+cn: ms-DFSR-StagingCleanupTriggerInPercent
+ldapDisplayName: msDFSR-StagingCleanupTriggerInPercent
+attributeId: 1.2.840.113556.1.6.13.3.40
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: d64b9c23-e1fa-467b-b317-6964d744d633
+searchFlags: 0
+
+cn: ms-DFSR-ReplicationGroupGuid
+ldapDisplayName: msDFSR-ReplicationGroupGuid
+attributeId: 1.2.840.113556.1.6.13.3.23
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 2dad8796-7619-4ff8-966e-0a5cc67b287f
+searchFlags: fATTINDEX
+rangeLower: 16
+rangeUpper: 16
+
+cn: ms-DFSR-ReplicationGroupType
+ldapDisplayName: msDFSR-ReplicationGroupType
+attributeId: 1.2.840.113556.1.6.13.3.10
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: eeed0fc8-1001-45ed-80cc-bbf744930720
+searchFlags: 0
+
+cn: ms-DFSR-RootFence
+ldapDisplayName: msDFSR-RootFence
+attributeId: 1.2.840.113556.1.6.13.3.22
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 51928e94-2cd8-4abe-b552-e50412444370
+searchFlags: 0
+
+cn: ms-DFSR-RootPath
+ldapDisplayName: msDFSR-RootPath
+attributeId: 1.2.840.113556.1.6.13.3.3
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: d7d5e8c1-e61f-464f-9fcf-20bbe0a2ec54
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 32767
+
+cn: ms-DFSR-RootSizeInMb
+ldapDisplayName: msDFSR-RootSizeInMb
+attributeId: 1.2.840.113556.1.6.13.3.4
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: 90b769ac-4413-43cf-ad7a-867142e740a3
+searchFlags: 0
+rangeLower: 0
+rangeUpper: -1
+
+cn: Max-Storage
+ldapDisplayName: maxStorage
+attributeId: 1.2.840.113556.1.4.76
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: bf9679bd-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: fCOPY
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-DFSR-Schedule
+ldapDisplayName: msDFSR-Schedule
+attributeId: 1.2.840.113556.1.6.13.3.14
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 4699f15f-a71f-48e2-9ff5-5897c0759205
+searchFlags: 0
+rangeLower: 336
+rangeUpper: 336
+
+cn: ms-DFSR-StagingPath
+ldapDisplayName: msDFSR-StagingPath
+attributeId: 1.2.840.113556.1.6.13.3.5
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 86b9a69e-f0a6-405d-99bb-77d977992c2a
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 32767
+
+cn: ms-DFSR-StagingSizeInMb
+ldapDisplayName: msDFSR-StagingSizeInMb
+attributeId: 1.2.840.113556.1.6.13.3.6
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: 250a8f20-f6fc-4559-ae65-e4b24c67aebe
+searchFlags: 0
+rangeLower: 0
+rangeUpper: -1
+
+cn: ms-DFSR-TombstoneExpiryInMin
+ldapDisplayName: msDFSR-TombstoneExpiryInMin
+attributeId: 1.2.840.113556.1.6.13.3.11
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 23e35d4c-e324-4861-a22f-e199140dae00
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 2147483647
+
+cn: ms-DFSR-Version
+ldapDisplayName: msDFSR-Version
+attributeId: 1.2.840.113556.1.6.13.3.1
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 1a861408-38c3-49ea-ba75-85481a77c655
+searchFlags: 0
+rangeUpper: 256
+
+cn: MS-DRM-Identity-Certificate
+ldapDisplayName: msDRM-IdentityCertificate
+attributeId: 1.2.840.113556.1.4.1843
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: FALSE
+schemaIdGuid: e85e1204-3434-41ad-9b56-e2901228fff0
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 10240
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-DS-Additional-Dns-Host-Name
+ldapDisplayName: msDS-AdditionalDnsHostName
+attributeId: 1.2.840.113556.1.4.1717
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 80863791-dbe9-4eb8-837e-7f0ab55d9ac7
+systemOnly: TRUE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 2048
+attributeSecurityGuid: 72e39547-7b18-11d1-adef-00c04fd8d5cd
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Additional-Sam-Account-Name
+ldapDisplayName: msDS-AdditionalSamAccountName
+attributeId: 1.2.840.113556.1.4.1718
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 975571df-a4d5-429a-9f59-cdc6581d91e6
+systemOnly: TRUE
+searchFlags: fPRESERVEONDELETE| fANR | fATTINDEX
+rangeLower: 0
+rangeUpper: 256
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Allowed-DNS-Suffixes
+ldapDisplayName: msDS-AllowedDNSSuffixes
+attributeId: 1.2.840.113556.1.4.1710
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 8469441b-9ac4-4e45-8205-bd219dbf672d
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 2048
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Allowed-To-Delegate-To
+ldapDisplayName: msDS-AllowedToDelegateTo
+attributeId: 1.2.840.113556.1.4.1787
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 800d94d7-b7a1-42a1-b14d-7cae1423d07f
+systemOnly: FALSE
+searchFlags: 0
+attributeSecurityGuid: e48d0154-bcf8-11d1-8702-00c04fb96050
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Max-Ticket-Age
+ldapDisplayName: maxTicketAge
+attributeId: 1.2.840.113556.1.4.77
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: bf9679be-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: MS-DS-All-Users-Trust-Quota
+ldapDisplayName: msDS-AllUsersTrustQuota
+attributeId: 1.2.840.113556.1.4.1789
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: d3aa4a5c-4e03-4810-97aa-2b339e7a434b
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Approx-Immed-Subordinates
+ldapDisplayName: msDS-Approx-Immed-Subordinates
+attributeId: 1.2.840.113556.1.4.1669
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: e185d243-f6ce-4adb-b496-b0c005d7823c
+systemOnly: TRUE
+searchFlags: 0
+attributeSecurityGuid: e48d0154-bcf8-11d1-8702-00c04fb96050
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_IS_CONSTRUCTED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-AuthenticatedAt-DC
+ldapDisplayName: msDS-AuthenticatedAtDC
+attributeId: 1.2.840.113556.1.4.1958
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: 3e1ee99c-6604-4489-89d9-84798a89515a
+systemOnly: FALSE
+searchFlags: 0
+linkID: 2112
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-AuthenticatedTo-Accountlist
+ldapDisplayName: msDS-AuthenticatedToAccountlist
+attributeId: 1.2.840.113556.1.4.1957
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: e8b2c971-a6df-47bc-8d6f-62770d527aa5
+systemOnly: TRUE
+searchFlags: 0
+linkID: 2113
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_NOT_REPLICATED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Auxiliary-Classes
+ldapDisplayName: msDS-Auxiliary-Classes
+attributeId: 1.2.840.113556.1.4.1458
+attributeSyntax: 2.5.5.2
+omSyntax: 6
+isSingleValued: FALSE
+schemaIdGuid: c4af1073-ee50-4be0-b8c0-89a41fe99abe
+systemOnly: TRUE
+searchFlags: fPRESERVEONDELETE
+attributeSecurityGuid: e48d0154-bcf8-11d1-8702-00c04fb96050
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_IS_CONSTRUCTED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Az-Application-Data
+ldapDisplayName: msDS-AzApplicationData
+attributeId: 1.2.840.113556.1.4.1819
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 503fc3e8-1cc6-461a-99a3-9eee04f402a7
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-DS-Az-Application-Name
+ldapDisplayName: msDS-AzApplicationName
+attributeId: 1.2.840.113556.1.4.1798
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: db5b0728-6208-4876-83b7-95d3e5695275
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 512
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-DS-Az-Application-Version
+ldapDisplayName: msDS-AzApplicationVersion
+attributeId: 1.2.840.113556.1.4.1817
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 7184a120-3ac4-47ae-848f-fe0ab20784d4
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-DS-Az-Biz-Rule
+ldapDisplayName: msDS-AzBizRule
+attributeId: 1.2.840.113556.1.4.1801
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 33d41ea8-c0c9-4c92-9494-f104878413fd
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 65536
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-DS-Az-Biz-Rule-Language
+ldapDisplayName: msDS-AzBizRuleLanguage
+attributeId: 1.2.840.113556.1.4.1802
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 52994b56-0e6c-4e07-aa5c-ef9d7f5a0e25
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 64
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: May-Contain
+ldapDisplayName: mayContain
+attributeId: 1.2.840.113556.1.2.25
+attributeSyntax: 2.5.5.2
+omSyntax: 6
+isSingleValued: FALSE
+schemaIdGuid: bf9679bf-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Az-Class-ID
+ldapDisplayName: msDS-AzClassId
+attributeId: 1.2.840.113556.1.4.1816
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 013a7277-5c2d-49ef-a7de-b765b36a3f6f
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 40
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-DS-Az-Domain-Timeout
+ldapDisplayName: msDS-AzDomainTimeout
+attributeId: 1.2.840.113556.1.4.1795
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 6448f56a-ca70-4e2e-b0af-d20e4ce653d0
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-DS-Az-Generate-Audits
+ldapDisplayName: msDS-AzGenerateAudits
+attributeId: 1.2.840.113556.1.4.1805
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: f90abab0-186c-4418-bb85-88447c87222a
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-DS-Az-Generic-Data
+ldapDisplayName: msDS-AzGenericData
+attributeId: 1.2.840.113556.1.4.1950
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: b5f7e349-7a5b-407c-a334-a31c3f538b98
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 65536
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-DS-Az-Last-Imported-Biz-Rule-Path
+ldapDisplayName: msDS-AzLastImportedBizRulePath
+attributeId: 1.2.840.113556.1.4.1803
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 665acb5c-bb92-4dbc-8c59-b3638eab09b3
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 65536
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-DS-Az-LDAP-Query
+ldapDisplayName: msDS-AzLDAPQuery
+attributeId: 1.2.840.113556.1.4.1792
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 5e53368b-fc94-45c8-9d7d-daf31ee7112d
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 4096
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Az-Major-Version
+ldapDisplayName: msDS-AzMajorVersion
+attributeId: 1.2.840.113556.1.4.1824
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: cfb9adb7-c4b7-4059-9568-1ed9db6b7248
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-DS-Az-Minor-Version
+ldapDisplayName: msDS-AzMinorVersion
+attributeId: 1.2.840.113556.1.4.1825
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: ee85ed93-b209-4788-8165-e702f51bfbf3
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-DS-Az-Object-Guid
+ldapDisplayName: msDS-AzObjectGuid
+attributeId: 1.2.840.113556.1.4.1949
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 8491e548-6c38-4365-a732-af041569b02c
+systemOnly: TRUE
+searchFlags: fATTINDEX
+rangeLower: 16
+rangeUpper: 16
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-DS-Az-Operation-ID
+ldapDisplayName: msDS-AzOperationID
+attributeId: 1.2.840.113556.1.4.1800
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: a5f3b553-5d76-4cbe-ba3f-4312152cab18
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: meetingAdvertiseScope
+ldapDisplayName: meetingAdvertiseScope
+attributeId: 1.2.840.113556.1.4.582
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 11b6cc8b-48c4-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-DS-Az-Scope-Name
+ldapDisplayName: msDS-AzScopeName
+attributeId: 1.2.840.113556.1.4.1799
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 515a6b06-2617-4173-8099-d5605df043c6
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 65536
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-DS-Az-Script-Engine-Cache-Max
+ldapDisplayName: msDS-AzScriptEngineCacheMax
+attributeId: 1.2.840.113556.1.4.1796
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 2629f66a-1f95-4bf3-a296-8e9d7b9e30c8
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-DS-Az-Script-Timeout
+ldapDisplayName: msDS-AzScriptTimeout
+attributeId: 1.2.840.113556.1.4.1797
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 87d0fb41-2c8b-41f6-b972-11fdfd50d6b0
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-DS-Az-Task-Is-Role-Definition
+ldapDisplayName: msDS-AzTaskIsRoleDefinition
+attributeId: 1.2.840.113556.1.4.1818
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: 7b078544-6c82-4fe9-872f-ff48ad2b2e26
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-DS-Behavior-Version
+ldapDisplayName: msDS-Behavior-Version
+attributeId: 1.2.840.113556.1.4.1459
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: d31a8757-2447-4545-8081-3bb610cacbf2
+systemOnly: TRUE
+searchFlags: 0
+rangeLower: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-BridgeHead-Servers-Used
+ldapDisplayName: msDS-BridgeHeadServersUsed
+attributeId: 1.2.840.113556.1.4.2049
+attributeSyntax: 2.5.5.7
+omSyntax: 127
+omObjectClass: 1.2.840.113556.1.1.1.11
+linkID: 2160
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+schemaIdGuid: 3ced1465-7b71-2541-8780-1e1ea6243a82
+searchFlags: 0
+systemFlags: FLAG_ATTR_NOT_REPLICATED | FLAG_ATTR_IS_OPERATIONAL | FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Byte-Array
+ldapDisplayName: msDS-ByteArray
+attributeId: 1.2.840.113556.1.4.1831
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: FALSE
+schemaIdGuid: f0d8972e-dd5b-40e5-a51d-044c7c17ece7
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 1000000
+
+cn: ms-DS-Cached-Membership
+ldapDisplayName: msDS-Cached-Membership
+attributeId: 1.2.840.113556.1.4.1441
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 69cab008-cdd4-4bc9-bab8-0ff37efe1b20
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_NOT_REPLICATED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Cached-Membership-Time-Stamp
+ldapDisplayName: msDS-Cached-Membership-Time-Stamp
+attributeId: 1.2.840.113556.1.4.1442
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: 3566bf1f-beee-4dcb-8abe-ef89fcfec6c1
+systemOnly: FALSE
+searchFlags: fATTINDEX
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_NOT_REPLICATED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Date-Time
+ldapDisplayName: msDS-DateTime
+attributeId: 1.2.840.113556.1.4.1832
+attributeSyntax: 2.5.5.11
+omSyntax: 24
+isSingleValued: FALSE
+schemaIdGuid: 234fcbd8-fb52-4908-a328-fd9f6e58e403
+systemOnly: FALSE
+searchFlags: 0
+
+cn: ms-DS-Default-Quota
+ldapDisplayName: msDS-DefaultQuota
+attributeId: 1.2.840.113556.1.4.1846
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 6818f726-674b-441b-8a3a-f40596374cea
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Machine-Architecture
+ldapDisplayName: machineArchitecture
+attributeId: 1.2.840.113556.1.4.68
+attributeSyntax: 2.5.5.9
+omSyntax: 10
+isSingleValued: FALSE
+schemaIdGuid: bf9679af-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: meetingApplication
+ldapDisplayName: meetingApplication
+attributeId: 1.2.840.113556.1.4.573
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 11b6cc83-48c4-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-DS-DnsRootAlias
+ldapDisplayName: msDS-DnsRootAlias
+attributeId: 1.2.840.113556.1.4.1719
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 2143acca-eead-4d29-b591-85fa49ce9173
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 255
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Entry-Time-To-Die
+ldapDisplayName: msDS-Entry-Time-To-Die
+attributeId: 1.2.840.113556.1.4.1622
+attributeSyntax: 2.5.5.11
+omSyntax: 24
+isSingleValued: TRUE
+schemaIdGuid: e1e9bad7-c6dd-4101-a843-794cec85b038
+systemOnly: TRUE
+searchFlags: fPRESERVEONDELETE | fATTINDEX
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_IS_OPERATIONAL
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-ExecuteScriptPassword
+ldapDisplayName: msDS-ExecuteScriptPassword
+attributeId: 1.2.840.113556.1.4.1783
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 9d054a5a-d187-46c1-9d85-42dfc44a56dd
+systemOnly: TRUE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 64
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_NOT_REPLICATED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-External-Key
+ldapDisplayName: msDS-ExternalKey
+attributeId: 1.2.840.113556.1.4.1833
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: b92fd528-38ac-40d4-818d-0433380837c1
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 10000
+
+cn: ms-DS-External-Store
+ldapDisplayName: msDS-ExternalStore
+attributeId: 1.2.840.113556.1.4.1834
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 604877cd-9cdb-47c7-b03d-3daadb044910
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 10000
+
+cn: ms-DS-Failed-Interactive-Logon-Count
+ldapDisplayName: msDS-FailedInteractiveLogonCount
+attributeId: 1.2.840.113556.1.4.1972
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: dc3ca86f-70ad-4960-8425-a4d6313d93dd
+systemOnly: TRUE
+searchFlags: 0
+attributeSecurityGuid: 77b5b886-944a-11d1-aebd-0000f80367c1
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Failed-Interactive-Logon-Count-At-Last-Successful-Logon
+ldapDisplayName: msDS-FailedInteractiveLogonCountAtLastSuccessfulLogon
+attributeId: 1.2.840.113556.1.4.1973
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: c5d234e5-644a-4403-a665-e26e0aef5e98
+systemOnly: TRUE
+searchFlags: 0
+attributeSecurityGuid: 77b5b886-944a-11d1-aebd-0000f80367c1
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Filter-Containers
+ldapDisplayName: msDS-FilterContainers
+attributeId: 1.2.840.113556.1.4.1703
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: fb00dcdf-ac37-483a-9c12-ac53a6603033
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 64
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-DS-HAB-Seniority-Index
+ldapDisplayName: msDS-HABSeniorityIndex
+attributeId: 1.2.840.113556.1.4.1997
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: def449f1-fd3b-4045-98cf-d9658da788b5
+systemOnly: FALSE
+searchFlags: fATTINDEX
+attributeSecurityGuid: e48d0154-bcf8-11d1-8702-00c04fb96050
+mapiID: 36000
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-DS-Has-Domain-NCs
+ldapDisplayName: msDS-HasDomainNCs
+attributeId: 1.2.840.113556.1.4.1820
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: 6f17e347-a842-4498-b8b3-15e007da4fed
+systemOnly: TRUE
+searchFlags: 0
+rangeLower: 4
+rangeUpper: 4
+linkID: 2026
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: meetingBandwidth
+ldapDisplayName: meetingBandwidth
+attributeId: 1.2.840.113556.1.4.589
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: FALSE
+schemaIdGuid: 11b6cc92-48c4-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-DS-Has-Full-Replica-NCs
+ldapDisplayName: msDS-hasFullReplicaNCs
+attributeId: 1.2.840.113556.1.4.1925
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: 1d3c2d18-42d0-4868-99fe-0eca1e6fa9f3
+systemOnly: TRUE
+searchFlags: 0
+linkID: 2104
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Has-Instantiated-NCs
+ldapDisplayName: msDS-HasInstantiatedNCs
+attributeId: 1.2.840.113556.1.4.1709
+attributeSyntax: 2.5.5.7
+omSyntax: 127
+omObjectClass: 1.2.840.113556.1.1.1.11
+isSingleValued: FALSE
+schemaIdGuid: 11e9a5bc-4517-4049-af9c-51554fb0fc09
+systemOnly: TRUE
+searchFlags: 0
+rangeLower: 4
+rangeUpper: 4
+linkID: 2002
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Has-Master-NCs
+ldapDisplayName: msDS-hasMasterNCs
+attributeId: 1.2.840.113556.1.4.1836
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: ae2de0e2-59d7-4d47-8d47-ed4dfe4357ad
+systemOnly: TRUE
+searchFlags: 0
+linkID: 2036
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Integer
+ldapDisplayName: msDS-Integer
+attributeId: 1.2.840.113556.1.4.1835
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: FALSE
+schemaIdGuid: 7bc64cea-c04e-4318-b102-3e0729371a65
+systemOnly: FALSE
+searchFlags: 0
+
+cn: ms-DS-IntId
+ldapDisplayName: msDS-IntId
+attributeId: 1.2.840.113556.1.4.1716
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: bc60096a-1b47-4b30-8877-602c93f56532
+systemOnly: TRUE
+searchFlags: fPRESERVEONDELETE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Is-Domain-For
+ldapDisplayName: msDS-IsDomainFor
+attributeId: 1.2.840.113556.1.4.1933
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: ff155a2a-44e5-4de0-8318-13a58988de4f
+systemOnly: TRUE
+searchFlags: 0
+linkID: 2027
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_NOT_REPLICATED
+
+cn: ms-DS-Is-Full-Replica-For
+ldapDisplayName: msDS-IsFullReplicaFor
+attributeId: 1.2.840.113556.1.4.1932
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: c8bc72e0-a6b4-48f0-94a5-fd76a88c9987
+systemOnly: TRUE
+searchFlags: 0
+linkID: 2105
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_NOT_REPLICATED
+
+cn: ms-DS-isGC
+ldapDisplayName: msDS-isGC
+attributeId: 1.2.840.113556.1.4.1959
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: 1df5cf33-0fe5-499e-90e1-e94b42718a46
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_IS_CONSTRUCTED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Is-Partial-Replica-For
+ldapDisplayName: msDS-IsPartialReplicaFor
+attributeId: 1.2.840.113556.1.4.1934
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: 37c94ff6-c6d4-498f-b2f9-c6f7f8647809
+systemOnly: TRUE
+searchFlags: 0
+linkID: 75
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_NOT_REPLICATED
+
+cn: ms-DS-isRODC
+ldapDisplayName: msDS-isRODC
+attributeId: 1.2.840.113556.1.4.1960
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: a8e8aa23-3e67-4af1-9d7a-2f1a1d633ac9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_IS_CONSTRUCTED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: meetingBlob
+ldapDisplayName: meetingBlob
+attributeId: 1.2.840.113556.1.4.590
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 11b6cc93-48c4-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-DS-KeyVersionNumber
+ldapDisplayName: msDS-KeyVersionNumber
+attributeId: 1.2.840.113556.1.4.1782
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: c523e9c0-33b5-4ac8-8923-b57b927f42f6
+systemOnly: TRUE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_IS_CONSTRUCTED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-KrbTgt-Link
+ldapDisplayName: msDS-KrbTgtLink
+attributeId: 1.2.840.113556.1.4.1923
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: TRUE
+schemaIdGuid: 778ff5c9-6f4e-4b74-856a-d68383313910
+systemOnly: FALSE
+searchFlags: 0
+linkID: 2100
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-KrbTgt-Link-BL
+ldapDisplayName: msDS-KrbTgtLinkBl
+attributeId: 1.2.840.113556.1.4.1931
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: 5dd68c41-bfdf-438b-9b5d-39d9618bf260
+systemOnly: TRUE
+searchFlags: 0
+linkID: 2101
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_NOT_REPLICATED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Last-Failed-Interactive-Logon-Time
+ldapDisplayName: msDS-LastFailedInteractiveLogonTime
+attributeId: 1.2.840.113556.1.4.1971
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: c7e7dafa-10c3-4b8b-9acd-54f11063742e
+systemOnly: TRUE
+searchFlags: 0
+attributeSecurityGuid: 77b5b886-944a-11d1-aebd-0000f80367c1
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Last-Successful-Interactive-Logon-Time
+ldapDisplayName: msDS-LastSuccessfulInteractiveLogonTime
+attributeId: 1.2.840.113556.1.4.1970
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: 011929e6-8b5d-4258-b64a-00b0b4949747
+systemOnly: TRUE
+searchFlags: 0
+attributeSecurityGuid: 77b5b886-944a-11d1-aebd-0000f80367c1
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Lockout-Duration
+ldapDisplayName: msDS-LockoutDuration
+attributeId: 1.2.840.113556.1.4.2018
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+schemaIdGuid: 421f889a-472e-4fe4-8eb9-e1d0bc6071b2
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Lockout-Threshold
+ldapDisplayName: msDS-LockoutThreshold
+attributeId: 1.2.840.113556.1.4.2019
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 65535
+schemaIdGuid: b8c8c35e-4a19-4a95-99d0-69fe4446286f
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Lockout-Observation-Window
+ldapDisplayName: msDS-LockoutObservationWindow
+attributeId: 1.2.840.113556.1.4.2017
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+schemaIdGuid: b05bda89-76af-468a-b892-1be55558ecc8
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Logon-Time-Sync-Interval
+ldapDisplayName: msDS-LogonTimeSyncInterval
+attributeId: 1.2.840.113556.1.4.1784
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: ad7940f8-e43a-4a42-83bc-d688e59ea605
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Mastered-By
+ldapDisplayName: msDs-masteredBy
+attributeId: 1.2.840.113556.1.4.1837
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: 60234769-4819-4615-a1b2-49d2f119acb5
+systemOnly: TRUE
+searchFlags: 0
+linkID: 2037
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_NOT_REPLICATED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: meetingContactInfo
+ldapDisplayName: meetingContactInfo
+attributeId: 1.2.840.113556.1.4.578
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 11b6cc87-48c4-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-DS-Maximum-Password-Age
+ldapDisplayName: msDS-MaximumPasswordAge
+attributeId: 1.2.840.113556.1.4.2011
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+schemaIdGuid: fdd337f5-4999-4fce-b252-8ff9c9b43875
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Max-Values
+ldapDisplayName: msDs-MaxValues
+attributeId: 1.2.840.113556.1.4.1842
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: d1e169a4-ebe9-49bf-8fcb-8aef3874592d
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-DS-Members-For-Az-Role
+ldapDisplayName: msDS-MembersForAzRole
+attributeId: 1.2.840.113556.1.4.1806
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: cbf7e6cd-85a4-4314-8939-8bfe80597835
+systemOnly: FALSE
+searchFlags: 0
+linkID: 2016
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Members-For-Az-Role-BL
+ldapDisplayName: msDS-MembersForAzRoleBL
+attributeId: 1.2.840.113556.1.4.1807
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: ececcd20-a7e0-4688-9ccf-02ece5e287f5
+systemOnly: TRUE
+searchFlags: 0
+linkID: 2017
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_NOT_REPLICATED
+
+cn: ms-DS-Minimum-Password-Age
+ldapDisplayName: msDS-MinimumPasswordAge
+attributeId: 1.2.840.113556.1.4.2012
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+schemaIdGuid: 2a74f878-4d9c-49f9-97b3-6767d1cbd9a3
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Minimum-Password-Length
+ldapDisplayName: msDS-MinimumPasswordLength
+attributeId: 1.2.840.113556.1.4.2013
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 255
+schemaIdGuid: b21b3439-4c3a-441c-bb5f-08f20e9b315e
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-NC-Replica-Locations
+ldapDisplayName: msDS-NC-Replica-Locations
+attributeId: 1.2.840.113556.1.4.1661
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: 97de9615-b537-46bc-ac0f-10720f3909f3
+systemOnly: FALSE
+searchFlags: 0
+linkID: 1044
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-NC-RO-Replica-Locations
+ldapDisplayName: msDS-NC-RO-Replica-Locations
+attributeId: 1.2.840.113556.1.4.1967
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: 3df793df-9858-4417-a701-735a1ecebf74
+systemOnly: FALSE
+searchFlags: 0
+linkID: 2114
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-NC-RO-Replica-Locations-BL
+ldapDisplayName: msDS-NC-RO-Replica-Locations-BL
+attributeId: 1.2.840.113556.1.4.1968
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: f547511c-5b2a-44cc-8358-992a88258164
+systemOnly: FALSE
+searchFlags: 0
+linkID: 2115
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_NOT_REPLICATED
+
+cn: ms-DS-NC-Type
+ldapDisplayName: msDS-NcType
+attributeId: 1.2.840.113556.1.4.2024
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+systemOnly: TRUE
+searchFlags: 0
+schemaIdGuid: 5a2eacd7-cc2b-48cf-9d9a-b6f1a0024de9
+showInAdvancedViewOnly: TRUE
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_NOT_REPLICATED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: meetingDescription
+ldapDisplayName: meetingDescription
+attributeId: 1.2.840.113556.1.4.567
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 11b6cc7e-48c4-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-DS-NC-Repl-Cursors
+ldapDisplayName: msDS-NCReplCursors
+attributeId: 1.2.840.113556.1.4.1704
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 8a167ce4-f9e8-47eb-8d78-f7fe80abb2cc
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_IS_CONSTRUCTED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-NC-Repl-Inbound-Neighbors
+ldapDisplayName: msDS-NCReplInboundNeighbors
+attributeId: 1.2.840.113556.1.4.1705
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 9edba85a-3e9e-431b-9b1a-a5b6e9eda796
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_IS_CONSTRUCTED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-NC-Repl-Outbound-Neighbors
+ldapDisplayName: msDS-NCReplOutboundNeighbors
+attributeId: 1.2.840.113556.1.4.1706
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 855f2ef5-a1c5-4cc4-ba6d-32522848b61f
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_IS_CONSTRUCTED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Never-Reveal-Group
+ldapDisplayName: msDS-NeverRevealGroup
+attributeId: 1.2.840.113556.1.4.1926
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: 15585999-fd49-4d66-b25d-eeb96aba8174
+systemOnly: FALSE
+searchFlags: 0
+linkID: 2106
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Non-Security-Group-Extra-Classes
+ldapDisplayName: msDS-Non-Security-Group-Extra-Classes
+attributeId: 1.2.840.113556.1.4.1689
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 2de144fc-1f52-486f-bdf4-16fcc3084e54
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-DS-Non-Members
+ldapDisplayName: msDS-NonMembers
+attributeId: 1.2.840.113556.1.4.1793
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: cafcb1de-f23c-46b5-adf7-1e64957bd5db
+systemOnly: FALSE
+searchFlags: 0
+linkID: 2014
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Non-Members-BL
+ldapDisplayName: msDS-NonMembersBL
+attributeId: 1.2.840.113556.1.4.1794
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: 2a8c68fc-3a7a-4e87-8720-fe77c51cbe74
+systemOnly: TRUE
+searchFlags: 0
+linkID: 2015
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_NOT_REPLICATED
+
+cn: ms-DS-Object-Reference
+ldapDisplayName: msDS-ObjectReference
+attributeId: 1.2.840.113556.1.4.1840
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: 638ec2e8-22e7-409c-85d2-11b21bee72de
+systemOnly: FALSE
+searchFlags: 0
+linkID: 2038
+
+cn: ms-DS-Object-Reference-BL
+ldapDisplayName: msDS-ObjectReferenceBL
+attributeId: 1.2.840.113556.1.4.1841
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: 2b702515-c1f7-4b3b-b148-c0e4c6ceecb4
+systemOnly: TRUE
+searchFlags: 0
+linkID: 2039
+systemFlags: FLAG_ATTR_NOT_REPLICATED
+
+cn: ms-DS-Operations-For-Az-Role
+ldapDisplayName: msDS-OperationsForAzRole
+attributeId: 1.2.840.113556.1.4.1812
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: 93f701be-fa4c-43b6-bc2f-4dbea718ffab
+systemOnly: FALSE
+searchFlags: 0
+linkID: 2022
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: meetingEndTime
+ldapDisplayName: meetingEndTime
+attributeId: 1.2.840.113556.1.4.588
+attributeSyntax: 2.5.5.11
+omSyntax: 23
+isSingleValued: FALSE
+schemaIdGuid: 11b6cc91-48c4-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-DS-Operations-For-Az-Role-BL
+ldapDisplayName: msDS-OperationsForAzRoleBL
+attributeId: 1.2.840.113556.1.4.1813
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: f85b6228-3734-4525-b6b7-3f3bb220902c
+systemOnly: TRUE
+searchFlags: 0
+linkID: 2023
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_NOT_REPLICATED
+
+cn: ms-DS-Operations-For-Az-Task
+ldapDisplayName: msDS-OperationsForAzTask
+attributeId: 1.2.840.113556.1.4.1808
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: 1aacb436-2e9d-44a9-9298-ce4debeb6ebf
+systemOnly: FALSE
+searchFlags: 0
+linkID: 2018
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-DS-Operations-For-Az-Task-BL
+ldapDisplayName: msDS-OperationsForAzTaskBL
+attributeId: 1.2.840.113556.1.4.1809
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: a637d211-5739-4ed1-89b2-88974548bc59
+systemOnly: TRUE
+searchFlags: 0
+linkID: 2019
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_NOT_REPLICATED
+
+cn: ms-DS-Other-Settings
+ldapDisplayName: msDS-Other-Settings
+attributeId: 1.2.840.113556.1.4.1621
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 79d2f34c-9d7d-42bb-838f-866b3e4400e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Password-Complexity-Enabled
+ldapDisplayName: msDS-PasswordComplexityEnabled
+attributeId: 1.2.840.113556.1.4.2015
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+systemOnly: FALSE
+searchFlags: 0
+schemaIdGuid: db68054b-c9c3-4bf0-b15b-0fb52552a610
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Password-History-Length
+ldapDisplayName: msDS-PasswordHistoryLength
+attributeId: 1.2.840.113556.1.4.2014
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 65535
+schemaIdGuid: fed81bb7-768c-4c2f-9641-2245de34794d
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Password-Reversible-Encryption-Enabled
+ldapDisplayName: msDS-PasswordReversibleEncryptionEnabled
+attributeId: 1.2.840.113556.1.4.2016
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+systemOnly: FALSE
+searchFlags: 0
+schemaIdGuid: 75ccdd8f-af6c-4487-bb4b-69e4d38a959c
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Password-Settings-Precedence
+ldapDisplayName: msDS-PasswordSettingsPrecedence
+attributeId: 1.2.840.113556.1.4.2023
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+schemaIdGuid: 456374ac-1f0a-4617-93cf-bc55a7c9d341
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: MS-DS-Per-User-Trust-Quota
+ldapDisplayName: msDS-PerUserTrustQuota
+attributeId: 1.2.840.113556.1.4.1788
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: d161adf0-ca24-4993-a3aa-8b2c981302e8
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: MS-DS-Per-User-Trust-Tombstones-Quota
+ldapDisplayName: msDS-PerUserTrustTombstonesQuota
+attributeId: 1.2.840.113556.1.4.1790
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 8b70a6c6-50f9-4fa3-a71e-1ce03040449b
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: meetingID
+ldapDisplayName: meetingID
+attributeId: 1.2.840.113556.1.4.565
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 11b6cc7c-48c4-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-DS-Phonetic-Company-Name
+ldapDisplayName: msDS-PhoneticCompanyName
+attributeId: 1.2.840.113556.1.4.1945
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 5bd5208d-e5f4-46ae-a514-543bc9c47659
+systemOnly: FALSE
+searchFlags: fATTINDEX | fANR
+rangeLower: 1
+rangeUpper: 64
+attributeSecurityGuid: e48d0154-bcf8-11d1-8702-00c04fb96050
+mapiID: 35985
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-DS-Phonetic-Department
+ldapDisplayName: msDS-PhoneticDepartment
+attributeId: 1.2.840.113556.1.4.1944
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 6cd53daf-003e-49e7-a702-6fa896e7a6ef
+systemOnly: FALSE
+searchFlags: fATTINDEX | fANR
+rangeLower: 1
+rangeUpper: 64
+attributeSecurityGuid: e48d0154-bcf8-11d1-8702-00c04fb96050
+mapiID: 35984
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-DS-Phonetic-Display-Name
+ldapDisplayName: msDS-PhoneticDisplayName
+attributeId: 1.2.840.113556.1.4.1946
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: e21a94e4-2d66-4ce5-b30d-0ef87a776ff0
+systemOnly: FALSE
+searchFlags: fATTINDEX | fANR
+rangeLower: 0
+rangeUpper: 256
+attributeSecurityGuid: e48d0154-bcf8-11d1-8702-00c04fb96050
+mapiID: 35986
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Phonetic-First-Name
+ldapDisplayName: msDS-PhoneticFirstName
+attributeId: 1.2.840.113556.1.4.1942
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 4b1cba4e-302f-4134-ac7c-f01f6c797843
+systemOnly: FALSE
+searchFlags: fATTINDEX | fANR
+rangeLower: 1
+rangeUpper: 64
+attributeSecurityGuid: e48d0154-bcf8-11d1-8702-00c04fb96050
+mapiID: 35982
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-DS-Phonetic-Last-Name
+ldapDisplayName: msDS-PhoneticLastName
+attributeId: 1.2.840.113556.1.4.1943
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: f217e4ec-0836-4b90-88af-2f5d4bbda2bc
+systemOnly: FALSE
+searchFlags: fATTINDEX | fANR
+rangeLower: 1
+rangeUpper: 64
+attributeSecurityGuid: e48d0154-bcf8-11d1-8702-00c04fb96050
+mapiID: 35983
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-DS-Preferred-GC-Site
+ldapDisplayName: msDS-Preferred-GC-Site
+attributeId: 1.2.840.113556.1.4.1444
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: TRUE
+schemaIdGuid: d921b50a-0ab2-42cd-87f6-09cf83a91854
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Principal-Name
+ldapDisplayName: msDS-PrincipalName
+attributeId: 1.2.840.113556.1.4.1865
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 564e9325-d057-c143-9e3b-4f9e5ef46f93
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_IS_CONSTRUCTED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Promotion-Settings
+ldapDisplayName: msDS-PromotionSettings
+attributeId: 1.2.840.113556.1.4.1962
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: c881b4e2-43c0-4ebe-b9bb-5250aa9b434c
+systemOnly: TRUE
+searchFlags: 0
+rangeUpper: 65536
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-DS-PSO-Applied
+ldapDisplayName: msDS-PSOApplied
+attributeId: 1.2.840.113556.1.4.2021
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+isSingleValued: FALSE
+systemOnly: TRUE
+searchFlags: 0
+omObjectClass: 1.3.12.2.1011.28.0.714
+schemaIdGuid: 5e6cf031-bda8-43c8-aca4-8fee4127005b
+linkID: 2119
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_NOT_REPLICATED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-PSO-Applies-To
+ldapDisplayName: msDS-PSOAppliesTo
+attributeId: 1.2.840.113556.1.4.2020
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+isSingleValued: FALSE
+systemOnly: FALSE
+searchFlags: 0
+omObjectClass: 1.3.12.2.1011.28.0.714
+schemaIdGuid: 64c80f48-cdd2-4881-a86d-4e97b6f561fc
+linkID: 2118
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: meetingIP
+ldapDisplayName: meetingIP
+attributeId: 1.2.840.113556.1.4.580
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 11b6cc89-48c4-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-DS-Quota-Amount
+ldapDisplayName: msDS-QuotaAmount
+attributeId: 1.2.840.113556.1.4.1845
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: fbb9a00d-3a8c-4233-9cf9-7189264903a1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Quota-Effective
+ldapDisplayName: msDS-QuotaEffective
+attributeId: 1.2.840.113556.1.4.1848
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 6655b152-101c-48b4-b347-e1fcebc60157
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_IS_CONSTRUCTED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Quota-Trustee
+ldapDisplayName: msDS-QuotaTrustee
+attributeId: 1.2.840.113556.1.4.1844
+attributeSyntax: 2.5.5.17
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 16378906-4ea5-49be-a8d1-bfd41dff4f65
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 28
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Quota-Used
+ldapDisplayName: msDS-QuotaUsed
+attributeId: 1.2.840.113556.1.4.1849
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: b5a84308-615d-4bb7-b05f-2f1746aa439f
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_IS_CONSTRUCTED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Repl-Attribute-Meta-Data
+ldapDisplayName: msDS-ReplAttributeMetaData
+attributeId: 1.2.840.113556.1.4.1707
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: d7c53242-724e-4c39-9d4c-2df8c9d66c7a
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_IS_CONSTRUCTED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Replication-Notify-First-DSA-Delay
+ldapDisplayName: msDS-Replication-Notify-First-DSA-Delay
+attributeId: 1.2.840.113556.1.4.1663
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 85abd4f4-0a89-4e49-bdec-6f35bb2562ba
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Replication-Notify-Subsequent-DSA-Delay
+ldapDisplayName: msDS-Replication-Notify-Subsequent-DSA-Delay
+attributeId: 1.2.840.113556.1.4.1664
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: d63db385-dd92-4b52-b1d8-0d3ecc0e86b6
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-ReplicationEpoch
+ldapDisplayName: msDS-ReplicationEpoch
+attributeId: 1.2.840.113556.1.4.1720
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 08e3aa79-eb1c-45b5-af7b-8f94246c8e41
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_NOT_REPLICATED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Repl-Value-Meta-Data
+ldapDisplayName: msDS-ReplValueMetaData
+attributeId: 1.2.840.113556.1.4.1708
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 2f5c8145-e1bd-410b-8957-8bfa81d5acfd
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_IS_CONSTRUCTED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Resultant-PSO
+ldapDisplayName: msDS-ResultantPSO
+attributeId: 1.2.840.113556.1.4.2022
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+isSingleValued: TRUE
+systemOnly: TRUE
+searchFlags: 0
+omObjectClass: 1.3.12.2.1011.28.0.714
+schemaIdGuid: b77ea093-88d0-4780-9a98-911f8e8b1dca
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_IS_CONSTRUCTED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: meetingIsEncrypted
+ldapDisplayName: meetingIsEncrypted
+attributeId: 1.2.840.113556.1.4.585
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 11b6cc8e-48c4-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-DS-Retired-Repl-NC-Signatures
+ldapDisplayName: msDS-RetiredReplNCSignatures
+attributeId: 1.2.840.113556.1.4.1826
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: d5b35506-19d6-4d26-9afb-11357ac99b5e
+systemOnly: TRUE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_NOT_REPLICATED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Revealed-DSAs
+ldapDisplayName: msDS-RevealedDSAs
+attributeId: 1.2.840.113556.1.4.1930
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: 94f6f2ac-c76d-4b5e-b71f-f332c3e93c22
+systemOnly: TRUE
+searchFlags: 0
+linkID: 2103
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_NOT_REPLICATED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Revealed-List
+ldapDisplayName: msDS-RevealedList
+attributeId: 1.2.840.113556.1.4.1940
+attributeSyntax: 2.5.5.14
+omSyntax: 127
+omObjectClass: 1.2.840.113556.1.1.1.12
+isSingleValued: FALSE
+schemaIdGuid: cbdad11c-7fec-387b-6219-3a0627d9af81
+systemOnly: TRUE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_IS_CONSTRUCTED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Revealed-List-BL
+ldapDisplayName: msDS-RevealedListBL
+attributeId: 1.2.840.113556.1.4.1975
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: aa1c88fd-b0f6-429f-b2ca-9d902266e808
+systemOnly: TRUE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_IS_CONSTRUCTED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Revealed-Users
+ldapDisplayName: msDS-RevealedUsers
+attributeId: 1.2.840.113556.1.4.1924
+attributeSyntax: 2.5.5.7
+omSyntax: 127
+omObjectClass: 1.2.840.113556.1.1.1.11
+isSingleValued: FALSE
+schemaIdGuid: 185c7821-3749-443a-bd6a-288899071adb
+systemOnly: TRUE
+searchFlags: 0
+linkID: 2102
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Reveal-OnDemand-Group
+ldapDisplayName: msDS-RevealOnDemandGroup
+attributeId: 1.2.840.113556.1.4.1928
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: 303d9f4a-1dd6-4b38-8fc5-33afe8c988ad
+systemOnly: FALSE
+searchFlags: 0
+linkID: 2110
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-ds-Schema-Extensions
+ldapDisplayName: msDs-Schema-Extensions
+attributeId: 1.2.840.113556.1.4.1440
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: FALSE
+schemaIdGuid: b39a61be-ed07-4cab-9a4a-4963ed0141e1
+systemOnly: TRUE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-DS-SD-Reference-Domain
+ldapDisplayName: msDS-SDReferenceDomain
+attributeId: 1.2.840.113556.1.4.1711
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: TRUE
+schemaIdGuid: 4c51e316-f628-43a5-b06b-ffb695fcb4f3
+systemOnly: FALSE
+searchFlags: 0
+linkID: 2000
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Secondary-KrbTgt-Number
+ldapDisplayName: msDS-SecondaryKrbTgtNumber
+attributeId: 1.2.840.113556.1.4.1929
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: aa156612-2396-467e-ad6a-28d23fdb1865
+systemOnly: TRUE
+searchFlags: fATTINDEX
+rangeLower: 65536
+rangeUpper: 65536
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Security-Group-Extra-Classes
+ldapDisplayName: msDS-Security-Group-Extra-Classes
+attributeId: 1.2.840.113556.1.4.1688
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 4f146ae8-a4fe-4801-a731-f51848a4f4e4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: meetingKeyword
+ldapDisplayName: meetingKeyword
+attributeId: 1.2.840.113556.1.4.568
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 11b6cc7f-48c4-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-DS-Settings
+ldapDisplayName: msDS-Settings
+attributeId: 1.2.840.113556.1.4.1697
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 0e1b47d7-40a3-4b48-8d1b-4cac0c1cdf21
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 1000000
+
+cn: ms-DS-SiteName
+ldapDisplayName: msDS-SiteName
+attributeId: 1.2.840.113556.1.4.1961
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 98a7f36d-3595-448a-9e6f-6b8965baed9c
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_IS_CONSTRUCTED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Site-Affinity
+ldapDisplayName: msDS-Site-Affinity
+attributeId: 1.2.840.113556.1.4.1443
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: FALSE
+schemaIdGuid: c17c5602-bcb7-46f0-9656-6370ca884b72
+systemOnly: FALSE
+searchFlags: fATTINDEX
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Source-Object-DN
+ldapDisplayName: msDS-SourceObjectDN
+attributeId: 1.2.840.113556.1.4.1879
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 773e93af-d3b4-48d4-b3f9-06457602d3d0
+attributeSecurityGuid: e48d0154-bcf8-11d1-8702-00c04fb96050
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 10240
+
+cn: ms-DS-SPN-Suffixes
+ldapDisplayName: msDS-SPNSuffixes
+attributeId: 1.2.840.113556.1.4.1715
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 789ee1eb-8c8e-4e4c-8cec-79b31b7617b5
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 255
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Supported-Encryption-Types
+ldapDisplayName: msDS-SupportedEncryptionTypes
+attributeId: 1.2.840.113556.1.4.1963
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 20119867-1d04-4ab7-9371-cfc3d5df0afd
+systemOnly: FALSE
+searchFlags: 0
+attributeSecurityGuid: 77b5b886-944a-11d1-aebd-0000f80367c1
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Tasks-For-Az-Role
+ldapDisplayName: msDS-TasksForAzRole
+attributeId: 1.2.840.113556.1.4.1814
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: 35319082-8c4a-4646-9386-c2949d49894d
+systemOnly: FALSE
+searchFlags: 0
+linkID: 2024
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-DS-Tasks-For-Az-Role-BL
+ldapDisplayName: msDS-TasksForAzRoleBL
+attributeId: 1.2.840.113556.1.4.1815
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: a0dcd536-5158-42fe-8c40-c00a7ad37959
+systemOnly: TRUE
+searchFlags: 0
+linkID: 2025
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_NOT_REPLICATED
+
+cn: ms-DS-Tasks-For-Az-Task
+ldapDisplayName: msDS-TasksForAzTask
+attributeId: 1.2.840.113556.1.4.1810
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: b11c8ee2-5fcd-46a7-95f0-f38333f096cf
+systemOnly: FALSE
+searchFlags: 0
+linkID: 2020
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Machine-Password-Change-Interval
+ldapDisplayName: machinePasswordChangeInterval
+attributeId: 1.2.840.113556.1.4.520
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: c9b6358e-bb38-11d0-afef-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+attributeSecurityGuid: a29b89fe-c7e8-11d0-9bae-00c04fd92ef5
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: meetingLanguage
+ldapDisplayName: meetingLanguage
+attributeId: 1.2.840.113556.1.4.574
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 11b6cc84-48c4-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-DS-Tasks-For-Az-Task-BL
+ldapDisplayName: msDS-TasksForAzTaskBL
+attributeId: 1.2.840.113556.1.4.1811
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: df446e52-b5fa-4ca2-a42f-13f98a526c8f
+systemOnly: TRUE
+searchFlags: 0
+linkID: 2021
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_NOT_REPLICATED
+
+cn: ms-DS-Tombstone-Quota-Factor
+ldapDisplayName: msDS-TombstoneQuotaFactor
+attributeId: 1.2.840.113556.1.4.1847
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 461744d7-f3b6-45ba-8753-fb9552a5df32
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 100
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Top-Quota-Usage
+ldapDisplayName: msDS-TopQuotaUsage
+attributeId: 1.2.840.113556.1.4.1850
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 7b7cce4f-f1f5-4bb6-b7eb-23504af19e75
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_IS_CONSTRUCTED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-Trust-Forest-Trust-Info
+ldapDisplayName: msDS-TrustForestTrustInfo
+attributeId: 1.2.840.113556.1.4.1702
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 29cc866e-49d3-4969-942e-1dbc0925d183
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-UpdateScript
+ldapDisplayName: msDS-UpdateScript
+attributeId: 1.2.840.113556.1.4.1721
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 146eb639-bb9f-4fc1-a825-e29e00c77920
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-User-Account-Control-Computed
+ldapDisplayName: msDS-User-Account-Control-Computed
+attributeId: 1.2.840.113556.1.4.1460
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 2cc4b836-b63f-4940-8d23-ea7acf06af56
+systemOnly: FALSE
+searchFlags: 0
+attributeSecurityGuid: 4c164200-20c0-11d0-a768-00aa006e0529
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_IS_CONSTRUCTED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-DS-User-Password-Expiry-Time-Computed
+ldapDisplayName: msDS-UserPasswordExpiryTimeComputed
+attributeId: 1.2.840.113556.1.4.1996
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: add5cf10-7b09-4449-9ae6-2534148f8a72
+systemOnly: FALSE
+searchFlags: 0
+attributeSecurityGuid: 4c164200-20c0-11d0-a768-00aa006e0529
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_IS_CONSTRUCTED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-Exch-Assistant-Name
+ldapDisplayName: msExchAssistantName
+attributeId: 1.2.840.113556.1.2.444
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: a8df7394-c5ea-11d1-bbcb-0080c76670c0
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 256
+mapiID: 14896
+
+cn: ms-Exch-House-Identifier
+ldapDisplayName: msExchHouseIdentifier
+attributeId: 1.2.840.113556.1.2.596
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: a8df7407-c5ea-11d1-bbcb-0080c76670c0
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 128
+mapiID: 35924
+
+cn: ms-Exch-LabeledURI
+ldapDisplayName: msExchLabeledURI
+attributeId: 1.2.840.113556.1.2.593
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 16775820-47f3-11d1-a9c3-0000f80367c1
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 1024
+mapiID: 35921
+
+cn: meetingLocation
+ldapDisplayName: meetingLocation
+attributeId: 1.2.840.113556.1.4.569
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 11b6cc80-48c4-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-FRS-Hub-Member
+ldapDisplayName: msFRS-Hub-Member
+attributeId: 1.2.840.113556.1.4.1693
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: TRUE
+schemaIdGuid: 5643ff81-35b6-4ca9-9512-baf0bd0a2772
+searchFlags: 0
+linkID: 1046
+
+cn: ms-FRS-Topology-Pref
+ldapDisplayName: msFRS-Topology-Pref
+attributeId: 1.2.840.113556.1.4.1692
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 92aa27e0-5c50-402d-9ec1-ee847def9788
+searchFlags: 0
+
+cn: ms-FVE-KeyPackage
+ldapDisplayName: msFVE-KeyPackage
+attributeId: 1.2.840.113556.1.4.1999
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+rangeUpper: 102400
+schemaIdGuid: 1fd55ea8-88a7-47dc-8129-0daa97186a54
+searchFlags: fRODCFilteredAttribute | fCONFIDENTIAL | fCOPY |fPRESERVEONDELETE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-FVE-RecoveryGuid
+ldapDisplayName: msFVE-RecoveryGuid
+attributeId: 1.2.840.113556.1.4.1965
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: f76909bc-e678-47a0-b0b3-f86a0044c06d
+searchFlags: fCOPY | fPRESERVEONDELETE | fPDNTATTINDEX | fATTINDEX
+rangeUpper: 128
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-FVE-RecoveryPassword
+ldapDisplayName: msFVE-RecoveryPassword
+attributeId: 1.2.840.113556.1.4.1964
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+rangeUpper: 256
+schemaIdGuid: 43061ac1-c8ad-4ccc-b785-2bfac20fc60a
+searchFlags: fRODCFilteredAttribute | fCONFIDENTIAL | fCOPY |fPRESERVEONDELETE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-FVE-VolumeGuid
+ldapDisplayName: msFVE-VolumeGuid
+attributeId: 1.2.840.113556.1.4.1998
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+searchFlags: fCOPY | fPRESERVEONDELETE | fPDNTATTINDEX | fATTINDEX
+rangeUpper: 128
+schemaIdGuid: 85e5a5cf-dcee-4075-9cfd-ac9db6a2f245
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-ieee-80211-Data
+ldapDisplayName: msieee80211-Data
+attributeId: 1.2.840.113556.1.4.1821
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 0e0d0938-2658-4580-a9f6-7a0ac7b566cb
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-ieee-80211-Data-Type
+ldapDisplayName: msieee80211-DataType
+attributeId: 1.2.840.113556.1.4.1822
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 6558b180-35da-4efe-beed-521f8f48cafb
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-ieee-80211-ID
+ldapDisplayName: msieee80211-ID
+attributeId: 1.2.840.113556.1.4.1823
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 7f73ef75-14c9-4c23-81de-dd07a06f9e8b
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Msi-File-List
+ldapDisplayName: msiFileList
+attributeId: 1.2.840.113556.1.4.671
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 7bfdcb7d-4807-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: meetingMaxParticipants
+ldapDisplayName: meetingMaxParticipants
+attributeId: 1.2.840.113556.1.4.576
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 11b6cc85-48c4-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-IIS-FTP-Dir
+ldapDisplayName: msIIS-FTPDir
+attributeId: 1.2.840.113556.1.4.1786
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 8a5c99e9-2230-46eb-b8e8-e59d712eb9ee
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 256
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-IIS-FTP-Root
+ldapDisplayName: msIIS-FTPRoot
+attributeId: 1.2.840.113556.1.4.1785
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 2a7827a4-1483-49a5-9d84-52e3812156b4
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 256
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Msi-Script
+ldapDisplayName: msiScript
+attributeId: 1.2.840.113556.1.4.814
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: d9e18313-8939-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Msi-Script-Name
+ldapDisplayName: msiScriptName
+attributeId: 1.2.840.113556.1.4.845
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 96a7dd62-9118-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Msi-Script-Path
+ldapDisplayName: msiScriptPath
+attributeId: 1.2.840.113556.1.4.15
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: bf967937-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Msi-Script-Size
+ldapDisplayName: msiScriptSize
+attributeId: 1.2.840.113556.1.4.846
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 96a7dd63-9118-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Multicast-Address
+ldapDisplayName: MSMQ-MulticastAddress
+attributeId: 1.2.840.113556.1.4.1714
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 1d2f4412-f10d-4337-9b48-6e5b125cd265
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 9
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Recipient-FormatName
+ldapDisplayName: msMQ-Recipient-FormatName
+attributeId: 1.2.840.113556.1.4.1695
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 3bfe6748-b544-485a-b067-1b310c4334bf
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 255
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Secured-Source
+ldapDisplayName: MSMQ-SecuredSource
+attributeId: 1.2.840.113556.1.4.1713
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: 8bf0221b-7a06-4d63-91f0-1499941813d3
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Authenticate
+ldapDisplayName: mSMQAuthenticate
+attributeId: 1.2.840.113556.1.4.923
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: 9a0dc326-c100-11d1-bbc5-0080c76670c0
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: meetingName
+ldapDisplayName: meetingName
+attributeId: 1.2.840.113556.1.4.566
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 11b6cc7d-48c4-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Base-Priority
+ldapDisplayName: mSMQBasePriority
+attributeId: 1.2.840.113556.1.4.920
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 9a0dc323-c100-11d1-bbc5-0080c76670c0
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Computer-Type
+ldapDisplayName: mSMQComputerType
+attributeId: 1.2.840.113556.1.4.933
+attributeSyntax: 2.5.5.4
+omSyntax: 20
+isSingleValued: TRUE
+schemaIdGuid: 9a0dc32e-c100-11d1-bbc5-0080c76670c0
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Computer-Type-Ex
+ldapDisplayName: mSMQComputerTypeEx
+attributeId: 1.2.840.113556.1.4.1417
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 18120de8-f4c4-4341-bd95-32eb5bcf7c80
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Cost
+ldapDisplayName: mSMQCost
+attributeId: 1.2.840.113556.1.4.946
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 9a0dc33a-c100-11d1-bbc5-0080c76670c0
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-CSP-Name
+ldapDisplayName: mSMQCSPName
+attributeId: 1.2.840.113556.1.4.940
+attributeSyntax: 2.5.5.4
+omSyntax: 20
+isSingleValued: TRUE
+schemaIdGuid: 9a0dc334-c100-11d1-bbc5-0080c76670c0
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Dependent-Client-Service
+ldapDisplayName: mSMQDependentClientService
+attributeId: 1.2.840.113556.1.4.1239
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: 2df90d83-009f-11d2-aa4c-00c04fd7d83a
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Dependent-Client-Services
+ldapDisplayName: mSMQDependentClientServices
+attributeId: 1.2.840.113556.1.4.1226
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: 2df90d76-009f-11d2-aa4c-00c04fd7d83a
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Digests
+ldapDisplayName: mSMQDigests
+attributeId: 1.2.840.113556.1.4.948
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: FALSE
+schemaIdGuid: 9a0dc33c-c100-11d1-bbc5-0080c76670c0
+systemOnly: FALSE
+searchFlags: fATTINDEX
+rangeLower: 16
+rangeUpper: 16
+attributeSecurityGuid: 77b5b886-944a-11d1-aebd-0000f80367c1
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Digests-Mig
+ldapDisplayName: mSMQDigestsMig
+attributeId: 1.2.840.113556.1.4.966
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: FALSE
+schemaIdGuid: 0f71d8e0-da3b-11d1-90a5-00c04fd91ab1
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Ds-Service
+ldapDisplayName: mSMQDsService
+attributeId: 1.2.840.113556.1.4.1238
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: 2df90d82-009f-11d2-aa4c-00c04fd7d83a
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: meetingOriginator
+ldapDisplayName: meetingOriginator
+attributeId: 1.2.840.113556.1.4.577
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 11b6cc86-48c4-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Ds-Services
+ldapDisplayName: mSMQDsServices
+attributeId: 1.2.840.113556.1.4.1228
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: 2df90d78-009f-11d2-aa4c-00c04fd7d83a
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Encrypt-Key
+ldapDisplayName: mSMQEncryptKey
+attributeId: 1.2.840.113556.1.4.936
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 9a0dc331-c100-11d1-bbc5-0080c76670c0
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Foreign
+ldapDisplayName: mSMQForeign
+attributeId: 1.2.840.113556.1.4.934
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: 9a0dc32f-c100-11d1-bbc5-0080c76670c0
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-In-Routing-Servers
+ldapDisplayName: mSMQInRoutingServers
+attributeId: 1.2.840.113556.1.4.929
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: 9a0dc32c-c100-11d1-bbc5-0080c76670c0
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Interval1
+ldapDisplayName: mSMQInterval1
+attributeId: 1.2.840.113556.1.4.1308
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 8ea825aa-3b7b-11d2-90cc-00c04fd91ab1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Interval2
+ldapDisplayName: mSMQInterval2
+attributeId: 1.2.840.113556.1.4.1309
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 99b88f52-3b7b-11d2-90cc-00c04fd91ab1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Journal
+ldapDisplayName: mSMQJournal
+attributeId: 1.2.840.113556.1.4.918
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: 9a0dc321-c100-11d1-bbc5-0080c76670c0
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Journal-Quota
+ldapDisplayName: mSMQJournalQuota
+attributeId: 1.2.840.113556.1.4.921
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 9a0dc324-c100-11d1-bbc5-0080c76670c0
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Label
+ldapDisplayName: mSMQLabel
+attributeId: 1.2.840.113556.1.4.922
+attributeSyntax: 2.5.5.4
+omSyntax: 20
+isSingleValued: TRUE
+schemaIdGuid: 9a0dc325-c100-11d1-bbc5-0080c76670c0
+systemOnly: FALSE
+searchFlags: fATTINDEX
+rangeLower: 0
+rangeUpper: 124
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Label-Ex
+ldapDisplayName: mSMQLabelEx
+attributeId: 1.2.840.113556.1.4.1415
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 4580ad25-d407-48d2-ad24-43e6e56793d7
+systemOnly: FALSE
+searchFlags: fATTINDEX
+rangeLower: 0
+rangeUpper: 124
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: meetingOwner
+ldapDisplayName: meetingOwner
+attributeId: 1.2.840.113556.1.4.579
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 11b6cc88-48c4-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Long-Lived
+ldapDisplayName: mSMQLongLived
+attributeId: 1.2.840.113556.1.4.941
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 9a0dc335-c100-11d1-bbc5-0080c76670c0
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Migrated
+ldapDisplayName: mSMQMigrated
+attributeId: 1.2.840.113556.1.4.952
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: 9a0dc33f-c100-11d1-bbc5-0080c76670c0
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Name-Style
+ldapDisplayName: mSMQNameStyle
+attributeId: 1.2.840.113556.1.4.939
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: 9a0dc333-c100-11d1-bbc5-0080c76670c0
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Nt4-Flags
+ldapDisplayName: mSMQNt4Flags
+attributeId: 1.2.840.113556.1.4.964
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: eb38a158-d57f-11d1-90a2-00c04fd91ab1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Nt4-Stub
+ldapDisplayName: mSMQNt4Stub
+attributeId: 1.2.840.113556.1.4.960
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: FALSE
+schemaIdGuid: 6f914be6-d57e-11d1-90a2-00c04fd91ab1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-OS-Type
+ldapDisplayName: mSMQOSType
+attributeId: 1.2.840.113556.1.4.935
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 9a0dc330-c100-11d1-bbc5-0080c76670c0
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Out-Routing-Servers
+ldapDisplayName: mSMQOutRoutingServers
+attributeId: 1.2.840.113556.1.4.928
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: 9a0dc32b-c100-11d1-bbc5-0080c76670c0
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Owner-ID
+ldapDisplayName: mSMQOwnerID
+attributeId: 1.2.840.113556.1.4.925
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 9a0dc328-c100-11d1-bbc5-0080c76670c0
+systemOnly: FALSE
+searchFlags: fPRESERVEONDELETE | fATTINDEX
+rangeLower: 16
+rangeUpper: 16
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: MSMQ-Prev-Site-Gates
+ldapDisplayName: mSMQPrevSiteGates
+attributeId: 1.2.840.113556.1.4.1225
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: 2df90d75-009f-11d2-aa4c-00c04fd7d83a
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Privacy-Level
+ldapDisplayName: mSMQPrivacyLevel
+attributeId: 1.2.840.113556.1.4.924
+attributeSyntax: 2.5.5.9
+omSyntax: 10
+isSingleValued: TRUE
+schemaIdGuid: 9a0dc327-c100-11d1-bbc5-0080c76670c0
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 2
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: meetingProtocol
+ldapDisplayName: meetingProtocol
+attributeId: 1.2.840.113556.1.4.570
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 11b6cc81-48c4-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-QM-ID
+ldapDisplayName: mSMQQMID
+attributeId: 1.2.840.113556.1.4.951
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 9a0dc33e-c100-11d1-bbc5-0080c76670c0
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 16
+rangeUpper: 16
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Queue-Journal-Quota
+ldapDisplayName: mSMQQueueJournalQuota
+attributeId: 1.2.840.113556.1.4.963
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 8e441266-d57f-11d1-90a2-00c04fd91ab1
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Queue-Name-Ext
+ldapDisplayName: mSMQQueueNameExt
+attributeId: 1.2.840.113556.1.4.1243
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 2df90d87-009f-11d2-aa4c-00c04fd7d83a
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 92
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Queue-Quota
+ldapDisplayName: mSMQQueueQuota
+attributeId: 1.2.840.113556.1.4.962
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 3f6b8e12-d57f-11d1-90a2-00c04fd91ab1
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Queue-Type
+ldapDisplayName: mSMQQueueType
+attributeId: 1.2.840.113556.1.4.917
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 9a0dc320-c100-11d1-bbc5-0080c76670c0
+systemOnly: FALSE
+searchFlags: fATTINDEX
+rangeLower: 16
+rangeUpper: 16
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Quota
+ldapDisplayName: mSMQQuota
+attributeId: 1.2.840.113556.1.4.919
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 9a0dc322-c100-11d1-bbc5-0080c76670c0
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Routing-Service
+ldapDisplayName: mSMQRoutingService
+attributeId: 1.2.840.113556.1.4.1237
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: 2df90d81-009f-11d2-aa4c-00c04fd7d83a
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Routing-Services
+ldapDisplayName: mSMQRoutingServices
+attributeId: 1.2.840.113556.1.4.1227
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: 2df90d77-009f-11d2-aa4c-00c04fd7d83a
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Services
+ldapDisplayName: mSMQServices
+attributeId: 1.2.840.113556.1.4.950
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 9a0dc33d-c100-11d1-bbc5-0080c76670c0
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Service-Type
+ldapDisplayName: mSMQServiceType
+attributeId: 1.2.840.113556.1.4.930
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 9a0dc32d-c100-11d1-bbc5-0080c76670c0
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: meetingRating
+ldapDisplayName: meetingRating
+attributeId: 1.2.840.113556.1.4.584
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 11b6cc8d-48c4-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Sign-Certificates
+ldapDisplayName: mSMQSignCertificates
+attributeId: 1.2.840.113556.1.4.947
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 9a0dc33b-c100-11d1-bbc5-0080c76670c0
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 1048576
+attributeSecurityGuid: 77b5b886-944a-11d1-aebd-0000f80367c1
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Sign-Certificates-Mig
+ldapDisplayName: mSMQSignCertificatesMig
+attributeId: 1.2.840.113556.1.4.967
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 3881b8ea-da3b-11d1-90a5-00c04fd91ab1
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 1048576
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Sign-Key
+ldapDisplayName: mSMQSignKey
+attributeId: 1.2.840.113556.1.4.937
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 9a0dc332-c100-11d1-bbc5-0080c76670c0
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Site-1
+ldapDisplayName: mSMQSite1
+attributeId: 1.2.840.113556.1.4.943
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: TRUE
+schemaIdGuid: 9a0dc337-c100-11d1-bbc5-0080c76670c0
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Site-2
+ldapDisplayName: mSMQSite2
+attributeId: 1.2.840.113556.1.4.944
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: TRUE
+schemaIdGuid: 9a0dc338-c100-11d1-bbc5-0080c76670c0
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Site-Foreign
+ldapDisplayName: mSMQSiteForeign
+attributeId: 1.2.840.113556.1.4.961
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: FALSE
+schemaIdGuid: fd129d8a-d57e-11d1-90a2-00c04fd91ab1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Site-Gates
+ldapDisplayName: mSMQSiteGates
+attributeId: 1.2.840.113556.1.4.945
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: 9a0dc339-c100-11d1-bbc5-0080c76670c0
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Site-Gates-Mig
+ldapDisplayName: mSMQSiteGatesMig
+attributeId: 1.2.840.113556.1.4.1310
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: e2704852-3b7b-11d2-90cc-00c04fd91ab1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Site-ID
+ldapDisplayName: mSMQSiteID
+attributeId: 1.2.840.113556.1.4.953
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 9a0dc340-c100-11d1-bbc5-0080c76670c0
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Site-Name
+ldapDisplayName: mSMQSiteName
+attributeId: 1.2.840.113556.1.4.965
+attributeSyntax: 2.5.5.4
+omSyntax: 20
+isSingleValued: TRUE
+schemaIdGuid: ffadb4b2-de39-11d1-90a5-00c04fd91ab1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: meetingRecurrence
+ldapDisplayName: meetingRecurrence
+attributeId: 1.2.840.113556.1.4.586
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 11b6cc8f-48c4-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Site-Name-Ex
+ldapDisplayName: mSMQSiteNameEx
+attributeId: 1.2.840.113556.1.4.1416
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 422144fa-c17f-4649-94d6-9731ed2784ed
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Sites
+ldapDisplayName: mSMQSites
+attributeId: 1.2.840.113556.1.4.927
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: FALSE
+schemaIdGuid: 9a0dc32a-c100-11d1-bbc5-0080c76670c0
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 16
+rangeUpper: 16
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Transactional
+ldapDisplayName: mSMQTransactional
+attributeId: 1.2.840.113556.1.4.926
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: 9a0dc329-c100-11d1-bbc5-0080c76670c0
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-User-Sid
+ldapDisplayName: mSMQUserSid
+attributeId: 1.2.840.113556.1.4.1337
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: c58aae32-56f9-11d2-90d0-00c04fd91ab1
+systemOnly: TRUE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 128
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT |FLAG_ATTR_REQ_PARTIAL_SET_MEMBER
+
+cn: MSMQ-Version
+ldapDisplayName: mSMQVersion
+attributeId: 1.2.840.113556.1.4.942
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 9a0dc336-c100-11d1-bbc5-0080c76670c0
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: msNPAllowDialin
+ldapDisplayName: msNPAllowDialin
+attributeId: 1.2.840.113556.1.4.1119
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: db0c9085-c1f2-11d1-bbc5-0080c76670c0
+systemOnly: FALSE
+searchFlags: fCOPY
+attributeSecurityGuid: 037088f8-0ae1-11d2-b422-00a0c968f939
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: msNPCalledStationID
+ldapDisplayName: msNPCalledStationID
+attributeId: 1.2.840.113556.1.4.1123
+attributeSyntax: 2.5.5.5
+omSyntax: 22
+isSingleValued: FALSE
+schemaIdGuid: db0c9089-c1f2-11d1-bbc5-0080c76670c0
+systemOnly: FALSE
+searchFlags: fCOPY
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: msNPCallingStationID
+ldapDisplayName: msNPCallingStationID
+attributeId: 1.2.840.113556.1.4.1124
+attributeSyntax: 2.5.5.5
+omSyntax: 22
+isSingleValued: FALSE
+schemaIdGuid: db0c908a-c1f2-11d1-bbc5-0080c76670c0
+systemOnly: FALSE
+searchFlags: fCOPY
+attributeSecurityGuid: 037088f8-0ae1-11d2-b422-00a0c968f939
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: msNPSavedCallingStationID
+ldapDisplayName: msNPSavedCallingStationID
+attributeId: 1.2.840.113556.1.4.1130
+attributeSyntax: 2.5.5.5
+omSyntax: 22
+isSingleValued: FALSE
+schemaIdGuid: db0c908e-c1f2-11d1-bbc5-0080c76670c0
+systemOnly: FALSE
+searchFlags: fCOPY
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-PKI-Cert-Template-OID
+ldapDisplayName: msPKI-Cert-Template-OID
+attributeId: 1.2.840.113556.1.4.1436
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 3164c36a-ba26-468c-8bda-c1e5cc256728
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: meetingScope
+ldapDisplayName: meetingScope
+attributeId: 1.2.840.113556.1.4.581
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 11b6cc8a-48c4-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-PKI-Certificate-Application-Policy
+ldapDisplayName: msPKI-Certificate-Application-Policy
+attributeId: 1.2.840.113556.1.4.1674
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: dbd90548-aa37-4202-9966-8c537ba5ce32
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-PKI-Certificate-Name-Flag
+ldapDisplayName: msPKI-Certificate-Name-Flag
+attributeId: 1.2.840.113556.1.4.1432
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: ea1dddc4-60ff-416e-8cc0-17cee534bce7
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-PKI-Certificate-Policy
+ldapDisplayName: msPKI-Certificate-Policy
+attributeId: 1.2.840.113556.1.4.1439
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 38942346-cc5b-424b-a7d8-6ffd12029c5f
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-PKI-Enrollment-Flag
+ldapDisplayName: msPKI-Enrollment-Flag
+attributeId: 1.2.840.113556.1.4.1430
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: d15ef7d8-f226-46db-ae79-b34e560bd12c
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-PKI-Minimal-Key-Size
+ldapDisplayName: msPKI-Minimal-Key-Size
+attributeId: 1.2.840.113556.1.4.1433
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: e96a63f5-417f-46d3-be52-db7703c503df
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-PKI-OID-Attribute
+ldapDisplayName: msPKI-OID-Attribute
+attributeId: 1.2.840.113556.1.4.1671
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 8c9e1288-5028-4f4f-a704-76d026f246ef
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-PKI-OID-CPS
+ldapDisplayName: msPKI-OID-CPS
+attributeId: 1.2.840.113556.1.4.1672
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 5f49940e-a79f-4a51-bb6f-3d446a54dc6b
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 32768
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-PKI-OID-User-Notice
+ldapDisplayName: msPKI-OID-User-Notice
+attributeId: 1.2.840.113556.1.4.1673
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 04c4da7a-e114-4e69-88de-e293f2d3b395
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 32768
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-PKI-OID-LocalizedName
+ldapDisplayName: msPKI-OIDLocalizedName
+attributeId: 1.2.840.113556.1.4.1712
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 7d59a816-bb05-4a72-971f-5c1331f67559
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 512
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-PKI-Private-Key-Flag
+ldapDisplayName: msPKI-Private-Key-Flag
+attributeId: 1.2.840.113556.1.4.1431
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: bab04ac2-0435-4709-9307-28380e7c7001
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Machine-Role
+ldapDisplayName: machineRole
+attributeId: 1.2.840.113556.1.4.71
+attributeSyntax: 2.5.5.9
+omSyntax: 10
+isSingleValued: TRUE
+schemaIdGuid: bf9679b2-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: meetingStartTime
+ldapDisplayName: meetingStartTime
+attributeId: 1.2.840.113556.1.4.587
+attributeSyntax: 2.5.5.11
+omSyntax: 23
+isSingleValued: FALSE
+schemaIdGuid: 11b6cc90-48c4-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-PKI-RA-Application-Policies
+ldapDisplayName: msPKI-RA-Application-Policies
+attributeId: 1.2.840.113556.1.4.1675
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 3c91fbbf-4773-4ccd-a87b-85d53e7bcf6a
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-PKI-RA-Policies
+ldapDisplayName: msPKI-RA-Policies
+attributeId: 1.2.840.113556.1.4.1438
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: d546ae22-0951-4d47-817e-1c9f96faad46
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-PKI-RA-Signature
+ldapDisplayName: msPKI-RA-Signature
+attributeId: 1.2.840.113556.1.4.1429
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: fe17e04b-937d-4f7e-8e0e-9292c8d5683e
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-PKI-Supersede-Templates
+ldapDisplayName: msPKI-Supersede-Templates
+attributeId: 1.2.840.113556.1.4.1437
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 9de8ae7d-7a5b-421d-b5e4-061f79dfd5d7
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-PKI-Template-Minor-Revision
+ldapDisplayName: msPKI-Template-Minor-Revision
+attributeId: 1.2.840.113556.1.4.1435
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 13f5236c-1884-46b1-b5d0-484e38990d58
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-PKI-Template-Schema-Version
+ldapDisplayName: msPKI-Template-Schema-Version
+attributeId: 1.2.840.113556.1.4.1434
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 0c15e9f5-491d-4594-918f-32813a091da9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-PKI-AccountCredentials
+ldapDisplayName: msPKIAccountCredentials
+attributeId: 1.2.840.113556.1.4.1894
+attributeSyntax: 2.5.5.7
+omSyntax: 127
+omObjectClass: 1.2.840.113556.1.1.1.11
+isSingleValued: FALSE
+schemaIdGuid: b8dfa744-31dc-4ef1-ac7c-84baf7ef9da7
+systemOnly: FALSE
+searchFlags: fCONFIDENTIAL | fRODCFilteredAttribute
+attributeSecurityGuid: 91e647de-d96f-4b70-9557-d63ff4f3ccd8
+linkID: 2048
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-PKI-DPAPIMasterKeys
+ldapDisplayName: msPKIDPAPIMasterKeys
+attributeId: 1.2.840.113556.1.4.1893
+attributeSyntax: 2.5.5.7
+omSyntax: 127
+omObjectClass: 1.2.840.113556.1.1.1.11
+isSingleValued: FALSE
+schemaIdGuid: b3f93023-9239-4f7c-b99c-6745d87adbc2
+systemOnly: FALSE
+searchFlags: fCONFIDENTAIL | fRODCFilteredAttribute
+attributeSecurityGuid: 91e647de-d96f-4b70-9557-d63ff4f3ccd8
+linkID: 2046
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-PKI-RoamingTimeStamp
+ldapDisplayName: msPKIRoamingTimeStamp
+attributeId: 1.2.840.113556.1.4.1892
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 6617e4ac-a2f1-43ab-b60c-11fbd1facf05
+systemOnly: FALSE
+searchFlags: fCONFIDENTIAL | fRODCFilteredAttribute
+attributeSecurityGuid: 91e647de-d96f-4b70-9557-d63ff4f3ccd8
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: msRADIUSCallbackNumber
+ldapDisplayName: msRADIUSCallbackNumber
+attributeId: 1.2.840.113556.1.4.1145
+attributeSyntax: 2.5.5.5
+omSyntax: 22
+isSingleValued: TRUE
+schemaIdGuid: db0c909c-c1f2-11d1-bbc5-0080c76670c0
+systemOnly: FALSE
+searchFlags: fCOPY
+attributeSecurityGuid: 037088f8-0ae1-11d2-b422-00a0c968f939
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: meetingType
+ldapDisplayName: meetingType
+attributeId: 1.2.840.113556.1.4.571
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 11b6cc82-48c4-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-RADIUS-FramedInterfaceId
+ldapDisplayName: msRADIUS-FramedInterfaceId
+attributeId: 1.2.840.113556.1.4.1913
+attributeSyntax: 2.5.5.5
+omSyntax: 22
+isSingleValued: TRUE
+schemaIdGuid: a6f24a23-d65c-4d65-a64f-35fb6873c2b9
+systemOnly: FALSE
+searchFlags: fCOPY
+rangeUpper: 8
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: msRADIUSFramedIPAddress
+ldapDisplayName: msRADIUSFramedIPAddress
+attributeId: 1.2.840.113556.1.4.1153
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: db0c90a4-c1f2-11d1-bbc5-0080c76670c0
+systemOnly: FALSE
+searchFlags: fCOPY
+attributeSecurityGuid: 037088f8-0ae1-11d2-b422-00a0c968f939
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-RADIUS-FramedIpv6Prefix
+ldapDisplayName: msRADIUS-FramedIpv6Prefix
+attributeId: 1.2.840.113556.1.4.1915
+attributeSyntax: 2.5.5.5
+omSyntax: 22
+isSingleValued: TRUE
+schemaIdGuid: f63ed610-d67c-494d-87be-cd1e24359a38
+systemOnly: FALSE
+searchFlags: fCOPY
+rangeUpper: 16
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-RADIUS-FramedIpv6Route
+ldapDisplayName: msRADIUS-FramedIpv6Route
+attributeId: 1.2.840.113556.1.4.1917
+attributeSyntax: 2.5.5.5
+omSyntax: 22
+isSingleValued: FALSE
+schemaIdGuid: 5a5aa804-3083-4863-94e5-018a79a22ec0
+systemOnly: FALSE
+searchFlags: fCOPY
+rangeUpper: 4096
+
+cn: msRADIUSFramedRoute
+ldapDisplayName: msRADIUSFramedRoute
+attributeId: 1.2.840.113556.1.4.1158
+attributeSyntax: 2.5.5.5
+omSyntax: 22
+isSingleValued: FALSE
+schemaIdGuid: db0c90a9-c1f2-11d1-bbc5-0080c76670c0
+systemOnly: FALSE
+searchFlags: fCOPY
+attributeSecurityGuid: 037088f8-0ae1-11d2-b422-00a0c968f939
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-RADIUS-SavedFramedInterfaceId
+ldapDisplayName: msRADIUS-SavedFramedInterfaceId
+attributeId: 1.2.840.113556.1.4.1914
+attributeSyntax: 2.5.5.5
+omSyntax: 22
+isSingleValued: TRUE
+schemaIdGuid: a4da7289-92a3-42e5-b6b6-dad16d280ac9
+systemOnly: FALSE
+searchFlags: fCOPY
+rangeUpper: 8
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-RADIUS-SavedFramedIpv6Prefix
+ldapDisplayName: msRADIUS-SavedFramedIpv6Prefix
+attributeId: 1.2.840.113556.1.4.1916
+attributeSyntax: 2.5.5.5
+omSyntax: 22
+isSingleValued: TRUE
+schemaIdGuid: 0965a062-b1e1-403b-b48d-5c0eb0e952cc
+systemOnly: FALSE
+searchFlags: fCOPY
+rangeUpper: 16
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-RADIUS-SavedFramedIpv6Route
+ldapDisplayName: msRADIUS-SavedFramedIpv6Route
+attributeId: 1.2.840.113556.1.4.1918
+attributeSyntax: 2.5.5.5
+omSyntax: 22
+isSingleValued: FALSE
+schemaIdGuid: 9666bb5c-df9d-4d41-b437-2eec7e27c9b3
+systemOnly: FALSE
+searchFlags: fCOPY
+rangeUpper: 4096
+
+cn: msRADIUSServiceType
+ldapDisplayName: msRADIUSServiceType
+attributeId: 1.2.840.113556.1.4.1171
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: db0c90b6-c1f2-11d1-bbc5-0080c76670c0
+systemOnly: FALSE
+searchFlags: fCOPY
+attributeSecurityGuid: 037088f8-0ae1-11d2-b422-00a0c968f939
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: msRASSavedCallbackNumber
+ldapDisplayName: msRASSavedCallbackNumber
+attributeId: 1.2.840.113556.1.4.1189
+attributeSyntax: 2.5.5.5
+omSyntax: 22
+isSingleValued: TRUE
+schemaIdGuid: db0c90c5-c1f2-11d1-bbc5-0080c76670c0
+systemOnly: FALSE
+searchFlags: fCOPY
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: meetingURL
+ldapDisplayName: meetingURL
+attributeId: 1.2.840.113556.1.4.583
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 11b6cc8c-48c4-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: msRASSavedFramedIPAddress
+ldapDisplayName: msRASSavedFramedIPAddress
+attributeId: 1.2.840.113556.1.4.1190
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: db0c90c6-c1f2-11d1-bbc5-0080c76670c0
+systemOnly: FALSE
+searchFlags: fCOPY
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: msRASSavedFramedRoute
+ldapDisplayName: msRASSavedFramedRoute
+attributeId: 1.2.840.113556.1.4.1191
+attributeSyntax: 2.5.5.5
+omSyntax: 22
+isSingleValued: FALSE
+schemaIdGuid: db0c90c7-c1f2-11d1-bbc5-0080c76670c0
+systemOnly: FALSE
+searchFlags: fCOPY
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-RRAS-Attribute
+ldapDisplayName: msRRASAttribute
+attributeId: 1.2.840.113556.1.4.884
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: f39b98ad-938d-11d1-aebd-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-RRAS-Vendor-Attribute-Entry
+ldapDisplayName: msRRASVendorAttributeEntry
+attributeId: 1.2.840.113556.1.4.883
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: f39b98ac-938d-11d1-aebd-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: msSFU-30-Aliases
+ldapDisplayName: msSFU30Aliases
+attributeId: 1.2.840.113556.1.6.18.1.323
+attributeSyntax: 2.5.5.5
+omSyntax: 22
+isSingleValued: FALSE
+schemaIdGuid: 20ebf171-c69a-4c31-b29d-dcb837d8912d
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 153600
+
+cn: msSFU-30-Crypt-Method
+ldapDisplayName: msSFU30CryptMethod
+attributeId: 1.2.840.113556.1.6.18.1.352
+attributeSyntax: 2.5.5.5
+omSyntax: 22
+isSingleValued: TRUE
+schemaIdGuid: 4503d2a3-3d70-41b8-b077-dff123c15865
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 1024
+
+cn: msSFU-30-Domains
+ldapDisplayName: msSFU30Domains
+attributeId: 1.2.840.113556.1.6.18.1.340
+attributeSyntax: 2.5.5.5
+omSyntax: 22
+isSingleValued: FALSE
+schemaIdGuid: 93095ed3-6f30-4bdd-b734-65d569f5f7c9
+systemOnly: FALSE
+searchFlags: fATTINDEX
+rangeUpper: 256000
+
+cn: msSFU-30-Field-Separator
+ldapDisplayName: msSFU30FieldSeparator
+attributeId: 1.2.840.113556.1.6.18.1.302
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: a2e11a42-e781-4ca1-a7fa-ec307f62b6a1
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 50
+
+cn: msSFU-30-Intra-Field-Separator
+ldapDisplayName: msSFU30IntraFieldSeparator
+attributeId: 1.2.840.113556.1.6.18.1.303
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 95b2aef0-27e4-4cb9-880a-a2d9a9ea23b8
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 50
+
+cn: msSFU-30-Is-Valid-Container
+ldapDisplayName: msSFU30IsValidContainer
+attributeId: 1.2.840.113556.1.6.18.1.350
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 0dea42f5-278d-4157-b4a7-49b59664915b
+systemOnly: FALSE
+searchFlags: fATTINDEX
+
+cn: Member
+ldapDisplayName: member
+attributeId: 2.5.4.31
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: bf9679c0-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+attributeSecurityGuid: bc0ac240-79a9-11d0-9020-00c04fc2d4cf
+linkID: 2
+mapiID: 32777
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT |FLAG_ATTR_REQ_PARTIAL_SET_MEMBER
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: msSFU-30-Key-Attributes
+ldapDisplayName: msSFU30KeyAttributes
+attributeId: 1.2.840.113556.1.6.18.1.301
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 32ecd698-ce9e-4894-a134-7ad76b082e83
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 1024
+
+cn: msSFU-30-Key-Values
+ldapDisplayName: msSFU30KeyValues
+attributeId: 1.2.840.113556.1.6.18.1.324
+attributeSyntax: 2.5.5.5
+omSyntax: 22
+isSingleValued: FALSE
+schemaIdGuid: 37830235-e5e9-46f2-922b-d8d44f03e7ae
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 10240
+
+cn: msSFU-30-Map-Filter
+ldapDisplayName: msSFU30MapFilter
+attributeId: 1.2.840.113556.1.6.18.1.306
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: b7b16e01-024f-4e23-ad0d-71f1a406b684
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 1024
+
+cn: msSFU-30-Master-Server-Name
+ldapDisplayName: msSFU30MasterServerName
+attributeId: 1.2.840.113556.1.6.18.1.307
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 4cc908a2-9e18-410e-8459-f17cc422020a
+systemOnly: FALSE
+searchFlags: fATTINDEX
+rangeUpper: 1024
+
+cn: msSFU-30-Max-Gid-Number
+ldapDisplayName: msSFU30MaxGidNumber
+attributeId: 1.2.840.113556.1.6.18.1.342
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 04ee6aa6-f83b-469a-bf5a-3c00d3634669
+systemOnly: FALSE
+searchFlags: fATTINDEX
+
+cn: msSFU-30-Max-Uid-Number
+ldapDisplayName: msSFU30MaxUidNumber
+attributeId: 1.2.840.113556.1.6.18.1.343
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: ec998437-d944-4a28-8500-217588adfc75
+systemOnly: FALSE
+searchFlags: fATTINDEX
+
+cn: msSFU-30-Name
+ldapDisplayName: msSFU30Name
+attributeId: 1.2.840.113556.1.6.18.1.309
+attributeSyntax: 2.5.5.5
+omSyntax: 22
+isSingleValued: TRUE
+schemaIdGuid: 16c5d1d3-35c2-4061-a870-a5cefda804f0
+systemOnly: FALSE
+searchFlags: fATTINDEX
+rangeUpper: 1024
+
+cn: msSFU-30-Netgroup-Host-At-Domain
+ldapDisplayName: msSFU30NetgroupHostAtDomain
+attributeId: 1.2.840.113556.1.6.18.1.348
+attributeSyntax: 2.5.5.5
+omSyntax: 22
+isSingleValued: FALSE
+schemaIdGuid: 97d2bf65-0466-4852-a25a-ec20f57ee36c
+systemOnly: FALSE
+searchFlags: fATTINDEX
+rangeUpper: 2048
+
+cn: msSFU-30-Netgroup-User-At-Domain
+ldapDisplayName: msSFU30NetgroupUserAtDomain
+attributeId: 1.2.840.113556.1.6.18.1.349
+attributeSyntax: 2.5.5.5
+omSyntax: 22
+isSingleValued: FALSE
+schemaIdGuid: a9e84eed-e630-4b67-b4b3-cad2a82d345e
+systemOnly: FALSE
+searchFlags: fATTINDEX
+rangeUpper: 2048
+
+cn: msSFU-30-Nis-Domain
+ldapDisplayName: msSFU30NisDomain
+attributeId: 1.2.840.113556.1.6.18.1.339
+attributeSyntax: 2.5.5.5
+omSyntax: 22
+isSingleValued: TRUE
+schemaIdGuid: 9ee3b2e3-c7f3-45f8-8c9f-1382be4984d2
+systemOnly: FALSE
+searchFlags: fPRESERVEONDELETE | fATTINDEX
+rangeUpper: 1024
+
+cn: MemberNisNetgroup
+ldapDisplayName: memberNisNetgroup
+attributeId: 1.3.6.1.1.1.1.13
+attributeSyntax: 2.5.5.5
+omSyntax: 22
+isSingleValued: FALSE
+schemaIdGuid: 0f6a17dc-53e5-4be8-9442-8f3ce2f9012a
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 153600
+
+cn: msSFU-30-NSMAP-Field-Position
+ldapDisplayName: msSFU30NSMAPFieldPosition
+attributeId: 1.2.840.113556.1.6.18.1.345
+attributeSyntax: 2.5.5.5
+omSyntax: 22
+isSingleValued: TRUE
+schemaIdGuid: 585c9d5e-f599-4f07-9cf9-4373af4b89d3
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 1024
+
+cn: msSFU-30-Order-Number
+ldapDisplayName: msSFU30OrderNumber
+attributeId: 1.2.840.113556.1.6.18.1.308
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 02625f05-d1ee-4f9f-b366-55266becb95c
+systemOnly: FALSE
+searchFlags: fATTINDEX
+rangeUpper: 1024
+
+cn: msSFU-30-Posix-Member
+ldapDisplayName: msSFU30PosixMember
+attributeId: 1.2.840.113556.1.6.18.1.346
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: c875d82d-2848-4cec-bb50-3c5486d09d57
+systemOnly: FALSE
+searchFlags: 0
+linkID: 2030
+
+cn: msSFU-30-Posix-Member-Of
+ldapDisplayName: msSFU30PosixMemberOf
+attributeId: 1.2.840.113556.1.6.18.1.347
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: 7bd76b92-3244-438a-ada6-24f5ea34381e
+systemOnly: FALSE
+searchFlags: 0
+linkID: 2031
+systemFlags: FLAG_ATTR_NOT_REPLICATED
+
+cn: msSFU-30-Result-Attributes
+ldapDisplayName: msSFU30ResultAttributes
+attributeId: 1.2.840.113556.1.6.18.1.305
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: e167b0b6-4045-4433-ac35-53f972d45cba
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 1024
+
+cn: msSFU-30-Search-Attributes
+ldapDisplayName: msSFU30SearchAttributes
+attributeId: 1.2.840.113556.1.6.18.1.304
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: ef9a2df0-2e57-48c8-8950-0cc674004733
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 1024
+
+cn: msSFU-30-Search-Container
+ldapDisplayName: msSFU30SearchContainer
+attributeId: 1.2.840.113556.1.6.18.1.300
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 27eebfa2-fbeb-4f8e-aad6-c50247994291
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 2048
+
+cn: msSFU-30-Yp-Servers
+ldapDisplayName: msSFU30YpServers
+attributeId: 1.2.840.113556.1.6.18.1.341
+attributeSyntax: 2.5.5.5
+omSyntax: 22
+isSingleValued: FALSE
+schemaIdGuid: 084a944b-e150-4bfe-9345-40e1aedaebba
+systemOnly: FALSE
+searchFlags: fATTINDEX
+rangeUpper: 20480
+
+cn: ms-TAPI-Conference-Blob
+ldapDisplayName: msTAPI-ConferenceBlob
+attributeId: 1.2.840.113556.1.4.1700
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 4cc4601e-7201-4141-abc8-3e529ae88863
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-TAPI-Ip-Address
+ldapDisplayName: msTAPI-IpAddress
+attributeId: 1.2.840.113556.1.4.1701
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: efd7d7f7-178e-4767-87fa-f8a16b840544
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Is-Member-Of-DL
+ldapDisplayName: memberOf
+attributeId: 1.2.840.113556.1.2.102
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: bf967991-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+searchFlags: fCOPY
+attributeSecurityGuid: bc0ac240-79a9-11d0-9020-00c04fc2d4cf
+linkID: 3
+mapiID: 32776
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_NOT_REPLICATED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-TAPI-Protocol-Id
+ldapDisplayName: msTAPI-ProtocolId
+attributeId: 1.2.840.113556.1.4.1699
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 89c1ebcf-7a5f-41fd-99ca-c900b32299ab
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-TAPI-Unique-Identifier
+ldapDisplayName: msTAPI-uid
+attributeId: 1.2.840.113556.1.4.1698
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 70a4e7ea-b3b9-4643-8918-e6dd2471bfd4
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 256
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-TPM-OwnerInformation
+ldapDisplayName: msTPM-OwnerInformation
+attributeId: 1.2.840.113556.1.4.1966
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: aa4e1a6d-550d-4e05-8c35-4afcb917a9fe
+searchFlags: fRODCFilteredAttribute | fCOPY | fPRESERVEONDELETE |fPDNTATTINDEX | fATTINDEX
+rangeUpper: 128
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-TS-Allow-Logon
+ldapDisplayName: msTSAllowLogon
+attributeId: 1.2.840.113556.1.4.1979
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: 3a0cd464-bc54-40e7-93ae-a646a6ecc4b4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-TS-Broken-Connection-Action
+ldapDisplayName: msTSBrokenConnectionAction
+attributeId: 1.2.840.113556.1.4.1985
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: 1cf41bba-5604-463e-94d6-1a1287b72ca3
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-TS-Connect-Client-Drives
+ldapDisplayName: msTSConnectClientDrives
+attributeId: 1.2.840.113556.1.4.1986
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: 23572aaf-29dd-44ea-b0fa-7e8438b9a4a3
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-TS-Connect-Printer-Drives
+ldapDisplayName: msTSConnectPrinterDrives
+attributeId: 1.2.840.113556.1.4.1987
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: 8ce6a937-871b-4c92-b285-d99d4036681c
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-TS-Default-To-Main-Printer
+ldapDisplayName: msTSDefaultToMainPrinter
+attributeId: 1.2.840.113556.1.4.1988
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: c0ffe2bd-cacf-4dc7-88d5-61e9e95766f6
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-TS-ExpireDate
+ldapDisplayName: msTSExpireDate
+attributeId: 1.2.840.113556.1.4.1993
+attributeSyntax: 2.5.5.11
+omSyntax: 24
+isSingleValued: TRUE
+schemaIdGuid: 70004ef5-25c3-446a-97c8-996ae8566776
+systemOnly: FALSE
+searchFlags: fATTINDEX
+attributeSecurityGuid: 5805bc62-bdc9-4428-a5e2-856a0f4c185e
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+showInAdvancedViewOnly: TRUE
+
+cn: MS-TS-ExpireDate2
+ldapDisplayName: msTSExpireDate2
+attributeId: 1.2.840.113556.1.4.2000
+attributeSyntax: 2.5.5.11
+omSyntax: 24
+isSingleValued: TRUE
+schemaIdGuid: 54dfcf71-bc3f-4f0b-9d5a-4b2476bb8925
+systemOnly: FALSE
+searchFlags: fATTINDEX
+attributeSecurityGuid: 5805bc62-bdc9-4428-a5e2-856a0f4c185e
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+showInAdvancedViewOnly: TRUE
+
+cn: MemberUid
+ldapDisplayName: memberUid
+attributeId: 1.3.6.1.1.1.1.12
+attributeSyntax: 2.5.5.5
+omSyntax: 22
+isSingleValued: FALSE
+schemaIdGuid: 03dab236-672e-4f61-ab64-f77d2dc2ffab
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 256000
+
+cn: MS-TS-ExpireDate3
+ldapDisplayName: msTSExpireDate3
+attributeId: 1.2.840.113556.1.4.2003
+attributeSyntax: 2.5.5.11
+omSyntax: 24
+isSingleValued: TRUE
+schemaIdGuid: 41bc7f04-be72-4930-bd10-1f3439412387
+systemOnly: FALSE
+searchFlags: fATTINDEX
+attributeSecurityGuid: 5805bc62-bdc9-4428-a5e2-856a0f4c185e
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+showInAdvancedViewOnly: TRUE
+
+cn: MS-TS-ExpireDate4
+ldapDisplayName: msTSExpireDate4
+attributeId: 1.2.840.113556.1.4.2006
+attributeSyntax: 2.5.5.11
+omSyntax: 24
+isSingleValued: TRUE
+schemaIdGuid: 5e11dc43-204a-4faf-a008-6863621c6f5f
+systemOnly: FALSE
+searchFlags: fATTINDEX
+attributeSecurityGuid: 5805bc62-bdc9-4428-a5e2-856a0f4c185e
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+showInAdvancedViewOnly: TRUE
+
+cn: ms-TS-Home-Directory
+ldapDisplayName: msTSHomeDirectory
+attributeId: 1.2.840.113556.1.4.1977
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 5d3510f0-c4e7-4122-b91f-a20add90e246
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 32767
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-TS-Home-Drive
+ldapDisplayName: msTSHomeDrive
+attributeId: 1.2.840.113556.1.4.1978
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 5f0a24d9-dffa-4cd9-acbf-a0680c03731e
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 32767
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-TS-Initial-Program
+ldapDisplayName: msTSInitialProgram
+attributeId: 1.2.840.113556.1.4.1990
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 9201ac6f-1d69-4dfb-802e-d95510109599
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 32767
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-TS-LicenseVersion
+ldapDisplayName: msTSLicenseVersion
+attributeId: 1.2.840.113556.1.4.1994
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 0ae94a89-372f-4df2-ae8a-c64a2bc47278
+systemOnly: FALSE
+searchFlags: fATTINDEX
+attributeSecurityGuid: 5805bc62-bdc9-4428-a5e2-856a0f4c185e
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-TS-LicenseVersion2
+ldapDisplayName: msTSLicenseVersion2
+attributeId: 1.2.840.113556.1.4.2001
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 4b0df103-8d97-45d9-ad69-85c3080ba4e7
+systemOnly: FALSE
+searchFlags: fATTINDEX
+rangeLower: 0
+rangeUpper: 255
+attributeSecurityGuid: 5805bc62-bdc9-4428-a5e2-856a0f4c185e
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-TS-LicenseVersion3
+ldapDisplayName: msTSLicenseVersion3
+attributeId: 1.2.840.113556.1.4.2004
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: f8ba8f81-4cab-4973-a3c8-3a6da62a5e31
+systemOnly: FALSE
+searchFlags: fATTINDEX
+rangeLower: 0
+rangeUpper: 255
+attributeSecurityGuid: 5805bc62-bdc9-4428-a5e2-856a0f4c185e
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-TS-LicenseVersion4
+ldapDisplayName: msTSLicenseVersion4
+attributeId: 1.2.840.113556.1.4.2007
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 70ca5d97-2304-490a-8a27-52678c8d2095
+systemOnly: FALSE
+searchFlags: fATTINDEX
+rangeLower: 0
+rangeUpper: 255
+attributeSecurityGuid: 5805bc62-bdc9-4428-a5e2-856a0f4c185e
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-TSLS-Property01
+ldapDisplayName: msTSLSProperty01
+attributeId: 1.2.840.113556.1.4.2009
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+systemOnly: FALSE
+searchFlags: fATTINDEX
+rangeLower: 0
+rangeUpper: 32767
+schemaIdGuid: 87e53590-971d-4a52-955b-4794d15a84ae
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MHS-OR-Address
+ldapDisplayName: mhsORAddress
+attributeId: 1.2.840.113556.1.4.650
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 0296c122-40da-11d1-a9c0-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-TSLS-Property02
+ldapDisplayName: msTSLSProperty02
+attributeId: 1.2.840.113556.1.4.2010
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+systemOnly: FALSE
+searchFlags: fATTINDEX
+rangeLower: 0
+rangeUpper: 32767
+schemaIdGuid: 47c77bb0-316e-4e2f-97f1-0d4c48fca9dd
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-TS-ManagingLS
+ldapDisplayName: msTSManagingLS
+attributeId: 1.2.840.113556.1.4.1995
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: f3bcc547-85b0-432c-9ac0-304506bf2c83
+systemOnly: FALSE
+searchFlags: fATTINDEX
+attributeSecurityGuid: 5805bc62-bdc9-4428-a5e2-856a0f4c185e
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-TS-ManagingLS2
+ldapDisplayName: msTSManagingLS2
+attributeId: 1.2.840.113556.1.4.2002
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+systemOnly: FALSE
+rangeLower: 0
+rangeUpper: 255
+schemaIdGuid: 349f0757-51bd-4fc8-9d66-3eceea8a25be
+searchFlags: fATTINDEX
+attributeSecurityGuid: 5805bc62-bdc9-4428-a5e2-856a0f4c185e
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-TS-ManagingLS3
+ldapDisplayName: msTSManagingLS3
+attributeId: 1.2.840.113556.1.4.2005
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+systemOnly: FALSE
+rangeLower: 0
+rangeUpper: 255
+schemaIdGuid: fad5dcc1-2130-4c87-a118-75322cd67050
+searchFlags: fATTINDEX
+attributeSecurityGuid: 5805bc62-bdc9-4428-a5e2-856a0f4c185e
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-TS-ManagingLS4
+ldapDisplayName: msTSManagingLS4
+attributeId: 1.2.840.113556.1.4.2008
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+systemOnly: FALSE
+rangeLower: 0
+rangeUpper: 255
+schemaIdGuid: f7a3b6a0-2107-4140-b306-75cb521731e5
+searchFlags: fATTINDEX
+attributeSecurityGuid: 5805bc62-bdc9-4428-a5e2-856a0f4c185e
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-TS-Max-Connection-Time
+ldapDisplayName: msTSMaxConnectionTime
+attributeId: 1.2.840.113556.1.4.1982
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 1d960ee2-6464-4e95-a781-e3b5cd5f9588
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-TS-Max-Disconnection-Time
+ldapDisplayName: msTSMaxDisconnectionTime
+attributeId: 1.2.840.113556.1.4.1981
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 326f7089-53d8-4784-b814-46d8535110d2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-TS-Max-Idle-Time
+ldapDisplayName: msTSMaxIdleTime
+attributeId: 1.2.840.113556.1.4.1983
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: ff739e9c-6bb7-460e-b221-e250f3de0f95
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-TS-Profile-Path
+ldapDisplayName: msTSProfilePath
+attributeId: 1.2.840.113556.1.4.1976
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: e65c30db-316c-4060-a3a0-387b083f09cd
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 32767
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-TS-Property01
+ldapDisplayName: msTSProperty01
+attributeId: 1.2.840.113556.1.4.1991
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: faaea977-9655-49d7-853d-f27bb7aaca0f
+systemOnly: FALSE
+searchFlags: fATTINDEX
+rangeLower: 0
+rangeUpper: 32767
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Other-Name
+ldapDisplayName: middleName
+attributeId: 2.16.840.1.113730.3.1.34
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: bf9679f2-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 64
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-TS-Property02
+ldapDisplayName: msTSProperty02
+attributeId: 1.2.840.113556.1.4.1992
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 3586f6ac-51b7-4978-ab42-f936463198e7
+systemOnly: FALSE
+searchFlags: fATTINDEX
+rangeLower: 0
+rangeUpper: 32767
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-TS-Reconnection-Action
+ldapDisplayName: msTSReconnectionAction
+attributeId: 1.2.840.113556.1.4.1984
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: 366ed7ca-3e18-4c7f-abae-351a01e4b4f7
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-TS-Remote-Control
+ldapDisplayName: msTSRemoteControl
+attributeId: 1.2.840.113556.1.4.1980
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 15177226-8642-468b-8c48-03ddfd004982
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-TS-Work-Directory
+ldapDisplayName: msTSWorkDirectory
+attributeId: 1.2.840.113556.1.4.1989
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: a744f666-3d3c-4cc8-834b-9d4f6f687b8b
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 32767
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-WMI-Author
+ldapDisplayName: msWMI-Author
+attributeId: 1.2.840.113556.1.4.1623
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 6366c0c1-6972-4e66-b3a5-1d52ad0c0547
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-WMI-ChangeDate
+ldapDisplayName: msWMI-ChangeDate
+attributeId: 1.2.840.113556.1.4.1624
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: f9cdf7a0-ec44-4937-a79b-cd91522b3aa8
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-WMI-Class
+ldapDisplayName: msWMI-Class
+attributeId: 1.2.840.113556.1.4.1676
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 90c1925f-4a24-4b07-b202-be32eb3c8b74
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-WMI-ClassDefinition
+ldapDisplayName: msWMI-ClassDefinition
+attributeId: 1.2.840.113556.1.4.1625
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 2b9c0ebc-c272-45cb-99d2-4d0e691632e0
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-WMI-CreationDate
+ldapDisplayName: msWMI-CreationDate
+attributeId: 1.2.840.113556.1.4.1626
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 748b0a2e-3351-4b3f-b171-2f17414ea779
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-WMI-Genus
+ldapDisplayName: msWMI-Genus
+attributeId: 1.2.840.113556.1.4.1677
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 50c8673a-8f56-4614-9308-9e1340fb9af3
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Min-Pwd-Age
+ldapDisplayName: minPwdAge
+attributeId: 1.2.840.113556.1.4.78
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: bf9679c2-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+attributeSecurityGuid: c7407360-20bf-11d0-a768-00aa006e0529
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-WMI-ID
+ldapDisplayName: msWMI-ID
+attributeId: 1.2.840.113556.1.4.1627
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 9339a803-94b8-47f7-9123-a853b9ff7e45
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-WMI-int8Default
+ldapDisplayName: msWMI-Int8Default
+attributeId: 1.2.840.113556.1.4.1632
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: f4d8085a-8c5b-4785-959b-dc585566e445
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-WMI-int8Max
+ldapDisplayName: msWMI-Int8Max
+attributeId: 1.2.840.113556.1.4.1633
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: e3d8b547-003d-4946-a32b-dc7cedc96b74
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-WMI-int8Min
+ldapDisplayName: msWMI-Int8Min
+attributeId: 1.2.840.113556.1.4.1634
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: ed1489d1-54cc-4066-b368-a00daa2664f1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-WMI-int8ValidValues
+ldapDisplayName: msWMI-Int8ValidValues
+attributeId: 1.2.840.113556.1.4.1635
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: FALSE
+schemaIdGuid: 103519a9-c002-441b-981a-b0b3e012c803
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-WMI-intDefault
+ldapDisplayName: msWMI-IntDefault
+attributeId: 1.2.840.113556.1.4.1628
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 1b0c07f8-76dd-4060-a1e1-70084619dc90
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-WMI-intFlags1
+ldapDisplayName: msWMI-intFlags1
+attributeId: 1.2.840.113556.1.4.1678
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 18e006b9-6445-48e3-9dcf-b5ecfbc4df8e
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-WMI-intFlags2
+ldapDisplayName: msWMI-intFlags2
+attributeId: 1.2.840.113556.1.4.1679
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 075a42c9-c55a-45b1-ac93-eb086b31f610
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-WMI-intFlags3
+ldapDisplayName: msWMI-intFlags3
+attributeId: 1.2.840.113556.1.4.1680
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: f29fa736-de09-4be4-b23a-e734c124bacc
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-WMI-intFlags4
+ldapDisplayName: msWMI-intFlags4
+attributeId: 1.2.840.113556.1.4.1681
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: bd74a7ac-c493-4c9c-bdfa-5c7b119ca6b2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Machine-Wide-Policy
+ldapDisplayName: machineWidePolicy
+attributeId: 1.2.840.113556.1.4.459
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: FALSE
+schemaIdGuid: 80a67e4f-9f22-11d0-afdd-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+attributeSecurityGuid: a29b8a01-c7e8-11d0-9bae-00c04fd92ef5
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Min-Pwd-Length
+ldapDisplayName: minPwdLength
+attributeId: 1.2.840.113556.1.4.79
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: bf9679c3-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+attributeSecurityGuid: c7407360-20bf-11d0-a768-00aa006e0529
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-WMI-intMax
+ldapDisplayName: msWMI-IntMax
+attributeId: 1.2.840.113556.1.4.1629
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: fb920c2c-f294-4426-8ac1-d24b42aa2bce
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-WMI-intMin
+ldapDisplayName: msWMI-IntMin
+attributeId: 1.2.840.113556.1.4.1630
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 68c2e3ba-9837-4c70-98e0-f0c33695d023
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-WMI-intValidValues
+ldapDisplayName: msWMI-IntValidValues
+attributeId: 1.2.840.113556.1.4.1631
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: FALSE
+schemaIdGuid: 6af565f6-a749-4b72-9634-3c5d47e6b4e0
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-WMI-Mof
+ldapDisplayName: msWMI-Mof
+attributeId: 1.2.840.113556.1.4.1638
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 6736809f-2064-443e-a145-81262b1f1366
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-WMI-Name
+ldapDisplayName: msWMI-Name
+attributeId: 1.2.840.113556.1.4.1639
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: c6c8ace5-7e81-42af-ad72-77412c5941c4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-WMI-NormalizedClass
+ldapDisplayName: msWMI-NormalizedClass
+attributeId: 1.2.840.113556.1.4.1640
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: eaba628f-eb8e-4fe9-83fc-693be695559b
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-WMI-Parm1
+ldapDisplayName: msWMI-Parm1
+attributeId: 1.2.840.113556.1.4.1682
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 27e81485-b1b0-4a8b-bedd-ce19a837e26e
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-WMI-Parm2
+ldapDisplayName: msWMI-Parm2
+attributeId: 1.2.840.113556.1.4.1683
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 0003508e-9c42-4a76-a8f4-38bf64bab0de
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-WMI-Parm3
+ldapDisplayName: msWMI-Parm3
+attributeId: 1.2.840.113556.1.4.1684
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 45958fb6-52bd-48ce-9f9f-c2712d9f2bfc
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-WMI-Parm4
+ldapDisplayName: msWMI-Parm4
+attributeId: 1.2.840.113556.1.4.1685
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 3800d5a3-f1ce-4b82-a59a-1528ea795f59
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Min-Ticket-Age
+ldapDisplayName: minTicketAge
+attributeId: 1.2.840.113556.1.4.80
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: bf9679c4-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-WMI-PropertyName
+ldapDisplayName: msWMI-PropertyName
+attributeId: 1.2.840.113556.1.4.1641
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: ab920883-e7f8-4d72-b4a0-c0449897509d
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-WMI-Query
+ldapDisplayName: msWMI-Query
+attributeId: 1.2.840.113556.1.4.1642
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 65fff93e-35e3-45a3-85ae-876c6718297f
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-WMI-QueryLanguage
+ldapDisplayName: msWMI-QueryLanguage
+attributeId: 1.2.840.113556.1.4.1643
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 7d3cfa98-c17b-4254-8bd7-4de9b932a345
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-WMI-ScopeGuid
+ldapDisplayName: msWMI-ScopeGuid
+attributeId: 1.2.840.113556.1.4.1686
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 87b78d51-405f-4b7f-80ed-2bd28786f48d
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-WMI-SourceOrganization
+ldapDisplayName: msWMI-SourceOrganization
+attributeId: 1.2.840.113556.1.4.1644
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 34f7ed6c-615d-418d-aa00-549a7d7be03e
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-WMI-stringDefault
+ldapDisplayName: msWMI-StringDefault
+attributeId: 1.2.840.113556.1.4.1636
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 152e42b6-37c5-4f55-ab48-1606384a9aea
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-WMI-stringValidValues
+ldapDisplayName: msWMI-StringValidValues
+attributeId: 1.2.840.113556.1.4.1637
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 37609d31-a2bf-4b58-8f53-2b64e57a076d
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-WMI-TargetClass
+ldapDisplayName: msWMI-TargetClass
+attributeId: 1.2.840.113556.1.4.1645
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 95b6d8d6-c9e8-4661-a2bc-6a5cabc04c62
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-WMI-TargetNameSpace
+ldapDisplayName: msWMI-TargetNameSpace
+attributeId: 1.2.840.113556.1.4.1646
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 1c4ab61f-3420-44e5-849d-8b5dbf60feb7
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-WMI-TargetObject
+ldapDisplayName: msWMI-TargetObject
+attributeId: 1.2.840.113556.1.4.1647
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: FALSE
+schemaIdGuid: c44f67a5-7de5-4a1f-92d9-662b57364b77
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Phone-Mobile-Primary
+ldapDisplayName: mobile
+attributeId: 0.9.2342.19200300.100.1.41
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: f0f8ffa3-1191-11d0-a060-00aa006c33ed
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 64
+attributeSecurityGuid: 77b5b886-944a-11d1-aebd-0000f80367c1
+mapiID: 14876
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-WMI-TargetPath
+ldapDisplayName: msWMI-TargetPath
+attributeId: 1.2.840.113556.1.4.1648
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 5006a79a-6bfe-4561-9f52-13cf4dd3e560
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-WMI-TargetType
+ldapDisplayName: msWMI-TargetType
+attributeId: 1.2.840.113556.1.4.1649
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: ca2a281e-262b-4ff7-b419-bc123352a4e9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Must-Contain
+ldapDisplayName: mustContain
+attributeId: 1.2.840.113556.1.2.24
+attributeSyntax: 2.5.5.2
+omSyntax: 6
+isSingleValued: FALSE
+schemaIdGuid: bf9679d3-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Modified-Count
+ldapDisplayName: modifiedCount
+attributeId: 1.2.840.113556.1.4.168
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: bf9679c5-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+attributeSecurityGuid: b8119fd0-04f6-4762-ab7a-4986c76b3f9a
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_NOT_REPLICATED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Modified-Count-At-Last-Prom
+ldapDisplayName: modifiedCountAtLastProm
+attributeId: 1.2.840.113556.1.4.81
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: bf9679c6-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Modify-Time-Stamp
+ldapDisplayName: modifyTimeStamp
+attributeId: 2.5.18.2
+attributeSyntax: 2.5.5.11
+omSyntax: 24
+isSingleValued: TRUE
+schemaIdGuid: 9a7ad94a-ca53-11d1-bbd0-0080c76670c0
+systemOnly: TRUE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_IS_CONSTRUCTED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Moniker
+ldapDisplayName: moniker
+attributeId: 1.2.840.113556.1.4.82
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: FALSE
+schemaIdGuid: bf9679c7-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Moniker-Display-Name
+ldapDisplayName: monikerDisplayName
+attributeId: 1.2.840.113556.1.4.83
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: bf9679c8-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Move-Tree-State
+ldapDisplayName: moveTreeState
+attributeId: 1.2.840.113556.1.4.1305
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: FALSE
+schemaIdGuid: 1f2ac2c8-3b71-11d2-90cc-00c04fd91ab1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-DS-Consistency-Child-Count
+ldapDisplayName: mS-DS-ConsistencyChildCount
+attributeId: 1.2.840.113556.1.4.1361
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 178b7bc2-b63a-11d2-90e1-00c04fd91ab1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: E-mail-Addresses
+ldapDisplayName: mail
+attributeId: 0.9.2342.19200300.100.1.3
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: bf967961-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: fATTINDEX
+rangeLower: 0
+rangeUpper: 256
+attributeSecurityGuid: e48d0154-bcf8-11d1-8702-00c04fb96050
+mapiID: 14846
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-DS-Consistency-Guid
+ldapDisplayName: mS-DS-ConsistencyGuid
+attributeId: 1.2.840.113556.1.4.1360
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 23773dc2-b63a-11d2-90e1-00c04fd91ab1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-DS-Creator-SID
+ldapDisplayName: mS-DS-CreatorSID
+attributeId: 1.2.840.113556.1.4.1410
+attributeSyntax: 2.5.5.17
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: c5e60132-1480-11d3-91c1-0000f87a57d4
+systemOnly: TRUE
+searchFlags: fATTINDEX
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: MS-DS-Machine-Account-Quota
+ldapDisplayName: ms-DS-MachineAccountQuota
+attributeId: 1.2.840.113556.1.4.1411
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: d064fb68-1480-11d3-91c1-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: MS-DS-Replicates-NC-Reason
+ldapDisplayName: mS-DS-ReplicatesNCReason
+attributeId: 1.2.840.113556.1.4.1408
+attributeSyntax: 2.5.5.7
+omSyntax: 127
+omObjectClass: 1.2.840.113556.1.1.1.11
+isSingleValued: FALSE
+schemaIdGuid: 0ea12b84-08b3-11d3-91bc-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: ms-net-ieee-80211-GP-PolicyData
+ldapDisplayName: ms-net-ieee-80211-GP-PolicyData
+attributeId: 1.2.840.113556.1.4.1952
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 9c1495a5-4d76-468e-991e-1433b0a67855
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 4194304
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-net-ieee-80211-GP-PolicyGUID
+ldapDisplayName: ms-net-ieee-80211-GP-PolicyGUID
+attributeId: 1.2.840.113556.1.4.1951
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 35697062-1eaf-448b-ac1e-388e0be4fdee
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 64
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-net-ieee-80211-GP-PolicyReserved
+ldapDisplayName: ms-net-ieee-80211-GP-PolicyReserved
+attributeId: 1.2.840.113556.1.4.1953
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 0f69c62e-088e-4ff5-a53a-e923cec07c0a
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 4194304
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-net-ieee-8023-GP-PolicyData
+ldapDisplayName: ms-net-ieee-8023-GP-PolicyData
+attributeId: 1.2.840.113556.1.4.1955
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 8398948b-7457-4d91-bd4d-8d7ed669c9f7
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 1048576
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-net-ieee-8023-GP-PolicyGUID
+ldapDisplayName: ms-net-ieee-8023-GP-PolicyGUID
+attributeId: 1.2.840.113556.1.4.1954
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 94a7b05a-b8b2-4f59-9c25-39e69baa1684
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 64
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-net-ieee-8023-GP-PolicyReserved
+ldapDisplayName: ms-net-ieee-8023-GP-PolicyReserved
+attributeId: 1.2.840.113556.1.4.1956
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: d3c527c7-2606-4deb-8cfd-18426feec8ce
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 1048576
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: SMTP-Mail-Address
+ldapDisplayName: mailAddress
+attributeId: 1.2.840.113556.1.4.786
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 26d9736f-6070-11d1-a9c6-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: MS-SQL-Alias
+ldapDisplayName: mS-SQL-Alias
+attributeId: 1.2.840.113556.1.4.1395
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: e0c6baae-ccee-11d2-9993-0000f87a57d4
+systemOnly: FALSE
+searchFlags: fATTINDEX
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-SQL-AllowAnonymousSubscription
+ldapDisplayName: mS-SQL-AllowAnonymousSubscription
+attributeId: 1.2.840.113556.1.4.1394
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: db77be4a-ccee-11d2-9993-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-SQL-AllowImmediateUpdatingSubscription
+ldapDisplayName: mS-SQL-AllowImmediateUpdatingSubscription
+attributeId: 1.2.840.113556.1.4.1404
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: c4186b6e-d34b-11d2-999a-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-SQL-AllowKnownPullSubscription
+ldapDisplayName: mS-SQL-AllowKnownPullSubscription
+attributeId: 1.2.840.113556.1.4.1403
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: c3bb7054-d34b-11d2-999a-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-SQL-AllowQueuedUpdatingSubscription
+ldapDisplayName: mS-SQL-AllowQueuedUpdatingSubscription
+attributeId: 1.2.840.113556.1.4.1405
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: c458ca80-d34b-11d2-999a-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-SQL-AllowSnapshotFilesFTPDownloading
+ldapDisplayName: mS-SQL-AllowSnapshotFilesFTPDownloading
+attributeId: 1.2.840.113556.1.4.1406
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: c49b8be8-d34b-11d2-999a-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-SQL-AppleTalk
+ldapDisplayName: mS-SQL-AppleTalk
+attributeId: 1.2.840.113556.1.4.1378
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 8fda89f4-ccee-11d2-9993-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-SQL-Applications
+ldapDisplayName: mS-SQL-Applications
+attributeId: 1.2.840.113556.1.4.1400
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: fbcda2ea-ccee-11d2-9993-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-SQL-Build
+ldapDisplayName: mS-SQL-Build
+attributeId: 1.2.840.113556.1.4.1368
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 603e94c4-ccee-11d2-9993-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-SQL-CharacterSet
+ldapDisplayName: mS-SQL-CharacterSet
+attributeId: 1.2.840.113556.1.4.1370
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 696177a6-ccee-11d2-9993-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Managed-By
+ldapDisplayName: managedBy
+attributeId: 1.2.840.113556.1.4.653
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: TRUE
+schemaIdGuid: 0296c120-40da-11d1-a9c0-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+linkID: 72
+mapiID: 32780
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: MS-SQL-Clustered
+ldapDisplayName: mS-SQL-Clustered
+attributeId: 1.2.840.113556.1.4.1373
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: 7778bd90-ccee-11d2-9993-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-SQL-ConnectionURL
+ldapDisplayName: mS-SQL-ConnectionURL
+attributeId: 1.2.840.113556.1.4.1383
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: a92d23da-ccee-11d2-9993-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-SQL-Contact
+ldapDisplayName: mS-SQL-Contact
+attributeId: 1.2.840.113556.1.4.1365
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 4f6cbdd8-ccee-11d2-9993-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-SQL-CreationDate
+ldapDisplayName: mS-SQL-CreationDate
+attributeId: 1.2.840.113556.1.4.1397
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: ede14754-ccee-11d2-9993-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-SQL-Database
+ldapDisplayName: mS-SQL-Database
+attributeId: 1.2.840.113556.1.4.1393
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: d5a0dbdc-ccee-11d2-9993-0000f87a57d4
+systemOnly: FALSE
+searchFlags: fATTINDEX
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-SQL-Description
+ldapDisplayName: mS-SQL-Description
+attributeId: 1.2.840.113556.1.4.1390
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 8386603c-ccef-11d2-9993-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-SQL-GPSHeight
+ldapDisplayName: mS-SQL-GPSHeight
+attributeId: 1.2.840.113556.1.4.1387
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: bcdd4f0e-ccee-11d2-9993-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-SQL-GPSLatitude
+ldapDisplayName: mS-SQL-GPSLatitude
+attributeId: 1.2.840.113556.1.4.1385
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: b222ba0e-ccee-11d2-9993-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-SQL-GPSLongitude
+ldapDisplayName: mS-SQL-GPSLongitude
+attributeId: 1.2.840.113556.1.4.1386
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: b7577c94-ccee-11d2-9993-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-SQL-InformationDirectory
+ldapDisplayName: mS-SQL-InformationDirectory
+attributeId: 1.2.840.113556.1.4.1392
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: d0aedb2e-ccee-11d2-9993-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Managed-Objects
+ldapDisplayName: managedObjects
+attributeId: 1.2.840.113556.1.4.654
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: 0296c124-40da-11d1-a9c0-0000f80367c1
+systemOnly: TRUE
+searchFlags: 0
+linkID: 73
+mapiID: 32804
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_NOT_REPLICATED
+
+cn: MS-SQL-InformationURL
+ldapDisplayName: mS-SQL-InformationURL
+attributeId: 1.2.840.113556.1.4.1382
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: a42cd510-ccee-11d2-9993-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-SQL-Keywords
+ldapDisplayName: mS-SQL-Keywords
+attributeId: 1.2.840.113556.1.4.1401
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 01e9a98a-ccef-11d2-9993-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-SQL-Language
+ldapDisplayName: mS-SQL-Language
+attributeId: 1.2.840.113556.1.4.1389
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: c57f72f4-ccee-11d2-9993-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-SQL-LastBackupDate
+ldapDisplayName: mS-SQL-LastBackupDate
+attributeId: 1.2.840.113556.1.4.1398
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: f2b6abca-ccee-11d2-9993-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-SQL-LastDiagnosticDate
+ldapDisplayName: mS-SQL-LastDiagnosticDate
+attributeId: 1.2.840.113556.1.4.1399
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: f6d6dd88-ccee-11d2-9993-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-SQL-LastUpdatedDate
+ldapDisplayName: mS-SQL-LastUpdatedDate
+attributeId: 1.2.840.113556.1.4.1381
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 9fcc43d4-ccee-11d2-9993-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-SQL-Location
+ldapDisplayName: mS-SQL-Location
+attributeId: 1.2.840.113556.1.4.1366
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 561c9644-ccee-11d2-9993-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-SQL-Memory
+ldapDisplayName: mS-SQL-Memory
+attributeId: 1.2.840.113556.1.4.1367
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: 5b5d448c-ccee-11d2-9993-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-SQL-MultiProtocol
+ldapDisplayName: mS-SQL-MultiProtocol
+attributeId: 1.2.840.113556.1.4.1375
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 8157fa38-ccee-11d2-9993-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-SQL-Name
+ldapDisplayName: mS-SQL-Name
+attributeId: 1.2.840.113556.1.4.1363
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 3532dfd8-ccee-11d2-9993-0000f87a57d4
+systemOnly: FALSE
+searchFlags: fATTINDEX
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-DFS-Comment-v2
+ldapDisplayName: msDFS-Commentv2
+attributeId: 1.2.840.113556.1.4.2036
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 4fb42f00-29bd-4f82-b94b-07c7fa61e449
+isMemberOfPartialAttributeSet: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 32766
+
+cn: ms-DFS-Generation-GUID-v2
+ldapDisplayName: msDFS-GenerationGUIDv2
+attributeId: 1.2.840.113556.1.4.2032
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 62a45d41-424c-4905-b728-e5ef1fc4fe42
+isMemberOfPartialAttributeSet: FALSE
+searchFlags: 0
+rangeLower: 16
+rangeUpper: 16
+
+cn: ms-DFS-Last-Modified-v2
+ldapDisplayName: msDFS-LastModifiedv2
+attributeId: 1.2.840.113556.1.4.2034
+attributeSyntax: 2.5.5.11
+omSyntax: 24
+isSingleValued: TRUE
+schemaIdGuid: d6147e9b-b369-4b98-9f7b-1f345bb0680a
+isMemberOfPartialAttributeSet: FALSE
+searchFlags: 0
+
+cn: ms-DFS-Link-Identity-GUID-v2
+ldapDisplayName: msDFS-LinkIdentityGUIDv2
+attributeId: 1.2.840.113556.1.4.2041
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 19e2bd91-e8fa-49b2-be2b-7efd5ae5676d
+isMemberOfPartialAttributeSet: FALSE
+searchFlags: 0
+rangeLower:16
+rangeUpper: 16
+
+cn: ms-DFS-Link-Path-v2
+ldapDisplayName: msDFS-LinkPathv2
+attributeId: 1.2.840.113556.1.4.2039
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 5882bb1e-3101-4845-a21e-1516e59279f2
+isMemberOfPartialAttributeSet: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 32766
+
+cn: ms-DFS-Link-Security-Descriptor-v2
+ldapDisplayName: msDFS-LinkSecurityDescriptorv2
+attributeId: 1.2.840.113556.1.4.2040
+attributeSyntax: 2.5.5.15
+omSyntax: 66
+isSingleValued: TRUE
+schemaIdGuid: d780b945-3caa-4d28-975b-eb3f08e455e1
+isMemberOfPartialAttributeSet: FALSE
+searchFlags: 0
+
+cn: ms-DFS-Namespace-Identity-GUID-v2
+ldapDisplayName: msDFS-NamespaceIdentityGUIDv2
+attributeId: 1.2.840.113556.1.4.2033
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 87011f22-e651-4c27-b55b-51daf9f9d364
+isMemberOfPartialAttributeSet: FALSE
+searchFlags: 0
+rangeLower: 16
+rangeUpper: 16
+
+cn: ms-DFS-Properties-v2
+ldapDisplayName: msDFS-Propertiesv2
+attributeId: 1.2.840.113556.1.4.2037
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 1c070014-ebf6-4088-95b4-28b16cc31241
+isMemberOfPartialAttributeSet: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 1024
+
+cn: ms-DFS-Schema-Major-Version
+ldapDisplayName: msDFS-SchemaMajorVersion
+attributeId: 1.2.840.113556.1.4.2030
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 2bcf447b-39d8-4ee8-909a-bb0755cc2f8d
+isMemberOfPartialAttributeSet: FALSE
+searchFlags: 0
+rangeLower: 2
+rangeUpper: 2
+
+cn: ms-DFS-Schema-Minor-Version
+ldapDisplayName: msDFS-SchemaMinorVersion
+attributeId: 1.2.840.113556.1.4.2031
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: d1e1dafb-8559-4519-866e-89e775557b9c
+isMemberOfPartialAttributeSet: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 0
+
+cn: ms-DFS-Short-Name-Link-Path-v2
+ldapDisplayName: msDFS-ShortNameLinkPathv2
+attributeId: 1.2.840.113556.1.4.2042
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 52bfc673-9713-4e9b-aafd-56ee72fd16a4
+isMemberOfPartialAttributeSet: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 32766
+
+cn: ms-DFS-Target-List-v2
+ldapDisplayName: msDFS-TargetListv2
+attributeId: 1.2.840.113556.1.4.2038
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 9649b643-59a7-4791-999d-79100cf871d7
+isMemberOfPartialAttributeSet: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 2097152
+
+cn: ms-DFS-Ttl-v2
+ldapDisplayName: msDFS-Ttlv2
+attributeId: 1.2.840.113556.1.4.2035
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 81ee1500-467e-4c83-a41a-295d12bdcc23
+isMemberOfPartialAttributeSet: FALSE
+searchFlags: 0
+
+cn: ms-DS-Is-User-Cachable-At-Rodc
+ldapDisplayName: msDS-IsUserCachableAtRodc
+attributeId: 1.2.840.113556.1.4.2025
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: fe01245a-341f-4556-951f-48c033a89050
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_IS_CONSTRUCTED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: RDN
+ldapDisplayName: name
+attributeId: 1.2.840.113556.1.4.1
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: bf967a0e-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+searchFlags: fPRESERVEONDELETE| fANR | fATTINDEX
+rangeLower: 1
+rangeUpper: 255
+attributeSecurityGuid: e48d0154-bcf8-11d1-8702-00c04fb96050
+mapiID: 33282
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT |FLAG_ATTR_REQ_PARTIAL_SET_MEMBER
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Netboot-Initialization
+ldapDisplayName: netbootInitialization
+attributeId: 1.2.840.113556.1.4.358
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 3e978920-8c01-11d0-afda-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: PKI-Key-Usage
+ldapDisplayName: pKIKeyUsage
+attributeId: 1.2.840.113556.1.4.1328
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: e9b0a87e-3b9d-11d2-90cc-00c04fd91ab1
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: PKI-Max-Issuing-Depth
+ldapDisplayName: pKIMaxIssuingDepth
+attributeId: 1.2.840.113556.1.4.1329
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: f0bfdefa-3b9d-11d2-90cc-00c04fd91ab1
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: PKI-Overlap-Period
+ldapDisplayName: pKIOverlapPeriod
+attributeId: 1.2.840.113556.1.4.1332
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 1219a3ec-3b9e-11d2-90cc-00c04fd91ab1
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: PKT
+ldapDisplayName: pKT
+attributeId: 1.2.840.113556.1.4.206
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 8447f9f1-1027-11d0-a05f-00aa006c33ed
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 10485760
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: PKT-Guid
+ldapDisplayName: pKTGuid
+attributeId: 1.2.840.113556.1.4.205
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 8447f9f0-1027-11d0-a05f-00aa006c33ed
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 16
+rangeUpper: 16
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Policy-Replication-Flags
+ldapDisplayName: policyReplicationFlags
+attributeId: 1.2.840.113556.1.4.633
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 19405b96-3cfa-11d1-a9c0-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Port-Name
+ldapDisplayName: portName
+attributeId: 1.2.840.113556.1.4.228
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 281416c4-1968-11d0-a28f-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Possible-Inferiors
+ldapDisplayName: possibleInferiors
+attributeId: 1.2.840.113556.1.4.915
+attributeSyntax: 2.5.5.2
+omSyntax: 6
+isSingleValued: FALSE
+schemaIdGuid: 9a7ad94c-ca53-11d1-bbd0-0080c76670c0
+systemOnly: TRUE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_IS_CONSTRUCTED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Poss-Superiors
+ldapDisplayName: possSuperiors
+attributeId: 1.2.840.113556.1.2.8
+attributeSyntax: 2.5.5.2
+omSyntax: 6
+isSingleValued: FALSE
+schemaIdGuid: bf9679fa-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Postal-Address
+ldapDisplayName: postalAddress
+attributeId: 2.5.4.16
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: bf9679fc-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: fCOPY
+rangeLower: 1
+rangeUpper: 4096
+attributeSecurityGuid: 77b5b886-944a-11d1-aebd-0000f80367c1
+mapiID: 33036
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: netboot-IntelliMirror-OSes
+ldapDisplayName: netbootIntelliMirrorOSes
+attributeId: 1.2.840.113556.1.4.857
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 0738307e-91df-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Postal-Code
+ldapDisplayName: postalCode
+attributeId: 2.5.4.17
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: bf9679fd-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: fCOPY
+rangeLower: 1
+rangeUpper: 40
+attributeSecurityGuid: 77b5b886-944a-11d1-aebd-0000f80367c1
+mapiID: 14890
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Post-Office-Box
+ldapDisplayName: postOfficeBox
+attributeId: 2.5.4.18
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: bf9679fb-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: fCOPY
+rangeLower: 1
+rangeUpper: 40
+attributeSecurityGuid: 77b5b886-944a-11d1-aebd-0000f80367c1
+mapiID: 14891
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Preferred-Delivery-Method
+ldapDisplayName: preferredDeliveryMethod
+attributeId: 2.5.4.28
+attributeSyntax: 2.5.5.9
+omSyntax: 10
+isSingleValued: FALSE
+schemaIdGuid: bf9679fe-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+attributeSecurityGuid: 77b5b886-944a-11d1-aebd-0000f80367c1
+mapiID: 33037
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: preferredLanguage
+ldapDisplayName: preferredLanguage
+attributeId: 2.16.840.1.113730.3.1.39
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 856be0d0-18e7-46e1-8f5f-7ee4d9020e0d
+systemOnly: FALSE
+searchFlags: 0
+
+cn: Preferred-OU
+ldapDisplayName: preferredOU
+attributeId: 1.2.840.113556.1.4.97
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: TRUE
+schemaIdGuid: bf9679ff-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: fCOPY
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Prefix-Map
+ldapDisplayName: prefixMap
+attributeId: 1.2.840.113556.1.4.538
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 52458022-ca6a-11d0-afff-0000f80367c1
+systemOnly: TRUE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_NOT_REPLICATED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Presentation-Address
+ldapDisplayName: presentationAddress
+attributeId: 2.5.4.29
+attributeSyntax: 2.5.5.13
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.732
+isSingleValued: TRUE
+schemaIdGuid: a8df744b-c5ea-11d1-bbcb-0080c76670c0
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Previous-CA-Certificates
+ldapDisplayName: previousCACertificates
+attributeId: 1.2.840.113556.1.4.692
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 963d2739-48be-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Previous-Parent-CA
+ldapDisplayName: previousParentCA
+attributeId: 1.2.840.113556.1.4.694
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: 963d273d-48be-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Primary-Group-ID
+ldapDisplayName: primaryGroupID
+attributeId: 1.2.840.113556.1.4.98
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: bf967a00-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: fCOPY| fATTINDEX
+attributeSecurityGuid: 59ba2f42-79a2-11d0-9020-00c04fc2d3cf
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT |FLAG_ATTR_REQ_PARTIAL_SET_MEMBER
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: netboot-Limit-Clients
+ldapDisplayName: netbootLimitClients
+attributeId: 1.2.840.113556.1.4.850
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: 07383077-91df-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Primary-Group-Token
+ldapDisplayName: primaryGroupToken
+attributeId: 1.2.840.113556.1.4.1412
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: c0ed8738-7efd-4481-84d9-66d2db8be369
+systemOnly: TRUE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_IS_CONSTRUCTED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Phone-ISDN-Primary
+ldapDisplayName: primaryInternationalISDNNumber
+attributeId: 1.2.840.113556.1.4.649
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 0296c11f-40da-11d1-a9c0-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 64
+attributeSecurityGuid: 77b5b886-944a-11d1-aebd-0000f80367c1
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Telex-Primary
+ldapDisplayName: primaryTelexNumber
+attributeId: 1.2.840.113556.1.4.648
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 0296c121-40da-11d1-a9c0-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 64
+attributeSecurityGuid: 77b5b886-944a-11d1-aebd-0000f80367c1
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Print-Attributes
+ldapDisplayName: printAttributes
+attributeId: 1.2.840.113556.1.4.247
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 281416d7-1968-11d0-a28f-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Print-Bin-Names
+ldapDisplayName: printBinNames
+attributeId: 1.2.840.113556.1.4.237
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 281416cd-1968-11d0-a28f-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Print-Collate
+ldapDisplayName: printCollate
+attributeId: 1.2.840.113556.1.4.242
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: 281416d2-1968-11d0-a28f-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Print-Color
+ldapDisplayName: printColor
+attributeId: 1.2.840.113556.1.4.243
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: 281416d3-1968-11d0-a28f-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Print-Duplex-Supported
+ldapDisplayName: printDuplexSupported
+attributeId: 1.2.840.113556.1.4.1311
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: 281416cc-1968-11d0-a28f-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Print-End-Time
+ldapDisplayName: printEndTime
+attributeId: 1.2.840.113556.1.4.234
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 281416ca-1968-11d0-a28f-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Printer-Name
+ldapDisplayName: printerName
+attributeId: 1.2.840.113556.1.4.300
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 244b296e-5abd-11d0-afd2-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: netboot-Locally-Installed-OSes
+ldapDisplayName: netbootLocallyInstalledOSes
+attributeId: 1.2.840.113556.1.4.859
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 07383080-91df-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Print-Form-Name
+ldapDisplayName: printFormName
+attributeId: 1.2.840.113556.1.4.235
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 281416cb-1968-11d0-a28f-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Print-Keep-Printed-Jobs
+ldapDisplayName: printKeepPrintedJobs
+attributeId: 1.2.840.113556.1.4.275
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: ba305f6d-47e3-11d0-a1a6-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Print-Language
+ldapDisplayName: printLanguage
+attributeId: 1.2.840.113556.1.4.246
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 281416d6-1968-11d0-a28f-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 256
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Print-MAC-Address
+ldapDisplayName: printMACAddress
+attributeId: 1.2.840.113556.1.4.288
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: ba305f7a-47e3-11d0-a1a6-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Print-Max-Copies
+ldapDisplayName: printMaxCopies
+attributeId: 1.2.840.113556.1.4.241
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 281416d1-1968-11d0-a28f-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Print-Max-Resolution-Supported
+ldapDisplayName: printMaxResolutionSupported
+attributeId: 1.2.840.113556.1.4.238
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 281416cf-1968-11d0-a28f-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Print-Max-X-Extent
+ldapDisplayName: printMaxXExtent
+attributeId: 1.2.840.113556.1.4.277
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: ba305f6f-47e3-11d0-a1a6-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Print-Max-Y-Extent
+ldapDisplayName: printMaxYExtent
+attributeId: 1.2.840.113556.1.4.278
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: ba305f70-47e3-11d0-a1a6-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Print-Media-Ready
+ldapDisplayName: printMediaReady
+attributeId: 1.2.840.113556.1.4.289
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 3bcbfcf5-4d3d-11d0-a1a6-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Print-Media-Supported
+ldapDisplayName: printMediaSupported
+attributeId: 1.2.840.113556.1.4.299
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 244b296f-5abd-11d0-afd2-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Netboot-Machine-File-Path
+ldapDisplayName: netbootMachineFilePath
+attributeId: 1.2.840.113556.1.4.361
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 3e978923-8c01-11d0-afda-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Print-Memory
+ldapDisplayName: printMemory
+attributeId: 1.2.840.113556.1.4.282
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: ba305f74-47e3-11d0-a1a6-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Print-Min-X-Extent
+ldapDisplayName: printMinXExtent
+attributeId: 1.2.840.113556.1.4.279
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: ba305f71-47e3-11d0-a1a6-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Print-Min-Y-Extent
+ldapDisplayName: printMinYExtent
+attributeId: 1.2.840.113556.1.4.280
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: ba305f72-47e3-11d0-a1a6-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Print-Network-Address
+ldapDisplayName: printNetworkAddress
+attributeId: 1.2.840.113556.1.4.287
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: ba305f79-47e3-11d0-a1a6-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Print-Notify
+ldapDisplayName: printNotify
+attributeId: 1.2.840.113556.1.4.272
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: ba305f6a-47e3-11d0-a1a6-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Print-Number-Up
+ldapDisplayName: printNumberUp
+attributeId: 1.2.840.113556.1.4.290
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 3bcbfcf4-4d3d-11d0-a1a6-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Print-Orientations-Supported
+ldapDisplayName: printOrientationsSupported
+attributeId: 1.2.840.113556.1.4.240
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 281416d0-1968-11d0-a28f-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 256
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Print-Owner
+ldapDisplayName: printOwner
+attributeId: 1.2.840.113556.1.4.271
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: ba305f69-47e3-11d0-a1a6-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Print-Pages-Per-Minute
+ldapDisplayName: printPagesPerMinute
+attributeId: 1.2.840.113556.1.4.631
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 19405b97-3cfa-11d1-a9c0-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Print-Rate
+ldapDisplayName: printRate
+attributeId: 1.2.840.113556.1.4.285
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: ba305f77-47e3-11d0-a1a6-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: netboot-Max-Clients
+ldapDisplayName: netbootMaxClients
+attributeId: 1.2.840.113556.1.4.851
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 07383078-91df-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Print-Rate-Unit
+ldapDisplayName: printRateUnit
+attributeId: 1.2.840.113556.1.4.286
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: ba305f78-47e3-11d0-a1a6-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Print-Separator-File
+ldapDisplayName: printSeparatorFile
+attributeId: 1.2.840.113556.1.4.230
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 281416c6-1968-11d0-a28f-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Print-Share-Name
+ldapDisplayName: printShareName
+attributeId: 1.2.840.113556.1.4.270
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: ba305f68-47e3-11d0-a1a6-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Print-Spooling
+ldapDisplayName: printSpooling
+attributeId: 1.2.840.113556.1.4.274
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: ba305f6c-47e3-11d0-a1a6-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Print-Stapling-Supported
+ldapDisplayName: printStaplingSupported
+attributeId: 1.2.840.113556.1.4.281
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: ba305f73-47e3-11d0-a1a6-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Print-Start-Time
+ldapDisplayName: printStartTime
+attributeId: 1.2.840.113556.1.4.233
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 281416c9-1968-11d0-a28f-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Print-Status
+ldapDisplayName: printStatus
+attributeId: 1.2.840.113556.1.4.273
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: ba305f6b-47e3-11d0-a1a6-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Priority
+ldapDisplayName: priority
+attributeId: 1.2.840.113556.1.4.231
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 281416c7-1968-11d0-a28f-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Prior-Set-Time
+ldapDisplayName: priorSetTime
+attributeId: 1.2.840.113556.1.4.99
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: bf967a01-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Prior-Value
+ldapDisplayName: priorValue
+attributeId: 1.2.840.113556.1.4.100
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: bf967a02-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Netboot-Mirror-Data-File
+ldapDisplayName: netbootMirrorDataFile
+attributeId: 1.2.840.113556.1.4.1241
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 2df90d85-009f-11d2-aa4c-00c04fd7d83a
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Private-Key
+ldapDisplayName: privateKey
+attributeId: 1.2.840.113556.1.4.101
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: bf967a03-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Privilege-Attributes
+ldapDisplayName: privilegeAttributes
+attributeId: 1.2.840.113556.1.4.636
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 19405b9a-3cfa-11d1-a9c0-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Privilege-Display-Name
+ldapDisplayName: privilegeDisplayName
+attributeId: 1.2.840.113556.1.4.634
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 19405b98-3cfa-11d1-a9c0-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Privilege-Holder
+ldapDisplayName: privilegeHolder
+attributeId: 1.2.840.113556.1.4.637
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: 19405b9b-3cfa-11d1-a9c0-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+linkID: 70
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Privilege-Value
+ldapDisplayName: privilegeValue
+attributeId: 1.2.840.113556.1.4.635
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: 19405b99-3cfa-11d1-a9c0-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Product-Code
+ldapDisplayName: productCode
+attributeId: 1.2.840.113556.1.4.818
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: d9e18317-8939-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 16
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Profile-Path
+ldapDisplayName: profilePath
+attributeId: 1.2.840.113556.1.4.139
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: bf967a05-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: fCOPY
+attributeSecurityGuid: 5f202010-79a5-11d0-9020-00c04fc2d4cf
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Proxied-Object-Name
+ldapDisplayName: proxiedObjectName
+attributeId: 1.2.840.113556.1.4.1249
+attributeSyntax: 2.5.5.7
+omSyntax: 127
+omObjectClass: 1.2.840.113556.1.1.1.11
+isSingleValued: TRUE
+schemaIdGuid: e1aea402-cd5b-11d0-afff-0000f80367c1
+systemOnly: TRUE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT |FLAG_ATTR_REQ_PARTIAL_SET_MEMBER
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Proxy-Addresses
+ldapDisplayName: proxyAddresses
+attributeId: 1.2.840.113556.1.2.210
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: bf967a06-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: fANR | fATTINDEX
+rangeLower: 1
+rangeUpper: 1123
+attributeSecurityGuid: e48d0154-bcf8-11d1-8702-00c04fb96050
+mapiID: 32783
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Proxy-Generation-Enabled
+ldapDisplayName: proxyGenerationEnabled
+attributeId: 1.2.840.113556.1.2.523
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: 5fd424d6-1262-11d0-a060-00aa006c33ed
+systemOnly: FALSE
+searchFlags: 0
+mapiID: 33201
+
+cn: netboot-New-Machine-Naming-Policy
+ldapDisplayName: netbootNewMachineNamingPolicy
+attributeId: 1.2.840.113556.1.4.855
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 0738307c-91df-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Proxy-Lifetime
+ldapDisplayName: proxyLifetime
+attributeId: 1.2.840.113556.1.4.103
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: bf967a07-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Public-Key-Policy
+ldapDisplayName: publicKeyPolicy
+attributeId: 1.2.840.113556.1.4.420
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 80a67e28-9f22-11d0-afdd-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+attributeSecurityGuid: a29b89fd-c7e8-11d0-9bae-00c04fd92ef5
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Purported-Search
+ldapDisplayName: purportedSearch
+attributeId: 1.2.840.113556.1.4.886
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: b4b54e50-943a-11d1-aebd-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 2048
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Pwd-History-Length
+ldapDisplayName: pwdHistoryLength
+attributeId: 1.2.840.113556.1.4.95
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: bf967a09-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 65535
+attributeSecurityGuid: c7407360-20bf-11d0-a768-00aa006e0529
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Pwd-Last-Set
+ldapDisplayName: pwdLastSet
+attributeId: 1.2.840.113556.1.4.96
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: bf967a0a-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+attributeSecurityGuid: 4c164200-20c0-11d0-a768-00aa006e0529
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Pwd-Properties
+ldapDisplayName: pwdProperties
+attributeId: 1.2.840.113556.1.4.93
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: bf967a0b-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+attributeSecurityGuid: c7407360-20bf-11d0-a768-00aa006e0529
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Quality-Of-Service
+ldapDisplayName: qualityOfService
+attributeId: 1.2.840.113556.1.4.458
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 80a67e4e-9f22-11d0-afdd-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+attributeSecurityGuid: a29b8a01-c7e8-11d0-9bae-00c04fd92ef5
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Query-Filter
+ldapDisplayName: queryFilter
+attributeId: 1.2.840.113556.1.4.1355
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: cbf70a26-7e78-11d2-9921-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: QueryPoint
+ldapDisplayName: queryPoint
+attributeId: 1.2.840.113556.1.4.680
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 7bfdcb86-4807-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Query-Policy-BL
+ldapDisplayName: queryPolicyBL
+attributeId: 1.2.840.113556.1.4.608
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: e1aea404-cd5b-11d0-afff-0000f80367c1
+systemOnly: TRUE
+searchFlags: 0
+linkID: 69
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_NOT_REPLICATED
+
+cn: netboot-New-Machine-OU
+ldapDisplayName: netbootNewMachineOU
+attributeId: 1.2.840.113556.1.4.856
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: TRUE
+schemaIdGuid: 0738307d-91df-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Query-Policy-Object
+ldapDisplayName: queryPolicyObject
+attributeId: 1.2.840.113556.1.4.607
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: TRUE
+schemaIdGuid: e1aea403-cd5b-11d0-afff-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+linkID: 68
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Range-Lower
+ldapDisplayName: rangeLower
+attributeId: 1.2.840.113556.1.2.34
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: bf967a0c-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+mapiID: 33043
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Range-Upper
+ldapDisplayName: rangeUpper
+attributeId: 1.2.840.113556.1.2.35
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: bf967a0d-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+mapiID: 33044
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: RDN-Att-ID
+ldapDisplayName: rDNAttID
+attributeId: 1.2.840.113556.1.2.26
+attributeSyntax: 2.5.5.2
+omSyntax: 6
+isSingleValued: TRUE
+schemaIdGuid: bf967a0f-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Registered-Address
+ldapDisplayName: registeredAddress
+attributeId: 2.5.4.26
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: FALSE
+schemaIdGuid: bf967a10-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 4096
+attributeSecurityGuid: 77b5b886-944a-11d1-aebd-0000f80367c1
+mapiID: 33049
+
+cn: Remote-Server-Name
+ldapDisplayName: remoteServerName
+attributeId: 1.2.840.113556.1.4.105
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: bf967a12-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Remote-Source
+ldapDisplayName: remoteSource
+attributeId: 1.2.840.113556.1.4.107
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: bf967a14-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 1024
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Remote-Source-Type
+ldapDisplayName: remoteSourceType
+attributeId: 1.2.840.113556.1.4.108
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: bf967a15-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Remote-Storage-GUID
+ldapDisplayName: remoteStorageGUID
+attributeId: 1.2.840.113556.1.4.809
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 2a39c5b0-8960-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Replica-Source
+ldapDisplayName: replicaSource
+attributeId: 1.2.840.113556.1.4.109
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: bf967a18-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: netboot-SCP-BL
+ldapDisplayName: netbootSCPBL
+attributeId: 1.2.840.113556.1.4.864
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: 07383082-91df-11d1-aebc-0000f80367c1
+systemOnly: TRUE
+searchFlags: 0
+linkID: 101
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_NOT_REPLICATED
+isSingleValued: TRUE
+
+cn: Repl-Interval
+ldapDisplayName: replInterval
+attributeId: 1.2.840.113556.1.4.1336
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 45ba9d1a-56fa-11d2-90d0-00c04fd91ab1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Repl-Property-Meta-Data
+ldapDisplayName: replPropertyMetaData
+attributeId: 1.2.840.113556.1.4.3
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 281416c0-1968-11d0-a28f-00aa003049e2
+systemOnly: TRUE
+searchFlags: fPRESERVEONDELETE
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_IS_OPERATIONAL |FLAG_ATTR_REQ_PARTIAL_SET_MEMBER | FLAG_ATTR_NOT_REPLICATED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Repl-Topology-Stay-Of-Execution
+ldapDisplayName: replTopologyStayOfExecution
+attributeId: 1.2.840.113556.1.4.677
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 7bfdcb83-4807-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Repl-UpToDate-Vector
+ldapDisplayName: replUpToDateVector
+attributeId: 1.2.840.113556.1.4.4
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: bf967a16-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT |FLAG_ATTR_REQ_PARTIAL_SET_MEMBER | FLAG_ATTR_NOT_REPLICATED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Reps-From
+ldapDisplayName: repsFrom
+attributeId: 1.2.840.113556.1.2.91
+attributeSyntax: 2.5.5.10
+omSyntax: 127
+omObjectClass: 1.2.840.113556.1.1.1.6
+isSingleValued: FALSE
+schemaIdGuid: bf967a1d-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT |FLAG_ATTR_REQ_PARTIAL_SET_MEMBER | FLAG_ATTR_NOT_REPLICATED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Reps-To
+ldapDisplayName: repsTo
+attributeId: 1.2.840.113556.1.2.83
+attributeSyntax: 2.5.5.10
+omSyntax: 127
+omObjectClass: 1.2.840.113556.1.1.1.6
+isSingleValued: FALSE
+schemaIdGuid: bf967a1e-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT |FLAG_ATTR_REQ_PARTIAL_SET_MEMBER | FLAG_ATTR_NOT_REPLICATED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Required-Categories
+ldapDisplayName: requiredCategories
+attributeId: 1.2.840.113556.1.4.321
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: FALSE
+schemaIdGuid: 7d6c0e93-7e20-11d0-afd6-00c04fd930c9
+systemOnly: FALSE
+searchFlags: fATTINDEX
+rangeLower: 16
+rangeUpper: 16
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Retired-Repl-DSA-Signatures
+ldapDisplayName: retiredReplDSASignatures
+attributeId: 1.2.840.113556.1.4.673
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 7bfdcb7f-4807-11d1-a9c3-0000f80367c1
+systemOnly: TRUE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Revision
+ldapDisplayName: revision
+attributeId: 1.2.840.113556.1.4.145
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: bf967a21-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Rid
+ldapDisplayName: rid
+attributeId: 1.2.840.113556.1.4.153
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: bf967a22-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Name-Service-Flags
+ldapDisplayName: nameServiceFlags
+attributeId: 1.2.840.113556.1.4.753
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 80212840-4bdc-11d1-a9c4-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: netboot-Server
+ldapDisplayName: netbootServer
+attributeId: 1.2.840.113556.1.4.860
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: TRUE
+schemaIdGuid: 07383081-91df-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+linkID: 100
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: RID-Allocation-Pool
+ldapDisplayName: rIDAllocationPool
+attributeId: 1.2.840.113556.1.4.371
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: 66171889-8f3c-11d0-afda-00c04fd930c9
+systemOnly: TRUE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: RID-Available-Pool
+ldapDisplayName: rIDAvailablePool
+attributeId: 1.2.840.113556.1.4.370
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: 66171888-8f3c-11d0-afda-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: RID-Manager-Reference
+ldapDisplayName: rIDManagerReference
+attributeId: 1.2.840.113556.1.4.368
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: TRUE
+schemaIdGuid: 66171886-8f3c-11d0-afda-00c04fd930c9
+systemOnly: TRUE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: RID-Next-RID
+ldapDisplayName: rIDNextRID
+attributeId: 1.2.840.113556.1.4.374
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 6617188c-8f3c-11d0-afda-00c04fd930c9
+systemOnly: TRUE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_NOT_REPLICATED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: RID-Previous-Allocation-Pool
+ldapDisplayName: rIDPreviousAllocationPool
+attributeId: 1.2.840.113556.1.4.372
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: 6617188a-8f3c-11d0-afda-00c04fd930c9
+systemOnly: TRUE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_NOT_REPLICATED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: RID-Set-References
+ldapDisplayName: rIDSetReferences
+attributeId: 1.2.840.113556.1.4.669
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: 7bfdcb7b-4807-11d1-a9c3-0000f80367c1
+systemOnly: TRUE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: RID-Used-Pool
+ldapDisplayName: rIDUsedPool
+attributeId: 1.2.840.113556.1.4.373
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: 6617188b-8f3c-11d0-afda-00c04fd930c9
+systemOnly: TRUE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Rights-Guid
+ldapDisplayName: rightsGuid
+attributeId: 1.2.840.113556.1.4.340
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 8297931c-86d3-11d0-afda-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 36
+rangeUpper: 36
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Role-Occupant
+ldapDisplayName: roleOccupant
+attributeId: 2.5.4.33
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: a8df7465-c5ea-11d1-bbcb-0080c76670c0
+systemOnly: FALSE
+searchFlags: 0
+mapiID: 33061
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: roomNumber
+ldapDisplayName: roomNumber
+attributeId: 0.9.2342.19200300.100.1.6
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 81d7f8c2-e327-4a0d-91c6-b42d4009115f
+systemOnly: FALSE
+searchFlags: 0
+
+cn: Netboot-SIF-File
+ldapDisplayName: netbootSIFFile
+attributeId: 1.2.840.113556.1.4.1240
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 2df90d84-009f-11d2-aa4c-00c04fd7d83a
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Root-Trust
+ldapDisplayName: rootTrust
+attributeId: 1.2.840.113556.1.4.674
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: 7bfdcb80-4807-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: rpc-Ns-Annotation
+ldapDisplayName: rpcNsAnnotation
+attributeId: 1.2.840.113556.1.4.366
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 88611bde-8cf4-11d0-afda-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: rpc-Ns-Bindings
+ldapDisplayName: rpcNsBindings
+attributeId: 1.2.840.113556.1.4.113
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: bf967a23-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: rpc-Ns-Codeset
+ldapDisplayName: rpcNsCodeset
+attributeId: 1.2.840.113556.1.4.367
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 7a0ba0e0-8e98-11d0-afda-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: rpc-Ns-Entry-Flags
+ldapDisplayName: rpcNsEntryFlags
+attributeId: 1.2.840.113556.1.4.754
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 80212841-4bdc-11d1-a9c4-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: rpc-Ns-Group
+ldapDisplayName: rpcNsGroup
+attributeId: 1.2.840.113556.1.4.114
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: bf967a24-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: rpc-Ns-Interface-ID
+ldapDisplayName: rpcNsInterfaceID
+attributeId: 1.2.840.113556.1.4.115
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: bf967a25-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: fATTINDEX
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: rpc-Ns-Object-ID
+ldapDisplayName: rpcNsObjectID
+attributeId: 1.2.840.113556.1.4.312
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 29401c48-7a27-11d0-afd6-00c04fd930c9
+systemOnly: FALSE
+searchFlags: fATTINDEX
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: rpc-Ns-Priority
+ldapDisplayName: rpcNsPriority
+attributeId: 1.2.840.113556.1.4.117
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: FALSE
+schemaIdGuid: bf967a27-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: rpc-Ns-Profile-Entry
+ldapDisplayName: rpcNsProfileEntry
+attributeId: 1.2.840.113556.1.4.118
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: bf967a28-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: netboot-Tools
+ldapDisplayName: netbootTools
+attributeId: 1.2.840.113556.1.4.858
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 0738307f-91df-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: rpc-Ns-Transfer-Syntax
+ldapDisplayName: rpcNsTransferSyntax
+attributeId: 1.2.840.113556.1.4.314
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 29401c4a-7a27-11d0-afd6-00c04fd930c9
+systemOnly: FALSE
+searchFlags: fATTINDEX
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: SAM-Account-Name
+ldapDisplayName: sAMAccountName
+attributeId: 1.2.840.113556.1.4.221
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 3e0abfd0-126a-11d0-a060-00aa006c33ed
+systemOnly: FALSE
+searchFlags: fPRESERVEONDELETE| fANR | fATTINDEX
+rangeLower: 0
+rangeUpper: 256
+attributeSecurityGuid: 59ba2f42-79a2-11d0-9020-00c04fc2d3cf
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT |FLAG_ATTR_REQ_PARTIAL_SET_MEMBER
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: SAM-Account-Type
+ldapDisplayName: sAMAccountType
+attributeId: 1.2.840.113556.1.4.302
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 6e7b626c-64f2-11d0-afd2-00c04fd930c9
+systemOnly: FALSE
+searchFlags: fATTINDEX
+attributeSecurityGuid: 59ba2f42-79a2-11d0-9020-00c04fc2d3cf
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT |FLAG_ATTR_REQ_PARTIAL_SET_MEMBER
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: SAM-Domain-Updates
+ldapDisplayName: samDomainUpdates
+attributeId: 1.2.840.113556.1.4.1969
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 04d2d114-f799-4e9b-bcdc-90e8f5ba7ebe
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 1024
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Schedule
+ldapDisplayName: schedule
+attributeId: 1.2.840.113556.1.4.211
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: dd712224-10e4-11d0-a05f-00aa006c33ed
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Schema-Flags-Ex
+ldapDisplayName: schemaFlagsEx
+attributeId: 1.2.840.113556.1.4.120
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: bf967a2b-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Schema-ID-GUID
+ldapDisplayName: schemaIDGUID
+attributeId: 1.2.840.113556.1.4.148
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: bf967923-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+searchFlags: 0
+rangeLower: 16
+rangeUpper: 16
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Schema-Info
+ldapDisplayName: schemaInfo
+attributeId: 1.2.840.113556.1.4.1358
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: FALSE
+schemaIdGuid: f9fb64ae-93b4-11d2-9945-0000f87a57d4
+systemOnly: TRUE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Schema-Update
+ldapDisplayName: schemaUpdate
+attributeId: 1.2.840.113556.1.4.481
+attributeSyntax: 2.5.5.11
+omSyntax: 24
+isSingleValued: TRUE
+schemaIdGuid: 1e2d06b4-ac8f-11d0-afe3-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_NOT_REPLICATED
+
+cn: Schema-Version
+ldapDisplayName: schemaVersion
+attributeId: 1.2.840.113556.1.2.471
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: FALSE
+schemaIdGuid: bf967a2c-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+mapiID: 33148
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Network-Address
+ldapDisplayName: networkAddress
+attributeId: 1.2.840.113556.1.2.459
+attributeSyntax: 2.5.5.4
+omSyntax: 20
+isSingleValued: FALSE
+schemaIdGuid: bf9679d9-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 256
+mapiID: 33136
+
+cn: Scope-Flags
+ldapDisplayName: scopeFlags
+attributeId: 1.2.840.113556.1.4.1354
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 16f3a4c2-7e79-11d2-9921-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Script-Path
+ldapDisplayName: scriptPath
+attributeId: 1.2.840.113556.1.4.62
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: bf9679a8-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: fCOPY
+attributeSecurityGuid: 5f202010-79a5-11d0-9020-00c04fc2d4cf
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: SD-Rights-Effective
+ldapDisplayName: sDRightsEffective
+attributeId: 1.2.840.113556.1.4.1304
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: c3dbafa6-33df-11d2-98b2-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+attributeSecurityGuid: 59ba2f42-79a2-11d0-9020-00c04fc2d3cf
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_IS_CONSTRUCTED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Search-Flags
+ldapDisplayName: searchFlags
+attributeId: 1.2.840.113556.1.2.334
+attributeSyntax: 2.5.5.9
+omSyntax: 10
+isSingleValued: TRUE
+schemaIdGuid: bf967a2d-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+mapiID: 33069
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Search-Guide
+ldapDisplayName: searchGuide
+attributeId: 2.5.4.14
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: FALSE
+schemaIdGuid: bf967a2e-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+mapiID: 33070
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: secretary
+ldapDisplayName: secretary
+attributeId: 0.9.2342.19200300.100.1.21
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: 01072d9a-98ad-4a53-9744-e83e287278fb
+systemOnly: FALSE
+searchFlags: 0
+
+cn: Security-Identifier
+ldapDisplayName: securityIdentifier
+attributeId: 1.2.840.113556.1.4.121
+attributeSyntax: 2.5.5.17
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: bf967a2f-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: See-Also
+ldapDisplayName: seeAlso
+attributeId: 2.5.4.34
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: bf967a31-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+mapiID: 33071
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Seq-Notification
+ldapDisplayName: seqNotification
+attributeId: 1.2.840.113556.1.4.504
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: ddac0cf2-af8f-11d0-afeb-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Serial-Number
+ldapDisplayName: serialNumber
+attributeId: 2.5.4.5
+attributeSyntax: 2.5.5.5
+omSyntax: 19
+isSingleValued: FALSE
+schemaIdGuid: bf967a32-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 64
+mapiID: 33072
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Next-Level-Store
+ldapDisplayName: nextLevelStore
+attributeId: 1.2.840.113556.1.4.214
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: TRUE
+schemaIdGuid: bf9679da-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Server-Name
+ldapDisplayName: serverName
+attributeId: 1.2.840.113556.1.4.223
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 09dcb7a0-165f-11d0-a064-00aa006c33ed
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 1024
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Server-Reference
+ldapDisplayName: serverReference
+attributeId: 1.2.840.113556.1.4.515
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: TRUE
+schemaIdGuid: 26d9736d-6070-11d1-a9c6-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+linkID: 94
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Server-Reference-BL
+ldapDisplayName: serverReferenceBL
+attributeId: 1.2.840.113556.1.4.516
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: 26d9736e-6070-11d1-a9c6-0000f80367c1
+systemOnly: TRUE
+searchFlags: 0
+linkID: 95
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_NOT_REPLICATED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+isSingleValued: TRUE
+
+cn: Server-Role
+ldapDisplayName: serverRole
+attributeId: 1.2.840.113556.1.4.157
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: bf967a33-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+attributeSecurityGuid: b8119fd0-04f6-4762-ab7a-4986c76b3f9a
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Server-State
+ldapDisplayName: serverState
+attributeId: 1.2.840.113556.1.4.154
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: bf967a34-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+attributeSecurityGuid: b8119fd0-04f6-4762-ab7a-4986c76b3f9a
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_NOT_REPLICATED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Service-Binding-Information
+ldapDisplayName: serviceBindingInformation
+attributeId: 1.2.840.113556.1.4.510
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: b7b1311c-b82e-11d0-afee-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Service-Class-ID
+ldapDisplayName: serviceClassID
+attributeId: 1.2.840.113556.1.4.122
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: bf967a35-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Service-Class-Info
+ldapDisplayName: serviceClassInfo
+attributeId: 1.2.840.113556.1.4.123
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: FALSE
+schemaIdGuid: bf967a36-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Service-Class-Name
+ldapDisplayName: serviceClassName
+attributeId: 1.2.840.113556.1.4.509
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: b7b1311d-b82e-11d0-afee-0000f80367c1
+systemOnly: FALSE
+searchFlags: fATTINDEX
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Service-DNS-Name
+ldapDisplayName: serviceDNSName
+attributeId: 1.2.840.113556.1.4.657
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 28630eb8-41d5-11d1-a9c1-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Next-Rid
+ldapDisplayName: nextRid
+attributeId: 1.2.840.113556.1.4.88
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: bf9679db-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Service-DNS-Name-Type
+ldapDisplayName: serviceDNSNameType
+attributeId: 1.2.840.113556.1.4.659
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 28630eba-41d5-11d1-a9c1-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 256
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Service-Instance-Version
+ldapDisplayName: serviceInstanceVersion
+attributeId: 1.2.840.113556.1.4.199
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: bf967a37-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 8
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Service-Principal-Name
+ldapDisplayName: servicePrincipalName
+attributeId: 1.2.840.113556.1.4.771
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: f3a64788-5306-11d1-a9c5-0000f80367c1
+systemOnly: FALSE
+searchFlags: fATTINDEX
+attributeSecurityGuid: e48d0154-bcf8-11d1-8702-00c04fb96050
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT |FLAG_ATTR_REQ_PARTIAL_SET_MEMBER
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Setup-Command
+ldapDisplayName: setupCommand
+attributeId: 1.2.840.113556.1.4.325
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 7d6c0e97-7e20-11d0-afd6-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ShadowExpire
+ldapDisplayName: shadowExpire
+attributeId: 1.3.6.1.1.1.1.10
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 75159a00-1fff-4cf4-8bff-4ef2695cf643
+systemOnly: FALSE
+searchFlags: 0
+
+cn: ShadowFlag
+ldapDisplayName: shadowFlag
+attributeId: 1.3.6.1.1.1.1.11
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 8dfeb70d-c5db-46b6-b15e-a4389e6cee9b
+systemOnly: FALSE
+searchFlags: 0
+
+cn: ShadowInactive
+ldapDisplayName: shadowInactive
+attributeId: 1.3.6.1.1.1.1.9
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 86871d1f-3310-4312-8efd-af49dcfb2671
+systemOnly: FALSE
+searchFlags: 0
+
+cn: ShadowLastChange
+ldapDisplayName: shadowLastChange
+attributeId: 1.3.6.1.1.1.1.5
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: f8f2689c-29e8-4843-8177-e8b98e15eeac
+systemOnly: FALSE
+searchFlags: 0
+
+cn: ShadowMax
+ldapDisplayName: shadowMax
+attributeId: 1.3.6.1.1.1.1.7
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: f285c952-50dd-449e-9160-3b880d99988d
+systemOnly: FALSE
+searchFlags: 0
+
+cn: ShadowMin
+ldapDisplayName: shadowMin
+attributeId: 1.3.6.1.1.1.1.6
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: a76b8737-e5a1-4568-b057-dc12e04be4b2
+systemOnly: FALSE
+searchFlags: 0
+
+cn: NisMapEntry
+ldapDisplayName: nisMapEntry
+attributeId: 1.3.6.1.1.1.1.27
+attributeSyntax: 2.5.5.5
+omSyntax: 22
+isSingleValued: TRUE
+schemaIdGuid: 4a95216e-fcc0-402e-b57f-5971626148a9
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 1024
+
+cn: ShadowWarning
+ldapDisplayName: shadowWarning
+attributeId: 1.3.6.1.1.1.1.8
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 7ae89c9c-2976-4a46-bb8a-340f88560117
+systemOnly: FALSE
+searchFlags: 0
+
+cn: Shell-Context-Menu
+ldapDisplayName: shellContextMenu
+attributeId: 1.2.840.113556.1.4.615
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 553fd039-f32e-11d0-b0bc-00c04fd8dca6
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Shell-Property-Pages
+ldapDisplayName: shellPropertyPages
+attributeId: 1.2.840.113556.1.4.563
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 52458039-ca6a-11d0-afff-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Short-Server-Name
+ldapDisplayName: shortServerName
+attributeId: 1.2.840.113556.1.4.1209
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 45b01501-c419-11d1-bbc9-0080c76670c0
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Show-In-Address-Book
+ldapDisplayName: showInAddressBook
+attributeId: 1.2.840.113556.1.4.644
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: 3e74f60e-3e73-11d1-a9c0-0000f80367c1
+systemOnly: FALSE
+searchFlags: fCOPY
+attributeSecurityGuid: e48d0154-bcf8-11d1-8702-00c04fb96050
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Show-In-Advanced-View-Only
+ldapDisplayName: showInAdvancedViewOnly
+attributeId: 1.2.840.113556.1.2.169
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: bf967984-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: fCOPY| fATTINDEX
+attributeSecurityGuid: 59ba2f42-79a2-11d0-9020-00c04fc2d3cf
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: SID-History
+ldapDisplayName: sIDHistory
+attributeId: 1.2.840.113556.1.4.609
+attributeSyntax: 2.5.5.17
+omSyntax: 4
+isSingleValued: FALSE
+schemaIdGuid: 17eb4278-d167-11d0-b002-0000f80367c1
+systemOnly: FALSE
+searchFlags: fATTINDEX
+attributeSecurityGuid: 59ba2f42-79a2-11d0-9020-00c04fc2d3cf
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT |FLAG_ATTR_REQ_PARTIAL_SET_MEMBER
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+systemOnly: TRUE
+
+cn: Signature-Algorithms
+ldapDisplayName: signatureAlgorithms
+attributeId: 1.2.840.113556.1.4.824
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 2a39c5b2-8960-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Site-GUID
+ldapDisplayName: siteGUID
+attributeId: 1.2.840.113556.1.4.362
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 3e978924-8c01-11d0-afda-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 16
+rangeUpper: 16
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Site-Link-List
+ldapDisplayName: siteLinkList
+attributeId: 1.2.840.113556.1.4.822
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: d50c2cdd-8951-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+linkID: 142
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: NisMapName
+ldapDisplayName: nisMapName
+attributeId: 1.3.6.1.1.1.1.26
+attributeSyntax: 2.5.5.5
+omSyntax: 22
+isSingleValued: TRUE
+schemaIdGuid: 969d3c79-0e9a-4d95-b0ac-bdde7ff8f3a1
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 1024
+
+cn: Site-List
+ldapDisplayName: siteList
+attributeId: 1.2.840.113556.1.4.821
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: d50c2cdc-8951-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+linkID: 144
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Site-Object
+ldapDisplayName: siteObject
+attributeId: 1.2.840.113556.1.4.512
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: TRUE
+schemaIdGuid: 3e10944c-c354-11d0-aff8-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+linkID: 46
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Site-Object-BL
+ldapDisplayName: siteObjectBL
+attributeId: 1.2.840.113556.1.4.513
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: 3e10944d-c354-11d0-aff8-0000f80367c1
+systemOnly: TRUE
+searchFlags: 0
+linkID: 47
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_NOT_REPLICATED
+
+cn: Site-Server
+ldapDisplayName: siteServer
+attributeId: 1.2.840.113556.1.4.494
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: 1be8f17c-a9ff-11d0-afe2-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Surname
+ldapDisplayName: sn
+attributeId: 2.5.4.4
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: bf967a41-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: fANR | fATTINDEX
+rangeLower: 1
+rangeUpper: 64
+attributeSecurityGuid: e48d0154-bcf8-11d1-8702-00c04fb96050
+mapiID: 14865
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: SPN-Mappings
+ldapDisplayName: sPNMappings
+attributeId: 1.2.840.113556.1.4.1347
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 2ab0e76c-7041-11d2-9905-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: State-Or-Province-Name
+ldapDisplayName: st
+attributeId: 2.5.4.8
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: bf967a39-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: fCOPY
+rangeLower: 1
+rangeUpper: 128
+attributeSecurityGuid: 77b5b886-944a-11d1-aebd-0000f80367c1
+mapiID: 14888
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT |FLAG_ATTR_REQ_PARTIAL_SET_MEMBER
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Street-Address
+ldapDisplayName: street
+attributeId: 2.5.4.9
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: bf967a3a-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: fCOPY
+rangeLower: 1
+rangeUpper: 1024
+attributeSecurityGuid: 77b5b886-944a-11d1-aebd-0000f80367c1
+mapiID: 33082
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT |FLAG_ATTR_REQ_PARTIAL_SET_MEMBER
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Address
+ldapDisplayName: streetAddress
+attributeId: 1.2.840.113556.1.2.256
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: f0f8ff84-1191-11d0-a060-00aa006c33ed
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 1024
+attributeSecurityGuid: 77b5b886-944a-11d1-aebd-0000f80367c1
+mapiID: 14889
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Structural-Object-Class
+ldapDisplayName: structuralObjectClass
+attributeId: 2.5.21.9
+attributeSyntax: 2.5.5.2
+omSyntax: 6
+isSingleValued: FALSE
+schemaIdGuid: 3860949f-f6a8-4b38-9950-81ecb6bc2982
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_IS_CONSTRUCTED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: NisNetgroupTriple
+ldapDisplayName: nisNetgroupTriple
+attributeId: 1.3.6.1.1.1.1.14
+attributeSyntax: 2.5.5.5
+omSyntax: 22
+isSingleValued: FALSE
+schemaIdGuid: a8032e74-30ef-4ff5-affc-0fc217783fec
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 153600
+
+cn: Sub-Class-Of
+ldapDisplayName: subClassOf
+attributeId: 1.2.840.113556.1.2.21
+attributeSyntax: 2.5.5.2
+omSyntax: 6
+isSingleValued: TRUE
+schemaIdGuid: bf967a3b-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+searchFlags: fPRESERVEONDELETE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Sub-Refs
+ldapDisplayName: subRefs
+attributeId: 1.2.840.113556.1.2.7
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: bf967a3c-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+searchFlags: 0
+mapiID: 33083
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT |FLAG_ATTR_REQ_PARTIAL_SET_MEMBER | FLAG_ATTR_NOT_REPLICATED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: SubSchemaSubEntry
+ldapDisplayName: subSchemaSubEntry
+attributeId: 2.5.18.10
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: 9a7ad94d-ca53-11d1-bbd0-0080c76670c0
+systemOnly: TRUE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_IS_CONSTRUCTED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Superior-DNS-Root
+ldapDisplayName: superiorDNSRoot
+attributeId: 1.2.840.113556.1.4.532
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 5245801d-ca6a-11d0-afff-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Super-Scope-Description
+ldapDisplayName: superScopeDescription
+attributeId: 1.2.840.113556.1.4.711
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 963d274c-48be-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Super-Scopes
+ldapDisplayName: superScopes
+attributeId: 1.2.840.113556.1.4.710
+attributeSyntax: 2.5.5.5
+omSyntax: 19
+isSingleValued: FALSE
+schemaIdGuid: 963d274b-48be-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Supplemental-Credentials
+ldapDisplayName: supplementalCredentials
+attributeId: 1.2.840.113556.1.4.125
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: FALSE
+schemaIdGuid: bf967a3f-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Supported-Application-Context
+ldapDisplayName: supportedApplicationContext
+attributeId: 2.5.4.30
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: FALSE
+schemaIdGuid: 1677588f-47f3-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+mapiID: 33085
+
+cn: Sync-Attributes
+ldapDisplayName: syncAttributes
+attributeId: 1.2.840.113556.1.4.666
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 037651e4-441d-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Sync-Membership
+ldapDisplayName: syncMembership
+attributeId: 1.2.840.113556.1.4.665
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: 037651e3-441d-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+linkID: 78
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Non-Security-Member
+ldapDisplayName: nonSecurityMember
+attributeId: 1.2.840.113556.1.4.530
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: 52458018-ca6a-11d0-afff-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+linkID: 50
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Sync-With-Object
+ldapDisplayName: syncWithObject
+attributeId: 1.2.840.113556.1.4.664
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: TRUE
+schemaIdGuid: 037651e2-441d-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Sync-With-SID
+ldapDisplayName: syncWithSID
+attributeId: 1.2.840.113556.1.4.667
+attributeSyntax: 2.5.5.17
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 037651e5-441d-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: System-Auxiliary-Class
+ldapDisplayName: systemAuxiliaryClass
+attributeId: 1.2.840.113556.1.4.198
+attributeSyntax: 2.5.5.2
+omSyntax: 6
+isSingleValued: FALSE
+schemaIdGuid: bf967a43-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: System-Flags
+ldapDisplayName: systemFlags
+attributeId: 1.2.840.113556.1.4.375
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: e0fa1e62-9b45-11d0-afdd-00c04fd930c9
+systemOnly: TRUE
+searchFlags: fPRESERVEONDELETE
+attributeSecurityGuid: e48d0154-bcf8-11d1-8702-00c04fb96050
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: System-May-Contain
+ldapDisplayName: systemMayContain
+attributeId: 1.2.840.113556.1.4.196
+attributeSyntax: 2.5.5.2
+omSyntax: 6
+isSingleValued: FALSE
+schemaIdGuid: bf967a44-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: System-Must-Contain
+ldapDisplayName: systemMustContain
+attributeId: 1.2.840.113556.1.4.197
+attributeSyntax: 2.5.5.2
+omSyntax: 6
+isSingleValued: FALSE
+schemaIdGuid: bf967a45-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: System-Only
+ldapDisplayName: systemOnly
+attributeId: 1.2.840.113556.1.4.170
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: bf967a46-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: System-Poss-Superiors
+ldapDisplayName: systemPossSuperiors
+attributeId: 1.2.840.113556.1.4.195
+attributeSyntax: 2.5.5.2
+omSyntax: 6
+isSingleValued: FALSE
+schemaIdGuid: bf967a47-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT |FLAG_ATTR_REQ_PARTIAL_SET_MEMBER
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Telephone-Number
+ldapDisplayName: telephoneNumber
+attributeId: 2.5.4.20
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: bf967a49-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 64
+attributeSecurityGuid: 77b5b886-944a-11d1-aebd-0000f80367c1
+mapiID: 14856
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Teletex-Terminal-Identifier
+ldapDisplayName: teletexTerminalIdentifier
+attributeId: 2.5.4.22
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: FALSE
+schemaIdGuid: bf967a4a-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+attributeSecurityGuid: 77b5b886-944a-11d1-aebd-0000f80367c1
+mapiID: 33091
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: NC-Name
+ldapDisplayName: nCName
+attributeId: 1.2.840.113556.1.2.16
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: TRUE
+schemaIdGuid: bf9679d6-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+searchFlags: fPRESERVEONDELETE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Non-Security-Member-BL
+ldapDisplayName: nonSecurityMemberBL
+attributeId: 1.2.840.113556.1.4.531
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: 52458019-ca6a-11d0-afff-0000f80367c1
+systemOnly: TRUE
+searchFlags: 0
+linkID: 51
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_NOT_REPLICATED
+
+cn: Telex-Number
+ldapDisplayName: telexNumber
+attributeId: 2.5.4.21
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: FALSE
+schemaIdGuid: bf967a4b-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 32
+attributeSecurityGuid: 77b5b886-944a-11d1-aebd-0000f80367c1
+mapiID: 14892
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Template-Roots
+ldapDisplayName: templateRoots
+attributeId: 1.2.840.113556.1.4.1346
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: ed9de9a0-7041-11d2-9905-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Template-Roots2
+ldapDisplayName: templateRoots2
+attributeId: 1.2.840.113556.1.4.2048
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+linkId: 2126
+schemaIdGuid: b1cba91a-0682-4362-a659-153e201ef069
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Terminal-Server
+ldapDisplayName: terminalServer
+attributeId: 1.2.840.113556.1.4.885
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 6db69a1c-9422-11d1-aebd-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 20480
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Text-Encoded-OR-Address
+ldapDisplayName: textEncodedORAddress
+attributeId: 0.9.2342.19200300.100.1.2
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: a8df7489-c5ea-11d1-bbcb-0080c76670c0
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 1024
+mapiID: 35969
+
+cn: Logo
+ldapDisplayName: thumbnailLogo
+attributeId: 2.16.840.1.113730.3.1.36
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: bf9679a9-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 32767
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Picture
+ldapDisplayName: thumbnailPhoto
+attributeId: 2.16.840.1.113730.3.1.35
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 8d3bca50-1d7e-11d0-a081-00aa006c33ed
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 102400
+mapiId: 35998
+attributeSecurityGuid: 77b5b886-944a-11d1-aebd-0000f80367c1
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Time-Refresh
+ldapDisplayName: timeRefresh
+attributeId: 1.2.840.113556.1.4.503
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: ddac0cf1-af8f-11d0-afeb-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Time-Vol-Change
+ldapDisplayName: timeVolChange
+attributeId: 1.2.840.113556.1.4.502
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: ddac0cf0-af8f-11d0-afeb-00c04fd930c9
+systemOnly: FALSE
+searchFlags: fATTINDEX
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Title
+ldapDisplayName: title
+attributeId: 2.5.4.12
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: bf967a55-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 128
+attributeSecurityGuid: e48d0154-bcf8-11d1-8702-00c04fb96050
+mapiID: 14871
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Token-Groups
+ldapDisplayName: tokenGroups
+attributeId: 1.2.840.113556.1.4.1301
+attributeSyntax: 2.5.5.17
+omSyntax: 4
+isSingleValued: FALSE
+schemaIdGuid: b7c69e6d-2cc7-11d2-854e-00a0c983f608
+systemOnly: FALSE
+searchFlags: 0
+attributeSecurityGuid: 037088f8-0ae1-11d2-b422-00a0c968f939
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_IS_CONSTRUCTED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Additional-Information
+ldapDisplayName: notes
+attributeId: 1.2.840.113556.1.4.265
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 6d05fb41-246b-11d0-a9c8-00aa006c33ed
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 32768
+attributeSecurityGuid: e48d0154-bcf8-11d1-8702-00c04fb96050
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Token-Groups-Global-And-Universal
+ldapDisplayName: tokenGroupsGlobalAndUniversal
+attributeId: 1.2.840.113556.1.4.1418
+attributeSyntax: 2.5.5.17
+omSyntax: 4
+isSingleValued: FALSE
+schemaIdGuid: 46a9b11d-60ae-405a-b7e8-ff8a58d456d2
+systemOnly: FALSE
+searchFlags: 0
+attributeSecurityGuid: 037088f8-0ae1-11d2-b422-00a0c968f939
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_IS_CONSTRUCTED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Token-Groups-No-GC-Acceptable
+ldapDisplayName: tokenGroupsNoGCAcceptable
+attributeId: 1.2.840.113556.1.4.1303
+attributeSyntax: 2.5.5.17
+omSyntax: 4
+isSingleValued: FALSE
+schemaIdGuid: 040fc392-33df-11d2-98b2-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+attributeSecurityGuid: 037088f8-0ae1-11d2-b422-00a0c968f939
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_IS_CONSTRUCTED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Tombstone-Lifetime
+ldapDisplayName: tombstoneLifetime
+attributeId: 1.2.840.113556.1.2.54
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 16c3a860-1273-11d0-a060-00aa006c33ed
+systemOnly: FALSE
+searchFlags: 0
+mapiID: 33093
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Transport-Address-Attribute
+ldapDisplayName: transportAddressAttribute
+attributeId: 1.2.840.113556.1.4.895
+attributeSyntax: 2.5.5.2
+omSyntax: 6
+isSingleValued: TRUE
+schemaIdGuid: c1dc867c-a261-11d1-b606-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Transport-DLL-Name
+ldapDisplayName: transportDLLName
+attributeId: 1.2.840.113556.1.4.789
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 26d97372-6070-11d1-a9c6-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 1024
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Transport-Type
+ldapDisplayName: transportType
+attributeId: 1.2.840.113556.1.4.791
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: TRUE
+schemaIdGuid: 26d97374-6070-11d1-a9c6-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Treat-As-Leaf
+ldapDisplayName: treatAsLeaf
+attributeId: 1.2.840.113556.1.4.806
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: 8fd044e3-771f-11d1-aeae-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Tree-Name
+ldapDisplayName: treeName
+attributeId: 1.2.840.113556.1.4.660
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 28630ebd-41d5-11d1-a9c1-0000f80367c1
+systemOnly: TRUE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Trust-Attributes
+ldapDisplayName: trustAttributes
+attributeId: 1.2.840.113556.1.4.470
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 80a67e5a-9f22-11d0-afdd-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Trust-Auth-Incoming
+ldapDisplayName: trustAuthIncoming
+attributeId: 1.2.840.113556.1.4.129
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: bf967a59-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 32767
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Notification-List
+ldapDisplayName: notificationList
+attributeId: 1.2.840.113556.1.4.303
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: TRUE
+schemaIdGuid: 19195a56-6da0-11d0-afd3-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Trust-Auth-Outgoing
+ldapDisplayName: trustAuthOutgoing
+attributeId: 1.2.840.113556.1.4.135
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: bf967a5f-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 32767
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Trust-Direction
+ldapDisplayName: trustDirection
+attributeId: 1.2.840.113556.1.4.132
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: bf967a5c-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Trust-Parent
+ldapDisplayName: trustParent
+attributeId: 1.2.840.113556.1.4.471
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: TRUE
+schemaIdGuid: b000ea7a-a086-11d0-afdd-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Trust-Partner
+ldapDisplayName: trustPartner
+attributeId: 1.2.840.113556.1.4.133
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: bf967a5d-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: fATTINDEX
+rangeLower: 1
+rangeUpper: 1024
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Trust-Posix-Offset
+ldapDisplayName: trustPosixOffset
+attributeId: 1.2.840.113556.1.4.134
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: bf967a5e-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Trust-Type
+ldapDisplayName: trustType
+attributeId: 1.2.840.113556.1.4.136
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: bf967a60-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: UAS-Compat
+ldapDisplayName: uASCompat
+attributeId: 1.2.840.113556.1.4.155
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: bf967a61-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+attributeSecurityGuid: b8119fd0-04f6-4762-ab7a-4986c76b3f9a
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: uid
+ldapDisplayName: uid
+attributeId: 0.9.2342.19200300.100.1.1
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 0bb0fca0-1e89-429f-901a-1413894d9f59
+systemOnly: FALSE
+searchFlags: fPRESERVEONDELETE
+attributeSecurityGuid: 59ba2f42-79a2-11d0-9020-00c04fc2d3cf
+
+cn: UidNumber
+ldapDisplayName: uidNumber
+attributeId: 1.3.6.1.1.1.1.0
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 850fcc8f-9c6b-47e1-b671-7c654be4d5b3
+systemOnly: FALSE
+searchFlags: fATTINDEX
+
+cn: UNC-Name
+ldapDisplayName: uNCName
+attributeId: 1.2.840.113556.1.4.137
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: bf967a64-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: fATTINDEX
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: NT-Group-Members
+ldapDisplayName: nTGroupMembers
+attributeId: 1.2.840.113556.1.4.89
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: FALSE
+schemaIdGuid: bf9679df-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Unicode-Pwd
+ldapDisplayName: unicodePwd
+attributeId: 1.2.840.113556.1.4.90
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: bf9679e1-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: uniqueIdentifier
+ldapDisplayName: uniqueIdentifier
+attributeId: 0.9.2342.19200300.100.1.44
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: ba0184c7-38c5-4bed-a526-75421470580c
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 256
+
+cn: uniqueMember
+ldapDisplayName: uniqueMember
+attributeId: 2.5.4.50
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: 8f888726-f80a-44d7-b1ee-cb9df21392c8
+systemOnly: FALSE
+searchFlags: 0
+
+cn: UnixHomeDirectory
+ldapDisplayName: unixHomeDirectory
+attributeId: 1.3.6.1.1.1.1.3
+attributeSyntax: 2.5.5.5
+omSyntax: 22
+isSingleValued: TRUE
+schemaIdGuid: bc2dba12-000f-464d-bf1d-0808465d8843
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 2048
+
+cn: UnixUserPassword
+ldapDisplayName: unixUserPassword
+attributeId: 1.2.840.113556.1.4.1910
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: FALSE
+schemaIdGuid: 612cb747-c0e8-4f92-9221-fdd5f15b550d
+systemOnly: FALSE
+searchFlags:fCONFIDENTIAL
+rangeLower: 1
+rangeUpper: 128
+
+cn: unstructuredAddress
+ldapDisplayName: unstructuredAddress
+attributeId: 1.2.840.113549.1.9.8
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 50950839-cc4c-4491-863a-fcf942d684b7
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 256
+
+cn: unstructuredName
+ldapDisplayName: unstructuredName
+attributeId: 1.2.840.113549.1.9.2
+attributeSyntax: 2.5.5.5
+omSyntax: 22
+isSingleValued: FALSE
+schemaIdGuid: 9c8ef177-41cf-45c9-9673-7716c0c8901b
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 256
+
+cn: Upgrade-Product-Code
+ldapDisplayName: upgradeProductCode
+attributeId: 1.2.840.113556.1.4.813
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: FALSE
+schemaIdGuid: d9e18312-8939-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 16
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: UPN-Suffixes
+ldapDisplayName: uPNSuffixes
+attributeId: 1.2.840.113556.1.4.890
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 032160bf-9824-11d1-aec0-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: WWW-Page-Other
+ldapDisplayName: url
+attributeId: 1.2.840.113556.1.4.749
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 9a9a0221-4a5b-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+attributeSecurityGuid: e45795b3-9455-11d1-aebd-0000f80367c1
+mapiID: 33141
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: NT-Mixed-Domain
+ldapDisplayName: nTMixedDomain
+attributeId: 1.2.840.113556.1.4.357
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 3e97891f-8c01-11d0-afda-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: User-Account-Control
+ldapDisplayName: userAccountControl
+attributeId: 1.2.840.113556.1.4.8
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: bf967a68-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: fCOPY| fPRESERVEONDELETE | fATTINDEX
+attributeSecurityGuid: 4c164200-20c0-11d0-a768-00aa006e0529
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT |FLAG_ATTR_REQ_PARTIAL_SET_MEMBER
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: User-Cert
+ldapDisplayName: userCert
+attributeId: 1.2.840.113556.1.4.645
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: bf967a69-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 32767
+attributeSecurityGuid: 77b5b886-944a-11d1-aebd-0000f80367c1
+mapiID: 14882
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: X509-Cert
+ldapDisplayName: userCertificate
+attributeId: 2.5.4.36
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: FALSE
+schemaIdGuid: bf967a7f-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 32768
+attributeSecurityGuid: 77b5b886-944a-11d1-aebd-0000f80367c1
+mapiID: 35946
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: userClass
+ldapDisplayName: userClass
+attributeId: 0.9.2342.19200300.100.1.8
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 11732a8a-e14d-4cc5-b92f-d93f51c6d8e4
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 256
+
+cn: User-Parameters
+ldapDisplayName: userParameters
+attributeId: 1.2.840.113556.1.4.138
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: bf967a6d-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 32767
+attributeSecurityGuid: 4c164200-20c0-11d0-a768-00aa006e0529
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: User-Password
+ldapDisplayName: userPassword
+attributeId: 2.5.4.35
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: FALSE
+schemaIdGuid: bf967a6e-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 128
+mapiID: 33107
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: userPKCS12
+ldapDisplayName: userPKCS12
+attributeId: 2.16.840.1.113730.3.1.216
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: FALSE
+schemaIdGuid: 23998ab5-70f8-4007-a4c1-a84a38311f9a
+systemOnly: FALSE
+searchFlags: 0
+
+cn: User-Principal-Name
+ldapDisplayName: userPrincipalName
+attributeId: 1.2.840.113556.1.4.656
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 28630ebb-41d5-11d1-a9c1-0000f80367c1
+systemOnly: FALSE
+searchFlags: fATTINDEX
+rangeUpper: 1024
+attributeSecurityGuid: e48d0154-bcf8-11d1-8702-00c04fb96050
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT |FLAG_ATTR_REQ_PARTIAL_SET_MEMBER
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: User-Shared-Folder
+ldapDisplayName: userSharedFolder
+attributeId: 1.2.840.113556.1.4.751
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 9a9a021f-4a5b-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+attributeSecurityGuid: 77b5b886-944a-11d1-aebd-0000f80367c1
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: User-Shared-Folder-Other
+ldapDisplayName: userSharedFolderOther
+attributeId: 1.2.840.113556.1.4.752
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 9a9a0220-4a5b-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+attributeSecurityGuid: 77b5b886-944a-11d1-aebd-0000f80367c1
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Nt-Pwd-History
+ldapDisplayName: ntPwdHistory
+attributeId: 1.2.840.113556.1.4.94
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: FALSE
+schemaIdGuid: bf9679e2-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: User-SMIME-Certificate
+ldapDisplayName: userSMIMECertificate
+attributeId: 2.16.840.1.113730.3.140
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: FALSE
+schemaIdGuid: e16a9db2-403c-11d1-a9c0-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+rangeUpper: 32768
+attributeSecurityGuid: 77b5b886-944a-11d1-aebd-0000f80367c1
+mapiID: 14960
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: User-Workstations
+ldapDisplayName: userWorkstations
+attributeId: 1.2.840.113556.1.4.86
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: bf9679d7-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: fCOPY
+rangeLower: 0
+rangeUpper: 1024
+attributeSecurityGuid: 5f202010-79a5-11d0-9020-00c04fc2d4cf
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: USN-Changed
+ldapDisplayName: uSNChanged
+attributeId: 1.2.840.113556.1.2.120
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: bf967a6f-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+searchFlags: fPRESERVEONDELETE | fATTINDEX
+mapiID: 32809
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT |FLAG_ATTR_REQ_PARTIAL_SET_MEMBER | FLAG_ATTR_NOT_REPLICATED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: USN-Created
+ldapDisplayName: uSNCreated
+attributeId: 1.2.840.113556.1.2.19
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: bf967a70-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+searchFlags: fPRESERVEONDELETE | fATTINDEX
+mapiID: 33108
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT |FLAG_ATTR_REQ_PARTIAL_SET_MEMBER | FLAG_ATTR_NOT_REPLICATED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: USN-DSA-Last-Obj-Removed
+ldapDisplayName: uSNDSALastObjRemoved
+attributeId: 1.2.840.113556.1.2.267
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: bf967a71-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+searchFlags: 0
+mapiID: 33109
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: USN-Intersite
+ldapDisplayName: USNIntersite
+attributeId: 1.2.840.113556.1.2.469
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: a8df7498-c5ea-11d1-bbcb-0080c76670c0
+systemOnly: FALSE
+searchFlags: fATTINDEX
+mapiID: 33146
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: USN-Last-Obj-Rem
+ldapDisplayName: uSNLastObjRem
+attributeId: 1.2.840.113556.1.2.121
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: bf967a73-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+searchFlags: 0
+mapiID: 33110
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT |FLAG_ATTR_REQ_PARTIAL_SET_MEMBER | FLAG_ATTR_NOT_REPLICATED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: USN-Source
+ldapDisplayName: uSNSource
+attributeId: 1.2.840.113556.1.4.896
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: 167758ad-47f3-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+mapiID: 33111
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Valid-Accesses
+ldapDisplayName: validAccesses
+attributeId: 1.2.840.113556.1.4.1356
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 4d2fa380-7f54-11d2-992a-0000f87a57d4
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Vendor
+ldapDisplayName: vendor
+attributeId: 1.2.840.113556.1.4.255
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 281416df-1968-11d0-a28f-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 512
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: NT-Security-Descriptor
+ldapDisplayName: nTSecurityDescriptor
+attributeId: 1.2.840.113556.1.2.281
+attributeSyntax: 2.5.5.15
+omSyntax: 66
+isSingleValued: TRUE
+schemaIdGuid: bf9679e3-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: fPRESERVEONDELETE
+rangeLower: 0
+rangeUpper: 132096
+mapiID: 32787
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_IS_OPERATIONAL |FLAG_ATTR_REQ_PARTIAL_SET_MEMBER
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Version-Number
+ldapDisplayName: versionNumber
+attributeId: 1.2.840.113556.1.4.141
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: bf967a76-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Version-Number-Hi
+ldapDisplayName: versionNumberHi
+attributeId: 1.2.840.113556.1.4.328
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 7d6c0e9a-7e20-11d0-afd6-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Version-Number-Lo
+ldapDisplayName: versionNumberLo
+attributeId: 1.2.840.113556.1.4.329
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 7d6c0e9b-7e20-11d0-afd6-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Vol-Table-GUID
+ldapDisplayName: volTableGUID
+attributeId: 1.2.840.113556.1.4.336
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 1f0075fd-7e40-11d0-afd6-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 16
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Vol-Table-Idx-GUID
+ldapDisplayName: volTableIdxGUID
+attributeId: 1.2.840.113556.1.4.334
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 1f0075fb-7e40-11d0-afd6-00c04fd930c9
+systemOnly: FALSE
+searchFlags: fATTINDEX
+rangeLower: 0
+rangeUpper: 16
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Volume-Count
+ldapDisplayName: volumeCount
+attributeId: 1.2.840.113556.1.4.507
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 34aaa217-b699-11d0-afee-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Wbem-Path
+ldapDisplayName: wbemPath
+attributeId: 1.2.840.113556.1.4.301
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 244b2970-5abd-11d0-afd2-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Well-Known-Objects
+ldapDisplayName: wellKnownObjects
+attributeId: 1.2.840.113556.1.4.618
+attributeSyntax: 2.5.5.7
+omSyntax: 127
+omObjectClass: 1.2.840.113556.1.1.1.11
+isSingleValued: FALSE
+schemaIdGuid: 05308983-7688-11d1-aded-00c04fd8d5cd
+systemOnly: TRUE
+searchFlags: 0
+rangeLower: 16
+rangeUpper: 16
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT |FLAG_ATTR_REQ_PARTIAL_SET_MEMBER
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: When-Changed
+ldapDisplayName: whenChanged
+attributeId: 1.2.840.113556.1.2.3
+attributeSyntax: 2.5.5.11
+omSyntax: 24
+isSingleValued: TRUE
+schemaIdGuid: bf967a77-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+searchFlags: 0
+mapiID: 12296
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT |FLAG_ATTR_REQ_PARTIAL_SET_MEMBER | FLAG_ATTR_NOT_REPLICATED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: When-Created
+ldapDisplayName: whenCreated
+attributeId: 1.2.840.113556.1.2.2
+attributeSyntax: 2.5.5.11
+omSyntax: 24
+isSingleValued: TRUE
+schemaIdGuid: bf967a78-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+searchFlags: 0
+mapiID: 12295
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT |FLAG_ATTR_REQ_PARTIAL_SET_MEMBER
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Organization-Name
+ldapDisplayName: o
+attributeId: 2.5.4.10
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: bf9679ef-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 64
+attributeSecurityGuid: e48d0154-bcf8-11d1-8702-00c04fb96050
+mapiID: 33025
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT |FLAG_ATTR_REQ_PARTIAL_SET_MEMBER
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Winsock-Addresses
+ldapDisplayName: winsockAddresses
+attributeId: 1.2.840.113556.1.4.142
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: FALSE
+schemaIdGuid: bf967a79-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: WWW-Home-Page
+ldapDisplayName: wWWHomePage
+attributeId: 1.2.840.113556.1.2.464
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: bf967a7a-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 2048
+attributeSecurityGuid: e45795b3-9455-11d1-aebd-0000f80367c1
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: X121-Address
+ldapDisplayName: x121Address
+attributeId: 2.5.4.24
+attributeSyntax: 2.5.5.6
+omSyntax: 18
+isSingleValued: FALSE
+schemaIdGuid: bf967a7b-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 15
+attributeSecurityGuid: 77b5b886-944a-11d1-aebd-0000f80367c1
+mapiID: 33112
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: x500uniqueIdentifier
+ldapDisplayName: x500uniqueIdentifier
+attributeId: 2.5.4.45
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: FALSE
+schemaIdGuid: d07da11f-8a3d-42b6-b0aa-76c962be719a
+systemOnly: FALSE
+searchFlags: 0
+
+cn: Object-Category
+ldapDisplayName: objectCategory
+attributeId: 1.2.840.113556.1.4.782
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: TRUE
+schemaIdGuid: 26d97369-6070-11d1-a9c6-0000f80367c1
+systemOnly: FALSE
+searchFlags: fATTINDEX
+attributeSecurityGuid: e48d0154-bcf8-11d1-8702-00c04fb96050
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT |FLAG_ATTR_REQ_PARTIAL_SET_MEMBER
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Object-Class
+ldapDisplayName: objectClass
+attributeId: 2.5.4.0
+attributeSyntax: 2.5.5.2
+omSyntax: 6
+isSingleValued: FALSE
+schemaIdGuid: bf9679e5-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+searchFlags: fPRESERVEONDELETE
+attributeSecurityGuid: e48d0154-bcf8-11d1-8702-00c04fb96050
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT |FLAG_ATTR_REQ_PARTIAL_SET_MEMBER
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: NETBIOS-Name
+ldapDisplayName: nETBIOSName
+attributeId: 1.2.840.113556.1.4.87
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: bf9679d8-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: fATTINDEX
+rangeLower: 1
+rangeUpper: 16
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Object-Class-Category
+ldapDisplayName: objectClassCategory
+attributeId: 1.2.840.113556.1.2.370
+attributeSyntax: 2.5.5.9
+omSyntax: 10
+isSingleValued: TRUE
+schemaIdGuid: bf9679e6-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 3
+mapiID: 33014
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Object-Classes
+ldapDisplayName: objectClasses
+attributeId: 2.5.21.6
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 9a7ad94b-ca53-11d1-bbd0-0080c76670c0
+systemOnly: TRUE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_IS_CONSTRUCTED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Object-Count
+ldapDisplayName: objectCount
+attributeId: 1.2.840.113556.1.4.506
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 34aaa216-b699-11d0-afee-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Object-Guid
+ldapDisplayName: objectGUID
+attributeId: 1.2.840.113556.1.4.2
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: bf9679e7-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+searchFlags: fPRESERVEONDELETE | fATTINDEX
+rangeLower: 16
+rangeUpper: 16
+attributeSecurityGuid: e48d0154-bcf8-11d1-8702-00c04fb96050
+mapiID: 35949
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT |FLAG_ATTR_REQ_PARTIAL_SET_MEMBER | FLAG_ATTR_NOT_REPLICATED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Object-Sid
+ldapDisplayName: objectSid
+attributeId: 1.2.840.113556.1.4.146
+attributeSyntax: 2.5.5.17
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: bf9679e8-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+searchFlags: fPRESERVEONDELETE | fATTINDEX
+rangeLower: 0
+rangeUpper: 28
+attributeSecurityGuid: 59ba2f42-79a2-11d0-9020-00c04fc2d3cf
+mapiID: 32807
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT |FLAG_ATTR_REQ_PARTIAL_SET_MEMBER
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+systemOnly: FALSE
+
+cn: Object-Version
+ldapDisplayName: objectVersion
+attributeId: 1.2.840.113556.1.2.76
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 16775848-47f3-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+mapiID: 33015
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: OEM-Information
+ldapDisplayName: oEMInformation
+attributeId: 1.2.840.113556.1.4.151
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: bf9679ea-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 32767
+attributeSecurityGuid: b8119fd0-04f6-4762-ab7a-4986c76b3f9a
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: OM-Object-Class
+ldapDisplayName: oMObjectClass
+attributeId: 1.2.840.113556.1.2.218
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: bf9679ec-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+searchFlags: 0
+mapiID: 33021
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: OM-Syntax
+ldapDisplayName: oMSyntax
+attributeId: 1.2.840.113556.1.2.231
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: bf9679ed-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+searchFlags: fPRESERVEONDELETE
+mapiID: 33022
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: OMT-Guid
+ldapDisplayName: oMTGuid
+attributeId: 1.2.840.113556.1.4.505
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: ddac0cf3-af8f-11d0-afeb-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 0
+rangeUpper: 16
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: netboot-Allow-New-Clients
+ldapDisplayName: netbootAllowNewClients
+attributeId: 1.2.840.113556.1.4.849
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: 07383076-91df-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: OMT-Indx-Guid
+ldapDisplayName: oMTIndxGuid
+attributeId: 1.2.840.113556.1.4.333
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 1f0075fa-7e40-11d0-afd6-00c04fd930c9
+systemOnly: FALSE
+searchFlags: fATTINDEX
+rangeLower: 0
+rangeUpper: 16
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: OncRpcNumber
+ldapDisplayName: oncRpcNumber
+attributeId: 1.3.6.1.1.1.1.18
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 966825f5-01d9-4a5c-a011-d15ae84efa55
+systemOnly: FALSE
+searchFlags: 0
+
+cn: Operating-System
+ldapDisplayName: operatingSystem
+attributeId: 1.2.840.113556.1.4.363
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 3e978925-8c01-11d0-afda-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Operating-System-Hotfix
+ldapDisplayName: operatingSystemHotfix
+attributeId: 1.2.840.113556.1.4.415
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: bd951b3c-9c96-11d0-afdd-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Operating-System-Service-Pack
+ldapDisplayName: operatingSystemServicePack
+attributeId: 1.2.840.113556.1.4.365
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 3e978927-8c01-11d0-afda-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Operating-System-Version
+ldapDisplayName: operatingSystemVersion
+attributeId: 1.2.840.113556.1.4.364
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 3e978926-8c01-11d0-afda-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Operator-Count
+ldapDisplayName: operatorCount
+attributeId: 1.2.840.113556.1.4.144
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: bf9679ee-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Option-Description
+ldapDisplayName: optionDescription
+attributeId: 1.2.840.113556.1.4.712
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 963d274d-48be-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Options
+ldapDisplayName: options
+attributeId: 1.2.840.113556.1.4.307
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 19195a53-6da0-11d0-afd3-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Options-Location
+ldapDisplayName: optionsLocation
+attributeId: 1.2.840.113556.1.4.713
+attributeSyntax: 2.5.5.5
+omSyntax: 19
+isSingleValued: FALSE
+schemaIdGuid: 963d274e-48be-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: netboot-Answer-Only-Valid-Clients
+ldapDisplayName: netbootAnswerOnlyValidClients
+attributeId: 1.2.840.113556.1.4.854
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: 0738307b-91df-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: organizationalStatus
+ldapDisplayName: organizationalStatus
+attributeId: 0.9.2342.19200300.100.1.45
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 28596019-7349-4d2f-adff-5a629961f942
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 256
+
+cn: Original-Display-Table
+ldapDisplayName: originalDisplayTable
+attributeId: 1.2.840.113556.1.2.445
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 5fd424ce-1262-11d0-a060-00aa006c33ed
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 32768
+mapiID: 33027
+
+cn: Original-Display-Table-MSDOS
+ldapDisplayName: originalDisplayTableMSDOS
+attributeId: 1.2.840.113556.1.2.214
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 5fd424cf-1262-11d0-a060-00aa006c33ed
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 32768
+mapiID: 33028
+
+cn: Phone-Fax-Other
+ldapDisplayName: otherFacsimileTelephoneNumber
+attributeId: 1.2.840.113556.1.4.646
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 0296c11d-40da-11d1-a9c0-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 64
+attributeSecurityGuid: 77b5b886-944a-11d1-aebd-0000f80367c1
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Phone-Home-Other
+ldapDisplayName: otherHomePhone
+attributeId: 1.2.840.113556.1.2.277
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: f0f8ffa2-1191-11d0-a060-00aa006c33ed
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 64
+attributeSecurityGuid: 77b5b886-944a-11d1-aebd-0000f80367c1
+mapiID: 14895
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Phone-Ip-Other
+ldapDisplayName: otherIpPhone
+attributeId: 1.2.840.113556.1.4.722
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 4d146e4b-48d4-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+attributeSecurityGuid: 77b5b886-944a-11d1-aebd-0000f80367c1
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Other-Login-Workstations
+ldapDisplayName: otherLoginWorkstations
+attributeId: 1.2.840.113556.1.4.91
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: bf9679f1-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: fCOPY
+rangeLower: 0
+rangeUpper: 1024
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Other-Mailbox
+ldapDisplayName: otherMailbox
+attributeId: 1.2.840.113556.1.4.651
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 0296c123-40da-11d1-a9c0-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+attributeSecurityGuid: e48d0154-bcf8-11d1-8702-00c04fb96050
+
+cn: Phone-Mobile-Other
+ldapDisplayName: otherMobile
+attributeId: 1.2.840.113556.1.4.647
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 0296c11e-40da-11d1-a9c0-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 64
+attributeSecurityGuid: 77b5b886-944a-11d1-aebd-0000f80367c1
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Phone-Pager-Other
+ldapDisplayName: otherPager
+attributeId: 1.2.840.113556.1.2.118
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: f0f8ffa4-1191-11d0-a060-00aa006c33ed
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 64
+attributeSecurityGuid: 77b5b886-944a-11d1-aebd-0000f80367c1
+mapiID: 35950
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: netboot-Answer-Requests
+ldapDisplayName: netbootAnswerRequests
+attributeId: 1.2.840.113556.1.4.853
+attributeSyntax: 2.5.5.8
+omSyntax: 1
+isSingleValued: TRUE
+schemaIdGuid: 0738307a-91df-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Phone-Office-Other
+ldapDisplayName: otherTelephone
+attributeId: 1.2.840.113556.1.2.18
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: f0f8ffa5-1191-11d0-a060-00aa006c33ed
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 64
+attributeSecurityGuid: 77b5b886-944a-11d1-aebd-0000f80367c1
+mapiID: 14875
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Other-Well-Known-Objects
+ldapDisplayName: otherWellKnownObjects
+attributeId: 1.2.840.113556.1.4.1359
+attributeSyntax: 2.5.5.7
+omSyntax: 127
+omObjectClass: 1.2.840.113556.1.1.1.11
+isSingleValued: FALSE
+schemaIdGuid: 1ea64e5d-ac0f-11d2-90df-00c04fd91ab1
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 16
+rangeUpper: 16
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Organizational-Unit-Name
+ldapDisplayName: ou
+attributeId: 2.5.4.11
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: bf9679f0-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: fATTINDEX
+rangeLower: 1
+rangeUpper: 64
+attributeSecurityGuid: e48d0154-bcf8-11d1-8702-00c04fb96050
+mapiID: 33026
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT |FLAG_ATTR_REQ_PARTIAL_SET_MEMBER
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Owner
+ldapDisplayName: owner
+attributeId: 2.5.4.32
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: TRUE
+schemaIdGuid: bf9679f3-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: 0
+linkID: 44
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-Exch-Owner-BL
+ldapDisplayName: ownerBL
+attributeId: 1.2.840.113556.1.2.104
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: bf9679f4-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+searchFlags: 0
+linkID: 45
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_NOT_REPLICATED
+
+cn: Package-Flags
+ldapDisplayName: packageFlags
+attributeId: 1.2.840.113556.1.4.327
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 7d6c0e99-7e20-11d0-afd6-00c04fd930c9
+systemOnly: FALSE
+searchFlags: fATTINDEX
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Package-Name
+ldapDisplayName: packageName
+attributeId: 1.2.840.113556.1.4.326
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 7d6c0e98-7e20-11d0-afd6-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Package-Type
+ldapDisplayName: packageType
+attributeId: 1.2.840.113556.1.4.324
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 7d6c0e96-7e20-11d0-afd6-00c04fd930c9
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Phone-Pager-Primary
+ldapDisplayName: pager
+attributeId: 0.9.2342.19200300.100.1.42
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: f0f8ffa6-1191-11d0-a060-00aa006c33ed
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 64
+attributeSecurityGuid: 77b5b886-944a-11d1-aebd-0000f80367c1
+mapiID: 14881
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Parent-CA
+ldapDisplayName: parentCA
+attributeId: 1.2.840.113556.1.4.557
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: TRUE
+schemaIdGuid: 5245801b-ca6a-11d0-afff-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: netboot-Current-Client-Count
+ldapDisplayName: netbootCurrentClientCount
+attributeId: 1.2.840.113556.1.4.852
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 07383079-91df-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Parent-CA-Certificate-Chain
+ldapDisplayName: parentCACertificateChain
+attributeId: 1.2.840.113556.1.4.685
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 963d2733-48be-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Parent-GUID
+ldapDisplayName: parentGUID
+attributeId: 1.2.840.113556.1.4.1224
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 2df90d74-009f-11d2-aa4c-00c04fd7d83a
+systemOnly: TRUE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_IS_CONSTRUCTED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Partial-Attribute-Deletion-List
+ldapDisplayName: partialAttributeDeletionList
+attributeId: 1.2.840.113556.1.4.663
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 28630ec0-41d5-11d1-a9c1-0000f80367c1
+systemOnly: TRUE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT |FLAG_ATTR_REQ_PARTIAL_SET_MEMBER | FLAG_ATTR_NOT_REPLICATED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Partial-Attribute-Set
+ldapDisplayName: partialAttributeSet
+attributeId: 1.2.840.113556.1.4.640
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 19405b9e-3cfa-11d1-a9c0-0000f80367c1
+systemOnly: TRUE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT |FLAG_ATTR_REQ_PARTIAL_SET_MEMBER | FLAG_ATTR_NOT_REPLICATED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Pek-Key-Change-Interval
+ldapDisplayName: pekKeyChangeInterval
+attributeId: 1.2.840.113556.1.4.866
+attributeSyntax: 2.5.5.16
+omSyntax: 65
+isSingleValued: TRUE
+schemaIdGuid: 07383084-91df-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Pek-List
+ldapDisplayName: pekList
+attributeId: 1.2.840.113556.1.4.865
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 07383083-91df-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT | FLAG_ATTR_NOT_REPLICATED
+schemaFlagsEx: FLAG_ATTR_IS_CRITICAL
+
+cn: Pending-CA-Certificates
+ldapDisplayName: pendingCACertificates
+attributeId: 1.2.840.113556.1.4.693
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 963d273c-48be-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Pending-Parent-CA
+ldapDisplayName: pendingParentCA
+attributeId: 1.2.840.113556.1.4.695
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: FALSE
+schemaIdGuid: 963d273e-48be-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Per-Msg-Dialog-Display-Table
+ldapDisplayName: perMsgDialogDisplayTable
+attributeId: 1.2.840.113556.1.2.325
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 5fd424d3-1262-11d0-a060-00aa006c33ed
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 32768
+mapiID: 33032
+
+cn: Per-Recip-Dialog-Display-Table
+ldapDisplayName: perRecipDialogDisplayTable
+attributeId: 1.2.840.113556.1.2.326
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 5fd424d4-1262-11d0-a060-00aa006c33ed
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 32768
+mapiID: 33033
+
+cn: Netboot-GUID
+ldapDisplayName: netbootGUID
+attributeId: 1.2.840.113556.1.4.359
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 3e978921-8c01-11d0-afda-00c04fd930c9
+systemOnly: FALSE
+searchFlags: fATTINDEX
+rangeLower: 16
+rangeUpper: 16
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Personal-Title
+ldapDisplayName: personalTitle
+attributeId: 1.2.840.113556.1.2.615
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: 16775858-47f3-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+searchFlags: 0
+rangeLower: 1
+rangeUpper: 64
+attributeSecurityGuid: 77b5b886-944a-11d1-aebd-0000f80367c1
+mapiID: 35947
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: photo
+ldapDisplayName: photo
+attributeId: 0.9.2342.19200300.100.1.7
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: FALSE
+schemaIdGuid: 9c979768-ba1a-4c08-9632-c6a5c1ed649a
+systemOnly: FALSE
+searchFlags: 0
+
+cn: Physical-Delivery-Office-Name
+ldapDisplayName: physicalDeliveryOfficeName
+attributeId: 2.5.4.19
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: TRUE
+schemaIdGuid: bf9679f7-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+searchFlags: fANR | fATTINDEX
+rangeLower: 1
+rangeUpper: 128
+attributeSecurityGuid: 77b5b886-944a-11d1-aebd-0000f80367c1
+mapiID: 14873
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Physical-Location-Object
+ldapDisplayName: physicalLocationObject
+attributeId: 1.2.840.113556.1.4.514
+attributeSyntax: 2.5.5.1
+omSyntax: 127
+omObjectClass: 1.3.12.2.1011.28.0.714
+isSingleValued: TRUE
+schemaIdGuid: b7b13119-b82e-11d0-afee-0000f80367c1
+systemOnly: FALSE
+searchFlags: fATTINDEX
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: PKI-Critical-Extensions
+ldapDisplayName: pKICriticalExtensions
+attributeId: 1.2.840.113556.1.4.1330
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: fc5a9106-3b9d-11d2-90cc-00c04fd91ab1
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: PKI-Default-CSPs
+ldapDisplayName: pKIDefaultCSPs
+attributeId: 1.2.840.113556.1.4.1334
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 1ef6336e-3b9e-11d2-90cc-00c04fd91ab1
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: PKI-Default-Key-Spec
+ldapDisplayName: pKIDefaultKeySpec
+attributeId: 1.2.840.113556.1.4.1327
+attributeSyntax: 2.5.5.9
+omSyntax: 2
+isSingleValued: TRUE
+schemaIdGuid: 426cae6e-3b9d-11d2-90cc-00c04fd91ab1
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: PKI-Enrollment-Access
+ldapDisplayName: pKIEnrollmentAccess
+attributeId: 1.2.840.113556.1.4.1335
+attributeSyntax: 2.5.5.15
+omSyntax: 66
+isSingleValued: FALSE
+schemaIdGuid: 926be278-56f9-11d2-90d0-00c04fd91ab1
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: PKI-Expiration-Period
+ldapDisplayName: pKIExpirationPeriod
+attributeId: 1.2.840.113556.1.4.1331
+attributeSyntax: 2.5.5.10
+omSyntax: 4
+isSingleValued: TRUE
+schemaIdGuid: 041570d2-3b9e-11d2-90cc-00c04fd91ab1
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: PKI-Extended-Key-Usage
+ldapDisplayName: pKIExtendedKeyUsage
+attributeId: 1.2.840.113556.1.4.1333
+attributeSyntax: 2.5.5.12
+omSyntax: 64
+isSingleValued: FALSE
+schemaIdGuid: 18976af6-3b9e-11d2-90cc-00c04fd91ab1
+systemOnly: FALSE
+searchFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
diff --git a/source4/setup/ad-schema/MS-AD_Schema_Classes_v20080618.txt b/source4/setup/ad-schema/MS-AD_Schema_Classes_v20080618.txt
new file mode 100644
index 0000000000..e2655d57da
--- /dev/null
+++ b/source4/setup/ad-schema/MS-AD_Schema_Classes_v20080618.txt
@@ -0,0 +1,3418 @@
+# © 2008 Microsoft Corporation. All rights reserved
+#
+# Intellectual Property Rights Notice for Protocol Documentation
+#
+# Copyrights.
+# This protocol documentation is covered by Microsoft
+# copyrights. Regardless of any other terms that are contained in the
+# terms of use for the Microsoft website that hosts this documentation,
+# you may make copies of it in order to develop implementations of the
+# protocols, and may distribute portions of it in your implementations
+# of the protocols or your documentation as necessary to properly
+# document the implementation. You may also distribute in your
+# implementation, with or without modification, any schema, IDL's, or
+# code samples that are included in the documentation. This permission
+# also applies to any documents that are referenced in the protocol
+# documentation.
+#
+# No Trade Secrets.
+# Microsoft does not claim any trade secret rights in this documentation.
+#
+# Patents.
+# Microsoft has patents that may cover your implementations of the
+# protocols. Neither this notice nor Microsoft's delivery of the
+# documentation grants any licenses under those or any other Microsoft
+# patents. However, the protocols may be covered by MicrosoftÂ’s Open
+# Specification Promise (available here:
+# http://www.microsoft.com/interop/osp). If you would prefer a written
+# license, or if the protocols are not covered by the OSP, patent
+# licenses are available by contacting protocol@microsoft.com.
+#
+# Trademarks.
+# The names of companies and products contained in this documentation
+# may be covered by trademarks or similar intellectual property
+# rights. This notice does not grant any licenses under those
+# rights.Reservation of Rights. All other rights are reserved, and this
+# notice does not grant any rights other than specifically described
+# above, whether by implication, estoppel, or otherwise.
+#
+# Tools.
+# This protocol documentation is intended for use in conjunction with
+# publicly available standard specifications and network programming
+# art, and assumes that the reader either is familiar with the
+# aforementioned material or has immediate access to it. A protocol
+# specification does not require the use of Microsoft programming tools
+# or programming environments in order for you to develop an
+# implementation. If you have access to Microsoft programming tools and
+# environments you are free to take advantage of them.
+
+cn: account
+ldapDisplayName: account
+governsId: 0.9.2342.19200300.100.4.5
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+mayContain: uid, host, ou, o, l, seeAlso, description
+possSuperiors: organizationalUnit, container
+schemaIdGuid:2628a46a-a6ad-4ae0-b854-2b12d9fe6f9e
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLOLORCWOWDSDDTDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=account,CN=Schema,CN=Configuration,<RootDomainDN>
+
+cn: Application-Site-Settings
+ldapDisplayName: applicationSiteSettings
+governsId: 1.2.840.113556.1.5.68
+objectClassCategory: 2
+rdnAttId: cn
+subClassOf: top
+systemMayContain: notificationList, applicationName
+systemPossSuperiors: site
+schemaIdGuid:19195a5c-6da0-11d0-afd3-00c04fd930c9
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Application-Site-Settings,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-DFSR-ReplicationGroup
+ldapDisplayName: msDFSR-ReplicationGroup
+governsId: 1.2.840.113556.1.6.13.4.5
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+mustContain: msDFSR-ReplicationGroupType
+mayContain: msDFSR-Options2, msDFSR-OnDemandExclusionDirectoryFilter,msDFSR-OnDemandExclusionFileFilter,msDFSR-DefaultCompressionExclusionFilter, msDFSR-DeletedSizeInMb,msDFSR-DirectoryFilter, msDFSR-FileFilter, msDFSR-ConflictSizeInMb,msDFSR-StagingSizeInMb, msDFSR-RootSizeInMb, description,msDFSR-TombstoneExpiryInMin, msDFSR-Flags, msDFSR-Options,msDFSR-Extension, msDFSR-Schedule, msDFSR-Version
+possSuperiors: msDFSR-GlobalSettings
+schemaIdGuid:1c332fe0-0c2a-4f32-afca-23c5e45a9e77
+defaultSecurityDescriptor: D:(A;;RPLCLORC;;;AU)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;DA)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;CO)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;SY)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=ms-DFSR-ReplicationGroup,CN=Schema,CN=Configuration,<RootDomainDN>
+
+cn: ms-DFSR-Subscriber
+ldapDisplayName: msDFSR-Subscriber
+governsId: 1.2.840.113556.1.6.13.4.2
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+mustContain: msDFSR-MemberReference, msDFSR-ReplicationGroupGuid
+mayContain: msDFSR-Flags, msDFSR-Options, msDFSR-Extension
+possSuperiors: msDFSR-LocalSettings
+schemaIdGuid:e11505d7-92c4-43e7-bf5c-295832ffc896
+defaultSecurityDescriptor: D:(A;;RPLCLORC;;;AU)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;DA)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;CO)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;SY)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=ms-DFSR-Subscriber,CN=Schema,CN=Configuration,<RootDomainDN>
+
+cn: ms-DFSR-Subscription
+ldapDisplayName: msDFSR-Subscription
+governsId: 1.2.840.113556.1.6.13.4.3
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+mustContain: msDFSR-ContentSetGuid, msDFSR-ReplicationGroupGuid
+mayContain: msDFSR-StagingCleanupTriggerInPercent, msDFSR-Options2,msDFSR-OnDemandExclusionDirectoryFilter,msDFSR-OnDemandExclusionFileFilter, msDFSR-MaxAgeInCacheInMin,msDFSR-MinDurationCacheInMin, msDFSR-CachePolicy, msDFSR-ReadOnly,msDFSR-DeletedSizeInMb, msDFSR-DeletedPath, msDFSR-RootPath,msDFSR-RootSizeInMb, msDFSR-StagingPath, msDFSR-StagingSizeInMb,msDFSR-ConflictPath, msDFSR-ConflictSizeInMb, msDFSR-Enabled,msDFSR-RootFence, msDFSR-DfsLinkTarget, msDFSR-Flags,msDFSR-Options, msDFSR-Extension
+possSuperiors: msDFSR-Subscriber
+schemaIdGuid:67212414-7bcc-4609-87e0-088dad8abdee
+defaultSecurityDescriptor: D:(A;;RPLCLORC;;;AU)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;DA)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;CO)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;SY)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=ms-DFSR-Subscription,CN=Schema,CN=Configuration,<RootDomainDN>
+
+cn: ms-DFSR-Topology
+ldapDisplayName: msDFSR-Topology
+governsId: 1.2.840.113556.1.6.13.4.8
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+mayContain: msDFSR-Flags, msDFSR-Options, msDFSR-Extension
+possSuperiors: msDFSR-ReplicationGroup
+schemaIdGuid:04828aa9-6e42-4e80-b962-e2fe00754d17
+defaultSecurityDescriptor: D:(A;;RPLCLORC;;;AU)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;DA)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;CO)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;SY)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=ms-DFSR-Topology,CN=Schema,CN=Configuration,<RootDomainDN>
+
+cn: ms-DS-App-Configuration
+ldapDisplayName: msDS-App-Configuration
+governsId: 1.2.840.113556.1.5.220
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: applicationSettings
+mayContain: owner, msDS-ObjectReference, msDS-Integer, msDS-DateTime,msDS-ByteArray, managedBy, keywords
+possSuperiors: organizationalUnit, computer, container
+schemaIdGuid:90df3c3e-1854-4455-a5d7-cad40d56657a
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=ms-DS-App-Configuration,CN=Schema,CN=Configuration,<RootDomainDN>
+
+cn: ms-DS-App-Data
+ldapDisplayName: msDS-AppData
+governsId: 1.2.840.113556.1.5.241
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: applicationSettings
+mayContain: owner, msDS-ObjectReference, msDS-Integer, msDS-DateTime,msDS-ByteArray, managedBy, keywords
+possSuperiors: organizationalUnit, computer, container
+schemaIdGuid:9e67d761-e327-4d55-bc95-682f875e2f8e
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=ms-DS-App-Data,CN=Schema,CN=Configuration,<RootDomainDN>
+
+cn: ms-DS-Az-Admin-Manager
+ldapDisplayName: msDS-AzAdminManager
+governsId: 1.2.840.113556.1.5.234
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMayContain: msDS-AzGenericData, msDS-AzObjectGuid,msDS-AzMinorVersion, msDS-AzMajorVersion, msDS-AzApplicationData,msDS-AzGenerateAudits, msDS-AzScriptTimeout,msDS-AzScriptEngineCacheMax, msDS-AzDomainTimeout, description
+systemPossSuperiors: domainDNS, organizationalUnit, container
+schemaIdGuid:cfee1051-5f28-4bae-a863-5d0cc18a8ed1
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;CO)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=ms-DS-Az-Admin-Manager,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-DS-Az-Application
+ldapDisplayName: msDS-AzApplication
+governsId: 1.2.840.113556.1.5.235
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMayContain: msDS-AzGenericData, msDS-AzObjectGuid,msDS-AzApplicationData, msDS-AzGenerateAudits,msDS-AzApplicationVersion, msDS-AzClassId, msDS-AzApplicationName,description
+systemPossSuperiors: msDS-AzAdminManager
+schemaIdGuid:ddf8de9b-cba5-4e12-842e-28d8b66f75ec
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;CO)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=ms-DS-Az-Application,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-DS-Az-Operation
+ldapDisplayName: msDS-AzOperation
+governsId: 1.2.840.113556.1.5.236
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMustContain: msDS-AzOperationID
+systemMayContain: msDS-AzGenericData, msDS-AzObjectGuid,msDS-AzApplicationData, description
+systemPossSuperiors: container, msDS-AzApplication
+schemaIdGuid:860abe37-9a9b-4fa4-b3d2-b8ace5df9ec5
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;CO)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=ms-DS-Az-Operation,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-DS-Az-Role
+ldapDisplayName: msDS-AzRole
+governsId: 1.2.840.113556.1.5.239
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMayContain: msDS-AzGenericData, msDS-AzObjectGuid,msDS-AzApplicationData, msDS-TasksForAzRole,msDS-OperationsForAzRole, msDS-MembersForAzRole, description
+systemPossSuperiors: container, msDS-AzScope, msDS-AzApplication
+schemaIdGuid:8213eac9-9d55-44dc-925c-e9a52b927644
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;CO)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=ms-DS-Az-Role,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Application-Version
+ldapDisplayName: applicationVersion
+governsId: 1.2.840.113556.1.5.216
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: applicationSettings
+mayContain: owner, managedBy, keywords, versionNumberLo,versionNumberHi, versionNumber, vendor, appSchemaVersion
+possSuperiors: organizationalUnit, computer, container
+schemaIdGuid:ddc790ac-af4d-442a-8f0f-a1d4caa7dd92
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Application-Version,CN=Schema,CN=Configuration,<RootDomainDN>
+
+cn: ms-DS-Az-Scope
+ldapDisplayName: msDS-AzScope
+governsId: 1.2.840.113556.1.5.237
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMustContain: msDS-AzScopeName
+systemMayContain: msDS-AzGenericData, msDS-AzObjectGuid,msDS-AzApplicationData, description
+systemPossSuperiors: msDS-AzApplication
+schemaIdGuid:4feae054-ce55-47bb-860e-5b12063a51de
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;CO)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=ms-DS-Az-Scope,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-DS-Az-Task
+ldapDisplayName: msDS-AzTask
+governsId: 1.2.840.113556.1.5.238
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMayContain: msDS-AzGenericData, msDS-AzObjectGuid,msDS-TasksForAzTask, msDS-OperationsForAzTask,msDS-AzApplicationData, msDS-AzTaskIsRoleDefinition,msDS-AzLastImportedBizRulePath, msDS-AzBizRuleLanguage,msDS-AzBizRule, description
+systemPossSuperiors: container, msDS-AzScope, msDS-AzApplication
+schemaIdGuid:1ed3a473-9b1b-418a-bfa0-3a37b95a5306
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;CO)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=ms-DS-Az-Task,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-DS-Password-Settings
+ldapDisplayName: msDS-PasswordSettings
+governsId: 1.2.840.113556.1.5.255
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMustContain: msDS-MaximumPasswordAge, msDS-MinimumPasswordAge,msDS-MinimumPasswordLength, msDS-PasswordComplexityEnabled,msDS-LockoutObservationWindow, msDS-LockoutDuration,msDS-LockoutThreshold, msDS-PasswordReversibleEncryptionEnabled,msDS-PasswordSettingsPrecedence, msDS-PasswordHistoryLength
+systemMayContain: msDS-PSOAppliesTo
+systemPossSuperiors: msDS-PasswordSettingsContainer
+schemaIdGuid: 3bcd9db8-f84b-451c-952f-6c52b81f9ec6
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)
+systemOnly: FALSE
+defaultObjectCategory: CN=ms-DS-Password-Settings,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-DS-Password-Settings-Container
+ldapDisplayName: msDS-PasswordSettingsContainer
+governsId: 1.2.840.113556.1.5.256
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemPossSuperiors: Container
+schemaIdGuid: 5b06b06a-4cf3-44c0-bd16-43bc10a987da
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)
+systemOnly: FALSE
+defaultObjectCategory: CN=ms-DS-Password-Settings-Container,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-DS-Quota-Container
+ldapDisplayName: msDS-QuotaContainer
+governsId: 1.2.840.113556.1.5.242
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMustContain: cn
+systemMayContain: msDS-TopQuotaUsage, msDS-QuotaUsed,msDS-QuotaEffective, msDS-TombstoneQuotaFactor, msDS-DefaultQuota
+systemPossSuperiors: configuration, domainDNS
+schemaIdGuid:da83fc4f-076f-4aea-b4dc-8f4dab9b5993
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPLCLORC;;;BA)(OA;;CR;4ecc03fe-ffc0-4947-b630-eb672a8a9dbc;;WD)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=ms-DS-Quota-Container,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-DS-Quota-Control
+ldapDisplayName: msDS-QuotaControl
+governsId: 1.2.840.113556.1.5.243
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMustContain: msDS-QuotaAmount, msDS-QuotaTrustee, cn
+systemPossSuperiors: msDS-QuotaContainer
+schemaIdGuid:de91fc26-bd02-4b52-ae26-795999e96fc7
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPLCLORC;;;BA)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=ms-DS-Quota-Control,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-Exch-Configuration-Container
+ldapDisplayName: msExchConfigurationContainer
+governsId: 1.2.840.113556.1.5.176
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: container
+systemMayContain: templateRoots, addressBookRoots, globalAddressList,templateRoots2, addressBookRoots2, globalAddressList2
+schemaIdGuid:d03d6858-06f4-11d2-aa53-00c04fd7d83a
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=ms-Exch-Configuration-Container,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-FVE-RecoveryInformation
+ldapDisplayName: msFVE-RecoveryInformation
+governsId: 1.2.840.113556.1.5.253
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMustContain: msFVE-RecoveryPassword, msFVE-RecoveryGuid
+mayContain: msFVE-KeyPackage, msFVE-VolumeGuid
+systemPossSuperiors: computer
+schemaIdGuid:ea715d30-8f53-40d0-bd1e-6109186d782c
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=ms-FVE-RecoveryInformation,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-ieee-80211-Policy
+ldapDisplayName: msieee80211-Policy
+governsId: 1.2.840.113556.1.5.240
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMayContain: msieee80211-ID, msieee80211-DataType,msieee80211-Data
+systemPossSuperiors: organizationalUnit, container, computer
+schemaIdGuid:7b9a2d92-b7eb-4382-9772-c3e0f9baaf94
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=ms-ieee-80211-Policy,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Custom-Recipient
+ldapDisplayName: msMQ-Custom-Recipient
+governsId: 1.2.840.113556.1.5.218
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMayContain: msMQ-Recipient-FormatName
+systemPossSuperiors: organizationalUnit, domainDNS, container
+schemaIdGuid:876d6817-35cc-436c-acea-5ef7174dd9be
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: FALSE
+systemOnly: FALSE
+defaultObjectCategory: CN=MSMQ-Custom-Recipient,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Attribute-Schema
+ldapDisplayName: attributeSchema
+governsId: 1.2.840.113556.1.3.14
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMustContain: schemaIDGUID, oMSyntax, lDAPDisplayName,isSingleValued, cn, attributeSyntax, attributeID
+systemMayContain: systemOnly, searchFlags, schemaFlagsEx,rangeUpper, rangeLower, oMObjectClass, msDs-Schema-Extensions,msDS-IntId, mAPIID, linkID, isMemberOfPartialAttributeSet,isEphemeral, isDefunct, extendedCharsAllowed, classDisplayName,attributeSecurityGUID
+systemPossSuperiors: dMD
+schemaIdGuid:bf967a80-0de6-11d0-a285-00aa003049e2
+defaultSecurityDescriptor: D:S:
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Attribute-Schema,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Group
+ldapDisplayName: msMQ-Group
+governsId: 1.2.840.113556.1.5.219
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMustContain: member
+systemPossSuperiors: organizationalUnit
+schemaIdGuid:46b27aac-aafa-4ffb-b773-e5bf621ee87b
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=MSMQ-Group,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Configuration
+ldapDisplayName: mSMQConfiguration
+governsId: 1.2.840.113556.1.5.162
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMayContain: mSMQSites, mSMQSignKey, mSMQServiceType,mSMQRoutingServices, mSMQQuota, mSMQOwnerID, mSMQOutRoutingServers,mSMQOSType, mSMQJournalQuota, mSMQInRoutingServers, mSMQForeign,mSMQEncryptKey, mSMQDsServices, mSMQDependentClientServices,mSMQComputerTypeEx, mSMQComputerType
+systemPossSuperiors: computer
+schemaIdGuid:9a0dc344-c100-11d1-bbc5-0080c76670c0
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=MSMQ-Configuration,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Enterprise-Settings
+ldapDisplayName: mSMQEnterpriseSettings
+governsId: 1.2.840.113556.1.5.163
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMayContain: mSMQVersion, mSMQNameStyle, mSMQLongLived,mSMQInterval2, mSMQInterval1, mSMQCSPName
+systemPossSuperiors: container
+schemaIdGuid:9a0dc345-c100-11d1-bbc5-0080c76670c0
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=MSMQ-Enterprise-Settings,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Migrated-User
+ldapDisplayName: mSMQMigratedUser
+governsId: 1.2.840.113556.1.5.179
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMayContain: mSMQUserSid, mSMQSignCertificatesMig,mSMQSignCertificates, mSMQDigestsMig, mSMQDigests, objectSid
+systemPossSuperiors: organizationalUnit, domainDNS, builtinDomain
+schemaIdGuid:50776997-3c3d-11d2-90cc-00c04fd91ab1
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=MSMQ-Migrated-User,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Queue
+ldapDisplayName: mSMQQueue
+governsId: 1.2.840.113556.1.5.161
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMayContain: mSMQTransactional, MSMQ-SecuredSource,mSMQQueueType, mSMQQueueQuota, mSMQQueueNameExt,mSMQQueueJournalQuota, mSMQPrivacyLevel, mSMQOwnerID,MSMQ-MulticastAddress, mSMQLabelEx, mSMQLabel, mSMQJournal,mSMQBasePriority, mSMQAuthenticate
+systemPossSuperiors: mSMQConfiguration
+schemaIdGuid:9a0dc343-c100-11d1-bbc5-0080c76670c0
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: FALSE
+systemOnly: FALSE
+defaultObjectCategory: CN=MSMQ-Queue,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Settings
+ldapDisplayName: mSMQSettings
+governsId: 1.2.840.113556.1.5.165
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMayContain: mSMQSiteNameEx, mSMQSiteName, mSMQServices,mSMQRoutingService, mSMQQMID, mSMQOwnerID, mSMQNt4Flags,mSMQMigrated, mSMQDsService, mSMQDependentClientService
+systemPossSuperiors: server
+schemaIdGuid:9a0dc347-c100-11d1-bbc5-0080c76670c0
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=MSMQ-Settings,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MSMQ-Site-Link
+ldapDisplayName: mSMQSiteLink
+governsId: 1.2.840.113556.1.5.164
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMustContain: mSMQSite2, mSMQSite1, mSMQCost
+systemMayContain: mSMQSiteGatesMig, mSMQSiteGates
+systemPossSuperiors: mSMQEnterpriseSettings
+schemaIdGuid:9a0dc346-c100-11d1-bbc5-0080c76670c0
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: FALSE
+systemOnly: FALSE
+defaultObjectCategory: CN=MSMQ-Site-Link,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-PKI-Enterprise-Oid
+ldapDisplayName: msPKI-Enterprise-Oid
+governsId: 1.2.840.113556.1.5.196
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMayContain: msPKI-OID-User-Notice, msPKI-OIDLocalizedName,msPKI-OID-CPS, msPKI-OID-Attribute, msPKI-Cert-Template-OID
+systemPossSuperiors: msPKI-Enterprise-Oid, container
+schemaIdGuid:37cfd85c-6719-4ad8-8f9e-8678ba627563
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=ms-PKI-Enterprise-Oid,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-PKI-Key-Recovery-Agent
+ldapDisplayName: msPKI-Key-Recovery-Agent
+governsId: 1.2.840.113556.1.5.195
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: user
+systemPossSuperiors: container
+schemaIdGuid:26ccf238-a08e-4b86-9a82-a8c9ac7ee5cb
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=ms-PKI-Key-Recovery-Agent,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-PKI-Private-Key-Recovery-Agent
+ldapDisplayName: msPKI-PrivateKeyRecoveryAgent
+governsId: 1.2.840.113556.1.5.223
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMustContain: userCertificate
+systemPossSuperiors: container
+schemaIdGuid:1562a632-44b9-4a7e-a2d3-e426c96a3acc
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=ms-PKI-Private-Key-Recovery-Agent,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: BootableDevice
+ldapDisplayName: bootableDevice
+governsId: 1.3.6.1.1.1.2.12
+objectClassCategory: 3
+rdnAttId: cn
+subClassOf: top
+mayContain: cn, bootParameter, bootFile
+schemaIdGuid:4bcb2477-4bb3-4545-a9fc-fb66e136b435
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=BootableDevice,CN=Schema,CN=Configuration,<RootDomainDN>
+
+cn: ms-Print-ConnectionPolicy
+ldapDisplayName: msPrint-ConnectionPolicy
+governsId: 1.2.840.113556.1.6.23.2
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+mustContain: cn
+mayContain: printerName, printAttributes, serverName, uNCName
+possSuperiors: container
+schemaIdGuid:a16f33c7-7fd6-4828-9364-435138fda08d
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=ms-Print-ConnectionPolicy,CN=Schema,CN=Configuration,<RootDomainDN>
+
+cn: msSFU-30-Domain-Info
+ldapDisplayName: msSFU30DomainInfo
+governsId: 1.2.840.113556.1.6.18.2.215
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+mayContain: msSFU30Domains, msSFU30YpServers, msSFU30SearchContainer,msSFU30IsValidContainer, msSFU30MasterServerName,msSFU30OrderNumber, msSFU30MaxGidNumber, msSFU30MaxUidNumber,msSFU30CryptMethod
+possSuperiors: container
+schemaIdGuid:36297dce-656b-4423-ab65-dabb2770819e
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=msSFU-30-Domain-Info,CN=Schema,CN=Configuration,<RootDomainDN>
+
+cn: msSFU-30-Mail-Aliases
+ldapDisplayName: msSFU30MailAliases
+governsId: 1.2.840.113556.1.6.18.2.211
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+mayContain: msSFU30Name, msSFU30NisDomain, msSFU30Aliases, nisMapName
+possSuperiors: domainDNS, nisMap, container
+schemaIdGuid:d6710785-86ff-44b7-85b5-f1f8689522ce
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=msSFU-30-Mail-Aliases,CN=Schema,CN=Configuration,<RootDomainDN>
+
+cn: msSFU-30-Net-Id
+ldapDisplayName: msSFU30NetId
+governsId: 1.2.840.113556.1.6.18.2.212
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+mayContain: msSFU30KeyValues, msSFU30Name, msSFU30NisDomain,nisMapName
+possSuperiors: domainDNS, nisMap, container
+schemaIdGuid:e263192c-2a02-48df-9792-94f2328781a0
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=msSFU-30-Net-Id,CN=Schema,CN=Configuration,<RootDomainDN>
+
+cn: msSFU-30-Network-User
+ldapDisplayName: msSFU30NetworkUser
+governsId: 1.2.840.113556.1.6.18.2.216
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+mayContain: msSFU30KeyValues, msSFU30Name, msSFU30NisDomain,nisMapName
+possSuperiors: domainDNS, nisMap, container
+schemaIdGuid:e15334a3-0bf0-4427-b672-11f5d84acc92
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=msSFU-30-Network-User,CN=Schema,CN=Configuration,<RootDomainDN>
+
+cn: msSFU-30-NIS-Map-Config
+ldapDisplayName: msSFU30NISMapConfig
+governsId: 1.2.840.113556.1.6.18.2.217
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+mayContain: msSFU30KeyAttributes, msSFU30FieldSeparator,msSFU30NSMAPFieldPosition, msSFU30IntraFieldSeparator,msSFU30SearchAttributes, msSFU30ResultAttributes, msSFU30MapFilter
+possSuperiors: container
+schemaIdGuid:faf733d0-f8eb-4dcf-8d75-f1753af6a50b
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=msSFU-30-NIS-Map-Config,CN=Schema,CN=Configuration,<RootDomainDN>
+
+cn: ms-TAPI-Rt-Conference
+ldapDisplayName: msTAPI-RtConference
+governsId: 1.2.840.113556.1.5.221
+objectClassCategory: 1
+rdnAttId: msTAPI-uid
+subClassOf: top
+systemMustContain: msTAPI-uid
+systemMayContain: msTAPI-ConferenceBlob, msTAPI-ProtocolId
+systemPossSuperiors: organizationalUnit
+schemaIdGuid:ca7b9735-4b2a-4e49-89c3-99025334dc94
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=ms-TAPI-Rt-Conference,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-TAPI-Rt-Person
+ldapDisplayName: msTAPI-RtPerson
+governsId: 1.2.840.113556.1.5.222
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMayContain: msTAPI-uid, msTAPI-IpAddress
+systemPossSuperiors: organization, organizationalUnit
+schemaIdGuid:53ea1cb5-b704-4df9-818f-5cb4ec86cac1
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=ms-TAPI-Rt-Person,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-WMI-IntRangeParam
+ldapDisplayName: msWMI-IntRangeParam
+governsId: 1.2.840.113556.1.5.205
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: msWMI-RangeParam
+systemMustContain: msWMI-IntDefault
+systemMayContain: msWMI-IntMax, msWMI-IntMin
+systemPossSuperiors: msWMI-MergeablePolicyTemplate
+schemaIdGuid:50ca5d7d-5c8b-4ef3-b9df-5b66d491e526
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=ms-WMI-IntRangeParam,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-WMI-IntSetParam
+ldapDisplayName: msWMI-IntSetParam
+governsId: 1.2.840.113556.1.5.206
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: msWMI-RangeParam
+systemMustContain: msWMI-IntDefault
+systemMayContain: msWMI-IntValidValues
+systemPossSuperiors: msWMI-MergeablePolicyTemplate
+schemaIdGuid:292f0d9a-cf76-42b0-841f-b650f331df62
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPCCDCLCLODTRC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=ms-WMI-IntSetParam,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Builtin-Domain
+ldapDisplayName: builtinDomain
+governsId: 1.2.840.113556.1.5.4
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemAuxiliaryClass: samDomainBase
+systemPossSuperiors: domainDNS
+schemaIdGuid:bf967a81-0de6-11d0-a285-00aa003049e2
+defaultSecurityDescriptor: D:(A;;RPLCLORC;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Builtin-Domain,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-WMI-MergeablePolicyTemplate
+ldapDisplayName: msWMI-MergeablePolicyTemplate
+governsId: 1.2.840.113556.1.5.202
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: msWMI-PolicyTemplate
+systemPossSuperiors: container
+schemaIdGuid:07502414-fdca-4851-b04a-13645b11d226
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPCCDCLCLODTRC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=ms-WMI-MergeablePolicyTemplate,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-WMI-ObjectEncoding
+ldapDisplayName: msWMI-ObjectEncoding
+governsId: 1.2.840.113556.1.5.217
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMustContain: msWMI-Class, msWMI-ScopeGuid, msWMI-Parm1,msWMI-Parm2, msWMI-Parm3, msWMI-Parm4, msWMI-Genus, msWMI-intFlags1,msWMI-intFlags2, msWMI-intFlags3, msWMI-intFlags4, msWMI-ID,msWMI-TargetObject
+systemPossSuperiors: container
+schemaIdGuid:55dd81c9-c312-41f9-a84d-c6adbdf1e8e1
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=ms-WMI-ObjectEncoding,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-WMI-PolicyTemplate
+ldapDisplayName: msWMI-PolicyTemplate
+governsId: 1.2.840.113556.1.5.200
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMustContain: msWMI-NormalizedClass, msWMI-TargetPath,msWMI-TargetClass, msWMI-TargetNameSpace, msWMI-Name, msWMI-ID
+systemMayContain: msWMI-TargetType, msWMI-SourceOrganization,msWMI-Parm4, msWMI-Parm3, msWMI-Parm2, msWMI-Parm1, msWMI-intFlags4,msWMI-intFlags3, msWMI-intFlags2, msWMI-intFlags1,msWMI-CreationDate, msWMI-ChangeDate, msWMI-Author
+systemPossSuperiors: container
+schemaIdGuid:e2bc80f1-244a-4d59-acc6-ca5c4f82e6e1
+defaultSecurityDescriptor: D:(A;;RPLCLORC;;;AU)(A;;RPWPCRLCLOCCDCRCWDWOSW;;;DA)(A;;CC;;;PA)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;SY)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=ms-WMI-PolicyTemplate,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-WMI-PolicyType
+ldapDisplayName: msWMI-PolicyType
+governsId: 1.2.840.113556.1.5.211
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMustContain: msWMI-TargetObject, msWMI-ID
+systemMayContain: msWMI-SourceOrganization, msWMI-Parm4,msWMI-Parm3, msWMI-Parm2, msWMI-Parm1, msWMI-intFlags4,msWMI-intFlags3, msWMI-intFlags2, msWMI-intFlags1,msWMI-CreationDate, msWMI-ChangeDate, msWMI-Author
+systemPossSuperiors: container
+schemaIdGuid:595b2613-4109-4e77-9013-a3bb4ef277c7
+defaultSecurityDescriptor: D:(A;;RPLCLORC;;;AU)(A;;RPWPCRLCLOCCDCRCWDWOSW;;;DA)(A;;CC;;;PA)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;SY)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=ms-WMI-PolicyType,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-WMI-RangeParam
+ldapDisplayName: msWMI-RangeParam
+governsId: 1.2.840.113556.1.5.203
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMustContain: msWMI-TargetType, msWMI-TargetClass,msWMI-PropertyName
+systemPossSuperiors: msWMI-MergeablePolicyTemplate
+schemaIdGuid:45fb5a57-5018-4d0f-9056-997c8c9122d9
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPCCDCLCLODTRC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=ms-WMI-RangeParam,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-WMI-RealRangeParam
+ldapDisplayName: msWMI-RealRangeParam
+governsId: 1.2.840.113556.1.5.209
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: msWMI-RangeParam
+systemMustContain: msWMI-Int8Default
+systemMayContain: msWMI-Int8Max, msWMI-Int8Min
+systemPossSuperiors: msWMI-MergeablePolicyTemplate
+schemaIdGuid:6afe8fe2-70bc-4cce-b166-a96f7359c514
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=ms-WMI-RealRangeParam,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-WMI-Rule
+ldapDisplayName: msWMI-Rule
+governsId: 1.2.840.113556.1.5.214
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMustContain: msWMI-QueryLanguage, msWMI-TargetNameSpace,msWMI-Query
+systemPossSuperiors: msWMI-Som, container
+schemaIdGuid:3c7e6f83-dd0e-481b-a0c2-74cd96ef2a66
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=ms-WMI-Rule,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-WMI-ShadowObject
+ldapDisplayName: msWMI-ShadowObject
+governsId: 1.2.840.113556.1.5.212
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMustContain: msWMI-TargetObject
+systemPossSuperiors: msWMI-PolicyType
+schemaIdGuid:f1e44bdf-8dd3-4235-9c86-f91f31f5b569
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=ms-WMI-ShadowObject,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-WMI-SimplePolicyTemplate
+ldapDisplayName: msWMI-SimplePolicyTemplate
+governsId: 1.2.840.113556.1.5.201
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: msWMI-PolicyTemplate
+systemMustContain: msWMI-TargetObject
+systemPossSuperiors: container
+schemaIdGuid:6cc8b2b5-12df-44f6-8307-e74f5cdee369
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPCCDCLCLODTRC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=ms-WMI-SimplePolicyTemplate,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-WMI-Som
+ldapDisplayName: msWMI-Som
+governsId: 1.2.840.113556.1.5.213
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMustContain: msWMI-Name, msWMI-ID
+systemMayContain: msWMI-SourceOrganization, msWMI-Parm4, msWMI-Parm3,msWMI-Parm2, msWMI-Parm1, msWMI-intFlags4, msWMI-intFlags3,msWMI-intFlags2, msWMI-intFlags1, msWMI-CreationDate,msWMI-ChangeDate, msWMI-Author
+systemPossSuperiors: container
+schemaIdGuid:ab857078-0142-4406-945b-34c9b6b13372
+defaultSecurityDescriptor: D:(A;;RPLCLORC;;;AU)(A;;RPWPCRLCLOCCDCRCWDWOSW;;;DA)(A;;CC;;;PA)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;SY)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=ms-WMI-Som,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Category-Registration
+ldapDisplayName: categoryRegistration
+governsId: 1.2.840.113556.1.5.74
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: leaf
+systemMayContain: managedBy, localizedDescription, localeID,categoryId
+systemPossSuperiors: classStore
+schemaIdGuid:7d6c0e9d-7e20-11d0-afd6-00c04fd930c9
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Category-Registration,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-WMI-StringSetParam
+ldapDisplayName: msWMI-StringSetParam
+governsId: 1.2.840.113556.1.5.210
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: msWMI-RangeParam
+systemMustContain: msWMI-StringDefault
+systemMayContain: msWMI-StringValidValues
+systemPossSuperiors: msWMI-MergeablePolicyTemplate
+schemaIdGuid:0bc579a2-1da7-4cea-b699-807f3b9d63a4
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPCCDCLCLODTRC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=ms-WMI-StringSetParam,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-WMI-UintRangeParam
+ldapDisplayName: msWMI-UintRangeParam
+governsId: 1.2.840.113556.1.5.207
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: msWMI-RangeParam
+systemMustContain: msWMI-IntDefault
+systemMayContain: msWMI-IntMax, msWMI-IntMin
+systemPossSuperiors: msWMI-MergeablePolicyTemplate
+schemaIdGuid:d9a799b2-cef3-48b3-b5ad-fb85f8dd3214
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=ms-WMI-UintRangeParam,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-WMI-UintSetParam
+ldapDisplayName: msWMI-UintSetParam
+governsId: 1.2.840.113556.1.5.208
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: msWMI-RangeParam
+systemMustContain: msWMI-IntDefault
+systemMayContain: msWMI-IntValidValues
+systemPossSuperiors: msWMI-MergeablePolicyTemplate
+schemaIdGuid:8f4beb31-4e19-46f5-932e-5fa03c339b1d
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPCCDCLCLODTRC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=ms-WMI-UintSetParam,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-WMI-UnknownRangeParam
+ldapDisplayName: msWMI-UnknownRangeParam
+governsId: 1.2.840.113556.1.5.204
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: msWMI-RangeParam
+systemMustContain: msWMI-TargetObject, msWMI-NormalizedClass
+systemPossSuperiors: msWMI-MergeablePolicyTemplate
+schemaIdGuid:b82ac26b-c6db-4098-92c6-49c18a3336e1
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=ms-WMI-UnknownRangeParam,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-WMI-WMIGPO
+ldapDisplayName: msWMI-WMIGPO
+governsId: 1.2.840.113556.1.5.215
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMustContain: msWMI-TargetClass
+systemMayContain: msWMI-Parm4, msWMI-Parm3, msWMI-Parm2, msWMI-Parm1,msWMI-intFlags4, msWMI-intFlags3, msWMI-intFlags2, msWMI-intFlags1
+systemPossSuperiors: container
+schemaIdGuid:05630000-3927-4ede-bf27-ca91f275c26f
+defaultSecurityDescriptor: D:(A;;RPLCLORC;;;AU)(A;;RPWPCRLCLOCCDCRCWDWOSW;;;DA)(A;;CC;;;PA)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;SY)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=ms-WMI-WMIGPO,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: NisMap
+ldapDisplayName: nisMap
+governsId: 1.3.6.1.1.1.2.9
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+mustContain: cn, nisMapName
+mayContain: description
+possSuperiors: domainDNS, container, organizationalUnit
+schemaIdGuid:7672666c-02c1-4f33-9ecf-f649c1dd9b7c
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=NisMap,CN=Schema,CN=Configuration,<RootDomainDN>
+
+cn: NisNetgroup
+ldapDisplayName: nisNetgroup
+governsId: 1.3.6.1.1.1.2.8
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+mustContain: cn
+mayContain: description, memberNisNetgroup, nisNetgroupTriple,msSFU30Name, msSFU30NisDomain, nisMapName,msSFU30NetgroupHostAtDomain, msSFU30NetgroupUserAtDomain
+possSuperiors: domainDNS, nisMap, container, organizationalUnit
+schemaIdGuid:72efbf84-6e7b-4a5c-a8db-8a75a7cad254
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=NisNetgroup,CN=Schema,CN=Configuration,<RootDomainDN>
+
+cn: NisObject
+ldapDisplayName: nisObject
+governsId: 1.3.6.1.1.1.2.10
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+mustContain: cn, nisMapName, nisMapEntry
+mayContain: description, msSFU30Name, msSFU30NisDomain
+possSuperiors: domainDNS, nisMap, container, organizationalUnit
+schemaIdGuid:904f8a93-4954-4c5f-b1e1-53c097a31e13
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=NisObject,CN=Schema,CN=Configuration,<RootDomainDN>
+
+cn: NTDS-Connection
+ldapDisplayName: nTDSConnection
+governsId: 1.2.840.113556.1.5.71
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: leaf
+systemMustContain: options, fromServer, enabledConnection
+systemMayContain: transportType, schedule, mS-DS-ReplicatesNCReason,generatedConnection
+systemPossSuperiors: nTFRSMember, nTFRSReplicaSet, nTDSDSA
+schemaIdGuid:19195a60-6da0-11d0-afd3-00c04fd930c9
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=NTDS-Connection,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: NTDS-DSA
+ldapDisplayName: nTDSDSA
+governsId: 1.2.840.113556.1.5.7000.47
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: applicationSettings
+systemMayContain: msDS-IsUserCachableAtRodc, msDS-Sitename,msDS-isRODC, msDS-isGC, msDS-RevealedUsers,msDS-NeverRevealGroup, msDS-RevealOnDemandGroup,msDS-hasFullReplicaNCs, serverReference,msDS-RetiredReplNCSignatures, retiredReplDSASignatures,queryPolicyObject, options, networkAddress, msDS-ReplicationEpoch,msDS-HasInstantiatedNCs, msDS-hasMasterNCs, msDS-HasDomainNCs,msDS-Behavior-Version, managedBy, lastBackupRestorationTime,invocationId, hasPartialReplicaNCs, hasMasterNCs, fRSRootPath,dMDLocation
+systemPossSuperiors: organization, server
+schemaIdGuid:f0f8ffab-1191-11d0-a060-00aa006c33ed
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: TRUE
+defaultObjectCategory: CN=NTDS-DSA,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Certification-Authority
+ldapDisplayName: certificationAuthority
+governsId: 2.5.6.16
+objectClassCategory: 0
+rdnAttId: cn
+subClassOf: top
+systemMustContain: cn, certificateRevocationList, cACertificate,authorityRevocationList
+systemMayContain: teletexTerminalIdentifier,supportedApplicationContext, signatureAlgorithms, searchGuide,previousParentCA, previousCACertificates, pendingParentCA,pendingCACertificates, parentCACertificateChain, parentCA,enrollmentProviders, domainPolicyObject, domainID, dNSHostName,deltaRevocationList, currentParentCA, crossCertificatePair,cRLObject, certificateTemplates, cAWEBURL, cAUsages, cAConnect,cACertificateDN
+systemPossSuperiors: container
+schemaIdGuid:3fdfee50-47f4-11d1-a9c3-0000f80367c1
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Certification-Authority,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: NTDS-DSA-RO
+ldapDisplayName: nTDSDSARO
+governsId: 1.2.840.113556.1.5.254
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: nTDSDSA
+systemPossSuperiors: server, organization
+schemaIdGuid:85d16ec1-0791-4bc8-8ab3-70980602ff8c
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: TRUE
+defaultObjectCategory: CN=NTDS-DSA-RO,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: NTDS-Service
+ldapDisplayName: nTDSService
+governsId: 1.2.840.113556.1.5.72
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMayContain: tombstoneLifetime, sPNMappings,replTopologyStayOfExecution, msDS-Other-Settings, garbageCollPeriod,dSHeuristics
+systemPossSuperiors: container
+schemaIdGuid:19195a5f-6da0-11d0-afd3-00c04fd930c9
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=NTDS-Service,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: NTDS-Site-Settings
+ldapDisplayName: nTDSSiteSettings
+governsId: 1.2.840.113556.1.5.69
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: applicationSiteSettings
+systemMayContain: schedule, queryPolicyObject, options,msDS-Preferred-GC-Site, managedBy, interSiteTopologyRenew,interSiteTopologyGenerator, interSiteTopologyFailover
+systemPossSuperiors: site
+schemaIdGuid:19195a5d-6da0-11d0-afd3-00c04fd930c9
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=NTDS-Site-Settings,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: NTFRS-Member
+ldapDisplayName: nTFRSMember
+governsId: 1.2.840.113556.1.5.153
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMayContain: serverReference, fRSUpdateTimeout,fRSServiceCommand, fRSRootSecurity, fRSPartnerAuthLevel, fRSFlags,fRSExtensions, fRSControlOutboundBacklog, fRSControlInboundBacklog,fRSControlDataCreation, frsComputerReference
+systemPossSuperiors: nTFRSReplicaSet
+schemaIdGuid:2a132586-9373-11d1-aebc-0000f80367c1
+defaultSecurityDescriptor: D:(A;;RPLCLORC;;;AU)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;DA)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;CO)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;SY)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=NTFRS-Member,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: NTFRS-Replica-Set
+ldapDisplayName: nTFRSReplicaSet
+governsId: 1.2.840.113556.1.5.102
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMayContain: schedule, msFRS-Topology-Pref, msFRS-Hub-Member,managedBy, fRSVersionGUID, fRSServiceCommand, fRSRootSecurity,fRSReplicaSetType, fRSReplicaSetGUID, fRSPrimaryMember,fRSPartnerAuthLevel, fRSLevelLimit, fRSFlags, fRSFileFilter,fRSExtensions, fRSDSPoll, fRSDirectoryFilter
+systemPossSuperiors: nTFRSSettings
+schemaIdGuid:5245803a-ca6a-11d0-afff-0000f80367c1
+defaultSecurityDescriptor: D:(A;;RPLCLORC;;;AU)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;DA)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;CO)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;SY)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=NTFRS-Replica-Set,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: NTFRS-Settings
+ldapDisplayName: nTFRSSettings
+governsId: 1.2.840.113556.1.5.89
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: applicationSettings
+systemMayContain: managedBy, fRSExtensions
+systemPossSuperiors: nTFRSSettings, container, organizationalUnit,organization
+schemaIdGuid:f780acc2-56f0-11d1-a9c6-0000f80367c1
+defaultSecurityDescriptor: D:(A;;RPLCLORC;;;AU)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;DA)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;CO)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;SY)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=NTFRS-Settings,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: NTFRS-Subscriber
+ldapDisplayName: nTFRSSubscriber
+governsId: 1.2.840.113556.1.5.155
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMustContain: fRSStagingPath, fRSRootPath
+systemMayContain: schedule, fRSUpdateTimeout,fRSTimeLastConfigChange, fRSTimeLastCommand,fRSServiceCommandStatus, fRSServiceCommand, fRSMemberReference,fRSFlags, fRSFaultCondition, fRSExtensions
+systemPossSuperiors: nTFRSSubscriptions
+schemaIdGuid:2a132588-9373-11d1-aebc-0000f80367c1
+defaultSecurityDescriptor: D:(A;;RPLCLORC;;;AU)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;CO)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=NTFRS-Subscriber,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: NTFRS-Subscriptions
+ldapDisplayName: nTFRSSubscriptions
+governsId: 1.2.840.113556.1.5.154
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMayContain: fRSWorkingPath, fRSVersion, fRSExtensions
+systemPossSuperiors: user, computer, nTFRSSubscriptions
+schemaIdGuid:2a132587-9373-11d1-aebc-0000f80367c1
+defaultSecurityDescriptor: D:(A;;RPLCLORC;;;AU)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;CO)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=NTFRS-Subscriptions,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: OncRpc
+ldapDisplayName: oncRpc
+governsId: 1.3.6.1.1.1.2.5
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+mustContain: cn, oncRpcNumber
+mayContain: description, msSFU30Name, msSFU30NisDomain, nisMapName,msSFU30Aliases
+possSuperiors: domainDNS, nisMap, container, organizationalUnit
+schemaIdGuid:cadd1e5e-fefc-4f3f-b5a9-70e994204303
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=OncRpc,CN=Schema,CN=Configuration,<RootDomainDN>
+
+cn: Organization
+ldapDisplayName: organization
+governsId: 2.5.6.4
+objectClassCategory: 1
+rdnAttId: o
+subClassOf: top
+systemMustContain: o
+systemMayContain: x121Address, userPassword, telexNumber,teletexTerminalIdentifier, telephoneNumber, street, st, seeAlso,searchGuide, registeredAddress, preferredDeliveryMethod, postalCode,postalAddress, postOfficeBox, physicalDeliveryOfficeName, l,internationalISDNNumber, facsimileTelephoneNumber,destinationIndicator, businessCategory
+systemPossSuperiors: locality, country, domainDNS
+schemaIdGuid:bf967aa3-0de6-11d0-a285-00aa003049e2
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: FALSE
+systemOnly: FALSE
+defaultObjectCategory: CN=Organization,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Class-Registration
+ldapDisplayName: classRegistration
+governsId: 1.2.840.113556.1.5.10
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: leaf
+systemMayContain: requiredCategories, managedBy,implementedCategories, cOMTreatAsClassId, cOMProgID,cOMOtherProgId, cOMInterfaceID, cOMCLSID
+systemPossSuperiors: classStore
+schemaIdGuid:bf967a82-0de6-11d0-a285-00aa003049e2
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Class-Registration,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Organizational-Person
+ldapDisplayName: organizationalPerson
+governsId: 2.5.6.7
+objectClassCategory: 0
+rdnAttId: cn
+subClassOf: person
+mayContain: msDS-HABSeniorityIndex, msDS-PhoneticDisplayName,msDS-PhoneticCompanyName, msDS-PhoneticDepartment,msDS-PhoneticLastName, msDS-PhoneticFirstName, houseIdentifier,msExchHouseIdentifier, homePostalAddress
+systemMayContain: x121Address, comment, title, co,primaryTelexNumber, telexNumber, teletexTerminalIdentifier, street,st, registeredAddress, preferredDeliveryMethod, postalCode,postalAddress, postOfficeBox, thumbnailPhoto,physicalDeliveryOfficeName, pager, otherPager, otherTelephone,mobile, otherMobile, primaryInternationalISDNNumber, ipPhone,otherIpPhone, otherHomePhone, homePhone,otherFacsimileTelephoneNumber, personalTitle, middleName,otherMailbox, ou, o, mhsORAddress, msDS-AllowedToDelegateTo,manager, thumbnailLogo, l, internationalISDNNumber, initials,givenName, generationQualifier, facsimileTelephoneNumber,employeeID, mail, division, destinationIndicator, department, c,countryCode, company, assistant, streetAddress
+systemPossSuperiors: organizationalUnit, organization, container
+schemaIdGuid:bf967aa4-0de6-11d0-a285-00aa003049e2
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Person,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Organizational-Role
+ldapDisplayName: organizationalRole
+governsId: 2.5.6.8
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMustContain: cn
+systemMayContain: x121Address, telexNumber,teletexTerminalIdentifier, telephoneNumber, street, st, seeAlso,roleOccupant, registeredAddress, preferredDeliveryMethod,postalCode, postalAddress, postOfficeBox,physicalDeliveryOfficeName, ou, l, internationalISDNNumber,facsimileTelephoneNumber, destinationIndicator
+systemPossSuperiors: organizationalUnit, organization, container
+schemaIdGuid:a8df74bf-c5ea-11d1-bbcb-0080c76670c0
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Organizational-Role,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Organizational-Unit
+ldapDisplayName: organizationalUnit
+governsId: 2.5.6.5
+objectClassCategory: 1
+rdnAttId: ou
+subClassOf: top
+systemMustContain: ou
+systemMayContain: x121Address, userPassword, uPNSuffixes, co,telexNumber, teletexTerminalIdentifier, telephoneNumber, street, st,seeAlso, searchGuide, registeredAddress, preferredDeliveryMethod,postalCode, postalAddress, postOfficeBox,physicalDeliveryOfficeName, msCOM-UserPartitionSetLink, managedBy,thumbnailLogo, l, internationalISDNNumber, gPOptions, gPLink,facsimileTelephoneNumber, destinationIndicator, desktopProfile,defaultGroup, countryCode, c, businessCategory
+systemPossSuperiors: country, organization, organizationalUnit,domainDNS
+schemaIdGuid:bf967aa5-0de6-11d0-a285-00aa003049e2
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(OA;;CCDC;bf967a86-0de6-11d0-a285-00aa003049e2;;AO)(OA;;CCDC;bf967aba-0de6-11d0-a285-00aa003049e2;;AO)(OA;;CCDC;bf967a9c-0de6-11d0-a285-00aa003049e2;;AO)(OA;;CCDC;bf967aa8-0de6-11d0-a285-00aa003049e2;;PO)(A;;RPLCLORC;;;AU)(A;;LCRPLORC;;;ED)(OA;;CCDC;4828CC14-1437-45bc-9B07-AD6F015E5F28;;AO)
+defaultHidingValue: FALSE
+systemOnly: FALSE
+defaultObjectCategory: CN=Organizational-Unit,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Package-Registration
+ldapDisplayName: packageRegistration
+governsId: 1.2.840.113556.1.5.49
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMayContain: versionNumberLo, versionNumberHi, vendor,upgradeProductCode, setupCommand, productCode, packageType,packageName, packageFlags, msiScriptSize, msiScriptPath,msiScriptName, msiScript, msiFileList, managedBy,machineArchitecture, localeID, lastUpdateSequence, installUiLevel,iconPath, fileExtPriority, cOMTypelibId, cOMProgID, cOMInterfaceID,cOMClassID, categories, canUpgradeScript
+systemPossSuperiors: classStore
+schemaIdGuid:bf967aa6-0de6-11d0-a285-00aa003049e2
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Package-Registration,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Person
+ldapDisplayName: person
+governsId: 2.5.6.6
+objectClassCategory: 0
+rdnAttId: cn
+subClassOf: top
+systemMustContain: cn
+mayContain: attributeCertificateAttribute
+systemMayContain: userPassword, telephoneNumber, sn, serialNumber,seeAlso
+systemPossSuperiors: organizationalUnit, container
+schemaIdGuid:bf967aa7-0de6-11d0-a285-00aa003049e2
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Person,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Physical-Location
+ldapDisplayName: physicalLocation
+governsId: 1.2.840.113556.1.5.97
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: locality
+systemMayContain: managedBy
+systemPossSuperiors: physicalLocation, configuration
+schemaIdGuid:b7b13122-b82e-11d0-afee-0000f80367c1
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Physical-Location,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: PKI-Certificate-Template
+ldapDisplayName: pKICertificateTemplate
+governsId: 1.2.840.113556.1.5.177
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMayContain: pKIOverlapPeriod, pKIMaxIssuingDepth, pKIKeyUsage,pKIExtendedKeyUsage, pKIExpirationPeriod, pKIEnrollmentAccess,pKIDefaultCSPs, pKIDefaultKeySpec, pKICriticalExtensions,msPKI-RA-Signature, msPKI-RA-Policies,msPKI-RA-Application-Policies, msPKI-Template-Schema-Version,msPKI-Template-Minor-Revision, msPKI-Supersede-Templates,msPKI-Private-Key-Flag, msPKI-Minimal-Key-Size,msPKI-Enrollment-Flag, msPKI-Certificate-Policy,msPKI-Certificate-Name-Flag, msPKI-Certificate-Application-Policy,msPKI-Cert-Template-OID, flags, displayName
+systemPossSuperiors: container
+schemaIdGuid:e5209ca2-3bba-11d2-90cc-00c04fd91ab1
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=PKI-Certificate-Template,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: PKI-Enrollment-Service
+ldapDisplayName: pKIEnrollmentService
+governsId: 1.2.840.113556.1.5.178
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMayContain: signatureAlgorithms, enrollmentProviders,dNSHostName, certificateTemplates, cACertificateDN, cACertificate
+systemPossSuperiors: container
+schemaIdGuid:ee4aa692-3bba-11d2-90cc-00c04fd91ab1
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=PKI-Enrollment-Service,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: PosixAccount
+ldapDisplayName: posixAccount
+governsId: 1.3.6.1.1.1.2.0
+objectClassCategory: 3
+rdnAttId: uid
+subClassOf: top
+mayContain: uid, cn, uidNumber, gidNumber, unixHomeDirectory,homeDirectory, userPassword, unixUserPassword, loginShell, gecos,description
+schemaIdGuid:ad44bb41-67d5-4d88-b575-7b20674e76d8
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=PosixAccount,CN=Schema,CN=Configuration,<RootDomainDN>
+
+cn: PosixGroup
+ldapDisplayName: posixGroup
+governsId: 1.3.6.1.1.1.2.2
+objectClassCategory: 3
+rdnAttId: cn
+subClassOf: top
+mayContain: cn, userPassword, unixUserPassword, description,gidNumber, memberUid
+schemaIdGuid:2a9350b8-062c-4ed0-9903-dde10d06deba
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=PosixGroup,CN=Schema,CN=Configuration,<RootDomainDN>
+
+cn: Class-Schema
+ldapDisplayName: classSchema
+governsId: 1.2.840.113556.1.3.13
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMustContain: subClassOf, schemaIDGUID, objectClassCategory,governsID, defaultObjectCategory, cn
+systemMayContain: systemPossSuperiors, systemOnly, systemMustContain,systemMayContain, systemAuxiliaryClass, schemaFlagsEx, rDNAttID,possSuperiors, mustContain, msDs-Schema-Extensions, msDS-IntId,mayContain, lDAPDisplayName, isDefunct, defaultSecurityDescriptor,defaultHidingValue, classDisplayName, auxiliaryClass
+systemPossSuperiors: dMD
+schemaIdGuid:bf967a83-0de6-11d0-a285-00aa003049e2
+defaultSecurityDescriptor: D:S:
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Class-Schema,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Print-Queue
+ldapDisplayName: printQueue
+governsId: 1.2.840.113556.1.5.23
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: connectionPoint
+systemMustContain: versionNumber, uNCName, shortServerName,serverName, printerName
+systemMayContain: priority, printStatus, printStartTime,printStaplingSupported, printSpooling, printShareName,printSeparatorFile, printRateUnit, printRate, printPagesPerMinute,printOwner, printOrientationsSupported, printNumberUp, printNotify,printNetworkAddress, printMinYExtent, printMinXExtent, printMemory,printMediaSupported, printMediaReady, printMaxYExtent,printMaxXExtent, printMaxResolutionSupported, printMaxCopies,printMACAddress, printLanguage, printKeepPrintedJobs, printFormName,printEndTime, printDuplexSupported, printColor, printCollate,printBinNames, printAttributes, portName, physicalLocationObject,operatingSystemVersion, operatingSystemServicePack,operatingSystemHotfix, operatingSystem, location, driverVersion,driverName, defaultPriority, bytesPerMinute, assetNumber
+systemPossSuperiors: organizationalUnit, domainDNS, container,computer
+schemaIdGuid:bf967aa8-0de6-11d0-a285-00aa003049e2
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;PO)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;CO)(A;;RPLCLORC;;;AU)
+defaultHidingValue: FALSE
+systemOnly: FALSE
+defaultObjectCategory: CN=Print-Queue,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Query-Policy
+ldapDisplayName: queryPolicy
+governsId: 1.2.840.113556.1.5.106
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMayContain: lDAPIPDenyList, lDAPAdminLimits
+systemPossSuperiors: container
+schemaIdGuid:83cc7075-cca7-11d0-afff-0000f80367c1
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Query-Policy,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Remote-Mail-Recipient
+ldapDisplayName: remoteMailRecipient
+governsId: 1.2.840.113556.1.5.24
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemAuxiliaryClass: mailRecipient
+systemMayContain: remoteSourceType, remoteSource, managedBy
+systemPossSuperiors: organizationalUnit, domainDNS
+schemaIdGuid:bf967aa9-0de6-11d0-a285-00aa003049e2
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)(OA;;CR;ab721a55-1e2f-11d0-9819-00aa0040529b;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Remote-Mail-Recipient,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Remote-Storage-Service-Point
+ldapDisplayName: remoteStorageServicePoint
+governsId: 1.2.840.113556.1.5.146
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: serviceAdministrationPoint
+systemMayContain: remoteStorageGUID
+systemPossSuperiors: computer
+schemaIdGuid:2a39c5bd-8960-11d1-aebc-0000f80367c1
+defaultSecurityDescriptor: D:(A;;RPLCLORC;;;AU)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;DA)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;CO)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;SY)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Remote-Storage-Service-Point,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Residential-Person
+ldapDisplayName: residentialPerson
+governsId: 2.5.6.10
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: person
+systemMayContain: x121Address, title, telexNumber,teletexTerminalIdentifier, street, st, registeredAddress,preferredDeliveryMethod, postalCode, postalAddress, postOfficeBox,physicalDeliveryOfficeName, ou, l, internationalISDNNumber,facsimileTelephoneNumber, destinationIndicator, businessCategory
+systemPossSuperiors: locality, container
+schemaIdGuid:a8df74d6-c5ea-11d1-bbcb-0080c76670c0
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Residential-Person,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: rFC822LocalPart
+ldapDisplayName: rFC822LocalPart
+governsId: 0.9.2342.19200300.100.4.14
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: domain
+mayContain: x121Address, telexNumber, teletexTerminalIdentifier,telephoneNumber, street, sn, seeAlso, registeredAddress,preferredDeliveryMethod, postOfficeBox, postalCode, postalAddress,physicalDeliveryOfficeName, internationalISDNNumber,facsimileTelephoneNumber, destinationIndicator, description, cn
+possSuperiors: organizationalUnit, container
+schemaIdGuid:b93e3a78-cbae-485e-a07b-5ef4ae505686
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLOLORCWOWDSDDTDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=rFC822LocalPart,CN=Schema,CN=Configuration,<RootDomainDN>
+
+cn: RID-Manager
+ldapDisplayName: rIDManager
+governsId: 1.2.840.113556.1.5.83
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMustContain: rIDAvailablePool
+systemPossSuperiors: container
+schemaIdGuid:6617188d-8f3c-11d0-afda-00c04fd930c9
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)S:(AU;SA;CRWP;;;WD)
+defaultHidingValue: TRUE
+systemOnly: TRUE
+defaultObjectCategory: CN=RID-Manager,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: RID-Set
+ldapDisplayName: rIDSet
+governsId: 1.2.840.113556.1.5.129
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMustContain: rIDUsedPool, rIDPreviousAllocationPool,rIDNextRID, rIDAllocationPool
+systemPossSuperiors: user, container, computer
+schemaIdGuid:7bfdcb89-4807-11d1-a9c3-0000f80367c1
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: TRUE
+defaultObjectCategory: CN=RID-Set,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: room
+ldapDisplayName: room
+governsId: 0.9.2342.19200300.100.4.7
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+mustContain: cn
+mayContain: location, telephoneNumber, seeAlso, description,roomNumber
+possSuperiors: organizationalUnit, container
+schemaIdGuid:7860e5d2-c8b0-4cbb-bd45-d9455beb9206
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLOLORCWOWDSDDTDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=room,CN=Schema,CN=Configuration,<RootDomainDN>
+
+cn: Rpc-Container
+ldapDisplayName: rpcContainer
+governsId: 1.2.840.113556.1.5.136
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: container
+systemMayContain: nameServiceFlags
+systemPossSuperiors: container
+schemaIdGuid:80212842-4bdc-11d1-a9c4-0000f80367c1
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Rpc-Container,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Class-Store
+ldapDisplayName: classStore
+governsId: 1.2.840.113556.1.5.44
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMayContain: versionNumber, nextLevelStore, lastUpdateSequence,appSchemaVersion
+systemPossSuperiors: domainPolicy, computer, group, user, classStore,organizationalUnit, domainDNS, container
+schemaIdGuid:bf967a84-0de6-11d0-a285-00aa003049e2
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Class-Store,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: rpc-Entry
+ldapDisplayName: rpcEntry
+governsId: 1.2.840.113556.1.5.27
+objectClassCategory: 2
+rdnAttId: cn
+subClassOf: connectionPoint
+systemPossSuperiors: container
+schemaIdGuid:bf967aac-0de6-11d0-a285-00aa003049e2
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=rpc-Entry,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: rpc-Group
+ldapDisplayName: rpcGroup
+governsId: 1.2.840.113556.1.5.80
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: rpcEntry
+systemMayContain: rpcNsObjectID, rpcNsGroup
+systemPossSuperiors: container
+schemaIdGuid:88611bdf-8cf4-11d0-afda-00c04fd930c9
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=rpc-Group,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: rpc-Profile
+ldapDisplayName: rpcProfile
+governsId: 1.2.840.113556.1.5.82
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: rpcEntry
+systemPossSuperiors: container
+schemaIdGuid:88611be1-8cf4-11d0-afda-00c04fd930c9
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=rpc-Profile,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: rpc-Profile-Element
+ldapDisplayName: rpcProfileElement
+governsId: 1.2.840.113556.1.5.26
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: rpcEntry
+systemMustContain: rpcNsPriority, rpcNsInterfaceID
+systemMayContain: rpcNsProfileEntry, rpcNsAnnotation
+systemPossSuperiors: rpcProfile
+schemaIdGuid:f29653cf-7ad0-11d0-afd6-00c04fd930c9
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=rpc-Profile-Element,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: rpc-Server
+ldapDisplayName: rpcServer
+governsId: 1.2.840.113556.1.5.81
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: rpcEntry
+systemMayContain: rpcNsObjectID, rpcNsEntryFlags, rpcNsCodeset
+systemPossSuperiors: container
+schemaIdGuid:88611be0-8cf4-11d0-afda-00c04fd930c9
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=rpc-Server,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: rpc-Server-Element
+ldapDisplayName: rpcServerElement
+governsId: 1.2.840.113556.1.5.73
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: rpcEntry
+systemMustContain: rpcNsTransferSyntax, rpcNsInterfaceID,rpcNsBindings
+systemPossSuperiors: rpcServer
+schemaIdGuid:f29653d0-7ad0-11d0-afd6-00c04fd930c9
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=rpc-Server-Element,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: RRAS-Administration-Connection-Point
+ldapDisplayName: rRASAdministrationConnectionPoint
+governsId: 1.2.840.113556.1.5.150
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: serviceAdministrationPoint
+systemMayContain: msRRASAttribute
+systemPossSuperiors: computer
+schemaIdGuid:2a39c5be-8960-11d1-aebc-0000f80367c1
+defaultSecurityDescriptor: D:(A;;RPLCLORC;;;AU)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;DA)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;CO)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;SY)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=RRAS-Administration-Connection-Point,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: RRAS-Administration-Dictionary
+ldapDisplayName: rRASAdministrationDictionary
+governsId: 1.2.840.113556.1.5.156
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMayContain: msRRASVendorAttributeEntry
+systemPossSuperiors: container
+schemaIdGuid:f39b98ae-938d-11d1-aebd-0000f80367c1
+defaultSecurityDescriptor: D:(A;;RPLCLORC;;;AU)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;CO)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=RRAS-Administration-Dictionary,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Sam-Domain
+ldapDisplayName: samDomain
+governsId: 1.2.840.113556.1.5.3
+objectClassCategory: 3
+rdnAttId: cn
+subClassOf: top
+systemAuxiliaryClass: samDomainBase
+systemMayContain: treeName, rIDManagerReference, replicaSource,pwdProperties, pwdHistoryLength, privateKey, pekList,pekKeyChangeInterval, nTMixedDomain, nextRid, nETBIOSName,msDS-PerUserTrustTombstonesQuota, msDS-PerUserTrustQuota,ms-DS-MachineAccountQuota, msDS-LogonTimeSyncInterval,msDS-AllUsersTrustQuota, modifiedCountAtLastProm, minPwdLength,minPwdAge, maxPwdAge, lSAModifiedCount, lSACreationTime,lockoutThreshold, lockoutDuration, lockOutObservationWindow,gPOptions, gPLink, eFSPolicy, domainPolicyObject, desktopProfile,description, defaultLocalPolicyObject, creationTime,controlAccessRights, cACertificate, builtinModifiedCount,builtinCreationTime, auditingPolicy
+schemaIdGuid:bf967a90-0de6-11d0-a285-00aa003049e2
+defaultSecurityDescriptor: D:(A;;RP;;;WD)(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)(A;;RPLCLORC;;;AU)(A;;RPWPCRLCLOCCRCWDWOSW;;;DA)(A;CI;RPWPCRLCLOCCRCWDWOSDSW;;;BA)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;SY)(A;CI;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;EA)(A;CI;LC;;;RU)(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)(A;;RPRC;;;RU)(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(A;;LCRPLORC;;;ED)(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)(OA;CIIO;RPLCLORC;;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;S-1-5-32-557)(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;ED)(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;BA)(OA;CIIO;CRRPWP;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;RO)S:(AU;SA;WDWOWP;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Sam-Domain,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Sam-Domain-Base
+ldapDisplayName: samDomainBase
+governsId: 1.2.840.113556.1.5.2
+objectClassCategory: 3
+rdnAttId: cn
+subClassOf: top
+systemMayContain: uASCompat, serverState, serverRole, revision,pwdProperties, pwdHistoryLength, oEMInformation, objectSid,nTSecurityDescriptor, nextRid, modifiedCountAtLastProm,modifiedCount, minPwdLength, minPwdAge, maxPwdAge, lockoutThreshold,lockoutDuration, lockOutObservationWindow, forceLogoff,domainReplica, creationTime
+schemaIdGuid:bf967a91-0de6-11d0-a285-00aa003049e2
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Sam-Domain-Base,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ACS-Policy
+ldapDisplayName: aCSPolicy
+governsId: 1.2.840.113556.1.5.137
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMayContain: aCSTotalNoOfFlows, aCSTimeOfDay, aCSServiceType,aCSPriority, aCSPermissionBits, aCSMinimumDelayVariation,aCSMinimumLatency, aCSMaximumSDUSize, aCSMinimumPolicedSize,aCSMaxTokenRatePerFlow, aCSMaxTokenBucketPerFlow,aCSMaxPeakBandwidthPerFlow, aCSMaxDurationPerFlow,aCSMaxAggregatePeakRatePerUser, aCSIdentityName, aCSDirection,aCSAggregateTokenRatePerUser
+systemPossSuperiors: container
+schemaIdGuid:7f561288-5301-11d1-a9c5-0000f80367c1
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLOLORCWOWDSDDTDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=ACS-Policy,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Com-Connection-Point
+ldapDisplayName: comConnectionPoint
+governsId: 1.2.840.113556.1.5.11
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: connectionPoint
+systemMustContain: cn
+systemMayContain: monikerDisplayName, moniker, marshalledInterface
+systemPossSuperiors: container
+schemaIdGuid:bf967a85-0de6-11d0-a285-00aa003049e2
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Com-Connection-Point,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Sam-Server
+ldapDisplayName: samServer
+governsId: 1.2.840.113556.1.5.5
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: securityObject
+systemMayContain: samDomainUpdates
+systemPossSuperiors: domainDNS
+schemaIdGuid:bf967aad-0de6-11d0-a285-00aa003049e2
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)(A;;RPLCLORC;;;RU)(OA;;CR;91d67418-0135-4acc-8d79-c08e857cfbec;;AU)(OA;;CR;91d67418-0135-4acc-8d79-c08e857cfbec;;RU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Sam-Server,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Secret
+ldapDisplayName: secret
+governsId: 1.2.840.113556.1.5.28
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: leaf
+systemMayContain: priorValue, priorSetTime, lastSetTime, currentValue
+systemPossSuperiors: container
+schemaIdGuid:bf967aae-0de6-11d0-a285-00aa003049e2
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Secret,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Security-Object
+ldapDisplayName: securityObject
+governsId: 1.2.840.113556.1.5.1
+objectClassCategory: 2
+rdnAttId: cn
+subClassOf: top
+systemMustContain: cn
+systemPossSuperiors: container
+schemaIdGuid:bf967aaf-0de6-11d0-a285-00aa003049e2
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Security-Object,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Security-Principal
+ldapDisplayName: securityPrincipal
+governsId: 1.2.840.113556.1.5.6
+objectClassCategory: 3
+rdnAttId: cn
+subClassOf: top
+systemMustContain: sAMAccountName, objectSid
+systemMayContain: supplementalCredentials, sIDHistory,securityIdentifier, sAMAccountType, rid, tokenGroupsNoGCAcceptable,tokenGroupsGlobalAndUniversal, tokenGroups, nTSecurityDescriptor,msDS-KeyVersionNumber, altSecurityIdentities, accountNameHistory
+schemaIdGuid:bf967ab0-0de6-11d0-a285-00aa003049e2
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Security-Principal,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Server
+ldapDisplayName: server
+governsId: 1.2.840.113556.1.5.17
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMayContain: msDS-IsUserCachableAtRodc, msDS-SiteName,msDS-isRODC, msDS-isGC, mailAddress, serverReference, serialNumber,managedBy, dNSHostName, bridgeheadTransportList
+systemPossSuperiors: serversContainer
+schemaIdGuid:bf967a92-0de6-11d0-a285-00aa003049e2
+defaultSecurityDescriptor: D:(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Server,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Servers-Container
+ldapDisplayName: serversContainer
+governsId: 1.2.840.113556.1.5.7000.48
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemPossSuperiors: site
+schemaIdGuid:f780acc0-56f0-11d1-a9c6-0000f80367c1
+defaultSecurityDescriptor: D:(A;;CC;;;BA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Servers-Container,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Service-Administration-Point
+ldapDisplayName: serviceAdministrationPoint
+governsId: 1.2.840.113556.1.5.94
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: serviceConnectionPoint
+systemPossSuperiors: computer
+schemaIdGuid:b7b13123-b82e-11d0-afee-0000f80367c1
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Service-Administration-Point,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Service-Class
+ldapDisplayName: serviceClass
+governsId: 1.2.840.113556.1.5.29
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: leaf
+systemMustContain: serviceClassID, displayName
+systemMayContain: serviceClassInfo
+systemPossSuperiors: container
+schemaIdGuid:bf967ab1-0de6-11d0-a285-00aa003049e2
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Service-Class,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Service-Connection-Point
+ldapDisplayName: serviceConnectionPoint
+governsId: 1.2.840.113556.1.5.126
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: connectionPoint
+systemMayContain: versionNumberLo, versionNumberHi, versionNumber,vendor, serviceDNSNameType, serviceDNSName, serviceClassName,serviceBindingInformation, appSchemaVersion
+systemPossSuperiors: organizationalUnit, container, computer
+schemaIdGuid:28630ec1-41d5-11d1-a9c1-0000f80367c1
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;CO)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Service-Connection-Point,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Service-Instance
+ldapDisplayName: serviceInstance
+governsId: 1.2.840.113556.1.5.30
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: connectionPoint
+systemMustContain: serviceClassID, displayName
+systemMayContain: winsockAddresses, serviceInstanceVersion
+systemPossSuperiors: container
+schemaIdGuid:bf967ab2-0de6-11d0-a285-00aa003049e2
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Service-Instance,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Computer
+ldapDisplayName: computer
+governsId: 1.2.840.113556.1.3.30
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: user
+auxiliaryClass: ipHost
+mayContain: msSFU30Aliases, msSFU30NisDomain, nisMapName,msSFU30Name
+systemMayContain: msDS-IsUserCachableAtRodc, msTSProperty02,msTSProperty01, msTPM-OwnerInformation, msDS-RevealOnDemandGroup,msDS-NeverRevealGroup, msDS-PromotionSettings, msDS-SiteName,msDS-isRODC, msDS-isGC, msDS-AuthenticatedAtDC, msDS-RevealedList,msDS-RevealedUsers, msDS-ExecuteScriptPassword, msDS-KrbTgtLink,volumeCount, siteGUID, rIDSetReferences, policyReplicationFlags,physicalLocationObject, operatingSystemVersion,operatingSystemServicePack, operatingSystemHotfix, operatingSystem,networkAddress, netbootSIFFile, netbootMirrorDataFile,netbootMachineFilePath, netbootInitialization, netbootGUID,msDS-AdditionalSamAccountName, msDS-AdditionalDnsHostName,managedBy, machineRole, location, localPolicyFlags, dNSHostName,defaultLocalPolicyObject, cn, catalogs
+systemPossSuperiors: container, organizationalUnit, domainDNS
+schemaIdGuid:bf967a86-0de6-11d0-a285-00aa003049e2
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;AO)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPCRLCLORCSDDT;;;CO)(OA;;WP;4c164200-20c0-11d0-a768-00aa006e0529;;CO)(A;;RPLCLORC;;;AU)(OA;;CR;ab721a53-1e2f-11d0-9819-00aa0040529b;;WD)(A;;CCDC;;;PS)(OA;;CCDC;bf967aa8-0de6-11d0-a285-00aa003049e2;;PO)(OA;;RPWP;bf967a7f-0de6-11d0-a285-00aa003049e2;;CA)(OA;;SW;f3a64788-5306-11d1-a9c5-0000f80367c1;;PS)(OA;;RPWP;77B5B886-944A-11d1-AEBD-0000F80367C1;;PS)(OA;;SW;72e39547-7b18-11d1-adef-00c04fd8d5cd;;PS)(OA;;SW;72e39547-7b18-11d1-adef-00c04fd8d5cd;;CO)(OA;;SW;f3a64788-5306-11d1-a9c5-0000f80367c1;;CO)(OA;;WP;3e0abfd0-126a-11d0-a060-00aa006c33ed;bf967a86-0de6-11d0-a285-00aa003049e2;CO)(OA;;WP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967a86-0de6-11d0-a285-00aa003049e2;CO)(OA;;WP;bf967950-0de6-11d0-a285-00aa003049e2;bf967a86-0de6-11d0-a285-00aa003049e2;CO)(OA;;WP;bf967953-0de6-11d0-a285-00aa003049e2;bf967a86-0de6-11d0-a285-00aa003049e2;CO)(OA;;RP;46a9b11d-60ae-405a-b7e8-ff8a58d456d2;;S-1-5-32-560)
+defaultHidingValue: FALSE
+systemOnly: FALSE
+defaultObjectCategory: CN=Computer,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ShadowAccount
+ldapDisplayName: shadowAccount
+governsId: 1.3.6.1.1.1.2.1
+objectClassCategory: 3
+rdnAttId: uid
+subClassOf: top
+mayContain: uid, userPassword, description, shadowLastChange,shadowMin, shadowMax, shadowWarning, shadowInactive, shadowExpire,shadowFlag
+schemaIdGuid:5b6d8467-1a18-4174-b350-9cc6e7b4ac8d
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=ShadowAccount,CN=Schema,CN=Configuration,<RootDomainDN>
+
+cn: simpleSecurityObject
+ldapDisplayName: simpleSecurityObject
+governsId: 0.9.2342.19200300.100.4.19
+objectClassCategory: 3
+rdnAttId: cn
+subClassOf: top
+mayContain: userPassword
+schemaIdGuid:5fe69b0b-e146-4f15-b0ab-c1e5d488e094
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLOLORCWOWDSDDTDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=simpleSecurityObject,CN=Schema,CN=Configuration,<RootDomainDN>
+
+cn: Site
+ldapDisplayName: site
+governsId: 1.2.840.113556.1.5.31
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMayContain: notificationList, mSMQSiteID, mSMQSiteForeign,mSMQNt4Stub, mSMQInterval2, mSMQInterval1, managedBy, location,gPOptions, gPLink, msDS-BridgeHeadServersUsed
+systemPossSuperiors: sitesContainer
+schemaIdGuid:bf967ab3-0de6-11d0-a285-00aa003049e2
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)(A;;LCRPLORC;;;ED)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Site,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Site-Link
+ldapDisplayName: siteLink
+governsId: 1.2.840.113556.1.5.147
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMustContain: siteList
+systemMayContain: schedule, replInterval, options, cost
+systemPossSuperiors: interSiteTransport
+schemaIdGuid:d50c2cde-8951-11d1-aebc-0000f80367c1
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: FALSE
+systemOnly: FALSE
+defaultObjectCategory: CN=Site-Link,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Site-Link-Bridge
+ldapDisplayName: siteLinkBridge
+governsId: 1.2.840.113556.1.5.148
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMustContain: siteLinkList
+systemPossSuperiors: interSiteTransport
+schemaIdGuid:d50c2cdf-8951-11d1-aebc-0000f80367c1
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: FALSE
+systemOnly: FALSE
+defaultObjectCategory: CN=Site-Link-Bridge,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Sites-Container
+ldapDisplayName: sitesContainer
+governsId: 1.2.840.113556.1.5.107
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemPossSuperiors: configuration
+schemaIdGuid:7a4117da-cd67-11d0-afff-0000f80367c1
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Sites-Container,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Storage
+ldapDisplayName: storage
+governsId: 1.2.840.113556.1.5.33
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: connectionPoint
+systemMayContain: monikerDisplayName, moniker, iconPath
+systemPossSuperiors: container
+schemaIdGuid:bf967ab5-0de6-11d0-a285-00aa003049e2
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Storage,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Subnet
+ldapDisplayName: subnet
+governsId: 1.2.840.113556.1.5.96
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMayContain: siteObject, physicalLocationObject, location
+systemPossSuperiors: subnetContainer
+schemaIdGuid:b7b13124-b82e-11d0-afee-0000f80367c1
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Subnet,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Subnet-Container
+ldapDisplayName: subnetContainer
+governsId: 1.2.840.113556.1.5.95
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemPossSuperiors: sitesContainer
+schemaIdGuid:b7b13125-b82e-11d0-afee-0000f80367c1
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Subnet-Container,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: SubSchema
+ldapDisplayName: subSchema
+governsId: 2.5.20.1
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMayContain: objectClasses, modifyTimeStamp, extendedClassInfo,extendedAttributeInfo, dITContentRules, attributeTypes
+systemPossSuperiors: dMD
+schemaIdGuid:5a8b3261-c38d-11d1-bbc9-0080c76670c0
+defaultSecurityDescriptor: D:S:
+defaultHidingValue: TRUE
+systemOnly: TRUE
+defaultObjectCategory: CN=SubSchema,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Configuration
+ldapDisplayName: configuration
+governsId: 1.2.840.113556.1.5.12
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMustContain: cn
+systemMayContain: gPOptions, gPLink
+systemPossSuperiors: domainDNS
+schemaIdGuid:bf967a87-0de6-11d0-a285-00aa003049e2
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: TRUE
+defaultObjectCategory: CN=Configuration,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Top
+ldapDisplayName: top
+governsId: 2.5.6.0
+objectClassCategory: 2
+rdnAttId: cn
+subClassOf: top
+systemMustContain: objectClass, objectCategory, nTSecurityDescriptor,instanceType
+mayContain: msSFU30PosixMemberOf, msDFSR-ComputerReferenceBL,msDFSR-MemberReferenceBL, msDS-ObjectReferenceBL
+systemMayContain: msDS-NcType, msDS-PSOApplied, msDS-PrincipalName,msDS-RevealedListBL, msDS-AuthenticatedToAccountlist,msDS-IsPartialReplicaFor, msDS-IsDomainFor, msDS-IsFullReplicaFor,msDS-RevealedDSAs, msDS-KrbTgtLinkBl, url, wWWHomePage, whenCreated,whenChanged, wellKnownObjects, wbemPath, uSNSource, uSNLastObjRem,USNIntersite, uSNDSALastObjRemoved, uSNCreated, uSNChanged,systemFlags, subSchemaSubEntry, subRefs, structuralObjectClass,siteObjectBL, serverReferenceBL, sDRightsEffective, revision,repsTo, repsFrom, directReports, replUpToDateVector,replPropertyMetaData, name, queryPolicyBL, proxyAddresses,proxiedObjectName, possibleInferiors, partialAttributeSet,partialAttributeDeletionList, otherWellKnownObjects, objectVersion,objectGUID, distinguishedName, nonSecurityMemberBL, netbootSCPBL,ownerBL, msDS-ReplValueMetaData, msDS-ReplAttributeMetaData,msDS-NonMembersBL, msDS-NCReplOutboundNeighbors,msDS-NCReplInboundNeighbors, msDS-NCReplCursors,msDS-TasksForAzRoleBL, msDS-TasksForAzTaskBL,msDS-OperationsForAzRoleBL, msDS-OperationsForAzTaskBL,msDS-MembersForAzRoleBL, msDs-masteredBy, mS-DS-ConsistencyGuid,mS-DS-ConsistencyChildCount, msDS-Approx-Immed-Subordinates,msCOM-PartitionSetLink, msCOM-UserLink, modifyTimeStamp, masteredBy,managedObjects, lastKnownParent, isPrivilegeHolder, memberOf,isDeleted, isCriticalSystemObject, showInAdvancedViewOnly,fSMORoleOwner, fRSMemberReferenceBL, frsComputerReferenceBL,fromEntry, flags, extensionName, dSASignature,dSCorePropagationData, displayNamePrintable, displayName,description, createTimeStamp, cn, canonicalName,bridgeheadServerListBL, allowedChildClassesEffective,allowedChildClasses, allowedAttributesEffective, allowedAttributes,adminDisplayName, adminDescription, msDS-NC-RO-Replica-Locations-BL
+systemPossSuperiors: lostAndFound
+schemaIdGuid:bf967ab7-0de6-11d0-a285-00aa003049e2
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: TRUE
+defaultObjectCategory: CN=Top,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Trusted-Domain
+ldapDisplayName: trustedDomain
+governsId: 1.2.840.113556.1.5.34
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: leaf
+systemMayContain: msDS-SupportedEncryptionTypes, trustType,trustPosixOffset, trustPartner, trustDirection, trustAuthOutgoing,trustAuthIncoming, trustAttributes, securityIdentifier,msDS-TrustForestTrustInfo, mS-DS-CreatorSID, initialAuthOutgoing,initialAuthIncoming, flatName, domainIdentifier, domainCrossRef,additionalTrustedServiceNames
+systemPossSuperiors: container
+schemaIdGuid:bf967ab8-0de6-11d0-a285-00aa003049e2
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)(OA;;WP;736e4812-af31-11d2-b7df-00805f48caeb;bf967ab8-0de6-11d0-a285-00aa003049e2;CO)(A;;SD;;;CO)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Trusted-Domain,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Type-Library
+ldapDisplayName: typeLibrary
+governsId: 1.2.840.113556.1.5.53
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMayContain: cOMUniqueLIBID, cOMInterfaceID, cOMClassID
+systemPossSuperiors: classStore
+schemaIdGuid:281416e2-1968-11d0-a28f-00aa003049e2
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Type-Library,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: User
+ldapDisplayName: user
+governsId: 1.2.840.113556.1.5.9
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: organizationalPerson
+auxiliaryClass: shadowAccount, posixAccount
+systemAuxiliaryClass: securityPrincipal, mailRecipient
+mayContain: msSFU30NisDomain, msSFU30Name, msDS-SourceObjectDN,x500uniqueIdentifier, userSMIMECertificate, userPKCS12, uid,secretary, roomNumber, preferredLanguage, photo, labeledURI,jpegPhoto, homePostalAddress, givenName, employeeType,employeeNumber, displayName, departmentNumber, carLicense, audio
+systemMayContain: msDS-ResultantPSO, MSTSLSProperty01,MSTSLSProperty02, msTSManagingLS2, msTSManagingLS3, msTSManagingLS4,msTSLicenseVersion2, msTSLicenseVersion3, msTSLicenseVersion4,msTSExpireDate2, msTSExpireDate3, msTSExpireDate4,msDS-AuthenticatedAtDC, msDS-UserPasswordExpiryTimeComputed,msTSManagingLS, msTSLicenseVersion, msTSExpireDate, msTSProperty02,msTSProperty01, msTSInitialProgram, msTSWorkDirectory,msTSDefaultToMainPrinter, msTSConnectPrinterDrives,msTSConnectClientDrives, msTSBrokenConnectionAction,msTSReconnectionAction, msTSMaxIdleTime, msTSMaxConnectionTime,msTSMaxDisconnectionTime, msTSRemoteControl, msTSAllowLogon,msTSHomeDrive, msTSHomeDirectory, msTSProfilePath,msDS-FailedInteractiveLogonCountAtLastSuccessfulLogon,msDS-FailedInteractiveLogonCount,msDS-LastFailedInteractiveLogonTime,msDS-LastSuccessfulInteractiveLogonTime,msRADIUS-SavedFramedIpv6Route, msRADIUS-FramedIpv6Route,msRADIUS-SavedFramedIpv6Prefix, msRADIUS-FramedIpv6Prefix,msRADIUS-SavedFramedInterfaceId, msRADIUS-FramedInterfaceId,msPKIAccountCredentials, msPKIDPAPIMasterKeys,msPKIRoamingTimeStamp, msDS-SupportedEncryptionTypes,msDS-SecondaryKrbTgtNumber, pager, o, mobile, manager, mail,initials, homePhone, businessCategory, userCertificate,userWorkstations, userSharedFolderOther, userSharedFolder,userPrincipalName, userParameters, userAccountControl, unicodePwd,terminalServer, servicePrincipalName, scriptPath, pwdLastSet,profilePath, primaryGroupID, preferredOU, otherLoginWorkstations,operatorCount, ntPwdHistory, networkAddress, msRASSavedFramedRoute,msRASSavedFramedIPAddress, msRASSavedCallbackNumber,msRADIUSServiceType, msRADIUSFramedRoute, msRADIUSFramedIPAddress,msRADIUSCallbackNumber, msNPSavedCallingStationID,msNPCallingStationID, msNPAllowDialin, mSMQSignCertificatesMig,mSMQSignCertificates, mSMQDigestsMig, mSMQDigests, msIIS-FTPRoot,msIIS-FTPDir, msDS-User-Account-Control-Computed,msDS-Site-Affinity, mS-DS-CreatorSID,msDS-Cached-Membership-Time-Stamp, msDS-Cached-Membership,msDRM-IdentityCertificate, msCOM-UserPartitionSetLink, maxStorage,logonWorkstation, logonHours, logonCount, lockoutTime, localeID,lmPwdHistory, lastLogonTimestamp, lastLogon, lastLogoff, homeDrive,homeDirectory, groupsToIgnore, groupPriority, groupMembershipSAM,dynamicLDAPServer, desktopProfile, defaultClassStore, dBCSPwd,controlAccessRights, codePage, badPwdCount, badPasswordTime,adminCount, aCSPolicyName, accountExpires
+schemaIdGuid:bf967aba-0de6-11d0-a285-00aa003049e2
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;AO)(A;;RPLCLORC;;;PS)(OA;;CR;ab721a53-1e2f-11d0-9819-00aa0040529b;;PS)(OA;;CR;ab721a54-1e2f-11d0-9819-00aa0040529b;;PS)(OA;;CR;ab721a56-1e2f-11d0-9819-00aa0040529b;;PS)(OA;;RPWP;77B5B886-944A-11d1-AEBD-0000F80367C1;;PS)(OA;;RPWP;E45795B2-9455-11d1-AEBD-0000F80367C1;;PS)(OA;;RPWP;E45795B3-9455-11d1-AEBD-0000F80367C1;;PS)(OA;;RP;037088f8-0ae1-11d2-b422-00a0c968f939;;RS)(OA;;RP;4c164200-20c0-11d0-a768-00aa006e0529;;RS)(OA;;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;;RS)(A;;RC;;;AU)(OA;;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;;AU)(OA;;RP;77B5B886-944A-11d1-AEBD-0000F80367C1;;AU)(OA;;RP;E45795B3-9455-11d1-AEBD-0000F80367C1;;AU)(OA;;RP;e48d0154-bcf8-11d1-8702-00c04fb96050;;AU)(OA;;CR;ab721a53-1e2f-11d0-9819-00aa0040529b;;WD)(OA;;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;;RS)(OA;;RPWP;bf967a7f-0de6-11d0-a285-00aa003049e2;;CA)(OA;;RP;46a9b11d-60ae-405a-b7e8-ff8a58d456d2;;S-1-5-32-560)(OA;;WPRP;6db69a1c-9422-11d1-aebd-0000f80367c1;;S-1-5-32-561)
+systemPossSuperiors: builtinDomain, organizationalUnit, domainDNS
+defaultHidingValue: FALSE
+systemOnly: FALSE
+defaultObjectCategory: CN=Person,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Volume
+ldapDisplayName: volume
+governsId: 1.2.840.113556.1.5.36
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: connectionPoint
+systemMustContain: uNCName
+systemMayContain: lastContentIndexed, contentIndexingAllowed
+systemPossSuperiors: organizationalUnit, domainDNS
+schemaIdGuid:bf967abb-0de6-11d0-a285-00aa003049e2
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: FALSE
+systemOnly: FALSE
+defaultObjectCategory: CN=Volume,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Connection-Point
+ldapDisplayName: connectionPoint
+governsId: 1.2.840.113556.1.5.14
+objectClassCategory: 2
+rdnAttId: cn
+subClassOf: leaf
+systemMustContain: cn
+systemMayContain: msDS-Settings, managedBy, keywords
+systemPossSuperiors: container, computer
+schemaIdGuid:5cb41ecf-0e4c-11d0-a286-00aa003049e2
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Connection-Point,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Contact
+ldapDisplayName: contact
+governsId: 1.2.840.113556.1.5.15
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: organizationalPerson
+systemAuxiliaryClass: mailRecipient
+systemMustContain: cn
+mayContain: msDS-SourceObjectDN
+systemMayContain: notes
+systemPossSuperiors: organizationalUnit, domainDNS
+schemaIdGuid:5cb41ed0-0e4c-11d0-a286-00aa003049e2
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: FALSE
+systemOnly: FALSE
+defaultObjectCategory: CN=Person,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Container
+ldapDisplayName: container
+governsId: 1.2.840.113556.1.3.23
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMustContain: cn
+mayContain: msDS-ObjectReference
+systemMayContain: schemaVersion, defaultClassStore
+systemPossSuperiors: msDS-AzScope, msDS-AzApplication,msDS-AzAdminManager, subnet, server, nTDSService, domainDNS,organization, configuration, container, organizationalUnit
+schemaIdGuid:bf967a8b-0de6-11d0-a285-00aa003049e2
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Container,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Control-Access-Right
+ldapDisplayName: controlAccessRight
+governsId: 1.2.840.113556.1.5.77
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMayContain: validAccesses, rightsGuid, localizationDisplayId,appliesTo
+systemPossSuperiors: container
+schemaIdGuid:8297931e-86d3-11d0-afda-00c04fd930c9
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Control-Access-Right,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Country
+ldapDisplayName: country
+governsId: 2.5.6.2
+objectClassCategory: 0
+rdnAttId: c
+subClassOf: top
+systemMustContain: c
+systemMayContain: co, searchGuide
+systemPossSuperiors: domainDNS, organization
+schemaIdGuid:bf967a8c-0de6-11d0-a285-00aa003049e2
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Country,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: CRL-Distribution-Point
+ldapDisplayName: cRLDistributionPoint
+governsId: 2.5.6.19
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMustContain: cn
+systemMayContain: deltaRevocationList, cRLPartitionedRevocationList,certificateRevocationList, certificateAuthorityObject,authorityRevocationList
+systemPossSuperiors: container
+schemaIdGuid:167758ca-47f3-11d1-a9c3-0000f80367c1
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=CRL-Distribution-Point,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Cross-Ref
+ldapDisplayName: crossRef
+governsId: 1.2.840.113556.1.3.11
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMustContain: nCName, dnsRoot, cn
+systemMayContain: msDS-NC-RO-Replica-Locations, trustParent,superiorDNSRoot, rootTrust, nTMixedDomain, nETBIOSName, Enabled,msDS-SDReferenceDomain,msDS-Replication-Notify-Subsequent-DSA-Delay,msDS-Replication-Notify-First-DSA-Delay, msDS-NC-Replica-Locations,msDS-DnsRootAlias, msDS-Behavior-Version
+systemPossSuperiors: crossRefContainer
+schemaIdGuid:bf967a8d-0de6-11d0-a285-00aa003049e2
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Cross-Ref,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ACS-Resource-Limits
+ldapDisplayName: aCSResourceLimits
+governsId: 1.2.840.113556.1.5.191
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMayContain: aCSMaxTokenRatePerFlow, aCSServiceType,aCSMaxPeakBandwidthPerFlow, aCSMaxPeakBandwidth,aCSAllocableRSVPBandwidth
+systemPossSuperiors: container
+schemaIdGuid:2e899b04-2834-11d3-91d4-0000f87a57d4
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLOLORCWOWDSDDTDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=ACS-Resource-Limits,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Cross-Ref-Container
+ldapDisplayName: crossRefContainer
+governsId: 1.2.840.113556.1.5.7000.53
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMayContain: msDS-SPNSuffixes, uPNSuffixes, msDS-UpdateScript,msDS-ExecuteScriptPassword, msDS-Behavior-Version
+systemPossSuperiors: configuration
+schemaIdGuid:ef9e60e0-56f7-11d1-a9c6-0000f80367c1
+defaultSecurityDescriptor: D:(A;;GA;;;SY)
+defaultHidingValue: FALSE
+systemOnly: TRUE
+defaultObjectCategory: CN=Cross-Ref-Container,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Device
+ldapDisplayName: device
+governsId: 2.5.6.14
+objectClassCategory: 0
+rdnAttId: cn
+subClassOf: top
+auxiliaryClass: ipHost, ieee802Device, bootableDevice
+systemMustContain: cn
+mayContain: msSFU30Name, msSFU30NisDomain, nisMapName, msSFU30Aliases
+systemMayContain: serialNumber, seeAlso, owner, ou, o, l
+systemPossSuperiors: domainDNS, organizationalUnit, organization,container
+schemaIdGuid:bf967a8e-0de6-11d0-a285-00aa003049e2
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Device,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Dfs-Configuration
+ldapDisplayName: dfsConfiguration
+governsId: 1.2.840.113556.1.5.42
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemPossSuperiors: container, domainDNS
+schemaIdGuid:8447f9f2-1027-11d0-a05f-00aa006c33ed
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Dfs-Configuration,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: DHCP-Class
+ldapDisplayName: dHCPClass
+governsId: 1.2.840.113556.1.5.132
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMustContain: dhcpUniqueKey, dhcpType, dhcpIdentification,dhcpFlags
+systemMayContain: superScopes, superScopeDescription,optionsLocation, optionDescription, networkAddress, mscopeId,dhcpUpdateTime, dhcpSubnets, dhcpState, dhcpSites, dhcpServers,dhcpReservations, dhcpRanges, dhcpProperties, dhcpOptions,dhcpObjName, dhcpObjDescription, dhcpMaxKey, dhcpMask, dhcpClasses
+systemPossSuperiors: container
+schemaIdGuid:963d2756-48be-11d1-a9c3-0000f80367c1
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=DHCP-Class,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Display-Specifier
+ldapDisplayName: displaySpecifier
+governsId: 1.2.840.113556.1.5.84
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMayContain: treatAsLeaf, shellPropertyPages, shellContextMenu,scopeFlags, queryFilter, iconPath, extraColumns, creationWizard,createWizardExt, createDialog, contextMenu, classDisplayName,attributeDisplayNames, adminPropertyPages,adminMultiselectPropertyPages, adminContextMenu
+systemPossSuperiors: container
+schemaIdGuid:e0fa1e8a-9b45-11d0-afdd-00c04fd930c9
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Display-Specifier,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Display-Template
+ldapDisplayName: displayTemplate
+governsId: 1.2.840.113556.1.3.59
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMustContain: cn
+systemMayContain: originalDisplayTableMSDOS, originalDisplayTable,helpFileName, helpData32, helpData16, addressEntryDisplayTableMSDOS,addressEntryDisplayTable
+systemPossSuperiors: container
+schemaIdGuid:5fd4250c-1262-11d0-a060-00aa006c33ed
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Display-Template,CN=Schema,CN=Configuration,<RootDomainDN>
+
+cn: DMD
+ldapDisplayName: dMD
+governsId: 1.2.840.113556.1.3.9
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMustContain: cn
+systemMayContain: schemaUpdate, schemaInfo, prefixMap,msDs-Schema-Extensions, msDS-IntId, dmdName
+systemPossSuperiors: configuration
+schemaIdGuid:bf967a8f-0de6-11d0-a285-00aa003049e2
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: TRUE
+defaultObjectCategory: CN=DMD,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Dns-Node
+ldapDisplayName: dnsNode
+governsId: 1.2.840.113556.1.5.86
+objectClassCategory: 1
+rdnAttId: dc
+subClassOf: top
+systemMustContain: dc
+systemMayContain: dNSTombstoned, dnsRecord, dNSProperty
+systemPossSuperiors: dnsZone
+schemaIdGuid:e0fa1e8c-9b45-11d0-afdd-00c04fd930c9
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;ED)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;CO)(A;;RPLCLORC;;;WD)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Dns-Node,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Dns-Zone
+ldapDisplayName: dnsZone
+governsId: 1.2.840.113556.1.5.85
+objectClassCategory: 1
+rdnAttId: dc
+subClassOf: top
+systemMustContain: dc
+systemMayContain: managedBy, dnsSecureSecondaries, dNSProperty,dnsNotifySecondaries, dnsAllowXFR, dnsAllowDynamic
+systemPossSuperiors: container
+schemaIdGuid:e0fa1e8b-9b45-11d0-afdd-00c04fd930c9
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;ED)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;CC;;;AU)(A;;RPLCLORC;;;WD)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;CO)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Dns-Zone,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: document
+ldapDisplayName: document
+governsId: 0.9.2342.19200300.100.4.6
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+mayContain: documentIdentifier, documentPublisher, documentLocation,documentAuthor, documentVersion, documentTitle, ou, o, l, seeAlso,description, cn
+possSuperiors: organizationalUnit, container
+schemaIdGuid:39bad96d-c2d6-4baf-88ab-7e4207600117
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLOLORCWOWDSDDTDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=document,CN=Schema,CN=Configuration,<RootDomainDN>
+
+cn: ACS-Subnet
+ldapDisplayName: aCSSubnet
+governsId: 1.2.840.113556.1.5.138
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMayContain: aCSServerList, aCSRSVPLogFilesLocation,aCSRSVPAccountFilesLocation, aCSNonReservedTxSize,aCSNonReservedTxLimit, aCSNonReservedTokenSize,aCSNonReservedPeakRate, aCSNonReservedMinPolicedSize,aCSNonReservedMaxSDUSize, aCSMaxTokenRatePerFlow,aCSMaxSizeOfRSVPLogFile, aCSMaxSizeOfRSVPAccountFile,aCSMaxPeakBandwidthPerFlow, aCSMaxPeakBandwidth, aCSMaxNoOfLogFiles,aCSMaxNoOfAccountFiles, aCSMaxDurationPerFlow, aCSEventLogLevel,aCSEnableRSVPMessageLogging, aCSEnableRSVPAccounting,aCSEnableACSService, aCSDSBMRefresh, aCSDSBMPriority,aCSDSBMDeadTime, aCSCacheTimeout, aCSAllocableRSVPBandwidth
+systemPossSuperiors: container
+schemaIdGuid:7f561289-5301-11d1-a9c5-0000f80367c1
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=ACS-Subnet,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: documentSeries
+ldapDisplayName: documentSeries
+governsId: 0.9.2342.19200300.100.4.9
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+mustContain: cn
+mayContain: telephoneNumber, ou, o, l, seeAlso, description
+possSuperiors: organizationalUnit, container
+schemaIdGuid:7a2be07c-302f-4b96-bc90-0795d66885f8
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLOLORCWOWDSDDTDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=documentSeries,CN=Schema,CN=Configuration,<RootDomainDN>
+
+cn: Domain
+ldapDisplayName: domain
+governsId: 1.2.840.113556.1.5.66
+objectClassCategory: 2
+rdnAttId: dc
+subClassOf: top
+systemMustContain: dc
+systemPossSuperiors: domain, organization
+schemaIdGuid:19195a5a-6da0-11d0-afd3-00c04fd930c9
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Domain-DNS,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Domain-DNS
+ldapDisplayName: domainDNS
+governsId: 1.2.840.113556.1.5.67
+objectClassCategory: 1
+rdnAttId: dc
+subClassOf: domain
+systemAuxiliaryClass: samDomain
+systemMayContain: msDS-Behavior-Version, msDS-AllowedDNSSuffixes,managedBy
+systemPossSuperiors: domainDNS
+schemaIdGuid:19195a5b-6da0-11d0-afd3-00c04fd930c9
+defaultSecurityDescriptor: D:(A;;RP;;;WD)(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)(A;;RPLCLORC;;;AU)(A;;RPWPCRLCLOCCRCWDWOSW;;;DA)(A;CI;RPWPCRLCLOCCRCWDWOSDSW;;;BA)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;SY)(A;CI;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;EA)(A;CI;LC;;;RU)(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)(A;;RPRC;;;RU)(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(A;;LCRPLORC;;;ED)(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)(OA;CIIO;RPLCLORC;;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;S-1-5-32-557)(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;ED)(OA;;CR;1131f6ae-9c07-11d1-f79f-00c04fc2dcd2;;BA)(OA;CIIO;CRRPWP;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;RO)S:(AU;SA;WDWOWP;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)
+defaultHidingValue: FALSE
+systemOnly: FALSE
+defaultObjectCategory: CN=Domain-DNS,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Domain-Policy
+ldapDisplayName: domainPolicy
+governsId: 1.2.840.113556.1.5.18
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: leaf
+systemMayContain: qualityOfService, pwdProperties, pwdHistoryLength,publicKeyPolicy, proxyLifetime, minTicketAge, minPwdLength,minPwdAge, maxTicketAge, maxRenewAge, maxPwdAge, managedBy,lockoutThreshold, lockoutDuration, lockOutObservationWindow,ipsecPolicyReference, forceLogoff, eFSPolicy, domainWidePolicy,domainPolicyReference, domainCAs, defaultLocalPolicyObject,authenticationOptions
+systemPossSuperiors: organizationalUnit, domainDNS, container
+schemaIdGuid:bf967a99-0de6-11d0-a285-00aa003049e2
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Domain-Policy,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: domainRelatedObject
+ldapDisplayName: domainRelatedObject
+governsId: 0.9.2342.19200300.100.4.17
+objectClassCategory: 3
+rdnAttId: cn
+subClassOf: top
+mayContain: associatedDomain
+schemaIdGuid:8bfd2d3d-efda-4549-852c-f85e137aedc6
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLOLORCWOWDSDDTDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=domainRelatedObject,CN=Schema,CN=Configuration,<RootDomainDN>
+
+cn: DSA
+ldapDisplayName: dSA
+governsId: 2.5.6.13
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: applicationEntity
+systemMayContain: knowledgeInformation
+systemPossSuperiors: server, computer
+schemaIdGuid:3fdfee52-47f4-11d1-a9c3-0000f80367c1
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=DSA,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: DS-UI-Settings
+ldapDisplayName: dSUISettings
+governsId: 1.2.840.113556.1.5.183
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMayContain: msDS-Non-Security-Group-Extra-Classes,msDS-Security-Group-Extra-Classes, msDS-FilterContainers,dSUIShellMaximum, dSUIAdminNotification, dSUIAdminMaximum
+systemPossSuperiors: container
+schemaIdGuid:09b10f14-6f93-11d2-9905-0000f87a57d4
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=DS-UI-Settings,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Dynamic-Object
+ldapDisplayName: dynamicObject
+governsId: 1.3.6.1.4.1.1466.101.119.2
+objectClassCategory: 3
+rdnAttId: cn
+subClassOf: top
+systemMayContain: msDS-Entry-Time-To-Die, entryTTL
+schemaIdGuid:66d51249-3355-4c1f-b24e-81f252aca23b
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Dynamic-Object,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: File-Link-Tracking
+ldapDisplayName: fileLinkTracking
+governsId: 1.2.840.113556.1.5.52
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemPossSuperiors: container
+schemaIdGuid:dd712229-10e4-11d0-a05f-00aa006c33ed
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=File-Link-Tracking,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: File-Link-Tracking-Entry
+ldapDisplayName: fileLinkTrackingEntry
+governsId: 1.2.840.113556.1.5.59
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemPossSuperiors: fileLinkTracking
+schemaIdGuid:8e4eb2ed-4712-11d0-a1a0-00c04fd930c9
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=File-Link-Tracking-Entry,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Address-Book-Container
+ldapDisplayName: addressBookContainer
+governsId: 1.2.840.113556.1.5.125
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMustContain: displayName
+systemMayContain: purportedSearch
+systemPossSuperiors: addressBookContainer, configuration
+schemaIdGuid:3e74f60f-3e73-11d1-a9c0-0000f80367c1
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)(OA;;CR;a1990816-4298-11d1-ade2-00c04fd8d5cd;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Address-Book-Container,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Foreign-Security-Principal
+ldapDisplayName: foreignSecurityPrincipal
+governsId: 1.2.840.113556.1.5.76
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMustContain: objectSid
+systemMayContain: foreignIdentifier
+systemPossSuperiors: container
+schemaIdGuid:89e31c12-8530-11d0-afda-00c04fd930c9
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;AO)(A;;RPLCLORC;;;PS)(OA;;CR;ab721a53-1e2f-11d0-9819-00aa0040529b;;PS)(OA;;CR;ab721a54-1e2f-11d0-9819-00aa0040529b;;PS)(OA;;CR;ab721a56-1e2f-11d0-9819-00aa0040529b;;PS)(OA;;RPWP;77B5B886-944A-11d1-AEBD-0000F80367C1;;PS)(OA;;RPWP;E45795B2-9455-11d1-AEBD-0000F80367C1;;PS)(OA;;RPWP;E45795B3-9455-11d1-AEBD-0000F80367C1;;PS)(A;;RC;;;AU)(OA;;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;;AU)(OA;;RP;77B5B886-944A-11d1-AEBD-0000F80367C1;;AU)(OA;;RP;E45795B3-9455-11d1-AEBD-0000F80367C1;;AU)(OA;;RP;e48d0154-bcf8-11d1-8702-00c04fb96050;;AU)(OA;;CR;ab721a53-1e2f-11d0-9819-00aa0040529b;;WD)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Foreign-Security-Principal,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: friendlyCountry
+ldapDisplayName: friendlyCountry
+governsId: 0.9.2342.19200300.100.4.18
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: country
+mustContain: co
+schemaIdGuid:c498f152-dc6b-474a-9f52-7cdba3d7d351
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLOLORCWOWDSDDTDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=friendlyCountry,CN=Schema,CN=Configuration,<RootDomainDN>
+
+cn: FT-Dfs
+ldapDisplayName: fTDfs
+governsId: 1.2.840.113556.1.5.43
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMustContain: remoteServerName, pKTGuid, pKT
+systemMayContain: uNCName, managedBy, keywords
+systemPossSuperiors: dfsConfiguration
+schemaIdGuid:8447f9f3-1027-11d0-a05f-00aa006c33ed
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;CO)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=FT-Dfs,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Group
+ldapDisplayName: group
+governsId: 1.2.840.113556.1.5.8
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+auxiliaryClass: posixGroup
+systemAuxiliaryClass: mailRecipient, securityPrincipal
+systemMustContain: groupType
+mayContain: msSFU30Name, msSFU30NisDomain, msSFU30PosixMember
+systemMayContain: msDS-AzApplicationData,msDS-AzLastImportedBizRulePath, msDS-AzBizRuleLanguage,msDS-AzBizRule, msDS-AzGenericData, msDS-AzObjectGuid,primaryGroupToken, operatorCount, nTGroupMembers, nonSecurityMember,msDS-NonMembers, msDS-AzLDAPQuery, member, managedBy,groupMembershipSAM, groupAttributes, mail, desktopProfile,controlAccessRights, adminCount
+systemPossSuperiors: msDS-AzScope, msDS-AzApplication,msDS-AzAdminManager, container, builtinDomain, organizationalUnit,domainDNS
+schemaIdGuid:bf967a9c-0de6-11d0-a285-00aa003049e2
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;AO)(A;;RPLCLORC;;;PS)(OA;;CR;ab721a55-1e2f-11d0-9819-00aa0040529b;;AU)(OA;;RP;46a9b11d-60ae-405a-b7e8-ff8a58d456d2;;S-1-5-32-560)
+defaultHidingValue: FALSE
+systemOnly: FALSE
+defaultObjectCategory: CN=Group,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Group-Of-Names
+ldapDisplayName: groupOfNames
+governsId: 2.5.6.9
+objectClassCategory: 0
+rdnAttId: cn
+subClassOf: top
+systemMustContain: member, cn
+systemMayContain: seeAlso, owner, ou, o, businessCategory
+systemPossSuperiors: organizationalUnit, locality, organization,container
+schemaIdGuid:bf967a9d-0de6-11d0-a285-00aa003049e2
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Group-Of-Names,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: groupOfUniqueNames
+ldapDisplayName: groupOfUniqueNames
+governsId: 2.5.6.17
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+mustContain: uniqueMember, cn
+mayContain: seeAlso, owner, ou, o, description, businessCategory
+possSuperiors: domainDNS, organizationalUnit, container
+schemaIdGuid:0310a911-93a3-4e21-a7a3-55d85ab2c48b
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;AO)(A;;RPLCLORC;;;PS)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=groupOfUniqueNames,CN=Schema,CN=Configuration,<RootDomainDN>
+
+cn: Group-Policy-Container
+ldapDisplayName: groupPolicyContainer
+governsId: 1.2.840.113556.1.5.157
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: container
+systemMayContain: versionNumber, gPCWQLFilter, gPCUserExtensionNames,gPCMachineExtensionNames, gPCFunctionalityVersion, gPCFileSysPath,flags
+schemaIdGuid:f30e3bc2-9ff0-11d1-b603-0000f80367c1
+defaultSecurityDescriptor: D:P(A;CI;RPWPCCDCLCLOLORCWOWDSDDTSW;;;DA)(A;CI;RPWPCCDCLCLOLORCWOWDSDDTSW;;;EA)(A;CI;RPWPCCDCLCLOLORCWOWDSDDTSW;;;CO)(A;CI;RPWPCCDCLCLORCWOWDSDDTSW;;;SY)(A;CI;RPLCLORC;;;AU)(OA;CI;CR;edacfd8f-ffb3-11d1-b41d-00a0c968f939;;AU)(A;CI;LCRPLORC;;;ED)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Group-Policy-Container,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: IEEE802Device
+ldapDisplayName: ieee802Device
+governsId: 1.3.6.1.1.1.2.11
+objectClassCategory: 3
+rdnAttId: cn
+subClassOf: top
+mayContain: cn, macAddress
+schemaIdGuid:a699e529-a637-4b7d-a0fb-5dc466a0b8a7
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=IEEE802Device,CN=Schema,CN=Configuration,<RootDomainDN>
+
+cn: Index-Server-Catalog
+ldapDisplayName: indexServerCatalog
+governsId: 1.2.840.113556.1.5.130
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: connectionPoint
+systemMustContain: creator
+systemMayContain: uNCName, queryPoint, indexedScopes, friendlyNames
+systemPossSuperiors: organizationalUnit, container
+schemaIdGuid:7bfdcb8a-4807-11d1-a9c3-0000f80367c1
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Index-Server-Catalog,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: inetOrgPerson
+ldapDisplayName: inetOrgPerson
+governsId: 2.16.840.1.113730.3.2.2
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: user
+mayContain: x500uniqueIdentifier, userSMIMECertificate, userPKCS12,userCertificate, uid, secretary, roomNumber, preferredLanguage,photo, pager, o, mobile, manager, mail, labeledURI, jpegPhoto,initials, homePostalAddress, homePhone, givenName, employeeType,employeeNumber, displayName, departmentNumber, carLicense,businessCategory, audio
+possSuperiors: domainDNS, organizationalUnit, container
+schemaIdGuid:4828cc14-1437-45bc-9b07-ad6f015e5f28
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;AO)(A;;RPLCLORC;;;PS)(OA;;CR;ab721a53-1e2f-11d0-9819-00aa0040529b;;PS)(OA;;CR;ab721a54-1e2f-11d0-9819-00aa0040529b;;PS)(OA;;CR;ab721a56-1e2f-11d0-9819-00aa0040529b;;PS)(OA;;RPWP;77B5B886-944A-11d1-AEBD-0000F80367C1;;PS)(OA;;RPWP;E45795B2-9455-11d1-AEBD-0000F80367C1;;PS)(OA;;RPWP;E45795B3-9455-11d1-AEBD-0000F80367C1;;PS)(OA;;RP;037088f8-0ae1-11d2-b422-00a0c968f939;;RS)(OA;;RP;4c164200-20c0-11d0-a768-00aa006e0529;;RS)(OA;;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;;RS)(A;;RC;;;AU)(OA;;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;;AU)(OA;;RP;77B5B886-944A-11d1-AEBD-0000F80367C1;;AU)(OA;;RP;E45795B3-9455-11d1-AEBD-0000F80367C1;;AU)(OA;;RP;e48d0154-bcf8-11d1-8702-00c04fb96050;;AU)(OA;;CR;ab721a53-1e2f-11d0-9819-00aa0040529b;;WD)(OA;;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;;RS)(OA;;RPWP;bf967a7f-0de6-11d0-a285-00aa003049e2;;CA)(OA;;RP;46a9b11d-60ae-405a-b7e8-ff8a58d456d2;;S-1-5-32-560)(OA;;WPRP;6db69a1c-9422-11d1-aebd-0000f80367c1;;S-1-5-32-561)
+defaultHidingValue: FALSE
+systemOnly: FALSE
+defaultObjectCategory: CN=Person,CN=Schema,CN=Configuration,<RootDomainDN>
+
+cn: Address-Template
+ldapDisplayName: addressTemplate
+governsId: 1.2.840.113556.1.3.58
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: displayTemplate
+systemMustContain: displayName
+systemMayContain: proxyGenerationEnabled, perRecipDialogDisplayTable,perMsgDialogDisplayTable, addressType, addressSyntax
+systemPossSuperiors: container
+schemaIdGuid:5fd4250a-1262-11d0-a060-00aa006c33ed
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Address-Template,CN=Schema,CN=Configuration,<RootDomainDN>
+
+cn: Infrastructure-Update
+ldapDisplayName: infrastructureUpdate
+governsId: 1.2.840.113556.1.5.175
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMayContain: dNReferenceUpdate
+systemPossSuperiors: infrastructureUpdate, domain
+schemaIdGuid:2df90d89-009f-11d2-aa4c-00c04fd7d83a
+defaultSecurityDescriptor: D:(A;;GA;;;SY)
+defaultHidingValue: TRUE
+systemOnly: TRUE
+defaultObjectCategory: CN=Infrastructure-Update,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Intellimirror-Group
+ldapDisplayName: intellimirrorGroup
+governsId: 1.2.840.113556.1.5.152
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemPossSuperiors: domainDNS, organizationalUnit, container
+schemaIdGuid:07383086-91df-11d1-aebc-0000f80367c1
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;CCDC;;;CO)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Intellimirror-Group,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Intellimirror-SCP
+ldapDisplayName: intellimirrorSCP
+governsId: 1.2.840.113556.1.5.151
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: serviceAdministrationPoint
+systemMayContain: netbootTools, netbootServer, netbootNewMachineOU,netbootNewMachineNamingPolicy, netbootMaxClients,netbootMachineFilePath, netbootLocallyInstalledOSes,netbootLimitClients, netbootIntelliMirrorOSes,netbootCurrentClientCount, netbootAnswerRequests,netbootAnswerOnlyValidClients, netbootAllowNewClients
+systemPossSuperiors: computer, intellimirrorGroup
+schemaIdGuid:07383085-91df-11d1-aebc-0000f80367c1
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Intellimirror-SCP,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Inter-Site-Transport
+ldapDisplayName: interSiteTransport
+governsId: 1.2.840.113556.1.5.141
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMustContain: transportDLLName, transportAddressAttribute
+systemMayContain: replInterval, options
+systemPossSuperiors: interSiteTransportContainer
+schemaIdGuid:26d97376-6070-11d1-a9c6-0000f80367c1
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Inter-Site-Transport,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Inter-Site-Transport-Container
+ldapDisplayName: interSiteTransportContainer
+governsId: 1.2.840.113556.1.5.140
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemPossSuperiors: sitesContainer
+schemaIdGuid:26d97375-6070-11d1-a9c6-0000f80367c1
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Inter-Site-Transport-Container,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: IpHost
+ldapDisplayName: ipHost
+governsId: 1.3.6.1.1.1.2.6
+objectClassCategory: 3
+rdnAttId: cn
+subClassOf: top
+mayContain: manager, cn, description, ipHostNumber, uid, l
+schemaIdGuid:ab911646-8827-4f95-8780-5a8f008eb68f
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=IpHost,CN=Schema,CN=Configuration,<RootDomainDN>
+
+cn: IpNetwork
+ldapDisplayName: ipNetwork
+governsId: 1.3.6.1.1.1.2.7
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+mustContain: cn, ipNetworkNumber
+mayContain: manager, description, ipNetmaskNumber, uid, l,msSFU30Name, msSFU30NisDomain, nisMapName, msSFU30Aliases
+possSuperiors: domainDNS, nisMap, container, organizationalUnit
+schemaIdGuid:d95836c3-143e-43fb-992a-b057f1ecadf9
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=IpNetwork,CN=Schema,CN=Configuration,<RootDomainDN>
+
+cn: IpProtocol
+ldapDisplayName: ipProtocol
+governsId: 1.3.6.1.1.1.2.4
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+mustContain: cn, ipProtocolNumber
+mayContain: description, msSFU30Name, msSFU30NisDomain, nisMapName,msSFU30Aliases
+possSuperiors: domainDNS, nisMap, container, organizationalUnit
+schemaIdGuid:9c2dcbd2-fbf0-4dc7-ace0-8356dcd0f013
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=IpProtocol,CN=Schema,CN=Configuration,<RootDomainDN>
+
+cn: Ipsec-Base
+ldapDisplayName: ipsecBase
+governsId: 1.2.840.113556.1.5.7000.56
+objectClassCategory: 2
+rdnAttId: cn
+subClassOf: top
+systemMayContain: ipsecOwnersReference, ipsecName, ipsecID,ipsecDataType, ipsecData
+schemaIdGuid:b40ff825-427a-11d1-a9c2-0000f80367c1
+defaultSecurityDescriptor: D:
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Ipsec-Base,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Ipsec-Filter
+ldapDisplayName: ipsecFilter
+governsId: 1.2.840.113556.1.5.118
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: ipsecBase
+systemPossSuperiors: organizationalUnit, computer, container
+schemaIdGuid:b40ff826-427a-11d1-a9c2-0000f80367c1
+defaultSecurityDescriptor: D:
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Ipsec-Filter,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Application-Entity
+ldapDisplayName: applicationEntity
+governsId: 2.5.6.12
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMustContain: presentationAddress, cn
+systemMayContain: supportedApplicationContext, seeAlso, ou, o, l
+systemPossSuperiors: applicationProcess, organizationalUnit,container
+schemaIdGuid:3fdfee4f-47f4-11d1-a9c3-0000f80367c1
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Application-Entity,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Ipsec-ISAKMP-Policy
+ldapDisplayName: ipsecISAKMPPolicy
+governsId: 1.2.840.113556.1.5.120
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: ipsecBase
+systemPossSuperiors: container, computer, organizationalUnit
+schemaIdGuid:b40ff828-427a-11d1-a9c2-0000f80367c1
+defaultSecurityDescriptor: D:
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Ipsec-ISAKMP-Policy,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Ipsec-Negotiation-Policy
+ldapDisplayName: ipsecNegotiationPolicy
+governsId: 1.2.840.113556.1.5.119
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: ipsecBase
+systemMayContain: iPSECNegotiationPolicyType,iPSECNegotiationPolicyAction
+systemPossSuperiors: organizationalUnit, computer, container
+schemaIdGuid:b40ff827-427a-11d1-a9c2-0000f80367c1
+defaultSecurityDescriptor: D:
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Ipsec-Negotiation-Policy,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Ipsec-NFA
+ldapDisplayName: ipsecNFA
+governsId: 1.2.840.113556.1.5.121
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: ipsecBase
+systemMayContain: ipsecNegotiationPolicyReference,ipsecFilterReference
+systemPossSuperiors: container, computer, organizationalUnit
+schemaIdGuid:b40ff829-427a-11d1-a9c2-0000f80367c1
+defaultSecurityDescriptor: D:
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Ipsec-NFA,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Ipsec-Policy
+ldapDisplayName: ipsecPolicy
+governsId: 1.2.840.113556.1.5.98
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: ipsecBase
+systemMayContain: ipsecNFAReference, ipsecISAKMPReference
+systemPossSuperiors: organizationalUnit, computer, container
+schemaIdGuid:b7b13121-b82e-11d0-afee-0000f80367c1
+defaultSecurityDescriptor: D:
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Ipsec-Policy,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: IpService
+ldapDisplayName: ipService
+governsId: 1.3.6.1.1.1.2.3
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+mustContain: ipServiceProtocol, ipServicePort, cn
+mayContain: description, msSFU30Name, msSFU30NisDomain,msSFU30Aliases, nisMapName
+possSuperiors: domainDNS, nisMap, container, organizationalUnit
+schemaIdGuid:2517fadf-fa97-48ad-9de6-79ac5721f864
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=IpService,CN=Schema,CN=Configuration,<RootDomainDN>
+
+cn: Leaf
+ldapDisplayName: leaf
+governsId: 1.2.840.113556.1.5.20
+objectClassCategory: 2
+rdnAttId: cn
+subClassOf: top
+schemaIdGuid:bf967a9e-0de6-11d0-a285-00aa003049e2
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Leaf,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Licensing-Site-Settings
+ldapDisplayName: licensingSiteSettings
+governsId: 1.2.840.113556.1.5.78
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: applicationSiteSettings
+systemMayContain: siteServer
+systemPossSuperiors: site
+schemaIdGuid:1be8f17d-a9ff-11d0-afe2-00c04fd930c9
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Licensing-Site-Settings,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Link-Track-Object-Move-Table
+ldapDisplayName: linkTrackObjectMoveTable
+governsId: 1.2.840.113556.1.5.91
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: fileLinkTracking
+systemPossSuperiors: fileLinkTracking
+schemaIdGuid:ddac0cf5-af8f-11d0-afeb-00c04fd930c9
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Link-Track-Object-Move-Table,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Link-Track-OMT-Entry
+ldapDisplayName: linkTrackOMTEntry
+governsId: 1.2.840.113556.1.5.93
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: leaf
+systemMayContain: timeRefresh, oMTIndxGuid, oMTGuid, currentLocation,birthLocation
+systemPossSuperiors: linkTrackObjectMoveTable
+schemaIdGuid:ddac0cf7-af8f-11d0-afeb-00c04fd930c9
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Link-Track-OMT-Entry,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Link-Track-Vol-Entry
+ldapDisplayName: linkTrackVolEntry
+governsId: 1.2.840.113556.1.5.92
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: leaf
+systemMayContain: volTableIdxGUID, volTableGUID, timeVolChange,timeRefresh, seqNotification, objectCount, linkTrackSecret,currMachineId
+systemPossSuperiors: linkTrackVolumeTable
+schemaIdGuid:ddac0cf6-af8f-11d0-afeb-00c04fd930c9
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Link-Track-Vol-Entry,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Application-Process
+ldapDisplayName: applicationProcess
+governsId: 2.5.6.11
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMustContain: cn
+systemMayContain: seeAlso, ou, l
+systemPossSuperiors: organizationalUnit, organization, container,computer
+schemaIdGuid:5fd4250b-1262-11d0-a060-00aa006c33ed
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: TRUE
+defaultObjectCategory: CN=Application-Process,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Link-Track-Volume-Table
+ldapDisplayName: linkTrackVolumeTable
+governsId: 1.2.840.113556.1.5.90
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: fileLinkTracking
+systemPossSuperiors: fileLinkTracking
+schemaIdGuid:ddac0cf4-af8f-11d0-afeb-00c04fd930c9
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Link-Track-Volume-Table,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Locality
+ldapDisplayName: locality
+governsId: 2.5.6.3
+objectClassCategory: 1
+rdnAttId: l
+subClassOf: top
+systemMustContain: l
+systemMayContain: street, st, seeAlso, searchGuide
+systemPossSuperiors: domainDNS, country, organizationalUnit,organization, locality
+schemaIdGuid:bf967aa0-0de6-11d0-a285-00aa003049e2
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: FALSE
+systemOnly: FALSE
+defaultObjectCategory: CN=Locality,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Lost-And-Found
+ldapDisplayName: lostAndFound
+governsId: 1.2.840.113556.1.5.139
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMayContain: moveTreeState
+systemPossSuperiors: configuration, domainDNS, dMD
+schemaIdGuid:52ab8671-5709-11d1-a9c6-0000f80367c1
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Lost-And-Found,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Mail-Recipient
+ldapDisplayName: mailRecipient
+governsId: 1.2.840.113556.1.3.46
+objectClassCategory: 3
+rdnAttId: cn
+subClassOf: top
+systemMustContain: cn
+mayContain: msDS-PhoneticDisplayName, userSMIMECertificate,secretary, msExchLabeledURI, msExchAssistantName, labeledURI
+systemMayContain: userCertificate, userCert, textEncodedORAddress,telephoneNumber, showInAddressBook, legacyExchangeDN,garbageCollPeriod, info
+systemPossSuperiors: container
+schemaIdGuid:bf967aa1-0de6-11d0-a285-00aa003049e2
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Mail-Recipient,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Meeting
+ldapDisplayName: meeting
+governsId: 1.2.840.113556.1.5.104
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMustContain: meetingName
+systemMayContain: meetingURL, meetingType, meetingStartTime,meetingScope, meetingRecurrence, meetingRating, meetingProtocol,meetingOwner, meetingOriginator, meetingMaxParticipants,meetingLocation, meetingLanguage, meetingKeyword,meetingIsEncrypted, meetingIP, meetingID, meetingEndTime,meetingDescription, meetingContactInfo, meetingBlob,meetingBandwidth, meetingApplication, meetingAdvertiseScope
+systemPossSuperiors: container
+schemaIdGuid:11b6cc94-48c4-11d1-a9c3-0000f80367c1
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Meeting,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-SQL-OLAPCube
+ldapDisplayName: mS-SQL-OLAPCube
+governsId: 1.2.840.113556.1.5.190
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMayContain: mS-SQL-Keywords, mS-SQL-PublicationURL,mS-SQL-InformationURL, mS-SQL-Status, mS-SQL-LastUpdatedDate,mS-SQL-Size, mS-SQL-Description, mS-SQL-Contact, mS-SQL-Name
+systemPossSuperiors: mS-SQL-OLAPDatabase
+schemaIdGuid:09f0506a-cd28-11d2-9993-0000f87a57d4
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=MS-SQL-OLAPCube,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-SQL-OLAPDatabase
+ldapDisplayName: mS-SQL-OLAPDatabase
+governsId: 1.2.840.113556.1.5.189
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMayContain: mS-SQL-Keywords, mS-SQL-PublicationURL,mS-SQL-ConnectionURL, mS-SQL-InformationURL, mS-SQL-Status,mS-SQL-Applications, mS-SQL-LastBackupDate, mS-SQL-LastUpdatedDate,mS-SQL-Size, mS-SQL-Type, mS-SQL-Description, mS-SQL-Contact,mS-SQL-Name
+systemPossSuperiors: mS-SQL-OLAPServer
+schemaIdGuid:20af031a-ccef-11d2-9993-0000f87a57d4
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=MS-SQL-OLAPDatabase,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-SQL-OLAPServer
+ldapDisplayName: mS-SQL-OLAPServer
+governsId: 1.2.840.113556.1.5.185
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: serviceConnectionPoint
+systemMayContain: mS-SQL-Keywords, mS-SQL-PublicationURL,mS-SQL-InformationURL, mS-SQL-Status, mS-SQL-Language,mS-SQL-ServiceAccount, mS-SQL-Contact, mS-SQL-RegisteredOwner,mS-SQL-Build, mS-SQL-Version, mS-SQL-Name
+systemPossSuperiors: serviceConnectionPoint
+schemaIdGuid:0c7e18ea-ccef-11d2-9993-0000f87a57d4
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=MS-SQL-OLAPServer,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-SQL-SQLDatabase
+ldapDisplayName: mS-SQL-SQLDatabase
+governsId: 1.2.840.113556.1.5.188
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMayContain: mS-SQL-Keywords, mS-SQL-InformationURL,mS-SQL-Status, mS-SQL-Applications, mS-SQL-LastDiagnosticDate,mS-SQL-LastBackupDate, mS-SQL-CreationDate, mS-SQL-Size,mS-SQL-Contact, mS-SQL-Alias, mS-SQL-Description, mS-SQL-Name
+systemPossSuperiors: mS-SQL-SQLServer
+schemaIdGuid:1d08694a-ccef-11d2-9993-0000f87a57d4
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=MS-SQL-SQLDatabase,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-SQL-SQLPublication
+ldapDisplayName: mS-SQL-SQLPublication
+governsId: 1.2.840.113556.1.5.187
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMayContain: mS-SQL-ThirdParty,mS-SQL-AllowSnapshotFilesFTPDownloading,mS-SQL-AllowQueuedUpdatingSubscription,mS-SQL-AllowImmediateUpdatingSubscription,mS-SQL-AllowKnownPullSubscription, mS-SQL-Publisher,mS-SQL-AllowAnonymousSubscription, mS-SQL-Database, mS-SQL-Type,mS-SQL-Status, mS-SQL-Description, mS-SQL-Name
+systemPossSuperiors: mS-SQL-SQLServer
+schemaIdGuid:17c2f64e-ccef-11d2-9993-0000f87a57d4
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=MS-SQL-SQLPublication,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: Application-Settings
+ldapDisplayName: applicationSettings
+governsId: 1.2.840.113556.1.5.7000.49
+objectClassCategory: 2
+rdnAttId: cn
+subClassOf: top
+systemMayContain: notificationList, msDS-Settings, applicationName
+systemPossSuperiors: server
+schemaIdGuid:f780acc1-56f0-11d1-a9c6-0000f80367c1
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=Application-Settings,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-SQL-SQLRepository
+ldapDisplayName: mS-SQL-SQLRepository
+governsId: 1.2.840.113556.1.5.186
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMayContain: mS-SQL-InformationDirectory, mS-SQL-Version,mS-SQL-Description, mS-SQL-Status, mS-SQL-Build, mS-SQL-Contact,mS-SQL-Name
+systemPossSuperiors: mS-SQL-SQLServer
+schemaIdGuid:11d43c5c-ccef-11d2-9993-0000f87a57d4
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=MS-SQL-SQLRepository,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: MS-SQL-SQLServer
+ldapDisplayName: mS-SQL-SQLServer
+governsId: 1.2.840.113556.1.5.184
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: serviceConnectionPoint
+systemMayContain: mS-SQL-Keywords, mS-SQL-GPSHeight,mS-SQL-GPSLongitude, mS-SQL-GPSLatitude, mS-SQL-InformationURL,mS-SQL-LastUpdatedDate, mS-SQL-Status, mS-SQL-Vines,mS-SQL-AppleTalk, mS-SQL-TCPIP, mS-SQL-SPX, mS-SQL-MultiProtocol,mS-SQL-NamedPipe, mS-SQL-Clustered, mS-SQL-UnicodeSortOrder,mS-SQL-SortOrder, mS-SQL-CharacterSet, mS-SQL-ServiceAccount,mS-SQL-Build, mS-SQL-Memory, mS-SQL-Location, mS-SQL-Contact,mS-SQL-RegisteredOwner, mS-SQL-Name
+systemPossSuperiors: serviceConnectionPoint
+schemaIdGuid:05f6c878-ccef-11d2-9993-0000f87a57d4
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=MS-SQL-SQLServer,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-COM-Partition
+ldapDisplayName: msCOM-Partition
+governsId: 1.2.840.113556.1.5.193
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMayContain: msCOM-ObjectId
+systemPossSuperiors: domainDNS, organizationalUnit, container
+schemaIdGuid:c9010e74-4e58-49f7-8a89-5e3e2340fcf8
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=ms-COM-Partition,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-COM-PartitionSet
+ldapDisplayName: msCOM-PartitionSet
+governsId: 1.2.840.113556.1.5.194
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+systemMayContain: msCOM-PartitionLink, msCOM-DefaultPartitionLink,msCOM-ObjectId
+systemPossSuperiors: domainDNS, organizationalUnit, container
+schemaIdGuid:250464ab-c417-497a-975a-9e0d459a7ca1
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=ms-COM-PartitionSet,CN=Schema,CN=Configuration,<RootDomainDN>
+systemFlags: FLAG_SCHEMA_BASE_OBJECT
+
+cn: ms-DFSR-Connection
+ldapDisplayName: msDFSR-Connection
+governsId: 1.2.840.113556.1.6.13.4.10
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+mustContain: fromServer
+mayContain: msDFSR-Options2, msDFSR-DisablePacketPrivacy,msDFSR-Priority, msDFSR-Enabled, msDFSR-RdcEnabled,msDFSR-RdcMinFileSizeInKb, msDFSR-Keywords, msDFSR-Schedule,msDFSR-Flags, msDFSR-Options, msDFSR-Extension
+possSuperiors: msDFSR-Member
+schemaIdGuid:e58f972e-64b5-46ef-8d8b-bbc3e1897eab
+defaultSecurityDescriptor: D:(A;;RPLCLORC;;;AU)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;DA)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;CO)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;SY)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=ms-DFSR-Connection,CN=Schema,CN=Configuration,<RootDomainDN>
+
+cn: ms-DFSR-Content
+ldapDisplayName: msDFSR-Content
+governsId: 1.2.840.113556.1.6.13.4.6
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+mayContain: msDFSR-Flags, msDFSR-Options, msDFSR-Extension
+possSuperiors: msDFSR-ReplicationGroup
+schemaIdGuid:64759b35-d3a1-42e4-b5f1-a3de162109b3
+defaultSecurityDescriptor: D:(A;;RPLCLORC;;;AU)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;DA)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;CO)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;SY)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=ms-DFSR-Content,CN=Schema,CN=Configuration,<RootDomainDN>
+
+cn: ms-DFSR-ContentSet
+ldapDisplayName: msDFSR-ContentSet
+governsId: 1.2.840.113556.1.6.13.4.7
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+mayContain: msDFSR-Options2, msDFSR-OnDemandExclusionDirectoryFilter,msDFSR-OnDemandExclusionFileFilter,msDFSR-DefaultCompressionExclusionFilter, msDFSR-DeletedSizeInMb,msDFSR-Priority, msDFSR-ConflictSizeInMb, msDFSR-StagingSizeInMb,msDFSR-RootSizeInMb, description, msDFSR-DfsPath, msDFSR-FileFilter,msDFSR-DirectoryFilter, msDFSR-Flags, msDFSR-Options,msDFSR-Extension
+possSuperiors: msDFSR-Content
+schemaIdGuid:4937f40d-a6dc-4d48-97ca-06e5fbfd3f16
+defaultSecurityDescriptor: D:(A;;RPLCLORC;;;AU)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;DA)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;CO)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;SY)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=ms-DFSR-ContentSet,CN=Schema,CN=Configuration,<RootDomainDN>
+
+cn: ms-DFSR-GlobalSettings
+ldapDisplayName: msDFSR-GlobalSettings
+governsId: 1.2.840.113556.1.6.13.4.4
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+mayContain: msDFSR-Flags, msDFSR-Options, msDFSR-Extension
+possSuperiors: container
+schemaIdGuid:7b35dbad-b3ec-486a-aad4-2fec9d6ea6f6
+defaultSecurityDescriptor: D:(A;;RPLCLORC;;;AU)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;DA)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;CO)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;SY)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=ms-DFSR-GlobalSettings,CN=Schema,CN=Configuration,<RootDomainDN>
+
+cn: ms-DFSR-LocalSettings
+ldapDisplayName: msDFSR-LocalSettings
+governsId: 1.2.840.113556.1.6.13.4.1
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+mayContain: msDFSR-Version, msDFSR-Flags, msDFSR-Options,msDFSR-Extension
+possSuperiors: computer
+schemaIdGuid:fa85c591-197f-477e-83bd-ea5a43df2239
+defaultSecurityDescriptor: D:(A;;RPLCLORC;;;AU)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;DA)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;CO)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;SY)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=ms-DFSR-LocalSettings,CN=Schema,CN=Configuration,<RootDomainDN>
+
+cn: ms-DFSR-Member
+ldapDisplayName: msDFSR-Member
+governsId: 1.2.840.113556.1.6.13.4.9
+objectClassCategory: 1
+rdnAttId: cn
+subClassOf: top
+mustContain: msDFSR-ComputerReference
+mayContain: serverReference, msDFSR-Keywords, msDFSR-Flags,msDFSR-Options, msDFSR-Extension
+possSuperiors: msDFSR-Topology
+schemaIdGuid:4229c897-c211-437c-a5ae-dbf705b696e5
+defaultSecurityDescriptor: D:(A;;RPLCLORC;;;AU)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;DA)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;CO)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;SY)
+defaultHidingValue: TRUE
+systemOnly: FALSE
+defaultObjectCategory: CN=ms-DFSR-Member,CN=Schema,CN=Configuration,<RootDomainDN>
+
diff --git a/source4/setup/ad-schema/licence.txt b/source4/setup/ad-schema/licence.txt
new file mode 100644
index 0000000000..9db966461b
--- /dev/null
+++ b/source4/setup/ad-schema/licence.txt
@@ -0,0 +1,54 @@
+NOTE:
+
+The licence on these schema files is not GPL, or a standard Open
+Source licence. Be careful to redistribute thes files as part of
+Samba or 'your implementation', but not alone.
+
+--
+
+Intellectual Property Rights Notice for Protocol Documentation
+
+Copyrights.
+This protocol documentation is covered by Microsoft
+copyrights. Regardless of any other terms that are contained in the
+terms of use for the Microsoft website that hosts this documentation,
+you may make copies of it in order to develop implementations of the
+protocols, and may distribute portions of it in your implementations
+of the protocols or your documentation as necessary to properly
+document the implementation. You may also distribute in your
+implementation, with or without modification, any schema, IDL's, or
+code samples that are included in the documentation. This permission
+also applies to any documents that are referenced in the protocol
+documentation.
+
+No Trade Secrets.
+Microsoft does not claim any trade secret rights in this documentation.
+
+Patents.
+Microsoft has patents that may cover your implementations of the
+protocols. Neither this notice nor Microsoft's delivery of the
+documentation grants any licenses under those or any other Microsoft
+patents. However, the protocols may be covered by MicrosoftÂ’s Open
+Specification Promise (available here:
+http://www.microsoft.com/interop/osp). If you would prefer a written
+license, or if the protocols are not covered by the OSP, patent
+licenses are available by contacting protocol@microsoft.com.
+
+Trademarks.
+The names of companies and products contained in this documentation
+may be covered by trademarks or similar intellectual property
+rights. This notice does not grant any licenses under those
+rights.Reservation of Rights. All other rights are reserved, and this
+notice does not grant any rights other than specifically described
+above, whether by implication, estoppel, or otherwise.
+
+Tools.
+This protocol documentation is intended for use in conjunction with
+publicly available standard specifications and network programming
+art, and assumes that the reader either is familiar with the
+aforementioned material or has immediate access to it. A protocol
+specification does not require the use of Microsoft programming tools
+or programming environments in order for you to develop an
+implementation. If you have access to Microsoft programming tools and
+environments you are free to take advantage of them.
+
diff --git a/source4/setup/aggregate_schema.ldif b/source4/setup/aggregate_schema.ldif
new file mode 100644
index 0000000000..2726704719
--- /dev/null
+++ b/source4/setup/aggregate_schema.ldif
@@ -0,0 +1,3 @@
+dn: CN=Aggregate,${SCHEMADN}
+objectClass: top
+objectClass: subSchema
diff --git a/source4/setup/cn=replicator.ldif b/source4/setup/cn=replicator.ldif
new file mode 100644
index 0000000000..6001456b4d
--- /dev/null
+++ b/source4/setup/cn=replicator.ldif
@@ -0,0 +1,12 @@
+dn: cn=replicator
+objectClass: top
+objectClass: person
+cn: replicator
+userPassword:: ${MMR_PASSWORD_B64}
+structuralObjectClass: person
+entryUUID: ${UUID}
+creatorsName:
+createTimestamp: ${LDAPTIME}
+entryCSN: 20080714010529.241039Z#000000#000#000000
+modifiersName:
+modifyTimestamp: ${LDAPTIME}
diff --git a/source4/setup/cn=samba-admin.ldif b/source4/setup/cn=samba-admin.ldif
new file mode 100644
index 0000000000..c59ffd9ab6
--- /dev/null
+++ b/source4/setup/cn=samba-admin.ldif
@@ -0,0 +1,12 @@
+dn: cn=samba-admin
+objectClass: top
+objectClass: person
+cn: samba-admin
+userPassword:: ${LDAPADMINPASS_B64}
+structuralObjectClass: person
+entryUUID: ${UUID}
+creatorsName:
+createTimestamp: ${LDAPTIME}
+entryCSN: 20080714010529.241038Z#000000#000#000000
+modifiersName:
+modifyTimestamp: ${LDAPTIME}
diff --git a/source4/setup/cn=samba.ldif b/source4/setup/cn=samba.ldif
new file mode 100644
index 0000000000..3be6242fe3
--- /dev/null
+++ b/source4/setup/cn=samba.ldif
@@ -0,0 +1,11 @@
+dn: cn=Samba
+objectClass: top
+objectClass: container
+cn: Samba
+structuralObjectClass: container
+entryUUID: b1d4823a-e58c-102c-9f74-51b6d59a1b68
+creatorsName:
+createTimestamp: 20080714010529Z
+entryCSN: 20080714010529.194412Z#000000#000#000000
+modifiersName:
+modifyTimestamp: 20080714010529Z
diff --git a/source4/setup/display_specifiers.ldif b/source4/setup/display_specifiers.ldif
new file mode 100644
index 0000000000..7d6633244d
--- /dev/null
+++ b/source4/setup/display_specifiers.ldif
@@ -0,0 +1,111 @@
+dn: CN=DisplaySpecifiers,${CONFIGDN}
+objectClass: top
+objectClass: container
+
+dn: CN=409,CN=DisplaySpecifiers,${CONFIGDN}
+objectClass: top
+objectClass: container
+cn: 409
+
+dn: CN=user-Display,CN=409,CN=DisplaySpecifiers,${CONFIGDN}
+objectClass: top
+objectClass: displaySpecifier
+cn: user-Display
+contextMenu: 0,{62AE1F9A-126A-11D0-A14B-0800361B1103}
+adminPropertyPages: 9,{FA3E1D55-16DF-446d-872E-BD04D4F39C93}
+adminPropertyPages: 8,{0910dd01-df8c-11d1-ae27-00c04fa35813}
+adminPropertyPages: 7,{8c5b1b50-d46e-11d1-8091-00a024c48131}
+adminPropertyPages: 6,{4E40F770-369C-11d0-8922-00A024AB2DBB}
+adminPropertyPages: 5,{6dfe6488-a212-11d0-bcd5-00c04fd8d5b6}
+adminPropertyPages: 4,{FD57D295-4FD9-11D1-854E-00C04FC31FD3}
+adminPropertyPages: 3,{B52C1E50-1DD2-11D1-BC43-00C04FC31FD3}
+adminPropertyPages: 1,{6dfe6485-a212-11d0-bcd5-00c04fd8d5b6}
+shellPropertyPages: 2,{dde2c5e9-c8ae-11d0-bcdb-00c04fd8d5b6}
+shellPropertyPages: 1,{f5d121ed-c8ac-11d0-bcdb-00c04fd8d5b6}
+adminContextMenu: 1,{08eb4fa6-6ffd-11d1-b0e0-00c04fd8dca6}
+adminMultiselectPropertyPages: 1,{50d30564-9911-11d1-b9af-00c04fd8d5b0}
+
+dn: CN=group-Display,CN=409,CN=DisplaySpecifiers,${CONFIGDN}
+objectClass: top
+objectClass: displaySpecifier
+cn: group-Display
+contextMenu: 0,{62AE1F9A-126A-11D0-A14B-0800361B1103}
+adminPropertyPages: 4,{4E40F770-369C-11d0-8922-00A024AB2DBB}
+adminPropertyPages: 3,{6dfe6488-a212-11d0-bcd5-00c04fd8d5b6}
+adminPropertyPages: 2,{6dfe648b-a212-11d0-bcd5-00c04fd8d5b6}
+adminPropertyPages: 1,{6dfe6489-a212-11d0-bcd5-00c04fd8d5b6}
+shellPropertyPages: 2,{dde2c5e9-c8ae-11d0-bcdb-00c04fd8d5b6}
+shellPropertyPages: 1,{f5d121ee-c8ac-11d0-bcdb-00c04fd8d5b6}
+adminContextMenu: 1,{08eb4fa6-6ffd-11d1-b0e0-00c04fd8dca6}
+
+dn: CN=domainDNS-Display,CN=409,CN=DisplaySpecifiers,${CONFIGDN}
+objectClass: top
+objectClass: displaySpecifier
+cn: domainDNS-Display
+name: domainDNS-Display
+adminPropertyPages: 5,{4E40F770-369C-11d0-8922-00A024AB2DBB}
+adminPropertyPages: 4,{6dfe6488-a212-11d0-bcd5-00c04fd8d5b6}
+adminPropertyPages: 3,{6dfe648b-a212-11d0-bcd5-00c04fd8d5b6}
+adminPropertyPages: 1,{6dfe648c-a212-11d0-bcd5-00c04fd8d5b6}
+shellPropertyPages: 2,{dde2c5e9-c8ae-11d0-bcdb-00c04fd8d5b6}
+shellPropertyPages: 1,{f5d121ef-c8ac-11d0-bcdb-00c04fd8d5b6}
+adminContextMenu: 2,{08eb4fa6-6ffd-11d1-b0e0-00c04fd8dca6}
+adminContextMenu: 1,{6BA3F852-23C6-11D1-B91F-00A0C9A06D2D}
+
+dn: CN=computer-Display,CN=409,CN=DisplaySpecifiers,${CONFIGDN}
+objectClass: top
+objectClass: displaySpecifier
+cn: computer-Display
+contextMenu: 0,{62AE1F9A-126A-11D0-A14B-0800361B1103}
+adminPropertyPages: 10,{0F65B1BF-740F-11d1-BBE6-0060081692B3}
+adminPropertyPages: 7,{B52C1E50-1DD2-11D1-BC43-00C04FC31FD3}
+adminPropertyPages: 6,{4E40F770-369C-11d0-8922-00A024AB2DBB}
+adminPropertyPages: 5,{6dfe6488-a212-11d0-bcd5-00c04fd8d5b6}
+adminPropertyPages: 4,{6dfe648b-a212-11d0-bcd5-00c04fd8d5b6}
+adminPropertyPages: 3,{77597368-7b15-11d0-a0c2-080036af3f03}
+adminPropertyPages: 1,{6dfe6492-a212-11d0-bcd5-00c04fd8d5b6}
+shellPropertyPages: 2,{dde2c5e9-c8ae-11d0-bcdb-00c04fd8d5b6}
+shellPropertyPages: 1,{f5d121f4-c8ac-11d0-bcdb-00c04fd8d5b6}
+adminContextMenu: 1,{08eb4fa6-6ffd-11d1-b0e0-00c04fd8dca6}
+createWizardExt: 1,{D6D8C25A-4E83-11d2-8424-00C04FA372D4}
+
+dn: CN=organizationalUnit-Display,CN=409,CN=DisplaySpecifiers,${CONFIGDN}
+objectClass: top
+objectClass: displaySpecifier
+cn: organizationalUnit-Display
+contextMenu: 0,{62AE1F9A-126A-11D0-A14B-0800361B1103}
+adminPropertyPages: 6,{FA3E1D55-16DF-446d-872E-BD04D4F39C93}
+adminPropertyPages: 5,{4E40F770-369C-11d0-8922-00A024AB2DBB}
+adminPropertyPages: 4,{6dfe6488-a212-11d0-bcd5-00c04fd8d5b6}
+adminPropertyPages: 3,{6dfe648b-a212-11d0-bcd5-00c04fd8d5b6}
+adminPropertyPages: 1,{9da6fd63-c63b-11d0-b94d-00c04fd8d5b0}
+shellPropertyPages: 2,{dde2c5e9-c8ae-11d0-bcdb-00c04fd8d5b6}
+shellPropertyPages: 1,{f2c3faae-c8ac-11d0-bcdb-00c04fd8d5b6}
+adminContextMenu: 2,{08eb4fa6-6ffd-11d1-b0e0-00c04fd8dca6}
+adminContextMenu: 1,{6BA3F852-23C6-11D1-B91F-00A0C9A06D2D}
+
+dn: CN=container-Display,CN=409,CN=DisplaySpecifiers,${CONFIGDN}
+objectClass: top
+objectClass: displaySpecifier
+cn: container-Display
+contextMenu: 0,{62AE1F9A-126A-11D0-A14B-0800361B1103}
+adminPropertyPages: 3,{4E40F770-369C-11d0-8922-00A024AB2DBB}
+adminPropertyPages: 2,{6dfe6488-a212-11d0-bcd5-00c04fd8d5b6}
+adminPropertyPages: 1,{5a96f2d8-736e-11d1-bd0d-00c04fd8d5b6}
+shellPropertyPages: 1,{f2c3faae-c8ac-11d0-bcdb-00c04fd8d5b6}
+adminContextMenu: 4,{AB790AA1-CDC1-478a-9351-B2E05CFCAD09}
+adminContextMenu: 3,{EEBD2F15-87EE-4F93-856F-6AD7E31787B3}
+adminContextMenu: 2,{08eb4fa6-6ffd-11d1-b0e0-00c04fd8dca6}
+adminContextMenu: 1,{6BA3F852-23C6-11D1-B91F-00A0C9A06D2D}
+
+dn: CN=default-Display,CN=409,CN=DisplaySpecifiers,${CONFIGDN}
+objectClass: top
+objectClass: displaySpecifier
+cn: default-Display
+adminPropertyPages: 3,{4E40F770-369C-11d0-8922-00A024AB2DBB}
+adminPropertyPages: 2,{6dfe6488-a212-11d0-bcd5-00c04fd8d5b6}
+adminPropertyPages: 1,{6384e23e-736d-11d1-bd0d-00c04fd8d5b6}
+shellPropertyPages: 1,{f2c3faae-c8ac-11d0-bcdb-00c04fd8d5b6}
+adminContextMenu: 0,{08eb4fa6-6ffd-11d1-b0e0-00c04fd8dca6}
+adminMultiselectPropertyPages: 1,{50d30563-9911-11d1-b9af-00c04fd8d5b0}
+
diff --git a/source4/setup/enableaccount b/source4/setup/enableaccount
new file mode 100755
index 0000000000..061997b804
--- /dev/null
+++ b/source4/setup/enableaccount
@@ -0,0 +1,77 @@
+#!/usr/bin/python
+#
+# add a new user to a Samba4 server
+# Copyright Andrew Tridgell 2005
+# Copyright Jelmer Vernooij 2008
+# Released under the GNU GPL version 3 or later
+#
+import os, sys
+
+sys.path.insert(0, os.path.join(os.path.dirname(sys.argv[0]), "../bin/python"))
+
+import samba.getopt as options
+import optparse
+import pwd
+import ldb
+
+from samba.auth import system_session
+from samba.samdb import SamDB
+
+parser = optparse.OptionParser("enableaccount [username] [options]")
+sambaopts = options.SambaOptions(parser)
+parser.add_option_group(sambaopts)
+parser.add_option_group(options.VersionOptions(parser))
+credopts = options.CredentialsOptions(parser)
+parser.add_option_group(credopts)
+parser.add_option("-H", help="LDB URL for database or target server", type=str)
+parser.add_option("--base", help="Base DN to search for user under", type=str)
+
+opts, args = parser.parse_args()
+
+#
+# print a message if quiet is not set
+#
+def message(text):
+ if not opts.quiet:
+ print text
+
+if len(args) == 0:
+ parser.print_usage()
+ sys.exit(1)
+
+username = args[0]
+
+if username is None:
+ print "username must be specified"
+
+lp = sambaopts.get_loadparm()
+
+creds = credopts.get_credentials(lp)
+
+if opts.H is not None:
+ url = opts.H
+else:
+ url = lp.get("sam database")
+
+samdb = SamDB(url=url, session_info=system_session(),
+ credentials=creds, lp=lp)
+
+domain_dn = opts.base
+if opts.base is None:
+ res = samdb.search("", scope=ldb.SCOPE_BASE,
+ expression="(defaultNamingContext=*)",
+ attrs=["defaultNamingContext"])
+ assert(len(res) == 1 and res[0]["defaultNamingContext"] is not None)
+ domain_dn = res[0]["defaultNamingContext"][0]
+else:
+ domain_dn = opts.base
+
+filter = "(&(objectClass=user)(samAccountName=%s))" % username
+
+res = samdb.search(domain_dn, scope=ldb.SCOPE_SUBTREE,
+ expression=filter,
+ attrs=[])
+assert(len(res) == 1)
+user_dn = res[0].dn
+
+samdb.enable_account(user_dn)
diff --git a/source4/setup/fedora-ds-init.ldif b/source4/setup/fedora-ds-init.ldif
new file mode 100644
index 0000000000..83cdb6b392
--- /dev/null
+++ b/source4/setup/fedora-ds-init.ldif
@@ -0,0 +1,27 @@
+# These entries need to be added to get the container for the
+# provision to be aimed at.
+
+dn: cn="dc=tammy,dc=abartlet,dc=net",cn=mapping tree,cn=config
+objectclass: top
+objectclass: extensibleObject
+objectclass: nsMappingTree
+nsslapd-state: backend
+nsslapd-backend: UserData
+cn: dc=tammy,dc=abartlet,dc=net
+
+dn: cn=UserData,cn=ldbm database,cn=plugins,cn=config
+objectclass: extensibleObject
+objectclass: nsBackendInstance
+nsslapd-suffix: dc=tammy,dc=abartlet,dc=net
+
+# Generate 99_ad.ldif with
+
+# bin/ad2oLschema -I setup/fedora-ds-init.ldif --option=convert:target=fedora-ds -O /opt/fedora-ds/slapd-piglett/config/schema/99_ad.ldif -H /data/samba/samba4/prefix/private/sam.ldb
+# Then install 00_staish_core.ldif 30ns-common.ldif and 99_ad.ldif
+# into /opt/fedora-ds/slapd-piglett/config/schema/
+#
+
+
+# provision with --ldap-backend=ldap://localhost:4389 --ldap-module=nsuniqueid --aci='aci: (targetattr = "*") (version 3.0;acl "full access to all by all";allow (all)(userdn = "ldap:///anyone");)'
+
+
diff --git a/source4/setup/fedorads-partitions.ldif b/source4/setup/fedorads-partitions.ldif
new file mode 100644
index 0000000000..571fb599b9
--- /dev/null
+++ b/source4/setup/fedorads-partitions.ldif
@@ -0,0 +1,30 @@
+dn: cn="${CONFIGDN}",cn=mapping tree,cn=config
+objectclass: top
+objectclass: extensibleObject
+objectclass: nsMappingTree
+nsslapd-state: backend
+nsslapd-backend: configData
+cn: ${CONFIGDN}
+
+dn: cn=configData,cn=ldbm database,cn=plugins,cn=config
+objectclass: top
+objectclass: extensibleObject
+objectclass: nsBackendInstance
+nsslapd-suffix: ${CONFIGDN}
+cn: configData
+
+dn: cn="${SCHEMADN}",cn=mapping tree,cn=config
+objectclass: top
+objectclass: extensibleObject
+objectclass: nsMappingTree
+nsslapd-state: backend
+nsslapd-backend: schemaData
+cn: ${SCHEMADN}
+
+dn: cn=schemaData,cn=ldbm database,cn=plugins,cn=config
+objectclass: top
+objectclass: extensibleObject
+objectclass: nsBackendInstance
+nsslapd-suffix: ${SCHEMADN}
+cn: schemaData
+
diff --git a/source4/setup/fedorads.inf b/source4/setup/fedorads.inf
new file mode 100644
index 0000000000..fe51d01db1
--- /dev/null
+++ b/source4/setup/fedorads.inf
@@ -0,0 +1,29 @@
+[General]
+SuiteSpotUserID = ${ROOT}
+FullMachineName= ${HOSTNAME}.${DNSDOMAIN}
+ServerRoot= ${LDAPDIR}
+
+[slapd]
+ldapifilepath=${LDAPDIR}/ldapi
+Suffix= ${DOMAINDN}
+RootDN= ${LDAPMANAGERDN}
+RootDNPwd= ${LDAPMANAGERPASS}
+ServerIdentifier= samba4
+${SERVERPORT}
+
+inst_dir= ${LDAPDIR}/slapd-samba4
+config_dir= ${LDAPDIR}/slapd-samba4
+schema_dir= ${LDAPDIR}/slapd-samba4/schema
+lock_dir= ${LDAPDIR}/slapd-samba4/lock
+log_dir= ${LDAPDIR}/slapd-samba4/logs
+run_dir= ${LDAPDIR}/slapd-samba4/logs
+db_dir= ${LDAPDIR}/slapd-samba4/db
+bak_dir= ${LDAPDIR}/slapd-samba4/bak
+tmp_dir= ${LDAPDIR}/slapd-samba4/tmp
+ldif_dir= ${LDAPDIR}/slapd-samba4/ldif
+cert_dir= ${LDAPDIR}/slapd-samba4
+
+start_server= 0
+install_full_schema= 0
+SchemaFile=${LDAPDIR}/99_ad.ldif
+ConfigFile = ${LDAPDIR}/fedorads-partitions.ldif
diff --git a/source4/setup/idmap_init.ldif b/source4/setup/idmap_init.ldif
new file mode 100644
index 0000000000..43e5b65562
--- /dev/null
+++ b/source4/setup/idmap_init.ldif
@@ -0,0 +1,4 @@
+dn: CN=CONFIG
+cn: CONFIG
+lowerBound: 3000000
+upperBound: 4000000
diff --git a/source4/setup/krb5.conf b/source4/setup/krb5.conf
new file mode 100644
index 0000000000..7dad63de73
--- /dev/null
+++ b/source4/setup/krb5.conf
@@ -0,0 +1,17 @@
+[libdefaults]
+ default_realm = ${REALM}
+ dns_lookup_realm = false
+ dns_lookup_kdc = false
+ ticket_lifetime = 24h
+ forwardable = yes
+
+[realms]
+ ${REALM} = {
+ kdc = ${HOSTNAME}.${DNSDOMAIN}:88
+ admin_server = ${HOSTNAME}.${DNSDOMAIN}:749
+ default_domain = ${DNSDOMAIN}
+ }
+
+[domain_realm]
+ .${DNSDOMAIN} = ${REALM}
+ ${DNSDOMAIN} = ${REALM}
diff --git a/source4/setup/memberof.conf b/source4/setup/memberof.conf
new file mode 100644
index 0000000000..77e57c86d4
--- /dev/null
+++ b/source4/setup/memberof.conf
@@ -0,0 +1,9 @@
+overlay memberof
+memberof-dn cn=samba-admin,cn=samba
+memberof-dangling error
+memberof-refint TRUE
+memberof-group-oc top
+memberof-member-ad ${MEMBER_ATTR}
+memberof-memberof-ad ${MEMBEROF_ATTR}
+memberof-dangling-error 32
+
diff --git a/source4/setup/mmr_serverids.conf b/source4/setup/mmr_serverids.conf
new file mode 100644
index 0000000000..e4daf2028a
--- /dev/null
+++ b/source4/setup/mmr_serverids.conf
@@ -0,0 +1 @@
+ServerID ${SERVERID} "${LDAPSERVER}"
diff --git a/source4/setup/mmr_syncrepl.conf b/source4/setup/mmr_syncrepl.conf
new file mode 100644
index 0000000000..1373858c4e
--- /dev/null
+++ b/source4/setup/mmr_syncrepl.conf
@@ -0,0 +1,12 @@
+# Generated from template mmr_syncrepl.conf
+
+syncrepl rid=${RID}
+ provider="${LDAPSERVER}"
+ searchbase="${MMRDN}"
+ type=refreshAndPersist
+ retry="10 +"
+ bindmethod=sasl
+ saslmech=DIGEST-MD5
+ authcid="replicator"
+ credentials="${MMR_PASSWORD}"
+
diff --git a/source4/setup/modules.conf b/source4/setup/modules.conf
new file mode 100644
index 0000000000..c90dab767f
--- /dev/null
+++ b/source4/setup/modules.conf
@@ -0,0 +1 @@
+#OpenLDAP modules configuration file for ${REALM}
diff --git a/source4/setup/named.conf b/source4/setup/named.conf
new file mode 100644
index 0000000000..0b087069c7
--- /dev/null
+++ b/source4/setup/named.conf
@@ -0,0 +1,67 @@
+# This file should be included in your main BIND configuration file
+#
+# For example with
+# include "${PRIVATE_DIR}/named.conf";
+
+zone "${DNSDOMAIN}." IN {
+ type master;
+ file "${PRIVATE_DIR}/${DNSDOMAIN}.zone";
+ /*
+ * Attention: Not all BIND versions support "ms-self". The instead use
+ * of allow-update { any; }; is another, but less secure possibility.
+ */
+ update-policy {
+ /*
+ * A rather long description here, as the "ms-self" option does
+ * not appear in any docs yet (it can only be found in the
+ * source code).
+ *
+ * The short of it is that each host is allowed to update its
+ * own A and AAAA records, when the update request is properly
+ * signed by the host itself.
+ *
+ * The long description is (look at the
+ * dst_gssapi_identitymatchesrealmms() call in lib/dns/ssu.c and
+ * its definition in lib/dns/gssapictx.c for details):
+ *
+ * A GSS-TSIG update request will be signed by a given signer
+ * (e.g. machine-name$@${REALM}). The signer name is split into
+ * the machine component (e.g. "machine-name") and the realm
+ * component (e.g. "${REALM}"). The update is allowed if the
+ * following conditions are met:
+ *
+ * 1) The machine component of the signer name matches the first
+ * (host) component of the FQDN that is being updated.
+ *
+ * 2) The realm component of the signer name matches the realm
+ * in the grant statement below (${REALM}).
+ *
+ * 3) The domain component of the FQDN that is being updated
+ * matches the realm in the grant statement below.
+ *
+ * If the 3 conditions above are satisfied, the update succeeds.
+ */
+ grant ${REALM} ms-self * A AAAA;
+ };
+};
+
+# The reverse zone configuration is optional. The following example assumes a
+# subnet of 192.168.123.0/24:
+
+/*
+zone "123.168.192.in-addr.arpa" in {
+ type master;
+ file "123.168.192.in-addr.arpa.zone";
+ update-policy {
+ grant ${REALM_WC} wildcard *.123.168.192.in-addr.arpa. PTR;
+ };
+};
+*/
+
+# Note that the reverse zone file is not created during the provision process.
+
+# The most recent BIND versions (9.5.0a5 or later) support secure GSS-TSIG
+# updates. If you are running an earlier version of BIND, or if you do not wish
+# to use secure GSS-TSIG updates, you may remove the update-policy sections in
+# both examples above.
+
diff --git a/source4/setup/named.txt b/source4/setup/named.txt
new file mode 100644
index 0000000000..c1e6b3a9ee
--- /dev/null
+++ b/source4/setup/named.txt
@@ -0,0 +1,46 @@
+# Additional informations for DNS setup using BIND
+
+# If you are running a capable version of BIND and you wish to support secure
+# GSS-TSIG updates, you must make the following configuration changes:
+
+# - Insert the following lines into the options {} section of your named.conf
+# file:
+tkey-gssapi-credential "DNS/${DNSDOMAIN}";
+tkey-domain "${REALM}";
+
+# - Modify BIND init scripts to pass the location of the generated keytab file.
+# Fedora 8 & later provide a variable named KEYTAB_FILE in /etc/sysconfig/named
+# for this purpose:
+KEYTAB_FILE="${DNS_KEYTAB_ABS}"
+# Note that the Fedora scripts translate KEYTAB_FILE behind the scenes into a
+# variable named KRB5_KTNAME, which is ultimately passed to the BIND daemon. If
+# your distribution does not provide a variable like KEYTAB_FILE to pass a
+# keytab file to the BIND daemon, a workaround is to place the following line in
+# BIND's sysconfig file or in the init script for BIND:
+export KRB5_KTNAME="${DNS_KEYTAB_ABS}"
+
+# - Set appropriate ownership and permissions on the ${DNS_KEYTAB} file. Note
+# that most distributions have BIND configured to run under a non-root user
+# account. For example, Fedora 9 runs BIND as the user "named" once the daemon
+# relinquishes its rights. Therefore, the file ${DNS_KEYTAB} must be readable
+# by the user that BIND run as. If BIND is running as a non-root user, the
+# "${DNS_KEYTAB}" file must have its permissions altered to allow the daemon to
+# read it. Under Fedora 9, execute the following commands:
+chgrp named ${DNS_KEYTAB_ABS}
+chmod g+r ${DNS_KEYTAB_ABS}
+
+# - Ensure the BIND zone file(s) that will be dynamically updated are in a
+# directory where the BIND daemon can write. When BIND performs dynamic
+# updates, it not only needs to update the zone file itself but it must also
+# create a journal (.jnl) file to track the dynamic updates as they occur.
+# Under Fedora 9, the /var/named directory can not be written to by the "named"
+# user. However, the directory /var/named/dynamic directory does provide write
+# access. Therefore the zone files were placed under the /var/named/dynamic
+# directory. The file directives in both example zone statements at the
+# beginning of this file were changed by prepending the directory "dynamic/".
+
+# - If SELinux is enabled, ensure that all files have the appropriate SELinux
+# file contexts. The ${DNS_KEYTAB} file must be accessible by the BIND daemon
+# and should have a SELinux type of named_conf_t. This can be set with the
+# following command:
+chcon -t named_conf_t ${DNS_KEYTAB_ABS}
diff --git a/source4/setup/newuser b/source4/setup/newuser
new file mode 100755
index 0000000000..281e3849fa
--- /dev/null
+++ b/source4/setup/newuser
@@ -0,0 +1,60 @@
+#!/usr/bin/python
+#
+# Add a new user to a Samba4 server
+# Copyright Jelmer Vernooij 2008
+#
+# Based on the original in EJS:
+# Copyright 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 <http://www.gnu.org/licenses/>.
+import sys
+
+# Find right directory when running from source tree
+sys.path.insert(0, "bin/python")
+
+import samba.getopt as options
+import optparse
+from getpass import getpass
+from samba.auth import system_session
+
+parser = optparse.OptionParser("newuser [options] <username> [<password>]")
+sambaopts = options.SambaOptions(parser)
+parser.add_option_group(sambaopts)
+parser.add_option_group(options.VersionOptions(parser))
+credopts = options.CredentialsOptions(parser)
+parser.add_option_group(credopts)
+parser.add_option("--quiet", help="Be quiet", action="store_true")
+parser.add_option("--unixname", help="Unix Username", type=str)
+
+opts, args = parser.parse_args()
+
+if len(args) == 0:
+ parser.print_usage()
+ sys.exit(1)
+
+username = args[0]
+if len(args) > 1:
+ password = args[1]
+else:
+ password = getpass("New Password: ")
+
+if opts.unixname is None:
+ opts.unixname = username
+
+lp = sambaopts.get_loadparm()
+creds = credopts.get_credentials(lp)
+
+samdb = sambaopts.get_hostconfig().get_samdb(session_info=system_session(),
+ credentials=creds)
+samdb.newuser(username, opts.unixname, password)
diff --git a/source4/setup/olc_acl.conf b/source4/setup/olc_acl.conf
new file mode 100644
index 0000000000..c248b30fb5
--- /dev/null
+++ b/source4/setup/olc_acl.conf
@@ -0,0 +1,4 @@
+access to dn.sub="cn=config"
+ by dn="cn=samba-admin,cn=samba" write
+ by dn="cn=replicator,cn=samba" read
+
diff --git a/source4/setup/olc_mmr.conf b/source4/setup/olc_mmr.conf
new file mode 100644
index 0000000000..2f60df1421
--- /dev/null
+++ b/source4/setup/olc_mmr.conf
@@ -0,0 +1,3 @@
+overlay syncprov
+MirrorMode on
+
diff --git a/source4/setup/olc_pass.conf b/source4/setup/olc_pass.conf
new file mode 100644
index 0000000000..4c66c1c43f
--- /dev/null
+++ b/source4/setup/olc_pass.conf
@@ -0,0 +1,3 @@
+database config
+rootdn cn=config
+
diff --git a/source4/setup/olc_seed.ldif b/source4/setup/olc_seed.ldif
new file mode 100644
index 0000000000..afc3abe5a0
--- /dev/null
+++ b/source4/setup/olc_seed.ldif
@@ -0,0 +1,16 @@
+dn: cn=config
+objectClass: olcGlobal
+cn: config
+${OLC_SERVER_ID_CONF}
+
+dn: olcDatabase={0}config,cn=config
+objectClass: olcDatabaseConfig
+olcDatabase: {0}config
+olcRootDN: cn=config
+olcRootPW: ${OLC_PW}
+${OLC_SYNCREPL_CONF}olcMirrorMode: TRUE
+
+dn: olcOverlay=syncprov,olcDatabase={0}config,cn=config
+objectClass: olcSyncProvConfig
+olcOverlay: syncprov
+
diff --git a/source4/setup/olc_serverid.conf b/source4/setup/olc_serverid.conf
new file mode 100644
index 0000000000..3d28acbfb4
--- /dev/null
+++ b/source4/setup/olc_serverid.conf
@@ -0,0 +1 @@
+olcServerID: ${SERVERID} "${LDAPSERVER}"
diff --git a/source4/setup/olc_syncrepl.conf b/source4/setup/olc_syncrepl.conf
new file mode 100644
index 0000000000..fd7a58d03b
--- /dev/null
+++ b/source4/setup/olc_syncrepl.conf
@@ -0,0 +1,13 @@
+# Generated from template olc_syncrepl.conf
+
+syncrepl rid=${RID}
+ provider="${LDAPSERVER}"
+ searchbase="cn=config"
+ filter="(!(olcDatabase={0}config))"
+ type=refreshAndPersist
+ retry="10 +"
+ bindmethod=sasl
+ saslmech=DIGEST-MD5
+ authcid="replicator"
+ credentials="${MMR_PASSWORD}"
+
diff --git a/source4/setup/olc_syncrepl_seed.conf b/source4/setup/olc_syncrepl_seed.conf
new file mode 100644
index 0000000000..1833fb9228
--- /dev/null
+++ b/source4/setup/olc_syncrepl_seed.conf
@@ -0,0 +1,5 @@
+olcSyncRepl: rid=${RID} provider="${LDAPSERVER}"
+ binddn="cn=config" bindmethod=sasl saslmech=DIGEST-MD5
+ authcid="replicator" credentials="linux"
+ searchbase="cn=config" filter="(!(olcDatabase={0}config))"
+ type=refreshAndPersist retry="10 +"
diff --git a/source4/setup/phpldapadmin-config.php b/source4/setup/phpldapadmin-config.php
new file mode 100644
index 0000000000..5a4c2d7a6b
--- /dev/null
+++ b/source4/setup/phpldapadmin-config.php
@@ -0,0 +1,28 @@
+<?php
+/**
+ * The phpLDAPadmin config file, customised for use with Samba4
+ * This overrides phpLDAPadmin defaults
+ * that are defined in config_default.php.
+ *
+ * DONT change config_default.php, you changes will be lost by the next release
+ * of PLA. Instead change this file - as it will NOT be replaced by a new
+ * version of phpLDAPadmin.
+ */
+
+/*********************************************/
+/* Useful important configuration overrides */
+/*********************************************/
+
+/* phpLDAPadmin can encrypt the content of sensitive cookies if you set this
+ to a big random string. */
+
+$i=0;
+$ldapservers = new LDAPServers;
+
+/* A convenient name that will appear in the tree viewer and throughout
+ phpLDAPadmin to identify this LDAP server to users. */
+$ldapservers->SetValue($i,'server','name','Samba4 LDAP Server');
+$ldapservers->SetValue($i,'server','host','${S4_LDAPI_URI}');
+$ldapservers->SetValue($i,'server','auth_type','session');
+$ldapservers->SetValue($i,'login','attr','dn');
+?>
diff --git a/source4/setup/prefixMap.txt b/source4/setup/prefixMap.txt
new file mode 100644
index 0000000000..267098000b
--- /dev/null
+++ b/source4/setup/prefixMap.txt
@@ -0,0 +1,41 @@
+0:2.5.4
+1:2.5.6
+2:1.2.840.113556.1.2
+3:1.2.840.113556.1.3
+4:2.16.840.1.101.2.2.1
+5:2.16.840.1.101.2.2.3
+6:2.16.840.1.101.2.1.5
+7:2.16.840.1.101.2.1.4
+8:2.5.5
+9:1.2.840.113556.1.4
+10:1.2.840.113556.1.5
+19:0.9.2342.19200300.100
+20:2.16.840.1.113730.3
+21:0.9.2342.19200300.100.1
+22:2.16.840.1.113730.3.1
+23:1.2.840.113556.1.5.7000
+24:2.5.21
+25:2.5.18
+26:2.5.20
+11:1.2.840.113556.1.4.260
+12:1.2.840.113556.1.5.56
+13:1.2.840.113556.1.4.262
+14:1.2.840.113556.1.5.57
+15:1.2.840.113556.1.4.263
+16:1.2.840.113556.1.5.58
+17:1.2.840.113556.1.5.73
+18:1.2.840.113556.1.4.305
+27:1.3.6.1.4.1.1466.101.119
+28:2.16.840.1.113730.3.2
+29:1.3.6.1.4.1.250.1
+30:1.2.840.113549.1.9
+31:0.9.2342.19200300.100.4
+32:1.2.840.113556.1.6.23
+33:1.2.840.113556.1.6.18.1
+34:1.2.840.113556.1.6.18.2
+35:1.2.840.113556.1.6.13.3
+36:1.2.840.113556.1.6.13.4
+37:1.3.6.1.1.1.1
+38:1.3.6.1.1.1.2
+39:1.3.6.1.4.1.7165.4.1
+40:1.3.6.1.4.1.7165.4.2
diff --git a/source4/setup/provision b/source4/setup/provision
new file mode 100755
index 0000000000..5cb851ceb7
--- /dev/null
+++ b/source4/setup/provision
@@ -0,0 +1,192 @@
+#!/usr/bin/python
+#
+# Unix SMB/CIFS implementation.
+# provision a Samba4 server
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008
+# Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008
+#
+# Based on the original in EJS:
+# 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 <http://www.gnu.org/licenses/>.
+#
+
+import getopt
+import optparse
+import os
+import sys
+
+# Find right directory when running from source tree
+sys.path.insert(0, "bin/python")
+
+import samba
+from samba.credentials import DONT_USE_KERBEROS
+from samba.auth import system_session
+import samba.getopt as options
+from samba import param
+from samba.provision import provision, FILL_FULL, FILL_NT4SYNC, FILL_DRS, find_setup_dir
+
+# how do we make this case insensitive??
+
+parser = optparse.OptionParser("provision [options]")
+sambaopts = options.SambaOptions(parser)
+parser.add_option_group(sambaopts)
+parser.add_option_group(options.VersionOptions(parser))
+credopts = options.CredentialsOptions(parser)
+parser.add_option_group(credopts)
+parser.add_option("--interactive", help="Ask for names", action="store_true")
+parser.add_option("--setupdir", type="string", metavar="DIR",
+ help="directory with setup files")
+parser.add_option("--realm", type="string", metavar="REALM", help="set realm")
+parser.add_option("--domain", type="string", metavar="DOMAIN",
+ help="set domain")
+parser.add_option("--domain-guid", type="string", metavar="GUID",
+ help="set domainguid (otherwise random)")
+parser.add_option("--domain-sid", type="string", metavar="SID",
+ help="set domainsid (otherwise random)")
+parser.add_option("--policy-guid", type="string", metavar="GUID",
+ help="set policy guid")
+parser.add_option("--invocationid", type="string", metavar="GUID",
+ help="set invocationid (otherwise random)")
+parser.add_option("--host-name", type="string", metavar="HOSTNAME",
+ help="set hostname")
+parser.add_option("--host-ip", type="string", metavar="IPADDRESS",
+ help="set IPv4 ipaddress")
+parser.add_option("--host-ip6", type="string", metavar="IP6ADDRESS",
+ help="set IPv6 ipaddress")
+parser.add_option("--adminpass", type="string", metavar="PASSWORD",
+ help="choose admin password (otherwise random)")
+parser.add_option("--krbtgtpass", type="string", metavar="PASSWORD",
+ help="choose krbtgt password (otherwise random)")
+parser.add_option("--machinepass", type="string", metavar="PASSWORD",
+ help="choose machine password (otherwise random)")
+parser.add_option("--dnspass", type="string", metavar="PASSWORD",
+ help="choose dns password (otherwise random)")
+parser.add_option("--root", type="string", metavar="USERNAME",
+ help="choose 'root' unix username")
+parser.add_option("--nobody", type="string", metavar="USERNAME",
+ help="choose 'nobody' user")
+parser.add_option("--nogroup", type="string", metavar="GROUPNAME",
+ help="choose 'nogroup' group")
+parser.add_option("--wheel", type="string", metavar="GROUPNAME",
+ help="choose 'wheel' privileged group")
+parser.add_option("--users", type="string", metavar="GROUPNAME",
+ help="choose 'users' group")
+parser.add_option("--quiet", help="Be quiet", action="store_true")
+parser.add_option("--blank", action="store_true",
+ help="do not add users or groups, just the structure")
+parser.add_option("--ldap-backend", type="string", metavar="LDAPSERVER",
+ help="LDAP server to use for this provision")
+parser.add_option("--ldap-backend-type", type="choice", metavar="LDAP-BACKEND-TYPE",
+ help="LDB mapping module to use for the LDAP backend",
+ choices=["fedora-ds", "openldap"])
+parser.add_option("--aci", type="string", metavar="ACI",
+ help="An arbitary LDIF fragment, particularly useful to loading a backend ACI value into a target LDAP server. You must provide at least a realm and domain")
+parser.add_option("--server-role", type="choice", metavar="ROLE",
+ choices=["domain controller", "dc", "member server", "member", "standalone"],
+ help="Set server role to provision for (default standalone)")
+parser.add_option("--partitions-only",
+ help="Configure Samba's partitions, but do not modify them (ie, join a BDC)", action="store_true")
+parser.add_option("--targetdir", type="string", metavar="DIR",
+ help="Set target directory")
+
+opts = parser.parse_args()[0]
+
+def message(text):
+ """print a message if quiet is not set."""
+ if not opts.quiet:
+ print text
+
+if len(sys.argv) == 1:
+ opts.interactive = True
+
+if not opts.interactive and (opts.realm is None or opts.domain is None):
+ if opts.realm is None:
+ print >>sys.stderr, "No realm set"
+ if opts.domain is None:
+ print >>sys.stderr, "No domain set"
+ parser.print_usage()
+ sys.exit(1)
+
+if opts.interactive:
+ from getpass import getpass
+ import readline
+ import socket
+ def ask(prompt, default=None):
+ if default is not None:
+ print "%s [%s]: " % (prompt,default),
+ else:
+ print "%s: " % (prompt,),
+ return sys.stdin.readline().rstrip("\n") or default
+ try:
+ opts.realm = ask("Realm", socket.getfqdn().split(".", 1)[1].upper())
+ except IndexError:
+ print >>sys.stderr, "Cannot guess realm from %s" % ( socket.getfqdn())
+ sys.exit(1)
+
+ try:
+ opts.domain = ask("Domain", opts.realm.split(".")[0])
+ except IndexError:
+ print >>sys.stderr, "Cannot guess domain from %s" % ( opts.realm())
+ sys.exit(1)
+
+ opts.server_role = ask("Server Role (dc, member, standalone)", "dc")
+ for i in range(3):
+ opts.adminpass = getpass("Administrator password: ")
+ if not opts.adminpass:
+ print >>sys.stderr, "Invalid administrator password."
+ else:
+ break
+
+lp = sambaopts.get_loadparm()
+smbconf = lp.configfile
+
+if opts.aci is not None:
+ print "set ACI: %s" % opts.aci
+
+if opts.server_role == "dc":
+ server_role = "domain controller"
+elif opts.server_role == "member":
+ server_role = "member server"
+else:
+ server_role = opts.server_role
+
+creds = credopts.get_credentials(lp)
+
+creds.set_kerberos_state(DONT_USE_KERBEROS)
+
+setup_dir = opts.setupdir
+if setup_dir is None:
+ setup_dir = find_setup_dir()
+
+samdb_fill = FILL_FULL
+if opts.blank:
+ samdb_fill = FILL_NT4SYNC
+elif opts.partitions_only:
+ samdb_fill = FILL_DRS
+
+session = system_session()
+provision(setup_dir, message,
+ session, creds, smbconf=smbconf, targetdir=opts.targetdir,
+ samdb_fill=samdb_fill, realm=opts.realm, domain=opts.domain,
+ domainguid=opts.domain_guid, domainsid=opts.domain_sid,
+ policyguid=opts.policy_guid, hostname=opts.host_name,
+ hostip=opts.host_ip, hostip6=opts.host_ip6,
+ invocationid=opts.invocationid, adminpass=opts.adminpass,
+ krbtgtpass=opts.krbtgtpass, machinepass=opts.machinepass,
+ dnspass=opts.dnspass, root=opts.root, nobody=opts.nobody,
+ nogroup=opts.nogroup, wheel=opts.wheel, users=opts.users,
+ aci=opts.aci, serverrole=server_role,
+ ldap_backend=opts.ldap_backend,
+ ldap_backend_type=opts.ldap_backend_type)
diff --git a/source4/setup/provision-backend b/source4/setup/provision-backend
new file mode 100755
index 0000000000..28e73ae302
--- /dev/null
+++ b/source4/setup/provision-backend
@@ -0,0 +1,114 @@
+#!/usr/bin/python
+#
+# Unix SMB/CIFS implementation.
+# provision a Samba4 server
+# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008
+# Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008
+# Copyright (C) Oliver Liebel <oliver@itc.li> 2008-2009
+#
+# Based on the original in EJS:
+# 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 <http://www.gnu.org/licenses/>.
+#
+
+import os, sys
+
+sys.path.insert(0, "bin/python")
+
+import getopt
+import optparse
+
+import samba
+from samba import param
+
+from samba.auth import system_session
+import samba.getopt as options
+from samba.provision import provision_backend, find_setup_dir
+
+parser = optparse.OptionParser("provision [options]")
+sambaopts = options.SambaOptions(parser)
+parser.add_option_group(sambaopts)
+parser.add_option_group(options.VersionOptions(parser))
+credopts = options.CredentialsOptions(parser)
+parser.add_option_group(credopts)
+parser.add_option("--setupdir", type="string", metavar="DIR",
+ help="directory with setup files")
+parser.add_option("--realm", type="string", metavar="REALM", help="set realm")
+parser.add_option("--domain", type="string", metavar="DOMAIN",
+ help="set domain")
+parser.add_option("--host-name", type="string", metavar="HOSTNAME",
+ help="set hostname")
+parser.add_option("--ldap-admin-pass", type="string", metavar="PASSWORD",
+ help="choose LDAP admin password (otherwise random)")
+parser.add_option("--root", type="string", metavar="USERNAME",
+ help="choose 'root' unix username")
+parser.add_option("--quiet", help="Be quiet", action="store_true")
+parser.add_option("--ldap-backend-type", type="choice", metavar="LDAP-BACKEND-TYPE",
+ help="LDB mapping module to use for the LDAP backend",
+ choices=["fedora-ds", "openldap"])
+parser.add_option("--ldap-backend-port", type="int", metavar="PORT",
+ help="TCP Port LDAP server should listen to (default ldapi only)")
+parser.add_option("--server-role", type="choice", metavar="ROLE",
+ choices=["domain controller", "dc", "member server", "member", "standalone"],
+ help="Set server role to provision for (default standalone)")
+parser.add_option("--targetdir", type="string", metavar="DIR",
+ help="Set target directory")
+parser.add_option("--ol-mmr-urls", type="string", metavar="LDAPSERVER",
+ help="List of LDAP-URLS [ ldap://<FQHN>:<PORT>/ (where <PORT> has to be different from 389!) ] separated with whitespaces for use with OpenLDAP-MMR (Multi-Master-Replication)")
+parser.add_option("--ol-olc", type="choice", metavar="OPENLDAP-OLC",
+ help="To setup OpenLDAP-Backend with Online-Configuration [slapd.d] choose 'yes'. Note: Only OpenLDAP-Versions greater or equal 2.4.15 should be used!",
+ choices=["yes", "no"])
+parser.add_option("--ol-slaptest", type="string", metavar="SLAPTEST-PATH",
+ help="Path to slaptest-binary [e.g.:'/usr/local/sbin']. Only for use with --ol-olc='yes'")
+
+opts = parser.parse_args()[0]
+
+def message(text):
+ """print a message if quiet is not set."""
+ if not opts.quiet:
+ print text
+
+if opts.realm is None or opts.domain is None:
+ if opts.realm is None:
+ print >>sys.stderr, "No realm set"
+ if opts.domain is None:
+ print >>sys.stderr, "No domain set"
+ parser.print_usage()
+ sys.exit(1)
+
+smbconf = sambaopts.get_loadparm().configfile
+
+if opts.server_role == "dc":
+ server_role = "domain controller"
+elif opts.server_role == "member":
+ server_role = "member server"
+else:
+ server_role = opts.server_role
+
+setup_dir = opts.setupdir
+if setup_dir is None:
+ setup_dir = find_setup_dir()
+
+provision_backend(setup_dir=setup_dir, message=message, smbconf=smbconf, targetdir=opts.targetdir,
+ realm=opts.realm, domain=opts.domain,
+ hostname=opts.host_name,
+ adminpass=opts.ldap_admin_pass,
+ root=opts.root, serverrole=server_role,
+ ldap_backend_type=opts.ldap_backend_type,
+ ldap_backend_port=opts.ldap_backend_port,
+ ol_mmr_urls=opts.ol_mmr_urls,
+ ol_olc=opts.ol_olc,
+ ol_slaptest=opts.ol_slaptest)
+
diff --git a/source4/setup/provision.ldif b/source4/setup/provision.ldif
new file mode 100644
index 0000000000..2f734e83b2
--- /dev/null
+++ b/source4/setup/provision.ldif
@@ -0,0 +1,84 @@
+dn: OU=Domain Controllers,${DOMAINDN}
+objectClass: top
+objectClass: organizationalUnit
+cn: Domain Controllers
+description: Default container for domain controllers
+systemFlags: 2348810240
+isCriticalSystemObject: TRUE
+showInAdvancedViewOnly: FALSE
+
+dn: CN=ForeignSecurityPrincipals,${DOMAINDN}
+objectClass: top
+objectClass: container
+cn: ForeignSecurityPrincipals
+description: Default container for security identifiers (SIDs) associated with objects from external, trusted domains
+systemFlags: 2348810240
+isCriticalSystemObject: TRUE
+showInAdvancedViewOnly: FALSE
+
+dn: CN=System,${DOMAINDN}
+objectClass: top
+objectClass: container
+cn: System
+description: Builtin system settings
+systemFlags: 2348810240
+isCriticalSystemObject: TRUE
+
+dn: CN=RID Manager$,CN=System,${DOMAINDN}
+objectclass: top
+objectclass: rIDManager
+cn: RID Manager$
+systemFlags: 2348810240
+isCriticalSystemObject: TRUE
+fSMORoleOwner: CN=NTDS Settings,${SERVERDN}
+rIDAvailablePool: 4611686014132423217
+
+dn: CN=DomainUpdates,CN=System,${DOMAINDN}
+objectClass: top
+objectClass: container
+cn: DomainUpdates
+
+dn: CN=Windows2003Update,CN=DomainUpdates,CN=System,${DOMAINDN}
+objectClass: top
+objectClass: container
+cn: Windows2003Update
+revision: 8
+
+dn: CN=Infrastructure,${DOMAINDN}
+objectclass: top
+objectclass: infrastructureUpdate
+cn: Infrastructure
+systemFlags: 2348810240
+isCriticalSystemObject: TRUE
+fSMORoleOwner: CN=NTDS Settings,${SERVERDN}
+
+dn: CN=Builtin,${DOMAINDN}
+objectClass: top
+objectClass: builtinDomain
+cn: Builtin
+forceLogoff: 9223372036854775808
+lockoutDuration: -18000000000
+lockOutObservationWindow: -18000000000
+lockoutThreshold: 0
+maxPwdAge: -37108517437440
+minPwdAge: 0
+minPwdLength: 0
+modifiedCountAtLastProm: 0
+nextRid: 1000
+pwdProperties: 0
+pwdHistoryLength: 0
+objectSid: S-1-5-32
+serverState: 1
+uASCompat: 1
+modifiedCount: 1
+isCriticalSystemObject: TRUE
+showInAdvancedViewOnly: FALSE
+
+dn: CN=Policies,CN=System,${DOMAINDN}
+objectClass: top
+objectClass: container
+
+dn: CN=IP Security,CN=System,${DOMAINDN}
+objectClass: top
+objectClass: container
+
diff --git a/source4/setup/provision.reg b/source4/setup/provision.reg
new file mode 100644
index 0000000000..54da54270b
--- /dev/null
+++ b/source4/setup/provision.reg
@@ -0,0 +1,45 @@
+REGEDIT4
+
+[HKEY_LOCAL_MACHINE]
+
+[HKEY_LOCAL_MACHINE\SOFTWARE]
+
+[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft]
+
+[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT]
+
+[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion]
+CurrentVersion=5.2
+
+[HKEY_LOCAL_MACHINE\SYSTEM]
+
+[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet]
+
+[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control]
+
+[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\ProductOptions]
+ProductType=LanmanNT
+
+[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Print]
+
+[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server]
+
+[HKEY_LOCAL_MACHINE\SYSTEM]
+
+[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet]
+
+[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services]
+
+[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Netlogon]
+
+[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters]
+RefusePasswordChange=REG_DWORD:0
+
+[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Alerter]
+
+[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Alerter\Parameters]
+
+[HKEY_USERS]
+
+[HKEY_CLASSES_ROOT]
+
diff --git a/source4/setup/provision.smb.conf.dc b/source4/setup/provision.smb.conf.dc
new file mode 100644
index 0000000000..ad06be4301
--- /dev/null
+++ b/source4/setup/provision.smb.conf.dc
@@ -0,0 +1,15 @@
+[globals]
+ netbios name = ${HOSTNAME}
+ workgroup = ${DOMAIN}
+ realm = ${REALM}
+ server role = ${SERVERROLE}
+ ${PRIVATEDIR_LINE}
+ ${LOCKDIR_LINE}
+
+[netlogon]
+ path = ${NETLOGONPATH}
+ read only = no
+
+[sysvol]
+ path = ${SYSVOLPATH}
+ read only = no
diff --git a/source4/setup/provision.smb.conf.member b/source4/setup/provision.smb.conf.member
new file mode 100644
index 0000000000..0d742fb903
--- /dev/null
+++ b/source4/setup/provision.smb.conf.member
@@ -0,0 +1,7 @@
+[globals]
+ netbios name = ${HOSTNAME}
+ workgroup = ${DOMAIN}
+ realm = ${REALM}
+ server role = ${SERVERROLE}
+ ${PRIVATEDIR_LINE}
+ ${LOCKDIR_LINE}
diff --git a/source4/setup/provision.smb.conf.standalone b/source4/setup/provision.smb.conf.standalone
new file mode 100644
index 0000000000..0d742fb903
--- /dev/null
+++ b/source4/setup/provision.smb.conf.standalone
@@ -0,0 +1,7 @@
+[globals]
+ netbios name = ${HOSTNAME}
+ workgroup = ${DOMAIN}
+ realm = ${REALM}
+ server role = ${SERVERROLE}
+ ${PRIVATEDIR_LINE}
+ ${LOCKDIR_LINE}
diff --git a/source4/setup/provision.zone b/source4/setup/provision.zone
new file mode 100644
index 0000000000..e7d600df87
--- /dev/null
+++ b/source4/setup/provision.zone
@@ -0,0 +1,50 @@
+; -*- zone -*-
+; generated by provision.pl
+$ORIGIN ${DNSDOMAIN}.
+$TTL 1W
+@ IN SOA @ hostmaster (
+ ${DATESTRING} ; serial
+ 2D ; refresh
+ 4H ; retry
+ 6W ; expiry
+ 1W ) ; minimum
+ IN NS ${HOSTNAME}
+${HOSTIP6_BASE_LINE}
+${HOSTIP_BASE_LINE}
+;
+${HOSTIP6_HOST_LINE}
+${HOSTIP_HOST_LINE}
+gc._msdcs IN CNAME ${HOSTNAME}
+${HOSTGUID}._msdcs IN CNAME ${HOSTNAME}
+;
+; global catalog servers
+_gc._tcp IN SRV 0 100 3268 ${HOSTNAME}
+_gc._tcp.${DEFAULTSITE}._sites IN SRV 0 100 3268 ${HOSTNAME}
+_ldap._tcp.gc._msdcs IN SRV 0 100 389 ${HOSTNAME}
+_ldap._tcp.${DEFAULTSITE}._sites.gc._msdcs IN SRV 0 100 389 ${HOSTNAME}
+;
+; ldap servers
+_ldap._tcp IN SRV 0 100 389 ${HOSTNAME}
+_ldap._tcp.dc._msdcs IN SRV 0 100 389 ${HOSTNAME}
+_ldap._tcp.pdc._msdcs IN SRV 0 100 389 ${HOSTNAME}
+_ldap._tcp.${DOMAINGUID} IN SRV 0 100 389 ${HOSTNAME}
+_ldap._tcp.${DOMAINGUID}.domains._msdcs IN SRV 0 100 389 ${HOSTNAME}
+_ldap._tcp.${DEFAULTSITE}._sites IN SRV 0 100 389 ${HOSTNAME}
+_ldap._tcp.${DEFAULTSITE}._sites.dc._msdcs IN SRV 0 100 389 ${HOSTNAME}
+;
+; krb5 servers
+_kerberos._tcp IN SRV 0 100 88 ${HOSTNAME}
+_kerberos._tcp.dc._msdcs IN SRV 0 100 88 ${HOSTNAME}
+_kerberos._tcp.${DEFAULTSITE}._sites IN SRV 0 100 88 ${HOSTNAME}
+_kerberos._tcp.${DEFAULTSITE}._sites.dc._msdcs IN SRV 0 100 88 ${HOSTNAME}
+_kerberos._udp IN SRV 0 100 88 ${HOSTNAME}
+; MIT kpasswd likes to lookup this name on password change
+_kerberos-master._tcp IN SRV 0 100 88 ${HOSTNAME}
+_kerberos-master._udp IN SRV 0 100 88 ${HOSTNAME}
+;
+; kpasswd
+_kpasswd._tcp IN SRV 0 100 464 ${HOSTNAME}
+_kpasswd._udp IN SRV 0 100 464 ${HOSTNAME}
+;
+; heimdal 'find realm for host' hack
+_kerberos IN TXT ${REALM}
diff --git a/source4/setup/provision_basedn.ldif b/source4/setup/provision_basedn.ldif
new file mode 100644
index 0000000000..7fdecfa3c0
--- /dev/null
+++ b/source4/setup/provision_basedn.ldif
@@ -0,0 +1,8 @@
+################################
+## Domain Naming Context
+################################
+dn: ${DOMAINDN}
+objectClass: top
+objectClass: ${DOMAIN_OC}
+${ACI}
+
diff --git a/source4/setup/provision_basedn_modify.ldif b/source4/setup/provision_basedn_modify.ldif
new file mode 100644
index 0000000000..7b13a193cb
--- /dev/null
+++ b/source4/setup/provision_basedn_modify.ldif
@@ -0,0 +1,85 @@
+###############################
+# Domain Naming Context
+###############################
+dn: ${DOMAINDN}
+changetype: modify
+-
+replace: forceLogoff
+forceLogoff: 9223372036854775808
+-
+replace: lockoutDuration
+lockoutDuration: -18000000000
+-
+replace: lockOutObservationWindow
+lockOutObservationWindow: -18000000000
+-
+replace: lockoutThreshold
+lockoutThreshold: 0
+-
+replace: maxPwdAge
+maxPwdAge: -37108517437440
+-
+replace: minPwdAge
+minPwdAge: 0
+-
+replace: minPwdLength
+minPwdLength: 7
+-
+replace: modifiedCountAtLastProm
+modifiedCountAtLastProm: 0
+-
+replace: nextRid
+nextRid: 1000
+-
+replace: pwdProperties
+pwdProperties: 1
+-
+replace: pwdHistoryLength
+pwdHistoryLength: 24
+-
+replace: objectSid
+objectSid: ${DOMAINSID}
+-
+replace: oEMInformation
+oEMInformation: Provisioned by Samba4: ${LDAPTIME}
+-
+replace: serverState
+serverState: 1
+-
+replace: nTMixedDomain
+nTMixedDomain: 1
+-
+replace: msDS-Behavior-Version
+msDS-Behavior-Version: 0
+-
+replace: ridManagerReference
+ridManagerReference: CN=RID Manager$,CN=System,${DOMAINDN}
+-
+replace: uASCompat
+uASCompat: 1
+-
+replace: modifiedCount
+modifiedCount: 1
+-
+replace: fSMORoleOwner
+fSMORoleOwner: CN=NTDS Settings,${SERVERDN}
+-
+replace: isCriticalSystemObject
+isCriticalSystemObject: TRUE
+-
+replace: subRefs
+subRefs: ${CONFIGDN}
+subRefs: ${SCHEMADN}
+-
+replace: gPLink
+gPLink: [LDAP://CN={${POLICYGUID}},CN=Policies,CN=System,${DOMAINDN};0]
+-
+replace: wellKnownObjects
+wellKnownObjects: B:32:22b70c67d56e4efb91e9300fca3dc1aa:CN=ForeignSecurityPrincipals,${DOMAINDN}
+wellKnownObjects: B:32:2fbac1870ade11d297c400c04fd8d5cd:CN=Infrastructure,${DOMAINDN}
+wellKnownObjects: B:32:ab1d30f3768811d1aded00c04fd8d5cd:CN=System,${DOMAINDN}
+wellKnownObjects: B:32:a361b2ffffd211d1aa4b00c04fd7d83a:OU=Domain Controllers,${DOMAINDN}
+wellKnownObjects: B:32:aa312825768811d1aded00c04fd8d5cd:CN=Computers,${DOMAINDN}
+wellKnownObjects: B:32:a9d1ca15768811d1aded00c04fd8d5cd:CN=Users,${DOMAINDN}
+-
+${DOMAINGUID_MOD}
diff --git a/source4/setup/provision_computers_add.ldif b/source4/setup/provision_computers_add.ldif
new file mode 100644
index 0000000000..6db3f41524
--- /dev/null
+++ b/source4/setup/provision_computers_add.ldif
@@ -0,0 +1,3 @@
+dn: CN=Computers,${DOMAINDN}
+objectClass: top
+objectClass: container
diff --git a/source4/setup/provision_computers_modify.ldif b/source4/setup/provision_computers_modify.ldif
new file mode 100644
index 0000000000..3bb4074d42
--- /dev/null
+++ b/source4/setup/provision_computers_modify.ldif
@@ -0,0 +1,13 @@
+dn: CN=Computers,${DOMAINDN}
+changetype: modify
+replace: description
+description: Default container for upgraded computer accounts
+-
+replace: showInAdvancedViewOnly
+showInAdvancedViewOnly: FALSE
+-
+replace: systemFlags
+systemFlags: 2348810240
+-
+replace: isCriticalSystemObject
+isCriticalSystemObject: TRUE
diff --git a/source4/setup/provision_configuration.ldif b/source4/setup/provision_configuration.ldif
new file mode 100644
index 0000000000..2a7357d7ad
--- /dev/null
+++ b/source4/setup/provision_configuration.ldif
@@ -0,0 +1,94 @@
+###############################
+# Configuration Naming Context
+###############################
+dn: CN=Partitions,${CONFIGDN}
+objectClass: top
+objectClass: crossRefContainer
+cn: Partitions
+systemFlags: 2147483648
+msDS-Behavior-Version: 0
+fSMORoleOwner: CN=NTDS Settings,${SERVERDN}
+
+dn: CN=Enterprise Configuration,CN=Partitions,${CONFIGDN}
+objectClass: top
+objectClass: crossRef
+cn: Enterprise Configuration
+systemFlags: 1
+nCName: ${CONFIGDN}
+dnsRoot: ${DNSDOMAIN}
+
+dn: CN=Enterprise Schema,CN=Partitions,${CONFIGDN}
+objectClass: top
+objectClass: crossRef
+cn: Enterprise Schema
+systemFlags: 1
+nCName: ${SCHEMADN}
+dnsRoot: ${DNSDOMAIN}
+
+dn: CN=${DOMAIN},CN=Partitions,${CONFIGDN}
+objectClass: top
+objectClass: crossRef
+cn: ${DOMAIN}
+systemFlags: 3
+nCName: ${DOMAINDN}
+nETBIOSName: ${DOMAIN}
+dnsRoot: ${DNSDOMAIN}
+
+dn: CN=Sites,${CONFIGDN}
+objectClass: top
+objectClass: sitesContainer
+cn: Sites
+systemFlags: 2181038080
+
+dn: CN=${DEFAULTSITE},CN=Sites,${CONFIGDN}
+objectClass: top
+objectClass: site
+cn: ${DEFAULTSITE}
+systemFlags: 2181038080
+
+dn: CN=Servers,CN=${DEFAULTSITE},CN=Sites,${CONFIGDN}
+objectClass: top
+objectClass: serversContainer
+cn: Servers
+systemFlags: 2181038080
+
+dn: CN=Services,${CONFIGDN}
+objectClass: top
+objectClass: container
+cn: Services
+systemFlags: 2147483648
+
+dn: CN=Windows NT,CN=Services,${CONFIGDN}
+objectClass: top
+objectClass: container
+cn: Windows NT
+
+dn: CN=Directory Service,CN=Windows NT,CN=Services,${CONFIGDN}
+objectClass: top
+objectClass: nTDSService
+cn: Directory Service
+sPNMappings: host=ldap,dns,cifs,http
+
+dn: CN=Query-Policies,CN=Directory Service,CN=Windows NT,CN=Services,${CONFIGDN}
+objectClass: top
+objectClass: container
+cn: Query-Policies
+
+dn: CN=Default Query Policy,CN=Query-Policies,CN=Directory Service,CN=Windows NT,CN=Services,${CONFIGDN}
+objectClass: top
+objectClass: queryPolicy
+cn: Default Query Policy
+lDAPAdminLimits: MaxValRange=1500
+lDAPAdminLimits: MaxReceiveBuffer=10485760
+lDAPAdminLimits: MaxDatagramRecv=4096
+lDAPAdminLimits: MaxPoolThreads=4
+lDAPAdminLimits: MaxResultSetSize=262144
+lDAPAdminLimits: MaxTempTableSize=10000
+lDAPAdminLimits: MaxQueryDuration=120
+lDAPAdminLimits: MaxPageSize=1000
+lDAPAdminLimits: MaxNotificationPerConn=5
+lDAPAdminLimits: MaxActiveQueries=20
+lDAPAdminLimits: MaxConnIdleTime=900
+lDAPAdminLimits: InitRecvTimeout=120
+lDAPAdminLimits: MaxConnections=5000
+
diff --git a/source4/setup/provision_configuration_basedn.ldif b/source4/setup/provision_configuration_basedn.ldif
new file mode 100644
index 0000000000..575f8faa0a
--- /dev/null
+++ b/source4/setup/provision_configuration_basedn.ldif
@@ -0,0 +1,8 @@
+###############################
+# Configuration Naming Context
+###############################
+dn: ${CONFIGDN}
+objectClass: top
+objectClass: configuration
+${ACI}
+cn: Configuration
diff --git a/source4/setup/provision_configuration_basedn_modify.ldif b/source4/setup/provision_configuration_basedn_modify.ldif
new file mode 100644
index 0000000000..9b87e1cead
--- /dev/null
+++ b/source4/setup/provision_configuration_basedn_modify.ldif
@@ -0,0 +1,7 @@
+###############################
+# Configuration Naming Context
+###############################
+dn: ${CONFIGDN}
+changetype: modify
+replace: subRefs
+subRefs: ${SCHEMADN}
diff --git a/source4/setup/provision_group_policy.ldif b/source4/setup/provision_group_policy.ldif
new file mode 100644
index 0000000000..0f3e1f15f9
--- /dev/null
+++ b/source4/setup/provision_group_policy.ldif
@@ -0,0 +1,25 @@
+dn: CN={${POLICYGUID}},CN=Policies,CN=System,${DOMAINDN}
+objectClass: top
+objectClass: container
+objectClass: groupPolicyContainer
+displayName: Default Domain Policy
+gPCFunctionalityVersion: 2
+gPCFileSysPath: \\${DNSDOMAIN}\sysvol\${DNSDOMAIN}\Policies\{${POLICYGUID}}
+versionNumber: 1
+flags: 0
+gPCMachineExtensionNames: [{35378EAC-683F-11D2-A89A-00C04FBBCFA2}{53D6AB1B-248
+ 8-11D1-A28C-00C04FB94F17}][{827D319E-6EAC-11D2-A4EA-00C04F79F83A}{803E14A0-B4
+ FB-11D0-A0D0-00A0C90F574B}][{B1BE8D72-6EAC-11D2-A4EA-00C04F79F83A}{53D6AB1B-2
+ 488-11D1-A28C-00C04FB94F17}]
+gPCUserExtensionNames: [{3060E8D0-7020-11D2-842D-00C04FA372D4}{3060E8CE-7020-1
+ 1D2-842D-00C04FA372D4}][{35378EAC-683F-11D2-A89A-00C04FBBCFA2}{0F6B957E-509E-
+ 11D1-A7CC-0000F87571E3}]
+nTSecurityDescriptor: O:${DOMAINSID}-512G:${DOMAINSID}-512D:PAI(A;CI;RPWPCCDCLCLORCWOWDSDDTSW;;;${DOMAINSID}-512)(A;CI;RPWPCCDCLCLORCWOWDSDDTSW;;;${DOMAINSID}-519)(A;;RPWPCCDCLCLORCWOWDSDDTSW;;;${DOMAINSID}-512)(A;CIIO;RPWPCCDCLCLORCWOWDSDDTSW;;;CO)(A;CI;RPWPCCDCLCLORCWOWDSDDTSW;;;SY)(A;CI;RPLCLORC;;;AU)(OA;CI;CR;edacfd8f-ffb3-11d1-b41d-00a0c968f939;;AU)(A;CI;RPLCLORC;;;ED)S:AI(OU;CIIDSA;WPWD;;f30e3bc2-9ff0-11d1-b603-0000f80367c1;WD)(OU;CIIOIDSA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)(OU;CIIOIDSA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)
+
+dn: CN=User,CN={${POLICYGUID}},CN=Policies,CN=System,${DOMAINDN}
+objectClass: top
+objectClass: container
+
+dn: CN=Machine,CN={${POLICYGUID}},CN=Policies,CN=System,${DOMAINDN}
+objectClass: top
+objectClass: container
diff --git a/source4/setup/provision_init.ldif b/source4/setup/provision_init.ldif
new file mode 100644
index 0000000000..8e9b68fb30
--- /dev/null
+++ b/source4/setup/provision_init.ldif
@@ -0,0 +1,54 @@
+#These attributes are only used as far as the bootstrapping of the
+# schema. After that, the attributes from the schema are used.
+#
+# Therefore, they must strictly match the schema
+
+dn: @ATTRIBUTES
+userPrincipalName: CASE_INSENSITIVE
+servicePrincipalName: CASE_INSENSITIVE
+dnsDomain: CASE_INSENSITIVE
+dnsRoot: CASE_INSENSITIVE
+nETBIOSName: CASE_INSENSITIVE
+cn: CASE_INSENSITIVE
+dc: CASE_INSENSITIVE
+name: CASE_INSENSITIVE
+lDAPDisplayName: CASE_INSENSITIVE
+subClassOf: CASE_INSENSITIVE
+dn: CASE_INSENSITIVE
+sAMAccountName: CASE_INSENSITIVE
+objectClass: CASE_INSENSITIVE
+userPassword: HIDDEN
+krb5Key: HIDDEN
+ntPwdHash: HIDDEN
+sambaNTPwdHistory: HIDDEN
+lmPwdHash: HIDDEN
+sambaLMPwdHistory: HIDDEN
+createTimestamp: HIDDEN
+modifyTimestamp: HIDDEN
+groupType: INTEGER
+sAMAccountType: INTEGER
+systemFlags: INTEGER
+userAccountControl: INTEGER
+
+dn: @OPTIONS
+checkBaseOnSearch: TRUE
+
+dn: @KLUDGEACL
+passwordAttribute: clearTextPassword
+passwordAttribute: userPassword
+passwordAttribute: ntPwdHash
+passwordAttribute: sambaNTPwdHistory
+passwordAttribute: lmPwdHash
+passwordAttribute: sambaLMPwdHistory
+passwordAttribute: krb5key
+passwordAttribute: dBCSPwd
+passwordAttribute: unicodePwd
+passwordAttribute: ntPwdHistory
+passwordAttribute: lmPwdHistory
+passwordAttribute: supplementalCredentials
+passwordAttribute: priorValue
+passwordAttribute: currentValue
+passwordAttribute: trustAuthOutgoing
+passwordAttribute: trustAuthIncoming
+passwordAttribute: initialAuthOutgoing
+passwordAttribute: initialAuthIncoming
diff --git a/source4/setup/provision_partitions.ldif b/source4/setup/provision_partitions.ldif
new file mode 100644
index 0000000000..93fea6bc2d
--- /dev/null
+++ b/source4/setup/provision_partitions.ldif
@@ -0,0 +1,13 @@
+dn: @PARTITION
+partition: ${SCHEMADN}:${SCHEMADN_LDB}
+partition: ${CONFIGDN}:${CONFIGDN_LDB}
+partition: ${DOMAINDN}:${DOMAINDN_LDB}
+replicateEntries: @ATTRIBUTES
+replicateEntries: @INDEXLIST
+replicateEntries: @OPTIONS
+modules:${SCHEMADN}:${SCHEMADN_MOD},${BACKEND_MOD}
+modules:${CONFIGDN}:${CONFIGDN_MOD},${BACKEND_MOD}
+modules:${DOMAINDN}:${DOMAINDN_MOD},${BACKEND_MOD}
+
+dn: @MODULES
+@LIST: ${MODULES_LIST}${TDB_MODULES_LIST},${MODULES_LIST2}
diff --git a/source4/setup/provision_rootdse_add.ldif b/source4/setup/provision_rootdse_add.ldif
new file mode 100644
index 0000000000..14e0d71df6
--- /dev/null
+++ b/source4/setup/provision_rootdse_add.ldif
@@ -0,0 +1,17 @@
+# the rootDSE module looks in this record for its base data
+dn: @ROOTDSE
+subschemaSubentry: CN=Aggregate,${SCHEMADN}
+dsServiceName: CN=NTDS Settings,${SERVERDN}
+defaultNamingContext: ${DOMAINDN}
+rootDomainNamingContext: ${ROOTDN}
+configurationNamingContext: ${CONFIGDN}
+schemaNamingContext: ${SCHEMADN}
+supportedLDAPVersion: 3
+dnsHostName: ${DNSNAME}
+ldapServiceName: ${DNSDOMAIN}:${NETBIOSNAME}$@${REALM}
+serverName: ${SERVERDN}
+domainFunctionality: 0
+forestFunctionality: 0
+domainControllerFunctionality: 2
+isSynchronized: FALSE
+vendorName: Samba Team (http://samba.org)
diff --git a/source4/setup/provision_rootdse_modify.ldif b/source4/setup/provision_rootdse_modify.ldif
new file mode 100644
index 0000000000..1f950171a2
--- /dev/null
+++ b/source4/setup/provision_rootdse_modify.ldif
@@ -0,0 +1,5 @@
+# mark the database as syncronized
+dn: @ROOTDSE
+changetype: modify
+replace: isSynchronized
+isSynchronized: TRUE
diff --git a/source4/setup/provision_schema_basedn.ldif b/source4/setup/provision_schema_basedn.ldif
new file mode 100644
index 0000000000..fbfd4c09d6
--- /dev/null
+++ b/source4/setup/provision_schema_basedn.ldif
@@ -0,0 +1,8 @@
+###############################
+# Schema Naming Context
+###############################
+dn: ${SCHEMADN}
+objectClass: top
+objectClass: dMD
+${ACI}
+cn: Schema
diff --git a/source4/setup/provision_schema_basedn_modify.ldif b/source4/setup/provision_schema_basedn_modify.ldif
new file mode 100644
index 0000000000..d6c458904e
--- /dev/null
+++ b/source4/setup/provision_schema_basedn_modify.ldif
@@ -0,0 +1,14 @@
+###############################
+# Schema Naming Context
+###############################
+dn: ${SCHEMADN}
+changetype: modify
+replace: fSMORoleOwner
+fSMORoleOwner: CN=NTDS Settings,${SERVERDN}
+-
+replace: objectVersion
+objectVersion: 30
+-
+replace: prefixMap
+prefixMap:: ${PREFIXMAP_B64}
+
diff --git a/source4/setup/provision_self_join.ldif b/source4/setup/provision_self_join.ldif
new file mode 100644
index 0000000000..77a2e49865
--- /dev/null
+++ b/source4/setup/provision_self_join.ldif
@@ -0,0 +1,62 @@
+#Join the DC to itself by default
+
+dn: CN=${NETBIOSNAME},OU=Domain Controllers,${DOMAINDN}
+objectClass: computer
+cn: ${NETBIOSNAME}
+userAccountControl: 532480
+localPolicyFlags: 0
+primaryGroupID: 516
+accountExpires: 9223372036854775807
+sAMAccountName: ${NETBIOSNAME}$
+operatingSystem: Samba
+operatingSystemVersion: 4.0
+dNSHostName: ${DNSNAME}
+isCriticalSystemObject: TRUE
+userPassword:: ${MACHINEPASS_B64}
+servicePrincipalName: HOST/${DNSNAME}
+servicePrincipalName: HOST/${NETBIOSNAME}
+servicePrincipalName: HOST/${DNSNAME}/${REALM}
+servicePrincipalName: HOST/${NETBIOSNAME}/${REALM}
+servicePrincipalName: HOST/${DNSNAME}/${DOMAIN}
+servicePrincipalName: HOST/${NETBIOSNAME}/${DOMAIN}
+
+#Provide a account for DNS keytab export
+dn: CN=dns,CN=Users,${DOMAINDN}
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: user
+cn: dns
+description: DNS Service Account
+userAccountControl: 514
+accountExpires: 9223372036854775807
+sAMAccountName: dns
+servicePrincipalName: DNS/${DNSDOMAIN}
+isCriticalSystemObject: TRUE
+userPassword:: ${DNSPASS_B64}
+showInAdvancedViewOnly: TRUE
+
+dn: ${SERVERDN}
+objectClass: top
+objectClass: server
+cn: ${NETBIOSNAME}
+systemFlags: 1375731712
+dNSHostName: ${DNSNAME}
+serverReference: CN=${NETBIOSNAME},OU=Domain Controllers,${DOMAINDN}
+
+dn: CN=NTDS Settings,${SERVERDN}
+objectClass: top
+objectClass: applicationSettings
+objectClass: nTDSDSA
+cn: NTDS Settings
+options: 1
+systemFlags: 33554432
+dMDLocation: ${SCHEMADN}
+invocationId: ${INVOCATIONID}
+msDS-Behavior-Version: 2
+msDS-hasMasterNCs: ${CONFIGDN}
+msDS-hasMasterNCs: ${SCHEMADN}
+msDS-hasMasterNCs: ${DOMAINDN}
+hasMasterNCs: ${CONFIGDN}
+hasMasterNCs: ${SCHEMADN}
+hasMasterNCs: ${DOMAINDN}
diff --git a/source4/setup/provision_templates.ldif b/source4/setup/provision_templates.ldif
new file mode 100644
index 0000000000..04257549d5
--- /dev/null
+++ b/source4/setup/provision_templates.ldif
@@ -0,0 +1,43 @@
+###
+# Templates to be put in templates.ldb. Not part of main samdb any more.
+###
+
+dn: CN=Templates
+objectClass: top
+objectClass: container
+description: Container for SAM account templates
+
+dn: CN=TemplateUser,CN=Templates
+userAccountControl: 546
+badPwdCount: 0
+codePage: 0
+countryCode: 0
+badPasswordTime: 0
+lastLogoff: 0
+lastLogon: 0
+pwdLastSet: 0
+primaryGroupID: 513
+accountExpires: 9223372036854775807
+logonCount: 0
+
+dn: CN=TemplateTrustingDomain,CN=Templates
+userAccountControl: 2080
+badPwdCount: 0
+codePage: 0
+countryCode: 0
+badPasswordTime: 0
+lastLogoff: 0
+lastLogon: 0
+primaryGroupID: 513
+accountExpires: 9223372036854775807
+logonCount: 0
+
+dn: CN=TemplateGroup,CN=Templates
+groupType: -2147483646
+
+dn: CN=TemplateForeignSecurityPrincipal,CN=Templates
+
+dn: CN=TemplateSecret,CN=Templates
+
+dn: CN=TemplateTrustedDomain,CN=Templates
+
diff --git a/source4/setup/provision_templates_init.ldif b/source4/setup/provision_templates_init.ldif
new file mode 100644
index 0000000000..6d6a3c228c
--- /dev/null
+++ b/source4/setup/provision_templates_init.ldif
@@ -0,0 +1,10 @@
+dn: @OPTIONS
+checkBaseOnSearch: TRUE
+
+dn: @INDEXLIST
+@IDXATTR: cn
+
+dn: @ATTRIBUTES
+cn: CASE_INSENSITIVE
+dn: CASE_INSENSITIVE
+
diff --git a/source4/setup/provision_users.ldif b/source4/setup/provision_users.ldif
new file mode 100644
index 0000000000..c61cb805c4
--- /dev/null
+++ b/source4/setup/provision_users.ldif
@@ -0,0 +1,588 @@
+dn: CN=Administrator,CN=Users,${DOMAINDN}
+objectClass: user
+cn: Administrator
+description: Built-in account for administering the computer/domain
+userAccountControl: 66048
+objectSid: ${DOMAINSID}-500
+adminCount: 1
+accountExpires: 9223372036854775807
+sAMAccountName: Administrator
+isCriticalSystemObject: TRUE
+userPassword:: ${ADMINPASS_B64}
+
+dn: CN=Guest,CN=Users,${DOMAINDN}
+objectClass: user
+cn: Guest
+description: Built-in account for guest access to the computer/domain
+userAccountControl: 66082
+primaryGroupID: 514
+objectSid: ${DOMAINSID}-501
+sAMAccountName: Guest
+isCriticalSystemObject: TRUE
+
+dn: CN=Enterprise Admins,CN=Users,${DOMAINDN}
+objectClass: top
+objectClass: group
+cn: Enterprise Admins
+description: Designated administrators of the enterprise
+member: CN=Administrator,CN=Users,${DOMAINDN}
+objectSid: ${DOMAINSID}-519
+adminCount: 1
+sAMAccountName: Enterprise Admins
+isCriticalSystemObject: TRUE
+
+dn: CN=krbtgt,CN=Users,${DOMAINDN}
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: user
+cn: krbtgt
+description: Key Distribution Center Service Account
+showInAdvancedViewOnly: TRUE
+userAccountControl: 514
+objectSid: ${DOMAINSID}-502
+adminCount: 1
+accountExpires: 9223372036854775807
+sAMAccountName: krbtgt
+servicePrincipalName: kadmin/changepw
+isCriticalSystemObject: TRUE
+userPassword:: ${KRBTGTPASS_B64}
+
+dn: CN=Domain Computers,CN=Users,${DOMAINDN}
+objectClass: top
+objectClass: group
+cn: Domain Computers
+description: All workstations and servers joined to the domain
+objectSid: ${DOMAINSID}-515
+sAMAccountName: Domain Computers
+isCriticalSystemObject: TRUE
+
+dn: CN=Domain Controllers,CN=Users,${DOMAINDN}
+objectClass: top
+objectClass: group
+cn: Domain Controllers
+description: All domain controllers in the domain
+objectSid: ${DOMAINSID}-516
+adminCount: 1
+sAMAccountName: Domain Controllers
+isCriticalSystemObject: TRUE
+
+dn: CN=Schema Admins,CN=Users,${DOMAINDN}
+objectClass: top
+objectClass: group
+cn: Schema Admins
+description: Designated administrators of the schema
+member: CN=Administrator,CN=Users,${DOMAINDN}
+objectSid: ${DOMAINSID}-518
+adminCount: 1
+sAMAccountName: Schema Admins
+isCriticalSystemObject: TRUE
+
+dn: CN=Cert Publishers,CN=Users,${DOMAINDN}
+objectClass: top
+objectClass: group
+cn: Cert Publishers
+description: Members of this group are permitted to publish certificates to the Active Directory
+groupType: -2147483644
+objectSid: ${DOMAINSID}-517
+sAMAccountName: Cert Publishers
+isCriticalSystemObject: TRUE
+
+dn: CN=Domain Admins,CN=Users,${DOMAINDN}
+objectClass: top
+objectClass: group
+cn: Domain Admins
+description: Designated administrators of the domain
+member: CN=Administrator,CN=Users,${DOMAINDN}
+objectSid: ${DOMAINSID}-512
+adminCount: 1
+sAMAccountName: Domain Admins
+isCriticalSystemObject: TRUE
+
+dn: CN=Domain Users,CN=Users,${DOMAINDN}
+objectClass: top
+objectClass: group
+cn: Domain Users
+description: All domain users
+objectSid: ${DOMAINSID}-513
+sAMAccountName: Domain Users
+isCriticalSystemObject: TRUE
+
+dn: CN=Domain Guests,CN=Users,${DOMAINDN}
+objectClass: top
+objectClass: group
+cn: Domain Guests
+description: All domain guests
+objectSid: ${DOMAINSID}-514
+sAMAccountName: Domain Guests
+isCriticalSystemObject: TRUE
+
+dn: CN=Group Policy Creator Owners,CN=Users,${DOMAINDN}
+objectClass: top
+objectClass: group
+cn: Group Policy Creator Owners
+description: Members in this group can modify group policy for the domain
+member: CN=Administrator,CN=Users,${DOMAINDN}
+objectSid: ${DOMAINSID}-520
+sAMAccountName: Group Policy Creator Owners
+isCriticalSystemObject: TRUE
+
+dn: CN=RAS and IAS Servers,CN=Users,${DOMAINDN}
+objectClass: top
+objectClass: group
+cn: RAS and IAS Servers
+description: Servers in this group can access remote access properties of users
+objectSid: ${DOMAINSID}-553
+sAMAccountName: RAS and IAS Servers
+groupType: -2147483644
+isCriticalSystemObject: TRUE
+
+dn: CN=Read-Only Domain Controllers,CN=Users,${DOMAINDN}
+objectClass: top
+objectClass: group
+cn: Read-Only Domain Controllers
+description: read-only domain controllers
+objectSid: ${DOMAINSID}-521
+sAMAccountName: Read-Only Domain Controllers
+groupType: -2147483644
+isCriticalSystemObject: TRUE
+
+dn: CN=Enterprise Read-Only Domain Controllers,CN=Users,${DOMAINDN}
+objectClass: top
+objectClass: group
+cn: Enterprise Read-Only Domain Controllers
+description: enterprise read-only domain controllers
+objectSid: ${DOMAINSID}-498
+sAMAccountName: Enterprise Read-Only Domain Controllers
+groupType: -2147483644
+isCriticalSystemObject: TRUE
+
+dn: CN=Certificate Service DCOM Access,CN=Users,${DOMAINDN}
+objectClass: top
+objectClass: group
+cn: Certificate Service DCOM Access
+description: Certificate Service DCOM Access
+objectSid: ${DOMAINSID}-574
+sAMAccountName: Certificate Service DCOM Access
+groupType: -2147483644
+isCriticalSystemObject: TRUE
+
+dn: CN=Cryptographic Operators,CN=Users,${DOMAINDN}
+objectClass: top
+objectClass: group
+cn: Cryptographic Operators
+description: Cryptographic Operators
+objectSid: ${DOMAINSID}-569
+sAMAccountName: Cryptographic Operators
+groupType: -2147483644
+isCriticalSystemObject: TRUE
+
+dn: CN=Event Log Readers,CN=Users,${DOMAINDN}
+objectClass: top
+objectClass: group
+cn: Event Log Readers
+description: Event Log Readers
+objectSid: ${DOMAINSID}-573
+sAMAccountName: Event Log Readers
+groupType: -2147483644
+isCriticalSystemObject: TRUE
+
+dn: CN=IIS_IUSRS,CN=Users,${DOMAINDN}
+objectClass: top
+objectClass: group
+cn: IIS_IUSRS
+description: IIS_IUSRS
+objectSid: ${DOMAINSID}-568
+sAMAccountName: IIS_IUSRS
+groupType: -2147483644
+isCriticalSystemObject: TRUE
+
+dn: CN=Administrators,CN=Builtin,${DOMAINDN}
+objectClass: top
+objectClass: group
+cn: Administrators
+description: Administrators have complete and unrestricted access to the computer/domain
+member: CN=Domain Admins,CN=Users,${DOMAINDN}
+member: CN=Enterprise Admins,CN=Users,${DOMAINDN}
+member: CN=Administrator,CN=Users,${DOMAINDN}
+objectSid: S-1-5-32-544
+adminCount: 1
+sAMAccountName: Administrators
+systemFlags: 2348810240
+groupType: -2147483643
+isCriticalSystemObject: TRUE
+privilege: SeSecurityPrivilege
+privilege: SeBackupPrivilege
+privilege: SeRestorePrivilege
+privilege: SeSystemtimePrivilege
+privilege: SeShutdownPrivilege
+privilege: SeRemoteShutdownPrivilege
+privilege: SeTakeOwnershipPrivilege
+privilege: SeDebugPrivilege
+privilege: SeSystemEnvironmentPrivilege
+privilege: SeSystemProfilePrivilege
+privilege: SeProfileSingleProcessPrivilege
+privilege: SeIncreaseBasePriorityPrivilege
+privilege: SeLoadDriverPrivilege
+privilege: SeCreatePagefilePrivilege
+privilege: SeIncreaseQuotaPrivilege
+privilege: SeChangeNotifyPrivilege
+privilege: SeUndockPrivilege
+privilege: SeManageVolumePrivilege
+privilege: SeImpersonatePrivilege
+privilege: SeCreateGlobalPrivilege
+privilege: SeEnableDelegationPrivilege
+privilege: SeInteractiveLogonRight
+privilege: SeNetworkLogonRight
+privilege: SeRemoteInteractiveLogonRight
+
+dn: CN=Users,CN=Builtin,${DOMAINDN}
+objectClass: top
+objectClass: group
+cn: Users
+description: Users are prevented from making accidental or intentional system-wide changes. Thus, Users can run certified applications, but not most legacy applications
+member: CN=Domain Users,CN=Users,${DOMAINDN}
+objectSid: S-1-5-32-545
+sAMAccountName: Users
+systemFlags: 2348810240
+groupType: -2147483643
+isCriticalSystemObject: TRUE
+
+dn: CN=Guests,CN=Builtin,${DOMAINDN}
+objectClass: top
+objectClass: group
+cn: Guests
+description: Guests have the same access as members of the Users group by default, except for the Guest account which is further restricted
+member: CN=Domain Guests,CN=Users,${DOMAINDN}
+member: CN=Guest,CN=Users,${DOMAINDN}
+objectSid: S-1-5-32-546
+sAMAccountName: Guests
+systemFlags: 2348810240
+groupType: -2147483643
+isCriticalSystemObject: TRUE
+
+dn: CN=Print Operators,CN=Builtin,${DOMAINDN}
+objectClass: top
+objectClass: group
+cn: Print Operators
+description: Members can administer domain printers
+objectSid: S-1-5-32-550
+adminCount: 1
+sAMAccountName: Print Operators
+systemFlags: 2348810240
+groupType: -2147483643
+isCriticalSystemObject: TRUE
+privilege: SeLoadDriverPrivilege
+privilege: SeShutdownPrivilege
+privilege: SeInteractiveLogonRight
+
+dn: CN=Backup Operators,CN=Builtin,${DOMAINDN}
+objectClass: top
+objectClass: group
+cn: Backup Operators
+description: Backup Operators can override security restrictions for the sole purpose of backing up or restoring files
+objectSid: S-1-5-32-551
+adminCount: 1
+sAMAccountName: Backup Operators
+systemFlags: 2348810240
+groupType: -2147483643
+isCriticalSystemObject: TRUE
+privilege: SeBackupPrivilege
+privilege: SeRestorePrivilege
+privilege: SeShutdownPrivilege
+privilege: SeInteractiveLogonRight
+
+dn: CN=Replicator,CN=Builtin,${DOMAINDN}
+objectClass: top
+objectClass: group
+cn: Replicator
+description: Supports file replication in a domain
+objectSid: S-1-5-32-552
+adminCount: 1
+sAMAccountName: Replicator
+systemFlags: 2348810240
+groupType: -2147483643
+isCriticalSystemObject: TRUE
+
+dn: CN=Remote Desktop Users,CN=Builtin,${DOMAINDN}
+objectClass: top
+objectClass: group
+cn: Remote Desktop Users
+description: Members in this group are granted the right to logon remotely
+objectSid: S-1-5-32-555
+sAMAccountName: Remote Desktop Users
+systemFlags: 2348810240
+groupType: -2147483643
+isCriticalSystemObject: TRUE
+
+dn: CN=Network Configuration Operators,CN=Builtin,${DOMAINDN}
+objectClass: top
+objectClass: group
+cn: Network Configuration Operators
+description: Members in this group can have some administrative privileges to manage configuration of networking features
+objectSid: S-1-5-32-556
+sAMAccountName: Network Configuration Operators
+systemFlags: 2348810240
+groupType: -2147483643
+isCriticalSystemObject: TRUE
+
+dn: CN=Performance Monitor Users,CN=Builtin,${DOMAINDN}
+objectClass: top
+objectClass: group
+cn: Performance Monitor Users
+description: Members of this group have remote access to monitor this computer
+objectSid: S-1-5-32-558
+sAMAccountName: Performance Monitor Users
+systemFlags: 2348810240
+groupType: -2147483643
+isCriticalSystemObject: TRUE
+
+dn: CN=Performance Log Users,CN=Builtin,${DOMAINDN}
+objectClass: top
+objectClass: group
+cn: Performance Log Users
+description: Members of this group have remote access to schedule logging of performance counters on this computer
+objectSid: S-1-5-32-559
+sAMAccountName: Performance Log Users
+systemFlags: 2348810240
+groupType: -2147483643
+isCriticalSystemObject: TRUE
+
+dn: CN=Server Operators,CN=Builtin,${DOMAINDN}
+objectClass: top
+objectClass: group
+cn: Server Operators
+description: Members can administer domain servers
+objectSid: S-1-5-32-549
+adminCount: 1
+sAMAccountName: Server Operators
+systemFlags: 2348810240
+groupType: -2147483643
+isCriticalSystemObject: TRUE
+privilege: SeBackupPrivilege
+privilege: SeSystemtimePrivilege
+privilege: SeRemoteShutdownPrivilege
+privilege: SeRestorePrivilege
+privilege: SeShutdownPrivilege
+privilege: SeInteractiveLogonRight
+
+dn: CN=Account Operators,CN=Builtin,${DOMAINDN}
+objectClass: top
+objectClass: group
+cn: Account Operators
+description: Members can administer domain user and group accounts
+objectSid: S-1-5-32-548
+adminCount: 1
+sAMAccountName: Account Operators
+systemFlags: 2348810240
+groupType: -2147483643
+isCriticalSystemObject: TRUE
+privilege: SeInteractiveLogonRight
+
+dn: CN=Pre-Windows 2000 Compatible Access,CN=Builtin,${DOMAINDN}
+objectClass: top
+objectClass: group
+cn: Pre-Windows 2000 Compatible Access
+description: A backward compatibility group which allows read access on all users and groups in the domain
+objectSid: S-1-5-32-554
+sAMAccountName: Pre-Windows 2000 Compatible Access
+systemFlags: 2348810240
+groupType: -2147483643
+isCriticalSystemObject: TRUE
+privilege: SeRemoteInteractiveLogonRight
+privilege: SeChangeNotifyPrivilege
+
+dn: CN=Incoming Forest Trust Builders,CN=Builtin,${DOMAINDN}
+objectClass: top
+objectClass: group
+cn: Incoming Forest Trust Builders
+description: Members of this group can create incoming, one-way trusts to this forest
+objectSid: S-1-5-32-557
+sAMAccountName: Incoming Forest Trust Builders
+systemFlags: 2348810240
+groupType: -2147483643
+isCriticalSystemObject: TRUE
+
+dn: CN=Windows Authorization Access Group,CN=Builtin,${DOMAINDN}
+objectClass: top
+objectClass: group
+cn: Windows Authorization Access Group
+description: Members of this group have access to the computed tokenGroupsGlobalAndUniversal attribute on User objects
+objectSid: S-1-5-32-560
+sAMAccountName: Windows Authorization Access Group
+systemFlags: 2348810240
+groupType: -2147483643
+isCriticalSystemObject: TRUE
+
+dn: CN=Terminal Server License Servers,CN=Builtin,${DOMAINDN}
+objectClass: top
+objectClass: group
+cn: Terminal Server License Servers
+description: Terminal Server License Servers
+objectSid: S-1-5-32-561
+sAMAccountName: Terminal Server License Servers
+systemFlags: 2348810240
+groupType: -2147483643
+isCriticalSystemObject: TRUE
+
+dn: CN=Distributed COM Users,CN=Builtin,${DOMAINDN}
+objectClass: top
+objectClass: group
+cn: Distributed COM Users
+description: Members are allowed to launch, activate and use Distributed COM objects on this machine.
+objectSid: S-1-5-32-562
+sAMAccountName: Distributed COM Users
+systemFlags: 2348810240
+groupType: -2147483643
+isCriticalSystemObject: TRUE
+
+dn: CN=WellKnown Security Principals,${CONFIGDN}
+objectClass: top
+objectClass: container
+cn: WellKnown Security Principals
+systemFlags: 2147483648
+
+dn: CN=Anonymous Logon,CN=WellKnown Security Principals,${CONFIGDN}
+objectClass: top
+objectClass: foreignSecurityPrincipal
+cn: Anonymous Logon
+objectSid: S-1-5-7
+
+dn: CN=Authenticated Users,CN=WellKnown Security Principals,${CONFIGDN}
+objectClass: top
+objectClass: foreignSecurityPrincipal
+cn: Authenticated Users
+objectSid: S-1-5-11
+
+dn: CN=Batch,CN=WellKnown Security Principals,${CONFIGDN}
+objectClass: top
+objectClass: foreignSecurityPrincipal
+cn: Batch
+objectSid: S-1-5-3
+
+dn: CN=Creator Group,CN=WellKnown Security Principals,${CONFIGDN}
+objectClass: top
+objectClass: foreignSecurityPrincipal
+cn: Creator Group
+objectSid: S-1-3-1
+
+dn: CN=Creator Owner,CN=WellKnown Security Principals,${CONFIGDN}
+objectClass: top
+objectClass: foreignSecurityPrincipal
+cn: Creator Owner
+objectSid: S-1-3-0
+
+dn: CN=Dialup,CN=WellKnown Security Principals,${CONFIGDN}
+objectClass: top
+objectClass: foreignSecurityPrincipal
+cn: Dialup
+objectSid: S-1-5-1
+
+dn: CN=Digest Authentication,CN=WellKnown Security Principals,${CONFIGDN}
+objectClass: top
+objectClass: foreignSecurityPrincipal
+cn: Digest Authentication
+objectSid: S-1-5-64-21
+
+dn: CN=Enterprise Domain Controllers,CN=WellKnown Security Principals,${CONFIGDN}
+objectClass: top
+objectClass: foreignSecurityPrincipal
+cn: Enterprise Domain Controllers
+objectSid: S-1-5-9
+
+dn: CN=Everyone,CN=WellKnown Security Principals,${CONFIGDN}
+objectClass: top
+objectClass: foreignSecurityPrincipal
+cn: Everyone
+objectSid: S-1-1-0
+
+dn: CN=Interactive,CN=WellKnown Security Principals,${CONFIGDN}
+objectClass: top
+objectClass: foreignSecurityPrincipal
+cn: Interactive
+objectSid: S-1-5-4
+
+dn: CN=Local Service,CN=WellKnown Security Principals,${CONFIGDN}
+objectClass: top
+objectClass: foreignSecurityPrincipal
+cn: Local Service
+objectSid: S-1-5-19
+
+dn: CN=Network,CN=WellKnown Security Principals,${CONFIGDN}
+objectClass: top
+objectClass: foreignSecurityPrincipal
+cn: Network
+objectSid: S-1-5-2
+
+dn: CN=Network Service,CN=WellKnown Security Principals,${CONFIGDN}
+objectClass: top
+objectClass: foreignSecurityPrincipal
+cn: Network Service
+objectSid: S-1-5-20
+
+dn: CN=NTLM Authentication,CN=WellKnown Security Principals,${CONFIGDN}
+objectClass: top
+objectClass: foreignSecurityPrincipal
+cn: NTLM Authentication
+objectSid: S-1-5-64-10
+
+dn: CN=Other Organization,CN=WellKnown Security Principals,${CONFIGDN}
+objectClass: top
+objectClass: foreignSecurityPrincipal
+cn: Other Organization
+objectSid: S-1-5-1000
+
+dn: CN=Proxy,CN=WellKnown Security Principals,${CONFIGDN}
+objectClass: top
+objectClass: foreignSecurityPrincipal
+cn: Proxy
+objectSid: S-1-5-8
+
+dn: CN=Remote Interactive Logon,CN=WellKnown Security Principals,${CONFIGDN}
+objectClass: top
+objectClass: foreignSecurityPrincipal
+cn: Remote Interactive Logon
+objectSid: S-1-5-14
+
+dn: CN=Restricted,CN=WellKnown Security Principals,${CONFIGDN}
+objectClass: top
+objectClass: foreignSecurityPrincipal
+cn: Restricted
+objectSid: S-1-5-12
+
+dn: CN=SChannel Authentication,CN=WellKnown Security Principals,${CONFIGDN}
+objectClass: top
+objectClass: foreignSecurityPrincipal
+cn: SChannel Authentication
+objectSid: S-1-5-64-14
+
+dn: CN=Self,CN=WellKnown Security Principals,${CONFIGDN}
+objectClass: top
+objectClass: foreignSecurityPrincipal
+cn: Self
+objectSid: S-1-5-10
+
+dn: CN=Service,CN=WellKnown Security Principals,${CONFIGDN}
+objectClass: top
+objectClass: foreignSecurityPrincipal
+cn: Service
+objectSid: S-1-5-6
+
+dn: CN=Terminal Server User,CN=WellKnown Security Principals,${CONFIGDN}
+objectClass: top
+objectClass: foreignSecurityPrincipal
+cn: Terminal Server User
+objectSid: S-1-5-13
+
+dn: CN=This Organization,CN=WellKnown Security Principals,${CONFIGDN}
+objectClass: top
+objectClass: foreignSecurityPrincipal
+cn: This Organization
+objectSid: S-1-5-15
+
+dn: CN=Well-Known-Security-Id-System,CN=WellKnown Security Principals,${CONFIGDN}
+objectClass: top
+objectClass: foreignSecurityPrincipal
+cn: Well-Known-Security-Id-System
+objectSid: S-1-5-18
+
diff --git a/source4/setup/provision_users_add.ldif b/source4/setup/provision_users_add.ldif
new file mode 100644
index 0000000000..db075d9c80
--- /dev/null
+++ b/source4/setup/provision_users_add.ldif
@@ -0,0 +1,3 @@
+dn: CN=Users,${DOMAINDN}
+objectClass: top
+objectClass: container
diff --git a/source4/setup/provision_users_modify.ldif b/source4/setup/provision_users_modify.ldif
new file mode 100644
index 0000000000..06954c44f0
--- /dev/null
+++ b/source4/setup/provision_users_modify.ldif
@@ -0,0 +1,13 @@
+dn: CN=Users,${DOMAINDN}
+changetype: modify
+replace: description
+description: Default container for upgraded user accounts
+-
+replace: showInAdvancedViewOnly
+showInAdvancedViewOnly: FALSE
+-
+replace: systemFlags
+systemFlags: 2348810240
+-
+replace: isCriticalSystemObject
+isCriticalSystemObject: TRUE
diff --git a/source4/setup/refint.conf b/source4/setup/refint.conf
new file mode 100644
index 0000000000..a3a7d3e0ad
--- /dev/null
+++ b/source4/setup/refint.conf
@@ -0,0 +1,3 @@
+overlay refint
+refint_modifiersName cn=samba-admin,cn=samba
+refint_attributes ${LINK_ATTRS}
diff --git a/source4/setup/schema-map-fedora-ds-1.0 b/source4/setup/schema-map-fedora-ds-1.0
new file mode 100644
index 0000000000..74d9e2ac5a
--- /dev/null
+++ b/source4/setup/schema-map-fedora-ds-1.0
@@ -0,0 +1,31 @@
+#Standard OpenLDAP attributes
+name
+objectClasses
+createTimeStamp
+attributeTypes
+objectClass
+userPassword
+seeAlso
+modifyTimeStamp
+distinguishedName
+description
+cn
+dITContentRules
+top
+#This should be provided by the LDAP server, only in our schema to permit provision
+aci
+#Skip ObjectClasses
+#MiddleName has a conflicting OID
+2.16.840.1.113730.3.1.34:1.3.6.1.4.1.7165.4.255.1
+#defaultGroup has a conflicting OID
+1.2.840.113556.1.4.480:1.3.6.1.4.1.7165.4.255.2
+#This large integer format is unimplemented in OpenLDAP 2.3
+1.2.840.113556.1.4.906:1.3.6.1.4.1.1466.115.121.1.27
+#This case insensitive string isn't available
+1.2.840.113556.1.4.905:1.3.6.1.4.1.1466.115.121.1.15
+#Treat Security Descriptors as binary
+1.2.840.113556.1.4.907:1.3.6.1.4.1.1466.115.121.1.40
+#NumbericString is not supported in Fedora DS 1.0, map to a directory string
+1.3.6.1.4.1.1466.115.121.1.36:1.3.6.1.4.1.1466.115.121.1.15
+#Treat Object(DN-Binary) as a binary blob
+1.2.840.113556.1.4.903:1.3.6.1.4.1.1466.115.121.1.40
diff --git a/source4/setup/schema-map-openldap-2.3 b/source4/setup/schema-map-openldap-2.3
new file mode 100644
index 0000000000..0d38652dae
--- /dev/null
+++ b/source4/setup/schema-map-openldap-2.3
@@ -0,0 +1,54 @@
+#Standard OpenLDAP attributes
+labeledURI
+createTimeStamp
+objectClass
+userPassword
+seeAlso
+uid
+subSchemaSubEntry
+structuralObjectClass
+distinguishedName
+description
+cn
+top
+uidNumber
+gidNumber
+#The memberOf plugin provides this attribute
+memberOf
+#These conflict with OpenLDAP builtins
+attributeTypes:samba4AttributeTypes
+2.5.21.5:1.3.6.1.4.1.7165.4.255.7
+dITContentRules:samba4DITContentRules
+2.5.21.2:1.3.6.1.4.1.7165.4.255.6
+objectClasses:samba4ObjectClasses
+2.5.21.6:1.3.6.1.4.1.7165.4.255.5
+subSchema:samba4SubSchema
+2.5.20.1:1.3.6.1.4.1.7165.4.255.4
+#'name' is the RDN in AD, but something else in OpenLDAP
+name:samba4RDN
+#Remap these so that we don't put operational attributes in a schema MAY
+modifyTimeStamp:samba4ModifyTimestamp
+2.5.18.2:1.3.6.1.4.1.7165.4.255.3
+dynamicObject:samba4DynaimcObject
+1.3.6.1.4.1.1466.101.119.2:1.3.6.1.4.1.7165.4.255.8
+entryTTL:samba4EntryTTL
+1.3.6.1.4.1.1466.101.119.3:1.3.6.1.4.1.7165.4.255.9
+#MiddleName has a conflicting OID
+2.16.840.1.113730.3.1.34:1.3.6.1.4.1.7165.4.255.1
+#defaultGroup has a conflicting OID
+1.2.840.113556.1.4.480:1.3.6.1.4.1.7165.4.255.2
+#This large integer format is unimplemented in OpenLDAP 2.3
+1.2.840.113556.1.4.906:1.3.6.1.4.1.1466.115.121.1.27
+#This case insensitive string isn't available
+1.2.840.113556.1.4.905:1.3.6.1.4.1.1466.115.121.1.44
+#Treat Security Descriptors as binary
+1.2.840.113556.1.4.907:1.3.6.1.4.1.1466.115.121.1.40
+#Treat OIDs as case insensitive strings (as otherwise ldap class and
+#attribute names, declared at OIDs fail
+1.3.6.1.4.1.1466.115.121.1.38:1.3.6.1.4.1.1466.115.121.1.44
+#Treat Object(DN-Binary) as a binary blob
+1.2.840.113556.1.4.903:1.3.6.1.4.1.1466.115.121.1.40
+#Treat Object(DN-String) as a binary blob
+1.2.840.113556.1.4.904:1.3.6.1.4.1.1466.115.121.1.40
+#Treat UTC-Time as GeneralizedTime
+1.3.6.1.4.1.1466.115.121.1.53:1.3.6.1.4.1.1466.115.121.1.24
diff --git a/source4/setup/schema.ldif b/source4/setup/schema.ldif
new file mode 100644
index 0000000000..56eb7ce0c0
--- /dev/null
+++ b/source4/setup/schema.ldif
@@ -0,0 +1,10375 @@
+dn: CN=SD-Rights-Effective,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1304
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: SD-Rights-Effective
+adminDescription: SD-Rights-Effective
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: sDRightsEffective
+schemaIDGUID: c3dbafa6-33df-11d2-98b2-0000f87a57d4
+attributeSecurityGUID: 59ba2f42-79a2-11d0-9020-00c04fc2d3cf
+systemOnly: FALSE
+systemFlags: 134217748
+
+dn: CN=ms-Exch-Owner-BL,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.104
+attributeSyntax: 2.5.5.1
+isSingleValued: FALSE
+linkID: 45
+showInAdvancedViewOnly: TRUE
+adminDisplayName: ms-Exch-Owner-BL
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: ms-Exch-Owner-BL
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: ownerBL
+schemaIDGUID: bf9679f4-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+systemFlags: 17
+
+dn: CN=Is-Member-Of-DL,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.102
+attributeSyntax: 2.5.5.1
+isSingleValued: FALSE
+mAPIID: 32776
+linkID: 3
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Is-Member-Of-DL
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Is-Member-Of-DL
+oMSyntax: 127
+searchFlags: 16
+lDAPDisplayName: memberOf
+schemaIDGUID: bf967991-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: bc0ac240-79a9-11d0-9020-00c04fc2d4cf
+systemOnly: TRUE
+systemFlags: 17
+
+dn: CN=Search-Guide,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 2.5.4.14
+attributeSyntax: 2.5.5.10
+isSingleValued: FALSE
+mAPIID: 33070
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Search-Guide
+adminDescription: Search-Guide
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: searchGuide
+schemaIDGUID: bf967a2e-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=ms-DS-ReplicationEpoch,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1720
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: ms-DS-ReplicationEpoch
+adminDescription: ms-DS-ReplicationEpoch
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: msDS-ReplicationEpoch
+schemaIDGUID: 08e3aa79-eb1c-45b5-af7b-8f94246c8e41
+systemOnly: FALSE
+systemFlags: 17
+
+dn: CN=Auditing-Policy,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.202
+attributeSyntax: 2.5.5.10
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Auditing-Policy
+adminDescription: Auditing-Policy
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: auditingPolicy
+schemaIDGUID: 6da8a4fe-0e52-11d0-a286-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Phone-Fax-Other,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.646
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+rangeLower: 1
+rangeUpper: 64
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Phone-Fax-Other
+adminDescription: Phone-Fax-Other
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: otherFacsimileTelephoneNumber
+schemaIDGUID: 0296c11d-40da-11d1-a9c0-0000f80367c1
+attributeSecurityGUID: 77b5b886-944a-11d1-aebd-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Address,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.256
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 1
+rangeUpper: 1024
+mAPIID: 14889
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Address
+adminDescription: Address
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: streetAddress
+schemaIDGUID: f0f8ff84-1191-11d0-a060-00aa006c33ed
+attributeSecurityGUID: 77b5b886-944a-11d1-aebd-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Security-Identifier,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.121
+attributeSyntax: 2.5.5.17
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Security-Identifier
+adminDescription: Security-Identifier
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: securityIdentifier
+schemaIDGUID: bf967a2f-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=ms-DS-KeyVersionNumber,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1782
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: FALSE
+adminDisplayName: ms-DS-KeyVersionNumber
+adminDescription: The Kerberos version number of the current key for this account. This is a constructed attribute.
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: msDS-KeyVersionNumber
+schemaIDGUID: c523e9c0-33b5-4ac8-8923-b57b927f42f6
+systemOnly: TRUE
+systemFlags: 20
+
+dn: CN=Account-Name-History,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1307
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Account-Name-History
+adminDescription: Account-Name-History
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: accountNameHistory
+schemaIDGUID: 031952ec-3b72-11d2-90cc-00c04fd91ab1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=preferredLanguage,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 2.16.840.1.113730.3.1.39
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+showInAdvancedViewOnly: FALSE
+adminDisplayName: preferredLanguage
+adminDescription: The preferred written or spoken language for a person.
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: preferredLanguage
+schemaIDGUID: 856be0d0-18e7-46e1-8f5f-7ee4d9020e0d
+systemOnly: FALSE
+systemFlags: 0
+
+dn: CN=User-Shared-Folder-Other,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.752
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: User-Shared-Folder-Other
+adminDescription: User-Shared-Folder-Other
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: userSharedFolderOther
+schemaIDGUID: 9a9a0220-4a5b-11d1-a9c3-0000f80367c1
+attributeSecurityGUID: 77b5b886-944a-11d1-aebd-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=User-Shared-Folder,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.751
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: User-Shared-Folder
+adminDescription: User-Shared-Folder
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: userSharedFolder
+schemaIDGUID: 9a9a021f-4a5b-11d1-a9c3-0000f80367c1
+attributeSecurityGUID: 77b5b886-944a-11d1-aebd-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=MSMQ-Digests-Mig,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.966
+attributeSyntax: 2.5.5.10
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: MSMQ-Digests-Mig
+adminDescription: MSMQ-Digests-Mig
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: mSMQDigestsMig
+schemaIDGUID: 0f71d8e0-da3b-11d1-90a5-00c04fd91ab1
+systemOnly: FALSE
+systemFlags: 16
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=Domain-Identifier,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.755
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Domain-Identifier
+adminDescription: Domain-Identifier
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: domainIdentifier
+schemaIDGUID: 7f561278-5301-11d1-a9c5-0000f80367c1
+systemOnly: TRUE
+systemFlags: 16
+
+dn: CN=Legacy-Exchange-DN,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.655
+attributeSyntax: 2.5.5.4
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Legacy-Exchange-DN
+adminDescription: Legacy-Exchange-DN
+oMSyntax: 20
+searchFlags: 13
+lDAPDisplayName: legacyExchangeDN
+schemaIDGUID: 28630ebc-41d5-11d1-a9c1-0000f80367c1
+attributeSecurityGUID: e48d0154-bcf8-11d1-8702-00c04fb96050
+systemOnly: FALSE
+systemFlags: 16
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=Well-Known-Objects,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.618
+attributeSyntax: 2.5.5.7
+isSingleValued: FALSE
+rangeLower: 16
+rangeUpper: 16
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Well-Known-Objects
+oMObjectClass:: KoZIhvcUAQEBCw==
+adminDescription: Well-Known-Objects
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: wellKnownObjects
+schemaIDGUID: 05308983-7688-11d1-aded-00c04fd8d5cd
+systemOnly: TRUE
+systemFlags: 18
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=RDN,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 1
+rangeUpper: 255
+mAPIID: 33282
+showInAdvancedViewOnly: TRUE
+adminDisplayName: RDN
+adminDescription: RDN
+oMSyntax: 64
+searchFlags: 13
+lDAPDisplayName: name
+schemaIDGUID: bf967a0e-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: e48d0154-bcf8-11d1-8702-00c04fb96050
+systemOnly: TRUE
+systemFlags: 18
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=Non-Security-Member-BL,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.531
+attributeSyntax: 2.5.5.1
+isSingleValued: FALSE
+linkID: 51
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Non-Security-Member-BL
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Non-Security-Member-BL
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: nonSecurityMemberBL
+schemaIDGUID: 52458019-ca6a-11d0-afff-0000f80367c1
+systemOnly: TRUE
+systemFlags: 17
+
+dn: CN=ms-DS-Repl-Attribute-Meta-Data,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1707
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: ms-DS-Repl-Attribute-Meta-Data
+adminDescription: ms-DS-Repl-Attribute-Meta-Data
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: msDS-ReplAttributeMetaData
+schemaIDGUID: d7c53242-724e-4c39-9d4c-2df8c9d66c7a
+systemOnly: FALSE
+systemFlags: 20
+
+dn: CN=DN-Reference-Update,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1242
+attributeSyntax: 2.5.5.1
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: DN-Reference-Update
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: DN-Reference-Update
+oMSyntax: 127
+searchFlags: 8
+lDAPDisplayName: dNReferenceUpdate
+schemaIDGUID: 2df90d86-009f-11d2-aa4c-00c04fd7d83a
+systemOnly: TRUE
+systemFlags: 16
+
+dn: CN=GP-Options,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.892
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: GP-Options
+adminDescription: GP-Options
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: gPOptions
+schemaIDGUID: f30e3bbf-9ff0-11d1-b603-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=MS-DS-Per-User-Trust-Tombstones-Quota,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1790
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: MS-DS-Per-User-Trust-Tombstones-Quota
+adminDescription: Used to enforce a per-user quota for deleting Trusted-Domain objects when authorization is based on matching the user's SID to the value of MS-DS-Creator-SID on the Trusted-Domain object.
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: msDS-PerUserTrustTombstonesQuota
+schemaIDGUID: 8b70a6c6-50f9-4fa3-a71e-1ce03040449b
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Phone-Pager-Primary,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 0.9.2342.19200300.100.1.42
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 1
+rangeUpper: 64
+mAPIID: 14881
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Phone-Pager-Primary
+adminDescription: Phone-Pager-Primary
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: pager
+schemaIDGUID: f0f8ffa6-1191-11d0-a060-00aa006c33ed
+attributeSecurityGUID: 77b5b886-944a-11d1-aebd-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Site-GUID,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.362
+attributeSyntax: 2.5.5.10
+isSingleValued: TRUE
+rangeLower: 16
+rangeUpper: 16
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Site-GUID
+adminDescription: Site-GUID
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: siteGUID
+schemaIDGUID: 3e978924-8c01-11d0-afda-00c04fd930c9
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=ms-DS-Az-Script-Engine-Cache-Max,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1796
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+rangeLower: 0
+showInAdvancedViewOnly: TRUE
+adminDisplayName: MS-DS-Az-Script-Engine-Cache-Max
+adminDescription: Maximum number of scripts that are cached by the application
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: msDS-AzScriptEngineCacheMax
+schemaIDGUID: 2629f66a-1f95-4bf3-a296-8e9d7b9e30c8
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Token-Groups-No-GC-Acceptable,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1303
+attributeSyntax: 2.5.5.17
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Token-Groups-No-GC-Acceptable
+adminDescription: Token-Groups-No-GC-Acceptable
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: tokenGroupsNoGCAcceptable
+schemaIDGUID: 040fc392-33df-11d2-98b2-0000f87a57d4
+attributeSecurityGUID: 037088f8-0ae1-11d2-b422-00a0c968f939
+systemOnly: FALSE
+systemFlags: 134217748
+
+dn: CN=Token-Groups-Global-And-Universal,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1418
+attributeSyntax: 2.5.5.17
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Token-Groups-Global-And-Universal
+adminDescription: Token-Groups-Global-And-Universal
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: tokenGroupsGlobalAndUniversal
+schemaIDGUID: 46a9b11d-60ae-405a-b7e8-ff8a58d456d2
+attributeSecurityGUID: 037088f8-0ae1-11d2-b422-00a0c968f939
+systemOnly: FALSE
+systemFlags: 134217748
+
+dn: CN=Alt-Security-Identities,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.867
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Alt-Security-Identities
+adminDescription: Alt-Security-Identities
+oMSyntax: 64
+searchFlags: 1
+lDAPDisplayName: altSecurityIdentities
+schemaIDGUID: 00fbf30c-91fe-11d1-aebc-0000f80367c1
+attributeSecurityGUID: e48d0154-bcf8-11d1-8702-00c04fb96050
+systemOnly: FALSE
+systemFlags: 18
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=labeledURI,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.3.6.1.4.1.250.1.57
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+showInAdvancedViewOnly: FALSE
+adminDisplayName: labeledURI
+adminDescription: A Uniform Resource Identifier followed by a label. The label is used to describe the resource to which the URI points, and is intended as a friendly name fit for human consumption.
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: labeledURI
+schemaIDGUID: c569bb46-c680-44bc-a273-e6c227d71b45
+systemOnly: FALSE
+systemFlags: 0
+
+dn: CN=Pwd-Last-Set,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.96
+attributeSyntax: 2.5.5.16
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Pwd-Last-Set
+adminDescription: Pwd-Last-Set
+oMSyntax: 65
+searchFlags: 0
+lDAPDisplayName: pwdLastSet
+schemaIDGUID: bf967a0a-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: 4c164200-20c0-11d0-a768-00aa006e0529
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Object-Classes,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 2.5.21.6
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Object-Classes
+adminDescription: Object-Classes
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: objectClasses
+schemaIDGUID: 9a7ad94b-ca53-11d1-bbd0-0080c76670c0
+systemOnly: TRUE
+systemFlags: 134217748
+
+dn: CN=Trust-Attributes,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.470
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Trust-Attributes
+adminDescription: Trust-Attributes
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: trustAttributes
+schemaIDGUID: 80a67e5a-9f22-11d0-afdd-00c04fd930c9
+systemOnly: FALSE
+systemFlags: 16
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=ms-DS-Trust-Forest-Trust-Info,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1702
+attributeSyntax: 2.5.5.10
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: ms-DS-Trust-Forest-Trust-Info
+adminDescription: ms-DS-Trust-Forest-Trust-Info
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: msDS-TrustForestTrustInfo
+schemaIDGUID: 29cc866e-49d3-4969-942e-1dbc0925d183
+systemOnly: FALSE
+systemFlags: 16
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=Site-Object,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.512
+attributeSyntax: 2.5.5.1
+isSingleValued: TRUE
+linkID: 46
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Site-Object
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Site-Object
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: siteObject
+schemaIDGUID: 3e10944c-c354-11d0-aff8-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Is-Privilege-Holder,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.638
+attributeSyntax: 2.5.5.1
+isSingleValued: FALSE
+linkID: 71
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Is-Privilege-Holder
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Is-Privilege-Holder
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: isPrivilegeHolder
+schemaIDGUID: 19405b9c-3cfa-11d1-a9c0-0000f80367c1
+systemOnly: TRUE
+systemFlags: 17
+
+dn: CN=Dns-Root,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.28
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+rangeLower: 1
+rangeUpper: 255
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Dns-Root
+adminDescription: Dns-Root
+oMSyntax: 64
+searchFlags: 1
+lDAPDisplayName: dnsRoot
+schemaIDGUID: bf967959-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Modified-Count,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.168
+attributeSyntax: 2.5.5.16
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Modified-Count
+adminDescription: Modified-Count
+oMSyntax: 65
+searchFlags: 0
+lDAPDisplayName: modifiedCount
+schemaIDGUID: bf9679c5-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: b8119fd0-04f6-4762-ab7a-4986c76b3f9a
+systemOnly: FALSE
+systemFlags: 17
+
+dn: CN=International-ISDN-Number,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 2.5.4.25
+attributeSyntax: 2.5.5.6
+isSingleValued: FALSE
+rangeLower: 1
+rangeUpper: 16
+mAPIID: 32958
+showInAdvancedViewOnly: TRUE
+adminDisplayName: International-ISDN-Number
+adminDescription: International-ISDN-Number
+oMSyntax: 18
+searchFlags: 0
+lDAPDisplayName: internationalISDNNumber
+schemaIDGUID: bf96798d-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: 77b5b886-944a-11d1-aebd-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Business-Category,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 2.5.4.15
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+rangeLower: 1
+rangeUpper: 128
+mAPIID: 32855
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Business-Category
+adminDescription: Business-Category
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: businessCategory
+schemaIDGUID: bf967931-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=houseIdentifier,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 2.5.4.51
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+rangeLower: 1
+rangeUpper: 32768
+showInAdvancedViewOnly: TRUE
+adminDisplayName: houseIdentifier
+adminDescription: The houseIdentifier attribute type specifies a linguistic construct used to identify a particular building, for example a house number or house name relative to a street, avenue, town or city, etc.
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: houseIdentifier
+schemaIDGUID: a45398b7-c44a-4eb6-82d3-13c10946dbfe
+systemOnly: FALSE
+
+dn: CN=Other-Name,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 2.16.840.1.113730.3.1.34
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 0
+rangeUpper: 64
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Other-Name
+adminDescription: Other-Name
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: middleName
+schemaIDGUID: bf9679f2-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Attribute-ID,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.30
+attributeSyntax: 2.5.5.2
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Attribute-ID
+adminDescription: Attribute-ID
+oMSyntax: 6
+searchFlags: 8
+lDAPDisplayName: attributeID
+schemaIDGUID: bf967922-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+systemFlags: 16
+
+dn: CN=Repl-Topology-Stay-Of-Execution,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.677
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Repl-Topology-Stay-Of-Execution
+adminDescription: Repl-Topology-Stay-Of-Execution
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: replTopologyStayOfExecution
+schemaIDGUID: 7bfdcb83-4807-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Netboot-GUID,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.359
+attributeSyntax: 2.5.5.10
+isSingleValued: TRUE
+rangeLower: 16
+rangeUpper: 16
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Netboot-GUID
+adminDescription: Netboot-GUID
+oMSyntax: 4
+searchFlags: 1
+lDAPDisplayName: netbootGUID
+schemaIDGUID: 3e978921-8c01-11d0-afda-00c04fd930c9
+systemOnly: FALSE
+systemFlags: 16
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=RDN-Att-ID,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.26
+attributeSyntax: 2.5.5.2
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: RDN-Att-ID
+adminDescription: RDN-Att-ID
+oMSyntax: 6
+searchFlags: 0
+lDAPDisplayName: rDNAttID
+schemaIDGUID: bf967a0f-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+systemFlags: 16
+
+dn: CN=May-Contain,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.25
+attributeSyntax: 2.5.5.2
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: May-Contain
+adminDescription: May-Contain
+oMSyntax: 6
+searchFlags: 0
+lDAPDisplayName: mayContain
+schemaIDGUID: bf9679bf-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Trust-Auth-Outgoing,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.135
+attributeSyntax: 2.5.5.10
+isSingleValued: TRUE
+rangeLower: 0
+rangeUpper: 32767
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Trust-Auth-Outgoing
+adminDescription: Trust-Auth-Outgoing
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: trustAuthOutgoing
+schemaIDGUID: bf967a5f-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=GPC-WQL-Filter,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1694
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: GPC-WQL-Filter
+adminDescription: GPC-WQL-Filter
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: gPCWQLFilter
+schemaIDGUID: 7bd4c7a6-1add-4436-8c04-3999a880154c
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Server-Reference-BL,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.516
+attributeSyntax: 2.5.5.1
+isSingleValued: FALSE
+linkID: 95
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Server-Reference-BL
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Server-Reference-BL
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: serverReferenceBL
+schemaIDGUID: 26d9736e-6070-11d1-a9c6-0000f80367c1
+systemOnly: TRUE
+systemFlags: 17
+
+dn: CN=Create-Time-Stamp,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 2.5.18.1
+attributeSyntax: 2.5.5.11
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Create-Time-Stamp
+adminDescription: Create-Time-Stamp
+oMSyntax: 24
+searchFlags: 0
+lDAPDisplayName: createTimeStamp
+schemaIDGUID: 2df90d73-009f-11d2-aa4c-00c04fd7d83a
+systemOnly: TRUE
+systemFlags: 134217748
+
+dn: CN=Attribute-Display-Names,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.748
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Attribute-Display-Names
+adminDescription: Attribute-Display-Names
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: attributeDisplayNames
+schemaIDGUID: cb843f80-48d9-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Admin-Context-Menu,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.614
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Admin-Context-Menu
+adminDescription: Admin-Context-Menu
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: adminContextMenu
+schemaIDGUID: 553fd038-f32e-11d0-b0bc-00c04fd8dca6
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=LSA-Modified-Count,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.67
+attributeSyntax: 2.5.5.16
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: LSA-Modified-Count
+adminDescription: LSA-Modified-Count
+oMSyntax: 65
+searchFlags: 0
+lDAPDisplayName: lSAModifiedCount
+schemaIDGUID: bf9679ae-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=LSA-Creation-Time,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.66
+attributeSyntax: 2.5.5.16
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: LSA-Creation-Time
+adminDescription: LSA-Creation-Time
+oMSyntax: 65
+searchFlags: 0
+lDAPDisplayName: lSACreationTime
+schemaIDGUID: bf9679ad-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Server-State,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.154
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Server-State
+adminDescription: Server-State
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: serverState
+schemaIDGUID: bf967a34-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: b8119fd0-04f6-4762-ab7a-4986c76b3f9a
+systemOnly: FALSE
+systemFlags: 17
+
+dn: CN=LDAP-Display-Name,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.460
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 1
+rangeUpper: 256
+mAPIID: 33137
+showInAdvancedViewOnly: TRUE
+adminDisplayName: LDAP-Display-Name
+adminDescription: LDAP-Display-Name
+oMSyntax: 64
+searchFlags: 9
+lDAPDisplayName: lDAPDisplayName
+schemaIDGUID: bf96799a-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=Supplemental-Credentials,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.125
+attributeSyntax: 2.5.5.10
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Supplemental-Credentials
+adminDescription: Supplemental-Credentials
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: supplementalCredentials
+schemaIDGUID: bf967a3f-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=msNPSavedCallingStationID,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1130
+attributeSyntax: 2.5.5.5
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: msNPSavedCallingStationID
+adminDescription: msNPSavedCallingStationID
+oMSyntax: 22
+searchFlags: 0
+lDAPDisplayName: msNPSavedCallingStationID
+schemaIDGUID: db0c908e-c1f2-11d1-bbc5-0080c76670c0
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Flags,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.38
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Flags
+adminDescription: Flags
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: flags
+schemaIDGUID: bf967976-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=Create-Wizard-Ext,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.812
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Create-Wizard-Ext
+adminDescription: Create-Wizard-Ext
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: createWizardExt
+schemaIDGUID: 2b09958b-8931-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=DMD-Location,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.36
+attributeSyntax: 2.5.5.1
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: DMD-Location
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: DMD-Location
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: dMDLocation
+schemaIDGUID: f0f8ff8b-1191-11d0-a060-00aa006c33ed
+systemOnly: TRUE
+systemFlags: 16
+
+dn: CN=ms-Exch-House-Identifier,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.596
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 1
+rangeUpper: 128
+mAPIID: 35924
+adminDisplayName: ms-Exch-House-Identifier
+adminDescription: ms-Exch-House-Identifier
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: msExchHouseIdentifier
+schemaIDGUID: a8df7407-c5ea-11d1-bbcb-0080c76670c0
+
+dn: CN=Phone-Mobile-Other,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.647
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+rangeLower: 1
+rangeUpper: 64
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Phone-Mobile-Other
+adminDescription: Phone-Mobile-Other
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: otherMobile
+schemaIDGUID: 0296c11e-40da-11d1-a9c0-0000f80367c1
+attributeSecurityGUID: 77b5b886-944a-11d1-aebd-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Generation-Qualifier,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 2.5.4.44
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 1
+rangeUpper: 64
+mAPIID: 35923
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Generation-Qualifier
+adminDescription: Generation-Qualifier
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: generationQualifier
+schemaIDGUID: 16775804-47f3-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Attribute-Syntax,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.32
+attributeSyntax: 2.5.5.2
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Attribute-Syntax
+adminDescription: Attribute-Syntax
+oMSyntax: 6
+searchFlags: 8
+lDAPDisplayName: attributeSyntax
+schemaIDGUID: bf967925-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+systemFlags: 16
+
+dn: CN=Attribute-Security-GUID,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.149
+attributeSyntax: 2.5.5.10
+isSingleValued: TRUE
+rangeLower: 16
+rangeUpper: 16
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Attribute-Security-GUID
+adminDescription: Attribute-Security-GUID
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: attributeSecurityGUID
+schemaIDGUID: bf967924-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=DS-Heuristics,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.212
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: DS-Heuristics
+adminDescription: DS-Heuristics
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: dSHeuristics
+schemaIDGUID: f0f8ff86-1191-11d0-a060-00aa006c33ed
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Serial-Number,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 2.5.4.5
+attributeSyntax: 2.5.5.5
+isSingleValued: FALSE
+rangeLower: 1
+rangeUpper: 64
+mAPIID: 33072
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Serial-Number
+adminDescription: Serial-Number
+oMSyntax: 19
+searchFlags: 0
+lDAPDisplayName: serialNumber
+schemaIDGUID: bf967a32-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=ms-DS-Settings,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1697
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+rangeUpper: 1000000
+showInAdvancedViewOnly: TRUE
+adminDisplayName: ms-DS-Settings
+adminDescription: ms-DS-Settings
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: msDS-Settings
+schemaIDGUID: 0e1b47d7-40a3-4b48-8d1b-4cac0c1cdf21
+systemOnly: FALSE
+systemFlags: 0
+
+dn: CN=Operator-Count,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.144
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Operator-Count
+adminDescription: Operator-Count
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: operatorCount
+schemaIDGUID: bf9679ee-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=msRADIUSFramedIPAddress,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1153
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: msRADIUSFramedIPAddress
+adminDescription: msRADIUSFramedIPAddress
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: msRADIUSFramedIPAddress
+schemaIDGUID: db0c90a4-c1f2-11d1-bbc5-0080c76670c0
+attributeSecurityGUID: 037088f8-0ae1-11d2-b422-00a0c968f939
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Home-Drive,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.45
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Home-Drive
+adminDescription: Home-Drive
+oMSyntax: 64
+searchFlags: 16
+lDAPDisplayName: homeDrive
+schemaIDGUID: bf967986-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: 5f202010-79a5-11d0-9020-00c04fc2d4cf
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Attribute-Types,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 2.5.21.5
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Attribute-Types
+adminDescription: Attribute-Types
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: attributeTypes
+schemaIDGUID: 9a7ad944-ca53-11d1-bbd0-0080c76670c0
+systemOnly: TRUE
+systemFlags: 134217748
+
+dn: CN=Initial-Auth-Outgoing,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.540
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Initial-Auth-Outgoing
+adminDescription: Initial-Auth-Outgoing
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: initialAuthOutgoing
+schemaIDGUID: 52458024-ca6a-11d0-afff-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Version-Number,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.141
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Version-Number
+adminDescription: Version-Number
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: versionNumber
+schemaIDGUID: bf967a76-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=Object-Class,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 2.5.4.0
+attributeSyntax: 2.5.5.2
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Object-Class
+adminDescription: Object-Class
+oMSyntax: 6
+searchFlags: 8
+lDAPDisplayName: objectClass
+schemaIDGUID: bf9679e5-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: e48d0154-bcf8-11d1-8702-00c04fb96050
+systemOnly: TRUE
+systemFlags: 18
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=Possible-Inferiors,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.915
+attributeSyntax: 2.5.5.2
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Possible-Inferiors
+adminDescription: Possible-Inferiors
+oMSyntax: 6
+searchFlags: 0
+lDAPDisplayName: possibleInferiors
+schemaIDGUID: 9a7ad94c-ca53-11d1-bbd0-0080c76670c0
+systemOnly: TRUE
+systemFlags: 134217748
+
+dn: CN=ms-DS-Approx-Immed-Subordinates,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1669
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: ms-DS-Approx-Immed-Subordinates
+adminDescription: ms-DS-Approx-Immed-Subordinates
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: msDS-Approx-Immed-Subordinates
+schemaIDGUID: e185d243-f6ce-4adb-b496-b0c005d7823c
+attributeSecurityGUID: e48d0154-bcf8-11d1-8702-00c04fb96050
+systemOnly: TRUE
+systemFlags: 20
+
+dn: CN=ms-DS-Replication-Notify-Subsequent-DSA-Delay,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1664
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: ms-DS-Replication-Notify-Subsequent-DSA-Delay
+adminDescription: This attribute controls the delay between notification of each subsequent replica partner for an NC.
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: msDS-Replication-Notify-Subsequent-DSA-Delay
+schemaIDGUID: d63db385-dd92-4b52-b1d8-0d3ecc0e86b6
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Create-Dialog,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.810
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Create-Dialog
+adminDescription: Create-Dialog
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: createDialog
+schemaIDGUID: 2b09958a-8931-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Query-Policy-Object,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.607
+attributeSyntax: 2.5.5.1
+isSingleValued: TRUE
+linkID: 68
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Query-Policy-Object
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Query-Policy-Object
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: queryPolicyObject
+schemaIDGUID: e1aea403-cd5b-11d0-afff-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=FRS-Root-Path,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.487
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 0
+rangeUpper: 2048
+showInAdvancedViewOnly: TRUE
+adminDisplayName: FRS-Root-Path
+adminDescription: FRS-Root-Path
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: fRSRootPath
+schemaIDGUID: 1be8f174-a9ff-11d0-afe2-00c04fd930c9
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Organizational-Unit-Name,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 2.5.4.11
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+rangeLower: 1
+rangeUpper: 64
+mAPIID: 33026
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Organizational-Unit-Name
+adminDescription: Organizational-Unit-Name
+oMSyntax: 64
+searchFlags: 1
+lDAPDisplayName: ou
+schemaIDGUID: bf9679f0-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: e48d0154-bcf8-11d1-8702-00c04fb96050
+systemOnly: FALSE
+systemFlags: 18
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=Telex-Number,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 2.5.4.21
+attributeSyntax: 2.5.5.10
+isSingleValued: FALSE
+rangeLower: 1
+rangeUpper: 32
+mAPIID: 14892
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Telex-Number
+adminDescription: Telex-Number
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: telexNumber
+schemaIDGUID: bf967a4b-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: 77b5b886-944a-11d1-aebd-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Address-Home,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.617
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 1
+rangeUpper: 4096
+mAPIID: 14941
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Address-Home
+adminDescription: Address-Home
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: homePostalAddress
+schemaIDGUID: 16775781-47f3-11d1-a9c3-0000f80367c1
+attributeSecurityGUID: 77b5b886-944a-11d1-aebd-0000f80367c1
+systemOnly: FALSE
+systemFlags: 0
+
+dn: CN=Assistant,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.652
+attributeSyntax: 2.5.5.1
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Assistant
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Assistant
+oMSyntax: 127
+searchFlags: 16
+lDAPDisplayName: assistant
+schemaIDGUID: 0296c11c-40da-11d1-a9c0-0000f80367c1
+attributeSecurityGUID: 77b5b886-944a-11d1-aebd-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Netboot-Machine-File-Path,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.361
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Netboot-Machine-File-Path
+adminDescription: Netboot-Machine-File-Path
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: netbootMachineFilePath
+schemaIDGUID: 3e978923-8c01-11d0-afda-00c04fd930c9
+systemOnly: FALSE
+systemFlags: 16
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=x500uniqueIdentifier,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 2.5.4.45
+attributeSyntax: 2.5.5.10
+isSingleValued: FALSE
+showInAdvancedViewOnly: FALSE
+adminDisplayName: x500uniqueIdentifier
+adminDescription: Used to distinguish between objects when a distinguished name has been reused. This is a different attribute type from both the "uid" and "uniqueIdentifier" types.
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: x500uniqueIdentifier
+schemaIDGUID: d07da11f-8a3d-42b6-b0aa-76c962be719a
+systemOnly: FALSE
+systemFlags: 0
+
+dn: CN=DBCS-Pwd,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.55
+attributeSyntax: 2.5.5.10
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: DBCS-Pwd
+adminDescription: DBCS-Pwd
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: dBCSPwd
+schemaIDGUID: bf96799c-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Prefix-Map,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.538
+attributeSyntax: 2.5.5.10
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Prefix-Map
+adminDescription: Prefix-Map
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: prefixMap
+schemaIDGUID: 52458022-ca6a-11d0-afff-0000f80367c1
+systemOnly: TRUE
+systemFlags: 17
+
+dn: CN=ms-DS-Members-For-Az-Role-BL,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1807
+attributeSyntax: 2.5.5.1
+isSingleValued: FALSE
+linkID: 2017
+showInAdvancedViewOnly: TRUE
+adminDisplayName: MS-DS-Members-For-Az-Role-BL
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Back-link from member application group or user to Az-Role object(s) linking to it
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: msDS-MembersForAzRoleBL
+schemaIDGUID: ececcd20-a7e0-4688-9ccf-02ece5e287f5
+systemOnly: TRUE
+systemFlags: 17
+
+dn: CN=Last-Known-Parent,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.781
+attributeSyntax: 2.5.5.1
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Last-Known-Parent
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Last-Known-Parent
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: lastKnownParent
+schemaIDGUID: 52ab8670-5709-11d1-a9c6-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=FSMO-Role-Owner,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.369
+attributeSyntax: 2.5.5.1
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: FSMO-Role-Owner
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: FSMO-Role-Owner
+oMSyntax: 127
+searchFlags: 1
+lDAPDisplayName: fSMORoleOwner
+schemaIDGUID: 66171887-8f3c-11d0-afda-00c04fd930c9
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Retired-Repl-DSA-Signatures,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.673
+attributeSyntax: 2.5.5.10
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Retired-Repl-DSA-Signatures
+adminDescription: Retired-Repl-DSA-Signatures
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: retiredReplDSASignatures
+schemaIDGUID: 7bfdcb7f-4807-11d1-a9c3-0000f80367c1
+systemOnly: TRUE
+systemFlags: 16
+
+dn: CN=Network-Address,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.459
+attributeSyntax: 2.5.5.4
+isSingleValued: FALSE
+rangeLower: 0
+rangeUpper: 256
+mAPIID: 33136
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Network-Address
+adminDescription: Network-Address
+oMSyntax: 20
+searchFlags: 0
+lDAPDisplayName: networkAddress
+schemaIDGUID: bf9679d9-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+
+dn: CN=Schema-Version,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.471
+attributeSyntax: 2.5.5.9
+isSingleValued: FALSE
+mAPIID: 33148
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Schema-Version
+adminDescription: Schema-Version
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: schemaVersion
+schemaIDGUID: bf967a2c-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Poss-Superiors,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.8
+attributeSyntax: 2.5.5.2
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Poss-Superiors
+adminDescription: Poss-Superiors
+oMSyntax: 6
+searchFlags: 0
+lDAPDisplayName: possSuperiors
+schemaIDGUID: bf9679fa-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=Default-Security-Descriptor,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.224
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 0
+rangeUpper: 32767
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Default-Security-Descriptor
+adminDescription: Default-Security-Descriptor
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: defaultSecurityDescriptor
+schemaIDGUID: 807a6d30-1669-11d0-a064-00aa006c33ed
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=User-SMIME-Certificate,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 2.16.840.1.113730.3.140
+attributeSyntax: 2.5.5.10
+isSingleValued: FALSE
+rangeUpper: 32768
+mAPIID: 14960
+showInAdvancedViewOnly: TRUE
+adminDisplayName: User-SMIME-Certificate
+adminDescription: User-SMIME-Certificate
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: userSMIMECertificate
+schemaIDGUID: e16a9db2-403c-11d1-a9c0-0000f80367c1
+attributeSecurityGUID: 77b5b886-944a-11d1-aebd-0000f80367c1
+systemOnly: FALSE
+systemFlags: 0
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=userPKCS12,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 2.16.840.1.113730.3.1.216
+attributeSyntax: 2.5.5.10
+isSingleValued: FALSE
+showInAdvancedViewOnly: FALSE
+adminDisplayName: userPKCS12
+adminDescription: PKCS #12 PFX PDU for exchange of personal identity information.
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: userPKCS12
+schemaIDGUID: 23998ab5-70f8-4007-a4c1-a84a38311f9a
+systemOnly: FALSE
+systemFlags: 0
+
+dn: CN=User-Account-Control,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.8
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: User-Account-Control
+adminDescription: User-Account-Control
+oMSyntax: 2
+searchFlags: 25
+lDAPDisplayName: userAccountControl
+schemaIDGUID: bf967a68-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: 4c164200-20c0-11d0-a768-00aa006e0529
+systemOnly: FALSE
+systemFlags: 18
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=Terminal-Server,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.885
+attributeSyntax: 2.5.5.10
+isSingleValued: TRUE
+rangeUpper: 20480
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Terminal-Server
+adminDescription: Terminal-Server
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: terminalServer
+schemaIDGUID: 6db69a1c-9422-11d1-aebd-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Account-Expires,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.159
+attributeSyntax: 2.5.5.16
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Account-Expires
+adminDescription: Account-Expires
+oMSyntax: 65
+searchFlags: 16
+lDAPDisplayName: accountExpires
+schemaIDGUID: bf967915-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: 4c164200-20c0-11d0-a768-00aa006e0529
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Group-Type,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.750
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Group-Type
+adminDescription: Group-Type
+oMSyntax: 2
+searchFlags: 9
+lDAPDisplayName: groupType
+schemaIDGUID: 9a9a021e-4a5b-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+systemFlags: 18
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=NT-Group-Members,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.89
+attributeSyntax: 2.5.5.10
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: NT-Group-Members
+adminDescription: NT-Group-Members
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: nTGroupMembers
+schemaIDGUID: bf9679df-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=WWW-Page-Other,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.749
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+mAPIID: 33141
+showInAdvancedViewOnly: TRUE
+adminDisplayName: WWW-Page-Other
+adminDescription: WWW-Page-Other
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: url
+schemaIDGUID: 9a9a0221-4a5b-11d1-a9c3-0000f80367c1
+attributeSecurityGUID: e45795b3-9455-11d1-aebd-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Revision,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.145
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Revision
+adminDescription: Revision
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: revision
+schemaIDGUID: bf967a21-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Object-Version,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.76
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+mAPIID: 33015
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Object-Version
+adminDescription: Object-Version
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: objectVersion
+schemaIDGUID: 16775848-47f3-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=ms-DS-NC-Repl-Inbound-Neighbors,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1705
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: ms-DS-NC-Repl-Inbound-Neighbors
+adminDescription: ms-DS-NC-Repl-Inbound-Neighbors
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: msDS-NCReplInboundNeighbors
+schemaIDGUID: 9edba85a-3e9e-431b-9b1a-a5b6e9eda796
+systemOnly: FALSE
+systemFlags: 20
+
+dn: CN=ms-COM-UserLink,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1425
+attributeSyntax: 2.5.5.1
+isSingleValued: FALSE
+linkID: 1049
+showInAdvancedViewOnly: TRUE
+adminDisplayName: ms-COM-UserLink
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Link from a PartitionSet to a User. Default = adminDisplayName
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: msCOM-UserLink
+schemaIDGUID: 9e6f3a4d-242c-4f37-b068-36b57f9fc852
+systemOnly: TRUE
+systemFlags: 17
+
+dn: CN=Mastered-By,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1409
+attributeSyntax: 2.5.5.1
+isSingleValued: FALSE
+linkID: 77
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Mastered-By
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Mastered-By
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: masteredBy
+schemaIDGUID: e48e64e0-12c9-11d3-9102-00c04fd91ab1
+systemOnly: TRUE
+systemFlags: 17
+
+dn: CN=Canonical-Name,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.916
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Canonical-Name
+adminDescription: Canonical-Name
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: canonicalName
+schemaIDGUID: 9a7ad945-ca53-11d1-bbd0-0080c76670c0
+systemOnly: TRUE
+systemFlags: 134217748
+
+dn: CN=ms-DS-NC-Replica-Locations,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1661
+attributeSyntax: 2.5.5.1
+isSingleValued: FALSE
+linkID: 1044
+showInAdvancedViewOnly: TRUE
+adminDisplayName: ms-DS-NC-Replica-Locations
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: This is a list of servers that are the replica set for the corresponding Non-Domain Naming Context.
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: msDS-NC-Replica-Locations
+schemaIDGUID: 97de9615-b537-46bc-ac0f-10720f3909f3
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=ms-DS-UpdateScript,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1721
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: ms-DS-UpdateScript
+adminDescription: ms-DS-UpdateScript
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: msDS-UpdateScript
+schemaIDGUID: 146eb639-bb9f-4fc1-a825-e29e00c77920
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Next-Rid,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.88
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Next-Rid
+adminDescription: Next-Rid
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: nextRid
+schemaIDGUID: bf9679db-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=X121-Address,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 2.5.4.24
+attributeSyntax: 2.5.5.6
+isSingleValued: FALSE
+rangeLower: 1
+rangeUpper: 15
+mAPIID: 33112
+showInAdvancedViewOnly: TRUE
+adminDisplayName: X121-Address
+adminDescription: X121-Address
+oMSyntax: 18
+searchFlags: 0
+lDAPDisplayName: x121Address
+schemaIDGUID: bf967a7b-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: 77b5b886-944a-11d1-aebd-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=User-Password,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 2.5.4.35
+attributeSyntax: 2.5.5.10
+isSingleValued: FALSE
+rangeLower: 1
+rangeUpper: 128
+mAPIID: 33107
+showInAdvancedViewOnly: TRUE
+adminDisplayName: User-Password
+adminDescription: User-Password
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: userPassword
+schemaIDGUID: bf967a6e-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Telephone-Number,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 2.5.4.20
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 1
+rangeUpper: 64
+mAPIID: 14856
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Telephone-Number
+adminDescription: Telephone-Number
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: telephoneNumber
+schemaIDGUID: bf967a49-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: 77b5b886-944a-11d1-aebd-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=Department,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.141
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 1
+rangeUpper: 64
+mAPIID: 14872
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Department
+adminDescription: Department
+oMSyntax: 64
+searchFlags: 16
+lDAPDisplayName: department
+schemaIDGUID: bf96794f-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: e48d0154-bcf8-11d1-8702-00c04fb96050
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Is-Member-Of-Partial-Attribute-Set,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.639
+attributeSyntax: 2.5.5.8
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Is-Member-Of-Partial-Attribute-Set
+adminDescription: Is-Member-Of-Partial-Attribute-Set
+oMSyntax: 1
+searchFlags: 0
+lDAPDisplayName: isMemberOfPartialAttributeSet
+schemaIDGUID: 19405b9d-3cfa-11d1-a9c0-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Policy-Replication-Flags,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.633
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Policy-Replication-Flags
+adminDescription: Policy-Replication-Flags
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: policyReplicationFlags
+schemaIDGUID: 19405b96-3cfa-11d1-a9c0-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Ipsec-ISAKMP-Reference,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.626
+attributeSyntax: 2.5.5.1
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Ipsec-ISAKMP-Reference
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Ipsec-ISAKMP-Reference
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: ipsecISAKMPReference
+schemaIDGUID: b40ff820-427a-11d1-a9c2-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Application-Name,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.218
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 1
+rangeUpper: 64
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Application-Name
+adminDescription: Application-Name
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: applicationName
+schemaIDGUID: dd712226-10e4-11d0-a05f-00aa006c33ed
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=System-May-Contain,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.196
+attributeSyntax: 2.5.5.2
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: System-May-Contain
+adminDescription: System-May-Contain
+oMSyntax: 6
+searchFlags: 0
+lDAPDisplayName: systemMayContain
+schemaIDGUID: bf967a44-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+systemFlags: 16
+
+dn: CN=msRASSavedFramedRoute,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1191
+attributeSyntax: 2.5.5.5
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: msRASSavedFramedRoute
+adminDescription: msRASSavedFramedRoute
+oMSyntax: 22
+searchFlags: 0
+lDAPDisplayName: msRASSavedFramedRoute
+schemaIDGUID: db0c90c7-c1f2-11d1-bbc5-0080c76670c0
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=msRASSavedCallbackNumber,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1189
+attributeSyntax: 2.5.5.5
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: msRASSavedCallbackNumber
+adminDescription: msRASSavedCallbackNumber
+oMSyntax: 22
+searchFlags: 0
+lDAPDisplayName: msRASSavedCallbackNumber
+schemaIDGUID: db0c90c5-c1f2-11d1-bbc5-0080c76670c0
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Trust-Type,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.136
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Trust-Type
+adminDescription: Trust-Type
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: trustType
+schemaIDGUID: bf967a60-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=Domain-Replica,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.158
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 0
+rangeUpper: 32767
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Domain-Replica
+adminDescription: Domain-Replica
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: domainReplica
+schemaIDGUID: bf96795e-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: b8119fd0-04f6-4762-ab7a-4986c76b3f9a
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Personal-Title,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.615
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 1
+rangeUpper: 64
+mAPIID: 35947
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Personal-Title
+adminDescription: Personal-Title
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: personalTitle
+schemaIDGUID: 16775858-47f3-11d1-a9c3-0000f80367c1
+attributeSecurityGUID: 77b5b886-944a-11d1-aebd-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Other-Mailbox,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.651
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Other-Mailbox
+adminDescription: Other-Mailbox
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: otherMailbox
+schemaIDGUID: 0296c123-40da-11d1-a9c0-0000f80367c1
+attributeSecurityGUID: e48d0154-bcf8-11d1-8702-00c04fb96050
+systemOnly: FALSE
+
+dn: CN=E-mail-Addresses,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 0.9.2342.19200300.100.1.3
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 0
+rangeUpper: 256
+mAPIID: 14846
+showInAdvancedViewOnly: TRUE
+adminDisplayName: E-mail-Addresses
+adminDescription: E-mail-Addresses
+oMSyntax: 64
+searchFlags: 1
+lDAPDisplayName: mail
+schemaIDGUID: bf967961-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: e48d0154-bcf8-11d1-8702-00c04fb96050
+systemOnly: FALSE
+systemFlags: 16
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=OM-Syntax,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.231
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+mAPIID: 33022
+showInAdvancedViewOnly: TRUE
+adminDisplayName: OM-Syntax
+adminDescription: OM-Syntax
+oMSyntax: 2
+searchFlags: 8
+lDAPDisplayName: oMSyntax
+schemaIDGUID: bf9679ed-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+systemFlags: 16
+
+dn: CN=Is-Defunct,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.661
+attributeSyntax: 2.5.5.8
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Is-Defunct
+adminDescription: Is-Defunct
+oMSyntax: 1
+searchFlags: 0
+lDAPDisplayName: isDefunct
+schemaIDGUID: 28630ebe-41d5-11d1-a9c1-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=ms-DS-Other-Settings,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1621
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: ms-DS-Other-Settings
+adminDescription: ms-DS-Other-Settings
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: msDS-Other-Settings
+schemaIDGUID: 79d2f34c-9d7d-42bb-838f-866b3e4400e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Machine-Role,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.71
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Machine-Role
+adminDescription: Machine-Role
+oMSyntax: 10
+searchFlags: 0
+lDAPDisplayName: machineRole
+schemaIDGUID: bf9679b2-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=ms-DS-Az-Domain-Timeout,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1795
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+rangeLower: 0
+showInAdvancedViewOnly: TRUE
+adminDisplayName: MS-DS-Az-Domain-Timeout
+adminDescription: Time (in ms) after a domain is detected to be un-reachable, and before the DC is tried again
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: msDS-AzDomainTimeout
+schemaIDGUID: 6448f56a-ca70-4e2e-b0af-d20e4ce653d0
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=System-Auxiliary-Class,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.198
+attributeSyntax: 2.5.5.2
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: System-Auxiliary-Class
+adminDescription: System-Auxiliary-Class
+oMSyntax: 6
+searchFlags: 0
+lDAPDisplayName: systemAuxiliaryClass
+schemaIDGUID: bf967a43-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+systemFlags: 16
+
+dn: CN=Primary-Group-ID,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.98
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Primary-Group-ID
+adminDescription: Primary-Group-ID
+oMSyntax: 2
+searchFlags: 17
+lDAPDisplayName: primaryGroupID
+schemaIDGUID: bf967a00-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: 59ba2f42-79a2-11d0-9020-00c04fc2d3cf
+systemOnly: FALSE
+systemFlags: 18
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=Lm-Pwd-History,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.160
+attributeSyntax: 2.5.5.10
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Lm-Pwd-History
+adminDescription: Lm-Pwd-History
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: lmPwdHistory
+schemaIDGUID: bf96799d-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Group-Membership-SAM,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.166
+attributeSyntax: 2.5.5.10
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Group-Membership-SAM
+adminDescription: Group-Membership-SAM
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: groupMembershipSAM
+schemaIDGUID: bf967980-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Trust-Partner,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.133
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 1
+rangeUpper: 1024
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Trust-Partner
+adminDescription: Trust-Partner
+oMSyntax: 64
+searchFlags: 1
+lDAPDisplayName: trustPartner
+schemaIDGUID: bf967a5d-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=Instance-Type,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.1
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+mAPIID: 32957
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Instance-Type
+adminDescription: Instance-Type
+oMSyntax: 2
+searchFlags: 8
+lDAPDisplayName: instanceType
+schemaIDGUID: bf96798c-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+systemFlags: 18
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=Treat-As-Leaf,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.806
+attributeSyntax: 2.5.5.8
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Treat-As-Leaf
+adminDescription: Treat-As-Leaf
+oMSyntax: 1
+searchFlags: 0
+lDAPDisplayName: treatAsLeaf
+schemaIDGUID: 8fd044e3-771f-11d1-aeae-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Admin-Property-Pages,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.562
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Admin-Property-Pages
+adminDescription: Admin-Property-Pages
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: adminPropertyPages
+schemaIDGUID: 52458038-ca6a-11d0-afff-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=ms-DS-Az-Scope-Name,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1799
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 0
+rangeUpper: 65536
+showInAdvancedViewOnly: TRUE
+adminDisplayName: MS-DS-Az-Scope-Name
+adminDescription: A string that uniquely identifies a scope object
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: msDS-AzScopeName
+schemaIDGUID: 515a6b06-2617-4173-8099-d5605df043c6
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=See-Also,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 2.5.4.34
+attributeSyntax: 2.5.5.1
+isSingleValued: FALSE
+mAPIID: 33071
+showInAdvancedViewOnly: TRUE
+adminDisplayName: See-Also
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: See-Also
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: seeAlso
+schemaIDGUID: bf967a31-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=LDAP-IPDeny-List,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.844
+attributeSyntax: 2.5.5.10
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: LDAP-IPDeny-List
+adminDescription: LDAP-IPDeny-List
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: lDAPIPDenyList
+schemaIDGUID: 7359a353-90f7-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=ms-DS-Retired-Repl-NC-Signatures,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1826
+attributeSyntax: 2.5.5.10
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: ms-DS-Retired-Repl-NC-Signatures
+adminDescription: Information about naming contexts that are no longer held on this computer
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: msDS-RetiredReplNCSignatures
+schemaIDGUID: d5b35506-19d6-4d26-9afb-11357ac99b5e
+systemOnly: TRUE
+systemFlags: 17
+
+dn: CN=Has-Master-NCs,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.14
+attributeSyntax: 2.5.5.1
+isSingleValued: FALSE
+mAPIID: 32950
+linkID: 76
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Has-Master-NCs
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Has-Master-NCs
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: hasMasterNCs
+schemaIDGUID: bf967982-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+systemFlags: 16
+
+dn: CN=Modified-Count-At-Last-Prom,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.81
+attributeSyntax: 2.5.5.16
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Modified-Count-At-Last-Prom
+adminDescription: Modified-Count-At-Last-Prom
+oMSyntax: 65
+searchFlags: 0
+lDAPDisplayName: modifiedCountAtLastProm
+schemaIDGUID: bf9679c6-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Min-Pwd-Age,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.78
+attributeSyntax: 2.5.5.16
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Min-Pwd-Age
+adminDescription: Min-Pwd-Age
+oMSyntax: 65
+searchFlags: 0
+lDAPDisplayName: minPwdAge
+schemaIDGUID: bf9679c2-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: c7407360-20bf-11d0-a768-00aa006e0529
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Force-Logoff,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.39
+attributeSyntax: 2.5.5.16
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Force-Logoff
+adminDescription: Force-Logoff
+oMSyntax: 65
+searchFlags: 0
+lDAPDisplayName: forceLogoff
+schemaIDGUID: bf967977-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: b8119fd0-04f6-4762-ab7a-4986c76b3f9a
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Move-Tree-State,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1305
+attributeSyntax: 2.5.5.10
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Move-Tree-State
+adminDescription: Move-Tree-State
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: moveTreeState
+schemaIDGUID: 1f2ac2c8-3b71-11d2-90cc-00c04fd91ab1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=ms-DS-Allowed-To-Delegate-To,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1787
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: ms-DS-Allowed-To-Delegate-To
+adminDescription: Allowed-To-Delegate-To contains a list of SPNs that are used for Constrained Delegation
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: msDS-AllowedToDelegateTo
+schemaIDGUID: 800d94d7-b7a1-42a1-b14d-7cae1423d07f
+attributeSecurityGUID: e48d0154-bcf8-11d1-8702-00c04fb96050
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=System-Only,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.170
+attributeSyntax: 2.5.5.8
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: System-Only
+adminDescription: System-Only
+oMSyntax: 1
+searchFlags: 0
+lDAPDisplayName: systemOnly
+schemaIDGUID: bf967a46-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+systemFlags: 16
+
+dn: CN=ms-DS-IntId,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1716
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: ms-DS-IntId
+adminDescription: ms-DS-IntId
+oMSyntax: 2
+searchFlags: 8
+lDAPDisplayName: msDS-IntId
+schemaIDGUID: bc60096a-1b47-4b30-8877-602c93f56532
+systemOnly: TRUE
+systemFlags: 16
+
+dn: CN=DNS-Host-Name,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.619
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 0
+rangeUpper: 2048
+showInAdvancedViewOnly: TRUE
+adminDisplayName: DNS-Host-Name
+adminDescription: DNS-Host-Name
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: dNSHostName
+schemaIDGUID: 72e39547-7b18-11d1-adef-00c04fd8d5cd
+attributeSecurityGUID: 72e39547-7b18-11d1-adef-00c04fd8d5cd
+systemOnly: FALSE
+systemFlags: 16
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=ms-DS-Az-Minor-Version,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1825
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+rangeLower: 0
+showInAdvancedViewOnly: TRUE
+adminDisplayName: MS-DS-Az-Minor-Version
+adminDescription: Minor version number for AzRoles
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: msDS-AzMinorVersion
+schemaIDGUID: ee85ed93-b209-4788-8165-e702f51bfbf3
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Bad-Password-Time,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.49
+attributeSyntax: 2.5.5.16
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Bad-Password-Time
+adminDescription: Bad-Password-Time
+oMSyntax: 65
+searchFlags: 0
+lDAPDisplayName: badPasswordTime
+schemaIDGUID: bf96792d-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 17
+
+dn: CN=Primary-Group-Token,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1412
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Primary-Group-Token
+adminDescription: Primary-Group-Token
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: primaryGroupToken
+schemaIDGUID: c0ed8738-7efd-4481-84d9-66d2db8be369
+systemOnly: TRUE
+systemFlags: 20
+
+dn: CN=USN-Intersite,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.469
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+mAPIID: 33146
+showInAdvancedViewOnly: TRUE
+adminDisplayName: USN-Intersite
+adminDescription: USN-Intersite
+oMSyntax: 2
+searchFlags: 1
+lDAPDisplayName: USNIntersite
+schemaIDGUID: a8df7498-c5ea-11d1-bbcb-0080c76670c0
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=FRS-Member-Reference-BL,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.876
+attributeSyntax: 2.5.5.1
+isSingleValued: FALSE
+linkID: 105
+showInAdvancedViewOnly: TRUE
+adminDisplayName: FRS-Member-Reference-BL
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: FRS-Member-Reference-BL
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: fRSMemberReferenceBL
+schemaIDGUID: 2a13257f-9373-11d1-aebc-0000f80367c1
+systemOnly: TRUE
+systemFlags: 17
+
+dn: CN=ms-DS-SD-Reference-Domain,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1711
+attributeSyntax: 2.5.5.1
+isSingleValued: TRUE
+linkID: 2000
+showInAdvancedViewOnly: TRUE
+adminDisplayName: ms-DS-SD-Reference-Domain
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: The domain to be used for default security descriptor translation for a Non-Domain Naming Context.
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: msDS-SDReferenceDomain
+schemaIDGUID: 4c51e316-f628-43a5-b06b-ffb695fcb4f3
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Ipsec-ID,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.621
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Ipsec-ID
+adminDescription: Ipsec-ID
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: ipsecID
+schemaIDGUID: b40ff81d-427a-11d1-a9c2-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=LDAP-Admin-Limits,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.843
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: LDAP-Admin-Limits
+adminDescription: LDAP-Admin-Limits
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: lDAPAdminLimits
+schemaIDGUID: 7359a352-90f7-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Last-Backup-Restoration-Time,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.519
+attributeSyntax: 2.5.5.16
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Last-Backup-Restoration-Time
+adminDescription: Last-Backup-Restoration-Time
+oMSyntax: 65
+searchFlags: 0
+lDAPDisplayName: lastBackupRestorationTime
+schemaIDGUID: 1fbb0be8-ba63-11d0-afef-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Tree-Name,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.660
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Tree-Name
+adminDescription: Tree-Name
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: treeName
+schemaIDGUID: 28630ebd-41d5-11d1-a9c1-0000f80367c1
+systemOnly: TRUE
+systemFlags: 16
+
+dn: CN=OEM-Information,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.151
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 0
+rangeUpper: 32767
+showInAdvancedViewOnly: TRUE
+adminDisplayName: OEM-Information
+adminDescription: OEM-Information
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: oEMInformation
+schemaIDGUID: bf9679ea-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: b8119fd0-04f6-4762-ab7a-4986c76b3f9a
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Given-Name,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 2.5.4.42
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 1
+rangeUpper: 64
+mAPIID: 14854
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Given-Name
+adminDescription: Given-Name
+oMSyntax: 64
+searchFlags: 5
+lDAPDisplayName: givenName
+schemaIDGUID: f0f8ff8e-1191-11d0-a060-00aa006c33ed
+attributeSecurityGUID: e48d0154-bcf8-11d1-8702-00c04fb96050
+systemOnly: FALSE
+systemFlags: 16
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=SPN-Mappings,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1347
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: SPN-Mappings
+adminDescription: SPN-Mappings
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: sPNMappings
+schemaIDGUID: 2ab0e76c-7041-11d2-9905-0000f87a57d4
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Operating-System-Version,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.364
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Operating-System-Version
+adminDescription: Operating-System-Version
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: operatingSystemVersion
+schemaIDGUID: 3e978926-8c01-11d0-afda-00c04fd930c9
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Notification-List,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.303
+attributeSyntax: 2.5.5.1
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Notification-List
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Notification-List
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: notificationList
+schemaIDGUID: 19195a56-6da0-11d0-afd3-00c04fd930c9
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Token-Groups,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1301
+attributeSyntax: 2.5.5.17
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Token-Groups
+adminDescription: Token-Groups
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: tokenGroups
+schemaIDGUID: b7c69e6d-2cc7-11d2-854e-00a0c983f608
+attributeSecurityGUID: 037088f8-0ae1-11d2-b422-00a0c968f939
+systemOnly: FALSE
+systemFlags: 134217748
+
+dn: CN=carLicense,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 2.16.840.1.113730.3.1.1
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+showInAdvancedViewOnly: FALSE
+adminDisplayName: carLicense
+adminDescription: Vehicle license or registration plate.
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: carLicense
+schemaIDGUID: d4159c92-957d-4a87-8a67-8d2934e01649
+systemOnly: FALSE
+systemFlags: 0
+
+dn: CN=Preferred-OU,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.97
+attributeSyntax: 2.5.5.1
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Preferred-OU
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Preferred-OU
+oMSyntax: 127
+searchFlags: 16
+lDAPDisplayName: preferredOU
+schemaIDGUID: bf9679ff-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=MS-DS-Creator-SID,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1410
+attributeSyntax: 2.5.5.17
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: MS-DS-Creator-SID
+adminDescription: MS-DS-Creator-SID
+oMSyntax: 4
+searchFlags: 1
+lDAPDisplayName: mS-DS-CreatorSID
+schemaIDGUID: c5e60132-1480-11d3-91c1-0000f87a57d4
+systemOnly: TRUE
+systemFlags: 16
+
+dn: CN=ms-DS-Non-Members,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1793
+attributeSyntax: 2.5.5.1
+isSingleValued: FALSE
+linkID: 2014
+showInAdvancedViewOnly: TRUE
+adminDisplayName: MS-DS-Non-Members
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: ms-DS-Non-Members
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: msDS-NonMembers
+schemaIDGUID: cafcb1de-f23c-46b5-adf7-1e64957bd5db
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=ms-DS-Tasks-For-Az-Role-BL,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1815
+attributeSyntax: 2.5.5.1
+isSingleValued: FALSE
+linkID: 2025
+showInAdvancedViewOnly: TRUE
+adminDisplayName: MS-DS-Tasks-For-Az-Role-BL
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Back-link from Az-Task to Az-Role object(s) linking to it
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: msDS-TasksForAzRoleBL
+schemaIDGUID: a0dcd536-5158-42fe-8c40-c00a7ad37959
+systemOnly: TRUE
+systemFlags: 17
+
+dn: CN=Extension-Name,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.227
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+rangeLower: 1
+rangeUpper: 255
+mAPIID: 32937
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Extension-Name
+adminDescription: Extension-Name
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: extensionName
+schemaIDGUID: bf967972-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=ms-DS-Replication-Notify-First-DSA-Delay,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1663
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: ms-DS-Replication-Notify-First-DSA-Delay
+adminDescription: This attribute controls the delay between changes to the DS, and notification of the first replica partner for an NC.
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: msDS-Replication-Notify-First-DSA-Delay
+schemaIDGUID: 85abd4f4-0a89-4e49-bdec-6f35bb2562ba
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Max-Pwd-Age,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.74
+attributeSyntax: 2.5.5.16
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Max-Pwd-Age
+adminDescription: Max-Pwd-Age
+oMSyntax: 65
+searchFlags: 0
+lDAPDisplayName: maxPwdAge
+schemaIDGUID: bf9679bb-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: c7407360-20bf-11d0-a768-00aa006e0529
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Phone-Ip-Other,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.722
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Phone-Ip-Other
+adminDescription: Phone-Ip-Other
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: otherIpPhone
+schemaIDGUID: 4d146e4b-48d4-11d1-a9c3-0000f80367c1
+attributeSecurityGUID: 77b5b886-944a-11d1-aebd-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=Ipsec-NFA-Reference,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.627
+attributeSyntax: 2.5.5.1
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Ipsec-NFA-Reference
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Ipsec-NFA-Reference
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: ipsecNFAReference
+schemaIDGUID: b40ff821-427a-11d1-a9c2-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=secretary,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 0.9.2342.19200300.100.1.21
+attributeSyntax: 2.5.5.1
+isSingleValued: FALSE
+showInAdvancedViewOnly: FALSE
+adminDisplayName: secretary
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Specifies the secretary of a person.
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: secretary
+schemaIDGUID: 01072d9a-98ad-4a53-9744-e83e287278fb
+systemOnly: FALSE
+systemFlags: 0
+
+dn: CN=User-Parameters,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.138
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 0
+rangeUpper: 32767
+showInAdvancedViewOnly: TRUE
+adminDisplayName: User-Parameters
+adminDescription: User-Parameters
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: userParameters
+schemaIDGUID: bf967a6d-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: 4c164200-20c0-11d0-a768-00aa006e0529
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Trust-Posix-Offset,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.134
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Trust-Posix-Offset
+adminDescription: Trust-Posix-Offset
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: trustPosixOffset
+schemaIDGUID: bf967a5e-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Bridgehead-Server-List-BL,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.820
+attributeSyntax: 2.5.5.1
+isSingleValued: FALSE
+linkID: 99
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Bridgehead-Server-List-BL
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Bridgehead-Server-List-BL
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: bridgeheadServerListBL
+schemaIDGUID: d50c2cdb-8951-11d1-aebc-0000f80367c1
+systemOnly: TRUE
+systemFlags: 17
+
+dn: CN=ms-DS-Az-Application-Data,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1819
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 0
+showInAdvancedViewOnly: TRUE
+adminDisplayName: MS-DS-Az-Application-Data
+adminDescription: A string that is used by individual applications to store whatever information they may need to
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: msDS-AzApplicationData
+schemaIDGUID: 503fc3e8-1cc6-461a-99a3-9eee04f402a7
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Pek-Key-Change-Interval,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.866
+attributeSyntax: 2.5.5.16
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Pek-Key-Change-Interval
+adminDescription: Pek-Key-Change-Interval
+oMSyntax: 65
+searchFlags: 0
+lDAPDisplayName: pekKeyChangeInterval
+schemaIDGUID: 07383084-91df-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Country-Name,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 2.5.4.6
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 1
+rangeUpper: 3
+mAPIID: 32873
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Country-Name
+adminDescription: Country-Name
+oMSyntax: 64
+searchFlags: 16
+lDAPDisplayName: c
+schemaIDGUID: bf967945-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: 77b5b886-944a-11d1-aebd-0000f80367c1
+systemOnly: FALSE
+systemFlags: 18
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=Destination-Indicator,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 2.5.4.27
+attributeSyntax: 2.5.5.5
+isSingleValued: FALSE
+rangeLower: 1
+rangeUpper: 128
+mAPIID: 32880
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Destination-Indicator
+adminDescription: Destination-Indicator
+oMSyntax: 19
+searchFlags: 0
+lDAPDisplayName: destinationIndicator
+schemaIDGUID: bf967951-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Country-Code,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.25
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+rangeLower: 0
+rangeUpper: 65535
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Country-Code
+adminDescription: Country-Code
+oMSyntax: 2
+searchFlags: 16
+lDAPDisplayName: countryCode
+schemaIDGUID: 5fd42471-1262-11d0-a060-00aa006c33ed
+attributeSecurityGUID: 59ba2f42-79a2-11d0-9020-00c04fc2d3cf
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Phone-Mobile-Primary,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 0.9.2342.19200300.100.1.41
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 1
+rangeUpper: 64
+mAPIID: 14876
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Phone-Mobile-Primary
+adminDescription: Phone-Mobile-Primary
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: mobile
+schemaIDGUID: f0f8ffa3-1191-11d0-a060-00aa006c33ed
+attributeSecurityGUID: 77b5b886-944a-11d1-aebd-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Schema-ID-GUID,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.148
+attributeSyntax: 2.5.5.10
+isSingleValued: TRUE
+rangeLower: 16
+rangeUpper: 16
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Schema-ID-GUID
+adminDescription: Schema-ID-GUID
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: schemaIDGUID
+schemaIDGUID: bf967923-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+systemFlags: 16
+
+dn: CN=RID-Set-References,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.669
+attributeSyntax: 2.5.5.1
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: RID-Set-References
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: RID-Set-References
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: rIDSetReferences
+schemaIDGUID: 7bfdcb7b-4807-11d1-a9c3-0000f80367c1
+systemOnly: TRUE
+systemFlags: 16
+
+dn: CN=Auxiliary-Class,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.351
+attributeSyntax: 2.5.5.2
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Auxiliary-Class
+adminDescription: Auxiliary-Class
+oMSyntax: 6
+searchFlags: 0
+lDAPDisplayName: auxiliaryClass
+schemaIDGUID: bf96792c-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=uid,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 0.9.2342.19200300.100.1.1
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+showInAdvancedViewOnly: FALSE
+adminDisplayName: uid
+adminDescription: A user ID.
+oMSyntax: 64
+searchFlags: 8
+lDAPDisplayName: uid
+schemaIDGUID: 0bb0fca0-1e89-429f-901a-1413894d9f59
+attributeSecurityGUID: 59ba2f42-79a2-11d0-9020-00c04fc2d3cf
+systemOnly: FALSE
+systemFlags: 0
+
+dn: CN=departmentNumber,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 2.16.840.1.113730.3.1.2
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+showInAdvancedViewOnly: FALSE
+adminDisplayName: departmentNumber
+adminDescription: Identifies a department within an organization.
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: departmentNumber
+schemaIDGUID: be9ef6ee-cbc7-4f22-b27b-96967e7ee585
+systemOnly: FALSE
+systemFlags: 0
+
+dn: CN=Additional-Trusted-Service-Names,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.889
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Additional-Trusted-Service-Names
+adminDescription: Additional-Trusted-Service-Names
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: additionalTrustedServiceNames
+schemaIDGUID: 032160be-9824-11d1-aec0-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=WWW-Home-Page,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.464
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 1
+rangeUpper: 2048
+showInAdvancedViewOnly: TRUE
+adminDisplayName: WWW-Home-Page
+adminDescription: WWW-Home-Page
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: wWWHomePage
+schemaIDGUID: bf967a7a-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: e45795b3-9455-11d1-aebd-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=USN-Source,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.896
+attributeSyntax: 2.5.5.16
+isSingleValued: TRUE
+mAPIID: 33111
+showInAdvancedViewOnly: TRUE
+adminDisplayName: USN-Source
+adminDescription: USN-Source
+oMSyntax: 65
+searchFlags: 0
+lDAPDisplayName: uSNSource
+schemaIDGUID: 167758ad-47f3-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=MS-DS-Consistency-Guid,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1360
+attributeSyntax: 2.5.5.10
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: MS-DS-Consistency-Guid
+adminDescription: MS-DS-Consistency-Guid
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: mS-DS-ConsistencyGuid
+schemaIDGUID: 23773dc2-b63a-11d2-90e1-00c04fd91ab1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Frs-Computer-Reference-BL,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.870
+attributeSyntax: 2.5.5.1
+isSingleValued: FALSE
+linkID: 103
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Frs-Computer-Reference-BL
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Frs-Computer-Reference-BL
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: frsComputerReferenceBL
+schemaIDGUID: 2a132579-9373-11d1-aebc-0000f80367c1
+systemOnly: TRUE
+systemFlags: 17
+
+dn: CN=Allowed-Attributes,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.913
+attributeSyntax: 2.5.5.2
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Allowed-Attributes
+adminDescription: Allowed-Attributes
+oMSyntax: 6
+searchFlags: 0
+lDAPDisplayName: allowedAttributes
+schemaIDGUID: 9a7ad940-ca53-11d1-bbd0-0080c76670c0
+attributeSecurityGUID: e48d0154-bcf8-11d1-8702-00c04fb96050
+systemOnly: TRUE
+systemFlags: 134217748
+
+dn: CN=ms-DS-Az-Application-Name,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1798
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 0
+rangeUpper: 512
+showInAdvancedViewOnly: TRUE
+adminDisplayName: MS-DS-Az-Application-Name
+adminDescription: A string that uniquely identifies an application object
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: msDS-AzApplicationName
+schemaIDGUID: db5b0728-6208-4876-83b7-95d3e5695275
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=UPN-Suffixes,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.890
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: UPN-Suffixes
+adminDescription: UPN-Suffixes
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: uPNSuffixes
+schemaIDGUID: 032160bf-9824-11d1-aec0-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=MS-DS-Per-User-Trust-Quota,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1788
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: MS-DS-Per-User-Trust-Quota
+adminDescription: Used to enforce a per-user quota for creating Trusted-Domain objects authorized by the control access right, "Create inbound Forest trust". This attribute limits the number of Trusted-Domain objects that can be created by a single non-admin user in the domain.
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: msDS-PerUserTrustQuota
+schemaIDGUID: d161adf0-ca24-4993-a3aa-8b2c981302e8
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=MS-DS-Machine-Account-Quota,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1411
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: MS-DS-Machine-Account-Quota
+adminDescription: MS-DS-Machine-Account-Quota
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: ms-DS-MachineAccountQuota
+schemaIDGUID: d064fb68-1480-11d3-91c1-0000f87a57d4
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Server-Role,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.157
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Server-Role
+adminDescription: Server-Role
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: serverRole
+schemaIDGUID: bf967a33-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: b8119fd0-04f6-4762-ab7a-4986c76b3f9a
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Phone-Home-Primary,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 0.9.2342.19200300.100.1.20
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 1
+rangeUpper: 64
+mAPIID: 14857
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Phone-Home-Primary
+adminDescription: Phone-Home-Primary
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: homePhone
+schemaIDGUID: f0f8ffa1-1191-11d0-a060-00aa006c33ed
+attributeSecurityGUID: 77b5b886-944a-11d1-aebd-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=Range-Lower,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.34
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+mAPIID: 33043
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Range-Lower
+adminDescription: Range-Lower
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: rangeLower
+schemaIDGUID: bf967a0c-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=Operating-System-Hotfix,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.415
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Operating-System-Hotfix
+adminDescription: Operating-System-Hotfix
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: operatingSystemHotfix
+schemaIDGUID: bd951b3c-9c96-11d0-afdd-00c04fd930c9
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=ms-DS-Additional-Dns-Host-Name,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1717
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+rangeLower: 0
+rangeUpper: 2048
+showInAdvancedViewOnly: TRUE
+adminDisplayName: ms-DS-Additional-Dns-Host-Name
+adminDescription: ms-DS-Additional-Dns-Host-Name
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: msDS-AdditionalDnsHostName
+schemaIDGUID: 80863791-dbe9-4eb8-837e-7f0ab55d9ac7
+attributeSecurityGUID: 72e39547-7b18-11d1-adef-00c04fd8d5cd
+systemOnly: TRUE
+systemFlags: 16
+
+dn: CN=ms-DS-Az-Script-Timeout,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1797
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+rangeLower: 0
+showInAdvancedViewOnly: TRUE
+adminDisplayName: MS-DS-Az-Script-Timeout
+adminDescription: Maximum time (in ms) to wait for a script to finish auditing a specific policy
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: msDS-AzScriptTimeout
+schemaIDGUID: 87d0fb41-2c8b-41f6-b972-11fdfd50d6b0
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Must-Contain,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.24
+attributeSyntax: 2.5.5.2
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Must-Contain
+adminDescription: Must-Contain
+oMSyntax: 6
+searchFlags: 0
+lDAPDisplayName: mustContain
+schemaIDGUID: bf9679d3-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=X509-Cert,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 2.5.4.36
+attributeSyntax: 2.5.5.10
+isSingleValued: FALSE
+rangeUpper: 32768
+mAPIID: 35946
+showInAdvancedViewOnly: TRUE
+adminDisplayName: X509-Cert
+adminDescription: X509-Cert
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: userCertificate
+schemaIDGUID: bf967a7f-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: 77b5b886-944a-11d1-aebd-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=msNPCallingStationID,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1124
+attributeSyntax: 2.5.5.5
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: msNPCallingStationID
+adminDescription: msNPCallingStationID
+oMSyntax: 22
+searchFlags: 0
+lDAPDisplayName: msNPCallingStationID
+schemaIDGUID: db0c908a-c1f2-11d1-bbc5-0080c76670c0
+attributeSecurityGUID: 037088f8-0ae1-11d2-b422-00a0c968f939
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=ms-DS-User-Account-Control-Computed,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1460
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: ms-DS-User-Account-Control-Computed
+adminDescription: ms-DS-User-Account-Control-Computed
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: msDS-User-Account-Control-Computed
+schemaIDGUID: 2cc4b836-b63f-4940-8d23-ea7acf06af56
+attributeSecurityGUID: 4c164200-20c0-11d0-a768-00aa006e0529
+systemOnly: FALSE
+systemFlags: 20
+
+dn: CN=Home-Directory,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.44
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Home-Directory
+adminDescription: Home-Directory
+oMSyntax: 64
+searchFlags: 16
+lDAPDisplayName: homeDirectory
+schemaIDGUID: bf967985-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: 5f202010-79a5-11d0-9020-00c04fc2d4cf
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=ms-DS-Az-LDAP-Query,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1792
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 0
+rangeUpper: 4096
+showInAdvancedViewOnly: TRUE
+adminDisplayName: MS-DS-Az-LDAP-Query
+adminDescription: ms-DS-Az-LDAP-Query
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: msDS-AzLDAPQuery
+schemaIDGUID: 5e53368b-fc94-45c8-9d7d-daf31ee7112d
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Partial-Attribute-Deletion-List,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.663
+attributeSyntax: 2.5.5.10
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Partial-Attribute-Deletion-List
+adminDescription: Partial-Attribute-Deletion-List
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: partialAttributeDeletionList
+schemaIDGUID: 28630ec0-41d5-11d1-a9c1-0000f80367c1
+systemOnly: TRUE
+systemFlags: 19
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=Is-Critical-System-Object,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.868
+attributeSyntax: 2.5.5.8
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Is-Critical-System-Object
+adminDescription: Is-Critical-System-Object
+oMSyntax: 1
+searchFlags: 0
+lDAPDisplayName: isCriticalSystemObject
+schemaIDGUID: 00fbf30d-91fe-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=GP-Link,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.891
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: GP-Link
+adminDescription: GP-Link
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: gPLink
+schemaIDGUID: f30e3bbe-9ff0-11d1-b603-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=Scope-Flags,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1354
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Scope-Flags
+adminDescription: Scope-Flags
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: scopeFlags
+schemaIDGUID: 16f3a4c2-7e79-11d2-9921-0000f87a57d4
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Lockout-Duration,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.60
+attributeSyntax: 2.5.5.16
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Lockout-Duration
+adminDescription: Lockout-Duration
+oMSyntax: 65
+searchFlags: 0
+lDAPDisplayName: lockoutDuration
+schemaIDGUID: bf9679a5-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: c7407360-20bf-11d0-a768-00aa006e0529
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=ms-COM-UserPartitionSetLink,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1426
+attributeSyntax: 2.5.5.1
+isSingleValued: TRUE
+linkID: 1048
+showInAdvancedViewOnly: TRUE
+adminDisplayName: ms-COM-UserPartitionSetLink
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Link from a User to a PartitionSet. Default = adminDisplayName
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: msCOM-UserPartitionSetLink
+schemaIDGUID: 8e940c8a-e477-4367-b08d-ff2ff942dcd7
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Logo,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 2.16.840.1.113730.3.1.36
+attributeSyntax: 2.5.5.10
+isSingleValued: TRUE
+rangeLower: 1
+rangeUpper: 32767
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Logo
+adminDescription: Logo
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: thumbnailLogo
+schemaIDGUID: bf9679a9-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Picture,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 2.16.840.1.113730.3.1.35
+attributeSyntax: 2.5.5.10
+isSingleValued: TRUE
+rangeLower: 0
+rangeUpper: 102400
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Picture
+adminDescription: Picture
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: thumbnailPhoto
+schemaIDGUID: 8d3bca50-1d7e-11d0-a081-00aa006c33ed
+attributeSecurityGUID: 77b5b886-944a-11d1-aebd-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Location,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.222
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 0
+rangeUpper: 1024
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Location
+adminDescription: Location
+oMSyntax: 64
+searchFlags: 1
+lDAPDisplayName: location
+schemaIDGUID: 09dcb79f-165f-11d0-a064-00aa006c33ed
+systemOnly: FALSE
+systemFlags: 16
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=User-Workstations,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.86
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 0
+rangeUpper: 1024
+showInAdvancedViewOnly: TRUE
+adminDisplayName: User-Workstations
+adminDescription: User-Workstations
+oMSyntax: 64
+searchFlags: 16
+lDAPDisplayName: userWorkstations
+schemaIDGUID: bf9679d7-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: 5f202010-79a5-11d0-9020-00c04fc2d4cf
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Logon-Workstation,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.65
+attributeSyntax: 2.5.5.10
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Logon-Workstation
+adminDescription: Logon-Workstation
+oMSyntax: 4
+searchFlags: 16
+lDAPDisplayName: logonWorkstation
+schemaIDGUID: bf9679ac-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: 5f202010-79a5-11d0-9020-00c04fc2d4cf
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Last-Logon-Timestamp,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1696
+attributeSyntax: 2.5.5.16
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Last-Logon-Timestamp
+adminDescription: Last-Logon-Timestamp
+oMSyntax: 65
+searchFlags: 0
+lDAPDisplayName: lastLogonTimestamp
+schemaIDGUID: c0e20a04-0e5a-4ff3-9482-5efeaecd7060
+attributeSecurityGUID: 5f202010-79a5-11d0-9020-00c04fc2d4cf
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Prior-Value,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.100
+attributeSyntax: 2.5.5.10
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Prior-Value
+adminDescription: Prior-Value
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: priorValue
+schemaIDGUID: bf967a02-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Last-Set-Time,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.53
+attributeSyntax: 2.5.5.16
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Last-Set-Time
+adminDescription: Last-Set-Time
+oMSyntax: 65
+searchFlags: 0
+lDAPDisplayName: lastSetTime
+schemaIDGUID: bf967998-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Object-Guid,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.2
+attributeSyntax: 2.5.5.10
+isSingleValued: TRUE
+rangeLower: 16
+rangeUpper: 16
+mAPIID: 35949
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Object-Guid
+adminDescription: Object-Guid
+oMSyntax: 4
+searchFlags: 9
+lDAPDisplayName: objectGUID
+schemaIDGUID: bf9679e7-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: e48d0154-bcf8-11d1-8702-00c04fb96050
+systemOnly: TRUE
+systemFlags: 19
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=ms-DS-Tasks-For-Az-Task-BL,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1811
+attributeSyntax: 2.5.5.1
+isSingleValued: FALSE
+linkID: 2021
+showInAdvancedViewOnly: TRUE
+adminDisplayName: MS-DS-Tasks-For-Az-Task-BL
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Back-link from Az-Task to the Az-Task object(s) linking to it
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: msDS-TasksForAzTaskBL
+schemaIDGUID: df446e52-b5fa-4ca2-a42f-13f98a526c8f
+systemOnly: TRUE
+systemFlags: 17
+
+dn: CN=Managed-By,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.653
+attributeSyntax: 2.5.5.1
+isSingleValued: TRUE
+mAPIID: 32780
+linkID: 72
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Managed-By
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Managed-By
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: managedBy
+schemaIDGUID: 0296c120-40da-11d1-a9c0-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Pwd-Properties,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.93
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Pwd-Properties
+adminDescription: Pwd-Properties
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: pwdProperties
+schemaIDGUID: bf967a0b-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: c7407360-20bf-11d0-a768-00aa006e0529
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Builtin-Creation-Time,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.13
+attributeSyntax: 2.5.5.16
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Builtin-Creation-Time
+adminDescription: Builtin-Creation-Time
+oMSyntax: 65
+searchFlags: 0
+lDAPDisplayName: builtinCreationTime
+schemaIDGUID: bf96792f-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Post-Office-Box,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 2.5.4.18
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+rangeLower: 1
+rangeUpper: 40
+mAPIID: 14891
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Post-Office-Box
+adminDescription: Post-Office-Box
+oMSyntax: 64
+searchFlags: 16
+lDAPDisplayName: postOfficeBox
+schemaIDGUID: bf9679fb-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: 77b5b886-944a-11d1-aebd-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Company,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.146
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 1
+rangeUpper: 64
+mAPIID: 14870
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Company
+adminDescription: Company
+oMSyntax: 64
+searchFlags: 16
+lDAPDisplayName: company
+schemaIDGUID: f0f8ff88-1191-11d0-a060-00aa006c33ed
+attributeSecurityGUID: e48d0154-bcf8-11d1-8702-00c04fb96050
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Catalogs,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.675
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Catalogs
+adminDescription: Catalogs
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: catalogs
+schemaIDGUID: 7bfdcb81-4807-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Default-Object-Category,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.783
+attributeSyntax: 2.5.5.1
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Default-Object-Category
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Default-Object-Category
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: defaultObjectCategory
+schemaIDGUID: 26d97367-6070-11d1-a9c6-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=msRADIUSFramedRoute,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1158
+attributeSyntax: 2.5.5.5
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: msRADIUSFramedRoute
+adminDescription: msRADIUSFramedRoute
+oMSyntax: 22
+searchFlags: 0
+lDAPDisplayName: msRADIUSFramedRoute
+schemaIDGUID: db0c90a9-c1f2-11d1-bbc5-0080c76670c0
+attributeSecurityGUID: 037088f8-0ae1-11d2-b422-00a0c968f939
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Prior-Set-Time,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.99
+attributeSyntax: 2.5.5.16
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Prior-Set-Time
+adminDescription: Prior-Set-Time
+oMSyntax: 65
+searchFlags: 0
+lDAPDisplayName: priorSetTime
+schemaIDGUID: bf967a01-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=User-Cert,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.645
+attributeSyntax: 2.5.5.10
+isSingleValued: TRUE
+rangeLower: 0
+rangeUpper: 32767
+mAPIID: 14882
+showInAdvancedViewOnly: TRUE
+adminDisplayName: User-Cert
+adminDescription: User-Cert
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: userCert
+schemaIDGUID: bf967a69-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: 77b5b886-944a-11d1-aebd-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=Non-Security-Member,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.530
+attributeSyntax: 2.5.5.1
+isSingleValued: FALSE
+linkID: 50
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Non-Security-Member
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Non-Security-Member
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: nonSecurityMember
+schemaIDGUID: 52458018-ca6a-11d0-afff-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Member,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 2.5.4.31
+attributeSyntax: 2.5.5.1
+isSingleValued: FALSE
+mAPIID: 32777
+linkID: 2
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Member
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Member
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: member
+schemaIDGUID: bf9679c0-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: bc0ac240-79a9-11d0-9020-00c04fc2d4cf
+systemOnly: FALSE
+systemFlags: 18
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=Group-Attributes,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.152
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Group-Attributes
+adminDescription: Group-Attributes
+oMSyntax: 2
+searchFlags: 1
+lDAPDisplayName: groupAttributes
+schemaIDGUID: bf96797e-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=System-Flags,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.375
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: System-Flags
+adminDescription: System-Flags
+oMSyntax: 2
+searchFlags: 8
+lDAPDisplayName: systemFlags
+schemaIDGUID: e0fa1e62-9b45-11d0-afdd-00c04fd930c9
+attributeSecurityGUID: e48d0154-bcf8-11d1-8702-00c04fb96050
+systemOnly: TRUE
+systemFlags: 16
+
+dn: CN=Proxied-Object-Name,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1249
+attributeSyntax: 2.5.5.7
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Proxied-Object-Name
+oMObjectClass:: KoZIhvcUAQEBCw==
+adminDescription: Proxied-Object-Name
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: proxiedObjectName
+schemaIDGUID: e1aea402-cd5b-11d0-afff-0000f80367c1
+systemOnly: TRUE
+systemFlags: 18
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=ms-DS-Repl-Value-Meta-Data,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1708
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: ms-DS-Repl-Value-Meta-Data
+adminDescription: ms-DS-Repl-Value-Meta-Data
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: msDS-ReplValueMetaData
+schemaIDGUID: 2f5c8145-e1bd-410b-8957-8bfa81d5acfd
+systemOnly: FALSE
+systemFlags: 20
+
+dn: CN=Allowed-Child-Classes-Effective,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.912
+attributeSyntax: 2.5.5.2
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Allowed-Child-Classes-Effective
+adminDescription: Allowed-Child-Classes-Effective
+oMSyntax: 6
+searchFlags: 0
+lDAPDisplayName: allowedChildClassesEffective
+schemaIDGUID: 9a7ad943-ca53-11d1-bbd0-0080c76670c0
+attributeSecurityGUID: e48d0154-bcf8-11d1-8702-00c04fb96050
+systemOnly: TRUE
+systemFlags: 134217748
+
+dn: CN=ms-DS-Az-Generate-Audits,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1805
+attributeSyntax: 2.5.5.8
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: MS-DS-Az-Generate-Audits
+adminDescription: A boolean field indicating if runtime audits need to be turned on (include audits for access checks, etc.)
+oMSyntax: 1
+searchFlags: 0
+lDAPDisplayName: msDS-AzGenerateAudits
+schemaIDGUID: f90abab0-186c-4418-bb85-88447c87222a
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=ms-DS-Az-Application-Version,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1817
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 0
+showInAdvancedViewOnly: TRUE
+adminDisplayName: MS-DS-Az-Application-Version
+adminDescription: A version number to indicate that the AzApplication is updated
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: msDS-AzApplicationVersion
+schemaIDGUID: 7184a120-3ac4-47ae-848f-fe0ab20784d4
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Icon-Path,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.219
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+rangeLower: 0
+rangeUpper: 2048
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Icon-Path
+adminDescription: Icon-Path
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: iconPath
+schemaIDGUID: f0f8ff83-1191-11d0-a060-00aa006c33ed
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Street-Address,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 2.5.4.9
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 1
+rangeUpper: 1024
+mAPIID: 33082
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Street-Address
+adminDescription: Street-Address
+oMSyntax: 64
+searchFlags: 16
+lDAPDisplayName: street
+schemaIDGUID: bf967a3a-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: 77b5b886-944a-11d1-aebd-0000f80367c1
+systemOnly: FALSE
+systemFlags: 18
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=ms-DS-ExecuteScriptPassword,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1783
+attributeSyntax: 2.5.5.10
+isSingleValued: TRUE
+rangeLower: 0
+rangeUpper: 64
+showInAdvancedViewOnly: TRUE
+adminDisplayName: ms-DS-ExecuteScriptPassword
+adminDescription: ms-DS-ExecuteScriptPassword
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: msDS-ExecuteScriptPassword
+schemaIDGUID: 9d054a5a-d187-46c1-9d85-42dfc44a56dd
+systemOnly: TRUE
+systemFlags: 17
+
+dn: CN=ms-DS-Logon-Time-Sync-Interval,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1784
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+rangeLower: 0
+showInAdvancedViewOnly: TRUE
+adminDisplayName: ms-DS-Logon-Time-Sync-Interval
+adminDescription: ms-DS-Logon-Time-Sync-Interval
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: msDS-LogonTimeSyncInterval
+schemaIDGUID: ad7940f8-e43a-4a42-83bc-d688e59ea605
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Garbage-Coll-Period,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.301
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+mAPIID: 32943
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Garbage-Coll-Period
+adminDescription: Garbage-Coll-Period
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: garbageCollPeriod
+schemaIDGUID: 5fd424a1-1262-11d0-a060-00aa006c33ed
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=MSMQ-Sign-Certificates-Mig,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.967
+attributeSyntax: 2.5.5.10
+isSingleValued: TRUE
+rangeUpper: 1048576
+showInAdvancedViewOnly: TRUE
+adminDisplayName: MSMQ-Sign-Certificates-Mig
+adminDescription: MSMQ-Sign-Certificates-Mig
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: mSMQSignCertificatesMig
+schemaIDGUID: 3881b8ea-da3b-11d1-90a5-00c04fd91ab1
+systemOnly: FALSE
+systemFlags: 16
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=ms-DS-Cached-Membership-Time-Stamp,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1442
+attributeSyntax: 2.5.5.16
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: ms-DS-Cached-Membership-Time-Stamp
+adminDescription: ms-DS-Cached-Membership-Time-Stamp
+oMSyntax: 65
+searchFlags: 1
+lDAPDisplayName: msDS-Cached-Membership-Time-Stamp
+schemaIDGUID: 3566bf1f-beee-4dcb-8abe-ef89fcfec6c1
+systemOnly: FALSE
+systemFlags: 17
+
+dn: CN=Logon-Count,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.169
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Logon-Count
+adminDescription: Logon-Count
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: logonCount
+schemaIDGUID: bf9679aa-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: 5f202010-79a5-11d0-9020-00c04fc2d4cf
+systemOnly: FALSE
+systemFlags: 17
+
+dn: CN=Locale-ID,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.58
+attributeSyntax: 2.5.5.9
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Locale-ID
+adminDescription: Locale-ID
+oMSyntax: 2
+searchFlags: 16
+lDAPDisplayName: localeID
+schemaIDGUID: bf9679a1-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Bad-Pwd-Count,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.12
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Bad-Pwd-Count
+adminDescription: Bad-Pwd-Count
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: badPwdCount
+schemaIDGUID: bf96792e-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: 5f202010-79a5-11d0-9020-00c04fc2d4cf
+systemOnly: FALSE
+systemFlags: 17
+
+dn: CN=Trust-Auth-Incoming,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.129
+attributeSyntax: 2.5.5.10
+isSingleValued: TRUE
+rangeLower: 0
+rangeUpper: 32767
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Trust-Auth-Incoming
+adminDescription: Trust-Auth-Incoming
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: trustAuthIncoming
+schemaIDGUID: bf967a59-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=SubSchemaSubEntry,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 2.5.18.10
+attributeSyntax: 2.5.5.1
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: SubSchemaSubEntry
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: SubSchemaSubEntry
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: subSchemaSubEntry
+schemaIDGUID: 9a7ad94d-ca53-11d1-bbd0-0080c76670c0
+systemOnly: TRUE
+systemFlags: 134217748
+
+dn: CN=Structural-Object-Class,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 2.5.21.9
+attributeSyntax: 2.5.5.2
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Structural-Object-Class
+adminDescription: The class hierarchy without auxiliary classes
+oMSyntax: 6
+searchFlags: 0
+lDAPDisplayName: structuralObjectClass
+schemaIDGUID: 3860949f-f6a8-4b38-9950-81ecb6bc2982
+systemOnly: FALSE
+systemFlags: 20
+
+dn: CN=Is-Deleted,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.48
+attributeSyntax: 2.5.5.8
+isSingleValued: TRUE
+mAPIID: 32960
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Is-Deleted
+adminDescription: Is-Deleted
+oMSyntax: 1
+searchFlags: 0
+lDAPDisplayName: isDeleted
+schemaIDGUID: bf96798f-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+systemFlags: 18
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=Extra-Columns,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1687
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Extra-Columns
+adminDescription: Extra-Columns
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: extraColumns
+schemaIDGUID: d24e2846-1dd9-4bcf-99d7-a6227cc86da7
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Admin-Multiselect-Property-Pages,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1690
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Admin-Multiselect-Property-Pages
+adminDescription: Admin-Multiselect-Property-Pages
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: adminMultiselectPropertyPages
+schemaIDGUID: 18f9b67d-5ac6-4b3b-97db-d0a406afb7ba
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Options,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.307
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Options
+adminDescription: Options
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: options
+schemaIDGUID: 19195a53-6da0-11d0-afd3-00c04fd930c9
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Lock-Out-Observation-Window,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.61
+attributeSyntax: 2.5.5.16
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Lock-Out-Observation-Window
+adminDescription: Lock-Out-Observation-Window
+oMSyntax: 65
+searchFlags: 0
+lDAPDisplayName: lockOutObservationWindow
+schemaIDGUID: bf9679a4-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: c7407360-20bf-11d0-a768-00aa006e0529
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Default-Local-Policy-Object,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.57
+attributeSyntax: 2.5.5.1
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Default-Local-Policy-Object
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Default-Local-Policy-Object
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: defaultLocalPolicyObject
+schemaIDGUID: bf96799f-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Creation-Time,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.26
+attributeSyntax: 2.5.5.16
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Creation-Time
+adminDescription: Creation-Time
+oMSyntax: 65
+searchFlags: 0
+lDAPDisplayName: creationTime
+schemaIDGUID: bf967946-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Registered-Address,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 2.5.4.26
+attributeSyntax: 2.5.5.10
+isSingleValued: FALSE
+rangeLower: 1
+rangeUpper: 4096
+mAPIID: 33049
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Registered-Address
+adminDescription: Registered-Address
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: registeredAddress
+schemaIDGUID: bf967a10-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: 77b5b886-944a-11d1-aebd-0000f80367c1
+systemOnly: FALSE
+
+dn: CN=Postal-Address,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 2.5.4.16
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+rangeLower: 1
+rangeUpper: 4096
+mAPIID: 33036
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Postal-Address
+adminDescription: Postal-Address
+oMSyntax: 64
+searchFlags: 16
+lDAPDisplayName: postalAddress
+schemaIDGUID: bf9679fc-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: 77b5b886-944a-11d1-aebd-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Initials,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 2.5.4.43
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 1
+rangeUpper: 6
+mAPIID: 14858
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Initials
+adminDescription: Initials
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: initials
+schemaIDGUID: f0f8ff90-1191-11d0-a060-00aa006c33ed
+attributeSecurityGUID: e48d0154-bcf8-11d1-8702-00c04fb96050
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Is-Single-Valued,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.33
+attributeSyntax: 2.5.5.8
+isSingleValued: TRUE
+mAPIID: 32961
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Is-Single-Valued
+adminDescription: Is-Single-Valued
+oMSyntax: 1
+searchFlags: 0
+lDAPDisplayName: isSingleValued
+schemaIDGUID: bf967992-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+systemFlags: 16
+
+dn: CN=Netboot-SIF-File,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1240
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Netboot-SIF-File
+adminDescription: Netboot-SIF-File
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: netbootSIFFile
+schemaIDGUID: 2df90d84-009f-11d2-aa4c-00c04fd7d83a
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=ms-DS-Additional-Sam-Account-Name,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1718
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+rangeLower: 0
+rangeUpper: 256
+showInAdvancedViewOnly: TRUE
+adminDisplayName: ms-DS-Additional-Sam-Account-Name
+adminDescription: ms-DS-Additional-Sam-Account-Name
+oMSyntax: 64
+searchFlags: 13
+lDAPDisplayName: msDS-AdditionalSamAccountName
+schemaIDGUID: 975571df-a4d5-429a-9f59-cdc6581d91e6
+systemOnly: TRUE
+systemFlags: 16
+
+dn: CN=System-Poss-Superiors,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.195
+attributeSyntax: 2.5.5.2
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: System-Poss-Superiors
+adminDescription: System-Poss-Superiors
+oMSyntax: 6
+searchFlags: 0
+lDAPDisplayName: systemPossSuperiors
+schemaIDGUID: bf967a47-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+systemFlags: 18
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=photo,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 0.9.2342.19200300.100.1.7
+attributeSyntax: 2.5.5.10
+isSingleValued: FALSE
+showInAdvancedViewOnly: FALSE
+adminDisplayName: photo
+adminDescription: An object encoded in G3 fax as explained in recommendation T.4, with an ASN.1 wrapper to make it compatible with an X.400 BodyPart as defined in X.420.
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: photo
+schemaIDGUID: 9c979768-ba1a-4c08-9632-c6a5c1ed649a
+systemOnly: FALSE
+systemFlags: 0
+
+dn: CN=Employee-Number,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.610
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 1
+rangeUpper: 512
+mAPIID: 35943
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Employee-Number
+adminDescription: Employee-Number
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: employeeNumber
+schemaIDGUID: a8df73ef-c5ea-11d1-bbcb-0080c76670c0
+systemOnly: FALSE
+systemFlags: 0
+
+dn: CN=Lockout-Time,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.662
+attributeSyntax: 2.5.5.16
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Lockout-Time
+adminDescription: Lockout-Time
+oMSyntax: 65
+searchFlags: 0
+lDAPDisplayName: lockoutTime
+schemaIDGUID: 28630ebf-41d5-11d1-a9c1-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Dynamic-LDAP-Server,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.537
+attributeSyntax: 2.5.5.1
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Dynamic-LDAP-Server
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Dynamic-LDAP-Server
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: dynamicLDAPServer
+schemaIDGUID: 52458021-ca6a-11d0-afff-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Extended-Attribute-Info,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.909
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Extended-Attribute-Info
+adminDescription: Extended-Attribute-Info
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: extendedAttributeInfo
+schemaIDGUID: 9a7ad947-ca53-11d1-bbd0-0080c76670c0
+systemOnly: TRUE
+systemFlags: 134217748
+
+dn: CN=ms-Exch-Assistant-Name,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.444
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 1
+rangeUpper: 256
+mAPIID: 14896
+adminDisplayName: ms-Exch-Assistant-Name
+adminDescription: ms-Exch-Assistant-Name
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: msExchAssistantName
+schemaIDGUID: a8df7394-c5ea-11d1-bbcb-0080c76670c0
+
+dn: CN=GPC-User-Extension-Names,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1349
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: GPC-User-Extension-Names
+adminDescription: GPC-User-Extension-Names
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: gPCUserExtensionNames
+schemaIDGUID: 42a75fc6-783f-11d2-9916-0000f87a57d4
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=ms-DS-Non-Members-BL,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1794
+attributeSyntax: 2.5.5.1
+isSingleValued: FALSE
+linkID: 2015
+showInAdvancedViewOnly: TRUE
+adminDisplayName: ms-DS-Non-Members-BL
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: MS-DS-Non-Members-BL
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: msDS-NonMembersBL
+schemaIDGUID: 2a8c68fc-3a7a-4e87-8720-fe77c51cbe74
+systemOnly: TRUE
+systemFlags: 17
+
+dn: CN=Admin-Display-Name,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.194
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 1
+rangeUpper: 256
+mAPIID: 32843
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Admin-Display-Name
+adminDescription: Admin-Display-Name
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: adminDisplayName
+schemaIDGUID: bf96791a-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Context-Menu,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.499
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Context-Menu
+adminDescription: Context-Menu
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: contextMenu
+schemaIDGUID: 4d8601ee-ac85-11d0-afe3-00c04fd930c9
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Link-ID,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.50
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+mAPIID: 32965
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Link-ID
+adminDescription: Link-ID
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: linkID
+schemaIDGUID: bf96799b-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+systemFlags: 16
+
+dn: CN=attributeCertificateAttribute,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 2.5.4.58
+attributeSyntax: 2.5.5.10
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: attributeCertificateAttribute
+adminDescription: A digitally signed or certified identity and set of attributes. Used to bind authorization information to an identity. X.509
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: attributeCertificateAttribute
+schemaIDGUID: fa4693bb-7bc2-4cb9-81a8-c99c43b7905e
+systemOnly: FALSE
+systemFlags: 0
+
+dn: CN=Surname,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 2.5.4.4
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 1
+rangeUpper: 64
+mAPIID: 14865
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Surname
+adminDescription: Surname
+oMSyntax: 64
+searchFlags: 5
+lDAPDisplayName: sn
+schemaIDGUID: bf967a41-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: e48d0154-bcf8-11d1-8702-00c04fb96050
+systemOnly: FALSE
+systemFlags: 16
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=SAM-Account-Name,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.221
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 0
+rangeUpper: 256
+showInAdvancedViewOnly: TRUE
+adminDisplayName: SAM-Account-Name
+adminDescription: SAM-Account-Name
+oMSyntax: 64
+searchFlags: 13
+lDAPDisplayName: sAMAccountName
+schemaIDGUID: 3e0abfd0-126a-11d0-a060-00aa006c33ed
+attributeSecurityGUID: 59ba2f42-79a2-11d0-9020-00c04fc2d3cf
+systemOnly: FALSE
+systemFlags: 18
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=Governs-ID,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.22
+attributeSyntax: 2.5.5.2
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Governs-ID
+adminDescription: Governs-ID
+oMSyntax: 6
+searchFlags: 8
+lDAPDisplayName: governsID
+schemaIDGUID: bf96797d-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+systemFlags: 16
+
+dn: CN=jpegPhoto,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 0.9.2342.19200300.100.1.60
+attributeSyntax: 2.5.5.10
+isSingleValued: FALSE
+showInAdvancedViewOnly: FALSE
+adminDisplayName: jpegPhoto
+adminDescription: Used to store one or more images of a person using the JPEG File Interchange Format [JFIF].
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: jpegPhoto
+schemaIDGUID: bac80572-09c4-4fa9-9ae6-7628d7adbe0e
+systemOnly: FALSE
+systemFlags: 0
+
+dn: CN=MSMQ-Sign-Certificates,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.947
+attributeSyntax: 2.5.5.10
+isSingleValued: TRUE
+rangeUpper: 1048576
+showInAdvancedViewOnly: TRUE
+adminDisplayName: MSMQ-Sign-Certificates
+adminDescription: MSMQ-Sign-Certificates
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: mSMQSignCertificates
+schemaIDGUID: 9a0dc33b-c100-11d1-bbc5-0080c76670c0
+attributeSecurityGUID: 77b5b886-944a-11d1-aebd-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=Initial-Auth-Incoming,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.539
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Initial-Auth-Incoming
+adminDescription: Initial-Auth-Incoming
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: initialAuthIncoming
+schemaIDGUID: 52458023-ca6a-11d0-afff-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Domain-Cross-Ref,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.472
+attributeSyntax: 2.5.5.1
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Domain-Cross-Ref
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Domain-Cross-Ref
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: domainCrossRef
+schemaIDGUID: b000ea7b-a086-11d0-afdd-00c04fd930c9
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Text-Encoded-OR-Address,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 0.9.2342.19200300.100.1.2
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 1
+rangeUpper: 1024
+mAPIID: 35969
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Text-Encoded-OR-Address
+adminDescription: Text-Encoded-OR-Address
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: textEncodedORAddress
+schemaIDGUID: a8df7489-c5ea-11d1-bbcb-0080c76670c0
+systemOnly: FALSE
+
+dn: CN=GPC-Functionality-Version,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.893
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: GPC-Functionality-Version
+adminDescription: GPC-Functionality-Version
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: gPCFunctionalityVersion
+schemaIDGUID: f30e3bc0-9ff0-11d1-b603-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=USN-DSA-Last-Obj-Removed,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.267
+attributeSyntax: 2.5.5.16
+isSingleValued: TRUE
+mAPIID: 33109
+showInAdvancedViewOnly: TRUE
+adminDisplayName: USN-DSA-Last-Obj-Removed
+adminDescription: USN-DSA-Last-Obj-Removed
+oMSyntax: 65
+searchFlags: 0
+lDAPDisplayName: uSNDSALastObjRemoved
+schemaIDGUID: bf967a71-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+systemFlags: 16
+
+dn: CN=ms-DS-Operations-For-Az-Role-BL,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1813
+attributeSyntax: 2.5.5.1
+isSingleValued: FALSE
+linkID: 2023
+showInAdvancedViewOnly: TRUE
+adminDisplayName: MS-DS-Operations-For-Az-Role-BL
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Back-link from Az-Operation to Az-Role object(s) linking to it
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: msDS-OperationsForAzRoleBL
+schemaIDGUID: f85b6228-3734-4525-b6b7-3f3bb220902c
+systemOnly: TRUE
+systemFlags: 17
+
+dn: CN=MS-DS-Consistency-Child-Count,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1361
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: MS-DS-Consistency-Child-Count
+adminDescription: MS-DS-Consistency-Child-Count
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: mS-DS-ConsistencyChildCount
+schemaIDGUID: 178b7bc2-b63a-11d2-90e1-00c04fd91ab1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=DSA-Signature,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.74
+attributeSyntax: 2.5.5.10
+isSingleValued: TRUE
+mAPIID: 32887
+showInAdvancedViewOnly: TRUE
+adminDisplayName: DSA-Signature
+adminDescription: DSA-Signature
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: dSASignature
+schemaIDGUID: 167757bc-47f3-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Allowed-Child-Classes,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.911
+attributeSyntax: 2.5.5.2
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Allowed-Child-Classes
+adminDescription: Allowed-Child-Classes
+oMSyntax: 6
+searchFlags: 0
+lDAPDisplayName: allowedChildClasses
+schemaIDGUID: 9a7ad942-ca53-11d1-bbd0-0080c76670c0
+attributeSecurityGUID: e48d0154-bcf8-11d1-8702-00c04fb96050
+systemOnly: TRUE
+systemFlags: 134217748
+
+dn: CN=Allowed-Attributes-Effective,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.914
+attributeSyntax: 2.5.5.2
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Allowed-Attributes-Effective
+adminDescription: Allowed-Attributes-Effective
+oMSyntax: 6
+searchFlags: 0
+lDAPDisplayName: allowedAttributesEffective
+schemaIDGUID: 9a7ad941-ca53-11d1-bbd0-0080c76670c0
+attributeSecurityGUID: e48d0154-bcf8-11d1-8702-00c04fb96050
+systemOnly: TRUE
+systemFlags: 134217748
+
+dn: CN=NT-Mixed-Domain,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.357
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: NT-Mixed-Domain
+adminDescription: NT-Mixed-Domain
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: nTMixedDomain
+schemaIDGUID: 3e97891f-8c01-11d0-afda-00c04fd930c9
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=ms-DS-Has-Instantiated-NCs,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1709
+attributeSyntax: 2.5.5.7
+isSingleValued: FALSE
+rangeLower: 4
+rangeUpper: 4
+linkID: 2002
+showInAdvancedViewOnly: TRUE
+adminDisplayName: ms-DS-Has-Instantiated-NCs
+oMObjectClass:: KoZIhvcUAQEBCw==
+adminDescription: DS replication information detailing the state of the NCs present on a particular server.
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: msDS-HasInstantiatedNCs
+schemaIDGUID: 11e9a5bc-4517-4049-af9c-51554fb0fc09
+systemOnly: TRUE
+systemFlags: 16
+
+dn: CN=Min-Pwd-Length,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.79
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Min-Pwd-Length
+adminDescription: Min-Pwd-Length
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: minPwdLength
+schemaIDGUID: bf9679c3-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: c7407360-20bf-11d0-a768-00aa006e0529
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Domain-Policy-Object,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.32
+attributeSyntax: 2.5.5.1
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Domain-Policy-Object
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Domain-Policy-Object
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: domainPolicyObject
+schemaIDGUID: bf96795d-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Physical-Delivery-Office-Name,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 2.5.4.19
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 1
+rangeUpper: 128
+mAPIID: 14873
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Physical-Delivery-Office-Name
+adminDescription: Physical-Delivery-Office-Name
+oMSyntax: 64
+searchFlags: 5
+lDAPDisplayName: physicalDeliveryOfficeName
+schemaIDGUID: bf9679f7-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: 77b5b886-944a-11d1-aebd-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Volume-Count,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.507
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Volume-Count
+adminDescription: Volume-Count
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: volumeCount
+schemaIDGUID: 34aaa217-b699-11d0-afee-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=msRADIUSServiceType,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1171
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: msRADIUSServiceType
+adminDescription: msRADIUSServiceType
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: msRADIUSServiceType
+schemaIDGUID: db0c90b6-c1f2-11d1-bbc5-0080c76670c0
+attributeSecurityGUID: 037088f8-0ae1-11d2-b422-00a0c968f939
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Last-Logon,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.52
+attributeSyntax: 2.5.5.16
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Last-Logon
+adminDescription: Last-Logon
+oMSyntax: 65
+searchFlags: 0
+lDAPDisplayName: lastLogon
+schemaIDGUID: bf967997-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: 5f202010-79a5-11d0-9020-00c04fc2d4cf
+systemOnly: FALSE
+systemFlags: 17
+
+dn: CN=Groups-to-Ignore,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.344
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Groups-to-Ignore
+adminDescription: Groups-to-Ignore
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: groupsToIgnore
+schemaIDGUID: eea65904-8ac6-11d0-afda-00c04fd930c9
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Schema-Info,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1358
+attributeSyntax: 2.5.5.10
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Schema-Info
+adminDescription: Schema-Info
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: schemaInfo
+schemaIDGUID: f9fb64ae-93b4-11d2-9945-0000f87a57d4
+systemOnly: TRUE
+systemFlags: 16
+
+dn: CN=Domain-Component,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 0.9.2342.19200300.100.1.25
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 1
+rangeUpper: 255
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Domain-Component
+adminDescription: Domain-Component
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: dc
+schemaIDGUID: 19195a55-6da0-11d0-afd3-00c04fd930c9
+systemOnly: FALSE
+systemFlags: 18
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=Object-Category,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.782
+attributeSyntax: 2.5.5.1
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Object-Category
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Object-Category
+oMSyntax: 127
+searchFlags: 1
+lDAPDisplayName: objectCategory
+schemaIDGUID: 26d97369-6070-11d1-a9c6-0000f80367c1
+attributeSecurityGUID: e48d0154-bcf8-11d1-8702-00c04fb96050
+systemOnly: FALSE
+systemFlags: 18
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=Modify-Time-Stamp,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 2.5.18.2
+attributeSyntax: 2.5.5.11
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Modify-Time-Stamp
+adminDescription: Modify-Time-Stamp
+oMSyntax: 24
+searchFlags: 0
+lDAPDisplayName: modifyTimeStamp
+schemaIDGUID: 9a7ad94a-ca53-11d1-bbd0-0080c76670c0
+systemOnly: TRUE
+systemFlags: 134217748
+
+dn: CN=Display-Name,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.13
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 0
+rangeUpper: 256
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Display-Name
+adminDescription: Display-Name
+oMSyntax: 64
+searchFlags: 5
+lDAPDisplayName: displayName
+schemaIDGUID: bf967953-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: 59ba2f42-79a2-11d0-9020-00c04fc2d3cf
+systemOnly: FALSE
+systemFlags: 16
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=Admin-Description,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.226
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 0
+rangeUpper: 1024
+mAPIID: 32842
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Admin-Description
+adminDescription: Admin-Description
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: adminDescription
+schemaIDGUID: bf967919-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: 59ba2f42-79a2-11d0-9020-00c04fc2d3cf
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=ms-DS-DnsRootAlias,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1719
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 0
+rangeUpper: 255
+showInAdvancedViewOnly: TRUE
+adminDisplayName: ms-DS-DnsRootAlias
+adminDescription: ms-DS-DnsRootAlias
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: msDS-DnsRootAlias
+schemaIDGUID: 2143acca-eead-4d29-b591-85fa49ce9173
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Creation-Wizard,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.498
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Creation-Wizard
+adminDescription: Creation-Wizard
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: creationWizard
+schemaIDGUID: 4d8601ed-ac85-11d0-afe3-00c04fd930c9
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Has-Partial-Replica-NCs,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.15
+attributeSyntax: 2.5.5.1
+isSingleValued: FALSE
+mAPIID: 32949
+linkID: 74
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Has-Partial-Replica-NCs
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Has-Partial-Replica-NCs
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: hasPartialReplicaNCs
+schemaIDGUID: bf967981-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+systemFlags: 16
+
+dn: CN=Control-Access-Rights,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.200
+attributeSyntax: 2.5.5.10
+isSingleValued: FALSE
+rangeLower: 16
+rangeUpper: 16
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Control-Access-Rights
+adminDescription: Control-Access-Rights
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: controlAccessRights
+schemaIDGUID: 6da8a4fc-0e52-11d0-a286-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=UAS-Compat,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.155
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: UAS-Compat
+adminDescription: UAS-Compat
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: uASCompat
+schemaIDGUID: bf967a61-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: b8119fd0-04f6-4762-ab7a-4986c76b3f9a
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Object-Sid,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.146
+attributeSyntax: 2.5.5.17
+isSingleValued: TRUE
+rangeLower: 0
+rangeUpper: 28
+mAPIID: 32807
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Object-Sid
+adminDescription: Object-Sid
+oMSyntax: 4
+searchFlags: 9
+lDAPDisplayName: objectSid
+schemaIDGUID: bf9679e8-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: 59ba2f42-79a2-11d0-9020-00c04fc2d3cf
+systemOnly: TRUE
+systemFlags: 18
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=Title,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 2.5.4.12
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 1
+rangeUpper: 64
+mAPIID: 14871
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Title
+adminDescription: Title
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: title
+schemaIDGUID: bf967a55-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: e48d0154-bcf8-11d1-8702-00c04fb96050
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Phone-Pager-Other,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.118
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+rangeLower: 1
+rangeUpper: 64
+mAPIID: 35950
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Phone-Pager-Other
+adminDescription: Phone-Pager-Other
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: otherPager
+schemaIDGUID: f0f8ffa4-1191-11d0-a060-00aa006c33ed
+attributeSecurityGUID: 77b5b886-944a-11d1-aebd-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Division,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.261
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 0
+rangeUpper: 256
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Division
+adminDescription: Division
+oMSyntax: 64
+searchFlags: 16
+lDAPDisplayName: division
+schemaIDGUID: fe6136a0-2073-11d0-a9c2-00aa006c33ed
+attributeSecurityGUID: e48d0154-bcf8-11d1-8702-00c04fb96050
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Range-Upper,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.35
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+mAPIID: 33044
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Range-Upper
+adminDescription: Range-Upper
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: rangeUpper
+schemaIDGUID: bf967a0d-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=OM-Object-Class,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.218
+attributeSyntax: 2.5.5.10
+isSingleValued: TRUE
+mAPIID: 33021
+showInAdvancedViewOnly: TRUE
+adminDisplayName: OM-Object-Class
+adminDescription: OM-Object-Class
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: oMObjectClass
+schemaIDGUID: bf9679ec-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+systemFlags: 16
+
+dn: CN=MAPI-ID,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.49
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+mAPIID: 32974
+showInAdvancedViewOnly: TRUE
+adminDisplayName: MAPI-ID
+adminDescription: MAPI-ID
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: mAPIID
+schemaIDGUID: bf9679b7-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+systemFlags: 16
+
+dn: CN=SAM-Account-Type,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.302
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: SAM-Account-Type
+adminDescription: SAM-Account-Type
+oMSyntax: 2
+searchFlags: 1
+lDAPDisplayName: sAMAccountType
+schemaIDGUID: 6e7b626c-64f2-11d0-afd2-00c04fd930c9
+attributeSecurityGUID: 59ba2f42-79a2-11d0-9020-00c04fc2d3cf
+systemOnly: FALSE
+systemFlags: 18
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=Object-Class-Category,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.370
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+rangeLower: 0
+rangeUpper: 3
+mAPIID: 33014
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Object-Class-Category
+adminDescription: Object-Class-Category
+oMSyntax: 10
+searchFlags: 0
+lDAPDisplayName: objectClassCategory
+schemaIDGUID: bf9679e6-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+systemFlags: 16
+
+dn: CN=Default-Hiding-Value,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.518
+attributeSyntax: 2.5.5.8
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Default-Hiding-Value
+adminDescription: Default-Hiding-Value
+oMSyntax: 1
+searchFlags: 0
+lDAPDisplayName: defaultHidingValue
+schemaIDGUID: b7b13116-b82e-11d0-afee-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=msNPAllowDialin,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1119
+attributeSyntax: 2.5.5.8
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: msNPAllowDialin
+adminDescription: msNPAllowDialin
+oMSyntax: 1
+searchFlags: 0
+lDAPDisplayName: msNPAllowDialin
+schemaIDGUID: db0c9085-c1f2-11d1-bbc5-0080c76670c0
+attributeSecurityGUID: 037088f8-0ae1-11d2-b422-00a0c968f939
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Code-Page,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.16
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+rangeLower: 0
+rangeUpper: 65535
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Code-Page
+adminDescription: Code-Page
+oMSyntax: 2
+searchFlags: 16
+lDAPDisplayName: codePage
+schemaIDGUID: bf967938-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: 59ba2f42-79a2-11d0-9020-00c04fc2d3cf
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Admin-Count,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.150
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Admin-Count
+adminDescription: Admin-Count
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: adminCount
+schemaIDGUID: bf967918-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Schema-Update,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.481
+attributeSyntax: 2.5.5.11
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Schema-Update
+adminDescription: Schema-Update
+oMSyntax: 24
+searchFlags: 0
+lDAPDisplayName: schemaUpdate
+schemaIDGUID: 1e2d06b4-ac8f-11d0-afe3-00c04fd930c9
+systemOnly: FALSE
+systemFlags: 17
+
+dn: CN=Trust-Direction,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.132
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Trust-Direction
+adminDescription: Trust-Direction
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: trustDirection
+schemaIDGUID: bf967a5c-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=Enabled,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.557
+attributeSyntax: 2.5.5.8
+isSingleValued: TRUE
+mAPIID: 35873
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Enabled
+adminDescription: Enabled
+oMSyntax: 1
+searchFlags: 0
+lDAPDisplayName: Enabled
+schemaIDGUID: a8df73f2-c5ea-11d1-bbcb-0080c76670c0
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Locality-Name,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 2.5.4.7
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 1
+rangeUpper: 128
+mAPIID: 14887
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Locality-Name
+adminDescription: Locality-Name
+oMSyntax: 64
+searchFlags: 17
+lDAPDisplayName: l
+schemaIDGUID: bf9679a2-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: 77b5b886-944a-11d1-aebd-0000f80367c1
+systemOnly: FALSE
+systemFlags: 18
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=EFSPolicy,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.268
+attributeSyntax: 2.5.5.10
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: EFSPolicy
+adminDescription: EFSPolicy
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: eFSPolicy
+schemaIDGUID: 8e4eb2ec-4712-11d0-a1a0-00c04fd930c9
+attributeSecurityGUID: a29b89fd-c7e8-11d0-9bae-00c04fd92ef5
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Builtin-Modified-Count,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.14
+attributeSyntax: 2.5.5.16
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Builtin-Modified-Count
+adminDescription: Builtin-Modified-Count
+oMSyntax: 65
+searchFlags: 0
+lDAPDisplayName: builtinModifiedCount
+schemaIDGUID: bf967930-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Phone-Office-Other,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.18
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+rangeLower: 1
+rangeUpper: 64
+mAPIID: 14875
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Phone-Office-Other
+adminDescription: Phone-Office-Other
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: otherTelephone
+schemaIDGUID: f0f8ffa5-1191-11d0-a060-00aa006c33ed
+attributeSecurityGUID: 77b5b886-944a-11d1-aebd-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Phone-ISDN-Primary,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.649
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 1
+rangeUpper: 64
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Phone-ISDN-Primary
+adminDescription: Phone-ISDN-Primary
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: primaryInternationalISDNNumber
+schemaIDGUID: 0296c11f-40da-11d1-a9c0-0000f80367c1
+attributeSecurityGUID: 77b5b886-944a-11d1-aebd-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Employee-ID,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.35
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 0
+rangeUpper: 16
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Employee-ID
+adminDescription: Employee-ID
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: employeeID
+schemaIDGUID: bf967962-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Tombstone-Lifetime,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.54
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+mAPIID: 33093
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Tombstone-Lifetime
+adminDescription: Tombstone-Lifetime
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: tombstoneLifetime
+schemaIDGUID: 16c3a860-1273-11d0-a060-00aa006c33ed
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Operating-System-Service-Pack,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.365
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Operating-System-Service-Pack
+adminDescription: Operating-System-Service-Pack
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: operatingSystemServicePack
+schemaIDGUID: 3e978927-8c01-11d0-afda-00c04fd930c9
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Netboot-Initialization,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.358
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Netboot-Initialization
+adminDescription: Netboot-Initialization
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: netbootInitialization
+schemaIDGUID: 3e978920-8c01-11d0-afda-00c04fd930c9
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=User-Principal-Name,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.656
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeUpper: 1024
+showInAdvancedViewOnly: TRUE
+adminDisplayName: User-Principal-Name
+adminDescription: User-Principal-Name
+oMSyntax: 64
+searchFlags: 1
+lDAPDisplayName: userPrincipalName
+schemaIDGUID: 28630ebb-41d5-11d1-a9c1-0000f80367c1
+attributeSecurityGUID: e48d0154-bcf8-11d1-8702-00c04fb96050
+systemOnly: FALSE
+systemFlags: 18
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=Service-Principal-Name,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.771
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Service-Principal-Name
+adminDescription: Service-Principal-Name
+oMSyntax: 64
+searchFlags: 1
+lDAPDisplayName: servicePrincipalName
+schemaIDGUID: f3a64788-5306-11d1-a9c5-0000f80367c1
+attributeSecurityGUID: e48d0154-bcf8-11d1-8702-00c04fb96050
+systemOnly: FALSE
+systemFlags: 18
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=Other-Login-Workstations,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.91
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+rangeLower: 0
+rangeUpper: 1024
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Other-Login-Workstations
+adminDescription: Other-Login-Workstations
+oMSyntax: 64
+searchFlags: 16
+lDAPDisplayName: otherLoginWorkstations
+schemaIDGUID: bf9679f1-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=ms-IIS-FTP-Dir,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1786
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 1
+rangeUpper: 256
+showInAdvancedViewOnly: TRUE
+adminDisplayName: ms-IIS-FTP-Dir
+adminDescription: Relative user directory on an FTP Root share.
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: msIIS-FTPDir
+schemaIDGUID: 8a5c99e9-2230-46eb-b8e8-e59d712eb9ee
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=ms-DS-Site-Affinity,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1443
+attributeSyntax: 2.5.5.10
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: ms-DS-Site-Affinity
+adminDescription: ms-DS-Site-Affinity
+oMSyntax: 4
+searchFlags: 1
+lDAPDisplayName: msDS-Site-Affinity
+schemaIDGUID: c17c5602-bcb7-46f0-9656-6370ca884b72
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Max-Storage,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.76
+attributeSyntax: 2.5.5.16
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Max-Storage
+adminDescription: Max-Storage
+oMSyntax: 65
+searchFlags: 16
+lDAPDisplayName: maxStorage
+schemaIDGUID: bf9679bd-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=NT-Security-Descriptor,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.281
+attributeSyntax: 2.5.5.15
+isSingleValued: TRUE
+rangeLower: 0
+rangeUpper: 132096
+mAPIID: 32787
+showInAdvancedViewOnly: TRUE
+adminDisplayName: NT-Security-Descriptor
+adminDescription: NT-Security-Descriptor
+oMSyntax: 66
+searchFlags: 8
+lDAPDisplayName: nTSecurityDescriptor
+schemaIDGUID: bf9679e3-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 26
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=Site-Object-BL,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.513
+attributeSyntax: 2.5.5.1
+isSingleValued: FALSE
+linkID: 47
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Site-Object-BL
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Site-Object-BL
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: siteObjectBL
+schemaIDGUID: 3e10944d-c354-11d0-aff8-0000f80367c1
+systemOnly: TRUE
+systemFlags: 17
+
+dn: CN=Query-Policy-BL,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.608
+attributeSyntax: 2.5.5.1
+isSingleValued: FALSE
+linkID: 69
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Query-Policy-BL
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Query-Policy-BL
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: queryPolicyBL
+schemaIDGUID: e1aea404-cd5b-11d0-afff-0000f80367c1
+systemOnly: TRUE
+systemFlags: 17
+
+dn: CN=Partial-Attribute-Set,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.640
+attributeSyntax: 2.5.5.10
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Partial-Attribute-Set
+adminDescription: Partial-Attribute-Set
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: partialAttributeSet
+schemaIDGUID: 19405b9e-3cfa-11d1-a9c0-0000f80367c1
+systemOnly: TRUE
+systemFlags: 19
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=Obj-Dist-Name,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 2.5.4.49
+attributeSyntax: 2.5.5.1
+isSingleValued: TRUE
+mAPIID: 32828
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Obj-Dist-Name
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Obj-Dist-Name
+oMSyntax: 127
+searchFlags: 8
+lDAPDisplayName: distinguishedName
+schemaIDGUID: bf9679e4-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: e48d0154-bcf8-11d1-8702-00c04fb96050
+systemOnly: TRUE
+systemFlags: 19
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=Description,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 2.5.4.13
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+rangeLower: 0
+rangeUpper: 1024
+mAPIID: 32879
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Description
+adminDescription: Description
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: description
+schemaIDGUID: bf967950-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: e48d0154-bcf8-11d1-8702-00c04fb96050
+systemOnly: FALSE
+systemFlags: 16
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=ms-DS-Az-Class-ID,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1816
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 0
+rangeUpper: 40
+showInAdvancedViewOnly: TRUE
+adminDisplayName: MS-DS-Az-Class-ID
+adminDescription: A class ID required by the AzRoles UI on the AzApplication object
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: msDS-AzClassId
+schemaIDGUID: 013a7277-5c2d-49ef-a7de-b765b36a3f6f
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=RID-Available-Pool,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.370
+attributeSyntax: 2.5.5.16
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: RID-Available-Pool
+adminDescription: RID-Available-Pool
+oMSyntax: 65
+searchFlags: 0
+lDAPDisplayName: rIDAvailablePool
+schemaIDGUID: 66171888-8f3c-11d0-afda-00c04fd930c9
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Shell-Property-Pages,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.563
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Shell-Property-Pages
+adminDescription: Shell-Property-Pages
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: shellPropertyPages
+schemaIDGUID: 52458039-ca6a-11d0-afff-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=ms-DS-SPN-Suffixes,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1715
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+rangeUpper: 255
+showInAdvancedViewOnly: TRUE
+adminDisplayName: ms-DS-SPN-Suffixes
+adminDescription: ms-DS-SPN-Suffixes
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: msDS-SPNSuffixes
+schemaIDGUID: 789ee1eb-8c8e-4e4c-8cec-79b31b7617b5
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Private-Key,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.101
+attributeSyntax: 2.5.5.10
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Private-Key
+adminDescription: Private-Key
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: privateKey
+schemaIDGUID: bf967a03-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Facsimile-Telephone-Number,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 2.5.4.23
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 1
+rangeUpper: 64
+mAPIID: 14883
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Facsimile-Telephone-Number
+adminDescription: Facsimile-Telephone-Number
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: facsimileTelephoneNumber
+schemaIDGUID: bf967974-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: 77b5b886-944a-11d1-aebd-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Search-Flags,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.334
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+rangeLower: 0
+mAPIID: 33069
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Search-Flags
+adminDescription: Search-Flags
+oMSyntax: 10
+searchFlags: 0
+lDAPDisplayName: searchFlags
+schemaIDGUID: bf967a2d-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Schema-Flags-Ex,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.120
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Schema-Flags-Ex
+adminDescription: Schema-Flags-Ex
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: schemaFlagsEx
+schemaIDGUID: bf967a2b-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Is-Ephemeral,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1212
+attributeSyntax: 2.5.5.8
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Is-Ephemeral
+adminDescription: Is-Ephemeral
+oMSyntax: 1
+searchFlags: 0
+lDAPDisplayName: isEphemeral
+schemaIDGUID: f4c453f0-c5f1-11d1-bbcb-0080c76670c0
+systemOnly: TRUE
+systemFlags: 16
+
+dn: CN=MSMQ-Nt4-Stub,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.960
+attributeSyntax: 2.5.5.9
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: MSMQ-Nt4-Stub
+adminDescription: MSMQ-Nt4-Stub
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: mSMQNt4Stub
+schemaIDGUID: 6f914be6-d57e-11d1-90a2-00c04fd91ab1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=ms-IIS-FTP-Root,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1785
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 1
+rangeUpper: 256
+showInAdvancedViewOnly: TRUE
+adminDisplayName: ms-IIS-FTP-Root
+adminDescription: Virtual FTP Root where user home directory resides.
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: msIIS-FTPRoot
+schemaIDGUID: 2a7827a4-1483-49a5-9d84-52e3812156b4
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Group-Priority,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.345
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Group-Priority
+adminDescription: Group-Priority
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: groupPriority
+schemaIDGUID: eea65905-8ac6-11d0-afda-00c04fd930c9
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Bridgehead-Transport-List,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.819
+attributeSyntax: 2.5.5.1
+isSingleValued: FALSE
+linkID: 98
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Bridgehead-Transport-List
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Bridgehead-Transport-List
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: bridgeheadTransportList
+schemaIDGUID: d50c2cda-8951-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Extended-Class-Info,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.908
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Extended-Class-Info
+adminDescription: Extended-Class-Info
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: extendedClassInfo
+schemaIDGUID: 9a7ad948-ca53-11d1-bbd0-0080c76670c0
+systemOnly: TRUE
+systemFlags: 134217748
+
+dn: CN=Flat-Name,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.511
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Flat-Name
+adminDescription: Flat-Name
+oMSyntax: 64
+searchFlags: 1
+lDAPDisplayName: flatName
+schemaIDGUID: b7b13117-b82e-11d0-afee-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Wbem-Path,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.301
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Wbem-Path
+adminDescription: Wbem-Path
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: wbemPath
+schemaIDGUID: 244b2970-5abd-11d0-afd2-00c04fd930c9
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=ms-DS-NC-Repl-Outbound-Neighbors,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1706
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: ms-DS-NC-Repl-Outbound-Neighbors
+adminDescription: ms-DS-NC-Repl-Outbound-Neighbors
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: msDS-NCReplOutboundNeighbors
+schemaIDGUID: 855f2ef5-a1c5-4cc4-ba6d-32522848b61f
+systemOnly: FALSE
+systemFlags: 20
+
+dn: CN=ms-DS-Operations-For-Az-Task-BL,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1809
+attributeSyntax: 2.5.5.1
+isSingleValued: FALSE
+linkID: 2019
+showInAdvancedViewOnly: TRUE
+adminDisplayName: MS-DS-Operations-For-Az-Task-BL
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Back-link from Az-Operation to Az-Task object(s) linking to it
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: msDS-OperationsForAzTaskBL
+schemaIDGUID: a637d211-5739-4ed1-89b2-88974548bc59
+systemOnly: TRUE
+systemFlags: 17
+
+dn: CN=Show-In-Advanced-View-Only,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.169
+attributeSyntax: 2.5.5.8
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Show-In-Advanced-View-Only
+adminDescription: Show-In-Advanced-View-Only
+oMSyntax: 1
+searchFlags: 17
+lDAPDisplayName: showInAdvancedViewOnly
+schemaIDGUID: bf967984-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: 59ba2f42-79a2-11d0-9020-00c04fc2d3cf
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=ms-DS-Behavior-Version,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1459
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+rangeLower: 0
+showInAdvancedViewOnly: TRUE
+adminDisplayName: ms-DS-Behavior-Version
+adminDescription: ms-DS-Behavior-Version
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: msDS-Behavior-Version
+schemaIDGUID: d31a8757-2447-4545-8081-3bb610cacbf2
+systemOnly: TRUE
+systemFlags: 16
+
+dn: CN=ms-DS-Has-Master-NCs,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1836
+attributeSyntax: 2.5.5.1
+isSingleValued: FALSE
+linkID: 2036
+showInAdvancedViewOnly: TRUE
+adminDisplayName: ms-DS-Has-Master-NCs
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: A list of the naming contexts contained by a DC. Deprecates hasMasterNCs.
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: msDS-hasMasterNCs
+schemaIDGUID: ae2de0e2-59d7-4d47-8d47-ed4dfe4357ad
+systemOnly: TRUE
+systemFlags: 16
+
+dn: CN=Pwd-History-Length,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.95
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+rangeLower: 0
+rangeUpper: 65535
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Pwd-History-Length
+adminDescription: Pwd-History-Length
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: pwdHistoryLength
+schemaIDGUID: bf967a09-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: c7407360-20bf-11d0-a768-00aa006e0529
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Pek-List,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.865
+attributeSyntax: 2.5.5.10
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Pek-List
+adminDescription: Pek-List
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: pekList
+schemaIDGUID: 07383083-91df-11d1-aebc-0000f80367c1
+systemOnly: FALSE
+systemFlags: 17
+
+dn: CN=Postal-Code,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 2.5.4.17
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 1
+rangeUpper: 40
+mAPIID: 14890
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Postal-Code
+adminDescription: Postal-Code
+oMSyntax: 64
+searchFlags: 16
+lDAPDisplayName: postalCode
+schemaIDGUID: bf9679fd-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: 77b5b886-944a-11d1-aebd-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Netboot-Mirror-Data-File,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1241
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Netboot-Mirror-Data-File
+adminDescription: Netboot-Mirror-Data-File
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: netbootMirrorDataFile
+schemaIDGUID: 2df90d85-009f-11d2-aa4c-00c04fd7d83a
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Default-Class-Store,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.213
+attributeSyntax: 2.5.5.1
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Default-Class-Store
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Default-Class-Store
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: defaultClassStore
+schemaIDGUID: bf967948-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=MSMQ-Site-ID,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.953
+attributeSyntax: 2.5.5.10
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: MSMQ-Site-ID
+adminDescription: MSMQ-Site-ID
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: mSMQSiteID
+schemaIDGUID: 9a0dc340-c100-11d1-bbc5-0080c76670c0
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Show-In-Address-Book,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.644
+attributeSyntax: 2.5.5.1
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Show-In-Address-Book
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Show-In-Address-Book
+oMSyntax: 127
+searchFlags: 16
+lDAPDisplayName: showInAddressBook
+schemaIDGUID: 3e74f60e-3e73-11d1-a9c0-0000f80367c1
+attributeSecurityGUID: e48d0154-bcf8-11d1-8702-00c04fb96050
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=When-Created,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.2
+attributeSyntax: 2.5.5.11
+isSingleValued: TRUE
+mAPIID: 12295
+showInAdvancedViewOnly: TRUE
+adminDisplayName: When-Created
+adminDescription: When-Created
+oMSyntax: 24
+searchFlags: 0
+lDAPDisplayName: whenCreated
+schemaIDGUID: bf967a78-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+systemFlags: 18
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=DS-Core-Propagation-Data,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1357
+attributeSyntax: 2.5.5.11
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: DS-Core-Propagation-Data
+adminDescription: DS-Core-Propagation-Data
+oMSyntax: 24
+searchFlags: 0
+lDAPDisplayName: dSCorePropagationData
+schemaIDGUID: d167aa4b-8b08-11d2-9939-0000f87a57d4
+systemOnly: TRUE
+systemFlags: 19
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=Display-Name-Printable,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.353
+attributeSyntax: 2.5.5.5
+isSingleValued: TRUE
+rangeLower: 1
+rangeUpper: 256
+mAPIID: 14847
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Display-Name-Printable
+adminDescription: Display-Name-Printable
+oMSyntax: 19
+searchFlags: 0
+lDAPDisplayName: displayNamePrintable
+schemaIDGUID: bf967954-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: e48d0154-bcf8-11d1-8702-00c04fb96050
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Ipsec-Owners-Reference,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.624
+attributeSyntax: 2.5.5.1
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Ipsec-Owners-Reference
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Ipsec-Owners-Reference
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: ipsecOwnersReference
+schemaIDGUID: b40ff824-427a-11d1-a9c2-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=State-Or-Province-Name,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 2.5.4.8
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 1
+rangeUpper: 128
+mAPIID: 14888
+showInAdvancedViewOnly: TRUE
+adminDisplayName: State-Or-Province-Name
+adminDescription: State-Or-Province-Name
+oMSyntax: 64
+searchFlags: 16
+lDAPDisplayName: st
+schemaIDGUID: bf967a39-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: 77b5b886-944a-11d1-aebd-0000f80367c1
+systemOnly: FALSE
+systemFlags: 18
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=Server-Reference,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.515
+attributeSyntax: 2.5.5.1
+isSingleValued: TRUE
+linkID: 94
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Server-Reference
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Server-Reference
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: serverReference
+schemaIDGUID: 26d9736d-6070-11d1-a9c6-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=ms-DS-Has-Domain-NCs,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1820
+attributeSyntax: 2.5.5.1
+isSingleValued: FALSE
+rangeLower: 4
+rangeUpper: 4
+linkID: 2026
+showInAdvancedViewOnly: TRUE
+adminDisplayName: ms-DS-Has-Domain-NCs
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: DS replication information detailing the domain NCs present on a particular server.
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: msDS-HasDomainNCs
+schemaIDGUID: 6f17e347-a842-4498-b8b3-15e007da4fed
+systemOnly: TRUE
+systemFlags: 16
+
+dn: CN=Invocation-Id,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.115
+attributeSyntax: 2.5.5.10
+isSingleValued: TRUE
+mAPIID: 32959
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Invocation-Id
+adminDescription: Invocation-Id
+oMSyntax: 4
+searchFlags: 1
+lDAPDisplayName: invocationId
+schemaIDGUID: bf96798e-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+systemFlags: 16
+
+dn: CN=Replica-Source,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.109
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Replica-Source
+adminDescription: Replica-Source
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: replicaSource
+schemaIDGUID: bf967a18-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+systemFlags: 16
+
+dn: CN=Phone-Ip-Primary,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.721
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeUpper: 64
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Phone-Ip-Primary
+adminDescription: Phone-Ip-Primary
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: ipPhone
+schemaIDGUID: 4d146e4a-48d4-11d1-a9c3-0000f80367c1
+attributeSecurityGUID: 77b5b886-944a-11d1-aebd-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=Phone-Home-Other,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.277
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+rangeLower: 1
+rangeUpper: 64
+mAPIID: 14895
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Phone-Home-Other
+adminDescription: Phone-Home-Other
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: otherHomePhone
+schemaIDGUID: f0f8ffa2-1191-11d0-a060-00aa006c33ed
+attributeSecurityGUID: 77b5b886-944a-11d1-aebd-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Organization-Name,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 2.5.4.10
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+rangeLower: 1
+rangeUpper: 64
+mAPIID: 33025
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Organization-Name
+adminDescription: Organization-Name
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: o
+schemaIDGUID: bf9679ef-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: e48d0154-bcf8-11d1-8702-00c04fb96050
+systemOnly: FALSE
+systemFlags: 18
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=Extended-Chars-Allowed,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.380
+attributeSyntax: 2.5.5.8
+isSingleValued: TRUE
+mAPIID: 32935
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Extended-Chars-Allowed
+adminDescription: Extended-Chars-Allowed
+oMSyntax: 1
+searchFlags: 0
+lDAPDisplayName: extendedCharsAllowed
+schemaIDGUID: bf967966-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Operating-System,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.363
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Operating-System
+adminDescription: Operating-System
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: operatingSystem
+schemaIDGUID: 3e978925-8c01-11d0-afda-00c04fd930c9
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=ms-DS-Object-Reference,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1840
+attributeSyntax: 2.5.5.1
+isSingleValued: FALSE
+linkID: 2038
+showInAdvancedViewOnly: FALSE
+adminDisplayName: ms-DS-Object-Reference
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: A link to the object that uses the data stored in the object that contains this attribute.
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: msDS-ObjectReference
+schemaIDGUID: 638ec2e8-22e7-409c-85d2-11b21bee72de
+systemOnly: FALSE
+
+dn: CN=MSMQ-Interval1,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1308
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: MSMQ-Interval1
+adminDescription: MSMQ-Interval1
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: mSMQInterval1
+schemaIDGUID: 8ea825aa-3b7b-11d2-90cc-00c04fd91ab1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Rid,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.153
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Rid
+adminDescription: Rid
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: rid
+schemaIDGUID: bf967a22-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Profile-Path,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.139
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Profile-Path
+adminDescription: Profile-Path
+oMSyntax: 64
+searchFlags: 16
+lDAPDisplayName: profilePath
+schemaIDGUID: bf967a05-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: 5f202010-79a5-11d0-9020-00c04fc2d4cf
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=msRADIUSCallbackNumber,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1145
+attributeSyntax: 2.5.5.5
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: msRADIUSCallbackNumber
+adminDescription: msRADIUSCallbackNumber
+oMSyntax: 22
+searchFlags: 0
+lDAPDisplayName: msRADIUSCallbackNumber
+schemaIDGUID: db0c909c-c1f2-11d1-bbc5-0080c76670c0
+attributeSecurityGUID: 037088f8-0ae1-11d2-b422-00a0c968f939
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=ACS-Policy-Name,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.772
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: ACS-Policy-Name
+adminDescription: ACS-Policy-Name
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: aCSPolicyName
+schemaIDGUID: 1cb3559a-56d0-11d1-a9c6-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Comment,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.81
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 1
+rangeUpper: 1024
+mAPIID: 12292
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Comment
+adminDescription: Comment
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: info
+schemaIDGUID: bf96793e-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: 77b5b886-944a-11d1-aebd-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=ms-DS-Object-Reference-BL,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1841
+attributeSyntax: 2.5.5.1
+isSingleValued: FALSE
+linkID: 2039
+showInAdvancedViewOnly: FALSE
+adminDisplayName: ms-DS-Object-Reference-BL
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Back link for ms-DS-Object-Reference.
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: msDS-ObjectReferenceBL
+schemaIDGUID: 2b702515-c1f7-4b3b-b148-c0e4c6ceecb4
+systemOnly: TRUE
+systemFlags: 1
+
+dn: CN=When-Changed,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.3
+attributeSyntax: 2.5.5.11
+isSingleValued: TRUE
+mAPIID: 12296
+showInAdvancedViewOnly: TRUE
+adminDisplayName: When-Changed
+adminDescription: When-Changed
+oMSyntax: 24
+searchFlags: 0
+lDAPDisplayName: whenChanged
+schemaIDGUID: bf967a77-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+systemFlags: 19
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=USN-Last-Obj-Rem,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.121
+attributeSyntax: 2.5.5.16
+isSingleValued: TRUE
+mAPIID: 33110
+showInAdvancedViewOnly: TRUE
+adminDisplayName: USN-Last-Obj-Rem
+adminDescription: USN-Last-Obj-Rem
+oMSyntax: 65
+searchFlags: 0
+lDAPDisplayName: uSNLastObjRem
+schemaIDGUID: bf967a73-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+systemFlags: 19
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=Reps-To,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.83
+attributeSyntax: 2.5.5.10
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Reps-To
+oMObjectClass:: KoZIhvcUAQEBBg==
+adminDescription: Reps-To
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: repsTo
+schemaIDGUID: bf967a1e-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+systemFlags: 19
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=Repl-UpToDate-Vector,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.4
+attributeSyntax: 2.5.5.10
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Repl-UpToDate-Vector
+adminDescription: Repl-UpToDate-Vector
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: replUpToDateVector
+schemaIDGUID: bf967a16-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+systemFlags: 19
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=netboot-SCP-BL,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.864
+attributeSyntax: 2.5.5.1
+isSingleValued: FALSE
+linkID: 101
+showInAdvancedViewOnly: TRUE
+adminDisplayName: netboot-SCP-BL
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: netboot-SCP-BL
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: netbootSCPBL
+schemaIDGUID: 07383082-91df-11d1-aebc-0000f80367c1
+systemOnly: TRUE
+systemFlags: 17
+
+dn: CN=ms-DS-Mastered-By,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1837
+attributeSyntax: 2.5.5.1
+isSingleValued: FALSE
+linkID: 2037
+showInAdvancedViewOnly: TRUE
+adminDisplayName: ms-DS-Mastered-By
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Back link for msDS-hasMasterNCs.
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: msDs-masteredBy
+schemaIDGUID: 60234769-4819-4615-a1b2-49d2f119acb5
+systemOnly: TRUE
+systemFlags: 17
+
+dn: CN=ms-COM-PartitionSetLink,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1424
+attributeSyntax: 2.5.5.1
+isSingleValued: FALSE
+linkID: 1041
+showInAdvancedViewOnly: TRUE
+adminDisplayName: ms-COM-PartitionSetLink
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Link from a Partition to a PartitionSet. Default = adminDisplayName
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: msCOM-PartitionSetLink
+schemaIDGUID: 67f121dc-7d02-4c7d-82f5-9ad4c950ac34
+systemOnly: TRUE
+systemFlags: 17
+
+dn: CN=Common-Name,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 2.5.4.3
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 1
+rangeUpper: 64
+mAPIID: 14863
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Common-Name
+adminDescription: Common-Name
+oMSyntax: 64
+searchFlags: 1
+lDAPDisplayName: cn
+schemaIDGUID: bf96793f-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: e48d0154-bcf8-11d1-8702-00c04fb96050
+systemOnly: FALSE
+systemFlags: 18
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=MS-DS-All-Users-Trust-Quota,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1789
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: MS-DS-All-Users-Trust-Quota
+adminDescription: Used to enforce a combined users quota on the total number of Trusted-Domain objects created by using the control access right, "Create inbound Forest trust".
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: msDS-AllUsersTrustQuota
+schemaIDGUID: d3aa4a5c-4e03-4810-97aa-2b339e7a434b
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Default-Group,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.480
+attributeSyntax: 2.5.5.1
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Default-Group
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Default-Group
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: defaultGroup
+schemaIDGUID: 720bc4e2-a54a-11d0-afdf-00c04fd930c9
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Ipsec-Filter-Reference,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.629
+attributeSyntax: 2.5.5.1
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Ipsec-Filter-Reference
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Ipsec-Filter-Reference
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: ipsecFilterReference
+schemaIDGUID: b40ff823-427a-11d1-a9c2-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=User-Comment,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.156
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: User-Comment
+adminDescription: User-Comment
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: comment
+schemaIDGUID: bf967a6a-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: 59ba2f42-79a2-11d0-9020-00c04fc2d3cf
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=ms-ds-Schema-Extensions,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1440
+attributeSyntax: 2.5.5.10
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: ms-ds-Schema-Extensions
+adminDescription: ms-ds-Schema-Extensions
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: msDs-Schema-Extensions
+schemaIDGUID: b39a61be-ed07-4cab-9a4a-4963ed0141e1
+systemOnly: TRUE
+systemFlags: 16
+
+dn: CN=Local-Policy-Flags,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.56
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Local-Policy-Flags
+adminDescription: Local-Policy-Flags
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: localPolicyFlags
+schemaIDGUID: bf96799e-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=MSMQ-Interval2,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1309
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: MSMQ-Interval2
+adminDescription: MSMQ-Interval2
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: mSMQInterval2
+schemaIDGUID: 99b88f52-3b7b-11d2-90cc-00c04fd91ab1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=SID-History,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.609
+attributeSyntax: 2.5.5.17
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: SID-History
+adminDescription: SID-History
+oMSyntax: 4
+searchFlags: 1
+lDAPDisplayName: sIDHistory
+schemaIDGUID: 17eb4278-d167-11d0-b002-0000f80367c1
+attributeSecurityGUID: 59ba2f42-79a2-11d0-9020-00c04fc2d3cf
+systemOnly: FALSE
+systemFlags: 18
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=Unicode-Pwd,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.90
+attributeSyntax: 2.5.5.10
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Unicode-Pwd
+adminDescription: Unicode-Pwd
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: unicodePwd
+schemaIDGUID: bf9679e1-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=msRASSavedFramedIPAddress,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1190
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: msRASSavedFramedIPAddress
+adminDescription: msRASSavedFramedIPAddress
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: msRASSavedFramedIPAddress
+schemaIDGUID: db0c90c6-c1f2-11d1-bbc5-0080c76670c0
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=MS-DRM-Identity-Certificate,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1843
+attributeSyntax: 2.5.5.10
+isSingleValued: FALSE
+rangeLower: 1
+rangeUpper: 10240
+showInAdvancedViewOnly: TRUE
+adminDisplayName: ms-DRM-Identity-Certificate
+adminDescription: The XrML digital rights management certificates for this user.
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: msDRM-IdentityCertificate
+schemaIDGUID: e85e1204-3434-41ad-9b56-e2901228fff0
+systemFlags: 16
+
+dn: CN=Last-Logoff,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.51
+attributeSyntax: 2.5.5.16
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Last-Logoff
+adminDescription: Last-Logoff
+oMSyntax: 65
+searchFlags: 0
+lDAPDisplayName: lastLogoff
+schemaIDGUID: bf967996-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: 5f202010-79a5-11d0-9020-00c04fc2d4cf
+systemOnly: FALSE
+systemFlags: 17
+
+dn: CN=DMD-Name,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.598
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 1
+rangeUpper: 1024
+mAPIID: 35926
+showInAdvancedViewOnly: TRUE
+adminDisplayName: DMD-Name
+adminDescription: DMD-Name
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: dmdName
+schemaIDGUID: 167757b9-47f3-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=ms-Exch-LabeledURI,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.593
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+rangeLower: 1
+rangeUpper: 1024
+mAPIID: 35921
+adminDisplayName: ms-Exch-LabeledURI
+adminDescription: ms-Exch-LabeledURI
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: msExchLabeledURI
+schemaIDGUID: 16775820-47f3-11d1-a9c3-0000f80367c1
+
+dn: CN=Reports,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.436
+attributeSyntax: 2.5.5.1
+isSingleValued: FALSE
+mAPIID: 32782
+linkID: 43
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Reports
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Reports
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: directReports
+schemaIDGUID: bf967a1c-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: e48d0154-bcf8-11d1-8702-00c04fb96050
+systemOnly: TRUE
+systemFlags: 17
+
+dn: CN=Repl-Property-Meta-Data,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.3
+attributeSyntax: 2.5.5.10
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Repl-Property-Meta-Data
+adminDescription: Repl-Property-Meta-Data
+oMSyntax: 4
+searchFlags: 8
+lDAPDisplayName: replPropertyMetaData
+schemaIDGUID: 281416c0-1968-11d0-a28f-00aa003049e2
+systemOnly: TRUE
+systemFlags: 27
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=From-Entry,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.910
+attributeSyntax: 2.5.5.8
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: From-Entry
+adminDescription: From-Entry
+oMSyntax: 1
+searchFlags: 0
+lDAPDisplayName: fromEntry
+schemaIDGUID: 9a7ad949-ca53-11d1-bbd0-0080c76670c0
+systemOnly: TRUE
+systemFlags: 134217748
+
+dn: CN=Trust-Parent,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.471
+attributeSyntax: 2.5.5.1
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Trust-Parent
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Trust-Parent
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: trustParent
+schemaIDGUID: b000ea7a-a086-11d0-afdd-00c04fd930c9
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Ipsec-Data-Type,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.622
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Ipsec-Data-Type
+adminDescription: Ipsec-Data-Type
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: ipsecDataType
+schemaIDGUID: b40ff81e-427a-11d1-a9c2-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Ipsec-Data,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.623
+attributeSyntax: 2.5.5.10
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Ipsec-Data
+adminDescription: Ipsec-Data
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: ipsecData
+schemaIDGUID: b40ff81f-427a-11d1-a9c2-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=RID-Manager-Reference,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.368
+attributeSyntax: 2.5.5.1
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: RID-Manager-Reference
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: RID-Manager-Reference
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: rIDManagerReference
+schemaIDGUID: 66171886-8f3c-11d0-afda-00c04fd930c9
+systemOnly: TRUE
+systemFlags: 16
+
+dn: CN=Lockout-Threshold,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.73
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+rangeUpper: 65535
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Lockout-Threshold
+adminDescription: Lockout-Threshold
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: lockoutThreshold
+schemaIDGUID: bf9679a6-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: c7407360-20bf-11d0-a768-00aa006e0529
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Desktop-Profile,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.346
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Desktop-Profile
+adminDescription: Desktop-Profile
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: desktopProfile
+schemaIDGUID: eea65906-8ac6-11d0-afda-00c04fd930c9
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Text-Country,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.131
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 1
+rangeUpper: 128
+mAPIID: 14886
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Text-Country
+adminDescription: Text-Country
+oMSyntax: 64
+searchFlags: 16
+lDAPDisplayName: co
+schemaIDGUID: f0f8ffa7-1191-11d0-a060-00aa006c33ed
+attributeSecurityGUID: e48d0154-bcf8-11d1-8702-00c04fb96050
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Teletex-Terminal-Identifier,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 2.5.4.22
+attributeSyntax: 2.5.5.10
+isSingleValued: FALSE
+mAPIID: 33091
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Teletex-Terminal-Identifier
+adminDescription: Teletex-Terminal-Identifier
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: teletexTerminalIdentifier
+schemaIDGUID: bf967a4a-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: 77b5b886-944a-11d1-aebd-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Telex-Primary,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.648
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 1
+rangeUpper: 64
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Telex-Primary
+adminDescription: Telex-Primary
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: primaryTelexNumber
+schemaIDGUID: 0296c121-40da-11d1-a9c0-0000f80367c1
+attributeSecurityGUID: 77b5b886-944a-11d1-aebd-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Manager,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 0.9.2342.19200300.100.1.10
+attributeSyntax: 2.5.5.1
+isSingleValued: TRUE
+mAPIID: 32773
+linkID: 42
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Manager
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Manager
+oMSyntax: 127
+searchFlags: 16
+lDAPDisplayName: manager
+schemaIDGUID: bf9679b5-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: e48d0154-bcf8-11d1-8702-00c04fb96050
+systemOnly: FALSE
+systemFlags: 16
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=Physical-Location-Object,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.514
+attributeSyntax: 2.5.5.1
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Physical-Location-Object
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Physical-Location-Object
+oMSyntax: 127
+searchFlags: 1
+lDAPDisplayName: physicalLocationObject
+schemaIDGUID: b7b13119-b82e-11d0-afee-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=ms-DS-Az-Major-Version,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1824
+attributeSyntax: 2.5.5.9
+isSingleValued: TRUE
+rangeLower: 1
+showInAdvancedViewOnly: TRUE
+adminDisplayName: MS-DS-Az-Major-Version
+adminDescription: Major version number for AzRoles
+oMSyntax: 2
+searchFlags: 0
+lDAPDisplayName: msDS-AzMajorVersion
+schemaIDGUID: cfb9adb7-c4b7-4059-9568-1ed9db6b7248
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Sub-Class-Of,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.21
+attributeSyntax: 2.5.5.2
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Sub-Class-Of
+adminDescription: Sub-Class-Of
+oMSyntax: 6
+searchFlags: 8
+lDAPDisplayName: subClassOf
+schemaIDGUID: bf967a3b-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+systemFlags: 16
+
+dn: CN=System-Must-Contain,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.197
+attributeSyntax: 2.5.5.2
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: System-Must-Contain
+adminDescription: System-Must-Contain
+oMSyntax: 6
+searchFlags: 0
+lDAPDisplayName: systemMustContain
+schemaIDGUID: bf967a45-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+systemFlags: 16
+
+dn: CN=roomNumber,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 0.9.2342.19200300.100.1.6
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+showInAdvancedViewOnly: FALSE
+adminDisplayName: roomNumber
+adminDescription: The room number of an object.
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: roomNumber
+schemaIDGUID: 81d7f8c2-e327-4a0d-91c6-b42d4009115f
+systemOnly: FALSE
+systemFlags: 0
+
+dn: CN=Employee-Type,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.613
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 1
+rangeUpper: 256
+mAPIID: 35945
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Employee-Type
+adminDescription: Employee-Type
+oMSyntax: 64
+searchFlags: 16
+lDAPDisplayName: employeeType
+schemaIDGUID: a8df73f0-c5ea-11d1-bbcb-0080c76670c0
+systemOnly: FALSE
+systemFlags: 0
+
+dn: CN=Current-Value,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.27
+attributeSyntax: 2.5.5.10
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Current-Value
+adminDescription: Current-Value
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: currentValue
+schemaIDGUID: bf967947-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=DIT-Content-Rules,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 2.5.21.2
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: DIT-Content-Rules
+adminDescription: DIT-Content-Rules
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: dITContentRules
+schemaIDGUID: 9a7ad946-ca53-11d1-bbd0-0080c76670c0
+systemOnly: TRUE
+systemFlags: 134217748
+
+dn: CN=GPC-Machine-Extension-Names,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1348
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: GPC-Machine-Extension-Names
+adminDescription: GPC-Machine-Extension-Names
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: gPCMachineExtensionNames
+schemaIDGUID: 32ff8ecc-783f-11d2-9916-0000f87a57d4
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=USN-Created,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.19
+attributeSyntax: 2.5.5.16
+isSingleValued: TRUE
+mAPIID: 33108
+showInAdvancedViewOnly: TRUE
+adminDisplayName: USN-Created
+adminDescription: USN-Created
+oMSyntax: 65
+searchFlags: 9
+lDAPDisplayName: uSNCreated
+schemaIDGUID: bf967a70-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+systemFlags: 19
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=Sub-Refs,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.7
+attributeSyntax: 2.5.5.1
+isSingleValued: FALSE
+mAPIID: 33083
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Sub-Refs
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Sub-Refs
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: subRefs
+schemaIDGUID: bf967a3c-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+systemFlags: 19
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=Proxy-Addresses,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.210
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+rangeLower: 1
+rangeUpper: 1123
+mAPIID: 32783
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Proxy-Addresses
+adminDescription: Proxy-Addresses
+oMSyntax: 64
+searchFlags: 5
+lDAPDisplayName: proxyAddresses
+schemaIDGUID: bf967a06-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: e48d0154-bcf8-11d1-8702-00c04fb96050
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Superior-DNS-Root,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.532
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Superior-DNS-Root
+adminDescription: Superior-DNS-Root
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: superiorDNSRoot
+schemaIDGUID: 5245801d-ca6a-11d0-afff-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Root-Trust,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.674
+attributeSyntax: 2.5.5.1
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Root-Trust
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Root-Trust
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: rootTrust
+schemaIDGUID: 7bfdcb80-4807-11d1-a9c3-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Shell-Context-Menu,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.615
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Shell-Context-Menu
+adminDescription: Shell-Context-Menu
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: shellContextMenu
+schemaIDGUID: 553fd039-f32e-11d0-b0bc-00c04fd8dca6
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Class-Display-Name,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.610
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Class-Display-Name
+adminDescription: Class-Display-Name
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: classDisplayName
+schemaIDGUID: 548e1c22-dea6-11d0-b010-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Ipsec-Name,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.620
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Ipsec-Name
+adminDescription: Ipsec-Name
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: ipsecName
+schemaIDGUID: b40ff81c-427a-11d1-a9c2-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=CA-Certificate,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 2.5.4.37
+attributeSyntax: 2.5.5.10
+isSingleValued: FALSE
+rangeLower: 1
+rangeUpper: 32768
+mAPIID: 32771
+showInAdvancedViewOnly: TRUE
+adminDisplayName: CA-Certificate
+adminDescription: CA-Certificate
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: cACertificate
+schemaIDGUID: bf967932-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=Ipsec-Negotiation-Policy-Reference,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.628
+attributeSyntax: 2.5.5.1
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Ipsec-Negotiation-Policy-Reference
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Ipsec-Negotiation-Policy-Reference
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: ipsecNegotiationPolicyReference
+schemaIDGUID: b40ff822-427a-11d1-a9c2-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=MHS-OR-Address,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.650
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: MHS-OR-Address
+adminDescription: MHS-OR-Address
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: mhsORAddress
+schemaIDGUID: 0296c122-40da-11d1-a9c0-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Nt-Pwd-History,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.94
+attributeSyntax: 2.5.5.10
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Nt-Pwd-History
+adminDescription: Nt-Pwd-History
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: ntPwdHistory
+schemaIDGUID: bf9679e2-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=SMTP-Mail-Address,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.786
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: SMTP-Mail-Address
+adminDescription: SMTP-Mail-Address
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: mailAddress
+schemaIDGUID: 26d9736f-6070-11d1-a9c6-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Foreign-Identifier,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.356
+attributeSyntax: 2.5.5.10
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Foreign-Identifier
+adminDescription: Foreign-Identifier
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: foreignIdentifier
+schemaIDGUID: 3e97891e-8c01-11d0-afda-00c04fd930c9
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=USN-Changed,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.120
+attributeSyntax: 2.5.5.16
+isSingleValued: TRUE
+mAPIID: 32809
+showInAdvancedViewOnly: TRUE
+adminDisplayName: USN-Changed
+adminDescription: USN-Changed
+oMSyntax: 65
+searchFlags: 9
+lDAPDisplayName: uSNChanged
+schemaIDGUID: bf967a6f-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+systemFlags: 19
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=Reps-From,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.91
+attributeSyntax: 2.5.5.10
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Reps-From
+oMObjectClass:: KoZIhvcUAQEBBg==
+adminDescription: Reps-From
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: repsFrom
+schemaIDGUID: bf967a1d-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+systemFlags: 19
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=Other-Well-Known-Objects,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1359
+attributeSyntax: 2.5.5.7
+isSingleValued: FALSE
+rangeLower: 16
+rangeUpper: 16
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Other-Well-Known-Objects
+oMObjectClass:: KoZIhvcUAQEBCw==
+adminDescription: Other-Well-Known-Objects
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: otherWellKnownObjects
+schemaIDGUID: 1ea64e5d-ac0f-11d2-90df-00c04fd91ab1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=ms-DS-NC-Repl-Cursors,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1704
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: ms-DS-NC-Repl-Cursors
+adminDescription: ms-DS-NC-Repl-Cursors
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: msDS-NCReplCursors
+schemaIDGUID: 8a167ce4-f9e8-47eb-8d78-f7fe80abb2cc
+systemOnly: FALSE
+systemFlags: 20
+
+dn: CN=Managed-Objects,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.654
+attributeSyntax: 2.5.5.1
+isSingleValued: FALSE
+mAPIID: 32804
+linkID: 73
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Managed-Objects
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: Managed-Objects
+oMSyntax: 127
+searchFlags: 0
+lDAPDisplayName: managedObjects
+schemaIDGUID: 0296c124-40da-11d1-a9c0-0000f80367c1
+systemOnly: TRUE
+systemFlags: 17
+
+dn: CN=ms-DS-Allowed-DNS-Suffixes,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1710
+attributeSyntax: 2.5.5.12
+isSingleValued: FALSE
+rangeLower: 0
+rangeUpper: 2048
+showInAdvancedViewOnly: TRUE
+adminDisplayName: ms-DS-Allowed-DNS-Suffixes
+adminDescription: Allowed suffixes for dNSHostName on computer
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: msDS-AllowedDNSSuffixes
+schemaIDGUID: 8469441b-9ac4-4e45-8205-bd219dbf672d
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=NC-Name,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.2.16
+attributeSyntax: 2.5.5.1
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: NC-Name
+oMObjectClass:: KwwCh3McAIVK
+adminDescription: NC-Name
+oMSyntax: 127
+searchFlags: 8
+lDAPDisplayName: nCName
+schemaIDGUID: bf9679d6-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+systemFlags: 16
+
+dn: CN=NETBIOS-Name,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.87
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+rangeLower: 1
+rangeUpper: 16
+showInAdvancedViewOnly: TRUE
+adminDisplayName: NETBIOS-Name
+adminDescription: NETBIOS-Name
+oMSyntax: 64
+searchFlags: 1
+lDAPDisplayName: nETBIOSName
+schemaIDGUID: bf9679d8-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Query-Filter,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1355
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Query-Filter
+adminDescription: Query-Filter
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: queryFilter
+schemaIDGUID: cbf70a26-7e78-11d2-9921-0000f87a57d4
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Preferred-Delivery-Method,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 2.5.4.28
+attributeSyntax: 2.5.5.9
+isSingleValued: FALSE
+mAPIID: 33037
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Preferred-Delivery-Method
+adminDescription: Preferred-Delivery-Method
+oMSyntax: 10
+searchFlags: 0
+lDAPDisplayName: preferredDeliveryMethod
+schemaIDGUID: bf9679fe-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: 77b5b886-944a-11d1-aebd-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=MSMQ-Site-Foreign,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.961
+attributeSyntax: 2.5.5.8
+isSingleValued: FALSE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: MSMQ-Site-Foreign
+adminDescription: MSMQ-Site-Foreign
+oMSyntax: 1
+searchFlags: 0
+lDAPDisplayName: mSMQSiteForeign
+schemaIDGUID: fd129d8a-d57e-11d1-90a2-00c04fd91ab1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=audio,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 0.9.2342.19200300.100.1.55
+attributeSyntax: 2.5.5.10
+isSingleValued: FALSE
+rangeUpper: 250000
+showInAdvancedViewOnly: FALSE
+adminDisplayName: audio
+adminDescription: The Audio attribute type allows the storing of sounds in the Directory.
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: audio
+schemaIDGUID: d0e1d224-e1a0-42ce-a2da-793ba5244f35
+systemOnly: FALSE
+systemFlags: 0
+
+dn: CN=Script-Path,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.62
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Script-Path
+adminDescription: Script-Path
+oMSyntax: 64
+searchFlags: 16
+lDAPDisplayName: scriptPath
+schemaIDGUID: bf9679a8-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: 5f202010-79a5-11d0-9020-00c04fc2d4cf
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=MSMQ-Digests,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.948
+attributeSyntax: 2.5.5.10
+isSingleValued: FALSE
+rangeLower: 16
+rangeUpper: 16
+showInAdvancedViewOnly: TRUE
+adminDisplayName: MSMQ-Digests
+adminDescription: MSMQ-Digests
+oMSyntax: 4
+searchFlags: 1
+lDAPDisplayName: mSMQDigests
+schemaIDGUID: 9a0dc33c-c100-11d1-bbc5-0080c76670c0
+attributeSecurityGUID: 77b5b886-944a-11d1-aebd-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+isMemberOfPartialAttributeSet: TRUE
+
+dn: CN=ms-DS-Cached-Membership,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.1441
+attributeSyntax: 2.5.5.10
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: ms-DS-Cached-Membership
+adminDescription: ms-DS-Cached-Membership
+oMSyntax: 4
+searchFlags: 0
+lDAPDisplayName: msDS-Cached-Membership
+schemaIDGUID: 69cab008-cdd4-4bc9-bab8-0ff37efe1b20
+systemOnly: FALSE
+systemFlags: 17
+
+dn: CN=Logon-Hours,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.64
+attributeSyntax: 2.5.5.10
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Logon-Hours
+adminDescription: Logon-Hours
+oMSyntax: 4
+searchFlags: 16
+lDAPDisplayName: logonHours
+schemaIDGUID: bf9679ab-0de6-11d0-a285-00aa003049e2
+attributeSecurityGUID: 5f202010-79a5-11d0-9020-00c04fc2d4cf
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=GPC-File-Sys-Path,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+attributeID: 1.2.840.113556.1.4.894
+attributeSyntax: 2.5.5.12
+isSingleValued: TRUE
+showInAdvancedViewOnly: TRUE
+adminDisplayName: GPC-File-Sys-Path
+adminDescription: GPC-File-Sys-Path
+oMSyntax: 64
+searchFlags: 0
+lDAPDisplayName: gPCFileSysPath
+schemaIDGUID: f30e3bc1-9ff0-11d1-b603-0000f80367c1
+systemOnly: FALSE
+systemFlags: 16
+
+dn: CN=Top,${SCHEMADN}
+objectClass: top
+objectClass: classSchema
+subClassOf: top
+governsID: 2.5.6.0
+mayContain: msDS-ObjectReferenceBL
+rDNAttID: cn
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Top
+adminDescription: Top
+objectClassCategory: 2
+lDAPDisplayName: top
+schemaIDGUID: bf967ab7-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+systemPossSuperiors: lostAndFound
+systemMayContain: url
+systemMayContain: wWWHomePage
+systemMayContain: whenCreated
+systemMayContain: whenChanged
+systemMayContain: wellKnownObjects
+systemMayContain: wbemPath
+systemMayContain: uSNSource
+systemMayContain: uSNLastObjRem
+systemMayContain: USNIntersite
+systemMayContain: uSNDSALastObjRemoved
+systemMayContain: uSNCreated
+systemMayContain: uSNChanged
+systemMayContain: systemFlags
+systemMayContain: subSchemaSubEntry
+systemMayContain: subRefs
+systemMayContain: structuralObjectClass
+systemMayContain: siteObjectBL
+systemMayContain: serverReferenceBL
+systemMayContain: sDRightsEffective
+systemMayContain: revision
+systemMayContain: repsTo
+systemMayContain: repsFrom
+systemMayContain: directReports
+systemMayContain: replUpToDateVector
+systemMayContain: replPropertyMetaData
+systemMayContain: name
+systemMayContain: queryPolicyBL
+systemMayContain: proxyAddresses
+systemMayContain: proxiedObjectName
+systemMayContain: possibleInferiors
+systemMayContain: partialAttributeSet
+systemMayContain: partialAttributeDeletionList
+systemMayContain: otherWellKnownObjects
+systemMayContain: objectVersion
+systemMayContain: objectGUID
+systemMayContain: distinguishedName
+systemMayContain: nonSecurityMemberBL
+systemMayContain: netbootSCPBL
+systemMayContain: ownerBL
+systemMayContain: msDS-ReplValueMetaData
+systemMayContain: msDS-ReplAttributeMetaData
+systemMayContain: msDS-NonMembersBL
+systemMayContain: msDS-NCReplOutboundNeighbors
+systemMayContain: msDS-NCReplInboundNeighbors
+systemMayContain: msDS-NCReplCursors
+systemMayContain: msDS-TasksForAzRoleBL
+systemMayContain: msDS-TasksForAzTaskBL
+systemMayContain: msDS-OperationsForAzRoleBL
+systemMayContain: msDS-OperationsForAzTaskBL
+systemMayContain: msDS-MembersForAzRoleBL
+systemMayContain: msDs-masteredBy
+systemMayContain: mS-DS-ConsistencyGuid
+systemMayContain: mS-DS-ConsistencyChildCount
+systemMayContain: msDS-Approx-Immed-Subordinates
+systemMayContain: msCOM-PartitionSetLink
+systemMayContain: msCOM-UserLink
+systemMayContain: modifyTimeStamp
+systemMayContain: masteredBy
+systemMayContain: managedObjects
+systemMayContain: lastKnownParent
+systemMayContain: isPrivilegeHolder
+systemMayContain: memberOf
+systemMayContain: isDeleted
+systemMayContain: isCriticalSystemObject
+systemMayContain: showInAdvancedViewOnly
+systemMayContain: fSMORoleOwner
+systemMayContain: fRSMemberReferenceBL
+systemMayContain: frsComputerReferenceBL
+systemMayContain: fromEntry
+systemMayContain: flags
+systemMayContain: extensionName
+systemMayContain: dSASignature
+systemMayContain: dSCorePropagationData
+systemMayContain: displayNamePrintable
+systemMayContain: displayName
+systemMayContain: description
+systemMayContain: createTimeStamp
+systemMayContain: cn
+systemMayContain: canonicalName
+systemMayContain: bridgeheadServerListBL
+systemMayContain: allowedChildClassesEffective
+systemMayContain: allowedChildClasses
+systemMayContain: allowedAttributesEffective
+systemMayContain: allowedAttributes
+systemMayContain: adminDisplayName
+systemMayContain: adminDescription
+systemMustContain: objectClass
+systemMustContain: objectCategory
+systemMustContain: nTSecurityDescriptor
+systemMustContain: instanceType
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+systemFlags: 16
+defaultHidingValue: TRUE
+defaultObjectCategory: CN=Top,${SCHEMADN}
+
+dn: CN=Ipsec-ISAKMP-Policy,${SCHEMADN}
+objectClass: top
+objectClass: classSchema
+subClassOf: ipsecBase
+governsID: 1.2.840.113556.1.5.120
+rDNAttID: cn
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Ipsec-ISAKMP-Policy
+adminDescription: Ipsec-ISAKMP-Policy
+objectClassCategory: 1
+lDAPDisplayName: ipsecISAKMPPolicy
+schemaIDGUID: b40ff828-427a-11d1-a9c2-0000f80367c1
+systemOnly: FALSE
+systemPossSuperiors: container
+systemPossSuperiors: computer
+systemPossSuperiors: organizationalUnit
+defaultSecurityDescriptor: D:
+systemFlags: 16
+defaultHidingValue: TRUE
+defaultObjectCategory: CN=Ipsec-ISAKMP-Policy,${SCHEMADN}
+
+dn: CN=Domain-DNS,${SCHEMADN}
+objectClass: top
+objectClass: classSchema
+subClassOf: domain
+governsID: 1.2.840.113556.1.5.67
+possibleInferiors: group
+possibleInferiors: lostAndFound
+possibleInferiors: builtinDomain
+possibleInferiors: computer
+possibleInferiors: user
+possibleInferiors: container
+possibleInferiors: groupPolicyContainer
+possibleInferiors: organization
+possibleInferiors: domainDNS
+possibleInferiors: locality
+possibleInferiors: msDS-AzAdminManager
+possibleInferiors: country
+possibleInferiors: organizationalUnit
+rDNAttID: dc
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Domain-DNS
+adminDescription: Domain-DNS
+objectClassCategory: 1
+lDAPDisplayName: domainDNS
+schemaIDGUID: 19195a5b-6da0-11d0-afd3-00c04fd930c9
+systemOnly: FALSE
+systemPossSuperiors: domainDNS
+systemMayContain: msDS-Behavior-Version
+systemMayContain: msDS-AllowedDNSSuffixes
+systemMayContain: managedBy
+systemAuxiliaryClass: samDomain
+defaultSecurityDescriptor: D:(A;;RP;;;WD)(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)(A;;RPLCLORC;;;AU)(A;;RPWPCRLCLOCCRCWDWOSW;;;DA)(A;CI;RPWPCRLCLOCCRCWDWOSDSW;;;BA)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;SY)(A;CI;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;EA)(A;CI;LC;;;RU)(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)(A;;RPRC;;;RU)(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(A;;LCRPLORC;;;ED)(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)(OA;CIIO;RPLCLORC;;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;S-1-5-32-557)(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)S:(AU;SA;WDWOWP;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)
+systemFlags: 16
+defaultHidingValue: FALSE
+defaultObjectCategory: CN=Domain-DNS,${SCHEMADN}
+
+dn: CN=ms-DS-Az-Application,${SCHEMADN}
+objectClass: top
+objectClass: classSchema
+subClassOf: top
+governsID: 1.2.840.113556.1.5.235
+possibleInferiors: group
+possibleInferiors: container
+possibleInferiors: msDS-AzScope
+possibleInferiors: groupPolicyContainer
+rDNAttID: cn
+showInAdvancedViewOnly: TRUE
+adminDisplayName: MS-DS-Az-Application
+adminDescription: Defines an installed instance of an application bound to a particular policy store.
+objectClassCategory: 1
+lDAPDisplayName: msDS-AzApplication
+schemaIDGUID: ddf8de9b-cba5-4e12-842e-28d8b66f75ec
+systemOnly: FALSE
+systemPossSuperiors: msDS-AzAdminManager
+systemMayContain: msDS-AzApplicationData
+systemMayContain: msDS-AzGenerateAudits
+systemMayContain: msDS-AzApplicationVersion
+systemMayContain: msDS-AzClassId
+systemMayContain: msDS-AzApplicationName
+systemMayContain: description
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;CO)
+systemFlags: 16
+defaultHidingValue: TRUE
+defaultObjectCategory: CN=ms-DS-Az-Application,${SCHEMADN}
+
+dn: CN=Builtin-Domain,${SCHEMADN}
+objectClass: top
+objectClass: classSchema
+subClassOf: top
+governsID: 1.2.840.113556.1.5.4
+possibleInferiors: group
+possibleInferiors: computer
+possibleInferiors: user
+rDNAttID: cn
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Builtin-Domain
+adminDescription: Builtin-Domain
+objectClassCategory: 1
+lDAPDisplayName: builtinDomain
+schemaIDGUID: bf967a81-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemPossSuperiors: domainDNS
+systemAuxiliaryClass: samDomainBase
+defaultSecurityDescriptor: D:(A;;RPLCLORC;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+systemFlags: 16
+defaultHidingValue: TRUE
+defaultObjectCategory: CN=Builtin-Domain,${SCHEMADN}
+
+dn: CN=Infrastructure-Update,${SCHEMADN}
+objectClass: top
+objectClass: classSchema
+subClassOf: top
+governsID: 1.2.840.113556.1.5.175
+rDNAttID: cn
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Infrastructure-Update
+adminDescription: Infrastructure-Update
+objectClassCategory: 1
+lDAPDisplayName: infrastructureUpdate
+schemaIDGUID: 2df90d89-009f-11d2-aa4c-00c04fd7d83a
+systemOnly: TRUE
+systemPossSuperiors: infrastructureUpdate
+systemPossSuperiors: domain
+systemMayContain: dNReferenceUpdate
+defaultSecurityDescriptor: D:(A;;GA;;;SY)
+systemFlags: 16
+defaultHidingValue: TRUE
+defaultObjectCategory: CN=Infrastructure-Update,${SCHEMADN}
+
+dn: CN=Configuration,${SCHEMADN}
+objectClass: top
+objectClass: classSchema
+subClassOf: top
+governsID: 1.2.840.113556.1.5.12
+possibleInferiors: lostAndFound
+possibleInferiors: sitesContainer
+possibleInferiors: container
+possibleInferiors: groupPolicyContainer
+rDNAttID: cn
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Configuration
+adminDescription: Configuration
+objectClassCategory: 1
+lDAPDisplayName: configuration
+schemaIDGUID: bf967a87-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+systemPossSuperiors: domainDNS
+systemMayContain: gPOptions
+systemMayContain: gPLink
+systemMustContain: cn
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+systemFlags: 16
+defaultHidingValue: TRUE
+defaultObjectCategory: CN=Configuration,${SCHEMADN}
+
+dn: CN=Cross-Ref,${SCHEMADN}
+objectClass: top
+objectClass: classSchema
+subClassOf: top
+governsID: 1.2.840.113556.1.3.11
+rDNAttID: cn
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Cross-Ref
+adminDescription: Cross-Ref
+objectClassCategory: 1
+lDAPDisplayName: crossRef
+schemaIDGUID: bf967a8d-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemPossSuperiors: crossRefContainer
+systemMayContain: trustParent
+systemMayContain: superiorDNSRoot
+systemMayContain: rootTrust
+systemMayContain: nTMixedDomain
+systemMayContain: nETBIOSName
+systemMayContain: Enabled
+systemMayContain: msDS-SDReferenceDomain
+systemMayContain: msDS-Replication-Notify-Subsequent-DSA-Delay
+systemMayContain: msDS-Replication-Notify-First-DSA-Delay
+systemMayContain: msDS-NC-Replica-Locations
+systemMayContain: msDS-DnsRootAlias
+systemMayContain: msDS-Behavior-Version
+systemMustContain: nCName
+systemMustContain: dnsRoot
+systemMustContain: cn
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+systemFlags: 16
+defaultHidingValue: TRUE
+defaultObjectCategory: CN=Cross-Ref,${SCHEMADN}
+
+dn: CN=RID-Manager,${SCHEMADN}
+objectClass: top
+objectClass: classSchema
+subClassOf: top
+governsID: 1.2.840.113556.1.5.83
+rDNAttID: cn
+showInAdvancedViewOnly: TRUE
+adminDisplayName: RID-Manager
+adminDescription: RID-Manager
+objectClassCategory: 1
+lDAPDisplayName: rIDManager
+schemaIDGUID: 6617188d-8f3c-11d0-afda-00c04fd930c9
+systemOnly: TRUE
+systemPossSuperiors: container
+systemMustContain: rIDAvailablePool
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)S:(AU;SA;CRWP;;;WD)
+systemFlags: 16
+defaultHidingValue: TRUE
+defaultObjectCategory: CN=RID-Manager,${SCHEMADN}
+
+dn: CN=Display-Specifier,${SCHEMADN}
+objectClass: top
+objectClass: classSchema
+subClassOf: top
+governsID: 1.2.840.113556.1.5.84
+rDNAttID: cn
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Display-Specifier
+adminDescription: Display-Specifier
+objectClassCategory: 1
+lDAPDisplayName: displaySpecifier
+schemaIDGUID: e0fa1e8a-9b45-11d0-afdd-00c04fd930c9
+systemOnly: FALSE
+systemPossSuperiors: container
+systemMayContain: treatAsLeaf
+systemMayContain: shellPropertyPages
+systemMayContain: shellContextMenu
+systemMayContain: scopeFlags
+systemMayContain: queryFilter
+systemMayContain: iconPath
+systemMayContain: extraColumns
+systemMayContain: creationWizard
+systemMayContain: createWizardExt
+systemMayContain: createDialog
+systemMayContain: contextMenu
+systemMayContain: classDisplayName
+systemMayContain: attributeDisplayNames
+systemMayContain: adminPropertyPages
+systemMayContain: adminMultiselectPropertyPages
+systemMayContain: adminContextMenu
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+systemFlags: 16
+defaultHidingValue: TRUE
+defaultObjectCategory: CN=Display-Specifier,${SCHEMADN}
+
+dn: CN=Ipsec-Base,${SCHEMADN}
+objectClass: top
+objectClass: classSchema
+subClassOf: top
+governsID: 1.2.840.113556.1.5.7000.56
+rDNAttID: cn
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Ipsec-Base
+adminDescription: Ipsec-Base
+objectClassCategory: 2
+lDAPDisplayName: ipsecBase
+schemaIDGUID: b40ff825-427a-11d1-a9c2-0000f80367c1
+systemOnly: FALSE
+systemMayContain: ipsecOwnersReference
+systemMayContain: ipsecName
+systemMayContain: ipsecID
+systemMayContain: ipsecDataType
+systemMayContain: ipsecData
+defaultSecurityDescriptor: D:
+systemFlags: 16
+defaultHidingValue: TRUE
+defaultObjectCategory: CN=Ipsec-Base,${SCHEMADN}
+
+dn: CN=ms-DS-Az-Scope,${SCHEMADN}
+objectClass: top
+objectClass: classSchema
+subClassOf: top
+governsID: 1.2.840.113556.1.5.237
+possibleInferiors: group
+possibleInferiors: container
+possibleInferiors: groupPolicyContainer
+rDNAttID: cn
+showInAdvancedViewOnly: TRUE
+adminDisplayName: MS-DS-Az-Scope
+adminDescription: Describes a set of objects managed by an application
+objectClassCategory: 1
+lDAPDisplayName: msDS-AzScope
+schemaIDGUID: 4feae054-ce55-47bb-860e-5b12063a51de
+systemOnly: FALSE
+systemPossSuperiors: msDS-AzApplication
+systemMayContain: msDS-AzApplicationData
+systemMayContain: description
+systemMustContain: msDS-AzScopeName
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;CO)
+systemFlags: 16
+defaultHidingValue: TRUE
+defaultObjectCategory: CN=ms-DS-Az-Scope,${SCHEMADN}
+
+dn: CN=Locality,${SCHEMADN}
+objectClass: top
+objectClass: classSchema
+subClassOf: top
+governsID: 2.5.6.3
+possibleInferiors: organization
+possibleInferiors: locality
+rDNAttID: l
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Locality
+adminDescription: Locality
+objectClassCategory: 1
+lDAPDisplayName: locality
+schemaIDGUID: bf967aa0-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemPossSuperiors: domainDNS
+systemPossSuperiors: country
+systemPossSuperiors: organizationalUnit
+systemPossSuperiors: organization
+systemPossSuperiors: locality
+systemMayContain: street
+systemMayContain: st
+systemMayContain: seeAlso
+systemMayContain: searchGuide
+systemMustContain: l
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+systemFlags: 16
+defaultHidingValue: FALSE
+defaultObjectCategory: CN=Locality,${SCHEMADN}
+
+dn: CN=Cross-Ref-Container,${SCHEMADN}
+objectClass: top
+objectClass: classSchema
+subClassOf: top
+governsID: 1.2.840.113556.1.5.7000.53
+possibleInferiors: crossRef
+rDNAttID: cn
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Cross-Ref-Container
+adminDescription: Cross-Ref-Container
+objectClassCategory: 1
+lDAPDisplayName: crossRefContainer
+schemaIDGUID: ef9e60e0-56f7-11d1-a9c6-0000f80367c1
+systemOnly: TRUE
+systemPossSuperiors: configuration
+systemMayContain: msDS-SPNSuffixes
+systemMayContain: uPNSuffixes
+systemMayContain: msDS-UpdateScript
+systemMayContain: msDS-ExecuteScriptPassword
+systemMayContain: msDS-Behavior-Version
+defaultSecurityDescriptor: D:(A;;GA;;;SY)
+systemFlags: 16
+defaultHidingValue: FALSE
+defaultObjectCategory: CN=Cross-Ref-Container,${SCHEMADN}
+
+dn: CN=Query-Policy,${SCHEMADN}
+objectClass: top
+objectClass: classSchema
+subClassOf: top
+governsID: 1.2.840.113556.1.5.106
+rDNAttID: cn
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Query-Policy
+adminDescription: Query-Policy
+objectClassCategory: 1
+lDAPDisplayName: queryPolicy
+schemaIDGUID: 83cc7075-cca7-11d0-afff-0000f80367c1
+systemOnly: FALSE
+systemPossSuperiors: container
+systemMayContain: lDAPIPDenyList
+systemMayContain: lDAPAdminLimits
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+systemFlags: 16
+defaultHidingValue: TRUE
+defaultObjectCategory: CN=Query-Policy,${SCHEMADN}
+
+dn: CN=Subnet-Container,${SCHEMADN}
+objectClass: top
+objectClass: classSchema
+subClassOf: top
+governsID: 1.2.840.113556.1.5.95
+possibleInferiors: subnet
+rDNAttID: cn
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Subnet-Container
+adminDescription: Subnet-Container
+objectClassCategory: 1
+lDAPDisplayName: subnetContainer
+schemaIDGUID: b7b13125-b82e-11d0-afee-0000f80367c1
+systemOnly: FALSE
+systemPossSuperiors: sitesContainer
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+systemFlags: 16
+defaultHidingValue: TRUE
+defaultObjectCategory: CN=Subnet-Container,${SCHEMADN}
+
+dn: CN=NTDS-DSA,${SCHEMADN}
+objectClass: top
+objectClass: classSchema
+subClassOf: applicationSettings
+governsID: 1.2.840.113556.1.5.7000.47
+rDNAttID: cn
+showInAdvancedViewOnly: TRUE
+adminDisplayName: NTDS-DSA
+adminDescription: NTDS-DSA
+objectClassCategory: 1
+lDAPDisplayName: nTDSDSA
+schemaIDGUID: f0f8ffab-1191-11d0-a060-00aa006c33ed
+systemOnly: TRUE
+systemPossSuperiors: organization
+systemPossSuperiors: server
+systemMayContain: serverReference
+systemMayContain: msDS-RetiredReplNCSignatures
+systemMayContain: retiredReplDSASignatures
+systemMayContain: queryPolicyObject
+systemMayContain: options
+systemMayContain: networkAddress
+systemMayContain: msDS-ReplicationEpoch
+systemMayContain: msDS-HasInstantiatedNCs
+systemMayContain: msDS-hasMasterNCs
+systemMayContain: msDS-HasDomainNCs
+systemMayContain: msDS-Behavior-Version
+systemMayContain: managedBy
+systemMayContain: lastBackupRestorationTime
+systemMayContain: invocationId
+systemMayContain: hasPartialReplicaNCs
+systemMayContain: hasMasterNCs
+systemMayContain: fRSRootPath
+systemMayContain: dMDLocation
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+systemFlags: 16
+defaultHidingValue: TRUE
+defaultObjectCategory: CN=NTDS-DSA,${SCHEMADN}
+
+dn: CN=Sam-Domain,${SCHEMADN}
+objectClass: top
+objectClass: classSchema
+subClassOf: top
+governsID: 1.2.840.113556.1.5.3
+rDNAttID: cn
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Sam-Domain
+adminDescription: Sam-Domain
+objectClassCategory: 3
+lDAPDisplayName: samDomain
+schemaIDGUID: bf967a90-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemMayContain: treeName
+systemMayContain: rIDManagerReference
+systemMayContain: replicaSource
+systemMayContain: pwdProperties
+systemMayContain: pwdHistoryLength
+systemMayContain: privateKey
+systemMayContain: pekList
+systemMayContain: pekKeyChangeInterval
+systemMayContain: nTMixedDomain
+systemMayContain: nextRid
+systemMayContain: nETBIOSName
+systemMayContain: msDS-PerUserTrustTombstonesQuota
+systemMayContain: msDS-PerUserTrustQuota
+systemMayContain: ms-DS-MachineAccountQuota
+systemMayContain: msDS-LogonTimeSyncInterval
+systemMayContain: msDS-AllUsersTrustQuota
+systemMayContain: modifiedCountAtLastProm
+systemMayContain: minPwdLength
+systemMayContain: minPwdAge
+systemMayContain: maxPwdAge
+systemMayContain: lSAModifiedCount
+systemMayContain: lSACreationTime
+systemMayContain: lockoutThreshold
+systemMayContain: lockoutDuration
+systemMayContain: lockOutObservationWindow
+systemMayContain: gPOptions
+systemMayContain: gPLink
+systemMayContain: eFSPolicy
+systemMayContain: domainPolicyObject
+systemMayContain: desktopProfile
+systemMayContain: description
+systemMayContain: defaultLocalPolicyObject
+systemMayContain: creationTime
+systemMayContain: controlAccessRights
+systemMayContain: cACertificate
+systemMayContain: builtinModifiedCount
+systemMayContain: builtinCreationTime
+systemMayContain: auditingPolicy
+systemAuxiliaryClass: samDomainBase
+defaultSecurityDescriptor: D:(A;;RP;;;WD)(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)(A;;RPLCLORC;;;AU)(A;;RPWPCRLCLOCCRCWDWOSW;;;DA)(A;CI;RPWPCRLCLOCCRCWDWOSDSW;;;BA)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;SY)(A;CI;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;EA)(A;CI;LC;;;RU)(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)(A;;RPRC;;;RU)(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(A;;LCRPLORC;;;ED)(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)(OA;CIIO;RPLCLORC;;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;S-1-5-32-557)(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)S:(AU;SA;WDWOWP;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)
+systemFlags: 16
+defaultHidingValue: TRUE
+defaultObjectCategory: CN=Sam-Domain,${SCHEMADN}
+
+dn: CN=Sam-Domain-Base,${SCHEMADN}
+objectClass: top
+objectClass: classSchema
+subClassOf: top
+governsID: 1.2.840.113556.1.5.2
+rDNAttID: cn
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Sam-Domain-Base
+adminDescription: Sam-Domain-Base
+objectClassCategory: 3
+lDAPDisplayName: samDomainBase
+schemaIDGUID: bf967a91-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemMayContain: uASCompat
+systemMayContain: serverState
+systemMayContain: serverRole
+systemMayContain: revision
+systemMayContain: pwdProperties
+systemMayContain: pwdHistoryLength
+systemMayContain: oEMInformation
+systemMayContain: objectSid
+systemMayContain: nTSecurityDescriptor
+systemMayContain: nextRid
+systemMayContain: modifiedCountAtLastProm
+systemMayContain: modifiedCount
+systemMayContain: minPwdLength
+systemMayContain: minPwdAge
+systemMayContain: maxPwdAge
+systemMayContain: lockoutThreshold
+systemMayContain: lockoutDuration
+systemMayContain: lockOutObservationWindow
+systemMayContain: forceLogoff
+systemMayContain: domainReplica
+systemMayContain: creationTime
+systemFlags: 16
+defaultHidingValue: TRUE
+defaultObjectCategory: CN=Sam-Domain-Base,${SCHEMADN}
+
+dn: CN=Country,${SCHEMADN}
+objectClass: top
+objectClass: classSchema
+subClassOf: top
+governsID: 2.5.6.2
+possibleInferiors: organization
+possibleInferiors: locality
+possibleInferiors: organizationalUnit
+rDNAttID: c
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Country
+adminDescription: Country
+objectClassCategory: 0
+lDAPDisplayName: country
+schemaIDGUID: bf967a8c-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemPossSuperiors: domainDNS
+systemPossSuperiors: organization
+systemMayContain: co
+systemMayContain: searchGuide
+systemMustContain: c
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+systemFlags: 16
+defaultHidingValue: TRUE
+defaultObjectCategory: CN=Country,${SCHEMADN}
+
+dn: CN=Organizational-Unit,${SCHEMADN}
+objectClass: top
+objectClass: classSchema
+subClassOf: top
+governsID: 2.5.6.5
+possibleInferiors: group
+possibleInferiors: computer
+possibleInferiors: user
+possibleInferiors: container
+possibleInferiors: groupPolicyContainer
+possibleInferiors: person
+possibleInferiors: ipsecNFA
+possibleInferiors: locality
+possibleInferiors: msDS-AzAdminManager
+possibleInferiors: organizationalUnit
+possibleInferiors: ipsecPolicy
+possibleInferiors: organizationalPerson
+possibleInferiors: ipsecISAKMPPolicy
+rDNAttID: ou
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Organizational-Unit
+adminDescription: Organizational-Unit
+objectClassCategory: 1
+lDAPDisplayName: organizationalUnit
+schemaIDGUID: bf967aa5-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemPossSuperiors: country
+systemPossSuperiors: organization
+systemPossSuperiors: organizationalUnit
+systemPossSuperiors: domainDNS
+systemMayContain: x121Address
+systemMayContain: userPassword
+systemMayContain: uPNSuffixes
+systemMayContain: co
+systemMayContain: telexNumber
+systemMayContain: teletexTerminalIdentifier
+systemMayContain: telephoneNumber
+systemMayContain: street
+systemMayContain: st
+systemMayContain: seeAlso
+systemMayContain: searchGuide
+systemMayContain: registeredAddress
+systemMayContain: preferredDeliveryMethod
+systemMayContain: postalCode
+systemMayContain: postalAddress
+systemMayContain: postOfficeBox
+systemMayContain: physicalDeliveryOfficeName
+systemMayContain: msCOM-UserPartitionSetLink
+systemMayContain: managedBy
+systemMayContain: thumbnailLogo
+systemMayContain: l
+systemMayContain: internationalISDNNumber
+systemMayContain: gPOptions
+systemMayContain: gPLink
+systemMayContain: facsimileTelephoneNumber
+systemMayContain: destinationIndicator
+systemMayContain: desktopProfile
+systemMayContain: defaultGroup
+systemMayContain: countryCode
+systemMayContain: c
+systemMayContain: businessCategory
+systemMustContain: ou
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(OA;;CCDC;bf967a86-0de6-11d0-a285-00aa003049e2;;AO)(OA;;CCDC;bf967aba-0de6-11d0-a285-00aa003049e2;;AO)(OA;;CCDC;bf967a9c-0de6-11d0-a285-00aa003049e2;;AO)(OA;;CCDC;bf967aa8-0de6-11d0-a285-00aa003049e2;;PO)(A;;RPLCLORC;;;AU)(A;;LCRPLORC;;;ED)(OA;;CCDC;4828CC14-1437-45bc-9B07-AD6F015E5F28;;AO)
+systemFlags: 16
+defaultHidingValue: FALSE
+defaultObjectCategory: CN=Organizational-Unit,${SCHEMADN}
+
+dn: CN=Ipsec-NFA,${SCHEMADN}
+objectClass: top
+objectClass: classSchema
+subClassOf: ipsecBase
+governsID: 1.2.840.113556.1.5.121
+rDNAttID: cn
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Ipsec-NFA
+adminDescription: Ipsec-NFA
+objectClassCategory: 1
+lDAPDisplayName: ipsecNFA
+schemaIDGUID: b40ff829-427a-11d1-a9c2-0000f80367c1
+systemOnly: FALSE
+systemPossSuperiors: container
+systemPossSuperiors: computer
+systemPossSuperiors: organizationalUnit
+systemMayContain: ipsecNegotiationPolicyReference
+systemMayContain: ipsecFilterReference
+defaultSecurityDescriptor: D:
+systemFlags: 16
+defaultHidingValue: TRUE
+defaultObjectCategory: CN=Ipsec-NFA,${SCHEMADN}
+
+dn: CN=Lost-And-Found,${SCHEMADN}
+objectClass: top
+objectClass: classSchema
+subClassOf: top
+governsID: 1.2.840.113556.1.5.139
+possibleInferiors: group
+possibleInferiors: msDS-AzApplication
+possibleInferiors: lostAndFound
+possibleInferiors: trustedDomain
+possibleInferiors: subnetContainer
+possibleInferiors: builtinDomain
+possibleInferiors: sitesContainer
+possibleInferiors: serversContainer
+possibleInferiors: attributeSchema
+possibleInferiors: classSchema
+possibleInferiors: computer
+possibleInferiors: foreignSecurityPrincipal
+possibleInferiors: user
+possibleInferiors: container
+possibleInferiors: msDS-AzScope
+possibleInferiors: groupPolicyContainer
+possibleInferiors: site
+possibleInferiors: organization
+possibleInferiors: domainDNS
+possibleInferiors: person
+possibleInferiors: ipsecNFA
+possibleInferiors: queryPolicy
+possibleInferiors: locality
+possibleInferiors: subnet
+possibleInferiors: msDS-AzAdminManager
+possibleInferiors: crossRef
+possibleInferiors: displaySpecifier
+possibleInferiors: nTDSService
+possibleInferiors: country
+possibleInferiors: organizationalUnit
+possibleInferiors: secret
+possibleInferiors: ipsecPolicy
+possibleInferiors: organizationalPerson
+possibleInferiors: server
+possibleInferiors: ipsecISAKMPPolicy
+rDNAttID: cn
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Lost-And-Found
+adminDescription: Lost-And-Found
+objectClassCategory: 1
+lDAPDisplayName: lostAndFound
+schemaIDGUID: 52ab8671-5709-11d1-a9c6-0000f80367c1
+systemOnly: FALSE
+systemPossSuperiors: configuration
+systemPossSuperiors: domainDNS
+systemPossSuperiors: dMD
+systemMayContain: moveTreeState
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+systemFlags: 16
+defaultHidingValue: TRUE
+defaultObjectCategory: CN=Lost-And-Found,${SCHEMADN}
+
+dn: CN=Organizational-Person,${SCHEMADN}
+objectClass: top
+objectClass: classSchema
+subClassOf: person
+governsID: 2.5.6.7
+mayContain: houseIdentifier
+mayContain: msExchHouseIdentifier
+mayContain: homePostalAddress
+rDNAttID: cn
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Organizational-Person
+adminDescription: Organizational-Person
+objectClassCategory: 0
+lDAPDisplayName: organizationalPerson
+schemaIDGUID: bf967aa4-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemPossSuperiors: organizationalUnit
+systemPossSuperiors: organization
+systemPossSuperiors: container
+systemMayContain: x121Address
+systemMayContain: comment
+systemMayContain: title
+systemMayContain: co
+systemMayContain: primaryTelexNumber
+systemMayContain: telexNumber
+systemMayContain: teletexTerminalIdentifier
+systemMayContain: street
+systemMayContain: st
+systemMayContain: registeredAddress
+systemMayContain: preferredDeliveryMethod
+systemMayContain: postalCode
+systemMayContain: postalAddress
+systemMayContain: postOfficeBox
+systemMayContain: thumbnailPhoto
+systemMayContain: physicalDeliveryOfficeName
+systemMayContain: pager
+systemMayContain: otherPager
+systemMayContain: otherTelephone
+systemMayContain: mobile
+systemMayContain: otherMobile
+systemMayContain: primaryInternationalISDNNumber
+systemMayContain: ipPhone
+systemMayContain: otherIpPhone
+systemMayContain: otherHomePhone
+systemMayContain: homePhone
+systemMayContain: otherFacsimileTelephoneNumber
+systemMayContain: personalTitle
+systemMayContain: middleName
+systemMayContain: otherMailbox
+systemMayContain: ou
+systemMayContain: o
+systemMayContain: mhsORAddress
+systemMayContain: msDS-AllowedToDelegateTo
+systemMayContain: manager
+systemMayContain: thumbnailLogo
+systemMayContain: l
+systemMayContain: internationalISDNNumber
+systemMayContain: initials
+systemMayContain: givenName
+systemMayContain: generationQualifier
+systemMayContain: facsimileTelephoneNumber
+systemMayContain: employeeID
+systemMayContain: mail
+systemMayContain: division
+systemMayContain: destinationIndicator
+systemMayContain: department
+systemMayContain: c
+systemMayContain: countryCode
+systemMayContain: company
+systemMayContain: assistant
+systemMayContain: streetAddress
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+systemFlags: 16
+defaultHidingValue: TRUE
+defaultObjectCategory: CN=Person,${SCHEMADN}
+
+dn: CN=Attribute-Schema,${SCHEMADN}
+objectClass: top
+objectClass: classSchema
+subClassOf: top
+governsID: 1.2.840.113556.1.3.14
+rDNAttID: cn
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Attribute-Schema
+adminDescription: Attribute-Schema
+objectClassCategory: 1
+lDAPDisplayName: attributeSchema
+schemaIDGUID: bf967a80-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemPossSuperiors: dMD
+systemMayContain: systemOnly
+systemMayContain: searchFlags
+systemMayContain: schemaFlagsEx
+systemMayContain: rangeUpper
+systemMayContain: rangeLower
+systemMayContain: oMObjectClass
+systemMayContain: msDs-Schema-Extensions
+systemMayContain: msDS-IntId
+systemMayContain: mAPIID
+systemMayContain: linkID
+systemMayContain: isMemberOfPartialAttributeSet
+systemMayContain: isEphemeral
+systemMayContain: isDefunct
+systemMayContain: extendedCharsAllowed
+systemMayContain: classDisplayName
+systemMayContain: attributeSecurityGUID
+systemMustContain: schemaIDGUID
+systemMustContain: oMSyntax
+systemMustContain: lDAPDisplayName
+systemMustContain: isSingleValued
+systemMustContain: cn
+systemMustContain: attributeSyntax
+systemMustContain: attributeID
+defaultSecurityDescriptor: D:S:
+systemFlags: 134217744
+defaultHidingValue: TRUE
+defaultObjectCategory: CN=Attribute-Schema,${SCHEMADN}
+
+dn: CN=NTDS-Service,${SCHEMADN}
+objectClass: top
+objectClass: classSchema
+subClassOf: top
+governsID: 1.2.840.113556.1.5.72
+possibleInferiors: container
+possibleInferiors: groupPolicyContainer
+rDNAttID: cn
+showInAdvancedViewOnly: TRUE
+adminDisplayName: NTDS-Service
+adminDescription: NTDS-Service
+objectClassCategory: 1
+lDAPDisplayName: nTDSService
+schemaIDGUID: 19195a5f-6da0-11d0-afd3-00c04fd930c9
+systemOnly: FALSE
+systemPossSuperiors: container
+systemMayContain: tombstoneLifetime
+systemMayContain: sPNMappings
+systemMayContain: replTopologyStayOfExecution
+systemMayContain: msDS-Other-Settings
+systemMayContain: garbageCollPeriod
+systemMayContain: dSHeuristics
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+systemFlags: 16
+defaultHidingValue: TRUE
+defaultObjectCategory: CN=NTDS-Service,${SCHEMADN}
+
+dn: CN=Servers-Container,${SCHEMADN}
+objectClass: top
+objectClass: classSchema
+subClassOf: top
+governsID: 1.2.840.113556.1.5.7000.48
+possibleInferiors: server
+rDNAttID: cn
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Servers-Container
+adminDescription: Servers-Container
+objectClassCategory: 1
+lDAPDisplayName: serversContainer
+schemaIDGUID: f780acc0-56f0-11d1-a9c6-0000f80367c1
+systemOnly: FALSE
+systemPossSuperiors: site
+defaultSecurityDescriptor: D:(A;;CC;;;BA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+systemFlags: 16
+defaultHidingValue: TRUE
+defaultObjectCategory: CN=Servers-Container,${SCHEMADN}
+
+dn: CN=Computer,${SCHEMADN}
+objectClass: top
+objectClass: classSchema
+subClassOf: user
+governsID: 1.2.840.113556.1.3.30
+possibleInferiors: ipsecNFA
+possibleInferiors: ipsecPolicy
+possibleInferiors: ipsecISAKMPPolicy
+rDNAttID: cn
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Computer
+adminDescription: Computer
+objectClassCategory: 1
+lDAPDisplayName: computer
+schemaIDGUID: bf967a86-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemPossSuperiors: container
+systemPossSuperiors: organizationalUnit
+systemPossSuperiors: domainDNS
+systemMayContain: volumeCount
+systemMayContain: siteGUID
+systemMayContain: rIDSetReferences
+systemMayContain: policyReplicationFlags
+systemMayContain: physicalLocationObject
+systemMayContain: operatingSystemVersion
+systemMayContain: operatingSystemServicePack
+systemMayContain: operatingSystemHotfix
+systemMayContain: operatingSystem
+systemMayContain: networkAddress
+systemMayContain: netbootSIFFile
+systemMayContain: netbootMirrorDataFile
+systemMayContain: netbootMachineFilePath
+systemMayContain: netbootInitialization
+systemMayContain: netbootGUID
+systemMayContain: msDS-AdditionalSamAccountName
+systemMayContain: msDS-AdditionalDnsHostName
+systemMayContain: managedBy
+systemMayContain: machineRole
+systemMayContain: location
+systemMayContain: localPolicyFlags
+systemMayContain: dNSHostName
+systemMayContain: defaultLocalPolicyObject
+systemMayContain: cn
+systemMayContain: catalogs
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;AO)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPCRLCLORCSDDT;;;CO)(OA;;WP;4c164200-20c0-11d0-a768-00aa006e0529;;CO)(A;;RPLCLORC;;;AU)(OA;;CR;ab721a53-1e2f-11d0-9819-00aa0040529b;;WD)(A;;CCDC;;;PS)(OA;;CCDC;bf967aa8-0de6-11d0-a285-00aa003049e2;;PO)(OA;;RPWP;bf967a7f-0de6-11d0-a285-00aa003049e2;;CA)(OA;;SW;f3a64788-5306-11d1-a9c5-0000f80367c1;;PS)(OA;;RPWP;77B5B886-944A-11d1-AEBD-0000F80367C1;;PS)(OA;;SW;72e39547-7b18-11d1-adef-00c04fd8d5cd;;PS)(OA;;SW;72e39547-7b18-11d1-adef-00c04fd8d5cd;;CO)(OA;;SW;f3a64788-5306-11d1-a9c5-0000f80367c1;;CO)(OA;;WP;3e0abfd0-126a-11d0-a060-00aa006c33ed;bf967a86-0de6-11d0-a285-00aa003049e2;CO)(OA;;WP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967a86-0de6-11d0-a285-00aa003049e2;CO)(OA;;WP;bf967950-0de6-11d0-a285-00aa003049e2;bf967a86-0de6-11d0-a285-00aa003049e2;CO)(OA;;WP;bf967953-0de6-11d0-a285-00aa003049e2;bf967a86-0de6-11d0-a285-00aa003049e2;CO)(OA;;RP;46a9b11d-60ae-405a-b7e8-ff8a58d456d2;;S-1-5-32-560)
+systemFlags: 16
+defaultHidingValue: FALSE
+defaultObjectCategory: CN=Computer,${SCHEMADN}
+
+dn: CN=Person,${SCHEMADN}
+objectClass: top
+objectClass: classSchema
+subClassOf: top
+governsID: 2.5.6.6
+mayContain: attributeCertificateAttribute
+rDNAttID: cn
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Person
+adminDescription: Person
+objectClassCategory: 0
+lDAPDisplayName: person
+schemaIDGUID: bf967aa7-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemPossSuperiors: organizationalUnit
+systemPossSuperiors: container
+systemMayContain: userPassword
+systemMayContain: telephoneNumber
+systemMayContain: sn
+systemMayContain: serialNumber
+systemMayContain: seeAlso
+systemMustContain: cn
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+systemFlags: 16
+defaultHidingValue: TRUE
+defaultObjectCategory: CN=Person,${SCHEMADN}
+
+dn: CN=Ipsec-Policy,${SCHEMADN}
+objectClass: top
+objectClass: classSchema
+subClassOf: ipsecBase
+governsID: 1.2.840.113556.1.5.98
+rDNAttID: cn
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Ipsec-Policy
+adminDescription: Ipsec-Policy
+objectClassCategory: 1
+lDAPDisplayName: ipsecPolicy
+schemaIDGUID: b7b13121-b82e-11d0-afee-0000f80367c1
+systemOnly: FALSE
+systemPossSuperiors: organizationalUnit
+systemPossSuperiors: computer
+systemPossSuperiors: container
+systemMayContain: ipsecNFAReference
+systemMayContain: ipsecISAKMPReference
+defaultSecurityDescriptor: D:
+systemFlags: 16
+defaultHidingValue: TRUE
+defaultObjectCategory: CN=Ipsec-Policy,${SCHEMADN}
+
+dn: CN=Container,${SCHEMADN}
+objectClass: top
+objectClass: classSchema
+subClassOf: top
+governsID: 1.2.840.113556.1.3.23
+possibleInferiors: group
+possibleInferiors: trustedDomain
+possibleInferiors: computer
+possibleInferiors: foreignSecurityPrincipal
+possibleInferiors: user
+possibleInferiors: container
+possibleInferiors: groupPolicyContainer
+possibleInferiors: person
+possibleInferiors: ipsecNFA
+possibleInferiors: queryPolicy
+possibleInferiors: msDS-AzAdminManager
+possibleInferiors: displaySpecifier
+possibleInferiors: nTDSService
+possibleInferiors: secret
+possibleInferiors: ipsecPolicy
+possibleInferiors: organizationalPerson
+possibleInferiors: ipsecISAKMPPolicy
+mayContain: msDS-ObjectReference
+rDNAttID: cn
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Container
+adminDescription: Container
+objectClassCategory: 1
+lDAPDisplayName: container
+schemaIDGUID: bf967a8b-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemPossSuperiors: msDS-AzScope
+systemPossSuperiors: msDS-AzApplication
+systemPossSuperiors: msDS-AzAdminManager
+systemPossSuperiors: subnet
+systemPossSuperiors: server
+systemPossSuperiors: nTDSService
+systemPossSuperiors: domainDNS
+systemPossSuperiors: organization
+systemPossSuperiors: configuration
+systemPossSuperiors: container
+systemPossSuperiors: organizationalUnit
+systemMayContain: schemaVersion
+systemMayContain: defaultClassStore
+systemMustContain: cn
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+systemFlags: 16
+defaultHidingValue: TRUE
+defaultObjectCategory: CN=Container,${SCHEMADN}
+
+dn: CN=Site,${SCHEMADN}
+objectClass: top
+objectClass: classSchema
+subClassOf: top
+governsID: 1.2.840.113556.1.5.31
+possibleInferiors: serversContainer
+rDNAttID: cn
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Site
+adminDescription: Site
+objectClassCategory: 1
+lDAPDisplayName: site
+schemaIDGUID: bf967ab3-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemPossSuperiors: sitesContainer
+systemMayContain: notificationList
+systemMayContain: mSMQSiteID
+systemMayContain: mSMQSiteForeign
+systemMayContain: mSMQNt4Stub
+systemMayContain: mSMQInterval2
+systemMayContain: mSMQInterval1
+systemMayContain: managedBy
+systemMayContain: location
+systemMayContain: gPOptions
+systemMayContain: gPLink
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)(A;;LCRPLORC;;;ED)
+systemFlags: 16
+defaultHidingValue: TRUE
+defaultObjectCategory: CN=Site,${SCHEMADN}
+
+dn: CN=Organization,${SCHEMADN}
+objectClass: top
+objectClass: classSchema
+subClassOf: top
+governsID: 2.5.6.4
+possibleInferiors: computer
+possibleInferiors: user
+possibleInferiors: container
+possibleInferiors: groupPolicyContainer
+possibleInferiors: domainDNS
+possibleInferiors: locality
+possibleInferiors: country
+possibleInferiors: organizationalUnit
+possibleInferiors: organizationalPerson
+rDNAttID: o
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Organization
+adminDescription: Organization
+objectClassCategory: 1
+lDAPDisplayName: organization
+schemaIDGUID: bf967aa3-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemPossSuperiors: locality
+systemPossSuperiors: country
+systemPossSuperiors: domainDNS
+systemMayContain: x121Address
+systemMayContain: userPassword
+systemMayContain: telexNumber
+systemMayContain: teletexTerminalIdentifier
+systemMayContain: telephoneNumber
+systemMayContain: street
+systemMayContain: st
+systemMayContain: seeAlso
+systemMayContain: searchGuide
+systemMayContain: registeredAddress
+systemMayContain: preferredDeliveryMethod
+systemMayContain: postalCode
+systemMayContain: postalAddress
+systemMayContain: postOfficeBox
+systemMayContain: physicalDeliveryOfficeName
+systemMayContain: l
+systemMayContain: internationalISDNNumber
+systemMayContain: facsimileTelephoneNumber
+systemMayContain: destinationIndicator
+systemMayContain: businessCategory
+systemMustContain: o
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+systemFlags: 16
+defaultHidingValue: FALSE
+defaultObjectCategory: CN=Organization,${SCHEMADN}
+
+dn: CN=ms-DS-Az-Admin-Manager,${SCHEMADN}
+objectClass: top
+objectClass: classSchema
+subClassOf: top
+governsID: 1.2.840.113556.1.5.234
+possibleInferiors: group
+possibleInferiors: msDS-AzApplication
+possibleInferiors: container
+possibleInferiors: groupPolicyContainer
+rDNAttID: cn
+showInAdvancedViewOnly: TRUE
+adminDisplayName: MS-DS-Az-Admin-Manager
+adminDescription: Root of Authorization Policy store instance
+objectClassCategory: 1
+lDAPDisplayName: msDS-AzAdminManager
+schemaIDGUID: cfee1051-5f28-4bae-a863-5d0cc18a8ed1
+systemOnly: FALSE
+systemPossSuperiors: domainDNS
+systemPossSuperiors: organizationalUnit
+systemPossSuperiors: container
+systemMayContain: msDS-AzMinorVersion
+systemMayContain: msDS-AzMajorVersion
+systemMayContain: msDS-AzApplicationData
+systemMayContain: msDS-AzGenerateAudits
+systemMayContain: msDS-AzScriptTimeout
+systemMayContain: msDS-AzScriptEngineCacheMax
+systemMayContain: msDS-AzDomainTimeout
+systemMayContain: description
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;CO)
+systemFlags: 16
+defaultHidingValue: TRUE
+defaultObjectCategory: CN=ms-DS-Az-Admin-Manager,${SCHEMADN}
+
+dn: CN=Security-Principal,${SCHEMADN}
+objectClass: top
+objectClass: classSchema
+subClassOf: top
+governsID: 1.2.840.113556.1.5.6
+rDNAttID: cn
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Security-Principal
+adminDescription: Security-Principal
+objectClassCategory: 3
+lDAPDisplayName: securityPrincipal
+schemaIDGUID: bf967ab0-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemMayContain: supplementalCredentials
+systemMayContain: sIDHistory
+systemMayContain: securityIdentifier
+systemMayContain: sAMAccountType
+systemMayContain: rid
+systemMayContain: tokenGroupsNoGCAcceptable
+systemMayContain: tokenGroupsGlobalAndUniversal
+systemMayContain: tokenGroups
+systemMayContain: nTSecurityDescriptor
+systemMayContain: msDS-KeyVersionNumber
+systemMayContain: altSecurityIdentities
+systemMayContain: accountNameHistory
+systemMustContain: sAMAccountName
+systemMustContain: objectSid
+systemFlags: 16
+defaultHidingValue: TRUE
+defaultObjectCategory: CN=Security-Principal,${SCHEMADN}
+
+dn: CN=Application-Settings,${SCHEMADN}
+objectClass: top
+objectClass: classSchema
+subClassOf: top
+governsID: 1.2.840.113556.1.5.7000.49
+rDNAttID: cn
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Application-Settings
+adminDescription: Application-Settings
+objectClassCategory: 2
+lDAPDisplayName: applicationSettings
+schemaIDGUID: f780acc1-56f0-11d1-a9c6-0000f80367c1
+systemOnly: FALSE
+systemPossSuperiors: server
+systemMayContain: notificationList
+systemMayContain: msDS-Settings
+systemMayContain: applicationName
+systemFlags: 16
+defaultHidingValue: TRUE
+defaultObjectCategory: CN=Application-Settings,${SCHEMADN}
+
+dn: CN=Class-Schema,${SCHEMADN}
+objectClass: top
+objectClass: classSchema
+subClassOf: top
+governsID: 1.2.840.113556.1.3.13
+rDNAttID: cn
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Class-Schema
+adminDescription: Class-Schema
+objectClassCategory: 1
+lDAPDisplayName: classSchema
+schemaIDGUID: bf967a83-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemPossSuperiors: dMD
+systemMayContain: systemPossSuperiors
+systemMayContain: systemOnly
+systemMayContain: systemMustContain
+systemMayContain: systemMayContain
+systemMayContain: systemAuxiliaryClass
+systemMayContain: schemaFlagsEx
+systemMayContain: rDNAttID
+systemMayContain: possSuperiors
+systemMayContain: mustContain
+systemMayContain: msDs-Schema-Extensions
+systemMayContain: msDS-IntId
+systemMayContain: mayContain
+systemMayContain: lDAPDisplayName
+systemMayContain: isDefunct
+systemMayContain: defaultSecurityDescriptor
+systemMayContain: defaultHidingValue
+systemMayContain: classDisplayName
+systemMayContain: auxiliaryClass
+systemMustContain: subClassOf
+systemMustContain: schemaIDGUID
+systemMustContain: objectClassCategory
+systemMustContain: governsID
+systemMustContain: defaultObjectCategory
+systemMustContain: cn
+defaultSecurityDescriptor: D:S:
+systemFlags: 134217744
+defaultHidingValue: TRUE
+defaultObjectCategory: CN=Class-Schema,${SCHEMADN}
+
+dn: CN=User,${SCHEMADN}
+objectClass: top
+objectClass: classSchema
+subClassOf: organizationalPerson
+governsID: 1.2.840.113556.1.5.9
+mayContain: x500uniqueIdentifier
+mayContain: userSMIMECertificate
+mayContain: userPKCS12
+mayContain: uid
+mayContain: secretary
+mayContain: roomNumber
+mayContain: preferredLanguage
+mayContain: photo
+mayContain: labeledURI
+mayContain: jpegPhoto
+mayContain: homePostalAddress
+mayContain: givenName
+mayContain: employeeType
+mayContain: employeeNumber
+mayContain: displayName
+mayContain: departmentNumber
+mayContain: carLicense
+mayContain: audio
+rDNAttID: cn
+showInAdvancedViewOnly: TRUE
+adminDisplayName: User
+adminDescription: User
+objectClassCategory: 1
+lDAPDisplayName: user
+schemaIDGUID: bf967aba-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemPossSuperiors: builtinDomain
+systemPossSuperiors: organizationalUnit
+systemPossSuperiors: domainDNS
+systemMayContain: pager
+systemMayContain: o
+systemMayContain: mobile
+systemMayContain: manager
+systemMayContain: mail
+systemMayContain: initials
+systemMayContain: homePhone
+systemMayContain: businessCategory
+systemMayContain: userCertificate
+systemMayContain: userWorkstations
+systemMayContain: userSharedFolderOther
+systemMayContain: userSharedFolder
+systemMayContain: userPrincipalName
+systemMayContain: userParameters
+systemMayContain: userAccountControl
+systemMayContain: unicodePwd
+systemMayContain: terminalServer
+systemMayContain: servicePrincipalName
+systemMayContain: scriptPath
+systemMayContain: pwdLastSet
+systemMayContain: profilePath
+systemMayContain: primaryGroupID
+systemMayContain: preferredOU
+systemMayContain: otherLoginWorkstations
+systemMayContain: operatorCount
+systemMayContain: ntPwdHistory
+systemMayContain: networkAddress
+systemMayContain: msRASSavedFramedRoute
+systemMayContain: msRASSavedFramedIPAddress
+systemMayContain: msRASSavedCallbackNumber
+systemMayContain: msRADIUSServiceType
+systemMayContain: msRADIUSFramedRoute
+systemMayContain: msRADIUSFramedIPAddress
+systemMayContain: msRADIUSCallbackNumber
+systemMayContain: msNPSavedCallingStationID
+systemMayContain: msNPCallingStationID
+systemMayContain: msNPAllowDialin
+systemMayContain: mSMQSignCertificatesMig
+systemMayContain: mSMQSignCertificates
+systemMayContain: mSMQDigestsMig
+systemMayContain: mSMQDigests
+systemMayContain: msIIS-FTPRoot
+systemMayContain: msIIS-FTPDir
+systemMayContain: msDS-User-Account-Control-Computed
+systemMayContain: msDS-Site-Affinity
+systemMayContain: mS-DS-CreatorSID
+systemMayContain: msDS-Cached-Membership-Time-Stamp
+systemMayContain: msDS-Cached-Membership
+systemMayContain: msDRM-IdentityCertificate
+systemMayContain: msCOM-UserPartitionSetLink
+systemMayContain: maxStorage
+systemMayContain: logonWorkstation
+systemMayContain: logonHours
+systemMayContain: logonCount
+systemMayContain: lockoutTime
+systemMayContain: localeID
+systemMayContain: lmPwdHistory
+systemMayContain: lastLogonTimestamp
+systemMayContain: lastLogon
+systemMayContain: lastLogoff
+systemMayContain: homeDrive
+systemMayContain: homeDirectory
+systemMayContain: groupsToIgnore
+systemMayContain: groupPriority
+systemMayContain: groupMembershipSAM
+systemMayContain: dynamicLDAPServer
+systemMayContain: desktopProfile
+systemMayContain: defaultClassStore
+systemMayContain: dBCSPwd
+systemMayContain: controlAccessRights
+systemMayContain: codePage
+systemMayContain: badPwdCount
+systemMayContain: badPasswordTime
+systemMayContain: adminCount
+systemMayContain: aCSPolicyName
+systemMayContain: accountExpires
+systemAuxiliaryClass: securityPrincipal
+systemAuxiliaryClass: mailRecipient
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;AO)(A;;RPLCLORC;;;PS)(OA;;CR;ab721a53-1e2f-11d0-9819-00aa0040529b;;PS)(OA;;CR;ab721a54-1e2f-11d0-9819-00aa0040529b;;PS)(OA;;CR;ab721a56-1e2f-11d0-9819-00aa0040529b;;PS)(OA;;RPWP;77B5B886-944A-11d1-AEBD-0000F80367C1;;PS)(OA;;RPWP;E45795B2-9455-11d1-AEBD-0000F80367C1;;PS)(OA;;RPWP;E45795B3-9455-11d1-AEBD-0000F80367C1;;PS)(OA;;RP;037088f8-0ae1-11d2-b422-00a0c968f939;;RS)(OA;;RP;4c164200-20c0-11d0-a768-00aa006e0529;;RS)(OA;;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;;RS)(A;;RC;;;AU)(OA;;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;;AU)(OA;;RP;77B5B886-944A-11d1-AEBD-0000F80367C1;;AU)(OA;;RP;E45795B3-9455-11d1-AEBD-0000F80367C1;;AU)(OA;;RP;e48d0154-bcf8-11d1-8702-00c04fb96050;;AU)(OA;;CR;ab721a53-1e2f-11d0-9819-00aa0040529b;;WD)(OA;;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;;RS)(OA;;RPWP;bf967a7f-0de6-11d0-a285-00aa003049e2;;CA)(OA;;RP;46a9b11d-60ae-405a-b7e8-ff8a58d456d2;;S-1-5-32-560)(OA;;WPRP;6db69a1c-9422-11d1-aebd-0000f80367c1;;S-1-5-32-561)
+systemFlags: 16
+defaultHidingValue: FALSE
+defaultObjectCategory: CN=Person,${SCHEMADN}
+
+dn: CN=DMD,${SCHEMADN}
+objectClass: top
+objectClass: classSchema
+subClassOf: top
+governsID: 1.2.840.113556.1.3.9
+possibleInferiors: lostAndFound
+possibleInferiors: attributeSchema
+possibleInferiors: classSchema
+rDNAttID: cn
+showInAdvancedViewOnly: TRUE
+adminDisplayName: DMD
+adminDescription: DMD
+objectClassCategory: 1
+lDAPDisplayName: dMD
+schemaIDGUID: bf967a8f-0de6-11d0-a285-00aa003049e2
+systemOnly: TRUE
+systemPossSuperiors: configuration
+systemMayContain: schemaUpdate
+systemMayContain: schemaInfo
+systemMayContain: prefixMap
+systemMayContain: msDs-Schema-Extensions
+systemMayContain: msDS-IntId
+systemMayContain: dmdName
+systemMustContain: cn
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+systemFlags: 16
+defaultHidingValue: TRUE
+defaultObjectCategory: CN=DMD,${SCHEMADN}
+
+dn: CN=Leaf,${SCHEMADN}
+objectClass: top
+objectClass: classSchema
+subClassOf: top
+governsID: 1.2.840.113556.1.5.20
+rDNAttID: cn
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Leaf
+adminDescription: Leaf
+objectClassCategory: 2
+lDAPDisplayName: leaf
+schemaIDGUID: bf967a9e-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+systemFlags: 16
+defaultHidingValue: TRUE
+defaultObjectCategory: CN=Leaf,${SCHEMADN}
+
+dn: CN=Secret,${SCHEMADN}
+objectClass: top
+objectClass: classSchema
+subClassOf: leaf
+governsID: 1.2.840.113556.1.5.28
+rDNAttID: cn
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Secret
+adminDescription: Secret
+objectClassCategory: 1
+lDAPDisplayName: secret
+schemaIDGUID: bf967aae-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemPossSuperiors: container
+systemMayContain: priorValue
+systemMayContain: priorSetTime
+systemMayContain: lastSetTime
+systemMayContain: currentValue
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)
+systemFlags: 16
+defaultHidingValue: TRUE
+defaultObjectCategory: CN=Secret,${SCHEMADN}
+
+dn: CN=Sites-Container,${SCHEMADN}
+objectClass: top
+objectClass: classSchema
+subClassOf: top
+governsID: 1.2.840.113556.1.5.107
+possibleInferiors: subnetContainer
+possibleInferiors: site
+rDNAttID: cn
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Sites-Container
+adminDescription: Sites-Container
+objectClassCategory: 1
+lDAPDisplayName: sitesContainer
+schemaIDGUID: 7a4117da-cd67-11d0-afff-0000f80367c1
+systemOnly: FALSE
+systemPossSuperiors: configuration
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+systemFlags: 16
+defaultHidingValue: TRUE
+defaultObjectCategory: CN=Sites-Container,${SCHEMADN}
+
+dn: CN=Server,${SCHEMADN}
+objectClass: top
+objectClass: classSchema
+subClassOf: top
+governsID: 1.2.840.113556.1.5.17
+possibleInferiors: container
+possibleInferiors: groupPolicyContainer
+rDNAttID: cn
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Server
+adminDescription: Server
+objectClassCategory: 1
+lDAPDisplayName: server
+schemaIDGUID: bf967a92-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemPossSuperiors: serversContainer
+systemMayContain: mailAddress
+systemMayContain: serverReference
+systemMayContain: serialNumber
+systemMayContain: managedBy
+systemMayContain: dNSHostName
+systemMayContain: bridgeheadTransportList
+defaultSecurityDescriptor: D:(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+systemFlags: 16
+defaultHidingValue: TRUE
+defaultObjectCategory: CN=Server,${SCHEMADN}
+
+dn: CN=SubSchema,${SCHEMADN}
+objectClass: top
+objectClass: classSchema
+subClassOf: top
+governsID: 2.5.20.1
+rDNAttID: cn
+showInAdvancedViewOnly: TRUE
+adminDisplayName: SubSchema
+adminDescription: SubSchema
+objectClassCategory: 1
+lDAPDisplayName: subSchema
+schemaIDGUID: 5a8b3261-c38d-11d1-bbc9-0080c76670c0
+systemOnly: TRUE
+systemPossSuperiors: dMD
+systemMayContain: objectClasses
+systemMayContain: modifyTimeStamp
+systemMayContain: extendedClassInfo
+systemMayContain: extendedAttributeInfo
+systemMayContain: dITContentRules
+systemMayContain: attributeTypes
+defaultSecurityDescriptor: D:S:
+systemFlags: 134217744
+defaultHidingValue: TRUE
+defaultObjectCategory: CN=SubSchema,${SCHEMADN}
+
+dn: CN=Trusted-Domain,${SCHEMADN}
+objectClass: top
+objectClass: classSchema
+subClassOf: leaf
+governsID: 1.2.840.113556.1.5.34
+rDNAttID: cn
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Trusted-Domain
+adminDescription: Trusted-Domain
+objectClassCategory: 1
+lDAPDisplayName: trustedDomain
+schemaIDGUID: bf967ab8-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemPossSuperiors: container
+systemMayContain: trustType
+systemMayContain: trustPosixOffset
+systemMayContain: trustPartner
+systemMayContain: trustDirection
+systemMayContain: trustAuthOutgoing
+systemMayContain: trustAuthIncoming
+systemMayContain: trustAttributes
+systemMayContain: securityIdentifier
+systemMayContain: msDS-TrustForestTrustInfo
+systemMayContain: mS-DS-CreatorSID
+systemMayContain: initialAuthOutgoing
+systemMayContain: initialAuthIncoming
+systemMayContain: flatName
+systemMayContain: domainIdentifier
+systemMayContain: domainCrossRef
+systemMayContain: additionalTrustedServiceNames
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)(OA;;WP;736e4812-af31-11d2-b7df-00805f48caeb;bf967ab8-0de6-11d0-a285-00aa003049e2;CO)(A;;SD;;;CO)
+systemFlags: 16
+defaultHidingValue: TRUE
+defaultObjectCategory: CN=Trusted-Domain,${SCHEMADN}
+
+dn: CN=Domain,${SCHEMADN}
+objectClass: top
+objectClass: classSchema
+subClassOf: top
+governsID: 1.2.840.113556.1.5.66
+possibleInferiors: domainDNS
+rDNAttID: dc
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Domain
+adminDescription: Domain
+objectClassCategory: 2
+lDAPDisplayName: domain
+schemaIDGUID: 19195a5a-6da0-11d0-afd3-00c04fd930c9
+systemOnly: FALSE
+systemPossSuperiors: domain
+systemPossSuperiors: organization
+systemMustContain: dc
+systemFlags: 16
+defaultHidingValue: TRUE
+defaultObjectCategory: CN=Domain-DNS,${SCHEMADN}
+
+dn: CN=Foreign-Security-Principal,${SCHEMADN}
+objectClass: top
+objectClass: classSchema
+subClassOf: top
+governsID: 1.2.840.113556.1.5.76
+rDNAttID: cn
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Foreign-Security-Principal
+adminDescription: Foreign-Security-Principal
+objectClassCategory: 1
+lDAPDisplayName: foreignSecurityPrincipal
+schemaIDGUID: 89e31c12-8530-11d0-afda-00c04fd930c9
+systemOnly: FALSE
+systemPossSuperiors: container
+systemMayContain: foreignIdentifier
+systemMustContain: objectSid
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;AO)(A;;RPLCLORC;;;PS)(OA;;CR;ab721a53-1e2f-11d0-9819-00aa0040529b;;PS)(OA;;CR;ab721a54-1e2f-11d0-9819-00aa0040529b;;PS)(OA;;CR;ab721a56-1e2f-11d0-9819-00aa0040529b;;PS)(OA;;RPWP;77B5B886-944A-11d1-AEBD-0000F80367C1;;PS)(OA;;RPWP;E45795B2-9455-11d1-AEBD-0000F80367C1;;PS)(OA;;RPWP;E45795B3-9455-11d1-AEBD-0000F80367C1;;PS)(A;;RC;;;AU)(OA;;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;;AU)(OA;;RP;77B5B886-944A-11d1-AEBD-0000F80367C1;;AU)(OA;;RP;E45795B3-9455-11d1-AEBD-0000F80367C1;;AU)(OA;;RP;e48d0154-bcf8-11d1-8702-00c04fb96050;;AU)(OA;;CR;ab721a53-1e2f-11d0-9819-00aa0040529b;;WD)
+systemFlags: 16
+defaultHidingValue: TRUE
+defaultObjectCategory: CN=Foreign-Security-Principal,${SCHEMADN}
+
+dn: CN=Subnet,${SCHEMADN}
+objectClass: top
+objectClass: classSchema
+subClassOf: top
+governsID: 1.2.840.113556.1.5.96
+possibleInferiors: container
+possibleInferiors: groupPolicyContainer
+rDNAttID: cn
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Subnet
+adminDescription: Subnet
+objectClassCategory: 1
+lDAPDisplayName: subnet
+schemaIDGUID: b7b13124-b82e-11d0-afee-0000f80367c1
+systemOnly: FALSE
+systemPossSuperiors: subnetContainer
+systemMayContain: siteObject
+systemMayContain: physicalLocationObject
+systemMayContain: location
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+systemFlags: 16
+defaultHidingValue: TRUE
+defaultObjectCategory: CN=Subnet,${SCHEMADN}
+
+dn: CN=Mail-Recipient,${SCHEMADN}
+objectClass: top
+objectClass: classSchema
+subClassOf: top
+governsID: 1.2.840.113556.1.3.46
+mayContain: userSMIMECertificate
+mayContain: secretary
+mayContain: msExchLabeledURI
+mayContain: msExchAssistantName
+mayContain: labeledURI
+rDNAttID: cn
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Mail-Recipient
+adminDescription: Mail-Recipient
+objectClassCategory: 3
+lDAPDisplayName: mailRecipient
+schemaIDGUID: bf967aa1-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemPossSuperiors: container
+systemMayContain: userCertificate
+systemMayContain: userCert
+systemMayContain: textEncodedORAddress
+systemMayContain: telephoneNumber
+systemMayContain: showInAddressBook
+systemMayContain: legacyExchangeDN
+systemMayContain: garbageCollPeriod
+systemMayContain: info
+systemMustContain: cn
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+systemFlags: 16
+defaultHidingValue: TRUE
+defaultObjectCategory: CN=Mail-Recipient,${SCHEMADN}
+
+dn: CN=Group,${SCHEMADN}
+objectClass: top
+objectClass: classSchema
+subClassOf: top
+governsID: 1.2.840.113556.1.5.8
+rDNAttID: cn
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Group
+adminDescription: Group
+objectClassCategory: 1
+lDAPDisplayName: group
+schemaIDGUID: bf967a9c-0de6-11d0-a285-00aa003049e2
+systemOnly: FALSE
+systemPossSuperiors: msDS-AzScope
+systemPossSuperiors: msDS-AzApplication
+systemPossSuperiors: msDS-AzAdminManager
+systemPossSuperiors: container
+systemPossSuperiors: builtinDomain
+systemPossSuperiors: organizationalUnit
+systemPossSuperiors: domainDNS
+systemMayContain: primaryGroupToken
+systemMayContain: operatorCount
+systemMayContain: nTGroupMembers
+systemMayContain: nonSecurityMember
+systemMayContain: msDS-NonMembers
+systemMayContain: msDS-AzLDAPQuery
+systemMayContain: member
+systemMayContain: managedBy
+systemMayContain: groupMembershipSAM
+systemMayContain: groupAttributes
+systemMayContain: mail
+systemMayContain: desktopProfile
+systemMayContain: controlAccessRights
+systemMayContain: adminCount
+systemMustContain: groupType
+systemAuxiliaryClass: mailRecipient
+systemAuxiliaryClass: securityPrincipal
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;AO)(A;;RPLCLORC;;;PS)(OA;;CR;ab721a55-1e2f-11d0-9819-00aa0040529b;;AU)(OA;;RP;46a9b11d-60ae-405a-b7e8-ff8a58d456d2;;S-1-5-32-560)
+systemFlags: 16
+defaultHidingValue: FALSE
+defaultObjectCategory: CN=Group,${SCHEMADN}
+
+dn: CN=Group-Policy-Container,${SCHEMADN}
+objectClass: top
+objectClass: classSchema
+subClassOf: container
+governsID: 1.2.840.113556.1.5.157
+possibleInferiors: group
+possibleInferiors: trustedDomain
+possibleInferiors: computer
+possibleInferiors: foreignSecurityPrincipal
+possibleInferiors: user
+possibleInferiors: container
+possibleInferiors: groupPolicyContainer
+possibleInferiors: person
+possibleInferiors: ipsecNFA
+possibleInferiors: queryPolicy
+possibleInferiors: msDS-AzAdminManager
+possibleInferiors: displaySpecifier
+possibleInferiors: nTDSService
+possibleInferiors: secret
+possibleInferiors: ipsecPolicy
+possibleInferiors: organizationalPerson
+possibleInferiors: ipsecISAKMPPolicy
+rDNAttID: cn
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Group-Policy-Container
+adminDescription: Group-Policy-Container
+objectClassCategory: 1
+lDAPDisplayName: groupPolicyContainer
+schemaIDGUID: f30e3bc2-9ff0-11d1-b603-0000f80367c1
+systemOnly: FALSE
+systemMayContain: versionNumber
+systemMayContain: gPCWQLFilter
+systemMayContain: gPCUserExtensionNames
+systemMayContain: gPCMachineExtensionNames
+systemMayContain: gPCFunctionalityVersion
+systemMayContain: gPCFileSysPath
+systemMayContain: flags
+defaultSecurityDescriptor: D:P(A;CI;RPWPCCDCLCLOLORCWOWDSDDTSW;;;DA)(A;CI;RPWPCCDCLCLOLORCWOWDSDDTSW;;;EA)(A;CI;RPWPCCDCLCLOLORCWOWDSDDTSW;;;CO)(A;CI;RPWPCCDCLCLORCWOWDSDDTSW;;;SY)(A;CI;RPLCLORC;;;AU)(OA;CI;CR;edacfd8f-ffb3-11d1-b41d-00a0c968f939;;AU)(A;CI;LCRPLORC;;;ED)
+systemFlags: 16
+defaultHidingValue: TRUE
+defaultObjectCategory: CN=Group-Policy-Container,${SCHEMADN}
+
diff --git a/source4/setup/schema_samba4.ldif b/source4/setup/schema_samba4.ldif
new file mode 100644
index 0000000000..47010da637
--- /dev/null
+++ b/source4/setup/schema_samba4.ldif
@@ -0,0 +1,373 @@
+#
+# Schema elements which do not exist in AD, but which we use in Samba4
+#
+## Samba4 OID allocation from Samba3's examples/LDAP/samba.schema
+## 1.3.6.1.4.1.7165.4.1.x - attributetypes
+
+## 1.3.6.1.4.1.7165.4.2.x - objectclasses
+
+## 1.3.6.1.4.1.7165.4.3.x - LDB/LDAP Controls
+### see dsdb/samdb/samdb.h
+
+## 1.3.6.1.4.1.7165.4.4.x - LDB/LDAP Extended Operations
+### see dsdb/samdb/samdb.h
+
+## 1.3.6.1.4.1.7165.4.255.x - mapped OIDs due to conflicts between AD and standards-track
+#
+#
+
+
+#
+# Not used anymore
+#
+#dn: cn=ntpwdHash,${SCHEMADN}
+#cn: ntpwdHash
+#name: NTPWDHash
+#objectClass: top
+#objectClass: attributeSchema
+#lDAPDisplayName: ntpwdhash
+#isSingleValued: TRUE
+#systemFlags: 17
+#systemOnly: TRUE
+#schemaIDGUID: E961130F-5084-458C-9E9C-DEC16DA08592
+#adminDisplayName: NT-PWD-Hash
+#attributeID: 1.3.6.1.4.1.7165.4.1.1
+#attributeSyntax: 2.5.5.10
+#oMSyntax: 4
+
+#
+# Not used anymore
+#
+#dn: cn=lmpwdHash,${SCHEMADN}
+#cn: lmpwdHash
+#name: lmpwdHash
+#objectClass: top
+#objectClass: attributeSchema
+#lDAPDisplayName: lmpwdhash
+#isSingleValued: TRUE
+#systemFlags: 17
+#systemOnly: TRUE
+#schemaIDGUID: CBD0D18C-9C54-4A77-87C4-5CEEAF781253
+#adminDisplayName: LM-PWD-Hash
+#attributeID: 1.3.6.1.4.1.7165.4.1.2
+#attributeSyntax: 2.5.5.10
+#oMSyntax: 4
+
+#
+# Not used anymore
+#
+#dn: cn=sambaNtPwdHistory,${SCHEMADN}
+#cn: sambaNtPwdHistory
+#name: sambaNtPwdHistory
+#objectClass: top
+#objectClass: attributeSchema
+#lDAPDisplayName: sambaNtPwdHistory
+#isSingleValued: TRUE
+#systemFlags: 17
+#systemOnly: TRUE
+#schemaIDGUID: 8CCD7658-C574-4435-A38C-99572E349E6B
+#adminDisplayName: SAMBA-NT-PWD-History
+#attributeID: 1.3.6.1.4.1.7165.4.1.3
+#attributeSyntax: 2.5.5.10
+#oMSyntax: 4
+
+#
+# Not used anymore
+#
+#dn: cn=sambaLmPwdHistory,${SCHEMADN}
+#cn: sambaLmPwdHistory
+#name: sambaLmPwdHistory
+#objectClass: top
+#objectClass: attributeSchema
+#lDAPDisplayName: sambaLmPwdHistory
+#isSingleValued: FALSE
+#systemFlags: 17
+#systemOnly: TRUE
+#schemaIDGUID: 0EAFE3DD-0F53-495E-8A34-97BB28AF17A4
+#adminDisplayName: SAMBA-LM-PWDHistory
+#attributeID: 1.3.6.1.4.1.7165.4.1.4
+#attributeSyntax: 2.5.5.10
+#oMSyntax: 4
+
+#
+# Not used anymore
+#
+#dn: CN=sambaPassword,${SCHEMADN}
+#objectClass: top
+#objectClass: attributeSchema
+#lDAPDisplayName: sambaPassword
+#isSingleValued: FALSE
+#systemFlags: 17
+#systemOnly: TRUE
+#schemaIDGUID: 87F10301-229A-4E69-B63A-998339ADA37A
+#adminDisplayName: SAMBA-Password
+#attributeID: 1.3.6.1.4.1.7165.4.1.5
+#attributeSyntax: 2.5.5.5
+#oMSyntax: 22
+
+#
+# Not used anymore
+#
+#dn: cn=dnsDomain,${SCHEMADN}
+#objectClass: top
+#objectClass: attributeSchema
+#lDAPDisplayName: dnsDomain
+#isSingleValued: FALSE
+#systemFlags: 17
+#systemOnly: TRUE
+#schemaIDGUID: A40165E6-5E45-44A7-A8FA-186C94333018
+#adminDisplayName: DNS-Domain
+#attributeID: 1.3.6.1.4.1.7165.4.1.6
+#attributeSyntax: 2.5.5.4
+#oMSyntax: 20
+
+dn: cn=privilege,${SCHEMADN}
+objectClass: top
+objectClass: attributeSchema
+lDAPDisplayName: privilege
+isSingleValued: FALSE
+systemFlags: 17
+systemOnly: TRUE
+schemaIDGUID: 7429BC94-CC6A-4481-8B2C-A97E316EB182
+adminDisplayName: Privilege
+attributeID: 1.3.6.1.4.1.7165.4.1.7
+attributeSyntax: 2.5.5.4
+oMSyntax: 20
+
+#
+# Not used anymore
+#
+#dn: CN=unixName,${SCHEMADN}
+#cn: unixName
+#name: unixName
+#objectClass: top
+#objectClass: attributeSchema
+#lDAPDisplayName: unixName
+#isSingleValued: TRUE
+#systemFlags: 16
+#systemOnly: FALSE
+#schemaIDGUID: bf9679f2-0de6-11d0-a285-00aa003049e2
+#adminDisplayName: Unix-Name
+#attributeID: 1.3.6.1.4.1.7165.4.1.9
+#attributeSyntax: 2.5.5.4
+#oMSyntax: 20
+
+#
+# Not used anymore
+#
+#dn: cn=krb5Key,${SCHEMADN}
+#cn: krb5Key
+#name: krb5Key
+#objectClass: top
+#objectClass: attributeSchema
+#lDAPDisplayName: krb5Key
+#isSingleValued: FALSE
+#systemFlags: 17
+#systemOnly: TRUE
+#schemaIDGUID: 0EAFE3DD-0F53-495E-8A34-97BB28AF17A4
+#adminDisplayName: krb5-Key
+#attributeID: 1.3.6.1.4.1.5322.10.1.10
+#attributeSyntax: 2.5.5.10
+#oMSyntax: 4
+
+#Allocated: (not used anymore) DSDB_CONTROL_REPLICATED_OBJECT_OID 1.3.6.1.4.1.7165.4.3.1
+
+#Allocated: DSDB_CONTROL_CURRENT_PARTITION_OID 1.3.6.1.4.1.7165.4.3.2
+
+#Allocated: DSDB_CONTROL_REPLICATED_UPDATE_OID 1.3.6.1.4.1.7165.4.3.3
+
+#Allocated: DSDB_EXTENDED_REPLICATED_OBJECTS_OID 1.3.6.1.4.1.7165.4.4.1
+#Allocated: DSDB_EXTENDED_SCHEMA_UPDATE_NOW_OID 1.3.6.1.4.1.7165.4.4.2
+#Allocated: LDB_EXTENDED_SEQUENCE_NUMBER 1.3.6.1.4.1.7165.4.4.3
+
+#Allocated: (middleName) attributeID: 1.3.6.1.4.1.7165.4.255.1
+
+#Allocated: (defaultGroup) attributeID: 1.3.6.1.4.1.7165.4.255.2
+
+#Allocated: (modifyTimestamp) samba4ModifyTimestamp: 1.3.6.1.4.1.7165.4.255.3
+#Allocated: (subSchema) samba4SubSchema: 1.3.6.1.4.1.7165.4.255.4
+#Allocated: (objectClasses) samba4ObjectClasses: 1.3.6.1.4.1.7165.4.255.5
+#Allocated: (ditContentRules) samba4DitContentRules: 1.3.6.1.4.1.7165.4.255.6
+#Allocated: (attributeTypes) samba4AttributeTypes: 1.3.6.1.4.1.7165.4.255.7
+#Allocated: (dynamicObject) samba4DynamicObject: 1.3.6.1.4.1.7165.4.255.8
+#Allocated: (entryTTL) samba4EntryTTL: 1.3.6.1.4.1.7165.4.255.9
+
+#
+# Fedora DS uses this attribute, and we need to set it via our module stack
+#
+dn: CN=aci,${SCHEMADN}
+cn: aci
+name: aci
+objectClass: top
+objectClass: attributeSchema
+lDAPDisplayName: aci
+isSingleValued: TRUE
+systemFlags: 16
+systemOnly: FALSE
+schemaIDGUID: d8e6c1fa-db08-4f26-a53b-23c414aac92d
+adminDisplayName: aci
+attributeID: 1.3.6.1.4.1.7165.4.1.11
+attributeSyntax: 2.5.5.4
+oMSyntax: 20
+
+#
+# Based on domainDNS, but without the DNS bits.
+#
+
+dn: CN=Samba4-Local-Domain,${SCHEMADN}
+objectClass: top
+objectClass: classSchema
+subClassOf: top
+governsID: 1.3.6.1.4.1.7165.4.2.2
+possibleInferiors: group
+possibleInferiors: lostAndFound
+possibleInferiors: builtinDomain
+possibleInferiors: computer
+possibleInferiors: user
+possibleInferiors: container
+possibleInferiors: groupPolicyContainer
+possibleInferiors: organization
+possibleInferiors: domainDNS
+possibleInferiors: locality
+possibleInferiors: msDS-AzAdminManager
+possibleInferiors: country
+possibleInferiors: organizationalUnit
+rDNAttID: cn
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Samba4-Local-Domain
+adminDescription: Samba4-Local-Domain
+systemMayContain: msDS-Behavior-Version
+systemMayContain: managedBy
+objectClassCategory: 1
+lDAPDisplayName: samba4LocalDomain
+schemaIDGUID: 07be1647-8310-4fba-91ae-34e55d5a8293
+systemOnly: FALSE
+systemAuxiliaryClass: samDomain
+defaultSecurityDescriptor: D:(A;;RPLCLORC;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+systemFlags: 16
+defaultHidingValue: TRUE
+defaultObjectCategory: CN=Samba4-Local-Domain,${SCHEMADN}
+
+
+dn: CN=Samba4Top,${SCHEMADN}
+objectClass: top
+objectClass: classSchema
+subClassOf: top
+governsID: 1.3.6.1.4.1.7165.4.2.1
+mayContain: msDS-ObjectReferenceBL
+rDNAttID: cn
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Samba4TopTop
+adminDescription: Attributes used in top in Samba4 that OpenLDAP does not
+objectClassCategory: 3
+lDAPDisplayName: samba4Top
+schemaIDGUID: 073598d0-635b-4685-a929-da731b98d84e
+systemOnly: TRUE
+systemPossSuperiors: lostAndFound
+systemMayContain: url
+systemMayContain: wWWHomePage
+systemMayContain: wellKnownObjects
+systemMayContain: wbemPath
+systemMayContain: uSNSource
+systemMayContain: uSNLastObjRem
+systemMayContain: USNIntersite
+systemMayContain: uSNDSALastObjRemoved
+systemMayContain: systemFlags
+systemMayContain: subRefs
+systemMayContain: siteObjectBL
+systemMayContain: serverReferenceBL
+systemMayContain: sDRightsEffective
+systemMayContain: revision
+systemMayContain: repsTo
+systemMayContain: repsFrom
+systemMayContain: directReports
+systemMayContain: replUpToDateVector
+systemMayContain: replPropertyMetaData
+systemMayContain: name
+systemMayContain: queryPolicyBL
+systemMayContain: proxyAddresses
+systemMayContain: proxiedObjectName
+systemMayContain: possibleInferiors
+systemMayContain: partialAttributeSet
+systemMayContain: partialAttributeDeletionList
+systemMayContain: otherWellKnownObjects
+systemMayContain: objectVersion
+systemMayContain: nonSecurityMemberBL
+systemMayContain: netbootSCPBL
+systemMayContain: ownerBL
+systemMayContain: msDS-ReplValueMetaData
+systemMayContain: msDS-ReplAttributeMetaData
+systemMayContain: msDS-NonMembersBL
+systemMayContain: msDS-NCReplOutboundNeighbors
+systemMayContain: msDS-NCReplInboundNeighbors
+systemMayContain: msDS-NCReplCursors
+systemMayContain: msDS-TasksForAzRoleBL
+systemMayContain: msDS-TasksForAzTaskBL
+systemMayContain: msDS-OperationsForAzRoleBL
+systemMayContain: msDS-OperationsForAzTaskBL
+systemMayContain: msDS-MembersForAzRoleBL
+systemMayContain: msDs-masteredBy
+systemMayContain: mS-DS-ConsistencyGuid
+systemMayContain: mS-DS-ConsistencyChildCount
+systemMayContain: msDS-Approx-Immed-Subordinates
+systemMayContain: msCOM-PartitionSetLink
+systemMayContain: msCOM-UserLink
+systemMayContain: masteredBy
+systemMayContain: managedObjects
+systemMayContain: lastKnownParent
+systemMayContain: isPrivilegeHolder
+systemMayContain: isDeleted
+systemMayContain: isCriticalSystemObject
+systemMayContain: showInAdvancedViewOnly
+systemMayContain: fSMORoleOwner
+systemMayContain: fRSMemberReferenceBL
+systemMayContain: frsComputerReferenceBL
+systemMayContain: fromEntry
+systemMayContain: flags
+systemMayContain: extensionName
+systemMayContain: dSASignature
+systemMayContain: dSCorePropagationData
+systemMayContain: displayNamePrintable
+systemMayContain: displayName
+systemMayContain: description
+systemMayContain: cn
+systemMayContain: canonicalName
+systemMayContain: bridgeheadServerListBL
+systemMayContain: allowedChildClassesEffective
+systemMayContain: allowedChildClasses
+systemMayContain: allowedAttributesEffective
+systemMayContain: allowedAttributes
+systemMayContain: adminDisplayName
+systemMayContain: adminDescription
+systemMustContain: objectCategory
+systemMustContain: nTSecurityDescriptor
+systemMustContain: instanceType
+systemAuxiliaryClass: samba4TopExtra
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+systemFlags: 16
+defaultHidingValue: TRUE
+objectCategory: CN=Class-Schema,${SCHEMADN}
+defaultObjectCategory: CN=Samba4Top,${SCHEMADN}
+
+
+dn: CN=Samba4TopExtra,${SCHEMADN}
+objectClass: top
+objectClass: classSchema
+subClassOf: top
+governsID: 1.3.6.1.4.1.7165.4.2.3
+rDNAttID: cn
+showInAdvancedViewOnly: TRUE
+adminDisplayName: Samba4TopExtra
+adminDescription: Attributes used in top in Samba4 that OpenLDAP does not
+objectClassCategory: 2
+lDAPDisplayName: samba4TopExtra
+schemaIDGUID: 073598d0-635b-4685-a929-da731b98d84e
+systemOnly: TRUE
+mayContain: privilege
+systemPossSuperiors: lostAndFound
+defaultSecurityDescriptor: D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)
+systemFlags: 16
+defaultHidingValue: TRUE
+objectCategory: CN=Class-Schema,${SCHEMADN}
+defaultObjectCategory: CN=Samba4TopExtra,${SCHEMADN}
+
diff --git a/source4/setup/secrets.ldif b/source4/setup/secrets.ldif
new file mode 100644
index 0000000000..95cbe20e5f
--- /dev/null
+++ b/source4/setup/secrets.ldif
@@ -0,0 +1,10 @@
+dn: CN=LSA Secrets
+objectClass: top
+objectClass: container
+cn: LSA Secrets
+
+dn: CN=Primary Domains
+objectClass: top
+objectClass: container
+cn: Primary Domains
+
diff --git a/source4/setup/secrets_dc.ldif b/source4/setup/secrets_dc.ldif
new file mode 100644
index 0000000000..8ae5578e6b
--- /dev/null
+++ b/source4/setup/secrets_dc.ldif
@@ -0,0 +1,39 @@
+dn: flatname=${DOMAIN},CN=Primary Domains
+objectClass: top
+objectClass: primaryDomain
+objectClass: kerberosSecret
+flatname: ${DOMAIN}
+realm: ${REALM}
+secret:: ${MACHINEPASS_B64}
+secureChannelType: 6
+sAMAccountName: ${NETBIOSNAME}$
+msDS-KeyVersionNumber: 1
+objectSid: ${DOMAINSID}
+privateKeytab: ${SECRETS_KEYTAB}
+
+# A hook from our credentials system into HDB, as we must be on a KDC,
+# we can look directly into the database.
+dn: samAccountName=krbtgt,flatname=${DOMAIN},CN=Principals
+objectClass: top
+objectClass: secret
+objectClass: kerberosSecret
+flatname: ${DOMAIN}
+realm: ${REALM}
+sAMAccountName: krbtgt
+objectSid: ${DOMAINSID}
+servicePrincipalName: kadmin/changepw
+krb5Keytab: HDB:samba4:${SAM_LDB}:
+#The trailing : here is a HACK, but it matches the Heimdal format.
+
+# A hook from our credentials system into HDB, as we must be on a KDC,
+# we can look directly into the database.
+dn: servicePrincipalName=DNS/${DNSDOMAIN},CN=Principals
+objectClass: top
+objectClass: secret
+objectClass: kerberosSecret
+realm: ${REALM}
+servicePrincipalName: DNS/${DNSDOMAIN}
+msDS-KeyVersionNumber: 1
+privateKeytab: ${DNS_KEYTAB}
+secret:: ${DNSPASS_B64}
+
diff --git a/source4/setup/secrets_init.ldif b/source4/setup/secrets_init.ldif
new file mode 100644
index 0000000000..eb423a5122
--- /dev/null
+++ b/source4/setup/secrets_init.ldif
@@ -0,0 +1,15 @@
+dn: @INDEXLIST
+@IDXATTR: cn
+@IDXATTR: flatname
+@IDXATTR: realm
+
+dn: @ATTRIBUTES
+realm: CASE_INSENSITIVE
+flatname: CASE_INSENSITIVE
+sAMAccountName: CASE_INSENSITIVE
+
+#Add modules to the list to activate them by default
+#beware often order is important
+dn: @MODULES
+@LIST: update_keytab,operational,objectguid,rdn_name
+
diff --git a/source4/setup/secrets_sasl_ldap.ldif b/source4/setup/secrets_sasl_ldap.ldif
new file mode 100644
index 0000000000..81ccfee209
--- /dev/null
+++ b/source4/setup/secrets_sasl_ldap.ldif
@@ -0,0 +1,9 @@
+dn: CN=SAMDB Credentials
+objectClass: top
+objectClass: ldapSecret
+cn: SAMDB Credentials
+secret:: ${LDAPADMINPASS_B64}
+samAccountName: ${LDAPADMINUSER}
+realm: ${LDAPADMINREALM}
+
+
diff --git a/source4/setup/secrets_simple_ldap.ldif b/source4/setup/secrets_simple_ldap.ldif
new file mode 100644
index 0000000000..3f5ccd2df1
--- /dev/null
+++ b/source4/setup/secrets_simple_ldap.ldif
@@ -0,0 +1,6 @@
+dn: CN=SAMDB Credentials
+objectClass: top
+objectClass: ldapSecret
+cn: SAMDB Credentials
+secret:: ${LDAPMANAGERPASS_B64}
+ldapBindDn: ${LDAPMANAGERDN}
diff --git a/source4/setup/setexpiry b/source4/setup/setexpiry
new file mode 100755
index 0000000000..e47330510c
--- /dev/null
+++ b/source4/setup/setexpiry
@@ -0,0 +1,44 @@
+#!/usr/bin/python
+#
+# set the password expiry for a user
+# Copyright Andrew Tridgell 2005
+# Copyright Jelmer Vernooij 2008
+# Released under the GNU GPL version 3 or later
+#
+
+import sys
+
+# Find right directory when running from source tree
+sys.path.insert(0, "bin/python")
+
+import samba.getopt as options
+import optparse
+from getpass import getpass
+from samba.auth import system_session
+
+parser = optparse.OptionParser("setexpiry [options] <username>")
+sambaopts = options.SambaOptions(parser)
+parser.add_option_group(sambaopts)
+parser.add_option_group(options.VersionOptions(parser))
+credopts = options.CredentialsOptions(parser)
+parser.add_option_group(credopts)
+parser.add_option("--days", help="Days to expiry", type=int)
+parser.add_option("--noexpiry", help="Never expire", action="store_true")
+
+opts, args = parser.parse_args()
+
+if len(args) == 0:
+ parser.print_usage()
+ sys.exit(1)
+
+username = args[0]
+
+lp = sambaopts.get_loadparm()
+creds = credopts.get_credentials(lp)
+
+samdb = sambaopts.get_hostconfig().get_samdb(session_info=system_session(),
+ credentials=creds)
+days = opts.days
+if days is None:
+ days = 0
+samdb.setexpiry(username, days*24*3600, opts.noexpiry)
diff --git a/source4/setup/setpassword b/source4/setup/setpassword
new file mode 100755
index 0000000000..90a217fb6f
--- /dev/null
+++ b/source4/setup/setpassword
@@ -0,0 +1,77 @@
+#!/usr/bin/python
+#
+# Add a new user to a Samba4 server
+# Copyright Jelmer Vernooij 2008
+#
+# Based on the original in EJS:
+# Copyright 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 <http://www.gnu.org/licenses/>.
+#
+
+import os, sys
+
+# Find right directory when running from source tree
+sys.path.insert(0, "bin/python")
+
+import samba.getopt as options
+import optparse
+import pwd
+import sys
+from getpass import getpass
+from samba.auth import system_session
+from samba.samdb import SamDB
+
+parser = optparse.OptionParser("setpassword [username] [options]")
+sambaopts = options.SambaOptions(parser)
+parser.add_option_group(sambaopts)
+parser.add_option_group(options.VersionOptions(parser))
+credopts = options.CredentialsOptions(parser)
+parser.add_option_group(credopts)
+parser.add_option("--filter", help="LDAP Filter to set password on", type=str)
+parser.add_option("--newpassword", help="Set password", type=str)
+
+opts, args = parser.parse_args()
+
+#
+# print a message if quiet is not set
+#
+def message(text):
+ if not opts.quiet:
+ print text
+
+if len(args) == 0:
+ parser.print_usage()
+ sys.exit(1)
+
+password = opts.newpassword;
+if password is None:
+ password = getpass("New Password: ")
+
+filter = opts.filter
+
+if filter is None:
+ username = args[0]
+ if username is None:
+ print "Either username or --filter must be specified"
+
+ filter = "(&(objectclass=user)(samAccountName=%s))" % (username)
+
+
+lp = sambaopts.get_loadparm()
+creds = credopts.get_credentials(lp)
+
+samdb = SamDB(url=lp.get("sam database"), session_info=system_session(),
+ credentials=creds, lp=lp)
+samdb.setpassword(filter, password)
diff --git a/source4/setup/share.ldif b/source4/setup/share.ldif
new file mode 100644
index 0000000000..750a070c8a
--- /dev/null
+++ b/source4/setup/share.ldif
@@ -0,0 +1,46 @@
+dn: @INDEXLIST
+@IDXATTR: name
+
+dn: @ATTRIBUTES
+cn: CASE_INSENSITIVE
+dc: CASE_INSENSITIVE
+name: CASE_INSENSITIVE
+dn: CASE_INSENSITIVE
+objectClass: CASE_INSENSITIVE
+
+### Shares basedn
+dn: CN=Shares
+objectClass: top
+objectClass: organizationalUnit
+cn: Shares
+
+### Default IPC$ Share
+dn: CN=IPC$,CN=Shares
+objectClass: top
+objectClass: share
+cn: IPC$
+name: IPC$
+type: IPC
+path: /tmp
+comment: Remote IPC
+max-connections: -1
+available: True
+readonly: True
+browseable: False
+ntvfs-handler: default
+
+### Default ADMIN$ Share
+dn: CN=ADMIN$,CN=Shares
+objectClass: top
+objectClass: share
+cn: ADMIN$
+name: ADMIN$
+type: DISK
+path: /tmp
+comment: Remote Admin
+max-connections: -1
+available: True
+readonly: True
+browseable: False
+ntvfs-handler: default
+
diff --git a/source4/setup/slapd.conf b/source4/setup/slapd.conf
new file mode 100644
index 0000000000..09dffbbfa3
--- /dev/null
+++ b/source4/setup/slapd.conf
@@ -0,0 +1,155 @@
+loglevel 0
+
+### needed for initial content load ###
+sizelimit unlimited
+
+### Multimaster-ServerIDs and URLs ###
+
+${MMR_SERVERIDS_CONFIG}
+
+include ${LDAPDIR}/backend-schema.schema
+
+pidfile ${LDAPDIR}/slapd.pid
+argsfile ${LDAPDIR}/slapd.args
+sasl-realm ${DNSDOMAIN}
+
+#authz-regexp
+# uid=([^,]*),cn=${DNSDOMAIN},cn=digest-md5,cn=auth
+# ldap:///${DOMAINDN}??sub?(samAccountName=\$1)
+
+#authz-regexp
+# uid=([^,]*),cn=([^,]*),cn=digest-md5,cn=auth
+# ldap:///${DOMAINDN}??sub?(samAccountName=\$1)
+
+authz-regexp
+ uid=([^,]*),cn=([^,]*),cn=digest-md5,cn=auth
+ ldap:///cn=samba??one?(cn=\$1)
+
+authz-regexp
+ uid=([^,]*),cn=([^,]*),cn=ntlm,cn=auth
+ ldap:///cn=samba??one?(cn=\$1)
+
+access to dn.base=""
+ by dn=cn=samba-admin,cn=samba manage
+ by anonymous read
+ by * read
+
+access to dn.subtree="cn=samba"
+ by anonymous auth
+
+access to dn.subtree="${DOMAINDN}"
+ by dn=cn=samba-admin,cn=samba manage${REPLICATOR_ACL}
+ by dn=cn=manager manage
+ by * none
+
+password-hash {CLEARTEXT}
+
+include ${LDAPDIR}/modules.conf
+
+defaultsearchbase ${DOMAINDN}
+
+rootdn cn=Manager
+
+overlay deref
+
+${REFINT_CONFIG}
+
+${MEMBEROF_CONFIG}
+
+database ldif
+suffix cn=Samba
+directory ${LDAPDIR}/db/samba
+rootdn cn=Manager,cn=Samba
+
+########################################
+## olc - configuration ###
+${OLC_CONFIG_PASS}
+${OLC_SYNCREPL_CONFIG}
+${OLC_MMR_CONFIG}
+${OLC_CONFIG_ACL}
+
+########################################
+### cn=schema ###
+database hdb
+suffix ${SCHEMADN}
+rootdn cn=Manager,${SCHEMADN}
+directory ${LDAPDIR}/db/schema
+index objectClass eq
+index samAccountName eq
+index name eq
+index objectCategory eq
+index lDAPDisplayName eq
+index subClassOf eq
+index cn eq
+index entryUUID,entryCSN eq
+
+#syncprov is stable in OpenLDAP 2.3, and available in 2.2.
+#We need this for the contextCSN attribute and mmr.
+overlay syncprov
+syncprov-sessionlog 100
+syncprov-checkpoint 100 10
+
+
+### Multimaster-Replication of cn=schema Subcontext ###
+${MMR_SYNCREPL_SCHEMA_CONFIG}
+${MIRRORMODE}
+
+#########################################
+### cn=config ###
+database hdb
+suffix ${CONFIGDN}
+rootdn cn=Manager,${CONFIGDN}
+directory ${LDAPDIR}/db/config
+index objectClass eq
+index samAccountName eq
+index name eq
+index objectSid eq
+index objectCategory eq
+index nCName eq
+index subClassOf eq
+index dnsRoot eq
+index nETBIOSName eq
+index cn eq
+index entryUUID,entryCSN eq
+
+#syncprov is stable in OpenLDAP 2.3, and available in 2.2.
+#We need this for the contextCSN attribute and mmr.
+overlay syncprov
+syncprov-sessionlog 100
+syncprov-checkpoint 100 10
+
+### Multimaster-Replication of cn=config Subcontext ###
+${MMR_SYNCREPL_CONFIG_CONFIG}
+${MIRRORMODE}
+
+########################################
+### cn=users /base-dn ###
+database hdb
+suffix ${DOMAINDN}
+rootdn cn=Manager,${DOMAINDN}
+directory ${LDAPDIR}/db/user
+index objectClass eq
+index samAccountName eq
+index name eq
+index objectSid eq
+index objectCategory eq
+index member eq
+index uidNumber eq
+index gidNumber eq
+index nCName eq
+index lDAPDisplayName eq
+index subClassOf eq
+index dnsRoot eq
+index nETBIOSName eq
+index cn eq
+index entryUUID,entryCSN eq
+
+#syncprov is stable in OpenLDAP 2.3, and available in 2.2.
+#We need this for the contextCSN attribute and mmr.
+overlay syncprov
+syncprov-sessionlog 100
+syncprov-checkpoint 100 10
+
+### Multimaster-Replication of cn=user/base-dn context ###
+${MMR_SYNCREPL_USER_CONFIG}
+${MIRRORMODE}
diff --git a/source4/setup/tests/blackbox_newuser.sh b/source4/setup/tests/blackbox_newuser.sh
new file mode 100755
index 0000000000..d25c70669b
--- /dev/null
+++ b/source4/setup/tests/blackbox_newuser.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+if [ $# -lt 1 ]; then
+cat <<EOF
+Usage: blackbox_newuser.sh PREFIX
+EOF
+exit 1;
+fi
+
+PREFIX="$1"
+shift 1
+
+. `dirname $0`/../../../testprogs/blackbox/subunit.sh
+
+
+testit "simple-dc" $PYTHON ./setup/provision --server-role="dc" --domain=FOO --realm=foo.example.com --domain-sid=S-1-5-21-4177067393-1453636373-93818738 --targetdir=$PREFIX/simple-dc
+
+CONFIG="--configfile=$PREFIX/simple-dc/etc/smb.conf"
+
+testit "newuser" $PYTHON ./setup/newuser $CONFIG testuser testpass
+
+# check the enable account script
+testit "enableaccount" $PYTHON ./setup/enableaccount $CONFIG testuser
+
+# check the enable account script
+testit "setpassword" $PYTHON ./setup/setpassword $CONFIG testuser --newpassword=testpass2
+
+# check the setexpiry script
+testit "noexpiry" $PYTHON ./setup/setexpiry $CONFIG testuser --noexpiry
+testit "expiry" $PYTHON ./setup/setexpiry $CONFIG testuser --days=7
+
+exit $failed
diff --git a/source4/setup/tests/blackbox_provision-backend.sh b/source4/setup/tests/blackbox_provision-backend.sh
new file mode 100755
index 0000000000..04f22dbf1d
--- /dev/null
+++ b/source4/setup/tests/blackbox_provision-backend.sh
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+if [ $# -lt 1 ]; then
+cat <<EOF
+Usage: blackbox_provision.sh PREFIX
+EOF
+exit 1;
+fi
+
+PREFIX="$1"
+shift 1
+
+. `dirname $0`/../../../testprogs/blackbox/subunit.sh
+
+testit "openldap-backend" $PYTHON ./setup/provision-backend --domain=FOO --realm=foo.example.com --host-name=samba --ldap-backend-type=openldap --targetdir=$PREFIX/openldap-backend
+testit "openldap-mmr-backend" $PYTHON ./setup/provision-backend --domain=FOO --realm=foo.example.com --host-name=samba --ldap-backend-type=openldap --targetdir=$PREFIX/openldap-mmr-backend --ol-mmr-urls='ldap://localdc1:9000,ldap://localdc2:9000,ldap://localdc3:9000'
+testit "fedora-ds-backend" $PYTHON ./setup/provision-backend --domain=FOO --realm=foo.example.com --host-name=samba --ldap-backend-type=fedora-ds --targetdir=$PREFIX/fedora-ds-backend
+
+reprovision() {
+ $PYTHON ./setup/provision-backend --domain=FOO --realm=foo.example.com --host-name=samba --ldap-backend-type=openldap --targetdir=$PREFIX/openldap-backend-reprovision
+ $PYTHON ./setup/provision-backend --domain=FOO --realm=foo.example.com --host-name=samba --ldap-backend-type=openldap --targetdir=$PREFIX/openldap-backend-reprovision
+}
+
+testit "reprovision-backend" reprovision
+
+exit $failed
diff --git a/source4/setup/tests/blackbox_provision.sh b/source4/setup/tests/blackbox_provision.sh
new file mode 100755
index 0000000000..1a915aff79
--- /dev/null
+++ b/source4/setup/tests/blackbox_provision.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+if [ $# -lt 1 ]; then
+cat <<EOF
+Usage: blackbox_provision.sh PREFIX
+EOF
+exit 1;
+fi
+
+PREFIX="$1"
+shift 1
+
+. `dirname $0`/../../../testprogs/blackbox/subunit.sh
+
+testit "simple-default" $PYTHON ./setup/provision --domain=FOO --realm=foo.example.com --targetdir=$PREFIX/simple-default
+testit "simple-dc" $PYTHON ./setup/provision --server-role="dc" --domain=FOO --realm=foo.example.com --domain-sid=S-1-5-21-4177067393-1453636373-93818738 --targetdir=$PREFIX/simple-dc
+testit "simple-member" $PYTHON ./setup/provision --server-role="member" --domain=FOO --realm=foo.example.com --targetdir=$PREFIX/simple-member
+testit "simple-standalone" $PYTHON ./setup/provision --server-role="standalone" --domain=FOO --realm=foo.example.com --targetdir=$PREFIX/simple-standalone
+testit "blank-dc" $PYTHON ./setup/provision --server-role="dc" --domain=FOO --realm=foo.example.com --domain-sid=S-1-5-21-4177067393-1453636373-93818738 --targetdir=$PREFIX/blank-dc --blank
+testit "partitions-only-dc" $PYTHON ./setup/provision --server-role="dc" --domain=FOO --realm=foo.example.com --domain-sid=S-1-5-21-4177067393-1453636373-93818738 --targetdir=$PREFIX/partitions-only-dc --partitions-only
+
+reprovision() {
+ $PYTHON ./setup/provision --domain=FOO --realm=foo.example.com --targetdir="$PREFIX/reprovision"
+ $PYTHON ./setup/provision --domain=FOO --realm=foo.example.com --targetdir="$PREFIX/reprovision"
+}
+
+testit "reprovision" reprovision
+
+exit $failed
diff --git a/source4/setup/tests/blackbox_setpassword.sh b/source4/setup/tests/blackbox_setpassword.sh
new file mode 100755
index 0000000000..89f1aa5858
--- /dev/null
+++ b/source4/setup/tests/blackbox_setpassword.sh
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+if [ $# -lt 1 ]; then
+cat <<EOF
+Usage: blackbox_setpassword.sh PREFIX
+EOF
+exit 1;
+fi
+
+PREFIX="$1"
+shift 1
+
+. `dirname $0`/../../../testprogs/blackbox/subunit.sh
+
+testit "simple-dc" $PYTHON ./setup/provision --server-role="dc" --domain=FOO --realm=foo.example.com --domain-sid=S-1-5-21-4177067393-1453636373-93818738 --targetdir=$PREFIX/simple-dc
+
+testit "newuser" $PYTHON ./setup/newuser --configfile=$PREFIX/simple-dc/etc/smb.conf testuser testpass
+
+testit "setpassword" $PYTHON ./setup/setpassword --configfile=$PREFIX/simple-dc/etc/smb.conf testuser --newpassword=testpass
+
+exit $failed
diff --git a/source4/setup/upgrade b/source4/setup/upgrade
new file mode 100755
index 0000000000..3d1316949f
--- /dev/null
+++ b/source4/setup/upgrade
@@ -0,0 +1,83 @@
+#!/usr/bin/python
+#
+# Upgrade from Samba3
+# Copyright Jelmer Vernooij 2005-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 <http://www.gnu.org/licenses/>.
+
+import getopt
+import optparse
+import os, sys
+
+# Find right directory when running from source tree
+sys.path.insert(0, "bin/python")
+
+import samba
+import samba.getopt as options
+from samba import param
+from samba.auth import system_session
+
+parser = optparse.OptionParser("upgrade [options] <libdir> <smbconf>")
+sambaopts = options.SambaOptions(parser)
+parser.add_option_group(sambaopts)
+parser.add_option_group(options.VersionOptions(parser))
+credopts = options.CredentialsOptions(parser)
+parser.add_option_group(credopts)
+parser.add_option("--setupdir", type="string", metavar="DIR",
+ help="directory with setup files")
+parser.add_option("--realm", type="string", metavar="REALM", help="set realm")
+parser.add_option("--quiet", help="Be quiet")
+parser.add_option("--blank",
+ help="do not add users or groups, just the structure")
+parser.add_option("--targetdir", type="string", metavar="DIR",
+ help="Set target directory")
+
+opts, args = parser.parse_args()
+
+def message(text):
+ """Print a message if quiet is not set."""
+ if opts.quiet:
+ print text
+
+if len(args) < 1:
+ parser.print_usage()
+ sys.exit(1)
+from samba.samba3 import Samba3
+message("Reading Samba3 databases and smb.conf\n")
+libdir = args[0]
+if not os.path.isdir(libdir):
+ print "error: %s is not a directory"
+ sys.exit(1)
+if len(args) > 1:
+ smbconf = args[1]
+else:
+ smbconf = os.path.join(libdir, "smb.conf")
+samba3 = Samba3(libdir, smbconf)
+
+from samba.provision import find_setup_dir
+from samba.upgrade import upgrade_provision
+
+message("Provisioning\n")
+
+setup_dir = opts.setupdir
+if setup_dir is None:
+ setup_dir = find_setup_dir()
+
+lp = sambaopts.get_loadparm()
+smbconf = lp.configfile
+creds = credopts.get_credentials(lp)
+
+upgrade_provision(samba3, setup_dir, message, credentials=creds,
+ session_info=system_session(), smbconf=smbconf,
+ targetdir=opts.targetdir)
diff --git a/source4/smb_server/blob.c b/source4/smb_server/blob.c
new file mode 100644
index 0000000000..f72074898a
--- /dev/null
+++ b/source4/smb_server/blob.c
@@ -0,0 +1,793 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Andrew Tridgell 2003
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "../lib/util/dlinklist.h"
+#include "smb_server/smb_server.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "ntvfs/ntvfs.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+
+#define BLOB_CHECK(cmd) do { \
+ NTSTATUS _status; \
+ _status = cmd; \
+ NT_STATUS_NOT_OK_RETURN(_status); \
+} while (0)
+
+#define BLOB_CHECK_MIN_SIZE(blob, size) do { \
+ if ((blob)->length < (size)) { \
+ return NT_STATUS_INVALID_PARAMETER; \
+ } \
+} while (0)
+
+
+/* align the end of the blob on an 8 byte boundary */
+#define BLOB_ALIGN(blob, alignment) do { \
+ if ((blob)->length & ((alignment)-1)) { \
+ uint8_t _pad = (alignment) - ((blob)->length & ((alignment)-1)); \
+ BLOB_CHECK(smbsrv_blob_fill_data(blob, blob, (blob)->length+_pad)); \
+ } \
+} while (0)
+
+/* grow the data size of a trans2 reply */
+NTSTATUS smbsrv_blob_grow_data(TALLOC_CTX *mem_ctx,
+ DATA_BLOB *blob,
+ uint32_t new_size)
+{
+ if (new_size > blob->length) {
+ uint8_t *p;
+ p = talloc_realloc(mem_ctx, blob->data, uint8_t, new_size);
+ NT_STATUS_HAVE_NO_MEMORY(p);
+ blob->data = p;
+ }
+ blob->length = new_size;
+ return NT_STATUS_OK;
+}
+
+/* grow the data, zero filling any new bytes */
+NTSTATUS smbsrv_blob_fill_data(TALLOC_CTX *mem_ctx,
+ DATA_BLOB *blob,
+ uint32_t new_size)
+{
+ uint32_t old_size = blob->length;
+ BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, new_size));
+ if (new_size > old_size) {
+ memset(blob->data + old_size, 0, new_size - old_size);
+ }
+ return NT_STATUS_OK;
+}
+
+/*
+ pull a string from a blob in a trans2 request
+*/
+size_t smbsrv_blob_pull_string(struct request_bufinfo *bufinfo,
+ const DATA_BLOB *blob,
+ uint16_t offset,
+ const char **str,
+ int flags)
+{
+ *str = NULL;
+ /* we use STR_NO_RANGE_CHECK because the params are allocated
+ separately in a DATA_BLOB, so we need to do our own range
+ checking */
+ if (offset >= blob->length) {
+ return 0;
+ }
+
+ return req_pull_string(bufinfo, str,
+ blob->data + offset,
+ blob->length - offset,
+ STR_NO_RANGE_CHECK | flags);
+}
+
+/*
+ push a string into the data section of a trans2 request
+ return the number of bytes consumed in the output
+*/
+size_t smbsrv_blob_push_string(TALLOC_CTX *mem_ctx,
+ DATA_BLOB *blob,
+ uint32_t len_offset,
+ uint32_t offset,
+ const char *str,
+ int dest_len,
+ int default_flags,
+ int flags)
+{
+ int alignment = 0, ret = 0, pkt_len;
+
+ /* we use STR_NO_RANGE_CHECK because the params are allocated
+ separately in a DATA_BLOB, so we need to do our own range
+ checking */
+ if (!str || offset >= blob->length) {
+ if (flags & STR_LEN8BIT) {
+ SCVAL(blob->data, len_offset, 0);
+ } else {
+ SIVAL(blob->data, len_offset, 0);
+ }
+ return 0;
+ }
+
+ flags |= STR_NO_RANGE_CHECK;
+
+ if (dest_len == -1 || (dest_len > blob->length - offset)) {
+ dest_len = blob->length - offset;
+ }
+
+ if (!(flags & (STR_ASCII|STR_UNICODE))) {
+ flags |= default_flags;
+ }
+
+ if ((offset&1) && (flags & STR_UNICODE) && !(flags & STR_NOALIGN)) {
+ alignment = 1;
+ if (dest_len > 0) {
+ SCVAL(blob->data + offset, 0, 0);
+ ret = push_string(blob->data + offset + 1, str, dest_len-1, flags);
+ }
+ } else {
+ ret = push_string(blob->data + offset, str, dest_len, flags);
+ }
+
+ /* sometimes the string needs to be terminated, but the length
+ on the wire must not include the termination! */
+ pkt_len = ret;
+
+ if ((flags & STR_LEN_NOTERM) && (flags & STR_TERMINATE)) {
+ if ((flags & STR_UNICODE) && ret >= 2) {
+ pkt_len = ret-2;
+ }
+ if ((flags & STR_ASCII) && ret >= 1) {
+ pkt_len = ret-1;
+ }
+ }
+
+ if (flags & STR_LEN8BIT) {
+ SCVAL(blob->data, len_offset, pkt_len);
+ } else {
+ SIVAL(blob->data, len_offset, pkt_len);
+ }
+
+ return ret + alignment;
+}
+
+/*
+ append a string to the data section of a trans2 reply
+ len_offset points to the place in the packet where the length field
+ should go
+*/
+NTSTATUS smbsrv_blob_append_string(TALLOC_CTX *mem_ctx,
+ DATA_BLOB *blob,
+ const char *str,
+ uint_t len_offset,
+ int default_flags,
+ int flags)
+{
+ size_t ret;
+ uint32_t offset;
+ const int max_bytes_per_char = 3;
+
+ offset = blob->length;
+ BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, offset + (2+strlen_m(str))*max_bytes_per_char));
+ ret = smbsrv_blob_push_string(mem_ctx, blob, len_offset, offset, str, -1, default_flags, flags);
+ if (ret < 0) {
+ return NT_STATUS_FOOBAR;
+ }
+ BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, offset + ret));
+ return NT_STATUS_OK;
+}
+
+NTSTATUS smbsrv_push_passthru_fsinfo(TALLOC_CTX *mem_ctx,
+ DATA_BLOB *blob,
+ enum smb_fsinfo_level level,
+ union smb_fsinfo *fsinfo,
+ int default_str_flags)
+{
+ uint_t i;
+ DATA_BLOB guid_blob;
+
+ switch (level) {
+ case RAW_QFS_VOLUME_INFORMATION:
+ BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, 18));
+
+ push_nttime(blob->data, 0, fsinfo->volume_info.out.create_time);
+ SIVAL(blob->data, 8, fsinfo->volume_info.out.serial_number);
+ SSVAL(blob->data, 16, 0); /* padding */
+ BLOB_CHECK(smbsrv_blob_append_string(mem_ctx, blob,
+ fsinfo->volume_info.out.volume_name.s,
+ 12, default_str_flags,
+ STR_UNICODE));
+
+ return NT_STATUS_OK;
+
+ case RAW_QFS_SIZE_INFORMATION:
+ BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, 24));
+
+ SBVAL(blob->data, 0, fsinfo->size_info.out.total_alloc_units);
+ SBVAL(blob->data, 8, fsinfo->size_info.out.avail_alloc_units);
+ SIVAL(blob->data, 16, fsinfo->size_info.out.sectors_per_unit);
+ SIVAL(blob->data, 20, fsinfo->size_info.out.bytes_per_sector);
+
+ return NT_STATUS_OK;
+
+ case RAW_QFS_DEVICE_INFORMATION:
+ BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, 8));
+
+ SIVAL(blob->data, 0, fsinfo->device_info.out.device_type);
+ SIVAL(blob->data, 4, fsinfo->device_info.out.characteristics);
+
+ return NT_STATUS_OK;
+
+ case RAW_QFS_ATTRIBUTE_INFORMATION:
+ BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, 12));
+
+ SIVAL(blob->data, 0, fsinfo->attribute_info.out.fs_attr);
+ SIVAL(blob->data, 4, fsinfo->attribute_info.out.max_file_component_length);
+ /* this must not be null terminated or win98 gets
+ confused! also note that w2k3 returns this as
+ unicode even when ascii is negotiated */
+ BLOB_CHECK(smbsrv_blob_append_string(mem_ctx, blob,
+ fsinfo->attribute_info.out.fs_type.s,
+ 8, default_str_flags,
+ STR_UNICODE));
+ return NT_STATUS_OK;
+
+
+ case RAW_QFS_QUOTA_INFORMATION:
+ BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, 48));
+
+ SBVAL(blob->data, 0, fsinfo->quota_information.out.unknown[0]);
+ SBVAL(blob->data, 8, fsinfo->quota_information.out.unknown[1]);
+ SBVAL(blob->data, 16, fsinfo->quota_information.out.unknown[2]);
+ SBVAL(blob->data, 24, fsinfo->quota_information.out.quota_soft);
+ SBVAL(blob->data, 32, fsinfo->quota_information.out.quota_hard);
+ SBVAL(blob->data, 40, fsinfo->quota_information.out.quota_flags);
+
+ return NT_STATUS_OK;
+
+
+ case RAW_QFS_FULL_SIZE_INFORMATION:
+ BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, 32));
+
+ SBVAL(blob->data, 0, fsinfo->full_size_information.out.total_alloc_units);
+ SBVAL(blob->data, 8, fsinfo->full_size_information.out.call_avail_alloc_units);
+ SBVAL(blob->data, 16, fsinfo->full_size_information.out.actual_avail_alloc_units);
+ SIVAL(blob->data, 24, fsinfo->full_size_information.out.sectors_per_unit);
+ SIVAL(blob->data, 28, fsinfo->full_size_information.out.bytes_per_sector);
+
+ return NT_STATUS_OK;
+
+ case RAW_QFS_OBJECTID_INFORMATION: {
+ enum ndr_err_code ndr_err;
+
+ BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, 64));
+
+ ndr_err = ndr_push_struct_blob(&guid_blob, mem_ctx, NULL,
+ &fsinfo->objectid_information.out.guid,
+ (ndr_push_flags_fn_t)ndr_push_GUID);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ BLOB_CHECK(ndr_map_error2ntstatus(ndr_err));
+ }
+
+ memcpy(blob->data, guid_blob.data, guid_blob.length);
+
+ for (i=0;i<6;i++) {
+ SBVAL(blob->data, 16 + 8*i, fsinfo->objectid_information.out.unknown[i]);
+ }
+
+ return NT_STATUS_OK;
+ }
+ default:
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ return NT_STATUS_INVALID_LEVEL;
+}
+
+NTSTATUS smbsrv_push_passthru_fileinfo(TALLOC_CTX *mem_ctx,
+ DATA_BLOB *blob,
+ enum smb_fileinfo_level level,
+ union smb_fileinfo *st,
+ int default_str_flags)
+{
+ uint_t i;
+ size_t list_size;
+
+ switch (level) {
+ case RAW_FILEINFO_BASIC_INFORMATION:
+ BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, 40));
+
+ push_nttime(blob->data, 0, st->basic_info.out.create_time);
+ push_nttime(blob->data, 8, st->basic_info.out.access_time);
+ push_nttime(blob->data, 16, st->basic_info.out.write_time);
+ push_nttime(blob->data, 24, st->basic_info.out.change_time);
+ SIVAL(blob->data, 32, st->basic_info.out.attrib);
+ SIVAL(blob->data, 36, 0); /* padding */
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_NETWORK_OPEN_INFORMATION:
+ BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, 56));
+
+ push_nttime(blob->data, 0, st->network_open_information.out.create_time);
+ push_nttime(blob->data, 8, st->network_open_information.out.access_time);
+ push_nttime(blob->data, 16, st->network_open_information.out.write_time);
+ push_nttime(blob->data, 24, st->network_open_information.out.change_time);
+ SBVAL(blob->data, 32, st->network_open_information.out.alloc_size);
+ SBVAL(blob->data, 40, st->network_open_information.out.size);
+ SIVAL(blob->data, 48, st->network_open_information.out.attrib);
+ SIVAL(blob->data, 52, 0); /* padding */
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_STANDARD_INFORMATION:
+ BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, 24));
+
+ SBVAL(blob->data, 0, st->standard_info.out.alloc_size);
+ SBVAL(blob->data, 8, st->standard_info.out.size);
+ SIVAL(blob->data, 16, st->standard_info.out.nlink);
+ SCVAL(blob->data, 20, st->standard_info.out.delete_pending);
+ SCVAL(blob->data, 21, st->standard_info.out.directory);
+ SSVAL(blob->data, 22, 0); /* padding */
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION:
+ BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, 8));
+
+ SIVAL(blob->data, 0, st->attribute_tag_information.out.attrib);
+ SIVAL(blob->data, 4, st->attribute_tag_information.out.reparse_tag);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_EA_INFORMATION:
+ BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, 4));
+
+ SIVAL(blob->data, 0, st->ea_info.out.ea_size);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_MODE_INFORMATION:
+ BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, 4));
+
+ SIVAL(blob->data, 0, st->mode_information.out.mode);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_ALIGNMENT_INFORMATION:
+ BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, 4));
+
+ SIVAL(blob->data, 0,
+ st->alignment_information.out.alignment_requirement);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_ACCESS_INFORMATION:
+ BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, 4));
+
+ SIVAL(blob->data, 0, st->access_information.out.access_flags);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_POSITION_INFORMATION:
+ BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, 8));
+
+ SBVAL(blob->data, 0, st->position_information.out.position);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_COMPRESSION_INFORMATION:
+ BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, 16));
+
+ SBVAL(blob->data, 0, st->compression_info.out.compressed_size);
+ SSVAL(blob->data, 8, st->compression_info.out.format);
+ SCVAL(blob->data, 10, st->compression_info.out.unit_shift);
+ SCVAL(blob->data, 11, st->compression_info.out.chunk_shift);
+ SCVAL(blob->data, 12, st->compression_info.out.cluster_shift);
+ SSVAL(blob->data, 13, 0); /* 3 bytes padding */
+ SCVAL(blob->data, 15, 0);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_INTERNAL_INFORMATION:
+ BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, 8));
+
+ SBVAL(blob->data, 0, st->internal_information.out.file_id);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_ALL_INFORMATION:
+ BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, 72));
+
+ push_nttime(blob->data, 0, st->all_info.out.create_time);
+ push_nttime(blob->data, 8, st->all_info.out.access_time);
+ push_nttime(blob->data, 16, st->all_info.out.write_time);
+ push_nttime(blob->data, 24, st->all_info.out.change_time);
+ SIVAL(blob->data, 32, st->all_info.out.attrib);
+ SIVAL(blob->data, 36, 0); /* padding */
+ SBVAL(blob->data, 40, st->all_info.out.alloc_size);
+ SBVAL(blob->data, 48, st->all_info.out.size);
+ SIVAL(blob->data, 56, st->all_info.out.nlink);
+ SCVAL(blob->data, 60, st->all_info.out.delete_pending);
+ SCVAL(blob->data, 61, st->all_info.out.directory);
+ SSVAL(blob->data, 62, 0); /* padding */
+ SIVAL(blob->data, 64, st->all_info.out.ea_size);
+ BLOB_CHECK(smbsrv_blob_append_string(mem_ctx, blob,
+ st->all_info.out.fname.s,
+ 68, default_str_flags,
+ STR_UNICODE));
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_NAME_INFORMATION:
+ BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, 4));
+
+ BLOB_CHECK(smbsrv_blob_append_string(mem_ctx, blob,
+ st->name_info.out.fname.s,
+ 0, default_str_flags,
+ STR_UNICODE));
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_ALT_NAME_INFORMATION:
+ BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, 4));
+
+ BLOB_CHECK(smbsrv_blob_append_string(mem_ctx, blob,
+ st->alt_name_info.out.fname.s,
+ 0, default_str_flags,
+ STR_UNICODE));
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_STREAM_INFORMATION:
+ for (i=0;i<st->stream_info.out.num_streams;i++) {
+ uint32_t data_size = blob->length;
+ uint8_t *data;
+
+ BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, data_size + 24));
+ data = blob->data + data_size;
+ SBVAL(data, 8, st->stream_info.out.streams[i].size);
+ SBVAL(data, 16, st->stream_info.out.streams[i].alloc_size);
+ BLOB_CHECK(smbsrv_blob_append_string(mem_ctx, blob,
+ st->stream_info.out.streams[i].stream_name.s,
+ data_size + 4, default_str_flags,
+ STR_UNICODE));
+ if (i == st->stream_info.out.num_streams - 1) {
+ SIVAL(blob->data, data_size, 0);
+ } else {
+ BLOB_CHECK(smbsrv_blob_fill_data(mem_ctx, blob, (blob->length+7)&~7));
+ SIVAL(blob->data, data_size,
+ blob->length - data_size);
+ }
+ }
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_SMB2_ALL_EAS:
+ /* if no eas are returned the backend should
+ * have returned NO_EAS_ON_FILE or NO_MORE_EAS
+ *
+ * so it's a programmer error if num_eas == 0
+ */
+ if (st->all_eas.out.num_eas == 0) {
+ smb_panic("0 eas for SMB2_ALL_EAS - programmer error in ntvfs backend");
+ }
+
+ list_size = ea_list_size_chained(st->all_eas.out.num_eas,
+ st->all_eas.out.eas, 4);
+ BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, list_size));
+
+ ea_put_list_chained(blob->data,
+ st->all_eas.out.num_eas,
+ st->all_eas.out.eas, 4);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_SMB2_ALL_INFORMATION:
+ BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, 0x64));
+
+ push_nttime(blob->data, 0x00, st->all_info2.out.create_time);
+ push_nttime(blob->data, 0x08, st->all_info2.out.access_time);
+ push_nttime(blob->data, 0x10, st->all_info2.out.write_time);
+ push_nttime(blob->data, 0x18, st->all_info2.out.change_time);
+ SIVAL(blob->data, 0x20, st->all_info2.out.attrib);
+ SIVAL(blob->data, 0x24, st->all_info2.out.unknown1);
+ SBVAL(blob->data, 0x28, st->all_info2.out.alloc_size);
+ SBVAL(blob->data, 0x30, st->all_info2.out.size);
+ SIVAL(blob->data, 0x38, st->all_info2.out.nlink);
+ SCVAL(blob->data, 0x3C, st->all_info2.out.delete_pending);
+ SCVAL(blob->data, 0x3D, st->all_info2.out.directory);
+ SSVAL(blob->data, 0x3E, 0); /* padding */
+ SBVAL(blob->data, 0x40, st->all_info2.out.file_id);
+ SIVAL(blob->data, 0x48, st->all_info2.out.ea_size);
+ SIVAL(blob->data, 0x4C, st->all_info2.out.access_mask);
+ SBVAL(blob->data, 0x50, st->all_info2.out.position);
+ SIVAL(blob->data, 0x58, st->all_info2.out.mode);
+ SIVAL(blob->data, 0x5C, st->all_info2.out.alignment_requirement);
+ BLOB_CHECK(smbsrv_blob_append_string(mem_ctx, blob,
+ st->all_info2.out.fname.s,
+ 0x60, default_str_flags,
+ STR_UNICODE));
+ return NT_STATUS_OK;
+
+ default:
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ return NT_STATUS_INVALID_LEVEL;
+}
+
+NTSTATUS smbsrv_pull_passthru_sfileinfo(TALLOC_CTX *mem_ctx,
+ enum smb_setfileinfo_level level,
+ union smb_setfileinfo *st,
+ const DATA_BLOB *blob,
+ int default_str_flags,
+ struct request_bufinfo *bufinfo)
+{
+ uint32_t len, ofs;
+ DATA_BLOB str_blob;
+
+ switch (level) {
+ case SMB_SFILEINFO_BASIC_INFORMATION:
+ BLOB_CHECK_MIN_SIZE(blob, 40);
+
+ st->basic_info.in.create_time = pull_nttime(blob->data, 0);
+ st->basic_info.in.access_time = pull_nttime(blob->data, 8);
+ st->basic_info.in.write_time = pull_nttime(blob->data, 16);
+ st->basic_info.in.change_time = pull_nttime(blob->data, 24);
+ st->basic_info.in.attrib = IVAL(blob->data, 32);
+ st->basic_info.in.reserved = IVAL(blob->data, 36);
+
+ return NT_STATUS_OK;
+
+ case SMB_SFILEINFO_DISPOSITION_INFORMATION:
+ BLOB_CHECK_MIN_SIZE(blob, 1);
+
+ st->disposition_info.in.delete_on_close = CVAL(blob->data, 0);
+
+ return NT_STATUS_OK;
+
+ case SMB_SFILEINFO_ALLOCATION_INFORMATION:
+ BLOB_CHECK_MIN_SIZE(blob, 8);
+
+ st->allocation_info.in.alloc_size = BVAL(blob->data, 0);
+
+ return NT_STATUS_OK;
+
+ case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
+ BLOB_CHECK_MIN_SIZE(blob, 8);
+
+ st->end_of_file_info.in.size = BVAL(blob->data, 0);
+
+ return NT_STATUS_OK;
+
+ case RAW_SFILEINFO_RENAME_INFORMATION:
+ if (!bufinfo) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ BLOB_CHECK_MIN_SIZE(blob, 12);
+ st->rename_information.in.overwrite = CVAL(blob->data, 0);
+ st->rename_information.in.root_fid = IVAL(blob->data, 4);
+ len = IVAL(blob->data, 8);
+ ofs = 12;
+ str_blob = *blob;
+ str_blob.length = MIN(str_blob.length, ofs+len);
+ smbsrv_blob_pull_string(bufinfo, &str_blob, ofs,
+ &st->rename_information.in.new_name,
+ STR_UNICODE);
+ if (st->rename_information.in.new_name == NULL) {
+ return NT_STATUS_FOOBAR;
+ }
+
+ return NT_STATUS_OK;
+
+
+ case RAW_SFILEINFO_LINK_INFORMATION:
+ if (!bufinfo) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ BLOB_CHECK_MIN_SIZE(blob, 20);
+ st->link_information.in.overwrite = CVAL(blob->data, 0);
+ st->link_information.in.root_fid = IVAL(blob->data, 8);
+ len = IVAL(blob->data, 16);
+ ofs = 20;
+ str_blob = *blob;
+ str_blob.length = MIN(str_blob.length, ofs+len);
+ smbsrv_blob_pull_string(bufinfo, &str_blob, ofs,
+ &st->link_information.in.new_name,
+ STR_UNICODE);
+ if (st->link_information.in.new_name == NULL) {
+ return NT_STATUS_FOOBAR;
+ }
+
+ return NT_STATUS_OK;
+
+ case RAW_SFILEINFO_RENAME_INFORMATION_SMB2:
+ /* SMB2 uses a different format for rename information */
+ if (!bufinfo) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ BLOB_CHECK_MIN_SIZE(blob, 20);
+ st->rename_information.in.overwrite = CVAL(blob->data, 0);
+ st->rename_information.in.root_fid = BVAL(blob->data, 8);
+ len = IVAL(blob->data,16);
+ ofs = 20;
+ str_blob = *blob;
+ str_blob.length = MIN(str_blob.length, ofs+len);
+ smbsrv_blob_pull_string(bufinfo, &str_blob, ofs,
+ &st->rename_information.in.new_name,
+ STR_UNICODE);
+ if (st->rename_information.in.new_name == NULL) {
+ return NT_STATUS_FOOBAR;
+ }
+
+ return NT_STATUS_OK;
+
+ case RAW_SFILEINFO_POSITION_INFORMATION:
+ BLOB_CHECK_MIN_SIZE(blob, 8);
+
+ st->position_information.in.position = BVAL(blob->data, 0);
+
+ return NT_STATUS_OK;
+
+ case RAW_SFILEINFO_MODE_INFORMATION:
+ BLOB_CHECK_MIN_SIZE(blob, 4);
+
+ st->mode_information.in.mode = IVAL(blob->data, 0);
+
+ return NT_STATUS_OK;
+
+ default:
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ return NT_STATUS_INVALID_LEVEL;
+}
+
+/*
+ fill a single entry in a trans2 find reply
+*/
+NTSTATUS smbsrv_push_passthru_search(TALLOC_CTX *mem_ctx,
+ DATA_BLOB *blob,
+ enum smb_search_data_level level,
+ const union smb_search_data *file,
+ int default_str_flags)
+{
+ uint8_t *data;
+ uint_t ofs = blob->length;
+
+ switch (level) {
+ case RAW_SEARCH_DATA_DIRECTORY_INFO:
+ BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, ofs + 64));
+ data = blob->data + ofs;
+ SIVAL(data, 4, file->directory_info.file_index);
+ push_nttime(data, 8, file->directory_info.create_time);
+ push_nttime(data, 16, file->directory_info.access_time);
+ push_nttime(data, 24, file->directory_info.write_time);
+ push_nttime(data, 32, file->directory_info.change_time);
+ SBVAL(data, 40, file->directory_info.size);
+ SBVAL(data, 48, file->directory_info.alloc_size);
+ SIVAL(data, 56, file->directory_info.attrib);
+ BLOB_CHECK(smbsrv_blob_append_string(mem_ctx, blob, file->directory_info.name.s,
+ ofs + 60, default_str_flags,
+ STR_TERMINATE_ASCII));
+ BLOB_ALIGN(blob, 8);
+ data = blob->data + ofs;
+ SIVAL(data, 0, blob->length - ofs);
+ return NT_STATUS_OK;
+
+ case RAW_SEARCH_DATA_FULL_DIRECTORY_INFO:
+ BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, ofs + 68));
+ data = blob->data + ofs;
+ SIVAL(data, 4, file->full_directory_info.file_index);
+ push_nttime(data, 8, file->full_directory_info.create_time);
+ push_nttime(data, 16, file->full_directory_info.access_time);
+ push_nttime(data, 24, file->full_directory_info.write_time);
+ push_nttime(data, 32, file->full_directory_info.change_time);
+ SBVAL(data, 40, file->full_directory_info.size);
+ SBVAL(data, 48, file->full_directory_info.alloc_size);
+ SIVAL(data, 56, file->full_directory_info.attrib);
+ SIVAL(data, 64, file->full_directory_info.ea_size);
+ BLOB_CHECK(smbsrv_blob_append_string(mem_ctx, blob, file->full_directory_info.name.s,
+ ofs + 60, default_str_flags,
+ STR_TERMINATE_ASCII));
+ BLOB_ALIGN(blob, 8);
+ data = blob->data + ofs;
+ SIVAL(data, 0, blob->length - ofs);
+ return NT_STATUS_OK;
+
+ case RAW_SEARCH_DATA_NAME_INFO:
+ BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, ofs + 12));
+ data = blob->data + ofs;
+ SIVAL(data, 4, file->name_info.file_index);
+ BLOB_CHECK(smbsrv_blob_append_string(mem_ctx, blob, file->name_info.name.s,
+ ofs + 8, default_str_flags,
+ STR_TERMINATE_ASCII));
+ BLOB_ALIGN(blob, 8);
+ data = blob->data + ofs;
+ SIVAL(data, 0, blob->length - ofs);
+ return NT_STATUS_OK;
+
+ case RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO:
+ BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, ofs + 94));
+ data = blob->data + ofs;
+ SIVAL(data, 4, file->both_directory_info.file_index);
+ push_nttime(data, 8, file->both_directory_info.create_time);
+ push_nttime(data, 16, file->both_directory_info.access_time);
+ push_nttime(data, 24, file->both_directory_info.write_time);
+ push_nttime(data, 32, file->both_directory_info.change_time);
+ SBVAL(data, 40, file->both_directory_info.size);
+ SBVAL(data, 48, file->both_directory_info.alloc_size);
+ SIVAL(data, 56, file->both_directory_info.attrib);
+ SIVAL(data, 64, file->both_directory_info.ea_size);
+ SCVAL(data, 69, 0); /* reserved */
+ memset(data+70,0,24);
+ smbsrv_blob_push_string(mem_ctx, blob,
+ 68 + ofs, 70 + ofs,
+ file->both_directory_info.short_name.s,
+ 24, default_str_flags,
+ STR_UNICODE | STR_LEN8BIT);
+ BLOB_CHECK(smbsrv_blob_append_string(mem_ctx, blob, file->both_directory_info.name.s,
+ ofs + 60, default_str_flags,
+ STR_TERMINATE_ASCII));
+ BLOB_ALIGN(blob, 8);
+ data = blob->data + ofs;
+ SIVAL(data, 0, blob->length - ofs);
+ return NT_STATUS_OK;
+
+ case RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO:
+ BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, ofs + 80));
+ data = blob->data + ofs;
+ SIVAL(data, 4, file->id_full_directory_info.file_index);
+ push_nttime(data, 8, file->id_full_directory_info.create_time);
+ push_nttime(data, 16, file->id_full_directory_info.access_time);
+ push_nttime(data, 24, file->id_full_directory_info.write_time);
+ push_nttime(data, 32, file->id_full_directory_info.change_time);
+ SBVAL(data, 40, file->id_full_directory_info.size);
+ SBVAL(data, 48, file->id_full_directory_info.alloc_size);
+ SIVAL(data, 56, file->id_full_directory_info.attrib);
+ SIVAL(data, 64, file->id_full_directory_info.ea_size);
+ SIVAL(data, 68, 0); /* padding */
+ SBVAL(data, 72, file->id_full_directory_info.file_id);
+ BLOB_CHECK(smbsrv_blob_append_string(mem_ctx, blob, file->id_full_directory_info.name.s,
+ ofs + 60, default_str_flags,
+ STR_TERMINATE_ASCII));
+ BLOB_ALIGN(blob, 8);
+ data = blob->data + ofs;
+ SIVAL(data, 0, blob->length - ofs);
+ return NT_STATUS_OK;
+
+ case RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO:
+ BLOB_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, ofs + 104));
+ data = blob->data + ofs;
+ SIVAL(data, 4, file->id_both_directory_info.file_index);
+ push_nttime(data, 8, file->id_both_directory_info.create_time);
+ push_nttime(data, 16, file->id_both_directory_info.access_time);
+ push_nttime(data, 24, file->id_both_directory_info.write_time);
+ push_nttime(data, 32, file->id_both_directory_info.change_time);
+ SBVAL(data, 40, file->id_both_directory_info.size);
+ SBVAL(data, 48, file->id_both_directory_info.alloc_size);
+ SIVAL(data, 56, file->id_both_directory_info.attrib);
+ SIVAL(data, 64, file->id_both_directory_info.ea_size);
+ SCVAL(data, 69, 0); /* reserved */
+ memset(data+70,0,26);
+ smbsrv_blob_push_string(mem_ctx, blob,
+ 68 + ofs, 70 + ofs,
+ file->id_both_directory_info.short_name.s,
+ 24, default_str_flags,
+ STR_UNICODE | STR_LEN8BIT);
+ SBVAL(data, 96, file->id_both_directory_info.file_id);
+ BLOB_CHECK(smbsrv_blob_append_string(mem_ctx, blob, file->id_both_directory_info.name.s,
+ ofs + 60, default_str_flags,
+ STR_TERMINATE_ASCII));
+ BLOB_ALIGN(blob, 8);
+ data = blob->data + ofs;
+ SIVAL(data, 0, blob->length - ofs);
+ return NT_STATUS_OK;
+
+ default:
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ return NT_STATUS_INVALID_LEVEL;
+}
diff --git a/source4/smb_server/config.mk b/source4/smb_server/config.mk
new file mode 100644
index 0000000000..a9d3decf49
--- /dev/null
+++ b/source4/smb_server/config.mk
@@ -0,0 +1,42 @@
+# SMB server subsystem
+#
+[MODULE::SERVICE_SMB]
+INIT_FUNCTION = server_service_smb_init
+SUBSYSTEM = service
+OUTPUT_TYPE = MERGED_OBJ
+PRIVATE_DEPENDENCIES = SMB_SERVER
+
+SERVICE_SMB_OBJ_FILES = $(smb_serversrcdir)/smb_server.o
+
+$(eval $(call proto_header_template,$(smb_serversrcdir)/service_smb_proto.h,$(SERVICE_SMB_OBJ_FILES:.o=.c)))
+
+# samba3 SMB server subsystem
+#
+[MODULE::SERVICE_SAMBA3_SMB]
+INIT_FUNCTION = server_service_samba3_smb_init
+SUBSYSTEM = service
+
+SERVICE_SAMBA3_SMB_OBJ_FILES = $(smb_serversrcdir)/smb_samba3.o
+
+#######################
+# Start SUBSYSTEM SMB
+[SUBSYSTEM::SMB_SERVER]
+PUBLIC_DEPENDENCIES = \
+ share \
+ LIBPACKET \
+ SMB_PROTOCOL \
+ SMB2_PROTOCOL
+# End SUBSYSTEM SMB
+#######################
+
+SMB_SERVER_OBJ_FILES = $(addprefix $(smb_serversrcdir)/, \
+ handle.o \
+ tcon.o \
+ session.o \
+ blob.o \
+ management.o)
+
+$(eval $(call proto_header_template,$(smb_serversrcdir)/smb_server_proto.h,$(SMB_SERVER_OBJ_FILES:.o=.c)))
+
+mkinclude smb/config.mk
+mkinclude smb2/config.mk
diff --git a/source4/smb_server/handle.c b/source4/smb_server/handle.c
new file mode 100644
index 0000000000..6ee4e163ad
--- /dev/null
+++ b/source4/smb_server/handle.c
@@ -0,0 +1,144 @@
+/*
+ Unix SMB/CIFS implementation.
+ Manage smbsrv_handle structures
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "../lib/util/dlinklist.h"
+#include "smb_server/smb_server.h"
+#include "ntvfs/ntvfs.h"
+
+
+/****************************************************************************
+init the handle structures
+****************************************************************************/
+NTSTATUS smbsrv_init_handles(struct smbsrv_tcon *tcon, uint32_t limit)
+{
+ /*
+ * the idr_* functions take 'int' as limit,
+ * and only work with a max limit 0x00FFFFFF
+ */
+ limit &= 0x00FFFFFF;
+
+ tcon->handles.idtree_hid = idr_init(tcon);
+ NT_STATUS_HAVE_NO_MEMORY(tcon->handles.idtree_hid);
+ tcon->handles.idtree_limit = limit;
+ tcon->handles.list = NULL;
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+find a handle given a handle id
+****************************************************************************/
+static struct smbsrv_handle *smbsrv_handle_find(struct smbsrv_handles_context *handles_ctx,
+ uint32_t hid, struct timeval request_time)
+{
+ void *p;
+ struct smbsrv_handle *handle;
+
+ if (hid == 0) return NULL;
+
+ if (hid > handles_ctx->idtree_limit) return NULL;
+
+ p = idr_find(handles_ctx->idtree_hid, hid);
+ if (!p) return NULL;
+
+ handle = talloc_get_type(p, struct smbsrv_handle);
+ if (!handle) return NULL;
+
+ /* only give it away when the ntvfs subsystem has made the handle valid */
+ if (!handle->ntvfs) return NULL;
+
+ handle->statistics.last_use_time = request_time;
+
+ return handle;
+}
+
+struct smbsrv_handle *smbsrv_smb_handle_find(struct smbsrv_tcon *smb_tcon,
+ uint16_t fnum, struct timeval request_time)
+{
+ return smbsrv_handle_find(&smb_tcon->handles, fnum, request_time);
+}
+
+struct smbsrv_handle *smbsrv_smb2_handle_find(struct smbsrv_tcon *smb_tcon,
+ uint32_t hid, struct timeval request_time)
+{
+ return smbsrv_handle_find(&smb_tcon->handles, hid, request_time);
+}
+
+/*
+ destroy a connection structure
+*/
+static int smbsrv_handle_destructor(struct smbsrv_handle *handle)
+{
+ struct smbsrv_handles_context *handles_ctx;
+
+ handles_ctx = &handle->tcon->handles;
+
+ idr_remove(handles_ctx->idtree_hid, handle->hid);
+ DLIST_REMOVE(handles_ctx->list, handle);
+ DLIST_REMOVE(handle->session->handles, &handle->session_item);
+
+ /* tell the ntvfs backend that we are disconnecting */
+ if (handle->ntvfs) {
+ talloc_free(handle->ntvfs);
+ handle->ntvfs = NULL;
+ }
+
+ return 0;
+}
+
+/*
+ find first available handle slot
+*/
+struct smbsrv_handle *smbsrv_handle_new(struct smbsrv_session *session,
+ struct smbsrv_tcon *tcon,
+ TALLOC_CTX *mem_ctx,
+ struct timeval request_time)
+{
+ struct smbsrv_handles_context *handles_ctx = &tcon->handles;
+ struct smbsrv_handle *handle;
+ int i;
+
+ handle = talloc_zero(mem_ctx, struct smbsrv_handle);
+ if (!handle) return NULL;
+ handle->tcon = tcon;
+ handle->session = session;
+
+ i = idr_get_new_above(handles_ctx->idtree_hid, handle, 1, handles_ctx->idtree_limit);
+ if (i == -1) {
+ DEBUG(1,("ERROR! Out of handle structures\n"));
+ goto failed;
+ }
+ handle->hid = i;
+ handle->session_item.handle = handle;
+
+ DLIST_ADD(handles_ctx->list, handle);
+ DLIST_ADD(session->handles, &handle->session_item);
+ talloc_set_destructor(handle, smbsrv_handle_destructor);
+
+ /* now fill in some statistics */
+ handle->statistics.open_time = request_time;
+ handle->statistics.last_use_time = request_time;
+
+ return handle;
+
+failed:
+ talloc_free(handle);
+ return NULL;
+}
diff --git a/source4/smb_server/management.c b/source4/smb_server/management.c
new file mode 100644
index 0000000000..e58c278613
--- /dev/null
+++ b/source4/smb_server/management.c
@@ -0,0 +1,137 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ management calls for smb server
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "smb_server/smb_server.h"
+#include "smbd/service_stream.h"
+#include "lib/messaging/irpc.h"
+#include "librpc/gen_ndr/ndr_irpc.h"
+#include "auth/auth.h"
+
+/*
+ return a list of open sessions
+*/
+static NTSTATUS smbsrv_session_information(struct irpc_message *msg,
+ struct smbsrv_information *r)
+{
+ struct smbsrv_connection *smb_conn = talloc_get_type(msg->private_data,
+ struct smbsrv_connection);
+ int i=0, count=0;
+ struct smbsrv_session *sess;
+
+ /* count the number of sessions */
+ for (sess=smb_conn->sessions.list; sess; sess=sess->next) {
+ count++;
+ }
+
+ r->out.info.sessions.num_sessions = count;
+ r->out.info.sessions.sessions = talloc_array(r, struct smbsrv_session_info, count);
+ NT_STATUS_HAVE_NO_MEMORY(r->out.info.sessions.sessions);
+
+ for (sess=smb_conn->sessions.list; sess; sess=sess->next) {
+ struct smbsrv_session_info *info = &r->out.info.sessions.sessions[i];
+ struct socket_address *client_addr;
+ client_addr = socket_get_peer_addr(smb_conn->connection->socket, r);
+
+ if (client_addr) {
+ info->client_ip = client_addr->addr;
+ } else {
+ info->client_ip = NULL;
+ }
+
+ info->vuid = sess->vuid;
+ info->account_name = sess->session_info->server_info->account_name;
+ info->domain_name = sess->session_info->server_info->domain_name;
+
+ info->connect_time = timeval_to_nttime(&sess->statistics.connect_time);
+ info->auth_time = timeval_to_nttime(&sess->statistics.auth_time);
+ info->last_use_time= timeval_to_nttime(&sess->statistics.last_request_time);
+ i++;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ return a list of tree connects
+*/
+static NTSTATUS smbsrv_tcon_information(struct irpc_message *msg,
+ struct smbsrv_information *r)
+{
+ struct smbsrv_connection *smb_conn = talloc_get_type(msg->private_data,
+ struct smbsrv_connection);
+ int i=0, count=0;
+ struct smbsrv_tcon *tcon;
+
+ /* count the number of tcons */
+ for (tcon=smb_conn->smb_tcons.list; tcon; tcon=tcon->next) {
+ count++;
+ }
+
+ r->out.info.tcons.num_tcons = count;
+ r->out.info.tcons.tcons = talloc_array(r, struct smbsrv_tcon_info, count);
+ NT_STATUS_HAVE_NO_MEMORY(r->out.info.tcons.tcons);
+
+ for (tcon=smb_conn->smb_tcons.list; tcon; tcon=tcon->next) {
+ struct smbsrv_tcon_info *info = &r->out.info.tcons.tcons[i];
+ struct socket_address *client_addr;
+ client_addr = socket_get_peer_addr(smb_conn->connection->socket, r);
+
+ if (client_addr) {
+ info->client_ip = client_addr->addr;
+ } else {
+ info->client_ip = NULL;
+ }
+
+ info->tid = tcon->tid;
+ info->share_name = tcon->share_name;
+ info->connect_time = timeval_to_nttime(&tcon->statistics.connect_time);
+ info->last_use_time= timeval_to_nttime(&tcon->statistics.last_request_time);
+ i++;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ serve smbserver information via irpc
+*/
+static NTSTATUS smbsrv_information(struct irpc_message *msg,
+ struct smbsrv_information *r)
+{
+ switch (r->in.level) {
+ case SMBSRV_INFO_SESSIONS:
+ return smbsrv_session_information(msg, r);
+ case SMBSRV_INFO_TCONS:
+ return smbsrv_tcon_information(msg, r);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ initialise irpc management calls on a connection
+*/
+void smbsrv_management_init(struct smbsrv_connection *smb_conn)
+{
+ IRPC_REGISTER(smb_conn->connection->msg_ctx, irpc, SMBSRV_INFORMATION,
+ smbsrv_information, smb_conn);
+}
diff --git a/source4/smb_server/session.c b/source4/smb_server/session.c
new file mode 100644
index 0000000000..0e626307d6
--- /dev/null
+++ b/source4/smb_server/session.c
@@ -0,0 +1,169 @@
+/*
+ Unix SMB/CIFS implementation.
+ Password and authentication handling
+ Copyright (C) Andrew Tridgell 1992-2005
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
+ Copyright (C) Stefan Metzmacher 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "smb_server/smb_server.h"
+#include "../lib/util/dlinklist.h"
+
+
+/*
+ * init the sessions structures
+ */
+NTSTATUS smbsrv_init_sessions(struct smbsrv_connection *smb_conn, uint64_t limit)
+{
+ /*
+ * the idr_* functions take 'int' as limit,
+ * and only work with a max limit 0x00FFFFFF
+ */
+ limit &= 0x00FFFFFF;
+
+ smb_conn->sessions.idtree_vuid = idr_init(smb_conn);
+ NT_STATUS_HAVE_NO_MEMORY(smb_conn->sessions.idtree_vuid);
+ smb_conn->sessions.idtree_limit = limit;
+ smb_conn->sessions.list = NULL;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ * Find the session structure assoicated with a VUID
+ * (not one from an in-progress session setup)
+ */
+struct smbsrv_session *smbsrv_session_find(struct smbsrv_connection *smb_conn,
+ uint64_t vuid, struct timeval request_time)
+{
+ void *p;
+ struct smbsrv_session *sess;
+
+ if (vuid == 0) return NULL;
+
+ if (vuid > smb_conn->sessions.idtree_limit) return NULL;
+
+ p = idr_find(smb_conn->sessions.idtree_vuid, vuid);
+ if (!p) return NULL;
+
+ /* only return a finished session */
+ sess = talloc_get_type(p, struct smbsrv_session);
+ if (sess && sess->session_info) {
+ sess->statistics.last_request_time = request_time;
+ return sess;
+ }
+
+ return NULL;
+}
+
+/*
+ * Find the session structure assoicated with a VUID
+ * (assoicated with an in-progress session setup)
+ */
+struct smbsrv_session *smbsrv_session_find_sesssetup(struct smbsrv_connection *smb_conn, uint64_t vuid)
+{
+ void *p;
+ struct smbsrv_session *sess;
+
+ if (vuid == 0) return NULL;
+
+ if (vuid > smb_conn->sessions.idtree_limit) return NULL;
+
+ p = idr_find(smb_conn->sessions.idtree_vuid, vuid);
+ if (!p) return NULL;
+
+ /* only return an unfinished session */
+ sess = talloc_get_type(p, struct smbsrv_session);
+ if (sess && !sess->session_info) {
+ return sess;
+ }
+ return NULL;
+}
+
+/*
+ * the session will be marked as valid for usage
+ * by attaching a auth_session_info to the session.
+ *
+ * session_info will be talloc_stealed
+ */
+NTSTATUS smbsrv_session_sesssetup_finished(struct smbsrv_session *sess,
+ struct auth_session_info *session_info)
+{
+ /* this check is to catch programmer errors */
+ if (!session_info) {
+ talloc_free(sess);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* mark the session as successful authenticated */
+ sess->session_info = talloc_steal(sess, session_info);
+
+ /* now fill in some statistics */
+ sess->statistics.auth_time = timeval_current();
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+destroy a session structure
+****************************************************************************/
+static int smbsrv_session_destructor(struct smbsrv_session *sess)
+{
+ struct smbsrv_connection *smb_conn = sess->smb_conn;
+
+ idr_remove(smb_conn->sessions.idtree_vuid, sess->vuid);
+ DLIST_REMOVE(smb_conn->sessions.list, sess);
+ return 0;
+}
+
+/*
+ * allocate a new session structure with a VUID.
+ * gensec_ctx is optional, but talloc_steal'ed when present
+ */
+struct smbsrv_session *smbsrv_session_new(struct smbsrv_connection *smb_conn,
+ TALLOC_CTX *mem_ctx,
+ struct gensec_security *gensec_ctx)
+{
+ struct smbsrv_session *sess = NULL;
+ int i;
+
+ /* Ensure no vuid gets registered in share level security. */
+ if (smb_conn->config.security == SEC_SHARE) return NULL;
+
+ sess = talloc_zero(mem_ctx, struct smbsrv_session);
+ if (!sess) return NULL;
+ sess->smb_conn = smb_conn;
+
+ i = idr_get_new_random(smb_conn->sessions.idtree_vuid, sess, smb_conn->sessions.idtree_limit);
+ if (i == -1) {
+ DEBUG(1,("ERROR! Out of connection structures\n"));
+ talloc_free(sess);
+ return NULL;
+ }
+ sess->vuid = i;
+
+ /* use this to keep tabs on all our info from the authentication */
+ sess->gensec_ctx = talloc_steal(sess, gensec_ctx);
+
+ DLIST_ADD(smb_conn->sessions.list, sess);
+ talloc_set_destructor(sess, smbsrv_session_destructor);
+
+ /* now fill in some statistics */
+ sess->statistics.connect_time = timeval_current();
+
+ return sess;
+}
diff --git a/source4/smb_server/smb/config.mk b/source4/smb_server/smb/config.mk
new file mode 100644
index 0000000000..eadc122831
--- /dev/null
+++ b/source4/smb_server/smb/config.mk
@@ -0,0 +1,22 @@
+#######################
+# Start SUBSYSTEM SMB_PROTOCOL
+[SUBSYSTEM::SMB_PROTOCOL]
+PUBLIC_DEPENDENCIES = \
+ ntvfs LIBPACKET CREDENTIALS samba_server_gensec
+# End SUBSYSTEM SMB_PROTOCOL
+#######################
+
+SMB_PROTOCOL_OBJ_FILES = $(addprefix $(smb_serversrcdir)/smb/, \
+ receive.o \
+ negprot.o \
+ nttrans.o \
+ reply.o \
+ request.o \
+ search.o \
+ service.o \
+ sesssetup.o \
+ srvtime.o \
+ trans2.o \
+ signing.o)
+
+$(eval $(call proto_header_template,$(smb_serversrcdir)/smb/smb_proto.h,$(SMB_PROTOCOL_OBJ_FILES:.o=.c)))
diff --git a/source4/smb_server/smb/negprot.c b/source4/smb_server/smb/negprot.c
new file mode 100644
index 0000000000..c3399fdd48
--- /dev/null
+++ b/source4/smb_server/smb/negprot.c
@@ -0,0 +1,542 @@
+/*
+ Unix SMB/CIFS implementation.
+ negprot reply code
+ Copyright (C) Andrew Tridgell 1992-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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "auth/credentials/credentials.h"
+#include "auth/gensec/gensec.h"
+#include "auth/auth.h"
+#include "smb_server/smb_server.h"
+#include "libcli/smb2/smb2.h"
+#include "smb_server/smb2/smb2_server.h"
+#include "smb_server/service_smb_proto.h"
+#include "smbd/service_stream.h"
+#include "lib/stream/packet.h"
+#include "param/param.h"
+
+
+/* initialise the auth_context for this server and return the cryptkey */
+static NTSTATUS get_challenge(struct smbsrv_connection *smb_conn, uint8_t buff[8])
+{
+ NTSTATUS nt_status;
+ const uint8_t *challenge;
+
+ /* muliple negprots are not premitted */
+ if (smb_conn->negotiate.auth_context) {
+ DEBUG(3,("get challenge: is this a secondary negprot? auth_context is non-NULL!\n"));
+ return NT_STATUS_FOOBAR;
+ }
+
+ DEBUG(10, ("get challenge: creating negprot_global_auth_context\n"));
+
+ nt_status = auth_context_create(smb_conn,
+ smb_conn->connection->event.ctx,
+ smb_conn->connection->msg_ctx,
+ smb_conn->lp_ctx,
+ &smb_conn->negotiate.auth_context);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("auth_context_create() returned %s", nt_errstr(nt_status)));
+ return nt_status;
+ }
+
+ nt_status = auth_get_challenge(smb_conn->negotiate.auth_context, &challenge);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("auth_get_challenge() returned %s", nt_errstr(nt_status)));
+ return nt_status;
+ }
+
+ memcpy(buff, challenge, 8);
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Reply for the core protocol.
+****************************************************************************/
+static void reply_corep(struct smbsrv_request *req, uint16_t choice)
+{
+ smbsrv_setup_reply(req, 1, 0);
+
+ SSVAL(req->out.vwv, VWV(0), choice);
+
+ req->smb_conn->negotiate.protocol = PROTOCOL_CORE;
+
+ if (req->smb_conn->signing.mandatory_signing) {
+ smbsrv_terminate_connection(req->smb_conn,
+ "CORE does not support SMB signing, and it is mandatory\n");
+ return;
+ }
+
+ smbsrv_send_reply(req);
+}
+
+/****************************************************************************
+ Reply for the coreplus protocol.
+this is quite incomplete - we only fill in a small part of the reply, but as nobody uses
+this any more it probably doesn't matter
+****************************************************************************/
+static void reply_coreplus(struct smbsrv_request *req, uint16_t choice)
+{
+ uint16_t raw = (lp_readraw(req->smb_conn->lp_ctx)?1:0) | (lp_writeraw(req->smb_conn->lp_ctx)?2:0);
+
+ smbsrv_setup_reply(req, 13, 0);
+
+ /* Reply, SMBlockread, SMBwritelock supported. */
+ SCVAL(req->out.hdr,HDR_FLG,
+ CVAL(req->out.hdr, HDR_FLG) | FLAG_SUPPORT_LOCKREAD);
+
+ SSVAL(req->out.vwv, VWV(0), choice);
+ SSVAL(req->out.vwv, VWV(1), 0x1); /* user level security, don't encrypt */
+
+ /* tell redirector we support
+ readbraw and writebraw (possibly) */
+ SSVAL(req->out.vwv, VWV(5), raw);
+
+ req->smb_conn->negotiate.protocol = PROTOCOL_COREPLUS;
+
+ if (req->smb_conn->signing.mandatory_signing) {
+ smbsrv_terminate_connection(req->smb_conn,
+ "COREPLUS does not support SMB signing, and it is mandatory\n");
+ return;
+ }
+
+ smbsrv_send_reply(req);
+}
+
+/****************************************************************************
+ Reply for the lanman 1.0 protocol.
+****************************************************************************/
+static void reply_lanman1(struct smbsrv_request *req, uint16_t choice)
+{
+ int raw = (lp_readraw(req->smb_conn->lp_ctx)?1:0) | (lp_writeraw(req->smb_conn->lp_ctx)?2:0);
+ int secword=0;
+ time_t t = req->request_time.tv_sec;
+
+ req->smb_conn->negotiate.encrypted_passwords = lp_encrypted_passwords(req->smb_conn->lp_ctx);
+
+ if (lp_security(req->smb_conn->lp_ctx) != SEC_SHARE)
+ secword |= NEGOTIATE_SECURITY_USER_LEVEL;
+
+ if (req->smb_conn->negotiate.encrypted_passwords)
+ secword |= NEGOTIATE_SECURITY_CHALLENGE_RESPONSE;
+
+ req->smb_conn->negotiate.protocol = PROTOCOL_LANMAN1;
+
+ smbsrv_setup_reply(req, 13, req->smb_conn->negotiate.encrypted_passwords ? 8 : 0);
+
+ /* SMBlockread, SMBwritelock supported. */
+ SCVAL(req->out.hdr,HDR_FLG,
+ CVAL(req->out.hdr, HDR_FLG) | FLAG_SUPPORT_LOCKREAD);
+
+ SSVAL(req->out.vwv, VWV(0), choice);
+ SSVAL(req->out.vwv, VWV(1), secword);
+ SSVAL(req->out.vwv, VWV(2), req->smb_conn->negotiate.max_recv);
+ SSVAL(req->out.vwv, VWV(3), lp_maxmux(req->smb_conn->lp_ctx));
+ SSVAL(req->out.vwv, VWV(4), 1);
+ SSVAL(req->out.vwv, VWV(5), raw);
+ SIVAL(req->out.vwv, VWV(6), req->smb_conn->connection->server_id.id);
+ srv_push_dos_date(req->smb_conn, req->out.vwv, VWV(8), t);
+ SSVAL(req->out.vwv, VWV(10), req->smb_conn->negotiate.zone_offset/60);
+ SIVAL(req->out.vwv, VWV(11), 0); /* reserved */
+
+ /* Create a token value and add it to the outgoing packet. */
+ if (req->smb_conn->negotiate.encrypted_passwords) {
+ NTSTATUS nt_status;
+
+ SSVAL(req->out.vwv, VWV(11), 8);
+
+ nt_status = get_challenge(req->smb_conn, req->out.data);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ smbsrv_terminate_connection(req->smb_conn, "LANMAN1 get_challenge failed\n");
+ return;
+ }
+ }
+
+ if (req->smb_conn->signing.mandatory_signing) {
+ smbsrv_terminate_connection(req->smb_conn,
+ "LANMAN1 does not support SMB signing, and it is mandatory\n");
+ return;
+ }
+
+ smbsrv_send_reply(req);
+}
+
+/****************************************************************************
+ Reply for the lanman 2.0 protocol.
+****************************************************************************/
+static void reply_lanman2(struct smbsrv_request *req, uint16_t choice)
+{
+ int raw = (lp_readraw(req->smb_conn->lp_ctx)?1:0) | (lp_writeraw(req->smb_conn->lp_ctx)?2:0);
+ int secword=0;
+ time_t t = req->request_time.tv_sec;
+
+ req->smb_conn->negotiate.encrypted_passwords = lp_encrypted_passwords(req->smb_conn->lp_ctx);
+
+ if (lp_security(req->smb_conn->lp_ctx) != SEC_SHARE)
+ secword |= NEGOTIATE_SECURITY_USER_LEVEL;
+
+ if (req->smb_conn->negotiate.encrypted_passwords)
+ secword |= NEGOTIATE_SECURITY_CHALLENGE_RESPONSE;
+
+ req->smb_conn->negotiate.protocol = PROTOCOL_LANMAN2;
+
+ smbsrv_setup_reply(req, 13, 0);
+
+ SSVAL(req->out.vwv, VWV(0), choice);
+ SSVAL(req->out.vwv, VWV(1), secword);
+ SSVAL(req->out.vwv, VWV(2), req->smb_conn->negotiate.max_recv);
+ SSVAL(req->out.vwv, VWV(3), lp_maxmux(req->smb_conn->lp_ctx));
+ SSVAL(req->out.vwv, VWV(4), 1);
+ SSVAL(req->out.vwv, VWV(5), raw);
+ SIVAL(req->out.vwv, VWV(6), req->smb_conn->connection->server_id.id);
+ srv_push_dos_date(req->smb_conn, req->out.vwv, VWV(8), t);
+ SSVAL(req->out.vwv, VWV(10), req->smb_conn->negotiate.zone_offset/60);
+ SIVAL(req->out.vwv, VWV(11), 0);
+
+ /* Create a token value and add it to the outgoing packet. */
+ if (req->smb_conn->negotiate.encrypted_passwords) {
+ SSVAL(req->out.vwv, VWV(11), 8);
+ req_grow_data(req, 8);
+ get_challenge(req->smb_conn, req->out.data);
+ }
+
+ req_push_str(req, NULL, lp_workgroup(req->smb_conn->lp_ctx), -1, STR_TERMINATE);
+
+ if (req->smb_conn->signing.mandatory_signing) {
+ smbsrv_terminate_connection(req->smb_conn,
+ "LANMAN2 does not support SMB signing, and it is mandatory\n");
+ return;
+ }
+
+ smbsrv_send_reply(req);
+}
+
+static void reply_nt1_orig(struct smbsrv_request *req)
+{
+ /* Create a token value and add it to the outgoing packet. */
+ if (req->smb_conn->negotiate.encrypted_passwords) {
+ req_grow_data(req, 8);
+ /* note that we do not send a challenge at all if
+ we are using plaintext */
+ get_challenge(req->smb_conn, req->out.ptr);
+ req->out.ptr += 8;
+ SCVAL(req->out.vwv+1, VWV(16), 8);
+ }
+ req_push_str(req, NULL, lp_workgroup(req->smb_conn->lp_ctx), -1, STR_UNICODE|STR_TERMINATE|STR_NOALIGN);
+ req_push_str(req, NULL, lp_netbios_name(req->smb_conn->lp_ctx), -1, STR_UNICODE|STR_TERMINATE|STR_NOALIGN);
+ DEBUG(3,("not using extended security (SPNEGO or NTLMSSP)\n"));
+}
+
+/****************************************************************************
+ Reply for the nt protocol.
+****************************************************************************/
+static void reply_nt1(struct smbsrv_request *req, uint16_t choice)
+{
+ /* dual names + lock_and_read + nt SMBs + remote API calls */
+ int capabilities;
+ int secword=0;
+ time_t t = req->request_time.tv_sec;
+ NTTIME nttime;
+ bool negotiate_spnego = false;
+ char *large_test_path;
+
+ unix_to_nt_time(&nttime, t);
+
+ capabilities =
+ CAP_NT_FIND | CAP_LOCK_AND_READ |
+ CAP_LEVEL_II_OPLOCKS | CAP_NT_SMBS | CAP_RPC_REMOTE_APIS;
+
+ req->smb_conn->negotiate.encrypted_passwords = lp_encrypted_passwords(req->smb_conn->lp_ctx);
+
+ /* do spnego in user level security if the client
+ supports it and we can do encrypted passwords */
+
+ if (req->smb_conn->negotiate.encrypted_passwords &&
+ (lp_security(req->smb_conn->lp_ctx) != SEC_SHARE) &&
+ lp_use_spnego(req->smb_conn->lp_ctx) &&
+ (req->flags2 & FLAGS2_EXTENDED_SECURITY)) {
+ negotiate_spnego = true;
+ capabilities |= CAP_EXTENDED_SECURITY;
+ }
+
+ if (lp_unix_extensions(req->smb_conn->lp_ctx)) {
+ capabilities |= CAP_UNIX;
+ }
+
+ if (lp_large_readwrite(req->smb_conn->lp_ctx)) {
+ capabilities |= CAP_LARGE_READX | CAP_LARGE_WRITEX | CAP_W2K_SMBS;
+ }
+
+ large_test_path = lock_path(req, req->smb_conn->lp_ctx, "large_test.dat");
+ if (large_file_support(large_test_path)) {
+ capabilities |= CAP_LARGE_FILES;
+ }
+
+ if (lp_readraw(req->smb_conn->lp_ctx) &&
+ lp_writeraw(req->smb_conn->lp_ctx)) {
+ capabilities |= CAP_RAW_MODE;
+ }
+
+ /* allow for disabling unicode */
+ if (lp_unicode(req->smb_conn->lp_ctx)) {
+ capabilities |= CAP_UNICODE;
+ }
+
+ if (lp_nt_status_support(req->smb_conn->lp_ctx)) {
+ capabilities |= CAP_STATUS32;
+ }
+
+ if (lp_host_msdfs(req->smb_conn->lp_ctx)) {
+ capabilities |= CAP_DFS;
+ }
+
+ if (lp_security(req->smb_conn->lp_ctx) != SEC_SHARE) {
+ secword |= NEGOTIATE_SECURITY_USER_LEVEL;
+ }
+
+ if (req->smb_conn->negotiate.encrypted_passwords) {
+ secword |= NEGOTIATE_SECURITY_CHALLENGE_RESPONSE;
+ }
+
+ if (req->smb_conn->signing.allow_smb_signing) {
+ secword |= NEGOTIATE_SECURITY_SIGNATURES_ENABLED;
+ }
+
+ if (req->smb_conn->signing.mandatory_signing) {
+ secword |= NEGOTIATE_SECURITY_SIGNATURES_REQUIRED;
+ }
+
+ req->smb_conn->negotiate.protocol = PROTOCOL_NT1;
+
+ smbsrv_setup_reply(req, 17, 0);
+
+ SSVAL(req->out.vwv, VWV(0), choice);
+ SCVAL(req->out.vwv, VWV(1), secword);
+
+ /* notice the strange +1 on vwv here? That's because
+ this is the one and only SMB packet that is malformed in
+ the specification - all the command words after the secword
+ are offset by 1 byte */
+ SSVAL(req->out.vwv+1, VWV(1), lp_maxmux(req->smb_conn->lp_ctx));
+ SSVAL(req->out.vwv+1, VWV(2), 1); /* num vcs */
+ SIVAL(req->out.vwv+1, VWV(3), req->smb_conn->negotiate.max_recv);
+ SIVAL(req->out.vwv+1, VWV(5), 0x10000); /* raw size. full 64k */
+ SIVAL(req->out.vwv+1, VWV(7), req->smb_conn->connection->server_id.id); /* session key */
+ SIVAL(req->out.vwv+1, VWV(9), capabilities);
+ push_nttime(req->out.vwv+1, VWV(11), nttime);
+ SSVALS(req->out.vwv+1,VWV(15), req->smb_conn->negotiate.zone_offset/60);
+
+ if (!negotiate_spnego) {
+ reply_nt1_orig(req);
+ } else {
+ struct cli_credentials *server_credentials;
+ struct gensec_security *gensec_security;
+ DATA_BLOB null_data_blob = data_blob(NULL, 0);
+ DATA_BLOB blob;
+ const char *oid;
+ NTSTATUS nt_status;
+
+ server_credentials
+ = cli_credentials_init(req);
+ if (!server_credentials) {
+ smbsrv_terminate_connection(req->smb_conn, "Failed to init server credentials\n");
+ return;
+ }
+
+ cli_credentials_set_conf(server_credentials, req->smb_conn->lp_ctx);
+ nt_status = cli_credentials_set_machine_account(server_credentials, req->smb_conn->lp_ctx);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(10, ("Failed to obtain server credentials, perhaps a standalone server?: %s\n", nt_errstr(nt_status)));
+ talloc_free(server_credentials);
+ server_credentials = NULL;
+ }
+
+ nt_status = samba_server_gensec_start(req,
+ req->smb_conn->connection->event.ctx,
+ req->smb_conn->connection->msg_ctx,
+ req->smb_conn->lp_ctx,
+ server_credentials,
+ "cifs",
+ &gensec_security);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("Failed to start GENSEC: %s\n", nt_errstr(nt_status)));
+ smbsrv_terminate_connection(req->smb_conn, "Failed to start GENSEC\n");
+ return;
+ }
+
+ if (req->smb_conn->negotiate.auth_context) {
+ smbsrv_terminate_connection(req->smb_conn, "reply_nt1: is this a secondary negprot? auth_context is non-NULL!\n");
+ return;
+ }
+ req->smb_conn->negotiate.server_credentials = talloc_steal(req->smb_conn, server_credentials);
+
+ gensec_set_target_service(gensec_security, "cifs");
+
+ gensec_set_credentials(gensec_security, server_credentials);
+
+ oid = GENSEC_OID_SPNEGO;
+ nt_status = gensec_start_mech_by_oid(gensec_security, oid);
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ /* Get and push the proposed OID list into the packets */
+ nt_status = gensec_update(gensec_security, req, null_data_blob, &blob);
+
+ if (!NT_STATUS_IS_OK(nt_status) && !NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ DEBUG(1, ("Failed to get SPNEGO to give us the first token: %s\n", nt_errstr(nt_status)));
+ }
+ }
+
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ DEBUG(3,("using SPNEGO\n"));
+ } else {
+ DEBUG(5, ("Failed to start SPNEGO, falling back to NTLMSSP only: %s\n", nt_errstr(nt_status)));
+ oid = GENSEC_OID_NTLMSSP;
+ nt_status = gensec_start_mech_by_oid(gensec_security, oid);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("Failed to start SPNEGO as well as NTLMSSP fallback: %s\n", nt_errstr(nt_status)));
+ reply_nt1_orig(req);
+ return;
+ }
+ /* NTLMSSP is a client-first exchange */
+ blob = data_blob(NULL, 0);
+ DEBUG(3,("using raw-NTLMSSP\n"));
+ }
+
+ req->smb_conn->negotiate.oid = oid;
+
+ req_grow_data(req, blob.length + 16);
+ /* a NOT very random guid, perhaps we should get it
+ * from the credentials (kitchen sink...) */
+ memset(req->out.ptr, '\0', 16);
+ req->out.ptr += 16;
+
+ memcpy(req->out.ptr, blob.data, blob.length);
+ SCVAL(req->out.vwv+1, VWV(16), blob.length + 16);
+ req->out.ptr += blob.length;
+ }
+
+ smbsrv_send_reply_nosign(req);
+}
+
+/****************************************************************************
+ Reply for the SMB2 2.001 protocol
+****************************************************************************/
+static void reply_smb2(struct smbsrv_request *req, uint16_t choice)
+{
+ struct smbsrv_connection *smb_conn = req->smb_conn;
+ NTSTATUS status;
+
+ talloc_free(smb_conn->sessions.idtree_vuid);
+ ZERO_STRUCT(smb_conn->sessions);
+ talloc_free(smb_conn->smb_tcons.idtree_tid);
+ ZERO_STRUCT(smb_conn->smb_tcons);
+ ZERO_STRUCT(smb_conn->signing);
+
+ /* reply with a SMB2 packet */
+ status = smbsrv_init_smb2_connection(smb_conn);
+ if (!NT_STATUS_IS_OK(status)) {
+ smbsrv_terminate_connection(smb_conn, nt_errstr(status));
+ talloc_free(req);
+ return;
+ }
+ packet_set_callback(smb_conn->packet, smbsrv_recv_smb2_request);
+ smb2srv_reply_smb_negprot(req);
+ req = NULL;
+}
+
+/* List of supported protocols, most desired first */
+static const struct {
+ const char *proto_name;
+ const char *short_name;
+ void (*proto_reply_fn)(struct smbsrv_request *req, uint16_t choice);
+ int protocol_level;
+} supported_protocols[] = {
+ {"SMB 2.002", "SMB2", reply_smb2, PROTOCOL_SMB2},
+ {"SMB 2.001", "SMB2", reply_smb2, PROTOCOL_SMB2},
+ {"NT LANMAN 1.0", "NT1", reply_nt1, PROTOCOL_NT1},
+ {"NT LM 0.12", "NT1", reply_nt1, PROTOCOL_NT1},
+ {"LANMAN2.1", "LANMAN2", reply_lanman2, PROTOCOL_LANMAN2},
+ {"LM1.2X002", "LANMAN2", reply_lanman2, PROTOCOL_LANMAN2},
+ {"Samba", "LANMAN2", reply_lanman2, PROTOCOL_LANMAN2},
+ {"DOS LM1.2X002", "LANMAN2", reply_lanman2, PROTOCOL_LANMAN2},
+ {"Windows for Workgroups 3.1a", "LANMAN1", reply_lanman1, PROTOCOL_LANMAN1},
+ {"LANMAN1.0", "LANMAN1", reply_lanman1, PROTOCOL_LANMAN1},
+ {"MICROSOFT NETWORKS 3.0", "LANMAN1", reply_lanman1, PROTOCOL_LANMAN1},
+ {"MICROSOFT NETWORKS 1.03", "COREPLUS", reply_coreplus, PROTOCOL_COREPLUS},
+ {"PC NETWORK PROGRAM 1.0", "CORE", reply_corep, PROTOCOL_CORE},
+ {NULL,NULL,NULL,0},
+};
+
+/****************************************************************************
+ Reply to a negprot.
+****************************************************************************/
+
+void smbsrv_reply_negprot(struct smbsrv_request *req)
+{
+ int protocol;
+ uint8_t *p;
+ uint32_t protos_count = 0;
+ char **protos = NULL;
+
+ if (req->smb_conn->negotiate.done_negprot) {
+ smbsrv_terminate_connection(req->smb_conn, "multiple negprot's are not permitted");
+ return;
+ }
+ req->smb_conn->negotiate.done_negprot = true;
+
+ p = req->in.data;
+ while (true) {
+ size_t len;
+
+ protos = talloc_realloc(req, protos, char *, protos_count + 1);
+ if (!protos) {
+ smbsrv_terminate_connection(req->smb_conn, nt_errstr(NT_STATUS_NO_MEMORY));
+ return;
+ }
+ protos[protos_count] = NULL;
+ len = req_pull_ascii4(&req->in.bufinfo, (const char **)&protos[protos_count], p, STR_ASCII|STR_TERMINATE);
+ p += len;
+ if (len == 0 || !protos[protos_count]) break;
+
+ DEBUG(5,("Requested protocol [%d][%s]\n", protos_count, protos[protos_count]));
+ protos_count++;
+ }
+
+ /* Check for protocols, most desirable first */
+ for (protocol = 0; supported_protocols[protocol].proto_name; protocol++) {
+ int i;
+
+ if (supported_protocols[protocol].protocol_level > lp_srv_maxprotocol(req->smb_conn->lp_ctx))
+ continue;
+ if (supported_protocols[protocol].protocol_level < lp_srv_minprotocol(req->smb_conn->lp_ctx))
+ continue;
+
+ for (i = 0; i < protos_count; i++) {
+ if (strcmp(supported_protocols[protocol].proto_name, protos[i]) != 0) continue;
+
+ supported_protocols[protocol].proto_reply_fn(req, i);
+ DEBUG(3,("Selected protocol [%d][%s]\n",
+ i, supported_protocols[protocol].proto_name));
+ return;
+ }
+ }
+
+ smbsrv_terminate_connection(req->smb_conn, "No protocol supported !");
+}
diff --git a/source4/smb_server/smb/nttrans.c b/source4/smb_server/smb/nttrans.c
new file mode 100644
index 0000000000..e739f391b9
--- /dev/null
+++ b/source4/smb_server/smb/nttrans.c
@@ -0,0 +1,810 @@
+/*
+ Unix SMB/CIFS implementation.
+ NT transaction handling
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) James J Myers 2003 <myersjj@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+/*
+ This file handles the parsing of transact2 requests
+*/
+
+#include "includes.h"
+#include "smb_server/smb_server.h"
+#include "ntvfs/ntvfs.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "librpc/gen_ndr/ndr_security.h"
+
+/*
+ hold the state of a nttrans op while in progress. Needed to allow for async backend
+ functions.
+*/
+struct nttrans_op {
+ struct smb_nttrans *trans;
+ NTSTATUS (*send_fn)(struct nttrans_op *);
+ void *op_info;
+};
+
+
+/* setup a nttrans reply, given the data and params sizes */
+static NTSTATUS nttrans_setup_reply(struct nttrans_op *op,
+ struct smb_nttrans *trans,
+ uint32_t param_size, uint32_t data_size,
+ uint8_t setup_count)
+{
+ trans->out.setup_count = setup_count;
+ if (setup_count != 0) {
+ trans->out.setup = talloc_zero_array(op, uint8_t, setup_count*2);
+ NT_STATUS_HAVE_NO_MEMORY(trans->out.setup);
+ }
+ trans->out.params = data_blob_talloc(op, NULL, param_size);
+ if (param_size != 0) {
+ NT_STATUS_HAVE_NO_MEMORY(trans->out.params.data);
+ }
+ trans->out.data = data_blob_talloc(op, NULL, data_size);
+ if (data_size != 0) {
+ NT_STATUS_HAVE_NO_MEMORY(trans->out.data.data);
+ }
+ return NT_STATUS_OK;
+}
+
+/*
+ send a nttrans create reply
+*/
+static NTSTATUS nttrans_create_send(struct nttrans_op *op)
+{
+ union smb_open *io = talloc_get_type(op->op_info, union smb_open);
+ uint8_t *params;
+ NTSTATUS status;
+
+ status = nttrans_setup_reply(op, op->trans, 69, 0, 0);
+ NT_STATUS_NOT_OK_RETURN(status);
+ params = op->trans->out.params.data;
+
+ SSVAL(params, 0, io->ntcreatex.out.oplock_level);
+ smbsrv_push_fnum(params, 2, io->ntcreatex.out.file.ntvfs);
+ SIVAL(params, 4, io->ntcreatex.out.create_action);
+ SIVAL(params, 8, 0); /* ea error offset */
+ push_nttime(params, 12, io->ntcreatex.out.create_time);
+ push_nttime(params, 20, io->ntcreatex.out.access_time);
+ push_nttime(params, 28, io->ntcreatex.out.write_time);
+ push_nttime(params, 36, io->ntcreatex.out.change_time);
+ SIVAL(params, 44, io->ntcreatex.out.attrib);
+ SBVAL(params, 48, io->ntcreatex.out.alloc_size);
+ SBVAL(params, 56, io->ntcreatex.out.size);
+ SSVAL(params, 64, io->ntcreatex.out.file_type);
+ SSVAL(params, 66, io->ntcreatex.out.ipc_state);
+ SCVAL(params, 68, io->ntcreatex.out.is_directory);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ parse NTTRANS_CREATE request
+ */
+static NTSTATUS nttrans_create(struct smbsrv_request *req,
+ struct nttrans_op *op)
+{
+ struct smb_nttrans *trans = op->trans;
+ union smb_open *io;
+ uint16_t fname_len;
+ uint32_t sd_length, ea_length;
+ NTSTATUS status;
+ uint8_t *params;
+ enum ndr_err_code ndr_err;
+
+ if (trans->in.params.length < 54) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* parse the request */
+ io = talloc(op, union smb_open);
+ NT_STATUS_HAVE_NO_MEMORY(io);
+
+ io->ntcreatex.level = RAW_OPEN_NTTRANS_CREATE;
+
+ params = trans->in.params.data;
+
+ io->ntcreatex.in.flags = IVAL(params, 0);
+ io->ntcreatex.in.root_fid = IVAL(params, 4);
+ io->ntcreatex.in.access_mask = IVAL(params, 8);
+ io->ntcreatex.in.alloc_size = BVAL(params, 12);
+ io->ntcreatex.in.file_attr = IVAL(params, 20);
+ io->ntcreatex.in.share_access = IVAL(params, 24);
+ io->ntcreatex.in.open_disposition = IVAL(params, 28);
+ io->ntcreatex.in.create_options = IVAL(params, 32);
+ sd_length = IVAL(params, 36);
+ ea_length = IVAL(params, 40);
+ fname_len = IVAL(params, 44);
+ io->ntcreatex.in.impersonation = IVAL(params, 48);
+ io->ntcreatex.in.security_flags = CVAL(params, 52);
+ io->ntcreatex.in.sec_desc = NULL;
+ io->ntcreatex.in.ea_list = NULL;
+ io->ntcreatex.in.query_maximal_access = false;
+
+ req_pull_string(&req->in.bufinfo, &io->ntcreatex.in.fname,
+ params + 53,
+ MIN(fname_len+1, trans->in.params.length - 53),
+ STR_NO_RANGE_CHECK | STR_TERMINATE);
+ if (!io->ntcreatex.in.fname) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (sd_length > trans->in.data.length ||
+ ea_length > trans->in.data.length ||
+ (sd_length+ea_length) > trans->in.data.length) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* this call has an optional security descriptor */
+ if (sd_length != 0) {
+ DATA_BLOB blob;
+ blob.data = trans->in.data.data;
+ blob.length = sd_length;
+ io->ntcreatex.in.sec_desc = talloc(io, struct security_descriptor);
+ if (io->ntcreatex.in.sec_desc == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ ndr_err = ndr_pull_struct_blob(&blob, io, NULL,
+ io->ntcreatex.in.sec_desc,
+ (ndr_pull_flags_fn_t)ndr_pull_security_descriptor);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+ }
+
+ /* and an optional ea_list */
+ if (ea_length > 4) {
+ DATA_BLOB blob;
+ blob.data = trans->in.data.data + sd_length;
+ blob.length = ea_length;
+ io->ntcreatex.in.ea_list = talloc(io, struct smb_ea_list);
+ if (io->ntcreatex.in.ea_list == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = ea_pull_list_chained(&blob, io,
+ &io->ntcreatex.in.ea_list->num_eas,
+ &io->ntcreatex.in.ea_list->eas);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ op->send_fn = nttrans_create_send;
+ op->op_info = io;
+
+ return ntvfs_open(req->ntvfs, io);
+}
+
+
+/*
+ send NTTRANS_QUERY_SEC_DESC reply
+ */
+static NTSTATUS nttrans_query_sec_desc_send(struct nttrans_op *op)
+{
+ union smb_fileinfo *io = talloc_get_type(op->op_info, union smb_fileinfo);
+ uint8_t *params;
+ NTSTATUS status;
+ enum ndr_err_code ndr_err;
+
+ status = nttrans_setup_reply(op, op->trans, 4, 0, 0);
+ NT_STATUS_NOT_OK_RETURN(status);
+ params = op->trans->out.params.data;
+
+ ndr_err = ndr_push_struct_blob(&op->trans->out.data, op, NULL,
+ io->query_secdesc.out.sd,
+ (ndr_push_flags_fn_t)ndr_push_security_descriptor);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ SIVAL(params, 0, op->trans->out.data.length);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ parse NTTRANS_QUERY_SEC_DESC request
+ */
+static NTSTATUS nttrans_query_sec_desc(struct smbsrv_request *req,
+ struct nttrans_op *op)
+{
+ struct smb_nttrans *trans = op->trans;
+ union smb_fileinfo *io;
+
+ if (trans->in.params.length < 8) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* parse the request */
+ io = talloc(op, union smb_fileinfo);
+ NT_STATUS_HAVE_NO_MEMORY(io);
+
+ io->query_secdesc.level = RAW_FILEINFO_SEC_DESC;
+ io->query_secdesc.in.file.ntvfs = smbsrv_pull_fnum(req, trans->in.params.data, 0);
+ io->query_secdesc.in.secinfo_flags = IVAL(trans->in.params.data, 4);
+
+ op->op_info = io;
+ op->send_fn = nttrans_query_sec_desc_send;
+
+ SMBSRV_CHECK_FILE_HANDLE_NTSTATUS(io->query_secdesc.in.file.ntvfs);
+ return ntvfs_qfileinfo(req->ntvfs, io);
+}
+
+
+/*
+ parse NTTRANS_SET_SEC_DESC request
+ */
+static NTSTATUS nttrans_set_sec_desc(struct smbsrv_request *req,
+ struct nttrans_op *op)
+{
+ struct smb_nttrans *trans = op->trans;
+ union smb_setfileinfo *io;
+ enum ndr_err_code ndr_err;
+
+ if (trans->in.params.length < 8) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* parse the request */
+ io = talloc(req, union smb_setfileinfo);
+ NT_STATUS_HAVE_NO_MEMORY(io);
+
+ io->set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
+ io->set_secdesc.in.file.ntvfs = smbsrv_pull_fnum(req, trans->in.params.data, 0);
+ io->set_secdesc.in.secinfo_flags = IVAL(trans->in.params.data, 4);
+
+ io->set_secdesc.in.sd = talloc(io, struct security_descriptor);
+ NT_STATUS_HAVE_NO_MEMORY(io->set_secdesc.in.sd);
+
+ ndr_err = ndr_pull_struct_blob(&trans->in.data, req, NULL,
+ io->set_secdesc.in.sd,
+ (ndr_pull_flags_fn_t)ndr_pull_security_descriptor);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ SMBSRV_CHECK_FILE_HANDLE_NTSTATUS(io->set_secdesc.in.file.ntvfs);
+ return ntvfs_setfileinfo(req->ntvfs, io);
+}
+
+
+/* parse NTTRANS_RENAME request
+ */
+static NTSTATUS nttrans_rename(struct smbsrv_request *req,
+ struct nttrans_op *op)
+{
+ struct smb_nttrans *trans = op->trans;
+ union smb_rename *io;
+
+ if (trans->in.params.length < 5) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* parse the request */
+ io = talloc(req, union smb_rename);
+ NT_STATUS_HAVE_NO_MEMORY(io);
+
+ io->nttrans.level = RAW_RENAME_NTTRANS;
+ io->nttrans.in.file.ntvfs = smbsrv_pull_fnum(req, trans->in.params.data, 0);
+ io->nttrans.in.flags = SVAL(trans->in.params.data, 2);
+
+ smbsrv_blob_pull_string(&req->in.bufinfo, &trans->in.params, 4,
+ &io->nttrans.in.new_name,
+ STR_TERMINATE);
+ if (!io->nttrans.in.new_name) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ SMBSRV_CHECK_FILE_HANDLE_NTSTATUS(io->nttrans.in.file.ntvfs);
+ return ntvfs_rename(req->ntvfs, io);
+}
+
+/*
+ parse NTTRANS_IOCTL send
+ */
+static NTSTATUS nttrans_ioctl_send(struct nttrans_op *op)
+{
+ union smb_ioctl *info = talloc_get_type(op->op_info, union smb_ioctl);
+ NTSTATUS status;
+
+ /*
+ * we pass 0 as data_count here,
+ * because we reuse the DATA_BLOB from the smb_ioctl
+ * struct
+ */
+ status = nttrans_setup_reply(op, op->trans, 0, 0, 1);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ op->trans->out.setup[0] = 0;
+ op->trans->out.data = info->ntioctl.out.blob;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ parse NTTRANS_IOCTL request
+ */
+static NTSTATUS nttrans_ioctl(struct smbsrv_request *req,
+ struct nttrans_op *op)
+{
+ struct smb_nttrans *trans = op->trans;
+ union smb_ioctl *nt;
+
+ /* should have at least 4 setup words */
+ if (trans->in.setup_count != 4) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ nt = talloc(op, union smb_ioctl);
+ NT_STATUS_HAVE_NO_MEMORY(nt);
+
+ nt->ntioctl.level = RAW_IOCTL_NTIOCTL;
+ nt->ntioctl.in.function = IVAL(trans->in.setup, 0);
+ nt->ntioctl.in.file.ntvfs = smbsrv_pull_fnum(req, (uint8_t *)trans->in.setup, 4);
+ nt->ntioctl.in.fsctl = CVAL(trans->in.setup, 6);
+ nt->ntioctl.in.filter = CVAL(trans->in.setup, 7);
+ nt->ntioctl.in.max_data = trans->in.max_data;
+ nt->ntioctl.in.blob = trans->in.data;
+
+ op->op_info = nt;
+ op->send_fn = nttrans_ioctl_send;
+
+ SMBSRV_CHECK_FILE_HANDLE_NTSTATUS(nt->ntioctl.in.file.ntvfs);
+ return ntvfs_ioctl(req->ntvfs, nt);
+}
+
+
+/*
+ send NTTRANS_NOTIFY_CHANGE reply
+ */
+static NTSTATUS nttrans_notify_change_send(struct nttrans_op *op)
+{
+ union smb_notify *info = talloc_get_type(op->op_info, union smb_notify);
+ size_t size = 0;
+ int i;
+ NTSTATUS status;
+ uint8_t *p;
+#define MAX_BYTES_PER_CHAR 3
+
+ /* work out how big the reply buffer could be */
+ for (i=0;i<info->nttrans.out.num_changes;i++) {
+ size += 12 + 3 + (1+strlen(info->nttrans.out.changes[i].name.s)) * MAX_BYTES_PER_CHAR;
+ }
+
+ status = nttrans_setup_reply(op, op->trans, size, 0, 0);
+ NT_STATUS_NOT_OK_RETURN(status);
+ p = op->trans->out.params.data;
+
+ /* construct the changes buffer */
+ for (i=0;i<info->nttrans.out.num_changes;i++) {
+ uint32_t ofs;
+ ssize_t len;
+
+ SIVAL(p, 4, info->nttrans.out.changes[i].action);
+ len = push_string(p + 12, info->nttrans.out.changes[i].name.s,
+ op->trans->out.params.length -
+ (p+12 - op->trans->out.params.data), STR_UNICODE);
+ SIVAL(p, 8, len);
+
+ ofs = len + 12;
+
+ if (ofs & 3) {
+ int pad = 4 - (ofs & 3);
+ memset(p+ofs, 0, pad);
+ ofs += pad;
+ }
+
+ if (i == info->nttrans.out.num_changes-1) {
+ SIVAL(p, 0, 0);
+ } else {
+ SIVAL(p, 0, ofs);
+ }
+
+ p += ofs;
+ }
+
+ op->trans->out.params.length = p - op->trans->out.params.data;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ parse NTTRANS_NOTIFY_CHANGE request
+ */
+static NTSTATUS nttrans_notify_change(struct smbsrv_request *req,
+ struct nttrans_op *op)
+{
+ struct smb_nttrans *trans = op->trans;
+ union smb_notify *info;
+
+ /* should have at least 4 setup words */
+ if (trans->in.setup_count != 4) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ info = talloc(op, union smb_notify);
+ NT_STATUS_HAVE_NO_MEMORY(info);
+
+ info->nttrans.level = RAW_NOTIFY_NTTRANS;
+ info->nttrans.in.completion_filter = IVAL(trans->in.setup, 0);
+ info->nttrans.in.file.ntvfs = smbsrv_pull_fnum(req, (uint8_t *)trans->in.setup, 4);
+ info->nttrans.in.recursive = SVAL(trans->in.setup, 6);
+ info->nttrans.in.buffer_size = trans->in.max_param;
+
+ op->op_info = info;
+ op->send_fn = nttrans_notify_change_send;
+
+ SMBSRV_CHECK_FILE_HANDLE_NTSTATUS(info->nttrans.in.file.ntvfs);
+ return ntvfs_notify(req->ntvfs, info);
+}
+
+/*
+ backend for nttrans requests
+*/
+static NTSTATUS nttrans_backend(struct smbsrv_request *req,
+ struct nttrans_op *op)
+{
+ /* the nttrans command is in function */
+ switch (op->trans->in.function) {
+ case NT_TRANSACT_CREATE:
+ return nttrans_create(req, op);
+ case NT_TRANSACT_IOCTL:
+ return nttrans_ioctl(req, op);
+ case NT_TRANSACT_RENAME:
+ return nttrans_rename(req, op);
+ case NT_TRANSACT_QUERY_SECURITY_DESC:
+ return nttrans_query_sec_desc(req, op);
+ case NT_TRANSACT_SET_SECURITY_DESC:
+ return nttrans_set_sec_desc(req, op);
+ case NT_TRANSACT_NOTIFY_CHANGE:
+ return nttrans_notify_change(req, op);
+ }
+
+ /* an unknown nttrans command */
+ return NT_STATUS_DOS(ERRSRV, ERRerror);
+}
+
+
+static void reply_nttrans_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+ uint32_t params_left, data_left;
+ uint8_t *params, *data;
+ struct smb_nttrans *trans;
+ struct nttrans_op *op;
+
+ SMBSRV_CHECK_ASYNC_STATUS(op, struct nttrans_op);
+
+ trans = op->trans;
+
+ /* if this function needs work to form the nttrans reply buffer, then
+ call that now */
+ if (op->send_fn != NULL) {
+ NTSTATUS status;
+ status = op->send_fn(op);
+ if (!NT_STATUS_IS_OK(status)) {
+ smbsrv_send_error(req, status);
+ return;
+ }
+ }
+
+ smbsrv_setup_reply(req, 18 + trans->out.setup_count, 0);
+
+ /* note that we don't check the max_setup count (matching w2k3
+ behaviour) */
+
+ if (trans->out.params.length > trans->in.max_param) {
+ smbsrv_setup_error(req, NT_STATUS_BUFFER_TOO_SMALL);
+ trans->out.params.length = trans->in.max_param;
+ }
+ if (trans->out.data.length > trans->in.max_data) {
+ smbsrv_setup_error(req, NT_STATUS_BUFFER_TOO_SMALL);
+ trans->out.data.length = trans->in.max_data;
+ }
+
+ params_left = trans->out.params.length;
+ data_left = trans->out.data.length;
+ params = trans->out.params.data;
+ data = trans->out.data.data;
+
+ /* we need to divide up the reply into chunks that fit into
+ the negotiated buffer size */
+ do {
+ uint32_t this_data, this_param, max_bytes;
+ uint_t align1 = 1, align2 = (params_left ? 2 : 0);
+ struct smbsrv_request *this_req;
+
+ max_bytes = req_max_data(req) - (align1 + align2);
+
+ this_param = params_left;
+ if (this_param > max_bytes) {
+ this_param = max_bytes;
+ }
+ max_bytes -= this_param;
+
+ this_data = data_left;
+ if (this_data > max_bytes) {
+ this_data = max_bytes;
+ }
+
+ /* don't destroy unless this is the last chunk */
+ if (params_left - this_param != 0 ||
+ data_left - this_data != 0) {
+ this_req = smbsrv_setup_secondary_request(req);
+ } else {
+ this_req = req;
+ }
+
+ req_grow_data(this_req, this_param + this_data + (align1 + align2));
+
+ SSVAL(this_req->out.vwv, 0, 0); /* reserved */
+ SCVAL(this_req->out.vwv, 2, 0); /* reserved */
+ SIVAL(this_req->out.vwv, 3, trans->out.params.length);
+ SIVAL(this_req->out.vwv, 7, trans->out.data.length);
+
+ SIVAL(this_req->out.vwv, 11, this_param);
+ SIVAL(this_req->out.vwv, 15, align1 + PTR_DIFF(this_req->out.data, this_req->out.hdr));
+ SIVAL(this_req->out.vwv, 19, PTR_DIFF(params, trans->out.params.data));
+
+ SIVAL(this_req->out.vwv, 23, this_data);
+ SIVAL(this_req->out.vwv, 27, align1 + align2 +
+ PTR_DIFF(this_req->out.data + this_param, this_req->out.hdr));
+ SIVAL(this_req->out.vwv, 31, PTR_DIFF(data, trans->out.data.data));
+
+ SCVAL(this_req->out.vwv, 35, trans->out.setup_count);
+ memcpy((char *)(this_req->out.vwv) + VWV(18), trans->out.setup,
+ sizeof(uint16_t) * trans->out.setup_count);
+ memset(this_req->out.data, 0, align1);
+ if (this_param != 0) {
+ memcpy(this_req->out.data + align1, params, this_param);
+ }
+ memset(this_req->out.data+this_param+align1, 0, align2);
+ if (this_data != 0) {
+ memcpy(this_req->out.data+this_param+align1+align2,
+ data, this_data);
+ }
+
+ params_left -= this_param;
+ data_left -= this_data;
+ params += this_param;
+ data += this_data;
+
+ smbsrv_send_reply(this_req);
+ } while (params_left != 0 || data_left != 0);
+}
+
+/*
+ send a continue request
+*/
+static void reply_nttrans_continue(struct smbsrv_request *req, struct smb_nttrans *trans)
+{
+ struct smbsrv_request *req2;
+ struct smbsrv_trans_partial *tp;
+ int count;
+
+ /* make sure they don't flood us */
+ for (count=0,tp=req->smb_conn->trans_partial;tp;tp=tp->next) count++;
+ if (count > 100) {
+ smbsrv_send_error(req, NT_STATUS_INSUFFICIENT_RESOURCES);
+ return;
+ }
+
+ tp = talloc(req, struct smbsrv_trans_partial);
+
+ tp->req = req;
+ tp->u.nttrans = trans;
+ tp->command = SMBnttrans;
+
+ DLIST_ADD(req->smb_conn->trans_partial, tp);
+ talloc_set_destructor(tp, smbsrv_trans_partial_destructor);
+
+ req2 = smbsrv_setup_secondary_request(req);
+
+ /* send a 'please continue' reply */
+ smbsrv_setup_reply(req2, 0, 0);
+ smbsrv_send_reply(req2);
+}
+
+
+/*
+ answer a reconstructed trans request
+*/
+static void reply_nttrans_complete(struct smbsrv_request *req, struct smb_nttrans *trans)
+{
+ struct nttrans_op *op;
+
+ SMBSRV_TALLOC_IO_PTR(op, struct nttrans_op);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_nttrans_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ op->trans = trans;
+ op->op_info = NULL;
+ op->send_fn = NULL;
+
+ /* its a full request, give it to the backend */
+ ZERO_STRUCT(trans->out);
+ SMBSRV_CALL_NTVFS_BACKEND(nttrans_backend(req, op));
+}
+
+
+/****************************************************************************
+ Reply to an SMBnttrans request
+****************************************************************************/
+void smbsrv_reply_nttrans(struct smbsrv_request *req)
+{
+ struct smb_nttrans *trans;
+ uint32_t param_ofs, data_ofs;
+ uint32_t param_count, data_count;
+ uint32_t param_total, data_total;
+
+ /* parse request */
+ if (req->in.wct < 19) {
+ smbsrv_send_error(req, NT_STATUS_FOOBAR);
+ return;
+ }
+
+ trans = talloc(req, struct smb_nttrans);
+ if (trans == NULL) {
+ smbsrv_send_error(req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+
+ trans->in.max_setup = CVAL(req->in.vwv, 0);
+ param_total = IVAL(req->in.vwv, 3);
+ data_total = IVAL(req->in.vwv, 7);
+ trans->in.max_param = IVAL(req->in.vwv, 11);
+ trans->in.max_data = IVAL(req->in.vwv, 15);
+ param_count = IVAL(req->in.vwv, 19);
+ param_ofs = IVAL(req->in.vwv, 23);
+ data_count = IVAL(req->in.vwv, 27);
+ data_ofs = IVAL(req->in.vwv, 31);
+ trans->in.setup_count= CVAL(req->in.vwv, 35);
+ trans->in.function = SVAL(req->in.vwv, 36);
+
+ if (req->in.wct != 19 + trans->in.setup_count) {
+ smbsrv_send_error(req, NT_STATUS_DOS(ERRSRV, ERRerror));
+ return;
+ }
+
+ /* parse out the setup words */
+ trans->in.setup = talloc_array(req, uint8_t, trans->in.setup_count*2);
+ if (!trans->in.setup) {
+ smbsrv_send_error(req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ memcpy(trans->in.setup, (char *)(req->in.vwv) + VWV(19),
+ sizeof(uint16_t) * trans->in.setup_count);
+
+ if (!req_pull_blob(&req->in.bufinfo, req->in.hdr + param_ofs, param_count, &trans->in.params) ||
+ !req_pull_blob(&req->in.bufinfo, req->in.hdr + data_ofs, data_count, &trans->in.data)) {
+ smbsrv_send_error(req, NT_STATUS_FOOBAR);
+ return;
+ }
+
+ /* is it a partial request? if so, then send a 'send more' message */
+ if (param_total > param_count || data_total > data_count) {
+ reply_nttrans_continue(req, trans);
+ return;
+ }
+
+ reply_nttrans_complete(req, trans);
+}
+
+
+/****************************************************************************
+ Reply to an SMBnttranss request
+****************************************************************************/
+void smbsrv_reply_nttranss(struct smbsrv_request *req)
+{
+ struct smbsrv_trans_partial *tp;
+ struct smb_nttrans *trans = NULL;
+ uint32_t param_ofs, data_ofs;
+ uint32_t param_count, data_count;
+ uint32_t param_disp, data_disp;
+ uint32_t param_total, data_total;
+ DATA_BLOB params, data;
+
+ /* parse request */
+ if (req->in.wct != 18) {
+ smbsrv_send_error(req, NT_STATUS_DOS(ERRSRV, ERRerror));
+ return;
+ }
+
+ for (tp=req->smb_conn->trans_partial;tp;tp=tp->next) {
+ if (tp->command == SMBnttrans &&
+ SVAL(tp->req->in.hdr, HDR_MID) == SVAL(req->in.hdr, HDR_MID)) {
+/* TODO: check the VUID, PID and TID too? */
+ break;
+ }
+ }
+
+ if (tp == NULL) {
+ smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ trans = tp->u.nttrans;
+
+ param_total = IVAL(req->in.vwv, 3);
+ data_total = IVAL(req->in.vwv, 7);
+ param_count = IVAL(req->in.vwv, 11);
+ param_ofs = IVAL(req->in.vwv, 15);
+ param_disp = IVAL(req->in.vwv, 19);
+ data_count = IVAL(req->in.vwv, 23);
+ data_ofs = IVAL(req->in.vwv, 27);
+ data_disp = IVAL(req->in.vwv, 31);
+
+ if (!req_pull_blob(&req->in.bufinfo, req->in.hdr + param_ofs, param_count, &params) ||
+ !req_pull_blob(&req->in.bufinfo, req->in.hdr + data_ofs, data_count, &data)) {
+ smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ /* only allow contiguous requests */
+ if ((param_count != 0 &&
+ param_disp != trans->in.params.length) ||
+ (data_count != 0 &&
+ data_disp != trans->in.data.length)) {
+ smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ /* add to the existing request */
+ if (param_count != 0) {
+ trans->in.params.data = talloc_realloc(trans,
+ trans->in.params.data,
+ uint8_t,
+ param_disp + param_count);
+ if (trans->in.params.data == NULL) {
+ smbsrv_send_error(tp->req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ trans->in.params.length = param_disp + param_count;
+ }
+
+ if (data_count != 0) {
+ trans->in.data.data = talloc_realloc(trans,
+ trans->in.data.data,
+ uint8_t,
+ data_disp + data_count);
+ if (trans->in.data.data == NULL) {
+ smbsrv_send_error(tp->req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ trans->in.data.length = data_disp + data_count;
+ }
+
+ memcpy(trans->in.params.data + param_disp, params.data, params.length);
+ memcpy(trans->in.data.data + data_disp, data.data, data.length);
+
+ /* the sequence number of the reply is taken from the last secondary
+ response */
+ tp->req->seq_num = req->seq_num;
+
+ /* we don't reply to Transs2 requests */
+ talloc_free(req);
+
+ if (trans->in.params.length == param_total &&
+ trans->in.data.length == data_total) {
+ /* its now complete */
+ req = tp->req;
+ talloc_free(tp);
+ reply_nttrans_complete(req, trans);
+ }
+ return;
+}
diff --git a/source4/smb_server/smb/receive.c b/source4/smb_server/smb/receive.c
new file mode 100644
index 0000000000..03631f8f0b
--- /dev/null
+++ b/source4/smb_server/smb/receive.c
@@ -0,0 +1,689 @@
+/*
+ Unix SMB/CIFS implementation.
+ process incoming packets - main loop
+ Copyright (C) Andrew Tridgell 1992-2005
+ Copyright (C) James J Myers 2003 <myersjj@samba.org>
+ Copyright (C) Stefan Metzmacher 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/time.h"
+#include "smbd/service_stream.h"
+#include "smb_server/smb_server.h"
+#include "smb_server/service_smb_proto.h"
+#include "ntvfs/ntvfs.h"
+#include "system/filesys.h"
+#include "param/param.h"
+
+
+/*
+ send an oplock break request to a client
+*/
+NTSTATUS smbsrv_send_oplock_break(void *p, struct ntvfs_handle *ntvfs, uint8_t level)
+{
+ struct smbsrv_tcon *tcon = talloc_get_type(p, struct smbsrv_tcon);
+ struct smbsrv_request *req;
+
+ req = smbsrv_init_request(tcon->smb_conn);
+ NT_STATUS_HAVE_NO_MEMORY(req);
+
+ smbsrv_setup_reply(req, 8, 0);
+
+ SCVAL(req->out.hdr,HDR_COM,SMBlockingX);
+ SSVAL(req->out.hdr,HDR_TID,tcon->tid);
+ SSVAL(req->out.hdr,HDR_PID,0xFFFF);
+ SSVAL(req->out.hdr,HDR_UID,0);
+ SSVAL(req->out.hdr,HDR_MID,0xFFFF);
+ SCVAL(req->out.hdr,HDR_FLG,0);
+ SSVAL(req->out.hdr,HDR_FLG2,0);
+
+ SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+ SSVAL(req->out.vwv, VWV(1), 0);
+ smbsrv_push_fnum(req->out.vwv, VWV(2), ntvfs);
+ SCVAL(req->out.vwv, VWV(3), LOCKING_ANDX_OPLOCK_RELEASE);
+ SCVAL(req->out.vwv, VWV(3)+1, level);
+ SIVAL(req->out.vwv, VWV(4), 0);
+ SSVAL(req->out.vwv, VWV(6), 0);
+ SSVAL(req->out.vwv, VWV(7), 0);
+
+ smbsrv_send_reply(req);
+ return NT_STATUS_OK;
+}
+
+static void switch_message(int type, struct smbsrv_request *req);
+
+/*
+ These flags determine some of the permissions required to do an operation
+*/
+#define NEED_SESS (1<<0)
+#define NEED_TCON (1<<1)
+#define SIGNING_NO_REPLY (1<<2)
+/* does VWV(0) of the request hold chaining information */
+#define AND_X (1<<3)
+/* The 64Kb question: are requests > 64K valid? */
+#define LARGE_REQUEST (1<<4)
+
+/*
+ define a list of possible SMB messages and their corresponding
+ functions. Any message that has a NULL function is unimplemented -
+ please feel free to contribute implementations!
+*/
+static const struct smb_message_struct
+{
+ const char *name;
+ void (*fn)(struct smbsrv_request *);
+#define message_flags(type) smb_messages[(type) & 0xff].flags
+ int flags;
+}
+ smb_messages[256] = {
+/* 0x00 */ { "SMBmkdir", smbsrv_reply_mkdir, NEED_SESS|NEED_TCON },
+/* 0x01 */ { "SMBrmdir", smbsrv_reply_rmdir, NEED_SESS|NEED_TCON },
+/* 0x02 */ { "SMBopen", smbsrv_reply_open, NEED_SESS|NEED_TCON },
+/* 0x03 */ { "SMBcreate", smbsrv_reply_mknew, NEED_SESS|NEED_TCON },
+/* 0x04 */ { "SMBclose", smbsrv_reply_close, NEED_SESS|NEED_TCON },
+/* 0x05 */ { "SMBflush", smbsrv_reply_flush, NEED_SESS|NEED_TCON },
+/* 0x06 */ { "SMBunlink", smbsrv_reply_unlink, NEED_SESS|NEED_TCON },
+/* 0x07 */ { "SMBmv", smbsrv_reply_mv, NEED_SESS|NEED_TCON },
+/* 0x08 */ { "SMBgetatr", smbsrv_reply_getatr, NEED_SESS|NEED_TCON },
+/* 0x09 */ { "SMBsetatr", smbsrv_reply_setatr, NEED_SESS|NEED_TCON },
+/* 0x0a */ { "SMBread", smbsrv_reply_read, NEED_SESS|NEED_TCON },
+/* 0x0b */ { "SMBwrite", smbsrv_reply_write, NEED_SESS|NEED_TCON },
+/* 0x0c */ { "SMBlock", smbsrv_reply_lock, NEED_SESS|NEED_TCON },
+/* 0x0d */ { "SMBunlock", smbsrv_reply_unlock, NEED_SESS|NEED_TCON },
+/* 0x0e */ { "SMBctemp", smbsrv_reply_ctemp, NEED_SESS|NEED_TCON },
+/* 0x0f */ { "SMBmknew", smbsrv_reply_mknew, NEED_SESS|NEED_TCON },
+/* 0x10 */ { "SMBchkpth", smbsrv_reply_chkpth, NEED_SESS|NEED_TCON },
+/* 0x11 */ { "SMBexit", smbsrv_reply_exit, NEED_SESS },
+/* 0x12 */ { "SMBlseek", smbsrv_reply_lseek, NEED_SESS|NEED_TCON },
+/* 0x13 */ { "SMBlockread", smbsrv_reply_lockread, NEED_SESS|NEED_TCON },
+/* 0x14 */ { "SMBwriteunlock", smbsrv_reply_writeunlock, NEED_SESS|NEED_TCON },
+/* 0x15 */ { NULL, NULL, 0 },
+/* 0x16 */ { NULL, NULL, 0 },
+/* 0x17 */ { NULL, NULL, 0 },
+/* 0x18 */ { NULL, NULL, 0 },
+/* 0x19 */ { NULL, NULL, 0 },
+/* 0x1a */ { "SMBreadbraw", smbsrv_reply_readbraw, NEED_SESS|NEED_TCON },
+/* 0x1b */ { "SMBreadBmpx", smbsrv_reply_readbmpx, NEED_SESS|NEED_TCON },
+/* 0x1c */ { "SMBreadBs", NULL, 0 },
+/* 0x1d */ { "SMBwritebraw", smbsrv_reply_writebraw, NEED_SESS|NEED_TCON },
+/* 0x1e */ { "SMBwriteBmpx", smbsrv_reply_writebmpx, NEED_SESS|NEED_TCON },
+/* 0x1f */ { "SMBwriteBs", smbsrv_reply_writebs, NEED_SESS|NEED_TCON },
+/* 0x20 */ { "SMBwritec", NULL, 0 },
+/* 0x21 */ { NULL, NULL, 0 },
+/* 0x22 */ { "SMBsetattrE", smbsrv_reply_setattrE, NEED_SESS|NEED_TCON },
+/* 0x23 */ { "SMBgetattrE", smbsrv_reply_getattrE, NEED_SESS|NEED_TCON },
+/* 0x24 */ { "SMBlockingX", smbsrv_reply_lockingX, NEED_SESS|NEED_TCON|AND_X },
+/* 0x25 */ { "SMBtrans", smbsrv_reply_trans, NEED_SESS|NEED_TCON },
+/* 0x26 */ { "SMBtranss", smbsrv_reply_transs, NEED_SESS|NEED_TCON },
+/* 0x27 */ { "SMBioctl", smbsrv_reply_ioctl, NEED_SESS|NEED_TCON },
+/* 0x28 */ { "SMBioctls", NULL, NEED_SESS|NEED_TCON },
+/* 0x29 */ { "SMBcopy", smbsrv_reply_copy, NEED_SESS|NEED_TCON },
+/* 0x2a */ { "SMBmove", NULL, NEED_SESS|NEED_TCON },
+/* 0x2b */ { "SMBecho", smbsrv_reply_echo, 0 },
+/* 0x2c */ { "SMBwriteclose", smbsrv_reply_writeclose, NEED_SESS|NEED_TCON },
+/* 0x2d */ { "SMBopenX", smbsrv_reply_open_and_X, NEED_SESS|NEED_TCON|AND_X },
+/* 0x2e */ { "SMBreadX", smbsrv_reply_read_and_X, NEED_SESS|NEED_TCON|AND_X },
+/* 0x2f */ { "SMBwriteX", smbsrv_reply_write_and_X, NEED_SESS|NEED_TCON|AND_X|LARGE_REQUEST},
+/* 0x30 */ { NULL, NULL, 0 },
+/* 0x31 */ { NULL, NULL, 0 },
+/* 0x32 */ { "SMBtrans2", smbsrv_reply_trans2, NEED_SESS|NEED_TCON },
+/* 0x33 */ { "SMBtranss2", smbsrv_reply_transs2, NEED_SESS|NEED_TCON },
+/* 0x34 */ { "SMBfindclose", smbsrv_reply_findclose, NEED_SESS|NEED_TCON },
+/* 0x35 */ { "SMBfindnclose", smbsrv_reply_findnclose, NEED_SESS|NEED_TCON },
+/* 0x36 */ { NULL, NULL, 0 },
+/* 0x37 */ { NULL, NULL, 0 },
+/* 0x38 */ { NULL, NULL, 0 },
+/* 0x39 */ { NULL, NULL, 0 },
+/* 0x3a */ { NULL, NULL, 0 },
+/* 0x3b */ { NULL, NULL, 0 },
+/* 0x3c */ { NULL, NULL, 0 },
+/* 0x3d */ { NULL, NULL, 0 },
+/* 0x3e */ { NULL, NULL, 0 },
+/* 0x3f */ { NULL, NULL, 0 },
+/* 0x40 */ { NULL, NULL, 0 },
+/* 0x41 */ { NULL, NULL, 0 },
+/* 0x42 */ { NULL, NULL, 0 },
+/* 0x43 */ { NULL, NULL, 0 },
+/* 0x44 */ { NULL, NULL, 0 },
+/* 0x45 */ { NULL, NULL, 0 },
+/* 0x46 */ { NULL, NULL, 0 },
+/* 0x47 */ { NULL, NULL, 0 },
+/* 0x48 */ { NULL, NULL, 0 },
+/* 0x49 */ { NULL, NULL, 0 },
+/* 0x4a */ { NULL, NULL, 0 },
+/* 0x4b */ { NULL, NULL, 0 },
+/* 0x4c */ { NULL, NULL, 0 },
+/* 0x4d */ { NULL, NULL, 0 },
+/* 0x4e */ { NULL, NULL, 0 },
+/* 0x4f */ { NULL, NULL, 0 },
+/* 0x50 */ { NULL, NULL, 0 },
+/* 0x51 */ { NULL, NULL, 0 },
+/* 0x52 */ { NULL, NULL, 0 },
+/* 0x53 */ { NULL, NULL, 0 },
+/* 0x54 */ { NULL, NULL, 0 },
+/* 0x55 */ { NULL, NULL, 0 },
+/* 0x56 */ { NULL, NULL, 0 },
+/* 0x57 */ { NULL, NULL, 0 },
+/* 0x58 */ { NULL, NULL, 0 },
+/* 0x59 */ { NULL, NULL, 0 },
+/* 0x5a */ { NULL, NULL, 0 },
+/* 0x5b */ { NULL, NULL, 0 },
+/* 0x5c */ { NULL, NULL, 0 },
+/* 0x5d */ { NULL, NULL, 0 },
+/* 0x5e */ { NULL, NULL, 0 },
+/* 0x5f */ { NULL, NULL, 0 },
+/* 0x60 */ { NULL, NULL, 0 },
+/* 0x61 */ { NULL, NULL, 0 },
+/* 0x62 */ { NULL, NULL, 0 },
+/* 0x63 */ { NULL, NULL, 0 },
+/* 0x64 */ { NULL, NULL, 0 },
+/* 0x65 */ { NULL, NULL, 0 },
+/* 0x66 */ { NULL, NULL, 0 },
+/* 0x67 */ { NULL, NULL, 0 },
+/* 0x68 */ { NULL, NULL, 0 },
+/* 0x69 */ { NULL, NULL, 0 },
+/* 0x6a */ { NULL, NULL, 0 },
+/* 0x6b */ { NULL, NULL, 0 },
+/* 0x6c */ { NULL, NULL, 0 },
+/* 0x6d */ { NULL, NULL, 0 },
+/* 0x6e */ { NULL, NULL, 0 },
+/* 0x6f */ { NULL, NULL, 0 },
+/* 0x70 */ { "SMBtcon", smbsrv_reply_tcon, NEED_SESS },
+/* 0x71 */ { "SMBtdis", smbsrv_reply_tdis, NEED_TCON },
+/* 0x72 */ { "SMBnegprot", smbsrv_reply_negprot, 0 },
+/* 0x73 */ { "SMBsesssetupX", smbsrv_reply_sesssetup, AND_X },
+/* 0x74 */ { "SMBulogoffX", smbsrv_reply_ulogoffX, NEED_SESS|AND_X }, /* ulogoff doesn't give a valid TID */
+/* 0x75 */ { "SMBtconX", smbsrv_reply_tcon_and_X, NEED_SESS|AND_X },
+/* 0x76 */ { NULL, NULL, 0 },
+/* 0x77 */ { NULL, NULL, 0 },
+/* 0x78 */ { NULL, NULL, 0 },
+/* 0x79 */ { NULL, NULL, 0 },
+/* 0x7a */ { NULL, NULL, 0 },
+/* 0x7b */ { NULL, NULL, 0 },
+/* 0x7c */ { NULL, NULL, 0 },
+/* 0x7d */ { NULL, NULL, 0 },
+/* 0x7e */ { NULL, NULL, 0 },
+/* 0x7f */ { NULL, NULL, 0 },
+/* 0x80 */ { "SMBdskattr", smbsrv_reply_dskattr, NEED_SESS|NEED_TCON },
+/* 0x81 */ { "SMBsearch", smbsrv_reply_search, NEED_SESS|NEED_TCON },
+/* 0x82 */ { "SMBffirst", smbsrv_reply_search, NEED_SESS|NEED_TCON },
+/* 0x83 */ { "SMBfunique", smbsrv_reply_search, NEED_SESS|NEED_TCON },
+/* 0x84 */ { "SMBfclose", smbsrv_reply_fclose, NEED_SESS|NEED_TCON },
+/* 0x85 */ { NULL, NULL, 0 },
+/* 0x86 */ { NULL, NULL, 0 },
+/* 0x87 */ { NULL, NULL, 0 },
+/* 0x88 */ { NULL, NULL, 0 },
+/* 0x89 */ { NULL, NULL, 0 },
+/* 0x8a */ { NULL, NULL, 0 },
+/* 0x8b */ { NULL, NULL, 0 },
+/* 0x8c */ { NULL, NULL, 0 },
+/* 0x8d */ { NULL, NULL, 0 },
+/* 0x8e */ { NULL, NULL, 0 },
+/* 0x8f */ { NULL, NULL, 0 },
+/* 0x90 */ { NULL, NULL, 0 },
+/* 0x91 */ { NULL, NULL, 0 },
+/* 0x92 */ { NULL, NULL, 0 },
+/* 0x93 */ { NULL, NULL, 0 },
+/* 0x94 */ { NULL, NULL, 0 },
+/* 0x95 */ { NULL, NULL, 0 },
+/* 0x96 */ { NULL, NULL, 0 },
+/* 0x97 */ { NULL, NULL, 0 },
+/* 0x98 */ { NULL, NULL, 0 },
+/* 0x99 */ { NULL, NULL, 0 },
+/* 0x9a */ { NULL, NULL, 0 },
+/* 0x9b */ { NULL, NULL, 0 },
+/* 0x9c */ { NULL, NULL, 0 },
+/* 0x9d */ { NULL, NULL, 0 },
+/* 0x9e */ { NULL, NULL, 0 },
+/* 0x9f */ { NULL, NULL, 0 },
+/* 0xa0 */ { "SMBnttrans", smbsrv_reply_nttrans, NEED_SESS|NEED_TCON|LARGE_REQUEST },
+/* 0xa1 */ { "SMBnttranss", smbsrv_reply_nttranss, NEED_SESS|NEED_TCON },
+/* 0xa2 */ { "SMBntcreateX", smbsrv_reply_ntcreate_and_X, NEED_SESS|NEED_TCON|AND_X },
+/* 0xa3 */ { NULL, NULL, 0 },
+/* 0xa4 */ { "SMBntcancel", smbsrv_reply_ntcancel, NEED_SESS|NEED_TCON|SIGNING_NO_REPLY },
+/* 0xa5 */ { "SMBntrename", smbsrv_reply_ntrename, NEED_SESS|NEED_TCON },
+/* 0xa6 */ { NULL, NULL, 0 },
+/* 0xa7 */ { NULL, NULL, 0 },
+/* 0xa8 */ { NULL, NULL, 0 },
+/* 0xa9 */ { NULL, NULL, 0 },
+/* 0xaa */ { NULL, NULL, 0 },
+/* 0xab */ { NULL, NULL, 0 },
+/* 0xac */ { NULL, NULL, 0 },
+/* 0xad */ { NULL, NULL, 0 },
+/* 0xae */ { NULL, NULL, 0 },
+/* 0xaf */ { NULL, NULL, 0 },
+/* 0xb0 */ { NULL, NULL, 0 },
+/* 0xb1 */ { NULL, NULL, 0 },
+/* 0xb2 */ { NULL, NULL, 0 },
+/* 0xb3 */ { NULL, NULL, 0 },
+/* 0xb4 */ { NULL, NULL, 0 },
+/* 0xb5 */ { NULL, NULL, 0 },
+/* 0xb6 */ { NULL, NULL, 0 },
+/* 0xb7 */ { NULL, NULL, 0 },
+/* 0xb8 */ { NULL, NULL, 0 },
+/* 0xb9 */ { NULL, NULL, 0 },
+/* 0xba */ { NULL, NULL, 0 },
+/* 0xbb */ { NULL, NULL, 0 },
+/* 0xbc */ { NULL, NULL, 0 },
+/* 0xbd */ { NULL, NULL, 0 },
+/* 0xbe */ { NULL, NULL, 0 },
+/* 0xbf */ { NULL, NULL, 0 },
+/* 0xc0 */ { "SMBsplopen", smbsrv_reply_printopen, NEED_SESS|NEED_TCON },
+/* 0xc1 */ { "SMBsplwr", smbsrv_reply_printwrite, NEED_SESS|NEED_TCON },
+/* 0xc2 */ { "SMBsplclose", smbsrv_reply_printclose, NEED_SESS|NEED_TCON },
+/* 0xc3 */ { "SMBsplretq", smbsrv_reply_printqueue, NEED_SESS|NEED_TCON },
+/* 0xc4 */ { NULL, NULL, 0 },
+/* 0xc5 */ { NULL, NULL, 0 },
+/* 0xc6 */ { NULL, NULL, 0 },
+/* 0xc7 */ { NULL, NULL, 0 },
+/* 0xc8 */ { NULL, NULL, 0 },
+/* 0xc9 */ { NULL, NULL, 0 },
+/* 0xca */ { NULL, NULL, 0 },
+/* 0xcb */ { NULL, NULL, 0 },
+/* 0xcc */ { NULL, NULL, 0 },
+/* 0xcd */ { NULL, NULL, 0 },
+/* 0xce */ { NULL, NULL, 0 },
+/* 0xcf */ { NULL, NULL, 0 },
+/* 0xd0 */ { "SMBsends", NULL, 0 },
+/* 0xd1 */ { "SMBsendb", NULL, 0 },
+/* 0xd2 */ { "SMBfwdname", NULL, 0 },
+/* 0xd3 */ { "SMBcancelf", NULL, 0 },
+/* 0xd4 */ { "SMBgetmac", NULL, 0 },
+/* 0xd5 */ { "SMBsendstrt", NULL, 0 },
+/* 0xd6 */ { "SMBsendend", NULL, 0 },
+/* 0xd7 */ { "SMBsendtxt", NULL, 0 },
+/* 0xd8 */ { NULL, NULL, 0 },
+/* 0xd9 */ { NULL, NULL, 0 },
+/* 0xda */ { NULL, NULL, 0 },
+/* 0xdb */ { NULL, NULL, 0 },
+/* 0xdc */ { NULL, NULL, 0 },
+/* 0xdd */ { NULL, NULL, 0 },
+/* 0xde */ { NULL, NULL, 0 },
+/* 0xdf */ { NULL, NULL, 0 },
+/* 0xe0 */ { NULL, NULL, 0 },
+/* 0xe1 */ { NULL, NULL, 0 },
+/* 0xe2 */ { NULL, NULL, 0 },
+/* 0xe3 */ { NULL, NULL, 0 },
+/* 0xe4 */ { NULL, NULL, 0 },
+/* 0xe5 */ { NULL, NULL, 0 },
+/* 0xe6 */ { NULL, NULL, 0 },
+/* 0xe7 */ { NULL, NULL, 0 },
+/* 0xe8 */ { NULL, NULL, 0 },
+/* 0xe9 */ { NULL, NULL, 0 },
+/* 0xea */ { NULL, NULL, 0 },
+/* 0xeb */ { NULL, NULL, 0 },
+/* 0xec */ { NULL, NULL, 0 },
+/* 0xed */ { NULL, NULL, 0 },
+/* 0xee */ { NULL, NULL, 0 },
+/* 0xef */ { NULL, NULL, 0 },
+/* 0xf0 */ { NULL, NULL, 0 },
+/* 0xf1 */ { NULL, NULL, 0 },
+/* 0xf2 */ { NULL, NULL, 0 },
+/* 0xf3 */ { NULL, NULL, 0 },
+/* 0xf4 */ { NULL, NULL, 0 },
+/* 0xf5 */ { NULL, NULL, 0 },
+/* 0xf6 */ { NULL, NULL, 0 },
+/* 0xf7 */ { NULL, NULL, 0 },
+/* 0xf8 */ { NULL, NULL, 0 },
+/* 0xf9 */ { NULL, NULL, 0 },
+/* 0xfa */ { NULL, NULL, 0 },
+/* 0xfb */ { NULL, NULL, 0 },
+/* 0xfc */ { NULL, NULL, 0 },
+/* 0xfd */ { NULL, NULL, 0 },
+/* 0xfe */ { NULL, NULL, 0 },
+/* 0xff */ { NULL, NULL, 0 }
+};
+
+/****************************************************************************
+receive a SMB request header from the wire, forming a request_context
+from the result
+****************************************************************************/
+NTSTATUS smbsrv_recv_smb_request(void *private_data, DATA_BLOB blob)
+{
+ struct smbsrv_connection *smb_conn = talloc_get_type(private_data, struct smbsrv_connection);
+ struct smbsrv_request *req;
+ struct timeval cur_time = timeval_current();
+ uint8_t command;
+
+ smb_conn->statistics.last_request_time = cur_time;
+
+ /* see if its a special NBT packet */
+ if (CVAL(blob.data, 0) != 0) {
+ req = smbsrv_init_request(smb_conn);
+ NT_STATUS_HAVE_NO_MEMORY(req);
+
+ ZERO_STRUCT(req->in);
+
+ req->in.buffer = talloc_steal(req, blob.data);
+ req->in.size = blob.length;
+ req->request_time = cur_time;
+
+ smbsrv_reply_special(req);
+ return NT_STATUS_OK;
+ }
+
+ if ((NBT_HDR_SIZE + MIN_SMB_SIZE) > blob.length) {
+ DEBUG(2,("Invalid SMB packet: length %ld\n", (long)blob.length));
+ smbsrv_terminate_connection(smb_conn, "Invalid SMB packet");
+ return NT_STATUS_OK;
+ }
+
+ /* Make sure this is an SMB packet */
+ if (IVAL(blob.data, NBT_HDR_SIZE) != SMB_MAGIC) {
+ DEBUG(2,("Non-SMB packet of length %ld. Terminating connection\n",
+ (long)blob.length));
+ smbsrv_terminate_connection(smb_conn, "Non-SMB packet");
+ return NT_STATUS_OK;
+ }
+
+ req = smbsrv_init_request(smb_conn);
+ NT_STATUS_HAVE_NO_MEMORY(req);
+
+ req->in.buffer = talloc_steal(req, blob.data);
+ req->in.size = blob.length;
+ req->request_time = cur_time;
+ req->chained_fnum = -1;
+ req->in.allocated = req->in.size;
+ req->in.hdr = req->in.buffer + NBT_HDR_SIZE;
+ req->in.vwv = req->in.hdr + HDR_VWV;
+ req->in.wct = CVAL(req->in.hdr, HDR_WCT);
+
+ command = CVAL(req->in.hdr, HDR_COM);
+
+ if (req->in.vwv + VWV(req->in.wct) <= req->in.buffer + req->in.size) {
+ req->in.data = req->in.vwv + VWV(req->in.wct) + 2;
+ req->in.data_size = SVAL(req->in.vwv, VWV(req->in.wct));
+
+ /* the bcc length is only 16 bits, but some packets
+ (such as SMBwriteX) can be much larger than 64k. We
+ detect this by looking for a large non-chained NBT
+ packet (at least 64k bigger than what is
+ specified). If it is detected then the NBT size is
+ used instead of the bcc size */
+ if (req->in.data_size + 0x10000 <=
+ req->in.size - PTR_DIFF(req->in.data, req->in.buffer) &&
+ ( message_flags(command) & LARGE_REQUEST) &&
+ ( !(message_flags(command) & AND_X) ||
+ (req->in.wct < 1 || SVAL(req->in.vwv, VWV(0)) == SMB_CHAIN_NONE) )
+ ) {
+ /* its an oversized packet! fun for all the family */
+ req->in.data_size = req->in.size - PTR_DIFF(req->in.data,req->in.buffer);
+ }
+ }
+
+ if (NBT_HDR_SIZE + MIN_SMB_SIZE + 2*req->in.wct > req->in.size) {
+ DEBUG(2,("Invalid SMB word count %d\n", req->in.wct));
+ smbsrv_terminate_connection(req->smb_conn, "Invalid SMB packet");
+ return NT_STATUS_OK;
+ }
+
+ if (NBT_HDR_SIZE + MIN_SMB_SIZE + 2*req->in.wct + req->in.data_size > req->in.size) {
+ DEBUG(2,("Invalid SMB buffer length count %d\n",
+ (int)req->in.data_size));
+ smbsrv_terminate_connection(req->smb_conn, "Invalid SMB packet");
+ return NT_STATUS_OK;
+ }
+
+ req->flags2 = SVAL(req->in.hdr, HDR_FLG2);
+
+ /* fix the bufinfo */
+ smbsrv_setup_bufinfo(req);
+
+ if (!smbsrv_signing_check_incoming(req)) {
+ smbsrv_send_error(req, NT_STATUS_ACCESS_DENIED);
+ return NT_STATUS_OK;
+ }
+
+ command = CVAL(req->in.hdr, HDR_COM);
+ switch_message(command, req);
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+return a string containing the function name of a SMB command
+****************************************************************************/
+static const char *smb_fn_name(uint8_t type)
+{
+ const char *unknown_name = "SMBunknown";
+
+ if (smb_messages[type].name == NULL)
+ return unknown_name;
+
+ return smb_messages[type].name;
+}
+
+
+/****************************************************************************
+ Do a switch on the message type and call the specific reply function for this
+message. Unlike earlier versions of Samba the reply functions are responsible
+for sending the reply themselves, rather than returning a size to this function
+The reply functions may also choose to delay the processing by pushing the message
+onto the message queue
+****************************************************************************/
+static void switch_message(int type, struct smbsrv_request *req)
+{
+ int flags;
+ struct smbsrv_connection *smb_conn = req->smb_conn;
+ NTSTATUS status;
+
+ type &= 0xff;
+
+ errno = 0;
+
+ if (smb_messages[type].fn == NULL) {
+ DEBUG(0,("Unknown message type %d!\n",type));
+ smbsrv_reply_unknown(req);
+ return;
+ }
+
+ flags = smb_messages[type].flags;
+
+ req->tcon = smbsrv_smb_tcon_find(smb_conn, SVAL(req->in.hdr,HDR_TID), req->request_time);
+
+ if (!req->session) {
+ /* setup the user context for this request if it
+ hasn't already been initialised (to cope with SMB
+ chaining) */
+
+ /* In share mode security we must ignore the vuid. */
+ if (smb_conn->config.security == SEC_SHARE) {
+ if (req->tcon) {
+ req->session = req->tcon->sec_share.session;
+ }
+ } else {
+ req->session = smbsrv_session_find(req->smb_conn, SVAL(req->in.hdr,HDR_UID), req->request_time);
+ }
+ }
+
+ DEBUG(5,("switch message %s (task_id %u)\n",
+ smb_fn_name(type), (unsigned)req->smb_conn->connection->server_id.id));
+
+ /* this must be called before we do any reply */
+ if (flags & SIGNING_NO_REPLY) {
+ smbsrv_signing_no_reply(req);
+ }
+
+ /* see if the vuid is valid */
+ if ((flags & NEED_SESS) && !req->session) {
+ status = NT_STATUS_DOS(ERRSRV, ERRbaduid);
+ /* amazingly, the error code depends on the command */
+ switch (type) {
+ case SMBntcreateX:
+ case SMBntcancel:
+ case SMBulogoffX:
+ break;
+ default:
+ if (req->smb_conn->config.nt_status_support &&
+ req->smb_conn->negotiate.client_caps & CAP_STATUS32) {
+ status = NT_STATUS_INVALID_HANDLE;
+ }
+ break;
+ }
+ /*
+ * TODO:
+ * don't know how to handle smb signing for this case
+ * so just skip the reply
+ */
+ if ((flags & SIGNING_NO_REPLY) &&
+ (req->smb_conn->signing.signing_state != SMB_SIGNING_ENGINE_OFF)) {
+ DEBUG(1,("SKIP ERROR REPLY: %s %s because of unknown smb signing case\n",
+ smb_fn_name(type), nt_errstr(status)));
+ talloc_free(req);
+ return;
+ }
+ smbsrv_send_error(req, status);
+ return;
+ }
+
+ /* does this protocol need a valid tree connection? */
+ if ((flags & NEED_TCON) && !req->tcon) {
+ status = NT_STATUS_DOS(ERRSRV, ERRinvnid);
+ /* amazingly, the error code depends on the command */
+ switch (type) {
+ case SMBntcreateX:
+ case SMBntcancel:
+ case SMBtdis:
+ break;
+ default:
+ if (req->smb_conn->config.nt_status_support &&
+ req->smb_conn->negotiate.client_caps & CAP_STATUS32) {
+ status = NT_STATUS_INVALID_HANDLE;
+ }
+ break;
+ }
+ /*
+ * TODO:
+ * don't know how to handle smb signing for this case
+ * so just skip the reply
+ */
+ if ((flags & SIGNING_NO_REPLY) &&
+ (req->smb_conn->signing.signing_state != SMB_SIGNING_ENGINE_OFF)) {
+ DEBUG(1,("SKIP ERROR REPLY: %s %s because of unknown smb signing case\n",
+ smb_fn_name(type), nt_errstr(status)));
+ talloc_free(req);
+ return;
+ }
+ smbsrv_send_error(req, status);
+ return;
+ }
+
+ smb_messages[type].fn(req);
+}
+
+/*
+ we call this when first first part of a possibly chained request has been completed
+ and we need to call the 2nd part, if any
+*/
+void smbsrv_chain_reply(struct smbsrv_request *req)
+{
+ uint16_t chain_cmd, chain_offset;
+ uint8_t *vwv, *data;
+ uint16_t wct;
+ uint16_t data_size;
+
+ if (req->in.wct < 2 || req->out.wct < 2) {
+ smbsrv_send_error(req, NT_STATUS_DOS(ERRSRV, ERRerror));
+ return;
+ }
+
+ chain_cmd = CVAL(req->in.vwv, VWV(0));
+ chain_offset = SVAL(req->in.vwv, VWV(1));
+
+ if (chain_cmd == SMB_CHAIN_NONE) {
+ /* end of chain */
+ SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+ SSVAL(req->out.vwv, VWV(1), 0);
+ smbsrv_send_reply(req);
+ return;
+ }
+
+ if (chain_offset + req->in.hdr >= req->in.buffer + req->in.size) {
+ goto error;
+ }
+
+ wct = CVAL(req->in.hdr, chain_offset);
+ vwv = req->in.hdr + chain_offset + 1;
+
+ if (vwv + VWV(wct) + 2 > req->in.buffer + req->in.size) {
+ goto error;
+ }
+
+ data_size = SVAL(vwv, VWV(wct));
+ data = vwv + VWV(wct) + 2;
+
+ if (data + data_size > req->in.buffer + req->in.size) {
+ goto error;
+ }
+
+ /* all seems legit */
+ req->in.vwv = vwv;
+ req->in.wct = wct;
+ req->in.data = data;
+ req->in.data_size = data_size;
+ req->in.ptr = data;
+
+ /* fix the bufinfo */
+ smbsrv_setup_bufinfo(req);
+
+ req->chain_count++;
+
+ SSVAL(req->out.vwv, VWV(0), chain_cmd);
+ SSVAL(req->out.vwv, VWV(1), req->out.size - NBT_HDR_SIZE);
+
+ /* cleanup somestuff for the next request */
+ talloc_free(req->ntvfs);
+ req->ntvfs = NULL;
+ talloc_free(req->io_ptr);
+ req->io_ptr = NULL;
+
+ switch_message(chain_cmd, req);
+ return;
+
+error:
+ SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+ SSVAL(req->out.vwv, VWV(1), 0);
+ smbsrv_send_error(req, NT_STATUS_DOS(ERRSRV, ERRerror));
+}
+
+/*
+ * init the SMB protocol related stuff
+ */
+NTSTATUS smbsrv_init_smb_connection(struct smbsrv_connection *smb_conn, struct loadparm_context *lp_ctx)
+{
+ NTSTATUS status;
+
+ /* now initialise a few default values associated with this smb socket */
+ smb_conn->negotiate.max_send = 0xFFFF;
+
+ /* this is the size that w2k uses, and it appears to be important for
+ good performance */
+ smb_conn->negotiate.max_recv = lp_max_xmit(lp_ctx);
+
+ smb_conn->negotiate.zone_offset = get_time_zone(time(NULL));
+
+ smb_conn->config.security = lp_security(lp_ctx);
+ smb_conn->config.nt_status_support = lp_nt_status_support(lp_ctx);
+
+ status = smbsrv_init_sessions(smb_conn, UINT16_MAX);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ status = smbsrv_smb_init_tcons(smb_conn);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ smbsrv_init_signing(smb_conn);
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/smb_server/smb/reply.c b/source4/smb_server/smb/reply.c
new file mode 100644
index 0000000000..9c4ee993d2
--- /dev/null
+++ b/source4/smb_server/smb/reply.c
@@ -0,0 +1,2337 @@
+/*
+ Unix SMB/CIFS implementation.
+ Main SMB reply routines
+ Copyright (C) Andrew Tridgell 1992-2003
+ Copyright (C) James J Myers 2003 <myersjj@samba.org>
+ 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 <http://www.gnu.org/licenses/>.
+*/
+/*
+ This file handles most of the reply_ calls that the server
+ makes to handle specific SMB commands
+*/
+
+#include "includes.h"
+#include "smb_server/smb_server.h"
+#include "ntvfs/ntvfs.h"
+#include "librpc/gen_ndr/ndr_nbt.h"
+
+
+/****************************************************************************
+ Reply to a simple request (async send)
+****************************************************************************/
+static void reply_simple_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+
+ SMBSRV_CHECK_ASYNC_STATUS_SIMPLE;
+
+ smbsrv_setup_reply(req, 0, 0);
+ smbsrv_send_reply(req);
+}
+
+
+/****************************************************************************
+ Reply to a tcon.
+****************************************************************************/
+void smbsrv_reply_tcon(struct smbsrv_request *req)
+{
+ union smb_tcon con;
+ NTSTATUS status;
+ uint8_t *p;
+
+ /* parse request */
+ SMBSRV_CHECK_WCT(req, 0);
+
+ con.tcon.level = RAW_TCON_TCON;
+
+ p = req->in.data;
+ p += req_pull_ascii4(&req->in.bufinfo, &con.tcon.in.service, p, STR_TERMINATE);
+ p += req_pull_ascii4(&req->in.bufinfo, &con.tcon.in.password, p, STR_TERMINATE);
+ p += req_pull_ascii4(&req->in.bufinfo, &con.tcon.in.dev, p, STR_TERMINATE);
+
+ if (!con.tcon.in.service || !con.tcon.in.password || !con.tcon.in.dev) {
+ smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ /* call backend */
+ status = smbsrv_tcon_backend(req, &con);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ smbsrv_send_error(req, status);
+ return;
+ }
+
+ /* construct reply */
+ smbsrv_setup_reply(req, 2, 0);
+
+ SSVAL(req->out.vwv, VWV(0), con.tcon.out.max_xmit);
+ SSVAL(req->out.vwv, VWV(1), con.tcon.out.tid);
+ SSVAL(req->out.hdr, HDR_TID, req->tcon->tid);
+
+ smbsrv_send_reply(req);
+}
+
+
+/****************************************************************************
+ Reply to a tcon and X.
+****************************************************************************/
+void smbsrv_reply_tcon_and_X(struct smbsrv_request *req)
+{
+ NTSTATUS status;
+ union smb_tcon con;
+ uint8_t *p;
+ uint16_t passlen;
+
+ con.tconx.level = RAW_TCON_TCONX;
+
+ /* parse request */
+ SMBSRV_CHECK_WCT(req, 4);
+
+ con.tconx.in.flags = SVAL(req->in.vwv, VWV(2));
+ passlen = SVAL(req->in.vwv, VWV(3));
+
+ p = req->in.data;
+
+ if (!req_pull_blob(&req->in.bufinfo, p, passlen, &con.tconx.in.password)) {
+ smbsrv_send_error(req, NT_STATUS_ILL_FORMED_PASSWORD);
+ return;
+ }
+ p += passlen;
+
+ p += req_pull_string(&req->in.bufinfo, &con.tconx.in.path, p, -1, STR_TERMINATE);
+ p += req_pull_string(&req->in.bufinfo, &con.tconx.in.device, p, -1, STR_ASCII);
+
+ if (!con.tconx.in.path || !con.tconx.in.device) {
+ smbsrv_send_error(req, NT_STATUS_BAD_DEVICE_TYPE);
+ return;
+ }
+
+ /* call backend */
+ status = smbsrv_tcon_backend(req, &con);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ smbsrv_send_error(req, status);
+ return;
+ }
+
+ /* construct reply - two variants */
+ if (req->smb_conn->negotiate.protocol < PROTOCOL_NT1) {
+ smbsrv_setup_reply(req, 2, 0);
+
+ SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+ SSVAL(req->out.vwv, VWV(1), 0);
+
+ req_push_str(req, NULL, con.tconx.out.dev_type, -1, STR_TERMINATE|STR_ASCII);
+ } else {
+ smbsrv_setup_reply(req, 3, 0);
+
+ SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+ SSVAL(req->out.vwv, VWV(1), 0);
+ SSVAL(req->out.vwv, VWV(2), con.tconx.out.options);
+
+ req_push_str(req, NULL, con.tconx.out.dev_type, -1, STR_TERMINATE|STR_ASCII);
+ req_push_str(req, NULL, con.tconx.out.fs_type, -1, STR_TERMINATE);
+ }
+
+ /* set the incoming and outgoing tid to the just created one */
+ SSVAL(req->in.hdr, HDR_TID, con.tconx.out.tid);
+ SSVAL(req->out.hdr,HDR_TID, con.tconx.out.tid);
+
+ smbsrv_chain_reply(req);
+}
+
+
+/****************************************************************************
+ Reply to an unknown request
+****************************************************************************/
+void smbsrv_reply_unknown(struct smbsrv_request *req)
+{
+ int type;
+
+ type = CVAL(req->in.hdr, HDR_COM);
+
+ DEBUG(0,("unknown command type %d (0x%X)\n", type, type));
+
+ smbsrv_send_error(req, NT_STATUS_DOS(ERRSRV, ERRunknownsmb));
+}
+
+
+/****************************************************************************
+ Reply to an ioctl (async reply)
+****************************************************************************/
+static void reply_ioctl_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+ union smb_ioctl *io;
+
+ SMBSRV_CHECK_ASYNC_STATUS(io, union smb_ioctl);
+
+ /* the +1 is for nicer alignment */
+ smbsrv_setup_reply(req, 8, io->ioctl.out.blob.length+1);
+ SSVAL(req->out.vwv, VWV(1), io->ioctl.out.blob.length);
+ SSVAL(req->out.vwv, VWV(5), io->ioctl.out.blob.length);
+ SSVAL(req->out.vwv, VWV(6), PTR_DIFF(req->out.data, req->out.hdr) + 1);
+
+ memcpy(req->out.data+1, io->ioctl.out.blob.data, io->ioctl.out.blob.length);
+
+ smbsrv_send_reply(req);
+}
+
+/****************************************************************************
+ Reply to an ioctl.
+****************************************************************************/
+void smbsrv_reply_ioctl(struct smbsrv_request *req)
+{
+ union smb_ioctl *io;
+
+ /* parse request */
+ SMBSRV_CHECK_WCT(req, 3);
+ SMBSRV_TALLOC_IO_PTR(io, union smb_ioctl);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_ioctl_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ io->ioctl.level = RAW_IOCTL_IOCTL;
+ io->ioctl.in.file.ntvfs = smbsrv_pull_fnum(req, req->in.vwv, VWV(0));
+ io->ioctl.in.request = IVAL(req->in.vwv, VWV(1));
+
+ SMBSRV_CHECK_FILE_HANDLE_ERROR(io->ioctl.in.file.ntvfs,
+ NT_STATUS_DOS(ERRSRV, ERRerror));
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_ioctl(req->ntvfs, io));
+}
+
+
+/****************************************************************************
+ Reply to a chkpth.
+****************************************************************************/
+void smbsrv_reply_chkpth(struct smbsrv_request *req)
+{
+ union smb_chkpath *io;
+
+ SMBSRV_TALLOC_IO_PTR(io, union smb_chkpath);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_simple_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ req_pull_ascii4(&req->in.bufinfo, &io->chkpath.in.path, req->in.data, STR_TERMINATE);
+
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_chkpath(req->ntvfs, io));
+}
+
+/****************************************************************************
+ Reply to a getatr (async reply)
+****************************************************************************/
+static void reply_getatr_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+ union smb_fileinfo *st;
+
+ SMBSRV_CHECK_ASYNC_STATUS(st, union smb_fileinfo);
+
+ /* construct reply */
+ smbsrv_setup_reply(req, 10, 0);
+
+ SSVAL(req->out.vwv, VWV(0), st->getattr.out.attrib);
+ srv_push_dos_date3(req->smb_conn, req->out.vwv, VWV(1), st->getattr.out.write_time);
+ SIVAL(req->out.vwv, VWV(3), st->getattr.out.size);
+
+ SMBSRV_VWV_RESERVED(5, 5);
+
+ smbsrv_send_reply(req);
+}
+
+
+/****************************************************************************
+ Reply to a getatr.
+****************************************************************************/
+void smbsrv_reply_getatr(struct smbsrv_request *req)
+{
+ union smb_fileinfo *st;
+
+ SMBSRV_TALLOC_IO_PTR(st, union smb_fileinfo);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_getatr_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ st->getattr.level = RAW_FILEINFO_GETATTR;
+
+ /* parse request */
+ req_pull_ascii4(&req->in.bufinfo, &st->getattr.in.file.path, req->in.data, STR_TERMINATE);
+ if (!st->getattr.in.file.path) {
+ smbsrv_send_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ return;
+ }
+
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_qpathinfo(req->ntvfs, st));
+}
+
+
+/****************************************************************************
+ Reply to a setatr.
+****************************************************************************/
+void smbsrv_reply_setatr(struct smbsrv_request *req)
+{
+ union smb_setfileinfo *st;
+
+ /* parse request */
+ SMBSRV_CHECK_WCT(req, 8);
+ SMBSRV_TALLOC_IO_PTR(st, union smb_setfileinfo);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_simple_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ st->setattr.level = RAW_SFILEINFO_SETATTR;
+ st->setattr.in.attrib = SVAL(req->in.vwv, VWV(0));
+ st->setattr.in.write_time = srv_pull_dos_date3(req->smb_conn, req->in.vwv + VWV(1));
+
+ req_pull_ascii4(&req->in.bufinfo, &st->setattr.in.file.path, req->in.data, STR_TERMINATE);
+
+ if (!st->setattr.in.file.path) {
+ smbsrv_send_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ return;
+ }
+
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_setpathinfo(req->ntvfs, st));
+}
+
+
+/****************************************************************************
+ Reply to a dskattr (async reply)
+****************************************************************************/
+static void reply_dskattr_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+ union smb_fsinfo *fs;
+
+ SMBSRV_CHECK_ASYNC_STATUS(fs, union smb_fsinfo);
+
+ /* construct reply */
+ smbsrv_setup_reply(req, 5, 0);
+
+ SSVAL(req->out.vwv, VWV(0), fs->dskattr.out.units_total);
+ SSVAL(req->out.vwv, VWV(1), fs->dskattr.out.blocks_per_unit);
+ SSVAL(req->out.vwv, VWV(2), fs->dskattr.out.block_size);
+ SSVAL(req->out.vwv, VWV(3), fs->dskattr.out.units_free);
+
+ SMBSRV_VWV_RESERVED(4, 1);
+
+ smbsrv_send_reply(req);
+}
+
+
+/****************************************************************************
+ Reply to a dskattr.
+****************************************************************************/
+void smbsrv_reply_dskattr(struct smbsrv_request *req)
+{
+ union smb_fsinfo *fs;
+
+ SMBSRV_TALLOC_IO_PTR(fs, union smb_fsinfo);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_dskattr_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ fs->dskattr.level = RAW_QFS_DSKATTR;
+
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_fsinfo(req->ntvfs, fs));
+}
+
+
+/****************************************************************************
+ Reply to an open (async reply)
+****************************************************************************/
+static void reply_open_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+ union smb_open *oi;
+
+ SMBSRV_CHECK_ASYNC_STATUS(oi, union smb_open);
+
+ /* construct reply */
+ smbsrv_setup_reply(req, 7, 0);
+
+ smbsrv_push_fnum(req->out.vwv, VWV(0), oi->openold.out.file.ntvfs);
+ SSVAL(req->out.vwv, VWV(1), oi->openold.out.attrib);
+ srv_push_dos_date3(req->smb_conn, req->out.vwv, VWV(2), oi->openold.out.write_time);
+ SIVAL(req->out.vwv, VWV(4), oi->openold.out.size);
+ SSVAL(req->out.vwv, VWV(6), oi->openold.out.rmode);
+
+ smbsrv_send_reply(req);
+}
+
+/****************************************************************************
+ Reply to an open.
+****************************************************************************/
+void smbsrv_reply_open(struct smbsrv_request *req)
+{
+ union smb_open *oi;
+
+ /* parse request */
+ SMBSRV_CHECK_WCT(req, 2);
+ SMBSRV_TALLOC_IO_PTR(oi, union smb_open);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_open_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ oi->openold.level = RAW_OPEN_OPEN;
+ oi->openold.in.open_mode = SVAL(req->in.vwv, VWV(0));
+ oi->openold.in.search_attrs = SVAL(req->in.vwv, VWV(1));
+
+ req_pull_ascii4(&req->in.bufinfo, &oi->openold.in.fname, req->in.data, STR_TERMINATE);
+
+ if (!oi->openold.in.fname) {
+ smbsrv_send_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ return;
+ }
+
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_open(req->ntvfs, oi));
+}
+
+
+/****************************************************************************
+ Reply to an open and X (async reply)
+****************************************************************************/
+static void reply_open_and_X_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+ union smb_open *oi;
+
+ SMBSRV_CHECK_ASYNC_STATUS(oi, union smb_open);
+
+ /* build the reply */
+ if (oi->openx.in.flags & OPENX_FLAGS_EXTENDED_RETURN) {
+ smbsrv_setup_reply(req, 19, 0);
+ } else {
+ smbsrv_setup_reply(req, 15, 0);
+ }
+
+ SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+ SSVAL(req->out.vwv, VWV(1), 0);
+ smbsrv_push_fnum(req->out.vwv, VWV(2), oi->openx.out.file.ntvfs);
+ SSVAL(req->out.vwv, VWV(3), oi->openx.out.attrib);
+ srv_push_dos_date3(req->smb_conn, req->out.vwv, VWV(4), oi->openx.out.write_time);
+ SIVAL(req->out.vwv, VWV(6), oi->openx.out.size);
+ SSVAL(req->out.vwv, VWV(8), oi->openx.out.access);
+ SSVAL(req->out.vwv, VWV(9), oi->openx.out.ftype);
+ SSVAL(req->out.vwv, VWV(10),oi->openx.out.devstate);
+ SSVAL(req->out.vwv, VWV(11),oi->openx.out.action);
+ SIVAL(req->out.vwv, VWV(12),oi->openx.out.unique_fid);
+ SSVAL(req->out.vwv, VWV(14),0); /* reserved */
+ if (oi->openx.in.flags & OPENX_FLAGS_EXTENDED_RETURN) {
+ SIVAL(req->out.vwv, VWV(15),oi->openx.out.access_mask);
+ SMBSRV_VWV_RESERVED(17, 2);
+ }
+
+ req->chained_fnum = SVAL(req->out.vwv, VWV(2));
+
+ smbsrv_chain_reply(req);
+}
+
+
+/****************************************************************************
+ Reply to an open and X.
+****************************************************************************/
+void smbsrv_reply_open_and_X(struct smbsrv_request *req)
+{
+ union smb_open *oi;
+
+ /* parse the request */
+ SMBSRV_CHECK_WCT(req, 15);
+ SMBSRV_TALLOC_IO_PTR(oi, union smb_open);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_open_and_X_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ oi->openx.level = RAW_OPEN_OPENX;
+ oi->openx.in.flags = SVAL(req->in.vwv, VWV(2));
+ oi->openx.in.open_mode = SVAL(req->in.vwv, VWV(3));
+ oi->openx.in.search_attrs = SVAL(req->in.vwv, VWV(4));
+ oi->openx.in.file_attrs = SVAL(req->in.vwv, VWV(5));
+ oi->openx.in.write_time = srv_pull_dos_date3(req->smb_conn, req->in.vwv + VWV(6));
+ oi->openx.in.open_func = SVAL(req->in.vwv, VWV(8));
+ oi->openx.in.size = IVAL(req->in.vwv, VWV(9));
+ oi->openx.in.timeout = IVAL(req->in.vwv, VWV(11));
+
+ req_pull_ascii4(&req->in.bufinfo, &oi->openx.in.fname, req->in.data, STR_TERMINATE);
+
+ if (!oi->openx.in.fname) {
+ smbsrv_send_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ return;
+ }
+
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_open(req->ntvfs, oi));
+}
+
+
+/****************************************************************************
+ Reply to a mknew or a create.
+****************************************************************************/
+static void reply_mknew_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+ union smb_open *oi;
+
+ SMBSRV_CHECK_ASYNC_STATUS(oi, union smb_open);
+
+ /* build the reply */
+ smbsrv_setup_reply(req, 1, 0);
+
+ smbsrv_push_fnum(req->out.vwv, VWV(0), oi->mknew.out.file.ntvfs);
+
+ smbsrv_send_reply(req);
+}
+
+
+/****************************************************************************
+ Reply to a mknew or a create.
+****************************************************************************/
+void smbsrv_reply_mknew(struct smbsrv_request *req)
+{
+ union smb_open *oi;
+
+ /* parse the request */
+ SMBSRV_CHECK_WCT(req, 3);
+ SMBSRV_TALLOC_IO_PTR(oi, union smb_open);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_mknew_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ if (CVAL(req->in.hdr, HDR_COM) == SMBmknew) {
+ oi->mknew.level = RAW_OPEN_MKNEW;
+ } else {
+ oi->mknew.level = RAW_OPEN_CREATE;
+ }
+ oi->mknew.in.attrib = SVAL(req->in.vwv, VWV(0));
+ oi->mknew.in.write_time = srv_pull_dos_date3(req->smb_conn, req->in.vwv + VWV(1));
+
+ req_pull_ascii4(&req->in.bufinfo, &oi->mknew.in.fname, req->in.data, STR_TERMINATE);
+
+ if (!oi->mknew.in.fname) {
+ smbsrv_send_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ return;
+ }
+
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_open(req->ntvfs, oi));
+}
+
+/****************************************************************************
+ Reply to a create temporary file (async reply)
+****************************************************************************/
+static void reply_ctemp_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+ union smb_open *oi;
+
+ SMBSRV_CHECK_ASYNC_STATUS(oi, union smb_open);
+
+ /* build the reply */
+ smbsrv_setup_reply(req, 1, 0);
+
+ smbsrv_push_fnum(req->out.vwv, VWV(0), oi->ctemp.out.file.ntvfs);
+
+ /* the returned filename is relative to the directory */
+ req_push_str(req, NULL, oi->ctemp.out.name, -1, STR_TERMINATE | STR_ASCII);
+
+ smbsrv_send_reply(req);
+}
+
+/****************************************************************************
+ Reply to a create temporary file.
+****************************************************************************/
+void smbsrv_reply_ctemp(struct smbsrv_request *req)
+{
+ union smb_open *oi;
+
+ /* parse the request */
+ SMBSRV_CHECK_WCT(req, 3);
+ SMBSRV_TALLOC_IO_PTR(oi, union smb_open);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_ctemp_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ oi->ctemp.level = RAW_OPEN_CTEMP;
+ oi->ctemp.in.attrib = SVAL(req->in.vwv, VWV(0));
+ oi->ctemp.in.write_time = srv_pull_dos_date3(req->smb_conn, req->in.vwv + VWV(1));
+
+ /* the filename is actually a directory name, the server provides a filename
+ in that directory */
+ req_pull_ascii4(&req->in.bufinfo, &oi->ctemp.in.directory, req->in.data, STR_TERMINATE);
+
+ if (!oi->ctemp.in.directory) {
+ smbsrv_send_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ return;
+ }
+
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_open(req->ntvfs, oi));
+}
+
+
+/****************************************************************************
+ Reply to a unlink
+****************************************************************************/
+void smbsrv_reply_unlink(struct smbsrv_request *req)
+{
+ union smb_unlink *unl;
+
+ /* parse the request */
+ SMBSRV_CHECK_WCT(req, 1);
+ SMBSRV_TALLOC_IO_PTR(unl, union smb_unlink);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_simple_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ unl->unlink.in.attrib = SVAL(req->in.vwv, VWV(0));
+
+ req_pull_ascii4(&req->in.bufinfo, &unl->unlink.in.pattern, req->in.data, STR_TERMINATE);
+
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_unlink(req->ntvfs, unl));
+}
+
+
+/****************************************************************************
+ Reply to a readbraw (core+ protocol).
+ this is a strange packet because it doesn't use a standard SMB header in the reply,
+ only the 4 byte NBT header
+ This command must be replied to synchronously
+****************************************************************************/
+void smbsrv_reply_readbraw(struct smbsrv_request *req)
+{
+ NTSTATUS status;
+ union smb_read io;
+
+ io.readbraw.level = RAW_READ_READBRAW;
+
+ /* there are two variants, one with 10 and one with 8 command words */
+ if (req->in.wct < 8) {
+ goto failed;
+ }
+
+ io.readbraw.in.file.ntvfs = smbsrv_pull_fnum(req, req->in.vwv, VWV(0));
+ io.readbraw.in.offset = IVAL(req->in.vwv, VWV(1));
+ io.readbraw.in.maxcnt = SVAL(req->in.vwv, VWV(3));
+ io.readbraw.in.mincnt = SVAL(req->in.vwv, VWV(4));
+ io.readbraw.in.timeout = IVAL(req->in.vwv, VWV(5));
+
+ if (!io.readbraw.in.file.ntvfs) {
+ goto failed;
+ }
+
+ /* the 64 bit variant */
+ if (req->in.wct == 10) {
+ uint32_t offset_high = IVAL(req->in.vwv, VWV(8));
+ io.readbraw.in.offset |= (((off_t)offset_high) << 32);
+ }
+
+ /* before calling the backend we setup the raw buffer. This
+ * saves a copy later */
+ req->out.size = io.readbraw.in.maxcnt + NBT_HDR_SIZE;
+ req->out.buffer = talloc_size(req, req->out.size);
+ if (req->out.buffer == NULL) {
+ goto failed;
+ }
+ SIVAL(req->out.buffer, 0, 0); /* init NBT header */
+
+ /* tell the backend where to put the data */
+ io.readbraw.out.data = req->out.buffer + NBT_HDR_SIZE;
+
+ /* prepare the ntvfs request */
+ req->ntvfs = ntvfs_request_create(req->tcon->ntvfs, req,
+ req->session->session_info,
+ SVAL(req->in.hdr,HDR_PID),
+ req->request_time,
+ req, NULL, 0);
+ if (!req->ntvfs) {
+ goto failed;
+ }
+
+ /* call the backend */
+ status = ntvfs_read(req->ntvfs, &io);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+
+ req->out.size = io.readbraw.out.nread + NBT_HDR_SIZE;
+
+ smbsrv_send_reply_nosign(req);
+ return;
+
+failed:
+ /* any failure in readbraw is equivalent to reading zero bytes */
+ req->out.size = 4;
+ req->out.buffer = talloc_size(req, req->out.size);
+ SIVAL(req->out.buffer, 0, 0); /* init NBT header */
+
+ smbsrv_send_reply_nosign(req);
+}
+
+
+/****************************************************************************
+ Reply to a lockread (async reply)
+****************************************************************************/
+static void reply_lockread_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+ union smb_read *io;
+
+ SMBSRV_CHECK_ASYNC_STATUS(io, union smb_read);
+
+ /* trim packet */
+ io->lockread.out.nread = MIN(io->lockread.out.nread,
+ req_max_data(req) - 3);
+ req_grow_data(req, 3 + io->lockread.out.nread);
+
+ /* construct reply */
+ SSVAL(req->out.vwv, VWV(0), io->lockread.out.nread);
+ SMBSRV_VWV_RESERVED(1, 4);
+
+ SCVAL(req->out.data, 0, SMB_DATA_BLOCK);
+ SSVAL(req->out.data, 1, io->lockread.out.nread);
+
+ smbsrv_send_reply(req);
+}
+
+
+/****************************************************************************
+ Reply to a lockread (core+ protocol).
+ note that the lock is a write lock, not a read lock!
+****************************************************************************/
+void smbsrv_reply_lockread(struct smbsrv_request *req)
+{
+ union smb_read *io;
+
+ /* parse request */
+ SMBSRV_CHECK_WCT(req, 5);
+ SMBSRV_TALLOC_IO_PTR(io, union smb_read);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_lockread_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ io->lockread.level = RAW_READ_LOCKREAD;
+ io->lockread.in.file.ntvfs= smbsrv_pull_fnum(req, req->in.vwv, VWV(0));
+ io->lockread.in.count = SVAL(req->in.vwv, VWV(1));
+ io->lockread.in.offset = IVAL(req->in.vwv, VWV(2));
+ io->lockread.in.remaining = SVAL(req->in.vwv, VWV(4));
+
+ /* setup the reply packet assuming the maximum possible read */
+ smbsrv_setup_reply(req, 5, 3 + io->lockread.in.count);
+
+ /* tell the backend where to put the data */
+ io->lockread.out.data = req->out.data + 3;
+
+ SMBSRV_CHECK_FILE_HANDLE(io->lockread.in.file.ntvfs);
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_read(req->ntvfs, io));
+}
+
+
+
+/****************************************************************************
+ Reply to a read (async reply)
+****************************************************************************/
+static void reply_read_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+ union smb_read *io;
+
+ SMBSRV_CHECK_ASYNC_STATUS(io, union smb_read);
+
+ /* trim packet */
+ io->read.out.nread = MIN(io->read.out.nread,
+ req_max_data(req) - 3);
+ req_grow_data(req, 3 + io->read.out.nread);
+
+ /* construct reply */
+ SSVAL(req->out.vwv, VWV(0), io->read.out.nread);
+ SMBSRV_VWV_RESERVED(1, 4);
+
+ SCVAL(req->out.data, 0, SMB_DATA_BLOCK);
+ SSVAL(req->out.data, 1, io->read.out.nread);
+
+ smbsrv_send_reply(req);
+}
+
+/****************************************************************************
+ Reply to a read.
+****************************************************************************/
+void smbsrv_reply_read(struct smbsrv_request *req)
+{
+ union smb_read *io;
+
+ /* parse request */
+ SMBSRV_CHECK_WCT(req, 5);
+ SMBSRV_TALLOC_IO_PTR(io, union smb_read);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_read_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ io->read.level = RAW_READ_READ;
+ io->read.in.file.ntvfs = smbsrv_pull_fnum(req, req->in.vwv, VWV(0));
+ io->read.in.count = SVAL(req->in.vwv, VWV(1));
+ io->read.in.offset = IVAL(req->in.vwv, VWV(2));
+ io->read.in.remaining = SVAL(req->in.vwv, VWV(4));
+
+ /* setup the reply packet assuming the maximum possible read */
+ smbsrv_setup_reply(req, 5, 3 + io->read.in.count);
+
+ /* tell the backend where to put the data */
+ io->read.out.data = req->out.data + 3;
+
+ SMBSRV_CHECK_FILE_HANDLE(io->read.in.file.ntvfs);
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_read(req->ntvfs, io));
+}
+
+/****************************************************************************
+ Reply to a read and X (async reply)
+****************************************************************************/
+static void reply_read_and_X_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+ union smb_read *io;
+
+ SMBSRV_CHECK_ASYNC_STATUS(io, union smb_read);
+
+ /* readx reply packets can be over-sized */
+ req->control_flags |= SMBSRV_REQ_CONTROL_LARGE;
+ if (io->readx.in.maxcnt != 0xFFFF &&
+ io->readx.in.mincnt != 0xFFFF) {
+ req_grow_data(req, 1 + io->readx.out.nread);
+ SCVAL(req->out.data, 0, 0); /* padding */
+ } else {
+ req_grow_data(req, io->readx.out.nread);
+ }
+
+ /* construct reply */
+ SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+ SSVAL(req->out.vwv, VWV(1), 0);
+ SSVAL(req->out.vwv, VWV(2), io->readx.out.remaining);
+ SSVAL(req->out.vwv, VWV(3), io->readx.out.compaction_mode);
+ SMBSRV_VWV_RESERVED(4, 1);
+ SSVAL(req->out.vwv, VWV(5), io->readx.out.nread);
+ SSVAL(req->out.vwv, VWV(6), PTR_DIFF(io->readx.out.data, req->out.hdr));
+ SMBSRV_VWV_RESERVED(7, 5);
+
+ smbsrv_chain_reply(req);
+}
+
+/****************************************************************************
+ Reply to a read and X.
+****************************************************************************/
+void smbsrv_reply_read_and_X(struct smbsrv_request *req)
+{
+ union smb_read *io;
+
+ /* parse request */
+ if (req->in.wct != 12) {
+ SMBSRV_CHECK_WCT(req, 10);
+ }
+
+ SMBSRV_TALLOC_IO_PTR(io, union smb_read);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_read_and_X_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ io->readx.level = RAW_READ_READX;
+ io->readx.in.file.ntvfs = smbsrv_pull_fnum(req, req->in.vwv, VWV(2));
+ io->readx.in.offset = IVAL(req->in.vwv, VWV(3));
+ io->readx.in.maxcnt = SVAL(req->in.vwv, VWV(5));
+ io->readx.in.mincnt = SVAL(req->in.vwv, VWV(6));
+ io->readx.in.remaining = SVAL(req->in.vwv, VWV(9));
+ if (req->flags2 & FLAGS2_READ_PERMIT_EXECUTE) {
+ io->readx.in.read_for_execute = true;
+ } else {
+ io->readx.in.read_for_execute = false;
+ }
+
+ if (req->smb_conn->negotiate.client_caps & CAP_LARGE_READX) {
+ uint32_t high_part = IVAL(req->in.vwv, VWV(7));
+ if (high_part == 1) {
+ io->readx.in.maxcnt |= high_part << 16;
+ }
+ }
+
+ /* the 64 bit variant */
+ if (req->in.wct == 12) {
+ uint32_t offset_high = IVAL(req->in.vwv, VWV(10));
+ io->readx.in.offset |= (((uint64_t)offset_high) << 32);
+ }
+
+ /* setup the reply packet assuming the maximum possible read */
+ smbsrv_setup_reply(req, 12, 1 + io->readx.in.maxcnt);
+
+ /* tell the backend where to put the data. Notice the pad byte. */
+ if (io->readx.in.maxcnt != 0xFFFF &&
+ io->readx.in.mincnt != 0xFFFF) {
+ io->readx.out.data = req->out.data + 1;
+ } else {
+ io->readx.out.data = req->out.data;
+ }
+
+ SMBSRV_CHECK_FILE_HANDLE(io->readx.in.file.ntvfs);
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_read(req->ntvfs, io));
+}
+
+
+/****************************************************************************
+ Reply to a writebraw (core+ or LANMAN1.0 protocol).
+****************************************************************************/
+void smbsrv_reply_writebraw(struct smbsrv_request *req)
+{
+ smbsrv_send_error(req, NT_STATUS_DOS(ERRSRV, ERRuseSTD));
+}
+
+
+/****************************************************************************
+ Reply to a writeunlock (async reply)
+****************************************************************************/
+static void reply_writeunlock_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+ union smb_write *io;
+
+ SMBSRV_CHECK_ASYNC_STATUS(io, union smb_write);
+
+ /* construct reply */
+ smbsrv_setup_reply(req, 1, 0);
+
+ SSVAL(req->out.vwv, VWV(0), io->writeunlock.out.nwritten);
+
+ smbsrv_send_reply(req);
+}
+
+/****************************************************************************
+ Reply to a writeunlock (core+).
+****************************************************************************/
+void smbsrv_reply_writeunlock(struct smbsrv_request *req)
+{
+ union smb_write *io;
+
+ SMBSRV_CHECK_WCT(req, 5);
+ SMBSRV_TALLOC_IO_PTR(io, union smb_write);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_writeunlock_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ io->writeunlock.level = RAW_WRITE_WRITEUNLOCK;
+ io->writeunlock.in.file.ntvfs = smbsrv_pull_fnum(req, req->in.vwv, VWV(0));
+ io->writeunlock.in.count = SVAL(req->in.vwv, VWV(1));
+ io->writeunlock.in.offset = IVAL(req->in.vwv, VWV(2));
+ io->writeunlock.in.remaining = SVAL(req->in.vwv, VWV(4));
+ io->writeunlock.in.data = req->in.data + 3;
+
+ /* make sure they gave us the data they promised */
+ if (io->writeunlock.in.count+3 > req->in.data_size) {
+ smbsrv_send_error(req, NT_STATUS_FOOBAR);
+ return;
+ }
+
+ /* make sure the data block is big enough */
+ if (SVAL(req->in.data, 1) < io->writeunlock.in.count) {
+ smbsrv_send_error(req, NT_STATUS_FOOBAR);
+ return;
+ }
+
+ SMBSRV_CHECK_FILE_HANDLE(io->writeunlock.in.file.ntvfs);
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_write(req->ntvfs, io));
+}
+
+
+
+/****************************************************************************
+ Reply to a write (async reply)
+****************************************************************************/
+static void reply_write_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+ union smb_write *io;
+
+ SMBSRV_CHECK_ASYNC_STATUS(io, union smb_write);
+
+ /* construct reply */
+ smbsrv_setup_reply(req, 1, 0);
+
+ SSVAL(req->out.vwv, VWV(0), io->write.out.nwritten);
+
+ smbsrv_send_reply(req);
+}
+
+/****************************************************************************
+ Reply to a write
+****************************************************************************/
+void smbsrv_reply_write(struct smbsrv_request *req)
+{
+ union smb_write *io;
+
+ SMBSRV_CHECK_WCT(req, 5);
+ SMBSRV_TALLOC_IO_PTR(io, union smb_write);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_write_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ io->write.level = RAW_WRITE_WRITE;
+ io->write.in.file.ntvfs = smbsrv_pull_fnum(req, req->in.vwv, VWV(0));
+ io->write.in.count = SVAL(req->in.vwv, VWV(1));
+ io->write.in.offset = IVAL(req->in.vwv, VWV(2));
+ io->write.in.remaining = SVAL(req->in.vwv, VWV(4));
+ io->write.in.data = req->in.data + 3;
+
+ /* make sure they gave us the data they promised */
+ if (req_data_oob(&req->in.bufinfo, io->write.in.data, io->write.in.count)) {
+ smbsrv_send_error(req, NT_STATUS_FOOBAR);
+ return;
+ }
+
+ /* make sure the data block is big enough */
+ if (SVAL(req->in.data, 1) < io->write.in.count) {
+ smbsrv_send_error(req, NT_STATUS_FOOBAR);
+ return;
+ }
+
+ SMBSRV_CHECK_FILE_HANDLE(io->write.in.file.ntvfs);
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_write(req->ntvfs, io));
+}
+
+
+/****************************************************************************
+ Reply to a write and X (async reply)
+****************************************************************************/
+static void reply_write_and_X_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+ union smb_write *io;
+
+ SMBSRV_CHECK_ASYNC_STATUS(io, union smb_write);
+
+ /* construct reply */
+ smbsrv_setup_reply(req, 6, 0);
+
+ SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+ SSVAL(req->out.vwv, VWV(1), 0);
+ SSVAL(req->out.vwv, VWV(2), io->writex.out.nwritten & 0xFFFF);
+ SSVAL(req->out.vwv, VWV(3), io->writex.out.remaining);
+ SSVAL(req->out.vwv, VWV(4), io->writex.out.nwritten >> 16);
+ SMBSRV_VWV_RESERVED(5, 1);
+
+ smbsrv_chain_reply(req);
+}
+
+/****************************************************************************
+ Reply to a write and X.
+****************************************************************************/
+void smbsrv_reply_write_and_X(struct smbsrv_request *req)
+{
+ union smb_write *io;
+
+ if (req->in.wct != 14) {
+ SMBSRV_CHECK_WCT(req, 12);
+ }
+
+ SMBSRV_TALLOC_IO_PTR(io, union smb_write);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_write_and_X_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ io->writex.level = RAW_WRITE_WRITEX;
+ io->writex.in.file.ntvfs= smbsrv_pull_fnum(req, req->in.vwv, VWV(2));
+ io->writex.in.offset = IVAL(req->in.vwv, VWV(3));
+ io->writex.in.wmode = SVAL(req->in.vwv, VWV(7));
+ io->writex.in.remaining = SVAL(req->in.vwv, VWV(8));
+ io->writex.in.count = SVAL(req->in.vwv, VWV(10));
+ io->writex.in.data = req->in.hdr + SVAL(req->in.vwv, VWV(11));
+
+ if (req->in.wct == 14) {
+ uint32_t offset_high = IVAL(req->in.vwv, VWV(12));
+ uint16_t count_high = SVAL(req->in.vwv, VWV(9));
+ io->writex.in.offset |= (((uint64_t)offset_high) << 32);
+ io->writex.in.count |= ((uint32_t)count_high) << 16;
+ }
+
+ /* make sure the data is in bounds */
+ if (req_data_oob(&req->in.bufinfo, io->writex.in.data, io->writex.in.count)) {
+ smbsrv_send_error(req, NT_STATUS_FOOBAR);
+ return;
+ }
+
+ SMBSRV_CHECK_FILE_HANDLE(io->writex.in.file.ntvfs);
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_write(req->ntvfs, io));
+}
+
+
+/****************************************************************************
+ Reply to a lseek (async reply)
+****************************************************************************/
+static void reply_lseek_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+ union smb_seek *io;
+
+ SMBSRV_CHECK_ASYNC_STATUS(io, union smb_seek);
+
+ /* construct reply */
+ smbsrv_setup_reply(req, 2, 0);
+
+ SIVALS(req->out.vwv, VWV(0), io->lseek.out.offset);
+
+ smbsrv_send_reply(req);
+}
+
+/****************************************************************************
+ Reply to a lseek.
+****************************************************************************/
+void smbsrv_reply_lseek(struct smbsrv_request *req)
+{
+ union smb_seek *io;
+
+ SMBSRV_CHECK_WCT(req, 4);
+ SMBSRV_TALLOC_IO_PTR(io, union smb_seek);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_lseek_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ io->lseek.in.file.ntvfs = smbsrv_pull_fnum(req, req->in.vwv, VWV(0));
+ io->lseek.in.mode = SVAL(req->in.vwv, VWV(1));
+ io->lseek.in.offset = IVALS(req->in.vwv, VWV(2));
+
+ SMBSRV_CHECK_FILE_HANDLE(io->lseek.in.file.ntvfs);
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_seek(req->ntvfs, io));
+}
+
+/****************************************************************************
+ Reply to a flush.
+****************************************************************************/
+void smbsrv_reply_flush(struct smbsrv_request *req)
+{
+ union smb_flush *io;
+ uint16_t fnum;
+
+ /* parse request */
+ SMBSRV_CHECK_WCT(req, 1);
+ SMBSRV_TALLOC_IO_PTR(io, union smb_flush);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_simple_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ fnum = SVAL(req->in.vwv, VWV(0));
+ if (fnum == 0xFFFF) {
+ io->flush_all.level = RAW_FLUSH_ALL;
+ } else {
+ io->flush.level = RAW_FLUSH_FLUSH;
+ io->flush.in.file.ntvfs = smbsrv_pull_fnum(req, req->in.vwv, VWV(0));
+ SMBSRV_CHECK_FILE_HANDLE(io->flush.in.file.ntvfs);
+ }
+
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_flush(req->ntvfs, io));
+}
+
+/****************************************************************************
+ Reply to a close
+
+ Note that this has to deal with closing a directory opened by NT SMB's.
+****************************************************************************/
+void smbsrv_reply_close(struct smbsrv_request *req)
+{
+ union smb_close *io;
+
+ /* parse request */
+ SMBSRV_CHECK_WCT(req, 3);
+ SMBSRV_TALLOC_IO_PTR(io, union smb_close);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_simple_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ io->close.level = RAW_CLOSE_CLOSE;
+ io->close.in.file.ntvfs = smbsrv_pull_fnum(req, req->in.vwv, VWV(0));
+ io->close.in.write_time = srv_pull_dos_date3(req->smb_conn, req->in.vwv + VWV(1));
+
+ SMBSRV_CHECK_FILE_HANDLE(io->close.in.file.ntvfs);
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_close(req->ntvfs, io));
+}
+
+
+/****************************************************************************
+ Reply to a writeclose (async reply)
+****************************************************************************/
+static void reply_writeclose_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+ union smb_write *io;
+
+ SMBSRV_CHECK_ASYNC_STATUS(io, union smb_write);
+
+ /* construct reply */
+ smbsrv_setup_reply(req, 1, 0);
+
+ SSVAL(req->out.vwv, VWV(0), io->write.out.nwritten);
+
+ smbsrv_send_reply(req);
+}
+
+/****************************************************************************
+ Reply to a writeclose (Core+ protocol).
+****************************************************************************/
+void smbsrv_reply_writeclose(struct smbsrv_request *req)
+{
+ union smb_write *io;
+
+ /* this one is pretty weird - the wct can be 6 or 12 */
+ if (req->in.wct != 12) {
+ SMBSRV_CHECK_WCT(req, 6);
+ }
+
+ SMBSRV_TALLOC_IO_PTR(io, union smb_write);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_writeclose_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ io->writeclose.level = RAW_WRITE_WRITECLOSE;
+ io->writeclose.in.file.ntvfs = smbsrv_pull_fnum(req, req->in.vwv, VWV(0));
+ io->writeclose.in.count = SVAL(req->in.vwv, VWV(1));
+ io->writeclose.in.offset = IVAL(req->in.vwv, VWV(2));
+ io->writeclose.in.mtime = srv_pull_dos_date3(req->smb_conn, req->in.vwv + VWV(4));
+ io->writeclose.in.data = req->in.data + 1;
+
+ /* make sure they gave us the data they promised */
+ if (req_data_oob(&req->in.bufinfo, io->writeclose.in.data, io->writeclose.in.count)) {
+ smbsrv_send_error(req, NT_STATUS_FOOBAR);
+ return;
+ }
+
+ SMBSRV_CHECK_FILE_HANDLE(io->writeclose.in.file.ntvfs);
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_write(req->ntvfs, io));
+}
+
+/****************************************************************************
+ Reply to a lock.
+****************************************************************************/
+void smbsrv_reply_lock(struct smbsrv_request *req)
+{
+ union smb_lock *lck;
+
+ /* parse request */
+ SMBSRV_CHECK_WCT(req, 5);
+ SMBSRV_TALLOC_IO_PTR(lck, union smb_lock);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_simple_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ lck->lock.level = RAW_LOCK_LOCK;
+ lck->lock.in.file.ntvfs = smbsrv_pull_fnum(req, req->in.vwv, VWV(0));
+ lck->lock.in.count = IVAL(req->in.vwv, VWV(1));
+ lck->lock.in.offset = IVAL(req->in.vwv, VWV(3));
+
+ SMBSRV_CHECK_FILE_HANDLE(lck->lock.in.file.ntvfs);
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_lock(req->ntvfs, lck));
+}
+
+
+/****************************************************************************
+ Reply to a unlock.
+****************************************************************************/
+void smbsrv_reply_unlock(struct smbsrv_request *req)
+{
+ union smb_lock *lck;
+
+ /* parse request */
+ SMBSRV_CHECK_WCT(req, 5);
+ SMBSRV_TALLOC_IO_PTR(lck, union smb_lock);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_simple_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ lck->unlock.level = RAW_LOCK_UNLOCK;
+ lck->unlock.in.file.ntvfs = smbsrv_pull_fnum(req, req->in.vwv, VWV(0));
+ lck->unlock.in.count = IVAL(req->in.vwv, VWV(1));
+ lck->unlock.in.offset = IVAL(req->in.vwv, VWV(3));
+
+ SMBSRV_CHECK_FILE_HANDLE(lck->unlock.in.file.ntvfs);
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_lock(req->ntvfs, lck));
+}
+
+
+/****************************************************************************
+ Reply to a tdis.
+****************************************************************************/
+void smbsrv_reply_tdis(struct smbsrv_request *req)
+{
+ struct smbsrv_handle *h, *nh;
+
+ SMBSRV_CHECK_WCT(req, 0);
+
+ /*
+ * TODO: cancel all pending requests on this tcon
+ */
+
+ /*
+ * close all handles on this tcon
+ */
+ for (h=req->tcon->handles.list; h; h=nh) {
+ nh = h->next;
+ talloc_free(h);
+ }
+
+ /* finally destroy the tcon */
+ talloc_free(req->tcon);
+ req->tcon = NULL;
+
+ smbsrv_setup_reply(req, 0, 0);
+ smbsrv_send_reply(req);
+}
+
+
+/****************************************************************************
+ Reply to a echo. This is one of the few calls that is handled directly (the
+ backends don't see it at all)
+****************************************************************************/
+void smbsrv_reply_echo(struct smbsrv_request *req)
+{
+ uint16_t count;
+ int i;
+
+ SMBSRV_CHECK_WCT(req, 1);
+
+ count = SVAL(req->in.vwv, VWV(0));
+
+ smbsrv_setup_reply(req, 1, req->in.data_size);
+
+ memcpy(req->out.data, req->in.data, req->in.data_size);
+
+ for (i=1; i <= count;i++) {
+ struct smbsrv_request *this_req;
+
+ if (i != count) {
+ this_req = smbsrv_setup_secondary_request(req);
+ } else {
+ this_req = req;
+ }
+
+ SSVAL(this_req->out.vwv, VWV(0), i);
+ smbsrv_send_reply(this_req);
+ }
+}
+
+
+
+/****************************************************************************
+ Reply to a printopen (async reply)
+****************************************************************************/
+static void reply_printopen_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+ union smb_open *oi;
+
+ SMBSRV_CHECK_ASYNC_STATUS(oi, union smb_open);
+
+ /* construct reply */
+ smbsrv_setup_reply(req, 1, 0);
+
+ smbsrv_push_fnum(req->out.vwv, VWV(0), oi->openold.out.file.ntvfs);
+
+ smbsrv_send_reply(req);
+}
+
+/****************************************************************************
+ Reply to a printopen.
+****************************************************************************/
+void smbsrv_reply_printopen(struct smbsrv_request *req)
+{
+ union smb_open *oi;
+
+ /* parse request */
+ SMBSRV_CHECK_WCT(req, 2);
+ SMBSRV_TALLOC_IO_PTR(oi, union smb_open);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_printopen_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ oi->splopen.level = RAW_OPEN_SPLOPEN;
+ oi->splopen.in.setup_length = SVAL(req->in.vwv, VWV(0));
+ oi->splopen.in.mode = SVAL(req->in.vwv, VWV(1));
+
+ req_pull_ascii4(&req->in.bufinfo, &oi->splopen.in.ident, req->in.data, STR_TERMINATE);
+
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_open(req->ntvfs, oi));
+}
+
+/****************************************************************************
+ Reply to a printclose.
+****************************************************************************/
+void smbsrv_reply_printclose(struct smbsrv_request *req)
+{
+ union smb_close *io;
+
+ /* parse request */
+ SMBSRV_CHECK_WCT(req, 3);
+ SMBSRV_TALLOC_IO_PTR(io, union smb_close);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_simple_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ io->splclose.level = RAW_CLOSE_SPLCLOSE;
+ io->splclose.in.file.ntvfs = smbsrv_pull_fnum(req, req->in.vwv, VWV(0));
+
+ SMBSRV_CHECK_FILE_HANDLE(io->splclose.in.file.ntvfs);
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_close(req->ntvfs, io));
+}
+
+/****************************************************************************
+ Reply to a printqueue.
+****************************************************************************/
+static void reply_printqueue_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+ union smb_lpq *lpq;
+ int i, maxcount;
+ const uint_t el_size = 28;
+
+ SMBSRV_CHECK_ASYNC_STATUS(lpq,union smb_lpq);
+
+ /* construct reply */
+ smbsrv_setup_reply(req, 2, 0);
+
+ /* truncate the returned list to fit in the negotiated buffer size */
+ maxcount = (req_max_data(req) - 3) / el_size;
+ if (maxcount < lpq->retq.out.count) {
+ lpq->retq.out.count = maxcount;
+ }
+
+ /* setup enough space in the reply */
+ req_grow_data(req, 3 + el_size*lpq->retq.out.count);
+
+ /* and fill it in */
+ SSVAL(req->out.vwv, VWV(0), lpq->retq.out.count);
+ SSVAL(req->out.vwv, VWV(1), lpq->retq.out.restart_idx);
+
+ SCVAL(req->out.data, 0, SMB_DATA_BLOCK);
+ SSVAL(req->out.data, 1, el_size*lpq->retq.out.count);
+
+ req->out.ptr = req->out.data + 3;
+
+ for (i=0;i<lpq->retq.out.count;i++) {
+ srv_push_dos_date2(req->smb_conn, req->out.ptr, 0 , lpq->retq.out.queue[i].time);
+ SCVAL(req->out.ptr, 4, lpq->retq.out.queue[i].status);
+ SSVAL(req->out.ptr, 5, lpq->retq.out.queue[i].job);
+ SIVAL(req->out.ptr, 7, lpq->retq.out.queue[i].size);
+ SCVAL(req->out.ptr, 11, 0); /* reserved */
+ req_push_str(req, req->out.ptr+12, lpq->retq.out.queue[i].user, 16, STR_ASCII);
+ req->out.ptr += el_size;
+ }
+
+ smbsrv_send_reply(req);
+}
+
+/****************************************************************************
+ Reply to a printqueue.
+****************************************************************************/
+void smbsrv_reply_printqueue(struct smbsrv_request *req)
+{
+ union smb_lpq *lpq;
+
+ /* parse request */
+ SMBSRV_CHECK_WCT(req, 2);
+ SMBSRV_TALLOC_IO_PTR(lpq, union smb_lpq);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_printqueue_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ lpq->retq.level = RAW_LPQ_RETQ;
+ lpq->retq.in.maxcount = SVAL(req->in.vwv, VWV(0));
+ lpq->retq.in.startidx = SVAL(req->in.vwv, VWV(1));
+
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_lpq(req->ntvfs, lpq));
+}
+
+
+/****************************************************************************
+ Reply to a printwrite.
+****************************************************************************/
+void smbsrv_reply_printwrite(struct smbsrv_request *req)
+{
+ union smb_write *io;
+
+ /* parse request */
+ SMBSRV_CHECK_WCT(req, 1);
+ SMBSRV_TALLOC_IO_PTR(io, union smb_write);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_simple_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ if (req->in.data_size < 3) {
+ smbsrv_send_error(req, NT_STATUS_FOOBAR);
+ return;
+ }
+
+ io->splwrite.level = RAW_WRITE_SPLWRITE;
+ io->splwrite.in.file.ntvfs = smbsrv_pull_fnum(req, req->in.vwv, VWV(0));
+ io->splwrite.in.count = SVAL(req->in.data, 1);
+ io->splwrite.in.data = req->in.data + 3;
+
+ /* make sure they gave us the data they promised */
+ if (req_data_oob(&req->in.bufinfo, io->splwrite.in.data, io->splwrite.in.count)) {
+ smbsrv_send_error(req, NT_STATUS_FOOBAR);
+ return;
+ }
+
+ SMBSRV_CHECK_FILE_HANDLE(io->splwrite.in.file.ntvfs);
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_write(req->ntvfs, io));
+}
+
+
+/****************************************************************************
+ Reply to a mkdir.
+****************************************************************************/
+void smbsrv_reply_mkdir(struct smbsrv_request *req)
+{
+ union smb_mkdir *io;
+
+ /* parse the request */
+ SMBSRV_CHECK_WCT(req, 0);
+ SMBSRV_TALLOC_IO_PTR(io, union smb_mkdir);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_simple_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ io->generic.level = RAW_MKDIR_MKDIR;
+ req_pull_ascii4(&req->in.bufinfo, &io->mkdir.in.path, req->in.data, STR_TERMINATE);
+
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_mkdir(req->ntvfs, io));
+}
+
+
+/****************************************************************************
+ Reply to a rmdir.
+****************************************************************************/
+void smbsrv_reply_rmdir(struct smbsrv_request *req)
+{
+ struct smb_rmdir *io;
+
+ /* parse the request */
+ SMBSRV_CHECK_WCT(req, 0);
+ SMBSRV_TALLOC_IO_PTR(io, struct smb_rmdir);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_simple_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ req_pull_ascii4(&req->in.bufinfo, &io->in.path, req->in.data, STR_TERMINATE);
+
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_rmdir(req->ntvfs, io));
+}
+
+
+/****************************************************************************
+ Reply to a mv.
+****************************************************************************/
+void smbsrv_reply_mv(struct smbsrv_request *req)
+{
+ union smb_rename *io;
+ uint8_t *p;
+
+ /* parse the request */
+ SMBSRV_CHECK_WCT(req, 1);
+ SMBSRV_TALLOC_IO_PTR(io, union smb_rename);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_simple_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ io->generic.level = RAW_RENAME_RENAME;
+ io->rename.in.attrib = SVAL(req->in.vwv, VWV(0));
+
+ p = req->in.data;
+ p += req_pull_ascii4(&req->in.bufinfo, &io->rename.in.pattern1, p, STR_TERMINATE);
+ p += req_pull_ascii4(&req->in.bufinfo, &io->rename.in.pattern2, p, STR_TERMINATE);
+
+ if (!io->rename.in.pattern1 || !io->rename.in.pattern2) {
+ smbsrv_send_error(req, NT_STATUS_FOOBAR);
+ return;
+ }
+
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_rename(req->ntvfs, io));
+}
+
+
+/****************************************************************************
+ Reply to an NT rename.
+****************************************************************************/
+void smbsrv_reply_ntrename(struct smbsrv_request *req)
+{
+ union smb_rename *io;
+ uint8_t *p;
+
+ /* parse the request */
+ SMBSRV_CHECK_WCT(req, 4);
+ SMBSRV_TALLOC_IO_PTR(io, union smb_rename);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_simple_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ io->generic.level = RAW_RENAME_NTRENAME;
+ io->ntrename.in.attrib = SVAL(req->in.vwv, VWV(0));
+ io->ntrename.in.flags = SVAL(req->in.vwv, VWV(1));
+ io->ntrename.in.cluster_size = IVAL(req->in.vwv, VWV(2));
+
+ p = req->in.data;
+ p += req_pull_ascii4(&req->in.bufinfo, &io->ntrename.in.old_name, p, STR_TERMINATE);
+ p += req_pull_ascii4(&req->in.bufinfo, &io->ntrename.in.new_name, p, STR_TERMINATE);
+
+ if (!io->ntrename.in.old_name || !io->ntrename.in.new_name) {
+ smbsrv_send_error(req, NT_STATUS_FOOBAR);
+ return;
+ }
+
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_rename(req->ntvfs, io));
+}
+
+/****************************************************************************
+ Reply to a file copy (async reply)
+****************************************************************************/
+static void reply_copy_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+ struct smb_copy *cp;
+
+ SMBSRV_CHECK_ASYNC_STATUS(cp, struct smb_copy);
+
+ /* build the reply */
+ smbsrv_setup_reply(req, 1, 0);
+
+ SSVAL(req->out.vwv, VWV(0), cp->out.count);
+
+ smbsrv_send_reply(req);
+}
+
+/****************************************************************************
+ Reply to a file copy.
+****************************************************************************/
+void smbsrv_reply_copy(struct smbsrv_request *req)
+{
+ struct smb_copy *cp;
+ uint8_t *p;
+
+ /* parse request */
+ SMBSRV_CHECK_WCT(req, 3);
+ SMBSRV_TALLOC_IO_PTR(cp, struct smb_copy);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_copy_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ cp->in.tid2 = SVAL(req->in.vwv, VWV(0));
+ cp->in.ofun = SVAL(req->in.vwv, VWV(1));
+ cp->in.flags = SVAL(req->in.vwv, VWV(2));
+
+ p = req->in.data;
+ p += req_pull_ascii4(&req->in.bufinfo, &cp->in.path1, p, STR_TERMINATE);
+ p += req_pull_ascii4(&req->in.bufinfo, &cp->in.path2, p, STR_TERMINATE);
+
+ if (!cp->in.path1 || !cp->in.path2) {
+ smbsrv_send_error(req, NT_STATUS_FOOBAR);
+ return;
+ }
+
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_copy(req->ntvfs, cp));
+}
+
+/****************************************************************************
+ Reply to a lockingX request (async send)
+****************************************************************************/
+static void reply_lockingX_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+ union smb_lock *lck;
+
+ SMBSRV_CHECK_ASYNC_STATUS(lck, union smb_lock);
+
+ /* if it was an oplock break ack then we only send a reply if
+ there was an error */
+ if (lck->lockx.in.ulock_cnt + lck->lockx.in.lock_cnt == 0) {
+ talloc_free(req);
+ return;
+ }
+
+ /* construct reply */
+ smbsrv_setup_reply(req, 2, 0);
+
+ SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+ SSVAL(req->out.vwv, VWV(1), 0);
+
+ smbsrv_chain_reply(req);
+}
+
+
+/****************************************************************************
+ Reply to a lockingX request.
+****************************************************************************/
+void smbsrv_reply_lockingX(struct smbsrv_request *req)
+{
+ union smb_lock *lck;
+ uint_t total_locks, i;
+ uint_t lck_size;
+ uint8_t *p;
+
+ /* parse request */
+ SMBSRV_CHECK_WCT(req, 8);
+ SMBSRV_TALLOC_IO_PTR(lck, union smb_lock);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_lockingX_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ lck->lockx.level = RAW_LOCK_LOCKX;
+ lck->lockx.in.file.ntvfs= smbsrv_pull_fnum(req, req->in.vwv, VWV(2));
+ lck->lockx.in.mode = SVAL(req->in.vwv, VWV(3));
+ lck->lockx.in.timeout = IVAL(req->in.vwv, VWV(4));
+ lck->lockx.in.ulock_cnt = SVAL(req->in.vwv, VWV(6));
+ lck->lockx.in.lock_cnt = SVAL(req->in.vwv, VWV(7));
+
+ total_locks = lck->lockx.in.ulock_cnt + lck->lockx.in.lock_cnt;
+
+ /* there are two variants, one with 64 bit offsets and counts */
+ if (lck->lockx.in.mode & LOCKING_ANDX_LARGE_FILES) {
+ lck_size = 20;
+ } else {
+ lck_size = 10;
+ }
+
+ /* make sure we got the promised data */
+ if (req_data_oob(&req->in.bufinfo, req->in.data, total_locks * lck_size)) {
+ smbsrv_send_error(req, NT_STATUS_FOOBAR);
+ return;
+ }
+
+ /* allocate the locks array */
+ if (total_locks) {
+ lck->lockx.in.locks = talloc_array(req, struct smb_lock_entry,
+ total_locks);
+ if (lck->lockx.in.locks == NULL) {
+ smbsrv_send_error(req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ }
+
+ p = req->in.data;
+
+ /* construct the locks array */
+ for (i=0;i<total_locks;i++) {
+ uint32_t ofs_high=0, count_high=0;
+
+ lck->lockx.in.locks[i].pid = SVAL(p, 0);
+
+ if (lck->lockx.in.mode & LOCKING_ANDX_LARGE_FILES) {
+ ofs_high = IVAL(p, 4);
+ lck->lockx.in.locks[i].offset = IVAL(p, 8);
+ count_high = IVAL(p, 12);
+ lck->lockx.in.locks[i].count = IVAL(p, 16);
+ } else {
+ lck->lockx.in.locks[i].offset = IVAL(p, 2);
+ lck->lockx.in.locks[i].count = IVAL(p, 6);
+ }
+ if (ofs_high != 0 || count_high != 0) {
+ lck->lockx.in.locks[i].count |= ((uint64_t)count_high) << 32;
+ lck->lockx.in.locks[i].offset |= ((uint64_t)ofs_high) << 32;
+ }
+ p += lck_size;
+ }
+
+ SMBSRV_CHECK_FILE_HANDLE(lck->lockx.in.file.ntvfs);
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_lock(req->ntvfs, lck));
+}
+
+/****************************************************************************
+ Reply to a SMBreadbmpx (read block multiplex) request.
+****************************************************************************/
+void smbsrv_reply_readbmpx(struct smbsrv_request *req)
+{
+ /* tell the client to not use a multiplexed read - its too broken to use */
+ smbsrv_send_error(req, NT_STATUS_DOS(ERRSRV, ERRuseSTD));
+}
+
+
+/****************************************************************************
+ Reply to a SMBsetattrE.
+****************************************************************************/
+void smbsrv_reply_setattrE(struct smbsrv_request *req)
+{
+ union smb_setfileinfo *info;
+
+ /* parse request */
+ SMBSRV_CHECK_WCT(req, 7);
+ SMBSRV_TALLOC_IO_PTR(info, union smb_setfileinfo);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_simple_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ info->setattre.level = RAW_SFILEINFO_SETATTRE;
+ info->setattre.in.file.ntvfs = smbsrv_pull_fnum(req, req->in.vwv, VWV(0));
+ info->setattre.in.create_time = srv_pull_dos_date2(req->smb_conn, req->in.vwv + VWV(1));
+ info->setattre.in.access_time = srv_pull_dos_date2(req->smb_conn, req->in.vwv + VWV(3));
+ info->setattre.in.write_time = srv_pull_dos_date2(req->smb_conn, req->in.vwv + VWV(5));
+
+ SMBSRV_CHECK_FILE_HANDLE(info->setattre.in.file.ntvfs);
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_setfileinfo(req->ntvfs, info));
+}
+
+
+/****************************************************************************
+ Reply to a SMBwritebmpx (write block multiplex primary) request.
+****************************************************************************/
+void smbsrv_reply_writebmpx(struct smbsrv_request *req)
+{
+ smbsrv_send_error(req, NT_STATUS_DOS(ERRSRV, ERRuseSTD));
+}
+
+
+/****************************************************************************
+ Reply to a SMBwritebs (write block multiplex secondary) request.
+****************************************************************************/
+void smbsrv_reply_writebs(struct smbsrv_request *req)
+{
+ smbsrv_send_error(req, NT_STATUS_DOS(ERRSRV, ERRuseSTD));
+}
+
+
+
+/****************************************************************************
+ Reply to a SMBgetattrE (async reply)
+****************************************************************************/
+static void reply_getattrE_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+ union smb_fileinfo *info;
+
+ SMBSRV_CHECK_ASYNC_STATUS(info, union smb_fileinfo);
+
+ /* setup reply */
+ smbsrv_setup_reply(req, 11, 0);
+
+ srv_push_dos_date2(req->smb_conn, req->out.vwv, VWV(0), info->getattre.out.create_time);
+ srv_push_dos_date2(req->smb_conn, req->out.vwv, VWV(2), info->getattre.out.access_time);
+ srv_push_dos_date2(req->smb_conn, req->out.vwv, VWV(4), info->getattre.out.write_time);
+ SIVAL(req->out.vwv, VWV(6), info->getattre.out.size);
+ SIVAL(req->out.vwv, VWV(8), info->getattre.out.alloc_size);
+ SSVAL(req->out.vwv, VWV(10), info->getattre.out.attrib);
+
+ smbsrv_send_reply(req);
+}
+
+/****************************************************************************
+ Reply to a SMBgetattrE.
+****************************************************************************/
+void smbsrv_reply_getattrE(struct smbsrv_request *req)
+{
+ union smb_fileinfo *info;
+
+ /* parse request */
+ SMBSRV_CHECK_WCT(req, 1);
+ SMBSRV_TALLOC_IO_PTR(info, union smb_fileinfo);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_getattrE_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ info->getattr.level = RAW_FILEINFO_GETATTRE;
+ info->getattr.in.file.ntvfs = smbsrv_pull_fnum(req, req->in.vwv, VWV(0));
+
+ SMBSRV_CHECK_FILE_HANDLE(info->getattr.in.file.ntvfs);
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_qfileinfo(req->ntvfs, info));
+}
+
+void smbsrv_reply_sesssetup_send(struct smbsrv_request *req,
+ union smb_sesssetup *io,
+ NTSTATUS status)
+{
+ switch (io->old.level) {
+ case RAW_SESSSETUP_OLD:
+ if (!NT_STATUS_IS_OK(status)) {
+ smbsrv_send_error(req, status);
+ return;
+ }
+
+ /* construct reply */
+ smbsrv_setup_reply(req, 3, 0);
+
+ SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+ SSVAL(req->out.vwv, VWV(1), 0);
+ SSVAL(req->out.vwv, VWV(2), io->old.out.action);
+
+ SSVAL(req->out.hdr, HDR_UID, io->old.out.vuid);
+
+ smbsrv_chain_reply(req);
+ return;
+
+ case RAW_SESSSETUP_NT1:
+ if (!NT_STATUS_IS_OK(status)) {
+ smbsrv_send_error(req, status);
+ return;
+ }
+
+ /* construct reply */
+ smbsrv_setup_reply(req, 3, 0);
+
+ SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+ SSVAL(req->out.vwv, VWV(1), 0);
+ SSVAL(req->out.vwv, VWV(2), io->nt1.out.action);
+
+ SSVAL(req->out.hdr, HDR_UID, io->nt1.out.vuid);
+
+ req_push_str(req, NULL, io->nt1.out.os, -1, STR_TERMINATE);
+ req_push_str(req, NULL, io->nt1.out.lanman, -1, STR_TERMINATE);
+ req_push_str(req, NULL, io->nt1.out.domain, -1, STR_TERMINATE);
+
+ smbsrv_chain_reply(req);
+ return;
+
+ case RAW_SESSSETUP_SPNEGO:
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ smbsrv_send_error(req, status);
+ return;
+ }
+
+ /* construct reply */
+ smbsrv_setup_reply(req, 4, io->spnego.out.secblob.length);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ smbsrv_setup_error(req, status);
+ }
+
+ SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+ SSVAL(req->out.vwv, VWV(1), 0);
+ SSVAL(req->out.vwv, VWV(2), io->spnego.out.action);
+ SSVAL(req->out.vwv, VWV(3), io->spnego.out.secblob.length);
+
+ SSVAL(req->out.hdr, HDR_UID, io->spnego.out.vuid);
+
+ memcpy(req->out.data, io->spnego.out.secblob.data, io->spnego.out.secblob.length);
+ req_push_str(req, NULL, io->spnego.out.os, -1, STR_TERMINATE);
+ req_push_str(req, NULL, io->spnego.out.lanman, -1, STR_TERMINATE);
+ req_push_str(req, NULL, io->spnego.out.workgroup, -1, STR_TERMINATE);
+
+ smbsrv_chain_reply(req);
+ return;
+
+ case RAW_SESSSETUP_SMB2:
+ break;
+ }
+
+ smbsrv_send_error(req, NT_STATUS_INTERNAL_ERROR);
+}
+
+/****************************************************************************
+reply to an old style session setup command
+****************************************************************************/
+static void reply_sesssetup_old(struct smbsrv_request *req)
+{
+ uint8_t *p;
+ uint16_t passlen;
+ union smb_sesssetup *io;
+
+ SMBSRV_TALLOC_IO_PTR(io, union smb_sesssetup);
+
+ io->old.level = RAW_SESSSETUP_OLD;
+
+ /* parse request */
+ io->old.in.bufsize = SVAL(req->in.vwv, VWV(2));
+ io->old.in.mpx_max = SVAL(req->in.vwv, VWV(3));
+ io->old.in.vc_num = SVAL(req->in.vwv, VWV(4));
+ io->old.in.sesskey = IVAL(req->in.vwv, VWV(5));
+ passlen = SVAL(req->in.vwv, VWV(7));
+
+ /* check the request isn't malformed */
+ if (req_data_oob(&req->in.bufinfo, req->in.data, passlen)) {
+ smbsrv_send_error(req, NT_STATUS_FOOBAR);
+ return;
+ }
+
+ p = req->in.data;
+ if (!req_pull_blob(&req->in.bufinfo, p, passlen, &io->old.in.password)) {
+ smbsrv_send_error(req, NT_STATUS_FOOBAR);
+ return;
+ }
+ p += passlen;
+
+ p += req_pull_string(&req->in.bufinfo, &io->old.in.user, p, -1, STR_TERMINATE);
+ p += req_pull_string(&req->in.bufinfo, &io->old.in.domain, p, -1, STR_TERMINATE);
+ p += req_pull_string(&req->in.bufinfo, &io->old.in.os, p, -1, STR_TERMINATE);
+ p += req_pull_string(&req->in.bufinfo, &io->old.in.lanman, p, -1, STR_TERMINATE);
+
+ /* call the generic handler */
+ smbsrv_sesssetup_backend(req, io);
+}
+
+/****************************************************************************
+reply to an NT1 style session setup command
+****************************************************************************/
+static void reply_sesssetup_nt1(struct smbsrv_request *req)
+{
+ uint8_t *p;
+ uint16_t passlen1, passlen2;
+ union smb_sesssetup *io;
+
+ SMBSRV_TALLOC_IO_PTR(io, union smb_sesssetup);
+
+ io->nt1.level = RAW_SESSSETUP_NT1;
+
+ /* parse request */
+ io->nt1.in.bufsize = SVAL(req->in.vwv, VWV(2));
+ io->nt1.in.mpx_max = SVAL(req->in.vwv, VWV(3));
+ io->nt1.in.vc_num = SVAL(req->in.vwv, VWV(4));
+ io->nt1.in.sesskey = IVAL(req->in.vwv, VWV(5));
+ passlen1 = SVAL(req->in.vwv, VWV(7));
+ passlen2 = SVAL(req->in.vwv, VWV(8));
+ io->nt1.in.capabilities = IVAL(req->in.vwv, VWV(11));
+
+ /* check the request isn't malformed */
+ if (req_data_oob(&req->in.bufinfo, req->in.data, passlen1) ||
+ req_data_oob(&req->in.bufinfo, req->in.data + passlen1, passlen2)) {
+ smbsrv_send_error(req, NT_STATUS_FOOBAR);
+ return;
+ }
+
+ p = req->in.data;
+ if (!req_pull_blob(&req->in.bufinfo, p, passlen1, &io->nt1.in.password1)) {
+ smbsrv_send_error(req, NT_STATUS_FOOBAR);
+ return;
+ }
+ p += passlen1;
+ if (!req_pull_blob(&req->in.bufinfo, p, passlen2, &io->nt1.in.password2)) {
+ smbsrv_send_error(req, NT_STATUS_FOOBAR);
+ return;
+ }
+ p += passlen2;
+
+ p += req_pull_string(&req->in.bufinfo, &io->nt1.in.user, p, -1, STR_TERMINATE);
+ p += req_pull_string(&req->in.bufinfo, &io->nt1.in.domain, p, -1, STR_TERMINATE);
+ p += req_pull_string(&req->in.bufinfo, &io->nt1.in.os, p, -1, STR_TERMINATE);
+ p += req_pull_string(&req->in.bufinfo, &io->nt1.in.lanman, p, -1, STR_TERMINATE);
+
+ /* call the generic handler */
+ smbsrv_sesssetup_backend(req, io);
+}
+
+
+/****************************************************************************
+reply to an SPNEGO style session setup command
+****************************************************************************/
+static void reply_sesssetup_spnego(struct smbsrv_request *req)
+{
+ uint8_t *p;
+ uint16_t blob_len;
+ union smb_sesssetup *io;
+
+ SMBSRV_TALLOC_IO_PTR(io, union smb_sesssetup);
+
+ io->spnego.level = RAW_SESSSETUP_SPNEGO;
+
+ /* parse request */
+ io->spnego.in.bufsize = SVAL(req->in.vwv, VWV(2));
+ io->spnego.in.mpx_max = SVAL(req->in.vwv, VWV(3));
+ io->spnego.in.vc_num = SVAL(req->in.vwv, VWV(4));
+ io->spnego.in.sesskey = IVAL(req->in.vwv, VWV(5));
+ blob_len = SVAL(req->in.vwv, VWV(7));
+ io->spnego.in.capabilities = IVAL(req->in.vwv, VWV(10));
+
+ p = req->in.data;
+ if (!req_pull_blob(&req->in.bufinfo, p, blob_len, &io->spnego.in.secblob)) {
+ smbsrv_send_error(req, NT_STATUS_FOOBAR);
+ return;
+ }
+ p += blob_len;
+
+ p += req_pull_string(&req->in.bufinfo, &io->spnego.in.os, p, -1, STR_TERMINATE);
+ p += req_pull_string(&req->in.bufinfo, &io->spnego.in.lanman, p, -1, STR_TERMINATE);
+ p += req_pull_string(&req->in.bufinfo, &io->spnego.in.workgroup, p, -1, STR_TERMINATE);
+
+ /* call the generic handler */
+ smbsrv_sesssetup_backend(req, io);
+}
+
+
+/****************************************************************************
+reply to a session setup command
+****************************************************************************/
+void smbsrv_reply_sesssetup(struct smbsrv_request *req)
+{
+ switch (req->in.wct) {
+ case 10:
+ /* a pre-NT1 call */
+ reply_sesssetup_old(req);
+ return;
+ case 13:
+ /* a NT1 call */
+ reply_sesssetup_nt1(req);
+ return;
+ case 12:
+ /* a SPNEGO call */
+ reply_sesssetup_spnego(req);
+ return;
+ }
+
+ /* unsupported variant */
+ smbsrv_send_error(req, NT_STATUS_FOOBAR);
+}
+
+/****************************************************************************
+ Reply to a exit. This closes all files open by a smbpid
+****************************************************************************/
+void smbsrv_reply_exit(struct smbsrv_request *req)
+{
+ struct smbsrv_handle_session_item *i, *ni;
+ struct smbsrv_handle *h;
+ struct smbsrv_tcon *tcon;
+ uint16_t smbpid;
+
+ SMBSRV_CHECK_WCT(req, 0);
+
+ smbpid = SVAL(req->in.hdr,HDR_PID);
+
+ /* first destroy all handles, which have the same PID as the request */
+ for (i=req->session->handles; i; i=ni) {
+ ni = i->next;
+ h = i->handle;
+ if (h->smbpid != smbpid) continue;
+
+ talloc_free(h);
+ }
+
+ /*
+ * then let the ntvfs backends proxy the call if they want to,
+ * but we didn't check the return value of the backends,
+ * as for the SMB client the call succeed
+ */
+ for (tcon=req->smb_conn->smb_tcons.list;tcon;tcon=tcon->next) {
+ req->tcon = tcon;
+ SMBSRV_SETUP_NTVFS_REQUEST(NULL,0);
+ ntvfs_exit(req->ntvfs);
+ talloc_free(req->ntvfs);
+ req->ntvfs = NULL;
+ req->tcon = NULL;
+ }
+
+ smbsrv_setup_reply(req, 0, 0);
+ smbsrv_send_reply(req);
+}
+
+/****************************************************************************
+ Reply to a SMBulogoffX.
+****************************************************************************/
+void smbsrv_reply_ulogoffX(struct smbsrv_request *req)
+{
+ struct smbsrv_handle_session_item *i, *ni;
+ struct smbsrv_handle *h;
+ struct smbsrv_tcon *tcon;
+
+ SMBSRV_CHECK_WCT(req, 2);
+
+ /*
+ * TODO: cancel all pending requests
+ */
+
+
+ /* destroy all handles */
+ for (i=req->session->handles; i; i=ni) {
+ ni = i->next;
+ h = i->handle;
+ talloc_free(h);
+ }
+
+ /*
+ * then let the ntvfs backends proxy the call if they want to,
+ * but we didn't check the return value of the backends,
+ * as for the SMB client the call succeed
+ */
+ for (tcon=req->smb_conn->smb_tcons.list;tcon;tcon=tcon->next) {
+ req->tcon = tcon;
+ SMBSRV_SETUP_NTVFS_REQUEST(NULL,0);
+ ntvfs_logoff(req->ntvfs);
+ talloc_free(req->ntvfs);
+ req->ntvfs = NULL;
+ req->tcon = NULL;
+ }
+
+ talloc_free(req->session);
+ req->session = NULL; /* it is now invalid, don't use on
+ any chained packets */
+
+ smbsrv_setup_reply(req, 2, 0);
+
+ SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+ SSVAL(req->out.vwv, VWV(1), 0);
+
+ smbsrv_chain_reply(req);
+}
+
+/****************************************************************************
+ Reply to an SMBfindclose request
+****************************************************************************/
+void smbsrv_reply_findclose(struct smbsrv_request *req)
+{
+ union smb_search_close *io;
+
+ /* parse request */
+ SMBSRV_CHECK_WCT(req, 1);
+ SMBSRV_TALLOC_IO_PTR(io, union smb_search_close);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_simple_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ io->findclose.level = RAW_FINDCLOSE_FINDCLOSE;
+ io->findclose.in.handle = SVAL(req->in.vwv, VWV(0));
+
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_search_close(req->ntvfs, io));
+}
+
+/****************************************************************************
+ Reply to an SMBfindnclose request
+****************************************************************************/
+void smbsrv_reply_findnclose(struct smbsrv_request *req)
+{
+ smbsrv_send_error(req, NT_STATUS_FOOBAR);
+}
+
+
+/****************************************************************************
+ Reply to an SMBntcreateX request (async send)
+****************************************************************************/
+static void reply_ntcreate_and_X_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+ union smb_open *io;
+
+ SMBSRV_CHECK_ASYNC_STATUS(io, union smb_open);
+
+ /* construct reply */
+ smbsrv_setup_reply(req, 34, 0);
+
+ SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
+ SSVAL(req->out.vwv, VWV(1), 0);
+ SCVAL(req->out.vwv, VWV(2), io->ntcreatex.out.oplock_level);
+
+ /* the rest of the parameters are not aligned! */
+ smbsrv_push_fnum(req->out.vwv, 5, io->ntcreatex.out.file.ntvfs);
+ SIVAL(req->out.vwv, 7, io->ntcreatex.out.create_action);
+ push_nttime(req->out.vwv, 11, io->ntcreatex.out.create_time);
+ push_nttime(req->out.vwv, 19, io->ntcreatex.out.access_time);
+ push_nttime(req->out.vwv, 27, io->ntcreatex.out.write_time);
+ push_nttime(req->out.vwv, 35, io->ntcreatex.out.change_time);
+ SIVAL(req->out.vwv, 43, io->ntcreatex.out.attrib);
+ SBVAL(req->out.vwv, 47, io->ntcreatex.out.alloc_size);
+ SBVAL(req->out.vwv, 55, io->ntcreatex.out.size);
+ SSVAL(req->out.vwv, 63, io->ntcreatex.out.file_type);
+ SSVAL(req->out.vwv, 65, io->ntcreatex.out.ipc_state);
+ SCVAL(req->out.vwv, 67, io->ntcreatex.out.is_directory);
+
+ req->chained_fnum = SVAL(req->out.vwv, 5);
+
+ smbsrv_chain_reply(req);
+}
+
+/****************************************************************************
+ Reply to an SMBntcreateX request
+****************************************************************************/
+void smbsrv_reply_ntcreate_and_X(struct smbsrv_request *req)
+{
+ union smb_open *io;
+ uint16_t fname_len;
+
+ /* parse the request */
+ SMBSRV_CHECK_WCT(req, 24);
+ SMBSRV_TALLOC_IO_PTR(io, union smb_open);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_ntcreate_and_X_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ io->ntcreatex.level = RAW_OPEN_NTCREATEX;
+
+ /* notice that the word parameters are not word aligned, so we don't use VWV() */
+ fname_len = SVAL(req->in.vwv, 5);
+ io->ntcreatex.in.flags = IVAL(req->in.vwv, 7);
+ io->ntcreatex.in.root_fid = IVAL(req->in.vwv, 11);
+ io->ntcreatex.in.access_mask = IVAL(req->in.vwv, 15);
+ io->ntcreatex.in.alloc_size = BVAL(req->in.vwv, 19);
+ io->ntcreatex.in.file_attr = IVAL(req->in.vwv, 27);
+ io->ntcreatex.in.share_access = IVAL(req->in.vwv, 31);
+ io->ntcreatex.in.open_disposition = IVAL(req->in.vwv, 35);
+ io->ntcreatex.in.create_options = IVAL(req->in.vwv, 39);
+ io->ntcreatex.in.impersonation = IVAL(req->in.vwv, 43);
+ io->ntcreatex.in.security_flags = CVAL(req->in.vwv, 47);
+ io->ntcreatex.in.ea_list = NULL;
+ io->ntcreatex.in.sec_desc = NULL;
+ io->ntcreatex.in.query_maximal_access = false;
+
+ /* we use a couple of bits of the create options internally */
+ if (io->ntcreatex.in.create_options & NTCREATEX_OPTIONS_PRIVATE_MASK) {
+ smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ /* we need a neater way to handle this alignment */
+ if ((req->flags2 & FLAGS2_UNICODE_STRINGS) &&
+ ucs2_align(req->in.buffer, req->in.data, STR_TERMINATE|STR_UNICODE)) {
+ fname_len++;
+ }
+
+ req_pull_string(&req->in.bufinfo, &io->ntcreatex.in.fname, req->in.data, fname_len, STR_TERMINATE);
+ if (!io->ntcreatex.in.fname) {
+ smbsrv_send_error(req, NT_STATUS_FOOBAR);
+ return;
+ }
+
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_open(req->ntvfs, io));
+}
+
+
+/****************************************************************************
+ Reply to an SMBntcancel request
+****************************************************************************/
+void smbsrv_reply_ntcancel(struct smbsrv_request *req)
+{
+ struct smbsrv_request *r;
+ uint16_t tid = SVAL(req->in.hdr,HDR_TID);
+ uint16_t uid = SVAL(req->in.hdr,HDR_UID);
+ uint16_t mid = SVAL(req->in.hdr,HDR_MID);
+ uint16_t pid = SVAL(req->in.hdr,HDR_PID);
+
+ for (r = req->smb_conn->requests; r; r = r->next) {
+ if (tid != SVAL(r->in.hdr,HDR_TID)) continue;
+ if (uid != SVAL(r->in.hdr,HDR_UID)) continue;
+ if (mid != SVAL(r->in.hdr,HDR_MID)) continue;
+ if (pid != SVAL(r->in.hdr,HDR_PID)) continue;
+
+ SMBSRV_CHECK(ntvfs_cancel(r->ntvfs));
+
+ /* NOTE: this request does not generate a reply */
+ talloc_free(req);
+ return;
+ }
+
+ /* TODO: workout the correct error code,
+ * until we know how the smb signing works
+ * for ntcancel replies, don't send an error
+ */
+ /*smbsrv_send_error(req, NT_STATUS_FOOBAR);*/
+ talloc_free(req);
+}
+
+/*
+ parse the called/calling names from session request
+*/
+static NTSTATUS parse_session_request(struct smbsrv_request *req)
+{
+ DATA_BLOB blob;
+ NTSTATUS status;
+
+ blob.data = req->in.buffer + 4;
+ blob.length = ascii_len_n((const char *)blob.data, req->in.size - PTR_DIFF(blob.data, req->in.buffer));
+ if (blob.length == 0) return NT_STATUS_BAD_NETWORK_NAME;
+
+ req->smb_conn->negotiate.called_name = talloc(req->smb_conn, struct nbt_name);
+ req->smb_conn->negotiate.calling_name = talloc(req->smb_conn, struct nbt_name);
+ if (req->smb_conn->negotiate.called_name == NULL ||
+ req->smb_conn->negotiate.calling_name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = nbt_name_from_blob(req->smb_conn, &blob,
+ req->smb_conn->negotiate.called_name);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ blob.data += blob.length;
+ blob.length = ascii_len_n((const char *)blob.data, req->in.size - PTR_DIFF(blob.data, req->in.buffer));
+ if (blob.length == 0) return NT_STATUS_BAD_NETWORK_NAME;
+
+ status = nbt_name_from_blob(req->smb_conn, &blob,
+ req->smb_conn->negotiate.calling_name);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ req->smb_conn->negotiate.done_nbt_session = true;
+
+ return NT_STATUS_OK;
+}
+
+
+
+/****************************************************************************
+ Reply to a special message - a SMB packet with non zero NBT message type
+****************************************************************************/
+void smbsrv_reply_special(struct smbsrv_request *req)
+{
+ uint8_t msg_type;
+ uint8_t *buf = talloc_zero_array(req, uint8_t, 4);
+
+ msg_type = CVAL(req->in.buffer,0);
+
+ SIVAL(buf, 0, 0);
+
+ switch (msg_type) {
+ case 0x81: /* session request */
+ if (req->smb_conn->negotiate.done_nbt_session) {
+ DEBUG(0,("Warning: ignoring secondary session request\n"));
+ return;
+ }
+
+ SCVAL(buf,0,0x82);
+ SCVAL(buf,3,0);
+
+ /* we don't check the status - samba always accepts session
+ requests for any name */
+ parse_session_request(req);
+
+ req->out.buffer = buf;
+ req->out.size = 4;
+ smbsrv_send_reply_nosign(req);
+ return;
+
+ case 0x89: /* session keepalive request
+ (some old clients produce this?) */
+ SCVAL(buf, 0, SMBkeepalive);
+ SCVAL(buf, 3, 0);
+ req->out.buffer = buf;
+ req->out.size = 4;
+ smbsrv_send_reply_nosign(req);
+ return;
+
+ case SMBkeepalive:
+ /* session keepalive - swallow it */
+ talloc_free(req);
+ return;
+ }
+
+ DEBUG(0,("Unexpected NBT session packet (%d)\n", msg_type));
+ talloc_free(req);
+}
diff --git a/source4/smb_server/smb/request.c b/source4/smb_server/smb/request.c
new file mode 100644
index 0000000000..6846f80594
--- /dev/null
+++ b/source4/smb_server/smb/request.c
@@ -0,0 +1,778 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ this file implements functions for manipulating the 'struct smbsrv_request' structure in smbd
+*/
+
+#include "includes.h"
+#include "smb_server/smb_server.h"
+#include "smb_server/service_smb_proto.h"
+#include "smbd/service_stream.h"
+#include "lib/stream/packet.h"
+#include "ntvfs/ntvfs.h"
+
+
+/* we over allocate the data buffer to prevent too many realloc calls */
+#define REQ_OVER_ALLOCATION 0
+
+/* setup the bufinfo used for strings and range checking */
+void smbsrv_setup_bufinfo(struct smbsrv_request *req)
+{
+ req->in.bufinfo.mem_ctx = req;
+ req->in.bufinfo.flags = 0;
+ if (req->flags2 & FLAGS2_UNICODE_STRINGS) {
+ req->in.bufinfo.flags |= BUFINFO_FLAG_UNICODE;
+ }
+ req->in.bufinfo.align_base = req->in.buffer;
+ req->in.bufinfo.data = req->in.data;
+ req->in.bufinfo.data_size = req->in.data_size;
+}
+
+
+static int smbsrv_request_destructor(struct smbsrv_request *req)
+{
+ DLIST_REMOVE(req->smb_conn->requests, req);
+ return 0;
+}
+
+/****************************************************************************
+construct a basic request packet, mostly used to construct async packets
+such as change notify and oplock break requests
+****************************************************************************/
+struct smbsrv_request *smbsrv_init_request(struct smbsrv_connection *smb_conn)
+{
+ struct smbsrv_request *req;
+
+ req = talloc_zero(smb_conn, struct smbsrv_request);
+ if (!req) {
+ return NULL;
+ }
+
+ /* setup the request context */
+ req->smb_conn = smb_conn;
+
+ talloc_set_destructor(req, smbsrv_request_destructor);
+
+ return req;
+}
+
+
+/*
+ setup a chained reply in req->out with the given word count and initial data buffer size.
+*/
+static void req_setup_chain_reply(struct smbsrv_request *req, uint_t wct, uint_t buflen)
+{
+ uint32_t chain_base_size = req->out.size;
+
+ /* we need room for the wct value, the words, the buffer length and the buffer */
+ req->out.size += 1 + VWV(wct) + 2 + buflen;
+
+ /* over allocate by a small amount */
+ req->out.allocated = req->out.size + REQ_OVER_ALLOCATION;
+
+ req->out.buffer = talloc_realloc(req, req->out.buffer,
+ uint8_t, req->out.allocated);
+ if (!req->out.buffer) {
+ smbsrv_terminate_connection(req->smb_conn, "allocation failed");
+ return;
+ }
+
+ req->out.hdr = req->out.buffer + NBT_HDR_SIZE;
+ req->out.vwv = req->out.buffer + chain_base_size + 1;
+ req->out.wct = wct;
+ req->out.data = req->out.vwv + VWV(wct) + 2;
+ req->out.data_size = buflen;
+ req->out.ptr = req->out.data;
+
+ SCVAL(req->out.buffer, chain_base_size, wct);
+ SSVAL(req->out.vwv, VWV(wct), buflen);
+}
+
+
+/*
+ setup a reply in req->out with the given word count and initial data buffer size.
+ the caller will then fill in the command words and data before calling req_send_reply() to
+ send the reply on its way
+*/
+void smbsrv_setup_reply(struct smbsrv_request *req, uint_t wct, size_t buflen)
+{
+ uint16_t flags2;
+
+ if (req->chain_count != 0) {
+ req_setup_chain_reply(req, wct, buflen);
+ return;
+ }
+
+ req->out.size = NBT_HDR_SIZE + MIN_SMB_SIZE + VWV(wct) + buflen;
+
+ /* over allocate by a small amount */
+ req->out.allocated = req->out.size + REQ_OVER_ALLOCATION;
+
+ req->out.buffer = talloc_size(req, req->out.allocated);
+ if (!req->out.buffer) {
+ smbsrv_terminate_connection(req->smb_conn, "allocation failed");
+ return;
+ }
+
+ flags2 = FLAGS2_LONG_PATH_COMPONENTS |
+ FLAGS2_EXTENDED_ATTRIBUTES |
+ FLAGS2_IS_LONG_NAME;
+#define _SMB_FLAGS2_ECHOED_FLAGS ( \
+ FLAGS2_UNICODE_STRINGS | \
+ FLAGS2_EXTENDED_SECURITY | \
+ FLAGS2_SMB_SECURITY_SIGNATURES \
+)
+ flags2 |= (req->flags2 & _SMB_FLAGS2_ECHOED_FLAGS);
+ if (req->smb_conn->negotiate.client_caps & CAP_STATUS32) {
+ flags2 |= FLAGS2_32_BIT_ERROR_CODES;
+ }
+
+ req->out.hdr = req->out.buffer + NBT_HDR_SIZE;
+ req->out.vwv = req->out.hdr + HDR_VWV;
+ req->out.wct = wct;
+ req->out.data = req->out.vwv + VWV(wct) + 2;
+ req->out.data_size = buflen;
+ req->out.ptr = req->out.data;
+
+ SIVAL(req->out.hdr, HDR_RCLS, 0);
+
+ SCVAL(req->out.hdr, HDR_WCT, wct);
+ SSVAL(req->out.vwv, VWV(wct), buflen);
+
+ memcpy(req->out.hdr, "\377SMB", 4);
+ SCVAL(req->out.hdr,HDR_FLG, FLAG_REPLY | FLAG_CASELESS_PATHNAMES);
+ SSVAL(req->out.hdr,HDR_FLG2, flags2);
+ SSVAL(req->out.hdr,HDR_PIDHIGH,0);
+ memset(req->out.hdr + HDR_SS_FIELD, 0, 10);
+
+ if (req->in.hdr) {
+ /* copy the cmd, tid, pid, uid and mid from the request */
+ SCVAL(req->out.hdr,HDR_COM,CVAL(req->in.hdr,HDR_COM));
+ SSVAL(req->out.hdr,HDR_TID,SVAL(req->in.hdr,HDR_TID));
+ SSVAL(req->out.hdr,HDR_PID,SVAL(req->in.hdr,HDR_PID));
+ SSVAL(req->out.hdr,HDR_UID,SVAL(req->in.hdr,HDR_UID));
+ SSVAL(req->out.hdr,HDR_MID,SVAL(req->in.hdr,HDR_MID));
+ } else {
+ SCVAL(req->out.hdr,HDR_COM,0);
+ SSVAL(req->out.hdr,HDR_TID,0);
+ SSVAL(req->out.hdr,HDR_PID,0);
+ SSVAL(req->out.hdr,HDR_UID,0);
+ SSVAL(req->out.hdr,HDR_MID,0);
+ }
+}
+
+
+/*
+ setup a copy of a request, used when the server needs to send
+ more than one reply for a single request packet
+*/
+struct smbsrv_request *smbsrv_setup_secondary_request(struct smbsrv_request *old_req)
+{
+ struct smbsrv_request *req;
+ ptrdiff_t diff;
+
+ req = talloc_memdup(old_req, old_req, sizeof(struct smbsrv_request));
+ if (req == NULL) {
+ return NULL;
+ }
+
+ req->out.buffer = talloc_memdup(req, req->out.buffer, req->out.allocated);
+ if (req->out.buffer == NULL) {
+ talloc_free(req);
+ return NULL;
+ }
+
+ diff = req->out.buffer - old_req->out.buffer;
+
+ req->out.hdr += diff;
+ req->out.vwv += diff;
+ req->out.data += diff;
+ req->out.ptr += diff;
+
+ return req;
+}
+
+/*
+ work out the maximum data size we will allow for this reply, given
+ the negotiated max_xmit. The basic reply packet must be setup before
+ this call
+
+ note that this is deliberately a signed integer reply
+*/
+int req_max_data(struct smbsrv_request *req)
+{
+ int ret;
+ ret = req->smb_conn->negotiate.max_send;
+ ret -= PTR_DIFF(req->out.data, req->out.hdr);
+ if (ret < 0) ret = 0;
+ return ret;
+}
+
+
+/*
+ grow the allocation of the data buffer portion of a reply
+ packet. Note that as this can reallocate the packet buffer this
+ invalidates any local pointers into the packet.
+
+ To cope with this req->out.ptr is supplied. This will be updated to
+ point at the same offset into the packet as before this call
+*/
+static void req_grow_allocation(struct smbsrv_request *req, uint_t new_size)
+{
+ int delta;
+ uint8_t *buf2;
+
+ delta = new_size - req->out.data_size;
+ if (delta + req->out.size <= req->out.allocated) {
+ /* it fits in the preallocation */
+ return;
+ }
+
+ /* we need to realloc */
+ req->out.allocated = req->out.size + delta + REQ_OVER_ALLOCATION;
+ buf2 = talloc_realloc(req, req->out.buffer, uint8_t, req->out.allocated);
+ if (buf2 == NULL) {
+ smb_panic("out of memory in req_grow_allocation");
+ }
+
+ if (buf2 == req->out.buffer) {
+ /* the malloc library gave us the same pointer */
+ return;
+ }
+
+ /* update the pointers into the packet */
+ req->out.data = buf2 + PTR_DIFF(req->out.data, req->out.buffer);
+ req->out.ptr = buf2 + PTR_DIFF(req->out.ptr, req->out.buffer);
+ req->out.vwv = buf2 + PTR_DIFF(req->out.vwv, req->out.buffer);
+ req->out.hdr = buf2 + PTR_DIFF(req->out.hdr, req->out.buffer);
+
+ req->out.buffer = buf2;
+}
+
+
+/*
+ grow the data buffer portion of a reply packet. Note that as this
+ can reallocate the packet buffer this invalidates any local pointers
+ into the packet.
+
+ To cope with this req->out.ptr is supplied. This will be updated to
+ point at the same offset into the packet as before this call
+*/
+void req_grow_data(struct smbsrv_request *req, size_t new_size)
+{
+ int delta;
+
+ if (!(req->control_flags & SMBSRV_REQ_CONTROL_LARGE) && new_size > req_max_data(req)) {
+ smb_panic("reply buffer too large!");
+ }
+
+ req_grow_allocation(req, new_size);
+
+ delta = new_size - req->out.data_size;
+
+ req->out.size += delta;
+ req->out.data_size += delta;
+
+ /* set the BCC to the new data size */
+ SSVAL(req->out.vwv, VWV(req->out.wct), new_size);
+}
+
+/*
+ send a reply and destroy the request buffer
+
+ note that this only looks at req->out.buffer and req->out.size, allowing manually
+ constructed packets to be sent
+*/
+void smbsrv_send_reply_nosign(struct smbsrv_request *req)
+{
+ DATA_BLOB blob;
+ NTSTATUS status;
+
+ if (req->smb_conn->connection->event.fde == NULL) {
+ /* we are in the process of shutting down this connection */
+ talloc_free(req);
+ return;
+ }
+
+ if (req->out.size > NBT_HDR_SIZE) {
+ _smb_setlen(req->out.buffer, req->out.size - NBT_HDR_SIZE);
+ }
+
+ blob = data_blob_const(req->out.buffer, req->out.size);
+ status = packet_send(req->smb_conn->packet, blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ smbsrv_terminate_connection(req->smb_conn, nt_errstr(status));
+ }
+ talloc_free(req);
+}
+
+/*
+ possibly sign a message then send a reply and destroy the request buffer
+
+ note that this only looks at req->out.buffer and req->out.size, allowing manually
+ constructed packets to be sent
+*/
+void smbsrv_send_reply(struct smbsrv_request *req)
+{
+ if (req->smb_conn->connection->event.fde == NULL) {
+ /* we are in the process of shutting down this connection */
+ talloc_free(req);
+ return;
+ }
+ smbsrv_sign_packet(req);
+
+ smbsrv_send_reply_nosign(req);
+}
+
+/*
+ setup the header of a reply to include an NTSTATUS code
+*/
+void smbsrv_setup_error(struct smbsrv_request *req, NTSTATUS status)
+{
+ if (!req->smb_conn->config.nt_status_support || !(req->smb_conn->negotiate.client_caps & CAP_STATUS32)) {
+ /* convert to DOS error codes */
+ uint8_t eclass;
+ uint32_t ecode;
+ ntstatus_to_dos(status, &eclass, &ecode);
+ SCVAL(req->out.hdr, HDR_RCLS, eclass);
+ SSVAL(req->out.hdr, HDR_ERR, ecode);
+ SSVAL(req->out.hdr, HDR_FLG2, SVAL(req->out.hdr, HDR_FLG2) & ~FLAGS2_32_BIT_ERROR_CODES);
+ return;
+ }
+
+ if (NT_STATUS_IS_DOS(status)) {
+ /* its a encoded DOS error, using the reserved range */
+ SSVAL(req->out.hdr, HDR_RCLS, NT_STATUS_DOS_CLASS(status));
+ SSVAL(req->out.hdr, HDR_ERR, NT_STATUS_DOS_CODE(status));
+ SSVAL(req->out.hdr, HDR_FLG2, SVAL(req->out.hdr, HDR_FLG2) & ~FLAGS2_32_BIT_ERROR_CODES);
+ } else {
+ SIVAL(req->out.hdr, HDR_RCLS, NT_STATUS_V(status));
+ SSVAL(req->out.hdr, HDR_FLG2, SVAL(req->out.hdr, HDR_FLG2) | FLAGS2_32_BIT_ERROR_CODES);
+ }
+}
+
+/*
+ construct and send an error packet, then destroy the request
+ auto-converts to DOS error format when appropriate
+*/
+void smbsrv_send_error(struct smbsrv_request *req, NTSTATUS status)
+{
+ if (req->smb_conn->connection->event.fde == NULL) {
+ /* the socket has been destroyed - no point trying to send an error! */
+ talloc_free(req);
+ return;
+ }
+ smbsrv_setup_reply(req, 0, 0);
+
+ /* error returns never have any data */
+ req_grow_data(req, 0);
+
+ smbsrv_setup_error(req, status);
+ smbsrv_send_reply(req);
+}
+
+
+/*
+ push a string into the data portion of the request packet, growing it if necessary
+ this gets quite tricky - please be very careful to cover all cases when modifying this
+
+ if dest is NULL, then put the string at the end of the data portion of the packet
+
+ if dest_len is -1 then no limit applies
+*/
+size_t req_push_str(struct smbsrv_request *req, uint8_t *dest, const char *str, int dest_len, size_t flags)
+{
+ size_t len;
+ uint_t grow_size;
+ uint8_t *buf0;
+ const int max_bytes_per_char = 3;
+
+ if (!(flags & (STR_ASCII|STR_UNICODE))) {
+ flags |= (req->flags2 & FLAGS2_UNICODE_STRINGS) ? STR_UNICODE : STR_ASCII;
+ }
+
+ if (dest == NULL) {
+ dest = req->out.data + req->out.data_size;
+ }
+
+ if (dest_len != -1) {
+ len = dest_len;
+ } else {
+ len = (strlen(str)+2) * max_bytes_per_char;
+ }
+
+ grow_size = len + PTR_DIFF(dest, req->out.data);
+ buf0 = req->out.buffer;
+
+ req_grow_allocation(req, grow_size);
+
+ if (buf0 != req->out.buffer) {
+ dest = req->out.buffer + PTR_DIFF(dest, buf0);
+ }
+
+ len = push_string(dest, str, len, flags);
+
+ grow_size = len + PTR_DIFF(dest, req->out.data);
+
+ if (grow_size > req->out.data_size) {
+ req_grow_data(req, grow_size);
+ }
+
+ return len;
+}
+
+/*
+ append raw bytes into the data portion of the request packet
+ return the number of bytes added
+*/
+size_t req_append_bytes(struct smbsrv_request *req,
+ const uint8_t *bytes, size_t byte_len)
+{
+ req_grow_allocation(req, byte_len + req->out.data_size);
+ memcpy(req->out.data + req->out.data_size, bytes, byte_len);
+ req_grow_data(req, byte_len + req->out.data_size);
+ return byte_len;
+}
+/*
+ append variable block (type 5 buffer) into the data portion of the request packet
+ return the number of bytes added
+*/
+size_t req_append_var_block(struct smbsrv_request *req,
+ const uint8_t *bytes, uint16_t byte_len)
+{
+ req_grow_allocation(req, byte_len + 3 + req->out.data_size);
+ SCVAL(req->out.data + req->out.data_size, 0, 5);
+ SSVAL(req->out.data + req->out.data_size, 1, byte_len); /* add field length */
+ if (byte_len > 0) {
+ memcpy(req->out.data + req->out.data_size + 3, bytes, byte_len);
+ }
+ req_grow_data(req, byte_len + 3 + req->out.data_size);
+ return byte_len + 3;
+}
+/**
+ pull a UCS2 string from a request packet, returning a talloced unix string
+
+ the string length is limited by the 3 things:
+ - the data size in the request (end of packet)
+ - the passed 'byte_len' if it is not -1
+ - the end of string (null termination)
+
+ Note that 'byte_len' is the number of bytes in the packet
+
+ on failure zero is returned and *dest is set to NULL, otherwise the number
+ of bytes consumed in the packet is returned
+*/
+static size_t req_pull_ucs2(struct request_bufinfo *bufinfo, const char **dest, const uint8_t *src, int byte_len, uint_t flags)
+{
+ int src_len, src_len2, alignment=0;
+ bool ret;
+ char *dest2;
+
+ if (!(flags & STR_NOALIGN) && ucs2_align(bufinfo->align_base, src, flags)) {
+ src++;
+ alignment=1;
+ if (byte_len != -1) {
+ byte_len--;
+ }
+ }
+
+ if (flags & STR_NO_RANGE_CHECK) {
+ src_len = byte_len;
+ } else {
+ src_len = bufinfo->data_size - PTR_DIFF(src, bufinfo->data);
+ if (byte_len != -1 && src_len > byte_len) {
+ src_len = byte_len;
+ }
+ }
+
+ if (src_len < 0) {
+ *dest = NULL;
+ return 0;
+ }
+
+ src_len2 = utf16_len_n(src, src_len);
+ if (src_len2 == 0) {
+ *dest = talloc_strdup(bufinfo->mem_ctx, "");
+ return src_len2 + alignment;
+ }
+
+ ret = convert_string_talloc(bufinfo->mem_ctx, CH_UTF16, CH_UNIX, src, src_len2, (void **)&dest2, NULL, false);
+
+ if (!ret) {
+ *dest = NULL;
+ return 0;
+ }
+ *dest = dest2;
+
+ return src_len2 + alignment;
+}
+
+/**
+ pull a ascii string from a request packet, returning a talloced string
+
+ the string length is limited by the 3 things:
+ - the data size in the request (end of packet)
+ - the passed 'byte_len' if it is not -1
+ - the end of string (null termination)
+
+ Note that 'byte_len' is the number of bytes in the packet
+
+ on failure zero is returned and *dest is set to NULL, otherwise the number
+ of bytes consumed in the packet is returned
+*/
+static size_t req_pull_ascii(struct request_bufinfo *bufinfo, const char **dest, const uint8_t *src, int byte_len, uint_t flags)
+{
+ int src_len, src_len2;
+ bool ret;
+ char *dest2;
+
+ if (flags & STR_NO_RANGE_CHECK) {
+ src_len = byte_len;
+ } else {
+ src_len = bufinfo->data_size - PTR_DIFF(src, bufinfo->data);
+ if (src_len < 0) {
+ *dest = NULL;
+ return 0;
+ }
+ if (byte_len != -1 && src_len > byte_len) {
+ src_len = byte_len;
+ }
+ }
+
+ src_len2 = strnlen((const char *)src, src_len);
+ if (src_len2 <= src_len - 1) {
+ /* include the termination if we didn't reach the end of the packet */
+ src_len2++;
+ }
+
+ ret = convert_string_talloc(bufinfo->mem_ctx, CH_DOS, CH_UNIX, src, src_len2, (void **)&dest2, NULL, false);
+
+ if (!ret) {
+ *dest = NULL;
+ return 0;
+ }
+ *dest = dest2;
+
+ return src_len2;
+}
+
+/**
+ pull a string from a request packet, returning a talloced string
+
+ the string length is limited by the 3 things:
+ - the data size in the request (end of packet)
+ - the passed 'byte_len' if it is not -1
+ - the end of string (null termination)
+
+ Note that 'byte_len' is the number of bytes in the packet
+
+ on failure zero is returned and *dest is set to NULL, otherwise the number
+ of bytes consumed in the packet is returned
+*/
+size_t req_pull_string(struct request_bufinfo *bufinfo, const char **dest, const uint8_t *src, int byte_len, uint_t flags)
+{
+ if (!(flags & STR_ASCII) &&
+ (((flags & STR_UNICODE) || (bufinfo->flags & BUFINFO_FLAG_UNICODE)))) {
+ return req_pull_ucs2(bufinfo, dest, src, byte_len, flags);
+ }
+
+ return req_pull_ascii(bufinfo, dest, src, byte_len, flags);
+}
+
+
+/**
+ pull a ASCII4 string buffer from a request packet, returning a talloced string
+
+ an ASCII4 buffer is a null terminated string that has a prefix
+ of the character 0x4. It tends to be used in older parts of the protocol.
+
+ on failure *dest is set to the zero length string. This seems to
+ match win2000 behaviour
+*/
+size_t req_pull_ascii4(struct request_bufinfo *bufinfo, const char **dest, const uint8_t *src, uint_t flags)
+{
+ ssize_t ret;
+
+ if (PTR_DIFF(src, bufinfo->data) + 1 > bufinfo->data_size) {
+ /* win2000 treats this as the empty string! */
+ (*dest) = talloc_strdup(bufinfo->mem_ctx, "");
+ return 0;
+ }
+
+ /* this consumes the 0x4 byte. We don't check whether the byte
+ is actually 0x4 or not. This matches win2000 server
+ behaviour */
+ src++;
+
+ ret = req_pull_string(bufinfo, dest, src, -1, flags);
+ if (ret == -1) {
+ (*dest) = talloc_strdup(bufinfo->mem_ctx, "");
+ return 1;
+ }
+
+ return ret + 1;
+}
+
+/**
+ pull a DATA_BLOB from a request packet, returning a talloced blob
+
+ return false if any part is outside the data portion of the packet
+*/
+bool req_pull_blob(struct request_bufinfo *bufinfo, const uint8_t *src, int len, DATA_BLOB *blob)
+{
+ if (len != 0 && req_data_oob(bufinfo, src, len)) {
+ return false;
+ }
+
+ (*blob) = data_blob_talloc(bufinfo->mem_ctx, src, len);
+
+ return true;
+}
+
+/* check that a lump of data in a request is within the bounds of the data section of
+ the packet */
+bool req_data_oob(struct request_bufinfo *bufinfo, const uint8_t *ptr, uint32_t count)
+{
+ if (count == 0) {
+ return false;
+ }
+
+ /* be careful with wraparound! */
+ if ((uintptr_t)ptr < (uintptr_t)bufinfo->data ||
+ (uintptr_t)ptr >= (uintptr_t)bufinfo->data + bufinfo->data_size ||
+ count > bufinfo->data_size ||
+ (uintptr_t)ptr + count > (uintptr_t)bufinfo->data + bufinfo->data_size) {
+ return true;
+ }
+ return false;
+}
+
+
+/*
+ pull an open file handle from a packet, taking account of the chained_fnum
+*/
+static uint16_t req_fnum(struct smbsrv_request *req, const uint8_t *base, uint_t offset)
+{
+ if (req->chained_fnum != -1) {
+ return req->chained_fnum;
+ }
+ return SVAL(base, offset);
+}
+
+struct ntvfs_handle *smbsrv_pull_fnum(struct smbsrv_request *req, const uint8_t *base, uint_t offset)
+{
+ struct smbsrv_handle *handle;
+ uint16_t fnum = req_fnum(req, base, offset);
+
+ handle = smbsrv_smb_handle_find(req->tcon, fnum, req->request_time);
+ if (!handle) {
+ return NULL;
+ }
+
+ /*
+ * For SMB tcons and sessions can be mixed!
+ * But we need to make sure that file handles
+ * are only accessed by the opening session!
+ *
+ * So check if the handle is valid for the given session!
+ */
+ if (handle->session != req->session) {
+ return NULL;
+ }
+
+ return handle->ntvfs;
+}
+
+void smbsrv_push_fnum(uint8_t *base, uint_t offset, struct ntvfs_handle *ntvfs)
+{
+ struct smbsrv_handle *handle = talloc_get_type(ntvfs->frontend_data.private_data,
+ struct smbsrv_handle);
+ SSVAL(base, offset, handle->hid);
+}
+
+NTSTATUS smbsrv_handle_create_new(void *private_data, struct ntvfs_request *ntvfs, struct ntvfs_handle **_h)
+{
+ struct smbsrv_request *req = talloc_get_type(ntvfs->frontend_data.private_data,
+ struct smbsrv_request);
+ struct smbsrv_handle *handle;
+ struct ntvfs_handle *h;
+
+ handle = smbsrv_handle_new(req->session, req->tcon, req, req->request_time);
+ if (!handle) return NT_STATUS_INSUFFICIENT_RESOURCES;
+
+ h = talloc_zero(handle, struct ntvfs_handle);
+ if (!h) goto nomem;
+
+ /*
+ * note: we don't set handle->ntvfs yet,
+ * this will be done by smbsrv_handle_make_valid()
+ * this makes sure the handle is invalid for clients
+ * until the ntvfs subsystem has made it valid
+ */
+ h->ctx = ntvfs->ctx;
+ h->session_info = ntvfs->session_info;
+ h->smbpid = ntvfs->smbpid;
+
+ h->frontend_data.private_data = handle;
+
+ *_h = h;
+ return NT_STATUS_OK;
+nomem:
+ talloc_free(handle);
+ return NT_STATUS_NO_MEMORY;
+}
+
+NTSTATUS smbsrv_handle_make_valid(void *private_data, struct ntvfs_handle *h)
+{
+ struct smbsrv_tcon *tcon = talloc_get_type(private_data, struct smbsrv_tcon);
+ struct smbsrv_handle *handle = talloc_get_type(h->frontend_data.private_data,
+ struct smbsrv_handle);
+ /* this tells the frontend that the handle is valid */
+ handle->ntvfs = h;
+ /* this moves the smbsrv_request to the smbsrv_tcon memory context */
+ talloc_steal(tcon, handle);
+ return NT_STATUS_OK;
+}
+
+void smbsrv_handle_destroy(void *private_data, struct ntvfs_handle *h)
+{
+ struct smbsrv_handle *handle = talloc_get_type(h->frontend_data.private_data,
+ struct smbsrv_handle);
+ talloc_free(handle);
+}
+
+struct ntvfs_handle *smbsrv_handle_search_by_wire_key(void *private_data, struct ntvfs_request *ntvfs, const DATA_BLOB *key)
+{
+ struct smbsrv_request *req = talloc_get_type(ntvfs->frontend_data.private_data,
+ struct smbsrv_request);
+
+ if (key->length != 2) return NULL;
+
+ return smbsrv_pull_fnum(req, key->data, 0);
+}
+
+DATA_BLOB smbsrv_handle_get_wire_key(void *private_data, struct ntvfs_handle *handle, TALLOC_CTX *mem_ctx)
+{
+ uint8_t key[2];
+
+ smbsrv_push_fnum(key, 0, handle);
+
+ return data_blob_talloc(mem_ctx, key, sizeof(key));
+}
diff --git a/source4/smb_server/smb/search.c b/source4/smb_server/smb/search.c
new file mode 100644
index 0000000000..bf4721757b
--- /dev/null
+++ b/source4/smb_server/smb/search.c
@@ -0,0 +1,283 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMBsearch handling
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) James J Myers 2003 <myersjj@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+/*
+ This file handles the parsing of transact2 requests
+*/
+
+#include "includes.h"
+#include "smb_server/smb_server.h"
+#include "ntvfs/ntvfs.h"
+
+
+/* a structure to encapsulate the state information about
+ * an in-progress search first/next operation */
+struct search_state {
+ struct smbsrv_request *req;
+ union smb_search_data *file;
+ uint16_t last_entry_offset;
+};
+
+/*
+ fill a single entry in a search find reply
+*/
+static bool find_fill_info(struct smbsrv_request *req,
+ const union smb_search_data *file)
+{
+ uint8_t *p;
+
+ if (req->out.data_size + 43 > req_max_data(req)) {
+ return false;
+ }
+
+ req_grow_data(req, req->out.data_size + 43);
+ p = req->out.data + req->out.data_size - 43;
+
+ SCVAL(p, 0, file->search.id.reserved);
+ memcpy(p+1, file->search.id.name, 11);
+ SCVAL(p, 12, file->search.id.handle);
+ SIVAL(p, 13, file->search.id.server_cookie);
+ SIVAL(p, 17, file->search.id.client_cookie);
+ SCVAL(p, 21, file->search.attrib);
+ srv_push_dos_date(req->smb_conn, p, 22, file->search.write_time);
+ SIVAL(p, 26, file->search.size);
+ memset(p+30, ' ', 12);
+ memcpy(p+30, file->search.name, MIN(strlen(file->search.name)+1, 12));
+ SCVAL(p,42,0);
+
+ return true;
+}
+
+/* callback function for search first/next */
+static bool find_callback(void *private_data, const union smb_search_data *file)
+{
+ struct search_state *state = (struct search_state *)private_data;
+
+ return find_fill_info(state->req, file);
+}
+
+/****************************************************************************
+ Reply to a search first (async reply)
+****************************************************************************/
+static void reply_search_first_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+ union smb_search_first *sf;
+
+ SMBSRV_CHECK_ASYNC_STATUS(sf, union smb_search_first);
+
+ SSVAL(req->out.vwv, VWV(0), sf->search_first.out.count);
+
+ smbsrv_send_reply(req);
+}
+
+/****************************************************************************
+ Reply to a search next (async reply)
+****************************************************************************/
+static void reply_search_next_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+ union smb_search_next *sn;
+
+ SMBSRV_CHECK_ASYNC_STATUS(sn, union smb_search_next);
+
+ SSVAL(req->out.vwv, VWV(0), sn->search_next.out.count);
+
+ smbsrv_send_reply(req);
+}
+
+/****************************************************************************
+ Reply to a search.
+****************************************************************************/
+void smbsrv_reply_search(struct smbsrv_request *req)
+{
+ union smb_search_first *sf;
+ uint16_t resume_key_length;
+ struct search_state *state;
+ uint8_t *p;
+ enum smb_search_level level = RAW_SEARCH_SEARCH;
+ uint8_t op = CVAL(req->in.hdr,HDR_COM);
+
+ if (op == SMBffirst) {
+ level = RAW_SEARCH_FFIRST;
+ } else if (op == SMBfunique) {
+ level = RAW_SEARCH_FUNIQUE;
+ }
+
+ /* parse request */
+ if (req->in.wct != 2) {
+ smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ SMBSRV_TALLOC_IO_PTR(sf, union smb_search_first);
+
+ p = req->in.data;
+ p += req_pull_ascii4(&req->in.bufinfo, &sf->search_first.in.pattern,
+ p, STR_TERMINATE);
+ if (!sf->search_first.in.pattern) {
+ smbsrv_send_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ return;
+ }
+
+ if (req_data_oob(&req->in.bufinfo, p, 3)) {
+ smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+ if (*p != 5) {
+ smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+ resume_key_length = SVAL(p, 1);
+ p += 3;
+
+ /* setup state for callback */
+ state = talloc(req, struct search_state);
+ if (!state) {
+ smbsrv_send_error(req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+
+ state->req = req;
+ state->file = NULL;
+ state->last_entry_offset = 0;
+
+ /* construct reply */
+ smbsrv_setup_reply(req, 1, 0);
+ SSVAL(req->out.vwv, VWV(0), 0);
+ req_append_var_block(req, NULL, 0);
+
+ if (resume_key_length != 0) {
+ union smb_search_next *sn;
+
+ if (resume_key_length != 21 ||
+ req_data_oob(&req->in.bufinfo, p, 21) ||
+ level == RAW_SEARCH_FUNIQUE) {
+ smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ /* do a search next operation */
+ SMBSRV_TALLOC_IO_PTR(sn, union smb_search_next);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_search_next_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ sn->search_next.in.id.reserved = CVAL(p, 0);
+ memcpy(sn->search_next.in.id.name, p+1, 11);
+ sn->search_next.in.id.handle = CVAL(p, 12);
+ sn->search_next.in.id.server_cookie = IVAL(p, 13);
+ sn->search_next.in.id.client_cookie = IVAL(p, 17);
+
+ sn->search_next.level = level;
+ sn->search_next.data_level = RAW_SEARCH_DATA_SEARCH;
+ sn->search_next.in.max_count = SVAL(req->in.vwv, VWV(0));
+ sn->search_next.in.search_attrib = SVAL(req->in.vwv, VWV(1));
+
+ /* call backend */
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_search_next(req->ntvfs, sn, state, find_callback));
+ } else {
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_search_first_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ /* do a search first operation */
+ sf->search_first.level = level;
+ sf->search_first.data_level = RAW_SEARCH_DATA_SEARCH;
+ sf->search_first.in.search_attrib = SVAL(req->in.vwv, VWV(1));
+ sf->search_first.in.max_count = SVAL(req->in.vwv, VWV(0));
+
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_search_first(req->ntvfs, sf, state, find_callback));
+ }
+}
+
+
+/****************************************************************************
+ Reply to a fclose (async reply)
+****************************************************************************/
+static void reply_fclose_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+
+ SMBSRV_CHECK_ASYNC_STATUS_SIMPLE;
+
+ /* construct reply */
+ smbsrv_setup_reply(req, 1, 0);
+
+ SSVAL(req->out.vwv, VWV(0), 0);
+
+ smbsrv_send_reply(req);
+}
+
+
+/****************************************************************************
+ Reply to fclose (stop directory search).
+****************************************************************************/
+void smbsrv_reply_fclose(struct smbsrv_request *req)
+{
+ union smb_search_close *sc;
+ uint16_t resume_key_length;
+ uint8_t *p;
+ const char *pattern;
+
+ /* parse request */
+ if (req->in.wct != 2) {
+ smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ SMBSRV_TALLOC_IO_PTR(sc, union smb_search_close);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_fclose_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ p = req->in.data;
+ p += req_pull_ascii4(&req->in.bufinfo, &pattern, p, STR_TERMINATE);
+ if (pattern && *pattern) {
+ smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ if (req_data_oob(&req->in.bufinfo, p, 3)) {
+ smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+ if (*p != 5) {
+ smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+ resume_key_length = SVAL(p, 1);
+ p += 3;
+
+ if (resume_key_length != 21) {
+ smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ if (req_data_oob(&req->in.bufinfo, p, 21)) {
+ smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ sc->fclose.level = RAW_FINDCLOSE_FCLOSE;
+ sc->fclose.in.max_count = SVAL(req->in.vwv, VWV(0));
+ sc->fclose.in.search_attrib = SVAL(req->in.vwv, VWV(1));
+ sc->fclose.in.id.reserved = CVAL(p, 0);
+ memcpy(sc->fclose.in.id.name, p+1, 11);
+ sc->fclose.in.id.handle = CVAL(p, 12);
+ sc->fclose.in.id.server_cookie = IVAL(p, 13);
+ sc->fclose.in.id.client_cookie = IVAL(p, 17);
+
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_search_close(req->ntvfs, sc));
+
+}
diff --git a/source4/smb_server/smb/service.c b/source4/smb_server/smb/service.c
new file mode 100644
index 0000000000..52471c09c9
--- /dev/null
+++ b/source4/smb_server/smb/service.c
@@ -0,0 +1,216 @@
+/*
+ Unix SMB/CIFS implementation.
+ service (connection) handling
+ Copyright (C) Andrew Tridgell 1992-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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "smb_server/smb_server.h"
+#include "smbd/service_stream.h"
+#include "ntvfs/ntvfs.h"
+#include "param/share.h"
+#include "param/param.h"
+
+/****************************************************************************
+ Make a connection, given the snum to connect to, and the vuser of the
+ connecting user if appropriate.
+****************************************************************************/
+static NTSTATUS make_connection_scfg(struct smbsrv_request *req,
+ struct share_config *scfg,
+ enum ntvfs_type type,
+ DATA_BLOB password,
+ const char *dev)
+{
+ struct smbsrv_tcon *tcon;
+ NTSTATUS status;
+ uint64_t ntvfs_caps = 0;
+
+ tcon = smbsrv_smb_tcon_new(req->smb_conn, scfg->name);
+ if (!tcon) {
+ DEBUG(0,("Couldn't find free connection.\n"));
+ return NT_STATUS_INSUFFICIENT_RESOURCES;
+ }
+ req->tcon = tcon;
+
+ if (req->smb_conn->negotiate.client_caps & CAP_LEVEL_II_OPLOCKS) {
+ ntvfs_caps |= NTVFS_CLIENT_CAP_LEVEL_II_OPLOCKS;
+ }
+
+ /* init ntvfs function pointers */
+ status = ntvfs_init_connection(tcon, scfg, type,
+ req->smb_conn->negotiate.protocol,
+ ntvfs_caps,
+ req->smb_conn->connection->event.ctx,
+ req->smb_conn->connection->msg_ctx,
+ req->smb_conn->lp_ctx,
+ req->smb_conn->connection->server_id,
+ &tcon->ntvfs);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("make_connection_scfg: connection failed for service %s\n",
+ scfg->name));
+ goto failed;
+ }
+
+ status = ntvfs_set_oplock_handler(tcon->ntvfs, smbsrv_send_oplock_break, tcon);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("make_connection: NTVFS failed to set the oplock handler!\n"));
+ goto failed;
+ }
+
+ status = ntvfs_set_addr_callbacks(tcon->ntvfs, smbsrv_get_my_addr, smbsrv_get_peer_addr, req->smb_conn);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("make_connection: NTVFS failed to set the addr callbacks!\n"));
+ goto failed;
+ }
+
+ status = ntvfs_set_handle_callbacks(tcon->ntvfs,
+ smbsrv_handle_create_new,
+ smbsrv_handle_make_valid,
+ smbsrv_handle_destroy,
+ smbsrv_handle_search_by_wire_key,
+ smbsrv_handle_get_wire_key,
+ tcon);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("make_connection: NTVFS failed to set the handle callbacks!\n"));
+ goto failed;
+ }
+
+ req->ntvfs = ntvfs_request_create(req->tcon->ntvfs, req,
+ req->session->session_info,
+ SVAL(req->in.hdr,HDR_PID),
+ req->request_time,
+ req, NULL, 0);
+ if (!req->ntvfs) {
+ status = NT_STATUS_NO_MEMORY;
+ goto failed;
+ }
+
+ /* Invoke NTVFS connection hook */
+ status = ntvfs_connect(req->ntvfs, scfg->name);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("make_connection: NTVFS make connection failed!\n"));
+ goto failed;
+ }
+
+ return NT_STATUS_OK;
+
+failed:
+ req->tcon = NULL;
+ talloc_free(tcon);
+ return status;
+}
+
+/****************************************************************************
+ Make a connection to a service.
+ *
+ * @param service
+****************************************************************************/
+static NTSTATUS make_connection(struct smbsrv_request *req,
+ const char *service, DATA_BLOB password,
+ const char *dev)
+{
+ NTSTATUS status;
+ enum ntvfs_type type;
+ const char *type_str;
+ struct share_config *scfg;
+ const char *sharetype;
+
+ /* the service might be of the form \\SERVER\SHARE. Should we put
+ the server name we get from this somewhere? */
+ if (strncmp(service, "\\\\", 2) == 0) {
+ char *p = strchr(service+2, '\\');
+ if (p) {
+ service = p + 1;
+ }
+ }
+
+ status = share_get_config(req, req->smb_conn->share_context, service, &scfg);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("make_connection: couldn't find service %s\n", service));
+ return NT_STATUS_BAD_NETWORK_NAME;
+ }
+
+ /* TODO: check the password, when it's share level security! */
+
+ if (!socket_check_access(req->smb_conn->connection->socket,
+ scfg->name,
+ share_string_list_option(req, scfg, SHARE_HOSTS_ALLOW),
+ share_string_list_option(req, scfg, SHARE_HOSTS_DENY))) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* work out what sort of connection this is */
+ sharetype = share_string_option(scfg, "type", "DISK");
+ if (sharetype && strcmp(sharetype, "IPC") == 0) {
+ type = NTVFS_IPC;
+ type_str = "IPC";
+ } else if (sharetype && strcmp(sharetype, "PRINTER") == 0) {
+ type = NTVFS_PRINT;
+ type_str = "LPT:";
+ } else {
+ type = NTVFS_DISK;
+ type_str = "A:";
+ }
+
+ if (strcmp(dev, "?????") != 0 && strcasecmp(type_str, dev) != 0) {
+ /* the client gave us the wrong device type */
+ return NT_STATUS_BAD_DEVICE_TYPE;
+ }
+
+ return make_connection_scfg(req, scfg, type, password, dev);
+}
+
+/*
+ backend for tree connect call
+*/
+NTSTATUS smbsrv_tcon_backend(struct smbsrv_request *req, union smb_tcon *con)
+{
+ NTSTATUS status;
+
+ if (con->generic.level == RAW_TCON_TCON) {
+ DATA_BLOB password;
+ password = data_blob_string_const(con->tcon.in.password);
+
+ status = make_connection(req, con->tcon.in.service, password, con->tcon.in.dev);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ con->tcon.out.max_xmit = req->smb_conn->negotiate.max_recv;
+ con->tcon.out.tid = req->tcon->tid;
+
+ return status;
+ }
+
+ /* TODO: take a look at tconx.in.flags! */
+
+ status = make_connection(req, con->tconx.in.path, con->tconx.in.password,
+ con->tconx.in.device);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ con->tconx.out.tid = req->tcon->tid;
+ con->tconx.out.dev_type = talloc_strdup(req, req->tcon->ntvfs->dev_type);
+ con->tconx.out.fs_type = talloc_strdup(req, req->tcon->ntvfs->fs_type);
+ con->tconx.out.options = SMB_SUPPORT_SEARCH_BITS | (share_int_option(req->tcon->ntvfs->config, SHARE_CSC_POLICY, SHARE_CSC_POLICY_DEFAULT) << 2);
+ if (share_bool_option(req->tcon->ntvfs->config, SHARE_MSDFS_ROOT, SHARE_MSDFS_ROOT_DEFAULT) && lp_host_msdfs(req->smb_conn->lp_ctx)) {
+ con->tconx.out.options |= SMB_SHARE_IN_DFS;
+ }
+
+ return status;
+}
diff --git a/source4/smb_server/smb/sesssetup.c b/source4/smb_server/smb/sesssetup.c
new file mode 100644
index 0000000000..e38447703b
--- /dev/null
+++ b/source4/smb_server/smb/sesssetup.c
@@ -0,0 +1,448 @@
+
+/*
+ Unix SMB/CIFS implementation.
+ handle SMBsessionsetup
+ Copyright (C) Andrew Tridgell 1998-2001
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-2005
+ Copyright (C) Jim McDonough 2002
+ Copyright (C) Luke Howard 2003
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "version.h"
+#include "auth/credentials/credentials.h"
+#include "auth/gensec/gensec.h"
+#include "auth/auth.h"
+#include "smb_server/smb_server.h"
+#include "smbd/service_stream.h"
+#include "librpc/gen_ndr/nbt.h"
+#include "param/param.h"
+
+/*
+ setup the OS, Lanman and domain portions of a session setup reply
+*/
+static void sesssetup_common_strings(struct smbsrv_request *req,
+ char **os, char **lanman, char **domain)
+{
+ (*os) = talloc_asprintf(req, "Unix");
+ (*lanman) = talloc_asprintf(req, "Samba %s", SAMBA_VERSION_STRING);
+ (*domain) = talloc_asprintf(req, "%s",
+ lp_workgroup(req->smb_conn->lp_ctx));
+}
+
+static void smbsrv_sesssetup_backend_send(struct smbsrv_request *req,
+ union smb_sesssetup *sess,
+ NTSTATUS status)
+{
+ if (NT_STATUS_IS_OK(status)) {
+ req->smb_conn->negotiate.done_sesssetup = true;
+ /* we need to keep the session long term */
+ req->session = talloc_steal(req->smb_conn, req->session);
+ }
+ smbsrv_reply_sesssetup_send(req, sess, status);
+}
+
+static void sesssetup_old_send(struct auth_check_password_request *areq,
+ void *private_data)
+{
+ struct smbsrv_request *req = talloc_get_type(private_data, struct smbsrv_request);
+ union smb_sesssetup *sess = talloc_get_type(req->io_ptr, union smb_sesssetup);
+ struct auth_serversupplied_info *server_info = NULL;
+ struct auth_session_info *session_info;
+ struct smbsrv_session *smb_sess;
+ NTSTATUS status;
+
+ status = auth_check_password_recv(areq, req, &server_info);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+
+ /* This references server_info into session_info */
+ status = auth_generate_session_info(req, req->smb_conn->connection->event.ctx, req->smb_conn->lp_ctx,
+ server_info, &session_info);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+
+ /* allocate a new session */
+ smb_sess = smbsrv_session_new(req->smb_conn, req, NULL);
+ if (!smb_sess) {
+ status = NT_STATUS_INSUFFICIENT_RESOURCES;
+ goto failed;
+ }
+
+ /* Ensure this is marked as a 'real' vuid, not one
+ * simply valid for the session setup leg */
+ status = smbsrv_session_sesssetup_finished(smb_sess, session_info);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+
+ /* To correctly process any AndX packet (like a tree connect)
+ * we need to fill in the session on the request here */
+ req->session = smb_sess;
+ sess->old.out.vuid = smb_sess->vuid;
+
+failed:
+ status = auth_nt_status_squash(status);
+ smbsrv_sesssetup_backend_send(req, sess, status);
+}
+
+/*
+ handler for old style session setup
+*/
+static void sesssetup_old(struct smbsrv_request *req, union smb_sesssetup *sess)
+{
+ struct auth_usersupplied_info *user_info = NULL;
+ struct socket_address *remote_address;
+ const char *remote_machine = NULL;
+
+ sess->old.out.vuid = 0;
+ sess->old.out.action = 0;
+
+ sesssetup_common_strings(req,
+ &sess->old.out.os,
+ &sess->old.out.lanman,
+ &sess->old.out.domain);
+
+ if (!req->smb_conn->negotiate.done_sesssetup) {
+ req->smb_conn->negotiate.max_send = sess->old.in.bufsize;
+ }
+
+ if (req->smb_conn->negotiate.calling_name) {
+ remote_machine = req->smb_conn->negotiate.calling_name->name;
+ }
+
+ remote_address = socket_get_peer_addr(req->smb_conn->connection->socket, req);
+ if (!remote_address) goto nomem;
+
+ if (!remote_machine) {
+ remote_machine = remote_address->addr;
+ }
+
+ user_info = talloc(req, struct auth_usersupplied_info);
+ if (!user_info) goto nomem;
+
+ user_info->mapped_state = false;
+ user_info->logon_parameters = 0;
+ user_info->flags = 0;
+ user_info->client.account_name = sess->old.in.user;
+ user_info->client.domain_name = sess->old.in.domain;
+ user_info->workstation_name = remote_machine;
+ user_info->remote_host = talloc_steal(user_info, remote_address);
+
+ user_info->password_state = AUTH_PASSWORD_RESPONSE;
+ user_info->password.response.lanman = sess->old.in.password;
+ user_info->password.response.lanman.data = talloc_steal(user_info, sess->old.in.password.data);
+ user_info->password.response.nt = data_blob(NULL, 0);
+
+ auth_check_password_send(req->smb_conn->negotiate.auth_context, user_info,
+ sesssetup_old_send, req);
+ return;
+
+nomem:
+ smbsrv_sesssetup_backend_send(req, sess, NT_STATUS_NO_MEMORY);
+}
+
+static void sesssetup_nt1_send(struct auth_check_password_request *areq,
+ void *private_data)
+{
+ struct smbsrv_request *req = talloc_get_type(private_data, struct smbsrv_request);
+ union smb_sesssetup *sess = talloc_get_type(req->io_ptr, union smb_sesssetup);
+ struct auth_serversupplied_info *server_info = NULL;
+ struct auth_session_info *session_info;
+ struct smbsrv_session *smb_sess;
+ NTSTATUS status;
+
+ status = auth_check_password_recv(areq, req, &server_info);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+
+ /* This references server_info into session_info */
+ status = auth_generate_session_info(req, req->smb_conn->connection->event.ctx,
+ req->smb_conn->lp_ctx,
+ server_info, &session_info);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+
+ /* allocate a new session */
+ smb_sess = smbsrv_session_new(req->smb_conn, req, NULL);
+ if (!smb_sess) {
+ status = NT_STATUS_INSUFFICIENT_RESOURCES;
+ goto failed;
+ }
+
+ /* Ensure this is marked as a 'real' vuid, not one
+ * simply valid for the session setup leg */
+ status = smbsrv_session_sesssetup_finished(smb_sess, session_info);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+
+ /* To correctly process any AndX packet (like a tree connect)
+ * we need to fill in the session on the request here */
+ req->session = smb_sess;
+ sess->nt1.out.vuid = smb_sess->vuid;
+
+ if (!smbsrv_setup_signing(req->smb_conn, &session_info->session_key, &sess->nt1.in.password2)) {
+ /* Already signing, or disabled */
+ goto done;
+ }
+
+done:
+ status = NT_STATUS_OK;
+failed:
+ status = auth_nt_status_squash(status);
+ smbsrv_sesssetup_backend_send(req, sess, status);
+}
+
+/*
+ handler for NT1 style session setup
+*/
+static void sesssetup_nt1(struct smbsrv_request *req, union smb_sesssetup *sess)
+{
+ NTSTATUS status;
+ struct auth_context *auth_context;
+ struct auth_usersupplied_info *user_info = NULL;
+ struct socket_address *remote_address;
+ const char *remote_machine = NULL;
+
+ sess->nt1.out.vuid = 0;
+ sess->nt1.out.action = 0;
+
+ sesssetup_common_strings(req,
+ &sess->nt1.out.os,
+ &sess->nt1.out.lanman,
+ &sess->nt1.out.domain);
+
+ if (!req->smb_conn->negotiate.done_sesssetup) {
+ req->smb_conn->negotiate.max_send = sess->nt1.in.bufsize;
+ req->smb_conn->negotiate.client_caps = sess->nt1.in.capabilities;
+ }
+
+ if (req->smb_conn->negotiate.oid) {
+ if (sess->nt1.in.user && *sess->nt1.in.user) {
+ /* We can't accept a normal login, because we
+ * don't have a challenge */
+ status = NT_STATUS_LOGON_FAILURE;
+ goto failed;
+ }
+
+ /* TODO: should we use just "anonymous" here? */
+ status = auth_context_create(req,
+ req->smb_conn->connection->event.ctx,
+ req->smb_conn->connection->msg_ctx,
+ req->smb_conn->lp_ctx,
+ &auth_context);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+ } else {
+ auth_context = req->smb_conn->negotiate.auth_context;
+ }
+
+ if (req->smb_conn->negotiate.calling_name) {
+ remote_machine = req->smb_conn->negotiate.calling_name->name;
+ }
+
+ remote_address = socket_get_peer_addr(req->smb_conn->connection->socket, req);
+ if (!remote_address) goto nomem;
+
+ if (!remote_machine) {
+ remote_machine = remote_address->addr;
+ }
+
+ user_info = talloc(req, struct auth_usersupplied_info);
+ if (!user_info) goto nomem;
+
+ user_info->mapped_state = false;
+ user_info->logon_parameters = 0;
+ user_info->flags = 0;
+ user_info->client.account_name = sess->nt1.in.user;
+ user_info->client.domain_name = sess->nt1.in.domain;
+ user_info->workstation_name = remote_machine;
+ user_info->remote_host = talloc_steal(user_info, remote_address);
+
+ user_info->password_state = AUTH_PASSWORD_RESPONSE;
+ user_info->password.response.lanman = sess->nt1.in.password1;
+ user_info->password.response.lanman.data = talloc_steal(user_info, sess->nt1.in.password1.data);
+ user_info->password.response.nt = sess->nt1.in.password2;
+ user_info->password.response.nt.data = talloc_steal(user_info, sess->nt1.in.password2.data);
+
+ auth_check_password_send(auth_context, user_info,
+ sesssetup_nt1_send, req);
+ return;
+
+nomem:
+ status = NT_STATUS_NO_MEMORY;
+failed:
+ status = auth_nt_status_squash(status);
+ smbsrv_sesssetup_backend_send(req, sess, status);
+}
+
+struct sesssetup_spnego_state {
+ struct smbsrv_request *req;
+ union smb_sesssetup *sess;
+ struct smbsrv_session *smb_sess;
+};
+
+static void sesssetup_spnego_send(struct gensec_update_request *greq, void *private_data)
+{
+ struct sesssetup_spnego_state *s = talloc_get_type(private_data,
+ struct sesssetup_spnego_state);
+ struct smbsrv_request *req = s->req;
+ union smb_sesssetup *sess = s->sess;
+ struct smbsrv_session *smb_sess = s->smb_sess;
+ struct auth_session_info *session_info = NULL;
+ NTSTATUS status;
+ NTSTATUS skey_status;
+ DATA_BLOB session_key;
+
+ status = gensec_update_recv(greq, req, &sess->spnego.out.secblob);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ goto done;
+ } else if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+
+ status = gensec_session_info(smb_sess->gensec_ctx, &session_info);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+
+ skey_status = gensec_session_key(smb_sess->gensec_ctx, &session_key);
+ if (NT_STATUS_IS_OK(skey_status)) {
+ smbsrv_setup_signing(req->smb_conn, &session_key, NULL);
+ }
+
+ /* Ensure this is marked as a 'real' vuid, not one
+ * simply valid for the session setup leg */
+ status = smbsrv_session_sesssetup_finished(smb_sess, session_info);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+
+ req->session = smb_sess;
+
+done:
+ sess->spnego.out.vuid = smb_sess->vuid;
+failed:
+ status = auth_nt_status_squash(status);
+ smbsrv_sesssetup_backend_send(req, sess, status);
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ talloc_free(smb_sess);
+ }
+}
+
+/*
+ handler for SPNEGO style session setup
+*/
+static void sesssetup_spnego(struct smbsrv_request *req, union smb_sesssetup *sess)
+{
+ NTSTATUS status;
+ struct smbsrv_session *smb_sess = NULL;
+ struct sesssetup_spnego_state *s = NULL;
+ uint16_t vuid;
+
+ sess->spnego.out.vuid = 0;
+ sess->spnego.out.action = 0;
+
+ sesssetup_common_strings(req,
+ &sess->spnego.out.os,
+ &sess->spnego.out.lanman,
+ &sess->spnego.out.workgroup);
+
+ if (!req->smb_conn->negotiate.done_sesssetup) {
+ req->smb_conn->negotiate.max_send = sess->spnego.in.bufsize;
+ req->smb_conn->negotiate.client_caps = sess->spnego.in.capabilities;
+ }
+
+ vuid = SVAL(req->in.hdr,HDR_UID);
+
+ /* lookup an existing session */
+ smb_sess = smbsrv_session_find_sesssetup(req->smb_conn, vuid);
+ if (!smb_sess) {
+ struct gensec_security *gensec_ctx;
+
+ status = samba_server_gensec_start(req,
+ req->smb_conn->connection->event.ctx,
+ req->smb_conn->connection->msg_ctx,
+ req->smb_conn->lp_ctx,
+ req->smb_conn->negotiate.server_credentials,
+ "cifs",
+ &gensec_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to start GENSEC server code: %s\n", nt_errstr(status)));
+ goto failed;
+ }
+
+ gensec_want_feature(gensec_ctx, GENSEC_FEATURE_SESSION_KEY);
+
+ status = gensec_start_mech_by_oid(gensec_ctx, req->smb_conn->negotiate.oid);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to start GENSEC %s server code: %s\n",
+ gensec_get_name_by_oid(gensec_ctx, req->smb_conn->negotiate.oid), nt_errstr(status)));
+ goto failed;
+ }
+
+ /* allocate a new session */
+ smb_sess = smbsrv_session_new(req->smb_conn, req->smb_conn, gensec_ctx);
+ if (!smb_sess) {
+ status = NT_STATUS_INSUFFICIENT_RESOURCES;
+ goto failed;
+ }
+ }
+
+ if (!smb_sess) {
+ status = NT_STATUS_ACCESS_DENIED;
+ goto failed;
+ }
+
+ if (!smb_sess->gensec_ctx) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ DEBUG(1, ("Internal ERROR: no gensec_ctx on session: %s\n", nt_errstr(status)));
+ goto failed;
+ }
+
+ s = talloc(req, struct sesssetup_spnego_state);
+ if (!s) goto nomem;
+ s->req = req;
+ s->sess = sess;
+ s->smb_sess = smb_sess;
+
+ gensec_update_send(smb_sess->gensec_ctx, sess->spnego.in.secblob,
+ sesssetup_spnego_send, s);
+ return;
+
+nomem:
+ status = NT_STATUS_NO_MEMORY;
+failed:
+ talloc_free(smb_sess);
+ status = auth_nt_status_squash(status);
+ smbsrv_sesssetup_backend_send(req, sess, status);
+}
+
+/*
+ backend for sessionsetup call - this takes all 3 variants of the call
+*/
+void smbsrv_sesssetup_backend(struct smbsrv_request *req,
+ union smb_sesssetup *sess)
+{
+ switch (sess->old.level) {
+ case RAW_SESSSETUP_OLD:
+ sesssetup_old(req, sess);
+ return;
+
+ case RAW_SESSSETUP_NT1:
+ sesssetup_nt1(req, sess);
+ return;
+
+ case RAW_SESSSETUP_SPNEGO:
+ sesssetup_spnego(req, sess);
+ return;
+
+ case RAW_SESSSETUP_SMB2:
+ break;
+ }
+
+ smbsrv_sesssetup_backend_send(req, sess, NT_STATUS_INVALID_LEVEL);
+}
diff --git a/source4/smb_server/smb/signing.c b/source4/smb_server/smb/signing.c
new file mode 100644
index 0000000000..0b5cf56fdb
--- /dev/null
+++ b/source4/smb_server/smb/signing.c
@@ -0,0 +1,173 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "smb_server/smb_server.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "param/param.h"
+
+
+/*
+ sign an outgoing packet
+*/
+void smbsrv_sign_packet(struct smbsrv_request *req)
+{
+#if 0
+ /* enable this when packet signing is preventing you working out why valgrind
+ says that data is uninitialised */
+ file_save("pkt.dat", req->out.buffer, req->out.size);
+#endif
+
+ switch (req->smb_conn->signing.signing_state) {
+ case SMB_SIGNING_ENGINE_OFF:
+ break;
+
+ case SMB_SIGNING_ENGINE_BSRSPYL:
+ /* mark the packet as signed - BEFORE we sign it...*/
+ mark_packet_signed(&req->out);
+
+ /* I wonder what BSRSPYL stands for - but this is what MS
+ actually sends! */
+ memcpy((req->out.hdr + HDR_SS_FIELD), "BSRSPYL ", 8);
+ break;
+
+ case SMB_SIGNING_ENGINE_ON:
+
+ sign_outgoing_message(&req->out,
+ &req->smb_conn->signing.mac_key,
+ req->seq_num+1);
+ break;
+ }
+ return;
+}
+
+
+
+/*
+ setup the signing key for a connection. Called after authentication succeeds
+ in a session setup
+*/
+bool smbsrv_setup_signing(struct smbsrv_connection *smb_conn,
+ DATA_BLOB *session_key,
+ DATA_BLOB *response)
+{
+ if (!set_smb_signing_common(&smb_conn->signing)) {
+ return false;
+ }
+ return smbcli_simple_set_signing(smb_conn,
+ &smb_conn->signing, session_key, response);
+}
+
+bool smbsrv_init_signing(struct smbsrv_connection *smb_conn)
+{
+ smb_conn->signing.mac_key = data_blob(NULL, 0);
+ if (!smbcli_set_signing_off(&smb_conn->signing)) {
+ return false;
+ }
+
+ switch (lp_server_signing(smb_conn->lp_ctx)) {
+ case SMB_SIGNING_OFF:
+ smb_conn->signing.allow_smb_signing = false;
+ break;
+ case SMB_SIGNING_SUPPORTED:
+ smb_conn->signing.allow_smb_signing = true;
+ break;
+ case SMB_SIGNING_REQUIRED:
+ smb_conn->signing.allow_smb_signing = true;
+ smb_conn->signing.mandatory_signing = true;
+ break;
+ case SMB_SIGNING_AUTO:
+ /* If we are a domain controller, SMB signing is
+ * really important, as it can prevent a number of
+ * attacks on communications between us and the
+ * clients */
+
+ if (lp_server_role(smb_conn->lp_ctx) == ROLE_DOMAIN_CONTROLLER) {
+ smb_conn->signing.allow_smb_signing = true;
+ smb_conn->signing.mandatory_signing = true;
+ } else {
+ /* However, it really sucks (no sendfile, CPU
+ * overhead) performance-wise when used on a
+ * file server, so disable it by default (auto
+ * is the default) on non-DCs */
+ smb_conn->signing.allow_smb_signing = false;
+ }
+ break;
+ }
+ return true;
+}
+
+/*
+ allocate a sequence number to a request
+*/
+static void req_signing_alloc_seq_num(struct smbsrv_request *req)
+{
+ req->seq_num = req->smb_conn->signing.next_seq_num;
+
+ if (req->smb_conn->signing.signing_state != SMB_SIGNING_ENGINE_OFF) {
+ req->smb_conn->signing.next_seq_num += 2;
+ }
+}
+
+/*
+ called for requests that do not produce a reply of their own
+*/
+void smbsrv_signing_no_reply(struct smbsrv_request *req)
+{
+ if (req->smb_conn->signing.signing_state != SMB_SIGNING_ENGINE_OFF) {
+ req->smb_conn->signing.next_seq_num--;
+ }
+}
+
+/***********************************************************
+ SMB signing - Simple implementation - check a MAC sent by client
+************************************************************/
+/**
+ * Check a packet supplied by the server.
+ * @return false if we had an established signing connection
+ * which had a back checksum, true otherwise
+ */
+bool smbsrv_signing_check_incoming(struct smbsrv_request *req)
+{
+ bool good;
+
+ req_signing_alloc_seq_num(req);
+
+ switch (req->smb_conn->signing.signing_state)
+ {
+ case SMB_SIGNING_ENGINE_OFF:
+ return true;
+ case SMB_SIGNING_ENGINE_BSRSPYL:
+ case SMB_SIGNING_ENGINE_ON:
+ {
+ if (req->in.size < (HDR_SS_FIELD + 8)) {
+ return false;
+ } else {
+ good = check_signed_incoming_message(&req->in,
+ &req->smb_conn->signing.mac_key,
+ req->seq_num);
+
+ return signing_good(&req->smb_conn->signing,
+ req->seq_num+1, good);
+ }
+ }
+ }
+ return false;
+}
diff --git a/source4/smb_server/smb/srvtime.c b/source4/smb_server/smb/srvtime.c
new file mode 100644
index 0000000000..7aee9077be
--- /dev/null
+++ b/source4/smb_server/smb/srvtime.c
@@ -0,0 +1,82 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ server side time handling
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "smb_server/smb_server.h"
+
+
+/*******************************************************************
+put a dos date into a buffer (time/date format)
+This takes GMT time and puts local time for zone_offset in the buffer
+********************************************************************/
+void srv_push_dos_date(struct smbsrv_connection *smb_server,
+ uint8_t *buf, int offset, time_t unixdate)
+{
+ push_dos_date(buf, offset, unixdate, smb_server->negotiate.zone_offset);
+}
+
+/*******************************************************************
+put a dos date into a buffer (date/time format)
+This takes GMT time and puts local time in the buffer
+********************************************************************/
+void srv_push_dos_date2(struct smbsrv_connection *smb_server,
+ uint8_t *buf, int offset, time_t unixdate)
+{
+ push_dos_date2(buf, offset, unixdate, smb_server->negotiate.zone_offset);
+}
+
+/*******************************************************************
+put a dos 32 bit "unix like" date into a buffer. This routine takes
+GMT and converts it to LOCAL time in zone_offset before putting it
+********************************************************************/
+void srv_push_dos_date3(struct smbsrv_connection *smb_server,
+ uint8_t *buf, int offset, time_t unixdate)
+{
+ push_dos_date3(buf, offset, unixdate, smb_server->negotiate.zone_offset);
+}
+
+/*******************************************************************
+convert a dos date
+********************************************************************/
+time_t srv_pull_dos_date(struct smbsrv_connection *smb_server,
+ const uint8_t *date_ptr)
+{
+ return pull_dos_date(date_ptr, smb_server->negotiate.zone_offset);
+}
+
+/*******************************************************************
+like srv_pull_dos_date() but the words are reversed
+********************************************************************/
+time_t srv_pull_dos_date2(struct smbsrv_connection *smb_server,
+ const uint8_t *date_ptr)
+{
+ return pull_dos_date2(date_ptr, smb_server->negotiate.zone_offset);
+}
+
+/*******************************************************************
+ create a unix GMT date from a dos date in 32 bit "unix like" format
+ these arrive in server zone, with corresponding DST
+ ******************************************************************/
+time_t srv_pull_dos_date3(struct smbsrv_connection *smb_server,
+ const uint8_t *date_ptr)
+{
+ return pull_dos_date3(date_ptr, smb_server->negotiate.zone_offset);
+}
diff --git a/source4/smb_server/smb/trans2.c b/source4/smb_server/smb/trans2.c
new file mode 100644
index 0000000000..e0f6438dc6
--- /dev/null
+++ b/source4/smb_server/smb/trans2.c
@@ -0,0 +1,1428 @@
+/*
+ Unix SMB/CIFS implementation.
+ transaction2 handling
+ 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 <http://www.gnu.org/licenses/>.
+*/
+/*
+ This file handles the parsing of transact2 requests
+*/
+
+#include "includes.h"
+#include "../lib/util/dlinklist.h"
+#include "smb_server/smb_server.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "ntvfs/ntvfs.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+
+#define TRANS2_CHECK_ASYNC_STATUS_SIMPLE do { \
+ if (!NT_STATUS_IS_OK(req->ntvfs->async_states->status)) { \
+ trans2_setup_reply(trans, 0, 0, 0);\
+ return req->ntvfs->async_states->status; \
+ } \
+} while (0)
+#define TRANS2_CHECK_ASYNC_STATUS(ptr, type) do { \
+ TRANS2_CHECK_ASYNC_STATUS_SIMPLE; \
+ ptr = talloc_get_type(op->op_info, type); \
+} while (0)
+#define TRANS2_CHECK(cmd) do { \
+ NTSTATUS _status; \
+ _status = cmd; \
+ NT_STATUS_NOT_OK_RETURN(_status); \
+} while (0)
+
+/*
+ hold the state of a nttrans op while in progress. Needed to allow for async backend
+ functions.
+*/
+struct trans_op {
+ struct smbsrv_request *req;
+ struct smb_trans2 *trans;
+ uint8_t command;
+ NTSTATUS (*send_fn)(struct trans_op *);
+ void *op_info;
+};
+
+#define CHECK_MIN_BLOB_SIZE(blob, size) do { \
+ if ((blob)->length < (size)) { \
+ return NT_STATUS_INFO_LENGTH_MISMATCH; \
+ }} while (0)
+
+/* setup a trans2 reply, given the data and params sizes */
+static NTSTATUS trans2_setup_reply(struct smb_trans2 *trans,
+ uint16_t param_size, uint16_t data_size,
+ uint8_t setup_count)
+{
+ trans->out.setup_count = setup_count;
+ if (setup_count > 0) {
+ trans->out.setup = talloc_zero_array(trans, uint16_t, setup_count);
+ NT_STATUS_HAVE_NO_MEMORY(trans->out.setup);
+ }
+ trans->out.params = data_blob_talloc(trans, NULL, param_size);
+ if (param_size > 0) NT_STATUS_HAVE_NO_MEMORY(trans->out.params.data);
+
+ trans->out.data = data_blob_talloc(trans, NULL, data_size);
+ if (data_size > 0) NT_STATUS_HAVE_NO_MEMORY(trans->out.data.data);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS trans2_push_fsinfo(struct smbsrv_connection *smb_conn,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *blob,
+ union smb_fsinfo *fsinfo,
+ int default_str_flags)
+{
+ enum smb_fsinfo_level passthru_level;
+
+ switch (fsinfo->generic.level) {
+ case RAW_QFS_ALLOCATION:
+ TRANS2_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, 18));
+
+ SIVAL(blob->data, 0, fsinfo->allocation.out.fs_id);
+ SIVAL(blob->data, 4, fsinfo->allocation.out.sectors_per_unit);
+ SIVAL(blob->data, 8, fsinfo->allocation.out.total_alloc_units);
+ SIVAL(blob->data, 12, fsinfo->allocation.out.avail_alloc_units);
+ SSVAL(blob->data, 16, fsinfo->allocation.out.bytes_per_sector);
+
+ return NT_STATUS_OK;
+
+ case RAW_QFS_VOLUME:
+ TRANS2_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, 5));
+
+ SIVAL(blob->data, 0, fsinfo->volume.out.serial_number);
+ /* w2k3 implements this incorrectly for unicode - it
+ * leaves the last byte off the string */
+ TRANS2_CHECK(smbsrv_blob_append_string(mem_ctx, blob,
+ fsinfo->volume.out.volume_name.s,
+ 4, default_str_flags,
+ STR_LEN8BIT|STR_NOALIGN));
+
+ return NT_STATUS_OK;
+
+ case RAW_QFS_VOLUME_INFO:
+ passthru_level = RAW_QFS_VOLUME_INFORMATION;
+ break;
+
+ case RAW_QFS_SIZE_INFO:
+ passthru_level = RAW_QFS_SIZE_INFORMATION;
+ break;
+
+ case RAW_QFS_DEVICE_INFO:
+ passthru_level = RAW_QFS_DEVICE_INFORMATION;
+ break;
+
+ case RAW_QFS_ATTRIBUTE_INFO:
+ passthru_level = RAW_QFS_ATTRIBUTE_INFORMATION;
+ break;
+
+ default:
+ passthru_level = fsinfo->generic.level;
+ break;
+ }
+
+ return smbsrv_push_passthru_fsinfo(mem_ctx, blob,
+ passthru_level, fsinfo,
+ default_str_flags);
+}
+
+/*
+ trans2 qfsinfo implementation send
+*/
+static NTSTATUS trans2_qfsinfo_send(struct trans_op *op)
+{
+ struct smbsrv_request *req = op->req;
+ struct smb_trans2 *trans = op->trans;
+ union smb_fsinfo *fsinfo;
+
+ TRANS2_CHECK_ASYNC_STATUS(fsinfo, union smb_fsinfo);
+
+ TRANS2_CHECK(trans2_setup_reply(trans, 0, 0, 0));
+
+ TRANS2_CHECK(trans2_push_fsinfo(req->smb_conn, trans,
+ &trans->out.data, fsinfo,
+ SMBSRV_REQ_DEFAULT_STR_FLAGS(req)));
+
+ return NT_STATUS_OK;
+}
+
+/*
+ trans2 qfsinfo implementation
+*/
+static NTSTATUS trans2_qfsinfo(struct smbsrv_request *req, struct trans_op *op)
+{
+ struct smb_trans2 *trans = op->trans;
+ union smb_fsinfo *fsinfo;
+ uint16_t level;
+
+ /* make sure we got enough parameters */
+ if (trans->in.params.length != 2) {
+ return NT_STATUS_FOOBAR;
+ }
+
+ fsinfo = talloc(op, union smb_fsinfo);
+ NT_STATUS_HAVE_NO_MEMORY(fsinfo);
+
+ level = SVAL(trans->in.params.data, 0);
+
+ /* work out the backend level - we make it 1-1 in the header */
+ fsinfo->generic.level = (enum smb_fsinfo_level)level;
+ if (fsinfo->generic.level >= RAW_QFS_GENERIC) {
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ op->op_info = fsinfo;
+ op->send_fn = trans2_qfsinfo_send;
+
+ return ntvfs_fsinfo(req->ntvfs, fsinfo);
+}
+
+
+/*
+ trans2 open implementation send
+*/
+static NTSTATUS trans2_open_send(struct trans_op *op)
+{
+ struct smbsrv_request *req = op->req;
+ struct smb_trans2 *trans = op->trans;
+ union smb_open *io;
+
+ TRANS2_CHECK_ASYNC_STATUS(io, union smb_open);
+
+ TRANS2_CHECK(trans2_setup_reply(trans, 30, 0, 0));
+
+ smbsrv_push_fnum(trans->out.params.data, VWV(0), io->t2open.out.file.ntvfs);
+ SSVAL(trans->out.params.data, VWV(1), io->t2open.out.attrib);
+ srv_push_dos_date3(req->smb_conn, trans->out.params.data,
+ VWV(2), io->t2open.out.write_time);
+ SIVAL(trans->out.params.data, VWV(4), io->t2open.out.size);
+ SSVAL(trans->out.params.data, VWV(6), io->t2open.out.access);
+ SSVAL(trans->out.params.data, VWV(7), io->t2open.out.ftype);
+ SSVAL(trans->out.params.data, VWV(8), io->t2open.out.devstate);
+ SSVAL(trans->out.params.data, VWV(9), io->t2open.out.action);
+ SIVAL(trans->out.params.data, VWV(10), 0); /* reserved */
+ SSVAL(trans->out.params.data, VWV(12), 0); /* EaErrorOffset */
+ SIVAL(trans->out.params.data, VWV(13), 0); /* EaLength */
+
+ return NT_STATUS_OK;
+}
+
+/*
+ trans2 open implementation
+*/
+static NTSTATUS trans2_open(struct smbsrv_request *req, struct trans_op *op)
+{
+ struct smb_trans2 *trans = op->trans;
+ union smb_open *io;
+
+ /* make sure we got enough parameters */
+ if (trans->in.params.length < 29) {
+ return NT_STATUS_FOOBAR;
+ }
+
+ io = talloc(op, union smb_open);
+ NT_STATUS_HAVE_NO_MEMORY(io);
+
+ io->t2open.level = RAW_OPEN_T2OPEN;
+ io->t2open.in.flags = SVAL(trans->in.params.data, VWV(0));
+ io->t2open.in.open_mode = SVAL(trans->in.params.data, VWV(1));
+ io->t2open.in.search_attrs = SVAL(trans->in.params.data, VWV(2));
+ io->t2open.in.file_attrs = SVAL(trans->in.params.data, VWV(3));
+ io->t2open.in.write_time = srv_pull_dos_date(req->smb_conn,
+ trans->in.params.data + VWV(4));
+ io->t2open.in.open_func = SVAL(trans->in.params.data, VWV(6));
+ io->t2open.in.size = IVAL(trans->in.params.data, VWV(7));
+ io->t2open.in.timeout = IVAL(trans->in.params.data, VWV(9));
+ io->t2open.in.num_eas = 0;
+ io->t2open.in.eas = NULL;
+
+ smbsrv_blob_pull_string(&req->in.bufinfo, &trans->in.params, 28, &io->t2open.in.fname, 0);
+ if (io->t2open.in.fname == NULL) {
+ return NT_STATUS_FOOBAR;
+ }
+
+ TRANS2_CHECK(ea_pull_list(&trans->in.data, io, &io->t2open.in.num_eas, &io->t2open.in.eas));
+
+ op->op_info = io;
+ op->send_fn = trans2_open_send;
+
+ return ntvfs_open(req->ntvfs, io);
+}
+
+
+/*
+ trans2 simple send
+*/
+static NTSTATUS trans2_simple_send(struct trans_op *op)
+{
+ struct smbsrv_request *req = op->req;
+ struct smb_trans2 *trans = op->trans;
+
+ TRANS2_CHECK_ASYNC_STATUS_SIMPLE;
+
+ TRANS2_CHECK(trans2_setup_reply(trans, 2, 0, 0));
+
+ SSVAL(trans->out.params.data, VWV(0), 0);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ trans2 mkdir implementation
+*/
+static NTSTATUS trans2_mkdir(struct smbsrv_request *req, struct trans_op *op)
+{
+ struct smb_trans2 *trans = op->trans;
+ union smb_mkdir *io;
+
+ /* make sure we got enough parameters */
+ if (trans->in.params.length < 5) {
+ return NT_STATUS_FOOBAR;
+ }
+
+ io = talloc(op, union smb_mkdir);
+ NT_STATUS_HAVE_NO_MEMORY(io);
+
+ io->t2mkdir.level = RAW_MKDIR_T2MKDIR;
+ smbsrv_blob_pull_string(&req->in.bufinfo, &trans->in.params, 4, &io->t2mkdir.in.path, 0);
+ if (io->t2mkdir.in.path == NULL) {
+ return NT_STATUS_FOOBAR;
+ }
+
+ TRANS2_CHECK(ea_pull_list(&trans->in.data, io,
+ &io->t2mkdir.in.num_eas,
+ &io->t2mkdir.in.eas));
+
+ op->op_info = io;
+ op->send_fn = trans2_simple_send;
+
+ return ntvfs_mkdir(req->ntvfs, io);
+}
+
+static NTSTATUS trans2_push_fileinfo(struct smbsrv_connection *smb_conn,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *blob,
+ union smb_fileinfo *st,
+ int default_str_flags)
+{
+ uint32_t list_size;
+ enum smb_fileinfo_level passthru_level;
+
+ switch (st->generic.level) {
+ case RAW_FILEINFO_GENERIC:
+ case RAW_FILEINFO_GETATTR:
+ case RAW_FILEINFO_GETATTRE:
+ case RAW_FILEINFO_SEC_DESC:
+ case RAW_FILEINFO_SMB2_ALL_EAS:
+ case RAW_FILEINFO_SMB2_ALL_INFORMATION:
+ /* handled elsewhere */
+ return NT_STATUS_INVALID_LEVEL;
+
+ case RAW_FILEINFO_UNIX_BASIC:
+ case RAW_FILEINFO_UNIX_LINK:
+ /* not implemented yet */
+ return NT_STATUS_INVALID_LEVEL;
+
+ case RAW_FILEINFO_STANDARD:
+ TRANS2_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, 22));
+
+ srv_push_dos_date2(smb_conn, blob->data, 0, st->standard.out.create_time);
+ srv_push_dos_date2(smb_conn, blob->data, 4, st->standard.out.access_time);
+ srv_push_dos_date2(smb_conn, blob->data, 8, st->standard.out.write_time);
+ SIVAL(blob->data, 12, st->standard.out.size);
+ SIVAL(blob->data, 16, st->standard.out.alloc_size);
+ SSVAL(blob->data, 20, st->standard.out.attrib);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_EA_SIZE:
+ TRANS2_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, 26));
+
+ srv_push_dos_date2(smb_conn, blob->data, 0, st->ea_size.out.create_time);
+ srv_push_dos_date2(smb_conn, blob->data, 4, st->ea_size.out.access_time);
+ srv_push_dos_date2(smb_conn, blob->data, 8, st->ea_size.out.write_time);
+ SIVAL(blob->data, 12, st->ea_size.out.size);
+ SIVAL(blob->data, 16, st->ea_size.out.alloc_size);
+ SSVAL(blob->data, 20, st->ea_size.out.attrib);
+ SIVAL(blob->data, 22, st->ea_size.out.ea_size);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_EA_LIST:
+ list_size = ea_list_size(st->ea_list.out.num_eas,
+ st->ea_list.out.eas);
+ TRANS2_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, list_size));
+
+ ea_put_list(blob->data,
+ st->ea_list.out.num_eas, st->ea_list.out.eas);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_ALL_EAS:
+ list_size = ea_list_size(st->all_eas.out.num_eas,
+ st->all_eas.out.eas);
+ TRANS2_CHECK(smbsrv_blob_grow_data(mem_ctx, blob, list_size));
+
+ ea_put_list(blob->data,
+ st->all_eas.out.num_eas, st->all_eas.out.eas);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_IS_NAME_VALID:
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_BASIC_INFO:
+ passthru_level = RAW_FILEINFO_BASIC_INFORMATION;
+ break;
+
+ case RAW_FILEINFO_STANDARD_INFO:
+ passthru_level = RAW_FILEINFO_STANDARD_INFORMATION;
+ break;
+
+ case RAW_FILEINFO_EA_INFO:
+ passthru_level = RAW_FILEINFO_EA_INFORMATION;
+ break;
+
+ case RAW_FILEINFO_COMPRESSION_INFO:
+ passthru_level = RAW_FILEINFO_COMPRESSION_INFORMATION;
+ break;
+
+ case RAW_FILEINFO_ALL_INFO:
+ passthru_level = RAW_FILEINFO_ALL_INFORMATION;
+ break;
+
+ case RAW_FILEINFO_NAME_INFO:
+ passthru_level = RAW_FILEINFO_NAME_INFORMATION;
+ break;
+
+ case RAW_FILEINFO_ALT_NAME_INFO:
+ passthru_level = RAW_FILEINFO_ALT_NAME_INFORMATION;
+ break;
+
+ case RAW_FILEINFO_STREAM_INFO:
+ passthru_level = RAW_FILEINFO_STREAM_INFORMATION;
+ break;
+
+ default:
+ passthru_level = st->generic.level;
+ break;
+ }
+
+ return smbsrv_push_passthru_fileinfo(mem_ctx, blob,
+ passthru_level, st,
+ default_str_flags);
+}
+
+/*
+ fill in the reply from a qpathinfo or qfileinfo call
+*/
+static NTSTATUS trans2_fileinfo_send(struct trans_op *op)
+{
+ struct smbsrv_request *req = op->req;
+ struct smb_trans2 *trans = op->trans;
+ union smb_fileinfo *st;
+
+ TRANS2_CHECK_ASYNC_STATUS(st, union smb_fileinfo);
+
+ TRANS2_CHECK(trans2_setup_reply(trans, 2, 0, 0));
+ SSVAL(trans->out.params.data, 0, 0);
+
+ TRANS2_CHECK(trans2_push_fileinfo(req->smb_conn, trans,
+ &trans->out.data, st,
+ SMBSRV_REQ_DEFAULT_STR_FLAGS(req)));
+
+ return NT_STATUS_OK;
+}
+
+/*
+ trans2 qpathinfo implementation
+*/
+static NTSTATUS trans2_qpathinfo(struct smbsrv_request *req, struct trans_op *op)
+{
+ struct smb_trans2 *trans = op->trans;
+ union smb_fileinfo *st;
+ uint16_t level;
+
+ /* make sure we got enough parameters */
+ if (trans->in.params.length < 2) {
+ return NT_STATUS_FOOBAR;
+ }
+
+ st = talloc(op, union smb_fileinfo);
+ NT_STATUS_HAVE_NO_MEMORY(st);
+
+ level = SVAL(trans->in.params.data, 0);
+
+ smbsrv_blob_pull_string(&req->in.bufinfo, &trans->in.params, 6, &st->generic.in.file.path, 0);
+ if (st->generic.in.file.path == NULL) {
+ return NT_STATUS_FOOBAR;
+ }
+
+ /* work out the backend level - we make it 1-1 in the header */
+ st->generic.level = (enum smb_fileinfo_level)level;
+ if (st->generic.level >= RAW_FILEINFO_GENERIC) {
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ if (st->generic.level == RAW_FILEINFO_EA_LIST) {
+ TRANS2_CHECK(ea_pull_name_list(&trans->in.data, req,
+ &st->ea_list.in.num_names,
+ &st->ea_list.in.ea_names));
+ }
+
+ op->op_info = st;
+ op->send_fn = trans2_fileinfo_send;
+
+ return ntvfs_qpathinfo(req->ntvfs, st);
+}
+
+
+/*
+ trans2 qpathinfo implementation
+*/
+static NTSTATUS trans2_qfileinfo(struct smbsrv_request *req, struct trans_op *op)
+{
+ struct smb_trans2 *trans = op->trans;
+ union smb_fileinfo *st;
+ uint16_t level;
+ struct ntvfs_handle *h;
+
+ /* make sure we got enough parameters */
+ if (trans->in.params.length < 4) {
+ return NT_STATUS_FOOBAR;
+ }
+
+ st = talloc(op, union smb_fileinfo);
+ NT_STATUS_HAVE_NO_MEMORY(st);
+
+ h = smbsrv_pull_fnum(req, trans->in.params.data, 0);
+ level = SVAL(trans->in.params.data, 2);
+
+ st->generic.in.file.ntvfs = h;
+ /* work out the backend level - we make it 1-1 in the header */
+ st->generic.level = (enum smb_fileinfo_level)level;
+ if (st->generic.level >= RAW_FILEINFO_GENERIC) {
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ if (st->generic.level == RAW_FILEINFO_EA_LIST) {
+ TRANS2_CHECK(ea_pull_name_list(&trans->in.data, req,
+ &st->ea_list.in.num_names,
+ &st->ea_list.in.ea_names));
+ }
+
+ op->op_info = st;
+ op->send_fn = trans2_fileinfo_send;
+
+ SMBSRV_CHECK_FILE_HANDLE_NTSTATUS(st->generic.in.file.ntvfs);
+ return ntvfs_qfileinfo(req->ntvfs, st);
+}
+
+
+/*
+ parse a trans2 setfileinfo/setpathinfo data blob
+*/
+static NTSTATUS trans2_parse_sfileinfo(struct smbsrv_request *req,
+ union smb_setfileinfo *st,
+ const DATA_BLOB *blob)
+{
+ enum smb_setfileinfo_level passthru_level;
+
+ switch (st->generic.level) {
+ case RAW_SFILEINFO_GENERIC:
+ case RAW_SFILEINFO_SETATTR:
+ case RAW_SFILEINFO_SETATTRE:
+ case RAW_SFILEINFO_SEC_DESC:
+ /* handled elsewhere */
+ return NT_STATUS_INVALID_LEVEL;
+
+ case RAW_SFILEINFO_STANDARD:
+ CHECK_MIN_BLOB_SIZE(blob, 12);
+
+ st->standard.in.create_time = srv_pull_dos_date2(req->smb_conn, blob->data + 0);
+ st->standard.in.access_time = srv_pull_dos_date2(req->smb_conn, blob->data + 4);
+ st->standard.in.write_time = srv_pull_dos_date2(req->smb_conn, blob->data + 8);
+
+ return NT_STATUS_OK;
+
+ case RAW_SFILEINFO_EA_SET:
+ return ea_pull_list(blob, req,
+ &st->ea_set.in.num_eas,
+ &st->ea_set.in.eas);
+
+ case SMB_SFILEINFO_BASIC_INFO:
+ case SMB_SFILEINFO_BASIC_INFORMATION:
+ passthru_level = SMB_SFILEINFO_BASIC_INFORMATION;
+ break;
+
+ case SMB_SFILEINFO_DISPOSITION_INFO:
+ case SMB_SFILEINFO_DISPOSITION_INFORMATION:
+ passthru_level = SMB_SFILEINFO_DISPOSITION_INFORMATION;
+ break;
+
+ case SMB_SFILEINFO_ALLOCATION_INFO:
+ case SMB_SFILEINFO_ALLOCATION_INFORMATION:
+ passthru_level = SMB_SFILEINFO_ALLOCATION_INFORMATION;
+ break;
+
+ case RAW_SFILEINFO_END_OF_FILE_INFO:
+ case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
+ passthru_level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
+ break;
+
+ case RAW_SFILEINFO_RENAME_INFORMATION:
+ case RAW_SFILEINFO_POSITION_INFORMATION:
+ case RAW_SFILEINFO_MODE_INFORMATION:
+ passthru_level = st->generic.level;
+ break;
+
+ case RAW_SFILEINFO_UNIX_BASIC:
+ case RAW_SFILEINFO_UNIX_LINK:
+ case RAW_SFILEINFO_UNIX_HLINK:
+ case RAW_SFILEINFO_PIPE_INFORMATION:
+ case RAW_SFILEINFO_VALID_DATA_INFORMATION:
+ case RAW_SFILEINFO_SHORT_NAME_INFORMATION:
+ case RAW_SFILEINFO_1025:
+ case RAW_SFILEINFO_1027:
+ case RAW_SFILEINFO_1029:
+ case RAW_SFILEINFO_1030:
+ case RAW_SFILEINFO_1031:
+ case RAW_SFILEINFO_1032:
+ case RAW_SFILEINFO_1036:
+ case RAW_SFILEINFO_1041:
+ case RAW_SFILEINFO_1042:
+ case RAW_SFILEINFO_1043:
+ case RAW_SFILEINFO_1044:
+ return NT_STATUS_INVALID_LEVEL;
+
+ default:
+ /* we need a default here to cope with invalid values on the wire */
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ return smbsrv_pull_passthru_sfileinfo(st, passthru_level, st,
+ blob, SMBSRV_REQ_DEFAULT_STR_FLAGS(req),
+ &req->in.bufinfo);
+}
+
+/*
+ trans2 setfileinfo implementation
+*/
+static NTSTATUS trans2_setfileinfo(struct smbsrv_request *req, struct trans_op *op)
+{
+ struct smb_trans2 *trans = op->trans;
+ union smb_setfileinfo *st;
+ uint16_t level;
+ struct ntvfs_handle *h;
+
+ /* make sure we got enough parameters */
+ if (trans->in.params.length < 4) {
+ return NT_STATUS_FOOBAR;
+ }
+
+ st = talloc(op, union smb_setfileinfo);
+ NT_STATUS_HAVE_NO_MEMORY(st);
+
+ h = smbsrv_pull_fnum(req, trans->in.params.data, 0);
+ level = SVAL(trans->in.params.data, 2);
+
+ st->generic.in.file.ntvfs = h;
+ /* work out the backend level - we make it 1-1 in the header */
+ st->generic.level = (enum smb_setfileinfo_level)level;
+ if (st->generic.level >= RAW_SFILEINFO_GENERIC) {
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ TRANS2_CHECK(trans2_parse_sfileinfo(req, st, &trans->in.data));
+
+ op->op_info = st;
+ op->send_fn = trans2_simple_send;
+
+ SMBSRV_CHECK_FILE_HANDLE_NTSTATUS(st->generic.in.file.ntvfs);
+ return ntvfs_setfileinfo(req->ntvfs, st);
+}
+
+/*
+ trans2 setpathinfo implementation
+*/
+static NTSTATUS trans2_setpathinfo(struct smbsrv_request *req, struct trans_op *op)
+{
+ struct smb_trans2 *trans = op->trans;
+ union smb_setfileinfo *st;
+ uint16_t level;
+
+ /* make sure we got enough parameters */
+ if (trans->in.params.length < 4) {
+ return NT_STATUS_FOOBAR;
+ }
+
+ st = talloc(op, union smb_setfileinfo);
+ NT_STATUS_HAVE_NO_MEMORY(st);
+
+ level = SVAL(trans->in.params.data, 0);
+
+ smbsrv_blob_pull_string(&req->in.bufinfo, &trans->in.params, 6, &st->generic.in.file.path, 0);
+ if (st->generic.in.file.path == NULL) {
+ return NT_STATUS_FOOBAR;
+ }
+
+ /* work out the backend level - we make it 1-1 in the header */
+ st->generic.level = (enum smb_setfileinfo_level)level;
+ if (st->generic.level >= RAW_SFILEINFO_GENERIC) {
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ TRANS2_CHECK(trans2_parse_sfileinfo(req, st, &trans->in.data));
+
+ op->op_info = st;
+ op->send_fn = trans2_simple_send;
+
+ return ntvfs_setpathinfo(req->ntvfs, st);
+}
+
+
+/* a structure to encapsulate the state information about an in-progress ffirst/fnext operation */
+struct find_state {
+ struct trans_op *op;
+ void *search;
+ enum smb_search_data_level data_level;
+ uint16_t last_entry_offset;
+ uint16_t flags;
+};
+
+/*
+ fill a single entry in a trans2 find reply
+*/
+static NTSTATUS find_fill_info(struct find_state *state,
+ const union smb_search_data *file)
+{
+ struct smbsrv_request *req = state->op->req;
+ struct smb_trans2 *trans = state->op->trans;
+ uint8_t *data;
+ uint_t ofs = trans->out.data.length;
+ uint32_t ea_size;
+
+ switch (state->data_level) {
+ case RAW_SEARCH_DATA_GENERIC:
+ case RAW_SEARCH_DATA_SEARCH:
+ /* handled elsewhere */
+ return NT_STATUS_INVALID_LEVEL;
+
+ case RAW_SEARCH_DATA_STANDARD:
+ if (state->flags & FLAG_TRANS2_FIND_REQUIRE_RESUME) {
+ TRANS2_CHECK(smbsrv_blob_grow_data(trans, &trans->out.data, ofs + 27));
+ SIVAL(trans->out.data.data, ofs, file->standard.resume_key);
+ ofs += 4;
+ } else {
+ TRANS2_CHECK(smbsrv_blob_grow_data(trans, &trans->out.data, ofs + 23));
+ }
+ data = trans->out.data.data + ofs;
+ srv_push_dos_date2(req->smb_conn, data, 0, file->standard.create_time);
+ srv_push_dos_date2(req->smb_conn, data, 4, file->standard.access_time);
+ srv_push_dos_date2(req->smb_conn, data, 8, file->standard.write_time);
+ SIVAL(data, 12, file->standard.size);
+ SIVAL(data, 16, file->standard.alloc_size);
+ SSVAL(data, 20, file->standard.attrib);
+ TRANS2_CHECK(smbsrv_blob_append_string(trans, &trans->out.data, file->standard.name.s,
+ ofs + 22, SMBSRV_REQ_DEFAULT_STR_FLAGS(req),
+ STR_LEN8BIT | STR_TERMINATE | STR_LEN_NOTERM));
+ break;
+
+ case RAW_SEARCH_DATA_EA_SIZE:
+ if (state->flags & FLAG_TRANS2_FIND_REQUIRE_RESUME) {
+ TRANS2_CHECK(smbsrv_blob_grow_data(trans, &trans->out.data, ofs + 31));
+ SIVAL(trans->out.data.data, ofs, file->ea_size.resume_key);
+ ofs += 4;
+ } else {
+ TRANS2_CHECK(smbsrv_blob_grow_data(trans, &trans->out.data, ofs + 27));
+ }
+ data = trans->out.data.data + ofs;
+ srv_push_dos_date2(req->smb_conn, data, 0, file->ea_size.create_time);
+ srv_push_dos_date2(req->smb_conn, data, 4, file->ea_size.access_time);
+ srv_push_dos_date2(req->smb_conn, data, 8, file->ea_size.write_time);
+ SIVAL(data, 12, file->ea_size.size);
+ SIVAL(data, 16, file->ea_size.alloc_size);
+ SSVAL(data, 20, file->ea_size.attrib);
+ SIVAL(data, 22, file->ea_size.ea_size);
+ TRANS2_CHECK(smbsrv_blob_append_string(trans, &trans->out.data, file->ea_size.name.s,
+ ofs + 26, SMBSRV_REQ_DEFAULT_STR_FLAGS(req),
+ STR_LEN8BIT | STR_NOALIGN));
+ TRANS2_CHECK(smbsrv_blob_fill_data(trans, &trans->out.data, trans->out.data.length + 1));
+ break;
+
+ case RAW_SEARCH_DATA_EA_LIST:
+ ea_size = ea_list_size(file->ea_list.eas.num_eas, file->ea_list.eas.eas);
+ if (state->flags & FLAG_TRANS2_FIND_REQUIRE_RESUME) {
+ TRANS2_CHECK(smbsrv_blob_grow_data(trans, &trans->out.data, ofs + 27 + ea_size));
+ SIVAL(trans->out.data.data, ofs, file->ea_list.resume_key);
+ ofs += 4;
+ } else {
+ TRANS2_CHECK(smbsrv_blob_grow_data(trans, &trans->out.data, ofs + 23 + ea_size));
+ }
+ data = trans->out.data.data + ofs;
+ srv_push_dos_date2(req->smb_conn, data, 0, file->ea_list.create_time);
+ srv_push_dos_date2(req->smb_conn, data, 4, file->ea_list.access_time);
+ srv_push_dos_date2(req->smb_conn, data, 8, file->ea_list.write_time);
+ SIVAL(data, 12, file->ea_list.size);
+ SIVAL(data, 16, file->ea_list.alloc_size);
+ SSVAL(data, 20, file->ea_list.attrib);
+ ea_put_list(data+22, file->ea_list.eas.num_eas, file->ea_list.eas.eas);
+ TRANS2_CHECK(smbsrv_blob_append_string(trans, &trans->out.data, file->ea_list.name.s,
+ ofs + 22 + ea_size, SMBSRV_REQ_DEFAULT_STR_FLAGS(req),
+ STR_LEN8BIT | STR_NOALIGN));
+ TRANS2_CHECK(smbsrv_blob_fill_data(trans, &trans->out.data, trans->out.data.length + 1));
+ break;
+
+ case RAW_SEARCH_DATA_DIRECTORY_INFO:
+ case RAW_SEARCH_DATA_FULL_DIRECTORY_INFO:
+ case RAW_SEARCH_DATA_NAME_INFO:
+ case RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO:
+ case RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO:
+ case RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO:
+ return smbsrv_push_passthru_search(trans, &trans->out.data, state->data_level, file,
+ SMBSRV_REQ_DEFAULT_STR_FLAGS(req));
+
+ case RAW_SEARCH_DATA_UNIX_INFO:
+ case RAW_SEARCH_DATA_UNIX_INFO2:
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/* callback function for trans2 findfirst/findnext */
+static bool find_callback(void *private_data, const union smb_search_data *file)
+{
+ struct find_state *state = talloc_get_type(private_data, struct find_state);
+ struct smb_trans2 *trans = state->op->trans;
+ uint_t old_length;
+
+ old_length = trans->out.data.length;
+
+ if (!NT_STATUS_IS_OK(find_fill_info(state, file)) ||
+ trans->out.data.length > trans->in.max_data) {
+ /* restore the old length and tell the backend to stop */
+ smbsrv_blob_grow_data(trans, &trans->out.data, old_length);
+ return false;
+ }
+
+ state->last_entry_offset = old_length;
+ return true;
+}
+
+/*
+ trans2 findfirst send
+ */
+static NTSTATUS trans2_findfirst_send(struct trans_op *op)
+{
+ struct smbsrv_request *req = op->req;
+ struct smb_trans2 *trans = op->trans;
+ union smb_search_first *search;
+ struct find_state *state;
+ uint8_t *param;
+
+ TRANS2_CHECK_ASYNC_STATUS(state, struct find_state);
+ search = talloc_get_type(state->search, union smb_search_first);
+
+ /* fill in the findfirst reply header */
+ param = trans->out.params.data;
+ SSVAL(param, VWV(0), search->t2ffirst.out.handle);
+ SSVAL(param, VWV(1), search->t2ffirst.out.count);
+ SSVAL(param, VWV(2), search->t2ffirst.out.end_of_search);
+ SSVAL(param, VWV(3), 0);
+ SSVAL(param, VWV(4), state->last_entry_offset);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ trans2 findfirst implementation
+*/
+static NTSTATUS trans2_findfirst(struct smbsrv_request *req, struct trans_op *op)
+{
+ struct smb_trans2 *trans = op->trans;
+ union smb_search_first *search;
+ uint16_t level;
+ struct find_state *state;
+
+ /* make sure we got all the parameters */
+ if (trans->in.params.length < 14) {
+ return NT_STATUS_FOOBAR;
+ }
+
+ search = talloc(op, union smb_search_first);
+ NT_STATUS_HAVE_NO_MEMORY(search);
+
+ search->t2ffirst.in.search_attrib = SVAL(trans->in.params.data, 0);
+ search->t2ffirst.in.max_count = SVAL(trans->in.params.data, 2);
+ search->t2ffirst.in.flags = SVAL(trans->in.params.data, 4);
+ level = SVAL(trans->in.params.data, 6);
+ search->t2ffirst.in.storage_type = IVAL(trans->in.params.data, 8);
+
+ smbsrv_blob_pull_string(&req->in.bufinfo, &trans->in.params, 12, &search->t2ffirst.in.pattern, 0);
+ if (search->t2ffirst.in.pattern == NULL) {
+ return NT_STATUS_FOOBAR;
+ }
+
+ search->t2ffirst.level = RAW_SEARCH_TRANS2;
+ search->t2ffirst.data_level = (enum smb_search_data_level)level;
+ if (search->t2ffirst.data_level >= RAW_SEARCH_DATA_GENERIC) {
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ if (search->t2ffirst.data_level == RAW_SEARCH_DATA_EA_LIST) {
+ TRANS2_CHECK(ea_pull_name_list(&trans->in.data, req,
+ &search->t2ffirst.in.num_names,
+ &search->t2ffirst.in.ea_names));
+ }
+
+ /* setup the private state structure that the backend will
+ give us in the callback */
+ state = talloc(op, struct find_state);
+ NT_STATUS_HAVE_NO_MEMORY(state);
+ state->op = op;
+ state->search = search;
+ state->data_level = search->t2ffirst.data_level;
+ state->last_entry_offset= 0;
+ state->flags = search->t2ffirst.in.flags;
+
+ /* setup for just a header in the reply */
+ TRANS2_CHECK(trans2_setup_reply(trans, 10, 0, 0));
+
+ op->op_info = state;
+ op->send_fn = trans2_findfirst_send;
+
+ return ntvfs_search_first(req->ntvfs, search, state, find_callback);
+}
+
+
+/*
+ trans2 findnext send
+*/
+static NTSTATUS trans2_findnext_send(struct trans_op *op)
+{
+ struct smbsrv_request *req = op->req;
+ struct smb_trans2 *trans = op->trans;
+ union smb_search_next *search;
+ struct find_state *state;
+ uint8_t *param;
+
+ TRANS2_CHECK_ASYNC_STATUS(state, struct find_state);
+ search = talloc_get_type(state->search, union smb_search_next);
+
+ /* fill in the findfirst reply header */
+ param = trans->out.params.data;
+ SSVAL(param, VWV(0), search->t2fnext.out.count);
+ SSVAL(param, VWV(1), search->t2fnext.out.end_of_search);
+ SSVAL(param, VWV(2), 0);
+ SSVAL(param, VWV(3), state->last_entry_offset);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ trans2 findnext implementation
+*/
+static NTSTATUS trans2_findnext(struct smbsrv_request *req, struct trans_op *op)
+{
+ struct smb_trans2 *trans = op->trans;
+ union smb_search_next *search;
+ uint16_t level;
+ struct find_state *state;
+
+ /* make sure we got all the parameters */
+ if (trans->in.params.length < 12) {
+ return NT_STATUS_FOOBAR;
+ }
+
+ search = talloc(op, union smb_search_next);
+ NT_STATUS_HAVE_NO_MEMORY(search);
+
+ search->t2fnext.in.handle = SVAL(trans->in.params.data, 0);
+ search->t2fnext.in.max_count = SVAL(trans->in.params.data, 2);
+ level = SVAL(trans->in.params.data, 4);
+ search->t2fnext.in.resume_key = IVAL(trans->in.params.data, 6);
+ search->t2fnext.in.flags = SVAL(trans->in.params.data, 10);
+
+ smbsrv_blob_pull_string(&req->in.bufinfo, &trans->in.params, 12, &search->t2fnext.in.last_name, 0);
+ if (search->t2fnext.in.last_name == NULL) {
+ return NT_STATUS_FOOBAR;
+ }
+
+ search->t2fnext.level = RAW_SEARCH_TRANS2;
+ search->t2fnext.data_level = (enum smb_search_data_level)level;
+ if (search->t2fnext.data_level >= RAW_SEARCH_DATA_GENERIC) {
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ if (search->t2fnext.data_level == RAW_SEARCH_DATA_EA_LIST) {
+ TRANS2_CHECK(ea_pull_name_list(&trans->in.data, req,
+ &search->t2fnext.in.num_names,
+ &search->t2fnext.in.ea_names));
+ }
+
+ /* setup the private state structure that the backend will give us in the callback */
+ state = talloc(op, struct find_state);
+ NT_STATUS_HAVE_NO_MEMORY(state);
+ state->op = op;
+ state->search = search;
+ state->data_level = search->t2fnext.data_level;
+ state->last_entry_offset= 0;
+ state->flags = search->t2fnext.in.flags;
+
+ /* setup for just a header in the reply */
+ TRANS2_CHECK(trans2_setup_reply(trans, 8, 0, 0));
+
+ op->op_info = state;
+ op->send_fn = trans2_findnext_send;
+
+ return ntvfs_search_next(req->ntvfs, search, state, find_callback);
+}
+
+
+/*
+ backend for trans2 requests
+*/
+static NTSTATUS trans2_backend(struct smbsrv_request *req, struct trans_op *op)
+{
+ struct smb_trans2 *trans = op->trans;
+ NTSTATUS status;
+
+ /* direct trans2 pass thru */
+ status = ntvfs_trans2(req->ntvfs, trans);
+ if (!NT_STATUS_EQUAL(NT_STATUS_NOT_IMPLEMENTED, status)) {
+ return status;
+ }
+
+ /* must have at least one setup word */
+ if (trans->in.setup_count < 1) {
+ return NT_STATUS_FOOBAR;
+ }
+
+ /* the trans2 command is in setup[0] */
+ switch (trans->in.setup[0]) {
+ case TRANSACT2_FINDFIRST:
+ return trans2_findfirst(req, op);
+ case TRANSACT2_FINDNEXT:
+ return trans2_findnext(req, op);
+ case TRANSACT2_QPATHINFO:
+ return trans2_qpathinfo(req, op);
+ case TRANSACT2_QFILEINFO:
+ return trans2_qfileinfo(req, op);
+ case TRANSACT2_SETFILEINFO:
+ return trans2_setfileinfo(req, op);
+ case TRANSACT2_SETPATHINFO:
+ return trans2_setpathinfo(req, op);
+ case TRANSACT2_QFSINFO:
+ return trans2_qfsinfo(req, op);
+ case TRANSACT2_OPEN:
+ return trans2_open(req, op);
+ case TRANSACT2_MKDIR:
+ return trans2_mkdir(req, op);
+ }
+
+ /* an unknown trans2 command */
+ return NT_STATUS_FOOBAR;
+}
+
+int smbsrv_trans_partial_destructor(struct smbsrv_trans_partial *tp)
+{
+ DLIST_REMOVE(tp->req->smb_conn->trans_partial, tp);
+ return 0;
+}
+
+
+/*
+ send a continue request
+*/
+static void reply_trans_continue(struct smbsrv_request *req, uint8_t command,
+ struct smb_trans2 *trans)
+{
+ struct smbsrv_request *req2;
+ struct smbsrv_trans_partial *tp;
+ int count;
+
+ /* make sure they don't flood us */
+ for (count=0,tp=req->smb_conn->trans_partial;tp;tp=tp->next) count++;
+ if (count > 100) {
+ smbsrv_send_error(req, NT_STATUS_INSUFFICIENT_RESOURCES);
+ return;
+ }
+
+ tp = talloc(req, struct smbsrv_trans_partial);
+
+ tp->req = req;
+ tp->u.trans = trans;
+ tp->command = command;
+
+ DLIST_ADD(req->smb_conn->trans_partial, tp);
+ talloc_set_destructor(tp, smbsrv_trans_partial_destructor);
+
+ req2 = smbsrv_setup_secondary_request(req);
+
+ /* send a 'please continue' reply */
+ smbsrv_setup_reply(req2, 0, 0);
+ smbsrv_send_reply(req2);
+}
+
+
+/*
+ answer a reconstructed trans request
+*/
+static void reply_trans_send(struct ntvfs_request *ntvfs)
+{
+ struct smbsrv_request *req;
+ struct trans_op *op;
+ struct smb_trans2 *trans;
+ uint16_t params_left, data_left;
+ uint8_t *params, *data;
+ int i;
+
+ SMBSRV_CHECK_ASYNC_STATUS_ERR(op, struct trans_op);
+ trans = op->trans;
+
+ /* if this function needs work to form the nttrans reply buffer, then
+ call that now */
+ if (op->send_fn != NULL) {
+ NTSTATUS status;
+ status = op->send_fn(op);
+ if (!NT_STATUS_IS_OK(status)) {
+ smbsrv_send_error(req, status);
+ return;
+ }
+ }
+
+ params_left = trans->out.params.length;
+ data_left = trans->out.data.length;
+ params = trans->out.params.data;
+ data = trans->out.data.data;
+
+ smbsrv_setup_reply(req, 10 + trans->out.setup_count, 0);
+
+ if (!NT_STATUS_IS_OK(req->ntvfs->async_states->status)) {
+ smbsrv_setup_error(req, req->ntvfs->async_states->status);
+ }
+
+ /* we need to divide up the reply into chunks that fit into
+ the negotiated buffer size */
+ do {
+ uint16_t this_data, this_param, max_bytes;
+ uint_t align1 = 1, align2 = (params_left ? 2 : 0);
+ struct smbsrv_request *this_req;
+
+ max_bytes = req_max_data(req) - (align1 + align2);
+
+ this_param = params_left;
+ if (this_param > max_bytes) {
+ this_param = max_bytes;
+ }
+ max_bytes -= this_param;
+
+ this_data = data_left;
+ if (this_data > max_bytes) {
+ this_data = max_bytes;
+ }
+
+ /* don't destroy unless this is the last chunk */
+ if (params_left - this_param != 0 ||
+ data_left - this_data != 0) {
+ this_req = smbsrv_setup_secondary_request(req);
+ } else {
+ this_req = req;
+ }
+
+ req_grow_data(this_req, this_param + this_data + (align1 + align2));
+
+ SSVAL(this_req->out.vwv, VWV(0), trans->out.params.length);
+ SSVAL(this_req->out.vwv, VWV(1), trans->out.data.length);
+ SSVAL(this_req->out.vwv, VWV(2), 0);
+
+ SSVAL(this_req->out.vwv, VWV(3), this_param);
+ SSVAL(this_req->out.vwv, VWV(4), align1 + PTR_DIFF(this_req->out.data, this_req->out.hdr));
+ SSVAL(this_req->out.vwv, VWV(5), PTR_DIFF(params, trans->out.params.data));
+
+ SSVAL(this_req->out.vwv, VWV(6), this_data);
+ SSVAL(this_req->out.vwv, VWV(7), align1 + align2 +
+ PTR_DIFF(this_req->out.data + this_param, this_req->out.hdr));
+ SSVAL(this_req->out.vwv, VWV(8), PTR_DIFF(data, trans->out.data.data));
+
+ SCVAL(this_req->out.vwv, VWV(9), trans->out.setup_count);
+ SCVAL(this_req->out.vwv, VWV(9)+1, 0); /* reserved */
+ for (i=0;i<trans->out.setup_count;i++) {
+ SSVAL(this_req->out.vwv, VWV(10+i), trans->out.setup[i]);
+ }
+
+ memset(this_req->out.data, 0, align1);
+ if (this_param != 0) {
+ memcpy(this_req->out.data + align1, params, this_param);
+ }
+ memset(this_req->out.data+this_param+align1, 0, align2);
+ if (this_data != 0) {
+ memcpy(this_req->out.data+this_param+align1+align2, data, this_data);
+ }
+
+ params_left -= this_param;
+ data_left -= this_data;
+ params += this_param;
+ data += this_data;
+
+ smbsrv_send_reply(this_req);
+ } while (params_left != 0 || data_left != 0);
+}
+
+
+/*
+ answer a reconstructed trans request
+*/
+static void reply_trans_complete(struct smbsrv_request *req, uint8_t command,
+ struct smb_trans2 *trans)
+{
+ struct trans_op *op;
+
+ SMBSRV_TALLOC_IO_PTR(op, struct trans_op);
+ SMBSRV_SETUP_NTVFS_REQUEST(reply_trans_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ op->req = req;
+ op->trans = trans;
+ op->command = command;
+ op->op_info = NULL;
+ op->send_fn = NULL;
+
+ /* its a full request, give it to the backend */
+ if (command == SMBtrans) {
+ SMBSRV_CALL_NTVFS_BACKEND(ntvfs_trans(req->ntvfs, trans));
+ return;
+ } else {
+ SMBSRV_CALL_NTVFS_BACKEND(trans2_backend(req, op));
+ return;
+ }
+}
+
+/*
+ Reply to an SMBtrans or SMBtrans2 request
+*/
+static void reply_trans_generic(struct smbsrv_request *req, uint8_t command)
+{
+ struct smb_trans2 *trans;
+ int i;
+ uint16_t param_ofs, data_ofs;
+ uint16_t param_count, data_count;
+ uint16_t param_total, data_total;
+
+ /* parse request */
+ if (req->in.wct < 14) {
+ smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ trans = talloc(req, struct smb_trans2);
+ if (trans == NULL) {
+ smbsrv_send_error(req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+
+ param_total = SVAL(req->in.vwv, VWV(0));
+ data_total = SVAL(req->in.vwv, VWV(1));
+ trans->in.max_param = SVAL(req->in.vwv, VWV(2));
+ trans->in.max_data = SVAL(req->in.vwv, VWV(3));
+ trans->in.max_setup = CVAL(req->in.vwv, VWV(4));
+ trans->in.flags = SVAL(req->in.vwv, VWV(5));
+ trans->in.timeout = IVAL(req->in.vwv, VWV(6));
+ param_count = SVAL(req->in.vwv, VWV(9));
+ param_ofs = SVAL(req->in.vwv, VWV(10));
+ data_count = SVAL(req->in.vwv, VWV(11));
+ data_ofs = SVAL(req->in.vwv, VWV(12));
+ trans->in.setup_count = CVAL(req->in.vwv, VWV(13));
+
+ if (req->in.wct != 14 + trans->in.setup_count) {
+ smbsrv_send_error(req, NT_STATUS_DOS(ERRSRV, ERRerror));
+ return;
+ }
+
+ /* parse out the setup words */
+ trans->in.setup = talloc_array(trans, uint16_t, trans->in.setup_count);
+ if (trans->in.setup_count && !trans->in.setup) {
+ smbsrv_send_error(req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ for (i=0;i<trans->in.setup_count;i++) {
+ trans->in.setup[i] = SVAL(req->in.vwv, VWV(14+i));
+ }
+
+ if (command == SMBtrans) {
+ req_pull_string(&req->in.bufinfo, &trans->in.trans_name, req->in.data, -1, STR_TERMINATE);
+ }
+
+ if (!req_pull_blob(&req->in.bufinfo, req->in.hdr + param_ofs, param_count, &trans->in.params) ||
+ !req_pull_blob(&req->in.bufinfo, req->in.hdr + data_ofs, data_count, &trans->in.data)) {
+ smbsrv_send_error(req, NT_STATUS_FOOBAR);
+ return;
+ }
+
+ /* is it a partial request? if so, then send a 'send more' message */
+ if (param_total > param_count || data_total > data_count) {
+ reply_trans_continue(req, command, trans);
+ return;
+ }
+
+ reply_trans_complete(req, command, trans);
+}
+
+
+/*
+ Reply to an SMBtranss2 request
+*/
+static void reply_transs_generic(struct smbsrv_request *req, uint8_t command)
+{
+ struct smbsrv_trans_partial *tp;
+ struct smb_trans2 *trans = NULL;
+ uint16_t param_ofs, data_ofs;
+ uint16_t param_count, data_count;
+ uint16_t param_disp, data_disp;
+ uint16_t param_total, data_total;
+ DATA_BLOB params, data;
+ uint8_t wct;
+
+ if (command == SMBtrans2) {
+ wct = 9;
+ } else {
+ wct = 8;
+ }
+
+ /* parse request */
+ if (req->in.wct != wct) {
+ /*
+ * TODO: add some error code tests
+ * w2k3 returns NT_STATUS_DOS(ERRSRV, ERRerror) here
+ */
+ smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ for (tp=req->smb_conn->trans_partial;tp;tp=tp->next) {
+ if (tp->command == command &&
+ SVAL(tp->req->in.hdr, HDR_MID) == SVAL(req->in.hdr, HDR_MID)) {
+/* TODO: check the VUID, PID and TID too? */
+ break;
+ }
+ }
+
+ if (tp == NULL) {
+ smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ trans = tp->u.trans;
+
+ param_total = SVAL(req->in.vwv, VWV(0));
+ data_total = SVAL(req->in.vwv, VWV(1));
+ param_count = SVAL(req->in.vwv, VWV(2));
+ param_ofs = SVAL(req->in.vwv, VWV(3));
+ param_disp = SVAL(req->in.vwv, VWV(4));
+ data_count = SVAL(req->in.vwv, VWV(5));
+ data_ofs = SVAL(req->in.vwv, VWV(6));
+ data_disp = SVAL(req->in.vwv, VWV(7));
+
+ if (!req_pull_blob(&req->in.bufinfo, req->in.hdr + param_ofs, param_count, &params) ||
+ !req_pull_blob(&req->in.bufinfo, req->in.hdr + data_ofs, data_count, &data)) {
+ smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ /* only allow contiguous requests */
+ if ((param_count != 0 &&
+ param_disp != trans->in.params.length) ||
+ (data_count != 0 &&
+ data_disp != trans->in.data.length)) {
+ smbsrv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ /* add to the existing request */
+ if (param_count != 0) {
+ trans->in.params.data = talloc_realloc(trans,
+ trans->in.params.data,
+ uint8_t,
+ param_disp + param_count);
+ if (trans->in.params.data == NULL) {
+ smbsrv_send_error(tp->req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ trans->in.params.length = param_disp + param_count;
+ }
+
+ if (data_count != 0) {
+ trans->in.data.data = talloc_realloc(trans,
+ trans->in.data.data,
+ uint8_t,
+ data_disp + data_count);
+ if (trans->in.data.data == NULL) {
+ smbsrv_send_error(tp->req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ trans->in.data.length = data_disp + data_count;
+ }
+
+ memcpy(trans->in.params.data + param_disp, params.data, params.length);
+ memcpy(trans->in.data.data + data_disp, data.data, data.length);
+
+ /* the sequence number of the reply is taken from the last secondary
+ response */
+ tp->req->seq_num = req->seq_num;
+
+ /* we don't reply to Transs2 requests */
+ talloc_free(req);
+
+ if (trans->in.params.length == param_total &&
+ trans->in.data.length == data_total) {
+ /* its now complete */
+ req = tp->req;
+ talloc_free(tp);
+ reply_trans_complete(req, command, trans);
+ }
+ return;
+}
+
+
+/*
+ Reply to an SMBtrans2
+*/
+void smbsrv_reply_trans2(struct smbsrv_request *req)
+{
+ reply_trans_generic(req, SMBtrans2);
+}
+
+/*
+ Reply to an SMBtrans
+*/
+void smbsrv_reply_trans(struct smbsrv_request *req)
+{
+ reply_trans_generic(req, SMBtrans);
+}
+
+/*
+ Reply to an SMBtranss request
+*/
+void smbsrv_reply_transs(struct smbsrv_request *req)
+{
+ reply_transs_generic(req, SMBtrans);
+}
+
+/*
+ Reply to an SMBtranss2 request
+*/
+void smbsrv_reply_transs2(struct smbsrv_request *req)
+{
+ reply_transs_generic(req, SMBtrans2);
+}
diff --git a/source4/smb_server/smb2/config.mk b/source4/smb_server/smb2/config.mk
new file mode 100644
index 0000000000..f0c3739926
--- /dev/null
+++ b/source4/smb_server/smb2/config.mk
@@ -0,0 +1,19 @@
+#######################
+# Start SUBSYSTEM SMB2_PROTOCOL
+[SUBSYSTEM::SMB2_PROTOCOL]
+PUBLIC_DEPENDENCIES = \
+ ntvfs LIBPACKET LIBCLI_SMB2 samba_server_gensec
+# End SUBSYSTEM SMB2_PROTOCOL
+#######################
+
+SMB2_PROTOCOL_OBJ_FILES = $(addprefix $(smb_serversrcdir)/smb2/, \
+ receive.o \
+ negprot.o \
+ sesssetup.o \
+ tcon.o \
+ fileio.o \
+ fileinfo.o \
+ find.o \
+ keepalive.o)
+
+$(eval $(call proto_header_template,$(smb_serversrcdir)/smb2/smb2_proto.h,$(SMB2_PROTOCOL_OBJ_FILES:.o=.c)))
diff --git a/source4/smb_server/smb2/fileinfo.c b/source4/smb_server/smb2/fileinfo.c
new file mode 100644
index 0000000000..82b006c4a1
--- /dev/null
+++ b/source4/smb_server/smb2/fileinfo.c
@@ -0,0 +1,378 @@
+/*
+ Unix SMB2 implementation.
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "smb_server/smb_server.h"
+#include "smb_server/service_smb_proto.h"
+#include "smb_server/smb2/smb2_server.h"
+#include "ntvfs/ntvfs.h"
+#include "librpc/gen_ndr/ndr_security.h"
+
+struct smb2srv_getinfo_op {
+ struct smb2srv_request *req;
+ struct smb2_getinfo *info;
+ void *io_ptr;
+ NTSTATUS (*send_fn)(struct smb2srv_getinfo_op *op);
+};
+
+static void smb2srv_getinfo_send(struct ntvfs_request *ntvfs)
+{
+ struct smb2srv_getinfo_op *op;
+ struct smb2srv_request *req;
+
+ /*
+ * SMB2 uses NT_STATUS_INVALID_INFO_CLASS
+ * so we need to translated it here
+ */
+ if (NT_STATUS_EQUAL(NT_STATUS_INVALID_LEVEL, ntvfs->async_states->status)) {
+ ntvfs->async_states->status = NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ SMB2SRV_CHECK_ASYNC_STATUS(op, struct smb2srv_getinfo_op);
+
+ ZERO_STRUCT(op->info->out);
+ if (op->send_fn) {
+ SMB2SRV_CHECK(op->send_fn(op));
+ }
+
+ if (op->info->in.output_buffer_length < op->info->out.blob.length) {
+ smb2srv_send_error(req, NT_STATUS_INFO_LENGTH_MISMATCH);
+ return;
+ }
+
+ SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x08, true, op->info->out.blob.length));
+
+ SMB2SRV_CHECK(smb2_push_o16s32_blob(&req->out, 0x02, op->info->out.blob));
+ SSVAL(req->out.body, 0x06, 0);
+
+ smb2srv_send_reply(req);
+}
+
+static NTSTATUS smb2srv_getinfo_file_send(struct smb2srv_getinfo_op *op)
+{
+ union smb_fileinfo *io = talloc_get_type(op->io_ptr, union smb_fileinfo);
+ NTSTATUS status;
+
+ status = smbsrv_push_passthru_fileinfo(op->req,
+ &op->info->out.blob,
+ io->generic.level, io,
+ STR_UNICODE);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS smb2srv_getinfo_file(struct smb2srv_getinfo_op *op, uint8_t smb2_level)
+{
+ union smb_fileinfo *io;
+ uint16_t level;
+
+ io = talloc(op, union smb_fileinfo);
+ NT_STATUS_HAVE_NO_MEMORY(io);
+
+ level = op->info->in.info_type | (op->info->in.info_class << 8);
+ switch (level) {
+ case RAW_FILEINFO_SMB2_ALL_EAS:
+ io->all_eas.level = level;
+ io->all_eas.in.file.ntvfs = op->info->in.file.ntvfs;
+ io->all_eas.in.continue_flags = op->info->in.getinfo_flags;
+ break;
+
+ case RAW_FILEINFO_SMB2_ALL_INFORMATION:
+ io->all_info2.level = level;
+ io->all_info2.in.file.ntvfs = op->info->in.file.ntvfs;
+ break;
+
+ default:
+ /* the rest directly maps to the passthru levels */
+ io->generic.level = smb2_level + 1000;
+ io->generic.in.file.ntvfs = op->info->in.file.ntvfs;
+ break;
+ }
+
+ op->io_ptr = io;
+ op->send_fn = smb2srv_getinfo_file_send;
+
+ return ntvfs_qfileinfo(op->req->ntvfs, io);
+}
+
+static NTSTATUS smb2srv_getinfo_fs_send(struct smb2srv_getinfo_op *op)
+{
+ union smb_fsinfo *io = talloc_get_type(op->io_ptr, union smb_fsinfo);
+ NTSTATUS status;
+
+ status = smbsrv_push_passthru_fsinfo(op->req,
+ &op->info->out.blob,
+ io->generic.level, io,
+ STR_UNICODE);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS smb2srv_getinfo_fs(struct smb2srv_getinfo_op *op, uint8_t smb2_level)
+{
+ union smb_fsinfo *io;
+
+ io = talloc(op, union smb_fsinfo);
+ NT_STATUS_HAVE_NO_MEMORY(io);
+
+ /* the rest directly maps to the passthru levels */
+ io->generic.level = smb2_level + 1000;
+
+ /* TODO: allow qfsinfo only the share root directory handle */
+
+ op->io_ptr = io;
+ op->send_fn = smb2srv_getinfo_fs_send;
+
+ return ntvfs_fsinfo(op->req->ntvfs, io);
+}
+
+static NTSTATUS smb2srv_getinfo_security_send(struct smb2srv_getinfo_op *op)
+{
+ union smb_fileinfo *io = talloc_get_type(op->io_ptr, union smb_fileinfo);
+ enum ndr_err_code ndr_err;
+
+ ndr_err = ndr_push_struct_blob(&op->info->out.blob, op->req, NULL,
+ io->query_secdesc.out.sd,
+ (ndr_push_flags_fn_t)ndr_push_security_descriptor);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS smb2srv_getinfo_security(struct smb2srv_getinfo_op *op, uint8_t smb2_level)
+{
+ union smb_fileinfo *io;
+
+ switch (smb2_level) {
+ case 0x00:
+ io = talloc(op, union smb_fileinfo);
+ NT_STATUS_HAVE_NO_MEMORY(io);
+
+ io->query_secdesc.level = RAW_FILEINFO_SEC_DESC;
+ io->query_secdesc.in.file.ntvfs = op->info->in.file.ntvfs;
+ io->query_secdesc.in.secinfo_flags = op->info->in.additional_information;
+
+ op->io_ptr = io;
+ op->send_fn = smb2srv_getinfo_security_send;
+
+ return ntvfs_qfileinfo(op->req->ntvfs, io);
+ }
+
+ return NT_STATUS_INVALID_PARAMETER;
+}
+
+static NTSTATUS smb2srv_getinfo_backend(struct smb2srv_getinfo_op *op)
+{
+ switch (op->info->in.info_type) {
+ case SMB2_GETINFO_FILE:
+ return smb2srv_getinfo_file(op, op->info->in.info_class);
+
+ case SMB2_GETINFO_FS:
+ return smb2srv_getinfo_fs(op, op->info->in.info_class);
+
+ case SMB2_GETINFO_SECURITY:
+ return smb2srv_getinfo_security(op, op->info->in.info_class);
+
+ case SMB2_GETINFO_QUOTA:
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ return NT_STATUS_INVALID_PARAMETER;
+}
+
+void smb2srv_getinfo_recv(struct smb2srv_request *req)
+{
+ struct smb2_getinfo *info;
+ struct smb2srv_getinfo_op *op;
+
+ SMB2SRV_CHECK_BODY_SIZE(req, 0x28, true);
+ SMB2SRV_TALLOC_IO_PTR(info, struct smb2_getinfo);
+ /* this overwrites req->io_ptr !*/
+ SMB2SRV_TALLOC_IO_PTR(op, struct smb2srv_getinfo_op);
+ op->req = req;
+ op->info = info;
+ op->io_ptr = NULL;
+ op->send_fn = NULL;
+ SMB2SRV_SETUP_NTVFS_REQUEST(smb2srv_getinfo_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ info->in.info_type = CVAL(req->in.body, 0x02);
+ info->in.info_class = CVAL(req->in.body, 0x03);
+ info->in.output_buffer_length = IVAL(req->in.body, 0x04);
+ info->in.reserved = IVAL(req->in.body, 0x0C);
+ info->in.additional_information = IVAL(req->in.body, 0x10);
+ info->in.getinfo_flags = IVAL(req->in.body, 0x14);
+ info->in.file.ntvfs = smb2srv_pull_handle(req, req->in.body, 0x18);
+ SMB2SRV_CHECK(smb2_pull_o16As32_blob(&req->in, op,
+ req->in.body+0x08, &info->in.blob));
+
+ SMB2SRV_CHECK_FILE_HANDLE(info->in.file.ntvfs);
+ SMB2SRV_CALL_NTVFS_BACKEND(smb2srv_getinfo_backend(op));
+}
+
+struct smb2srv_setinfo_op {
+ struct smb2srv_request *req;
+ struct smb2_setinfo *info;
+};
+
+static void smb2srv_setinfo_send(struct ntvfs_request *ntvfs)
+{
+ struct smb2srv_setinfo_op *op;
+ struct smb2srv_request *req;
+
+ /*
+ * SMB2 uses NT_STATUS_INVALID_INFO_CLASS
+ * so we need to translated it here
+ */
+ if (NT_STATUS_EQUAL(NT_STATUS_INVALID_LEVEL, ntvfs->async_states->status)) {
+ ntvfs->async_states->status = NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ SMB2SRV_CHECK_ASYNC_STATUS(op, struct smb2srv_setinfo_op);
+
+ SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x02, false, 0));
+
+ smb2srv_send_reply(req);
+}
+
+static NTSTATUS smb2srv_setinfo_file(struct smb2srv_setinfo_op *op, uint8_t smb2_level)
+{
+ union smb_setfileinfo *io;
+ NTSTATUS status;
+
+ io = talloc(op, union smb_setfileinfo);
+ NT_STATUS_HAVE_NO_MEMORY(io);
+
+ /* the levels directly map to the passthru levels */
+ io->generic.level = smb2_level + 1000;
+ io->generic.in.file.ntvfs = op->info->in.file.ntvfs;
+
+ /* handle cases that don't map directly */
+ if (io->generic.level == RAW_SFILEINFO_RENAME_INFORMATION) {
+ io->generic.level = RAW_SFILEINFO_RENAME_INFORMATION_SMB2;
+ }
+
+ status = smbsrv_pull_passthru_sfileinfo(io, io->generic.level, io,
+ &op->info->in.blob,
+ STR_UNICODE, &op->req->in.bufinfo);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ return ntvfs_setfileinfo(op->req->ntvfs, io);
+}
+
+static NTSTATUS smb2srv_setinfo_fs(struct smb2srv_setinfo_op *op, uint8_t smb2_level)
+{
+ switch (smb2_level) {
+ case 0x02:
+ return NT_STATUS_NOT_IMPLEMENTED;
+
+ case 0x06:
+ return NT_STATUS_ACCESS_DENIED;
+
+ case 0x08:
+ return NT_STATUS_ACCESS_DENIED;
+
+ case 0x0A:
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ return NT_STATUS_INVALID_INFO_CLASS;
+}
+
+static NTSTATUS smb2srv_setinfo_security(struct smb2srv_setinfo_op *op, uint8_t smb2_level)
+{
+ union smb_setfileinfo *io;
+ enum ndr_err_code ndr_err;
+
+ switch (smb2_level) {
+ case 0x00:
+ io = talloc(op, union smb_setfileinfo);
+ NT_STATUS_HAVE_NO_MEMORY(io);
+
+ io->set_secdesc.level = RAW_SFILEINFO_SEC_DESC;
+ io->set_secdesc.in.file.ntvfs = op->info->in.file.ntvfs;
+ io->set_secdesc.in.secinfo_flags = op->info->in.flags;
+
+ io->set_secdesc.in.sd = talloc(io, struct security_descriptor);
+ NT_STATUS_HAVE_NO_MEMORY(io->set_secdesc.in.sd);
+
+ ndr_err = ndr_pull_struct_blob(&op->info->in.blob, io, NULL,
+ io->set_secdesc.in.sd,
+ (ndr_pull_flags_fn_t)ndr_pull_security_descriptor);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ return ntvfs_setfileinfo(op->req->ntvfs, io);
+ }
+
+ return NT_STATUS_INVALID_INFO_CLASS;
+}
+
+static NTSTATUS smb2srv_setinfo_backend(struct smb2srv_setinfo_op *op)
+{
+ uint8_t smb2_class;
+ uint8_t smb2_level;
+
+ smb2_class = 0xFF & op->info->in.level;
+ smb2_level = 0xFF & (op->info->in.level>>8);
+
+ switch (smb2_class) {
+ case SMB2_GETINFO_FILE:
+ return smb2srv_setinfo_file(op, smb2_level);
+
+ case SMB2_GETINFO_FS:
+ return smb2srv_setinfo_fs(op, smb2_level);
+
+ case SMB2_GETINFO_SECURITY:
+ return smb2srv_setinfo_security(op, smb2_level);
+
+ case 0x04:
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ return NT_STATUS_INVALID_PARAMETER;
+}
+
+void smb2srv_setinfo_recv(struct smb2srv_request *req)
+{
+ struct smb2_setinfo *info;
+ struct smb2srv_setinfo_op *op;
+
+ SMB2SRV_CHECK_BODY_SIZE(req, 0x20, true);
+ SMB2SRV_TALLOC_IO_PTR(info, struct smb2_setinfo);
+ /* this overwrites req->io_ptr !*/
+ SMB2SRV_TALLOC_IO_PTR(op, struct smb2srv_setinfo_op);
+ op->req = req;
+ op->info = info;
+ SMB2SRV_SETUP_NTVFS_REQUEST(smb2srv_setinfo_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ info->in.level = SVAL(req->in.body, 0x02);
+ SMB2SRV_CHECK(smb2_pull_s32o16_blob(&req->in, info, req->in.body+0x04, &info->in.blob));
+ info->in.flags = IVAL(req->in.body, 0x0C);
+ info->in.file.ntvfs = smb2srv_pull_handle(req, req->in.body, 0x10);
+
+ SMB2SRV_CHECK_FILE_HANDLE(info->in.file.ntvfs);
+ SMB2SRV_CALL_NTVFS_BACKEND(smb2srv_setinfo_backend(op));
+}
diff --git a/source4/smb_server/smb2/fileio.c b/source4/smb_server/smb2/fileio.c
new file mode 100644
index 0000000000..bb894b2c4e
--- /dev/null
+++ b/source4/smb_server/smb2/fileio.c
@@ -0,0 +1,539 @@
+/*
+ Unix SMB2 implementation.
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "smb_server/smb_server.h"
+#include "smb_server/service_smb_proto.h"
+#include "smb_server/smb2/smb2_server.h"
+#include "ntvfs/ntvfs.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "librpc/gen_ndr/ndr_security.h"
+
+static void smb2srv_create_send(struct ntvfs_request *ntvfs)
+{
+ struct smb2srv_request *req;
+ union smb_open *io;
+ DATA_BLOB blob;
+
+ SMB2SRV_CHECK_ASYNC_STATUS(io, union smb_open);
+
+ /* setup the blobs we should give in the reply */
+ if (io->smb2.out.maximal_access != 0) {
+ uint32_t data[2];
+ SIVAL(data, 0, 0);
+ SIVAL(data, 4, io->smb2.out.maximal_access);
+ SMB2SRV_CHECK(smb2_create_blob_add(req, &io->smb2.out.blobs,
+ SMB2_CREATE_TAG_MXAC,
+ data_blob_const(data, 8)));
+ }
+
+
+ SMB2SRV_CHECK(smb2_create_blob_push(req, &blob, io->smb2.out.blobs));
+ SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x58, true, blob.length));
+
+ SCVAL(req->out.body, 0x02, io->smb2.out.oplock_level);
+ SCVAL(req->out.body, 0x03, io->smb2.out.reserved);
+ SIVAL(req->out.body, 0x04, io->smb2.out.create_action);
+ SBVAL(req->out.body, 0x08, io->smb2.out.create_time);
+ SBVAL(req->out.body, 0x10, io->smb2.out.access_time);
+ SBVAL(req->out.body, 0x18, io->smb2.out.write_time);
+ SBVAL(req->out.body, 0x20, io->smb2.out.change_time);
+ SBVAL(req->out.body, 0x28, io->smb2.out.alloc_size);
+ SBVAL(req->out.body, 0x30, io->smb2.out.size);
+ SIVAL(req->out.body, 0x38, io->smb2.out.file_attr);
+ SIVAL(req->out.body, 0x3C, io->smb2.out.reserved2);
+ smb2srv_push_handle(req->out.body, 0x40, io->smb2.out.file.ntvfs);
+ SMB2SRV_CHECK(smb2_push_o32s32_blob(&req->out, 0x50, blob));
+
+ /* also setup the chained file handle */
+ req->chained_file_handle = req->_chained_file_handle;
+ smb2srv_push_handle(req->chained_file_handle, 0, io->smb2.out.file.ntvfs);
+
+ smb2srv_send_reply(req);
+}
+
+void smb2srv_create_recv(struct smb2srv_request *req)
+{
+ union smb_open *io;
+ DATA_BLOB blob;
+ int i;
+
+ SMB2SRV_CHECK_BODY_SIZE(req, 0x38, true);
+ SMB2SRV_TALLOC_IO_PTR(io, union smb_open);
+ SMB2SRV_SETUP_NTVFS_REQUEST(smb2srv_create_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ ZERO_STRUCT(io->smb2.in);
+ io->smb2.level = RAW_OPEN_SMB2;
+ io->smb2.in.security_flags = CVAL(req->in.body, 0x02);
+ io->smb2.in.oplock_level = CVAL(req->in.body, 0x03);
+ io->smb2.in.impersonation_level = IVAL(req->in.body, 0x04);
+ io->smb2.in.create_flags = BVAL(req->in.body, 0x08);
+ io->smb2.in.reserved = BVAL(req->in.body, 0x10);
+ io->smb2.in.desired_access = IVAL(req->in.body, 0x18);
+ io->smb2.in.file_attributes = IVAL(req->in.body, 0x1C);
+ io->smb2.in.share_access = IVAL(req->in.body, 0x20);
+ io->smb2.in.create_disposition = IVAL(req->in.body, 0x24);
+ io->smb2.in.create_options = IVAL(req->in.body, 0x28);
+ SMB2SRV_CHECK(smb2_pull_o16s16_string(&req->in, io, req->in.body+0x2C, &io->smb2.in.fname));
+ SMB2SRV_CHECK(smb2_pull_o32s32_blob(&req->in, io, req->in.body+0x30, &blob));
+ SMB2SRV_CHECK(smb2_create_blob_parse(io, blob, &io->smb2.in.blobs));
+
+ /* interpret the parsed tags that a server needs to respond to */
+ for (i=0;i<io->smb2.in.blobs.num_blobs;i++) {
+ if (strcmp(io->smb2.in.blobs.blobs[i].tag, SMB2_CREATE_TAG_EXTA) == 0) {
+ SMB2SRV_CHECK(ea_pull_list_chained(&io->smb2.in.blobs.blobs[i].data, io,
+ &io->smb2.in.eas.num_eas,
+ &io->smb2.in.eas.eas));
+ }
+ if (strcmp(io->smb2.in.blobs.blobs[i].tag, SMB2_CREATE_TAG_SECD) == 0) {
+ enum ndr_err_code ndr_err;
+ io->smb2.in.sec_desc = talloc(io, struct security_descriptor);
+ if (io->smb2.in.sec_desc == NULL) {
+ smb2srv_send_error(req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ ndr_err = ndr_pull_struct_blob(&io->smb2.in.blobs.blobs[i].data, io, NULL,
+ io->smb2.in.sec_desc,
+ (ndr_pull_flags_fn_t)ndr_pull_security_descriptor);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ smb2srv_send_error(req, ndr_map_error2ntstatus(ndr_err));
+ return;
+ }
+ }
+ if (strcmp(io->smb2.in.blobs.blobs[i].tag, SMB2_CREATE_TAG_DHNQ) == 0) {
+ io->smb2.in.durable_open = true;
+ }
+ if (strcmp(io->smb2.in.blobs.blobs[i].tag, SMB2_CREATE_TAG_DHNC) == 0) {
+ if (io->smb2.in.blobs.blobs[i].data.length != 16) {
+ smb2srv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+ io->smb2.in.durable_handle = talloc(io, struct smb2_handle);
+ if (io->smb2.in.durable_handle == NULL) {
+ smb2srv_send_error(req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ smb2_pull_handle(io->smb2.in.blobs.blobs[i].data.data, io->smb2.in.durable_handle);
+ }
+ if (strcmp(io->smb2.in.blobs.blobs[i].tag, SMB2_CREATE_TAG_ALSI) == 0) {
+ if (io->smb2.in.blobs.blobs[i].data.length != 8) {
+ smb2srv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+ io->smb2.in.alloc_size = BVAL(io->smb2.in.blobs.blobs[i].data.data, 0);
+ }
+ if (strcmp(io->smb2.in.blobs.blobs[i].tag, SMB2_CREATE_TAG_MXAC) == 0) {
+ io->smb2.in.query_maximal_access = true;
+ }
+ if (strcmp(io->smb2.in.blobs.blobs[i].tag, SMB2_CREATE_TAG_TWRP) == 0) {
+ if (io->smb2.in.blobs.blobs[i].data.length != 8) {
+ smb2srv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+ io->smb2.in.timewarp = BVAL(io->smb2.in.blobs.blobs[i].data.data, 0);
+ }
+ if (strcmp(io->smb2.in.blobs.blobs[i].tag, SMB2_CREATE_TAG_QFID) == 0) {
+ io->smb2.in.query_on_disk_id = true;
+ }
+ }
+
+ /* the VFS backend does not yet handle NULL filenames */
+ if (io->smb2.in.fname == NULL) {
+ io->smb2.in.fname = "";
+ }
+
+ SMB2SRV_CALL_NTVFS_BACKEND(ntvfs_open(req->ntvfs, io));
+}
+
+static void smb2srv_close_send(struct ntvfs_request *ntvfs)
+{
+ struct smb2srv_request *req;
+ union smb_close *io;
+
+ SMB2SRV_CHECK_ASYNC_STATUS(io, union smb_close);
+ SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x3C, false, 0));
+
+ SSVAL(req->out.body, 0x02, io->smb2.out.flags);
+ SIVAL(req->out.body, 0x04, io->smb2.out._pad);
+ SBVAL(req->out.body, 0x08, io->smb2.out.create_time);
+ SBVAL(req->out.body, 0x10, io->smb2.out.access_time);
+ SBVAL(req->out.body, 0x18, io->smb2.out.write_time);
+ SBVAL(req->out.body, 0x20, io->smb2.out.change_time);
+ SBVAL(req->out.body, 0x28, io->smb2.out.alloc_size);
+ SBVAL(req->out.body, 0x30, io->smb2.out.size);
+ SIVAL(req->out.body, 0x38, io->smb2.out.file_attr);
+
+ smb2srv_send_reply(req);
+}
+
+void smb2srv_close_recv(struct smb2srv_request *req)
+{
+ union smb_close *io;
+
+ SMB2SRV_CHECK_BODY_SIZE(req, 0x18, false);
+ SMB2SRV_TALLOC_IO_PTR(io, union smb_close);
+ SMB2SRV_SETUP_NTVFS_REQUEST(smb2srv_close_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ io->smb2.level = RAW_CLOSE_SMB2;
+ io->smb2.in.flags = SVAL(req->in.body, 0x02);
+ io->smb2.in._pad = IVAL(req->in.body, 0x04);
+ io->smb2.in.file.ntvfs = smb2srv_pull_handle(req, req->in.body, 0x08);
+
+ SMB2SRV_CHECK_FILE_HANDLE(io->smb2.in.file.ntvfs);
+ SMB2SRV_CALL_NTVFS_BACKEND(ntvfs_close(req->ntvfs, io));
+}
+
+static void smb2srv_flush_send(struct ntvfs_request *ntvfs)
+{
+ struct smb2srv_request *req;
+ union smb_flush *io;
+
+ SMB2SRV_CHECK_ASYNC_STATUS(io, union smb_flush);
+ SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x04, false, 0));
+
+ SSVAL(req->out.body, 0x02, io->smb2.out.reserved);
+
+ smb2srv_send_reply(req);
+}
+
+void smb2srv_flush_recv(struct smb2srv_request *req)
+{
+ union smb_flush *io;
+
+ SMB2SRV_CHECK_BODY_SIZE(req, 0x18, false);
+ SMB2SRV_TALLOC_IO_PTR(io, union smb_flush);
+ SMB2SRV_SETUP_NTVFS_REQUEST(smb2srv_flush_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ io->smb2.level = RAW_FLUSH_SMB2;
+ io->smb2.in.reserved1 = SVAL(req->in.body, 0x02);
+ io->smb2.in.reserved2 = IVAL(req->in.body, 0x04);
+ io->smb2.in.file.ntvfs = smb2srv_pull_handle(req, req->in.body, 0x08);
+
+ SMB2SRV_CHECK_FILE_HANDLE(io->smb2.in.file.ntvfs);
+ SMB2SRV_CALL_NTVFS_BACKEND(ntvfs_flush(req->ntvfs, io));
+}
+
+static void smb2srv_read_send(struct ntvfs_request *ntvfs)
+{
+ struct smb2srv_request *req;
+ union smb_read *io;
+
+ SMB2SRV_CHECK_ASYNC_STATUS(io, union smb_read);
+ SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x10, true, io->smb2.out.data.length));
+
+ /* TODO: avoid the memcpy */
+ SMB2SRV_CHECK(smb2_push_o16s32_blob(&req->out, 0x02, io->smb2.out.data));
+ SIVAL(req->out.body, 0x08, io->smb2.out.remaining);
+ SIVAL(req->out.body, 0x0C, io->smb2.out.reserved);
+
+ smb2srv_send_reply(req);
+}
+
+void smb2srv_read_recv(struct smb2srv_request *req)
+{
+ union smb_read *io;
+
+ SMB2SRV_CHECK_BODY_SIZE(req, 0x30, true);
+
+ /* MS-SMB2 2.2.19 read must have a single byte of zero */
+ if (req->in.body_size - req->in.body_fixed < 1) {
+ smb2srv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+ SMB2SRV_TALLOC_IO_PTR(io, union smb_read);
+ SMB2SRV_SETUP_NTVFS_REQUEST(smb2srv_read_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ io->smb2.level = RAW_READ_SMB2;
+ io->smb2.in._pad = SVAL(req->in.body, 0x02);
+ io->smb2.in.length = IVAL(req->in.body, 0x04);
+ io->smb2.in.offset = BVAL(req->in.body, 0x08);
+ io->smb2.in.file.ntvfs = smb2srv_pull_handle(req, req->in.body, 0x10);
+ io->smb2.in.min_count = IVAL(req->in.body, 0x20);
+ io->smb2.in.channel = IVAL(req->in.body, 0x24);
+ io->smb2.in.remaining = IVAL(req->in.body, 0x28);
+ io->smb2.in.channel_offset = SVAL(req->in.body, 0x2C);
+ io->smb2.in.channel_length = SVAL(req->in.body, 0x2E);
+
+ SMB2SRV_CHECK_FILE_HANDLE(io->smb2.in.file.ntvfs);
+
+ /* preallocate the buffer for the backends */
+ io->smb2.out.data = data_blob_talloc(io, NULL, io->smb2.in.length);
+ if (io->smb2.out.data.length != io->smb2.in.length) {
+ SMB2SRV_CHECK(NT_STATUS_NO_MEMORY);
+ }
+
+ SMB2SRV_CALL_NTVFS_BACKEND(ntvfs_read(req->ntvfs, io));
+}
+
+static void smb2srv_write_send(struct ntvfs_request *ntvfs)
+{
+ struct smb2srv_request *req;
+ union smb_write *io;
+
+ SMB2SRV_CHECK_ASYNC_STATUS(io, union smb_write);
+ SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x10, true, 0));
+
+ SSVAL(req->out.body, 0x02, io->smb2.out._pad);
+ SIVAL(req->out.body, 0x04, io->smb2.out.nwritten);
+ SBVAL(req->out.body, 0x08, io->smb2.out.unknown1);
+
+ smb2srv_send_reply(req);
+}
+
+void smb2srv_write_recv(struct smb2srv_request *req)
+{
+ union smb_write *io;
+
+ SMB2SRV_CHECK_BODY_SIZE(req, 0x30, true);
+ SMB2SRV_TALLOC_IO_PTR(io, union smb_write);
+ SMB2SRV_SETUP_NTVFS_REQUEST(smb2srv_write_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ /* TODO: avoid the memcpy */
+ io->smb2.level = RAW_WRITE_SMB2;
+ SMB2SRV_CHECK(smb2_pull_o16s32_blob(&req->in, io, req->in.body+0x02, &io->smb2.in.data));
+ io->smb2.in.offset = BVAL(req->in.body, 0x08);
+ io->smb2.in.file.ntvfs = smb2srv_pull_handle(req, req->in.body, 0x10);
+ io->smb2.in.unknown1 = BVAL(req->in.body, 0x20);
+ io->smb2.in.unknown2 = BVAL(req->in.body, 0x28);
+
+ SMB2SRV_CHECK_FILE_HANDLE(io->smb2.in.file.ntvfs);
+ SMB2SRV_CALL_NTVFS_BACKEND(ntvfs_write(req->ntvfs, io));
+}
+
+static void smb2srv_lock_send(struct ntvfs_request *ntvfs)
+{
+ struct smb2srv_request *req;
+ union smb_lock *io;
+
+ SMB2SRV_CHECK_ASYNC_STATUS_ERR(io, union smb_lock);
+ SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x04, false, 0));
+
+ SSVAL(req->out.body, 0x02, io->smb2.out.reserved);
+
+ smb2srv_send_reply(req);
+}
+
+void smb2srv_lock_recv(struct smb2srv_request *req)
+{
+ union smb_lock *io;
+ int i;
+
+ SMB2SRV_CHECK_BODY_SIZE(req, 0x30, false);
+ SMB2SRV_TALLOC_IO_PTR(io, union smb_lock);
+ SMB2SRV_SETUP_NTVFS_REQUEST(smb2srv_lock_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ io->smb2.level = RAW_LOCK_SMB2;
+ io->smb2.in.lock_count = SVAL(req->in.body, 0x02);
+ io->smb2.in.reserved = IVAL(req->in.body, 0x04);
+ io->smb2.in.file.ntvfs = smb2srv_pull_handle(req, req->in.body, 0x08);
+ if (req->in.body_size < 24 + 24*(uint64_t)io->smb2.in.lock_count) {
+ DEBUG(0,("%s: lock buffer too small\n", __location__));
+ smb2srv_send_error(req, NT_STATUS_FOOBAR);
+ return;
+ }
+ io->smb2.in.locks = talloc_array(io, struct smb2_lock_element,
+ io->smb2.in.lock_count);
+ if (io->smb2.in.locks == NULL) {
+ smb2srv_send_error(req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+
+ for (i=0;i<io->smb2.in.lock_count;i++) {
+ io->smb2.in.locks[i].offset = BVAL(req->in.body, 24 + i*24);
+ io->smb2.in.locks[i].length = BVAL(req->in.body, 32 + i*24);
+ io->smb2.in.locks[i].flags = IVAL(req->in.body, 40 + i*24);
+ io->smb2.in.locks[i].reserved = IVAL(req->in.body, 44 + i*24);
+ }
+
+ SMB2SRV_CHECK_FILE_HANDLE(io->smb2.in.file.ntvfs);
+ SMB2SRV_CALL_NTVFS_BACKEND(ntvfs_lock(req->ntvfs, io));
+}
+
+static void smb2srv_ioctl_send(struct ntvfs_request *ntvfs)
+{
+ struct smb2srv_request *req;
+ union smb_ioctl *io;
+
+ SMB2SRV_CHECK_ASYNC_STATUS_ERR(io, union smb_ioctl);
+ SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x30, true, 0));
+
+ SSVAL(req->out.body, 0x02, io->smb2.out._pad);
+ SIVAL(req->out.body, 0x04, io->smb2.out.function);
+ if (io->smb2.level == RAW_IOCTL_SMB2_NO_HANDLE) {
+ struct smb2_handle h;
+ h.data[0] = UINT64_MAX;
+ h.data[1] = UINT64_MAX;
+ smb2_push_handle(req->out.body + 0x08, &h);
+ } else {
+ smb2srv_push_handle(req->out.body, 0x08,io->smb2.in.file.ntvfs);
+ }
+ SMB2SRV_CHECK(smb2_push_o32s32_blob(&req->out, 0x18, io->smb2.out.in));
+ SMB2SRV_CHECK(smb2_push_o32s32_blob(&req->out, 0x20, io->smb2.out.out));
+ SIVAL(req->out.body, 0x28, io->smb2.out.unknown2);
+ SIVAL(req->out.body, 0x2C, io->smb2.out.unknown3);
+
+ smb2srv_send_reply(req);
+}
+
+void smb2srv_ioctl_recv(struct smb2srv_request *req)
+{
+ union smb_ioctl *io;
+ struct smb2_handle h;
+
+ SMB2SRV_CHECK_BODY_SIZE(req, 0x38, true);
+ SMB2SRV_TALLOC_IO_PTR(io, union smb_ioctl);
+ SMB2SRV_SETUP_NTVFS_REQUEST(smb2srv_ioctl_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ /* TODO: avoid the memcpy */
+ io->smb2.in._pad = SVAL(req->in.body, 0x02);
+ io->smb2.in.function = IVAL(req->in.body, 0x04);
+ /* file handle ... */
+ SMB2SRV_CHECK(smb2_pull_o32s32_blob(&req->in, io, req->in.body+0x18, &io->smb2.in.out));
+ io->smb2.in.unknown2 = IVAL(req->in.body, 0x20);
+ SMB2SRV_CHECK(smb2_pull_o32s32_blob(&req->in, io, req->in.body+0x24, &io->smb2.in.in));
+ io->smb2.in.max_response_size = IVAL(req->in.body, 0x2C);
+ io->smb2.in.flags = BVAL(req->in.body, 0x30);
+
+ smb2_pull_handle(req->in.body + 0x08, &h);
+ if (h.data[0] == UINT64_MAX && h.data[1] == UINT64_MAX) {
+ io->smb2.level = RAW_IOCTL_SMB2_NO_HANDLE;
+ } else {
+ io->smb2.level = RAW_IOCTL_SMB2;
+ io->smb2.in.file.ntvfs = smb2srv_pull_handle(req, req->in.body, 0x08);
+ SMB2SRV_CHECK_FILE_HANDLE(io->smb2.in.file.ntvfs);
+ }
+
+ SMB2SRV_CALL_NTVFS_BACKEND(ntvfs_ioctl(req->ntvfs, io));
+}
+
+static void smb2srv_notify_send(struct ntvfs_request *ntvfs)
+{
+ struct smb2srv_request *req;
+ union smb_notify *io;
+ size_t size = 0;
+ int i;
+ uint8_t *p;
+ DATA_BLOB blob = data_blob(NULL, 0);
+
+ SMB2SRV_CHECK_ASYNC_STATUS(io, union smb_notify);
+ SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x08, true, 0));
+
+#define MAX_BYTES_PER_CHAR 3
+
+ /* work out how big the reply buffer could be */
+ for (i=0;i<io->smb2.out.num_changes;i++) {
+ size += 12 + 3 + (1+strlen(io->smb2.out.changes[i].name.s)) * MAX_BYTES_PER_CHAR;
+ }
+
+ blob = data_blob_talloc(req, NULL, size);
+ if (size > 0 && !blob.data) {
+ SMB2SRV_CHECK(NT_STATUS_NO_MEMORY);
+ }
+
+ p = blob.data;
+
+ /* construct the changes buffer */
+ for (i=0;i<io->smb2.out.num_changes;i++) {
+ uint32_t ofs;
+ ssize_t len;
+
+ SIVAL(p, 4, io->smb2.out.changes[i].action);
+ len = push_string(p + 12, io->smb2.out.changes[i].name.s,
+ blob.length - (p+12 - blob.data), STR_UNICODE);
+ SIVAL(p, 8, len);
+
+ ofs = len + 12;
+
+ if (ofs & 3) {
+ int pad = 4 - (ofs & 3);
+ memset(p+ofs, 0, pad);
+ ofs += pad;
+ }
+
+ if (i == io->smb2.out.num_changes-1) {
+ SIVAL(p, 0, 0);
+ } else {
+ SIVAL(p, 0, ofs);
+ }
+
+ p += ofs;
+ }
+
+ blob.length = p - blob.data;
+
+ SMB2SRV_CHECK(smb2_push_o16s32_blob(&req->out, 0x02, blob));
+
+ smb2srv_send_reply(req);
+}
+
+void smb2srv_notify_recv(struct smb2srv_request *req)
+{
+ union smb_notify *io;
+
+ SMB2SRV_CHECK_BODY_SIZE(req, 0x20, false);
+ SMB2SRV_TALLOC_IO_PTR(io, union smb_notify);
+ SMB2SRV_SETUP_NTVFS_REQUEST(smb2srv_notify_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ io->smb2.level = RAW_NOTIFY_SMB2;
+ io->smb2.in.recursive = SVAL(req->in.body, 0x02);
+ io->smb2.in.buffer_size = IVAL(req->in.body, 0x04);
+ io->smb2.in.file.ntvfs = smb2srv_pull_handle(req, req->in.body, 0x08);
+ io->smb2.in.completion_filter = IVAL(req->in.body, 0x18);
+ io->smb2.in.unknown = BVAL(req->in.body, 0x1C);
+
+ SMB2SRV_CHECK_FILE_HANDLE(io->smb2.in.file.ntvfs);
+ SMB2SRV_CALL_NTVFS_BACKEND(ntvfs_notify(req->ntvfs, io));
+}
+
+static void smb2srv_break_send(struct ntvfs_request *ntvfs)
+{
+ struct smb2srv_request *req;
+ union smb_lock *io;
+
+ SMB2SRV_CHECK_ASYNC_STATUS_ERR(io, union smb_lock);
+ SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x18, false, 0));
+
+ SCVAL(req->out.body, 0x02, io->smb2_break.out.oplock_level);
+ SCVAL(req->out.body, 0x03, io->smb2_break.out.reserved);
+ SIVAL(req->out.body, 0x04, io->smb2_break.out.reserved2);
+ smb2srv_push_handle(req->out.body, 0x08,io->smb2_break.out.file.ntvfs);
+
+ smb2srv_send_reply(req);
+}
+
+void smb2srv_break_recv(struct smb2srv_request *req)
+{
+ union smb_lock *io;
+
+ SMB2SRV_CHECK_BODY_SIZE(req, 0x18, false);
+ SMB2SRV_TALLOC_IO_PTR(io, union smb_lock);
+ SMB2SRV_SETUP_NTVFS_REQUEST(smb2srv_break_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ io->smb2_break.level = RAW_LOCK_SMB2_BREAK;
+ io->smb2_break.in.oplock_level = CVAL(req->in.body, 0x02);
+ io->smb2_break.in.reserved = CVAL(req->in.body, 0x03);
+ io->smb2_break.in.reserved2 = IVAL(req->in.body, 0x04);
+ io->smb2_break.in.file.ntvfs = smb2srv_pull_handle(req, req->in.body, 0x08);
+
+ SMB2SRV_CHECK_FILE_HANDLE(io->smb2_break.in.file.ntvfs);
+ SMB2SRV_CALL_NTVFS_BACKEND(ntvfs_lock(req->ntvfs, io));
+}
diff --git a/source4/smb_server/smb2/find.c b/source4/smb_server/smb2/find.c
new file mode 100644
index 0000000000..10fcda9434
--- /dev/null
+++ b/source4/smb_server/smb2/find.c
@@ -0,0 +1,171 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB2 Find
+ Copyright (C) Andrew Tridgell 2003
+ 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 <http://www.gnu.org/licenses/>.
+*/
+/*
+ This file handles the parsing of transact2 requests
+*/
+
+#include "includes.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "smb_server/smb_server.h"
+#include "smb_server/service_smb_proto.h"
+#include "smb_server/smb2/smb2_server.h"
+#include "ntvfs/ntvfs.h"
+
+
+/* a structure to encapsulate the state information about an in-progress ffirst/fnext operation */
+struct smb2srv_find_state {
+ struct smb2srv_request *req;
+ struct smb2_find *info;
+ union smb_search_first *ff;
+ union smb_search_next *fn;
+ uint32_t last_entry_offset;
+};
+
+/* callback function for SMB2 Find */
+static bool smb2srv_find_callback(void *private_data, const union smb_search_data *file)
+{
+ struct smb2srv_find_state *state = talloc_get_type(private_data, struct smb2srv_find_state);
+ struct smb2_find *info = state->info;
+ uint32_t old_length;
+ NTSTATUS status;
+
+ old_length = info->out.blob.length;
+
+ status = smbsrv_push_passthru_search(state, &info->out.blob, info->data_level, file, STR_UNICODE);
+ if (!NT_STATUS_IS_OK(status) ||
+ info->out.blob.length > info->in.max_response_size) {
+ /* restore the old length and tell the backend to stop */
+ smbsrv_blob_grow_data(state, &info->out.blob, old_length);
+ return false;
+ }
+
+ state->last_entry_offset = old_length;
+
+ return true;
+}
+
+static void smb2srv_find_send(struct ntvfs_request *ntvfs)
+{
+ struct smb2srv_request *req;
+ struct smb2srv_find_state *state;
+
+ SMB2SRV_CHECK_ASYNC_STATUS(state, struct smb2srv_find_state);
+ SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x08, true, state->info->out.blob.length));
+
+ if (state->info->out.blob.length > 0) {
+ SIVAL(state->info->out.blob.data + state->last_entry_offset, 0, 0);
+ }
+
+ SMB2SRV_CHECK(smb2_push_o16s32_blob(&req->out, 0x02, state->info->out.blob));
+
+ smb2srv_send_reply(req);
+}
+
+static NTSTATUS smb2srv_find_backend(struct smb2srv_find_state *state)
+{
+ struct smb2_find *info = state->info;
+
+ switch (info->in.level) {
+ case SMB2_FIND_DIRECTORY_INFO:
+ info->data_level = RAW_SEARCH_DATA_DIRECTORY_INFO;
+ break;
+
+ case SMB2_FIND_FULL_DIRECTORY_INFO:
+ info->data_level = RAW_SEARCH_DATA_FULL_DIRECTORY_INFO;
+ break;
+
+ case SMB2_FIND_BOTH_DIRECTORY_INFO:
+ info->data_level = RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO;
+ break;
+
+ case SMB2_FIND_NAME_INFO:
+ info->data_level = RAW_SEARCH_DATA_NAME_INFO;
+ break;
+
+ case SMB2_FIND_ID_BOTH_DIRECTORY_INFO:
+ info->data_level = RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO;
+ break;
+
+ case SMB2_FIND_ID_FULL_DIRECTORY_INFO:
+ info->data_level = RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO;
+ break;
+
+ default:
+ return NT_STATUS_FOOBAR;
+ }
+
+ if (info->in.continue_flags & SMB2_CONTINUE_FLAG_REOPEN) {
+ state->ff = talloc(state, union smb_search_first);
+ NT_STATUS_HAVE_NO_MEMORY(state->ff);
+
+ state->ff->smb2 = *info;
+ state->info = &state->ff->smb2;
+ ZERO_STRUCT(state->ff->smb2.out);
+
+ return ntvfs_search_first(state->req->ntvfs, state->ff, state, smb2srv_find_callback);
+ } else {
+ state->fn = talloc(state, union smb_search_next);
+ NT_STATUS_HAVE_NO_MEMORY(state->fn);
+
+ state->fn->smb2 = *info;
+ state->info = &state->fn->smb2;
+ ZERO_STRUCT(state->fn->smb2.out);
+
+ return ntvfs_search_next(state->req->ntvfs, state->fn, state, smb2srv_find_callback);
+ }
+
+ /* should not be reached */
+ return NT_STATUS_INTERNAL_ERROR;
+}
+
+void smb2srv_find_recv(struct smb2srv_request *req)
+{
+ struct smb2srv_find_state *state;
+ struct smb2_find *info;
+
+ SMB2SRV_CHECK_BODY_SIZE(req, 0x20, true);
+ SMB2SRV_TALLOC_IO_PTR(info, struct smb2_find);
+ /* this overwrites req->io_ptr !*/
+ SMB2SRV_TALLOC_IO_PTR(state, struct smb2srv_find_state);
+ state->req = req;
+ state->info = info;
+ state->ff = NULL;
+ state->fn = NULL;
+ state->last_entry_offset= 0;
+ SMB2SRV_SETUP_NTVFS_REQUEST(smb2srv_find_send, NTVFS_ASYNC_STATE_MAY_ASYNC);
+
+ info->level = RAW_SEARCH_SMB2;
+ info->data_level = RAW_SEARCH_DATA_GENERIC;/* will be overwritten later */
+ info->in.level = CVAL(req->in.body, 0x02);
+ info->in.continue_flags = CVAL(req->in.body, 0x03);
+ info->in.file_index = IVAL(req->in.body, 0x04);
+ info->in.file.ntvfs = smb2srv_pull_handle(req, req->in.body, 0x08);
+ SMB2SRV_CHECK(smb2_pull_o16s16_string(&req->in, info, req->in.body+0x18, &info->in.pattern));
+ info->in.max_response_size = IVAL(req->in.body, 0x1C);
+
+ /* the VFS backend does not yet handle NULL patterns */
+ if (info->in.pattern == NULL) {
+ info->in.pattern = "";
+ }
+
+ SMB2SRV_CHECK_FILE_HANDLE(info->in.file.ntvfs);
+ SMB2SRV_CALL_NTVFS_BACKEND(smb2srv_find_backend(state));
+}
diff --git a/source4/smb_server/smb2/keepalive.c b/source4/smb_server/smb2/keepalive.c
new file mode 100644
index 0000000000..ff47d594f0
--- /dev/null
+++ b/source4/smb_server/smb2/keepalive.c
@@ -0,0 +1,76 @@
+/*
+ Unix SMB2 implementation.
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/smb2/smb2.h"
+#include "smb_server/smb_server.h"
+#include "smb_server/service_smb_proto.h"
+#include "smb_server/smb2/smb2_server.h"
+
+static NTSTATUS smb2srv_keepalive_backend(struct smb2srv_request *req)
+{
+ /* TODO: maybe update some flags on the connection struct */
+ return NT_STATUS_OK;
+}
+
+static void smb2srv_keepalive_send(struct smb2srv_request *req)
+{
+ NTSTATUS status;
+
+ if (NT_STATUS_IS_ERR(req->status)) {
+ smb2srv_send_error(req, req->status);
+ return;
+ }
+
+ status = smb2srv_setup_reply(req, 0x04, false, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ smbsrv_terminate_connection(req->smb_conn, nt_errstr(status));
+ talloc_free(req);
+ return;
+ }
+
+ SSVAL(req->out.body, 0x02, 0);
+
+ smb2srv_send_reply(req);
+}
+
+void smb2srv_keepalive_recv(struct smb2srv_request *req)
+{
+ uint16_t _pad;
+
+ if (req->in.body_size != 0x04) {
+ smb2srv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ if (SVAL(req->in.body, 0x00) != 0x04) {
+ smb2srv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ _pad = SVAL(req->in.body, 0x02);
+
+ req->status = smb2srv_keepalive_backend(req);
+
+ if (req->control_flags & SMB2SRV_REQ_CTRL_FLAG_NOT_REPLY) {
+ talloc_free(req);
+ return;
+ }
+ smb2srv_keepalive_send(req);
+}
diff --git a/source4/smb_server/smb2/negprot.c b/source4/smb_server/smb2/negprot.c
new file mode 100644
index 0000000000..0b65a19634
--- /dev/null
+++ b/source4/smb_server/smb2/negprot.c
@@ -0,0 +1,292 @@
+/*
+ Unix SMB2 implementation.
+
+ Copyright (C) Andrew Bartlett 2001-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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "auth/credentials/credentials.h"
+#include "auth/auth.h"
+#include "auth/gensec/gensec.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "smb_server/smb_server.h"
+#include "smb_server/service_smb_proto.h"
+#include "smb_server/smb2/smb2_server.h"
+#include "smbd/service_stream.h"
+#include "param/param.h"
+#include "librpc/ndr/libndr.h"
+
+static NTSTATUS smb2srv_negprot_secblob(struct smb2srv_request *req, DATA_BLOB *_blob)
+{
+ struct gensec_security *gensec_security;
+ DATA_BLOB null_data_blob = data_blob(NULL, 0);
+ DATA_BLOB blob;
+ NTSTATUS nt_status;
+ struct cli_credentials *server_credentials;
+
+ server_credentials = cli_credentials_init(req);
+ if (!server_credentials) {
+ smbsrv_terminate_connection(req->smb_conn, "Failed to init server credentials\n");
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ cli_credentials_set_conf(server_credentials, req->smb_conn->lp_ctx);
+ nt_status = cli_credentials_set_machine_account(server_credentials, req->smb_conn->lp_ctx);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(10, ("Failed to obtain server credentials, perhaps a standalone server?: %s\n", nt_errstr(nt_status)));
+ talloc_free(server_credentials);
+ server_credentials = NULL;
+ }
+
+ req->smb_conn->negotiate.server_credentials = talloc_steal(req->smb_conn, server_credentials);
+
+ nt_status = samba_server_gensec_start(req,
+ req->smb_conn->connection->event.ctx,
+ req->smb_conn->connection->msg_ctx,
+ req->smb_conn->lp_ctx,
+ server_credentials,
+ "cifs",
+ &gensec_security);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("Failed to start GENSEC: %s\n", nt_errstr(nt_status)));
+ smbsrv_terminate_connection(req->smb_conn, "Failed to start GENSEC\n");
+ return nt_status;
+ }
+
+ gensec_set_target_service(gensec_security, "cifs");
+
+ gensec_set_credentials(gensec_security, server_credentials);
+
+ nt_status = gensec_start_mech_by_oid(gensec_security, GENSEC_OID_SPNEGO);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("Failed to start SPNEGO: %s\n", nt_errstr(nt_status)));
+ smbsrv_terminate_connection(req->smb_conn, "Failed to start SPNEGO\n");
+ return nt_status;
+ }
+
+ nt_status = gensec_update(gensec_security, req, null_data_blob, &blob);
+ if (!NT_STATUS_IS_OK(nt_status) && !NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ DEBUG(0, ("Failed to get SPNEGO to give us the first token: %s\n", nt_errstr(nt_status)));
+ smbsrv_terminate_connection(req->smb_conn, "Failed to start SPNEGO - no first token\n");
+ return nt_status;
+ }
+
+ *_blob = blob;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS smb2srv_negprot_backend(struct smb2srv_request *req, struct smb2_negprot *io)
+{
+ NTSTATUS status;
+ struct timeval current_time;
+ struct timeval boot_time;
+
+ /* we only do one dialect for now */
+ if (io->in.dialect_count < 1) {
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+ if (io->in.dialects[0] != 0 &&
+ io->in.dialects[0] != SMB2_DIALECT_REVISION) {
+ DEBUG(0,("Got unexpected SMB2 dialect %u\n", io->in.dialects[0]));
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ req->smb_conn->negotiate.protocol = PROTOCOL_SMB2;
+
+ current_time = timeval_current(); /* TODO: handle timezone?! */
+ boot_time = timeval_current(); /* TODO: fix me */
+
+ ZERO_STRUCT(io->out);
+ switch (lp_server_signing(req->smb_conn->lp_ctx)) {
+ case SMB_SIGNING_OFF:
+ io->out.security_mode = 0;
+ break;
+ case SMB_SIGNING_SUPPORTED:
+ case SMB_SIGNING_AUTO:
+ io->out.security_mode = SMB2_NEGOTIATE_SIGNING_ENABLED;
+ break;
+ case SMB_SIGNING_REQUIRED:
+ io->out.security_mode = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED;
+ /* force signing on immediately */
+ req->smb_conn->smb2_signing_required = true;
+ break;
+ }
+ io->out.dialect_revision = SMB2_DIALECT_REVISION;
+ io->out.capabilities = 0;
+ io->out.max_transact_size = lp_parm_ulong(req->smb_conn->lp_ctx, NULL,
+ "smb2", "max transaction size", 0x10000);
+ io->out.max_read_size = lp_parm_ulong(req->smb_conn->lp_ctx, NULL,
+ "smb2", "max read size", 0x10000);
+ io->out.max_write_size = lp_parm_ulong(req->smb_conn->lp_ctx, NULL,
+ "smb2", "max write size", 0x10000);
+ io->out.system_time = timeval_to_nttime(&current_time);
+ io->out.server_start_time = timeval_to_nttime(&boot_time);
+ io->out.reserved2 = 0;
+ status = smb2srv_negprot_secblob(req, &io->out.secblob);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ return NT_STATUS_OK;
+}
+
+static void smb2srv_negprot_send(struct smb2srv_request *req, struct smb2_negprot *io)
+{
+ NTSTATUS status;
+ enum ndr_err_code ndr_err;
+
+ if (NT_STATUS_IS_ERR(req->status)) {
+ smb2srv_send_error(req, req->status); /* TODO: is this correct? */
+ return;
+ }
+
+ status = smb2srv_setup_reply(req, 0x40, true, io->out.secblob.length);
+ if (!NT_STATUS_IS_OK(status)) {
+ smbsrv_terminate_connection(req->smb_conn, nt_errstr(status));
+ talloc_free(req);
+ return;
+ }
+
+ SSVAL(req->out.body, 0x02, io->out.security_mode);
+ SIVAL(req->out.body, 0x04, io->out.dialect_revision);
+ SIVAL(req->out.body, 0x06, io->out.reserved);
+ ndr_err = smbcli_push_guid(req->out.body, 0x08, &io->out.server_guid);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ smbsrv_terminate_connection(req->smb_conn, nt_errstr(status));
+ talloc_free(req);
+ return;
+ }
+ SIVAL(req->out.body, 0x18, io->out.capabilities);
+ SIVAL(req->out.body, 0x1C, io->out.max_transact_size);
+ SIVAL(req->out.body, 0x20, io->out.max_read_size);
+ SIVAL(req->out.body, 0x24, io->out.max_write_size);
+ push_nttime(req->out.body, 0x28, io->out.system_time);
+ push_nttime(req->out.body, 0x30, io->out.server_start_time);
+ SIVAL(req->out.body, 0x3C, io->out.reserved2);
+ status = smb2_push_o16s16_blob(&req->out, 0x38, io->out.secblob);
+ if (!NT_STATUS_IS_OK(status)) {
+ smbsrv_terminate_connection(req->smb_conn, nt_errstr(status));
+ talloc_free(req);
+ return;
+ }
+
+ smb2srv_send_reply(req);
+}
+
+void smb2srv_negprot_recv(struct smb2srv_request *req)
+{
+ struct smb2_negprot *io;
+ int i;
+ enum ndr_err_code ndr_err;
+
+ if (req->in.body_size < 0x26) {
+ smbsrv_terminate_connection(req->smb_conn, "Bad body size in SMB2 negprot");
+ return;
+ }
+
+ io = talloc(req, struct smb2_negprot);
+ if (!io) {
+ smbsrv_terminate_connection(req->smb_conn, nt_errstr(NT_STATUS_NO_MEMORY));
+ talloc_free(req);
+ return;
+ }
+
+ io->in.dialect_count = SVAL(req->in.body, 0x02);
+ io->in.security_mode = SVAL(req->in.body, 0x04);
+ io->in.reserved = SVAL(req->in.body, 0x06);
+ io->in.capabilities = IVAL(req->in.body, 0x08);
+ ndr_err = smbcli_pull_guid(req->in.body, 0xC, &io->in.client_guid);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ smbsrv_terminate_connection(req->smb_conn, "Bad GUID in SMB2 negprot");
+ talloc_free(req);
+ return;
+ }
+ io->in.start_time = smbcli_pull_nttime(req->in.body, 0x1C);
+
+ io->in.dialects = talloc_array(req, uint16_t, io->in.dialect_count);
+ if (io->in.dialects == NULL) {
+ smbsrv_terminate_connection(req->smb_conn, nt_errstr(NT_STATUS_NO_MEMORY));
+ talloc_free(req);
+ return;
+ }
+ for (i=0;i<io->in.dialect_count;i++) {
+ io->in.dialects[i] = SVAL(req->in.body, 0x24+i*2);
+ }
+
+ req->status = smb2srv_negprot_backend(req, io);
+
+ if (req->control_flags & SMB2SRV_REQ_CTRL_FLAG_NOT_REPLY) {
+ talloc_free(req);
+ return;
+ }
+ smb2srv_negprot_send(req, io);
+}
+
+/*
+ * reply to a SMB negprot request with dialect "SMB 2.002"
+ */
+void smb2srv_reply_smb_negprot(struct smbsrv_request *smb_req)
+{
+ struct smb2srv_request *req;
+ uint32_t body_fixed_size = 0x26;
+
+ req = talloc_zero(smb_req->smb_conn, struct smb2srv_request);
+ if (!req) goto nomem;
+ req->smb_conn = smb_req->smb_conn;
+ req->request_time = smb_req->request_time;
+ talloc_steal(req, smb_req);
+
+ req->in.size = NBT_HDR_SIZE+SMB2_HDR_BODY+body_fixed_size;
+ req->in.allocated = req->in.size;
+ req->in.buffer = talloc_array(req, uint8_t, req->in.allocated);
+ if (!req->in.buffer) goto nomem;
+ req->in.hdr = req->in.buffer + NBT_HDR_SIZE;
+ req->in.body = req->in.hdr + SMB2_HDR_BODY;
+ req->in.body_size = body_fixed_size;
+ req->in.dynamic = NULL;
+
+ smb2srv_setup_bufinfo(req);
+
+ SIVAL(req->in.hdr, 0, SMB2_MAGIC);
+ SSVAL(req->in.hdr, SMB2_HDR_LENGTH, SMB2_HDR_BODY);
+ SSVAL(req->in.hdr, SMB2_HDR_EPOCH, 0);
+ SIVAL(req->in.hdr, SMB2_HDR_STATUS, 0);
+ SSVAL(req->in.hdr, SMB2_HDR_OPCODE, SMB2_OP_NEGPROT);
+ SSVAL(req->in.hdr, SMB2_HDR_CREDIT, 0);
+ SIVAL(req->in.hdr, SMB2_HDR_FLAGS, 0);
+ SIVAL(req->in.hdr, SMB2_HDR_NEXT_COMMAND, 0);
+ SBVAL(req->in.hdr, SMB2_HDR_MESSAGE_ID, 0);
+ SIVAL(req->in.hdr, SMB2_HDR_PID, 0);
+ SIVAL(req->in.hdr, SMB2_HDR_TID, 0);
+ SBVAL(req->in.hdr, SMB2_HDR_SESSION_ID, 0);
+ memset(req->in.hdr+SMB2_HDR_SIGNATURE, 0, 16);
+
+ /* this seems to be a bug, they use 0x24 but the length is 0x26 */
+ SSVAL(req->in.body, 0x00, 0x24);
+
+ SSVAL(req->in.body, 0x02, 1);
+ memset(req->in.body+0x04, 0, 32);
+ SSVAL(req->in.body, 0x24, 0);
+
+ smb2srv_negprot_recv(req);
+ return;
+nomem:
+ smbsrv_terminate_connection(smb_req->smb_conn, nt_errstr(NT_STATUS_NO_MEMORY));
+ talloc_free(req);
+ return;
+}
diff --git a/source4/smb_server/smb2/receive.c b/source4/smb_server/smb2/receive.c
new file mode 100644
index 0000000000..5a857e133f
--- /dev/null
+++ b/source4/smb_server/smb2/receive.c
@@ -0,0 +1,654 @@
+/*
+ Unix SMB2 implementation.
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/time.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "smb_server/smb_server.h"
+#include "smb_server/service_smb_proto.h"
+#include "smb_server/smb2/smb2_server.h"
+#include "smbd/service_stream.h"
+#include "lib/stream/packet.h"
+#include "ntvfs/ntvfs.h"
+#include "param/param.h"
+#include "auth/gensec/gensec.h"
+#include "auth/auth.h"
+
+
+/* fill in the bufinfo */
+void smb2srv_setup_bufinfo(struct smb2srv_request *req)
+{
+ req->in.bufinfo.mem_ctx = req;
+ req->in.bufinfo.flags = BUFINFO_FLAG_UNICODE | BUFINFO_FLAG_SMB2;
+ req->in.bufinfo.align_base = req->in.buffer;
+ if (req->in.dynamic) {
+ req->in.bufinfo.data = req->in.dynamic;
+ req->in.bufinfo.data_size = req->in.body_size - req->in.body_fixed;
+ } else {
+ req->in.bufinfo.data = NULL;
+ req->in.bufinfo.data_size = 0;
+ }
+}
+
+static int smb2srv_request_destructor(struct smb2srv_request *req)
+{
+ DLIST_REMOVE(req->smb_conn->requests2.list, req);
+ if (req->pending_id) {
+ idr_remove(req->smb_conn->requests2.idtree_req, req->pending_id);
+ }
+ return 0;
+}
+
+static int smb2srv_request_deny_destructor(struct smb2srv_request *req)
+{
+ return -1;
+}
+
+struct smb2srv_request *smb2srv_init_request(struct smbsrv_connection *smb_conn)
+{
+ struct smb2srv_request *req;
+
+ req = talloc_zero(smb_conn, struct smb2srv_request);
+ if (!req) return NULL;
+
+ req->smb_conn = smb_conn;
+
+ talloc_set_destructor(req, smb2srv_request_destructor);
+
+ return req;
+}
+
+NTSTATUS smb2srv_setup_reply(struct smb2srv_request *req, uint16_t body_fixed_size,
+ bool body_dynamic_present, uint32_t body_dynamic_size)
+{
+ uint32_t flags = SMB2_HDR_FLAG_REDIRECT;
+ uint32_t pid = IVAL(req->in.hdr, SMB2_HDR_PID);
+ uint32_t tid = IVAL(req->in.hdr, SMB2_HDR_TID);
+
+ if (req->pending_id) {
+ flags |= SMB2_HDR_FLAG_ASYNC;
+ pid = req->pending_id;
+ tid = 0;
+ }
+
+ if (body_dynamic_present) {
+ if (body_dynamic_size == 0) {
+ body_dynamic_size = 1;
+ }
+ } else {
+ body_dynamic_size = 0;
+ }
+
+ req->out.size = SMB2_HDR_BODY+NBT_HDR_SIZE+body_fixed_size;
+
+ req->out.allocated = req->out.size + body_dynamic_size;
+ req->out.buffer = talloc_array(req, uint8_t,
+ req->out.allocated);
+ NT_STATUS_HAVE_NO_MEMORY(req->out.buffer);
+
+ req->out.hdr = req->out.buffer + NBT_HDR_SIZE;
+ req->out.body = req->out.hdr + SMB2_HDR_BODY;
+ req->out.body_fixed = body_fixed_size;
+ req->out.body_size = body_fixed_size;
+ req->out.dynamic = (body_dynamic_size ? req->out.body + body_fixed_size : NULL);
+
+ SIVAL(req->out.hdr, 0, SMB2_MAGIC);
+ SSVAL(req->out.hdr, SMB2_HDR_LENGTH, SMB2_HDR_BODY);
+ SSVAL(req->out.hdr, SMB2_HDR_EPOCH, 0);
+ SIVAL(req->out.hdr, SMB2_HDR_STATUS, NT_STATUS_V(req->status));
+ SSVAL(req->out.hdr, SMB2_HDR_OPCODE, SVAL(req->in.hdr, SMB2_HDR_OPCODE));
+ SSVAL(req->out.hdr, SMB2_HDR_CREDIT, 0x0001);
+ SIVAL(req->out.hdr, SMB2_HDR_FLAGS, flags);
+ SIVAL(req->out.hdr, SMB2_HDR_NEXT_COMMAND, 0);
+ SBVAL(req->out.hdr, SMB2_HDR_MESSAGE_ID, req->seqnum);
+ SIVAL(req->out.hdr, SMB2_HDR_PID, pid);
+ SIVAL(req->out.hdr, SMB2_HDR_TID, tid);
+ SBVAL(req->out.hdr, SMB2_HDR_SESSION_ID, BVAL(req->in.hdr, SMB2_HDR_SESSION_ID));
+ memset(req->out.hdr+SMB2_HDR_SIGNATURE, 0, 16);
+
+ /* set the length of the fixed body part and +1 if there's a dynamic part also */
+ SSVAL(req->out.body, 0, body_fixed_size + (body_dynamic_size?1:0));
+
+ /*
+ * if we have a dynamic part, make sure the first byte
+ * which is always be part of the packet is initialized
+ */
+ if (body_dynamic_size) {
+ req->out.size += 1;
+ SCVAL(req->out.dynamic, 0, 0);
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS smb2srv_reply(struct smb2srv_request *req);
+
+static void smb2srv_chain_reply(struct smb2srv_request *p_req)
+{
+ NTSTATUS status;
+ struct smb2srv_request *req;
+ uint32_t chain_offset;
+ uint32_t protocol_version;
+ uint16_t buffer_code;
+ uint32_t dynamic_size;
+
+ chain_offset = p_req->chain_offset;
+ p_req->chain_offset = 0;
+
+ if (p_req->in.size < (NBT_HDR_SIZE + chain_offset + SMB2_MIN_SIZE_NO_BODY)) {
+ DEBUG(2,("Invalid SMB2 chained packet at offset 0x%X\n",
+ chain_offset));
+ smbsrv_terminate_connection(p_req->smb_conn, "Invalid SMB2 chained packet");
+ return;
+ }
+
+ protocol_version = IVAL(p_req->in.buffer, NBT_HDR_SIZE + chain_offset);
+ if (protocol_version != SMB2_MAGIC) {
+ DEBUG(2,("Invalid SMB chained packet: protocol prefix: 0x%08X\n",
+ protocol_version));
+ smbsrv_terminate_connection(p_req->smb_conn, "NON-SMB2 chained packet");
+ return;
+ }
+
+ req = smb2srv_init_request(p_req->smb_conn);
+ if (!req) {
+ smbsrv_terminate_connection(p_req->smb_conn, "SMB2 chained packet - no memory");
+ return;
+ }
+
+ req->in.buffer = talloc_steal(req, p_req->in.buffer);
+ req->in.size = p_req->in.size;
+ req->request_time = p_req->request_time;
+ req->in.allocated = req->in.size;
+
+ req->in.hdr = req->in.buffer+ NBT_HDR_SIZE + chain_offset;
+ req->in.body = req->in.hdr + SMB2_HDR_BODY;
+ req->in.body_size = req->in.size - (NBT_HDR_SIZE+ chain_offset + SMB2_HDR_BODY);
+ req->in.dynamic = NULL;
+
+ req->seqnum = BVAL(req->in.hdr, SMB2_HDR_MESSAGE_ID);
+
+ if (req->in.body_size < 2) {
+ /* error handling for this is different for negprot to
+ other packet types */
+ uint16_t opcode = SVAL(req->in.hdr, SMB2_HDR_OPCODE);
+ if (opcode == SMB2_OP_NEGPROT) {
+ smbsrv_terminate_connection(req->smb_conn, "Bad body size in SMB2 negprot");
+ } else {
+ smb2srv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ }
+ }
+
+ buffer_code = SVAL(req->in.body, 0);
+ req->in.body_fixed = (buffer_code & ~1);
+ dynamic_size = req->in.body_size - req->in.body_fixed;
+
+ if (dynamic_size != 0 && (buffer_code & 1)) {
+ req->in.dynamic = req->in.body + req->in.body_fixed;
+ if (smb2_oob(&req->in, req->in.dynamic, dynamic_size)) {
+ DEBUG(1,("SMB2 chained request invalid dynamic size 0x%x\n",
+ dynamic_size));
+ smb2srv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+ }
+
+ smb2srv_setup_bufinfo(req);
+
+ if (p_req->chained_file_handle) {
+ memcpy(req->_chained_file_handle,
+ p_req->_chained_file_handle,
+ sizeof(req->_chained_file_handle));
+ req->chained_file_handle = req->_chained_file_handle;
+ }
+
+ /*
+ * TODO: - make sure the length field is 64
+ * - make sure it's a request
+ */
+
+ status = smb2srv_reply(req);
+ if (!NT_STATUS_IS_OK(status)) {
+ smbsrv_terminate_connection(req->smb_conn, nt_errstr(status));
+ talloc_free(req);
+ return;
+ }
+}
+
+void smb2srv_send_reply(struct smb2srv_request *req)
+{
+ DATA_BLOB blob;
+ NTSTATUS status;
+
+ if (req->smb_conn->connection->event.fde == NULL) {
+ /* the socket has been destroyed - no point trying to send a reply! */
+ talloc_free(req);
+ return;
+ }
+
+ if (req->out.size > NBT_HDR_SIZE) {
+ _smb2_setlen(req->out.buffer, req->out.size - NBT_HDR_SIZE);
+ }
+
+ /* if signing is active on the session then sign the packet */
+ if (req->is_signed) {
+ status = smb2_sign_message(&req->out,
+ req->session->session_info->session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ smbsrv_terminate_connection(req->smb_conn, nt_errstr(status));
+ return;
+ }
+ }
+
+
+ blob = data_blob_const(req->out.buffer, req->out.size);
+ status = packet_send(req->smb_conn->packet, blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ smbsrv_terminate_connection(req->smb_conn, nt_errstr(status));
+ }
+ if (req->chain_offset) {
+ smb2srv_chain_reply(req);
+ return;
+ }
+ talloc_free(req);
+}
+
+void smb2srv_send_error(struct smb2srv_request *req, NTSTATUS error)
+{
+ NTSTATUS status;
+
+ if (req->smb_conn->connection->event.fde == NULL) {
+ /* the socket has been destroyed - no point trying to send an error! */
+ talloc_free(req);
+ return;
+ }
+
+ status = smb2srv_setup_reply(req, 8, true, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ smbsrv_terminate_connection(req->smb_conn, nt_errstr(status));
+ talloc_free(req);
+ return;
+ }
+
+ SIVAL(req->out.hdr, SMB2_HDR_STATUS, NT_STATUS_V(error));
+
+ SSVAL(req->out.body, 0x02, 0);
+ SIVAL(req->out.body, 0x04, 0);
+
+ smb2srv_send_reply(req);
+}
+
+static NTSTATUS smb2srv_reply(struct smb2srv_request *req)
+{
+ uint16_t opcode;
+ uint32_t tid;
+ uint64_t uid;
+ uint32_t flags;
+
+ if (SVAL(req->in.hdr, SMB2_HDR_LENGTH) != SMB2_HDR_BODY) {
+ smbsrv_terminate_connection(req->smb_conn, "Invalid SMB2 header length");
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ opcode = SVAL(req->in.hdr, SMB2_HDR_OPCODE);
+ req->chain_offset = IVAL(req->in.hdr, SMB2_HDR_NEXT_COMMAND);
+ req->seqnum = BVAL(req->in.hdr, SMB2_HDR_MESSAGE_ID);
+ tid = IVAL(req->in.hdr, SMB2_HDR_TID);
+ uid = BVAL(req->in.hdr, SMB2_HDR_SESSION_ID);
+ flags = IVAL(req->in.hdr, SMB2_HDR_FLAGS);
+
+ if (req->smb_conn->highest_smb2_seqnum != 0 &&
+ req->seqnum <= req->smb_conn->highest_smb2_seqnum) {
+ smbsrv_terminate_connection(req->smb_conn, "Invalid SMB2 sequence number");
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ req->smb_conn->highest_smb2_seqnum = req->seqnum;
+
+ req->session = smbsrv_session_find(req->smb_conn, uid, req->request_time);
+ req->tcon = smbsrv_smb2_tcon_find(req->session, tid, req->request_time);
+
+ errno = 0;
+
+ /* supporting signing is mandatory in SMB2, and is per-packet. So we
+ should check the signature on any incoming packet that is signed, and
+ should give a signed reply to any signed request */
+ if (flags & SMB2_HDR_FLAG_SIGNED) {
+ NTSTATUS status;
+
+ if (!req->session) goto nosession;
+
+ req->is_signed = true;
+ status = smb2_check_signature(&req->in,
+ req->session->session_info->session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ smb2srv_send_error(req, status);
+ return NT_STATUS_OK;
+ }
+ } else if (req->session && req->session->smb2_signing.active) {
+ /* we require signing and this request was not signed */
+ smb2srv_send_error(req, NT_STATUS_ACCESS_DENIED);
+ return NT_STATUS_OK;
+ }
+
+ /* TODO: check the seqnum */
+
+ switch (opcode) {
+ case SMB2_OP_NEGPROT:
+ smb2srv_negprot_recv(req);
+ return NT_STATUS_OK;
+ case SMB2_OP_SESSSETUP:
+ smb2srv_sesssetup_recv(req);
+ return NT_STATUS_OK;
+ case SMB2_OP_LOGOFF:
+ if (!req->session) goto nosession;
+ smb2srv_logoff_recv(req);
+ return NT_STATUS_OK;
+ case SMB2_OP_TCON:
+ if (!req->session) goto nosession;
+ smb2srv_tcon_recv(req);
+ return NT_STATUS_OK;
+ case SMB2_OP_TDIS:
+ if (!req->session) goto nosession;
+ if (!req->tcon) goto notcon;
+ smb2srv_tdis_recv(req);
+ return NT_STATUS_OK;
+ case SMB2_OP_CREATE:
+ if (!req->session) goto nosession;
+ if (!req->tcon) goto notcon;
+ smb2srv_create_recv(req);
+ return NT_STATUS_OK;
+ case SMB2_OP_CLOSE:
+ if (!req->session) goto nosession;
+ if (!req->tcon) goto notcon;
+ smb2srv_close_recv(req);
+ return NT_STATUS_OK;
+ case SMB2_OP_FLUSH:
+ if (!req->session) goto nosession;
+ if (!req->tcon) goto notcon;
+ smb2srv_flush_recv(req);
+ return NT_STATUS_OK;
+ case SMB2_OP_READ:
+ if (!req->session) goto nosession;
+ if (!req->tcon) goto notcon;
+ smb2srv_read_recv(req);
+ return NT_STATUS_OK;
+ case SMB2_OP_WRITE:
+ if (!req->session) goto nosession;
+ if (!req->tcon) goto notcon;
+ smb2srv_write_recv(req);
+ return NT_STATUS_OK;
+ case SMB2_OP_LOCK:
+ if (!req->session) goto nosession;
+ if (!req->tcon) goto notcon;
+ smb2srv_lock_recv(req);
+ return NT_STATUS_OK;
+ case SMB2_OP_IOCTL:
+ if (!req->session) goto nosession;
+ if (!req->tcon) goto notcon;
+ smb2srv_ioctl_recv(req);
+ return NT_STATUS_OK;
+ case SMB2_OP_CANCEL:
+ smb2srv_cancel_recv(req);
+ return NT_STATUS_OK;
+ case SMB2_OP_KEEPALIVE:
+ smb2srv_keepalive_recv(req);
+ return NT_STATUS_OK;
+ case SMB2_OP_FIND:
+ if (!req->session) goto nosession;
+ if (!req->tcon) goto notcon;
+ smb2srv_find_recv(req);
+ return NT_STATUS_OK;
+ case SMB2_OP_NOTIFY:
+ if (!req->session) goto nosession;
+ if (!req->tcon) goto notcon;
+ smb2srv_notify_recv(req);
+ return NT_STATUS_OK;
+ case SMB2_OP_GETINFO:
+ if (!req->session) goto nosession;
+ if (!req->tcon) goto notcon;
+ smb2srv_getinfo_recv(req);
+ return NT_STATUS_OK;
+ case SMB2_OP_SETINFO:
+ if (!req->session) goto nosession;
+ if (!req->tcon) goto notcon;
+ smb2srv_setinfo_recv(req);
+ return NT_STATUS_OK;
+ case SMB2_OP_BREAK:
+ if (!req->session) goto nosession;
+ if (!req->tcon) goto notcon;
+ smb2srv_break_recv(req);
+ return NT_STATUS_OK;
+ }
+
+ DEBUG(1,("Invalid SMB2 opcode: 0x%04X\n", opcode));
+ smbsrv_terminate_connection(req->smb_conn, "Invalid SMB2 opcode");
+ return NT_STATUS_OK;
+
+nosession:
+ smb2srv_send_error(req, NT_STATUS_USER_SESSION_DELETED);
+ return NT_STATUS_OK;
+notcon:
+ smb2srv_send_error(req, NT_STATUS_NETWORK_NAME_DELETED);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS smbsrv_recv_smb2_request(void *private_data, DATA_BLOB blob)
+{
+ struct smbsrv_connection *smb_conn = talloc_get_type(private_data, struct smbsrv_connection);
+ struct smb2srv_request *req;
+ struct timeval cur_time = timeval_current();
+ uint32_t protocol_version;
+ uint16_t buffer_code;
+ uint32_t dynamic_size;
+
+ smb_conn->statistics.last_request_time = cur_time;
+
+ /* see if its a special NBT packet */
+ if (CVAL(blob.data,0) != 0) {
+ DEBUG(2,("Special NBT packet on SMB2 connection"));
+ smbsrv_terminate_connection(smb_conn, "Special NBT packet on SMB2 connection");
+ return NT_STATUS_OK;
+ }
+
+ if (blob.length < (NBT_HDR_SIZE + SMB2_MIN_SIZE_NO_BODY)) {
+ DEBUG(2,("Invalid SMB2 packet length count %ld\n", (long)blob.length));
+ smbsrv_terminate_connection(smb_conn, "Invalid SMB2 packet");
+ return NT_STATUS_OK;
+ }
+
+ protocol_version = IVAL(blob.data, NBT_HDR_SIZE);
+ if (protocol_version != SMB2_MAGIC) {
+ DEBUG(2,("Invalid SMB packet: protocol prefix: 0x%08X\n",
+ protocol_version));
+ smbsrv_terminate_connection(smb_conn, "NON-SMB2 packet");
+ return NT_STATUS_OK;
+ }
+
+ req = smb2srv_init_request(smb_conn);
+ NT_STATUS_HAVE_NO_MEMORY(req);
+
+ req->in.buffer = talloc_steal(req, blob.data);
+ req->in.size = blob.length;
+ req->request_time = cur_time;
+ req->in.allocated = req->in.size;
+
+ req->in.hdr = req->in.buffer+ NBT_HDR_SIZE;
+ req->in.body = req->in.hdr + SMB2_HDR_BODY;
+ req->in.body_size = req->in.size - (SMB2_HDR_BODY+NBT_HDR_SIZE);
+ req->in.dynamic = NULL;
+
+ req->seqnum = BVAL(req->in.hdr, SMB2_HDR_MESSAGE_ID);
+
+ if (req->in.body_size < 2) {
+ /* error handling for this is different for negprot to
+ other packet types */
+ uint16_t opcode = SVAL(req->in.hdr, SMB2_HDR_OPCODE);
+ if (opcode == SMB2_OP_NEGPROT) {
+ smbsrv_terminate_connection(req->smb_conn, "Bad body size in SMB2 negprot");
+ } else {
+ smb2srv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ }
+ }
+
+ buffer_code = SVAL(req->in.body, 0);
+ req->in.body_fixed = (buffer_code & ~1);
+ dynamic_size = req->in.body_size - req->in.body_fixed;
+
+ if (dynamic_size != 0 && (buffer_code & 1)) {
+ req->in.dynamic = req->in.body + req->in.body_fixed;
+ if (smb2_oob(&req->in, req->in.dynamic, dynamic_size)) {
+ DEBUG(1,("SMB2 request invalid dynamic size 0x%x\n",
+ dynamic_size));
+ smb2srv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+ return NT_STATUS_OK;
+ }
+ }
+
+ smb2srv_setup_bufinfo(req);
+
+ /*
+ * TODO: - make sure the length field is 64
+ * - make sure it's a request
+ */
+
+ return smb2srv_reply(req);
+}
+
+static NTSTATUS smb2srv_init_pending(struct smbsrv_connection *smb_conn)
+{
+ smb_conn->requests2.idtree_req = idr_init(smb_conn);
+ NT_STATUS_HAVE_NO_MEMORY(smb_conn->requests2.idtree_req);
+ smb_conn->requests2.idtree_limit = 0x00FFFFFF & (UINT32_MAX - 1);
+ smb_conn->requests2.list = NULL;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS smb2srv_queue_pending(struct smb2srv_request *req)
+{
+ NTSTATUS status;
+ bool signing_used = false;
+ int id;
+
+ if (req->pending_id) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ id = idr_get_new_above(req->smb_conn->requests2.idtree_req, req,
+ 1, req->smb_conn->requests2.idtree_limit);
+ if (id == -1) {
+ return NT_STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ DLIST_ADD_END(req->smb_conn->requests2.list, req, struct smb2srv_request *);
+ req->pending_id = id;
+
+ if (req->smb_conn->connection->event.fde == NULL) {
+ /* the socket has been destroyed - no point trying to send an error! */
+ return NT_STATUS_REMOTE_DISCONNECT;
+ }
+
+ talloc_set_destructor(req, smb2srv_request_deny_destructor);
+
+ status = smb2srv_setup_reply(req, 8, true, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ SIVAL(req->out.hdr, SMB2_HDR_STATUS, NT_STATUS_V(STATUS_PENDING));
+
+ SSVAL(req->out.body, 0x02, 0);
+ SIVAL(req->out.body, 0x04, 0);
+
+ /* if the real reply will be signed set the signed flags, but don't sign */
+ if (req->is_signed) {
+ SIVAL(req->out.hdr, SMB2_HDR_FLAGS, IVAL(req->out.hdr, SMB2_HDR_FLAGS) | SMB2_HDR_FLAG_SIGNED);
+ signing_used = req->is_signed;
+ req->is_signed = false;
+ }
+
+ smb2srv_send_reply(req);
+
+ req->is_signed = signing_used;
+
+ talloc_set_destructor(req, smb2srv_request_destructor);
+ return NT_STATUS_OK;
+}
+
+void smb2srv_cancel_recv(struct smb2srv_request *req)
+{
+ uint32_t pending_id;
+ uint32_t flags;
+ void *p;
+ struct smb2srv_request *r;
+
+ if (!req->session) goto done;
+
+ flags = IVAL(req->in.hdr, SMB2_HDR_FLAGS);
+ pending_id = IVAL(req->in.hdr, SMB2_HDR_PID);
+
+ if (!(flags & SMB2_HDR_FLAG_ASYNC)) {
+ /* TODO: what to do here? */
+ goto done;
+ }
+
+ p = idr_find(req->smb_conn->requests2.idtree_req, pending_id);
+ if (!p) goto done;
+
+ r = talloc_get_type(p, struct smb2srv_request);
+ if (!r) goto done;
+
+ if (!r->ntvfs) goto done;
+
+ ntvfs_cancel(r->ntvfs);
+
+done:
+ /* we never generate a reply for a SMB2 Cancel */
+ talloc_free(req);
+}
+
+/*
+ * init the SMB2 protocol related stuff
+ */
+NTSTATUS smbsrv_init_smb2_connection(struct smbsrv_connection *smb_conn)
+{
+ NTSTATUS status;
+
+ /* now initialise a few default values associated with this smb socket */
+ smb_conn->negotiate.max_send = 0xFFFF;
+
+ /* this is the size that w2k uses, and it appears to be important for
+ good performance */
+ smb_conn->negotiate.max_recv = lp_max_xmit(smb_conn->lp_ctx);
+
+ smb_conn->negotiate.zone_offset = get_time_zone(time(NULL));
+
+ smb_conn->config.security = SEC_USER;
+ smb_conn->config.nt_status_support = true;
+
+ status = smbsrv_init_sessions(smb_conn, UINT64_MAX);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ status = smb2srv_init_pending(smb_conn);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ return NT_STATUS_OK;
+
+}
diff --git a/source4/smb_server/smb2/sesssetup.c b/source4/smb_server/smb2/sesssetup.c
new file mode 100644
index 0000000000..9a8c1bfaa9
--- /dev/null
+++ b/source4/smb_server/smb2/sesssetup.c
@@ -0,0 +1,276 @@
+/*
+ Unix SMB2 implementation.
+
+ Copyright (C) Andrew Bartlett 2001-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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "auth/credentials/credentials.h"
+#include "auth/gensec/gensec.h"
+#include "auth/auth.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "smb_server/smb_server.h"
+#include "smb_server/service_smb_proto.h"
+#include "smb_server/smb2/smb2_server.h"
+#include "smbd/service_stream.h"
+#include "param/param.h"
+
+static void smb2srv_sesssetup_send(struct smb2srv_request *req, union smb_sesssetup *io)
+{
+ uint16_t credit;
+
+ if (NT_STATUS_IS_OK(req->status)) {
+ credit = 0x0003;
+ } else if (NT_STATUS_EQUAL(req->status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ credit = 0x0002;
+ } else {
+ smb2srv_send_error(req, req->status);
+ return;
+ }
+
+ SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x08, true, io->smb2.out.secblob.length));
+
+ SSVAL(req->out.hdr, SMB2_HDR_CREDIT, credit);
+ SBVAL(req->out.hdr, SMB2_HDR_SESSION_ID, io->smb2.out.uid);
+
+ SSVAL(req->out.body, 0x02, io->smb2.out.session_flags);
+ SMB2SRV_CHECK(smb2_push_o16s16_blob(&req->out, 0x04, io->smb2.out.secblob));
+
+ smb2srv_send_reply(req);
+}
+
+struct smb2srv_sesssetup_callback_ctx {
+ struct smb2srv_request *req;
+ union smb_sesssetup *io;
+ struct smbsrv_session *smb_sess;
+};
+
+static void smb2srv_sesssetup_callback(struct gensec_update_request *greq, void *private_data)
+{
+ struct smb2srv_sesssetup_callback_ctx *ctx = talloc_get_type(private_data,
+ struct smb2srv_sesssetup_callback_ctx);
+ struct smb2srv_request *req = ctx->req;
+ union smb_sesssetup *io = ctx->io;
+ struct smbsrv_session *smb_sess = ctx->smb_sess;
+ struct auth_session_info *session_info = NULL;
+ NTSTATUS status;
+
+ status = gensec_update_recv(greq, req, &io->smb2.out.secblob);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ goto done;
+ } else if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+
+ status = gensec_session_info(smb_sess->gensec_ctx, &session_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+
+ /* Ensure this is marked as a 'real' vuid, not one
+ * simply valid for the session setup leg */
+ status = smbsrv_session_sesssetup_finished(smb_sess, session_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+ req->session = smb_sess;
+
+ if (smb_sess->smb2_signing.required) {
+ /* activate smb2 signing on the session */
+ smb_sess->smb2_signing.active = true;
+ }
+done:
+ io->smb2.out.uid = smb_sess->vuid;
+failed:
+ req->status = auth_nt_status_squash(status);
+ smb2srv_sesssetup_send(req, io);
+ if (!NT_STATUS_IS_OK(status) && !
+ NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ talloc_free(smb_sess);
+ }
+}
+
+static void smb2srv_sesssetup_backend(struct smb2srv_request *req, union smb_sesssetup *io)
+{
+ NTSTATUS status;
+ struct smb2srv_sesssetup_callback_ctx *callback_ctx;
+ struct smbsrv_session *smb_sess = NULL;
+ uint64_t vuid;
+
+ io->smb2.out.session_flags = 0;
+ io->smb2.out.uid = 0;
+ io->smb2.out.secblob = data_blob(NULL, 0);
+
+ vuid = BVAL(req->in.hdr, SMB2_HDR_SESSION_ID);
+
+ /*
+ * only when we got '0' we should allocate a new session
+ */
+ if (vuid == 0) {
+ struct gensec_security *gensec_ctx;
+
+ status = samba_server_gensec_start(req,
+ req->smb_conn->connection->event.ctx,
+ req->smb_conn->connection->msg_ctx,
+ req->smb_conn->lp_ctx,
+ req->smb_conn->negotiate.server_credentials,
+ "cifs",
+ &gensec_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to start GENSEC server code: %s\n", nt_errstr(status)));
+ goto failed;
+ }
+
+ gensec_want_feature(gensec_ctx, GENSEC_FEATURE_SESSION_KEY);
+
+ status = gensec_start_mech_by_oid(gensec_ctx, GENSEC_OID_SPNEGO);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to start GENSEC SPNEGO server code: %s\n", nt_errstr(status)));
+ goto failed;
+ }
+
+ /* allocate a new session */
+ smb_sess = smbsrv_session_new(req->smb_conn, req->smb_conn, gensec_ctx);
+ if (!smb_sess) {
+ status = NT_STATUS_INSUFFICIENT_RESOURCES;
+ goto failed;
+ }
+ status = smbsrv_smb2_init_tcons(smb_sess);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+ } else {
+ /* lookup an existing session */
+ smb_sess = smbsrv_session_find_sesssetup(req->smb_conn, vuid);
+ }
+
+ if (!smb_sess) {
+ /* see WSPP test suite - test 11 */
+ status = NT_STATUS_REQUEST_NOT_ACCEPTED;
+ goto failed;
+ }
+
+ if (!smb_sess->gensec_ctx) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ DEBUG(1, ("Internal ERROR: no gensec_ctx on session: %s\n", nt_errstr(status)));
+ goto failed;
+ }
+
+ callback_ctx = talloc(req, struct smb2srv_sesssetup_callback_ctx);
+ if (!callback_ctx) goto nomem;
+ callback_ctx->req = req;
+ callback_ctx->io = io;
+ callback_ctx->smb_sess = smb_sess;
+
+ gensec_update_send(smb_sess->gensec_ctx, io->smb2.in.secblob,
+ smb2srv_sesssetup_callback, callback_ctx);
+
+ /* note that we ignore SMB2_NEGOTIATE_SIGNING_ENABLED from the client.
+ This is deliberate as windows does not set it even when it does
+ set SMB2_NEGOTIATE_SIGNING_REQUIRED */
+ if (io->smb2.in.security_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED) {
+ smb_sess->smb2_signing.required = true;
+ } else if (req->smb_conn->smb2_signing_required) {
+ /*
+ * if required signing was negotiates in SMB2 Negotiate
+ * then the client made an error not using it here
+ */
+ DEBUG(1, ("SMB2 signing required on the connection but not used on session\n"));
+ req->status = NT_STATUS_FOOBAR;
+ goto failed;
+ }
+
+ return;
+nomem:
+ status = NT_STATUS_NO_MEMORY;
+failed:
+ talloc_free(smb_sess);
+ req->status = auth_nt_status_squash(status);
+ smb2srv_sesssetup_send(req, io);
+}
+
+void smb2srv_sesssetup_recv(struct smb2srv_request *req)
+{
+ union smb_sesssetup *io;
+
+ SMB2SRV_CHECK_BODY_SIZE(req, 0x18, true);
+ SMB2SRV_TALLOC_IO_PTR(io, union smb_sesssetup);
+
+ io->smb2.level = RAW_SESSSETUP_SMB2;
+ io->smb2.in.vc_number = CVAL(req->in.body, 0x02);
+ io->smb2.in.security_mode = CVAL(req->in.body, 0x03);
+ io->smb2.in.capabilities = IVAL(req->in.body, 0x04);
+ io->smb2.in.channel = IVAL(req->in.body, 0x08);
+ io->smb2.in.previous_sessionid = BVAL(req->in.body, 0x10);
+ SMB2SRV_CHECK(smb2_pull_o16s16_blob(&req->in, io, req->in.body+0x0C, &io->smb2.in.secblob));
+
+ smb2srv_sesssetup_backend(req, io);
+}
+
+static int smb2srv_cleanup_session_destructor(struct smbsrv_session **session)
+{
+ /* TODO: call ntvfs backends to close file of this session */
+ DEBUG(0,("free session[%p]\n", *session));
+ talloc_free(*session);
+ return 0;
+}
+
+static NTSTATUS smb2srv_logoff_backend(struct smb2srv_request *req)
+{
+ struct smbsrv_session **session_ptr;
+
+ /* we need to destroy the session after sending the reply */
+ session_ptr = talloc(req, struct smbsrv_session *);
+ NT_STATUS_HAVE_NO_MEMORY(session_ptr);
+
+ *session_ptr = req->session;
+ talloc_set_destructor(session_ptr, smb2srv_cleanup_session_destructor);
+
+ return NT_STATUS_OK;
+}
+
+static void smb2srv_logoff_send(struct smb2srv_request *req)
+{
+ if (NT_STATUS_IS_ERR(req->status)) {
+ smb2srv_send_error(req, req->status);
+ return;
+ }
+
+ SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x04, false, 0));
+
+ SSVAL(req->out.body, 0x02, 0);
+
+ smb2srv_send_reply(req);
+}
+
+void smb2srv_logoff_recv(struct smb2srv_request *req)
+{
+ uint16_t _pad;
+
+ SMB2SRV_CHECK_BODY_SIZE(req, 0x04, false);
+
+ _pad = SVAL(req->in.body, 0x02);
+
+ req->status = smb2srv_logoff_backend(req);
+
+ if (req->control_flags & SMB2SRV_REQ_CTRL_FLAG_NOT_REPLY) {
+ talloc_free(req);
+ return;
+ }
+ smb2srv_logoff_send(req);
+}
diff --git a/source4/smb_server/smb2/smb2_server.h b/source4/smb_server/smb2/smb2_server.h
new file mode 100644
index 0000000000..ba3021a3a9
--- /dev/null
+++ b/source4/smb_server/smb2/smb2_server.h
@@ -0,0 +1,187 @@
+/*
+ Unix SMB2 implementation.
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+/* the context for a single SMB2 request. This is passed to any request-context
+ functions */
+struct smb2srv_request {
+ /* the smbsrv_connection needs a list of requests queued for send */
+ struct smb2srv_request *next, *prev;
+
+ /* the server_context contains all context specific to this SMB socket */
+ struct smbsrv_connection *smb_conn;
+
+ /* conn is only set for operations that have a valid TID */
+ struct smbsrv_tcon *tcon;
+
+ /* the session context is derived from the vuid */
+ struct smbsrv_session *session;
+
+#define SMB2SRV_REQ_CTRL_FLAG_NOT_REPLY (1<<0)
+ uint32_t control_flags;
+
+ /* the system time when the request arrived */
+ struct timeval request_time;
+
+ /* a pointer to the per request union smb_* io structure */
+ void *io_ptr;
+
+ /* the ntvfs_request */
+ struct ntvfs_request *ntvfs;
+
+ /* Now the SMB2 specific stuff */
+
+ /* the status the backend returned */
+ NTSTATUS status;
+
+ /* for matching request and reply */
+ uint64_t seqnum;
+
+ /* the id that can be used to cancel the request */
+ uint32_t pending_id;
+
+ /* the offset to the next SMB2 Header for chained requests */
+ uint32_t chain_offset;
+
+ /* chained file handle */
+ uint8_t _chained_file_handle[16];
+ uint8_t *chained_file_handle;
+
+ bool is_signed;
+
+ struct smb2_request_buffer in;
+ struct smb2_request_buffer out;
+};
+
+struct smbsrv_request;
+
+#include "smb_server/smb2/smb2_proto.h"
+
+/* useful way of catching field size errors with file and line number */
+#define SMB2SRV_CHECK_BODY_SIZE(req, size, dynamic) do { \
+ size_t is_size = req->in.body_size; \
+ uint16_t field_size; \
+ uint16_t want_size = ((dynamic)?(size)+1:(size)); \
+ if (is_size < (size)) { \
+ DEBUG(0,("%s: buffer too small 0x%x. Expected 0x%x\n", \
+ __location__, (unsigned)is_size, (unsigned)want_size)); \
+ smb2srv_send_error(req, NT_STATUS_INVALID_PARAMETER); \
+ return; \
+ }\
+ field_size = SVAL(req->in.body, 0); \
+ if (field_size != want_size) { \
+ DEBUG(0,("%s: unexpected fixed body size 0x%x. Expected 0x%x\n", \
+ __location__, (unsigned)field_size, (unsigned)want_size)); \
+ smb2srv_send_error(req, NT_STATUS_INVALID_PARAMETER); \
+ return; \
+ } \
+} while (0)
+
+#define SMB2SRV_CHECK(cmd) do {\
+ NTSTATUS _status; \
+ _status = cmd; \
+ if (!NT_STATUS_IS_OK(_status)) { \
+ smb2srv_send_error(req, _status); \
+ return; \
+ } \
+} while (0)
+
+/* useful wrapper for talloc with NO_MEMORY reply */
+#define SMB2SRV_TALLOC_IO_PTR(ptr, type) do { \
+ ptr = talloc(req, type); \
+ if (!ptr) { \
+ smb2srv_send_error(req, NT_STATUS_NO_MEMORY); \
+ return; \
+ } \
+ req->io_ptr = ptr; \
+} while (0)
+
+#define SMB2SRV_SETUP_NTVFS_REQUEST(send_fn, state) do { \
+ req->ntvfs = ntvfs_request_create(req->tcon->ntvfs, req, \
+ req->session->session_info,\
+ 0, \
+ req->request_time, \
+ req, send_fn, state); \
+ if (!req->ntvfs) { \
+ smb2srv_send_error(req, NT_STATUS_NO_MEMORY); \
+ return; \
+ } \
+ (void)talloc_steal(req->tcon->ntvfs, req); \
+ req->ntvfs->frontend_data.private_data = req; \
+} while (0)
+
+#define SMB2SRV_CHECK_FILE_HANDLE(handle) do { \
+ if (!handle) { \
+ smb2srv_send_error(req, NT_STATUS_FILE_CLOSED); \
+ return; \
+ } \
+} while (0)
+
+/*
+ check if the backend wants to handle the request asynchronously.
+ if it wants it handled synchronously then call the send function
+ immediately
+*/
+#define SMB2SRV_CALL_NTVFS_BACKEND(cmd) do { \
+ req->ntvfs->async_states->status = cmd; \
+ if (req->ntvfs->async_states->state & NTVFS_ASYNC_STATE_ASYNC) { \
+ NTSTATUS _status; \
+ _status = smb2srv_queue_pending(req); \
+ if (!NT_STATUS_IS_OK(_status)) { \
+ ntvfs_cancel(req->ntvfs); \
+ } \
+ } else { \
+ req->ntvfs->async_states->send_fn(req->ntvfs); \
+ } \
+} while (0)
+
+/* check req->ntvfs->async_states->status and if not OK then send an error reply */
+#define SMB2SRV_CHECK_ASYNC_STATUS_ERR_SIMPLE do { \
+ req = talloc_get_type(ntvfs->async_states->private_data, struct smb2srv_request); \
+ if (ntvfs->async_states->state & NTVFS_ASYNC_STATE_CLOSE || NT_STATUS_EQUAL(ntvfs->async_states->status, NT_STATUS_NET_WRITE_FAULT)) { \
+ smbsrv_terminate_connection(req->smb_conn, get_friendly_nt_error_msg (ntvfs->async_states->status)); \
+ talloc_free(req); \
+ return; \
+ } \
+ req->status = ntvfs->async_states->status; \
+ if (NT_STATUS_IS_ERR(ntvfs->async_states->status)) { \
+ smb2srv_send_error(req, ntvfs->async_states->status); \
+ return; \
+ } \
+} while (0)
+#define SMB2SRV_CHECK_ASYNC_STATUS_ERR(ptr, type) do { \
+ SMB2SRV_CHECK_ASYNC_STATUS_ERR_SIMPLE; \
+ ptr = talloc_get_type(req->io_ptr, type); \
+} while (0)
+#define SMB2SRV_CHECK_ASYNC_STATUS_SIMPLE do { \
+ req = talloc_get_type(ntvfs->async_states->private_data, struct smb2srv_request); \
+ if (ntvfs->async_states->state & NTVFS_ASYNC_STATE_CLOSE || NT_STATUS_EQUAL(ntvfs->async_states->status, NT_STATUS_NET_WRITE_FAULT)) { \
+ smbsrv_terminate_connection(req->smb_conn, get_friendly_nt_error_msg (ntvfs->async_states->status)); \
+ talloc_free(req); \
+ return; \
+ } \
+ req->status = ntvfs->async_states->status; \
+ if (!NT_STATUS_IS_OK(ntvfs->async_states->status)) { \
+ smb2srv_send_error(req, ntvfs->async_states->status); \
+ return; \
+ } \
+} while (0)
+#define SMB2SRV_CHECK_ASYNC_STATUS(ptr, type) do { \
+ SMB2SRV_CHECK_ASYNC_STATUS_SIMPLE; \
+ ptr = talloc_get_type(req->io_ptr, type); \
+} while (0)
diff --git a/source4/smb_server/smb2/tcon.c b/source4/smb_server/smb2/tcon.c
new file mode 100644
index 0000000000..be64013bb2
--- /dev/null
+++ b/source4/smb_server/smb2/tcon.c
@@ -0,0 +1,459 @@
+/*
+ Unix SMB2 implementation.
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "smb_server/smb_server.h"
+#include "smb_server/service_smb_proto.h"
+#include "smb_server/smb2/smb2_server.h"
+#include "librpc/gen_ndr/security.h"
+#include "smbd/service_stream.h"
+#include "ntvfs/ntvfs.h"
+#include "param/param.h"
+
+/*
+ send an oplock break request to a client
+*/
+static NTSTATUS smb2srv_send_oplock_break(void *p, struct ntvfs_handle *h, uint8_t level)
+{
+ struct smbsrv_handle *handle = talloc_get_type(h->frontend_data.private_data,
+ struct smbsrv_handle);
+ struct smb2srv_request *req;
+ NTSTATUS status;
+
+ /* setup a dummy request structure */
+ req = smb2srv_init_request(handle->tcon->smb_conn);
+ NT_STATUS_HAVE_NO_MEMORY(req);
+
+ req->in.buffer = talloc_array(req, uint8_t,
+ NBT_HDR_SIZE + SMB2_MIN_SIZE);
+ NT_STATUS_HAVE_NO_MEMORY(req->in.buffer);
+ req->in.size = NBT_HDR_SIZE + SMB2_MIN_SIZE;
+ req->in.allocated = req->in.size;
+
+ req->in.hdr = req->in.buffer+ NBT_HDR_SIZE;
+ req->in.body = req->in.hdr + SMB2_HDR_BODY;
+ req->in.body_size = req->in.size - (SMB2_HDR_BODY+NBT_HDR_SIZE);
+ req->in.dynamic = NULL;
+
+ req->seqnum = UINT64_MAX;
+
+ smb2srv_setup_bufinfo(req);
+
+ SIVAL(req->in.hdr, 0, SMB2_MAGIC);
+ SSVAL(req->in.hdr, SMB2_HDR_LENGTH, SMB2_HDR_BODY);
+ SSVAL(req->in.hdr, SMB2_HDR_EPOCH, 0);
+ SIVAL(req->in.hdr, SMB2_HDR_STATUS, 0);
+ SSVAL(req->in.hdr, SMB2_HDR_OPCODE, SMB2_OP_BREAK);
+ SSVAL(req->in.hdr, SMB2_HDR_CREDIT, 0);
+ SIVAL(req->in.hdr, SMB2_HDR_FLAGS, 0);
+ SIVAL(req->in.hdr, SMB2_HDR_NEXT_COMMAND, 0);
+ SBVAL(req->in.hdr, SMB2_HDR_MESSAGE_ID, 0);
+ SIVAL(req->in.hdr, SMB2_HDR_PID, 0);
+ SIVAL(req->in.hdr, SMB2_HDR_TID, 0);
+ SBVAL(req->in.hdr, SMB2_HDR_SESSION_ID, 0);
+ memset(req->in.hdr+SMB2_HDR_SIGNATURE, 0, 16);
+
+ SSVAL(req->in.body, 0, 2);
+
+ status = smb2srv_setup_reply(req, 0x18, false, 0);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ SSVAL(req->out.hdr, SMB2_HDR_CREDIT, 0x0000);
+
+ SSVAL(req->out.body, 0x02, 0x0001);
+ SIVAL(req->out.body, 0x04, 0x00000000);
+ smb2srv_push_handle(req->out.body, 0x08, h);
+
+ smb2srv_send_reply(req);
+
+ return NT_STATUS_OK;
+}
+
+struct ntvfs_handle *smb2srv_pull_handle(struct smb2srv_request *req, const uint8_t *base, uint_t offset)
+{
+ struct smbsrv_tcon *tcon;
+ struct smbsrv_handle *handle;
+ uint32_t hid;
+ uint32_t tid;
+ uint64_t uid;
+
+ /*
+ * if there're chained requests used the cached handle
+ *
+ * TODO: check if this also correct when the given handle
+ * isn't all 0xFF.
+ */
+ if (req->chained_file_handle) {
+ base = req->chained_file_handle;
+ offset = 0;
+ }
+
+ hid = IVAL(base, offset);
+ tid = IVAL(base, offset + 4);
+ uid = BVAL(base, offset + 8);
+
+ /* if it's the wildcard handle, don't waste time to search it... */
+ if (hid == UINT32_MAX && tid == UINT32_MAX && uid == UINT64_MAX) {
+ return NULL;
+ }
+
+ /*
+ * if the (v)uid part doesn't match the given session the handle isn't
+ * valid
+ */
+ if (uid != req->session->vuid) {
+ return NULL;
+ }
+
+ /*
+ * the handle can belong to a different tcon
+ * as that TID in the SMB2 header says, but
+ * the request should succeed nevertheless!
+ *
+ * because of this we put the 32 bit TID into the
+ * 128 bit handle, so that we can extract the tcon from the
+ * handle
+ */
+ tcon = req->tcon;
+ if (tid != req->tcon->tid) {
+ tcon = smbsrv_smb2_tcon_find(req->session, tid, req->request_time);
+ if (!tcon) {
+ return NULL;
+ }
+ }
+
+ handle = smbsrv_smb2_handle_find(tcon, hid, req->request_time);
+ if (!handle) {
+ return NULL;
+ }
+
+ /*
+ * as the smb2srv_tcon is a child object of the smb2srv_session
+ * the handle belongs to the correct session!
+ *
+ * Note: no check is needed here for SMB2
+ */
+
+ /*
+ * as the handle may have overwritten the tcon
+ * we need to set it on the request so that the
+ * correct ntvfs context will be used for the ntvfs_*() request
+ *
+ * TODO: check if that's correct for chained requests as well!
+ */
+ req->tcon = tcon;
+ return handle->ntvfs;
+}
+
+void smb2srv_push_handle(uint8_t *base, uint_t offset, struct ntvfs_handle *ntvfs)
+{
+ struct smbsrv_handle *handle = talloc_get_type(ntvfs->frontend_data.private_data,
+ struct smbsrv_handle);
+
+ /*
+ * the handle is 128 bit on the wire
+ */
+ SIVAL(base, offset, handle->hid);
+ SIVAL(base, offset + 4, handle->tcon->tid);
+ SBVAL(base, offset + 8, handle->session->vuid);
+}
+
+static NTSTATUS smb2srv_handle_create_new(void *private_data, struct ntvfs_request *ntvfs, struct ntvfs_handle **_h)
+{
+ struct smb2srv_request *req = talloc_get_type(ntvfs->frontend_data.private_data,
+ struct smb2srv_request);
+ struct smbsrv_handle *handle;
+ struct ntvfs_handle *h;
+
+ handle = smbsrv_handle_new(req->session, req->tcon, req, req->request_time);
+ if (!handle) return NT_STATUS_INSUFFICIENT_RESOURCES;
+
+ h = talloc_zero(handle, struct ntvfs_handle);
+ if (!h) goto nomem;
+
+ /*
+ * note: we don't set handle->ntvfs yet,
+ * this will be done by smbsrv_handle_make_valid()
+ * this makes sure the handle is invalid for clients
+ * until the ntvfs subsystem has made it valid
+ */
+ h->ctx = ntvfs->ctx;
+ h->session_info = ntvfs->session_info;
+ h->smbpid = ntvfs->smbpid;
+
+ h->frontend_data.private_data = handle;
+
+ *_h = h;
+ return NT_STATUS_OK;
+nomem:
+ talloc_free(handle);
+ return NT_STATUS_NO_MEMORY;
+}
+
+static NTSTATUS smb2srv_handle_make_valid(void *private_data, struct ntvfs_handle *h)
+{
+ struct smbsrv_tcon *tcon = talloc_get_type(private_data, struct smbsrv_tcon);
+ struct smbsrv_handle *handle = talloc_get_type(h->frontend_data.private_data,
+ struct smbsrv_handle);
+ /* this tells the frontend that the handle is valid */
+ handle->ntvfs = h;
+ /* this moves the smbsrv_request to the smbsrv_tcon memory context */
+ talloc_steal(tcon, handle);
+ return NT_STATUS_OK;
+}
+
+static void smb2srv_handle_destroy(void *private_data, struct ntvfs_handle *h)
+{
+ struct smbsrv_handle *handle = talloc_get_type(h->frontend_data.private_data,
+ struct smbsrv_handle);
+ talloc_free(handle);
+}
+
+static struct ntvfs_handle *smb2srv_handle_search_by_wire_key(void *private_data, struct ntvfs_request *ntvfs, const DATA_BLOB *key)
+{
+ return NULL;
+}
+
+static DATA_BLOB smb2srv_handle_get_wire_key(void *private_data, struct ntvfs_handle *handle, TALLOC_CTX *mem_ctx)
+{
+ return data_blob(NULL, 0);
+}
+
+static NTSTATUS smb2srv_tcon_backend(struct smb2srv_request *req, union smb_tcon *io)
+{
+ struct smbsrv_tcon *tcon;
+ NTSTATUS status;
+ enum ntvfs_type type;
+ const char *service = io->smb2.in.path;
+ struct share_config *scfg;
+ const char *sharetype;
+ uint64_t ntvfs_caps = 0;
+
+ if (strncmp(service, "\\\\", 2) == 0) {
+ const char *p = strchr(service+2, '\\');
+ if (p) {
+ service = p + 1;
+ }
+ }
+
+ status = share_get_config(req, req->smb_conn->share_context, service, &scfg);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("smb2srv_tcon_backend: couldn't find service %s\n", service));
+ return NT_STATUS_BAD_NETWORK_NAME;
+ }
+
+ if (!socket_check_access(req->smb_conn->connection->socket,
+ scfg->name,
+ share_string_list_option(req, scfg, SHARE_HOSTS_ALLOW),
+ share_string_list_option(req, scfg, SHARE_HOSTS_DENY))) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* work out what sort of connection this is */
+ sharetype = share_string_option(scfg, SHARE_TYPE, "DISK");
+ if (sharetype && strcmp(sharetype, "IPC") == 0) {
+ type = NTVFS_IPC;
+ } else if (sharetype && strcmp(sharetype, "PRINTER") == 0) {
+ type = NTVFS_PRINT;
+ } else {
+ type = NTVFS_DISK;
+ }
+
+ tcon = smbsrv_smb2_tcon_new(req->session, scfg->name);
+ if (!tcon) {
+ DEBUG(0,("smb2srv_tcon_backend: Couldn't find free connection.\n"));
+ return NT_STATUS_INSUFFICIENT_RESOURCES;
+ }
+ req->tcon = tcon;
+
+ ntvfs_caps = NTVFS_CLIENT_CAP_LEVEL_II_OPLOCKS;
+
+ /* init ntvfs function pointers */
+ status = ntvfs_init_connection(tcon, scfg, type,
+ req->smb_conn->negotiate.protocol,
+ ntvfs_caps,
+ req->smb_conn->connection->event.ctx,
+ req->smb_conn->connection->msg_ctx,
+ req->smb_conn->lp_ctx,
+ req->smb_conn->connection->server_id,
+ &tcon->ntvfs);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("smb2srv_tcon_backend: ntvfs_init_connection failed for service %s\n",
+ scfg->name));
+ goto failed;
+ }
+
+ status = ntvfs_set_oplock_handler(tcon->ntvfs, smb2srv_send_oplock_break, tcon);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("smb2srv_tcon_backend: NTVFS failed to set the oplock handler!\n"));
+ goto failed;
+ }
+
+ status = ntvfs_set_addr_callbacks(tcon->ntvfs, smbsrv_get_my_addr, smbsrv_get_peer_addr, req->smb_conn);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("smb2srv_tcon_backend: NTVFS failed to set the addr callbacks!\n"));
+ goto failed;
+ }
+
+ status = ntvfs_set_handle_callbacks(tcon->ntvfs,
+ smb2srv_handle_create_new,
+ smb2srv_handle_make_valid,
+ smb2srv_handle_destroy,
+ smb2srv_handle_search_by_wire_key,
+ smb2srv_handle_get_wire_key,
+ tcon);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("smb2srv_tcon_backend: NTVFS failed to set the handle callbacks!\n"));
+ goto failed;
+ }
+
+ req->ntvfs = ntvfs_request_create(req->tcon->ntvfs, req,
+ req->session->session_info,
+ SVAL(req->in.hdr, SMB2_HDR_PID),
+ req->request_time,
+ req, NULL, 0);
+ if (!req->ntvfs) {
+ status = NT_STATUS_NO_MEMORY;
+ goto failed;
+ }
+
+ /* Invoke NTVFS connection hook */
+ status = ntvfs_connect(req->ntvfs, scfg->name);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("smb2srv_tcon_backend: NTVFS ntvfs_connect() failed!\n"));
+ goto failed;
+ }
+
+ io->smb2.out.share_type = (unsigned)type; /* 1 - DISK, 2 - Print, 3 - IPC */
+ io->smb2.out.reserved = 0;
+ io->smb2.out.flags = 0x00000000;
+ io->smb2.out.capabilities = 0;
+ io->smb2.out.access_mask = SEC_RIGHTS_FILE_ALL;
+
+ io->smb2.out.tid = tcon->tid;
+
+ return NT_STATUS_OK;
+
+failed:
+ req->tcon = NULL;
+ talloc_free(tcon);
+ return status;
+}
+
+static void smb2srv_tcon_send(struct smb2srv_request *req, union smb_tcon *io)
+{
+ uint16_t credit;
+
+ if (!NT_STATUS_IS_OK(req->status)) {
+ smb2srv_send_error(req, req->status);
+ return;
+ }
+ if (io->smb2.out.share_type == NTVFS_IPC) {
+ /* if it's an IPC share vista returns 0x0005 */
+ credit = 0x0005;
+ } else {
+ credit = 0x0001;
+ }
+
+ SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x10, false, 0));
+
+ SIVAL(req->out.hdr, SMB2_HDR_TID, io->smb2.out.tid);
+ SSVAL(req->out.hdr, SMB2_HDR_CREDIT,credit);
+
+ SCVAL(req->out.body, 0x02, io->smb2.out.share_type);
+ SCVAL(req->out.body, 0x03, io->smb2.out.reserved);
+ SIVAL(req->out.body, 0x04, io->smb2.out.flags);
+ SIVAL(req->out.body, 0x08, io->smb2.out.capabilities);
+ SIVAL(req->out.body, 0x0C, io->smb2.out.access_mask);
+
+ smb2srv_send_reply(req);
+}
+
+void smb2srv_tcon_recv(struct smb2srv_request *req)
+{
+ union smb_tcon *io;
+
+ SMB2SRV_CHECK_BODY_SIZE(req, 0x08, true);
+ SMB2SRV_TALLOC_IO_PTR(io, union smb_tcon);
+
+ io->smb2.level = RAW_TCON_SMB2;
+ io->smb2.in.reserved = SVAL(req->in.body, 0x02);
+ SMB2SRV_CHECK(smb2_pull_o16s16_string(&req->in, io, req->in.body+0x04, &io->smb2.in.path));
+
+ /* the VFS backend does not yet handle NULL paths */
+ if (io->smb2.in.path == NULL) {
+ io->smb2.in.path = "";
+ }
+
+ req->status = smb2srv_tcon_backend(req, io);
+
+ if (req->control_flags & SMB2SRV_REQ_CTRL_FLAG_NOT_REPLY) {
+ talloc_free(req);
+ return;
+ }
+ smb2srv_tcon_send(req, io);
+}
+
+static NTSTATUS smb2srv_tdis_backend(struct smb2srv_request *req)
+{
+ /* TODO: call ntvfs backends to close file of this tcon */
+ talloc_free(req->tcon);
+ req->tcon = NULL;
+ return NT_STATUS_OK;
+}
+
+static void smb2srv_tdis_send(struct smb2srv_request *req)
+{
+ NTSTATUS status;
+
+ if (NT_STATUS_IS_ERR(req->status)) {
+ smb2srv_send_error(req, req->status);
+ return;
+ }
+
+ status = smb2srv_setup_reply(req, 0x04, false, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ smbsrv_terminate_connection(req->smb_conn, nt_errstr(status));
+ talloc_free(req);
+ return;
+ }
+
+ SSVAL(req->out.body, 0x02, 0);
+
+ smb2srv_send_reply(req);
+}
+
+void smb2srv_tdis_recv(struct smb2srv_request *req)
+{
+ uint16_t _pad;
+
+ SMB2SRV_CHECK_BODY_SIZE(req, 0x04, false);
+
+ _pad = SVAL(req->in.body, 0x02);
+
+ req->status = smb2srv_tdis_backend(req);
+
+ if (req->control_flags & SMB2SRV_REQ_CTRL_FLAG_NOT_REPLY) {
+ talloc_free(req);
+ return;
+ }
+ smb2srv_tdis_send(req);
+}
diff --git a/source4/smb_server/smb_samba3.c b/source4/smb_server/smb_samba3.c
new file mode 100644
index 0000000000..1c84392b0c
--- /dev/null
+++ b/source4/smb_server/smb_samba3.c
@@ -0,0 +1,175 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ process incoming connections and fork a samba3 in inetd mode
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "smbd/service_task.h"
+#include "smbd/service_stream.h"
+#include "smbd/service.h"
+#include "lib/messaging/irpc.h"
+#include "lib/stream/packet.h"
+#include "lib/socket/socket.h"
+#include "libcli/smb2/smb2.h"
+#include "smb_server/smb2/smb2_server.h"
+#include "system/network.h"
+#include "lib/socket/netif.h"
+#include "param/share.h"
+#include "param/param.h"
+#include "dynconfig/dynconfig.h"
+#include "smbd/process_model.h"
+
+/*
+ initialise a server_context from a open socket and register a event handler
+ for reading from that socket
+*/
+static void samba3_smb_accept(struct stream_connection *conn)
+{
+ int i;
+ int fd = socket_get_fd(conn->socket);
+ const char *prog;
+ char *argv[2];
+ char *reason;
+ extern char **environ;
+
+ close(0);
+ close(1);
+ dup2(fd, 0);
+ dup2(fd, 1);
+ dup2(fd, 2);
+ for (i=3;i<256;i++) {
+ close(i);
+ }
+
+ prog = lp_parm_string(conn->lp_ctx, NULL, "samba3", "smbd");
+
+ if (prog == NULL) {
+ argv[0] = talloc_asprintf(conn, "%s/%s", dyn_BINDIR, "smbd3");
+ }
+ else {
+ argv[0] = talloc_strdup(conn, prog);
+ }
+
+ if (argv[0] == NULL) {
+ stream_terminate_connection(conn, "out of memory");
+ return;
+ }
+ argv[1] = NULL;
+
+ execve(argv[0], argv, environ);
+
+ /*
+ * Should never get here
+ */
+ reason = talloc_asprintf(conn, "Could not execute %s", argv[0]);
+ if (reason == NULL) {
+ stream_terminate_connection(conn, "out of memory");
+ return;
+ }
+ stream_terminate_connection(conn, reason);
+ talloc_free(reason);
+}
+
+static const struct stream_server_ops samba3_smb_stream_ops = {
+ .name = "samba3",
+ .accept_connection = samba3_smb_accept,
+};
+
+/*
+ setup a listening socket on all the SMB ports for a particular address
+*/
+static NTSTATUS samba3_add_socket(struct tevent_context *event_context,
+ struct loadparm_context *lp_ctx,
+ const struct model_ops *model_ops,
+ const char *address)
+{
+ const char **ports = lp_smb_ports(lp_ctx);
+ int i;
+ NTSTATUS status;
+
+ for (i=0;ports[i];i++) {
+ uint16_t port = atoi(ports[i]);
+ if (port == 0) continue;
+ status = stream_setup_socket(event_context, lp_ctx,
+ model_ops, &samba3_smb_stream_ops,
+ "ip", address, &port,
+ lp_socket_options(lp_ctx),
+ NULL);
+ NT_STATUS_NOT_OK_RETURN(status);
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ open the smb server sockets
+*/
+static void samba3_smb_task_init(struct task_server *task)
+{
+ NTSTATUS status;
+ const struct model_ops *model_ops;
+
+ model_ops = process_model_startup(task->event_ctx, "standard");
+
+ if (model_ops == NULL) {
+ goto failed;
+ }
+
+ task_server_set_title(task, "task[samba3_smb]");
+
+ if (lp_interfaces(task->lp_ctx)
+ && lp_bind_interfaces_only(task->lp_ctx)) {
+ int num_interfaces;
+ int i;
+ struct interface *ifaces;
+
+ load_interfaces(task, lp_interfaces(task->lp_ctx), &ifaces);
+
+ num_interfaces = iface_count(ifaces);
+
+ /* We have been given an interfaces line, and been
+ told to only bind to those interfaces. Create a
+ socket per interface and bind to only these.
+ */
+ for(i = 0; i < num_interfaces; i++) {
+ const char *address = iface_n_ip(ifaces, i);
+ status = samba3_add_socket(task->event_ctx,
+ task->lp_ctx,
+ model_ops, address);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+ }
+ } else {
+ /* Just bind to lp_socket_address() (usually 0.0.0.0) */
+ status = samba3_add_socket(task->event_ctx, task->lp_ctx,
+ model_ops,
+ lp_socket_address(task->lp_ctx));
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+ }
+
+ return;
+failed:
+ task_server_terminate(task, "Failed to startup samba3 smb task");
+}
+
+/* called at smbd startup - register ourselves as a server service */
+NTSTATUS server_service_samba3_smb_init(void)
+{
+ return register_server_service("samba3_smb", samba3_smb_task_init);
+}
diff --git a/source4/smb_server/smb_server.c b/source4/smb_server/smb_server.c
new file mode 100644
index 0000000000..6206a03b21
--- /dev/null
+++ b/source4/smb_server/smb_server.c
@@ -0,0 +1,259 @@
+/*
+ Unix SMB/CIFS implementation.
+ process incoming packets - main loop
+ Copyright (C) Andrew Tridgell 2004-2005
+ Copyright (C) Stefan Metzmacher 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "smbd/service_task.h"
+#include "smbd/service_stream.h"
+#include "smbd/service.h"
+#include "smb_server/smb_server.h"
+#include "smb_server/service_smb_proto.h"
+#include "lib/messaging/irpc.h"
+#include "lib/stream/packet.h"
+#include "libcli/smb2/smb2.h"
+#include "smb_server/smb2/smb2_server.h"
+#include "system/network.h"
+#include "lib/socket/netif.h"
+#include "param/share.h"
+#include "dsdb/samdb/samdb.h"
+#include "param/param.h"
+
+static NTSTATUS smbsrv_recv_generic_request(void *private_data, DATA_BLOB blob)
+{
+ NTSTATUS status;
+ struct smbsrv_connection *smb_conn = talloc_get_type(private_data, struct smbsrv_connection);
+ uint32_t protocol_version;
+
+ /* see if its a special NBT packet */
+ if (CVAL(blob.data,0) != 0) {
+ status = smbsrv_init_smb_connection(smb_conn, smb_conn->lp_ctx);
+ NT_STATUS_NOT_OK_RETURN(status);
+ packet_set_callback(smb_conn->packet, smbsrv_recv_smb_request);
+ return smbsrv_recv_smb_request(smb_conn, blob);
+ }
+
+ if (blob.length < (NBT_HDR_SIZE + MIN_SMB_SIZE)) {
+ DEBUG(2,("Invalid SMB packet length count %ld\n", (long)blob.length));
+ smbsrv_terminate_connection(smb_conn, "Invalid SMB packet");
+ return NT_STATUS_OK;
+ }
+
+ protocol_version = IVAL(blob.data, NBT_HDR_SIZE);
+
+ switch (protocol_version) {
+ case SMB_MAGIC:
+ status = smbsrv_init_smb_connection(smb_conn, smb_conn->lp_ctx);
+ NT_STATUS_NOT_OK_RETURN(status);
+ packet_set_callback(smb_conn->packet, smbsrv_recv_smb_request);
+ return smbsrv_recv_smb_request(smb_conn, blob);
+ case SMB2_MAGIC:
+ if (lp_srv_maxprotocol(smb_conn->lp_ctx) < PROTOCOL_SMB2) break;
+ status = smbsrv_init_smb2_connection(smb_conn);
+ NT_STATUS_NOT_OK_RETURN(status);
+ packet_set_callback(smb_conn->packet, smbsrv_recv_smb2_request);
+ return smbsrv_recv_smb2_request(smb_conn, blob);
+ }
+
+ DEBUG(2,("Invalid SMB packet: protocol prefix: 0x%08X\n", protocol_version));
+ smbsrv_terminate_connection(smb_conn, "NON-SMB packet");
+ return NT_STATUS_OK;
+}
+
+/*
+ close the socket and shutdown a server_context
+*/
+void smbsrv_terminate_connection(struct smbsrv_connection *smb_conn, const char *reason)
+{
+ stream_terminate_connection(smb_conn->connection, reason);
+}
+
+/*
+ called when a SMB socket becomes readable
+*/
+static void smbsrv_recv(struct stream_connection *conn, uint16_t flags)
+{
+ struct smbsrv_connection *smb_conn = talloc_get_type(conn->private_data,
+ struct smbsrv_connection);
+
+ DEBUG(10,("smbsrv_recv\n"));
+
+ packet_recv(smb_conn->packet);
+}
+
+/*
+ called when a SMB socket becomes writable
+*/
+static void smbsrv_send(struct stream_connection *conn, uint16_t flags)
+{
+ struct smbsrv_connection *smb_conn = talloc_get_type(conn->private_data,
+ struct smbsrv_connection);
+ packet_queue_run(smb_conn->packet);
+}
+
+/*
+ handle socket recv errors
+*/
+static void smbsrv_recv_error(void *private_data, NTSTATUS status)
+{
+ struct smbsrv_connection *smb_conn = talloc_get_type(private_data, struct smbsrv_connection);
+
+ smbsrv_terminate_connection(smb_conn, nt_errstr(status));
+}
+
+/*
+ initialise a server_context from a open socket and register a event handler
+ for reading from that socket
+*/
+static void smbsrv_accept(struct stream_connection *conn)
+{
+ struct smbsrv_connection *smb_conn;
+
+ DEBUG(5,("smbsrv_accept\n"));
+
+ smb_conn = talloc_zero(conn, struct smbsrv_connection);
+ if (!smb_conn) {
+ stream_terminate_connection(conn, "out of memory");
+ return;
+ }
+
+ smb_conn->packet = packet_init(smb_conn);
+ if (!smb_conn->packet) {
+ smbsrv_terminate_connection(smb_conn, "out of memory");
+ return;
+ }
+ packet_set_private(smb_conn->packet, smb_conn);
+ packet_set_socket(smb_conn->packet, conn->socket);
+ packet_set_callback(smb_conn->packet, smbsrv_recv_generic_request);
+ packet_set_full_request(smb_conn->packet, packet_full_request_nbt);
+ packet_set_error_handler(smb_conn->packet, smbsrv_recv_error);
+ packet_set_event_context(smb_conn->packet, conn->event.ctx);
+ packet_set_fde(smb_conn->packet, conn->event.fde);
+ packet_set_serialise(smb_conn->packet);
+
+ smb_conn->lp_ctx = conn->lp_ctx;
+ smb_conn->connection = conn;
+ conn->private_data = smb_conn;
+
+ smb_conn->statistics.connect_time = timeval_current();
+
+ smbsrv_management_init(smb_conn);
+
+ irpc_add_name(conn->msg_ctx, "smb_server");
+
+ if (!NT_STATUS_IS_OK(share_get_context_by_name(smb_conn, lp_share_backend(smb_conn->lp_ctx),
+ smb_conn->connection->event.ctx,
+ smb_conn->lp_ctx, &(smb_conn->share_context)))) {
+ smbsrv_terminate_connection(smb_conn, "share_init failed!");
+ return;
+ }
+}
+
+static const struct stream_server_ops smb_stream_ops = {
+ .name = "smbsrv",
+ .accept_connection = smbsrv_accept,
+ .recv_handler = smbsrv_recv,
+ .send_handler = smbsrv_send,
+};
+
+/*
+ setup a listening socket on all the SMB ports for a particular address
+*/
+_PUBLIC_ NTSTATUS smbsrv_add_socket(struct tevent_context *event_context,
+ struct loadparm_context *lp_ctx,
+ const struct model_ops *model_ops,
+ const char *address)
+{
+ const char **ports = lp_smb_ports(lp_ctx);
+ int i;
+ NTSTATUS status;
+
+ for (i=0;ports[i];i++) {
+ uint16_t port = atoi(ports[i]);
+ if (port == 0) continue;
+ status = stream_setup_socket(event_context, lp_ctx,
+ model_ops, &smb_stream_ops,
+ "ipv4", address, &port,
+ lp_socket_options(lp_ctx),
+ NULL);
+ NT_STATUS_NOT_OK_RETURN(status);
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ pre-open some of our ldb databases, to prevent an explosion of memory usage
+ when we fork
+ */
+static void smbsrv_preopen_ldb(struct task_server *task)
+{
+ /* yes, this looks strange. It is a hack to preload the
+ schema. I'd like to share most of the ldb context with the
+ child too. That will come later */
+ talloc_free(samdb_connect(task, task->event_ctx, task->lp_ctx, NULL));
+}
+
+/*
+ open the smb server sockets
+*/
+static void smbsrv_task_init(struct task_server *task)
+{
+ NTSTATUS status;
+
+ task_server_set_title(task, "task[smbsrv]");
+
+ if (lp_interfaces(task->lp_ctx) && lp_bind_interfaces_only(task->lp_ctx)) {
+ int num_interfaces;
+ int i;
+ struct interface *ifaces;
+
+ load_interfaces(task, lp_interfaces(task->lp_ctx), &ifaces);
+
+ num_interfaces = iface_count(ifaces);
+
+ /* We have been given an interfaces line, and been
+ told to only bind to those interfaces. Create a
+ socket per interface and bind to only these.
+ */
+ for(i = 0; i < num_interfaces; i++) {
+ const char *address = iface_n_ip(ifaces, i);
+ status = smbsrv_add_socket(task->event_ctx, task->lp_ctx, task->model_ops, address);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+ }
+ } else {
+ /* Just bind to lp_socket_address() (usually 0.0.0.0) */
+ status = smbsrv_add_socket(task->event_ctx, task->lp_ctx, task->model_ops,
+ lp_socket_address(task->lp_ctx));
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+ }
+
+ smbsrv_preopen_ldb(task);
+
+ return;
+failed:
+ task_server_terminate(task, "Failed to startup smb server task");
+}
+
+/* called at smbd startup - register ourselves as a server service */
+NTSTATUS server_service_smb_init(void)
+{
+ share_init();
+ return register_server_service("smb", smbsrv_task_init);
+}
diff --git a/source4/smb_server/smb_server.h b/source4/smb_server/smb_server.h
new file mode 100644
index 0000000000..d238e03a75
--- /dev/null
+++ b/source4/smb_server/smb_server.h
@@ -0,0 +1,521 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) James J Myers 2003 <myersjj@samba.org>
+ Copyright (C) Stefan Metzmacher 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "libcli/raw/request.h"
+#include "libcli/raw/interfaces.h"
+#include "lib/socket/socket.h"
+#include "../lib/util/dlinklist.h"
+
+struct tevent_context;
+
+/*
+ this header declares the core context structures associated with smb
+ sockets, tree connects, requests etc
+
+ the idea is that we will eventually get rid of all our global
+ variables and instead store our state from structures hanging off
+ these basic elements
+*/
+
+struct smbsrv_tcons_context {
+ /* an id tree used to allocate tids */
+ struct idr_context *idtree_tid;
+
+ /* this is the limit of vuid values for this connection */
+ uint32_t idtree_limit;
+
+ /* list of open tree connects */
+ struct smbsrv_tcon *list;
+};
+
+struct smbsrv_sessions_context {
+ /* an id tree used to allocate vuids */
+ /* this holds info on session vuids that are already
+ * validated for this VC */
+ struct idr_context *idtree_vuid;
+
+ /* this is the limit of vuid values for this connection */
+ uint64_t idtree_limit;
+
+ /* also kept as a link list so it can be enumerated by
+ the management code */
+ struct smbsrv_session *list;
+};
+
+struct smbsrv_handles_context {
+ /* an id tree used to allocate file handles */
+ struct idr_context *idtree_hid;
+
+ /* this is the limit of handle values for this context */
+ uint64_t idtree_limit;
+
+ /* also kept as a link list so it can be enumerated by
+ the management code */
+ struct smbsrv_handle *list;
+};
+
+/* the current user context for a request */
+struct smbsrv_session {
+ struct smbsrv_session *prev, *next;
+
+ struct smbsrv_connection *smb_conn;
+
+ /*
+ * in SMB2 tcons belong to just one session
+ * and not to the whole connection
+ */
+ struct smbsrv_tcons_context smb2_tcons;
+
+ /*
+ * the open file handles for this session,
+ * used for SMBexit, SMBulogoff and SMB2 SessionLogoff
+ */
+ struct smbsrv_handle_session_item *handles;
+
+ /*
+ * an index passed over the wire:
+ * - 16 bit for smb
+ * - 64 bit for smb2
+ */
+ uint64_t vuid;
+
+ struct gensec_security *gensec_ctx;
+
+ struct auth_session_info *session_info;
+
+ struct {
+ bool required;
+ bool active;
+ } smb2_signing;
+
+ /* some statistics for the management tools */
+ struct {
+ /* the time when the session setup started */
+ struct timeval connect_time;
+ /* the time when the session setup was finished */
+ struct timeval auth_time;
+ /* the time when the last request comes in */
+ struct timeval last_request_time;
+ } statistics;
+};
+
+/* we need a forward declaration of the ntvfs_ops strucutre to prevent
+ include recursion */
+struct ntvfs_context;
+
+struct smbsrv_tcon {
+ struct smbsrv_tcon *next, *prev;
+
+ /* the server context that this was created on */
+ struct smbsrv_connection *smb_conn;
+
+ /* the open file handles on this tcon */
+ struct smbsrv_handles_context handles;
+
+ /*
+ * an index passed over the wire:
+ * - 16 bit for smb
+ * - 32 bit for smb2
+ */
+ uint32_t tid; /* an index passed over the wire (the TID) */
+
+ /* the share name */
+ const char *share_name;
+
+ /* the NTVFS context - see source/ntvfs/ for details */
+ struct ntvfs_context *ntvfs;
+
+ /* some stuff to support share level security */
+ struct {
+ /* in share level security we need to fake up a session */
+ struct smbsrv_session *session;
+ } sec_share;
+
+ /* some stuff to support share level security */
+ struct {
+ /* in SMB2 a tcon always belongs to one session */
+ struct smbsrv_session *session;
+ } smb2;
+
+ /* some statistics for the management tools */
+ struct {
+ /* the time when the tree connect started */
+ struct timeval connect_time;
+ /* the time when the last request comes in */
+ struct timeval last_request_time;
+ } statistics;
+};
+
+struct smbsrv_handle {
+ struct smbsrv_handle *next, *prev;
+
+ /* the tcon the handle belongs to */
+ struct smbsrv_tcon *tcon;
+
+ /* the session the handle was opened on */
+ struct smbsrv_session *session;
+
+ /* the smbpid used on the open, used for SMBexit */
+ uint16_t smbpid;
+
+ /*
+ * this is for adding the handle into a linked list
+ * on the smbsrv_session, we can't use *next,*prev
+ * for this because they're used for the linked list on the
+ * smbsrv_tcon
+ */
+ struct smbsrv_handle_session_item {
+ struct smbsrv_handle_session_item *prev, *next;
+ struct smbsrv_handle *handle;
+ } session_item;
+
+ /*
+ * the value passed over the wire
+ * - 16 bit for smb
+ * - 32 bit for smb2
+ * Note: for SMB2 handles are 128 bit
+ * we'll fill them with
+ * - 32 bit HID
+ * - 32 bit TID
+ * - 64 bit VUID
+ */
+ uint32_t hid;
+
+ /*
+ * the ntvfs handle passed to the ntvfs backend
+ */
+ struct ntvfs_handle *ntvfs;
+
+ /* some statistics for the management tools */
+ struct {
+ /* the time when the tree connect started */
+ struct timeval open_time;
+ /* the time when the last request comes in */
+ struct timeval last_use_time;
+ } statistics;
+};
+
+/* a set of flags to control handling of request structures */
+#define SMBSRV_REQ_CONTROL_LARGE (1<<1) /* allow replies larger than max_xmit */
+
+#define SMBSRV_REQ_DEFAULT_STR_FLAGS(req) (((req)->flags2 & FLAGS2_UNICODE_STRINGS) ? STR_UNICODE : STR_ASCII)
+
+/* the context for a single SMB request. This is passed to any request-context
+ functions */
+struct smbsrv_request {
+ /* the smbsrv_connection needs a list of requests queued for send */
+ struct smbsrv_request *next, *prev;
+
+ /* the server_context contains all context specific to this SMB socket */
+ struct smbsrv_connection *smb_conn;
+
+ /* conn is only set for operations that have a valid TID */
+ struct smbsrv_tcon *tcon;
+
+ /* the session context is derived from the vuid */
+ struct smbsrv_session *session;
+
+ /* a set of flags to control usage of the request. See SMBSRV_REQ_CONTROL_* */
+ uint32_t control_flags;
+
+ /* the system time when the request arrived */
+ struct timeval request_time;
+
+ /* a pointer to the per request union smb_* io structure */
+ void *io_ptr;
+
+ /* the ntvfs_request */
+ struct ntvfs_request *ntvfs;
+
+ /* Now the SMB specific stuff */
+
+ /* the flags from the SMB request, in raw form (host byte order) */
+ uint16_t flags2;
+
+ /* this can contain a fnum from an earlier part of a chained
+ * message (such as an SMBOpenX), or -1 */
+ int chained_fnum;
+
+ /* how far through the chain of SMB commands have we gone? */
+ unsigned chain_count;
+
+ /* the sequence number for signing */
+ uint64_t seq_num;
+
+ struct smb_request_buffer in;
+ struct smb_request_buffer out;
+};
+
+enum security_types {SEC_SHARE,SEC_USER};
+
+/* smb server context structure. This should contain all the state
+ * information associated with a SMB server connection
+ */
+struct smbsrv_connection {
+ /* context that has been negotiated between the client and server */
+ struct {
+ /* have we already done the NBT session establishment? */
+ bool done_nbt_session;
+
+ /* only one negprot per connection is allowed */
+ bool done_negprot;
+
+ /* multiple session setups are allowed, but some parameters are
+ ignored in any but the first */
+ bool done_sesssetup;
+
+ /*
+ * Size of data we can send to client. Set
+ * by the client for all protocols above CORE.
+ * Set by us for CORE protocol.
+ */
+ unsigned max_send; /* init to BUFFER_SIZE */
+
+ /*
+ * Size of the data we can receive. Set by us.
+ * Can be modified by the max xmit parameter.
+ */
+ unsigned max_recv; /* init to BUFFER_SIZE */
+
+ /* the negotiatiated protocol */
+ enum protocol_types protocol;
+
+ /* authentication context for multi-part negprot */
+ struct auth_context *auth_context;
+
+ /* reference to the kerberos keytab, or machine trust account */
+ struct cli_credentials *server_credentials;
+
+ /* did we tell the client we support encrypted passwords? */
+ bool encrypted_passwords;
+
+ /* Did we choose SPNEGO, or perhaps raw NTLMSSP, or even no extended security at all? */
+ const char *oid;
+
+ /* client capabilities */
+ uint32_t client_caps;
+
+ /* the timezone we sent to the client */
+ int zone_offset;
+
+ /* NBT names only set when done_nbt_session is true */
+ struct nbt_name *called_name;
+ struct nbt_name *calling_name;
+ } negotiate;
+
+ /* the context associated with open tree connects on a smb socket, not for SMB2 */
+ struct smbsrv_tcons_context smb_tcons;
+
+ /* context associated with currently valid session setups */
+ struct smbsrv_sessions_context sessions;
+
+ /*
+ * the server_context holds a linked list of pending requests,
+ * this is used for finding the request structures on ntcancel requests
+ * For SMB only
+ */
+ struct smbsrv_request *requests;
+
+ /*
+ * the server_context holds a linked list of pending requests,
+ * and an idtree for finding the request structures on SMB2 Cancel
+ * For SMB2 only
+ */
+ struct {
+ /* an id tree used to allocate ids */
+ struct idr_context *idtree_req;
+
+ /* this is the limit of pending requests values for this connection */
+ uint32_t idtree_limit;
+
+ /* list of open tree connects */
+ struct smb2srv_request *list;
+ } requests2;
+
+ struct smb_signing_context signing;
+
+ struct stream_connection *connection;
+
+ /* this holds a partially received request */
+ struct packet_context *packet;
+
+ /* a list of partially received transaction requests */
+ struct smbsrv_trans_partial {
+ struct smbsrv_trans_partial *next, *prev;
+ struct smbsrv_request *req;
+ uint8_t command;
+ union {
+ struct smb_trans2 *trans;
+ struct smb_nttrans *nttrans;
+ } u;
+ } *trans_partial;
+
+ /* configuration parameters */
+ struct {
+ enum security_types security;
+ bool nt_status_support;
+ } config;
+
+ /* some statictics for the management tools */
+ struct {
+ /* the time when the client connects */
+ struct timeval connect_time;
+ /* the time when the last request comes in */
+ struct timeval last_request_time;
+ } statistics;
+
+ struct share_context *share_context;
+
+ struct loadparm_context *lp_ctx;
+
+ bool smb2_signing_required;
+
+ uint64_t highest_smb2_seqnum;
+};
+
+struct model_ops;
+struct loadparm_context;
+
+NTSTATUS smbsrv_add_socket(struct tevent_context *event_context,
+ struct loadparm_context *lp_ctx,
+ const struct model_ops *model_ops,
+ const char *address);
+
+struct loadparm_context;
+
+#include "smb_server/smb_server_proto.h"
+#include "smb_server/smb/smb_proto.h"
+
+/* useful way of catching wct errors with file and line number */
+#define SMBSRV_CHECK_WCT(req, wcount) do { \
+ if ((req)->in.wct != (wcount)) { \
+ DEBUG(1,("Unexpected WCT %u at %s(%d) - expected %d\n", \
+ (req)->in.wct, __FILE__, __LINE__, wcount)); \
+ smbsrv_send_error(req, NT_STATUS_DOS(ERRSRV, ERRerror)); \
+ return; \
+ } \
+} while (0)
+
+/* useful wrapper for talloc with NO_MEMORY reply */
+#define SMBSRV_TALLOC_IO_PTR(ptr, type) do { \
+ ptr = talloc(req, type); \
+ if (!ptr) { \
+ smbsrv_send_error(req, NT_STATUS_NO_MEMORY); \
+ return; \
+ } \
+ req->io_ptr = ptr; \
+} while (0)
+
+#define SMBSRV_SETUP_NTVFS_REQUEST(send_fn, state) do { \
+ req->ntvfs = ntvfs_request_create(req->tcon->ntvfs, req, \
+ req->session->session_info,\
+ SVAL(req->in.hdr,HDR_PID), \
+ req->request_time, \
+ req, send_fn, state); \
+ if (!req->ntvfs) { \
+ smbsrv_send_error(req, NT_STATUS_NO_MEMORY); \
+ return; \
+ } \
+ (void)talloc_steal(req->tcon->ntvfs, req); \
+ req->ntvfs->frontend_data.private_data = req; \
+} while (0)
+
+#define SMBSRV_CHECK_FILE_HANDLE(handle) do { \
+ if (!handle) { \
+ smbsrv_send_error(req, NT_STATUS_INVALID_HANDLE); \
+ return; \
+ } \
+} while (0)
+
+#define SMBSRV_CHECK_FILE_HANDLE_ERROR(handle, _status) do { \
+ if (!handle) { \
+ smbsrv_send_error(req, _status); \
+ return; \
+ } \
+} while (0)
+
+#define SMBSRV_CHECK_FILE_HANDLE_NTSTATUS(handle) do { \
+ if (!handle) { \
+ return NT_STATUS_INVALID_HANDLE; \
+ } \
+} while (0)
+
+#define SMBSRV_CHECK(cmd) do {\
+ NTSTATUS _status; \
+ _status = cmd; \
+ if (!NT_STATUS_IS_OK(_status)) { \
+ smbsrv_send_error(req, _status); \
+ return; \
+ } \
+} while (0)
+
+/*
+ check if the backend wants to handle the request asynchronously.
+ if it wants it handled synchronously then call the send function
+ immediately
+*/
+#define SMBSRV_CALL_NTVFS_BACKEND(cmd) do { \
+ req->ntvfs->async_states->status = cmd; \
+ if (req->ntvfs->async_states->state & NTVFS_ASYNC_STATE_ASYNC) { \
+ DLIST_ADD_END(req->smb_conn->requests, req, struct smbsrv_request *); \
+ } else { \
+ req->ntvfs->async_states->send_fn(req->ntvfs); \
+ } \
+} while (0)
+
+/* check req->ntvfs->async_states->status and if not OK then send an error reply */
+#define SMBSRV_CHECK_ASYNC_STATUS_ERR_SIMPLE do { \
+ req = talloc_get_type(ntvfs->async_states->private_data, struct smbsrv_request); \
+ if (ntvfs->async_states->state & NTVFS_ASYNC_STATE_CLOSE || NT_STATUS_EQUAL(ntvfs->async_states->status, NT_STATUS_NET_WRITE_FAULT)) { \
+ smbsrv_terminate_connection(req->smb_conn, get_friendly_nt_error_msg (ntvfs->async_states->status)); \
+ talloc_free(req); \
+ return; \
+ } \
+ if (NT_STATUS_IS_ERR(ntvfs->async_states->status)) { \
+ smbsrv_send_error(req, ntvfs->async_states->status); \
+ return; \
+ } \
+} while (0)
+#define SMBSRV_CHECK_ASYNC_STATUS_ERR(ptr, type) do { \
+ SMBSRV_CHECK_ASYNC_STATUS_ERR_SIMPLE; \
+ ptr = talloc_get_type(req->io_ptr, type); \
+} while (0)
+#define SMBSRV_CHECK_ASYNC_STATUS_SIMPLE do { \
+ req = talloc_get_type(ntvfs->async_states->private_data, struct smbsrv_request); \
+ if (ntvfs->async_states->state & NTVFS_ASYNC_STATE_CLOSE || NT_STATUS_EQUAL(ntvfs->async_states->status, NT_STATUS_NET_WRITE_FAULT)) { \
+ smbsrv_terminate_connection(req->smb_conn, get_friendly_nt_error_msg (ntvfs->async_states->status)); \
+ talloc_free(req); \
+ return; \
+ } \
+ if (!NT_STATUS_IS_OK(ntvfs->async_states->status)) { \
+ smbsrv_send_error(req, ntvfs->async_states->status); \
+ return; \
+ } \
+} while (0)
+#define SMBSRV_CHECK_ASYNC_STATUS(ptr, type) do { \
+ SMBSRV_CHECK_ASYNC_STATUS_SIMPLE; \
+ ptr = talloc_get_type(req->io_ptr, type); \
+} while (0)
+
+/* zero out some reserved fields in a reply */
+#define SMBSRV_VWV_RESERVED(start, count) memset(req->out.vwv + VWV(start), 0, (count)*2)
+
+#include "smb_server/service_smb_proto.h"
diff --git a/source4/smb_server/tcon.c b/source4/smb_server/tcon.c
new file mode 100644
index 0000000000..12131ea259
--- /dev/null
+++ b/source4/smb_server/tcon.c
@@ -0,0 +1,209 @@
+/*
+ Unix SMB/CIFS implementation.
+ Manage smbsrv_tcon structures
+ Copyright (C) Andrew Tridgell 1998
+ Copyright (C) Alexander Bokovoy 2002
+ Copyright (C) Stefan Metzmacher 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "../lib/util/dlinklist.h"
+#include "smb_server/smb_server.h"
+#include "smbd/service_stream.h"
+#include "ntvfs/ntvfs.h"
+
+struct socket_address *smbsrv_get_my_addr(void *p, TALLOC_CTX *mem_ctx)
+{
+ struct smbsrv_connection *smb_conn = talloc_get_type(p,
+ struct smbsrv_connection);
+
+ return socket_get_my_addr(smb_conn->connection->socket, mem_ctx);
+}
+
+struct socket_address *smbsrv_get_peer_addr(void *p, TALLOC_CTX *mem_ctx)
+{
+ struct smbsrv_connection *smb_conn = talloc_get_type(p,
+ struct smbsrv_connection);
+
+ return socket_get_peer_addr(smb_conn->connection->socket, mem_ctx);
+}
+
+/****************************************************************************
+init the tcon structures
+****************************************************************************/
+static NTSTATUS smbsrv_init_tcons(struct smbsrv_tcons_context *tcons_ctx, TALLOC_CTX *mem_ctx, uint32_t limit)
+{
+ /*
+ * the idr_* functions take 'int' as limit,
+ * and only work with a max limit 0x00FFFFFF
+ */
+ limit &= 0x00FFFFFF;
+
+ tcons_ctx->idtree_tid = idr_init(mem_ctx);
+ NT_STATUS_HAVE_NO_MEMORY(tcons_ctx->idtree_tid);
+ tcons_ctx->idtree_limit = limit;
+ tcons_ctx->list = NULL;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS smbsrv_smb_init_tcons(struct smbsrv_connection *smb_conn)
+{
+ return smbsrv_init_tcons(&smb_conn->smb_tcons, smb_conn, UINT16_MAX);
+}
+
+NTSTATUS smbsrv_smb2_init_tcons(struct smbsrv_session *smb_sess)
+{
+ return smbsrv_init_tcons(&smb_sess->smb2_tcons, smb_sess, UINT32_MAX);
+}
+
+/****************************************************************************
+find a tcon given a tid for SMB
+****************************************************************************/
+static struct smbsrv_tcon *smbsrv_tcon_find(struct smbsrv_tcons_context *tcons_ctx,
+ uint32_t tid, struct timeval request_time)
+{
+ void *p;
+ struct smbsrv_tcon *tcon;
+
+ if (tid == 0) return NULL;
+
+ if (tid > tcons_ctx->idtree_limit) return NULL;
+
+ p = idr_find(tcons_ctx->idtree_tid, tid);
+ if (!p) return NULL;
+
+ tcon = talloc_get_type(p, struct smbsrv_tcon);
+ if (!tcon) return NULL;
+
+ tcon->statistics.last_request_time = request_time;
+
+ return tcon;
+}
+
+struct smbsrv_tcon *smbsrv_smb_tcon_find(struct smbsrv_connection *smb_conn,
+ uint32_t tid, struct timeval request_time)
+{
+ return smbsrv_tcon_find(&smb_conn->smb_tcons, tid, request_time);
+}
+
+struct smbsrv_tcon *smbsrv_smb2_tcon_find(struct smbsrv_session *smb_sess,
+ uint32_t tid, struct timeval request_time)
+{
+ if (!smb_sess) return NULL;
+ return smbsrv_tcon_find(&smb_sess->smb2_tcons, tid, request_time);
+}
+
+/*
+ destroy a connection structure
+*/
+static int smbsrv_tcon_destructor(struct smbsrv_tcon *tcon)
+{
+ struct smbsrv_tcons_context *tcons_ctx;
+ struct socket_address *client_addr;
+
+ client_addr = socket_get_peer_addr(tcon->smb_conn->connection->socket, tcon);
+ DEBUG(3,("%s closed connection to service %s\n",
+ client_addr ? client_addr->addr : "(unknown)",
+ tcon->share_name));
+
+ /* tell the ntvfs backend that we are disconnecting */
+ if (tcon->ntvfs) {
+ ntvfs_disconnect(tcon->ntvfs);
+ tcon->ntvfs = NULL;
+ }
+
+ if (tcon->smb2.session) {
+ tcons_ctx = &tcon->smb2.session->smb2_tcons;
+ } else {
+ tcons_ctx = &tcon->smb_conn->smb_tcons;
+ }
+
+ idr_remove(tcons_ctx->idtree_tid, tcon->tid);
+ DLIST_REMOVE(tcons_ctx->list, tcon);
+ return 0;
+}
+
+/*
+ find first available connection slot
+*/
+static struct smbsrv_tcon *smbsrv_tcon_new(struct smbsrv_connection *smb_conn,
+ struct smbsrv_session *smb_sess,
+ const char *share_name)
+{
+ TALLOC_CTX *mem_ctx;
+ struct smbsrv_tcons_context *tcons_ctx;
+ uint32_t handle_uint_max;
+ struct smbsrv_tcon *tcon;
+ NTSTATUS status;
+ int i;
+
+ if (smb_sess) {
+ mem_ctx = smb_sess;
+ tcons_ctx = &smb_sess->smb2_tcons;
+ handle_uint_max = UINT32_MAX;
+ } else {
+ mem_ctx = smb_conn;
+ tcons_ctx = &smb_conn->smb_tcons;
+ handle_uint_max = UINT16_MAX;
+ }
+
+ tcon = talloc_zero(mem_ctx, struct smbsrv_tcon);
+ if (!tcon) return NULL;
+ tcon->smb_conn = smb_conn;
+ tcon->smb2.session = smb_sess;
+ tcon->share_name = talloc_strdup(tcon, share_name);
+ if (!tcon->share_name) goto failed;
+
+ /*
+ * the use -1 here, because we don't want to give away the wildcard
+ * fnum used in SMBflush
+ */
+ status = smbsrv_init_handles(tcon, handle_uint_max - 1);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1,("ERROR! failed to init handles: %s\n", nt_errstr(status)));
+ goto failed;
+ }
+
+ i = idr_get_new_random(tcons_ctx->idtree_tid, tcon, tcons_ctx->idtree_limit);
+ if (i == -1) {
+ DEBUG(1,("ERROR! Out of connection structures\n"));
+ goto failed;
+ }
+ tcon->tid = i;
+
+ DLIST_ADD(tcons_ctx->list, tcon);
+ talloc_set_destructor(tcon, smbsrv_tcon_destructor);
+
+ /* now fill in some statistics */
+ tcon->statistics.connect_time = timeval_current();
+
+ return tcon;
+
+failed:
+ talloc_free(tcon);
+ return NULL;
+}
+
+struct smbsrv_tcon *smbsrv_smb_tcon_new(struct smbsrv_connection *smb_conn, const char *share_name)
+{
+ return smbsrv_tcon_new(smb_conn, NULL, share_name);
+}
+
+struct smbsrv_tcon *smbsrv_smb2_tcon_new(struct smbsrv_session *smb_sess, const char *share_name)
+{
+ return smbsrv_tcon_new(smb_sess->smb_conn, smb_sess, share_name);
+}
diff --git a/source4/smbd/config.mk b/source4/smbd/config.mk
new file mode 100644
index 0000000000..a76d10cbe7
--- /dev/null
+++ b/source4/smbd/config.mk
@@ -0,0 +1,42 @@
+# server subsystem
+
+[SUBSYSTEM::service]
+PRIVATE_DEPENDENCIES = \
+ LIBTEVENT MESSAGING samba_socket NDR_NAMED_PIPE_AUTH
+
+service_OBJ_FILES = $(addprefix $(smbdsrcdir)/, \
+ service.o \
+ service_stream.o \
+ service_named_pipe.o \
+ service_task.o)
+
+$(eval $(call proto_header_template,$(smbdsrcdir)/service_proto.h,$(service_OBJ_FILES:.o=.c)))
+
+[SUBSYSTEM::PIDFILE]
+
+PIDFILE_OBJ_FILES = $(smbdsrcdir)/pidfile.o
+
+$(eval $(call proto_header_template,$(smbdsrcdir)/pidfile.h,$(PIDFILE_OBJ_FILES:.o=.c)))
+
+[BINARY::samba]
+INSTALLDIR = SBINDIR
+PRIVATE_DEPENDENCIES = \
+ LIBEVENTS \
+ process_model \
+ service \
+ LIBSAMBA-HOSTCONFIG \
+ LIBSAMBA-UTIL \
+ POPT_SAMBA \
+ PIDFILE \
+ LIBPOPT \
+ gensec \
+ registry \
+ ntptr \
+ ntvfs \
+ share \
+ CLUSTER
+
+samba_OBJ_FILES = $(smbdsrcdir)/server.o
+$(samba_OBJ_FILES): CFLAGS+=-DSTATIC_service_MODULES="$(service_INIT_FUNCTIONS)NULL"
+
+MANPAGES += $(smbdsrcdir)/samba.8
diff --git a/source4/smbd/pidfile.c b/source4/smbd/pidfile.c
new file mode 100644
index 0000000000..44600d33cf
--- /dev/null
+++ b/source4/smbd/pidfile.c
@@ -0,0 +1,121 @@
+/* this code is broken - there is a race condition with the unlink (tridge) */
+
+/*
+ Unix SMB/CIFS implementation.
+ pidfile handling
+ Copyright (C) Andrew Tridgell 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "smbd/pidfile.h"
+
+/**
+ * @file
+ * @brief Pid file handling
+ */
+
+/**
+ * return the pid in a pidfile. return 0 if the process (or pidfile)
+ * does not exist
+ */
+pid_t pidfile_pid(const char *piddir, const char *name)
+{
+ int fd;
+ char pidstr[20];
+ pid_t ret;
+ char *pidFile;
+
+ asprintf(&pidFile, "%s/%s.pid", piddir, name);
+
+ fd = open(pidFile, O_NONBLOCK | O_RDONLY, 0644);
+
+ if (fd == -1) {
+ SAFE_FREE(pidFile);
+ return 0;
+ }
+
+ ZERO_STRUCT(pidstr);
+
+ if (read(fd, pidstr, sizeof(pidstr)-1) <= 0) {
+ goto noproc;
+ }
+
+ ret = (pid_t)atoi(pidstr);
+
+ if (!process_exists_by_pid(ret)) {
+ goto noproc;
+ }
+
+ if (fcntl_lock(fd,F_SETLK,0,1,F_RDLCK)) {
+ /* we could get the lock - it can't be a Samba process */
+ goto noproc;
+ }
+
+ close(fd);
+ SAFE_FREE(pidFile);
+ return ret;
+
+ noproc:
+ close(fd);
+ unlink(pidFile);
+ SAFE_FREE(pidFile);
+ return 0;
+}
+
+/**
+ * create a pid file in the pid directory. open it and leave it locked
+ */
+void pidfile_create(const char *piddir, const char *name)
+{
+ int fd;
+ char buf[20];
+ char *pidFile;
+ pid_t pid;
+
+ asprintf(&pidFile, "%s/%s.pid", piddir, name);
+
+ pid = pidfile_pid(piddir, name);
+ if (pid != 0) {
+ DEBUG(0,("ERROR: %s is already running. File %s exists and process id %d is running.\n",
+ name, pidFile, (int)pid));
+ exit(1);
+ }
+
+ fd = open(pidFile, O_NONBLOCK | O_CREAT | O_WRONLY | O_EXCL, 0644);
+ if (fd == -1) {
+ DEBUG(0,("ERROR: can't open %s: Error was %s\n", pidFile,
+ strerror(errno)));
+ exit(1);
+ }
+
+ if (fcntl_lock(fd,F_SETLK,0,1,F_WRLCK)==false) {
+ DEBUG(0,("ERROR: %s : fcntl lock of file %s failed. Error was %s\n",
+ name, pidFile, strerror(errno)));
+ exit(1);
+ }
+
+ memset(buf, 0, sizeof(buf));
+ slprintf(buf, sizeof(buf) - 1, "%u\n", (unsigned int) getpid());
+ if (write(fd, buf, strlen(buf)) != (ssize_t)strlen(buf)) {
+ DEBUG(0,("ERROR: can't write to file %s: %s\n",
+ pidFile, strerror(errno)));
+ exit(1);
+ }
+
+ /* Leave pid file open & locked for the duration... */
+ SAFE_FREE(pidFile);
+}
diff --git a/source4/smbd/process_model.c b/source4/smbd/process_model.c
new file mode 100644
index 0000000000..0ae544e037
--- /dev/null
+++ b/source4/smbd/process_model.c
@@ -0,0 +1,131 @@
+/*
+ Unix SMB/CIFS implementation.
+ process model manager - main loop
+ Copyright (C) Andrew Tridgell 1992-2003
+ Copyright (C) James J Myers 2003 <myersjj@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "smbd/process_model.h"
+#include "smbd/process_model_proto.h"
+#include "param/param.h"
+
+static const struct model_ops *process_model_byname(const char *name);
+
+/*
+ setup the events for the chosen process model
+*/
+_PUBLIC_ const struct model_ops *process_model_startup(struct tevent_context *ev, const char *model)
+{
+ const struct model_ops *ops;
+
+ ops = process_model_byname(model);
+ if (!ops) {
+ DEBUG(0,("Unknown process model '%s'\n", model));
+ exit(-1);
+ }
+
+ ops->model_init(ev);
+
+ return ops;
+}
+
+/* the list of currently registered process models */
+static struct process_model {
+ struct model_ops *ops;
+} *models = NULL;
+static int num_models;
+
+/*
+ register a process model.
+
+ The 'name' can be later used by other backends to find the operations
+ structure for this backend.
+*/
+_PUBLIC_ NTSTATUS register_process_model(const void *_ops)
+{
+ const struct model_ops *ops = _ops;
+
+ if (process_model_byname(ops->name) != NULL) {
+ /* its already registered! */
+ DEBUG(0,("PROCESS_MODEL '%s' already registered\n",
+ ops->name));
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ models = realloc_p(models, struct process_model, num_models+1);
+ if (!models) {
+ smb_panic("out of memory in register_process_model");
+ }
+
+ models[num_models].ops = smb_xmemdup(ops, sizeof(*ops));
+ models[num_models].ops->name = smb_xstrdup(ops->name);
+
+ num_models++;
+
+ DEBUG(3,("PROCESS_MODEL '%s' registered\n",
+ ops->name));
+
+ return NT_STATUS_OK;
+}
+
+_PUBLIC_ NTSTATUS process_model_init(struct loadparm_context *lp_ctx)
+{
+ extern NTSTATUS process_model_thread_init(void);
+ extern NTSTATUS process_model_standard_init(void);
+ extern NTSTATUS process_model_prefork_init(void);
+ extern NTSTATUS process_model_single_init(void);
+ init_module_fn static_init[] = { STATIC_process_model_MODULES };
+ init_module_fn *shared_init = load_samba_modules(NULL, lp_ctx, "process_model");
+
+ run_init_functions(static_init);
+ run_init_functions(shared_init);
+
+ talloc_free(shared_init);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ return the operations structure for a named backend of the specified type
+*/
+static const struct model_ops *process_model_byname(const char *name)
+{
+ int i;
+
+ for (i=0;i<num_models;i++) {
+ if (strcmp(models[i].ops->name, name) == 0) {
+ return models[i].ops;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ return the PROCESS_MODEL module version, and the size of some critical types
+ This can be used by process model modules to either detect compilation errors, or provide
+ multiple implementations for different smbd compilation options in one module
+*/
+const struct process_model_critical_sizes *process_model_version(void)
+{
+ static const struct process_model_critical_sizes critical_sizes = {
+ PROCESS_MODEL_VERSION,
+ sizeof(struct model_ops)
+ };
+
+ return &critical_sizes;
+}
diff --git a/source4/smbd/process_model.h b/source4/smbd/process_model.h
new file mode 100644
index 0000000000..29f57d9df6
--- /dev/null
+++ b/source4/smbd/process_model.h
@@ -0,0 +1,85 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ process model manager - structures
+
+ Copyright (C) Andrew Tridgell 1992-2005
+ Copyright (C) James J Myers 2003 <myersjj@samba.org>
+ Copyright (C) Stefan (metze) Metzmacher 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __PROCESS_MODEL_H__
+#define __PROCESS_MODEL_H__
+
+#include "lib/socket/socket.h"
+#include "smbd/service.h"
+
+/* modules can use the following to determine if the interface has changed
+ * please increment the version number after each interface change
+ * with a comment and maybe update struct process_model_critical_sizes.
+ */
+/* version 1 - initial version - metze */
+#define PROCESS_MODEL_VERSION 1
+
+/* the process model operations structure - contains function pointers to
+ the model-specific implementations of each operation */
+struct model_ops {
+ /* the name of the process_model */
+ const char *name;
+
+ /* called at startup when the model is selected */
+ void (*model_init)(struct tevent_context *);
+
+ /* function to accept new connection */
+ void (*accept_connection)(struct tevent_context *,
+ struct loadparm_context *,
+ struct socket_context *,
+ void (*)(struct tevent_context *,
+ struct loadparm_context *,
+ struct socket_context *,
+ struct server_id , void *),
+ void *);
+
+ /* function to create a task */
+ void (*new_task)(struct tevent_context *,
+ struct loadparm_context *lp_ctx,
+ const char *service_name,
+ void (*)(struct tevent_context *,
+ struct loadparm_context *, struct server_id,
+ void *),
+ void *);
+
+ /* function to terminate a connection or task */
+ void (*terminate)(struct tevent_context *, struct loadparm_context *lp_ctx,
+ const char *reason);
+
+ /* function to set a title for the connection or task */
+ void (*set_title)(struct tevent_context *, const char *title);
+};
+
+/* this structure is used by modules to determine the size of some critical types */
+struct process_model_critical_sizes {
+ int interface_version;
+ int sizeof_model_ops;
+};
+
+extern const struct model_ops single_ops;
+
+const struct model_ops *process_model_startup(struct tevent_context *ev, const char *model);
+NTSTATUS register_process_model(const void *_ops);
+NTSTATUS process_model_init(struct loadparm_context *lp_ctx);
+
+#endif /* __PROCESS_MODEL_H__ */
diff --git a/source4/smbd/process_model.m4 b/source4/smbd/process_model.m4
new file mode 100644
index 0000000000..b2265bdac9
--- /dev/null
+++ b/source4/smbd/process_model.m4
@@ -0,0 +1,60 @@
+dnl # Server process model subsystem
+
+SMB_ENABLE(process_model_thread,NO)
+
+#################################################
+# check for pthread support
+AC_MSG_CHECKING(whether to use pthreads)
+AC_ARG_WITH(pthreads,
+[AS_HELP_STRING([--with-pthreads],[Include pthreads (default=no)])],
+[ case "$withval" in
+ yes)
+ AC_MSG_RESULT(yes)
+ if test x"$ac_cv_func_pread" != x"yes" -o x"$ac_cv_func_pwrite" != x"yes";then
+ AC_MSG_ERROR([You cannot enable threads when you don't have pread/pwrite!])
+ fi
+ SMB_ENABLE(process_model_thread,YES)
+ SMB_ENABLE(PTHREAD,YES)
+ ;;
+ *)
+ AC_MSG_RESULT(no)
+ ;;
+ esac ],
+AC_MSG_RESULT(no)
+)
+
+SMB_EXT_LIB(PTHREAD,[-lpthread])
+
+AC_MSG_CHECKING(whether to search for setproctitle support)
+AC_ARG_WITH(setproctitle,
+[AS_HELP_STRING([--with-setproctitle], [Search for setproctitle support (default=no)])],
+[ case "$withval" in
+ yes)
+ AC_MSG_RESULT(yes)
+ AC_CHECK_HEADERS(setproctitle.h)
+ AC_CHECK_FUNC(setproctitle, [], [
+ AC_CHECK_LIB_EXT(setproctitle, SETPROCTITLE_LIBS, setproctitle)
+ ])
+ AC_MSG_CHECKING(whether to use setproctitle)
+ if test x"$ac_cv_func_setproctitle" = x"yes" -o \
+ \( x"$ac_cv_header_setproctitle_h" = x"yes" -a \
+ x"$ac_cv_lib_ext_setproctitle_setproctitle" = x"yes" \) ; then
+ AC_MSG_RESULT(yes)
+ SMB_ENABLE(SETPROCTITLE, YES)
+ AC_DEFINE(HAVE_SETPROCTITLE,1,[Whether setproctitle() is available])
+ else
+ AC_MSG_RESULT(no)
+ fi
+ ;;
+ *)
+ AC_MSG_RESULT(no)
+ ;;
+ esac ],
+AC_MSG_RESULT(no)
+)
+
+SMB_EXT_LIB(SETPROCTITLE,
+ [${SETPROCTITLE_LIBS}],
+ [${SETPROCTITLE_CFLAGS}],
+ [${SETPROCTITLE_CPPFLAGS}],
+ [${SETPROCTITLE_LDFLAGS}])
diff --git a/source4/smbd/process_model.mk b/source4/smbd/process_model.mk
new file mode 100644
index 0000000000..d216edd401
--- /dev/null
+++ b/source4/smbd/process_model.mk
@@ -0,0 +1,52 @@
+# Server process model subsystem
+
+################################################
+# Start MODULE process_model_single
+[MODULE::process_model_single]
+INIT_FUNCTION = process_model_single_init
+OUTPUT_TYPE = MERGED_OBJ
+SUBSYSTEM = process_model
+# End MODULE process_model_single
+################################################
+
+process_model_single_OBJ_FILES = $(smbdsrcdir)/process_single.o
+
+################################################
+# Start MODULE process_model_standard
+[MODULE::process_model_standard]
+INIT_FUNCTION = process_model_standard_init
+SUBSYSTEM = process_model
+PRIVATE_DEPENDENCIES = LIBEVENTS SETPROCTITLE
+# End MODULE process_model_standard
+################################################
+
+process_model_standard_OBJ_FILES = $(smbdsrcdir)/process_standard.o
+
+################################################
+# Start MODULE process_model_thread
+[MODULE::process_model_thread]
+INIT_FUNCTION = process_model_thread_init
+SUBSYSTEM = process_model
+PRIVATE_DEPENDENCIES = PTHREAD
+# End MODULE process_model_thread
+################################################
+
+process_model_thread_OBJ_FILES = $(smbdsrcdir)/process_thread.o
+
+################################################
+# Start MODULE process_model_prefork
+[MODULE::process_model_prefork]
+INIT_FUNCTION = process_model_prefork_init
+SUBSYSTEM = process_model
+PRIVATE_DEPENDENCIES = LIBEVENTS SETPROCTITLE
+# End MODULE process_model_thread
+################################################
+
+process_model_prefork_OBJ_FILES = $(smbdsrcdir)/process_prefork.o
+
+[SUBSYSTEM::process_model]
+PRIVATE_DEPENDENCIES = LIBSAMBA-UTIL LIBSAMBA-HOSTCONFIG
+
+process_model_OBJ_FILES = $(smbdsrcdir)/process_model.o
+
+$(eval $(call proto_header_template,$(smbdsrcdir)/process_model_proto.h,$(process_model_OBJ_FILES:.o=.c)))
diff --git a/source4/smbd/process_prefork.c b/source4/smbd/process_prefork.c
new file mode 100644
index 0000000000..979a65482e
--- /dev/null
+++ b/source4/smbd/process_prefork.c
@@ -0,0 +1,222 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ process model: prefork (n client connections per process)
+
+ Copyright (C) Andrew Tridgell 1992-2005
+ Copyright (C) James J Myers 2003 <myersjj@samba.org>
+ Copyright (C) Stefan (metze) Metzmacher 2004
+ Copyright (C) Andrew Bartlett 2008 <abartlet@samba.org>
+ Copyright (C) David Disseldorp 2008 <ddiss@sgi.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/events/events.h"
+#include "../tdb/include/tdb.h"
+#include "lib/socket/socket.h"
+#include "smbd/process_model.h"
+#include "param/secrets.h"
+#include "system/filesys.h"
+#include "cluster/cluster.h"
+#include "param/param.h"
+
+#ifdef HAVE_SETPROCTITLE
+#ifdef HAVE_SETPROCTITLE_H
+#include <setproctitle.h>
+#endif
+#else
+#define setproctitle none_setproctitle
+static int none_setproctitle(const char *fmt, ...) PRINTF_ATTRIBUTE(1, 2);
+static int none_setproctitle(const char *fmt, ...)
+{
+ return 0;
+}
+#endif
+
+/*
+ called when the process model is selected
+*/
+static void prefork_model_init(struct tevent_context *ev)
+{
+ signal(SIGCHLD, SIG_IGN);
+}
+
+static void prefork_reload_after_fork(void)
+{
+ /* tdb needs special fork handling */
+ if (tdb_reopen_all(1) == -1) {
+ DEBUG(0,("prefork_reload_after_fork: tdb_reopen_all failed.\n"));
+ }
+
+ /* Ensure that the forked children do not expose identical random streams */
+ set_need_random_reseed();
+}
+
+/*
+ called when a listening socket becomes readable.
+*/
+static void prefork_accept_connection(struct tevent_context *ev,
+ struct loadparm_context *lp_ctx,
+ struct socket_context *listen_socket,
+ void (*new_conn)(struct tevent_context *,
+ struct loadparm_context *, struct socket_context *,
+ struct server_id , void *),
+ void *private_data)
+{
+ NTSTATUS status;
+ struct socket_context *connected_socket;
+ pid_t pid = getpid();
+
+ /* accept an incoming connection. */
+ status = socket_accept(listen_socket, &connected_socket);
+ if (!NT_STATUS_IS_OK(status)) {
+ return;
+ }
+
+ talloc_steal(private_data, connected_socket);
+
+ new_conn(ev, lp_ctx, connected_socket, cluster_id(pid, socket_get_fd(connected_socket)), private_data);
+}
+
+/*
+ called to create a new server task
+*/
+static void prefork_new_task(struct tevent_context *ev,
+ struct loadparm_context *lp_ctx,
+ const char *service_name,
+ void (*new_task_fn)(struct tevent_context *, struct loadparm_context *lp_ctx, struct server_id , void *),
+ void *private_data)
+{
+ pid_t pid;
+ int i, num_children;
+
+ struct tevent_context *ev2, *ev_parent;
+
+ pid = fork();
+
+ if (pid != 0) {
+ /* parent or error code ... go back to the event loop */
+ return;
+ }
+
+ pid = getpid();
+
+ /* This is now the child code. We need a completely new event_context to work with */
+ ev2 = s4_event_context_init(NULL);
+
+ /* the service has given us a private pointer that
+ encapsulates the context it needs for this new connection -
+ everything else will be freed */
+ talloc_steal(ev2, private_data);
+
+ /* this will free all the listening sockets and all state that
+ is not associated with this new connection */
+ talloc_free(ev);
+
+ setproctitle("task %s server_id[%d]", service_name, pid);
+
+ prefork_reload_after_fork();
+
+ /* setup this new connection: process will bind to it's sockets etc */
+ new_task_fn(ev2, lp_ctx, cluster_id(pid, 0), private_data);
+
+ num_children = lp_parm_int(lp_ctx, NULL, "prefork children", service_name, 0);
+ if (num_children == 0) {
+
+ /* We don't want any kids hanging around for this one,
+ * let the parent do all the work */
+ event_loop_wait(ev2);
+
+ talloc_free(ev2);
+ exit(0);
+ }
+
+ /* We are now free to spawn some child proccesses */
+
+ for (i=0; i < num_children; i++) {
+
+ pid = fork();
+ if (pid > 0) {
+ continue;
+ } else if (pid == -1) {
+ return;
+ } else {
+ pid = getpid();
+ setproctitle("task %s server_id[%d]", service_name, pid);
+
+ prefork_reload_after_fork();
+
+ /* we can't return to the top level here, as that event context is gone,
+ so we now process events in the new event context until there are no
+ more to process */
+ event_loop_wait(ev2);
+
+ talloc_free(ev2);
+ exit(0);
+ }
+ }
+
+ /* Don't listen on the sockets we just gave to the children */
+ talloc_free(ev2);
+
+ /* But we need a events system to handle reaping children */
+ ev_parent = s4_event_context_init(NULL);
+
+ /* TODO: Handle some events... */
+
+ /* we can't return to the top level here, as that event context is gone,
+ so we now process events in the new event context until there are no
+ more to process */
+ event_loop_wait(ev_parent);
+
+ talloc_free(ev_parent);
+ exit(0);
+
+}
+
+
+/* called when a task goes down */
+_NORETURN_ static void prefork_terminate(struct tevent_context *ev, struct loadparm_context *lp_ctx, const char *reason)
+{
+ DEBUG(2,("prefork_terminate: reason[%s]\n",reason));
+}
+
+/* called to set a title of a task or connection */
+static void prefork_set_title(struct tevent_context *ev, const char *title)
+{
+ if (title) {
+ setproctitle("%s", title);
+ } else {
+ setproctitle(NULL);
+ }
+}
+
+static const struct model_ops prefork_ops = {
+ .name = "prefork",
+ .model_init = prefork_model_init,
+ .accept_connection = prefork_accept_connection,
+ .new_task = prefork_new_task,
+ .terminate = prefork_terminate,
+ .set_title = prefork_set_title,
+};
+
+/*
+ initialise the prefork process model, registering ourselves with the process model subsystem
+ */
+NTSTATUS process_model_prefork_init(void)
+{
+ return register_process_model(&prefork_ops);
+}
diff --git a/source4/smbd/process_single.c b/source4/smbd/process_single.c
new file mode 100644
index 0000000000..738ace95c7
--- /dev/null
+++ b/source4/smbd/process_single.c
@@ -0,0 +1,124 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ process model: process (1 process handles all client connections)
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) James J Myers 2003 <myersjj@samba.org>
+ Copyright (C) Stefan (metze) Metzmacher 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "smbd/process_model.h"
+#include "system/filesys.h"
+#include "cluster/cluster.h"
+
+/*
+ called when the process model is selected
+*/
+static void single_model_init(struct tevent_context *ev)
+{
+}
+
+/*
+ called when a listening socket becomes readable.
+*/
+static void single_accept_connection(struct tevent_context *ev,
+ struct loadparm_context *lp_ctx,
+ struct socket_context *listen_socket,
+ void (*new_conn)(struct tevent_context *,
+ struct loadparm_context *,
+ struct socket_context *,
+ struct server_id , void *),
+ void *private_data)
+{
+ NTSTATUS status;
+ struct socket_context *connected_socket;
+
+ /* accept an incoming connection. */
+ status = socket_accept(listen_socket, &connected_socket);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("single_accept_connection: accept: %s\n", nt_errstr(status)));
+ /* this looks strange, but is correct.
+
+ We can only be here if woken up from select, due to
+ an incomming connection.
+
+ We need to throttle things until the system clears
+ enough resources to handle this new socket.
+
+ If we don't then we will spin filling the log and
+ causing more problems. We don't panic as this is
+ probably a temporary resource constraint */
+ sleep(1);
+ return;
+ }
+
+ talloc_steal(private_data, connected_socket);
+
+ /* The cluster_id(0, fd) cannot collide with the incrementing
+ * task below, as the first component is 0, not 1 */
+ new_conn(ev, lp_ctx, connected_socket,
+ cluster_id(0, socket_get_fd(connected_socket)), private_data);
+}
+
+/*
+ called to startup a new task
+*/
+static void single_new_task(struct tevent_context *ev,
+ struct loadparm_context *lp_ctx,
+ const char *service_name,
+ void (*new_task)(struct tevent_context *, struct loadparm_context *, struct server_id, void *),
+ void *private_data)
+{
+ static uint32_t taskid = 0;
+
+ /* We use 1 so we cannot collide in with cluster ids generated
+ * in the accept connection above, and unlikly to collide with
+ * PIDs from process modal standard (don't run samba as
+ * init) */
+ new_task(ev, lp_ctx, cluster_id(1, taskid++), private_data);
+}
+
+
+/* called when a task goes down */
+static void single_terminate(struct tevent_context *ev, struct loadparm_context *lp_ctx, const char *reason)
+{
+ DEBUG(2,("single_terminate: reason[%s]\n",reason));
+}
+
+/* called to set a title of a task or connection */
+static void single_set_title(struct tevent_context *ev, const char *title)
+{
+}
+
+const struct model_ops single_ops = {
+ .name = "single",
+ .model_init = single_model_init,
+ .new_task = single_new_task,
+ .accept_connection = single_accept_connection,
+ .terminate = single_terminate,
+ .set_title = single_set_title,
+};
+
+/*
+ initialise the single process model, registering ourselves with the
+ process model subsystem
+ */
+NTSTATUS process_model_single_init(void)
+{
+ return register_process_model(&single_ops);
+}
diff --git a/source4/smbd/process_standard.c b/source4/smbd/process_standard.c
new file mode 100644
index 0000000000..137e0a7ce0
--- /dev/null
+++ b/source4/smbd/process_standard.c
@@ -0,0 +1,239 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ process model: standard (1 process per client connection)
+
+ Copyright (C) Andrew Tridgell 1992-2005
+ Copyright (C) James J Myers 2003 <myersjj@samba.org>
+ Copyright (C) Stefan (metze) Metzmacher 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/events/events.h"
+#include "../tdb/include/tdb.h"
+#include "lib/socket/socket.h"
+#include "smbd/process_model.h"
+#include "param/secrets.h"
+#include "system/filesys.h"
+#include "cluster/cluster.h"
+#include "param/param.h"
+
+#ifdef HAVE_SETPROCTITLE
+#ifdef HAVE_SETPROCTITLE_H
+#include <setproctitle.h>
+#endif
+#else
+#define setproctitle none_setproctitle
+static int none_setproctitle(const char *fmt, ...) PRINTF_ATTRIBUTE(1, 2);
+static int none_setproctitle(const char *fmt, ...)
+{
+ return 0;
+}
+#endif
+
+/*
+ called when the process model is selected
+*/
+static void standard_model_init(struct tevent_context *ev)
+{
+ signal(SIGCHLD, SIG_IGN);
+}
+
+/*
+ called when a listening socket becomes readable.
+*/
+static void standard_accept_connection(struct tevent_context *ev,
+ struct loadparm_context *lp_ctx,
+ struct socket_context *sock,
+ void (*new_conn)(struct tevent_context *,
+ struct loadparm_context *, struct socket_context *,
+ struct server_id , void *),
+ void *private_data)
+{
+ NTSTATUS status;
+ struct socket_context *sock2;
+ pid_t pid;
+ struct tevent_context *ev2;
+ struct socket_address *c, *s;
+
+ /* accept an incoming connection. */
+ status = socket_accept(sock, &sock2);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("standard_accept_connection: accept: %s\n",
+ nt_errstr(status)));
+ /* this looks strange, but is correct. We need to throttle things until
+ the system clears enough resources to handle this new socket */
+ sleep(1);
+ return;
+ }
+
+ pid = fork();
+
+ if (pid != 0) {
+ /* parent or error code ... */
+ talloc_free(sock2);
+ /* go back to the event loop */
+ return;
+ }
+
+ pid = getpid();
+
+ /* This is now the child code. We need a completely new event_context to work with */
+ ev2 = s4_event_context_init(NULL);
+
+ /* the service has given us a private pointer that
+ encapsulates the context it needs for this new connection -
+ everything else will be freed */
+ talloc_steal(ev2, private_data);
+ talloc_steal(private_data, sock2);
+
+ /* this will free all the listening sockets and all state that
+ is not associated with this new connection */
+ talloc_free(sock);
+ talloc_free(ev);
+
+ /* we don't care if the dup fails, as its only a select()
+ speed optimisation */
+ socket_dup(sock2);
+
+ /* tdb needs special fork handling */
+ if (tdb_reopen_all(1) == -1) {
+ DEBUG(0,("standard_accept_connection: tdb_reopen_all failed.\n"));
+ }
+
+ /* Ensure that the forked children do not expose identical random streams */
+ set_need_random_reseed();
+
+ /* setup the process title */
+ c = socket_get_peer_addr(sock2, ev2);
+ s = socket_get_my_addr(sock2, ev2);
+ if (s && c) {
+ setproctitle("conn c[%s:%u] s[%s:%u] server_id[%d]",
+ c->addr, c->port, s->addr, s->port, pid);
+ }
+ talloc_free(c);
+ talloc_free(s);
+
+ /* setup this new connection. Cluster ID is PID based for this process modal */
+ new_conn(ev2, lp_ctx, sock2, cluster_id(pid, 0), private_data);
+
+ /* we can't return to the top level here, as that event context is gone,
+ so we now process events in the new event context until there are no
+ more to process */
+ event_loop_wait(ev2);
+
+ talloc_free(ev2);
+ exit(0);
+}
+
+/*
+ called to create a new server task
+*/
+static void standard_new_task(struct tevent_context *ev,
+ struct loadparm_context *lp_ctx,
+ const char *service_name,
+ void (*new_task)(struct tevent_context *, struct loadparm_context *lp_ctx, struct server_id , void *),
+ void *private_data)
+{
+ pid_t pid;
+ struct tevent_context *ev2;
+
+ pid = fork();
+
+ if (pid != 0) {
+ /* parent or error code ... go back to the event loop */
+ return;
+ }
+
+ pid = getpid();
+
+ /* This is now the child code. We need a completely new event_context to work with */
+ ev2 = s4_event_context_init(NULL);
+
+ /* the service has given us a private pointer that
+ encapsulates the context it needs for this new connection -
+ everything else will be freed */
+ talloc_steal(ev2, private_data);
+
+ /* this will free all the listening sockets and all state that
+ is not associated with this new connection */
+ talloc_free(ev);
+
+ /* tdb needs special fork handling */
+ if (tdb_reopen_all(1) == -1) {
+ DEBUG(0,("standard_accept_connection: tdb_reopen_all failed.\n"));
+ }
+
+ /* Ensure that the forked children do not expose identical random streams */
+ set_need_random_reseed();
+
+ setproctitle("task %s server_id[%d]", service_name, pid);
+
+ /* setup this new task. Cluster ID is PID based for this process modal */
+ new_task(ev2, lp_ctx, cluster_id(pid, 0), private_data);
+
+ /* we can't return to the top level here, as that event context is gone,
+ so we now process events in the new event context until there are no
+ more to process */
+ event_loop_wait(ev2);
+
+ talloc_free(ev2);
+ exit(0);
+}
+
+
+/* called when a task goes down */
+_NORETURN_ static void standard_terminate(struct tevent_context *ev, struct loadparm_context *lp_ctx,
+ const char *reason)
+{
+ DEBUG(2,("standard_terminate: reason[%s]\n",reason));
+
+ /* this reload_charcnv() has the effect of freeing the iconv context memory,
+ which makes leak checking easier */
+ reload_charcnv(lp_ctx);
+
+ talloc_free(ev);
+
+ /* terminate this process */
+ exit(0);
+}
+
+/* called to set a title of a task or connection */
+static void standard_set_title(struct tevent_context *ev, const char *title)
+{
+ if (title) {
+ setproctitle("%s", title);
+ } else {
+ setproctitle(NULL);
+ }
+}
+
+static const struct model_ops standard_ops = {
+ .name = "standard",
+ .model_init = standard_model_init,
+ .accept_connection = standard_accept_connection,
+ .new_task = standard_new_task,
+ .terminate = standard_terminate,
+ .set_title = standard_set_title,
+};
+
+/*
+ initialise the standard process model, registering ourselves with the process model subsystem
+ */
+NTSTATUS process_model_standard_init(void)
+{
+ return register_process_model(&standard_ops);
+}
diff --git a/source4/smbd/process_thread.c b/source4/smbd/process_thread.c
new file mode 100644
index 0000000000..5be6374c14
--- /dev/null
+++ b/source4/smbd/process_thread.c
@@ -0,0 +1,572 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ thread model: standard (1 thread per client connection)
+
+ Copyright (C) Andrew Tridgell 2003-2005
+ Copyright (C) James J Myers 2003 <myersjj@samba.org>
+ Copyright (C) Stefan (metze) Metzmacher 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "version.h"
+#include <pthread.h>
+#ifdef HAVE_BACKTRACE
+#include <execinfo.h>
+#endif
+#include "system/wait.h"
+#include "system/filesys.h"
+#include "lib/events/events.h"
+#include "lib/util/dlinklist.h"
+#include "lib/util/mutex.h"
+#include "smbd/process_model.h"
+
+static pthread_key_t title_key;
+
+struct new_conn_state {
+ struct tevent_context *ev;
+ struct socket_context *sock;
+ struct loadparm_context *lp_ctx;
+ void (*new_conn)(struct tevent_context *, struct loadparm_context *lp_ctx, struct socket_context *, uint32_t , void *);
+ void *private_data;
+};
+
+static void *thread_connection_fn(void *thread_parm)
+{
+ struct new_conn_state *new_conn = talloc_get_type(thread_parm, struct new_conn_state);
+
+ new_conn->new_conn(new_conn->ev, new_conn->lp_ctx, new_conn->sock, pthread_self(), new_conn->private_data);
+
+ /* run this connection from here */
+ event_loop_wait(new_conn->ev);
+
+ talloc_free(new_conn);
+
+ return NULL;
+}
+
+/*
+ called when a listening socket becomes readable
+*/
+static void thread_accept_connection(struct tevent_context *ev,
+ struct loadparm_context *lp_ctx,
+ struct socket_context *sock,
+ void (*new_conn)(struct tevent_context *,
+ struct loadparm_context *,
+ struct socket_context *,
+ uint32_t , void *),
+ void *private_data)
+{
+ NTSTATUS status;
+ int rc;
+ pthread_t thread_id;
+ pthread_attr_t thread_attr;
+ struct new_conn_state *state;
+ struct tevent_context *ev2;
+
+ ev2 = s4_event_context_init(ev);
+ if (ev2 == NULL) return;
+
+ state = talloc(ev2, struct new_conn_state);
+ if (state == NULL) {
+ talloc_free(ev2);
+ return;
+ }
+
+ state->new_conn = new_conn;
+ state->private_data = private_data;
+ state->lp_ctx = lp_ctx;
+ state->ev = ev2;
+
+ /* accept an incoming connection. */
+ status = socket_accept(sock, &state->sock);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(ev2);
+ /* We need to throttle things until the system clears
+ enough resources to handle this new socket. If we
+ don't then we will spin filling the log and causing
+ more problems. We don't panic as this is probably a
+ temporary resource constraint */
+ sleep(1);
+ return;
+ }
+
+ talloc_steal(state, state->sock);
+
+ pthread_attr_init(&thread_attr);
+ pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
+ rc = pthread_create(&thread_id, &thread_attr, thread_connection_fn, state);
+ pthread_attr_destroy(&thread_attr);
+ if (rc == 0) {
+ DEBUG(4,("accept_connection_thread: created thread_id=%lu for fd=%d\n",
+ (unsigned long int)thread_id, socket_get_fd(sock)));
+ } else {
+ DEBUG(0,("accept_connection_thread: thread create failed for fd=%d, rc=%d\n", socket_get_fd(sock), rc));
+ talloc_free(ev2);
+ }
+}
+
+
+struct new_task_state {
+ struct tevent_context *ev;
+ struct loadparm_context *lp_ctx;
+ void (*new_task)(struct tevent_context *, struct loadparm_context *,
+ uint32_t , void *);
+ void *private_data;
+};
+
+static void *thread_task_fn(void *thread_parm)
+{
+ struct new_task_state *new_task = talloc_get_type(thread_parm, struct new_task_state);
+
+ new_task->new_task(new_task->ev, new_task->lp_ctx, pthread_self(),
+ new_task->private_data);
+
+ /* run this connection from here */
+ event_loop_wait(new_task->ev);
+
+ talloc_free(new_task);
+
+ return NULL;
+}
+
+/*
+ called when a new task is needed
+*/
+static void thread_new_task(struct tevent_context *ev,
+ struct loadparm_context *lp_ctx,
+ const char *service_name,
+ void (*new_task)(struct tevent_context *,
+ struct loadparm_context *,
+ uint32_t , void *),
+ void *private_data)
+{
+ int rc;
+ pthread_t thread_id;
+ pthread_attr_t thread_attr;
+ struct new_task_state *state;
+ struct tevent_context *ev2;
+
+ ev2 = s4_event_context_init(ev);
+ if (ev2 == NULL) return;
+
+ state = talloc(ev2, struct new_task_state);
+ if (state == NULL) {
+ talloc_free(ev2);
+ return;
+ }
+
+ state->new_task = new_task;
+ state->lp_ctx = lp_ctx;
+ state->private_data = private_data;
+ state->ev = ev2;
+
+ pthread_attr_init(&thread_attr);
+ pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
+ rc = pthread_create(&thread_id, &thread_attr, thread_task_fn, state);
+ pthread_attr_destroy(&thread_attr);
+ if (rc == 0) {
+ DEBUG(4,("thread_new_task: created %s thread_id=%lu\n",
+ service_name, (unsigned long int)thread_id));
+ } else {
+ DEBUG(0,("thread_new_task: thread create for %s failed rc=%d\n", service_name, rc));
+ talloc_free(ev2);
+ }
+}
+
+/* called when a task goes down */
+static void thread_terminate(struct tevent_context *event_ctx, struct loadparm_context *lp_ctx, const char *reason)
+{
+ DEBUG(10,("thread_terminate: reason[%s]\n",reason));
+
+ talloc_free(event_ctx);
+
+ /* terminate this thread */
+ pthread_exit(NULL); /* thread cleanup routine will do actual cleanup */
+}
+
+/* called to set a title of a task or connection */
+static void thread_set_title(struct tevent_context *ev, const char *title)
+{
+ char *old_title;
+ char *new_title;
+
+ old_title = pthread_getspecific(title_key);
+ talloc_free(old_title);
+
+ new_title = talloc_strdup(ev, title);
+ pthread_setspecific(title_key, new_title);
+}
+
+/*
+ mutex init function for thread model
+*/
+static int thread_mutex_init(smb_mutex_t *mutex, const char *name)
+{
+ pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
+ mutex->mutex = memdup(&m, sizeof(m));
+ if (! mutex->mutex) {
+ errno = ENOMEM;
+ return -1;
+ }
+ return pthread_mutex_init((pthread_mutex_t *)mutex->mutex, NULL);
+}
+
+/*
+ mutex destroy function for thread model
+*/
+static int thread_mutex_destroy(smb_mutex_t *mutex, const char *name)
+{
+ return pthread_mutex_destroy((pthread_mutex_t *)mutex->mutex);
+}
+
+static void mutex_start_timer(struct timeval *tp1)
+{
+ gettimeofday(tp1,NULL);
+}
+
+static double mutex_end_timer(struct timeval tp1)
+{
+ struct timeval tp2;
+ gettimeofday(&tp2,NULL);
+ return((tp2.tv_sec - tp1.tv_sec) +
+ (tp2.tv_usec - tp1.tv_usec)*1.0e-6);
+}
+
+/*
+ mutex lock function for thread model
+*/
+static int thread_mutex_lock(smb_mutex_t *mutexP, const char *name)
+{
+ pthread_mutex_t *mutex = (pthread_mutex_t *)mutexP->mutex;
+ int rc;
+ double t;
+ struct timeval tp1;
+ /* Test below is ONLY for debugging */
+ if ((rc = pthread_mutex_trylock(mutex))) {
+ if (rc == EBUSY) {
+ mutex_start_timer(&tp1);
+ printf("mutex lock: thread %d, lock %s not available\n",
+ (uint32_t)pthread_self(), name);
+ print_suspicious_usage("mutex_lock", name);
+ pthread_mutex_lock(mutex);
+ t = mutex_end_timer(tp1);
+ printf("mutex lock: thread %d, lock %s now available, waited %g seconds\n",
+ (uint32_t)pthread_self(), name, t);
+ return 0;
+ }
+ printf("mutex lock: thread %d, lock %s failed rc=%d\n",
+ (uint32_t)pthread_self(), name, rc);
+ SMB_ASSERT(errno == 0); /* force error */
+ }
+ return 0;
+}
+
+/*
+ mutex unlock for thread model
+*/
+static int thread_mutex_unlock(smb_mutex_t *mutex, const char *name)
+{
+ return pthread_mutex_unlock((pthread_mutex_t *)mutex->mutex);
+}
+
+/*****************************************************************
+ Read/write lock routines.
+*****************************************************************/
+/*
+ rwlock init function for thread model
+*/
+static int thread_rwlock_init(smb_rwlock_t *rwlock, const char *name)
+{
+ pthread_rwlock_t m = PTHREAD_RWLOCK_INITIALIZER;
+ rwlock->rwlock = memdup(&m, sizeof(m));
+ if (! rwlock->rwlock) {
+ errno = ENOMEM;
+ return -1;
+ }
+ return pthread_rwlock_init((pthread_rwlock_t *)rwlock->rwlock, NULL);
+}
+
+/*
+ rwlock destroy function for thread model
+*/
+static int thread_rwlock_destroy(smb_rwlock_t *rwlock, const char *name)
+{
+ return pthread_rwlock_destroy((pthread_rwlock_t *)rwlock->rwlock);
+}
+
+/*
+ rwlock lock for read function for thread model
+*/
+static int thread_rwlock_lock_read(smb_rwlock_t *rwlockP, const char *name)
+{
+ pthread_rwlock_t *rwlock = (pthread_rwlock_t *)rwlockP->rwlock;
+ int rc;
+ double t;
+ struct timeval tp1;
+ /* Test below is ONLY for debugging */
+ if ((rc = pthread_rwlock_tryrdlock(rwlock))) {
+ if (rc == EBUSY) {
+ mutex_start_timer(&tp1);
+ printf("rwlock lock_read: thread %d, lock %s not available\n",
+ (uint32_t)pthread_self(), name);
+ print_suspicious_usage("rwlock_lock_read", name);
+ pthread_rwlock_rdlock(rwlock);
+ t = mutex_end_timer(tp1);
+ printf("rwlock lock_read: thread %d, lock %s now available, waited %g seconds\n",
+ (uint32_t)pthread_self(), name, t);
+ return 0;
+ }
+ printf("rwlock lock_read: thread %d, lock %s failed rc=%d\n",
+ (uint32_t)pthread_self(), name, rc);
+ SMB_ASSERT(errno == 0); /* force error */
+ }
+ return 0;
+}
+
+/*
+ rwlock lock for write function for thread model
+*/
+static int thread_rwlock_lock_write(smb_rwlock_t *rwlockP, const char *name)
+{
+ pthread_rwlock_t *rwlock = (pthread_rwlock_t *)rwlockP->rwlock;
+ int rc;
+ double t;
+ struct timeval tp1;
+ /* Test below is ONLY for debugging */
+ if ((rc = pthread_rwlock_trywrlock(rwlock))) {
+ if (rc == EBUSY) {
+ mutex_start_timer(&tp1);
+ printf("rwlock lock_write: thread %d, lock %s not available\n",
+ (uint32_t)pthread_self(), name);
+ print_suspicious_usage("rwlock_lock_write", name);
+ pthread_rwlock_wrlock(rwlock);
+ t = mutex_end_timer(tp1);
+ printf("rwlock lock_write: thread %d, lock %s now available, waited %g seconds\n",
+ (uint32_t)pthread_self(), name, t);
+ return 0;
+ }
+ printf("rwlock lock_write: thread %d, lock %s failed rc=%d\n",
+ (uint32_t)pthread_self(), name, rc);
+ SMB_ASSERT(errno == 0); /* force error */
+ }
+ return 0;
+}
+
+
+/*
+ rwlock unlock for thread model
+*/
+static int thread_rwlock_unlock(smb_rwlock_t *rwlock, const char *name)
+{
+ return pthread_rwlock_unlock((pthread_rwlock_t *)rwlock->rwlock);
+}
+
+/*****************************************************************
+ Log suspicious usage (primarily for possible thread-unsafe behavior).
+*****************************************************************/
+static void thread_log_suspicious_usage(const char* from, const char* info)
+{
+ DEBUG(1,("log_suspicious_usage: from %s info='%s'\n", from, info));
+#ifdef HAVE_BACKTRACE
+ {
+ void *addresses[10];
+ int num_addresses = backtrace(addresses, 8);
+ char **bt_symbols = backtrace_symbols(addresses, num_addresses);
+ int i;
+
+ if (bt_symbols) {
+ for (i=0; i<num_addresses; i++) {
+ DEBUG(1,("log_suspicious_usage: %s%s\n", DEBUGTAB(1), bt_symbols[i]));
+ }
+ free(bt_symbols);
+ }
+ }
+#endif
+}
+
+/*****************************************************************
+ Log suspicious usage to stdout (primarily for possible thread-unsafe behavior.
+ Used in mutex code where DEBUG calls would cause recursion.
+*****************************************************************/
+static void thread_print_suspicious_usage(const char* from, const char* info)
+{
+ printf("log_suspicious_usage: from %s info='%s'\n", from, info);
+#ifdef HAVE_BACKTRACE
+ {
+ void *addresses[10];
+ int num_addresses = backtrace(addresses, 8);
+ char **bt_symbols = backtrace_symbols(addresses, num_addresses);
+ int i;
+
+ if (bt_symbols) {
+ for (i=0; i<num_addresses; i++) {
+ printf("log_suspicious_usage: %s%s\n", DEBUGTAB(1), bt_symbols[i]);
+ }
+ free(bt_symbols);
+ }
+ }
+#endif
+}
+
+static uint32_t thread_get_task_id(void)
+{
+ return (uint32_t)pthread_self();
+}
+
+static void thread_log_task_id(int fd)
+{
+ char *s= NULL;
+
+ asprintf(&s, "thread[%u][%s]:\n",
+ (uint32_t)pthread_self(),
+ (const char *)pthread_getspecific(title_key));
+ if (!s) return;
+ write(fd, s, strlen(s));
+ free(s);
+}
+
+/****************************************************************************
+catch serious errors
+****************************************************************************/
+static void thread_sig_fault(int sig)
+{
+ DEBUG(0,("===============================================================\n"));
+ DEBUG(0,("TERMINAL ERROR: Recursive signal %d in thread [%u][%s] (%s)\n",
+ sig,(uint32_t)pthread_self(),
+ (const char *)pthread_getspecific(title_key),
+ SAMBA_VERSION_STRING));
+ DEBUG(0,("===============================================================\n"));
+ exit(1); /* kill the whole server for now */
+}
+
+/*******************************************************************
+setup our recursive fault handlers
+********************************************************************/
+static void thread_fault_setup(void)
+{
+#ifdef SIGSEGV
+ CatchSignal(SIGSEGV,SIGNAL_CAST thread_sig_fault);
+#endif
+#ifdef SIGBUS
+ CatchSignal(SIGBUS,SIGNAL_CAST thread_sig_fault);
+#endif
+#ifdef SIGABRT
+ CatchSignal(SIGABRT,SIGNAL_CAST thread_sig_fault);
+#endif
+}
+
+/*******************************************************************
+report a fault in a thread
+********************************************************************/
+static void thread_fault_handler(int sig)
+{
+ static int counter;
+
+ /* try to catch recursive faults */
+ thread_fault_setup();
+
+ counter++; /* count number of faults that have occurred */
+
+ DEBUG(0,("===============================================================\n"));
+ DEBUG(0,("INTERNAL ERROR: Signal %d in thread [%u] [%s] (%s)\n",
+ sig,(uint32_t)pthread_self(),
+ (const char *)pthread_getspecific(title_key),
+ SAMBA_VERSION_STRING));
+ DEBUG(0,("Please read the file BUGS.txt in the distribution\n"));
+ DEBUG(0,("===============================================================\n"));
+#ifdef HAVE_BACKTRACE
+ {
+ void *addresses[10];
+ int num_addresses = backtrace(addresses, 8);
+ char **bt_symbols = backtrace_symbols(addresses, num_addresses);
+ int i;
+
+ if (bt_symbols) {
+ for (i=0; i<num_addresses; i++) {
+ DEBUG(1,("fault_report: %s%s\n", DEBUGTAB(1), bt_symbols[i]));
+ }
+ free(bt_symbols);
+ }
+ }
+#endif
+ pthread_exit(NULL); /* terminate failing thread only */
+}
+
+/*
+ called when the process model is selected
+*/
+static void thread_model_init(struct tevent_context *event_context)
+{
+ struct mutex_ops m_ops;
+ struct debug_ops d_ops;
+
+ ZERO_STRUCT(m_ops);
+ ZERO_STRUCT(d_ops);
+
+ pthread_key_create(&title_key, NULL);
+ pthread_setspecific(title_key, talloc_strdup(event_context, ""));
+
+ /* register mutex/rwlock handlers */
+ m_ops.mutex_init = thread_mutex_init;
+ m_ops.mutex_lock = thread_mutex_lock;
+ m_ops.mutex_unlock = thread_mutex_unlock;
+ m_ops.mutex_destroy = thread_mutex_destroy;
+
+ m_ops.rwlock_init = thread_rwlock_init;
+ m_ops.rwlock_lock_write = thread_rwlock_lock_write;
+ m_ops.rwlock_lock_read = thread_rwlock_lock_read;
+ m_ops.rwlock_unlock = thread_rwlock_unlock;
+ m_ops.rwlock_destroy = thread_rwlock_destroy;
+
+ register_mutex_handlers("thread", &m_ops);
+
+ register_fault_handler("thread", thread_fault_handler);
+
+ d_ops.log_suspicious_usage = thread_log_suspicious_usage;
+ d_ops.print_suspicious_usage = thread_print_suspicious_usage;
+ d_ops.get_task_id = thread_get_task_id;
+ d_ops.log_task_id = thread_log_task_id;
+
+ register_debug_handlers("thread", &d_ops);
+}
+
+
+static const struct model_ops thread_ops = {
+ .name = "thread",
+ .model_init = thread_model_init,
+ .accept_connection = thread_accept_connection,
+ .new_task = thread_new_task,
+ .terminate = thread_terminate,
+ .set_title = thread_set_title,
+};
+
+/*
+ initialise the thread process model, registering ourselves with the model subsystem
+ */
+NTSTATUS process_model_thread_init(void)
+{
+ NTSTATUS ret;
+
+ /* register ourselves with the PROCESS_MODEL subsystem. */
+ ret = register_process_model(&thread_ops);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("Failed to register process_model 'thread'!\n"));
+ return ret;
+ }
+
+ return ret;
+}
diff --git a/source4/smbd/samba.8.xml b/source4/smbd/samba.8.xml
new file mode 100644
index 0000000000..e1ec8cabd5
--- /dev/null
+++ b/source4/smbd/samba.8.xml
@@ -0,0 +1,178 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE refentry PUBLIC "-//Samba-Team//DTD DocBook V4.2-Based Variant V1.0//EN" "http://www.samba.org/samba/DTD/samba-doc">
+<refentry id="samba.8">
+
+<refmeta>
+ <refentrytitle>samba</refentrytitle>
+ <manvolnum>8</manvolnum>
+</refmeta>
+
+
+<refnamediv>
+ <refname>samba</refname>
+ <refpurpose>server to provide filesharing- and directory services to clients</refpurpose>
+</refnamediv>
+
+<refsynopsisdiv>
+ <cmdsynopsis>
+ <command>samba</command>
+ <arg choice="opt">-i</arg>
+ <arg choice="opt">-M model</arg>
+ </cmdsynopsis>
+</refsynopsisdiv>
+
+<refsect1>
+ <title>DESCRIPTION</title>
+ <para>This program is part of the <citerefentry><refentrytitle>samba</refentrytitle>
+ <manvolnum>7</manvolnum></citerefentry> suite.</para>
+
+ <para><command>samba</command> is the server daemon that
+ provides filesharing and directory services to Windows clients.
+ The server provides filespace and directory services to
+ clients using the SMB (or CIFS) protocol and other
+ related protocols such as DCE/RPC, LDAP and Kerberos.
+ </para>
+
+ <para>
+ Clients supported include MSCLIENT 3.0 for DOS, Windows for
+ Workgroups, Windows 95/98/ME, Windows NT, Windows 2000/XP/2003,
+ OS/2, DAVE for Macintosh, and cifsfs for Linux.</para>
+
+ <para>An extensive description of the services that the
+ server can provide is given in the man page for the
+ configuration file controlling the attributes of those
+ services (see <citerefentry><refentrytitle>smb.conf</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry>. This man page will not describe the
+ services, but will concentrate on the administrative aspects
+ of running the server.</para>
+
+ <para>Please note that there are significant security
+ implications to running this server, and the <citerefentry><refentrytitle>smb.conf</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> manual page should be regarded as mandatory reading before
+ proceeding with installation.</para>
+
+ <para>As of Samba 4, there is a single daemon that incorporates the
+ functionality of both smbd and nmbd that are present in older versions
+ of Samba.</para>
+
+</refsect1>
+
+<refsect1>
+ <title>OPTIONS</title>
+
+ <variablelist>
+ <varlistentry>
+ <term>-i</term>
+ <listitem><para>If this parameter is specified it causes the
+ server to run "interactively", not as a daemon, even if the
+ server is executed on the command line of a shell. Setting this
+ parameter negates the implicit deamon mode when run from the
+ command line. <command>samba</command> also logs to standard
+ output, as if the <command>-S</command> parameter had been
+ given.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-M model</term>
+ <listitem><para>This parameter can be used to specify the
+ &quot;process model&quot; samba should use. This determines
+ how concurrent clients are handled. Available process
+ models include <emphasis>single</emphasis> (everything in
+ a single process), <emphasis>standard</emphasis> (similar
+ behaviour to that of Samba 3), <emphasis>thread</emphasis>
+ (single process, different threads.
+ </para></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect1>
+
+<refsect1>
+ <title>FILES</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><filename>/etc/rc</filename></term>
+ <listitem><para>or whatever initialization script your
+ system uses).</para>
+
+ <para>If running the server as a daemon at startup,
+ this file will need to contain an appropriate startup
+ sequence for the server. </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><filename>/etc/services</filename></term>
+ <listitem><para>If running the server via the
+ meta-daemon <command>inetd</command>, this file
+ must contain a mapping of service name (e.g., netbios-ssn)
+ to service port (e.g., 139) and protocol type (e.g., tcp).
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><filename>/usr/local/samba/lib/smb.conf</filename></term>
+ <listitem><para>This is the default location of the <citerefentry><refentrytitle>smb.conf</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> server configuration file. Other common places that systems
+ install this file are <filename>/usr/samba/lib/smb.conf</filename>
+ and <filename>/etc/samba/smb.conf</filename>.</para>
+
+ <para>This file describes all the services the server
+ is to make available to clients. See <citerefentry><refentrytitle>smb.conf</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for more information.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+</refsect1>
+
+<refsect1>
+ <title>VERSION</title>
+
+ <para>This man page is correct for version 4 of
+ the Samba suite.</para>
+</refsect1>
+
+<refsect1>
+ <title>DIAGNOSTICS</title>
+
+ <para>Most diagnostics issued by the server are logged
+ in a specified log file. The log file name is specified
+ at compile time, but may be overridden on the command line.</para>
+
+ <para>The number and nature of diagnostics available depends
+ on the debug level used by the server. If you have problems, set
+ the debug level to 3 and peruse the log files.</para>
+
+ <para>Most messages are reasonably self-explanatory. Unfortunately,
+ at the time this man page was created, there are too many diagnostics
+ available in the source code to warrant describing each and every
+ diagnostic. At this stage your best bet is still to grep the
+ source code and inspect the conditions that gave rise to the
+ diagnostics you are seeing.</para>
+</refsect1>
+
+<refsect1>
+ <title>SEE ALSO</title>
+ <para><citerefentry><refentrytitle>hosts_access</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry>
+ <citerefentry><refentrytitle>smb.conf</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry>, <citerefentry><refentrytitle>smbclient</refentrytitle>
+ <manvolnum>1</manvolnum></citerefentry>, <citerefentry><refentrytitle>testparm</refentrytitle>
+ <manvolnum>1</manvolnum></citerefentry>, and the
+ Internet RFC's <filename>rfc1001.txt</filename>, <filename>rfc1002.txt</filename>.
+ In addition the CIFS (formerly SMB) specification is available
+ as a link from the Web page <ulink noescape="1" url="http://samba.org/cifs/">
+ http://samba.org/cifs/</ulink>.</para>
+</refsect1>
+
+<refsect1>
+ <title>AUTHOR</title>
+
+ <para>The original Samba software and related utilities
+ were created by Andrew Tridgell. Samba is now developed
+ by the Samba Team as an Open Source project similar
+ to the way the Linux kernel is developed.</para>
+
+</refsect1>
+
+</refentry>
diff --git a/source4/smbd/server.c b/source4/smbd/server.c
new file mode 100644
index 0000000000..d576782ab1
--- /dev/null
+++ b/source4/smbd/server.c
@@ -0,0 +1,369 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Main SMB server routines
+
+ Copyright (C) Andrew Tridgell 1992-2005
+ Copyright (C) Martin Pool 2002
+ Copyright (C) Jelmer Vernooij 2002
+ Copyright (C) James J Myers 2003 <myersjj@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/events/events.h"
+#include "version.h"
+#include "lib/cmdline/popt_common.h"
+#include "system/dir.h"
+#include "system/filesys.h"
+#include "ldb/include/ldb.h"
+#include "registry/registry.h"
+#include "ntvfs/ntvfs.h"
+#include "ntptr/ntptr.h"
+#include "auth/gensec/gensec.h"
+#include "smbd/process_model.h"
+#include "smbd/service.h"
+#include "param/secrets.h"
+#include "smbd/pidfile.h"
+#include "param/param.h"
+
+/*
+ recursively delete a directory tree
+*/
+static void recursive_delete(const char *path)
+{
+ DIR *dir;
+ struct dirent *de;
+
+ dir = opendir(path);
+ if (!dir) {
+ return;
+ }
+
+ for (de=readdir(dir);de;de=readdir(dir)) {
+ char *fname;
+ struct stat st;
+
+ if (ISDOT(de->d_name) || ISDOTDOT(de->d_name)) {
+ continue;
+ }
+
+ fname = talloc_asprintf(path, "%s/%s", path, de->d_name);
+ if (stat(fname, &st) != 0) {
+ continue;
+ }
+ if (S_ISDIR(st.st_mode)) {
+ recursive_delete(fname);
+ talloc_free(fname);
+ continue;
+ }
+ if (unlink(fname) != 0) {
+ DEBUG(0,("Unabled to delete '%s' - %s\n",
+ fname, strerror(errno)));
+ smb_panic("unable to cleanup tmp files");
+ }
+ talloc_free(fname);
+ }
+ closedir(dir);
+}
+
+/*
+ cleanup temporary files. This is the new alternative to
+ TDB_CLEAR_IF_FIRST. Unfortunately TDB_CLEAR_IF_FIRST is not
+ efficient on unix systems due to the lack of scaling of the byte
+ range locking system. So instead of putting the burden on tdb to
+ cleanup tmp files, this function deletes them.
+*/
+static void cleanup_tmp_files(struct loadparm_context *lp_ctx)
+{
+ char *path;
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+
+ path = smbd_tmp_path(mem_ctx, lp_ctx, NULL);
+
+ recursive_delete(path);
+ talloc_free(mem_ctx);
+}
+
+static void sig_hup(int sig)
+{
+ debug_schedule_reopen_logs();
+}
+
+static void sig_term(int sig)
+{
+#if HAVE_GETPGRP
+ static int done_sigterm;
+ if (done_sigterm == 0 && getpgrp() == getpid()) {
+ DEBUG(0,("SIGTERM: killing children\n"));
+ done_sigterm = 1;
+ kill(-getpgrp(), SIGTERM);
+ }
+#endif
+ exit(0);
+}
+
+/*
+ setup signal masks
+*/
+static void setup_signals(void)
+{
+ /* we are never interested in SIGPIPE */
+ BlockSignals(true,SIGPIPE);
+
+#if defined(SIGFPE)
+ /* we are never interested in SIGFPE */
+ BlockSignals(true,SIGFPE);
+#endif
+
+ /* We are no longer interested in USR1 */
+ BlockSignals(true, SIGUSR1);
+
+#if defined(SIGUSR2)
+ /* We are no longer interested in USR2 */
+ BlockSignals(true,SIGUSR2);
+#endif
+
+ /* POSIX demands that signals are inherited. If the invoking process has
+ * these signals masked, we will have problems, as we won't recieve them. */
+ BlockSignals(false, SIGHUP);
+ BlockSignals(false, SIGTERM);
+
+ CatchSignal(SIGHUP, sig_hup);
+ CatchSignal(SIGTERM, sig_term);
+}
+
+/*
+ handle io on stdin
+*/
+static void server_stdin_handler(struct tevent_context *event_ctx, struct tevent_fd *fde,
+ uint16_t flags, void *private_data)
+{
+ const char *binary_name = (const char *)private_data;
+ uint8_t c;
+ if (read(0, &c, 1) == 0) {
+ DEBUG(0,("%s: EOF on stdin - terminating\n", binary_name));
+#if HAVE_GETPGRP
+ if (getpgrp() == getpid()) {
+ kill(-getpgrp(), SIGTERM);
+ }
+#endif
+ exit(0);
+ }
+}
+
+/*
+ die if the user selected maximum runtime is exceeded
+*/
+_NORETURN_ static void max_runtime_handler(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval t, void *private_data)
+{
+ const char *binary_name = (const char *)private_data;
+ DEBUG(0,("%s: maximum runtime exceeded - terminating\n", binary_name));
+ exit(0);
+}
+
+/*
+ main server.
+*/
+static int binary_smbd_main(const char *binary_name, int argc, const char *argv[])
+{
+ bool opt_daemon = false;
+ bool opt_interactive = false;
+ int opt;
+ poptContext pc;
+ extern NTSTATUS server_service_wrepl_init(void);
+ extern NTSTATUS server_service_kdc_init(void);
+ extern NTSTATUS server_service_ldap_init(void);
+ extern NTSTATUS server_service_web_init(void);
+ extern NTSTATUS server_service_ldap_init(void);
+ extern NTSTATUS server_service_winbind_init(void);
+ extern NTSTATUS server_service_nbtd_init(void);
+ extern NTSTATUS server_service_auth_init(void);
+ extern NTSTATUS server_service_cldapd_init(void);
+ extern NTSTATUS server_service_smb_init(void);
+ extern NTSTATUS server_service_drepl_init(void);
+ extern NTSTATUS server_service_rpc_init(void);
+ extern NTSTATUS server_service_ntp_signd_init(void);
+ extern NTSTATUS server_service_samba3_smb_init(void);
+ init_module_fn static_init[] = { STATIC_service_MODULES };
+ init_module_fn *shared_init;
+ struct tevent_context *event_ctx;
+ uint16_t stdin_event_flags;
+ NTSTATUS status;
+ const char *model = "standard";
+ int max_runtime = 0;
+ enum {
+ OPT_DAEMON = 1000,
+ OPT_INTERACTIVE,
+ OPT_PROCESS_MODEL
+ };
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {"daemon", 'D', POPT_ARG_NONE, NULL, OPT_DAEMON,
+ "Become a daemon (default)", NULL },
+ {"interactive", 'i', POPT_ARG_NONE, NULL, OPT_INTERACTIVE,
+ "Run interactive (not a daemon)", NULL},
+ {"model", 'M', POPT_ARG_STRING, NULL, OPT_PROCESS_MODEL,
+ "Select process model", "MODEL"},
+ {"maximum-runtime",0, POPT_ARG_INT, &max_runtime, 0,
+ "set maximum runtime of the server process, till autotermination", "seconds"},
+ POPT_COMMON_SAMBA
+ POPT_COMMON_VERSION
+ { NULL }
+ };
+
+ pc = poptGetContext(binary_name, argc, argv, long_options, 0);
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch(opt) {
+ case OPT_DAEMON:
+ opt_daemon = true;
+ break;
+ case OPT_INTERACTIVE:
+ opt_interactive = true;
+ break;
+ case OPT_PROCESS_MODEL:
+ model = poptGetOptArg(pc);
+ break;
+ default:
+ fprintf(stderr, "\nInvalid option %s: %s\n\n",
+ poptBadOption(pc, 0), poptStrerror(opt));
+ poptPrintUsage(pc, stderr, 0);
+ exit(1);
+ }
+ }
+
+ if (opt_daemon && opt_interactive) {
+ fprintf(stderr,"\nERROR: "
+ "Option -i|--interactive is not allowed together with -D|--daemon\n\n");
+ poptPrintUsage(pc, stderr, 0);
+ exit(1);
+ } else if (!opt_interactive) {
+ /* default is --daemon */
+ opt_daemon = true;
+ }
+
+ poptFreeContext(pc);
+
+ setup_logging(binary_name, opt_interactive?DEBUG_STDOUT:DEBUG_FILE);
+ setup_signals();
+
+ /* we want total control over the permissions on created files,
+ so set our umask to 0 */
+ umask(0);
+
+ DEBUG(0,("%s version %s started.\n", binary_name, SAMBA_VERSION_STRING));
+ DEBUGADD(0,("Copyright Andrew Tridgell and the Samba Team 1992-2009\n"));
+
+ if (sizeof(uint16_t) < 2 || sizeof(uint32_t) < 4 || sizeof(uint64_t) < 8) {
+ DEBUG(0,("ERROR: Samba is not configured correctly for the word size on your machine\n"));
+ DEBUGADD(0,("sizeof(uint16_t) = %u, sizeof(uint32_t) %u, sizeof(uint64_t) = %u\n",
+ (unsigned int)sizeof(uint16_t), (unsigned int)sizeof(uint32_t), (unsigned int)sizeof(uint64_t)));
+ exit(1);
+ }
+
+ if (opt_daemon) {
+ DEBUG(3,("Becoming a daemon.\n"));
+ become_daemon(true, false);
+ }
+
+ cleanup_tmp_files(cmdline_lp_ctx);
+
+ if (!directory_exist(lp_lockdir(cmdline_lp_ctx))) {
+ mkdir(lp_lockdir(cmdline_lp_ctx), 0755);
+ }
+
+ pidfile_create(lp_piddir(cmdline_lp_ctx), binary_name);
+
+ /* Do *not* remove this, until you have removed
+ * passdb/secrets.c, and proved that Samba still builds... */
+ /* Setup the SECRETS subsystem */
+ if (secrets_init(talloc_autofree_context(), cmdline_lp_ctx) == NULL) {
+ exit(1);
+ }
+
+ gensec_init(cmdline_lp_ctx); /* FIXME: */
+
+ ntptr_init(cmdline_lp_ctx); /* FIXME: maybe run this in the initialization function
+ of the spoolss RPC server instead? */
+
+ ntvfs_init(cmdline_lp_ctx); /* FIXME: maybe run this in the initialization functions
+ of the SMB[,2] server instead? */
+
+ process_model_init(cmdline_lp_ctx);
+
+ shared_init = load_samba_modules(NULL, cmdline_lp_ctx, "service");
+
+ run_init_functions(static_init);
+ run_init_functions(shared_init);
+
+ talloc_free(shared_init);
+
+ /* the event context is the top level structure in smbd. Everything else
+ should hang off that */
+ event_ctx = s4_event_context_init(talloc_autofree_context());
+
+ if (event_ctx == NULL) {
+ DEBUG(0,("Initializing event context failed\n"));
+ return 1;
+ }
+
+ if (opt_interactive) {
+ /* terminate when stdin goes away */
+ stdin_event_flags = TEVENT_FD_READ;
+ } else {
+ /* stay alive forever */
+ stdin_event_flags = 0;
+ }
+
+ /* catch EOF on stdin */
+#ifdef SIGTTIN
+ signal(SIGTTIN, SIG_IGN);
+#endif
+ tevent_add_fd(event_ctx, event_ctx, 0, stdin_event_flags,
+ server_stdin_handler,
+ discard_const(binary_name));
+
+ if (max_runtime) {
+ tevent_add_timer(event_ctx, event_ctx,
+ timeval_current_ofs(max_runtime, 0),
+ max_runtime_handler,
+ discard_const(binary_name));
+ }
+
+ DEBUG(0,("%s: using '%s' process model\n", binary_name, model));
+ status = server_service_startup(event_ctx, cmdline_lp_ctx, model,
+ lp_server_services(cmdline_lp_ctx));
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Starting Services failed - %s\n", nt_errstr(status)));
+ return 1;
+ }
+
+ /* wait for events - this is where smbd sits for most of its
+ life */
+ tevent_loop_wait(event_ctx);
+
+ /* as everything hangs off this event context, freeing it
+ should initiate a clean shutdown of all services */
+ talloc_free(event_ctx);
+
+ return 0;
+}
+
+ int main(int argc, const char *argv[])
+{
+ return binary_smbd_main("smbd", argc, argv);
+}
diff --git a/source4/smbd/service.c b/source4/smbd/service.c
new file mode 100644
index 0000000000..7b53e9fa4e
--- /dev/null
+++ b/source4/smbd/service.c
@@ -0,0 +1,104 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SERVER SERVICE code
+
+ Copyright (C) Andrew Tridgell 2003-2005
+ Copyright (C) Stefan (metze) Metzmacher 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "../lib/util/dlinklist.h"
+#include "smbd/process_model.h"
+
+/*
+ a linked list of registered servers
+*/
+static struct registered_server {
+ struct registered_server *next, *prev;
+ const char *service_name;
+ void (*task_init)(struct task_server *);
+} *registered_servers;
+
+/*
+ register a server service.
+*/
+NTSTATUS register_server_service(const char *name,
+ void (*task_init)(struct task_server *))
+{
+ struct registered_server *srv;
+ srv = talloc(talloc_autofree_context(), struct registered_server);
+ NT_STATUS_HAVE_NO_MEMORY(srv);
+ srv->service_name = name;
+ srv->task_init = task_init;
+ DLIST_ADD_END(registered_servers, srv, struct registered_server *);
+ return NT_STATUS_OK;
+}
+
+
+/*
+ initialise a server service
+*/
+static NTSTATUS server_service_init(const char *name,
+ struct tevent_context *event_context,
+ struct loadparm_context *lp_ctx,
+ const struct model_ops *model_ops)
+{
+ struct registered_server *srv;
+ for (srv=registered_servers; srv; srv=srv->next) {
+ if (strcasecmp(name, srv->service_name) == 0) {
+ return task_server_startup(event_context, lp_ctx, srv->service_name,
+ model_ops, srv->task_init);
+ }
+ }
+ return NT_STATUS_INVALID_SYSTEM_SERVICE;
+}
+
+
+/*
+ startup all of our server services
+*/
+NTSTATUS server_service_startup(struct tevent_context *event_ctx,
+ struct loadparm_context *lp_ctx,
+ const char *model, const char **server_services)
+{
+ int i;
+ const struct model_ops *model_ops;
+
+ if (!server_services) {
+ DEBUG(0,("server_service_startup: no endpoint servers configured\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ model_ops = process_model_startup(event_ctx, model);
+ if (!model_ops) {
+ DEBUG(0,("process_model_startup('%s') failed\n", model));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ for (i=0;server_services[i];i++) {
+ NTSTATUS status;
+
+ status = server_service_init(server_services[i], event_ctx, lp_ctx, model_ops);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Failed to start service '%s' - %s\n",
+ server_services[i], nt_errstr(status)));
+ }
+ NT_STATUS_NOT_OK_RETURN(status);
+ }
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/smbd/service.h b/source4/smbd/service.h
new file mode 100644
index 0000000000..23a9e6315b
--- /dev/null
+++ b/source4/smbd/service.h
@@ -0,0 +1,30 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SERVER SERVICE code
+
+ Copyright (C) Andrew Tridgell 2003-2005
+ Copyright (C) Stefan (metze) Metzmacher 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __SERVICE_H__
+#define __SERVICE_H__
+
+#include "smbd/service_stream.h"
+#include "smbd/service_task.h"
+#include "smbd/service_proto.h"
+
+#endif /* __SERVICE_H__ */
diff --git a/source4/smbd/service_named_pipe.c b/source4/smbd/service_named_pipe.c
new file mode 100644
index 0000000000..de4726e4d5
--- /dev/null
+++ b/source4/smbd/service_named_pipe.c
@@ -0,0 +1,368 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ helper functions for NAMED PIPE servers
+
+ Copyright (C) Stefan (metze) 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include <tevent.h>
+#include "lib/socket/socket.h"
+#include "smbd/service.h"
+#include "param/param.h"
+#include "auth/session.h"
+#include "auth/auth_sam_reply.h"
+#include "lib/stream/packet.h"
+#include "librpc/gen_ndr/ndr_named_pipe_auth.h"
+#include "system/passwd.h"
+
+struct named_pipe_socket {
+ const char *pipe_name;
+ const char *pipe_path;
+ const struct stream_server_ops *ops;
+ void *private_data;
+};
+
+struct named_pipe_connection {
+ struct stream_connection *connection;
+ struct packet_context *packet;
+ const struct named_pipe_socket *pipe_sock;
+ NTSTATUS status;
+};
+
+static void named_pipe_handover_connection(void *private_data)
+{
+ struct named_pipe_connection *pipe_conn = talloc_get_type(
+ private_data, struct named_pipe_connection);
+ struct stream_connection *conn = pipe_conn->connection;
+
+ TEVENT_FD_NOT_WRITEABLE(conn->event.fde);
+
+ if (!NT_STATUS_IS_OK(pipe_conn->status)) {
+ stream_terminate_connection(conn, nt_errstr(pipe_conn->status));
+ return;
+ }
+
+ /*
+ * remove the named_pipe layer together with its packet layer
+ */
+ conn->ops = pipe_conn->pipe_sock->ops;
+ conn->private_data = pipe_conn->pipe_sock->private_data;
+ talloc_free(pipe_conn);
+
+ /* we're now ready to start receiving events on this stream */
+ TEVENT_FD_READABLE(conn->event.fde);
+
+ /*
+ * hand over to the real pipe implementation,
+ * now that we have setup the transport session_info
+ */
+ conn->ops->accept_connection(conn);
+
+ DEBUG(10,("named_pipe_handover_connection[%s]: succeeded\n",
+ conn->ops->name));
+}
+
+static NTSTATUS named_pipe_recv_auth_request(void *private_data,
+ DATA_BLOB req_blob)
+{
+ struct named_pipe_connection *pipe_conn = talloc_get_type(
+ private_data, struct named_pipe_connection);
+ struct stream_connection *conn = pipe_conn->connection;
+ enum ndr_err_code ndr_err;
+ struct named_pipe_auth_req req;
+ union netr_Validation val;
+ struct auth_serversupplied_info *server_info;
+ struct named_pipe_auth_rep rep;
+ DATA_BLOB rep_blob;
+ NTSTATUS status;
+
+ /*
+ * make sure nothing happens on the socket untill the
+ * real implemenation takes over
+ */
+ packet_recv_disable(pipe_conn->packet);
+
+ /*
+ * TODO: check it's a root (uid == 0) pipe
+ */
+
+ ZERO_STRUCT(rep);
+ rep.level = 0;
+ rep.status = NT_STATUS_INTERNAL_ERROR;
+
+ DEBUG(10,("named_pipe_auth: req_blob.length[%u]\n",
+ (unsigned int)req_blob.length));
+ dump_data(10, req_blob.data, req_blob.length);
+
+ /* parse the passed credentials */
+ ndr_err = ndr_pull_struct_blob_all(
+ &req_blob,
+ pipe_conn,
+ lp_iconv_convenience(conn->lp_ctx),
+ &req,
+ (ndr_pull_flags_fn_t)ndr_pull_named_pipe_auth_req);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ rep.status = ndr_map_error2ntstatus(ndr_err);
+ DEBUG(2, ("Could not unmarshall named_pipe_auth_req: %s\n",
+ nt_errstr(rep.status)));
+ goto reply;
+ }
+
+ if (strcmp(NAMED_PIPE_AUTH_MAGIC, req.magic) != 0) {
+ DEBUG(2, ("named_pipe_auth_req: invalid magic '%s' != %s\n",
+ req.magic, NAMED_PIPE_AUTH_MAGIC));
+ rep.status = NT_STATUS_INVALID_PARAMETER;
+ goto reply;
+ }
+
+ switch (req.level) {
+ case 0:
+ /*
+ * anon connection, we don't create a session info
+ * and leave it NULL
+ */
+ rep.level = 0;
+ rep.status = NT_STATUS_OK;
+ break;
+ case 1:
+ val.sam3 = &req.info.info1;
+
+ rep.level = 1;
+ rep.status = make_server_info_netlogon_validation(pipe_conn,
+ "TODO",
+ 3, &val,
+ &server_info);
+ if (!NT_STATUS_IS_OK(rep.status)) {
+ DEBUG(2, ("make_server_info_netlogon_validation returned "
+ "%s\n", nt_errstr(rep.status)));
+ goto reply;
+ }
+
+ /* setup the session_info on the connection */
+ rep.status = auth_generate_session_info(conn,
+ conn->event.ctx,
+ conn->lp_ctx,
+ server_info,
+ &conn->session_info);
+ if (!NT_STATUS_IS_OK(rep.status)) {
+ DEBUG(2, ("auth_generate_session_info failed: %s\n",
+ nt_errstr(rep.status)));
+ goto reply;
+ }
+
+ break;
+ default:
+ DEBUG(2, ("named_pipe_auth_req: unknown level %u\n",
+ req.level));
+ rep.level = 0;
+ rep.status = NT_STATUS_INVALID_LEVEL;
+ goto reply;
+ }
+
+reply:
+ /* create the output */
+ ndr_err = ndr_push_struct_blob(&rep_blob, pipe_conn,
+ lp_iconv_convenience(conn->lp_ctx),
+ &rep,
+ (ndr_push_flags_fn_t)ndr_push_named_pipe_auth_rep);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ DEBUG(2, ("Could not marshall named_pipe_auth_rep: %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+
+ pipe_conn->status = rep.status;
+
+ DEBUG(10,("named_pipe_auth reply[%u]\n", rep_blob.length));
+ dump_data(10, rep_blob.data, rep_blob.length);
+ status = packet_send_callback(pipe_conn->packet, rep_blob,
+ named_pipe_handover_connection,
+ pipe_conn);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("packet_send_callback returned %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ called when a pipe socket becomes readable
+*/
+static void named_pipe_recv(struct stream_connection *conn, uint16_t flags)
+{
+ struct named_pipe_connection *pipe_conn = talloc_get_type(
+ conn->private_data, struct named_pipe_connection);
+
+ DEBUG(10,("named_pipe_recv\n"));
+
+ packet_recv(pipe_conn->packet);
+}
+
+/*
+ called when a pipe socket becomes writable
+*/
+static void named_pipe_send(struct stream_connection *conn, uint16_t flags)
+{
+ struct named_pipe_connection *pipe_conn = talloc_get_type(
+ conn->private_data, struct named_pipe_connection);
+
+ packet_queue_run(pipe_conn->packet);
+}
+
+/*
+ handle socket recv errors
+*/
+static void named_pipe_recv_error(void *private_data, NTSTATUS status)
+{
+ struct named_pipe_connection *pipe_conn = talloc_get_type(
+ private_data, struct named_pipe_connection);
+
+ stream_terminate_connection(pipe_conn->connection, nt_errstr(status));
+}
+
+static NTSTATUS named_pipe_full_request(void *private_data, DATA_BLOB blob, size_t *size)
+{
+ if (blob.length < 8) {
+ return STATUS_MORE_ENTRIES;
+ }
+
+ if (memcmp(NAMED_PIPE_AUTH_MAGIC, &blob.data[4], 4) != 0) {
+ DEBUG(0,("named_pipe_full_request: wrong protocol\n"));
+ *size = blob.length;
+ /* the error will be handled in named_pipe_recv_auth_request */
+ return NT_STATUS_OK;
+ }
+
+ *size = 4 + RIVAL(blob.data, 0);
+ if (*size > blob.length) {
+ return STATUS_MORE_ENTRIES;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static void named_pipe_accept(struct stream_connection *conn)
+{
+ struct named_pipe_socket *pipe_sock = talloc_get_type(
+ conn->private_data, struct named_pipe_socket);
+ struct named_pipe_connection *pipe_conn;
+
+ DEBUG(5,("named_pipe_accept\n"));
+
+ pipe_conn = talloc_zero(conn, struct named_pipe_connection);
+ if (!pipe_conn) {
+ stream_terminate_connection(conn, "out of memory");
+ return;
+ }
+
+ pipe_conn->packet = packet_init(pipe_conn);
+ if (!pipe_conn->packet) {
+ stream_terminate_connection(conn, "out of memory");
+ return;
+ }
+ packet_set_private(pipe_conn->packet, pipe_conn);
+ packet_set_socket(pipe_conn->packet, conn->socket);
+ packet_set_callback(pipe_conn->packet, named_pipe_recv_auth_request);
+ packet_set_full_request(pipe_conn->packet, named_pipe_full_request);
+ packet_set_error_handler(pipe_conn->packet, named_pipe_recv_error);
+ packet_set_event_context(pipe_conn->packet, conn->event.ctx);
+ packet_set_fde(pipe_conn->packet, conn->event.fde);
+ packet_set_serialise(pipe_conn->packet);
+ packet_set_initial_read(pipe_conn->packet, 8);
+
+ pipe_conn->pipe_sock = pipe_sock;
+
+ pipe_conn->connection = conn;
+ conn->private_data = pipe_conn;
+}
+
+static const struct stream_server_ops named_pipe_stream_ops = {
+ .name = "named_pipe",
+ .accept_connection = named_pipe_accept,
+ .recv_handler = named_pipe_recv,
+ .send_handler = named_pipe_send,
+};
+
+NTSTATUS stream_setup_named_pipe(struct tevent_context *event_context,
+ struct loadparm_context *lp_ctx,
+ const struct model_ops *model_ops,
+ const struct stream_server_ops *stream_ops,
+ const char *pipe_name,
+ void *private_data)
+{
+ char *dirname;
+ struct named_pipe_socket *pipe_sock;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;;
+
+ pipe_sock = talloc(event_context, struct named_pipe_socket);
+ if (pipe_sock == NULL) {
+ goto fail;
+ }
+
+ /* remember the details about the pipe */
+ pipe_sock->pipe_name = talloc_strdup(pipe_sock, pipe_name);
+ if (pipe_sock->pipe_name == NULL) {
+ goto fail;
+ }
+
+ dirname = talloc_asprintf(pipe_sock, "%s/np", lp_ncalrpc_dir(lp_ctx));
+ if (dirname == NULL) {
+ goto fail;
+ }
+
+ if (!directory_create_or_exist(dirname, geteuid(), 0700)) {
+ status = map_nt_error_from_unix(errno);
+ goto fail;
+ }
+
+ if (strncmp(pipe_name, "\\pipe\\", 6) == 0) {
+ pipe_name += 6;
+ }
+
+ pipe_sock->pipe_path = talloc_asprintf(pipe_sock, "%s/%s", dirname,
+ pipe_name);
+ if (pipe_sock->pipe_path == NULL) {
+ goto fail;
+ }
+
+ talloc_free(dirname);
+
+ pipe_sock->ops = stream_ops;
+ pipe_sock->private_data = talloc_reference(pipe_sock, private_data);
+
+ status = stream_setup_socket(event_context,
+ lp_ctx,
+ model_ops,
+ &named_pipe_stream_ops,
+ "unix",
+ pipe_sock->pipe_path,
+ NULL,
+ NULL,
+ pipe_sock);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+ return NT_STATUS_OK;
+
+ fail:
+ talloc_free(pipe_sock);
+ return status;
+}
diff --git a/source4/smbd/service_stream.c b/source4/smbd/service_stream.c
new file mode 100644
index 0000000000..e09ac1d9e6
--- /dev/null
+++ b/source4/smbd/service_stream.c
@@ -0,0 +1,369 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ helper functions for stream based servers
+
+ Copyright (C) Andrew Tridgell 2003-2005
+ Copyright (C) Stefan (metze) Metzmacher 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include <tevent.h>
+#include "process_model.h"
+#include "lib/socket/socket.h"
+#include "smbd/service.h"
+#include "smbd/service_stream.h"
+#include "lib/messaging/irpc.h"
+#include "cluster/cluster.h"
+#include "param/param.h"
+
+/* the range of ports to try for dcerpc over tcp endpoints */
+#define SERVER_TCP_LOW_PORT 1024
+#define SERVER_TCP_HIGH_PORT 1300
+
+/* size of listen() backlog in smbd */
+#define SERVER_LISTEN_BACKLOG 10
+
+
+/*
+ private structure for a single listening stream socket
+*/
+struct stream_socket {
+ const struct stream_server_ops *ops;
+ struct loadparm_context *lp_ctx;
+ struct tevent_context *event_ctx;
+ const struct model_ops *model_ops;
+ struct socket_context *sock;
+ void *private_data;
+};
+
+
+/*
+ close the socket and shutdown a stream_connection
+*/
+void stream_terminate_connection(struct stream_connection *srv_conn, const char *reason)
+{
+ struct tevent_context *event_ctx = srv_conn->event.ctx;
+ const struct model_ops *model_ops = srv_conn->model_ops;
+
+ if (!reason) reason = "unknown reason";
+
+ DEBUG(3,("Terminating connection - '%s'\n", reason));
+
+ srv_conn->terminate = reason;
+
+ if (srv_conn->processing) {
+ /*
+ * if we're currently inside the stream_io_handler(),
+ * defer the termination to the end of stream_io_hendler()
+ *
+ * and we don't want to read or write to the connection...
+ */
+ tevent_fd_set_flags(srv_conn->event.fde, 0);
+ return;
+ }
+
+ talloc_free(srv_conn->event.fde);
+ srv_conn->event.fde = NULL;
+ model_ops->terminate(event_ctx, srv_conn->lp_ctx, reason);
+ talloc_free(srv_conn);
+}
+
+/**
+ the select loop has indicated that a stream is ready for IO
+*/
+static void stream_io_handler(struct stream_connection *conn, uint16_t flags)
+{
+ conn->processing++;
+ if (flags & TEVENT_FD_WRITE) {
+ conn->ops->send_handler(conn, flags);
+ } else if (flags & TEVENT_FD_READ) {
+ conn->ops->recv_handler(conn, flags);
+ }
+ conn->processing--;
+
+ if (conn->terminate) {
+ stream_terminate_connection(conn, conn->terminate);
+ }
+}
+
+static void stream_io_handler_fde(struct tevent_context *ev, struct tevent_fd *fde,
+ uint16_t flags, void *private_data)
+{
+ struct stream_connection *conn = talloc_get_type(private_data,
+ struct stream_connection);
+ stream_io_handler(conn, flags);
+}
+
+void stream_io_handler_callback(void *private_data, uint16_t flags)
+{
+ struct stream_connection *conn = talloc_get_type(private_data,
+ struct stream_connection);
+ stream_io_handler(conn, flags);
+}
+
+/*
+ this creates a stream_connection from an already existing connection,
+ used for protocols, where a client connection needs to switched into
+ a server connection
+*/
+NTSTATUS stream_new_connection_merge(struct tevent_context *ev,
+ struct loadparm_context *lp_ctx,
+ const struct model_ops *model_ops,
+ struct socket_context *sock,
+ const struct stream_server_ops *stream_ops,
+ struct messaging_context *msg_ctx,
+ void *private_data,
+ struct stream_connection **_srv_conn)
+{
+ struct stream_connection *srv_conn;
+
+ srv_conn = talloc_zero(ev, struct stream_connection);
+ NT_STATUS_HAVE_NO_MEMORY(srv_conn);
+
+ talloc_steal(srv_conn, sock);
+
+ srv_conn->private_data = private_data;
+ srv_conn->model_ops = model_ops;
+ srv_conn->socket = sock;
+ srv_conn->server_id = cluster_id(0, 0);
+ srv_conn->ops = stream_ops;
+ srv_conn->msg_ctx = msg_ctx;
+ srv_conn->event.ctx = ev;
+ srv_conn->lp_ctx = lp_ctx;
+ srv_conn->event.fde = tevent_add_fd(ev, srv_conn, socket_get_fd(sock),
+ TEVENT_FD_READ,
+ stream_io_handler_fde, srv_conn);
+ if (!srv_conn->event.fde) {
+ talloc_free(srv_conn);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ *_srv_conn = srv_conn;
+ return NT_STATUS_OK;
+}
+
+/*
+ called when a new socket connection has been established. This is called in the process
+ context of the new process (if appropriate)
+*/
+static void stream_new_connection(struct tevent_context *ev,
+ struct loadparm_context *lp_ctx,
+ struct socket_context *sock,
+ struct server_id server_id, void *private_data)
+{
+ struct stream_socket *stream_socket = talloc_get_type(private_data, struct stream_socket);
+ struct stream_connection *srv_conn;
+ struct socket_address *c, *s;
+
+ srv_conn = talloc_zero(ev, struct stream_connection);
+ if (!srv_conn) {
+ DEBUG(0,("talloc(mem_ctx, struct stream_connection) failed\n"));
+ return;
+ }
+
+ talloc_steal(srv_conn, sock);
+
+ srv_conn->private_data = stream_socket->private_data;
+ srv_conn->model_ops = stream_socket->model_ops;
+ srv_conn->socket = sock;
+ srv_conn->server_id = server_id;
+ srv_conn->ops = stream_socket->ops;
+ srv_conn->event.ctx = ev;
+ srv_conn->lp_ctx = lp_ctx;
+
+ if (!socket_check_access(sock, "smbd", lp_hostsallow(NULL, lp_default_service(lp_ctx)), lp_hostsdeny(NULL, lp_default_service(lp_ctx)))) {
+ stream_terminate_connection(srv_conn, "denied by access rules");
+ return;
+ }
+
+ srv_conn->event.fde = tevent_add_fd(ev, srv_conn, socket_get_fd(sock),
+ 0, stream_io_handler_fde, srv_conn);
+ if (!srv_conn->event.fde) {
+ stream_terminate_connection(srv_conn, "tevent_add_fd() failed");
+ return;
+ }
+
+ /* setup to receive internal messages on this connection */
+ srv_conn->msg_ctx = messaging_init(srv_conn,
+ lp_messaging_path(srv_conn, lp_ctx),
+ srv_conn->server_id,
+ lp_iconv_convenience(lp_ctx),
+ ev);
+ if (!srv_conn->msg_ctx) {
+ stream_terminate_connection(srv_conn, "messaging_init() failed");
+ return;
+ }
+
+ c = socket_get_peer_addr(sock, ev);
+ s = socket_get_my_addr(sock, ev);
+ if (s && c) {
+ const char *title;
+ title = talloc_asprintf(s, "conn[%s] c[%s:%u] s[%s:%u] server_id[%s]",
+ stream_socket->ops->name,
+ c->addr, c->port, s->addr, s->port,
+ cluster_id_string(s, server_id));
+ if (title) {
+ stream_connection_set_title(srv_conn, title);
+ }
+ }
+ talloc_free(c);
+ talloc_free(s);
+
+ /* we're now ready to start receiving events on this stream */
+ TEVENT_FD_READABLE(srv_conn->event.fde);
+
+ /* call the server specific accept code */
+ stream_socket->ops->accept_connection(srv_conn);
+}
+
+
+/*
+ called when someone opens a connection to one of our listening ports
+*/
+static void stream_accept_handler(struct tevent_context *ev, struct tevent_fd *fde,
+ uint16_t flags, void *private_data)
+{
+ struct stream_socket *stream_socket = talloc_get_type(private_data, struct stream_socket);
+
+ /* ask the process model to create us a process for this new
+ connection. When done, it calls stream_new_connection()
+ with the newly created socket */
+ stream_socket->model_ops->accept_connection(ev, stream_socket->lp_ctx,
+ stream_socket->sock,
+ stream_new_connection, stream_socket);
+}
+
+/*
+ setup a listen stream socket
+ if you pass *port == 0, then a port > 1024 is used
+
+ FIXME: This function is TCP/IP specific - uses an int rather than
+ a string for the port. Should leave allocating a port nr
+ to the socket implementation - JRV20070903
+ */
+NTSTATUS stream_setup_socket(struct tevent_context *event_context,
+ struct loadparm_context *lp_ctx,
+ const struct model_ops *model_ops,
+ const struct stream_server_ops *stream_ops,
+ const char *family,
+ const char *sock_addr,
+ uint16_t *port,
+ const char *socket_options,
+ void *private_data)
+{
+ NTSTATUS status;
+ struct stream_socket *stream_socket;
+ struct socket_address *socket_address;
+ struct tevent_fd *fde;
+ int i;
+
+ stream_socket = talloc_zero(event_context, struct stream_socket);
+ NT_STATUS_HAVE_NO_MEMORY(stream_socket);
+
+ status = socket_create(family, SOCKET_TYPE_STREAM, &stream_socket->sock, 0);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ talloc_steal(stream_socket, stream_socket->sock);
+
+ stream_socket->lp_ctx = talloc_reference(stream_socket, lp_ctx);
+
+ /* ready to listen */
+ status = socket_set_option(stream_socket->sock, "SO_KEEPALIVE", NULL);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ if (socket_options != NULL) {
+ status = socket_set_option(stream_socket->sock, socket_options, NULL);
+ NT_STATUS_NOT_OK_RETURN(status);
+ }
+
+ /* TODO: set socket ACL's (host allow etc) here when they're
+ * implemented */
+
+ /* Some sockets don't have a port, or are just described from
+ * the string. We are indicating this by having port == NULL */
+ if (!port) {
+ socket_address = socket_address_from_strings(stream_socket,
+ stream_socket->sock->backend_name,
+ sock_addr, 0);
+ NT_STATUS_HAVE_NO_MEMORY(socket_address);
+ status = socket_listen(stream_socket->sock, socket_address, SERVER_LISTEN_BACKLOG, 0);
+ talloc_free(socket_address);
+
+ } else if (*port == 0) {
+ for (i=SERVER_TCP_LOW_PORT;i<= SERVER_TCP_HIGH_PORT;i++) {
+ socket_address = socket_address_from_strings(stream_socket,
+ stream_socket->sock->backend_name,
+ sock_addr, i);
+ NT_STATUS_HAVE_NO_MEMORY(socket_address);
+ status = socket_listen(stream_socket->sock, socket_address,
+ SERVER_LISTEN_BACKLOG, 0);
+ talloc_free(socket_address);
+ if (NT_STATUS_IS_OK(status)) {
+ *port = i;
+ break;
+ }
+ }
+ } else {
+ socket_address = socket_address_from_strings(stream_socket,
+ stream_socket->sock->backend_name,
+ sock_addr, *port);
+ NT_STATUS_HAVE_NO_MEMORY(socket_address);
+ status = socket_listen(stream_socket->sock, socket_address, SERVER_LISTEN_BACKLOG, 0);
+ talloc_free(socket_address);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Failed to listen on %s:%u - %s\n",
+ sock_addr, *port, nt_errstr(status)));
+ talloc_free(stream_socket);
+ return status;
+ }
+
+ /* Add the FD from the newly created socket into the event
+ * subsystem. it will call the accept handler whenever we get
+ * new connections */
+
+ fde = tevent_add_fd(event_context, stream_socket->sock,
+ socket_get_fd(stream_socket->sock),
+ TEVENT_FD_READ,
+ stream_accept_handler, stream_socket);
+ if (!fde) {
+ DEBUG(0,("Failed to setup fd event\n"));
+ talloc_free(stream_socket);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* we let events system to the close on the socket. This avoids
+ * nasty interactions with waiting for talloc to close the socket. */
+ tevent_fd_set_close_fn(fde, socket_tevent_fd_close_fn);
+ socket_set_flags(stream_socket->sock, SOCKET_FLAG_NOCLOSE);
+
+ stream_socket->private_data = talloc_reference(stream_socket, private_data);
+ stream_socket->ops = stream_ops;
+ stream_socket->event_ctx = event_context;
+ stream_socket->model_ops = model_ops;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ setup a connection title
+*/
+void stream_connection_set_title(struct stream_connection *conn, const char *title)
+{
+ conn->model_ops->set_title(conn->event.ctx, title);
+}
diff --git a/source4/smbd/service_stream.h b/source4/smbd/service_stream.h
new file mode 100644
index 0000000000..5d577d4dd8
--- /dev/null
+++ b/source4/smbd/service_stream.h
@@ -0,0 +1,73 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ structures specific to stream servers
+
+ Copyright (C) Stefan (metze) Metzmacher 2004
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __SERVICE_STREAM_H__
+#define __SERVICE_STREAM_H__
+
+#include "librpc/gen_ndr/server_id.h"
+
+/* modules can use the following to determine if the interface has changed
+ * please increment the version number after each interface change
+ * with a comment and maybe update struct stream_connection_critical_sizes.
+ */
+/* version 0 - initial version - metze */
+#define SERVER_SERVICE_VERSION 0
+
+/*
+ top level context for an established stream connection
+*/
+struct stream_connection {
+ const struct stream_server_ops *ops;
+ const struct model_ops *model_ops;
+ struct server_id server_id;
+ void *private_data;
+
+ struct {
+ struct tevent_context *ctx;
+ struct tevent_fd *fde;
+ } event;
+
+ struct socket_context *socket;
+ struct messaging_context *msg_ctx;
+ struct loadparm_context *lp_ctx;
+
+ /*
+ * this transport layer session info, normally NULL
+ * which means the same as an anonymous session info
+ */
+ struct auth_session_info *session_info;
+
+ bool processing;
+ const char *terminate;
+};
+
+
+/* operations passed to the service_stream code */
+struct stream_server_ops {
+ /* the name of the server_service */
+ const char *name;
+ void (*accept_connection)(struct stream_connection *);
+ void (*recv_handler)(struct stream_connection *, uint16_t);
+ void (*send_handler)(struct stream_connection *, uint16_t);
+};
+
+#endif /* __SERVICE_STREAM_H__ */
diff --git a/source4/smbd/service_task.c b/source4/smbd/service_task.c
new file mode 100644
index 0000000000..c4fd3d4e98
--- /dev/null
+++ b/source4/smbd/service_task.c
@@ -0,0 +1,110 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ helper functions for task based servers (nbtd, winbind etc)
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "process_model.h"
+#include "smbd/service.h"
+#include "smbd/service_task.h"
+#include "lib/messaging/irpc.h"
+#include "param/param.h"
+
+/*
+ terminate a task service
+*/
+void task_server_terminate(struct task_server *task, const char *reason)
+{
+ struct tevent_context *event_ctx = task->event_ctx;
+ const struct model_ops *model_ops = task->model_ops;
+ DEBUG(0,("task_server_terminate: [%s]\n", reason));
+ model_ops->terminate(event_ctx, task->lp_ctx, reason);
+
+ /* don't free this above, it might contain the 'reason' being printed */
+ talloc_free(task);
+}
+
+/* used for the callback from the process model code */
+struct task_state {
+ void (*task_init)(struct task_server *);
+ const struct model_ops *model_ops;
+};
+
+
+/*
+ called by the process model code when the new task starts up. This then calls
+ the server specific startup code
+*/
+static void task_server_callback(struct tevent_context *event_ctx,
+ struct loadparm_context *lp_ctx,
+ struct server_id server_id, void *private_data)
+{
+ struct task_state *state = talloc_get_type(private_data, struct task_state);
+ struct task_server *task;
+
+ task = talloc(event_ctx, struct task_server);
+ if (task == NULL) return;
+
+ task->event_ctx = event_ctx;
+ task->model_ops = state->model_ops;
+ task->server_id = server_id;
+ task->lp_ctx = lp_ctx;
+
+ task->msg_ctx = messaging_init(task,
+ lp_messaging_path(task, task->lp_ctx),
+ task->server_id,
+ lp_iconv_convenience(task->lp_ctx),
+ task->event_ctx);
+ if (!task->msg_ctx) {
+ task_server_terminate(task, "messaging_init() failed");
+ return;
+ }
+
+ state->task_init(task);
+}
+
+/*
+ startup a task based server
+*/
+NTSTATUS task_server_startup(struct tevent_context *event_ctx,
+ struct loadparm_context *lp_ctx,
+ const char *service_name,
+ const struct model_ops *model_ops,
+ void (*task_init)(struct task_server *))
+{
+ struct task_state *state;
+
+ state = talloc(event_ctx, struct task_state);
+ NT_STATUS_HAVE_NO_MEMORY(state);
+
+ state->task_init = task_init;
+ state->model_ops = model_ops;
+
+ model_ops->new_task(event_ctx, lp_ctx, service_name, task_server_callback, state);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ setup a task title
+*/
+void task_server_set_title(struct task_server *task, const char *title)
+{
+ task->model_ops->set_title(task->event_ctx, title);
+}
diff --git a/source4/smbd/service_task.h b/source4/smbd/service_task.h
new file mode 100644
index 0000000000..69ecb1b970
--- /dev/null
+++ b/source4/smbd/service_task.h
@@ -0,0 +1,38 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ structures for task based servers
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __SERVICE_TASK_H__
+#define __SERVICE_TASK_H__
+
+#include "librpc/gen_ndr/server_id.h"
+
+struct task_server {
+ struct tevent_context *event_ctx;
+ const struct model_ops *model_ops;
+ struct messaging_context *msg_ctx;
+ struct loadparm_context *lp_ctx;
+ struct server_id server_id;
+ void *private_data;
+};
+
+
+
+#endif /* __SERVICE_TASK_H__ */
diff --git a/source4/static_deps.mk b/source4/static_deps.mk
new file mode 100644
index 0000000000..e366abf681
--- /dev/null
+++ b/source4/static_deps.mk
@@ -0,0 +1,15 @@
+# These should also be autogenerated at some point
+# perhaps by some perl scripts run from config.status ?
+#
+$(gen_ndrsrcdir)/misc.h: idl
+$(ndrsrcdir)/libndr.h: $(gen_ndrsrcdir)/misc.h
+$(dcerpcsrcdir)/dcerpc.h: $(dcerpcsrcdir)/dcerpc_proto.h
+$(authsrcdir)/credentials/credentials.h: $(authsrcdir)/credentials/credentials_proto.h
+$(libclisrcdir)/nbt/libnbt.h: $(libclisrcdir)/nbt/nbt_proto.h
+
+include/includes.h: \
+ include/config.h \
+ $(gen_ndrsrcdir)/misc.h
+
+proto::
+basics:: include/includes.h idl proto
diff --git a/source4/torture/auth/ntlmssp.c b/source4/torture/auth/ntlmssp.c
new file mode 100644
index 0000000000..e62b150a4b
--- /dev/null
+++ b/source4/torture/auth/ntlmssp.c
@@ -0,0 +1,157 @@
+/*
+ Unix SMB/CIFS implementation.
+ Small self-tests for the NTLMSSP code
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "auth/gensec/gensec.h"
+#include "auth/gensec/gensec_proto.h"
+#include "auth/ntlmssp/ntlmssp.h"
+#include "lib/cmdline/popt_common.h"
+#include "torture/torture.h"
+#include "param/param.h"
+
+static bool torture_ntlmssp_self_check(struct torture_context *tctx)
+{
+ struct gensec_security *gensec_security;
+ struct gensec_ntlmssp_state *gensec_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,
+ tctx->ev, lp_gensec_settings(tctx, tctx->lp_ctx)),
+ "gensec client start");
+
+ gensec_set_credentials(gensec_security, cmdline_credentials);
+
+ 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_state = (struct gensec_ntlmssp_state *)gensec_security->private_data;
+
+ gensec_ntlmssp_state->session_key = strhex_to_data_blob(tctx, "0102030405060708090a0b0c0d0e0f00");
+ dump_data_pw("NTLMSSP session key: \n",
+ gensec_ntlmssp_state->session_key.data,
+ gensec_ntlmssp_state->session_key.length);
+
+ gensec_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(gensec_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, gensec_security,
+ data.data, data.length, data.data, data.length, &sig),
+ NT_STATUS_ACCESS_DENIED, "Check of just signed packet (should fail, wrong end)");
+
+ gensec_ntlmssp_state->session_key = data_blob(NULL, 0);
+
+ torture_assert_ntstatus_equal(tctx,
+ gensec_ntlmssp_check_packet(gensec_security, 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,
+ tctx->ev, lp_gensec_settings(tctx, tctx->lp_ctx)),
+ "Failed to start GENSEC for NTLMSSP");
+
+ gensec_set_credentials(gensec_security, cmdline_credentials);
+
+ 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_state = (struct gensec_ntlmssp_state *)gensec_security->private_data;
+
+ gensec_ntlmssp_state->session_key = strhex_to_data_blob(tctx, "0102030405e538b0");
+ dump_data_pw("NTLMSSP session key: \n",
+ gensec_ntlmssp_state->session_key.data,
+ gensec_ntlmssp_state->session_key.length);
+
+ gensec_ntlmssp_state->neg_flags = NTLMSSP_NEGOTIATE_SIGN | NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_NEGOTIATE_KEY_EXCH;
+
+ torture_assert_ntstatus_ok(tctx,
+ ntlmssp_sign_init(gensec_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, 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, 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 0000000000..fd54863686
--- /dev/null
+++ b/source4/torture/auth/pac.c
@@ -0,0 +1,752 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Validate the krb5 pac generation routines
+
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/kerberos.h"
+#include "auth/auth.h"
+#include "auth/kerberos/kerberos.h"
+#include "librpc/gen_ndr/ndr_krb5pac.h"
+#include "samba3/samba3.h"
+#include "libcli/security/security.h"
+#include "torture/torture.h"
+#include "auth/auth_sam_reply.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, arbitary 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_serversupplied_info *server_info;
+ struct auth_serversupplied_info *server_info_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,
+ NULL,
+ tctx->lp_ctx,
+ &smb_krb5_context),
+ "smb_krb5_init_context");
+
+ generate_random_buffer(server_bytes, 16);
+ generate_random_buffer(krbtgt_bytes, 16);
+
+ ret = krb5_keyblock_init(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 = krb5_keyblock_init(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_server_info(mem_ctx, lp_netbios_name(tctx->lp_ctx), &server_info);
+
+ 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_server_info");
+ }
+
+ ret = krb5_parse_name_flags(smb_krb5_context->krb5_context,
+ server_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,
+ lp_iconv_convenience(tctx->lp_ctx),
+ server_info,
+ 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,
+ lp_iconv_convenience(tctx->lp_ctx),
+ &pac_data,
+ tmp_blob,
+ smb_krb5_context->krb5_context,
+ &krbtgt_keyblock,
+ &server_keyblock,
+ client_principal,
+ logon_time, NULL);
+
+ 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_server_info(mem_ctx,
+ lp_iconv_convenience(tctx->lp_ctx),
+ tmp_blob,
+ smb_krb5_context->krb5_context,
+ &server_info_out);
+
+ if (!dom_sid_equal(server_info->account_sid,
+ server_info_out->account_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, server_info->account_sid),
+ dom_sid_string(mem_ctx, server_info_out->account_sid)));
+ }
+ talloc_free(server_info_out);
+
+ /* Now check that we can read it back (yet again) */
+ nt_status = kerberos_pac_logon_info(mem_ctx,
+ lp_iconv_convenience(tctx->lp_ctx),
+ &logon_info,
+ tmp_blob,
+ smb_krb5_context->krb5_context,
+ &krbtgt_keyblock,
+ &server_keyblock,
+ client_principal,
+ logon_time,
+ NULL);
+
+ 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_server_info_netlogon_validation(mem_ctx,
+ "",
+ 3, &validation,
+ &server_info_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(server_info->account_sid,
+ server_info_out->account_sid)) {
+ torture_fail(tctx,
+ talloc_asprintf(tctx,
+ "(self test) PAC Decode resulted in *different* domain SID: %s != %s",
+ dom_sid_string(mem_ctx, server_info->account_sid),
+ dom_sid_string(mem_ctx, server_info_out->account_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_serversupplied_info *server_info_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, NULL,
+ 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 = krb5_keyblock_init(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 = krb5_keyblock_init(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,
+ lp_iconv_convenience(tctx->lp_ctx),
+ &pac_data,
+ tmp_blob,
+ smb_krb5_context->krb5_context,
+ krbtgt_keyblock_p,
+ &server_keyblock,
+ client_principal, authtime, 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) 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_server_info(mem_ctx,
+ lp_iconv_convenience(tctx->lp_ctx),
+ tmp_blob,
+ smb_krb5_context->krb5_context,
+ &server_info_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) 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"),
+ server_info_out->account_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, server_info_out->account_sid)));
+ }
+
+ talloc_free(server_info_out);
+
+ /* Parse the PAC again, for the logon info this time (using Samba4's parsing) */
+ nt_status = kerberos_pac_logon_info(mem_ctx,
+ lp_iconv_convenience(tctx->lp_ctx),
+ &logon_info,
+ tmp_blob,
+ smb_krb5_context->krb5_context,
+ krbtgt_keyblock_p,
+ &server_keyblock,
+ client_principal, authtime, 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) PAC decoding (for logon info) failed: %s",
+ nt_errstr(nt_status)));
+ }
+
+ validation.sam3 = &logon_info->info3;
+ nt_status = make_server_info_netlogon_validation(mem_ctx,
+ "",
+ 3, &validation,
+ &server_info_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"),
+ server_info_out->account_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, server_info_out->account_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,
+ lp_iconv_convenience(tctx->lp_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,
+ lp_iconv_convenience(tctx->lp_ctx),
+ server_info_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,
+ lp_iconv_convenience(tctx->lp_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,
+ lp_iconv_convenience(tctx->lp_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,
+ lp_iconv_convenience(tctx->lp_ctx),
+ &pac_data,
+ tmp_blob,
+ smb_krb5_context->krb5_context,
+ krbtgt_keyblock_p,
+ &server_keyblock,
+ client_principal,
+ authtime + 1, 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, "(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,
+ lp_iconv_convenience(tctx->lp_ctx),
+ &pac_data,
+ tmp_blob,
+ smb_krb5_context->krb5_context,
+ krbtgt_keyblock_p,
+ &server_keyblock,
+ client_principal,
+ authtime, 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);
+ 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,
+ lp_iconv_convenience(tctx->lp_ctx),
+ &pac_data,
+ tmp_blob,
+ smb_krb5_context->krb5_context,
+ krbtgt_keyblock_p,
+ &server_keyblock,
+ client_principal,
+ authtime, 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);
+ 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/basic/aliases.c b/source4/torture/basic/aliases.c
new file mode 100644
index 0000000000..0309557acb
--- /dev/null
+++ b/source4/torture/basic/aliases.c
@@ -0,0 +1,397 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB trans2 alias scanner
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "../lib/util/dlinklist.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "torture/torture.h"
+#include "libcli/libcli.h"
+#include "torture/util.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;
+
+ 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);
+ 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));
+ 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;
+
+ 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);
+ 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));
+ 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;
+
+ 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);
+ 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);
+ 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;
+
+ 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);
+ 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));
+ 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, "QFILEINFO aliases", qfsinfo_aliases);
+ torture_suite_add_1smb_test(suite, "QFSINFO 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 0000000000..07071bb68a
--- /dev/null
+++ b/source4/torture/basic/attr.c
@@ -0,0 +1,183 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "libcli/libcli.h"
+#include "torture/util.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 {
+ uint_t 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;
+ uint_t 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_comment(tctx, "[%d] trunc open 0x%x -> 0x%x of %s failed - should have succeeded !(%s)\n",
+ k, open_attrs_table[i],
+ open_attrs_table[j],
+ fname, smbcli_errstr(cli1->tree));
+ CHECK_MAX_FAILURES(error_exit);
+ }
+ }
+ torture_assert_ntstatus_equal(tctx,
+ smbcli_nt_error(cli1->tree), NT_STATUS_ACCESS_DENIED,
+ 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],
+ 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_comment(tctx, "[%d] getatr check failed. [0x%x] trunc [0x%x] got attr 0x%x, should be 0x%x\n",
+ k, open_attrs_table[i],
+ open_attrs_table[j],
+ (uint_t)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);
+
+ return true;
+}
+
diff --git a/source4/torture/basic/base.c b/source4/torture/basic/base.c
new file mode 100644
index 0000000000..ea7b6c08fd
--- /dev/null
+++ b/source4/torture/basic/base.c
@@ -0,0 +1,1780 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 "librpc/gen_ndr/ndr_nbt.h"
+#include "lib/events/events.h"
+#include "lib/cmdline/popt_common.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;
+
+ make_nbt_name_client(&calling, lp_netbios_name(tctx->lp_ctx));
+
+ nbt_choose_called_name(NULL, &called, host, NBT_NAME_SERVER);
+
+ cli = smbcli_state_init(NULL);
+ if (!cli) {
+ torture_comment(tctx, "Failed initialize smbcli_struct to connect with %s\n", host);
+ goto failed;
+ }
+
+ lp_smbcli_options(tctx->lp_ctx, &options);
+
+ if (!smbcli_socket_connect(cli, host, lp_smb_ports(tctx->lp_ctx), tctx->ev,
+ lp_resolve_context(tctx->lp_ctx), &options,
+ lp_iconv_convenience(tctx->lp_ctx),
+ lp_socket_options(tctx->lp_ctx))) {
+ torture_comment(tctx, "Failed to connect with %s\n", host);
+ goto failed;
+ }
+
+ if (!smbcli_transport_establish(cli, &calling, &called)) {
+ torture_comment(tctx, "%s rejected the session\n",host);
+ 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_comment(tctx, "getatr failed (%s)\n", smbcli_errstr(cli->tree));
+ correct = false;
+ }
+
+ torture_comment(tctx, "New file time is %s", ctime(&t));
+
+ if (abs(t - time(NULL)) > 60*60*24*10) {
+ torture_comment(tctx, "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_comment(tctx, "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_comment(tctx, "ERROR: qfilename failed (%s)\n", smbcli_errstr(cli->tree));
+ correct = false;
+ }
+
+ if (!pname || strcmp(pname, fname)) {
+ torture_comment(tctx, "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_comment(tctx, "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_comment(tctx, "ERROR: qpathinfo failed (%s)\n", smbcli_errstr(cli->tree));
+ correct = false;
+ } else {
+ 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 (a_time % (60*60) == 0) {
+ torture_comment(tctx, "access time=%s", ctime(&a_time));
+ torture_comment(tctx, "This system appears to set a midnight access time\n");
+ correct = false;
+ }
+
+ if (abs(m_time - time(NULL)) > 60*60*24*7) {
+ torture_comment(tctx, "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_comment(tctx, "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_comment(tctx, "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_comment(tctx, "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_comment(tctx, "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_comment(tctx, "ERROR: qpathinfo2 failed (%s)\n", smbcli_errstr(cli->tree));
+ correct = false;
+ } else {
+ if (m_time2 == m_time) {
+ torture_comment(tctx, "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 smbcli_request *req;
+ req = smb_raw_negotiate_send(cli->transport, lp_unicode(tctx->lp_ctx), PROTOCOL_NT1);
+ event_loop_once(cli->transport->socket->event.ctx);
+ if (req->state == SMBCLI_REQUEST_ERROR) {
+ if (i > 0) {
+ torture_comment(tctx, "Failed to fill pipe packet[%d] - %s (ignored)\n", i+1, nt_errstr(req->status));
+ break;
+ } else {
+ torture_comment(tctx, "Failed to fill pipe - %s \n", nt_errstr(req->status));
+ torture_close_connection(cli);
+ return false;
+ }
+ }
+ }
+
+ torture_comment(tctx, "Opening secondary connection\n");
+ if (!torture_open_connection(&cli2, tctx, 1)) {
+ torture_comment(tctx, "Failed to open secondary connection\n");
+ correct = false;
+ }
+
+ if (!torture_close_connection(cli2)) {
+ torture_comment(tctx, "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_comment(tctx, "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_comment(tctx, "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_comment(tctx, "%s refused 2nd tree connect (%s)\n", host,
+ smbcli_errstr(cli->tree));
+ talloc_free(cli);
+ 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_comment(tctx, "* 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_comment(tctx, "* 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_comment(tctx, "* 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_comment(tctx, "close failed (%s)\n", smbcli_errstr(cli->tree));
+ return false;
+ }
+
+ cli->tree->tid = cnum2;
+
+ if (NT_STATUS_IS_ERR(smbcli_tdis(cli))) {
+ torture_comment(tctx, "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;i<torture_numops;i++)
+ {
+ size_t buf_size = ((uint_t)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);
+
+ if ((bytes_written = smbcli_write(c1->tree, fnum1, 0, buf, 0, buf_size)) != buf_size) {
+ torture_comment(tctx, "write failed (%s)\n", smbcli_errstr(c1->tree));
+ torture_comment(tctx, "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_comment(tctx, "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 retries=4;
+ int i = 0;
+ bool correct = true;
+ int nsec;
+ int msec;
+ double sec;
+
+ nsec = torture_setting_int(tctx, "sharedelay", 1000000);
+ msec = nsec / 1000;
+ sec = ((double)nsec) / ((double) 1000000);
+
+ if (retries <= 0) {
+ torture_comment(tctx, "failed to connect\n");
+ return false;
+ }
+
+ torture_comment(tctx, "Testing deferred open requests.\n");
+
+ while (i < 4) {
+ int fnum = -1;
+
+ do {
+ struct timeval tv;
+ tv = timeval_current();
+ 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);
+ if (fnum != -1) {
+ break;
+ }
+ if (NT_STATUS_EQUAL(smbcli_nt_error(cli->tree),NT_STATUS_SHARING_VIOLATION)) {
+ double e = timeval_elapsed(&tv);
+ if (e < (0.5 * sec) || e > ((1.5 * sec) + 1)) {
+ torture_comment(tctx,"Timing incorrect %.2f violation 1 sec == %.2f\n",
+ e, sec);
+ return false;
+ }
+ }
+ } while (NT_STATUS_EQUAL(smbcli_nt_error(cli->tree),NT_STATUS_SHARING_VIOLATION));
+
+ if (fnum == -1) {
+ torture_comment(tctx,"Failed to open %s, error=%s\n", fname, smbcli_errstr(cli->tree));
+ return false;
+ }
+
+ torture_comment(tctx, "pid %u open %d\n", (unsigned)getpid(), i);
+
+ msleep(10 * msec);
+ i++;
+ if (NT_STATUS_IS_ERR(smbcli_close(cli->tree, fnum))) {
+ torture_comment(tctx,"Failed to close %s, error=%s\n", fname, smbcli_errstr(cli->tree));
+ return false;
+ }
+ msleep(2 * msec);
+ }
+
+ if (NT_STATUS_IS_ERR(smbcli_unlink(cli->tree, fname))) {
+ /* All until the last unlink will fail with sharing violation. */
+ if (!NT_STATUS_EQUAL(smbcli_nt_error(cli->tree),NT_STATUS_SHARING_VIOLATION)) {
+ torture_comment(tctx, "unlink of %s failed (%s)\n", fname, smbcli_errstr(cli->tree));
+ correct = false;
+ }
+ }
+
+ torture_comment(tctx, "deferred test finished\n");
+ 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;
+
+ uint16_t orig_vuid;
+ NTSTATUS result;
+
+ smbcli_unlink(cli->tree, fname);
+
+ fnum = smbcli_open(cli->tree, fname,
+ O_RDWR | O_CREAT | O_TRUNC, DENY_NONE);
+
+ orig_vuid = cli->session->vuid;
+
+ 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;
+
+ asprintf(&control_char_fname, "\\readonly.afile");
+ 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_comment(tctx, "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);
+ }
+ 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_comment(tctx, "open of %s failed (%s)\n", fname, smbcli_errstr(cli1->tree));
+ return false;
+ }
+
+ if (NT_STATUS_IS_ERR(smbcli_close(cli1->tree, fnum1))) {
+ torture_comment(tctx, "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_comment(tctx, "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_comment(tctx, "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_comment(tctx, "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_comment(tctx, "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_comment(tctx, "(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_comment(tctx, "write failed (%s)\n", smbcli_errstr(cli1->tree));
+ correct = false;
+ }
+
+ if (NT_STATUS_IS_ERR(smbcli_close(cli1->tree, fnum1))) {
+ torture_comment(tctx, "(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_comment(tctx, "(3) getatr failed (%s)\n", smbcli_errstr(cli1->tree));
+ CHECK_MAX_FAILURES(error_test3);
+ return false;
+ }
+
+ if (fsize != 20) {
+ torture_comment(tctx, "(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_comment(tctx, "(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_comment(tctx, "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_comment(tctx, "(3) getatr failed (%s)\n", smbcli_errstr(cli1->tree));
+ CHECK_MAX_FAILURES(error_test3);
+ return false;
+ }
+
+ if (fsize != 0) {
+ torture_comment(tctx, "(3) file size != 0\n");
+ CHECK_MAX_FAILURES(error_test3);
+ return false;
+ }
+ torture_comment(tctx, "finished open test 3\n");
+error_test3:
+ smbcli_unlink(cli1->tree, fname);
+
+
+ torture_comment(tctx, "testing ctemp\n");
+ fnum1 = smbcli_ctemp(cli1->tree, "\\", &tmp_path);
+ if (fnum1 == -1) {
+ torture_comment(tctx, "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);
+ 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));
+ }
+error_test4:
+ /* Test the non-io opens... */
+
+ smbcli_setatr(cli2->tree, fname, 0, 0);
+ smbcli_unlink(cli2->tree, fname);
+
+ torture_comment(tctx, "TEST #1 testing 2 non-io opens (no 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_comment(tctx, "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_comment(tctx, "test 1 open 2 of %s failed (%s)\n", fname, smbcli_errstr(cli2->tree));
+ CHECK_MAX_FAILURES(error_test10);
+ return false;
+ }
+
+ 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));
+ return false;
+ }
+ 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));
+ return false;
+ }
+
+ torture_comment(tctx, "non-io open test #1 passed.\n");
+error_test10:
+ smbcli_unlink(cli1->tree, fname);
+
+ torture_comment(tctx, "TEST #2 testing 2 non-io opens (first with delete)\n");
+
+ 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_comment(tctx, "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_comment(tctx, "test 2 open 2 of %s failed (%s)\n", fname, smbcli_errstr(cli2->tree));
+ CHECK_MAX_FAILURES(error_test20);
+ return false;
+ }
+
+ 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));
+ return false;
+ }
+ 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));
+ return false;
+ }
+
+ torture_comment(tctx, "non-io open test #2 passed.\n");
+error_test20:
+ 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_comment(tctx, "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_comment(tctx, "test 3 open 2 of %s failed (%s)\n", fname, smbcli_errstr(cli2->tree));
+ CHECK_MAX_FAILURES(error_test30);
+ return false;
+ }
+
+ 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));
+ return false;
+ }
+ 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));
+ return false;
+ }
+
+ torture_comment(tctx, "non-io open test #3 passed.\n");
+error_test30:
+ smbcli_unlink(cli1->tree, fname);
+
+ torture_comment(tctx, "TEST #4 testing 2 non-io opens (both with delete)\n");
+
+ 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_comment(tctx, "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_comment(tctx, "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");
+
+ 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));
+ return false;
+ }
+
+ torture_comment(tctx, "non-io open test #4 passed.\n");
+error_test40:
+ smbcli_unlink(cli1->tree, fname);
+
+ torture_comment(tctx, "TEST #5 testing 2 non-io opens (both with delete - both with file share delete)\n");
+
+ 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_comment(tctx, "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_comment(tctx, "test 5 open 2 of %s failed (%s)\n", fname, smbcli_errstr(cli2->tree));
+ CHECK_MAX_FAILURES(error_test50);
+ return false;
+ }
+
+ 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));
+ return false;
+ }
+
+ 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));
+ return false;
+ }
+
+ torture_comment(tctx, "non-io open test #5 passed.\n");
+error_test50:
+ torture_comment(tctx, "TEST #6 testing 1 non-io open, one io open\n");
+
+ 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_comment(tctx, "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_comment(tctx, "test 6 open 2 of %s failed (%s)\n", fname, smbcli_errstr(cli2->tree));
+ CHECK_MAX_FAILURES(error_test60);
+ return false;
+ }
+
+ 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));
+ return false;
+ }
+
+ 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));
+ return false;
+ }
+
+ torture_comment(tctx, "non-io open test #6 passed.\n");
+error_test60:
+ torture_comment(tctx, "TEST #7 testing 1 non-io open, one io open with delete\n");
+
+ 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_comment(tctx, "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_comment(tctx, "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");
+
+ 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));
+ return false;
+ }
+
+ torture_comment(tctx, "non-io open test #7 passed.\n");
+
+error_test70:
+
+ torture_comment(tctx, "TEST #8 testing one normal open, followed by lock, followed by open with truncate\n");
+
+ smbcli_unlink(cli1->tree, fname);
+
+ fnum1 = smbcli_open(cli1->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
+ if (fnum1 == -1) {
+ torture_comment(tctx, "(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_comment(tctx, "(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_comment(tctx, "(8) getatr (1) failed (%s)\n", smbcli_errstr(cli1->tree));
+ CHECK_MAX_FAILURES(error_test80);
+ return false;
+ }
+
+ if (fsize != 20) {
+ torture_comment(tctx, "(8) file size != 20\n");
+ 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_comment(tctx, "(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_comment(tctx, "(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_comment(tctx, "(8) getatr (2) failed (%s)\n", smbcli_errstr(cli1->tree));
+ CHECK_MAX_FAILURES(error_test80);
+ return false;
+ }
+
+ if (fsize != 0) {
+ torture_comment(tctx, "(8) file size != 0\n");
+ CHECK_MAX_FAILURES(error_test80);
+ return false;
+ }
+
+ if (NT_STATUS_IS_ERR(smbcli_close(cli1->tree, fnum1))) {
+ torture_comment(tctx, "(8) close1 failed (%s)\n", smbcli_errstr(cli1->tree));
+ return false;
+ }
+
+ if (NT_STATUS_IS_ERR(smbcli_close(cli1->tree, fnum2))) {
+ torture_comment(tctx, "(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 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<num_reads; i++) {
+ ssize_t res;
+ if (ops++ > 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<num_writes; i++) {
+ ssize_t res;
+ if (ops++ > 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)));
+ }
+ }
+
+ return true;
+}
+
+/**
+ 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(cli->tree, "\\chkpath.dir\\*");
+ smbcli_rmdir(cli->tree, "\\chkpath.dir");
+
+ if (NT_STATUS_IS_ERR(smbcli_mkdir(cli->tree, "\\chkpath.dir"))) {
+ torture_comment(tctx, "mkdir1 failed : %s\n", smbcli_errstr(cli->tree));
+ return false;
+ }
+
+ if (NT_STATUS_IS_ERR(smbcli_mkdir(cli->tree, "\\chkpath.dir\\dir2"))) {
+ torture_comment(tctx, "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_comment(tctx, "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_comment(tctx, "chkpath1 failed: %s\n", smbcli_errstr(cli->tree));
+ ret = false;
+ }
+
+ if (NT_STATUS_IS_ERR(smbcli_chkpath(cli->tree, "\\chkpath.dir\\dir2"))) {
+ torture_comment(tctx, "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_comment(tctx, "* 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_comment(tctx, "* 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_comment(tctx, "* chkpath on a non existent component should fail\n");
+ ret = false;
+ }
+
+ smbcli_rmdir(cli->tree, "\\chkpath.dir\\dir2");
+ smbcli_unlink(cli->tree, "\\chkpath.dir\\*");
+ smbcli_rmdir(cli->tree, "\\chkpath.dir");
+
+ return ret;
+}
+
+/*
+ * This is a test to excercise some weird Samba3 error paths.
+ */
+
+static bool torture_samba3_errorpaths(struct torture_context *tctx)
+{
+ bool nt_status_support;
+ 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 = lp_nt_status_support(tctx->lp_ctx);
+
+ if (!lp_set_cmdline(tctx->lp_ctx, "nt status support", "yes")) {
+ torture_comment(tctx, "Could not set 'nt status support = yes'\n");
+ goto fail;
+ }
+
+ if (!torture_open_connection(&cli_nt, tctx, 0)) {
+ goto fail;
+ }
+
+ if (!lp_set_cmdline(tctx->lp_ctx, "nt status support", "no")) {
+ torture_comment(tctx, "Could not set 'nt status support = yes'\n");
+ goto fail;
+ }
+
+ if (!torture_open_connection(&cli_dos, tctx, 1)) {
+ goto fail;
+ }
+
+ if (!lp_set_cmdline(tctx->lp_ctx, "nt status support",
+ nt_status_support ? "yes":"no")) {
+ torture_comment(tctx, "Could not reset 'nt status support = yes'");
+ 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_comment(tctx, "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 = 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_comment(tctx, "(%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_comment(tctx, "(%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_comment(tctx, "(%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_comment(tctx, "(%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_comment(tctx, "(%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_comment(tctx, "(%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_comment(tctx, "(%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_comment(tctx, "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_comment(tctx, "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_comment(tctx, "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_comment(tctx, "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 = 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_comment(tctx, "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_comment(tctx, "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_comment(tctx, "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_comment(tctx, "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_comment(tctx, "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_comment(tctx, "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;
+}
+
+
+NTSTATUS torture_base_init(void)
+{
+ struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "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_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());
+ 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_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());
+ 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_simple_test(suite, "BENCH-HOLDCON", torture_holdcon);
+ 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_smb_multi_test(suite, "SCAN-MAXFID", run_maxfidtest);
+
+ suite->description = talloc_strdup(suite,
+ "Basic SMB tests (imported from the original smbtorture)");
+
+ torture_register_suite(suite);
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/torture/basic/charset.c b/source4/torture/basic/charset.c
new file mode 100644
index 0000000000..5ac299dbbe
--- /dev/null
+++ b/source4/torture/basic/charset.c
@@ -0,0 +1,210 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "libcli/raw/libcliraw.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 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;i<u_name_len;i++) {
+ SSVAL(ucs_name, i*2, u_name[i]);
+ }
+ SSVAL(ucs_name, i*2, 0);
+
+ if (!convert_string_talloc_convenience(ucs_name, lp_iconv_convenience(tctx->lp_ctx), CH_UTF16, CH_UNIX, ucs_name, (1+u_name_len)*2, (void **)&fname, &i, false)) {
+ 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 = 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/delaywrite.c b/source4/torture/basic/delaywrite.c
new file mode 100644
index 0000000000..61678f4e52
--- /dev/null
+++ b/source4/torture/basic/delaywrite.c
@@ -0,0 +1,2892 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#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"
+
+#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;
+ int 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");
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ 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;
+
+ 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) */
+ msleep(3 * msec);
+
+ written = smbcli_write(cli->tree, fnum1, 0, "x", 0, 1);
+
+ if (written != 1) {
+ torture_result(tctx, TORTURE_FAIL,
+ "write failed - wrote %d bytes (%s)\n",
+ (int)written, __location__);
+ return false;
+ }
+
+ start = timeval_current();
+ end = timeval_add(&start, (120*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 < (2 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
+ torture_comment(tctx, "Server updated write_time after %.2f seconds"
+ "(1 sec == %.2f)(wrong!)\n",
+ diff, sec);
+ ret = false;
+ break;
+ }
+
+ torture_comment(tctx, "Server updated write_time after %.2f seconds"
+ "(1 sec == %.2f)(correct)\n",
+ diff, sec);
+ break;
+ }
+ fflush(stdout);
+ 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!)");
+ ret = false;
+ }
+
+
+ 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;
+ int 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];
+
+ torture_comment(tctx, "\nRunning test_delayed_write_update1\n");
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ 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;
+ }
+
+ 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) */
+ 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_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", 1024, 0);
+
+ if (written != 0) {
+ torture_result(tctx, TORTURE_FAIL,
+ "write failed - wrote %d bytes (%s)\n",
+ (int)written, __location__);
+ return false;
+ }
+
+ start = timeval_current();
+ end = timeval_add(&start, (120*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;
+ }
+
+ if (finfo2.all_info.out.size != 1024) {
+ torture_result(tctx, TORTURE_FAIL,
+ "file not truncated, size = %u (should be 1024)",
+ (unsigned int)finfo2.all_info.out.size);
+ ret = false;
+ break;
+ }
+
+ 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) {
+ double diff = timeval_elapsed(&start);
+ if (diff > (0.25 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
+ torture_comment(tctx, "After SMBwrite truncate "
+ "server updated write_time after %.2f seconds"
+ "(1 sec == %.2f)(wrong!)\n",
+ diff, sec);
+ ret = false;
+ break;
+ }
+
+ torture_comment(tctx, "After SMBwrite truncate "
+ "server updated write_time after %.2f seconds"
+ "(1 sec == %.2f)(correct)\n",
+ diff, sec);
+ break;
+ }
+ fflush(stdout);
+ msleep(1 * msec);
+ }
+
+ if (finfo1.all_info.out.write_time == finfo2.all_info.out.write_time) {
+ torture_result(tctx, TORTURE_FAIL,
+ "Server did not update write time (wrong!)");
+ ret = false;
+ }
+
+ /* 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);
+
+ if (written != 1) {
+ torture_result(tctx, TORTURE_FAIL,
+ "write failed - wrote %d bytes (%s)",
+ (int)written, __location__);
+ return false;
+ }
+
+ start = timeval_current();
+ end = timeval_add(&start, (10*sec), 0);
+ while (!timeval_expired(&end)) {
+ status = smb_raw_fileinfo(cli->tree, tctx, &finfo3);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("fileinfo failed: %s\n", nt_errstr(status)));
+ ret = false;
+ break;
+ }
+
+ if (finfo3.all_info.out.size != 1024) {
+ DEBUG(0, ("file not truncated, size = %u (should be 1024)\n",
+ (unsigned int)finfo3.all_info.out.size));
+ ret = false;
+ break;
+ }
+
+ torture_comment(tctx, "write time %s\n",
+ nt_time_string(tctx, finfo3.all_info.out.write_time));
+ if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
+ double diff = timeval_elapsed(&start);
+
+ torture_comment(tctx, "server updated write_time after %.2f seconds"
+ "(1 sec == %.2f)(correct)\n",
+ diff, sec);
+ break;
+ }
+ fflush(stdout);
+ msleep(1 * msec);
+ }
+
+ if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
+ torture_result(tctx, TORTURE_FAIL,
+ "Server updated write time (wrong!)");
+ ret = false;
+ }
+
+ /* 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");
+
+ if (finfo3.all_info.out.write_time == pinfo4.all_info.out.write_time) {
+ torture_result(tctx, TORTURE_FAIL,
+ "Server did not update write time on close (wrong!)");
+ ret = false;
+ } else if (finfo3.all_info.out.write_time < pinfo4.all_info.out.write_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;
+ int 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];
+
+ torture_comment(tctx, "\nRunning test_delayed_write_update1a\n");
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ 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;
+ }
+
+ 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) */
+ 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_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);
+
+ if (written != 0) {
+ torture_result(tctx, TORTURE_FAIL, "write failed - wrote %d bytes (%s)",
+ (int)written, __location__);
+ return false;
+ }
+
+ start = timeval_current();
+ end = timeval_add(&start, (120*sec), 0);
+ while (!timeval_expired(&end)) {
+ status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_result(tctx, TORTURE_FAIL, "fileinfo failed: %s",
+ nt_errstr(status));
+ ret = false;
+ break;
+ }
+
+ if (finfo2.all_info.out.size != 10240) {
+ torture_result(tctx, TORTURE_FAIL,
+ "file not truncated, size = %u (should be 10240)",
+ (unsigned int)finfo2.all_info.out.size);
+ ret = false;
+ break;
+ }
+
+ 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) {
+ double diff = timeval_elapsed(&start);
+ if (diff > (0.25 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
+ torture_comment(tctx, "After SMBwrite truncate "
+ "server updated write_time after %.2f seconds"
+ "(1 sec == %.2f)(wrong!)\n",
+ diff, sec);
+ ret = false;
+ break;
+ }
+
+ torture_comment(tctx, "After SMBwrite truncate "
+ "server updated write_time after %.2f seconds"
+ "(1 sec == %.2f)(correct)\n",
+ diff, sec);
+ break;
+ }
+ fflush(stdout);
+ msleep(1 * msec);
+ }
+
+ if (finfo1.all_info.out.write_time == finfo2.all_info.out.write_time) {
+ torture_result(tctx, TORTURE_FAIL,
+ "Server did not update write time (wrong!)");
+ ret = false;
+ }
+
+ /* 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);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_result(tctx, TORTURE_FAIL, "fileinfo failed: %s\n",
+ nt_errstr(status));
+ ret = false;
+ break;
+ }
+
+ if (finfo3.all_info.out.size != 10240) {
+ torture_result(tctx, TORTURE_FAIL,
+ "file not truncated, size = %u (should be 10240)",
+ (unsigned int)finfo3.all_info.out.size);
+ ret = false;
+ break;
+ }
+
+ torture_comment(tctx, "write time %s\n",
+ nt_time_string(tctx, finfo3.all_info.out.write_time));
+ if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
+ double diff = timeval_elapsed(&start);
+
+ torture_comment(tctx, "server updated write_time after %.2f seconds"
+ "(1 sec == %.2f)(correct)\n",
+ diff, sec);
+ break;
+ }
+ fflush(stdout);
+ msleep(1 * msec);
+ }
+
+ if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
+ torture_result(tctx, TORTURE_FAIL,
+ "Server updated write time (wrong!)");
+ ret = false;
+ }
+
+ /* 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");
+
+ if (finfo3.all_info.out.write_time == pinfo4.all_info.out.write_time) {
+ torture_result(tctx, TORTURE_FAIL,
+ "Server did not update write time on close (wrong!)");
+ ret = false;
+ } else if (finfo3.all_info.out.write_time < pinfo4.all_info.out.write_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;
+ int 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];
+
+ torture_comment(tctx, "\nRunning test_delayed_write_update1b\n");
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ 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;
+ }
+
+ 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) */
+ 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_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);
+ 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;
+ }
+
+ if (finfo2.all_info.out.size != 10240) {
+ torture_result(tctx, TORTURE_FAIL,
+ "file not truncated (size = %u, should be 10240)",
+ (unsigned int)finfo2.all_info.out.size );
+ ret = false;
+ break;
+ }
+
+ 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) {
+ double diff = timeval_elapsed(&start);
+ if (diff > (0.25 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
+ torture_result(tctx, TORTURE_FAIL,
+ "After SET_END_OF_FILE truncate "
+ "server updated write_time after %.2f seconds"
+ "(1 sec == %.2f)(wrong!)",
+ diff, sec);
+ ret = false;
+ break;
+ }
+
+ torture_comment(tctx, "After SET_END_OF_FILE truncate "
+ "server updated write_time after %.2f seconds"
+ "(1 sec == %.2f)(correct)\n",
+ diff, sec);
+ break;
+ }
+ fflush(stdout);
+ msleep(1 * msec);
+ }
+
+ if (finfo1.all_info.out.write_time == finfo2.all_info.out.write_time) {
+ torture_result(tctx, TORTURE_FAIL,
+ "Server did not update write time (wrong!)");
+ ret = false;
+ }
+
+ /* 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);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_result(tctx, TORTURE_FAIL,
+ "fileinfo failed: %s", nt_errstr(status));
+ ret = false;
+ break;
+ }
+
+ if (finfo3.all_info.out.size != 10240) {
+ DEBUG(0, ("file not truncated (size = %u, should be 10240)\n",
+ (unsigned int)finfo3.all_info.out.size ));
+ ret = false;
+ break;
+ }
+
+ torture_comment(tctx, "write time %s\n",
+ nt_time_string(tctx, finfo3.all_info.out.write_time));
+ if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
+ double diff = timeval_elapsed(&start);
+
+ torture_comment(tctx, "server updated write_time after %.2f seconds"
+ "(1 sec == %.2f)(correct)\n",
+ diff, sec);
+ break;
+ }
+ fflush(stdout);
+ msleep(1 * msec);
+ }
+
+ if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
+ torture_result(tctx, TORTURE_FAIL, "Server updated write time (wrong!)\n");
+ ret = false;
+ }
+
+ /* 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");
+
+ if (finfo3.all_info.out.write_time == pinfo4.all_info.out.write_time) {
+ torture_result(tctx, TORTURE_FAIL, "Server did not update write time on close (wrong!)\n");
+ ret = false;
+ } else if (finfo3.all_info.out.write_time < pinfo4.all_info.out.write_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;
+ int 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];
+
+ torture_comment(tctx, "\nRunning test_delayed_write_update1c\n");
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ 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;
+ }
+
+ 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) */
+ 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_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);
+ while (!timeval_expired(&end)) {
+ status = smb_raw_fileinfo(cli->tree, tctx, &finfo2);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_result(tctx, TORTURE_FAIL, "fileinfo failed: %s",
+ nt_errstr(status));
+ ret = false;
+ break;
+ }
+
+ if (finfo2.all_info.out.size != 0) {
+ torture_result(tctx, TORTURE_FAIL,
+ "file not truncated (size = %u, should be 10240)",
+ (unsigned int)finfo2.all_info.out.size);
+ ret = false;
+ break;
+ }
+
+ 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) {
+ double diff = timeval_elapsed(&start);
+ if (diff > (0.25 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
+ torture_comment(tctx, "After SET_ALLOCATION_INFO truncate "
+ "server updated write_time after %.2f seconds"
+ "(1 sec == %.2f)(wrong!)\n",
+ diff, sec);
+ ret = false;
+ break;
+ }
+
+ torture_comment(tctx, "After SET_ALLOCATION_INFO truncate "
+ "server updated write_time after %.2f seconds"
+ "(1 sec == %.2f)(correct)\n",
+ diff, sec);
+ break;
+ }
+ fflush(stdout);
+ msleep(1 * msec);
+ }
+
+ if (finfo1.all_info.out.write_time == finfo2.all_info.out.write_time) {
+ torture_result(tctx, TORTURE_FAIL,
+ "Server did not update write time (wrong!)");
+ ret = false;
+ }
+
+ /* 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);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_result(tctx, TORTURE_FAIL, "fileinfo failed: %s",
+ nt_errstr(status));
+ ret = false;
+ break;
+ }
+
+ if (finfo3.all_info.out.size != 1) {
+ torture_result(tctx, TORTURE_FAIL, "file not expanded");
+ ret = false;
+ break;
+ }
+
+ torture_comment(tctx, "write time %s\n",
+ nt_time_string(tctx, finfo3.all_info.out.write_time));
+ if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
+ double diff = timeval_elapsed(&start);
+
+ torture_comment(tctx, "server updated write_time after %.2f seconds"
+ "(1 sec == %.2f)(correct)\n",
+ diff, sec);
+ break;
+ }
+ fflush(stdout);
+ msleep(1 * msec);
+ }
+
+ if (finfo2.all_info.out.write_time != finfo3.all_info.out.write_time) {
+ torture_result(tctx, TORTURE_FAIL,
+ "Server updated write time (wrong!)");
+ ret = false;
+ }
+
+ /* 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");
+
+ if (finfo3.all_info.out.write_time == pinfo4.all_info.out.write_time) {
+ torture_result(tctx, TORTURE_FAIL, "Server did not update write time on close (wrong!)\n");
+ ret = false;
+ } else if (finfo3.all_info.out.write_time < pinfo4.all_info.out.write_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;
+}
+
+/*
+ * 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;
+ int 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");
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ 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) */
+ 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_comment(tctx, "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_comment(tctx, "Server updated write_time after %.2f seconds"
+ "(1sec == %.2f) (wrong!)\n",
+ diff, sec);
+ ret = false;
+ break;
+ }
+ fflush(stdout);
+ 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");
+ }
+
+ fnum2 = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
+ if (fnum2 == -1) {
+ torture_comment(tctx, "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_comment(tctx, "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_comment(tctx, "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_comment(tctx, "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_comment(tctx, "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_comment(tctx, "Server updated write_time after %.2f seconds "
+ "(1sec == %.2f) (wrong!)\n",
+ diff, sec);
+ ret = false;
+ break;
+ }
+ fflush(stdout);
+ 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));
+
+ 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_comment(tctx, "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_comment(tctx, "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 < (2 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
+ torture_comment(tctx, "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"
+ "(1sec == %.2f) (correct)\n",
+ diff, sec);
+ break;
+ }
+ fflush(stdout);
+ 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;
+ int 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");
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ 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;
+ }
+
+ 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_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 = finfo1.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)
+
+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, finfo4;
+ union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5;
+ const char *fname = BASEDIR "\\torture_file3.txt";
+ int fnum1 = -1;
+ bool ret = true;
+ ssize_t written;
+ struct timeval start;
+ struct timeval end;
+ int 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");
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ 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;
+ 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;
+
+ /* get the initial times */
+ GET_INFO_BOTH(finfo0,pinfo0);
+
+ /*
+ * make sure the write time is updated 2 seconds later
+ * calcuated from the first write
+ * (but expect upto 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 < (2 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
+ torture_comment(tctx, "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 "
+ "(1sec == %.2f) (correct)\n",
+ diff, sec);
+ break;
+ }
+ 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_comment(tctx, "Server updated write_time after %.2f seconds "
+ "(1sec == %.2f) (wrong!)\n",
+ diff, sec);
+ ret = false;
+ break;
+ }
+ msleep(2 * 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 */
+ 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, finfo4;
+ union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5;
+ const char *fname = BASEDIR "\\torture_file3a.txt";
+ int fnum1 = -1;
+ bool ret = true;
+ ssize_t written;
+ int i;
+ struct timeval start;
+ struct timeval end;
+ int 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");
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ 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;
+ 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;
+
+ /* 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
+ */
+ 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
+ * calcuated from the first write
+ * (but expect upto 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 < (2 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
+ torture_comment(tctx, "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 "
+ "(1sec == %.2f) (correct)\n",
+ diff, sec);
+ break;
+ }
+ msleep(0.5 * msec);
+ }
+
+ GET_INFO_BOTH(finfo1,pinfo1);
+ COMPARE_WRITE_TIME_GREATER(pinfo1, pinfo0);
+
+ /*
+ * demonstrate that a truncate write always
+ * updates the write time immediately
+ */
+ for (i=0; i < 3; i++) {
+ msleep(1 * 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;
+ }
+
+ /* 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_comment(tctx, "Server updated write_time after %.2f seconds "
+ "(1sec == %.2f) (wrong!)\n",
+ diff, sec);
+ ret = false;
+ break;
+ }
+ msleep(2 * 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 */
+ 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++) {
+ msleep(1 * 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 */
+ msleep(5 * 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, finfo4;
+ union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5;
+ const char *fname = BASEDIR "\\torture_file3b.txt";
+ int fnum1 = -1;
+ bool ret = true;
+ ssize_t written;
+ struct timeval start;
+ struct timeval end;
+ int 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");
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ 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;
+ 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;
+
+ /* 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
+ */
+ 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
+ * calcuated from the first write
+ * (but expect upto 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 < (2 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
+ torture_comment(tctx, "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 "
+ "(1sec == %.2f) (correct)\n",
+ diff, sec);
+ break;
+ }
+ 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_comment(tctx, "Server updated write_time after %.2f seconds "
+ "(1sec == %.2f) (wrong!)\n",
+ diff, sec);
+ ret = false;
+ break;
+ }
+ msleep(2 * 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 */
+ 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, finfo4;
+ union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5;
+ const char *fname = BASEDIR "\\torture_file3c.txt";
+ int fnum1 = -1;
+ bool ret = true;
+ ssize_t written;
+ int i;
+ struct timeval start;
+ struct timeval end;
+ int 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");
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ 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;
+ 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;
+
+ /* 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
+ */
+ 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++) {
+ msleep(1 * 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_comment(tctx, "Server updated write_time after %.2f seconds "
+ "(1sec == %.2f) (wrong!)\n",
+ diff, sec);
+ ret = false;
+ break;
+ }
+ msleep(2 * 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 */
+ 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++) {
+ msleep(1 * 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 */
+ 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_comment(tctx, "Server updated write_time after %.2f seconds "
+ "(1sec == %.2f) (wrong!)\n",
+ diff, sec);
+ ret = false;
+ break;
+ }
+ msleep(2 * 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 */
+ 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, finfo4;
+ union smb_fileinfo pinfo0, pinfo1, pinfo2, pinfo3, pinfo4, pinfo5;
+ const char *fname = BASEDIR "\\torture_file4.txt";
+ int fnum1 = -1;
+ bool ret = true;
+ ssize_t written;
+ struct timeval start;
+ struct timeval end;
+ int 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");
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ 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;
+ 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;
+
+ /* get the initial times */
+ GET_INFO_BOTH(finfo0,pinfo0);
+
+ /* sleep a bit */
+ 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
+ * calcuated from the first write
+ * (but expect upto 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 < (2 * sec * 0.75)) { /* 0.75 to cope with vmware timing */
+ torture_comment(tctx, "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 "
+ "(1sec == %.2f) (correct)\n",
+ diff, sec);
+ break;
+ }
+ 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_comment(tctx, "Server updated write_time after %.2f seconds "
+ "(1sec == %.2f) (wrong!)\n",
+ diff, sec);
+ ret = false;
+ break;
+ }
+ msleep(2 * 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 */
+ 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;
+ int 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");
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ 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_comment(tctx, "Server updated write_time after %.2f seconds "
+ "(1sec == %.2f) (wrong!)\n",
+ diff, sec);
+ ret = false;
+ break;
+ }
+ msleep(2 * 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_comment(tctx, "Server updated write_time after %.2f seconds "
+ "(1sec == %.2f) (wrong!)\n",
+ diff, sec);
+ ret = false;
+ break;
+ }
+ msleep(2 * 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 */
+ 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;
+ int 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");
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ 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_comment(tctx, "Server updated write_time after %.2f seconds "
+ "(1sec == %.2f) (wrong!)\n",
+ diff, sec);
+ ret = false;
+ break;
+ }
+ msleep(2 * 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_comment(tctx, "Server updated write_time after %.2f seconds "
+ "(1sec == %.2f) (wrong!)\n",
+ diff, sec);
+ ret = false;
+ break;
+ }
+ msleep(2 * 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 */
+ 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;
+ int 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");
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+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, 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_comment(tctx, "Server updated write_time after %.2f seconds "
+ "(1sec == %.2f) (wrong!)\n",
+ diff, sec);
+ ret = false;
+ break;
+ }
+ msleep(2 * 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_comment(tctx, "Server updated write_time after %.2f seconds "
+ "(1sec == %.2f) (wrong!)\n",
+ diff, sec);
+ ret = false;
+ break;
+ }
+ msleep(2 * 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 */
+ 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");
+ }
+
+ /* 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;
+}
+
+/*
+ testing of delayed update of write_time
+*/
+struct torture_suite *torture_delay_write(void)
+{
+ struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "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);
+
+ return suite;
+}
diff --git a/source4/torture/basic/delete.c b/source4/torture/basic/delete.c
new file mode 100644
index 0000000000..c1ac62f1b1
--- /dev/null
+++ b/source4/torture/basic/delete.c
@@ -0,0 +1,1558 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/libcli.h"
+#include "torture/torture.h"
+#include "torture/util.h"
+#include "system/filesys.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+
+#include "torture/raw/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;
+
+ del_clean_area(cli1, cli2);
+
+ /* This should fail - we need to set DELETE_ACCESS. */
+ fnum1 = smbcli_nt_create_full(cli1->tree, fname, 0,
+ SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA,
+ 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 succeeded should have failed!",
+ fname));
+
+ 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),
+ "setting 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));
+
+ 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 - 1 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 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;
+
+ del_clean_area(cli1, cli2);
+
+ /* Test 20 -- non-empty directory hardest to get right... */
+
+ if (torture_setting_bool(tctx, "samba3", false)) {
+ return true;
+ }
+
+ 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;
+ asprintf(&fullname, "\\%s%s", dname, fname);
+ 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,
+ "setting delete_on_close on file failed !");
+
+ {
+ char *fullname;
+ asprintf(&fullname, "\\%s%s", dname, fname);
+ 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 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
+ * enogh to delete the file for this test */
+ 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);
+
+ return correct;
+}
+
+/*
+ Test delete on close semantics.
+ */
+struct torture_suite *torture_test_delete(void)
+{
+ struct torture_suite *suite = torture_suite_create(
+ talloc_autofree_context(),
+ "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, "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, "deltest17", deltest17);
+ 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_simple_test(suite, "deltest21", deltest21);
+ torture_suite_add_simple_test(suite, "deltest22", deltest22);
+
+ return suite;
+}
diff --git a/source4/torture/basic/denytest.c b/source4/torture/basic/denytest.c
new file mode 100644
index 0000000000..bc64d4b2ff
--- /dev/null
+++ b/source4/torture/basic/denytest.c
@@ -0,0 +1,2033 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "system/filesys.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/libcli.h"
+#include "torture/util.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;ARRAY_SIZE(results);i++) {
+ if (results[i].res == res) return results[i].name;
+ }
+ return "*";
+}
+
+static const struct {
+ int isexe;
+ int mode1, deny1;
+ int mode2, deny2;
+ enum deny_result result;
+} denytable2[] = {
+{1, O_RDWR, DENY_DOS, O_RDWR, DENY_DOS, A_RW},
+{1, O_RDWR, DENY_DOS, O_RDONLY, DENY_DOS, A_R},
+{1, O_RDWR, DENY_DOS, O_WRONLY, DENY_DOS, A_W},
+{1, O_RDWR, DENY_DOS, O_RDWR, DENY_ALL, A_0},
+{1, O_RDWR, DENY_DOS, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_DOS, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_DOS, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_DOS, O_RDONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_DOS, O_WRONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_DOS, O_RDWR, DENY_READ, A_0},
+{1, O_RDWR, DENY_DOS, O_RDONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_DOS, O_WRONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_DOS, O_RDWR, DENY_NONE, A_RW},
+{1, O_RDWR, DENY_DOS, O_RDONLY, DENY_NONE, A_R},
+{1, O_RDWR, DENY_DOS, O_WRONLY, DENY_NONE, A_W},
+{1, O_RDWR, DENY_DOS, O_RDWR, DENY_FCB, A_0},
+{1, O_RDWR, DENY_DOS, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_DOS, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_DOS, O_RDWR, DENY_DOS, A_RW},
+{1, O_RDONLY, DENY_DOS, O_RDONLY, DENY_DOS, A_R},
+{1, O_RDONLY, DENY_DOS, O_WRONLY, DENY_DOS, A_W},
+{1, O_RDONLY, DENY_DOS, O_RDWR, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_DOS, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_DOS, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_DOS, O_RDWR, DENY_WRITE, A_RW},
+{1, O_RDONLY, DENY_DOS, O_RDONLY, DENY_WRITE, A_R},
+{1, O_RDONLY, DENY_DOS, O_WRONLY, DENY_WRITE, A_W},
+{1, O_RDONLY, DENY_DOS, O_RDWR, DENY_READ, A_0},
+{1, O_RDONLY, DENY_DOS, O_RDONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_DOS, O_WRONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_DOS, O_RDWR, DENY_NONE, A_RW},
+{1, O_RDONLY, DENY_DOS, O_RDONLY, DENY_NONE, A_R},
+{1, O_RDONLY, DENY_DOS, O_WRONLY, DENY_NONE, A_W},
+{1, O_RDONLY, DENY_DOS, O_RDWR, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_DOS, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_DOS, O_WRONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_DOS, O_RDWR, DENY_DOS, A_RW},
+{1, O_WRONLY, DENY_DOS, O_RDONLY, DENY_DOS, A_R},
+{1, O_WRONLY, DENY_DOS, O_WRONLY, DENY_DOS, A_W},
+{1, O_WRONLY, DENY_DOS, O_RDWR, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_DOS, O_RDONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_DOS, O_WRONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_DOS, O_RDWR, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_DOS, O_RDONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_DOS, O_WRONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_DOS, O_RDWR, DENY_READ, A_RW},
+{1, O_WRONLY, DENY_DOS, O_RDONLY, DENY_READ, A_R},
+{1, O_WRONLY, DENY_DOS, O_WRONLY, DENY_READ, A_W},
+{1, O_WRONLY, DENY_DOS, O_RDWR, DENY_NONE, A_RW},
+{1, O_WRONLY, DENY_DOS, O_RDONLY, DENY_NONE, A_R},
+{1, O_WRONLY, DENY_DOS, O_WRONLY, DENY_NONE, A_W},
+{1, O_WRONLY, DENY_DOS, O_RDWR, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_DOS, O_RDONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_DOS, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_ALL, O_RDWR, DENY_DOS, A_0},
+{1, O_RDWR, DENY_ALL, O_RDONLY, DENY_DOS, A_0},
+{1, O_RDWR, DENY_ALL, O_WRONLY, DENY_DOS, A_0},
+{1, O_RDWR, DENY_ALL, O_RDWR, DENY_ALL, A_0},
+{1, O_RDWR, DENY_ALL, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_ALL, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_ALL, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_ALL, O_RDONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_ALL, O_WRONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_ALL, O_RDWR, DENY_READ, A_0},
+{1, O_RDWR, DENY_ALL, O_RDONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_ALL, O_WRONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_ALL, O_RDWR, DENY_NONE, A_0},
+{1, O_RDWR, DENY_ALL, O_RDONLY, DENY_NONE, A_0},
+{1, O_RDWR, DENY_ALL, O_WRONLY, DENY_NONE, A_0},
+{1, O_RDWR, DENY_ALL, O_RDWR, DENY_FCB, A_0},
+{1, O_RDWR, DENY_ALL, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_ALL, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDWR, DENY_DOS, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDONLY, DENY_DOS, A_0},
+{1, O_RDONLY, DENY_ALL, O_WRONLY, DENY_DOS, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDWR, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_ALL, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDONLY, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_ALL, O_WRONLY, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDWR, DENY_READ, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_ALL, O_WRONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDWR, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDONLY, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_ALL, O_WRONLY, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDWR, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_ALL, O_WRONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDWR, DENY_DOS, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDONLY, DENY_DOS, A_0},
+{1, O_WRONLY, DENY_ALL, O_WRONLY, DENY_DOS, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDWR, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_ALL, O_WRONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDWR, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_ALL, O_WRONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDWR, DENY_READ, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDONLY, DENY_READ, A_0},
+{1, O_WRONLY, DENY_ALL, O_WRONLY, DENY_READ, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDWR, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDONLY, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_ALL, O_WRONLY, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDWR, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_ALL, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDWR, DENY_DOS, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDONLY, DENY_DOS, A_R},
+{1, O_RDWR, DENY_WRITE, O_WRONLY, DENY_DOS, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDWR, DENY_ALL, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_WRITE, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_WRITE, O_WRONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDWR, DENY_READ, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_WRITE, O_WRONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDWR, DENY_NONE, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDONLY, DENY_NONE, A_R},
+{1, O_RDWR, DENY_WRITE, O_WRONLY, DENY_NONE, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDWR, DENY_FCB, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_WRITE, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDWR, DENY_DOS, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_DOS, A_R},
+{1, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_DOS, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDWR, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_WRITE, A_R},
+{1, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDWR, DENY_READ, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDWR, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_NONE, A_R},
+{1, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDWR, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDWR, DENY_DOS, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_DOS, A_R},
+{1, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_DOS, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDWR, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDWR, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDWR, DENY_READ, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_READ, A_R},
+{1, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_READ, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDWR, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_NONE, A_R},
+{1, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDWR, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_READ, O_RDWR, DENY_DOS, A_0},
+{1, O_RDWR, DENY_READ, O_RDONLY, DENY_DOS, A_0},
+{1, O_RDWR, DENY_READ, O_WRONLY, DENY_DOS, A_W},
+{1, O_RDWR, DENY_READ, O_RDWR, DENY_ALL, A_0},
+{1, O_RDWR, DENY_READ, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_READ, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_READ, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_READ, O_RDONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_READ, O_WRONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_READ, O_RDWR, DENY_READ, A_0},
+{1, O_RDWR, DENY_READ, O_RDONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_READ, O_WRONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_READ, O_RDWR, DENY_NONE, A_0},
+{1, O_RDWR, DENY_READ, O_RDONLY, DENY_NONE, A_0},
+{1, O_RDWR, DENY_READ, O_WRONLY, DENY_NONE, A_W},
+{1, O_RDWR, DENY_READ, O_RDWR, DENY_FCB, A_0},
+{1, O_RDWR, DENY_READ, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_READ, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_READ, O_RDWR, DENY_DOS, A_0},
+{1, O_RDONLY, DENY_READ, O_RDONLY, DENY_DOS, A_0},
+{1, O_RDONLY, DENY_READ, O_WRONLY, DENY_DOS, A_W},
+{1, O_RDONLY, DENY_READ, O_RDWR, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_READ, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_READ, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_READ, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_READ, O_RDONLY, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_READ, O_WRONLY, DENY_WRITE, A_W},
+{1, O_RDONLY, DENY_READ, O_RDWR, DENY_READ, A_0},
+{1, O_RDONLY, DENY_READ, O_RDONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_READ, O_WRONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_READ, O_RDWR, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_READ, O_RDONLY, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_READ, O_WRONLY, DENY_NONE, A_W},
+{1, O_RDONLY, DENY_READ, O_RDWR, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_READ, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_READ, O_WRONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_READ, O_RDWR, DENY_DOS, A_0},
+{1, O_WRONLY, DENY_READ, O_RDONLY, DENY_DOS, A_0},
+{1, O_WRONLY, DENY_READ, O_WRONLY, DENY_DOS, A_W},
+{1, O_WRONLY, DENY_READ, O_RDWR, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_READ, O_RDONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_READ, O_WRONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_READ, O_RDWR, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_READ, O_RDONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_READ, O_WRONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_READ, O_RDWR, DENY_READ, A_0},
+{1, O_WRONLY, DENY_READ, O_RDONLY, DENY_READ, A_0},
+{1, O_WRONLY, DENY_READ, O_WRONLY, DENY_READ, A_W},
+{1, O_WRONLY, DENY_READ, O_RDWR, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_READ, O_RDONLY, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_READ, O_WRONLY, DENY_NONE, A_W},
+{1, O_WRONLY, DENY_READ, O_RDWR, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_READ, O_RDONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_READ, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_NONE, O_RDWR, DENY_DOS, A_RW},
+{1, O_RDWR, DENY_NONE, O_RDONLY, DENY_DOS, A_R},
+{1, O_RDWR, DENY_NONE, O_WRONLY, DENY_DOS, A_W},
+{1, O_RDWR, DENY_NONE, O_RDWR, DENY_ALL, A_0},
+{1, O_RDWR, DENY_NONE, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_NONE, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_NONE, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_NONE, O_RDONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_NONE, O_WRONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_NONE, O_RDWR, DENY_READ, A_0},
+{1, O_RDWR, DENY_NONE, O_RDONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_NONE, O_WRONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_NONE, O_RDWR, DENY_NONE, A_RW},
+{1, O_RDWR, DENY_NONE, O_RDONLY, DENY_NONE, A_R},
+{1, O_RDWR, DENY_NONE, O_WRONLY, DENY_NONE, A_W},
+{1, O_RDWR, DENY_NONE, O_RDWR, DENY_FCB, A_0},
+{1, O_RDWR, DENY_NONE, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_NONE, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_NONE, O_RDWR, DENY_DOS, A_RW},
+{1, O_RDONLY, DENY_NONE, O_RDONLY, DENY_DOS, A_R},
+{1, O_RDONLY, DENY_NONE, O_WRONLY, DENY_DOS, A_W},
+{1, O_RDONLY, DENY_NONE, O_RDWR, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_NONE, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_NONE, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_NONE, O_RDWR, DENY_WRITE, A_RW},
+{1, O_RDONLY, DENY_NONE, O_RDONLY, DENY_WRITE, A_R},
+{1, O_RDONLY, DENY_NONE, O_WRONLY, DENY_WRITE, A_W},
+{1, O_RDONLY, DENY_NONE, O_RDWR, DENY_READ, A_0},
+{1, O_RDONLY, DENY_NONE, O_RDONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_NONE, O_WRONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_NONE, O_RDWR, DENY_NONE, A_RW},
+{1, O_RDONLY, DENY_NONE, O_RDONLY, DENY_NONE, A_R},
+{1, O_RDONLY, DENY_NONE, O_WRONLY, DENY_NONE, A_W},
+{1, O_RDONLY, DENY_NONE, O_RDWR, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_NONE, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_NONE, O_WRONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_NONE, O_RDWR, DENY_DOS, A_RW},
+{1, O_WRONLY, DENY_NONE, O_RDONLY, DENY_DOS, A_R},
+{1, O_WRONLY, DENY_NONE, O_WRONLY, DENY_DOS, A_W},
+{1, O_WRONLY, DENY_NONE, O_RDWR, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_NONE, O_RDONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_NONE, O_WRONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_NONE, O_RDWR, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_NONE, O_RDONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_NONE, O_WRONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_NONE, O_RDWR, DENY_READ, A_RW},
+{1, O_WRONLY, DENY_NONE, O_RDONLY, DENY_READ, A_R},
+{1, O_WRONLY, DENY_NONE, O_WRONLY, DENY_READ, A_W},
+{1, O_WRONLY, DENY_NONE, O_RDWR, DENY_NONE, A_RW},
+{1, O_WRONLY, DENY_NONE, O_RDONLY, DENY_NONE, A_R},
+{1, O_WRONLY, DENY_NONE, O_WRONLY, DENY_NONE, A_W},
+{1, O_WRONLY, DENY_NONE, O_RDWR, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_NONE, O_RDONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_NONE, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_FCB, O_RDWR, DENY_DOS, A_0},
+{1, O_RDWR, DENY_FCB, O_RDONLY, DENY_DOS, A_0},
+{1, O_RDWR, DENY_FCB, O_WRONLY, DENY_DOS, A_0},
+{1, O_RDWR, DENY_FCB, O_RDWR, DENY_ALL, A_0},
+{1, O_RDWR, DENY_FCB, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_FCB, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_FCB, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_FCB, O_RDONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_FCB, O_WRONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_FCB, O_RDWR, DENY_READ, A_0},
+{1, O_RDWR, DENY_FCB, O_RDONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_FCB, O_WRONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_FCB, O_RDWR, DENY_NONE, A_0},
+{1, O_RDWR, DENY_FCB, O_RDONLY, DENY_NONE, A_0},
+{1, O_RDWR, DENY_FCB, O_WRONLY, DENY_NONE, A_0},
+{1, O_RDWR, DENY_FCB, O_RDWR, DENY_FCB, A_0},
+{1, O_RDWR, DENY_FCB, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_FCB, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDWR, DENY_DOS, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDONLY, DENY_DOS, A_0},
+{1, O_RDONLY, DENY_FCB, O_WRONLY, DENY_DOS, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDWR, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_FCB, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDONLY, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_FCB, O_WRONLY, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDWR, DENY_READ, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_FCB, O_WRONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDWR, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDONLY, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_FCB, O_WRONLY, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDWR, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_FCB, O_WRONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDWR, DENY_DOS, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDONLY, DENY_DOS, A_0},
+{1, O_WRONLY, DENY_FCB, O_WRONLY, DENY_DOS, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDWR, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_FCB, O_WRONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDWR, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_FCB, O_WRONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDWR, DENY_READ, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDONLY, DENY_READ, A_0},
+{1, O_WRONLY, DENY_FCB, O_WRONLY, DENY_READ, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDWR, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDONLY, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_FCB, O_WRONLY, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDWR, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_FCB, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_DOS, O_RDWR, DENY_DOS, A_0},
+{0, O_RDWR, DENY_DOS, O_RDONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_DOS, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_DOS, O_RDWR, DENY_ALL, A_0},
+{0, O_RDWR, DENY_DOS, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_DOS, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_DOS, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_DOS, O_RDONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_DOS, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_DOS, O_RDWR, DENY_READ, A_0},
+{0, O_RDWR, DENY_DOS, O_RDONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_DOS, O_WRONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_DOS, O_RDWR, DENY_NONE, A_0},
+{0, O_RDWR, DENY_DOS, O_RDONLY, DENY_NONE, A_0},
+{0, O_RDWR, DENY_DOS, O_WRONLY, DENY_NONE, A_0},
+{0, O_RDWR, DENY_DOS, O_RDWR, DENY_FCB, A_0},
+{0, O_RDWR, DENY_DOS, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_DOS, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDWR, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDONLY, DENY_DOS, A_R},
+{0, O_RDONLY, DENY_DOS, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDWR, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_DOS, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDONLY, DENY_WRITE, A_R},
+{0, O_RDONLY, DENY_DOS, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDWR, DENY_READ, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_DOS, O_WRONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDWR, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDONLY, DENY_NONE, A_R},
+{0, O_RDONLY, DENY_DOS, O_WRONLY, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDWR, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_DOS, O_WRONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDWR, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_DOS, O_WRONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDWR, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_DOS, O_WRONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDWR, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_DOS, O_WRONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDWR, DENY_READ, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDONLY, DENY_READ, A_0},
+{0, O_WRONLY, DENY_DOS, O_WRONLY, DENY_READ, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDWR, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDONLY, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_DOS, O_WRONLY, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDWR, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_DOS, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_ALL, O_RDWR, DENY_DOS, A_0},
+{0, O_RDWR, DENY_ALL, O_RDONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_ALL, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_ALL, O_RDWR, DENY_ALL, A_0},
+{0, O_RDWR, DENY_ALL, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_ALL, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_ALL, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_ALL, O_RDONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_ALL, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_ALL, O_RDWR, DENY_READ, A_0},
+{0, O_RDWR, DENY_ALL, O_RDONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_ALL, O_WRONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_ALL, O_RDWR, DENY_NONE, A_0},
+{0, O_RDWR, DENY_ALL, O_RDONLY, DENY_NONE, A_0},
+{0, O_RDWR, DENY_ALL, O_WRONLY, DENY_NONE, A_0},
+{0, O_RDWR, DENY_ALL, O_RDWR, DENY_FCB, A_0},
+{0, O_RDWR, DENY_ALL, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_ALL, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDWR, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDONLY, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_ALL, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDWR, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_ALL, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDONLY, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_ALL, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDWR, DENY_READ, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_ALL, O_WRONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDWR, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDONLY, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_ALL, O_WRONLY, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDWR, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_ALL, O_WRONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDWR, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_ALL, O_WRONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDWR, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_ALL, O_WRONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDWR, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_ALL, O_WRONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDWR, DENY_READ, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDONLY, DENY_READ, A_0},
+{0, O_WRONLY, DENY_ALL, O_WRONLY, DENY_READ, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDWR, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDONLY, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_ALL, O_WRONLY, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDWR, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_ALL, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDWR, DENY_DOS, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_WRITE, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDWR, DENY_ALL, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_WRITE, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_WRITE, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDWR, DENY_READ, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_WRITE, O_WRONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDWR, DENY_NONE, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDONLY, DENY_NONE, A_R},
+{0, O_RDWR, DENY_WRITE, O_WRONLY, DENY_NONE, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDWR, DENY_FCB, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_WRITE, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDWR, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_DOS, A_R},
+{0, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDWR, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_WRITE, A_R},
+{0, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDWR, DENY_READ, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDWR, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_NONE, A_R},
+{0, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDWR, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDWR, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDWR, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDWR, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDWR, DENY_READ, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_READ, A_R},
+{0, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_READ, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDWR, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_NONE, A_R},
+{0, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDWR, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_READ, O_RDWR, DENY_DOS, A_0},
+{0, O_RDWR, DENY_READ, O_RDONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_READ, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_READ, O_RDWR, DENY_ALL, A_0},
+{0, O_RDWR, DENY_READ, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_READ, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_READ, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_READ, O_RDONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_READ, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_READ, O_RDWR, DENY_READ, A_0},
+{0, O_RDWR, DENY_READ, O_RDONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_READ, O_WRONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_READ, O_RDWR, DENY_NONE, A_0},
+{0, O_RDWR, DENY_READ, O_RDONLY, DENY_NONE, A_0},
+{0, O_RDWR, DENY_READ, O_WRONLY, DENY_NONE, A_W},
+{0, O_RDWR, DENY_READ, O_RDWR, DENY_FCB, A_0},
+{0, O_RDWR, DENY_READ, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_READ, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_READ, O_RDWR, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_READ, O_RDONLY, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_READ, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_READ, O_RDWR, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_READ, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_READ, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_READ, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_READ, O_RDONLY, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_READ, O_WRONLY, DENY_WRITE, A_W},
+{0, O_RDONLY, DENY_READ, O_RDWR, DENY_READ, A_0},
+{0, O_RDONLY, DENY_READ, O_RDONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_READ, O_WRONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_READ, O_RDWR, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_READ, O_RDONLY, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_READ, O_WRONLY, DENY_NONE, A_W},
+{0, O_RDONLY, DENY_READ, O_RDWR, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_READ, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_READ, O_WRONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_READ, O_RDWR, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_READ, O_RDONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_READ, O_WRONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_READ, O_RDWR, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_READ, O_RDONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_READ, O_WRONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_READ, O_RDWR, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_READ, O_RDONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_READ, O_WRONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_READ, O_RDWR, DENY_READ, A_0},
+{0, O_WRONLY, DENY_READ, O_RDONLY, DENY_READ, A_0},
+{0, O_WRONLY, DENY_READ, O_WRONLY, DENY_READ, A_W},
+{0, O_WRONLY, DENY_READ, O_RDWR, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_READ, O_RDONLY, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_READ, O_WRONLY, DENY_NONE, A_W},
+{0, O_WRONLY, DENY_READ, O_RDWR, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_READ, O_RDONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_READ, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_NONE, O_RDWR, DENY_DOS, A_0},
+{0, O_RDWR, DENY_NONE, O_RDONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_NONE, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_NONE, O_RDWR, DENY_ALL, A_0},
+{0, O_RDWR, DENY_NONE, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_NONE, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_NONE, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_NONE, O_RDONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_NONE, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_NONE, O_RDWR, DENY_READ, A_0},
+{0, O_RDWR, DENY_NONE, O_RDONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_NONE, O_WRONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_NONE, O_RDWR, DENY_NONE, A_RW},
+{0, O_RDWR, DENY_NONE, O_RDONLY, DENY_NONE, A_R},
+{0, O_RDWR, DENY_NONE, O_WRONLY, DENY_NONE, A_W},
+{0, O_RDWR, DENY_NONE, O_RDWR, DENY_FCB, A_0},
+{0, O_RDWR, DENY_NONE, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_NONE, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_NONE, O_RDWR, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_NONE, O_RDONLY, DENY_DOS, A_R},
+{0, O_RDONLY, DENY_NONE, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_NONE, O_RDWR, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_NONE, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_NONE, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_NONE, O_RDWR, DENY_WRITE, A_RW},
+{0, O_RDONLY, DENY_NONE, O_RDONLY, DENY_WRITE, A_R},
+{0, O_RDONLY, DENY_NONE, O_WRONLY, DENY_WRITE, A_W},
+{0, O_RDONLY, DENY_NONE, O_RDWR, DENY_READ, A_0},
+{0, O_RDONLY, DENY_NONE, O_RDONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_NONE, O_WRONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_NONE, O_RDWR, DENY_NONE, A_RW},
+{0, O_RDONLY, DENY_NONE, O_RDONLY, DENY_NONE, A_R},
+{0, O_RDONLY, DENY_NONE, O_WRONLY, DENY_NONE, A_W},
+{0, O_RDONLY, DENY_NONE, O_RDWR, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_NONE, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_NONE, O_WRONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_NONE, O_RDWR, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_NONE, O_RDONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_NONE, O_WRONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_NONE, O_RDWR, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_NONE, O_RDONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_NONE, O_WRONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_NONE, O_RDWR, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_NONE, O_RDONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_NONE, O_WRONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_NONE, O_RDWR, DENY_READ, A_RW},
+{0, O_WRONLY, DENY_NONE, O_RDONLY, DENY_READ, A_R},
+{0, O_WRONLY, DENY_NONE, O_WRONLY, DENY_READ, A_W},
+{0, O_WRONLY, DENY_NONE, O_RDWR, DENY_NONE, A_RW},
+{0, O_WRONLY, DENY_NONE, O_RDONLY, DENY_NONE, A_R},
+{0, O_WRONLY, DENY_NONE, O_WRONLY, DENY_NONE, A_W},
+{0, O_WRONLY, DENY_NONE, O_RDWR, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_NONE, O_RDONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_NONE, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_FCB, O_RDWR, DENY_DOS, A_0},
+{0, O_RDWR, DENY_FCB, O_RDONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_FCB, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_FCB, O_RDWR, DENY_ALL, A_0},
+{0, O_RDWR, DENY_FCB, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_FCB, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_FCB, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_FCB, O_RDONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_FCB, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_FCB, O_RDWR, DENY_READ, A_0},
+{0, O_RDWR, DENY_FCB, O_RDONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_FCB, O_WRONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_FCB, O_RDWR, DENY_NONE, A_0},
+{0, O_RDWR, DENY_FCB, O_RDONLY, DENY_NONE, A_0},
+{0, O_RDWR, DENY_FCB, O_WRONLY, DENY_NONE, A_0},
+{0, O_RDWR, DENY_FCB, O_RDWR, DENY_FCB, A_0},
+{0, O_RDWR, DENY_FCB, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_FCB, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDWR, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDONLY, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_FCB, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDWR, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_FCB, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDONLY, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_FCB, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDWR, DENY_READ, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_FCB, O_WRONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDWR, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDONLY, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_FCB, O_WRONLY, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDWR, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_FCB, O_WRONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDWR, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_FCB, O_WRONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDWR, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_FCB, O_WRONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDWR, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_FCB, O_WRONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDWR, DENY_READ, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDONLY, DENY_READ, A_0},
+{0, O_WRONLY, DENY_FCB, O_WRONLY, DENY_READ, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDWR, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDONLY, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_FCB, O_WRONLY, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDWR, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_FCB, O_WRONLY, DENY_FCB, A_0}
+};
+
+
+static const struct {
+ int isexe;
+ int mode1, deny1;
+ int mode2, deny2;
+ enum deny_result result;
+} denytable1[] = {
+{1, O_RDWR, DENY_DOS, O_RDWR, DENY_DOS, A_RW},
+{1, O_RDWR, DENY_DOS, O_RDONLY, DENY_DOS, A_R},
+{1, O_RDWR, DENY_DOS, O_WRONLY, DENY_DOS, A_W},
+{1, O_RDWR, DENY_DOS, O_RDWR, DENY_ALL, A_0},
+{1, O_RDWR, DENY_DOS, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_DOS, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_DOS, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_DOS, O_RDONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_DOS, O_WRONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_DOS, O_RDWR, DENY_READ, A_0},
+{1, O_RDWR, DENY_DOS, O_RDONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_DOS, O_WRONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_DOS, O_RDWR, DENY_NONE, A_RW},
+{1, O_RDWR, DENY_DOS, O_RDONLY, DENY_NONE, A_R},
+{1, O_RDWR, DENY_DOS, O_WRONLY, DENY_NONE, A_W},
+{1, O_RDWR, DENY_DOS, O_RDWR, DENY_FCB, A_0},
+{1, O_RDWR, DENY_DOS, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_DOS, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_DOS, O_RDWR, DENY_DOS, A_RW},
+{1, O_RDONLY, DENY_DOS, O_RDONLY, DENY_DOS, A_R},
+{1, O_RDONLY, DENY_DOS, O_WRONLY, DENY_DOS, A_W},
+{1, O_RDONLY, DENY_DOS, O_RDWR, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_DOS, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_DOS, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_DOS, O_RDWR, DENY_WRITE, A_RW},
+{1, O_RDONLY, DENY_DOS, O_RDONLY, DENY_WRITE, A_R},
+{1, O_RDONLY, DENY_DOS, O_WRONLY, DENY_WRITE, A_W},
+{1, O_RDONLY, DENY_DOS, O_RDWR, DENY_READ, A_0},
+{1, O_RDONLY, DENY_DOS, O_RDONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_DOS, O_WRONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_DOS, O_RDWR, DENY_NONE, A_RW},
+{1, O_RDONLY, DENY_DOS, O_RDONLY, DENY_NONE, A_R},
+{1, O_RDONLY, DENY_DOS, O_WRONLY, DENY_NONE, A_W},
+{1, O_RDONLY, DENY_DOS, O_RDWR, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_DOS, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_DOS, O_WRONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_DOS, O_RDWR, DENY_DOS, A_RW},
+{1, O_WRONLY, DENY_DOS, O_RDONLY, DENY_DOS, A_R},
+{1, O_WRONLY, DENY_DOS, O_WRONLY, DENY_DOS, A_W},
+{1, O_WRONLY, DENY_DOS, O_RDWR, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_DOS, O_RDONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_DOS, O_WRONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_DOS, O_RDWR, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_DOS, O_RDONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_DOS, O_WRONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_DOS, O_RDWR, DENY_READ, A_RW},
+{1, O_WRONLY, DENY_DOS, O_RDONLY, DENY_READ, A_R},
+{1, O_WRONLY, DENY_DOS, O_WRONLY, DENY_READ, A_W},
+{1, O_WRONLY, DENY_DOS, O_RDWR, DENY_NONE, A_RW},
+{1, O_WRONLY, DENY_DOS, O_RDONLY, DENY_NONE, A_R},
+{1, O_WRONLY, DENY_DOS, O_WRONLY, DENY_NONE, A_W},
+{1, O_WRONLY, DENY_DOS, O_RDWR, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_DOS, O_RDONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_DOS, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_ALL, O_RDWR, DENY_DOS, A_0},
+{1, O_RDWR, DENY_ALL, O_RDONLY, DENY_DOS, A_0},
+{1, O_RDWR, DENY_ALL, O_WRONLY, DENY_DOS, A_0},
+{1, O_RDWR, DENY_ALL, O_RDWR, DENY_ALL, A_0},
+{1, O_RDWR, DENY_ALL, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_ALL, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_ALL, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_ALL, O_RDONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_ALL, O_WRONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_ALL, O_RDWR, DENY_READ, A_0},
+{1, O_RDWR, DENY_ALL, O_RDONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_ALL, O_WRONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_ALL, O_RDWR, DENY_NONE, A_0},
+{1, O_RDWR, DENY_ALL, O_RDONLY, DENY_NONE, A_0},
+{1, O_RDWR, DENY_ALL, O_WRONLY, DENY_NONE, A_0},
+{1, O_RDWR, DENY_ALL, O_RDWR, DENY_FCB, A_0},
+{1, O_RDWR, DENY_ALL, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_ALL, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDWR, DENY_DOS, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDONLY, DENY_DOS, A_0},
+{1, O_RDONLY, DENY_ALL, O_WRONLY, DENY_DOS, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDWR, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_ALL, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDONLY, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_ALL, O_WRONLY, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDWR, DENY_READ, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_ALL, O_WRONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDWR, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDONLY, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_ALL, O_WRONLY, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDWR, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_ALL, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_ALL, O_WRONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDWR, DENY_DOS, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDONLY, DENY_DOS, A_0},
+{1, O_WRONLY, DENY_ALL, O_WRONLY, DENY_DOS, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDWR, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_ALL, O_WRONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDWR, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_ALL, O_WRONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDWR, DENY_READ, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDONLY, DENY_READ, A_0},
+{1, O_WRONLY, DENY_ALL, O_WRONLY, DENY_READ, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDWR, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDONLY, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_ALL, O_WRONLY, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDWR, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_ALL, O_RDONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_ALL, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDWR, DENY_DOS, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDONLY, DENY_DOS, A_R},
+{1, O_RDWR, DENY_WRITE, O_WRONLY, DENY_DOS, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDWR, DENY_ALL, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_WRITE, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_WRITE, O_WRONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDWR, DENY_READ, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_WRITE, O_WRONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDWR, DENY_NONE, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDONLY, DENY_NONE, A_R},
+{1, O_RDWR, DENY_WRITE, O_WRONLY, DENY_NONE, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDWR, DENY_FCB, A_0},
+{1, O_RDWR, DENY_WRITE, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_WRITE, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDWR, DENY_DOS, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_DOS, A_R},
+{1, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_DOS, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDWR, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_WRITE, A_R},
+{1, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDWR, DENY_READ, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDWR, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_NONE, A_R},
+{1, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDWR, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDWR, DENY_DOS, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_DOS, A_R},
+{1, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_DOS, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDWR, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDWR, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDWR, DENY_READ, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_READ, A_R},
+{1, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_READ, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDWR, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_NONE, A_R},
+{1, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDWR, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_READ, O_RDWR, DENY_DOS, A_0},
+{1, O_RDWR, DENY_READ, O_RDONLY, DENY_DOS, A_0},
+{1, O_RDWR, DENY_READ, O_WRONLY, DENY_DOS, A_W},
+{1, O_RDWR, DENY_READ, O_RDWR, DENY_ALL, A_0},
+{1, O_RDWR, DENY_READ, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_READ, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_READ, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_READ, O_RDONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_READ, O_WRONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_READ, O_RDWR, DENY_READ, A_0},
+{1, O_RDWR, DENY_READ, O_RDONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_READ, O_WRONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_READ, O_RDWR, DENY_NONE, A_0},
+{1, O_RDWR, DENY_READ, O_RDONLY, DENY_NONE, A_0},
+{1, O_RDWR, DENY_READ, O_WRONLY, DENY_NONE, A_W},
+{1, O_RDWR, DENY_READ, O_RDWR, DENY_FCB, A_0},
+{1, O_RDWR, DENY_READ, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_READ, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_READ, O_RDWR, DENY_DOS, A_0},
+{1, O_RDONLY, DENY_READ, O_RDONLY, DENY_DOS, A_0},
+{1, O_RDONLY, DENY_READ, O_WRONLY, DENY_DOS, A_W},
+{1, O_RDONLY, DENY_READ, O_RDWR, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_READ, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_READ, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_READ, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_READ, O_RDONLY, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_READ, O_WRONLY, DENY_WRITE, A_W},
+{1, O_RDONLY, DENY_READ, O_RDWR, DENY_READ, A_0},
+{1, O_RDONLY, DENY_READ, O_RDONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_READ, O_WRONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_READ, O_RDWR, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_READ, O_RDONLY, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_READ, O_WRONLY, DENY_NONE, A_W},
+{1, O_RDONLY, DENY_READ, O_RDWR, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_READ, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_READ, O_WRONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_READ, O_RDWR, DENY_DOS, A_0},
+{1, O_WRONLY, DENY_READ, O_RDONLY, DENY_DOS, A_0},
+{1, O_WRONLY, DENY_READ, O_WRONLY, DENY_DOS, A_W},
+{1, O_WRONLY, DENY_READ, O_RDWR, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_READ, O_RDONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_READ, O_WRONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_READ, O_RDWR, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_READ, O_RDONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_READ, O_WRONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_READ, O_RDWR, DENY_READ, A_0},
+{1, O_WRONLY, DENY_READ, O_RDONLY, DENY_READ, A_0},
+{1, O_WRONLY, DENY_READ, O_WRONLY, DENY_READ, A_W},
+{1, O_WRONLY, DENY_READ, O_RDWR, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_READ, O_RDONLY, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_READ, O_WRONLY, DENY_NONE, A_W},
+{1, O_WRONLY, DENY_READ, O_RDWR, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_READ, O_RDONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_READ, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_NONE, O_RDWR, DENY_DOS, A_RW},
+{1, O_RDWR, DENY_NONE, O_RDONLY, DENY_DOS, A_R},
+{1, O_RDWR, DENY_NONE, O_WRONLY, DENY_DOS, A_W},
+{1, O_RDWR, DENY_NONE, O_RDWR, DENY_ALL, A_0},
+{1, O_RDWR, DENY_NONE, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_NONE, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_NONE, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_NONE, O_RDONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_NONE, O_WRONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_NONE, O_RDWR, DENY_READ, A_0},
+{1, O_RDWR, DENY_NONE, O_RDONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_NONE, O_WRONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_NONE, O_RDWR, DENY_NONE, A_RW},
+{1, O_RDWR, DENY_NONE, O_RDONLY, DENY_NONE, A_R},
+{1, O_RDWR, DENY_NONE, O_WRONLY, DENY_NONE, A_W},
+{1, O_RDWR, DENY_NONE, O_RDWR, DENY_FCB, A_0},
+{1, O_RDWR, DENY_NONE, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_NONE, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_NONE, O_RDWR, DENY_DOS, A_RW},
+{1, O_RDONLY, DENY_NONE, O_RDONLY, DENY_DOS, A_R},
+{1, O_RDONLY, DENY_NONE, O_WRONLY, DENY_DOS, A_W},
+{1, O_RDONLY, DENY_NONE, O_RDWR, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_NONE, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_NONE, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_NONE, O_RDWR, DENY_WRITE, A_RW},
+{1, O_RDONLY, DENY_NONE, O_RDONLY, DENY_WRITE, A_R},
+{1, O_RDONLY, DENY_NONE, O_WRONLY, DENY_WRITE, A_W},
+{1, O_RDONLY, DENY_NONE, O_RDWR, DENY_READ, A_0},
+{1, O_RDONLY, DENY_NONE, O_RDONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_NONE, O_WRONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_NONE, O_RDWR, DENY_NONE, A_RW},
+{1, O_RDONLY, DENY_NONE, O_RDONLY, DENY_NONE, A_R},
+{1, O_RDONLY, DENY_NONE, O_WRONLY, DENY_NONE, A_W},
+{1, O_RDONLY, DENY_NONE, O_RDWR, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_NONE, O_RDONLY, DENY_FCB, A_0},
+{1, O_RDONLY, DENY_NONE, O_WRONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_NONE, O_RDWR, DENY_DOS, A_RW},
+{1, O_WRONLY, DENY_NONE, O_RDONLY, DENY_DOS, A_R},
+{1, O_WRONLY, DENY_NONE, O_WRONLY, DENY_DOS, A_W},
+{1, O_WRONLY, DENY_NONE, O_RDWR, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_NONE, O_RDONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_NONE, O_WRONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_NONE, O_RDWR, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_NONE, O_RDONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_NONE, O_WRONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_NONE, O_RDWR, DENY_READ, A_RW},
+{1, O_WRONLY, DENY_NONE, O_RDONLY, DENY_READ, A_R},
+{1, O_WRONLY, DENY_NONE, O_WRONLY, DENY_READ, A_W},
+{1, O_WRONLY, DENY_NONE, O_RDWR, DENY_NONE, A_RW},
+{1, O_WRONLY, DENY_NONE, O_RDONLY, DENY_NONE, A_R},
+{1, O_WRONLY, DENY_NONE, O_WRONLY, DENY_NONE, A_W},
+{1, O_WRONLY, DENY_NONE, O_RDWR, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_NONE, O_RDONLY, DENY_FCB, A_0},
+{1, O_WRONLY, DENY_NONE, O_WRONLY, DENY_FCB, A_0},
+{1, O_RDWR, DENY_FCB, O_RDWR, DENY_DOS, A_RW},
+{1, O_RDWR, DENY_FCB, O_RDONLY, DENY_DOS, A_R},
+{1, O_RDWR, DENY_FCB, O_WRONLY, DENY_DOS, A_W},
+{1, O_RDWR, DENY_FCB, O_RDWR, DENY_ALL, A_0},
+{1, O_RDWR, DENY_FCB, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_FCB, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDWR, DENY_FCB, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_FCB, O_RDONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_FCB, O_WRONLY, DENY_WRITE, A_0},
+{1, O_RDWR, DENY_FCB, O_RDWR, DENY_READ, A_0},
+{1, O_RDWR, DENY_FCB, O_RDONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_FCB, O_WRONLY, DENY_READ, A_0},
+{1, O_RDWR, DENY_FCB, O_RDWR, DENY_NONE, A_0},
+{1, O_RDWR, DENY_FCB, O_RDONLY, DENY_NONE, A_0},
+{1, O_RDWR, DENY_FCB, O_WRONLY, DENY_NONE, A_0},
+{1, O_RDWR, DENY_FCB, O_RDWR, DENY_FCB, A_RW},
+{1, O_RDWR, DENY_FCB, O_RDONLY, DENY_FCB, A_RW},
+{1, O_RDWR, DENY_FCB, O_WRONLY, DENY_FCB, A_RW},
+{1, O_RDONLY, DENY_FCB, O_RDWR, DENY_DOS, A_RW},
+{1, O_RDONLY, DENY_FCB, O_RDONLY, DENY_DOS, A_R},
+{1, O_RDONLY, DENY_FCB, O_WRONLY, DENY_DOS, A_W},
+{1, O_RDONLY, DENY_FCB, O_RDWR, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_FCB, O_WRONLY, DENY_ALL, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDWR, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDONLY, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_FCB, O_WRONLY, DENY_WRITE, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDWR, DENY_READ, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_FCB, O_WRONLY, DENY_READ, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDWR, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDONLY, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_FCB, O_WRONLY, DENY_NONE, A_0},
+{1, O_RDONLY, DENY_FCB, O_RDWR, DENY_FCB, A_RW},
+{1, O_RDONLY, DENY_FCB, O_RDONLY, DENY_FCB, A_RW},
+{1, O_RDONLY, DENY_FCB, O_WRONLY, DENY_FCB, A_RW},
+{1, O_WRONLY, DENY_FCB, O_RDWR, DENY_DOS, A_RW},
+{1, O_WRONLY, DENY_FCB, O_RDONLY, DENY_DOS, A_R},
+{1, O_WRONLY, DENY_FCB, O_WRONLY, DENY_DOS, A_W},
+{1, O_WRONLY, DENY_FCB, O_RDWR, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_FCB, O_WRONLY, DENY_ALL, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDWR, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_FCB, O_WRONLY, DENY_WRITE, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDWR, DENY_READ, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDONLY, DENY_READ, A_0},
+{1, O_WRONLY, DENY_FCB, O_WRONLY, DENY_READ, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDWR, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDONLY, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_FCB, O_WRONLY, DENY_NONE, A_0},
+{1, O_WRONLY, DENY_FCB, O_RDWR, DENY_FCB, A_RW},
+{1, O_WRONLY, DENY_FCB, O_RDONLY, DENY_FCB, A_RW},
+{1, O_WRONLY, DENY_FCB, O_WRONLY, DENY_FCB, A_RW},
+{0, O_RDWR, DENY_DOS, O_RDWR, DENY_DOS, A_RW},
+{0, O_RDWR, DENY_DOS, O_RDONLY, DENY_DOS, A_R},
+{0, O_RDWR, DENY_DOS, O_WRONLY, DENY_DOS, A_W},
+{0, O_RDWR, DENY_DOS, O_RDWR, DENY_ALL, A_0},
+{0, O_RDWR, DENY_DOS, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_DOS, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_DOS, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_DOS, O_RDONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_DOS, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_DOS, O_RDWR, DENY_READ, A_0},
+{0, O_RDWR, DENY_DOS, O_RDONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_DOS, O_WRONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_DOS, O_RDWR, DENY_NONE, A_0},
+{0, O_RDWR, DENY_DOS, O_RDONLY, DENY_NONE, A_0},
+{0, O_RDWR, DENY_DOS, O_WRONLY, DENY_NONE, A_0},
+{0, O_RDWR, DENY_DOS, O_RDWR, DENY_FCB, A_RW},
+{0, O_RDWR, DENY_DOS, O_RDONLY, DENY_FCB, A_RW},
+{0, O_RDWR, DENY_DOS, O_WRONLY, DENY_FCB, A_RW},
+{0, O_RDONLY, DENY_DOS, O_RDWR, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDONLY, DENY_DOS, A_R},
+{0, O_RDONLY, DENY_DOS, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDWR, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_DOS, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDONLY, DENY_WRITE, A_R},
+{0, O_RDONLY, DENY_DOS, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDWR, DENY_READ, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_DOS, O_WRONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDWR, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDONLY, DENY_NONE, A_R},
+{0, O_RDONLY, DENY_DOS, O_WRONLY, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDWR, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_DOS, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_DOS, O_WRONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDWR, DENY_DOS, A_RW},
+{0, O_WRONLY, DENY_DOS, O_RDONLY, DENY_DOS, A_R},
+{0, O_WRONLY, DENY_DOS, O_WRONLY, DENY_DOS, A_W},
+{0, O_WRONLY, DENY_DOS, O_RDWR, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_DOS, O_WRONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDWR, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_DOS, O_WRONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDWR, DENY_READ, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDONLY, DENY_READ, A_0},
+{0, O_WRONLY, DENY_DOS, O_WRONLY, DENY_READ, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDWR, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDONLY, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_DOS, O_WRONLY, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_DOS, O_RDWR, DENY_FCB, A_RW},
+{0, O_WRONLY, DENY_DOS, O_RDONLY, DENY_FCB, A_RW},
+{0, O_WRONLY, DENY_DOS, O_WRONLY, DENY_FCB, A_RW},
+{0, O_RDWR, DENY_ALL, O_RDWR, DENY_DOS, A_0},
+{0, O_RDWR, DENY_ALL, O_RDONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_ALL, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_ALL, O_RDWR, DENY_ALL, A_0},
+{0, O_RDWR, DENY_ALL, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_ALL, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_ALL, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_ALL, O_RDONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_ALL, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_ALL, O_RDWR, DENY_READ, A_0},
+{0, O_RDWR, DENY_ALL, O_RDONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_ALL, O_WRONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_ALL, O_RDWR, DENY_NONE, A_0},
+{0, O_RDWR, DENY_ALL, O_RDONLY, DENY_NONE, A_0},
+{0, O_RDWR, DENY_ALL, O_WRONLY, DENY_NONE, A_0},
+{0, O_RDWR, DENY_ALL, O_RDWR, DENY_FCB, A_0},
+{0, O_RDWR, DENY_ALL, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_ALL, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDWR, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDONLY, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_ALL, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDWR, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_ALL, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDONLY, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_ALL, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDWR, DENY_READ, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_ALL, O_WRONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDWR, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDONLY, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_ALL, O_WRONLY, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDWR, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_ALL, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_ALL, O_WRONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDWR, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_ALL, O_WRONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDWR, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_ALL, O_WRONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDWR, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_ALL, O_WRONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDWR, DENY_READ, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDONLY, DENY_READ, A_0},
+{0, O_WRONLY, DENY_ALL, O_WRONLY, DENY_READ, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDWR, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDONLY, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_ALL, O_WRONLY, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDWR, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_ALL, O_RDONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_ALL, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDWR, DENY_DOS, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_WRITE, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDWR, DENY_ALL, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_WRITE, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_WRITE, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDWR, DENY_READ, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_WRITE, O_WRONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDWR, DENY_NONE, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDONLY, DENY_NONE, A_R},
+{0, O_RDWR, DENY_WRITE, O_WRONLY, DENY_NONE, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDWR, DENY_FCB, A_0},
+{0, O_RDWR, DENY_WRITE, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_WRITE, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDWR, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_DOS, A_R},
+{0, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDWR, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_WRITE, A_R},
+{0, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDWR, DENY_READ, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDWR, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_NONE, A_R},
+{0, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDWR, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_WRITE, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_WRITE, O_WRONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDWR, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDWR, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDWR, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDWR, DENY_READ, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_READ, A_R},
+{0, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_READ, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDWR, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_NONE, A_R},
+{0, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDWR, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_WRITE, O_RDONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_WRITE, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_READ, O_RDWR, DENY_DOS, A_0},
+{0, O_RDWR, DENY_READ, O_RDONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_READ, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_READ, O_RDWR, DENY_ALL, A_0},
+{0, O_RDWR, DENY_READ, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_READ, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_READ, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_READ, O_RDONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_READ, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_READ, O_RDWR, DENY_READ, A_0},
+{0, O_RDWR, DENY_READ, O_RDONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_READ, O_WRONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_READ, O_RDWR, DENY_NONE, A_0},
+{0, O_RDWR, DENY_READ, O_RDONLY, DENY_NONE, A_0},
+{0, O_RDWR, DENY_READ, O_WRONLY, DENY_NONE, A_W},
+{0, O_RDWR, DENY_READ, O_RDWR, DENY_FCB, A_0},
+{0, O_RDWR, DENY_READ, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_READ, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_READ, O_RDWR, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_READ, O_RDONLY, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_READ, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_READ, O_RDWR, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_READ, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_READ, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_READ, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_READ, O_RDONLY, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_READ, O_WRONLY, DENY_WRITE, A_W},
+{0, O_RDONLY, DENY_READ, O_RDWR, DENY_READ, A_0},
+{0, O_RDONLY, DENY_READ, O_RDONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_READ, O_WRONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_READ, O_RDWR, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_READ, O_RDONLY, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_READ, O_WRONLY, DENY_NONE, A_W},
+{0, O_RDONLY, DENY_READ, O_RDWR, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_READ, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_READ, O_WRONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_READ, O_RDWR, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_READ, O_RDONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_READ, O_WRONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_READ, O_RDWR, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_READ, O_RDONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_READ, O_WRONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_READ, O_RDWR, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_READ, O_RDONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_READ, O_WRONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_READ, O_RDWR, DENY_READ, A_0},
+{0, O_WRONLY, DENY_READ, O_RDONLY, DENY_READ, A_0},
+{0, O_WRONLY, DENY_READ, O_WRONLY, DENY_READ, A_W},
+{0, O_WRONLY, DENY_READ, O_RDWR, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_READ, O_RDONLY, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_READ, O_WRONLY, DENY_NONE, A_W},
+{0, O_WRONLY, DENY_READ, O_RDWR, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_READ, O_RDONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_READ, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_NONE, O_RDWR, DENY_DOS, A_0},
+{0, O_RDWR, DENY_NONE, O_RDONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_NONE, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDWR, DENY_NONE, O_RDWR, DENY_ALL, A_0},
+{0, O_RDWR, DENY_NONE, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_NONE, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_NONE, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_NONE, O_RDONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_NONE, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_NONE, O_RDWR, DENY_READ, A_0},
+{0, O_RDWR, DENY_NONE, O_RDONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_NONE, O_WRONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_NONE, O_RDWR, DENY_NONE, A_RW},
+{0, O_RDWR, DENY_NONE, O_RDONLY, DENY_NONE, A_R},
+{0, O_RDWR, DENY_NONE, O_WRONLY, DENY_NONE, A_W},
+{0, O_RDWR, DENY_NONE, O_RDWR, DENY_FCB, A_0},
+{0, O_RDWR, DENY_NONE, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_NONE, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_NONE, O_RDWR, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_NONE, O_RDONLY, DENY_DOS, A_R},
+{0, O_RDONLY, DENY_NONE, O_WRONLY, DENY_DOS, A_0},
+{0, O_RDONLY, DENY_NONE, O_RDWR, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_NONE, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_NONE, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_NONE, O_RDWR, DENY_WRITE, A_RW},
+{0, O_RDONLY, DENY_NONE, O_RDONLY, DENY_WRITE, A_R},
+{0, O_RDONLY, DENY_NONE, O_WRONLY, DENY_WRITE, A_W},
+{0, O_RDONLY, DENY_NONE, O_RDWR, DENY_READ, A_0},
+{0, O_RDONLY, DENY_NONE, O_RDONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_NONE, O_WRONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_NONE, O_RDWR, DENY_NONE, A_RW},
+{0, O_RDONLY, DENY_NONE, O_RDONLY, DENY_NONE, A_R},
+{0, O_RDONLY, DENY_NONE, O_WRONLY, DENY_NONE, A_W},
+{0, O_RDONLY, DENY_NONE, O_RDWR, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_NONE, O_RDONLY, DENY_FCB, A_0},
+{0, O_RDONLY, DENY_NONE, O_WRONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_NONE, O_RDWR, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_NONE, O_RDONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_NONE, O_WRONLY, DENY_DOS, A_0},
+{0, O_WRONLY, DENY_NONE, O_RDWR, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_NONE, O_RDONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_NONE, O_WRONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_NONE, O_RDWR, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_NONE, O_RDONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_NONE, O_WRONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_NONE, O_RDWR, DENY_READ, A_RW},
+{0, O_WRONLY, DENY_NONE, O_RDONLY, DENY_READ, A_R},
+{0, O_WRONLY, DENY_NONE, O_WRONLY, DENY_READ, A_W},
+{0, O_WRONLY, DENY_NONE, O_RDWR, DENY_NONE, A_RW},
+{0, O_WRONLY, DENY_NONE, O_RDONLY, DENY_NONE, A_R},
+{0, O_WRONLY, DENY_NONE, O_WRONLY, DENY_NONE, A_W},
+{0, O_WRONLY, DENY_NONE, O_RDWR, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_NONE, O_RDONLY, DENY_FCB, A_0},
+{0, O_WRONLY, DENY_NONE, O_WRONLY, DENY_FCB, A_0},
+{0, O_RDWR, DENY_FCB, O_RDWR, DENY_DOS, A_RW},
+{0, O_RDWR, DENY_FCB, O_RDONLY, DENY_DOS, A_R},
+{0, O_RDWR, DENY_FCB, O_WRONLY, DENY_DOS, A_W},
+{0, O_RDWR, DENY_FCB, O_RDWR, DENY_ALL, A_0},
+{0, O_RDWR, DENY_FCB, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_FCB, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDWR, DENY_FCB, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_FCB, O_RDONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_FCB, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDWR, DENY_FCB, O_RDWR, DENY_READ, A_0},
+{0, O_RDWR, DENY_FCB, O_RDONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_FCB, O_WRONLY, DENY_READ, A_0},
+{0, O_RDWR, DENY_FCB, O_RDWR, DENY_NONE, A_0},
+{0, O_RDWR, DENY_FCB, O_RDONLY, DENY_NONE, A_0},
+{0, O_RDWR, DENY_FCB, O_WRONLY, DENY_NONE, A_0},
+{0, O_RDWR, DENY_FCB, O_RDWR, DENY_FCB, A_RW},
+{0, O_RDWR, DENY_FCB, O_RDONLY, DENY_FCB, A_RW},
+{0, O_RDWR, DENY_FCB, O_WRONLY, DENY_FCB, A_RW},
+{0, O_RDONLY, DENY_FCB, O_RDWR, DENY_DOS, A_RW},
+{0, O_RDONLY, DENY_FCB, O_RDONLY, DENY_DOS, A_R},
+{0, O_RDONLY, DENY_FCB, O_WRONLY, DENY_DOS, A_W},
+{0, O_RDONLY, DENY_FCB, O_RDWR, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_FCB, O_WRONLY, DENY_ALL, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDWR, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDONLY, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_FCB, O_WRONLY, DENY_WRITE, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDWR, DENY_READ, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_FCB, O_WRONLY, DENY_READ, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDWR, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDONLY, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_FCB, O_WRONLY, DENY_NONE, A_0},
+{0, O_RDONLY, DENY_FCB, O_RDWR, DENY_FCB, A_RW},
+{0, O_RDONLY, DENY_FCB, O_RDONLY, DENY_FCB, A_RW},
+{0, O_RDONLY, DENY_FCB, O_WRONLY, DENY_FCB, A_RW},
+{0, O_WRONLY, DENY_FCB, O_RDWR, DENY_DOS, A_RW},
+{0, O_WRONLY, DENY_FCB, O_RDONLY, DENY_DOS, A_R},
+{0, O_WRONLY, DENY_FCB, O_WRONLY, DENY_DOS, A_W},
+{0, O_WRONLY, DENY_FCB, O_RDWR, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_FCB, O_WRONLY, DENY_ALL, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDWR, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_FCB, O_WRONLY, DENY_WRITE, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDWR, DENY_READ, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDONLY, DENY_READ, A_0},
+{0, O_WRONLY, DENY_FCB, O_WRONLY, DENY_READ, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDWR, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDONLY, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_FCB, O_WRONLY, DENY_NONE, A_0},
+{0, O_WRONLY, DENY_FCB, O_RDWR, DENY_FCB, A_RW},
+{0, O_WRONLY, DENY_FCB, O_RDONLY, DENY_FCB, A_RW},
+{0, O_WRONLY, DENY_FCB, O_WRONLY, DENY_FCB, A_RW}
+};
+
+
+static void progress_bar(struct torture_context *tctx, uint_t i, uint_t total)
+{
+ if (torture_setting_bool(tctx, "progress", true)) {
+ torture_comment(tctx, "%5d/%5d\r", i, total);
+ fflush(stdout);
+ }
+}
+
+/*
+ this produces a matrix of deny mode behaviour for 1 connection
+ */
+bool torture_denytest1(struct torture_context *tctx,
+ struct smbcli_state *cli1)
+{
+ int fnum1, fnum2;
+ int i;
+ bool correct = true;
+ struct timeval tv, tv_start;
+ const char *fnames[2] = {"\\denytest1.dat", "\\denytest1.exe"};
+ int failures=0;
+
+ torture_comment(tctx, "Testing deny modes with 1 connection\n");
+
+ 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);
+ }
+
+ torture_comment(tctx, "testing %d entries\n", (int)ARRAY_SIZE(denytable1));
+
+ GetTimeOfDay(&tv_start);
+
+ for (i=0; i<ARRAY_SIZE(denytable1); i++) {
+ enum deny_result res;
+ const char *fname = fnames[denytable1[i].isexe];
+
+ progress_bar(tctx, i, ARRAY_SIZE(denytable1));
+
+ fnum1 = smbcli_open(cli1->tree, 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;
+ GetTimeOfDay(&tv);
+ tdif = usec_time_diff(&tv, &tv_start);
+ tdif /= 1000;
+ 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, "finshed 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 timeval 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);
+ }
+
+ GetTimeOfDay(&tv_start);
+
+ for (i=0; i<ARRAY_SIZE(denytable2); i++) {
+ enum deny_result res;
+ const char *fname = fnames[denytable2[i].isexe];
+
+ progress_bar(tctx, i, ARRAY_SIZE(denytable1));
+
+ fnum1 = smbcli_open(cli1->tree, 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;
+ GetTimeOfDay(&tv);
+ tdif = usec_time_diff(&tv, &tv_start);
+ tdif /= 1000;
+ 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, "finshed 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;i<nbits;i++) {
+ if (b & (1<<i)) {
+ ret |= bv[i].value;
+ }
+ }
+ return ret;
+}
+
+static const char *bit_string(TALLOC_CTX *mem_ctx, const struct bit_value *bv, int b, int nbits)
+{
+ char *ret = NULL;
+ int i;
+ for (i=0;i<nbits;i++) {
+ if (b & (1<<i)) {
+ if (ret == NULL) {
+ ret = talloc_asprintf(mem_ctx, "%s", bv[i].name);
+ } else {
+ ret = talloc_asprintf_append_buffer(ret, " | %s", bv[i].name);
+ }
+ }
+ }
+ if (ret == NULL) ret = talloc_strdup(mem_ctx, "(NONE)");
+ return ret;
+}
+
+
+/*
+ determine if two opens conflict
+*/
+static NTSTATUS predict_share_conflict(uint32_t sa1, uint32_t am1, uint32_t sa2, uint32_t am2,
+ bool read_for_execute, enum deny_result *res)
+{
+#define CHECK_MASK(am, sa, right, share) do { \
+ if (((am) & (right)) && !((sa) & (share))) { \
+ *res = A_0; \
+ return NT_STATUS_SHARING_VIOLATION; \
+ }} while (0)
+
+ *res = A_0;
+ if (am2 & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)) {
+ *res += A_W;
+ }
+ if (am2 & SEC_FILE_READ_DATA) {
+ *res += A_R;
+ } else if ((am2 & SEC_FILE_EXECUTE) && read_for_execute) {
+ *res += A_R;
+ }
+
+ /* if either open involves no read.write or delete access then
+ it can't conflict */
+ if (!(am1 & (SEC_FILE_WRITE_DATA |
+ SEC_FILE_APPEND_DATA |
+ SEC_FILE_READ_DATA |
+ SEC_FILE_EXECUTE |
+ SEC_STD_DELETE))) {
+ return NT_STATUS_OK;
+ }
+ if (!(am2 & (SEC_FILE_WRITE_DATA |
+ SEC_FILE_APPEND_DATA |
+ SEC_FILE_READ_DATA |
+ SEC_FILE_EXECUTE |
+ SEC_STD_DELETE))) {
+ return NT_STATUS_OK;
+ }
+
+ /* check the basic share access */
+ CHECK_MASK(am1, sa2,
+ SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA,
+ NTCREATEX_SHARE_ACCESS_WRITE);
+ CHECK_MASK(am2, sa1,
+ SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA,
+ NTCREATEX_SHARE_ACCESS_WRITE);
+
+ CHECK_MASK(am1, sa2,
+ SEC_FILE_READ_DATA | SEC_FILE_EXECUTE,
+ NTCREATEX_SHARE_ACCESS_READ);
+ CHECK_MASK(am2, sa1,
+ SEC_FILE_READ_DATA | SEC_FILE_EXECUTE,
+ NTCREATEX_SHARE_ACCESS_READ);
+
+ CHECK_MASK(am1, sa2,
+ SEC_STD_DELETE,
+ NTCREATEX_SHARE_ACCESS_DELETE);
+ CHECK_MASK(am2, sa1,
+ SEC_STD_DELETE,
+ NTCREATEX_SHARE_ACCESS_DELETE);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ a denytest for ntcreatex
+ */
+static bool torture_ntdenytest(struct torture_context *tctx,
+ struct smbcli_state *cli1, struct smbcli_state *cli2, int client)
+{
+ const struct bit_value share_access_bits[] = {
+ { NTCREATEX_SHARE_ACCESS_READ, "S_R" },
+ { NTCREATEX_SHARE_ACCESS_WRITE, "S_W" },
+ { NTCREATEX_SHARE_ACCESS_DELETE, "S_D" }
+ };
+ const struct bit_value access_mask_bits[] = {
+ { SEC_FILE_READ_DATA, "R_DATA" },
+ { SEC_FILE_WRITE_DATA, "W_DATA" },
+ { SEC_FILE_READ_ATTRIBUTE, "R_ATTR" },
+ { SEC_FILE_WRITE_ATTRIBUTE, "W_ATTR" },
+ { SEC_FILE_READ_EA, "R_EAS " },
+ { SEC_FILE_WRITE_EA, "W_EAS " },
+ { SEC_FILE_APPEND_DATA, "A_DATA" },
+ { SEC_FILE_EXECUTE, "EXEC " }
+ };
+ int fnum1;
+ int i;
+ bool correct = true;
+ struct timeval tv, tv_start;
+ const char *fname;
+ int nbits1 = ARRAY_SIZE(share_access_bits);
+ int nbits2 = ARRAY_SIZE(access_mask_bits);
+ union smb_open io1, io2;
+ extern int torture_numops;
+ int failures = 0;
+ uint8_t buf[1];
+
+ torture_comment(tctx, "format: server correct\n");
+
+ ZERO_STRUCT(buf);
+
+ fname = talloc_asprintf(cli1, "\\ntdeny_%d.dll", client);
+
+ smbcli_unlink(cli1->tree, 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);
+
+ GetTimeOfDay(&tv_start);
+
+ io1.ntcreatex.level = RAW_OPEN_NTCREATEX;
+ io1.ntcreatex.in.root_fid = 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;i<torture_numops;i++) {
+ NTSTATUS status1, status2, status2_p;
+ int64_t tdif;
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+ enum deny_result res, res2;
+ int b_sa1 = random() & ((1<<nbits1)-1);
+ int b_am1 = random() & ((1<<nbits2)-1);
+ int b_sa2 = random() & ((1<<nbits1)-1);
+ int b_am2 = random() & ((1<<nbits2)-1);
+ bool read_for_execute;
+
+ progress_bar(tctx, i, torture_numops);
+
+ io1.ntcreatex.in.share_access = map_bits(share_access_bits, b_sa1, nbits1);
+ io1.ntcreatex.in.access_mask = map_bits(access_mask_bits, b_am1, nbits2);
+
+ io2.ntcreatex.in.share_access = map_bits(share_access_bits, b_sa2, nbits1);
+ io2.ntcreatex.in.access_mask = map_bits(access_mask_bits, b_am2, nbits2);
+
+ status1 = smb_raw_open(cli1->tree, 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);
+
+ GetTimeOfDay(&tv);
+ tdif = usec_time_diff(&tv, &tv_start);
+ tdif /= 1000;
+ 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, "finshed 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 CHECK_STATUS(status, correct) do { \
+ if (!NT_STATUS_EQUAL(status, correct)) { \
+ torture_comment(tctx, "(%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_comment(tctx, "(%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;
+}
+
+
diff --git a/source4/torture/basic/dir.c b/source4/torture/basic/dir.c
new file mode 100644
index 0000000000..9a1ae2f744
--- /dev/null
+++ b/source4/torture/basic/dir.c
@@ -0,0 +1,165 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/libcli.h"
+#include "torture/torture.h"
+#include "torture/util.h"
+#include "system/filesys.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;
+
+ torture_comment(tctx, "Creating %d random filenames\n", torture_numops);
+
+ srandom(0);
+ tv = timeval_current();
+ for (i=0;i<torture_numops;i++) {
+ char *fname;
+ asprintf(&fname, "\\%x", (int)random());
+ fnum = smbcli_open(cli->tree, 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;i<torture_numops;i++) {
+ char *fname;
+ asprintf(&fname, "\\%x", (int)random());
+ smbcli_unlink(cli->tree, 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;
+
+ 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;i<torture_entries;i++) {
+ char *fname;
+ asprintf(&fname, "\\LISTDIR\\f%d", i);
+ fnum = smbcli_nt_create_full(cli->tree, 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;i<torture_entries;i++) {
+ char *fname;
+ asprintf(&fname, "\\LISTDIR\\d%d", i);
+ if (NT_STATUS_IS_ERR(smbcli_mkdir(cli->tree, 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 0000000000..89e05d6839
--- /dev/null
+++ b/source4/torture/basic/disconnect.c
@@ -0,0 +1,174 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#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"
+
+#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;
+ NTSTATUS status;
+ struct smbcli_request *req1, *req2;
+
+ printf("trying open/disconnect\n");
+
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 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);
+
+ 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);
+
+ 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;
+ }
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ for (i=0;i<torture_numops;i++) {
+ ret &= test_disconnect_lock(cli, mem_ctx);
+ if (!torture_open_connection(&cli, torture, 0)) {
+ return false;
+ }
+
+ ret &= test_disconnect_open(cli, mem_ctx);
+ if (!torture_open_connection(&cli, torture, 0)) {
+ return false;
+ }
+
+ if (torture_setting_bool(torture, "samba3", false)) {
+ /*
+ * In Samba3 it might happen that the old smbd from
+ * test_disconnect_lock is not scheduled before the
+ * new process comes in. Try to get rid of the random
+ * failures in the build farm.
+ */
+ msleep(200);
+ }
+ }
+
+ smb_raw_exit(cli->session);
+ 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 0000000000..06a960bc81
--- /dev/null
+++ b/source4/torture/basic/locking.c
@@ -0,0 +1,817 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/libcli.h"
+#include "torture/smbtorture.h"
+#include "torture/util.h"
+#include "system/time.h"
+#include "system/filesys.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;
+ uint_t lock_timeout;
+
+ if (!torture_setup_dir(cli1, BASEDIR)) {
+ return false;
+ }
+
+ 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(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(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",
+ (uint_t)(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;
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ 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");
+
+ if (!torture_setup_dir(cli1, BASEDIR)) {
+ return false;
+ }
+
+ 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;i<torture_numops;i++) {
+ NEXT_OFFSET;
+ torture_assert_ntstatus_ok(tctx,
+ smbcli_lock(cli1->tree, 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;i<torture_numops;i++) {
+ NEXT_OFFSET;
+
+ torture_assert(tctx,
+ !NT_STATUS_IS_OK(smbcli_lock(cli1->tree, 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;i<torture_numops;i++) {
+ NEXT_OFFSET;
+
+ torture_assert_ntstatus_ok(tctx,
+ smbcli_unlock(cli1->tree, 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 processs %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 processs 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 processs %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;
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ 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 0000000000..98c53aac21
--- /dev/null
+++ b/source4/torture/basic/mangle_test.c
@@ -0,0 +1,206 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "system/filesys.h"
+#include "system/dir.h"
+#include "../tdb/include/tdb.h"
+#include "../lib/util/util_tdb.h"
+#include "libcli/libcli.h"
+#include "torture/util.h"
+
+static TDB_CONTEXT *tdb;
+
+#define NAME_LENGTH 20
+
+static uint_t 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._-$~...";
+ uint_t max_idx = strlen(chars);
+ uint_t 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<len;i++) {
+ p[i] = chars[random() % max_idx];
+ }
+
+ p[i] = 0;
+
+ if (ISDOT(p) || ISDOTDOT(p)) {
+ p[0] = '_';
+ }
+
+ /* have a high probability of a common lead char */
+ if (random() % 2 == 0) {
+ p[0] = 'A';
+ }
+
+ /* and a medium probability of a common lead string */
+ if ((len > 5) && (random() % 10 == 0)) {
+ strncpy(p, "ABCDE", 5);
+ }
+
+ /* 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;i<torture_numops;i++) {
+ char *name;
+
+ name = gen_name(torture);
+
+ if (!test_one(torture, cli, name)) {
+ break;
+ }
+ if (total && total % 100 == 0) {
+ if (torture_setting_bool(torture, "progress", true)) {
+ printf("collisions %u/%u - %.2f%% (%u failures)\r",
+ collisions, total, (100.0*collisions) / total, failures);
+ }
+ }
+ }
+
+ smbcli_unlink(cli->tree, "\\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 0000000000..f4f91c8ba3
--- /dev/null
+++ b/source4/torture/basic/misc.c
@@ -0,0 +1,948 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#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/raw/ioctl.h"
+#include "libcli/libcli.h"
+#include "lib/events/events.h"
+#include "libcli/resolve/resolve.h"
+#include "auth/credentials/credentials.h"
+#include "librpc/gen_ndr/ndr_nbt.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"
+
+extern struct cli_credentials *cmdline_credentials;
+
+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;i<torture_numops;i++) {
+ uint_t n = (uint_t)random()%10;
+ if (i % 10 == 0) {
+ if (torture_setting_bool(tctx, "progress", true)) {
+ torture_comment(tctx, "%d\r", i);
+ fflush(stdout);
+ }
+ }
+ asprintf(&fname, "\\torture.%u", n);
+
+ if (!wait_lock(c, fnum2, n*sizeof(int), sizeof(int))) {
+ return false;
+ }
+
+ fnum = smbcli_open(c->tree, 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;i<torture_numops;i++) {
+ if (!torture_open_connection(&cli[i], tctx, i)) {
+ return false;
+ }
+ if (torture_setting_bool(tctx, "progress", true)) {
+ torture_comment(tctx, "opened %d connections\r", i);
+ fflush(stdout);
+ }
+ }
+
+ torture_comment(tctx, "\nStarting pings\n");
+
+ while (1) {
+ for (i=0;i<torture_numops;i++) {
+ NTSTATUS status;
+ if (cli[i]) {
+ status = smbcli_chkpath(cli[i]->tree, "\\");
+ 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;
+}
+
+/*
+test how many open files this server supports on the one socket
+*/
+bool run_maxfidtest(struct torture_context *tctx, struct smbcli_state *cli, int dummy)
+{
+#define MAXFID_TEMPLATE "\\maxfid\\fid%d\\maxfid.%d.%d"
+ char *fname;
+ int fnums[0x11000], i;
+ int retries=4, maxfid;
+ bool correct = true;
+
+ 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) {
+ asprintf(&fname, "\\maxfid\\fid%d", i/1000);
+ 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);
+ }
+ asprintf(&fname, MAXFID_TEMPLATE, i/1000, i,(int)getpid());
+ 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);
+ i--;
+
+ maxfid = i;
+
+ torture_comment(tctx, "cleaning up\n");
+ for (i=0;i<maxfid/2;i++) {
+ asprintf(&fname, MAXFID_TEMPLATE, i/1000, i,(int)getpid());
+ if (NT_STATUS_IS_ERR(smbcli_close(cli->tree, 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);
+
+ asprintf(&fname, MAXFID_TEMPLATE, (maxfid-i)/1000, maxfid-i,(int)getpid());
+ if (NT_STATUS_IS_ERR(smbcli_close(cli->tree, fnums[maxfid-i]))) {
+ torture_comment(tctx, "Close of fnum %d failed - %s\n", fnums[maxfid-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 %6d\r", i, maxfid-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");
+ if (!torture_close_connection(cli)) {
+ correct = false;
+ }
+ 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 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 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;
+ } *lp_params;
+};
+
+/*
+ init params using lp_parm_xxx
+ return number of unclist entries
+*/
+static int init_benchrw_params(struct torture_context *tctx,
+ struct 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 = lp_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->lp_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->lp_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->lp_params->blocksize));
+ rd.generic.level = RAW_READ_READX;
+ rd.readx.in.file.fnum = state->fnum ;
+ rd.readx.in.offset = state->readcnt*state->lp_params->blocksize;
+ rd.readx.in.mincnt = state->lp_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->lp_params->writeblocks){
+ state->readcnt++;
+ }else{
+ /*start reading from beginn 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->lp_params->blocksize,
+ (state->lp_params->writeblocks*state->lp_params->blocksize));
+ wr.generic.level = RAW_WRITE_WRITEX ;
+ wr.writex.in.file.fnum = state->fnum ;
+ wr.writex.in.offset = state->writecnt *
+ state->lp_params->blocksize;
+ wr.writex.in.wmode = 0 ;
+ wr.writex.in.remaining = (state->lp_params->writeblocks *
+ state->lp_params->blocksize)-
+ ((state->writecnt+1)*state->
+ lp_params->blocksize);
+ wr.writex.in.count = state->lp_params->blocksize;
+ wr.writex.in.data = state->buffer;
+ state->writecnt++;
+ if(state->writecnt == state->lp_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->lp_params->blocksize);
+ NT_STATUS_HAVE_NO_MEMORY(writedata);
+ generate_random_buffer(writedata,state->lp_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;
+
+ /*dont 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->lp_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->lp_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;
+ }
+}
+
+/*
+ establishs 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=lp_smb_ports(tctx->lp_ctx);
+ smb->in.socket_options = lp_socket_options(tctx->lp_ctx);
+ smb->in.called_name = strupper_talloc(mem_ctx, host);
+ smb->in.service_type=NULL;
+ smb->in.credentials=cmdline_credentials;
+ smb->in.fallback_to_anonymous=false;
+ smb->in.iconv_convenience = lp_iconv_convenience(tctx->lp_ctx);
+ smb->in.gensec_settings = lp_gensec_settings(mem_ctx, tctx->lp_ctx);
+ smb->in.workgroup=workgroup;
+ lp_smbcli_options(tctx->lp_ctx, &smb->in.options);
+ lp_smbcli_session_options(tctx->lp_ctx, &smb->in.session_options);
+
+ return smb_composite_connect_send(smb,mem_ctx,
+ lp_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 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 lp_parm_xxx */
+ num_unc_names = init_benchrw_params(tctx,&lpparams);
+
+ /* init private data structs*/
+ for(i = 0; i<torture_nprocs;i++){
+ state[i]=talloc(tctx,struct benchrw_state);
+ state[i]->tctx = tctx;
+ state[i]->completed=0;
+ state[i]->num_parallel_requests=0;
+ state[i]->lp_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; i<torture_nprocs;i++){
+ switch (state[i]->mode){
+ /*open multiple connections with the same userid */
+ case START:
+ smb_con = talloc(
+ 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 occured , 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;
+ case FINISHED:
+ finished++;
+ break;
+ default:
+ event_loop_once(ev);
+ }
+ }
+ }
+
+ return success;
+}
+
diff --git a/source4/torture/basic/properties.c b/source4/torture/basic/properties.c
new file mode 100644
index 0000000000..1825dff993
--- /dev/null
+++ b/source4/torture/basic/properties.c
@@ -0,0 +1,119 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/libcli.h"
+#include "torture/util.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 0000000000..12fd5e1c99
--- /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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/libcli.h"
+#include "torture/torture.h"
+#include "torture/util.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 0000000000..a6173e4501
--- /dev/null
+++ b/source4/torture/basic/scanner.c
@@ -0,0 +1,629 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "libcli/libcli.h"
+#include "torture/util.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "system/filesys.h"
+#include "param/param.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 existance 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 existance 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(
+ &param[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(
+ &param[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(
+ &param[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 existance of a nttrans call
+****************************************************************************/
+static bool scan_nttrans(struct smb_iconv_convenience *iconv_convenience,
+ 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(
+ &param[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(
+ &param[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(&param[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";
+ struct smb_iconv_convenience *iconv_convenience = lp_iconv_convenience(torture->lp_ctx);
+ 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(iconv_convenience,
+ cli, op, level, fnum, dnum, fname);
+ }
+
+ for (level = 0x100; level <= 0x130; level++) {
+ scan_nttrans(iconv_convenience,
+ cli, op, level, fnum, dnum, fname);
+ }
+
+ for (level = 1000; level < 1050; level++) {
+ scan_nttrans(iconv_convenience,
+ 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 0000000000..3fdd9a9bbd
--- /dev/null
+++ b/source4/torture/basic/secleak.c
@@ -0,0 +1,78 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.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 "libcli/smb_composite/proto.h"
+#include "auth/credentials/credentials.h"
+#include "param/param.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;
+
+ lp_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 = lp_workgroup(tctx->lp_ctx);
+ setup.in.credentials = cli_credentials_init(session);
+ setup.in.gensec_settings = lp_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(NULL);
+ int timelimit = torture_setting_int(tctx, "timelimit", 20);
+
+ while (time(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 0000000000..9e13021d20
--- /dev/null
+++ b/source4/torture/basic/unlink.c
@@ -0,0 +1,92 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "system/filesys.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/libcli.h"
+#include "torture/util.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 = 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 0000000000..ca6c2fd576
--- /dev/null
+++ b/source4/torture/basic/utable.c
@@ -0,0 +1,191 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "system/filesys.h"
+#include "system/locale.h"
+#include "libcli/libcli.h"
+#include "torture/util.h"
+#include "param/param.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);
+ convert_string_convenience(lp_iconv_convenience(tctx->lp_ctx), CH_UTF16, CH_UNIX,
+ c2, 2,
+ p, sizeof(fname)-strlen(fname), &len, false);
+ 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)));
+ write(fd, valid, 0x10000);
+ close(fd);
+ torture_comment(tctx, "wrote valid.dat\n");
+
+ return true;
+}
+
+
+static char *form_name(struct smb_iconv_convenience *iconv_convenience, int c)
+{
+ static char fname[256];
+ uint8_t c2[4];
+ char *p;
+ size_t len;
+
+ strncpy(fname, "\\utable\\", sizeof(fname)-1);
+ p = fname+strlen(fname);
+ SSVAL(c2, 0, c);
+
+ convert_string_convenience(iconv_convenience, CH_UTF16, CH_UNIX,
+ c2, 2,
+ p, sizeof(fname)-strlen(fname), &len, false);
+ 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(lp_iconv_convenience(tctx->lp_ctx), c);
+ 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);
+
+ torture_assert(tctx, fnum != -1,
+ talloc_asprintf(tctx,
+ "Failed to create file with char %04x\n", c));
+
+ 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; i<size/sizeof(int); i++) {
+ torture_comment(tctx, "%04x ", c2[i]);
+ equiv[c][i+1] = c2[i];
+ }
+ torture_comment(tctx, "\n");
+ }
+
+ smbcli_write(cli->tree, fnum, 0, &c, size, sizeof(c));
+ smbcli_close(cli->tree, fnum);
+ }
+
+ smbcli_unlink(cli->tree, "\\utable\\*");
+ smbcli_rmdir(cli->tree, "\\utable");
+
+ return true;
+}
diff --git a/source4/torture/config.mk b/source4/torture/config.mk
new file mode 100644
index 0000000000..895fef6174
--- /dev/null
+++ b/source4/torture/config.mk
@@ -0,0 +1,364 @@
+[SUBSYSTEM::TORTURE_UTIL]
+PRIVATE_DEPENDENCIES = LIBCLI_RAW
+PUBLIC_DEPENDENCIES = torture POPT_CREDENTIALS
+
+TORTURE_UTIL_OBJ_FILES = $(addprefix $(torturesrcdir)/, util_smb.o)
+
+#################################
+# Start SUBSYSTEM TORTURE_BASIC
+[MODULE::TORTURE_BASIC]
+SUBSYSTEM = smbtorture
+INIT_FUNCTION = torture_base_init
+OUTPUT_TYPE = MERGED_OBJ
+PRIVATE_DEPENDENCIES = \
+ LIBCLI_SMB POPT_CREDENTIALS \
+ TORTURE_UTIL LIBCLI_RAW \
+ TORTURE_RAW
+# End SUBSYSTEM TORTURE_BASIC
+#################################
+
+TORTURE_BASIC_OBJ_FILES = $(addprefix $(torturesrcdir)/basic/, \
+ base.o \
+ misc.o \
+ scanner.o \
+ utable.o \
+ charset.o \
+ mangle_test.o \
+ denytest.o \
+ aliases.o \
+ locking.o \
+ secleak.o \
+ rename.o \
+ dir.o \
+ delete.o \
+ unlink.o \
+ disconnect.o \
+ delaywrite.o \
+ attr.o \
+ properties.o)
+
+$(eval $(call proto_header_template,$(torturesrcdir)/basic/proto.h,$(TORTURE_BASIC_OBJ_FILES:.o=.c)))
+
+#################################
+# Start SUBSYSTEM TORTURE_RAW
+[MODULE::TORTURE_RAW]
+OUTPUT_TYPE = MERGED_OBJ
+SUBSYSTEM = smbtorture
+INIT_FUNCTION = torture_raw_init
+PRIVATE_DEPENDENCIES = \
+ LIBCLI_SMB LIBCLI_LSA LIBCLI_SMB_COMPOSITE \
+ POPT_CREDENTIALS TORTURE_UTIL
+# End SUBSYSTEM TORTURE_RAW
+#################################
+
+TORTURE_RAW_OBJ_FILES = $(addprefix $(torturesrcdir)/raw/, \
+ qfsinfo.o \
+ qfileinfo.o \
+ setfileinfo.o \
+ search.o \
+ close.o \
+ open.o \
+ mkdir.o \
+ oplock.o \
+ notify.o \
+ mux.o \
+ ioctl.o \
+ chkpath.o \
+ unlink.o \
+ read.o \
+ context.o \
+ write.o \
+ lock.o \
+ pingpong.o \
+ lockbench.o \
+ lookuprate.o \
+ tconrate.o \
+ openbench.o \
+ rename.o \
+ eas.o \
+ streams.o \
+ acls.o \
+ seek.o \
+ samba3hide.o \
+ samba3misc.o \
+ composite.o \
+ raw.o \
+ offline.o)
+
+$(eval $(call proto_header_template,$(torturesrcdir)/raw/proto.h,$(TORTURE_RAW_OBJ_FILES:.o=.c)))
+
+mkinclude smb2/config.mk
+mkinclude winbind/config.mk
+
+[SUBSYSTEM::TORTURE_NDR]
+PRIVATE_DEPENDENCIES = torture SERVICE_SMB
+
+TORTURE_NDR_OBJ_FILES = $(addprefix $(torturesrcdir)/ndr/, ndr.o winreg.o atsvc.o lsa.o epmap.o dfs.o netlogon.o drsuapi.o spoolss.o samr.o)
+
+$(eval $(call proto_header_template,$(torturesrcdir)/ndr/proto.h,$(TORTURE_NDR_OBJ_FILES:.o=.c)))
+
+[MODULE::torture_rpc]
+OUTPUT_TYPE = MERGED_OBJ
+# TORTURE_NET and TORTURE_NBT use functions from torture_rpc...
+#OUTPUT_TYPE = MERGED_OBJ
+SUBSYSTEM = smbtorture
+INIT_FUNCTION = torture_rpc_init
+PRIVATE_DEPENDENCIES = \
+ NDR_TABLE RPC_NDR_UNIXINFO dcerpc_samr RPC_NDR_WINREG RPC_NDR_INITSHUTDOWN \
+ RPC_NDR_OXIDRESOLVER RPC_NDR_EVENTLOG RPC_NDR_ECHO RPC_NDR_SVCCTL \
+ RPC_NDR_NETLOGON dcerpc_atsvc dcerpc_mgmt 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_ROT RPC_NDR_DSSETUP \
+ RPC_NDR_REMACT RPC_NDR_OXIDRESOLVER RPC_NDR_NTSVCS WB_HELPER LIBSAMBA-NET \
+ LIBCLI_AUTH POPT_CREDENTIALS TORTURE_LDAP TORTURE_LDB TORTURE_UTIL TORTURE_RAP \
+ dcerpc_server service process_model ntvfs SERVICE_SMB RPC_NDR_BROWSER
+
+torture_rpc_OBJ_FILES = $(addprefix $(torturesrcdir)/rpc/, \
+ join.o lsa.o lsa_lookup.o session_key.o echo.o dfs.o drsuapi.o \
+ drsuapi_cracknames.o dssync.o spoolss.o spoolss_notify.o spoolss_win.o \
+ unixinfo.o samr.o samr_accessmask.o wkssvc.o srvsvc.o svcctl.o atsvc.o \
+ eventlog.o epmapper.o winreg.o initshutdown.o oxidresolve.o remact.o mgmt.o \
+ scanner.o autoidl.o countcalls.o testjoin.o schannel.o netlogon.o remote_pac.o samlogon.o \
+ samsync.o bind.o dssetup.o alter_context.o bench.o samba3rpc.o rpc.o async_bind.o \
+ handles.o frsapi.o object_uuid.o ntsvcs.o browser.o)
+
+$(eval $(call proto_header_template,$(torturesrcdir)/rpc/proto.h,$(torture_rpc_OBJ_FILES:.o=.c)))
+
+#################################
+# Start SUBSYSTEM TORTURE_RAP
+[MODULE::TORTURE_RAP]
+OUTPUT_TYPE = MERGED_OBJ
+SUBSYSTEM = smbtorture
+INIT_FUNCTION = torture_rap_init
+PRIVATE_DEPENDENCIES = TORTURE_UTIL LIBCLI_SMB
+# End SUBSYSTEM TORTURE_RAP
+#################################
+
+TORTURE_RAP_OBJ_FILES = $(torturesrcdir)/rap/rap.o
+
+$(eval $(call proto_header_template,$(torturesrcdir)/rap/proto.h,$(TORTURE_RAP_OBJ_FILES:.o=.c)))
+
+#################################
+# Start SUBSYSTEM TORTURE_AUTH
+[MODULE::TORTURE_AUTH]
+OUTPUT_TYPE = MERGED_OBJ
+SUBSYSTEM = smbtorture
+PRIVATE_DEPENDENCIES = \
+ LIBCLI_SMB gensec auth KERBEROS \
+ POPT_CREDENTIALS SMBPASSWD torture
+# End SUBSYSTEM TORTURE_AUTH
+#################################
+
+TORTURE_AUTH_OBJ_FILES = $(addprefix $(torturesrcdir)/auth/, ntlmssp.o pac.o)
+
+$(eval $(call proto_header_template,$(torturesrcdir)/auth/proto.h,$(TORTURE_AUTH_OBJ_FILES:.o=.c)))
+
+mkinclude local/config.mk
+
+#################################
+# Start MODULE TORTURE_NBENCH
+[MODULE::TORTURE_NBENCH]
+OUTPUT_TYPE = MERGED_OBJ
+SUBSYSTEM = smbtorture
+INIT_FUNCTION = torture_nbench_init
+PRIVATE_DEPENDENCIES = TORTURE_UTIL
+# End MODULE TORTURE_NBENCH
+#################################
+
+TORTURE_NBENCH_OBJ_FILES = $(addprefix $(torturesrcdir)/nbench/, nbio.o nbench.o)
+
+$(eval $(call proto_header_template,$(torturesrcdir)/nbench/proto.h,$(TORTURE_NBENCH_OBJ_FILES:.o=.c)))
+
+#################################
+# Start MODULE TORTURE_UNIX
+[MODULE::TORTURE_UNIX]
+SUBSYSTEM = smbtorture
+OUTPUT_TYPE = MERGED_OBJ
+INIT_FUNCTION = torture_unix_init
+PRIVATE_DEPENDENCIES = TORTURE_UTIL
+# End MODULE TORTURE_UNIX
+#################################
+
+TORTURE_UNIX_OBJ_FILES = $(addprefix $(torturesrcdir)/unix/, unix.o whoami.o unix_info2.o)
+
+$(eval $(call proto_header_template,$(torturesrcdir)/unix/proto.h,$(TORTURE_UNIX_OBJ_FILES:.o=.c)))
+
+#################################
+# Start SUBSYSTEM TORTURE_LDAP
+[MODULE::TORTURE_LDAP]
+SUBSYSTEM = smbtorture
+OUTPUT_TYPE = MERGED_OBJ
+INIT_FUNCTION = torture_ldap_init
+PRIVATE_DEPENDENCIES = \
+ LIBCLI_LDAP LIBCLI_CLDAP SAMDB POPT_CREDENTIALS torture
+# End SUBSYSTEM TORTURE_LDAP
+#################################
+
+TORTURE_LDAP_OBJ_FILES = $(addprefix $(torturesrcdir)/ldap/, common.o basic.o schema.o uptodatevector.o cldap.o cldapbench.o)
+
+$(eval $(call proto_header_template,$(torturesrcdir)/ldap/proto.h,$(TORTURE_LDAP_OBJ_FILES:.o=.c)))
+
+#################################
+# Start SUBSYSTEM TORTURE_LDB
+[MODULE::TORTURE_LDB]
+SUBSYSTEM = smbtorture
+OUTPUT_TYPE = MERGED_OBJ
+INIT_FUNCTION = torture_ldb_init
+PRIVATE_DEPENDENCIES = \
+ LDB_WRAP
+# End SUBSYSTEM TORTURE_LDB
+#################################
+
+TORTURE_LDB_OBJ_FILES = $(addprefix $(torturesrcdir)/ldb/, ldb.o)
+
+$(eval $(call proto_header_template,$(torturesrcdir)/ldb/proto.h,$(TORTURE_LDB_OBJ_FILES:.o=.c)))
+
+#################################
+# Start SUBSYSTEM TORTURE_NBT
+[MODULE::TORTURE_NBT]
+SUBSYSTEM = smbtorture
+OUTPUT_TYPE = MERGED_OBJ
+INIT_FUNCTION = torture_nbt_init
+PRIVATE_DEPENDENCIES = \
+ LIBCLI_SMB LIBCLI_NBT LIBCLI_DGRAM LIBCLI_WREPL torture_rpc
+# End SUBSYSTEM TORTURE_NBT
+#################################
+
+TORTURE_NBT_OBJ_FILES = $(addprefix $(torturesrcdir)/nbt/, query.o register.o \
+ wins.o winsbench.o winsreplication.o dgram.o nbt.o)
+
+$(eval $(call proto_header_template,$(torturesrcdir)/nbt/proto.h,$(TORTURE_NBT_OBJ_FILES:.o=.c)))
+
+#################################
+# Start SUBSYSTEM TORTURE_NET
+[MODULE::TORTURE_NET]
+SUBSYSTEM = smbtorture
+OUTPUT_TYPE = MERGED_OBJ
+INIT_FUNCTION = torture_net_init
+PRIVATE_DEPENDENCIES = \
+ LIBSAMBA-NET \
+ POPT_CREDENTIALS \
+ torture_rpc \
+ PROVISION
+# End SUBSYSTEM TORTURE_NET
+#################################
+
+TORTURE_NET_OBJ_FILES = $(addprefix $(torturesrcdir)/libnet/, libnet.o \
+ utils.o userinfo.o userman.o groupinfo.o groupman.o \
+ domain.o libnet_lookup.o libnet_user.o libnet_group.o \
+ libnet_share.o libnet_rpc.o libnet_domain.o libnet_BecomeDC.o)
+
+$(eval $(call proto_header_template,$(torturesrcdir)/libnet/proto.h,$(TORTURE_NET_OBJ_FILES:.o=.c)))
+
+#################################
+# Start BINARY smbtorture
+[BINARY::smbtorture]
+INSTALLDIR = BINDIR
+PRIVATE_DEPENDENCIES = \
+ torture \
+ LIBPOPT \
+ POPT_SAMBA \
+ POPT_CREDENTIALS \
+ dcerpc \
+ LIBCLI_SMB \
+ SMBREADLINE
+# End BINARY smbtorture
+#################################
+
+smbtorture_OBJ_FILES = $(torturesrcdir)/smbtorture.o $(torturesrcdir)/torture.o
+
+PUBLIC_HEADERS += $(torturesrcdir)/smbtorture.h
+MANPAGES += $(torturesrcdir)/man/smbtorture.1
+
+#################################
+# Start BINARY gentest
+[BINARY::gentest]
+INSTALLDIR = BINDIR
+PRIVATE_DEPENDENCIES = \
+ LIBSAMBA-HOSTCONFIG \
+ LIBSAMBA-UTIL \
+ LIBPOPT \
+ POPT_SAMBA \
+ POPT_CREDENTIALS \
+ LIBCLI_SMB \
+ LIBCLI_RAW
+# End BINARY gentest
+#################################
+
+gentest_OBJ_FILES = $(torturesrcdir)/gentest.o
+
+MANPAGES += $(torturesrcdir)/man/gentest.1
+
+#################################
+# Start BINARY masktest
+[BINARY::masktest]
+INSTALLDIR = BINDIR
+PRIVATE_DEPENDENCIES = \
+ LIBSAMBA-HOSTCONFIG \
+ LIBSAMBA-UTIL \
+ LIBPOPT \
+ POPT_SAMBA \
+ POPT_CREDENTIALS \
+ LIBCLI_SMB
+# End BINARY masktest
+#################################
+
+masktest_OBJ_FILES = $(torturesrcdir)/masktest.o
+
+MANPAGES += $(torturesrcdir)/man/masktest.1
+
+#################################
+# Start BINARY locktest
+[BINARY::locktest]
+INSTALLDIR = BINDIR
+PRIVATE_DEPENDENCIES = \
+ LIBPOPT \
+ POPT_SAMBA \
+ POPT_CREDENTIALS \
+ LIBSAMBA-UTIL \
+ LIBCLI_SMB \
+ LIBSAMBA-HOSTCONFIG
+# End BINARY locktest
+#################################
+
+locktest_OBJ_FILES = $(torturesrcdir)/locktest.o
+
+MANPAGES += $(torturesrcdir)/man/locktest.1
+
+GCOV=0
+
+ifeq ($(MAKECMDGOALS),gcov)
+GCOV=1
+endif
+
+ifeq ($(MAKECMDGOALS),lcov)
+GCOV=1
+endif
+
+ifeq ($(MAKECMDGOALS),testcov-html)
+GCOV=1
+endif
+
+ifeq ($(GCOV),1)
+CFLAGS += --coverage
+LDFLAGS += --coverage
+endif
+
+COV_TARGET = test
+
+gcov: test
+ for I in $(sort $(dir $(ALL_OBJS))); \
+ do $(GCOV) -p -o $$I $$I/*.c; \
+ done
+
+samba.info: test
+ -rm heimdal/lib/*/{lex,parse}.{gcda,gcno}
+ lcov --base-directory `pwd` --directory . --capture --output-file samba.info
+
+lcov: samba.info
+ genhtml -o coverage $<
+
+testcov-html:: lcov
+
+clean::
+ @rm -f samba.info
diff --git a/source4/torture/gentest.c b/source4/torture/gentest.c
new file mode 100644
index 0000000000..be02f33378
--- /dev/null
+++ b/source4/torture/gentest.c
@@ -0,0 +1,3262 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/cmdline/popt_common.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"
+
+#define NSERVERS 2
+#define NINSTANCES 2
+
+/* global options */
+static struct gentest_options {
+ int showall;
+ int analyze;
+ int analyze_always;
+ int analyze_continuous;
+ uint_t max_open_handles;
+ uint_t seed;
+ uint_t 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;
+ uint_t instance;
+ struct smb2_handle smb2_handle[NSERVERS]; /* SMB2 */
+ uint16_t smb_handle[NSERVERS]; /* SMB */
+ const char *name;
+} *open_handles;
+static uint_t 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 {
+ uint_t 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;
+ uint_t seed;
+ NTSTATUS status;
+ uint_t 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;h<options.max_open_handles;h++) {
+ if (!open_handles[h].active) continue;
+ for (i=0;i<NSERVERS;i++) {
+ NTSTATUS status;
+ if (options.smb2) {
+ status = smb2_util_close(servers[i].smb2_tree[open_handles[h].instance],
+ open_handles[h].smb2_handle[i]);
+ } else {
+ status = smbcli_close(servers[i].smb_tree[open_handles[h].instance],
+ open_handles[h].smb_handle[i]);
+ }
+ if (NT_STATUS_IS_ERR(status)) {
+ return false;
+ }
+ open_handles[h].active = false;
+ }
+ }
+
+ return true;
+}
+
+
+
+
+/*****************************************************
+connect to the servers
+*******************************************************/
+static bool connect_servers(struct tevent_context *ev,
+ struct loadparm_context *lp_ctx)
+{
+ int i, j;
+
+ if (options.fast_reconnect && servers[0].smb2_tree[0]) {
+ if (connect_servers_fast()) {
+ return true;
+ }
+ }
+
+ /* close any existing connections */
+ for (i=0;i<NSERVERS;i++) {
+ for (j=0;j<NINSTANCES;j++) {
+ if (servers[i].smb2_tree[j]) {
+ smb2_tdis(servers[i].smb2_tree[j]);
+ talloc_free(servers[i].smb2_tree[j]);
+ servers[i].smb2_tree[j] = NULL;
+ }
+ if (servers[i].smb_tree[j]) {
+ smb_tree_disconnect(servers[i].smb_tree[j]);
+ talloc_free(servers[i].smb_tree[j]);
+ servers[i].smb_tree[j] = NULL;
+ }
+ }
+ }
+
+ for (i=0;i<NSERVERS;i++) {
+ for (j=0;j<NINSTANCES;j++) {
+ NTSTATUS status;
+ struct smbcli_options smb_options;
+ struct smbcli_session_options smb_session_options;
+ lp_smbcli_options(lp_ctx, &smb_options);
+ lp_smbcli_session_options(lp_ctx, &smb_session_options);
+
+ printf("Connecting to \\\\%s\\%s as %s - instance %d\n",
+ servers[i].server_name, servers[i].share_name,
+ servers[i].credentials->username, j);
+
+ cli_credentials_set_workstation(servers[i].credentials,
+ "gentest", CRED_SPECIFIED);
+
+ if (options.smb2) {
+ status = smb2_connect(NULL, servers[i].server_name,
+ lp_smb_ports(lp_ctx),
+ servers[i].share_name,
+ lp_resolve_context(lp_ctx),
+ servers[i].credentials,
+ &servers[i].smb2_tree[j],
+ ev, &smb_options,
+ lp_socket_options(lp_ctx),
+ lp_gensec_settings(lp_ctx, lp_ctx)
+ );
+ } else {
+ status = smbcli_tree_full_connection(NULL,
+ &servers[i].smb_tree[j],
+ servers[i].server_name,
+ lp_smb_ports(lp_ctx),
+ servers[i].share_name, "A:",
+ lp_socket_options(lp_ctx),
+ servers[i].credentials,
+ lp_resolve_context(lp_ctx), ev,
+ &smb_options,
+ &smb_session_options,
+ lp_iconv_convenience(lp_ctx),
+ lp_gensec_settings(lp_ctx, lp_ctx));
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed to connect to \\\\%s\\%s - %s\n",
+ servers[i].server_name, servers[i].share_name,
+ nt_errstr(status));
+ return false;
+ }
+
+ if (options.smb2) {
+ servers[i].smb2_tree[j]->session->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 uint_t time_skew(void)
+{
+ uint_t ret;
+ if (options.smb2) {
+ ret = labs(servers[0].smb2_tree[0]->session->transport->negotiate.system_time -
+ servers[1].smb2_tree[0]->session->transport->negotiate.system_time);
+ } else {
+ ret = labs(servers[0].smb_tree[0]->session->transport->negotiate.server_time -
+ servers[1].smb_tree[0]->session->transport->negotiate.server_time);
+ }
+ 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 uint_t fnum_to_handle_smb2(int server, int instance, struct smb2_handle server_handle)
+{
+ uint_t i;
+ for (i=0;i<options.max_open_handles;i++) {
+ if (!open_handles[i].active ||
+ instance != open_handles[i].instance) continue;
+ if (smb2_handle_equal(&open_handles[i].smb2_handle[server], &server_handle)) {
+ return i;
+ }
+ }
+ printf("Invalid server handle in fnum_to_handle on server %d instance %d\n",
+ server, instance);
+ return BAD_HANDLE;
+}
+
+/*
+ turn a server handle into a local handle
+*/
+static uint_t fnum_to_handle_smb(int server, int instance, uint16_t server_handle)
+{
+ uint_t i;
+ for (i=0;i<options.max_open_handles;i++) {
+ if (!open_handles[i].active ||
+ instance != open_handles[i].instance) continue;
+ if (open_handles[i].smb_handle[server] == server_handle) {
+ return i;
+ }
+ }
+ printf("Invalid server handle in fnum_to_handle on server %d instance %d\n",
+ server, instance);
+ return BAD_HANDLE;
+}
+
+/*
+ add some newly opened handles
+*/
+static void gen_add_handle_smb2(int instance, const char *name, struct smb2_handle handles[NSERVERS])
+{
+ int i, h;
+ for (h=0;h<options.max_open_handles;h++) {
+ if (!open_handles[h].active) break;
+ }
+ if (h == options.max_open_handles) {
+ /* we have to force close a random handle */
+ h = random() % options.max_open_handles;
+ for (i=0;i<NSERVERS;i++) {
+ NTSTATUS status;
+ status = smb2_util_close(servers[i].smb2_tree[open_handles[h].instance],
+ open_handles[h].smb2_handle[i]);
+ if (NT_STATUS_IS_ERR(status)) {
+ printf("INTERNAL ERROR: Close failed when recovering handle! - %s\n",
+ nt_errstr(status));
+ }
+ }
+ printf("Recovered handle %d\n", h);
+ num_open_handles--;
+ }
+ for (i=0;i<NSERVERS;i++) {
+ open_handles[h].smb2_handle[i] = handles[i];
+ open_handles[h].instance = instance;
+ open_handles[h].active = true;
+ open_handles[h].name = name;
+ }
+ num_open_handles++;
+
+ printf("OPEN num_open_handles=%d h=%d (%s)\n",
+ num_open_handles, h, name);
+}
+
+/*
+ add some newly opened handles
+*/
+static void gen_add_handle_smb(int instance, const char *name, uint16_t handles[NSERVERS])
+{
+ int i, h;
+ for (h=0;h<options.max_open_handles;h++) {
+ if (!open_handles[h].active) break;
+ }
+ if (h == options.max_open_handles) {
+ /* we have to force close a random handle */
+ h = random() % options.max_open_handles;
+ for (i=0;i<NSERVERS;i++) {
+ NTSTATUS status;
+ status = smbcli_close(servers[i].smb_tree[open_handles[h].instance],
+ open_handles[h].smb_handle[i]);
+ if (NT_STATUS_IS_ERR(status)) {
+ printf("INTERNAL ERROR: Close failed when recovering handle! - %s\n",
+ nt_errstr(status));
+ }
+ }
+ printf("Recovered handle %d\n", h);
+ num_open_handles--;
+ }
+ for (i=0;i<NSERVERS;i++) {
+ open_handles[h].smb_handle[i] = handles[i];
+ open_handles[h].instance = instance;
+ open_handles[h].active = true;
+ open_handles[h].name = name;
+ }
+ num_open_handles++;
+
+ printf("OPEN num_open_handles=%d h=%d (%s)\n",
+ num_open_handles, h, name);
+}
+
+
+/*
+ remove a closed handle
+*/
+static void gen_remove_handle_smb2(int instance, struct smb2_handle handles[NSERVERS])
+{
+ int h;
+ for (h=0;h<options.max_open_handles;h++) {
+ if (instance == open_handles[h].instance &&
+ smb2_handle_equal(&open_handles[h].smb2_handle[0], &handles[0])) {
+ open_handles[h].active = false;
+ num_open_handles--;
+ printf("CLOSE num_open_handles=%d h=%d (%s)\n",
+ num_open_handles, h,
+ open_handles[h].name);
+ return;
+ }
+ }
+ printf("Removing invalid handle!?\n");
+ exit(1);
+}
+
+/*
+ remove a closed handle
+*/
+static void gen_remove_handle_smb(int instance, uint16_t handles[NSERVERS])
+{
+ int h;
+ for (h=0;h<options.max_open_handles;h++) {
+ if (instance == open_handles[h].instance &&
+ open_handles[h].smb_handle[0] == handles[0]) {
+ open_handles[h].active = false;
+ num_open_handles--;
+ printf("CLOSE num_open_handles=%d h=%d (%s)\n",
+ num_open_handles, h,
+ open_handles[h].name);
+ return;
+ }
+ }
+ printf("Removing invalid handle!?\n");
+ exit(1);
+}
+
+/*
+ return true with 'chance' probability as a percentage
+*/
+static bool gen_chance(uint_t chance)
+{
+ return ((random() % 100) <= chance);
+}
+
+/*
+ map an internal handle number to a server handle
+*/
+static struct smb2_handle gen_lookup_handle_smb2(int server, uint16_t handle)
+{
+ if (handle == BAD_HANDLE) return bad_smb2_handle;
+ return open_handles[handle].smb2_handle[server];
+}
+
+/*
+ map an internal handle number to a server handle
+*/
+static uint16_t gen_lookup_handle_smb(int server, uint16_t handle)
+{
+ if (handle == BAD_HANDLE) return BAD_HANDLE;
+ return open_handles[handle].smb_handle[server];
+}
+
+/*
+ return a file handle
+*/
+static uint16_t gen_fnum(int instance)
+{
+ uint16_t h;
+ int count = 0;
+
+ if (gen_chance(20)) return BAD_HANDLE;
+
+ while (num_open_handles > 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)
+{
+ uint_t 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;i<nlevels;i++) {
+ pct = va_arg(ap, uint32_t);
+ mask = va_arg(ap, uint32_t);
+ if (pct == 100 || gen_chance(pct)) {
+ va_end(ap);
+ return mask & random();
+ }
+ }
+ va_end(ap);
+ return 0;
+}
+
+/*
+ generate a bitmask
+*/
+static uint32_t gen_bits_mask(uint_t mask)
+{
+ uint_t ret = random();
+ return ret & mask;
+}
+
+/*
+ generate a bitmask with high probability of the first mask
+ and low of the second
+*/
+static uint32_t gen_bits_mask2(uint32_t mask1, uint32_t mask2)
+{
+ if (!options.valid && gen_chance(10)) return gen_bits_mask(mask2);
+ return gen_bits_mask(mask1);
+}
+
+/*
+ generate reserved values
+ */
+static uint64_t gen_reserved8(void)
+{
+ if (options.valid) return 0;
+ return gen_bits_mask(0xFF);
+}
+
+static uint64_t gen_reserved16(void)
+{
+ if (options.valid) return 0;
+ return gen_bits_mask(0xFFFF);
+}
+
+static uint64_t gen_reserved32(void)
+{
+ if (options.valid) return 0;
+ return gen_bits_mask(0xFFFFFFFF);
+}
+
+static uint64_t gen_reserved64(void)
+{
+ if (options.valid) return 0;
+ return gen_bits_mask(0xFFFFFFFF) | (((uint64_t)gen_bits_mask(0xFFFFFFFF))<<32);
+}
+
+
+
+/*
+ generate a boolean
+*/
+static bool gen_bool(void)
+{
+ return gen_bits_mask2(0x1, 0xFF);
+}
+
+/*
+ generate ntrename flags
+*/
+static uint16_t gen_rename_flags(void)
+{
+ if (gen_chance(30)) return RENAME_FLAG_RENAME;
+ if (gen_chance(30)) return RENAME_FLAG_HARD_LINK;
+ if (gen_chance(30)) return RENAME_FLAG_COPY;
+ return gen_bits_mask(0xFFFF);
+}
+
+/*
+ generate a pid
+*/
+static uint16_t gen_pid(void)
+{
+ if (gen_chance(10)) return gen_bits_mask(0xFFFF);
+ return getpid();
+}
+
+/*
+ return a set of lock flags
+*/
+static uint16_t gen_lock_flags_smb2(void)
+{
+ if (!options.valid && gen_chance(5)) return gen_bits_mask(0xFFFF);
+ if (gen_chance(20)) return gen_bits_mask(0x1F);
+ if (gen_chance(50)) return SMB2_LOCK_FLAG_UNLOCK;
+ return gen_bits_mask(SMB2_LOCK_FLAG_SHARED |
+ SMB2_LOCK_FLAG_EXCLUSIVE |
+ SMB2_LOCK_FLAG_FAIL_IMMEDIATELY);
+}
+
+/*
+ generate a lock count
+*/
+static off_t gen_lock_count(void)
+{
+ return gen_int_range(0, 3);
+}
+
+/*
+ generate a NT access mask
+*/
+static uint32_t gen_access_mask(void)
+{
+ uint32_t ret;
+ if (gen_chance(70)) return SEC_FLAG_MAXIMUM_ALLOWED;
+ if (gen_chance(70)) return SEC_FILE_ALL;
+ ret = gen_bits_mask(0xFFFFFFFF);
+ if (options.valid) ret &= ~SEC_MASK_INVALID;
+ return ret;
+}
+
+/*
+ return a lockingx lock mode
+*/
+static uint16_t gen_lock_mode(void)
+{
+ if (!options.valid && gen_chance(5)) return gen_bits_mask(0xFFFF);
+ if (gen_chance(20)) return gen_bits_mask(0x1F);
+ return gen_bits_mask(LOCKING_ANDX_SHARED_LOCK | LOCKING_ANDX_LARGE_FILES);
+}
+
+/*
+ generate a ntcreatex flags field
+*/
+static uint32_t gen_ntcreatex_flags(void)
+{
+ if (gen_chance(70)) return NTCREATEX_FLAGS_EXTENDED;
+ return gen_bits_mask2(0x1F, 0xFFFFFFFF);
+}
+
+/*
+ generate a ntcreatex create options bitfield
+*/
+static uint32_t gen_create_options(void)
+{
+ if (!options.valid && gen_chance(20)) return gen_bits_mask(0xFFFFFFFF);
+ if (gen_chance(50)) return 0;
+ return gen_bits_mask(NTCREATEX_OPTIONS_DELETE_ON_CLOSE | NTCREATEX_OPTIONS_DIRECTORY);
+}
+
+/*
+ generate a ntcreatex open disposition
+*/
+static uint32_t gen_open_disp(void)
+{
+ if (gen_chance(50)) return NTCREATEX_DISP_OPEN_IF;
+ if (!options.valid && gen_chance(10)) return gen_bits_mask(0xFFFFFFFF);
+ return gen_int_range(0, 5);
+}
+
+/*
+ generate an openx open mode
+*/
+static uint16_t gen_openx_mode(void)
+{
+ if (!options.valid && gen_chance(20)) return gen_bits_mask(0xFFFF);
+ if (gen_chance(20)) return gen_bits_mask(0xFF);
+ return OPENX_MODE_DENY_NONE | gen_bits_mask(0x3);
+}
+
+/*
+ generate an openx flags field
+*/
+static uint16_t gen_openx_flags(void)
+{
+ if (!options.valid && gen_chance(20)) return gen_bits_mask(0xFFFF);
+ return gen_bits_mask(0x7);
+}
+
+/*
+ generate an openx open function
+*/
+static uint16_t gen_openx_func(void)
+{
+ if (!options.valid && gen_chance(20)) return gen_bits_mask(0xFFFF);
+ return gen_bits_mask(0x13);
+}
+
+/*
+ generate a file attrib combination
+*/
+static uint32_t gen_attrib(void)
+{
+ uint32_t ret;
+ if (gen_chance(20)) {
+ ret = gen_bits_mask(0xFFFFFFFF);
+ if (options.valid) ret &= FILE_ATTRIBUTE_ALL_MASK;
+ return ret;
+ }
+ return gen_bits_mask(FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_DIRECTORY);
+}
+
+/*
+ generate a unix timestamp
+*/
+static time_t gen_timet(void)
+{
+ if (gen_chance(30)) return 0;
+ return (time_t)random();
+}
+
+/*
+ generate a milliseconds protocol timeout
+*/
+static uint32_t gen_timeout(void)
+{
+ if (gen_chance(98)) return 0;
+ return random() % 50;
+}
+
+/*
+ generate a timestamp
+*/
+static NTTIME gen_nttime(void)
+{
+ NTTIME ret;
+ unix_to_nt_time(&ret, gen_timet());
+ return ret;
+}
+
+/*
+ generate a timewarp value
+*/
+static NTTIME gen_timewarp(void)
+{
+ NTTIME ret = gen_nttime();
+ if (gen_chance(98)) ret = 0;
+ return ret;
+}
+
+/*
+ generate a file allocation size
+*/
+static uint_t gen_alloc_size(void)
+{
+ uint_t ret;
+
+ if (gen_chance(30)) return 0;
+
+ ret = random() % 4*1024*1024;
+ /* give a high chance of a round number */
+ if (gen_chance(60)) {
+ ret &= ~(1024*1024 - 1);
+ }
+ return ret;
+}
+
+/*
+ generate an ea_struct
+*/
+static struct ea_struct gen_ea_struct(void)
+{
+ struct ea_struct ea;
+ const char *names[] = {"EAONE",
+ "",
+ "FOO!",
+ " WITH SPACES ",
+ ".",
+ "AVERYLONGATTRIBUTENAME"};
+ const char *values[] = {"VALUE1",
+ "",
+ "NOT MUCH FOO",
+ " LEADING SPACES ",
+ ":",
+ "ASOMEWHATLONGERATTRIBUTEVALUE"};
+ int i;
+
+ ZERO_STRUCT(ea);
+
+ do {
+ i = gen_int_range(0, ARRAY_SIZE(names)-1);
+ } while (ignore_pattern(names[i]));
+
+ ea.name.s = names[i];
+
+ do {
+ i = gen_int_range(0, ARRAY_SIZE(values)-1);
+ } while (ignore_pattern(values[i]));
+
+ ea.value = data_blob(values[i], strlen(values[i]));
+
+ if (gen_chance(10)) ea.flags = gen_bits_mask(0xFF);
+ ea.flags = 0;
+
+ return ea;
+}
+
+/*
+ generate an ea_struct
+*/
+static struct smb_ea_list gen_ea_list(void)
+{
+ struct smb_ea_list eas;
+ int i;
+ if (options.no_eas) {
+ ZERO_STRUCT(eas);
+ return eas;
+ }
+ eas.num_eas = gen_int_range(0, 3);
+ eas.eas = talloc_array(current_op.mem_ctx, struct ea_struct, eas.num_eas);
+ for (i=0;i<eas.num_eas;i++) {
+ eas.eas[i] = gen_ea_struct();
+ }
+ return eas;
+}
+
+/* generate a security descriptor */
+static struct security_descriptor *gen_sec_desc(void)
+{
+ struct security_descriptor *sd;
+ if (options.no_acls || gen_chance(90)) return NULL;
+
+ sd = security_descriptor_dacl_create(current_op.mem_ctx,
+ 0, NULL, NULL,
+ NULL,
+ 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);
+ return sd;
+}
+
+
+static void oplock_handler_close_recv_smb(struct smbcli_request *req)
+{
+ NTSTATUS status;
+ status = smbcli_request_simple_recv(req);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("close failed in oplock_handler\n");
+ smb_panic("close failed in oplock_handler");
+ }
+}
+
+/*
+ the oplock handler will either ack the break or close the file
+*/
+static bool oplock_handler_smb(struct smbcli_transport *transport, uint16_t tid, uint16_t fnum, uint8_t level, void *private_data)
+{
+ union smb_close io;
+ int i, j;
+ bool do_close;
+ struct smbcli_tree *tree = NULL;
+ struct smbcli_request *req;
+
+ srandom(current_op.seed);
+ do_close = gen_chance(50);
+
+ for (i=0;i<NSERVERS;i++) {
+ for (j=0;j<NINSTANCES;j++) {
+ if (transport == servers[i].smb_tree[j]->session->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;i<NSERVERS;i++) {
+ for (j=0;j<NINSTANCES;j++) {
+ if (servers[i].smb_tree[j] &&
+ transport != servers[i].smb_tree[j]->session->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;i<NSERVERS;i++) {
+ for (j=0;j<NINSTANCES;j++) {
+ if (servers[i].smb2_tree[j] &&
+ transport != servers[i].smb2_tree[j]->session->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;
+
+ msleep(20);
+
+ for (j=0;j<NINSTANCES;j++) {
+ for (i=0;i<NSERVERS;i++) {
+ // smb2_transport_process(servers[i].smb2_tree[j]->session->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;j<NINSTANCES;j++) {
+ for (i=1;i<NSERVERS;i++) {
+ if (oplocks[0][j].got_break != oplocks[i][j].got_break ||
+ oplocks[0][j].handle != oplocks[i][j].handle ||
+ oplocks[0][j].level != oplocks[i][j].level) {
+ if (tries++ < 10) goto again;
+ printf("oplock break inconsistent - %d/%d/%d vs %d/%d/%d\n",
+ oplocks[0][j].got_break,
+ oplocks[0][j].handle,
+ oplocks[0][j].level,
+ oplocks[i][j].got_break,
+ oplocks[i][j].handle,
+ oplocks[i][j].level);
+ current_op.mismatch = "oplock break";
+ return false;
+ }
+ }
+ }
+
+ /* if we got a break and closed then remove the handle */
+ for (j=0;j<NINSTANCES;j++) {
+ if (oplocks[0][j].got_break &&
+ oplocks[0][j].do_close) {
+ uint16_t fnums[NSERVERS];
+ for (i=0;i<NSERVERS;i++) {
+ fnums[i] = oplocks[i][j].smb_handle;
+ }
+ gen_remove_handle_smb(j, fnums);
+ break;
+ }
+ }
+ return true;
+}
+
+
+/*
+ check that the same change notify info has been received by all instances
+*/
+static bool check_notifies(const char *call)
+{
+ int i, j;
+ int tries = 0;
+
+ if (options.smb2) {
+ /* no smb2 notifies in gentest yet */
+ return true;
+ }
+
+again:
+ check_pending();
+
+ for (j=0;j<NINSTANCES;j++) {
+ for (i=1;i<NSERVERS;i++) {
+ int n;
+ union smb_notify not1, not2;
+
+ if (notifies[0][j].notify_count != notifies[i][j].notify_count) {
+ if (tries++ < 10) goto again;
+ printf("Notify count inconsistent %d %d\n",
+ notifies[0][j].notify_count,
+ notifies[i][j].notify_count);
+ current_op.mismatch = "notify count";
+ return false;
+ }
+
+ if (notifies[0][j].notify_count == 0) continue;
+
+ if (!NT_STATUS_EQUAL(notifies[0][j].status,
+ notifies[i][j].status)) {
+ printf("Notify status mismatch - %s - %s\n",
+ nt_errstr(notifies[0][j].status),
+ nt_errstr(notifies[i][j].status));
+ current_op.mismatch = "Notify status";
+ return false;
+ }
+
+ if (!NT_STATUS_IS_OK(notifies[0][j].status)) {
+ continue;
+ }
+
+ not1 = notifies[0][j].notify;
+ not2 = notifies[i][j].notify;
+
+ for (n=0;n<not1.nttrans.out.num_changes;n++) {
+ if (not1.nttrans.out.changes[n].action !=
+ not2.nttrans.out.changes[n].action) {
+ printf("Notify action %d inconsistent %d %d\n", n,
+ not1.nttrans.out.changes[n].action,
+ not2.nttrans.out.changes[n].action);
+ current_op.mismatch = "notify action";
+ return false;
+ }
+ if (strcmp(not1.nttrans.out.changes[n].name.s,
+ not2.nttrans.out.changes[n].name.s)) {
+ printf("Notify name %d inconsistent %s %s\n", n,
+ not1.nttrans.out.changes[n].name.s,
+ not2.nttrans.out.changes[n].name.s);
+ current_op.mismatch = "notify name";
+ return false;
+ }
+ if (not1.nttrans.out.changes[n].name.private_length !=
+ not2.nttrans.out.changes[n].name.private_length) {
+ printf("Notify name length %d inconsistent %d %d\n", n,
+ not1.nttrans.out.changes[n].name.private_length,
+ not2.nttrans.out.changes[n].name.private_length);
+ current_op.mismatch = "notify name length";
+ return false;
+ }
+ }
+ }
+ }
+
+ ZERO_STRUCT(notifies);
+
+ return true;
+}
+
+#define GEN_COPY_PARM do { \
+ int i; \
+ for (i=1;i<NSERVERS;i++) { \
+ parm[i] = parm[0]; \
+ } \
+} while (0)
+
+#define GEN_CALL(call, treetype, treefield) do { \
+ int i; \
+ ZERO_STRUCT(oplocks); \
+ ZERO_STRUCT(notifies); \
+ for (i=0;i<NSERVERS;i++) { \
+ struct treetype *tree = servers[i].treefield[instance]; \
+ status[i] = call; \
+ } \
+ current_op.status = status[0]; \
+ for (i=1;i<NSERVERS;i++) { \
+ if (!compare_status(status[0], status[1])) { \
+ printf("status different in %s - %s %s\n", #call, \
+ nt_errstr(status[0]), nt_errstr(status[i])); \
+ current_op.mismatch = nt_errstr(status[0]); \
+ return false; \
+ } \
+ } \
+ if (!check_oplocks(#call)) return false; \
+ if (!check_notifies(#call)) return false; \
+ if (!NT_STATUS_IS_OK(status[0])) { \
+ return true; \
+ } \
+} while(0)
+
+#define GEN_CALL_SMB(call) GEN_CALL(call, smbcli_tree, smb_tree)
+#define GEN_CALL_SMB2(call) GEN_CALL(call, smb2_tree, smb2_tree)
+
+#define ADD_HANDLE_SMB2(name, field) do { \
+ struct smb2_handle handles[NSERVERS]; \
+ int i; \
+ for (i=0;i<NSERVERS;i++) { \
+ handles[i] = parm[i].field; \
+ } \
+ gen_add_handle_smb2(instance, name, handles); \
+} while(0)
+
+#define REMOVE_HANDLE_SMB2(field) do { \
+ struct smb2_handle handles[NSERVERS]; \
+ int i; \
+ for (i=0;i<NSERVERS;i++) { \
+ handles[i] = parm[i].field; \
+ } \
+ gen_remove_handle_smb2(instance, handles); \
+} while(0)
+
+#define ADD_HANDLE_SMB(name, field) do { \
+ uint16_t handles[NSERVERS]; \
+ int i; \
+ for (i=0;i<NSERVERS;i++) { \
+ handles[i] = parm[i].field; \
+ } \
+ gen_add_handle_smb(instance, name, handles); \
+} while(0)
+
+#define REMOVE_HANDLE_SMB(field) do { \
+ uint16_t handles[NSERVERS]; \
+ int i; \
+ for (i=0;i<NSERVERS;i++) { \
+ handles[i] = parm[i].field; \
+ } \
+ gen_remove_handle_smb(instance, handles); \
+} while(0)
+
+#define GEN_SET_FNUM_SMB2(field) do { \
+ int i; \
+ for (i=0;i<NSERVERS;i++) { \
+ parm[i].field = gen_lookup_handle_smb2(i, parm[i].field.data[0]); \
+ } \
+} while(0)
+
+#define GEN_SET_FNUM_SMB(field) do { \
+ int i; \
+ for (i=0;i<NSERVERS;i++) { \
+ parm[i].field = gen_lookup_handle_smb(i, parm[i].field); \
+ } \
+} while(0)
+
+#define CHECK_EQUAL(field) do { \
+ if (parm[0].field != parm[1].field && !ignore_pattern(#field)) { \
+ current_op.mismatch = #field; \
+ printf("Mismatch in %s - 0x%llx 0x%llx\n", #field, \
+ (unsigned long long)parm[0].field, (unsigned long long)parm[1].field); \
+ return false; \
+ } \
+} while(0)
+
+#define CHECK_SECDESC(field) do { \
+ if (!security_acl_equal(parm[0].field->dacl, 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<parm[0].all_eas.out.num_eas;i++) {
+ CHECK_EQUAL(all_eas.out.eas[i].flags);
+ CHECK_WSTR_EQUAL(all_eas.out.eas[i].name);
+ CHECK_BLOB_EQUAL(all_eas.out.eas[i].value);
+ }
+ break;
+
+ case RAW_FILEINFO_IS_NAME_VALID:
+ break;
+
+ case RAW_FILEINFO_BASIC_INFO:
+ case RAW_FILEINFO_BASIC_INFORMATION:
+ CHECK_NTTIMES_EQUAL(basic_info.out.create_time);
+ CHECK_NTTIMES_EQUAL(basic_info.out.access_time);
+ CHECK_NTTIMES_EQUAL(basic_info.out.write_time);
+ CHECK_NTTIMES_EQUAL(basic_info.out.change_time);
+ CHECK_ATTRIB(basic_info.out.attrib);
+ break;
+
+ case RAW_FILEINFO_STANDARD_INFO:
+ case RAW_FILEINFO_STANDARD_INFORMATION:
+ CHECK_EQUAL(standard_info.out.alloc_size);
+ CHECK_EQUAL(standard_info.out.size);
+ CHECK_EQUAL(standard_info.out.nlink);
+ CHECK_EQUAL(standard_info.out.delete_pending);
+ CHECK_EQUAL(standard_info.out.directory);
+ break;
+
+ case RAW_FILEINFO_EA_INFO:
+ case RAW_FILEINFO_EA_INFORMATION:
+ CHECK_EQUAL(ea_info.out.ea_size);
+ break;
+
+ case RAW_FILEINFO_NAME_INFO:
+ case RAW_FILEINFO_NAME_INFORMATION:
+ CHECK_WSTR_EQUAL(name_info.out.fname);
+ break;
+
+ case RAW_FILEINFO_ALL_INFO:
+ case RAW_FILEINFO_ALL_INFORMATION:
+ CHECK_NTTIMES_EQUAL(all_info.out.create_time);
+ CHECK_NTTIMES_EQUAL(all_info.out.access_time);
+ CHECK_NTTIMES_EQUAL(all_info.out.write_time);
+ CHECK_NTTIMES_EQUAL(all_info.out.change_time);
+ CHECK_ATTRIB(all_info.out.attrib);
+ CHECK_EQUAL(all_info.out.alloc_size);
+ CHECK_EQUAL(all_info.out.size);
+ CHECK_EQUAL(all_info.out.nlink);
+ CHECK_EQUAL(all_info.out.delete_pending);
+ CHECK_EQUAL(all_info.out.directory);
+ CHECK_EQUAL(all_info.out.ea_size);
+ CHECK_WSTR_EQUAL(all_info.out.fname);
+ break;
+
+ case RAW_FILEINFO_ALT_NAME_INFO:
+ case RAW_FILEINFO_ALT_NAME_INFORMATION:
+ CHECK_WSTR_EQUAL(alt_name_info.out.fname);
+ break;
+
+ case RAW_FILEINFO_STREAM_INFO:
+ case RAW_FILEINFO_STREAM_INFORMATION:
+ CHECK_EQUAL(stream_info.out.num_streams);
+ for (i=0;i<parm[0].stream_info.out.num_streams;i++) {
+ CHECK_EQUAL(stream_info.out.streams[i].size);
+ CHECK_EQUAL(stream_info.out.streams[i].alloc_size);
+ CHECK_WSTR_EQUAL(stream_info.out.streams[i].stream_name);
+ }
+ break;
+
+ case RAW_FILEINFO_COMPRESSION_INFO:
+ case RAW_FILEINFO_COMPRESSION_INFORMATION:
+ CHECK_EQUAL(compression_info.out.compressed_size);
+ CHECK_EQUAL(compression_info.out.format);
+ CHECK_EQUAL(compression_info.out.unit_shift);
+ CHECK_EQUAL(compression_info.out.chunk_shift);
+ CHECK_EQUAL(compression_info.out.cluster_shift);
+ break;
+
+ case RAW_FILEINFO_INTERNAL_INFORMATION:
+ CHECK_EQUAL(internal_information.out.file_id);
+ break;
+
+ case RAW_FILEINFO_ACCESS_INFORMATION:
+ CHECK_EQUAL(access_information.out.access_flags);
+ break;
+
+ case RAW_FILEINFO_POSITION_INFORMATION:
+ CHECK_EQUAL(position_information.out.position);
+ break;
+
+ case RAW_FILEINFO_MODE_INFORMATION:
+ CHECK_EQUAL(mode_information.out.mode);
+ break;
+
+ case RAW_FILEINFO_ALIGNMENT_INFORMATION:
+ CHECK_EQUAL(alignment_information.out.alignment_requirement);
+ break;
+
+ case RAW_FILEINFO_NETWORK_OPEN_INFORMATION:
+ CHECK_NTTIMES_EQUAL(network_open_information.out.create_time);
+ CHECK_NTTIMES_EQUAL(network_open_information.out.access_time);
+ CHECK_NTTIMES_EQUAL(network_open_information.out.write_time);
+ CHECK_NTTIMES_EQUAL(network_open_information.out.change_time);
+ CHECK_EQUAL(network_open_information.out.alloc_size);
+ CHECK_EQUAL(network_open_information.out.size);
+ CHECK_ATTRIB(network_open_information.out.attrib);
+ break;
+
+ case RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION:
+ CHECK_ATTRIB(attribute_tag_information.out.attrib);
+ CHECK_EQUAL(attribute_tag_information.out.reparse_tag);
+ break;
+
+ case RAW_FILEINFO_SMB2_ALL_INFORMATION:
+ CHECK_NTTIMES_EQUAL(all_info2.out.create_time);
+ CHECK_NTTIMES_EQUAL(all_info2.out.access_time);
+ CHECK_NTTIMES_EQUAL(all_info2.out.write_time);
+ CHECK_NTTIMES_EQUAL(all_info2.out.change_time);
+ CHECK_ATTRIB(all_info2.out.attrib);
+ CHECK_EQUAL(all_info2.out.unknown1);
+ CHECK_EQUAL(all_info2.out.alloc_size);
+ CHECK_EQUAL(all_info2.out.size);
+ CHECK_EQUAL(all_info2.out.nlink);
+ CHECK_EQUAL(all_info2.out.delete_pending);
+ CHECK_EQUAL(all_info2.out.directory);
+ CHECK_EQUAL(all_info2.out.file_id);
+ CHECK_EQUAL(all_info2.out.ea_size);
+ CHECK_EQUAL(all_info2.out.access_mask);
+ CHECK_EQUAL(all_info2.out.position);
+ CHECK_EQUAL(all_info2.out.mode);
+ CHECK_EQUAL(all_info2.out.alignment_requirement);
+ CHECK_WSTR_EQUAL(all_info2.out.fname);
+ break;
+
+ case RAW_FILEINFO_SMB2_ALL_EAS:
+ CHECK_EQUAL(all_eas.out.num_eas);
+ for (i=0;i<parm[0].all_eas.out.num_eas;i++) {
+ CHECK_EQUAL(all_eas.out.eas[i].flags);
+ CHECK_WSTR_EQUAL(all_eas.out.eas[i].name);
+ CHECK_BLOB_EQUAL(all_eas.out.eas[i].value);
+ }
+ break;
+
+ case RAW_FILEINFO_SEC_DESC:
+ CHECK_SECDESC(query_secdesc.out.sd);
+ break;
+
+ /* Unhandled levels */
+ case RAW_FILEINFO_EA_LIST:
+ case RAW_FILEINFO_UNIX_BASIC:
+ case RAW_FILEINFO_UNIX_LINK:
+ case RAW_FILEINFO_UNIX_INFO2:
+ break;
+ }
+
+ return true;
+}
+
+
+
+/*
+ generate openx operations
+*/
+static bool handler_smb_openx(int instance)
+{
+ union smb_open parm[NSERVERS];
+ NTSTATUS status[NSERVERS];
+
+ parm[0].openx.level = RAW_OPEN_OPENX;
+ parm[0].openx.in.flags = gen_openx_flags();
+ parm[0].openx.in.open_mode = gen_openx_mode();
+ parm[0].openx.in.search_attrs = gen_attrib();
+ parm[0].openx.in.file_attrs = gen_attrib();
+ parm[0].openx.in.write_time = gen_timet();
+ parm[0].openx.in.open_func = gen_openx_func();
+ parm[0].openx.in.size = gen_io_count();
+ parm[0].openx.in.timeout = gen_timeout();
+ parm[0].openx.in.fname = gen_fname_open(instance);
+
+ if (!options.use_oplocks) {
+ /* mask out oplocks */
+ parm[0].openx.in.flags &= ~(OPENX_FLAGS_REQUEST_OPLOCK|
+ OPENX_FLAGS_REQUEST_BATCH_OPLOCK);
+ }
+
+ GEN_COPY_PARM;
+ GEN_CALL_SMB(smb_raw_open(tree, current_op.mem_ctx, &parm[i]));
+
+ CHECK_ATTRIB(openx.out.attrib);
+ CHECK_EQUAL(openx.out.size);
+ CHECK_EQUAL(openx.out.access);
+ CHECK_EQUAL(openx.out.ftype);
+ CHECK_EQUAL(openx.out.devstate);
+ CHECK_EQUAL(openx.out.action);
+ CHECK_EQUAL(openx.out.access_mask);
+ CHECK_EQUAL(openx.out.unknown);
+ CHECK_TIMES_EQUAL(openx.out.write_time);
+
+ /* open creates a new file handle */
+ ADD_HANDLE_SMB(parm[0].openx.in.fname, openx.out.file.fnum);
+
+ return true;
+}
+
+
+/*
+ generate open operations
+*/
+static bool handler_smb_open(int instance)
+{
+ union smb_open parm[NSERVERS];
+ NTSTATUS status[NSERVERS];
+
+ parm[0].openold.level = RAW_OPEN_OPEN;
+ parm[0].openold.in.open_mode = gen_bits_mask2(0xF, 0xFFFF);
+ parm[0].openold.in.search_attrs = gen_attrib();
+ parm[0].openold.in.fname = gen_fname_open(instance);
+
+ if (!options.use_oplocks) {
+ /* mask out oplocks */
+ parm[0].openold.in.open_mode &= ~(OPENX_FLAGS_REQUEST_OPLOCK|
+ OPENX_FLAGS_REQUEST_BATCH_OPLOCK);
+ }
+
+ GEN_COPY_PARM;
+ GEN_CALL_SMB(smb_raw_open(tree, current_op.mem_ctx, &parm[i]));
+
+ CHECK_ATTRIB(openold.out.attrib);
+ CHECK_TIMES_EQUAL(openold.out.write_time);
+ CHECK_EQUAL(openold.out.size);
+ CHECK_EQUAL(openold.out.rmode);
+
+ /* open creates a new file handle */
+ ADD_HANDLE_SMB(parm[0].openold.in.fname, openold.out.file.fnum);
+
+ return true;
+}
+
+
+/*
+ generate ntcreatex operations
+*/
+static bool handler_smb_ntcreatex(int instance)
+{
+ union smb_open parm[NSERVERS];
+ NTSTATUS status[NSERVERS];
+
+ parm[0].ntcreatex.level = RAW_OPEN_NTCREATEX;
+ parm[0].ntcreatex.in.flags = gen_ntcreatex_flags();
+ parm[0].ntcreatex.in.root_fid = gen_root_fid(instance);
+ parm[0].ntcreatex.in.access_mask = gen_access_mask();
+ parm[0].ntcreatex.in.alloc_size = gen_alloc_size();
+ parm[0].ntcreatex.in.file_attr = gen_attrib();
+ parm[0].ntcreatex.in.share_access = gen_bits_mask2(0x7, 0xFFFFFFFF);
+ parm[0].ntcreatex.in.open_disposition = gen_open_disp();
+ parm[0].ntcreatex.in.create_options = gen_create_options();
+ parm[0].ntcreatex.in.impersonation = gen_bits_mask2(0, 0xFFFFFFFF);
+ parm[0].ntcreatex.in.security_flags = gen_bits_mask2(0, 0xFF);
+ parm[0].ntcreatex.in.fname = gen_fname_open(instance);
+
+ if (!options.use_oplocks) {
+ /* mask out oplocks */
+ parm[0].ntcreatex.in.flags &= ~(NTCREATEX_FLAGS_REQUEST_OPLOCK|
+ NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK);
+ }
+
+ GEN_COPY_PARM;
+ if (parm[0].ntcreatex.in.root_fid != 0) {
+ GEN_SET_FNUM_SMB(ntcreatex.in.root_fid);
+ }
+ GEN_CALL_SMB(smb_raw_open(tree, current_op.mem_ctx, &parm[i]));
+
+ CHECK_EQUAL(ntcreatex.out.oplock_level);
+ CHECK_EQUAL(ntcreatex.out.create_action);
+ CHECK_NTTIMES_EQUAL(ntcreatex.out.create_time);
+ CHECK_NTTIMES_EQUAL(ntcreatex.out.access_time);
+ CHECK_NTTIMES_EQUAL(ntcreatex.out.write_time);
+ CHECK_NTTIMES_EQUAL(ntcreatex.out.change_time);
+ CHECK_ATTRIB(ntcreatex.out.attrib);
+ CHECK_EQUAL(ntcreatex.out.alloc_size);
+ CHECK_EQUAL(ntcreatex.out.size);
+ CHECK_EQUAL(ntcreatex.out.file_type);
+ CHECK_EQUAL(ntcreatex.out.ipc_state);
+ CHECK_EQUAL(ntcreatex.out.is_directory);
+
+ /* ntcreatex creates a new file handle */
+ ADD_HANDLE_SMB(parm[0].ntcreatex.in.fname, ntcreatex.out.file.fnum);
+
+ return true;
+}
+
+/*
+ generate close operations
+*/
+static bool handler_smb_close(int instance)
+{
+ union smb_close parm[NSERVERS];
+ NTSTATUS status[NSERVERS];
+
+ parm[0].close.level = RAW_CLOSE_CLOSE;
+ parm[0].close.in.file.fnum = gen_fnum_close(instance);
+ parm[0].close.in.write_time = gen_timet();
+
+ GEN_COPY_PARM;
+ GEN_SET_FNUM_SMB(close.in.file.fnum);
+ GEN_CALL_SMB(smb_raw_close(tree, &parm[i]));
+
+ REMOVE_HANDLE_SMB(close.in.file.fnum);
+
+ return true;
+}
+
+/*
+ generate unlink operations
+*/
+static bool handler_smb_unlink(int instance)
+{
+ union smb_unlink parm[NSERVERS];
+ NTSTATUS status[NSERVERS];
+
+ parm[0].unlink.in.pattern = gen_pattern();
+ parm[0].unlink.in.attrib = gen_attrib();
+
+ GEN_COPY_PARM;
+ GEN_CALL_SMB(smb_raw_unlink(tree, &parm[i]));
+
+ return true;
+}
+
+/*
+ generate chkpath operations
+*/
+static bool handler_smb_chkpath(int instance)
+{
+ union smb_chkpath parm[NSERVERS];
+ NTSTATUS status[NSERVERS];
+
+ parm[0].chkpath.in.path = gen_fname_open(instance);
+
+ GEN_COPY_PARM;
+ GEN_CALL_SMB(smb_raw_chkpath(tree, &parm[i]));
+
+ return true;
+}
+
+/*
+ generate mkdir operations
+*/
+static bool handler_smb_mkdir(int instance)
+{
+ union smb_mkdir parm[NSERVERS];
+ NTSTATUS status[NSERVERS];
+
+ parm[0].mkdir.level = RAW_MKDIR_MKDIR;
+ parm[0].mkdir.in.path = gen_fname_open(instance);
+
+ GEN_COPY_PARM;
+ GEN_CALL_SMB(smb_raw_mkdir(tree, &parm[i]));
+
+ return true;
+}
+
+/*
+ generate rmdir operations
+*/
+static bool handler_smb_rmdir(int instance)
+{
+ struct smb_rmdir parm[NSERVERS];
+ NTSTATUS status[NSERVERS];
+
+ parm[0].in.path = gen_fname_open(instance);
+
+ GEN_COPY_PARM;
+ GEN_CALL_SMB(smb_raw_rmdir(tree, &parm[i]));
+
+ return true;
+}
+
+/*
+ generate rename operations
+*/
+static bool handler_smb_rename(int instance)
+{
+ union smb_rename parm[NSERVERS];
+ NTSTATUS status[NSERVERS];
+
+ parm[0].generic.level = RAW_RENAME_RENAME;
+ parm[0].rename.in.pattern1 = gen_pattern();
+ parm[0].rename.in.pattern2 = gen_pattern();
+ parm[0].rename.in.attrib = gen_attrib();
+
+ GEN_COPY_PARM;
+ GEN_CALL_SMB(smb_raw_rename(tree, &parm[i]));
+
+ return true;
+}
+
+/*
+ generate ntrename operations
+*/
+static bool handler_smb_ntrename(int instance)
+{
+ union smb_rename parm[NSERVERS];
+ NTSTATUS status[NSERVERS];
+
+ parm[0].generic.level = RAW_RENAME_NTRENAME;
+ parm[0].ntrename.in.old_name = gen_fname();
+ parm[0].ntrename.in.new_name = gen_fname();
+ parm[0].ntrename.in.attrib = gen_attrib();
+ parm[0].ntrename.in.cluster_size = gen_bits_mask2(0, 0xFFFFFFF);
+ parm[0].ntrename.in.flags = gen_rename_flags();
+
+ GEN_COPY_PARM;
+ GEN_CALL_SMB(smb_raw_rename(tree, &parm[i]));
+
+ return true;
+}
+
+
+/*
+ generate seek operations
+*/
+static bool handler_smb_seek(int instance)
+{
+ union smb_seek parm[NSERVERS];
+ NTSTATUS status[NSERVERS];
+
+ parm[0].lseek.in.file.fnum = gen_fnum(instance);
+ parm[0].lseek.in.mode = gen_bits_mask2(0x3, 0xFFFF);
+ parm[0].lseek.in.offset = gen_offset();
+
+ GEN_COPY_PARM;
+ GEN_SET_FNUM_SMB(lseek.in.file.fnum);
+ GEN_CALL_SMB(smb_raw_seek(tree, &parm[i]));
+
+ CHECK_EQUAL(lseek.out.offset);
+
+ return true;
+}
+
+
+/*
+ generate readx operations
+*/
+static bool handler_smb_readx(int instance)
+{
+ union smb_read parm[NSERVERS];
+ NTSTATUS status[NSERVERS];
+
+ parm[0].readx.level = RAW_READ_READX;
+ parm[0].readx.in.file.fnum = gen_fnum(instance);
+ parm[0].readx.in.offset = gen_offset();
+ parm[0].readx.in.mincnt = gen_io_count();
+ parm[0].readx.in.maxcnt = gen_io_count();
+ parm[0].readx.in.remaining = gen_io_count();
+ parm[0].readx.in.read_for_execute = gen_bool();
+ parm[0].readx.out.data = talloc_array(current_op.mem_ctx, uint8_t,
+ MAX(parm[0].readx.in.mincnt, parm[0].readx.in.maxcnt));
+
+ GEN_COPY_PARM;
+ GEN_SET_FNUM_SMB(readx.in.file.fnum);
+ GEN_CALL_SMB(smb_raw_read(tree, &parm[i]));
+
+ CHECK_EQUAL(readx.out.remaining);
+ CHECK_EQUAL(readx.out.compaction_mode);
+ CHECK_EQUAL(readx.out.nread);
+
+ return true;
+}
+
+/*
+ generate writex operations
+*/
+static bool handler_smb_writex(int instance)
+{
+ union smb_write parm[NSERVERS];
+ NTSTATUS status[NSERVERS];
+
+ parm[0].writex.level = RAW_WRITE_WRITEX;
+ parm[0].writex.in.file.fnum = gen_fnum(instance);
+ parm[0].writex.in.offset = gen_offset();
+ parm[0].writex.in.wmode = gen_bits_mask(0xFFFF);
+ parm[0].writex.in.remaining = gen_io_count();
+ parm[0].writex.in.count = gen_io_count();
+ parm[0].writex.in.data = talloc_zero_array(current_op.mem_ctx, uint8_t, parm[0].writex.in.count);
+
+ GEN_COPY_PARM;
+ GEN_SET_FNUM_SMB(writex.in.file.fnum);
+ GEN_CALL_SMB(smb_raw_write(tree, &parm[i]));
+
+ CHECK_EQUAL(writex.out.nwritten);
+ CHECK_EQUAL(writex.out.remaining);
+
+ return true;
+}
+
+/*
+ generate lockingx operations
+*/
+static bool handler_smb_lockingx(int instance)
+{
+ union smb_lock parm[NSERVERS];
+ NTSTATUS status[NSERVERS];
+ int n, nlocks;
+
+ parm[0].lockx.level = RAW_LOCK_LOCKX;
+ parm[0].lockx.in.file.fnum = gen_fnum(instance);
+ parm[0].lockx.in.mode = gen_lock_mode();
+ parm[0].lockx.in.timeout = gen_timeout();
+ do {
+ /* make sure we don't accidentially generate an oplock
+ break ack - otherwise the server can just block forever */
+ parm[0].lockx.in.ulock_cnt = gen_lock_count();
+ parm[0].lockx.in.lock_cnt = gen_lock_count();
+ nlocks = parm[0].lockx.in.ulock_cnt + parm[0].lockx.in.lock_cnt;
+ } while (nlocks == 0);
+
+ if (nlocks > 0) {
+ parm[0].lockx.in.locks = talloc_array(current_op.mem_ctx,
+ struct smb_lock_entry,
+ nlocks);
+ for (n=0;n<nlocks;n++) {
+ parm[0].lockx.in.locks[n].pid = gen_pid();
+ parm[0].lockx.in.locks[n].offset = gen_offset();
+ parm[0].lockx.in.locks[n].count = gen_io_count();
+ }
+ }
+
+ GEN_COPY_PARM;
+ GEN_SET_FNUM_SMB(lockx.in.file.fnum);
+ GEN_CALL_SMB(smb_raw_lock(tree, &parm[i]));
+
+ return true;
+}
+
+#if 0
+/*
+ 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 {
+ enum smb_setfileinfo_level level;
+ const char *name;
+ } levels[] = {
+#if 0
+ /* disabled until win2003 can handle them ... */
+ LVL(EA_SET), LVL(BASIC_INFO), LVL(DISPOSITION_INFO),
+ LVL(STANDARD), LVL(ALLOCATION_INFO), LVL(END_OF_FILE_INFO),
+#endif
+ LVL(SETATTR), LVL(SETATTRE), LVL(BASIC_INFORMATION),
+ LVL(RENAME_INFORMATION), LVL(DISPOSITION_INFORMATION),
+ LVL(POSITION_INFORMATION), LVL(MODE_INFORMATION),
+ LVL(ALLOCATION_INFORMATION), LVL(END_OF_FILE_INFORMATION),
+ LVL(1023), LVL(1025), LVL(1029), LVL(1032), LVL(1039), LVL(1040)
+ };
+ do {
+ i = gen_int_range(0, ARRAY_SIZE(levels)-1);
+ } while (ignore_pattern(levels[i].name));
+
+ 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_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:
+ /* 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;
+ struct smbcli_transport *transport = req->transport;
+
+ tid = SVAL(req->in.hdr, HDR_TID);
+
+ notify.nttrans.level = RAW_NOTIFY_NTTRANS;
+ status = smb_raw_changenotify_recv(req, current_op.mem_ctx, &notify);
+ 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;i<NSERVERS;i++) {
+ for (j=0;j<NINSTANCES;j++) {
+ if (transport == servers[i].smb_tree[j]->session->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;n<NSERVERS;n++) {
+ struct smbcli_request *req;
+ req = smb_raw_changenotify_send(servers[n].smb_tree[instance], &parm[n]);
+ req->async.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.reserved = gen_reserved32();
+
+ parm[0].in.locks = talloc_array(current_op.mem_ctx,
+ struct smb2_lock_element,
+ parm[0].in.lock_count);
+ for (n=0;n<parm[0].in.lock_count;n++) {
+ parm[0].in.locks[n].offset = gen_offset();
+ parm[0].in.locks[n].length = gen_io_count();
+ /* don't yet cope with async replies */
+ parm[0].in.locks[n].flags = gen_lock_flags_smb2() |
+ SMB2_LOCK_FLAG_FAIL_IMMEDIATELY;
+ parm[0].in.locks[n].reserved = gen_bits_mask2(0x0, 0xFFFFFFFF);
+ }
+
+ GEN_COPY_PARM;
+ GEN_SET_FNUM_SMB2(in.file.handle);
+ GEN_CALL_SMB2(smb2_lock(tree, &parm[i]));
+
+ return true;
+}
+
+/*
+ generate flush operations
+*/
+static bool handler_smb2_flush(int instance)
+{
+ struct smb2_flush parm[NSERVERS];
+ NTSTATUS status[NSERVERS];
+
+ ZERO_STRUCT(parm[0]);
+ parm[0].in.file.handle.data[0] = gen_fnum(instance);
+ parm[0].in.reserved1 = gen_reserved16();
+ parm[0].in.reserved2 = gen_reserved32();
+
+ GEN_COPY_PARM;
+ GEN_SET_FNUM_SMB2(in.file.handle);
+ GEN_CALL_SMB2(smb2_flush(tree, &parm[i]));
+
+ CHECK_EQUAL(out.reserved);
+
+ return true;
+}
+
+/*
+ generate echo operations
+*/
+static bool handler_smb2_echo(int instance)
+{
+ NTSTATUS status[NSERVERS];
+
+ GEN_CALL_SMB2(smb2_keepalive(tree->session->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<NSERVERS;i++) {
+ int n;
+ if (options.smb2) {
+ n = smb2_deltree(servers[i].smb2_tree[0], "gentest");
+ } else {
+ n = smbcli_deltree(servers[i].smb_tree[0], "gentest");
+ }
+ if (n == -1) {
+ printf("Failed to wipe tree on server %d\n", i);
+ exit(1);
+ }
+ if (options.smb2) {
+ status = smb2_util_mkdir(servers[i].smb2_tree[0], "gentest");
+ } else {
+ status = smbcli_mkdir(servers[i].smb_tree[0], "gentest");
+ }
+ if (NT_STATUS_IS_ERR(status)) {
+ printf("Failed to create gentest on server %d - %s\n", i, nt_errstr(status));
+ exit(1);
+ }
+ if (n > 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<options.numops;i++) {
+ fprintf(f, "%u\n", op_parms[i].seed);
+ }
+ fclose(f);
+ rename("seeds.tmp", options.seeds_file);
+}
+
+
+
+/*
+ the list of top-level operations that we will generate
+*/
+static struct {
+ const char *name;
+ bool (*handler)(int instance);
+ bool smb2;
+ int count, success_count;
+} gen_ops[] = {
+ {"CREATE", handler_smb2_create, true},
+ {"CLOSE", handler_smb2_close, true},
+ {"READ", handler_smb2_read, true},
+ {"WRITE", handler_smb2_write, true},
+ {"LOCK", handler_smb2_lock, true},
+ {"FLUSH", handler_smb2_flush, true},
+ {"ECHO", handler_smb2_echo, true},
+ {"QFILEINFO", handler_smb2_qfileinfo, true},
+ {"SFILEINFO", handler_smb2_sfileinfo, true},
+
+ {"OPEN", handler_smb_open, false},
+ {"OPENX", handler_smb_openx, false},
+ {"NTCREATEX", handler_smb_ntcreatex, false},
+ {"CLOSE", handler_smb_close, false},
+ {"UNLINK", handler_smb_unlink, false},
+ {"MKDIR", handler_smb_mkdir, false},
+ {"RMDIR", handler_smb_rmdir, false},
+ {"RENAME", handler_smb_rename, false},
+ {"NTRENAME", handler_smb_ntrename, false},
+ {"READX", handler_smb_readx, false},
+ {"WRITEX", handler_smb_writex, false},
+ {"CHKPATH", handler_smb_chkpath, false},
+ {"SEEK", handler_smb_seek, false},
+ {"LOCKINGX", handler_smb_lockingx, false},
+ {"QPATHINFO", handler_smb_qpathinfo, false},
+ {"QFILEINFO", handler_smb_qfileinfo, false},
+ {"SPATHINFO", handler_smb_spathinfo, false},
+ {"SFILEINFO", handler_smb_sfileinfo, false},
+ {"NOTIFY", handler_smb_notify, false},
+ {"SEEK", handler_smb_seek, false},
+};
+
+
+/*
+ run the test with the current set of op_parms parameters
+ return the number of operations that completed successfully
+*/
+static int run_test(struct tevent_context *ev, struct loadparm_context *lp_ctx)
+{
+ int op, i;
+
+ if (!connect_servers(ev, lp_ctx)) {
+ printf("Failed to connect to servers\n");
+ exit(1);
+ }
+
+ dump_seeds();
+
+ /* wipe any leftover files from old runs */
+ wipe_files();
+
+ /* reset the open handles array */
+ memset(open_handles, 0, options.max_open_handles * sizeof(open_handles[0]));
+ num_open_handles = 0;
+
+ for (i=0;i<ARRAY_SIZE(gen_ops);i++) {
+ gen_ops[i].count = 0;
+ gen_ops[i].success_count = 0;
+ }
+
+ for (op=0; op<options.numops; op++) {
+ int instance, which_op;
+ bool ret;
+
+ if (op_parms[op].disabled) continue;
+
+ srandom(op_parms[op].seed);
+
+ instance = gen_int_range(0, NINSTANCES-1);
+
+ /* generate a non-ignored operation */
+ do {
+ which_op = gen_int_range(0, ARRAY_SIZE(gen_ops)-1);
+ } while (ignore_pattern(gen_ops[which_op].name) ||
+ gen_ops[which_op].smb2 != options.smb2);
+
+ DEBUG(3,("Generating op %s on instance %d\n",
+ gen_ops[which_op].name, instance));
+
+ current_op.seed = op_parms[op].seed;
+ current_op.opnum = op;
+ current_op.name = gen_ops[which_op].name;
+ current_op.status = NT_STATUS_OK;
+ talloc_free(current_op.mem_ctx);
+ current_op.mem_ctx = talloc_named(NULL, 0, "%s", current_op.name);
+
+ ret = gen_ops[which_op].handler(instance);
+
+ gen_ops[which_op].count++;
+ if (NT_STATUS_IS_OK(current_op.status)) {
+ gen_ops[which_op].success_count++;
+ }
+
+ if (!ret) {
+ printf("Failed at operation %d - %s\n",
+ op, gen_ops[which_op].name);
+ return op;
+ }
+
+ if (op % 100 == 0) {
+ printf("%d\n", op);
+ }
+ }
+
+ for (i=0;i<ARRAY_SIZE(gen_ops);i++) {
+ printf("Op %-10s got %d/%d success\n",
+ gen_ops[i].name,
+ gen_ops[i].success_count,
+ gen_ops[i].count);
+ }
+
+ return op;
+}
+
+/*
+ perform a backtracking analysis of the minimal set of operations
+ to generate an error
+*/
+static void backtrack_analyze(struct tevent_context *ev,
+ struct loadparm_context *lp_ctx)
+{
+ int chunk, ret;
+ const char *mismatch = current_op.mismatch;
+
+ chunk = options.numops / 2;
+
+ do {
+ int base;
+ for (base=0;
+ chunk > 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<max; i++) {
+ op_parms[i].disabled = true;
+ }
+ printf("Testing %d ops with %d-%d disabled\n",
+ options.numops, base, max-1);
+ ret = run_test(ev, lp_ctx);
+ printf("Completed %d of %d ops\n", ret, options.numops);
+ for (i=base;i<max; i++) {
+ op_parms[i].disabled = false;
+ }
+ if (ret == options.numops) {
+ /* this chunk is needed */
+ base += chunk;
+ } else if (mismatch != current_op.mismatch &&
+ strcmp(mismatch, current_op.mismatch)) {
+ base += chunk;
+ printf("Different error in backtracking\n");
+ } else if (ret < base) {
+ printf("damn - inconsistent errors! found early error\n");
+ options.numops = ret+1;
+ base = 0;
+ } else {
+ /* it failed - this chunk isn't needed for a failure */
+ memmove(&op_parms[base], &op_parms[max],
+ sizeof(op_parms[0]) * (options.numops - max));
+ options.numops = (ret+1) - (max - base);
+ }
+ }
+
+ if (chunk == 2) {
+ chunk = 1;
+ } else {
+ chunk *= 0.4;
+ }
+
+ if (options.analyze_continuous && chunk == 0 && options.numops != 1) {
+ chunk = 1;
+ }
+ } while (chunk > 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]));
+
+ srandom(options.seed);
+ op_parms = calloc(options.numops, sizeof(op_parms[0]));
+
+ /* 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<options.numops;op++) {
+ if (!preset[op]) {
+ printf("Not enough seeds in %s\n", options.seeds_file);
+ exit(1);
+ }
+ op_parms[op].seed = atoi(preset[op]);
+ }
+ printf("Loaded %d seeds from %s\n", options.numops, options.seeds_file);
+ } else {
+ for (op=0; op<options.numops; op++) {
+ op_parms[op].seed = random();
+ }
+ }
+
+ ret = run_test(ev, lp_ctx);
+
+ if (ret != options.numops && options.analyze) {
+ options.numops = ret+1;
+ backtrack_analyze(ev, lp_ctx);
+ } else if (options.analyze_always) {
+ backtrack_analyze(ev, lp_ctx);
+ } else if (options.analyze_continuous) {
+ while (run_test(ev, lp_ctx) == options.numops) ;
+ }
+
+ return ret == options.numops;
+}
+
+
+static void usage(poptContext pc)
+{
+ printf(
+"Usage:\n\
+ gentest //server1/share1 //server2/share2 [options..]\n\
+");
+ poptPrintUsage(pc, stdout, 0);
+}
+
+/**
+ split a UNC name into server and share names
+*/
+static bool split_unc_name(const char *unc, char **server, char **share)
+{
+ char *p = strdup(unc);
+ if (!p) return false;
+ all_string_sub(p, "\\", "/", 0);
+ if (strncmp(p, "//", 2) != 0) return false;
+
+ (*server) = p+2;
+ p = strchr(*server, '/');
+ if (!p) return false;
+
+ *p = 0;
+ (*share) = p+1;
+
+ return true;
+}
+
+
+
+/****************************************************************************
+ main program
+****************************************************************************/
+ int main(int argc, char *argv[])
+{
+ int opt;
+ int i, username_count=0;
+ bool ret;
+ char *ignore_file=NULL;
+ struct tevent_context *ev;
+ struct loadparm_context *lp_ctx;
+ poptContext pc;
+ int argc_new;
+ char **argv_new;
+ enum {OPT_UNCLIST=1000};
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {"smb2", 0, POPT_ARG_NONE, &options.smb2, 0, "use SMB2 protocol", NULL},
+ {"seed", 0, POPT_ARG_INT, &options.seed, 0, "Seed to use for randomizer", NULL},
+ {"num-ops", 0, POPT_ARG_INT, &options.numops, 0, "num ops", NULL},
+ {"oplocks", 0, POPT_ARG_NONE, &options.use_oplocks,0, "use oplocks", NULL},
+ {"showall", 0, POPT_ARG_NONE, &options.showall, 0, "display all operations", NULL},
+ {"analyse", 0, POPT_ARG_NONE, &options.analyze, 0, "do backtrack analysis", NULL},
+ {"analysealways", 0, POPT_ARG_NONE, &options.analyze_always, 0, "analysis always", NULL},
+ {"analysecontinuous", 0, POPT_ARG_NONE, &options.analyze_continuous, 0, "analysis continuous", NULL},
+ {"ignore", 0, POPT_ARG_STRING, &ignore_file, 0, "ignore from file", NULL},
+ {"preset", 0, POPT_ARG_NONE, &options.use_preset_seeds, 0, "use preset seeds", NULL},
+ {"fast", 0, POPT_ARG_NONE, &options.fast_reconnect, 0, "use fast reconnect", NULL},
+ {"unclist", 0, POPT_ARG_STRING, NULL, OPT_UNCLIST, "unclist", NULL},
+ {"seedsfile", 0, POPT_ARG_STRING, &options.seeds_file, 0, "seed file", NULL},
+ { "user", 'U', POPT_ARG_STRING, NULL, 'U', "Set the network username", "[DOMAIN/]USERNAME[%PASSWORD]" },
+ {"maskindexing", 0, POPT_ARG_NONE, &options.mask_indexing, 0, "mask out the indexed file attrib", NULL},
+ {"noeas", 0, POPT_ARG_NONE, &options.no_eas, 0, "don't use extended attributes", NULL},
+ {"noacls", 0, POPT_ARG_NONE, &options.no_acls, 0, "don't use ACLs", NULL},
+ {"skip-cleanup", 0, POPT_ARG_NONE, &options.skip_cleanup, 0, "don't delete files at start", NULL},
+ {"valid", 0, POPT_ARG_NONE, &options.valid, 0, "generate only valid fields", NULL},
+ POPT_COMMON_SAMBA
+ POPT_COMMON_CONNECTION
+ POPT_COMMON_CREDENTIALS
+ POPT_COMMON_VERSION
+ { NULL }
+ };
+
+ memset(&bad_smb2_handle, 0xFF, sizeof(bad_smb2_handle));
+
+ setlinebuf(stdout);
+ options.seed = time(NULL);
+ options.numops = 1000;
+ options.max_open_handles = 20;
+ options.seeds_file = "gentest_seeds.dat";
+
+ pc = poptGetContext("gentest", argc, (const char **) argv, long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+
+ poptSetOtherOptionHelp(pc, "<unc1> <unc2>");
+
+ lp_ctx = cmdline_lp_ctx;
+ servers[0].credentials = cli_credentials_init(talloc_autofree_context());
+ servers[1].credentials = cli_credentials_init(talloc_autofree_context());
+ 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:
+ lp_set_cmdline(cmdline_lp_ctx, "torture:unclist", poptGetOptArg(pc));
+ break;
+ case 'U':
+ if (username_count == 2) {
+ usage(pc);
+ exit(1);
+ }
+ cli_credentials_parse_string(servers[username_count].credentials, poptGetOptArg(pc), CRED_SPECIFIED);
+ username_count++;
+ break;
+ }
+ }
+
+ 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<argc; i++) {
+ if (argv_new[i] == NULL) {
+ argc_new = i;
+ break;
+ }
+ }
+
+ if (!(argc_new >= 3)) {
+ usage(pc);
+ exit(1);
+ }
+
+ setlinebuf(stdout);
+
+ setup_logging("gentest", DEBUG_STDOUT);
+
+ if (argc < 3 || argv[1][0] == '-') {
+ usage(pc);
+ exit(1);
+ }
+
+ setup_logging(argv[0], DEBUG_STDOUT);
+
+ for (i=0;i<NSERVERS;i++) {
+ const char *share = argv[1+i];
+ if (!split_unc_name(share, &servers[i].server_name, &servers[i].share_name)) {
+ printf("Invalid share name '%s'\n", share);
+ return -1;
+ }
+ }
+
+ if (username_count == 0) {
+ usage(pc);
+ return -1;
+ }
+ if (username_count == 1) {
+ servers[1].credentials = servers[0].credentials;
+ }
+
+ printf("seed=%u\n", options.seed);
+
+ ev = s4_event_context_init(talloc_autofree_context());
+
+ gensec_init(lp_ctx);
+
+ ret = start_gentest(ev, lp_ctx);
+
+ if (ret) {
+ printf("gentest completed - no errors\n");
+ } else {
+ printf("gentest failed\n");
+ }
+
+ return ret?0:-1;
+}
diff --git a/source4/torture/ldap/basic.c b/source4/torture/ldap/basic.c
new file mode 100644
index 0000000000..358bf53590
--- /dev/null
+++ b/source4/torture/ldap/basic.c
@@ -0,0 +1,239 @@
+/*
+ Unix SMB/CIFS mplementation.
+ 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 <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "includes.h"
+#include "libcli/ldap/ldap_client.h"
+#include "lib/cmdline/popt_common.h"
+
+#include "torture/torture.h"
+#include "torture/ldap/proto.h"
+
+#include "param/param.h"
+
+static bool test_bind_simple(struct ldap_connection *conn, const char *userdn, const char *password)
+{
+ NTSTATUS status;
+ bool ret = true;
+
+ status = torture_ldap_bind(conn, userdn, password);
+ if (!NT_STATUS_IS_OK(status)) {
+ ret = false;
+ }
+
+ return ret;
+}
+
+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)
+{
+ bool ret = true;
+
+ printf("Testing multiple binds on a single connnection as anonymous and user\n");
+
+ ret = test_bind_simple(conn, NULL, NULL);
+ if (!ret) {
+ printf("1st bind as anonymous failed\n");
+ return ret;
+ }
+
+ ret = test_bind_simple(conn, userdn, password);
+ if (!ret) {
+ printf("2nd bind as authenticated user failed\n");
+ }
+
+ return ret;
+}
+
+static bool test_search_rootDSE(struct ldap_connection *conn, char **basedn)
+{
+ 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;
+
+ 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; i<r->num_attributes; i++) {
+ int j;
+ for (j=0; j<r->attributes[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);
+ }
+ }
+ }
+
+ talloc_free(req);
+
+ 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;
+}
+
+
+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);
+ char *url;
+ char *basedn;
+
+ 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)) {
+ ret = false;
+ }
+
+ /* other basic tests here */
+
+ if (!test_multibind(conn, userdn, secret)) {
+ ret = false;
+ }
+
+ if (!test_bind_sasl(torture, conn, cmdline_credentials)) {
+ ret = false;
+ }
+
+ if (!test_compare_sasl(conn, basedn)) {
+ ret = false;
+ }
+
+ /* no more test we are closing */
+ torture_ldap_close(conn);
+ talloc_free(mem_ctx);
+
+
+ return ret;
+}
+
diff --git a/source4/torture/ldap/cldap.c b/source4/torture/ldap/cldap.c
new file mode 100644
index 0000000000..1ddc628a5c
--- /dev/null
+++ b/source4/torture/ldap/cldap.c
@@ -0,0 +1,497 @@
+/*
+ Unix SMB/CIFS mplementation.
+
+ test CLDAP 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 <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "includes.h"
+#include "libcli/cldap/cldap.h"
+#include "libcli/ldap/ldap.h"
+#include "librpc/gen_ndr/ndr_nbt.h"
+#include "librpc/gen_ndr/netlogon.h"
+#include "torture/torture.h"
+#include "lib/ldb/include/ldb.h"
+#include "param/param.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");
+/*
+ test netlogon operations
+*/
+static bool test_cldap_netlogon(struct torture_context *tctx, const char *dest)
+{
+ struct cldap_socket *cldap;
+ NTSTATUS status;
+ struct cldap_netlogon search, empty_search;
+ struct netlogon_samlogon_response n1;
+ struct GUID guid;
+ int i;
+
+ cldap = cldap_socket_init(tctx, tctx->ev, lp_iconv_convenience(tctx->lp_ctx));
+
+ ZERO_STRUCT(search);
+ search.in.dest_address = dest;
+ search.in.dest_port = lp_cldap_port(tctx->lp_ctx);
+ 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 = cldap_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 = cldap_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<<i);
+ printf("Trying netlogon level 0x%x\n", i);
+ status = cldap_netlogon(cldap, tctx, &search);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ }
+
+ search.in.version = NETLOGON_NT_VERSION_5|NETLOGON_NT_VERSION_5EX|NETLOGON_NT_VERSION_IP;
+
+ status = cldap_netlogon(cldap, tctx, &search);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("Trying with User=NULL\n");
+
+ search.in.user = NULL;
+ status = cldap_netlogon(cldap, tctx, &search);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.user_name, "");
+ CHECK_VAL(search.out.netlogon.data.nt5_ex.command, LOGON_SAM_LOGON_RESPONSE_EX);
+
+ printf("Trying with User=Administrator\n");
+
+ search.in.user = "Administrator";
+ status = cldap_netlogon(cldap, tctx, &search);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.user_name, search.in.user);
+ CHECK_VAL(search.out.netlogon.data.nt5_ex.command, LOGON_SAM_LOGON_USER_UNKNOWN_EX);
+
+ search.in.version = NETLOGON_NT_VERSION_5;
+ status = cldap_netlogon(cldap, tctx, &search);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("Trying with User=NULL\n");
+
+ search.in.user = NULL;
+ status = cldap_netlogon(cldap, tctx, &search);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.user_name, "");
+ CHECK_VAL(search.out.netlogon.data.nt5_ex.command, LOGON_SAM_LOGON_RESPONSE);
+
+ printf("Trying with User=Administrator\n");
+
+ search.in.user = "Administrator";
+ status = cldap_netlogon(cldap, tctx, &search);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.user_name, search.in.user);
+ CHECK_VAL(search.out.netlogon.data.nt5_ex.command, LOGON_SAM_LOGON_USER_UNKNOWN);
+
+ search.in.version = NETLOGON_NT_VERSION_5 | NETLOGON_NT_VERSION_5EX;
+
+ printf("Trying with a GUID\n");
+ search.in.realm = NULL;
+ search.in.domain_guid = GUID_string(tctx, &n1.data.nt5_ex.domain_uuid);
+ status = cldap_netlogon(cldap, tctx, &search);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VAL(search.out.netlogon.data.nt5_ex.command, LOGON_SAM_LOGON_USER_UNKNOWN_EX);
+ CHECK_STRING(GUID_string(tctx, &search.out.netlogon.data.nt5_ex.domain_uuid), search.in.domain_guid);
+
+ printf("Trying with a incorrect GUID\n");
+ guid = GUID_random();
+ search.in.user = NULL;
+ search.in.domain_guid = GUID_string(tctx, &guid);
+ status = cldap_netlogon(cldap, tctx, &search);
+ CHECK_STATUS(status, NT_STATUS_NOT_FOUND);
+
+ printf("Trying with a AAC\n");
+ search.in.acct_control = ACB_WSTRUST|ACB_SVRTRUST;
+ search.in.realm = n1.data.nt5_ex.dns_domain;
+ status = cldap_netlogon(cldap, tctx, &search);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VAL(search.out.netlogon.data.nt5_ex.command, LOGON_SAM_LOGON_RESPONSE_EX);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.user_name, "");
+
+ printf("Trying with a zero AAC\n");
+ search.in.acct_control = 0x0;
+ search.in.realm = n1.data.nt5_ex.dns_domain;
+ status = cldap_netlogon(cldap, tctx, &search);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VAL(search.out.netlogon.data.nt5_ex.command, LOGON_SAM_LOGON_RESPONSE_EX);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.user_name, "");
+
+ printf("Trying with a zero AAC and user=Administrator\n");
+ search.in.acct_control = 0x0;
+ search.in.user = "Administrator";
+ search.in.realm = n1.data.nt5_ex.dns_domain;
+ status = cldap_netlogon(cldap, tctx, &search);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VAL(search.out.netlogon.data.nt5_ex.command, LOGON_SAM_LOGON_USER_UNKNOWN_EX);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.user_name, "Administrator");
+
+ printf("Trying with a bad AAC\n");
+ search.in.user = NULL;
+ search.in.acct_control = 0xFF00FF00;
+ search.in.realm = n1.data.nt5_ex.dns_domain;
+ status = cldap_netlogon(cldap, tctx, &search);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VAL(search.out.netlogon.data.nt5_ex.command, LOGON_SAM_LOGON_RESPONSE_EX);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.user_name, "");
+
+ printf("Trying with a user only\n");
+ search = empty_search;
+ search.in.user = "Administrator";
+ status = cldap_netlogon(cldap, tctx, &search);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.dns_domain, n1.data.nt5_ex.dns_domain);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.user_name, search.in.user);
+
+ printf("Trying with just a bad username\n");
+ search.in.user = "___no_such_user___";
+ status = cldap_netlogon(cldap, tctx, &search);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.user_name, search.in.user);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.dns_domain, n1.data.nt5_ex.dns_domain);
+ CHECK_VAL(search.out.netlogon.data.nt5_ex.command, LOGON_SAM_LOGON_USER_UNKNOWN_EX);
+
+ printf("Trying with just a bad domain\n");
+ search = empty_search;
+ search.in.realm = "___no_such_domain___";
+ status = cldap_netlogon(cldap, tctx, &search);
+ CHECK_STATUS(status, NT_STATUS_NOT_FOUND);
+
+ printf("Trying with a incorrect domain and correct guid\n");
+ search.in.domain_guid = GUID_string(tctx, &n1.data.nt5_ex.domain_uuid);
+ status = cldap_netlogon(cldap, tctx, &search);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.dns_domain, n1.data.nt5_ex.dns_domain);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.user_name, "");
+ CHECK_VAL(search.out.netlogon.data.nt5_ex.command, LOGON_SAM_LOGON_RESPONSE_EX);
+
+ printf("Trying with a incorrect domain and incorrect guid\n");
+ search.in.domain_guid = GUID_string(tctx, &guid);
+ status = cldap_netlogon(cldap, tctx, &search);
+ CHECK_STATUS(status, NT_STATUS_NOT_FOUND);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.dns_domain, n1.data.nt5_ex.dns_domain);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.user_name, "");
+ CHECK_VAL(search.out.netlogon.data.nt5_ex.command, LOGON_SAM_LOGON_RESPONSE_EX);
+
+ printf("Trying with a incorrect GUID and correct domain\n");
+ search.in.domain_guid = GUID_string(tctx, &guid);
+ search.in.realm = n1.data.nt5_ex.dns_domain;
+ status = cldap_netlogon(cldap, tctx, &search);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.dns_domain, n1.data.nt5_ex.dns_domain);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.user_name, "");
+ CHECK_VAL(search.out.netlogon.data.nt5_ex.command, LOGON_SAM_LOGON_RESPONSE_EX);
+
+ return true;
+}
+
+/*
+ test cldap netlogon server type flags
+*/
+static bool test_cldap_netlogon_flags(struct torture_context *tctx,
+ const char *dest)
+{
+ struct cldap_socket *cldap;
+ NTSTATUS status;
+ struct cldap_netlogon search;
+ struct netlogon_samlogon_response n1;
+ uint32_t server_type;
+
+ cldap = cldap_socket_init(tctx, tctx->ev, lp_iconv_convenience(tctx->lp_ctx));
+
+ printf("Printing out netlogon server type flags:\n");
+
+ ZERO_STRUCT(search);
+ search.in.dest_address = dest;
+ search.in.dest_port = lp_cldap_port(tctx->lp_ctx);
+ search.in.acct_control = -1;
+ search.in.version = NETLOGON_NT_VERSION_5 | NETLOGON_NT_VERSION_5EX;
+ search.in.map_response = true;
+
+ status = cldap_netlogon(cldap, tctx, &search);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ n1 = search.out.netlogon;
+ if (n1.ntver == NETLOGON_NT_VERSION_5)
+ server_type = n1.data.nt5.server_type;
+ else if (n1.ntver == NETLOGON_NT_VERSION_5EX)
+ server_type = n1.data.nt5_ex.server_type;
+
+ printf("The word is: %i\n", server_type);
+ if (server_type & NBT_SERVER_PDC)
+ printf("NBT_SERVER_PDC ");
+ if (server_type & NBT_SERVER_GC)
+ printf("NBT_SERVER_GC ");
+ if (server_type & NBT_SERVER_LDAP)
+ printf("NBT_SERVER_LDAP ");
+ if (server_type & NBT_SERVER_DS)
+ printf("NBT_SERVER_DS ");
+ if (server_type & NBT_SERVER_KDC)
+ printf("NBT_SERVER_KDC ");
+ if (server_type & NBT_SERVER_TIMESERV)
+ printf("NBT_SERVER_TIMESERV ");
+ if (server_type & NBT_SERVER_CLOSEST)
+ printf("NBT_SERVER_CLOSEST ");
+ if (server_type & NBT_SERVER_WRITABLE)
+ printf("NBT_SERVER_WRITABLE ");
+ if (server_type & NBT_SERVER_GOOD_TIMESERV)
+ printf("NBT_SERVER_GOOD_TIMESERV ");
+ if (server_type & NBT_SERVER_NDNC)
+ printf("NBT_SERVER_NDNC ");
+ if (server_type & NBT_SERVER_SELECT_SECRET_DOMAIN_6)
+ printf("NBT_SERVER_SELECT_SECRET_DOMAIN_6");
+ if (server_type & NBT_SERVER_FULL_SECRET_DOMAIN_6)
+ printf("NBT_SERVER_FULL_SECRET_DOMAIN_6");
+ if (server_type & DS_DNS_CONTROLLER)
+ printf("DS_DNS_CONTROLLER ");
+ if (server_type & DS_DNS_DOMAIN)
+ printf("DS_DNS_DOMAIN ");
+ if (server_type & DS_DNS_FOREST)
+ printf("DS_DNS_FOREST ");
+
+ printf("\n");
+
+ return true;
+}
+
+/*
+ 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 cldap netlogon server type flag "NBT_SERVER_DS_DNS_FOREST"
+*/
+static bool test_cldap_netlogon_flag_ds_dns_forest(struct torture_context *tctx,
+ const char *dest)
+{
+ struct cldap_socket *cldap;
+ NTSTATUS status;
+ struct cldap_netlogon search;
+ uint32_t server_type;
+ struct netlogon_samlogon_response n1;
+
+ bool result = true;
+
+ cldap = cldap_socket_init(tctx, tctx->ev, lp_iconv_convenience(tctx->lp_ctx));
+
+ printf("Testing netlogon server type flag NBT_SERVER_DS_DNS_FOREST: ");
+
+ ZERO_STRUCT(search);
+ search.in.dest_address = dest;
+ search.in.dest_port = lp_cldap_port(tctx->lp_ctx);
+ search.in.acct_control = -1;
+ search.in.version = NETLOGON_NT_VERSION_5 | NETLOGON_NT_VERSION_5EX;
+ search.in.map_response = true;
+
+ status = cldap_netlogon(cldap, tctx, &search);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ n1 = search.out.netlogon;
+ if (n1.ntver == NETLOGON_NT_VERSION_5)
+ server_type = n1.data.nt5.server_type;
+ else if (n1.ntver == NETLOGON_NT_VERSION_5EX)
+ server_type = n1.data.nt5_ex.server_type;
+
+ if (server_type & DS_DNS_FOREST) {
+ struct cldap_search search2;
+ const char *attrs[] = { "defaultNamingContext", "rootDomainNamingContext",
+ NULL };
+ struct ldb_context *ldb;
+ struct ldb_message *msg;
+
+ /* Trying to fetch the attributes "defaultNamingContext" and
+ "rootDomainNamingContext" */
+ ZERO_STRUCT(search2);
+ search2.in.dest_address = dest;
+ search2.in.dest_port = lp_cldap_port(tctx->lp_ctx);
+ search2.in.timeout = 10;
+ search2.in.retries = 3;
+ search2.in.filter = "(objectclass=*)";
+ search2.in.attributes = attrs;
+
+ status = cldap_search(cldap, tctx, &search2);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ ldb = ldb_init(NULL, NULL);
+
+ msg = ldap_msg_to_ldb(ldb, ldb, search2.out.response);
+
+ /* Try to compare the two attributes */
+ if (ldb_msg_element_compare(ldb_msg_find_element(msg, attrs[0]),
+ ldb_msg_find_element(msg, attrs[1])))
+ result = false;
+
+ talloc_free(ldb);
+ }
+
+ if (result)
+ printf("passed\n");
+ else
+ printf("failed\n");
+
+ return result;
+}
+
+/*
+ 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 };
+
+ cldap = cldap_socket_init(tctx, tctx->ev, lp_iconv_convenience(tctx->lp_ctx));
+
+ ZERO_STRUCT(search);
+ search.in.dest_address = dest;
+ search.in.dest_port = lp_cldap_port(tctx->lp_ctx);
+ 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 = "(objectclass2=*)";
+ 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_netlogon(torture, host);
+ ret &= test_cldap_netlogon_flags(torture, host);
+ ret &= test_cldap_netlogon_flag_ds_dns_forest(torture, host);
+ 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 0000000000..ae2cb80836
--- /dev/null
+++ b/source4/torture/ldap/cldapbench.c
@@ -0,0 +1,204 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/events/events.h"
+#include "libcli/cldap/cldap.h"
+#include "libcli/resolve/resolve.h"
+#include "torture/torture.h"
+#include "param/param.h"
+
+struct bench_state {
+ int pass_count, fail_count;
+};
+
+static void request_netlogon_handler(struct cldap_request *req)
+{
+ struct cldap_netlogon io;
+ struct bench_state *state = talloc_get_type(req->async.private_data, struct bench_state);
+ NTSTATUS status;
+ TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+ io.in.version = 6;
+ status = cldap_netlogon_recv(req, tmp_ctx, &io);
+ 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();
+ bool ret = true;
+ int timelimit = torture_setting_int(tctx, "timelimit", 10);
+ struct cldap_netlogon search;
+ struct bench_state *state;
+
+ cldap = cldap_socket_init(tctx, tctx->ev, lp_iconv_convenience(tctx->lp_ctx));
+
+ state = talloc_zero(tctx, struct bench_state);
+
+ ZERO_STRUCT(search);
+ search.in.dest_address = address;
+ search.in.dest_port = lp_cldap_port(tctx->lp_ctx);
+ 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 cldap_request *req;
+ req = cldap_netlogon_send(cldap, &search);
+
+ req->async.private_data = state;
+ req->async.fn = request_netlogon_handler;
+ 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);
+ }
+ }
+ }
+
+ event_loop_once(cldap->event_ctx);
+ }
+
+ while (num_sent != (state->pass_count + state->fail_count)) {
+ event_loop_once(cldap->event_ctx);
+ }
+
+ printf("%.1f queries per second (%d failures) \n",
+ state->pass_count / timeval_elapsed(&tv),
+ state->fail_count);
+
+ talloc_free(cldap);
+ return ret;
+}
+
+static void request_rootdse_handler(struct cldap_request *req)
+{
+ struct cldap_search io;
+ struct bench_state *state = talloc_get_type(req->async.private_data, struct bench_state);
+ NTSTATUS status;
+ TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+ status = cldap_search_recv(req, tmp_ctx, &io);
+ 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();
+ bool ret = true;
+ int timelimit = torture_setting_int(tctx, "timelimit", 10);
+ struct cldap_search search;
+ struct bench_state *state;
+
+ cldap = cldap_socket_init(tctx, tctx->ev, lp_iconv_convenience(tctx->lp_ctx));
+
+ state = talloc_zero(tctx, struct bench_state);
+
+ ZERO_STRUCT(search);
+ search.in.dest_address = address;
+ search.in.dest_port = lp_cldap_port(tctx->lp_ctx);
+ 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 cldap_request *req;
+ req = cldap_search_send(cldap, &search);
+
+ req->async.private_data = state;
+ req->async.fn = request_rootdse_handler;
+ 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 ret;
+}
+
+/*
+ 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(lp_resolve_context(torture->lp_ctx), &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 0000000000..2c11de729c
--- /dev/null
+++ b/source4/torture/ldap/common.c
@@ -0,0 +1,117 @@
+/*
+ Unix SMB/CIFS mplementation.
+ 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 <http://www.gnu.org/licenses/>.
+
+*/
+
+#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;
+}
+
+/* open a ldap connection to a server */
+NTSTATUS torture_ldap_connection2(struct torture_context *tctx, struct ldap_connection **conn,
+ const char *url, const char *userdn, const char *password)
+{
+ NTSTATUS status;
+
+ status = torture_ldap_connection(tctx, conn, url);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ status = ldap_bind_simple(*conn, userdn, password);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed a simple ldap bind - %s\n", ldap_errstr(*conn, tctx, status));
+ }
+
+ return status;
+}
+
+/* close an ldap connection to a server */
+NTSTATUS torture_ldap_close(struct ldap_connection *conn)
+{
+ talloc_free(conn);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS torture_ldap_init(void)
+{
+ struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "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, "CLDAP", torture_cldap);
+ torture_suite_add_simple_test(suite, "SCHEMA", torture_ldap_schema);
+ torture_suite_add_simple_test(suite, "UPTODATEVECTOR", torture_ldap_uptodatevector);
+
+ suite->description = talloc_strdup(suite, "LDAP and CLDAP tests");
+
+ torture_register_suite(suite);
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/torture/ldap/schema.c b/source4/torture/ldap/schema.c
new file mode 100644
index 0000000000..6184ad266d
--- /dev/null
+++ b/source4/torture/ldap/schema.c
@@ -0,0 +1,409 @@
+/*
+ Unix SMB/CIFS mplementation.
+ 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 <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "includes.h"
+#include "libcli/ldap/ldap_client.h"
+#include "lib/cmdline/popt_common.h"
+#include "ldb_wrap.h"
+#include "lib/ldb/include/ldb.h"
+#include "lib/ldb/include/ldb_errors.h"
+#include "dsdb/samdb/samdb.h"
+#include "../lib/util/dlinklist.h"
+
+#include "torture/torture.h"
+#include "torture/ldap/proto.h"
+
+#include "param/param.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);
+ struct dsdb_attribute *attr = NULL;
+ WERROR status;
+
+ attr = talloc_zero(schema, struct dsdb_attribute);
+ if (!attr) {
+ goto failed;
+ }
+
+ status = dsdb_attribute_from_ldb(schema, msg, attr, attr);
+ if (!W_ERROR_IS_OK(status)) {
+ goto failed;
+ }
+
+ DLIST_ADD_END(schema->attributes, attr, struct dsdb_attribute *);
+ return LDB_SUCCESS;
+failed:
+ talloc_free(attr);
+ 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);
+ struct dsdb_class *obj;
+ WERROR status;
+
+ obj = talloc_zero(schema, struct dsdb_class);
+ if (!obj) {
+ goto failed;
+ }
+
+ status = dsdb_class_from_ldb(schema, msg, obj, obj);
+ if (!W_ERROR_IS_OK(status)) {
+ goto failed;
+ }
+
+ DLIST_ADD_END(schema->classes, obj, struct dsdb_class *);
+ 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(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;
+}
+
+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,
+ cmdline_credentials,
+ 0, NULL);
+ 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);
+
+failed:
+ return ret;
+}
diff --git a/source4/torture/ldap/uptodatevector.c b/source4/torture/ldap/uptodatevector.c
new file mode 100644
index 0000000000..bf32edb065
--- /dev/null
+++ b/source4/torture/ldap/uptodatevector.c
@@ -0,0 +1,177 @@
+/*
+ Unix SMB/CIFS mplementation.
+ 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 <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "includes.h"
+#include "libcli/ldap/ldap_client.h"
+#include "lib/cmdline/popt_common.h"
+#include "ldb_wrap.h"
+#include "lib/ldb/include/ldb.h"
+#include "lib/ldb/include/ldb_errors.h"
+#include "dsdb/samdb/samdb.h"
+#include "../lib/util/dlinklist.h"
+
+#include "torture/torture.h"
+#include "torture/ldap/proto.h"
+
+#include "librpc/ndr/libndr.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,
+ lp_iconv_convenience(torture->lp_ctx), &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;j<msg->num_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,
+ lp_iconv_convenience(torture->lp_ctx), &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)samdb_result_uint64(r->msgs[0], "uSNChanged", 0),
+ samdb_result_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,
+ cmdline_credentials,
+ 0, NULL);
+ if (!ldb) goto failed;
+
+ ret &= test_check_uptodatevector(torture, ldb, samdb_base_dn(ldb));
+ ret &= test_check_uptodatevector(torture, ldb, samdb_config_dn(ldb));
+ ret &= test_check_uptodatevector(torture, ldb, samdb_schema_dn(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 0000000000..c2977bcb70
--- /dev/null
+++ b/source4/torture/ldb/ldb.c
@@ -0,0 +1,735 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Test LDB attribute functions
+
+ Copyright (C) Andrew Bartlet <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/events/events.h"
+#include "lib/ldb/include/ldb.h"
+#include "lib/ldb/include/ldb_errors.h"
+#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/ldb/proto.h"
+
+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 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;
+
+ 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), 0,
+ "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");
+
+
+
+ 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), 0,
+ "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), 0,
+ "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, "<GUID=%s>;<SID=%s>;%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 incorect");
+
+ torture_assert_data_blob_equal(torture, *ldb_dn_get_extended_component(dn, "GUID"), guid_blob,
+ "Extended DN GUID incorect");
+
+ 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",
+ "componet zero incorrect");
+
+ torture_assert_data_blob_equal(torture, *ldb_dn_get_component_val(dn, 0), data_blob_string_const("admin"),
+ "componet zero incorrect");
+
+ torture_assert_str_equal(torture, ldb_dn_get_extended_linearized(mem_ctx, dn, 1),
+ talloc_asprintf(mem_ctx, "<GUID=%s>;<SID=%s>;%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, "<GUID=%s>;<SID=%s>;%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 incorect");
+
+ torture_assert_data_blob_equal(torture, *ldb_dn_get_extended_component(dn, "GUID"), guid_blob,
+ "Extended DN GUID incorect");
+
+ 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, "<GUID=%s>;<SID=%s>;%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, "<GUID=%s>;<SID=%s>;%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=%s>",
+ 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' componet 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 incorect");
+
+ 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=%s>",
+ guid),
+ "Clear extended linearized DN incorrect");
+
+ torture_assert_str_equal(torture, ldb_dn_get_extended_linearized(mem_ctx, dn, 0),
+ talloc_asprintf(mem_ctx, "<GUID=%s>",
+ 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, "<GUID=%s>",
+ 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 incorect");
+
+ 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=%s>",
+ 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 incorect");
+
+ 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=%s>",
+ sid),
+ "Clear extended linearized DN incorrect");
+
+ torture_assert_str_equal(torture, ldb_dn_get_extended_linearized(mem_ctx, dn, 0),
+ talloc_asprintf(mem_ctx, "<SID=%s>",
+ 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, "<SID=%s>",
+ 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 incorect");
+
+ 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;
+
+ torture_assert(torture,
+ ldb = ldb_init(mem_ctx, torture->ev),
+ "Failed to init ldb");
+
+ torture_assert_int_equal(torture,
+ ldb_register_samba_handlers(ldb), 0,
+ "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");
+
+ 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), 0,
+ "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, "<PID=%s>;%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, "<GUID=%s>%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, "<GUID=%s>;",
+ 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, "<GUID=%s>;",
+ 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, "<SID=%s>;",
+ 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, "<SID=%s>;",
+ 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");
+
+ return true;
+}
+
+NTSTATUS torture_ldb_init(void)
+{
+ struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "LDB");
+ 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);
+
+ suite->description = talloc_strdup(suite, "LDB (samba-specific behaviour) tests");
+
+ torture_register_suite(suite);
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/torture/libnet/domain.c b/source4/torture/libnet/domain.c
new file mode 100644
index 0000000000..ab7846e5e1
--- /dev/null
+++ b/source4/torture/libnet/domain.c
@@ -0,0 +1,113 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/rpc/rpc.h"
+#include "lib/events/events.h"
+#include "libnet/libnet.h"
+#include "librpc/gen_ndr/ndr_samr_c.h"
+#include "param/param.h"
+
+static bool test_domainopen(struct libnet_context *net_ctx, TALLOC_CTX *mem_ctx,
+ struct lsa_String *domname,
+ struct policy_handle *domain_handle)
+{
+ NTSTATUS status;
+ struct libnet_DomainOpen io;
+
+ printf("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)) {
+ printf("Composite domain open failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ *domain_handle = io.out.domain_handle;
+ return true;
+}
+
+
+static bool test_cleanup(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *domain_handle)
+{
+ NTSTATUS status;
+ struct samr_Close r;
+ struct policy_handle handle;
+
+ r.in.handle = domain_handle;
+ r.out.handle = &handle;
+
+ printf("closing domain handle\n");
+
+ status = dcerpc_samr_Close(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Close failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ 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 = lp_workgroup(torture->lp_ctx);
+
+ /*
+ * Testing synchronous version
+ */
+ if (!test_domainopen(net_ctx, mem_ctx, &name, &h)) {
+ ret = false;
+ goto done;
+ }
+
+ if (!test_cleanup(net_ctx->samr.pipe, 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 0000000000..4ddb1cefbe
--- /dev/null
+++ b/source4/torture/libnet/groupinfo.c
@@ -0,0 +1,123 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/rpc/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/utils.h"
+
+#define TEST_GROUPNAME "libnetgroupinfotest"
+
+
+static bool test_groupinfo(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);
+
+ group.in.domain_handle = *domain_handle;
+ group.in.sid = dom_sid_string(mem_ctx, group_sid);
+ group.in.level = level; /* this should be extended */
+
+ printf("Testing sync libnet_rpc_groupinfo (SID argument)\n");
+ status = libnet_rpc_groupinfo(p, mem_ctx, &group);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("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(p, mem_ctx, &group);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("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;
+
+ mem_ctx = talloc_init("test_userinfo");
+
+ status = torture_rpc_connection(torture,
+ &p,
+ &ndr_table_samr);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ name.string = lp_workgroup(torture->lp_ctx);
+
+ /*
+ * Testing synchronous version
+ */
+ if (!test_opendomain(torture, p, mem_ctx, &h, &name, &sid)) {
+ ret = false;
+ goto done;
+ }
+
+ if (!test_group_create(p, mem_ctx, &h, TEST_GROUPNAME, &rid)) {
+ ret = false;
+ goto done;
+ }
+
+ if (!test_groupinfo(p, mem_ctx, &h, &sid, TEST_GROUPNAME, &rid)) {
+ ret = false;
+ goto done;
+ }
+
+ if (!test_group_cleanup(p, 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 0000000000..51b1c65b30
--- /dev/null
+++ b/source4/torture/libnet/groupman.c
@@ -0,0 +1,91 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/rpc/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/utils.h"
+
+
+static bool test_groupadd(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;
+
+ group.in.domain_handle = *domain_handle;
+ group.in.groupname = name;
+
+ printf("Testing libnet_rpc_groupadd\n");
+
+ status = libnet_rpc_groupadd(p, 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;
+
+ mem_ctx = talloc_init("test_groupadd");
+
+ status = torture_rpc_connection(torture,
+ &p,
+ &ndr_table_samr);
+
+ torture_assert_ntstatus_ok(torture, status, "RPC connection");
+
+ domain_name.string = lp_workgroup(torture->lp_ctx);
+ if (!test_opendomain(torture, p, mem_ctx, &h, &domain_name, &sid)) {
+ ret = false;
+ goto done;
+ }
+
+ if (!test_groupadd(p, mem_ctx, &h, name)) {
+ ret = false;
+ goto done;
+ }
+
+ if (!test_group_cleanup(p, 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 0000000000..9d030acd17
--- /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 <http://www.gnu.org/licenses/>.
+*/
+
+#define TEST_GROUPNAME "libnetgrptest"
diff --git a/source4/torture/libnet/libnet.c b/source4/torture/libnet/libnet.c
new file mode 100644
index 0000000000..8c8353e8d6
--- /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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/smbtorture.h"
+#include "librpc/rpc/dcerpc.h"
+#include "librpc/gen_ndr/security.h"
+#include "librpc/gen_ndr/lsa.h"
+#include "libnet/composite.h"
+#include "torture/libnet/proto.h"
+
+NTSTATUS torture_net_init(void)
+{
+ struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "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(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 0000000000..2b1bff40ee
--- /dev/null
+++ b/source4/torture/libnet/libnet_BecomeDC.c
@@ -0,0 +1,709 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ libnet_BecomeDC() tests
+
+ Copyright (C) Stefan Metzmacher <metze@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/cmdline/popt_common.h"
+#include "torture/torture.h"
+#include "torture/rpc/rpc.h"
+#include "libnet/libnet.h"
+#include "lib/events/events.h"
+#include "dsdb/samdb/samdb.h"
+#include "../lib/util/dlinklist.h"
+#include "lib/ldb/include/ldb.h"
+#include "lib/ldb/include/ldb_errors.h"
+#include "librpc/ndr/libndr.h"
+#include "librpc/gen_ndr/ndr_drsuapi.h"
+#include "librpc/gen_ndr/ndr_drsblobs.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "system/time.h"
+#include "lib/ldb_wrap.h"
+#include "auth/auth.h"
+#include "param/param.h"
+#include "torture/util.h"
+#include "param/provision.h"
+
+struct test_become_dc_state {
+ struct libnet_context *ctx;
+ struct torture_context *tctx;
+ const char *netbios_name;
+ struct test_join *tj;
+ struct cli_credentials *machine_account;
+ struct dsdb_schema *self_made_schema;
+ const struct dsdb_schema *schema;
+
+ struct ldb_context *ldb;
+
+ struct {
+ uint32_t object_count;
+ struct drsuapi_DsReplicaObjectListItemEx *first_object;
+ struct drsuapi_DsReplicaObjectListItemEx *last_object;
+ } schema_part;
+
+ const char *targetdir;
+
+ struct loadparm_context *lp_ctx;
+};
+
+static NTSTATUS test_become_dc_prepare_db(void *private_data,
+ const struct libnet_BecomeDC_PrepareDB *p)
+{
+ struct test_become_dc_state *s = talloc_get_type(private_data, struct test_become_dc_state);
+ struct provision_settings settings;
+ struct provision_result result;
+ NTSTATUS status;
+
+ settings.site_name = p->dest_dsa->site_name;
+ settings.root_dn_str = p->forest->root_dn_str;
+ settings.domain_dn_str = p->domain->dn_str;
+ settings.config_dn_str = p->forest->config_dn_str;
+ settings.schema_dn_str = p->forest->schema_dn_str;
+ settings.server_dn_str = torture_join_server_dn_str(s->tj);
+ settings.invocation_id = &p->dest_dsa->invocation_id;
+ settings.netbios_name = p->dest_dsa->netbios_name;
+ settings.host_ip = NULL;
+ settings.realm = torture_join_dom_dns_name(s->tj);
+ settings.domain = torture_join_dom_netbios_name(s->tj);
+ settings.ntds_dn_str = p->dest_dsa->ntds_dn_str;
+ settings.machine_password = cli_credentials_get_password(s->machine_account);
+ settings.targetdir = s->targetdir;
+
+ status = provision_bare(s, s->lp_ctx, &settings, &result);
+
+ s->ldb = result.samdb;
+ s->lp_ctx = result.lp_ctx;
+ return NT_STATUS_OK;
+
+
+}
+
+static NTSTATUS test_become_dc_check_options(void *private_data,
+ const struct libnet_BecomeDC_CheckOptions *o)
+{
+ struct test_become_dc_state *s = talloc_get_type(private_data, struct test_become_dc_state);
+
+ DEBUG(0,("Become DC [%s] of Domain[%s]/[%s]\n",
+ s->netbios_name,
+ o->domain->netbios_name, o->domain->dns_name));
+
+ DEBUG(0,("Promotion Partner is Server[%s] from Site[%s]\n",
+ o->source_dsa->dns_name, o->source_dsa->site_name));
+
+ DEBUG(0,("Options:crossRef behavior_version[%u]\n"
+ "\tschema object_version[%u]\n"
+ "\tdomain behavior_version[%u]\n"
+ "\tdomain w2k3_update_revision[%u]\n",
+ o->forest->crossref_behavior_version,
+ o->forest->schema_object_version,
+ o->domain->behavior_version,
+ o->domain->w2k3_update_revision));
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS test_apply_schema(struct test_become_dc_state *s,
+ const struct libnet_BecomeDC_StoreChunk *c)
+{
+ WERROR status;
+ const struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr;
+ uint32_t object_count;
+ struct drsuapi_DsReplicaObjectListItemEx *first_object;
+ struct drsuapi_DsReplicaObjectListItemEx *cur;
+ uint32_t linked_attributes_count;
+ struct drsuapi_DsReplicaLinkedAttribute *linked_attributes;
+ const struct drsuapi_DsReplicaCursor2CtrEx *uptodateness_vector;
+ struct dsdb_extended_replicated_objects *objs;
+ struct repsFromTo1 *s_dsa;
+ char *tmp_dns_name;
+ struct ldb_message *msg;
+ struct ldb_val prefixMap_val;
+ struct ldb_message_element *prefixMap_el;
+ struct ldb_val schemaInfo_val;
+ char *sam_ldb_path;
+ uint32_t i;
+ int ret;
+ bool ok;
+
+ DEBUG(0,("Analyze and apply schema objects\n"));
+
+ s_dsa = talloc_zero(s, struct repsFromTo1);
+ NT_STATUS_HAVE_NO_MEMORY(s_dsa);
+ s_dsa->other_info = talloc(s_dsa, struct repsFromTo1OtherInfo);
+ NT_STATUS_HAVE_NO_MEMORY(s_dsa->other_info);
+
+ switch (c->ctr_level) {
+ case 1:
+ mapping_ctr = &c->ctr1->mapping_ctr;
+ object_count = s->schema_part.object_count;
+ first_object = s->schema_part.first_object;
+ linked_attributes_count = 0;
+ linked_attributes = NULL;
+ s_dsa->highwatermark = c->ctr1->new_highwatermark;
+ s_dsa->source_dsa_obj_guid = c->ctr1->source_dsa_guid;
+ s_dsa->source_dsa_invocation_id = c->ctr1->source_dsa_invocation_id;
+ uptodateness_vector = NULL; /* TODO: map it */
+ break;
+ case 6:
+ mapping_ctr = &c->ctr6->mapping_ctr;
+ object_count = s->schema_part.object_count;
+ first_object = s->schema_part.first_object;
+ linked_attributes_count = 0; /* TODO: ! */
+ linked_attributes = NULL; /* TODO: ! */;
+ s_dsa->highwatermark = c->ctr6->new_highwatermark;
+ s_dsa->source_dsa_obj_guid = c->ctr6->source_dsa_guid;
+ s_dsa->source_dsa_invocation_id = c->ctr6->source_dsa_invocation_id;
+ uptodateness_vector = c->ctr6->uptodateness_vector;
+ break;
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ s_dsa->replica_flags = DRSUAPI_DS_REPLICA_NEIGHBOUR_WRITEABLE
+ | DRSUAPI_DS_REPLICA_NEIGHBOUR_SYNC_ON_STARTUP
+ | DRSUAPI_DS_REPLICA_NEIGHBOUR_DO_SCHEDULED_SYNCS;
+ memset(s_dsa->schedule, 0x11, sizeof(s_dsa->schedule));
+
+ tmp_dns_name = GUID_string(s_dsa->other_info, &s_dsa->source_dsa_obj_guid);
+ NT_STATUS_HAVE_NO_MEMORY(tmp_dns_name);
+ tmp_dns_name = talloc_asprintf_append_buffer(tmp_dns_name, "._msdcs.%s", c->forest->dns_name);
+ NT_STATUS_HAVE_NO_MEMORY(tmp_dns_name);
+ s_dsa->other_info->dns_name = tmp_dns_name;
+
+ for (cur = first_object; cur; cur = cur->next_object) {
+ bool is_attr = false;
+ bool is_class = false;
+
+ for (i=0; i < cur->object.attribute_ctr.num_attributes; i++) {
+ struct drsuapi_DsReplicaAttribute *a;
+ uint32_t j;
+ const char *oid = NULL;
+
+ a = &cur->object.attribute_ctr.attributes[i];
+ status = dsdb_map_int2oid(s->self_made_schema, a->attid, s, &oid);
+ if (!W_ERROR_IS_OK(status)) {
+ return werror_to_ntstatus(status);
+ }
+
+ switch (a->attid) {
+ case DRSUAPI_ATTRIBUTE_objectClass:
+ for (j=0; j < a->value_ctr.num_values; j++) {
+ uint32_t val = 0xFFFFFFFF;
+
+ if (a->value_ctr.values[i].blob
+ && a->value_ctr.values[i].blob->length == 4) {
+ val = IVAL(a->value_ctr.values[i].blob->data,0);
+ }
+
+ if (val == DRSUAPI_OBJECTCLASS_attributeSchema) {
+ is_attr = true;
+ }
+ if (val == DRSUAPI_OBJECTCLASS_classSchema) {
+ is_class = true;
+ }
+ }
+
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (is_attr) {
+ struct dsdb_attribute *sa;
+
+ sa = talloc_zero(s->self_made_schema, struct dsdb_attribute);
+ NT_STATUS_HAVE_NO_MEMORY(sa);
+
+ status = dsdb_attribute_from_drsuapi(s->self_made_schema, &cur->object, s, sa);
+ if (!W_ERROR_IS_OK(status)) {
+ return werror_to_ntstatus(status);
+ }
+
+ DLIST_ADD_END(s->self_made_schema->attributes, sa, struct dsdb_attribute *);
+ }
+
+ if (is_class) {
+ struct dsdb_class *sc;
+
+ sc = talloc_zero(s->self_made_schema, struct dsdb_class);
+ NT_STATUS_HAVE_NO_MEMORY(sc);
+
+ status = dsdb_class_from_drsuapi(s->self_made_schema, &cur->object, s, sc);
+ if (!W_ERROR_IS_OK(status)) {
+ return werror_to_ntstatus(status);
+ }
+
+ DLIST_ADD_END(s->self_made_schema->classes, sc, struct dsdb_class *);
+ }
+ }
+
+ /* attach the schema to the ldb */
+ ret = dsdb_set_schema(s->ldb, s->self_made_schema);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_FOOBAR;
+ }
+ /* we don't want to access the self made schema anymore */
+ s->self_made_schema = NULL;
+ s->schema = dsdb_get_schema(s->ldb);
+
+ status = dsdb_extended_replicated_objects_commit(s->ldb,
+ c->partition->nc.dn,
+ mapping_ctr,
+ object_count,
+ first_object,
+ linked_attributes_count,
+ linked_attributes,
+ s_dsa,
+ uptodateness_vector,
+ c->gensec_skey,
+ s, &objs);
+ if (!W_ERROR_IS_OK(status)) {
+ DEBUG(0,("Failed to commit objects: %s\n", win_errstr(status)));
+ return werror_to_ntstatus(status);
+ }
+
+ if (lp_parm_bool(s->tctx->lp_ctx, NULL, "become dc", "dump objects", false)) {
+ for (i=0; i < objs->num_objects; i++) {
+ struct ldb_ldif ldif;
+ fprintf(stdout, "#\n");
+ ldif.changetype = LDB_CHANGETYPE_NONE;
+ ldif.msg = objs->objects[i].msg;
+ ldb_ldif_write_file(s->ldb, stdout, &ldif);
+ NDR_PRINT_DEBUG(replPropertyMetaDataBlob, objs->objects[i].meta_data);
+ }
+ }
+
+ msg = ldb_msg_new(objs);
+ NT_STATUS_HAVE_NO_MEMORY(msg);
+ msg->dn = objs->partition_dn;
+
+ status = dsdb_get_oid_mappings_ldb(s->schema, msg, &prefixMap_val, &schemaInfo_val);
+ if (!W_ERROR_IS_OK(status)) {
+ DEBUG(0,("Failed dsdb_get_oid_mappings_ldb(%s)\n", win_errstr(status)));
+ return werror_to_ntstatus(status);
+ }
+
+ /* we only add prefixMap here, because schemaInfo is a replicated attribute and already applied */
+ ret = ldb_msg_add_value(msg, "prefixMap", &prefixMap_val, &prefixMap_el);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_FOOBAR;
+ }
+ prefixMap_el->flags = LDB_FLAG_MOD_REPLACE;
+
+ ret = ldb_modify(s->ldb, msg);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0,("Failed to add prefixMap and schemaInfo %s\n", ldb_strerror(ret)));
+ return NT_STATUS_FOOBAR;
+ }
+
+ talloc_free(s_dsa);
+ talloc_free(objs);
+
+ /* reopen the ldb */
+ talloc_free(s->ldb); /* this also free's the s->schema, because dsdb_set_schema() steals it */
+ s->schema = NULL;
+
+ sam_ldb_path = talloc_asprintf(s, "%s/%s", s->targetdir, "private/sam.ldb");
+ DEBUG(0,("Reopen the SAM LDB with system credentials and a already stored schema: %s\n", sam_ldb_path));
+ s->ldb = ldb_wrap_connect(s, s->tctx->ev, s->tctx->lp_ctx, sam_ldb_path,
+ system_session(s, s->tctx->lp_ctx),
+ NULL, 0, NULL);
+ if (!s->ldb) {
+ DEBUG(0,("Failed to open '%s'\n",
+ sam_ldb_path));
+ return NT_STATUS_INTERNAL_DB_ERROR;
+ }
+
+ ok = samdb_set_ntds_invocation_id(s->ldb, &c->dest_dsa->invocation_id);
+ if (!ok) {
+ DEBUG(0,("Failed to set cached ntds invocationId\n"));
+ return NT_STATUS_FOOBAR;
+ }
+ ok = samdb_set_ntds_objectGUID(s->ldb, &c->dest_dsa->ntds_guid);
+ if (!ok) {
+ DEBUG(0,("Failed to set cached ntds objectGUID\n"));
+ return NT_STATUS_FOOBAR;
+ }
+
+ s->schema = dsdb_get_schema(s->ldb);
+ if (!s->schema) {
+ DEBUG(0,("Failed to get loaded dsdb_schema\n"));
+ return NT_STATUS_FOOBAR;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS test_become_dc_schema_chunk(void *private_data,
+ const struct libnet_BecomeDC_StoreChunk *c)
+{
+ struct test_become_dc_state *s = talloc_get_type(private_data, struct test_become_dc_state);
+ WERROR status;
+ const struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr;
+ uint32_t nc_object_count;
+ uint32_t object_count;
+ struct drsuapi_DsReplicaObjectListItemEx *first_object;
+ struct drsuapi_DsReplicaObjectListItemEx *cur;
+ uint32_t nc_linked_attributes_count;
+ uint32_t linked_attributes_count;
+
+ switch (c->ctr_level) {
+ case 1:
+ mapping_ctr = &c->ctr1->mapping_ctr;
+ nc_object_count = c->ctr1->extended_ret; /* maybe w2k send this unexpected? */
+ object_count = c->ctr1->object_count;
+ first_object = c->ctr1->first_object;
+ nc_linked_attributes_count = 0;
+ linked_attributes_count = 0;
+ break;
+ case 6:
+ mapping_ctr = &c->ctr6->mapping_ctr;
+ nc_object_count = c->ctr6->nc_object_count;
+ object_count = c->ctr6->object_count;
+ first_object = c->ctr6->first_object;
+ nc_linked_attributes_count = c->ctr6->nc_linked_attributes_count;
+ linked_attributes_count = c->ctr6->linked_attributes_count;
+ break;
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (nc_object_count) {
+ DEBUG(0,("Schema-DN[%s] objects[%u/%u] linked_values[%u/%u]\n",
+ c->partition->nc.dn, object_count, nc_object_count,
+ linked_attributes_count, nc_linked_attributes_count));
+ } else {
+ DEBUG(0,("Schema-DN[%s] objects[%u] linked_values[%u\n",
+ c->partition->nc.dn, object_count, linked_attributes_count));
+ }
+
+ if (!s->schema) {
+ s->self_made_schema = dsdb_new_schema(s, lp_iconv_convenience(s->lp_ctx));
+
+ NT_STATUS_HAVE_NO_MEMORY(s->self_made_schema);
+
+ status = dsdb_load_oid_mappings_drsuapi(s->self_made_schema, mapping_ctr);
+ if (!W_ERROR_IS_OK(status)) {
+ return werror_to_ntstatus(status);
+ }
+
+ s->schema = s->self_made_schema;
+ } else {
+ status = dsdb_verify_oid_mappings_drsuapi(s->schema, mapping_ctr);
+ if (!W_ERROR_IS_OK(status)) {
+ return werror_to_ntstatus(status);
+ }
+ }
+
+ if (!s->schema_part.first_object) {
+ s->schema_part.object_count = object_count;
+ s->schema_part.first_object = talloc_steal(s, first_object);
+ } else {
+ s->schema_part.object_count += object_count;
+ s->schema_part.last_object->next_object = talloc_steal(s->schema_part.last_object,
+ first_object);
+ }
+ for (cur = first_object; cur->next_object; cur = cur->next_object) {}
+ s->schema_part.last_object = cur;
+
+ if (!c->partition->more_data) {
+ return test_apply_schema(s, c);
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS test_become_dc_store_chunk(void *private_data,
+ const struct libnet_BecomeDC_StoreChunk *c)
+{
+ struct test_become_dc_state *s = talloc_get_type(private_data, struct test_become_dc_state);
+ WERROR status;
+ const struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr;
+ uint32_t nc_object_count;
+ uint32_t object_count;
+ struct drsuapi_DsReplicaObjectListItemEx *first_object;
+ uint32_t nc_linked_attributes_count;
+ uint32_t linked_attributes_count;
+ struct drsuapi_DsReplicaLinkedAttribute *linked_attributes;
+ const struct drsuapi_DsReplicaCursor2CtrEx *uptodateness_vector;
+ struct dsdb_extended_replicated_objects *objs;
+ struct repsFromTo1 *s_dsa;
+ char *tmp_dns_name;
+ uint32_t i;
+
+ s_dsa = talloc_zero(s, struct repsFromTo1);
+ NT_STATUS_HAVE_NO_MEMORY(s_dsa);
+ s_dsa->other_info = talloc(s_dsa, struct repsFromTo1OtherInfo);
+ NT_STATUS_HAVE_NO_MEMORY(s_dsa->other_info);
+
+ switch (c->ctr_level) {
+ case 1:
+ mapping_ctr = &c->ctr1->mapping_ctr;
+ nc_object_count = c->ctr1->extended_ret; /* maybe w2k send this unexpected? */
+ object_count = c->ctr1->object_count;
+ first_object = c->ctr1->first_object;
+ nc_linked_attributes_count = 0;
+ linked_attributes_count = 0;
+ linked_attributes = NULL;
+ s_dsa->highwatermark = c->ctr1->new_highwatermark;
+ s_dsa->source_dsa_obj_guid = c->ctr1->source_dsa_guid;
+ s_dsa->source_dsa_invocation_id = c->ctr1->source_dsa_invocation_id;
+ uptodateness_vector = NULL; /* TODO: map it */
+ break;
+ case 6:
+ mapping_ctr = &c->ctr6->mapping_ctr;
+ nc_object_count = c->ctr6->nc_object_count;
+ object_count = c->ctr6->object_count;
+ first_object = c->ctr6->first_object;
+ nc_linked_attributes_count = c->ctr6->nc_linked_attributes_count;
+ linked_attributes_count = c->ctr6->linked_attributes_count;
+ linked_attributes = c->ctr6->linked_attributes;
+ s_dsa->highwatermark = c->ctr6->new_highwatermark;
+ s_dsa->source_dsa_obj_guid = c->ctr6->source_dsa_guid;
+ s_dsa->source_dsa_invocation_id = c->ctr6->source_dsa_invocation_id;
+ uptodateness_vector = c->ctr6->uptodateness_vector;
+ break;
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ s_dsa->replica_flags = DRSUAPI_DS_REPLICA_NEIGHBOUR_WRITEABLE
+ | DRSUAPI_DS_REPLICA_NEIGHBOUR_SYNC_ON_STARTUP
+ | DRSUAPI_DS_REPLICA_NEIGHBOUR_DO_SCHEDULED_SYNCS;
+ memset(s_dsa->schedule, 0x11, sizeof(s_dsa->schedule));
+
+ tmp_dns_name = GUID_string(s_dsa->other_info, &s_dsa->source_dsa_obj_guid);
+ NT_STATUS_HAVE_NO_MEMORY(tmp_dns_name);
+ tmp_dns_name = talloc_asprintf_append_buffer(tmp_dns_name, "._msdcs.%s", c->forest->dns_name);
+ NT_STATUS_HAVE_NO_MEMORY(tmp_dns_name);
+ s_dsa->other_info->dns_name = tmp_dns_name;
+
+ if (nc_object_count) {
+ DEBUG(0,("Partition[%s] objects[%u/%u] linked_values[%u/%u]\n",
+ c->partition->nc.dn, object_count, nc_object_count,
+ linked_attributes_count, nc_linked_attributes_count));
+ } else {
+ DEBUG(0,("Partition[%s] objects[%u] linked_values[%u\n",
+ c->partition->nc.dn, object_count, linked_attributes_count));
+ }
+
+ status = dsdb_extended_replicated_objects_commit(s->ldb,
+ c->partition->nc.dn,
+ mapping_ctr,
+ object_count,
+ first_object,
+ linked_attributes_count,
+ linked_attributes,
+ s_dsa,
+ uptodateness_vector,
+ c->gensec_skey,
+ s, &objs);
+ if (!W_ERROR_IS_OK(status)) {
+ DEBUG(0,("Failed to commit objects: %s\n", win_errstr(status)));
+ return werror_to_ntstatus(status);
+ }
+
+ if (lp_parm_bool(s->tctx->lp_ctx, NULL, "become dc", "dump objects", false)) {
+ for (i=0; i < objs->num_objects; i++) {
+ struct ldb_ldif ldif;
+ fprintf(stdout, "#\n");
+ ldif.changetype = LDB_CHANGETYPE_NONE;
+ ldif.msg = objs->objects[i].msg;
+ ldb_ldif_write_file(s->ldb, stdout, &ldif);
+ NDR_PRINT_DEBUG(replPropertyMetaDataBlob, objs->objects[i].meta_data);
+ }
+ }
+ talloc_free(s_dsa);
+ talloc_free(objs);
+
+ for (i=0; i < linked_attributes_count; i++) {
+ const struct dsdb_attribute *sa;
+
+ if (!linked_attributes[i].identifier) {
+ return NT_STATUS_FOOBAR;
+ }
+
+ if (!linked_attributes[i].value.blob) {
+ return NT_STATUS_FOOBAR;
+ }
+
+ sa = dsdb_attribute_by_attributeID_id(s->schema,
+ linked_attributes[i].attid);
+ if (!sa) {
+ return NT_STATUS_FOOBAR;
+ }
+
+ if (lp_parm_bool(s->tctx->lp_ctx, NULL, "become dc", "dump objects", false)) {
+ DEBUG(0,("# %s\n", sa->lDAPDisplayName));
+ NDR_PRINT_DEBUG(drsuapi_DsReplicaLinkedAttribute, &linked_attributes[i]);
+ dump_data(0,
+ linked_attributes[i].value.blob->data,
+ linked_attributes[i].value.blob->length);
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+bool torture_net_become_dc(struct torture_context *torture)
+{
+ bool ret = true;
+ NTSTATUS status;
+ struct libnet_BecomeDC b;
+ struct libnet_UnbecomeDC u;
+ struct test_become_dc_state *s;
+ struct ldb_message *msg;
+ int ldb_ret;
+ uint32_t i;
+ char *sam_ldb_path;
+
+ char *location = NULL;
+ torture_assert_ntstatus_ok(torture, torture_temp_dir(torture, "libnet_BecomeDC", &location),
+ "torture_temp_dir should return NT_STATUS_OK" );
+
+ s = talloc_zero(torture, struct test_become_dc_state);
+ if (!s) return false;
+
+ s->tctx = torture;
+ s->lp_ctx = torture->lp_ctx;
+
+ s->netbios_name = lp_parm_string(torture->lp_ctx, NULL, "become dc", "smbtorture dc");
+ if (!s->netbios_name || !s->netbios_name[0]) {
+ s->netbios_name = "smbtorturedc";
+ }
+
+ s->targetdir = location;
+
+ /* Join domain as a member server. */
+ s->tj = torture_join_domain(torture, s->netbios_name,
+ ACB_WSTRUST,
+ &s->machine_account);
+ if (!s->tj) {
+ DEBUG(0, ("%s failed to join domain as workstation\n",
+ s->netbios_name));
+ return false;
+ }
+
+ s->ctx = libnet_context_init(torture->ev, torture->lp_ctx);
+ s->ctx->cred = cmdline_credentials;
+
+ s->ldb = ldb_init(s, torture->ev);
+
+ ZERO_STRUCT(b);
+ b.in.domain_dns_name = torture_join_dom_dns_name(s->tj);
+ b.in.domain_netbios_name = torture_join_dom_netbios_name(s->tj);
+ b.in.domain_sid = torture_join_sid(s->tj);
+ b.in.source_dsa_address = torture_setting_string(torture, "host", NULL);
+ b.in.dest_dsa_netbios_name = s->netbios_name;
+
+ b.in.callbacks.private_data = s;
+ b.in.callbacks.check_options = test_become_dc_check_options;
+ b.in.callbacks.prepare_db = test_become_dc_prepare_db;
+ b.in.callbacks.schema_chunk = test_become_dc_schema_chunk;
+ b.in.callbacks.config_chunk = test_become_dc_store_chunk;
+ b.in.callbacks.domain_chunk = test_become_dc_store_chunk;
+
+ status = libnet_BecomeDC(s->ctx, s, &b);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("libnet_BecomeDC() failed - %s\n", nt_errstr(status));
+ ret = false;
+ goto cleanup;
+ }
+
+ msg = ldb_msg_new(s);
+ if (!msg) {
+ printf("ldb_msg_new() failed\n");
+ ret = false;
+ goto cleanup;
+ }
+ msg->dn = ldb_dn_new(msg, s->ldb, "@ROOTDSE");
+ if (!msg->dn) {
+ printf("ldb_msg_new(@ROOTDSE) failed\n");
+ ret = false;
+ goto cleanup;
+ }
+
+ ldb_ret = ldb_msg_add_string(msg, "isSynchronized", "TRUE");
+ if (ldb_ret != LDB_SUCCESS) {
+ printf("ldb_msg_add_string(msg, isSynchronized, TRUE) failed: %d\n", ldb_ret);
+ ret = false;
+ goto cleanup;
+ }
+
+ for (i=0; i < msg->num_elements; i++) {
+ msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
+ }
+
+ printf("mark ROOTDSE with isSynchronized=TRUE\n");
+ ldb_ret = ldb_modify(s->ldb, msg);
+ if (ldb_ret != LDB_SUCCESS) {
+ printf("ldb_modify() failed: %d\n", ldb_ret);
+ ret = false;
+ goto cleanup;
+ }
+
+ /* reopen the ldb */
+ talloc_free(s->ldb); /* this also free's the s->schema, because dsdb_set_schema() steals it */
+ s->schema = NULL;
+
+ sam_ldb_path = talloc_asprintf(s, "%s/%s", s->targetdir, "private/sam.ldb");
+ DEBUG(0,("Reopen the SAM LDB with system credentials and all replicated data: %s\n", sam_ldb_path));
+ s->ldb = ldb_wrap_connect(s, s->tctx->ev, s->lp_ctx, sam_ldb_path,
+ system_session(s, s->lp_ctx),
+ NULL, 0, NULL);
+ if (!s->ldb) {
+ DEBUG(0,("Failed to open '%s'\n",
+ sam_ldb_path));
+ ret = false;
+ goto cleanup;
+ }
+
+ s->schema = dsdb_get_schema(s->ldb);
+ if (!s->schema) {
+ DEBUG(0,("Failed to get loaded dsdb_schema\n"));
+ ret = false;
+ goto cleanup;
+ }
+
+ /* Make sure we get this from the command line */
+ if (lp_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(s->tj);
+ u.in.domain_netbios_name = torture_join_dom_netbios_name(s->tj);
+ u.in.source_dsa_address = torture_setting_string(torture, "host", NULL);
+ u.in.dest_dsa_netbios_name = s->netbios_name;
+
+ status = libnet_UnbecomeDC(s->ctx, s, &u);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("libnet_UnbecomeDC() failed - %s\n", nt_errstr(status));
+ ret = false;
+ }
+
+ /* Leave domain. */
+ torture_leave_domain(torture, s->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 0000000000..3c28d1a019
--- /dev/null
+++ b/source4/torture/libnet/libnet_domain.c
@@ -0,0 +1,449 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+
+#include "includes.h"
+#include "lib/cmdline/popt_common.h"
+#include "lib/events/events.h"
+#include "auth/credentials/credentials.h"
+#include "libnet/libnet.h"
+#include "librpc/gen_ndr/ndr_samr_c.h"
+#include "librpc/gen_ndr/ndr_lsa_c.h"
+#include "libcli/security/security.h"
+#include "librpc/rpc/dcerpc.h"
+#include "torture/torture.h"
+#include "torture/rpc/rpc.h"
+#include "param/param.h"
+
+
+static bool test_opendomain_samr(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle, struct lsa_String *domname,
+ uint32_t *access_mask, struct dom_sid **sid_p)
+{
+ NTSTATUS status;
+ struct policy_handle h, domain_handle;
+ struct samr_Connect r1;
+ struct samr_LookupDomain r2;
+ struct dom_sid2 *sid = NULL;
+ struct samr_OpenDomain r3;
+
+ printf("connecting\n");
+
+ *access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+
+ r1.in.system_name = 0;
+ r1.in.access_mask = *access_mask;
+ r1.out.connect_handle = &h;
+
+ status = dcerpc_samr_Connect(p, mem_ctx, &r1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Connect failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ r2.in.connect_handle = &h;
+ r2.in.domain_name = domname;
+ r2.out.sid = &sid;
+
+ printf("domain lookup on %s\n", domname->string);
+
+ status = dcerpc_samr_LookupDomain(p, mem_ctx, &r2);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("LookupDomain failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ 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;
+
+ printf("opening domain\n");
+
+ status = dcerpc_samr_OpenDomain(p, mem_ctx, &r3);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("OpenDomain failed - %s\n", nt_errstr(status));
+ return false;
+ } else {
+ *handle = domain_handle;
+ }
+
+ return true;
+}
+
+
+static bool test_opendomain_lsa(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle, struct lsa_String *domname,
+ uint32_t *access_mask)
+{
+ NTSTATUS status;
+ 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;
+
+ status = dcerpc_lsa_OpenPolicy2(p, mem_ctx, &open);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ 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 = lp_workgroup(torture->lp_ctx);
+
+ ctx = libnet_context_init(torture->ev, torture->lp_ctx);
+ if (ctx == NULL) {
+ d_printf("failed to create libnet context\n");
+ return false;
+ }
+
+ ctx->cred = cmdline_credentials;
+
+ 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)) {
+ d_printf("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;
+
+ status = dcerpc_lsa_Close(ctx->lsa.pipe, ctx, &lsa_close);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("failed to close domain on lsa service: %s\n", nt_errstr(status));
+ ret = false;
+ }
+
+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) {
+ d_printf("failed to create libnet context\n");
+ ret = false;
+ goto done;
+ }
+
+ ctx->cred = cmdline_credentials;
+
+ mem_ctx = talloc_init("torture_domain_close_lsa");
+ status = dcerpc_pipe_connect_b(mem_ctx, &p, binding, &ndr_table_lsarpc,
+ cmdline_credentials, torture->ev, torture->lp_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("failed to connect to server: %s\n", nt_errstr(status));
+ ret = false;
+ goto done;
+ }
+
+ domain_name.string = lp_workgroup(torture->lp_ctx);
+
+ if (!test_opendomain_lsa(p, torture, &h, &domain_name, &access_mask)) {
+ d_printf("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;
+ /* we have to use pipe's event context, otherwise the call will
+ hang indefinitely */
+ ctx->event_ctx = p->conn->event_ctx;
+
+ 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 = cmdline_credentials;
+
+ /* 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 = lp_workgroup(torture->lp_ctx);
+
+ /*
+ * Testing synchronous version
+ */
+ printf("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)) {
+ printf("Composite domain open failed - %s\n", nt_errstr(status));
+ ret = false;
+ goto done;
+ }
+
+ domain_handle = ctx->samr.handle;
+
+ r.in.handle = &domain_handle;
+ r.out.handle = &handle;
+
+ printf("closing domain handle\n");
+
+ status = dcerpc_samr_Close(ctx->samr.pipe, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Close failed - %s\n", nt_errstr(status));
+ ret = false;
+ goto done;
+ }
+
+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) {
+ d_printf("failed to create libnet context\n");
+ ret = false;
+ goto done;
+ }
+
+ ctx->cred = cmdline_credentials;
+
+ 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)) {
+ d_printf("failed to connect to server: %s\n", nt_errstr(status));
+ ret = false;
+ goto done;
+ }
+
+ domain_name.string = talloc_strdup(mem_ctx, lp_workgroup(torture->lp_ctx));
+
+ if (!test_opendomain_samr(p, torture, &h, &domain_name, &access_mask, &sid)) {
+ d_printf("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);
+ /* we have to use pipe's event context, otherwise the call will
+ hang indefinitely - this wouldn't be the case if pipe was opened
+ by means of libnet call */
+ ctx->event_ctx = p->conn->event_ctx;
+
+ 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) {
+ d_printf("failed to create libnet context\n");
+ ret = false;
+ goto done;
+ }
+
+ ctx->cred = cmdline_credentials;
+
+ mem_ctx = talloc_init("torture_domain_close_samr");
+
+ /*
+ * querying the domain list using default buffer size
+ */
+
+ ZERO_STRUCT(r);
+ r.in.hostname = binding->host;
+
+ status = libnet_DomainList(ctx, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ ret = false;
+ goto done;
+ }
+
+ d_printf("Received list or domains (everything in one piece):\n");
+
+ for (i = 0; i < r.out.count; i++) {
+ d_printf("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 = binding->host;
+
+ status = libnet_DomainList(ctx, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ ret = false;
+ goto done;
+ }
+
+ d_printf("Received list or domains (collected in more than one round):\n");
+
+ for (i = 0; i < r.out.count; i++) {
+ d_printf("Name[%d]: %s\n", i, r.out.domains[i].name);
+ }
+
+done:
+ d_printf("\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 0000000000..c7fdfbd10b
--- /dev/null
+++ b/source4/torture/libnet/libnet_group.c
@@ -0,0 +1,399 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+
+#include "includes.h"
+#include "lib/cmdline/popt_common.h"
+#include "libnet/libnet.h"
+#include "librpc/gen_ndr/ndr_samr_c.h"
+#include "librpc/gen_ndr/ndr_lsa_c.h"
+#include "torture/torture.h"
+#include "torture/rpc/rpc.h"
+#include "param/param.h"
+
+
+#define TEST_GROUPNAME "libnetgrouptest"
+
+
+static bool test_cleanup(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *domain_handle, const char *groupname)
+{
+ NTSTATUS status;
+ 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 = groupname;
+
+ r1.in.domain_handle = domain_handle;
+ r1.in.num_names = 1;
+ r1.in.names = names;
+ r1.out.rids = &rids;
+ r1.out.types = &types;
+
+ printf("group account lookup '%s'\n", groupname);
+
+ status = dcerpc_samr_LookupNames(p, mem_ctx, &r1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("LookupNames failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ 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;
+
+ printf("opening group account\n");
+
+ status = dcerpc_samr_OpenGroup(p, mem_ctx, &r2);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("OpenGroup failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ r3.in.group_handle = &group_handle;
+ r3.out.group_handle = &group_handle;
+
+ printf("deleting group account\n");
+
+ status = dcerpc_samr_DeleteDomainGroup(p, mem_ctx, &r3);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("DeleteGroup failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+
+static bool test_creategroup(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle, const char *name)
+{
+ NTSTATUS status;
+ struct lsa_String groupname;
+ struct samr_CreateDomainGroup r;
+ struct policy_handle group_handle;
+ uint32_t group_rid;
+
+ groupname.string = name;
+
+ r.in.domain_handle = handle;
+ r.in.name = &groupname;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.group_handle = &group_handle;
+ r.out.rid = &group_rid;
+
+ printf("creating group account %s\n", name);
+
+ status = dcerpc_samr_CreateDomainGroup(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("CreateGroup failed - %s\n", nt_errstr(status));
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_GROUP_EXISTS)) {
+ printf("Group (%s) already exists - attempting to delete and recreate group again\n", name);
+ if (!test_cleanup(p, mem_ctx, handle, TEST_GROUPNAME)) {
+ return false;
+ }
+
+ printf("creating group account\n");
+
+ status = dcerpc_samr_CreateDomainGroup(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("CreateGroup failed - %s\n", nt_errstr(status));
+ return false;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ return true;
+}
+
+
+static bool test_opendomain(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle, struct lsa_String *domname)
+{
+ NTSTATUS status;
+ struct policy_handle h, domain_handle;
+ struct samr_Connect r1;
+ struct samr_LookupDomain r2;
+ struct dom_sid2 *sid = NULL;
+ struct samr_OpenDomain r3;
+
+ printf("connecting\n");
+
+ r1.in.system_name = 0;
+ r1.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r1.out.connect_handle = &h;
+
+ status = dcerpc_samr_Connect(p, mem_ctx, &r1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Connect failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ r2.in.connect_handle = &h;
+ r2.in.domain_name = domname;
+ r2.out.sid = &sid;
+
+ printf("domain lookup on %s\n", domname->string);
+
+ status = dcerpc_samr_LookupDomain(p, mem_ctx, &r2);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("LookupDomain failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ r3.in.connect_handle = &h;
+ r3.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r3.in.sid = *r2.out.sid;
+ r3.out.domain_handle = &domain_handle;
+
+ printf("opening domain\n");
+
+ status = dcerpc_samr_OpenDomain(p, mem_ctx, &r3);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("OpenDomain failed - %s\n", nt_errstr(status));
+ return false;
+ } else {
+ *handle = domain_handle;
+ }
+
+ return true;
+}
+
+
+static bool test_samr_close(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *domain_handle)
+{
+ NTSTATUS status;
+ struct samr_Close r;
+
+ r.in.handle = domain_handle;
+ r.out.handle = domain_handle;
+
+ status = dcerpc_samr_Close(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Close samr domain failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+
+static bool test_lsa_close(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *domain_handle)
+{
+ NTSTATUS status;
+ struct lsa_Close r;
+
+ r.in.handle = domain_handle;
+ r.out.handle = domain_handle;
+
+ status = dcerpc_lsa_Close(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Close lsa domain failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+
+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;
+ 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");
+
+ ctx = libnet_context_init(torture->ev, torture->lp_ctx);
+ ctx->cred = cmdline_credentials;
+
+ status = torture_rpc_connection(torture,
+ &p,
+ &ndr_table_samr);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ domain_name.string = lp_workgroup(torture->lp_ctx);
+ if (!test_opendomain(p, prep_mem_ctx, &h, &domain_name)) {
+ ret = false;
+ goto done;
+ }
+
+ if (!test_creategroup(p, prep_mem_ctx, &h, name)) {
+ ret = false;
+ goto done;
+ }
+
+ mem_ctx = talloc_init("torture group info");
+
+ 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)) {
+ printf("libnet_GroupInfo call failed: %s\n", nt_errstr(status));
+ ret = false;
+ talloc_free(mem_ctx);
+ goto done;
+ }
+
+ if (!test_cleanup(ctx->samr.pipe, mem_ctx, &ctx->samr.handle, TEST_GROUPNAME)) {
+ printf("cleanup failed\n");
+ ret = false;
+ goto done;
+ }
+
+ if (!test_samr_close(ctx->samr.pipe, mem_ctx, &ctx->samr.handle)) {
+ printf("domain close failed\n");
+ ret = false;
+ }
+
+ talloc_free(ctx);
+
+done:
+ 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 = cmdline_credentials;
+
+ domain_name.string = lp_workgroup(torture->lp_ctx);
+ mem_ctx = talloc_init("torture group list");
+
+ ZERO_STRUCT(req);
+
+ printf("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++) {
+ printf("\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))) {
+ printf("libnet_GroupList call failed: %s\n", nt_errstr(status));
+ ret = false;
+ goto done;
+ }
+
+ if (!test_samr_close(ctx->samr.pipe, mem_ctx, &ctx->samr.handle)) {
+ printf("domain close failed\n");
+ ret = false;
+ }
+
+ if (!test_lsa_close(ctx->lsa.pipe, mem_ctx, &ctx->lsa.handle)) {
+ printf("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 = cmdline_credentials;
+
+ req.in.group_name = TEST_GROUPNAME;
+ req.in.domain_name = lp_workgroup(torture->lp_ctx);
+ req.out.error_string = NULL;
+
+ status = libnet_CreateGroup(ctx, mem_ctx, &req);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("libnet_CreateGroup call failed: %s\n", nt_errstr(status));
+ ret = false;
+ goto done;
+ }
+
+ if (!test_cleanup(ctx->samr.pipe, mem_ctx, &ctx->samr.handle, TEST_GROUPNAME)) {
+ printf("cleanup failed\n");
+ ret = false;
+ goto done;
+ }
+
+ if (!test_samr_close(ctx->samr.pipe, mem_ctx, &ctx->samr.handle)) {
+ printf("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 0000000000..b25b51b7d9
--- /dev/null
+++ b/source4/torture/libnet/libnet_lookup.c
@@ -0,0 +1,189 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/cmdline/popt_common.h"
+#include "libnet/libnet.h"
+#include "librpc/gen_ndr/nbt.h"
+#include "librpc/rpc/dcerpc.h"
+#include "libcli/libcli.h"
+#include "torture/rpc/rpc.h"
+#include "torture/torture.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 = cmdline_credentials;
+
+ 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 = 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)) {
+ printf("Couldn't lookup name %s: %s\n", lookup.in.hostname, nt_errstr(status));
+ ret = false;
+ goto done;
+ }
+
+ ret = true;
+
+ printf("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 = cmdline_credentials;
+
+ 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 = binding->host;
+ }
+ }
+
+ lookup.in.resolve_ctx = NULL;
+ lookup.out.address = NULL;
+
+ status = libnet_LookupHost(ctx, mem_ctx, &lookup);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Couldn't lookup host %s: %s\n", lookup.in.hostname, nt_errstr(status));
+ ret = false;
+ goto done;
+ }
+
+ ret = true;
+
+ printf("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 = cmdline_credentials;
+
+ talloc_steal(ctx, mem_ctx);
+
+ lookup = talloc(mem_ctx, struct libnet_LookupDCs);
+ if (!lookup) {
+ ret = false;
+ goto done;
+ }
+
+ lookup->in.domain_name = lp_workgroup(torture->lp_ctx);
+ lookup->in.name_type = NBT_NAME_PDC;
+
+ status = libnet_LookupDCs(ctx, mem_ctx, lookup);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Couldn't lookup pdc %s: %s\n", lookup->in.domain_name,
+ nt_errstr(status));
+ ret = false;
+ goto done;
+ }
+
+ ret = true;
+
+ printf("DCs of domain [%s] found.\n", lookup->in.domain_name);
+ for (i = 0; i < lookup->out.num_dcs; i++) {
+ printf("\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;
+
+ ctx = libnet_context_init(torture->ev, torture->lp_ctx);
+ ctx->cred = cmdline_credentials;
+
+ mem_ctx = talloc_init("torture lookup sam name");
+ if (mem_ctx == NULL) return false;
+
+ r.in.name = "Administrator";
+ r.in.domain_name = lp_workgroup(torture->lp_ctx);
+
+ status = libnet_LookupName(ctx, mem_ctx, &r);
+
+ talloc_free(mem_ctx);
+ talloc_free(ctx);
+
+ return true;
+}
diff --git a/source4/torture/libnet/libnet_rpc.c b/source4/torture/libnet/libnet_rpc.c
new file mode 100644
index 0000000000..0bcfcb6a4c
--- /dev/null
+++ b/source4/torture/libnet/libnet_rpc.c
@@ -0,0 +1,226 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/cmdline/popt_common.h"
+#include "auth/credentials/credentials.h"
+#include "libnet/libnet.h"
+#include "libcli/security/security.h"
+#include "librpc/ndr/libndr.h"
+#include "librpc/gen_ndr/ndr_lsa.h"
+#include "librpc/gen_ndr/ndr_samr.h"
+#include "librpc/gen_ndr/ndr_srvsvc.h"
+#include "librpc/rpc/dcerpc.h"
+#include "torture/rpc/rpc.h"
+#include "torture/torture.h"
+#include "param/param.h"
+
+
+static bool test_connect_service(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;
+ 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)) {
+ d_printf("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;
+ }
+
+ d_printf("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)) {
+ d_printf("Domain Controller Info:\n");
+ d_printf("\tDomain Name:\t %s\n", connect_r.out.domain_name);
+ d_printf("\tDomain SID:\t %s\n", dom_sid_string(ctx, connect_r.out.domain_sid));
+ d_printf("\tRealm:\t\t %s\n", connect_r.out.realm);
+ d_printf("\tGUID:\t\t %s\n", GUID_string(ctx, connect_r.out.guid));
+
+ } else if (!NT_STATUS_IS_OK(status)) {
+ d_printf("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 = cmdline_credentials;
+
+ d_printf("Testing connection to LSA interface\n");
+ if (!test_connect_service(ctx, &ndr_table_lsarpc, bindstr,
+ hostname, level, false, NT_STATUS_OK)) {
+ d_printf("failed to connect LSA interface\n");
+ return false;
+ }
+
+ d_printf("Testing connection to SAMR interface\n");
+ if (!test_connect_service(ctx, &ndr_table_samr, bindstr,
+ hostname, level, false, NT_STATUS_OK)) {
+ d_printf("failed to connect SAMR interface\n");
+ return false;
+ }
+
+ d_printf("Testing connection to SRVSVC interface\n");
+ if (!test_connect_service(ctx, &ndr_table_srvsvc, bindstr,
+ hostname, level, false, NT_STATUS_OK)) {
+ d_printf("failed to connect SRVSVC interface\n");
+ return false;
+ }
+
+ d_printf("Testing connection to LSA interface with wrong credentials\n");
+ if (!test_connect_service(ctx, &ndr_table_lsarpc, bindstr,
+ hostname, level, true, NT_STATUS_LOGON_FAILURE)) {
+ d_printf("failed to test wrong credentials on LSA interface\n");
+ return false;
+ }
+
+ d_printf("Testing connection to SAMR interface with wrong credentials\n");
+ if (!test_connect_service(ctx, &ndr_table_samr, bindstr,
+ hostname, level, true, NT_STATUS_LOGON_FAILURE)) {
+ d_printf("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;
+
+ status = torture_rpc_binding(torture, &binding);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ return torture_rpc_connect(torture, level, NULL, binding->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 = lp_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 = lp_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 = lp_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 0000000000..e49461357a
--- /dev/null
+++ b/source4/torture/libnet/libnet_share.c
@@ -0,0 +1,236 @@
+/*
+ Unix SMB/CIFS implementation.
+ Test suite for libnet calls.
+
+ Copyright (C) Gregory LEOCADIE <gleocadie@idealx.com> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/rpc/rpc.h"
+#include "libnet/libnet.h"
+#include "lib/cmdline/popt_common.h"
+#include "librpc/gen_ndr/ndr_srvsvc_c.h"
+
+
+#define TEST_SHARENAME "libnetsharetest"
+
+
+static void test_displayshares(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];
+ d_printf("\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) break;
+ }
+ d_printf("\t[%d] %s (%s)\t%s\n", i, info->name,
+ info->comment, share_types[j].desc);
+ }
+ 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) break;
+ }
+ d_printf("\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;
+
+ 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) break;
+ }
+ d_printf("\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;
+
+ 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) break;
+ }
+ d_printf("\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;
+ }
+}
+
+
+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) {
+ printf("Couldn't allocate libnet context\n");
+ ret = false;
+ goto done;
+ }
+
+ libnetctx->cred = cmdline_credentials;
+
+ printf("Testing libnet_ListShare\n");
+
+ share.in.server_name = talloc_asprintf(mem_ctx, "%s", binding->host);
+
+ for (i = 0; i < ARRAY_SIZE(levels); i++) {
+ share.in.level = levels[i];
+ printf("testing libnet_ListShare level %u\n", share.in.level);
+
+ status = libnet_ListShares(libnetctx, mem_ctx, &share);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("libnet_ListShare level %u failed - %s\n", share.in.level, share.out.error_string);
+ ret = false;
+ goto done;
+ }
+
+ printf("listing shares:\n");
+ test_displayshares(share);
+ }
+
+done:
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+
+static bool test_addshare(struct dcerpc_pipe *svc_pipe, TALLOC_CTX *mem_ctx, const char *host,
+ const char* share)
+{
+ NTSTATUS status;
+ struct srvsvc_NetShareAdd add;
+ union srvsvc_NetShareInfo info;
+ struct srvsvc_NetShareInfo2 i;
+
+ 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(svc_pipe, mem_ctx, &add);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed to add a new share\n");
+ return false;
+ }
+
+ printf("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 = cmdline_credentials;
+
+ status = torture_rpc_connection(torture,
+ &p,
+ &ndr_table_srvsvc);
+
+ torture_assert_ntstatus_ok(torture, status, "Failed to get rpc connection");
+
+ if (!test_addshare(p, torture, host, TEST_SHARENAME)) {
+ return false;
+ }
+
+ share.in.server_name = 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 0000000000..18007dccad
--- /dev/null
+++ b/source4/torture/libnet/libnet_user.c
@@ -0,0 +1,741 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/time.h"
+#include "lib/cmdline/popt_common.h"
+#include "libnet/libnet.h"
+#include "librpc/gen_ndr/ndr_samr_c.h"
+#include "librpc/gen_ndr/ndr_lsa_c.h"
+#include "torture/torture.h"
+#include "torture/rpc/rpc.h"
+#include "torture/libnet/usertest.h"
+#include "param/param.h"
+
+
+static bool test_cleanup(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *domain_handle, const char *username)
+{
+ NTSTATUS status;
+ 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;
+
+ names[0].string = username;
+
+ r1.in.domain_handle = domain_handle;
+ r1.in.num_names = 1;
+ r1.in.names = names;
+ r1.out.rids = &rids;
+ r1.out.types = &types;
+
+ printf("user account lookup '%s'\n", username);
+
+ status = dcerpc_samr_LookupNames(p, mem_ctx, &r1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("LookupNames failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ 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;
+
+ printf("opening user account\n");
+
+ status = dcerpc_samr_OpenUser(p, mem_ctx, &r2);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("OpenUser failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ r3.in.user_handle = &user_handle;
+ r3.out.user_handle = &user_handle;
+
+ printf("deleting user account\n");
+
+ status = dcerpc_samr_DeleteUser(p, mem_ctx, &r3);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("DeleteUser failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+
+static bool test_opendomain(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle, struct lsa_String *domname)
+{
+ NTSTATUS status;
+ struct policy_handle h, domain_handle;
+ struct samr_Connect r1;
+ struct samr_LookupDomain r2;
+ struct dom_sid2 *sid = NULL;
+ struct samr_OpenDomain r3;
+
+ printf("connecting\n");
+
+ r1.in.system_name = 0;
+ r1.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r1.out.connect_handle = &h;
+
+ status = dcerpc_samr_Connect(p, mem_ctx, &r1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Connect failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ r2.in.connect_handle = &h;
+ r2.in.domain_name = domname;
+ r2.out.sid = &sid;
+
+ printf("domain lookup on %s\n", domname->string);
+
+ status = dcerpc_samr_LookupDomain(p, mem_ctx, &r2);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("LookupDomain failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ r3.in.connect_handle = &h;
+ r3.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r3.in.sid = *r2.out.sid;
+ r3.out.domain_handle = &domain_handle;
+
+ printf("opening domain\n");
+
+ status = dcerpc_samr_OpenDomain(p, mem_ctx, &r3);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("OpenDomain failed - %s\n", nt_errstr(status));
+ return false;
+ } else {
+ *handle = domain_handle;
+ }
+
+ return true;
+}
+
+
+static bool test_samr_close(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *domain_handle)
+{
+ NTSTATUS status;
+ struct samr_Close r;
+
+ r.in.handle = domain_handle;
+ r.out.handle = domain_handle;
+
+ status = dcerpc_samr_Close(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Close samr domain failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+
+static bool test_lsa_close(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *domain_handle)
+{
+ NTSTATUS status;
+ struct lsa_Close r;
+
+ r.in.handle = domain_handle;
+ r.out.handle = domain_handle;
+
+ status = dcerpc_lsa_Close(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Close lsa domain failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+
+static bool test_createuser(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle, const char* user)
+{
+ NTSTATUS status;
+ struct policy_handle user_handle;
+ struct lsa_String username;
+ struct samr_CreateUser r1;
+ struct samr_Close r2;
+ uint32_t user_rid;
+
+ username.string = user;
+
+ r1.in.domain_handle = handle;
+ r1.in.account_name = &username;
+ r1.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r1.out.user_handle = &user_handle;
+ r1.out.rid = &user_rid;
+
+ printf("creating user '%s'\n", username.string);
+
+ status = dcerpc_samr_CreateUser(p, mem_ctx, &r1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("CreateUser failed - %s\n", nt_errstr(status));
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
+ printf("User (%s) already exists - attempting to delete and recreate account again\n", user);
+ if (!test_cleanup(p, mem_ctx, handle, TEST_USERNAME)) {
+ return false;
+ }
+
+ printf("creating user account\n");
+
+ status = dcerpc_samr_CreateUser(p, mem_ctx, &r1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("CreateUser failed - %s\n", nt_errstr(status));
+ return false;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ r2.in.handle = &user_handle;
+ r2.out.handle = &user_handle;
+
+ printf("closing user '%s'\n", username.string);
+
+ status = dcerpc_samr_Close(p, mem_ctx, &r2);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Close failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+
+bool torture_createuser(struct torture_context *torture)
+{
+ NTSTATUS status;
+ TALLOC_CTX *mem_ctx;
+ struct libnet_context *ctx;
+ struct libnet_CreateUser req;
+ bool ret = true;
+
+ mem_ctx = talloc_init("test_createuser");
+
+ ctx = libnet_context_init(torture->ev, torture->lp_ctx);
+ ctx->cred = cmdline_credentials;
+
+ req.in.user_name = TEST_USERNAME;
+ req.in.domain_name = lp_workgroup(torture->lp_ctx);
+ req.out.error_string = NULL;
+
+ status = libnet_CreateUser(ctx, mem_ctx, &req);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("libnet_CreateUser call failed: %s\n", nt_errstr(status));
+ ret = false;
+ goto done;
+ }
+
+ if (!test_cleanup(ctx->samr.pipe, mem_ctx, &ctx->samr.handle, TEST_USERNAME)) {
+ printf("cleanup failed\n");
+ ret = false;
+ goto done;
+ }
+
+ if (!test_samr_close(ctx->samr.pipe, mem_ctx, &ctx->samr.handle)) {
+ printf("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 *prep_mem_ctx, *mem_ctx;
+ struct policy_handle h;
+ struct lsa_String domain_name;
+ const char *name = TEST_USERNAME;
+ struct libnet_context *ctx;
+ struct libnet_DeleteUser req;
+ bool ret = true;
+
+ prep_mem_ctx = talloc_init("prepare test_deleteuser");
+
+ ctx = libnet_context_init(torture->ev, torture->lp_ctx);
+ ctx->cred = cmdline_credentials;
+
+ req.in.user_name = TEST_USERNAME;
+ req.in.domain_name = lp_workgroup(torture->lp_ctx);
+
+ status = torture_rpc_connection(torture,
+ &p,
+ &ndr_table_samr);
+ if (!NT_STATUS_IS_OK(status)) {
+ ret = false;
+ goto done;
+ }
+
+ domain_name.string = lp_workgroup(torture->lp_ctx);
+ if (!test_opendomain(p, prep_mem_ctx, &h, &domain_name)) {
+ ret = false;
+ goto done;
+ }
+
+ if (!test_createuser(p, prep_mem_ctx, &h, name)) {
+ ret = false;
+ goto done;
+ }
+
+ mem_ctx = talloc_init("test_deleteuser");
+
+ status = libnet_DeleteUser(ctx, mem_ctx, &req);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("libnet_DeleteUser call failed: %s\n", nt_errstr(status));
+ ret = false;
+ }
+
+ talloc_free(mem_ctx);
+
+done:
+ talloc_free(ctx);
+ talloc_free(prep_mem_ctx);
+ return ret;
+}
+
+
+/*
+ Generate testing set of random changes
+*/
+
+static void set_test_changes(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;
+
+ printf("Fields to change: [");
+
+ for (i = 0; i < num_changes && i < FIELDS_NUM; i++) {
+ const char *fldname;
+
+ testfld = (req_change == none) ? (random() % FIELDS_NUM) : req_change;
+
+ /* get one in case we hit time field this time */
+ gettimeofday(&now, NULL);
+
+ switch (testfld) {
+ case account_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 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 description:
+ continue_if_field_set(r->in.description);
+ r->in.description = talloc_asprintf(mem_ctx, TEST_CHG_DESCRIPTION,
+ (long)random());
+ fldname = "description";
+ break;
+
+ case 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 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 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 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 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";
+ }
+
+ printf(((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;
+ }
+
+ printf("]\n");
+}
+
+
+#define TEST_STR_FLD(fld) \
+ if (!strequal(req.in.fld, user_req.out.fld)) { \
+ printf("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)) { \
+ printf("failed to change '%s'\n", #fld); \
+ ret = false; \
+ goto cleanup; \
+ }
+
+#define TEST_NUM_FLD(fld) \
+ if (req.in.fld != user_req.out.fld) { \
+ printf("failed to change '%s'\n", #fld); \
+ ret = false; \
+ goto cleanup; \
+ }
+
+
+bool torture_modifyuser(struct torture_context *torture)
+{
+ NTSTATUS status;
+ struct dcerpc_binding *binding;
+ struct dcerpc_pipe *p;
+ TALLOC_CTX *prep_mem_ctx;
+ struct policy_handle h;
+ struct lsa_String domain_name;
+ char *name;
+ struct libnet_context *ctx;
+ struct libnet_ModifyUser req;
+ struct libnet_UserInfo user_req;
+ int fld;
+ bool ret = true;
+
+ prep_mem_ctx = talloc_init("prepare test_deleteuser");
+
+ ctx = libnet_context_init(torture->ev, torture->lp_ctx);
+ ctx->cred = cmdline_credentials;
+
+ status = torture_rpc_connection(torture,
+ &p,
+ &ndr_table_samr);
+ if (!NT_STATUS_IS_OK(status)) {
+ ret = false;
+ goto done;
+ }
+
+ name = talloc_strdup(prep_mem_ctx, TEST_USERNAME);
+
+ domain_name.string = lp_workgroup(torture->lp_ctx);
+ if (!test_opendomain(p, prep_mem_ctx, &h, &domain_name)) {
+ ret = false;
+ goto done;
+ }
+
+ if (!test_createuser(p, prep_mem_ctx, &h, name)) {
+ ret = false;
+ goto done;
+ }
+
+ status = torture_rpc_binding(torture, &binding);
+ if (!NT_STATUS_IS_OK(status)) {
+ ret = false;
+ goto done;
+ }
+
+ printf("Testing change of all fields - each single one in turn\n");
+
+ for (fld = 1; fld < FIELDS_NUM - 1; fld++) {
+ ZERO_STRUCT(req);
+ req.in.domain_name = lp_workgroup(torture->lp_ctx);
+ req.in.user_name = name;
+
+ set_test_changes(torture, &req, 1, &name, fld);
+
+ status = libnet_ModifyUser(ctx, torture, &req);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("libnet_ModifyUser call failed: %s\n", nt_errstr(status));
+ ret = false;
+ continue;
+ }
+
+ ZERO_STRUCT(user_req);
+ user_req.in.domain_name = lp_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)) {
+ printf("libnet_UserInfo call failed: %s\n", nt_errstr(status));
+ ret = false;
+ continue;
+ }
+
+ switch (fld) {
+ case account_name: TEST_STR_FLD(account_name);
+ break;
+ case full_name: TEST_STR_FLD(full_name);
+ break;
+ case comment: TEST_STR_FLD(comment);
+ break;
+ case description: TEST_STR_FLD(description);
+ break;
+ case home_directory: TEST_STR_FLD(home_directory);
+ break;
+ case home_drive: TEST_STR_FLD(home_drive);
+ break;
+ case logon_script: TEST_STR_FLD(logon_script);
+ break;
+ case 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;
+ }
+
+ if (fld == account_name) {
+ /* restore original testing username - it's useful when test fails
+ because it prevents from problems with recreating account */
+ ZERO_STRUCT(req);
+ req.in.domain_name = lp_workgroup(torture->lp_ctx);
+ req.in.user_name = name;
+ req.in.account_name = TEST_USERNAME;
+
+ status = libnet_ModifyUser(ctx, torture, &req);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("libnet_ModifyUser call failed: %s\n", nt_errstr(status));
+ ret = false;
+ goto done;
+ }
+
+ name = talloc_strdup(torture, TEST_USERNAME);
+ }
+ }
+
+cleanup:
+ if (!test_cleanup(ctx->samr.pipe, torture, &ctx->samr.handle, name)) {
+ printf("cleanup failed\n");
+ ret = false;
+ goto done;
+ }
+
+ if (!test_samr_close(ctx->samr.pipe, torture, &ctx->samr.handle)) {
+ printf("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;
+ struct dcerpc_pipe *p;
+ struct policy_handle h;
+ struct lsa_String domain_name;
+ struct libnet_UserInfo req;
+
+ prep_mem_ctx = talloc_init("prepare torture user info");
+
+ ctx = libnet_context_init(torture->ev, torture->lp_ctx);
+ ctx->cred = cmdline_credentials;
+
+ status = torture_rpc_connection(torture,
+ &p,
+ &ndr_table_samr);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ domain_name.string = lp_workgroup(torture->lp_ctx);
+ if (!test_opendomain(p, prep_mem_ctx, &h, &domain_name)) {
+ ret = false;
+ goto done;
+ }
+
+ if (!test_createuser(p, prep_mem_ctx, &h, name)) {
+ ret = false;
+ goto done;
+ }
+
+ mem_ctx = talloc_init("torture user info");
+
+ 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)) {
+ printf("libnet_UserInfo call failed: %s\n", nt_errstr(status));
+ ret = false;
+ talloc_free(mem_ctx);
+ goto done;
+ }
+
+ if (!test_cleanup(ctx->samr.pipe, mem_ctx, &ctx->samr.handle, TEST_USERNAME)) {
+ printf("cleanup failed\n");
+ ret = false;
+ goto done;
+ }
+
+ if (!test_samr_close(ctx->samr.pipe, mem_ctx, &ctx->samr.handle)) {
+ printf("domain close failed\n");
+ ret = false;
+ }
+
+ talloc_free(ctx);
+
+done:
+ 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 = cmdline_credentials;
+
+ domain_name.string = lp_workgroup(torture->lp_ctx);
+ mem_ctx = talloc_init("torture user list");
+
+ ZERO_STRUCT(req);
+
+ printf("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++) {
+ printf("\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))) {
+ printf("libnet_UserList call failed: %s\n", nt_errstr(status));
+ ret = false;
+ goto done;
+ }
+
+ if (!test_samr_close(ctx->samr.pipe, mem_ctx, &ctx->samr.handle)) {
+ printf("samr domain close failed\n");
+ ret = false;
+ goto done;
+ }
+
+ if (!test_lsa_close(ctx->lsa.pipe, mem_ctx, &ctx->lsa.handle)) {
+ printf("lsa domain close failed\n");
+ ret = false;
+ }
+
+ talloc_free(ctx);
+
+done:
+ talloc_free(mem_ctx);
+ return ret;
+}
diff --git a/source4/torture/libnet/userinfo.c b/source4/torture/libnet/userinfo.c
new file mode 100644
index 0000000000..11e57f852d
--- /dev/null
+++ b/source4/torture/libnet/userinfo.c
@@ -0,0 +1,202 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/rpc/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/utils.h"
+
+
+#define TEST_USERNAME "libnetuserinfotest"
+
+static bool test_userinfo(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);
+
+ user.in.domain_handle = *domain_handle;
+ user.in.sid = dom_sid_string(mem_ctx, user_sid);
+ user.in.level = level; /* this should be extended */
+
+ printf("Testing sync libnet_rpc_userinfo (SID argument)\n");
+ status = libnet_rpc_userinfo(p, mem_ctx, &user);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed to call sync libnet_rpc_userinfo - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ ZERO_STRUCT(user);
+
+ user.in.domain_handle = *domain_handle;
+ user.in.sid = NULL;
+ user.in.username = TEST_USERNAME;
+ user.in.level = level;
+
+ printf("Testing sync libnet_rpc_userinfo (username argument)\n");
+ status = libnet_rpc_userinfo(p, mem_ctx, &user);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed to call sync libnet_rpc_userinfo - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+
+static bool test_userinfo_async(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);
+
+ user.in.domain_handle = *domain_handle;
+ user.in.sid = dom_sid_string(mem_ctx, user_sid);
+ user.in.level = level; /* this should be extended */
+
+ printf("Testing async libnet_rpc_userinfo (SID argument)\n");
+
+ c = libnet_rpc_userinfo_send(p, &user, msg_handler);
+ if (!c) {
+ printf("Failed to call sync libnet_rpc_userinfo_send\n");
+ return false;
+ }
+
+ status = libnet_rpc_userinfo_recv(c, mem_ctx, &user);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Calling async libnet_rpc_userinfo failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ ZERO_STRUCT(user);
+
+ user.in.domain_handle = *domain_handle;
+ user.in.sid = NULL;
+ user.in.username = TEST_USERNAME;
+ user.in.level = level;
+
+ printf("Testing async libnet_rpc_userinfo (username argument)\n");
+
+ c = libnet_rpc_userinfo_send(p, &user, msg_handler);
+ if (!c) {
+ printf("Failed to call sync libnet_rpc_userinfo_send\n");
+ return false;
+ }
+
+ status = libnet_rpc_userinfo_recv(c, mem_ctx, &user);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Calling async libnet_rpc_userinfo failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ 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;
+
+ mem_ctx = talloc_init("test_userinfo");
+
+ status = torture_rpc_connection(torture,
+ &p,
+ &ndr_table_samr);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ name.string = lp_workgroup(torture->lp_ctx);
+
+ /*
+ * Testing synchronous version
+ */
+ if (!test_opendomain(torture, p, mem_ctx, &h, &name, &sid)) {
+ ret = false;
+ goto done;
+ }
+
+ if (!test_user_create(torture, p, mem_ctx, &h, TEST_USERNAME, &rid)) {
+ ret = false;
+ goto done;
+ }
+
+ if (!test_userinfo(p, mem_ctx, &h, &sid, TEST_USERNAME, &rid)) {
+ ret = false;
+ goto done;
+ }
+
+ if (!test_user_cleanup(torture, p, mem_ctx, &h, TEST_USERNAME)) {
+ ret = false;
+ goto done;
+ }
+
+ /*
+ * Testing asynchronous version and monitor messages
+ */
+ if (!test_opendomain(torture, p, mem_ctx, &h, &name, &sid)) {
+ ret = false;
+ goto done;
+ }
+
+ if (!test_user_create(torture, p, mem_ctx, &h, TEST_USERNAME, &rid)) {
+ ret = false;
+ goto done;
+ }
+
+ if (!test_userinfo_async(p, mem_ctx, &h, &sid, TEST_USERNAME, &rid)) {
+ ret = false;
+ goto done;
+ }
+
+ if (!test_user_cleanup(torture, p, 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 0000000000..a5d8540d7b
--- /dev/null
+++ b/source4/torture/libnet/userman.c
@@ -0,0 +1,461 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/rpc/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/utils.h"
+
+
+static bool test_useradd(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;
+
+ printf("Testing libnet_rpc_useradd\n");
+
+ status = libnet_rpc_useradd(p, mem_ctx, &user);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed to call libnet_rpc_useradd - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ return ret;
+}
+
+
+static bool test_useradd_async(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;
+
+ printf("Testing async libnet_rpc_useradd\n");
+
+ c = libnet_rpc_useradd_send(p, &user, msg_handler);
+ if (!c) {
+ printf("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)) {
+ printf("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 < FIELDS_NUM - 1; i++) {
+ const char *fldname;
+
+ testfld = (random() % (FIELDS_NUM - 1)) + 1;
+
+ gettimeofday(&now, NULL);
+
+ switch (testfld) {
+ case account_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 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 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 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 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 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 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 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(p, mem_ctx, mod);
+ torture_assert_ntstatus_ok(tctx, status, "Failed to call sync libnet_rpc_usermod");
+
+ return true;
+}
+
+
+static bool test_userdel(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle, const char *username)
+{
+ NTSTATUS status;
+ struct libnet_rpc_userdel user;
+
+ user.in.domain_handle = *handle;
+ user.in.username = username;
+
+ status = libnet_rpc_userdel(p, mem_ctx, &user);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("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)) { \
+ printf("'%s' field does not match\n", #fld); \
+ printf("received: '%s'\n", i->fld.string); \
+ printf("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)) { \
+ printf("'%s' field does not match\n", #fld); \
+ printf("received: '%s (+%ld us)'\n", \
+ timestring(mem_ctx, t.tv_sec), t.tv_usec); \
+ printf("expected: '%s (+%ld us)'\n", \
+ timestring(mem_ctx, mod->in.change.fld->tv_sec), \
+ 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)) { \
+ printf("'%s' field does not match\n", #fld); \
+ printf("received: '%04x'\n", i->fld); \
+ printf("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(p, 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;
+
+ mem_ctx = talloc_init("test_useradd");
+
+ status = torture_rpc_connection(torture,
+ &p,
+ &ndr_table_samr);
+
+ torture_assert_ntstatus_ok(torture, status, "RPC connect failed");
+
+ domain_name.string = lp_workgroup(torture->lp_ctx);
+ if (!test_opendomain(torture, p, mem_ctx, &h, &domain_name, &sid)) {
+ ret = false;
+ goto done;
+ }
+
+ if (!test_useradd(p, mem_ctx, &h, name)) {
+ ret = false;
+ goto done;
+ }
+
+ if (!test_user_cleanup(torture, p, mem_ctx, &h, name)) {
+ ret = false;
+ goto done;
+ }
+
+ if (!test_opendomain(torture, p, mem_ctx, &h, &domain_name, &sid)) {
+ ret = false;
+ goto done;
+ }
+
+ if (!test_useradd_async(p, mem_ctx, &h, name)) {
+ ret = false;
+ goto done;
+ }
+
+ if (!test_user_cleanup(torture, p, 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;
+
+ mem_ctx = talloc_init("test_userdel");
+
+ status = torture_rpc_connection(torture,
+ &p,
+ &ndr_table_samr);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ domain_name.string = lp_workgroup(torture->lp_ctx);
+ if (!test_opendomain(torture, p, mem_ctx, &h, &domain_name, &sid)) {
+ ret = false;
+ goto done;
+ }
+
+ if (!test_user_create(torture, p, mem_ctx, &h, name, &rid)) {
+ ret = false;
+ goto done;
+ }
+
+ if (!test_userdel(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;
+
+ mem_ctx = talloc_init("test_userdel");
+
+ status = torture_rpc_connection(torture,
+ &p,
+ &ndr_table_samr);
+
+ torture_assert_ntstatus_ok(torture, status, "RPC connect");
+
+ domain_name.string = lp_workgroup(torture->lp_ctx);
+ name = talloc_strdup(mem_ctx, TEST_USERNAME);
+
+ if (!test_opendomain(torture, p, mem_ctx, &h, &domain_name, &sid)) {
+ ret = false;
+ goto done;
+ }
+
+ if (!test_user_create(torture, p, mem_ctx, &h, name, &rid)) {
+ ret = false;
+ goto done;
+ }
+
+ for (i = 1; i < FIELDS_NUM; 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, p, mem_ctx, &h, name)) {
+ 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 0000000000..19d56415b3
--- /dev/null
+++ b/source4/torture/libnet/usertest.h
@@ -0,0 +1,38 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#define TEST_USERNAME "libnetusertest"
+
+#define continue_if_field_set(field) \
+ if (field != 0) { \
+ i--; \
+ continue; \
+ }
+
+
+#define FIELDS_NUM 11
+enum test_fields { none = 0, account_name, full_name, description, home_directory, home_drive,
+ comment, logon_script, 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 0000000000..942540c80e
--- /dev/null
+++ b/source4/torture/libnet/utils.c
@@ -0,0 +1,301 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * These are more general use functions shared among the tests.
+ */
+
+#include "includes.h"
+#include "torture/rpc/rpc.h"
+#include "libnet/libnet.h"
+#include "librpc/gen_ndr/ndr_samr_c.h"
+#include "torture/libnet/utils.h"
+
+
+bool test_opendomain(struct torture_context *tctx,
+ struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle, struct lsa_String *domname,
+ struct dom_sid2 *sid_p)
+{
+ NTSTATUS status;
+ 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");
+
+ r1.in.system_name = 0;
+ r1.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r1.out.connect_handle = &h;
+
+ status = dcerpc_samr_Connect(p, mem_ctx, &r1);
+ torture_assert_ntstatus_ok(tctx, status, "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);
+
+ status = dcerpc_samr_LookupDomain(p, mem_ctx, &r2);
+ torture_assert_ntstatus_ok(tctx, status, "LookupDomain failed");
+
+ r3.in.connect_handle = &h;
+ 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\n");
+
+ status = dcerpc_samr_OpenDomain(p, mem_ctx, &r3);
+ torture_assert_ntstatus_ok(tctx, status, "OpenDomain failed");
+ *handle = domain_handle;
+
+ *sid_p = **r2.out.sid;
+ return true;
+}
+
+
+bool test_user_cleanup(struct torture_context *tctx, struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx, struct policy_handle *domain_handle,
+ const char *name)
+{
+ NTSTATUS status;
+ 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;
+
+ 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, "user account lookup '%s'\n", name);
+
+ status = dcerpc_samr_LookupNames(p, mem_ctx, &r1);
+ torture_assert_ntstatus_ok(tctx, status, "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");
+
+ status = dcerpc_samr_OpenUser(p, mem_ctx, &r2);
+ torture_assert_ntstatus_ok(tctx, status, "OpenUser failed");
+
+ r3.in.user_handle = &user_handle;
+ r3.out.user_handle = &user_handle;
+
+ torture_comment(tctx, "deleting user account\n");
+
+ status = dcerpc_samr_DeleteUser(p, mem_ctx, &r3);
+ torture_assert_ntstatus_ok(tctx, status, "DeleteUser failed");
+
+ return true;
+}
+
+
+bool test_user_create(struct torture_context *tctx,
+ struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle, const char *name,
+ uint32_t *rid)
+{
+ NTSTATUS status;
+ struct lsa_String username;
+ struct samr_CreateUser r;
+ struct policy_handle user_handle;
+
+ username.string = name;
+
+ r.in.domain_handle = handle;
+ r.in.account_name = &username;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.user_handle = &user_handle;
+ r.out.rid = rid;
+
+ torture_comment(tctx, "creating user account %s\n", name);
+
+ status = dcerpc_samr_CreateUser(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("CreateUser failed - %s\n", nt_errstr(status));
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
+ torture_comment(tctx, "User (%s) already exists - attempting to delete and recreate account again\n", name);
+ if (!test_user_cleanup(tctx, p, mem_ctx, handle, name)) {
+ return false;
+ }
+
+ torture_comment(tctx, "creating user account\n");
+
+ status = dcerpc_samr_CreateUser(p, mem_ctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "CreateUser failed");
+ return true;
+ }
+ return false;
+ }
+
+ return true;
+}
+
+
+bool test_group_cleanup(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *domain_handle,
+ const char *name)
+{
+ NTSTATUS status;
+ 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;
+
+ printf("group account lookup '%s'\n", name);
+
+ status = dcerpc_samr_LookupNames(p, mem_ctx, &r1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("LookupNames failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ 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;
+
+ printf("opening group account\n");
+
+ status = dcerpc_samr_OpenGroup(p, mem_ctx, &r2);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("OpenGroup failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ r3.in.group_handle = &group_handle;
+ r3.out.group_handle = &group_handle;
+
+ printf("deleting group account\n");
+
+ status = dcerpc_samr_DeleteDomainGroup(p, mem_ctx, &r3);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("DeleteGroup failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+
+bool test_group_create(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle, const char *name,
+ uint32_t *rid)
+{
+ NTSTATUS status;
+ struct lsa_String groupname;
+ struct samr_CreateDomainGroup r;
+ struct policy_handle group_handle;
+
+ groupname.string = name;
+
+ r.in.domain_handle = handle;
+ r.in.name = &groupname;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.group_handle = &group_handle;
+ r.out.rid = rid;
+
+ printf("creating group account %s\n", name);
+
+ status = dcerpc_samr_CreateDomainGroup(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("CreateGroup failed - %s\n", nt_errstr(status));
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
+ printf("Group (%s) already exists - attempting to delete and recreate account again\n", name);
+ if (!test_group_cleanup(p, mem_ctx, handle, name)) {
+ return false;
+ }
+
+ printf("creating group account\n");
+
+ status = dcerpc_samr_CreateDomainGroup(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("CreateGroup failed - %s\n", nt_errstr(status));
+ return false;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ return true;
+}
+
+
+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/libnet/utils.h b/source4/torture/libnet/utils.h
new file mode 100644
index 0000000000..b513b1a29e
--- /dev/null
+++ b/source4/torture/libnet/utils.h
@@ -0,0 +1,45 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+
+bool test_opendomain(struct torture_context *tctx,
+ struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle, struct lsa_String *domname,
+ struct dom_sid2 *sid);
+
+bool test_user_create(struct torture_context *tctx,
+ struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle, const char *name,
+ uint32_t *rid);
+
+bool test_user_cleanup(struct torture_context *tctx,
+ struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *domain_handle,
+ const char *name);
+
+bool test_group_create(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle, const char *name,
+ uint32_t *rid);
+
+bool test_group_cleanup(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *domain_handle,
+ const char *name);
+
+void msg_handler(struct monitor_msg *m);
diff --git a/source4/torture/local/config.mk b/source4/torture/local/config.mk
new file mode 100644
index 0000000000..36f4f08072
--- /dev/null
+++ b/source4/torture/local/config.mk
@@ -0,0 +1,57 @@
+#################################
+# Start SUBSYSTEM TORTURE_LOCAL
+[MODULE::TORTURE_LOCAL]
+SUBSYSTEM = smbtorture
+OUTPUT_TYPE = MERGED_OBJ
+INIT_FUNCTION = torture_local_init
+PRIVATE_DEPENDENCIES = \
+ RPC_NDR_ECHO \
+ TDR \
+ LIBCLI_SMB \
+ MESSAGING \
+ ICONV \
+ POPT_CREDENTIALS \
+ TORTURE_AUTH \
+ TORTURE_UTIL \
+ TORTURE_NDR \
+ TORTURE_LIBCRYPTO \
+ share \
+ torture_registry \
+ PROVISION
+# End SUBSYSTEM TORTURE_LOCAL
+#################################
+
+TORTURE_LOCAL_OBJ_FILES = \
+ $(torturesrcdir)/../../lib/util/charset/tests/iconv.o \
+ $(torturesrcdir)/../../lib/talloc/testsuite.o \
+ $(torturesrcdir)/../../lib/replace/test/getifaddrs.o \
+ $(torturesrcdir)/../../lib/replace/test/os2_delete.o \
+ $(torturesrcdir)/../../lib/replace/test/strptime.o \
+ $(torturesrcdir)/../../lib/replace/test/testsuite.o \
+ $(torturesrcdir)/../lib/messaging/tests/messaging.o \
+ $(torturesrcdir)/../lib/messaging/tests/irpc.o \
+ $(torturesrcdir)/../librpc/tests/binding_string.o \
+ $(torturesrcdir)/../../lib/util/tests/idtree.o \
+ $(torturesrcdir)/../lib/socket/testsuite.o \
+ $(torturesrcdir)/../../lib/socket_wrapper/testsuite.o \
+ $(torturesrcdir)/../libcli/resolve/testsuite.o \
+ $(torturesrcdir)/../../lib/util/tests/strlist.o \
+ $(torturesrcdir)/../../lib/util/tests/str.o \
+ $(torturesrcdir)/../../lib/util/tests/time.o \
+ $(torturesrcdir)/../../lib/util/tests/data_blob.o \
+ $(torturesrcdir)/../../lib/util/tests/file.o \
+ $(torturesrcdir)/../../lib/util/tests/genrand.o \
+ $(torturesrcdir)/../../lib/compression/testsuite.o \
+ $(torturesrcdir)/../../lib/util/charset/tests/charset.o \
+ $(torturesrcdir)/../libcli/security/tests/sddl.o \
+ $(torturesrcdir)/../lib/tdr/testsuite.o \
+ $(torturesrcdir)/../../lib/tevent/testsuite.o \
+ $(torturesrcdir)/../param/tests/share.o \
+ $(torturesrcdir)/../param/tests/loadparm.o \
+ $(torturesrcdir)/../auth/credentials/tests/simple.o \
+ $(torturesrcdir)/local/local.o \
+ $(torturesrcdir)/local/dbspeed.o \
+ $(torturesrcdir)/local/torture.o
+
+
+$(eval $(call proto_header_template,$(torturesrcdir)/local/proto.h,$(TORTURE_LOCAL_OBJ_FILES:.o=.c)))
diff --git a/source4/torture/local/dbspeed.c b/source4/torture/local/dbspeed.c
new file mode 100644
index 0000000000..9547e9ff74
--- /dev/null
+++ b/source4/torture/local/dbspeed.c
@@ -0,0 +1,258 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "../tdb/include/tdb.h"
+#include "lib/ldb/include/ldb.h"
+#include "lib/ldb/include/ldb_errors.h"
+#include "lib/ldb_wrap.h"
+#include "lib/tdb_wrap.h"
+#include "torture/smbtorture.h"
+#include "param/param.h"
+
+float tdb_speed;
+
+static bool tdb_add_record(struct tdb_wrap *tdbw, const char *fmt1,
+ const char *fmt2, int i)
+{
+ TDB_DATA key, data;
+ int ret;
+ key.dptr = (uint8_t *)talloc_asprintf(tdbw, fmt1, i);
+ key.dsize = strlen((char *)key.dptr)+1;
+ data.dptr = (uint8_t *)talloc_asprintf(tdbw, fmt2, 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, 0, O_RDWR|O_CREAT|O_TRUNC, 0600);
+ if (!tdbw) {
+ unlink("test.tdb");
+ talloc_free(tmp_ctx);
+ torture_fail(torture, "Failed to open test.tdb");
+ }
+
+ torture_comment(torture, "Adding %d SID records\n", torture_entries);
+
+ for (i=0;i<torture_entries;i++) {
+ if (!tdb_add_record(tdbw,
+ "S-1-5-21-53173311-3623041448-2049097239-%u",
+ "UID %u", i)) {
+ torture_result(torture, TORTURE_FAIL, "Failed to add SID %d\n", i);
+ goto failed;
+ }
+ if (!tdb_add_record(tdbw,
+ "UID %u",
+ "S-1-5-21-53173311-3623041448-2049097239-%u", i)) {
+ torture_result(torture, TORTURE_FAIL, "Failed to add UID %d\n", i);
+ goto failed;
+ }
+ }
+
+ torture_comment(torture, "Testing for %d seconds\n", timelimit);
+
+ tv = timeval_current();
+
+ for (count=0;timeval_elapsed(&tv) < timelimit;count++) {
+ TDB_DATA key, data;
+ i = random() % torture_entries;
+ key.dptr = (uint8_t *)talloc_asprintf(tmp_ctx, "S-1-5-21-53173311-3623041448-2049097239-%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 fetch SID %d\n", 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 fetch UID %d\n", i);
+ goto failed;
+ }
+ free(data.dptr);
+ }
+
+ tdb_speed = count/timeval_elapsed(&tv);
+ torture_comment(torture, "tdb speed %.2f ops/sec\n", tdb_speed);
+
+
+ unlink("test.tdb");
+ talloc_free(tmp_ctx);
+ return true;
+
+failed:
+ unlink("test.tdb");
+ talloc_free(tmp_ctx);
+ 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) {
+ return false;
+ }
+
+ if (ldb_msg_add_fmt(msg, "UID", "%u", rid) != 0) {
+ 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, NULL);
+ if (!ldb) {
+ unlink("./test.ldb");
+ talloc_free(tmp_ctx);
+ torture_fail(torture, "Failed to open test.ldb");
+ }
+
+ /* add an index */
+ ldif = ldb_ldif_read_string(ldb, &init_ldif);
+ if (ldif == NULL) goto failed;
+ if (ldb_add(ldb, ldif->msg) != LDB_SUCCESS) goto failed;
+ talloc_free(ldif);
+
+ torture_comment(torture, "Adding %d SID records\n", torture_entries);
+
+ for (i=0;i<torture_entries;i++) {
+ if (!ldb_add_record(ldb, i)) {
+ torture_result(torture, TORTURE_FAIL, "Failed to add SID %d\n", i);
+ goto failed;
+ }
+ }
+
+ if (talloc_total_blocks(torture) > 100) {
+ torture_result(torture, TORTURE_FAIL, "memory leak in ldb add\n");
+ 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_fail(torture, talloc_asprintf(torture, "Failed to find SID %d", i));
+ }
+ 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_fail(torture, talloc_asprintf(torture, "Failed to find UID %d", i));
+ }
+ talloc_free(res);
+ }
+
+ if (talloc_total_blocks(torture) > 100) {
+ unlink("./test.ldb");
+ talloc_free(tmp_ctx);
+ torture_fail(torture, "memory leak in ldb search");
+ }
+
+ 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));
+
+
+ unlink("./test.ldb");
+ talloc_free(tmp_ctx);
+ return true;
+
+failed:
+ unlink("./test.ldb");
+ talloc_free(tmp_ctx);
+ 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/local.c b/source4/torture/local/local.c
new file mode 100644
index 0000000000..6b38cd6db0
--- /dev/null
+++ b/source4/torture/local/local.c
@@ -0,0 +1,90 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/smbtorture.h"
+#include "torture/local/proto.h"
+#include "torture/ndr/ndr.h"
+#include "torture/ndr/proto.h"
+#include "torture/auth/proto.h"
+#include "../lib/crypto/test_proto.h"
+#include "lib/registry/tests/proto.h"
+
+/* ignore me */ static struct torture_suite *
+ (*suite_generators[]) (TALLOC_CTX *mem_ctx) =
+{
+ torture_local_binding_string,
+ torture_ntlmssp,
+ 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_idtree,
+ torture_local_genrand,
+ torture_local_iconv,
+ torture_local_socket,
+ torture_local_socket_wrapper,
+ torture_pac,
+ torture_local_resolve,
+ torture_local_sddl,
+ torture_local_ndr,
+ torture_local_tdr,
+ torture_local_share,
+ torture_local_loadparm,
+ torture_local_charset,
+ torture_local_compression,
+ torture_local_event,
+ torture_local_torture,
+ torture_local_dbspeed,
+ torture_local_credentials,
+ torture_registry,
+ NULL
+};
+
+NTSTATUS torture_local_init(void)
+{
+ int i;
+ struct torture_suite *suite = torture_suite_create(
+ talloc_autofree_context(),
+ "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);
+ torture_suite_add_simple_test(suite, "CRYPTO-MD5",
+ torture_local_crypto_md5);
+ torture_suite_add_simple_test(suite, "CRYPTO-HMACMD5",
+ torture_local_crypto_hmacmd5);
+
+ for (i = 0; suite_generators[i]; i++)
+ torture_suite_add_suite(suite,
+ suite_generators[i](talloc_autofree_context()));
+
+ suite->description = talloc_strdup(suite,
+ "Local, Samba-specific tests");
+
+ torture_register_suite(suite);
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/torture/local/torture.c b/source4/torture/local/torture.c
new file mode 100644
index 0000000000..7935f6cc35
--- /dev/null
+++ b/source4/torture/local/torture.c
@@ -0,0 +1,86 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "system/wait.h"
+#include "lib/events/events.h"
+#include "libcli/raw/libcliraw.h"
+#include "torture/util.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";
+
+ 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/locktest.c b/source4/torture/locktest.c
new file mode 100644
index 0000000000..0a937e9638
--- /dev/null
+++ b/source4/torture/locktest.c
@@ -0,0 +1,657 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/cmdline/popt_common.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 "dynconfig/dynconfig.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 uint_t lock_range = 100;
+static uint_t lock_base = 0;
+static uint_t 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;
+
+ lp_smbcli_options(lp_ctx, &options);
+ lp_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 = lp_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-%u-%u", 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,
+ lp_smb_ports(lp_ctx),
+ share, NULL,
+ lp_socket_options(lp_ctx),
+ servers[snum],
+ lp_resolve_context(lp_ctx),
+ ev, &options, &session_options,
+ lp_iconv_convenience(lp_ctx),
+ lp_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;server<NSERVERS;server++)
+ for (conn=0;conn<NCONNECTIONS;conn++) {
+ if (cli[server][conn]) {
+ for (f=0;f<NFILES;f++) {
+ if (fnum[server][conn][f] != -1) {
+ smbcli_close(cli[server][conn]->tree, 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)
+{
+ uint_t conn = rec->conn;
+ uint_t 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;server<NSERVERS;server++) {
+ NTSTATUS res;
+ struct smbcli_tree *tree=cli[server][conn]->tree;
+ int fn=fnum[server][conn][f];
+
+ if (!(tree->session->transport->negotiate.capabilities & CAP_LARGE_FILES)) {
+ res=smbcli_lock(tree, fn, start, len, LOCK_TIMEOUT, 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_op == 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] = smbcli_nt_error(cli[server][conn]->tree);
+ 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;server<NSERVERS;server++) {
+ NTSTATUS res;
+ struct smbcli_tree *tree=cli[server][conn]->tree;
+ 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] = smbcli_nt_error(cli[server][conn]->tree);
+ }
+ 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;server<NSERVERS;server++) {
+ smbcli_close(cli[server][conn]->tree, fnum[server][conn][f]);
+ fnum[server][conn][f] = -1;
+ }
+ for (server=0;server<NSERVERS;server++) {
+ fnum[server][conn][f] = smbcli_open(cli[server][conn]->tree, 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;server<NSERVERS;server++)
+ for (conn=0;conn<NCONNECTIONS;conn++)
+ for (f=0;f<NFILES;f++) {
+ if (fnum[server][conn][f] != -1) {
+ smbcli_close(cli[server][conn]->tree, fnum[server][conn][f]);
+ fnum[server][conn][f] = -1;
+ }
+ }
+ for (server=0;server<NSERVERS;server++) {
+ smbcli_unlink(cli[server][0]->tree, FILENAME);
+ }
+}
+
+static void open_files(struct smbcli_state *cli[NSERVERS][NCONNECTIONS],
+ int fnum[NSERVERS][NCONNECTIONS][NFILES])
+{
+ int server, conn, f;
+
+ for (server=0;server<NSERVERS;server++)
+ for (conn=0;conn<NCONNECTIONS;conn++)
+ for (f=0;f<NFILES;f++) {
+ fnum[server][conn][f] = smbcli_open(cli[server][conn]->tree, 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<n; i++) {
+ if (i && i % 100 == 0) {
+ printf("%u\n", i);
+ }
+
+ if (recorded[i].needed &&
+ !test_one(cli, fnum, &recorded[i])) return i;
+ }
+ return n;
+}
+
+
+/* each server has two connections open to it. Each connection has two file
+ descriptors open on the file - 8 file descriptors in total
+
+ we then do random locking ops in tamdem on the 4 fnums from each
+ server and ensure that the results match
+ */
+static int test_locks(struct tevent_context *ev,
+ struct loadparm_context *lp_ctx,
+ TALLOC_CTX *mem_ctx,
+ char *share[NSERVERS])
+{
+ struct smbcli_state *cli[NSERVERS][NCONNECTIONS];
+ int fnum[NSERVERS][NCONNECTIONS][NFILES];
+ int n, i, n1, skip, r1, r2;
+
+ ZERO_STRUCT(fnum);
+ ZERO_STRUCT(cli);
+
+ recorded = malloc_array_p(struct record, numops);
+
+ for (n=0; n<numops; n++) {
+#if PRESETS
+ if (n < sizeof(preset) / sizeof(preset[0])) {
+ recorded[n] = preset[n];
+ } else {
+#endif
+ recorded[n].conn = random() % NCONNECTIONS;
+ recorded[n].f = random() % NFILES;
+ recorded[n].start = lock_base + ((uint_t)random() % (lock_range-1));
+ recorded[n].len = min_length +
+ random() % (lock_range-(recorded[n].start-lock_base));
+ recorded[n].start *= RANGE_MULTIPLE;
+ recorded[n].len *= RANGE_MULTIPLE;
+ recorded[n].pid = random()%3;
+ if (recorded[n].pid == 2) {
+ recorded[n].pid = 0xFFFF; /* see if its magic */
+ }
+ r1 = random() % 100;
+ r2 = random() % 100;
+ if (r1 < READ_PCT) {
+ recorded[n].lock_type = READ_LOCK;
+ } else {
+ recorded[n].lock_type = WRITE_LOCK;
+ }
+ if (r2 < LOCK_PCT) {
+ recorded[n].lock_op = OP_LOCK;
+ } else if (r2 < UNLOCK_PCT) {
+ recorded[n].lock_op = OP_UNLOCK;
+ } else {
+ recorded[n].lock_op = OP_REOPEN;
+ }
+ recorded[n].needed = true;
+ if (!zero_zero && recorded[n].start==0 && recorded[n].len==0) {
+ recorded[n].len = 1;
+ }
+#if PRESETS
+ }
+#endif
+ }
+
+ reconnect(ev, lp_ctx, mem_ctx, cli, fnum, share);
+ open_files(cli, fnum);
+ n = retest(cli, fnum, numops);
+
+ if (n == numops || !analyze) {
+ if (n != numops) {
+ return 1;
+ }
+ return 0;
+ }
+ n++;
+
+ skip = n/2;
+
+ while (1) {
+ n1 = n;
+
+ close_files(cli, fnum);
+ reconnect(ev, lp_ctx, mem_ctx, cli, fnum, share);
+ open_files(cli, fnum);
+
+ for (i=0;i<n-skip;i+=skip) {
+ int m, j;
+ printf("excluding %d-%d\n", i, i+skip-1);
+ for (j=i;j<i+skip;j++) {
+ recorded[j].needed = false;
+ }
+
+ close_files(cli, fnum);
+ open_files(cli, fnum);
+
+ m = retest(cli, fnum, n);
+ if (m == n) {
+ for (j=i;j<i+skip;j++) {
+ recorded[j].needed = true;
+ }
+ } else {
+ if (i+(skip-1) < m) {
+ memmove(&recorded[i], &recorded[i+skip],
+ (m-(i+skip-1))*sizeof(recorded[0]));
+ }
+ n = m-(skip-1);
+ i--;
+ }
+ }
+
+ if (skip > 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<n;i++) {
+ printf("{%d, %d, %u, %u, %.0f, %.0f, %u},\n",
+ recorded[i].lock_op,
+ recorded[i].lock_type,
+ recorded[i].conn,
+ recorded[i].f,
+ (double)recorded[i].start,
+ (double)recorded[i].len,
+ recorded[i].needed);
+ }
+
+ return 1;
+}
+
+
+
+static void usage(poptContext pc)
+{
+ printf("Usage:\n\tlocktest //server1/share1 //server2/share2 [options..]\n");
+ poptPrintUsage(pc, stdout, 0);
+}
+
+/****************************************************************************
+ main program
+****************************************************************************/
+ int main(int argc,char *argv[])
+{
+ char *share[NSERVERS];
+ int opt;
+ int seed, server;
+ int username_count=0;
+ struct tevent_context *ev;
+ struct loadparm_context *lp_ctx;
+ poptContext pc;
+ int argc_new, i;
+ char **argv_new;
+ 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, &numops, 0, "num ops", NULL},
+ {"lockrange", 0, POPT_ARG_INT, &lock_range,0, "locking range", NULL},
+ {"lockbase", 0, POPT_ARG_INT, &lock_base, 0, "locking base", NULL},
+ {"minlength", 0, POPT_ARG_INT, &min_length,0, "min lock length", NULL},
+ {"hidefails", 0, POPT_ARG_NONE, &hide_unlock_fails,0,"hide unlock fails", NULL},
+ {"oplocks", 0, POPT_ARG_NONE, &use_oplocks,0, "use oplocks", NULL},
+ {"showall", 0, POPT_ARG_NONE, &showall, 0, "display all operations", NULL},
+ {"analyse", 0, POPT_ARG_NONE, &analyze, 0, "do backtrack analysis", NULL},
+ {"zerozero", 0, POPT_ARG_NONE, &zero_zero, 0, "do zero/zero lock", NULL},
+ {"exacterrors", 0, POPT_ARG_NONE, &exact_error_codes,0,"use exact error codes", NULL},
+ {"unclist", 0, POPT_ARG_STRING, NULL, OPT_UNCLIST, "unclist", NULL},
+ { "user", 'U', POPT_ARG_STRING, NULL, 'U', "Set the network username", "[DOMAIN/]USERNAME[%PASSWORD]" },
+ POPT_COMMON_SAMBA
+ POPT_COMMON_CONNECTION
+ POPT_COMMON_CREDENTIALS
+ POPT_COMMON_VERSION
+ { NULL }
+ };
+
+ setlinebuf(stdout);
+ seed = time(NULL);
+
+ pc = poptGetContext("locktest", argc, (const char **) argv, long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+
+ poptSetOtherOptionHelp(pc, "<unc1> <unc2>");
+
+ lp_ctx = cmdline_lp_ctx;
+ servers[0] = cli_credentials_init(talloc_autofree_context());
+ servers[1] = cli_credentials_init(talloc_autofree_context());
+ cli_credentials_guess(servers[0], lp_ctx);
+ cli_credentials_guess(servers[1], lp_ctx);
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case OPT_UNCLIST:
+ lp_set_cmdline(cmdline_lp_ctx, "torture:unclist", poptGetOptArg(pc));
+ break;
+ case 'U':
+ if (username_count == 2) {
+ usage(pc);
+ exit(1);
+ }
+ cli_credentials_parse_string(servers[username_count], poptGetOptArg(pc), CRED_SPECIFIED);
+ username_count++;
+ break;
+ }
+ }
+
+ argv_new = discard_const_p(char *, poptGetArgs(pc));
+ argc_new = argc;
+ for (i=0; i<argc; i++) {
+ if (argv_new[i] == NULL) {
+ argc_new = i;
+ break;
+ }
+ }
+
+ if (!(argc_new >= 3)) {
+ usage(pc);
+ exit(1);
+ }
+
+ setup_logging("locktest", DEBUG_STDOUT);
+
+ for (server=0;server<NSERVERS;server++) {
+ share[server] = argv_new[1+server];
+ all_string_sub(share[server],"/","\\",0);
+ }
+
+ lp_ctx = cmdline_lp_ctx;
+
+ if (username_count == 0) {
+ usage(pc);
+ return -1;
+ }
+ if (username_count == 1) {
+ servers[1] = servers[0];
+ }
+
+ ev = s4_event_context_init(talloc_autofree_context());
+
+ gensec_init(lp_ctx);
+
+ DEBUG(0,("seed=%u base=%d range=%d min_length=%d\n",
+ seed, lock_base, lock_range, min_length));
+ srandom(seed);
+
+ return test_locks(ev, lp_ctx, NULL, share);
+}
+
diff --git a/source4/torture/locktest2.c b/source4/torture/locktest2.c
new file mode 100644
index 0000000000..40b0085881
--- /dev/null
+++ b/source4/torture/locktest2.c
@@ -0,0 +1,578 @@
+/*
+ Unix SMB/CIFS implementation.
+ byte range lock tester - with local filesystem support
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/passwd.h"
+#include "lib/events/events.h"
+
+static fstring password;
+static fstring username;
+static int got_pass;
+static int numops = 1000;
+static bool showall;
+static bool analyze;
+static bool hide_unlock_fails;
+static bool use_oplocks;
+
+#define FILENAME "\\locktest.dat"
+#define LOCKRANGE 100
+#define LOCKBASE 0
+
+/*
+#define LOCKBASE (0x40000000 - 50)
+*/
+
+#define READ_PCT 50
+#define LOCK_PCT 25
+#define UNLOCK_PCT 65
+#define RANGE_MULTIPLE 1
+
+#define NSERVERS 2
+#define NCONNECTIONS 2
+#define NUMFSTYPES 2
+#define NFILES 2
+#define LOCK_TIMEOUT 0
+
+#define FSTYPE_SMB 0
+#define FSTYPE_NFS 1
+
+struct record {
+ char r1, r2;
+ char conn, f, fstype;
+ uint_t start, len;
+ char needed;
+};
+
+static struct record *recorded;
+
+static int try_open(struct smbcli_state *c, char *nfs, int fstype, const char *fname, int flags)
+{
+ char *path;
+ int ret;
+
+ switch (fstype) {
+ case FSTYPE_SMB:
+ return smbcli_open(c, fname, flags, DENY_NONE);
+
+ case FSTYPE_NFS:
+ asprintf(&path, "%s%s", nfs, fname);
+ string_replace(path,'\\', '/');
+ ret = open(path, flags, 0666);
+ SAFE_FREE(Path);
+ return ret;
+ }
+
+ return -1;
+}
+
+static bool try_close(struct smbcli_state *c, int fstype, int fd)
+{
+ switch (fstype) {
+ case FSTYPE_SMB:
+ return smbcli_close(c, fd);
+
+ case FSTYPE_NFS:
+ return close(fd) == 0;
+ }
+
+ return false;
+}
+
+static bool try_lock(struct smbcli_state *c, int fstype,
+ int fd, uint_t start, uint_t len,
+ enum brl_type op)
+{
+ struct flock lock;
+
+ switch (fstype) {
+ case FSTYPE_SMB:
+ return smbcli_lock(c, fd, start, len, LOCK_TIMEOUT, op);
+
+ case FSTYPE_NFS:
+ lock.l_type = (op==READ_LOCK) ? F_RDLCK:F_WRLCK;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = start;
+ lock.l_len = len;
+ lock.l_pid = getpid();
+ return fcntl(fd,F_SETLK,&lock) == 0;
+ }
+
+ return false;
+}
+
+static bool try_unlock(struct smbcli_state *c, int fstype,
+ int fd, uint_t start, uint_t len)
+{
+ struct flock lock;
+
+ switch (fstype) {
+ case FSTYPE_SMB:
+ return smbcli_unlock(c, fd, start, len);
+
+ case FSTYPE_NFS:
+ lock.l_type = F_UNLCK;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = start;
+ lock.l_len = len;
+ lock.l_pid = getpid();
+ return fcntl(fd,F_SETLK,&lock) == 0;
+ }
+
+ return false;
+}
+
+/*****************************************************
+return a connection to a server
+*******************************************************/
+static struct smbcli_state *connect_one(TALLOC_CTX *mem_ctx,
+ char *share, const char **ports,
+ struct smb_options *options,
+ struct smb_options *session_options,
+ struct gensec_settings *gensec_settings,
+ struct tevent_context *ev)
+{
+ struct smbcli_state *c;
+ char *server_n;
+ char *server;
+ char *myname;
+ static int count;
+ NTSTATUS nt_status;
+
+ server = talloc_strdup(mem_ctx, share+2);
+ share = strchr_m(server,'\\');
+ if (!share) return NULL;
+ *share = 0;
+ share++;
+
+ server_n = server;
+
+ if (!got_pass) {
+ char *pass = getpass("Password: ");
+ if (pass) {
+ password = talloc_strdup(mem_ctx, pass);
+ }
+ }
+
+ myname = talloc_asprintf(mem_ctx, "lock-%u-%u", getpid(), count++);
+
+ nt_status = smbcli_full_connection(NULL,
+ &c, myname, server_n, ports, share, NULL,
+ username, lp_workgroup(), password, ev,
+ options, session_options, gensec_settings);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("smbcli_full_connection failed with error %s\n", nt_errstr(nt_status)));
+ return NULL;
+ }
+
+ c->use_oplocks = use_oplocks;
+
+ return c;
+}
+
+
+static void reconnect(TALLOC_CTX *mem_ctx,
+ struct smbcli_state *cli[NSERVERS][NCONNECTIONS],
+ char *nfs[NSERVERS],
+ int fnum[NSERVERS][NUMFSTYPES][NCONNECTIONS][NFILES],
+ const char **ports,
+ struct smbcli_options *options,
+ struct smbcli_session_options *session_options,
+ struct gensec_settings *gensec_settings,
+ struct tevent_context *ev,
+ char *share1, char *share2)
+{
+ int server, conn, f, fstype;
+ char *share[2];
+ share[0] = share1;
+ share[1] = share2;
+
+ fstype = FSTYPE_SMB;
+
+ for (server=0;server<NSERVERS;server++)
+ for (conn=0;conn<NCONNECTIONS;conn++) {
+ if (cli[server][conn]) {
+ for (f=0;f<NFILES;f++) {
+ smbcli_close(cli[server][conn], fnum[server][fstype][conn][f]);
+ }
+ smbcli_ulogoff(cli[server][conn]);
+ talloc_free(cli[server][conn]);
+ }
+ cli[server][conn] = connect_one(mem_ctx, share[server], ports, options, session_options, gensec_settings, ev);
+ 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],
+ char *nfs[NSERVERS],
+ int fnum[NSERVERS][NUMFSTYPES][NCONNECTIONS][NFILES],
+ struct record *rec)
+{
+ uint_t conn = rec->conn;
+ uint_t f = rec->f;
+ uint_t fstype = rec->fstype;
+ uint_t start = rec->start;
+ uint_t len = rec->len;
+ uint_t r1 = rec->r1;
+ uint_t r2 = rec->r2;
+ enum brl_type op;
+ int server;
+ bool ret[NSERVERS];
+
+ if (r1 < READ_PCT) {
+ op = READ_LOCK;
+ } else {
+ op = WRITE_LOCK;
+ }
+
+ if (r2 < LOCK_PCT) {
+ /* set a lock */
+ for (server=0;server<NSERVERS;server++) {
+ ret[server] = try_lock(cli[server][conn], fstype,
+ fnum[server][fstype][conn][f],
+ start, len, op);
+ }
+ if (showall || ret[0] != ret[1]) {
+ printf("lock conn=%u fstype=%u f=%u range=%u:%u(%u) op=%s -> %u:%u\n",
+ conn, fstype, f,
+ start, start+len-1, len,
+ op==READ_LOCK?"READ_LOCK":"WRITE_LOCK",
+ ret[0], ret[1]);
+ }
+ if (ret[0] != ret[1]) return false;
+ } else if (r2 < LOCK_PCT+UNLOCK_PCT) {
+ /* unset a lock */
+ for (server=0;server<NSERVERS;server++) {
+ ret[server] = try_unlock(cli[server][conn], fstype,
+ fnum[server][fstype][conn][f],
+ start, len);
+ }
+ if (showall || (!hide_unlock_fails && (ret[0] != ret[1]))) {
+ printf("unlock conn=%u fstype=%u f=%u range=%u:%u(%u) -> %u:%u\n",
+ conn, fstype, f,
+ start, start+len-1, len,
+ ret[0], ret[1]);
+ }
+ if (!hide_unlock_fails && ret[0] != ret[1]) return false;
+ } else {
+ /* reopen the file */
+ for (server=0;server<NSERVERS;server++) {
+ try_close(cli[server][conn], fstype, fnum[server][fstype][conn][f]);
+ fnum[server][fstype][conn][f] = try_open(cli[server][conn], nfs[server], fstype, FILENAME,
+ O_RDWR|O_CREAT);
+ if (fnum[server][fstype][conn][f] == -1) {
+ printf("failed to reopen on share1\n");
+ return false;
+ }
+ }
+ if (showall) {
+ printf("reopen conn=%u fstype=%u f=%u\n",
+ conn, fstype, f);
+ }
+ }
+ return true;
+}
+
+static void close_files(struct smbcli_state *cli[NSERVERS][NCONNECTIONS],
+ char *nfs[NSERVERS],
+ int fnum[NSERVERS][NUMFSTYPES][NCONNECTIONS][NFILES])
+{
+ int server, conn, f, fstype;
+
+ for (server=0;server<NSERVERS;server++)
+ for (fstype=0;fstype<NUMFSTYPES;fstype++)
+ for (conn=0;conn<NCONNECTIONS;conn++)
+ for (f=0;f<NFILES;f++) {
+ if (fnum[server][fstype][conn][f] != -1) {
+ try_close(cli[server][conn], fstype, fnum[server][fstype][conn][f]);
+ fnum[server][fstype][conn][f] = -1;
+ }
+ }
+ for (server=0;server<NSERVERS;server++) {
+ smbcli_unlink(cli[server][0], FILENAME);
+ }
+}
+
+static void open_files(struct smbcli_state *cli[NSERVERS][NCONNECTIONS],
+ char *nfs[NSERVERS],
+ int fnum[NSERVERS][NUMFSTYPES][NCONNECTIONS][NFILES])
+{
+ int server, fstype, conn, f;
+
+ for (server=0;server<NSERVERS;server++)
+ for (fstype=0;fstype<NUMFSTYPES;fstype++)
+ for (conn=0;conn<NCONNECTIONS;conn++)
+ for (f=0;f<NFILES;f++) {
+ fnum[server][fstype][conn][f] = try_open(cli[server][conn], nfs[server], fstype, FILENAME,
+ O_RDWR|O_CREAT);
+ if (fnum[server][fstype][conn][f] == -1) {
+ fprintf(stderr,"Failed to open fnum[%u][%u][%u][%u]\n",
+ server, fstype, conn, f);
+ exit(1);
+ }
+ }
+}
+
+
+static int retest(struct smbcli_state *cli[NSERVERS][NCONNECTIONS],
+ char *nfs[NSERVERS],
+ int fnum[NSERVERS][NUMFSTYPES][NCONNECTIONS][NFILES],
+ int n)
+{
+ int i;
+ printf("testing %u ...\n", n);
+ for (i=0; i<n; i++) {
+ if (i && i % 100 == 0) {
+ printf("%u\n", i);
+ }
+
+ if (recorded[i].needed &&
+ !test_one(cli, nfs, fnum, &recorded[i])) return i;
+ }
+ return n;
+}
+
+
+/* each server has two connections open to it. Each connection has two file
+ descriptors open on the file - 8 file descriptors in total
+
+ we then do random locking ops in tamdem on the 4 fnums from each
+ server and ensure that the results match
+ */
+static void test_locks(TALLOC_CTX *mem_ctx, char *share1, char *share2,
+ char *nfspath1, char *nfspath2,
+ const char **ports,
+ struct smbcli_options *options,
+ struct smbcli_session_options *session_options,
+ struct gensec_settings *gensec_settings,
+ struct tevent_context *ev)
+{
+ struct smbcli_state *cli[NSERVERS][NCONNECTIONS];
+ char *nfs[NSERVERS];
+ int fnum[NSERVERS][NUMFSTYPES][NCONNECTIONS][NFILES];
+ int n, i, n1;
+
+ nfs[0] = nfspath1;
+ nfs[1] = nfspath2;
+
+ ZERO_STRUCT(fnum);
+ ZERO_STRUCT(cli);
+
+ recorded = malloc_array_p(struct record, numops);
+
+ for (n=0; n<numops; n++) {
+ recorded[n].conn = random() % NCONNECTIONS;
+ recorded[n].fstype = random() % NUMFSTYPES;
+ recorded[n].f = random() % NFILES;
+ recorded[n].start = LOCKBASE + ((uint_t)random() % (LOCKRANGE-1));
+ recorded[n].len = 1 +
+ random() % (LOCKRANGE-(recorded[n].start-LOCKBASE));
+ recorded[n].start *= RANGE_MULTIPLE;
+ recorded[n].len *= RANGE_MULTIPLE;
+ recorded[n].r1 = random() % 100;
+ recorded[n].r2 = random() % 100;
+ recorded[n].needed = true;
+ }
+
+ reconnect(mem_ctx, cli, nfs, fnum, ports, options, session_options, gensec_settings, ev, share1, share2);
+ open_files(cli, nfs, fnum);
+ n = retest(cli, nfs, fnum, numops);
+
+ if (n == numops || !analyze) return;
+ n++;
+
+ while (1) {
+ n1 = n;
+
+ close_files(cli, nfs, fnum);
+ reconnect(mem_ctx, cli, nfs, fnum, ports, options, session_options, ev, share1, share2);
+ open_files(cli, nfs, fnum);
+
+ for (i=0;i<n-1;i++) {
+ int m;
+ recorded[i].needed = false;
+
+ close_files(cli, nfs, fnum);
+ open_files(cli, nfs, fnum);
+
+ m = retest(cli, nfs, fnum, n);
+ if (m == n) {
+ recorded[i].needed = true;
+ } else {
+ if (i < m) {
+ memmove(&recorded[i], &recorded[i+1],
+ (m-i)*sizeof(recorded[0]));
+ }
+ n = m;
+ i--;
+ }
+ }
+
+ if (n1 == n) break;
+ }
+
+ close_files(cli, nfs, fnum);
+ reconnect(mem_ctx, cli, nfs, fnum, ports, options, session_options, gensec_settings, ev, share1, share2);
+ open_files(cli, nfs, fnum);
+ showall = true;
+ n1 = retest(cli, nfs, fnum, n);
+ if (n1 != n-1) {
+ printf("ERROR - inconsistent result (%u %u)\n", n1, n);
+ }
+ close_files(cli, nfs, fnum);
+
+ for (i=0;i<n;i++) {
+ printf("{%u, %u, %u, %u, %u, %u, %u, %u},\n",
+ recorded[i].r1,
+ recorded[i].r2,
+ recorded[i].conn,
+ recorded[i].fstype,
+ recorded[i].f,
+ recorded[i].start,
+ recorded[i].len,
+ recorded[i].needed);
+ }
+}
+
+
+
+static void usage(void)
+{
+ printf(
+"Usage:\n\
+ locktest //server1/share1 //server2/share2 /path1 /path2 [options..]\n\
+ options:\n\
+ -U user%%pass\n\
+ -s seed\n\
+ -o numops\n\
+ -u hide unlock fails\n\
+ -a (show all ops)\n\
+ -O use oplocks\n\
+");
+}
+
+/****************************************************************************
+ main program
+****************************************************************************/
+ int main(int argc,char *argv[])
+{
+ char *share1, *share2, *nfspath1, *nfspath2;
+ extern char *optarg;
+ extern int optind;
+ struct smbcli_options options;
+ struct smbcli_session_options session_options;
+ TALLOC_CTX *mem_ctx;
+ int opt;
+ char *p;
+ int seed;
+ struct loadparm_context *lp_ctx;
+ struct tevent_context *ev;
+
+ mem_ctx = talloc_autofree_context();
+
+ setlinebuf(stdout);
+
+ dbf = x_stderr;
+
+ if (argc < 5 || argv[1][0] == '-') {
+ usage();
+ exit(1);
+ }
+
+ share1 = argv[1];
+ share2 = argv[2];
+ nfspath1 = argv[3];
+ nfspath2 = argv[4];
+
+ all_string_sub(share1,"/","\\",0);
+ all_string_sub(share2,"/","\\",0);
+
+ setup_logging(argv[0], DEBUG_STDOUT);
+
+ argc -= 4;
+ argv += 4;
+
+ lp_ctx = loadparm_init(mem_ctx);
+ lp_load(lp_ctx, dyn_CONFIGFILE);
+
+ if (getenv("USER")) {
+ username = talloc_strdup(mem_ctx, getenv("USER"));
+ }
+
+ seed = time(NULL);
+
+ while ((opt = getopt(argc, argv, "U:s:ho:aAW:O")) != EOF) {
+ switch (opt) {
+ case 'U':
+ username = talloc_strdup(mem_ctx, optarg);
+ p = strchr_m(username,'%');
+ if (p) {
+ *p = 0;
+ password = talloc_strdup(mem_ctx, p+1);
+ got_pass = 1;
+ }
+ break;
+ case 's':
+ seed = atoi(optarg);
+ break;
+ case 'u':
+ hide_unlock_fails = true;
+ break;
+ case 'o':
+ numops = atoi(optarg);
+ break;
+ case 'O':
+ use_oplocks = true;
+ break;
+ case 'a':
+ showall = true;
+ break;
+ case 'A':
+ analyze = true;
+ break;
+ case 'h':
+ usage();
+ exit(1);
+ default:
+ printf("Unknown option %c (%d)\n", (char)opt, opt);
+ exit(1);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ DEBUG(0,("seed=%u\n", seed));
+ srandom(seed);
+
+ ev = s4_event_context_init(mem_ctx);
+
+ locking_init(1);
+ lp_smbcli_options(lp_ctx, &options);
+ lp_smbcli_session_options(lp_ctx, &session_options);
+ test_locks(mem_ctx, share1, share2, nfspath1, nfspath2,
+ lp_smb_ports(lp_ctx),
+ &options, &session_options, lp_gensec_settings(lp_ctx), ev);
+
+ return(0);
+}
diff --git a/source4/torture/man/gentest.1.xml b/source4/torture/man/gentest.1.xml
new file mode 100644
index 0000000000..8f7f314ec6
--- /dev/null
+++ b/source4/torture/man/gentest.1.xml
@@ -0,0 +1,160 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE refentry PUBLIC "-//Samba-Team//DTD DocBook V4.2-Based Variant V1.0//EN" "http://www.samba.org/samba/DTD/samba-doc">
+
+<refentry id="gentest.1">
+
+<refmeta>
+ <refentrytitle>gentest</refentrytitle>
+ <manvolnum>1</manvolnum>
+</refmeta>
+
+
+<refnamediv>
+ <refname>gentest</refname>
+ <refpurpose>Run random generic SMB operations against two SMB servers
+ and show the differences in behavior</refpurpose>
+</refnamediv>
+
+<refsynopsisdiv>
+ <cmdsynopsis>
+ <command>gentest</command>
+ <arg choice="req">//server1/share1</arg>
+ <arg choice="req">//server2/share2</arg>
+ <arg choice="req">-U user%pass</arg>
+ <arg choice="req">-U user%pass</arg>
+ <arg choice="opt">-s seed</arg>
+ <arg choice="opt">-o numops</arg>
+ <arg choice="opt">-a</arg>
+ <arg choice="opt">-A</arg>
+ <arg choice="opt">-i FILE</arg>
+ <arg choice="opt">-O</arg>
+ <arg choice="opt">-S FILE</arg>
+ <arg choice="opt">-L</arg>
+ <arg choice="opt">-F</arg>
+ <arg choice="opt">-C</arg>
+ <arg choice="opt">-X</arg>
+ </cmdsynopsis>
+
+</refsynopsisdiv>
+
+<refsect1>
+ <title>DESCRIPTION</title>
+
+ <para><application>gentest</application> is a utility for
+ detecting differences in behaviour between SMB servers.
+ It will run a random set of generic operations against
+ <parameter>//server1/share1</parameter> and then the same
+ random set against <parameter>//server2/share2</parameter>
+ and display the differences in the responses it gets.
+ </para>
+
+ <para>
+ This utility is used by the Samba team to find differences in
+ behaviour between Samba and Windows servers.
+ </para>
+</refsect1>
+
+
+<refsect1>
+ <title>OPTIONS</title>
+
+ <variablelist>
+ <varlistentry>
+ <term>-U user%pass</term>
+ <listitem><para>
+ Specify the user and password to use when logging on
+ on the shares. This parameter is mandatory and has to
+ be specified twice.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-s seed</term>
+ <listitem><para>
+ Seed the random number generator with the specified value.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-o numops</term>
+ <listitem><para>Set the number of operations to perform.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-a</term>
+ <listitem><para>Print the operations that are performed. </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-A</term>
+ <listitem><para>Backtrack to find minimal number of operations
+ required to make the response to a certain call differ.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-i FILE</term>
+ <listitem><para>
+ 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.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-O</term>
+ <listitem><para>Enable oplocks.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-S FILE</term>
+ <listitem><para>Set preset seeds file. The default is <filename>gentest_seeds.dat</filename>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-L</term>
+ <listitem><para>Use preset seeds</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-F</term>
+ <listitem><para>Fast reconnect (just close files)</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-C</term>
+ <listitem><para>Continuous analysis mode</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-X</term>
+ <listitem><para>Analyse even when the test succeeded.</para></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect1>
+
+<refsect1>
+ <title>VERSION</title>
+
+ <para>This man page is correct for version 4.0 of the Samba suite.</para>
+</refsect1>
+
+<refsect1>
+ <title>SEE ALSO</title>
+
+ <para>Samba</para>
+
+</refsect1>
+
+<refsect1>
+ <title>AUTHOR</title>
+
+ <para>This utility is part of the <ulink url="http://www.samba.org/">Samba</ulink> suite, which is developed by the global <ulink url="http://www.samba.org/samba/team/">Samba Team</ulink>.</para>
+
+ <para>gentest was written by Andrew Tridgell.</para>
+
+ <para>This manpage was written by Jelmer Vernooij.</para>
+
+</refsect1>
+
+</refentry>
diff --git a/source4/torture/man/locktest.1.xml b/source4/torture/man/locktest.1.xml
new file mode 100644
index 0000000000..37d056af53
--- /dev/null
+++ b/source4/torture/man/locktest.1.xml
@@ -0,0 +1,157 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE refentry PUBLIC "-//Samba-Team//DTD DocBook V4.2-Based Variant V1.0//EN" "http://www.samba.org/samba/DTD/samba-doc">
+<refentry id="locktest.1">
+
+<refmeta>
+ <refentrytitle>locktest</refentrytitle>
+ <manvolnum>1</manvolnum>
+</refmeta>
+
+
+<refnamediv>
+ <refname>locktest</refname>
+ <refpurpose>Find differences in locking between two SMB servers</refpurpose>
+</refnamediv>
+
+<refsynopsisdiv>
+ <cmdsynopsis>
+ <command>locktest</command>
+ <arg choice="req">//server1/share1</arg>
+ <arg choice="req">//server2/share2</arg>
+ <arg choice="opt">-U user%pass</arg>
+ <arg choice="opt">-U user%pass</arg>
+ <arg choice="opt">-s seed</arg>
+ <arg choice="opt">-o numops</arg>
+ <arg choice="opt">-a</arg>
+ <arg choice="opt">-O</arg>
+ <arg choice="opt">-E</arg>
+ <arg choice="opt">-Z</arg>
+ <arg choice="opt">-R range</arg>
+ <arg choice="opt">-B base</arg>
+ <arg choice="opt">-M min</arg>
+ </cmdsynopsis>
+
+</refsynopsisdiv>
+
+<refsect1>
+ <title>DESCRIPTION</title>
+
+ <para><application>locktest</application> is a utility for
+ detecting differences in behaviour in locking between SMB servers.
+ It will run a random set of locking operations against
+ <parameter>//server1/share1</parameter> and then the same
+ random set against <parameter>//server2/share2</parameter>
+ and display the differences in the responses it gets.
+ </para>
+
+ <para>
+ This utility is used by the Samba team to find differences in
+ behaviour between Samba and Windows servers.
+ </para>
+</refsect1>
+
+
+<refsect1>
+ <title>OPTIONS</title>
+
+ <variablelist>
+ <varlistentry>
+ <term>-U user%pass</term>
+ <listitem><para>
+ 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).
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-s seed</term>
+ <listitem><para>
+ Seed the random number generator with the specified value.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-o numops</term>
+ <listitem><para>Set the number of operations to perform.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-a</term>
+ <listitem><para>Print the operations that are performed. </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-A</term>
+ <listitem><para>Backtrack to find minimal number of operations
+ required to make the response to a certain call differ.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-O</term>
+ <listitem><para>Enable oplocks.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-u</term>
+ <listitem><para>Hide unlock fails.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-E</term>
+ <listitem><para>enable exact error code checking</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-Z</term>
+ <listitem><para>enable the zero/zero lock</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-R range</term>
+ <listitem><para>set lock range</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-B base</term>
+ <listitem><para>set lock base</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-M min</term>
+ <listitem><para>set min lock length</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-k</term>
+ <listitem><para>Use kerberos</para></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect1>
+
+<refsect1>
+ <title>VERSION</title>
+
+ <para>This man page is correct for version 4.0 of the Samba suite.</para>
+</refsect1>
+
+<refsect1>
+ <title>SEE ALSO</title>
+
+ <para>Samba</para>
+
+</refsect1>
+
+<refsect1>
+ <title>AUTHOR</title>
+
+ <para>This utility is part of the <ulink url="http://www.samba.org/">Samba</ulink> suite, which is developed by the global <ulink url="http://www.samba.org/samba/team/">Samba Team</ulink>.</para>
+
+ <para>locktest was written by Andrew Tridgell.</para>
+
+ <para>This manpage was written by Jelmer Vernooij.</para>
+
+</refsect1>
+
+</refentry>
diff --git a/source4/torture/man/masktest.1.xml b/source4/torture/man/masktest.1.xml
new file mode 100644
index 0000000000..170450bc08
--- /dev/null
+++ b/source4/torture/man/masktest.1.xml
@@ -0,0 +1,139 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE refentry PUBLIC "-//Samba-Team//DTD DocBook V4.2-Based Variant V1.0//EN" "http://www.samba.org/samba/DTD/samba-doc">
+<refentry id="masktest.1">
+
+<refmeta>
+ <refentrytitle>masktest</refentrytitle>
+ <manvolnum>1</manvolnum>
+</refmeta>
+
+
+<refnamediv>
+ <refname>masktest</refname>
+ <refpurpose>Find differences in wildcard matching between
+ Samba's implementation and that of a remote server.</refpurpose>
+</refnamediv>
+
+<refsynopsisdiv>
+ <cmdsynopsis>
+ <command>masktest</command>
+ <arg choice="req">//server/share</arg>
+ <arg choice="opt">-U user%pass</arg>
+ <arg choice="opt">-d debuglevel</arg>
+ <arg choice="opt">-W workgroup</arg>
+ <arg choice="opt">-n numloops</arg>
+ <arg choice="opt">-s seed</arg>
+ <arg choice="opt">-a</arg>
+ <arg choice="opt">-E</arg>
+ <arg choice="opt">-M max protocol</arg>
+ <arg choice="opt">-f filechars</arg>
+ <arg choice="opt">-m maskchars</arg>
+ <arg choice="opt">-v</arg>
+ </cmdsynopsis>
+
+</refsynopsisdiv>
+
+<refsect1>
+ <title>DESCRIPTION</title>
+
+ <para><application>masktest</application> 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.
+ </para>
+
+ <para>
+ This utility is used by the Samba team to find differences in
+ behaviour between Samba and Windows servers.
+ </para>
+</refsect1>
+
+
+<refsect1>
+ <title>OPTIONS</title>
+
+ <variablelist>
+ <varlistentry>
+ <term>-U user%pass</term>
+ <listitem><para>
+ 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).
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-s seed</term>
+ <listitem><para>
+ Seed the random number generator with the specified value.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-n numops</term>
+ <listitem><para>Set the number of operations to perform.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-a</term>
+ <listitem><para>Print the operations that are performed. </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-M max_protocol</term>
+ <listitem><para>
+ Maximum protocol to use.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-f</term>
+ <listitem><para>Specify characters that can be used
+ when generating file names. Default: abcdefghijklm.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-E</term>
+ <listitem><para>Abort when difference in behaviour is found.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-m maskchars</term>
+ <listitem><para>Specify characters used for wildcards.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-v</term>
+ <listitem><para>Be verbose</para></listitem>
+ </varlistentry>
+
+ </variablelist>
+</refsect1>
+
+<refsect1>
+ <title>VERSION</title>
+
+ <para>This man page is correct for version 4.0 of the Samba suite.</para>
+</refsect1>
+
+<refsect1>
+ <title>SEE ALSO</title>
+
+ <para>Samba</para>
+
+</refsect1>
+
+<refsect1>
+ <title>AUTHOR</title>
+
+ <para>This utility is part of the <ulink url="http://www.samba.org/">Samba</ulink> suite, which is developed by the global <ulink url="http://www.samba.org/samba/team/">Samba Team</ulink>.</para>
+
+ <para>masktest was written by Andrew Tridgell.</para>
+
+ <para>This manpage was written by Jelmer Vernooij.</para>
+
+</refsect1>
+
+</refentry>
diff --git a/source4/torture/man/smbtorture.1.xml b/source4/torture/man/smbtorture.1.xml
new file mode 100644
index 0000000000..44984b00f5
--- /dev/null
+++ b/source4/torture/man/smbtorture.1.xml
@@ -0,0 +1,259 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE refentry PUBLIC "-//Samba-Team//DTD DocBook V4.2-Based Variant V1.0//EN" "http://www.samba.org/samba/DTD/samba-doc">
+<refentry id="smbtorture.1">
+
+<refmeta>
+ <refentrytitle>smbtorture</refentrytitle>
+ <manvolnum>1</manvolnum>
+</refmeta>
+
+
+<refnamediv>
+ <refname>smbtorture</refname>
+ <refpurpose>Run a series of tests against a SMB server</refpurpose>
+</refnamediv>
+
+<refsynopsisdiv>
+ <cmdsynopsis>
+ <command>smbtorture</command>
+ </cmdsynopsis>
+
+ <cmdsynopsis>
+ <command>smbtorture</command>
+ <arg choice="req">//server/share</arg>
+ <arg choice="opt">-d debuglevel</arg>
+ <arg choice="opt">-U user%pass</arg>
+ <arg choice="opt">-k</arg>
+ <arg choice="opt">-N numprocs</arg>
+ <arg choice="opt">-n netbios_name</arg>
+ <arg choice="opt">-W workgroup</arg>
+ <arg choice="opt">-o num_operations</arg>
+ <arg choice="opt">-e num files(entries)</arg>
+ <arg choice="opt">-O socket_options</arg>
+ <arg choice="opt">-m maximum_protocol</arg>
+ <arg choice="opt">-L</arg>
+ <arg choice="opt">-c CLIENT.TXT</arg>
+ <arg choice="opt">-t timelimit</arg>
+ <arg choice="opt">-C filename</arg>
+ <arg choice="opt">-A</arg>
+ <arg choice="opt">-p port</arg>
+ <arg choice="opt">-s seed</arg>
+ <arg choice="opt">-f max_failures</arg>
+ <arg choice="opt">-X</arg>
+ <arg choice="req">BINDING-STRING|UNC</arg>
+ <arg choice="req">TEST1</arg>
+ <arg choice="opt">TEST2</arg>
+ <arg choice="opt">...</arg>
+ </cmdsynopsis>
+
+</refsynopsisdiv>
+
+<refsect1>
+ <title>DESCRIPTION</title>
+
+ <para>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.
+ </para>
+
+ <para>Any number of tests can be specified
+ on the command-line. If no tests are specified, all tests
+ are run. </para>
+
+ <para>If no arguments are specified at all, all available options
+ and tests are listed.</para>
+
+ <refsect2>
+ <title>Binding string format</title>
+
+ <para>The binding string format is:</para>
+
+ <para>TRANSPORT:host[flags]</para>
+
+ <para>Where TRANSPORT is either ncacn_np for SMB, ncacn_ip_tcp for RPC/TCP or ncalrpc for local connections.
+ </para>
+
+ <para>
+ '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.
+ </para>
+
+ <para>
+ '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.
+ </para>
+
+ <para>
+ other recognised flags are:
+ </para>
+
+ <variablelist>
+ <varlistentry><term>sign</term>
+ <listitem><para>enable ntlmssp signing</para></listitem>
+ </varlistentry>
+
+ <varlistentry><term>seal</term>
+ <listitem><para>enable ntlmssp sealing</para></listitem>
+ </varlistentry>
+
+ <varlistentry><term>connect</term>
+ <listitem><para>enable rpc connect level auth (auth, but no sign or seal)</para></listitem>
+ </varlistentry>
+
+ <varlistentry><term>validate</term>
+ <listitem><para>enable the NDR validator</para></listitem>
+ </varlistentry>
+
+ <varlistentry><term>print</term>
+ <listitem><para>enable debugging of the packets</para></listitem>
+ </varlistentry>
+
+ <varlistentry><term>bigendian</term>
+ <listitem><para>use bigendian RPC</para></listitem>
+ </varlistentry>
+
+ <varlistentry><term>padcheck</term>
+ <listitem><para>check reply data for non-zero pad bytes</para></listitem>
+ </varlistentry>
+ </variablelist>
+
+ <para>For example, these all connect to the samr pipe:</para>
+
+ <itemizedlist>
+ <listitem><para>ncacn_np:myserver</para></listitem>
+ <listitem><para>ncacn_np:myserver[samr]</para></listitem>
+ <listitem><para>ncacn_np:myserver[\\pipe\\samr]</para></listitem>
+ <listitem><para>ncacn_np:myserver[/pipe/samr]</para></listitem>
+ <listitem><para>ncacn_np:myserver[samr,sign,print]</para></listitem>
+ <listitem><para>ncacn_np:myserver[\\pipe\\samr,sign,seal,bigendian]</para></listitem>
+ <listitem><para>ncacn_np:myserver[/pipe/samr,seal,validate]</para></listitem>
+ <listitem><para>ncacn_np:</para></listitem>
+ <listitem><para>ncacn_np:[/pipe/samr]</para></listitem>
+ <listitem><para>ncacn_ip_tcp:myserver</para></listitem>
+ <listitem><para>ncacn_ip_tcp:myserver[1024]</para></listitem>
+ <listitem><para>ncacn_ip_tcp:myserver[1024,sign,seal]</para></listitem>
+ <listitem><para>ncalrpc:</para></listitem>
+ </itemizedlist>
+
+ </refsect2>
+
+ <refsect2>
+ <title>UNC Format</title>
+
+ <para>The UNC format is:</para>
+
+ <para>//server/share</para>
+ </refsect2>
+
+</refsect1>
+
+
+<refsect1>
+ <title>OPTIONS</title>
+
+ <variablelist>
+ <varlistentry><term>-d debuglevel</term>
+ <listitem><para>Use the specified Samba debug level. A higher debug level
+ means more output.</para></listitem>
+ </varlistentry>
+ <varlistentry><term>-U user%pass</term>
+ <listitem><para>Use the specified username/password combination when logging in to a remote server.</para></listitem>
+ </varlistentry>
+ <varlistentry><term>-k</term>
+ <listitem><para>Use kerberos when authenticating.</para></listitem>
+ </varlistentry>
+ <varlistentry><term>-W workgroup</term>
+ <listitem><para>Use specified name as our workgroup name.</para></listitem>
+ </varlistentry>
+ <varlistentry><term>-n netbios_name</term>
+ <listitem><para>Use specified name as our NetBIOS name.</para></listitem>
+ </varlistentry>
+
+ <varlistentry><term>-O socket_options</term>
+ <listitem><para>Use specified socket options, equivalent of the smb.conf option <quote>socket options</quote>. See the smb.conf(5) manpage for details.</para></listitem>
+ </varlistentry>
+
+ <varlistentry><term>-m max_protocol</term>
+ <listitem><para>Specify the maximum SMB dialect that should be used. Possible values are: CORE, COREPLUS, LANMAN1, LANMAN2, NT1</para></listitem>
+ </varlistentry>
+
+ <varlistentry><term>-s seed</term>
+ <listitem><para>Initialize the randomizer using <replaceable>seed</replaceable> as seed.</para></listitem>
+ </varlistentry>
+
+ <varlistentry><term>-L</term>
+ <listitem><para>Use oplocks.</para></listitem>
+ </varlistentry>
+
+ <varlistentry><term>-X</term>
+ <listitem><para>Enable dangerous tests. Use with care! This might crash your server...</para></listitem>
+ </varlistentry>
+
+ <varlistentry><term>-t timelimit</term>
+ <listitem><para>Specify the NBENCH time limit in seconds. Defaults to 600.</para></listitem>
+ </varlistentry>
+
+ <varlistentry><term>-p ports</term>
+ <listitem><para>Specify ports to connect to.</para></listitem>
+ </varlistentry>
+
+ <varlistentry><term>-c file</term>
+ <listitem><para>Read NBENCH commands from <replaceable>file</replaceable> instead of from CLIENT.TXT.</para></listitem>
+ </varlistentry>
+
+ <varlistentry><term>-A</term>
+ <listitem><para>Show not just OK or FAILED but more detailed
+ output. Used only by DENY test at the moment.</para></listitem>
+ </varlistentry>
+
+ <varlistentry><term>-C filename</term>
+ <listitem><para>Load a list of UNC names from the specified filename. Smbtorture instances will connect to a random host from this list.</para></listitem>
+ </varlistentry>
+
+ <varlistentry><term>-N numprocs</term>
+ <listitem><para>Specify number of smbtorture processes to launch.</para></listitem>
+ </varlistentry>
+
+ <varlistentry><term>-o num_operations</term>
+ <listitem><para>Number of times some operations should be tried before assuming they're output is consistent (default:100).</para></listitem>
+ </varlistentry>
+
+ <varlistentry><term>-e num_files</term>
+ <listitem><para>Number of entries to use in certain tests (such as creating X files) (default: 1000).</para></listitem>
+ </varlistentry>
+
+ <varlistentry><term>-f max_failures</term>
+ <listitem><para>Number of failures before aborting a test (default: 1).</para></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect1>
+
+<refsect1>
+ <title>VERSION</title>
+
+ <para>This man page is correct for version 4.0 of the Samba suite.</para>
+</refsect1>
+
+<refsect1>
+ <title>SEE ALSO</title>
+
+ <para>Samba</para>
+
+</refsect1>
+
+<refsect1>
+ <title>AUTHOR</title>
+
+ <para>This utility is part of the <ulink url="http://www.samba.org/">Samba</ulink> suite, which is developed by the global <ulink url="http://www.samba.org/samba/team/">Samba Team</ulink>.</para>
+
+ <para>smbtorture was written by Andrew Tridgell.</para>
+
+ <para>This manpage was written by Jelmer Vernooij.</para>
+
+</refsect1>
+
+</refentry>
diff --git a/source4/torture/masktest.c b/source4/torture/masktest.c
new file mode 100644
index 0000000000..7b0251d159
--- /dev/null
+++ b/source4/torture/masktest.c
@@ -0,0 +1,391 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/cmdline/popt_common.h"
+#include "system/filesys.h"
+#include "system/dir.h"
+#include "libcli/libcli.h"
+#include "libcli/raw/libcliraw.h"
+#include "system/time.h"
+#include "auth/credentials/credentials.h"
+#include "auth/gensec/gensec.h"
+#include "param/param.h"
+#include "dynconfig/dynconfig.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(pattern, file, cli->transport->negotiate.protocol)==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 smb_iconv_convenience *iconv_convenience,
+ struct gensec_settings *gensec_settings)
+{
+ struct smbcli_state *c;
+ char *server;
+ NTSTATUS status;
+
+ server = talloc_strdup(mem_ctx, share+2);
+ share = strchr_m(server,'\\');
+ if (!share) return NULL;
+ *share = 0;
+ share++;
+
+ cli_credentials_set_workstation(cmdline_credentials, "masktest", CRED_SPECIFIED);
+
+ status = smbcli_full_connection(NULL, &c,
+ server,
+ ports,
+ share, NULL,
+ socket_options,
+ cmdline_credentials, resolve_ctx, ev,
+ options, session_options,
+ iconv_convenience,
+ 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 = talloc_strdup(mem_ctx, last_hit.short_name);
+ strlower(*short_name);
+ *long_name = talloc_strdup(mem_ctx, last_hit.long_name);
+ strlower(*long_name);
+ }
+
+ if (*short_name == '\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++;
+
+ safe_strcpy(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);
+ safe_strcpy(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(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;i<l1;i++) {
+ mask[i+l] = maskchars[random() % mc_len];
+ }
+ mask[l+l1] = 0;
+
+ for (i=0;i<l2;i++) {
+ file[i+l] = filechars[random() % fc_len];
+ }
+ file[l+l2] = 0;
+
+ if (ISDOT(file+l) || ISDOTDOT(file+l) || ISDOTDOT(mask+l)) {
+ continue;
+ }
+
+ if (strspn(file+l, ".") == strlen(file+l)) continue;
+
+ testpair(mem_ctx, cli, mask, file);
+ if (NumLoops && (--NumLoops == 0))
+ break;
+ }
+
+ finished:
+ smbcli_rmdir(cli->tree, "\\masktest");
+ talloc_free(mem_ctx);
+}
+
+
+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,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;
+ 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
+ { NULL }
+ };
+
+ setlinebuf(stdout);
+ seed = time(NULL);
+
+ pc = poptGetContext("locktest", argc, (const char **) argv, long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+
+ poptSetOtherOptionHelp(pc, "<unc>");
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case OPT_UNCLIST:
+ lp_set_cmdline(cmdline_lp_ctx, "torture:unclist", poptGetOptArg(pc));
+ break;
+ }
+ }
+
+ argv_new = discard_const_p(char *, poptGetArgs(pc));
+ argc_new = argc;
+ for (i=0; i<argc; i++) {
+ if (argv_new[i] == NULL) {
+ argc_new = i;
+ break;
+ }
+ }
+
+ if (!(argc_new >= 2)) {
+ usage(pc);
+ exit(1);
+ }
+
+ setup_logging("masktest", DEBUG_STDOUT);
+
+ share = argv_new[1];
+
+ all_string_sub(share,"/","\\",0);
+
+ lp_ctx = cmdline_lp_ctx;
+
+ mem_ctx = talloc_autofree_context();
+
+ ev = s4_event_context_init(mem_ctx);
+
+ gensec_init(lp_ctx);
+
+ lp_smbcli_options(lp_ctx, &options);
+ lp_smbcli_session_options(lp_ctx, &session_options);
+
+ cli = connect_one(lp_resolve_context(lp_ctx), ev, mem_ctx, share,
+ lp_smb_ports(lp_ctx), lp_socket_options(lp_ctx),
+ &options, &session_options,
+ lp_iconv_convenience(lp_ctx),
+ lp_gensec_settings(mem_ctx, lp_ctx));
+ if (!cli) {
+ DEBUG(0,("Failed to connect to %s\n", share));
+ 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);
+
+ return(0);
+}
diff --git a/source4/torture/nbench/nbench.c b/source4/torture/nbench/nbench.c
new file mode 100644
index 0000000000..5a4037f906
--- /dev/null
+++ b/source4/torture/nbench/nbench.c
@@ -0,0 +1,293 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/libcli.h"
+#include "torture/util.h"
+#include "torture/smbtorture.h"
+#include "system/filesys.h"
+#include "system/locale.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<nb_max_retries;n++) {
+ if (nb_reconnect(cli, tctx, client)) {
+ printf("[%d] Reconnected client %d\n", nbench_line_count, client);
+ return;
+ }
+ }
+ printf("[%d] Failed to reconnect client %d\n", nbench_line_count, client);
+ nb_exit(1);
+}
+
+/* run a test that simulates an approximate netbench client load */
+static bool run_netbench(struct torture_context *tctx, struct smbcli_state *cli, int client)
+{
+ int torture_nprocs = torture_setting_int(tctx, "nprocs", 4);
+ int i;
+ char line[1024];
+ char *cname;
+ FILE *f;
+ bool correct = true;
+ double target_rate = torture_setting_double(tctx, "targetrate", 0);
+ int n;
+
+ if (target_rate != 0 && client == 0) {
+ printf("Targetting %.4f MByte/sec\n", target_rate);
+ }
+
+ nb_setup(cli, client);
+
+ if (torture_nprocs == 1) {
+ if (!read_only) {
+ NB_RETRY(torture_setup_dir(cli, "\\clients"));
+ }
+ }
+
+ asprintf(&cname, "client%d", client+1);
+
+ f = fopen(loadfile, "r");
+
+ if (!f) {
+ perror(loadfile);
+ return false;
+ }
+
+again:
+ nbio_time_reset();
+
+ while (fgets(line, sizeof(line)-1, f)) {
+ NTSTATUS status;
+ const char **params0, **params;
+
+ nbench_line_count++;
+
+ line[strlen(line)-1] = 0;
+
+ all_string_sub(line,"client1", cname, sizeof(line));
+
+ params = params0 = 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) {
+ status = NT_STATUS(strtoul(params[i-1], NULL, 16));
+ } 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 childs 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(void)
+{
+ struct torture_suite *suite =
+ torture_suite_create(
+ talloc_autofree_context(),
+ "BENCH");
+
+ torture_suite_add_simple_test(suite, "NBENCH", torture_nbench);
+
+ suite->description = talloc_strdup(suite, "Benchmarks");
+
+ torture_register_suite(suite);
+ return NT_STATUS_OK;
+}
diff --git a/source4/torture/nbench/nbio.c b/source4/torture/nbench/nbio.c
new file mode 100644
index 0000000000..bf594088cd
--- /dev/null
+++ b/source4/torture/nbench/nbio.c
@@ -0,0 +1,997 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/time.h"
+#include "system/filesys.h"
+#include "../lib/util/dlinklist.h"
+#include "libcli/libcli.h"
+#include "libcli/raw/libcliraw.h"
+#include "torture/torture.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;
+ uint_t create_options;
+ uint_t 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,
+ uint_t create_options,
+ uint_t 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,
+ uint_t create_options,
+ uint_t 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) {
+ 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) {
+ 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<nprocs;i++) {
+ total += children[i].bytes - children[i].warmup_bytes;
+ }
+ return 1.0e-6 * total / timeval_elapsed2(&tv_start, &tv_end);
+}
+
+double nbio_latency(void)
+{
+ int i;
+ double max_latency = 0;
+ for (i=0;i<nprocs;i++) {
+ if (children[i].max_latency > 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;i<nprocs;i++) {
+ if (children[i].connected) {
+ num_connected++;
+ }
+ if (children[i].bytes == 0) {
+ in_warmup = 1;
+ }
+ lines += children[i].line;
+ }
+
+ t = timeval_elapsed(&tv_start);
+
+ if (!in_warmup && warmup>0 && t > warmup) {
+ tv_start = timeval_current();
+ warmup = 0;
+ for (i=0;i<nprocs;i++) {
+ children[i].warmup_bytes = children[i].bytes;
+ }
+ goto next;
+ }
+ if (t < warmup) {
+ in_warmup = 1;
+ } else if (!in_warmup && !in_cleanup && t > timelimit) {
+ for (i=0;i<nprocs;i++) {
+ children[i].done = 1;
+ }
+ tv_end = timeval_current();
+ in_cleanup = 1;
+ }
+ if (t < 1) {
+ goto next;
+ }
+ if (!in_cleanup) {
+ tv_end = timeval_current();
+ }
+
+ if (in_warmup) {
+ printf("%4d %8d %.2f MB/sec warmup %.0f sec \n",
+ num_connected, lines/nprocs,
+ nbio_result(), t);
+ } else if (in_cleanup) {
+ printf("%4d %8d %.2f MB/sec cleanup %.0f sec \n",
+ num_connected, lines/nprocs,
+ nbio_result(), t);
+ } else {
+ printf("%4d %8d %.2f MB/sec execute %.0f sec latency %.2f msec \n",
+ num_connected, lines/nprocs,
+ nbio_result(), t, nbio_latency() * 1.0e3);
+ }
+
+ fflush(stdout);
+next:
+ signal(SIGALRM, nb_alarm);
+ alarm(1);
+}
+
+void nbio_shmem(int n, int t_timelimit, int t_warmup)
+{
+ nprocs = n;
+ children = shm_setup(sizeof(*children) * nprocs);
+ if (!children) {
+ printf("Failed to setup shared memory!\n");
+ nb_exit(1);
+ }
+ memset(children, 0, sizeof(*children) * nprocs);
+ timelimit = t_timelimit;
+ warmup = t_warmup;
+ in_cleanup = 0;
+ tv_start = timeval_current();
+}
+
+static struct lock_info* find_lock(struct lock_info *linfo, off_t offset, int size)
+{
+ while (linfo != NULL) {
+ if (linfo->offset == 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,
+ uint_t create_options,
+ uint_t create_disposition,
+ int handle,
+ NTSTATUS status,
+ bool retry)
+{
+ union smb_open io;
+ uint32_t desired_access;
+ NTSTATUS ret;
+ TALLOC_CTX *mem_ctx;
+ uint_t 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 = 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, struct ftable *);
+ }
+
+ f->handle = handle;
+ f->fd = io.ntcreatex.out.file.fnum;
+
+ return true;
+}
+
+bool nb_createx(const char *fname,
+ uint_t create_options, uint_t 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, struct lock_info *);
+ }
+
+ 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, uint_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 = 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/browse.c b/source4/torture/nbt/browse.c
new file mode 100644
index 0000000000..a0e7b7d9ca
--- /dev/null
+++ b/source4/torture/nbt/browse.c
@@ -0,0 +1,54 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Browse service
+
+ (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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "librpc/gen_ndr/nbt.h"
+#include "libcli/resolve/resolve.h"
+#include "torture/torture.h"
+
+/*
+ test nbt dgram operations
+*/
+bool torture_nbt_browse(struct torture_context *torture)
+{
+ const char *address;
+ struct nbt_name name;
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+ NTSTATUS status;
+ bool ret = true;
+
+ name.name = lp_workgroup();
+ name.type = NBT_NAME_BROWSER;
+ name.scope = NULL;
+
+ /* do an initial name resolution to find its IP */
+ status = resolve_name(&name, mem_ctx, &address, torture->ev);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed to resolve %s - %s\n",
+ name.name, nt_errstr(status));
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ talloc_free(mem_ctx);
+
+ return ret;
+}
diff --git a/source4/torture/nbt/dgram.c b/source4/torture/nbt/dgram.c
new file mode 100644
index 0000000000..8eb315127a
--- /dev/null
+++ b/source4/torture/nbt/dgram.c
@@ -0,0 +1,665 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/dgram/libdgram.h"
+#include "librpc/gen_ndr/samr.h"
+#include "librpc/gen_ndr/ndr_nbt.h"
+#include "librpc/gen_ndr/ndr_netlogon.h"
+#include "lib/socket/socket.h"
+#include "lib/events/events.h"
+#include "torture/rpc/rpc.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(dgmslot, 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,
+ lp_iconv_convenience(tctx->lp_ctx));
+ 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 = lp_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(lp_resolve_context(tctx->lp_ctx), &name, tctx, &address, tctx->ev),
+ talloc_asprintf(tctx, "Failed to resolve %s", name.name));
+
+ load_interfaces(tctx, lp_interfaces(tctx->lp_ctx), &ifaces);
+ myaddress = talloc_strdup(dgmsock, iface_best_ip(ifaces, address));
+
+
+ socket_address = socket_address_from_strings(dgmsock, dgmsock->sock->backend_name,
+ myaddress, lp_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);
+
+ 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, lp_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) {
+ event_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,
+ lp_iconv_convenience(tctx->lp_ctx));
+ 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 = lp_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(lp_resolve_context(tctx->lp_ctx), &name, tctx, &address, tctx->ev),
+ talloc_asprintf(tctx, "Failed to resolve %s", name.name));
+
+ load_interfaces(tctx, lp_interfaces(tctx->lp_ctx), &ifaces);
+ myaddress = talloc_strdup(dgmsock, iface_best_ip(ifaces, address));
+
+ socket_address = socket_address_from_strings(dgmsock, dgmsock->sock->backend_name,
+ myaddress, lp_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);
+
+
+ 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, lp_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) {
+ event_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");
+
+ /* setup (another) temporary mailslot listener for replies */
+ dgmslot = dgram_mailslot_temp(dgmsock, NBT_MAILSLOT_GETDC,
+ netlogon_handler, 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.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, lp_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) {
+ event_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");
+
+ 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",
+ lp_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);
+
+ 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, lp_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) {
+ event_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");
+
+ /* setup (another) temporary mailslot listener for replies */
+ dgmslot = dgram_mailslot_temp(dgmsock, NBT_MAILSLOT_GETDC,
+ netlogon_handler, 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_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, lp_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) {
+ event_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");
+
+ 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, lp_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) {
+ event_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_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,
+ lp_iconv_convenience(tctx->lp_ctx));
+ 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 = lp_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(lp_resolve_context(tctx->lp_ctx), &name, tctx, &address, tctx->ev),
+ talloc_asprintf(tctx, "Failed to resolve %s", name.name));
+
+ load_interfaces(tctx, lp_interfaces(tctx->lp_ctx), &ifaces);
+ myaddress = talloc_strdup(dgmsock, iface_best_ip(ifaces, address));
+
+ socket_address = socket_address_from_strings(dgmsock, dgmsock->sock->backend_name,
+ myaddress, lp_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);
+ dom_sid = torture_join_sid(join_ctx);
+
+ torture_assert(tctx, join_ctx != NULL,
+ talloc_asprintf(tctx, "Failed to join domain %s as %s\n",
+ lp_workgroup(tctx->lp_ctx), TEST_NAME));
+
+ /* setup a temporary mailslot listener for replies */
+ dgmslot = dgram_mailslot_temp(dgmsock, NBT_MAILSLOT_GETDC,
+ netlogon_handler, 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.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, lp_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) {
+ event_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");
+
+
+ /* setup a temporary mailslot listener for replies */
+ dgmslot = dgram_mailslot_temp(dgmsock, NBT_MAILSLOT_GETDC,
+ netlogon_handler, 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.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, lp_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) {
+ event_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");
+
+
+ /* setup (another) temporary mailslot listener for replies */
+ dgmslot = dgram_mailslot_temp(dgmsock, NBT_MAILSLOT_GETDC,
+ netlogon_handler, NULL);
+
+ 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, lp_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) {
+ event_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);
+
+ 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, lp_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) {
+ event_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 0000000000..aee0c54358
--- /dev/null
+++ b/source4/torture/nbt/nbt.c
@@ -0,0 +1,67 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#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, lp_iconv_convenience(tctx->lp_ctx));
+}
+
+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(lp_resolve_context(tctx->lp_ctx), name, tctx, address, tctx->ev),
+ talloc_asprintf(tctx,
+ "Failed to resolve %s", name->name));
+
+ return true;
+}
+
+NTSTATUS torture_nbt_init(void)
+{
+ struct torture_suite *suite = torture_suite_create(
+ talloc_autofree_context(), "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(suite);
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/torture/nbt/query.c b/source4/torture/nbt/query.c
new file mode 100644
index 0000000000..b1b703a3c2
--- /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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 = lp_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);
+ }
+ }
+ }
+
+ event_loop_once(nbtsock->event_ctx);
+ }
+
+ while (num_sent != (result->num_pass + result->num_fail)) {
+ event_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 0000000000..8ddea4096e
--- /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 <http://www.gnu.org/licenses/>.
+*/
+
+#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_interfaces(tctx, lp_interfaces(tctx->lp_ctx), &ifaces);
+
+ myaddress = iface_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 = lp_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_interfaces(tctx, lp_interfaces(tctx->lp_ctx), &ifaces);
+
+ myaddress = iface_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 = lp_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 0000000000..c581a69ca7
--- /dev/null
+++ b/source4/torture/nbt/wins.c
@@ -0,0 +1,477 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "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)
+{
+ 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;
+
+ load_interfaces(tctx, lp_interfaces(tctx->lp_ctx), &ifaces);
+
+ myaddress = talloc_strdup(tctx, iface_best_ip(ifaces, address));
+
+ socket_address = socket_address_from_strings(tctx,
+ nbtsock->sock->backend_name,
+ myaddress, lp_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 = lp_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 = lp_nbt_port(tctx->lp_ctx);
+ io.in.wins_servers = str_list_make(tctx, address, NULL);
+ io.in.addresses = str_list_make(tctx, "127.64.64.1", NULL);
+ 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 = lp_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) {
+ event_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,
+ struct nbt_name_request *);
+ EVENT_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 = lp_nbt_port(tctx->lp_ctx);
+ io.in.wins_servers = (const char **)str_list_make(tctx, address, NULL);
+ io.in.addresses = (const char **)str_list_make(tctx, myaddress, NULL);
+ 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, 0);
+
+ 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 = lp_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 = lp_nbt_port(tctx->lp_ctx);
+ refresh.in.wins_servers = (const char **)str_list_make(tctx, address, NULL);
+ refresh.in.addresses = (const char **)str_list_make(tctx, myaddress, NULL);
+ 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 = lp_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 = lp_nbt_port(tctx->lp_ctx);
+ io.in.wins_servers = str_list_make(tctx, address, NULL);
+ io.in.addresses = str_list_make(tctx, "127.64.64.1", NULL);
+ 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 = lp_nbt_port(tctx->lp_ctx);
+ refresh.in.wins_servers = str_list_make(tctx, address, NULL);
+ refresh.in.addresses = str_list_make(tctx, myaddress, NULL);
+ 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 = lp_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;
+}
+
+
+
+/*
+ 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);
+
+ name.type = NBT_NAME_MASTER;
+ ret &= nbt_test_wins_name(tctx, address, &name, NBT_NODE_H, false);
+
+ ret &= nbt_test_wins_name(tctx, address, &name, NBT_NODE_H | NBT_NM_GROUP, false);
+
+ name.type = NBT_NAME_SERVER;
+ ret &= nbt_test_wins_name(tctx, address, &name, NBT_NODE_H, true);
+
+ name.type = NBT_NAME_LOGON;
+ ret &= nbt_test_wins_name(tctx, address, &name, NBT_NODE_H | NBT_NM_GROUP, false);
+
+ name.type = NBT_NAME_BROWSER;
+ ret &= nbt_test_wins_name(tctx, address, &name, NBT_NODE_H | NBT_NM_GROUP, false);
+
+ name.type = NBT_NAME_PDC;
+ ret &= nbt_test_wins_name(tctx, address, &name, NBT_NODE_H, true);
+
+ name.type = 0xBF;
+ ret &= nbt_test_wins_name(tctx, address, &name, NBT_NODE_H, true);
+
+ name.type = 0xBE;
+ ret &= nbt_test_wins_name(tctx, address, &name, NBT_NODE_H, false);
+
+ name.scope = "example";
+ name.type = 0x72;
+ ret &= nbt_test_wins_name(tctx, address, &name, NBT_NODE_H, true);
+
+ name.scope = "example";
+ name.type = 0x71;
+ ret &= nbt_test_wins_name(tctx, address, &name, NBT_NODE_H | NBT_NM_GROUP, false);
+
+ name.scope = "foo.example.com";
+ name.type = 0x72;
+ ret &= nbt_test_wins_name(tctx, address, &name, NBT_NODE_H, false);
+
+ name.name = talloc_asprintf(tctx, "_T\01-%5u.foo", r);
+ ret &= nbt_test_wins_name(tctx, address, &name, NBT_NODE_H, false);
+
+ name.name = "";
+ ret &= nbt_test_wins_name(tctx, address, &name, NBT_NODE_H, false);
+
+ name.name = talloc_asprintf(tctx, ".");
+ ret &= nbt_test_wins_name(tctx, address, &name, NBT_NODE_H, false);
+
+ name.name = talloc_asprintf(tctx, "%5u-\377\200\300FOO", r);
+ ret &= nbt_test_wins_name(tctx, address, &name, NBT_NODE_H, false);
+
+ 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 0000000000..bea3d4f9cf
--- /dev/null
+++ b/source4/torture/nbt/winsbench.c
@@ -0,0 +1,301 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#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, lp_iconv_convenience(tctx->lp_ctx));
+ 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 = lp_nbt_port(tctx->lp_ctx);
+ load_interfaces(tctx, lp_interfaces(tctx->lp_ctx), &ifaces);
+ state->my_ip = talloc_strdup(tctx, iface_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);
+ }
+ }
+ }
+
+ event_loop_once(nbtsock->event_ctx);
+ }
+
+ while (num_sent != (state->pass_count + state->fail_count)) {
+ event_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 0000000000..ca557de8fe
--- /dev/null
+++ b/source4/torture/nbt/winsreplication.c
@@ -0,0 +1,9705 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/wrepl/winsrepl.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_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 wrepl_request *req;
+ struct wrepl_socket *wrepl_socket1;
+ struct wrepl_associate associate1;
+ struct wrepl_socket *wrepl_socket2;
+ struct wrepl_associate associate2;
+ struct wrepl_pull_table pull_table;
+ 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;
+
+ if (!torture_nbt_get_name(tctx, &name, &address))
+ return false;
+
+ torture_comment(tctx, "Test if assoc_ctx is only valid on the conection it was created on\n");
+
+ wrepl_socket1 = wrepl_socket_init(tctx, tctx->ev, lp_iconv_convenience(tctx->lp_ctx));
+ wrepl_socket2 = wrepl_socket_init(tctx, tctx->ev, lp_iconv_convenience(tctx->lp_ctx));
+
+ 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 anwser 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;
+ req = wrepl_request_send(wrepl_socket2, &packet, &ctrl);
+ status = wrepl_request_recv(req, tctx, &rep_packet);
+ 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");
+ pull_table.in.assoc_ctx = 0;
+ req = wrepl_pull_table_send(wrepl_socket1, &pull_table);
+ status = wrepl_request_recv(req, tctx, &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), reson: %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), reson: %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, lp_iconv_convenience(tctx->lp_ctx));
+
+ 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;i<name->num_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, lp_iconv_convenience(tctx->lp_ctx));
+
+ 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_packet packet;
+ struct wrepl_request *req;
+
+ ZERO_STRUCT(packet);
+ packet.opcode = WREPL_OPCODE_BITS;
+ packet.assoc_ctx = associate.out.assoc_ctx;
+ packet.mess_type = WREPL_STOP_ASSOCIATION;
+ packet.message.stop.reason = 0;
+
+ req = wrepl_request_send(wrepl_socket, &packet, NULL);
+ talloc_free(req);
+
+ 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;i<pull_table.out.num_partners;i++) {
+ struct wrepl_wins_owner *partner = &pull_table.out.partners[i];
+ torture_comment(tctx, "%s max_version=%6llu min_version=%6llu type=%d\n",
+ partner->address,
+ (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;j<pull_names.out.num_names;j++) {
+ display_entry(tctx, &pull_names.out.names[j]);
+ }
+ }
+
+ torture_comment(tctx, "Close wrepl connections\n");
+ talloc_free(wrepl_socket);
+ return true;
+}
+
+struct test_wrepl_conflict_conn {
+ const char *address;
+ struct wrepl_socket *pull;
+ uint32_t pull_assoc;
+
+#define TEST_OWNER_A_ADDRESS "127.65.65.1"
+#define TEST_ADDRESS_A_PREFIX "127.0.65"
+#define TEST_OWNER_B_ADDRESS "127.66.66.1"
+#define TEST_ADDRESS_B_PREFIX "127.0.66"
+#define TEST_OWNER_X_ADDRESS "127.88.88.1"
+#define TEST_ADDRESS_X_PREFIX "127.0.88"
+
+ struct wrepl_wins_owner a, b, c, x;
+
+ struct socket_address *myaddr;
+ struct socket_address *myaddr2;
+ struct nbt_name_socket *nbtsock;
+ struct nbt_name_socket *nbtsock2;
+
+ struct nbt_name_socket *nbtsock_srv;
+ struct nbt_name_socket *nbtsock_srv2;
+
+ uint32_t addresses_best_num;
+ struct wrepl_ip *addresses_best;
+
+ uint32_t addresses_best2_num;
+ struct wrepl_ip *addresses_best2;
+
+ uint32_t addresses_all_num;
+ struct wrepl_ip *addresses_all;
+
+ uint32_t addresses_mhomed_num;
+ struct wrepl_ip *addresses_mhomed;
+};
+
+static const struct wrepl_ip addresses_A_1[] = {
+ {
+ .owner = TEST_OWNER_A_ADDRESS,
+ .ip = TEST_ADDRESS_A_PREFIX".1"
+ }
+};
+static const struct wrepl_ip addresses_A_2[] = {
+ {
+ .owner = TEST_OWNER_A_ADDRESS,
+ .ip = TEST_ADDRESS_A_PREFIX".2"
+ }
+};
+static const struct wrepl_ip addresses_A_3_4[] = {
+ {
+ .owner = TEST_OWNER_A_ADDRESS,
+ .ip = TEST_ADDRESS_A_PREFIX".3"
+ },
+ {
+ .owner = TEST_OWNER_A_ADDRESS,
+ .ip = TEST_ADDRESS_A_PREFIX".4"
+ }
+};
+static const struct wrepl_ip addresses_A_3_4_X_3_4[] = {
+ {
+ .owner = TEST_OWNER_A_ADDRESS,
+ .ip = TEST_ADDRESS_A_PREFIX".3"
+ },
+ {
+ .owner = TEST_OWNER_A_ADDRESS,
+ .ip = TEST_ADDRESS_A_PREFIX".4"
+ },
+ {
+ .owner = TEST_OWNER_X_ADDRESS,
+ .ip = TEST_ADDRESS_X_PREFIX".3"
+ },
+ {
+ .owner = TEST_OWNER_X_ADDRESS,
+ .ip = TEST_ADDRESS_X_PREFIX".4"
+ }
+};
+static const struct wrepl_ip addresses_A_3_4_B_3_4[] = {
+ {
+ .owner = TEST_OWNER_A_ADDRESS,
+ .ip = TEST_ADDRESS_A_PREFIX".3"
+ },
+ {
+ .owner = TEST_OWNER_A_ADDRESS,
+ .ip = TEST_ADDRESS_A_PREFIX".4"
+ },
+ {
+ .owner = TEST_OWNER_B_ADDRESS,
+ .ip = TEST_ADDRESS_B_PREFIX".3"
+ },
+ {
+ .owner = TEST_OWNER_B_ADDRESS,
+ .ip = TEST_ADDRESS_B_PREFIX".4"
+ }
+};
+static const struct wrepl_ip addresses_A_3_4_OWNER_B[] = {
+ {
+ .owner = TEST_OWNER_B_ADDRESS,
+ .ip = TEST_ADDRESS_A_PREFIX".3"
+ },
+ {
+ .owner = TEST_OWNER_B_ADDRESS,
+ .ip = TEST_ADDRESS_A_PREFIX".4"
+ }
+};
+static const struct wrepl_ip addresses_A_3_4_X_3_4_OWNER_B[] = {
+ {
+ .owner = TEST_OWNER_B_ADDRESS,
+ .ip = TEST_ADDRESS_A_PREFIX".3"
+ },
+ {
+ .owner = TEST_OWNER_B_ADDRESS,
+ .ip = TEST_ADDRESS_A_PREFIX".4"
+ },
+ {
+ .owner = TEST_OWNER_B_ADDRESS,
+ .ip = TEST_ADDRESS_X_PREFIX".3"
+ },
+ {
+ .owner = TEST_OWNER_B_ADDRESS,
+ .ip = TEST_ADDRESS_X_PREFIX".4"
+ }
+};
+
+static const struct wrepl_ip addresses_A_3_4_X_1_2[] = {
+ {
+ .owner = TEST_OWNER_A_ADDRESS,
+ .ip = TEST_ADDRESS_A_PREFIX".3"
+ },
+ {
+ .owner = TEST_OWNER_A_ADDRESS,
+ .ip = TEST_ADDRESS_A_PREFIX".4"
+ },
+ {
+ .owner = TEST_OWNER_X_ADDRESS,
+ .ip = TEST_ADDRESS_X_PREFIX".1"
+ },
+ {
+ .owner = TEST_OWNER_X_ADDRESS,
+ .ip = TEST_ADDRESS_X_PREFIX".2"
+ }
+};
+
+static const struct wrepl_ip addresses_B_1[] = {
+ {
+ .owner = TEST_OWNER_B_ADDRESS,
+ .ip = TEST_ADDRESS_B_PREFIX".1"
+ }
+};
+static const struct wrepl_ip addresses_B_2[] = {
+ {
+ .owner = TEST_OWNER_B_ADDRESS,
+ .ip = TEST_ADDRESS_B_PREFIX".2"
+ }
+};
+static const struct wrepl_ip addresses_B_3_4[] = {
+ {
+ .owner = TEST_OWNER_B_ADDRESS,
+ .ip = TEST_ADDRESS_B_PREFIX".3"
+ },
+ {
+ .owner = TEST_OWNER_B_ADDRESS,
+ .ip = TEST_ADDRESS_B_PREFIX".4"
+ }
+};
+static const struct wrepl_ip addresses_B_3_4_X_3_4[] = {
+ {
+ .owner = TEST_OWNER_B_ADDRESS,
+ .ip = TEST_ADDRESS_B_PREFIX".3"
+ },
+ {
+ .owner = TEST_OWNER_B_ADDRESS,
+ .ip = TEST_ADDRESS_B_PREFIX".4"
+ },
+ {
+ .owner = TEST_OWNER_X_ADDRESS,
+ .ip = TEST_ADDRESS_X_PREFIX".3"
+ },
+ {
+ .owner = TEST_OWNER_X_ADDRESS,
+ .ip = TEST_ADDRESS_X_PREFIX".4"
+ }
+};
+static const struct wrepl_ip addresses_B_3_4_X_1_2[] = {
+ {
+ .owner = TEST_OWNER_B_ADDRESS,
+ .ip = TEST_ADDRESS_B_PREFIX".3"
+ },
+ {
+ .owner = TEST_OWNER_B_ADDRESS,
+ .ip = TEST_ADDRESS_B_PREFIX".4"
+ },
+ {
+ .owner = TEST_OWNER_X_ADDRESS,
+ .ip = TEST_ADDRESS_X_PREFIX".1"
+ },
+ {
+ .owner = TEST_OWNER_X_ADDRESS,
+ .ip = TEST_ADDRESS_X_PREFIX".2"
+ }
+};
+
+static const struct wrepl_ip addresses_X_1_2[] = {
+ {
+ .owner = TEST_OWNER_X_ADDRESS,
+ .ip = TEST_ADDRESS_X_PREFIX".1"
+ },
+ {
+ .owner = TEST_OWNER_X_ADDRESS,
+ .ip = TEST_ADDRESS_X_PREFIX".2"
+ }
+};
+static const struct wrepl_ip addresses_X_3_4[] = {
+ {
+ .owner = TEST_OWNER_X_ADDRESS,
+ .ip = TEST_ADDRESS_X_PREFIX".3"
+ },
+ {
+ .owner = TEST_OWNER_X_ADDRESS,
+ .ip = TEST_ADDRESS_X_PREFIX".4"
+ }
+};
+
+static struct test_wrepl_conflict_conn *test_create_conflict_ctx(
+ struct torture_context *tctx, const char *address)
+{
+ struct test_wrepl_conflict_conn *ctx;
+ struct wrepl_associate associate;
+ struct wrepl_pull_table pull_table;
+ struct socket_address *nbt_srv_addr;
+ NTSTATUS status;
+ uint32_t i;
+ struct interface *ifaces;
+
+ ctx = talloc_zero(tctx, struct test_wrepl_conflict_conn);
+ if (!ctx) return NULL;
+
+ ctx->address = address;
+ ctx->pull = wrepl_socket_init(ctx, tctx->ev, lp_iconv_convenience(tctx->lp_ctx));
+ 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, lp_iconv_convenience(tctx->lp_ctx));
+ if (!ctx->nbtsock) return NULL;
+
+ load_interfaces(tctx, lp_interfaces(tctx->lp_ctx), &ifaces);
+
+ ctx->myaddr = socket_address_from_strings(tctx, ctx->nbtsock->sock->backend_name, iface_best_ip(ifaces, address), 0);
+ if (!ctx->myaddr) return NULL;
+
+ for (i = 0; i < iface_count(ifaces); i++) {
+ if (strcmp(ctx->myaddr->addr, iface_n_ip(ifaces, i)) == 0) continue;
+ ctx->myaddr2 = socket_address_from_strings(tctx, ctx->nbtsock->sock->backend_name, iface_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, lp_iconv_convenience(tctx->lp_ctx));
+ 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, lp_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 sockewrapper */
+ 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, lp_iconv_convenience(tctx->lp_ctx));
+ 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, lp_iconv_convenience(tctx->lp_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,
+ lp_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 sockewrapper */
+ 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;
+
+ ctx->addresses_all_num = iface_count(ifaces);
+ ctx->addresses_all = talloc_array(ctx, struct wrepl_ip, ctx->addresses_all_num);
+ if (!ctx->addresses_all) return NULL;
+ for (i=0; i < ctx->addresses_all_num; i++) {
+ ctx->addresses_all[i].owner = ctx->b.address;
+ ctx->addresses_all[i].ip = talloc_strdup(ctx->addresses_all, iface_n_ip(ifaces, i));
+ 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_wins_owner *send_request;
+ 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, lp_iconv_convenience(tctx->lp_ctx));
+
+ 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);
+ send_request = &repl_recv->message.replication.info.owner;
+
+ 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);
+ CHECK_VALUE(tctx, names[0].name.type, name->name->type);
+ CHECK_VALUE_STRING(tctx, names[0].name.name, name->name->name);
+ CHECK_VALUE_STRING(tctx, names[0].name.scope, name->name->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 bool test_conflict_same_owner(struct torture_context *tctx,
+ struct test_wrepl_conflict_conn *ctx)
+{
+ static bool ret = true;
+ struct nbt_name name;
+ 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;
+ uint8_t types[] = { 0x00, 0x1C };
+ 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,tomstone 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,
+ }
+ };
+
+ name.name = "_SAME_OWNER_A";
+ name.type = 0;
+ name.scope = NULL;
+
+ wins_name_tmp = NULL;
+ wins_name_last = &wins_name2;
+ wins_name_cur = &wins_name1;
+
+ for (j=0; ret && j < ARRAY_SIZE(types); j++) {
+ name.type = types[j];
+ torture_comment(tctx, "Test Replica Conflicts with same owner[%s] for %s\n",
+ nbt_name_string(ctx, &name), 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 = &name;
+ 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(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(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(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(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(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 = lp_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 = lp_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(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 = lp_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 {
+ 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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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...
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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...
+ */
+ {
+ .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
+ */
+ {
+ .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
+ */
+ {
+ .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)
+ */
+ {
+ .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)
+ */
+ {
+ .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)
+ */
+ {
+ .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)
+ */
+ {
+ .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)
+ */
+ {
+ .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)
+ */
+ {
+ .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)
+ */
+ {
+ .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)
+ */
+ {
+ .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",
+ lp_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 = lp_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 */
+ event_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) {
+ event_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(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) {
+ event_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) {
+ event_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 = lp_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(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_ASSERT(v, correct) do { \
+ if ((v) != (correct)) { \
+ printf("(%s) Incorrect value %s=%d - should be %s (%d)\n", \
+ __location__, #v, v, #correct, correct); \
+ return; \
+ } \
+} while (0)
+
+#define _NBT_ASSERT_STRING(v, correct) do { \
+ if ( ((!v) && (correct)) || \
+ ((v) && (!correct)) || \
+ ((v) && (correct) && strcmp(v,correct) != 0)) { \
+ printf("(%s) Incorrect value %s=%s - should be %s\n", \
+ __location__, #v, v, correct); \
+ 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(name->type, rec->name.type);
+ _NBT_ASSERT_STRING(name->name, rec->name.name);
+ _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_AUTHORITIVE |
+ 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_AUTHORITIVE |
+ 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) {
+ event_loop_once(nbtsock->event_ctx);
+ }
+ 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(name->type, rec->name.type);
+ _NBT_ASSERT_STRING(name->name, rec->name.name);
+ _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_AUTHORITIVE;
+
+ 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) {
+ event_loop_once(nbtsock->event_ctx);
+ }
+ 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;
+
+ 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:
+ printf("%s: unexpected incoming packet\n", __location__);
+ return;
+ }
+}
+
+/*
+ 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/atsvc.c b/source4/torture/ndr/atsvc.c
new file mode 100644
index 0000000000..583a3eae49
--- /dev/null
+++ b/source4/torture/ndr/atsvc.c
@@ -0,0 +1,214 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/ndr/ndr.h"
+#include "librpc/gen_ndr/ndr_atsvc.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/dfs.c b/source4/torture/ndr/dfs.c
new file mode 100644
index 0000000000..489de608e6
--- /dev/null
+++ b/source4/torture/ndr/dfs.c
@@ -0,0 +1,114 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/ndr/ndr.h"
+#include "librpc/gen_ndr/ndr_dfs.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/drsuapi.c b/source4/torture/ndr/drsuapi.c
new file mode 100644
index 0000000000..664be941f3
--- /dev/null
+++ b/source4/torture/ndr/drsuapi.c
@@ -0,0 +1,304 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/ndr/ndr.h"
+#include "librpc/gen_ndr/ndr_drsuapi.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 0000000000..9f7b1efa3e
--- /dev/null
+++ b/source4/torture/ndr/epmap.c
@@ -0,0 +1,77 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/ndr/ndr.h"
+#include "librpc/gen_ndr/ndr_epmapper.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;
+}
+
+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;
+}
+
+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/lsa.c b/source4/torture/ndr/lsa.c
new file mode 100644
index 0000000000..68eb36ebbd
--- /dev/null
+++ b/source4/torture/ndr/lsa.c
@@ -0,0 +1,2041 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/ndr/ndr.h"
+#include "librpc/gen_ndr/ndr_lsa.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.unknown1, 0, "unknown 1");
+ torture_assert_int_equal(tctx, r->in.unknown2, 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_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 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.unknown1, 0, "unknown 1");
+ torture_assert_int_equal(tctx, r->in.unknown2, 0, "unknown 2");
+
+ return true;
+}
+
+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;
+}
+
+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;
+}
+
+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);
+
+ return suite;
+}
+
diff --git a/source4/torture/ndr/ndr.c b/source4/torture/ndr/ndr.c
new file mode 100644
index 0000000000..70bd070009
--- /dev/null
+++ b/source4/torture/ndr/ndr.c
@@ -0,0 +1,287 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/ndr/ndr.h"
+#include "torture/ndr/proto.h"
+#include "torture/torture.h"
+#include "../lib/util/dlinklist.h"
+#include "param/param.h"
+
+struct ndr_pull_test_data {
+ DATA_BLOB data;
+ size_t struct_size;
+ ndr_pull_flags_fn_t pull_fn;
+ int ndr_flags;
+};
+
+static bool wrap_ndr_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 = ndr_pull_init_blob(&(data->data), tctx, lp_iconv_convenience(tctx->lp_ctx));
+
+ ndr->flags |= LIBNDR_FLAG_REF_ALLOC;
+
+ torture_assert_ndr_success(tctx, data->pull_fn(ndr, data->ndr_flags, ds),
+ "pulling");
+
+ torture_assert(tctx, ndr->offset == ndr->data_size,
+ talloc_asprintf(tctx,
+ "%d unread bytes", ndr->data_size - ndr->offset));
+
+ if (check_fn != NULL)
+ return check_fn(tctx, ds);
+ else
+ return true;
+}
+
+_PUBLIC_ struct torture_test *_torture_suite_add_ndr_pull_test(
+ struct torture_suite *suite,
+ const char *name, ndr_pull_flags_fn_t pull_fn,
+ DATA_BLOB db,
+ size_t struct_size,
+ int ndr_flags,
+ 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, name);
+ test->description = NULL;
+ test->run = wrap_ndr_pull_test;
+ data = talloc(test, struct ndr_pull_test_data);
+ data->data = db;
+ data->ndr_flags = ndr_flags;
+ data->struct_size = struct_size;
+ data->pull_fn = pull_fn;
+ test->data = data;
+ test->fn = check_fn;
+ test->dangerous = false;
+
+ DLIST_ADD_END(tcase->tests, test, struct torture_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, lp_iconv_convenience(tctx->lp_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, lp_iconv_convenience(tctx->lp_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;
+ torture_assert_ntstatus_equal(tctx, NT_STATUS_INVALID_PARAMETER,
+ GUID_from_string("bla", &g1),
+ "parameter not invalid");
+ return true;
+}
+
+static bool test_guid_from_string(struct torture_context *tctx)
+{
+ struct GUID g1, exp;
+ torture_assert_ntstatus_ok(tctx,
+ GUID_from_string("00000001-0002-0003-0405-060708090a0b", &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;
+ torture_assert(tctx, GUID_equal(&g1, &exp), "UUID parsed incorrectly");
+ torture_assert_ntstatus_ok(tctx,
+ GUID_from_string("{00000001-0002-0003-0405-060708090a0b}", &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_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, 10, GUID_compare(&g1, &g2),
+ "GUID diff invalid");
+
+ g1.time_low = 0;
+ g1.clock_seq[1] = 20;
+ torture_assert_int_equal(tctx, 20, GUID_compare(&g1, &g2),
+ "GUID diff invalid");
+ 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_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_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_samr_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_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);
+
+ return suite;
+}
+
diff --git a/source4/torture/ndr/ndr.h b/source4/torture/ndr/ndr.h
new file mode 100644
index 0000000000..aca6e58397
--- /dev/null
+++ b/source4/torture/ndr/ndr.h
@@ -0,0 +1,44 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __TORTURE_NDR_H__
+#define __TORTURE_NDR_H__
+
+#include "torture/torture.h"
+#include "librpc/ndr/libndr.h"
+
+_PUBLIC_ struct torture_test *_torture_suite_add_ndr_pull_test(
+ struct torture_suite *suite,
+ const char *name, ndr_pull_flags_fn_t fn,
+ DATA_BLOB db,
+ size_t struct_size,
+ int ndr_flags,
+ bool (*check_fn) (struct torture_context *, void *data));
+
+#define torture_suite_add_ndr_pull_test(suite,name,data,check_fn) \
+ _torture_suite_add_ndr_pull_test(suite, #name, \
+ (ndr_pull_flags_fn_t)ndr_pull_ ## name, data_blob_talloc(suite, data, sizeof(data)), \
+ sizeof(struct name), 0, (bool (*) (struct torture_context *, void *)) check_fn);
+
+#define torture_suite_add_ndr_pull_fn_test(suite,name,data,flags,check_fn) \
+ _torture_suite_add_ndr_pull_test(suite, #name "_" #flags, \
+ (ndr_pull_flags_fn_t)ndr_pull_ ## name, data_blob_talloc(suite, data, sizeof(data)), \
+ sizeof(struct name), flags, (bool (*) (struct torture_context *, void *)) check_fn);
+
+#endif /* __TORTURE_NDR_H__ */
diff --git a/source4/torture/ndr/netlogon.c b/source4/torture/ndr/netlogon.c
new file mode 100644
index 0000000000..ea11a20bb2
--- /dev/null
+++ b/source4/torture/ndr/netlogon.c
@@ -0,0 +1,120 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for netlogon 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/ndr/ndr.h"
+#include "librpc/gen_ndr/ndr_netlogon.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;
+}
+
+
+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 );
+
+ return suite;
+}
+
diff --git a/source4/torture/ndr/samr.c b/source4/torture/ndr/samr.c
new file mode 100644
index 0000000000..5218b9b15e
--- /dev/null
+++ b/source4/torture/ndr/samr.c
@@ -0,0 +1,211 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for samr 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/ndr/ndr.h"
+#include "librpc/gen_ndr/ndr_samr.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
+};
+
+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_fn_test(suite, samr_QueryUserInfo, samr_queryuserinfo_out_data, NDR_OUT, 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);
+
+
+
+ return suite;
+}
+
diff --git a/source4/torture/ndr/spoolss.c b/source4/torture/ndr/spoolss.c
new file mode 100644
index 0000000000..bcff814ad1
--- /dev/null
+++ b/source4/torture/ndr/spoolss.c
@@ -0,0 +1,483 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for spoolss 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/ndr/ndr.h"
+#include "librpc/gen_ndr/ndr_spoolss.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 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 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
+};
+
+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, 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, 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, spoolss_GetPrinterData, getprinterdata_req_data, NDR_IN, NULL );
+ torture_suite_add_ndr_pull_fn_test(suite, spoolss_GetPrinterData, getprinterdata_resp_data, NDR_OUT, 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, 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, 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, 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_fn_test(suite, spoolss_GetPrinterDriverDirectory, getprinterdriverdir_out_data, NDR_OUT, NULL ); */
+
+ torture_suite_add_ndr_pull_fn_test(suite, spoolss_AddPrinterDriverEx, addprinterdriverex_in_data, NDR_IN, NULL );
+
+ return suite;
+}
diff --git a/source4/torture/ndr/winreg.c b/source4/torture/ndr/winreg.c
new file mode 100644
index 0000000000..de804b7721
--- /dev/null
+++ b/source4/torture/ndr/winreg.c
@@ -0,0 +1,575 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#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"
+
+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_PARAM,
+ "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[0].name->name, "HOMEPATH",
+ "name");
+
+ torture_assert_int_equal(tctx, r->in.values[0].type, 0, "type");
+ torture_assert_int_equal(tctx, r->in.values[0].offset, 0, "offset");
+ torture_assert_int_equal(tctx, r->in.values[0].length, 0, "length");
+ torture_assert_int_equal(tctx, *r->in.buffer_size, 76, "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[0].name->name, "HOMEPATH",
+ "name");
+
+ torture_assert_int_equal(tctx, r->out.values[0].type, 0, "type");
+ torture_assert_int_equal(tctx, r->out.values[0].offset, 0, "offset");
+ torture_assert_int_equal(tctx, r->out.values[0].length, 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;
+}
+
+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.unknown, 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_BADFILE, "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_BADFILE, "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;
+}
+
+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;
+}
+
+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_PARAM, "notify change key value");
+ return true;
+}
+
+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;
+}
+
+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_fn_test(suite, winreg_QueryMultipleValues, querymultiplevalues_out_data, NDR_OUT, querymultiplevalues_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/rap/rap.c b/source4/torture/rap/rap.c
new file mode 100644
index 0000000000..be231107dd
--- /dev/null
+++ b/source4/torture/rap/rap.c
@@ -0,0 +1,563 @@
+/*
+ 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
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/libcli.h"
+#include "torture/smbtorture.h"
+#include "torture/util.h"
+#include "libcli/rap/rap.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/libcli.h"
+#include "librpc/ndr/libndr.h"
+#include "param/param.h"
+
+#define RAP_GOTO(call) do { \
+ NTSTATUS _status; \
+ _status = call; \
+ if (!NT_STATUS_IS_OK(_status)) { \
+ result = _status; \
+ goto done; \
+ } \
+} while (0)
+
+#define NDR_GOTO(call) do { \
+ enum ndr_err_code _ndr_err; \
+ _ndr_err = call; \
+ if (!NDR_ERR_CODE_IS_SUCCESS(_ndr_err)) { \
+ result = ndr_map_error2ntstatus(_ndr_err); \
+ goto done; \
+ } \
+} while (0)
+
+#define NDR_RETURN(call) do { \
+ enum ndr_err_code _ndr_err; \
+ _ndr_err = call; \
+ if (!NDR_ERR_CODE_IS_SUCCESS(_ndr_err)) { \
+ return ndr_map_error2ntstatus(_ndr_err); \
+ } \
+} while (0)
+
+struct rap_call {
+ uint16_t callno;
+ char *paramdesc;
+ const char *datadesc;
+
+ uint16_t status;
+ uint16_t convert;
+
+ uint16_t rcv_paramlen, rcv_datalen;
+
+ struct ndr_push *ndr_push_param;
+ struct ndr_push *ndr_push_data;
+ struct ndr_pull *ndr_pull_param;
+ struct ndr_pull *ndr_pull_data;
+};
+
+#define RAPNDR_FLAGS (LIBNDR_FLAG_NOALIGN|LIBNDR_FLAG_STR_ASCII|LIBNDR_FLAG_STR_NULLTERM);
+
+static struct rap_call *new_rap_cli_call(TALLOC_CTX *mem_ctx, struct smb_iconv_convenience *iconv_convenience, uint16_t callno)
+{
+ struct rap_call *call;
+
+ call = talloc(mem_ctx, struct rap_call);
+
+ if (call == NULL)
+ return NULL;
+
+ call->callno = callno;
+ call->rcv_paramlen = 4;
+
+ call->paramdesc = NULL;
+ call->datadesc = NULL;
+
+ call->ndr_push_param = ndr_push_init_ctx(mem_ctx, iconv_convenience);
+ call->ndr_push_param->flags = RAPNDR_FLAGS;
+
+ call->ndr_push_data = ndr_push_init_ctx(mem_ctx, iconv_convenience);
+ call->ndr_push_data->flags = RAPNDR_FLAGS;
+
+ return call;
+}
+
+static void rap_cli_push_paramdesc(struct rap_call *call, char desc)
+{
+ int len = 0;
+
+ if (call->paramdesc != NULL)
+ len = strlen(call->paramdesc);
+
+ call->paramdesc = talloc_realloc(call,
+ call->paramdesc,
+ char,
+ len+2);
+
+ call->paramdesc[len] = desc;
+ call->paramdesc[len+1] = '\0';
+}
+
+static void rap_cli_push_word(struct rap_call *call, uint16_t val)
+{
+ rap_cli_push_paramdesc(call, 'W');
+ ndr_push_uint16(call->ndr_push_param, NDR_SCALARS, val);
+}
+
+static void rap_cli_push_dword(struct rap_call *call, uint32_t val)
+{
+ rap_cli_push_paramdesc(call, 'D');
+ ndr_push_uint32(call->ndr_push_param, NDR_SCALARS, val);
+}
+
+static void rap_cli_push_rcvbuf(struct rap_call *call, int len)
+{
+ rap_cli_push_paramdesc(call, 'r');
+ rap_cli_push_paramdesc(call, 'L');
+ ndr_push_uint16(call->ndr_push_param, NDR_SCALARS, len);
+ call->rcv_datalen = len;
+}
+
+static void rap_cli_expect_multiple_entries(struct rap_call *call)
+{
+ rap_cli_push_paramdesc(call, 'e');
+ rap_cli_push_paramdesc(call, 'h');
+ call->rcv_paramlen += 4; /* uint16_t entry count, uint16_t total */
+}
+
+static void rap_cli_expect_word(struct rap_call *call)
+{
+ rap_cli_push_paramdesc(call, 'h');
+ call->rcv_paramlen += 2;
+}
+
+static void rap_cli_push_string(struct rap_call *call, const char *str)
+{
+ if (str == NULL) {
+ rap_cli_push_paramdesc(call, 'O');
+ return;
+ }
+ rap_cli_push_paramdesc(call, 'z');
+ ndr_push_string(call->ndr_push_param, NDR_SCALARS, str);
+}
+
+static void rap_cli_expect_format(struct rap_call *call, const char *format)
+{
+ call->datadesc = format;
+}
+
+static NTSTATUS rap_pull_string(TALLOC_CTX *mem_ctx, struct ndr_pull *ndr,
+ uint16_t convert, char **dest)
+{
+ uint16_t string_offset;
+ uint16_t ignore;
+ const char *p;
+ size_t len;
+
+ NDR_RETURN(ndr_pull_uint16(ndr, NDR_SCALARS, &string_offset));
+ NDR_RETURN(ndr_pull_uint16(ndr, NDR_SCALARS, &ignore));
+
+ string_offset -= convert;
+
+ if (string_offset+1 > ndr->data_size)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ p = (const char *)(ndr->data + string_offset);
+ len = strnlen(p, ndr->data_size-string_offset);
+
+ if ( string_offset + len + 1 > ndr->data_size )
+ return NT_STATUS_INVALID_PARAMETER;
+
+ *dest = talloc_zero_array(mem_ctx, char, len+1);
+ pull_string(*dest, p, len+1, len, STR_ASCII);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS rap_cli_do_call(struct smbcli_tree *tree,
+ struct smb_iconv_convenience *iconv_convenience,
+ struct rap_call *call)
+{
+ NTSTATUS result;
+ DATA_BLOB param_blob;
+ struct ndr_push *params;
+ struct smb_trans2 trans;
+
+ params = ndr_push_init_ctx(call, iconv_convenience);
+
+ if (params == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ params->flags = RAPNDR_FLAGS;
+
+ trans.in.max_param = call->rcv_paramlen;
+ trans.in.max_data = call->rcv_datalen;
+ trans.in.max_setup = 0;
+ trans.in.flags = 0;
+ trans.in.timeout = 0;
+ trans.in.setup_count = 0;
+ trans.in.setup = NULL;
+ trans.in.trans_name = "\\PIPE\\LANMAN";
+
+ NDR_RETURN(ndr_push_uint16(params, NDR_SCALARS, call->callno));
+ if (call->paramdesc)
+ NDR_RETURN(ndr_push_string(params, NDR_SCALARS, call->paramdesc));
+ if (call->datadesc)
+ NDR_RETURN(ndr_push_string(params, NDR_SCALARS, call->datadesc));
+
+ param_blob = ndr_push_blob(call->ndr_push_param);
+ NDR_RETURN(ndr_push_bytes(params, param_blob.data,
+ param_blob.length));
+
+ trans.in.params = ndr_push_blob(params);
+ trans.in.data = data_blob(NULL, 0);
+
+ result = smb_raw_trans(tree, call, &trans);
+
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ call->ndr_pull_param = ndr_pull_init_blob(&trans.out.params, call,
+ iconv_convenience);
+ call->ndr_pull_param->flags = RAPNDR_FLAGS;
+
+ call->ndr_pull_data = ndr_pull_init_blob(&trans.out.data, call,
+ iconv_convenience);
+ call->ndr_pull_data->flags = RAPNDR_FLAGS;
+
+ return result;
+}
+
+
+static NTSTATUS smbcli_rap_netshareenum(struct smbcli_tree *tree,
+ struct smb_iconv_convenience *iconv_convenience,
+ TALLOC_CTX *mem_ctx,
+ struct rap_NetShareEnum *r)
+{
+ struct rap_call *call;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ int i;
+
+ call = new_rap_cli_call(tree, iconv_convenience, RAP_WshareEnum);
+
+ if (call == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ rap_cli_push_word(call, r->in.level); /* Level */
+ rap_cli_push_rcvbuf(call, r->in.bufsize);
+ rap_cli_expect_multiple_entries(call);
+
+ switch(r->in.level) {
+ case 0:
+ rap_cli_expect_format(call, "B13");
+ break;
+ case 1:
+ rap_cli_expect_format(call, "B13BWz");
+ break;
+ }
+
+ result = rap_cli_do_call(tree, iconv_convenience, call);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.status));
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.convert));
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.count));
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.available));
+
+ r->out.info = talloc_array(mem_ctx, union rap_shareenum_info, r->out.count);
+
+ if (r->out.info == NULL) {
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ for (i=0; i<r->out.count; i++) {
+ switch(r->in.level) {
+ case 0:
+ NDR_GOTO(ndr_pull_bytes(call->ndr_pull_data,
+ (uint8_t *)r->out.info[i].info0.name, 13));
+ break;
+ case 1:
+ NDR_GOTO(ndr_pull_bytes(call->ndr_pull_data,
+ (uint8_t *)r->out.info[i].info1.name, 13));
+ NDR_GOTO(ndr_pull_bytes(call->ndr_pull_data,
+ (uint8_t *)&r->out.info[i].info1.pad, 1));
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_data,
+ NDR_SCALARS, &r->out.info[i].info1.type));
+ RAP_GOTO(rap_pull_string(mem_ctx, call->ndr_pull_data,
+ r->out.convert,
+ &r->out.info[i].info1.comment));
+ break;
+ }
+ }
+
+ result = NT_STATUS_OK;
+
+ done:
+ talloc_free(call);
+ return result;
+}
+
+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, lp_iconv_convenience(tctx->lp_ctx), tctx, &r), "");
+
+ for (i=0; i<r.out.count; i++) {
+ printf("%s %d %s\n", r.out.info[i].info1.name,
+ r.out.info[i].info1.type,
+ r.out.info[i].info1.comment);
+ }
+
+ return true;
+}
+
+static NTSTATUS smbcli_rap_netserverenum2(struct smbcli_tree *tree,
+ struct smb_iconv_convenience *iconv_convenience,
+ TALLOC_CTX *mem_ctx,
+ struct rap_NetServerEnum2 *r)
+{
+ struct rap_call *call;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ int i;
+
+ call = new_rap_cli_call(mem_ctx, iconv_convenience, RAP_NetServerEnum2);
+
+ if (call == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ rap_cli_push_word(call, r->in.level);
+ rap_cli_push_rcvbuf(call, r->in.bufsize);
+ rap_cli_expect_multiple_entries(call);
+ rap_cli_push_dword(call, r->in.servertype);
+ rap_cli_push_string(call, r->in.domain);
+
+ switch(r->in.level) {
+ case 0:
+ rap_cli_expect_format(call, "B16");
+ break;
+ case 1:
+ rap_cli_expect_format(call, "B16BBDz");
+ break;
+ }
+
+ result = rap_cli_do_call(tree, iconv_convenience, call);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = NT_STATUS_INVALID_PARAMETER;
+
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.status));
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.convert));
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.count));
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.available));
+
+ r->out.info = talloc_array(mem_ctx, union rap_server_info, r->out.count);
+
+ if (r->out.info == NULL) {
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ for (i=0; i<r->out.count; i++) {
+ switch(r->in.level) {
+ case 0:
+ NDR_GOTO(ndr_pull_bytes(call->ndr_pull_data,
+ (uint8_t *)r->out.info[i].info0.name, 16));
+ break;
+ case 1:
+ NDR_GOTO(ndr_pull_bytes(call->ndr_pull_data,
+ (uint8_t *)r->out.info[i].info1.name, 16));
+ NDR_GOTO(ndr_pull_bytes(call->ndr_pull_data,
+ &r->out.info[i].info1.version_major, 1));
+ NDR_GOTO(ndr_pull_bytes(call->ndr_pull_data,
+ &r->out.info[i].info1.version_minor, 1));
+ NDR_GOTO(ndr_pull_uint32(call->ndr_pull_data,
+ NDR_SCALARS, &r->out.info[i].info1.servertype));
+ RAP_GOTO(rap_pull_string(mem_ctx, call->ndr_pull_data,
+ r->out.convert,
+ &r->out.info[i].info1.comment));
+ }
+ }
+
+ result = NT_STATUS_OK;
+
+ done:
+ talloc_free(call);
+ return result;
+}
+
+static bool test_netserverenum(struct torture_context *tctx,
+ struct smbcli_state *cli)
+{
+ struct rap_NetServerEnum2 r;
+ int i;
+
+ r.in.level = 0;
+ r.in.bufsize = 8192;
+ r.in.servertype = 0xffffffff;
+ r.in.servertype = 0x80000000;
+ r.in.domain = NULL;
+
+ torture_assert_ntstatus_ok(tctx,
+ smbcli_rap_netserverenum2(cli->tree, lp_iconv_convenience(tctx->lp_ctx), tctx, &r), "");
+
+ for (i=0; i<r.out.count; i++) {
+ switch (r.in.level) {
+ case 0:
+ printf("%s\n", r.out.info[i].info0.name);
+ break;
+ case 1:
+ printf("%s %x %s\n", r.out.info[i].info1.name,
+ r.out.info[i].info1.servertype,
+ r.out.info[i].info1.comment);
+ break;
+ }
+ }
+
+ return true;
+}
+
+NTSTATUS smbcli_rap_netservergetinfo(struct smbcli_tree *tree,
+ struct smb_iconv_convenience *iconv_convenience,
+ TALLOC_CTX *mem_ctx,
+ struct rap_WserverGetInfo *r)
+{
+ struct rap_call *call;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ if (!(call = new_rap_cli_call(mem_ctx, iconv_convenience, RAP_WserverGetInfo))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rap_cli_push_word(call, r->in.level);
+ rap_cli_push_rcvbuf(call, r->in.bufsize);
+ rap_cli_expect_word(call);
+
+ switch(r->in.level) {
+ case 0:
+ rap_cli_expect_format(call, "B16");
+ break;
+ case 1:
+ rap_cli_expect_format(call, "B16BBDz");
+ break;
+ default:
+ result = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ result = rap_cli_do_call(tree, iconv_convenience, call);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.status));
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.convert));
+ NDR_GOTO(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.available));
+
+ switch(r->in.level) {
+ case 0:
+ NDR_GOTO(ndr_pull_bytes(call->ndr_pull_data,
+ (uint8_t *)r->out.info.info0.name, 16));
+ break;
+ case 1:
+ NDR_GOTO(ndr_pull_bytes(call->ndr_pull_data,
+ (uint8_t *)r->out.info.info1.name, 16));
+ NDR_GOTO(ndr_pull_bytes(call->ndr_pull_data,
+ &r->out.info.info1.version_major, 1));
+ NDR_GOTO(ndr_pull_bytes(call->ndr_pull_data,
+ &r->out.info.info1.version_minor, 1));
+ NDR_GOTO(ndr_pull_uint32(call->ndr_pull_data,
+ NDR_SCALARS, &r->out.info.info1.servertype));
+ RAP_GOTO(rap_pull_string(mem_ctx, call->ndr_pull_data,
+ r->out.convert,
+ &r->out.info.info1.comment));
+ }
+ done:
+ talloc_free(call);
+ return result;
+}
+
+static bool test_netservergetinfo(struct torture_context *tctx,
+ struct smbcli_state *cli)
+{
+ struct rap_WserverGetInfo r;
+ bool res = true;
+
+ r.in.bufsize = 0xffff;
+
+ r.in.level = 0;
+ torture_assert_ntstatus_ok(tctx, smbcli_rap_netservergetinfo(cli->tree, lp_iconv_convenience(tctx->lp_ctx), tctx, &r), "");
+ r.in.level = 1;
+ torture_assert_ntstatus_ok(tctx, smbcli_rap_netservergetinfo(cli->tree, lp_iconv_convenience(tctx->lp_ctx), tctx, &r), "");
+
+ return res;
+}
+
+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, lp_iconv_convenience(torture->lp_ctx), callno);
+ NTSTATUS result;
+
+ result = rap_cli_do_call(cli->tree, lp_iconv_convenience(torture->lp_ctx), 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(void)
+{
+ struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "RAP");
+ struct torture_suite *suite_basic = torture_suite_create(suite, "BASIC");
+
+ torture_suite_add_suite(suite, suite_basic);
+
+ 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, "SCAN", torture_rap_scan);
+
+ suite->description = talloc_strdup(suite,
+ "Remote Administration Protocol tests");
+
+ torture_register_suite(suite);
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/torture/raw/acls.c b/source4/torture/raw/acls.c
new file mode 100644
index 0000000000..48dec6e561
--- /dev/null
+++ b/source4/torture/raw/acls.c
@@ -0,0 +1,2011 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/libcli.h"
+#include "librpc/gen_ndr/lsa.h"
+#include "libcli/util/clilsa.h"
+#include "libcli/security/security.h"
+#include "torture/util.h"
+#include "librpc/gen_ndr/ndr_security.h"
+
+#define BASEDIR "\\testsd"
+
+#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)
+
+
+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;
+ struct dom_sid *test_sid;
+
+ printf("TESTING SETFILEINFO EA_SET\n");
+
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 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;
+
+ printf("add a new ACE to the DACL\n");
+
+ test_sid = dom_sid_parse_talloc(tctx, "S-1-5-32-1234-5432");
+
+ 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);
+
+ status = smb_raw_fileinfo(cli->tree, tctx, &q);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ if (!security_acl_equal(q.query_secdesc.out.sd->dacl, sd->dacl)) {
+ printf("%s: security descriptors don't match!\n", __location__);
+ printf("got:\n");
+ NDR_PRINT_DEBUG(security_descriptor, q.query_secdesc.out.sd);
+ printf("expected:\n");
+ NDR_PRINT_DEBUG(security_descriptor, sd);
+ ret = false;
+ }
+
+ printf("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);
+
+ status = smb_raw_fileinfo(cli->tree, tctx, &q);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ if (!security_acl_equal(q.query_secdesc.out.sd->dacl, sd->dacl)) {
+ printf("%s: security descriptors don't match!\n", __location__);
+ printf("got:\n");
+ NDR_PRINT_DEBUG(security_descriptor, q.query_secdesc.out.sd);
+ printf("expected:\n");
+ NDR_PRINT_DEBUG(security_descriptor, sd);
+ ret = false;
+ }
+
+done:
+ smbcli_close(cli->tree, fnum);
+ return ret;
+}
+
+
+/*
+ test using nttrans create to create a file with an initial acl set
+*/
+static bool test_nttrans_create(struct torture_context *tctx,
+ struct smbcli_state *cli)
+{
+ 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;
+ struct dom_sid *test_sid;
+
+ printf("testing nttrans create with sec_desc\n");
+
+ io.generic.level = RAW_OPEN_NTTRANS_CREATE;
+ io.ntcreatex.in.root_fid = 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;
+ io.ntcreatex.in.sec_desc = NULL;
+ io.ntcreatex.in.ea_list = NULL;
+
+ printf("creating normal file\n");
+
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+
+ printf("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;
+
+ smbcli_close(cli->tree, fnum);
+ smbcli_unlink(cli->tree, fname);
+
+ printf("adding a new ACE\n");
+ test_sid = dom_sid_parse_talloc(tctx, "S-1-5-32-1234-54321");
+
+ 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);
+
+ printf("creating a file 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;
+
+ q.query_secdesc.in.file.fnum = fnum;
+ status = smb_raw_fileinfo(cli->tree, tctx, &q);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ if (!security_acl_equal(q.query_secdesc.out.sd->dacl, sd->dacl)) {
+ printf("%s: security descriptors don't match!\n", __location__);
+ printf("got:\n");
+ NDR_PRINT_DEBUG(security_descriptor, q.query_secdesc.out.sd);
+ printf("expected:\n");
+ NDR_PRINT_DEBUG(security_descriptor, sd);
+ ret = false;
+ }
+
+done:
+ smbcli_close(cli->tree, fnum);
+ return ret;
+}
+
+#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)) { \
+ printf("(%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 using NTTRANS CREATE to create a file with a null ACL set
+*/
+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 "\\acl3.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;
+
+ printf("TESTING SEC_DESC WITH A NULL DACL\n");
+
+ io.generic.level = RAW_OPEN_NTTRANS_CREATE;
+ io.ntcreatex.in.root_fid = 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;
+
+ printf("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;
+
+ printf("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)) {
+ printf("DACL_PRESENT flag not set by the server!\n");
+ ret = false;
+ goto done;
+ }
+ if (q.query_secdesc.out.sd->dacl == NULL) {
+ printf("no DACL has been created on the server!\n");
+ ret = false;
+ goto done;
+ }
+
+ printf("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);
+
+ printf("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)) {
+ printf("DACL_PRESENT flag not set by the server!\n");
+ ret = false;
+ goto done;
+ }
+ if (q.query_secdesc.out.sd->dacl != NULL) {
+ printf("DACL has been created on the server!\n");
+ ret = false;
+ goto done;
+ }
+
+ printf("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);
+
+ printf("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);
+
+ printf("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);
+
+ printf("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);
+
+ printf("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);
+
+ printf("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);
+
+ printf("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)) {
+ printf("DACL_PRESENT flag not set by the server!\n");
+ ret = false;
+ goto done;
+ }
+ if (q.query_secdesc.out.sd->dacl == NULL) {
+ printf("no DACL has been created on the server!\n");
+ ret = false;
+ goto done;
+ }
+ if (q.query_secdesc.out.sd->dacl->num_aces != 0) {
+ printf("DACL has %u aces!\n",
+ q.query_secdesc.out.sd->dacl->num_aces);
+ ret = false;
+ goto done;
+ }
+
+ printf("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);
+
+ printf("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);
+
+ printf("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);
+
+ printf("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);
+
+ printf("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);
+
+ printf("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);
+
+ printf("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)) {
+ printf("DACL_PRESENT flag not set by the server!\n");
+ ret = false;
+ goto done;
+ }
+ if (q.query_secdesc.out.sd->dacl != NULL) {
+ printf("DACL has been created on the server!\n");
+ ret = false;
+ goto done;
+ }
+done:
+ smbcli_close(cli->tree, fnum);
+ return ret;
+}
+
+/*
+ test the behaviour of the well known SID_CREATOR_OWNER sid, and some generic
+ mapping bits
+*/
+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;
+
+ printf("TESTING SID_CREATOR_OWNER\n");
+
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 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;
+
+ printf("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);
+
+ printf("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);
+
+ printf("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);
+
+ printf("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);
+
+ printf("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);
+
+ printf("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);
+
+ printf("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);
+
+ printf("check that sd has been mapped correctly\n");
+ status = smb_raw_fileinfo(cli->tree, tctx, &q);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ if (!security_descriptor_equal(q.query_secdesc.out.sd, sd)) {
+ printf("%s: security descriptors don't match!\n", __location__);
+ printf("got:\n");
+ NDR_PRINT_DEBUG(security_descriptor, q.query_secdesc.out.sd);
+ printf("expected:\n");
+ NDR_PRINT_DEBUG(security_descriptor, sd);
+ ret = false;
+ }
+
+ printf("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);
+
+ printf("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);
+
+ printf("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);
+
+ printf("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);
+
+ printf("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);
+
+ printf("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);
+ if (!security_descriptor_equal(q.query_secdesc.out.sd, sd2)) {
+ printf("%s: security descriptors don't match!\n", __location__);
+ printf("got:\n");
+ NDR_PRINT_DEBUG(security_descriptor, q.query_secdesc.out.sd);
+ printf("expected:\n");
+ NDR_PRINT_DEBUG(security_descriptor, sd2);
+ ret = false;
+ }
+
+
+ printf("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);
+
+ printf("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);
+
+ printf("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);
+
+ printf("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);
+
+
+ printf("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);
+ return ret;
+}
+
+
+/*
+ test the mapping of the SEC_GENERIC_xx bits to SEC_STD_xx and
+ SEC_FILE_xx bits
+*/
+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;
+
+ printf("TESTING FILE GENERIC BITS\n");
+
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 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;
+
+ printf("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 = 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)) {
+ printf("smblsa_sid_check_privilege - %s\n", nt_errstr(status));
+ }
+ printf("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)) {
+ printf("smblsa_sid_check_privilege - %s\n", nt_errstr(status));
+ }
+ printf("SEC_PRIV_TAKE_OWNERSHIP - %s\n", has_take_ownership_privilege?"Yes":"No");
+
+ for (i=0;i<ARRAY_SIZE(file_mappings);i++) {
+ uint32_t expected_mask =
+ SEC_STD_WRITE_DAC |
+ SEC_STD_READ_CONTROL |
+ SEC_FILE_READ_ATTRIBUTE |
+ SEC_STD_DELETE;
+ uint32_t expected_mask_anon = SEC_FILE_READ_ATTRIBUTE;
+
+ if (has_restore_privilege) {
+ expected_mask_anon |= SEC_STD_DELETE;
+ }
+
+ printf("testing generic bits 0x%08x\n",
+ file_mappings[i].gen_bits);
+ sd = security_descriptor_dacl_create(tctx,
+ 0, owner_sid, 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, 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);
+ if (!security_descriptor_equal(q.query_secdesc.out.sd, sd2)) {
+ printf("%s: security descriptors don't match!\n", __location__);
+ printf("got:\n");
+ NDR_PRINT_DEBUG(security_descriptor, q.query_secdesc.out.sd);
+ printf("expected:\n");
+ NDR_PRINT_DEBUG(security_descriptor, sd2);
+ ret = false;
+ }
+
+ 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;
+ }
+
+ printf("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);
+ if (!security_descriptor_equal(q.query_secdesc.out.sd, sd2)) {
+ printf("%s: security descriptors don't match!\n", __location__);
+ printf("got:\n");
+ NDR_PRINT_DEBUG(security_descriptor, q.query_secdesc.out.sd);
+ printf("expected:\n");
+ NDR_PRINT_DEBUG(security_descriptor, sd2);
+ ret = false;
+ }
+
+ 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);
+ }
+
+ printf("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);
+
+
+ printf("TESTING DIR GENERIC BITS\n");
+
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 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;
+
+ printf("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 = 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)) {
+ printf("smblsa_sid_check_privilege - %s\n", nt_errstr(status));
+ }
+ printf("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)) {
+ printf("smblsa_sid_check_privilege - %s\n", nt_errstr(status));
+ }
+ printf("SEC_PRIV_TAKE_OWNERSHIP - %s\n", has_take_ownership_privilege?"Yes":"No");
+
+ for (i=0;i<ARRAY_SIZE(dir_mappings);i++) {
+ uint32_t expected_mask =
+ SEC_STD_WRITE_DAC |
+ SEC_STD_READ_CONTROL |
+ SEC_FILE_READ_ATTRIBUTE |
+ SEC_STD_DELETE;
+ uint32_t expected_mask_anon = SEC_FILE_READ_ATTRIBUTE;
+
+ if (has_restore_privilege) {
+ expected_mask_anon |= SEC_STD_DELETE;
+ }
+
+ printf("testing generic bits 0x%08x\n",
+ file_mappings[i].gen_bits);
+ sd = security_descriptor_dacl_create(tctx,
+ 0, owner_sid, NULL,
+ owner_sid,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ dir_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, 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);
+ if (!security_descriptor_equal(q.query_secdesc.out.sd, sd2)) {
+ printf("%s: security descriptors don't match!\n", __location__);
+ printf("got:\n");
+ NDR_PRINT_DEBUG(security_descriptor, q.query_secdesc.out.sd);
+ printf("expected:\n");
+ NDR_PRINT_DEBUG(security_descriptor, sd2);
+ ret = false;
+ }
+
+ 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;
+ }
+
+ printf("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);
+ if (!security_descriptor_equal(q.query_secdesc.out.sd, sd2)) {
+ printf("%s: security descriptors don't match!\n", __location__);
+ printf("got:\n");
+ NDR_PRINT_DEBUG(security_descriptor, q.query_secdesc.out.sd);
+ printf("expected:\n");
+ NDR_PRINT_DEBUG(security_descriptor, sd2);
+ ret = false;
+ }
+
+ 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);
+ }
+
+ printf("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);
+ return ret;
+}
+
+
+/*
+ see what access bits the owner of a file always gets
+*/
+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;
+
+ printf("TESTING FILE OWNER BITS\n");
+
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 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;
+
+ printf("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 = 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)) {
+ printf("smblsa_sid_check_privilege - %s\n", nt_errstr(status));
+ }
+ printf("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)) {
+ printf("smblsa_sid_check_privilege - %s\n", nt_errstr(status));
+ }
+ printf("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<<i);
+ io.ntcreatex.in.access_mask = bit;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ if (expected_bits & bit) {
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("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)) {
+ printf("open succeeded with access mask 0x%08x of "
+ "expected 0x%08x - should fail\n",
+ bit, expected_bits);
+ }
+ CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
+ }
+ }
+
+ printf("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);
+ return ret;
+}
+
+
+
+/*
+ test the inheritance of ACL flags onto new files and directories
+*/
+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_def;
+ 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,
+ }
+ };
+
+ smbcli_rmdir(cli->tree, dname);
+
+ printf("TESTING ACL INHERITANCE\n");
+
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 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;
+
+ printf("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);
+
+ printf("owner_sid is %s\n", owner_sid);
+
+ sd_def = 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);
+
+ creator_owner = dom_sid_parse_talloc(tctx, SID_CREATOR_OWNER);
+
+ for (i=0;i<ARRAY_SIZE(test_flags);i++) {
+ sd = security_descriptor_dacl_create(tctx,
+ 0, NULL, NULL,
+ SID_CREATOR_OWNER,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ SEC_FILE_WRITE_DATA,
+ test_flags[i].parent_flags,
+ 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);
+
+ 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_def)) {
+ printf("Expected default sd:\n");
+ NDR_PRINT_DEBUG(security_descriptor, sd_def);
+ printf("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)) {
+ printf("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) {
+ printf("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_def)) {
+ printf("Expected default sd for dir at %d:\n", i);
+ NDR_PRINT_DEBUG(security_descriptor, sd_def);
+ printf("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) {
+ printf("(CI & NP) Bad sd in child dir at %d (parent 0x%x)\n",
+ i, test_flags[i].parent_flags);
+ NDR_PRINT_DEBUG(security_descriptor, q.query_secdesc.out.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)) {
+ printf("(CI) Bad sd in child dir at %d (parent 0x%x)\n",
+ i, test_flags[i].parent_flags);
+ NDR_PRINT_DEBUG(security_descriptor, q.query_secdesc.out.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) {
+ printf("(0) Bad sd in child dir at %d (parent 0x%x)\n",
+ i, test_flags[i].parent_flags);
+ NDR_PRINT_DEBUG(security_descriptor, q.query_secdesc.out.sd);
+ ret = false;
+ continue;
+ }
+ }
+ }
+
+ printf("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);
+
+ 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);
+ if (!security_descriptor_equal(q.query_secdesc.out.sd, sd2)) {
+ printf("%s: security descriptors don't match!\n", __location__);
+ printf("got:\n");
+ NDR_PRINT_DEBUG(security_descriptor, q.query_secdesc.out.sd);
+ printf("expected:\n");
+ NDR_PRINT_DEBUG(security_descriptor, sd2);
+ ret = false;
+ }
+
+ 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)) {
+ printf("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 {
+ CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
+ }
+
+ printf("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);
+ CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
+
+ printf("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);
+ 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);
+
+ printf("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);
+ 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);
+
+ smbcli_unlink(cli->tree, fname1);
+ smbcli_rmdir(cli->tree, dname);
+
+done:
+ 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);
+ return ret;
+}
+
+
+/*
+ test dynamic acl inheritance
+*/
+static bool test_inheritance_dynamic(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;
+ union smb_fileinfo q;
+ union smb_setfileinfo set;
+ struct security_descriptor *sd, *sd_orig=NULL;
+ const char *owner_sid;
+
+ printf("TESTING DYNAMIC ACL INHERITANCE\n");
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 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;
+
+ printf("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);
+
+ printf("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);
+
+ printf("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);
+
+ printf("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);
+
+ printf("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);
+
+ printf("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);
+
+ printf("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);
+
+
+ printf("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)) {
+ printf("Server does not have dynamic inheritance\n");
+ }
+ if (NT_STATUS_EQUAL(status, NT_STATUS_OK)) {
+ printf("Server does have dynamic inheritance\n");
+ }
+ CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
+
+ smbcli_unlink(cli->tree, fname1);
+
+done:
+ printf("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);
+
+ 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)) {\
+ printf("(%s) %s but flags 0x%08X are not granted! granted[0x%08X] desired[0x%08X]\n", \
+ __location__, nt_errstr(status), access, granted, desired); \
+ ret = false; \
+ goto done; \
+ } \
+ } else { \
+ if (granted & access) {\
+ printf("(%s) %s but flags 0x%08X are granted! granted[0x%08X] desired[0x%08X]\n", \
+ __location__, nt_errstr(status), access, granted, desired); \
+ ret = false; \
+ goto done; \
+ } \
+ } \
+ CHECK_STATUS_FOR_BIT_ACTION(status, bits, do {} while (0)); \
+} while (0)
+
+/* test what access mask is needed for getting and setting security_descriptors */
+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;
+
+ printf("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 = 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);
+
+ return ret;
+}
+
+
+/*
+ basic testing of security descriptor calls
+*/
+bool torture_raw_acls(struct torture_context *tctx, struct smbcli_state *cli)
+{
+ bool ret = true;
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ ret &= test_sd(tctx, cli);
+ ret &= test_nttrans_create(tctx, cli);
+ ret &= test_nttrans_create_null_dacl(tctx, cli);
+ ret &= test_creator_sid(tctx, cli);
+ ret &= test_generic_bits(tctx, cli);
+ ret &= test_owner_bits(tctx, cli);
+ ret &= test_inheritance(tctx, cli);
+ ret &= test_inheritance_dynamic(tctx, cli);
+ ret &= test_sd_get_set(tctx, cli);
+
+ smb_raw_exit(cli->session);
+ smbcli_deltree(cli->tree, BASEDIR);
+
+ return ret;
+}
diff --git a/source4/torture/raw/chkpath.c b/source4/torture/raw/chkpath.c
new file mode 100644
index 0000000000..a39993489b
--- /dev/null
+++ b/source4/torture/raw/chkpath.c
@@ -0,0 +1,394 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/locale.h"
+#include "torture/torture.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/libcli.h"
+#include "torture/util.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;
+ int fnum1 = -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. */
+ fnum1 = 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. */
+ fnum1 = 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");
+ fnum1 = 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;
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ 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 0000000000..01175836df
--- /dev/null
+++ b/source4/torture/raw/close.c
@@ -0,0 +1,177 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#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"
+
+/**
+ * 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 0000000000..49564dc1ad
--- /dev/null
+++ b/source4/torture/raw/composite.c
@@ -0,0 +1,430 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#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/popt_common.h"
+#include "torture/util.h"
+#include "param/param.h"
+#include "libcli/resolve/resolve.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 smbcli_state *cli, struct torture_context *tctx)
+{
+ 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;
+
+ printf("testing savefile\n");
+
+ status = smb_composite_savefile(cli->tree, &io1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("(%s) savefile failed: %s\n", __location__,nt_errstr(status));
+ return false;
+ }
+
+ io2.in.fname = fname;
+
+ printf("testing parallel loadfile with %d ops\n", num_ops);
+
+ c = talloc_array(tctx, struct composite_context *, num_ops);
+
+ for (i=0;i<num_ops;i++) {
+ c[i] = smb_composite_loadfile_send(cli->tree, &io2);
+ c[i]->async.fn = loadfile_complete;
+ c[i]->async.private_data = count;
+ }
+
+ printf("waiting for completion\n");
+ while (*count != num_ops) {
+ event_loop_once(cli->transport->socket->event.ctx);
+ if (torture_setting_bool(tctx, "progress", true)) {
+ printf("(%s) count=%d\r", __location__, *count);
+ fflush(stdout);
+ }
+ }
+ printf("count=%d\n", *count);
+
+ for (i=0;i<num_ops;i++) {
+ status = smb_composite_loadfile_recv(c[i], tctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("(%s) loadfile[%d] failed - %s\n", __location__, i, nt_errstr(status));
+ return false;
+ }
+
+ if (io2.out.size != len) {
+ printf("(%s) wrong length in returned data - %d should be %d\n",__location__,
+ io2.out.size, (int)len);
+ return false;
+ }
+
+ if (memcmp(io2.out.data, data, len) != 0) {
+ printf("(%s) wrong data in loadfile!\n",__location__);
+ return false;
+ }
+ }
+
+ talloc_free(data);
+
+ return true;
+}
+
+/*
+ test a simple savefile/loadfile combination
+*/
+static bool test_fetchfile(struct smbcli_state *cli, struct torture_context *tctx)
+{
+ 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);
+
+ io1.in.fname = fname;
+ io1.in.data = data;
+ io1.in.size = len;
+
+ printf("testing savefile\n");
+
+ status = smb_composite_savefile(cli->tree, &io1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("(%s) savefile failed: %s\n",__location__, nt_errstr(status));
+ return false;
+ }
+
+ io2.in.dest_host = torture_setting_string(tctx, "host", NULL);
+ io2.in.ports = lp_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.credentials = cmdline_credentials;
+ io2.in.workgroup = lp_workgroup(tctx->lp_ctx);
+ io2.in.filename = fname;
+ io2.in.resolve_ctx = lp_resolve_context(tctx->lp_ctx);
+ io2.in.iconv_convenience = lp_iconv_convenience(tctx->lp_ctx);
+ io2.in.gensec_settings = lp_gensec_settings(tctx, tctx->lp_ctx);
+ lp_smbcli_options(tctx->lp_ctx, &io2.in.options);
+ lp_smbcli_session_options(tctx->lp_ctx, &io2.in.session_options);
+
+ printf("testing parallel fetchfile with %d ops\n", torture_numops);
+
+ event_ctx = cli->transport->socket->event.ctx;
+ c = talloc_array(tctx, struct composite_context *, torture_numops);
+
+ for (i=0; i<torture_numops; i++) {
+ c[i] = smb_composite_fetchfile_send(&io2, event_ctx);
+ c[i]->async.fn = loadfile_complete;
+ c[i]->async.private_data = count;
+ }
+
+ printf("waiting for completion\n");
+
+ while (*count != torture_numops) {
+ event_loop_once(event_ctx);
+ if (torture_setting_bool(tctx, "progress", true)) {
+ printf("(%s) count=%d\r", __location__, *count);
+ fflush(stdout);
+ }
+ }
+ printf("count=%d\n", *count);
+
+ for (i=0;i<torture_numops;i++) {
+ status = smb_composite_fetchfile_recv(c[i], tctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("(%s) loadfile[%d] failed - %s\n", __location__, i,
+ nt_errstr(status));
+ ret = false;
+ continue;
+ }
+
+ if (io2.out.size != len) {
+ printf("(%s) wrong length in returned data - %d "
+ "should be %d\n", __location__,
+ io2.out.size, (int)len);
+ ret = false;
+ continue;
+ }
+
+ if (memcmp(io2.out.data, data, len) != 0) {
+ printf("(%s) wrong data in loadfile!\n", __location__);
+ ret = false;
+ continue;
+ }
+ }
+
+ return ret;
+}
+
+/*
+ test setfileacl
+*/
+static bool test_appendacl(struct smbcli_state *cli, struct torture_context *tctx)
+{
+ 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);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("(%s) savefile failed: %s\n", __location__, nt_errstr(status));
+ return false;
+ }
+
+ 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]);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("(%s) appendacl failed: %s\n", __location__, nt_errstr(status));
+ return false;
+ }
+ }
+
+
+ /* 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);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("(%s) appendacl failed: %s\n", __location__, nt_errstr(status));
+ return false;
+ }
+
+ /* set parameters for appendacl async call */
+
+ printf("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;
+ printf("waiting for completion\n");
+ while (*count != num_ops) {
+ event_loop_once(event_ctx);
+ if (torture_setting_bool(tctx, "progress", true)) {
+ printf("(%s) count=%d\r", __location__, *count);
+ fflush(stdout);
+ }
+ }
+ printf("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)) {
+ printf("(%s) appendacl[%d] failed - %s\n", __location__, i, nt_errstr(status));
+ return false;
+ }
+
+ security_descriptor_dacl_add(io_orig[i]->out.sd, ace);
+ if (!security_acl_equal(io_orig[i]->out.sd->dacl, io[i]->out.sd->dacl)) {
+ printf("(%s) appendacl[%d] failed - needed acl isn't set\n", __location__, i);
+ return false;
+ }
+ }
+
+
+ talloc_free (ace);
+ talloc_free (test_sid);
+ talloc_free (test_sd);
+
+ return true;
+}
+
+/* test a query FS info by asking for share's GUID */
+static bool test_fsinfo(struct smbcli_state *cli, struct torture_context *tctx)
+{
+ 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 = lp_smb_ports(tctx->lp_ctx);
+ io1.in.socket_options = lp_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 = cmdline_credentials;
+ io1.in.workgroup = lp_workgroup(tctx->lp_ctx);
+ io1.in.level = RAW_QFS_OBJECTID_INFORMATION;
+ io1.in.iconv_convenience = lp_iconv_convenience(tctx->lp_ctx);
+ io1.in.gensec_settings = lp_gensec_settings(tctx, tctx->lp_ctx);
+
+ printf("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; i<torture_numops; i++) {
+ c[i] = smb_composite_fsinfo_send(cli->tree, &io1, lp_resolve_context(tctx->lp_ctx));
+ c[i]->async.fn = loadfile_complete;
+ c[i]->async.private_data = count;
+ }
+
+ printf("waiting for completion\n");
+
+ while (*count < torture_numops) {
+ event_loop_once(event_ctx);
+ if (torture_setting_bool(tctx, "progress", true)) {
+ printf("(%s) count=%d\r", __location__, *count);
+ fflush(stdout);
+ }
+ }
+ printf("count=%d\n", *count);
+
+ for (i=0;i<torture_numops;i++) {
+ status = smb_composite_fsinfo_recv(c[i], tctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("(%s) fsinfo[%d] failed - %s\n", __location__, i, nt_errstr(status));
+ ret = false;
+ continue;
+ }
+
+ if (io1.out.fsinfo->generic.level != RAW_QFS_OBJECTID_INFORMATION) {
+ printf("(%s) wrong level in returned info - %d "
+ "should be %d\n", __location__,
+ io1.out.fsinfo->generic.level, RAW_QFS_OBJECTID_INFORMATION);
+ ret = false;
+ continue;
+ }
+
+ guid=GUID_string(tctx, &io1.out.fsinfo->objectid_information.out.guid);
+ printf("[%d] GUID: %s\n", i, guid);
+
+
+ }
+
+ return ret;
+}
+
+
+/*
+ basic testing of libcli composite calls
+*/
+bool torture_raw_composite(struct torture_context *tctx,
+ struct smbcli_state *cli)
+{
+ bool ret = true;
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ ret &= test_fetchfile(cli, tctx);
+ ret &= test_loadfile(cli, tctx);
+ ret &= test_appendacl(cli, tctx);
+ ret &= test_fsinfo(cli, tctx);
+
+ smb_raw_exit(cli->session);
+ smbcli_deltree(cli->tree, BASEDIR);
+
+ return ret;
+}
diff --git a/source4/torture/raw/context.c b/source4/torture/raw/context.c
new file mode 100644
index 0000000000..a9d36b7788
--- /dev/null
+++ b/source4/torture/raw/context.c
@@ -0,0 +1,917 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/composite/composite.h"
+#include "libcli/smb_composite/smb_composite.h"
+#include "lib/cmdline/popt_common.h"
+#include "lib/events/events.h"
+#include "libcli/libcli.h"
+#include "torture/util.h"
+#include "auth/credentials/credentials.h"
+#include "param/param.h"
+
+#define BASEDIR "\\rawcontext"
+
+#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 CHECK_VALUE(v, correct) do { \
+ if ((v) != (correct)) { \
+ printf("(%s) Incorrect value %s=%d - should be %d\n", \
+ __location__, #v, v, correct); \
+ ret = false; \
+ goto done; \
+ }} while (0)
+
+#define CHECK_NOT_VALUE(v, correct) do { \
+ if ((v) == (correct)) { \
+ printf("(%s) Incorrect value %s=%d - should not be %d\n", \
+ __location__, #v, v, correct); \
+ ret = false; \
+ goto done; \
+ }} while (0)
+
+
+/*
+ test session ops
+*/
+static bool test_session(struct smbcli_state *cli, struct torture_context *tctx)
+{
+ NTSTATUS status;
+ bool ret = true;
+ struct smbcli_session *session;
+ struct smbcli_session *session2;
+ 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;
+
+ printf("TESTING SESSION HANDLING\n");
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ printf("create a second security context on the same transport\n");
+
+ lp_smbcli_session_options(tctx->lp_ctx, &options);
+ gensec_settings = lp_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 = lp_workgroup(tctx->lp_ctx);
+
+ setup.in.credentials = cmdline_credentials;
+ setup.in.gensec_settings = gensec_settings;
+
+ status = smb_composite_sesssetup(session, &setup);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ session->vuid = setup.out.vuid;
+
+ printf("create a third security context on the same transport, with vuid set\n");
+ session2 = smbcli_session_init(cli->transport, tctx, false, options);
+
+ session2->vuid = session->vuid;
+ 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 = lp_workgroup(tctx->lp_ctx);
+
+ setup.in.credentials = cmdline_credentials;
+
+ status = smb_composite_sesssetup(session2, &setup);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ session2->vuid = setup.out.vuid;
+ printf("vuid1=%d vuid2=%d vuid3=%d\n", cli->session->vuid, session->vuid, session2->vuid);
+
+ if (cli->transport->negotiate.capabilities & CAP_EXTENDED_SECURITY) {
+ /* Samba4 currently fails this - we need to determine if this insane behaviour is important */
+ if (session2->vuid == session->vuid) {
+ printf("server allows the user to re-use an existing vuid in session setup \n");
+ }
+ } else {
+ CHECK_NOT_VALUE(session2->vuid, session->vuid);
+ }
+ talloc_free(session2);
+
+ if (cli->transport->negotiate.capabilities & CAP_EXTENDED_SECURITY) {
+ printf("create a fourth security context on the same transport, without extended security\n");
+ session3 = smbcli_session_init(cli->transport, tctx, false, options);
+
+ session3->vuid = session->vuid;
+ setup.in.sesskey = cli->transport->negotiate.sesskey;
+ setup.in.capabilities &= ~CAP_EXTENDED_SECURITY; /* force a non extended security login (should fail) */
+ setup.in.workgroup = lp_workgroup(tctx->lp_ctx);
+
+ setup.in.credentials = cmdline_credentials;
+
+ status = smb_composite_sesssetup(session3, &setup);
+ CHECK_STATUS(status, NT_STATUS_LOGON_FAILURE);
+
+ printf("create a fouth anonymous security context on the same transport, without extended security\n");
+ session4 = smbcli_session_init(cli->transport, tctx, false, options);
+
+ session4->vuid = session->vuid;
+ setup.in.sesskey = cli->transport->negotiate.sesskey;
+ setup.in.capabilities &= ~CAP_EXTENDED_SECURITY; /* force a non extended security login (should fail) */
+ setup.in.workgroup = lp_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);
+ }
+
+ printf("use the same tree as the existing connection\n");
+ tree = smbcli_tree_init(session, tctx, false);
+ tree->tid = cli->tree->tid;
+
+ printf("create a file using the new vuid\n");
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 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;
+
+ printf("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);
+
+ printf("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);
+
+ printf("logoff the new vuid\n");
+ status = smb_raw_ulogoff(session);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("the new vuid should not now be accessible\n");
+ status = smb_raw_write(tree, &wr);
+ CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);
+
+ printf("second logoff for the new vuid should fail\n");
+ status = smb_raw_ulogoff(session);
+ CHECK_STATUS(status, NT_STATUS_DOS(ERRSRV, ERRbaduid));
+ talloc_free(session);
+
+ printf("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);
+
+ printf("create %d secondary security contexts on the same transport\n",
+ (int)ARRAY_SIZE(sessions));
+ for (i=0; i <ARRAY_SIZE(sessions); i++) {
+ setups[i].in.sesskey = cli->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 = lp_workgroup(tctx->lp_ctx);
+
+ setups[i].in.credentials = cmdline_credentials;
+ 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]);
+
+ }
+
+
+ printf("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;
+ printf("VUID: %d\n", sessions[i]->vuid);
+ status = smb_raw_ulogoff(sessions[i]);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ }
+
+
+ talloc_free(tree);
+
+done:
+ return ret;
+}
+
+
+/*
+ test tree ops
+*/
+static bool test_tree(struct smbcli_state *cli, struct torture_context *tctx)
+{
+ 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;
+
+ printf("TESTING TREE HANDLING\n");
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ share = torture_setting_string(tctx, "share", NULL);
+ host = torture_setting_string(tctx, "host", NULL);
+
+ printf("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 = 0;
+ 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;
+ printf("tid1=%d tid2=%d\n", cli->tree->tid, tree->tid);
+
+ printf("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);
+
+
+ printf("create a file using the new tid\n");
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 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;
+
+ printf("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);
+
+ printf("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);
+
+ printf("disconnect the new tid\n");
+ status = smb_tree_disconnect(tree);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("the new tid should not now be accessible\n");
+ status = smb_raw_write(tree, &wr);
+ CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);
+
+ printf("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 smbcli_state *cli, struct torture_context *tctx)
+{
+ 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;
+
+ printf("TESTING TREE with ulogoff\n");
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ share = torture_setting_string(tctx, "share", NULL);
+ host = torture_setting_string(tctx, "host", NULL);
+
+ lp_smbcli_session_options(tctx->lp_ctx, &options);
+
+ printf("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 = lp_workgroup(tctx->lp_ctx);
+ setup.in.credentials = cmdline_credentials;
+ setup.in.gensec_settings = lp_gensec_settings(tctx, tctx->lp_ctx);
+ status = smb_composite_sesssetup(session1, &setup);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ session1->vuid = setup.out.vuid;
+ printf("vuid1=%d\n", session1->vuid);
+
+ printf("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 = 0;
+ 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;
+ printf("tid=%d\n", tree->tid);
+
+ printf("create a file using vuid1\n");
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 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;
+
+ printf("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);
+
+ printf("ulogoff the vuid1\n");
+ status = smb_raw_ulogoff(session1);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("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 = lp_workgroup(tctx->lp_ctx);
+ setup.in.credentials = cmdline_credentials;
+ setup.in.gensec_settings = lp_gensec_settings(tctx, tctx->lp_ctx);
+ status = smb_composite_sesssetup(session2, &setup);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ session2->vuid = setup.out.vuid;
+ printf("vuid2=%d\n", session2->vuid);
+
+ printf("use the existing tree with vuid2\n");
+ tree->session = session2;
+
+ printf("create a file using vuid2\n");
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 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;
+
+ printf("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);
+
+ printf("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 */
+ printf("disconnect the existing tree connection\n");
+ status = smb_tree_disconnect(tree);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("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 smbcli_state *cli, TALLOC_CTX *mem_ctx)
+{
+ NTSTATUS status;
+ 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;
+
+ printf("TESTING PID HANDLING exit() only cares about open() PID\n");
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ pid1 = cli->session->pid;
+ pid2 = pid1 + 1;
+
+ printf("pid1=%d pid2=%d\n", pid1, pid2);
+
+ printf("create a file using pid1\n");
+ cli->session->pid = pid1;
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 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;
+
+ printf("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);
+
+ printf("exit pid2\n");
+ cli->session->pid = pid2;
+ status = smb_raw_exit(cli->session);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("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);
+
+ printf("exit pid2\n");
+ cli->session->pid = pid2;
+ status = smb_raw_exit(cli->session);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("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);
+
+ printf("exit pid1\n");
+ cli->session->pid = pid1;
+ status = smb_raw_exit(cli->session);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("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);
+
+ printf("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 smbcli_state *cli, struct torture_context *tctx)
+{
+ 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;
+
+ printf("TESTING PID HANDLING WITH 2 SESSIONS\n");
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ lp_smbcli_session_options(tctx->lp_ctx, &options);
+
+ printf("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 = lp_workgroup(tctx->lp_ctx);
+ setup.in.credentials = cmdline_credentials;
+ setup.in.gensec_settings = lp_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;
+
+ printf("vuid1=%d vuid2=%d\n", vuid1, vuid2);
+
+ printf("create a file using the vuid1\n");
+ cli->session->vuid = vuid1;
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 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;
+
+ printf("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);
+
+ printf("exit the pid with vuid2\n");
+ cli->session->vuid = vuid2;
+ status = smb_raw_exit(cli->session);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("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);
+
+ printf("exit the pid with vuid1\n");
+ cli->session->vuid = vuid1;
+ status = smb_raw_exit(cli->session);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("the fnum should not now be accessible\n");
+ status = smb_raw_write(cli->tree, &wr);
+ CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);
+
+ printf("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 smbcli_state *cli, struct torture_context *tctx)
+{
+ 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;
+
+ printf("TESTING PID HANDLING WITH 2 TCONS\n");
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ share = torture_setting_string(tctx, "share", NULL);
+ host = torture_setting_string(tctx, "host", NULL);
+
+ printf("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 = 0;
+ 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;
+ printf("tid1=%d tid2=%d\n", tid1, tid2);
+
+ printf("create a file using the tid1\n");
+ cli->tree->tid = tid1;
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 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;
+
+ printf("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);
+
+ printf("create a file using the tid2\n");
+ cli->tree->tid = tid2;
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 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;
+
+ printf("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);
+
+ printf("exit the pid\n");
+ status = smb_raw_exit(cli->session);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("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);
+
+ printf("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);
+
+ printf("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);
+
+ printf("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;
+}
+
+
+/*
+ basic testing of session/tree context calls
+*/
+static bool torture_raw_context_int(struct torture_context *tctx,
+ struct smbcli_state *cli)
+{
+ bool ret = true;
+
+ ret &= test_session(cli, tctx);
+ ret &= test_tree(cli, tctx);
+ ret &= test_tree_ulogoff(cli, tctx);
+ ret &= test_pid_exit_only_sees_open(cli, tctx);
+ ret &= test_pid_2sess(cli, tctx);
+ ret &= test_pid_2tcon(cli, tctx);
+
+ smb_raw_exit(cli->session);
+ smbcli_deltree(cli->tree, BASEDIR);
+
+ return ret;
+}
+/*
+ basic testing of session/tree context calls
+*/
+bool torture_raw_context(struct torture_context *torture,
+ struct smbcli_state *cli)
+{
+ bool ret = true;
+ if (lp_use_spnego(torture->lp_ctx)) {
+ ret &= torture_raw_context_int(torture, cli);
+ lp_set_cmdline(torture->lp_ctx, "use spnego", "False");
+ }
+
+ ret &= torture_raw_context_int(torture, cli);
+
+ return ret;
+}
diff --git a/source4/torture/raw/eas.c b/source4/torture/raw/eas.c
new file mode 100644
index 0000000000..49a81dd534
--- /dev/null
+++ b/source4/torture/raw/eas.c
@@ -0,0 +1,489 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/libcli.h"
+#include "torture/util.h"
+
+#define BASEDIR "\\testeas"
+
+#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)
+
+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 bool test_eas(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
+{
+ NTSTATUS status;
+ union smb_setfileinfo setfile;
+ union smb_open io;
+ const char *fname = BASEDIR "\\ea.txt";
+ bool ret = true;
+ int fnum = -1;
+
+ printf("TESTING SETFILEINFO EA_SET\n");
+
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 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;
+
+ ret &= check_ea(cli, fname, "EAONE", NULL);
+
+ printf("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(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_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");
+
+ printf("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");
+
+ printf("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);
+
+ printf("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");
+
+ printf("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);
+
+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 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) {
+ printf ("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) {
+ printf ("[%s] EA size %d succeeded! "
+ "(high=%d low=%d)\n",
+ eaname, i, high, low);
+ }
+ low = i;
+ if (low == maxeasize) {
+ printf ("Max. EA size for \"%s\"=%d "
+ "[but could be possibly larger]\n",
+ eaname, low);
+ break;
+ }
+ if (high - low == 1 && high != maxeasize) {
+ printf ("Max. EA size for \"%s\"=%d\n",
+ eaname, low);
+ break;
+ }
+ i += (high - low + 1) / 2;
+ } else {
+ if (eadebug) {
+ printf ("[%s] EA size %d failed! "
+ "(high=%d low=%d) [%s]\n",
+ eaname, i, high, low,
+ nt_errstr(status));
+ }
+ high = i;
+ if (high - low <= 1) {
+ printf ("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, total;
+ DATA_BLOB eablob;
+ char *eaname = NULL;
+ int maxeasize;
+ int maxeanames;
+ int maxeastart;
+
+ printf("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_int(tctx, "maxeadebug", 0);
+
+ /* Do some sanity check on possibly passed parms */
+ if (maxeasize <= 0) {
+ printf("Invalid parameter 'maxeasize=%d'",maxeasize);
+ err = true;
+ }
+ if (maxeanames <= 0) {
+ printf("Invalid parameter 'maxeanames=%d'",maxeanames);
+ err = true;
+ }
+ if (maxeastart <= 0) {
+ printf("Invalid parameter 'maxeastart=%d'",maxeastart);
+ err = true;
+ }
+ if (maxeadebug < 0) {
+ printf("Invalid parameter 'maxeadebug=%d'",maxeadebug);
+ err = true;
+ }
+ if (err) {
+ printf("\n\n");
+ goto done;
+ }
+ if (maxeastart > maxeasize) {
+ maxeastart = maxeasize;
+ printf ("'maxeastart' outside range - corrected to %d\n",
+ maxeastart);
+ }
+ printf("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 = 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(cli, fnum, eaname, eablob, last, maxeadebug);
+ if (j <= 0) {
+ break;
+ }
+ total += j;
+ last = j;
+ }
+
+ printf("Total EA size:%d\n", total);
+ if (i == maxeanames) {
+ printf ("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, TALLOC_CTX *mem_ctx)
+{
+ NTSTATUS status;
+ union smb_open io;
+ const char *fname = BASEDIR "\\ea2.txt";
+ bool ret = true;
+ int fnum = -1;
+ struct ea_struct eas[3];
+ struct smb_ea_list ea_list;
+
+ printf("TESTING NTTRANS CREATE WITH EAS\n");
+
+ io.generic.level = RAW_OPEN_NTTRANS_CREATE;
+ io.ntcreatex.in.root_fid = 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, mem_ctx, &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);
+
+ printf("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, mem_ctx, &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);
+
+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;
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ 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;
+ }
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ 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 0000000000..4cb366f807
--- /dev/null
+++ b/source4/torture/raw/ioctl.c
@@ -0,0 +1,171 @@
+/*
+ Unix SMB/CIFS implementation.
+ ioctl individual test suite
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) James J Myers 2003 <myersjj@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "libcli/raw/ioctl.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/libcli.h"
+#include "torture/util.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 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;
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ 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 0000000000..72a03e1623
--- /dev/null
+++ b/source4/torture/raw/lock.c
@@ -0,0 +1,1899 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#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/popt_common.h"
+#include "param/param.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 CHECK_STATUS_CONT(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; \
+ }} while (0)
+
+#define CHECK_STATUS_OR(status, correct1, correct2) do { \
+ if ((!NT_STATUS_EQUAL(status, correct1)) && \
+ (!NT_STATUS_EQUAL(status, correct2))) { \
+ printf("(%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))) { \
+ printf("(%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_WIN7(_tctx) (torture_setting_bool(_tctx, "win7", false))
+
+/*
+ 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 (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ printf("Testing RAW_LOCK_LOCK\n");
+ io.generic.level = RAW_LOCK_LOCK;
+
+ 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 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);
+
+ printf("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);
+
+ printf("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);
+
+ printf("Trying 0xEF000000 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);
+
+ printf("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);
+
+ printf("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";
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ printf("Testing RAW_LOCK_LOCKX\n");
+ io.generic.level = RAW_LOCK_LOCKX;
+
+ 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;
+ }
+
+ 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);
+
+
+ printf("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);
+
+ printf("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);
+
+ printf("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);
+
+ printf("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);
+
+ printf("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);
+
+ printf("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);
+
+ printf("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_IS_WIN7(tctx))
+ CHECK_STATUS(status, NT_STATUS_WIN7_INVALID_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);
+
+ /* XXX This is very strange - Win7 gives us an invalid range when we
+ * unlock the range even though the range is locked! Win7 bug? */
+ if (TARGET_IS_WIN7(tctx))
+ CHECK_STATUS(status, NT_STATUS_WIN7_INVALID_RANGE);
+ else {
+ 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;
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ printf("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);
+ if (fnum == -1) {
+ printf("Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree));
+ ret = false;
+ goto done;
+ }
+
+ if (smbcli_write(cli->tree, fnum, 0, &c, 0, 1) != 1) {
+ printf("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) {
+ printf("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) {
+ printf("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) {
+ printf("High pid is used on this server!\n");
+ ret = false;
+ } else {
+ printf("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;
+ struct smbcli_session_options options;
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ lp_smbcli_session_options(tctx->lp_ctx, &options);
+
+ printf("Testing LOCKING_ANDX_CANCEL_LOCK\n");
+ io.generic.level = RAW_LOCK_LOCKX;
+
+ 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;
+ }
+
+ 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);
+
+ t = time(NULL);
+
+ printf("testing cancel by CANCEL_LOCK\n");
+
+ /* setup a timed lock */
+ io.lockx.in.timeout = 10000;
+ req = smb_raw_lock_send(cli->tree, &io);
+ if (req == NULL) {
+ printf("Failed to setup timed lock (%s)\n", __location__);
+ ret = false;
+ goto done;
+ }
+
+ /* 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);
+
+ if (time(NULL) > t+2) {
+ printf("lock cancel was not immediate (%s)\n", __location__);
+ ret = false;
+ goto done;
+ }
+
+ printf("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;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+ io.lockx.in.timeout = 5000;
+ req = smb_raw_lock_send(cli->tree, &io);
+ if (req == NULL) {
+ printf("Failed to setup timed lock (%s)\n", __location__);
+ ret = false;
+ goto done;
+ }
+
+ 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(NULL);
+ status = smbcli_request_simple_recv(req);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ if (time(NULL) > t+2) {
+ printf("lock cancel by unlock was not immediate (%s) - took %d secs\n",
+ __location__, (int)(time(NULL)-t));
+ ret = false;
+ goto done;
+ }
+
+ printf("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_FILE_LOCK_CONFLICT);
+
+ t = time(NULL);
+ io.lockx.in.timeout = 10000;
+ req = smb_raw_lock_send(cli->tree, &io);
+ if (req == NULL) {
+ printf("Failed to setup timed lock (%s)\n", __location__);
+ ret = false;
+ goto done;
+ }
+
+ status = smbcli_close(cli->tree, fnum);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = smbcli_request_simple_recv(req);
+ CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
+
+ if (time(NULL) > t+2) {
+ printf("lock cancel by close was not immediate (%s)\n", __location__);
+ ret = false;
+ goto done;
+ }
+
+ printf("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 = lp_workgroup(tctx->lp_ctx);
+ setup.in.credentials = cmdline_credentials;
+ setup.in.gensec_settings = lp_gensec_settings(tctx, tctx->lp_ctx);
+ status = smb_composite_sesssetup(session, &setup);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ session->vuid = setup.out.vuid;
+
+ printf("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 = 0;
+ 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;
+
+ printf("testing cancel by exit\n");
+ fname = BASEDIR "\\test_exit.txt";
+ fnum = smbcli_open(tree, fname, O_RDWR|O_CREAT, DENY_NONE);
+ if (fnum == -1) {
+ printf("Failed to reopen %s - %s\n", fname, smbcli_errstr(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 = 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(NULL);
+ req = smb_raw_lock_send(tree, &io);
+ if (req == NULL) {
+ printf("Failed to setup timed lock (%s)\n", __location__);
+ ret = false;
+ goto done;
+ }
+
+ status = smb_raw_exit(session);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = smbcli_request_simple_recv(req);
+ CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
+
+ if (time(NULL) > t+2) {
+ printf("lock cancel by exit was not immediate (%s)\n", __location__);
+ ret = false;
+ goto done;
+ }
+
+ printf("testing cancel by ulogoff\n");
+ fname = BASEDIR "\\test_ulogoff.txt";
+ fnum = smbcli_open(tree, fname, O_RDWR|O_CREAT, DENY_NONE);
+ if (fnum == -1) {
+ printf("Failed to reopen %s - %s\n", fname, smbcli_errstr(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 = 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(NULL);
+ req = smb_raw_lock_send(tree, &io);
+ if (req == NULL) {
+ printf("Failed to setup timed lock (%s)\n", __location__);
+ ret = false;
+ goto done;
+ }
+
+ status = smb_raw_ulogoff(session);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = smbcli_request_simple_recv(req);
+ if (NT_STATUS_EQUAL(NT_STATUS_FILE_LOCK_CONFLICT, status)) {
+ printf("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);
+
+ if (time(NULL) > t+2) {
+ printf("lock cancel by ulogoff was not immediate (%s)\n", __location__);
+ ret = false;
+ goto done;
+ }
+
+ printf("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);
+ if (fnum == -1) {
+ printf("Failed to reopen %s - %s\n", fname, smbcli_errstr(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 = 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(NULL);
+ req = smb_raw_lock_send(tree, &io);
+ if (req == NULL) {
+ printf("Failed to setup timed lock (%s)\n", __location__);
+ ret = false;
+ goto done;
+ }
+
+ status = smb_tree_disconnect(tree);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = smbcli_request_simple_recv(req);
+ CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
+
+ if (time(NULL) > t+2) {
+ printf("lock cancel by tdis was not immediate (%s)\n", __location__);
+ ret = false;
+ goto done;
+ }
+
+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;
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ printf("Testing LOCK_NOT_GRANTED vs. FILE_LOCK_CONFLICT\n");
+
+ printf("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
+ */
+ 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 | OPENX_MODE_DENY_DOS;
+ 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 the 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 the 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 the 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 the 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 the a successful lock in a different range,
+ * doesn't reset the cache, the failing lock on the 2nd handle
+ * resets the 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);
+ printf("testing with timeout > 0 (=1)\n");
+ fname = BASEDIR "\\test1.txt";
+ t = 1;
+ goto next_run;
+ }
+
+ /*
+ * 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);
+ printf("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);
+ if (fnum == -1) {
+ printf("Failed to reopen %s - %s\n", fname, 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 = 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(NULL);
+ io.lockx.in.timeout = 1000;
+ req = smb_raw_lock_send(cli->tree, &io);
+ if (req == NULL) {
+ printf("Failed to setup timed lock (%s)\n", __location__);
+ ret = false;
+ goto done;
+ }
+
+ 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);
+
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED);
+
+ if (time(NULL) < start+1) {
+ printf("lock comes back to early (%s)\n", __location__);
+ ret = false;
+ goto done;
+ }
+
+ smbcli_close(cli->tree, fnum);
+ fname = BASEDIR "\\test3.txt";
+ fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
+ if (fnum == -1) {
+ printf("Failed to reopen %s - %s\n", fname, 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 = 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(NULL);
+ io.lockx.in.timeout = 1000;
+ req = smb_raw_lock_send(cli->tree, &io);
+ if (req == NULL) {
+ printf("Failed to setup timed lock (%s)\n", __location__);
+ ret = false;
+ goto done;
+ }
+
+ 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);
+
+ lock[0].offset = 100;
+ lock[0].count = 10;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+ if (time(NULL) < start+1) {
+ printf("lock comes back to early (%s)\n", __location__);
+ ret = false;
+ goto done;
+ }
+
+ smbcli_close(cli->tree, fnum);
+ fname = BASEDIR "\\test4.txt";
+ fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
+ if (fnum == -1) {
+ printf("Failed to reopen %s - %s\n", fname, 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 = 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(NULL);
+ io.lockx.in.timeout = 1000;
+ req = smb_raw_lock_send(cli->tree, &io);
+ if (req == NULL) {
+ printf("Failed to setup timed lock (%s)\n", __location__);
+ ret = false;
+ goto done;
+ }
+
+ 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);
+
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+ if (time(NULL) < start+1) {
+ printf("lock comes back to early (%s)\n", __location__);
+ ret = false;
+ goto done;
+ }
+
+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";
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ printf("Testing LOCKING_ANDX_CHANGE_LOCKTYPE\n");
+ io.generic.level = RAW_LOCK_LOCKX;
+
+ 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;
+ }
+
+ 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) {
+ printf("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) {
+ printf("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.
+ */
+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;
+ struct smb_lock_entry zerozero;
+ NTSTATUS status;
+ bool ret = true;
+ int fnum, i;
+ const char *fname = BASEDIR "\\zero.txt";
+
+ printf("Testing zero length byte range locks:\n");
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ io.generic.level = RAW_LOCK_LOCKX;
+
+ 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;
+ }
+
+ /* 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 < sizeof(zero_byte_tests) / sizeof(struct double_lock_test);
+ i++) {
+ printf(" ... {%d, %llu, %llu} + {%d, %llu, %llu} = %s\n",
+ zero_byte_tests[i].lock1.pid,
+ zero_byte_tests[i].lock1.offset,
+ zero_byte_tests[i].lock1.count,
+ zero_byte_tests[i].lock2.pid,
+ zero_byte_tests[i].lock2.offset,
+ 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 = &zero_byte_tests[i].lock1;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ io.lockx.in.locks = &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 = &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;
+
+ printf("Testing LOCKX unlock:\n");
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
+ if (fnum1 == -1) {
+ printf("Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree));
+ ret = false;
+ goto done;
+ }
+ fnum2 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
+ if (fnum2 == -1) {
+ printf("Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree));
+ ret = false;
+ goto done;
+ }
+
+ /* 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.
+ */
+ printf(" 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);
+
+ printf(" 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);
+
+ printf(" 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.
+ */
+ printf(" 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);
+
+ printf(" 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.
+ */
+
+ printf(" 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 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_WIN7(tctx))
+ CHECK_STATUS(status, NT_STATUS_OK);
+ else {
+ 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);
+
+ /* XXX Same as above. */
+ if (TARGET_IS_WIN7(tctx))
+ CHECK_STATUS(status, NT_STATUS_OK);
+ else
+ CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
+
+ 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];
+
+ printf("Testing LOCKX multiple unlock:\n");
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
+ if (fnum1 == -1) {
+ printf("Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree));
+ ret = false;
+ goto done;
+ }
+
+ /* 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. */
+ printf(" 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. */
+ printf(" 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);
+
+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;
+ struct smb_lock_entry lock2;
+
+ printf("Testing stacking:\n");
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ io.generic.level = RAW_LOCK_LOCKX;
+
+ fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
+ if (fnum1 == -1) {
+ printf("Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree));
+ ret = false;
+ goto done;
+ }
+
+ /* 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;
+
+ /**
+ * Try to take a shared lock, then stack an exclusive.
+ */
+ printf(" 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.
+ */
+ printf(" 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;
+}
+
+/*
+ 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);
+
+ return suite;
+}
diff --git a/source4/torture/raw/lockbench.c b/source4/torture/raw/lockbench.c
new file mode 100644
index 0000000000..d20175a018
--- /dev/null
+++ b/source4/torture/raw/lockbench.c
@@ -0,0 +1,418 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#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/popt_common.h"
+#include "libcli/composite/composite.h"
+#include "libcli/smb_composite/smb_composite.h"
+#include "libcli/resolve/resolve.h"
+#include "param/param.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 = event_add_timed(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 */
+ event_add_timed(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 = lp_gensec_settings(state->mem_ctx, state->tctx->lp_ctx);
+ io->in.socket_options = lp_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 = cmdline_credentials;
+ io->in.fallback_to_anonymous = false;
+ io->in.workgroup = lp_workgroup(state->tctx->lp_ctx);
+ io->in.iconv_convenience = lp_iconv_convenience(state->tctx->lp_ctx);
+ lp_smbcli_options(state->tctx->lp_ctx, &io->in.options);
+ lp_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,
+ lp_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)) {
+ 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 = event_add_timed(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)) {
+ 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 = event_add_timed(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;i<nprocs;i++) {
+ printf("%5u ", (unsigned)(state[i].count - state[i].lastcount));
+ state[i].lastcount = state[i].count;
+ }
+ printf("\r");
+ fflush(stdout);
+ event_add_timed(ev, state, timeval_current_ofs(1, 0), report_rate, state);
+
+ /* send an echo on each interface to ensure it stays alive - this helps
+ with IP takeover */
+ for (i=0;i<nprocs;i++) {
+ struct smb_echo p;
+ struct smbcli_request *req;
+
+ if (!state[i].tree) {
+ continue;
+ }
+
+ p.in.repeat_count = 1;
+ p.in.size = 0;
+ p.in.data = NULL;
+ req = smb_raw_echo_send(state[i].tree->session->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;
+ 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;
+
+ 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;i<nprocs;i++) {
+ state[i].tctx = torture;
+ state[i].mem_ctx = talloc_new(state);
+ state[i].client_num = i;
+ state[i].ev = torture->ev;
+ if (!torture_open_connection_ev(&cli, i, torture, torture->ev)) {
+ return false;
+ }
+ talloc_steal(mem_ctx, state);
+ state[i].tree = cli->tree;
+ state[i].dest_host = talloc_strdup(state[i].mem_ctx,
+ cli->tree->session->transport->socket->hostname);
+ 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",
+ cli->tree->session->transport->socket->port);
+ state[i].dest_ports[1] = NULL;
+ state[i].called_name = talloc_strdup(state[i].mem_ctx,
+ cli->tree->session->transport->called.name);
+ state[i].service_type = talloc_strdup(state[i].mem_ctx,
+ cli->tree->device);
+ }
+
+ num_connected = i;
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ goto failed;
+ }
+
+ for (i=0;i<nprocs;i++) {
+ state[i].fnum = smbcli_open(state[i].tree,
+ FNAME,
+ O_RDWR|O_CREAT, DENY_NONE);
+ if (state[i].fnum == -1) {
+ printf("Failed to open %s on connection %d\n", FNAME, i);
+ goto failed;
+ }
+
+ state[i].stage = LOCK_INITIAL;
+ lock_send(&state[i]);
+ }
+
+ tv = timeval_current();
+
+ if (progress) {
+ event_add_timed(torture->ev, state, timeval_current_ofs(1, 0), report_rate, state);
+ }
+
+ printf("Running for %d seconds\n", timelimit);
+ while (timeval_elapsed(&tv) < timelimit) {
+ event_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;i<nprocs;i++) {
+ printf("[%d] %u ops\n", i, state[i].count);
+ if (state[i].count < minops) minops = state[i].count;
+ }
+ if (minops < 0.5*total/nprocs) {
+ printf("Failed: unbalanced locking\n");
+ goto failed;
+ }
+
+ for (i=0;i<nprocs;i++) {
+ talloc_free(state[i].req);
+ smb_raw_exit(state[i].tree->session);
+ }
+
+ 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/lookuprate.c b/source4/torture/raw/lookuprate.c
new file mode 100644
index 0000000000..e4caf7b1be
--- /dev/null
+++ b/source4/torture/raw/lookuprate.c
@@ -0,0 +1,319 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "torture/smbtorture.h"
+#include "torture/basic/proto.h"
+#include "libcli/libcli.h"
+#include "torture/util.h"
+#include "lib/cmdline/popt_common.h"
+#include "auth/credentials/credentials.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,
+ OPENX_MODE_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-existant 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 of not.
+ */
+ 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 0000000000..0f4104b596
--- /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 0000000000..a81aacdaa4
--- /dev/null
+++ b/source4/torture/raw/mkdir.c
@@ -0,0 +1,174 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/libcli.h"
+#include "torture/util.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;
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ /*
+ 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 0000000000..5b5db3a557
--- /dev/null
+++ b/source4/torture/raw/mux.c
@@ -0,0 +1,361 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#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"
+
+#define BASEDIR "\\test_mux"
+
+#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 the delayed reply to a open that leads to a sharing violation
+*/
+static bool test_mux_open(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;
+
+ printf("testing multiplexed open/open/close\n");
+
+ printf("send first open\n");
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 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);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum1 = io.ntcreatex.out.file.fnum;
+
+ printf("send 2nd open, non-conflicting\n");
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
+ status = smb_raw_open(cli->tree, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum2 = io.ntcreatex.out.file.fnum;
+
+ tv = timeval_current();
+
+ printf("send 3rd open, conflicting\n");
+ io.ntcreatex.in.share_access = 0;
+ status = smb_raw_open(cli->tree, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
+
+ d = timeval_elapsed(&tv);
+ if (d < 0.5 || d > 1.5) {
+ printf("bad timeout for conflict - %.2f should be 1.0\n", d);
+ } else {
+ printf("open delay %.2f\n", d);
+ }
+
+ printf("send async open, conflicting\n");
+ tv = timeval_current();
+ req1 = smb_raw_open_send(cli->tree, &io);
+
+ printf("send 2nd async open, conflicting\n");
+ tv = timeval_current();
+ req2 = smb_raw_open_send(cli->tree, &io);
+
+ printf("close first sync open\n");
+ smbcli_close(cli->tree, fnum1);
+
+ printf("cancel 2nd async open (should be ignored)\n");
+ smb_raw_ntcancel(req2);
+
+ d = timeval_elapsed(&tv);
+ if (d > 0.25) {
+ printf("bad timeout after cancel - %.2f should be <0.25\n", d);
+ ret = false;
+ }
+
+ printf("close the 2nd sync open\n");
+ smbcli_close(cli->tree, fnum2);
+
+ printf("see if the 1st async open now succeeded\n");
+ status = smb_raw_open_recv(req1, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ d = timeval_elapsed(&tv);
+ if (d > 0.25) {
+ printf("bad timeout for async conflict - %.2f should be <0.25\n", d);
+ ret = false;
+ } else {
+ printf("async open delay %.2f\n", d);
+ }
+
+ printf("2nd async open should have timed out\n");
+ status = smb_raw_open_recv(req2, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
+ d = timeval_elapsed(&tv);
+ if (d < 0.8) {
+ printf("bad timeout for async conflict - %.2f should be 1.0\n", d);
+ }
+
+ printf("close the 1st async open\n");
+ smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
+
+done:
+ return ret;
+}
+
+
+/*
+ test a write that hits a byte range lock and send the close after the write
+*/
+static bool test_mux_write(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
+{
+ union smb_write io;
+ NTSTATUS status;
+ int fnum;
+ bool ret = true;
+ struct smbcli_request *req;
+
+ printf("testing multiplexed lock/write/close\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));
+ ret = false;
+ goto done;
+ }
+
+ cli->session->pid = 1;
+
+ /* lock a range */
+ if (NT_STATUS_IS_ERR(smbcli_lock(cli->tree, fnum, 0, 4, 0, WRITE_LOCK))) {
+ printf("lock failed in mux_write - %s\n", smbcli_errstr(cli->tree));
+ ret = false;
+ goto done;
+ }
+
+ 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);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+ smbcli_close(cli->tree, fnum);
+
+done:
+ return ret;
+}
+
+
+/*
+ test a lock that conflicts with an existing lock
+*/
+static bool test_mux_lock(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;
+
+ printf("TESTING MULTIPLEXED LOCK/LOCK/UNLOCK\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));
+ ret = false;
+ goto done;
+ }
+
+ printf("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);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("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);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+ printf("this will too, but we'll unlock while waiting\n");
+ t = timeval_current();
+ req = smb_raw_lock_send(cli->tree, &io);
+
+ printf("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);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("recv the async reply\n");
+ status = smbcli_request_simple_recv(req);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("async lock took %.2f msec\n", timeval_elapsed(&t) * 1000);
+ if (timeval_elapsed(&t) > 0.1) {
+ printf("failed to trigger early lock retry\n");
+ return false;
+ }
+
+ printf("reopening with an exit\n");
+ smb_raw_exit(cli->session);
+ fnum = smbcli_open(cli->tree, BASEDIR "\\write.dat", O_RDWR | O_CREAT, DENY_NONE);
+
+ printf("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);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ lock[0].pid = 2;
+ io.lockx.in.timeout = 1000;
+ status = smb_raw_lock(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+ req = smb_raw_lock_send(cli->tree, &io);
+
+ /* cancel the blocking lock */
+ smb_raw_ntcancel(req);
+
+ printf("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);
+
+ printf("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);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = smbcli_request_simple_recv(req);
+ CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+ printf("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);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ 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);
+
+ printf("recv the async reply\n");
+ status = smbcli_request_simple_recv(req);
+ CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
+ printf("async lock exit took %.2f msec\n", timeval_elapsed(&t) * 1000);
+ if (timeval_elapsed(&t) > 0.1) {
+ printf("failed to trigger early lock failure\n");
+ return false;
+ }
+
+done:
+ return ret;
+}
+
+
+
+/*
+ basic testing of multiplexing notify
+*/
+bool torture_raw_mux(struct torture_context *torture, struct smbcli_state *cli)
+{
+ bool ret = true;
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ ret &= test_mux_open(cli, torture);
+ ret &= test_mux_write(cli, torture);
+ ret &= test_mux_lock(cli, torture);
+
+ smb_raw_exit(cli->session);
+ smbcli_deltree(cli->tree, BASEDIR);
+ return ret;
+}
diff --git a/source4/torture/raw/notify.c b/source4/torture/raw/notify.c
new file mode 100644
index 0000000000..c92170cf61
--- /dev/null
+++ b/source4/torture/raw/notify.c
@@ -0,0 +1,1631 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/libcli.h"
+#include "system/filesys.h"
+#include "torture/util.h"
+
+#define BASEDIR "\\test_notify"
+
+#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_VAL(v, correct) do { \
+ if ((v) != (correct)) { \
+ printf("(%d) wrong value for %s 0x%x should be 0x%x\n", \
+ __LINE__, #v, (int)v, (int)correct); \
+ ret = false; \
+ goto done; \
+ }} while (0)
+
+#define CHECK_WSTR(field, value, flags) do { \
+ if (!field.s || strcmp(field.s, value) || wire_bad_flags(&field, flags, cli->transport)) { \
+ printf("(%d) %s [%s] != %s\n", __LINE__, #field, field.s, value); \
+ ret = false; \
+ goto done; \
+ }} while (0)
+
+
+/*
+ basic testing of change notify on directories
+*/
+static bool test_notify_dir(struct smbcli_state *cli, struct smbcli_state *cli2,
+ TALLOC_CTX *mem_ctx)
+{
+ 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;
+
+ printf("TESTING CHANGE NOTIFY ON DIRECTRIES\n");
+
+ /*
+ get a handle on the directory
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 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;
+
+ status = smb_raw_open(cli->tree, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+
+ status = smb_raw_open(cli->tree, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ 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;
+
+ printf("testing notify cancel\n");
+
+ req = smb_raw_changenotify_send(cli->tree, &notify);
+ smb_raw_ntcancel(req);
+ status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_CANCELLED);
+
+ printf("testing notify mkdir\n");
+
+ req = smb_raw_changenotify_send(cli->tree, &notify);
+ smbcli_mkdir(cli2->tree, BASEDIR "\\subdir-name");
+
+ status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ CHECK_VAL(notify.nttrans.out.num_changes, 1);
+ CHECK_VAL(notify.nttrans.out.changes[0].action, NOTIFY_ACTION_ADDED);
+ CHECK_WSTR(notify.nttrans.out.changes[0].name, "subdir-name", STR_UNICODE);
+
+ printf("testing notify rmdir\n");
+
+ req = smb_raw_changenotify_send(cli->tree, &notify);
+ smbcli_rmdir(cli2->tree, BASEDIR "\\subdir-name");
+
+ status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VAL(notify.nttrans.out.num_changes, 1);
+ CHECK_VAL(notify.nttrans.out.changes[0].action, NOTIFY_ACTION_REMOVED);
+ CHECK_WSTR(notify.nttrans.out.changes[0].name, "subdir-name", STR_UNICODE);
+
+ printf("testing notify mkdir - rmdir - mkdir - rmdir\n");
+
+ smbcli_mkdir(cli2->tree, BASEDIR "\\subdir-name");
+ smbcli_rmdir(cli2->tree, BASEDIR "\\subdir-name");
+ smbcli_mkdir(cli2->tree, BASEDIR "\\subdir-name");
+ smbcli_rmdir(cli2->tree, BASEDIR "\\subdir-name");
+ msleep(200);
+ req = smb_raw_changenotify_send(cli->tree, &notify);
+ status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VAL(notify.nttrans.out.num_changes, 4);
+ CHECK_VAL(notify.nttrans.out.changes[0].action, NOTIFY_ACTION_ADDED);
+ CHECK_WSTR(notify.nttrans.out.changes[0].name, "subdir-name", STR_UNICODE);
+ CHECK_VAL(notify.nttrans.out.changes[1].action, NOTIFY_ACTION_REMOVED);
+ CHECK_WSTR(notify.nttrans.out.changes[1].name, "subdir-name", STR_UNICODE);
+ CHECK_VAL(notify.nttrans.out.changes[2].action, NOTIFY_ACTION_ADDED);
+ CHECK_WSTR(notify.nttrans.out.changes[2].name, "subdir-name", STR_UNICODE);
+ CHECK_VAL(notify.nttrans.out.changes[3].action, NOTIFY_ACTION_REMOVED);
+ CHECK_WSTR(notify.nttrans.out.changes[3].name, "subdir-name", STR_UNICODE);
+
+ count = torture_numops;
+ printf("testing buffered notify on create of %d files\n", count);
+ for (i=0;i<count;i++) {
+ char *fname = talloc_asprintf(cli, BASEDIR "\\test%d.txt", i);
+ int fnum3 = smbcli_open(cli->tree, fname, O_CREAT|O_RDWR, DENY_NONE);
+ if (fnum3 == -1) {
+ printf("Failed to create %s - %s\n",
+ fname, smbcli_errstr(cli->tree));
+ ret = false;
+ goto done;
+ }
+ 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, &notify);
+
+ /* (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, &notify);
+
+ status = smbcli_unlink(cli->tree, BASEDIR "\\nonexistant.txt");
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+
+ /* (1st unlink) as the 2nd notify directly returns,
+ this unlink is only seen by the 1st notify and
+ the 3rd notify (later) */
+ printf("testing notify on unlink for the first file\n");
+ status = smbcli_unlink(cli2->tree, BASEDIR "\\test0.txt");
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ /* receive the reply from the 2nd notify */
+ status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ CHECK_VAL(notify.nttrans.out.num_changes, count);
+ for (i=1;i<count;i++) {
+ CHECK_VAL(notify.nttrans.out.changes[i].action, NOTIFY_ACTION_ADDED);
+ }
+ CHECK_WSTR(notify.nttrans.out.changes[0].name, "test0.txt", STR_UNICODE);
+
+ printf("and now from the 1st notify\n");
+ status = smb_raw_changenotify_recv(req2, mem_ctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VAL(notify.nttrans.out.num_changes, 1);
+ CHECK_VAL(notify.nttrans.out.changes[0].action, NOTIFY_ACTION_REMOVED);
+ CHECK_WSTR(notify.nttrans.out.changes[0].name, "test0.txt", STR_UNICODE);
+
+ printf("(3rd notify) this notify will only see the 1st unlink\n");
+ req = smb_raw_changenotify_send(cli->tree, &notify);
+
+ status = smbcli_unlink(cli->tree, BASEDIR "\\nonexistant.txt");
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+
+ printf("testing notify on wildcard unlink for %d files\n", count-1);
+ /* (2nd unlink) do a wildcard unlink */
+ status = smbcli_unlink(cli2->tree, BASEDIR "\\test*.txt");
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ /* receive the 3rd notify */
+ status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VAL(notify.nttrans.out.num_changes, 1);
+ CHECK_VAL(notify.nttrans.out.changes[0].action, NOTIFY_ACTION_REMOVED);
+ CHECK_WSTR(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, &notify);
+ status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VAL(notify.nttrans.out.num_changes, count-1);
+ for (i=0;i<notify.nttrans.out.num_changes;i++) {
+ CHECK_VAL(notify.nttrans.out.changes[i].action, NOTIFY_ACTION_REMOVED);
+ }
+ notify.nttrans.in.file.fnum = fnum2;
+ req = smb_raw_changenotify_send(cli->tree, &notify);
+ status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VAL(notify.nttrans.out.num_changes, count-1);
+ for (i=0;i<notify.nttrans.out.num_changes;i++) {
+ CHECK_VAL(notify.nttrans.out.changes[i].action, NOTIFY_ACTION_REMOVED);
+ }
+
+ printf("testing if a close() on the dir handle triggers the notify reply\n");
+
+ notify.nttrans.in.file.fnum = fnum;
+ req = smb_raw_changenotify_send(cli->tree, &notify);
+
+ 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_OK);
+
+ status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VAL(notify.nttrans.out.num_changes, 0);
+
+done:
+ smb_raw_exit(cli->session);
+ 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 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) {
+ if ((actions[i].name.s == NULL)
+ || (strcmp(actions[i].name.s, name) != 0)
+ || (wire_bad_flags(&actions[i].name, STR_UNICODE,
+ cli->transport))) {
+ printf("(%d) name [%s] != %s\n", line,
+ actions[i].name.s, name);
+ return false;
+ }
+ return true;
+ }
+ }
+
+ printf("(%d) expected action %d, not found\n", line, action);
+ return false;
+}
+
+/*
+ testing of recursive change notify
+*/
+static bool test_notify_recursive(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
+{
+ bool ret = true;
+ NTSTATUS status;
+ union smb_notify notify;
+ union smb_open io;
+ int fnum;
+ struct smbcli_request *req1, *req2;
+
+ printf("TESTING CHANGE NOTIFY WITH RECURSION\n");
+
+ /*
+ get a handle on the directory
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 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;
+
+ status = smb_raw_open(cli->tree, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ 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, &notify);
+
+ notify.nttrans.in.recursive = false;
+ req2 = smb_raw_changenotify_send(cli->tree, &notify);
+
+ /* cancel initial requests so the buffer is setup */
+ smb_raw_ntcancel(req1);
+ status = smb_raw_changenotify_recv(req1, mem_ctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_CANCELLED);
+
+ smb_raw_ntcancel(req2);
+ status = smb_raw_changenotify_recv(req2, mem_ctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_CANCELLED);
+
+ smbcli_mkdir(cli->tree, BASEDIR "\\subdir-name");
+ smbcli_mkdir(cli->tree, BASEDIR "\\subdir-name\\subname1");
+ smbcli_close(cli->tree,
+ smbcli_open(cli->tree, BASEDIR "\\subdir-name\\subname2", O_CREAT, 0));
+ smbcli_rename(cli->tree, BASEDIR "\\subdir-name\\subname1", BASEDIR "\\subdir-name\\subname1-r");
+ smbcli_rename(cli->tree, BASEDIR "\\subdir-name\\subname2", BASEDIR "\\subname2-r");
+ smbcli_rename(cli->tree, BASEDIR "\\subname2-r", BASEDIR "\\subname3-r");
+
+ notify.nttrans.in.completion_filter = 0;
+ notify.nttrans.in.recursive = true;
+ msleep(200);
+ req1 = smb_raw_changenotify_send(cli->tree, &notify);
+
+ smbcli_rmdir(cli->tree, BASEDIR "\\subdir-name\\subname1-r");
+ smbcli_rmdir(cli->tree, BASEDIR "\\subdir-name");
+ smbcli_unlink(cli->tree, BASEDIR "\\subname3-r");
+
+ notify.nttrans.in.recursive = false;
+ req2 = smb_raw_changenotify_send(cli->tree, &notify);
+
+ status = smb_raw_changenotify_recv(req1, mem_ctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ CHECK_VAL(notify.nttrans.out.num_changes, 11);
+ CHECK_VAL(notify.nttrans.out.changes[0].action, NOTIFY_ACTION_ADDED);
+ CHECK_WSTR(notify.nttrans.out.changes[0].name, "subdir-name", STR_UNICODE);
+ CHECK_VAL(notify.nttrans.out.changes[1].action, NOTIFY_ACTION_ADDED);
+ CHECK_WSTR(notify.nttrans.out.changes[1].name, "subdir-name\\subname1", STR_UNICODE);
+ CHECK_VAL(notify.nttrans.out.changes[2].action, NOTIFY_ACTION_ADDED);
+ CHECK_WSTR(notify.nttrans.out.changes[2].name, "subdir-name\\subname2", STR_UNICODE);
+ CHECK_VAL(notify.nttrans.out.changes[3].action, NOTIFY_ACTION_OLD_NAME);
+ CHECK_WSTR(notify.nttrans.out.changes[3].name, "subdir-name\\subname1", STR_UNICODE);
+ CHECK_VAL(notify.nttrans.out.changes[4].action, NOTIFY_ACTION_NEW_NAME);
+ CHECK_WSTR(notify.nttrans.out.changes[4].name, "subdir-name\\subname1-r", STR_UNICODE);
+
+ ret &= check_rename_reply(
+ cli, __LINE__, &notify.nttrans.out.changes[5],
+ NOTIFY_ACTION_ADDED, "subname2-r");
+ ret &= check_rename_reply(
+ cli, __LINE__, &notify.nttrans.out.changes[5],
+ NOTIFY_ACTION_REMOVED, "subdir-name\\subname2");
+ ret &= check_rename_reply(
+ cli, __LINE__, &notify.nttrans.out.changes[5],
+ NOTIFY_ACTION_MODIFIED, "subname2-r");
+
+ ret &= check_rename_reply(
+ cli, __LINE__, &notify.nttrans.out.changes[8],
+ NOTIFY_ACTION_OLD_NAME, "subname2-r");
+ ret &= check_rename_reply(
+ cli, __LINE__, &notify.nttrans.out.changes[8],
+ NOTIFY_ACTION_NEW_NAME, "subname3-r");
+ ret &= check_rename_reply(
+ cli, __LINE__, &notify.nttrans.out.changes[8],
+ NOTIFY_ACTION_MODIFIED, "subname3-r");
+
+ if (!ret) {
+ goto done;
+ }
+
+ status = smb_raw_changenotify_recv(req2, mem_ctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ CHECK_VAL(notify.nttrans.out.num_changes, 3);
+ CHECK_VAL(notify.nttrans.out.changes[0].action, NOTIFY_ACTION_REMOVED);
+ CHECK_WSTR(notify.nttrans.out.changes[0].name, "subdir-name\\subname1-r", STR_UNICODE);
+ CHECK_VAL(notify.nttrans.out.changes[1].action, NOTIFY_ACTION_REMOVED);
+ CHECK_WSTR(notify.nttrans.out.changes[1].name, "subdir-name", STR_UNICODE);
+ CHECK_VAL(notify.nttrans.out.changes[2].action, NOTIFY_ACTION_REMOVED);
+ CHECK_WSTR(notify.nttrans.out.changes[2].name, "subname3-r", STR_UNICODE);
+
+done:
+ smb_raw_exit(cli->session);
+ return ret;
+}
+
+/*
+ testing of change notify mask change
+*/
+static bool test_notify_mask_change(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
+{
+ bool ret = true;
+ NTSTATUS status;
+ union smb_notify notify;
+ union smb_open io;
+ int fnum;
+ struct smbcli_request *req1, *req2;
+
+ printf("TESTING CHANGE NOTIFY WITH MASK CHANGE\n");
+
+ /*
+ get a handle on the directory
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 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;
+
+ status = smb_raw_open(cli->tree, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ 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, &notify);
+
+ notify.nttrans.in.recursive = false;
+ req2 = smb_raw_changenotify_send(cli->tree, &notify);
+
+ /* cancel initial requests so the buffer is setup */
+ smb_raw_ntcancel(req1);
+ status = smb_raw_changenotify_recv(req1, mem_ctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_CANCELLED);
+
+ smb_raw_ntcancel(req2);
+ status = smb_raw_changenotify_recv(req2, mem_ctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_CANCELLED);
+
+ notify.nttrans.in.recursive = true;
+ req1 = smb_raw_changenotify_send(cli->tree, &notify);
+
+ /* Set to hidden then back again. */
+ smbcli_close(cli->tree, smbcli_open(cli->tree, BASEDIR "\\tname1", O_CREAT, 0));
+ smbcli_setatr(cli->tree, BASEDIR "\\tname1", FILE_ATTRIBUTE_HIDDEN, 0);
+ smbcli_unlink(cli->tree, BASEDIR "\\tname1");
+
+ status = smb_raw_changenotify_recv(req1, mem_ctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ CHECK_VAL(notify.nttrans.out.num_changes, 1);
+ CHECK_VAL(notify.nttrans.out.changes[0].action, NOTIFY_ACTION_MODIFIED);
+ CHECK_WSTR(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, &notify);
+
+ notify.nttrans.in.recursive = false;
+ req2 = smb_raw_changenotify_send(cli->tree, &notify);
+
+ smbcli_mkdir(cli->tree, BASEDIR "\\subdir-name");
+ smbcli_mkdir(cli->tree, BASEDIR "\\subdir-name\\subname1");
+ smbcli_close(cli->tree,
+ smbcli_open(cli->tree, BASEDIR "\\subdir-name\\subname2", O_CREAT, 0));
+ smbcli_rename(cli->tree, BASEDIR "\\subdir-name\\subname1", BASEDIR "\\subdir-name\\subname1-r");
+ smbcli_rename(cli->tree, BASEDIR "\\subdir-name\\subname2", BASEDIR "\\subname2-r");
+ smbcli_rename(cli->tree, BASEDIR "\\subname2-r", BASEDIR "\\subname3-r");
+
+ smbcli_rmdir(cli->tree, BASEDIR "\\subdir-name\\subname1-r");
+ smbcli_rmdir(cli->tree, BASEDIR "\\subdir-name");
+ smbcli_unlink(cli->tree, BASEDIR "\\subname3-r");
+
+ status = smb_raw_changenotify_recv(req1, mem_ctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ CHECK_VAL(notify.nttrans.out.num_changes, 1);
+ CHECK_VAL(notify.nttrans.out.changes[0].action, NOTIFY_ACTION_MODIFIED);
+ CHECK_WSTR(notify.nttrans.out.changes[0].name, "subname2-r", STR_UNICODE);
+
+ status = smb_raw_changenotify_recv(req2, mem_ctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ CHECK_VAL(notify.nttrans.out.num_changes, 1);
+ CHECK_VAL(notify.nttrans.out.changes[0].action, NOTIFY_ACTION_MODIFIED);
+ CHECK_WSTR(notify.nttrans.out.changes[0].name, "subname3-r", STR_UNICODE);
+
+ if (!ret) {
+ goto done;
+ }
+
+done:
+ smb_raw_exit(cli->session);
+ return ret;
+}
+
+
+/*
+ testing of mask bits for change notify
+*/
+static bool test_notify_mask(struct smbcli_state *cli, struct torture_context *tctx)
+{
+ bool ret = true;
+ NTSTATUS status;
+ union smb_notify notify;
+ union smb_open io;
+ int fnum, fnum2;
+ uint32_t mask;
+ int i;
+ char c = 1;
+ struct timeval tv;
+ NTTIME t;
+
+ printf("TESTING CHANGE NOTIFY COMPLETION FILTERS\n");
+
+ 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 = 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;
+
+ notify.nttrans.level = RAW_NOTIFY_NTTRANS;
+ notify.nttrans.in.buffer_size = 1000;
+ notify.nttrans.in.recursive = true;
+
+#define NOTIFY_MASK_TEST(test_name, setup, op, cleanup, Action, expected, nchanges) \
+ do { \
+ smbcli_getatr(cli->tree, test_name, NULL, NULL, NULL); \
+ do { for (mask=i=0;i<32;i++) { \
+ struct smbcli_request *req; \
+ status = smb_raw_open(cli->tree, tctx, &io); \
+ CHECK_STATUS(status, NT_STATUS_OK); \
+ fnum = io.ntcreatex.out.file.fnum; \
+ setup \
+ notify.nttrans.in.file.fnum = fnum; \
+ notify.nttrans.in.completion_filter = (1<<i); \
+ req = smb_raw_changenotify_send(cli->tree, &notify); \
+ op \
+ msleep(200); smb_raw_ntcancel(req); \
+ status = smb_raw_changenotify_recv(req, tctx, &notify); \
+ cleanup \
+ smbcli_close(cli->tree, fnum); \
+ if (NT_STATUS_EQUAL(status, NT_STATUS_CANCELLED)) continue; \
+ CHECK_STATUS(status, NT_STATUS_OK); \
+ /* 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) { \
+ printf("(rename file special handling OK)\n"); \
+ } else if (nchanges != notify.nttrans.out.num_changes) { \
+ printf("ERROR: 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); \
+ ret = false; \
+ } else if (notify.nttrans.out.changes[0].action != Action) { \
+ printf("ERROR: 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); \
+ ret = false; \
+ } else if (strcmp(notify.nttrans.out.changes[0].name.s, "tname1") != 0) { \
+ printf("ERROR: nchanges=%d action=%d filter=0x%08x name=%s\n", \
+ notify.nttrans.out.num_changes, \
+ notify.nttrans.out.changes[0].action, \
+ notify.nttrans.in.completion_filter, \
+ notify.nttrans.out.changes[0].name.s); \
+ ret = false; \
+ } \
+ mask |= (1<<i); \
+ } \
+ if ((expected) != mask) { \
+ if (((expected) & ~mask) != 0) { \
+ printf("ERROR: trigger on too few bits. mask=0x%08x expected=0x%08x\n", \
+ mask, expected); \
+ ret = false; \
+ } else { \
+ printf("WARNING: trigger on too many bits. mask=0x%08x expected=0x%08x\n", \
+ mask, expected); \
+ } \
+ } \
+ } while (0); \
+ } while (0);
+
+ printf("testing mkdir\n");
+ NOTIFY_MASK_TEST("testing mkdir",;,
+ smbcli_mkdir(cli->tree, BASEDIR "\\tname1");,
+ smbcli_rmdir(cli->tree, BASEDIR "\\tname1");,
+ NOTIFY_ACTION_ADDED,
+ FILE_NOTIFY_CHANGE_DIR_NAME, 1);
+
+ printf("testing create file\n");
+ NOTIFY_MASK_TEST("testing create file",;,
+ smbcli_close(cli->tree, smbcli_open(cli->tree, BASEDIR "\\tname1", O_CREAT, 0));,
+ smbcli_unlink(cli->tree, BASEDIR "\\tname1");,
+ NOTIFY_ACTION_ADDED,
+ FILE_NOTIFY_CHANGE_FILE_NAME, 1);
+
+ printf("testing unlink\n");
+ NOTIFY_MASK_TEST("testing unlink",
+ smbcli_close(cli->tree, smbcli_open(cli->tree, BASEDIR "\\tname1", O_CREAT, 0));,
+ smbcli_unlink(cli->tree, BASEDIR "\\tname1");,
+ ;,
+ NOTIFY_ACTION_REMOVED,
+ FILE_NOTIFY_CHANGE_FILE_NAME, 1);
+
+ printf("testing rmdir\n");
+ NOTIFY_MASK_TEST("testing rmdir",
+ smbcli_mkdir(cli->tree, BASEDIR "\\tname1");,
+ smbcli_rmdir(cli->tree, BASEDIR "\\tname1");,
+ ;,
+ NOTIFY_ACTION_REMOVED,
+ FILE_NOTIFY_CHANGE_DIR_NAME, 1);
+
+ printf("testing rename file\n");
+ NOTIFY_MASK_TEST("testing rename file",
+ smbcli_close(cli->tree, smbcli_open(cli->tree, BASEDIR "\\tname1", O_CREAT, 0));,
+ smbcli_rename(cli->tree, BASEDIR "\\tname1", BASEDIR "\\tname2");,
+ smbcli_unlink(cli->tree, BASEDIR "\\tname2");,
+ NOTIFY_ACTION_OLD_NAME,
+ FILE_NOTIFY_CHANGE_FILE_NAME|FILE_NOTIFY_CHANGE_ATTRIBUTES|FILE_NOTIFY_CHANGE_CREATION, 2);
+
+ printf("testing rename dir\n");
+ NOTIFY_MASK_TEST("testing rename dir",
+ smbcli_mkdir(cli->tree, BASEDIR "\\tname1");,
+ smbcli_rename(cli->tree, BASEDIR "\\tname1", BASEDIR "\\tname2");,
+ smbcli_rmdir(cli->tree, BASEDIR "\\tname2");,
+ NOTIFY_ACTION_OLD_NAME,
+ FILE_NOTIFY_CHANGE_DIR_NAME, 2);
+
+ printf("testing set path attribute\n");
+ NOTIFY_MASK_TEST("testing set path attribute",
+ smbcli_close(cli->tree, smbcli_open(cli->tree, BASEDIR "\\tname1", O_CREAT, 0));,
+ smbcli_setatr(cli->tree, BASEDIR "\\tname1", FILE_ATTRIBUTE_HIDDEN, 0);,
+ smbcli_unlink(cli->tree, BASEDIR "\\tname1");,
+ NOTIFY_ACTION_MODIFIED,
+ FILE_NOTIFY_CHANGE_ATTRIBUTES, 1);
+
+ printf("testing set path write time\n");
+ NOTIFY_MASK_TEST("testing set path write time",
+ smbcli_close(cli->tree, smbcli_open(cli->tree, BASEDIR "\\tname1", O_CREAT, 0));,
+ smbcli_setatr(cli->tree, BASEDIR "\\tname1", FILE_ATTRIBUTE_NORMAL, 1000);,
+ smbcli_unlink(cli->tree, BASEDIR "\\tname1");,
+ NOTIFY_ACTION_MODIFIED,
+ FILE_NOTIFY_CHANGE_LAST_WRITE, 1);
+
+ printf("testing set file attribute\n");
+ NOTIFY_MASK_TEST("testing set file attribute",
+ fnum2 = create_complex_file(cli, tctx, BASEDIR "\\tname1");,
+ smbcli_fsetatr(cli->tree, fnum2, FILE_ATTRIBUTE_HIDDEN, 0, 0, 0, 0);,
+ (smbcli_close(cli->tree, fnum2), smbcli_unlink(cli->tree, BASEDIR "\\tname1"));,
+ NOTIFY_ACTION_MODIFIED,
+ FILE_NOTIFY_CHANGE_ATTRIBUTES, 1);
+
+ if (torture_setting_bool(tctx, "samba3", false)) {
+ printf("Samba3 does not yet support create times "
+ "everywhere\n");
+ }
+ else {
+ printf("testing set file create time\n");
+ NOTIFY_MASK_TEST("testing set file create time",
+ fnum2 = create_complex_file(cli, tctx,
+ BASEDIR "\\tname1");,
+ smbcli_fsetatr(cli->tree, fnum2, 0, t, 0, 0, 0);,
+ (smbcli_close(cli->tree, fnum2),
+ smbcli_unlink(cli->tree, BASEDIR "\\tname1"));,
+ NOTIFY_ACTION_MODIFIED,
+ FILE_NOTIFY_CHANGE_CREATION, 1);
+ }
+
+ printf("testing set file access time\n");
+ NOTIFY_MASK_TEST("testing set file access time",
+ fnum2 = create_complex_file(cli, tctx, BASEDIR "\\tname1");,
+ smbcli_fsetatr(cli->tree, fnum2, 0, 0, t, 0, 0);,
+ (smbcli_close(cli->tree, fnum2), smbcli_unlink(cli->tree, BASEDIR "\\tname1"));,
+ NOTIFY_ACTION_MODIFIED,
+ FILE_NOTIFY_CHANGE_LAST_ACCESS, 1);
+
+ printf("testing set file write time\n");
+ NOTIFY_MASK_TEST("testing set file write time",
+ fnum2 = create_complex_file(cli, tctx, BASEDIR "\\tname1");,
+ smbcli_fsetatr(cli->tree, fnum2, 0, 0, 0, t, 0);,
+ (smbcli_close(cli->tree, fnum2), smbcli_unlink(cli->tree, BASEDIR "\\tname1"));,
+ NOTIFY_ACTION_MODIFIED,
+ FILE_NOTIFY_CHANGE_LAST_WRITE, 1);
+
+ printf("testing set file change time\n");
+ NOTIFY_MASK_TEST("testing set file change time",
+ fnum2 = create_complex_file(cli, tctx, BASEDIR "\\tname1");,
+ smbcli_fsetatr(cli->tree, fnum2, 0, 0, 0, 0, t);,
+ (smbcli_close(cli->tree, fnum2), smbcli_unlink(cli->tree, BASEDIR "\\tname1"));,
+ NOTIFY_ACTION_MODIFIED,
+ 0, 1);
+
+
+ printf("testing write\n");
+ NOTIFY_MASK_TEST("testing write",
+ fnum2 = create_complex_file(cli, tctx, BASEDIR "\\tname1");,
+ smbcli_write(cli->tree, fnum2, 1, &c, 10000, 1);,
+ (smbcli_close(cli->tree, fnum2), smbcli_unlink(cli->tree, BASEDIR "\\tname1"));,
+ NOTIFY_ACTION_MODIFIED,
+ 0, 1);
+
+ printf("testing truncate\n");
+ NOTIFY_MASK_TEST("testing truncate",
+ fnum2 = create_complex_file(cli, tctx, BASEDIR "\\tname1");,
+ smbcli_ftruncate(cli->tree, fnum2, 10000);,
+ (smbcli_close(cli->tree, fnum2), smbcli_unlink(cli->tree, BASEDIR "\\tname1"));,
+ NOTIFY_ACTION_MODIFIED,
+ FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_ATTRIBUTES, 1);
+
+done:
+ smb_raw_exit(cli->session);
+ return ret;
+}
+
+/*
+ basic testing of change notify on files
+*/
+static bool test_notify_file(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
+{
+ 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 "\\file.txt";
+
+ printf("TESTING CHANGE NOTIFY ON FILES\n");
+
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 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;
+
+ /* 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;
+
+ printf("testing if notifies on file handles are invalid (should be)\n");
+
+ req = smb_raw_changenotify_send(cli->tree, &notify);
+ status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
+
+ 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_OK);
+
+ status = smbcli_unlink(cli->tree, fname);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+done:
+ smb_raw_exit(cli->session);
+ return ret;
+}
+
+/*
+ basic testing of change notifies followed by a tdis
+*/
+static bool test_notify_tdis(struct torture_context *tctx)
+{
+ bool ret = true;
+ NTSTATUS status;
+ union smb_notify notify;
+ union smb_open io;
+ int fnum;
+ struct smbcli_request *req;
+ struct smbcli_state *cli = NULL;
+
+ printf("TESTING CHANGE NOTIFY FOLLOWED BY TDIS\n");
+
+ if (!torture_open_connection(&cli, tctx, 0)) {
+ return false;
+ }
+
+ /*
+ get a handle on the directory
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 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;
+
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ 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, &notify);
+
+ status = smbcli_tdis(cli);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ cli->tree = NULL;
+
+ status = smb_raw_changenotify_recv(req, tctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VAL(notify.nttrans.out.num_changes, 0);
+
+done:
+ torture_close_connection(cli);
+ return ret;
+}
+
+/*
+ basic testing of change notifies followed by a exit
+*/
+static bool test_notify_exit(struct torture_context *tctx)
+{
+ bool ret = true;
+ NTSTATUS status;
+ union smb_notify notify;
+ union smb_open io;
+ int fnum;
+ struct smbcli_request *req;
+ struct smbcli_state *cli = NULL;
+
+ printf("TESTING CHANGE NOTIFY FOLLOWED BY EXIT\n");
+
+ if (!torture_open_connection(&cli, tctx, 0)) {
+ return false;
+ }
+
+ /*
+ get a handle on the directory
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 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;
+
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ 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, &notify);
+
+ status = smb_raw_exit(cli->session);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = smb_raw_changenotify_recv(req, tctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VAL(notify.nttrans.out.num_changes, 0);
+
+done:
+ torture_close_connection(cli);
+ return ret;
+}
+
+/*
+ basic testing of change notifies followed by a ulogoff
+*/
+static bool test_notify_ulogoff(struct torture_context *tctx)
+{
+ bool ret = true;
+ NTSTATUS status;
+ union smb_notify notify;
+ union smb_open io;
+ int fnum;
+ struct smbcli_request *req;
+ struct smbcli_state *cli = NULL;
+
+ printf("TESTING CHANGE NOTIFY FOLLOWED BY ULOGOFF\n");
+
+ if (!torture_open_connection(&cli, tctx, 0)) {
+ return false;
+ }
+
+ /*
+ get a handle on the directory
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 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;
+
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ 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, &notify);
+
+ status = smb_raw_ulogoff(cli->session);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = smb_raw_changenotify_recv(req, tctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VAL(notify.nttrans.out.num_changes, 0);
+
+done:
+ torture_close_connection(cli);
+ 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
+*/
+static bool test_notify_tcp_dis(struct torture_context *tctx)
+{
+ bool ret = true;
+ NTSTATUS status;
+ union smb_notify notify;
+ union smb_open io;
+ int fnum;
+ struct smbcli_request *req;
+ struct smbcli_state *cli = NULL;
+
+ printf("TESTING CHANGE NOTIFY FOLLOWED BY TCP DISCONNECT\n");
+
+ if (!torture_open_connection(&cli, tctx, 0)) {
+ return false;
+ }
+
+ /*
+ get a handle on the directory
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 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;
+
+ status = smb_raw_open(cli->tree, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ 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, &notify);
+
+ smbcli_transport_idle_handler(cli->transport, tcp_dis_handler, 250, cli);
+
+ status = smb_raw_changenotify_recv(req, tctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_LOCAL_DISCONNECT);
+
+done:
+ torture_close_connection(cli);
+ return ret;
+}
+
+/*
+ test setting up two change notify requests on one handle
+*/
+static bool test_notify_double(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
+{
+ bool ret = true;
+ NTSTATUS status;
+ union smb_notify notify;
+ union smb_open io;
+ int fnum;
+ struct smbcli_request *req1, *req2;
+
+ printf("TESTING CHANGE NOTIFY TWICE ON ONE DIRECTORY\n");
+
+ /*
+ get a handle on the directory
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 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;
+
+ status = smb_raw_open(cli->tree, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ 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, &notify);
+ req2 = smb_raw_changenotify_send(cli->tree, &notify);
+
+ smbcli_mkdir(cli->tree, BASEDIR "\\subdir-name");
+
+ status = smb_raw_changenotify_recv(req1, mem_ctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VAL(notify.nttrans.out.num_changes, 1);
+ CHECK_WSTR(notify.nttrans.out.changes[0].name, "subdir-name", STR_UNICODE);
+
+ smbcli_mkdir(cli->tree, BASEDIR "\\subdir-name2");
+
+ status = smb_raw_changenotify_recv(req2, mem_ctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VAL(notify.nttrans.out.num_changes, 1);
+ CHECK_WSTR(notify.nttrans.out.changes[0].name, "subdir-name2", STR_UNICODE);
+
+done:
+ smb_raw_exit(cli->session);
+ return ret;
+}
+
+
+/*
+ test multiple change notifies at different depths and with/without recursion
+*/
+static bool test_notify_tree(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
+{
+ 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[] = {
+ {BASEDIR "\\abc", true, FILE_NOTIFY_CHANGE_NAME, 30 },
+ {BASEDIR "\\zqy", true, FILE_NOTIFY_CHANGE_NAME, 8 },
+ {BASEDIR "\\atsy", true, FILE_NOTIFY_CHANGE_NAME, 4 },
+ {BASEDIR "\\abc\\foo", true, FILE_NOTIFY_CHANGE_NAME, 2 },
+ {BASEDIR "\\abc\\blah", true, FILE_NOTIFY_CHANGE_NAME, 13 },
+ {BASEDIR "\\abc\\blah", false, FILE_NOTIFY_CHANGE_NAME, 7 },
+ {BASEDIR "\\abc\\blah\\a", true, FILE_NOTIFY_CHANGE_NAME, 2 },
+ {BASEDIR "\\abc\\blah\\b", true, FILE_NOTIFY_CHANGE_NAME, 2 },
+ {BASEDIR "\\abc\\blah\\c", true, FILE_NOTIFY_CHANGE_NAME, 2 },
+ {BASEDIR "\\abc\\fooblah", true, FILE_NOTIFY_CHANGE_NAME, 2 },
+ {BASEDIR "\\zqy\\xx", true, FILE_NOTIFY_CHANGE_NAME, 2 },
+ {BASEDIR "\\zqy\\yyy", true, FILE_NOTIFY_CHANGE_NAME, 2 },
+ {BASEDIR "\\zqy\\..", true, FILE_NOTIFY_CHANGE_NAME, 40 },
+ {BASEDIR, true, FILE_NOTIFY_CHANGE_NAME, 40 },
+ {BASEDIR, false,FILE_NOTIFY_CHANGE_NAME, 6 },
+ {BASEDIR "\\atsy", false,FILE_NOTIFY_CHANGE_NAME, 4 },
+ {BASEDIR "\\abc", true, FILE_NOTIFY_CHANGE_NAME, 24 },
+ {BASEDIR "\\abc", false,FILE_NOTIFY_CHANGE_FILE_NAME, 0 },
+ {BASEDIR "\\abc", true, FILE_NOTIFY_CHANGE_FILE_NAME, 0 },
+ {BASEDIR "\\abc", true, FILE_NOTIFY_CHANGE_NAME, 24 },
+ };
+ int i;
+ NTSTATUS status;
+ bool all_done = false;
+
+ printf("TESTING CHANGE NOTIFY FOR DIFFERENT DEPTHS\n");
+
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 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;i<ARRAY_SIZE(dirs);i++) {
+ io.ntcreatex.in.fname = dirs[i].path;
+ status = smb_raw_open(cli->tree, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ 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, &notify);
+ smb_raw_ntcancel(req);
+ status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_CANCELLED);
+ }
+
+ /* trigger 2 events in each dir */
+ for (i=0;i<ARRAY_SIZE(dirs);i++) {
+ char *path = talloc_asprintf(mem_ctx, "%s\\test.dir", dirs[i].path);
+ smbcli_mkdir(cli->tree, path);
+ smbcli_rmdir(cli->tree, path);
+ talloc_free(path);
+ }
+
+ /* give a bit of time for the events to propogate */
+ tv = timeval_current();
+
+ do {
+ /* count events that have happened in each dir */
+ for (i=0;i<ARRAY_SIZE(dirs);i++) {
+ notify.nttrans.in.file.fnum = dirs[i].fnum;
+ req = smb_raw_changenotify_send(cli->tree, &notify);
+ smb_raw_ntcancel(req);
+ notify.nttrans.out.num_changes = 0;
+ status = smb_raw_changenotify_recv(req, mem_ctx, &notify);
+ dirs[i].counted += notify.nttrans.out.num_changes;
+ }
+
+ all_done = true;
+
+ for (i=0;i<ARRAY_SIZE(dirs);i++) {
+ if (dirs[i].counted != dirs[i].expected) {
+ all_done = false;
+ }
+ }
+ } while (!all_done && timeval_elapsed(&tv) < 20);
+
+ printf("took %.4f seconds to propogate all events\n", timeval_elapsed(&tv));
+
+ for (i=0;i<ARRAY_SIZE(dirs);i++) {
+ if (dirs[i].counted != dirs[i].expected) {
+ printf("ERROR: i=%d expected %d got %d for '%s'\n",
+ i, dirs[i].expected, dirs[i].counted, dirs[i].path);
+ ret = false;
+ }
+ }
+
+ /*
+ run from the back, closing and deleting
+ */
+ for (i=ARRAY_SIZE(dirs)-1;i>=0;i--) {
+ smbcli_close(cli->tree, dirs[i].fnum);
+ smbcli_rmdir(cli->tree, dirs[i].path);
+ }
+
+done:
+ smb_raw_exit(cli->session);
+ return ret;
+}
+
+/*
+ Test response when cached server events exceed single NT NOTFIY response
+ packet size.
+*/
+static bool test_notify_overflow(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
+{
+ bool ret = true;
+ NTSTATUS status;
+ union smb_notify notify;
+ union smb_open io;
+ int fnum, fnum2;
+ int count = 100;
+ struct smbcli_request *req1;
+ int i;
+
+ printf("TESTING CHANGE NOTIFY EVENT OVERFLOW\n");
+
+ /* get a handle on the directory */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 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;
+
+ status = smb_raw_open(cli->tree, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ 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, &notify);
+
+ /* cancel initial requests so the buffer is setup */
+ smb_raw_ntcancel(req1);
+ status = smb_raw_changenotify_recv(req1, mem_ctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_CANCELLED);
+
+ /* open a lot of files, filling up the server side notify buffer */
+ printf("testing overflowed buffer notify on create of %d files\n",
+ count);
+ for (i=0;i<count;i++) {
+ char *fname = talloc_asprintf(cli, BASEDIR "\\test%d.txt", i);
+ int fnum2 = smbcli_open(cli->tree, fname, O_CREAT|O_RDWR,
+ DENY_NONE);
+ if (fnum2 == -1) {
+ printf("Failed to create %s - %s\n",
+ fname, smbcli_errstr(cli->tree));
+ ret = false;
+ goto done;
+ }
+ 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, &notify);
+ status = smb_raw_changenotify_recv(req1, mem_ctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VAL(notify.nttrans.out.num_changes, 0);
+
+done:
+ smb_raw_exit(cli->session);
+ return ret;
+}
+
+/*
+ Test if notifications are returned for changes to the base directory.
+ They shouldn't be.
+*/
+static bool test_notify_basedir(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
+{
+ bool ret = true;
+ NTSTATUS status;
+ union smb_notify notify;
+ union smb_open io;
+ int fnum, fnum2;
+ int count = 100;
+ struct smbcli_request *req1;
+ int i;
+
+ printf("TESTING CHANGE NOTIFY BASEDIR EVENTS\n");
+
+ /* get a handle on the directory */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 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;
+
+ status = smb_raw_open(cli->tree, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+
+ /* create a test file that will also be modified */
+ smbcli_close(cli->tree, smbcli_open(cli->tree, BASEDIR "\\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, &notify);
+
+ /* set attribute on the base dir */
+ smbcli_setatr(cli->tree, BASEDIR, FILE_ATTRIBUTE_HIDDEN, 0);
+
+ /* set attribute on a file to assure we receive a notification */
+ smbcli_setatr(cli->tree, BASEDIR "\\tname1", FILE_ATTRIBUTE_HIDDEN, 0);
+ msleep(200);
+
+ /* check how many responses were given, expect only 1 for the file */
+ status = smb_raw_changenotify_recv(req1, mem_ctx, &notify);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VAL(notify.nttrans.out.num_changes, 1);
+ CHECK_VAL(notify.nttrans.out.changes[0].action, NOTIFY_ACTION_MODIFIED);
+ CHECK_WSTR(notify.nttrans.out.changes[0].name, "tname1", STR_UNICODE);
+
+done:
+ smb_raw_exit(cli->session);
+ 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);
+
+ printf("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 = 0;
+ 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);
+ printf("Failed to create secondary tree\n");
+ return NULL;
+ }
+
+ tree->tid = tcon.tconx.out.tid;
+ printf("tid1=%d tid2=%d\n", cli->tree->tid, tree->tid);
+
+ return tree;
+}
+
+
+/*
+ very simple change notify test
+*/
+static bool test_notify_tcon(struct smbcli_state *cli, struct torture_context *torture)
+{
+ bool ret = true;
+ NTSTATUS status;
+ union smb_notify notify;
+ union smb_open io;
+ int fnum, fnum2;
+ struct smbcli_request *req;
+ extern int torture_numops;
+ struct smbcli_tree *tree = NULL;
+
+ printf("TESTING SIMPLE CHANGE NOTIFY\n");
+
+ /*
+ get a handle on the directory
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 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;
+
+ status = smb_raw_open(cli->tree, torture, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+
+ status = smb_raw_open(cli->tree, torture, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ 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;
+
+ printf("testing notify mkdir\n");
+ req = smb_raw_changenotify_send(cli->tree, &notify);
+ smbcli_mkdir(cli->tree, BASEDIR "\\subdir-name");
+
+ status = smb_raw_changenotify_recv(req, torture, &notify);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ CHECK_VAL(notify.nttrans.out.num_changes, 1);
+ CHECK_VAL(notify.nttrans.out.changes[0].action, NOTIFY_ACTION_ADDED);
+ CHECK_WSTR(notify.nttrans.out.changes[0].name, "subdir-name", STR_UNICODE);
+
+ printf("testing notify rmdir\n");
+ req = smb_raw_changenotify_send(cli->tree, &notify);
+ smbcli_rmdir(cli->tree, BASEDIR "\\subdir-name");
+
+ status = smb_raw_changenotify_recv(req, torture, &notify);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VAL(notify.nttrans.out.num_changes, 1);
+ CHECK_VAL(notify.nttrans.out.changes[0].action, NOTIFY_ACTION_REMOVED);
+ CHECK_WSTR(notify.nttrans.out.changes[0].name, "subdir-name", STR_UNICODE);
+
+ printf("SIMPLE CHANGE NOTIFY OK\n");
+
+ printf("TESTING WITH SECONDARY TCON\n");
+ tree = secondary_tcon(cli, torture);
+
+ printf("testing notify mkdir\n");
+ req = smb_raw_changenotify_send(cli->tree, &notify);
+ smbcli_mkdir(cli->tree, BASEDIR "\\subdir-name");
+
+ status = smb_raw_changenotify_recv(req, torture, &notify);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ CHECK_VAL(notify.nttrans.out.num_changes, 1);
+ CHECK_VAL(notify.nttrans.out.changes[0].action, NOTIFY_ACTION_ADDED);
+ CHECK_WSTR(notify.nttrans.out.changes[0].name, "subdir-name", STR_UNICODE);
+
+ printf("testing notify rmdir\n");
+ req = smb_raw_changenotify_send(cli->tree, &notify);
+ smbcli_rmdir(cli->tree, BASEDIR "\\subdir-name");
+
+ status = smb_raw_changenotify_recv(req, torture, &notify);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VAL(notify.nttrans.out.num_changes, 1);
+ CHECK_VAL(notify.nttrans.out.changes[0].action, NOTIFY_ACTION_REMOVED);
+ CHECK_WSTR(notify.nttrans.out.changes[0].name, "subdir-name", STR_UNICODE);
+
+ printf("CHANGE NOTIFY WITH TCON OK\n");
+
+ printf("Disconnecting secondary tree\n");
+ status = smb_tree_disconnect(tree);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ talloc_free(tree);
+
+ printf("testing notify mkdir\n");
+ req = smb_raw_changenotify_send(cli->tree, &notify);
+ smbcli_mkdir(cli->tree, BASEDIR "\\subdir-name");
+
+ status = smb_raw_changenotify_recv(req, torture, &notify);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ CHECK_VAL(notify.nttrans.out.num_changes, 1);
+ CHECK_VAL(notify.nttrans.out.changes[0].action, NOTIFY_ACTION_ADDED);
+ CHECK_WSTR(notify.nttrans.out.changes[0].name, "subdir-name", STR_UNICODE);
+
+ printf("testing notify rmdir\n");
+ req = smb_raw_changenotify_send(cli->tree, &notify);
+ smbcli_rmdir(cli->tree, BASEDIR "\\subdir-name");
+
+ status = smb_raw_changenotify_recv(req, torture, &notify);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VAL(notify.nttrans.out.num_changes, 1);
+ CHECK_VAL(notify.nttrans.out.changes[0].action, NOTIFY_ACTION_REMOVED);
+ CHECK_WSTR(notify.nttrans.out.changes[0].name, "subdir-name", STR_UNICODE);
+
+ printf("CHANGE NOTIFY WITH TDIS OK\n");
+done:
+ smb_raw_exit(cli->session);
+ return ret;
+}
+
+
+/*
+ basic testing of change notify
+*/
+bool torture_raw_notify(struct torture_context *torture,
+ struct smbcli_state *cli,
+ struct smbcli_state *cli2)
+{
+ bool ret = true;
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ ret &= test_notify_tcon(cli, torture);
+ ret &= test_notify_dir(cli, cli2, torture);
+ ret &= test_notify_mask(cli, torture);
+ ret &= test_notify_recursive(cli, torture);
+ ret &= test_notify_mask_change(cli, torture);
+ ret &= test_notify_file(cli, torture);
+ ret &= test_notify_tdis(torture);
+ ret &= test_notify_exit(torture);
+ ret &= test_notify_ulogoff(torture);
+ ret &= test_notify_tcp_dis(torture);
+ ret &= test_notify_double(cli, torture);
+ ret &= test_notify_tree(cli, torture);
+ ret &= test_notify_overflow(cli, torture);
+ ret &= test_notify_basedir(cli, torture);
+
+ smb_raw_exit(cli->session);
+ smbcli_deltree(cli->tree, BASEDIR);
+ return ret;
+}
diff --git a/source4/torture/raw/offline.c b/source4/torture/raw/offline.c
new file mode 100644
index 0000000000..5322f471a2
--- /dev/null
+++ b/source4/torture/raw/offline.c
@@ -0,0 +1,513 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ test offline files
+ */
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "libcli/raw/libcliraw.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/popt_common.h"
+#include "libcli/composite/composite.h"
+#include "libcli/smb_composite/smb_composite.h"
+#include "libcli/resolve/resolve.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++;
+ }
+
+ /* 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;i<FILE_SIZE;i++) {
+ if (state->loadfile->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;
+
+ 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 mininum 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)) {
+ 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<numstates;i++) {
+ total += state[i].count - state[i].lastcount;
+ if (timeval_elapsed(&state[i].tv_start) > 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);
+ event_add_timed(ev, state, timeval_current_ofs(1, 0), report_rate, state);
+
+ for (i=0;i<OP_ENDOFLIST;i++) {
+ if (latencies[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;i<numstates;i++) {
+ struct smb_echo p;
+ struct smbcli_request *req;
+
+ if (!state[i].tree) {
+ continue;
+ }
+
+ p.in.repeat_count = 1;
+ p.in.size = 0;
+ p.in.data = NULL;
+ req = smb_raw_echo_send(state[i].tree->session->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;i<nconnections;i++) {
+ state[i].tctx = torture;
+ state[i].mem_ctx = talloc_new(state);
+ state[i].ev = torture->ev;
+ 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;i<numstates;i++) {
+ state[i].tctx = torture;
+ state[i].mem_ctx = talloc_new(state);
+ state[i].ev = torture->ev;
+ 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;i<torture_numops;i++) {
+ int fnum;
+ char *fname = filename(mem_ctx, i);
+ char buf[FILE_SIZE];
+ NTSTATUS status;
+
+ memset(buf, 1+(i % 255), sizeof(buf));
+
+ fnum = smbcli_open(state[0].tree, fname, O_RDWR|O_CREAT, DENY_NONE);
+ if (fnum == -1) {
+ printf("Failed to open %s on connection %d\n", fname, i);
+ goto failed;
+ }
+
+ if (smbcli_write(state[0].tree, fnum, 0, buf, 0, sizeof(buf)) != sizeof(buf)) {
+ printf("Failed to write file of size %u\n", FILE_SIZE);
+ goto failed;
+ }
+
+ status = smbcli_close(state[0].tree, fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Close failed - %s\n", nt_errstr(status));
+ goto failed;
+ }
+
+ talloc_free(fname);
+ }
+
+ /* start the async ops */
+ for (i=0;i<numstates;i++) {
+ state[i].tv_start = timeval_current();
+ test_offline(&state[i]);
+ }
+
+ tv = timeval_current();
+
+ if (progress) {
+ event_add_timed(torture->ev, state, timeval_current_ofs(1, 0), report_rate, state);
+ }
+
+ printf("Running for %d seconds\n", timelimit);
+ while (timeval_elapsed(&tv) < timelimit) {
+ event_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;i<numstates;i++) {
+ while (state[i].loadfile ||
+ state[i].savefile ||
+ state[i].req) {
+ event_loop_once(torture->ev);
+ }
+ }
+
+ 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 0000000000..9f35aae4d0
--- /dev/null
+++ b/source4/torture/raw/open.c
@@ -0,0 +1,1680 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 "librpc/gen_ndr/security.h"
+#include "lib/events/events.h"
+#include "libcli/libcli.h"
+#include "torture/util.h"
+#include "auth/credentials/credentials.h"
+#include "lib/cmdline/popt_common.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)) { \
+ printf("(%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) { \
+ printf("(%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) { \
+ printf("(%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 (abs(t1-t2) > 2) { \
+ printf("(%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 (t != t2) { \
+ printf("(%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)) { \
+ printf("(%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)) { \
+ printf("(%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.path = fname; \
+ sfinfo.basic_info.in.attrib = sattrib; \
+ status = smb_raw_setpathinfo(cli->tree, &sfinfo); \
+ if (!NT_STATUS_IS_OK(status)) { \
+ printf("(%s) Failed to set attrib 0x%x on %s\n", \
+ __location__, sattrib, fname); \
+ }} while (0)
+
+/*
+ test RAW_OPEN_OPEN
+*/
+static bool test_open(struct smbcli_state *cli, struct torture_context *tctx)
+{
+ union smb_open io;
+ union smb_fileinfo finfo;
+ const char *fname = BASEDIR "\\torture_open.txt";
+ NTSTATUS status;
+ int fnum = -1, fnum2;
+ bool ret = true;
+
+ printf("Checking RAW_OPEN_OPEN\n");
+
+ 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) {
+ printf("(%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_unlink(cli->tree, fname);
+
+ return ret;
+}
+
+
+/*
+ test RAW_OPEN_OPENX
+*/
+static bool test_openx(struct smbcli_state *cli, struct torture_context *tctx)
+{
+ 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 },
+ };
+
+ printf("Checking RAW_OPEN_OPENX\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;
+ 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; i<ARRAY_SIZE(open_funcs); i++) {
+ if (open_funcs[i].with_file) {
+ fnum = create_complex_file(cli, tctx, fname);
+ if (fnum == -1) {
+ d_printf("Failed to create file %s - %s\n", fname, smbcli_errstr(cli->tree));
+ 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)) {
+ printf("(%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, (int)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) {
+ printf("(%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_unlink(cli->tree, fname_exe);
+ smbcli_unlink(cli->tree, fname);
+
+ 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 smbcli_state *cli, struct torture_context *tctx)
+{
+ 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 },
+ };
+
+ fnum = create_complex_file(cli, tctx, fname1);
+ if (fnum == -1) {
+ d_printf("Failed to create file %s - %s\n", fname1, smbcli_errstr(cli->tree));
+ ret = false;
+ goto done;
+ }
+ smbcli_close(cli->tree, fnum);
+
+ printf("Checking RAW_OPEN_T2OPEN\n");
+
+ 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; i<ARRAY_SIZE(open_funcs); i++) {
+ again:
+ if (open_funcs[i].with_file) {
+ io.t2open.in.fname = fname1;
+ } else {
+ io.t2open.in.fname = fname2;
+ }
+ io.t2open.in.open_func = open_funcs[i].open_func;
+ status = smb_raw_open(cli->tree, tctx, &io);
+ if ((io.t2open.in.num_eas != 0)
+ && NT_STATUS_EQUAL(status, NT_STATUS_EAS_NOT_SUPPORTED)
+ && torture_setting_bool(tctx, "samba3", false)) {
+ printf("(%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)) {
+ printf("(%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, (int)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_unlink(cli->tree, fname);
+
+ return ret;
+}
+
+
+/*
+ test RAW_OPEN_NTCREATEX
+*/
+static bool test_ntcreatex(struct smbcli_state *cli, struct torture_context *tctx)
+{
+ 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 },
+ };
+
+ printf("Checking RAW_OPEN_NTCREATEX\n");
+
+ /* reasonable default parameters */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED;
+ io.ntcreatex.in.root_fid = 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; i<ARRAY_SIZE(open_funcs); i++) {
+ if (open_funcs[i].with_file) {
+ fnum = smbcli_open(cli->tree, fname, O_CREAT|O_RDWR|O_TRUNC, DENY_NONE);
+ if (fnum == -1) {
+ d_printf("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)) {
+ printf("(%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.alloc_size, 0);
+ CHECK_VAL(io.ntcreatex.out.file_type, FILE_TYPE_DISK);
+ smbcli_unlink(cli->tree, fname);
+
+
+done:
+ smbcli_close(cli->tree, fnum);
+ smbcli_unlink(cli->tree, fname);
+
+ return ret;
+}
+
+
+/*
+ test RAW_OPEN_NTTRANS_CREATE
+*/
+static bool test_nttrans_create(struct smbcli_state *cli, struct torture_context *tctx)
+{
+ 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 },
+ };
+
+ printf("Checking RAW_OPEN_NTTRANS_CREATE\n");
+
+ /* reasonable default parameters */
+ io.generic.level = RAW_OPEN_NTTRANS_CREATE;
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED;
+ io.ntcreatex.in.root_fid = 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; i<ARRAY_SIZE(open_funcs); i++) {
+ if (open_funcs[i].with_file) {
+ fnum = smbcli_open(cli->tree, fname, O_CREAT|O_RDWR|O_TRUNC, DENY_NONE);
+ if (fnum == -1) {
+ d_printf("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)) {
+ printf("(%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 = (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)) {
+ printf("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 = 1<<i;
+ if (create_option & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) {
+ continue;
+ }
+ io.ntcreatex.in.create_options = create_option;
+ status = smb_raw_open(cli->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<<i;
+ } else {
+ unexpected_mask |= 1<<i;
+ printf("create option 0x%08x returned %s\n", create_option, nt_errstr(status));
+ }
+ }
+
+ CHECK_VAL(ok_mask, 0x00efcfce);
+ CHECK_VAL(not_a_directory_mask, 0x00000001);
+ CHECK_VAL(not_supported_mask, 0x00002000);
+ CHECK_VAL(invalid_parameter_mask, 0xff100030);
+ CHECK_VAL(unexpected_mask, 0x00000000);
+
+ 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.alloc_size, 0);
+ CHECK_VAL(io.ntcreatex.out.file_type, FILE_TYPE_DISK);
+ smbcli_unlink(cli->tree, fname);
+
+
+done:
+ smbcli_close(cli->tree, fnum);
+ smbcli_unlink(cli->tree, fname);
+
+ 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 smbcli_state *cli, struct torture_context *tctx)
+{
+ 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;
+
+ printf("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 = 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 = 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_unlink(cli->tree, fname);
+ return ret;
+}
+
+/*
+ test RAW_OPEN_MKNEW
+*/
+static bool test_mknew(struct smbcli_state *cli, struct torture_context *tctx)
+{
+ 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;
+
+ printf("Checking RAW_OPEN_MKNEW\n");
+
+ 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_unlink(cli->tree, fname);
+
+ return ret;
+}
+
+
+/*
+ test RAW_OPEN_CREATE
+*/
+static bool test_create(struct smbcli_state *cli, struct torture_context *tctx)
+{
+ 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;
+
+ printf("Checking RAW_OPEN_CREATE\n");
+
+ 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_unlink(cli->tree, fname);
+
+ return ret;
+}
+
+
+/*
+ test RAW_OPEN_CTEMP
+*/
+static bool test_ctemp(struct smbcli_state *cli, TALLOC_CTX *tctx)
+{
+ 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;
+
+ printf("Checking RAW_OPEN_CTEMP\n");
+
+ 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;
+ d_printf("ctemp name=%s real name=%s\n", name, fname);
+
+done:
+ smbcli_close(cli->tree, fnum);
+ if (fname) {
+ smbcli_unlink(cli->tree, fname);
+ }
+
+ return ret;
+}
+
+
+/*
+ test chained RAW_OPEN_OPENX_READX
+*/
+static bool test_chained(struct smbcli_state *cli, TALLOC_CTX *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];
+
+ printf("Checking RAW_OPEN_OPENX chained with READX\n");
+ smbcli_unlink(cli->tree, fname);
+
+ 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(buf);
+ io.openxreadx.in.maxcnt = sizeof(buf);
+ 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, sizeof(buf)) != 0) {
+ d_printf("wrong data in reply buffer\n");
+ ret = false;
+ }
+
+done:
+ smbcli_close(cli->tree, fnum);
+ smbcli_unlink(cli->tree, fname);
+
+ 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 smbcli_state *cli, TALLOC_CTX *tctx)
+{
+ 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";
+
+ printf("Checking RAW_OPEN_OPENX without leading slash on path\n");
+ 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_unlink(cli->tree, fname);
+
+ 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 smbcli_state *cli, TALLOC_CTX *tctx)
+{
+ union smb_open io;
+ const char *fname = BASEDIR "\\openx_over_dir";
+ NTSTATUS status;
+ int d_fnum = -1;
+ int fnum = -1;
+ bool ret = true;
+
+ printf("Checking RAW_OPEN_OPENX over an existing directory\n");
+ smbcli_unlink(cli->tree, fname);
+
+ /* 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_unlink(cli->tree, fname);
+
+ 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;
+ 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)) {
+ DEBUG(0, ("talloc failed\n"));
+ 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; i<num_files; i++) {
+ if (!torture_open_connection_share(mem_ctx, &(clients[i]),
+ tctx, host, share, tctx->ev)) {
+ DEBUG(0, ("Could not open %d'th connection\n", 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 = 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; i<num_files; i++) {
+ ios[i] = io;
+ requests[i] = smb_raw_open_send(clients[i]->tree, &ios[i]);
+ if (requests[i] == NULL) {
+ DEBUG(0, ("could not send %d'th request\n", i));
+ return false;
+ }
+ }
+
+ DEBUG(10, ("waiting for replies\n"));
+ while (1) {
+ bool unreplied = false;
+ for (i=0; i<num_files; i++) {
+ if (requests[i] == NULL) {
+ continue;
+ }
+ if (requests[i]->state < SMBCLI_REQUEST_DONE) {
+ unreplied = true;
+ break;
+ }
+ status = smb_raw_open_recv(requests[i], mem_ctx,
+ &ios[i]);
+
+ DEBUG(0, ("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 (event_loop_once(tctx->ev) != 0) {
+ DEBUG(0, ("event_loop_once failed\n"));
+ return false;
+ }
+ }
+
+ if ((num_ok != 1) || (num_ok + num_collision != num_files)) {
+ ret = false;
+ }
+
+ for (i=0; i<num_files; i++) {
+ torture_close_connection(clients[i]);
+ }
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+/*
+ test opening for delete on a read-only attribute file.
+*/
+static bool test_open_for_delete(struct smbcli_state *cli, struct torture_context *tctx)
+{
+ union smb_open io;
+ union smb_fileinfo finfo;
+ const char *fname = BASEDIR "\\torture_open_for_delete.txt";
+ NTSTATUS status;
+ int fnum = -1;
+ bool ret = true;
+
+ printf("Checking RAW_NTCREATEX for delete on a readonly file.\n");
+
+ /* reasonable default parameters */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_READONLY;
+ 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;
+
+ /* Create the readonly file. */
+
+ 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);
+ 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_unlink(cli->tree, fname);
+
+ return ret;
+}
+
+
+/* basic testing of all RAW_OPEN_* calls
+*/
+bool torture_raw_open(struct torture_context *torture, struct smbcli_state *cli)
+{
+ bool ret = true;
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ ret &= test_ntcreatex_brlocked(cli, torture);
+ ret &= test_open(cli, torture);
+ ret &= test_raw_open_multi(torture);
+ ret &= test_openx(cli, torture);
+ ret &= test_ntcreatex(cli, torture);
+ ret &= test_nttrans_create(cli, torture);
+ ret &= test_t2open(cli, torture);
+ ret &= test_mknew(cli, torture);
+ ret &= test_create(cli, torture);
+ ret &= test_ctemp(cli, torture);
+ ret &= test_chained(cli, torture);
+ ret &= test_no_leading_slash(cli, torture);
+ ret &= test_openx_over_dir(cli, torture);
+ ret &= test_open_for_delete(cli, torture);
+
+ smb_raw_exit(cli->session);
+ smbcli_deltree(cli->tree, BASEDIR);
+
+ return ret;
+}
diff --git a/source4/torture/raw/openbench.c b/source4/torture/raw/openbench.c
new file mode 100644
index 0000000000..bdad2b16a5
--- /dev/null
+++ b/source4/torture/raw/openbench.c
@@ -0,0 +1,487 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#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/popt_common.h"
+#include "libcli/composite/composite.h"
+#include "libcli/smb_composite/smb_composite.h"
+#include "libcli/resolve/resolve.h"
+#include "param/param.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 = event_add_timed(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 = lp_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 = cmdline_credentials;
+ io->in.fallback_to_anonymous = false;
+ io->in.workgroup = lp_workgroup(state->tctx->lp_ctx);
+ io->in.gensec_settings = lp_gensec_settings(state->mem_ctx, state->tctx->lp_ctx);
+ lp_smbcli_options(state->tctx->lp_ctx, &io->in.options);
+ lp_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,
+ lp_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 = 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)) {
+ 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 = event_add_timed(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)) {
+ 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 = event_add_timed(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)) {
+ 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 = event_add_timed(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;i<nprocs;i++) {
+ printf("%5u ", (unsigned)(state[i].count - state[i].lastcount));
+ state[i].lastcount = state[i].count;
+ }
+ printf("\r");
+ fflush(stdout);
+ report_te = event_add_timed(ev, state, timeval_current_ofs(1, 0),
+ report_rate, state);
+
+ /* send an echo on each interface to ensure it stays alive - this helps
+ with IP takeover */
+ for (i=0;i<nprocs;i++) {
+ struct smb_echo p;
+ struct smbcli_request *req;
+
+ if (!state[i].tree) {
+ continue;
+ }
+
+ p.in.repeat_count = 1;
+ p.in.size = 0;
+ p.in.data = NULL;
+ req = smb_raw_echo_send(state[i].tree->session->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;i<nprocs;i++) {
+ state[i].tctx = torture;
+ state[i].mem_ctx = talloc_new(state);
+ state[i].client_num = i;
+ state[i].ev = torture->ev;
+ if (!torture_open_connection_ev(&state[i].cli, i, torture, torture->ev)) {
+ return false;
+ }
+ talloc_steal(mem_ctx, state);
+ state[i].tree = state[i].cli->tree;
+ state[i].dest_host = talloc_strdup(state[i].mem_ctx,
+ state[i].cli->tree->session->transport->socket->hostname);
+ 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", state[i].cli->tree->session->transport->socket->port);
+ state[i].dest_ports[1] = NULL;
+ state[i].called_name = talloc_strdup(state[i].mem_ctx,
+ state[i].cli->tree->session->transport->called.name);
+ state[i].service_type = talloc_strdup(state[i].mem_ctx,
+ state[i].cli->tree->device);
+ }
+
+ 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;i<nprocs;i++) {
+ /* all connections start with the same file */
+ state[i].next_file_num = 0;
+ state[i].open_fnum = -1;
+ state[i].close_fnum = -1;
+ next_open(&state[i]);
+ }
+
+ tv = timeval_current();
+
+ if (progress) {
+ report_te = event_add_timed(torture->ev, state, timeval_current_ofs(1, 0),
+ report_rate, state);
+ }
+
+ printf("Running for %d seconds\n", timelimit);
+ while (timeval_elapsed(&tv) < timelimit) {
+ event_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;i<nprocs;i++) {
+ printf(" ");
+ }
+ printf("\r");
+ }
+
+ minops = state[0].count;
+ for (i=0;i<nprocs;i++) {
+ total += state[i].count;
+ total_retries += state[i].open_retries;
+ printf("[%d] %u ops (%u retries)\n",
+ i, state[i].count, state[i].open_retries);
+ if (state[i].count < minops) minops = state[i].count;
+ }
+ printf("%.2f ops/second (%d retries)\n",
+ total/timeval_elapsed(&tv), total_retries);
+ if (minops < 0.5*total/nprocs) {
+ printf("Failed: unbalanced open\n");
+ goto failed;
+ }
+
+ for (i=0;i<nprocs;i++) {
+ talloc_free(state[i].req_open);
+ talloc_free(state[i].req_close);
+ smb_raw_exit(state[i].tree->session);
+ }
+
+ 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 0000000000..0aeded8664
--- /dev/null
+++ b/source4/torture/raw/oplock.c
@@ -0,0 +1,3099 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/security.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/popt_common.h"
+#include "libcli/resolve/resolve.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_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_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;
+
+ lp_smbcli_options(tctx->lp_ctx, &options);
+ lp_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),
+ lp_smb_ports(tctx->lp_ctx),
+ torture_setting_string(tctx, "share", NULL),
+ NULL, lp_socket_options(tctx->lp_ctx), cmdline_credentials,
+ lp_resolve_context(tctx->lp_ctx),
+ tctx->ev, &options, &session_options,
+ lp_iconv_convenience(tctx->lp_ctx),
+ lp_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;
+}
+
+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 = 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);
+ 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);
+ 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 = 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;
+ 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);
+ 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);
+ CHECK_VAL(break_info.count, 0);
+ CHECK_VAL(break_info.failures, 0);
+
+ torture_comment(tctx, "close 1st 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);
+ 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 = 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, "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);
+ CHECK_VAL(break_info.count, 1);
+ 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 = 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);
+ 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 = 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 dispostion 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);
+ CHECK_VAL(break_info.count, 1);
+ 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 = 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;
+
+ printf("trying rename while first file open\n");
+ status = smb_raw_rename(cli2->tree, &rn);
+
+ CHECK_STATUS(tctx, status, NT_STATUS_SHARING_VIOLATION);
+ 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_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 = 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);
+
+ 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);
+
+ 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);
+ msleep(100);
+ smbcli_write(cli1->tree, fnum, 0, &c, 1, 1);
+
+ 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 = 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);
+
+ 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);
+
+ 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);
+ msleep(100);
+ smbcli_write(cli1->tree, fnum, 0, &c, 1, 1);
+
+ 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 = 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);
+
+ 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 = 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.read.level = RAW_READ_READ;
+ rd.read.in.file.fnum = fnum;
+ rd.read.in.count = 1;
+ rd.read.in.offset = 0;
+ rd.read.in.remaining = 0;
+ status = smb_raw_read(cli1->tree, &rd);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ 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 = 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);
+
+ 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);
+
+ /*
+ base ntcreatex parms
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 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);
+ 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.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);
+
+ 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);
+ msleep(100);
+ smbcli_write(cli1->tree, fnum, 0, &c, 1, 1);
+
+ 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 = 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);
+
+ 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 = 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);
+ 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 = 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;
+ 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;
+ 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);
+
+ /* Now the oplock break request comes in. But right now we can't
+ * answer it. Do another write */
+
+ msleep(100);
+ smbcli_write(cli2->tree, fnum2, 0, &c, 1, 1);
+
+ 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 = 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;
+ CHECK_VAL(break_info.count, 0);
+ CHECK_VAL(break_info.failures, 0);
+ CHECK_VAL(io.ntcreatex.out.oplock_level, 0);
+
+ 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;
+ 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);
+ }
+
+ /* Now the oplock break request comes in. But right now we can't
+ * answer it. Do another write */
+
+ msleep(100);
+
+ {
+ 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);
+ }
+
+ 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 = 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-eof on pathname breaks an exclusive oplock. */
+ torture_comment(tctx, "BATCH11: Test if setpathinfo set EOF 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;
+ 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);
+ CHECK_VAL(break_info.count, 1);
+ 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 = 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;
+ 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);
+ CHECK_VAL(break_info.count, 1);
+ 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 = 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);
+ 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.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 dispostion 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);
+ CHECK_VAL(break_info.count, 1);
+ 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 = 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);
+ 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.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 dispostion 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);
+ CHECK_VAL(break_info.count, 1);
+ 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 = 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);
+ 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.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;
+ 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);
+ 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 = 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);
+ 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.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 dispostion 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);
+ CHECK_VAL(break_info.count, 1);
+ 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 = 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;
+
+ printf("trying rename while first file open\n");
+ status = smb_raw_rename(cli2->tree, &rn);
+
+ CHECK_STATUS(tctx, status, NT_STATUS_SHARING_VIOLATION);
+ 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 = 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;
+ printf("trying rename while first file open\n");
+ status = smb_raw_rename(cli2->tree, &rn);
+
+ CHECK_STATUS(tctx, status, NT_STATUS_SHARING_VIOLATION);
+ 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 = 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, "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 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);
+ 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);
+ 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);
+
+ smbcli_close(cli1->tree, fnum);
+
+done:
+ 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 = 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, "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);
+ 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);
+ 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);
+
+ smbcli_close(cli1->tree, fnum);
+
+done:
+ 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 = 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);
+ 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 = 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);
+
+ 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);
+ 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, "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);
+ CHECK_VAL(break_info.count, 1);
+ CHECK_VAL(break_info.failures, 0);
+ CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_LEVEL_II);
+
+ 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);
+ CHECK_VAL(break_info.count, 1);
+ CHECK_VAL(break_info.failures, 0);
+ CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_LEVEL_II);
+
+ 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);
+
+ 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_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 = 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);
+
+ 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;
+ struct timeval tv;
+ int timeout = torture_setting_int(tctx, "oplocktimeout", 30);
+ int te;
+
+ if (torture_setting_bool(tctx, "samba3", false)) {
+ torture_skip(tctx, "BATCH22 disabled against samba3\n");
+ }
+
+ 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 = 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 shoud 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);
+ CHECK_STATUS(tctx, status, NT_STATUS_SHARING_VIOLATION);
+ te = (int)timeval_elapsed(&tv);
+ 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 shoud 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);
+ CHECK_VAL(io.ntcreatex.out.oplock_level, LEVEL_II_OPLOCK_RETURN);
+ te = (int)timeval_elapsed(&tv);
+ /* it should come in without delay */
+ CHECK_RANGE(te+1, 0, timeout);
+ fnum2 = io.ntcreatex.out.file.fnum;
+
+ CHECK_VAL(break_info.count, 0);
+
+ smbcli_close(cli1->tree, fnum);
+ smbcli_close(cli1->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_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);
+
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree);
+
+ ret = open_connection_no_level2_oplocks(tctx, &cli3);
+ CHECK_VAL(ret, true);
+
+ /*
+ base ntcreatex parms
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 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);
+ 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);
+
+ 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);
+
+ 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);
+
+ 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);
+
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree);
+
+ ret = open_connection_no_level2_oplocks(tctx, &cli3);
+ CHECK_VAL(ret, true);
+
+ /*
+ base ntcreatex parms
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 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);
+ 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);
+
+ 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);
+
+ 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 = 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);
+ 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;
+}
+
+/*
+ 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, "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, "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);
+
+ 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;i<torture_nprocs;i++) {
+ if (!torture_open_connection_ev(&cli[i], i, torture, torture->ev)) {
+ 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 = 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;i<torture_nprocs;i++) {
+ NTSTATUS status;
+
+ status = smb_raw_open(cli[i]->tree, 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[] = {
+ { BASEDIR "\\notshared_close", true,
+ NTCREATEX_SHARE_ACCESS_NONE, },
+ { BASEDIR "\\notshared_noclose", false,
+ NTCREATEX_SHARE_ACCESS_NONE, },
+ { BASEDIR "\\shared_close", true,
+ NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_WRITE|NTCREATEX_SHARE_ACCESS_DELETE, },
+ { BASEDIR "\\shared_noclose", false,
+ 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;i<ARRAY_SIZE(hold_info);i++) {
+ if (hold_info[i].fnum == fnum) break;
+ }
+
+ if (i == ARRAY_SIZE(hold_info)) {
+ printf("oplock break for unknown fnum %u\n", fnum);
+ return false;
+ }
+
+ info = &hold_info[i];
+
+ if (info->close_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 =
+ (struct tevent_context *)cli->transport->socket->event.ctx;
+ int i;
+
+ printf("Setting up open files with oplocks in %s\n", BASEDIR);
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ smbcli_oplock_handler(cli->transport, oplock_handler_hold, cli->tree);
+
+ /* setup the files */
+ for (i=0;i<ARRAY_SIZE(hold_info);i++) {
+ union smb_open io;
+ NTSTATUS status;
+ char c = 1;
+
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 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 = hold_info[i].share_access;
+ 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 = hold_info[i].fname;
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
+ NTCREATEX_FLAGS_REQUEST_OPLOCK |
+ NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
+ printf("opening %s\n", hold_info[i].fname);
+
+ status = smb_raw_open(cli->tree, 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");
+ event_loop_wait(ev);
+
+ return true;
+}
diff --git a/source4/torture/raw/pingpong.c b/source4/torture/raw/pingpong.c
new file mode 100755
index 0000000000..a19b330dbd
--- /dev/null
+++ b/source4/torture/raw/pingpong.c
@@ -0,0 +1,266 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ 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 "torture/torture.h"
+#include "libcli/raw/libcliraw.h"
+#include "system/time.h"
+#include "system/filesys.h"
+#include "libcli/libcli.h"
+#include "torture/util.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 dont 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);
+ }
+}
+
+
+static struct timeval tp1, tp2;
+
+static void start_timer(void)
+{
+ gettimeofday(&tp1, NULL);
+}
+
+static double end_timer(void)
+{
+ gettimeofday(&tp2, NULL);
+ return (tp2.tv_sec + (tp2.tv_usec*1.0e-6)) -
+ (tp1.tv_sec + (tp1.tv_usec*1.0e-6));
+}
+
+/*
+ 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;
+
+ 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_timer();
+ 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 (end_timer() > 1.0) {
+ printf("%8u locks/sec\r",
+ (unsigned)(2*count/end_timer()));
+ fflush(stdout);
+ start_timer();
+ count=0;
+ }
+ loops++;
+ }
+
+ talloc_free(mem_ctx);
+ return true;
+}
+
diff --git a/source4/torture/raw/qfileinfo.c b/source4/torture/raw/qfileinfo.c
new file mode 100644
index 0000000000..c7673be526
--- /dev/null
+++ b/source4/torture/raw/qfileinfo.c
@@ -0,0 +1,865 @@
+/*
+ Unix SMB/CIFS implementation.
+ RAW_FILEINFO_* individual test suite
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/libcli.h"
+#include "torture/util.h"
+#include "librpc/rpc/dcerpc.h"
+#include "torture/rpc/rpc.h"
+#include "torture/raw/proto.h"
+#include "param/param.h"
+
+static struct {
+ const char *name;
+ enum smb_fileinfo_level level;
+ uint_t only_paths:1;
+ uint_t only_handles:1;
+ uint32_t capability_mask;
+ uint_t 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,
+ },
+ { .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
+ },
+ { .name ="STREAM_INFO",
+ .level = RAW_FILEINFO_STREAM_INFO,
+ .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER
+ },
+ { .name ="COMPRESSION_INFO",
+ .level = RAW_FILEINFO_COMPRESSION_INFO,
+ .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER
+ },
+ { .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
+ },
+ { .name ="STREAM_INFORMATION",
+ .level = RAW_FILEINFO_STREAM_INFORMATION,
+ .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER
+ },
+ { .name = "COMPRESSION_INFORMATION",
+ .level = RAW_FILEINFO_COMPRESSION_INFORMATION,
+ .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER
+ },
+ { .name ="NETWORK_OPEN_INFORMATION",
+ .level = RAW_FILEINFO_NETWORK_OPEN_INFORMATION,
+ .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER
+ },
+ { .name = "ATTRIBUTE_TAG_INFORMATION",
+ .level = RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION,
+ .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER
+ },
+ { 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 (abs(t2 - t) <= 2) return 0;
+ return t2 - t;
+}
+
+
+/*
+ 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, (uint_t)s1->n1.out.v1, \
+ #n2, #v2, (uint_t)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, \
+ (uint_t)s1->n1.out.v1, \
+ (uint_t)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)
+{
+ int i;
+ bool ret = true;
+ int 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(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].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_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("%d 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, \
+ (uint_t)s1->stype.out.tfield, \
+ (uint_t)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, \
+ (uint_t)s1->stype.out.tfield, \
+ (uint_t)correct_size); \
+ ret = false; \
+ }} while (0)
+
+ s1 = fnum_find("STANDARD_INFO");
+ correct_size = s1->standard_info.out.size;
+ torture_comment(torture, "size: %u\n", (uint_t)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", (uint_t)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, \
+ (uint_t)s1->stype.out.tfield, \
+ (uint_t)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, \
+ (uint_t)s1->stype.out.tfield, \
+ (uint_t)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", (uint_t)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;
+ 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;i<s1->all_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;i<s1->all_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, 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, \
+ (uint_t)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, \
+ (uint_t)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";
+ struct dcerpc_pipe *p;
+ struct smbcli_tree *ipc_tree;
+ NTSTATUS status;
+
+ if (!(p = dcerpc_pipe_init(torture, cli->tree->session->transport->socket->event.ctx,
+ lp_iconv_convenience(torture->lp_ctx)))) {
+ return false;
+ }
+
+ status = dcerpc_pipe_open_smb(p, cli->tree, fname);
+ torture_assert_ntstatus_ok(torture, status, "dcerpc_pipe_open_smb failed");
+
+ ipc_tree = dcerpc_smb_tree(p->conn);
+ fnum = dcerpc_smb_fnum(p->conn);
+
+ ret = torture_raw_qfileinfo_internals(torture, torture, ipc_tree, fnum, fname, true /* is_ipc */);
+
+ talloc_free(p);
+ return ret;
+}
diff --git a/source4/torture/raw/qfsinfo.c b/source4/torture/raw/qfsinfo.c
new file mode 100644
index 0000000000..c1c77edba9
--- /dev/null
+++ b/source4/torture/raw/qfsinfo.c
@@ -0,0 +1,296 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/libcli.h"
+#include "torture/util.h"
+
+
+static struct {
+ const char *name;
+ enum smb_fsinfo_level level;
+ uint32_t capability_mask;
+ NTSTATUS status;
+ union smb_fsinfo fsinfo;
+} levels[] = {
+ {"DSKATTR", RAW_QFS_DSKATTR, },
+ {"ALLOCATION", RAW_QFS_ALLOCATION, },
+ {"VOLUME", RAW_QFS_VOLUME, },
+ {"VOLUME_INFO", RAW_QFS_VOLUME_INFO, },
+ {"SIZE_INFO", RAW_QFS_SIZE_INFO, },
+ {"DEVICE_INFO", RAW_QFS_DEVICE_INFO, },
+ {"ATTRIBUTE_INFO", RAW_QFS_ATTRIBUTE_INFO, },
+ {"UNIX_INFO", RAW_QFS_UNIX_INFO, CAP_UNIX},
+ {"VOLUME_INFORMATION", RAW_QFS_VOLUME_INFORMATION, },
+ {"SIZE_INFORMATION", RAW_QFS_SIZE_INFORMATION, },
+ {"DEVICE_INFORMATION", RAW_QFS_DEVICE_INFORMATION, },
+ {"ATTRIBUTE_INFORMATION", RAW_QFS_ATTRIBUTE_INFORMATION, },
+ {"QUOTA_INFORMATION", RAW_QFS_QUOTA_INFORMATION, },
+ {"FULL_SIZE_INFORMATION", RAW_QFS_FULL_SIZE_INFORMATION, },
+#if 0
+ /* w2k3 seems to no longer support this */
+ {"OBJECTID_INFORMATION", RAW_QFS_OBJECTID_INFORMATION, },
+#endif
+ { 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, (uint_t)s1->n1.out.v1, \
+ #n2, #v2, (uint_t)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, (uint_t)s1->n1.out.v1, \
+ #n2, #v2, (uint_t)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, \
+ (uint_t)s1->n1.out.v1, \
+ (uint_t)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)
+{
+ int i;
+ bool ret = true;
+ int 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, "%d 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 (abs(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 (abs(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 0000000000..138f263106
--- /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 <http://www.gnu.org/licenses/>.
+*/
+
+#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(void)
+{
+ struct torture_suite *suite = torture_suite_create(
+ talloc_autofree_context(),
+ "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_1smb_test(suite, "SFILEINFO", torture_raw_sfileinfo);
+ torture_suite_add_1smb_test(suite, "SFILEINFO-BUG", torture_raw_sfileinfo_bug);
+ torture_suite_add_1smb_test(suite, "SFILEINFO-RENAME",
+ torture_raw_sfileinfo_rename);
+ torture_suite_add_suite(suite, torture_raw_search(suite));
+ torture_suite_add_1smb_test(suite, "CLOSE", torture_raw_close);
+ torture_suite_add_1smb_test(suite, "OPEN", torture_raw_open);
+ 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_2smb_test(suite, "NOTIFY", torture_raw_notify);
+ 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_1smb_test(suite, "CONTEXT", torture_raw_context);
+ 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_1smb_test(suite, "STREAMS", torture_raw_streams);
+ torture_suite_add_1smb_test(suite, "ACLS", torture_raw_acls);
+ torture_suite_add_1smb_test(suite, "COMPOSITE", torture_raw_composite);
+ torture_suite_add_simple_test(suite, "SAMBA3HIDE", torture_samba3_hide);
+ torture_suite_add_simple_test(suite, "SAMBA3CLOSEERR", torture_samba3_closeerr);
+ torture_suite_add_simple_test(suite, "SAMBA3ROOTDIRFID",
+ torture_samba3_rootdirfid);
+ torture_suite_add_simple_test(suite, "SAMBA3CHECKFSP", torture_samba3_checkfsp);
+ torture_suite_add_simple_test(suite, "SAMBA3OPLOCKLOGOFF", torture_samba3_oplock_logoff);
+ torture_suite_add_simple_test(suite, "SAMBA3BADPATH", torture_samba3_badpath);
+ torture_suite_add_simple_test(suite, "SAMBA3CASEINSENSITIVE",
+ torture_samba3_caseinsensitive);
+ torture_suite_add_simple_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(suite);
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/torture/raw/read.c b/source4/torture/raw/read.c
new file mode 100644
index 0000000000..977ac6b8cb
--- /dev/null
+++ b/source4/torture/raw/read.c
@@ -0,0 +1,953 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#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"
+
+#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 CHECK_VALUE(v, correct) do { \
+ if ((v) != (correct)) { \
+ printf("(%s) Incorrect value %s=%ld - should be %ld\n", \
+ __location__, #v, (long)v, (long)correct); \
+ ret = false; \
+ goto done; \
+ }} while (0)
+
+#define CHECK_BUFFER(buf, seed, len) do { \
+ if (!check_buffer(buf, seed, len, __LINE__)) { \
+ ret = false; \
+ goto done; \
+ }} while (0)
+
+#define BASEDIR "\\testread"
+
+
+/*
+ setup a random buffer based on a seed
+*/
+static void setup_buffer(uint8_t *buf, uint_t seed, int len)
+{
+ int i;
+ srandom(seed);
+ for (i=0;i<len;i++) buf[i] = random();
+}
+
+/*
+ check a random buffer based on a seed
+*/
+static bool check_buffer(uint8_t *buf, uint_t seed, int len, int line)
+{
+ int i;
+ srandom(seed);
+ for (i=0;i<len;i++) {
+ uint8_t v = random();
+ if (buf[i] != v) {
+ printf("Buffer incorrect at line %d! ofs=%d v1=0x%x v2=0x%x\n",
+ line, i, buf[i], v);
+ return false;
+ }
+ }
+ return true;
+}
+
+/*
+ test read ops
+*/
+static bool test_read(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";
+ uint_t seed = time(NULL);
+
+ buf = talloc_zero_array(tctx, uint8_t, maxsize);
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ printf("Testing RAW_READ_READ\n");
+ io.generic.level = RAW_READ_READ;
+
+ 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.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";
+ uint_t seed = time(NULL);
+
+ buf = talloc_zero_array(tctx, uint8_t, maxsize);
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ 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";
+ uint_t seed = time(NULL);
+
+ buf = talloc_zero_array(tctx, uint8_t, maxsize);
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ 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);
+
+ 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);
+
+ 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("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);
+ 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);
+ 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);
+ }
+
+ 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));
+ 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 large read\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);
+
+ 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 (torture_setting_bool(tctx, "samba3", false)) {
+ printf("SAMBA3: large read extension\n");
+ CHECK_VALUE(io.readx.out.nread, 80000);
+ } else {
+ CHECK_VALUE(io.readx.out.nread, 0);
+ }
+ CHECK_BUFFER(buf, seed, io.readx.out.nread);
+
+ 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);
+
+ 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);
+
+ 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);
+
+ io.readx.in.maxcnt = 0x10000;
+ status = smb_raw_read(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ if (torture_setting_bool(tctx, "samba3", false)) {
+ printf("SAMBA3: large read extension\n");
+ CHECK_VALUE(io.readx.out.nread, 0x10000);
+ } else {
+ CHECK_VALUE(io.readx.out.nread, 0);
+ }
+
+ io.readx.in.maxcnt = 0x10001;
+ status = smb_raw_read(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ if (torture_setting_bool(tctx, "samba3", false)) {
+ printf("SAMBA3: large read extension\n");
+ CHECK_VALUE(io.readx.out.nread, 0x10001);
+ } else {
+ CHECK_VALUE(io.readx.out.nread, 0);
+ }
+ }
+
+ 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);
+
+ 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);
+
+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";
+ uint_t seed = time(NULL);
+
+ buf = talloc_zero_array(tctx, uint8_t, maxsize);
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ 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);
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ printf("Testing RAW_READ_READX with read_for_execute\n");
+
+ op.generic.level = RAW_OPEN_NTCREATEX;
+ op.ntcreatex.in.root_fid = 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 = 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 = 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 0000000000..951d91a684
--- /dev/null
+++ b/source4/torture/raw/rename.c
@@ -0,0 +1,624 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/libcli.h"
+#include "torture/util.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 CHECK_VALUE(v, correct) do { \
+ if ((v) != (correct)) { \
+ printf("(%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;
+
+ printf("Testing SMBmv\n");
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ printf("Trying simple rename\n");
+
+ op.generic.level = RAW_OPEN_NTCREATEX;
+ op.ntcreatex.in.root_fid = 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;
+
+ printf("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;
+
+ printf("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);
+
+ printf("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) {
+ printf("(%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;
+
+ printf("trying rename while not open\n");
+ smb_raw_exit(cli->session);
+ status = smb_raw_rename(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("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);
+
+
+ printf("trying wildcard rename\n");
+ io.rename.in.pattern1 = BASEDIR "\\*.txt";
+ io.rename.in.pattern2 = fname1;
+
+ status = smb_raw_rename(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("and again\n");
+ status = smb_raw_rename(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("Trying extension change\n");
+ io.rename.in.pattern1 = BASEDIR "\\*.txt";
+ io.rename.in.pattern2 = BASEDIR "\\*.bak";
+ status = smb_raw_rename(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = smb_raw_rename(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_NO_SUCH_FILE);
+
+ printf("Checking attrib handling\n");
+ torture_set_file_attribute(cli->tree, BASEDIR "\\test1.bak", FILE_ATTRIBUTE_HIDDEN);
+ io.rename.in.pattern1 = BASEDIR "\\test1.bak";
+ io.rename.in.pattern2 = BASEDIR "\\*.txt";
+ io.rename.in.attrib = 0;
+ status = smb_raw_rename(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_NO_SUCH_FILE);
+
+ io.rename.in.attrib = FILE_ATTRIBUTE_HIDDEN;
+ status = smb_raw_rename(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_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;
+
+ printf("\nTesting OSX Rename\n");
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+ op.generic.level = RAW_OPEN_NTCREATEX;
+ op.ntcreatex.in.root_fid = 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. */
+
+ printf("Checking os X rename (case changing)\n");
+
+ finfo.generic.level = RAW_FILEINFO_ALL_INFO;
+ finfo.all_info.in.file.path = FNAME1;
+ printf("Looking for file %s \n",FNAME1);
+ status = smb_raw_pathinfo(cli->tree, tctx, &finfo);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_OK)) {
+ printf("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 */
+ printf("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);
+ printf("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;
+
+ printf("Testing SMBntrename\n");
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ printf("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);
+
+ smb_raw_exit(cli->session);
+ status = smb_raw_rename(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("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);
+
+ printf("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);
+
+ printf("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);
+
+ printf("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);
+
+ printf("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);
+
+ printf("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);
+ CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
+
+ io.ntrename.in.flags = 300;
+ status = smb_raw_rename(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
+
+ io.ntrename.in.flags = 0x106;
+ status = smb_raw_rename(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
+
+ printf("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);
+
+ printf("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)) {
+ printf("i=%d status=%s\n", i, nt_errstr(status));
+ }
+ }
+#endif
+
+ printf("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 (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ printf("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 *fname = BASEDIR "\\dir_for_rename\\file.txt";
+ bool ret = true;
+ int fnum = -1;
+
+ printf("Checking rename on a directory containing an open file.\n");
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ /* create a directory */
+ smbcli_rmdir(cli->tree, dname1);
+ smbcli_rmdir(cli->tree, dname2);
+ smbcli_unlink(cli->tree, dname1);
+ smbcli_unlink(cli->tree, dname2);
+
+ 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);
+
+ /* 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 = 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);
+
+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 0000000000..1f1501045c
--- /dev/null
+++ b/source4/torture/raw/samba3hide.c
@@ -0,0 +1,339 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "libcli/raw/libcliraw.h"
+#include "system/time.h"
+#include "system/filesys.h"
+#include "libcli/libcli.h"
+#include "torture/util.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_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 = "test.txt";
+ int fnum;
+ NTSTATUS status;
+ struct smbcli_tree *hideunread;
+ struct smbcli_tree *hideunwrite;
+
+ if (!torture_open_connection_share(
+ torture, &cli, torture, torture_setting_string(torture, "host", NULL),
+ torture_setting_string(torture, "share", NULL), torture->ev)) {
+ d_printf("torture_open_connection_share failed\n");
+ return false;
+ }
+
+ status = torture_second_tcon(torture, cli->session, "hideunread",
+ &hideunread);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("second_tcon(hideunread) failed: %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ status = torture_second_tcon(torture, cli->session, "hideunwrite",
+ &hideunwrite);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("second_tcon(hideunwrite) failed: %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ 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) {
+ d_printf("Failed to create %s - %s\n", fname,
+ smbcli_errstr(cli->tree));
+ return false;
+ }
+
+ smbcli_close(cli->tree, fnum);
+
+ if (!smbcli_file_exists(cli->tree, fname)) {
+ d_printf("%s does not exist\n", fname);
+ return false;
+ }
+
+ /* R/W file should be visible everywhere */
+
+ status = smbcli_chmod(cli->tree, fname, UNIX_R_USR|UNIX_W_USR);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("smbcli_chmod failed: %s\n", nt_errstr(status));
+ return false;
+ }
+ if (!is_writeable(torture, cli->tree, fname)) {
+ d_printf("File not writable\n");
+ return false;
+ }
+ if (!is_readable(cli->tree, fname)) {
+ d_printf("File not readable\n");
+ return false;
+ }
+ if (!is_visible(cli->tree, fname)) {
+ d_printf("r/w file not visible via normal share\n");
+ return false;
+ }
+ if (!is_visible(hideunread, fname)) {
+ d_printf("r/w file not visible via hide unreadable\n");
+ return false;
+ }
+ if (!is_visible(hideunwrite, fname)) {
+ d_printf("r/w file not visible via hide unwriteable\n");
+ return false;
+ }
+
+ /* R/O file should not be visible via hide unwriteable files */
+
+ status = smbcli_chmod(cli->tree, fname, UNIX_R_USR);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("smbcli_chmod failed: %s\n", nt_errstr(status));
+ return false;
+ }
+ if (is_writeable(torture, cli->tree, fname)) {
+ d_printf("r/o is writable\n");
+ return false;
+ }
+ if (!is_readable(cli->tree, fname)) {
+ d_printf("r/o not readable\n");
+ return false;
+ }
+ if (!is_visible(cli->tree, fname)) {
+ d_printf("r/o file not visible via normal share\n");
+ return false;
+ }
+ if (!is_visible(hideunread, fname)) {
+ d_printf("r/o file not visible via hide unreadable\n");
+ return false;
+ }
+ if (is_visible(hideunwrite, fname)) {
+ d_printf("r/o file visible via hide unwriteable\n");
+ return false;
+ }
+
+ /* inaccessible file should be only visible on normal share */
+
+ status = smbcli_chmod(cli->tree, fname, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("smbcli_chmod failed: %s\n", nt_errstr(status));
+ return false;
+ }
+ if (is_writeable(torture, cli->tree, fname)) {
+ d_printf("inaccessible file is writable\n");
+ return false;
+ }
+ if (is_readable(cli->tree, fname)) {
+ d_printf("inaccessible file is readable\n");
+ return false;
+ }
+ if (!is_visible(cli->tree, fname)) {
+ d_printf("inaccessible file not visible via normal share\n");
+ return false;
+ }
+ if (is_visible(hideunread, fname)) {
+ d_printf("inaccessible file visible via hide unreadable\n");
+ return false;
+ }
+ if (is_visible(hideunwrite, fname)) {
+ d_printf("inaccessible file visible via hide unwriteable\n");
+ return false;
+ }
+
+ 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 = NULL;
+ bool result = false;
+ NTSTATUS status;
+ const char *dname = "closeerr.dir";
+ const char *fname = "closeerr.dir\\closerr.txt";
+ int fnum;
+
+ if (!torture_open_connection(&cli, tctx, 0)) {
+ goto fail;
+ }
+
+ 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;
+
+ fail:
+ if (cli) {
+ torture_close_connection(cli);
+ }
+ return result;
+}
diff --git a/source4/torture/raw/samba3misc.c b/source4/torture/raw/samba3misc.c
new file mode 100644
index 0000000000..c4c790cb0a
--- /dev/null
+++ b/source4/torture/raw/samba3misc.c
@@ -0,0 +1,974 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#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"
+
+#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; \
+ } \
+} while (0)
+
+bool torture_samba3_checkfsp(struct torture_context *torture)
+{
+ struct smbcli_state *cli;
+ const char *fname = "test.txt";
+ const char *dirname = "testdir";
+ int fnum;
+ NTSTATUS status;
+ bool ret = true;
+ TALLOC_CTX *mem_ctx;
+ ssize_t nread;
+ char buf[16];
+ struct smbcli_tree *tree2;
+
+ if ((mem_ctx = talloc_init("torture_samba3_checkfsp")) == NULL) {
+ d_printf("talloc_init failed\n");
+ return false;
+ }
+
+ if (!torture_open_connection_share(
+ torture, &cli, torture, torture_setting_string(torture, "host", NULL),
+ torture_setting_string(torture, "share", NULL), torture->ev)) {
+ d_printf("torture_open_connection_share failed\n");
+ ret = false;
+ goto done;
+ }
+
+ smbcli_deltree(cli->tree, dirname);
+
+ status = torture_second_tcon(torture, cli->session,
+ torture_setting_string(torture, "share", NULL),
+ &tree2);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ if (!NT_STATUS_IS_OK(status))
+ goto done;
+
+ /* Try a read on an invalid FID */
+
+ nread = smbcli_read(cli->tree, 4711, buf, 0, sizeof(buf));
+ CHECK_STATUS(smbcli_nt_error(cli->tree), NT_STATUS_INVALID_HANDLE);
+
+ /* Try a read on a directory handle */
+
+ status = smbcli_mkdir(cli->tree, dirname);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("smbcli_mkdir failed: %s\n", nt_errstr(status));
+ ret = false;
+ goto done;
+ }
+
+ /* 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 = 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.fname = dirname;
+ status = smb_raw_open(cli->tree, mem_ctx, &io);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("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) {
+ d_printf("smbcli_read on a directory succeeded, expected "
+ "failure\n");
+ ret = false;
+ }
+
+ CHECK_STATUS(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) {
+ d_printf("smbcli_read on a directory succeeded, expected "
+ "failure\n");
+ ret = false;
+ }
+
+ CHECK_STATUS(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) {
+ d_printf("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(smbcli_nt_error(tree2), NT_STATUS_INVALID_HANDLE);
+
+ smbcli_close(cli->tree, fnum);
+
+ done:
+ smbcli_deltree(cli->tree, dirname);
+ torture_close_connection(cli);
+ 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;
+ uint_t openfn=0;
+ uint_t 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<<OPENX_MODE_DENY_SHIFT);
+
+ if ((flags & O_ACCMODE) == O_RDWR) {
+ accessmode |= OPENX_MODE_ACCESS_RDWR;
+ } else if ((flags & O_ACCMODE) == O_WRONLY) {
+ accessmode |= OPENX_MODE_ACCESS_WRITE;
+ } else if ((flags & O_ACCMODE) == O_RDONLY) {
+ accessmode |= OPENX_MODE_ACCESS_READ;
+ }
+
+#if defined(O_SYNC)
+ if ((flags & O_SYNC) == O_SYNC) {
+ accessmode |= OPENX_MODE_WRITE_THRU;
+ }
+#endif
+
+ if (share_mode == DENY_FCB) {
+ accessmode = OPENX_MODE_ACCESS_FCB | OPENX_MODE_DENY_FCB;
+ }
+
+ open_parms.openx.level = RAW_OPEN_OPENX;
+ open_parms.openx.in.flags = 0;
+ open_parms.openx.in.open_mode = accessmode;
+ 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 = openfn;
+ open_parms.openx.in.size = 0;
+ open_parms.openx.in.timeout = 0;
+ open_parms.openx.in.fname = fname;
+
+ status = smb_raw_open(tree, mem_ctx, &open_parms);
+ talloc_free(mem_ctx);
+
+ if (fnum && NT_STATUS_IS_OK(status)) {
+ *fnum = open_parms.openx.out.file.fnum;
+ }
+
+ return status;
+}
+
+static NTSTATUS raw_smbcli_t2open(struct smbcli_tree *tree, const char *fname, int flags, int share_mode, int *fnum)
+{
+ union smb_open io;
+ uint_t openfn=0;
+ uint_t accessmode=0;
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+
+ mem_ctx = talloc_init("raw_t2open");
+ 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<<OPENX_MODE_DENY_SHIFT);
+
+ if ((flags & O_ACCMODE) == O_RDWR) {
+ accessmode |= OPENX_MODE_ACCESS_RDWR;
+ } else if ((flags & O_ACCMODE) == O_WRONLY) {
+ accessmode |= OPENX_MODE_ACCESS_WRITE;
+ } else if ((flags & O_ACCMODE) == O_RDONLY) {
+ accessmode |= OPENX_MODE_ACCESS_READ;
+ }
+
+#if defined(O_SYNC)
+ if ((flags & O_SYNC) == O_SYNC) {
+ accessmode |= OPENX_MODE_WRITE_THRU;
+ }
+#endif
+
+ if (share_mode == DENY_FCB) {
+ accessmode = OPENX_MODE_ACCESS_FCB | OPENX_MODE_DENY_FCB;
+ }
+
+ memset(&io, '\0', sizeof(io));
+ io.t2open.level = RAW_OPEN_T2OPEN;
+ io.t2open.in.flags = 0;
+ io.t2open.in.open_mode = accessmode;
+ io.t2open.in.search_attrs = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN;
+ io.t2open.in.file_attrs = 0;
+ io.t2open.in.write_time = 0;
+ io.t2open.in.open_func = openfn;
+ io.t2open.in.size = 0;
+ io.t2open.in.timeout = 0;
+ io.t2open.in.fname = fname;
+
+ io.t2open.in.num_eas = 1;
+ io.t2open.in.eas = talloc_array(mem_ctx, 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(mem_ctx, "first value", 11);
+
+ status = smb_raw_open(tree, mem_ctx, &io);
+ talloc_free(mem_ctx);
+
+ if (fnum && NT_STATUS_IS_OK(status)) {
+ *fnum = io.openx.out.file.fnum;
+ }
+
+ return status;
+}
+
+static NTSTATUS raw_smbcli_ntcreate(struct smbcli_tree *tree, const char *fname, int *fnum)
+{
+ union smb_open io;
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+
+ mem_ctx = talloc_init("raw_t2open");
+ if (!mem_ctx) return NT_STATUS_NO_MEMORY;
+
+ memset(&io, '\0', sizeof(io));
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED;
+ io.ntcreatex.in.root_fid = 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;
+ 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(tree, mem_ctx, &io);
+ talloc_free(mem_ctx);
+
+ if (fnum && NT_STATUS_IS_OK(status)) {
+ *fnum = io.openx.out.file.fnum;
+ }
+
+ return status;
+}
+
+
+bool torture_samba3_badpath(struct torture_context *torture)
+{
+ struct smbcli_state *cli_nt;
+ struct smbcli_state *cli_dos;
+ const char *fname = "test.txt";
+ const char *fname1 = "test1.txt";
+ const char *dirname = "testdir";
+ char *fpath;
+ char *fpath1;
+ int fnum;
+ NTSTATUS status;
+ bool ret = true;
+ TALLOC_CTX *mem_ctx;
+ bool nt_status_support;
+
+ if (!(mem_ctx = talloc_init("torture_samba3_badpath"))) {
+ d_printf("talloc_init failed\n");
+ return false;
+ }
+
+ nt_status_support = lp_nt_status_support(torture->lp_ctx);
+
+ if (!lp_set_cmdline(torture->lp_ctx, "nt status support", "yes")) {
+ printf("Could not set 'nt status support = yes'\n");
+ goto fail;
+ }
+
+ if (!torture_open_connection(&cli_nt, torture, 0)) {
+ goto fail;
+ }
+
+ if (!lp_set_cmdline(torture->lp_ctx, "nt status support", "no")) {
+ printf("Could not set 'nt status support = yes'\n");
+ goto fail;
+ }
+
+ if (!torture_open_connection(&cli_dos, torture, 1)) {
+ goto fail;
+ }
+
+ if (!lp_set_cmdline(torture->lp_ctx, "nt status support",
+ nt_status_support ? "yes":"no")) {
+ printf("Could not reset 'nt status support = yes'");
+ goto fail;
+ }
+
+ smbcli_deltree(cli_nt->tree, dirname);
+
+ status = smbcli_mkdir(cli_nt->tree, dirname);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("smbcli_mkdir failed: %s\n", nt_errstr(status));
+ ret = false;
+ goto done;
+ }
+
+ status = smbcli_chkpath(cli_nt->tree, dirname);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = smbcli_chkpath(cli_nt->tree,
+ talloc_asprintf(mem_ctx, "%s\\bla", dirname));
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+
+ status = smbcli_chkpath(cli_dos->tree,
+ talloc_asprintf(mem_ctx, "%s\\bla", dirname));
+ CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS, ERRbadpath));
+
+ status = smbcli_chkpath(cli_nt->tree,
+ talloc_asprintf(mem_ctx, "%s\\bla\\blub",
+ dirname));
+ CHECK_STATUS(status, NT_STATUS_OBJECT_PATH_NOT_FOUND);
+ status = smbcli_chkpath(cli_dos->tree,
+ talloc_asprintf(mem_ctx, "%s\\bla\\blub",
+ dirname));
+ CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS, ERRbadpath));
+
+ if (!(fpath = talloc_asprintf(mem_ctx, "%s\\%s", dirname, fname))) {
+ goto fail;
+ }
+ fnum = smbcli_open(cli_nt->tree, fpath, O_RDWR | O_CREAT, DENY_NONE);
+ if (fnum == -1) {
+ d_printf("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) {
+ d_printf("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(status, NT_STATUS_NOT_A_DIRECTORY);
+ status = smbcli_chkpath(cli_dos->tree, fpath);
+ CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS, ERRbadpath));
+
+ status = smbcli_chkpath(cli_nt->tree, "..");
+ CHECK_STATUS(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD);
+ status = smbcli_chkpath(cli_dos->tree, "..");
+ CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS, ERRinvalidpath));
+
+ status = smbcli_chkpath(cli_nt->tree, ".");
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
+ status = smbcli_chkpath(cli_dos->tree, ".");
+ CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS, ERRbadpath));
+
+ status = smbcli_chkpath(cli_nt->tree, "\t");
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
+ status = smbcli_chkpath(cli_dos->tree, "\t");
+ CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS, ERRbadpath));
+
+ status = smbcli_chkpath(cli_nt->tree, "\t\\bla");
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
+ status = smbcli_chkpath(cli_dos->tree, "\t\\bla");
+ CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS, ERRbadpath));
+
+ status = smbcli_chkpath(cli_nt->tree, "<");
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
+ status = smbcli_chkpath(cli_dos->tree, "<");
+ CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS, ERRbadpath));
+
+ status = smbcli_chkpath(cli_nt->tree, "<\\bla");
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
+ status = smbcli_chkpath(cli_dos->tree, "<\\bla");
+ CHECK_STATUS(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(status, NT_STATUS_OK);
+ status = smbcli_getatr(cli_dos->tree, fpath, NULL, NULL, NULL);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = smbcli_getatr(cli_nt->tree, "..", NULL, NULL, NULL);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD);
+ status = smbcli_getatr(cli_dos->tree, "..", NULL, NULL, NULL);
+ CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS, ERRinvalidpath));
+
+ status = smbcli_getatr(cli_nt->tree, ".", NULL, NULL, NULL);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
+ status = smbcli_getatr(cli_dos->tree, ".", NULL, NULL, NULL);
+ CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS, ERRinvalidname));
+
+ status = smbcli_getatr(cli_nt->tree, "\t", NULL, NULL, NULL);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
+ status = smbcli_getatr(cli_dos->tree, "\t", NULL, NULL, NULL);
+ CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS, ERRinvalidname));
+
+ status = smbcli_getatr(cli_nt->tree, "\t\\bla", NULL, NULL, NULL);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
+ status = smbcli_getatr(cli_dos->tree, "\t\\bla", NULL, NULL, NULL);
+ CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS, ERRinvalidname));
+
+ status = smbcli_getatr(cli_nt->tree, "<", NULL, NULL, NULL);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
+ status = smbcli_getatr(cli_dos->tree, "<", NULL, NULL, NULL);
+ CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS, ERRinvalidname));
+
+ status = smbcli_getatr(cli_nt->tree, "<\\bla", NULL, NULL, NULL);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
+ status = smbcli_getatr(cli_dos->tree, "<\\bla", NULL, NULL, NULL);
+ CHECK_STATUS(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(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD);
+ status = raw_smbcli_open(cli_dos->tree, "..", O_RDONLY, DENY_NONE, NULL);
+ CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS, ERRinvalidpath));
+
+ status = raw_smbcli_open(cli_nt->tree, ".", O_RDONLY, DENY_NONE, NULL);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
+ status = raw_smbcli_open(cli_dos->tree, ".", O_RDONLY, DENY_NONE, NULL);
+ CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS, ERRinvalidname));
+
+ status = raw_smbcli_open(cli_nt->tree, "\t", O_RDONLY, DENY_NONE, NULL);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
+ status = raw_smbcli_open(cli_dos->tree, "\t", O_RDONLY, DENY_NONE, NULL);
+ CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS, ERRinvalidname));
+
+ status = raw_smbcli_open(cli_nt->tree, "\t\\bla", O_RDONLY, DENY_NONE, NULL);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
+ status = raw_smbcli_open(cli_dos->tree, "\t\\bla", O_RDONLY, DENY_NONE, NULL);
+ CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS, ERRinvalidname));
+
+ status = raw_smbcli_open(cli_nt->tree, "<", O_RDONLY, DENY_NONE, NULL);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
+ status = raw_smbcli_open(cli_dos->tree, "<", O_RDONLY, DENY_NONE, NULL);
+ CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS, ERRinvalidname));
+
+ status = raw_smbcli_open(cli_nt->tree, "<\\bla", O_RDONLY, DENY_NONE, NULL);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
+ status = raw_smbcli_open(cli_dos->tree, "<\\bla", O_RDONLY, DENY_NONE, NULL);
+ CHECK_STATUS(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(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(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(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(status, NT_STATUS_DOS(ERRDOS,ERRfilexists));
+ }
+
+ status = raw_smbcli_ntcreate(cli_nt->tree, fpath, NULL);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_COLLISION);
+ status = raw_smbcli_ntcreate(cli_dos->tree, fpath, NULL);
+ CHECK_STATUS(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(status, NT_STATUS_OBJECT_NAME_COLLISION);
+ status = smb_raw_rename(cli_dos->tree, &io);
+ CHECK_STATUS(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(status, NT_STATUS_OBJECT_NAME_COLLISION);
+ status = smb_raw_rename(cli_dos->tree, &io);
+ CHECK_STATUS(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;
+ NTSTATUS status;
+ const char *dirname = "insensitive";
+ const char *ucase_dirname = "InSeNsItIvE";
+ const char *fname = "foo";
+ char *fpath;
+ int fnum;
+ int counter = 0;
+ bool ret = true;
+
+ if (!(mem_ctx = talloc_init("torture_samba3_caseinsensitive"))) {
+ d_printf("talloc_init failed\n");
+ return false;
+ }
+
+ if (!torture_open_connection(&cli, torture, 0)) {
+ goto done;
+ }
+
+ smbcli_deltree(cli->tree, dirname);
+
+ status = smbcli_mkdir(cli->tree, dirname);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("smbcli_mkdir failed: %s\n", nt_errstr(status));
+ goto done;
+ }
+
+ 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) {
+ d_printf("Could not create file %s: %s\n", 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 {
+ d_fprintf(stderr, "expected 3 entries, got %d\n", counter);
+ ret = false;
+ }
+
+ done:
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+/*
+ * Check that Samba3 correctly deals with conflicting posix byte range locks
+ * on an underlying file
+ *
+ * Note: This test depends on "posix locking = yes".
+ * Note: To run this test, use "--option=torture:localdir=<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;
+
+ if (!torture_open_connection(&cli, tctx, 0)) {
+ ret = false;
+ goto done;
+ }
+
+ smbcli_deltree(cli->tree, dirname);
+
+ status = smbcli_mkdir(cli->tree, dirname);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_warning(tctx, "smbcli_mkdir failed: %s\n",
+ nt_errstr(status));
+ ret = false;
+ goto done;
+ }
+
+ 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(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+ 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;
+ }
+
+ /*
+ * Ship the async timed request to the server
+ */
+ event_loop_once(req->transport->socket->event.ctx);
+ msleep(500);
+
+ close(fd);
+
+ status = smbcli_request_simple_recv(req);
+
+ CHECK_STATUS(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;
+ NTSTATUS status;
+ uint16_t dnum;
+ union smb_open io;
+ const char *fname = "testfile";
+ bool ret = false;
+
+ if (!torture_open_connection(&cli, tctx, 0)) {
+ ret = false;
+ goto done;
+ }
+
+ 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 = 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.fname = "\\";
+ status = smb_raw_open(cli->tree, tctx, &io);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("smb_open on the directory failed: %s\n",
+ nt_errstr(status));
+ ret = false;
+ goto done;
+ }
+ dnum = io.ntcreatex.out.file.fnum;
+
+ io.ntcreatex.in.flags =
+ NTCREATEX_FLAGS_REQUEST_OPLOCK
+ | NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
+ io.ntcreatex.in.root_fid = 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.fname = fname;
+
+ status = smb_raw_open(cli->tree, tctx, &io);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("smb_open on the file %s failed: %s\n",
+ fname, nt_errstr(status));
+ ret = false;
+ goto done;
+ }
+
+ 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_oplock_logoff(struct torture_context *tctx)
+{
+ struct smbcli_state *cli;
+ NTSTATUS status;
+ uint16_t fnum1;
+ union smb_open io;
+ const char *fname = "testfile";
+ bool ret = false;
+ struct smbcli_request *req;
+ struct smb_echo echo_req;
+
+ if (!torture_open_connection(&cli, tctx, 0)) {
+ ret = false;
+ goto done;
+ }
+
+ 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 = 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.fname = "testfile";
+ status = smb_raw_open(cli->tree, tctx, &io);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("first smb_open failed: %s\n", nt_errstr(status));
+ ret = false;
+ goto done;
+ }
+ fnum1 = io.ntcreatex.out.file.fnum;
+
+ /*
+ * Create a conflicting open, causing the one-second delay
+ */
+
+ req = smb_raw_open_send(cli->tree, &io);
+ if (req == NULL) {
+ d_printf("smb_raw_open_send failed\n");
+ ret = false;
+ goto done;
+ }
+
+ /*
+ * 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.
+ */
+
+ status = smb_raw_ulogoff(cli->session);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("ulogoff failed: %s\n", nt_errstr(status));
+ ret = false;
+ goto done;
+ }
+
+ echo_req.in.repeat_count = 1;
+ echo_req.in.size = 1;
+ echo_req.in.data = (uint8_t *)"";
+
+ status = smb_raw_echo(cli->session->transport, &echo_req);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("smb_raw_echo returned %s\n",
+ nt_errstr(status));
+ ret = false;
+ goto done;
+ }
+
+ ret = true;
+ done:
+ return ret;
+}
diff --git a/source4/torture/raw/search.c b/source4/torture/raw/search.c
new file mode 100644
index 0000000000..8a3168dcc4
--- /dev/null
+++ b/source4/torture/raw/search.c
@@ -0,0 +1,1391 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#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"
+
+
+#define BASEDIR "\\testsearch"
+
+/*
+ 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[] = {
+ {"FFIRST",
+ RAW_SEARCH_FFIRST, RAW_SEARCH_DATA_SEARCH,
+ offsetof(union smb_search_data, search.name),
+ -1,
+ },
+ {"FUNIQUE",
+ RAW_SEARCH_FUNIQUE, RAW_SEARCH_DATA_SEARCH,
+ offsetof(union smb_search_data, search.name),
+ -1,
+ },
+ {"SEARCH",
+ RAW_SEARCH_SEARCH, RAW_SEARCH_DATA_SEARCH,
+ offsetof(union smb_search_data, search.name),
+ -1,
+ },
+ {"STANDARD",
+ RAW_SEARCH_TRANS2, RAW_SEARCH_DATA_STANDARD,
+ offsetof(union smb_search_data, standard.name.s),
+ offsetof(union smb_search_data, standard.resume_key),
+ },
+ {"EA_SIZE",
+ RAW_SEARCH_TRANS2, RAW_SEARCH_DATA_EA_SIZE,
+ offsetof(union smb_search_data, ea_size.name.s),
+ offsetof(union smb_search_data, ea_size.resume_key),
+ },
+ {"DIRECTORY_INFO",
+ RAW_SEARCH_TRANS2, RAW_SEARCH_DATA_DIRECTORY_INFO,
+ offsetof(union smb_search_data, directory_info.name.s),
+ offsetof(union smb_search_data, directory_info.file_index),
+ },
+ {"FULL_DIRECTORY_INFO",
+ RAW_SEARCH_TRANS2, RAW_SEARCH_DATA_FULL_DIRECTORY_INFO,
+ offsetof(union smb_search_data, full_directory_info.name.s),
+ offsetof(union smb_search_data, full_directory_info.file_index),
+ },
+ {"NAME_INFO",
+ RAW_SEARCH_TRANS2, RAW_SEARCH_DATA_NAME_INFO,
+ offsetof(union smb_search_data, name_info.name.s),
+ offsetof(union smb_search_data, name_info.file_index),
+ },
+ {"BOTH_DIRECTORY_INFO",
+ RAW_SEARCH_TRANS2, RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO,
+ offsetof(union smb_search_data, both_directory_info.name.s),
+ offsetof(union smb_search_data, both_directory_info.file_index),
+ },
+ {"ID_FULL_DIRECTORY_INFO",
+ RAW_SEARCH_TRANS2, RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO,
+ offsetof(union smb_search_data, id_full_directory_info.name.s),
+ offsetof(union smb_search_data, id_full_directory_info.file_index),
+ },
+ {"ID_BOTH_DIRECTORY_INFO",
+ RAW_SEARCH_TRANS2, RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO,
+ offsetof(union smb_search_data, id_both_directory_info.name.s),
+ offsetof(union smb_search_data, id_both_directory_info.file_index),
+ },
+ {"UNIX_INFO",
+ RAW_SEARCH_TRANS2, RAW_SEARCH_DATA_UNIX_INFO,
+ offsetof(union smb_search_data, unix_info.name),
+ offsetof(union smb_search_data, unix_info.file_index),
+ 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;i<ARRAY_SIZE(levels);i++) {
+ if (level == levels[i].level &&
+ data_level == levels[i].data_level) {
+ return levels[i].name;
+ }
+ }
+ return NULL;
+}
+
+/*
+ extract the name from a smb_data structure and level
+*/
+static const char *extract_name(void *data, enum smb_search_level level,
+ enum smb_search_data_level data_level)
+{
+ int i;
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ if (level == levels[i].level &&
+ data_level == levels[i].data_level) {
+ return *(const char **)(levels[i].name_offset + (char *)data);
+ }
+ }
+ return NULL;
+}
+
+/*
+ extract the name from a smb_data structure and level
+*/
+static uint32_t extract_resume_key(void *data, enum smb_search_level level,
+ enum smb_search_data_level data_level)
+{
+ int i;
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ if (level == levels[i].level &&
+ data_level == levels[i].data_level) {
+ return *(uint32_t *)(levels[i].resume_key_offset + (char *)data);
+ }
+ }
+ return 0;
+}
+
+/* find a level in the table by name */
+static union smb_search_data *find(const char *name)
+{
+ int i;
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ if (NT_STATUS_IS_OK(levels[i].status) &&
+ strcmp(levels[i].name, name) == 0) {
+ return &levels[i].data;
+ }
+ }
+ return NULL;
+}
+
+/*
+ basic testing of all RAW_SEARCH_* calls using a single file
+*/
+static bool test_one_file(struct torture_context *tctx,
+ struct smbcli_state *cli)
+{
+ 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;
+ union smb_search_data *s;
+
+ fnum = create_complex_file(cli, tctx, fname);
+ if (fnum == -1) {
+ printf("ERROR: open of %s failed (%s)\n", fname, smbcli_errstr(cli->tree));
+ ret = false;
+ goto done;
+ }
+
+ /* call all the levels */
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ NTSTATUS expected_status;
+ uint32_t cap = cli->transport->negotiate.capabilities;
+
+ torture_comment(tctx, "testing %s\n", levels[i].name);
+
+ levels[i].status = torture_single_search(cli, 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) {
+ 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)) {
+ printf("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, 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)) {
+ printf("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);
+ torture_assert_ntstatus_ok(tctx, status, "RAW_FILEINFO_ALL_INFO failed");
+
+ 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);
+ torture_assert_ntstatus_ok(tctx, status, "RAW_FILEINFO_ALT_NAME_INFO failed");
+
+ internal_info.generic.level = RAW_FILEINFO_INTERNAL_INFORMATION;
+ internal_info.generic.in.file.path = fname;
+ status = smb_raw_pathinfo(cli->tree, tctx, &internal_info);
+ torture_assert_ntstatus_ok(tctx, status, "RAW_FILEINFO_INTERNAL_INFORMATION failed");
+
+ name_info.generic.level = RAW_FILEINFO_NAME_INFO;
+ name_info.generic.in.file.path = fname;
+ status = smb_raw_pathinfo(cli->tree, tctx, &name_info);
+ torture_assert_ntstatus_ok(tctx, status, "RAW_FILEINFO_NAME_INFO failed");
+
+#define CHECK_VAL(name, sname1, field1, v, sname2, field2) do { \
+ s = find(name); \
+ if (s) { \
+ if ((s->sname1.field1) != (v.sname2.out.field2)) { \
+ printf("(%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))) { \
+ printf("(%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) { \
+ printf("(%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)) { \
+ printf("(%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)) { \
+ printf("(%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)) { \
+ printf("(%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)) { \
+ printf("(%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);
+
+ 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+1, 0);
+ CHECK_NAME("EA_SIZE", ea_size, name, fname+1, 0);
+ CHECK_NAME("DIRECTORY_INFO", directory_info, name, fname+1, STR_TERMINATE_ASCII);
+ CHECK_NAME("FULL_DIRECTORY_INFO", full_directory_info, name, fname+1, STR_TERMINATE_ASCII);
+ CHECK_NAME("NAME_INFO", name_info, name, fname+1, STR_TERMINATE_ASCII);
+ CHECK_NAME("BOTH_DIRECTORY_INFO", both_directory_info, name, fname+1, STR_TERMINATE_ASCII);
+ CHECK_NAME("ID_FULL_DIRECTORY_INFO", id_full_directory_info, name, fname+1, STR_TERMINATE_ASCII);
+ CHECK_NAME("ID_BOTH_DIRECTORY_INFO", id_both_directory_info, name, fname+1, STR_TERMINATE_ASCII);
+ CHECK_UNIX_NAME("UNIX_INFO", unix_info, name, fname+1, STR_TERMINATE_ASCII);
+
+ 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}
+ };
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ torture_comment(tctx, "Testing with %d files\n", num_files);
+
+ for (i=0;i<num_files;i++) {
+ fname = talloc_asprintf(cli, BASEDIR "\\t%03d-%d.txt", i, i);
+ fnum = smbcli_open(cli->tree, 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;t<ARRAY_SIZE(search_types);t++) {
+ ZERO_STRUCT(result);
+
+ if ((search_types[t].cont_type == CONT_RESUME_KEY) &&
+ (search_types[t].data_level != RAW_SEARCH_DATA_SEARCH) &&
+ torture_setting_bool(tctx, "samba3", false)) {
+ torture_comment(tctx,
+ "SKIP: Continue %s via %s\n",
+ search_types[t].name, search_types[t].cont_name);
+ continue;
+ }
+
+ result.tctx = talloc_new(tctx);
+
+ torture_comment(tctx,
+ "Continue %s via %s\n", search_types[t].name, search_types[t].cont_name);
+
+ status = multiple_search(cli, tctx, BASEDIR "\\*.*",
+ search_types[t].data_level,
+ search_types[t].cont_type,
+ &result);
+
+ torture_assert_ntstatus_ok(tctx, status, "search failed");
+ CHECK_VALUE(result.count, num_files);
+
+ compare_data_level = search_types[t].data_level;
+
+ qsort(result.list, result.count, sizeof(result.list[0]),
+ QSORT_CAST search_compare);
+
+ for (i=0;i<result.count;i++) {
+ const char *s;
+ enum smb_search_level level;
+ if (compare_data_level == RAW_SEARCH_DATA_SEARCH) {
+ level = RAW_SEARCH_SEARCH;
+ } else {
+ level = RAW_SEARCH_TRANS2;
+ }
+ s = extract_name(&result.list[i], level, compare_data_level);
+ fname = talloc_asprintf(cli, "t%03d-%d.txt", i, i);
+ torture_assert_str_equal(tctx, fname, s, "Incorrect name");
+ talloc_free(fname);
+ }
+ talloc_free(result.tctx);
+ }
+
+ smb_raw_exit(cli->session);
+ 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;i<result->count;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;
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ 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;i<result.count;i++) {
+ printf("%s %s (0x%x)\n",
+ result.list[i].both_directory_info.name.s,
+ attrib_string(tctx, result.list[i].both_directory_info.attrib),
+ result.list[i].both_directory_info.attrib);
+ }
+ }
+
+done:
+ smb_raw_exit(cli->session);
+ 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;
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ printf("Creating %d files\n", num_files);
+
+ for (i=0;i<num_files;i++) {
+ fname = talloc_asprintf(cli, BASEDIR "\\%s.txt", generate_random_str_list(tctx, 10, "abcdefgh"));
+ 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);
+ }
+
+
+ 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<num_files-1;i++) {
+ const char *name1, *name2;
+ name1 = result.list[i].both_directory_info.name.s;
+ name2 = result.list[i+1].both_directory_info.name.s;
+ if (strcasecmp_m(name1, name2) > 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_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ printf("Creating %d dirs\n", num_dirs);
+
+ for (i=0;i<num_dirs;i++) {
+ dname = talloc_asprintf(cli, BASEDIR "\\d%d", i);
+ status = smbcli_mkdir(cli->tree, 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;i<num_dirs;i++) {
+ union smb_search_first io;
+ io.search_first.level = RAW_SEARCH_SEARCH;
+ io.search_first.data_level = RAW_SEARCH_DATA_SEARCH;
+ io.search_first.in.max_count = 1;
+ io.search_first.in.search_attrib = 0;
+ io.search_first.in.pattern = talloc_asprintf(tctx, BASEDIR "\\d%d\\*.txt", i);
+ fname = talloc_asprintf(tctx, "f%d-", i);
+
+ io.search_first.out.count = 0;
+
+ status = smb_raw_search_first(cli->tree, 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;i<num_dirs;i++) {
+ union smb_search_next io2;
+
+ io2.search_next.level = RAW_SEARCH_SEARCH;
+ io2.search_next.data_level = RAW_SEARCH_DATA_SEARCH;
+ io2.search_next.in.max_count = 1;
+ io2.search_next.in.search_attrib = 0;
+ io2.search_next.in.id = file[i].search.id;
+ fname = talloc_asprintf(tctx, "f%d-", i);
+
+ io2.search_next.out.count = 0;
+
+ status = smb_raw_search_next(cli->tree, 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;i<num_dirs;i++) {
+ union smb_search_next io2;
+
+ io2.search_next.level = RAW_SEARCH_SEARCH;
+ io2.search_next.data_level = RAW_SEARCH_DATA_SEARCH;
+ io2.search_next.in.max_count = 1;
+ io2.search_next.in.search_attrib = 0;
+ io2.search_next.in.id = file[i].search.id;
+ fname = talloc_asprintf(tctx, "f%d-", i);
+ io2.search_next.out.count = 0;
+
+ status = smb_raw_search_next(cli->tree, 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 (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_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ printf("Testing OS/2 style delete on %d files\n", num_files);
+
+ for (i=0;i<num_files;i++) {
+ fname = talloc_asprintf(cli, BASEDIR "\\file%u.txt", 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);
+ }
+
+
+ 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;i<MIN(result.count, delete_count);i++) {
+ fname = talloc_asprintf(cli, BASEDIR "\\%s", result.list[i].ea_size.name.s);
+ status = smbcli_unlink(cli->tree, 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;i<MIN(result.count, delete_count);i++) {
+ fname = talloc_asprintf(cli, BASEDIR "\\%s", result.list[i].ea_size.name.s);
+ status = smbcli_unlink(cli->tree, 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;
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ printf("Testing RAW_SEARCH_EA_LIST level\n");
+
+ 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 */
+ qsort(result.list, result.count, sizeof(result.list[0]),
+ (comparison_fn_t)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;
+}
+
+
+
+/*
+ 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_1smb_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);
+
+ return suite;
+}
diff --git a/source4/torture/raw/seek.c b/source4/torture/raw/seek.c
new file mode 100644
index 0000000000..3ba022feef
--- /dev/null
+++ b/source4/torture/raw/seek.c
@@ -0,0 +1,243 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "system/filesys.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/libcli.h"
+#include "torture/util.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/setfileinfo.c b/source4/torture/raw/setfileinfo.c
new file mode 100644
index 0000000000..10eaa6710d
--- /dev/null
+++ b/source4/torture/raw/setfileinfo.c
@@ -0,0 +1,703 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#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"
+
+#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.
+*/
+bool torture_raw_sfileinfo(struct torture_context *torture,
+ struct smbcli_state *cli)
+{
+ bool ret = true;
+ int fnum = -1;
+ char *fnum_fname;
+ char *fnum_fname_new;
+ 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;
+
+ asprintf(&path_fname, BASEDIR "\\fname_test_%d.txt", n);
+ asprintf(&path_fname_new, BASEDIR "\\fname_test_new_%d.txt", n);
+ asprintf(&fnum_fname, BASEDIR "\\fnum_test_%d.txt", n);
+ asprintf(&fnum_fname_new, BASEDIR "\\fnum_test_new_%d.txt", n);
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+#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, 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_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, 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_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, \
+ (uint_t)value, (uint_t)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, \
+ (uint_t)value, \
+ (uint_t)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);
+
+ 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
+ */
+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;
+
+ asprintf(&path_fname, BASEDIR "\\fname_test_%d.txt", n);
+ asprintf(&path_fname_new, BASEDIR "\\fname_test_new_%d.txt", n);
+ asprintf(&fnum_fname, BASEDIR "\\fnum_test_%d.txt", n);
+ asprintf(&fnum_fname_new, BASEDIR "\\fnum_test_new_%d.txt", n);
+ asprintf(&path_dname, BASEDIR "\\dname_test_%d", n);
+ asprintf(&path_dname_new, BASEDIR "\\dname_test_new_%d", n);
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ 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
+*/
+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;
+}
diff --git a/source4/torture/raw/streams.c b/source4/torture/raw/streams.c
new file mode 100644
index 0000000000..d0d21ccc06
--- /dev/null
+++ b/source4/torture/raw/streams.c
@@ -0,0 +1,1694 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/locale.h"
+#include "torture/torture.h"
+#include "libcli/raw/libcliraw.h"
+#include "system/filesys.h"
+#include "libcli/libcli.h"
+#include "torture/util.h"
+
+#define BASEDIR "\\teststreams"
+
+#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 CHECK_VALUE(v, correct) do { \
+ if ((v) != (correct)) { \
+ printf("(%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)) { \
+ printf("(%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) { \
+ printf("(%s) Incorrect value %s='%s' - should be '%s'\n", \
+ __location__, #v, (v)?(v):"NULL", \
+ (correct)?(correct):"NULL"); \
+ ret = false; \
+ }} 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(const void *v1, const void *v2)
+{
+ char * const *s1 = v1;
+ char * const *s2 = v2;
+ return strcmp(*s1, *s2);
+}
+
+static int qsort_stream(const void *v1, const void *v2)
+{
+ const struct stream_struct * s1 = v1;
+ const struct stream_struct * s2 = v2;
+ return strcmp(s1->stream_name.s, s2->stream_name.s);
+}
+
+static bool check_stream_list(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;
+
+ finfo.generic.level = RAW_FILEINFO_STREAM_INFO;
+ finfo.generic.in.file.path = fname;
+
+ status = smb_raw_pathinfo(cli->tree, tmp_ctx, &finfo);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "(%s) smb_raw_pathinfo failed: %s\n",
+ __location__, nt_errstr(status));
+ goto fail;
+ }
+
+ if (finfo.stream_info.out.num_streams != num_exp) {
+ d_fprintf(stderr, "(%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;
+ }
+
+ qsort(exp_sort, num_exp, sizeof(*exp_sort), 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;
+ }
+
+ qsort(stream_sort, finfo.stream_info.out.num_streams,
+ sizeof(*stream_sort), qsort_stream);
+
+ for (i=0; i<num_exp; i++) {
+ if (strcmp(exp_sort[i], stream_sort[i].stream_name.s) != 0) {
+ d_fprintf(stderr, "(%s) expected stream name %s, got "
+ "%s\n", __location__, exp_sort[i],
+ stream_sort[i].stream_name.s);
+ goto fail;
+ }
+ }
+
+ ret = true;
+ fail:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+/*
+ test bahavior of streams on directories
+*/
+static bool test_stream_dir(struct torture_context *tctx,
+ struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
+{
+ NTSTATUS status;
+ union smb_open io;
+ const char *fname = BASEDIR "\\stream.txt";
+ const char *sname1;
+ bool ret = true;
+ const char *basedir_data;
+
+ basedir_data = talloc_asprintf(mem_ctx, "%s::$DATA", BASEDIR);
+ sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
+
+ printf("(%s) opening non-existant directory stream\n", __location__);
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 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_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, mem_ctx, &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 = 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, mem_ctx, &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 = 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, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_FILE_IS_A_DIRECTORY);
+
+ printf("(%s) list the streams on the basedir\n", __location__);
+ ret &= check_stream_list(cli, BASEDIR, 0, NULL);
+done:
+ return ret;
+}
+
+/*
+ test basic behavior of streams on directories
+*/
+static bool test_stream_io(struct torture_context *tctx,
+ struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
+{
+ 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" };
+
+ sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
+ sname2 = talloc_asprintf(mem_ctx, "%s:%s:$DaTa", fname, "Second Stream");
+
+ printf("(%s) creating a stream on a non-existant file\n", __location__);
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 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, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+
+ ret &= check_stream(cli, __location__, mem_ctx, 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, mem_ctx, &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__, mem_ctx, fname, "Stream One", "test data");
+
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
+ io.ntcreatex.in.fname = sname1;
+ status = smb_raw_open(cli->tree, mem_ctx, &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__, mem_ctx, 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, mem_ctx, &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__, mem_ctx, fname, "Stream One", "test MORE DATA ");
+ ret &= check_stream(cli, __location__, mem_ctx, fname, "Stream One:$DATA", "test MORE DATA ");
+ ret &= check_stream(cli, __location__, mem_ctx, fname, "Stream One:", NULL);
+ ret &= check_stream(cli, __location__, mem_ctx, fname, "Second Stream", "SECOND STREAM");
+ if (!torture_setting_bool(tctx, "samba4", false)) {
+ ret &= check_stream(cli, __location__, mem_ctx, fname,
+ "SECOND STREAM:$DATA", "SECOND STREAM");
+ }
+ ret &= check_stream(cli, __location__, mem_ctx, fname, "Second Stream:$DATA", "SECOND STREAM");
+ ret &= check_stream(cli, __location__, mem_ctx, fname, "Second Stream:", NULL);
+ ret &= check_stream(cli, __location__, mem_ctx, fname, "Second Stream:$FOO", NULL);
+
+ check_stream_list(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(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_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
+
+ status = smb_raw_open(cli->tree, mem_ctx, &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(cli, fname, 1, one);
+
+ if (!torture_setting_bool(tctx, "samba4", false)) {
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+ io.ntcreatex.in.fname = sname1;
+ status = smb_raw_open(cli->tree, mem_ctx, &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, mem_ctx, &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);
+ return ret;
+}
+
+/*
+ test stream sharemodes
+*/
+static bool test_stream_sharemodes(struct torture_context *tctx,
+ struct smbcli_state *cli,
+ TALLOC_CTX *mem_ctx)
+{
+ 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;
+
+ sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
+ sname2 = talloc_asprintf(mem_ctx, "%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 = 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, mem_ctx, &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, mem_ctx, &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, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
+
+ io.ntcreatex.in.fname = sname2;
+ status = smb_raw_open(cli->tree, mem_ctx, &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);
+ 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 ending.
+ *
+ * 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, TALLOC_CTX *mem_ctx)
+{
+ 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;
+
+ sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
+
+ printf("(%s) opening non-existant file stream\n", __location__);
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 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, mem_ctx, &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, mem_ctx, &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, mem_ctx, &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, mem_ctx, &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, mem_ctx, &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, mem_ctx, &finfo);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ /* w2k and w2k3 return 0 and w2k8 returns 1
+ CHECK_VALUE(finfo.all_info.out.delete_pending, 0);
+ */
+
+ 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, mem_ctx, &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, mem_ctx, &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, mem_ctx, &finfo);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ smbcli_close(cli->tree, fnum);
+
+ status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo);
+ CHECK_STATUS(status, NT_STATUS_OK);
+done:
+ smbcli_close(cli->tree, fnum);
+ smbcli_unlink(cli->tree, fname);
+ return ret;
+}
+
+/*
+ test stream names
+*/
+static bool test_stream_names(struct torture_context *tctx,
+ struct smbcli_state *cli,
+ TALLOC_CTX *mem_ctx)
+{
+ NTSTATUS status;
+ union smb_open io;
+ 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, *snamer2;
+ 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"
+ };
+
+ sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "\x05Stream\n One");
+ sname1b = talloc_asprintf(mem_ctx, "%s:", sname1);
+ sname1c = talloc_asprintf(mem_ctx, "%s:$FOO", sname1);
+ sname1d = talloc_asprintf(mem_ctx, "%s:?D*a", sname1);
+ sname2 = talloc_asprintf(mem_ctx, "%s:%s:$DaTa", fname, "MStream Two");
+ snamew = talloc_asprintf(mem_ctx, "%s:%s:$DATA", fname, "?Stream*");
+ snamew2 = talloc_asprintf(mem_ctx, "%s\\stream*:%s:$DATA", BASEDIR, "?Stream*");
+ snamer1 = talloc_asprintf(mem_ctx, "%s:%s:$DATA", fname, "BeforeRename");
+ snamer2 = talloc_asprintf(mem_ctx, "%s:%s:$DATA", fname, "AfterRename");
+
+ printf("(%s) testing stream names\n", __location__);
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 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, mem_ctx, &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, mem_ctx, &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, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
+
+ io.ntcreatex.in.fname = sname1b;
+ status = smb_raw_open(cli->tree, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
+
+ io.ntcreatex.in.fname = sname1c;
+ status = smb_raw_open(cli->tree, mem_ctx, &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, mem_ctx, &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, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
+
+ io.ntcreatex.in.fname = snamew;
+ status = smb_raw_open(cli->tree, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum3 = io.ntcreatex.out.file.fnum;
+
+ io.ntcreatex.in.fname = snamew2;
+ status = smb_raw_open(cli->tree, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
+
+ ret &= check_stream_list(cli, fname, 4, four);
+
+ smbcli_close(cli->tree, fnum1);
+ smbcli_close(cli->tree, fnum2);
+ smbcli_close(cli->tree, fnum3);
+
+ if (torture_setting_bool(tctx, "samba4", true)) {
+ goto done;
+ }
+
+ finfo.generic.level = RAW_FILEINFO_ALL_INFO;
+ finfo.generic.in.file.path = fname;
+ status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ ret &= check_stream_list(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, mem_ctx, &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, mem_ctx, &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, mem_ctx, &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, mem_ctx, &stinfo);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ if (!torture_setting_bool(tctx, "samba3", false)) {
+ CHECK_STR(rpath, stinfo.name_info.out.fname.s);
+ }
+
+ 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, mem_ctx, &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(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, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum1 = io.ntcreatex.out.file.fnum;
+
+ ret &= check_stream_list(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(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(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);
+ CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
+
+ ret &= check_stream_list(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);
+ return ret;
+}
+
+/*
+ test stream names
+*/
+static bool test_stream_names2(struct torture_context *tctx,
+ struct smbcli_state *cli,
+ TALLOC_CTX *mem_ctx)
+{
+ NTSTATUS status;
+ union smb_open io;
+ const char *fname = BASEDIR "\\stream_names2.txt";
+ bool ret = true;
+ int fnum1 = -1;
+ uint8_t i;
+
+ printf("(%s) testing stream names\n", __location__);
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 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, mem_ctx, &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, mem_ctx, &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);
+ return ret;
+}
+
+#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, 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,
+ TALLOC_CTX *mem_ctx)
+{
+ 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;
+ bool check_fnum;
+ const char *call_name;
+
+ sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One");
+ sname2 = talloc_asprintf(mem_ctx, "%s:%s:$DaTa", fname, "Second Stream");
+
+ printf("(%s) testing stream renames\n", __location__);
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 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, mem_ctx, &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, mem_ctx, &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.open_disposition = NTCREATEX_DISP_OPEN_IF;
+ status = smb_raw_open(cli->tree, mem_ctx, &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);
+ return ret;
+}
+
+static bool test_stream_rename2(struct torture_context *tctx,
+ struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
+{
+ 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;
+
+ sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname1, "Stream One");
+ sname2 = talloc_asprintf(mem_ctx, "%s:%s", fname1, "Stream Two");
+
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 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, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
+
+ /*
+ * Check raw rename with <base>:<stream>.
+ */
+ printf("(%s) Checking NTRENAME of a stream using <base>:<stream>\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 :<stream>.
+ */
+ printf("(%s) Checking NTRENAME to default stream using :<stream>\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 :<stream>.
+ */
+ printf("(%s) Checking NTRENAME of a stream using :<stream>\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, mem_ctx, &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, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+
+ /*
+ * Check trans2 rename of a stream using :<stream>.
+ */
+ printf("(%s) Checking trans2 rename of a stream using :<stream>\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 :<stream>.
+ */
+ printf("(%s) Checking trans2 rename of an overwriting stream using "
+ ":<stream>\n", __location__);
+
+ /* Create second stream. */
+ io.ntcreatex.in.fname = sname2;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+ status = smb_raw_open(cli->tree, mem_ctx, &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, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+
+ /*
+ * Check trans2 rename of a stream using <base>:<stream>.
+ */
+ printf("(%s) Checking trans2 rename of a stream using "
+ "<base>:<stream>\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 :<stream>.
+ */
+ printf("(%s) Checking trans2 rename to defaualt stream using "
+ ":<stream>\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);
+ return ret;
+}
+
+static bool create_file_with_stream(struct torture_context *tctx,
+ struct smbcli_state *cli,
+ 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 */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 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, mem_ctx, &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,
+ TALLOC_CTX *mem_ctx)
+{
+ 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 = true;
+ int fnum = -1;
+
+ 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, cli, mem_ctx, fname,
+ fname_stream)) {
+ goto done;
+ }
+
+ /* Open the base file with OPEN */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 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, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
+ if (!check_stream_list(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, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
+ if (!check_stream_list(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, mem_ctx, fname,
+ fname_stream)) {
+ goto done;
+ }
+
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OVERWRITE_IF;
+ status = smb_raw_open(cli->tree, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
+ if (!check_stream_list(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, mem_ctx, fname,
+ fname_stream)) {
+ goto done;
+ }
+
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_SUPERSEDE;
+ status = smb_raw_open(cli->tree, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
+ if (!check_stream_list(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, mem_ctx, fname,
+ 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, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ smbcli_close(cli->tree, io.ntcreatex.out.file.fnum);
+ if (!check_stream_list(cli, fname, 1, &default_stream_name)) {
+ 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, mem_ctx, fname,
+ 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;
+ 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, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ smbcli_close(cli->tree, io.openx.out.file.fnum);
+ if (!check_stream_list(cli, fname, 1, &default_stream_name)) {
+ goto done;
+ }
+
+ done:
+ smbcli_close(cli->tree, fnum);
+ smbcli_unlink(cli->tree, fname);
+ return ret;
+}
+
+/* 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,
+ TALLOC_CTX *mem_ctx)
+{
+#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;
+
+ lstream_name = talloc_array(mem_ctx, 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';
+
+ printf("(%s) Creating a file with a lot of streams\n", __location__);
+ for (i = 0; i < 10000; i++) {
+ fname_stream = talloc_asprintf(mem_ctx, "%s:%s%d", fname,
+ lstream_name, i);
+ ret = create_file_with_stream(tctx, cli, mem_ctx, fname,
+ 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, mem_ctx, &finfo);
+ CHECK_STATUS(status, STATUS_BUFFER_OVERFLOW);
+
+ done:
+ smbcli_unlink(cli->tree, fname);
+ return ret;
+}
+
+/* Test the effect of setting attributes on a stream. */
+static bool test_stream_attributes(struct torture_context *tctx,
+ struct smbcli_state *cli,
+ TALLOC_CTX *mem_ctx)
+{
+ 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;
+
+ printf ("(%s) testing attribute setting on stream\n", __location__);
+
+ 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, cli, mem_ctx, fname,
+ 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, mem_ctx, &finfo);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ if (finfo.basic_info.out.attrib != FILE_ATTRIBUTE_ARCHIVE) {
+ printf("(%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;
+ }
+
+ /* Now open the stream name. */
+
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 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, mem_ctx, &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);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) {
+ printf("(%s) %s - %s (should be %s)\n", __location__, "SETATTR",
+ nt_errstr(status), nt_errstr(NT_STATUS_OK));
+ ret = false;
+ goto done;
+ }
+
+ 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, mem_ctx, &finfo);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("(%s) %s pathinfo - %s\n", __location__, "SETATTRE", nt_errstr(status));
+ ret = false;
+ goto done;
+ }
+
+ if (finfo.all_info.out.attrib != FILE_ATTRIBUTE_READONLY) {
+ printf("(%s) attrib incorrect. Was 0x%x, should be 0x%x\n",
+ __location__,
+ (unsigned int)finfo.all_info.out.attrib,
+ (unsigned int)FILE_ATTRIBUTE_READONLY);
+ ret = false;
+ goto done;
+ }
+
+ if (nt_time_to_unix(finfo.all_info.out.write_time) != basetime) {
+ printf("(%s) time incorrect.\n",
+ __location__);
+ ret = false;
+ goto done;
+ }
+
+ done:
+
+ if (fnum != -1) {
+ smbcli_close(cli->tree, fnum);
+ }
+ smbcli_unlink(cli->tree, fname);
+ return ret;
+}
+
+/*
+ basic testing of streams calls
+*/
+bool torture_raw_streams(struct torture_context *torture,
+ struct smbcli_state *cli)
+{
+ bool ret = true;
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ ret &= test_stream_dir(torture, cli, torture);
+ smb_raw_exit(cli->session);
+ ret &= test_stream_io(torture, cli, torture);
+ smb_raw_exit(cli->session);
+ ret &= test_stream_sharemodes(torture, cli, torture);
+ smb_raw_exit(cli->session);
+ if (!torture_setting_bool(torture, "samba4", false)) {
+ ret &= test_stream_delete(torture, cli, torture);
+ }
+ ret &= test_stream_names(torture, cli, torture);
+ smb_raw_exit(cli->session);
+ ret &= test_stream_names2(torture, cli, torture);
+ smb_raw_exit(cli->session);
+ ret &= test_stream_rename(torture, cli, torture);
+ smb_raw_exit(cli->session);
+ ret &= test_stream_rename2(torture, cli, torture);
+ smb_raw_exit(cli->session);
+ ret &= test_stream_create_disposition(torture, cli, torture);
+ smb_raw_exit(cli->session);
+
+ ret &= test_stream_attributes(torture, cli, torture);
+ smb_raw_exit(cli->session);
+
+ /* ret &= test_stream_large_streaminfo(torture, cli, torture); */
+/* smb_raw_exit(cli->session); */
+
+ smbcli_deltree(cli->tree, BASEDIR);
+
+ return ret;
+}
diff --git a/source4/torture/raw/tconrate.c b/source4/torture/raw/tconrate.c
new file mode 100644
index 0000000000..076e5be31f
--- /dev/null
+++ b/source4/torture/raw/tconrate.c
@@ -0,0 +1,204 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/libcli.h"
+#include "libcli/resolve/resolve.h"
+#include "torture/smbtorture.h"
+#include "lib/cmdline/popt_common.h"
+#include "param/param.h"
+
+#include "system/filesys.h"
+#include "system/shmem.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;
+
+ lp_smbcli_options(tctx->lp_ctx, &options);
+ lp_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, lp_smb_ports(tctx->lp_ctx), share,
+ NULL, lp_socket_options(tctx->lp_ctx), cmdline_credentials,
+ lp_resolve_context(tctx->lp_ctx),
+ tctx->ev, &options, &session_options,
+ lp_iconv_convenience(tctx->lp_ctx),
+ lp_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);
+
+ *tcon_count = *tcon_count + 1;
+ now = timeval_current();
+ }
+
+done:
+ exit(0);
+}
+
+static bool children_remain(void)
+{
+ /* Reap as many children as possible. */
+ for (;;) {
+ pid_t ret = waitpid(-1, NULL, WNOHANG);
+ if (ret == 0) {
+ /* no children ready */
+ return true;
+ }
+ if (ret == -1) {
+ /* no children left. maybe */
+ return errno == ECHILD ? false : true;
+ }
+ }
+
+ /* notreached */
+ return false;
+}
+
+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_array(NULL, 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 0000000000..1058a58d9d
--- /dev/null
+++ b/source4/torture/raw/unlink.c
@@ -0,0 +1,449 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#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"
+
+#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";
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ printf("Trying non-existant 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);
+
+ printf("Trying wildcards\n");
+ smbcli_close(cli->tree, smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE));
+ io.unlink.in.pattern = BASEDIR "\\t*.t";
+ io.unlink.in.attrib = 0;
+ status = smb_raw_unlink(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_NO_SUCH_FILE);
+
+ io.unlink.in.pattern = BASEDIR "\\z*";
+ io.unlink.in.attrib = 0;
+ status = smb_raw_unlink(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_NO_SUCH_FILE);
+
+ io.unlink.in.pattern = BASEDIR "\\z*";
+ io.unlink.in.attrib = FILE_ATTRIBUTE_DIRECTORY;
+ status = smb_raw_unlink(cli->tree, &io);
+
+ if (torture_setting_bool(tctx, "samba3", false)) {
+ /*
+ * In Samba3 we gave up upon getting the error codes in
+ * wildcard unlink correct. Trying gentest showed that this is
+ * irregular beyond our capabilities. So for
+ * FILE_ATTRIBUTE_DIRECTORY we always return NAME_INVALID.
+ * Tried by jra and vl. If others feel like solving this
+ * puzzle, please tell us :-)
+ */
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
+ }
+ else {
+ CHECK_STATUS(status, NT_STATUS_NO_SUCH_FILE);
+ }
+
+ io.unlink.in.pattern = BASEDIR "\\*";
+ io.unlink.in.attrib = FILE_ATTRIBUTE_DIRECTORY;
+ status = smb_raw_unlink(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
+
+ io.unlink.in.pattern = BASEDIR "\\?";
+ io.unlink.in.attrib = FILE_ATTRIBUTE_DIRECTORY;
+ status = smb_raw_unlink(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
+
+ io.unlink.in.pattern = BASEDIR "\\t*";
+ io.unlink.in.attrib = FILE_ATTRIBUTE_DIRECTORY;
+ status = smb_raw_unlink(cli->tree, &io);
+ if (torture_setting_bool(tctx, "samba3", false)) {
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
+ }
+ else {
+ CHECK_STATUS(status, NT_STATUS_OK);
+ }
+
+ smbcli_close(cli->tree, smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE));
+
+ io.unlink.in.pattern = BASEDIR "\\*.dat";
+ io.unlink.in.attrib = FILE_ATTRIBUTE_DIRECTORY;
+ status = smb_raw_unlink(cli->tree, &io);
+ if (torture_setting_bool(tctx, "samba3", false)) {
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID);
+ }
+ else {
+ CHECK_STATUS(status, NT_STATUS_NO_SUCH_FILE);
+ }
+
+ io.unlink.in.pattern = BASEDIR "\\*.tx?";
+ io.unlink.in.attrib = 0;
+ status = smb_raw_unlink(cli->tree, &io);
+ if (torture_setting_bool(tctx, "samba3", false)) {
+ CHECK_STATUS(status, NT_STATUS_NO_SUCH_FILE);
+ }
+ else {
+ CHECK_STATUS(status, NT_STATUS_OK);
+ }
+
+ status = smb_raw_unlink(cli->tree, &io);
+ CHECK_STATUS(status, NT_STATUS_NO_SUCH_FILE);
+
+
+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;
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ 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 = 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 = 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 = 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;
+}
+
+
+/*
+ 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);
+
+ return suite;
+}
diff --git a/source4/torture/raw/write.c b/source4/torture/raw/write.c
new file mode 100644
index 0000000000..5d3628ca86
--- /dev/null
+++ b/source4/torture/raw/write.c
@@ -0,0 +1,724 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#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"
+
+#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 CHECK_VALUE(v, correct) do { \
+ if ((v) != (correct)) { \
+ printf("(%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(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) { \
+ printf("(%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, uint_t seed, int len)
+{
+ int i;
+ srandom(seed);
+ for (i=0;i<len;i++) buf[i] = random();
+}
+
+/*
+ check a random buffer based on a seed
+*/
+static bool check_buffer(uint8_t *buf, uint_t seed, int len, const char *location)
+{
+ int i;
+ srandom(seed);
+ for (i=0;i<len;i++) {
+ uint8_t v = random();
+ if (buf[i] != v) {
+ printf("Buffer incorrect at %s! ofs=%d buf=0x%x correct=0x%x\n",
+ location, i, buf[i], v);
+ return false;
+ }
+ }
+ return true;
+}
+
+/*
+ test write ops
+*/
+static bool test_write(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";
+ uint_t seed = time(NULL);
+ union smb_fileinfo finfo;
+
+ buf = talloc_zero_array(tctx, uint8_t, maxsize);
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ printf("Testing RAW_WRITE_WRITE\n");
+ io.generic.level = RAW_WRITE_WRITE;
+
+ 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 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);
+
+ printf("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) {
+ printf("read failed at %s\n", __location__);
+ ret = false;
+ goto done;
+ }
+ CHECK_BUFFER(buf+4, seed, 9);
+ CHECK_VALUE(IVAL(buf,0), 0);
+
+ setup_buffer(buf, seed, maxsize);
+
+ printf("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) {
+ printf("read failed at %s\n", __location__);
+ ret = false;
+ goto done;
+ }
+ CHECK_BUFFER(buf, seed, 4000);
+
+ printf("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);
+
+ printf("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)) {
+ printf("skipping large file tests - CAP_LARGE_FILES not set\n");
+ goto done;
+ }
+
+ if (!(cli->transport->negotiate.capabilities & CAP_LARGE_FILES)) {
+ printf("skipping large file tests - CAP_LARGE_FILES not set\n");
+ goto done;
+ }
+
+ printf("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) {
+ printf("read failed at %s\n", __location__);
+ ret = false;
+ goto done;
+ }
+ 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";
+ uint_t 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 (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ printf("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) {
+ printf("Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree));
+ ret = false;
+ goto done;
+ }
+
+ printf("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);
+
+ printf("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) {
+ printf("read failed at %s\n", __location__);
+ ret = false;
+ goto done;
+ }
+ CHECK_BUFFER(buf+4, seed, 9);
+ CHECK_VALUE(IVAL(buf,0), 0);
+
+ setup_buffer(buf, seed, maxsize);
+
+ printf("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) {
+ printf("read failed at %s\n", __location__);
+ ret = false;
+ goto done;
+ }
+ CHECK_BUFFER(buf, seed, 4000);
+
+ printf("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);
+
+ printf("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);
+
+
+ printf("Trying locked region\n");
+ cli->session->pid++;
+ if (NT_STATUS_IS_ERR(smbcli_lock(cli->tree, fnum, 3, 1, 0, WRITE_LOCK))) {
+ printf("Failed to lock file at %s\n", __location__);
+ ret = false;
+ goto done;
+ }
+ 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);
+
+ printf("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)) {
+ printf("skipping large file tests - CAP_LARGE_FILES not set\n");
+ goto done;
+ }
+
+ printf("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) {
+ printf("read failed at %s\n", __location__);
+ ret = false;
+ goto done;
+ }
+ CHECK_BUFFER(buf, seed, 4000);
+
+ for (i=33;i<max_bits;i++) {
+ printf("Trying 2^%d offset\n", i);
+ setup_buffer(buf, seed+1, maxsize);
+ io.writex.in.file.fnum = fnum;
+ io.writex.in.count = 4000;
+ io.writex.in.offset = ((uint64_t)1) << i;
+ io.writex.in.data = buf;
+ status = smb_raw_write(cli->tree, &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) {
+ printf("read failed at %s\n", __location__);
+ ret = false;
+ goto done;
+ }
+ CHECK_BUFFER(buf, seed+1, 4000);
+ }
+ printf("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";
+ uint_t seed = time(NULL);
+ union smb_fileinfo finfo;
+
+ buf = talloc_zero_array(tctx, uint8_t, maxsize);
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ printf("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) {
+ printf("Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree));
+ ret = false;
+ goto done;
+ }
+
+ printf("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);
+
+ printf("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) {
+ printf("read failed at %s\n", __location__);
+ ret = false;
+ goto done;
+ }
+ 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) {
+ printf("read failed at %s\n", __location__);
+ ret = false;
+ goto done;
+ }
+ CHECK_BUFFER(buf+4, seed, 9);
+ CHECK_VALUE(IVAL(buf,0), 0);
+
+ setup_buffer(buf, seed, maxsize);
+
+ printf("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) {
+ printf("read failed at %s\n", __location__);
+ ret = false;
+ goto done;
+ }
+ CHECK_BUFFER(buf, seed, 4000);
+
+ printf("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);
+
+ printf("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)) {
+ printf("skipping large file tests - CAP_LARGE_FILES not set\n");
+ goto done;
+ }
+
+ printf("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) {
+ printf("read failed at %s\n", __location__);
+ ret = false;
+ goto done;
+ }
+ 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";
+ uint_t seed = time(NULL);
+ union smb_fileinfo finfo;
+
+ buf = talloc_zero_array(tctx, uint8_t, maxsize);
+
+ if (!torture_setup_dir(cli, BASEDIR)) {
+ return false;
+ }
+
+ printf("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) {
+ printf("Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree));
+ ret = false;
+ goto done;
+ }
+
+ printf("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);
+
+ printf("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) {
+ printf("read failed at %s\n", __location__);
+ ret = false;
+ goto done;
+ }
+ 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) {
+ printf("read failed at %s\n", __location__);
+ ret = false;
+ goto done;
+ }
+ CHECK_BUFFER(buf+4, seed, 9);
+ CHECK_VALUE(IVAL(buf,0), 0);
+
+ setup_buffer(buf, seed, maxsize);
+
+ printf("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) {
+ printf("read failed at %s\n", __location__);
+ ret = false;
+ goto done;
+ }
+ CHECK_BUFFER(buf, seed, 4000);
+
+ printf("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);
+
+ printf("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)) {
+ printf("skipping large file tests - CAP_LARGE_FILES not set\n");
+ goto done;
+ }
+
+ printf("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) {
+ printf("read failed at %s\n", __location__);
+ ret = false;
+ goto done;
+ }
+ CHECK_BUFFER(buf, seed, 4000);
+
+done:
+ 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);
+
+ return suite;
+}
diff --git a/source4/torture/rpc/alter_context.c b/source4/torture/rpc/alter_context.c
new file mode 100644
index 0000000000..7843713074
--- /dev/null
+++ b/source4/torture/rpc/alter_context.c
@@ -0,0 +1,86 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/ndr_lsa.h"
+#include "librpc/gen_ndr/ndr_dssetup.h"
+#include "librpc/rpc/dcerpc.h"
+#include "torture/rpc/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;
+ struct ndr_syntax_id syntax;
+ struct ndr_syntax_id transfer_syntax;
+ 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");
+
+ if (!test_lsa_OpenPolicy2(p, torture, &handle)) {
+ ret = false;
+ }
+
+ 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");
+
+ 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, torture, handle);
+ }
+
+ syntax = p->syntax;
+ transfer_syntax = p->transfer_syntax;
+
+ torture_comment(torture, "Testing change of primary context\n");
+ status = dcerpc_alter_context(p, torture, &p2->syntax, &p2->transfer_syntax);
+ 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_NET_WRITE_FAULT);
+
+ ret &= test_lsa_OpenPolicy2(p, torture, &handle);
+
+ if (handle) {
+ ret &= test_lsa_Close(p, 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 0000000000..0ebbef1ce6
--- /dev/null
+++ b/source4/torture/rpc/async_bind.c
@@ -0,0 +1,90 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "lib/events/events.h"
+#include "libcli/composite/composite.h"
+#include "librpc/gen_ndr/ndr_lsa.h"
+#include "lib/cmdline/popt_common.h"
+#include "librpc/rpc/dcerpc.h"
+#include "torture/rpc/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 **pipe;
+ 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;
+ pipe = talloc_array(torture, struct dcerpc_pipe*, torture_numasync);
+ if (pipe == NULL) return false;
+ table = talloc_array(torture, const struct ndr_interface_table*, torture_numasync);
+ if (table == NULL) return false;
+
+ /* credentials */
+ creds = cmdline_credentials;
+
+ /* 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, &pipe[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 0000000000..23d76ae502
--- /dev/null
+++ b/source4/torture/rpc/atsvc.c
@@ -0,0 +1,139 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/ndr_atsvc_c.h"
+#include "torture/rpc/rpc.h"
+
+static bool test_JobGetInfo(struct dcerpc_pipe *p, struct torture_context *tctx, uint32_t job_id)
+{
+ NTSTATUS status;
+ 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;
+
+ status = dcerpc_atsvc_JobGetInfo(p, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "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)
+{
+ NTSTATUS status;
+ 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;
+
+ status = dcerpc_atsvc_JobDel(p, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "JobDel failed");
+
+ return true;
+}
+
+static bool test_JobEnum(struct torture_context *tctx, struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ 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;
+
+ status = dcerpc_atsvc_JobEnum(p, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "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)
+{
+ NTSTATUS status;
+ 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;
+
+ status = dcerpc_atsvc_JobAdd(p, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "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/autoidl.c b/source4/torture/rpc/autoidl.c
new file mode 100644
index 0000000000..5ae0201855
--- /dev/null
+++ b/source4/torture/rpc/autoidl.c
@@ -0,0 +1,277 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ auto-idl scanner
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/ndr_drsuapi_c.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "librpc/ndr/ndr_table.h"
+#include "torture/rpc/rpc.h"
+#include "librpc/rpc/dcerpc_proto.h"
+
+
+#if 1
+/*
+ get a DRSUAPI policy handle
+*/
+static bool get_policy_handle(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct drsuapi_DsBind r;
+
+ ZERO_STRUCT(r);
+ r.out.bind_handle = handle;
+
+ status = dcerpc_drsuapi_DsBind(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("drsuapi_DsBind failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+#else
+/*
+ get a SAMR handle
+*/
+static bool get_policy_handle(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct samr_Connect r;
+
+ r.in.system_name = 0;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.connect_handle = handle;
+
+ status = dcerpc_samr_Connect(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("samr_Connect failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+#endif
+
+static void fill_blob_handle(DATA_BLOB *blob, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle)
+{
+ DATA_BLOB b2;
+
+ if (blob->length < 20) {
+ return;
+ }
+
+ ndr_push_struct_blob(&b2, mem_ctx, NULL, handle, (ndr_push_flags_fn_t)ndr_push_policy_handle);
+
+ memcpy(blob->data, b2.data, 20);
+}
+
+static void reopen(struct torture_context *tctx,
+ struct dcerpc_pipe **p,
+ const struct ndr_interface_table *iface)
+{
+ NTSTATUS status;
+
+ talloc_free(*p);
+
+ status = torture_rpc_connection(tctx, p, iface);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed to reopen '%s' - %s\n", iface->name, nt_errstr(status));
+ exit(1);
+ }
+}
+
+static void print_depth(int depth)
+{
+ int i;
+ for (i=0;i<depth;i++) {
+ printf(" ");
+ }
+}
+
+static void test_ptr_scan(struct torture_context *tctx, const struct ndr_interface_table *iface,
+ int opnum, DATA_BLOB *base_in, int min_ofs, int max_ofs, int depth);
+
+static void try_expand(struct torture_context *tctx, const struct ndr_interface_table *iface,
+ int opnum, DATA_BLOB *base_in, int insert_ofs, int depth)
+{
+ DATA_BLOB stub_in, stub_out;
+ int n;
+ NTSTATUS status;
+ struct dcerpc_pipe *p = NULL;
+
+ reopen(tctx, &p, iface);
+
+ /* work out how much to expand to get a non fault */
+ for (n=0;n<2000;n++) {
+ stub_in = data_blob(NULL, base_in->length + n);
+ data_blob_clear(&stub_in);
+ memcpy(stub_in.data, base_in->data, insert_ofs);
+ memcpy(stub_in.data+insert_ofs+n, base_in->data+insert_ofs, base_in->length-insert_ofs);
+
+ status = dcerpc_request(p, NULL, opnum, false, tctx, &stub_in, &stub_out);
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ print_depth(depth);
+ printf("expand by %d gives %s\n", n, nt_errstr(status));
+ if (n >= 4) {
+ test_ptr_scan(tctx, iface, opnum, &stub_in,
+ insert_ofs, insert_ofs+n, depth+1);
+ }
+ return;
+ } else {
+#if 0
+ print_depth(depth);
+ printf("expand by %d gives fault %s\n", n, dcerpc_errstr(tctx, p->last_fault_code));
+#endif
+ }
+ if (p->last_fault_code == 5) {
+ reopen(tctx, &p, iface);
+ }
+ }
+
+ talloc_free(p);
+}
+
+
+static void test_ptr_scan(struct torture_context *tctx, const struct ndr_interface_table *iface,
+ int opnum, DATA_BLOB *base_in, int min_ofs, int max_ofs, int depth)
+{
+ DATA_BLOB stub_in, stub_out;
+ int ofs;
+ NTSTATUS status;
+ struct dcerpc_pipe *p = NULL;
+
+ reopen(tctx, &p, iface);
+
+ stub_in = data_blob(NULL, base_in->length);
+ memcpy(stub_in.data, base_in->data, base_in->length);
+
+ /* work out which elements are pointers */
+ for (ofs=min_ofs;ofs<=max_ofs-4;ofs+=4) {
+ SIVAL(stub_in.data, ofs, 1);
+ status = dcerpc_request(p, NULL, opnum, false, tctx, &stub_in, &stub_out);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ print_depth(depth);
+ printf("possible ptr at ofs %d - fault %s\n",
+ ofs-min_ofs, dcerpc_errstr(tctx, p->last_fault_code));
+ if (p->last_fault_code == 5) {
+ reopen(tctx, &p, iface);
+ }
+ if (depth == 0) {
+ try_expand(tctx, iface, opnum, &stub_in, ofs+4, depth+1);
+ } else {
+ try_expand(tctx, iface, opnum, &stub_in, max_ofs, depth+1);
+ }
+ SIVAL(stub_in.data, ofs, 0);
+ continue;
+ }
+ SIVAL(stub_in.data, ofs, 0);
+ }
+
+ talloc_free(p);
+}
+
+
+static void test_scan_call(struct torture_context *tctx, const struct ndr_interface_table *iface, int opnum)
+{
+ DATA_BLOB stub_in, stub_out;
+ int i;
+ NTSTATUS status;
+ struct dcerpc_pipe *p = NULL;
+ struct policy_handle handle;
+
+ reopen(tctx, &p, iface);
+
+ get_policy_handle(p, tctx, &handle);
+
+ /* work out the minimum amount of input data */
+ for (i=0;i<2000;i++) {
+ stub_in = data_blob(NULL, i);
+ data_blob_clear(&stub_in);
+
+
+ status = dcerpc_request(p, NULL, opnum, false, tctx, &stub_in, &stub_out);
+
+ if (NT_STATUS_IS_OK(status)) {
+ printf("opnum %d min_input %d - output %d\n",
+ opnum, (int)stub_in.length, (int)stub_out.length);
+ dump_data(0, stub_out.data, stub_out.length);
+ talloc_free(p);
+ test_ptr_scan(tctx, iface, opnum, &stub_in, 0, stub_in.length, 0);
+ return;
+ }
+
+ fill_blob_handle(&stub_in, tctx, &handle);
+
+ status = dcerpc_request(p, NULL, opnum, false, tctx, &stub_in, &stub_out);
+
+ if (NT_STATUS_IS_OK(status)) {
+ printf("opnum %d min_input %d - output %d (with handle)\n",
+ opnum, (int)stub_in.length, (int)stub_out.length);
+ dump_data(0, stub_out.data, stub_out.length);
+ talloc_free(p);
+ test_ptr_scan(tctx, iface, opnum, &stub_in, 0, stub_in.length, 0);
+ return;
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ printf("opnum %d size %d fault %s\n", opnum, i, dcerpc_errstr(tctx, p->last_fault_code));
+ if (p->last_fault_code == 5) {
+ reopen(tctx, &p, iface);
+ }
+ continue;
+ }
+
+ printf("opnum %d size %d error %s\n", opnum, i, nt_errstr(status));
+ }
+
+ printf("opnum %d minimum not found!?\n", opnum);
+ talloc_free(p);
+}
+
+
+static void test_auto_scan(struct torture_context *tctx, const struct ndr_interface_table *iface)
+{
+ test_scan_call(tctx, iface, 2);
+}
+
+bool torture_rpc_autoidl(struct torture_context *torture)
+{
+ const struct ndr_interface_table *iface;
+
+ iface = ndr_table_by_name("drsuapi");
+ if (!iface) {
+ printf("Unknown interface!\n");
+ return false;
+ }
+
+ printf("\nProbing pipe '%s'\n", iface->name);
+
+ test_auto_scan(torture, iface);
+
+ return true;
+}
diff --git a/source4/torture/rpc/bench.c b/source4/torture/rpc/bench.c
new file mode 100644
index 0000000000..6fa3815516
--- /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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/ndr_srvsvc_c.h"
+#include "torture/rpc/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;
+
+ 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;i<ARRAY_SIZE(levels);i++) {
+ resume_handle = 0;
+ info_ctr.level = levels[i];
+
+ switch (info_ctr.level) {
+ case 0:
+ ZERO_STRUCT(c0);
+ info_ctr.ctr.ctr0 = &c0;
+ break;
+ case 1:
+ ZERO_STRUCT(c1);
+ info_ctr.ctr.ctr1 = &c1;
+ break;
+ case 2:
+ ZERO_STRUCT(c2);
+ info_ctr.ctr.ctr2 = &c2;
+ break;
+ case 501:
+ ZERO_STRUCT(c501);
+ info_ctr.ctr.ctr501 = &c501;
+ break;
+ case 502:
+ ZERO_STRUCT(c502);
+ info_ctr.ctr.ctr502 = &c502;
+ break;
+ }
+
+ status = dcerpc_srvsvc_NetShareEnumAll(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("NetShareEnumAll level %u failed - %s\n", info_ctr.level, nt_errstr(status));
+ ret = false;
+ continue;
+ }
+ if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("NetShareEnumAll level %u failed - %s\n", info_ctr.level, win_errstr(r.out.result));
+ continue;
+ }
+ }
+
+ return ret;
+}
+
+/*
+ benchmark srvsvc netshareenumall queries
+*/
+static bool bench_NetShareEnumAll(struct torture_context *tctx, struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx)
+{
+ struct timeval tv = timeval_current();
+ bool ret = true;
+ int timelimit = torture_setting_int(tctx, "timelimit", 10);
+ int count=0;
+
+ printf("Running for %d seconds\n", timelimit);
+ while (timeval_elapsed(&tv) < timelimit) {
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+ if (!test_NetShareEnumAll(p, tmp_ctx)) break;
+ talloc_free(tmp_ctx);
+ count++;
+ if (count % 50 == 0) {
+ if (torture_setting_bool(tctx, "progress", true)) {
+ printf("%.1f queries per second \r",
+ count / timeval_elapsed(&tv));
+ }
+ }
+ }
+
+ printf("%.1f queries per second \n", count / timeval_elapsed(&tv));
+
+ return ret;
+}
+
+
+bool torture_bench_rpc(struct torture_context *torture)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p;
+ TALLOC_CTX *mem_ctx;
+ bool ret = true;
+
+ mem_ctx = talloc_init("torture_rpc_srvsvc");
+
+ status = torture_rpc_connection(torture,
+ &p,
+ &ndr_table_srvsvc);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ if (!bench_NetShareEnumAll(torture, p, mem_ctx)) {
+ ret = false;
+ }
+
+ talloc_free(mem_ctx);
+
+ return ret;
+}
diff --git a/source4/torture/rpc/bind.c b/source4/torture/rpc/bind.c
new file mode 100644
index 0000000000..55c9d3686d
--- /dev/null
+++ b/source4/torture/rpc/bind.c
@@ -0,0 +1,82 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ dcerpc torture tests
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Andrew Bartlett <abartlet@samba.org 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/ndr_lsa.h"
+#include "librpc/gen_ndr/ndr_lsa_c.h"
+#include "lib/cmdline/popt_common.h"
+#include "librpc/rpc/dcerpc.h"
+#include "torture/rpc/rpc.h"
+#include "libcli/libcli.h"
+#include "libcli/composite/composite.h"
+#include "libcli/smb_composite/smb_composite.h"
+
+/*
+ This test is 'bogus' in that it doesn't actually perform to the
+ spec. We need to deal with other things inside the DCERPC layer,
+ before we could have multiple binds.
+
+ We should never pass this test, until such details are fixed in our
+ client, and it looks like multible binds are never used anyway.
+
+*/
+
+bool torture_multi_bind(struct torture_context *torture)
+{
+ struct dcerpc_pipe *p;
+ struct dcerpc_binding *binding;
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+ bool ret;
+
+ mem_ctx = talloc_init("torture_multi_bind");
+
+ status = torture_rpc_binding(torture, &binding);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ status = torture_rpc_connection(torture, &p, &ndr_table_lsarpc);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ status = dcerpc_pipe_auth(mem_ctx, &p, binding, &ndr_table_lsarpc, cmdline_credentials,
+ torture->lp_ctx);
+
+ if (NT_STATUS_IS_OK(status)) {
+ printf("(incorrectly) allowed re-bind to uuid %s - %s\n",
+ GUID_string(mem_ctx, &ndr_table_lsarpc.syntax_id.uuid), nt_errstr(status));
+ ret = false;
+ } else {
+ printf("\n");
+ ret = true;
+ }
+
+ talloc_free(mem_ctx);
+
+ return ret;
+}
diff --git a/source4/torture/rpc/browser.c b/source4/torture/rpc/browser.c
new file mode 100644
index 0000000000..32b552acc8
--- /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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/ndr_browser_c.h"
+#include "torture/rpc/rpc.h"
+
+bool test_BrowserrQueryOtherDomains(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ 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(p, 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(p, 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(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "BrowserrQueryOtherDomains failed");
+ torture_assert_werr_equal(tctx, WERR_INVALID_PARAM, r.out.result,
+ "BrowserrQueryOtherDomains failed");
+
+ info.level = 101;
+ info.info.info101 = &ctr101;
+
+ status = dcerpc_BrowserrQueryOtherDomains(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "BrowserrQueryOtherDomains failed");
+ torture_assert_werr_equal(tctx, WERR_UNKNOWN_LEVEL, r.out.result,
+ "BrowserrQueryOtherDomains");
+
+ info.info.info101 = &ctr101;
+ ctr101.entries_read = ARRAY_SIZE(entries101);
+ ctr101.entries = entries101;
+
+ status = dcerpc_BrowserrQueryOtherDomains(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "BrowserrQueryOtherDomains failed");
+ torture_assert_werr_equal(tctx, WERR_UNKNOWN_LEVEL, r.out.result,
+ "BrowserrQueryOtherDomains");
+
+ info.info.info101 = NULL;
+ status = dcerpc_BrowserrQueryOtherDomains(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "BrowserrQueryOtherDomains failed");
+ torture_assert_werr_equal(tctx, WERR_UNKNOWN_LEVEL, r.out.result,
+ "BrowserrQueryOtherDomains");
+
+ info.level = 102;
+ status = dcerpc_BrowserrQueryOtherDomains(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "BrowserrQueryOtherDomains failed");
+ torture_assert_werr_equal(tctx, WERR_UNKNOWN_LEVEL, r.out.result,
+ "BrowserrQueryOtherDomains");
+
+ info.level = 0;
+ status = dcerpc_BrowserrQueryOtherDomains(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "BrowserrQueryOtherDomains failed");
+ torture_assert_werr_equal(tctx, WERR_UNKNOWN_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/countcalls.c b/source4/torture/rpc/countcalls.c
new file mode 100644
index 0000000000..205ee1ec86
--- /dev/null
+++ b/source4/torture/rpc/countcalls.c
@@ -0,0 +1,129 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ count number of calls on an interface
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "librpc/ndr/libndr.h"
+#include "librpc/ndr/ndr_table.h"
+#include "torture/rpc/rpc.h"
+#include "param/param.h"
+#include "librpc/rpc/dcerpc_proto.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_EQUAL(NT_STATUS_NET_WRITE_FAULT, 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_talloc(p, mem_ctx, 0);
+
+ printf("\nScanning pipe '%s'\n", iface->name);
+
+ for (i=0;i<500;i++) {
+ status = dcerpc_request(p, NULL, i, false, p, &stub_in, &stub_out);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT) &&
+ p->last_fault_code == DCERPC_FAULT_OP_RNG_ERROR) {
+ i--;
+ break;
+ }
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT) &&
+ p->last_fault_code == DCERPC_FAULT_OP_RNG_ERROR) {
+ 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 = lp_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 0000000000..1c81766ebe
--- /dev/null
+++ b/source4/torture/rpc/dfs.c
@@ -0,0 +1,665 @@
+/*
+ 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/torture.h"
+#include "torture/rpc/rpc.h"
+#include "librpc/gen_ndr/ndr_dfs_c.h"
+#include "librpc/gen_ndr/ndr_srvsvc_c.h"
+#include "libnet/libnet.h"
+#include "libcli/raw/libcliraw.h"
+#include "torture/util.h"
+#include "libcli/libcli.h"
+#include "lib/cmdline/popt_common.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(TALLOC_CTX *mem_ctx,
+ 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 = cmdline_credentials;
+
+ 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, mem_ctx, &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(TALLOC_CTX *mem_ctx,
+ struct torture_context *tctx,
+ const char *host,
+ const char *sharename)
+{
+ NTSTATUS status;
+ struct libnet_context* libnetctx;
+ struct libnet_DelShare r;
+
+ printf("Deleting share %s\n", sharename);
+
+ if (!(libnetctx = libnet_context_init(tctx->ev, tctx->lp_ctx))) {
+ return false;
+ }
+
+ libnetctx->cred = cmdline_credentials;
+
+ r.in.share_name = sharename;
+ r.in.server_name = host;
+
+ status = libnet_DelShare(libnetctx, mem_ctx, &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 smbcli_state *cli,
+ const char *dir)
+{
+ printf("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(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ enum dfs_ManagerVersion *version)
+{
+ NTSTATUS status;
+ struct dfs_GetManagerVersion r;
+
+ r.out.version = version;
+
+ status = dcerpc_dfs_GetManagerVersion(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("GetManagerVersion failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_ManagerInitialize(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ const char *host)
+{
+ NTSTATUS status;
+ enum dfs_ManagerVersion version;
+ struct dfs_ManagerInitialize r;
+
+ printf("Testing ManagerInitialize\n");
+
+ if (!test_GetManagerVersion(p, mem_ctx, &version)) {
+ return false;
+ }
+
+ r.in.servername = host;
+ r.in.flags = 0;
+
+ status = dcerpc_dfs_ManagerInitialize(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("ManagerInitialize failed - %s\n", nt_errstr(status));
+ return false;
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("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 dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ uint16_t level,
+ const char *root)
+{
+ NTSTATUS status;
+ struct dfs_GetInfo r;
+
+ printf("Testing GetInfo level %u on '%s'\n", level, root);
+
+ r.in.dfs_entry_path = talloc_strdup(mem_ctx, root);
+ r.in.servername = NULL;
+ r.in.sharename = NULL;
+ r.in.level = level;
+
+ status = dcerpc_dfs_GetInfo(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("GetInfo failed - %s\n", nt_errstr(status));
+ return false;
+ } else if (!W_ERROR_IS_OK(r.out.result) &&
+ !W_ERROR_EQUAL(WERR_NO_MORE_ITEMS, r.out.result)) {
+ printf("dfs_GetInfo failed - %s\n", win_errstr(r.out.result));
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_GetInfo(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ 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;i<ARRAY_SIZE(levels);i++) {
+ if (!test_GetInfoLevel(p, mem_ctx, levels[i], root)) {
+ ret = false;
+ }
+ }
+ return ret;
+}
+
+static bool test_EnumLevelEx(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ uint16_t level,
+ const char *dfs_name)
+{
+ NTSTATUS status;
+ struct dfs_EnumEx rex;
+ uint32_t total=0;
+ struct dfs_EnumStruct e;
+ struct dfs_Info1 s;
+ struct dfs_EnumArray1 e1;
+ bool ret = true;
+
+ rex.in.level = level;
+ rex.in.bufsize = (uint32_t)-1;
+ rex.in.total = &total;
+ rex.in.info = &e;
+ rex.in.dfs_name = dfs_name;
+
+ e.level = rex.in.level;
+ e.e.info1 = &e1;
+ e.e.info1->count = 0;
+ e.e.info1->s = &s;
+ s.path = NULL;
+
+ printf("Testing EnumEx level %u on '%s'\n", level, dfs_name);
+
+ status = dcerpc_dfs_EnumEx(p, mem_ctx, &rex);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("EnumEx failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if (level == 1 && rex.out.total) {
+ int i;
+ for (i=0;i<*rex.out.total;i++) {
+ const char *root = talloc_strdup(mem_ctx,
+ rex.out.info->e.info1->s[i].path);
+ if (!test_GetInfo(p, mem_ctx, 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 = talloc_strdup(mem_ctx,
+ rex.out.info->e.info300->s[i].dom_root);
+ for (k=0;k<ARRAY_SIZE(levels);k++) {
+ if (!test_EnumLevelEx(p, mem_ctx,
+ levels[k], root))
+ {
+ ret = false;
+ }
+ }
+ if (!test_GetInfo(p, mem_ctx, root)) {
+ ret = false;
+ }
+ }
+ }
+
+ return ret;
+}
+
+
+static bool test_EnumLevel(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ uint16_t level)
+{
+ NTSTATUS status;
+ struct dfs_Enum r;
+ uint32_t total=0;
+ struct dfs_EnumStruct e;
+ struct dfs_Info1 s;
+ struct dfs_EnumArray1 e1;
+ bool ret = true;
+
+ r.in.level = level;
+ r.in.bufsize = (uint32_t)-1;
+ r.in.total = &total;
+ r.in.info = &e;
+
+ e.level = r.in.level;
+ e.e.info1 = &e1;
+ e.e.info1->count = 0;
+ e.e.info1->s = &s;
+ s.path = NULL;
+
+ printf("Testing Enum level %u\n", level);
+
+ status = dcerpc_dfs_Enum(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Enum failed - %s\n", nt_errstr(status));
+ return false;
+ } else if (!W_ERROR_IS_OK(r.out.result) &&
+ !W_ERROR_EQUAL(WERR_NO_MORE_ITEMS, r.out.result)) {
+ printf("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(p, mem_ctx, root)) {
+ ret = false;
+ }
+ }
+
+ }
+
+ return ret;
+}
+
+
+static bool test_Enum(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx)
+{
+ bool ret = true;
+ uint16_t levels[] = {1, 2, 3, 4, 5, 6, 200, 300};
+ int i;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ if (!test_EnumLevel(p, mem_ctx, levels[i])) {
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_EnumEx(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ const char *host)
+{
+ bool ret = true;
+ uint16_t levels[] = {1, 2, 3, 4, 5, 6, 200, 300};
+ int i;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ if (!test_EnumLevelEx(p, mem_ctx, levels[i], host)) {
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_RemoveStdRoot(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ const char *host,
+ const char *sharename)
+{
+ struct dfs_RemoveStdRoot r;
+ NTSTATUS status;
+
+ printf("Testing RemoveStdRoot\n");
+
+ r.in.servername = host;
+ r.in.rootshare = sharename;
+ r.in.flags = 0;
+
+ status = dcerpc_dfs_RemoveStdRoot(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("RemoveStdRoot failed - %s\n", nt_errstr(status));
+ return false;
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("dfs_RemoveStdRoot failed - %s\n",
+ win_errstr(r.out.result));
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_AddStdRoot(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ const char *host,
+ const char *sharename)
+{
+ NTSTATUS status;
+ struct dfs_AddStdRoot r;
+
+ printf("Testing AddStdRoot\n");
+
+ r.in.servername = host;
+ r.in.rootshare = sharename;
+ r.in.comment = "standard dfs standalone DFS root created by smbtorture (dfs_AddStdRoot)";
+ r.in.flags = 0;
+
+ status = dcerpc_dfs_AddStdRoot(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("AddStdRoot failed - %s\n", nt_errstr(status));
+ return false;
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("dfs_AddStdRoot failed - %s\n",
+ win_errstr(r.out.result));
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_AddStdRootForced(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ const char *host,
+ const char *sharename)
+{
+ NTSTATUS status;
+ struct dfs_AddStdRootForced r;
+ enum dfs_ManagerVersion version;
+
+ printf("Testing AddStdRootForced\n");
+
+ if (!test_GetManagerVersion(p, mem_ctx, &version)) {
+ return false;
+ }
+
+ r.in.servername = host;
+ r.in.rootshare = sharename;
+ r.in.comment = "standard dfs forced standalone DFS root created by smbtorture (dfs_AddStdRootForced)";
+ r.in.store = SMBTORTURE_DFS_PATHNAME;
+
+ status = dcerpc_dfs_AddStdRootForced(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("AddStdRootForced failed - %s\n", nt_errstr(status));
+ return false;
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("dfs_AddStdRootForced failed - %s\n",
+ win_errstr(r.out.result));
+ IS_DFS_VERSION_UNSUPPORTED_CALL_W2K3(version, r.out.result);
+ return false;
+ }
+
+ return test_RemoveStdRoot(p, mem_ctx, host, sharename);
+}
+
+static void test_cleanup_stdroot(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ struct torture_context *tctx,
+ const char *host,
+ const char *sharename,
+ const char *dir)
+{
+ struct smbcli_state *cli;
+
+ printf("Cleaning up StdRoot\n");
+
+ test_RemoveStdRoot(p, mem_ctx, host, sharename);
+ test_NetShareDel(mem_ctx, tctx, host, sharename);
+ torture_open_connection_share(mem_ctx, &cli, tctx, host, "C$", tctx->ev);
+ test_DeleteDir(cli, dir);
+ torture_close_connection(cli);
+}
+
+static bool test_StdRoot(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ struct torture_context *tctx,
+ const char *host)
+{
+ 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;
+
+ printf("Testing StdRoot\n");
+
+ test_cleanup_stdroot(p, mem_ctx, tctx, host, sharename, dir);
+
+ ret &= test_CreateDir(mem_ctx, &cli, tctx, host, "C$", dir);
+ ret &= test_NetShareAdd(mem_ctx, tctx, host, sharename, path);
+ ret &= test_AddStdRoot(p, mem_ctx, host, sharename);
+ ret &= test_RemoveStdRoot(p, mem_ctx, host, sharename);
+ ret &= test_AddStdRootForced(p, mem_ctx, host, sharename);
+ ret &= test_NetShareDel(mem_ctx, tctx, host, sharename);
+ ret &= test_DeleteDir(cli, dir);
+
+ torture_close_connection(cli);
+
+ return ret;
+}
+
+static bool test_GetDcAddress(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ const char *host)
+{
+ NTSTATUS status;
+ struct dfs_GetDcAddress r;
+ uint8_t is_root = 0;
+ uint32_t ttl = 0;
+ const char *ptr;
+
+ printf("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;
+
+ status = dcerpc_dfs_GetDcAddress(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("GetDcAddress failed - %s\n", nt_errstr(status));
+ return false;
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("dfs_GetDcAddress failed - %s\n",
+ win_errstr(r.out.result));
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_SetDcAddress(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ const char *host)
+{
+ NTSTATUS status;
+ struct dfs_SetDcAddress r;
+
+ printf("Testing SetDcAddress\n");
+
+ r.in.servername = host;
+ r.in.server_fullname = host;
+ r.in.flags = 0;
+ r.in.ttl = 1000;
+
+ status = dcerpc_dfs_SetDcAddress(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetDcAddress failed - %s\n", nt_errstr(status));
+ return false;
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("dfs_SetDcAddress failed - %s\n",
+ win_errstr(r.out.result));
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_DcAddress(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ const char *host)
+{
+ if (!test_GetDcAddress(p, mem_ctx, host)) {
+ return false;
+ }
+
+ if (!test_SetDcAddress(p, mem_ctx, host)) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_FlushFtTable(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ const char *host,
+ const char *sharename)
+{
+ NTSTATUS status;
+ struct dfs_FlushFtTable r;
+ enum dfs_ManagerVersion version;
+
+ printf("Testing FlushFtTable\n");
+
+ if (!test_GetManagerVersion(p, mem_ctx, &version)) {
+ return false;
+ }
+
+ r.in.servername = host;
+ r.in.rootshare = sharename;
+
+ status = dcerpc_dfs_FlushFtTable(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("FlushFtTable failed - %s\n", nt_errstr(status));
+ return false;
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("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 dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ const char *host)
+{
+ const char *sharename = SMBTORTURE_DFS_SHARENAME;
+
+ return test_FlushFtTable(p, mem_ctx, host, sharename);
+}
+
+bool torture_rpc_dfs(struct torture_context *torture)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p;
+ bool ret = true;
+ enum dfs_ManagerVersion version;
+ const char *host = torture_setting_string(torture, "host", NULL);
+
+ status = torture_rpc_connection(torture, &p, &ndr_table_netdfs);
+ torture_assert_ntstatus_ok(torture, status, "Unable to connect");
+
+ ret &= test_GetManagerVersion(p, torture, &version);
+ ret &= test_ManagerInitialize(p, torture, host);
+ ret &= test_Enum(p, torture);
+ ret &= test_EnumEx(p, torture, host);
+ ret &= test_StdRoot(p, torture, torture, host);
+ ret &= test_FtRoot(p, torture, host);
+ ret &= test_DcAddress(p, torture, host);
+
+ return ret;
+}
diff --git a/source4/torture/rpc/drsuapi.c b/source4/torture/rpc/drsuapi.c
new file mode 100644
index 0000000000..da6ce2b5f5
--- /dev/null
+++ b/source4/torture/rpc/drsuapi.c
@@ -0,0 +1,817 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ DRSUapi tests
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Stefan (metze) Metzmacher 2004
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/ndr_drsuapi_c.h"
+#include "torture/rpc/rpc.h"
+#include "param/param.h"
+
+#define TEST_MACHINE_NAME "torturetest"
+
+bool test_DsBind(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct DsPrivate *priv)
+{
+ NTSTATUS status;
+ struct drsuapi_DsBind r;
+
+ GUID_from_string(DRSUAPI_DS_BIND_GUID, &priv->bind_guid);
+
+ r.in.bind_guid = &priv->bind_guid;
+ r.in.bind_info = NULL;
+ r.out.bind_handle = &priv->bind_handle;
+
+ torture_comment(tctx, "testing DsBind\n");
+
+ status = dcerpc_drsuapi_DsBind(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ errstr = dcerpc_errstr(tctx, p->last_fault_code);
+ }
+ torture_fail(tctx, "dcerpc_drsuapi_DsBind failed");
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ torture_fail(tctx, "DsBind failed");
+ }
+
+ return true;
+}
+
+static bool test_DsGetDomainControllerInfo(struct dcerpc_pipe *p, struct torture_context *torture,
+ struct DsPrivate *priv)
+{
+ NTSTATUS status;
+ struct drsuapi_DsGetDomainControllerInfo r;
+ union drsuapi_DsGetDCInfoCtr ctr;
+ 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++) {
+ union drsuapi_DsGetDCInfoRequest req;
+ 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(torture,
+ "testing DsGetDomainControllerInfo level %d on domainname '%s'\n",
+ r.in.req->req1.level, r.in.req->req1.domain_name);
+
+ status = dcerpc_drsuapi_DsGetDomainControllerInfo(p, torture, &r);
+ torture_assert_ntstatus_ok(torture, status,
+ "dcerpc_drsuapi_DsGetDomainControllerInfo with dns domain failed");
+ torture_assert_werr_equal(torture,
+ 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(torture,
+ r.in.req->req1.level, *r.out.level_out,
+ "dcerpc_drsuapi_DsGetDomainControllerInfo level");
+
+ 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(torture, 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;
+
+ printf("testing DsGetDomainControllerInfo level %d on domainname '%s'\n",
+ r.in.req->req1.level, r.in.req->req1.domain_name);
+
+ status = dcerpc_drsuapi_DsGetDomainControllerInfo(p, torture, &r);
+
+ torture_assert_ntstatus_ok(torture, status,
+ "dcerpc_drsuapi_DsGetDomainControllerInfo with dns domain failed");
+ torture_assert_werr_ok(torture, r.out.result,
+ "DsGetDomainControllerInfo with dns domain failed");
+
+ {
+ const char *dc_account = talloc_asprintf(torture, "%s\\%s$",
+ torture_join_dom_netbios_name(priv->join),
+ priv->dcinfo.netbios_name);
+ 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(torture, found,
+ "dcerpc_drsuapi_DsGetDomainControllerInfo level: Failed to find the domain controller in last logon records");
+ }
+
+
+ return true;
+}
+
+static bool test_DsWriteAccountSpn(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct DsPrivate *priv)
+{
+ NTSTATUS status;
+ struct drsuapi_DsWriteAccountSpn r;
+ union drsuapi_DsWriteAccountSpnRequest req;
+ struct drsuapi_DsNameString names[2];
+ union drsuapi_DsWriteAccountSpnResult res;
+ int32_t level_out;
+ bool ret = true;
+
+ r.in.bind_handle = &priv->bind_handle;
+ r.in.level = 1;
+ r.in.req = &req;
+
+ printf("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(mem_ctx, "smbtortureSPN/%s",priv->dcinfo.netbios_name);
+ names[1].str = talloc_asprintf(mem_ctx, "smbtortureSPN/%s",priv->dcinfo.dns_name);
+
+ r.out.res = &res;
+ r.out.level_out = &level_out;
+
+ status = dcerpc_drsuapi_DsWriteAccountSpn(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ errstr = dcerpc_errstr(mem_ctx, p->last_fault_code);
+ }
+ printf("dcerpc_drsuapi_DsWriteAccountSpn failed - %s\n", errstr);
+ ret = false;
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("DsWriteAccountSpn failed - %s\n", win_errstr(r.out.result));
+ ret = false;
+ }
+
+ r.in.req->req1.operation = DRSUAPI_DS_SPN_OPERATION_DELETE;
+ r.in.req->req1.unknown1 = 0;
+
+ status = dcerpc_drsuapi_DsWriteAccountSpn(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ errstr = dcerpc_errstr(mem_ctx, p->last_fault_code);
+ }
+ printf("dcerpc_drsuapi_DsWriteAccountSpn failed - %s\n", errstr);
+ ret = false;
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("DsWriteAccountSpn failed - %s\n", win_errstr(r.out.result));
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool test_DsReplicaGetInfo(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct DsPrivate *priv)
+{
+ NTSTATUS status;
+ struct drsuapi_DsReplicaGetInfo r;
+ union drsuapi_DsReplicaGetInfoRequest req;
+ union drsuapi_DsReplicaInfo info;
+ enum drsuapi_DsReplicaInfoType info_type;
+ bool ret = true;
+ 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_NEIGHBORS02,
+ NULL
+ },{
+ DRSUAPI_DS_REPLICA_GET_INFO2,
+ DRSUAPI_DS_REPLICA_INFO_CONNECTIONS04,
+ "__IGNORED__"
+ },{
+ DRSUAPI_DS_REPLICA_GET_INFO2,
+ DRSUAPI_DS_REPLICA_INFO_CURSORS05,
+ NULL
+ },{
+ DRSUAPI_DS_REPLICA_GET_INFO2,
+ DRSUAPI_DS_REPLICA_INFO_06,
+ NULL
+ }
+ };
+
+ if (torture_setting_bool(tctx, "samba4", false)) {
+ printf("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;
+
+ printf("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.guid1);
+ 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.guid1);
+ r.in.req->req2.unknown1 = 0;
+ r.in.req->req2.string1 = NULL;
+ r.in.req->req2.string2 = NULL;
+ r.in.req->req2.unknown2 = 0;
+ break;
+ }
+
+ r.out.info = &info;
+ r.out.info_type = &info_type;
+
+ status = dcerpc_drsuapi_DsReplicaGetInfo(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ errstr = dcerpc_errstr(tctx, p->last_fault_code);
+ }
+ if (p->last_fault_code != DCERPC_FAULT_INVALID_TAG) {
+ printf("dcerpc_drsuapi_DsReplicaGetInfo failed - %s\n", errstr);
+ ret = false;
+ } else {
+ printf("DsReplicaGetInfo level %d and/or infotype %d not supported by server\n",
+ array[i].level, array[i].infotype);
+ }
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("DsReplicaGetInfo failed - %s\n", win_errstr(r.out.result));
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_DsReplicaSync(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct DsPrivate *priv)
+{
+ NTSTATUS status;
+ bool ret = true;
+ int i;
+ struct drsuapi_DsReplicaSync r;
+ struct drsuapi_DsReplicaObjectIdentifier nc;
+ struct GUID null_guid;
+ struct dom_sid null_sid;
+ struct {
+ int32_t level;
+ } array[] = {
+ {
+ 1
+ }
+ };
+
+ if (!torture_setting_bool(tctx, "dangerous", false)) {
+ printf("DsReplicaSync disabled - enable dangerous tests to use\n");
+ return true;
+ }
+
+ if (torture_setting_bool(tctx, "samba4", false)) {
+ printf("skipping DsReplicaSync test against Samba4\n");
+ return true;
+ }
+
+ ZERO_STRUCT(null_guid);
+ ZERO_STRUCT(null_sid);
+
+ r.in.bind_handle = &priv->bind_handle;
+
+ for (i=0; i < ARRAY_SIZE(array); i++) {
+ printf("testing DsReplicaSync level %d\n",
+ array[i].level);
+
+ r.in.level = array[i].level;
+ switch(r.in.level) {
+ case 1:
+ nc.guid = null_guid;
+ nc.sid = null_sid;
+ nc.dn = priv->domain_obj_dn?priv->domain_obj_dn:"";
+
+ r.in.req.req1.naming_context = &nc;
+ r.in.req.req1.source_dsa_guid = priv->dcinfo.ntds_guid;
+ r.in.req.req1.other_info = NULL;
+ r.in.req.req1.options = 16;
+ break;
+ }
+
+ status = dcerpc_drsuapi_DsReplicaSync(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ errstr = dcerpc_errstr(tctx, p->last_fault_code);
+ }
+ printf("dcerpc_drsuapi_DsReplicaSync failed - %s\n", errstr);
+ ret = false;
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("DsReplicaSync failed - %s\n", win_errstr(r.out.result));
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_DsReplicaUpdateRefs(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct DsPrivate *priv)
+{
+ NTSTATUS status;
+ bool ret = true;
+ int i;
+ struct drsuapi_DsReplicaUpdateRefs r;
+ struct drsuapi_DsReplicaObjectIdentifier nc;
+ struct GUID null_guid;
+ struct dom_sid null_sid;
+ struct {
+ int32_t level;
+ } array[] = {
+ {
+ 1
+ }
+ };
+
+ if (torture_setting_bool(tctx, "samba4", false)) {
+ printf("skipping DsReplicaUpdateRefs test against Samba4\n");
+ return true;
+ }
+
+ ZERO_STRUCT(null_guid);
+ ZERO_STRUCT(null_sid);
+
+ r.in.bind_handle = &priv->bind_handle;
+
+ for (i=0; i < ARRAY_SIZE(array); i++) {
+ printf("testing DsReplicaUpdateRefs level %d\n",
+ array[i].level);
+
+ r.in.level = array[i].level;
+ switch(r.in.level) {
+ case 1:
+ nc.guid = null_guid;
+ nc.sid = null_sid;
+ nc.dn = priv->domain_obj_dn?priv->domain_obj_dn:"";
+
+ r.in.req.req1.naming_context = &nc;
+ r.in.req.req1.dest_dsa_dns_name = talloc_asprintf(tctx, "__some_dest_dsa_guid_string._msdn.%s",
+ priv->domain_dns_name);
+ r.in.req.req1.dest_dsa_guid = null_guid;
+ r.in.req.req1.options = 0;
+ break;
+ }
+
+ status = dcerpc_drsuapi_DsReplicaUpdateRefs(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ errstr = dcerpc_errstr(tctx, p->last_fault_code);
+ }
+ printf("dcerpc_drsuapi_DsReplicaUpdateRefs failed - %s\n", errstr);
+ ret = false;
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("DsReplicaUpdateRefs failed - %s\n", win_errstr(r.out.result));
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_DsGetNCChanges(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct DsPrivate *priv)
+{
+ NTSTATUS status;
+ bool ret = true;
+ int i;
+ struct drsuapi_DsGetNCChanges r;
+ union drsuapi_DsGetNCChangesRequest req;
+ union drsuapi_DsGetNCChangesCtr ctr;
+ struct drsuapi_DsReplicaObjectIdentifier nc;
+ struct GUID null_guid;
+ struct dom_sid null_sid;
+ int32_t level_out;
+ struct {
+ int32_t level;
+ } array[] = {
+ {
+ 5
+ },
+ {
+ 8
+ }
+ };
+
+ if (torture_setting_bool(tctx, "samba4", false)) {
+ printf("skipping DsGetNCChanges test against Samba4\n");
+ return true;
+ }
+
+ ZERO_STRUCT(null_guid);
+ ZERO_STRUCT(null_sid);
+
+ for (i=0; i < ARRAY_SIZE(array); i++) {
+ printf("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 = null_guid;
+ 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 = null_guid;
+ 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 (lp_parm_bool(tctx->lp_ctx, NULL, "drsuapi","compression", false)) {
+ r.in.req->req5.replica_flags |= DRSUAPI_DS_REPLICA_NEIGHBOUR_COMPRESS_CHANGES;
+ }
+ 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 = null_guid;
+ 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 = null_guid;
+ 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 (lp_parm_bool(tctx->lp_ctx, NULL, "drsuapi", "compression", false)) {
+ r.in.req->req8.replica_flags |= DRSUAPI_DS_REPLICA_NEIGHBOUR_COMPRESS_CHANGES;
+ }
+ if (lp_parm_bool(tctx->lp_ctx, NULL, "drsuapi", "neighbour_writeable", true)) {
+ r.in.req->req8.replica_flags |= DRSUAPI_DS_REPLICA_NEIGHBOUR_WRITEABLE;
+ }
+ r.in.req->req8.replica_flags |= DRSUAPI_DS_REPLICA_NEIGHBOUR_SYNC_ON_STARTUP
+ | DRSUAPI_DS_REPLICA_NEIGHBOUR_DO_SCHEDULED_SYNCS
+ | DRSUAPI_DS_REPLICA_NEIGHBOUR_RETURN_OBJECT_PARENTS
+ | DRSUAPI_DS_REPLICA_NEIGHBOUR_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(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ errstr = dcerpc_errstr(tctx, p->last_fault_code);
+ }
+ printf("dcerpc_drsuapi_DsGetNCChanges failed - %s\n", errstr);
+ ret = false;
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("DsGetNCChanges failed - %s\n", win_errstr(r.out.result));
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+bool test_QuerySitesByCost(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct DsPrivate *priv)
+{
+ NTSTATUS status;
+ struct drsuapi_QuerySitesByCost r;
+ union drsuapi_QuerySitesByCostRequest req;
+ bool ret = true;
+
+ 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(mem_ctx, my_site);
+ req.req1.num_req = 2;
+ req.req1.site_to = talloc_zero_array(mem_ctx, const char *, 2);
+ req.req1.site_to[0] = talloc_strdup(mem_ctx, remote_site1);
+ req.req1.site_to[1] = talloc_strdup(mem_ctx, 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(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ errstr = dcerpc_errstr(mem_ctx, p->last_fault_code);
+ }
+ printf("drsuapi_QuerySitesByCost - %s\n", errstr);
+ ret = false;
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("QuerySitesByCost failed - %s\n", win_errstr(r.out.result));
+ ret = false;
+ }
+
+ if (W_ERROR_IS_OK(r.out.result)) {
+
+ if (!W_ERROR_EQUAL(r.out.ctr->ctr1.info[0].error_code, WERR_DS_OBJ_NOT_FOUND) ||
+ !W_ERROR_EQUAL(r.out.ctr->ctr1.info[1].error_code, WERR_DS_OBJ_NOT_FOUND)) {
+ printf("expected error_code WERR_DS_OBJ_NOT_FOUND, got %s\n",
+ win_errstr(r.out.ctr->ctr1.info[0].error_code));
+ ret = false;
+ }
+
+ if ((r.out.ctr->ctr1.info[0].site_cost != (uint32_t) -1) ||
+ (r.out.ctr->ctr1.info[1].site_cost != (uint32_t) -1)) {
+ printf("expected site_cost %d, got %d\n",
+ (uint32_t) -1, r.out.ctr->ctr1.info[0].site_cost);
+ ret = false;
+ }
+ }
+
+ return ret;
+
+
+}
+
+bool test_DsUnbind(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct DsPrivate *priv)
+{
+ NTSTATUS status;
+ struct drsuapi_DsUnbind r;
+ bool ret = true;
+
+ r.in.bind_handle = &priv->bind_handle;
+ r.out.bind_handle = &priv->bind_handle;
+
+ printf("testing DsUnbind\n");
+
+ status = dcerpc_drsuapi_DsUnbind(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ errstr = dcerpc_errstr(mem_ctx, p->last_fault_code);
+ }
+ printf("dcerpc_drsuapi_DsUnbind failed - %s\n", errstr);
+ ret = false;
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("DsBind failed - %s\n", win_errstr(r.out.result));
+ ret = false;
+ }
+
+ return ret;
+}
+
+bool torture_rpc_drsuapi(struct torture_context *torture)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p;
+ bool ret = true;
+ struct DsPrivate priv;
+ struct cli_credentials *machine_credentials;
+
+ ZERO_STRUCT(priv);
+
+ priv.join = torture_join_domain(torture, TEST_MACHINE_NAME, ACB_SVRTRUST,
+ &machine_credentials);
+ if (!priv.join) {
+ torture_fail(torture, "Failed to join as BDC");
+ }
+
+ status = torture_rpc_connection(torture,
+ &p,
+ &ndr_table_drsuapi);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_leave_domain(torture, priv.join);
+ torture_fail(torture, "Unable to connect to DRSUAPI pipe");
+ }
+
+ ret &= test_DsBind(p, torture, &priv);
+#if 0
+ ret &= test_QuerySitesByCost(p, torture, &priv);
+#endif
+ ret &= test_DsGetDomainControllerInfo(p, torture, &priv);
+
+ ret &= test_DsCrackNames(torture, p, torture, &priv);
+
+ ret &= test_DsWriteAccountSpn(p, torture, &priv);
+
+ ret &= test_DsReplicaGetInfo(p, torture, &priv);
+
+ ret &= test_DsReplicaSync(p, torture, &priv);
+
+ ret &= test_DsReplicaUpdateRefs(p, torture, &priv);
+
+ ret &= test_DsGetNCChanges(p, torture, &priv);
+
+ ret &= test_DsUnbind(p, torture, &priv);
+
+ torture_leave_domain(torture, priv.join);
+
+ return ret;
+}
+
+
+bool torture_rpc_drsuapi_cracknames(struct torture_context *torture)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p;
+ bool ret = true;
+ struct DsPrivate priv;
+ struct cli_credentials *machine_credentials;
+
+ torture_comment(torture, "Connected to DRSUAPI pipe\n");
+
+ ZERO_STRUCT(priv);
+
+ priv.join = torture_join_domain(torture, TEST_MACHINE_NAME, ACB_SVRTRUST,
+ &machine_credentials);
+ if (!priv.join) {
+ torture_fail(torture, "Failed to join as BDC\n");
+ }
+
+ status = torture_rpc_connection(torture,
+ &p,
+ &ndr_table_drsuapi);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_leave_domain(torture, priv.join);
+ torture_fail(torture, "Unable to connect to DRSUAPI pipe");
+ }
+
+ ret &= test_DsBind(p, torture, &priv);
+
+ if (ret) {
+ /* We don't care if this fails, we just need some info from it */
+ test_DsGetDomainControllerInfo(p, torture, &priv);
+
+ ret &= test_DsCrackNames(torture, p, torture, &priv);
+
+ ret &= test_DsUnbind(p, torture, &priv);
+ }
+
+ torture_leave_domain(torture, priv.join);
+
+ return ret;
+}
+
diff --git a/source4/torture/rpc/drsuapi.h b/source4/torture/rpc/drsuapi.h
new file mode 100644
index 0000000000..98438cc9b7
--- /dev/null
+++ b/source4/torture/rpc/drsuapi.h
@@ -0,0 +1,36 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ DRSUapi tests
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Stefan (metze) Metzmacher 2004
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "librpc/gen_ndr/drsuapi.h"
+
+struct DsPrivate {
+ struct policy_handle bind_handle;
+ struct GUID bind_guid;
+ 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;
+};
+
diff --git a/source4/torture/rpc/drsuapi_cracknames.c b/source4/torture/rpc/drsuapi_cracknames.c
new file mode 100644
index 0000000000..65a15c97e0
--- /dev/null
+++ b/source4/torture/rpc/drsuapi_cracknames.c
@@ -0,0 +1,1012 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ DRSUapi tests
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Stefan (metze) Metzmacher 2004
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/ndr_drsuapi_c.h"
+#include "torture/rpc/rpc.h"
+#include "ldb/include/ldb.h"
+#include "libcli/security/security.h"
+
+static bool test_DsCrackNamesMatrix(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct DsPrivate *priv, const char *dn,
+ const char *user_principal_name, const char *service_principal_name)
+{
+
+
+ NTSTATUS status;
+ bool ret = true;
+ struct drsuapi_DsCrackNames r;
+ union drsuapi_DsNameRequest req;
+ int32_t level_out;
+ union drsuapi_DsNameCtr ctr;
+
+ 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;
+ status = dcerpc_drsuapi_DsCrackNames(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ errstr = dcerpc_errstr(mem_ctx, p->last_fault_code);
+ }
+ printf("testing DsCrackNames (matrix prep) with name '%s' from format: %d desired format:%d ",
+ names[0].str, r.in.req->req1.format_offered, r.in.req->req1.format_desired);
+
+ printf("dcerpc_drsuapi_DsCrackNames failed - %s\n", errstr);
+ ret = false;
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("testing DsCrackNames (matrix prep) with name '%s' from format: %d desired format:%d ",
+ names[0].str, r.in.req->req1.format_offered, r.in.req->req1.format_desired);
+
+ printf("DsCrackNames failed - %s\n", win_errstr(r.out.result));
+ ret = false;
+ }
+
+ if (!ret) {
+ return ret;
+ }
+ switch (formats[i]) {
+ case DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL:
+ if (r.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE) {
+ printf(__location__ ": Unexpected error (%d): This name lookup should fail\n",
+ r.out.ctr->ctr1->array[0].status);
+ return false;
+ }
+ printf ("(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) {
+ printf(__location__ ": Unexpected error (%d): This name lookup should fail\n",
+ r.out.ctr->ctr1->array[0].status);
+ return false;
+ }
+ printf ("(expected) error\n");
+ break;
+ case DRSUAPI_DS_NAME_FORMAT_DNS_DOMAIN:
+ case DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY:
+ if (r.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR) {
+ printf(__location__ ": Unexpected error (%d): This name lookup should fail\n",
+ r.out.ctr->ctr1->array[0].status);
+ return false;
+ }
+ printf ("(expected) error\n");
+ break;
+ default:
+ if (r.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) {
+ printf("Error: %d\n", r.out.ctr->ctr1->array[0].status);
+ return false;
+ }
+ }
+
+ 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_SID_OR_SID_HISTORY:
+ 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]);
+ }
+ }
+
+ 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(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ errstr = dcerpc_errstr(mem_ctx, p->last_fault_code);
+ }
+ printf("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);
+ ret = false;
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("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));
+ ret = false;
+ }
+
+ if (!ret) {
+ return ret;
+ }
+ 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++) {
+ 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) {
+ printf("dcerpc_drsuapi_DsCrackNames mismatch - from %d to %d: %s should be %s\n", formats[i], formats[j], n_matrix[i][j], n_from[j]);
+ ret = false;
+ } else if (n_matrix[i][j] != NULL && n_from[j] == NULL) {
+ printf("dcerpc_drsuapi_DsCrackNames mismatch - from %d to %d: %s should be %s\n", formats[i], formats[j], n_matrix[i][j], n_from[j]);
+ ret = false;
+ } else if (strcmp(n_matrix[i][j], n_from[j]) != 0) {
+ printf("dcerpc_drsuapi_DsCrackNames mismatch - from %d to %d: %s should be %s\n", formats[i], formats[j], n_matrix[i][j], n_from[j]);
+ ret = false;
+ }
+ }
+ }
+ return ret;
+}
+
+bool test_DsCrackNames(struct torture_context *tctx,
+ struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct DsPrivate *priv)
+{
+ NTSTATUS status;
+ struct drsuapi_DsCrackNames r;
+ union drsuapi_DsNameRequest req;
+ int32_t level_out;
+ union drsuapi_DsNameCtr ctr;
+ struct drsuapi_DsNameString names[1];
+ bool ret = true;
+ 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);
+
+ 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;
+
+ printf("testing DsCrackNames with name '%s' desired format:%d\n",
+ names[0].str, r.in.req->req1.format_desired);
+
+ status = dcerpc_drsuapi_DsCrackNames(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ errstr = dcerpc_errstr(mem_ctx, p->last_fault_code);
+ }
+ printf("dcerpc_drsuapi_DsCrackNames failed - %s\n", errstr);
+ ret = false;
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("DsCrackNames failed - %s\n", win_errstr(r.out.result));
+ ret = false;
+ } else if (r.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) {
+ printf("DsCrackNames failed on name - %d\n", r.out.ctr->ctr1->array[0].status);
+ ret = false;
+ }
+
+ if (!ret) {
+ return ret;
+ }
+
+ 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;
+
+ printf("testing DsCrackNames with name '%s' desired format:%d\n",
+ names[0].str, r.in.req->req1.format_desired);
+
+ status = dcerpc_drsuapi_DsCrackNames(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ errstr = dcerpc_errstr(mem_ctx, p->last_fault_code);
+ }
+ printf("dcerpc_drsuapi_DsCrackNames failed - %s\n", errstr);
+ ret = false;
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("DsCrackNames failed - %s\n", win_errstr(r.out.result));
+ ret = false;
+ } else if (r.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) {
+ printf("DsCrackNames failed on name - %d\n", r.out.ctr->ctr1->array[0].status);
+ ret = false;
+ }
+
+ if (!ret) {
+ return ret;
+ }
+
+ 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;
+
+ printf("testing DsCrackNames with name '%s' desired format:%d\n",
+ names[0].str, r.in.req->req1.format_desired);
+
+ status = dcerpc_drsuapi_DsCrackNames(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ errstr = dcerpc_errstr(mem_ctx, p->last_fault_code);
+ }
+ printf("dcerpc_drsuapi_DsCrackNames failed - %s\n", errstr);
+ ret = false;
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("DsCrackNames failed - %s\n", win_errstr(r.out.result));
+ ret = false;
+ } else if (r.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) {
+ printf("DsCrackNames failed on name - %d\n", r.out.ctr->ctr1->array[0].status);
+ ret = false;
+ }
+
+ if (!ret) {
+ return ret;
+ }
+
+ 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) {
+ printf("local Round trip on canonical name failed: %s != %s!\n",
+ realm_canonical,
+ talloc_asprintf(mem_ctx, "%s/", dns_domain));
+ return false;
+ };
+
+ 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) {
+ printf("local Round trip on canonical ex name failed: %s != %s!\n",
+ realm_canonical,
+ talloc_asprintf(mem_ctx, "%s\n", dns_domain));
+ return false;
+ };
+
+ 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;
+
+ printf("testing DsCrackNames with name '%s' desired format:%d\n",
+ names[0].str, r.in.req->req1.format_desired);
+
+ status = dcerpc_drsuapi_DsCrackNames(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ errstr = dcerpc_errstr(mem_ctx, p->last_fault_code);
+ }
+ printf("dcerpc_drsuapi_DsCrackNames failed - %s\n", errstr);
+ ret = false;
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("DsCrackNames failed - %s\n", win_errstr(r.out.result));
+ ret = false;
+ } else if (r.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) {
+ printf("DsCrackNames failed on name - %d\n", r.out.ctr->ctr1->array[0].status);
+ ret = false;
+ }
+
+ if (!ret) {
+ return ret;
+ }
+
+ 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);
+
+ printf("testing DsCrackNames with name '%s' desired format:%d\n",
+ names[0].str, r.in.req->req1.format_desired);
+
+ status = dcerpc_drsuapi_DsCrackNames(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ errstr = dcerpc_errstr(mem_ctx, p->last_fault_code);
+ }
+ printf("dcerpc_drsuapi_DsCrackNames failed - %s\n", errstr);
+ ret = false;
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("DsCrackNames failed - %s\n", win_errstr(r.out.result));
+ ret = false;
+ } else if (r.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) {
+ printf("DsCrackNames failed on name - %d\n", r.out.ctr->ctr1->array[0].status);
+ ret = false;
+ }
+
+ if (!ret) {
+ return ret;
+ }
+
+ 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;
+
+ printf("testing DsCrackNames with name '%s' desired format:%d\n",
+ names[0].str, r.in.req->req1.format_desired);
+
+ status = dcerpc_drsuapi_DsCrackNames(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ errstr = dcerpc_errstr(mem_ctx, p->last_fault_code);
+ }
+ printf("dcerpc_drsuapi_DsCrackNames failed - %s\n", errstr);
+ ret = false;
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("DsCrackNames failed - %s\n", win_errstr(r.out.result));
+ ret = false;
+ } else if (r.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) {
+ printf("DsCrackNames failed on name - %d\n", r.out.ctr->ctr1->array[0].status);
+ ret = false;
+ }
+
+ if (!ret) {
+ return ret;
+ }
+
+ if (strcmp(priv->domain_dns_name, r.out.ctr->ctr1->array[0].dns_domain_name) != 0) {
+ printf("DsCrackNames failed to return same DNS name - expected %s got %s\n", priv->domain_dns_name, r.out.ctr->ctr1->array[0].dns_domain_name);
+ return false;
+ }
+
+ 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 includs 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 = "DISLPAY 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 serivce 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 serivce 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 serivce 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 serivce 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 = GUID_string2(mem_ctx, &priv->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 = "BUITIN 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;
+ 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' desired format:%d\n",
+ crack[i].comment, names[0].str, r.in.req->req1.format_desired);
+ } else {
+ comment = talloc_asprintf(mem_ctx, "'%s' desired format:%d\n",
+ names[0].str, r.in.req->req1.format_desired);
+ }
+ if (crack[i].skip) {
+ printf("skipping: %s", comment);
+ continue;
+ }
+ status = dcerpc_drsuapi_DsCrackNames(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ errstr = dcerpc_errstr(mem_ctx, p->last_fault_code);
+ }
+ printf("dcerpc_drsuapi_DsCrackNames failed on %s - %s\n", comment, errstr);
+ ret = false;
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("DsCrackNames failed - %s\n", win_errstr(r.out.result));
+ ret = false;
+ } 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) {
+ printf("DsCrackNames unexpected status %d, wanted %d or %d on: %s\n",
+ r.out.ctr->ctr1->array[0].status,
+ crack[i].status,
+ crack[i].alternate_status,
+ comment);
+ ret = false;
+ }
+ } else {
+ printf("DsCrackNames unexpected status %d, wanted %d on: %s\n",
+ r.out.ctr->ctr1->array[0].status,
+ crack[i].status,
+ comment);
+ ret = false;
+ }
+ } 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) {
+ printf("DsCrackNames failed - got %s, expected %s on %s\n",
+ r.out.ctr->ctr1->array[0].result_name,
+ crack[i].expected_str, comment);
+ ret = false;
+ } else {
+ printf("(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)) {
+ printf("DsCrackNames failed - got DNS name %s, expected %s on %s\n",
+ r.out.ctr->ctr1->array[0].result_name,
+ crack[i].expected_str, comment);
+ ret = false;
+ }
+ }
+ }
+
+ if (!test_DsCrackNamesMatrix(p, mem_ctx, priv, FQDN_1779_name,
+ user_principal_name, service_principal_name)) {
+ ret = false;
+ }
+
+ return ret;
+}
diff --git a/source4/torture/rpc/dssetup.c b/source4/torture/rpc/dssetup.c
new file mode 100644
index 0000000000..aa5f12eb92
--- /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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/ndr_dssetup_c.h"
+#include "torture/rpc/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;
+
+ 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(p, 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/dssync.c b/source4/torture/rpc/dssync.c
new file mode 100644
index 0000000000..847b32827b
--- /dev/null
+++ b/source4/torture/rpc/dssync.c
@@ -0,0 +1,914 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/cmdline/popt_common.h"
+#include "librpc/gen_ndr/ndr_drsuapi_c.h"
+#include "librpc/gen_ndr/ndr_drsblobs.h"
+#include "libcli/cldap/cldap.h"
+#include "libcli/ldap/ldap_client.h"
+#include "torture/torture.h"
+#include "torture/ldap/proto.h"
+#include "libcli/auth/libcli_auth.h"
+#include "../lib/crypto/crypto.h"
+#include "auth/credentials/credentials.h"
+#include "libcli/auth/libcli_auth.h"
+#include "auth/gensec/gensec.h"
+#include "param/param.h"
+#include "dsdb/samdb/samdb.h"
+
+struct DsSyncBindInfo {
+ struct dcerpc_pipe *pipe;
+ 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 ldap_connection *conn;
+};
+
+struct DsSyncTest {
+ 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 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);
+ 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;
+ }
+ ctx->drsuapi_binding->flags |= DCERPC_SIGN | DCERPC_SEAL;
+
+ ctx->ldap_url = talloc_asprintf(ctx, "ldap://%s/", ctx->drsuapi_binding->host);
+
+ /* ctx->admin ...*/
+ ctx->admin.credentials = cmdline_credentials;
+
+ 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 = cmdline_credentials;
+
+ 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 (lp_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->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;
+ }
+
+ status = dcerpc_drsuapi_DsBind(b->pipe, ctx, &b->req);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ errstr = dcerpc_errstr(ctx, b->pipe->last_fault_code);
+ }
+ 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 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 28:
+ b->peer_bind_info28 = b->req.out.bind_info->info.info28;
+ 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)
+{
+ NTSTATUS status;
+ bool ret = true;
+
+ status = torture_ldap_connection(tctx, &l->conn, ctx->ldap_url);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("failed to connect to LDAP: %s\n", ctx->ldap_url);
+ return false;
+ }
+
+ printf("connected to LDAP: %s\n", ctx->ldap_url);
+
+ status = torture_ldap_bind_sasl(l->conn, credentials, tctx->lp_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("failed to bind to LDAP:\n");
+ return false;
+ }
+ printf("bound to LDAP.\n");
+
+ return ret;
+}
+
+static bool test_GetInfo(struct torture_context *tctx, struct DsSyncTest *ctx)
+{
+ NTSTATUS status;
+ struct drsuapi_DsCrackNames r;
+ union drsuapi_DsNameRequest req;
+ union drsuapi_DsNameCtr ctr;
+ int32_t level_out = 0;
+ struct drsuapi_DsNameString names[1];
+ bool ret = true;
+ struct cldap_socket *cldap;
+ struct cldap_netlogon search;
+
+ cldap = cldap_socket_init(ctx, tctx->ev, lp_iconv_convenience(tctx->lp_ctx));
+
+ r.in.bind_handle = &ctx->admin.drsuapi.bind_handle;
+ r.in.level = 1;
+ r.in.req = &req;
+ r.in.req->req1.codepage = 1252; /* western european */
+ 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_NT4_ACCOUNT;
+ r.in.req->req1.format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779;
+ names[0].str = talloc_asprintf(ctx, "%s\\", lp_workgroup(tctx->lp_ctx));
+
+ r.out.level_out = &level_out;
+ r.out.ctr = &ctr;
+
+ status = dcerpc_drsuapi_DsCrackNames(ctx->admin.drsuapi.pipe, ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ errstr = dcerpc_errstr(ctx, ctx->admin.drsuapi.pipe->last_fault_code);
+ }
+ printf("dcerpc_drsuapi_DsCrackNames failed - %s\n", errstr);
+ return false;
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("DsCrackNames failed - %s\n", win_errstr(r.out.result));
+ return false;
+ }
+
+ ctx->domain_dn = r.out.ctr->ctr1->array[0].result_name;
+
+ ZERO_STRUCT(search);
+ search.in.dest_address = ctx->drsuapi_binding->host;
+ search.in.dest_port = lp_cldap_port(tctx->lp_ctx);
+ search.in.acct_control = -1;
+ search.in.version = NETLOGON_NT_VERSION_5 | NETLOGON_NT_VERSION_5EX;
+ search.in.map_response = true;
+ status = cldap_netlogon(cldap, ctx, &search);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ ctx->site_name = talloc_asprintf(ctx, "%s", "Default-First-Site-Name");
+ printf("cldap_netlogon() returned %s. Defaulting to Site-Name: %s\n", errstr, ctx->site_name);
+ } else {
+ ctx->site_name = talloc_steal(ctx, search.out.netlogon.data.nt5_ex.client_site);
+ printf("cldap_netlogon() returned Client Site-Name: %s.\n",ctx->site_name);
+ printf("cldap_netlogon() returned Server Site-Name: %s.\n",search.out.netlogon.data.nt5_ex.server_site);
+ }
+
+ if (!ctx->domain_dn) {
+ struct ldb_context *ldb = ldb_init(ctx, tctx->ev);
+ struct ldb_dn *dn = samdb_dns_domain_to_dn(ldb, ctx, search.out.netlogon.data.nt5_ex.dns_domain);
+ ctx->domain_dn = ldb_dn_alloc_linearized(ctx, dn);
+ talloc_free(dn);
+ talloc_free(ldb);
+ }
+
+ return ret;
+}
+
+static DATA_BLOB decrypt_blob(TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *gensec_skey,
+ bool rcrypt,
+ struct drsuapi_DsReplicaObjectIdentifier *id,
+ uint32_t rid,
+ const DATA_BLOB *buffer)
+{
+ DATA_BLOB confounder;
+ DATA_BLOB enc_buffer;
+
+ struct MD5Context md5;
+ uint8_t _enc_key[16];
+ DATA_BLOB enc_key;
+
+ DATA_BLOB dec_buffer;
+
+ uint32_t crc32_given;
+ uint32_t crc32_calc;
+ DATA_BLOB checked_buffer;
+
+ DATA_BLOB plain_buffer;
+
+ /*
+ * the combination "c[3] s[1] e[1] d[0]..."
+ * was successful!!!!!!!!!!!!!!!!!!!!!!!!!!
+ */
+
+ /*
+ * the first 16 bytes at the beginning are the confounder
+ * followed by the 4 byte crc32 checksum
+ */
+ if (buffer->length < 20) {
+ return data_blob_const(NULL, 0);
+ }
+ confounder = data_blob_const(buffer->data, 16);
+ enc_buffer = data_blob_const(buffer->data + 16, buffer->length - 16);
+
+ /*
+ * build the encryption key md5 over the session key followed
+ * by the confounder
+ *
+ * here the gensec session key is used and
+ * not the dcerpc ncacn_ip_tcp "SystemLibraryDTC" key!
+ */
+ enc_key = data_blob_const(_enc_key, sizeof(_enc_key));
+ MD5Init(&md5);
+ MD5Update(&md5, gensec_skey->data, gensec_skey->length);
+ MD5Update(&md5, confounder.data, confounder.length);
+ MD5Final(enc_key.data, &md5);
+
+ /*
+ * copy the encrypted buffer part and
+ * decrypt it using the created encryption key using arcfour
+ */
+ dec_buffer = data_blob_talloc(mem_ctx, enc_buffer.data, enc_buffer.length);
+ if (!dec_buffer.data) {
+ return data_blob_const(NULL, 0);
+ }
+ arcfour_crypt_blob(dec_buffer.data, dec_buffer.length, &enc_key);
+
+ /*
+ * the first 4 byte are the crc32 checksum
+ * of the remaining bytes
+ */
+ crc32_given = IVAL(dec_buffer.data, 0);
+ crc32_calc = crc32_calc_buffer(dec_buffer.data + 4 , dec_buffer.length - 4);
+ if (crc32_given != crc32_calc) {
+ DEBUG(0,("CRC32: given[0x%08X] calc[0x%08X]\n",
+ crc32_given, crc32_calc));
+ return data_blob_const(NULL, 0);
+ }
+ checked_buffer = data_blob_talloc(mem_ctx, dec_buffer.data + 4, dec_buffer.length - 4);
+ if (!checked_buffer.data) {
+ return data_blob_const(NULL, 0);
+ }
+
+ /*
+ * some attributes seem to be in a usable form after this decryption
+ * (supplementalCredentials, priorValue, currentValue, trustAuthOutgoing,
+ * trustAuthIncoming, initialAuthOutgoing, initialAuthIncoming)
+ * At least supplementalCredentials contains plaintext
+ * like "Primary:Kerberos" (in unicode form)
+ *
+ * some attributes seem to have some additional encryption
+ * dBCSPwd, unicodePwd, ntPwdHistory, lmPwdHistory
+ *
+ * it's the sam_rid_crypt() function, as the value is constant,
+ * so it doesn't depend on sessionkeys.
+ */
+ if (rcrypt) {
+ uint32_t i, num_hashes;
+
+ if ((checked_buffer.length % 16) != 0) {
+ return data_blob_const(NULL, 0);
+ }
+
+ plain_buffer = data_blob_talloc(mem_ctx, checked_buffer.data, checked_buffer.length);
+ if (!plain_buffer.data) {
+ return data_blob_const(NULL, 0);
+ }
+
+ num_hashes = plain_buffer.length / 16;
+ for (i = 0; i < num_hashes; i++) {
+ uint32_t offset = i * 16;
+ sam_rid_crypt(rid, checked_buffer.data + offset, plain_buffer.data + offset, 0);
+ }
+ } else {
+ plain_buffer = checked_buffer;
+ }
+
+ return plain_buffer;
+}
+
+static void test_analyse_objects(struct torture_context *tctx,
+ struct DsSyncTest *ctx,
+ const DATA_BLOB *gensec_skey,
+ struct drsuapi_DsReplicaObjectListItemEx *cur)
+{
+ static uint32_t object_id;
+ const char *save_values_dir;
+
+ if (!lp_parm_bool(tctx->lp_ctx, NULL, "dssync", "print_pwd_blobs", false)) {
+ return;
+ }
+
+ save_values_dir = lp_parm_string(tctx->lp_ctx, NULL, "dssync", "save_pwd_blobs_dir");
+
+ for (; cur; cur = cur->next_object) {
+ const char *dn;
+ struct dom_sid *sid = NULL;
+ uint32_t rid = 0;
+ bool dn_printed = false;
+ uint32_t i;
+
+ if (!cur->object.identifier) continue;
+
+ dn = cur->object.identifier->dn;
+ if (cur->object.identifier->sid.num_auths > 0) {
+ sid = &cur->object.identifier->sid;
+ rid = sid->sub_auths[sid->num_auths - 1];
+ }
+
+ for (i=0; i < cur->object.attribute_ctr.num_attributes; i++) {
+ const char *name = NULL;
+ bool rcrypt = false;
+ DATA_BLOB *enc_data = 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_ATTRIBUTE_dBCSPwd:
+ name = "dBCSPwd";
+ rcrypt = true;
+ break;
+ case DRSUAPI_ATTRIBUTE_unicodePwd:
+ name = "unicodePwd";
+ rcrypt = true;
+ break;
+ case DRSUAPI_ATTRIBUTE_ntPwdHistory:
+ name = "ntPwdHistory";
+ rcrypt = true;
+ break;
+ case DRSUAPI_ATTRIBUTE_lmPwdHistory:
+ name = "lmPwdHistory";
+ rcrypt = true;
+ break;
+ case DRSUAPI_ATTRIBUTE_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_ATTRIBUTE_priorValue:
+ name = "priorValue";
+ break;
+ case DRSUAPI_ATTRIBUTE_currentValue:
+ name = "currentValue";
+ break;
+ case DRSUAPI_ATTRIBUTE_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_ATTRIBUTE_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_ATTRIBUTE_initialAuthOutgoing:
+ name = "initialAuthOutgoing";
+ break;
+ case DRSUAPI_ATTRIBUTE_initialAuthIncoming:
+ name = "initialAuthIncoming";
+ break;
+ default:
+ continue;
+ }
+
+ if (attr->value_ctr.num_values != 1) continue;
+
+ if (!attr->value_ctr.values[0].blob) continue;
+
+ enc_data = attr->value_ctr.values[0].blob;
+ ZERO_STRUCT(plain_data);
+
+ plain_data = decrypt_blob(ctx, gensec_skey, rcrypt,
+ cur->object.identifier, rid,
+ enc_data);
+ if (!dn_printed) {
+ object_id++;
+ DEBUG(0,("DN[%u] %s\n", object_id, dn));
+ dn_printed = true;
+ }
+ DEBUGADD(0,("ATTR: %s enc.length=%lu plain.length=%lu\n",
+ name, (long)enc_data->length, (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,
+ lp_iconv_convenience(tctx->lp_ctx), ptr,
+ pull_fn);
+ if (NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ ndr_print_debug(print_fn, name, ptr);
+ } else {
+ DEBUG(0, ("Failed to decode %s\n", name));
+ }
+ }
+ } else {
+ dump_data(0, enc_data->data, enc_data->length);
+ }
+ talloc_free(ptr);
+ }
+ }
+}
+
+static bool test_FetchData(struct torture_context *tctx, struct DsSyncTest *ctx)
+{
+ NTSTATUS status;
+ bool ret = true;
+ int i, y = 0;
+ uint64_t highest_usn = 0;
+ const char *partition = NULL;
+ struct drsuapi_DsGetNCChanges r;
+ union drsuapi_DsGetNCChangesRequest req;
+ struct drsuapi_DsReplicaObjectIdentifier nc;
+ struct drsuapi_DsGetNCChangesCtr1 *ctr1 = NULL;
+ struct drsuapi_DsGetNCChangesCtr6 *ctr6 = NULL;
+ int32_t out_level = 0;
+ struct GUID null_guid;
+ struct dom_sid null_sid;
+ DATA_BLOB gensec_skey;
+ struct {
+ int32_t level;
+ } array[] = {
+/* {
+ 5
+ },
+*/ {
+ 8
+ }
+ };
+
+ ZERO_STRUCT(null_guid);
+ ZERO_STRUCT(null_sid);
+
+ partition = lp_parm_string(tctx->lp_ctx, NULL, "dssync", "partition");
+ if (partition == NULL) {
+ partition = ctx->domain_dn;
+ printf("dssync:partition not specified, defaulting to %s.\n", ctx->domain_dn);
+ }
+
+ highest_usn = lp_parm_int(tctx->lp_ctx, NULL, "dssync", "highest_usn", 0);
+
+ array[0].level = lp_parm_int(tctx->lp_ctx, NULL, "dssync", "get_nc_changes_level", array[0].level);
+
+ if (lp_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.pipe->conn->security_state.generic_state,
+ &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 = null_guid;
+ nc.sid = null_sid;
+ nc.dn = partition;
+
+ r.in.req = &req;
+ r.in.req->req5.destination_dsa_guid = ctx->new_dc.invocation_id;
+ r.in.req->req5.source_dsa_invocation_id = null_guid;
+ 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 (lp_parm_bool(tctx->lp_ctx, NULL, "dssync", "compression", false)) {
+ r.in.req->req5.replica_flags |= DRSUAPI_DS_REPLICA_NEIGHBOUR_COMPRESS_CHANGES;
+ }
+ if (lp_parm_bool(tctx->lp_ctx, NULL, "dssync", "neighbour_writeable", true)) {
+ r.in.req->req5.replica_flags |= DRSUAPI_DS_REPLICA_NEIGHBOUR_WRITEABLE;
+ }
+ r.in.req->req5.replica_flags |= DRSUAPI_DS_REPLICA_NEIGHBOUR_SYNC_ON_STARTUP
+ | DRSUAPI_DS_REPLICA_NEIGHBOUR_DO_SCHEDULED_SYNCS
+ | DRSUAPI_DS_REPLICA_NEIGHBOUR_RETURN_OBJECT_PARENTS
+ | DRSUAPI_DS_REPLICA_NEIGHBOUR_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 = null_guid;
+ nc.sid = null_sid;
+ nc.dn = partition;
+ /* 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 = null_guid;
+ 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 (lp_parm_bool(tctx->lp_ctx, NULL, "dssync", "compression", false)) {
+ r.in.req->req8.replica_flags |= DRSUAPI_DS_REPLICA_NEIGHBOUR_COMPRESS_CHANGES;
+ }
+ if (lp_parm_bool(tctx->lp_ctx, NULL, "dssync", "neighbour_writeable", true)) {
+ r.in.req->req8.replica_flags |= DRSUAPI_DS_REPLICA_NEIGHBOUR_WRITEABLE;
+ }
+ r.in.req->req8.replica_flags |= DRSUAPI_DS_REPLICA_NEIGHBOUR_SYNC_ON_STARTUP
+ | DRSUAPI_DS_REPLICA_NEIGHBOUR_DO_SCHEDULED_SYNCS
+ | DRSUAPI_DS_REPLICA_NEIGHBOUR_RETURN_OBJECT_PARENTS
+ | DRSUAPI_DS_REPLICA_NEIGHBOUR_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;
+ }
+
+ printf("Dumping AD partition: %s\n", nc.dn);
+ for (y=0; ;y++) {
+ int32_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) {
+ DEBUG(0,("start[%d] tmp_higest_usn: %llu , highest_usn: %llu\n",y,
+ (long long)r.in.req->req5.highwatermark.tmp_highest_usn,
+ (long long)r.in.req->req5.highwatermark.highest_usn));
+ }
+
+ if (r.in.level == 8) {
+ DEBUG(0,("start[%d] tmp_higest_usn: %llu , highest_usn: %llu\n",y,
+ (long long)r.in.req->req8.highwatermark.tmp_highest_usn,
+ (long long)r.in.req->req8.highwatermark.highest_usn));
+ }
+
+ status = dcerpc_drsuapi_DsGetNCChanges(ctx->new_dc.drsuapi.pipe, ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ errstr = dcerpc_errstr(ctx, ctx->new_dc.drsuapi.pipe->last_fault_code);
+ }
+ printf("dcerpc_drsuapi_DsGetNCChanges failed - %s\n", errstr);
+ ret = false;
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("DsGetNCChanges failed - %s\n", win_errstr(r.out.result));
+ ret = false;
+ }
+
+ 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) {
+ DEBUG(0,("end[%d] tmp_highest_usn: %llu , highest_usn: %llu\n",y,
+ (long long)ctr1->new_highwatermark.tmp_highest_usn,
+ (long long)ctr1->new_highwatermark.highest_usn));
+
+ test_analyse_objects(tctx, ctx, &gensec_skey, ctr1->first_object);
+
+ 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_XPRESS
+ && r.out.ctr->ctr7.ctr.xpress6.ts) {
+ out_level = 6;
+ ctr6 = &r.out.ctr->ctr7.ctr.xpress6.ts->ctr6;
+ }
+
+ if (out_level == 6) {
+ DEBUG(0,("end[%d] tmp_highest_usn: %llu , highest_usn: %llu\n",y,
+ (long long)ctr6->new_highwatermark.tmp_highest_usn,
+ (long long)ctr6->new_highwatermark.highest_usn));
+
+ test_analyse_objects(tctx, ctx, &gensec_skey, ctr6->first_object);
+
+ if (ctr6->more_data) {
+ r.in.req->req8.highwatermark = ctr6->new_highwatermark;
+ continue;
+ }
+ }
+
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_FetchNT4Data(struct torture_context *tctx,
+ struct DsSyncTest *ctx)
+{
+ NTSTATUS status;
+ bool ret = true;
+ struct drsuapi_DsGetNT4ChangeLog r;
+ union drsuapi_DsGetNT4ChangeLogRequest req;
+ union drsuapi_DsGetNT4ChangeLogInfo info;
+ uint32_t level_out = 0;
+ struct GUID null_guid;
+ struct dom_sid null_sid;
+ DATA_BLOB cookie;
+
+ ZERO_STRUCT(null_guid);
+ ZERO_STRUCT(null_sid);
+ 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.unknown1 = lp_parm_int(tctx->lp_ctx, NULL, "dssync", "nt4-1", 3);
+ req.req1.unknown2 = lp_parm_int(tctx->lp_ctx, NULL, "dssync", "nt4-2", 0x00004000);
+
+ while (1) {
+ req.req1.length = cookie.length;
+ req.req1.data = cookie.data;
+
+ r.in.req = &req;
+
+ status = dcerpc_drsuapi_DsGetNT4ChangeLog(ctx->new_dc.drsuapi.pipe, ctx, &r);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
+ printf("DsGetNT4ChangeLog not supported by target server\n");
+ break;
+ } else if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ errstr = dcerpc_errstr(ctx, ctx->new_dc.drsuapi.pipe->last_fault_code);
+ }
+ printf("dcerpc_drsuapi_DsGetNT4ChangeLog failed - %s\n", errstr);
+ ret = false;
+ } else if (W_ERROR_EQUAL(r.out.result, WERR_INVALID_DOMAIN_ROLE)) {
+ printf("DsGetNT4ChangeLog not supported by target server\n");
+ break;
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("DsGetNT4ChangeLog failed - %s\n", win_errstr(r.out.result));
+ ret = false;
+ } else if (*r.out.level_out != 1) {
+ printf("DsGetNT4ChangeLog unknown level - %u\n", *r.out.level_out);
+ ret = false;
+ } 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.length1;
+ cookie.data = r.out.info->info1.data1;
+ continue;
+ } else {
+ printf("DsGetNT4ChangeLog failed - %s\n", nt_errstr(r.out.info->info1.status));
+ ret = false;
+ }
+
+ break;
+ }
+
+ return ret;
+}
+
+bool torture_rpc_dssync(struct torture_context *torture)
+{
+ bool ret = true;
+ TALLOC_CTX *mem_ctx;
+ struct DsSyncTest *ctx;
+
+ mem_ctx = talloc_init("torture_rpc_dssync");
+ ctx = test_create_context(torture);
+
+ ret &= _test_DsBind(torture, ctx, ctx->admin.credentials, &ctx->admin.drsuapi);
+ if (!ret) {
+ return ret;
+ }
+ ret &= test_LDAPBind(torture, ctx, ctx->admin.credentials, &ctx->admin.ldap);
+ if (!ret) {
+ return ret;
+ }
+ ret &= test_GetInfo(torture, ctx);
+ ret &= _test_DsBind(torture, ctx, ctx->new_dc.credentials, &ctx->new_dc.drsuapi);
+ if (!ret) {
+ return ret;
+ }
+ ret &= test_FetchData(torture, ctx);
+ ret &= test_FetchNT4Data(torture, ctx);
+
+ return ret;
+}
diff --git a/source4/torture/rpc/echo.c b/source4/torture/rpc/echo.c
new file mode 100644
index 0000000000..840ce5485c
--- /dev/null
+++ b/source4/torture/rpc/echo.c
@@ -0,0 +1,452 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "torture/rpc/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; \
+ status = dcerpc_echo_AddOne(p, tctx, &r); \
+ torture_assert_ntstatus_ok(tctx, status, 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;
+ NTSTATUS status;
+ uint32_t n;
+ struct echo_AddOne r;
+
+ 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;
+ NTSTATUS status;
+ uint8_t *data_in, *data_out;
+ int len;
+ struct echo_EchoData r;
+
+ 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;i<len;i++) {
+ data_in[i] = i;
+ }
+
+ r.in.len = len;
+ r.in.in_data = data_in;
+
+ status = dcerpc_echo_EchoData(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, talloc_asprintf(tctx,
+ "EchoData(%d) failed\n", len));
+
+ data_out = r.out.out_data;
+
+ for (i=0;i<len;i++) {
+ if (data_in[i] != data_out[i]) {
+ torture_comment(tctx, "Bad data returned for len %d at offset %d\n",
+ len, i);
+ torture_comment(tctx, "in:\n");
+ dump_data(0, data_in+i, MIN(len-i, 16));
+ torture_comment(tctx, "out:\n");
+ dump_data(0, data_out+i, MIN(len-1, 16));
+ return false;
+ }
+ }
+ return true;
+}
+
+/*
+ test the SourceData interface
+*/
+static bool test_sourcedata(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ int i;
+ NTSTATUS status;
+ int len;
+ struct echo_SourceData r;
+
+ 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;
+
+ status = dcerpc_echo_SourceData(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, talloc_asprintf(tctx,
+ "SourceData(%d) failed", len));
+
+ for (i=0;i<len;i++) {
+ uint8_t *v = (uint8_t *)r.out.data;
+ torture_assert(tctx, v[i] == (i & 0xFF),
+ talloc_asprintf(tctx,
+ "bad data 0x%x at %d\n", (uint8_t)r.out.data[i], i));
+ }
+ return true;
+}
+
+/*
+ test the SinkData interface
+*/
+static bool test_sinkdata(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ int i;
+ NTSTATUS status;
+ uint8_t *data_in;
+ int len;
+ struct echo_SinkData r;
+
+ 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;i<len;i++) {
+ data_in[i] = i+1;
+ }
+
+ r.in.len = len;
+ r.in.data = data_in;
+
+ status = dcerpc_echo_SinkData(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, talloc_asprintf(tctx,
+ "SinkData(%d) failed",
+ len));
+
+ torture_comment(tctx, "sunk %d bytes\n", len);
+ return true;
+}
+
+
+/*
+ test the testcall interface
+*/
+static bool test_testcall(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct echo_TestCall r;
+ const char *s = NULL;
+
+ r.in.s1 = "input string";
+ r.out.s2 = &s;
+
+ status = dcerpc_echo_TestCall(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "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)
+{
+ NTSTATUS status;
+ struct echo_TestCall2 r;
+ int i;
+
+ 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);
+ status = dcerpc_echo_TestCall2(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "TestCall2 failed");
+ }
+ return true;
+}
+
+/*
+ test the TestSleep interface
+*/
+static bool test_sleep(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ int i;
+ NTSTATUS status;
+#define ASYNC_COUNT 3
+ struct rpc_request *req[ASYNC_COUNT];
+ struct echo_TestSleep r[ASYNC_COUNT];
+ bool done[ASYNC_COUNT];
+ struct timeval snd[ASYNC_COUNT];
+ struct timeval rcv[ASYNC_COUNT];
+ struct timeval diff[ASYNC_COUNT];
+ struct tevent_context *ctx;
+ int total_done = 0;
+
+ 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");
+
+ for (i=0;i<ASYNC_COUNT;i++) {
+ done[i] = false;
+ snd[i] = timeval_current();
+ rcv[i] = timeval_zero();
+ r[i].in.seconds = ASYNC_COUNT-i;
+ req[i] = dcerpc_echo_TestSleep_send(p, tctx, &r[i]);
+ torture_assert(tctx, req[i], "Failed to send async sleep request\n");
+ }
+
+ ctx = dcerpc_event_context(p);
+ while (total_done < ASYNC_COUNT) {
+ torture_assert(tctx, event_loop_once(ctx) == 0,
+ "Event context loop failed");
+ for (i=0;i<ASYNC_COUNT;i++) {
+ if (done[i] == false && req[i]->state == RPC_REQUEST_DONE) {
+ int rounded_tdiff;
+ total_done++;
+ done[i] = true;
+ rcv[i] = timeval_current();
+ diff[i] = timeval_until(&snd[i], &rcv[i]);
+ rounded_tdiff = (int)(0.5 + diff[i].tv_sec + (1.0e-6*diff[i].tv_usec));
+ status = dcerpc_ndr_request_recv(req[i]);
+ torture_comment(tctx, "rounded_tdiff=%d\n", rounded_tdiff);
+ torture_assert_ntstatus_ok(tctx, status,
+ talloc_asprintf(tctx, "TestSleep(%d) failed", i));
+ torture_assert(tctx, r[i].out.result == r[i].in.seconds,
+ talloc_asprintf(tctx, "Failed - Asked to sleep for %u seconds (server replied with %u seconds and the reply takes only %u seconds)",
+ r[i].out.result, r[i].in.seconds, (uint_t)diff[i].tv_sec));
+ torture_assert(tctx, r[i].out.result <= rounded_tdiff,
+ talloc_asprintf(tctx, "Failed - Slept for %u seconds (but reply takes only %u.%06u seconds)",
+ r[i].out.result, (uint_t)diff[i].tv_sec, (uint_t)diff[i].tv_usec));
+ if (r[i].out.result+1 == rounded_tdiff) {
+ torture_comment(tctx, "Slept for %u seconds (but reply takes %u.%06u seconds - busy server?)\n",
+ r[i].out.result, (uint_t)diff[i].tv_sec, (uint_t)diff[i].tv_usec);
+ } else if (r[i].out.result == rounded_tdiff) {
+ torture_comment(tctx, "Slept for %u seconds (reply takes %u.%06u seconds - ok)\n",
+ r[i].out.result, (uint_t)diff[i].tv_sec, (uint_t)diff[i].tv_usec);
+ } else {
+ torture_comment(tctx, "(Failed) - Not async - Slept for %u seconds (but reply takes %u.%06u seconds)",
+ r[i].out.result, (uint_t)diff[i].tv_sec, (uint_t)diff[i].tv_usec);
+ /* TODO: let the test fail here, when we support async rpc on ncacn_np */
+ }
+ }
+ }
+ }
+ torture_comment(tctx, "\n");
+ return true;
+}
+
+/*
+ test enum handling
+*/
+static bool test_enum(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct echo_TestEnum r;
+ enum echo_Enum1 v = ECHO_ENUM1;
+ struct echo_Enum2 e2;
+ union echo_Enum3 e3;
+
+ 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;
+
+ status = dcerpc_echo_TestEnum(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "TestEnum failed");
+ return true;
+}
+
+/*
+ test surrounding conformant array handling
+*/
+static bool test_surrounding(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct echo_TestSurrounding r;
+
+ 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);
+
+ status = dcerpc_echo_TestSurrounding(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "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)
+{
+ NTSTATUS status;
+ struct echo_TestDoublePointer r;
+ uint16_t value = 12;
+ uint16_t *pvalue = &value;
+ uint16_t **ppvalue = &pvalue;
+
+ ZERO_STRUCT(r);
+ r.in.data = &ppvalue;
+
+ status = dcerpc_echo_TestDoublePointer(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "TestDoublePointer failed");
+
+ torture_assert_int_equal(tctx, value, r.out.result,
+ "TestDoublePointer did not return original value");
+ return true;
+}
+
+
+/*
+ test request timeouts
+*/
+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_ndr_request_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_ndr_request_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;
+}
+
+
+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);
+ torture_rpc_tcase_add_test(tcase, "timeout", test_timeout);
+
+ return suite;
+}
diff --git a/source4/torture/rpc/epmapper.c b/source4/torture/rpc/epmapper.c
new file mode 100644
index 0000000000..e99b3b1354
--- /dev/null
+++ b/source4/torture/rpc/epmapper.c
@@ -0,0 +1,275 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/ndr_epmapper_c.h"
+#include "librpc/ndr/ndr_table.h"
+#include "librpc/rpc/dcerpc_proto.h"
+#include "torture/rpc/rpc.h"
+
+
+/*
+ display any protocol tower
+ */
+static void display_tower(TALLOC_CTX *mem_ctx, struct epm_tower *twr)
+{
+ int i;
+
+ for (i=0;i<twr->num_floors;i++) {
+ printf(" %s", epm_floor_string(mem_ctx, &twr->floors[i]));
+ }
+ printf("\n");
+}
+
+
+static bool test_Map(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct epm_twr_t *twr)
+{
+ NTSTATUS status;
+ struct epm_Map r;
+ struct GUID uuid;
+ struct policy_handle handle;
+ int i;
+ struct ndr_syntax_id syntax;
+ uint32_t num_towers;
+
+ ZERO_STRUCT(uuid);
+ 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 = 100;
+ r.out.num_towers = &num_towers;
+
+ dcerpc_floor_get_lhs_data(&twr->tower.floors[0], &syntax);
+
+ printf("epm_Map results for '%s':\n",
+ ndr_interface_name(&syntax.uuid, syntax.if_version));
+
+ 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;
+
+ 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;
+
+ 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(p, mem_ctx, &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(mem_ctx, &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(p, mem_ctx, &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(mem_ctx, &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.http.port = 0;
+
+ status = dcerpc_epm_Map(p, mem_ctx, &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(mem_ctx, &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(p, mem_ctx, &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(mem_ctx, &r.out.towers[i].twr->tower);
+ }
+ }
+ }
+
+ /* FIXME: Extend to do other protocols as well (ncacn_unix_stream, ncalrpc) */
+
+ return true;
+}
+
+static bool test_Lookup(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct epm_Lookup r;
+ struct GUID uuid;
+ struct rpc_if_id_t iface;
+ struct policy_handle handle;
+ uint32_t num_ents;
+
+ ZERO_STRUCT(handle);
+
+ r.in.inquiry_type = 0;
+ r.in.object = &uuid;
+ r.in.interface_id = &iface;
+ r.in.vers_option = 0;
+ r.in.entry_handle = &handle;
+ r.out.entry_handle = &handle;
+ r.in.max_ents = 10;
+ r.out.num_ents = &num_ents;
+
+ do {
+ int i;
+
+ ZERO_STRUCT(uuid);
+ ZERO_STRUCT(iface);
+
+ status = dcerpc_epm_Lookup(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status) || r.out.result != 0) {
+ break;
+ }
+
+ printf("epm_Lookup returned %d events GUID %s\n",
+ *r.out.num_ents, GUID_string(tctx, &handle.uuid));
+
+ for (i=0;i<*r.out.num_ents;i++) {
+ printf("\nFound '%s'\n", r.out.entries[i].annotation);
+ display_tower(tctx, &r.out.entries[i].tower->tower);
+ if (r.out.entries[i].tower->tower.num_floors == 5) {
+ test_Map(p, tctx, r.out.entries[i].tower);
+ }
+ }
+ } while (NT_STATUS_IS_OK(status) &&
+ r.out.result == 0 &&
+ *r.out.num_ents == r.in.max_ents &&
+ !policy_handle_empty(&handle));
+
+ torture_assert_ntstatus_ok(tctx, status, "Lookup failed");
+
+ return true;
+}
+
+static bool test_Delete(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, struct epm_entry_t *entries)
+{
+ NTSTATUS status;
+ struct epm_Delete r;
+
+ r.in.num_ents = 1;
+ r.in.entries = entries;
+
+ status = dcerpc_epm_Delete(p, mem_ctx, &r);
+ if (NT_STATUS_IS_ERR(status)) {
+ printf("Delete failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if (r.out.result != 0) {
+ printf("Delete failed - %d\n", r.out.result);
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_Insert(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct epm_Insert r;
+ struct dcerpc_binding *bd;
+
+ 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]", &bd);
+ 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, bd, &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(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "Insert failed");
+
+ torture_assert(tctx, r.out.result == 0, "Insert failed");
+
+ if (!test_Delete(p, tctx, r.in.entries)) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_InqObject(struct torture_context *tctx, struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct epm_InqObject r;
+
+ r.in.epm_object = talloc(tctx, struct GUID);
+ *r.in.epm_object = ndr_table_epmapper.syntax_id.uuid;
+
+ status = dcerpc_epm_InqObject(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "InqObject failed");
+
+ return true;
+}
+
+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);
+
+ torture_rpc_tcase_add_test(tcase, "Lookup", test_Lookup);
+ torture_rpc_tcase_add_test(tcase, "Insert", test_Insert);
+ torture_rpc_tcase_add_test(tcase, "InqObject", test_InqObject);
+
+ return suite;
+}
diff --git a/source4/torture/rpc/eventlog.c b/source4/torture/rpc/eventlog.c
new file mode 100644
index 0000000000..865b4e2217
--- /dev/null
+++ b/source4/torture/rpc/eventlog.c
@@ -0,0 +1,459 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/ndr_eventlog.h"
+#include "librpc/gen_ndr/ndr_eventlog_c.h"
+#include "librpc/gen_ndr/ndr_lsa.h"
+#include "torture/rpc/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_pipe *p,
+ 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(p, 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;
+
+ if (!get_policy_handle(tctx, p, &handle))
+ return false;
+
+ ZERO_STRUCT(r);
+ r.in.handle = &handle;
+ r.out.number = &number;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_eventlog_GetNumRecords(p, tctx, &r),
+ "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(p, tctx, &cr),
+ "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;
+
+ uint32_t sent_size = 0;
+ uint32_t real_size = 0;
+
+ if (!get_policy_handle(tctx, p, &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;
+
+ status = dcerpc_eventlog_ReadEventLogW(p, tctx, &r);
+
+ torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_INVALID_PARAMETER,
+ "ReadEventLog failed");
+
+ while (1) {
+ DATA_BLOB blob;
+ 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;
+
+ status = dcerpc_eventlog_ReadEventLogW(p, tctx, &r);
+
+ 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);
+
+ status = dcerpc_eventlog_ReadEventLogW(p, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "ReadEventLog failed");
+
+ /* Decode a user-marshalled record */
+ size = IVAL(r.out.data, pos);
+
+ while (size > 0) {
+
+ 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,
+ lp_iconv_convenience(tctx->lp_ctx), &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, status,
+ "ReadEventLog failed parsing event log record");
+
+ r.in.offset++;
+ }
+
+ cr.in.handle = cr.out.handle = &handle;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_eventlog_CloseEventLog(p, tctx, &cr),
+ "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;
+
+ uint32_t record_number = 0;
+ time_t time_written = 0;
+ struct lsa_String servername, *strings;
+
+ if (!get_policy_handle(tctx, p, &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.out.record_number = &record_number;
+ r.out.time_written = &time_written;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_eventlog_ReportEventW(p, 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(p, tctx, &cr),
+ "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;
+
+ if (!get_policy_handle(tctx, p, &handle))
+ return false;
+
+ r.in.handle = &handle;
+
+ /* Huh? Does this RPC always return access denied? */
+ torture_assert_ntstatus_equal(tctx,
+ dcerpc_eventlog_FlushEventLog(p, tctx, &r),
+ NT_STATUS_ACCESS_DENIED,
+ "FlushEventLog failed");
+
+ cr.in.handle = cr.out.handle = &handle;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_eventlog_CloseEventLog(p, tctx, &cr),
+ "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;
+
+ if (!get_policy_handle(tctx, p, &handle))
+ return false;
+
+ r.in.handle = &handle;
+ r.in.backupfile = NULL;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_eventlog_ClearEventLogW(p, tctx, &r),
+ "ClearEventLog failed");
+
+ cr.in.handle = cr.out.handle = &handle;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_eventlog_CloseEventLog(p, tctx, &cr),
+ "CloseEventLog failed");
+
+ return true;
+}
+
+static bool test_GetLogInformation(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct eventlog_GetLogInformation r;
+ struct eventlog_CloseEventLog cr;
+ struct policy_handle handle;
+ uint32_t bytes_needed = 0;
+
+ if (!get_policy_handle(tctx, p, &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;
+
+ status = dcerpc_eventlog_GetLogInformation(p, tctx, &r);
+
+ torture_assert_ntstatus_equal(tctx, status, NT_STATUS_INVALID_LEVEL,
+ "GetLogInformation failed");
+
+ r.in.level = 0;
+
+ status = dcerpc_eventlog_GetLogInformation(p, tctx, &r);
+
+ torture_assert_ntstatus_equal(tctx, status, NT_STATUS_BUFFER_TOO_SMALL,
+ "GetLogInformation failed");
+
+ r.in.buf_size = bytes_needed;
+ r.out.buffer = talloc_array(tctx, uint8_t, bytes_needed);
+
+ status = dcerpc_eventlog_GetLogInformation(p, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "GetLogInformation failed");
+
+ cr.in.handle = cr.out.handle = &handle;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_eventlog_CloseEventLog(p, tctx, &cr),
+ "CloseEventLog failed");
+
+ return true;
+}
+
+
+static bool test_OpenEventLog(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct policy_handle handle;
+ struct eventlog_CloseEventLog cr;
+
+ if (!get_policy_handle(tctx, p, &handle))
+ return false;
+
+ cr.in.handle = cr.out.handle = &handle;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_eventlog_CloseEventLog(p, tctx, &cr),
+ "CloseEventLog failed");
+
+ return true;
+}
+
+static bool test_BackupLog(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct policy_handle handle, backup_handle;
+ struct eventlog_BackupEventLogW r;
+ struct eventlog_OpenBackupEventLogW b;
+ struct eventlog_CloseEventLog cr;
+ const char *tmp;
+ struct lsa_String backup_filename;
+ struct eventlog_OpenUnknown0 unknown0;
+
+ if (!get_policy_handle(tctx, p, &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;
+
+ status = dcerpc_eventlog_BackupEventLogW(p, tctx, &r);
+ torture_assert_ntstatus_equal(tctx, status,
+ 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;
+
+ status = dcerpc_eventlog_BackupEventLogW(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "BackupEventLogW failed");
+
+ status = dcerpc_eventlog_BackupEventLogW(p, tctx, &r);
+ torture_assert_ntstatus_equal(tctx, status,
+ NT_STATUS_OBJECT_NAME_COLLISION, "BackupEventLogW failed");
+
+ cr.in.handle = cr.out.handle = &handle;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_eventlog_CloseEventLog(p, tctx, &cr),
+ "BackupLog failed");
+
+ unknown0.unknown0 = 0x005c;
+ unknown0.unknown1 = 0x0001;
+
+ b.in.unknown0 = &unknown0;
+ b.in.backup_logname = &backup_filename;
+ b.in.major_version = 1;
+ b.in.minor_version = 1;
+ b.out.handle = &backup_handle;
+
+ status = dcerpc_eventlog_OpenBackupEventLogW(p, tctx, &b);
+
+ torture_assert_ntstatus_ok(tctx, status, "OpenBackupEventLogW failed");
+
+ cr.in.handle = cr.out.handle = &backup_handle;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_eventlog_CloseEventLog(p, tctx, &cr),
+ "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/frsapi.c b/source4/torture/rpc/frsapi.c
new file mode 100644
index 0000000000..c8e421a674
--- /dev/null
+++ b/source4/torture/rpc/frsapi.c
@@ -0,0 +1,275 @@
+/*
+ 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/torture.h"
+#include "torture/rpc/rpc.h"
+#include "librpc/gen_ndr/ndr_frsapi_c.h"
+#include "torture/util.h"
+#include "param/param.h"
+
+static bool test_GetDsPollingIntervalW(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ 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(p, 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_pipe *p,
+ 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(p, 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)
+{
+ uint32_t i1, i2, i3;
+ uint32_t k1, k2, k3;
+
+ if (!test_GetDsPollingIntervalW(tctx, p, &i1, &i2, &i3)) {
+ return false;
+ }
+
+ if (!test_SetDsPollingIntervalW(tctx, p, i1, i2, i3)) {
+ return false;
+ }
+
+ k1 = i1;
+ k2 = k3 = 0;
+
+ if (!test_SetDsPollingIntervalW(tctx, p, k1, k2, k3)) {
+ return false;
+ }
+
+ if (!test_GetDsPollingIntervalW(tctx, p, &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_pipe *p,
+ const char *path,
+ uint32_t type,
+ WERROR werr)
+{
+ struct frsapi_IsPathReplicated r;
+ struct GUID guid;
+ uint32_t unknown1, unknown2, unknown3 = 0;
+
+ ZERO_STRUCT(r);
+
+ r.in.path = path;
+ r.in.replica_set_type = type;
+ r.out.unknown1 = &unknown1;
+ r.out.unknown2 = &unknown2;
+ r.out.unknown3 = &unknown3;
+ r.out.replica_set_guid = &guid;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_frsapi_IsPathReplicated(p, 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_pipe *p,
+ const char *path,
+ uint32_t type)
+{
+ return test_IsPathReplicated_err(tctx, p, path, type, WERR_OK);
+}
+
+static bool test_IsPathReplicated(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ 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, p, NULL, 0,
+ WERR_FRS_INVALID_SERVICE_PARAMETER)) {
+ ret = false;
+ }
+
+ for (i=0; i<ARRAY_SIZE(lvls); i++) {
+ if (!_test_IsPathReplicated(tctx, p, dcerpc_server_name(p),
+ lvls[i])) {
+ ret = false;
+ }
+ }
+
+ for (i=0; i<ARRAY_SIZE(lvls); i++) {
+ const char *path = talloc_asprintf(tctx, "\\\\%s\\SYSVOL",
+ dcerpc_server_name(p));
+ if (!_test_IsPathReplicated(tctx, p, path, lvls[i])) {
+ ret = false;
+ }
+ }
+
+ for (i=0; i<ARRAY_SIZE(lvls); i++) {
+ if (!_test_IsPathReplicated(tctx, p,
+ "C:\\windows\\sysvol\\domain",
+ lvls[i])) {
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_ForceReplication(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct frsapi_ForceReplication r;
+
+ ZERO_STRUCT(r);
+
+ r.in.guid1 = NULL;
+ r.in.guid2 = NULL;
+ r.in.replica_set = talloc_asprintf(tctx, "%s",
+ lp_realm(tctx->lp_ctx));
+ r.in.partner_name = dcerpc_server_name(p);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_frsapi_ForceReplication(p, 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)
+{
+ 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(p, 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");
+ struct torture_test *test;
+
+ tcase = torture_suite_add_rpc_iface_tcase(suite, "frsapi",
+ &ndr_table_frsapi);
+
+ test = torture_rpc_tcase_add_test(tcase, "DsPollingIntervalW",
+ test_DsPollingIntervalW);
+
+ test = torture_rpc_tcase_add_test(tcase, "IsPathReplicated",
+ test_IsPathReplicated);
+
+ test = torture_rpc_tcase_add_test(tcase, "ForceReplication",
+ test_ForceReplication);
+
+ test = torture_rpc_tcase_add_test(tcase, "InfoW",
+ test_InfoW);
+ return suite;
+}
diff --git a/source4/torture/rpc/handles.c b/source4/torture/rpc/handles.c
new file mode 100644
index 0000000000..553025276d
--- /dev/null
+++ b/source4/torture/rpc/handles.c
@@ -0,0 +1,582 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.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/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 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");
+
+ status = torture_rpc_connection(torture, &p2, &ndr_table_lsarpc);
+ torture_assert_ntstatus_ok(torture, status, "opening lsa pipe1");
+
+ 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(p1, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ 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(p2, mem_ctx, &c);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_NET_WRITE_FAULT,
+ "closing policy handle on p2");
+ torture_assert_int_equal(torture, p2->last_fault_code, DCERPC_FAULT_CONTEXT_MISMATCH,
+ "closing policy handle on p2");
+
+ status = dcerpc_lsa_Close(p1, mem_ctx, &c);
+ torture_assert_ntstatus_ok(torture, status, "closing policy handle on p1");
+
+ status = dcerpc_lsa_Close(p1, mem_ctx, &c);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_NET_WRITE_FAULT,
+ "closing policy handle on p1 again");
+ torture_assert_int_equal(torture, p1->last_fault_code, DCERPC_FAULT_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 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");
+
+ transport = p1->conn->transport.transport,
+ assoc_group_id = p1->assoc_group_id;
+
+ 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);
+ torture_assert_ntstatus_ok(torture, status, "opening lsa pipe2");
+
+ 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");
+ status = dcerpc_lsa_OpenPolicy(p1, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ 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);
+ torture_assert_ntstatus_ok(torture, status, "opening lsa pipe3");
+
+ 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");
+ status = dcerpc_lsa_QuerySecurity(p1, mem_ctx, &qsec);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK,
+ "use policy handle on p1");
+
+ status = dcerpc_lsa_QuerySecurity(p2, mem_ctx, &qsec);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK,
+ "use policy handle on p2");
+
+ status = dcerpc_lsa_QuerySecurity(p3, mem_ctx, &qsec);
+ 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");
+ status = dcerpc_lsa_Close(p2, mem_ctx, &c);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK,
+ "closing policy handle on p2");
+
+ status = dcerpc_lsa_Close(p1, mem_ctx, &c);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_NET_WRITE_FAULT,
+ "closing policy handle on p1 again");
+ torture_assert_int_equal(torture, p1->last_fault_code, DCERPC_FAULT_CONTEXT_MISMATCH,
+ "closing policy handle on p1 again");
+
+ status = dcerpc_lsa_Close(p3, mem_ctx, &c);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_NET_WRITE_FAULT,
+ "closing policy handle on p3");
+ torture_assert_int_equal(torture, p1->last_fault_code, DCERPC_FAULT_CONTEXT_MISMATCH,
+ "closing policy handle on p3");
+
+ status = dcerpc_lsa_Close(p2, mem_ctx, &c);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_NET_WRITE_FAULT,
+ "closing policy handle on p2 again");
+ torture_assert_int_equal(torture, p1->last_fault_code, DCERPC_FAULT_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");
+ status = dcerpc_lsa_OpenPolicy(p3, mem_ctx, &r);
+ torture_assert_ntstatus_equal(torture, status, 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");
+ status = dcerpc_lsa_QuerySecurity(p1, mem_ctx, &qsec);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK,
+ "use policy handle on p1");
+
+ status = dcerpc_lsa_QuerySecurity(p2, mem_ctx, &qsec);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK,
+ "use policy handle on p2");
+
+ status = dcerpc_lsa_QuerySecurity(p3, mem_ctx, &qsec);
+ 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");
+ status = dcerpc_lsa_Close(p2, mem_ctx, &c);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK,
+ "closing policy handle on p2");
+
+ status = dcerpc_lsa_Close(p1, mem_ctx, &c);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_NET_WRITE_FAULT,
+ "closing policy handle on p1 again");
+ torture_assert_int_equal(torture, p1->last_fault_code, DCERPC_FAULT_CONTEXT_MISMATCH,
+ "closing policy handle on p1 again");
+
+ status = dcerpc_lsa_Close(p3, mem_ctx, &c);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_NET_WRITE_FAULT,
+ "closing policy handle on p3");
+ torture_assert_int_equal(torture, p1->last_fault_code, DCERPC_FAULT_CONTEXT_MISMATCH,
+ "closing policy handle on p3");
+
+ status = dcerpc_lsa_Close(p2, mem_ctx, &c);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_NET_WRITE_FAULT,
+ "closing policy handle on p2 again");
+ torture_assert_int_equal(torture, p1->last_fault_code, DCERPC_FAULT_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");
+ status = dcerpc_lsa_OpenPolicy(p1, mem_ctx, &r);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK,
+ "open 2nd policy handle on p1");
+
+ status = dcerpc_lsa_QuerySecurity(p1, mem_ctx, &qsec);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK,
+ "QuerySecurity handle on p1");
+
+ /* close first connection */
+ torture_comment(torture, "disconnect p1\n");
+ talloc_free(p1);
+ msleep(5);
+
+ /*
+ * and it's still available on p2,p3
+ */
+ torture_comment(torture, "use policy handle on p2,p3\n");
+ status = dcerpc_lsa_QuerySecurity(p2, mem_ctx, &qsec);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK,
+ "QuerySecurity handle on p2 after p1 was disconnected");
+
+ status = dcerpc_lsa_QuerySecurity(p3, mem_ctx, &qsec);
+ torture_assert_ntstatus_equal(torture, status, 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);
+ torture_assert_ntstatus_ok(torture, status, "opening lsa pipe4");
+
+ status = dcerpc_lsa_QuerySecurity(p4, mem_ctx, &qsec);
+ torture_assert_ntstatus_equal(torture, status, 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);
+ 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);
+ 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 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");
+
+ status = torture_rpc_connection(torture, &p2, &ndr_table_samr);
+ torture_assert_ntstatus_ok(torture, status, "opening samr pipe1");
+
+ r.in.system_name = 0;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.connect_handle = &handle;
+
+ status = dcerpc_samr_Connect(p1, mem_ctx, &r);
+ torture_assert_ntstatus_ok(torture, status, "opening policy handle on p1");
+
+ c.in.handle = &handle;
+ c.out.handle = &handle2;
+
+ status = dcerpc_samr_Close(p2, mem_ctx, &c);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_NET_WRITE_FAULT,
+ "closing policy handle on p2");
+ torture_assert_int_equal(torture, p2->last_fault_code, DCERPC_FAULT_CONTEXT_MISMATCH,
+ "closing policy handle on p2");
+
+ status = dcerpc_samr_Close(p1, mem_ctx, &c);
+ torture_assert_ntstatus_ok(torture, status, "closing policy handle on p1");
+
+ status = dcerpc_samr_Close(p1, mem_ctx, &c);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_NET_WRITE_FAULT,
+ "closing policy handle on p1 again");
+ torture_assert_int_equal(torture, p1->last_fault_code, DCERPC_FAULT_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 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");
+
+ transport = p1->conn->transport.transport,
+ assoc_group_id = p1->assoc_group_id;
+
+ 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);
+ torture_assert_ntstatus_ok(torture, status, "opening lsa pipe2");
+
+ 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");
+ status = dcerpc_samr_Connect(p1, mem_ctx, &r);
+ torture_assert_ntstatus_ok(torture, status, "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(p2, mem_ctx, &lc);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_NET_WRITE_FAULT,
+ "closing handle on lsa p2");
+ torture_assert_int_equal(torture, p2->last_fault_code, DCERPC_FAULT_CONTEXT_MISMATCH,
+ "closing handle on lsa p2");
+
+ torture_comment(torture, "closing policy handle on samr p1\n");
+ status = dcerpc_samr_Close(p1, mem_ctx, &sc);
+ torture_assert_ntstatus_ok(torture, status, "closing policy handle on p1");
+
+ talloc_free(p1);
+ talloc_free(p2);
+ 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);
+ 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);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_UNSUCCESSFUL,
+ "opening lsa pipe4");
+
+ 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);
+ 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);
+ 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");
+
+ transport = p1->conn->transport.transport,
+ assoc_group_id = p1->assoc_group_id;
+
+ torture_comment(torture, "pip1 use assoc_group_id[0x%08X]\n", assoc_group_id);
+
+ 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);
+ 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);
+ 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 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");
+
+ status = torture_rpc_connection(torture, &p2, &ndr_table_drsuapi);
+ torture_assert_ntstatus_ok(torture, status, "opening drsuapi pipe1");
+
+ 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(p1, 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(p2, mem_ctx, &c);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_NET_WRITE_FAULT,
+ "closing policy handle on p2");
+ torture_assert_int_equal(torture, p2->last_fault_code, DCERPC_FAULT_CONTEXT_MISMATCH,
+ "closing policy handle on p2");
+
+ status = dcerpc_drsuapi_DsUnbind(p1, mem_ctx, &c);
+ torture_assert_ntstatus_ok(torture, status, "closing policy handle on p1");
+
+ status = dcerpc_drsuapi_DsUnbind(p1, mem_ctx, &c);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_NET_WRITE_FAULT,
+ "closing policy handle on p1 again");
+ torture_assert_int_equal(torture, p1->last_fault_code, DCERPC_FAULT_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 0000000000..149fae6c26
--- /dev/null
+++ b/source4/torture/rpc/initshutdown.c
@@ -0,0 +1,114 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/ndr_initshutdown_c.h"
+#include "torture/rpc/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;
+
+ r.in.server = &server;
+
+ status = dcerpc_initshutdown_Abort(p, 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;
+
+ 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(p, 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;
+
+ 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(p, 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/join.c b/source4/torture/rpc/join.c
new file mode 100644
index 0000000000..b0c122c1b6
--- /dev/null
+++ b/source4/torture/rpc/join.c
@@ -0,0 +1,90 @@
+#include "includes.h"
+#include "libnet/libnet.h"
+#include "libcli/libcli.h"
+
+#include "auth/credentials/credentials.h"
+#include "torture/rpc/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;
+ }
+
+ lp_smbcli_options(torture->lp_ctx, &options);
+ lp_smbcli_session_options(torture->lp_ctx, &session_options);
+
+ status = smbcli_full_connection(tj, &cli, host,
+ lp_smb_ports(torture->lp_ctx),
+ "IPC$", NULL,
+ lp_socket_options(torture->lp_ctx),
+ machine_account,
+ lp_resolve_context(torture->lp_ctx),
+ torture->ev, &options, &session_options,
+ lp_iconv_convenience(torture->lp_ctx),
+ lp_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,
+ lp_smb_ports(torture->lp_ctx),
+ "IPC$", NULL,
+ lp_socket_options(torture->lp_ctx),
+ machine_account,
+ lp_resolve_context(torture->lp_ctx),
+ torture->ev, &options, &session_options,
+ lp_iconv_convenience(torture->lp_ctx),
+ lp_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 0000000000..7d03e7ef9e
--- /dev/null
+++ b/source4/torture/rpc/lsa.c
@@ -0,0 +1,2768 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for lsa rpc operations
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/ndr_lsa_c.h"
+#include "librpc/gen_ndr/netlogon.h"
+#include "librpc/gen_ndr/ndr_drsblobs.h"
+#include "lib/events/events.h"
+#include "libcli/security/security.h"
+#include "libcli/auth/libcli_auth.h"
+#include "torture/rpc/rpc.h"
+#include "param/param.h"
+#include "../lib/crypto/crypto.h"
+#define TEST_MACHINENAME "lsatestmach"
+
+static void init_lsa_String(struct lsa_String *name, const char *s)
+{
+ name->string = s;
+}
+
+static bool test_OpenPolicy(struct dcerpc_pipe *p,
+ struct torture_context *tctx)
+{
+ struct lsa_ObjectAttribute attr;
+ struct policy_handle handle;
+ struct lsa_QosInfo qos;
+ struct lsa_OpenPolicy r;
+ NTSTATUS status;
+ uint16_t system_name = '\\';
+
+ printf("\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;
+
+ status = dcerpc_lsa_OpenPolicy(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED)) {
+ printf("not considering %s to be an error\n", nt_errstr(status));
+ return true;
+ }
+ printf("OpenPolicy failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+
+bool test_lsa_OpenPolicy2(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle **handle)
+{
+ struct lsa_ObjectAttribute attr;
+ struct lsa_QosInfo qos;
+ struct lsa_OpenPolicy2 r;
+ NTSTATUS status;
+
+ printf("\nTesting OpenPolicy2\n");
+
+ *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;
+
+ status = dcerpc_lsa_OpenPolicy2(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED)) {
+ printf("not considering %s to be an error\n", nt_errstr(status));
+ talloc_free(*handle);
+ *handle = NULL;
+ return true;
+ }
+ printf("OpenPolicy2 failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+
+static const char *sid_type_lookup(enum lsa_SidType r)
+{
+ switch (r) {
+ case SID_NAME_USE_NONE: return "SID_NAME_USE_NONE"; break;
+ case SID_NAME_USER: return "SID_NAME_USER"; break;
+ case SID_NAME_DOM_GRP: return "SID_NAME_DOM_GRP"; break;
+ case SID_NAME_DOMAIN: return "SID_NAME_DOMAIN"; break;
+ case SID_NAME_ALIAS: return "SID_NAME_ALIAS"; break;
+ case SID_NAME_WKN_GRP: return "SID_NAME_WKN_GRP"; break;
+ case SID_NAME_DELETED: return "SID_NAME_DELETED"; break;
+ case SID_NAME_INVALID: return "SID_NAME_INVALID"; break;
+ case SID_NAME_UNKNOWN: return "SID_NAME_UNKNOWN"; break;
+ case SID_NAME_COMPUTER: return "SID_NAME_COMPUTER"; break;
+ }
+ return "Invalid sid type\n";
+}
+
+static bool test_LookupNames(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ 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;
+ NTSTATUS status;
+ int i;
+
+ printf("\nTesting LookupNames with %d names\n", tnames->count);
+
+ sids.count = 0;
+ sids.sids = NULL;
+
+ names = talloc_array(tctx, struct lsa_String, tnames->count);
+ for (i=0;i<tnames->count;i++) {
+ init_lsa_String(&names[i], tnames->names[i].name.string);
+ }
+
+ r.in.handle = handle;
+ r.in.num_names = tnames->count;
+ r.in.names = names;
+ r.in.sids = &sids;
+ r.in.level = 1;
+ r.in.count = &count;
+ r.out.count = &count;
+ r.out.sids = &sids;
+ r.out.domains = &domains;
+
+ status = dcerpc_lsa_LookupNames(p, tctx, &r);
+
+ if (NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
+ for (i=0;i< tnames->count;i++) {
+ if (i < count && sids.sids[i].sid_type == SID_NAME_UNKNOWN) {
+ printf("LookupName of %s was unmapped\n",
+ tnames->names[i].name.string);
+ } else if (i >=count) {
+ printf("LookupName of %s failed to return a result\n",
+ tnames->names[i].name.string);
+ }
+ }
+ printf("LookupNames failed - %s\n", nt_errstr(status));
+ return false;
+ } else if (!NT_STATUS_IS_OK(status)) {
+ printf("LookupNames failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ for (i=0;i< tnames->count;i++) {
+ if (i < count && sids.sids[i].sid_type != tnames->names[i].sid_type) {
+ printf("LookupName of %s got unexpected name type: %s\n",
+ tnames->names[i].name.string, sid_type_lookup(sids.sids[i].sid_type));
+ } else if (i >=count) {
+ printf("LookupName of %s failed to return a result\n",
+ tnames->names[i].name.string);
+ }
+ }
+ printf("\n");
+
+ return true;
+}
+
+static bool test_LookupNames_bogus(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ struct lsa_LookupNames r;
+ struct lsa_TransSidArray sids;
+ struct lsa_RefDomainList *domains = NULL;
+ struct lsa_String *names;
+ uint32_t count = 0;
+ NTSTATUS status;
+ int i;
+
+ struct lsa_TranslatedName name[2];
+ struct lsa_TransNameArray tnames;
+
+ tnames.names = name;
+ tnames.count = 2;
+ name[0].name.string = "NT AUTHORITY\\BOGUS";
+ name[1].name.string = NULL;
+
+ printf("\nTesting LookupNames with bogus names\n");
+
+ sids.count = 0;
+ sids.sids = NULL;
+
+ names = talloc_array(tctx, struct lsa_String, tnames.count);
+ for (i=0;i<tnames.count;i++) {
+ init_lsa_String(&names[i], tnames.names[i].name.string);
+ }
+
+ r.in.handle = handle;
+ r.in.num_names = tnames.count;
+ r.in.names = names;
+ r.in.sids = &sids;
+ r.in.level = 1;
+ r.in.count = &count;
+ r.out.count = &count;
+ r.out.sids = &sids;
+ r.out.domains = &domains;
+
+ status = dcerpc_lsa_LookupNames(p, tctx, &r);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
+ printf("LookupNames failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ printf("\n");
+
+ return true;
+}
+
+static bool test_LookupNames_wellknown(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ struct lsa_TranslatedName name;
+ struct lsa_TransNameArray tnames;
+ bool ret = true;
+
+ printf("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(p, tctx, handle, &tnames);
+
+ name.name.string = "NT AUTHORITY\\ANONYMOUS LOGON";
+ name.sid_type = SID_NAME_WKN_GRP;
+ ret &= test_LookupNames(p, tctx, handle, &tnames);
+
+ name.name.string = "NT AUTHORITY\\Authenticated Users";
+ name.sid_type = SID_NAME_WKN_GRP;
+ ret &= test_LookupNames(p, tctx, handle, &tnames);
+
+#if 0
+ name.name.string = "NT AUTHORITY";
+ ret &= test_LookupNames(p, tctx, handle, &tnames);
+
+ name.name.string = "NT AUTHORITY\\";
+ ret &= test_LookupNames(p, tctx, handle, &tnames);
+#endif
+
+ name.name.string = "BUILTIN\\";
+ name.sid_type = SID_NAME_DOMAIN;
+ ret &= test_LookupNames(p, tctx, handle, &tnames);
+
+ name.name.string = "BUILTIN\\Administrators";
+ name.sid_type = SID_NAME_ALIAS;
+ ret &= test_LookupNames(p, tctx, handle, &tnames);
+
+ name.name.string = "SYSTEM";
+ name.sid_type = SID_NAME_WKN_GRP;
+ ret &= test_LookupNames(p, tctx, handle, &tnames);
+
+ name.name.string = "Everyone";
+ name.sid_type = SID_NAME_WKN_GRP;
+ ret &= test_LookupNames(p, tctx, handle, &tnames);
+ return ret;
+}
+
+static bool test_LookupNames2(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ 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 count = 0;
+ NTSTATUS status;
+ int i;
+
+ printf("\nTesting LookupNames2 with %d names\n", tnames->count);
+
+ sids.count = 0;
+ sids.sids = NULL;
+
+ names = talloc_array(tctx, struct lsa_String, tnames->count);
+ for (i=0;i<tnames->count;i++) {
+ init_lsa_String(&names[i], tnames->names[i].name.string);
+ }
+
+ r.in.handle = handle;
+ r.in.num_names = tnames->count;
+ r.in.names = names;
+ r.in.sids = &sids;
+ r.in.level = 1;
+ 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_LookupNames2(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("LookupNames2 failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ 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");
+ }
+ }
+
+ printf("\n");
+
+ return true;
+}
+
+
+static bool test_LookupNames3(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ 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;
+ NTSTATUS status;
+ int i;
+
+ printf("\nTesting LookupNames3 with %d names\n", tnames->count);
+
+ sids.count = 0;
+ sids.sids = NULL;
+
+ names = talloc_array(tctx, struct lsa_String, tnames->count);
+ for (i=0;i<tnames->count;i++) {
+ init_lsa_String(&names[i], tnames->names[i].name.string);
+ }
+
+ r.in.handle = handle;
+ r.in.num_names = tnames->count;
+ r.in.names = names;
+ r.in.sids = &sids;
+ r.in.level = 1;
+ 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_LookupNames3(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("LookupNames3 failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ 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");
+ }
+ }
+
+ printf("\n");
+
+ return true;
+}
+
+static bool test_LookupNames4(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ 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;
+ NTSTATUS status;
+ int i;
+
+ printf("\nTesting LookupNames4 with %d names\n", tnames->count);
+
+ sids.count = 0;
+ sids.sids = NULL;
+
+ names = talloc_array(tctx, struct lsa_String, tnames->count);
+ for (i=0;i<tnames->count;i++) {
+ init_lsa_String(&names[i], tnames->names[i].name.string);
+ }
+
+ r.in.num_names = tnames->count;
+ r.in.names = names;
+ r.in.sids = &sids;
+ r.in.level = 1;
+ 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(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("LookupNames4 failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ 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");
+ }
+ }
+
+ printf("\n");
+
+ return true;
+}
+
+
+static bool test_LookupSids(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ struct lsa_SidArray *sids)
+{
+ struct lsa_LookupSids r;
+ struct lsa_TransNameArray names;
+ struct lsa_RefDomainList *domains = NULL;
+ uint32_t count = sids->num_sids;
+ NTSTATUS status;
+
+ printf("\nTesting LookupSids\n");
+
+ names.count = 0;
+ names.names = NULL;
+
+ r.in.handle = handle;
+ r.in.sids = sids;
+ r.in.names = &names;
+ r.in.level = 1;
+ r.in.count = &count;
+ r.out.count = &count;
+ r.out.names = &names;
+ r.out.domains = &domains;
+
+ status = dcerpc_lsa_LookupSids(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("LookupSids failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ printf("\n");
+
+ if (!test_LookupNames(p, tctx, handle, &names)) {
+ return false;
+ }
+
+ return true;
+}
+
+
+static bool test_LookupSids2(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ struct lsa_SidArray *sids)
+{
+ struct lsa_LookupSids2 r;
+ struct lsa_TransNameArray2 names;
+ struct lsa_RefDomainList *domains = NULL;
+ uint32_t count = sids->num_sids;
+ NTSTATUS status;
+
+ printf("\nTesting LookupSids2\n");
+
+ names.count = 0;
+ names.names = NULL;
+
+ r.in.handle = handle;
+ r.in.sids = sids;
+ r.in.names = &names;
+ r.in.level = 1;
+ r.in.count = &count;
+ r.in.unknown1 = 0;
+ r.in.unknown2 = 0;
+ r.out.count = &count;
+ r.out.names = &names;
+ r.out.domains = &domains;
+
+ status = dcerpc_lsa_LookupSids2(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("LookupSids2 failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ printf("\n");
+
+ if (!test_LookupNames2(p, tctx, handle, &names, false)) {
+ return false;
+ }
+
+ if (!test_LookupNames3(p, tctx, handle, &names, false)) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_LookupSids3(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ 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;
+
+ printf("\nTesting LookupSids3\n");
+
+ names.count = 0;
+ names.names = NULL;
+
+ r.in.sids = sids;
+ r.in.names = &names;
+ r.in.level = 1;
+ r.in.count = &count;
+ r.in.unknown1 = 0;
+ r.in.unknown2 = 0;
+ r.out.domains = &domains;
+ r.out.count = &count;
+ r.out.names = &names;
+
+ status = dcerpc_lsa_LookupSids3(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED)) {
+ printf("not considering %s to be an error\n", nt_errstr(status));
+ return true;
+ }
+ printf("LookupSids3 failed - %s - not considered an error\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ printf("\n");
+
+ if (!test_LookupNames4(p, tctx, &names, false)) {
+ return false;
+ }
+
+ return true;
+}
+
+bool test_many_LookupSids(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ uint32_t count;
+ NTSTATUS status;
+ struct lsa_SidArray sids;
+ int i;
+
+ printf("\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; i<sids.num_sids; i++) {
+ const char *sidstr = "S-1-5-32-545";
+ sids.sids[i].sid = dom_sid_parse_talloc(tctx, sidstr);
+ }
+
+ count = sids.num_sids;
+
+ if (handle) {
+ struct lsa_LookupSids r;
+ struct lsa_TransNameArray names;
+ struct lsa_RefDomainList *domains = NULL;
+ names.count = 0;
+ names.names = NULL;
+
+ r.in.handle = handle;
+ r.in.sids = &sids;
+ r.in.names = &names;
+ r.in.level = 1;
+ r.in.count = &names.count;
+ r.out.count = &count;
+ r.out.names = &names;
+ r.out.domains = &domains;
+
+ status = dcerpc_lsa_LookupSids(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("LookupSids failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ printf("\n");
+
+ if (!test_LookupNames(p, tctx, handle, &names)) {
+ return false;
+ }
+ } else if (p->conn->security_state.auth_info->auth_type == DCERPC_AUTH_TYPE_SCHANNEL &&
+ p->conn->security_state.auth_info->auth_level >= DCERPC_AUTH_LEVEL_INTEGRITY) {
+ struct lsa_LookupSids3 r;
+ struct lsa_RefDomainList *domains = NULL;
+ struct lsa_TransNameArray2 names;
+
+ names.count = 0;
+ names.names = NULL;
+
+ printf("\nTesting LookupSids3\n");
+
+ r.in.sids = &sids;
+ r.in.names = &names;
+ r.in.level = 1;
+ r.in.count = &count;
+ r.in.unknown1 = 0;
+ r.in.unknown2 = 0;
+ r.out.count = &count;
+ r.out.names = &names;
+ r.out.domains = &domains;
+
+ status = dcerpc_lsa_LookupSids3(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED)) {
+ printf("not considering %s to be an error\n", nt_errstr(status));
+ return true;
+ }
+ printf("LookupSids3 failed - %s\n",
+ nt_errstr(status));
+ return false;
+ }
+ if (!test_LookupNames4(p, tctx, &names, false)) {
+ return false;
+ }
+ }
+
+ printf("\n");
+
+
+
+ return true;
+}
+
+static void lookupsids_cb(struct rpc_request *req)
+{
+ int *replies = (int *)req->async.private_data;
+ NTSTATUS status;
+
+ status = dcerpc_ndr_request_recv(req);
+ 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_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ 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 rpc_request **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);
+
+ printf("\nTesting %d async lookupsids request\n", num_async_requests);
+
+ req = talloc_array(tctx, struct rpc_request *, 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; i<num_async_requests; i++) {
+ count[i] = 0;
+ names[i].count = 0;
+ names[i].names = NULL;
+
+ r[i].in.handle = handle;
+ r[i].in.sids = &sids;
+ r[i].in.names = &names[i];
+ r[i].in.level = 1;
+ r[i].in.count = &names[i].count;
+ r[i].out.count = &count[i];
+ r[i].out.names = &names[i];
+ r[i].out.domains = &domains;
+
+ req[i] = dcerpc_lsa_LookupSids_send(p, req, &r[i]);
+ if (req[i] == NULL) {
+ ret = false;
+ break;
+ }
+
+ req[i]->async.callback = lookupsids_cb;
+ req[i]->async.private_data = &replies;
+ }
+
+ while (replies >= 0 && replies < num_async_requests) {
+ event_loop_once(p->conn->event_ctx);
+ }
+
+ talloc_free(req);
+
+ if (replies < 0) {
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool test_LookupPrivValue(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ struct lsa_String *name)
+{
+ NTSTATUS status;
+ struct lsa_LookupPrivValue r;
+ struct lsa_LUID luid;
+
+ r.in.handle = handle;
+ r.in.name = name;
+ r.out.luid = &luid;
+
+ status = dcerpc_lsa_LookupPrivValue(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("\nLookupPrivValue failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_LookupPrivName(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ struct lsa_LUID *luid)
+{
+ NTSTATUS status;
+ struct lsa_LookupPrivName r;
+ struct lsa_StringLarge *name = NULL;
+
+ r.in.handle = handle;
+ r.in.luid = luid;
+ r.out.name = &name;
+
+ status = dcerpc_lsa_LookupPrivName(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("\nLookupPrivName failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_RemovePrivilegesFromAccount(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ struct policy_handle *acct_handle,
+ struct lsa_LUID *luid)
+{
+ NTSTATUS status;
+ struct lsa_RemovePrivilegesFromAccount r;
+ struct lsa_PrivilegeSet privs;
+ bool ret = true;
+
+ printf("\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;
+
+ status = dcerpc_lsa_RemovePrivilegesFromAccount(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+
+ 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;
+
+ status = dcerpc_lsa_LookupPrivName(p, tctx, &r_name);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("\nLookupPrivName failed - %s\n", nt_errstr(status));
+ return false;
+ }
+ /* Windows 2008 does not allow this to be removed */
+ if (strcmp("SeAuditPrivilege", name->string) == 0) {
+ return ret;
+ }
+
+ printf("RemovePrivilegesFromAccount failed to remove %s - %s\n",
+ name->string,
+ nt_errstr(status));
+ return false;
+ }
+
+ return ret;
+}
+
+static bool test_AddPrivilegesToAccount(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *acct_handle,
+ struct lsa_LUID *luid)
+{
+ NTSTATUS status;
+ struct lsa_AddPrivilegesToAccount r;
+ struct lsa_PrivilegeSet privs;
+ bool ret = true;
+
+ printf("\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;
+
+ status = dcerpc_lsa_AddPrivilegesToAccount(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("AddPrivilegesToAccount failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ return ret;
+}
+
+static bool test_EnumPrivsAccount(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ struct policy_handle *acct_handle)
+{
+ NTSTATUS status;
+ struct lsa_EnumPrivsAccount r;
+ struct lsa_PrivilegeSet *privs = NULL;
+ bool ret = true;
+
+ printf("\nTesting EnumPrivsAccount\n");
+
+ r.in.handle = acct_handle;
+ r.out.privs = &privs;
+
+ status = dcerpc_lsa_EnumPrivsAccount(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("EnumPrivsAccount failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if (privs && privs->count > 0) {
+ int i;
+ for (i=0;i<privs->count;i++) {
+ test_LookupPrivName(p, tctx, handle,
+ &privs->set[i].luid);
+ }
+
+ ret &= test_RemovePrivilegesFromAccount(p, tctx, handle, acct_handle,
+ &privs->set[0].luid);
+ ret &= test_AddPrivilegesToAccount(p, tctx, acct_handle,
+ &privs->set[0].luid);
+ }
+
+ return ret;
+}
+
+static bool test_GetSystemAccessAccount(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ struct policy_handle *acct_handle)
+{
+ NTSTATUS status;
+ uint32_t access_mask;
+ struct lsa_GetSystemAccessAccount r;
+
+ printf("\nTesting GetSystemAccessAccount\n");
+
+ r.in.handle = acct_handle;
+ r.out.access_mask = &access_mask;
+
+ status = dcerpc_lsa_GetSystemAccessAccount(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("GetSystemAccessAccount failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if (r.out.access_mask != NULL) {
+ printf("Rights:");
+ if (*(r.out.access_mask) & LSA_POLICY_MODE_INTERACTIVE)
+ printf(" LSA_POLICY_MODE_INTERACTIVE");
+ if (*(r.out.access_mask) & LSA_POLICY_MODE_NETWORK)
+ printf(" LSA_POLICY_MODE_NETWORK");
+ if (*(r.out.access_mask) & LSA_POLICY_MODE_BATCH)
+ printf(" LSA_POLICY_MODE_BATCH");
+ if (*(r.out.access_mask) & LSA_POLICY_MODE_SERVICE)
+ printf(" LSA_POLICY_MODE_SERVICE");
+ if (*(r.out.access_mask) & LSA_POLICY_MODE_PROXY)
+ printf(" LSA_POLICY_MODE_PROXY");
+ if (*(r.out.access_mask) & LSA_POLICY_MODE_DENY_INTERACTIVE)
+ printf(" LSA_POLICY_MODE_DENY_INTERACTIVE");
+ if (*(r.out.access_mask) & LSA_POLICY_MODE_DENY_NETWORK)
+ printf(" LSA_POLICY_MODE_DENY_NETWORK");
+ if (*(r.out.access_mask) & LSA_POLICY_MODE_DENY_BATCH)
+ printf(" LSA_POLICY_MODE_DENY_BATCH");
+ if (*(r.out.access_mask) & LSA_POLICY_MODE_DENY_SERVICE)
+ printf(" LSA_POLICY_MODE_DENY_SERVICE");
+ if (*(r.out.access_mask) & LSA_POLICY_MODE_REMOTE_INTERACTIVE)
+ printf(" LSA_POLICY_MODE_REMOTE_INTERACTIVE");
+ if (*(r.out.access_mask) & LSA_POLICY_MODE_DENY_REMOTE_INTERACTIVE)
+ printf(" LSA_POLICY_MODE_DENY_REMOTE_INTERACTIVE");
+ if (*(r.out.access_mask) & LSA_POLICY_MODE_ALL)
+ printf(" LSA_POLICY_MODE_ALL");
+ if (*(r.out.access_mask) & LSA_POLICY_MODE_ALL_NT4)
+ printf(" LSA_POLICY_MODE_ALL_NT4");
+ printf("\n");
+ }
+
+ return true;
+}
+
+static bool test_Delete(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct lsa_Delete r;
+
+ printf("\nTesting Delete\n");
+
+ r.in.handle = handle;
+ status = dcerpc_lsa_Delete(p, tctx, &r);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
+ printf("Delete should have failed NT_STATUS_NOT_SUPPORTED - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_DeleteObject(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct lsa_DeleteObject r;
+
+ printf("\nTesting DeleteObject\n");
+
+ r.in.handle = handle;
+ r.out.handle = handle;
+ status = dcerpc_lsa_DeleteObject(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("DeleteObject failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+
+static bool test_CreateAccount(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ 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");
+
+ printf("\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;
+
+ status = dcerpc_lsa_CreateAccount(p, tctx, &r);
+ if (NT_STATUS_EQUAL(status, 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;
+
+ status = dcerpc_lsa_OpenAccount(p, tctx, &r_o);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("OpenAccount failed - %s\n", nt_errstr(status));
+ return false;
+ }
+ } else if (!NT_STATUS_IS_OK(status)) {
+ printf("CreateAccount failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if (!test_Delete(p, tctx, &acct_handle)) {
+ return false;
+ }
+
+ if (!test_DeleteObject(p, tctx, &acct_handle)) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_DeleteTrustedDomain(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ struct lsa_StringLarge name)
+{
+ NTSTATUS status;
+ 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;
+
+ status = dcerpc_lsa_OpenTrustedDomainByName(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("OpenTrustedDomainByName failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if (!test_Delete(p, tctx, &trustdom_handle)) {
+ return false;
+ }
+
+ if (!test_DeleteObject(p, tctx, &trustdom_handle)) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_DeleteTrustedDomainBySid(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ struct dom_sid *sid)
+{
+ NTSTATUS status;
+ struct lsa_DeleteTrustedDomain r;
+
+ r.in.handle = handle;
+ r.in.dom_sid = sid;
+
+ status = dcerpc_lsa_DeleteTrustedDomain(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("DeleteTrustedDomain failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+
+static bool test_CreateSecret(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ 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, blob2;
+ 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;
+
+ secname[LOCAL] = talloc_asprintf(tctx, "torturesecret-%u", (uint_t)random());
+ secname[GLOBAL] = talloc_asprintf(tctx, "G$torturesecret-%u", (uint_t)random());
+
+ for (i=0; i< 2; i++) {
+ printf("\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;
+
+ status = dcerpc_lsa_CreateSecret(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("CreateSecret failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ r.in.handle = handle;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.sec_handle = &sec_handle3;
+
+ status = dcerpc_lsa_CreateSecret(p, tctx, &r);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
+ printf("CreateSecret should have failed OBJECT_NAME_COLLISION - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ r2.in.handle = handle;
+ r2.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r2.in.name = r.in.name;
+ r2.out.sec_handle = &sec_handle2;
+
+ printf("Testing OpenSecret\n");
+
+ status = dcerpc_lsa_OpenSecret(p, tctx, &r2);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("OpenSecret failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = dcerpc_fetch_session_key(p, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("dcerpc_fetch_session_key failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ 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;
+
+ printf("Testing SetSecret\n");
+
+ status = dcerpc_lsa_SetSecret(p, tctx, &r3);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetSecret failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ 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]++;
+
+ printf("Testing SetSecret with broken key\n");
+
+ status = dcerpc_lsa_SetSecret(p, tctx, &r3);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_UNKNOWN_REVISION)) {
+ printf("SetSecret should have failed UNKNOWN_REVISION - %s\n", nt_errstr(status));
+ ret = false;
+ }
+
+ 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;
+
+ printf("Testing QuerySecret\n");
+ status = dcerpc_lsa_QuerySecret(p, tctx, &r4);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QuerySecret failed - %s\n", nt_errstr(status));
+ ret = false;
+ } else {
+ if (r4.out.new_val == NULL || r4.out.new_val->buf == NULL) {
+ printf("No secret buffer returned\n");
+ ret = false;
+ } else {
+ blob1.data = r4.out.new_val->buf->data;
+ blob1.length = r4.out.new_val->buf->size;
+
+ blob2 = data_blob_talloc(tctx, NULL, blob1.length);
+
+ secret2 = sess_decrypt_string(tctx,
+ &blob1, &session_key);
+
+ if (strcmp(secret1, secret2) != 0) {
+ printf("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;
+
+
+ msleep(200);
+ printf("Testing SetSecret (existing value should move to old)\n");
+
+ status = dcerpc_lsa_SetSecret(p, tctx, &r5);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetSecret failed - %s\n", nt_errstr(status));
+ 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;
+
+ status = dcerpc_lsa_QuerySecret(p, tctx, &r6);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QuerySecret failed - %s\n", nt_errstr(status));
+ 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) {
+ printf("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;
+
+ blob2 = data_blob_talloc(tctx, NULL, blob1.length);
+
+ secret4 = sess_decrypt_string(tctx,
+ &blob1, &session_key);
+
+ if (strcmp(secret3, secret4) != 0) {
+ printf("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;
+
+ blob2 = data_blob_talloc(tctx, NULL, blob1.length);
+
+ secret2 = sess_decrypt_string(tctx,
+ &blob1, &session_key);
+
+ if (strcmp(secret1, secret2) != 0) {
+ printf("Returned OLD secret %s doesn't match %s\n", secret2, secret1);
+ ret = false;
+ }
+
+ if (*r6.out.new_mtime == *r6.out.old_mtime) {
+ printf("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;
+
+ printf("Testing SetSecret of old Secret only\n");
+
+ status = dcerpc_lsa_SetSecret(p, tctx, &r7);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetSecret failed - %s\n", nt_errstr(status));
+ 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;
+
+ status = dcerpc_lsa_QuerySecret(p, tctx, &r8);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QuerySecret failed - %s\n", nt_errstr(status));
+ ret = false;
+ } else {
+ if (!r8.out.new_val || !r8.out.old_val) {
+ printf("in/out pointers not returned, despite being set on in for QuerySecret\n");
+ ret = false;
+ } else if (r8.out.new_val->buf != NULL) {
+ printf("NEW secret buffer must not be returned after OLD set\n");
+ ret = false;
+ } else if (r8.out.old_val->buf == NULL) {
+ printf("OLD secret buffer was not returned after OLD set\n");
+ ret = false;
+ } else if (r8.out.new_mtime == NULL || r8.out.old_mtime == NULL) {
+ printf("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;
+
+ blob2 = data_blob_talloc(tctx, NULL, blob1.length);
+
+ secret6 = sess_decrypt_string(tctx,
+ &blob1, &session_key);
+
+ if (strcmp(secret5, secret6) != 0) {
+ printf("Returned OLD secret %s doesn't match %s\n", secret5, secret6);
+ ret = false;
+ }
+
+ if (*r8.out.new_mtime != *r8.out.old_mtime) {
+ printf("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(p, tctx, &sec_handle)) {
+ ret = false;
+ }
+
+ if (!test_DeleteObject(p, tctx, &sec_handle)) {
+ return false;
+ }
+
+ d_o.in.handle = &sec_handle2;
+ d_o.out.handle = &sec_handle2;
+ status = dcerpc_lsa_DeleteObject(p, tctx, &d_o);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_HANDLE)) {
+ printf("Second delete expected INVALID_HANDLE - %s\n", nt_errstr(status));
+ ret = false;
+ } else {
+
+ printf("Testing OpenSecret of just-deleted secret\n");
+
+ status = dcerpc_lsa_OpenSecret(p, tctx, &r2);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ printf("OpenSecret expected OBJECT_NAME_NOT_FOUND - %s\n", nt_errstr(status));
+ ret = false;
+ }
+ }
+
+ }
+
+ return ret;
+}
+
+
+static bool test_EnumAccountRights(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *acct_handle,
+ struct dom_sid *sid)
+{
+ NTSTATUS status;
+ struct lsa_EnumAccountRights r;
+ struct lsa_RightSet rights;
+
+ printf("\nTesting EnumAccountRights\n");
+
+ r.in.handle = acct_handle;
+ r.in.sid = sid;
+ r.out.rights = &rights;
+
+ status = dcerpc_lsa_EnumAccountRights(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("EnumAccountRights of %s failed - %s\n",
+ dom_sid_string(tctx, sid), nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+
+static bool test_QuerySecurity(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ struct policy_handle *acct_handle)
+{
+ NTSTATUS status;
+ struct lsa_QuerySecurity r;
+ struct sec_desc_buf *sdbuf = NULL;
+
+ if (torture_setting_bool(tctx, "samba4", false)) {
+ printf("\nskipping QuerySecurity test against Samba4\n");
+ return true;
+ }
+
+ printf("\nTesting QuerySecurity\n");
+
+ r.in.handle = acct_handle;
+ r.in.sec_info = 7;
+ r.out.sdbuf = &sdbuf;
+
+ status = dcerpc_lsa_QuerySecurity(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QuerySecurity failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_OpenAccount(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ struct dom_sid *sid)
+{
+ NTSTATUS status;
+ struct lsa_OpenAccount r;
+ struct policy_handle acct_handle;
+
+ printf("\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;
+
+ status = dcerpc_lsa_OpenAccount(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("OpenAccount failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if (!test_EnumPrivsAccount(p, tctx, handle, &acct_handle)) {
+ return false;
+ }
+
+ if (!test_GetSystemAccessAccount(p, tctx, handle, &acct_handle)) {
+ return false;
+ }
+
+ if (!test_QuerySecurity(p, tctx, handle, &acct_handle)) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_EnumAccounts(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct lsa_EnumAccounts r;
+ struct lsa_SidArray sids1, sids2;
+ uint32_t resume_handle = 0;
+ int i;
+ bool ret = true;
+
+ printf("\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) {
+ status = dcerpc_lsa_EnumAccounts(p, tctx, &r);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES)) {
+ break;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("EnumAccounts failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if (!test_LookupSids(p, tctx, handle, &sids1)) {
+ return false;
+ }
+
+ if (!test_LookupSids2(p, tctx, handle, &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 */
+
+ printf("Testing all accounts\n");
+ for (i=0;i<sids1.num_sids;i++) {
+ ret &= test_OpenAccount(p, tctx, handle, sids1.sids[i].sid);
+ ret &= test_EnumAccountRights(p, tctx, handle, sids1.sids[i].sid);
+ }
+ printf("\n");
+ }
+
+ if (sids1.num_sids < 3) {
+ return ret;
+ }
+
+ printf("Trying EnumAccounts partial listing (asking for 1 at 2)\n");
+ resume_handle = 2;
+ r.in.num_entries = 1;
+ r.out.sids = &sids2;
+
+ status = dcerpc_lsa_EnumAccounts(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("EnumAccounts failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if (sids2.num_sids != 1) {
+ printf("Returned wrong number of entries (%d)\n", sids2.num_sids);
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_LookupPrivDisplayName(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ struct lsa_String *priv_name)
+{
+ struct lsa_LookupPrivDisplayName r;
+ NTSTATUS status;
+ /* produce a reasonable range of language output without screwing up
+ terminals */
+ uint16_t language_id = (random() % 4) + 0x409;
+ uint16_t returned_language_id = 0;
+ struct lsa_StringLarge *disp_name = NULL;
+
+ printf("\nTesting LookupPrivDisplayName(%s)\n", priv_name->string);
+
+ 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;
+
+ status = dcerpc_lsa_LookupPrivDisplayName(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("LookupPrivDisplayName failed - %s\n", nt_errstr(status));
+ return false;
+ }
+ printf("%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_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ struct lsa_String *priv_name)
+{
+ struct lsa_EnumAccountsWithUserRight r;
+ struct lsa_SidArray sids;
+ NTSTATUS status;
+
+ ZERO_STRUCT(sids);
+
+ printf("\nTesting EnumAccountsWithUserRight(%s)\n", priv_name->string);
+
+ r.in.handle = handle;
+ r.in.name = priv_name;
+ r.out.sids = &sids;
+
+ status = dcerpc_lsa_EnumAccountsWithUserRight(p, tctx, &r);
+
+ /* NT_STATUS_NO_MORE_ENTRIES means noone has this privilege */
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES)) {
+ return true;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("EnumAccountsWithUserRight failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+
+static bool test_EnumPrivs(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct lsa_EnumPrivs r;
+ struct lsa_PrivArray privs1;
+ uint32_t resume_handle = 0;
+ int i;
+ bool ret = true;
+
+ printf("\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;
+ status = dcerpc_lsa_EnumPrivs(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("EnumPrivs failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ for (i = 0; i< privs1.count; i++) {
+ test_LookupPrivDisplayName(p, tctx, handle, (struct lsa_String *)&privs1.privs[i].name);
+ test_LookupPrivValue(p, tctx, handle, (struct lsa_String *)&privs1.privs[i].name);
+ if (!test_EnumAccountsWithUserRight(p, tctx, handle, (struct lsa_String *)&privs1.privs[i].name)) {
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_QueryForestTrustInformation(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ const char *trusted_domain_name)
+{
+ bool ret = true;
+ struct lsa_lsaRQueryForestTrustInformation r;
+ NTSTATUS status;
+ struct lsa_String string;
+ struct lsa_ForestTrustInformation info, *info_ptr;
+
+ printf("\nTesting lsaRQueryForestTrustInformation\n");
+
+ if (torture_setting_bool(tctx, "samba4", false)) {
+ printf("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.unknown = 0;
+ r.out.forest_trust_info = &info_ptr;
+
+ status = dcerpc_lsa_lsaRQueryForestTrustInformation(p, tctx, &r);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("lsaRQueryForestTrustInformation of %s failed - %s\n", trusted_domain_name, nt_errstr(status));
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool test_query_each_TrustDomEx(struct dcerpc_pipe *p,
+ 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 & NETR_TRUST_ATTRIBUTE_FOREST_TRANSITIVE) {
+ ret &= test_QueryForestTrustInformation(p, tctx, handle,
+ domains->domains[i].domain_name.string);
+ }
+ }
+
+ return ret;
+}
+
+static bool test_query_each_TrustDom(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ struct lsa_DomainList *domains)
+{
+ NTSTATUS status;
+ int i,j;
+ bool ret = true;
+
+ printf("\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;
+
+ status = dcerpc_lsa_OpenTrustedDomain(p, tctx, &trust);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("OpenTrustedDomain failed - %s\n", nt_errstr(status));
+ 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;
+ status = dcerpc_lsa_QueryTrustedDomainInfo(p, tctx, &q);
+ if (!NT_STATUS_IS_OK(status) && ok[j]) {
+ printf("QueryTrustedDomainInfo level %d failed - %s\n",
+ levels[j], nt_errstr(status));
+ ret = false;
+ } else if (NT_STATUS_IS_OK(status) && !ok[j]) {
+ printf("QueryTrustedDomainInfo level %d unexpectedly succeeded - %s\n",
+ levels[j], nt_errstr(status));
+ ret = false;
+ }
+ }
+
+ status = dcerpc_lsa_CloseTrustedDomainEx(p, tctx, &c_trust);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
+ printf("Expected CloseTrustedDomainEx to return NT_STATUS_NOT_IMPLEMENTED, instead - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ c.in.handle = &trustdom_handle;
+ c.out.handle = &handle2;
+
+ status = dcerpc_lsa_Close(p, tctx, &c);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Close of trusted domain failed - %s\n", nt_errstr(status));
+ 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;
+
+ status = dcerpc_lsa_QueryTrustedDomainInfoBySid(p, tctx, &q);
+ if (!NT_STATUS_IS_OK(status) && ok[j]) {
+ printf("QueryTrustedDomainInfoBySid level %d failed - %s\n",
+ levels[j], nt_errstr(status));
+ ret = false;
+ } else if (NT_STATUS_IS_OK(status) && !ok[j]) {
+ printf("QueryTrustedDomainInfoBySid level %d unexpectedly succeeded - %s\n",
+ levels[j], nt_errstr(status));
+ 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;
+
+ status = dcerpc_lsa_OpenTrustedDomainByName(p, tctx, &trust_by_name);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("OpenTrustedDomainByName failed - %s\n", nt_errstr(status));
+ 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;
+ status = dcerpc_lsa_QueryTrustedDomainInfo(p, tctx, &q);
+ if (!NT_STATUS_IS_OK(status) && ok[j]) {
+ printf("QueryTrustedDomainInfo level %d failed - %s\n",
+ levels[j], nt_errstr(status));
+ ret = false;
+ } else if (NT_STATUS_IS_OK(status) && !ok[j]) {
+ printf("QueryTrustedDomainInfo level %d unexpectedly succeeded - %s\n",
+ levels[j], nt_errstr(status));
+ ret = false;
+ }
+ }
+
+ c.in.handle = &trustdom_handle;
+ c.out.handle = &handle2;
+
+ status = dcerpc_lsa_Close(p, tctx, &c);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Close of trusted domain failed - %s\n", nt_errstr(status));
+ 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;
+ status = dcerpc_lsa_QueryTrustedDomainInfoByName(p, tctx, &q);
+ if (!NT_STATUS_IS_OK(status) && ok[j]) {
+ printf("QueryTrustedDomainInfoByName level %d failed - %s\n",
+ levels[j], nt_errstr(status));
+ ret = false;
+ } else if (NT_STATUS_IS_OK(status) && !ok[j]) {
+ printf("QueryTrustedDomainInfoByName level %d unexpectedly succeeded - %s\n",
+ levels[j], nt_errstr(status));
+ ret = false;
+ }
+ }
+ }
+ return ret;
+}
+
+static bool test_EnumTrustDom(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ struct lsa_EnumTrustDom r;
+ struct lsa_EnumTrustedDomainsEx r_ex;
+ NTSTATUS enum_status;
+ uint32_t resume_handle = 0;
+ struct lsa_DomainList domains;
+ struct lsa_DomainListEx domains_ex;
+ bool ret = true;
+
+ printf("\nTesting EnumTrustDom\n");
+
+ r.in.handle = handle;
+ r.in.resume_handle = &resume_handle;
+ r.in.max_size = 0;
+ r.out.domains = &domains;
+ r.out.resume_handle = &resume_handle;
+
+ enum_status = dcerpc_lsa_EnumTrustDom(p, tctx, &r);
+
+ if (NT_STATUS_IS_OK(enum_status)) {
+ if (domains.count == 0) {
+ printf("EnumTrustDom failed - should have returned 'NT_STATUS_NO_MORE_ENTRIES' for 0 trusted domains\n");
+ return false;
+ }
+ } else if (!(NT_STATUS_EQUAL(enum_status, STATUS_MORE_ENTRIES) || NT_STATUS_EQUAL(enum_status, NT_STATUS_NO_MORE_ENTRIES))) {
+ printf("EnumTrustDom of zero size failed - %s\n", nt_errstr(enum_status));
+ return false;
+ }
+
+ /* Start from the bottom again */
+ resume_handle = 0;
+
+ do {
+ r.in.handle = handle;
+ r.in.resume_handle = &resume_handle;
+ r.in.max_size = LSA_ENUM_TRUST_DOMAIN_MULTIPLIER * 3;
+ r.out.domains = &domains;
+ r.out.resume_handle = &resume_handle;
+
+ enum_status = dcerpc_lsa_EnumTrustDom(p, tctx, &r);
+
+ /* NO_MORE_ENTRIES is allowed */
+ if (NT_STATUS_EQUAL(enum_status, NT_STATUS_NO_MORE_ENTRIES)) {
+ if (domains.count == 0) {
+ return true;
+ }
+ printf("EnumTrustDom failed - should have returned 0 trusted domains with 'NT_STATUS_NO_MORE_ENTRIES'\n");
+ return false;
+ } else if (NT_STATUS_EQUAL(enum_status, 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) {
+ printf("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(enum_status)) {
+ printf("EnumTrustDom failed - %s\n", nt_errstr(enum_status));
+ return false;
+ }
+
+ if (domains.count == 0) {
+ printf("EnumTrustDom failed - should have returned 'NT_STATUS_NO_MORE_ENTRIES' for 0 trusted domains\n");
+ return false;
+ }
+
+ ret &= test_query_each_TrustDom(p, tctx, handle, &domains);
+
+ } while ((NT_STATUS_EQUAL(enum_status, STATUS_MORE_ENTRIES)));
+
+ printf("\nTesting EnumTrustedDomainsEx\n");
+
+ r_ex.in.handle = handle;
+ r_ex.in.resume_handle = &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 = &resume_handle;
+
+ enum_status = dcerpc_lsa_EnumTrustedDomainsEx(p, tctx, &r_ex);
+
+ if (!(NT_STATUS_EQUAL(enum_status, STATUS_MORE_ENTRIES) || NT_STATUS_EQUAL(enum_status, NT_STATUS_NO_MORE_ENTRIES))) {
+ printf("EnumTrustedDomainEx of zero size failed - %s\n", nt_errstr(enum_status));
+ return false;
+ }
+
+ resume_handle = 0;
+ do {
+ r_ex.in.handle = handle;
+ r_ex.in.resume_handle = &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 = &resume_handle;
+
+ enum_status = dcerpc_lsa_EnumTrustedDomainsEx(p, tctx, &r_ex);
+
+ /* NO_MORE_ENTRIES is allowed */
+ if (NT_STATUS_EQUAL(enum_status, NT_STATUS_NO_MORE_ENTRIES)) {
+ if (domains_ex.count == 0) {
+ return true;
+ }
+ printf("EnumTrustDomainsEx failed - should have returned 0 trusted domains with 'NT_STATUS_NO_MORE_ENTRIES'\n");
+ return false;
+ } else if (NT_STATUS_EQUAL(enum_status, 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) {
+ printf("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(enum_status)) {
+ printf("EnumTrustedDomainEx failed - %s\n", nt_errstr(enum_status));
+ return false;
+ }
+
+ if (domains_ex.count == 0) {
+ printf("EnumTrustDomainEx failed - should have returned 'NT_STATUS_NO_MORE_ENTRIES' for 0 trusted domains\n");
+ return false;
+ }
+
+ ret &= test_query_each_TrustDomEx(p, tctx, handle, &domains_ex);
+
+ } while ((NT_STATUS_EQUAL(enum_status, STATUS_MORE_ENTRIES)));
+
+ return ret;
+}
+
+static bool test_CreateTrustedDomain(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ bool ret = true;
+ struct lsa_CreateTrustedDomain r;
+ struct lsa_DomainInfo trustinfo;
+ struct dom_sid *domsid[12];
+ struct policy_handle trustdom_handle[12];
+ struct lsa_QueryTrustedDomainInfo q;
+ union lsa_TrustedDomainInfo *info = NULL;
+ int i;
+
+ printf("\nTesting CreateTrustedDomain for 12 domains\n");
+
+ if (!test_EnumTrustDom(p, tctx, handle)) {
+ ret = false;
+ }
+
+ for (i=0; i< 12; i++) {
+ char *trust_name = talloc_asprintf(tctx, "torturedom%02d", i);
+ char *trust_sid = talloc_asprintf(tctx, "S-1-5-21-97398-379795-100%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];
+
+ status = dcerpc_lsa_CreateTrustedDomain(p, tctx, &r);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
+ test_DeleteTrustedDomain(p, tctx, handle, trustinfo.name);
+ status = dcerpc_lsa_CreateTrustedDomain(p, tctx, &r);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("CreateTrustedDomain failed - %s\n", nt_errstr(status));
+ ret = false;
+ } else {
+
+ q.in.trustdom_handle = &trustdom_handle[i];
+ q.in.level = LSA_TRUSTED_DOMAIN_INFO_INFO_EX;
+ q.out.info = &info;
+ status = dcerpc_lsa_QueryTrustedDomainInfo(p, tctx, &q);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QueryTrustedDomainInfo level 1 failed - %s\n", nt_errstr(status));
+ ret = false;
+ } else if (!q.out.info) {
+ ret = false;
+ } else {
+ if (strcmp(info->info_ex.netbios_name.string, trustinfo.name.string) != 0) {
+ printf("QueryTrustedDomainInfo returned inconsistant 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) {
+ printf("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) {
+ printf("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) {
+ printf("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(p, tctx, handle)) {
+ ret = false;
+ }
+
+ for (i=0; i<12; i++) {
+ if (!test_DeleteTrustedDomainBySid(p, tctx, handle, domsid[i])) {
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_CreateTrustedDomainEx2(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ bool ret = true;
+ struct lsa_CreateTrustedDomainEx2 r;
+ struct lsa_TrustDomainInfoInfoEx trustinfo;
+ struct lsa_TrustDomainInfoAuthInfoInternal authinfo;
+ struct trustDomainPasswords auth_struct;
+ DATA_BLOB auth_blob;
+ struct dom_sid *domsid[12];
+ struct policy_handle trustdom_handle[12];
+ struct lsa_QueryTrustedDomainInfo q;
+ union lsa_TrustedDomainInfo *info = NULL;
+ DATA_BLOB session_key;
+ enum ndr_err_code ndr_err;
+ int i;
+
+ printf("\nTesting CreateTrustedDomainEx2 for 12 domains\n");
+
+ status = dcerpc_fetch_session_key(p, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("dcerpc_fetch_session_key failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ for (i=0; i< 12; i++) {
+ char *trust_name = talloc_asprintf(tctx, "torturedom%02d", i);
+ char *trust_name_dns = talloc_asprintf(tctx, "torturedom%02d.samba.example.com", i);
+ char *trust_sid = talloc_asprintf(tctx, "S-1-5-21-97398-379795-100%02d", i);
+
+ 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;
+
+ generate_random_buffer(auth_struct.confounder, sizeof(auth_struct.confounder));
+
+ auth_struct.outgoing.count = 0;
+ auth_struct.incoming.count = 0;
+
+ ndr_err = ndr_push_struct_blob(&auth_blob, tctx, lp_iconv_convenience(tctx->lp_ctx), &auth_struct,
+ (ndr_push_flags_fn_t)ndr_push_trustDomainPasswords);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ printf("ndr_push_struct_blob of trustDomainPasswords structure failed");
+ ret = false;
+ }
+
+ arcfour_crypt_blob(auth_blob.data, auth_blob.length, &session_key);
+
+ authinfo.auth_blob.size = auth_blob.length;
+ authinfo.auth_blob.data = auth_blob.data;
+
+ 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];
+
+ status = dcerpc_lsa_CreateTrustedDomainEx2(p, tctx, &r);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
+ test_DeleteTrustedDomain(p, tctx, handle, trustinfo.netbios_name);
+ status = dcerpc_lsa_CreateTrustedDomainEx2(p, tctx, &r);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("CreateTrustedDomainEx failed2 - %s\n", nt_errstr(status));
+ ret = false;
+ } else {
+
+ q.in.trustdom_handle = &trustdom_handle[i];
+ q.in.level = LSA_TRUSTED_DOMAIN_INFO_INFO_EX;
+ q.out.info = &info;
+ status = dcerpc_lsa_QueryTrustedDomainInfo(p, tctx, &q);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QueryTrustedDomainInfo level 1 failed - %s\n", nt_errstr(status));
+ ret = false;
+ } else if (!q.out.info) {
+ printf("QueryTrustedDomainInfo level 1 failed to return an info pointer\n");
+ ret = false;
+ } else {
+ if (strcmp(info->info_ex.netbios_name.string, trustinfo.netbios_name.string) != 0) {
+ printf("QueryTrustedDomainInfo returned inconsistant 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) {
+ printf("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) {
+ printf("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) {
+ printf("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(p, tctx, handle)) {
+ printf("test_EnumTrustDom failed\n");
+ ret = false;
+ }
+
+ for (i=0; i<12; i++) {
+ if (!test_DeleteTrustedDomainBySid(p, tctx, handle, domsid[i])) {
+ printf("test_DeleteTrustedDomainBySid failed\n");
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_QueryDomainInfoPolicy(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ struct lsa_QueryDomainInformationPolicy r;
+ union lsa_DomainInformationPolicy *info = NULL;
+ NTSTATUS status;
+ int i;
+ bool ret = true;
+
+ printf("\nTesting QueryDomainInformationPolicy\n");
+
+ for (i=2;i<4;i++) {
+ r.in.handle = handle;
+ r.in.level = i;
+ r.out.info = &info;
+
+ printf("\nTrying QueryDomainInformationPolicy level %d\n", i);
+
+ status = dcerpc_lsa_QueryDomainInformationPolicy(p, tctx, &r);
+
+ /* If the server does not support EFS, then this is the correct return */
+ if (i == LSA_DOMAIN_INFO_POLICY_EFS && NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ continue;
+ } else if (!NT_STATUS_IS_OK(status)) {
+ printf("QueryDomainInformationPolicy failed - %s\n", nt_errstr(status));
+ ret = false;
+ continue;
+ }
+ }
+
+ return ret;
+}
+
+
+static bool test_QueryInfoPolicyCalls( bool version2,
+ struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ struct lsa_QueryInfoPolicy r;
+ union lsa_PolicyInformation *info = NULL;
+ NTSTATUS status;
+ int i;
+ bool ret = true;
+
+ if (version2)
+ printf("\nTesting QueryInfoPolicy2\n");
+ else
+ printf("\nTesting QueryInfoPolicy\n");
+
+ for (i=1;i<=14;i++) {
+ r.in.handle = handle;
+ r.in.level = i;
+ r.out.info = &info;
+
+ if (version2)
+ printf("\nTrying QueryInfoPolicy2 level %d\n", i);
+ else
+ printf("\nTrying QueryInfoPolicy level %d\n", i);
+
+ if (version2)
+ /* We can perform the cast, because both types are
+ structurally equal */
+ status = dcerpc_lsa_QueryInfoPolicy2(p, tctx,
+ (struct lsa_QueryInfoPolicy2*) &r);
+ else
+ status = dcerpc_lsa_QueryInfoPolicy(p, tctx, &r);
+
+ switch (i) {
+ case LSA_POLICY_INFO_MOD:
+ case LSA_POLICY_INFO_AUDIT_FULL_SET:
+ case LSA_POLICY_INFO_AUDIT_FULL_QUERY:
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
+ printf("Server should have failed level %u: %s\n", i, nt_errstr(status));
+ ret = false;
+ }
+ break;
+ case LSA_POLICY_INFO_DOMAIN:
+ case LSA_POLICY_INFO_ACCOUNT_DOMAIN:
+ case LSA_POLICY_INFO_L_ACCOUNT_DOMAIN:
+ case LSA_POLICY_INFO_DNS_INT:
+ case LSA_POLICY_INFO_DNS:
+ case LSA_POLICY_INFO_REPLICA:
+ case LSA_POLICY_INFO_QUOTA:
+ case LSA_POLICY_INFO_ROLE:
+ case LSA_POLICY_INFO_AUDIT_LOG:
+ case LSA_POLICY_INFO_AUDIT_EVENTS:
+ case LSA_POLICY_INFO_PD:
+ if (!NT_STATUS_IS_OK(status)) {
+ if (version2)
+ printf("QueryInfoPolicy2 failed - %s\n", nt_errstr(status));
+ else
+ printf("QueryInfoPolicy failed - %s\n", nt_errstr(status));
+ ret = false;
+ }
+ break;
+ default:
+ if (torture_setting_bool(tctx, "samba4", false)) {
+ /* Other levels not implemented yet */
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_INFO_CLASS)) {
+ if (version2)
+ printf("QueryInfoPolicy2 failed - %s\n", nt_errstr(status));
+ else
+ printf("QueryInfoPolicy failed - %s\n", nt_errstr(status));
+ ret = false;
+ }
+ } else if (!NT_STATUS_IS_OK(status)) {
+ if (version2)
+ printf("QueryInfoPolicy2 failed - %s\n", nt_errstr(status));
+ else
+ printf("QueryInfoPolicy failed - %s\n", nt_errstr(status));
+ ret = false;
+ }
+ break;
+ }
+
+ if (NT_STATUS_IS_OK(status) && (i == LSA_POLICY_INFO_DNS
+ || i == LSA_POLICY_INFO_DNS_INT)) {
+ /* Let's look up some of these names */
+
+ struct lsa_TransNameArray tnames;
+ tnames.count = 14;
+ tnames.names = talloc_zero_array(tctx, struct lsa_TranslatedName, tnames.count);
+ tnames.names[0].name.string = info->dns.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(p, tctx, handle, &tnames);
+
+ }
+ }
+
+ return ret;
+}
+
+static bool test_QueryInfoPolicy(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ return test_QueryInfoPolicyCalls(false, p, tctx, handle);
+}
+
+static bool test_QueryInfoPolicy2(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ return test_QueryInfoPolicyCalls(true, p, tctx, handle);
+}
+
+static bool test_GetUserName(struct dcerpc_pipe *p,
+ struct torture_context *tctx)
+{
+ struct lsa_GetUserName r;
+ NTSTATUS status;
+ bool ret = true;
+ struct lsa_String *authority_name_p = NULL;
+ struct lsa_String *account_name_p = NULL;
+
+ printf("\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;
+
+ status = dcerpc_lsa_GetUserName(p, tctx, &r);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("GetUserName failed - %s\n", nt_errstr(status));
+ ret = false;
+ }
+
+ 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;
+
+ status = dcerpc_lsa_GetUserName(p, tctx, &r);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("GetUserName failed - %s\n", nt_errstr(status));
+ ret = false;
+ }
+
+ return ret;
+}
+
+bool test_lsa_Close(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct lsa_Close r;
+ struct policy_handle handle2;
+
+ printf("\nTesting Close\n");
+
+ r.in.handle = handle;
+ r.out.handle = &handle2;
+
+ status = dcerpc_lsa_Close(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Close failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = dcerpc_lsa_Close(p, tctx, &r);
+ /* its really a fault - we need a status code for rpc fault */
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ printf("Close failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ printf("\n");
+
+ return true;
+}
+
+bool torture_rpc_lsa(struct torture_context *tctx)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p;
+ bool ret = true;
+ struct policy_handle *handle;
+ struct test_join *join = NULL;
+ struct cli_credentials *machine_creds;
+
+ status = torture_rpc_connection(tctx, &p, &ndr_table_lsarpc);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ if (!test_OpenPolicy(p, tctx)) {
+ ret = false;
+ }
+
+ if (!test_lsa_OpenPolicy2(p, tctx, &handle)) {
+ ret = false;
+ }
+
+ if (handle) {
+ join = torture_join_domain(tctx, TEST_MACHINENAME, ACB_WSTRUST, &machine_creds);
+ if (!join) {
+ ret = false;
+ }
+ if (!test_LookupNames_wellknown(p, tctx, handle)) {
+ ret = false;
+ }
+
+ if (!test_LookupNames_bogus(p, tctx, handle)) {
+ ret = false;
+ }
+
+ if (!test_LookupSids_async(p, tctx, handle)) {
+ ret = false;
+ }
+
+ if (!test_QueryDomainInfoPolicy(p, tctx, handle)) {
+ ret = false;
+ }
+
+ if (!test_CreateAccount(p, tctx, handle)) {
+ ret = false;
+ }
+
+ if (!test_CreateSecret(p, tctx, handle)) {
+ ret = false;
+ }
+ if (!test_CreateTrustedDomain(p, tctx, handle)) {
+ ret = false;
+ }
+
+ if (!test_CreateTrustedDomainEx2(p, tctx, handle)) {
+ ret = false;
+ }
+
+ if (!test_EnumAccounts(p, tctx, handle)) {
+ ret = false;
+ }
+
+ if (!test_EnumPrivs(p, tctx, handle)) {
+ ret = false;
+ }
+
+ if (!test_QueryInfoPolicy(p, tctx, handle)) {
+ ret = false;
+ }
+
+ if (!test_QueryInfoPolicy2(p, tctx, handle)) {
+ ret = false;
+ }
+
+ if (!test_Delete(p, tctx, handle)) {
+ ret = false;
+ }
+
+ if (!test_many_LookupSids(p, tctx, handle)) {
+ ret = false;
+ }
+
+ if (!test_lsa_Close(p, tctx, handle)) {
+ ret = false;
+ }
+
+ torture_leave_domain(tctx, join);
+
+ } else {
+ if (!test_many_LookupSids(p, tctx, handle)) {
+ ret = false;
+ }
+ }
+
+ if (!test_GetUserName(p, tctx)) {
+ ret = false;
+ }
+
+ return ret;
+}
+
+bool torture_rpc_lsa_get_user(struct torture_context *tctx)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p;
+ bool ret = true;
+
+ status = torture_rpc_connection(tctx, &p, &ndr_table_lsarpc);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ if (!test_GetUserName(p, 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;
+
+ if (!test_OpenPolicy(p, tctx)) {
+ ret = false;
+ }
+
+ if (!test_lsa_OpenPolicy2(p, 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(p, tctx, handle, &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(p, tctx, handle, &tnames2, true)) {
+ ret = false;
+ }
+
+ if (!test_LookupNames3(p, tctx, handle, &tnames2, true)) {
+ ret = false;
+ }
+
+ if (!test_lsa_Close(p, 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;
+}
diff --git a/source4/torture/rpc/lsa_lookup.c b/source4/torture/rpc/lsa_lookup.c
new file mode 100644
index 0000000000..0a4c9904d7
--- /dev/null
+++ b/source4/torture/rpc/lsa_lookup.c
@@ -0,0 +1,414 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "lib/events/events.h"
+#include "libnet/libnet_join.h"
+#include "torture/rpc/rpc.h"
+#include "librpc/gen_ndr/ndr_lsa_c.h"
+#include "libcli/security/security.h"
+
+static bool open_policy(TALLOC_CTX *mem_ctx, struct dcerpc_pipe *p,
+ struct policy_handle **handle)
+{
+ struct lsa_ObjectAttribute attr;
+ struct lsa_QosInfo qos;
+ struct lsa_OpenPolicy2 r;
+ NTSTATUS status;
+
+ *handle = talloc(mem_ctx, 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;
+
+ status = dcerpc_lsa_OpenPolicy2(p, mem_ctx, &r);
+
+ return NT_STATUS_IS_OK(status);
+}
+
+static bool get_domainsid(TALLOC_CTX *mem_ctx, struct dcerpc_pipe *p,
+ struct policy_handle *handle,
+ struct dom_sid **sid)
+{
+ struct lsa_QueryInfoPolicy r;
+ union lsa_PolicyInformation *info = NULL;
+ NTSTATUS status;
+
+ r.in.level = LSA_POLICY_INFO_DOMAIN;
+ r.in.handle = handle;
+ r.out.info = &info;
+
+ status = dcerpc_lsa_QueryInfoPolicy(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) return false;
+
+ *sid = info->domain.sid;
+ return true;
+}
+
+static NTSTATUS lookup_sids(TALLOC_CTX *mem_ctx, uint16_t level,
+ struct dcerpc_pipe *p,
+ 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;
+
+ names->count = 0;
+ names->names = NULL;
+
+ sidarray.num_sids = num_sids;
+ sidarray.sids = talloc_array(mem_ctx, struct lsa_SidPtr, num_sids);
+
+ for (i=0; i<num_sids; i++) {
+ sidarray.sids[i].sid = sids[i];
+ }
+
+ r.in.handle = handle;
+ r.in.sids = &sidarray;
+ r.in.names = names;
+ r.in.level = level;
+ r.in.count = &count;
+ r.out.names = names;
+ r.out.count = &count;
+ r.out.domains = &domains;
+
+ return dcerpc_lsa_LookupSids(p, mem_ctx, &r);
+}
+
+static const char *sid_type_lookup(enum lsa_SidType r)
+{
+ switch (r) {
+ case SID_NAME_USE_NONE: return "SID_NAME_USE_NONE"; break;
+ case SID_NAME_USER: return "SID_NAME_USER"; break;
+ case SID_NAME_DOM_GRP: return "SID_NAME_DOM_GRP"; break;
+ case SID_NAME_DOMAIN: return "SID_NAME_DOMAIN"; break;
+ case SID_NAME_ALIAS: return "SID_NAME_ALIAS"; break;
+ case SID_NAME_WKN_GRP: return "SID_NAME_WKN_GRP"; break;
+ case SID_NAME_DELETED: return "SID_NAME_DELETED"; break;
+ case SID_NAME_INVALID: return "SID_NAME_INVALID"; break;
+ case SID_NAME_UNKNOWN: return "SID_NAME_UNKNOWN"; break;
+ case SID_NAME_COMPUTER: return "SID_NAME_COMPUTER"; break;
+ }
+ return "Invalid sid type\n";
+}
+
+static bool test_lookupsids(TALLOC_CTX *mem_ctx, struct dcerpc_pipe *p,
+ struct policy_handle *handle,
+ struct dom_sid **sids, uint32_t num_sids,
+ int level, NTSTATUS expected_result,
+ enum lsa_SidType *types)
+{
+ struct lsa_TransNameArray names;
+ NTSTATUS status;
+ uint32_t i;
+ bool ret = true;
+
+ status = lookup_sids(mem_ctx, level, p, handle, sids, num_sids,
+ &names);
+ if (!NT_STATUS_EQUAL(status, expected_result)) {
+ printf("For level %d expected %s, got %s\n",
+ level, nt_errstr(expected_result),
+ nt_errstr(status));
+ return false;
+ }
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OK) &&
+ !NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED)) {
+ return true;
+ }
+
+ for (i=0; i<num_sids; i++) {
+ if (names.names[i].sid_type != types[i]) {
+ printf("In level %d, for sid %s expected %s, "
+ "got %s\n", level,
+ dom_sid_string(mem_ctx, sids[i]),
+ sid_type_lookup(types[i]),
+ sid_type_lookup(names.names[i].sid_type));
+ ret = false;
+ }
+ }
+ return ret;
+}
+
+static bool get_downleveltrust(struct torture_context *tctx, struct dcerpc_pipe *p,
+ struct policy_handle *handle,
+ struct dom_sid **sid)
+{
+ struct lsa_EnumTrustDom r;
+ uint32_t resume_handle = 0;
+ struct lsa_DomainList domains;
+ NTSTATUS status;
+ int i;
+
+ r.in.handle = handle;
+ r.in.resume_handle = &resume_handle;
+ r.in.max_size = 1000;
+ r.out.domains = &domains;
+ r.out.resume_handle = &resume_handle;
+
+ status = dcerpc_lsa_EnumTrustDom(p, tctx, &r);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES))
+ torture_fail(tctx, "no trusts");
+
+ if (domains.count == 0) {
+ torture_fail(tctx, "no trusts");
+ }
+
+ for (i=0; i<domains.count; i++) {
+ struct lsa_QueryTrustedDomainInfoBySid q;
+ union lsa_TrustedDomainInfo *info = NULL;
+
+ if (domains.domains[i].sid == NULL)
+ continue;
+
+ q.in.handle = handle;
+ q.in.dom_sid = domains.domains[i].sid;
+ q.in.level = 6;
+ q.out.info = &info;
+
+ status = dcerpc_lsa_QueryTrustedDomainInfoBySid(p, tctx, &q);
+ if (!NT_STATUS_IS_OK(status)) continue;
+
+ if ((info->info_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;
+ struct dom_sid *trusted_sid;
+ struct dom_sid *sids[NUM_SIDS];
+
+ status = torture_rpc_connection(torture, &p, &ndr_table_lsarpc);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_fail(torture, "unable to connect to table");
+ }
+
+ ret &= open_policy(torture, p, &handle);
+ if (!ret) return false;
+
+ ret &= get_domainsid(torture, p, handle, &dom_sid);
+ if (!ret) return false;
+
+ ret &= get_downleveltrust(torture, p, 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, p, 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, p, 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, p, 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, p, 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, p, handle, sids, NUM_SIDS, 4,
+ STATUS_SOME_UNMAPPED, types);
+ }
+
+ ret &= test_lookupsids(torture, p, 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, p, handle, sids, NUM_SIDS, 6,
+ STATUS_SOME_UNMAPPED, types);
+ }
+
+ ret &= test_lookupsids(torture, p, handle, sids, NUM_SIDS, 7,
+ NT_STATUS_INVALID_PARAMETER, NULL);
+ ret &= test_lookupsids(torture, p, handle, sids, NUM_SIDS, 8,
+ NT_STATUS_INVALID_PARAMETER, NULL);
+ ret &= test_lookupsids(torture, p, handle, sids, NUM_SIDS, 9,
+ NT_STATUS_INVALID_PARAMETER, NULL);
+ ret &= test_lookupsids(torture, p, 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;
+
+ struct dom_sid **sids;
+ 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;
+ NTSTATUS status;
+ const char *dom_sid = "S-1-5-21-1111111111-2222222222-3333333333";
+ const char *dom_admin_sid;
+
+ if (!open_policy(tctx, p, &handle)) {
+ return false;
+ }
+
+ dom_admin_sid = talloc_asprintf(tctx, "%s-%d", dom_sid, 512);
+
+ sids = talloc_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_array(tctx, struct lsa_SidPtr, num_sids);
+
+ for (i=0; i<num_sids; i++) {
+ sidarray.sids[i].sid = sids[i];
+ }
+
+ r.in.handle = handle;
+ r.in.sids = &sidarray;
+ r.in.names = &names;
+ r.in.level = LSA_LOOKUP_NAMES_ALL;
+ r.in.count = &count;
+ r.out.names = &names;
+ r.out.count = &count;
+ r.out.domains = &domains;
+
+ status = dcerpc_lsa_LookupSids(p, tctx, &r);
+
+ torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NONE_MAPPED,
+ "unexpected error code");
+
+ torture_assert_int_equal(tctx, names.count, num_sids,
+ "unexpected names count");
+ torture_assert(tctx, names.names,
+ "unexpected names pointer");
+ torture_assert_str_equal(tctx, names.names[0].name.string, dom_admin_sid,
+ "unexpected names[0].string");
+
+#if 0
+ /* vista sp1 passes, w2k3 sp2 fails */
+ torture_assert_int_equal(tctx, domains->count, 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/mgmt.c b/source4/torture/rpc/mgmt.c
new file mode 100644
index 0000000000..7f618ab776
--- /dev/null
+++ b/source4/torture/rpc/mgmt.c
@@ -0,0 +1,270 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/ndr_mgmt_c.h"
+#include "auth/gensec/gensec.h"
+#include "librpc/ndr/ndr_table.h"
+#include "torture/rpc/rpc.h"
+#include "param/param.h"
+#include "librpc/rpc/dcerpc_proto.h"
+
+
+/*
+ ask the server what interface IDs are available on this endpoint
+*/
+bool test_inq_if_ids(struct torture_context *tctx,
+ struct dcerpc_pipe *p, 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)
+{
+ NTSTATUS status;
+ 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;
+
+ status = dcerpc_mgmt_inq_if_ids(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("inq_if_ids failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("inq_if_ids gave error code %s\n", win_errstr(r.out.result));
+ return false;
+ }
+
+ if (!vector) {
+ printf("inq_if_ids gave NULL if_id_vector\n");
+ return false;
+ }
+
+ for (i=0;i<vector->count;i++) {
+ struct ndr_syntax_id *id = vector->if_id[i].id;
+ if (!id) continue;
+
+ printf("\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 dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx)
+{
+ NTSTATUS status;
+ 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;
+
+ status = dcerpc_mgmt_inq_stats(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("inq_stats failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if (statistics.count != MGMT_STATS_ARRAY_MAX_SIZE) {
+ printf("Unexpected array size %d\n", statistics.count);
+ return false;
+ }
+
+ printf("\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(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx)
+{
+ NTSTATUS status;
+ struct mgmt_inq_princ_name r;
+ int i;
+ bool ret = false;
+
+ for (i=0;i<100;i++) {
+ r.in.authn_proto = i; /* DCERPC_AUTH_TYPE_* */
+ r.in.princ_name_size = 100;
+
+ status = dcerpc_mgmt_inq_princ_name(p, 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) {
+ printf("\tprinciple name for proto %u (%s) is '%s'\n",
+ i, name, r.out.princ_name);
+ } else {
+ printf("\tprinciple name for proto %u is '%s'\n",
+ i, r.out.princ_name);
+ }
+ }
+ }
+
+ if (!ret) {
+ printf("\tno principle names?\n");
+ }
+
+ return true;
+}
+
+static bool test_is_server_listening(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx)
+{
+ NTSTATUS status;
+ struct mgmt_is_server_listening r;
+ r.out.status = talloc(mem_ctx, uint32_t);
+
+ status = dcerpc_mgmt_is_server_listening(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("is_server_listening failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if (*r.out.status != 0 || r.out.result == 0) {
+ printf("\tserver is NOT listening\n");
+ } else {
+ printf("\tserver is listening\n");
+ }
+
+ return true;
+}
+
+static bool test_stop_server_listening(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx)
+{
+ NTSTATUS status;
+ struct mgmt_stop_server_listening r;
+
+ status = dcerpc_mgmt_stop_server_listening(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("stop_server_listening failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("\tserver refused to stop listening - %s\n", win_errstr(r.out.result));
+ } else {
+ printf("\tserver allowed a stop_server_listening request\n");
+ return false;
+ }
+
+ return true;
+}
+
+
+bool torture_rpc_mgmt(struct torture_context *torture)
+{
+ 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(torture, &b);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ for (l=ndr_table_list();l;l=l->next) {
+ 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;
+ }
+
+ printf("\nTesting pipe '%s'\n", l->table->name);
+
+ status = dcerpc_epm_map_binding(loop_ctx, b, l->table, NULL, 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;
+ }
+
+ lp_set_cmdline(torture->lp_ctx, "torture:binding", dcerpc_binding_string(loop_ctx, b));
+
+ status = torture_rpc_connection(torture, &p, &ndr_table_mgmt);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ printf("Interface not available - skipping\n");
+ talloc_free(loop_ctx);
+ continue;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(loop_ctx);
+ ret = false;
+ continue;
+ }
+
+ if (!test_is_server_listening(p, loop_ctx)) {
+ ret = false;
+ }
+
+ if (!test_stop_server_listening(p, loop_ctx)) {
+ ret = false;
+ }
+
+ if (!test_inq_stats(p, loop_ctx)) {
+ ret = false;
+ }
+
+ if (!test_inq_princ_name(p, loop_ctx)) {
+ ret = false;
+ }
+
+ if (!test_inq_if_ids(torture, p, 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 0000000000..ad94add071
--- /dev/null
+++ b/source4/torture/rpc/netlogon.c
@@ -0,0 +1,2372 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ test suite for netlogon rpc operations
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "lib/events/events.h"
+#include "auth/auth.h"
+#include "auth/gensec/gensec.h"
+#include "lib/cmdline/popt_common.h"
+#include "torture/rpc/rpc.h"
+#include "torture/rpc/netlogon.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_netlogon.h"
+#include "librpc/gen_ndr/ndr_lsa_c.h"
+#include "param/param.h"
+#include "libcli/security/security.h"
+
+#define TEST_MACHINE_NAME "torturetest"
+
+static bool test_LogonUasLogon(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct netr_LogonUasLogon r;
+ struct netr_UasInfo *info = NULL;
+
+ r.in.server_name = NULL;
+ r.in.account_name = cli_credentials_get_username(cmdline_credentials);
+ r.in.workstation = TEST_MACHINE_NAME;
+ r.out.info = &info;
+
+ status = dcerpc_netr_LogonUasLogon(p, 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;
+
+ r.in.server_name = NULL;
+ r.in.account_name = cli_credentials_get_username(cmdline_credentials);
+ r.in.workstation = TEST_MACHINE_NAME;
+ r.out.info = &info;
+
+ status = dcerpc_netr_LogonUasLogoff(p, 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 creds_CredentialState **creds_out)
+{
+ NTSTATUS status;
+ struct netr_ServerReqChallenge r;
+ struct netr_ServerAuthenticate a;
+ struct netr_Credential credentials1, credentials2, credentials3;
+ struct creds_CredentialState *creds;
+ const struct samr_Password *mach_password;
+ const char *machine_name;
+
+ mach_password = cli_credentials_get_nt_hash(credentials, tctx);
+ machine_name = cli_credentials_get_workstation(credentials);
+
+ torture_comment(tctx, "Testing ServerReqChallenge\n");
+
+ creds = talloc(tctx, struct creds_CredentialState);
+ torture_assert(tctx, creds != NULL, "memory allocation");
+
+ 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));
+
+ status = dcerpc_netr_ServerReqChallenge(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "ServerReqChallenge");
+
+ a.in.server_name = NULL;
+ a.in.account_name = talloc_asprintf(tctx, "%s$", machine_name);
+ a.in.secure_channel_type = SEC_CHAN_BDC;
+ a.in.computer_name = machine_name;
+ a.in.credentials = &credentials3;
+ a.out.return_credentials = &credentials3;
+
+ creds_client_init(creds, &credentials1, &credentials2,
+ mach_password, &credentials3,
+ 0);
+
+ torture_comment(tctx, "Testing ServerAuthenticate\n");
+
+ status = dcerpc_netr_ServerAuthenticate(p, tctx, &a);
+
+ /* This allows the tests to continue against the more fussy windows 2008 */
+ if (NT_STATUS_EQUAL(status, NT_STATUS_DOWNGRADE_DETECTED)) {
+ return test_SetupCredentials2(p, tctx, NETLOGON_NEG_AUTH2_ADS_FLAGS,
+ credentials, SEC_CHAN_BDC, creds_out);
+ }
+
+ torture_assert_ntstatus_ok(tctx, status, "ServerAuthenticate");
+
+ torture_assert(tctx, creds_client_check(creds, &credentials3),
+ "Credential chaining failed");
+
+ *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,
+ int sec_chan_type,
+ struct creds_CredentialState **creds_out)
+{
+ NTSTATUS status;
+ struct netr_ServerReqChallenge r;
+ struct netr_ServerAuthenticate2 a;
+ struct netr_Credential credentials1, credentials2, credentials3;
+ struct creds_CredentialState *creds;
+ const struct samr_Password *mach_password;
+ const char *machine_name;
+
+ mach_password = cli_credentials_get_nt_hash(machine_credentials, tctx);
+ machine_name = cli_credentials_get_workstation(machine_credentials);
+
+ torture_comment(tctx, "Testing ServerReqChallenge\n");
+
+ creds = talloc(tctx, struct creds_CredentialState);
+ torture_assert(tctx, creds != NULL, "memory allocation");
+
+ 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));
+
+ status = dcerpc_netr_ServerReqChallenge(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "ServerReqChallenge");
+
+ a.in.server_name = NULL;
+ a.in.account_name = talloc_asprintf(tctx, "%s$", machine_name);
+ a.in.secure_channel_type = sec_chan_type;
+ a.in.computer_name = machine_name;
+ a.in.negotiate_flags = &negotiate_flags;
+ a.out.negotiate_flags = &negotiate_flags;
+ a.in.credentials = &credentials3;
+ a.out.return_credentials = &credentials3;
+
+ creds_client_init(creds, &credentials1, &credentials2,
+ mach_password, &credentials3,
+ negotiate_flags);
+
+ torture_comment(tctx, "Testing ServerAuthenticate2\n");
+
+ status = dcerpc_netr_ServerAuthenticate2(p, tctx, &a);
+ torture_assert_ntstatus_ok(tctx, status, "ServerAuthenticate2");
+
+ torture_assert(tctx, creds_client_check(creds, &credentials3),
+ "Credential chaining failed");
+
+ torture_comment(tctx, "negotiate_flags=0x%08x\n", negotiate_flags);
+
+ *creds_out = creds;
+ return true;
+}
+
+
+static bool test_SetupCredentials3(struct dcerpc_pipe *p, struct torture_context *tctx,
+ uint32_t negotiate_flags,
+ struct cli_credentials *machine_credentials,
+ struct creds_CredentialState **creds_out)
+{
+ NTSTATUS status;
+ struct netr_ServerReqChallenge r;
+ struct netr_ServerAuthenticate3 a;
+ struct netr_Credential credentials1, credentials2, credentials3;
+ struct creds_CredentialState *creds;
+ struct samr_Password mach_password;
+ uint32_t rid;
+ const char *machine_name;
+ const char *plain_pass;
+
+ machine_name = cli_credentials_get_workstation(machine_credentials);
+ plain_pass = cli_credentials_get_password(machine_credentials);
+
+ torture_comment(tctx, "Testing ServerReqChallenge\n");
+
+ creds = talloc(tctx, struct creds_CredentialState);
+ torture_assert(tctx, creds != NULL, "memory allocation");
+
+ 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));
+
+ status = dcerpc_netr_ServerReqChallenge(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "ServerReqChallenge");
+
+ 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 = SEC_CHAN_BDC;
+ 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_client_init(creds, &credentials1, &credentials2,
+ &mach_password, &credentials3,
+ negotiate_flags);
+
+ torture_comment(tctx, "Testing ServerAuthenticate3\n");
+
+ status = dcerpc_netr_ServerAuthenticate3(p, tctx, &a);
+ torture_assert_ntstatus_ok(tctx, status, "ServerAuthenticate3");
+ torture_assert(tctx, 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 */
+ status = dcerpc_netr_ServerReqChallenge(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "ServerReqChallenge");
+
+ *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)
+{
+ NTSTATUS status;
+ struct netr_ServerPasswordSet r;
+ const char *password;
+ struct creds_CredentialState *creds;
+ struct netr_Authenticator credential, return_authenticator;
+ struct samr_Password new_password;
+
+ 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 = SEC_CHAN_BDC;
+ 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_str(tctx, 8);
+ E_md4hash(password, new_password.hash);
+
+ 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);
+
+ creds_client_authenticator(creds, &credential);
+
+ status = dcerpc_netr_ServerPasswordSet(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "ServerPasswordSet");
+
+ if (!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);
+
+ creds_client_authenticator(creds, &credential);
+
+ status = dcerpc_netr_ServerPasswordSet(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "ServerPasswordSet (2)");
+
+ if (!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(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *machine_credentials)
+{
+ NTSTATUS status;
+ struct netr_ServerPasswordSet2 r;
+ const char *password;
+ DATA_BLOB new_random_pass;
+ struct creds_CredentialState *creds;
+ struct samr_CryptPassword password_buf;
+ struct samr_Password nt_hash;
+ struct netr_Authenticator credential, return_authenticator;
+ struct netr_CryptPassword new_password;
+
+ 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 = SEC_CHAN_BDC;
+ 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_str(tctx, 8);
+ encode_pw_buffer(password_buf.data, password, STR_UNICODE);
+ 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);
+
+ creds_client_authenticator(creds, &credential);
+
+ status = dcerpc_netr_ServerPasswordSet2(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "ServerPasswordSet2");
+
+ if (!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);
+
+ if (!torture_setting_bool(tctx, "dangerous", false)) {
+ torture_comment(tctx,
+ "Not testing ability to set password to '', enable dangerous tests to perform this test\n");
+ } else {
+ /* by changing the machine password to ""
+ * we check if the server uses password restrictions
+ * for ServerPasswordSet2
+ * (win2k3 accepts "")
+ */
+ password = "";
+ encode_pw_buffer(password_buf.data, password, STR_UNICODE);
+ 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);
+
+ creds_client_authenticator(creds, &credential);
+
+ status = dcerpc_netr_ServerPasswordSet2(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "ServerPasswordSet2");
+
+ if (!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");
+
+ /* now try a random password */
+ password = generate_random_str(tctx, 8);
+ encode_pw_buffer(password_buf.data, password, STR_UNICODE);
+ 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);
+
+ creds_client_authenticator(creds, &credential);
+
+ status = dcerpc_netr_ServerPasswordSet2(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "ServerPasswordSet2 (2)");
+
+ if (!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);
+
+ creds_client_authenticator(creds, &credential);
+
+ status = dcerpc_netr_ServerPasswordSet2(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "ServerPasswordSet (3)");
+
+ if (!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);
+
+ 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 compleatly random password\n");
+
+ creds_client_authenticator(creds, &credential);
+
+ status = dcerpc_netr_ServerPasswordSet2(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "ServerPasswordSet (3)");
+
+ if (!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;
+}
+
+static bool test_GetPassword(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *machine_credentials)
+{
+ struct netr_ServerPasswordGet r;
+ struct creds_CredentialState *creds;
+ struct netr_Authenticator credential;
+ NTSTATUS status;
+ struct netr_Authenticator return_authenticator;
+ struct samr_Password password;
+
+ if (!test_SetupCredentials(p, tctx, machine_credentials, &creds)) {
+ return false;
+ }
+
+ 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 = SEC_CHAN_BDC;
+ 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(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "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 creds_CredentialState *creds;
+ struct netr_Authenticator credential;
+ NTSTATUS status;
+ struct netr_Authenticator return_authenticator;
+ struct samr_Password password, password2;
+
+ if (!test_SetupCredentials(p, tctx, machine_credentials, &creds)) {
+ return false;
+ }
+
+ 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 = SEC_CHAN_BDC;
+ r.in.computer_name = TEST_MACHINE_NAME;
+ r.in.credential = &credential;
+ r.out.return_authenticator = &return_authenticator;
+ r.out.password = &password;
+ r.out.password2 = &password2;
+
+ status = dcerpc_netr_ServerTrustPasswordsGet(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "ServerTrustPasswordsGet");
+
+ return true;
+}
+
+/*
+ try a netlogon SamLogon
+*/
+bool test_netlogon_ops(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct cli_credentials *credentials,
+ struct creds_CredentialState *creds)
+{
+ 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;
+ int flags = CLI_CRED_NTLM_AUTH;
+ if (lp_client_lanman_auth(tctx->lp_ctx)) {
+ flags |= CLI_CRED_LANMAN_AUTH;
+ }
+
+ if (lp_client_ntlmv2_auth(tctx->lp_ctx)) {
+ flags |= CLI_CRED_NTLMv2_AUTH;
+ }
+
+ cli_credentials_get_ntlm_username_domain(cmdline_credentials, 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(cmdline_credentials, tctx,
+ &flags,
+ chal,
+ 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_low = 0;
+ ninfo.identity_info.logon_id_high = 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 = 2;
+ 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);
+ creds_client_authenticator(creds, &auth);
+
+ r.in.validation_level = i;
+
+ status = dcerpc_netr_LogonSamLogon(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "LogonSamLogon failed");
+
+ torture_assert(tctx, creds_client_check(creds, &r.out.return_authenticator->cred),
+ "Credential chaining failed");
+ }
+
+ r.in.credential = NULL;
+
+ for (i=2;i<=3;i++) {
+
+ r.in.validation_level = i;
+
+ torture_comment(tctx, "Testing SamLogon with validation level %d and a NULL credential\n", i);
+
+ status = dcerpc_netr_LogonSamLogon(p, tctx, &r);
+ torture_assert_ntstatus_equal(tctx, status, NT_STATUS_INVALID_PARAMETER,
+ "LogonSamLogon expected INVALID_PARAMETER");
+
+ }
+
+ return true;
+}
+
+/*
+ try a netlogon SamLogon
+*/
+static bool test_SamLogon(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *credentials)
+{
+ struct creds_CredentialState *creds;
+
+ if (!test_SetupCredentials(p, tctx, credentials, &creds)) {
+ return false;
+ }
+
+ return test_netlogon_ops(p, tctx, credentials, creds);
+}
+
+/* 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)
+{
+ NTSTATUS status;
+ struct netr_DatabaseSync r;
+ struct 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;
+
+ 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;i<ARRAY_SIZE(database_ids);i++) {
+
+ uint32_t sync_context = 0;
+
+ r.in.database_id = database_ids[i];
+ r.in.sync_context = &sync_context;
+ r.out.sync_context = &sync_context;
+
+ torture_comment(tctx, "Testing DatabaseSync of id %d\n", r.in.database_id);
+
+ do {
+ creds_client_authenticator(creds, &credential);
+
+ r.in.credential = &credential;
+
+ status = dcerpc_netr_DatabaseSync(p, tctx, &r);
+ if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES))
+ break;
+
+ /* Native mode servers don't do this */
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
+ return true;
+ }
+ torture_assert_ntstatus_ok(tctx, status, "DatabaseSync");
+
+ if (!creds_client_check(creds, &r.out.return_authenticator->cred)) {
+ 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(status, 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)
+{
+ NTSTATUS status;
+ struct netr_DatabaseDeltas r;
+ struct 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;
+
+ 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;i<ARRAY_SIZE(database_ids);i++) {
+ r.in.database_id = database_ids[i];
+ r.in.sequence_num = &sequence_nums[r.in.database_id];
+
+ if (*r.in.sequence_num == 0) continue;
+
+ *r.in.sequence_num -= 1;
+
+ torture_comment(tctx, "Testing DatabaseDeltas of id %d at %llu\n",
+ r.in.database_id, (unsigned long long)*r.in.sequence_num);
+
+ do {
+ creds_client_authenticator(creds, &credential);
+
+ status = dcerpc_netr_DatabaseDeltas(p, tctx, &r);
+ if (NT_STATUS_EQUAL(status,
+ NT_STATUS_SYNCHRONIZATION_REQUIRED)) {
+ torture_comment(tctx, "not considering %s to be an error\n",
+ nt_errstr(status));
+ return true;
+ }
+ if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES))
+ break;
+
+ torture_assert_ntstatus_ok(tctx, status, "DatabaseDeltas");
+
+ if (!creds_client_check(creds, &return_authenticator.cred)) {
+ torture_comment(tctx, "Credential chaining failed\n");
+ }
+
+ (*r.in.sequence_num)++;
+ } while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES));
+ }
+
+ return true;
+}
+
+static bool test_DatabaseRedo(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *machine_credentials)
+{
+ NTSTATUS status;
+ struct netr_DatabaseRedo r;
+ struct creds_CredentialState *creds;
+ struct netr_Authenticator credential;
+ struct netr_Authenticator return_authenticator;
+ struct netr_DELTA_ENUM_ARRAY *delta_enum_array = NULL;
+ struct netr_ChangeLogEntry e;
+ struct dom_sid null_sid, *sid;
+ int i,d;
+
+ 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;
+
+ 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;i<ARRAY_SIZE(changes);i++) {
+
+ if (d != changes[i].db_index) {
+ continue;
+ }
+
+ creds_client_authenticator(creds, &credential);
+
+ r.in.credential = &credential;
+
+ e.serial_number1 = 0;
+ e.serial_number2 = 0;
+ e.object_rid = changes[i].rid;
+ e.flags = changes[i].flags;
+ e.db_index = changes[i].db_index;
+ e.delta_type = changes[i].delta_type;
+
+ switch (changes[i].flags & (NETR_CHANGELOG_NAME_INCLUDED | NETR_CHANGELOG_SID_INCLUDED)) {
+ case NETR_CHANGELOG_SID_INCLUDED:
+ e.object.object_sid = changes[i].sid;
+ break;
+ case NETR_CHANGELOG_NAME_INCLUDED:
+ e.object.object_name = changes[i].name;
+ break;
+ default:
+ break;
+ }
+
+ r.in.change_log_entry = e;
+
+ torture_comment(tctx, "Testing DatabaseRedo with database %s and %s\n",
+ database, changes[i].comment);
+
+ status = dcerpc_netr_DatabaseRedo(p, tctx, &r);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
+ return true;
+ }
+
+ torture_assert_ntstatus_equal(tctx, status, changes[i].expected_error, changes[i].comment);
+ if (delta_enum_array) {
+ torture_assert_int_equal(tctx,
+ delta_enum_array->num_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 (!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)
+{
+ NTSTATUS status;
+ struct netr_AccountDeltas r;
+ struct 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;
+
+ 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;
+ 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 */
+ status = dcerpc_netr_AccountDeltas(p, tctx, &r);
+ torture_assert_ntstatus_equal(tctx, status, 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)
+{
+ NTSTATUS status;
+ struct netr_AccountSync r;
+ struct 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;
+
+ 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;
+ 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 */
+ status = dcerpc_netr_AccountSync(p, tctx, &r);
+ torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_IMPLEMENTED, "AccountSync");
+
+ return true;
+}
+
+/*
+ try a netlogon GetDcName
+*/
+static bool test_GetDcName(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct netr_GetDcName r;
+ const char *dcname = NULL;
+
+ r.in.logon_server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.domainname = lp_workgroup(tctx->lp_ctx);
+ r.out.dcname = &dcname;
+
+ status = dcerpc_netr_GetDcName(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "GetDcName");
+ torture_assert_werr_ok(tctx, r.out.result, "GetDcName");
+
+ torture_comment(tctx, "\tDC is at '%s'\n", dcname);
+
+ return true;
+}
+
+/*
+ try a netlogon LogonControl
+*/
+static bool test_LogonControl(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct netr_LogonControl r;
+ union netr_CONTROL_QUERY_INFORMATION info;
+ int i;
+
+ r.in.logon_server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.function_code = 1;
+ r.out.info = &info;
+
+ for (i=1;i<4;i++) {
+ r.in.level = i;
+
+ torture_comment(tctx, "Testing LogonControl level %d\n", i);
+
+ status = dcerpc_netr_LogonControl(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "LogonControl");
+ }
+
+ return true;
+}
+
+
+/*
+ try a netlogon GetAnyDCName
+*/
+static bool test_GetAnyDCName(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct netr_GetAnyDCName r;
+ const char *dcname = NULL;
+
+ r.in.logon_server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.domainname = lp_workgroup(tctx->lp_ctx);
+ r.out.dcname = &dcname;
+
+ status = dcerpc_netr_GetAnyDCName(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "GetAnyDCName");
+ torture_assert_werr_ok(tctx, r.out.result, "GetAnyDCName");
+
+ if (dcname) {
+ torture_comment(tctx, "\tDC is at '%s'\n", dcname);
+ }
+
+ return true;
+}
+
+
+/*
+ try a netlogon LogonControl2
+*/
+static bool test_LogonControl2(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct netr_LogonControl2 r;
+ union netr_CONTROL_DATA_INFORMATION data;
+ union netr_CONTROL_QUERY_INFORMATION query;
+ int i;
+
+ data.domain = lp_workgroup(tctx->lp_ctx);
+
+ 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 level %d function %d\n",
+ i, r.in.function_code);
+
+ status = dcerpc_netr_LogonControl2(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "LogonControl");
+ }
+
+ data.domain = lp_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 level %d function %d\n",
+ i, r.in.function_code);
+
+ status = dcerpc_netr_LogonControl2(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "LogonControl");
+ }
+
+ data.domain = lp_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 level %d function %d\n",
+ i, r.in.function_code);
+
+ status = dcerpc_netr_LogonControl2(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "LogonControl");
+ }
+
+ 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 level %d function %d\n",
+ i, r.in.function_code);
+
+ status = dcerpc_netr_LogonControl2(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "LogonControl");
+ }
+
+ return true;
+}
+
+/*
+ try a netlogon DatabaseSync2
+*/
+static bool test_DatabaseSync2(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *machine_credentials)
+{
+ NTSTATUS status;
+ struct netr_DatabaseSync2 r;
+ struct netr_DELTA_ENUM_ARRAY *delta_enum_array = NULL;
+ struct netr_Authenticator return_authenticator, credential;
+
+ struct creds_CredentialState *creds;
+ const uint32_t database_ids[] = {0, 1, 2};
+ int i;
+
+ if (!test_SetupCredentials2(p, tctx, NETLOGON_NEG_AUTH2_FLAGS,
+ machine_credentials,
+ SEC_CHAN_BDC, &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;i<ARRAY_SIZE(database_ids);i++) {
+
+ uint32_t sync_context = 0;
+
+ r.in.database_id = database_ids[i];
+ r.in.sync_context = &sync_context;
+ r.out.sync_context = &sync_context;
+ r.in.restart_state = 0;
+
+ torture_comment(tctx, "Testing DatabaseSync2 of id %d\n", r.in.database_id);
+
+ do {
+ creds_client_authenticator(creds, &credential);
+
+ r.in.credential = &credential;
+
+ status = dcerpc_netr_DatabaseSync2(p, tctx, &r);
+ if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES))
+ break;
+
+ /* Native mode servers don't do this */
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
+ return true;
+ }
+
+ torture_assert_ntstatus_ok(tctx, status, "DatabaseSync2");
+
+ if (!creds_client_check(creds, &r.out.return_authenticator->cred)) {
+ torture_comment(tctx, "Credential chaining failed\n");
+ }
+
+ } while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES));
+ }
+
+ return true;
+}
+
+
+/*
+ try a netlogon LogonControl2Ex
+*/
+static bool test_LogonControl2Ex(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct netr_LogonControl2Ex r;
+ union netr_CONTROL_DATA_INFORMATION data;
+ union netr_CONTROL_QUERY_INFORMATION query;
+ int i;
+
+ data.domain = lp_workgroup(tctx->lp_ctx);
+
+ 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 level %d function %d\n",
+ i, r.in.function_code);
+
+ status = dcerpc_netr_LogonControl2Ex(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "LogonControl");
+ }
+
+ data.domain = lp_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 level %d function %d\n",
+ i, r.in.function_code);
+
+ status = dcerpc_netr_LogonControl2Ex(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "LogonControl");
+ }
+
+ data.domain = lp_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 level %d function %d\n",
+ i, r.in.function_code);
+
+ status = dcerpc_netr_LogonControl2Ex(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "LogonControl");
+ }
+
+ 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 level %d function %d\n",
+ i, r.in.function_code);
+
+ status = dcerpc_netr_LogonControl2Ex(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "LogonControl");
+ }
+
+ 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;
+
+ 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(p, 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;
+
+ 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(p, 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; i<r.out.trusts->count; i++) {
+
+ /* get info for transitive forest trusts */
+
+ if (r.out.trusts->array[i].trust_attributes & NETR_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;
+
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.out.trusted_domains_blob = &trusted_domains_blob;
+
+ status = dcerpc_netr_NetrEnumerateTrustedDomains(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "netr_NetrEnumerateTrustedDomains");
+ torture_assert_werr_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;
+
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.out.dom_trust_list = &dom_trust_list;
+
+ status = dcerpc_netr_NetrEnumerateTrustedDomainsEx(p, 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;
+
+ if (torture_setting_bool(tctx, "samba4", false))
+ torture_skip(tctx, "skipping DsRGetSiteName test against Samba4");
+
+ r.in.computer_name = computer_name;
+ r.out.site = &site;
+ torture_comment(tctx, "Testing netr_DsRGetSiteName\n");
+
+ status = dcerpc_netr_DsRGetSiteName(p, 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");
+
+ r.in.computer_name = talloc_asprintf(tctx, "\\\\%s", computer_name);
+ torture_comment(tctx,
+ "Testing netr_DsRGetSiteName with broken computer name: %s\n", r.in.computer_name);
+
+ status = dcerpc_netr_DsRGetSiteName(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "DsRGetSiteName");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_COMPUTERNAME, "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;
+
+ r.in.server_unc = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.domain_name = talloc_asprintf(tctx, "%s", lp_realm(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(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "DsRGetDCName");
+ torture_assert_werr_ok(tctx, r.out.result, "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;
+
+ r.in.server_unc = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.domain_name = talloc_asprintf(tctx, "%s", lp_realm(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(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "netr_DsRGetDCNameEx");
+ torture_assert_werr_ok(tctx, r.out.result, "netr_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;
+
+ 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 = talloc_asprintf(tctx, "%s", lp_realm(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(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "netr_DsRGetDCNameEx2");
+ torture_assert_werr_ok(tctx, r.out.result, "netr_DsRGetDCNameEx2");
+
+ torture_comment(tctx, "Testing netr_DsRGetDCNameEx2 with client acount\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(p, 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);
+}
+
+static bool test_netr_DsrGetDcSiteCoverageW(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct netr_DsrGetDcSiteCoverageW r;
+ struct DcSitesCtr *ctr = NULL;
+
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.out.ctr = &ctr;
+
+ status = dcerpc_netr_DsrGetDcSiteCoverageW(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "failed");
+ torture_assert_werr_ok(tctx, r.out.result, "failed");
+
+ return true;
+}
+
+static bool test_netr_DsRAddressToSitenamesW(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct netr_DsRAddressToSitenamesW r;
+ struct netr_DsRAddress addr;
+ struct netr_DsRAddressToSitenamesWCtr *ctr;
+
+ ctr = talloc(tctx, struct netr_DsRAddressToSitenamesWCtr);
+
+ addr.size = 16;
+ addr.buffer = talloc_zero_array(tctx, uint8_t, addr.size);
+
+ addr.buffer[0] = 2; /* AF_INET */
+ addr.buffer[4] = 127;
+ addr.buffer[5] = 0;
+ addr.buffer[6] = 0;
+ addr.buffer[7] = 1;
+
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.count = 1;
+ r.in.addresses = talloc_zero_array(tctx, struct netr_DsRAddress, r.in.count);
+ r.in.addresses[0] = addr;
+ r.out.ctr = &ctr;
+
+ status = dcerpc_netr_DsRAddressToSitenamesW(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "failed");
+ torture_assert_werr_ok(tctx, r.out.result, "failed");
+
+ return true;
+}
+
+static bool test_netr_DsRAddressToSitenamesExW(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct netr_DsRAddressToSitenamesExW r;
+ struct netr_DsRAddress addr;
+ struct netr_DsRAddressToSitenamesExWCtr *ctr;
+
+ ctr = talloc(tctx, struct netr_DsRAddressToSitenamesExWCtr);
+
+ addr.size = 16;
+ addr.buffer = talloc_zero_array(tctx, uint8_t, addr.size);
+
+ addr.buffer[0] = 2; /* AF_INET */
+ addr.buffer[4] = 127;
+ addr.buffer[5] = 0;
+ addr.buffer[6] = 0;
+ addr.buffer[7] = 1;
+
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.count = 1;
+ r.in.addresses = talloc_zero_array(tctx, struct netr_DsRAddress, r.in.count);
+ r.in.addresses[0] = addr;
+ r.out.ctr = &ctr;
+
+ status = dcerpc_netr_DsRAddressToSitenamesExW(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "failed");
+ torture_assert_werr_ok(tctx, r.out.result, "failed");
+
+ return true;
+}
+
+static bool test_netr_ServerGetTrustInfo(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *machine_credentials)
+{
+ NTSTATUS status;
+ 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 creds_CredentialState *creds;
+
+ if (!test_SetupCredentials3(p, tctx, NETLOGON_NEG_AUTH2_ADS_FLAGS,
+ machine_credentials, &creds)) {
+ return false;
+ }
+
+ 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 = SEC_CHAN_BDC;
+ 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;
+
+ status = dcerpc_netr_ServerGetTrustInfo(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "failed");
+ torture_assert(tctx, creds_client_check(creds, &return_authenticator.cred), "Credential chaining failed");
+
+ return true;
+}
+
+
+static bool test_GetDomainInfo(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *machine_credentials)
+{
+ NTSTATUS status;
+ struct netr_LogonGetDomainInfo r;
+ struct netr_DomainQuery1 q1;
+ struct netr_Authenticator a;
+ struct creds_CredentialState *creds;
+ union netr_DomainInfo info;
+
+ if (!test_SetupCredentials3(p, tctx, NETLOGON_NEG_AUTH2_ADS_FLAGS,
+ machine_credentials, &creds)) {
+ return false;
+ }
+
+ ZERO_STRUCT(r);
+
+ 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.level = 1;
+ r.in.credential = &a;
+ r.in.return_authenticator = &a;
+ r.out.return_authenticator = &a;
+ r.out.info = &info;
+
+ r.in.query.query1 = &q1;
+ ZERO_STRUCT(q1);
+
+ /* this should really be the fully qualified name */
+ q1.workstation_domain = TEST_MACHINE_NAME;
+ q1.workstation_site = "Default-First-Site-Name";
+ q1.blob2.length = 0;
+ q1.blob2.size = 0;
+ q1.blob2.array = NULL;
+ q1.product.string = "product string";
+
+ torture_comment(tctx, "Testing netr_LogonGetDomainInfo\n");
+
+ status = dcerpc_netr_LogonGetDomainInfo(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "netr_LogonGetDomainInfo");
+ torture_assert(tctx, creds_client_check(creds, &a.cred), "Credential chaining failed");
+
+ torture_comment(tctx, "Testing netr_LogonGetDomainInfo 2nd call\n");
+ creds_client_authenticator(creds, &a);
+
+ status = dcerpc_netr_LogonGetDomainInfo(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "netr_LogonGetDomainInfo");
+ torture_assert(tctx, creds_client_check(creds, &a.cred), "Credential chaining failed");
+
+ return true;
+}
+
+
+static void async_callback(struct rpc_request *req)
+{
+ int *counter = (int *)req->async.private_data;
+ if (NT_STATUS_IS_OK(req->status)) {
+ (*counter)++;
+ }
+}
+
+static bool test_GetDomainInfo_async(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *machine_credentials)
+{
+ NTSTATUS status;
+ struct netr_LogonGetDomainInfo r;
+ struct netr_DomainQuery1 q1;
+ struct netr_Authenticator a;
+#define ASYNC_COUNT 100
+ struct creds_CredentialState *creds;
+ struct creds_CredentialState *creds_async[ASYNC_COUNT];
+ struct rpc_request *req[ASYNC_COUNT];
+ int i;
+ int *async_counter = talloc(tctx, int);
+ union netr_DomainInfo info;
+
+ if (!test_SetupCredentials3(p, tctx, NETLOGON_NEG_AUTH2_ADS_FLAGS,
+ machine_credentials, &creds)) {
+ 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.level = 1;
+ r.in.credential = &a;
+ r.in.return_authenticator = &a;
+ r.out.return_authenticator = &a;
+ r.out.info = &info;
+
+ r.in.query.query1 = &q1;
+ ZERO_STRUCT(q1);
+
+ /* this should really be the fully qualified name */
+ q1.workstation_domain = TEST_MACHINE_NAME;
+ q1.workstation_site = "Default-First-Site-Name";
+ q1.blob2.length = 0;
+ q1.blob2.size = 0;
+ q1.blob2.array = NULL;
+ q1.product.string = "product string";
+
+ torture_comment(tctx, "Testing netr_LogonGetDomainInfo - async count %d\n", ASYNC_COUNT);
+
+ *async_counter = 0;
+
+ for (i=0;i<ASYNC_COUNT;i++) {
+ creds_client_authenticator(creds, &a);
+
+ creds_async[i] = (struct creds_CredentialState *)talloc_memdup(creds, creds, sizeof(*creds));
+ req[i] = dcerpc_netr_LogonGetDomainInfo_send(p, tctx, &r);
+
+ req[i]->async.callback = async_callback;
+ req[i]->async.private_data = async_counter;
+
+ /* even with this flush per request a w2k3 server seems to
+ clag with multiple outstanding requests. bleergh. */
+ torture_assert_int_equal(tctx, event_loop_once(dcerpc_event_context(p)), 0,
+ "event_loop_once failed");
+ }
+
+ for (i=0;i<ASYNC_COUNT;i++) {
+ status = dcerpc_ndr_request_recv(req[i]);
+
+ torture_assert_ntstatus_ok(tctx, status, "netr_LogonGetDomainInfo_async");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "netr_LogonGetDomainInfo_async");
+
+ torture_assert(tctx, 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_counter);
+
+ torture_assert_int_equal(tctx, (*async_counter), ASYNC_COUNT, "int");
+
+ return true;
+}
+
+static bool test_ManyGetDCName(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ 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;
+
+ int i;
+
+ if (p->conn->transport.transport != NCACN_NP) {
+ return true;
+ }
+
+ torture_comment(tctx, "Torturing GetDCName\n");
+
+ status = dcerpc_secondary_connection(p, &p2, p->binding);
+ torture_assert_ntstatus_ok(tctx, status, "Failed to create secondary connection");
+
+ status = dcerpc_bind_auth_none(p2, &ndr_table_lsarpc);
+ torture_assert_ntstatus_ok(tctx, status, "Failed to create bind on secondary connection");
+
+ 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;
+
+ status = dcerpc_lsa_OpenPolicy2(p2, tctx, &o);
+ torture_assert_ntstatus_ok(tctx, status, "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;
+
+ status = dcerpc_lsa_EnumTrustDom(p2, tctx, &t);
+
+ if ((!NT_STATUS_IS_OK(status) &&
+ (!NT_STATUS_EQUAL(status, 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; i<domains.count * 4; i++) {
+ struct lsa_DomainInfo *info =
+ &domains.domains[rand()%domains.count];
+
+ d.in.domainname = info->name.string;
+
+ status = dcerpc_netr_GetAnyDCName(p, 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;
+}
+
+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_rpc_iface_tcase(suite, "netlogon",
+ &ndr_table_netlogon, TEST_MACHINE_NAME);
+
+ torture_rpc_tcase_add_test(tcase, "LogonUasLogon", test_LogonUasLogon);
+ torture_rpc_tcase_add_test(tcase, "LogonUasLogoff", test_LogonUasLogoff);
+ torture_rpc_tcase_add_test_creds(tcase, "SamLogon", test_SamLogon);
+ torture_rpc_tcase_add_test_creds(tcase, "SetPassword", test_SetPassword);
+ torture_rpc_tcase_add_test_creds(tcase, "SetPassword2", test_SetPassword2);
+ torture_rpc_tcase_add_test_creds(tcase, "GetPassword", test_GetPassword);
+ torture_rpc_tcase_add_test_creds(tcase, "GetTrustPasswords", test_GetTrustPasswords);
+ torture_rpc_tcase_add_test_creds(tcase, "GetDomainInfo", test_GetDomainInfo);
+ torture_rpc_tcase_add_test_creds(tcase, "DatabaseSync", test_DatabaseSync);
+ torture_rpc_tcase_add_test_creds(tcase, "DatabaseDeltas", test_DatabaseDeltas);
+ torture_rpc_tcase_add_test_creds(tcase, "DatabaseRedo", test_DatabaseRedo);
+ torture_rpc_tcase_add_test_creds(tcase, "AccountDeltas", test_AccountDeltas);
+ torture_rpc_tcase_add_test_creds(tcase, "AccountSync", test_AccountSync);
+ torture_rpc_tcase_add_test(tcase, "GetDcName", test_GetDcName);
+ torture_rpc_tcase_add_test(tcase, "ManyGetDCName", test_ManyGetDCName);
+ torture_rpc_tcase_add_test(tcase, "LogonControl", test_LogonControl);
+ torture_rpc_tcase_add_test(tcase, "GetAnyDCName", test_GetAnyDCName);
+ torture_rpc_tcase_add_test(tcase, "LogonControl2", test_LogonControl2);
+ torture_rpc_tcase_add_test_creds(tcase, "DatabaseSync2", test_DatabaseSync2);
+ torture_rpc_tcase_add_test(tcase, "LogonControl2Ex", test_LogonControl2Ex);
+ torture_rpc_tcase_add_test(tcase, "DsrEnumerateDomainTrusts", test_DsrEnumerateDomainTrusts);
+ torture_rpc_tcase_add_test(tcase, "NetrEnumerateTrustedDomains", test_netr_NetrEnumerateTrustedDomains);
+ torture_rpc_tcase_add_test(tcase, "NetrEnumerateTrustedDomainsEx", test_netr_NetrEnumerateTrustedDomainsEx);
+ test = torture_rpc_tcase_add_test_creds(tcase, "GetDomainInfo_async", test_GetDomainInfo_async);
+ test->dangerous = true;
+ torture_rpc_tcase_add_test(tcase, "DsRGetDCName", test_netr_DsRGetDCName);
+ torture_rpc_tcase_add_test(tcase, "DsRGetDCNameEx", test_netr_DsRGetDCNameEx);
+ torture_rpc_tcase_add_test(tcase, "DsRGetDCNameEx2", test_netr_DsRGetDCNameEx2);
+ torture_rpc_tcase_add_test(tcase, "DsrGetDcSiteCoverageW", test_netr_DsrGetDcSiteCoverageW);
+ torture_rpc_tcase_add_test(tcase, "DsRAddressToSitenamesW", test_netr_DsRAddressToSitenamesW);
+ torture_rpc_tcase_add_test(tcase, "DsRAddressToSitenamesExW", test_netr_DsRAddressToSitenamesExW);
+ torture_rpc_tcase_add_test_creds(tcase, "ServerGetTrustInfo", test_netr_ServerGetTrustInfo);
+
+ return suite;
+}
diff --git a/source4/torture/rpc/netlogon.h b/source4/torture/rpc/netlogon.h
new file mode 100644
index 0000000000..92d366b46a
--- /dev/null
+++ b/source4/torture/rpc/netlogon.h
@@ -0,0 +1,6 @@
+
+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 creds_CredentialState **creds_out);
diff --git a/source4/torture/rpc/ntsvcs.c b/source4/torture/rpc/ntsvcs.c
new file mode 100644
index 0000000000..04494b3223
--- /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 "lib/torture/torture.h"
+#include "torture/rpc/rpc.h"
+#include "librpc/gen_ndr/ndr_ntsvcs_c.h"
+#include "torture/util.h"
+#include "param/param.h"
+
+static bool test_PNP_GetVersion(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct PNP_GetVersion r;
+ uint16_t version = 0;
+
+ r.out.version = &version;
+
+ status = dcerpc_PNP_GetVersion(p, 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 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(p, 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(p, 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 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(p, 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(p, 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(p, 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(p, 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)
+{
+ 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 = &reg_data_type;
+ r.in.buffer_size = &buffer_size;
+ r.in.needed = &needed;
+ r.out.buffer = buffer;
+ r.out.reg_data_type = &reg_data_type;
+ r.out.buffer_size = &buffer_size;
+ r.out.needed = &needed;
+
+ status = dcerpc_PNP_GetDeviceRegProp(p, 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(p, 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");
+ struct torture_test *test;
+
+ tcase = torture_suite_add_rpc_iface_tcase(suite, "ntsvcs",
+ &ndr_table_ntsvcs);
+
+ test = torture_rpc_tcase_add_test(tcase, "PNP_GetDeviceRegProp",
+ test_PNP_GetDeviceRegProp);
+ test = torture_rpc_tcase_add_test(tcase, "PNP_GetDeviceList",
+ test_PNP_GetDeviceList);
+ test = torture_rpc_tcase_add_test(tcase, "PNP_GetDeviceListSize",
+ test_PNP_GetDeviceListSize);
+ test = 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 0000000000..69905169af
--- /dev/null
+++ b/source4/torture/rpc/object_uuid.c
@@ -0,0 +1,89 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/ndr_dssetup.h"
+#include "librpc/gen_ndr/ndr_dssetup_c.h"
+#include "librpc/gen_ndr/ndr_lsa.h"
+#include "librpc/gen_ndr/ndr_lsa_c.h"
+#include "torture/rpc/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 rpc_request *req;
+ 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;
+ req = dcerpc_ndr_request_send(p1, &uuid,
+ &ndr_table_dssetup,
+ NDR_DSSETUP_DSROLEGETPRIMARYDOMAININFORMATION,
+ torture, &r1);
+ status = dcerpc_ndr_request_recv(req);
+ 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;
+
+ req = dcerpc_ndr_request_send(p2, &uuid,
+ &ndr_table_lsarpc,
+ NDR_LSA_GETUSERNAME,
+ torture, &r2);
+ status = dcerpc_ndr_request_recv(req);
+ 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/oxidresolve.c b/source4/torture/rpc/oxidresolve.c
new file mode 100644
index 0000000000..8e334f4539
--- /dev/null
+++ b/source4/torture/rpc/oxidresolve.c
@@ -0,0 +1,237 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for oxidresolve operations
+
+ Copyright (C) Jelmer Vernooij 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/ndr_oxidresolver_c.h"
+#include "librpc/gen_ndr/ndr_remact_c.h"
+#include "librpc/gen_ndr/epmapper.h"
+#include "torture/rpc/rpc.h"
+
+#define CLSID_IMAGEDOC "02B01C80-E03D-101A-B294-00DD010F2BF9"
+
+const struct GUID IUnknown_uuid = {
+ 0x00000000,0x0000,0x0000,{0xc0,0x00},{0x00,0x00,0x00,0x00,0x00,0x46}
+};
+
+static bool test_RemoteActivation(struct torture_context *tctx,
+ uint64_t *oxid, struct GUID *oid)
+{
+ struct RemoteActivation r;
+ NTSTATUS status;
+ struct GUID iids[2];
+ uint16_t protseq[3] = { EPM_PROTOCOL_TCP, EPM_PROTOCOL_NCALRPC, EPM_PROTOCOL_UUID };
+ struct dcerpc_pipe *p;
+
+ status = torture_rpc_connection(tctx, &p,
+ &ndr_table_IRemoteActivation);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ ZERO_STRUCT(r.in);
+ r.in.this_object.version.MajorVersion = 5;
+ r.in.this_object.version.MinorVersion = 1;
+ r.in.this_object.cid = GUID_random();
+ GUID_from_string(CLSID_IMAGEDOC, &r.in.Clsid);
+ r.in.ClientImpLevel = RPC_C_IMP_LEVEL_IDENTIFY;
+ r.in.num_protseqs = 3;
+ r.in.protseq = protseq;
+ r.in.Interfaces = 1;
+ iids[0] = IUnknown_uuid;
+ r.in.pIIDs = iids;
+ r.out.pOxid = oxid;
+ r.out.ipidRemUnknown = oid;
+
+ status = dcerpc_RemoteActivation(p, tctx, &r);
+ if(NT_STATUS_IS_ERR(status)) {
+ fprintf(stderr, "RemoteActivation: %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if(!W_ERROR_IS_OK(r.out.result)) {
+ fprintf(stderr, "RemoteActivation: %s\n", win_errstr(r.out.result));
+ return false;
+ }
+
+ if(!W_ERROR_IS_OK(*r.out.hr)) {
+ fprintf(stderr, "RemoteActivation: %s\n", win_errstr(*r.out.hr));
+ return false;
+ }
+
+ if(!W_ERROR_IS_OK(r.out.results[0])) {
+ fprintf(stderr, "RemoteActivation: %s\n", win_errstr(r.out.results[0]));
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_SimplePing(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct SimplePing r;
+ NTSTATUS status;
+ uint64_t setid;
+
+ r.in.SetId = &setid;
+
+ status = dcerpc_SimplePing(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "SimplePing");
+ torture_assert_werr_ok(tctx, r.out.result, "SimplePing");
+
+ return true;
+}
+
+static bool test_ComplexPing(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct ComplexPing r;
+ NTSTATUS status;
+ uint64_t setid;
+ struct GUID oid;
+ uint64_t oxid;
+
+ if (!test_RemoteActivation(tctx, &oxid, &oid))
+ return false;
+
+ setid = 0;
+ ZERO_STRUCT(r.in);
+
+ r.in.SequenceNum = 0;
+ r.in.SetId = &setid;
+ r.in.cAddToSet = 1;
+ r.in.AddToSet = &oid;
+
+ status = dcerpc_ComplexPing(p, tctx, &r);
+ if(NT_STATUS_IS_ERR(status)) {
+ fprintf(stderr, "ComplexPing: %s\n", nt_errstr(status));
+ return 0;
+ }
+
+ if(!W_ERROR_IS_OK(r.out.result)) {
+ fprintf(stderr, "ComplexPing: %s\n", win_errstr(r.out.result));
+ return 0;
+ }
+
+
+
+ return 1;
+}
+
+static bool test_ServerAlive(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct ServerAlive r;
+ NTSTATUS status;
+
+ status = dcerpc_ServerAlive(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "ServerAlive");
+ torture_assert_werr_ok(tctx, r.out.result, "ServerAlive");
+
+ return true;
+}
+
+static bool test_ResolveOxid(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct ResolveOxid r;
+ NTSTATUS status;
+ uint16_t protseq[2] = { EPM_PROTOCOL_TCP, EPM_PROTOCOL_SMB };
+ uint64_t oxid;
+ struct GUID oid;
+
+ if (!test_RemoteActivation(tctx, &oxid, &oid))
+ return false;
+
+ r.in.pOxid = oxid;
+ r.in.cRequestedProtseqs = 2;
+ r.in.arRequestedProtseqs = protseq;
+
+ status = dcerpc_ResolveOxid(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "ResolveOxid");
+ torture_assert_werr_ok(tctx, r.out.result, "ResolveOxid");
+
+ return true;
+}
+
+static bool test_ResolveOxid2(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct ResolveOxid2 r;
+ NTSTATUS status;
+ uint16_t protseq[2] = { EPM_PROTOCOL_TCP, EPM_PROTOCOL_SMB };
+ uint64_t oxid;
+ struct GUID oid;
+
+ if (!test_RemoteActivation(tctx, &oxid, &oid))
+ return false;
+
+ r.in.pOxid = oxid;
+ r.in.cRequestedProtseqs = 2;
+ r.in.arRequestedProtseqs = protseq;
+
+ status = dcerpc_ResolveOxid2(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "ResolveOxid2");
+
+ torture_assert_werr_ok(tctx, r.out.result, "ResolveOxid2");
+
+ torture_comment(tctx, "Remote server versions: %d, %d\n", r.out.ComVersion->MajorVersion, r.out.ComVersion->MinorVersion);
+
+ return true;
+}
+
+static bool test_ServerAlive2(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct ServerAlive2 r;
+ NTSTATUS status;
+
+ status = dcerpc_ServerAlive2(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "ServerAlive2");
+ torture_assert_werr_ok(tctx, r.out.result, "ServerAlive2");
+
+ return true;
+}
+
+struct torture_suite *torture_rpc_oxidresolve(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx,
+ "OXIDRESOLVE");
+ struct torture_rpc_tcase *tcase;
+
+ tcase = torture_suite_add_rpc_iface_tcase(suite, "oxidresolver",
+ &ndr_table_IOXIDResolver);
+
+ torture_rpc_tcase_add_test(tcase, "ServerAlive", test_ServerAlive);
+
+ torture_rpc_tcase_add_test(tcase, "ServerAlive2", test_ServerAlive2);
+
+ torture_rpc_tcase_add_test(tcase, "ComplexPing", test_ComplexPing);
+
+ torture_rpc_tcase_add_test(tcase, "SimplePing", test_SimplePing);
+
+ torture_rpc_tcase_add_test(tcase, "ResolveOxid", test_ResolveOxid);
+
+ torture_rpc_tcase_add_test(tcase, "ResolveOxid2", test_ResolveOxid2);
+
+ return suite;
+}
diff --git a/source4/torture/rpc/remact.c b/source4/torture/rpc/remact.c
new file mode 100644
index 0000000000..0c5ac09977
--- /dev/null
+++ b/source4/torture/rpc/remact.c
@@ -0,0 +1,89 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for remoteactivation operations
+
+ Copyright (C) Jelmer Vernooij 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/ndr_remact_c.h"
+#include "librpc/gen_ndr/ndr_epmapper_c.h"
+#include "torture/rpc/rpc.h"
+
+#define CLSID_IMAGEDOC "02B01C80-E03D-101A-B294-00DD010F2BF9"
+#define DCERPC_IUNKNOWN_UUID "00000000-0000-0000-c000-000000000046"
+#define DCERPC_ICLASSFACTORY_UUID "00000001-0000-0000-c000-000000000046"
+
+static bool test_RemoteActivation(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct RemoteActivation r;
+ NTSTATUS status;
+ struct GUID iids[1];
+ uint16_t protseq[3] = { EPM_PROTOCOL_TCP, EPM_PROTOCOL_NCALRPC, EPM_PROTOCOL_UUID };
+
+ ZERO_STRUCT(r.in);
+ r.in.this_object.version.MajorVersion = 5;
+ r.in.this_object.version.MinorVersion = 1;
+ r.in.this_object.cid = GUID_random();
+ GUID_from_string(CLSID_IMAGEDOC, &r.in.Clsid);
+ r.in.ClientImpLevel = RPC_C_IMP_LEVEL_IDENTIFY;
+ r.in.num_protseqs = 3;
+ r.in.protseq = protseq;
+ r.in.Interfaces = 1;
+ GUID_from_string(DCERPC_IUNKNOWN_UUID, &iids[0]);
+ r.in.pIIDs = iids;
+
+ status = dcerpc_RemoteActivation(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "RemoteActivation");
+
+ torture_assert_werr_ok(tctx, r.out.result, "RemoteActivation");
+
+ torture_assert_werr_ok(tctx, *r.out.hr, "RemoteActivation");
+
+ torture_assert_werr_ok(tctx, r.out.results[0], "RemoteActivation");
+
+ GUID_from_string(DCERPC_ICLASSFACTORY_UUID, &iids[0]);
+ r.in.Interfaces = 1;
+ r.in.Mode = MODE_GET_CLASS_OBJECT;
+
+ status = dcerpc_RemoteActivation(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "RemoteActivation(GetClassObject)");
+
+ torture_assert_werr_ok(tctx, r.out.result,
+ "RemoteActivation(GetClassObject)");
+
+ torture_assert_werr_ok(tctx, *r.out.hr, "RemoteActivation(GetClassObject)");
+
+ torture_assert_werr_ok(tctx, r.out.results[0],
+ "RemoteActivation(GetClassObject)");
+
+ return true;
+}
+
+struct torture_suite *torture_rpc_remact(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "REMACT");
+ struct torture_rpc_tcase *tcase;
+
+ tcase = torture_suite_add_rpc_iface_tcase(suite, "remact", &ndr_table_IRemoteActivation);
+
+ torture_rpc_tcase_add_test(tcase, "RemoteActivation", test_RemoteActivation);
+
+ return suite;
+}
diff --git a/source4/torture/rpc/remote_pac.c b/source4/torture/rpc/remote_pac.c
new file mode 100644
index 0000000000..53754e02af
--- /dev/null
+++ b/source4/torture/rpc/remote_pac.c
@@ -0,0 +1,337 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ test suite for netlogon PAC operations
+
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "lib/events/events.h"
+#include "auth/auth.h"
+#include "auth/gensec/gensec.h"
+#include "lib/cmdline/popt_common.h"
+#include "torture/rpc/rpc.h"
+#include "torture/rpc/netlogon.h"
+#include "libcli/auth/libcli_auth.h"
+#include "librpc/gen_ndr/ndr_netlogon_c.h"
+#include "librpc/gen_ndr/ndr_krb5pac.h"
+#include "param/param.h"
+#include "lib/messaging/irpc.h"
+#include "cluster/cluster.h"
+
+#define TEST_MACHINE_NAME "torturepactest"
+
+/* 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 verfication of our Kerberos stack */
+
+static bool test_PACVerify(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *credentials)
+{
+ NTSTATUS status;
+
+ struct netr_LogonSamLogon r;
+
+ union netr_LogonLevel logon;
+ union netr_Validation validation;
+ uint8_t authoritative;
+ struct netr_Authenticator return_authenticator;
+
+ struct netr_GenericInfo generic;
+ struct netr_Authenticator auth, auth2;
+
+
+ struct creds_CredentialState *creds;
+ struct gensec_security *gensec_client_context;
+ struct gensec_security *gensec_server_context;
+
+ DATA_BLOB client_to_server, server_to_client, pac_wrapped, payload;
+ struct PAC_Validate pac_wrapped_struct;
+
+ enum ndr_err_code ndr_err;
+
+ struct auth_session_info *session_info;
+
+ char *tmp_dir;
+
+ TALLOC_CTX *tmp_ctx = talloc_new(tctx);
+
+ torture_assert(tctx, tmp_ctx != NULL, "talloc_new() failed");
+
+ if (!test_SetupCredentials2(p, tctx, NETLOGON_NEG_AUTH2_ADS_FLAGS,
+ credentials, SEC_CHAN_BDC,
+ &creds)) {
+ return false;
+ }
+
+ status = torture_temp_dir(tctx, "PACVerify", &tmp_dir);
+ torture_assert_ntstatus_ok(tctx, status, "torture_temp_dir failed");
+
+ status = gensec_client_start(tctx, &gensec_client_context, tctx->ev,
+ lp_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, cmdline_credentials);
+ 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, tctx->ev,
+ lp_gensec_settings(tctx, tctx->lp_ctx),
+ NULL, &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_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, &session_info);
+ torture_assert_ntstatus_ok(tctx, status, "gensec_session_info failed");
+
+ pac_wrapped_struct.ChecksumLength = session_info->server_info->pac_srv_sig.signature.length;
+ pac_wrapped_struct.SignatureType = session_info->server_info->pac_kdc_sig.type;
+ pac_wrapped_struct.SignatureLength = session_info->server_info->pac_kdc_sig.signature.length;
+ pac_wrapped_struct.ChecksumAndSignature = payload
+ = data_blob_talloc(tmp_ctx, NULL,
+ pac_wrapped_struct.ChecksumLength
+ + pac_wrapped_struct.SignatureLength);
+ memcpy(&payload.data[0],
+ session_info->server_info->pac_srv_sig.signature.data,
+ pac_wrapped_struct.ChecksumLength);
+ memcpy(&payload.data[pac_wrapped_struct.ChecksumLength],
+ session_info->server_info->pac_kdc_sig.signature.data,
+ pac_wrapped_struct.SignatureLength);
+
+ ndr_err = ndr_push_struct_blob(&pac_wrapped, tmp_ctx, lp_iconv_convenience(tctx->lp_ctx), &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");
+ 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_high = 0;
+ generic.identity_info.logon_id_low = 0;
+ generic.identity_info.domain_name.string = session_info->server_info->domain_name;
+ generic.identity_info.account_name.string = session_info->server_info->account_name;
+ generic.identity_info.workstation.string = TEST_MACHINE_NAME;
+
+ generic.package_name.string = "Kerberos";
+
+ logon.generic = &generic;
+
+ ZERO_STRUCT(auth2);
+ 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(credentials);
+ r.in.validation_level = NetlogonValidationGenericInfo2;
+ r.out.validation = &validation;
+ r.out.authoritative = &authoritative;
+ r.out.return_authenticator = &return_authenticator;
+
+ status = dcerpc_netr_LogonSamLogon(p, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "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);
+ 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(credentials);
+ r.in.validation_level = NetlogonValidationGenericInfo2;
+
+ status = dcerpc_netr_LogonSamLogon(p, tctx, &r);
+
+ torture_assert_ntstatus_equal(tctx, status, NT_STATUS_LOGON_FAILURE, "LogonSamLogon failed");
+
+ torture_assert(tctx, 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);
+ 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(credentials);
+ r.in.validation_level = NetlogonValidationGenericInfo2;
+
+ status = dcerpc_netr_LogonSamLogon(p, tctx, &r);
+
+ torture_assert_ntstatus_equal(tctx, status, NT_STATUS_INVALID_PARAMETER, "LogonSamLogon failed");
+
+ torture_assert(tctx, creds_client_check(creds, &r.out.return_authenticator->cred),
+ "Credential chaining failed");
+
+ pac_wrapped_struct.ChecksumLength = session_info->server_info->pac_srv_sig.signature.length;
+ pac_wrapped_struct.SignatureType = session_info->server_info->pac_kdc_sig.type;
+
+ /* Break the SignatureType */
+ pac_wrapped_struct.SignatureType++;
+
+ pac_wrapped_struct.SignatureLength = session_info->server_info->pac_kdc_sig.signature.length;
+ pac_wrapped_struct.ChecksumAndSignature = payload
+ = data_blob_talloc(tmp_ctx, NULL,
+ pac_wrapped_struct.ChecksumLength
+ + pac_wrapped_struct.SignatureLength);
+ memcpy(&payload.data[0],
+ session_info->server_info->pac_srv_sig.signature.data,
+ pac_wrapped_struct.ChecksumLength);
+ memcpy(&payload.data[pac_wrapped_struct.ChecksumLength],
+ session_info->server_info->pac_kdc_sig.signature.data,
+ pac_wrapped_struct.SignatureLength);
+
+ ndr_err = ndr_push_struct_blob(&pac_wrapped, tmp_ctx, lp_iconv_convenience(tctx->lp_ctx), &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");
+ 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);
+ 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(credentials);
+ r.in.validation_level = NetlogonValidationGenericInfo2;
+
+ status = dcerpc_netr_LogonSamLogon(p, tctx, &r);
+
+ torture_assert_ntstatus_equal(tctx, status, NT_STATUS_LOGON_FAILURE, "LogonSamLogon failed");
+
+ torture_assert(tctx, creds_client_check(creds, &r.out.return_authenticator->cred),
+ "Credential chaining failed");
+
+ pac_wrapped_struct.ChecksumLength = session_info->server_info->pac_srv_sig.signature.length;
+ pac_wrapped_struct.SignatureType = session_info->server_info->pac_kdc_sig.type;
+ pac_wrapped_struct.SignatureLength = session_info->server_info->pac_kdc_sig.signature.length;
+
+ pac_wrapped_struct.ChecksumAndSignature = payload
+ = data_blob_talloc(tmp_ctx, NULL,
+ pac_wrapped_struct.ChecksumLength
+ + pac_wrapped_struct.SignatureLength);
+ memcpy(&payload.data[0],
+ session_info->server_info->pac_srv_sig.signature.data,
+ pac_wrapped_struct.ChecksumLength);
+ memcpy(&payload.data[pac_wrapped_struct.ChecksumLength],
+ session_info->server_info->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, tmp_ctx, lp_iconv_convenience(tctx->lp_ctx), &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");
+ 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);
+ 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(credentials);
+ r.in.validation_level = NetlogonValidationGenericInfo2;
+
+ status = dcerpc_netr_LogonSamLogon(p, tctx, &r);
+
+ torture_assert_ntstatus_equal(tctx, status, NT_STATUS_INVALID_PARAMETER, "LogonSamLogon failed");
+
+ torture_assert(tctx, creds_client_check(creds, &r.out.return_authenticator->cred),
+ "Credential chaining failed");
+ return true;
+}
+
+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_rpc_iface_tcase(suite, "netlogon",
+ &ndr_table_netlogon, TEST_MACHINE_NAME);
+ torture_rpc_tcase_add_test_creds(tcase, "verify", test_PACVerify);
+
+ return suite;
+}
diff --git a/source4/torture/rpc/rpc.c b/source4/torture/rpc/rpc.c
new file mode 100644
index 0000000000..19b223beba
--- /dev/null
+++ b/source4/torture/rpc/rpc.c
@@ -0,0 +1,454 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "auth/credentials/credentials.h"
+#include "lib/cmdline/popt_common.h"
+#include "librpc/rpc/dcerpc.h"
+#include "torture/rpc/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)) {
+ DEBUG(0,("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;
+
+ dcerpc_init(tctx->lp_ctx);
+
+ status = torture_rpc_binding(tctx, &binding);
+ if (NT_STATUS_IS_ERR(status))
+ return status;
+
+ status = dcerpc_pipe_connect_b(tctx,
+ p, binding, table,
+ cmdline_credentials, tctx->ev, tctx->lp_ctx);
+
+ if (NT_STATUS_IS_ERR(status)) {
+ printf("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)
+{
+ NTSTATUS status;
+ struct dcerpc_binding *binding;
+
+ status = torture_rpc_binding(tctx, &binding);
+ if (NT_STATUS_IS_ERR(status))
+ return status;
+
+ binding->transport = transport;
+ binding->assoc_group_id = assoc_group_id;
+
+ status = dcerpc_pipe_connect_b(tctx, p, binding, table,
+ cmdline_credentials, tctx->ev, tctx->lp_ctx);
+
+ if (NT_STATUS_IS_ERR(status)) {
+ *p = NULL;
+ }
+
+ return status;
+}
+
+static bool torture_rpc_setup_machine(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 = cmdline_credentials;
+ 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 true;
+}
+
+_PUBLIC_ struct torture_rpc_tcase *torture_suite_add_machine_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;
+ 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 true;
+}
+
+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 = cmdline_credentials;
+
+ status = torture_rpc_connection(tctx,
+ &(tcase_data->pipe),
+ tcase->table);
+
+ torture_assert_ntstatus_ok(tctx, status, "Error connecting to server");
+
+ return true;
+}
+
+
+
+_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);
+}
+
+_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(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(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(tcase->tcase.tests, test);
+
+ return test;
+}
+
+NTSTATUS torture_rpc_init(void)
+{
+ struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "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_echo(suite));
+ torture_suite_add_simple_test(suite, "DFS", torture_rpc_dfs);
+ 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_simple_test(suite, "SPOOLSS", torture_rpc_spoolss);
+ torture_suite_add_suite(suite, torture_rpc_spoolss_notify(suite));
+ torture_suite_add_suite(suite, torture_rpc_spoolss_win(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", torture_rpc_samr_passwords);
+ torture_suite_add_suite(suite, torture_rpc_netlogon(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_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_passwords_pwdlastset(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_epmapper(suite));
+ torture_suite_add_suite(suite, torture_rpc_initshutdown(suite));
+ torture_suite_add_suite(suite, torture_rpc_oxidresolve(suite));
+ torture_suite_add_suite(suite, torture_rpc_remact(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, "AUTOIDL", torture_rpc_autoidl);
+ torture_suite_add_simple_test(suite, "COUNTCALLS", torture_rpc_countcalls);
+ torture_suite_add_simple_test(suite, "MULTIBIND", torture_multi_bind);
+ torture_suite_add_simple_test(suite, "AUTHCONTEXT", torture_bind_authcontext);
+ torture_suite_add_simple_test(suite, "BINDSAMBA3", torture_bind_samba3);
+ torture_suite_add_simple_test(suite, "NETLOGSAMBA3", torture_netlogon_samba3);
+ torture_suite_add_simple_test(suite, "SAMBA3SESSIONKEY", torture_samba3_sessionkey);
+ torture_suite_add_simple_test(suite, "SAMBA3-SRVSVC", torture_samba3_rpc_srvsvc);
+ torture_suite_add_simple_test(suite, "SAMBA3-SHARESEC",
+ torture_samba3_rpc_sharesec);
+ torture_suite_add_simple_test(suite, "SAMBA3-GETUSERNAME",
+ torture_samba3_rpc_getusername);
+ torture_suite_add_simple_test(suite, "SAMBA3-RANDOMAUTH2",
+ torture_samba3_rpc_randomauth2);
+ torture_suite_add_simple_test(suite, "SAMBA3-LSA", torture_samba3_rpc_lsa);
+ torture_suite_add_simple_test(suite, "SAMBA3-SPOOLSS", torture_samba3_rpc_spoolss);
+ torture_suite_add_simple_test(suite, "SAMBA3-WKSSVC", torture_samba3_rpc_wkssvc);
+ torture_suite_add_simple_test(suite, "SAMBA3-WINREG", torture_samba3_rpc_winreg);
+ torture_suite_add_simple_test(suite, "DRSUAPI", torture_rpc_drsuapi);
+ torture_suite_add_simple_test(suite, "CRACKNAMES", torture_rpc_drsuapi_cracknames);
+ torture_suite_add_suite(suite, torture_rpc_dssetup(suite));
+ torture_suite_add_suite(suite, torture_rpc_browser(suite));
+ torture_suite_add_simple_test(suite, "SAMBA3-REGCONFIG", torture_samba3_regconfig);
+ torture_suite_add_simple_test(suite, "ALTERCONTEXT", torture_rpc_alter_context);
+ torture_suite_add_simple_test(suite, "JOIN", torture_rpc_join);
+ torture_suite_add_simple_test(suite, "DSSYNC", torture_rpc_dssync);
+ 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));
+
+ suite->description = talloc_strdup(suite, "DCE/RPC protocol and interface tests");
+
+ torture_register_suite(suite);
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/torture/rpc/rpc.h b/source4/torture/rpc/rpc.h
new file mode 100644
index 0000000000..9fd64f18b5
--- /dev/null
+++ b/source4/torture/rpc/rpc.h
@@ -0,0 +1,88 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 "torture/rpc/proto.h"
+
+struct torture_rpc_tcase {
+ struct torture_tcase tcase;
+ const struct ndr_interface_table *table;
+ const char *machine_name;
+};
+
+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);
+
+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_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_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_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/samba3rpc.c b/source4/torture/rpc/samba3rpc.c
new file mode 100644
index 0000000000..fe128fea52
--- /dev/null
+++ b/source4/torture/rpc/samba3rpc.c
@@ -0,0 +1,3542 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/rap/rap.h"
+#include "torture/torture.h"
+#include "torture/util.h"
+#include "torture/rap/proto.h"
+#include "librpc/gen_ndr/ndr_lsa.h"
+#include "librpc/gen_ndr/ndr_lsa_c.h"
+#include "librpc/gen_ndr/ndr_samr.h"
+#include "librpc/gen_ndr/ndr_samr_c.h"
+#include "librpc/gen_ndr/ndr_netlogon.h"
+#include "librpc/gen_ndr/ndr_netlogon_c.h"
+#include "librpc/gen_ndr/ndr_srvsvc.h"
+#include "librpc/gen_ndr/ndr_srvsvc_c.h"
+#include "librpc/gen_ndr/ndr_spoolss.h"
+#include "librpc/gen_ndr/ndr_spoolss_c.h"
+#include "librpc/gen_ndr/ndr_winreg.h"
+#include "librpc/gen_ndr/ndr_winreg_c.h"
+#include "librpc/gen_ndr/ndr_wkssvc.h"
+#include "librpc/gen_ndr/ndr_wkssvc_c.h"
+#include "lib/cmdline/popt_common.h"
+#include "librpc/rpc/dcerpc.h"
+#include "torture/rpc/rpc.h"
+#include "libcli/libcli.h"
+#include "libcli/composite/composite.h"
+#include "libcli/smb_composite/smb_composite.h"
+#include "libcli/auth/libcli_auth.h"
+#include "../lib/crypto/crypto.h"
+#include "auth/ntlmssp/ntlmssp.h"
+#include "libcli/security/security.h"
+#include "param/param.h"
+#include "lib/registry/registry.h"
+#include "libcli/resolve/resolve.h"
+
+/*
+ * 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;
+ struct smbcli_session *session2;
+ struct smbcli_state *cli;
+ struct dcerpc_pipe *lsa_pipe;
+ 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) {
+ d_printf("talloc_init failed\n");
+ return false;
+ }
+
+ lp_smbcli_options(torture->lp_ctx, &options);
+ lp_smbcli_session_options(torture->lp_ctx, &session_options);
+
+ status = smbcli_full_connection(mem_ctx, &cli,
+ torture_setting_string(torture, "host", NULL),
+ lp_smb_ports(torture->lp_ctx),
+ "IPC$", NULL,
+ lp_socket_options(torture->lp_ctx),
+ cmdline_credentials,
+ lp_resolve_context(torture->lp_ctx),
+ torture->ev, &options, &session_options,
+ lp_iconv_convenience(torture->lp_ctx),
+ lp_gensec_settings(torture, torture->lp_ctx));
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("smbcli_full_connection failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ lsa_pipe = dcerpc_pipe_init(mem_ctx, cli->transport->socket->event.ctx,
+ lp_iconv_convenience(torture->lp_ctx));
+ if (lsa_pipe == NULL) {
+ d_printf("dcerpc_pipe_init failed\n");
+ goto done;
+ }
+
+ status = dcerpc_pipe_open_smb(lsa_pipe, cli->tree, "\\lsarpc");
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("dcerpc_pipe_open_smb failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ status = dcerpc_bind_auth_none(lsa_pipe, &ndr_table_lsarpc);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("dcerpc_bind_auth_none failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ 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(lsa_pipe, mem_ctx, &openpolicy);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("dcerpc_lsa_OpenPolicy2 failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ close_handle.in.handle = &handle;
+ close_handle.out.handle = &handle;
+
+ status = dcerpc_lsa_Close(lsa_pipe, mem_ctx, &close_handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("dcerpc_lsa_Close failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ session2 = smbcli_session_init(cli->transport, mem_ctx, false, session_options);
+ if (session2 == NULL) {
+ d_printf("smbcli_session_init failed\n");
+ goto done;
+ }
+
+ if (!(anon_creds = cli_credentials_init_anon(mem_ctx))) {
+ d_printf("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 = lp_gensec_settings(torture, torture->lp_ctx);
+
+ status = smb_composite_sesssetup(session2, &setup);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("anon session setup failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+ session2->vuid = setup.out.vuid;
+
+ tmp = cli->tree->session;
+ cli->tree->session = session2;
+
+ status = dcerpc_lsa_OpenPolicy2(lsa_pipe, mem_ctx, &openpolicy);
+
+ cli->tree->session = tmp;
+ talloc_free(lsa_pipe);
+ lsa_pipe = NULL;
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_HANDLE)) {
+ d_printf("dcerpc_lsa_OpenPolicy2 with wrong vuid gave %s, "
+ "expected NT_STATUS_INVALID_HANDLE\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ ret = true;
+ done:
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+/*
+ * Bind to lsa using a specific auth method
+ */
+
+static bool bindtest(struct smbcli_state *cli,
+ struct cli_credentials *credentials,
+ struct loadparm_context *lp_ctx,
+ uint8_t auth_type, uint8_t auth_level)
+{
+ TALLOC_CTX *mem_ctx;
+ bool ret = false;
+ NTSTATUS status;
+
+ struct dcerpc_pipe *lsa_pipe;
+ 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) {
+ d_printf("talloc_init failed\n");
+ return false;
+ }
+
+ lsa_pipe = dcerpc_pipe_init(mem_ctx,
+ cli->transport->socket->event.ctx,
+ lp_iconv_convenience(lp_ctx));
+ if (lsa_pipe == NULL) {
+ d_printf("dcerpc_pipe_init failed\n");
+ goto done;
+ }
+
+ status = dcerpc_pipe_open_smb(lsa_pipe, cli->tree, "\\lsarpc");
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("dcerpc_pipe_open_smb failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ status = dcerpc_bind_auth(lsa_pipe, &ndr_table_lsarpc,
+ credentials, lp_gensec_settings(lp_ctx, lp_ctx), auth_type, auth_level,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("dcerpc_bind_auth failed: %s\n", nt_errstr(status));
+ goto done;
+ }
+
+ 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(lsa_pipe, mem_ctx, &openpolicy);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("dcerpc_lsa_OpenPolicy2 failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ query.in.handle = &handle;
+ query.in.level = LSA_POLICY_INFO_DOMAIN;
+ query.out.info = &info;
+
+ status = dcerpc_lsa_QueryInfoPolicy(lsa_pipe, mem_ctx, &query);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("dcerpc_lsa_QueryInfoPolicy failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ close_handle.in.handle = &handle;
+ close_handle.out.handle = &handle;
+
+ status = dcerpc_lsa_Close(lsa_pipe, mem_ctx, &close_handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("dcerpc_lsa_Close failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ ret = true;
+ done:
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+/*
+ * test authenticated RPC binds with the variants Samba3 does support
+ */
+
+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) {
+ d_printf("talloc_init failed\n");
+ return false;
+ }
+
+ lp_smbcli_options(torture->lp_ctx, &options);
+ lp_smbcli_session_options(torture->lp_ctx, &session_options);
+
+ status = smbcli_full_connection(mem_ctx, &cli,
+ torture_setting_string(torture, "host", NULL),
+ lp_smb_ports(torture->lp_ctx),
+ "IPC$", NULL,
+ lp_socket_options(torture->lp_ctx),
+ cmdline_credentials,
+ lp_resolve_context(torture->lp_ctx),
+ torture->ev, &options, &session_options,
+ lp_iconv_convenience(torture->lp_ctx),
+ lp_gensec_settings(torture, torture->lp_ctx));
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("smbcli_full_connection failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ ret = true;
+
+ ret &= bindtest(cli, cmdline_credentials, torture->lp_ctx, DCERPC_AUTH_TYPE_NTLMSSP,
+ DCERPC_AUTH_LEVEL_INTEGRITY);
+ ret &= bindtest(cli, cmdline_credentials, torture->lp_ctx, DCERPC_AUTH_TYPE_NTLMSSP,
+ DCERPC_AUTH_LEVEL_PRIVACY);
+ ret &= bindtest(cli, cmdline_credentials, torture->lp_ctx, DCERPC_AUTH_TYPE_SPNEGO,
+ DCERPC_AUTH_LEVEL_INTEGRITY);
+ ret &= bindtest(cli, cmdline_credentials, torture->lp_ctx, 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 NTSTATUS get_usr_handle(struct smbcli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_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;
+ 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;
+
+ samr_pipe = dcerpc_pipe_init(mem_ctx,
+ cli->transport->socket->event.ctx,
+ lp_iconv_convenience(lp_ctx));
+ if (samr_pipe == NULL) {
+ d_printf("dcerpc_pipe_init failed\n");
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ status = dcerpc_pipe_open_smb(samr_pipe, cli->tree, "\\samr");
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("dcerpc_pipe_open_smb failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ if (admin_creds != NULL) {
+ status = dcerpc_bind_auth(samr_pipe, &ndr_table_samr,
+ admin_creds, lp_gensec_settings(lp_ctx, lp_ctx), auth_type, auth_level,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("dcerpc_bind_auth failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+ } else {
+ /* We must have an authenticated SMB connection */
+ status = dcerpc_bind_auth_none(samr_pipe, &ndr_table_samr);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("dcerpc_bind_auth_none failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+ }
+
+ 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;
+
+ status = dcerpc_samr_Connect2(samr_pipe, mem_ctx, &conn);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("samr_Connect2 failed: %s\n", nt_errstr(status));
+ goto fail;
+ }
+
+ 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;
+
+ status = dcerpc_samr_EnumDomains(samr_pipe, mem_ctx, &enumdom);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("samr_EnumDomains failed: %s\n", nt_errstr(status));
+ goto fail;
+ }
+
+ if (*enumdom.out.num_entries != 2) {
+ d_printf("samr_EnumDomains returned %d entries, expected 2\n",
+ *enumdom.out.num_entries);
+ status = NT_STATUS_UNSUCCESSFUL;
+ goto fail;
+ }
+
+ 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;
+
+ status = dcerpc_samr_LookupDomain(samr_pipe, mem_ctx, &l);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("samr_LookupDomain failed: %s\n", nt_errstr(status));
+ goto fail;
+ }
+
+ 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;
+
+ status = dcerpc_samr_OpenDomain(samr_pipe, mem_ctx, &o);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("samr_OpenDomain failed: %s\n", nt_errstr(status));
+ goto fail;
+ }
+
+ 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;
+
+ status = dcerpc_samr_CreateUser2(samr_pipe, mem_ctx, &c);
+
+ if (NT_STATUS_EQUAL(status, 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;
+
+ status = dcerpc_samr_LookupNames(samr_pipe, mem_ctx, &ln);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("samr_LookupNames failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ 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;
+
+ status = dcerpc_samr_OpenUser(samr_pipe, mem_ctx, &ou);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("samr_OpenUser failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("samr_CreateUser failed: %s\n", nt_errstr(status));
+ goto fail;
+ }
+
+ *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 NT_STATUS_OK;
+
+ fail:
+ return status;
+}
+
+/*
+ * Create a test user
+ */
+
+static bool create_user(TALLOC_CTX *mem_ctx, struct smbcli_state *cli,
+ struct loadparm_context *lp_ctx,
+ 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 policy_handle *wks_handle;
+ bool ret = false;
+
+ if (!(tmp_ctx = talloc_new(mem_ctx))) {
+ d_printf("talloc_init failed\n");
+ return false;
+ }
+
+ status = get_usr_handle(cli, tmp_ctx, lp_ctx, admin_creds,
+ DCERPC_AUTH_TYPE_NTLMSSP,
+ DCERPC_AUTH_LEVEL_INTEGRITY,
+ username, domain_name, &samr_pipe, &wks_handle,
+ user_sid);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("get_usr_handle failed: %s\n", nt_errstr(status));
+ goto done;
+ }
+
+ {
+ 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)) {
+ d_printf("dcerpc_fetch_session_key failed\n");
+ goto done;
+ }
+ arcfour_crypt_blob(u_info.info23.password.data, 516,
+ &session_key);
+ 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(samr_pipe, tmp_ctx, &sui2);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("samr_SetUserInfo(23) failed: %s\n",
+ nt_errstr(status));
+ 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(samr_pipe, tmp_ctx, &sui);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("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(samr_pipe, tmp_ctx, &qui);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("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(samr_pipe, tmp_ctx, &sui);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("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 smbcli_state *cli,
+ struct loadparm_context *lp_ctx,
+ struct cli_credentials *admin_creds,
+ const char *username)
+{
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+ char *dom_name;
+ struct dcerpc_pipe *samr_pipe;
+ struct policy_handle *user_handle;
+ bool ret = false;
+
+ if ((mem_ctx = talloc_init("leave")) == NULL) {
+ d_printf("talloc_init failed\n");
+ return false;
+ }
+
+ status = get_usr_handle(cli, mem_ctx, lp_ctx, admin_creds,
+ DCERPC_AUTH_TYPE_NTLMSSP,
+ DCERPC_AUTH_LEVEL_INTEGRITY,
+ username, &dom_name, &samr_pipe,
+ &user_handle, NULL);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("get_wks_handle failed: %s\n", nt_errstr(status));
+ goto done;
+ }
+
+ {
+ struct samr_DeleteUser d;
+
+ d.in.user_handle = user_handle;
+ d.out.user_handle = user_handle;
+
+ status = dcerpc_samr_DeleteUser(samr_pipe, mem_ctx, &d);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("samr_DeleteUser failed %s\n", nt_errstr(status));
+ goto done;
+ }
+ }
+
+ ret = true;
+
+ done:
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+/*
+ * Do a Samba3-style join
+ */
+
+static bool join3(struct smbcli_state *cli,
+ struct loadparm_context *lp_ctx,
+ 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 policy_handle *wks_handle;
+ bool ret = false;
+ NTTIME last_password_change;
+
+ if ((mem_ctx = talloc_init("join3")) == NULL) {
+ d_printf("talloc_init failed\n");
+ return false;
+ }
+
+ status = get_usr_handle(
+ cli, mem_ctx, lp_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 (!NT_STATUS_IS_OK(status)) {
+ d_printf("get_wks_handle failed: %s\n", nt_errstr(status));
+ 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(samr_pipe, mem_ctx, &q);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("(%s) QueryUserInfo failed: %s\n",
+ __location__, nt_errstr(status));
+ 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;
+ DATA_BLOB confounded_session_key = data_blob_talloc(
+ mem_ctx, NULL, 16);
+ struct MD5Context ctx;
+ uint8_t confounder[16];
+
+ 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;
+ */
+
+ encode_pw_buffer(u_info.info25.password.data,
+ cli_credentials_get_password(wks_creds),
+ STR_UNICODE);
+ status = dcerpc_fetch_session_key(samr_pipe, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("dcerpc_fetch_session_key failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+ generate_random_buffer((uint8_t *)confounder, 16);
+
+ MD5Init(&ctx);
+ MD5Update(&ctx, confounder, 16);
+ MD5Update(&ctx, session_key.data, session_key.length);
+ MD5Final(confounded_session_key.data, &ctx);
+
+ arcfour_crypt_blob(u_info.info25.password.data, 516,
+ &confounded_session_key);
+ memcpy(&u_info.info25.password.data[516], confounder, 16);
+
+ sui2.in.user_handle = wks_handle;
+ sui2.in.level = 25;
+ sui2.in.info = &u_info;
+
+ status = dcerpc_samr_SetUserInfo2(samr_pipe, mem_ctx, &sui2);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("samr_SetUserInfo2(25) failed: %s\n",
+ nt_errstr(status));
+ 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)) {
+ d_printf("dcerpc_fetch_session_key failed\n");
+ goto done;
+ }
+ arcfour_crypt_blob(u_info.info24.password.data, 516,
+ &session_key);
+ sui2.in.user_handle = wks_handle;
+ sui2.in.info = &u_info;
+ sui2.in.level = 24;
+
+ status = dcerpc_samr_SetUserInfo2(samr_pipe, mem_ctx, &sui2);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("samr_SetUserInfo(24) failed: %s\n",
+ nt_errstr(status));
+ 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(samr_pipe, mem_ctx, &sui);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("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(samr_pipe, mem_ctx, &q);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("(%s) QueryUserInfo failed: %s\n",
+ __location__, nt_errstr(status));
+ goto done;
+ }
+
+ if (use_level25) {
+ if (last_password_change
+ == info->info21.last_password_change) {
+ d_printf("(%s) last_password_change unchanged "
+ "during join, level25 must change "
+ "it\n", __location__);
+ goto done;
+ }
+ }
+ else {
+ if (last_password_change
+ != info->info21.last_password_change) {
+ d_printf("(%s) last_password_change changed "
+ "during join, level24 doesn't "
+ "change it\n", __location__);
+ goto done;
+ }
+ }
+ }
+
+ ret = true;
+
+ done:
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+/*
+ * Do a ReqChallenge/Auth2 and get the wks creds
+ */
+
+static bool auth2(struct smbcli_state *cli,
+ struct loadparm_context *lp_ctx,
+ struct cli_credentials *wks_cred)
+{
+ TALLOC_CTX *mem_ctx;
+ struct dcerpc_pipe *net_pipe;
+ 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 creds_CredentialState *creds_state;
+ struct netr_Credential netr_cred;
+ struct samr_Password mach_pw;
+
+ mem_ctx = talloc_new(NULL);
+ if (mem_ctx == NULL) {
+ d_printf("talloc_new failed\n");
+ return false;
+ }
+
+ net_pipe = dcerpc_pipe_init(mem_ctx,
+ cli->transport->socket->event.ctx,
+ lp_iconv_convenience(lp_ctx));
+ if (net_pipe == NULL) {
+ d_printf("dcerpc_pipe_init failed\n");
+ goto done;
+ }
+
+ status = dcerpc_pipe_open_smb(net_pipe, cli->tree, "\\netlogon");
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("dcerpc_pipe_open_smb failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ status = dcerpc_bind_auth_none(net_pipe, &ndr_table_netlogon);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("dcerpc_bind_auth_none failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ 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) {
+ d_printf("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(net_pipe, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("netr_ServerReqChallenge failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ negotiate_flags = NETLOGON_NEG_AUTH2_FLAGS;
+ E_md4hash(cli_credentials_get_password(wks_cred), mach_pw.hash);
+
+ creds_state = talloc(mem_ctx, struct creds_CredentialState);
+ creds_client_init(creds_state, r.in.credentials,
+ r.out.return_credentials, &mach_pw,
+ &netr_cred, negotiate_flags);
+
+ 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;
+
+ status = dcerpc_netr_ServerAuthenticate2(net_pipe, mem_ctx, &a);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("netr_ServerServerAuthenticate2 failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ if (!creds_client_check(creds_state, a.out.return_credentials)) {
+ d_printf("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 smbcli_state *cli,
+ struct loadparm_context *lp_ctx,
+ struct cli_credentials *wks_creds,
+ struct cli_credentials *user_creds)
+{
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+ bool ret = false;
+ struct dcerpc_pipe *net_pipe;
+ int i;
+
+ mem_ctx = talloc_new(NULL);
+ if (mem_ctx == NULL) {
+ d_printf("talloc_new failed\n");
+ return false;
+ }
+
+ net_pipe = dcerpc_pipe_init(mem_ctx,
+ cli->transport->socket->event.ctx,
+ lp_iconv_convenience(lp_ctx));
+ if (net_pipe == NULL) {
+ d_printf("dcerpc_pipe_init failed\n");
+ goto done;
+ }
+
+ status = dcerpc_pipe_open_smb(net_pipe, cli->tree, "\\netlogon");
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("dcerpc_pipe_open_smb failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+#if 0
+ net_pipe->conn->flags |= DCERPC_DEBUG_PRINT_IN |
+ DCERPC_DEBUG_PRINT_OUT;
+#endif
+#if 1
+ net_pipe->conn->flags |= (DCERPC_SIGN | DCERPC_SEAL);
+ status = dcerpc_bind_auth(net_pipe, &ndr_table_netlogon,
+ wks_creds, lp_gensec_settings(lp_ctx, lp_ctx), DCERPC_AUTH_TYPE_SCHANNEL,
+ DCERPC_AUTH_LEVEL_PRIVACY,
+ NULL);
+#else
+ status = dcerpc_bind_auth_none(net_pipe, &ndr_table_netlogon);
+#endif
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("schannel bind failed: %s\n", nt_errstr(status));
+ goto done;
+ }
+
+
+ for (i=2; i<4; i++) {
+ int flags;
+ DATA_BLOB chal, nt_resp, lm_resp, names_blob, session_key;
+ struct 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) {
+ d_printf("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(user_creds),
+ cli_credentials_get_domain(user_creds));
+ status = cli_credentials_get_ntlm_response(
+ user_creds, mem_ctx, &flags, chal, names_blob,
+ &lm_resp, &nt_resp, NULL, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("cli_credentials_get_ntlm_response failed:"
+ " %s\n", nt_errstr(status));
+ goto done;
+ }
+
+ creds_state = cli_credentials_get_netlogon_creds(wks_creds);
+ 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_low = 0;
+ ninfo.identity_info.logon_id_high = 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 = 2;
+ 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(net_pipe, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("netr_LogonSamLogon failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ if ((r.out.return_authenticator == NULL) ||
+ (!creds_client_check(creds_state,
+ &r.out.return_authenticator->cred))) {
+ d_printf("Credentials check failed!\n");
+ goto done;
+ }
+
+ 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);
+ session_key = data_blob_talloc(mem_ctx,
+ creds_state->session_key, 16);
+ arcfour_crypt_blob(pinfo.ntpassword.hash,
+ sizeof(pinfo.ntpassword.hash),
+ &session_key);
+
+ logon.password = &pinfo;
+
+ r.in.logon_level = 1;
+ r.in.logon = &logon;
+ r.out.return_authenticator = &return_authenticator;
+
+ status = dcerpc_netr_LogonSamLogon(net_pipe, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("netr_LogonSamLogon failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ if ((r.out.return_authenticator == NULL) ||
+ (!creds_client_check(creds_state,
+ &r.out.return_authenticator->cred))) {
+ d_printf("Credentials check failed!\n");
+ goto done;
+ }
+ }
+
+ {
+ struct netr_ServerPasswordSet s;
+ char *password = generate_random_str(wks_creds, 8);
+ struct 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);
+ creds_des_encrypt(creds_state, &new_password);
+ creds_client_authenticator(creds_state, &credential);
+
+ status = dcerpc_netr_ServerPasswordSet(net_pipe, mem_ctx, &s);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("ServerPasswordSet - %s\n", nt_errstr(status));
+ goto done;
+ }
+
+ if (!creds_client_check(creds_state,
+ &s.out.return_authenticator->cred)) {
+ printf("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 smbcli_state *cli,
+ struct loadparm_context *lp_ctx,
+ 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(cli, lp_ctx, admin_creds, wks_name);
+ talloc_free(wks_name);
+ return ret;
+}
+
+/*
+ * Test the Samba3 DC code a bit. Join, do some schan netlogon ops, leave
+ */
+
+bool torture_netlogon_samba3(struct torture_context *torture)
+{
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+ bool ret = false;
+ struct smbcli_state *cli;
+ struct cli_credentials *anon_creds;
+ 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);
+ if (wks_name == NULL) {
+ wks_name = get_myname(torture);
+ }
+
+ mem_ctx = talloc_init("torture_netlogon_samba3");
+
+ if (mem_ctx == NULL) {
+ d_printf("talloc_init failed\n");
+ return false;
+ }
+
+ if (!(anon_creds = cli_credentials_init_anon(mem_ctx))) {
+ d_printf("create_anon_creds failed\n");
+ goto done;
+ }
+
+ lp_smbcli_options(torture->lp_ctx, &options);
+ lp_smbcli_session_options(torture->lp_ctx, &session_options);
+
+ status = smbcli_full_connection(mem_ctx, &cli,
+ torture_setting_string(torture, "host", NULL),
+ lp_smb_ports(torture->lp_ctx),
+ "IPC$", NULL,
+ lp_socket_options(torture->lp_ctx),
+ anon_creds,
+ lp_resolve_context(torture->lp_ctx),
+ torture->ev, &options, &session_options,
+ lp_iconv_convenience(torture->lp_ctx),
+ lp_gensec_settings(torture, torture->lp_ctx));
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("smbcli_full_connection failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ wks_creds = cli_credentials_init(mem_ctx);
+ if (wks_creds == NULL) {
+ d_printf("cli_credentials_init failed\n");
+ goto done;
+ }
+
+ 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_str(wks_creds, 8),
+ CRED_SPECIFIED);
+
+ if (!join3(cli, torture->lp_ctx, false, cmdline_credentials, wks_creds)) {
+ d_printf("join failed\n");
+ goto done;
+ }
+
+ cli_credentials_set_domain(
+ cmdline_credentials, 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;
+
+ if (!auth2(cli, torture->lp_ctx, wks_creds)) {
+ d_printf("auth2 failed\n");
+ goto done;
+ }
+
+ for (j=0; j<2; j++) {
+ if (!schan(cli, torture->lp_ctx, wks_creds, cmdline_credentials)) {
+ d_printf("schan failed\n");
+ goto done;
+ }
+ }
+ }
+
+ if (!leave(cli, torture->lp_ctx, cmdline_credentials, wks_creds)) {
+ d_printf("leave failed\n");
+ goto done;
+ }
+
+ ret = true;
+
+ done:
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+/*
+ * 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;
+ bool ret = false;
+ struct smbcli_state *cli;
+ struct cli_credentials *wks_creds;
+ struct smbcli_options options;
+ struct smbcli_session_options session_options;
+
+ lp_smbcli_options(tctx->lp_ctx, &options);
+ lp_smbcli_session_options(tctx->lp_ctx, &session_options);
+
+ status = smbcli_full_connection(tctx, &cli,
+ torture_setting_string(tctx, "host", NULL),
+ lp_smb_ports(tctx->lp_ctx),
+ "IPC$", NULL, lp_socket_options(tctx->lp_ctx),
+ smb_creds, lp_resolve_context(tctx->lp_ctx),
+ tctx->ev, &options, &session_options,
+ lp_iconv_convenience(tctx->lp_ctx),
+ lp_gensec_settings(tctx, tctx->lp_ctx));
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("smbcli_full_connection failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ wks_creds = cli_credentials_init(cli);
+ if (wks_creds == NULL) {
+ d_printf("cli_credentials_init failed\n");
+ goto done;
+ }
+
+ 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_str(wks_creds, 8),
+ CRED_SPECIFIED);
+
+ if (!join3(cli, tctx->lp_ctx, use_level25, samr_creds, wks_creds)) {
+ d_printf("join failed\n");
+ goto done;
+ }
+
+ cli_credentials_set_domain(
+ cmdline_credentials, cli_credentials_get_domain(wks_creds),
+ CRED_SPECIFIED);
+
+ if (!auth2(cli, tctx->lp_ctx, wks_creds)) {
+ d_printf("auth2 failed\n");
+ goto done;
+ }
+
+ if (!leave(cli, tctx->lp_ctx, samr_creds, wks_creds)) {
+ d_printf("leave failed\n");
+ goto done;
+ }
+
+ talloc_free(cli);
+
+ ret = true;
+
+ done:
+ return ret;
+}
+
+/*
+ * 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.
+ */
+
+bool torture_samba3_sessionkey(struct torture_context *torture)
+{
+ bool ret = false;
+ struct cli_credentials *anon_creds;
+ const char *wks_name;
+
+ wks_name = torture_setting_string(torture, "wksname", get_myname(torture));
+
+ if (!(anon_creds = cli_credentials_init_anon(torture))) {
+ d_printf("create_anon_creds failed\n");
+ goto done;
+ }
+
+ cli_credentials_set_workstation(anon_creds, wks_name, CRED_SPECIFIED);
+
+ ret = true;
+
+ 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)) {
+ d_printf("join using anonymous bind on an anonymous smb "
+ "connection succeeded -- HUH??\n");
+ ret = false;
+ }
+ }
+
+ if (!test_join3(torture, false, anon_creds, cmdline_credentials,
+ wks_name)) {
+ d_printf("join using ntlmssp bind on an anonymous smb "
+ "connection failed\n");
+ ret = false;
+ }
+
+ if (!test_join3(torture, false, cmdline_credentials, NULL, wks_name)) {
+ d_printf("join using anonymous bind on an authenticated smb "
+ "connection failed\n");
+ ret = false;
+ }
+
+ if (!test_join3(torture, false, cmdline_credentials,
+ cmdline_credentials,
+ wks_name)) {
+ d_printf("join using ntlmssp bind on an authenticated smb "
+ "connection failed\n");
+ ret = false;
+ }
+
+ /*
+ * The following two are tests for setuserinfolevel 25
+ */
+
+ if (!test_join3(torture, true, anon_creds, cmdline_credentials,
+ wks_name)) {
+ d_printf("join using ntlmssp bind on an anonymous smb "
+ "connection failed\n");
+ ret = false;
+ }
+
+ if (!test_join3(torture, true, cmdline_credentials, NULL, wks_name)) {
+ d_printf("join using anonymous bind on an authenticated smb "
+ "connection failed\n");
+ ret = false;
+ }
+
+ done:
+
+ return ret;
+}
+
+/*
+ * open pipe and bind, given an IPC$ context
+ */
+
+static NTSTATUS pipe_bind_smb(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_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, tree->session->transport->socket->event.ctx,
+ lp_iconv_convenience(lp_ctx)))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = dcerpc_pipe_open_smb(result, tree, pipe_name);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("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)) {
+ d_printf("schannel bind failed: %s\n", nt_errstr(status));
+ talloc_free(result);
+ return status;
+ }
+
+ *p = result;
+ return NT_STATUS_OK;
+}
+
+/*
+ * Sane wrapper around lsa_LookupNames
+ */
+
+static struct dom_sid *name2sid(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;
+
+ 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(p, tmp_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("OpenPolicy2 failed - %s\n", nt_errstr(status));
+ 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(p, tmp_ctx, &l);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("LookupNames of %s failed - %s\n", lsa_name.string,
+ nt_errstr(status));
+ 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(p, tmp_ctx, &c);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("dcerpc_lsa_Close failed - %s\n", nt_errstr(status));
+ 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(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ struct smbcli_tree *tree)
+{
+ struct dcerpc_pipe *lsa;
+ 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(mem_ctx, lp_ctx, tree, "\\pipe\\lsarpc",
+ &ndr_table_lsarpc, &lsa);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("(%s) Could not bind to LSA: %s\n",
+ __location__, nt_errstr(status));
+ return NULL;
+ }
+
+ 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(lsa, mem_ctx, &r);
+
+ authority_name_p = *r.out.authority_name;
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("(%s) GetUserName failed - %s\n",
+ __location__, nt_errstr(status));
+ talloc_free(lsa);
+ return NULL;
+ }
+
+ result = name2sid(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
+ */
+
+NTSTATUS secondary_tcon(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 = 0;
+ 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)) {
+ d_printf("(%s) smb_raw_tcon failed: %s\n", __location__,
+ nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ result->tid = tcon.tconx.out.tid;
+ 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
+ */
+
+bool torture_samba3_rpc_getusername(struct torture_context *torture)
+{
+ NTSTATUS status;
+ struct smbcli_state *cli;
+ TALLOC_CTX *mem_ctx;
+ 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;
+
+ if (!(mem_ctx = talloc_new(torture))) {
+ return false;
+ }
+
+ lp_smbcli_options(torture->lp_ctx, &options);
+ lp_smbcli_session_options(torture->lp_ctx, &session_options);
+
+ status = smbcli_full_connection(
+ mem_ctx, &cli, torture_setting_string(torture, "host", NULL),
+ lp_smb_ports(torture->lp_ctx),
+ "IPC$", NULL, lp_socket_options(torture->lp_ctx), cmdline_credentials,
+ lp_resolve_context(torture->lp_ctx),
+ torture->ev, &options, &session_options,
+ lp_iconv_convenience(torture->lp_ctx),
+ lp_gensec_settings(torture, torture->lp_ctx));
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("(%s) smbcli_full_connection failed: %s\n",
+ __location__, nt_errstr(status));
+ ret = false;
+ goto done;
+ }
+
+ if (!(user_sid = whoami(mem_ctx, torture->lp_ctx, cli->tree))) {
+ d_printf("(%s) whoami on auth'ed connection failed\n",
+ __location__);
+ ret = false;
+ }
+
+ talloc_free(cli);
+
+ if (!(anon_creds = cli_credentials_init_anon(mem_ctx))) {
+ d_printf("(%s) create_anon_creds failed\n", __location__);
+ ret = false;
+ goto done;
+ }
+
+ status = smbcli_full_connection(
+ mem_ctx, &cli, torture_setting_string(torture, "host", NULL),
+ lp_smb_ports(torture->lp_ctx), "IPC$", NULL,
+ lp_socket_options(torture->lp_ctx), anon_creds,
+ lp_resolve_context(torture->lp_ctx),
+ torture->ev, &options, &session_options,
+ lp_iconv_convenience(torture->lp_ctx),
+ lp_gensec_settings(torture, torture->lp_ctx));
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("(%s) anon smbcli_full_connection failed: %s\n",
+ __location__, nt_errstr(status));
+ ret = false;
+ goto done;
+ }
+
+ if (!(user_sid = whoami(mem_ctx, torture->lp_ctx, cli->tree))) {
+ d_printf("(%s) whoami on anon connection failed\n",
+ __location__);
+ ret = false;
+ goto done;
+ }
+
+ if (!dom_sid_equal(user_sid,
+ dom_sid_parse_talloc(mem_ctx, "s-1-5-7"))) {
+ d_printf("(%s) Anon lsa_GetUserName returned %s, expected "
+ "S-1-5-7", __location__,
+ dom_sid_string(mem_ctx, user_sid));
+ ret = false;
+ }
+
+ if (!(user_creds = cli_credentials_init(mem_ctx))) {
+ d_printf("(%s) cli_credentials_init failed\n", __location__);
+ ret = false;
+ goto done;
+ }
+
+ 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_str(user_creds, 8),
+ CRED_SPECIFIED);
+
+ if (!create_user(mem_ctx, cli, torture->lp_ctx, cmdline_credentials,
+ cli_credentials_get_username(user_creds),
+ cli_credentials_get_password(user_creds),
+ &domain_name, &created_sid)) {
+ d_printf("(%s) create_user failed\n", __location__);
+ ret = false;
+ goto done;
+ }
+
+ 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, mem_ctx, false, session_options);
+ if (session2 == NULL) {
+ d_printf("(%s) smbcli_session_init failed\n",
+ __location__);
+ goto done;
+ }
+
+ 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 = lp_gensec_settings(torture, torture->lp_ctx);
+
+ status = smb_composite_sesssetup(session2, &setup);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("(%s) session setup with new user failed: "
+ "%s\n", __location__, nt_errstr(status));
+ ret = false;
+ goto done;
+ }
+ session2->vuid = setup.out.vuid;
+
+ if (!NT_STATUS_IS_OK(secondary_tcon(mem_ctx, session2,
+ "IPC$", &tree))) {
+ d_printf("(%s) secondary_tcon failed\n",
+ __location__);
+ ret = false;
+ goto done;
+ }
+
+ if (!(user_sid = whoami(mem_ctx, torture->lp_ctx, tree))) {
+ d_printf("(%s) whoami on user connection failed\n",
+ __location__);
+ ret = false;
+ goto del;
+ }
+
+ talloc_free(tree);
+ }
+
+ d_printf("Created %s, found %s\n",
+ dom_sid_string(mem_ctx, created_sid),
+ dom_sid_string(mem_ctx, user_sid));
+
+ if (!dom_sid_equal(created_sid, user_sid)) {
+ ret = false;
+ }
+
+ del:
+ if (!delete_user(cli, torture->lp_ctx,
+ cmdline_credentials,
+ cli_credentials_get_username(user_creds))) {
+ d_printf("(%s) delete_user failed\n", __location__);
+ ret = false;
+ }
+
+ done:
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+static bool test_NetShareGetInfo(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ 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;
+
+ r.in.server_unc = talloc_asprintf(mem_ctx, "\\\\%s",
+ dcerpc_server_name(p));
+ r.in.share_name = sharename;
+ r.out.info = &info;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ r.in.level = levels[i];
+
+ printf("testing NetShareGetInfo level %u on share '%s'\n",
+ r.in.level, r.in.share_name);
+
+ status = dcerpc_srvsvc_NetShareGetInfo(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("NetShareGetInfo level %u on share '%s' failed"
+ " - %s\n", r.in.level, r.in.share_name,
+ nt_errstr(status));
+ ret = false;
+ continue;
+ }
+ if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("NetShareGetInfo level %u on share '%s' failed "
+ "- %s\n", r.in.level, r.in.share_name,
+ win_errstr(r.out.result));
+ ret = false;
+ continue;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_NetShareEnum(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ const char **one_sharename)
+{
+ 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;
+ struct srvsvc_NetShareCtr1004 c1004;
+ struct srvsvc_NetShareCtr1005 c1005;
+ struct srvsvc_NetShareCtr1006 c1006;
+ struct srvsvc_NetShareCtr1007 c1007;
+ uint32_t totalentries = 0;
+ uint32_t levels[] = { 0, 1, 2, 501, 502, 1004, 1005, 1006, 1007 };
+ int i;
+ bool ret = true;
+
+ 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 = NULL;
+ r.out.totalentries = &totalentries;
+ r.out.info_ctr = &info_ctr;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ info_ctr.level = levels[i];
+
+ switch (info_ctr.level) {
+ case 0:
+ ZERO_STRUCT(c0);
+ info_ctr.ctr.ctr0 = &c0;
+ break;
+ case 1:
+ ZERO_STRUCT(c1);
+ info_ctr.ctr.ctr1 = &c1;
+ break;
+ case 2:
+ ZERO_STRUCT(c2);
+ info_ctr.ctr.ctr2 = &c2;
+ break;
+ case 501:
+ ZERO_STRUCT(c501);
+ info_ctr.ctr.ctr501 = &c501;
+ break;
+ case 502:
+ ZERO_STRUCT(c502);
+ info_ctr.ctr.ctr502 = &c502;
+ break;
+ case 1004:
+ ZERO_STRUCT(c1004);
+ info_ctr.ctr.ctr1004 = &c1004;
+ break;
+ case 1005:
+ ZERO_STRUCT(c1005);
+ info_ctr.ctr.ctr1005 = &c1005;
+ break;
+ case 1006:
+ ZERO_STRUCT(c1006);
+ info_ctr.ctr.ctr1006 = &c1006;
+ break;
+ case 1007:
+ ZERO_STRUCT(c1007);
+ info_ctr.ctr.ctr1007 = &c1007;
+ break;
+ }
+
+ printf("testing NetShareEnum level %u\n", info_ctr.level);
+ status = dcerpc_srvsvc_NetShareEnum(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("NetShareEnum level %u failed - %s\n",
+ info_ctr.level, nt_errstr(status));
+ ret = false;
+ continue;
+ }
+ if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("NetShareEnum level %u failed - %s\n",
+ info_ctr.level, win_errstr(r.out.result));
+ continue;
+ }
+ if (info_ctr.level == 0) {
+ struct srvsvc_NetShareCtr0 *ctr = r.out.info_ctr->ctr.ctr0;
+ if (ctr->count > 0) {
+ *one_sharename = ctr->array[0].name;
+ }
+ }
+ }
+
+ return ret;
+}
+
+bool torture_samba3_rpc_srvsvc(struct torture_context *torture)
+{
+ struct dcerpc_pipe *p;
+ TALLOC_CTX *mem_ctx;
+ bool ret = true;
+ const char *sharename = NULL;
+ struct smbcli_state *cli;
+ NTSTATUS status;
+
+ if (!(mem_ctx = talloc_new(torture))) {
+ return false;
+ }
+
+ if (!(torture_open_connection_share(
+ mem_ctx, &cli, torture, torture_setting_string(torture, "host", NULL),
+ "IPC$", torture->ev))) {
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ status = pipe_bind_smb(mem_ctx, torture->lp_ctx, cli->tree,
+ "\\pipe\\srvsvc", &ndr_table_srvsvc, &p);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("(%s) could not bind to srvsvc pipe: %s\n",
+ __location__, nt_errstr(status));
+ ret = false;
+ goto done;
+ }
+
+ ret &= test_NetShareEnum(p, mem_ctx, &sharename);
+ if (sharename == NULL) {
+ printf("did not get sharename\n");
+ } else {
+ ret &= test_NetShareGetInfo(p, mem_ctx, sharename);
+ }
+
+ done:
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+/*
+ * Do a ReqChallenge/Auth2 with a random wks name, make sure it returns
+ * NT_STATUS_NO_SAM_ACCOUNT
+ */
+
+bool torture_samba3_rpc_randomauth2(struct torture_context *torture)
+{
+ TALLOC_CTX *mem_ctx;
+ struct dcerpc_pipe *net_pipe;
+ 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 creds_CredentialState *creds_state;
+ struct netr_Credential netr_cred;
+ struct samr_Password mach_pw;
+ struct smbcli_state *cli;
+
+ if (!(mem_ctx = talloc_new(torture))) {
+ d_printf("talloc_new failed\n");
+ return false;
+ }
+
+ if (!(wksname = generate_random_str_list(
+ mem_ctx, 14, "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"))) {
+ d_printf("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))) {
+ d_printf("IPC$ connection failed\n");
+ goto done;
+ }
+
+ if (!(net_pipe = dcerpc_pipe_init(
+ mem_ctx, cli->transport->socket->event.ctx,
+ lp_iconv_convenience(torture->lp_ctx)))) {
+ d_printf("dcerpc_pipe_init failed\n");
+ goto done;
+ }
+
+ status = dcerpc_pipe_open_smb(net_pipe, cli->tree, "\\netlogon");
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("dcerpc_pipe_open_smb failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ status = dcerpc_bind_auth_none(net_pipe, &ndr_table_netlogon);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("dcerpc_bind_auth_none failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ 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) {
+ d_printf("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(net_pipe, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("netr_ServerReqChallenge failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ negotiate_flags = NETLOGON_NEG_AUTH2_FLAGS;
+ E_md4hash("foobar", mach_pw.hash);
+
+ creds_state = talloc(mem_ctx, struct creds_CredentialState);
+ creds_client_init(creds_state, r.in.credentials,
+ r.out.return_credentials, &mach_pw,
+ &netr_cred, negotiate_flags);
+
+ 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;
+
+ status = dcerpc_netr_ServerAuthenticate2(net_pipe, mem_ctx, &a);
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NO_TRUST_SAM_ACCOUNT)) {
+ d_printf("dcerpc_netr_ServerAuthenticate2 returned %s, "
+ "expected NT_STATUS_NO_TRUST_SAM_ACCOUNT\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ result = true;
+ done:
+ talloc_free(mem_ctx);
+ return result;
+}
+
+static struct security_descriptor *get_sharesec(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ struct smbcli_session *sess,
+ const char *sharename)
+{
+ struct smbcli_tree *tree;
+ TALLOC_CTX *tmp_ctx;
+ struct dcerpc_pipe *p;
+ NTSTATUS status;
+ struct srvsvc_NetShareGetInfo r;
+ union srvsvc_NetShareInfo info;
+ struct security_descriptor *result;
+
+ if (!(tmp_ctx = talloc_new(mem_ctx))) {
+ d_printf("talloc_new failed\n");
+ return NULL;
+ }
+
+ if (!NT_STATUS_IS_OK(secondary_tcon(tmp_ctx, sess, "IPC$", &tree))) {
+ d_printf("secondary_tcon failed\n");
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+
+ status = pipe_bind_smb(mem_ctx, lp_ctx, tree, "\\pipe\\srvsvc",
+ &ndr_table_srvsvc, &p);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("(%s) could not bind to srvsvc pipe: %s\n",
+ __location__, nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+
+#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(p, tmp_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("srvsvc_NetShareGetInfo failed: %s\n",
+ nt_errstr(status));
+ 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(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ struct smbcli_session *sess,
+ const char *sharename,
+ struct security_descriptor *sd)
+{
+ struct smbcli_tree *tree;
+ TALLOC_CTX *tmp_ctx;
+ struct dcerpc_pipe *p;
+ 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))) {
+ d_printf("talloc_new failed\n");
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!NT_STATUS_IS_OK(secondary_tcon(tmp_ctx, sess, "IPC$", &tree))) {
+ d_printf("secondary_tcon failed\n");
+ talloc_free(tmp_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ status = pipe_bind_smb(mem_ctx, lp_ctx, tree, "\\pipe\\srvsvc",
+ &ndr_table_srvsvc, &p);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("(%s) could not bind to srvsvc pipe: %s\n",
+ __location__, nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+#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(p, tmp_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("srvsvc_NetShareSetInfo failed: %s\n",
+ nt_errstr(status));
+ }
+
+ talloc_free(tmp_ctx);
+ return status;
+}
+
+bool try_tcon(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_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))) {
+ d_printf("talloc_new failed\n");
+ return false;
+ }
+
+ status = secondary_tcon(tmp_ctx, session, sharename, &rmdir_tree);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("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))) {
+ d_printf("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) {
+ d_printf("security_descriptor_dacl_create failed\n");
+ talloc_free(tmp_ctx);
+ return false;
+ }
+
+ status = set_sharesec(mem_ctx, lp_ctx, session, sharename, sd);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("custom set_sharesec failed: %s\n",
+ nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return false;
+ }
+
+ status = secondary_tcon(tmp_ctx, session, sharename, &tree);
+ if (!NT_STATUS_EQUAL(status, expected_tcon)) {
+ d_printf("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)) {
+ d_printf("(%s) Expected %s, got %s\n", __location__,
+ nt_errstr(expected_mkdir), nt_errstr(status));
+ ret = false;
+ }
+
+ done:
+ smbcli_rmdir(rmdir_tree, "sharesec_testdir");
+
+ status = set_sharesec(mem_ctx, lp_ctx, session, sharename, orig_sd);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("custom set_sharesec failed: %s\n",
+ nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return false;
+ }
+
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+bool torture_samba3_rpc_sharesec(struct torture_context *torture)
+{
+ TALLOC_CTX *mem_ctx;
+ bool ret = true;
+ struct smbcli_state *cli;
+ struct security_descriptor *sd;
+ struct dom_sid *user_sid;
+
+ if (!(mem_ctx = talloc_new(torture))) {
+ return false;
+ }
+
+ if (!(torture_open_connection_share(
+ mem_ctx, &cli, torture, torture_setting_string(torture, "host", NULL),
+ "IPC$", torture->ev))) {
+ d_printf("IPC$ connection failed\n");
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ if (!(user_sid = whoami(mem_ctx, torture->lp_ctx, cli->tree))) {
+ d_printf("whoami failed\n");
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ sd = get_sharesec(mem_ctx, torture->lp_ctx, cli->session,
+ torture_setting_string(torture, "share", NULL));
+
+ ret &= try_tcon(mem_ctx, torture->lp_ctx, sd, cli->session,
+ torture_setting_string(torture, "share", NULL),
+ user_sid, 0, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK);
+
+ ret &= try_tcon(mem_ctx, torture->lp_ctx, sd, cli->session,
+ torture_setting_string(torture, "share", NULL),
+ user_sid, SEC_FILE_READ_DATA, NT_STATUS_OK,
+ NT_STATUS_MEDIA_WRITE_PROTECTED);
+
+ ret &= try_tcon(mem_ctx, torture->lp_ctx, sd, cli->session,
+ torture_setting_string(torture, "share", NULL),
+ user_sid, SEC_FILE_ALL, NT_STATUS_OK, NT_STATUS_OK);
+
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+bool torture_samba3_rpc_lsa(struct torture_context *torture)
+{
+ TALLOC_CTX *mem_ctx;
+ bool ret = true;
+ struct smbcli_state *cli;
+ struct dcerpc_pipe *p;
+ struct policy_handle lsa_handle;
+ NTSTATUS status;
+ struct dom_sid *domain_sid;
+
+ if (!(mem_ctx = talloc_new(torture))) {
+ return false;
+ }
+
+ if (!(torture_open_connection_share(
+ mem_ctx, &cli, torture, torture_setting_string(torture, "host", NULL),
+ "IPC$", torture->ev))) {
+ d_printf("IPC$ connection failed\n");
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ status = pipe_bind_smb(mem_ctx, torture->lp_ctx, cli->tree, "\\lsarpc",
+ &ndr_table_lsarpc, &p);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("(%s) pipe_bind_smb failed: %s\n", __location__,
+ nt_errstr(status));
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ {
+ struct lsa_ObjectAttribute attr;
+ struct lsa_OpenPolicy2 o;
+ o.in.system_name = talloc_asprintf(
+ mem_ctx, "\\\\%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;
+ status = dcerpc_lsa_OpenPolicy2(p, mem_ctx, &o);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("(%s) dcerpc_lsa_OpenPolicy2 failed: %s\n",
+ __location__, nt_errstr(status));
+ talloc_free(mem_ctx);
+ return false;
+ }
+ }
+
+#if 0
+ p->conn->flags |= DCERPC_DEBUG_PRINT_IN | DCERPC_DEBUG_PRINT_OUT;
+#endif
+
+ {
+ int i;
+ int levels[] = { 2,3,5,6 };
+
+ for (i=0; i<ARRAY_SIZE(levels); i++) {
+ struct lsa_QueryInfoPolicy r;
+ union lsa_PolicyInformation *info = NULL;
+ r.in.handle = &lsa_handle;
+ r.in.level = levels[i];
+ r.out.info = &info;
+ status = dcerpc_lsa_QueryInfoPolicy(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("(%s) dcerpc_lsa_QueryInfoPolicy %d "
+ "failed: %s\n", __location__,
+ levels[i], nt_errstr(status));
+ talloc_free(mem_ctx);
+ return false;
+ }
+ if (levels[i] == 5) {
+ domain_sid = info->account_domain.sid;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static NTSTATUS get_servername(TALLOC_CTX *mem_ctx, struct smbcli_tree *tree,
+ struct smb_iconv_convenience *iconv_convenience,
+ char **name)
+{
+ struct rap_WserverGetInfo r;
+ NTSTATUS status;
+ char servername[17];
+
+ r.in.level = 0;
+ r.in.bufsize = 0xffff;
+
+ status = smbcli_rap_netservergetinfo(tree, iconv_convenience, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ memcpy(servername, r.out.info.info0.name, 16);
+ servername[16] = '\0';
+
+ if (!pull_ascii_talloc(mem_ctx, name, servername, NULL)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS find_printers(TALLOC_CTX *ctx, struct loadparm_context *lp_ctx,
+ struct smbcli_tree *tree,
+ const char ***printers, int *num_printers)
+{
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+ struct dcerpc_pipe *p;
+ struct srvsvc_NetShareEnum r;
+ struct srvsvc_NetShareInfoCtr info_ctr;
+ struct srvsvc_NetShareCtr1 c1_in;
+ struct srvsvc_NetShareCtr1 *c1;
+ uint32_t totalentries = 0;
+ int i;
+
+ mem_ctx = talloc_new(ctx);
+ if (mem_ctx == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = pipe_bind_smb(mem_ctx, lp_ctx,
+ tree, "\\srvsvc", &ndr_table_srvsvc,
+ &p);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("could not bind to srvsvc pipe\n");
+ talloc_free(mem_ctx);
+ return status;
+ }
+
+ ZERO_STRUCT(c1_in);
+ info_ctr.level = 1;
+ info_ctr.ctr.ctr1 = &c1_in;
+
+ 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 = NULL;
+ r.out.totalentries = &totalentries;
+ r.out.info_ctr = &info_ctr;
+
+ status = dcerpc_srvsvc_NetShareEnum(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("NetShareEnum level %u failed - %s\n",
+ info_ctr.level, nt_errstr(status));
+ talloc_free(mem_ctx);
+ return status;
+ }
+
+ *printers = NULL;
+ *num_printers = 0;
+ c1 = r.out.info_ctr->ctr.ctr1;
+ for (i=0; i<c1->count; i++) {
+ if (c1->array[i].type != STYPE_PRINTQ) {
+ continue;
+ }
+ if (!add_string_to_array(ctx, c1->array[i].name,
+ printers, num_printers)) {
+ talloc_free(ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ talloc_free(mem_ctx);
+ return NT_STATUS_OK;
+}
+
+static bool enumprinters(TALLOC_CTX *mem_ctx, struct dcerpc_pipe *pipe,
+ const char *servername, int level, int *num_printers)
+{
+ struct spoolss_EnumPrinters r;
+ NTSTATUS status;
+ DATA_BLOB blob;
+ uint32_t needed;
+ uint32_t count;
+ union spoolss_PrinterInfo *info;
+
+ r.in.flags = PRINTER_ENUM_LOCAL;
+ r.in.server = talloc_asprintf(mem_ctx, "\\\\%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;
+
+ status = dcerpc_spoolss_EnumPrinters(pipe, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("(%s) dcerpc_spoolss_EnumPrinters failed: %s\n",
+ __location__, nt_errstr(status));
+ return false;
+ }
+
+ if (!W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ d_printf("(%s) EnumPrinters unexpected return code %s, should "
+ "be WERR_INSUFFICIENT_BUFFER\n", __location__,
+ win_errstr(r.out.result));
+ return false;
+ }
+
+ blob = data_blob_talloc_zero(mem_ctx, needed);
+ if (blob.data == NULL) {
+ d_printf("(%s) data_blob_talloc failed\n", __location__);
+ return false;
+ }
+
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+
+ status = dcerpc_spoolss_EnumPrinters(pipe, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(r.out.result)) {
+ d_printf("(%s) dcerpc_spoolss_EnumPrinters failed: %s, "
+ "%s\n", __location__, nt_errstr(status),
+ win_errstr(r.out.result));
+ return false;
+ }
+
+ *num_printers = count;
+
+ return true;
+}
+
+static NTSTATUS getprinterinfo(TALLOC_CTX *ctx, struct dcerpc_pipe *pipe,
+ struct policy_handle *handle, int level,
+ union spoolss_PrinterInfo **res)
+{
+ TALLOC_CTX *mem_ctx;
+ struct spoolss_GetPrinter r;
+ DATA_BLOB blob;
+ NTSTATUS status;
+ uint32_t needed;
+
+ mem_ctx = talloc_new(ctx);
+ if (mem_ctx == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ r.in.handle = handle;
+ r.in.level = level;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+ r.out.needed = &needed;
+
+ status = dcerpc_spoolss_GetPrinter(pipe, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("(%s) dcerpc_spoolss_GetPrinter failed: %s\n",
+ __location__, nt_errstr(status));
+ talloc_free(mem_ctx);
+ return status;
+ }
+
+ if (!W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ printf("GetPrinter unexpected return code %s, should "
+ "be WERR_INSUFFICIENT_BUFFER\n",
+ win_errstr(r.out.result));
+ talloc_free(mem_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ r.in.handle = handle;
+ r.in.level = level;
+ blob = data_blob_talloc(mem_ctx, NULL, needed);
+ if (blob.data == NULL) {
+ talloc_free(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ memset(blob.data, 0, blob.length);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+
+ status = dcerpc_spoolss_GetPrinter(pipe, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(r.out.result)) {
+ d_printf("(%s) dcerpc_spoolss_GetPrinter failed: %s, "
+ "%s\n", __location__, nt_errstr(status),
+ win_errstr(r.out.result));
+ talloc_free(mem_ctx);
+ return NT_STATUS_IS_OK(status) ?
+ NT_STATUS_UNSUCCESSFUL : status;
+ }
+
+ if (res != NULL) {
+ *res = talloc_steal(ctx, r.out.info);
+ }
+
+ talloc_free(mem_ctx);
+ return NT_STATUS_OK;
+}
+
+bool torture_samba3_rpc_spoolss(struct torture_context *torture)
+{
+ TALLOC_CTX *mem_ctx;
+ bool ret = true;
+ struct smbcli_state *cli;
+ struct dcerpc_pipe *p;
+ NTSTATUS status;
+ struct policy_handle server_handle, printer_handle;
+ const char **printers;
+ int num_printers;
+ struct spoolss_UserLevel1 userlevel1;
+ char *servername;
+
+ if (!(mem_ctx = talloc_new(torture))) {
+ return false;
+ }
+
+ if (!(torture_open_connection_share(
+ mem_ctx, &cli, torture, torture_setting_string(torture, "host", NULL),
+ "IPC$", torture->ev))) {
+ d_printf("IPC$ connection failed\n");
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ status = get_servername(mem_ctx, cli->tree, lp_iconv_convenience(torture->lp_ctx), &servername);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "(%s) get_servername returned %s\n",
+ __location__, nt_errstr(status));
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ if (!NT_STATUS_IS_OK(find_printers(mem_ctx, torture->lp_ctx, cli->tree,
+ &printers, &num_printers))) {
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ if (num_printers == 0) {
+ d_printf("Did not find printers\n");
+ talloc_free(mem_ctx);
+ return true;
+ }
+
+ status = pipe_bind_smb(mem_ctx, torture->lp_ctx, cli->tree, "\\spoolss",
+ &ndr_table_spoolss, &p);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("(%s) pipe_bind_smb failed: %s\n", __location__,
+ nt_errstr(status));
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ ZERO_STRUCT(userlevel1);
+ userlevel1.client = talloc_asprintf(
+ mem_ctx, "\\\\%s", lp_netbios_name(torture->lp_ctx));
+ userlevel1.user = cli_credentials_get_username(cmdline_credentials);
+ userlevel1.build = 2600;
+ userlevel1.major = 3;
+ userlevel1.minor = 0;
+ userlevel1.processor = 0;
+
+ {
+ struct spoolss_OpenPrinterEx r;
+
+ ZERO_STRUCT(r);
+ r.in.printername = talloc_asprintf(mem_ctx, "\\\\%s",
+ servername);
+ r.in.datatype = NULL;
+ r.in.access_mask = 0;
+ r.in.level = 1;
+ r.in.userlevel.level1 = &userlevel1;
+ r.out.handle = &server_handle;
+
+ status = dcerpc_spoolss_OpenPrinterEx(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(r.out.result)) {
+ d_printf("(%s) dcerpc_spoolss_OpenPrinterEx failed: "
+ "%s, %s\n", __location__, nt_errstr(status),
+ win_errstr(r.out.result));
+ talloc_free(mem_ctx);
+ return false;
+ }
+ }
+
+ {
+ struct spoolss_ClosePrinter r;
+
+ r.in.handle = &server_handle;
+ r.out.handle = &server_handle;
+
+ status = dcerpc_spoolss_ClosePrinter(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(r.out.result)) {
+ d_printf("(%s) dcerpc_spoolss_ClosePrinter failed: "
+ "%s, %s\n", __location__, nt_errstr(status),
+ win_errstr(r.out.result));
+ talloc_free(mem_ctx);
+ return false;
+ }
+ }
+
+ {
+ struct spoolss_OpenPrinterEx r;
+
+ ZERO_STRUCT(r);
+ r.in.printername = talloc_asprintf(
+ mem_ctx, "\\\\%s\\%s", servername, printers[0]);
+ r.in.datatype = NULL;
+ r.in.access_mask = 0;
+ r.in.level = 1;
+ r.in.userlevel.level1 = &userlevel1;
+ r.out.handle = &printer_handle;
+
+ status = dcerpc_spoolss_OpenPrinterEx(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(r.out.result)) {
+ d_printf("(%s) dcerpc_spoolss_OpenPrinterEx failed: "
+ "%s, %s\n", __location__, nt_errstr(status),
+ win_errstr(r.out.result));
+ talloc_free(mem_ctx);
+ return false;
+ }
+ }
+
+ {
+ int i;
+
+ for (i=0; i<8; i++) {
+ status = getprinterinfo(mem_ctx, p, &printer_handle,
+ i, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("(%s) getprinterinfo %d failed: %s\n",
+ __location__, i, nt_errstr(status));
+ ret = false;
+ }
+ }
+ }
+
+ {
+ struct spoolss_ClosePrinter r;
+
+ r.in.handle = &printer_handle;
+ r.out.handle = &printer_handle;
+
+ status = dcerpc_spoolss_ClosePrinter(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("(%s) dcerpc_spoolss_ClosePrinter failed: "
+ "%s\n", __location__, nt_errstr(status));
+ talloc_free(mem_ctx);
+ return false;
+ }
+ }
+
+ {
+ int num_enumerated;
+ if (!enumprinters(mem_ctx, p, servername, 1,
+ &num_enumerated)) {
+ d_printf("(%s) enumprinters failed\n", __location__);
+ talloc_free(mem_ctx);
+ return false;
+ }
+ if (num_printers != num_enumerated) {
+ d_printf("(%s) netshareenum gave %d printers, "
+ "enumprinters lvl 1 gave %d\n", __location__,
+ num_printers, num_enumerated);
+ talloc_free(mem_ctx);
+ return false;
+ }
+ }
+
+ {
+ int num_enumerated;
+ if (!enumprinters(mem_ctx, p, servername, 2,
+ &num_enumerated)) {
+ d_printf("(%s) enumprinters failed\n", __location__);
+ talloc_free(mem_ctx);
+ return false;
+ }
+ if (num_printers != num_enumerated) {
+ d_printf("(%s) netshareenum gave %d printers, "
+ "enumprinters lvl 2 gave %d\n", __location__,
+ num_printers, num_enumerated);
+ talloc_free(mem_ctx);
+ return false;
+ }
+ }
+
+ talloc_free(mem_ctx);
+
+ return ret;
+}
+
+bool torture_samba3_rpc_wkssvc(struct torture_context *torture)
+{
+ TALLOC_CTX *mem_ctx;
+ struct smbcli_state *cli;
+ struct dcerpc_pipe *p;
+ NTSTATUS status;
+ char *servername;
+
+ if (!(mem_ctx = talloc_new(torture))) {
+ return false;
+ }
+
+ if (!(torture_open_connection_share(
+ mem_ctx, &cli, torture, torture_setting_string(torture, "host", NULL),
+ "IPC$", torture->ev))) {
+ d_printf("IPC$ connection failed\n");
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ status = get_servername(mem_ctx, cli->tree, lp_iconv_convenience(torture->lp_ctx), &servername);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "(%s) get_servername returned %s\n",
+ __location__, nt_errstr(status));
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ status = pipe_bind_smb(mem_ctx, torture->lp_ctx, cli->tree, "\\wkssvc",
+ &ndr_table_wkssvc, &p);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("(%s) pipe_bind_smb failed: %s\n", __location__,
+ nt_errstr(status));
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ {
+ 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;
+
+ status = dcerpc_wkssvc_NetWkstaGetInfo(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(r.out.result)) {
+ d_printf("(%s) dcerpc_wkssvc_NetWksGetInfo failed: "
+ "%s, %s\n", __location__, nt_errstr(status),
+ win_errstr(r.out.result));
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ if (strcmp(servername,
+ r.out.info->info100->server_name) != 0) {
+ d_printf("(%s) servername inconsistency: RAP=%s, "
+ "dcerpc_wkssvc_NetWksGetInfo=%s",
+ __location__, servername,
+ r.out.info->info100->server_name);
+ talloc_free(mem_ctx);
+ return false;
+ }
+ }
+
+ talloc_free(mem_ctx);
+ return true;
+}
+
+static NTSTATUS winreg_close(struct dcerpc_pipe *p,
+ struct policy_handle *handle)
+{
+ struct winreg_CloseKey c;
+ NTSTATUS status;
+ TALLOC_CTX *mem_ctx;
+
+ c.in.handle = c.out.handle = handle;
+
+ if (!(mem_ctx = talloc_new(p))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = dcerpc_winreg_CloseKey(p, mem_ctx, &c);
+ talloc_free(mem_ctx);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (!W_ERROR_IS_OK(c.out.result)) {
+ return werror_to_ntstatus(c.out.result);
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS enumvalues(struct dcerpc_pipe *p, struct policy_handle *handle,
+ TALLOC_CTX *mem_ctx)
+{
+ 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;
+
+ 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(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(r.out.result)) {
+ return NT_STATUS_OK;
+ }
+ enum_index += 1;
+ }
+}
+
+static NTSTATUS enumkeys(struct dcerpc_pipe *p, struct policy_handle *handle,
+ TALLOC_CTX *mem_ctx, int depth)
+{
+ struct winreg_EnumKey r;
+ struct winreg_StringBuf kclass, name;
+ NTSTATUS status;
+ NTTIME t = 0;
+
+ if (depth <= 0) {
+ return NT_STATUS_OK;
+ }
+
+ 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 {
+ TALLOC_CTX *tmp_ctx;
+ struct winreg_OpenKey o;
+ struct policy_handle key_handle;
+ int i;
+
+ if (!(tmp_ctx = talloc_new(mem_ctx))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ name.name = NULL;
+ name.size = 1024;
+
+ status = dcerpc_winreg_EnumKey(p, tmp_ctx, &r);
+ if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(r.out.result)) {
+ /* We're done enumerating */
+ talloc_free(tmp_ctx);
+ return NT_STATUS_OK;
+ }
+
+ for (i=0; i<10-depth; i++)
+ printf(" ");
+ printf("%s\n", r.out.name->name);
+
+
+ o.in.parent_handle = handle;
+ o.in.keyname.name = r.out.name->name;
+ o.in.unknown = 0;
+ o.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ o.out.handle = &key_handle;
+
+ status = dcerpc_winreg_OpenKey(p, tmp_ctx, &o);
+ if (NT_STATUS_IS_OK(status) && W_ERROR_IS_OK(o.out.result)) {
+ enumkeys(p, &key_handle, tmp_ctx, depth-1);
+ enumvalues(p, &key_handle, tmp_ctx);
+ status = winreg_close(p, &key_handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ talloc_free(tmp_ctx);
+
+ r.in.enum_index += 1;
+ } while(true);
+
+ return NT_STATUS_OK;
+}
+
+typedef NTSTATUS (*winreg_open_fn)(struct dcerpc_pipe *, TALLOC_CTX *, void *);
+
+static bool test_Open3(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ const char *name, winreg_open_fn open_fn)
+{
+ struct policy_handle handle;
+ struct winreg_OpenHKLM r;
+ NTSTATUS status;
+
+ r.in.system_name = 0;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.handle = &handle;
+
+ status = open_fn(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(r.out.result)) {
+ d_printf("(%s) %s failed: %s, %s\n", __location__, name,
+ nt_errstr(status), win_errstr(r.out.result));
+ return false;
+ }
+
+ enumkeys(p, &handle, mem_ctx, 4);
+
+ status = winreg_close(p, &handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("(%s) dcerpc_CloseKey failed: %s\n",
+ __location__, nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+bool torture_samba3_rpc_winreg(struct torture_context *torture)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p;
+ TALLOC_CTX *mem_ctx;
+ bool ret = true;
+ struct {
+ const char *name;
+ winreg_open_fn fn;
+ } open_fns[] = {
+ {"OpenHKLM", (winreg_open_fn)dcerpc_winreg_OpenHKLM },
+ {"OpenHKU", (winreg_open_fn)dcerpc_winreg_OpenHKU },
+ {"OpenHKPD", (winreg_open_fn)dcerpc_winreg_OpenHKPD },
+ {"OpenHKPT", (winreg_open_fn)dcerpc_winreg_OpenHKPT },
+ {"OpenHKCR", (winreg_open_fn)dcerpc_winreg_OpenHKCR }};
+#if 0
+ int i;
+#endif
+
+ mem_ctx = talloc_init("torture_rpc_winreg");
+
+ status = torture_rpc_connection(torture, &p, &ndr_table_winreg);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+#if 1
+ ret = test_Open3(p, mem_ctx, open_fns[0].name, open_fns[0].fn);
+#else
+ for (i = 0; i < ARRAY_SIZE(open_fns); i++) {
+ if (!test_Open3(p, mem_ctx, open_fns[i].name, open_fns[i].fn))
+ ret = false;
+ }
+#endif
+
+ talloc_free(mem_ctx);
+
+ return ret;
+}
+
+static NTSTATUS get_shareinfo(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ struct smbcli_state *cli,
+ const char *share,
+ struct srvsvc_NetShareInfo502 **info502)
+{
+ struct smbcli_tree *ipc;
+ struct dcerpc_pipe *p;
+ struct srvsvc_NetShareGetInfo r;
+ union srvsvc_NetShareInfo info;
+ NTSTATUS status;
+
+ if (!(p = dcerpc_pipe_init(cli,
+ cli->transport->socket->event.ctx,
+ lp_iconv_convenience(lp_ctx)))) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ status = secondary_tcon(p, cli->session, "IPC$", &ipc);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ status = dcerpc_pipe_open_smb(p, ipc, "\\pipe\\srvsvc");
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("dcerpc_pipe_open_smb failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ status = dcerpc_bind_auth_none(p, &ndr_table_srvsvc);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("dcerpc_bind_auth_none failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ r.in.server_unc = talloc_asprintf(mem_ctx, "\\\\%s",
+ dcerpc_server_name(p));
+ r.in.share_name = share;
+ r.in.level = 502;
+ r.out.info = &info;
+
+ status = dcerpc_srvsvc_NetShareGetInfo(p, p, &r);
+ if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(r.out.result)) {
+ d_printf("(%s) srvsvc_NetShareGetInfo failed: %s, %s\n", __location__,
+ nt_errstr(status), win_errstr(r.out.result));
+ goto fail;
+ }
+
+ *info502 = talloc_move(mem_ctx, &info.info502);
+ return NT_STATUS_OK;
+
+ fail:
+ talloc_free(p);
+ return status;
+}
+
+/*
+ * Get us a handle on HKLM\
+ */
+
+static NTSTATUS get_hklm_handle(TALLOC_CTX *mem_ctx,
+ struct smbcli_state *cli,
+ struct smb_iconv_convenience *iconv_convenience,
+ struct dcerpc_pipe **pipe_p,
+ struct policy_handle **handle)
+{
+ struct smbcli_tree *ipc;
+ struct dcerpc_pipe *p;
+ struct winreg_OpenHKLM r;
+ NTSTATUS status;
+ struct policy_handle *result;
+
+ result = talloc(mem_ctx, struct policy_handle);
+
+ if (result == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!(p = dcerpc_pipe_init(result,
+ cli->transport->socket->event.ctx,
+ iconv_convenience))) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ status = secondary_tcon(p, cli->session, "IPC$", &ipc);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ status = dcerpc_pipe_open_smb(p, ipc, "\\winreg");
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("dcerpc_pipe_open_smb failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ status = dcerpc_bind_auth_none(p, &ndr_table_winreg);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("dcerpc_bind_auth_none failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ r.in.system_name = 0;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.handle = result;
+
+ status = dcerpc_winreg_OpenHKLM(p, p, &r);
+ if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(r.out.result)) {
+ d_printf("(%s) OpenHKLM failed: %s, %s\n", __location__,
+ nt_errstr(status), win_errstr(r.out.result));
+ goto fail;
+ }
+
+ *pipe_p = p;
+ *handle = result;
+ return NT_STATUS_OK;
+
+ fail:
+ talloc_free(result);
+ return status;
+}
+
+static NTSTATUS torture_samba3_createshare(struct smbcli_state *cli,
+ struct smb_iconv_convenience *iconv_convenience,
+ const char *sharename)
+{
+ struct dcerpc_pipe *p;
+ struct policy_handle *hklm = NULL;
+ struct policy_handle new_handle;
+ struct winreg_CreateKey c;
+ struct winreg_CloseKey cl;
+ enum winreg_CreateAction action_taken;
+ NTSTATUS status;
+ TALLOC_CTX *mem_ctx;
+
+ mem_ctx = talloc_new(cli);
+ NT_STATUS_HAVE_NO_MEMORY(mem_ctx);
+
+ status = get_hklm_handle(mem_ctx, cli, iconv_convenience, &p, &hklm);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("get_hklm_handle failed: %s\n", nt_errstr(status));
+ goto fail;
+ }
+
+ c.in.handle = hklm;
+ c.in.name.name = talloc_asprintf(
+ mem_ctx, "software\\samba\\smbconf\\%s", sharename);
+ if (c.in.name.name == NULL) {
+ d_printf("talloc_asprintf failed\n");
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+ 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;
+
+ status = dcerpc_winreg_CreateKey(p, p, &c);
+ if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(c.out.result)) {
+ d_printf("(%s) OpenKey failed: %s, %s\n", __location__,
+ nt_errstr(status), win_errstr(c.out.result));
+ goto fail;
+ }
+
+ cl.in.handle = &new_handle;
+ cl.out.handle = &new_handle;
+ status = dcerpc_winreg_CloseKey(p, p, &cl);
+ if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(cl.out.result)) {
+ d_printf("(%s) OpenKey failed: %s, %s\n", __location__,
+ nt_errstr(status), win_errstr(cl.out.result));
+ goto fail;
+ }
+
+
+ fail:
+ talloc_free(mem_ctx);
+ return status;
+}
+
+static NTSTATUS torture_samba3_deleteshare(struct torture_context *torture,
+ struct smbcli_state *cli,
+ const char *sharename)
+{
+ struct dcerpc_pipe *p;
+ struct policy_handle *hklm = NULL;
+ struct winreg_DeleteKey d;
+ NTSTATUS status;
+ TALLOC_CTX *mem_ctx;
+
+ mem_ctx = talloc_new(cli);
+ NT_STATUS_HAVE_NO_MEMORY(mem_ctx);
+
+ status = get_hklm_handle(cli, cli, lp_iconv_convenience(torture->lp_ctx),
+ &p, &hklm);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("get_hklm_handle failed: %s\n", nt_errstr(status));
+ goto fail;
+ }
+
+ d.in.handle = hklm;
+ d.in.key.name = talloc_asprintf(
+ mem_ctx, "software\\samba\\smbconf\\%s", sharename);
+ if (d.in.key.name == NULL) {
+ d_printf("talloc_asprintf failed\n");
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+ status = dcerpc_winreg_DeleteKey(p, p, &d);
+ if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(d.out.result)) {
+ d_printf("(%s) OpenKey failed: %s, %s\n", __location__,
+ nt_errstr(status), win_errstr(d.out.result));
+ goto fail;
+ }
+
+ fail:
+ talloc_free(mem_ctx);
+ return status;
+}
+
+static NTSTATUS torture_samba3_setconfig(struct smbcli_state *cli,
+ struct loadparm_context *lp_ctx,
+ const char *sharename,
+ const char *parameter,
+ const char *value)
+{
+ struct dcerpc_pipe *p = NULL;
+ struct policy_handle *hklm = NULL, key_handle;
+ struct winreg_OpenKey o;
+ struct winreg_SetValue s;
+ uint32_t type;
+ DATA_BLOB val;
+ NTSTATUS status;
+
+ status = get_hklm_handle(cli, cli, lp_iconv_convenience(lp_ctx), &p, &hklm);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("get_hklm_handle failed: %s\n", nt_errstr(status));
+ return status;;
+ }
+
+ o.in.parent_handle = hklm;
+ o.in.keyname.name = talloc_asprintf(
+ hklm, "software\\samba\\smbconf\\%s", sharename);
+ if (o.in.keyname.name == NULL) {
+ d_printf("talloc_asprintf failed\n");
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ o.in.unknown = 0;
+ o.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ o.out.handle = &key_handle;
+
+ status = dcerpc_winreg_OpenKey(p, p, &o);
+ if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(o.out.result)) {
+ d_printf("(%s) OpenKey failed: %s, %s\n", __location__,
+ nt_errstr(status), win_errstr(o.out.result));
+ goto done;
+ }
+
+ if (!reg_string_to_val(hklm, lp_iconv_convenience(lp_ctx), "REG_SZ",
+ value, &type, &val)) {
+ d_printf("(%s) reg_string_to_val failed\n", __location__);
+ goto done;
+ }
+
+ s.in.handle = &key_handle;
+ s.in.name.name = parameter;
+ s.in.type = type;
+ s.in.data = val.data;
+ s.in.size = val.length;
+
+ status = dcerpc_winreg_SetValue(p, p, &s);
+ if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(s.out.result)) {
+ d_printf("(%s) SetValue failed: %s, %s\n", __location__,
+ nt_errstr(status), win_errstr(s.out.result));
+ goto done;
+ }
+
+ done:
+ talloc_free(hklm);
+ return status;
+}
+
+bool torture_samba3_regconfig(struct torture_context *torture)
+{
+ struct smbcli_state *cli;
+ struct srvsvc_NetShareInfo502 *i = NULL;
+ NTSTATUS status;
+ bool ret = false;
+ const char *comment = "Dummer Kommentar";
+
+ if (!(torture_open_connection(&cli, torture, 0))) {
+ return false;
+ }
+
+ status = torture_samba3_createshare(cli, lp_iconv_convenience(torture->lp_ctx), "blubber");
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_warning(torture, "torture_samba3_createshare failed: "
+ "%s\n", nt_errstr(status));
+ goto done;
+ }
+
+ status = torture_samba3_setconfig(cli, torture->lp_ctx, "blubber", "comment", comment);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_warning(torture, "torture_samba3_setconfig failed: "
+ "%s\n", nt_errstr(status));
+ goto done;
+ }
+
+ status = get_shareinfo(torture, torture->lp_ctx, cli, "blubber", &i);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_warning(torture, "get_shareinfo failed: "
+ "%s\n", nt_errstr(status));
+ goto done;
+ }
+
+ if (strcmp(comment, i->comment) != 0) {
+ torture_warning(torture, "Expected comment [%s], got [%s]\n",
+ comment, i->comment);
+ goto done;
+ }
+
+ status = torture_samba3_deleteshare(torture, cli, "blubber");
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_warning(torture, "torture_samba3_deleteshare failed: "
+ "%s\n", nt_errstr(status));
+ goto done;
+ }
+
+ ret = true;
+ done:
+ talloc_free(cli);
+ return ret;
+}
diff --git a/source4/torture/rpc/samlogon.c b/source4/torture/rpc/samlogon.c
new file mode 100644
index 0000000000..ce9bf5ea6e
--- /dev/null
+++ b/source4/torture/rpc/samlogon.c
@@ -0,0 +1,1890 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ test suite for netlogon SamLogon operations
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 "auth/auth.h"
+#include "../lib/crypto/crypto.h"
+#include "lib/cmdline/popt_common.h"
+#include "torture/rpc/rpc.h"
+#include "auth/gensec/schannel_proto.h"
+#include "auth/gensec/gensec.h"
+#include "libcli/auth/libcli_auth.h"
+#include "param/param.h"
+
+#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;
+ 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 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;
+ struct smb_iconv_convenience *iconv_convenience;
+};
+
+/*
+ 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_low = 0;
+ ninfo.identity_info.logon_id_high = 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);
+ creds_client_authenticator(samlogon_state->creds, &samlogon_state->auth);
+
+ r->out.return_authenticator = NULL;
+ status = dcerpc_netr_LogonSamLogon(samlogon_state->p, samlogon_state->mem_ctx, r);
+ if (!r->out.return_authenticator ||
+ !creds_client_check(samlogon_state->creds, &r->out.return_authenticator->cred)) {
+ d_printf("Credential chaining failed\n");
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ if (error_string) {
+ *error_string = strdup(nt_errstr(status));
+ }
+ return status;
+ }
+
+ validation_level = r->in.validation_level;
+
+ creds_decrypt_samlogon(samlogon_state->creds, validation_level, r->out.validation);
+
+ 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(samlogon_state->p, samlogon_state->mem_ctx, r_ex);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (error_string) {
+ *error_string = strdup(nt_errstr(status));
+ }
+ return status;
+ }
+
+ validation_level = r_ex->in.validation_level;
+
+ creds_decrypt_samlogon(samlogon_state->creds, validation_level, r_ex->out.validation);
+
+ 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);
+ creds_client_authenticator(samlogon_state->creds, &samlogon_state->auth);
+
+ r_flags->out.return_authenticator = NULL;
+ status = dcerpc_netr_LogonSamLogonWithFlags(samlogon_state->p, samlogon_state->mem_ctx, r_flags);
+ if (!r_flags->out.return_authenticator ||
+ !creds_client_check(samlogon_state->creds, &r_flags->out.return_authenticator->cred)) {
+ d_printf("Credential chaining failed\n");
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ if (error_string) {
+ *error_string = strdup(nt_errstr(status));
+ }
+ return status;
+ }
+
+ validation_level = r_flags->in.validation_level;
+
+ creds_decrypt_samlogon(samlogon_state->creds, validation_level, r_flags->out.validation);
+
+ 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) {
+ d_printf("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 '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)) {
+ SAFE_FREE(*error_string);
+ asprintf(error_string, "Expected error: %s, got %s", nt_errstr(samlogon_state->expected_error), nt_errstr(nt_status));
+ 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;
+ }
+
+ if (memcmp(lm_hash, lm_key,
+ sizeof(lm_key)) != 0) {
+ d_printf("LM Key does not match expectations!\n");
+ d_printf("lm_key:\n");
+ dump_data(1, lm_key, 8);
+ d_printf("expected:\n");
+ dump_data(1, lm_hash, 8);
+ pass = false;
+ }
+
+ switch (break_which) {
+ case NO_NT:
+ {
+ 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) {
+ *error_string = strdup("NT Session Key does not match expectations (should be first-8 LM hash)!\n");
+ d_printf("user_session_key:\n");
+ dump_data(1, user_session_key, sizeof(user_session_key));
+ d_printf("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");
+ d_printf("user_session_key:\n");
+ dump_data(1, user_session_key, 16);
+ d_printf("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)) {
+ SAFE_FREE(*error_string);
+ asprintf(error_string, "Expected error: %s, got %s", nt_errstr(samlogon_state->expected_error), nt_errstr(nt_status));
+ 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 (lm_good) {
+ if (memcmp(lm_hash, lm_key,
+ sizeof(lm_key)) != 0) {
+ d_printf("LM Key does not match expectations!\n");
+ d_printf("lm_key:\n");
+ dump_data(1, lm_key, 8);
+ d_printf("expected:\n");
+ dump_data(1, lm_hash, 8);
+ pass = false;
+ }
+#if 0
+ } else {
+ if (memcmp(session_key.data, lm_key,
+ sizeof(lm_key)) != 0) {
+ d_printf("LM Key does not match expectations (first 8 session key)!\n");
+ d_printf("lm_key:\n");
+ dump_data(1, lm_key, 8);
+ d_printf("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) {
+ d_printf("NT Session Key does not match expectations (should be first-8 LM hash)!\n");
+ d_printf("user_session_key:\n");
+ dump_data(1, user_session_key, sizeof(user_session_key));
+ d_printf("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)) {
+ SAFE_FREE(*error_string);
+ asprintf(error_string, "Expected error: %s, got %s", nt_errstr(samlogon_state->expected_error), nt_errstr(nt_status));
+ 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 (memcmp(lm_hash, lm_key,
+ sizeof(lm_key)) != 0) {
+ d_printf("LM Key does not match expectations!\n");
+ d_printf("lm_key:\n");
+ dump_data(1, lm_key, 8);
+ d_printf("expected:\n");
+ dump_data(1, lm_hash, 8);
+ pass = false;
+ }
+ if (memcmp(session_key.data, user_session_key,
+ sizeof(user_session_key)) != 0) {
+ d_printf("NT Session Key does not match expectations!\n");
+ d_printf("user_session_key:\n");
+ dump_data(1, user_session_key, 16);
+ d_printf("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)) {
+ SAFE_FREE(*error_string);
+ asprintf(error_string, "Expected error: %s, got %s", nt_errstr(samlogon_state->expected_error), nt_errstr(nt_status));
+ 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) {
+ d_printf("USER (LMv2) Session Key does not match expectations!\n");
+ d_printf("user_session_key:\n");
+ dump_data(1, user_session_key, 16);
+ d_printf("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) {
+ d_printf("LM (LMv2) Session Key does not match expectations!\n");
+ d_printf("lm_session_key:\n");
+ dump_data(1, lm_session_key, 8);
+ d_printf("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) {
+ d_printf("USER (NTLMv2) Session Key expected, got LMv2 sessesion key instead:\n");
+ d_printf("user_session_key:\n");
+ dump_data(1, user_session_key, 16);
+ d_printf("expected:\n");
+ dump_data(1, ntlmv2_session_key.data, ntlmv2_session_key.length);
+ pass = false;
+
+ } else {
+ d_printf("USER (NTLMv2) Session Key does not match expectations!\n");
+ d_printf("user_session_key:\n");
+ dump_data(1, user_session_key, 16);
+ d_printf("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) {
+ d_printf("LM (NTLMv2) Session Key expected, got LMv2 sessesion key instead:\n");
+ d_printf("user_session_key:\n");
+ dump_data(1, lm_session_key, 8);
+ d_printf("expected:\n");
+ dump_data(1, ntlmv2_session_key.data, 8);
+ pass = false;
+ } else {
+ d_printf("LM (NTLMv2) Session Key does not match expectations!\n");
+ d_printf("lm_session_key:\n");
+ dump_data(1, lm_session_key, 8);
+ d_printf("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)) {
+ SAFE_FREE(*error_string);
+ asprintf(error_string, "Expected error: %s, got %s", nt_errstr(samlogon_state->expected_error), nt_errstr(nt_status));
+ 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) {
+ d_printf("USER (LMv2) Session Key does not match expectations!\n");
+ d_printf("user_session_key:\n");
+ dump_data(1, user_session_key, 16);
+ d_printf("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) {
+ d_printf("LM (LMv2) Session Key does not match expectations!\n");
+ d_printf("lm_session_key:\n");
+ dump_data(1, lm_session_key, 8);
+ d_printf("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) {
+ d_printf("USER (NTLMv2) Session Key does not match expectations!\n");
+ d_printf("user_session_key:\n");
+ dump_data(1, user_session_key, 16);
+ d_printf("expected:\n");
+ dump_data(1, ntlm_session_key.data, ntlm_session_key.length);
+ pass = false;
+ }
+ if (lm_good) {
+ if (memcmp(lm_hash, lm_session_key,
+ sizeof(lm_session_key)) != 0) {
+ d_printf("LM Session Key does not match expectations!\n");
+ d_printf("lm_session_key:\n");
+ dump_data(1, lm_session_key, 8);
+ d_printf("expected:\n");
+ dump_data(1, lm_hash, 8);
+ pass = false;
+ }
+ } else {
+ static const uint8_t zeros[8];
+ if (memcmp(zeros, lm_session_key,
+ sizeof(lm_session_key)) != 0) {
+ d_printf("LM Session Key does not match expectations (zeros)!\n");
+ d_printf("lm_session_key:\n");
+ dump_data(1, lm_session_key, 8);
+ d_printf("expected:\n");
+ dump_data(1, zeros, 8);
+ pass = false;
+ }
+ }
+ break;
+ default:
+ if (memcmp(ntlm_session_key.data, user_session_key,
+ sizeof(user_session_key)) != 0) {
+ d_printf("USER (NTLMv2) Session Key does not match expectations!\n");
+ d_printf("user_session_key:\n");
+ dump_data(1, user_session_key, 16);
+ d_printf("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) {
+ d_printf("LM (NTLMv2) Session Key does not match expectations!\n");
+ d_printf("lm_session_key:\n");
+ dump_data(1, lm_session_key, 8);
+ d_printf("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 feild)
+ *
+ * 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);
+
+ bool lm_good;
+ 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];
+
+ struct MD5Context md5_session_nonce_ctx;
+ HMACMD5Context hmac_ctx;
+
+ ZERO_STRUCT(user_session_key);
+ ZERO_STRUCT(lm_key);
+ generate_random_buffer(client_chall, 8);
+
+ MD5Init(&md5_session_nonce_ctx);
+ MD5Update(&md5_session_nonce_ctx, samlogon_state->chall.data, 8);
+ MD5Update(&md5_session_nonce_ctx, client_chall, 8);
+ MD5Final(session_nonce_hash, &md5_session_nonce_ctx);
+
+ E_md4hash(samlogon_state->password, (uint8_t *)nt_hash);
+ lm_good = 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);
+
+ hmac_md5_init_rfc2104(nt_key, 16, &hmac_ctx);
+ hmac_md5_update(samlogon_state->chall.data, 8, &hmac_ctx);
+ hmac_md5_update(client_chall, 8, &hmac_ctx);
+ hmac_md5_final(expected_user_session_key, &hmac_ctx);
+
+ 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)) {
+ SAFE_FREE(*error_string);
+ asprintf(error_string, "Expected error: %s, got %s", nt_errstr(samlogon_state->expected_error), nt_errstr(nt_status));
+ 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 (lm_good) {
+ if (memcmp(lm_hash, lm_key,
+ sizeof(lm_key)) != 0) {
+ d_printf("LM Key does not match expectations!\n");
+ d_printf("lm_key:\n");
+ dump_data(1, lm_key, 8);
+ d_printf("expected:\n");
+ dump_data(1, lm_hash, 8);
+ pass = false;
+ }
+ } else {
+ static const uint8_t zeros[8];
+ if (memcmp(zeros, lm_key,
+ sizeof(lm_key)) != 0) {
+ d_printf("LM Session Key does not match expectations (zeros)!\n");
+ d_printf("lm_key:\n");
+ dump_data(1, lm_key, 8);
+ d_printf("expected:\n");
+ dump_data(1, zeros, 8);
+ pass = false;
+ }
+ }
+ if (memcmp(nt_key, user_session_key, 16) != 0) {
+ d_printf("NT Session Key does not match expectations (should be NT Key)!\n");
+ d_printf("user_session_key:\n");
+ dump_data(1, user_session_key, sizeof(user_session_key));
+ d_printf("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;
+
+ uint8_t user_session_key[16];
+ uint8_t lm_key[16];
+ uint8_t lm_hash[16];
+ static const uint8_t zeros[8];
+ DATA_BLOB chall = data_blob_talloc(samlogon_state->mem_ctx, zeros, sizeof(zeros));
+ 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, NULL)) {
+ 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_convenience(samlogon_state->mem_ctx,
+ samlogon_state->iconv_convenience,
+ CH_UNIX, CH_DOS,
+ password, strlen(password)+1,
+ (void**)&dospw, NULL, false)) {
+ 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;
+ }
+ 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)) {
+ SAFE_FREE(*error_string);
+ asprintf(error_string, "Expected error: %s, got %s", nt_errstr(samlogon_state->expected_error), nt_errstr(nt_status));
+ 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;
+ }
+
+ 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},
+ {NULL, NULL}
+};
+
+/*
+ try a netlogon SamLogon
+*/
+static bool test_SamLogon(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct torture_context *tctx,
+ struct 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[] = { 2, 6 };
+ 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 = 0;
+ uint32_t flags = 0;
+
+ ZERO_STRUCT(logon);
+
+ d_printf("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 = lp_workgroup(tctx->lp_ctx);
+ samlogon_state.netbios_name = lp_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.iconv_convenience = lp_iconv_convenience(tctx->lp_ctx);
+
+ 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<ARRAY_SIZE(function_levels);f++) {
+ for (i=0; test_table[i].fn; i++) {
+ if (n_subtests && (i > n_subtests)) {
+ continue;
+ }
+ for (v=0;v<ARRAY_SIZE(validation_levels);v++) {
+ for (l=0;l<ARRAY_SIZE(logon_levels);l++) {
+ char *error_string = NULL;
+ TALLOC_CTX *tmp_ctx = talloc_named(fn_ctx, 0, "test_SamLogon inner loop");
+ samlogon_state.mem_ctx = tmp_ctx;
+ samlogon_state.function_level = function_levels[f];
+ samlogon_state.r.in.validation_level = validation_levels[v];
+ samlogon_state.r.in.logon_level = logon_levels[l];
+ samlogon_state.r_ex.in.validation_level = validation_levels[v];
+ samlogon_state.r_ex.in.logon_level = logon_levels[l];
+ samlogon_state.r_flags.in.validation_level = validation_levels[v];
+ samlogon_state.r_flags.in.logon_level = logon_levels[l];
+ if (!test_table[i].fn(&samlogon_state, &error_string)) {
+ d_printf("Testing '%s' [%s]\\[%s] '%s' at validation level %d, logon level %d, function %d: \n",
+ samlogon_state.comment,
+ samlogon_state.account_domain,
+ samlogon_state.account_name,
+ test_table[i].name, validation_levels[v],
+ logon_levels[l], function_levels[f]);
+
+ if (test_table[i].expect_fail) {
+ d_printf(" failed (expected, test incomplete): %s\n", error_string);
+ } else {
+ d_printf(" failed: %s\n", error_string);
+ ret = false;
+ }
+ SAFE_FREE(error_string);
+ }
+ talloc_free(tmp_ctx);
+ }
+ }
+ }
+ }
+ talloc_free(fn_ctx);
+ return ret;
+}
+
+/*
+ test an ADS style interactive domain logon
+*/
+bool test_InteractiveLogon(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct creds_CredentialState *creds,
+ const char *comment,
+ const char *workstation_name,
+ const char *account_domain, const char *account_name,
+ const char *plain_pass, uint32_t parameter_control,
+ NTSTATUS expected_error)
+{
+ NTSTATUS status;
+ TALLOC_CTX *fn_ctx = talloc_named(mem_ctx, 0, "test_InteractiveLogon function-level context");
+ struct netr_LogonSamLogonWithFlags r;
+ struct netr_Authenticator a, ra;
+ struct netr_PasswordInfo pinfo;
+ uint32_t flags = 0;
+
+ union netr_LogonLevel logon;
+ union netr_Validation validation;
+ uint8_t authoritative = 0;
+
+ ZERO_STRUCT(a);
+ ZERO_STRUCT(r);
+ ZERO_STRUCT(ra);
+
+ ZERO_STRUCT(logon);
+ ZERO_STRUCT(validation);
+
+ 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 = 5;
+ 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_low = 0;
+ pinfo.identity_info.logon_id_high = 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) {
+ creds_arcfour_crypt(creds, pinfo.lmpassword.hash, 16);
+ creds_arcfour_crypt(creds, pinfo.ntpassword.hash, 16);
+ } else {
+ creds_des_encrypt(creds, &pinfo.lmpassword);
+ creds_des_encrypt(creds, &pinfo.ntpassword);
+ }
+
+ d_printf("Testing netr_LogonSamLogonWithFlags '%s' (Interactive Logon)\n", comment);
+
+ status = dcerpc_netr_LogonSamLogonWithFlags(p, fn_ctx, &r);
+ if (!r.out.return_authenticator
+ || !creds_client_check(creds, &r.out.return_authenticator->cred)) {
+ d_printf("Credential chaining failed\n");
+ talloc_free(fn_ctx);
+ return false;
+ }
+
+ talloc_free(fn_ctx);
+
+ if (!NT_STATUS_EQUAL(expected_error, status)) {
+ d_printf("[%s]\\[%s] netr_LogonSamLogonWithFlags - expected %s got %s\n",
+ account_domain, account_name, nt_errstr(expected_error), nt_errstr(status));
+ return false;
+ }
+
+ 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;
+ char *user_password, *user_password_wrong_wks, *user_password_wrong_time;
+ const char *old_user_password;
+ char *test_machine_account;
+ 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 creds_CredentialState *creds;
+
+ test_machine_account = talloc_asprintf(mem_ctx, "%s$", TEST_MACHINE_NAME);
+ /* 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);
+ if (!join_ctx) {
+ d_printf("Failed to join as Workstation\n");
+ return false;
+ }
+
+ userdomain = torture_setting_string(torture, "userdomain", lp_workgroup(torture->lp_ctx));
+
+ user_ctx = torture_create_testuser(torture,
+ TEST_USER_NAME,
+ userdomain,
+ ACB_NORMAL,
+ (const char **)&user_password);
+ if (!user_ctx) {
+ d_printf("Failed to create a test user\n");
+ return false;
+ }
+
+ old_user_password = user_password;
+
+ test_ChangePasswordUser3(torture_join_samr_pipe(user_ctx), 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,
+ (const char **)&user_password_wrong_wks);
+ if (!user_ctx_wrong_wks) {
+ d_printf("Failed to create a test user (wrong workstation test)\n");
+ return false;
+ }
+
+ 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;
+
+ status = dcerpc_samr_SetUserInfo(torture_join_samr_pipe(user_ctx_wrong_wks), mem_ctx, &s);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetUserInfo (list of workstations) failed - %s\n", nt_errstr(status));
+ ret = false;
+ goto failed;
+ }
+
+ user_ctx_wrong_time
+ = torture_create_testuser(torture, TEST_USER_NAME_WRONG_TIME,
+ userdomain,
+ ACB_NORMAL,
+ (const char **)&user_password_wrong_time);
+ if (!user_ctx_wrong_time) {
+ d_printf("Failed to create a test user (wrong workstation test)\n");
+ return false;
+ }
+
+ 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);
+
+ status = dcerpc_samr_SetUserInfo(torture_join_samr_pipe(user_ctx_wrong_time), mem_ctx, &s);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetUserInfo (logon times and list of workstations) failed - %s\n", nt_errstr(status));
+ ret = false;
+ goto failed;
+ }
+
+ status = torture_rpc_binding(torture, &b);
+ if (!NT_STATUS_IS_OK(status)) {
+ ret = false;
+ goto failed;
+ }
+
+ /* We have to use schannel, otherwise the SamLogonEx fails
+ * with INTERNAL_ERROR */
+
+ b->flags &= ~DCERPC_AUTH_OPTIONS;
+ b->flags |= DCERPC_SCHANNEL | DCERPC_SIGN | DCERPC_SCHANNEL_128;
+
+ status = dcerpc_pipe_connect_b(mem_ctx, &p, b,
+ &ndr_table_netlogon,
+ machine_credentials, torture->ev, torture->lp_ctx);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("RPC pipe connect as domain member failed: %s\n", nt_errstr(status));
+ ret = false;
+ goto failed;
+ }
+
+ status = dcerpc_schannel_creds(p->conn->security_state.generic_state, mem_ctx, &creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ ret = false;
+ goto failed;
+ }
+
+ {
+
+ 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(cmdline_credentials),
+ .username = cli_credentials_get_username(cmdline_credentials),
+ .password = cli_credentials_get_password(cmdline_credentials),
+ .network_login = true,
+ .expected_interactive_error = NT_STATUS_OK,
+ .expected_network_error = NT_STATUS_OK
+ },
+ {
+ .comment = "realm\\user",
+ .domain = cli_credentials_get_realm(cmdline_credentials),
+ .username = cli_credentials_get_username(cmdline_credentials),
+ .password = cli_credentials_get_password(cmdline_credentials),
+ .network_login = true,
+ .expected_interactive_error = NT_STATUS_OK,
+ .expected_network_error = NT_STATUS_OK
+ },
+ {
+ .comment = "user@domain",
+ .domain = NULL,
+ .username = talloc_asprintf(mem_ctx,
+ "%s@%s",
+ cli_credentials_get_username(cmdline_credentials),
+ cli_credentials_get_domain(cmdline_credentials)
+ ),
+ .password = cli_credentials_get_password(cmdline_credentials),
+ .network_login = false, /* works for some things, but not NTLMv2. Odd */
+ .expected_interactive_error = NT_STATUS_OK,
+ .expected_network_error = NT_STATUS_OK
+ },
+ {
+ .comment = "user@realm",
+ .domain = NULL,
+ .username = talloc_asprintf(mem_ctx,
+ "%s@%s",
+ cli_credentials_get_username(cmdline_credentials),
+ cli_credentials_get_realm(cmdline_credentials)
+ ),
+ .password = cli_credentials_get_password(cmdline_credentials),
+ .network_login = true,
+ .expected_interactive_error = NT_STATUS_OK,
+ .expected_network_error = NT_STATUS_OK
+ },
+ {
+ .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
+ },
+ {
+ .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
+ },
+ {
+ .comment = "test user (long pw): user@realm",
+ .domain = NULL,
+ .username = talloc_asprintf(mem_ctx,
+ "%s@%s",
+ TEST_USER_NAME,
+ lp_realm(torture->lp_ctx)),
+ .password = user_password,
+ .network_login = true,
+ .expected_interactive_error = NT_STATUS_OK,
+ .expected_network_error = NT_STATUS_OK
+ },
+ {
+ .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
+ },
+ /* 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 (wong 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
+ }
+ };
+
+ /* Try all the tests for different username forms */
+ for (ci = 0; ci < ARRAY_SIZE(usercreds); ci++) {
+
+ if (!test_InteractiveLogon(p, mem_ctx, 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 = false;
+ }
+
+ if (usercreds[ci].network_login) {
+ if (!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 = false;
+ }
+ }
+ }
+
+ /* 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! */
+
+ if (!test_InteractiveLogon(p, mem_ctx, 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 = false;
+ }
+
+ if (usercreds[0].network_login) {
+ if (!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 = false;
+ }
+ }
+ }
+
+ }
+failed:
+ 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 0000000000..d7d6773e7a
--- /dev/null
+++ b/source4/torture/rpc/samr.c
@@ -0,0 +1,6861 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for samr rpc operations
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003
+ Copyright (C) Guenther Deschner 2008,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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "system/time.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/rpc.h"
+#include "param/param.h"
+
+#include <unistd.h>
+
+#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$"
+
+enum torture_samr_choice {
+ TORTURE_SAMR_PASSWORDS,
+ TORTURE_SAMR_PASSWORDS_PWDLASTSET,
+ TORTURE_SAMR_USER_ATTRIBUTES,
+ TORTURE_SAMR_USER_PRIVILEGES,
+ TORTURE_SAMR_OTHER,
+ TORTURE_SAMR_MANY_ACCOUNTS,
+ TORTURE_SAMR_MANY_GROUPS,
+ TORTURE_SAMR_MANY_ALIASES
+};
+
+static bool test_QueryUserInfo(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle);
+
+static bool test_QueryUserInfo2(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle);
+
+static bool test_QueryAliasInfo(struct dcerpc_pipe *p,
+ 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_pipe *p, struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct samr_Close r;
+
+ r.in.handle = handle;
+ r.out.handle = handle;
+
+ status = dcerpc_samr_Close(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "Close");
+
+ return true;
+}
+
+static bool test_Shutdown(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ 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");
+
+ status = dcerpc_samr_Shutdown(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "samr_Shutdown");
+
+ return true;
+}
+
+static bool test_SetDsrmPassword(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ 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");
+
+ status = dcerpc_samr_SetDsrmPassword(p, tctx, &r);
+ torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_SUPPORTED, "samr_SetDsrmPassword");
+
+ return true;
+}
+
+
+static bool test_QuerySecurity(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ 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;
+
+ status = dcerpc_samr_QuerySecurity(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "QuerySecurity");
+
+ 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");
+ }
+
+ status = dcerpc_samr_SetSecurity(p, tctx, &s);
+ torture_assert_ntstatus_ok(tctx, status, "SetSecurity");
+
+ status = dcerpc_samr_QuerySecurity(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "QuerySecurity");
+
+ return true;
+}
+
+
+static bool test_SetUserInfo(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *handle, uint32_t base_acct_flags,
+ const char *base_account_name)
+{
+ NTSTATUS status;
+ 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) \
+ status = dcerpc_samr_ ##call(p, 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; \
+ }
+
+#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 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);
+
+ /* Samba 3 cannot store comment fields atm. - gd */
+ if (!torture_setting_bool(tctx, "samba3", false)) {
+ 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, base_account_name, 0);
+ test_account_name = talloc_asprintf(tctx, "%sxx7-3", base_account_name);
+ TEST_USERINFO_STRING(7, account_name, 3, account_name, base_account_name, 0);
+ test_account_name = talloc_asprintf(tctx, "%sxx7-5", base_account_name);
+ TEST_USERINFO_STRING(7, account_name, 5, account_name, base_account_name, 0);
+ test_account_name = talloc_asprintf(tctx, "%sxx7-6", base_account_name);
+ TEST_USERINFO_STRING(7, account_name, 6, account_name, base_account_name, 0);
+ test_account_name = talloc_asprintf(tctx, "%sxx7-7", base_account_name);
+ TEST_USERINFO_STRING(7, account_name, 7, account_name, base_account_name, 0);
+ test_account_name = talloc_asprintf(tctx, "%sxx7-21", base_account_name);
+ TEST_USERINFO_STRING(7, account_name, 21, account_name, base_account_name, 0);
+ test_account_name = base_account_name;
+ TEST_USERINFO_STRING(21, account_name, 21, account_name, base_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 copy_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) + (random() % 6);
+ char *s = generate_random_str(mem_ctx, len);
+ 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_str(mem_ctx, 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 samr_GetUserPwInfo pwp;
+ struct samr_PwInfo info;
+ int policy_min_pw_len = 0;
+ pwp.in.user_handle = handle;
+ pwp.out.info = &info;
+
+ status = dcerpc_samr_GetUserPwInfo(p, tctx, &pwp);
+ if (NT_STATUS_IS_OK(status)) {
+ 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;
+
+ encode_pw_buffer(u.info24.password.data, newpass, STR_UNICODE);
+ u.info24.password_expired = 0;
+
+ status = dcerpc_fetch_session_key(p, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetUserInfo level %u - no session key - %s\n",
+ s.in.level, nt_errstr(status));
+ return false;
+ }
+
+ arcfour_crypt_blob(u.info24.password.data, 516, &session_key);
+
+ torture_comment(tctx, "Testing SetUserInfo level 24 (set password)\n");
+
+ status = dcerpc_samr_SetUserInfo(p, tctx, &s);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetUserInfo level %u failed - %s\n",
+ s.in.level, nt_errstr(status));
+ 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;
+ 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;
+
+ status = dcerpc_samr_GetUserPwInfo(p, tctx, &pwp);
+ if (NT_STATUS_IS_OK(status)) {
+ 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;
+
+ encode_pw_buffer(u.info23.password.data, newpass, STR_UNICODE);
+
+ status = dcerpc_fetch_session_key(p, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetUserInfo level %u - no session key - %s\n",
+ s.in.level, nt_errstr(status));
+ return false;
+ }
+
+ arcfour_crypt_blob(u.info23.password.data, 516, &session_key);
+
+ torture_comment(tctx, "Testing SetUserInfo level 23 (set password)\n");
+
+ status = dcerpc_samr_SetUserInfo(p, tctx, &s);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetUserInfo level %u failed - %s\n",
+ s.in.level, nt_errstr(status));
+ ret = false;
+ } else {
+ *password = newpass;
+ }
+
+ encode_pw_buffer(u.info23.password.data, newpass, STR_UNICODE);
+
+ status = dcerpc_fetch_session_key(p, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetUserInfo level %u - no session key - %s\n",
+ s.in.level, nt_errstr(status));
+ return false;
+ }
+
+ /* This should break the key nicely */
+ session_key.length--;
+ arcfour_crypt_blob(u.info23.password.data, 516, &session_key);
+
+ torture_comment(tctx, "Testing SetUserInfo level 23 (set password) with wrong password\n");
+
+ status = dcerpc_samr_SetUserInfo(p, tctx, &s);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
+ printf("SetUserInfo level %u should have failed with WRONG_PASSWORD- %s\n",
+ s.in.level, nt_errstr(status));
+ ret = false;
+ }
+
+ 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;
+ DATA_BLOB confounded_session_key = data_blob_talloc(tctx, NULL, 16);
+ uint8_t confounder[16];
+ char *newpass;
+ struct MD5Context ctx;
+ struct samr_GetUserPwInfo pwp;
+ struct samr_PwInfo info;
+ int policy_min_pw_len = 0;
+ pwp.in.user_handle = handle;
+ pwp.out.info = &info;
+
+ status = dcerpc_samr_GetUserPwInfo(p, tctx, &pwp);
+ if (NT_STATUS_IS_OK(status)) {
+ 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;
+
+ encode_pw_buffer(u.info26.password.data, newpass, STR_UNICODE);
+ u.info26.password_expired = 0;
+
+ status = dcerpc_fetch_session_key(p, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetUserInfo level %u - no session key - %s\n",
+ s.in.level, nt_errstr(status));
+ return false;
+ }
+
+ generate_random_buffer((uint8_t *)confounder, 16);
+
+ MD5Init(&ctx);
+ MD5Update(&ctx, confounder, 16);
+ MD5Update(&ctx, session_key.data, session_key.length);
+ MD5Final(confounded_session_key.data, &ctx);
+
+ arcfour_crypt_blob(u.info26.password.data, 516, &confounded_session_key);
+ memcpy(&u.info26.password.data[516], confounder, 16);
+
+ torture_comment(tctx, "Testing SetUserInfo level 26 (set password ex)\n");
+
+ status = dcerpc_samr_SetUserInfo(p, tctx, &s);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetUserInfo level %u failed - %s\n",
+ s.in.level, nt_errstr(status));
+ ret = false;
+ } else {
+ *password = newpass;
+ }
+
+ /* This should break the key nicely */
+ confounded_session_key.data[0]++;
+
+ arcfour_crypt_blob(u.info26.password.data, 516, &confounded_session_key);
+ memcpy(&u.info26.password.data[516], confounder, 16);
+
+ torture_comment(tctx, "Testing SetUserInfo level 26 (set password ex) with wrong session key\n");
+
+ status = dcerpc_samr_SetUserInfo(p, tctx, &s);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
+ printf("SetUserInfo level %u should have failed with WRONG_PASSWORD: %s\n",
+ s.in.level, nt_errstr(status));
+ 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;
+ DATA_BLOB confounded_session_key = data_blob_talloc(tctx, NULL, 16);
+ struct MD5Context ctx;
+ uint8_t confounder[16];
+ 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;
+
+ status = dcerpc_samr_GetUserPwInfo(p, tctx, &pwp);
+ if (NT_STATUS_IS_OK(status)) {
+ 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;
+
+ encode_pw_buffer(u.info25.password.data, newpass, STR_UNICODE);
+
+ status = dcerpc_fetch_session_key(p, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetUserInfo level %u - no session key - %s\n",
+ s.in.level, nt_errstr(status));
+ return false;
+ }
+
+ generate_random_buffer((uint8_t *)confounder, 16);
+
+ MD5Init(&ctx);
+ MD5Update(&ctx, confounder, 16);
+ MD5Update(&ctx, session_key.data, session_key.length);
+ MD5Final(confounded_session_key.data, &ctx);
+
+ arcfour_crypt_blob(u.info25.password.data, 516, &confounded_session_key);
+ memcpy(&u.info25.password.data[516], confounder, 16);
+
+ torture_comment(tctx, "Testing SetUserInfo level 25 (set password ex)\n");
+
+ status = dcerpc_samr_SetUserInfo(p, tctx, &s);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetUserInfo level %u failed - %s\n",
+ s.in.level, nt_errstr(status));
+ ret = false;
+ } else {
+ *password = newpass;
+ }
+
+ /* This should break the key nicely */
+ confounded_session_key.data[0]++;
+
+ arcfour_crypt_blob(u.info25.password.data, 516, &confounded_session_key);
+ memcpy(&u.info25.password.data[516], confounder, 16);
+
+ torture_comment(tctx, "Testing SetUserInfo level 25 (set password ex) with wrong session key\n");
+
+ status = dcerpc_samr_SetUserInfo(p, tctx, &s);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
+ printf("SetUserInfo level %u should have failed with WRONG_PASSWORD- %s\n",
+ s.in.level, nt_errstr(status));
+ 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 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;
+
+ status = dcerpc_samr_GetUserPwInfo(p, tctx, &pwp);
+ if (NT_STATUS_IS_OK(status)) {
+ 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)) {
+ printf("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, true);
+ 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, true);
+ memcpy(u.info18.lm_pwd.hash, out.data, out.length);
+ }
+
+ torture_comment(tctx, "Testing SetUserInfo level 18 (set password hash)\n");
+
+ status = dcerpc_samr_SetUserInfo(p, tctx, &s);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetUserInfo level %u failed - %s\n",
+ s.in.level, nt_errstr(status));
+ 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 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;
+
+ status = dcerpc_samr_GetUserPwInfo(p, tctx, &pwp);
+ if (NT_STATUS_IS_OK(status)) {
+ 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)) {
+ printf("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, true);
+ 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, true);
+ u.info21.nt_owf_password.array = (uint16_t *)out.data;
+ }
+
+ torture_comment(tctx, "Testing SetUserInfo level 21 (set password hash)\n");
+
+ status = dcerpc_samr_SetUserInfo(p, tctx, &s);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetUserInfo level %u failed - %s\n",
+ s.in.level, nt_errstr(status));
+ ret = false;
+ } else {
+ *password = newpass;
+ }
+
+ /* try invalid length */
+ if (fields_present & SAMR_FIELD_NT_PASSWORD_PRESENT) {
+
+ u.info21.nt_owf_password.length++;
+
+ status = dcerpc_samr_SetUserInfo(p, tctx, &s);
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
+ printf("SetUserInfo level %u should have failed with NT_STATUS_INVALID_PARAMETER - %s\n",
+ s.in.level, nt_errstr(status));
+ ret = false;
+ }
+ }
+
+ if (fields_present & SAMR_FIELD_LM_PASSWORD_PRESENT) {
+
+ u.info21.lm_owf_password.length++;
+
+ status = dcerpc_samr_SetUserInfo(p, tctx, &s);
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
+ printf("SetUserInfo level %u should have failed with NT_STATUS_INVALID_PARAMETER - %s\n",
+ s.in.level, nt_errstr(status));
+ 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;
+ DATA_BLOB confounded_session_key = data_blob_talloc(tctx, NULL, 16);
+ struct MD5Context ctx;
+ uint8_t confounder[16];
+ char *newpass;
+ 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;
+
+ status = dcerpc_samr_GetUserPwInfo(p, tctx, &pwp);
+ if (NT_STATUS_IS_OK(status)) {
+ 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", 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;
+
+ encode_pw_buffer(u.info23.password.data, newpass, STR_UNICODE);
+
+ break;
+ case 24:
+ u.info24.password_expired = password_expired;
+
+ encode_pw_buffer(u.info24.password.data, newpass, STR_UNICODE);
+
+ break;
+ case 25:
+ u.info25.info.fields_present = fields_present;
+ u.info25.info.password_expired = password_expired;
+ u.info25.info.comment.string = comment;
+
+ encode_pw_buffer(u.info25.password.data, newpass, STR_UNICODE);
+
+ break;
+ case 26:
+ u.info26.password_expired = password_expired;
+
+ encode_pw_buffer(u.info26.password.data, newpass, STR_UNICODE);
+
+ break;
+ }
+
+ status = dcerpc_fetch_session_key(p, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetUserInfo level %u - no session key - %s\n",
+ s.in.level, nt_errstr(status));
+ return false;
+ }
+
+ generate_random_buffer((uint8_t *)confounder, 16);
+
+ MD5Init(&ctx);
+ MD5Update(&ctx, confounder, 16);
+ MD5Update(&ctx, session_key.data, session_key.length);
+ MD5Final(confounded_session_key.data, &ctx);
+
+ 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, true);
+ 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, true);
+ 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, true);
+ 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, true);
+ u.info21.nt_owf_password.array = (uint16_t *)out.data;
+ }
+ break;
+ case 23:
+ arcfour_crypt_blob(u.info23.password.data, 516, &session_key);
+ break;
+ case 24:
+ arcfour_crypt_blob(u.info24.password.data, 516, &session_key);
+ break;
+ case 25:
+ arcfour_crypt_blob(u.info25.password.data, 516, &confounded_session_key);
+ memcpy(&u.info25.password.data[516], confounder, 16);
+ break;
+ case 26:
+ arcfour_crypt_blob(u.info26.password.data, 516, &confounded_session_key);
+ memcpy(&u.info26.password.data[516], confounder, 16);
+ break;
+ }
+
+ if (use_setinfo2) {
+ status = dcerpc_samr_SetUserInfo2(p, tctx, &s2);
+ } else {
+ status = dcerpc_samr_SetUserInfo(p, tctx, &s);
+ }
+
+ 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)) {
+ printf("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_pipe *p, struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ 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;i<ARRAY_SIZE(levels);i++) {
+ torture_comment(tctx, "Testing SetAliasInfo level %u\n", levels[i]);
+
+ r.in.alias_handle = handle;
+ r.in.level = levels[i];
+ r.in.info = talloc(tctx, union samr_AliasInfo);
+ switch (r.in.level) {
+ case ALIASINFONAME: init_lsa_String(&r.in.info->name,TEST_ALIASNAME); break;
+ case ALIASINFODESCRIPTION: init_lsa_String(&r.in.info->description,
+ "Test Description, should test I18N as well"); break;
+ case ALIASINFOALL: printf("ALIASINFOALL ignored\n"); break;
+ }
+
+ status = dcerpc_samr_SetAliasInfo(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetAliasInfo level %u failed - %s\n",
+ levels[i], nt_errstr(status));
+ ret = false;
+ }
+
+ q.in.alias_handle = handle;
+ q.in.level = levels[i];
+ q.out.info = &info;
+
+ status = dcerpc_samr_QueryAliasInfo(p, tctx, &q);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QueryAliasInfo level %u failed - %s\n",
+ levels[i], nt_errstr(status));
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_GetGroupsForUser(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *user_handle)
+{
+ struct samr_GetGroupsForUser r;
+ struct samr_RidWithAttributeArray *rids = NULL;
+ NTSTATUS status;
+
+ torture_comment(tctx, "testing GetGroupsForUser\n");
+
+ r.in.user_handle = user_handle;
+ r.out.rids = &rids;
+
+ status = dcerpc_samr_GetGroupsForUser(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "GetGroupsForUser");
+
+ return true;
+
+}
+
+static bool test_GetDomPwInfo(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct lsa_String *domain_name)
+{
+ NTSTATUS status;
+ struct samr_GetDomPwInfo r;
+ struct samr_PwInfo info;
+
+ r.in.domain_name = domain_name;
+ r.out.info = &info;
+
+ torture_comment(tctx, "Testing GetDomPwInfo with name %s\n", r.in.domain_name->string);
+
+ status = dcerpc_samr_GetDomPwInfo(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "GetDomPwInfo");
+
+ 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);
+
+ status = dcerpc_samr_GetDomPwInfo(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "GetDomPwInfo");
+
+ r.in.domain_name->string = "\\\\__NONAME__";
+ torture_comment(tctx, "Testing GetDomPwInfo with name %s\n", r.in.domain_name->string);
+
+ status = dcerpc_samr_GetDomPwInfo(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "GetDomPwInfo");
+
+ r.in.domain_name->string = "\\\\Builtin";
+ torture_comment(tctx, "Testing GetDomPwInfo with name %s\n", r.in.domain_name->string);
+
+ status = dcerpc_samr_GetDomPwInfo(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "GetDomPwInfo");
+
+ return true;
+}
+
+static bool test_GetUserPwInfo(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct samr_GetUserPwInfo r;
+ struct samr_PwInfo info;
+
+ torture_comment(tctx, "Testing GetUserPwInfo\n");
+
+ r.in.user_handle = handle;
+ r.out.info = &info;
+
+ status = dcerpc_samr_GetUserPwInfo(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "GetUserPwInfo");
+
+ return true;
+}
+
+static NTSTATUS test_LookupName(struct dcerpc_pipe *p, 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(p, tctx, &n);
+ if (NT_STATUS_IS_OK(status)) {
+ *rid = n.out.rids->ids[0];
+ } else {
+ return status;
+ }
+
+ init_lsa_String(&sname[1], "xxNONAMExx");
+ n.in.num_names = 2;
+ status = dcerpc_samr_LookupNames(p, tctx, &n);
+ if (!NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED)) {
+ printf("LookupNames[2] failed - %s\n", nt_errstr(status));
+ if (NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ return status;
+ }
+
+ n.in.num_names = 0;
+ status = dcerpc_samr_LookupNames(p, tctx, &n);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("LookupNames[0] failed - %s\n", nt_errstr(status));
+ return status;
+ }
+
+ init_lsa_String(&sname[0], "xxNONAMExx");
+ n.in.num_names = 1;
+ status = dcerpc_samr_LookupNames(p, tctx, &n);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
+ printf("LookupNames[1 bad name] failed - %s\n", nt_errstr(status));
+ if (NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ return status;
+ }
+
+ init_lsa_String(&sname[0], "xxNONAMExx");
+ init_lsa_String(&sname[1], "xxNONAME2xx");
+ n.in.num_names = 2;
+ status = dcerpc_samr_LookupNames(p, tctx, &n);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
+ printf("LookupNames[2 bad names] failed - %s\n", nt_errstr(status));
+ if (NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS test_OpenUser_byname(struct dcerpc_pipe *p,
+ 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(p, 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(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("OpenUser_byname(%s -> %d) failed - %s\n", name, rid, nt_errstr(status));
+ }
+
+ return status;
+}
+
+#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;
+ }
+
+ printf("Testing ChangePasswordUser for user 'testuser'\n");
+
+ printf("old password: %s\n", oldpass);
+ printf("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;
+
+ status = dcerpc_samr_ChangePasswordUser(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("ChangePasswordUser failed - %s\n", nt_errstr(status));
+ ret = false;
+ }
+
+ if (!test_samr_handle_Close(p, tctx, &user_handle)) {
+ ret = false;
+ }
+
+ return ret;
+}
+#endif
+
+static bool test_ChangePasswordUser(struct dcerpc_pipe *p, 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(p, tctx, handle, acct_name, &user_handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+ pwp.in.user_handle = &user_handle;
+ pwp.out.info = &info;
+
+ status = dcerpc_samr_GetUserPwInfo(p, tctx, &pwp);
+ if (NT_STATUS_IS_OK(status)) {
+ 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 LM hash */
+ hash1.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;
+
+ status = dcerpc_samr_ChangePasswordUser(p, tctx, &r);
+ torture_assert_ntstatus_equal(tctx, status, NT_STATUS_WRONG_PASSWORD,
+ "ChangePasswordUser failed: expected NT_STATUS_WRONG_PASSWORD because we broke the LM hash");
+
+ /* Unbreak the LM hash */
+ hash1.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 NT hash */
+ hash3.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;
+
+ status = dcerpc_samr_ChangePasswordUser(p, tctx, &r);
+ torture_assert_ntstatus_equal(tctx, status, 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;
+
+ status = dcerpc_samr_ChangePasswordUser(p, tctx, &r);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
+ printf("ChangePasswordUser failed: expected NT_STATUS_WRONG_PASSWORD because we broke the LM cross-hash, got %s\n", nt_errstr(status));
+ 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;
+
+ status = dcerpc_samr_ChangePasswordUser(p, tctx, &r);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
+ printf("ChangePasswordUser failed: expected NT_STATUS_WRONG_PASSWORD because we broke the NT cross-hash, got %s\n", nt_errstr(status));
+ 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;
+
+ status = dcerpc_samr_ChangePasswordUser(p, tctx, &r);
+ if (NT_STATUS_IS_OK(status)) {
+ changed = true;
+ *password = newpass;
+ } else if (!NT_STATUS_EQUAL(NT_STATUS_PASSWORD_RESTRICTION, status)) {
+ printf("ChangePasswordUser failed: expected NT_STATUS_OK, or at least NT_STATUS_PASSWORD_RESTRICTION, got %s\n", nt_errstr(status));
+ 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;
+
+ status = dcerpc_samr_ChangePasswordUser(p, tctx, &r);
+ if (NT_STATUS_IS_OK(status)) {
+ changed = true;
+ *password = newpass;
+ } else if (!NT_STATUS_EQUAL(NT_STATUS_PASSWORD_RESTRICTION, status)) {
+ printf("ChangePasswordUser failed: expected NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED, got %s\n", nt_errstr(status));
+ 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;
+
+ status = dcerpc_samr_ChangePasswordUser(p, tctx, &r);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION)) {
+ printf("ChangePasswordUser returned: %s perhaps min password age? (not fatal)\n", nt_errstr(status));
+ } else if (!NT_STATUS_IS_OK(status)) {
+ printf("ChangePasswordUser failed - %s\n", nt_errstr(status));
+ 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) {
+ status = dcerpc_samr_ChangePasswordUser(p, tctx, &r);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION)) {
+ printf("ChangePasswordUser returned: %s perhaps min password age? (not fatal)\n", nt_errstr(status));
+ } else if (!NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
+ printf("ChangePasswordUser failed: expected NT_STATUS_WRONG_PASSWORD because we already changed the password, got %s\n", nt_errstr(status));
+ ret = false;
+ }
+ }
+
+
+ if (!test_samr_handle_Close(p, 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)
+{
+ NTSTATUS status;
+ 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;
+ uint8_t old_lm_hash[16], new_lm_hash[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;
+
+ status = dcerpc_samr_GetDomPwInfo(p, tctx, &dom_pw_info);
+ if (NT_STATUS_IS_OK(status)) {
+ 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);
+ arcfour_crypt(lm_pass.data, old_lm_hash, 516);
+ 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]++;
+
+ status = dcerpc_samr_OemChangePasswordUser2(p, tctx, &r);
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION)
+ && !NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
+ printf("OemChangePasswordUser2 failed, should have returned WRONG_PASSWORD (or at least 'PASSWORD_RESTRICTON') for invalid password verifier - %s\n",
+ nt_errstr(status));
+ ret = false;
+ }
+
+ encode_pw_buffer(lm_pass.data, newpass, STR_ASCII);
+ /* Break the old password */
+ old_lm_hash[0]++;
+ arcfour_crypt(lm_pass.data, old_lm_hash, 516);
+ /* 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;
+
+ status = dcerpc_samr_OemChangePasswordUser2(p, tctx, &r);
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION)
+ && !NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
+ printf("OemChangePasswordUser2 failed, should have returned WRONG_PASSWORD (or at least 'PASSWORD_RESTRICTON') for invalidly encrpted password - %s\n",
+ nt_errstr(status));
+ ret = false;
+ }
+
+ encode_pw_buffer(lm_pass.data, newpass, STR_ASCII);
+ arcfour_crypt(lm_pass.data, old_lm_hash, 516);
+
+ r.in.server = &server;
+ r.in.account = &account;
+ r.in.password = &lm_pass;
+ r.in.hash = NULL;
+
+ status = dcerpc_samr_OemChangePasswordUser2(p, tctx, &r);
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION)
+ && !NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
+ printf("OemChangePasswordUser2 failed, should have returned INVALID_PARAMETER (or at least 'PASSWORD_RESTRICTON') for no supplied validation hash - %s\n",
+ nt_errstr(status));
+ ret = false;
+ }
+
+ /* This shouldn't be a valid name */
+ account_bad.string = TEST_ACCOUNT_NAME "XX";
+ r.in.account = &account_bad;
+
+ status = dcerpc_samr_OemChangePasswordUser2(p, tctx, &r);
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
+ printf("OemChangePasswordUser2 failed, should have returned INVALID_PARAMETER for no supplied validation hash and invalid user - %s\n",
+ nt_errstr(status));
+ 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;
+
+ status = dcerpc_samr_OemChangePasswordUser2(p, tctx, &r);
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
+ printf("OemChangePasswordUser2 failed, should have returned WRONG_PASSWORD for invalid user - %s\n",
+ nt_errstr(status));
+ 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;
+
+ status = dcerpc_samr_OemChangePasswordUser2(p, tctx, &r);
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
+ printf("OemChangePasswordUser2 failed, should have returned INVALID_PARAMETER for no supplied password and invalid user - %s\n",
+ nt_errstr(status));
+ ret = false;
+ }
+
+ E_deshash(oldpass, old_lm_hash);
+ E_deshash(newpass, new_lm_hash);
+
+ encode_pw_buffer(lm_pass.data, newpass, STR_ASCII);
+ arcfour_crypt(lm_pass.data, old_lm_hash, 516);
+ 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;
+
+ status = dcerpc_samr_OemChangePasswordUser2(p, tctx, &r);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION)) {
+ printf("OemChangePasswordUser2 returned: %s perhaps min password age? (not fatal)\n", nt_errstr(status));
+ } else if (!NT_STATUS_IS_OK(status)) {
+ printf("OemChangePasswordUser2 failed - %s\n", nt_errstr(status));
+ 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)
+{
+ NTSTATUS status;
+ 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;
+ uint8_t old_nt_hash[16], new_nt_hash[16];
+ uint8_t old_lm_hash[16], new_lm_hash[16];
+
+ struct samr_GetDomPwInfo dom_pw_info;
+ struct samr_PwInfo info;
+
+ 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 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;
+ status = dcerpc_samr_GetDomPwInfo(p, tctx, &dom_pw_info);
+ if (NT_STATUS_IS_OK(status)) {
+ 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);
+ arcfour_crypt(lm_pass.data, old_lm_hash, 516);
+ E_old_pw_hash(new_nt_hash, old_lm_hash, lm_verifier.hash);
+
+ encode_pw_buffer(nt_pass.data, newpass, STR_UNICODE);
+ arcfour_crypt(nt_pass.data, old_nt_hash, 516);
+ 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;
+
+ status = dcerpc_samr_ChangePasswordUser2(p, tctx, &r);
+ if (allow_password_restriction && NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION)) {
+ printf("ChangePasswordUser2 returned: %s perhaps min password age? (not fatal)\n", nt_errstr(status));
+ } else if (!NT_STATUS_IS_OK(status)) {
+ printf("ChangePasswordUser2 failed - %s\n", nt_errstr(status));
+ ret = false;
+ } else {
+ *password = newpass;
+ }
+
+ return ret;
+}
+
+
+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)
+{
+ NTSTATUS status;
+ 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;
+ uint8_t old_nt_hash[16], new_nt_hash[16];
+ uint8_t old_lm_hash[16], new_lm_hash[16];
+ NTTIME t;
+ struct samr_DomInfo1 *dominfo = NULL;
+ struct samr_ChangeReject *reject = NULL;
+
+ 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);
+
+ encode_pw_buffer(lm_pass.data, newpass, STR_UNICODE);
+ arcfour_crypt(lm_pass.data, old_nt_hash, 516);
+ E_old_pw_hash(new_nt_hash, old_lm_hash, lm_verifier.hash);
+
+ encode_pw_buffer(nt_pass.data, newpass, STR_UNICODE);
+ arcfour_crypt(nt_pass.data, old_nt_hash, 516);
+ 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;
+
+ status = dcerpc_samr_ChangePasswordUser3(p, tctx, &r);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION) &&
+ (!NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD))) {
+ printf("ChangePasswordUser3 failed, should have returned WRONG_PASSWORD (or at least 'PASSWORD_RESTRICTON') for invalid password verifier - %s\n",
+ nt_errstr(status));
+ ret = false;
+ }
+
+ encode_pw_buffer(lm_pass.data, newpass, STR_UNICODE);
+ arcfour_crypt(lm_pass.data, old_nt_hash, 516);
+ E_old_pw_hash(new_nt_hash, old_lm_hash, lm_verifier.hash);
+
+ encode_pw_buffer(nt_pass.data, newpass, STR_UNICODE);
+ /* Break the NT hash */
+ old_nt_hash[0]++;
+ arcfour_crypt(nt_pass.data, old_nt_hash, 516);
+ /* 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;
+
+ status = dcerpc_samr_ChangePasswordUser3(p, tctx, &r);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION) &&
+ (!NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD))) {
+ printf("ChangePasswordUser3 failed, should have returned WRONG_PASSWORD (or at least 'PASSWORD_RESTRICTON') for invalidly encrpted password - %s\n",
+ nt_errstr(status));
+ 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;
+ status = dcerpc_samr_ChangePasswordUser3(p, tctx, &r);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
+ printf("ChangePasswordUser3 failed, should have returned WRONG_PASSWORD for invalid username - %s\n",
+ nt_errstr(status));
+ 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);
+
+ encode_pw_buffer(lm_pass.data, newpass, STR_UNICODE);
+ arcfour_crypt(lm_pass.data, old_nt_hash, 516);
+ E_old_pw_hash(new_nt_hash, old_lm_hash, lm_verifier.hash);
+
+ encode_pw_buffer(nt_pass.data, newpass, STR_UNICODE);
+ arcfour_crypt(nt_pass.data, old_nt_hash, 516);
+ 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));
+
+ status = dcerpc_samr_ChangePasswordUser3(p, tctx, &r);
+
+ if (NT_STATUS_EQUAL(status, 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->reason != SAMR_REJECT_OTHER)) {
+ printf("expected SAMR_REJECT_OTHER (%d), got %d\n",
+ SAMR_REJECT_OTHER, reject->reason);
+ return false;
+ }
+ }
+
+ /* We tested the order of precendence 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->reason != SAMR_REJECT_OTHER) {
+ printf("expected SAMR_REJECT_OTHER (%d), got %d\n",
+ SAMR_REJECT_OTHER, reject->reason);
+ return false;
+ }
+
+ } else if ((dominfo->min_password_length > 0) &&
+ (strlen(newpass) < dominfo->min_password_length)) {
+
+ if (reject->reason != SAMR_REJECT_TOO_SHORT) {
+ printf("expected SAMR_REJECT_TOO_SHORT (%d), got %d\n",
+ SAMR_REJECT_TOO_SHORT, reject->reason);
+ return false;
+ }
+
+ } else if ((dominfo->password_history_length > 0) &&
+ strequal(oldpass, newpass)) {
+
+ if (reject->reason != SAMR_REJECT_IN_HISTORY) {
+ printf("expected SAMR_REJECT_IN_HISTORY (%d), got %d\n",
+ SAMR_REJECT_IN_HISTORY, reject->reason);
+ return false;
+ }
+ } else if (dominfo->password_properties & DOMAIN_PASSWORD_COMPLEX) {
+
+ if (reject->reason != SAMR_REJECT_COMPLEXITY) {
+ printf("expected SAMR_REJECT_COMPLEXITY (%d), got %d\n",
+ SAMR_REJECT_COMPLEXITY, reject->reason);
+ return false;
+ }
+
+ }
+
+ if (reject->reason == SAMR_REJECT_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(status, NT_STATUS_PASSWORD_RESTRICTION)) {
+ if (reject && reject->reason != SAMR_REJECT_OTHER) {
+ printf("expected SAMR_REJECT_OTHER (%d), got %d\n",
+ SAMR_REJECT_OTHER, reject->reason);
+ return false;
+ }
+ /* Perhaps the server has a 'min password age' set? */
+
+ } else {
+ torture_assert_ntstatus_ok(tctx, status, "ChangePasswordUser3");
+ *password = talloc_strdup(tctx, newpass);
+ }
+
+ return ret;
+}
+
+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;
+ DATA_BLOB confounded_session_key = data_blob_talloc(tctx, NULL, 16);
+ uint8_t confounder[16];
+ struct MD5Context ctx;
+
+ 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;
+ uint8_t old_nt_hash[16], new_nt_hash[16];
+ NTTIME t;
+ struct samr_DomInfo1 *dominfo = NULL;
+ struct samr_ChangeReject *reject = NULL;
+
+ 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);
+
+ status = dcerpc_fetch_session_key(p, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetUserInfo level %u - no session key - %s\n",
+ s.in.level, nt_errstr(status));
+ return false;
+ }
+
+ generate_random_buffer((uint8_t *)confounder, 16);
+
+ MD5Init(&ctx);
+ MD5Update(&ctx, confounder, 16);
+ MD5Update(&ctx, session_key.data, session_key.length);
+ MD5Final(confounded_session_key.data, &ctx);
+
+ arcfour_crypt_blob(u.info25.password.data, 516, &confounded_session_key);
+ memcpy(&u.info25.password.data[516], confounder, 16);
+
+ torture_comment(tctx, "Testing SetUserInfo level 25 (set password ex) with a password made up of only random bytes\n");
+
+ status = dcerpc_samr_SetUserInfo(p, tctx, &s);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetUserInfo level %u failed - %s\n",
+ s.in.level, nt_errstr(status));
+ 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);
+ arcfour_crypt(nt_pass.data, old_nt_hash, 516);
+ 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));
+
+ status = dcerpc_samr_ChangePasswordUser3(p, tctx, &r);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION)) {
+ if (reject && reject->reason != SAMR_REJECT_OTHER) {
+ printf("expected SAMR_REJECT_OTHER (%d), got %d\n",
+ SAMR_REJECT_OTHER, reject->reason);
+ return false;
+ }
+ /* Perhaps the server has a 'min password age' set? */
+
+ } else if (!NT_STATUS_IS_OK(status)) {
+ printf("ChangePasswordUser3 failed - %s\n", nt_errstr(status));
+ 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);
+
+ encode_pw_buffer(nt_pass.data, newpass, STR_UNICODE);
+ arcfour_crypt(nt_pass.data, old_nt_hash, 516);
+ 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));
+
+ status = dcerpc_samr_ChangePasswordUser3(p, tctx, &r);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION)) {
+ if (reject && reject->reason != SAMR_REJECT_OTHER) {
+ printf("expected SAMR_REJECT_OTHER (%d), got %d\n",
+ SAMR_REJECT_OTHER, reject->reason);
+ return false;
+ }
+ /* Perhaps the server has a 'min password age' set? */
+
+ } else {
+ torture_assert_ntstatus_ok(tctx, status, "ChangePasswordUser3 (on second random password)");
+ *password = talloc_strdup(tctx, newpass);
+ }
+
+ return ret;
+}
+
+
+static bool test_GetMembersInAlias(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *alias_handle)
+{
+ struct samr_GetMembersInAlias r;
+ struct lsa_SidArray sids;
+ NTSTATUS status;
+
+ torture_comment(tctx, "Testing GetMembersInAlias\n");
+
+ r.in.alias_handle = alias_handle;
+ r.out.sids = &sids;
+
+ status = dcerpc_samr_GetMembersInAlias(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "GetMembersInAlias");
+
+ return true;
+}
+
+static bool test_AddMemberToAlias(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *alias_handle,
+ const struct dom_sid *domain_sid)
+{
+ struct samr_AddAliasMember r;
+ struct samr_DeleteAliasMember d;
+ NTSTATUS status;
+ 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;
+
+ status = dcerpc_samr_AddAliasMember(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "AddAliasMember");
+
+ d.in.alias_handle = alias_handle;
+ d.in.sid = sid;
+
+ status = dcerpc_samr_DeleteAliasMember(p, tctx, &d);
+ torture_assert_ntstatus_ok(tctx, status, "DelAliasMember");
+
+ return true;
+}
+
+static bool test_AddMultipleMembersToAlias(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *alias_handle)
+{
+ struct samr_AddMultipleMembersToAlias a;
+ struct samr_RemoveMultipleMembersFromAlias r;
+ NTSTATUS status;
+ 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");
+
+ status = dcerpc_samr_AddMultipleMembersToAlias(p, tctx, &a);
+ torture_assert_ntstatus_ok(tctx, status, "AddMultipleMembersToAlias");
+
+
+ torture_comment(tctx, "testing RemoveMultipleMembersFromAlias\n");
+ r.in.alias_handle = alias_handle;
+ r.in.sids = &sids;
+
+ status = dcerpc_samr_RemoveMultipleMembersFromAlias(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "RemoveMultipleMembersFromAlias");
+
+ /* strange! removing twice doesn't give any error */
+ status = dcerpc_samr_RemoveMultipleMembersFromAlias(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "RemoveMultipleMembersFromAlias");
+
+ /* 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");
+
+ status = dcerpc_samr_RemoveMultipleMembersFromAlias(p, tctx, &r);
+ torture_assert_ntstatus_equal(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, "RemoveMultipleMembersFromAlias");
+
+ return true;
+}
+
+static bool test_TestPrivateFunctionsUser(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *user_handle)
+{
+ struct samr_TestPrivateFunctionsUser r;
+ NTSTATUS status;
+
+ torture_comment(tctx, "Testing TestPrivateFunctionsUser\n");
+
+ r.in.user_handle = user_handle;
+
+ status = dcerpc_samr_TestPrivateFunctionsUser(p, tctx, &r);
+ torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_IMPLEMENTED, "TestPrivateFunctionsUser");
+
+ return true;
+}
+
+static bool test_QueryUserInfo_pwdlastset(struct dcerpc_pipe *p,
+ 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; i<ARRAY_SIZE(levels); i++) {
+
+ struct samr_QueryUserInfo r;
+ struct samr_QueryUserInfo2 r2;
+ union samr_UserInfo *info;
+
+ if (use_info2) {
+ r2.in.user_handle = handle;
+ r2.in.level = levels[i];
+ r2.out.info = &info;
+ status = dcerpc_samr_QueryUserInfo2(p, tctx, &r2);
+
+ } else {
+ r.in.user_handle = handle;
+ r.in.level = levels[i];
+ r.out.info = &info;
+ status = dcerpc_samr_QueryUserInfo(p, tctx, &r);
+ }
+
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_INVALID_INFO_CLASS)) {
+ printf("QueryUserInfo%s level %u failed - %s\n",
+ use_info2 ? "2":"", levels[i], nt_errstr(status));
+ return false;
+ }
+
+ switch (levels[i]) {
+ case 3:
+ pwdlastset3 = info->info3.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: %lld)\n", *pwdlastset);
+
+ return true;
+}
+
+static bool test_SamLogon_Creds(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct cli_credentials *machine_credentials,
+ struct cli_credentials *test_credentials,
+ struct creds_CredentialState *creds,
+ NTSTATUS expected_result)
+{
+ 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 flags = CLI_CRED_NTLM_AUTH;
+
+ if (lp_client_lanman_auth(tctx->lp_ctx)) {
+ flags |= CLI_CRED_LANMAN_AUTH;
+ }
+
+ if (lp_client_ntlmv2_auth(tctx->lp_ctx)) {
+ flags |= CLI_CRED_NTLMv2_AUTH;
+ }
+
+ cli_credentials_get_ntlm_username_domain(test_credentials, 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(machine_credentials),
+ cli_credentials_get_domain(machine_credentials));
+
+ status = cli_credentials_get_ntlm_response(test_credentials, tctx,
+ &flags,
+ chal,
+ 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 =
+ MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT |
+ MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT;
+ ninfo.identity_info.logon_id_low = 0;
+ ninfo.identity_info.logon_id_high = 0;
+ ninfo.identity_info.workstation.string = cli_credentials_get_workstation(machine_credentials);
+
+ logon.network = &ninfo;
+
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.computer_name = cli_credentials_get_workstation(machine_credentials);
+ r.in.credential = &auth;
+ r.in.return_authenticator = &auth2;
+ r.in.logon_level = 2;
+ 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);
+
+ ZERO_STRUCT(auth2);
+ creds_client_authenticator(creds, &auth);
+
+ r.in.validation_level = 2;
+
+ status = dcerpc_netr_LogonSamLogon(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_assert_ntstatus_equal(tctx, status, expected_result, "LogonSamLogon failed");
+ return true;
+ } else {
+ torture_assert_ntstatus_ok(tctx, status, "LogonSamLogon failed");
+ }
+
+ torture_assert(tctx, creds_client_check(creds, &r.out.return_authenticator->cred),
+ "Credential chaining failed");
+
+ 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)
+{
+ struct creds_CredentialState *creds;
+
+ if (!test_SetupCredentials(p, tctx, machine_credentials, &creds)) {
+ return false;
+ }
+
+ return test_SamLogon_Creds(p, tctx, machine_credentials, test_credentials,
+ creds, expected_result);
+}
+
+static bool test_SamLogon_with_creds(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *machine_creds,
+ const char *acct_name,
+ char *password,
+ NTSTATUS expected_samlogon_result)
+{
+ bool ret = true;
+ struct cli_credentials *test_credentials;
+
+ test_credentials = cli_credentials_init(tctx);
+
+ cli_credentials_set_workstation(test_credentials,
+ TEST_ACCOUNT_NAME_PWD, CRED_SPECIFIED);
+ cli_credentials_set_domain(test_credentials,
+ lp_workgroup(tctx->lp_ctx), CRED_SPECIFIED);
+ cli_credentials_set_username(test_credentials,
+ acct_name, CRED_SPECIFIED);
+ cli_credentials_set_password(test_credentials,
+ password, CRED_SPECIFIED);
+ cli_credentials_set_secure_channel_type(test_credentials, SEC_CHAN_BDC);
+
+ printf("testing samlogon as %s@%s password: %s\n",
+ acct_name, TEST_ACCOUNT_NAME_PWD, password);
+
+ if (!test_SamLogon(tctx, p, machine_creds, test_credentials,
+ expected_samlogon_result)) {
+ torture_warning(tctx, "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;
+
+ switch (level) {
+ case 21:
+ case 23:
+ case 25:
+ 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(p, 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)) {
+ ret = false;
+ }
+
+ return ret;
+}
+
+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 = 500000;
+ bool set_levels[] = { false, true };
+ bool query_levels[] = { false, true };
+ uint32_t levels[] = { 18, 21, 23, 24, 25, 26 };
+ 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
+ };
+ NTSTATUS status;
+ struct dcerpc_pipe *np = NULL;
+
+ if (torture_setting_bool(tctx, "samba3", false)) {
+ delay = 999999;
+ printf("Samba3 has second granularity, setting delay to: %d\n",
+ delay);
+ }
+
+ status = torture_rpc_connection(tctx, &np, &ndr_table_netlogon);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ /* set to 1 to enable testing for all possible opcode
+ (SetUserInfo, SetUserInfo2, QueryUserInfo, QueryUserInfo2)
+ combinations */
+#if 0
+#define TEST_SET_LEVELS 1
+#define TEST_QUERY_LEVELS 1
+#endif
+ for (l=0; l<ARRAY_SIZE(levels); l++) {
+ for (z=0; z<ARRAY_SIZE(nonzeros); z++) {
+ for (f=0; f<ARRAY_SIZE(fields_present); f++) {
+#ifdef TEST_SET_LEVELS
+ for (s=0; s<ARRAY_SIZE(set_levels); s++) {
+#endif
+#ifdef TEST_QUERY_LEVELS
+ for (q=0; q<ARRAY_SIZE(query_levels); q++) {
+#endif
+ NTTIME pwdlastset_old = 0;
+ NTTIME pwdlastset_new = 0;
+ bool matched_expected_error = false;
+ NTSTATUS expected_samlogon_result = NT_STATUS_ACCOUNT_DISABLED;
+
+ torture_comment(tctx, "------------------------------\n"
+ "Testing pwdLastSet attribute for flags: 0x%08x "
+ "(s: %d (l: %d), q: %d)\n",
+ acct_flags, s, levels[l], q);
+
+ switch (levels[l]) {
+ case 21:
+ case 23:
+ case 25:
+ if (!((fields_present[f] & SAMR_FIELD_NT_PASSWORD_PRESENT) ||
+ (fields_present[f] & SAMR_FIELD_LM_PASSWORD_PRESENT))) {
+ expected_samlogon_result = NT_STATUS_WRONG_PASSWORD;
+ }
+ break;
+ }
+
+
+ /* set #1 */
+
+ /* 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_old,
+ expected_samlogon_result)) {
+ ret = false;
+ }
+
+ if (matched_expected_error == true) {
+ /* skipping on expected failure */
+ continue;
+ }
+
+ /* 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:
+ 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;
+ }
+ default:
+ if (pwdlastset_new != 0) {
+ torture_warning(tctx, "pwdLastSet test failed: "
+ "expected pwdLastSet 0 but got %lld\n",
+ pwdlastset_old);
+ ret = false;
+ }
+ break;
+ }
+
+ switch (levels[l]) {
+ case 21:
+ case 23:
+ case 25:
+ 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_warning(tctx, "pwdlastset not increasing\n");
+ ret = false;
+ }
+ break;
+ default:
+ if ((pwdlastset_old > 0) && (pwdlastset_new > 0) &&
+ (pwdlastset_old >= pwdlastset_new)) {
+ torture_warning(tctx, "pwdlastset not increasing\n");
+ ret = false;
+ }
+ break;
+ }
+
+ 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:
+
+ /* 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;
+ }
+ default:
+ if (pwdlastset_old >= pwdlastset_new) {
+ torture_warning(tctx, "pwdLastSet test failed: "
+ "expected last pwdlastset (%lld) < new pwdlastset (%lld)\n",
+ pwdlastset_old, pwdlastset_new);
+ ret = false;
+ }
+ if (pwdlastset_new == 0) {
+ torture_warning(tctx, "pwdLastSet test failed: "
+ "expected non-0 pwdlastset, got: %lld\n",
+ pwdlastset_new);
+ ret = false;
+ }
+ }
+
+ switch (levels[l]) {
+ case 21:
+ case 23:
+ case 25:
+ 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_warning(tctx, "pwdlastset not increasing\n");
+ ret = false;
+ }
+ break;
+ default:
+ if ((pwdlastset_old > 0) && (pwdlastset_new > 0) &&
+ (pwdlastset_old >= pwdlastset_new)) {
+ torture_warning(tctx, "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:
+
+ /* if no password has been changed, old and new pwdlastset
+ * need to be the same value */
+
+ if (!((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;
+ }
+ default:
+ if (pwdlastset_old >= pwdlastset_new) {
+ torture_warning(tctx, "pwdLastSet test failed: "
+ "expected last pwdlastset (%lld) < new pwdlastset (%lld)\n",
+ pwdlastset_old, pwdlastset_new);
+ ret = false;
+ }
+ if (pwdlastset_new == 0) {
+ torture_warning(tctx, "pwdLastSet test failed: "
+ "expected non-0 pwdlastset, got: %lld\n",
+ pwdlastset_new);
+ ret = false;
+ }
+ }
+
+ /* 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:
+ 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;
+ }
+ default:
+
+ if (pwdlastset_old == pwdlastset_new) {
+ torture_warning(tctx, "pwdLastSet test failed: "
+ "expected last pwdlastset (%lld) != new pwdlastset (%lld)\n",
+ pwdlastset_old, pwdlastset_new);
+ ret = false;
+ }
+
+ if (pwdlastset_new != 0) {
+ torture_warning(tctx, "pwdLastSet test failed: "
+ "expected pwdLastSet 0, got %lld\n",
+ pwdlastset_old);
+ ret = false;
+ }
+ break;
+ }
+
+ switch (levels[l]) {
+ case 21:
+ case 23:
+ case 25:
+ 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_warning(tctx, "pwdlastset not increasing\n");
+ ret = false;
+ }
+ break;
+ default:
+ if ((pwdlastset_old > 0) && (pwdlastset_new > 0) &&
+ (pwdlastset_old >= pwdlastset_new)) {
+ torture_warning(tctx, "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:
+ 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
+
+ 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)
+{
+ NTSTATUS status;
+ bool ret = true;
+
+ 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;
+
+ printf("Testing LSA EnumAccountRights\n");
+
+ r.in.handle = lsa_handle;
+ r.in.sid = user_sid;
+ r.out.rights = &rights;
+
+ status = dcerpc_lsa_EnumAccountRights(lp, tctx, &r);
+ torture_assert_ntstatus_equal(tctx, status, 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;
+
+ printf("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;
+
+ status = dcerpc_lsa_AddAccountRights(lp, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "Failed to add privileges");
+ }
+
+ {
+ struct lsa_EnumAccounts r;
+ uint32_t resume_handle = 0;
+ struct lsa_SidArray lsa_sid_array;
+ int i;
+ bool found_sid = false;
+
+ printf("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;
+
+ status = dcerpc_lsa_EnumAccounts(lp, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "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;
+
+ printf("Testing LSA EnumAccountRights\n");
+
+ r.in.handle = lsa_handle;
+ r.in.sid = user_sid;
+ r.out.rights = &user_rights;
+
+ status = dcerpc_lsa_EnumAccountRights(lp, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "Failed to enum rights for account");
+
+ if (user_rights.count < 1) {
+ torture_warning(tctx, "failed to find newly added rights");
+ return false;
+ }
+ }
+
+ {
+ struct lsa_OpenAccount r;
+
+ printf("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;
+
+ status = dcerpc_lsa_OpenAccount(lp, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "Failed to open lsa account");
+ }
+
+ {
+ struct lsa_GetSystemAccessAccount r;
+ uint32_t access_mask;
+
+ printf("Testing LSA GetSystemAccessAccount\n");
+
+ r.in.handle = &lsa_acct_handle;
+ r.out.access_mask = &access_mask;
+
+ status = dcerpc_lsa_GetSystemAccessAccount(lp, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "Failed to get lsa system access account");
+ }
+
+ {
+ struct lsa_Close r;
+
+ printf("Testing LSA Close\n");
+
+ r.in.handle = &lsa_acct_handle;
+ r.out.handle = &lsa_acct_handle;
+
+ status = dcerpc_lsa_Close(lp, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "Failed to close lsa");
+ }
+
+ {
+ struct samr_DeleteUser r;
+
+ printf("Testing SAMR DeleteUser\n");
+
+ r.in.user_handle = user_handle;
+ r.out.user_handle = user_handle;
+
+ status = dcerpc_samr_DeleteUser(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "Delete User failed");
+ }
+
+ {
+ struct lsa_EnumAccounts r;
+ uint32_t resume_handle = 0;
+ struct lsa_SidArray lsa_sid_array;
+ int i;
+ bool found_sid = false;
+
+ printf("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;
+
+ status = dcerpc_lsa_EnumAccounts(lp, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "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;
+
+ printf("Testing LSA EnumAccountRights\n");
+
+ r.in.handle = lsa_handle;
+ r.in.sid = user_sid;
+ r.out.rights = &user_rights;
+
+ status = dcerpc_lsa_EnumAccountRights(lp, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "Failed to enum rights for account");
+
+ if (user_rights.count < 1) {
+ torture_warning(tctx, "failed to find newly added rights");
+ return false;
+ }
+ }
+
+ {
+ struct lsa_OpenAccount r;
+
+ printf("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;
+
+ status = dcerpc_lsa_OpenAccount(lp, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "Failed to open lsa account");
+ }
+
+ {
+ struct lsa_GetSystemAccessAccount r;
+ uint32_t access_mask;
+
+ printf("Testing LSA GetSystemAccessAccount\n");
+
+ r.in.handle = &lsa_acct_handle;
+ r.out.access_mask = &access_mask;
+
+ status = dcerpc_lsa_GetSystemAccessAccount(lp, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "Failed to get lsa system access account");
+ }
+
+ {
+ struct lsa_DeleteObject r;
+
+ printf("Testing LSA DeleteObject\n");
+
+ r.in.handle = &lsa_acct_handle;
+ r.out.handle = &lsa_acct_handle;
+
+ status = dcerpc_lsa_DeleteObject(lp, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "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;
+
+ printf("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;
+
+ status = dcerpc_lsa_EnumAccounts(lp, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "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;
+
+ printf("Testing LSA EnumAccountRights\n");
+
+ r.in.handle = lsa_handle;
+ r.in.sid = user_sid;
+ r.out.rights = &user_rights;
+
+ status = dcerpc_lsa_EnumAccountRights(lp, tctx, &r);
+ torture_assert_ntstatus_equal(tctx, status, 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;
+
+ 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(p, 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(p, tctx, user_handle)) {
+ ret = false;
+ }
+
+ if (!test_QueryUserInfo(p, tctx, user_handle)) {
+ ret = false;
+ }
+
+ if (!test_QueryUserInfo2(p, tctx, user_handle)) {
+ ret = false;
+ }
+
+ if (!test_SetUserInfo(p, tctx, user_handle, base_acct_flags,
+ base_acct_name)) {
+ ret = false;
+ }
+
+ if (!test_GetUserPwInfo(p, tctx, user_handle)) {
+ ret = false;
+ }
+
+ if (!test_TestPrivateFunctionsUser(p, 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);
+
+ printf("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 compleatly 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_SetUserPassEx(p, tctx, user_handle, false, &password)) {
+ ret = false;
+ }
+
+ if (!test_ChangePassword(p, tctx, base_acct_name, domain_handle, &password)) {
+ ret = false;
+ }
+
+ if (torture_setting_bool(tctx, "samba4", false)) {
+ printf("skipping Set Password level 18 and 21 against Samba4\n");
+ } else {
+
+ 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;
+
+ status = dcerpc_samr_QueryUserInfo(p, tctx, &q);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QueryUserInfo level %u failed - %s\n",
+ q.in.level, nt_errstr(status));
+ ret = false;
+ } else {
+ uint32_t expected_flags = (base_acct_flags | ACB_PWNOTREQ | ACB_DISABLED);
+ if ((info->info5.acct_flags) != expected_flags) {
+ printf("QuerUserInfo level 5 failed, it returned 0x%08x when we expected flags of 0x%08x\n",
+ info->info5.acct_flags,
+ expected_flags);
+ /* FIXME: GD */
+ if (!torture_setting_bool(tctx, "samba3", false)) {
+ ret = false;
+ }
+ }
+ if (info->info5.rid != rid) {
+ printf("QuerUserInfo 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 */
+ if (!test_SetPassword_pwdlastset(p, tctx, base_acct_flags,
+ base_acct_name,
+ user_handle, &password,
+ machine_credentials)) {
+ ret = false;
+ }
+
+ if (ret == true) {
+ torture_comment(tctx, "pwdLastSet test succeeded\n");
+ } else {
+ torture_warning(tctx, "pwdLastSet test failed\n");
+ }
+
+ break;
+
+ case TORTURE_SAMR_USER_PRIVILEGES: {
+
+ struct dcerpc_pipe *lp;
+ struct policy_handle *lsa_handle;
+
+ status = torture_rpc_connection(tctx, &lp, &ndr_table_lsarpc);
+ torture_assert_ntstatus_ok(tctx, status, "Failed to open LSA pipe");
+
+ if (!test_lsa_OpenPolicy2(lp, 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(lp, tctx, lsa_handle)) {
+ ret = false;
+ }
+
+ if (!ret) {
+ torture_warning(tctx, "privileged user delete test failed\n");
+ }
+
+ break;
+ }
+ case TORTURE_SAMR_OTHER:
+ /* We just need the account to exist */
+ break;
+ }
+ return ret;
+}
+
+static bool test_alias_ops(struct dcerpc_pipe *p, 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(p, tctx, alias_handle)) {
+ ret = false;
+ }
+ }
+
+ if (!test_QueryAliasInfo(p, tctx, alias_handle)) {
+ ret = false;
+ }
+
+ if (!test_SetAliasInfo(p, tctx, alias_handle)) {
+ ret = false;
+ }
+
+ if (!test_AddMemberToAlias(p, tctx, alias_handle, domain_sid)) {
+ ret = false;
+ }
+
+ if (torture_setting_bool(tctx, "samba4", false)) {
+ printf("skipping MultipleMembers Alias tests against Samba4\n");
+ return ret;
+ }
+
+ if (!test_AddMultipleMembersToAlias(p, tctx, alias_handle)) {
+ ret = false;
+ }
+
+ return ret;
+}
+
+
+static bool test_DeleteUser(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *user_handle)
+{
+ struct samr_DeleteUser d;
+ NTSTATUS status;
+ torture_comment(tctx, "Testing DeleteUser\n");
+
+ d.in.user_handle = user_handle;
+ d.out.user_handle = user_handle;
+
+ status = dcerpc_samr_DeleteUser(p, tctx, &d);
+ torture_assert_ntstatus_ok(tctx, status, "DeleteUser");
+
+ return true;
+}
+
+bool test_DeleteUser_byname(struct dcerpc_pipe *p,
+ 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(p, tctx, handle, name, &rid);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+
+ status = test_OpenUser_byname(p, 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;
+ status = dcerpc_samr_DeleteUser(p, tctx, &d);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+
+ return true;
+
+failed:
+ printf("DeleteUser_byname(%s) failed - %s\n", name, nt_errstr(status));
+ return false;
+}
+
+
+static bool test_DeleteGroup_byname(struct dcerpc_pipe *p,
+ 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(p, 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;
+ status = dcerpc_samr_OpenGroup(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+
+ d.in.group_handle = &group_handle;
+ d.out.group_handle = &group_handle;
+ status = dcerpc_samr_DeleteDomainGroup(p, tctx, &d);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+
+ return true;
+
+failed:
+ printf("DeleteGroup_byname(%s) failed - %s\n", name, nt_errstr(status));
+ return false;
+}
+
+
+static bool test_DeleteAlias_byname(struct dcerpc_pipe *p,
+ 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;
+
+ printf("testing DeleteAlias_byname\n");
+
+ status = test_LookupName(p, 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;
+ status = dcerpc_samr_OpenAlias(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+
+ d.in.alias_handle = &alias_handle;
+ d.out.alias_handle = &alias_handle;
+ status = dcerpc_samr_DeleteDomAlias(p, tctx, &d);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+
+ return true;
+
+failed:
+ printf("DeleteAlias_byname(%s) failed - %s\n", name, nt_errstr(status));
+ return false;
+}
+
+static bool test_DeleteAlias(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *alias_handle)
+{
+ struct samr_DeleteDomAlias d;
+ NTSTATUS status;
+ bool ret = true;
+ printf("Testing DeleteAlias\n");
+
+ d.in.alias_handle = alias_handle;
+ d.out.alias_handle = alias_handle;
+
+ status = dcerpc_samr_DeleteDomAlias(p, tctx, &d);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("DeleteAlias failed - %s\n", nt_errstr(status));
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool test_CreateAlias(struct dcerpc_pipe *p, 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)
+{
+ NTSTATUS status;
+ 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;
+
+ printf("Testing CreateAlias (%s)\n", r.in.alias_name->string);
+
+ status = dcerpc_samr_CreateDomAlias(p, tctx, &r);
+
+ if (dom_sid_equal(domain_sid, dom_sid_parse_talloc(tctx, SID_BUILTIN))) {
+ if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ printf("Server correctly refused create of '%s'\n", r.in.alias_name->string);
+ return true;
+ } else {
+ printf("Server should have refused create of '%s', got %s instead\n", r.in.alias_name->string,
+ nt_errstr(status));
+ return false;
+ }
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_ALIAS_EXISTS)) {
+ if (!test_DeleteAlias_byname(p, tctx, domain_handle, r.in.alias_name->string)) {
+ return false;
+ }
+ status = dcerpc_samr_CreateDomAlias(p, tctx, &r);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("CreateAlias failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if (!test_alias) {
+ return ret;
+ }
+
+ if (!test_alias_ops(p, 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;
+
+ if (!*password) {
+ return false;
+ }
+
+ if (!test_ChangePasswordUser(p, 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;
+ NTSTATUS status;
+
+ len = 5;
+
+ r.in.domain_handle = domain_handle;
+ r.in.level = 1;
+ r.out.info = &info;
+
+ printf("testing samr_QueryDomainInfo level 1\n");
+ status = dcerpc_samr_QueryDomainInfo(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ 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;
+
+ printf("testing samr_SetDomainInfo level 1\n");
+ status = dcerpc_samr_SetDomainInfo(p, tctx, &s);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ printf("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;
+
+ printf("testing samr_SetDomainInfo level 1\n");
+ status = dcerpc_samr_SetDomainInfo(p, tctx, &s);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ }
+
+ {
+ NTSTATUS status;
+ 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;
+
+ status = dcerpc_samr_LookupNames(p, tctx, &n);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("LookupNames failed - %s\n", nt_errstr(status));
+ 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;
+
+ status = dcerpc_samr_OpenUser(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("OpenUser(%u) failed - %s\n", n.out.rids->ids[0], nt_errstr(status));
+ return false;
+ }
+
+ q.in.user_handle = &user_handle;
+ q.in.level = 5;
+ q.out.info = &info;
+
+ status = dcerpc_samr_QueryUserInfo(p, tctx, &q);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QueryUserInfo failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ printf("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;
+ }
+
+ 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;
+
+ NTSTATUS status;
+ 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 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;
+
+ printf("Testing CreateUser(%s)\n", r.in.account_name->string);
+
+ status = dcerpc_samr_CreateUser(p, user_ctx, &r);
+
+ if (dom_sid_equal(domain_sid, dom_sid_parse_talloc(tctx, SID_BUILTIN))) {
+ if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) || NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
+ printf("Server correctly refused create of '%s'\n", r.in.account_name->string);
+ return true;
+ } else {
+ printf("Server should have refused create of '%s', got %s instead\n", r.in.account_name->string,
+ nt_errstr(status));
+ return false;
+ }
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
+ if (!test_DeleteUser_byname(p, user_ctx, domain_handle, r.in.account_name->string)) {
+ talloc_free(user_ctx);
+ return false;
+ }
+ status = dcerpc_samr_CreateUser(p, user_ctx, &r);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(user_ctx);
+ printf("CreateUser failed - %s\n", nt_errstr(status));
+ 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;
+
+ status = dcerpc_samr_QueryUserInfo(p, user_ctx, &q);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QueryUserInfo level %u failed - %s\n",
+ q.in.level, nt_errstr(status));
+ ret = false;
+ } else {
+ if ((info->info16.acct_flags & acct_flags) != acct_flags) {
+ printf("QuerUserInfo 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 {
+ printf("Testing DeleteUser (createuser test)\n");
+
+ d.in.user_handle = &user_handle;
+ d.out.user_handle = &user_handle;
+
+ status = dcerpc_samr_DeleteUser(p, user_ctx, &d);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("DeleteUser failed - %s\n", nt_errstr(status));
+ 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)
+{
+ NTSTATUS status;
+ 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 {
+ 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_OK },
+ { 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;
+
+ printf("Testing CreateUser2(%s, 0x%x)\n", r.in.account_name->string, acct_flags);
+
+ status = dcerpc_samr_CreateUser2(p, user_ctx, &r);
+
+ if (dom_sid_equal(domain_sid, dom_sid_parse_talloc(tctx, SID_BUILTIN))) {
+ if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) || NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
+ printf("Server correctly refused create of '%s'\n", r.in.account_name->string);
+ continue;
+ } else {
+ printf("Server should have refused create of '%s', got %s instead\n", r.in.account_name->string,
+ nt_errstr(status));
+ ret = false;
+ continue;
+ }
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
+ if (!test_DeleteUser_byname(p, user_ctx, domain_handle, r.in.account_name->string)) {
+ talloc_free(user_ctx);
+ ret = false;
+ continue;
+ }
+ status = dcerpc_samr_CreateUser2(p, user_ctx, &r);
+
+ }
+ if (!NT_STATUS_EQUAL(status, account_types[i].nt_status)) {
+ printf("CreateUser2 failed gave incorrect error return - %s (should be %s)\n",
+ nt_errstr(status), nt_errstr(account_types[i].nt_status));
+ ret = false;
+ }
+
+ if (NT_STATUS_IS_OK(status)) {
+ q.in.user_handle = &user_handle;
+ q.in.level = 5;
+ q.out.info = &info;
+
+ status = dcerpc_samr_QueryUserInfo(p, user_ctx, &q);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QueryUserInfo level %u failed - %s\n",
+ q.in.level, nt_errstr(status));
+ 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) {
+ printf("QuerUserInfo 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) {
+ printf("QuerUserInfo 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) {
+ printf("QuerUserInfo 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) {
+ printf("QuerUserInfo 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 (!policy_handle_empty(&user_handle)) {
+ printf("Testing DeleteUser (createuser2 test)\n");
+
+ d.in.user_handle = &user_handle;
+ d.out.user_handle = &user_handle;
+
+ status = dcerpc_samr_DeleteUser(p, user_ctx, &d);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("DeleteUser failed - %s\n", nt_errstr(status));
+ ret = false;
+ }
+ }
+ }
+ talloc_free(user_ctx);
+ }
+
+ return ret;
+}
+
+static bool test_QueryAliasInfo(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct samr_QueryAliasInfo r;
+ union samr_AliasInfo *info;
+ uint16_t levels[] = {1, 2, 3};
+ int i;
+ bool ret = true;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ printf("Testing QueryAliasInfo level %u\n", levels[i]);
+
+ r.in.alias_handle = handle;
+ r.in.level = levels[i];
+ r.out.info = &info;
+
+ status = dcerpc_samr_QueryAliasInfo(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QueryAliasInfo level %u failed - %s\n",
+ levels[i], nt_errstr(status));
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_QueryGroupInfo(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct samr_QueryGroupInfo r;
+ union samr_GroupInfo *info;
+ uint16_t levels[] = {1, 2, 3, 4, 5};
+ int i;
+ bool ret = true;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ printf("Testing QueryGroupInfo level %u\n", levels[i]);
+
+ r.in.group_handle = handle;
+ r.in.level = levels[i];
+ r.out.info = &info;
+
+ status = dcerpc_samr_QueryGroupInfo(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QueryGroupInfo level %u failed - %s\n",
+ levels[i], nt_errstr(status));
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_QueryGroupMember(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct samr_QueryGroupMember r;
+ struct samr_RidTypeArray *rids = NULL;
+ bool ret = true;
+
+ printf("Testing QueryGroupMember\n");
+
+ r.in.group_handle = handle;
+ r.out.rids = &rids;
+
+ status = dcerpc_samr_QueryGroupMember(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QueryGroupInfo failed - %s\n", nt_errstr(status));
+ ret = false;
+ }
+
+ return ret;
+}
+
+
+static bool test_SetGroupInfo(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct samr_QueryGroupInfo r;
+ union samr_GroupInfo *info;
+ struct samr_SetGroupInfo s;
+ uint16_t levels[] = {1, 2, 3, 4};
+ uint16_t set_ok[] = {0, 1, 1, 1};
+ int i;
+ bool ret = true;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ printf("Testing QueryGroupInfo level %u\n", levels[i]);
+
+ r.in.group_handle = handle;
+ r.in.level = levels[i];
+ r.out.info = &info;
+
+ status = dcerpc_samr_QueryGroupInfo(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QueryGroupInfo level %u failed - %s\n",
+ levels[i], nt_errstr(status));
+ ret = false;
+ }
+
+ printf("Testing SetGroupInfo level %u\n", levels[i]);
+
+ s.in.group_handle = handle;
+ s.in.level = levels[i];
+ s.in.info = *r.out.info;
+
+#if 0
+ /* disabled this, as it changes the name only from the point of view of samr,
+ but leaves the name from the point of view of w2k3 internals (and ldap). This means
+ the name is still reserved, so creating the old name fails, but deleting by the old name
+ also fails */
+ if (s.in.level == 2) {
+ init_lsa_String(&s.in.info->string, "NewName");
+ }
+#endif
+
+ if (s.in.level == 4) {
+ init_lsa_String(&s.in.info->description, "test description");
+ }
+
+ status = dcerpc_samr_SetGroupInfo(p, tctx, &s);
+ if (set_ok[i]) {
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetGroupInfo level %u failed - %s\n",
+ r.in.level, nt_errstr(status));
+ ret = false;
+ continue;
+ }
+ } else {
+ if (!NT_STATUS_EQUAL(NT_STATUS_INVALID_INFO_CLASS, status)) {
+ printf("SetGroupInfo level %u gave %s - should have been NT_STATUS_INVALID_INFO_CLASS\n",
+ r.in.level, nt_errstr(status));
+ ret = false;
+ continue;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static bool test_QueryUserInfo(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ 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;i<ARRAY_SIZE(levels);i++) {
+ printf("Testing QueryUserInfo level %u\n", levels[i]);
+
+ r.in.user_handle = handle;
+ r.in.level = levels[i];
+ r.out.info = &info;
+
+ status = dcerpc_samr_QueryUserInfo(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QueryUserInfo level %u failed - %s\n",
+ levels[i], nt_errstr(status));
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_QueryUserInfo2(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct samr_QueryUserInfo2 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;i<ARRAY_SIZE(levels);i++) {
+ printf("Testing QueryUserInfo2 level %u\n", levels[i]);
+
+ r.in.user_handle = handle;
+ r.in.level = levels[i];
+ r.out.info = &info;
+
+ status = dcerpc_samr_QueryUserInfo2(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QueryUserInfo2 level %u failed - %s\n",
+ levels[i], nt_errstr(status));
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_OpenUser(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle, uint32_t rid)
+{
+ NTSTATUS status;
+ struct samr_OpenUser r;
+ struct policy_handle user_handle;
+ bool ret = true;
+
+ printf("Testing OpenUser(%u)\n", rid);
+
+ 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(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("OpenUser(%u) failed - %s\n", rid, nt_errstr(status));
+ return false;
+ }
+
+ if (!test_QuerySecurity(p, tctx, &user_handle)) {
+ ret = false;
+ }
+
+ if (!test_QueryUserInfo(p, tctx, &user_handle)) {
+ ret = false;
+ }
+
+ if (!test_QueryUserInfo2(p, tctx, &user_handle)) {
+ ret = false;
+ }
+
+ if (!test_GetUserPwInfo(p, tctx, &user_handle)) {
+ ret = false;
+ }
+
+ if (!test_GetGroupsForUser(p,tctx, &user_handle)) {
+ ret = false;
+ }
+
+ if (!test_samr_handle_Close(p, tctx, &user_handle)) {
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool test_OpenGroup(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle, uint32_t rid)
+{
+ NTSTATUS status;
+ struct samr_OpenGroup r;
+ struct policy_handle group_handle;
+ bool ret = true;
+
+ printf("Testing OpenGroup(%u)\n", rid);
+
+ r.in.domain_handle = handle;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.in.rid = rid;
+ r.out.group_handle = &group_handle;
+
+ status = dcerpc_samr_OpenGroup(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("OpenGroup(%u) failed - %s\n", rid, nt_errstr(status));
+ return false;
+ }
+
+ if (!torture_setting_bool(tctx, "samba3", false)) {
+ if (!test_QuerySecurity(p, tctx, &group_handle)) {
+ ret = false;
+ }
+ }
+
+ if (!test_QueryGroupInfo(p, tctx, &group_handle)) {
+ ret = false;
+ }
+
+ if (!test_QueryGroupMember(p, tctx, &group_handle)) {
+ ret = false;
+ }
+
+ if (!test_samr_handle_Close(p, tctx, &group_handle)) {
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool test_OpenAlias(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *handle, uint32_t rid)
+{
+ NTSTATUS status;
+ struct samr_OpenAlias r;
+ struct policy_handle alias_handle;
+ bool ret = true;
+
+ torture_comment(tctx, "Testing OpenAlias(%u)\n", rid);
+
+ r.in.domain_handle = handle;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.in.rid = rid;
+ r.out.alias_handle = &alias_handle;
+
+ status = dcerpc_samr_OpenAlias(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("OpenAlias(%u) failed - %s\n", rid, nt_errstr(status));
+ return false;
+ }
+
+ if (!torture_setting_bool(tctx, "samba3", false)) {
+ if (!test_QuerySecurity(p, tctx, &alias_handle)) {
+ ret = false;
+ }
+ }
+
+ if (!test_QueryAliasInfo(p, tctx, &alias_handle)) {
+ ret = false;
+ }
+
+ if (!test_GetMembersInAlias(p, tctx, &alias_handle)) {
+ ret = false;
+ }
+
+ if (!test_samr_handle_Close(p, tctx, &alias_handle)) {
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool check_mask(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *handle, uint32_t rid,
+ uint32_t acct_flag_mask)
+{
+ NTSTATUS status;
+ struct samr_OpenUser r;
+ struct samr_QueryUserInfo q;
+ union samr_UserInfo *info;
+ struct policy_handle user_handle;
+ bool ret = true;
+
+ torture_comment(tctx, "Testing OpenUser(%u)\n", rid);
+
+ 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(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("OpenUser(%u) failed - %s\n", rid, nt_errstr(status));
+ return false;
+ }
+
+ q.in.user_handle = &user_handle;
+ q.in.level = 16;
+ q.out.info = &info;
+
+ status = dcerpc_samr_QueryUserInfo(p, tctx, &q);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QueryUserInfo level 16 failed - %s\n",
+ nt_errstr(status));
+ ret = false;
+ } else {
+ if ((acct_flag_mask & info->info16.acct_flags) == 0) {
+ printf("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(p, tctx, &user_handle)) {
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool test_EnumDomainUsers_all(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status = STATUS_MORE_ENTRIES;
+ 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};
+
+ printf("Testing EnumDomainUsers\n");
+
+ for (mask_idx=0;mask_idx<ARRAY_SIZE(masks);mask_idx++) {
+ r.in.domain_handle = handle;
+ r.in.resume_handle = &resume_handle;
+ r.in.acct_flags = mask = masks[mask_idx];
+ r.in.max_size = (uint32_t)-1;
+ r.out.resume_handle = &resume_handle;
+ r.out.num_entries = &num_entries;
+ r.out.sam = &sam;
+
+ status = dcerpc_samr_EnumDomainUsers(p, tctx, &r);
+ if (!NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES) &&
+ !NT_STATUS_IS_OK(status)) {
+ printf("EnumDomainUsers failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ torture_assert(tctx, sam, "EnumDomainUsers failed: r.out.sam unexpectedly NULL");
+
+ if (sam->count == 0) {
+ continue;
+ }
+
+ for (i=0;i<sam->count;i++) {
+ if (mask) {
+ if (!check_mask(p, tctx, handle, sam->entries[i].idx, mask)) {
+ ret = false;
+ }
+ } else if (!test_OpenUser(p, tctx, handle, sam->entries[i].idx)) {
+ ret = false;
+ }
+ }
+ }
+
+ printf("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;i<sam->count;i++) {
+ n.in.names[i].string = sam->entries[i].name.string;
+ }
+ status = dcerpc_samr_LookupNames(p, tctx, &n);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("LookupNames failed - %s\n", nt_errstr(status));
+ ret = false;
+ }
+
+
+ printf("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;i<sam->count;i++) {
+ lr.in.rids[i] = sam->entries[i].idx;
+ }
+ status = dcerpc_samr_LookupRids(p, tctx, &lr);
+ torture_assert_ntstatus_ok(tctx, status, "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)
+{
+ NTSTATUS status;
+ struct samr_EnumDomainUsers r;
+ uint32_t resume_handle=0;
+ int i;
+#define ASYNC_COUNT 100
+ struct rpc_request *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;i<ASYNC_COUNT;i++) {
+ req[i] = dcerpc_samr_EnumDomainUsers_send(p, tctx, &r);
+ }
+
+ for (i=0;i<ASYNC_COUNT;i++) {
+ status = dcerpc_ndr_request_recv(req[i]);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("EnumDomainUsers[%d] failed - %s\n",
+ i, nt_errstr(status));
+ return false;
+ }
+ }
+
+ torture_comment(tctx, "%d async requests OK\n", i);
+
+ return true;
+}
+
+static bool test_EnumDomainGroups_all(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct samr_EnumDomainGroups r;
+ uint32_t resume_handle=0;
+ struct samr_SamArray *sam = NULL;
+ uint32_t num_entries = 0;
+ int i;
+ bool ret = true;
+
+ printf("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;
+
+ status = dcerpc_samr_EnumDomainGroups(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("EnumDomainGroups failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if (!sam) {
+ return false;
+ }
+
+ for (i=0;i<sam->count;i++) {
+ if (!test_OpenGroup(p, tctx, handle, sam->entries[i].idx)) {
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_EnumDomainAliases_all(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct samr_EnumDomainAliases r;
+ uint32_t resume_handle=0;
+ struct samr_SamArray *sam = NULL;
+ uint32_t num_entries = 0;
+ int i;
+ bool ret = true;
+
+ printf("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;
+
+ status = dcerpc_samr_EnumDomainAliases(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("EnumDomainAliases failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if (!sam) {
+ return false;
+ }
+
+ for (i=0;i<sam->count;i++) {
+ if (!test_OpenAlias(p, tctx, handle, sam->entries[i].idx)) {
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_GetDisplayEnumerationIndex(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ 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;i<ARRAY_SIZE(levels);i++) {
+ printf("Testing GetDisplayEnumerationIndex level %u\n", levels[i]);
+
+ init_lsa_String(&name, TEST_ACCOUNT_NAME);
+
+ r.in.domain_handle = handle;
+ r.in.level = levels[i];
+ r.in.name = &name;
+ r.out.idx = &idx;
+
+ status = dcerpc_samr_GetDisplayEnumerationIndex(p, tctx, &r);
+
+ if (ok_lvl[i] &&
+ !NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(NT_STATUS_NO_MORE_ENTRIES, status)) {
+ printf("GetDisplayEnumerationIndex level %u failed - %s\n",
+ levels[i], nt_errstr(status));
+ ret = false;
+ }
+
+ init_lsa_String(&name, "zzzzzzzz");
+
+ status = dcerpc_samr_GetDisplayEnumerationIndex(p, tctx, &r);
+
+ if (ok_lvl[i] && !NT_STATUS_EQUAL(NT_STATUS_NO_MORE_ENTRIES, status)) {
+ printf("GetDisplayEnumerationIndex level %u failed - %s\n",
+ levels[i], nt_errstr(status));
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_GetDisplayEnumerationIndex2(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct samr_GetDisplayEnumerationIndex2 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;i<ARRAY_SIZE(levels);i++) {
+ printf("Testing GetDisplayEnumerationIndex2 level %u\n", levels[i]);
+
+ init_lsa_String(&name, TEST_ACCOUNT_NAME);
+
+ r.in.domain_handle = handle;
+ r.in.level = levels[i];
+ r.in.name = &name;
+ r.out.idx = &idx;
+
+ status = dcerpc_samr_GetDisplayEnumerationIndex2(p, tctx, &r);
+ if (ok_lvl[i] &&
+ !NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(NT_STATUS_NO_MORE_ENTRIES, status)) {
+ printf("GetDisplayEnumerationIndex2 level %u failed - %s\n",
+ levels[i], nt_errstr(status));
+ ret = false;
+ }
+
+ init_lsa_String(&name, "zzzzzzzz");
+
+ status = dcerpc_samr_GetDisplayEnumerationIndex2(p, tctx, &r);
+ if (ok_lvl[i] && !NT_STATUS_EQUAL(NT_STATUS_NO_MORE_ENTRIES, status)) {
+ printf("GetDisplayEnumerationIndex2 level %u failed - %s\n",
+ levels[i], nt_errstr(status));
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+#define STRING_EQUAL_QUERY(s1, s2, user) \
+ if (s1.string == NULL && s2.string != NULL && s2.string[0] == '\0') { \
+ /* odd, but valid */ \
+ } else if ((s1.string && !s2.string) || (s2.string && !s1.string) || strcmp(s1.string, s2.string)) { \
+ printf("%s mismatch for %s: %s != %s (%s)\n", \
+ #s1, user.string, s1.string, s2.string, __location__); \
+ ret = false; \
+ }
+#define INT_EQUAL_QUERY(s1, s2, user) \
+ if (s1 != s2) { \
+ printf("%s mismatch for %s: 0x%llx != 0x%llx (%s)\n", \
+ #s1, user.string, (unsigned long long)s1, (unsigned long long)s2, __location__); \
+ ret = false; \
+ }
+
+static bool test_each_DisplayInfo_user(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct samr_QueryDisplayInfo *querydisplayinfo,
+ bool *seen_testuser)
+{
+ struct samr_OpenUser r;
+ struct samr_QueryUserInfo q;
+ union samr_UserInfo *info;
+ struct policy_handle user_handle;
+ int i, ret = true;
+ NTSTATUS status;
+ r.in.domain_handle = querydisplayinfo->in.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:
+ status = dcerpc_samr_OpenUser(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("OpenUser(%u) failed - %s\n", r.in.rid, nt_errstr(status));
+ return false;
+ }
+ }
+
+ q.in.user_handle = &user_handle;
+ q.in.level = 21;
+ q.out.info = &info;
+ status = dcerpc_samr_QueryUserInfo(p, tctx, &q);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QueryUserInfo(%u) failed - %s\n", r.in.rid, nt_errstr(status));
+ 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)) {
+ printf("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))) {
+ printf("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(p, tctx, &user_handle)) {
+ return false;
+ }
+ }
+ return ret;
+}
+
+static bool test_QueryDisplayInfo(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ 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;i<ARRAY_SIZE(levels);i++) {
+ printf("Testing QueryDisplayInfo level %u\n", levels[i]);
+
+ r.in.start_idx = 0;
+ status = STATUS_MORE_ENTRIES;
+ while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
+ r.in.domain_handle = handle;
+ r.in.level = levels[i];
+ r.in.max_entries = 2;
+ r.in.buf_size = (uint32_t)-1;
+ r.out.total_size = &total_size;
+ r.out.returned_size = &returned_size;
+ r.out.info = &disp_info;
+
+ status = dcerpc_samr_QueryDisplayInfo(p, tctx, &r);
+ if (!NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES) && !NT_STATUS_IS_OK(status)) {
+ printf("QueryDisplayInfo level %u failed - %s\n",
+ levels[i], nt_errstr(status));
+ ret = false;
+ }
+ switch (r.in.level) {
+ case 1:
+ if (!test_each_DisplayInfo_user(p, tctx, &r, &seen_testuser)) {
+ ret = false;
+ }
+ r.in.start_idx += r.out.info->info1.count;
+ break;
+ case 2:
+ if (!test_each_DisplayInfo_user(p, 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 */
+ status = dcerpc_samr_QueryDomainInfo(p, tctx, &dom_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QueryDomainInfo level %u failed - %s\n",
+ r.in.level, nt_errstr(status));
+ ret = false;
+ break;
+ }
+ switch (r.in.level) {
+ case 1:
+ case 4:
+ if (info->general.num_users < r.in.start_idx) {
+ printf("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(p, tctx, handle, TEST_ACCOUNT_NAME, &user_handle))) {
+ printf("Didn't find test user " TEST_ACCOUNT_NAME " in enumeration of %s\n",
+ info->general.domain_name.string);
+ ret = false;
+ test_samr_handle_Close(p, tctx, &user_handle);
+ }
+ }
+ break;
+ case 3:
+ case 5:
+ if (info->general.num_groups != r.in.start_idx) {
+ printf("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_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ 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;i<ARRAY_SIZE(levels);i++) {
+ printf("Testing QueryDisplayInfo2 level %u\n", levels[i]);
+
+ r.in.domain_handle = handle;
+ r.in.level = levels[i];
+ r.in.start_idx = 0;
+ r.in.max_entries = 1000;
+ r.in.buf_size = (uint32_t)-1;
+ r.out.total_size = &total_size;
+ r.out.returned_size = &returned_size;
+ r.out.info = &info;
+
+ status = dcerpc_samr_QueryDisplayInfo2(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QueryDisplayInfo2 level %u failed - %s\n",
+ levels[i], nt_errstr(status));
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_QueryDisplayInfo3(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct samr_QueryDisplayInfo3 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;i<ARRAY_SIZE(levels);i++) {
+ torture_comment(tctx, "Testing QueryDisplayInfo3 level %u\n", levels[i]);
+
+ r.in.domain_handle = handle;
+ r.in.level = levels[i];
+ r.in.start_idx = 0;
+ r.in.max_entries = 1000;
+ r.in.buf_size = (uint32_t)-1;
+ r.out.total_size = &total_size;
+ r.out.returned_size = &returned_size;
+ r.out.info = &info;
+
+ status = dcerpc_samr_QueryDisplayInfo3(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QueryDisplayInfo3 level %u failed - %s\n",
+ levels[i], nt_errstr(status));
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+
+static bool test_QueryDisplayInfo_continue(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct samr_QueryDisplayInfo r;
+ bool ret = true;
+ uint32_t total_size;
+ uint32_t returned_size;
+ union samr_DispInfo info;
+
+ printf("Testing QueryDisplayInfo continuation\n");
+
+ r.in.domain_handle = handle;
+ r.in.level = 1;
+ r.in.start_idx = 0;
+ r.in.max_entries = 1;
+ r.in.buf_size = (uint32_t)-1;
+ r.out.total_size = &total_size;
+ r.out.returned_size = &returned_size;
+ r.out.info = &info;
+
+ do {
+ status = dcerpc_samr_QueryDisplayInfo(p, tctx, &r);
+ if (NT_STATUS_IS_OK(status) && *r.out.returned_size != 0) {
+ if (r.out.info->info1.entries[0].idx != r.in.start_idx + 1) {
+ printf("expected idx %d but got %d\n",
+ r.in.start_idx + 1,
+ r.out.info->info1.entries[0].idx);
+ break;
+ }
+ }
+ if (!NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES) &&
+ !NT_STATUS_IS_OK(status)) {
+ printf("QueryDisplayInfo level %u failed - %s\n",
+ r.in.level, nt_errstr(status));
+ ret = false;
+ break;
+ }
+ r.in.start_idx++;
+ } while ((NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES) ||
+ NT_STATUS_IS_OK(status)) &&
+ *r.out.returned_size != 0);
+
+ return ret;
+}
+
+static bool test_QueryDomainInfo(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ 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;
+ 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;
+ status = dcerpc_samr_SetDomainInfo(p, tctx, &s);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetDomainInfo level %u (set comment) failed - %s\n",
+ s.in.level, nt_errstr(status));
+ return false;
+ }
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ torture_comment(tctx, "Testing QueryDomainInfo level %u\n", levels[i]);
+
+ r.in.domain_handle = handle;
+ r.in.level = levels[i];
+ r.out.info = &info;
+
+ status = dcerpc_samr_QueryDomainInfo(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QueryDomainInfo level %u failed - %s\n",
+ r.in.level, nt_errstr(status));
+ ret = false;
+ continue;
+ }
+
+ switch (levels[i]) {
+ case 2:
+ if (strcmp(info->general.oem_information.string, domain_comment) != 0) {
+ printf("QueryDomainInfo level %u returned different oem_information (comment) (%s, expected %s)\n",
+ levels[i], info->general.oem_information.string, domain_comment);
+ ret = false;
+ }
+ if (!info->general.primary.string) {
+ printf("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) {
+ printf("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) {
+ printf("QueryDomainInfo level %u returned different oem_information (comment) (%s, expected %s)\n",
+ levels[i], info->oem.oem_information.string, domain_comment);
+ ret = false;
+ }
+ break;
+ case 6:
+ if (!info->info6.primary.string) {
+ printf("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) {
+ printf("QueryDomainInfo level %u returned different comment (%s, expected %s)\n",
+ levels[i], info->general2.general.oem_information.string, domain_comment);
+ 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;
+
+ status = dcerpc_samr_SetDomainInfo(p, tctx, &s);
+ if (set_ok[i]) {
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetDomainInfo level %u failed - %s\n",
+ r.in.level, nt_errstr(status));
+ ret = false;
+ continue;
+ }
+ } else {
+ if (!NT_STATUS_EQUAL(NT_STATUS_INVALID_INFO_CLASS, status)) {
+ printf("SetDomainInfo level %u gave %s - should have been NT_STATUS_INVALID_INFO_CLASS\n",
+ r.in.level, nt_errstr(status));
+ ret = false;
+ continue;
+ }
+ }
+
+ status = dcerpc_samr_QueryDomainInfo(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QueryDomainInfo level %u failed - %s\n",
+ r.in.level, nt_errstr(status));
+ ret = false;
+ continue;
+ }
+ }
+
+ return ret;
+}
+
+
+static bool test_QueryDomainInfo2(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ 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;i<ARRAY_SIZE(levels);i++) {
+ printf("Testing QueryDomainInfo2 level %u\n", levels[i]);
+
+ r.in.domain_handle = handle;
+ r.in.level = levels[i];
+ r.out.info = &info;
+
+ status = dcerpc_samr_QueryDomainInfo2(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QueryDomainInfo2 level %u failed - %s\n",
+ r.in.level, nt_errstr(status));
+ ret = false;
+ continue;
+ }
+ }
+
+ return true;
+}
+
+/* Test whether querydispinfo level 5 and enumdomgroups return the same
+ set of group names. */
+static bool test_GroupList(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ struct samr_EnumDomainGroups q1;
+ struct samr_QueryDisplayInfo q2;
+ NTSTATUS status;
+ uint32_t resume_handle=0;
+ struct samr_SamArray *sam = NULL;
+ uint32_t num_entries = 0;
+ int i;
+ bool ret = true;
+ uint32_t total_size;
+ uint32_t returned_size;
+ union samr_DispInfo info;
+
+ int num_names = 0;
+ const char **names = NULL;
+
+ torture_comment(tctx, "Testing coherency of querydispinfo vs enumdomgroups\n");
+
+ q1.in.domain_handle = handle;
+ q1.in.resume_handle = &resume_handle;
+ q1.in.max_size = 5;
+ q1.out.resume_handle = &resume_handle;
+ q1.out.num_entries = &num_entries;
+ q1.out.sam = &sam;
+
+ status = STATUS_MORE_ENTRIES;
+ while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
+ status = dcerpc_samr_EnumDomainGroups(p, tctx, &q1);
+
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES))
+ break;
+
+ for (i=0; i<*q1.out.num_entries; i++) {
+ add_string_to_array(tctx,
+ sam->entries[i].name.string,
+ &names, &num_names);
+ }
+ }
+
+ torture_assert_ntstatus_ok(tctx, status, "EnumDomainGroups");
+
+ torture_assert(tctx, sam, "EnumDomainGroups failed to return sam");
+
+ 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)) {
+ status = dcerpc_samr_QueryDisplayInfo(p, tctx, &q2);
+
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES))
+ break;
+
+ for (i=0; i<q2.out.info->info5.count; i++) {
+ int j;
+ const char *name = q2.out.info->info5.entries[i].account_name.string;
+ bool found = false;
+ for (j=0; j<num_names; j++) {
+ if (names[j] == NULL)
+ continue;
+ if (strequal(names[j], name)) {
+ names[j] = NULL;
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ printf("QueryDisplayInfo gave name [%s] that EnumDomainGroups did not\n",
+ name);
+ ret = false;
+ }
+ }
+ q2.in.start_idx += q2.out.info->info5.count;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QueryDisplayInfo level 5 failed - %s\n",
+ nt_errstr(status));
+ ret = false;
+ }
+
+ for (i=0; i<num_names; i++) {
+ if (names[i] != NULL) {
+ printf("EnumDomainGroups gave name [%s] that QueryDisplayInfo did not\n",
+ names[i]);
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_DeleteDomainGroup(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *group_handle)
+{
+ struct samr_DeleteDomainGroup d;
+ NTSTATUS status;
+
+ torture_comment(tctx, "Testing DeleteDomainGroup\n");
+
+ d.in.group_handle = group_handle;
+ d.out.group_handle = group_handle;
+
+ status = dcerpc_samr_DeleteDomainGroup(p, tctx, &d);
+ torture_assert_ntstatus_ok(tctx, status, "DeleteDomainGroup");
+
+ return true;
+}
+
+static bool test_TestPrivateFunctionsDomain(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *domain_handle)
+{
+ struct samr_TestPrivateFunctionsDomain r;
+ NTSTATUS status;
+ bool ret = true;
+
+ torture_comment(tctx, "Testing TestPrivateFunctionsDomain\n");
+
+ r.in.domain_handle = domain_handle;
+
+ status = dcerpc_samr_TestPrivateFunctionsDomain(p, tctx, &r);
+ torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_IMPLEMENTED, "TestPrivateFunctionsDomain");
+
+ return ret;
+}
+
+static bool test_RidToSid(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct dom_sid *domain_sid,
+ struct policy_handle *domain_handle)
+{
+ struct samr_RidToSid r;
+ NTSTATUS status;
+ bool ret = true;
+ struct dom_sid *calc_sid, *out_sid;
+ int rids[] = { 0, 42, 512, 10200 };
+ int i;
+
+ for (i=0;i<ARRAY_SIZE(rids);i++) {
+ torture_comment(tctx, "Testing RidToSid\n");
+
+ calc_sid = dom_sid_dup(tctx, domain_sid);
+ r.in.domain_handle = domain_handle;
+ r.in.rid = rids[i];
+ r.out.sid = &out_sid;
+
+ status = dcerpc_samr_RidToSid(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("RidToSid for %d failed - %s\n", rids[i], nt_errstr(status));
+ ret = false;
+ } else {
+ calc_sid = dom_sid_add_rid(calc_sid, calc_sid, rids[i]);
+
+ if (!dom_sid_equal(calc_sid, out_sid)) {
+ printf("RidToSid for %d failed - got %s, expected %s\n", rids[i],
+ dom_sid_string(tctx, out_sid),
+ dom_sid_string(tctx, calc_sid));
+ ret = false;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static bool test_GetBootKeyInformation(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *domain_handle)
+{
+ struct samr_GetBootKeyInformation r;
+ NTSTATUS status;
+ bool ret = true;
+ uint32_t unknown = 0;
+
+ torture_comment(tctx, "Testing GetBootKeyInformation\n");
+
+ r.in.domain_handle = domain_handle;
+ r.out.unknown = &unknown;
+
+ status = dcerpc_samr_GetBootKeyInformation(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ /* w2k3 seems to fail this sometimes and pass it sometimes */
+ torture_comment(tctx, "GetBootKeyInformation (ignored) - %s\n", nt_errstr(status));
+ }
+
+ return ret;
+}
+
+static bool test_AddGroupMember(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *domain_handle,
+ struct policy_handle *group_handle)
+{
+ NTSTATUS status;
+ struct samr_AddGroupMember r;
+ struct samr_DeleteGroupMember d;
+ struct samr_QueryGroupMember q;
+ struct samr_RidTypeArray *rids = NULL;
+ struct samr_SetMemberAttributesOfGroup s;
+ uint32_t rid;
+
+ status = test_LookupName(p, tctx, domain_handle, TEST_ACCOUNT_NAME, &rid);
+ torture_assert_ntstatus_ok(tctx, status, "test_AddGroupMember looking up name " TEST_ACCOUNT_NAME);
+
+ r.in.group_handle = group_handle;
+ r.in.rid = rid;
+ r.in.flags = 0; /* ??? */
+
+ torture_comment(tctx, "Testing AddGroupMember and DeleteGroupMember\n");
+
+ d.in.group_handle = group_handle;
+ d.in.rid = rid;
+
+ status = dcerpc_samr_DeleteGroupMember(p, tctx, &d);
+ torture_assert_ntstatus_equal(tctx, NT_STATUS_MEMBER_NOT_IN_GROUP, status, "DeleteGroupMember");
+
+ status = dcerpc_samr_AddGroupMember(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "AddGroupMember");
+
+ status = dcerpc_samr_AddGroupMember(p, tctx, &r);
+ torture_assert_ntstatus_equal(tctx, NT_STATUS_MEMBER_IN_GROUP, status, "AddGroupMember");
+
+ if (torture_setting_bool(tctx, "samba4", false) ||
+ torture_setting_bool(tctx, "samba3", false)) {
+ torture_comment(tctx, "skipping SetMemberAttributesOfGroup test against Samba4\n");
+ } else {
+ /* this one is quite strange. I am using random inputs in the
+ hope of triggering an error that might give us a clue */
+
+ s.in.group_handle = group_handle;
+ s.in.unknown1 = random();
+ s.in.unknown2 = random();
+
+ status = dcerpc_samr_SetMemberAttributesOfGroup(p, tctx, &s);
+ torture_assert_ntstatus_ok(tctx, status, "SetMemberAttributesOfGroup");
+ }
+
+ q.in.group_handle = group_handle;
+ q.out.rids = &rids;
+
+ status = dcerpc_samr_QueryGroupMember(p, tctx, &q);
+ torture_assert_ntstatus_ok(tctx, status, "QueryGroupMember");
+
+ status = dcerpc_samr_DeleteGroupMember(p, tctx, &d);
+ torture_assert_ntstatus_ok(tctx, status, "DeleteGroupMember");
+
+ status = dcerpc_samr_AddGroupMember(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "AddGroupMember");
+
+ return true;
+}
+
+
+static bool test_CreateDomainGroup(struct dcerpc_pipe *p,
+ 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)
+{
+ NTSTATUS status;
+ 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;
+
+ printf("Testing CreateDomainGroup(%s)\n", r.in.name->string);
+
+ status = dcerpc_samr_CreateDomainGroup(p, tctx, &r);
+
+ if (dom_sid_equal(domain_sid, dom_sid_parse_talloc(tctx, SID_BUILTIN))) {
+ if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ torture_comment(tctx, "Server correctly refused create of '%s'\n", r.in.name->string);
+ return true;
+ } else {
+ printf("Server should have refused create of '%s', got %s instead\n", r.in.name->string,
+ nt_errstr(status));
+ return false;
+ }
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_GROUP_EXISTS)) {
+ if (!test_DeleteGroup_byname(p, tctx, domain_handle, r.in.name->string)) {
+ printf("CreateDomainGroup failed: Could not delete domain group %s - %s\n", r.in.name->string,
+ nt_errstr(status));
+ return false;
+ }
+ status = dcerpc_samr_CreateDomainGroup(p, tctx, &r);
+ }
+ if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
+ if (!test_DeleteUser_byname(p, tctx, domain_handle, r.in.name->string)) {
+
+ printf("CreateDomainGroup failed: Could not delete user %s - %s\n", r.in.name->string,
+ nt_errstr(status));
+ return false;
+ }
+ status = dcerpc_samr_CreateDomainGroup(p, tctx, &r);
+ }
+ torture_assert_ntstatus_ok(tctx, status, "CreateDomainGroup");
+
+ if (!test_group) {
+ return ret;
+ }
+
+ if (!test_AddGroupMember(p, tctx, domain_handle, group_handle)) {
+ printf("CreateDomainGroup failed - %s\n", nt_errstr(status));
+ ret = false;
+ }
+
+ if (!test_SetGroupInfo(p, 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_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *domain_handle)
+{
+ NTSTATUS status;
+ 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");
+
+ status = dcerpc_samr_RemoveMemberFromForeignDomain(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "RemoveMemberFromForeignDomain");
+
+ return true;
+}
+
+static bool test_EnumDomainUsers(struct dcerpc_pipe *p,
+ 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 = ACB_NORMAL;
+ 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;
+
+ printf("Testing EnumDomainUsers\n");
+
+ do {
+ status = dcerpc_samr_EnumDomainUsers(p, tctx, &r);
+ if (NT_STATUS_IS_ERR(status)) {
+ torture_assert_ntstatus_ok(tctx, status,
+ "failed to enumerate users");
+ }
+
+ 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_pipe *p,
+ 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;
+
+ printf("Testing EnumDomainGroups\n");
+
+ do {
+ status = dcerpc_samr_EnumDomainGroups(p, tctx, &r);
+ if (NT_STATUS_IS_ERR(status)) {
+ torture_assert_ntstatus_ok(tctx, status,
+ "failed to enumerate groups");
+ }
+
+ 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_pipe *p,
+ 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;
+
+ printf("Testing EnumDomainAliases\n");
+
+ do {
+ status = dcerpc_samr_EnumDomainAliases(p, tctx, &r);
+ if (NT_STATUS_IS_ERR(status)) {
+ torture_assert_ntstatus_ok(tctx, status,
+ "failed to enumerate aliases");
+ }
+
+ 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_ManyObjects(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *domain_handle,
+ struct dom_sid *domain_sid,
+ enum torture_samr_choice which_ops)
+{
+ uint32_t num_total = 1500;
+ uint32_t num_enum = 0;
+ uint32_t num_disp = 0;
+ uint32_t num_created = 0;
+ uint32_t num_anounced = 0;
+ bool ret = true;
+ NTSTATUS status;
+ uint32_t i;
+
+ /* query */
+
+ {
+ struct samr_QueryDomainInfo2 r;
+ union samr_DomainInfo *info;
+ r.in.domain_handle = domain_handle;
+ r.in.level = 2;
+ r.out.info = &info;
+
+ status = dcerpc_samr_QueryDomainInfo2(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "failed to query domain info");
+
+ switch (which_ops) {
+ 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++) {
+
+ struct policy_handle handle;
+ const char *name = NULL;
+
+ ZERO_STRUCT(handle);
+
+ switch (which_ops) {
+ case TORTURE_SAMR_MANY_ACCOUNTS:
+ name = talloc_asprintf(tctx, "%s%04d", TEST_ACCOUNT_NAME, i);
+ ret &= test_CreateUser(p, tctx, domain_handle, name, &handle, domain_sid, 0, NULL, false);
+ break;
+ case TORTURE_SAMR_MANY_GROUPS:
+ name = talloc_asprintf(tctx, "%s%04d", TEST_GROUPNAME, i);
+ ret &= test_CreateDomainGroup(p, tctx, domain_handle, name, &handle, domain_sid, false);
+ break;
+ case TORTURE_SAMR_MANY_ALIASES:
+ name = talloc_asprintf(tctx, "%s%04d", TEST_ALIASNAME, i);
+ ret &= test_CreateAlias(p, tctx, domain_handle, name, &handle, domain_sid, false);
+ break;
+ default:
+ return false;
+ }
+ if (!policy_handle_empty(&handle)) {
+ ret &= test_samr_handle_Close(p, tctx, &handle);
+ num_created++;
+ }
+ }
+
+ /* enum */
+
+ switch (which_ops) {
+ case TORTURE_SAMR_MANY_ACCOUNTS:
+ ret &= test_EnumDomainUsers(p, tctx, domain_handle, &num_enum);
+ break;
+ case TORTURE_SAMR_MANY_GROUPS:
+ ret &= test_EnumDomainGroups(p, tctx, domain_handle, &num_enum);
+ break;
+ case TORTURE_SAMR_MANY_ALIASES:
+ ret &= test_EnumDomainAliases(p, tctx, domain_handle, &num_enum);
+ break;
+ default:
+ return false;
+ }
+
+ torture_assert_int_equal(tctx, num_enum, num_anounced + num_created,
+ "unexpected number of results returned in enum call");
+#if 0
+ /* TODO: dispinfo */
+
+ switch (which_ops) {
+ case TORTURE_SAMR_MANY_ACCOUNTS:
+ break;
+ case TORTURE_SAMR_MANY_GROUPS:
+ break;
+ case TORTURE_SAMR_MANY_ALIASES:
+ break;
+ default:
+ return false;
+ }
+
+ torture_assert_int_equal(tctx, num_disp, num_anounced + num_created,
+ "unexpected number of results returned in dispinfo call");
+#endif
+ return ret;
+}
+
+static bool test_Connect(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *handle);
+
+static bool test_OpenDomain(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *handle, struct dom_sid *sid,
+ enum torture_samr_choice which_ops,
+ struct cli_credentials *machine_credentials)
+{
+ NTSTATUS status;
+ 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;
+
+ 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 = handle;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.in.sid = sid;
+ r.out.domain_handle = &domain_handle;
+
+ status = dcerpc_samr_OpenDomain(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "OpenDomain");
+
+ /* run the domain tests with the main handle closed - this tests
+ the servers reference counting */
+ ret &= test_samr_handle_Close(p, tctx, handle);
+
+ switch (which_ops) {
+ case TORTURE_SAMR_USER_ATTRIBUTES:
+ case TORTURE_SAMR_USER_PRIVILEGES:
+ case TORTURE_SAMR_PASSWORDS:
+ if (!torture_setting_bool(tctx, "samba3", false)) {
+ ret &= test_CreateUser2(p, tctx, &domain_handle, sid, which_ops, NULL);
+ }
+ ret &= test_CreateUser(p, tctx, &domain_handle, TEST_ACCOUNT_NAME, &user_handle, sid, which_ops, NULL, true);
+ /* This test needs 'complex' users to validate */
+ ret &= test_QueryDisplayInfo(p, tctx, &domain_handle);
+ if (!ret) {
+ printf("Testing PASSWORDS or ATTRIBUTES on domain %s failed!\n", dom_sid_string(tctx, sid));
+ }
+ break;
+ case TORTURE_SAMR_PASSWORDS_PWDLASTSET:
+ if (!torture_setting_bool(tctx, "samba3", false)) {
+ ret &= test_CreateUser2(p, tctx, &domain_handle, sid, which_ops, machine_credentials);
+ }
+ ret &= test_CreateUser(p, tctx, &domain_handle, TEST_ACCOUNT_NAME, &user_handle, sid, which_ops, machine_credentials, true);
+ if (!ret) {
+ printf("Testing PASSWORDS PWDLASTSET 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, which_ops);
+ break;
+ case TORTURE_SAMR_OTHER:
+ ret &= test_CreateUser(p, tctx, &domain_handle, TEST_ACCOUNT_NAME, &user_handle, sid, which_ops, NULL, true);
+ if (!ret) {
+ printf("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(p, tctx, &domain_handle);
+ }
+ ret &= test_RemoveMemberFromForeignDomain(p, tctx, &domain_handle);
+ ret &= test_CreateAlias(p, tctx, &domain_handle, TEST_ALIASNAME, &alias_handle, sid, true);
+ ret &= test_CreateDomainGroup(p, tctx, &domain_handle, TEST_GROUPNAME, &group_handle, sid, true);
+ ret &= test_QueryDomainInfo(p, tctx, &domain_handle);
+ ret &= test_QueryDomainInfo2(p, tctx, &domain_handle);
+ ret &= test_EnumDomainUsers_all(p, tctx, &domain_handle);
+ ret &= test_EnumDomainUsers_async(p, tctx, &domain_handle);
+ ret &= test_EnumDomainGroups_all(p, tctx, &domain_handle);
+ ret &= test_EnumDomainAliases_all(p, tctx, &domain_handle);
+ ret &= test_QueryDisplayInfo2(p, tctx, &domain_handle);
+ ret &= test_QueryDisplayInfo3(p, tctx, &domain_handle);
+ ret &= test_QueryDisplayInfo_continue(p, tctx, &domain_handle);
+
+ if (torture_setting_bool(tctx, "samba4", false)) {
+ torture_comment(tctx, "skipping GetDisplayEnumerationIndex test against Samba4\n");
+ } else {
+ ret &= test_GetDisplayEnumerationIndex(p, tctx, &domain_handle);
+ ret &= test_GetDisplayEnumerationIndex2(p, tctx, &domain_handle);
+ }
+ ret &= test_GroupList(p, tctx, &domain_handle);
+ ret &= test_TestPrivateFunctionsDomain(p, tctx, &domain_handle);
+ ret &= test_RidToSid(p, tctx, sid, &domain_handle);
+ ret &= test_GetBootKeyInformation(p, tctx, &domain_handle);
+ if (!ret) {
+ torture_comment(tctx, "Testing SAMR-OTHER on domain %s failed!\n", dom_sid_string(tctx, sid));
+ }
+ break;
+ }
+
+ if (!policy_handle_empty(&user_handle) &&
+ !test_DeleteUser(p, tctx, &user_handle)) {
+ ret = false;
+ }
+
+ if (!policy_handle_empty(&alias_handle) &&
+ !test_DeleteAlias(p, tctx, &alias_handle)) {
+ ret = false;
+ }
+
+ if (!policy_handle_empty(&group_handle) &&
+ !test_DeleteDomainGroup(p, tctx, &group_handle)) {
+ ret = false;
+ }
+
+ ret &= test_samr_handle_Close(p, tctx, &domain_handle);
+
+ /* reconnect the main handle */
+ ret &= test_Connect(p, tctx, handle);
+
+ if (!ret) {
+ printf("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 policy_handle *handle, const char *domain,
+ enum torture_samr_choice which_ops,
+ struct cli_credentials *machine_credentials)
+{
+ NTSTATUS status;
+ struct samr_LookupDomain r;
+ struct dom_sid2 *sid = NULL;
+ struct lsa_String n1;
+ struct lsa_String n2;
+ bool ret = true;
+
+ torture_comment(tctx, "Testing LookupDomain(%s)\n", domain);
+
+ /* check for correct error codes */
+ r.in.connect_handle = handle;
+ r.in.domain_name = &n2;
+ r.out.sid = &sid;
+ n2.string = NULL;
+
+ status = dcerpc_samr_LookupDomain(p, tctx, &r);
+ torture_assert_ntstatus_equal(tctx, NT_STATUS_INVALID_PARAMETER, status, "LookupDomain expected NT_STATUS_INVALID_PARAMETER");
+
+ init_lsa_String(&n2, "xxNODOMAINxx");
+
+ status = dcerpc_samr_LookupDomain(p, tctx, &r);
+ torture_assert_ntstatus_equal(tctx, NT_STATUS_NO_SUCH_DOMAIN, status, "LookupDomain expected NT_STATUS_NO_SUCH_DOMAIN");
+
+ r.in.connect_handle = handle;
+
+ init_lsa_String(&n1, domain);
+ r.in.domain_name = &n1;
+
+ status = dcerpc_samr_LookupDomain(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "LookupDomain");
+
+ if (!test_GetDomPwInfo(p, tctx, &n1)) {
+ ret = false;
+ }
+
+ if (!test_OpenDomain(p, tctx, handle, *r.out.sid, which_ops,
+ machine_credentials)) {
+ ret = false;
+ }
+
+ return ret;
+}
+
+
+static bool test_EnumDomains(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *handle, enum torture_samr_choice which_ops,
+ struct cli_credentials *machine_credentials)
+{
+ NTSTATUS status;
+ struct samr_EnumDomains r;
+ uint32_t resume_handle = 0;
+ uint32_t num_entries = 0;
+ struct samr_SamArray *sam = NULL;
+ int i;
+ bool ret = true;
+
+ r.in.connect_handle = 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;
+
+ status = dcerpc_samr_EnumDomains(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "EnumDomains");
+
+ if (!*r.out.sam) {
+ return false;
+ }
+
+ for (i=0;i<sam->count;i++) {
+ if (!test_LookupDomain(p, tctx, handle,
+ sam->entries[i].name.string, which_ops,
+ machine_credentials)) {
+ ret = false;
+ }
+ }
+
+ status = dcerpc_samr_EnumDomains(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "EnumDomains");
+
+ return ret;
+}
+
+
+static bool test_Connect(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ 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 = 0;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.connect_handle = &h;
+
+ status = dcerpc_samr_Connect(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "Connect failed - %s\n", nt_errstr(status));
+ 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;
+
+ status = dcerpc_samr_Connect2(p, tctx, &r2);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "Connect2 failed - %s\n", nt_errstr(status));
+ ret = false;
+ } else {
+ if (got_handle) {
+ test_samr_handle_Close(p, 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;
+
+ status = dcerpc_samr_Connect3(p, tctx, &r3);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Connect3 failed - %s\n", nt_errstr(status));
+ ret = false;
+ } else {
+ if (got_handle) {
+ test_samr_handle_Close(p, 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;
+
+ status = dcerpc_samr_Connect4(p, tctx, &r4);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Connect4 failed - %s\n", nt_errstr(status));
+ ret = false;
+ } else {
+ if (got_handle) {
+ test_samr_handle_Close(p, tctx, handle);
+ }
+ got_handle = true;
+ *handle = h;
+ }
+
+ torture_comment(tctx, "testing samr_Connect5\n");
+
+ info.info1.client_version = 0;
+ info.info1.unknown2 = 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;
+
+ status = dcerpc_samr_Connect5(p, tctx, &r5);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Connect5 failed - %s\n", nt_errstr(status));
+ ret = false;
+ } else {
+ if (got_handle) {
+ test_samr_handle_Close(p, tctx, handle);
+ }
+ got_handle = true;
+ *handle = h;
+ }
+
+ return ret;
+}
+
+
+bool torture_rpc_samr(struct torture_context *torture)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p;
+ bool ret = true;
+ struct policy_handle handle;
+
+ status = torture_rpc_connection(torture, &p, &ndr_table_samr);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ ret &= test_Connect(p, torture, &handle);
+
+ if (!torture_setting_bool(torture, "samba3", false)) {
+ ret &= test_QuerySecurity(p, torture, &handle);
+ }
+
+ ret &= test_EnumDomains(p, torture, &handle, TORTURE_SAMR_OTHER, NULL);
+
+ ret &= test_SetDsrmPassword(p, torture, &handle);
+
+ ret &= test_Shutdown(p, torture, &handle);
+
+ ret &= test_samr_handle_Close(p, torture, &handle);
+
+ return ret;
+}
+
+
+bool torture_rpc_samr_users(struct torture_context *torture)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p;
+ bool ret = true;
+ struct policy_handle handle;
+
+ status = torture_rpc_connection(torture, &p, &ndr_table_samr);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ ret &= test_Connect(p, torture, &handle);
+
+ if (!torture_setting_bool(torture, "samba3", false)) {
+ ret &= test_QuerySecurity(p, torture, &handle);
+ }
+
+ ret &= test_EnumDomains(p, torture, &handle, TORTURE_SAMR_USER_ATTRIBUTES, NULL);
+
+ ret &= test_SetDsrmPassword(p, torture, &handle);
+
+ ret &= test_Shutdown(p, torture, &handle);
+
+ ret &= test_samr_handle_Close(p, torture, &handle);
+
+ return ret;
+}
+
+
+bool torture_rpc_samr_passwords(struct torture_context *torture)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p;
+ bool ret = true;
+ struct policy_handle handle;
+
+ status = torture_rpc_connection(torture, &p, &ndr_table_samr);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ ret &= test_Connect(p, torture, &handle);
+
+ ret &= test_EnumDomains(p, torture, &handle, TORTURE_SAMR_PASSWORDS, NULL);
+
+ ret &= test_samr_handle_Close(p, torture, &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 policy_handle handle;
+
+ status = torture_rpc_connection(torture, &p, &ndr_table_samr);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ ret &= test_Connect(p, torture, &handle);
+
+ ret &= test_EnumDomains(p, torture, &handle,
+ TORTURE_SAMR_PASSWORDS_PWDLASTSET,
+ machine_credentials);
+
+ ret &= test_samr_handle_Close(p, torture, &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_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 policy_handle handle;
+
+ status = torture_rpc_connection(torture, &p, &ndr_table_samr);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ ret &= test_Connect(p, torture, &handle);
+
+ ret &= test_EnumDomains(p, torture, &handle,
+ TORTURE_SAMR_USER_PRIVILEGES,
+ machine_credentials);
+
+ ret &= test_samr_handle_Close(p, torture, &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_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,
+ struct cli_credentials *machine_credentials)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p;
+ bool ret = true;
+ struct policy_handle handle;
+
+ status = torture_rpc_connection(torture, &p, &ndr_table_samr);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ ret &= test_Connect(p, torture, &handle);
+
+ ret &= test_EnumDomains(p, torture, &handle,
+ TORTURE_SAMR_MANY_ACCOUNTS,
+ machine_credentials);
+
+ ret &= test_samr_handle_Close(p, torture, &handle);
+
+ return ret;
+}
+
+static bool torture_rpc_samr_many_groups(struct torture_context *torture,
+ struct dcerpc_pipe *p2,
+ struct cli_credentials *machine_credentials)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p;
+ bool ret = true;
+ struct policy_handle handle;
+
+ status = torture_rpc_connection(torture, &p, &ndr_table_samr);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ ret &= test_Connect(p, torture, &handle);
+
+ ret &= test_EnumDomains(p, torture, &handle,
+ TORTURE_SAMR_MANY_GROUPS,
+ machine_credentials);
+
+ ret &= test_samr_handle_Close(p, torture, &handle);
+
+ return ret;
+}
+
+static bool torture_rpc_samr_many_aliases(struct torture_context *torture,
+ struct dcerpc_pipe *p2,
+ struct cli_credentials *machine_credentials)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p;
+ bool ret = true;
+ struct policy_handle handle;
+
+ status = torture_rpc_connection(torture, &p, &ndr_table_samr);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ ret &= test_Connect(p, torture, &handle);
+
+ ret &= test_EnumDomains(p, torture, &handle,
+ TORTURE_SAMR_MANY_ALIASES,
+ machine_credentials);
+
+ ret &= test_samr_handle_Close(p, torture, &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;
+
+ tcase = torture_suite_add_machine_rpc_iface_tcase(suite, "samr",
+ &ndr_table_samr,
+ TEST_ACCOUNT_NAME);
+
+ torture_rpc_tcase_add_test_creds(tcase, "many_aliases",
+ torture_rpc_samr_many_aliases);
+ torture_rpc_tcase_add_test_creds(tcase, "many_groups",
+ torture_rpc_samr_many_groups);
+ torture_rpc_tcase_add_test_creds(tcase, "many_accounts",
+ torture_rpc_samr_many_accounts);
+
+ return suite;
+}
diff --git a/source4/torture/rpc/samr_accessmask.c b/source4/torture/rpc/samr_accessmask.c
new file mode 100644
index 0000000000..1e74455849
--- /dev/null
+++ b/source4/torture/rpc/samr_accessmask.c
@@ -0,0 +1,669 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for accessmasks on the SAMR pipe
+
+ Copyright (C) Ronnie Sahlberg 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/ndr_samr_c.h"
+#include "torture/rpc/rpc.h"
+#include "param/param.h"
+#include "libcli/security/security.h"
+#include "librpc/gen_ndr/ndr_security.h"
+
+
+/* test user created to test the ACLs associated to SAMR objects */
+#define TEST_USER_NAME "samr_testuser"
+
+
+static NTSTATUS torture_samr_Close(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *h)
+{
+ NTSTATUS status;
+ struct samr_Close cl;
+
+ cl.in.handle = h;
+ cl.out.handle = h;
+ status = dcerpc_samr_Close(p, tctx, &cl);
+
+ return status;
+}
+
+static NTSTATUS torture_samr_Connect5(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ 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.unknown2 = 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(p, tctx, &r5);
+
+ return status;
+}
+
+/* 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;
+
+ 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, p, 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, p, &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;
+
+ 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, p, 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;
+
+ status = dcerpc_samr_EnumDomains(p, tctx, &ed);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("EnumDomains failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = torture_samr_Close(tctx, p, &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;
+
+ status = dcerpc_samr_EnumDomains(p, tctx, &ed);
+ if(!NT_STATUS_EQUAL(NT_STATUS_ACCESS_DENIED, status)) {
+ printf("EnumDomains failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = torture_samr_Close(tctx, p, &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_pipe *p,
+ 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;
+ 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, p, 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;
+ status = dcerpc_samr_QuerySecurity(p, tctx, &qs);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QuerySecurity failed - %s\n", nt_errstr(status));
+ 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;
+ status = dcerpc_samr_SetSecurity(p, tctx, &ss);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetSecurity failed - %s\n", nt_errstr(status));
+ 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;
+ }
+
+ /* connect to SAMR as the user */
+ status = torture_samr_Connect5(tctx, test_p, SEC_FLAG_MAXIMUM_ALLOWED, &uch);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Connect5 failed - %s\n", nt_errstr(status));
+ return false;
+ }
+ /* disconnec the user */
+ talloc_free(test_p);
+
+
+ /* read the sequrity descriptor back. it should not have changed
+ * eventhough samr_SetSecurity returned SUCCESS
+ */
+ status = dcerpc_samr_QuerySecurity(p, tctx, &qs);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QuerySecurity failed - %s\n", nt_errstr(status));
+ ret = false;
+ }
+ if (sd_size != sdbuf->sd_size) {
+ printf("security descriptor changed\n");
+ ret = false;
+ }
+
+
+ status = torture_samr_Close(tctx, p, &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 hte 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_pipe *p,
+ 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;
+ 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;
+ }
+
+ /* connect to SAMR as the user */
+ status = torture_samr_Connect5(tctx, test_p, SAMR_ACCESS_SHUTDOWN_SERVER, &uch);
+ if (NT_STATUS_IS_OK(status)) {
+ printf("Connect5 failed - %s\n", nt_errstr(status));
+ return false;
+ }
+ printf(" OK\n");
+
+ /* disconnec 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;
+
+ 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, p, 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 = lp_workgroup(tctx->lp_ctx);
+
+ status = dcerpc_samr_LookupDomain(p, tctx, &ld);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("LookupDomain failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = torture_samr_Close(tctx, p, &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 = lp_workgroup(tctx->lp_ctx);
+
+ status = dcerpc_samr_LookupDomain(p, tctx, &ld);
+ if(!NT_STATUS_EQUAL(NT_STATUS_ACCESS_DENIED, status)) {
+ printf("LookupDomain failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = torture_samr_Close(tctx, p, &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;
+
+
+ /* first we must grab the sid of the domain */
+ status = torture_samr_Connect5(tctx, p, 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 = lp_workgroup(tctx->lp_ctx);
+ status = dcerpc_samr_LookupDomain(p, tctx, &ld);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("LookupDomain failed - %s\n", nt_errstr(status));
+ 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, p, 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;
+
+ status = dcerpc_samr_OpenDomain(p, tctx, &od);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("OpenDomain failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = torture_samr_Close(tctx, p, &dh);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Close failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = torture_samr_Close(tctx, p, &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, p, &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;
+
+ 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, lp_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, lp_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, p, 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, p, 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;
+}
diff --git a/source4/torture/rpc/samsync.c b/source4/torture/rpc/samsync.c
new file mode 100644
index 0000000000..00798214f3
--- /dev/null
+++ b/source4/torture/rpc/samsync.c
@@ -0,0 +1,1715 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ test suite for netlogon rpc operations
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "auth/auth.h"
+#include "../lib/util/dlinklist.h"
+#include "../lib/crypto/crypto.h"
+#include "system/time.h"
+#include "torture/rpc/rpc.h"
+#include "auth/gensec/schannel_proto.h"
+#include "auth/gensec/gensec.h"
+#include "libcli/auth/libcli_auth.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"
+
+#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 dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct 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;
+
+ ninfo.identity_info.domain_name.string = domain;
+ ninfo.identity_info.parameter_control = 0;
+ ninfo.identity_info.logon_id_low = 0;
+ ninfo.identity_info.logon_id_high = 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);
+ SMBOWFencrypt(nt_hash->hash, ninfo.challenge, ninfo.nt.data);
+ } 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);
+ SMBOWFencrypt(lm_hash->hash, ninfo.challenge, ninfo.lm.data);
+ } 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 = 2;
+ r.in.logon = &logon;
+ r.out.validation = &validation;
+ r.out.authoritative = &authoritative;
+
+ ZERO_STRUCT(auth2);
+ creds_client_authenticator(creds, &auth);
+
+ r.in.validation_level = 3;
+
+ status = dcerpc_netr_LogonSamLogon(p, mem_ctx, &r);
+
+ if (!creds_client_check(creds, &r.out.return_authenticator->cred)) {
+ printf("Credential chaining failed\n");
+ }
+
+ if (info3) {
+ *info3 = validation.sam3;
+ }
+
+ return status;
+}
+
+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 creds_CredentialState *creds;
+ struct 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_pipe *p_netlogon_wksta;
+ struct dcerpc_pipe *p_samr;
+ struct dcerpc_pipe *p_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(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(samsync_state->p_samr, mem_ctx, &l);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ printf("LookupDomain failed - %s\n", nt_errstr(nt_status));
+ 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) {
+ *sid_p = *l.out.sid;
+ }
+
+ nt_status = dcerpc_samr_OpenDomain(samsync_state->p_samr, mem_ctx, &o);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ printf("OpenDomain failed - %s\n", nt_errstr(nt_status));
+ return NULL;
+ }
+
+ return domain_handle;
+}
+
+static struct sec_desc_buf *samsync_query_samr_sec_desc(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(samsync_state->p_samr, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SAMR QuerySecurity failed - %s\n", nt_errstr(status));
+ return NULL;
+ }
+
+ return sdbuf;
+}
+
+static struct sec_desc_buf *samsync_query_lsa_sec_desc(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(samsync_state->p_lsa, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("LSA QuerySecurity failed - %s\n", nt_errstr(status));
+ return NULL;
+ }
+
+ return sdbuf;
+}
+
+#define TEST_UINT64_EQUAL(i1, i2) do {\
+ if (i1 != i2) {\
+ printf("%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) {\
+ printf("%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) {\
+ printf("%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) {\
+ printf("%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) {\
+ printf("%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)) {\
+ printf("%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(mem_ctx, samsync_state, \
+ handle); \
+ if (!sdbuf || !sdbuf->sd) { \
+ printf("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)) {\
+ printf("Security Descriptor Mismatch for %s:\n", #sd1);\
+ ndr_print_debug((ndr_print_fn_t)ndr_print_security_descriptor, "SamSync", sd1.sd);\
+ ndr_print_debug((ndr_print_fn_t)ndr_print_security_descriptor, "SamR", sdbuf->sd);\
+ ret = false;\
+ }\
+ }\
+} while (0)
+
+static bool samsync_handle_domain(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};
+ NTSTATUS nt_status;
+ 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) {
+ printf("BUILTIN domain has different name: %s\n", domain->domain_name.string);
+ }
+ break;
+ case SAM_DATABASE_PRIVS:
+ printf("DOMAIN entry on privs DB!\n");
+ return false;
+ break;
+ }
+
+ if (!samsync_state->domain_name[database_id]) {
+ samsync_state->domain_name[database_id] =
+ talloc_reference(samsync_state, domain->domain_name.string);
+ } else {
+ if (strcasecmp_m(samsync_state->domain_name[database_id], domain->domain_name.string) != 0) {
+ printf("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]
+ = talloc_reference(samsync_state,
+ samsync_open_domain(mem_ctx, samsync_state, samsync_state->domain_name[database_id],
+ &dom_sid));
+ }
+ if (samsync_state->domain_handle[database_id]) {
+ samsync_state->sid[database_id] = talloc_reference(samsync_state, dom_sid);
+ }
+
+ printf("\tsequence_nums[%d/%s]=%llu\n",
+ database_id, domain->domain_name.string,
+ (long long)samsync_state->seq_num[database_id]);
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+
+ q[levels[i]].in.domain_handle = samsync_state->domain_handle[database_id];
+ q[levels[i]].in.level = levels[i];
+ q[levels[i]].out.info = &info[levels[i]];
+
+ nt_status = dcerpc_samr_QueryDomainInfo(samsync_state->p_samr, mem_ctx, &q[levels[i]]);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ printf("QueryDomainInfo level %u failed - %s\n",
+ q[levels[i]].in.level, nt_errstr(nt_status));
+ return false;
+ }
+ }
+
+ 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(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;
+
+ 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_reference(samsync_state, policy->primary_domain_name.string);
+ } else {
+ if (strcasecmp_m(samsync_state->domain_name[SAM_DATABASE_DOMAIN], policy->primary_domain_name.string) != 0) {
+ printf("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)) {
+ printf("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;
+ }
+
+ printf("\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;
+ 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 = samsync_state->domain_name[database_id];
+ 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 getgroups;
+ struct samr_RidWithAttributeArray *rids;
+
+ if (!samsync_state->domain_name || !samsync_state->domain_handle[database_id]) {
+ printf("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;
+
+ nt_status = dcerpc_samr_OpenUser(samsync_state->p_samr, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ printf("OpenUser(%u) failed - %s\n", rid, nt_errstr(nt_status));
+ return false;
+ }
+
+ q.in.user_handle = &user_handle;
+ q.in.level = 21;
+ q.out.info = &info;
+
+ TEST_SEC_DESC_EQUAL(user->sdbuf, samr, &user_handle);
+
+ nt_status = dcerpc_samr_QueryUserInfo(samsync_state->p_samr, mem_ctx, &q);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ printf("QueryUserInfo level %u failed - %s\n",
+ q.in.level, nt_errstr(nt_status));
+ ret = false;
+ }
+
+ getgroups.in.user_handle = &user_handle;
+ getgroups.out.rids = &rids;
+
+ nt_status = dcerpc_samr_GetGroupsForUser(samsync_state->p_samr, mem_ctx, &getgroups);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ printf("GetGroupsForUser failed - %s\n",
+ nt_errstr(nt_status));
+ ret = false;
+ }
+
+ if (!test_samr_handle_Close(samsync_state->p_samr, mem_ctx, &user_handle)) {
+ printf("samr_handle_Close failed - %s\n",
+ nt_errstr(nt_status));
+ ret = false;
+ }
+ if (!ret) {
+ return false;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ printf("QueryUserInfo level %u failed - %s\n",
+ q.in.level, nt_errstr(nt_status));
+ 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) {
+ printf("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) {
+ printf("ACB flags mismatch: both expired and no expiry!\n");
+ ret = false;
+ }
+ if (info->info21.force_password_change != (NTTIME)0x7FFFFFFFFFFFFFFFULL) {
+ printf("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) {
+ sam_rid_crypt(rid, user->lmpassword.hash, lm_hash.hash, 0);
+ lm_hash_p = &lm_hash;
+ }
+ if (user->nt_password_present) {
+ sam_rid_crypt(rid, user->ntpassword.hash, nt_hash.hash, 0);
+ 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;
+ creds_arcfour_crypt(samsync_state->creds, data.data, data.length);
+ ndr_err = ndr_pull_struct_blob(&data, mem_ctx, lp_iconv_convenience(tctx->lp_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) {
+ sam_rid_crypt(rid, keys.keys.keys2.lmpassword.pwd.hash, lm_hash.hash, 0);
+ lm_hash_p = &lm_hash;
+ }
+ if (keys.keys.keys2.ntpassword.length == 16) {
+ sam_rid_crypt(rid, keys.keys.keys2.ntpassword.pwd.hash, nt_hash.hash, 0);
+ nt_hash_p = &nt_hash;
+ }
+ } else {
+ printf("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(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(mem_ctx, &lm_hash_blob)));
+ }
+
+ nt_status = test_SamLogon(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.last_logon);
+ TEST_TIME_EQUAL(user->acct_expiry, info3->base.acct_expiry);
+ 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.last_logoff == 0x7fffffffffffffffLL))) {
+ TEST_TIME_EQUAL(user->last_logoff, info3->base.last_logoff);
+ }
+
+ 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;
+ printf("Could not find group RID %u found in getgroups in NETLOGON reply\n",
+ rids->rids[i].rid);
+ }
+ }
+ }
+ return ret;
+ } else {
+ printf("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(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;
+ NTSTATUS nt_status;
+ bool ret = true;
+
+ struct samr_OpenAlias r;
+ struct samr_QueryAliasInfo q;
+ union samr_AliasInfo *info;
+ struct policy_handle alias_handle;
+
+ if (!samsync_state->domain_name || !samsync_state->domain_handle[database_id]) {
+ printf("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;
+
+ nt_status = dcerpc_samr_OpenAlias(samsync_state->p_samr, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ printf("OpenUser(%u) failed - %s\n", rid, nt_errstr(nt_status));
+ return false;
+ }
+
+ q.in.alias_handle = &alias_handle;
+ q.in.level = 1;
+ q.out.info = &info;
+
+ TEST_SEC_DESC_EQUAL(alias->sdbuf, samr, &alias_handle);
+
+ nt_status = dcerpc_samr_QueryAliasInfo(samsync_state->p_samr, mem_ctx, &q);
+ if (!test_samr_handle_Close(samsync_state->p_samr, mem_ctx, &alias_handle)) {
+ return false;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ printf("QueryAliasInfo level %u failed - %s\n",
+ q.in.level, nt_errstr(nt_status));
+ 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(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;
+ NTSTATUS nt_status;
+ bool ret = true;
+
+ struct samr_OpenGroup r;
+ struct samr_QueryGroupInfo q;
+ union samr_GroupInfo *info;
+ struct policy_handle group_handle;
+
+ if (!samsync_state->domain_name || !samsync_state->domain_handle[database_id]) {
+ printf("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;
+
+ nt_status = dcerpc_samr_OpenGroup(samsync_state->p_samr, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ printf("OpenUser(%u) failed - %s\n", rid, nt_errstr(nt_status));
+ return false;
+ }
+
+ q.in.group_handle = &group_handle;
+ q.in.level = 1;
+ q.out.info = &info;
+
+ TEST_SEC_DESC_EQUAL(group->sdbuf, samr, &group_handle);
+
+ nt_status = dcerpc_samr_QueryGroupInfo(samsync_state->p_samr, mem_ctx, &q);
+ if (!test_samr_handle_Close(samsync_state->p_samr, mem_ctx, &group_handle)) {
+ return false;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ printf("QueryGroupInfo level %u failed - %s\n",
+ q.in.level, nt_errstr(nt_status));
+ 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(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;
+
+ creds_arcfour_crypt(samsync_state->creds, secret->current_cipher.cipher_data,
+ secret->current_cipher.maxlen);
+
+ creds_arcfour_crypt(samsync_state->creds, secret->old_cipher.cipher_data,
+ secret->old_cipher.maxlen);
+
+ nsec->name = talloc_reference(nsec, name);
+ nsec->secret = data_blob_talloc(nsec, secret->current_cipher.cipher_data, secret->current_cipher.maxlen);
+ nsec->mtime = secret->current_cipher_set_time;
+
+ nsec = talloc_reference(samsync_state, nsec);
+ DLIST_ADD(samsync_state->secrets, nsec);
+
+ old->name = talloc_reference(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;
+
+ status = dcerpc_lsa_OpenSecret(samsync_state->p_lsa, mem_ctx, &o);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("OpenSecret failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+/*
+ 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)) {
+ printf("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;
+
+ status = dcerpc_lsa_QuerySecret(samsync_state->p_lsa, mem_ctx, &q);
+ if (NT_STATUS_EQUAL(NT_STATUS_ACCESS_DENIED, status)) {
+ /* some things are just off limits */
+ return true;
+ } else if (!NT_STATUS_IS_OK(status)) {
+ printf("QuerySecret failed - %s\n", nt_errstr(status));
+ 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)) {
+ printf("Failed to decrypt secrets OLD blob: %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if (!q.out.old_mtime) {
+ printf("OLD mtime not available on LSA for secret %s\n", old->name);
+ ret = false;
+ }
+ if (old->mtime != *q.out.old_mtime) {
+ printf("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) {
+ printf("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) {
+ printf("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)) {
+ printf("Failed to decrypt secrets OLD blob\n");
+ return false;
+ }
+
+ if (!q.out.new_mtime) {
+ printf("NEW mtime not available on LSA for secret %s\n", nsec->name);
+ ret = false;
+ }
+ if (nsec->mtime != *q.out.new_mtime) {
+ printf("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) {
+ printf("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) {
+ printf("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(TALLOC_CTX *mem_ctx, struct samsync_state *samsync_state,
+ int database_id, struct netr_DELTA_ENUM *delta)
+{
+ NTSTATUS status;
+ 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_reference(ndom, trusted_domain->domain_name.string);
+ ndom->sid = talloc_reference(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;
+
+ status = dcerpc_lsa_OpenTrustedDomain(samsync_state->p_lsa, mem_ctx, &t);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("OpenTrustedDomain failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ for (i=0; i< ARRAY_SIZE(levels); i++) {
+ q.in.trustdom_handle = &trustdom_handle;
+ q.in.level = levels[i];
+ q.out.info = &_info;
+ status = dcerpc_lsa_QueryTrustedDomainInfo(samsync_state->p_lsa, mem_ctx, &q);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (q.in.level == 8 && NT_STATUS_EQUAL(status,NT_STATUS_INVALID_PARAMETER)) {
+ info[levels[i]] = NULL;
+ continue;
+ }
+ printf("QueryInfoTrustedDomain level %d failed - %s\n",
+ levels[i], nt_errstr(status));
+ 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);
+*/
+ ndom = talloc_reference(samsync_state, ndom);
+ DLIST_ADD(samsync_state->trusted_domains, ndom);
+
+ return ret;
+}
+
+static bool samsync_handle_account(TALLOC_CTX *mem_ctx, struct samsync_state *samsync_state,
+ int database_id, struct netr_DELTA_ENUM *delta)
+{
+ NTSTATUS status;
+ 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;
+
+ status = dcerpc_lsa_OpenAccount(samsync_state->p_lsa, mem_ctx, &a);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("OpenTrustedDomain failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ 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;
+
+ status = dcerpc_lsa_EnumPrivsAccount(samsync_state->p_lsa, mem_ctx, &e);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("EnumPrivsAccount failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if ((account->privilege_entries && !privs)) {
+ printf("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) {
+ printf("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;
+
+ status = dcerpc_lsa_LookupPrivName(samsync_state->p_lsa, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("\nLookupPrivName failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if (!r.out.name) {
+ printf("\nLookupPrivName failed to return a name\n");
+ return false;
+ }
+ for (j=0;j<account->privilege_entries; j++) {
+ if (strcmp(name->string, account->privilege_name[j].string) == 0) {
+ found_priv_in_lsa[j] = true;
+ break;
+ }
+ }
+ }
+ for (j=0;j<account->privilege_entries; j++) {
+ if (!found_priv_in_lsa[j]) {
+ printf("Privilage %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)
+{
+ NTSTATUS status;
+ 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;i<ARRAY_SIZE(database_ids);i++) {
+
+ uint32_t sync_context = 0;
+
+ r.in.database_id = database_ids[i];
+ r.in.sync_context = &sync_context;
+ r.out.sync_context = &sync_context;
+
+ printf("Testing DatabaseSync of id %d\n", r.in.database_id);
+
+ do {
+ loop_ctx = talloc_named(mem_ctx, 0, "DatabaseSync loop context");
+ creds_client_authenticator(samsync_state->creds, &credential);
+
+ r.in.credential = &credential;
+
+ status = dcerpc_netr_DatabaseSync(samsync_state->p, loop_ctx, &r);
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
+ printf("DatabaseSync - %s\n", nt_errstr(status));
+ ret = false;
+ break;
+ }
+
+ if (!creds_client_check(samsync_state->creds, &r.out.return_authenticator->cred)) {
+ printf("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");
+ switch (delta_enum_array->delta_enum[d].delta_type) {
+ case NETR_DELTA_DOMAIN:
+ if (!samsync_handle_domain(delta_ctx, samsync_state,
+ r.in.database_id, &delta_enum_array->delta_enum[d])) {
+ printf("Failed to handle DELTA_DOMAIN\n");
+ ret = false;
+ }
+ break;
+ case NETR_DELTA_GROUP:
+ if (!samsync_handle_group(delta_ctx, samsync_state,
+ r.in.database_id, &delta_enum_array->delta_enum[d])) {
+ printf("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])) {
+ printf("Failed to handle DELTA_USER\n");
+ ret = false;
+ }
+ break;
+ case NETR_DELTA_ALIAS:
+ if (!samsync_handle_alias(delta_ctx, samsync_state,
+ r.in.database_id, &delta_enum_array->delta_enum[d])) {
+ printf("Failed to handle DELTA_ALIAS\n");
+ ret = false;
+ }
+ break;
+ case NETR_DELTA_POLICY:
+ if (!samsync_handle_policy(delta_ctx, samsync_state,
+ r.in.database_id, &delta_enum_array->delta_enum[d])) {
+ printf("Failed to handle DELTA_POLICY\n");
+ ret = false;
+ }
+ break;
+ case NETR_DELTA_TRUSTED_DOMAIN:
+ if (!samsync_handle_trusted_domain(delta_ctx, samsync_state,
+ r.in.database_id, &delta_enum_array->delta_enum[d])) {
+ printf("Failed to handle DELTA_TRUSTED_DOMAIN\n");
+ ret = false;
+ }
+ break;
+ case NETR_DELTA_ACCOUNT:
+ if (!samsync_handle_account(delta_ctx, samsync_state,
+ r.in.database_id, &delta_enum_array->delta_enum[d])) {
+ printf("Failed to handle DELTA_ACCOUNT\n");
+ ret = false;
+ }
+ break;
+ case NETR_DELTA_SECRET:
+ if (!samsync_handle_secret(delta_ctx, samsync_state,
+ r.in.database_id, &delta_enum_array->delta_enum[d])) {
+ printf("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:
+ printf("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(status, STATUS_MORE_ENTRIES));
+
+ }
+
+ domain = samsync_state->domain_name[SAM_DATABASE_DOMAIN];
+ if (!domain) {
+ printf("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);
+
+ printf("Checking password for %s\\%s\n", t->name, username);
+ nt_status = test_SamLogon(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)) {
+ printf("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)) {
+ printf("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(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)) {
+ printf("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 samsync_state *samsync_state, TALLOC_CTX *mem_ctx)
+{
+ NTSTATUS status;
+ 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;i<ARRAY_SIZE(database_ids);i++) {
+
+ uint64_t seq_num = samsync_state->seq_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;
+
+ printf("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");
+ creds_client_authenticator(samsync_state->creds, &credential);
+
+ status = dcerpc_netr_DatabaseDeltas(samsync_state->p, loop_ctx, &r);
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED)) {
+ printf("DatabaseDeltas - %s\n", nt_errstr(status));
+ ret = false;
+ }
+
+ if (!creds_client_check(samsync_state->creds, &return_authenticator.cred)) {
+ printf("Credential chaining failed\n");
+ }
+
+ seq_num++;
+ talloc_free(loop_ctx);
+ } while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES));
+ }
+
+ return ret;
+}
+
+
+/*
+ try a netlogon DatabaseSync2
+*/
+static bool test_DatabaseSync2(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct creds_CredentialState *creds)
+{
+ NTSTATUS status;
+ 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;
+
+ 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;i<ARRAY_SIZE(database_ids);i++) {
+
+ uint32_t sync_context = 0;
+
+ r.in.database_id = database_ids[i];
+ r.in.sync_context = &sync_context;
+ r.out.sync_context = &sync_context;
+ r.in.restart_state = 0;
+
+ printf("Testing DatabaseSync2 of id %d\n", r.in.database_id);
+
+ do {
+ loop_ctx = talloc_named(mem_ctx, 0, "test_DatabaseSync2 loop context");
+ creds_client_authenticator(creds, &credential);
+
+ r.in.credential = &credential;
+
+ status = dcerpc_netr_DatabaseSync2(p, loop_ctx, &r);
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
+ printf("DatabaseSync2 - %s\n", nt_errstr(status));
+ ret = false;
+ }
+
+ if (!creds_client_check(creds, &r.out.return_authenticator->cred)) {
+ printf("Credential chaining failed\n");
+ }
+
+ talloc_free(loop_ctx);
+ } while (NT_STATUS_EQUAL(status, 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,
+ lp_workgroup(torture->lp_ctx), ACB_SVRTRUST,
+ &machine_password);
+ if (!join_ctx) {
+ talloc_free(mem_ctx);
+ printf("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, lp_workgroup(torture->lp_ctx), ACB_WSTRUST, &wksta_machine_password);
+ if (!join_ctx2) {
+ talloc_free(mem_ctx);
+ printf("Failed to join as member\n");
+ return false;
+ }
+
+ user_ctx = torture_create_testuser(torture, TEST_USER_NAME,
+ lp_workgroup(torture->lp_ctx),
+ ACB_NORMAL, NULL);
+ if (!user_ctx) {
+ talloc_free(mem_ctx);
+ printf("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->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;
+
+ status = dcerpc_samr_Connect(samsync_state->p_samr, mem_ctx, &c);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("samr_Connect failed\n");
+ ret = false;
+ goto failed;
+ }
+
+ domain_policy = samsync_open_domain(mem_ctx, samsync_state, lp_workgroup(torture->lp_ctx), NULL);
+ if (!domain_policy) {
+ printf("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)));
+ status = dcerpc_samr_SetDomainInfo(samsync_state->p_samr, mem_ctx, &s);
+
+ if (!test_samr_handle_Close(samsync_state->p_samr, mem_ctx, domain_policy)) {
+ ret = false;
+ goto failed;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetDomainInfo level %u failed - %s\n",
+ s.in.level, nt_errstr(status));
+ ret = false;
+ goto failed;
+ }
+
+
+ status = torture_rpc_connection(torture,
+ &samsync_state->p_lsa,
+ &ndr_table_lsarpc);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ ret = false;
+ goto failed;
+ }
+
+ 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;
+
+ status = dcerpc_lsa_OpenPolicy2(samsync_state->p_lsa, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("OpenPolicy2 failed - %s\n", nt_errstr(status));
+ ret = false;
+ goto failed;
+ }
+
+ status = torture_rpc_binding(torture, &b);
+ if (!NT_STATUS_IS_OK(status)) {
+ ret = false;
+ goto failed;
+ }
+
+ b->flags &= ~DCERPC_AUTH_OPTIONS;
+ b->flags |= DCERPC_SCHANNEL | DCERPC_SIGN;
+
+ credentials = cli_credentials_init(mem_ctx);
+
+ cli_credentials_set_workstation(credentials, TEST_MACHINE_NAME, CRED_SPECIFIED);
+ cli_credentials_set_domain(credentials, lp_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)) {
+ printf("Failed to connect to server as a BDC: %s\n", nt_errstr(status));
+ ret = false;
+ goto failed;
+ }
+
+ status = dcerpc_schannel_creds(samsync_state->p->conn->security_state.generic_state,
+ samsync_state, &samsync_state->creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ ret = false;
+ }
+
+
+
+ status = torture_rpc_binding(torture, &b_netlogon_wksta);
+ if (!NT_STATUS_IS_OK(status)) {
+ ret = false;
+ goto failed;
+ }
+
+ b_netlogon_wksta->flags &= ~DCERPC_AUTH_OPTIONS;
+ b_netlogon_wksta->flags |= DCERPC_SCHANNEL | DCERPC_SIGN;
+
+ 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, lp_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)) {
+ printf("Failed to connect to server as a Workstation: %s\n", nt_errstr(status));
+ ret = false;
+ goto failed;
+ }
+
+ status = dcerpc_schannel_creds(samsync_state->p_netlogon_wksta->conn->security_state.generic_state,
+ samsync_state, &samsync_state->creds_netlogon_wksta);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed to obtail schanel creds!\n");
+ ret = false;
+ }
+
+ if (!test_DatabaseSync(torture, samsync_state, mem_ctx)) {
+ printf("DatabaseSync failed\n");
+ ret = false;
+ }
+
+ if (!test_DatabaseDeltas(samsync_state, mem_ctx)) {
+ printf("DatabaseDeltas failed\n");
+ ret = false;
+ }
+
+ if (!test_DatabaseSync2(samsync_state->p, mem_ctx, samsync_state->creds)) {
+ printf("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 0000000000..b761f406ec
--- /dev/null
+++ b/source4/torture/rpc/scanner.c
@@ -0,0 +1,151 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/ndr_mgmt_c.h"
+#include "librpc/ndr/ndr_table.h"
+#include "torture/rpc/rpc.h"
+#include "param/param.h"
+#include "librpc/rpc/dcerpc_proto.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;
+ int i;
+ DATA_BLOB stub_in, stub_out;
+ int idl_calls;
+ struct ndr_interface_table tbl;
+
+ /* FIXME: This should be fixed when torture_rpc_connection
+ * takes a ndr_syntax_id */
+ tbl.name = iface->name;
+ tbl.syntax_id = *id;
+
+ status = torture_rpc_connection(tctx, &p, iface);
+ 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(NULL, 1000);
+ memset(stub_in.data, 0xFF, stub_in.length);
+
+ for (i=0;i<200;i++) {
+ status = dcerpc_request(p, NULL, i, false, mem_ctx, &stub_in, &stub_out);
+ if (!NT_STATUS_IS_OK(status) &&
+ p->last_fault_code == DCERPC_FAULT_OP_RNG_ERROR) {
+ break;
+ }
+
+ if (!NT_STATUS_IS_OK(status) && p->last_fault_code == 5) {
+ printf("\tpipe disconnected at %d\n", i);
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(status) && p->last_fault_code == 0x80010111) {
+ printf("\terr 0x80010111 at %d\n", i);
+ goto done;
+ }
+ }
+
+ printf("\t%d calls available\n", i);
+ idl_calls = ndr_interface_num_calls(&id->uuid, id->if_version);
+ if (idl_calls == -1) {
+ printf("\tinterface not known in local IDL\n");
+ } else if (i != idl_calls) {
+ printf("\tWARNING: local IDL defines %u calls\n", idl_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;
+
+ status = torture_rpc_binding(torture, &b);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ 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 (b->transport == NCACN_IP_TCP) {
+ status = dcerpc_epm_map_binding(torture, b, l->table, NULL, 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 {
+ b->endpoint = talloc_strdup(b, l->table->name);
+ }
+
+ lp_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, 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 0000000000..528a148e9b
--- /dev/null
+++ b/source4/torture/rpc/schannel.c
@@ -0,0 +1,865 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 "torture/rpc/rpc.h"
+#include "lib/cmdline/popt_common.h"
+#include "auth/gensec/schannel_proto.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 "auth/gensec/gensec.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 creds_CredentialState *creds)
+{
+ NTSTATUS status;
+ struct netr_LogonSamLogonEx r;
+ struct netr_NetworkInfo ninfo;
+ union netr_LogonLevel logon;
+ union netr_Validation validation;
+ uint8_t authoritative = 0;
+ uint32_t _flags = 0;
+ DATA_BLOB names_blob, chal, lm_resp, nt_resp;
+ int i;
+ int flags = CLI_CRED_NTLM_AUTH;
+ if (lp_client_lanman_auth(tctx->lp_ctx)) {
+ flags |= CLI_CRED_LANMAN_AUTH;
+ }
+
+ if (lp_client_ntlmv2_auth(tctx->lp_ctx)) {
+ flags |= CLI_CRED_NTLMv2_AUTH;
+ }
+
+ cli_credentials_get_ntlm_username_domain(cmdline_credentials, 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(cmdline_credentials, tctx,
+ &flags,
+ chal,
+ 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_low = 0;
+ ninfo.identity_info.logon_id_high = 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 = 2;
+ r.in.logon= &logon;
+ r.in.flags = &_flags;
+ r.out.validation = &validation;
+ r.out.authoritative = &authoritative;
+ r.out.flags = &_flags;
+
+ torture_comment(tctx,
+ "Testing LogonSamLogonEx with name %s\n",
+ ninfo.identity_info.account_name.string);
+
+ for (i=2;i<3;i++) {
+ r.in.validation_level = i;
+
+ status = dcerpc_netr_LogonSamLogonEx(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "LogonSamLogon failed");
+ }
+
+ return true;
+}
+
+/*
+ do some samr ops using the schannel connection
+ */
+static bool test_samr_ops(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ 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 = lp_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;
+
+ printf("Testing Connect and OpenDomain on BUILTIN\n");
+
+ status = dcerpc_samr_Connect(p, tctx, &connect_r);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ printf("Connect failed (expected, schannel mapped to anonymous): %s\n",
+ nt_errstr(status));
+ } else {
+ printf("Connect failed - %s\n", nt_errstr(status));
+ 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;
+
+ status = dcerpc_samr_OpenDomain(p, tctx, &opendom);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("OpenDomain failed - %s\n", nt_errstr(status));
+ return false;
+ }
+ }
+
+ printf("Testing GetDomPwInfo with name %s\n", r.in.domain_name->string);
+
+ /* do several ops to test credential chaining */
+ for (i=0;i<5;i++) {
+ status = dcerpc_samr_GetDomPwInfo(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ printf("GetDomPwInfo op %d failed - %s\n", i, nt_errstr(status));
+ 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;
+ NTSTATUS status;
+ bool ret = true;
+ struct lsa_String *account_name_p = NULL;
+ struct lsa_String *authority_name_p = NULL;
+
+ printf("\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 */
+ status = dcerpc_lsa_GetUserName(p, tctx, &r);
+
+ authority_name_p = *r.out.authority_name;
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED)) {
+ printf("not considering %s to be an error\n", nt_errstr(status));
+ } else if (!NT_STATUS_IS_OK(status)) {
+ printf("GetUserName failed - %s\n", nt_errstr(status));
+ return false;
+ } else {
+ if (!r.out.account_name) {
+ return false;
+ }
+
+ if (strcmp(account_name_p->string, "ANONYMOUS LOGON") != 0) {
+ printf("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) {
+ printf("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;
+ }
+ }
+ }
+ if (!test_many_LookupSids(p, tctx, NULL)) {
+ printf("LsaLookupSids3 failed!\n");
+ 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 creds_CredentialState *creds;
+ struct cli_credentials *credentials;
+
+ 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");
+
+ b->flags &= ~DCERPC_AUTH_OPTIONS;
+ b->flags |= dcerpc_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 with schannel");
+
+ torture_assert(tctx, test_samr_ops(tctx, p),
+ "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_secondary_connection(p, &p_netlogon,
+ b);
+ torture_assert_ntstatus_ok(tctx, status, "seconday connection");
+
+ status = dcerpc_bind_auth(p_netlogon, &ndr_table_netlogon,
+ credentials, lp_gensec_settings(tctx, tctx->lp_ctx),
+ DCERPC_AUTH_TYPE_SCHANNEL,
+ dcerpc_auth_level(p->conn),
+ NULL);
+
+ torture_assert_ntstatus_ok(tctx, status, "bind auth");
+
+ status = dcerpc_schannel_creds(p_netlogon->conn->security_state.generic_state, tctx, &creds);
+ torture_assert_ntstatus_ok(tctx, status, "schannel creds");
+
+ /* 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");
+
+ /* 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");
+
+ status = dcerpc_secondary_connection(p, &p_lsa,
+ b);
+
+ torture_assert_ntstatus_ok(tctx, status, "seconday connection");
+
+ status = dcerpc_bind_auth(p_lsa, &ndr_table_lsarpc,
+ credentials, lp_gensec_settings(tctx, tctx->lp_ctx),
+ DCERPC_AUTH_TYPE_SCHANNEL,
+ dcerpc_auth_level(p->conn),
+ NULL);
+
+ torture_assert_ntstatus_ok(tctx, status, "bind auth");
+
+ torture_assert(tctx, test_lsa_ops(tctx, p_lsa),
+ "Failed to process schannel secured LSA ops");
+
+ /* 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");
+
+ b->flags &= ~DCERPC_AUTH_OPTIONS;
+ b->flags |= dcerpc_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),
+ "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_secondary_connection(p_samr2, &p_netlogon2,
+ b);
+ torture_assert_ntstatus_ok(tctx, status, "seconday connection");
+
+ /* and now setup an SCHANNEL bind on netlogon */
+ status = dcerpc_bind_auth(p_netlogon2, &ndr_table_netlogon,
+ credentials, lp_gensec_settings(tctx, tctx->lp_ctx),
+ DCERPC_AUTH_TYPE_SCHANNEL,
+ dcerpc_auth_level(p_samr2->conn),
+ NULL);
+
+ torture_assert_ntstatus_ok(tctx, status, "auth failed");
+
+ /* 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 */
+ b->flags &= ~DCERPC_AUTH_OPTIONS;
+
+ 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;
+}
+
+
+
+/*
+ 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},
+ { ACB_WSTRUST, DCERPC_SCHANNEL | DCERPC_SEAL},
+ { ACB_WSTRUST, DCERPC_SCHANNEL | DCERPC_SIGN | DCERPC_SCHANNEL_128},
+ { ACB_WSTRUST, DCERPC_SCHANNEL | DCERPC_SEAL | DCERPC_SCHANNEL_128 },
+ { ACB_SVRTRUST, DCERPC_SCHANNEL | DCERPC_SIGN },
+ { ACB_SVRTRUST, DCERPC_SCHANNEL | DCERPC_SEAL },
+ { ACB_SVRTRUST, DCERPC_SCHANNEL | DCERPC_SIGN | DCERPC_SCHANNEL_128 },
+ { ACB_SVRTRUST, DCERPC_SCHANNEL | DCERPC_SEAL | DCERPC_SCHANNEL_128 }
+ };
+ int i;
+
+ for (i=0;i<ARRAY_SIZE(tests);i++) {
+ if (!test_schannel(torture,
+ tests[i].acct_flags, tests[i].dcerpc_flags,
+ i)) {
+ torture_comment(torture, "Failed with acct_flags=0x%x dcerpc_flags=0x%x \n",
+ tests[i].acct_flags, tests[i].dcerpc_flags);
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+/*
+ test two schannel connections
+ */
+bool torture_rpc_schannel2(struct torture_context *torture)
+{
+ struct test_join *join_ctx;
+ NTSTATUS status;
+ const char *binding = torture_setting_string(torture, "binding", NULL);
+ struct dcerpc_binding *b;
+ struct dcerpc_pipe *p1 = NULL, *p2 = NULL;
+ struct cli_credentials *credentials1, *credentials2;
+ uint32_t dcerpc_flags = DCERPC_SCHANNEL | DCERPC_SIGN;
+
+ join_ctx = torture_join_domain(torture, talloc_asprintf(torture, "%s2", TEST_MACHINE_NAME),
+ ACB_WSTRUST, &credentials1);
+ torture_assert(torture, join_ctx != NULL,
+ "Failed to join domain with acct_flags=ACB_WSTRUST");
+
+ credentials2 = (struct cli_credentials *)talloc_memdup(torture, credentials1, sizeof(*credentials1));
+ credentials1->netlogon_creds = NULL;
+ credentials2->netlogon_creds = NULL;
+
+ status = dcerpc_parse_binding(torture, binding, &b);
+ torture_assert_ntstatus_ok(torture, status, "Bad binding string");
+
+ b->flags &= ~DCERPC_AUTH_OPTIONS;
+ b->flags |= dcerpc_flags;
+
+ printf("Opening first connection\n");
+ status = dcerpc_pipe_connect_b(torture, &p1, b, &ndr_table_netlogon,
+ credentials1, torture->ev, 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");
+
+ credentials1->netlogon_creds = NULL;
+ credentials2->netlogon_creds = 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;
+};
+
+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++;
+ }
+}
+
+static void torture_schannel_bench_recv(struct rpc_request *req);
+
+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 rpc_request *req;
+ struct cli_credentials *user_creds;
+
+ if (conn->total % 2) {
+ user_creds = s->user1_creds;
+ } else {
+ user_creds = s->user2_creds;
+ }
+
+ if (lp_client_lanman_auth(s->tctx->lp_ctx)) {
+ flags |= CLI_CRED_LANMAN_AUTH;
+ }
+
+ if (lp_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,
+ 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_low = 0;
+ conn->ninfo.identity_info.logon_id_high = 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 = 2;
+ 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;
+
+ req = dcerpc_netr_LogonSamLogonEx_send(conn->pipe, conn->tmp, &conn->r);
+ torture_assert(s->tctx, req, "Failed to setup LogonSamLogonEx request");
+
+ req->async.callback = torture_schannel_bench_recv;
+ req->async.private_data = conn;
+
+ return true;
+}
+
+static void torture_schannel_bench_recv(struct rpc_request *req)
+{
+ bool ret;
+ struct torture_schannel_bench_conn *conn =
+ (struct torture_schannel_bench_conn *)req->async.private_data;
+ struct torture_schannel_bench *s = talloc_get_type(conn->s,
+ struct torture_schannel_bench);
+
+ s->error = dcerpc_ndr_request_recv(req);
+ 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 = (struct cli_credentials *)talloc_memdup(s,
+ cmdline_credentials,
+ sizeof(*s->user1_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 = (struct cli_credentials *)talloc_memdup(s,
+ cmdline_credentials,
+ sizeof(*s->user1_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_DONT_USE_KERBEROS);
+ cli_credentials_set_kerberos_state(s->wks_creds2, CRED_DONT_USE_KERBEROS);
+
+ for (i=0; i < s->nprocs; i++) {
+ s->conns[i].s = s;
+ s->conns[i].index = i;
+ s->conns[i].wks_creds = (struct cli_credentials *)talloc_memdup(
+ s->conns, s->wks_creds1,sizeof(*s->wks_creds1));
+ if ((i % 2) && (torture_setting_bool(torture, "multijoin", false))) {
+ memcpy(s->conns[i].wks_creds, s->wks_creds2,
+ talloc_get_size(s->conns[i].wks_creds));
+ }
+ s->conns[i].wks_creds->netlogon_creds = NULL;
+ }
+
+ status = dcerpc_parse_binding(s, binding, &s->b);
+ torture_assert_ntstatus_ok(torture, status, "Bad binding string");
+ s->b->flags &= ~DCERPC_AUTH_OPTIONS;
+ s->b->flags |= DCERPC_SCHANNEL | DCERPC_SIGN;
+
+ 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 = event_loop_once(torture->ev);
+ torture_assert(torture, ev_ret == 0, "event_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_str(s->join_ctx1, 8);
+ struct 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);
+ creds_des_encrypt(creds_state, &new_password);
+ creds_client_authenticator(creds_state, &credential);
+
+ status = dcerpc_netr_ServerPasswordSet(net_pipe, torture, &pwset);
+ torture_assert_ntstatus_ok(torture, status,
+ "ServerPasswordSet failed");
+
+ if (!creds_client_check(creds_state,
+ &pwset.out.return_authenticator->cred)) {
+ printf("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 */
+
+ talloc_free(s->wks_creds1->netlogon_creds);
+ s->wks_creds1->netlogon_creds = 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 = event_loop_once(torture->ev);
+ torture_assert(torture, ev_ret == 0, "event_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 0000000000..c069c62724
--- /dev/null
+++ b/source4/torture/rpc/session_key.c
@@ -0,0 +1,233 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for lsa rpc operations
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/ndr_lsa_c.h"
+
+#include "libcli/auth/libcli_auth.h"
+#include "torture/rpc/rpc.h"
+#include "lib/cmdline/popt_common.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)
+{
+ NTSTATUS status;
+ struct lsa_CreateSecret r;
+ struct lsa_SetSecret r3;
+ struct lsa_QuerySecret r4;
+ struct policy_handle sec_handle;
+ struct lsa_DeleteObject d;
+ 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, blob2;
+ const char *secret1 = "abcdef12345699qwerty";
+ char *secret2;
+ char *secname;
+
+ secname = talloc_asprintf(tctx, "torturesecret-%u", (uint_t)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;
+
+ status = dcerpc_lsa_CreateSecret(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "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");
+
+ status = dcerpc_lsa_SetSecret(p, tctx, &r3);
+ torture_assert_ntstatus_ok(tctx, status, "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");
+
+ status = dcerpc_lsa_SetSecret(p, tctx, &r3);
+ torture_assert_ntstatus_equal(tctx, status, 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");
+ status = dcerpc_lsa_QuerySecret(p, tctx, &r4);
+ torture_assert_ntstatus_ok(tctx, status, "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;
+
+ blob2 = data_blob_talloc(tctx, NULL, blob1.length);
+
+ secret2 = sess_decrypt_string(tctx, &blob1, &session_key);
+
+ torture_assert_str_equal(tctx, secret1, secret2, "Returned secret invalid");
+
+ d.in.handle = &sec_handle;
+ d.out.handle = &sec_handle;
+ status = dcerpc_lsa_DeleteObject(p, tctx, &d);
+ torture_assert_ntstatus_ok(tctx, status, "delete should have returned OKINVALID_HANDLE");
+ 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;
+
+ lp_set_cmdline(torture->lp_ctx, "ntlmssp client:keyexchange", settings->keyexchange?"True":"False");
+ lp_set_cmdline(torture->lp_ctx, "ntlmssp_client:ntlm2", settings->ntlm2?"True":"False");
+ lp_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");
+
+ binding->flags |= settings->bindoptions;
+
+ torture_assert_ntstatus_ok(torture,
+ dcerpc_pipe_connect_b(torture, &p, binding,
+ &ndr_table_lsarpc,
+ cmdline_credentials,
+ torture->ev,
+ torture->lp_ctx),
+ "connect");
+
+ if (!test_lsa_OpenPolicy2(p, torture, &handle)) {
+ return false;
+ }
+
+ torture_assert(torture, handle, "OpenPolicy2 failed. This test cannot run against this server");
+
+ if (!test_CreateSecret_basic(p, torture, handle)) {
+ return false;
+ }
+
+ return true;
+}
+
+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, "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 0000000000..bfe667240c
--- /dev/null
+++ b/source4/torture/rpc/spoolss.c
@@ -0,0 +1,2058 @@
+/*
+ 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
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "torture/rpc/rpc.h"
+#include "librpc/gen_ndr/ndr_spoolss_c.h"
+
+struct test_spoolss_context {
+ /* 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[7];
+ union spoolss_DriverInfo *drivers[7];
+
+ /* 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];
+};
+
+#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_STRING_ARRAY(tctx, c,r,e)
+
+static bool test_OpenPrinter_server(struct torture_context *tctx, struct dcerpc_pipe *p, struct test_spoolss_context *ctx)
+{
+ NTSTATUS status;
+ struct spoolss_OpenPrinter op;
+
+ op.in.printername = talloc_asprintf(ctx, "\\\\%s", dcerpc_server_name(p));
+ op.in.datatype = NULL;
+ op.in.devmode_ctr.devmode= NULL;
+ op.in.access_mask = 0;
+ op.out.handle = &ctx->server_handle;
+
+ torture_comment(tctx, "Testing OpenPrinter(%s)\n", op.in.printername);
+
+ status = dcerpc_spoolss_OpenPrinter(p, ctx, &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,
+ struct dcerpc_pipe *p,
+ struct test_spoolss_context *ctx)
+{
+ NTSTATUS status;
+ struct spoolss_EnumPorts r;
+ uint16_t levels[] = { 1, 2 };
+ int i, j;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ int level = levels[i];
+ DATA_BLOB blob;
+ uint32_t needed;
+ uint32_t count;
+ union spoolss_PortInfo *info;
+
+ r.in.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 EnumPorts level %u\n", r.in.level);
+
+ status = dcerpc_spoolss_EnumPorts(p, ctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EnumPorts failed");
+ if (W_ERROR_IS_OK(r.out.result)) {
+ /* TODO: do some more checks here */
+ continue;
+ }
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INSUFFICIENT_BUFFER,
+ "EnumPorts unexpected return code");
+
+ blob = data_blob_talloc(ctx, NULL, needed);
+ data_blob_clear(&blob);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+
+ status = dcerpc_spoolss_EnumPorts(p, ctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EnumPorts failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "EnumPorts failed");
+
+ torture_assert(tctx, info, "EnumPorts returned no info");
+
+ ctx->port_count[level] = count;
+ ctx->ports[level] = info;
+ }
+
+ for (i=1;i<ARRAY_SIZE(levels);i++) {
+ int level = levels[i];
+ int old_level = levels[i-1];
+ torture_assert_int_equal(tctx, ctx->port_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;i<ARRAY_SIZE(levels);i++) {
+ int level = levels[i];
+ for (j=0;j<ctx->port_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,
+ struct dcerpc_pipe *p,
+ struct test_spoolss_context *ctx)
+{
+ NTSTATUS status;
+ 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(ctx, "\\\\%s", dcerpc_server_name(p))
+ },{
+ .level = 1024,
+ .server = talloc_asprintf(ctx, "\\\\%s", dcerpc_server_name(p))
+ }
+ };
+ int i;
+ uint32_t needed;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ int level = levels[i].level;
+ DATA_BLOB blob;
+
+ r.in.server = levels[i].server;
+ r.in.environment = SPOOLSS_ARCHITECTURE_NT_X86;
+ 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(p, ctx, &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(ctx, NULL, needed);
+ data_blob_clear(&blob);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+
+ status = dcerpc_spoolss_GetPrintProcessorDirectory(p, ctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_GetPrintProcessorDirectory failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "GetPrintProcessorDirectory failed");
+ }
+
+ return true;
+}
+
+
+static bool test_GetPrinterDriverDirectory(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct test_spoolss_context *ctx)
+{
+ NTSTATUS status;
+ 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(ctx, "\\\\%s", dcerpc_server_name(p))
+ },{
+ .level = 1024,
+ .server = talloc_asprintf(ctx, "\\\\%s", dcerpc_server_name(p))
+ }
+ };
+ int i;
+ uint32_t needed;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ int level = levels[i].level;
+ DATA_BLOB blob;
+
+ r.in.server = levels[i].server;
+ r.in.environment = SPOOLSS_ARCHITECTURE_NT_X86;
+ 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(p, ctx, &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(ctx, NULL, needed);
+ data_blob_clear(&blob);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+
+ status = dcerpc_spoolss_GetPrinterDriverDirectory(p, ctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_GetPrinterDriverDirectory failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "GetPrinterDriverDirectory failed");
+ }
+
+ return true;
+}
+
+static bool test_EnumPrinterDrivers(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct test_spoolss_context *ctx)
+{
+ NTSTATUS status;
+ struct spoolss_EnumPrinterDrivers r;
+ uint16_t levels[] = { 1, 2, 3, 4, 5, 6 };
+ int i, j;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ int level = levels[i];
+ DATA_BLOB blob;
+ uint32_t needed;
+ uint32_t count;
+ union spoolss_DriverInfo *info;
+
+ /* FIXME: gd, come back and fix "" as server, and handle
+ * priority of returned error codes in torture test and samba 3
+ * server */
+
+ r.in.server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.environment = SPOOLSS_ARCHITECTURE_NT_X86;
+ 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 EnumPrinterDrivers level %u\n", r.in.level);
+
+ status = dcerpc_spoolss_EnumPrinterDrivers(p, ctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "dcerpc_spoolss_EnumPrinterDrivers failed");
+ if (W_ERROR_IS_OK(r.out.result)) {
+ /* TODO: do some more checks here */
+ continue;
+ }
+ if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ blob = data_blob_talloc(ctx, NULL, needed);
+ data_blob_clear(&blob);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+
+ status = dcerpc_spoolss_EnumPrinterDrivers(p, ctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EnumPrinterDrivers failed");
+ }
+
+ torture_assert_werr_ok(tctx, r.out.result, "EnumPrinterDrivers failed");
+
+ ctx->driver_count[level] = count;
+ ctx->drivers[level] = info;
+ }
+
+ for (i=1;i<ARRAY_SIZE(levels);i++) {
+ int level = levels[i];
+ int old_level = levels[i-1];
+ torture_assert_int_equal(tctx, ctx->driver_count[level], ctx->driver_count[old_level],
+ "EnumPrinterDrivers invalid value");
+ }
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ int level = levels[i];
+ for (j=0;j<ctx->driver_count[level];j++) {
+ union spoolss_DriverInfo *cur = &ctx->drivers[level][j];
+ union spoolss_DriverInfo *ref = &ctx->drivers[6][j];
+ switch (level) {
+ case 1:
+ COMPARE_STRING(tctx, cur->info1, ref->info6, driver_name);
+ break;
+ case 2:
+ COMPARE_UINT32(tctx, cur->info2, ref->info6, version);
+ COMPARE_STRING(tctx, cur->info2, ref->info6, driver_name);
+ COMPARE_STRING(tctx, cur->info2, ref->info6, architecture);
+ COMPARE_STRING(tctx, cur->info2, ref->info6, driver_path);
+ COMPARE_STRING(tctx, cur->info2, ref->info6, data_file);
+ COMPARE_STRING(tctx, cur->info2, ref->info6, config_file);
+ break;
+ case 3:
+ COMPARE_UINT32(tctx, cur->info3, ref->info6, version);
+ COMPARE_STRING(tctx, cur->info3, ref->info6, driver_name);
+ COMPARE_STRING(tctx, cur->info3, ref->info6, architecture);
+ COMPARE_STRING(tctx, cur->info3, ref->info6, driver_path);
+ COMPARE_STRING(tctx, cur->info3, ref->info6, data_file);
+ COMPARE_STRING(tctx, cur->info3, ref->info6, config_file);
+ COMPARE_STRING(tctx, cur->info3, ref->info6, help_file);
+ COMPARE_STRING_ARRAY(tctx, cur->info3, ref->info6, dependent_files);
+ COMPARE_STRING(tctx, cur->info3, ref->info6, monitor_name);
+ COMPARE_STRING(tctx, cur->info3, ref->info6, default_datatype);
+ break;
+ case 4:
+ COMPARE_UINT32(tctx, cur->info4, ref->info6, version);
+ COMPARE_STRING(tctx, cur->info4, ref->info6, driver_name);
+ COMPARE_STRING(tctx, cur->info4, ref->info6, architecture);
+ COMPARE_STRING(tctx, cur->info4, ref->info6, driver_path);
+ COMPARE_STRING(tctx, cur->info4, ref->info6, data_file);
+ COMPARE_STRING(tctx, cur->info4, ref->info6, config_file);
+ COMPARE_STRING(tctx, cur->info4, ref->info6, help_file);
+ COMPARE_STRING_ARRAY(tctx, cur->info4, ref->info6, dependent_files);
+ COMPARE_STRING(tctx, cur->info4, ref->info6, monitor_name);
+ COMPARE_STRING(tctx, cur->info4, ref->info6, default_datatype);
+ COMPARE_STRING_ARRAY(tctx, cur->info4, ref->info6, previous_names);
+ break;
+ case 5:
+ COMPARE_UINT32(tctx, cur->info5, ref->info6, version);
+ COMPARE_STRING(tctx, cur->info5, ref->info6, driver_name);
+ COMPARE_STRING(tctx, cur->info5, ref->info6, architecture);
+ COMPARE_STRING(tctx, cur->info5, ref->info6, driver_path);
+ COMPARE_STRING(tctx, cur->info5, ref->info6, data_file);
+ COMPARE_STRING(tctx, cur->info5, ref->info6, config_file);
+ /*COMPARE_UINT32(tctx, cur->info5, ref->info6, driver_attributes);*/
+ /*COMPARE_UINT32(tctx, cur->info5, ref->info6, config_version);*/
+ /*TODO: ! COMPARE_UINT32(tctx, cur->info5, ref->info6, driver_version); */
+ break;
+ case 6:
+ /* level 6 is our reference, and it makes no sense to compare it to itself */
+ break;
+ }
+ }
+ }
+
+ return true;
+}
+
+static bool test_EnumMonitors(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct test_spoolss_context *ctx)
+{
+ NTSTATUS status;
+ struct spoolss_EnumMonitors r;
+ uint16_t levels[] = { 1, 2 };
+ int i, j;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ int level = levels[i];
+ DATA_BLOB blob;
+ uint32_t needed;
+ uint32_t count;
+ union spoolss_MonitorInfo *info;
+
+ r.in.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 EnumMonitors level %u\n", r.in.level);
+
+ status = dcerpc_spoolss_EnumMonitors(p, ctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EnumMonitors failed");
+ if (W_ERROR_IS_OK(r.out.result)) {
+ /* TODO: do some more checks here */
+ continue;
+ }
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INSUFFICIENT_BUFFER,
+ "EnumMonitors failed");
+
+ blob = data_blob_talloc(ctx, NULL, needed);
+ data_blob_clear(&blob);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+
+ status = dcerpc_spoolss_EnumMonitors(p, ctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EnumMonitors failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "EnumMonitors failed");
+
+ ctx->monitor_count[level] = count;
+ ctx->monitors[level] = info;
+ }
+
+ for (i=1;i<ARRAY_SIZE(levels);i++) {
+ int level = levels[i];
+ int old_level = levels[i-1];
+ torture_assert_int_equal(tctx, ctx->monitor_count[level], ctx->monitor_count[old_level],
+ "EnumMonitors invalid value");
+ }
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ int level = levels[i];
+ for (j=0;j<ctx->monitor_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:
+ /* level 2 is our reference, and it makes no sense to compare it to itself */
+ break;
+ }
+ }
+ }
+
+ return true;
+}
+
+static bool test_EnumPrintProcessors(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct test_spoolss_context *ctx)
+{
+ NTSTATUS status;
+ struct spoolss_EnumPrintProcessors r;
+ uint16_t levels[] = { 1 };
+ int i, j;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ int level = levels[i];
+ DATA_BLOB blob;
+ uint32_t needed;
+ uint32_t count;
+ union spoolss_PrintProcessorInfo *info;
+
+ r.in.servername = "";
+ r.in.environment = "Windows NT x86";
+ 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 level %u\n", r.in.level);
+
+ status = dcerpc_spoolss_EnumPrintProcessors(p, ctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EnumPrintProcessors failed");
+ if (W_ERROR_IS_OK(r.out.result)) {
+ /* TODO: do some more checks here */
+ continue;
+ }
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INSUFFICIENT_BUFFER,
+ "EnumPrintProcessors unexpected return code");
+
+ blob = data_blob_talloc(ctx, NULL, needed);
+ data_blob_clear(&blob);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+
+ status = dcerpc_spoolss_EnumPrintProcessors(p, ctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EnumPrintProcessors failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "EnumPrintProcessors failed");
+
+ ctx->print_processor_count[level] = count;
+ ctx->print_processors[level] = info;
+ }
+
+ for (i=1;i<ARRAY_SIZE(levels);i++) {
+ int level = levels[i];
+ int old_level = levels[i-1];
+ torture_assert_int_equal(tctx, ctx->print_processor_count[level], ctx->print_processor_count[old_level],
+ "EnumPrintProcessors failed");
+ }
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ int level = levels[i];
+ for (j=0;j<ctx->print_processor_count[level];j++) {
+#if 0
+ union spoolss_PrintProcessorInfo *cur = &ctx->print_processors[level][j];
+ union spoolss_PrintProcessorInfo *ref = &ctx->print_processors[1][j];
+#endif
+ switch (level) {
+ case 1:
+ /* level 1 is our reference, and it makes no sense to compare it to itself */
+ break;
+ }
+ }
+ }
+
+ return true;
+}
+
+static bool test_EnumPrintProcDataTypes(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct test_spoolss_context *ctx)
+{
+ NTSTATUS status;
+ struct spoolss_EnumPrintProcDataTypes r;
+ uint16_t levels[] = { 1 };
+ int i;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ int level = levels[i];
+ DATA_BLOB blob;
+ uint32_t needed;
+ uint32_t count;
+ union spoolss_PrintProcDataTypesInfo *info;
+
+ r.in.servername = "";
+ r.in.print_processor_name = "winprint";
+ 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 EnumPrintProcDataTypes level %u\n", r.in.level);
+
+ status = dcerpc_spoolss_EnumPrintProcDataTypes(p, ctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EnumPrintProcDataType failed");
+ if (W_ERROR_IS_OK(r.out.result)) {
+ /* TODO: do some more checks here */
+ continue;
+ }
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INSUFFICIENT_BUFFER,
+ "EnumPrintProcDataTypes unexpected return code");
+
+ blob = data_blob_talloc(ctx, NULL, needed);
+ data_blob_clear(&blob);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+
+ status = dcerpc_spoolss_EnumPrintProcDataTypes(p, ctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EnumPrintProcDataTypes failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "EnumPrintProcDataTypes failed");
+ }
+
+ return true;
+}
+
+
+static bool test_EnumPrinters(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct test_spoolss_context *ctx)
+{
+ struct spoolss_EnumPrinters r;
+ NTSTATUS status;
+ uint16_t levels[] = { 0, 1, 2, 4, 5 };
+ int i, j;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ int level = levels[i];
+ DATA_BLOB blob;
+ uint32_t needed;
+ uint32_t count;
+ union spoolss_PrinterInfo *info;
+
+ r.in.flags = PRINTER_ENUM_LOCAL;
+ r.in.server = "";
+ 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 level %u\n", r.in.level);
+
+ status = dcerpc_spoolss_EnumPrinters(p, ctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EnumPrinters failed");
+ if (W_ERROR_IS_OK(r.out.result)) {
+ /* TODO: do some more checks here */
+ continue;
+ }
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INSUFFICIENT_BUFFER,
+ "EnumPrinters unexpected return code");
+
+ blob = data_blob_talloc(ctx, NULL, needed);
+ data_blob_clear(&blob);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+
+ status = dcerpc_spoolss_EnumPrinters(p, ctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EnumPrinters failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "EnumPrinters failed");
+
+ ctx->printer_count[level] = count;
+ ctx->printers[level] = info;
+ }
+
+ for (i=1;i<ARRAY_SIZE(levels);i++) {
+ int level = levels[i];
+ int old_level = levels[i-1];
+ torture_assert_int_equal(tctx, ctx->printer_count[level], ctx->printer_count[old_level],
+ "EnumPrinters invalid value");
+ }
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ int level = levels[i];
+ for (j=0;j<ctx->printer_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_GetPrinter(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct spoolss_GetPrinter r;
+ uint16_t levels[] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
+ int i;
+ uint32_t needed;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ r.in.handle = handle;
+ r.in.level = levels[i];
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+ r.out.needed = &needed;
+
+ torture_comment(tctx, "Testing GetPrinter level %u\n", r.in.level);
+
+ status = dcerpc_spoolss_GetPrinter(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "GetPrinter failed");
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ DATA_BLOB blob = data_blob_talloc(tctx, NULL, needed);
+ data_blob_clear(&blob);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+ status = dcerpc_spoolss_GetPrinter(p, tctx, &r);
+ }
+
+ torture_assert_ntstatus_ok(tctx, status, "GetPrinter failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "GetPrinter failed");
+ }
+
+ return true;
+}
+
+
+static bool test_ClosePrinter(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ 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(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "ClosePrinter failed");
+
+ return true;
+}
+
+static bool test_GetForm(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle,
+ const char *form_name,
+ uint32_t level)
+{
+ 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 level %d\n", r.in.level);
+
+ status = dcerpc_spoolss_GetForm(p, 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(tctx, NULL, needed);
+ data_blob_clear(&blob);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+ status = dcerpc_spoolss_GetForm(p, 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");
+
+ return true;
+}
+
+static bool test_EnumForms(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle, bool print_server)
+{
+ NTSTATUS status;
+ struct spoolss_EnumForms r;
+ bool ret = true;
+ uint32_t needed;
+ uint32_t count;
+ uint32_t levels[] = { 1, 2 };
+ int i;
+
+ for (i=0; i<ARRAY_SIZE(levels); i++) {
+
+ union spoolss_FormInfo *info;
+
+ r.in.handle = handle;
+ r.in.level = levels[i];
+ 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", levels[i]);
+
+ status = dcerpc_spoolss_EnumForms(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "EnumForms failed");
+
+ if ((r.in.level == 2) && (W_ERROR_EQUAL(r.out.result, WERR_UNKNOWN_LEVEL))) {
+ break;
+ }
+
+ if (print_server && W_ERROR_EQUAL(r.out.result, WERR_BADFID))
+ torture_fail(tctx, "EnumForms on the PrintServer isn't supported by test server (NT4)");
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ int j;
+ DATA_BLOB blob = data_blob_talloc(tctx, NULL, needed);
+ data_blob_clear(&blob);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+
+ status = dcerpc_spoolss_EnumForms(p, tctx, &r);
+
+ torture_assert(tctx, info, "No forms returned");
+
+ for (j = 0; j < count; j++) {
+ if (!print_server)
+ ret &= test_GetForm(tctx, p, handle, info[j].info1.form_name, levels[i]);
+ }
+ }
+
+ torture_assert_ntstatus_ok(tctx, status, "EnumForms failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "EnumForms failed");
+ }
+
+ return true;
+}
+
+static bool test_DeleteForm(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle,
+ const char *form_name)
+{
+ NTSTATUS status;
+ struct spoolss_DeleteForm r;
+
+ r.in.handle = handle;
+ r.in.form_name = form_name;
+
+ status = dcerpc_spoolss_DeleteForm(p, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "DeleteForm failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "DeleteForm failed");
+
+ return true;
+}
+
+static bool test_AddForm(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle, bool print_server)
+{
+ struct spoolss_AddForm r;
+ struct spoolss_AddFormInfo1 addform;
+ const char *form_name = "testform3";
+ NTSTATUS status;
+ bool ret = true;
+
+ r.in.handle = handle;
+ r.in.level = 1;
+ r.in.info.info1 = &addform;
+ addform.flags = SPOOLSS_FORM_USER;
+ addform.form_name = form_name;
+ addform.size.width = 50;
+ addform.size.height = 25;
+ addform.area.left = 5;
+ addform.area.top = 10;
+ addform.area.right = 45;
+ addform.area.bottom = 15;
+
+ status = dcerpc_spoolss_AddForm(p, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "AddForm failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "AddForm failed");
+
+ if (!print_server) ret &= test_GetForm(tctx, p, handle, form_name, 1);
+
+ {
+ struct spoolss_SetForm sf;
+ struct spoolss_AddFormInfo1 setform;
+
+ sf.in.handle = handle;
+ sf.in.form_name = form_name;
+ sf.in.level = 1;
+ sf.in.info.info1= &setform;
+ setform.flags = addform.flags;
+ setform.form_name = addform.form_name;
+ setform.size = addform.size;
+ setform.area = addform.area;
+
+ setform.size.width = 1234;
+
+ status = dcerpc_spoolss_SetForm(p, tctx, &sf);
+
+ torture_assert_ntstatus_ok(tctx, status, "SetForm failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "SetForm failed");
+ }
+
+ if (!print_server) ret &= test_GetForm(tctx, p, handle, form_name, 1);
+
+ if (!test_DeleteForm(tctx, p, handle, form_name)) {
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool test_EnumPorts_old(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct spoolss_EnumPorts r;
+ uint32_t needed;
+ uint32_t count;
+ union spoolss_PortInfo *info;
+
+ 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(p, 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(tctx, NULL, needed);
+ data_blob_clear(&blob);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+
+ status = dcerpc_spoolss_EnumPorts(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "EnumPorts failed");
+
+ torture_assert(tctx, info, "No ports returned");
+ }
+
+ return true;
+}
+
+static bool test_AddPort(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct spoolss_AddPort r;
+
+ 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(p, 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(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle, uint32_t job_id)
+{
+ NTSTATUS status;
+ struct spoolss_GetJob r;
+ uint32_t needed;
+ uint32_t levels[] = {1, 2 /* 3, 4 */};
+ uint32_t i;
+
+ r.in.handle = handle;
+ r.in.job_id = job_id;
+ r.in.level = 0;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+ r.out.needed = &needed;
+
+ torture_comment(tctx, "Testing GetJob level %d\n", r.in.level);
+
+ status = dcerpc_spoolss_GetJob(p, tctx, &r);
+ torture_assert_werr_equal(tctx, r.out.result, WERR_UNKNOWN_LEVEL, "Unexpected return code");
+
+ for (i = 0; i < ARRAY_SIZE(levels); i++) {
+
+ torture_comment(tctx, "Testing GetJob level %d\n", r.in.level);
+
+ r.in.level = levels[i];
+ r.in.offered = 0;
+
+ status = dcerpc_spoolss_GetJob(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "GetJob failed");
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ DATA_BLOB blob = data_blob_talloc(tctx, NULL, needed);
+ data_blob_clear(&blob);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+
+ status = dcerpc_spoolss_GetJob(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "GetJob failed");
+
+ }
+ torture_assert(tctx, r.out.info, "No job info returned");
+ torture_assert_werr_ok(tctx, r.out.result, "GetJob failed");
+ }
+
+ return true;
+}
+
+static bool test_SetJob(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle, uint32_t job_id,
+ enum spoolss_JobControl command)
+{
+ NTSTATUS status;
+ struct spoolss_SetJob r;
+
+ r.in.handle = handle;
+ r.in.job_id = job_id;
+ r.in.ctr = NULL;
+ r.in.command = command;
+
+ switch (command) {
+ case SPOOLSS_JOB_CONTROL_PAUSE:
+ torture_comment(tctx, "Testing SetJob: SPOOLSS_JOB_CONTROL_PAUSE\n");
+ break;
+ case SPOOLSS_JOB_CONTROL_RESUME:
+ torture_comment(tctx, "Testing SetJob: SPOOLSS_JOB_CONTROL_RESUME\n");
+ break;
+ case SPOOLSS_JOB_CONTROL_CANCEL:
+ torture_comment(tctx, "Testing SetJob: SPOOLSS_JOB_CONTROL_CANCEL\n");
+ break;
+ case SPOOLSS_JOB_CONTROL_RESTART:
+ torture_comment(tctx, "Testing SetJob: SPOOLSS_JOB_CONTROL_RESTART\n");
+ break;
+ case SPOOLSS_JOB_CONTROL_DELETE:
+ torture_comment(tctx, "Testing SetJob: SPOOLSS_JOB_CONTROL_DELETE\n");
+ break;
+ case SPOOLSS_JOB_CONTROL_SEND_TO_PRINTER:
+ torture_comment(tctx, "Testing SetJob: SPOOLSS_JOB_CONTROL_SEND_TO_PRINTER\n");
+ break;
+ case SPOOLSS_JOB_CONTROL_LAST_PAGE_EJECTED:
+ torture_comment(tctx, "Testing SetJob: SPOOLSS_JOB_CONTROL_LAST_PAGE_EJECTED\n");
+ break;
+ case SPOOLSS_JOB_CONTROL_RETAIN:
+ torture_comment(tctx, "Testing SetJob: SPOOLSS_JOB_CONTROL_RETAIN\n");
+ break;
+ case SPOOLSS_JOB_CONTROL_RELEASE:
+ torture_comment(tctx, "Testing SetJob: SPOOLSS_JOB_CONTROL_RELEASE\n");
+ break;
+ default:
+ torture_comment(tctx, "Testing SetJob\n");
+ break;
+ }
+
+ status = dcerpc_spoolss_SetJob(p, 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_pipe *p,
+ 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(p, tctx, &r);
+ torture_assert_werr_equal(tctx, r.out.result, WERR_UNKNOWN_LEVEL, "AddJob failed");
+
+ r.in.level = 1;
+
+ status = dcerpc_spoolss_AddJob(p, tctx, &r);
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_PARAM, "AddJob failed");
+
+ return true;
+}
+
+
+static bool test_EnumJobs(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle)
+{
+ 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 = 1;
+ 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\n");
+
+ status = dcerpc_spoolss_EnumJobs(p, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "EnumJobs failed");
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ int j;
+ DATA_BLOB blob = data_blob_talloc(tctx, NULL, needed);
+ data_blob_clear(&blob);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+
+ status = dcerpc_spoolss_EnumJobs(p, tctx, &r);
+
+ torture_assert(tctx, info, "No jobs returned");
+
+ for (j = 0; j < count; j++) {
+
+ test_GetJob(tctx, p, handle, info[j].info1.job_id);
+
+ /* FIXME - gd */
+ if (!torture_setting_bool(tctx, "samba3", false)) {
+ test_SetJob(tctx, p, handle, info[j].info1.job_id, SPOOLSS_JOB_CONTROL_PAUSE);
+ test_SetJob(tctx, p, handle, info[j].info1.job_id, SPOOLSS_JOB_CONTROL_RESUME);
+ }
+ }
+
+ } else {
+ torture_assert_werr_ok(tctx, r.out.result, "EnumJobs failed");
+ }
+
+ return true;
+}
+
+static bool test_DoPrintTest(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle)
+{
+ bool ret = true;
+ NTSTATUS status;
+ struct spoolss_StartDocPrinter s;
+ struct spoolss_DocumentInfo1 info1;
+ struct spoolss_StartPagePrinter sp;
+ struct spoolss_WritePrinter w;
+ struct spoolss_EndPagePrinter ep;
+ struct spoolss_EndDocPrinter e;
+ int i;
+ uint32_t job_id;
+ uint32_t num_written;
+
+ torture_comment(tctx, "Testing StartDocPrinter\n");
+
+ s.in.handle = handle;
+ s.in.level = 1;
+ s.in.info.info1 = &info1;
+ s.out.job_id = &job_id;
+ info1.document_name = "TorturePrintJob";
+ info1.output_file = NULL;
+ info1.datatype = "RAW";
+
+ status = dcerpc_spoolss_StartDocPrinter(p, 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++) {
+ torture_comment(tctx, "Testing StartPagePrinter: Page[%d]\n", i);
+
+ sp.in.handle = handle;
+
+ status = dcerpc_spoolss_StartPagePrinter(p, tctx, &sp);
+ torture_assert_ntstatus_ok(tctx, status,
+ "dcerpc_spoolss_StartPagePrinter failed");
+ torture_assert_werr_ok(tctx, sp.out.result, "StartPagePrinter failed");
+
+ torture_comment(tctx, "Testing WritePrinter: Page[%d]\n", i);
+
+ 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(p, 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]\n", i);
+
+ ep.in.handle = handle;
+
+ status = dcerpc_spoolss_EndPagePrinter(p, 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\n");
+
+ e.in.handle = handle;
+
+ status = dcerpc_spoolss_EndDocPrinter(p, tctx, &e);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EndDocPrinter failed");
+ torture_assert_werr_ok(tctx, e.out.result, "EndDocPrinter failed");
+
+ ret &= test_AddJob(tctx, p, handle);
+ ret &= test_EnumJobs(tctx, p, handle);
+
+ ret &= test_SetJob(tctx, p, handle, job_id, SPOOLSS_JOB_CONTROL_DELETE);
+
+ return ret;
+}
+
+static bool test_PausePrinter(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct spoolss_SetPrinter r;
+ 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 = SPOOLSS_PRINTER_CONTROL_PAUSE;
+
+ torture_comment(tctx, "Testing SetPrinter: SPOOLSS_PRINTER_CONTROL_PAUSE\n");
+
+ status = dcerpc_spoolss_SetPrinter(p, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "SetPrinter failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "SetPrinter failed");
+
+ return true;
+}
+
+static bool test_ResumePrinter(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct spoolss_SetPrinter r;
+ 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 = SPOOLSS_PRINTER_CONTROL_RESUME;
+
+ torture_comment(tctx, "Testing SetPrinter: SPOOLSS_PRINTER_CONTROL_RESUME\n");
+
+ status = dcerpc_spoolss_SetPrinter(p, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "SetPrinter failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "SetPrinter failed");
+
+ return true;
+}
+
+static bool test_GetPrinterData(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle,
+ const char *value_name)
+{
+ NTSTATUS status;
+ struct spoolss_GetPrinterData r;
+ uint32_t needed;
+ enum winreg_Type type;
+ union spoolss_PrinterData data;
+
+ r.in.handle = handle;
+ r.in.value_name = value_name;
+ r.in.offered = 0;
+ r.out.needed = &needed;
+ r.out.type = &type;
+ r.out.data = &data;
+
+ torture_comment(tctx, "Testing GetPrinterData\n");
+
+ status = dcerpc_spoolss_GetPrinterData(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "GetPrinterData failed");
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA)) {
+ r.in.offered = needed;
+
+ status = dcerpc_spoolss_GetPrinterData(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "GetPrinterData failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "GetPrinterData failed");
+ }
+
+ 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)
+{
+ NTSTATUS status;
+ struct spoolss_GetPrinterDataEx r;
+ enum winreg_Type type;
+ uint32_t needed;
+
+ 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.buffer = NULL;
+
+ torture_comment(tctx, "Testing GetPrinterDataEx\n");
+
+ status = dcerpc_spoolss_GetPrinterDataEx(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (NT_STATUS_EQUAL(status,NT_STATUS_NET_WRITE_FAULT) &&
+ p->last_fault_code == DCERPC_FAULT_OP_RNG_ERROR) {
+ 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)) {
+ r.in.offered = needed;
+ r.out.buffer = talloc_array(tctx, uint8_t, needed);
+
+ status = dcerpc_spoolss_GetPrinterDataEx(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "GetPrinterDataEx failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "GetPrinterDataEx failed");
+ }
+
+ return true;
+}
+
+static bool test_EnumPrinterData(struct torture_context *tctx, struct dcerpc_pipe *p,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct spoolss_EnumPrinterData r;
+
+ ZERO_STRUCT(r);
+ r.in.handle = handle;
+ r.in.enum_index = 0;
+
+ do {
+ uint32_t value_size = 0;
+ uint32_t data_size = 0;
+ enum winreg_Type type = 0;
+
+ r.in.value_offered = value_size;
+ r.out.value_needed = &value_size;
+ r.in.data_offered = data_size;
+ r.out.data_needed = &data_size;
+
+ r.out.type = &type;
+ r.out.data = talloc_zero_array(tctx, uint8_t, 0);
+
+ torture_comment(tctx, "Testing EnumPrinterData\n");
+
+ status = dcerpc_spoolss_EnumPrinterData(p, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "EnumPrinterData failed");
+
+ r.in.value_offered = value_size;
+ r.out.value_name = talloc_zero_array(tctx, const char, value_size);
+ r.in.data_offered = data_size;
+ r.out.data = talloc_zero_array(tctx, uint8_t, data_size);
+
+ status = dcerpc_spoolss_EnumPrinterData(p, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "EnumPrinterData failed");
+
+ test_GetPrinterData(tctx, p, handle, r.out.value_name);
+
+ test_GetPrinterDataEx(tctx,
+ p, handle, "PrinterDriverData",
+ r.out.value_name);
+
+ r.in.enum_index++;
+
+ } while (W_ERROR_IS_OK(r.out.result));
+
+ return true;
+}
+
+static bool test_EnumPrinterDataEx(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct spoolss_EnumPrinterDataEx r;
+ struct spoolss_PrinterEnumValues *info;
+ uint32_t needed;
+ uint32_t count;
+
+ r.in.handle = handle;
+ r.in.key_name = "PrinterDriverData";
+ r.in.offered = 0;
+ r.out.needed = &needed;
+ r.out.count = &count;
+ r.out.info = &info;
+
+ torture_comment(tctx, "Testing EnumPrinterDataEx\n");
+
+ status = dcerpc_spoolss_EnumPrinterDataEx(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "EnumPrinterDataEx failed");
+
+ r.in.offered = needed;
+
+ status = dcerpc_spoolss_EnumPrinterDataEx(p, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "EnumPrinterDataEx failed");
+
+ return true;
+}
+
+
+static bool test_DeletePrinterData(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ 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\n");
+
+ status = dcerpc_spoolss_DeletePrinterData(p, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "DeletePrinterData failed");
+
+ return true;
+}
+
+static bool test_SetPrinterData(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct spoolss_SetPrinterData r;
+ const char *value_name = "spottyfoot";
+
+ r.in.handle = handle;
+ r.in.value_name = value_name;
+ r.in.type = REG_SZ;
+ r.in.data.string = "dog";
+
+ torture_comment(tctx, "Testing SetPrinterData\n");
+
+ status = dcerpc_spoolss_SetPrinterData(p, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "SetPrinterData failed");
+
+ if (!test_GetPrinterData(tctx, p, handle, value_name)) {
+ return false;
+ }
+
+ if (!test_DeletePrinterData(tctx, p, handle, value_name)) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_SecondaryClosePrinter(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct dcerpc_binding *b;
+ 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");
+
+ status = dcerpc_parse_binding(tctx, p->conn->binding_string, &b);
+ torture_assert_ntstatus_ok(tctx, status, "Failed to parse dcerpc binding");
+
+ status = dcerpc_secondary_connection(p, &p2, b);
+ torture_assert_ntstatus_ok(tctx, status, "Failed to create secondary connection");
+
+ status = dcerpc_bind_auth_none(p2, &ndr_table_spoolss);
+ torture_assert_ntstatus_ok(tctx, status, "Failed to create bind on secondary connection");
+
+ cp.in.handle = handle;
+ cp.out.handle = handle;
+
+ status = dcerpc_spoolss_ClosePrinter(p2, tctx, &cp);
+ torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NET_WRITE_FAULT,
+ "ERROR: Allowed close on secondary connection");
+
+ torture_assert_int_equal(tctx, p2->last_fault_code, DCERPC_FAULT_CONTEXT_MISMATCH,
+ "Unexpected fault code");
+
+ talloc_free(p2);
+
+ return true;
+}
+
+static bool test_OpenPrinter_badname(struct torture_context *tctx,
+ struct dcerpc_pipe *p, 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, "\nTesting OpenPrinter(%s) with bad name\n", op.in.printername);
+
+ status = dcerpc_spoolss_OpenPrinter(p, tctx, &op);
+ torture_assert_ntstatus_ok(tctx, status, "OpenPrinter failed");
+ if (!W_ERROR_EQUAL(WERR_INVALID_PRINTER_NAME,op.out.result)) {
+ torture_comment(tctx, "OpenPrinter(%s) unexpected result[%s] should be WERR_INVALID_PRINTER_NAME\n",
+ name, win_errstr(op.out.result));
+ }
+
+ if (W_ERROR_IS_OK(op.out.result)) {
+ ret &=test_ClosePrinter(tctx, p, &handle);
+ }
+
+ opEx.in.printername = name;
+ opEx.in.datatype = NULL;
+ opEx.in.devmode_ctr.devmode = NULL;
+ opEx.in.access_mask = 0;
+ opEx.in.level = 1;
+ opEx.in.userlevel.level1 = NULL;
+ opEx.out.handle = &handle;
+
+ torture_comment(tctx, "Testing OpenPrinterEx(%s) with bad name\n", opEx.in.printername);
+
+ status = dcerpc_spoolss_OpenPrinterEx(p, tctx, &opEx);
+ torture_assert_ntstatus_ok(tctx, status, "OpenPrinterEx failed");
+ if (!W_ERROR_EQUAL(WERR_INVALID_PARAM,opEx.out.result)) {
+ torture_comment(tctx, "OpenPrinterEx(%s) unexpected result[%s] should be WERR_INVALID_PARAM\n",
+ name, win_errstr(opEx.out.result));
+ }
+
+ if (W_ERROR_IS_OK(opEx.out.result)) {
+ ret &=test_ClosePrinter(tctx, p, &handle);
+ }
+
+ return ret;
+}
+
+static bool test_OpenPrinter(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *name)
+{
+ NTSTATUS status;
+ struct spoolss_OpenPrinter r;
+ struct policy_handle handle;
+ bool ret = true;
+
+ r.in.printername = talloc_asprintf(tctx, "\\\\%s\\%s", dcerpc_server_name(p), 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(p, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "OpenPrinter failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "OpenPrinter failed");
+
+ if (!test_GetPrinter(tctx, p, &handle)) {
+ ret = false;
+ }
+
+ if (!torture_setting_bool(tctx, "samba3", false)) {
+ if (!test_SecondaryClosePrinter(tctx, p, &handle)) {
+ ret = false;
+ }
+ }
+
+ if (!test_ClosePrinter(tctx, p, &handle)) {
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool call_OpenPrinterEx(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *name, struct policy_handle *handle)
+{
+ struct spoolss_OpenPrinterEx r;
+ struct spoolss_UserLevel1 userlevel1;
+ NTSTATUS status;
+
+ if (name && name[0]) {
+ r.in.printername = talloc_asprintf(tctx, "\\\\%s\\%s",
+ dcerpc_server_name(p), name);
+ } else {
+ 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.level = 1;
+ r.in.userlevel.level1 = &userlevel1;
+ r.out.handle = handle;
+
+ userlevel1.size = 1234;
+ userlevel1.client = "hello";
+ userlevel1.user = "spottyfoot!";
+ userlevel1.build = 1;
+ userlevel1.major = 2;
+ userlevel1.minor = 3;
+ userlevel1.processor = 4;
+
+ torture_comment(tctx, "Testing OpenPrinterEx(%s)\n", r.in.printername);
+
+ status = dcerpc_spoolss_OpenPrinterEx(p, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "OpenPrinterEx failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "OpenPrinterEx failed");
+
+ return true;
+}
+
+static bool test_OpenPrinterEx(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *name)
+{
+ struct policy_handle handle;
+ bool ret = true;
+
+ if (!call_OpenPrinterEx(tctx, p, name, &handle)) {
+ return false;
+ }
+
+ if (!test_GetPrinter(tctx, p, &handle)) {
+ ret = false;
+ }
+
+ if (!test_EnumForms(tctx, p, &handle, false)) {
+ ret = false;
+ }
+
+ if (!test_AddForm(tctx, p, &handle, false)) {
+ ret = false;
+ }
+
+ if (!test_EnumPrinterData(tctx, p, &handle)) {
+ ret = false;
+ }
+
+ if (!test_EnumPrinterDataEx(tctx, p, &handle)) {
+ ret = false;
+ }
+
+ if (!test_PausePrinter(tctx, p, &handle)) {
+ ret = false;
+ }
+
+ if (!test_DoPrintTest(tctx, p, &handle)) {
+ ret = false;
+ }
+
+ if (!test_ResumePrinter(tctx, p, &handle)) {
+ ret = false;
+ }
+
+ if (!test_SetPrinterData(tctx, p, &handle)) {
+ ret = false;
+ }
+
+ if (!torture_setting_bool(tctx, "samba3", false)) {
+ if (!test_SecondaryClosePrinter(tctx, p, &handle)) {
+ ret = false;
+ }
+ }
+
+ if (!test_ClosePrinter(tctx, p, &handle)) {
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool test_EnumPrinters_old(struct torture_context *tctx, struct dcerpc_pipe *p)
+{
+ struct spoolss_EnumPrinters r;
+ NTSTATUS status;
+ uint16_t levels[] = {1, 2, 4, 5};
+ int i;
+ bool ret = true;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ union spoolss_PrinterInfo *info;
+ int j;
+ uint32_t needed;
+ uint32_t count;
+
+ r.in.flags = PRINTER_ENUM_LOCAL;
+ r.in.server = "";
+ r.in.level = levels[i];
+ 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 level %u\n", r.in.level);
+
+ status = dcerpc_spoolss_EnumPrinters(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "EnumPrinters failed");
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ DATA_BLOB blob = data_blob_talloc(tctx, NULL, needed);
+ data_blob_clear(&blob);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+ status = dcerpc_spoolss_EnumPrinters(p, tctx, &r);
+ }
+
+ torture_assert_ntstatus_ok(tctx, status, "EnumPrinters failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "EnumPrinters failed");
+
+ if (!info) {
+ torture_comment(tctx, "No printers returned\n");
+ return true;
+ }
+
+ for (j=0;j<count;j++) {
+ if (r.in.level == 1) {
+ char *unc = talloc_strdup(tctx, info[j].info1.name);
+ char *slash, *name;
+ name = unc;
+ if (unc[0] == '\\' && unc[1] == '\\') {
+ unc +=2;
+ }
+ slash = strchr(unc, '\\');
+ if (slash) {
+ slash++;
+ name = slash;
+ }
+ if (!test_OpenPrinter(tctx, p, name)) {
+ ret = false;
+ }
+ if (!test_OpenPrinterEx(tctx, p, name)) {
+ ret = false;
+ }
+ }
+ }
+ }
+
+ return ret;
+}
+
+#if 0
+static bool test_GetPrinterDriver2(struct dcerpc_pipe *p,
+ struct policy_handle *handle,
+ const char *driver_name)
+{
+ NTSTATUS status;
+ struct spoolss_GetPrinterDriver2 r;
+ uint32_t needed;
+ uint32_t server_major_version;
+ uint32_t server_minor_version;
+
+ r.in.handle = handle;
+ r.in.architecture = "W32X86";
+ r.in.level = 1;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+ r.in.client_major_version = 0;
+ r.in.client_minor_version = 0;
+ r.out.needed = &needed;
+ r.out.server_major_version = &server_major_version;
+ r.out.server_minor_version = &server_minor_version;
+
+ printf("Testing GetPrinterDriver2\n");
+
+ status = dcerpc_spoolss_GetPrinterDriver2(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("GetPrinterDriver2 failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ r.in.offered = needed;
+ status = dcerpc_spoolss_GetPrinterDriver2(p, tctx, &r);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("GetPrinterDriver2 failed - %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("GetPrinterDriver2 failed - %s\n",
+ win_errstr(r.out.result));
+ return false;
+ }
+
+ return true;
+}
+#endif
+
+static bool test_EnumPrinterDrivers_old(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct spoolss_EnumPrinterDrivers r;
+ NTSTATUS status;
+ uint16_t levels[] = {1, 2, 3, 4, 5, 6};
+ int i;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+
+ uint32_t needed;
+ uint32_t count;
+ union spoolss_DriverInfo *info;
+
+ r.in.server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.environment = "Windows NT x86";
+ r.in.level = levels[i];
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+ r.out.needed = &needed;
+ r.out.count = &count;
+ r.out.info = &info;
+
+ torture_comment(tctx, "Testing EnumPrinterDrivers level %u\n", r.in.level);
+
+ status = dcerpc_spoolss_EnumPrinterDrivers(p, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "EnumPrinterDrivers failed");
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ DATA_BLOB blob = data_blob_talloc(tctx, NULL, needed);
+ data_blob_clear(&blob);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+ status = dcerpc_spoolss_EnumPrinterDrivers(p, tctx, &r);
+ }
+
+ torture_assert_ntstatus_ok(tctx, status, "EnumPrinterDrivers failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "EnumPrinterDrivers failed");
+
+ if (!info) {
+ torture_comment(tctx, "No printer drivers returned\n");
+ break;
+ }
+ }
+
+ return true;
+}
+
+bool torture_rpc_spoolss(struct torture_context *torture)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p;
+ bool ret = true;
+ struct test_spoolss_context *ctx;
+
+ status = torture_rpc_connection(torture, &p, &ndr_table_spoolss);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ ctx = talloc_zero(torture, struct test_spoolss_context);
+
+ ret &= test_OpenPrinter_server(torture, p, ctx);
+
+ ret &= test_GetPrinterData(torture, p, &ctx->server_handle, "W3SvcInstalled");
+ ret &= test_GetPrinterData(torture, p, &ctx->server_handle, "BeepEnabled");
+ ret &= test_GetPrinterData(torture, p, &ctx->server_handle, "EventLog");
+ ret &= test_GetPrinterData(torture, p, &ctx->server_handle, "NetPopup");
+ ret &= test_GetPrinterData(torture, p, &ctx->server_handle, "NetPopupToComputer");
+ ret &= test_GetPrinterData(torture, p, &ctx->server_handle, "MajorVersion");
+ ret &= test_GetPrinterData(torture, p, &ctx->server_handle, "MinorVersion");
+ ret &= test_GetPrinterData(torture, p, &ctx->server_handle, "DefaultSpoolDirectory");
+ ret &= test_GetPrinterData(torture, p, &ctx->server_handle, "Architecture");
+ ret &= test_GetPrinterData(torture, p, &ctx->server_handle, "DsPresent");
+ ret &= test_GetPrinterData(torture, p, &ctx->server_handle, "OSVersion");
+ ret &= test_GetPrinterData(torture, p, &ctx->server_handle, "OSVersionEx");
+ ret &= test_GetPrinterData(torture, p, &ctx->server_handle, "DNSMachineName");
+ ret &= test_EnumForms(torture, p, &ctx->server_handle, true);
+ ret &= test_AddForm(torture, p, &ctx->server_handle, true);
+ ret &= test_EnumPorts(torture, p, ctx);
+ ret &= test_GetPrinterDriverDirectory(torture, p, ctx);
+ ret &= test_GetPrintProcessorDirectory(torture, p, ctx);
+ ret &= test_EnumPrinterDrivers(torture, p, ctx);
+ ret &= test_EnumMonitors(torture, p, ctx);
+ ret &= test_EnumPrintProcessors(torture, p, ctx);
+ ret &= test_EnumPrintProcDataTypes(torture, p, ctx);
+ ret &= test_EnumPrinters(torture, p, ctx);
+ ret &= test_OpenPrinter_badname(torture, p, "__INVALID_PRINTER__");
+ ret &= test_OpenPrinter_badname(torture, p, "\\\\__INVALID_HOST__");
+ ret &= test_OpenPrinter_badname(torture, p, "");
+ ret &= test_OpenPrinter_badname(torture, p, "\\\\\\");
+ ret &= test_OpenPrinter_badname(torture, p, "\\\\\\__INVALID_PRINTER__");
+ ret &= test_OpenPrinter_badname(torture, p, talloc_asprintf(torture, "\\\\%s\\", dcerpc_server_name(p)));
+ ret &= test_OpenPrinter_badname(torture, p,
+ talloc_asprintf(torture, "\\\\%s\\__INVALID_PRINTER__", dcerpc_server_name(p)));
+
+
+ ret &= test_AddPort(torture, p);
+ ret &= test_EnumPorts_old(torture, p);
+ ret &= test_EnumPrinters_old(torture, p);
+ ret &= test_EnumPrinterDrivers_old(torture, p);
+
+ return ret;
+}
diff --git a/source4/torture/rpc/spoolss_notify.c b/source4/torture/rpc/spoolss_notify.c
new file mode 100644
index 0000000000..a8a0ca5df6
--- /dev/null
+++ b/source4/torture/rpc/spoolss_notify.c
@@ -0,0 +1,338 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for spoolss rpc notify 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "torture/rpc/rpc.h"
+#include "librpc/gen_ndr/ndr_spoolss_c.h"
+#include "rpc_server/dcerpc_server.h"
+#include "lib/events/events.h"
+#include "smbd/process_model.h"
+#include "smb_server/smb_server.h"
+#include "librpc/rpc/dcerpc_proto.h"
+#include "lib/socket/netif.h"
+#include "../lib/util/dlinklist.h"
+#include "ntvfs/ntvfs.h"
+#include "param/param.h"
+
+static NTSTATUS spoolss__op_bind(struct dcesrv_call_state *dce_call, 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)) {
+ dcerpc_log_packet(dce_call->conn->packet_log_dir,
+ &ndr_table_spoolss, opnum, NDR_IN,
+ &dce_call->pkt.u.request.stub_and_verifier);
+ dce_call->fault_code = DCERPC_FAULT_NDR;
+ return NT_STATUS_NET_WRITE_FAULT;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/* Note that received_packets are allocated in talloc_autofree_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 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(talloc_autofree_context(), struct received_packet);
+ rp->opnum = opnum;
+ rp->r = talloc_reference(rp, r);
+
+ DLIST_ADD_END(received_packets, rp, struct received_packet *);
+
+ switch (opnum) {
+ case 58: {
+ struct spoolss_ReplyOpenPrinter *r2 = (struct spoolss_ReplyOpenPrinter *)r;
+ r2->out.result = WERR_OK;
+ break;
+ }
+
+ default:
+ dce_call->fault_code = DCERPC_FAULT_OP_RNG_ERROR;
+ break;
+ }
+
+ if (dce_call->fault_code != 0) {
+ dcerpc_log_packet(dce_call->conn->packet_log_dir,
+ &ndr_table_spoolss, opnum, NDR_IN,
+ &dce_call->pkt.u.request.stub_and_verifier);
+ 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;
+}
+
+const static 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(&notify_test_spoolss_interface.syntax_id.uuid, uuid)) {
+ memcpy(iface,&notify_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, &notify_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)
+{
+ int i;
+
+ for (i=0;i<ndr_table_spoolss.endpoints->count;i++) {
+ NTSTATUS ret;
+ const char *name = ndr_table_spoolss.endpoints->names[i];
+
+ ret = dcesrv_interface_register(dce_ctx, name, &notify_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 bool test_RFFPCNEx(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct spoolss_OpenPrinter q;
+ struct spoolss_RemoteFindFirstPrinterChangeNotifyEx r;
+ struct dcesrv_endpoint_server ep_server;
+ NTSTATUS status;
+ struct dcesrv_context *dce_ctx;
+ const char *endpoints[] = { "spoolss", NULL };
+ struct spoolss_NotifyOption t1;
+ struct spoolss_ClosePrinter cp;
+
+ struct policy_handle handle;
+ const char *address;
+ struct interface *ifaces;
+
+ received_packets = NULL;
+
+ ntvfs_init(tctx->lp_ctx);
+
+ ZERO_STRUCT(q);
+
+ q.in.printername = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ q.in.datatype = NULL;
+ q.in.devmode_ctr.devmode= NULL;
+ q.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ q.out.handle = &handle;
+
+ torture_comment(tctx, "Testing OpenPrinter(%s)\n", q.in.printername);
+
+ status = dcerpc_spoolss_OpenPrinter(p, tctx, &q);
+
+ torture_assert_ntstatus_ok(tctx, status, "OpenPrinter failed");
+
+ torture_assert_werr_ok(tctx, q.out.result, "OpenPrinter failed");
+
+ /* Start DCE/RPC server */
+
+ /* fill in our name */
+ ep_server.name = "spoolss";
+
+ /* fill in all the operations */
+ ep_server.init_server = spoolss__op_init_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");
+
+ lp_set_cmdline(tctx->lp_ctx, "dcerpc endpoint servers", "spoolss");
+
+ load_interfaces(tctx, lp_interfaces(tctx->lp_ctx), &ifaces);
+ address = iface_n_ip(ifaces, 0);
+ torture_comment(tctx, "Listening for callbacks on %s\n", address);
+ status = smbsrv_add_socket(p->conn->event_ctx, tctx->lp_ctx, &single_ops, address);
+ torture_assert_ntstatus_ok(tctx, status, "starting smb server");
+
+ status = dcesrv_init_context(tctx, tctx->lp_ctx, endpoints, &dce_ctx);
+ torture_assert_ntstatus_ok(tctx, status,
+ "unable to initialize DCE/RPC server");
+
+ r.in.flags = 0;
+ r.in.local_machine = talloc_asprintf(tctx, "\\\\%s", address);
+ r.in.options = 0;
+ r.in.printer_local = 123;
+ t1.version = 2;
+ t1.flags = 0;
+ t1.count = 2;
+ t1.types = talloc_zero_array(tctx, struct spoolss_NotifyOptionType, 2);
+ t1.types[0].type = PRINTER_NOTIFY_TYPE;
+ t1.types[0].count = 1;
+ t1.types[0].fields = talloc_array(t1.types, union spoolss_Field, 1);
+ t1.types[0].fields[0].field = PRINTER_NOTIFY_FIELD_SERVER_NAME;
+
+ t1.types[1].type = JOB_NOTIFY_TYPE;
+ t1.types[1].count = 1;
+ t1.types[1].fields = talloc_array(t1.types, union spoolss_Field, 1);
+ t1.types[1].fields[0].field = PRINTER_NOTIFY_FIELD_PRINTER_NAME;
+
+ r.in.notify_options = &t1;
+ r.in.handle = &handle;
+
+ status = dcerpc_spoolss_RemoteFindFirstPrinterChangeNotifyEx(p, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "FFPCNEx failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "error return code for FFPCNEx");
+
+ cp.in.handle = &handle;
+ cp.out.handle = &handle;
+
+ torture_comment(tctx, "Testing ClosePrinter\n");
+
+ status = dcerpc_spoolss_ClosePrinter(p, tctx, &cp);
+ torture_assert_ntstatus_ok(tctx, status, "ClosePrinter failed");
+
+ /* We should've had an incoming packet 58 (ReplyOpenPrinter) */
+ torture_assert(tctx, received_packets != NULL, "no packets received");
+ torture_assert_int_equal(tctx, received_packets->opnum, 58, "invalid opnum");
+
+ /* Shut down DCE/RPC server */
+ talloc_free(dce_ctx);
+
+ 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 *pipe)
+{
+ struct spoolss_ReplyOpenPrinter r;
+ struct spoolss_ReplyClosePrinter s;
+ struct policy_handle h;
+
+ 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(pipe, 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(pipe, 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 0000000000..719d8e26d2
--- /dev/null
+++ b/source4/torture/rpc/spoolss_win.c
@@ -0,0 +1,627 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "torture/rpc/rpc.h"
+#include "librpc/gen_ndr/ndr_spoolss_c.h"
+#include "rpc_server/dcerpc_server.h"
+#include "ntvfs/ntvfs.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_pipe *p,
+ 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.level = 1;
+ op.in.userlevel.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(p, 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_pipe *p,
+ 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.level = 1;
+ op.in.userlevel.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(p, tctx, &op);
+
+ if (NT_STATUS_IS_OK(status) && W_ERROR_IS_OK(op.out.result)) {
+ status = dcerpc_spoolss_ClosePrinter(p, tctx, &cp);
+ torture_assert_ntstatus_ok(tctx, status, "ClosePrinter failed");
+ }
+
+ return true;
+}
+
+
+static bool test_ClosePrinter(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle);
+
+/* 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));
+
+ /* First, see if we can open the printer read_only */
+ ret = test_OpenPrinterEx(tctx, p, handle, printername, 0);
+ torture_assert(tctx, ret == true, "OpenPrinterEx failed.");
+
+ ret = test_ClosePrinter(tctx, p, handle);
+ torture_assert(tctx, ret, "ClosePrinter failed");
+
+ /* Now let's see if we have admin rights to it. */
+ ret = test_OpenPrinterAsAdmin(tctx, p, printername);
+ torture_assert(tctx, ret == true,
+ "OpenPrinterEx as admin failed unexpectedly.");
+
+ ret = test_OpenPrinterEx(tctx, p, handle, printername, SERVER_EXECUTE);
+ torture_assert(tctx, ret == true, "OpenPrinterEx failed.");
+
+ return true;
+}
+
+static bool test_GetPrinterData(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ 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;
+ union spoolss_PrinterData data;
+
+ 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(p, 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)) {
+ torture_assert_int_equal(tctx, data.value,
+ expected_value,
+ "GetPrinterData did not return expected value.");
+ }
+ 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;
+
+ 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(p, 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(p, 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_pipe *p,
+ 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(p, 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(p, 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_pipe *p,
+ 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");
+
+ ej.in.handle = handle;
+ 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(p, 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(p, 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_pipe *p,
+ 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(p, 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_pipe *p,
+ 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(p, 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(p, 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_pipe *p,
+ struct policy_handle *handle,
+ const char* key,
+ struct test_spoolss_win_context *ctx)
+{
+ NTSTATUS status;
+ struct spoolss_EnumPrinterKey epk;
+ uint32_t needed = 0;
+ const char **key_buffer = NULL;
+
+ 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;
+
+ status = dcerpc_spoolss_EnumPrinterKey(p, 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(p, 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;
+
+ return true;
+}
+
+static bool test_EnumPrinterDataEx(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ 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(p, 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(p, 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_ClosePrinter(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct spoolss_ClosePrinter cp;
+
+ cp.in.handle = handle;
+ cp.out.handle = handle;
+
+ status = dcerpc_spoolss_ClosePrinter(p, tctx, &cp);
+ torture_assert_ntstatus_ok(tctx, status, "ClosePrinter 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;
+
+ ntvfs_init(tctx->lp_ctx);
+
+ 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, p, &handle01,"UISingleJobStatusString",
+ WERR_INVALID_PARAM, 0);
+ torture_comment(tctx, "Skip RemoteFindNextPrinterChangeNotifyEx test\n");
+
+ server_name = talloc_asprintf(ctx, "\\\\%s", dcerpc_server_name(p));
+ ret &= test_OpenPrinterEx(tctx, p, &unused_handle1, server_name, 0);
+
+ ret &= test_EnumPrinters(tctx, p, ctx, 1024);
+
+ ret &= test_OpenPrinterEx(tctx, p, &handle02, server_name, 0);
+ ret &= test_GetPrinterData(tctx, p, &handle02, "MajorVersion", WERR_OK,
+ 3);
+ ret &= test_ClosePrinter(tctx, p, &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, p, &handle02,
+ ctx->printer_info[0].info2.printername,
+ PRINTER_ACCESS_USE);
+ ret &= test_GetPrinter(tctx, p, &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, p, &handle03,
+ ctx->printer_info[0].info2.printername, 0);
+ ret &= test_GetPrinter(tctx, p, &handle03, ctx, 0, 1164);
+ ret &= test_GetPrinter(tctx, p, &handle03, ctx, 2, 0);
+
+ ret &= test_OpenPrinterEx(tctx, p, &handle04,
+ ctx->printer_info[0].info2.printername, 0);
+ ret &= test_GetPrinter(tctx, p, &handle04, ctx, 2, 0);
+ ret &= test_ClosePrinter(tctx, p, &handle04);
+
+ ret &= test_OpenPrinterEx(tctx, p, &handle04,
+ ctx->printer_info[0].info2.printername, 0);
+ ret &= test_GetPrinter(tctx, p, &handle04, ctx, 2, 4096);
+ ret &= test_ClosePrinter(tctx, p, &handle04);
+
+ ret &= test_OpenPrinterAsAdmin(tctx, p,
+ ctx->printer_info[0].info2.printername);
+
+ ret &= test_OpenPrinterEx(tctx, p, &handle04,
+ ctx->printer_info[0].info2.printername, PRINTER_READ);
+ ret &= test_GetPrinterData(tctx, p, &handle04,"UISingleJobStatusString",
+ WERR_BADFILE, 0);
+ torture_comment(tctx, "Skip RemoteFindNextPrinterChangeNotifyEx test\n");
+
+ ret &= test_OpenPrinterEx(tctx, p, &unused_handle2,
+ ctx->printer_info[0].info2.printername, 0);
+
+ ret &= test_EnumJobs(tctx, p, &handle04);
+ ret &= test_GetPrinter(tctx, p, &handle04, ctx, 2, 4096);
+
+ ret &= test_ClosePrinter(tctx, p, &unused_handle2);
+ ret &= test_ClosePrinter(tctx, p, &handle04);
+
+ ret &= test_EnumPrinters(tctx, p, ctx, 1556);
+ ret &= test_GetPrinterDriver2(tctx, p, ctx, &handle03);
+ ret &= test_EnumForms(tctx, p, &handle03, 0);
+
+ ret &= test_EnumPrinterKey(tctx, p, &handle03, "", ctx);
+
+ for (i=0; ctx->printer_keys[i] != NULL; i++) {
+
+ ret &= test_EnumPrinterKey(tctx, p, &handle03,
+ ctx->printer_keys[i],
+ tmp_ctx);
+ ret &= test_EnumPrinterDataEx(tctx, p, &handle03,
+ ctx->printer_keys[i], 0,
+ WERR_OK);
+ }
+
+ ret &= test_EnumPrinterDataEx(tctx, p, &handle03, "", 0,
+ WERR_INVALID_PARAM);
+
+ ret &= test_GetPrinter(tctx, p, &handle03, tmp_ctx, 2, 0);
+
+ ret &= test_OpenPrinterEx(tctx, p, &unused_handle2,
+ ctx->printer_info[0].info2.printername, 0);
+ ret &= test_ClosePrinter(tctx, p, &unused_handle2);
+
+ ret &= test_GetPrinter(tctx, p, &handle03, tmp_ctx, 2, 2556);
+
+ ret &= test_OpenPrinterEx(tctx, p, &unused_handle2,
+ ctx->printer_info[0].info2.printername, 0);
+ ret &= test_ClosePrinter(tctx, p, &unused_handle2);
+
+ ret &= test_OpenPrinterEx(tctx, p, &unused_handle2,
+ ctx->printer_info[0].info2.printername, 0);
+ ret &= test_ClosePrinter(tctx, p, &unused_handle2);
+
+ ret &= test_GetPrinter(tctx, p, &handle03, tmp_ctx, 7, 0);
+
+ ret &= test_OpenPrinterEx(tctx, p, &unused_handle2,
+ ctx->printer_info[0].info2.printername, 0);
+ ret &= test_ClosePrinter(tctx, p, &unused_handle2);
+
+ ret &= test_ClosePrinter(tctx, p, &handle03);
+
+ ret &= test_OpenPrinterEx(tctx, p, &unused_handle2,
+ ctx->printer_info[0].info2.printername, 0);
+ ret &= test_ClosePrinter(tctx, p, &unused_handle2);
+
+ ret &= test_OpenPrinterEx(tctx, p, &handle03, server_name, 0);
+ ret &= test_GetPrinterData(tctx, p, &handle03, "W3SvcInstalled",
+ WERR_OK, 0);
+ ret &= test_ClosePrinter(tctx, p, &handle03);
+
+ ret &= test_ClosePrinter(tctx, p, &unused_handle1);
+ ret &= test_ClosePrinter(tctx, p, &handle02);
+
+ ret &= test_OpenPrinterEx(tctx, p, &handle02,
+ ctx->printer_info[0].info2.sharename, 0);
+ ret &= test_GetPrinter(tctx, p, &handle02, tmp_ctx, 2, 0);
+ ret &= test_ClosePrinter(tctx, p, &handle02);
+
+end_testWinXP:
+ ret &= test_ClosePrinter(tctx, p, &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 0000000000..82a8a67854
--- /dev/null
+++ b/source4/torture/rpc/srvsvc.c
@@ -0,0 +1,1181 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/ndr_srvsvc.h"
+#include "librpc/gen_ndr/ndr_srvsvc_c.h"
+#include "torture/rpc/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;
+
+ r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p));
+ r.in.device_name = devname;
+ r.out.info = &info;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ r.in.level = levels[i];
+ torture_comment(tctx, "testing NetCharDevGetInfo level %u on device '%s'\n",
+ r.in.level, r.in.device_name);
+ status = dcerpc_srvsvc_NetCharDevGetInfo(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "NetCharDevGetInfo failed");
+ torture_assert_werr_ok(tctx, r.out.result, "NetCharDevGetInfo failed");
+ }
+
+ return true;
+}
+
+static bool test_NetCharDevControl(struct dcerpc_pipe *p, struct torture_context *tctx,
+ const char *devname)
+{
+ NTSTATUS status;
+ struct srvsvc_NetCharDevControl r;
+ uint32_t opcodes[] = {0, 1};
+ int i;
+
+ r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p));
+ r.in.device_name = devname;
+
+ for (i=0;i<ARRAY_SIZE(opcodes);i++) {
+ ZERO_STRUCT(r.out);
+ r.in.opcode = opcodes[i];
+ torture_comment(tctx, "testing NetCharDevControl opcode %u on device '%s'\n",
+ r.in.opcode, r.in.device_name);
+ status = dcerpc_srvsvc_NetCharDevControl(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "NetCharDevControl failed");
+ torture_assert_werr_ok(tctx, r.out.result, "NetCharDevControl failed");
+ }
+
+ return true;
+}
+
+static bool test_NetCharDevEnum(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct srvsvc_NetCharDevEnum r;
+ struct srvsvc_NetCharDevInfoCtr info_ctr;
+ struct srvsvc_NetCharDevCtr0 c0;
+ struct srvsvc_NetCharDevCtr0 c1;
+ uint32_t totalentries = 0;
+ uint32_t levels[] = {0, 1};
+ int i;
+
+ 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;i<ARRAY_SIZE(levels);i++) {
+ int j;
+
+ info_ctr.level = levels[i];
+
+ switch(info_ctr.level) {
+ case 0:
+ ZERO_STRUCT(c0);
+ info_ctr.ctr.ctr0 = &c0;
+ break;
+ case 1:
+ ZERO_STRUCT(c1);
+ info_ctr.ctr.ctr0 = &c1;
+ break;
+ }
+
+ torture_comment(tctx, "testing NetCharDevEnum level %u\n", info_ctr.level);
+ status = dcerpc_srvsvc_NetCharDevEnum(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "NetCharDevEnum failed");
+ if (!W_ERROR_IS_OK(r.out.result)) {
+ torture_comment(tctx, "NetCharDevEnum failed: %s\n", win_errstr(r.out.result));
+ continue;
+ }
+
+ /* call test_NetCharDevGetInfo and test_NetCharDevControl for each returned share */
+ if (info_ctr.level == 1) {
+ for (j=0;j<r.out.info_ctr->ctr.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;
+
+ 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;i<ARRAY_SIZE(levels);i++) {
+ r.in.level = levels[i];
+ torture_comment(tctx, "testing NetCharDevQGetInfo level %u on devicequeue '%s'\n",
+ r.in.level, r.in.queue_name);
+ status = dcerpc_srvsvc_NetCharDevQGetInfo(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "NetCharDevQGetInfo failed");
+ torture_assert_werr_ok(tctx, r.out.result, "NetCharDevQGetInfo failed");
+ }
+
+ return true;
+}
+
+#if 0
+static bool test_NetCharDevQSetInfo(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ const char *devicequeue)
+{
+ NTSTATUS status;
+ struct srvsvc_NetCharDevQSetInfo r;
+ uint32_t parm_error;
+ uint32_t levels[] = {0, 1};
+ int i;
+ bool ret = true;
+
+ r.in.server_unc = talloc_asprintf(mem_ctx,"\\\\%s",dcerpc_server_name(p));
+ r.in.queue_name = devicequeue;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ ZERO_STRUCT(r.out);
+ parm_error = 0;
+ r.in.level = levels[i];
+ d_printf("testing NetCharDevQSetInfo level %u on devicequeue '%s'\n",
+ r.in.level, devicequeue);
+ switch (r.in.level) {
+ case 0:
+ r.in.info.info0 = talloc(mem_ctx, struct srvsvc_NetCharDevQInfo0);
+ r.in.info.info0->device = 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(p, 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;
+
+ 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;i<ARRAY_SIZE(levels);i++) {
+ int j;
+
+ info_ctr.level = levels[i];
+
+ switch (info_ctr.level) {
+ case 0:
+ ZERO_STRUCT(c0);
+ info_ctr.ctr.ctr0 = &c0;
+ break;
+ case 1:
+ ZERO_STRUCT(c1);
+ info_ctr.ctr.ctr1 = &c1;
+ break;
+ }
+ torture_comment(tctx, "testing NetCharDevQEnum level %u\n", info_ctr.level);
+ status = dcerpc_srvsvc_NetCharDevQEnum(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "NetCharDevQEnum failed");
+ if (!W_ERROR_IS_OK(r.out.result)) {
+ torture_comment(tctx, "NetCharDevQEnum failed: %s\n", win_errstr(r.out.result));
+ continue;
+ }
+
+ /* call test_NetCharDevGetInfo and test_NetCharDevControl for each returned share */
+ if (info_ctr.level == 1) {
+ for (j=0;j<r.out.info_ctr->ctr.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;
+
+ ZERO_STRUCT(info_ctr);
+
+ r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p));
+ r.in.path = talloc_asprintf(tctx,"%s","ADMIN$");
+ 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;i<ARRAY_SIZE(levels);i++) {
+ info_ctr.level = levels[i];
+
+ switch (info_ctr.level) {
+ case 0:
+ ZERO_STRUCT(c0);
+ info_ctr.ctr.ctr0 = &c0;
+ break;
+ case 1:
+ ZERO_STRUCT(c1);
+ info_ctr.ctr.ctr1 = &c1;
+ break;
+ }
+
+ torture_comment(tctx, "testing NetConnEnum level %u\n", info_ctr.level);
+ status = dcerpc_srvsvc_NetConnEnum(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "NetConnEnum failed");
+ if (!W_ERROR_IS_OK(r.out.result)) {
+ torture_comment(tctx, "NetConnEnum failed: %s\n", win_errstr(r.out.result));
+ }
+ }
+
+ return true;
+}
+
+/**************************/
+/* srvsvc_NetFile */
+/**************************/
+static bool test_NetFileEnum(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct srvsvc_NetFileEnum r;
+ struct srvsvc_NetFileInfoCtr info_ctr;
+ struct srvsvc_NetFileCtr2 c2;
+ struct srvsvc_NetFileCtr3 c3;
+ uint32_t totalentries = 0;
+ uint32_t levels[] = {2, 3};
+ int i;
+
+ 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;i<ARRAY_SIZE(levels);i++) {
+ info_ctr.level = levels[i];
+
+ switch (info_ctr.level) {
+ case 2:
+ ZERO_STRUCT(c2);
+ info_ctr.ctr.ctr2 = &c2;
+ break;
+ case 3:
+ ZERO_STRUCT(c3);
+ info_ctr.ctr.ctr3 = &c3;
+ break;
+ }
+ torture_comment(tctx, "testing NetFileEnum level %u\n", info_ctr.level);
+ status = dcerpc_srvsvc_NetFileEnum(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "NetFileEnum failed");
+ if (!W_ERROR_IS_OK(r.out.result)) {
+ torture_comment(tctx, "NetFileEnum failed: %s\n", win_errstr(r.out.result));
+ }
+ }
+
+ return true;
+}
+
+/**************************/
+/* srvsvc_NetSess */
+/**************************/
+static bool test_NetSessEnum(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct srvsvc_NetSessEnum r;
+ struct srvsvc_NetSessInfoCtr info_ctr;
+ struct srvsvc_NetSessCtr0 c0;
+ struct srvsvc_NetSessCtr1 c1;
+ struct srvsvc_NetSessCtr2 c2;
+ struct srvsvc_NetSessCtr10 c10;
+ struct srvsvc_NetSessCtr502 c502;
+ uint32_t totalentries = 0;
+ uint32_t levels[] = {0, 1, 2, 10, 502};
+ int i;
+
+ 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;i<ARRAY_SIZE(levels);i++) {
+ info_ctr.level = levels[i];
+
+ switch (info_ctr.level) {
+ case 0:
+ ZERO_STRUCT(c0);
+ info_ctr.ctr.ctr0 = &c0;
+ break;
+ case 1:
+ ZERO_STRUCT(c1);
+ info_ctr.ctr.ctr1 = &c1;
+ break;
+ case 2:
+ ZERO_STRUCT(c2);
+ info_ctr.ctr.ctr2 = &c2;
+ break;
+ case 10:
+ ZERO_STRUCT(c10);
+ info_ctr.ctr.ctr10 = &c10;
+ break;
+ case 502:
+ ZERO_STRUCT(c502);
+ info_ctr.ctr.ctr502 = &c502;
+ break;
+ }
+
+ torture_comment(tctx, "testing NetSessEnum level %u\n", info_ctr.level);
+ status = dcerpc_srvsvc_NetSessEnum(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "NetSessEnum failed");
+ if (!W_ERROR_IS_OK(r.out.result)) {
+ torture_comment(tctx, "NetSessEnum failed: %s\n", win_errstr(r.out.result));
+ }
+ }
+
+ return true;
+}
+
+/**************************/
+/* srvsvc_NetShare */
+/**************************/
+static bool test_NetShareCheck(struct dcerpc_pipe *p, struct torture_context *tctx,
+ const char *device_name)
+{
+ NTSTATUS status;
+ struct srvsvc_NetShareCheck r;
+ enum srvsvc_ShareType type;
+
+ 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(p, 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;
+
+ r.in.server_unc = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.share_name = sharename;
+ r.out.info = &info;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ WERROR expected;
+
+ r.in.level = levels[i].level;
+ expected = levels[i].anon_status;
+ if (admin) expected = levels[i].admin_status;
+
+ torture_comment(tctx, "testing NetShareGetInfo level %u on share '%s'\n",
+ r.in.level, r.in.share_name);
+
+ status = dcerpc_srvsvc_NetShareGetInfo(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "NetShareGetInfo failed");
+ torture_assert_werr_equal(tctx, r.out.result, expected, "NetShareGetInfo failed");
+
+ if (r.in.level != 2) continue;
+ if (!r.out.info->info2 || !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, "ADMIN$", true);
+}
+
+static bool test_NetShareGetInfoAdminAnon(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ return test_NetShareGetInfo(tctx, p, "ADMIN$", 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_UNKNOWN_LEVEL },
+ { 1, WERR_OK },
+ { 2, WERR_OK },
+ { 501, WERR_UNKNOWN_LEVEL },
+ { 502, WERR_OK },
+ { 1004, WERR_OK },
+ { 1005, WERR_OK },
+ { 1006, WERR_OK },
+/* { 1007, WERR_OK }, */
+ { 1501, WERR_OK },
+ };
+ int i;
+
+ 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(p, 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(p, 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(p, 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(p, 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;
+
+ 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;i<ARRAY_SIZE(levels);i++) {
+
+ int j;
+ WERROR expected;
+
+ info_ctr.level = levels[i].level;
+
+ switch (info_ctr.level) {
+ case 0:
+ ZERO_STRUCT(c0);
+ info_ctr.ctr.ctr0 = &c0;
+ break;
+ case 1:
+ ZERO_STRUCT(c1);
+ info_ctr.ctr.ctr1 = &c1;
+ break;
+ case 2:
+ ZERO_STRUCT(c2);
+ info_ctr.ctr.ctr2 = &c2;
+ break;
+ case 501:
+ ZERO_STRUCT(c501);
+ info_ctr.ctr.ctr501 = &c501;
+ break;
+ case 502:
+ ZERO_STRUCT(c502);
+ info_ctr.ctr.ctr502 = &c502;
+ break;
+ }
+
+ expected = levels[i].anon_status;
+ if (admin) expected = levels[i].admin_status;
+
+ resume_handle = 0;
+
+ torture_comment(tctx, "testing NetShareEnumAll level %u\n", info_ctr.level);
+ status = dcerpc_srvsvc_NetShareEnumAll(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "NetShareEnumAll failed");
+ torture_assert_werr_equal(tctx, r.out.result, expected, "NetShareEnumAll failed");
+
+ /* call srvsvc_NetShareGetInfo for each returned share */
+ if (info_ctr.level == 2 && r.out.info_ctr->ctr.ctr2) {
+ for (j=0;j<r.out.info_ctr->ctr.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_UNKNOWN_LEVEL, WERR_UNKNOWN_LEVEL },
+ { 502, WERR_ACCESS_DENIED, WERR_OK },
+ };
+ int i;
+
+ 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;i<ARRAY_SIZE(levels);i++) {
+ WERROR expected;
+
+ info_ctr.level = levels[i].level;
+
+ switch (info_ctr.level) {
+ case 0:
+ ZERO_STRUCT(c0);
+ info_ctr.ctr.ctr0 = &c0;
+ break;
+ case 1:
+ ZERO_STRUCT(c1);
+ info_ctr.ctr.ctr1 = &c1;
+ break;
+ case 2:
+ ZERO_STRUCT(c2);
+ info_ctr.ctr.ctr2 = &c2;
+ break;
+ case 501:
+ ZERO_STRUCT(c501);
+ info_ctr.ctr.ctr501 = &c501;
+ break;
+ case 502:
+ ZERO_STRUCT(c502);
+ info_ctr.ctr.ctr502 = &c502;
+ break;
+ }
+
+ expected = levels[i].anon_status;
+ if (admin) expected = levels[i].admin_status;
+
+ torture_comment(tctx, "testing NetShareEnum level %u\n", info_ctr.level);
+ status = dcerpc_srvsvc_NetShareEnum(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "NetShareEnum failed");
+ torture_assert_werr_equal(tctx, r.out.result, expected, "NetShareEnum failed");
+ }
+
+ return true;
+}
+
+static bool test_NetShareEnumFull(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ return test_NetShareEnum(tctx, p, true);
+}
+
+static bool test_NetShareEnumAnon(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ return test_NetShareEnum(tctx, p, false);
+}
+
+/**************************/
+/* srvsvc_NetSrv */
+/**************************/
+static bool test_NetSrvGetInfo(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct srvsvc_NetSrvGetInfo r;
+ union srvsvc_NetSrvInfo info;
+ uint32_t levels[] = {100, 101, 102, 502, 503};
+ int i;
+
+ r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p));
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ r.in.level = levels[i];
+ r.out.info = &info;
+ torture_comment(tctx, "testing NetSrvGetInfo level %u\n", r.in.level);
+ status = dcerpc_srvsvc_NetSrvGetInfo(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "NetSrvGetInfo failed");
+ if (!W_ERROR_IS_OK(r.out.result)) {
+ torture_comment(tctx, "NetSrvGetInfo failed: %s\n", win_errstr(r.out.result));
+ }
+ }
+
+ return true;
+}
+
+/**************************/
+/* srvsvc_NetDisk */
+/**************************/
+static bool test_NetDiskEnum(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct srvsvc_NetDiskEnum r;
+ struct srvsvc_NetDiskInfo info;
+ uint32_t totalentries = 0;
+ uint32_t levels[] = {0};
+ int i;
+ uint32_t resume_handle=0;
+
+ ZERO_STRUCT(info);
+
+ 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;i<ARRAY_SIZE(levels);i++) {
+ ZERO_STRUCTP(r.out.info);
+ r.in.level = levels[i];
+ torture_comment(tctx, "testing NetDiskEnum level %u\n", r.in.level);
+ status = dcerpc_srvsvc_NetDiskEnum(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "NetDiskEnum failed");
+ torture_assert_werr_ok(tctx, r.out.result, "NetDiskEnum failed");
+ }
+
+ return true;
+}
+
+/**************************/
+/* srvsvc_NetTransport */
+/**************************/
+static bool test_NetTransportEnum(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct srvsvc_NetTransportEnum r;
+ struct srvsvc_NetTransportInfoCtr transports;
+ struct srvsvc_NetTransportCtr0 ctr0;
+ struct srvsvc_NetTransportCtr1 ctr1;
+
+ uint32_t totalentries = 0;
+ uint32_t levels[] = {0, 1};
+ int i;
+
+ 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;i<ARRAY_SIZE(levels);i++) {
+ transports.level = levels[i];
+ switch (transports.level) {
+ case 0:
+ ZERO_STRUCT(ctr0);
+ transports.ctr.ctr0 = &ctr0;
+ break;
+ case 1:
+ ZERO_STRUCT(ctr1);
+ transports.ctr.ctr1 = &ctr1;
+ break;
+ }
+ torture_comment(tctx, "testing NetTransportEnum level %u\n", transports.level);
+ status = dcerpc_srvsvc_NetTransportEnum(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "NetTransportEnum failed");
+ if (!W_ERROR_IS_OK(r.out.result)) {
+ torture_comment(tctx, "unexpected result: %s\n", win_errstr(r.out.result));
+ }
+ }
+
+ return true;
+}
+
+/**************************/
+/* srvsvc_NetRemoteTOD */
+/**************************/
+static bool test_NetRemoteTOD(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct srvsvc_NetRemoteTOD r;
+ struct srvsvc_NetRemoteTODInfo *info = NULL;
+
+ 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(p, 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;
+
+ 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(p, 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;
+ 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(p, 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 0000000000..ad27060659
--- /dev/null
+++ b/source4/torture/rpc/svcctl.c
@@ -0,0 +1,563 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for srvsvc rpc operations
+
+ Copyright (C) Jelmer Vernooij 2004
+ Copyright (C) Guenther Deschner 2008,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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/ndr_svcctl_c.h"
+#include "librpc/gen_ndr/ndr_svcctl.h"
+#include "torture/rpc/rpc.h"
+#include "param/param.h"
+
+#define TORTURE_DEFAULT_SERVICE "Spooler"
+
+static bool test_OpenSCManager(struct dcerpc_pipe *p, 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(p, tctx, &r),
+ "OpenSCManager failed!");
+
+ return true;
+}
+
+static bool test_CloseServiceHandle(struct dcerpc_pipe *p, 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(p, tctx, &r),
+ "CloseServiceHandle failed");
+
+ return true;
+}
+
+static bool test_OpenService(struct dcerpc_pipe *p, 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(p, 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;
+
+ if (!test_OpenSCManager(p, tctx, &h))
+ return false;
+
+ if (!test_OpenService(p, tctx, &h, TORTURE_DEFAULT_SERVICE, &s))
+ return false;
+
+ r.in.handle = &s;
+ r.out.service_status = &service_status;
+
+ status = dcerpc_svcctl_QueryServiceStatus(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "QueryServiceStatus failed!");
+ torture_assert_werr_ok(tctx, r.out.result, "QueryServiceStatus failed!");
+
+ if (!test_CloseServiceHandle(p, tctx, &s))
+ return false;
+
+ if (!test_CloseServiceHandle(p, 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;
+
+ uint32_t info_level = SVC_STATUS_PROCESS_INFO;
+ uint8_t *buffer;
+ uint32_t offered = 0;
+ uint32_t needed = 0;
+
+ if (!test_OpenSCManager(p, tctx, &h))
+ return false;
+
+ if (!test_OpenService(p, 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(p, 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(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "QueryServiceStatusEx failed!");
+ torture_assert_werr_ok(tctx, r.out.result, "QueryServiceStatusEx failed!");
+ }
+
+ if (!test_CloseServiceHandle(p, tctx, &s))
+ return false;
+
+ if (!test_CloseServiceHandle(p, 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;
+
+ uint32_t offered = 0;
+ uint32_t needed = 0;
+
+ if (!test_OpenSCManager(p, tctx, &h))
+ return false;
+
+ if (!test_OpenService(p, 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(p, 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(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "QueryServiceConfigW failed!");
+ }
+
+ torture_assert_werr_ok(tctx, r.out.result, "QueryServiceConfigW failed!");
+
+ if (!test_CloseServiceHandle(p, tctx, &s))
+ return false;
+
+ if (!test_CloseServiceHandle(p, 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;
+
+ uint32_t info_level = SERVICE_CONFIG_DESCRIPTION;
+ uint8_t *buffer;
+ uint32_t offered = 0;
+ uint32_t needed = 0;
+
+ if (!test_OpenSCManager(p, tctx, &h))
+ return false;
+
+ if (!test_OpenService(p, 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(p, 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(p, 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(p, 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(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "QueryServiceConfig2W failed!");
+ torture_assert_werr_ok(tctx, r.out.result, "QueryServiceConfig2W failed!");
+ }
+
+ if (!test_CloseServiceHandle(p, tctx, &s))
+ return false;
+
+ if (!test_CloseServiceHandle(p, 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;
+
+ uint8_t *buffer;
+ uint32_t needed;
+
+ if (!test_OpenSCManager(p, tctx, &h))
+ return false;
+
+ if (!test_OpenService(p, 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(p, tctx, &r),
+ "QueryServiceObjectSecurity failed!");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_PARAM,
+ "QueryServiceObjectSecurity failed!");
+
+ r.in.security_flags = SECINFO_DACL;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_svcctl_QueryServiceObjectSecurity(p, 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(p, tctx, &r),
+ "QueryServiceObjectSecurity failed!");
+ }
+
+ torture_assert_werr_ok(tctx, r.out.result, "QueryServiceObjectSecurity failed!");
+
+ if (!test_CloseServiceHandle(p, tctx, &s))
+ return false;
+
+ if (!test_CloseServiceHandle(p, 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;
+
+ if (!test_OpenSCManager(p, tctx, &h))
+ return false;
+
+ if (!test_OpenService(p, 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(p, tctx, &r),
+ "StartServiceW failed!");
+ torture_assert_werr_equal(tctx, r.out.result,
+ WERR_SERVICE_ALREADY_RUNNING,
+ "StartServiceW failed!");
+
+ if (!test_CloseServiceHandle(p, tctx, &s))
+ return false;
+
+ if (!test_CloseServiceHandle(p, 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;
+
+ if (!test_OpenSCManager(p, tctx, &h))
+ return false;
+
+ if (!test_OpenService(p, 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(p, tctx, &r),
+ "ControlService failed!");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_PARAM,
+ "ControlService failed!");
+
+ if (!test_CloseServiceHandle(p, tctx, &s))
+ return false;
+
+ if (!test_CloseServiceHandle(p, 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;
+
+ if (!test_OpenSCManager(p, 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(p, 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(p, 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, lp_iconv_convenience(tctx->lp_ctx));
+
+ 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(p, 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 };
+
+ if (!test_OpenSCManager(p, tctx, &h))
+ return false;
+
+ if (!test_OpenService(p, 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(p, tctx, &r),
+ "EnumDependentServicesW failed!");
+
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_PARAM,
+ "EnumDependentServicesW failed!");
+
+ for (i=0; i<ARRAY_SIZE(states); i++) {
+
+ r.in.state = states[i];
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_svcctl_EnumDependentServicesW(p, tctx, &r),
+ "EnumDependentServicesW failed!");
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA)) {
+ r.in.offered = needed;
+ r.out.service_status = talloc_array(tctx, uint8_t, needed);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_svcctl_EnumDependentServicesW(p, tctx, &r),
+ "EnumDependentServicesW failed!");
+
+ }
+
+ torture_assert_werr_ok(tctx, r.out.result,
+ "EnumDependentServicesW failed");
+ }
+
+ if (!test_CloseServiceHandle(p, tctx, &s))
+ return false;
+
+ if (!test_CloseServiceHandle(p, tctx, &h))
+ return false;
+
+ return true;
+}
+
+static bool test_SCManager(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct policy_handle h;
+
+ if (!test_OpenSCManager(p, tctx, &h))
+ return false;
+
+ if (!test_CloseServiceHandle(p, tctx, &h))
+ 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, "QueryServiceObjectSecurity",
+ test_QueryServiceObjectSecurity);
+ torture_rpc_tcase_add_test(tcase, "StartServiceW",
+ test_StartServiceW);
+ torture_rpc_tcase_add_test(tcase, "ControlService",
+ test_ControlService);
+
+ return suite;
+}
diff --git a/source4/torture/rpc/testjoin.c b/source4/torture/rpc/testjoin.c
new file mode 100644
index 0000000000..c93358015c
--- /dev/null
+++ b/source4/torture/rpc/testjoin.c
@@ -0,0 +1,699 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ 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 "torture/torture.h"
+#include "system/time.h"
+#include "../lib/crypto/crypto.h"
+#include "libnet/libnet.h"
+#include "lib/cmdline/popt_common.h"
+#include "lib/ldb/include/ldb.h"
+#include "librpc/gen_ndr/ndr_samr_c.h"
+
+#include "libcli/auth/libcli_auth.h"
+#include "torture/rpc/rpc.h"
+#include "libcli/security/security.h"
+#include "param/param.h"
+
+struct test_join {
+ struct dcerpc_pipe *p;
+ struct policy_handle user_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 dcerpc_pipe *p, 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(p, mem_ctx, &n);
+ if (NT_STATUS_IS_OK(status)) {
+ rid = n.out.rids->ids[0];
+ } else {
+ return status;
+ }
+
+ 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(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("OpenUser(%s) failed - %s\n", name, nt_errstr(status));
+ return status;
+ }
+
+ d.in.user_handle = &user_handle;
+ d.out.user_handle = &user_handle;
+ status = dcerpc_samr_DeleteUser(p, mem_ctx, &d);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ 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(struct torture_context *torture,
+ const char *username,
+ const char *domain,
+ uint16_t acct_type,
+ const char **random_password)
+{
+ 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;
+ struct policy_handle domain_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(torture, "dc_binding", NULL);
+
+ join = talloc(NULL, struct test_join);
+ if (join == NULL) {
+ return NULL;
+ }
+
+ ZERO_STRUCTP(join);
+
+ printf("Connecting to SAMR\n");
+
+ if (dc_binding) {
+ status = dcerpc_pipe_connect(join,
+ &join->p,
+ dc_binding,
+ &ndr_table_samr,
+ cmdline_credentials, NULL, torture->lp_ctx);
+
+ } else {
+ status = torture_rpc_connection(torture,
+ &join->p,
+ &ndr_table_samr);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return NULL;
+ }
+
+ c.in.system_name = NULL;
+ c.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ c.out.connect_handle = &handle;
+
+ status = dcerpc_samr_Connect(join->p, join, &c);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ errstr = dcerpc_errstr(join, join->p->last_fault_code);
+ }
+ printf("samr_Connect failed - %s\n", errstr);
+ return NULL;
+ }
+
+ printf("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(join->p, join, &l);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("LookupDomain failed - %s\n", nt_errstr(status));
+ 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 = &domain_handle;
+
+ status = dcerpc_samr_OpenDomain(join->p, join, &o);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("OpenDomain failed - %s\n", nt_errstr(status));
+ goto failed;
+ }
+
+ printf("Creating account %s\n", username);
+
+again:
+ name.string = username;
+ r.in.domain_handle = &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(join->p, join, &r);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
+ status = DeleteUser_byname(join->p, join, &domain_handle, name.string);
+ if (NT_STATUS_IS_OK(status)) {
+ goto again;
+ }
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("CreateUser2 failed - %s\n", nt_errstr(status));
+ 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(join->p, join, &pwp);
+ if (NT_STATUS_IS_OK(status)) {
+ policy_min_pw_len = pwp.out.info->min_password_length;
+ }
+
+ random_pw = generate_random_str(join, MAX(8, policy_min_pw_len));
+
+ printf("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;
+
+ encode_pw_buffer(u.info24.password.data, random_pw, STR_UNICODE);
+ u.info24.password_expired = 0;
+
+ status = dcerpc_fetch_session_key(join->p, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetUserInfo level %u - no session key - %s\n",
+ s.in.level, nt_errstr(status));
+ torture_leave_domain(torture, join);
+ goto failed;
+ }
+
+ arcfour_crypt_blob(u.info24.password.data, 516, &session_key);
+
+ status = dcerpc_samr_SetUserInfo(join->p, join, &s);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetUserInfo failed - %s\n", nt_errstr(status));
+ 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",
+ lp_netbios_name(torture->lp_ctx),
+ timestring(join, time(NULL)));
+
+ printf("Resetting ACB flags, force pw change time\n");
+
+ status = dcerpc_samr_SetUserInfo(join->p, join, &s);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetUserInfo failed - %s\n", nt_errstr(status));
+ goto failed;
+ }
+
+ if (random_password) {
+ *random_password = random_pw;
+ }
+
+ return join;
+
+failed:
+ torture_leave_domain(torture, join);
+ return NULL;
+}
+
+
+_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;
+
+ tj = talloc(tctx, struct test_join);
+ if (!tj) return NULL;
+
+ libnet_r = talloc(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 = cmdline_credentials;
+ libnet_r->in.binding = torture_setting_string(tctx, "binding", NULL);
+ if (!libnet_r->in.binding) {
+ libnet_r->in.binding = talloc_asprintf(libnet_r, "ncacn_np:%s", torture_setting_string(tctx, "host", 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",
+ lp_netbios_name(tctx->lp_ctx), timestring(tj, time(NULL)));
+
+ status = dcerpc_samr_SetUserInfo(tj->p, tj, &s);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetUserInfo (non-critical) failed - %s\n", nt_errstr(status));
+ }
+
+ *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 specificed 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", 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", cmdline_credentials);
+ ldb_set_opaque(ldb_ctx, "loadparm", cmdline_lp_ctx);
+
+ rtn = ldb_connect(ldb_ctx, remote_ldb_url, 0, NULL);
+ if (rtn != 0) {
+ libnet_r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ rtn = ldb_delete(ldb_ctx, server_dn);
+ if (rtn != 0) {
+ 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 *torture, 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(join->p, join, &d);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Delete of machine account %s failed\n",
+ join->netbios_name);
+ } else {
+ printf("Delete of machine account %s was successful.\n",
+ join->netbios_name);
+ }
+
+ if (join->libnet_r) {
+ status = torture_leave_ads_domain(torture, 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;
+}
+
+const char *torture_join_server_dn_str(struct test_join *join)
+{
+ if (join->libnet_r) {
+ return join->libnet_r->out.server_dn_str;
+ }
+ return NULL;
+}
+
+
+#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=<DC>,CN=Servers,CN=Default-First-Site-Name, ...
+ */
+
+ /* ask for dnsHostName of CN=<DC>,CN=Servers,CN=Default-First-Site-Name, ... */
+
+ /* ask for objectGUID of CN=NTDS Settings,CN=<DC>,CN=Servers,CN=Default-First-Site-Name, ... */
+
+ /* ask for * of CN=Default-First-Site-Name, ... */
+
+ /* search (&(|(objectClass=user)(objectClass=computer))(sAMAccountName=<machine_name>$)) in Domain Partition
+ * attributes : distinguishedName, userAccountControl
+ */
+
+ /* ask * for CN=<machine_name>,CN=Servers,CN=Default-First-Site-Name,...
+ * should fail with noSuchObject
+ */
+
+ /* add CN=<machine_name>,CN=Servers,CN=Default-First-Site-Name,...
+ *
+ * objectClass = server
+ * systemFlags = 50000000
+ * serverReferenz = CN=<machine_name>,OU=Domain Controllers,...
+ */
+
+ /* ask for * of CN=NTDS Settings,CN=<machine_name>,CN=Servers,CN=Default-First-Site-Name, ...
+ * should fail with noSuchObject
+ */
+
+ /* search for (ncname=<domain_nc>) in CN=Partitions,CN=Configuration,...
+ * attributes: ncName, dnsRoot
+ */
+
+ /* modify add CN=<machine_name>,CN=Servers,CN=Default-First-Site-Name,...
+ * serverReferenz = CN=<machine_name>,OU=Domain Controllers,...
+ * should fail with attributeOrValueExists
+ */
+
+ /* modify replace CN=<machine_name>,CN=Servers,CN=Default-First-Site-Name,...
+ * serverReferenz = CN=<machine_name>,OU=Domain Controllers,...
+ */
+
+ /* DsAddEntry to create the CN=NTDS Settings,CN=<machine_name>,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=<machine_name>,CN=Servers,CN=Default-First-Site-Name, ...
+ */
+
+/* W2K3: see libnet/libnet_become_dc.c */
+ return join;
+}
+
+#endif
diff --git a/source4/torture/rpc/unixinfo.c b/source4/torture/rpc/unixinfo.c
new file mode 100644
index 0000000000..cbe8cf0ff1
--- /dev/null
+++ b/source4/torture/rpc/unixinfo.c
@@ -0,0 +1,144 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "torture/rpc/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)
+{
+ NTSTATUS status;
+ struct unixinfo_SidToUid r;
+ struct dom_sid *sid;
+ uint64_t uid;
+
+ sid = dom_sid_parse_talloc(tctx, "S-1-5-32-1234-5432");
+ r.in.sid = *sid;
+ r.out.uid = &uid;
+
+ status = dcerpc_unixinfo_SidToUid(p, tctx, &r);
+ if (NT_STATUS_EQUAL(NT_STATUS_NONE_MAPPED, status)) {
+ } else torture_assert_ntstatus_ok(tctx, status, "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;
+
+ r.in.uid = 1000;
+ r.out.sid = &sid;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_unixinfo_UidToSid(p, tctx, &r),
+ "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;
+ NTSTATUS result;
+
+ for (i=0; i<num_uids; i++) {
+ uids[i] = i;
+ }
+
+ r.in.count = &num_uids;
+ r.in.uids = uids;
+ r.out.count = &num_uids;
+ r.out.infos = talloc_array(tctx, struct unixinfo_GetPWUidInfo, num_uids);
+
+ result = dcerpc_unixinfo_GetPWUid(p, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, result, "GetPWUid failed");
+
+ return true;
+}
+
+/*
+ test the SidToGid interface
+*/
+static bool test_sidtogid(struct torture_context *tctx, struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct unixinfo_SidToGid r;
+ struct dom_sid *sid;
+ uint64_t gid;
+
+ sid = dom_sid_parse_talloc(tctx, "S-1-5-32-1234-5432");
+ r.in.sid = *sid;
+ r.out.gid = &gid;
+
+ status = dcerpc_unixinfo_SidToGid(p, tctx, &r);
+ if (NT_STATUS_EQUAL(NT_STATUS_NONE_MAPPED, status)) {
+ } else torture_assert_ntstatus_ok(tctx, status, "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;
+
+ r.in.gid = 1000;
+ r.out.sid = &sid;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_unixinfo_GidToSid(p, tctx, &r),
+ "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 0000000000..b85dac7bf1
--- /dev/null
+++ b/source4/torture/rpc/winreg.c
@@ -0,0 +1,1925 @@
+/*
+ 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
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/ndr_winreg_c.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "libcli/security/security.h"
+#include "torture/rpc/rpc.h"
+
+#define TEST_KEY_BASE "smbtorture test"
+#define TEST_KEY1 TEST_KEY_BASE "\\spottyfoot"
+#define TEST_KEY2 TEST_KEY_BASE "\\with a SD (#1)"
+#define TEST_KEY3 TEST_KEY_BASE "\\with a subkey"
+#define TEST_KEY4 TEST_KEY_BASE "\\sd_tests"
+#define TEST_SUBKEY TEST_KEY3 "\\subkey"
+#define TEST_SUBKEY_SD TEST_KEY4 "\\subkey_sd"
+#define TEST_SUBSUBKEY_SD TEST_KEY4 "\\subkey_sd\\subsubkey_sd"
+
+#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_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ struct winreg_GetVersion r;
+ uint32_t v;
+
+ ZERO_STRUCT(r);
+ r.in.handle = handle;
+ r.out.version = &v;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_winreg_GetVersion(p, tctx, &r),
+ "GetVersion failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "GetVersion failed");
+
+ return true;
+}
+
+static bool test_NotifyChangeKeyValue(struct dcerpc_pipe *p,
+ 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(p, 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(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *handle, const char *name,
+ const char *kclass)
+{
+ struct winreg_CreateKey r;
+ struct policy_handle newhandle;
+ enum winreg_CreateAction action_taken = 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 = NULL;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_winreg_CreateKey(p, tctx, &r),
+ "CreateKey failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "CreateKey failed");
+
+ return true;
+}
+
+
+/*
+ createkey testing with a SD
+*/
+static bool test_CreateKey_sd(struct dcerpc_pipe *p,
+ 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, NULL, 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(p, 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;
+
+ 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(p, 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, NULL, 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;
+
+ 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, NULL, 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(p, 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_pipe *p, 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(p, tctx, &r),
+ "CloseKey failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "CloseKey failed");
+
+ return true;
+}
+
+static bool test_FlushKey(struct dcerpc_pipe *p, 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(p, tctx, &r),
+ "FlushKey failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "FlushKey failed");
+
+ return true;
+}
+
+static bool _test_OpenKey(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *hive_handle,
+ const char *keyname, uint32_t access_mask,
+ struct policy_handle *key_handle,
+ WERROR open_werr,
+ bool *success)
+{
+ struct winreg_OpenKey r;
+
+ ZERO_STRUCT(r);
+ r.in.parent_handle = hive_handle;
+ init_winreg_String(&r.in.keyname, keyname);
+ r.in.unknown = 0x00000000;
+ r.in.access_mask = access_mask;
+ r.out.handle = key_handle;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_winreg_OpenKey(p, tctx, &r),
+ "OpenKey failed");
+
+ torture_assert_werr_equal(tctx, r.out.result, open_werr,
+ "OpenKey failed");
+
+ if (success && W_ERROR_EQUAL(r.out.result, WERR_OK)) {
+ *success = true;
+ }
+
+ return true;
+}
+
+static bool test_OpenKey(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *hive_handle,
+ const char *keyname, struct policy_handle *key_handle)
+{
+ return _test_OpenKey(p, tctx, hive_handle, keyname,
+ SEC_FLAG_MAXIMUM_ALLOWED, key_handle,
+ WERR_OK, NULL);
+}
+
+static bool test_Cleanup(struct dcerpc_pipe *p, 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(p, 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;
+
+ torture_comment(tctx, "SecurityDescriptor get & set\n");
+
+ if (!test_OpenKey(p, tctx, handle, key, &new_handle)) {
+ return false;
+ }
+
+ if (!_test_GetSetSecurityDescriptor(p, tctx, &new_handle,
+ WERR_OK, WERR_OK)) {
+ ret = false;
+ }
+
+ if (!test_CloseKey(p, 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;
+ bool got_key = false;
+
+ if (!_test_OpenKey(p, tctx, handle, key, access_mask, &new_handle,
+ open_werr, &got_key)) {
+ return false;
+ }
+
+ if (!got_key) {
+ return true;
+ }
+
+ if (!_test_GetSetSecurityDescriptor(p, tctx, &new_handle,
+ get_werr, set_werr)) {
+ ret = false;
+ }
+
+ if (!test_CloseKey(p, 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;
+
+ if (!test_OpenKey(p, tctx, handle, key, &new_handle)) {
+ return false;
+ }
+
+ ret = test_dacl_trustee_present(p, tctx, &new_handle, sid);
+
+ test_CloseKey(p, 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;
+
+ if (!_test_OpenKey(p, tctx, handle, key, SEC_FLAG_SYSTEM_SECURITY,
+ &new_handle, WERR_OK, NULL)) {
+ return false;
+ }
+
+ ret = test_sacl_trustee_present(p, tctx, &new_handle, sid);
+
+ test_CloseKey(p, 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;
+
+ if (!test_OpenKey(p, tctx, handle, key, &new_handle)) {
+ return false;
+ }
+
+ ret = test_owner_present(p, tctx, &new_handle, sid);
+
+ test_CloseKey(p, 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;
+
+ if (!test_OpenKey(p, tctx, handle, key, &new_handle)) {
+ return false;
+ }
+
+ ret = test_group_present(p, tctx, &new_handle, sid);
+
+ test_CloseKey(p, 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;
+
+ if (!test_OpenKey(p, tctx, handle, key, &new_handle)) {
+ return false;
+ }
+
+ if (!test_SetKeySecurity(p, tctx, &new_handle, sd)) {
+ ret = false;
+ }
+
+ if (!test_CloseKey(p, 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;
+
+ if (!test_OpenKey(p, tctx, handle, key, &new_handle)) {
+ return false;
+ }
+
+ if (!test_GetKeySecurity(p, tctx, &new_handle, sd)) {
+ ret = false;
+ }
+
+ if (!test_CloseKey(p, 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;
+ NTSTATUS status;
+ bool ret = true;
+
+ torture_comment(tctx, "SecurityDescriptor inheritance\n");
+
+ if (!test_OpenKey(p, 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);
+
+ status = security_descriptor_dacl_add(sd, ace);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("failed to add ace: %s\n", nt_errstr(status));
+ return false;
+ }
+
+ /* 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;
+ }
+
+ if (!test_dacl_ace_present(p, tctx, &new_handle, ace)) {
+ printf("new ACE not present!\n");
+ return false;
+ }
+
+ if (!test_CloseKey(p, tctx, &new_handle)) {
+ return false;
+ }
+
+ if (!test_CreateKey(p, tctx, handle, TEST_SUBKEY_SD, NULL)) {
+ ret = false;
+ goto out;
+ }
+
+ if (!test_OpenKey(p, tctx, handle, TEST_SUBKEY_SD, &new_handle)) {
+ ret = false;
+ goto out;
+ }
+
+ if (!test_dacl_ace_present(p, tctx, &new_handle, ace)) {
+ printf("inherited ACE not present!\n");
+ ret = false;
+ goto out;
+ }
+
+ test_CloseKey(p, tctx, &new_handle);
+ if (!test_CreateKey(p, tctx, handle, TEST_SUBSUBKEY_SD, NULL)) {
+ ret = false;
+ goto out;
+ }
+
+ if (!test_OpenKey(p, tctx, handle, TEST_SUBSUBKEY_SD, &new_handle)) {
+ ret = false;
+ goto out;
+ }
+
+ if (!test_dacl_ace_present(p, tctx, &new_handle, ace)) {
+ printf("inherited ACE not present!\n");
+ ret = false;
+ goto out;
+ }
+
+ out:
+ test_CloseKey(p, tctx, &new_handle);
+ test_Cleanup(p, tctx, handle, TEST_SUBKEY_SD);
+ test_RestoreSecurity(p, tctx, handle, key, sd_orig);
+
+ return true;
+}
+
+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;
+ NTSTATUS status;
+ bool ret = true;
+ uint8_t ace_flags = 0x0;
+
+ torture_comment(tctx, "SecurityDescriptor inheritance block\n");
+
+ if (!test_OpenKey(p, 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);
+
+ status = security_descriptor_dacl_add(sd, ace);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("failed to add ace: %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if (!_test_SetKeySecurity(p, tctx, &new_handle, NULL, sd, WERR_OK)) {
+ return false;
+ }
+
+ if (!test_dacl_ace_present(p, tctx, &new_handle, ace)) {
+ printf("new ACE not present!\n");
+ return false;
+ }
+
+ if (!test_CloseKey(p, tctx, &new_handle)) {
+ return false;
+ }
+
+ if (!test_CreateKey(p, tctx, handle, TEST_SUBSUBKEY_SD, NULL)) {
+ return false;
+ }
+
+ if (!test_OpenKey(p, tctx, handle, TEST_SUBSUBKEY_SD, &new_handle)) {
+ ret = false;
+ goto out;
+ }
+
+ if (test_dacl_ace_present(p, tctx, &new_handle, ace)) {
+ printf("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)) {
+ printf("inherited trustee SID present but should not!\n");
+ ret = false;
+ goto out;
+ }
+
+ test_CloseKey(p, tctx, &new_handle);
+
+ if (!test_OpenKey(p, tctx, handle, TEST_SUBKEY_SD, &new_handle)) {
+ ret = false;
+ goto out;
+ }
+
+ if (test_dacl_ace_present(p, tctx, &new_handle, ace)) {
+ printf("inherited ACE present but should not!\n");
+ ret = false;
+ goto out;
+ }
+
+ if (!test_dacl_trustee_flags_present(p, tctx, &new_handle, sid, ace_flags)) {
+ printf("inherited trustee SID with flags 0x%02x not present!\n",
+ ace_flags);
+ ret = false;
+ goto out;
+ }
+
+ out:
+ test_CloseKey(p, tctx, &new_handle);
+ test_Cleanup(p, 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_BADFILE, 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;
+ bool open_success = false;
+
+ torture_comment(tctx, "SecurityDescriptor (%s) sets for secinfo: "
+ "0x%08x, access_mask: 0x%08x\n",
+ test, sec_info, access_mask);
+
+ if (!_test_OpenKey(p, tctx, handle, key,
+ access_mask,
+ &new_handle,
+ WERR_OK,
+ &open_success)) {
+ return false;
+ }
+
+ if (!open_success) {
+ printf("key did not open\n");
+ test_CloseKey(p, tctx, &new_handle);
+ return false;
+ }
+
+ 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(p, tctx, &new_handle);
+ return false;
+ }
+
+ test_CloseKey(p, 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_owner, 0, WERR_OK,
+ false, (secinfo_verify_fn)_test_owner_present },
+ { sd_owner, SECINFO_OWNER, WERR_OK,
+ true, (secinfo_verify_fn)_test_owner_present },
+ { sd_owner, SECINFO_GROUP, WERR_INVALID_PARAM },
+ { sd_owner, SECINFO_DACL, WERR_OK,
+ true, (secinfo_verify_fn)_test_owner_present },
+ { sd_owner, SECINFO_SACL, WERR_ACCESS_DENIED },
+ };
+
+ uint32_t sd_owner_good_access_masks[] = {
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ /* SEC_STD_WRITE_OWNER, */
+ };
+
+ struct winreg_secinfo_table sec_info_group_tests[] = {
+ { sd_group, 0, WERR_OK,
+ false, (secinfo_verify_fn)_test_group_present },
+ { sd_group, SECINFO_OWNER, WERR_INVALID_PARAM },
+ { sd_group, SECINFO_GROUP, WERR_OK,
+ true, (secinfo_verify_fn)_test_group_present },
+ { sd_group, SECINFO_DACL, WERR_OK,
+ true, (secinfo_verify_fn)_test_group_present },
+ { sd_group, SECINFO_SACL, WERR_ACCESS_DENIED },
+ };
+
+ uint32_t sd_group_good_access_masks[] = {
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ };
+
+ struct winreg_secinfo_table sec_info_dacl_tests[] = {
+ { sd_dacl, 0, WERR_OK,
+ false, (secinfo_verify_fn)_test_dacl_trustee_present },
+ { sd_dacl, SECINFO_OWNER, WERR_INVALID_PARAM },
+ { sd_dacl, SECINFO_GROUP, WERR_INVALID_PARAM },
+ { sd_dacl, SECINFO_DACL, WERR_OK,
+ true, (secinfo_verify_fn)_test_dacl_trustee_present },
+ { sd_dacl, SECINFO_SACL, WERR_ACCESS_DENIED },
+ };
+
+ uint32_t sd_dacl_good_access_masks[] = {
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ SEC_STD_WRITE_DAC,
+ };
+
+ struct winreg_secinfo_table sec_info_sacl_tests[] = {
+ { sd_sacl, 0, WERR_OK,
+ false, (secinfo_verify_fn)_test_sacl_trustee_present },
+ { sd_sacl, SECINFO_OWNER, WERR_INVALID_PARAM },
+ { sd_sacl, SECINFO_GROUP, WERR_INVALID_PARAM },
+ { sd_sacl, SECINFO_DACL, WERR_OK,
+ false, (secinfo_verify_fn)_test_sacl_trustee_present },
+ { sd_sacl, SECINFO_SACL, WERR_OK,
+ true, (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))
+ {
+ printf("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))
+ {
+ printf("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))
+ {
+ printf("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))
+ {
+ printf("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)) {
+ printf("test_SecurityDescriptor failed\n");
+ ret = false;
+ }
+
+ if (!test_SecurityDescriptorInheritance(p, tctx, handle, key)) {
+ printf("test_SecurityDescriptorInheritance failed\n");
+ ret = false;
+ }
+
+ if (!test_SecurityDescriptorBlockInheritance(p, tctx, handle, key)) {
+ printf("test_SecurityDescriptorBlockInheritance failed\n");
+ ret = false;
+ }
+
+ if (!test_SecurityDescriptorsSecInfo(p, tctx, handle, key)) {
+ printf("test_SecurityDescriptorsSecInfo failed\n");
+ ret = false;
+ }
+
+ if (!test_SecurityDescriptorsMasks(p, tctx, handle, key)) {
+ printf("test_SecurityDescriptorsMasks failed\n");
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool test_DeleteKey(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *handle, const char *key)
+{
+ NTSTATUS status;
+ struct winreg_DeleteKey r;
+
+ r.in.handle = handle;
+ init_winreg_String(&r.in.key, key);
+
+ status = dcerpc_winreg_DeleteKey(p, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "DeleteKey failed");
+ torture_assert_werr_ok(tctx, r.out.result, "DeleteKey failed");
+
+ return true;
+}
+
+static bool test_QueryInfoKey(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle, char *kclass)
+{
+ 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(p, tctx, &r),
+ "QueryInfoKey failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "QueryInfoKey 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;
+
+ 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(p, 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(p, 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_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ const char *valuename)
+{
+ struct winreg_QueryMultipleValues r;
+ NTSTATUS status;
+ uint32_t bufsize=0;
+
+ ZERO_STRUCT(r);
+ r.in.key_handle = handle;
+ r.in.values = r.out.values = talloc_array(tctx, struct QueryMultipleValue, 1);
+ r.in.values[0].name = talloc(tctx, struct winreg_String);
+ r.in.values[0].name->name = valuename;
+ r.in.values[0].offset = 0;
+ r.in.values[0].length = 0;
+ r.in.values[0].type = 0;
+
+ 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);
+
+ status = dcerpc_winreg_QueryMultipleValues(p, tctx, &r);
+
+ if(NT_STATUS_IS_ERR(status))
+ torture_fail(tctx, "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_QueryValue(struct dcerpc_pipe *p,
+ 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(p, 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_EnumValue(struct dcerpc_pipe *p, 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 buf8;
+ struct winreg_ValNameBuf name;
+
+ name.name = "";
+ name.size = 1024;
+
+ 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.value = &buf8;
+ r.in.length = &zero;
+ r.in.size = &size;
+
+ do {
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winreg_EnumValue(p, tctx, &r),
+ "EnumValue failed");
+
+ if (W_ERROR_IS_OK(r.out.result)) {
+ ret &= test_QueryValue(p, tctx, handle,
+ r.out.name->name);
+ ret &= test_QueryMultipleValues(p, tctx, handle,
+ r.out.name->name);
+ }
+
+ 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_pipe *p,
+ 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(p, 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;
+
+ 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(p, tctx, &r),
+ "InitiateSystemShutdown failed");
+
+ torture_assert_werr_ok(tctx, r.out.result,
+ "InitiateSystemShutdown failed");
+
+ return test_AbortSystemShutdown(p, tctx);
+}
+
+
+static bool test_InitiateSystemShutdownEx(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct winreg_InitiateSystemShutdownEx r;
+ uint16_t hostname = 0x0;
+
+ 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(p, tctx, &r),
+ "InitiateSystemShutdownEx failed");
+
+ torture_assert_werr_ok(tctx, r.out.result,
+ "InitiateSystemShutdownEx failed");
+
+ return test_AbortSystemShutdown(p, 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)
+{
+ if (depth == MAX_DEPTH)
+ return true;
+
+ if (!test_QueryInfoKey(p, tctx, handle, NULL)) {
+ }
+
+ if (!test_NotifyChangeKeyValue(p, tctx, handle)) {
+ }
+
+ if (test_security && !test_GetKeySecurity(p, tctx, handle, NULL)) {
+ }
+
+ if (!test_EnumKey(p, tctx, handle, depth, test_security)) {
+ }
+
+ if (!test_EnumValue(p, tctx, handle, 0xFF, 0xFFFF)) {
+ }
+
+ test_CloseKey(p, tctx, handle);
+
+ return true;
+}
+
+typedef NTSTATUS (*winreg_open_fn)(struct dcerpc_pipe *, TALLOC_CTX *, void *);
+
+static bool test_Open_Security(struct torture_context *tctx,
+ struct dcerpc_pipe *p, void *userdata)
+{
+ struct policy_handle handle, newhandle;
+ bool ret = true, created2 = false;
+ bool created4 = false;
+ struct winreg_OpenHKLM r;
+
+ winreg_open_fn open_fn = userdata;
+
+ ZERO_STRUCT(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(p, tctx, &r),
+ "open");
+
+ test_Cleanup(p, tctx, &handle, TEST_KEY_BASE);
+
+ if (!test_CreateKey(p, tctx, &handle, TEST_KEY_BASE, NULL)) {
+ torture_comment(tctx,
+ "CreateKey (TEST_KEY_BASE) failed\n");
+ }
+
+ if (test_CreateKey_sd(p, tctx, &handle, TEST_KEY2,
+ NULL, &newhandle)) {
+ created2 = true;
+ }
+
+ if (created2 && !test_CloseKey(p, tctx, &newhandle)) {
+ printf("CloseKey failed\n");
+ ret = false;
+ }
+
+ if (test_CreateKey_sd(p, tctx, &handle, TEST_KEY4, NULL, &newhandle)) {
+ created4 = true;
+ }
+
+ if (created4 && !test_CloseKey(p, tctx, &newhandle)) {
+ printf("CloseKey failed\n");
+ ret = false;
+ }
+
+ if (created4 && !test_SecurityDescriptors(p, tctx, &handle, TEST_KEY4)) {
+ ret = false;
+ }
+
+ if (created4 && !test_DeleteKey(p, tctx, &handle, TEST_KEY4)) {
+ printf("DeleteKey failed\n");
+ ret = false;
+ }
+
+ if (created2 && !test_DeleteKey(p, tctx, &handle, TEST_KEY2)) {
+ printf("DeleteKey failed\n");
+ ret = false;
+ }
+
+ /* The HKCR hive has a very large fanout */
+ if (open_fn == (void *)dcerpc_winreg_OpenHKCR) {
+ if(!test_key(p, tctx, &handle, MAX_DEPTH - 1, true)) {
+ ret = false;
+ }
+ } else {
+ if (!test_key(p, tctx, &handle, 0, true)) {
+ ret = false;
+ }
+ }
+
+ test_Cleanup(p, tctx, &handle, TEST_KEY_BASE);
+
+ return ret;
+}
+
+static bool test_Open(struct torture_context *tctx, struct dcerpc_pipe *p,
+ void *userdata)
+{
+ struct policy_handle handle, newhandle;
+ bool ret = true, created = false, deleted = false;
+ bool created3 = false, created_subkey = false;
+ struct winreg_OpenHKLM r;
+
+ winreg_open_fn open_fn = userdata;
+
+ ZERO_STRUCT(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(p, tctx, &r),
+ "open");
+
+ test_Cleanup(p, tctx, &handle, TEST_KEY_BASE);
+
+ if (!test_CreateKey(p, tctx, &handle, TEST_KEY_BASE, NULL)) {
+ torture_comment(tctx,
+ "CreateKey (TEST_KEY_BASE) failed\n");
+ }
+
+ if (!test_CreateKey(p, tctx, &handle, TEST_KEY1, NULL)) {
+ torture_comment(tctx,
+ "CreateKey failed - not considering a failure\n");
+ } else {
+ created = true;
+ }
+
+ if (created && !test_FlushKey(p, tctx, &handle)) {
+ torture_comment(tctx, "FlushKey failed\n");
+ ret = false;
+ }
+
+ if (created && !test_OpenKey(p, tctx, &handle, TEST_KEY1, &newhandle))
+ torture_fail(tctx,
+ "CreateKey failed (OpenKey after Create didn't work)\n");
+
+ if (created && !test_CloseKey(p, tctx, &newhandle))
+ torture_fail(tctx,
+ "CreateKey failed (CloseKey after Open didn't work)\n");
+
+ if (created && !test_DeleteKey(p, tctx, &handle, TEST_KEY1)) {
+ torture_comment(tctx, "DeleteKey failed\n");
+ ret = false;
+ } else {
+ deleted = true;
+ }
+
+ if (created && !test_FlushKey(p, tctx, &handle)) {
+ torture_comment(tctx, "FlushKey failed\n");
+ ret = false;
+ }
+
+ if (created && deleted &&
+ !_test_OpenKey(p, tctx, &handle, TEST_KEY1,
+ SEC_FLAG_MAXIMUM_ALLOWED, &newhandle,
+ WERR_BADFILE, NULL)) {
+ torture_comment(tctx,
+ "DeleteKey failed (OpenKey after Delete "
+ "did not return WERR_BADFILE)\n");
+ ret = false;
+ }
+
+ if (!test_GetVersion(p, tctx, &handle)) {
+ torture_comment(tctx, "GetVersion failed\n");
+ ret = false;
+ }
+
+ if (created && test_CreateKey(p, tctx, &handle, TEST_KEY3, NULL)) {
+ created3 = true;
+ }
+
+ if (created3 &&
+ test_CreateKey(p, tctx, &handle, TEST_SUBKEY, NULL)) {
+ created_subkey = true;
+ }
+
+ if (created_subkey &&
+ !test_DeleteKey(p, tctx, &handle, TEST_KEY3)) {
+ printf("DeleteKey failed\n");
+ ret = false;
+ }
+
+ /* The HKCR hive has a very large fanout */
+ if (open_fn == (void *)dcerpc_winreg_OpenHKCR) {
+ if(!test_key(p, tctx, &handle, MAX_DEPTH - 1, false)) {
+ ret = false;
+ }
+ } else {
+ if (!test_key(p, tctx, &handle, 0, false)) {
+ ret = false;
+ }
+ }
+
+ test_Cleanup(p, tctx, &handle, TEST_KEY_BASE);
+
+ 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;
+
+ /* Basic tests without security descriptors */
+ torture_rpc_tcase_add_test_ex(tcase, "HKLM-basic",
+ test_Open,
+ (winreg_open_fn)dcerpc_winreg_OpenHKLM);
+ torture_rpc_tcase_add_test_ex(tcase, "HKU-basic",
+ test_Open,
+ (winreg_open_fn)dcerpc_winreg_OpenHKU);
+ torture_rpc_tcase_add_test_ex(tcase, "HKCR-basic",
+ test_Open,
+ (winreg_open_fn)dcerpc_winreg_OpenHKCR);
+ torture_rpc_tcase_add_test_ex(tcase, "HKCU-basic",
+ test_Open,
+ (winreg_open_fn)dcerpc_winreg_OpenHKCU);
+
+ /* Security descriptor tests */
+ torture_rpc_tcase_add_test_ex(tcase, "HKLM-security",
+ test_Open_Security,
+ (winreg_open_fn)dcerpc_winreg_OpenHKLM);
+ torture_rpc_tcase_add_test_ex(tcase, "HKU-security",
+ test_Open_Security,
+ (winreg_open_fn)dcerpc_winreg_OpenHKU);
+ torture_rpc_tcase_add_test_ex(tcase, "HKCR-security",
+ test_Open_Security,
+ (winreg_open_fn)dcerpc_winreg_OpenHKCR);
+ torture_rpc_tcase_add_test_ex(tcase, "HKCU-security",
+ test_Open_Security,
+ (winreg_open_fn)dcerpc_winreg_OpenHKCU);
+
+ return suite;
+}
diff --git a/source4/torture/rpc/wkssvc.c b/source4/torture/rpc/wkssvc.c
new file mode 100644
index 0000000000..3c34229dff
--- /dev/null
+++ b/source4/torture/rpc/wkssvc.c
@@ -0,0 +1,1451 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/ndr_wkssvc_c.h"
+#include "torture/rpc/rpc.h"
+#include "lib/cmdline/popt_common.h"
+#include "param/param.h"
+#include "../lib/crypto/crypto.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;
+
+ r.in.server_name = dcerpc_server_name(p);
+ r.out.info = &info;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ r.in.level = levels[i];
+ torture_comment(tctx, "testing NetWkstaGetInfo level %u\n",
+ r.in.level);
+ status = dcerpc_wkssvc_NetWkstaGetInfo(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ talloc_asprintf(tctx, "NetWkstaGetInfo level %u failed",
+ r.in.level));
+ torture_assert_werr_ok(tctx, r.out.result,
+ talloc_asprintf(tctx, "NetWkstaGetInfo level %u failed",
+ r.in.level));
+ }
+
+ return true;
+}
+
+static bool test_NetWkstaTransportEnum(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct wkssvc_NetWkstaTransportEnum r;
+ uint32_t resume_handle = 0;
+ struct wkssvc_NetWkstaTransportInfo info;
+ union wkssvc_NetWkstaTransportCtr ctr;
+ struct wkssvc_NetWkstaTransportCtr0 ctr0;
+ uint32_t total_entries = 0;
+
+ 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(p, 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;
+
+ 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(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "NetrWkstaTransportAdd failed");
+ torture_assert_werr_equal(tctx, r.out.result,
+ WERR_INVALID_PARAM,
+ "NetrWkstaTransportAdd level 0 failed");
+
+ return true;
+}
+
+static bool test_NetrWkstaTransportDel(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct wkssvc_NetrWkstaTransportDel r;
+
+ 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(p, 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;
+
+ for (i=0; i<ARRAY_SIZE(levels); i++) {
+
+ ZERO_STRUCT(info);
+
+ info.level = levels[i];
+ switch (info.level) {
+ case 0:
+ user0 = talloc_zero(tctx,
+ struct wkssvc_NetWkstaEnumUsersCtr0);
+ info.ctr.user0 = user0;
+ break;
+ case 1:
+ user1 = talloc_zero(tctx,
+ struct wkssvc_NetWkstaEnumUsersCtr1);
+ info.ctr.user1 = user1;
+ break;
+ default:
+ break;
+ }
+
+ r.in.server_name = 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;
+
+ torture_comment(tctx, "testing NetWkstaEnumUsers level %u\n",
+ levels[i]);
+
+ status = dcerpc_wkssvc_NetWkstaEnumUsers(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "NetWkstaEnumUsers failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "NetWkstaEnumUsers failed");
+ }
+
+ return true;
+}
+
+static bool test_NetrWkstaUserGetInfo(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct wkssvc_NetrWkstaUserGetInfo r;
+ union wkssvc_NetrWkstaUserInfo info;
+ const char *dom = lp_workgroup(tctx->lp_ctx);
+ struct cli_credentials *creds = cmdline_credentials;
+ const char *user = cli_credentials_get_username(creds);
+ int i;
+
+ 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_PARAM },
+ { dom, 1, WERR_INVALID_PARAM },
+ { dom, 1101, WERR_INVALID_PARAM },
+ { user, 0, WERR_INVALID_PARAM },
+ { user, 1, WERR_INVALID_PARAM },
+ { user, 1101, WERR_INVALID_PARAM },
+ };
+
+ for (i=0; i<ARRAY_SIZE(tests); i++) {
+ r.in.unknown = tests[i].unknown;
+ r.in.level = tests[i].level;
+ r.out.info = &info;
+
+ torture_comment(tctx, "testing NetrWkstaUserGetInfo level %u\n",
+ r.in.level);
+
+ status = dcerpc_wkssvc_NetrWkstaUserGetInfo(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "NetrWkstaUserGetInfo failed");
+ torture_assert_werr_equal(tctx, r.out.result,
+ tests[i].result,
+ "NetrWkstaUserGetInfo failed");
+ }
+
+ return true;
+}
+
+static bool test_NetrUseEnum(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;
+ struct wkssvc_NetrUseEnumCtr1 *use1;
+ struct wkssvc_NetrUseEnumCtr2 *use2;
+ uint32_t levels[] = { 0, 1, 2 };
+ int i;
+
+ for (i=0; i<ARRAY_SIZE(levels); i++) {
+
+ ZERO_STRUCT(info);
+
+ info.level = levels[i];
+ switch (info.level) {
+ case 0:
+ use0 = talloc_zero(tctx, struct wkssvc_NetrUseEnumCtr0);
+ info.ctr.ctr0 = use0;
+ break;
+ case 1:
+ use1 = talloc_zero(tctx, struct wkssvc_NetrUseEnumCtr1);
+ info.ctr.ctr1 = use1;
+ break;
+ case 2:
+ use2 = talloc_zero(tctx, struct wkssvc_NetrUseEnumCtr2);
+ info.ctr.ctr2 = use2;
+ break;
+ default:
+ break;
+ }
+
+ 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;
+
+ torture_comment(tctx, "testing NetrUseEnum level %u\n",
+ levels[i]);
+
+ status = dcerpc_wkssvc_NetrUseEnum(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "NetrUseEnum failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "NetrUseEnum failed");
+ }
+
+ return true;
+}
+
+static bool test_NetrUseAdd(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct wkssvc_NetrUseAdd r;
+ struct wkssvc_NetrUseInfo0 info0;
+ struct wkssvc_NetrUseInfo1 info1;
+ union wkssvc_NetrUseGetInfoCtr *ctr;
+ uint32_t parm_err = 0;
+
+ 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(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "NetrUseAdd failed");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_UNKNOWN_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(p, 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;
+
+ 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(p, 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;
+
+ 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(p, 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;
+
+ 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(p, 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; i<ARRAY_SIZE(levels); i++) {
+
+ if (!test_NetrUseGetInfo_level(tctx, p, use_name,
+ levels[i],
+ WERR_OK))
+ {
+ if (levels[i] != 0) {
+ return false;
+ }
+ }
+ }
+
+ use_name = r.out.info->ctr.ctr0->array[k].remote;
+
+ for (i=0; i<ARRAY_SIZE(levels); i++) {
+
+ if (!test_NetrUseGetInfo_level(tctx, p, use_name,
+ levels[i],
+ WERR_NOT_CONNECTED))
+ {
+ if (levels[i] != 0) {
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+static bool test_NetrLogonDomainNameAdd(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct wkssvc_NetrLogonDomainNameAdd r;
+
+ r.in.domain_name = lp_workgroup(tctx->lp_ctx);
+
+ torture_comment(tctx, "testing NetrLogonDomainNameAdd\n");
+
+ status = dcerpc_wkssvc_NetrLogonDomainNameAdd(p, 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;
+
+ r.in.domain_name = lp_workgroup(tctx->lp_ctx);
+
+ torture_comment(tctx, "testing NetrLogonDomainNameDel\n");
+
+ status = dcerpc_wkssvc_NetrLogonDomainNameDel(p, 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,
+ int *num_names)
+{
+ NTSTATUS status;
+ struct wkssvc_NetrEnumerateComputerNames r;
+ struct wkssvc_ComputerNamesCtr *ctr;
+ int i;
+
+ 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(p, 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; i<ctr->count; 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; i<ARRAY_SIZE(levels); i++) {
+
+ if (!test_NetrEnumerateComputerNames_level(tctx,
+ p,
+ levels[i],
+ NULL, NULL))
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool test_NetrValidateName(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct wkssvc_NetrValidateName r;
+ uint16_t levels[] = {0,1,2,3,4,5};
+ int i;
+
+ for (i=0; i<ARRAY_SIZE(levels); i++) {
+
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.name = lp_workgroup(tctx->lp_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(p, 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;
+
+ for (i=0; i<ARRAY_SIZE(levels); i++) {
+
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.name = lp_workgroup(tctx->lp_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(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "NetrValidateName2 failed");
+ torture_assert_werr_equal(tctx, r.out.result,
+ WERR_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;
+ int num_names = 0;
+ int i;
+
+ 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(p, 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; i<num_names; i++) {
+ if (strequal(names[i], SMBTORTURE_ALTERNATE_NAME)) {
+ return true;
+ }
+ }
+
+ torture_comment(tctx, "new alternate name not set\n");
+
+ return false;
+}
+
+static bool test_NetrRemoveAlternateComputerName(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct wkssvc_NetrRemoveAlternateComputerName r;
+ const char **names = NULL;
+ int num_names = 0;
+ int i;
+
+ 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(p, 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; i<num_names; i++) {
+ if (strequal(names[i], SMBTORTURE_ALTERNATE_NAME)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool test_NetrSetPrimaryComputername_name(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *name)
+{
+ NTSTATUS status;
+ struct wkssvc_NetrSetPrimaryComputername r;
+
+ 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(p, 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;
+ int 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;
+
+ 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(p, 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;
+
+ 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(p, 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;
+ int 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;
+
+ 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(p, 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;
+ uint8_t *msg;
+
+ push_ucs2_talloc(tctx, (void **)&msg, message, &size);
+
+ 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 = msg;
+ r.in.message_size = size;
+
+ torture_comment(tctx, "testing NetrMessageBufferSend\n");
+
+ status = dcerpc_wkssvc_NetrMessageBufferSend(p, 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 = "";
+
+ 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(p, 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 = "";
+
+ 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(p, 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;
+
+ r.in.server_name = dcerpc_server_name(p);
+ r.in.domain_name = lp_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(p, 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;
+
+ r.in.server_name = dcerpc_server_name(p);
+ r.in.domain_name = lp_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(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "NetrGetJoinableOus2 failed");
+ torture_assert_werr_equal(tctx, r.out.result,
+ WERR_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 = cmdline_credentials;
+ const char *user = cli_credentials_get_username(creds);
+ const char *admin_account = NULL;
+
+ admin_account = talloc_asprintf(tctx, "%s\\%s",
+ lp_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(p, 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 = cmdline_credentials;
+ const char *user = cli_credentials_get_username(creds);
+ const char *admin_account = NULL;
+
+ admin_account = talloc_asprintf(tctx, "%s\\%s",
+ lp_workgroup(tctx->lp_ctx),
+ user);
+
+ r.in.server_name = dcerpc_server_name(p);
+ r.in.domain_name = lp_realm(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(p, 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;
+}
+
+/* encode a wkssvc_PasswordBuffer for remote joining/unjoining:
+ *
+ * similar to samr_CryptPasswordEx. Different: 8byte confounder (instead of
+ * 16byte), confounder in front of the 516 byte buffer (instead of after that
+ * buffer), calling MD5Update() first with session_key and then with confounder
+ * (vice versa in samr) - Guenther */
+
+static void encode_wkssvc_join_password_buffer(TALLOC_CTX *mem_ctx,
+ const char *pwd,
+ DATA_BLOB *session_key,
+ struct wkssvc_PasswordBuffer *pwd_buf)
+{
+ uint8_t buffer[516];
+ struct MD5Context ctx;
+
+ DATA_BLOB confounded_session_key = data_blob_talloc(mem_ctx, NULL, 16);
+
+ int confounder_len = 8;
+ uint8_t confounder[8];
+
+ encode_pw_buffer(buffer, pwd, STR_UNICODE);
+
+ generate_random_buffer((uint8_t *)confounder, confounder_len);
+
+ MD5Init(&ctx);
+ MD5Update(&ctx, session_key->data, session_key->length);
+ MD5Update(&ctx, confounder, confounder_len);
+ MD5Final(confounded_session_key.data, &ctx);
+
+ arcfour_crypt_blob(buffer, 516, &confounded_session_key);
+
+ memcpy(&pwd_buf->data[0], confounder, confounder_len);
+ memcpy(&pwd_buf->data[8], buffer, 516);
+
+ data_blob_free(&confounded_session_key);
+}
+
+/*
+ * 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;
+ DATA_BLOB session_key;
+
+ /* FIXME: this test assumes to join workstations / servers and does not
+ * handle DCs (WERR_SETUP_DOMAIN_CONTROLLER) */
+
+ if (!test_GetJoinInformation(tctx, p, &join_status, &join_name))
+ {
+ return false;
+ }
+
+ switch (join_status) {
+ case NET_SETUP_DOMAIN_NAME:
+ expected_err = WERR_SETUP_ALREADY_JOINED;
+ 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;
+ }
+
+ encode_wkssvc_join_password_buffer(tctx, domain_admin_password,
+ &session_key, &pwd_buf);
+
+ 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(p, 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;
+ DATA_BLOB session_key;
+
+ /* FIXME: this test assumes to join workstations / servers and does not
+ * handle DCs (WERR_SETUP_DOMAIN_CONTROLLER) */
+
+ if (!test_GetJoinInformation(tctx, p, &join_status, &join_name))
+ {
+ return false;
+ }
+
+ switch (join_status) {
+ case NET_SETUP_UNJOINED:
+ expected_err = WERR_SETUP_NOT_JOINED;
+ 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;
+ }
+
+ encode_wkssvc_join_password_buffer(tctx, domain_admin_password,
+ &session_key, &pwd_buf);
+
+ 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(p, 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/smb2/config.mk b/source4/torture/smb2/config.mk
new file mode 100644
index 0000000000..e0fc29f278
--- /dev/null
+++ b/source4/torture/smb2/config.mk
@@ -0,0 +1,29 @@
+
+#################################
+# Start SUBSYSTEM TORTURE_SMB2
+[MODULE::TORTURE_SMB2]
+SUBSYSTEM = smbtorture
+OUTPUT_TYPE = MERGED_OBJ
+INIT_FUNCTION = torture_smb2_init
+PRIVATE_DEPENDENCIES = \
+ LIBCLI_SMB2 POPT_CREDENTIALS torture
+# End SUBSYSTEM TORTURE_SMB2
+#################################
+
+TORTURE_SMB2_OBJ_FILES = $(addprefix $(torturesrcdir)/smb2/, \
+ connect.o \
+ scan.o \
+ util.o \
+ getinfo.o \
+ setinfo.o \
+ find.o \
+ lock.o \
+ notify.o \
+ smb2.o \
+ persistent_handles.o \
+ oplocks.o \
+ create.o \
+ read.o)
+
+
+$(eval $(call proto_header_template,$(torturesrcdir)/smb2/proto.h,$(TORTURE_SMB2_OBJ_FILES:.o=.c)))
diff --git a/source4/torture/smb2/connect.c b/source4/torture/smb2/connect.c
new file mode 100644
index 0000000000..01ffffa484
--- /dev/null
+++ b/source4/torture/smb2/connect.c
@@ -0,0 +1,268 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#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"
+
+/*
+ send a close
+*/
+static NTSTATUS torture_smb2_close(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)) {
+ printf("close failed - %s\n", nt_errstr(status));
+ return status;
+ }
+
+ if (DEBUGLVL(1)) {
+ printf("Close gave:\n");
+ printf("create_time = %s\n", nt_time_string(tmp_ctx, io.out.create_time));
+ printf("access_time = %s\n", nt_time_string(tmp_ctx, io.out.access_time));
+ printf("write_time = %s\n", nt_time_string(tmp_ctx, io.out.write_time));
+ printf("change_time = %s\n", nt_time_string(tmp_ctx, io.out.change_time));
+ printf("alloc_size = %lld\n", (long long)io.out.alloc_size);
+ printf("size = %lld\n", (long long)io.out.size);
+ printf("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;
+
+ if (torture_setting_bool(tctx, "dangerous", false)) {
+ data = data_blob_talloc(tree, NULL, 160000);
+ } else if (torture_setting_bool(tctx, "samba4", false)) {
+ data = data_blob_talloc(tree, NULL, UINT16_MAX);
+ } else {
+ data = data_blob_talloc(tree, NULL, 120000);
+ }
+ for (i=0;i<data.length;i++) {
+ data.data[i] = i;
+ }
+
+ ZERO_STRUCT(w);
+ w.in.file.handle = handle;
+ w.in.offset = 0;
+ w.in.data = data;
+
+ status = smb2_write(tree, &w);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("write failed - %s\n", nt_errstr(status));
+ return status;
+ }
+
+ torture_smb2_all_info(tree, handle);
+
+ status = smb2_write(tree, &w);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("write failed - %s\n", nt_errstr(status));
+ return status;
+ }
+
+ torture_smb2_all_info(tree, handle);
+
+ ZERO_STRUCT(f);
+ f.in.file.handle = handle;
+
+ status = smb2_flush(tree, &f);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("flush failed - %s\n", nt_errstr(status));
+ return status;
+ }
+
+ ZERO_STRUCT(r);
+ r.in.file.handle = handle;
+ r.in.length = data.length;
+ r.in.offset = 0;
+
+ status = smb2_read(tree, tree, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("read failed - %s\n", nt_errstr(status));
+ return status;
+ }
+
+ if (data.length != r.out.data.length ||
+ memcmp(data.data, r.out.data.data, data.length) != 0) {
+ printf("read data mismatch\n");
+ return NT_STATUS_NET_WRITE_FAULT;
+ }
+
+ return status;
+}
+
+
+/*
+ send a create
+*/
+static struct smb2_handle torture_smb2_createfile(struct smb2_tree *tree,
+ const char *fname)
+{
+ struct smb2_create io;
+ NTSTATUS status;
+ TALLOC_CTX *tmp_ctx = talloc_new(tree);
+
+ ZERO_STRUCT(io);
+ io.in.oplock_level = 0;
+ io.in.desired_access = SEC_RIGHTS_FILE_ALL;
+ 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 = NTCREATEX_OPTIONS_WRITE_THROUGH;
+ io.in.fname = fname;
+
+ status = smb2_create(tree, tmp_ctx, &io);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("create1 failed - %s\n", nt_errstr(status));
+ return io.out.file.handle;
+ }
+
+ if (DEBUGLVL(1)) {
+ printf("Open gave:\n");
+ printf("oplock_flags = 0x%x\n", io.out.oplock_level);
+ printf("create_action = 0x%x\n", io.out.create_action);
+ printf("create_time = %s\n", nt_time_string(tmp_ctx, io.out.create_time));
+ printf("access_time = %s\n", nt_time_string(tmp_ctx, io.out.access_time));
+ printf("write_time = %s\n", nt_time_string(tmp_ctx, io.out.write_time));
+ printf("change_time = %s\n", nt_time_string(tmp_ctx, io.out.change_time));
+ printf("alloc_size = %lld\n", (long long)io.out.alloc_size);
+ printf("size = %lld\n", (long long)io.out.size);
+ printf("file_attr = 0x%x\n", io.out.file_attr);
+ printf("handle = %016llx%016llx\n",
+ (long long)io.out.file.handle.data[0],
+ (long long)io.out.file.handle.data[1]);
+ }
+
+ talloc_free(tmp_ctx);
+
+ return io.out.file.handle;
+}
+
+
+/*
+ basic testing of SMB2 connection calls
+*/
+bool torture_smb2_connect(struct torture_context *torture)
+{
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+ struct smb2_tree *tree;
+ struct smb2_request *req;
+ struct smb2_handle h1, h2;
+ NTSTATUS status;
+
+ if (!torture_smb2_connection(torture, &tree)) {
+ return false;
+ }
+
+ h1 = torture_smb2_createfile(tree, "test9.dat");
+ h2 = torture_smb2_createfile(tree, "test9.dat");
+ status = torture_smb2_write(torture, tree, h1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Write failed - %s\n", nt_errstr(status));
+ return false;
+ }
+ status = torture_smb2_close(tree, h1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Close failed - %s\n", nt_errstr(status));
+ return false;
+ }
+ status = torture_smb2_close(tree, h2);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Close failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2_util_close(tree, h1);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_FILE_CLOSED)) {
+ printf("close should have closed the handle - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2_tdis(tree);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("tdis failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2_tdis(tree);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_NAME_DELETED)) {
+ printf("tdis should have disabled session - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2_logoff(tree->session);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Logoff failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ req = smb2_logoff_send(tree->session);
+ if (!req) {
+ printf("smb2_logoff_send() failed\n");
+ return false;
+ }
+
+ req->session = NULL;
+
+ status = smb2_logoff_recv(req);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_USER_SESSION_DELETED)) {
+ printf("Logoff should have disabled session - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2_keepalive(tree->session->transport);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("keepalive failed? - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ 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 0000000000..6d898a128c
--- /dev/null
+++ b/source4/torture/smb2/create.c
@@ -0,0 +1,468 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "torture/torture.h"
+#include "torture/smb2/proto.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "libcli/security/security.h"
+
+#define FNAME "test_create.dat"
+
+#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)); \
+ return false; \
+ }} while (0)
+
+#define CHECK_EQUAL(v, correct) do { \
+ if (v != correct) { \
+ printf("(%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)
+
+/*
+ test some interesting combinations found by gentest
+ */
+static bool test_create_gentest(struct torture_context *torture, struct smb2_tree *tree)
+{
+ struct smb2_create io;
+ NTSTATUS status;
+ TALLOC_CTX *tmp_ctx = talloc_new(tree);
+ 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, tmp_ctx, &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, tmp_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
+
+ io.in.create_options = 0;
+
+ io.in.file_attributes = FILE_ATTRIBUTE_DEVICE;
+ status = smb2_create(tree, tmp_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
+
+ io.in.file_attributes = FILE_ATTRIBUTE_VOLUME;
+ status = smb2_create(tree, tmp_ctx, &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, tmp_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
+
+ io.in.create_disposition = NTCREATEX_DISP_CREATE;
+ io.in.desired_access = 0x08000000;
+ status = smb2_create(tree, tmp_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
+
+ io.in.desired_access = 0x04000000;
+ status = smb2_create(tree, tmp_ctx, &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 = 1<<i;
+ if (io.in.create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) {
+ continue;
+ }
+ status = smb2_create(tree, tmp_ctx, &io);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
+ not_supported_mask |= 1<<i;
+ } else if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
+ invalid_parameter_mask |= 1<<i;
+ } else if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_A_DIRECTORY)) {
+ not_a_directory_mask |= 1<<i;
+ } else if (NT_STATUS_EQUAL(status, NT_STATUS_OK)) {
+ ok_mask |= 1<<i;
+ status = smb2_util_close(tree, io.out.file.handle);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ } else {
+ unexpected_mask |= 1<<i;
+ printf("create option 0x%08x returned %s\n", 1<<i, nt_errstr(status));
+ }
+ }
+ }
+ io.in.create_options = 0;
+
+ CHECK_EQUAL(ok_mask, 0x00efcf7e);
+ CHECK_EQUAL(not_a_directory_mask, 0x00000001);
+ CHECK_EQUAL(not_supported_mask, 0x00102080);
+ CHECK_EQUAL(invalid_parameter_mask, 0xff000000);
+ CHECK_EQUAL(unexpected_mask, 0x00000000);
+
+ io.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
+ io.in.file_attributes = 0;
+ access_mask = 0;
+ {
+ int i;
+ for (i=0;i<32;i++) {
+ io.in.desired_access = 1<<i;
+ status = smb2_create(tree, tmp_ctx, &io);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_PRIVILEGE_NOT_HELD)) {
+ access_mask |= io.in.desired_access;
+ } else {
+ CHECK_STATUS(status, NT_STATUS_OK);
+ status = smb2_util_close(tree, io.out.file.handle);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ }
+ }
+ }
+
+ CHECK_EQUAL(access_mask, 0x0df0fe00);
+
+ io.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
+ io.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED;
+ io.in.file_attributes = 0;
+ ok_mask = 0;
+ invalid_parameter_mask = 0;
+ unexpected_mask = 0;
+ file_attributes_set = 0;
+ {
+ int i;
+ for (i=0;i<32;i++) {
+ io.in.file_attributes = 1<<i;
+ if (io.in.file_attributes & FILE_ATTRIBUTE_ENCRYPTED) {
+ continue;
+ }
+ smb2_deltree(tree, FNAME);
+ status = smb2_create(tree, tmp_ctx, &io);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
+ invalid_parameter_mask |= 1<<i;
+ } else if (NT_STATUS_IS_OK(status)) {
+ uint32_t expected;
+ ok_mask |= 1<<i;
+
+ expected = (io.in.file_attributes | FILE_ATTRIBUTE_ARCHIVE) & 0x00005127;
+ CHECK_EQUAL(io.out.file_attr, expected);
+ file_attributes_set |= io.out.file_attr;
+
+ status = smb2_util_close(tree, io.out.file.handle);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ } else {
+ unexpected_mask |= 1<<i;
+ printf("file attribute 0x%08x returned %s\n", 1<<i, nt_errstr(status));
+ }
+ }
+ }
+
+ CHECK_EQUAL(ok_mask, 0x00003fb7);
+ CHECK_EQUAL(invalid_parameter_mask, 0xffff8048);
+ CHECK_EQUAL(unexpected_mask, 0x00000000);
+ CHECK_EQUAL(file_attributes_set, 0x00001127);
+
+ smb2_deltree(tree, FNAME);
+
+ /*
+ * Standalone servers doesn't support encryption
+ */
+ io.in.file_attributes = FILE_ATTRIBUTE_ENCRYPTED;
+ status = smb2_create(tree, tmp_ctx, &io);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ printf("FILE_ATTRIBUTE_ENCRYPTED returned %s\n", nt_errstr(status));
+ } else {
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_EQUAL(io.out.file_attr, (FILE_ATTRIBUTE_ENCRYPTED | FILE_ATTRIBUTE_ARCHIVE));
+ status = smb2_util_close(tree, io.out.file.handle);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ }
+
+ smb2_deltree(tree, FNAME);
+
+ ZERO_STRUCT(io);
+ io.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED;
+ io.in.file_attributes = 0;
+ io.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF;
+ io.in.share_access =
+ NTCREATEX_SHARE_ACCESS_READ|
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ io.in.create_options = 0;
+ io.in.fname = FNAME ":stream1";
+ status = smb2_create(tree, tmp_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = smb2_util_close(tree, io.out.file.handle);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ io.in.fname = FNAME;
+ io.in.file_attributes = 0x8040;
+ io.in.share_access =
+ NTCREATEX_SHARE_ACCESS_READ;
+ status = smb2_create(tree, tmp_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
+
+ io.in.fname = FNAME;
+ io.in.file_attributes = 0;
+ io.in.desired_access = SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA;
+ io.in.query_maximal_access = true;
+ status = smb2_create(tree, tmp_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_EQUAL(io.out.maximal_access, 0x001f01ff);
+
+ q.access_information.level = RAW_FILEINFO_ACCESS_INFORMATION;
+ q.access_information.in.file.handle = io.out.file.handle;
+ status = smb2_getinfo_file(tree, tmp_ctx, &q);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_EQUAL(q.access_information.out.access_flags, io.in.desired_access);
+
+ io.in.file_attributes = 0;
+ io.in.desired_access = 0;
+ io.in.query_maximal_access = false;
+ io.in.share_access = 0;
+ status = smb2_create(tree, tmp_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
+
+ talloc_free(tmp_ctx);
+
+ smb2_deltree(tree, FNAME);
+
+ return true;
+}
+
+
+/*
+ try the various request blobs
+ */
+static bool test_create_blob(struct torture_context *torture, struct smb2_tree *tree)
+{
+ struct smb2_create io;
+ NTSTATUS status;
+ TALLOC_CTX *tmp_ctx = talloc_new(tree);
+
+ smb2_deltree(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 = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
+ NTCREATEX_OPTIONS_ASYNC_ALERT |
+ NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
+ 0x00200000;
+ io.in.fname = FNAME;
+
+ status = smb2_create(tree, tmp_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = smb2_util_close(tree, io.out.file.handle);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("testing alloc size\n");
+ io.in.alloc_size = 4096;
+ status = smb2_create(tree, tmp_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_EQUAL(io.out.alloc_size, io.in.alloc_size);
+
+ status = smb2_util_close(tree, io.out.file.handle);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("testing durable open\n");
+ io.in.durable_open = true;
+ status = smb2_create(tree, tmp_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = smb2_util_close(tree, io.out.file.handle);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("testing query maximal access\n");
+ io.in.query_maximal_access = true;
+ status = smb2_create(tree, tmp_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_EQUAL(io.out.maximal_access, 0x001f01ff);
+
+ status = smb2_util_close(tree, io.out.file.handle);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("testing timewarp\n");
+ io.in.timewarp = 10000;
+ status = smb2_create(tree, tmp_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ io.in.timewarp = 0;
+
+ printf("testing query_on_disk\n");
+ io.in.query_on_disk_id = true;
+ status = smb2_create(tree, tmp_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = smb2_util_close(tree, io.out.file.handle);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("testing unknown tag\n");
+ status = smb2_create_blob_add(tmp_ctx, &io.in.blobs,
+ "FooO", data_blob(NULL, 0));
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = smb2_create(tree, tmp_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = smb2_util_close(tree, io.out.file.handle);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("testing bad tag length\n");
+ status = smb2_create_blob_add(tmp_ctx, &io.in.blobs,
+ "xxx", data_blob(NULL, 0));
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = smb2_create(tree, tmp_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
+
+ talloc_free(tmp_ctx);
+
+ smb2_deltree(tree, FNAME);
+
+ return true;
+}
+
+/*
+ try creating with acls
+ */
+static bool test_create_acl(struct torture_context *torture, struct smb2_tree *tree)
+{
+ struct smb2_create io;
+ NTSTATUS status;
+ TALLOC_CTX *tmp_ctx = talloc_new(tree);
+ struct security_ace ace;
+ struct security_descriptor *sd, *sd2;
+ struct dom_sid *test_sid;
+ union smb_fileinfo q;
+
+ smb2_deltree(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 = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
+ NTCREATEX_OPTIONS_ASYNC_ALERT |
+ NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
+ 0x00200000;
+ io.in.fname = FNAME;
+
+ status = smb2_create(tree, tmp_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ q.query_secdesc.level = RAW_FILEINFO_SEC_DESC;
+ q.query_secdesc.in.file.handle = io.out.file.handle;
+ q.query_secdesc.in.secinfo_flags =
+ SECINFO_OWNER |
+ SECINFO_GROUP |
+ SECINFO_DACL;
+ status = smb2_getinfo_file(tree, tmp_ctx, &q);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ sd = q.query_secdesc.out.sd;
+
+ status = smb2_util_close(tree, io.out.file.handle);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ smb2_util_unlink(tree, FNAME);
+
+ printf("adding a new ACE\n");
+ test_sid = dom_sid_parse_talloc(tmp_ctx, "S-1-5-32-1234-54321");
+
+ 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);
+
+ printf("creating a file with an initial ACL\n");
+
+ io.in.sec_desc = sd;
+ status = smb2_create(tree, tmp_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ q.query_secdesc.in.file.handle = io.out.file.handle;
+ status = smb2_getinfo_file(tree, tmp_ctx, &q);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ sd2 = q.query_secdesc.out.sd;
+
+ if (!security_acl_equal(sd->dacl, sd2->dacl)) {
+ printf("%s: security descriptors don't match!\n", __location__);
+ printf("got:\n");
+ NDR_PRINT_DEBUG(security_descriptor, sd2);
+ printf("expected:\n");
+ NDR_PRINT_DEBUG(security_descriptor, sd);
+ return false;
+ }
+
+ talloc_free(tmp_ctx);
+
+ return true;
+}
+
+/*
+ basic testing of SMB2 read
+*/
+struct torture_suite *torture_smb2_create_init(void)
+{
+ struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "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, "ACL", test_create_acl);
+
+ suite->description = talloc_strdup(suite, "SMB2-CREATE tests");
+
+ return suite;
+}
diff --git a/source4/torture/smb2/dir.c b/source4/torture/smb2/dir.c
new file mode 100644
index 0000000000..58cf2229bc
--- /dev/null
+++ b/source4/torture/smb2/dir.c
@@ -0,0 +1,93 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 dir list 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+#include "torture/torture.h"
+#include "torture/smb2/proto.h"
+
+/*
+ test find continue
+*/
+static bool torture_smb2_find_dir(struct smb2_tree *tree)
+{
+ struct smb2_handle handle;
+ NTSTATUS status;
+ int i;
+ struct smb2_find f;
+ bool ret = true;
+ union smb_search_data *d;
+ uint_t count;
+
+ status = smb2_util_roothandle(tree, &handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ ZERO_STRUCT(f);
+ f.in.file.handle = handle;
+ 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_IS_OK(status)) {
+ printf("SMB2_FIND_ID_BOTH_DIRECTORY_INFO failed - %s\n", nt_errstr(status));
+ break;
+ }
+
+ printf("Got %d files\n", count);
+ for (i=0;i<count;i++) {
+ printf("\t'%s'\n",
+ d[i].both_directory_info.name.s);
+ }
+ f.in.continue_flags = 0;
+ f.in.max_response_size = 4096;
+ } while (count != 0);
+
+
+ return ret;
+}
+
+
+/*
+ basic testing of directory listing with continue
+*/
+bool torture_smb2_dir(struct torture_context *torture)
+{
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+ struct smb2_tree *tree;
+ bool ret = true;
+
+ if (!torture_smb2_connection(torture, &tree)) {
+ return false;
+ }
+
+ ret &= torture_smb2_find_dir(tree);
+
+ talloc_free(mem_ctx);
+
+ return ret;
+}
diff --git a/source4/torture/smb2/find.c b/source4/torture/smb2/find.c
new file mode 100644
index 0000000000..06222e8e9e
--- /dev/null
+++ b/source4/torture/smb2/find.c
@@ -0,0 +1,220 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 find 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+#include "torture/torture.h"
+#include "torture/smb2/proto.h"
+
+static struct {
+ const char *name;
+ uint16_t level;
+ NTSTATUS status;
+ union smb_search_data data;
+} levels[] = {
+#define LEVEL(x) #x, x
+ { LEVEL(SMB2_FIND_ID_BOTH_DIRECTORY_INFO) },
+ { LEVEL(SMB2_FIND_DIRECTORY_INFO) },
+ { LEVEL(SMB2_FIND_FULL_DIRECTORY_INFO) },
+ { LEVEL(SMB2_FIND_NAME_INFO) },
+ { LEVEL(SMB2_FIND_BOTH_DIRECTORY_INFO) },
+ { LEVEL(SMB2_FIND_ID_FULL_DIRECTORY_INFO) },
+};
+
+#define FNAME "smb2-find.dat"
+
+#define CHECK_VALUE(call_name, stype, field) do { \
+ union smb_search_data *d = find_level("SMB2_FIND_" #call_name); \
+ if (io.all_info2.out.field != d->stype.field) { \
+ printf("(%s) %s/%s should be 0x%llx - 0x%llx\n", __location__, \
+ #call_name, #field, \
+ (long long)io.all_info2.out.field, (long long)d->stype.field); \
+ ret = false; \
+ }} while (0)
+
+#define CHECK_CONST_STRING(call_name, stype, field, str) do { \
+ union smb_search_data *d = find_level("SMB2_FIND_" #call_name); \
+ if (strcmp(str, d->stype.field.s) != 0) { \
+ printf("(%s) %s/%s should be '%s' - '%s'\n", __location__, \
+ #call_name, #field, \
+ str, d->stype.field.s); \
+ ret = false; \
+ }} while (0)
+
+static union smb_search_data *find_level(const char *name)
+{
+ int i;
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ if (strcmp(name, levels[i].name) == 0) {
+ return &levels[i].data;
+ }
+ }
+ return NULL;
+}
+
+/*
+ test find levels
+*/
+static bool torture_smb2_find_levels(struct smb2_tree *tree)
+{
+ struct smb2_handle handle;
+ NTSTATUS status;
+ int i;
+ struct smb2_find f;
+ bool ret = true;
+ union smb_fileinfo io;
+ const char *alt_name;
+
+ status = smb2_create_complex_file(tree, FNAME, &handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ io.generic.level = RAW_FILEINFO_ALT_NAME_INFORMATION;
+ io.generic.in.file.handle = handle;
+ status = smb2_getinfo_file(tree, tree, &io);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+ alt_name = talloc_strdup(tree, io.alt_name_info.out.fname.s);
+
+ io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
+ io.generic.in.file.handle = handle;
+ status = smb2_getinfo_file(tree, tree, &io);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ status = smb2_util_roothandle(tree, &handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ ZERO_STRUCT(f);
+ f.in.file.handle = handle;
+ f.in.pattern = FNAME;
+ f.in.continue_flags = SMB2_CONTINUE_FLAG_RESTART;
+ f.in.max_response_size = 0x10000;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ union smb_search_data *d;
+ uint_t count;
+
+ f.in.level = levels[i].level - 0x100;
+
+ levels[i].status = smb2_find_level(tree, tree, &f, &count, &d);
+ if (!NT_STATUS_IS_OK(levels[i].status)) {
+ printf("%s failed - %s\n", levels[i].name,
+ nt_errstr(levels[i].status));
+ }
+
+ if (count != 1) {
+ printf("Expected count 1 - got %d in %s\n", count, levels[i].name);
+ ret = false;
+ }
+
+ levels[i].data = d[0];
+ }
+
+ CHECK_VALUE(DIRECTORY_INFO, directory_info, create_time);
+ CHECK_VALUE(DIRECTORY_INFO, directory_info, access_time);
+ CHECK_VALUE(DIRECTORY_INFO, directory_info, write_time);
+ CHECK_VALUE(DIRECTORY_INFO, directory_info, change_time);
+ CHECK_VALUE(DIRECTORY_INFO, directory_info, size);
+ CHECK_VALUE(DIRECTORY_INFO, directory_info, alloc_size);
+ CHECK_VALUE(DIRECTORY_INFO, directory_info, attrib);
+ CHECK_CONST_STRING(DIRECTORY_INFO, directory_info, name, FNAME);
+
+ CHECK_VALUE(FULL_DIRECTORY_INFO, full_directory_info, create_time);
+ CHECK_VALUE(FULL_DIRECTORY_INFO, full_directory_info, access_time);
+ CHECK_VALUE(FULL_DIRECTORY_INFO, full_directory_info, write_time);
+ CHECK_VALUE(FULL_DIRECTORY_INFO, full_directory_info, change_time);
+ CHECK_VALUE(FULL_DIRECTORY_INFO, full_directory_info, size);
+ CHECK_VALUE(FULL_DIRECTORY_INFO, full_directory_info, alloc_size);
+ CHECK_VALUE(FULL_DIRECTORY_INFO, full_directory_info, attrib);
+ CHECK_VALUE(FULL_DIRECTORY_INFO, full_directory_info, ea_size);
+ CHECK_CONST_STRING(FULL_DIRECTORY_INFO, full_directory_info, name, FNAME);
+
+ CHECK_VALUE(BOTH_DIRECTORY_INFO, both_directory_info, create_time);
+ CHECK_VALUE(BOTH_DIRECTORY_INFO, both_directory_info, access_time);
+ CHECK_VALUE(BOTH_DIRECTORY_INFO, both_directory_info, write_time);
+ CHECK_VALUE(BOTH_DIRECTORY_INFO, both_directory_info, change_time);
+ CHECK_VALUE(BOTH_DIRECTORY_INFO, both_directory_info, size);
+ CHECK_VALUE(BOTH_DIRECTORY_INFO, both_directory_info, alloc_size);
+ CHECK_VALUE(BOTH_DIRECTORY_INFO, both_directory_info, attrib);
+ CHECK_VALUE(BOTH_DIRECTORY_INFO, both_directory_info, ea_size);
+ CHECK_CONST_STRING(BOTH_DIRECTORY_INFO, both_directory_info, short_name, alt_name);
+ CHECK_CONST_STRING(BOTH_DIRECTORY_INFO, both_directory_info, name, FNAME);
+
+ CHECK_VALUE(ID_FULL_DIRECTORY_INFO, id_full_directory_info, create_time);
+ CHECK_VALUE(ID_FULL_DIRECTORY_INFO, id_full_directory_info, access_time);
+ CHECK_VALUE(ID_FULL_DIRECTORY_INFO, id_full_directory_info, write_time);
+ CHECK_VALUE(ID_FULL_DIRECTORY_INFO, id_full_directory_info, change_time);
+ CHECK_VALUE(ID_FULL_DIRECTORY_INFO, id_full_directory_info, size);
+ CHECK_VALUE(ID_FULL_DIRECTORY_INFO, id_full_directory_info, alloc_size);
+ CHECK_VALUE(ID_FULL_DIRECTORY_INFO, id_full_directory_info, attrib);
+ CHECK_VALUE(ID_FULL_DIRECTORY_INFO, id_full_directory_info, ea_size);
+ CHECK_VALUE(ID_FULL_DIRECTORY_INFO, id_full_directory_info, file_id);
+ CHECK_CONST_STRING(ID_FULL_DIRECTORY_INFO, id_full_directory_info, name, FNAME);
+
+ CHECK_VALUE(ID_BOTH_DIRECTORY_INFO, id_both_directory_info, create_time);
+ CHECK_VALUE(ID_BOTH_DIRECTORY_INFO, id_both_directory_info, access_time);
+ CHECK_VALUE(ID_BOTH_DIRECTORY_INFO, id_both_directory_info, write_time);
+ CHECK_VALUE(ID_BOTH_DIRECTORY_INFO, id_both_directory_info, change_time);
+ CHECK_VALUE(ID_BOTH_DIRECTORY_INFO, id_both_directory_info, size);
+ CHECK_VALUE(ID_BOTH_DIRECTORY_INFO, id_both_directory_info, alloc_size);
+ CHECK_VALUE(ID_BOTH_DIRECTORY_INFO, id_both_directory_info, attrib);
+ CHECK_VALUE(ID_BOTH_DIRECTORY_INFO, id_both_directory_info, ea_size);
+ CHECK_VALUE(ID_BOTH_DIRECTORY_INFO, id_both_directory_info, file_id);
+ CHECK_CONST_STRING(ID_BOTH_DIRECTORY_INFO, id_both_directory_info, name, FNAME);
+
+
+ return ret;
+}
+
+
+/* basic testing of all SMB2 find levels
+*/
+bool torture_smb2_find(struct torture_context *torture)
+{
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+ struct smb2_tree *tree;
+ bool ret = true;
+ NTSTATUS status;
+
+ if (!torture_smb2_connection(torture, &tree)) {
+ return false;
+ }
+
+ status = torture_setup_complex_file(tree, FNAME);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+ torture_setup_complex_file(tree, FNAME ":streamtwo");
+
+ ret &= torture_smb2_find_levels(tree);
+
+ talloc_free(mem_ctx);
+
+ return ret;
+}
diff --git a/source4/torture/smb2/getinfo.c b/source4/torture/smb2/getinfo.c
new file mode 100644
index 0000000000..c4ab31f4cf
--- /dev/null
+++ b/source4/torture/smb2/getinfo.c
@@ -0,0 +1,236 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+#include "torture/torture.h"
+#include "torture/smb2/proto.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) #x, 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) }
+};
+
+#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);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf(__location__ " Unable to create test file '%s' - %s\n", FNAME, nt_errstr(status));
+ goto failed;
+ }
+
+ status = torture_smb2_testdir(tree, DNAME, &hdir);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf(__location__ " Unable to create test directory '%s' - %s\n", DNAME, nt_errstr(status));
+ goto failed;
+ }
+
+ printf("Testing file info levels\n");
+ torture_smb2_all_info(tree, hfile);
+ torture_smb2_all_info(tree, hdir);
+
+ for (i=0;i<ARRAY_SIZE(file_levels);i++) {
+ if (file_levels[i].level == RAW_FILEINFO_SEC_DESC) {
+ file_levels[i].finfo.query_secdesc.in.secinfo_flags = 0x7;
+ file_levels[i].dinfo.query_secdesc.in.secinfo_flags = 0x7;
+ }
+ if (file_levels[i].level == RAW_FILEINFO_SMB2_ALL_EAS) {
+ file_levels[i].finfo.all_eas.in.continue_flags =
+ SMB2_CONTINUE_FLAG_RESTART;
+ file_levels[i].dinfo.all_eas.in.continue_flags =
+ SMB2_CONTINUE_FLAG_RESTART;
+ }
+ file_levels[i].finfo.generic.level = file_levels[i].level;
+ file_levels[i].finfo.generic.in.file.handle = hfile;
+ file_levels[i].fstatus = smb2_getinfo_file(tree, tree, &file_levels[i].finfo);
+ if (!NT_STATUS_IS_OK(file_levels[i].fstatus)) {
+ printf("(%s) %s failed on file - %s\n", __location__,
+ file_levels[i].name, nt_errstr(file_levels[i].fstatus));
+ goto failed;
+ }
+ file_levels[i].dinfo.generic.level = file_levels[i].level;
+ file_levels[i].dinfo.generic.in.file.handle = hdir;
+ file_levels[i].dstatus = smb2_getinfo_file(tree, tree, &file_levels[i].dinfo);
+ if (!NT_STATUS_IS_OK(file_levels[i].dstatus)) {
+ printf("(%s) %s failed on dir - %s\n", __location__,
+ file_levels[i].name, nt_errstr(file_levels[i].dstatus));
+ goto failed;
+ }
+ }
+
+ return true;
+
+failed:
+ return false;
+}
+
+
+/*
+ test fsinfo levels
+*/
+static bool torture_smb2_fsinfo(struct smb2_tree *tree)
+{
+ int i;
+ NTSTATUS status;
+ struct smb2_handle handle;
+
+ printf("Testing fsinfo levels\n");
+ status = smb2_util_roothandle(tree, &handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf(__location__ " Unable to create root handle - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ for (i=0;i<ARRAY_SIZE(fs_levels);i++) {
+ fs_levels[i].info.generic.level = fs_levels[i].level;
+ fs_levels[i].info.generic.handle = handle;
+ fs_levels[i].status = smb2_getinfo_fs(tree, tree, &fs_levels[i].info);
+ if (!NT_STATUS_IS_OK(fs_levels[i].status)) {
+ printf("%s failed - %s\n", fs_levels[i].name, nt_errstr(fs_levels[i].status));
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+/*
+ test for buffer size handling
+*/
+static bool torture_smb2_buffercheck(struct smb2_tree *tree)
+{
+ NTSTATUS status;
+ struct smb2_handle handle;
+ struct smb2_getinfo b;
+
+ printf("Testing buffer size handling\n");
+ status = smb2_util_roothandle(tree, &handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf(__location__ " Unable to create root handle - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ ZERO_STRUCT(b);
+ b.in.info_type = SMB2_GETINFO_FS;
+ b.in.info_class = 1;
+ b.in.output_buffer_length = 0x1;
+ b.in.input_buffer_length = 0;
+ b.in.file.handle = handle;
+
+ status = smb2_getinfo(tree, tree, &b);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_INFO_LENGTH_MISMATCH)) {
+ printf(__location__ " Wrong error code for small buffer %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+
+/* basic testing of all SMB2 getinfo levels
+*/
+bool torture_smb2_getinfo(struct torture_context *torture)
+{
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+ struct smb2_tree *tree;
+ bool ret = true;
+ NTSTATUS status;
+
+ if (!torture_smb2_connection(torture, &tree)) {
+ return false;
+ }
+
+ smb2_deltree(tree, FNAME);
+ smb2_deltree(tree, DNAME);
+
+ status = torture_setup_complex_file(tree, FNAME);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+ torture_setup_complex_file(tree, FNAME ":streamtwo");
+ status = torture_setup_complex_dir(tree, DNAME);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+ torture_setup_complex_file(tree, DNAME ":streamtwo");
+
+ ret &= torture_smb2_fileinfo(torture, tree);
+ ret &= torture_smb2_fsinfo(tree);
+ ret &= torture_smb2_buffercheck(tree);
+
+ talloc_free(mem_ctx);
+
+ return ret;
+}
diff --git a/source4/torture/smb2/lock.c b/source4/torture/smb2/lock.c
new file mode 100644
index 0000000000..d820983022
--- /dev/null
+++ b/source4/torture/smb2/lock.c
@@ -0,0 +1,545 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+#include "torture/torture.h"
+#include "torture/smb2/proto.h"
+
+#include "librpc/gen_ndr/ndr_security.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 CHECK_VALUE(v, correct) do { \
+ if ((v) != (correct)) { \
+ printf("(%s) Incorrect value %s=%d - should be %d\n", \
+ __location__, #v, v, correct); \
+ ret = false; \
+ goto done; \
+ }} while (0)
+
+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;
+
+ lck.in.lock_count = 0x0000;
+ lck.in.reserved = 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 = 0x0001;
+ lck.in.reserved = 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);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(lck.out.reserved, 0);
+
+ 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.reserved = 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);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(lck.out.reserved, 0);
+
+ lck.in.reserved = 0x123ab2;
+ status = smb2_lock(tree, &lck);
+ CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED);
+
+ lck.in.reserved = 0x123ab3;
+ status = smb2_lock(tree, &lck);
+ if (torture_setting_bool(torture, "windows", false)) {
+ CHECK_STATUS(status, NT_STATUS_OK);
+ } else {
+ CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED);
+ }
+ CHECK_VALUE(lck.out.reserved, 0);
+
+ lck.in.reserved = 0x123ab4;
+ status = smb2_lock(tree, &lck);
+ CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED);
+
+ lck.in.reserved = 0x123ab5;
+ status = smb2_lock(tree, &lck);
+ if (torture_setting_bool(torture, "windows", false)) {
+ CHECK_STATUS(status, NT_STATUS_OK);
+ } else {
+ CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED);
+ }
+ CHECK_VALUE(lck.out.reserved, 0);
+
+ lck.in.lock_count = 0x0001;
+ lck.in.reserved = 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);
+
+ status = smb2_lock(tree, &lck);
+ CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED);
+
+ status = smb2_lock(tree, &lck);
+ if (torture_setting_bool(torture, "windows", false)) {
+ CHECK_STATUS(status, NT_STATUS_OK);
+ } else {
+ CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED);
+ }
+ CHECK_VALUE(lck.out.reserved, 0);
+
+ status = smb2_lock(tree, &lck);
+ CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED);
+
+ status = smb2_lock(tree, &lck);
+ if (torture_setting_bool(torture, "windows", false)) {
+ CHECK_STATUS(status, NT_STATUS_OK);
+ } else {
+ CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED);
+ }
+ CHECK_VALUE(lck.out.reserved, 0);
+
+ el[0].flags = 0x00000000;
+ 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);
+
+ el[0].flags = 0x00000001;
+ 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.reserved = 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.reserved = 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_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);
+
+ lck.in.lock_count = 0x0001;
+ lck.in.reserved = 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);
+ 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);
+
+ 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);
+ CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
+
+ lck.in.lock_count = 2;
+ lck.in.reserved = 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 = 0;
+ el[1].flags = 0;
+ 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 = 0;
+ 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);
+
+ 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 = 0;
+ 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.reserved = 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.reserved = 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.reserved = 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,
+ };
+
+ 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_exclusiv(struct torture_context *torture, struct smb2_tree *tree)
+{
+ struct test_lock_read_write_state s = {
+ .fname = "lock_rw_exclusiv.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[2];
+
+ 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);
+ 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 (torture_setting_bool(torture, "windows", false)) {
+ CHECK_STATUS(status, NT_STATUS_OK);
+ } else {
+ CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED);
+ }
+
+
+
+done:
+ return ret;
+}
+
+
+/* basic testing of SMB2 locking
+*/
+struct torture_suite *torture_smb2_lock_init(void)
+{
+ struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "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-EXCLUSIV", test_lock_rw_exclusiv);
+ torture_suite_add_1smb2_test(suite, "AUTO-UNLOCK", test_lock_auto_unlock);
+
+ suite->description = talloc_strdup(suite, "SMB2-LOCK tests");
+
+ return suite;
+}
+
diff --git a/source4/torture/smb2/maxwrite.c b/source4/torture/smb2/maxwrite.c
new file mode 100644
index 0000000000..892bbd73ac
--- /dev/null
+++ b/source4/torture/smb2/maxwrite.c
@@ -0,0 +1,132 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#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<len;i++) {
+ w.in.data.data[i] = i % 256;
+ }
+
+ printf("trying to write %d bytes (min=%d max=%d)\n",
+ len, min, max);
+
+ status = smb2_write(tree, &w);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("write failed - %s\n", nt_errstr(status));
+ max = len-1;
+ status = smb2_util_close(tree, handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ /* vista bug */
+ printf("coping with server disconnect\n");
+ talloc_free(tree);
+ if (!torture_smb2_connection(torture, &tree)) {
+ printf("failed to reconnect\n");
+ return NT_STATUS_NET_WRITE_FAULT;
+ }
+ }
+ handle = torture_smb2_create(tree, FNAME);
+ continue;
+ } else {
+ min = len;
+ }
+
+
+ ZERO_STRUCT(r);
+ r.in.file.handle = handle;
+ r.in.length = len;
+ r.in.offset = 0;
+
+ printf("reading %d bytes\n", len);
+
+ status = smb2_read(tree, tmp_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("read failed - %s\n", nt_errstr(status));
+ } else if (w.in.data.length != r.out.data.length ||
+ memcmp(w.in.data.data, r.out.data.data, len) != 0) {
+ printf("read data mismatch\n");
+ }
+
+ talloc_free(tmp_ctx);
+ }
+
+ printf("converged: len=%d\n", max);
+ smb2_util_close(tree, handle);
+ smb2_util_unlink(tree, FNAME);
+
+ return NT_STATUS_OK;
+}
+
+
+
+/*
+ basic testing of SMB2 connection calls
+*/
+bool torture_smb2_maxwrite(struct torture_context *torture)
+{
+ struct smb2_tree *tree;
+ struct smb2_handle h1;
+ NTSTATUS status;
+
+ if (!torture_smb2_connection(torture, &tree)) {
+ return false;
+ }
+
+ h1 = torture_smb2_create(tree, FNAME);
+ status = torture_smb2_write(torture, tree, h1);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Write failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
diff --git a/source4/torture/smb2/notify.c b/source4/torture/smb2/notify.c
new file mode 100644
index 0000000000..574136ab3f
--- /dev/null
+++ b/source4/torture/smb2/notify.c
@@ -0,0 +1,203 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 notify 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 <http://www.gnu.org/licenses/>.
+*/
+
+#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/raw/libcliraw.h"
+#include "lib/events/events.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 CHECK_VALUE(v, correct) do { \
+ if ((v) != (correct)) { \
+ printf("(%s) Incorrect value %s=%d - should be %d\n", \
+ __location__, #v, v, correct); \
+ ret = false; \
+ goto done; \
+ }} while (0)
+
+#define CHECK_WIRE_STR(field, value) do { \
+ if (!field.s || strcmp(field.s, value)) { \
+ printf("(%s) %s [%s] != %s\n", \
+ __location__, #field, field.s, value); \
+ ret = false; \
+ goto done; \
+ }} while (0)
+
+#define FNAME "smb2-notify01.dat"
+
+static bool test_valid_request(TALLOC_CTX *mem_ctx, struct smb2_tree *tree)
+{
+ bool ret = true;
+ NTSTATUS status;
+ struct smb2_handle dh;
+ struct smb2_notify n;
+ struct smb2_request *req;
+
+ status = smb2_util_roothandle(tree, &dh);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ n.in.recursive = 0x0000;
+ n.in.buffer_size = 0x00080000;
+ n.in.file.handle = dh;
+ n.in.completion_filter = 0x00000FFF;
+ n.in.unknown = 0x00000000;
+ req = smb2_notify_send(tree, &n);
+
+ while (!req->cancel.can_cancel && req->state <= SMB2_REQUEST_RECV) {
+ if (event_loop_once(req->transport->socket->event.ctx) != 0) {
+ break;
+ }
+ }
+
+ status = torture_setup_complex_file(tree, FNAME);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = smb2_notify_recv(req, mem_ctx, &n);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(n.out.num_changes, 1);
+ CHECK_VALUE(n.out.changes[0].action, NOTIFY_ACTION_REMOVED);
+ 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 (event_loop_once(req->transport->socket->event.ctx) != 0) {
+ break;
+ }
+ }
+
+ status = torture_setup_complex_file(tree, FNAME);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = smb2_notify_recv(req, mem_ctx, &n);
+ CHECK_STATUS(status, STATUS_NOTIFY_ENUM_DIR);
+
+ /*
+ * if the change response fits in the buffer we get
+ * NT_STATUS_OK again
+ */
+ n.in.buffer_size = 0x00080000;
+ req = smb2_notify_send(tree, &n);
+
+ while (!req->cancel.can_cancel && req->state <= SMB2_REQUEST_RECV) {
+ if (event_loop_once(req->transport->socket->event.ctx) != 0) {
+ break;
+ }
+ }
+
+ status = torture_setup_complex_file(tree, FNAME);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = smb2_notify_recv(req, mem_ctx, &n);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VALUE(n.out.num_changes, 3);
+ CHECK_VALUE(n.out.changes[0].action, NOTIFY_ACTION_REMOVED);
+ CHECK_WIRE_STR(n.out.changes[0].name, FNAME);
+ CHECK_VALUE(n.out.changes[1].action, NOTIFY_ACTION_ADDED);
+ CHECK_WIRE_STR(n.out.changes[1].name, FNAME);
+ CHECK_VALUE(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 = 0x00000FFF;
+ n.in.unknown = 0x00000000;
+ req = smb2_notify_send(tree, &n);
+
+ while (!req->cancel.can_cancel && req->state <= SMB2_REQUEST_RECV) {
+ if (event_loop_once(req->transport->socket->event.ctx) != 0) {
+ break;
+ }
+ }
+
+ status = torture_setup_complex_file(tree, FNAME);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = smb2_notify_recv(req, mem_ctx, &n);
+ CHECK_STATUS(status, STATUS_NOTIFY_ENUM_DIR);
+
+ n.in.buffer_size = 0x00080000;
+ req = smb2_notify_send(tree, &n);
+ while (!req->cancel.can_cancel && req->state <= SMB2_REQUEST_RECV) {
+ if (event_loop_once(req->transport->socket->event.ctx) != 0) {
+ break;
+ }
+ }
+
+ status = torture_setup_complex_file(tree, FNAME);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = smb2_notify_recv(req, mem_ctx, &n);
+ CHECK_STATUS(status, STATUS_NOTIFY_ENUM_DIR);
+
+ /* if the buffer size is too large, we get invalid parameter */
+ n.in.recursive = 0x0000;
+ n.in.buffer_size = 0x00080001;
+ n.in.file.handle = dh;
+ n.in.completion_filter = 0x00000FFF;
+ n.in.unknown = 0x00000000;
+ req = smb2_notify_send(tree, &n);
+ status = smb2_notify_recv(req, mem_ctx, &n);
+ CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
+
+done:
+ return ret;
+}
+
+/* basic testing of SMB2 notify
+*/
+bool torture_smb2_notify(struct torture_context *torture)
+{
+ struct smb2_tree *tree;
+ bool ret = true;
+
+ if (!torture_smb2_connection(torture, &tree)) {
+ return false;
+ }
+
+ ret &= test_valid_request(torture, tree);
+
+ return ret;
+}
diff --git a/source4/torture/smb2/oplocks.c b/source4/torture/smb2/oplocks.c
new file mode 100644
index 0000000000..3fee0b4ab6
--- /dev/null
+++ b/source4/torture/smb2/oplocks.c
@@ -0,0 +1,177 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ test suite for SMB2 oplocks
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 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)
+
+static struct {
+ struct smb2_handle handle;
+ uint8_t level;
+ struct smb2_break br;
+ int count;
+ int failures;
+} 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++;
+ }
+
+ 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;
+ const char *name;
+ struct smb2_request *req;
+
+ 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);
+
+ ZERO_STRUCT(break_info.br);
+ 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;
+}
+
+bool torture_smb2_oplock_batch1(struct torture_context *tctx,
+ struct smb2_tree *tree)
+{
+ TALLOC_CTX *mem_ctx = talloc_new(tctx);
+ struct smb2_handle h1, h2;
+ struct smb2_create io;
+ NTSTATUS status;
+ const char *fname = "oplock.dat";
+ bool ret = true;
+
+ tree->session->transport->oplock.handler = torture_oplock_handler;
+ tree->session->transport->oplock.private_data = tree;
+
+ smb2_util_unlink(tree, fname);
+
+ ZERO_STRUCT(break_info);
+
+ ZERO_STRUCT(io);
+ io.in.security_flags = 0x00;
+ io.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH;
+ 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 = NTCREATEX_SHARE_ACCESS_READ |
+ NTCREATEX_SHARE_ACCESS_WRITE |
+ NTCREATEX_SHARE_ACCESS_DELETE;
+ io.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
+ io.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
+ NTCREATEX_OPTIONS_ASYNC_ALERT |
+ NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
+ 0x00200000;
+ io.in.fname = fname;
+
+ 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, 0);*/
+ CHECK_VAL(io.out.create_action, NTCREATEX_ACTION_CREATED);
+ CHECK_VAL(io.out.alloc_size, 0);
+ CHECK_VAL(io.out.size, 0);
+ CHECK_VAL(io.out.file_attr, FILE_ATTRIBUTE_ARCHIVE);
+ CHECK_VAL(io.out.reserved2, 0);
+ CHECK_VAL(break_info.count, 0);
+
+ h1 = io.out.file.handle;
+
+ ZERO_STRUCT(io.in.blobs);
+ status = smb2_create(tree, mem_ctx, &io);
+ CHECK_VAL(break_info.count, 1);
+ CHECK_VAL(break_info.failures, 0);
+ CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_II);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_II);
+ /*CHECK_VAL(io.out.reserved, 0);*/
+ CHECK_VAL(io.out.create_action, NTCREATEX_ACTION_EXISTED);
+ CHECK_VAL(io.out.alloc_size, 0);
+ CHECK_VAL(io.out.size, 0);
+ CHECK_VAL(io.out.file_attr, FILE_ATTRIBUTE_ARCHIVE);
+ CHECK_VAL(io.out.reserved2, 0);
+
+ h2 = io.out.file.handle;
+
+done:
+ talloc_free(mem_ctx);
+
+ smb2_util_close(tree, h1);
+ smb2_util_close(tree, h2);
+ smb2_util_unlink(tree, fname);
+ return ret;
+}
diff --git a/source4/torture/smb2/persistent_handles.c b/source4/torture/smb2/persistent_handles.c
new file mode 100644
index 0000000000..05c5dbbe85
--- /dev/null
+++ b/source4/torture/smb2/persistent_handles.c
@@ -0,0 +1,183 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ test suite for SMB2 persistent file handles
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 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)
+
+/*
+ basic testing of SMB2 persistent file handles
+ regarding the position information on the handle
+*/
+bool torture_smb2_persistent_handles1(struct torture_context *tctx,
+ struct smb2_tree *tree1,
+ struct smb2_tree *tree2)
+{
+ TALLOC_CTX *mem_ctx = talloc_new(tctx);
+ struct smb2_handle h1, h2;
+ struct smb2_create io1, io2;
+ NTSTATUS status;
+ const char *fname = "persistent_handles.dat";
+ DATA_BLOB b;
+ union smb_fileinfo qfinfo;
+ union smb_setfileinfo sfinfo;
+ bool ret = true;
+ uint64_t pos;
+
+ smb2_util_unlink(tree1, fname);
+
+ ZERO_STRUCT(io1);
+ io1.in.security_flags = 0x00;
+ io1.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH;
+ io1.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
+ io1.in.create_flags = 0x00000000;
+ io1.in.reserved = 0x00000000;
+ io1.in.desired_access = SEC_RIGHTS_FILE_ALL;
+ io1.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
+ io1.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
+ NTCREATEX_SHARE_ACCESS_WRITE |
+ NTCREATEX_SHARE_ACCESS_DELETE;
+ io1.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
+ io1.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
+ NTCREATEX_OPTIONS_ASYNC_ALERT |
+ NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
+ 0x00200000;
+ io1.in.fname = fname;
+
+ b = data_blob_talloc(mem_ctx, NULL, 16);
+ SBVAL(b.data, 0, 0);
+ SBVAL(b.data, 8, 0);
+
+ status = smb2_create_blob_add(tree1, &io1.in.blobs,
+ SMB2_CREATE_TAG_DHNQ,
+ b);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = smb2_create(tree1, mem_ctx, &io1);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
+ /*CHECK_VAL(io1.out.reserved, 0);*/
+ CHECK_VAL(io1.out.create_action, NTCREATEX_ACTION_CREATED);
+ CHECK_VAL(io1.out.alloc_size, 0);
+ CHECK_VAL(io1.out.size, 0);
+ CHECK_VAL(io1.out.file_attr, FILE_ATTRIBUTE_ARCHIVE);
+ CHECK_VAL(io1.out.reserved2, 0);
+
+ /* TODO: check extra blob content */
+
+ h1 = io1.out.file.handle;
+
+ ZERO_STRUCT(qfinfo);
+ qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
+ qfinfo.generic.in.file.handle = h1;
+ status = smb2_getinfo_file(tree1, 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 = h1;
+ sfinfo.position_information.in.position = 0x1000;
+ status = smb2_setinfo_file(tree1, &sfinfo);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ ZERO_STRUCT(qfinfo);
+ qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
+ qfinfo.generic.in.file.handle = h1;
+ status = smb2_getinfo_file(tree1, 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);
+
+ talloc_free(tree1);
+ tree1 = NULL;
+
+ ZERO_STRUCT(qfinfo);
+ qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
+ qfinfo.generic.in.file.handle = h1;
+ status = smb2_getinfo_file(tree2, mem_ctx, &qfinfo);
+ CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
+
+ ZERO_STRUCT(io2);
+ io2.in.fname = fname;
+
+ b = data_blob_talloc(tctx, NULL, 16);
+ SBVAL(b.data, 0, h1.data[0]);
+ SBVAL(b.data, 8, h1.data[1]);
+
+ status = smb2_create_blob_add(tree2, &io2.in.blobs,
+ SMB2_CREATE_TAG_DHNC,
+ b);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = smb2_create(tree2, mem_ctx, &io2);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
+ CHECK_VAL(io2.out.reserved, 0x00);
+ CHECK_VAL(io2.out.create_action, NTCREATEX_ACTION_EXISTED);
+ CHECK_VAL(io2.out.alloc_size, 0);
+ CHECK_VAL(io2.out.size, 0);
+ CHECK_VAL(io2.out.file_attr, FILE_ATTRIBUTE_ARCHIVE);
+ CHECK_VAL(io2.out.reserved2, 0);
+
+ h2 = io2.out.file.handle;
+
+ ZERO_STRUCT(qfinfo);
+ qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
+ qfinfo.generic.in.file.handle = h2;
+ status = smb2_getinfo_file(tree2, 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(tree2, h2);
+
+ talloc_free(mem_ctx);
+
+ smb2_util_unlink(tree2, fname);
+done:
+ return ret;
+}
diff --git a/source4/torture/smb2/read.c b/source4/torture/smb2/read.c
new file mode 100644
index 0000000000..548bd1ce61
--- /dev/null
+++ b/source4/torture/smb2/read.c
@@ -0,0 +1,245 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+#include "torture/torture.h"
+#include "torture/smb2/proto.h"
+
+#include "librpc/gen_ndr/ndr_security.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 CHECK_VALUE(v, correct) do { \
+ if ((v) != (correct)) { \
+ printf("(%s) Incorrect value %s=%u - should be %u\n", \
+ __location__, #v, (unsigned)v, (unsigned)correct); \
+ ret = false; \
+ goto done; \
+ }} while (0)
+
+#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[70000];
+ struct smb2_read rd;
+ TALLOC_CTX *tmp_ctx = talloc_new(tree);
+
+ ZERO_STRUCT(buf);
+
+ 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);
+
+ 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[70000];
+ struct smb2_read rd;
+ TALLOC_CTX *tmp_ctx = talloc_new(tree);
+ union smb_fileinfo info;
+
+ ZERO_STRUCT(buf);
+
+ 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;
+}
+
+
+/*
+ basic testing of SMB2 read
+*/
+struct torture_suite *torture_smb2_read_init(void)
+{
+ struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "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);
+
+ suite->description = talloc_strdup(suite, "SMB2-READ tests");
+
+ return suite;
+}
+
diff --git a/source4/torture/smb2/scan.c b/source4/torture/smb2/scan.c
new file mode 100644
index 0000000000..a5e682c111
--- /dev/null
+++ b/source4/torture/smb2/scan.c
@@ -0,0 +1,248 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "lib/cmdline/popt_common.h"
+#include "lib/events/events.h"
+#include "torture/torture.h"
+#include "param/param.h"
+#include "libcli/resolve/resolve.h"
+
+#include "torture/smb2/proto.h"
+
+#define FNAME "scan-getinfo.dat"
+#define DNAME "scan-getinfo.dir"
+
+
+/*
+ scan for valid SMB2 getinfo levels
+*/
+bool torture_smb2_getinfo_scan(struct torture_context *torture)
+{
+ struct smb2_tree *tree;
+ NTSTATUS status;
+ struct smb2_getinfo io;
+ struct smb2_handle fhandle, dhandle;
+ int c, i;
+
+ if (!torture_smb2_connection(torture, &tree)) {
+ return false;
+ }
+
+ status = torture_setup_complex_file(tree, FNAME);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed to setup complex file '%s'\n", FNAME);
+ return false;
+ }
+ torture_setup_complex_file(tree, FNAME ":2ndstream");
+
+ status = torture_setup_complex_dir(tree, DNAME);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed to setup complex dir '%s'\n", DNAME);
+ return false;
+ }
+ torture_setup_complex_file(tree, DNAME ":2ndstream");
+
+ 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, torture, &io);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_INFO_CLASS)) {
+ printf("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, torture, &io);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_INFO_CLASS)) {
+ printf("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);
+ }
+ }
+ }
+
+ return true;
+}
+
+/*
+ scan for valid SMB2 setinfo levels
+*/
+bool torture_smb2_setinfo_scan(struct torture_context *torture)
+{
+ struct smb2_tree *tree;
+ NTSTATUS status;
+ struct smb2_setinfo io;
+ struct smb2_handle handle;
+ int c, i;
+
+ if (!torture_smb2_connection(torture, &tree)) {
+ return false;
+ }
+
+ status = torture_setup_complex_file(tree, FNAME);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed to setup complex file '%s'\n", FNAME);
+ return false;
+ }
+ torture_setup_complex_file(tree, FNAME ":2ndstream");
+
+ torture_smb2_testfile(tree, FNAME, &handle);
+
+ ZERO_STRUCT(io);
+ io.in.blob = data_blob_talloc_zero(torture, 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)) {
+ printf("file level 0x%04x - %s\n",
+ io.in.level, nt_errstr(status));
+ }
+ }
+ }
+
+ return true;
+}
+
+
+/*
+ scan for valid SMB2 scan levels
+*/
+bool torture_smb2_find_scan(struct torture_context *torture)
+{
+ struct smb2_tree *tree;
+ NTSTATUS status;
+ struct smb2_find io;
+ struct smb2_handle handle;
+ int i;
+
+ if (!torture_smb2_connection(torture, &tree)) {
+ return false;
+ }
+
+ status = smb2_util_roothandle(tree, &handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed to open roothandle - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ 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, torture, &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)) {
+ printf("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
+*/
+bool torture_smb2_scan(struct torture_context *torture)
+{
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+ struct smb2_tree *tree;
+ const char *host = torture_setting_string(torture, "host", NULL);
+ const char *share = torture_setting_string(torture, "share", NULL);
+ struct cli_credentials *credentials = cmdline_credentials;
+ NTSTATUS status;
+ int opcode;
+ struct smb2_request *req;
+ struct smbcli_options options;
+
+ lp_smbcli_options(torture->lp_ctx, &options);
+
+ status = smb2_connect(mem_ctx, host,
+ lp_smb_ports(torture->lp_ctx),
+ share,
+ lp_resolve_context(torture->lp_ctx),
+ credentials, &tree, torture->ev, &options,
+ lp_socket_options(torture->lp_ctx),
+ lp_gensec_settings(torture, torture->lp_ctx));
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Connection failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ 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,
+ lp_smb_ports(torture->lp_ctx),
+ share,
+ lp_resolve_context(torture->lp_ctx),
+ credentials, &tree, torture->ev, &options,
+ lp_socket_options(torture->lp_ctx),
+ lp_gensec_settings(mem_ctx, torture->lp_ctx));
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Connection failed - %s\n", nt_errstr(status));
+ return false;
+ }
+ tree->session->transport->options.request_timeout = 3;
+ } else {
+ status = smb2_request_destroy(req);
+ printf("active opcode %4d gave status %s\n", opcode, nt_errstr(status));
+ }
+ }
+
+ talloc_free(mem_ctx);
+
+ return true;
+}
diff --git a/source4/torture/smb2/setinfo.c b/source4/torture/smb2/setinfo.c
new file mode 100644
index 0000000000..7a912b4989
--- /dev/null
+++ b/source4/torture/smb2/setinfo.c
@@ -0,0 +1,300 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#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"
+
+#define BASEDIR ""
+
+/* 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 *torture)
+{
+ struct smb2_tree *tree;
+ bool ret = true;
+ struct smb2_handle handle;
+ char *fname;
+ char *fname_new;
+ 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;
+
+ ZERO_STRUCT(handle);
+
+ fname = talloc_asprintf(torture, BASEDIR "fnum_test_%d.txt", n);
+ fname_new = talloc_asprintf(torture, BASEDIR "fnum_test_new_%d.txt", n);
+
+ if (!torture_smb2_connection(torture, &tree)) {
+ return false;
+ }
+
+#define RECREATE_FILE(fname) do { \
+ smb2_util_close(tree, handle); \
+ status = smb2_create_complex_file(tree, fname, &handle); \
+ if (!NT_STATUS_IS_OK(status)) { \
+ printf("(%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)) { \
+ printf("(%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, torture, &finfo2); \
+ if (!NT_STATUS_IS_OK(status2)) { \
+ printf("(%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) { \
+ printf("(%s) %s - %s/%s should be 0x%x - 0x%x\n", __location__, \
+ call_name, #stype, #field, \
+ (uint_t)value, (uint_t)finfo2.stype.out.field); \
+ torture_smb2_all_info(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) { \
+ printf("(%s) %s - %s/%s should be 0x%x - 0x%x\n", __location__, \
+ call_name, #stype, #field, \
+ (uint_t)value, \
+ (uint_t)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)); \
+ torture_smb2_all_info(tree, handle); \
+ ret = false; \
+ goto done; \
+ }} 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)
+
+ torture_smb2_all_info(tree, handle);
+
+ 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_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);
+
+ 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(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);
+
+ printf("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);
+
+ printf("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);
+
+ printf("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);
+
+ printf("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);
+
+ printf("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);
+
+ printf("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);
+
+ printf("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);
+
+ printf("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);
+
+ printf("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);
+
+ printf("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(torture, "S-1-5-32-1234-5432");
+ 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);
+
+ printf("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);
+ CHECK1(SEC_DESC);
+
+ if (!security_acl_equal(finfo2.query_secdesc.out.sd->dacl, sd->dacl)) {
+ printf("%s: security descriptors don't match!\n", __location__);
+ printf("got:\n");
+ NDR_PRINT_DEBUG(security_descriptor, finfo2.query_secdesc.out.sd);
+ printf("expected:\n");
+ NDR_PRINT_DEBUG(security_descriptor, sd);
+ ret = false;
+ }
+
+ printf("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);
+ CHECK1(SEC_DESC);
+
+ if (!security_acl_equal(finfo2.query_secdesc.out.sd->dacl, sd->dacl)) {
+ printf("%s: security descriptors don't match!\n", __location__);
+ printf("got:\n");
+ NDR_PRINT_DEBUG(security_descriptor, finfo2.query_secdesc.out.sd);
+ printf("expected:\n");
+ NDR_PRINT_DEBUG(security_descriptor, sd);
+ ret = false;
+ }
+
+done:
+ status = smb2_util_close(tree, handle);
+ if (NT_STATUS_IS_ERR(status)) {
+ printf("Failed to delete %s - %s\n", fname, nt_errstr(status));
+ }
+ smb2_util_unlink(tree, fname);
+
+ return ret;
+}
+
+
diff --git a/source4/torture/smb2/smb2.c b/source4/torture/smb2/smb2.c
new file mode 100644
index 0000000000..9418650de4
--- /dev/null
+++ b/source4/torture/smb2/smb2.c
@@ -0,0 +1,150 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.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;
+
+ if (!torture_smb2_connection(torture_ctx, &tree1))
+ return false;
+
+ fn = test->fn;
+
+ ret = fn(torture_ctx, tree1);
+
+ talloc_free(tree1);
+
+ 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, struct torture_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;
+
+ struct smb2_tree *tree1;
+ struct smb2_tree *tree2;
+ TALLOC_CTX *mem_ctx = talloc_new(torture_ctx);
+
+ if (!torture_smb2_connection(torture_ctx, &tree1) ||
+ !torture_smb2_connection(torture_ctx, &tree2)) {
+ return false;
+ }
+
+ talloc_steal(mem_ctx, tree1);
+ talloc_steal(mem_ctx, tree2);
+
+ fn = test->fn;
+
+ ret = fn(torture_ctx, tree1, tree2);
+
+ /* the test may already closed some of the connections */
+ talloc_free(mem_ctx);
+
+ return ret;
+}
+
+
+_PUBLIC_ 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, struct torture_test *);
+
+ return test;
+}
+
+NTSTATUS torture_smb2_init(void)
+{
+ struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "SMB2");
+ torture_suite_add_simple_test(suite, "CONNECT", torture_smb2_connect);
+ torture_suite_add_simple_test(suite, "SCAN", torture_smb2_scan);
+ torture_suite_add_simple_test(suite, "SCANGETINFO", torture_smb2_getinfo_scan);
+ torture_suite_add_simple_test(suite, "SCANSETINFO", torture_smb2_setinfo_scan);
+ torture_suite_add_simple_test(suite, "SCANFIND", torture_smb2_find_scan);
+ torture_suite_add_simple_test(suite, "GETINFO", torture_smb2_getinfo);
+ torture_suite_add_simple_test(suite, "SETINFO", torture_smb2_setinfo);
+ torture_suite_add_simple_test(suite, "FIND", torture_smb2_find);
+ torture_suite_add_suite(suite, torture_smb2_lock_init());
+ torture_suite_add_suite(suite, torture_smb2_read_init());
+ torture_suite_add_suite(suite, torture_smb2_create_init());
+ torture_suite_add_simple_test(suite, "NOTIFY", torture_smb2_notify);
+ torture_suite_add_2smb2_test(suite, "PERSISTENT-HANDLES1", torture_smb2_persistent_handles1);
+ torture_suite_add_1smb2_test(suite, "OPLOCK-BATCH1", torture_smb2_oplock_batch1);
+
+ suite->description = talloc_strdup(suite, "SMB2-specific tests");
+
+ torture_register_suite(suite);
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/torture/smb2/util.c b/source4/torture/smb2/util.c
new file mode 100644
index 0000000000..b17dc246e3
--- /dev/null
+++ b/source4/torture/smb2/util.c
@@ -0,0 +1,408 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "libcli/smb_composite/smb_composite.h"
+#include "lib/cmdline/popt_common.h"
+#include "lib/events/events.h"
+#include "system/time.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "param/param.h"
+#include "libcli/resolve/resolve.h"
+
+#include "torture/torture.h"
+#include "torture/smb2/proto.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 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);
+ 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)) {
+ printf("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)) {
+ printf("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) { \
+ printf("(%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 smb2_tree *tree, const char *fname,
+ struct smb2_handle *handle)
+{
+ return smb2_create_complex(tree, fname, handle, false);
+}
+
+/*
+ create a complex dir using the SMB2 protocol
+*/
+NTSTATUS smb2_create_complex_dir(struct smb2_tree *tree, const char *fname,
+ struct smb2_handle *handle)
+{
+ return smb2_create_complex(tree, fname, handle, true);
+}
+
+/*
+ show lots of information about a file
+*/
+void torture_smb2_all_info(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;
+ }
+
+ d_printf("all_info for '%s'\n", io.all_info2.out.fname.s);
+ d_printf("\tcreate_time: %s\n", nt_time_string(tmp_ctx, io.all_info2.out.create_time));
+ d_printf("\taccess_time: %s\n", nt_time_string(tmp_ctx, io.all_info2.out.access_time));
+ d_printf("\twrite_time: %s\n", nt_time_string(tmp_ctx, io.all_info2.out.write_time));
+ d_printf("\tchange_time: %s\n", nt_time_string(tmp_ctx, io.all_info2.out.change_time));
+ d_printf("\tattrib: 0x%x\n", io.all_info2.out.attrib);
+ d_printf("\tunknown1: 0x%x\n", io.all_info2.out.unknown1);
+ d_printf("\talloc_size: %llu\n", (long long)io.all_info2.out.alloc_size);
+ d_printf("\tsize: %llu\n", (long long)io.all_info2.out.size);
+ d_printf("\tnlink: %u\n", io.all_info2.out.nlink);
+ d_printf("\tdelete_pending: %u\n", io.all_info2.out.delete_pending);
+ d_printf("\tdirectory: %u\n", io.all_info2.out.directory);
+ d_printf("\tfile_id: %llu\n", (long long)io.all_info2.out.file_id);
+ d_printf("\tea_size: %u\n", io.all_info2.out.ea_size);
+ d_printf("\taccess_mask: 0x%08x\n", io.all_info2.out.access_mask);
+ d_printf("\tposition: 0x%llx\n", (long long)io.all_info2.out.position);
+ d_printf("\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)) {
+ d_printf("\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;i<io.all_eas.out.num_eas;i++) {
+ d_printf("\tEA[%d] flags=%d len=%d '%s'\n", i,
+ io.all_eas.out.eas[i].flags,
+ (int)io.all_eas.out.eas[i].value.length,
+ io.all_eas.out.eas[i].name.s);
+ }
+ }
+
+ /* streams, if available */
+ io.generic.level = RAW_FILEINFO_STREAM_INFORMATION;
+ status = smb2_getinfo_file(tree, tmp_ctx, &io);
+ if (NT_STATUS_IS_OK(status)) {
+ int i;
+ for (i=0;i<io.stream_info.out.num_streams;i++) {
+ d_printf("\tstream %d:\n", i);
+ d_printf("\t\tsize %ld\n",
+ (long)io.stream_info.out.streams[i].size);
+ d_printf("\t\talloc size %ld\n",
+ (long)io.stream_info.out.streams[i].alloc_size);
+ d_printf("\t\tname %s\n", io.stream_info.out.streams[i].stream_name.s);
+ }
+ }
+
+ if (DEBUGLVL(1)) {
+ /* the security descriptor */
+ io.query_secdesc.level = RAW_FILEINFO_SEC_DESC;
+ io.query_secdesc.in.secinfo_flags =
+ SECINFO_OWNER|SECINFO_GROUP|
+ SECINFO_DACL;
+ status = smb2_getinfo_file(tree, tmp_ctx, &io);
+ if (NT_STATUS_IS_OK(status)) {
+ NDR_PRINT_DEBUG(security_descriptor, io.query_secdesc.out.sd);
+ }
+ }
+
+ talloc_free(tmp_ctx);
+}
+
+
+/*
+ open a smb2 connection
+*/
+bool torture_smb2_connection(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 cli_credentials *credentials = cmdline_credentials;
+ struct smbcli_options options;
+
+ lp_smbcli_options(tctx->lp_ctx, &options);
+
+ status = smb2_connect(tctx, host,
+ lp_smb_ports(tctx->lp_ctx),
+ share,
+ lp_resolve_context(tctx->lp_ctx),
+ credentials, tree,
+ tctx->ev, &options,
+ lp_socket_options(tctx->lp_ctx),
+ lp_gensec_settings(tctx, tctx->lp_ctx)
+ );
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed to connect to SMB2 share \\\\%s\\%s - %s\n",
+ host, share, nt_errstr(status));
+ return false;
+ }
+ return true;
+}
+
+
+/*
+ create and return a handle to a test file
+*/
+NTSTATUS torture_smb2_testfile(struct smb2_tree *tree, const char *fname,
+ struct smb2_handle *handle)
+{
+ struct smb2_create io;
+ struct smb2_read r;
+ NTSTATUS status;
+
+ ZERO_STRUCT(io);
+ io.in.oplock_level = 0;
+ io.in.desired_access = SEC_RIGHTS_FILE_ALL;
+ 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;
+
+ ZERO_STRUCT(r);
+ r.in.file.handle = *handle;
+ r.in.length = 5;
+ r.in.offset = 0;
+
+ smb2_read(tree, tree, &r);
+
+ 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)
+{
+ struct smb2_create io;
+ NTSTATUS status;
+
+ ZERO_STRUCT(io);
+ io.in.oplock_level = 0;
+ io.in.desired_access = SEC_RIGHTS_DIR_ALL;
+ 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 a complex file using the old SMB protocol, to make it easier to
+ find fields in SMB2 getinfo levels
+*/
+NTSTATUS torture_setup_complex_file(struct smb2_tree *tree, const char *fname)
+{
+ struct smb2_handle handle;
+ NTSTATUS status = smb2_create_complex_file(tree, fname, &handle);
+ NT_STATUS_NOT_OK_RETURN(status);
+ return smb2_util_close(tree, handle);
+}
+
+
+/*
+ create a complex dir using the old SMB protocol, to make it easier to
+ find fields in SMB2 getinfo levels
+*/
+NTSTATUS torture_setup_complex_dir(struct smb2_tree *tree, const char *fname)
+{
+ struct smb2_handle handle;
+ NTSTATUS status = smb2_create_complex_dir(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 = NULL;
+
+ status = smb2_create(tree, tree, &io);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ *handle = io.out.file.handle;
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/torture/smbiconv.c b/source4/torture/smbiconv.c
new file mode 100644
index 0000000000..173f37175b
--- /dev/null
+++ b/source4/torture/smbiconv.c
@@ -0,0 +1,236 @@
+/*
+ Unix SMB/CIFS implementation.
+ Charset module tester
+
+ Copyright (C) Jelmer Vernooij 2003
+ Based on iconv/icon_prog.c from the GNU C Library,
+ Contributed by Ulrich Drepper <drepper@cygnus.com>, 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+static int process_block (smb_iconv_t cd, const char *addr, size_t len, FILE *output)
+{
+#define OUTBUF_SIZE 32768
+ const char *start = addr;
+ char outbuf[OUTBUF_SIZE];
+ char *outptr;
+ size_t outlen;
+ size_t n;
+
+ while (len > 0)
+ {
+ outptr = outbuf;
+ outlen = OUTBUF_SIZE;
+ n = smb_iconv (cd, &addr, &len, &outptr, &outlen);
+
+ if (outptr != outbuf)
+ {
+ /* We have something to write out. */
+ int errno_save = errno;
+
+ if (fwrite (outbuf, 1, outptr - outbuf, output)
+ < (size_t) (outptr - outbuf)
+ || ferror (output))
+ {
+ /* Error occurred while printing the result. */
+ DEBUG (0, ("conversion stopped due to problem in writing the output"));
+ return -1;
+ }
+
+ errno = errno_save;
+ }
+
+ if (errno != E2BIG)
+ {
+ /* iconv() ran into a problem. */
+ switch (errno)
+ {
+ case EILSEQ:
+ DEBUG(0,("illegal input sequence at position %ld",
+ (long) (addr - start)));
+ break;
+ case EINVAL:
+ DEBUG(0, ("\
+incomplete character or shift sequence at end of buffer"));
+ break;
+ case EBADF:
+ DEBUG(0, ("internal error (illegal descriptor)"));
+ break;
+ default:
+ DEBUG(0, ("unknown iconv() error %d", errno));
+ break;
+ }
+
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+static int process_fd (iconv_t cd, int fd, FILE *output)
+{
+ /* we have a problem with reading from a descriptor since we must not
+ provide the iconv() function an incomplete character or shift
+ sequence at the end of the buffer. Since we have to deal with
+ arbitrary encodings we must read the whole text in a buffer and
+ process it in one step. */
+ static char *inbuf = NULL;
+ static size_t maxlen = 0;
+ char *inptr = NULL;
+ size_t actlen = 0;
+
+ while (actlen < maxlen)
+ {
+ ssize_t n = read (fd, inptr, maxlen - actlen);
+
+ if (n == 0)
+ /* No more text to read. */
+ break;
+
+ if (n == -1)
+ {
+ /* Error while reading. */
+ DEBUG(0, ("error while reading the input"));
+ return -1;
+ }
+
+ inptr += n;
+ actlen += n;
+ }
+
+ if (actlen == maxlen)
+ while (1)
+ {
+ ssize_t n;
+ char *new_inbuf;
+
+ /* Increase the buffer. */
+ new_inbuf = (char *) realloc (inbuf, maxlen + 32768);
+ if (new_inbuf == NULL)
+ {
+ DEBUG(0, ("unable to allocate buffer for input"));
+ return -1;
+ }
+ inbuf = new_inbuf;
+ maxlen += 32768;
+ inptr = inbuf + actlen;
+
+ do
+ {
+ n = read (fd, inptr, maxlen - actlen);
+
+ if (n == 0)
+ /* No more text to read. */
+ break;
+
+ if (n == -1)
+ {
+ /* Error while reading. */
+ DEBUG(0, ("error while reading the input"));
+ return -1;
+ }
+
+ inptr += n;
+ actlen += n;
+ }
+ while (actlen < maxlen);
+
+ if (n == 0)
+ /* Break again so we leave both loops. */
+ break;
+ }
+
+ /* Now we have all the input in the buffer. Process it in one run. */
+ return process_block (cd, inbuf, actlen, output);
+}
+
+/* Main function */
+
+int main(int argc, char *argv[])
+{
+ const char *file = NULL;
+ char *from = "";
+ char *to = "";
+ char *output = NULL;
+ const char *preload_modules[] = {NULL, NULL};
+ FILE *out = stdout;
+ int fd;
+ smb_iconv_t cd;
+
+ /* make sure the vars that get altered (4th field) are in
+ a fixed location or certain compilers complain */
+ poptContext pc;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ { "from-code", 'f', POPT_ARG_STRING, &from, 0, "Encoding of original text" },
+ { "to-code", 't', POPT_ARG_STRING, &to, 0, "Encoding for output" },
+ { "output", 'o', POPT_ARG_STRING, &output, 0, "Write output to this file" },
+ { "preload-modules", 'p', POPT_ARG_STRING, &preload_modules[0], 0, "Modules to load" },
+ { NULL }
+ };
+
+ setlinebuf(stdout);
+
+ pc = poptGetContext("smbiconv", argc, (const char **) argv,
+ long_options, 0);
+
+ poptSetOtherOptionHelp(pc, "[FILE] ...");
+
+ while(poptGetNextOpt(pc) != -1);
+
+ if (preload_modules[0]) smb_load_modules(preload_modules);
+
+ if(output) {
+ out = fopen(output, "w");
+
+ if(!out) {
+ DEBUG(0, ("Can't open output file '%s': %s, exiting...\n", output, strerror(errno)));
+ return 1;
+ }
+ }
+
+ cd = smb_iconv_open_ex(tctx, to, from, lp_parm_bool(tctx->lp_ctx, NULL, "iconv", "native", true));
+ if((int)cd == -1) {
+ DEBUG(0,("unable to find from or to encoding, exiting...\n"));
+ if (out != stdout) fclose(out);
+ return 1;
+ }
+
+ while((file = poptGetArg(pc))) {
+ if(strcmp(file, "-") == 0) fd = 0;
+ else {
+ fd = open(file, O_RDONLY);
+
+ if(!fd) {
+ DEBUG(0, ("Can't open input file '%s': %s, ignoring...\n", file, strerror(errno)));
+ continue;
+ }
+ }
+
+ /* Loop thru all arguments */
+ process_fd(cd, fd, out);
+
+ close(fd);
+ }
+ poptFreeContext(pc);
+
+ fclose(out);
+
+ return 0;
+}
diff --git a/source4/torture/smbtorture.c b/source4/torture/smbtorture.c
new file mode 100644
index 0000000000..d01dc9a856
--- /dev/null
+++ b/source4/torture/smbtorture.c
@@ -0,0 +1,673 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/cmdline/popt_common.h"
+#include "system/time.h"
+#include "system/wait.h"
+#include "system/filesys.h"
+#include "system/readline.h"
+#include "lib/smbreadline/smbreadline.h"
+#include "libcli/libcli.h"
+#include "lib/ldb/include/ldb.h"
+#include "lib/events/events.h"
+#include "dynconfig/dynconfig.h"
+
+#include "torture/smbtorture.h"
+#include "../lib/util/dlinklist.h"
+#include "librpc/rpc/dcerpc.h"
+#include "auth/gensec/gensec.h"
+#include "param/param.h"
+
+#include "auth/credentials/credentials.h"
+
+static bool run_matching(struct torture_context *torture,
+ const char *prefix,
+ const char *expr,
+ struct torture_suite *suite,
+ bool *matched)
+{
+ bool ret = true;
+
+ if (suite == NULL) {
+ struct torture_suite *o;
+
+ for (o = (torture_root == NULL?NULL:torture_root->children); o; o = o->next) {
+ if (gen_fnmatch(expr, o->name) == 0) {
+ *matched = true;
+ reload_charcnv(torture->lp_ctx);
+ ret &= torture_run_suite(torture, o);
+ continue;
+ }
+
+ ret &= run_matching(torture, o->name, expr, o, matched);
+ }
+ } else {
+ char *name;
+ struct torture_suite *c;
+ struct torture_tcase *t;
+
+ for (c = suite->children; c; c = c->next) {
+ asprintf(&name, "%s-%s", prefix, c->name);
+
+ if (gen_fnmatch(expr, name) == 0) {
+ *matched = true;
+ reload_charcnv(torture->lp_ctx);
+ torture->active_testname = talloc_strdup(torture, prefix);
+ ret &= torture_run_suite(torture, c);
+ free(name);
+ continue;
+ }
+
+ ret &= run_matching(torture, name, expr, c, matched);
+
+ free(name);
+ }
+
+ for (t = suite->testcases; t; t = t->next) {
+ asprintf(&name, "%s-%s", prefix, t->name);
+ if (gen_fnmatch(expr, name) == 0) {
+ *matched = true;
+ reload_charcnv(torture->lp_ctx);
+ torture->active_testname = talloc_strdup(torture, prefix);
+ ret &= torture_run_tcase(torture, t);
+ talloc_free(torture->active_testname);
+ }
+ free(name);
+ }
+ }
+
+ return ret;
+}
+
+#define MAX_COLS 80 /* FIXME: Determine this at run-time */
+
+/****************************************************************************
+run a specified test or "ALL"
+****************************************************************************/
+static bool run_test(struct torture_context *torture, const char *name)
+{
+ bool ret = true;
+ bool matched = false;
+ struct torture_suite *o;
+
+ if (strequal(name, "ALL")) {
+ for (o = torture_root->children; o; o = o->next) {
+ ret &= torture_run_suite(torture, o);
+ }
+ return ret;
+ }
+
+ ret = run_matching(torture, NULL, name, NULL, &matched);
+
+ if (!matched) {
+ printf("Unknown torture operation '%s'\n", name);
+ return false;
+ }
+
+ return ret;
+}
+
+static bool parse_target(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)) {
+ status = dcerpc_parse_binding(talloc_autofree_context(), 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;
+ }
+ lp_set_cmdline(lp_ctx, "torture:host", binding_struct->host);
+ if (lp_parm_string(lp_ctx, NULL, "torture", "share") == NULL)
+ lp_set_cmdline(lp_ctx, "torture:share", "IPC$");
+ lp_set_cmdline(lp_ctx, "torture:binding", target);
+ } else {
+ lp_set_cmdline(lp_ctx, "torture:host", host);
+ lp_set_cmdline(lp_ctx, "torture:share", share);
+ lp_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;
+
+ /* retrievieng the userdn */
+ p = strchr_m(dns, '#');
+ if (!p) {
+ lp_set_cmdline(lp_ctx, "torture:ldap_userdn", "");
+ lp_set_cmdline(lp_ctx, "torture:ldap_basedn", "");
+ lp_set_cmdline(lp_ctx, "torture:ldap_secret", "");
+ return;
+ }
+ userdn = strndup(dns, p - dns);
+ lp_set_cmdline(lp_ctx, "torture:ldap_userdn", userdn);
+
+ /* retrieve the basedn */
+ d = p + 1;
+ p = strchr_m(d, '#');
+ if (!p) {
+ lp_set_cmdline(lp_ctx, "torture:ldap_basedn", "");
+ lp_set_cmdline(lp_ctx, "torture:ldap_secret", "");
+ return;
+ }
+ basedn = strndup(d, p - d);
+ lp_set_cmdline(lp_ctx, "torture:ldap_basedn", basedn);
+
+ /* retrieve the secret */
+ p = p + 1;
+ if (!p) {
+ lp_set_cmdline(lp_ctx, "torture:ldap_secret", "");
+ return;
+ }
+ secret = strdup(p);
+ lp_set_cmdline(lp_ctx, "torture:ldap_secret", secret);
+
+ printf ("%s - %s - %s\n", userdn, basedn, secret);
+
+}
+
+static void print_test_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);
+ }
+ }
+}
+
+_NORETURN_ static void usage(poptContext pc)
+{
+ struct torture_suite *o;
+ struct torture_suite *s;
+ struct torture_tcase *t;
+ int i;
+
+ 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:");
+
+ if (torture_root == NULL) {
+ printf("NO TESTS LOADED\n");
+ exit(1);
+ }
+
+ 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");
+
+ exit(1);
+}
+
+_NORETURN_ static void max_runtime_handler(int sig)
+{
+ DEBUG(0,("maximum runtime exceeded for smbtorture - terminating\n"));
+ exit(1);
+}
+
+struct timeval last_suite_started;
+
+static void simple_suite_start(struct torture_context *ctx,
+ struct torture_suite *suite)
+{
+ last_suite_started = timeval_current();
+ printf("Running %s\n", suite->name);
+}
+
+static void simple_suite_finish(struct torture_context *ctx,
+ struct torture_suite *suite)
+{
+
+ printf("%s took %g secs\n\n", suite->name,
+ timeval_elapsed(&last_suite_started));
+}
+
+static void simple_test_result(struct torture_context *context,
+ enum torture_result res, const char *reason)
+{
+ switch (res) {
+ case TORTURE_OK:
+ if (reason)
+ printf("OK: %s\n", reason);
+ break;
+ case TORTURE_FAIL:
+ printf("TEST %s FAILED! - %s\n", context->active_test->name, reason);
+ break;
+ case TORTURE_ERROR:
+ printf("ERROR IN TEST %s! - %s\n", context->active_test->name, reason);
+ break;
+ case TORTURE_SKIP:
+ printf("SKIP: %s - %s\n", context->active_test->name, reason);
+ break;
+ }
+}
+
+static void simple_comment(struct torture_context *test,
+ const char *comment)
+{
+ printf("%s", comment);
+}
+
+static void simple_warning(struct torture_context *test,
+ const char *comment)
+{
+ fprintf(stderr, "WARNING: %s\n", comment);
+}
+
+const static struct torture_ui_ops std_ui_ops = {
+ .comment = simple_comment,
+ .warning = simple_warning,
+ .suite_start = simple_suite_start,
+ .suite_finish = simple_suite_finish,
+ .test_result = simple_test_result
+};
+
+
+static void quiet_suite_start(struct torture_context *ctx,
+ struct torture_suite *suite)
+{
+ int i;
+ ctx->results->quiet = true;
+ for (i = 1; i < ctx->level; i++) putchar('\t');
+ printf("%s: ", suite->name);
+ fflush(stdout);
+}
+
+static void quiet_suite_finish(struct torture_context *ctx,
+ struct torture_suite *suite)
+{
+ putchar('\n');
+}
+
+static void quiet_test_result(struct torture_context *context,
+ enum torture_result res, const char *reason)
+{
+ fflush(stdout);
+ switch (res) {
+ case TORTURE_OK: putchar('.'); break;
+ case TORTURE_FAIL: putchar('F'); break;
+ case TORTURE_ERROR: putchar('E'); break;
+ case TORTURE_SKIP: putchar('I'); break;
+ }
+}
+
+const static struct torture_ui_ops quiet_ui_ops = {
+ .suite_start = quiet_suite_start,
+ .suite_finish = quiet_suite_finish,
+ .test_result = quiet_test_result
+};
+
+static void run_shell(struct torture_context *tctx)
+{
+ char *cline;
+ int argc;
+ const char **argv;
+ int ret;
+
+ while (1) {
+ cline = smb_readline("torture> ", NULL, NULL);
+
+ if (cline == NULL)
+ return;
+
+ ret = poptParseArgvString(cline, &argc, &argv);
+ if (ret != 0) {
+ fprintf(stderr, "Error parsing line\n");
+ continue;
+ }
+
+ if (!strcmp(argv[0], "quit")) {
+ return;
+ } else if (!strcmp(argv[0], "set")) {
+ if (argc < 3) {
+ fprintf(stderr, "Usage: set <variable> <value>\n");
+ } else {
+ char *name = talloc_asprintf(NULL, "torture:%s", argv[1]);
+ lp_set_cmdline(tctx->lp_ctx, name, argv[2]);
+ talloc_free(name);
+ }
+ } else if (!strcmp(argv[0], "help")) {
+ fprintf(stderr, "Available commands:\n"
+ " help - This help command\n"
+ " run - Run test\n"
+ " set - Change variables\n"
+ "\n");
+ } else if (!strcmp(argv[0], "run")) {
+ if (argc < 2) {
+ fprintf(stderr, "Usage: run TEST-NAME [OPTIONS...]\n");
+ } else {
+ run_test(tctx, argv[1]);
+ }
+ }
+ free(cline);
+ }
+}
+
+/****************************************************************************
+ main program
+****************************************************************************/
+int main(int argc,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 = "simple";
+ const char *basedir = NULL;
+ const char *extra_module = NULL;
+ static int list_tests = 0;
+ int num_extra_users = 0;
+ enum {OPT_LOADFILE=1000,OPT_UNCLIST,OPT_TIMELIMIT,OPT_DNS, OPT_LIST,
+ OPT_DANGEROUS,OPT_SMB_PORTS,OPT_ASYNC,OPT_NUMPROGS,OPT_EXTRA_USER};
+
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {"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", 0, POPT_ARG_NONE, &list_tests, 0, "List available tests 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},
+ POPT_COMMON_SAMBA
+ POPT_COMMON_CONNECTION
+ POPT_COMMON_CREDENTIALS
+ POPT_COMMON_VERSION
+ { NULL }
+ };
+
+ setlinebuf(stdout);
+
+ /* we are never interested in SIGPIPE */
+ BlockSignals(true, SIGPIPE);
+
+ pc = poptGetContext("smbtorture", argc, (const char **) argv, long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+
+ poptSetOtherOptionHelp(pc, "<binding>|<unc> TEST1 TEST2 ...");
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case OPT_LOADFILE:
+ lp_set_cmdline(cmdline_lp_ctx, "torture:loadfile", poptGetOptArg(pc));
+ break;
+ case OPT_UNCLIST:
+ lp_set_cmdline(cmdline_lp_ctx, "torture:unclist", poptGetOptArg(pc));
+ break;
+ case OPT_TIMELIMIT:
+ lp_set_cmdline(cmdline_lp_ctx, "torture:timelimit", poptGetOptArg(pc));
+ break;
+ case OPT_NUMPROGS:
+ lp_set_cmdline(cmdline_lp_ctx, "torture:nprocs", poptGetOptArg(pc));
+ break;
+ case OPT_DNS:
+ parse_dns(cmdline_lp_ctx, poptGetOptArg(pc));
+ break;
+ case OPT_DANGEROUS:
+ lp_set_cmdline(cmdline_lp_ctx, "torture:dangerous", "Yes");
+ break;
+ case OPT_ASYNC:
+ lp_set_cmdline(cmdline_lp_ctx, "torture:async", "Yes");
+ break;
+ case OPT_SMB_PORTS:
+ lp_set_cmdline(cmdline_lp_ctx, "smb ports", poptGetOptArg(pc));
+ break;
+ case OPT_EXTRA_USER:
+ {
+ char *option = talloc_asprintf(NULL, "torture:extra_user%u",
+ ++num_extra_users);
+ char *value = poptGetOptArg(pc);
+ lp_set_cmdline(cmdline_lp_ctx, option, value);
+ talloc_free(option);
+ }
+ break;
+ }
+ }
+
+ if (strcmp(target, "samba3") == 0) {
+ lp_set_cmdline(cmdline_lp_ctx, "torture:samba3", "true");
+ } else if (strcmp(target, "samba4") == 0) {
+ lp_set_cmdline(cmdline_lp_ctx, "torture:samba4", "true");
+ } else if (strcmp(target, "win7") == 0) {
+ lp_set_cmdline(cmdline_lp_ctx, "torture:win7", "true");
+ }
+
+ 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(talloc_autofree_context(), poptGetOptArg(pc));
+
+ if (fn == NULL)
+ d_printf("Unable to load module from %s\n", poptGetOptArg(pc));
+ else {
+ status = fn();
+ if (NT_STATUS_IS_ERR(status)) {
+ d_printf("Error initializing module %s: %s\n",
+ poptGetOptArg(pc), nt_errstr(status));
+ }
+ }
+ } else {
+ torture_init();
+ }
+
+ if (list_tests) {
+ print_test_list();
+ return 0;
+ }
+
+ if (torture_seed == 0) {
+ torture_seed = time(NULL);
+ }
+ printf("Using seed %d\n", torture_seed);
+ srandom(torture_seed);
+
+ argv_new = discard_const_p(char *, poptGetArgs(pc));
+
+ argc_new = argc;
+ for (i=0; i<argc; i++) {
+ if (argv_new[i] == NULL) {
+ argc_new = i;
+ break;
+ }
+ }
+
+ if (!(argc_new >= 3 || (shell && argc_new >= 2))) {
+ usage(pc);
+ exit(1);
+ }
+
+ if (!parse_target(cmdline_lp_ctx, argv_new[1])) {
+ usage(pc);
+ exit(1);
+ }
+
+ if (!strcmp(ui_ops_name, "simple")) {
+ ui_ops = &std_ui_ops;
+ } else if (!strcmp(ui_ops_name, "subunit")) {
+ ui_ops = &torture_subunit_ui_ops;
+ } else if (!strcmp(ui_ops_name, "quiet")) {
+ ui_ops = &quiet_ui_ops;
+ } else {
+ printf("Unknown output format '%s'\n", ui_ops_name);
+ exit(1);
+ }
+
+ results = torture_results_init(talloc_autofree_context(), ui_ops);
+
+ torture = torture_context_init(s4_event_context_init(NULL), results);
+ if (basedir != NULL) {
+ if (basedir[0] != '/') {
+ fprintf(stderr, "Please specify an absolute path to --basedir\n");
+ return 1;
+ }
+ torture->outputdir = basedir;
+ } else {
+ char *pwd = talloc_size(torture, PATH_MAX);
+ if (!getcwd(pwd, PATH_MAX)) {
+ fprintf(stderr, "Unable to determine current working directory\n");
+ return 1;
+ }
+ torture->outputdir = pwd;
+ }
+
+ torture->lp_ctx = cmdline_lp_ctx;
+
+ gensec_init(cmdline_lp_ctx);
+
+ if (argc_new == 0) {
+ printf("You must specify a test to run, or 'ALL'\n");
+ } else if (shell) {
+ run_shell(torture);
+ } else {
+ for (i=2;i<argc_new;i++) {
+ if (!run_test(torture, argv_new[i])) {
+ correct = false;
+ }
+ }
+ }
+
+ if (torture->results->returncode && correct) {
+ return(0);
+ } else {
+ return(1);
+ }
+}
diff --git a/source4/torture/smbtorture.h b/source4/torture/smbtorture.h
new file mode 100644
index 0000000000..c1363fd4c1
--- /dev/null
+++ b/source4/torture/smbtorture.h
@@ -0,0 +1,40 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#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(void);
+bool torture_register_suite(struct torture_suite *suite);
+
+#endif /* __SMBTORTURE_H__ */
diff --git a/source4/torture/tests/test_gentest.sh b/source4/torture/tests/test_gentest.sh
new file mode 100755
index 0000000000..c7f66c8445
--- /dev/null
+++ b/source4/torture/tests/test_gentest.sh
@@ -0,0 +1,34 @@
+#!/bin/sh
+# Blackbox tests for gentest
+# Copyright (C) 2008 Andrew Tridgell
+# based on test_smbclient.sh
+
+if [ $# -lt 4 ]; then
+cat <<EOF
+Usage: test_gentest.sh SERVER USERNAME PASSWORD DOMAIN
+EOF
+exit 1;
+fi
+
+SERVER=$1
+USERNAME=$2
+PASSWORD=$3
+DOMAIN=$4
+shift 4
+failed=0
+
+samba4bindir="$BUILDDIR/bin"
+gentest="$samba4bindir/gentest$EXEEXT"
+
+. `dirname $0`/../../../testprogs/blackbox/subunit.sh
+
+cat <<EOF > st/gentest.ignore
+all_info.out.fname
+internal_information.out.file_id
+EOF
+
+testit "gentest" $VALGRIND $gentest //$SERVER/test1 //$SERVER/test2 --num-ops=100 --ignore=st/gentest.ignore -W "$DOMAIN" -U"$USERNAME%$PASSWORD" -U"$USERNAME%$PASSWORD" $@ || failed=`expr $failed + 1`
+
+rm -f st/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 0000000000..a8f11ee0ba
--- /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 <<EOF
+Usage: test_locktest.sh SERVER USERNAME PASSWORD DOMAIN PREFIX
+EOF
+exit 1;
+fi
+
+SERVER=$1
+USERNAME=$2
+PASSWORD=$3
+DOMAIN=$4
+PREFIX=$5
+shift 5
+failed=0
+
+samba4bindir="$BUILDDIR/bin"
+locktest="$samba4bindir/locktest$EXEEXT"
+
+. `dirname $0`/../../../testprogs/blackbox/subunit.sh
+
+testit "locktest" $VALGRIND $locktest //$SERVER/test1 //$SERVER/test2 --num-ops=100 -W "$DOMAIN" -U"$USERNAME%$PASSWORD" $@ || failed=`expr $failed + 1`
+
+exit $failed
diff --git a/source4/torture/tests/test_masktest.sh b/source4/torture/tests/test_masktest.sh
new file mode 100755
index 0000000000..e4fa65ccfb
--- /dev/null
+++ b/source4/torture/tests/test_masktest.sh
@@ -0,0 +1,28 @@
+#!/bin/sh
+# Blackbox tests for masktest
+# Copyright (C) 2008 Andrew Tridgell
+# based on test_smbclient.sh
+
+if [ $# -lt 5 ]; then
+cat <<EOF
+Usage: test_masktest.sh SERVER USERNAME PASSWORD DOMAIN PREFIX
+EOF
+exit 1;
+fi
+
+SERVER=$1
+USERNAME=$2
+PASSWORD=$3
+DOMAIN=$4
+PREFIX=$5
+shift 5
+failed=0
+
+samba4bindir="$BUILDDIR/bin"
+masktest="$samba4bindir/masktest$EXEEXT"
+
+. `dirname $0`/../../../testprogs/blackbox/subunit.sh
+
+testit "masktest" $VALGRIND $masktest //$SERVER/tmp --num-ops=200 --dieonerror -W "$DOMAIN" -U"$USERNAME%$PASSWORD" $@ || failed=`expr $failed + 1`
+
+exit $failed
diff --git a/source4/torture/torture.c b/source4/torture/torture.c
new file mode 100644
index 0000000000..deae65c265
--- /dev/null
+++ b/source4/torture/torture.c
@@ -0,0 +1,73 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/time.h"
+#include "torture/torture.h"
+#include "../lib/util/dlinklist.h"
+#include "param/param.h"
+#include "lib/cmdline/popt_common.h"
+#include "torture/smbtorture.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(struct torture_suite *suite)
+{
+ if (!suite)
+ return true;
+
+ if (torture_root == NULL)
+ torture_root = talloc_zero(talloc_autofree_context(), struct torture_suite);
+
+ return torture_suite_add_suite(torture_root, suite);
+}
+
+
+_PUBLIC_ int torture_init(void)
+{
+ extern NTSTATUS torture_base_init(void);
+ extern NTSTATUS torture_ldap_init(void);
+ extern NTSTATUS torture_ldb_init(void);
+ extern NTSTATUS torture_local_init(void);
+ extern NTSTATUS torture_nbt_init(void);
+ extern NTSTATUS torture_nbench_init(void);
+ extern NTSTATUS torture_rap_init(void);
+ extern NTSTATUS torture_rpc_init(void);
+ extern NTSTATUS torture_smb2_init(void);
+ extern NTSTATUS torture_net_init(void);
+ extern NTSTATUS torture_raw_init(void);
+ extern NTSTATUS torture_unix_init(void);
+ extern NTSTATUS torture_winbind_init(void);
+ init_module_fn static_init[] = { STATIC_smbtorture_MODULES };
+ init_module_fn *shared_init = load_samba_modules(NULL, cmdline_lp_ctx, "smbtorture");
+
+ run_init_functions(static_init);
+ run_init_functions(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 0000000000..661e337270
--- /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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/smbtorture.h"
+#include "torture/unix/proto.h"
+
+NTSTATUS torture_unix_init(void)
+{
+ struct torture_suite *suite =
+ torture_suite_create(talloc_autofree_context(), "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(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 0000000000..30fe912234
--- /dev/null
+++ b/source4/torture/unix/unix_info2.c
@@ -0,0 +1,469 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/libcli.h"
+#include "libcli/raw/interfaces.h"
+#include "libcli/raw/raw_proto.h"
+#include "torture/torture.h"
+#include "torture/util.h"
+#include "torture/basic/proto.h"
+#include "lib/cmdline/popt_common.h"
+#include "auth/credentials/credentials.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;
+
+ lp_smbcli_options(tctx->lp_ctx, &options);
+ lp_smbcli_session_options(tctx->lp_ctx, &session_options);
+
+ status = smbcli_full_connection(tctx, &cli, host,
+ lp_smb_ports(tctx->lp_ctx),
+ share, NULL, lp_socket_options(tctx->lp_ctx),
+ cmdline_credentials,
+ lp_resolve_context(tctx->lp_ctx),
+ tctx->ev, &options, &session_options,
+ lp_iconv_convenience(tctx->lp_ctx),
+ lp_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));
+ return NULL;
+ }
+
+ 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");
+ torture_assert_int_equal(torture, search.t2ffirst.out.end_of_search, 1,
+ "expected end_of_search to be true");
+
+ 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 = (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 0000000000..b72b9fcb09
--- /dev/null
+++ b/source4/torture/unix/whoami.c
@@ -0,0 +1,339 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/libcli.h"
+#include "libcli/raw/interfaces.h"
+#include "libcli/raw/raw_proto.h"
+#include "torture/torture.h"
+#include "torture/basic/proto.h"
+#include "lib/cmdline/popt_common.h"
+#include "auth/credentials/credentials.h"
+#include "param/param.h"
+#include "libcli/resolve/resolve.h"
+
+/* Size (in bytes) of the required fields in the SMBwhoami response. */
+#define WHOAMI_REQUIRED_SIZE 40
+
+enum smb_whoami_flags {
+ SMB_WHOAMI_GUEST = 0x1 /* Logged in as (or squashed to) guest */
+};
+
+/*
+ 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)
+ 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;
+
+ lp_smbcli_options(tctx->lp_ctx, &options);
+ lp_smbcli_session_options(tctx->lp_ctx, &session_options);
+
+ status = smbcli_full_connection(tctx, &cli, host,
+ lp_smb_ports(tctx->lp_ctx),
+ share, NULL, lp_socket_options(tctx->lp_ctx),
+ creds, lp_resolve_context(tctx->lp_ctx),
+ tctx->ev, &options, &session_options,
+ lp_iconv_convenience(tctx->lp_ctx),
+ lp_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));
+ return NULL;
+ }
+
+ return cli;
+}
+
+static bool 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");
+
+ for (i = 0; i < whoami->num_gids; ++i) {
+ whoami->gid_list[i] = BVAL(tp.out.data.data, offset);
+ offset += 8;
+ }
+ }
+
+ /* 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");
+
+ for (i = 0; i < whoami->num_sids; ++i) {
+ if (!sid_parse(mem_ctx, torture,
+ &tp.out.data, &offset,
+ &whoami->sid_list[i])) {
+ return false;
+ }
+
+ }
+ }
+
+ /* 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;
+}
+
+bool torture_unix_whoami(struct torture_context *torture)
+{
+ struct smbcli_state *cli;
+ struct cli_credentials *anon_credentials;
+ struct smb_whoami whoami;
+
+ if (!(cli = connect_to_server(torture, cmdline_credentials))) {
+ return false;
+ }
+
+ /* Test basic authenticated mapping. */
+ printf("calling SMB_QFS_POSIX_WHOAMI on an authenticated connection\n");
+ if (!smb_raw_query_posix_whoami(torture, torture,
+ cli, &whoami, 0xFFFF)) {
+ smbcli_tdis(cli);
+ return false;
+ }
+
+ /* Test that the server drops the UID and GID list. */
+ printf("calling SMB_QFS_POSIX_WHOAMI with a small buffer\n");
+ if (!smb_raw_query_posix_whoami(torture, torture,
+ cli, &whoami, 0x40)) {
+ smbcli_tdis(cli);
+ return false;
+ }
+
+ 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);
+
+ printf("calling SMB_QFS_POSIX_WHOAMI on an anonymous connection\n");
+ anon_credentials = cli_credentials_init_anon(torture);
+
+ if (!(cli = connect_to_server(torture, anon_credentials))) {
+ return false;
+ }
+
+ if (!smb_raw_query_posix_whoami(torture, torture,
+ cli, &whoami, 0xFFFF)) {
+ smbcli_tdis(cli);
+ return false;
+ }
+
+ smbcli_tdis(cli);
+
+ /* 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) {
+ printf("checking whether we were logged in as guest... %s\n",
+ whoami.mapping_flags & SMB_WHOAMI_GUEST ? "YES" : "NO");
+ torture_assert(torture, whoami.mapping_flags & SMB_WHOAMI_GUEST,
+ "anonymous login did not map to guest");
+ } else {
+ printf("server does not support SMB_WHOAMI_GUEST flag\n");
+ }
+
+ return true;
+}
+
+/* vim: set sts=8 sw=8 : */
diff --git a/source4/torture/util.h b/source4/torture/util.h
new file mode 100644
index 0000000000..6a8ae36baf
--- /dev/null
+++ b/source4/torture/util.h
@@ -0,0 +1,97 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Jelmer Vernooij <jelmer@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _TORTURE_UTIL_H_
+#define _TORTURE_UTIL_H_
+
+#include "lib/torture/torture.h"
+
+struct smbcli_state;
+struct smbcli_tree;
+
+/**
+ 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);
+void *shm_setup(int size);
+
+/**
+ 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);
+
+
+
+#endif /* _TORTURE_UTIL_H_ */
diff --git a/source4/torture/util_smb.c b/source4/torture/util_smb.c
new file mode 100644
index 0000000000..1c50694279
--- /dev/null
+++ b/source4/torture/util_smb.c
@@ -0,0 +1,911 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/cmdline/popt_common.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/raw/ioctl.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 "auth/credentials/credentials.h"
+#include "libcli/resolve/resolve.h"
+#include "param/param.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 = 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;
+ 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, and are also
+ in different DST zones*/
+ setfile.generic.level = RAW_SFILEINFO_SETATTRE;
+ setfile.generic.in.file.fnum = fnum;
+
+ setfile.setattre.in.create_time = t + 9*30*24*60*60;
+ setfile.setattre.in.access_time = t + 6*30*24*60*60;
+ setfile.setattre.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_GETATTRE;
+ 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.setattre.in.create_time != fileinfo.getattre.out.create_time) {
+ printf("create_time not setup correctly\n");
+ }
+ if (setfile.setattre.in.access_time != fileinfo.getattre.out.access_time) {
+ printf("access_time not setup correctly\n");
+ }
+ if (setfile.setattre.in.write_time != fileinfo.getattre.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, and are also
+ in different DST zones*/
+ setfile.generic.level = RAW_SFILEINFO_SETATTRE;
+ setfile.generic.in.file.fnum = fnum;
+
+ setfile.setattre.in.create_time = t + 9*30*24*60*60;
+ setfile.setattre.in.access_time = t + 6*30*24*60*60;
+ setfile.setattre.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_GETATTRE;
+ 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.setattre.in.create_time != fileinfo.getattre.out.create_time) {
+ printf("create_time not setup correctly\n");
+ }
+ if (setfile.setattre.in.access_time != fileinfo.getattre.out.access_time) {
+ printf("access_time not setup correctly\n");
+ }
+ if (setfile.setattre.in.write_time != fileinfo.getattre.out.write_time) {
+ printf("write_time not setup correctly\n");
+ }
+
+ return fnum;
+}
+
+
+
+/* return a pointer to a anonymous shared memory segment of size "size"
+ which will persist across fork() but will disappear when all processes
+ exit
+
+ The memory is not zeroed
+
+ This function uses system5 shared memory. It takes advantage of a property
+ that the memory is not destroyed if it is attached when the id is removed
+ */
+void *shm_setup(int size)
+{
+ int shmid;
+ void *ret;
+
+ shmid = shmget(IPC_PRIVATE, size, SHM_R | SHM_W);
+ if (shmid == -1) {
+ printf("can't get shared memory\n");
+ exit(1);
+ }
+ ret = (void *)shmat(shmid, 0, 0);
+ if (!ret || ret == (void *)-1) {
+ printf("can't attach to shared memory\n");
+ return NULL;
+ }
+ /* the following releases the ipc, but note that this process
+ and all its children will still have access to the memory, its
+ just that the shmid is no longer valid for other shm calls. This
+ means we don't leave behind lots of shm segments after we exit
+
+ See Stevens "advanced programming in unix env" for details
+ */
+ shmctl(shmid, IPC_RMID, 0);
+
+ return ret;
+}
+
+
+/**
+ 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 infor 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;
+
+ lp_smbcli_options(tctx->lp_ctx, &options);
+ lp_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,
+ lp_smb_ports(tctx->lp_ctx),
+ sharename, NULL,
+ lp_socket_options(tctx->lp_ctx),
+ cmdline_credentials,
+ lp_resolve_context(tctx->lp_ctx),
+ ev, &options, &session_options,
+ lp_iconv_convenience(tctx->lp_ctx),
+ lp_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;
+ }
+
+ if (!smbcli_parse_unc(unc_list[conn_index % num_unc_names],
+ 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)
+{
+}
+
+double torture_create_procs(struct torture_context *tctx,
+ bool (*fn)(struct torture_context *, struct smbcli_state *, int), bool *result)
+{
+ int i, status;
+ volatile pid_t *child_status;
+ volatile bool *child_status_out;
+ int synccount;
+ int tries = 8;
+ int 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 = (volatile pid_t *)shm_setup(sizeof(pid_t)*torture_nprocs);
+ if (!child_status) {
+ printf("Failed to setup shared memory\n");
+ return -1;
+ }
+
+ child_status_out = (volatile bool *)shm_setup(sizeof(bool)*torture_nprocs);
+ if (!child_status_out) {
+ printf("Failed to setup result status shared memory\n");
+ return -1;
+ }
+
+ for (i = 0; i < torture_nprocs; i++) {
+ child_status[i] = 0;
+ child_status_out[i] = true;
+ }
+
+ tv = timeval_current();
+
+ for (i=0;i<torture_nprocs;i++) {
+ procnum = i;
+ if (fork() == 0) {
+ char *myname;
+
+ pid_t mypid = getpid();
+ srandom(((int)mypid) ^ ((int)time(NULL)));
+
+ if (asprintf(&myname, "CLIENT%d", i) == -1) {
+ printf("asprintf failed\n");
+ return -1;
+ }
+ lp_set_cmdline(tctx->lp_ctx, "netbios name", myname);
+ free(myname);
+
+
+ while (1) {
+ if (torture_open_connection(&current_cli, tctx, i)) {
+ break;
+ }
+ if (tries-- == 0) {
+ printf("pid %d failed to start\n", (int)getpid());
+ _exit(1);
+ }
+ msleep(100);
+ }
+
+ child_status[i] = getpid();
+
+ pause();
+
+ if (child_status[i]) {
+ printf("Child %d failed to start!\n", i);
+ child_status_out[i] = 1;
+ _exit(1);
+ }
+
+ child_status_out[i] = fn(tctx, current_cli, i);
+ _exit(0);
+ }
+ }
+
+ do {
+ synccount = 0;
+ for (i=0;i<torture_nprocs;i++) {
+ if (child_status[i]) synccount++;
+ }
+ if (synccount == torture_nprocs) break;
+ msleep(100);
+ } while (timeval_elapsed(&tv) < start_time_limit);
+
+ if (synccount != torture_nprocs) {
+ printf("FAILED TO START %d CLIENTS (started %d)\n", torture_nprocs, synccount);
+ *result = false;
+ return timeval_elapsed(&tv);
+ }
+
+ printf("Starting %d clients\n", torture_nprocs);
+
+ /* start the client load */
+ tv = timeval_current();
+ for (i=0;i<torture_nprocs;i++) {
+ child_status[i] = 0;
+ }
+
+ printf("%d clients started\n", torture_nprocs);
+
+ kill(0, SIGCONT);
+
+ for (i=0;i<torture_nprocs;i++) {
+ int ret;
+ while ((ret=waitpid(0, &status, 0)) == -1 && errno == EINTR) /* noop */ ;
+ if (ret == -1 || WEXITSTATUS(status) != 0) {
+ *result = false;
+ }
+ }
+
+ printf("\n");
+
+ for (i=0;i<torture_nprocs;i++) {
+ if (!child_status_out[i]) {
+ *result = false;
+ }
+ }
+ return timeval_elapsed(&tv);
+}
+
+static bool wrap_smb_multi_test(struct torture_context *torture,
+ struct torture_tcase *tcase,
+ struct torture_test *test)
+{
+ bool (*fn)(struct torture_context *, struct smbcli_state *, int ) = test->fn;
+ 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, struct torture_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;
+
+ struct smbcli_state *cli1, *cli2;
+
+ if (!torture_open_connection(&cli1, torture_ctx, 0) ||
+ !torture_open_connection(&cli2, torture_ctx, 1))
+ return false;
+
+ fn = test->fn;
+
+ ret = fn(torture_ctx, cli1, cli2);
+
+ 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, struct torture_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;
+
+ struct smbcli_state *cli1;
+
+ if (!torture_open_connection(&cli1, torture_ctx, 0))
+ return false;
+
+ fn = test->fn;
+
+ ret = fn(torture_ctx, cli1);
+
+ 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, struct torture_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 = 0;
+
+ /* 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;
+ *res = talloc_steal(mem_ctx, result);
+ talloc_free(tmp_ctx);
+ return NT_STATUS_OK;
+}
diff --git a/source4/torture/winbind/config.mk b/source4/torture/winbind/config.mk
new file mode 100644
index 0000000000..d2c57e9c97
--- /dev/null
+++ b/source4/torture/winbind/config.mk
@@ -0,0 +1,16 @@
+
+#################################
+# Start SUBSYSTEM TORTURE_WINBIND
+[MODULE::TORTURE_WINBIND]
+SUBSYSTEM = smbtorture
+OUTPUT_TYPE = MERGED_OBJ
+INIT_FUNCTION = torture_winbind_init
+PRIVATE_DEPENDENCIES = \
+ LIBWINBIND-CLIENT torture PAM_ERRORS
+# End SUBSYSTEM TORTURE_WINBIND
+#################################
+
+TORTURE_WINBIND_OBJ_FILES = $(addprefix $(torturesrcdir)/winbind/, winbind.o struct_based.o)
+
+$(eval $(call proto_header_template,$(torturesrcdir)/winbind/proto.h,$(TORTURE_WINBIND_OBJ_FILES:.o=.c)))
+
diff --git a/source4/torture/winbind/struct_based.c b/source4/torture/winbind/struct_based.c
new file mode 100644
index 0000000000..676b85e467
--- /dev/null
+++ b/source4/torture/winbind/struct_based.c
@@ -0,0 +1,1046 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "torture/winbind/proto.h"
+#include "nsswitch/winbind_client.h"
+#include "libcli/security/security.h"
+#include "librpc/gen_ndr/netlogon.h"
+#include "param/param.h"
+#include "auth/ntlm/pam_errors.h"
+
+#define DO_STRUCT_REQ_REP_EXT(op,req,rep,expected,strict,warnaction,cmt) do { \
+ NSS_STATUS __got, __expected = (expected); \
+ __got = winbindd_request_response(op, req, rep); \
+ if (__got != __expected) { \
+ const char *__cmt = (cmt); \
+ if (strict) { \
+ torture_result(torture, TORTURE_FAIL, \
+ __location__ ": " __STRING(op) \
+ " returned %d, expected %d%s%s", \
+ __got, __expected, \
+ (__cmt) ? ": " : "", \
+ (__cmt) ? (__cmt) : ""); \
+ return false; \
+ } else { \
+ torture_warning(torture, \
+ __location__ ": " __STRING(op) \
+ " returned %d, expected %d%s%s", \
+ __got, __expected, \
+ (__cmt) ? ": " : "", \
+ (__cmt) ? (__cmt) : ""); \
+ warnaction; \
+ } \
+ } \
+} while(0)
+
+#define DO_STRUCT_REQ_REP(op,req,rep) do { \
+ bool __noop = false; \
+ DO_STRUCT_REQ_REP_EXT(op,req,rep,NSS_STATUS_SUCCESS,true,__noop=true,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",
+ lp_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",
+ lp_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",
+ lp_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;
+ if (!extra_data) {
+ return true;
+ }
+
+ torture_assert(torture, extra_data, "NULL trust list");
+
+ 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");
+
+ *_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++) {
+ 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);
+
+ 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");
+
+ 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, "SID's doesn't match");
+
+ 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 '%s' => '%s' [%s]\n",
+ rep.data.domain_info.name,
+ rep.data.domain_info.alt_name,
+ flagstr);
+ }
+
+ return true;
+}
+
+static bool torture_winbind_struct_getdcname(struct torture_context *torture)
+{
+ bool ok;
+ bool strict = torture_setting_bool(torture, "strict mode", false);
+ 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;
+
+ 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 group 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, 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;
+ 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);
+
+ *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, &groups);
+ torture_assert(torture, ok, "failed to get group list");
+
+ for (count = 0; groups[count]; count++) { }
+
+ 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 recieved 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);
+
+ DO_STRUCT_REQ_REP(WINBINDD_SHOW_SEQUENCE, &req, &rep);
+
+ 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;
+ DO_STRUCT_REQ_REP(WINBINDD_GETPWENT, &req, &rep);
+ pwent = (struct winbindd_pw *)rep.extra_data.data;
+ torture_assert(torture, (pwent != NULL), "NULL 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;
+
+ 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;
+ strupper_m(domain);
+
+ 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;
+
+ 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);
+
+ 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, const char **list)
+{
+ uint32_t count;
+
+ for (count = 0; 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;
+ const char *invalid_user = "noone";
+ char *invalid_name;
+ bool strict = torture_setting_bool(torture, "strict mode", false);
+ char **users;
+ char **groups;
+ uint32_t count;
+ 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, &groups);
+ torture_assert(torture, ok, "failed to retrieve list of groups");
+ 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, (const char **)users) ||
+ name_is_in_list(invalid_name, (const char **)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;
+}
+
+struct torture_suite *torture_winbind_struct_init(void)
+{
+ struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "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);
+
+ 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 0000000000..b12e92552e
--- /dev/null
+++ b/source4/torture/winbind/winbind.c
@@ -0,0 +1,35 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB torture tester
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/smbtorture.h"
+#include "torture/winbind/proto.h"
+
+NTSTATUS torture_winbind_init(void)
+{
+ struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "WINBIND");
+
+ torture_suite_add_suite(suite, torture_winbind_struct_init());
+
+ suite->description = talloc_strdup(suite, "WINBIND tests");
+
+ torture_register_suite(suite);
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/utils/ad2oLschema.c b/source4/utils/ad2oLschema.c
new file mode 100644
index 0000000000..236b1fa350
--- /dev/null
+++ b/source4/utils/ad2oLschema.c
@@ -0,0 +1,442 @@
+/*
+ ldb database library
+
+ Copyright (C) Andrew Bartlett 2006-2008
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ad2oLschema
+ *
+ * Description: utility to convert an AD schema into the format required by OpenLDAP
+ *
+ * Author: Andrew Bartlett
+ */
+
+#include "includes.h"
+#include "ldb.h"
+#include "system/locale.h"
+#include "lib/ldb/tools/cmdline.h"
+#include "param/param.h"
+#include "lib/cmdline/popt_common.h"
+#include "dsdb/samdb/samdb.h"
+
+struct schema_conv {
+ int count;
+ int skipped;
+ int failures;
+};
+
+
+static void usage(void)
+{
+ printf("Usage: ad2oLschema <options>\n");
+ printf("\nConvert AD-like LDIF to OpenLDAP schema format\n\n");
+ printf("Options:\n");
+ printf(" -I inputfile inputfile of mapped OIDs and skipped attributes/ObjectClasses");
+ printf(" -H url LDB or LDAP server to read schmea from\n");
+ printf(" -O outputfile outputfile otherwise STDOUT\n");
+ printf(" -o options pass options like modules to activate\n");
+ printf(" e.g: -o modules:timestamps\n");
+ printf("\n");
+ printf("Converts records from an AD-like LDIF schema into an openLdap formatted schema\n\n");
+ exit(1);
+}
+
+static struct ldb_dn *find_schema_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx)
+{
+ const char *rootdse_attrs[] = {"schemaNamingContext", NULL};
+ struct ldb_dn *schemadn;
+ struct ldb_dn *basedn = ldb_dn_new(mem_ctx, ldb, NULL);
+ struct ldb_result *rootdse_res;
+ struct ldb_result *schema_res;
+ int ldb_ret;
+
+ if (!basedn) {
+ return NULL;
+ }
+
+ /* Search for rootdse */
+ ldb_ret = ldb_search(ldb, mem_ctx, &rootdse_res,
+ basedn, LDB_SCOPE_BASE, rootdse_attrs, NULL);
+ if (ldb_ret != LDB_SUCCESS) {
+ ldb_ret = ldb_search(ldb, mem_ctx, &schema_res, basedn, LDB_SCOPE_SUBTREE,
+ NULL, "(&(objectClass=dMD)(cn=Schema))");
+ if (ldb_ret) {
+ printf("cn=Schema Search failed: %s\n", ldb_errstring(ldb));
+ return NULL;
+ }
+
+ if (schema_res->count != 1) {
+ talloc_free(schema_res);
+ printf("Failed to find rootDSE");
+ return NULL;
+ }
+
+ schemadn = talloc_steal(mem_ctx, schema_res->msgs[0]->dn);
+ talloc_free(schema_res);
+ return schemadn;
+ }
+
+ if (rootdse_res->count != 1) {
+ printf("Failed to find rootDSE");
+ talloc_free(rootdse_res);
+ return NULL;
+ }
+
+ /* Locate schema */
+ schemadn = ldb_msg_find_attr_as_dn(ldb, mem_ctx, rootdse_res->msgs[0], "schemaNamingContext");
+ talloc_free(rootdse_res);
+
+ if (!schemadn) {
+ return NULL;
+ }
+
+ return schemadn;
+}
+
+
+static struct schema_conv process_convert(struct ldb_context *ldb, enum dsdb_schema_convert_target target, FILE *in, FILE *out)
+{
+ /* Read list of attributes to skip, OIDs to map */
+ TALLOC_CTX *mem_ctx = talloc_new(ldb);
+ char *line;
+ const char **attrs_skip = NULL;
+ int num_skip = 0;
+ struct oid_map {
+ char *old_oid;
+ char *new_oid;
+ } *oid_map = NULL;
+ int num_oid_maps = 0;
+ struct attr_map {
+ char *old_attr;
+ char *new_attr;
+ } *attr_map = NULL;
+ int num_attr_maps = 0;
+ struct dsdb_class *objectclass;
+ struct dsdb_attribute *attribute;
+ struct ldb_dn *schemadn;
+ struct schema_conv ret;
+ struct dsdb_schema *schema;
+ const char *seperator;
+ char *error_string;
+
+ int ldb_ret;
+
+ ret.count = 0;
+ ret.skipped = 0;
+ ret.failures = 0;
+
+ while ((line = afdgets(fileno(in), mem_ctx, 0))) {
+ /* Blank Line */
+ if (line[0] == '\0') {
+ continue;
+ }
+ /* Comment */
+ if (line[0] == '#') {
+ continue;
+ }
+ if (isdigit(line[0])) {
+ char *p = strchr(line, ':');
+ if (!p) {
+ ret.failures++;
+ return ret;
+ }
+ p[0] = '\0';
+ p++;
+ oid_map = talloc_realloc(mem_ctx, oid_map, struct oid_map, num_oid_maps + 2);
+ trim_string(line, " ", " ");
+ oid_map[num_oid_maps].old_oid = talloc_move(oid_map, &line);
+ trim_string(p, " ", " ");
+ oid_map[num_oid_maps].new_oid = p;
+ num_oid_maps++;
+ oid_map[num_oid_maps].old_oid = NULL;
+ } else {
+ char *p = strchr(line, ':');
+ if (p) {
+ /* remap attribute/objectClass */
+ p[0] = '\0';
+ p++;
+ attr_map = talloc_realloc(mem_ctx, attr_map, struct attr_map, num_attr_maps + 2);
+ trim_string(line, " ", " ");
+ attr_map[num_attr_maps].old_attr = talloc_move(attr_map, &line);
+ trim_string(p, " ", " ");
+ attr_map[num_attr_maps].new_attr = p;
+ num_attr_maps++;
+ attr_map[num_attr_maps].old_attr = NULL;
+ } else {
+ /* skip attribute/objectClass */
+ attrs_skip = talloc_realloc(mem_ctx, attrs_skip, const char *, num_skip + 2);
+ trim_string(line, " ", " ");
+ attrs_skip[num_skip] = talloc_move(attrs_skip, &line);
+ num_skip++;
+ attrs_skip[num_skip] = NULL;
+ }
+ }
+ }
+
+ schemadn = find_schema_dn(ldb, mem_ctx);
+ if (!schemadn) {
+ printf("Failed to find schema DN: %s\n", ldb_errstring(ldb));
+ ret.failures = 1;
+ return ret;
+ }
+
+ ldb_ret = dsdb_schema_from_schema_dn(mem_ctx, ldb,
+ lp_iconv_convenience(cmdline_lp_ctx),
+ schemadn, &schema, &error_string);
+ if (ldb_ret != LDB_SUCCESS) {
+ printf("Failed to load schema: %s\n", error_string);
+ ret.failures = 1;
+ return ret;
+ }
+
+ switch (target) {
+ case TARGET_OPENLDAP:
+ seperator = "\n ";
+ break;
+ case TARGET_FEDORA_DS:
+ seperator = "\n ";
+ fprintf(out, "dn: cn=schema\n");
+ break;
+ }
+
+ for (attribute=schema->attributes; attribute; attribute = attribute->next) {
+ const char *name = attribute->lDAPDisplayName;
+ const char *oid = attribute->attributeID_oid;
+ const char *syntax = attribute->attributeSyntax_oid;
+ const char *equality = NULL, *substring = NULL;
+ bool single_value = attribute->isSingleValued;
+
+ char *schema_entry = NULL;
+ int j;
+
+ /* We have been asked to skip some attributes/objectClasses */
+ if (attrs_skip && str_list_check_ci(attrs_skip, name)) {
+ ret.skipped++;
+ continue;
+ }
+
+ /* We might have been asked to remap this oid, due to a conflict */
+ for (j=0; oid && oid_map && oid_map[j].old_oid; j++) {
+ if (strcasecmp(oid, oid_map[j].old_oid) == 0) {
+ oid = oid_map[j].new_oid;
+ break;
+ }
+ }
+
+ if (attribute->syntax) {
+ /* We might have been asked to remap this oid,
+ * due to a conflict, or lack of
+ * implementation */
+ syntax = attribute->syntax->ldap_oid;
+ /* We might have been asked to remap this oid, due to a conflict */
+ for (j=0; syntax && oid_map && oid_map[j].old_oid; j++) {
+ if (strcasecmp(syntax, oid_map[j].old_oid) == 0) {
+ syntax = oid_map[j].new_oid;
+ break;
+ }
+ }
+
+ equality = attribute->syntax->equality;
+ substring = attribute->syntax->substring;
+ }
+
+ /* We might have been asked to remap this name, due to a conflict */
+ for (j=0; name && attr_map && attr_map[j].old_attr; j++) {
+ if (strcasecmp(name, attr_map[j].old_attr) == 0) {
+ name = attr_map[j].new_attr;
+ break;
+ }
+ }
+
+ schema_entry = schema_attribute_description(mem_ctx,
+ target,
+ seperator,
+ oid,
+ name,
+ equality,
+ substring,
+ syntax,
+ single_value,
+ false,
+ NULL, NULL,
+ NULL, NULL,
+ false, false);
+
+ if (schema_entry == NULL) {
+ ret.failures++;
+ return ret;
+ }
+
+ switch (target) {
+ case TARGET_OPENLDAP:
+ fprintf(out, "attributetype %s\n\n", schema_entry);
+ break;
+ case TARGET_FEDORA_DS:
+ fprintf(out, "attributeTypes: %s\n", schema_entry);
+ break;
+ }
+ ret.count++;
+ }
+
+ /* This is already sorted to have 'top' and similar classes first */
+ for (objectclass=schema->classes; objectclass; objectclass = objectclass->next) {
+ const char *name = objectclass->lDAPDisplayName;
+ const char *oid = objectclass->governsID_oid;
+ const char *subClassOf = objectclass->subClassOf;
+ int objectClassCategory = objectclass->objectClassCategory;
+ const char **must;
+ const char **may;
+ char *schema_entry = NULL;
+ const char *objectclass_name_as_list[] = {
+ objectclass->lDAPDisplayName,
+ NULL
+ };
+ int j;
+ int attr_idx;
+
+ /* We have been asked to skip some attributes/objectClasses */
+ if (attrs_skip && str_list_check_ci(attrs_skip, name)) {
+ ret.skipped++;
+ continue;
+ }
+
+ /* We might have been asked to remap this oid, due to a conflict */
+ for (j=0; oid_map && oid_map[j].old_oid; j++) {
+ if (strcasecmp(oid, oid_map[j].old_oid) == 0) {
+ oid = oid_map[j].new_oid;
+ break;
+ }
+ }
+
+ /* We might have been asked to remap this name, due to a conflict */
+ for (j=0; name && attr_map && attr_map[j].old_attr; j++) {
+ if (strcasecmp(name, attr_map[j].old_attr) == 0) {
+ name = attr_map[j].new_attr;
+ break;
+ }
+ }
+
+ may = dsdb_full_attribute_list(mem_ctx, schema, objectclass_name_as_list, DSDB_SCHEMA_ALL_MAY);
+
+ for (j=0; may && may[j]; j++) {
+ /* We might have been asked to remap this name, due to a conflict */
+ for (attr_idx=0; attr_map && attr_map[attr_idx].old_attr; attr_idx++) {
+ if (strcasecmp(may[j], attr_map[attr_idx].old_attr) == 0) {
+ may[j] = attr_map[attr_idx].new_attr;
+ break;
+ }
+ }
+ }
+
+ must = dsdb_full_attribute_list(mem_ctx, schema, objectclass_name_as_list, DSDB_SCHEMA_ALL_MUST);
+
+ for (j=0; must && must[j]; j++) {
+ /* We might have been asked to remap this name, due to a conflict */
+ for (attr_idx=0; attr_map && attr_map[attr_idx].old_attr; attr_idx++) {
+ if (strcasecmp(must[j], attr_map[attr_idx].old_attr) == 0) {
+ must[j] = attr_map[attr_idx].new_attr;
+ break;
+ }
+ }
+ }
+
+ schema_entry = schema_class_description(mem_ctx, target,
+ seperator,
+ oid,
+ name,
+ NULL,
+ subClassOf,
+ objectClassCategory,
+ must,
+ may,
+ NULL);
+ if (schema_entry == NULL) {
+ ret.failures++;
+ return ret;
+ }
+
+ switch (target) {
+ case TARGET_OPENLDAP:
+ fprintf(out, "objectclass %s\n\n", schema_entry);
+ break;
+ case TARGET_FEDORA_DS:
+ fprintf(out, "objectClasses: %s\n", schema_entry);
+ break;
+ }
+ ret.count++;
+ }
+
+ return ret;
+}
+
+ int main(int argc, const char **argv)
+{
+ TALLOC_CTX *ctx;
+ struct ldb_cmdline *options;
+ FILE *in = stdin;
+ FILE *out = stdout;
+ struct ldb_context *ldb;
+ struct schema_conv ret;
+ const char *target_str;
+ enum dsdb_schema_convert_target target;
+
+ ctx = talloc_new(NULL);
+ ldb = ldb_init(ctx, NULL);
+
+ options = ldb_cmdline_process(ldb, argc, argv, usage);
+
+ if (options->input) {
+ in = fopen(options->input, "r");
+ if (!in) {
+ perror(options->input);
+ exit(1);
+ }
+ }
+ if (options->output) {
+ out = fopen(options->output, "w");
+ if (!out) {
+ perror(options->output);
+ exit(1);
+ }
+ }
+
+ target_str = lp_parm_string(cmdline_lp_ctx, NULL, "convert", "target");
+
+ if (!target_str || strcasecmp(target_str, "openldap") == 0) {
+ target = TARGET_OPENLDAP;
+ } else if (strcasecmp(target_str, "fedora-ds") == 0) {
+ target = TARGET_FEDORA_DS;
+ } else {
+ printf("Unsupported target: %s\n", target_str);
+ exit(1);
+ }
+
+ ret = process_convert(ldb, target, in, out);
+
+ fclose(in);
+ fclose(out);
+
+ printf("Converted %d records (skipped %d) with %d failures\n", ret.count, ret.skipped, ret.failures);
+
+ return 0;
+}
diff --git a/source4/utils/config.mk b/source4/utils/config.mk
new file mode 100644
index 0000000000..ea8e00ff8a
--- /dev/null
+++ b/source4/utils/config.mk
@@ -0,0 +1,107 @@
+# utils subsystem
+
+#################################
+# Start BINARY ntlm_auth
+[BINARY::ntlm_auth]
+INSTALLDIR = BINDIR
+PRIVATE_DEPENDENCIES = \
+ LIBSAMBA-HOSTCONFIG \
+ LIBSAMBA-UTIL \
+ LIBPOPT \
+ POPT_SAMBA \
+ POPT_CREDENTIALS \
+ gensec \
+ LIBCLI_RESOLVE \
+ auth \
+ ntlm_check \
+ MESSAGING \
+ LIBEVENTS
+# End BINARY ntlm_auth
+#################################
+
+ntlm_auth_OBJ_FILES = $(utilssrcdir)/ntlm_auth.o
+
+MANPAGES += $(utilssrcdir)/man/ntlm_auth.1
+
+#################################
+# Start BINARY getntacl
+[BINARY::getntacl]
+INSTALLDIR = BINDIR
+PRIVATE_DEPENDENCIES = \
+ LIBSAMBA-HOSTCONFIG \
+ LIBSAMBA-UTIL \
+ NDR_XATTR \
+ WRAP_XATTR \
+ LIBSAMBA-ERRORS
+
+getntacl_OBJ_FILES = $(utilssrcdir)/getntacl.o
+
+# End BINARY getntacl
+#################################
+
+MANPAGES += $(utilssrcdir)/man/getntacl.1
+
+#################################
+# Start BINARY setntacl
+[BINARY::setntacl]
+# disabled until rewritten
+#INSTALLDIR = BINDIR
+# End BINARY setntacl
+#################################
+
+setntacl_OBJ_FILES = $(utilssrcdir)/setntacl.o
+
+#################################
+# Start BINARY setnttoken
+[BINARY::setnttoken]
+INSTALLDIR = BINDIR
+PRIVATE_DEPENDENCIES =
+# End BINARY setnttoken
+#################################
+
+setnttoken_OBJ_FILES = $(utilssrcdir)/setnttoken.o
+
+#################################
+# Start BINARY testparm
+[BINARY::testparm]
+INSTALLDIR = BINDIR
+PRIVATE_DEPENDENCIES = \
+ LIBSAMBA-HOSTCONFIG \
+ LIBSAMBA-UTIL \
+ LIBPOPT \
+ samba_socket \
+ POPT_SAMBA \
+ LIBCLI_RESOLVE \
+ CHARSET
+# End BINARY testparm
+#################################
+
+testparm_OBJ_FILES = $(utilssrcdir)/testparm.o
+
+################################################
+# Start BINARY oLschema2ldif
+[BINARY::oLschema2ldif]
+INSTALLDIR = BINDIR
+PRIVATE_DEPENDENCIES = \
+ LIBLDB_CMDLINE SAMDB
+# End BINARY oLschema2ldif
+################################################
+
+
+oLschema2ldif_OBJ_FILES = $(addprefix $(utilssrcdir)/, oLschema2ldif.o)
+
+MANPAGES += $(utilssrcdir)/man/oLschema2ldif.1
+
+################################################
+# Start BINARY ad2oLschema
+[BINARY::ad2oLschema]
+INSTALLDIR = BINDIR
+PRIVATE_DEPENDENCIES = \
+ LIBLDB_CMDLINE SAMDB
+# End BINARY ad2oLschema
+################################################
+
+ad2oLschema_OBJ_FILES = $(addprefix $(utilssrcdir)/, ad2oLschema.o)
+
+MANPAGES += $(utilssrcdir)/man/ad2oLschema.1
+
diff --git a/source4/utils/getntacl.c b/source4/utils/getntacl.c
new file mode 100644
index 0000000000..f26c87bd85
--- /dev/null
+++ b/source4/utils/getntacl.c
@@ -0,0 +1,121 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Get NT ACLs from UNIX files.
+
+ Copyright (C) Tim Potter <tpot@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "librpc/gen_ndr/ndr_xattr.h"
+#include "../lib/util/wrap_xattr.h"
+#include "param/param.h"
+
+static void ntacl_print_debug_helper(struct ndr_print *ndr, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
+
+static void ntacl_print_debug_helper(struct ndr_print *ndr, const char *format, ...)
+{
+ va_list ap;
+ char *s = NULL;
+ int i;
+
+ va_start(ap, format);
+ vasprintf(&s, format, ap);
+ va_end(ap);
+
+ for (i=0;i<ndr->depth;i++) {
+ printf(" ");
+ }
+
+ printf("%s\n", s);
+ free(s);
+}
+
+static NTSTATUS get_ntacl(TALLOC_CTX *mem_ctx,
+ char *filename,
+ struct xattr_NTACL **ntacl,
+ ssize_t *ntacl_len)
+{
+ DATA_BLOB blob;
+ ssize_t size;
+ enum ndr_err_code ndr_err;
+ struct ndr_pull *ndr;
+
+ *ntacl = talloc(mem_ctx, struct xattr_NTACL);
+
+ size = wrap_getxattr(filename, XATTR_NTACL_NAME, NULL, 0);
+
+ if (size < 0) {
+ fprintf(stderr, "get_ntacl: %s\n", strerror(errno));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ blob.data = talloc_array(*ntacl, uint8_t, size);
+ size = wrap_getxattr(filename, XATTR_NTACL_NAME, blob.data, size);
+ if (size < 0) {
+ fprintf(stderr, "get_ntacl: %s\n", strerror(errno));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ blob.length = size;
+
+ ndr = ndr_pull_init_blob(&blob, NULL, NULL);
+
+ ndr_err = ndr_pull_xattr_NTACL(ndr, NDR_SCALARS|NDR_BUFFERS, *ntacl);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ return NT_STATUS_OK;
+}
+
+static void print_ntacl(TALLOC_CTX *mem_ctx,
+ const char *fname,
+ struct xattr_NTACL *ntacl)
+{
+ struct ndr_print *pr;
+
+ pr = talloc_zero(mem_ctx, struct ndr_print);
+ if (!pr) return;
+ pr->print = ntacl_print_debug_helper;
+
+ ndr_print_xattr_NTACL(pr, fname, ntacl);
+ talloc_free(pr);
+}
+
+int main(int argc, char *argv[])
+{
+ NTSTATUS status;
+ struct xattr_NTACL *ntacl;
+ ssize_t ntacl_len;
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage: getntacl FILENAME\n");
+ return 1;
+ }
+
+ status = get_ntacl(NULL, argv[1], &ntacl, &ntacl_len);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr, "get_ntacl failed: %s\n", nt_errstr(status));
+ return 1;
+ }
+
+ print_ntacl(ntacl, argv[1], ntacl);
+
+ talloc_free(ntacl);
+
+ return 0;
+}
diff --git a/source4/utils/man/ad2oLschema.1.xml b/source4/utils/man/ad2oLschema.1.xml
new file mode 100644
index 0000000000..6ae8996477
--- /dev/null
+++ b/source4/utils/man/ad2oLschema.1.xml
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<refentry id="ad2oLschema.1">
+
+<refmeta>
+ <refentrytitle>ad2oLschema</refentrytitle>
+ <manvolnum>1</manvolnum>
+</refmeta>
+
+
+<refnamediv>
+ <refname>ad2oLschema</refname>
+ <refpurpose>Converts AC-like LDAP schemas to OpenLDAP
+ compatible schema files</refpurpose>
+</refnamediv>
+
+<refsynopsisdiv>
+ <cmdsynopsis>
+ <command>ad2oLschema</command>
+ <arg choice="opt">-I INPUT-FILE</arg>
+ <arg choice="opt">-O OUTPUT-FILE</arg>
+ </cmdsynopsis>
+</refsynopsisdiv>
+
+<refsect1>
+ <title>DESCRIPTION</title>
+
+ <para>ad2oLschema is a simple tool that converts AD-like LDIF
+ schema files into OpenLDAP schema files.</para>
+</refsect1>
+
+
+<refsect1>
+ <title>OPTIONS</title>
+
+ <variablelist>
+ <varlistentry>
+ <term>-H url</term>
+ <listitem><para>URL to an LDB or LDAP server with an AD schema to read. </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-I input-file</term> <listitem><para>AD schema
+ to read. If neither this nor -H is specified, the
+ schema file will be read from standard input.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-O output-file</term>
+ <listitem><para>File to write OpenLDAP version of schema to.
+ </para></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect1>
+
+<refsect1>
+ <title>VERSION</title>
+
+ <para>This man page is correct for version 4.0 of the Samba suite.</para>
+</refsect1>
+
+<refsect1>
+ <title>SEE ALSO</title>
+
+ <para>ldb(7), ldbmodify, ldbdel, ldif(5)</para>
+
+</refsect1>
+
+<refsect1>
+ <title>AUTHOR</title>
+
+ <para> ldb was written by
+ <ulink url="http://samba.org/~tridge/">Andrew Tridgell</ulink>.
+ ad2oLschema was written by <ulink
+ url="http://samba.org/~abartlet/">Andrew Bartlett</ulink>.
+ </para>
+
+ <para>
+If you wish to report a problem or make a suggestion then please see
+the <ulink url="http://ldb.samba.org/"/> web site for
+current contact and maintainer information.
+ </para>
+
+</refsect1>
+
+</refentry>
diff --git a/source4/utils/man/getntacl.1.xml b/source4/utils/man/getntacl.1.xml
new file mode 100644
index 0000000000..cbce5f2103
--- /dev/null
+++ b/source4/utils/man/getntacl.1.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE refentry PUBLIC "-//Samba-Team//DTD DocBook V4.2-Based Variant V1.0//EN" "http://www.samba.org/samba/DTD/samba-doc">
+<refentry id="getntacl.1">
+
+<refmeta>
+ <refentrytitle>getntacl</refentrytitle>
+ <manvolnum>1</manvolnum>
+</refmeta>
+
+
+<refnamediv>
+ <refname>getntacl</refname>
+ <refpurpose>Tool for displaying NT ACLs stored in extended attributes</refpurpose>
+</refnamediv>
+
+<refsynopsisdiv>
+ <cmdsynopsis>
+ <command>getntacl</command>
+ <arg choice="req">filename</arg>
+ </cmdsynopsis>
+</refsynopsisdiv>
+
+<refsect1>
+ <title>DESCRIPTION</title>
+
+ <para>Retrieves the NT security ACL on the specified file, as
+stored in the filesystems' extended attribute. </para>
+</refsect1>
+
+<refsect1>
+ <title>VERSION</title>
+
+ <para>This man page is correct for version 4.0 of the Samba suite.</para>
+</refsect1>
+
+<refsect1>
+ <title>AUTHOR</title>
+
+ <para>This utility is part of the <ulink url="http://www.samba.org/">Samba</ulink> suite, which is developed by the global <ulink url="http://www.samba.org/samba/team/">Samba Team</ulink>.</para>
+
+ <para>This manpage was written by Jelmer Vernooij. </para>
+
+</refsect1>
+
+</refentry>
diff --git a/source4/utils/man/ntlm_auth.1.xml b/source4/utils/man/ntlm_auth.1.xml
new file mode 100644
index 0000000000..1677500112
--- /dev/null
+++ b/source4/utils/man/ntlm_auth.1.xml
@@ -0,0 +1,269 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE refentry PUBLIC "-//Samba-Team//DTD DocBook V4.2-Based Variant V1.0//EN" "http://www.samba.org/samba/DTD/samba-doc">
+<refentry id="ntlm-auth.1">
+
+<refmeta>
+ <refentrytitle>ntlm_auth</refentrytitle>
+ <manvolnum>1</manvolnum>
+</refmeta>
+
+
+<refnamediv>
+ <refname>ntlm_auth</refname>
+ <refpurpose>tool to allow external access to Winbind's NTLM authentication function</refpurpose>
+</refnamediv>
+
+<refsynopsisdiv>
+ <cmdsynopsis>
+ <command>ntlm_auth</command>
+ <arg choice="opt">-d debuglevel</arg>
+ <arg choice="opt">-l logdir</arg>
+ <arg choice="opt">-s &lt;smb config file&gt;</arg>
+ </cmdsynopsis>
+</refsynopsisdiv>
+
+<refsect1>
+ <title>DESCRIPTION</title>
+
+ <para>This tool is part of the <citerefentry><refentrytitle>samba</refentrytitle>
+ <manvolnum>7</manvolnum></citerefentry> suite.</para>
+
+ <para><command>ntlm_auth</command> is a helper utility that authenticates
+ users using NT/LM authentication. It returns 0 if the users is authenticated
+ successfully and 1 if access was denied. ntlm_auth uses winbind to access
+ the user and authentication data for a domain. This utility
+ is only indended to be used by other programs (currently squid).
+ </para>
+</refsect1>
+
+<refsect1>
+ <title>OPERATIONAL REQUIREMENTS</title>
+
+ <para>
+ The <citerefentry><refentrytitle>winbindd</refentrytitle>
+ <manvolnum>8</manvolnum></citerefentry> daemon must be operational
+ for many of these commands to function.</para>
+
+ <para>Some of these commands also require access to the directory
+ <filename>winbindd_privileged</filename> in
+ <filename>$LOCKDIR</filename>. This should be done either by running
+ this command as root or providing group access
+ to the <filename>winbindd_privileged</filename> directory. For
+ security reasons, this directory should not be world-accessable. </para>
+
+</refsect1>
+
+
+<refsect1>
+ <title>OPTIONS</title>
+
+ <variablelist>
+ <varlistentry>
+ <term>--helper-protocol=PROTO</term>
+ <listitem><para>
+ Operate as a stdio-based helper. Valid helper protocols are:
+ </para>
+ <variablelist>
+ <varlistentry>
+ <term>squid-2.4-basic</term>
+ <listitem><para>
+ Server-side helper for use with Squid 2.4's basic (plaintext)
+ authentication. </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>squid-2.5-basic</term>
+ <listitem><para>
+ Server-side helper for use with Squid 2.5's basic (plaintext)
+ authentication. </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>squid-2.5-ntlmssp</term>
+ <listitem><para>
+ Server-side helper for use with Squid 2.5's NTLMSSP
+ authentication. </para>
+ <para>Requires access to the directory
+ <filename>winbindd_privileged</filename> in
+ <filename>$LOCKDIR</filename>. The protocol used is
+ described here: <ulink
+ url="http://devel.squid-cache.org/ntlm/squid_helper_protocol.html">http://devel.squid-cache.org/ntlm/squid_helper_protocol.html</ulink>
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>ntlmssp-client-1</term>
+ <listitem><para>
+ Cleint-side helper for use with arbitary external
+ programs that may wish to use Samba's NTLMSSP
+ authentication knowlege. </para>
+ <para>This helper is a client, and as such may be run by any
+ user. The protocol used is
+ effectivly the reverse of the previous protocol.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>gss-spnego</term>
+ <listitem><para>
+ Server-side helper that implements GSS-SPNEGO. This
+ uses a protocol that is almost the same as
+ <command>squid-2.5-ntlmssp</command>, but has some
+ subtle differences that are undocumented outside the
+ source at this stage.
+ </para>
+ <para>Requires access to the directory
+ <filename>winbindd_privileged</filename> in
+ <filename>$LOCKDIR</filename>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>gss-spnego-client</term>
+ <listitem><para>
+ Client-side helper that implements GSS-SPNEGO. This
+ also uses a protocol similar to the above helpers, but
+ is currently undocumented.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>--username=USERNAME</term>
+ <listitem><para>
+ Specify username of user to authenticate
+ </para></listitem>
+
+ </varlistentry>
+
+ <varlistentry>
+ <term>--domain=DOMAIN</term>
+ <listitem><para>
+ Specify domain of user to authenticate
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>--workstation=WORKSTATION</term>
+ <listitem><para>
+ Specify the workstation the user authenticated from
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>--challenge=STRING</term>
+ <listitem><para>NTLM challenge (in HEXADECIMAL)</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>--lm-response=RESPONSE</term>
+ <listitem><para>LM Response to the challenge (in HEXADECIMAL)</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>--nt-response=RESPONSE</term>
+ <listitem><para>NT or NTLMv2 Response to the challenge (in HEXADECIMAL)</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>--password=PASSWORD</term>
+ <listitem><para>User's plaintext password</para><para>If
+ not specified on the command line, this is prompted for when
+ required. </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>--request-lm-key</term>
+ <listitem><para>Retreive LM session key</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>--request-nt-key</term>
+ <listitem><para>Request NT key</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>--diagnostics</term>
+ <listitem><para>Perform Diagnostics on the authentication
+ chain. Uses the password from <command>--password</command>
+ or prompts for one.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>--require-membership-of={SID|Name}</term>
+ <listitem><para>Require that a user be a member of specified
+ group (either name or SID) for authentication to succeed.</para>
+ </listitem>
+ </varlistentry>
+
+ &popt.common.samba;
+ &stdarg.help;
+
+ </variablelist>
+</refsect1>
+
+<refsect1>
+ <title>EXAMPLE SETUP</title>
+
+ <para>To setup ntlm_auth for use by squid 2.5, with both basic and
+ NTLMSSP authentication, the following
+ should be placed in the <filename>squid.conf</filename> file.
+<programlisting>
+auth_param ntlm program ntlm_auth --helper-protocol=squid-2.5-ntlmssp
+auth_param basic program ntlm_auth --helper-protocol=squid-2.5-basic
+auth_param basic children 5
+auth_param basic realm Squid proxy-caching web server
+auth_param basic credentialsttl 2 hours
+</programlisting></para>
+
+<note><para>This example assumes that ntlm_auth has been installed into your
+ path, and that the group permissions on
+ <filename>winbindd_privileged</filename> are as described above.</para></note>
+
+ <para>To setup ntlm_auth for use by squid 2.5 with group limitation in addition to the above
+ example, the following should be added to the <filename>squid.conf</filename> file.
+<programlisting>
+auth_param ntlm program ntlm_auth --helper-protocol=squid-2.5-ntlmssp --require-membership-of='WORKGROUP\Domain Users'
+auth_param basic program ntlm_auth --helper-protocol=squid-2.5-basic --require-membership-of='WORKGROUP\Domain Users'
+</programlisting></para>
+
+</refsect1>
+
+<refsect1>
+ <title>TROUBLESHOOTING</title>
+
+ <para>If you're experiencing problems with authenticating Internet Explorer running
+ under MS Windows 9X or Millenium Edition against ntlm_auth's NTLMSSP authentication
+ helper (--helper-protocol=squid-2.5-ntlmssp), then please read
+ <ulink url="http://support.microsoft.com/support/kb/articles/Q239/8/69.ASP">
+ the Microsoft Knowledge Base article #239869 and follow instructions described there</ulink>.
+ </para>
+</refsect1>
+
+<refsect1>
+ <title>VERSION</title>
+
+ <para>This man page is correct for version 3.0 of the Samba
+ suite.</para>
+</refsect1>
+
+<refsect1>
+ <title>AUTHOR</title>
+
+ <para>The original Samba software and related utilities
+ were created by Andrew Tridgell. Samba is now developed
+ by the Samba Team as an Open Source project similar
+ to the way the Linux kernel is developed.</para>
+
+ <para>The ntlm_auth manpage was written by Jelmer Vernooij and
+ Andrew Bartlett.</para>
+</refsect1>
+
+</refentry>
diff --git a/source4/utils/man/oLschema2ldif.1.xml b/source4/utils/man/oLschema2ldif.1.xml
new file mode 100644
index 0000000000..b1e681be4e
--- /dev/null
+++ b/source4/utils/man/oLschema2ldif.1.xml
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<refentry id="oLschema2ldif.1">
+
+<refmeta>
+ <refentrytitle>oLschema2ldif</refentrytitle>
+ <manvolnum>1</manvolnum>
+</refmeta>
+
+
+<refnamediv>
+ <refname>oLschema2ldif</refname>
+ <refpurpose>Converts LDAP schema's to LDB-compatible LDIF</refpurpose>
+</refnamediv>
+
+<refsynopsisdiv>
+ <cmdsynopsis>
+ <command>oLschema2ldif</command>
+ <arg choice="opt">-I INPUT-FILE</arg>
+ <arg choice="opt">-O OUTPUT-FILE</arg>
+ </cmdsynopsis>
+</refsynopsisdiv>
+
+<refsect1>
+ <title>DESCRIPTION</title>
+
+ <para>oLschema2ldif is a simple tool that converts standard OpenLDAP schema files to a LDIF format that is understood by LDB.</para>
+</refsect1>
+
+
+<refsect1>
+ <title>OPTIONS</title>
+
+ <variablelist>
+ <varlistentry>
+ <term>-I input-file</term>
+ <listitem><para>OpenLDAP schema to read. If none are specified,
+the schema file will be read from standard input.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-O output-file</term>
+ <listitem><para>File to write ldif version of schema to.
+ </para></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect1>
+
+<refsect1>
+ <title>VERSION</title>
+
+ <para>This man page is correct for version 4.0 of the Samba suite.</para>
+</refsect1>
+
+<refsect1>
+ <title>SEE ALSO</title>
+
+ <para>ldb(7), ldbmodify, ldbdel, ldif(5)</para>
+
+</refsect1>
+
+<refsect1>
+ <title>AUTHOR</title>
+
+ <para> ldb was written by
+ <ulink url="http://samba.org/~tridge/">Andrew Tridgell</ulink>.
+ oLschema2ldif was written by <ulink url="mailto:idra@samba.org">Simo Sorce</ulink>.
+ </para>
+
+ <para>
+If you wish to report a problem or make a suggestion then please see
+the <ulink url="http://ldb.samba.org/"/> web site for
+current contact and maintainer information.
+ </para>
+
+</refsect1>
+
+</refentry>
diff --git a/source4/utils/net/config.mk b/source4/utils/net/config.mk
new file mode 100644
index 0000000000..b2f0fcf6b1
--- /dev/null
+++ b/source4/utils/net/config.mk
@@ -0,0 +1,27 @@
+# $(utilssrcdir)/net subsystem
+
+#################################
+# Start BINARY net
+[BINARY::net]
+INSTALLDIR = BINDIR
+PRIVATE_DEPENDENCIES = \
+ LIBSAMBA-HOSTCONFIG \
+ LIBSAMBA-UTIL \
+ LIBSAMBA-NET \
+ LIBPOPT \
+ POPT_SAMBA \
+ POPT_CREDENTIALS
+# End BINARY net
+#################################
+
+net_OBJ_FILES = $(addprefix $(utilssrcdir)/net/, \
+ net.o \
+ net_machinepw.o \
+ net_password.o \
+ net_time.o \
+ net_join.o \
+ net_vampire.o \
+ net_user.o)
+
+
+$(eval $(call proto_header_template,$(utilssrcdir)/net/net_proto.h,$(net_OBJ_FILES:.o=.c)))
diff --git a/source4/utils/net/net.c b/source4/utils/net/net.c
new file mode 100644
index 0000000000..d934403ade
--- /dev/null
+++ b/source4/utils/net/net.c
@@ -0,0 +1,219 @@
+/*
+ Samba Unix/Linux SMB client library
+ Distributed SMB/CIFS Server Management Utility
+ Copyright (C) 2001 Steve French (sfrench@us.ibm.com)
+ Copyright (C) 2001 Jim McDonough (jmcd@us.ibm.com)
+ Copyright (C) 2001 Andrew Tridgell (tridge@samba.org)
+ Copyright (C) 2001 Andrew Bartlett (abartlet@samba.org)
+ Copyright (C) 2004 Stefan Metzmacher (metze@samba.org)
+
+ Largely rewritten by metze in August 2004
+
+ Originally written by Steve and Jim. Largely rewritten by tridge in
+ November 2001.
+
+ Reworked again by abartlet in December 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 <http://www.gnu.org/licenses/>.
+*/
+
+/*****************************************************/
+/* */
+/* Distributed SMB/CIFS Server Management Utility */
+/* */
+/* The intent was to make the syntax similar */
+/* to the NET utility (first developed in DOS */
+/* with additional interesting & useful functions */
+/* added in later SMB server network operating */
+/* systems). */
+/* */
+/*****************************************************/
+
+#include "includes.h"
+#include "utils/net/net.h"
+#include "lib/cmdline/popt_common.h"
+#include "lib/ldb/include/ldb.h"
+#include "librpc/rpc/dcerpc.h"
+#include "param/param.h"
+#include "lib/events/events.h"
+#include "auth/credentials/credentials.h"
+
+/*
+ run a function from a function table. If not found then
+ call the specified usage function
+*/
+int net_run_function(struct net_context *ctx,
+ int argc, const char **argv,
+ const struct net_functable *functable,
+ int (*usage_fn)(struct net_context *ctx, int argc, const char **argv))
+{
+ int i;
+
+ if (argc == 0) {
+ return usage_fn(ctx, argc, argv);
+
+ } else if (argc == 1 && strequal(argv[0], "help")) {
+ return net_help(ctx, functable);
+ }
+
+ for (i=0; functable[i].name; i++) {
+ if (strcasecmp_m(argv[0], functable[i].name) == 0)
+ return functable[i].fn(ctx, argc-1, argv+1);
+ }
+
+ d_printf("No command: %s\n", argv[0]);
+ return usage_fn(ctx, argc, argv);
+}
+
+/*
+ run a usage function from a function table. If not found then fail
+*/
+int net_run_usage(struct net_context *ctx,
+ int argc, const char **argv,
+ const struct net_functable *functable)
+{
+ int i;
+
+ for (i=0; functable[i].name; i++) {
+ if (strcasecmp_m(argv[0], functable[i].name) == 0)
+ if (functable[i].usage) {
+ return functable[i].usage(ctx, argc-1, argv+1);
+ }
+ }
+
+ d_printf("No usage information for command: %s\n", argv[0]);
+
+ return 1;
+}
+
+
+/* main function table */
+static const struct net_functable net_functable[] = {
+ {"password", "change password\n", net_password, net_password_usage},
+ {"time", "get remote server's time\n", net_time, net_time_usage},
+ {"join", "join a domain\n", net_join, net_join_usage},
+ {"samdump", "dump the sam of a domain\n", net_samdump, net_samdump_usage},
+ {"vampire", "join and syncronise an AD domain onto the local server\n", net_vampire, net_vampire_usage},
+ {"samsync", "synchronise into the local ldb the sam of an NT4 domain\n", net_samsync_ldb, net_samsync_ldb_usage},
+ {"user", "manage user accounts\n", net_user, net_user_usage},
+ {"machinepw", "Get a machine password out of our SAM\n", net_machinepw,
+ net_machinepw_usage},
+ {NULL, NULL, NULL, NULL}
+};
+
+int net_help(struct net_context *ctx, const struct net_functable *ftable)
+{
+ int i = 0;
+ const char *name = ftable[i].name;
+ const char *desc = ftable[i].desc;
+
+ d_printf("Available commands:\n");
+ while (name && desc) {
+ d_printf("\t%s\t\t%s", name, desc);
+ name = ftable[++i].name;
+ desc = ftable[i].desc;
+ }
+
+ return 0;
+}
+
+static int net_usage(struct net_context *ctx, int argc, const char **argv)
+{
+ d_printf("Usage:\n");
+ d_printf("net <command> [options]\n");
+ return 0;
+}
+
+/****************************************************************************
+ main program
+****************************************************************************/
+static int binary_net(int argc, const char **argv)
+{
+ int opt,i;
+ int rc;
+ int argc_new;
+ const char **argv_new;
+ struct tevent_context *ev;
+ struct net_context *ctx = NULL;
+ poptContext pc;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ POPT_COMMON_SAMBA
+ POPT_COMMON_CONNECTION
+ POPT_COMMON_CREDENTIALS
+ POPT_COMMON_VERSION
+ { NULL }
+ };
+
+ setlinebuf(stdout);
+
+ pc = poptGetContext("net", argc, (const char **) argv, long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ default:
+ d_printf("Invalid option %s: %s\n",
+ poptBadOption(pc, 0), poptStrerror(opt));
+ net_usage(ctx, argc, argv);
+ exit(1);
+ }
+ }
+
+ argv_new = (const char **)poptGetArgs(pc);
+
+ argc_new = argc;
+ for (i=0; i<argc; i++) {
+ if (argv_new[i] == NULL) {
+ argc_new = i;
+ break;
+ }
+ }
+
+ if (argc_new < 2) {
+ return net_usage(ctx, argc, argv);
+ }
+
+ dcerpc_init(cmdline_lp_ctx);
+
+ ev = s4_event_context_init(NULL);
+ if (!ev) {
+ d_printf("Failed to create an event context\n");
+ exit(1);
+ }
+ ctx = talloc(ev, struct net_context);
+ if (!ctx) {
+ d_printf("Failed to talloc a net_context\n");
+ exit(1);
+ }
+
+ ZERO_STRUCTP(ctx);
+ ctx->lp_ctx = cmdline_lp_ctx;
+ ctx->credentials = cmdline_credentials;
+ ctx->event_ctx = ev;
+
+ rc = net_run_function(ctx, argc_new-1, argv_new+1, net_functable, net_usage);
+
+ if (rc != 0) {
+ DEBUG(0,("return code = %d\n", rc));
+ }
+
+ talloc_free(ev);
+ return rc;
+}
+
+ int main(int argc, const char **argv)
+{
+ return binary_net(argc, argv);
+}
diff --git a/source4/utils/net/net.h b/source4/utils/net/net.h
new file mode 100644
index 0000000000..16223a15bb
--- /dev/null
+++ b/source4/utils/net/net.h
@@ -0,0 +1,39 @@
+/*
+ Samba Unix/Linux SMB client library
+ Distributed SMB/CIFS Server Management Utility
+
+ Copyright (C) Stefan Metzmacher 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _UTIL_NET_H
+#define _UTIL_NET_H
+
+struct net_context {
+ struct cli_credentials *credentials;
+ struct loadparm_context *lp_ctx;
+ struct tevent_context *event_ctx;
+};
+
+struct net_functable {
+ const char *name;
+ const char *desc;
+ int (*fn)(struct net_context *ctx, int argc, const char **argv);
+ int (*usage)(struct net_context *ctx, int argc, const char **argv);
+};
+
+#include "utils/net/net_proto.h"
+
+#endif /* _UTIL_NET_H */
diff --git a/source4/utils/net/net_join.c b/source4/utils/net/net_join.c
new file mode 100644
index 0000000000..b0a25bb7c0
--- /dev/null
+++ b/source4/utils/net/net_join.c
@@ -0,0 +1,170 @@
+/*
+ Samba Unix/Linux SMB client library
+ Distributed SMB/CIFS Server Management Utility
+
+ Copyright (C) 2004 Stefan Metzmacher <metze@samba.org>
+ Copyright (C) 2005 Andrew Bartlett <abartlet@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "utils/net/net.h"
+#include "libnet/libnet.h"
+#include "libcli/security/security.h"
+#include "param/param.h"
+#include "lib/events/events.h"
+
+int net_join(struct net_context *ctx, int argc, const char **argv)
+{
+ NTSTATUS status;
+ struct libnet_context *libnetctx;
+ struct libnet_Join *r;
+ char *tmp;
+ const char *domain_name;
+ enum netr_SchannelType secure_channel_type = SEC_CHAN_WKSTA;
+
+ switch (argc) {
+ case 0: /* no args -> fail */
+ return net_join_usage(ctx, argc, argv);
+ case 1: /* only DOMAIN */
+ tmp = talloc_strdup(ctx, argv[0]);
+ break;
+ case 2: /* DOMAIN and role */
+ tmp = talloc_strdup(ctx, argv[0]);
+ if (strcasecmp(argv[1], "BDC") == 0) {
+ secure_channel_type = SEC_CHAN_BDC;
+ } else if (strcasecmp(argv[1], "MEMBER") == 0) {
+ secure_channel_type = SEC_CHAN_WKSTA;
+ } else {
+ d_fprintf(stderr, "net_join: Invalid 2nd argument (%s) must be MEMBER or BDC\n", argv[1]);
+ return net_join_usage(ctx, argc, argv);
+ }
+ break;
+ default: /* too many args -> fail */
+ return net_join_usage(ctx, argc, argv);
+ }
+
+ domain_name = tmp;
+
+ libnetctx = libnet_context_init(ctx->event_ctx, ctx->lp_ctx);
+ if (!libnetctx) {
+ return -1;
+ }
+ libnetctx->cred = ctx->credentials;
+ r = talloc(ctx, struct libnet_Join);
+ if (!r) {
+ return -1;
+ }
+ /* prepare parameters for the join */
+ r->in.netbios_name = lp_netbios_name(ctx->lp_ctx);
+ r->in.domain_name = domain_name;
+ r->in.join_type = secure_channel_type;
+ r->in.level = LIBNET_JOIN_AUTOMATIC;
+ r->out.error_string = NULL;
+
+ /* do the domain join */
+ status = libnet_Join(libnetctx, r, r);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "Joining domain failed: %s\n",
+ r->out.error_string ? r->out.error_string : nt_errstr(status));
+ talloc_free(r);
+ talloc_free(libnetctx);
+ return -1;
+ }
+ d_printf("Joined domain %s (%s)\n", r->out.domain_name, dom_sid_string(ctx, r->out.domain_sid));
+
+ talloc_free(libnetctx);
+ return 0;
+}
+
+int net_join_usage(struct net_context *ctx, int argc, const char **argv)
+{
+ d_printf("net join <domain> [BDC | MEMBER] [options]\n");
+ return 0;
+}
+
+int net_join_help(struct net_context *ctx, int argc, const char **argv)
+{
+ d_printf("Joins domain as either member or backup domain controller.\n");
+ return 0;
+}
+
+int net_vampire(struct net_context *ctx, int argc, const char **argv)
+{
+ NTSTATUS status;
+ struct libnet_context *libnetctx;
+ struct libnet_Vampire *r;
+ char *tmp, *targetdir = NULL;
+ const char *domain_name;
+
+ switch (argc) {
+ case 0: /* no args -> fail */
+ return net_vampire_usage(ctx, argc, argv);
+ case 1: /* only DOMAIN */
+ tmp = talloc_strdup(ctx, argv[0]);
+ break;
+ case 2: /* domain and target dir */
+ tmp = talloc_strdup(ctx, argv[0]);
+ targetdir = talloc_strdup(ctx, argv[1]);
+ break;
+ default: /* too many args -> fail */
+ return net_vampire_usage(ctx, argc, argv);
+ }
+
+ domain_name = tmp;
+
+ libnetctx = libnet_context_init(ctx->event_ctx, ctx->lp_ctx);
+ if (!libnetctx) {
+ return -1;
+ }
+ libnetctx->cred = ctx->credentials;
+ r = talloc(ctx, struct libnet_Vampire);
+ if (!r) {
+ return -1;
+ }
+ /* prepare parameters for the vampire */
+ r->in.netbios_name = lp_netbios_name(ctx->lp_ctx);
+ r->in.domain_name = domain_name;
+ r->in.targetdir = targetdir;
+ r->out.error_string = NULL;
+
+ /* do the domain vampire */
+ status = libnet_Vampire(libnetctx, r, r);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "Vampire of domain failed: %s\n",
+ r->out.error_string ? r->out.error_string : nt_errstr(status));
+ talloc_free(r);
+ talloc_free(libnetctx);
+ return -1;
+ }
+ d_printf("Vampired domain %s (%s)\n", r->out.domain_name, dom_sid_string(ctx, r->out.domain_sid));
+
+ talloc_free(libnetctx);
+ return 0;
+}
+
+int net_vampire_usage(struct net_context *ctx, int argc, const char **argv)
+{
+ d_printf("net vampire <domain> [options]\n");
+ return 0;
+}
+
+int net_vampire_help(struct net_context *ctx, int argc, const char **argv)
+{
+ d_printf("Vampires domain as either member or backup domain controller.\n");
+ return 0;
+}
diff --git a/source4/utils/net/net_machinepw.c b/source4/utils/net/net_machinepw.c
new file mode 100644
index 0000000000..390eb8df0b
--- /dev/null
+++ b/source4/utils/net/net_machinepw.c
@@ -0,0 +1,91 @@
+/*
+ Samba Unix/Linux SMB client library
+ Distributed SMB/CIFS Server Management Utility
+
+ Copyright (C) 2008 Volker Lendecke
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/events/events.h"
+#include "utils/net/net.h"
+#include "libnet/libnet.h"
+#include "libcli/security/security.h"
+#include "param/secrets.h"
+#include "param/param.h"
+#include "lib/util/util_ldb.h"
+
+int net_machinepw_usage(struct net_context *ctx, int argc, const char **argv)
+{
+ d_printf("net machinepw <accountname>\n");
+ return -1;
+}
+
+int net_machinepw(struct net_context *ctx, int argc, const char **argv)
+{
+ struct ldb_context *secrets;
+ TALLOC_CTX *mem_ctx;
+ struct tevent_context *ev;
+ struct ldb_message **msgs;
+ int num_records;
+ const char *attrs[] = { "secret", NULL };
+ const char *secret;
+
+ if (argc != 1) {
+ net_machinepw_usage(ctx, argc, argv);
+ return -1;
+ }
+
+ mem_ctx = talloc_new(ctx);
+ if (mem_ctx == NULL) {
+ d_fprintf(stderr, "talloc_new failed\n");
+ return -1;
+ }
+
+ ev = event_context_init(mem_ctx);
+ if (ev == NULL) {
+ d_fprintf(stderr, "event_context_init failed\n");
+ goto fail;
+ }
+
+ secrets = secrets_db_connect(mem_ctx, ev, ctx->lp_ctx);
+ if (secrets == NULL) {
+ d_fprintf(stderr, "secrets_db_connect failed\n");
+ goto fail;
+ }
+
+ num_records = gendb_search(secrets, mem_ctx, NULL, &msgs, attrs,
+ "(&(objectclass=primaryDomain)"
+ "(samaccountname=%s))", argv[0]);
+ if (num_records != 1) {
+ d_fprintf(stderr, "gendb_search returned %d records, "
+ "expected 1\n", num_records);
+ goto fail;
+ }
+
+ secret = ldb_msg_find_attr_as_string(msgs[0], "secret", NULL);
+ if (secret == NULL) {
+ d_fprintf(stderr, "machine account contains no secret\n");
+ goto fail;
+ }
+
+ printf("%s\n", secret);
+ talloc_free(mem_ctx);
+ return 0;
+
+ fail:
+ talloc_free(mem_ctx);
+ return -1;
+}
diff --git a/source4/utils/net/net_password.c b/source4/utils/net/net_password.c
new file mode 100644
index 0000000000..55f7c3c31d
--- /dev/null
+++ b/source4/utils/net/net_password.c
@@ -0,0 +1,171 @@
+/*
+ Samba Unix/Linux SMB client library
+ Distributed SMB/CIFS Server Management Utility
+
+ Copyright (C) 2004 Stefan Metzmacher (metze@samba.org)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "utils/net/net.h"
+#include "libnet/libnet.h"
+#include "system/filesys.h"
+#include "lib/events/events.h"
+#include "auth/credentials/credentials.h"
+
+/*
+ * Code for Changing and setting a password
+ */
+
+static int net_password_change_usage(struct net_context *ctx, int argc, const char **argv)
+{
+ d_printf("net_password_change_usage: TODO\n");
+ return 0;
+}
+
+
+static int net_password_change(struct net_context *ctx, int argc, const char **argv)
+{
+ NTSTATUS status;
+ struct libnet_context *libnetctx;
+ union libnet_ChangePassword r;
+ char *password_prompt = NULL;
+ const char *new_password;
+
+ if (argc > 0 && argv[0]) {
+ new_password = argv[0];
+ } else {
+ password_prompt = talloc_asprintf(ctx, "Enter new password for account [%s\\%s]:",
+ cli_credentials_get_domain(ctx->credentials),
+ cli_credentials_get_username(ctx->credentials));
+ new_password = getpass(password_prompt);
+ }
+
+ libnetctx = libnet_context_init(ctx->event_ctx, ctx->lp_ctx);
+ if (!libnetctx) {
+ return -1;
+ }
+ libnetctx->cred = ctx->credentials;
+
+ /* prepare password change */
+ r.generic.level = LIBNET_CHANGE_PASSWORD_GENERIC;
+ r.generic.in.account_name = cli_credentials_get_username(ctx->credentials);
+ r.generic.in.domain_name = cli_credentials_get_domain(ctx->credentials);
+ r.generic.in.oldpassword = cli_credentials_get_password(ctx->credentials);
+ r.generic.in.newpassword = new_password;
+
+ /* do password change */
+ status = libnet_ChangePassword(libnetctx, ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("net_password_change: %s\n",r.generic.out.error_string));
+ return -1;
+ }
+
+ talloc_free(libnetctx);
+
+ return 0;
+}
+
+
+static int net_password_set_usage(struct net_context *ctx, int argc, const char **argv)
+{
+ d_printf("net_password_set_usage: TODO\n");
+ return 0;
+}
+
+
+static int net_password_set(struct net_context *ctx, int argc, const char **argv)
+{
+ NTSTATUS status;
+ struct libnet_context *libnetctx;
+ union libnet_SetPassword r;
+ char *password_prompt = NULL;
+ char *p;
+ char *tmp;
+ const char *account_name;
+ const char *domain_name;
+ const char *new_password = NULL;
+
+ switch (argc) {
+ case 0: /* no args -> fail */
+ return net_password_set_usage(ctx, argc, argv);
+ case 1: /* only DOM\\user; prompt for password */
+ tmp = talloc_strdup(ctx, argv[0]);
+ break;
+ case 2: /* DOM\\USER and password */
+ tmp = talloc_strdup(ctx, argv[0]);
+ new_password = argv[1];
+ break;
+ default: /* too mayn args -> fail */
+ DEBUG(0,("net_password_set: too many args [%d]\n",argc));
+ return net_password_usage(ctx, argc, argv);
+ }
+
+ if ((p = strchr_m(tmp,'\\'))) {
+ *p = 0;
+ domain_name = tmp;
+ account_name = talloc_strdup(ctx, p+1);
+ } else {
+ account_name = tmp;
+ domain_name = cli_credentials_get_domain(ctx->credentials);
+ }
+
+ if (!new_password) {
+ password_prompt = talloc_asprintf(ctx, "Enter new password for account [%s\\%s]:",
+ domain_name, account_name);
+ new_password = getpass(password_prompt);
+ }
+
+ libnetctx = libnet_context_init(ctx->event_ctx, ctx->lp_ctx);
+ if (!libnetctx) {
+ return -1;
+ }
+ libnetctx->cred = ctx->credentials;
+
+ /* prepare password change */
+ r.generic.level = LIBNET_SET_PASSWORD_GENERIC;
+ r.generic.in.account_name = account_name;
+ r.generic.in.domain_name = domain_name;
+ r.generic.in.newpassword = new_password;
+
+ /* do password change */
+ status = libnet_SetPassword(libnetctx, ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("net_password_set: %s\n",r.generic.out.error_string));
+ return -1;
+ }
+
+ talloc_free(libnetctx);
+
+ return 0;
+}
+
+
+static const struct net_functable net_password_functable[] = {
+ {"change", "change password (old password required)\n", net_password_change, net_password_change_usage },
+ {"set", "set password\n", net_password_set, net_password_set_usage },
+ {NULL, NULL}
+};
+
+int net_password(struct net_context *ctx, int argc, const char **argv)
+{
+ return net_run_function(ctx, argc, argv, net_password_functable, net_password_usage);
+}
+
+int net_password_usage(struct net_context *ctx, int argc, const char **argv)
+{
+ d_printf("net password <command> [options]\n");
+ return 0;
+}
diff --git a/source4/utils/net/net_time.c b/source4/utils/net/net_time.c
new file mode 100644
index 0000000000..92e6e77481
--- /dev/null
+++ b/source4/utils/net/net_time.c
@@ -0,0 +1,78 @@
+/*
+ Samba Unix/Linux SMB client library
+ Distributed SMB/CIFS Server Management Utility
+
+ Copyright (C) 2004 Stefan Metzmacher (metze@samba.org)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libnet/libnet.h"
+#include "utils/net/net.h"
+#include "system/time.h"
+#include "lib/events/events.h"
+
+/*
+ * Code for getting the remote time
+ */
+
+int net_time(struct net_context *ctx, int argc, const char **argv)
+{
+ NTSTATUS status;
+ struct libnet_context *libnetctx;
+ union libnet_RemoteTOD r;
+ const char *server_name;
+ struct tm *tm;
+ char timestr[64];
+
+ if (argc > 0 && argv[0]) {
+ server_name = argv[0];
+ } else {
+ return net_time_usage(ctx, argc, argv);
+ }
+
+ libnetctx = libnet_context_init(ctx->event_ctx, ctx->lp_ctx);
+ if (!libnetctx) {
+ return -1;
+ }
+ libnetctx->cred = ctx->credentials;
+
+ /* prepare to get the time */
+ r.generic.level = LIBNET_REMOTE_TOD_GENERIC;
+ r.generic.in.server_name = server_name;
+
+ /* get the time */
+ status = libnet_RemoteTOD(libnetctx, ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("net_time: %s\n",r.generic.out.error_string));
+ return -1;
+ }
+
+ ZERO_STRUCT(timestr);
+ tm = localtime(&r.generic.out.time);
+ strftime(timestr, sizeof(timestr)-1, "%c %Z",tm);
+
+ printf("%s\n",timestr);
+
+ talloc_free(libnetctx);
+
+ return 0;
+}
+
+int net_time_usage(struct net_context *ctx, int argc, const char **argv)
+{
+ d_printf("net time <server> [options]\n");
+ return 0;
+}
diff --git a/source4/utils/net/net_user.c b/source4/utils/net/net_user.c
new file mode 100644
index 0000000000..c4b8ecb0c2
--- /dev/null
+++ b/source4/utils/net/net_user.c
@@ -0,0 +1,125 @@
+/*
+ Samba Unix/Linux SMB client library
+ Distributed SMB/CIFS Server Management Utility
+
+ Copyright (C) Rafal Szczesniak <mimir@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "utils/net/net.h"
+#include "libnet/libnet.h"
+#include "lib/events/events.h"
+#include "auth/credentials/credentials.h"
+
+static int net_user_add(struct net_context *ctx, int argc, const char **argv)
+{
+ NTSTATUS status;
+ struct libnet_context *lnet_ctx;
+ struct libnet_CreateUser r;
+ char *user_name;
+
+ /* command line argument preparation */
+ switch (argc) {
+ case 0:
+ return net_user_usage(ctx, argc, argv);
+ break;
+ case 1:
+ user_name = talloc_strdup(ctx, argv[0]);
+ break;
+ default:
+ return net_user_usage(ctx, argc, argv);
+ }
+
+ /* libnet context init and its params */
+ lnet_ctx = libnet_context_init(ctx->event_ctx, ctx->lp_ctx);
+ if (!lnet_ctx) return -1;
+
+ lnet_ctx->cred = ctx->credentials;
+
+ /* calling CreateUser function */
+ r.in.user_name = user_name;
+ r.in.domain_name = cli_credentials_get_domain(lnet_ctx->cred);
+
+ status = libnet_CreateUser(lnet_ctx, ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Failed to add user account: %s\n",
+ r.out.error_string));
+ return -1;
+ }
+
+ talloc_free(lnet_ctx);
+ return 0;
+}
+
+static int net_user_delete(struct net_context *ctx, int argc, const char **argv)
+{
+ NTSTATUS status;
+ struct libnet_context *lnet_ctx;
+ struct libnet_DeleteUser r;
+ char *user_name;
+
+ /* command line argument preparation */
+ switch (argc) {
+ case 0:
+ return net_user_usage(ctx, argc, argv);
+ break;
+ case 1:
+ user_name = talloc_strdup(ctx, argv[0]);
+ break;
+ default:
+ return net_user_usage(ctx, argc, argv);
+ }
+
+ /* libnet context init and its params */
+ lnet_ctx = libnet_context_init(ctx->event_ctx, ctx->lp_ctx);
+ if (!lnet_ctx) return -1;
+
+ lnet_ctx->cred = ctx->credentials;
+
+ /* calling DeleteUser function */
+ r.in.user_name = user_name;
+ r.in.domain_name = cli_credentials_get_domain(lnet_ctx->cred);
+
+ status = libnet_DeleteUser(lnet_ctx, ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Failed to delete user account: %s\n",
+ r.out.error_string));
+ return -1;
+ }
+
+ talloc_free(lnet_ctx);
+ return 0;
+}
+
+
+static const struct net_functable net_user_functable[] = {
+ { "add", "create new user account\n", net_user_add, net_user_usage },
+ { "delete", "delete an existing user account\n", net_user_delete, net_user_usage },
+ { NULL, NULL }
+};
+
+
+int net_user(struct net_context *ctx, int argc, const char **argv)
+{
+ return net_run_function(ctx, argc, argv, net_user_functable, net_user_usage);
+}
+
+
+int net_user_usage(struct net_context *ctx, int argc, const char **argv)
+{
+ d_printf("net user <command> [options]\n");
+ return 0;
+}
diff --git a/source4/utils/net/net_vampire.c b/source4/utils/net/net_vampire.c
new file mode 100644
index 0000000000..14f6a07e4b
--- /dev/null
+++ b/source4/utils/net/net_vampire.c
@@ -0,0 +1,181 @@
+/*
+ Samba Unix/Linux SMB client library
+ Distributed SMB/CIFS Server Management Utility
+
+ Copyright (C) 2004 Stefan Metzmacher <metze@samba.org>
+ Copyright (C) 2005 Andrew Bartlett <abartlet@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "utils/net/net.h"
+#include "libnet/libnet.h"
+#include "librpc/gen_ndr/samr.h"
+#include "auth/auth.h"
+#include "param/param.h"
+#include "lib/events/events.h"
+
+static int net_samdump_keytab_usage(struct net_context *ctx, int argc, const char **argv)
+{
+ d_printf("net samdump keytab <keytab>\n");
+ return 0;
+}
+
+static int net_samdump_keytab_help(struct net_context *ctx, int argc, const char **argv)
+{
+ d_printf("Dumps kerberos keys of a domain into a keytab.\n");
+ return 0;
+}
+
+static int net_samdump_keytab(struct net_context *ctx, int argc, const char **argv)
+{
+ NTSTATUS status;
+ struct libnet_context *libnetctx;
+ struct libnet_SamDump_keytab r;
+
+ switch (argc) {
+ case 0:
+ return net_samdump_keytab_usage(ctx, argc, argv);
+ break;
+ case 1:
+ r.in.keytab_name = argv[0];
+ break;
+ }
+
+ libnetctx = libnet_context_init(ctx->event_ctx, ctx->lp_ctx);
+ if (!libnetctx) {
+ return -1;
+ }
+ libnetctx->cred = ctx->credentials;
+
+ r.out.error_string = NULL;
+ r.in.machine_account = NULL;
+ r.in.binding_string = NULL;
+
+ status = libnet_SamDump_keytab(libnetctx, ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("libnet_SamDump returned %s: %s\n",
+ nt_errstr(status),
+ r.out.error_string));
+ return -1;
+ }
+
+ talloc_free(libnetctx);
+
+ return 0;
+}
+
+/* main function table */
+static const struct net_functable net_samdump_functable[] = {
+ {"keytab", "dump keys into a keytab\n", net_samdump_keytab, net_samdump_keytab_usage},
+ {NULL, NULL, NULL, NULL}
+};
+
+int net_samdump(struct net_context *ctx, int argc, const char **argv)
+{
+ NTSTATUS status;
+ struct libnet_context *libnetctx;
+ struct libnet_SamDump r;
+ int rc;
+
+ switch (argc) {
+ case 0:
+ break;
+ case 1:
+ default:
+ rc = net_run_function(ctx, argc, argv, net_samdump_functable,
+ net_samdump_usage);
+ return rc;
+ }
+
+ libnetctx = libnet_context_init(ctx->event_ctx, ctx->lp_ctx);
+ if (!libnetctx) {
+ return -1;
+ }
+ libnetctx->cred = ctx->credentials;
+
+ r.out.error_string = NULL;
+ r.in.machine_account = NULL;
+ r.in.binding_string = NULL;
+
+ status = libnet_SamDump(libnetctx, ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("libnet_SamDump returned %s: %s\n",
+ nt_errstr(status),
+ r.out.error_string));
+ return -1;
+ }
+
+ talloc_free(libnetctx);
+
+ return 0;
+}
+
+int net_samdump_usage(struct net_context *ctx, int argc, const char **argv)
+{
+ d_printf("net samdump\n");
+ d_printf("net samdump keytab <keytab>\n");
+ return 0;
+}
+
+int net_samdump_help(struct net_context *ctx, int argc, const char **argv)
+{
+ d_printf("Dumps the sam of the domain we are joined to.\n");
+ return 0;
+}
+
+int net_samsync_ldb(struct net_context *ctx, int argc, const char **argv)
+{
+ NTSTATUS status;
+ struct libnet_context *libnetctx;
+ struct libnet_samsync_ldb r;
+
+ libnetctx = libnet_context_init(ctx->event_ctx, ctx->lp_ctx);
+ if (!libnetctx) {
+ return -1;
+ }
+ libnetctx->cred = ctx->credentials;
+
+ r.out.error_string = NULL;
+ r.in.machine_account = NULL;
+ r.in.binding_string = NULL;
+
+ /* Needed to override the ACLs on ldb */
+ r.in.session_info = system_session(libnetctx, ctx->lp_ctx);
+
+ status = libnet_samsync_ldb(libnetctx, libnetctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("libnet_samsync_ldb returned %s: %s\n",
+ nt_errstr(status),
+ r.out.error_string));
+ return -1;
+ }
+
+ talloc_free(libnetctx);
+
+ return 0;
+}
+
+int net_samsync_ldb_usage(struct net_context *ctx, int argc, const char **argv)
+{
+ d_printf("net samsync\n");
+ return 0;
+}
+
+int net_samsync_ldb_help(struct net_context *ctx, int argc, const char **argv)
+{
+ d_printf("Synchronise into the local ldb the SAM of a domain.\n");
+ return 0;
+}
diff --git a/source4/utils/ntlm_auth.c b/source4/utils/ntlm_auth.c
new file mode 100644
index 0000000000..2c6f353252
--- /dev/null
+++ b/source4/utils/ntlm_auth.c
@@ -0,0 +1,1174 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind status program.
+
+ Copyright (C) Tim Potter 2000-2003
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003-2004
+ Copyright (C) Francesco Chemolli <kinkie@kame.usr.dsi.unimi.it> 2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "lib/cmdline/popt_common.h"
+#include "lib/ldb/include/ldb.h"
+#include "auth/credentials/credentials.h"
+#include "auth/gensec/gensec.h"
+#include "auth/auth.h"
+#include "librpc/gen_ndr/ndr_netlogon.h"
+#include "auth/auth_sam.h"
+#include "auth/ntlm/ntlm_check.h"
+#include "libcli/auth/libcli_auth.h"
+#include "libcli/security/security.h"
+#include "lib/events/events.h"
+#include "lib/messaging/messaging.h"
+#include "lib/messaging/irpc.h"
+#include "auth/ntlmssp/ntlmssp.h"
+#include "param/param.h"
+
+#define INITIAL_BUFFER_SIZE 300
+#define MAX_BUFFER_SIZE 63000
+
+enum stdio_helper_mode {
+ SQUID_2_4_BASIC,
+ SQUID_2_5_BASIC,
+ SQUID_2_5_NTLMSSP,
+ NTLMSSP_CLIENT_1,
+ GSS_SPNEGO_CLIENT,
+ GSS_SPNEGO_SERVER,
+ NTLM_SERVER_1,
+ NUM_HELPER_MODES
+};
+
+#define NTLM_AUTH_FLAG_USER_SESSION_KEY 0x0004
+#define NTLM_AUTH_FLAG_LMKEY 0x0008
+
+
+typedef void (*stdio_helper_function)(enum stdio_helper_mode stdio_helper_mode,
+ struct loadparm_context *lp_ctx,
+ char *buf, int length, void **private1,
+ unsigned int mux_id, void **private2);
+
+static void manage_squid_basic_request (enum stdio_helper_mode stdio_helper_mode,
+ struct loadparm_context *lp_ctx,
+ char *buf, int length, void **private1,
+ unsigned int mux_id, void **private2);
+
+static void manage_gensec_request (enum stdio_helper_mode stdio_helper_mode,
+ struct loadparm_context *lp_ctx,
+ char *buf, int length, void **private1,
+ unsigned int mux_id, void **private2);
+
+static void manage_ntlm_server_1_request (enum stdio_helper_mode stdio_helper_mode,
+ struct loadparm_context *lp_ctx,
+ char *buf, int length, void **private1,
+ unsigned int mux_id, void **private2);
+
+static void manage_squid_request(struct loadparm_context *lp_ctx,
+ enum stdio_helper_mode helper_mode,
+ stdio_helper_function fn, void **private2);
+
+static const struct {
+ enum stdio_helper_mode mode;
+ const char *name;
+ stdio_helper_function fn;
+} stdio_helper_protocols[] = {
+ { SQUID_2_4_BASIC, "squid-2.4-basic", manage_squid_basic_request},
+ { SQUID_2_5_BASIC, "squid-2.5-basic", manage_squid_basic_request},
+ { SQUID_2_5_NTLMSSP, "squid-2.5-ntlmssp", manage_gensec_request},
+ { GSS_SPNEGO_CLIENT, "gss-spnego-client", manage_gensec_request},
+ { GSS_SPNEGO_SERVER, "gss-spnego", manage_gensec_request},
+ { NTLMSSP_CLIENT_1, "ntlmssp-client-1", manage_gensec_request},
+ { NTLM_SERVER_1, "ntlm-server-1", manage_ntlm_server_1_request},
+ { NUM_HELPER_MODES, NULL, NULL}
+};
+
+extern int winbindd_fd;
+
+static const char *opt_username;
+static const char *opt_domain;
+static const char *opt_workstation;
+static const char *opt_password;
+static int opt_multiplex;
+static int use_cached_creds;
+
+
+static void mux_printf(unsigned int mux_id, const char *format, ...) PRINTF_ATTRIBUTE(2, 3);
+
+static void mux_printf(unsigned int mux_id, const char *format, ...)
+{
+ va_list ap;
+
+ if (opt_multiplex) {
+ x_fprintf(x_stdout, "%d ", mux_id);
+ }
+
+ va_start(ap, format);
+ x_vfprintf(x_stdout, format, ap);
+ va_end(ap);
+}
+
+
+
+/* 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_ntlm_auth_domain_user(const char *domuser, char **domain,
+ char **user, char winbind_separator)
+{
+
+ char *p = strchr(domuser, winbind_separator);
+
+ if (!p) {
+ return false;
+ }
+
+ *user = smb_xstrdup(p+1);
+ *domain = smb_xstrdup(domuser);
+ (*domain)[PTR_DIFF(p, domuser)] = 0;
+
+ return true;
+}
+
+/**
+ * Decode a base64 string into a DATA_BLOB - simple and slow algorithm
+ **/
+static DATA_BLOB base64_decode_data_blob(TALLOC_CTX *mem_ctx, const char *s)
+{
+ DATA_BLOB ret = data_blob_talloc(mem_ctx, s, strlen(s)+1);
+ ret.length = ldb_base64_decode((char *)ret.data);
+ return ret;
+}
+
+/**
+ * Encode a base64 string into a talloc()ed string caller to free.
+ **/
+static char *base64_encode_data_blob(TALLOC_CTX *mem_ctx, DATA_BLOB data)
+{
+ return ldb_base64_encode(mem_ctx, (const char *)data.data, data.length);
+}
+
+/**
+ * Decode a base64 string in-place - wrapper for the above
+ **/
+static void base64_decode_inplace(char *s)
+{
+ ldb_base64_decode(s);
+}
+
+
+
+/* Authenticate a user with a plaintext password */
+
+static bool check_plaintext_auth(const char *user, const char *pass,
+ bool stdout_diagnostics)
+{
+ return (strcmp(pass, opt_password) == 0);
+}
+
+/* authenticate a user with an encrypted username/password */
+
+static NTSTATUS local_pw_check_specified(struct loadparm_context *lp_ctx,
+ const char *username,
+ const char *domain,
+ const char *workstation,
+ const DATA_BLOB *challenge,
+ const DATA_BLOB *lm_response,
+ const DATA_BLOB *nt_response,
+ uint32_t flags,
+ DATA_BLOB *lm_session_key,
+ DATA_BLOB *user_session_key,
+ char **error_string,
+ char **unix_name)
+{
+ NTSTATUS nt_status;
+ struct samr_Password lm_pw, nt_pw;
+ struct samr_Password *lm_pwd, *nt_pwd;
+ TALLOC_CTX *mem_ctx = talloc_init("local_pw_check_specified");
+ if (!mem_ctx) {
+ nt_status = NT_STATUS_NO_MEMORY;
+ } else {
+
+ E_md4hash(opt_password, nt_pw.hash);
+ if (E_deshash(opt_password, lm_pw.hash)) {
+ lm_pwd = &lm_pw;
+ } else {
+ lm_pwd = NULL;
+ }
+ nt_pwd = &nt_pw;
+
+
+ nt_status = ntlm_password_check(mem_ctx,
+ lp_lanman_auth(lp_ctx),
+ lp_ntlm_auth(lp_ctx),
+ MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT |
+ MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT,
+ challenge,
+ lm_response,
+ nt_response,
+ username,
+ username,
+ domain,
+ lm_pwd, nt_pwd, user_session_key, lm_session_key);
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ if (unix_name) {
+ asprintf(unix_name,
+ "%s%c%s", domain,
+ *lp_winbind_separator(lp_ctx),
+ username);
+ }
+ } else {
+ DEBUG(3, ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n",
+ domain, username, workstation,
+ nt_errstr(nt_status)));
+ }
+ talloc_free(mem_ctx);
+ }
+ if (error_string) {
+ *error_string = strdup(nt_errstr(nt_status));
+ }
+ return nt_status;
+
+
+}
+
+static void manage_squid_basic_request(enum stdio_helper_mode stdio_helper_mode,
+ struct loadparm_context *lp_ctx,
+ char *buf, int length, void **private1,
+ unsigned int mux_id, void **private2)
+{
+ char *user, *pass;
+ user=buf;
+
+ pass = memchr(buf, ' ', length);
+ if (!pass) {
+ DEBUG(2, ("Password not found. Denying access\n"));
+ mux_printf(mux_id, "ERR\n");
+ return;
+ }
+ *pass='\0';
+ pass++;
+
+ if (stdio_helper_mode == SQUID_2_5_BASIC) {
+ rfc1738_unescape(user);
+ rfc1738_unescape(pass);
+ }
+
+ if (check_plaintext_auth(user, pass, false)) {
+ mux_printf(mux_id, "OK\n");
+ } else {
+ mux_printf(mux_id, "ERR\n");
+ }
+}
+
+/* This is a bit hairy, but the basic idea is to do a password callback
+ to the calling application. The callback comes from within gensec */
+
+static void manage_gensec_get_pw_request(enum stdio_helper_mode stdio_helper_mode,
+ struct loadparm_context *lp_ctx,
+ char *buf, int length, void **private1,
+ unsigned int mux_id, void **password)
+{
+ DATA_BLOB in;
+ if (strlen(buf) < 2) {
+ DEBUG(1, ("query [%s] invalid", buf));
+ mux_printf(mux_id, "BH Query invalid\n");
+ return;
+ }
+
+ if (strlen(buf) > 3) {
+ in = base64_decode_data_blob(NULL, buf + 3);
+ } else {
+ in = data_blob(NULL, 0);
+ }
+
+ if (strncmp(buf, "PW ", 3) == 0) {
+
+ *password = talloc_strndup(*private1 /* hopefully the right gensec context, useful to use for talloc */,
+ (const char *)in.data, in.length);
+
+ if (*password == NULL) {
+ DEBUG(1, ("Out of memory\n"));
+ mux_printf(mux_id, "BH Out of memory\n");
+ data_blob_free(&in);
+ return;
+ }
+
+ mux_printf(mux_id, "OK\n");
+ data_blob_free(&in);
+ return;
+ }
+ DEBUG(1, ("Asked for (and expected) a password\n"));
+ mux_printf(mux_id, "BH Expected a password\n");
+ data_blob_free(&in);
+}
+
+/**
+ * Callback for password credentials. This is not async, and when
+ * GENSEC and the credentials code is made async, it will look rather
+ * different.
+ */
+
+static const char *get_password(struct cli_credentials *credentials)
+{
+ char *password = NULL;
+
+ /* Ask for a password */
+ mux_printf((unsigned int)(uintptr_t)credentials->priv_data, "PW\n");
+ credentials->priv_data = NULL;
+
+ manage_squid_request(cmdline_lp_ctx, NUM_HELPER_MODES /* bogus */, manage_gensec_get_pw_request, (void **)&password);
+ return password;
+}
+
+/**
+ Check if a string is part of a list.
+**/
+static bool in_list(const char *s, const char *list, bool casesensitive)
+{
+ char *tok;
+ size_t tok_len = 1024;
+ const char *p=list;
+
+ if (!list)
+ return false;
+
+ tok = (char *)malloc(tok_len);
+ if (!tok) {
+ return false;
+ }
+
+ while (next_token(&p, tok, LIST_SEP, tok_len)) {
+ if ((casesensitive?strcmp:strcasecmp_m)(tok,s) == 0) {
+ free(tok);
+ return true;
+ }
+ }
+ free(tok);
+ return false;
+}
+
+static void gensec_want_feature_list(struct gensec_security *state, char* feature_list)
+{
+ if (in_list("NTLMSSP_FEATURE_SESSION_KEY", feature_list, true)) {
+ DEBUG(10, ("want GENSEC_FEATURE_SESSION_KEY\n"));
+ gensec_want_feature(state, GENSEC_FEATURE_SESSION_KEY);
+ }
+ if (in_list("NTLMSSP_FEATURE_SIGN", feature_list, true)) {
+ DEBUG(10, ("want GENSEC_FEATURE_SIGN\n"));
+ gensec_want_feature(state, GENSEC_FEATURE_SIGN);
+ }
+ if (in_list("NTLMSSP_FEATURE_SEAL", feature_list, true)) {
+ DEBUG(10, ("want GENSEC_FEATURE_SEAL\n"));
+ gensec_want_feature(state, GENSEC_FEATURE_SEAL);
+ }
+}
+
+static void manage_gensec_request(enum stdio_helper_mode stdio_helper_mode,
+ struct loadparm_context *lp_ctx,
+ char *buf, int length, void **private1,
+ unsigned int mux_id, void **private2)
+{
+ DATA_BLOB in;
+ DATA_BLOB out = data_blob(NULL, 0);
+ char *out_base64 = NULL;
+ const char *reply_arg = NULL;
+ struct gensec_ntlm_state {
+ struct gensec_security *gensec_state;
+ const char *set_password;
+ };
+ struct gensec_ntlm_state *state;
+ struct tevent_context *ev;
+ struct messaging_context *msg;
+
+ NTSTATUS nt_status;
+ bool first = false;
+ const char *reply_code;
+ struct cli_credentials *creds;
+
+ static char *want_feature_list = NULL;
+ static DATA_BLOB session_key;
+
+ TALLOC_CTX *mem_ctx;
+
+ if (*private1) {
+ state = (struct gensec_ntlm_state *)*private1;
+ } else {
+ state = talloc_zero(NULL, struct gensec_ntlm_state);
+ if (!state) {
+ mux_printf(mux_id, "BH No Memory\n");
+ exit(1);
+ }
+ *private1 = state;
+ if (opt_password) {
+ state->set_password = opt_password;
+ }
+ }
+
+ if (strlen(buf) < 2) {
+ DEBUG(1, ("query [%s] invalid", buf));
+ mux_printf(mux_id, "BH Query invalid\n");
+ return;
+ }
+
+ if (strlen(buf) > 3) {
+ if(strncmp(buf, "SF ", 3) == 0) {
+ DEBUG(10, ("Setting flags to negotiate\n"));
+ talloc_free(want_feature_list);
+ want_feature_list = talloc_strndup(state, buf+3, strlen(buf)-3);
+ mux_printf(mux_id, "OK\n");
+ return;
+ }
+ in = base64_decode_data_blob(NULL, buf + 3);
+ } else {
+ in = data_blob(NULL, 0);
+ }
+
+ if (strncmp(buf, "YR", 2) == 0) {
+ if (state->gensec_state) {
+ talloc_free(state->gensec_state);
+ state->gensec_state = NULL;
+ }
+ } else if ( (strncmp(buf, "OK", 2) == 0)) {
+ /* Just return BH, like ntlm_auth from Samba 3 does. */
+ mux_printf(mux_id, "BH Command expected\n");
+ data_blob_free(&in);
+ return;
+ } else if ( (strncmp(buf, "TT ", 3) != 0) &&
+ (strncmp(buf, "KK ", 3) != 0) &&
+ (strncmp(buf, "AF ", 3) != 0) &&
+ (strncmp(buf, "NA ", 3) != 0) &&
+ (strncmp(buf, "UG", 2) != 0) &&
+ (strncmp(buf, "PW ", 3) != 0) &&
+ (strncmp(buf, "GK", 2) != 0) &&
+ (strncmp(buf, "GF", 2) != 0)) {
+ DEBUG(1, ("SPNEGO request [%s] invalid\n", buf));
+ mux_printf(mux_id, "BH SPNEGO request invalid\n");
+ data_blob_free(&in);
+ return;
+ }
+
+ ev = s4_event_context_init(state);
+ if (!ev) {
+ exit(1);
+ }
+ /* setup gensec */
+ if (!(state->gensec_state)) {
+ switch (stdio_helper_mode) {
+ case GSS_SPNEGO_CLIENT:
+ case NTLMSSP_CLIENT_1:
+ /* setup the client side */
+
+ nt_status = gensec_client_start(NULL, &state->gensec_state, ev,
+ lp_gensec_settings(NULL, lp_ctx));
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ exit(1);
+ }
+
+ break;
+ case GSS_SPNEGO_SERVER:
+ case SQUID_2_5_NTLMSSP:
+ {
+ const char *winbind_method[] = { "winbind", NULL };
+ struct auth_context *auth_context;
+
+ msg = messaging_client_init(state, lp_messaging_path(state, lp_ctx),
+ lp_iconv_convenience(lp_ctx), ev);
+ if (!msg) {
+ exit(1);
+ }
+ nt_status = auth_context_create_methods(mem_ctx,
+ winbind_method,
+ ev,
+ msg,
+ lp_ctx,
+ &auth_context);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ exit(1);
+ }
+
+ if (!NT_STATUS_IS_OK(gensec_server_start(state, ev,
+ lp_gensec_settings(state, lp_ctx),
+ auth_context, &state->gensec_state))) {
+ exit(1);
+ }
+ break;
+ }
+ default:
+ abort();
+ }
+
+ creds = cli_credentials_init(state->gensec_state);
+ cli_credentials_set_conf(creds, lp_ctx);
+ if (opt_username) {
+ cli_credentials_set_username(creds, opt_username, CRED_SPECIFIED);
+ }
+ if (opt_domain) {
+ cli_credentials_set_domain(creds, opt_domain, CRED_SPECIFIED);
+ }
+ if (state->set_password) {
+ cli_credentials_set_password(creds, state->set_password, CRED_SPECIFIED);
+ } else {
+ cli_credentials_set_password_callback(creds, get_password);
+ creds->priv_data = (void*)(uintptr_t)mux_id;
+ }
+ if (opt_workstation) {
+ cli_credentials_set_workstation(creds, opt_workstation, CRED_SPECIFIED);
+ }
+
+ switch (stdio_helper_mode) {
+ case GSS_SPNEGO_SERVER:
+ case SQUID_2_5_NTLMSSP:
+ cli_credentials_set_machine_account(creds, lp_ctx);
+ break;
+ default:
+ break;
+ }
+
+ gensec_set_credentials(state->gensec_state, creds);
+ gensec_want_feature_list(state->gensec_state, want_feature_list);
+
+ switch (stdio_helper_mode) {
+ case GSS_SPNEGO_CLIENT:
+ case GSS_SPNEGO_SERVER:
+ nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_SPNEGO);
+ if (!in.length) {
+ first = true;
+ }
+ break;
+ case NTLMSSP_CLIENT_1:
+ if (!in.length) {
+ first = true;
+ }
+ /* fall through */
+ case SQUID_2_5_NTLMSSP:
+ nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_NTLMSSP);
+ break;
+ default:
+ abort();
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(1, ("GENSEC mech failed to start: %s\n", nt_errstr(nt_status)));
+ mux_printf(mux_id, "BH GENSEC mech failed to start\n");
+ return;
+ }
+
+ }
+
+ /* update */
+ mem_ctx = talloc_named(NULL, 0, "manage_gensec_request internal mem_ctx");
+
+ if (strncmp(buf, "PW ", 3) == 0) {
+ state->set_password = talloc_strndup(state,
+ (const char *)in.data,
+ in.length);
+
+ cli_credentials_set_password(gensec_get_credentials(state->gensec_state),
+ state->set_password,
+ CRED_SPECIFIED);
+ mux_printf(mux_id, "OK\n");
+ data_blob_free(&in);
+ talloc_free(mem_ctx);
+ return;
+ }
+
+ if (strncmp(buf, "UG", 2) == 0) {
+ int i;
+ char *grouplist = NULL;
+ struct auth_session_info *session_info;
+
+ nt_status = gensec_session_info(state->gensec_state, &session_info);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(1, ("gensec_session_info failed: %s\n", nt_errstr(nt_status)));
+ mux_printf(mux_id, "BH %s\n", nt_errstr(nt_status));
+ data_blob_free(&in);
+ talloc_free(mem_ctx);
+ return;
+ }
+
+ /* get the string onto the context */
+ grouplist = talloc_strdup(mem_ctx, "");
+
+ for (i=0; i<session_info->security_token->num_sids; i++) {
+ struct security_token *token = session_info->security_token;
+ const char *sidstr = dom_sid_string(session_info,
+ token->sids[i]);
+ grouplist = talloc_asprintf_append_buffer(grouplist, "%s,", sidstr);
+ }
+
+ mux_printf(mux_id, "GL %s\n", grouplist);
+ talloc_free(session_info);
+ data_blob_free(&in);
+ talloc_free(mem_ctx);
+ return;
+ }
+
+ if (strncmp(buf, "GK", 2) == 0) {
+ char *base64_key;
+ DEBUG(10, ("Requested session key\n"));
+ nt_status = gensec_session_key(state->gensec_state, &session_key);
+ if(!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(1, ("gensec_session_key failed: %s\n", nt_errstr(nt_status)));
+ mux_printf(mux_id, "BH No session key\n");
+ talloc_free(mem_ctx);
+ return;
+ } else {
+ base64_key = base64_encode_data_blob(state, session_key);
+ mux_printf(mux_id, "GK %s\n", base64_key);
+ talloc_free(base64_key);
+ }
+ talloc_free(mem_ctx);
+ return;
+ }
+
+ if (strncmp(buf, "GF", 2) == 0) {
+ struct gensec_ntlmssp_state *gensec_ntlmssp_state;
+ uint32_t neg_flags;
+
+ gensec_ntlmssp_state = talloc_get_type(state->gensec_state->private_data,
+ struct gensec_ntlmssp_state);
+ neg_flags = gensec_ntlmssp_state->neg_flags;
+
+ DEBUG(10, ("Requested negotiated feature flags\n"));
+ mux_printf(mux_id, "GF 0x%08x\n", neg_flags);
+ return;
+ }
+
+ nt_status = gensec_update(state->gensec_state, mem_ctx, in, &out);
+
+ /* don't leak 'bad password'/'no such user' info to the network client */
+ nt_status = auth_nt_status_squash(nt_status);
+
+ if (out.length) {
+ out_base64 = base64_encode_data_blob(mem_ctx, out);
+ } else {
+ out_base64 = NULL;
+ }
+
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ reply_arg = "*";
+ if (first) {
+ reply_code = "YR";
+ } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) {
+ reply_code = "KK";
+ } else if (state->gensec_state->gensec_role == GENSEC_SERVER) {
+ reply_code = "TT";
+ } else {
+ abort();
+ }
+
+
+ } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) {
+ reply_code = "BH NT_STATUS_ACCESS_DENIED";
+ reply_arg = nt_errstr(nt_status);
+ DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
+ } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_UNSUCCESSFUL)) {
+ reply_code = "BH NT_STATUS_UNSUCCESSFUL";
+ reply_arg = nt_errstr(nt_status);
+ DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
+ } else if (!NT_STATUS_IS_OK(nt_status)) {
+ reply_code = "NA";
+ reply_arg = nt_errstr(nt_status);
+ DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
+ } else if /* OK */ (state->gensec_state->gensec_role == GENSEC_SERVER) {
+ struct auth_session_info *session_info;
+
+ nt_status = gensec_session_info(state->gensec_state, &session_info);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ reply_code = "BH Failed to retrive session info";
+ reply_arg = nt_errstr(nt_status);
+ DEBUG(1, ("GENSEC failed to retreive the session info: %s\n", nt_errstr(nt_status)));
+ } else {
+
+ reply_code = "AF";
+ reply_arg = talloc_asprintf(state->gensec_state,
+ "%s%s%s", session_info->server_info->domain_name,
+ lp_winbind_separator(lp_ctx), session_info->server_info->account_name);
+ talloc_free(session_info);
+ }
+ } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) {
+ reply_code = "AF";
+ reply_arg = out_base64;
+ } else {
+ abort();
+ }
+
+ switch (stdio_helper_mode) {
+ case GSS_SPNEGO_SERVER:
+ mux_printf(mux_id, "%s %s %s\n", reply_code,
+ out_base64 ? out_base64 : "*",
+ reply_arg ? reply_arg : "*");
+ break;
+ default:
+ if (out_base64) {
+ mux_printf(mux_id, "%s %s\n", reply_code, out_base64);
+ } else if (reply_arg) {
+ mux_printf(mux_id, "%s %s\n", reply_code, reply_arg);
+ } else {
+ mux_printf(mux_id, "%s\n", reply_code);
+ }
+ }
+
+ talloc_free(mem_ctx);
+ return;
+}
+
+static void manage_ntlm_server_1_request(enum stdio_helper_mode stdio_helper_mode,
+ struct loadparm_context *lp_ctx,
+ char *buf, int length, void **private1,
+ unsigned int mux_id, void **private2)
+{
+ char *request, *parameter;
+ static DATA_BLOB challenge;
+ static DATA_BLOB lm_response;
+ static DATA_BLOB nt_response;
+ static char *full_username;
+ static char *username;
+ static char *domain;
+ static char *plaintext_password;
+ static bool ntlm_server_1_user_session_key;
+ static bool ntlm_server_1_lm_session_key;
+
+ if (strequal(buf, ".")) {
+ if (!full_username && !username) {
+ mux_printf(mux_id, "Error: No username supplied!\n");
+ } else if (plaintext_password) {
+ /* handle this request as plaintext */
+ if (!full_username) {
+ if (asprintf(&full_username, "%s%c%s", domain, *lp_winbind_separator(lp_ctx), username) == -1) {
+ mux_printf(mux_id, "Error: Out of memory in asprintf!\n.\n");
+ return;
+ }
+ }
+ if (check_plaintext_auth(full_username, plaintext_password, false)) {
+ mux_printf(mux_id, "Authenticated: Yes\n");
+ } else {
+ mux_printf(mux_id, "Authenticated: No\n");
+ }
+ } else if (!lm_response.data && !nt_response.data) {
+ mux_printf(mux_id, "Error: No password supplied!\n");
+ } else if (!challenge.data) {
+ mux_printf(mux_id, "Error: No lanman-challenge supplied!\n");
+ } else {
+ char *error_string = NULL;
+ DATA_BLOB lm_key;
+ DATA_BLOB user_session_key;
+ uint32_t flags = 0;
+
+ if (full_username && !username) {
+ SAFE_FREE(username);
+ SAFE_FREE(domain);
+ if (!parse_ntlm_auth_domain_user(full_username, &username,
+ &domain,
+ *lp_winbind_separator(lp_ctx))) {
+ /* username might be 'tainted', don't print into our new-line deleimianted stream */
+ mux_printf(mux_id, "Error: Could not parse into domain and username\n");
+ }
+ }
+
+ if (!domain) {
+ domain = smb_xstrdup(lp_workgroup(lp_ctx));
+ }
+
+ if (ntlm_server_1_lm_session_key)
+ flags |= NTLM_AUTH_FLAG_LMKEY;
+
+ if (ntlm_server_1_user_session_key)
+ flags |= NTLM_AUTH_FLAG_USER_SESSION_KEY;
+
+ if (!NT_STATUS_IS_OK(
+ local_pw_check_specified(lp_ctx,
+ username,
+ domain,
+ lp_netbios_name(lp_ctx),
+ &challenge,
+ &lm_response,
+ &nt_response,
+ flags,
+ &lm_key,
+ &user_session_key,
+ &error_string,
+ NULL))) {
+
+ mux_printf(mux_id, "Authenticated: No\n");
+ mux_printf(mux_id, "Authentication-Error: %s\n.\n", error_string);
+ SAFE_FREE(error_string);
+ } else {
+ static char zeros[16];
+ char *hex_lm_key;
+ char *hex_user_session_key;
+
+ mux_printf(mux_id, "Authenticated: Yes\n");
+
+ if (ntlm_server_1_lm_session_key
+ && lm_key.length
+ && (memcmp(zeros, lm_key.data,
+ lm_key.length) != 0)) {
+ hex_encode(lm_key.data,
+ lm_key.length,
+ &hex_lm_key);
+ mux_printf(mux_id, "LANMAN-Session-Key: %s\n", hex_lm_key);
+ SAFE_FREE(hex_lm_key);
+ }
+
+ if (ntlm_server_1_user_session_key
+ && user_session_key.length
+ && (memcmp(zeros, user_session_key.data,
+ user_session_key.length) != 0)) {
+ hex_encode(user_session_key.data,
+ user_session_key.length,
+ &hex_user_session_key);
+ mux_printf(mux_id, "User-Session-Key: %s\n", hex_user_session_key);
+ SAFE_FREE(hex_user_session_key);
+ }
+ }
+ }
+ /* clear out the state */
+ challenge = data_blob(NULL, 0);
+ nt_response = data_blob(NULL, 0);
+ lm_response = data_blob(NULL, 0);
+ SAFE_FREE(full_username);
+ SAFE_FREE(username);
+ SAFE_FREE(domain);
+ SAFE_FREE(plaintext_password);
+ ntlm_server_1_user_session_key = false;
+ ntlm_server_1_lm_session_key = false;
+ mux_printf(mux_id, ".\n");
+
+ return;
+ }
+
+ request = buf;
+
+ /* Indicates a base64 encoded structure */
+ parameter = strstr(request, ":: ");
+ if (!parameter) {
+ parameter = strstr(request, ": ");
+
+ if (!parameter) {
+ DEBUG(0, ("Parameter not found!\n"));
+ mux_printf(mux_id, "Error: Parameter not found!\n.\n");
+ return;
+ }
+
+ parameter[0] ='\0';
+ parameter++;
+ parameter[0] ='\0';
+ parameter++;
+
+ } else {
+ parameter[0] ='\0';
+ parameter++;
+ parameter[0] ='\0';
+ parameter++;
+ parameter[0] ='\0';
+ parameter++;
+
+ base64_decode_inplace(parameter);
+ }
+
+ if (strequal(request, "LANMAN-Challenge")) {
+ challenge = strhex_to_data_blob(NULL, parameter);
+ if (challenge.length != 8) {
+ mux_printf(mux_id, "Error: hex decode of %s failed! (got %d bytes, expected 8)\n.\n",
+ parameter,
+ (int)challenge.length);
+ challenge = data_blob(NULL, 0);
+ }
+ } else if (strequal(request, "NT-Response")) {
+ nt_response = strhex_to_data_blob(NULL, parameter);
+ if (nt_response.length < 24) {
+ mux_printf(mux_id, "Error: hex decode of %s failed! (only got %d bytes, needed at least 24)\n.\n",
+ parameter,
+ (int)nt_response.length);
+ nt_response = data_blob(NULL, 0);
+ }
+ } else if (strequal(request, "LANMAN-Response")) {
+ lm_response = strhex_to_data_blob(NULL, parameter);
+ if (lm_response.length != 24) {
+ mux_printf(mux_id, "Error: hex decode of %s failed! (got %d bytes, expected 24)\n.\n",
+ parameter,
+ (int)lm_response.length);
+ lm_response = data_blob(NULL, 0);
+ }
+ } else if (strequal(request, "Password")) {
+ plaintext_password = smb_xstrdup(parameter);
+ } else if (strequal(request, "NT-Domain")) {
+ domain = smb_xstrdup(parameter);
+ } else if (strequal(request, "Username")) {
+ username = smb_xstrdup(parameter);
+ } else if (strequal(request, "Full-Username")) {
+ full_username = smb_xstrdup(parameter);
+ } else if (strequal(request, "Request-User-Session-Key")) {
+ ntlm_server_1_user_session_key = strequal(parameter, "Yes");
+ } else if (strequal(request, "Request-LanMan-Session-Key")) {
+ ntlm_server_1_lm_session_key = strequal(parameter, "Yes");
+ } else {
+ mux_printf(mux_id, "Error: Unknown request %s\n.\n", request);
+ }
+}
+
+static void manage_squid_request(struct loadparm_context *lp_ctx, enum stdio_helper_mode helper_mode,
+ stdio_helper_function fn, void **private2)
+{
+ char *buf;
+ char tmp[INITIAL_BUFFER_SIZE+1];
+ unsigned int mux_id = 0;
+ int length, buf_size = 0;
+ char *c;
+ struct mux_private {
+ unsigned int max_mux;
+ void **private_pointers;
+ };
+
+ static struct mux_private *mux_private;
+ static void *normal_private;
+ void **private1;
+
+ buf = talloc_strdup(NULL, "");
+
+ if (buf == NULL) {
+ DEBUG(0, ("Failed to allocate memory for reading the input "
+ "buffer.\n"));
+ x_fprintf(x_stdout, "ERR\n");
+ return;
+ }
+
+ do {
+ /* this is not a typo - x_fgets doesn't work too well under
+ * squid */
+ if (fgets(tmp, INITIAL_BUFFER_SIZE, stdin) == NULL) {
+ if (ferror(stdin)) {
+ DEBUG(1, ("fgets() failed! dying..... errno=%d "
+ "(%s)\n", ferror(stdin),
+ strerror(ferror(stdin))));
+
+ exit(1); /* BIIG buffer */
+ }
+ exit(0);
+ }
+
+ buf = talloc_strdup_append_buffer(buf, tmp);
+ buf_size += INITIAL_BUFFER_SIZE;
+
+ if (buf_size > MAX_BUFFER_SIZE) {
+ DEBUG(0, ("Invalid Request (too large)\n"));
+ x_fprintf(x_stdout, "ERR\n");
+ talloc_free(buf);
+ return;
+ }
+
+ c = strchr(buf, '\n');
+ } while (c == NULL);
+
+ *c = '\0';
+ length = c-buf;
+
+ DEBUG(10, ("Got '%s' from squid (length: %d).\n",buf,length));
+
+ if (buf[0] == '\0') {
+ DEBUG(0, ("Invalid Request (empty)\n"));
+ x_fprintf(x_stdout, "ERR\n");
+ talloc_free(buf);
+ return;
+ }
+
+ if (opt_multiplex) {
+ if (sscanf(buf, "%u ", &mux_id) != 1) {
+ DEBUG(0, ("Invalid Request - no multiplex id\n"));
+ x_fprintf(x_stdout, "ERR\n");
+ talloc_free(buf);
+ return;
+ }
+ if (!mux_private) {
+ mux_private = talloc(NULL, struct mux_private);
+ mux_private->max_mux = 0;
+ mux_private->private_pointers = NULL;
+ }
+
+ c=strchr(buf,' ');
+ if (!c) {
+ DEBUG(0, ("Invalid Request - no data after multiplex id\n"));
+ x_fprintf(x_stdout, "ERR\n");
+ talloc_free(buf);
+ return;
+ }
+ c++;
+ if (mux_id >= mux_private->max_mux) {
+ unsigned int prev_max = mux_private->max_mux;
+ mux_private->max_mux = mux_id + 1;
+ mux_private->private_pointers
+ = talloc_realloc(mux_private,
+ mux_private->private_pointers,
+ void *, mux_private->max_mux);
+ memset(&mux_private->private_pointers[prev_max], '\0',
+ (sizeof(*mux_private->private_pointers) * (mux_private->max_mux - prev_max)));
+ };
+
+ private1 = &mux_private->private_pointers[mux_id];
+ } else {
+ c = buf;
+ private1 = &normal_private;
+ }
+
+ fn(helper_mode, lp_ctx, c, length, private1, mux_id, private2);
+ talloc_free(buf);
+}
+
+static void squid_stream(struct loadparm_context *lp_ctx,
+ enum stdio_helper_mode stdio_mode,
+ stdio_helper_function fn) {
+ /* initialize FDescs */
+ x_setbuf(x_stdout, NULL);
+ x_setbuf(x_stderr, NULL);
+ while(1) {
+ manage_squid_request(lp_ctx, stdio_mode, fn, NULL);
+ }
+}
+
+
+/* Main program */
+
+enum {
+ OPT_USERNAME = 1000,
+ OPT_DOMAIN,
+ OPT_WORKSTATION,
+ OPT_CHALLENGE,
+ OPT_RESPONSE,
+ OPT_LM,
+ OPT_NT,
+ OPT_PASSWORD,
+ OPT_LM_KEY,
+ OPT_USER_SESSION_KEY,
+ OPT_DIAGNOSTICS,
+ OPT_REQUIRE_MEMBERSHIP,
+ OPT_MULTIPLEX,
+ OPT_USE_CACHED_CREDS,
+};
+
+int main(int argc, const char **argv)
+{
+ static const char *helper_protocol;
+ int opt;
+
+ poptContext pc;
+
+ /* NOTE: DO NOT change this interface without considering the implications!
+ This is an external interface, which other programs will use to interact
+ with this helper.
+ */
+
+ /* We do not use single-letter command abbreviations, because they harm future
+ interface stability. */
+
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ { "helper-protocol", 0, POPT_ARG_STRING, &helper_protocol, OPT_DOMAIN, "operate as a stdio-based helper", "helper protocol to use"},
+ { "domain", 0, POPT_ARG_STRING, &opt_domain, OPT_DOMAIN, "domain name"},
+ { "workstation", 0, POPT_ARG_STRING, &opt_workstation, OPT_WORKSTATION, "workstation"},
+ { "username", 0, POPT_ARG_STRING, &opt_username, OPT_PASSWORD, "Username"},
+ { "password", 0, POPT_ARG_STRING, &opt_password, OPT_PASSWORD, "User's plaintext password"},
+ { "multiplex", 0, POPT_ARG_NONE, &opt_multiplex, OPT_MULTIPLEX, "Multiplex Mode"},
+ { "use-cached-creds", 0, POPT_ARG_NONE, &use_cached_creds, OPT_USE_CACHED_CREDS, "silently ignored for compatibility reasons"},
+ POPT_COMMON_SAMBA
+ POPT_COMMON_VERSION
+ { NULL }
+ };
+
+ /* Samba client initialisation */
+
+ setup_logging(NULL, DEBUG_STDERR);
+
+ /* Parse options */
+
+ pc = poptGetContext("ntlm_auth", argc, argv, long_options, 0);
+
+ /* Parse command line options */
+
+ if (argc == 1) {
+ poptPrintHelp(pc, stderr, 0);
+ return 1;
+ }
+
+ pc = poptGetContext(NULL, argc, (const char **)argv, long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ if (opt < -1) {
+ break;
+ }
+ }
+ if (opt < -1) {
+ fprintf(stderr, "%s: %s\n",
+ poptBadOption(pc, POPT_BADOPTION_NOALIAS),
+ poptStrerror(opt));
+ return 1;
+ }
+
+ gensec_init(cmdline_lp_ctx);
+
+ if (opt_domain == NULL) {
+ opt_domain = lp_workgroup(cmdline_lp_ctx);
+ }
+
+ if (helper_protocol) {
+ int i;
+ for (i=0; i<NUM_HELPER_MODES; i++) {
+ if (strcmp(helper_protocol, stdio_helper_protocols[i].name) == 0) {
+ squid_stream(cmdline_lp_ctx, stdio_helper_protocols[i].mode, stdio_helper_protocols[i].fn);
+ exit(0);
+ }
+ }
+ x_fprintf(x_stderr, "unknown helper protocol [%s]\n\nValid helper protools:\n\n", helper_protocol);
+
+ for (i=0; i<NUM_HELPER_MODES; i++) {
+ x_fprintf(x_stderr, "%s\n", stdio_helper_protocols[i].name);
+ }
+
+ exit(1);
+ }
+
+ if (!opt_username) {
+ x_fprintf(x_stderr, "username must be specified!\n\n");
+ poptPrintHelp(pc, stderr, 0);
+ exit(1);
+ }
+
+ if (opt_workstation == NULL) {
+ opt_workstation = lp_netbios_name(cmdline_lp_ctx);
+ }
+
+ if (!opt_password) {
+ opt_password = getpass("password: ");
+ }
+
+ {
+ char *user;
+
+ asprintf(&user, "%s%c%s", opt_domain, *lp_winbind_separator(cmdline_lp_ctx), opt_username);
+ if (!check_plaintext_auth(user, opt_password, true)) {
+ return 1;
+ }
+ }
+
+ /* Exit code */
+
+ poptFreeContext(pc);
+ return 0;
+}
diff --git a/source4/utils/oLschema2ldif.c b/source4/utils/oLschema2ldif.c
new file mode 100644
index 0000000000..701d221046
--- /dev/null
+++ b/source4/utils/oLschema2ldif.c
@@ -0,0 +1,604 @@
+/*
+ ldb database library
+
+ Copyright (C) Simo Sorce 2005
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: oLschema2ldif
+ *
+ * Description: utility to convert an OpenLDAP schema into AD LDIF
+ *
+ * Author: Simo Sorce
+ */
+
+#include "includes.h"
+#include "ldb.h"
+#include "tools/cmdline.h"
+#include "dsdb/samdb/samdb.h"
+
+#define SCHEMA_UNKNOWN 0
+#define SCHEMA_NAME 1
+#define SCHEMA_SUP 2
+#define SCHEMA_STRUCTURAL 3
+#define SCHEMA_ABSTRACT 4
+#define SCHEMA_AUXILIARY 5
+#define SCHEMA_MUST 6
+#define SCHEMA_MAY 7
+#define SCHEMA_SINGLE_VALUE 8
+#define SCHEMA_EQUALITY 9
+#define SCHEMA_ORDERING 10
+#define SCHEMA_SUBSTR 11
+#define SCHEMA_SYNTAX 12
+#define SCHEMA_DESC 13
+
+struct schema_conv {
+ int count;
+ int failures;
+};
+
+struct schema_token {
+ int type;
+ char *value;
+};
+
+struct ldb_context *ldb_ctx;
+struct ldb_dn *basedn;
+
+static int check_braces(const char *string)
+{
+ int b;
+ char *c;
+
+ b = 0;
+ if ((c = strchr(string, '(')) == NULL) {
+ return -1;
+ }
+ b++;
+ c++;
+ while (b) {
+ c = strpbrk(c, "()");
+ if (c == NULL) return 1;
+ if (*c == '(') b++;
+ if (*c == ')') b--;
+ c++;
+ }
+ return 0;
+}
+
+static char *skip_spaces(char *string) {
+ return (string + strspn(string, " \t\n"));
+}
+
+static int add_multi_string(struct ldb_message *msg, const char *attr, char *values)
+{
+ char *c;
+ char *s;
+ int n;
+
+ c = skip_spaces(values);
+ while (*c) {
+ n = strcspn(c, " \t$");
+ s = talloc_strndup(msg, c, n);
+ if (ldb_msg_add_string(msg, attr, s) != 0) {
+ return -1;
+ }
+ c += n;
+ c += strspn(c, " \t$");
+ }
+
+ return 0;
+}
+
+#define MSG_ADD_STRING(a, v) do { if (ldb_msg_add_string(msg, a, v) != 0) goto failed; } while(0)
+#define MSG_ADD_M_STRING(a, v) do { if (add_multi_string(msg, a, v) != 0) goto failed; } while(0)
+
+static char *get_def_value(TALLOC_CTX *ctx, char **string)
+{
+ char *c = *string;
+ char *value;
+ int n;
+
+ if (*c == '\'') {
+ c++;
+ n = strcspn(c, "\'");
+ value = talloc_strndup(ctx, c, n);
+ c += n;
+ c++; /* skip closing \' */
+ } else {
+ n = strcspn(c, " \t\n");
+ value = talloc_strndup(ctx, c, n);
+ c += n;
+ }
+ *string = c;
+
+ return value;
+}
+
+static struct schema_token *get_next_schema_token(TALLOC_CTX *ctx, char **string)
+{
+ char *c = skip_spaces(*string);
+ char *type;
+ struct schema_token *token;
+ int n;
+
+ token = talloc(ctx, struct schema_token);
+
+ n = strcspn(c, " \t\n");
+ type = talloc_strndup(token, c, n);
+ c += n;
+ c = skip_spaces(c);
+
+ if (strcasecmp("NAME", type) == 0) {
+ talloc_free(type);
+ token->type = SCHEMA_NAME;
+ /* we do not support aliases so we get only the first name given and skip others */
+ if (*c == '(') {
+ char *s = strchr(c, ')');
+ if (s == NULL) return NULL;
+ s = skip_spaces(s);
+ *string = s;
+
+ c++;
+ c = skip_spaces(c);
+ }
+
+ token->value = get_def_value(ctx, &c);
+
+ if (*string < c) { /* single name */
+ c = skip_spaces(c);
+ *string = c;
+ }
+ return token;
+ }
+ if (strcasecmp("SUP", type) == 0) {
+ talloc_free(type);
+ token->type = SCHEMA_SUP;
+
+ if (*c == '(') {
+ c++;
+ n = strcspn(c, ")");
+ token->value = talloc_strndup(ctx, c, n);
+ c += n;
+ c++;
+ } else {
+ token->value = get_def_value(ctx, &c);
+ }
+
+ c = skip_spaces(c);
+ *string = c;
+ return token;
+ }
+
+ if (strcasecmp("STRUCTURAL", type) == 0) {
+ talloc_free(type);
+ token->type = SCHEMA_STRUCTURAL;
+ *string = c;
+ return token;
+ }
+
+ if (strcasecmp("ABSTRACT", type) == 0) {
+ talloc_free(type);
+ token->type = SCHEMA_ABSTRACT;
+ *string = c;
+ return token;
+ }
+
+ if (strcasecmp("AUXILIARY", type) == 0) {
+ talloc_free(type);
+ token->type = SCHEMA_AUXILIARY;
+ *string = c;
+ return token;
+ }
+
+ if (strcasecmp("MUST", type) == 0) {
+ talloc_free(type);
+ token->type = SCHEMA_MUST;
+
+ if (*c == '(') {
+ c++;
+ n = strcspn(c, ")");
+ token->value = talloc_strndup(ctx, c, n);
+ c += n;
+ c++;
+ } else {
+ token->value = get_def_value(ctx, &c);
+ }
+
+ c = skip_spaces(c);
+ *string = c;
+ return token;
+ }
+
+ if (strcasecmp("MAY", type) == 0) {
+ talloc_free(type);
+ token->type = SCHEMA_MAY;
+
+ if (*c == '(') {
+ c++;
+ n = strcspn(c, ")");
+ token->value = talloc_strndup(ctx, c, n);
+ c += n;
+ c++;
+ } else {
+ token->value = get_def_value(ctx, &c);
+ }
+
+ c = skip_spaces(c);
+ *string = c;
+ return token;
+ }
+
+ if (strcasecmp("SINGLE-VALUE", type) == 0) {
+ talloc_free(type);
+ token->type = SCHEMA_SINGLE_VALUE;
+ *string = c;
+ return token;
+ }
+
+ if (strcasecmp("EQUALITY", type) == 0) {
+ talloc_free(type);
+ token->type = SCHEMA_EQUALITY;
+
+ token->value = get_def_value(ctx, &c);
+
+ c = skip_spaces(c);
+ *string = c;
+ return token;
+ }
+
+ if (strcasecmp("ORDERING", type) == 0) {
+ talloc_free(type);
+ token->type = SCHEMA_ORDERING;
+
+ token->value = get_def_value(ctx, &c);
+
+ c = skip_spaces(c);
+ *string = c;
+ return token;
+ }
+
+ if (strcasecmp("SUBSTR", type) == 0) {
+ talloc_free(type);
+ token->type = SCHEMA_SUBSTR;
+
+ token->value = get_def_value(ctx, &c);
+
+ c = skip_spaces(c);
+ *string = c;
+ return token;
+ }
+
+ if (strcasecmp("SYNTAX", type) == 0) {
+ talloc_free(type);
+ token->type = SCHEMA_SYNTAX;
+
+ token->value = get_def_value(ctx, &c);
+
+ c = skip_spaces(c);
+ *string = c;
+ return token;
+ }
+
+ if (strcasecmp("DESC", type) == 0) {
+ talloc_free(type);
+ token->type = SCHEMA_DESC;
+
+ token->value = get_def_value(ctx, &c);
+
+ c = skip_spaces(c);
+ *string = c;
+ return token;
+ }
+
+ token->type = SCHEMA_UNKNOWN;
+ token->value = type;
+ if (*c == ')') {
+ *string = c;
+ return token;
+ }
+ if (*c == '\'') {
+ c = strchr(++c, '\'');
+ c++;
+ } else {
+ c += strcspn(c, " \t\n");
+ }
+ c = skip_spaces(c);
+ *string = c;
+
+ return token;
+}
+
+static struct ldb_message *process_entry(TALLOC_CTX *mem_ctx, const char *entry)
+{
+ TALLOC_CTX *ctx;
+ struct ldb_message *msg;
+ struct schema_token *token;
+ char *c, *s;
+ int n;
+
+ ctx = talloc_new(mem_ctx);
+ msg = ldb_msg_new(ctx);
+
+ ldb_msg_add_string(msg, "objectClass", "top");
+
+ c = talloc_strdup(ctx, entry);
+ if (!c) return NULL;
+
+ c = skip_spaces(c);
+
+ switch (*c) {
+ case 'a':
+ if (strncmp(c, "attributetype", 13) == 0) {
+ c += 13;
+ MSG_ADD_STRING("objectClass", "attributeSchema");
+ break;
+ }
+ goto failed;
+ case 'o':
+ if (strncmp(c, "objectclass", 11) == 0) {
+ c += 11;
+ MSG_ADD_STRING("objectClass", "classSchema");
+ break;
+ }
+ goto failed;
+ default:
+ goto failed;
+ }
+
+ c = strchr(c, '(');
+ if (c == NULL) goto failed;
+ c++;
+
+ c = skip_spaces(c);
+
+ /* get attributeID */
+ n = strcspn(c, " \t");
+ s = talloc_strndup(msg, c, n);
+ MSG_ADD_STRING("attributeID", s);
+ c += n;
+ c = skip_spaces(c);
+
+ while (*c != ')') {
+ token = get_next_schema_token(msg, &c);
+ if (!token) goto failed;
+
+ switch (token->type) {
+ case SCHEMA_NAME:
+ MSG_ADD_STRING("cn", token->value);
+ MSG_ADD_STRING("name", token->value);
+ MSG_ADD_STRING("lDAPDisplayName", token->value);
+ msg->dn = ldb_dn_copy(msg, basedn);
+ ldb_dn_add_child_fmt(msg->dn, "CN=%s,CN=Schema,CN=Configuration", token->value);
+ break;
+
+ case SCHEMA_SUP:
+ MSG_ADD_M_STRING("subClassOf", token->value);
+ break;
+
+ case SCHEMA_STRUCTURAL:
+ MSG_ADD_STRING("objectClassCategory", "1");
+ break;
+
+ case SCHEMA_ABSTRACT:
+ MSG_ADD_STRING("objectClassCategory", "2");
+ break;
+
+ case SCHEMA_AUXILIARY:
+ MSG_ADD_STRING("objectClassCategory", "3");
+ break;
+
+ case SCHEMA_MUST:
+ MSG_ADD_M_STRING("mustContain", token->value);
+ break;
+
+ case SCHEMA_MAY:
+ MSG_ADD_M_STRING("mayContain", token->value);
+ break;
+
+ case SCHEMA_SINGLE_VALUE:
+ MSG_ADD_STRING("isSingleValued", "TRUE");
+ break;
+
+ case SCHEMA_EQUALITY:
+ /* TODO */
+ break;
+
+ case SCHEMA_ORDERING:
+ /* TODO */
+ break;
+
+ case SCHEMA_SUBSTR:
+ /* TODO */
+ break;
+
+ case SCHEMA_SYNTAX:
+ {
+ const struct dsdb_syntax *map =
+ find_syntax_map_by_standard_oid(token->value);
+ if (!map) {
+ break;
+ }
+ MSG_ADD_STRING("attributeSyntax", map->attributeSyntax_oid);
+ break;
+ }
+ case SCHEMA_DESC:
+ MSG_ADD_STRING("description", token->value);
+ break;
+
+ default:
+ fprintf(stderr, "Unknown Definition: %s\n", token->value);
+ }
+ }
+
+ talloc_steal(mem_ctx, msg);
+ talloc_free(ctx);
+ return msg;
+
+failed:
+ talloc_free(ctx);
+ return NULL;
+}
+
+static struct schema_conv process_file(FILE *in, FILE *out)
+{
+ TALLOC_CTX *ctx;
+ struct schema_conv ret;
+ char *entry;
+ int c, t, line;
+ struct ldb_ldif ldif;
+
+ ldif.changetype = LDB_CHANGETYPE_NONE;
+
+ ctx = talloc_new(NULL);
+
+ ret.count = 0;
+ ret.failures = 0;
+ line = 0;
+
+ while ((c = fgetc(in)) != EOF) {
+ line++;
+ /* fprintf(stderr, "Parsing line %d\n", line); */
+ if (c == '#') {
+ do {
+ c = fgetc(in);
+ } while (c != EOF && c != '\n');
+ continue;
+ }
+ if (c == '\n') {
+ continue;
+ }
+
+ t = 0;
+ entry = talloc_array(ctx, char, 1024);
+ if (entry == NULL) exit(-1);
+
+ do {
+ if (c == '\n') {
+ entry[t] = '\0';
+ if (check_braces(entry) == 0) {
+ ret.count++;
+ ldif.msg = process_entry(ctx, entry);
+ if (ldif.msg == NULL) {
+ ret.failures++;
+ fprintf(stderr, "No valid msg from entry \n[%s]\n at line %d\n", entry, line);
+ break;
+ }
+ ldb_ldif_write_file(ldb_ctx, out, &ldif);
+ break;
+ }
+ line++;
+ } else {
+ entry[t] = c;
+ t++;
+ }
+ if ((t % 1023) == 0) {
+ entry = talloc_realloc(ctx, entry, char, t + 1024);
+ if (entry == NULL) exit(-1);
+ }
+ } while ((c = fgetc(in)) != EOF);
+
+ if (c != '\n') {
+ entry[t] = '\0';
+ if (check_braces(entry) == 0) {
+ ret.count++;
+ ldif.msg = process_entry(ctx, entry);
+ if (ldif.msg == NULL) {
+ ret.failures++;
+ fprintf(stderr, "No valid msg from entry \n[%s]\n at line %d\n", entry, line);
+ break;
+ }
+ ldb_ldif_write_file(ldb_ctx, out, &ldif);
+ } else {
+ fprintf(stderr, "malformed entry on line %d\n", line);
+ ret.failures++;
+ }
+ }
+
+ if (c == EOF) break;
+ }
+
+ return ret;
+}
+
+static void usage(void)
+{
+ printf("Usage: oLschema2ldif -H NONE <options>\n");
+ printf("\nConvert OpenLDAP schema to AD-like LDIF format\n\n");
+ printf("Options:\n");
+ printf(" -I inputfile inputfile of OpenLDAP style schema otherwise STDIN\n");
+ printf(" -O outputfile outputfile otherwise STDOUT\n");
+ printf(" -o options pass options like modules to activate\n");
+ printf(" e.g: -o modules:timestamps\n");
+ printf("\n");
+ printf("Converts records from an openLdap formatted schema to an ldif schema\n\n");
+ exit(1);
+}
+
+ int main(int argc, const char **argv)
+{
+ TALLOC_CTX *ctx;
+ struct schema_conv ret;
+ struct ldb_cmdline *options;
+ FILE *in = stdin;
+ FILE *out = stdout;
+ ctx = talloc_new(NULL);
+ ldb_ctx = ldb_init(ctx, NULL);
+
+ setenv("LDB_URL", "NONE", 1);
+ options = ldb_cmdline_process(ldb_ctx, argc, argv, usage);
+
+ if (options->basedn == NULL) {
+ perror("Base DN not specified");
+ exit(1);
+ } else {
+ basedn = ldb_dn_new(ctx, ldb_ctx, options->basedn);
+ if ( ! ldb_dn_validate(basedn)) {
+ perror("Malformed Base DN");
+ exit(1);
+ }
+ }
+
+ if (options->input) {
+ in = fopen(options->input, "r");
+ if (!in) {
+ perror(options->input);
+ exit(1);
+ }
+ }
+ if (options->output) {
+ out = fopen(options->output, "w");
+ if (!out) {
+ perror(options->output);
+ exit(1);
+ }
+ }
+
+ ret = process_file(in, out);
+
+ fclose(in);
+ fclose(out);
+
+ printf("Converted %d records with %d failures\n", ret.count, ret.failures);
+
+ return 0;
+}
diff --git a/source4/utils/setntacl.c b/source4/utils/setntacl.c
new file mode 100644
index 0000000000..3a008a4c37
--- /dev/null
+++ b/source4/utils/setntacl.c
@@ -0,0 +1,28 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Set NT ACLs on UNIX files.
+
+ Copyright (C) Tim Potter <tpot@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+int main(int argc, char **argv)
+{
+ printf("This utility disabled until rewritten\n");
+ return 1;
+}
diff --git a/source4/utils/setnttoken.c b/source4/utils/setnttoken.c
new file mode 100644
index 0000000000..3a008a4c37
--- /dev/null
+++ b/source4/utils/setnttoken.c
@@ -0,0 +1,28 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Set NT ACLs on UNIX files.
+
+ Copyright (C) Tim Potter <tpot@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+int main(int argc, char **argv)
+{
+ printf("This utility disabled until rewritten\n");
+ return 1;
+}
diff --git a/source4/utils/testparm.c b/source4/utils/testparm.c
new file mode 100644
index 0000000000..7f53eb5a52
--- /dev/null
+++ b/source4/utils/testparm.c
@@ -0,0 +1,257 @@
+/*
+ Unix SMB/CIFS implementation.
+ Test validity of smb.conf
+ Copyright (C) Karl Auer 1993, 1994-1998
+
+ Extensively modified by Andrew Tridgell, 1995
+ Converted to popt by Jelmer Vernooij (jelmer@nl.linux.org), 2002
+ Updated for Samba4 by Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Testbed for loadparm.c/params.c
+ *
+ * This module simply loads a specified configuration file and
+ * if successful, dumps it's contents to stdout. Note that the
+ * operation is performed with DEBUGLEVEL at 3.
+ *
+ * Useful for a quick 'syntax check' of a configuration file.
+ *
+ */
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "lib/cmdline/popt_common.h"
+#include "lib/socket/socket.h"
+#include "param/param.h"
+#include "param/loadparm.h"
+
+
+/***********************************************
+ Here we do a set of 'hard coded' checks for bad
+ configuration settings.
+************************************************/
+
+static int do_global_checks(struct loadparm_context *lp_ctx)
+{
+ int ret = 0;
+
+ if (!directory_exist(lp_lockdir(lp_ctx))) {
+ fprintf(stderr, "ERROR: lock directory %s does not exist\n",
+ lp_lockdir(lp_ctx));
+ ret = 1;
+ }
+
+ if (!directory_exist(lp_piddir(lp_ctx))) {
+ fprintf(stderr, "ERROR: pid directory %s does not exist\n",
+ lp_piddir(lp_ctx));
+ ret = 1;
+ }
+
+ if (strlen(lp_winbind_separator(lp_ctx)) != 1) {
+ fprintf(stderr,"ERROR: the 'winbind separator' parameter must be a single character.\n");
+ ret = 1;
+ }
+
+ if (*lp_winbind_separator(lp_ctx) == '+') {
+ fprintf(stderr,"'winbind separator = +' might cause problems with group membership.\n");
+ }
+
+ return ret;
+}
+
+
+static int do_share_checks(struct loadparm_context *lp_ctx, const char *cname, const char *caddr, bool silent_mode,
+ bool show_defaults, const char *section_name, const char *parameter_name)
+{
+ int ret = 0;
+ int s;
+
+ for (s=0;s<lp_numservices(lp_ctx);s++) {
+ struct loadparm_service *service = lp_servicebynum(lp_ctx, s);
+ if (service != NULL)
+ if (strlen(lp_servicename(lp_servicebynum(lp_ctx, s))) > 12) {
+ fprintf(stderr, "WARNING: You have some share names that are longer than 12 characters.\n" );
+ fprintf(stderr, "These may not be accessible to some older clients.\n" );
+ fprintf(stderr, "(Eg. Windows9x, WindowsMe, and not listed in smbclient in Samba 3.0.)\n" );
+ break;
+ }
+ }
+
+ for (s=0;s<lp_numservices(lp_ctx);s++) {
+ struct loadparm_service *service = lp_servicebynum(lp_ctx, s);
+ if (service != NULL) {
+ const char **deny_list = lp_hostsdeny(service, lp_default_service(lp_ctx));
+ const char **allow_list = lp_hostsallow(service, lp_default_service(lp_ctx));
+ int i;
+ if(deny_list) {
+ for (i=0; deny_list[i]; i++) {
+ char *hasstar = strchr_m(deny_list[i], '*');
+ char *hasquery = strchr_m(deny_list[i], '?');
+ if(hasstar || hasquery) {
+ fprintf(stderr,"Invalid character %c in hosts deny list (%s) for service %s.\n",
+ hasstar ? *hasstar : *hasquery, deny_list[i], lp_servicename(service) );
+ }
+ }
+ }
+
+ if(allow_list) {
+ for (i=0; allow_list[i]; i++) {
+ char *hasstar = strchr_m(allow_list[i], '*');
+ char *hasquery = strchr_m(allow_list[i], '?');
+ if(hasstar || hasquery) {
+ fprintf(stderr,"Invalid character %c in hosts allow list (%s) for service %s.\n",
+ hasstar ? *hasstar : *hasquery, allow_list[i], lp_servicename(service) );
+ }
+ }
+ }
+ }
+ }
+
+
+ if (!cname) {
+ if (!silent_mode) {
+ fprintf(stderr,"Press enter to see a dump of your service definitions\n");
+ fflush(stdout);
+ getc(stdin);
+ }
+ if (section_name != NULL || parameter_name != NULL) {
+ struct loadparm_service *service = NULL;
+ if (!section_name) {
+ section_name = GLOBAL_NAME;
+ service = NULL;
+ } else if ((!strwicmp(section_name, GLOBAL_NAME)) == 0 &&
+ (service=lp_service(lp_ctx, section_name)) == NULL) {
+ fprintf(stderr,"Unknown section %s\n",
+ section_name);
+ return(1);
+ }
+ if (!parameter_name) {
+ lp_dump_one(stdout, show_defaults, service, lp_default_service(lp_ctx));
+ } else {
+ ret = !lp_dump_a_parameter(lp_ctx, service, parameter_name, stdout);
+ }
+ } else {
+ lp_dump(lp_ctx, stdout, show_defaults, lp_numservices(lp_ctx));
+ }
+ return(ret);
+ }
+
+ if(cname && caddr){
+ /* this is totally ugly, a real `quick' hack */
+ for (s=0;s<lp_numservices(lp_ctx);s++) {
+ struct loadparm_service *service = lp_servicebynum(lp_ctx, s);
+ if (service != NULL) {
+ if (allow_access(NULL, lp_hostsdeny(NULL, lp_default_service(lp_ctx)), lp_hostsallow(NULL, lp_default_service(lp_ctx)), cname, caddr)
+ && allow_access(NULL, lp_hostsdeny(service, lp_default_service(lp_ctx)), lp_hostsallow(service, lp_default_service(lp_ctx)), cname, caddr)) {
+ fprintf(stderr,"Allow connection from %s (%s) to %s\n",
+ cname,caddr,lp_servicename(service));
+ } else {
+ fprintf(stderr,"Deny connection from %s (%s) to %s\n",
+ cname,caddr,lp_servicename(service));
+ }
+ }
+ }
+ }
+
+ return ret;
+}
+
+
+ int main(int argc, const char *argv[])
+{
+ static bool silent_mode = false;
+ int ret = 0;
+ poptContext pc;
+/*
+ static int show_all_parameters = 0;
+ static char *new_local_machine = NULL;
+*/
+ static const char *section_name = NULL;
+ static char *parameter_name = NULL;
+ static const char *cname;
+ static const char *caddr;
+ static bool show_defaults = false;
+ struct loadparm_context *lp_ctx;
+
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {"suppress-prompt", 0, POPT_ARG_NONE, &silent_mode, true, "Suppress prompt for enter"},
+ {"verbose", 'v', POPT_ARG_NONE, &show_defaults, true, "Show default options too"},
+/*
+ We need support for smb.conf macros before this will work again
+ {"server", 'L',POPT_ARG_STRING, &new_local_machine, 0, "Set %%L macro to servername\n"},
+*/
+/*
+ These are harder to do with the new code structure
+ {"show-all-parameters", '\0', POPT_ARG_NONE, &show_all_parameters, 1, "Show the parameters, type, possible values" },
+*/
+ {"section-name", '\0', POPT_ARG_STRING, &section_name, 0, "Limit testparm to a named section" },
+ {"parameter-name", '\0', POPT_ARG_STRING, &parameter_name, 0, "Limit testparm to a named parameter" },
+ {"client-name", '\0', POPT_ARG_STRING, &cname, 0, "Client DNS name for 'hosts allow' checking (should match reverse lookup)"},
+ {"client-ip", '\0', POPT_ARG_STRING, &caddr, 0, "Client IP address for 'hosts allow' checking"},
+ POPT_COMMON_SAMBA
+ POPT_COMMON_VERSION
+ { NULL }
+ };
+
+ setup_logging(NULL, DEBUG_STDERR);
+
+ pc = poptGetContext(NULL, argc, argv, long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+ poptSetOtherOptionHelp(pc, "[OPTION...] [host-name] [host-ip]");
+
+ while(poptGetNextOpt(pc) != -1);
+
+/*
+ if (show_all_parameters) {
+ show_parameter_list();
+ exit(0);
+ }
+*/
+
+ if ( cname && ! caddr ) {
+ printf ( "ERROR: For 'hosts allow' check you must specify both a DNS name and an IP address.\n" );
+ return(1);
+ }
+/*
+ We need support for smb.conf macros before this will work again
+
+ if (new_local_machine) {
+ set_local_machine_name(new_local_machine, True);
+ }
+*/
+
+ lp_ctx = cmdline_lp_ctx;
+
+ /* We need this to force the output */
+ lp_set_cmdline(lp_ctx, "log level", "2");
+
+ fprintf(stderr, "Loaded smb config files from %s\n", lp_configfile(lp_ctx));
+
+ if (!lp_load(lp_ctx, lp_configfile(lp_ctx))) {
+ fprintf(stderr,"Error loading services.\n");
+ return(1);
+ }
+
+ fprintf(stderr,"Loaded services file OK.\n");
+
+ ret = do_global_checks(lp_ctx);
+ ret |= do_share_checks(lp_ctx, cname, caddr, silent_mode, show_defaults, section_name, parameter_name);
+
+ return(ret);
+}
+
diff --git a/source4/utils/tests/test_net.sh b/source4/utils/tests/test_net.sh
new file mode 100755
index 0000000000..eb598bf5e1
--- /dev/null
+++ b/source4/utils/tests/test_net.sh
@@ -0,0 +1,40 @@
+#!/bin/sh
+# Blackbox tests for net
+
+SERVER=$1
+USERNAME=$2
+PASSWORD=$3
+DOMAIN=$4
+shift 4
+
+failed=0
+
+samba4bindir="$BUILDDIR/bin"
+smbclient="$samba4bindir/smbclient$EXEEXT"
+net="$samba4bindir/net$EXEEXT"
+
+testit() {
+ name="$1"
+ shift
+ cmdline="$*"
+ echo "test: $name"
+ $cmdline
+ status=$?
+ if [ x$status = x0 ]; then
+ echo "success: $name"
+ else
+ echo "failure: $name"
+ failed=`expr $failed + 1`
+ fi
+ return $status
+}
+
+testit "domain join" $VALGRIND $net join $DOMAIN $CONFIGURATION -W "$DOMAIN" -U"$USERNAME%$PASSWORD" $@ || failed=`expr $failed + 1`
+
+testit "Test login with --machine-pass without kerberos" $VALGRIND $smbclient -c 'ls' $CONFIGURATION //$SERVER/tmp --machine-pass -k no || failed=`expr $failed + 1`
+
+testit "Test login with --machine-pass and kerberos" $VALGRIND $smbclient -c 'ls' $CONFIGURATION //$SERVER/tmp --machine-pass -k yes || failed=`expr $failed + 1`
+
+exit $failed
+
+
diff --git a/source4/utils/tests/test_nmblookup.sh b/source4/utils/tests/test_nmblookup.sh
new file mode 100755
index 0000000000..e2230e162f
--- /dev/null
+++ b/source4/utils/tests/test_nmblookup.sh
@@ -0,0 +1,39 @@
+#!/bin/sh
+# Blackbox tests for nmblookup
+
+NETBIOSNAME=$1
+NETBIOSALIAS=$2
+SERVER=$3
+SERVER_IP=$4
+shift 4
+TORTURE_OPTIONS=$*
+
+failed=0
+
+testit() {
+ name="$1"
+ shift
+ cmdline="$*"
+ echo "test: $name"
+ $cmdline
+ status=$?
+ if [ x$status = x0 ]; then
+ echo "success: $name"
+ else
+ echo "failure: $name"
+ failed=`expr $failed + 1`
+ fi
+ return $status
+}
+
+samba4bindir="$BUILDDIR/bin"
+nmblookup="$samba4bindir/nmblookup$EXEEXT"
+
+testit "nmblookup -U \$SERVER_IP \$SERVER" $nmblookup $TORTURE_OPTIONS -U $SERVER_IP $SERVER
+testit "nmblookup -U \$SERVER_IP \$NETBIOSNAME" $nmblookup $TORTURE_OPTIONS -U $SERVER_IP $NETBIOSNAME
+testit "nmblookup -U \$SERVER_IP \$NETBIOSALIAS" $nmblookup $TORTURE_OPTIONS -U $SERVER_IP $NETBIOSALIAS
+testit "nmblookup \$SERVER" $nmblookup $TORTURE_OPTIONS $SERVER
+testit "nmblookup \$NETBIOSNAME" $nmblookup $TORTURE_OPTIONS $NETBIOSNAME
+testit "nmblookup \$NETBIOSALIAS" $nmblookup $TORTURE_OPTIONS $NETBIOSALIAS
+
+exit $failed
diff --git a/source4/web_server/config.mk b/source4/web_server/config.mk
new file mode 100644
index 0000000000..ff587508fc
--- /dev/null
+++ b/source4/web_server/config.mk
@@ -0,0 +1,14 @@
+# web server subsystem
+
+#######################
+# Start SUBSYSTEM WEB
+[MODULE::WEB]
+INIT_FUNCTION = server_service_web_init
+SUBSYSTEM = service
+PRIVATE_DEPENDENCIES = LIBTLS smbcalls process_model LIBPYTHON
+# End SUBSYSTEM WEB
+#######################
+
+WEB_OBJ_FILES = $(addprefix $(web_serversrcdir)/, web_server.o wsgi.o)
+
+$(eval $(call proto_header_template,$(web_serversrcdir)/proto.h,$(WEB_OBJ_FILES:.o=.c)))
diff --git a/source4/web_server/swat/__init__.py b/source4/web_server/swat/__init__.py
new file mode 100644
index 0000000000..d434bb260b
--- /dev/null
+++ b/source4/web_server/swat/__init__.py
@@ -0,0 +1,39 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Unix SMB/CIFS implementation.
+# Copyright © Jelmer Vernooij <jelmer@samba.org> 2008
+#
+# Implementation of SWAT that uses WSGI
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+def __call__(environ, start_response):
+ status = '200 OK'
+ response_headers = [('Content-type','text/html')]
+ start_response(status, response_headers)
+ yield '<table>\n'
+
+ for key, value in environ.items():
+ if isinstance(value, str):
+ yield '\t<tr><td><b>%s</b></td><td>%s</td></tr>\n' % (key, value)
+
+ yield '</table>\n'
+
+if __name__ == '__main__':
+ from wsgiref import simple_server
+ httpd = simple_server.make_server('localhost', 8090, __call__)
+ print "Serving HTTP on port 8090..."
+ httpd.serve_forever()
diff --git a/source4/web_server/web_server.c b/source4/web_server/web_server.c
new file mode 100644
index 0000000000..2a2bfbb13b
--- /dev/null
+++ b/source4/web_server/web_server.c
@@ -0,0 +1,364 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ web server startup
+
+ Copyright (C) Andrew Tridgell 2005
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "smbd/service_task.h"
+#include "smbd/service_stream.h"
+#include "smbd/service.h"
+#include "web_server/web_server.h"
+#include "lib/events/events.h"
+#include "system/filesys.h"
+#include "system/network.h"
+#include "lib/socket/netif.h"
+#include "lib/tls/tls.h"
+#include "../lib/util/dlinklist.h"
+#include "param/param.h"
+
+/* don't allow connections to hang around forever */
+#define HTTP_TIMEOUT 120
+
+/*
+ destroy a web connection
+*/
+static int websrv_destructor(struct websrv_context *web)
+{
+ return 0;
+}
+
+/*
+ called when a connection times out. This prevents a stuck connection
+ from hanging around forever
+*/
+static void websrv_timeout(struct tevent_context *event_context,
+ struct tevent_timer *te,
+ struct timeval t, void *private_data)
+{
+ struct websrv_context *web = talloc_get_type(private_data, struct websrv_context);
+ struct stream_connection *conn = web->conn;
+ web->conn = NULL;
+ /* TODO: send a message to any running esp context on this connection
+ to stop running */
+ stream_terminate_connection(conn, "websrv_timeout: timed out");
+}
+
+/*
+ setup for a raw http level error
+*/
+void http_error(struct websrv_context *web, const char *status, const char *info)
+{
+ char *s;
+ s = talloc_asprintf(web,"<HTML><HEAD><TITLE>Error %s</TITLE></HEAD><BODY><H1>Error %s</H1><pre>%s</pre><p></BODY></HTML>\r\n\r\n",
+ status, status, info);
+ if (s == NULL) {
+ stream_terminate_connection(web->conn, "http_error: out of memory");
+ return;
+ }
+ websrv_output_headers(web, status, NULL);
+ websrv_output(web, s, strlen(s));
+}
+
+void websrv_output_headers(struct websrv_context *web, const char *status, struct http_header *headers)
+{
+ char *s;
+ DATA_BLOB b;
+ struct http_header *hdr;
+
+ s = talloc_asprintf(web, "HTTP/1.0 %s\r\n", status);
+ if (s == NULL) return;
+ for (hdr = headers; hdr; hdr = hdr->next) {
+ s = talloc_asprintf_append_buffer(s, "%s: %s\r\n", hdr->name, hdr->value);
+ }
+
+ s = talloc_asprintf_append_buffer(s, "\r\n");
+
+ b = web->output.content;
+ web->output.content = data_blob_string_const(s);
+ websrv_output(web, b.data, b.length);
+ data_blob_free(&b);
+}
+
+void websrv_output(struct websrv_context *web, void *data, size_t length)
+{
+ data_blob_append(web, &web->output.content, data, length);
+ EVENT_FD_NOT_READABLE(web->conn->event.fde);
+ EVENT_FD_WRITEABLE(web->conn->event.fde);
+ web->output.output_pending = true;
+}
+
+
+/*
+ parse one line of header input
+*/
+NTSTATUS http_parse_header(struct websrv_context *web, const char *line)
+{
+ if (line[0] == 0) {
+ web->input.end_of_headers = true;
+ } else if (strncasecmp(line,"GET ", 4)==0) {
+ web->input.url = talloc_strndup(web, &line[4], strcspn(&line[4], " \t"));
+ } else if (strncasecmp(line,"POST ", 5)==0) {
+ web->input.post_request = true;
+ web->input.url = talloc_strndup(web, &line[5], strcspn(&line[5], " \t"));
+ } else if (strchr(line, ':') == NULL) {
+ http_error(web, "400 Bad request", "This server only accepts GET and POST requests");
+ return NT_STATUS_INVALID_PARAMETER;
+ } else if (strncasecmp(line, "Content-Length: ", 16)==0) {
+ web->input.content_length = strtoul(&line[16], NULL, 10);
+ } else {
+ struct http_header *hdr = talloc_zero(web, struct http_header);
+ char *colon = strchr(line, ':');
+ if (colon == NULL) {
+ http_error(web, "500 Internal Server Error", "invalidly formatted header");
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ hdr->name = talloc_strndup(hdr, line, colon-line);
+ hdr->value = talloc_strdup(hdr, colon+1);
+ DLIST_ADD(web->input.headers, hdr);
+ }
+
+ /* ignore all other headers for now */
+ return NT_STATUS_OK;
+}
+
+/*
+ called when a web connection becomes readable
+*/
+static void websrv_recv(struct stream_connection *conn, uint16_t flags)
+{
+ struct web_server_data *wdata;
+ struct websrv_context *web = talloc_get_type(conn->private_data,
+ struct websrv_context);
+ NTSTATUS status;
+ uint8_t buf[1024];
+ size_t nread;
+ uint8_t *p;
+ DATA_BLOB b;
+
+ /* not the most efficient http parser ever, but good enough for us */
+ status = socket_recv(conn->socket, buf, sizeof(buf), &nread);
+ if (NT_STATUS_IS_ERR(status)) goto failed;
+ if (!NT_STATUS_IS_OK(status)) return;
+
+ if (!data_blob_append(web, &web->input.partial, buf, nread))
+ goto failed;
+
+ /* parse any lines that are available */
+ b = web->input.partial;
+ while (!web->input.end_of_headers &&
+ (p=(uint8_t *)memchr(b.data, '\n', b.length))) {
+ const char *line = (const char *)b.data;
+ *p = 0;
+ if (p != b.data && p[-1] == '\r') {
+ p[-1] = 0;
+ }
+ status = http_parse_header(web, line);
+ if (!NT_STATUS_IS_OK(status)) return;
+ b.length -= (p - b.data) + 1;
+ b.data = p+1;
+ }
+
+ /* keep any remaining bytes in web->input.partial */
+ if (b.length == 0) {
+ b.data = NULL;
+ }
+ b = data_blob_talloc(web, b.data, b.length);
+ data_blob_free(&web->input.partial);
+ web->input.partial = b;
+
+ /* we finish when we have both the full headers (terminated by
+ a blank line) and any post data, as indicated by the
+ content_length */
+ if (web->input.end_of_headers &&
+ web->input.partial.length >= web->input.content_length) {
+ if (web->input.partial.length > web->input.content_length) {
+ web->input.partial.data[web->input.content_length] = 0;
+ }
+ EVENT_FD_NOT_READABLE(web->conn->event.fde);
+
+ /* the reference/unlink code here is quite subtle. It
+ is needed because the rendering of the web-pages, and
+ in particular the esp/ejs backend, is semi-async. So
+ we could well end up in the connection timeout code
+ while inside http_process_input(), but we must not
+ destroy the stack variables being used by that
+ rendering process when we handle the timeout. */
+ if (!talloc_reference(web->task, web)) goto failed;
+ wdata = talloc_get_type(web->task->private_data, struct web_server_data);
+ if (wdata == NULL) goto failed;
+ wdata->http_process_input(wdata, web);
+ talloc_unlink(web->task, web);
+ }
+ return;
+
+failed:
+ stream_terminate_connection(conn, "websrv_recv: failed");
+}
+
+
+
+/*
+ called when a web connection becomes writable
+*/
+static void websrv_send(struct stream_connection *conn, uint16_t flags)
+{
+ struct websrv_context *web = talloc_get_type(conn->private_data,
+ struct websrv_context);
+ NTSTATUS status;
+ size_t nsent;
+ DATA_BLOB b;
+
+ b = web->output.content;
+ b.data += web->output.nsent;
+ b.length -= web->output.nsent;
+
+ status = socket_send(conn->socket, &b, &nsent);
+ if (NT_STATUS_IS_ERR(status)) {
+ stream_terminate_connection(web->conn, "socket_send: failed");
+ return;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return;
+ }
+
+ web->output.nsent += nsent;
+
+ if (web->output.content.length == web->output.nsent) {
+ stream_terminate_connection(web->conn, "websrv_send: finished sending");
+ }
+}
+
+/*
+ establish a new connection to the web server
+*/
+static void websrv_accept(struct stream_connection *conn)
+{
+ struct task_server *task = talloc_get_type(conn->private_data, struct task_server);
+ struct web_server_data *wdata = talloc_get_type(task->private_data, struct web_server_data);
+ struct websrv_context *web;
+ struct socket_context *tls_socket;
+
+ web = talloc_zero(conn, struct websrv_context);
+ if (web == NULL) goto failed;
+
+ web->task = task;
+ web->conn = conn;
+ conn->private_data = web;
+ talloc_set_destructor(web, websrv_destructor);
+
+ event_add_timed(conn->event.ctx, web,
+ timeval_current_ofs(HTTP_TIMEOUT, 0),
+ websrv_timeout, web);
+
+ /* Overwrite the socket with a (possibly) TLS socket */
+ tls_socket = tls_init_server(wdata->tls_params, conn->socket,
+ conn->event.fde, "GPHO");
+ /* We might not have TLS, or it might not have initilised */
+ if (tls_socket) {
+ talloc_unlink(conn, conn->socket);
+ talloc_steal(conn, tls_socket);
+ conn->socket = tls_socket;
+ } else {
+ DEBUG(3, ("TLS not available for web_server connections\n"));
+ }
+
+ return;
+
+failed:
+ talloc_free(conn);
+}
+
+
+static const struct stream_server_ops web_stream_ops = {
+ .name = "web",
+ .accept_connection = websrv_accept,
+ .recv_handler = websrv_recv,
+ .send_handler = websrv_send,
+};
+
+/*
+ startup the web server task
+*/
+static void websrv_task_init(struct task_server *task)
+{
+ NTSTATUS status;
+ uint16_t port = lp_web_port(task->lp_ctx);
+ const struct model_ops *model_ops;
+ struct web_server_data *wdata;
+
+ task_server_set_title(task, "task[websrv]");
+
+ /* run the web server as a single process */
+ model_ops = process_model_startup(task->event_ctx, "single");
+ if (!model_ops) goto failed;
+
+ if (lp_interfaces(task->lp_ctx) && lp_bind_interfaces_only(task->lp_ctx)) {
+ int num_interfaces;
+ int i;
+ struct interface *ifaces;
+
+ load_interfaces(NULL, lp_interfaces(task->lp_ctx), &ifaces);
+
+ num_interfaces = iface_count(ifaces);
+ for(i = 0; i < num_interfaces; i++) {
+ const char *address = iface_n_ip(ifaces, i);
+ status = stream_setup_socket(task->event_ctx,
+ task->lp_ctx, model_ops,
+ &web_stream_ops,
+ "ipv4", address,
+ &port, lp_socket_options(task->lp_ctx),
+ task);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+ }
+
+ talloc_free(ifaces);
+ } else {
+ status = stream_setup_socket(task->event_ctx, task->lp_ctx,
+ model_ops, &web_stream_ops,
+ "ipv4", lp_socket_address(task->lp_ctx),
+ &port, lp_socket_options(task->lp_ctx), task);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+ }
+
+ /* startup the esp processor - unfortunately we can't do this
+ per connection as that wouldn't allow for session variables */
+ wdata = talloc_zero(task, struct web_server_data);
+ if (wdata == NULL)goto failed;
+
+ task->private_data = wdata;
+
+ wdata->tls_params = tls_initialise(wdata, task->lp_ctx);
+ if (wdata->tls_params == NULL) goto failed;
+
+ if (!wsgi_initialize(wdata)) goto failed;
+
+ return;
+
+failed:
+ task_server_terminate(task, "websrv_task_init: failed to startup web server task");
+}
+
+
+/* called at smbd startup - register ourselves as a server service */
+NTSTATUS server_service_web_init(void)
+{
+ return register_server_service("web", websrv_task_init);
+}
diff --git a/source4/web_server/web_server.h b/source4/web_server/web_server.h
new file mode 100644
index 0000000000..3b02feaf7d
--- /dev/null
+++ b/source4/web_server/web_server.h
@@ -0,0 +1,65 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "smbd/process_model.h"
+
+struct websrv_context;
+
+struct web_server_data {
+ struct tls_params *tls_params;
+ void (*http_process_input)(struct web_server_data *wdata,
+ struct websrv_context *web);
+ void *private_data;
+};
+
+struct http_header {
+ char *name;
+ char *value;
+ struct http_header *prev, *next;
+};
+
+/*
+ context of one open web connection
+*/
+struct websrv_context {
+ struct task_server *task;
+ struct stream_connection *conn;
+ struct websrv_request_input {
+ bool tls_detect;
+ bool tls_first_char;
+ uint8_t first_byte;
+ DATA_BLOB partial;
+ bool end_of_headers;
+ char *url;
+ unsigned content_length;
+ bool post_request;
+ struct http_header *headers;
+ } input;
+ struct websrv_request_output {
+ bool output_pending;
+ DATA_BLOB content;
+ bool headers_sent;
+ unsigned nsent;
+ } output;
+ struct session_data *session;
+};
+
+
+#include "web_server/proto.h"
+
diff --git a/source4/web_server/wsgi.c b/source4/web_server/wsgi.c
new file mode 100644
index 0000000000..4d6b441f17
--- /dev/null
+++ b/source4/web_server/wsgi.c
@@ -0,0 +1,391 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright © Jelmer Vernooij <jelmer@samba.org> 2008
+
+ Implementation of the WSGI interface described in PEP0333
+ (http://www.python.org/dev/peps/pep-0333)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "web_server/web_server.h"
+#include "../lib/util/dlinklist.h"
+#include "../lib/util/data_blob.h"
+#include "lib/tls/tls.h"
+#include <Python.h>
+
+#ifndef Py_RETURN_NONE
+#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
+#endif
+
+typedef struct {
+ PyObject_HEAD
+ struct websrv_context *web;
+} web_request_Object;
+
+static PyObject *start_response(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *response_header, *exc_info = NULL;
+ char *status;
+ int i;
+ const char *kwnames[] = {
+ "status", "response_header", "exc_info", NULL
+ };
+ web_request_Object *py_web = (web_request_Object *)self;
+ struct websrv_context *web = py_web->web;
+ struct http_header *headers = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO|O:start_response", discard_const_p(char *, kwnames), &status, &response_header, &exc_info)) {
+ return NULL;
+ }
+
+ /* FIXME: exc_info */
+
+ if (!PyList_Check(response_header)) {
+ PyErr_SetString(PyExc_TypeError, "response_header should be list");
+ return NULL;
+ }
+
+ for (i = 0; i < PyList_Size(response_header); i++) {
+ struct http_header *hdr = talloc_zero(web, struct http_header);
+ PyObject *item = PyList_GetItem(response_header, i);
+ PyObject *py_name, *py_value;
+
+ if (!PyTuple_Check(item)) {
+ PyErr_SetString(PyExc_TypeError, "Expected tuple");
+ return NULL;
+ }
+
+ if (PyTuple_Size(item) != 2) {
+ PyErr_SetString(PyExc_TypeError, "header tuple has invalid size, expected 2");
+ return NULL;
+ }
+
+ py_name = PyTuple_GetItem(item, 0);
+
+ if (!PyString_Check(py_name)) {
+ PyErr_SetString(PyExc_TypeError, "header name should be string");
+ return NULL;
+ }
+
+ py_value = PyTuple_GetItem(item, 1);
+ if (!PyString_Check(py_value)) {
+ PyErr_SetString(PyExc_TypeError, "header value should be string");
+ return NULL;
+ }
+
+ hdr->name = talloc_strdup(hdr, PyString_AsString(py_name));
+ hdr->value = talloc_strdup(hdr, PyString_AsString(py_value));
+ DLIST_ADD(headers, hdr);
+ }
+
+ websrv_output_headers(web, status, headers);
+
+ Py_RETURN_NONE;
+}
+
+static PyMethodDef web_request_methods[] = {
+ { "start_response", (PyCFunction)start_response, METH_VARARGS|METH_KEYWORDS, NULL },
+ { NULL }
+};
+
+
+PyTypeObject web_request_Type = {
+ PyObject_HEAD_INIT(NULL) 0,
+ .tp_name = "wsgi.Request",
+ .tp_methods = web_request_methods,
+ .tp_basicsize = sizeof(web_request_Object),
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+};
+
+typedef struct {
+ PyObject_HEAD
+} error_Stream_Object;
+
+static PyObject *py_error_flush(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ /* Nothing to do here */
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_error_write(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ const char *kwnames[] = { "str", NULL };
+ char *str = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s:write", discard_const_p(char *, kwnames), &str)) {
+ return NULL;
+ }
+
+ DEBUG(0, ("WSGI App: %s", str));
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_error_writelines(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ const char *kwnames[] = { "seq", NULL };
+ PyObject *seq = NULL, *item;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O:writelines", discard_const_p(char *, kwnames), &seq)) {
+ return NULL;
+ }
+
+ while ((item = PyIter_Next(seq))) {
+ char *str = PyString_AsString(item);
+
+ DEBUG(0, ("WSGI App: %s", str));
+ }
+
+ Py_RETURN_NONE;
+}
+
+static PyMethodDef error_Stream_methods[] = {
+ { "flush", (PyCFunction)py_error_flush, METH_VARARGS|METH_KEYWORDS, NULL },
+ { "write", (PyCFunction)py_error_write, METH_VARARGS|METH_KEYWORDS, NULL },
+ { "writelines", (PyCFunction)py_error_writelines, METH_VARARGS|METH_KEYWORDS, NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject error_Stream_Type = {
+ PyObject_HEAD_INIT(NULL) 0,
+ .tp_name = "wsgi.ErrorStream",
+ .tp_basicsize = sizeof(error_Stream_Object),
+ .tp_methods = error_Stream_methods,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+};
+
+typedef struct {
+ PyObject_HEAD
+ struct websrv_context *web;
+ size_t offset;
+} input_Stream_Object;
+
+static PyObject *py_input_read(PyObject *_self, PyObject *args, PyObject *kwargs)
+{
+ const char *kwnames[] = { "size", NULL };
+ PyObject *ret;
+ input_Stream_Object *self = (input_Stream_Object *)_self;
+ int size = -1;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i", discard_const_p(char *, kwnames), &size))
+ return NULL;
+
+ /* Don't read beyond buffer boundaries */
+ if (size == -1)
+ size = self->web->input.partial.length-self->offset;
+ else
+ size = MIN(size, self->web->input.partial.length-self->offset);
+
+ ret = PyString_FromStringAndSize((char *)self->web->input.partial.data+self->offset, size);
+ self->offset += size;
+
+ return ret;
+}
+
+static PyObject *py_input_readline(PyObject *_self)
+{
+ /* FIXME */
+ PyErr_SetString(PyExc_NotImplementedError,
+ "readline() not yet implemented");
+ return NULL;
+}
+
+static PyObject *py_input_readlines(PyObject *_self, PyObject *args, PyObject *kwargs)
+{
+ const char *kwnames[] = { "hint", NULL };
+ int hint;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i", discard_const_p(char *, kwnames), &hint))
+ return NULL;
+
+ /* FIXME */
+ PyErr_SetString(PyExc_NotImplementedError,
+ "readlines() not yet implemented");
+ return NULL;
+}
+
+static PyObject *py_input___iter__(PyObject *_self)
+{
+ /* FIXME */
+ PyErr_SetString(PyExc_NotImplementedError,
+ "__iter__() not yet implemented");
+ return NULL;
+}
+
+static PyMethodDef input_Stream_methods[] = {
+ { "read", (PyCFunction)py_input_read, METH_VARARGS|METH_KEYWORDS, NULL },
+ { "readline", (PyCFunction)py_input_readline, METH_NOARGS, NULL },
+ { "readlines", (PyCFunction)py_input_readlines, METH_VARARGS|METH_KEYWORDS, NULL },
+ { "__iter__", (PyCFunction)py_input___iter__, METH_NOARGS, NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject input_Stream_Type = {
+ PyObject_HEAD_INIT(NULL) 0,
+ .tp_name = "wsgi.InputStream",
+ .tp_basicsize = sizeof(input_Stream_Object),
+ .tp_methods = input_Stream_methods,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+};
+
+static PyObject *Py_InputHttpStream(struct websrv_context *web)
+{
+ input_Stream_Object *ret = PyObject_New(input_Stream_Object, &input_Stream_Type);
+ ret->web = web;
+ ret->offset = 0;
+ return (PyObject *)ret;
+}
+
+static PyObject *Py_ErrorHttpStream(void)
+{
+ error_Stream_Object *ret = PyObject_New(error_Stream_Object, &error_Stream_Type);
+ return (PyObject *)ret;
+}
+
+static PyObject *create_environ(bool tls, int content_length, struct http_header *headers, const char *request_method, const char *servername, int serverport, PyObject *inputstream, const char *request_string)
+{
+ PyObject *env;
+ PyObject *errorstream;
+ PyObject *py_scheme;
+ struct http_header *hdr;
+ char *questionmark;
+
+ env = PyDict_New();
+ if (env == NULL) {
+ return NULL;
+ }
+
+ errorstream = Py_ErrorHttpStream();
+ if (errorstream == NULL) {
+ Py_DECREF(env);
+ Py_DECREF(inputstream);
+ return NULL;
+ }
+
+ PyDict_SetItemString(env, "wsgi.input", inputstream);
+ PyDict_SetItemString(env, "wsgi.errors", errorstream);
+ PyDict_SetItemString(env, "wsgi.version", Py_BuildValue("(i,i)", 1, 0));
+ PyDict_SetItemString(env, "wsgi.multithread", Py_False);
+ PyDict_SetItemString(env, "wsgi.multiprocess", Py_True);
+ PyDict_SetItemString(env, "wsgi.run_once", Py_False);
+ PyDict_SetItemString(env, "SERVER_PROTOCOL", PyString_FromString("HTTP/1.0"));
+ if (content_length > 0) {
+ PyDict_SetItemString(env, "CONTENT_LENGTH", PyLong_FromLong(content_length));
+ }
+ PyDict_SetItemString(env, "REQUEST_METHOD", PyString_FromString(request_method));
+
+ questionmark = strchr(request_string, '?');
+ if (questionmark == NULL) {
+ PyDict_SetItemString(env, "SCRIPT_NAME", PyString_FromString(request_string));
+ } else {
+ PyDict_SetItemString(env, "QUERY_STRING", PyString_FromString(questionmark+1));
+ PyDict_SetItemString(env, "SCRIPT_NAME", PyString_FromStringAndSize(request_string, questionmark-request_string));
+ }
+
+ PyDict_SetItemString(env, "SERVER_NAME", PyString_FromString(servername));
+ PyDict_SetItemString(env, "SERVER_PORT", PyInt_FromLong(serverport));
+ for (hdr = headers; hdr; hdr = hdr->next) {
+ char *name;
+ if (!strcasecmp(hdr->name, "Content-Type")) {
+ PyDict_SetItemString(env, "CONTENT_TYPE", PyString_FromString(hdr->value));
+ } else {
+ asprintf(&name, "HTTP_%s", hdr->name);
+ PyDict_SetItemString(env, name, PyString_FromString(hdr->value));
+ free(name);
+ }
+ }
+
+ if (tls) {
+ py_scheme = PyString_FromString("https");
+ } else {
+ py_scheme = PyString_FromString("http");
+ }
+ PyDict_SetItemString(env, "wsgi.url_scheme", py_scheme);
+
+ return env;
+}
+
+static void wsgi_process_http_input(struct web_server_data *wdata,
+ struct websrv_context *web)
+{
+ PyObject *py_environ, *result, *item, *iter;
+ PyObject *request_handler = (PyObject *)wdata->private_data;
+ struct socket_address *socket_address;
+
+ web_request_Object *py_web = PyObject_New(web_request_Object, &web_request_Type);
+ py_web->web = web;
+
+ socket_address = socket_get_my_addr(web->conn->socket, web);
+ py_environ = create_environ(tls_enabled(web->conn->socket),
+ web->input.content_length,
+ web->input.headers,
+ web->input.post_request?"POST":"GET",
+ socket_address->addr,
+ socket_address->port,
+ Py_InputHttpStream(web),
+ web->input.url
+ );
+ if (py_environ == NULL) {
+ DEBUG(0, ("Unable to create WSGI environment object\n"));
+ return;
+ }
+
+ result = PyObject_CallMethod(request_handler, discard_const_p(char, "__call__"), discard_const_p(char, "OO"),
+ py_environ, PyObject_GetAttrString((PyObject *)py_web, "start_response"));
+
+ if (result == NULL) {
+ DEBUG(0, ("error while running WSGI code\n"));
+ return;
+ }
+
+ iter = PyObject_GetIter(result);
+ Py_DECREF(result);
+
+ /* Now, iter over all the data returned */
+
+ while ((item = PyIter_Next(iter))) {
+ websrv_output(web, PyString_AsString(item), PyString_Size(item));
+ Py_DECREF(item);
+ }
+
+ Py_DECREF(iter);
+}
+
+bool wsgi_initialize(struct web_server_data *wdata)
+{
+ PyObject *py_swat;
+
+ Py_Initialize();
+
+ if (PyType_Ready(&web_request_Type) < 0)
+ return false;
+
+ if (PyType_Ready(&input_Stream_Type) < 0)
+ return false;
+
+ if (PyType_Ready(&error_Stream_Type) < 0)
+ return false;
+
+ wdata->http_process_input = wsgi_process_http_input;
+ py_swat = PyImport_Import(PyString_FromString("swat"));
+ if (py_swat == NULL) {
+ DEBUG(0, ("Unable to find SWAT\n"));
+ return false;
+ }
+ wdata->private_data = py_swat;
+ return true;
+}
diff --git a/source4/winbind/config.mk b/source4/winbind/config.mk
new file mode 100644
index 0000000000..16c1652fe4
--- /dev/null
+++ b/source4/winbind/config.mk
@@ -0,0 +1,79 @@
+# server subsystem
+
+#######################
+# Start SUBSYSTEM WINBIND
+[MODULE::WINBIND]
+INIT_FUNCTION = server_service_winbind_init
+SUBSYSTEM = service
+PRIVATE_DEPENDENCIES = \
+ WB_HELPER \
+ IDMAP \
+ NDR_WINBIND \
+ process_model \
+ RPC_NDR_LSA \
+ dcerpc_samr \
+ PAM_ERRORS \
+ LIBCLI_LDAP \
+ LIBSAMBA-NET
+# End SUBSYSTEM WINBIND
+#######################
+
+WINBIND_OBJ_FILES = $(addprefix $(winbindsrcdir)/, \
+ wb_server.o \
+ wb_setup_domains.o \
+ wb_irpc.o \
+ wb_samba3_protocol.o \
+ wb_samba3_cmd.o \
+ wb_init_domain.o \
+ wb_dom_info.o \
+ wb_dom_info_trusted.o \
+ wb_sid2domain.o \
+ wb_name2domain.o \
+ wb_sids2xids.o \
+ wb_xids2sids.o \
+ wb_gid2sid.o \
+ wb_sid2uid.o \
+ wb_sid2gid.o \
+ wb_uid2sid.o \
+ wb_connect_lsa.o \
+ wb_connect_sam.o \
+ wb_cmd_lookupname.o \
+ wb_cmd_lookupsid.o \
+ wb_cmd_getdcname.o \
+ wb_cmd_getgrnam.o \
+ wb_cmd_getgrgid.o \
+ wb_cmd_getpwnam.o \
+ wb_cmd_getpwuid.o \
+ wb_cmd_userdomgroups.o \
+ wb_cmd_usersids.o \
+ wb_cmd_list_groups.o \
+ wb_cmd_list_trustdom.o \
+ wb_cmd_list_users.o \
+ wb_cmd_setpwent.o \
+ wb_cmd_getpwent.o \
+ wb_pam_auth.o \
+ wb_sam_logon.o)
+
+$(eval $(call proto_header_template,$(winbindsrcdir)/wb_proto.h,$(WINBIND_OBJ_FILES:.o=.c)))
+
+################################################
+# Start SUBYSTEM WB_HELPER
+[SUBSYSTEM::WB_HELPER]
+PUBLIC_DEPENDENCIES = RPC_NDR_LSA dcerpc_samr
+# End SUBSYSTEM WB_HELPER
+################################################
+
+WB_HELPER_OBJ_FILES = $(addprefix $(winbindsrcdir)/, wb_async_helpers.o wb_utils.o)
+
+$(eval $(call proto_header_template,$(winbindsrcdir)/wb_helper.h,$(WB_HELPER_OBJ_FILES:.o=.c)))
+
+################################################
+# Start SUBYSTEM IDMAP
+[SUBSYSTEM::IDMAP]
+PUBLIC_DEPENDENCIES = SAMDB_COMMON
+# End SUBSYSTEM IDMAP
+################################################
+
+IDMAP_OBJ_FILES = $(winbindsrcdir)/idmap.o
+
+$(eval $(call proto_header_template,$(winbindsrcdir)/idmap_proto.h,$(IDMAP_OBJ_FILES:.o=.c)))
diff --git a/source4/winbind/idmap.c b/source4/winbind/idmap.c
new file mode 100644
index 0000000000..d0efbb159b
--- /dev/null
+++ b/source4/winbind/idmap.c
@@ -0,0 +1,714 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Map SIDs to unixids and back
+
+ Copyright (C) Kai Blin 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "auth/auth.h"
+#include "librpc/gen_ndr/lsa.h"
+#include "librpc/gen_ndr/samr.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "lib/ldb/include/ldb.h"
+#include "lib/ldb/include/ldb_errors.h"
+#include "lib/ldb_wrap.h"
+#include "param/param.h"
+#include "winbind/idmap.h"
+#include "libcli/security/security.h"
+#include "libcli/ldap/ldap_ndr.h"
+
+/**
+ * Get uid/gid bounds from idmap database
+ *
+ * \param idmap_ctx idmap context to use
+ * \param low lower uid/gid bound is stored here
+ * \param high upper uid/gid bound is stored here
+ * \return 0 on success, nonzero on failure
+ */
+static int idmap_get_bounds(struct idmap_context *idmap_ctx, uint32_t *low,
+ uint32_t *high)
+{
+ int ret = -1;
+ struct ldb_context *ldb = idmap_ctx->ldb_ctx;
+ struct ldb_dn *dn;
+ struct ldb_result *res = NULL;
+ TALLOC_CTX *tmp_ctx = talloc_new(idmap_ctx);
+ uint32_t lower_bound = (uint32_t) -1;
+ uint32_t upper_bound = (uint32_t) -1;
+
+ dn = ldb_dn_new(tmp_ctx, ldb, "CN=CONFIG");
+ if (dn == NULL) goto failed;
+
+ ret = ldb_search(ldb, tmp_ctx, &res, dn, LDB_SCOPE_BASE, NULL, NULL);
+ if (ret != LDB_SUCCESS) goto failed;
+
+ if (res->count != 1) {
+ ret = -1;
+ goto failed;
+ }
+
+ lower_bound = ldb_msg_find_attr_as_uint(res->msgs[0], "lowerBound", -1);
+ if (lower_bound != (uint32_t) -1) {
+ ret = LDB_SUCCESS;
+ } else {
+ ret = -1;
+ goto failed;
+ }
+
+ upper_bound = ldb_msg_find_attr_as_uint(res->msgs[0], "upperBound", -1);
+ if (upper_bound != (uint32_t) -1) {
+ ret = LDB_SUCCESS;
+ } else {
+ ret = -1;
+ }
+
+failed:
+ talloc_free(tmp_ctx);
+ *low = lower_bound;
+ *high = upper_bound;
+ return ret;
+}
+
+/**
+ * Add a dom_sid structure to a ldb_message
+ * \param idmap_ctx idmap context to use
+ * \param mem_ctx talloc context to use
+ * \param ldb_message ldb message to add dom_sid to
+ * \param attr_name name of the attribute to store the dom_sid in
+ * \param sid dom_sid to store
+ * \return 0 on success, an ldb error code on failure.
+ */
+static int idmap_msg_add_dom_sid(struct idmap_context *idmap_ctx,
+ TALLOC_CTX *mem_ctx, struct ldb_message *msg,
+ const char *attr_name, const struct dom_sid *sid)
+{
+ struct ldb_val val;
+ enum ndr_err_code ndr_err;
+
+ ndr_err = ndr_push_struct_blob(&val, mem_ctx,
+ lp_iconv_convenience(idmap_ctx->lp_ctx),
+ sid,
+ (ndr_push_flags_fn_t)ndr_push_dom_sid);
+
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return -1;
+ }
+
+ return ldb_msg_add_value(msg, attr_name, &val, NULL);
+}
+
+/**
+ * Get a dom_sid structure from a ldb message.
+ *
+ * \param mem_ctx talloc context to allocate dom_sid memory in
+ * \param msg ldb_message to get dom_sid from
+ * \param attr_name key that has the dom_sid as data
+ * \return dom_sid structure on success, NULL on failure
+ */
+static struct dom_sid *idmap_msg_get_dom_sid(TALLOC_CTX *mem_ctx,
+ struct ldb_message *msg, const char *attr_name)
+{
+ struct dom_sid *sid;
+ const struct ldb_val *val;
+ enum ndr_err_code ndr_err;
+
+ val = ldb_msg_find_ldb_val(msg, attr_name);
+ if (val == NULL) {
+ return NULL;
+ }
+
+ sid = talloc(mem_ctx, struct dom_sid);
+ if (sid == NULL) {
+ return NULL;
+ }
+
+ ndr_err = ndr_pull_struct_blob(val, sid, NULL, sid,
+ (ndr_pull_flags_fn_t)ndr_pull_dom_sid);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ talloc_free(sid);
+ return NULL;
+ }
+
+ return sid;
+}
+
+/**
+ * Initialize idmap context
+ *
+ * talloc_free to close.
+ *
+ * \param mem_ctx talloc context to use.
+ * \return allocated idmap_context on success, NULL on error
+ */
+struct idmap_context *idmap_init(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx)
+{
+ struct idmap_context *idmap_ctx;
+
+ idmap_ctx = talloc(mem_ctx, struct idmap_context);
+ if (idmap_ctx == NULL) {
+ return NULL;
+ }
+
+ idmap_ctx->lp_ctx = lp_ctx;
+
+ idmap_ctx->ldb_ctx = ldb_wrap_connect(mem_ctx, ev_ctx, lp_ctx,
+ lp_idmap_url(lp_ctx),
+ system_session(mem_ctx, lp_ctx),
+ NULL, 0, NULL);
+ if (idmap_ctx->ldb_ctx == NULL) {
+ return NULL;
+ }
+
+ idmap_ctx->unix_groups_sid = dom_sid_parse_talloc(mem_ctx, "S-1-22-2");
+ if (idmap_ctx->unix_groups_sid == NULL) {
+ return NULL;
+ }
+
+ idmap_ctx->unix_users_sid = dom_sid_parse_talloc(mem_ctx, "S-1-22-1");
+ if (idmap_ctx->unix_users_sid == NULL) {
+ return NULL;
+ }
+
+ return idmap_ctx;
+}
+
+/**
+ * Convert an unixid to the corresponding SID
+ *
+ * \param idmap_ctx idmap context to use
+ * \param mem_ctx talloc context the memory for the struct dom_sid is allocated
+ * from.
+ * \param unixid pointer to a unixid struct to convert
+ * \param sid pointer that will take the struct dom_sid pointer if the mapping
+ * succeeds.
+ * \return NT_STATUS_OK on success, NT_STATUS_NONE_MAPPED if mapping not
+ * possible or some other NTSTATUS that is more descriptive on failure.
+ */
+
+NTSTATUS idmap_xid_to_sid(struct idmap_context *idmap_ctx, TALLOC_CTX *mem_ctx,
+ const struct unixid *unixid, struct dom_sid **sid)
+{
+ int ret;
+ NTSTATUS status = NT_STATUS_NONE_MAPPED;
+ struct ldb_context *ldb = idmap_ctx->ldb_ctx;
+ struct ldb_result *res = NULL;
+ struct dom_sid *unix_sid, *new_sid;
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+ const char *id_type;
+
+ switch (unixid->type) {
+ case ID_TYPE_UID:
+ id_type = "ID_TYPE_UID";
+ break;
+ case ID_TYPE_GID:
+ id_type = "ID_TYPE_GID";
+ break;
+ default:
+ DEBUG(1, ("unixid->type must be type gid or uid\n"));
+ status = NT_STATUS_NONE_MAPPED;
+ goto failed;
+ }
+
+ ret = ldb_search(ldb, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE,
+ NULL, "(&(|(type=ID_TYPE_BOTH)(type=%s))"
+ "(xidNumber=%u))", id_type, unixid->id);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(1, ("Search failed: %s\n", ldb_errstring(ldb)));
+ status = NT_STATUS_NONE_MAPPED;
+ goto failed;
+ }
+
+ if (res->count == 1) {
+ *sid = idmap_msg_get_dom_sid(mem_ctx, res->msgs[0],
+ "objectSid");
+ if (*sid == NULL) {
+ DEBUG(1, ("Failed to get sid from db: %u\n", ret));
+ status = NT_STATUS_NONE_MAPPED;
+ goto failed;
+ }
+ talloc_free(tmp_ctx);
+ return NT_STATUS_OK;
+ }
+
+ DEBUG(6, ("xid not found in idmap db, create S-1-22- SID.\n"));
+
+ /* For local users/groups , we just create a rid = uid/gid */
+ if (unixid->type == ID_TYPE_UID) {
+ unix_sid = dom_sid_parse_talloc(tmp_ctx, "S-1-22-1");
+ } else {
+ unix_sid = dom_sid_parse_talloc(tmp_ctx, "S-1-22-2");
+ }
+ if (unix_sid == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto failed;
+ }
+
+ new_sid = dom_sid_add_rid(mem_ctx, unix_sid, unixid->id);
+ if (new_sid == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto failed;
+ }
+
+ *sid = new_sid;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_OK;
+
+failed:
+ talloc_free(tmp_ctx);
+ return status;
+}
+
+
+/**
+ * Map a SID to an unixid struct.
+ *
+ * If no mapping exists, a new mapping will be created.
+ *
+ * \todo Check if SIDs can be resolved if lp_idmap_trusted_only() == true
+ * \todo Fix backwards compatibility for Samba3
+ *
+ * \param idmap_ctx idmap context to use
+ * \param mem_ctx talloc context to use
+ * \param sid SID to map to an unixid struct
+ * \param unixid pointer to a unixid struct pointer
+ * \return NT_STATUS_OK on success, NT_STATUS_INVALID_SID if the sid is not from
+ * a trusted domain and idmap trusted only = true, NT_STATUS_NONE_MAPPED if the
+ * mapping failed.
+ */
+NTSTATUS idmap_sid_to_xid(struct idmap_context *idmap_ctx, TALLOC_CTX *mem_ctx,
+ const struct dom_sid *sid, struct unixid **unixid)
+{
+ int ret;
+ NTSTATUS status = NT_STATUS_NONE_MAPPED;
+ struct ldb_context *ldb = idmap_ctx->ldb_ctx;
+ struct ldb_dn *dn;
+ struct ldb_message *hwm_msg, *map_msg;
+ struct ldb_result *res = NULL;
+ int trans;
+ uint32_t low, high, hwm, new_xid;
+ char *sid_string, *unixid_string, *hwm_string;
+ bool hwm_entry_exists;
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+
+ if (dom_sid_in_domain(idmap_ctx->unix_users_sid, sid)) {
+ uint32_t rid;
+ DEBUG(6, ("This is a local unix uid, just calculate that.\n"));
+ status = dom_sid_split_rid(tmp_ctx, sid, NULL, &rid);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+
+ *unixid = talloc(mem_ctx, struct unixid);
+ if (*unixid == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto failed;
+ }
+ (*unixid)->id = rid;
+ (*unixid)->type = ID_TYPE_UID;
+
+ talloc_free(tmp_ctx);
+ return NT_STATUS_OK;
+ }
+
+ if (dom_sid_in_domain(idmap_ctx->unix_groups_sid, sid)) {
+ uint32_t rid;
+ DEBUG(6, ("This is a local unix gid, just calculate that.\n"));
+ status = dom_sid_split_rid(tmp_ctx, sid, NULL, &rid);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+
+ *unixid = talloc(mem_ctx, struct unixid);
+ if (*unixid == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto failed;
+ }
+ (*unixid)->id = rid;
+ (*unixid)->type = ID_TYPE_GID;
+
+ talloc_free(tmp_ctx);
+ return NT_STATUS_OK;
+ }
+
+ ret = ldb_search(ldb, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE,
+ NULL, "(&(objectClass=sidMap)(objectSid=%s))",
+ ldap_encode_ndr_dom_sid(tmp_ctx, sid));
+ if (ret != LDB_SUCCESS) {
+ DEBUG(1, ("Search failed: %s\n", ldb_errstring(ldb)));
+ status = NT_STATUS_NONE_MAPPED;
+ goto failed;
+ }
+
+ if (res->count == 1) {
+ const char *type = ldb_msg_find_attr_as_string(res->msgs[0],
+ "type", NULL);
+ new_xid = ldb_msg_find_attr_as_uint(res->msgs[0], "xidNumber",
+ -1);
+ if (new_xid == (uint32_t) -1) {
+ DEBUG(1, ("Invalid xid mapping.\n"));
+ status = NT_STATUS_NONE_MAPPED;
+ goto failed;
+ }
+
+ if (type == NULL) {
+ DEBUG(1, ("Invalid type for mapping entry.\n"));
+ status = NT_STATUS_NONE_MAPPED;
+ goto failed;
+ }
+
+ *unixid = talloc(mem_ctx, struct unixid);
+ if (*unixid == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto failed;
+ }
+
+ (*unixid)->id = new_xid;
+
+ if (strcmp(type, "ID_TYPE_BOTH") == 0) {
+ (*unixid)->type = ID_TYPE_BOTH;
+ } else if (strcmp(type, "ID_TYPE_UID") == 0) {
+ (*unixid)->type = ID_TYPE_UID;
+ } else {
+ (*unixid)->type = ID_TYPE_GID;
+ }
+
+ talloc_free(tmp_ctx);
+ return NT_STATUS_OK;
+ }
+
+ DEBUG(6, ("No existing mapping found, attempting to create one.\n"));
+
+ trans = ldb_transaction_start(ldb);
+ if (trans != LDB_SUCCESS) {
+ status = NT_STATUS_NONE_MAPPED;
+ goto failed;
+ }
+
+ /* Redo the search to make sure noone changed the mapping while we
+ * weren't looking */
+ ret = ldb_search(ldb, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE,
+ NULL, "(&(objectClass=sidMap)(objectSid=%s))",
+ ldap_encode_ndr_dom_sid(tmp_ctx, sid));
+ if (ret != LDB_SUCCESS) {
+ DEBUG(1, ("Search failed: %s\n", ldb_errstring(ldb)));
+ status = NT_STATUS_NONE_MAPPED;
+ goto failed;
+ }
+
+ if (res->count > 0) {
+ DEBUG(1, ("Database changed while trying to add a sidmap.\n"));
+ status = NT_STATUS_RETRY;
+ goto failed;
+ }
+
+ /*FIXME: if lp_idmap_trusted_only() == true, check if SID can be
+ * resolved here. */
+
+ ret = idmap_get_bounds(idmap_ctx, &low, &high);
+ if (ret != LDB_SUCCESS) {
+ status = NT_STATUS_NONE_MAPPED;
+ goto failed;
+ }
+
+ dn = ldb_dn_new(tmp_ctx, ldb, "CN=CONFIG");
+ if (dn == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto failed;
+ }
+
+ ret = ldb_search(ldb, tmp_ctx, &res, dn, LDB_SCOPE_BASE, NULL, NULL);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(1, ("Search failed: %s\n", ldb_errstring(ldb)));
+ status = NT_STATUS_NONE_MAPPED;
+ goto failed;
+ }
+
+ if (res->count != 1) {
+ DEBUG(1, ("No CN=CONFIG record, idmap database is broken.\n"));
+ status = NT_STATUS_NONE_MAPPED;
+ goto failed;
+ }
+
+ hwm = ldb_msg_find_attr_as_uint(res->msgs[0], "xidNumber", -1);
+ if (hwm == (uint32_t)-1) {
+ hwm = low;
+ hwm_entry_exists = false;
+ } else {
+ hwm_entry_exists = true;
+ }
+
+ if (hwm > high) {
+ DEBUG(1, ("Out of xids to allocate.\n"));
+ status = NT_STATUS_NONE_MAPPED;
+ goto failed;
+ }
+
+ hwm_msg = ldb_msg_new(tmp_ctx);
+ if (hwm_msg == NULL) {
+ DEBUG(1, ("Out of memory when creating ldb_message\n"));
+ status = NT_STATUS_NO_MEMORY;
+ goto failed;
+ }
+
+ hwm_msg->dn = dn;
+
+ new_xid = hwm;
+ hwm++;
+
+ hwm_string = talloc_asprintf(tmp_ctx, "%u", hwm);
+ if (hwm_string == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto failed;
+ }
+
+ sid_string = dom_sid_string(tmp_ctx, sid);
+ if (sid_string == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto failed;
+ }
+
+ unixid_string = talloc_asprintf(tmp_ctx, "%u", new_xid);
+ if (unixid_string == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto failed;
+ }
+
+ if (hwm_entry_exists) {
+ struct ldb_message_element *els;
+ struct ldb_val *vals;
+
+ /* We're modifying the entry, not just adding a new one. */
+ els = talloc_array(tmp_ctx, struct ldb_message_element, 2);
+ if (els == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto failed;
+ }
+
+ vals = talloc_array(tmp_ctx, struct ldb_val, 2);
+ if (els == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto failed;
+ }
+
+ hwm_msg->num_elements = 2;
+ hwm_msg->elements = els;
+
+ els[0].num_values = 1;
+ els[0].values = &vals[0];
+ els[0].flags = LDB_FLAG_MOD_DELETE;
+ els[0].name = talloc_strdup(tmp_ctx, "xidNumber");
+ if (els[0].name == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto failed;
+ }
+
+ els[1].num_values = 1;
+ els[1].values = &vals[1];
+ els[1].flags = LDB_FLAG_MOD_ADD;
+ els[1].name = els[0].name;
+
+ vals[0].data = (uint8_t *)unixid_string;
+ vals[0].length = strlen(unixid_string);
+ vals[1].data = (uint8_t *)hwm_string;
+ vals[1].length = strlen(hwm_string);
+ } else {
+ ret = ldb_msg_add_empty(hwm_msg, "xidNumber", LDB_FLAG_MOD_ADD,
+ NULL);
+ if (ret != LDB_SUCCESS) {
+ status = NT_STATUS_NONE_MAPPED;
+ goto failed;
+ }
+
+ ret = ldb_msg_add_string(hwm_msg, "xidNumber", hwm_string);
+ if (ret != LDB_SUCCESS)
+ {
+ status = NT_STATUS_NONE_MAPPED;
+ goto failed;
+ }
+ }
+
+ ret = ldb_modify(ldb, hwm_msg);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(1, ("Updating the xid high water mark failed: %s\n",
+ ldb_errstring(ldb)));
+ status = NT_STATUS_NONE_MAPPED;
+ goto failed;
+ }
+
+ map_msg = ldb_msg_new(tmp_ctx);
+ if (map_msg == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto failed;
+ }
+
+ map_msg->dn = ldb_dn_new_fmt(tmp_ctx, ldb, "CN=%s", sid_string);
+ if (map_msg->dn == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto failed;
+ }
+
+ ret = ldb_msg_add_string(map_msg, "xidNumber", unixid_string);
+ if (ret != LDB_SUCCESS) {
+ status = NT_STATUS_NONE_MAPPED;
+ goto failed;
+ }
+
+ ret = idmap_msg_add_dom_sid(idmap_ctx, tmp_ctx, map_msg, "objectSid",
+ sid);
+ if (ret != LDB_SUCCESS) {
+ status = NT_STATUS_NONE_MAPPED;
+ goto failed;
+ }
+
+ ret = ldb_msg_add_string(map_msg, "objectClass", "sidMap");
+ if (ret != LDB_SUCCESS) {
+ status = NT_STATUS_NONE_MAPPED;
+ goto failed;
+ }
+
+ ret = ldb_msg_add_string(map_msg, "type", "ID_TYPE_BOTH");
+ if (ret != LDB_SUCCESS) {
+ status = NT_STATUS_NONE_MAPPED;
+ goto failed;
+ }
+
+ ret = ldb_msg_add_string(map_msg, "cn", sid_string);
+ if (ret != LDB_SUCCESS) {
+ status = NT_STATUS_NONE_MAPPED;
+ goto failed;
+ }
+
+ ret = ldb_add(ldb, map_msg);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(1, ("Adding a sidmap failed: %s\n", ldb_errstring(ldb)));
+ status = NT_STATUS_NONE_MAPPED;
+ goto failed;
+ }
+
+ trans = ldb_transaction_commit(ldb);
+ if (trans != LDB_SUCCESS) {
+ DEBUG(1, ("Transaction failed: %s\n", ldb_errstring(ldb)));
+ status = NT_STATUS_NONE_MAPPED;
+ goto failed;
+ }
+
+ *unixid = talloc(mem_ctx, struct unixid);
+ if (*unixid == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto failed;
+ }
+
+ (*unixid)->id = new_xid;
+ (*unixid)->type = ID_TYPE_BOTH;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_OK;
+
+failed:
+ if (trans == LDB_SUCCESS) ldb_transaction_cancel(ldb);
+ talloc_free(tmp_ctx);
+ return status;
+}
+
+/**
+ * Convert an array of unixids to the corresponding array of SIDs
+ *
+ * \param idmap_ctx idmap context to use
+ * \param mem_ctx talloc context the memory for the dom_sids is allocated
+ * from.
+ * \param count length of id_mapping array.
+ * \param id array of id_mappings.
+ * \return NT_STATUS_OK on success, NT_STATUS_NONE_MAPPED if mapping is not
+ * possible at all, NT_STATUS_SOME_UNMAPPED if some mappings worked and some
+ * did not.
+ */
+
+NTSTATUS idmap_xids_to_sids(struct idmap_context *idmap_ctx,
+ TALLOC_CTX *mem_ctx, int count,
+ struct id_mapping *id)
+{
+ int i;
+ int error_count = 0;
+
+ for (i = 0; i < count; ++i) {
+ id[i].status = idmap_xid_to_sid(idmap_ctx, mem_ctx,
+ id[i].unixid, &id[i].sid);
+ if (NT_STATUS_EQUAL(id[i].status, NT_STATUS_RETRY)) {
+ id[i].status = idmap_xid_to_sid(idmap_ctx, mem_ctx,
+ id[i].unixid,
+ &id[i].sid);
+ }
+ if (!NT_STATUS_IS_OK(id[i].status)) {
+ DEBUG(1, ("idmapping xid_to_sid failed for id[%d]\n", i));
+ error_count++;
+ }
+ }
+
+ if (error_count == count) {
+ /* Mapping did not work at all. */
+ return NT_STATUS_NONE_MAPPED;
+ } else if (error_count > 0) {
+ /* Some mappings worked, some did not. */
+ return STATUS_SOME_UNMAPPED;
+ } else {
+ return NT_STATUS_OK;
+ }
+}
+
+/**
+ * Convert an array of SIDs to the corresponding array of unixids
+ *
+ * \param idmap_ctx idmap context to use
+ * \param mem_ctx talloc context the memory for the unixids is allocated
+ * from.
+ * \param count length of id_mapping array.
+ * \param id array of id_mappings.
+ * \return NT_STATUS_OK on success, NT_STATUS_NONE_MAPPED if mapping is not
+ * possible at all, NT_STATUS_SOME_UNMAPPED if some mappings worked and some
+ * did not.
+ */
+
+NTSTATUS idmap_sids_to_xids(struct idmap_context *idmap_ctx,
+ TALLOC_CTX *mem_ctx, int count,
+ struct id_mapping *id)
+{
+ int i;
+ int error_count = 0;
+
+ for (i = 0; i < count; ++i) {
+ id[i].status = idmap_sid_to_xid(idmap_ctx, mem_ctx,
+ id[i].sid, &id[i].unixid);
+ if (NT_STATUS_EQUAL(id[i].status, NT_STATUS_RETRY)) {
+ id[i].status = idmap_sid_to_xid(idmap_ctx, mem_ctx,
+ id[i].sid,
+ &id[i].unixid);
+ }
+ if (!NT_STATUS_IS_OK(id[i].status)) {
+ DEBUG(1, ("idmapping sid_to_xid failed for id[%d]\n", i));
+ error_count++;
+ }
+ }
+
+ if (error_count == count) {
+ /* Mapping did not work at all. */
+ return NT_STATUS_NONE_MAPPED;
+ } else if (error_count > 0) {
+ /* Some mappings worked, some did not. */
+ return STATUS_SOME_UNMAPPED;
+ } else {
+ return NT_STATUS_OK;
+ }
+}
+
diff --git a/source4/winbind/idmap.h b/source4/winbind/idmap.h
new file mode 100644
index 0000000000..7429466bed
--- /dev/null
+++ b/source4/winbind/idmap.h
@@ -0,0 +1,39 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Map SIDs to uids/gids and back
+
+ Copyright (C) Kai Blin 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _IDMAP_H_
+#define _IDMAP_H_
+
+#include "librpc/gen_ndr/winbind.h"
+
+struct idmap_context {
+ struct loadparm_context *lp_ctx;
+ struct ldb_context *ldb_ctx;
+ struct dom_sid *unix_groups_sid;
+ struct dom_sid *unix_users_sid;
+};
+
+struct tevent_context;
+
+#include "winbind/idmap_proto.h"
+
+#endif
+
diff --git a/source4/winbind/wb_async_helpers.c b/source4/winbind/wb_async_helpers.c
new file mode 100644
index 0000000000..a50a0fe473
--- /dev/null
+++ b/source4/winbind/wb_async_helpers.c
@@ -0,0 +1,458 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+/*
+ a composite API for finding a DC and its name
+*/
+
+#include "includes.h"
+#include "libcli/composite/composite.h"
+#include "winbind/wb_async_helpers.h"
+
+#include "lib/messaging/irpc.h"
+#include "librpc/gen_ndr/irpc.h"
+#include "auth/credentials/credentials.h"
+#include "libcli/security/security.h"
+#include "libcli/auth/libcli_auth.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 "winbind/wb_helper.h"
+
+struct lsa_lookupsids_state {
+ struct composite_context *ctx;
+ int num_sids;
+ struct lsa_LookupSids r;
+ struct lsa_SidArray sids;
+ struct lsa_TransNameArray names;
+ struct lsa_RefDomainList *domains;
+ uint32_t count;
+ struct wb_sid_object **result;
+};
+
+static void lsa_lookupsids_recv_names(struct rpc_request *req);
+
+struct composite_context *wb_lsa_lookupsids_send(TALLOC_CTX *mem_ctx,
+ struct dcerpc_pipe *lsa_pipe,
+ struct policy_handle *handle,
+ int num_sids,
+ const struct dom_sid **sids)
+{
+ struct composite_context *result;
+ struct rpc_request *req;
+ struct lsa_lookupsids_state *state;
+ int i;
+
+ result = composite_create(mem_ctx, lsa_pipe->conn->event_ctx);
+ if (result == NULL) goto failed;
+
+ state = talloc(result, struct lsa_lookupsids_state);
+ if (state == NULL) goto failed;
+ result->private_data = state;
+ state->ctx = result;
+
+ state->sids.num_sids = num_sids;
+ state->sids.sids = talloc_array(state, struct lsa_SidPtr, num_sids);
+ if (state->sids.sids == NULL) goto failed;
+
+ for (i=0; i<num_sids; i++) {
+ state->sids.sids[i].sid = dom_sid_dup(state->sids.sids,
+ sids[i]);
+ if (state->sids.sids[i].sid == NULL) goto failed;
+ }
+
+ state->domains = talloc(state, struct lsa_RefDomainList);
+ if (state->domains == NULL) goto failed;
+
+ state->count = 0;
+ state->num_sids = num_sids;
+ state->names.count = 0;
+ state->names.names = NULL;
+
+ state->r.in.handle = handle;
+ state->r.in.sids = &state->sids;
+ state->r.in.names = &state->names;
+ state->r.in.level = 1;
+ state->r.in.count = &state->count;
+ state->r.out.names = &state->names;
+ state->r.out.count = &state->count;
+ state->r.out.domains = &state->domains;
+
+ req = dcerpc_lsa_LookupSids_send(lsa_pipe, state, &state->r);
+ if (req == NULL) goto failed;
+
+ req->async.callback = lsa_lookupsids_recv_names;
+ req->async.private_data = state;
+ return result;
+
+ failed:
+ talloc_free(result);
+ return NULL;
+}
+
+static void lsa_lookupsids_recv_names(struct rpc_request *req)
+{
+ struct lsa_lookupsids_state *state =
+ talloc_get_type(req->async.private_data,
+ struct lsa_lookupsids_state);
+ int i;
+
+ state->ctx->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(state->ctx)) return;
+ state->ctx->status = state->r.out.result;
+ if (!NT_STATUS_IS_OK(state->ctx->status) &&
+ !NT_STATUS_EQUAL(state->ctx->status, STATUS_SOME_UNMAPPED)) {
+ composite_error(state->ctx, state->ctx->status);
+ return;
+ }
+
+ state->result = talloc_array(state, struct wb_sid_object *,
+ state->num_sids);
+ if (composite_nomem(state->result, state->ctx)) return;
+
+ for (i=0; i<state->num_sids; i++) {
+ struct lsa_TranslatedName *name =
+ &state->r.out.names->names[i];
+ struct lsa_DomainInfo *dom;
+ struct lsa_RefDomainList *domains =
+ state->domains;
+
+ state->result[i] = talloc_zero(state->result,
+ struct wb_sid_object);
+ if (composite_nomem(state->result[i], state->ctx)) return;
+
+ state->result[i]->type = name->sid_type;
+ if (state->result[i]->type == SID_NAME_UNKNOWN) {
+ continue;
+ }
+
+ if (name->sid_index >= domains->count) {
+ composite_error(state->ctx,
+ NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ dom = &domains->domains[name->sid_index];
+ state->result[i]->domain = talloc_reference(state->result[i],
+ dom->name.string);
+ if ((name->sid_type == SID_NAME_DOMAIN) ||
+ (name->name.string == NULL)) {
+ state->result[i]->name =
+ talloc_strdup(state->result[i], "");
+ } else {
+ state->result[i]->name =
+ talloc_steal(state->result[i],
+ name->name.string);
+ }
+
+ if (composite_nomem(state->result[i]->name, state->ctx)) {
+ return;
+ }
+ }
+
+ composite_done(state->ctx);
+}
+
+NTSTATUS wb_lsa_lookupsids_recv(struct composite_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct wb_sid_object ***names)
+{
+ NTSTATUS status = composite_wait(c);
+ if (NT_STATUS_IS_OK(status)) {
+ struct lsa_lookupsids_state *state =
+ talloc_get_type(c->private_data,
+ struct lsa_lookupsids_state);
+ *names = talloc_steal(mem_ctx, state->result);
+ }
+ talloc_free(c);
+ return status;
+}
+
+
+struct lsa_lookupnames_state {
+ struct composite_context *ctx;
+ uint32_t num_names;
+ struct lsa_LookupNames r;
+ struct lsa_TransSidArray sids;
+ struct lsa_RefDomainList *domains;
+ uint32_t count;
+ struct wb_sid_object **result;
+};
+
+static void lsa_lookupnames_recv_sids(struct rpc_request *req);
+
+struct composite_context *wb_lsa_lookupnames_send(TALLOC_CTX *mem_ctx,
+ struct dcerpc_pipe *lsa_pipe,
+ struct policy_handle *handle,
+ int num_names,
+ const char **names)
+{
+ struct composite_context *result;
+ struct rpc_request *req;
+ struct lsa_lookupnames_state *state;
+
+ struct lsa_String *lsa_names;
+ int i;
+
+ result = composite_create(mem_ctx, lsa_pipe->conn->event_ctx);
+ if (result == NULL) goto failed;
+
+ state = talloc(result, struct lsa_lookupnames_state);
+ if (state == NULL) goto failed;
+ result->private_data = state;
+ state->ctx = result;
+
+ state->sids.count = 0;
+ state->sids.sids = NULL;
+ state->num_names = num_names;
+ state->count = 0;
+
+ lsa_names = talloc_array(state, struct lsa_String, num_names);
+ if (lsa_names == NULL) goto failed;
+
+ for (i=0; i<num_names; i++) {
+ lsa_names[i].string = names[i];
+ }
+
+ state->domains = talloc(state, struct lsa_RefDomainList);
+ if (state->domains == NULL) goto failed;
+
+ state->r.in.handle = handle;
+ state->r.in.num_names = num_names;
+ state->r.in.names = lsa_names;
+ state->r.in.sids = &state->sids;
+ state->r.in.level = 1;
+ state->r.in.count = &state->count;
+ state->r.out.count = &state->count;
+ state->r.out.sids = &state->sids;
+ state->r.out.domains = &state->domains;
+
+ req = dcerpc_lsa_LookupNames_send(lsa_pipe, state, &state->r);
+ if (req == NULL) goto failed;
+
+ req->async.callback = lsa_lookupnames_recv_sids;
+ req->async.private_data = state;
+ return result;
+
+ failed:
+ talloc_free(result);
+ return NULL;
+}
+
+static void lsa_lookupnames_recv_sids(struct rpc_request *req)
+{
+ struct lsa_lookupnames_state *state =
+ talloc_get_type(req->async.private_data,
+ struct lsa_lookupnames_state);
+ int i;
+
+ state->ctx->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(state->ctx)) return;
+ state->ctx->status = state->r.out.result;
+ if (!NT_STATUS_IS_OK(state->ctx->status) &&
+ !NT_STATUS_EQUAL(state->ctx->status, STATUS_SOME_UNMAPPED)) {
+ composite_error(state->ctx, state->ctx->status);
+ return;
+ }
+
+ state->result = talloc_array(state, struct wb_sid_object *,
+ state->num_names);
+ if (composite_nomem(state->result, state->ctx)) return;
+
+ for (i=0; i<state->num_names; i++) {
+ struct lsa_TranslatedSid *sid = &state->r.out.sids->sids[i];
+ struct lsa_RefDomainList *domains = state->domains;
+ struct lsa_DomainInfo *dom;
+
+ state->result[i] = talloc_zero(state->result,
+ struct wb_sid_object);
+ if (composite_nomem(state->result[i], state->ctx)) return;
+
+ state->result[i]->type = sid->sid_type;
+ if (state->result[i]->type == SID_NAME_UNKNOWN) {
+ continue;
+ }
+
+ if (sid->sid_index >= domains->count) {
+ composite_error(state->ctx,
+ NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ dom = &domains->domains[sid->sid_index];
+
+ state->result[i]->sid = dom_sid_add_rid(state->result[i],
+ dom->sid, sid->rid);
+ }
+
+ composite_done(state->ctx);
+}
+
+NTSTATUS wb_lsa_lookupnames_recv(struct composite_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct wb_sid_object ***sids)
+{
+ NTSTATUS status = composite_wait(c);
+ if (NT_STATUS_IS_OK(status)) {
+ struct lsa_lookupnames_state *state =
+ talloc_get_type(c->private_data,
+ struct lsa_lookupnames_state);
+ *sids = talloc_steal(mem_ctx, state->result);
+ }
+ talloc_free(c);
+ return status;
+}
+struct samr_getuserdomgroups_state {
+ struct composite_context *ctx;
+ struct dcerpc_pipe *samr_pipe;
+
+ int num_rids;
+ uint32_t *rids;
+
+ struct samr_RidWithAttributeArray *rid_array;
+
+ struct policy_handle *user_handle;
+ struct samr_OpenUser o;
+ struct samr_GetGroupsForUser g;
+ struct samr_Close c;
+};
+
+static void samr_usergroups_recv_open(struct rpc_request *req);
+static void samr_usergroups_recv_groups(struct rpc_request *req);
+static void samr_usergroups_recv_close(struct rpc_request *req);
+
+struct composite_context *wb_samr_userdomgroups_send(TALLOC_CTX *mem_ctx,
+ struct dcerpc_pipe *samr_pipe,
+ struct policy_handle *domain_handle,
+ uint32_t rid)
+{
+ struct composite_context *result;
+ struct rpc_request *req;
+ struct samr_getuserdomgroups_state *state;
+
+ result = composite_create(mem_ctx, samr_pipe->conn->event_ctx);
+ if (result == NULL) goto failed;
+
+ state = talloc(result, struct samr_getuserdomgroups_state);
+ if (state == NULL) goto failed;
+ result->private_data = state;
+ state->ctx = result;
+
+ state->samr_pipe = samr_pipe;
+
+ state->user_handle = talloc(state, struct policy_handle);
+ if (state->user_handle == NULL) goto failed;
+
+ state->o.in.domain_handle = domain_handle;
+ state->o.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ state->o.in.rid = rid;
+ state->o.out.user_handle = state->user_handle;
+
+ req = dcerpc_samr_OpenUser_send(state->samr_pipe, state, &state->o);
+ if (req == NULL) goto failed;
+
+ req->async.callback = samr_usergroups_recv_open;
+ req->async.private_data = state;
+ return result;
+
+ failed:
+ talloc_free(result);
+ return NULL;
+}
+
+static void samr_usergroups_recv_open(struct rpc_request *req)
+{
+ struct samr_getuserdomgroups_state *state =
+ talloc_get_type(req->async.private_data,
+ struct samr_getuserdomgroups_state);
+
+ state->ctx->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(state->ctx)) return;
+ state->ctx->status = state->o.out.result;
+ if (!composite_is_ok(state->ctx)) return;
+
+ state->g.in.user_handle = state->user_handle;
+ state->g.out.rids = &state->rid_array;
+
+ req = dcerpc_samr_GetGroupsForUser_send(state->samr_pipe, state,
+ &state->g);
+ composite_continue_rpc(state->ctx, req, samr_usergroups_recv_groups,
+ state);
+}
+
+static void samr_usergroups_recv_groups(struct rpc_request *req)
+{
+ struct samr_getuserdomgroups_state *state =
+ talloc_get_type(req->async.private_data,
+ struct samr_getuserdomgroups_state);
+
+ state->ctx->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(state->ctx)) return;
+ state->ctx->status = state->g.out.result;
+ if (!composite_is_ok(state->ctx)) return;
+
+ state->c.in.handle = state->user_handle;
+ state->c.out.handle = state->user_handle;
+
+ req = dcerpc_samr_Close_send(state->samr_pipe, state, &state->c);
+ composite_continue_rpc(state->ctx, req, samr_usergroups_recv_close,
+ state);
+}
+
+static void samr_usergroups_recv_close(struct rpc_request *req)
+{
+ struct samr_getuserdomgroups_state *state =
+ talloc_get_type(req->async.private_data,
+ struct samr_getuserdomgroups_state);
+
+ state->ctx->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(state->ctx)) return;
+ state->ctx->status = state->c.out.result;
+ if (!composite_is_ok(state->ctx)) return;
+
+ composite_done(state->ctx);
+}
+
+NTSTATUS wb_samr_userdomgroups_recv(struct composite_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ int *num_rids, uint32_t **rids)
+{
+ struct samr_getuserdomgroups_state *state =
+ talloc_get_type(ctx->private_data,
+ struct samr_getuserdomgroups_state);
+
+ int i;
+ NTSTATUS status = composite_wait(ctx);
+ if (!NT_STATUS_IS_OK(status)) goto done;
+
+ *num_rids = state->rid_array->count;
+ *rids = talloc_array(mem_ctx, uint32_t, *num_rids);
+ if (*rids == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ for (i=0; i<*num_rids; i++) {
+ (*rids)[i] = state->rid_array->rids[i].rid;
+ }
+
+ done:
+ talloc_free(ctx);
+ return status;
+}
diff --git a/source4/winbind/wb_async_helpers.h b/source4/winbind/wb_async_helpers.h
new file mode 100644
index 0000000000..0dc2d4ff10
--- /dev/null
+++ b/source4/winbind/wb_async_helpers.h
@@ -0,0 +1,34 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB composite request interfaces
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __WB_ASYNC_HELPERS_H__
+#define __WB_ASYNC_HELPERS_H__
+
+#include "librpc/gen_ndr/lsa.h"
+
+struct wb_sid_object {
+ enum lsa_SidType type;
+ struct dom_sid *sid;
+ const char *domain;
+ const char *name;
+};
+
+#endif /* __WB_ASYNC_HELPERS_H__ */
diff --git a/source4/winbind/wb_cmd_getdcname.c b/source4/winbind/wb_cmd_getdcname.c
new file mode 100644
index 0000000000..66dcbf9d7b
--- /dev/null
+++ b/source4/winbind/wb_cmd_getdcname.c
@@ -0,0 +1,125 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Command backend for wbinfo --getdcname
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/composite/composite.h"
+#include "winbind/wb_server.h"
+#include "smbd/service_task.h"
+
+#include "librpc/gen_ndr/ndr_netlogon_c.h"
+
+struct cmd_getdcname_state {
+ struct composite_context *ctx;
+ const char *domain_name;
+
+ struct netr_GetAnyDCName g;
+};
+
+static void getdcname_recv_domain(struct composite_context *ctx);
+static void getdcname_recv_dcname(struct rpc_request *req);
+
+struct composite_context *wb_cmd_getdcname_send(TALLOC_CTX *mem_ctx,
+ struct wbsrv_service *service,
+ const char *domain_name)
+{
+ struct composite_context *result, *ctx;
+ struct cmd_getdcname_state *state;
+
+ result = composite_create(mem_ctx, service->task->event_ctx);
+ if (result == NULL) goto failed;
+
+ state = talloc(result, struct cmd_getdcname_state);
+ if (state == NULL) goto failed;
+ state->ctx = result;
+ result->private_data = state;
+
+ state->domain_name = talloc_strdup(state, domain_name);
+ if (state->domain_name == NULL) goto failed;
+
+ ctx = wb_sid2domain_send(state, service, service->primary_sid);
+ if (ctx == NULL) goto failed;
+
+ ctx->async.fn = getdcname_recv_domain;
+ ctx->async.private_data = state;
+ return result;
+
+ failed:
+ talloc_free(result);
+ return NULL;
+}
+
+static void getdcname_recv_domain(struct composite_context *ctx)
+{
+ struct cmd_getdcname_state *state =
+ talloc_get_type(ctx->async.private_data,
+ struct cmd_getdcname_state);
+ struct wbsrv_domain *domain;
+ struct rpc_request *req;
+
+ state->ctx->status = wb_sid2domain_recv(ctx, &domain);
+ if (!composite_is_ok(state->ctx)) return;
+
+ state->g.in.logon_server = talloc_asprintf(
+ state, "\\\\%s",
+ dcerpc_server_name(domain->netlogon_pipe));
+ state->g.in.domainname = state->domain_name;
+ state->g.out.dcname = talloc(state, const char *);
+
+ req = dcerpc_netr_GetAnyDCName_send(domain->netlogon_pipe, state,
+ &state->g);
+ if (composite_nomem(req, state->ctx)) return;
+
+ composite_continue_rpc(state->ctx, req, getdcname_recv_dcname, state);
+}
+
+static void getdcname_recv_dcname(struct rpc_request *req)
+{
+ struct cmd_getdcname_state *state =
+ talloc_get_type(req->async.private_data,
+ struct cmd_getdcname_state);
+
+ state->ctx->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(state->ctx)) return;
+ state->ctx->status = werror_to_ntstatus(state->g.out.result);
+ if (!composite_is_ok(state->ctx)) return;
+
+ composite_done(state->ctx);
+}
+
+NTSTATUS wb_cmd_getdcname_recv(struct composite_context *c,
+ TALLOC_CTX *mem_ctx,
+ const char **dcname)
+{
+ struct cmd_getdcname_state *state =
+ talloc_get_type(c->private_data, struct cmd_getdcname_state);
+ NTSTATUS status = composite_wait(c);
+ if (NT_STATUS_IS_OK(status)) {
+ const char *p = *(state->g.out.dcname);
+ if (*p == '\\') p += 1;
+ if (*p == '\\') p += 1;
+ *dcname = talloc_strdup(mem_ctx, p);
+ if (*dcname == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ }
+ }
+ talloc_free(state);
+ return status;
+}
diff --git a/source4/winbind/wb_cmd_getgrgid.c b/source4/winbind/wb_cmd_getgrgid.c
new file mode 100644
index 0000000000..80f4e9cfc3
--- /dev/null
+++ b/source4/winbind/wb_cmd_getgrgid.c
@@ -0,0 +1,177 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Backend for getgrgid
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/composite/composite.h"
+#include "winbind/wb_server.h"
+#include "winbind/wb_async_helpers.h"
+#include "winbind/wb_helper.h"
+#include "smbd/service_task.h"
+#include "libnet/libnet_proto.h"
+#include "param/param.h"
+#include "libcli/security/proto.h"
+#include "auth/credentials/credentials.h"
+
+struct cmd_getgrgid_state {
+ struct composite_context *ctx;
+ struct wbsrv_service *service;
+ gid_t gid;
+ struct dom_sid *sid;
+ char *workgroup;
+ struct wbsrv_domain *domain;
+
+ struct winbindd_gr *result;
+};
+
+static void cmd_getgrgid_recv_sid(struct composite_context *ctx);
+static void cmd_getgrgid_recv_domain(struct composite_context *ctx);
+static void cmd_getgrgid_recv_group_info(struct composite_context *ctx);
+
+/* Get the SID using the gid */
+
+struct composite_context *wb_cmd_getgrgid_send(TALLOC_CTX *mem_ctx,
+ struct wbsrv_service *service,
+ gid_t gid)
+{
+ struct composite_context *ctx, *result;
+ struct cmd_getgrgid_state *state;
+
+ DEBUG(5, ("wb_cmd_getgrgid_send called\n"));
+
+ result = composite_create(mem_ctx, service->task->event_ctx);
+ if (!result) return NULL;
+
+ state = talloc(result, struct cmd_getgrgid_state);
+ if (composite_nomem(state, result)) return result;
+ state->ctx = result;
+ result->private_data = state;
+ state->service = service;
+ state->gid = gid;
+
+ ctx = wb_gid2sid_send(state, service, gid);
+ if (composite_nomem(ctx, state->ctx)) return result;
+
+ composite_continue(result, ctx, cmd_getgrgid_recv_sid, state);
+ return result;
+}
+
+
+/* Receive the sid and get the domain structure with it */
+
+static void cmd_getgrgid_recv_sid(struct composite_context *ctx)
+{
+ struct cmd_getgrgid_state *state =
+ talloc_get_type(ctx->async.private_data,
+ struct cmd_getgrgid_state);
+
+ DEBUG(5, ("cmd_getgrgid_recv_sid called %p\n", ctx->private_data));
+
+ state->ctx->status = wb_gid2sid_recv(ctx, state, &state->sid);
+ if (!composite_is_ok(state->ctx)) return;
+
+ ctx = wb_sid2domain_send(state, state->service, state->sid);
+
+ composite_continue(state->ctx, ctx, cmd_getgrgid_recv_domain, state);
+}
+
+/* Receive the domain struct and call libnet to get the user info struct */
+
+static void cmd_getgrgid_recv_domain(struct composite_context *ctx)
+{
+ struct cmd_getgrgid_state *state =
+ talloc_get_type(ctx->async.private_data,
+ struct cmd_getgrgid_state);
+ struct libnet_GroupInfo *group_info;
+
+ DEBUG(5, ("cmd_getgrgid_recv_domain called\n"));
+
+ state->ctx->status = wb_sid2domain_recv(ctx, &state->domain);
+ if (!composite_is_ok(state->ctx)) return;
+
+ group_info = talloc(state, struct libnet_GroupInfo);
+ if (composite_nomem(group_info, state->ctx)) return;
+
+ group_info->in.level = GROUP_INFO_BY_SID;
+ group_info->in.data.group_sid = state->sid;
+ group_info->in.domain_name = state->domain->libnet_ctx->samr.name;
+
+ /* We need the workgroup later, so copy it */
+ state->workgroup = talloc_strdup(state,
+ state->domain->libnet_ctx->samr.name);
+ if (composite_nomem(state->workgroup, state->ctx)) return;
+
+ ctx = libnet_GroupInfo_send(state->domain->libnet_ctx, state,group_info,
+ NULL);
+
+ composite_continue(state->ctx, ctx, cmd_getgrgid_recv_group_info,state);
+}
+
+/* Receive the group info struct */
+
+static void cmd_getgrgid_recv_group_info(struct composite_context *ctx)
+{
+ struct cmd_getgrgid_state *state =
+ talloc_get_type(ctx->async.private_data,
+ struct cmd_getgrgid_state);
+ struct libnet_GroupInfo *group_info;
+ struct winbindd_gr *gr;
+
+ DEBUG(5, ("cmd_getgrgid_recv_group_info called\n"));
+
+ gr = talloc(state, struct winbindd_gr);
+ if (composite_nomem(gr, state->ctx)) return;
+
+ group_info = talloc(state, struct libnet_GroupInfo);
+ if(composite_nomem(group_info, state->ctx)) return;
+
+ state->ctx->status = libnet_GroupInfo_recv(ctx, state, group_info);
+ if (!composite_is_ok(state->ctx)) return;
+
+ WBSRV_SAMBA3_SET_STRING(gr->gr_name, group_info->out.group_name);
+ WBSRV_SAMBA3_SET_STRING(gr->gr_passwd, "*");
+
+ gr->gr_gid = state->gid;
+
+ state->result = gr;
+
+ composite_done(state->ctx);
+}
+
+NTSTATUS wb_cmd_getgrgid_recv(struct composite_context *ctx,
+ TALLOC_CTX *mem_ctx, struct winbindd_gr **gr)
+{
+ NTSTATUS status = composite_wait(ctx);
+
+ DEBUG(5, ("wb_cmd_getgrgid_recv called\n"));
+
+ DEBUG(5, ("status is %s\n", nt_errstr(status)));
+
+ if (NT_STATUS_IS_OK(status)) {
+ struct cmd_getgrgid_state *state =
+ talloc_get_type(ctx->private_data,
+ struct cmd_getgrgid_state);
+ *gr = talloc_steal(mem_ctx, state->result);
+ }
+ talloc_free(ctx);
+ return status;
+
+}
+
diff --git a/source4/winbind/wb_cmd_getgrnam.c b/source4/winbind/wb_cmd_getgrnam.c
new file mode 100644
index 0000000000..bfc30fc7a6
--- /dev/null
+++ b/source4/winbind/wb_cmd_getgrnam.c
@@ -0,0 +1,168 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Command backend for wbinfo --group-info
+
+ Copyright (C) Kai Blin 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/composite/composite.h"
+#include "winbind/wb_server.h"
+#include "winbind/wb_async_helpers.h"
+#include "param/param.h"
+#include "winbind/wb_helper.h"
+#include "smbd/service_task.h"
+#include "libnet/libnet_proto.h"
+#include "libcli/security/proto.h"
+
+struct cmd_getgrnam_state {
+ struct composite_context *ctx;
+ struct wbsrv_service *service;
+ char *name;
+ char *workgroup_name;
+ struct dom_sid *group_sid;
+
+ struct winbindd_gr *result;
+};
+
+static void cmd_getgrnam_recv_domain(struct composite_context *ctx);
+static void cmd_getgrnam_recv_group_info(struct composite_context *ctx);
+static void cmd_getgrnam_recv_gid(struct composite_context *ctx);
+
+struct composite_context *wb_cmd_getgrnam_send(TALLOC_CTX *mem_ctx,
+ struct wbsrv_service *service,
+ const char *name)
+{
+ struct composite_context *result, *ctx;
+ struct cmd_getgrnam_state *state;
+
+ DEBUG(5, ("wb_cmd_getgrnam_send called\n"));
+
+ result = composite_create(mem_ctx, service->task->event_ctx);
+ if (!result) return NULL;
+
+ state = talloc(result, struct cmd_getgrnam_state);
+ if (composite_nomem(state, result)) return result;
+ state->ctx = result;
+ result->private_data = state;
+ state->service = service;
+ state->name = talloc_strdup(state, name);
+ if(composite_nomem(state->name, result)) return result;
+
+ ctx = wb_name2domain_send(state, service, name);
+ if (composite_nomem(ctx, result)) return result;
+
+ composite_continue(result, ctx, cmd_getgrnam_recv_domain, state);
+ return result;
+}
+
+static void cmd_getgrnam_recv_domain(struct composite_context *ctx)
+{
+ struct cmd_getgrnam_state *state = talloc_get_type(
+ ctx->async.private_data, struct cmd_getgrnam_state);
+ struct wbsrv_domain *domain;
+ struct libnet_GroupInfo *group_info;
+ char *group_dom, *group_name;
+ bool ok;
+
+ state->ctx->status = wb_name2domain_recv(ctx, &domain);
+ if(!composite_is_ok(state->ctx)) return;
+
+ group_info = talloc(state, struct libnet_GroupInfo);
+ if (composite_nomem(group_info, state->ctx)) return;
+
+ ok = wb_samba3_split_username(state, state->service->task->lp_ctx,
+ state->name, &group_dom, &group_name);
+ if(!ok){
+ composite_error(state->ctx, NT_STATUS_OBJECT_NAME_INVALID);
+ return;
+ }
+
+ group_info->in.level = GROUP_INFO_BY_NAME;
+ group_info->in.data.group_name = group_name;
+ group_info->in.domain_name = group_dom;
+ state->workgroup_name = talloc_strdup(state, group_dom);
+ if(composite_nomem(state->workgroup_name, state->ctx)) return;
+
+ ctx = libnet_GroupInfo_send(domain->libnet_ctx, state, group_info,NULL);
+
+ composite_continue(state->ctx, ctx, cmd_getgrnam_recv_group_info,state);
+}
+
+static void cmd_getgrnam_recv_group_info(struct composite_context *ctx)
+{
+ struct cmd_getgrnam_state *state = talloc_get_type(
+ ctx->async.private_data, struct cmd_getgrnam_state);
+ struct libnet_GroupInfo *group_info;
+ struct winbindd_gr *gr;
+
+ DEBUG(5, ("cmd_getgrnam_recv_group_info called\n"));
+
+ group_info = talloc(state, struct libnet_GroupInfo);
+ if(composite_nomem(group_info, state->ctx)) return;
+
+ gr = talloc(state, struct winbindd_gr);
+ if(composite_nomem(gr, state->ctx)) return;
+
+ state->ctx->status = libnet_GroupInfo_recv(ctx, state, group_info);
+ if(!composite_is_ok(state->ctx)) return;
+
+ WBSRV_SAMBA3_SET_STRING(gr->gr_name, group_info->out.group_name);
+ WBSRV_SAMBA3_SET_STRING(gr->gr_passwd, "*");
+ gr->num_gr_mem = group_info->out.num_members;
+ gr->gr_mem_ofs = 0;
+
+ state->result = gr;
+
+ ctx = wb_sid2gid_send(state, state->service, group_info->out.group_sid);
+ composite_continue(state->ctx, ctx, cmd_getgrnam_recv_gid, state);
+}
+
+static void cmd_getgrnam_recv_gid(struct composite_context *ctx)
+{
+ struct cmd_getgrnam_state *state = talloc_get_type(
+ ctx->async.private_data, struct cmd_getgrnam_state);
+ gid_t gid;
+
+ DEBUG(5, ("cmd_getgrnam_recv_gid called\n"));
+
+ state->ctx->status = wb_sid2gid_recv(ctx, &gid);
+ if(!composite_is_ok(state->ctx)) return;
+
+ state->result->gr_gid = gid;
+
+ composite_done(state->ctx);
+}
+
+NTSTATUS wb_cmd_getgrnam_recv(struct composite_context *ctx,
+ TALLOC_CTX *mem_ctx, struct winbindd_gr **gr)
+{
+ NTSTATUS status = composite_wait(ctx);
+
+ DEBUG(5, ("wb_cmd_getgrnam_recv called\n"));
+
+ if (NT_STATUS_IS_OK(status)) {
+ struct cmd_getgrnam_state *state =
+ talloc_get_type(ctx->private_data,
+ struct cmd_getgrnam_state);
+ *gr = talloc_steal(mem_ctx, state->result);
+ }
+ talloc_free(ctx);
+ return status;
+
+}
+
diff --git a/source4/winbind/wb_cmd_getpwent.c b/source4/winbind/wb_cmd_getpwent.c
new file mode 100644
index 0000000000..980a8a4d92
--- /dev/null
+++ b/source4/winbind/wb_cmd_getpwent.c
@@ -0,0 +1,128 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Command backend for getpwent
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/composite/composite.h"
+#include "winbind/wb_server.h"
+#include "winbind/wb_async_helpers.h"
+#include "winbind/wb_helper.h"
+#include "smbd/service_task.h"
+#include "libnet/libnet_proto.h"
+
+struct cmd_getpwent_state {
+ struct composite_context *ctx;
+ struct wbsrv_service *service;
+
+ struct wbsrv_pwent *pwent;
+ uint32_t max_users;
+
+ uint32_t num_users;
+ struct winbindd_pw *result;
+};
+
+static void cmd_getpwent_recv_pwnam(struct composite_context *ctx);
+#if 0 /*FIXME: implement this*/
+static void cmd_getpwent_recv_user_list(struct composite_context *ctx);
+#endif
+
+struct composite_context *wb_cmd_getpwent_send(TALLOC_CTX *mem_ctx,
+ struct wbsrv_service *service, struct wbsrv_pwent *pwent,
+ uint32_t max_users)
+{
+ struct composite_context *ctx, *result;
+ struct cmd_getpwent_state *state;
+
+ DEBUG(5, ("wb_cmd_getpwent_send called\n"));
+
+ result = composite_create(mem_ctx, service->task->event_ctx);
+ if (!result) return NULL;
+
+ state = talloc(mem_ctx, struct cmd_getpwent_state);
+ if (composite_nomem(state, result)) return result;
+
+ state->ctx = result;
+ result->private_data = state;
+ state->service = service;
+ state->pwent = pwent;
+ state->max_users = max_users;
+ state->num_users = 0;
+
+ /* If there are users left in the libnet_UserList and we're below the
+ * maximum number of users to get per winbind getpwent call, use
+ * getpwnam to get the winbindd_pw struct */
+ if (pwent->page_index < pwent->user_list->out.count) {
+ int idx = pwent->page_index;
+ char *username = talloc_strdup(state,
+ pwent->user_list->out.users[idx].username);
+
+ pwent->page_index++;
+ ctx = wb_cmd_getpwnam_send(state, service, username);
+ if (composite_nomem(ctx, state->ctx)) return result;
+
+ composite_continue(state->ctx, ctx, cmd_getpwent_recv_pwnam,
+ state);
+ } else {
+ /* If there is no valid user left, call libnet_UserList to get a new
+ * list of users. */
+ composite_error(state->ctx, NT_STATUS_NO_MORE_ENTRIES);
+ }
+ return result;
+}
+
+static void cmd_getpwent_recv_pwnam(struct composite_context *ctx)
+{
+ struct cmd_getpwent_state *state =
+ talloc_get_type(ctx->async.private_data,
+ struct cmd_getpwent_state);
+ struct winbindd_pw *pw;
+
+ DEBUG(5, ("cmd_getpwent_recv_pwnam called\n"));
+
+ state->ctx->status = wb_cmd_getpwnam_recv(ctx, state, &pw);
+ if (!composite_is_ok(state->ctx)) return;
+
+ /*FIXME: Cheat for now and only get one user per call */
+ state->result = pw;
+
+ composite_done(state->ctx);
+}
+
+NTSTATUS wb_cmd_getpwent_recv(struct composite_context *ctx,
+ TALLOC_CTX *mem_ctx, struct winbindd_pw **pw,
+ uint32_t *num_users)
+{
+ NTSTATUS status = composite_wait(ctx);
+
+ DEBUG(5, ("wb_cmd_getpwent_recv called\n"));
+
+ if (NT_STATUS_IS_OK(status)) {
+ struct cmd_getpwent_state *state =
+ talloc_get_type(ctx->private_data,
+ struct cmd_getpwent_state);
+ *pw = talloc_steal(mem_ctx, state->result);
+ /*FIXME: Cheat and only get oner user */
+ *num_users = 1;
+ }
+
+ talloc_free(ctx);
+ return status;
+}
+
diff --git a/source4/winbind/wb_cmd_getpwnam.c b/source4/winbind/wb_cmd_getpwnam.c
new file mode 100644
index 0000000000..be43684f82
--- /dev/null
+++ b/source4/winbind/wb_cmd_getpwnam.c
@@ -0,0 +1,196 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Command backend for wbinfo -i
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/composite/composite.h"
+#include "winbind/wb_server.h"
+#include "winbind/wb_async_helpers.h"
+#include "param/param.h"
+#include "winbind/wb_helper.h"
+#include "smbd/service_task.h"
+#include "libnet/libnet_proto.h"
+#include "libcli/security/security.h"
+
+struct cmd_getpwnam_state {
+ struct composite_context *ctx;
+ struct wbsrv_service *service;
+ char *name;
+ char *workgroup_name;
+ struct dom_sid *group_sid;
+
+ struct winbindd_pw *result;
+};
+
+static void cmd_getpwnam_recv_domain(struct composite_context *ctx);
+static void cmd_getpwnam_recv_user_info(struct composite_context *ctx);
+static void cmd_getpwnam_recv_uid(struct composite_context *ctx);
+static void cmd_getpwnam_recv_gid(struct composite_context *ctx);
+
+struct composite_context *wb_cmd_getpwnam_send(TALLOC_CTX *mem_ctx,
+ struct wbsrv_service *service,
+ const char *name)
+{
+ struct composite_context *result, *ctx;
+ struct cmd_getpwnam_state *state;
+
+ DEBUG(5, ("wb_cmd_getpwnam_send called\n"));
+
+ result = composite_create(mem_ctx, service->task->event_ctx);
+ if (!result) return NULL;
+
+ state = talloc(result, struct cmd_getpwnam_state);
+ if (composite_nomem(state, result)) return result;
+ state->ctx = result;
+ result->private_data = state;
+ state->service = service;
+ state->name = talloc_strdup(state, name);
+ if(composite_nomem(state->name, result)) return result;
+
+ ctx = wb_name2domain_send(state, service, name);
+ if (composite_nomem(ctx, result)) return result;
+
+ composite_continue(result, ctx, cmd_getpwnam_recv_domain, state);
+ return result;
+}
+
+static void cmd_getpwnam_recv_domain(struct composite_context *ctx)
+{
+ struct cmd_getpwnam_state *state = talloc_get_type(
+ ctx->async.private_data, struct cmd_getpwnam_state);
+ struct wbsrv_domain *domain;
+ struct libnet_UserInfo *user_info;
+ char *user_dom, *user_name;
+ bool ok;
+
+ state->ctx->status = wb_name2domain_recv(ctx, &domain);
+ if(!composite_is_ok(state->ctx)) return;
+
+ user_info = talloc(state, struct libnet_UserInfo);
+ if (composite_nomem(user_info, state->ctx)) return;
+
+ ok= wb_samba3_split_username(state, state->service->task->lp_ctx, state->name, &user_dom, &user_name);
+ if(!ok){
+ composite_error(state->ctx, NT_STATUS_OBJECT_NAME_INVALID);
+ return;
+ }
+
+ user_info->in.level = USER_INFO_BY_NAME;
+ user_info->in.data.user_name = user_name;
+ user_info->in.domain_name = domain->libnet_ctx->samr.name;
+ state->workgroup_name = talloc_strdup(state,
+ domain->libnet_ctx->samr.name);
+ if(composite_nomem(state->workgroup_name, state->ctx)) return;
+
+ ctx = libnet_UserInfo_send(domain->libnet_ctx, state, user_info, NULL);
+
+ composite_continue(state->ctx, ctx, cmd_getpwnam_recv_user_info, state);
+}
+
+static void cmd_getpwnam_recv_user_info(struct composite_context *ctx)
+{
+ struct cmd_getpwnam_state *state = talloc_get_type(
+ ctx->async.private_data, struct cmd_getpwnam_state);
+ struct libnet_UserInfo *user_info;
+ struct winbindd_pw *pw;
+
+ DEBUG(5, ("cmd_getpwnam_recv_user_info called\n"));
+
+ user_info = talloc(state, struct libnet_UserInfo);
+ if(composite_nomem(user_info, state->ctx)) return;
+
+ pw = talloc(state, struct winbindd_pw);
+ if(composite_nomem(pw, state->ctx)) return;
+
+ state->ctx->status = libnet_UserInfo_recv(ctx, state, user_info);
+ if(!composite_is_ok(state->ctx)) return;
+
+ WBSRV_SAMBA3_SET_STRING(pw->pw_name, user_info->out.account_name);
+ WBSRV_SAMBA3_SET_STRING(pw->pw_passwd, "*");
+ WBSRV_SAMBA3_SET_STRING(pw->pw_gecos, user_info->out.full_name);
+ WBSRV_SAMBA3_SET_STRING(pw->pw_dir,
+ lp_template_homedir(state->service->task->lp_ctx));
+ all_string_sub(pw->pw_dir, "%WORKGROUP%", state->workgroup_name,
+ sizeof(fstring) - 1);
+ all_string_sub(pw->pw_dir, "%ACCOUNTNAME%", user_info->out.account_name,
+ sizeof(fstring) - 1);
+ WBSRV_SAMBA3_SET_STRING(pw->pw_shell,
+ lp_template_shell(state->service->task->lp_ctx));
+
+ state->group_sid = dom_sid_dup(state, user_info->out.primary_group_sid);
+ if(composite_nomem(state->group_sid, state->ctx)) return;
+
+ state->result = pw;
+
+ ctx = wb_sid2uid_send(state, state->service, user_info->out.user_sid);
+ composite_continue(state->ctx, ctx, cmd_getpwnam_recv_uid, state);
+}
+
+static void cmd_getpwnam_recv_uid(struct composite_context *ctx)
+{
+ struct cmd_getpwnam_state *state = talloc_get_type(
+ ctx->async.private_data, struct cmd_getpwnam_state);
+ uid_t uid;
+
+ DEBUG(5, ("cmd_getpwnam_recv_uid called\n"));
+
+ state->ctx->status = wb_sid2uid_recv(ctx, &uid);
+ if(!composite_is_ok(state->ctx)) return;
+
+ state->result->pw_uid = uid;
+
+ ctx = wb_sid2gid_send(state, state->service, state->group_sid);
+ composite_continue(state->ctx, ctx, cmd_getpwnam_recv_gid, state);
+}
+
+static void cmd_getpwnam_recv_gid(struct composite_context *ctx)
+{
+ struct cmd_getpwnam_state *state = talloc_get_type(
+ ctx->async.private_data, struct cmd_getpwnam_state);
+ gid_t gid;
+
+ DEBUG(5, ("cmd_getpwnam_recv_gid called\n"));
+
+ state->ctx->status = wb_sid2gid_recv(ctx, &gid);
+ if(!composite_is_ok(state->ctx)) return;
+
+ state->result->pw_gid = gid;
+
+ composite_done(state->ctx);
+}
+
+NTSTATUS wb_cmd_getpwnam_recv(struct composite_context *ctx,
+ TALLOC_CTX *mem_ctx, struct winbindd_pw **pw)
+{
+ NTSTATUS status = composite_wait(ctx);
+
+ DEBUG(5, ("wb_cmd_getpwnam_recv called\n"));
+
+ if (NT_STATUS_IS_OK(status)) {
+ struct cmd_getpwnam_state *state =
+ talloc_get_type(ctx->private_data,
+ struct cmd_getpwnam_state);
+ *pw = talloc_steal(mem_ctx, state->result);
+ }
+ talloc_free(ctx);
+ return status;
+
+}
+
diff --git a/source4/winbind/wb_cmd_getpwuid.c b/source4/winbind/wb_cmd_getpwuid.c
new file mode 100644
index 0000000000..b4e3d972f8
--- /dev/null
+++ b/source4/winbind/wb_cmd_getpwuid.c
@@ -0,0 +1,205 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Backend for getpwuid
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/composite/composite.h"
+#include "winbind/wb_server.h"
+#include "winbind/wb_async_helpers.h"
+#include "winbind/wb_helper.h"
+#include "smbd/service_task.h"
+#include "libnet/libnet_proto.h"
+#include "param/param.h"
+#include "libcli/security/proto.h"
+#include "auth/credentials/credentials.h"
+
+struct cmd_getpwuid_state {
+ struct composite_context *ctx;
+ struct wbsrv_service *service;
+ uid_t uid;
+ struct dom_sid *sid;
+ char *workgroup;
+ struct wbsrv_domain *domain;
+
+ struct winbindd_pw *result;
+};
+
+static void cmd_getpwuid_recv_sid(struct composite_context *ctx);
+static void cmd_getpwuid_recv_domain(struct composite_context *ctx);
+static void cmd_getpwuid_recv_user_info(struct composite_context *ctx);
+static void cmd_getpwuid_recv_gid(struct composite_context *ctx);
+
+/* Get the SID using the uid */
+
+struct composite_context *wb_cmd_getpwuid_send(TALLOC_CTX *mem_ctx,
+ struct wbsrv_service *service,
+ uid_t uid)
+{
+ struct composite_context *ctx, *result;
+ struct cmd_getpwuid_state *state;
+
+ DEBUG(5, ("wb_cmd_getpwuid_send called\n"));
+
+ result = composite_create(mem_ctx, service->task->event_ctx);
+ if (!result) return NULL;
+
+ state = talloc(result, struct cmd_getpwuid_state);
+ if (composite_nomem(state, result)) return result;
+ state->ctx = result;
+ result->private_data = state;
+ state->service = service;
+ state->uid = uid;
+
+ ctx = wb_uid2sid_send(state, service, uid);
+ if (composite_nomem(ctx, state->ctx)) return result;
+
+ composite_continue(result, ctx, cmd_getpwuid_recv_sid, state);
+ return result;
+}
+
+
+/* Receive the sid and get the domain structure with it */
+
+static void cmd_getpwuid_recv_sid(struct composite_context *ctx)
+{
+ struct cmd_getpwuid_state *state =
+ talloc_get_type(ctx->async.private_data,
+ struct cmd_getpwuid_state);
+
+ DEBUG(5, ("cmd_getpwuid_recv_sid called %p\n", ctx->private_data));
+
+ state->ctx->status = wb_uid2sid_recv(ctx, state, &state->sid);
+ if (!composite_is_ok(state->ctx)) return;
+
+ ctx = wb_sid2domain_send(state, state->service, state->sid);
+
+ composite_continue(state->ctx, ctx, cmd_getpwuid_recv_domain, state);
+}
+
+/* Receive the domain struct and call libnet to get the user info struct */
+
+static void cmd_getpwuid_recv_domain(struct composite_context *ctx)
+{
+ struct cmd_getpwuid_state *state =
+ talloc_get_type(ctx->async.private_data,
+ struct cmd_getpwuid_state);
+ struct libnet_UserInfo *user_info;
+
+ DEBUG(5, ("cmd_getpwuid_recv_domain called\n"));
+
+ state->ctx->status = wb_sid2domain_recv(ctx, &state->domain);
+ if (!composite_is_ok(state->ctx)) return;
+
+ user_info = talloc(state, struct libnet_UserInfo);
+ if (composite_nomem(user_info, state->ctx)) return;
+
+ user_info->in.level = USER_INFO_BY_SID;
+ user_info->in.data.user_sid = state->sid;
+ user_info->in.domain_name = state->domain->libnet_ctx->samr.name;
+
+ /* We need the workgroup later, so copy it */
+ state->workgroup = talloc_strdup(state,
+ state->domain->libnet_ctx->samr.name);
+ if (composite_nomem(state->workgroup, state->ctx)) return;
+
+ ctx = libnet_UserInfo_send(state->domain->libnet_ctx, state, user_info,
+ NULL);
+
+ composite_continue(state->ctx, ctx, cmd_getpwuid_recv_user_info, state);
+}
+
+/* Receive the user info struct and get the gid for the user */
+
+static void cmd_getpwuid_recv_user_info(struct composite_context *ctx)
+{
+ struct cmd_getpwuid_state *state =
+ talloc_get_type(ctx->async.private_data,
+ struct cmd_getpwuid_state);
+ struct libnet_UserInfo *user_info;
+ struct winbindd_pw *pw;
+
+ DEBUG(5, ("cmd_getpwuid_recv_user_info called\n"));
+
+ pw = talloc(state, struct winbindd_pw);
+ if (composite_nomem(pw, state->ctx)) return;
+
+ user_info = talloc(state, struct libnet_UserInfo);
+ if(composite_nomem(user_info, state->ctx)) return;
+
+ state->ctx->status = libnet_UserInfo_recv(ctx, state, user_info);
+ if (!composite_is_ok(state->ctx)) return;
+
+ WBSRV_SAMBA3_SET_STRING(pw->pw_name, user_info->out.account_name);
+ WBSRV_SAMBA3_SET_STRING(pw->pw_passwd, "*");
+ WBSRV_SAMBA3_SET_STRING(pw->pw_gecos, user_info->out.full_name);
+ WBSRV_SAMBA3_SET_STRING(pw->pw_dir,
+ lp_template_homedir(state->service->task->lp_ctx));
+ all_string_sub(pw->pw_dir, "%WORKGROUP%", state->workgroup,
+ sizeof(fstring) - 1);
+ all_string_sub(pw->pw_dir, "%ACCOUNTNAME%", user_info->out.account_name,
+ sizeof(fstring) - 1);
+ WBSRV_SAMBA3_SET_STRING(pw->pw_shell,
+ lp_template_shell(state->service->task->lp_ctx));
+
+ pw->pw_uid = state->uid;
+
+ state->result = pw;
+
+ ctx = wb_sid2gid_send(state, state->service,
+ user_info->out.primary_group_sid);
+
+ composite_continue(state->ctx, ctx, cmd_getpwuid_recv_gid, state);
+}
+
+static void cmd_getpwuid_recv_gid(struct composite_context *ctx)
+{
+ struct cmd_getpwuid_state *state =
+ talloc_get_type(ctx->async.private_data,
+ struct cmd_getpwuid_state);
+ gid_t gid;
+
+ DEBUG(5, ("cmd_getpwuid_recv_gid called\n"));
+
+ state->ctx->status = wb_sid2gid_recv(ctx, &gid);
+ if (!composite_is_ok(state->ctx)) return;
+
+ state->result->pw_gid = gid;
+
+ composite_done(state->ctx);
+}
+
+NTSTATUS wb_cmd_getpwuid_recv(struct composite_context *ctx,
+ TALLOC_CTX *mem_ctx, struct winbindd_pw **pw)
+{
+ NTSTATUS status = composite_wait(ctx);
+
+ DEBUG(5, ("wb_cmd_getpwuid_recv called\n"));
+
+ if (NT_STATUS_IS_OK(status)) {
+ struct cmd_getpwuid_state *state =
+ talloc_get_type(ctx->private_data,
+ struct cmd_getpwuid_state);
+ *pw = talloc_steal(mem_ctx, state->result);
+ }
+ talloc_free(ctx);
+ return status;
+
+}
+
diff --git a/source4/winbind/wb_cmd_list_groups.c b/source4/winbind/wb_cmd_list_groups.c
new file mode 100644
index 0000000000..04a4fc39df
--- /dev/null
+++ b/source4/winbind/wb_cmd_list_groups.c
@@ -0,0 +1,200 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Command backend for wbinfo -g
+
+ Copyright (C) Kai Blin 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/composite/composite.h"
+#include "winbind/wb_server.h"
+#include "winbind/wb_async_helpers.h"
+#include "winbind/wb_helper.h"
+#include "smbd/service_task.h"
+#include "libnet/libnet_proto.h"
+
+struct cmd_list_groups_state {
+ struct composite_context *ctx;
+ struct wbsrv_service *service;
+
+ struct wbsrv_domain *domain;
+ char *domain_name;
+ uint32_t resume_index;
+ char *result;
+};
+
+static void cmd_list_groups_recv_domain(struct composite_context *ctx);
+static void cmd_list_groups_recv_group_list(struct composite_context *ctx);
+
+struct composite_context *wb_cmd_list_groups_send(TALLOC_CTX *mem_ctx,
+ struct wbsrv_service *service, const char *domain_name)
+{
+ struct composite_context *ctx, *result;
+ struct cmd_list_groups_state *state;
+
+ DEBUG(5, ("wb_cmd_list_groups_send called\n"));
+
+ result = composite_create(mem_ctx, service->task->event_ctx);
+ if (!result) return NULL;
+
+ state = talloc(result, struct cmd_list_groups_state);
+ if (composite_nomem(state, result)) return result;
+
+ state->ctx = result;
+ result->private_data = state;
+ state->service = service;
+ state->resume_index = 0;
+ state->result = talloc_strdup(state, "");
+ if (composite_nomem(state->result, state->ctx)) return result;
+
+ /*FIXME: We should look up the domain in the winbind request if it is
+ * set, not just take the primary domain. However, I want to get the
+ * libnet logic to work first. */
+
+ if (domain_name && *domain_name != '\0') {
+ state->domain_name = talloc_strdup(state, domain_name);
+ if (composite_nomem(state->domain_name, state->ctx))
+ return result;
+ } else {
+ state->domain_name = NULL;
+ }
+
+ ctx = wb_sid2domain_send(state, service, service->primary_sid);
+ if (composite_nomem(ctx, state->ctx)) return result;
+
+ composite_continue(state->ctx, ctx, cmd_list_groups_recv_domain, state);
+ return result;
+}
+
+static void cmd_list_groups_recv_domain(struct composite_context *ctx)
+{
+ struct cmd_list_groups_state *state = talloc_get_type(
+ ctx->async.private_data, struct cmd_list_groups_state);
+ struct wbsrv_domain *domain;
+ struct libnet_GroupList *group_list;
+
+ DEBUG(5, ("cmd_list_groups_recv_domain called\n"));
+
+ state->ctx->status = wb_sid2domain_recv(ctx, &domain);
+ if (!composite_is_ok(state->ctx)) return;
+
+ state->domain = domain;
+
+ /* If this is non-null, we've looked up the domain given in the winbind
+ * request, otherwise we'll just use the default name.*/
+ if (state->domain_name == NULL) {
+ state->domain_name = talloc_strdup(state,
+ domain->libnet_ctx->samr.name);
+ if (composite_nomem(state->domain_name, state->ctx)) return;
+ }
+
+ group_list = talloc(state, struct libnet_GroupList);
+ if (composite_nomem(group_list, state->ctx)) return;
+
+ group_list->in.domain_name = state->domain_name;
+
+ /* Rafal suggested that 128 is a good number here. I don't like magic
+ * numbers too much, but for now it'll have to do.
+ */
+ group_list->in.page_size = 128;
+ group_list->in.resume_index = state->resume_index;
+
+ ctx = libnet_GroupList_send(domain->libnet_ctx, state, group_list,NULL);
+
+ composite_continue(state->ctx, ctx, cmd_list_groups_recv_group_list,
+ state);
+}
+
+static void cmd_list_groups_recv_group_list(struct composite_context *ctx)
+{
+ struct cmd_list_groups_state *state = talloc_get_type(
+ ctx->async.private_data, struct cmd_list_groups_state);
+ struct libnet_GroupList *group_list;
+ NTSTATUS status;
+ int i;
+
+ DEBUG(5, ("cmd_list_groups_recv_group_list called\n"));
+
+ group_list = talloc(state, struct libnet_GroupList);
+ if (composite_nomem(group_list, state->ctx)) return;
+
+ status = libnet_GroupList_recv(ctx, state, group_list);
+
+ /* If NTSTATUS is neither OK nor MORE_ENTRIES, something broke */
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
+ composite_error(state->ctx, status);
+ return;
+ }
+
+ for (i = 0; i < group_list->out.count; ++i) {
+ DEBUG(5, ("Appending group '%s'\n",
+ group_list->out.groups[i].groupname));
+ state->result = talloc_asprintf_append_buffer(state->result,
+ "%s,",
+ group_list->out.groups[i].groupname);
+ }
+
+ /* If the status is OK, we're finished, there's no more groups.
+ * So we'll trim off the trailing ',' and are done.*/
+ if (NT_STATUS_IS_OK(status)) {
+ int str_len = strlen(state->result);
+ DEBUG(5, ("list_GroupList_recv returned NT_STATUS_OK\n"));
+ state->result[str_len - 1] = '\0';
+ composite_done(state->ctx);
+ return;
+ }
+
+ DEBUG(5, ("list_GroupList_recv returned NT_STATUS_MORE_ENTRIES\n"));
+
+ /* Otherwise there's more groups to get, so call out to libnet and
+ * continue on this function here. */
+
+ group_list->in.domain_name = state->domain_name;
+ /* See comment above about the page size. 128 seems like a good default.
+ */
+ group_list->in.page_size = 128;
+ group_list->in.resume_index = group_list->out.resume_index;
+
+ ctx = libnet_GroupList_send(state->domain->libnet_ctx, state,group_list,
+ NULL);
+
+ composite_continue(state->ctx, ctx, cmd_list_groups_recv_group_list,
+ state);
+}
+
+NTSTATUS wb_cmd_list_groups_recv(struct composite_context *ctx,
+ TALLOC_CTX *mem_ctx, uint32_t *extra_data_len,
+ char **extra_data)
+{
+ NTSTATUS status = composite_wait(ctx);
+
+ DEBUG(5, ("wb_cmd_list_groups_recv called\n"));
+
+ if (NT_STATUS_IS_OK(status)) {
+ struct cmd_list_groups_state *state = talloc_get_type(
+ ctx->private_data, struct cmd_list_groups_state);
+
+ *extra_data_len = strlen(state->result);
+ *extra_data = talloc_steal(mem_ctx, state->result);
+ }
+
+ talloc_free(ctx);
+ return status;
+}
+
+
diff --git a/source4/winbind/wb_cmd_list_trustdom.c b/source4/winbind/wb_cmd_list_trustdom.c
new file mode 100644
index 0000000000..fe98ce2f6a
--- /dev/null
+++ b/source4/winbind/wb_cmd_list_trustdom.c
@@ -0,0 +1,196 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Command backend for wbinfo -m
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/composite/composite.h"
+#include "winbind/wb_server.h"
+#include "smbd/service_task.h"
+#include "librpc/gen_ndr/ndr_lsa_c.h"
+
+/* List trusted domains. To avoid the trouble with having to wait for other
+ * conflicting requests waiting for the lsa pipe we're opening our own lsa
+ * pipe here. */
+
+struct cmd_list_trustdom_state {
+ struct composite_context *ctx;
+ struct dcerpc_pipe *lsa_pipe;
+ struct policy_handle *lsa_policy;
+ int num_domains;
+ struct wb_dom_info **domains;
+
+ uint32_t resume_handle;
+ struct lsa_DomainList domainlist;
+ struct lsa_EnumTrustDom r;
+};
+
+static void cmd_list_trustdoms_recv_domain(struct composite_context *ctx);
+static void cmd_list_trustdoms_recv_lsa(struct composite_context *ctx);
+static void cmd_list_trustdoms_recv_doms(struct rpc_request *req);
+
+struct composite_context *wb_cmd_list_trustdoms_send(TALLOC_CTX *mem_ctx,
+ struct wbsrv_service *service)
+{
+ struct composite_context *result, *ctx;
+ struct cmd_list_trustdom_state *state;
+
+ result = composite_create(mem_ctx, service->task->event_ctx);
+ if (result == NULL) goto failed;
+
+ state = talloc(result, struct cmd_list_trustdom_state);
+ if (state == NULL) goto failed;
+ state->ctx = result;
+ result->private_data = state;
+
+ ctx = wb_sid2domain_send(state, service, service->primary_sid);
+ if (ctx == NULL) goto failed;
+ ctx->async.fn = cmd_list_trustdoms_recv_domain;
+ ctx->async.private_data = state;
+ return result;
+
+ failed:
+ talloc_free(result);
+ return NULL;
+}
+
+static void cmd_list_trustdoms_recv_domain(struct composite_context *ctx)
+{
+ struct cmd_list_trustdom_state *state =
+ talloc_get_type(ctx->async.private_data,
+ struct cmd_list_trustdom_state);
+ struct wbsrv_domain *domain;
+ struct smbcli_tree *tree;
+
+ state->ctx->status = wb_sid2domain_recv(ctx, &domain);
+ if (!composite_is_ok(state->ctx)) return;
+
+ tree = dcerpc_smb_tree(domain->libnet_ctx->lsa.pipe->conn);
+ if (composite_nomem(tree, state->ctx)) return;
+
+ ctx = wb_init_lsa_send(state, domain);
+ composite_continue(state->ctx, ctx, cmd_list_trustdoms_recv_lsa,
+ state);
+}
+
+static void cmd_list_trustdoms_recv_lsa(struct composite_context *ctx)
+{
+ struct cmd_list_trustdom_state *state =
+ talloc_get_type(ctx->async.private_data,
+ struct cmd_list_trustdom_state);
+ struct rpc_request *req;
+
+ state->ctx->status = wb_init_lsa_recv(ctx, state,
+ &state->lsa_pipe,
+ &state->lsa_policy);
+ if (!composite_is_ok(state->ctx)) return;
+
+ state->num_domains = 0;
+ state->domains = NULL;
+
+ state->domainlist.count = 0;
+ state->domainlist.domains = NULL;
+
+ state->resume_handle = 0;
+ state->r.in.handle = state->lsa_policy;
+ state->r.in.resume_handle = &state->resume_handle;
+ state->r.in.max_size = 1000;
+ state->r.out.resume_handle = &state->resume_handle;
+ state->r.out.domains = &state->domainlist;
+
+ req = dcerpc_lsa_EnumTrustDom_send(state->lsa_pipe, state, &state->r);
+ composite_continue_rpc(state->ctx, req, cmd_list_trustdoms_recv_doms,
+ state);
+}
+
+static void cmd_list_trustdoms_recv_doms(struct rpc_request *req)
+{
+ struct cmd_list_trustdom_state *state =
+ talloc_get_type(req->async.private_data,
+ struct cmd_list_trustdom_state);
+ int i, old_num_domains;
+
+ state->ctx->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(state->ctx)) return;
+ state->ctx->status = state->r.out.result;
+
+ if (!NT_STATUS_IS_OK(state->ctx->status) &&
+ !NT_STATUS_EQUAL(state->ctx->status, NT_STATUS_NO_MORE_ENTRIES) &&
+ !NT_STATUS_EQUAL(state->ctx->status, STATUS_MORE_ENTRIES)) {
+ composite_error(state->ctx, state->ctx->status);
+ return;
+ }
+
+ old_num_domains = state->num_domains;
+
+ state->num_domains += state->r.out.domains->count;
+ state->domains = talloc_realloc(state, state->domains,
+ struct wb_dom_info *,
+ state->num_domains);
+ if (state->num_domains &&
+ composite_nomem(state->domains, state->ctx)) return;
+
+ for (i=0; i<state->r.out.domains->count; i++) {
+ int j = i+old_num_domains;
+ state->domains[j] = talloc(state->domains,
+ struct wb_dom_info);
+ if (composite_nomem(state->domains[i], state->ctx)) return;
+ state->domains[j]->name = talloc_steal(
+ state->domains[j],
+ state->r.out.domains->domains[i].name.string);
+ state->domains[j]->sid = talloc_steal(
+ state->domains[j],
+ state->r.out.domains->domains[i].sid);
+ }
+
+ if (NT_STATUS_IS_OK(state->ctx->status) || NT_STATUS_EQUAL(state->ctx->status, NT_STATUS_NO_MORE_ENTRIES)) {
+ state->ctx->status = NT_STATUS_OK;
+ composite_done(state->ctx);
+ return;
+ }
+
+ state->domainlist.count = 0;
+ state->domainlist.domains = NULL;
+ state->r.in.handle = state->lsa_policy;
+ state->r.in.resume_handle = &state->resume_handle;
+ state->r.in.max_size = 1000;
+ state->r.out.resume_handle = &state->resume_handle;
+ state->r.out.domains = &state->domainlist;
+
+ req = dcerpc_lsa_EnumTrustDom_send(state->lsa_pipe, state, &state->r);
+ composite_continue_rpc(state->ctx, req, cmd_list_trustdoms_recv_doms,
+ state);
+}
+
+NTSTATUS wb_cmd_list_trustdoms_recv(struct composite_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ int *num_domains,
+ struct wb_dom_info ***domains)
+{
+ NTSTATUS status = composite_wait(ctx);
+ if (NT_STATUS_IS_OK(status)) {
+ struct cmd_list_trustdom_state *state =
+ talloc_get_type(ctx->private_data,
+ struct cmd_list_trustdom_state);
+ *num_domains = state->num_domains;
+ *domains = talloc_steal(mem_ctx, state->domains);
+ }
+ talloc_free(ctx);
+ return status;
+}
diff --git a/source4/winbind/wb_cmd_list_users.c b/source4/winbind/wb_cmd_list_users.c
new file mode 100644
index 0000000000..f67f133488
--- /dev/null
+++ b/source4/winbind/wb_cmd_list_users.c
@@ -0,0 +1,198 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Command backend for wbinfo -u
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/composite/composite.h"
+#include "winbind/wb_server.h"
+#include "winbind/wb_async_helpers.h"
+#include "winbind/wb_helper.h"
+#include "smbd/service_task.h"
+#include "libnet/libnet_proto.h"
+
+struct cmd_list_users_state {
+ struct composite_context *ctx;
+ struct wbsrv_service *service;
+
+ struct wbsrv_domain *domain;
+ char *domain_name;
+ uint resume_index;
+ char *result;
+};
+
+static void cmd_list_users_recv_domain(struct composite_context *ctx);
+static void cmd_list_users_recv_user_list(struct composite_context *ctx);
+
+struct composite_context *wb_cmd_list_users_send(TALLOC_CTX *mem_ctx,
+ struct wbsrv_service *service, const char *domain_name)
+{
+ struct composite_context *ctx, *result;
+ struct cmd_list_users_state *state;
+
+ DEBUG(5, ("wb_cmd_list_users_send called\n"));
+
+ result = composite_create(mem_ctx, service->task->event_ctx);
+ if (!result) return NULL;
+
+ state = talloc(result, struct cmd_list_users_state);
+ if (composite_nomem(state, result)) return result;
+
+ state->ctx = result;
+ result->private_data = state;
+ state->service = service;
+ state->resume_index = 0;
+ state->result = talloc_strdup(state, "");
+ if (composite_nomem(state->result, state->ctx)) return result;
+
+ /*FIXME: We should look up the domain in the winbind request if it is
+ * set, not just take the primary domain. However, I want to get the
+ * libnet logic to work first. */
+
+ if (domain_name && *domain_name != '\0') {
+ state->domain_name = talloc_strdup(state, domain_name);
+ if (composite_nomem(state->domain_name, state->ctx))
+ return result;
+ } else {
+ state->domain_name = NULL;
+ }
+
+ ctx = wb_sid2domain_send(state, service, service->primary_sid);
+ if (composite_nomem(ctx, state->ctx)) return result;
+
+ composite_continue(state->ctx, ctx, cmd_list_users_recv_domain, state);
+ return result;
+}
+
+static void cmd_list_users_recv_domain(struct composite_context *ctx)
+{
+ struct cmd_list_users_state *state = talloc_get_type(
+ ctx->async.private_data, struct cmd_list_users_state);
+ struct wbsrv_domain *domain;
+ struct libnet_UserList *user_list;
+
+ DEBUG(5, ("cmd_list_users_recv_domain called\n"));
+
+ state->ctx->status = wb_sid2domain_recv(ctx, &domain);
+ if (!composite_is_ok(state->ctx)) return;
+
+ state->domain = domain;
+
+ /* If this is non-null, we've looked up the domain given in the winbind
+ * request, otherwise we'll just use the default name.*/
+ if (state->domain_name == NULL) {
+ state->domain_name = talloc_strdup(state,
+ domain->libnet_ctx->samr.name);
+ if (composite_nomem(state->domain_name, state->ctx)) return;
+ }
+
+ user_list = talloc(state, struct libnet_UserList);
+ if (composite_nomem(user_list, state->ctx)) return;
+
+ user_list->in.domain_name = state->domain_name;
+
+ /* Rafal suggested that 128 is a good number here. I don't like magic
+ * numbers too much, but for now it'll have to do.
+ */
+ user_list->in.page_size = 128;
+ user_list->in.resume_index = state->resume_index;
+
+ ctx = libnet_UserList_send(domain->libnet_ctx, state, user_list, NULL);
+
+ composite_continue(state->ctx, ctx, cmd_list_users_recv_user_list,
+ state);
+}
+
+static void cmd_list_users_recv_user_list(struct composite_context *ctx)
+{
+ struct cmd_list_users_state *state = talloc_get_type(
+ ctx->async.private_data, struct cmd_list_users_state);
+ struct libnet_UserList *user_list;
+ NTSTATUS status;
+ int i;
+
+ DEBUG(5, ("cmd_list_users_recv_user_list called\n"));
+
+ user_list = talloc(state, struct libnet_UserList);
+ if (composite_nomem(user_list, state->ctx)) return;
+
+ status = libnet_UserList_recv(ctx, state, user_list);
+
+ /* If NTSTATUS is neither OK nor MORE_ENTRIES, something broke */
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
+ composite_error(state->ctx, status);
+ return;
+ }
+
+ for (i = 0; i < user_list->out.count; ++i) {
+ DEBUG(5, ("Appending user '%s'\n", user_list->out.users[i].username));
+ state->result = talloc_asprintf_append_buffer(state->result, "%s,",
+ user_list->out.users[i].username);
+ }
+
+ /* If the status is OK, we're finished, there's no more users.
+ * So we'll trim off the trailing ',' and are done.*/
+ if (NT_STATUS_IS_OK(status)) {
+ int str_len = strlen(state->result);
+ DEBUG(5, ("list_UserList_recv returned NT_STATUS_OK\n"));
+ state->result[str_len - 1] = '\0';
+ composite_done(state->ctx);
+ return;
+ }
+
+ DEBUG(5, ("list_UserList_recv returned NT_STATUS_MORE_ENTRIES\n"));
+
+ /* Otherwise there's more users to get, so call out to libnet and
+ * continue on this function here. */
+
+ user_list->in.domain_name = state->domain_name;
+ /* See comment above about the page size. 128 seems like a good default.
+ */
+ user_list->in.page_size = 128;
+ user_list->in.resume_index = user_list->out.resume_index;
+
+ ctx = libnet_UserList_send(state->domain->libnet_ctx, state, user_list,
+ NULL);
+
+ composite_continue(state->ctx, ctx, cmd_list_users_recv_user_list,
+ state);
+}
+
+NTSTATUS wb_cmd_list_users_recv(struct composite_context *ctx,
+ TALLOC_CTX *mem_ctx, uint32_t *extra_data_len,
+ char **extra_data)
+{
+ NTSTATUS status = composite_wait(ctx);
+
+ DEBUG(5, ("wb_cmd_list_users_recv called\n"));
+
+ if (NT_STATUS_IS_OK(status)) {
+ struct cmd_list_users_state *state = talloc_get_type(
+ ctx->private_data, struct cmd_list_users_state);
+
+ *extra_data_len = strlen(state->result);
+ *extra_data = talloc_steal(mem_ctx, state->result);
+ }
+
+ talloc_free(ctx);
+ return status;
+}
+
+
diff --git a/source4/winbind/wb_cmd_lookupname.c b/source4/winbind/wb_cmd_lookupname.c
new file mode 100644
index 0000000000..9839e2cd80
--- /dev/null
+++ b/source4/winbind/wb_cmd_lookupname.c
@@ -0,0 +1,121 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Command backend for wbinfo -n
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/composite/composite.h"
+#include "winbind/wb_server.h"
+#include "winbind/wb_async_helpers.h"
+#include "winbind/wb_helper.h"
+#include "smbd/service_task.h"
+
+struct cmd_lookupname_state {
+ struct composite_context *ctx;
+ const char *name;
+ struct wb_sid_object *result;
+};
+
+static void lookupname_recv_domain(struct composite_context *ctx);
+static void lookupname_recv_sids(struct composite_context *ctx);
+
+struct composite_context *wb_cmd_lookupname_send(TALLOC_CTX *mem_ctx,
+ struct wbsrv_service *service,
+ const char *dom_name,
+ const char *name)
+{
+ struct composite_context *result, *ctx;
+ struct cmd_lookupname_state *state;
+
+ result = composite_create(mem_ctx, service->task->event_ctx);
+ if (result == NULL) goto failed;
+
+ state = talloc(result, struct cmd_lookupname_state);
+ if (state == NULL) goto failed;
+ state->ctx = result;
+ result->private_data = state;
+
+ state->name = talloc_asprintf(state, "%s\\%s", dom_name, name);
+ if (state->name == NULL) goto failed;
+
+ ctx = wb_sid2domain_send(state, service, service->primary_sid);
+ if (ctx == NULL) goto failed;
+
+ ctx->async.fn = lookupname_recv_domain;
+ ctx->async.private_data = state;
+ return result;
+
+ failed:
+ talloc_free(result);
+ return NULL;
+}
+
+static void lookupname_recv_domain(struct composite_context *ctx)
+{
+ struct cmd_lookupname_state *state =
+ talloc_get_type(ctx->async.private_data,
+ struct cmd_lookupname_state);
+ struct wbsrv_domain *domain;
+
+ state->ctx->status = wb_sid2domain_recv(ctx, &domain);
+ if (!composite_is_ok(state->ctx)) return;
+
+ ctx = wb_lsa_lookupnames_send(state, domain->libnet_ctx->lsa.pipe,
+ &domain->libnet_ctx->lsa.handle, 1, &state->name);
+ composite_continue(state->ctx, ctx, lookupname_recv_sids, state);
+}
+
+static void lookupname_recv_sids(struct composite_context *ctx)
+{
+ struct cmd_lookupname_state *state =
+ talloc_get_type(ctx->async.private_data,
+ struct cmd_lookupname_state);
+ struct wb_sid_object **sids;
+
+ state->ctx->status = wb_lsa_lookupnames_recv(ctx, state, &sids);
+ if (!composite_is_ok(state->ctx)) return;
+
+ state->result = sids[0];
+ composite_done(state->ctx);
+}
+
+NTSTATUS wb_cmd_lookupname_recv(struct composite_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct wb_sid_object **sid)
+{
+ struct cmd_lookupname_state *state =
+ talloc_get_type(c->private_data, struct cmd_lookupname_state);
+ NTSTATUS status = composite_wait(c);
+ if (NT_STATUS_IS_OK(status)) {
+ *sid = talloc_steal(mem_ctx, state->result);
+ }
+ talloc_free(state);
+ return status;
+}
+
+NTSTATUS wb_cmd_lookupname(TALLOC_CTX *mem_ctx,
+ struct wbsrv_service *service,
+ const char *dom_name,
+ const char *name,
+ struct wb_sid_object **sid)
+{
+ struct composite_context *c =
+ wb_cmd_lookupname_send(mem_ctx, service, dom_name, name);
+ return wb_cmd_lookupname_recv(c, mem_ctx, sid);
+}
diff --git a/source4/winbind/wb_cmd_lookupsid.c b/source4/winbind/wb_cmd_lookupsid.c
new file mode 100644
index 0000000000..2d72ae2072
--- /dev/null
+++ b/source4/winbind/wb_cmd_lookupsid.c
@@ -0,0 +1,119 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Command backend for wbinfo -s
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/composite/composite.h"
+#include "winbind/wb_server.h"
+#include "winbind/wb_helper.h"
+#include "smbd/service_task.h"
+#include "libcli/security/security.h"
+
+struct cmd_lookupsid_state {
+ struct composite_context *ctx;
+ const struct dom_sid *sid;
+ struct wb_sid_object *result;
+};
+
+static void lookupsid_recv_domain(struct composite_context *ctx);
+static void lookupsid_recv_names(struct composite_context *ctx);
+
+struct composite_context *wb_cmd_lookupsid_send(TALLOC_CTX *mem_ctx,
+ struct wbsrv_service *service,
+ const struct dom_sid *sid)
+{
+ struct composite_context *result, *ctx;
+ struct cmd_lookupsid_state *state;
+
+
+ result = composite_create(mem_ctx, service->task->event_ctx);
+ if (result == NULL) goto failed;
+
+ state = talloc(result, struct cmd_lookupsid_state);
+ if (state == NULL) goto failed;
+ state->ctx = result;
+ result->private_data = state;
+
+ state->sid = dom_sid_dup(state, sid);
+ if (state->sid == NULL) goto failed;
+
+ ctx = wb_sid2domain_send(state, service, service->primary_sid);
+ if (ctx == NULL) goto failed;
+
+ ctx->async.fn = lookupsid_recv_domain;
+ ctx->async.private_data = state;
+ return result;
+
+ failed:
+ talloc_free(result);
+ return NULL;
+}
+
+static void lookupsid_recv_domain(struct composite_context *ctx)
+{
+ struct cmd_lookupsid_state *state =
+ talloc_get_type(ctx->async.private_data,
+ struct cmd_lookupsid_state);
+ struct wbsrv_domain *domain;
+
+ state->ctx->status = wb_sid2domain_recv(ctx, &domain);
+ if (!composite_is_ok(state->ctx)) return;
+
+ ctx = wb_lsa_lookupsids_send(state, domain->libnet_ctx->lsa.pipe,
+ &domain->libnet_ctx->lsa.handle, 1, &state->sid);
+ composite_continue(state->ctx, ctx, lookupsid_recv_names, state);
+}
+
+static void lookupsid_recv_names(struct composite_context *ctx)
+{
+ struct cmd_lookupsid_state *state =
+ talloc_get_type(ctx->async.private_data,
+ struct cmd_lookupsid_state);
+ struct wb_sid_object **names;
+
+ state->ctx->status = wb_lsa_lookupsids_recv(ctx, state, &names);
+ if (!composite_is_ok(state->ctx)) return;
+
+ state->result = names[0];
+ composite_done(state->ctx);
+}
+
+NTSTATUS wb_cmd_lookupsid_recv(struct composite_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct wb_sid_object **sid)
+{
+ struct cmd_lookupsid_state *state =
+ talloc_get_type(c->private_data, struct cmd_lookupsid_state);
+ NTSTATUS status = composite_wait(c);
+ if (NT_STATUS_IS_OK(status)) {
+ *sid = talloc_steal(mem_ctx, state->result);
+ }
+ talloc_free(state);
+ return status;
+}
+
+NTSTATUS wb_cmd_lookupsid(TALLOC_CTX *mem_ctx, struct wbsrv_service *service,
+ const struct dom_sid *sid,
+ struct wb_sid_object **name)
+{
+ struct composite_context *c =
+ wb_cmd_lookupsid_send(mem_ctx, service, sid);
+ return wb_cmd_lookupsid_recv(c, mem_ctx, name);
+}
diff --git a/source4/winbind/wb_cmd_setpwent.c b/source4/winbind/wb_cmd_setpwent.c
new file mode 100644
index 0000000000..50ab2fdff6
--- /dev/null
+++ b/source4/winbind/wb_cmd_setpwent.c
@@ -0,0 +1,142 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Command backend for setpwent
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/composite/composite.h"
+#include "winbind/wb_server.h"
+#include "winbind/wb_async_helpers.h"
+#include "winbind/wb_helper.h"
+#include "smbd/service_task.h"
+#include "libnet/libnet_proto.h"
+
+struct cmd_setpwent_state {
+ struct composite_context *ctx;
+ struct wbsrv_service *service;
+ struct libnet_context *libnet_ctx;
+
+ struct wbsrv_pwent *result;
+};
+
+static void cmd_setpwent_recv_domain(struct composite_context *ctx);
+static void cmd_setpwent_recv_user_list(struct composite_context *ctx);
+
+struct composite_context *wb_cmd_setpwent_send(TALLOC_CTX *mem_ctx,
+ struct wbsrv_service *service)
+{
+ struct composite_context *ctx, *result;
+ struct cmd_setpwent_state *state;
+
+ DEBUG(5, ("wb_cmd_setpwent_send called\n"));
+
+ result = composite_create(mem_ctx, service->task->event_ctx);
+ if (!result) return NULL;
+
+ state = talloc(mem_ctx, struct cmd_setpwent_state);
+ if (composite_nomem(state, result)) return result;
+
+ state->ctx = result;
+ result->private_data = state;
+ state->service = service;
+
+ state->result = talloc(state, struct wbsrv_pwent);
+ if (composite_nomem(state->result, state->ctx)) return result;
+
+ ctx = wb_sid2domain_send(state, service, service->primary_sid);
+ if (composite_nomem(ctx, state->ctx)) return result;
+
+ composite_continue(state->ctx, ctx, cmd_setpwent_recv_domain, state);
+ return result;
+}
+
+static void cmd_setpwent_recv_domain(struct composite_context *ctx)
+{
+ struct cmd_setpwent_state *state = talloc_get_type(
+ ctx->async.private_data, struct cmd_setpwent_state);
+ struct wbsrv_domain *domain;
+ struct libnet_UserList *user_list;
+
+ DEBUG(5, ("cmd_setpwent_recv_domain called\n"));
+
+ state->ctx->status = wb_sid2domain_recv(ctx, &domain);
+ if (!composite_is_ok(state->ctx)) return;
+
+ state->libnet_ctx = domain->libnet_ctx;
+
+ user_list = talloc(state->result, struct libnet_UserList);
+ if (composite_nomem(user_list, state->ctx)) return;
+
+ user_list->in.domain_name = talloc_strdup(state,
+ domain->libnet_ctx->samr.name);
+ if (composite_nomem(user_list->in.domain_name, state->ctx)) return;
+
+ /* Page size recommended by Rafal */
+ user_list->in.page_size = 128;
+
+ /* Always get the start of the list */
+ user_list->in.resume_index = 0;
+
+ ctx = libnet_UserList_send(domain->libnet_ctx, state->result, user_list,
+ NULL);
+
+ composite_continue(state->ctx, ctx, cmd_setpwent_recv_user_list, state);
+}
+
+static void cmd_setpwent_recv_user_list(struct composite_context *ctx)
+{
+ struct cmd_setpwent_state *state = talloc_get_type(
+ ctx->async.private_data, struct cmd_setpwent_state);
+ struct libnet_UserList *user_list;
+
+ DEBUG(5, ("cmd_setpwent_recv_user_list called\n"));
+
+ user_list = talloc(state->result, struct libnet_UserList);
+ if (composite_nomem(user_list, state->ctx)) return;
+
+ state->ctx->status = libnet_UserList_recv(ctx, state->result,
+ user_list);
+ if (!composite_is_ok(state->ctx)) return;
+
+ state->result->user_list = user_list;
+ state->result->page_index = 0;
+ state->result->libnet_ctx = state->libnet_ctx;
+
+ composite_done(state->ctx);
+}
+
+NTSTATUS wb_cmd_setpwent_recv(struct composite_context *ctx,
+ TALLOC_CTX *mem_ctx, struct wbsrv_pwent **pwent)
+{
+ NTSTATUS status = composite_wait(ctx);
+
+ DEBUG(5, ("wb_cmd_setpwent_recv called\n"));
+
+ if (NT_STATUS_IS_OK(status)) {
+ struct cmd_setpwent_state *state =
+ talloc_get_type(ctx->private_data,
+ struct cmd_setpwent_state);
+
+ *pwent = talloc_steal(mem_ctx, state->result);
+ }
+
+ talloc_free(ctx);
+ return status;
+}
+
diff --git a/source4/winbind/wb_cmd_userdomgroups.c b/source4/winbind/wb_cmd_userdomgroups.c
new file mode 100644
index 0000000000..649fe489cc
--- /dev/null
+++ b/source4/winbind/wb_cmd_userdomgroups.c
@@ -0,0 +1,147 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Command backend for wbinfo --user-domgroups
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/composite/composite.h"
+#include "libcli/security/security.h"
+#include "winbind/wb_server.h"
+#include "winbind/wb_helper.h"
+#include "smbd/service_task.h"
+
+struct cmd_userdomgroups_state {
+ struct composite_context *ctx;
+ struct dom_sid *dom_sid;
+ uint32_t user_rid;
+ int num_rids;
+ uint32_t *rids;
+};
+
+static void userdomgroups_recv_domain(struct composite_context *ctx);
+static void userdomgroups_recv_rids(struct composite_context *ctx);
+
+struct composite_context *wb_cmd_userdomgroups_send(TALLOC_CTX *mem_ctx,
+ struct wbsrv_service *service,
+ const struct dom_sid *sid)
+{
+ struct composite_context *result, *ctx;
+ struct cmd_userdomgroups_state *state;
+
+ result = composite_create(mem_ctx, service->task->event_ctx);
+ if (result == NULL) goto failed;
+
+ state = talloc(result, struct cmd_userdomgroups_state);
+ if (state == NULL) goto failed;
+ state->ctx = result;
+ result->private_data = state;
+
+ state->dom_sid = dom_sid_dup(state, sid);
+ if (state->dom_sid == NULL) goto failed;
+ state->dom_sid->num_auths -= 1;
+
+ state->user_rid = sid->sub_auths[sid->num_auths-1];
+
+ ctx = wb_sid2domain_send(state, service, sid);
+
+ composite_continue(state->ctx, ctx, userdomgroups_recv_domain, state);
+
+ if (ctx) {
+ return result;
+ }
+
+ failed:
+ talloc_free(result);
+ return NULL;
+}
+
+static void userdomgroups_recv_domain(struct composite_context *ctx)
+{
+ struct cmd_userdomgroups_state *state =
+ talloc_get_type(ctx->async.private_data,
+ struct cmd_userdomgroups_state);
+ struct wbsrv_domain *domain;
+
+ state->ctx->status = wb_sid2domain_recv(ctx, &domain);
+ if (!composite_is_ok(state->ctx)) return;
+
+ ctx = wb_samr_userdomgroups_send(state, domain->libnet_ctx->samr.pipe,
+ &domain->libnet_ctx->samr.handle,
+ state->user_rid);
+ composite_continue(state->ctx, ctx, userdomgroups_recv_rids, state);
+
+}
+
+static void userdomgroups_recv_rids(struct composite_context *ctx)
+{
+ struct cmd_userdomgroups_state *state =
+ talloc_get_type(ctx->async.private_data,
+ struct cmd_userdomgroups_state);
+
+ state->ctx->status = wb_samr_userdomgroups_recv(ctx, state,
+ &state->num_rids,
+ &state->rids);
+ if (!composite_is_ok(state->ctx)) return;
+
+ composite_done(state->ctx);
+}
+
+NTSTATUS wb_cmd_userdomgroups_recv(struct composite_context *c,
+ TALLOC_CTX *mem_ctx,
+ int *num_sids, struct dom_sid ***sids)
+{
+ struct cmd_userdomgroups_state *state =
+ talloc_get_type(c->private_data,
+ struct cmd_userdomgroups_state);
+ int i;
+ NTSTATUS status;
+
+ status = composite_wait(c);
+ if (!NT_STATUS_IS_OK(status)) goto done;
+
+ *num_sids = state->num_rids;
+ *sids = talloc_array(mem_ctx, struct dom_sid *, state->num_rids);
+ if (*sids == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ for (i=0; i<state->num_rids; i++) {
+ (*sids)[i] = dom_sid_add_rid((*sids), state->dom_sid,
+ state->rids[i]);
+ if ((*sids)[i] == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ }
+
+done:
+ talloc_free(c);
+ return status;
+}
+
+NTSTATUS wb_cmd_userdomgroups(TALLOC_CTX *mem_ctx,
+ struct wbsrv_service *service,
+ const struct dom_sid *sid,
+ int *num_sids, struct dom_sid ***sids)
+{
+ struct composite_context *c =
+ wb_cmd_userdomgroups_send(mem_ctx, service, sid);
+ return wb_cmd_userdomgroups_recv(c, mem_ctx, num_sids, sids);
+}
diff --git a/source4/winbind/wb_cmd_usersids.c b/source4/winbind/wb_cmd_usersids.c
new file mode 100644
index 0000000000..b414cf6313
--- /dev/null
+++ b/source4/winbind/wb_cmd_usersids.c
@@ -0,0 +1,193 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Command backend for wbinfo --user-sids
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/composite/composite.h"
+#include "winbind/wb_server.h"
+#include "smbd/service_task.h"
+#include "librpc/gen_ndr/ndr_samr_c.h"
+#include "libcli/security/security.h"
+
+/* Calculate the token in two steps: Go the user's originating domain, ask for
+ * the user's domain groups. Then with the resulting list of sids go to our
+ * own domain to expand the aliases aka domain local groups. */
+
+struct cmd_usersids_state {
+ struct composite_context *ctx;
+ struct wbsrv_service *service;
+ struct dom_sid *user_sid;
+ int num_domgroups;
+ struct dom_sid **domgroups;
+
+ struct lsa_SidArray lsa_sids;
+ struct samr_Ids rids;
+ struct samr_GetAliasMembership r;
+
+ int num_sids;
+ struct dom_sid **sids;
+};
+
+static void usersids_recv_domgroups(struct composite_context *ctx);
+static void usersids_recv_domain(struct composite_context *ctx);
+static void usersids_recv_aliases(struct rpc_request *req);
+
+struct composite_context *wb_cmd_usersids_send(TALLOC_CTX *mem_ctx,
+ struct wbsrv_service *service,
+ const struct dom_sid *sid)
+{
+ struct composite_context *result, *ctx;
+ struct cmd_usersids_state *state;
+
+
+ result = composite_create(mem_ctx, service->task->event_ctx);
+ if (result == NULL) goto failed;
+
+ state = talloc(result, struct cmd_usersids_state);
+ if (state == NULL) goto failed;
+ state->ctx = result;
+ result->private_data = state;
+
+ state->service = service;
+ state->user_sid = dom_sid_dup(state, sid);
+ if (state->user_sid == NULL) goto failed;
+
+ ctx = wb_cmd_userdomgroups_send(state, service, sid);
+ if (ctx == NULL) goto failed;
+
+ ctx->async.fn = usersids_recv_domgroups;
+ ctx->async.private_data = state;
+ return result;
+
+ failed:
+ talloc_free(result);
+ return NULL;
+}
+
+static void usersids_recv_domgroups(struct composite_context *ctx)
+{
+ struct cmd_usersids_state *state =
+ talloc_get_type(ctx->async.private_data,
+ struct cmd_usersids_state);
+
+ state->ctx->status = wb_cmd_userdomgroups_recv(ctx, state,
+ &state->num_domgroups,
+ &state->domgroups);
+ if (!composite_is_ok(state->ctx)) return;
+
+ ctx = wb_sid2domain_send(state, state->service,
+ state->service->primary_sid);
+ composite_continue(state->ctx, ctx, usersids_recv_domain, state);
+}
+
+static void usersids_recv_domain(struct composite_context *ctx)
+{
+ struct cmd_usersids_state *state =
+ talloc_get_type(ctx->async.private_data,
+ struct cmd_usersids_state);
+ struct rpc_request *req;
+ struct wbsrv_domain *domain;
+ int i;
+
+ state->ctx->status = wb_sid2domain_recv(ctx, &domain);
+ if (!composite_is_ok(state->ctx)) return;
+
+ state->lsa_sids.num_sids = state->num_domgroups+1;
+ state->lsa_sids.sids = talloc_array(state, struct lsa_SidPtr,
+ state->lsa_sids.num_sids);
+ if (composite_nomem(state->lsa_sids.sids, state->ctx)) return;
+
+ state->lsa_sids.sids[0].sid = state->user_sid;
+ for (i=0; i<state->num_domgroups; i++) {
+ state->lsa_sids.sids[i+1].sid = state->domgroups[i];
+ }
+
+ state->rids.count = 0;
+ state->rids.ids = NULL;
+
+ state->r.in.domain_handle = &domain->libnet_ctx->samr.handle;
+ state->r.in.sids = &state->lsa_sids;
+ state->r.out.rids = &state->rids;
+
+ req = dcerpc_samr_GetAliasMembership_send(domain->libnet_ctx->samr.pipe, state,
+ &state->r);
+ composite_continue_rpc(state->ctx, req, usersids_recv_aliases, state);
+}
+
+static void usersids_recv_aliases(struct rpc_request *req)
+{
+ struct cmd_usersids_state *state =
+ talloc_get_type(req->async.private_data,
+ struct cmd_usersids_state);
+ int i;
+
+ state->ctx->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(state->ctx)) return;
+ state->ctx->status = state->r.out.result;
+ if (!composite_is_ok(state->ctx)) return;
+
+ state->num_sids = 1 + state->num_domgroups + state->r.out.rids->count;
+ state->sids = talloc_array(state, struct dom_sid *, state->num_sids);
+ if (composite_nomem(state->sids, state->ctx)) return;
+
+ state->sids[0] = talloc_steal(state->sids, state->user_sid);
+
+ for (i=0; i<state->num_domgroups; i++) {
+ state->sids[1+i] =
+ talloc_steal(state->sids, state->domgroups[i]);
+ }
+
+ for (i=0; i<state->r.out.rids->count; i++) {
+ state->sids[1+state->num_domgroups+i] = dom_sid_add_rid(
+ state->sids, state->service->primary_sid,
+ state->r.out.rids->ids[i]);
+
+ if (composite_nomem(state->sids[1+state->num_domgroups+i],
+ state->ctx)) return;
+ }
+
+ composite_done(state->ctx);
+}
+
+NTSTATUS wb_cmd_usersids_recv(struct composite_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ int *num_sids, struct dom_sid ***sids)
+{
+ NTSTATUS status = composite_wait(ctx);
+ if (NT_STATUS_IS_OK(status)) {
+ struct cmd_usersids_state *state =
+ talloc_get_type(ctx->private_data,
+ struct cmd_usersids_state);
+ *num_sids = state->num_sids;
+ *sids = talloc_steal(mem_ctx, state->sids);
+ }
+ talloc_free(ctx);
+ return status;
+}
+
+NTSTATUS wb_cmd_usersids(TALLOC_CTX *mem_ctx, struct wbsrv_service *service,
+ const struct dom_sid *sid,
+ int *num_sids, struct dom_sid ***sids)
+{
+ struct composite_context *c =
+ wb_cmd_usersids_send(mem_ctx, service, sid);
+ return wb_cmd_usersids_recv(c, mem_ctx, num_sids, sids);
+}
+
diff --git a/source4/winbind/wb_connect_lsa.c b/source4/winbind/wb_connect_lsa.c
new file mode 100644
index 0000000000..a728f8abe4
--- /dev/null
+++ b/source4/winbind/wb_connect_lsa.c
@@ -0,0 +1,135 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Connect to the LSA pipe, given an smbcli_tree and possibly some
+ credentials. Try ntlmssp, schannel and anon in that order.
+
+ Copyright (C) Volker Lendecke 2005
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/composite/composite.h"
+
+#include "libcli/raw/libcliraw.h"
+#include "librpc/gen_ndr/ndr_lsa_c.h"
+#include "winbind/wb_server.h"
+
+/* Helper to initialize LSA with a specific auth methods. Verify by opening
+ * the LSA policy. */
+
+struct init_lsa_state {
+ struct composite_context *ctx;
+ struct dcerpc_pipe *lsa_pipe;
+
+ uint8_t auth_type;
+ struct cli_credentials *creds;
+
+ struct lsa_ObjectAttribute objectattr;
+ struct lsa_OpenPolicy2 openpolicy;
+ struct policy_handle *handle;
+};
+
+static void init_lsa_recv_pipe(struct composite_context *ctx);
+static void init_lsa_recv_openpol(struct rpc_request *req);
+
+struct composite_context *wb_init_lsa_send(TALLOC_CTX *mem_ctx,
+ struct wbsrv_domain *domain)
+{
+ struct composite_context *result, *ctx;
+ struct init_lsa_state *state;
+
+ result = composite_create(mem_ctx, domain->netlogon_pipe->conn->event_ctx);
+ if (result == NULL) goto failed;
+
+ state = talloc(result, struct init_lsa_state);
+ if (state == NULL) goto failed;
+ state->ctx = result;
+ result->private_data = state;
+
+ /* this will make the secondary connection on the same IPC$ share,
+ secured with SPNEGO or NTLMSSP */
+ ctx = dcerpc_secondary_auth_connection_send(domain->netlogon_pipe,
+ domain->lsa_binding,
+ &ndr_table_lsarpc,
+ domain->libnet_ctx->cred,
+ domain->libnet_ctx->lp_ctx);
+ composite_continue(state->ctx, ctx, init_lsa_recv_pipe, state);
+ return result;
+
+ failed:
+ talloc_free(result);
+ return NULL;
+}
+
+static void init_lsa_recv_pipe(struct composite_context *ctx)
+{
+ struct rpc_request *req;
+ struct init_lsa_state *state =
+ talloc_get_type(ctx->async.private_data,
+ struct init_lsa_state);
+
+ state->ctx->status = dcerpc_secondary_auth_connection_recv(ctx, state,
+ &state->lsa_pipe);
+ if (!composite_is_ok(state->ctx)) return;
+
+ state->handle = talloc(state, struct policy_handle);
+ if (composite_nomem(state->handle, state->ctx)) return;
+
+ state->openpolicy.in.system_name =
+ talloc_asprintf(state, "\\\\%s",
+ dcerpc_server_name(state->lsa_pipe));
+ ZERO_STRUCT(state->objectattr);
+ state->openpolicy.in.attr = &state->objectattr;
+ state->openpolicy.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ state->openpolicy.out.handle = state->handle;
+
+ req = dcerpc_lsa_OpenPolicy2_send(state->lsa_pipe, state,
+ &state->openpolicy);
+ composite_continue_rpc(state->ctx, req, init_lsa_recv_openpol, state);
+}
+
+static void init_lsa_recv_openpol(struct rpc_request *req)
+{
+ struct init_lsa_state *state =
+ talloc_get_type(req->async.private_data,
+ struct init_lsa_state);
+
+ state->ctx->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(state->ctx)) return;
+ state->ctx->status = state->openpolicy.out.result;
+ if (!composite_is_ok(state->ctx)) return;
+
+ composite_done(state->ctx);
+}
+
+NTSTATUS wb_init_lsa_recv(struct composite_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct dcerpc_pipe **lsa_pipe,
+ struct policy_handle **lsa_policy)
+{
+ NTSTATUS status = composite_wait(c);
+ if (NT_STATUS_IS_OK(status)) {
+ struct init_lsa_state *state =
+ talloc_get_type(c->private_data,
+ struct init_lsa_state);
+ *lsa_pipe = talloc_steal(mem_ctx, state->lsa_pipe);
+ *lsa_policy = talloc_steal(mem_ctx, state->handle);
+ }
+ talloc_free(c);
+ return status;
+}
+
diff --git a/source4/winbind/wb_connect_sam.c b/source4/winbind/wb_connect_sam.c
new file mode 100644
index 0000000000..efd715b164
--- /dev/null
+++ b/source4/winbind/wb_connect_sam.c
@@ -0,0 +1,164 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Connect to the SAMR pipe, and return connection and domain handles.
+
+ Copyright (C) Volker Lendecke 2005
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/composite/composite.h"
+
+#include "libcli/raw/libcliraw.h"
+#include "libcli/security/security.h"
+#include "librpc/gen_ndr/ndr_samr_c.h"
+#include "winbind/wb_server.h"
+
+
+/* Helper to initialize SAMR with a specific auth methods. Verify by opening
+ * the SAM handle */
+
+struct connect_samr_state {
+ struct composite_context *ctx;
+ struct dom_sid *sid;
+
+ struct dcerpc_pipe *samr_pipe;
+ struct policy_handle *connect_handle;
+ struct policy_handle *domain_handle;
+
+ struct samr_Connect2 c;
+ struct samr_OpenDomain o;
+};
+
+static void connect_samr_recv_pipe(struct composite_context *ctx);
+static void connect_samr_recv_conn(struct rpc_request *req);
+static void connect_samr_recv_open(struct rpc_request *req);
+
+struct composite_context *wb_connect_samr_send(TALLOC_CTX *mem_ctx,
+ struct wbsrv_domain *domain)
+{
+ struct composite_context *result, *ctx;
+ struct connect_samr_state *state;
+
+ result = composite_create(mem_ctx, domain->netlogon_pipe->conn->event_ctx);
+ if (result == NULL) goto failed;
+
+ state = talloc(result, struct connect_samr_state);
+ if (state == NULL) goto failed;
+ state->ctx = result;
+ result->private_data = state;
+
+ state->sid = dom_sid_dup(state, domain->info->sid);
+ if (state->sid == NULL) goto failed;
+
+ /* this will make the secondary connection on the same IPC$ share,
+ secured with SPNEGO, NTLMSSP or SCHANNEL */
+ ctx = dcerpc_secondary_auth_connection_send(domain->netlogon_pipe,
+ domain->samr_binding,
+ &ndr_table_samr,
+ domain->libnet_ctx->cred,
+ domain->libnet_ctx->lp_ctx);
+ composite_continue(state->ctx, ctx, connect_samr_recv_pipe, state);
+ return result;
+
+ failed:
+ talloc_free(result);
+ return NULL;
+}
+
+static void connect_samr_recv_pipe(struct composite_context *ctx)
+{
+ struct rpc_request *req;
+ struct connect_samr_state *state =
+ talloc_get_type(ctx->async.private_data,
+ struct connect_samr_state);
+
+ state->ctx->status = dcerpc_secondary_auth_connection_recv(ctx, state,
+ &state->samr_pipe);
+ if (!composite_is_ok(state->ctx)) return;
+
+ state->connect_handle = talloc(state, struct policy_handle);
+ if (composite_nomem(state->connect_handle, state->ctx)) return;
+
+ state->c.in.system_name =
+ talloc_asprintf(state, "\\\\%s",
+ dcerpc_server_name(state->samr_pipe));
+ state->c.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ state->c.out.connect_handle = state->connect_handle;
+
+ req = dcerpc_samr_Connect2_send(state->samr_pipe, state, &state->c);
+ composite_continue_rpc(state->ctx, req, connect_samr_recv_conn, state);
+ return;
+}
+
+static void connect_samr_recv_conn(struct rpc_request *req)
+{
+ struct connect_samr_state *state =
+ talloc_get_type(req->async.private_data,
+ struct connect_samr_state);
+
+ state->ctx->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(state->ctx)) return;
+ state->ctx->status = state->c.out.result;
+ if (!composite_is_ok(state->ctx)) return;
+
+ state->domain_handle = talloc(state, struct policy_handle);
+ if (composite_nomem(state->domain_handle, state->ctx)) return;
+
+ state->o.in.connect_handle = state->connect_handle;
+ state->o.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ state->o.in.sid = state->sid;
+ state->o.out.domain_handle = state->domain_handle;
+
+ req = dcerpc_samr_OpenDomain_send(state->samr_pipe, state, &state->o);
+ composite_continue_rpc(state->ctx, req,
+ connect_samr_recv_open, state);
+}
+
+static void connect_samr_recv_open(struct rpc_request *req)
+{
+ struct connect_samr_state *state =
+ talloc_get_type(req->async.private_data,
+ struct connect_samr_state);
+
+ state->ctx->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(state->ctx)) return;
+ state->ctx->status = state->o.out.result;
+ if (!composite_is_ok(state->ctx)) return;
+
+ composite_done(state->ctx);
+}
+
+NTSTATUS wb_connect_samr_recv(struct composite_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct dcerpc_pipe **samr_pipe,
+ struct policy_handle *connect_handle,
+ struct policy_handle *domain_handle)
+{
+ NTSTATUS status = composite_wait(c);
+ if (NT_STATUS_IS_OK(status)) {
+ struct connect_samr_state *state =
+ talloc_get_type(c->private_data,
+ struct connect_samr_state);
+ *samr_pipe = talloc_steal(mem_ctx, state->samr_pipe);
+ *connect_handle = *state->connect_handle;
+ *domain_handle = *state->domain_handle;
+ }
+ talloc_free(c);
+ return status;
+}
+
diff --git a/source4/winbind/wb_dom_info.c b/source4/winbind/wb_dom_info.c
new file mode 100644
index 0000000000..4fcbf3a6e5
--- /dev/null
+++ b/source4/winbind/wb_dom_info.c
@@ -0,0 +1,126 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Get a struct wb_dom_info for a domain using DNS, netbios, possibly cldap
+ etc.
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/composite/composite.h"
+#include "libcli/resolve/resolve.h"
+#include "libcli/security/security.h"
+#include "winbind/wb_server.h"
+#include "smbd/service_task.h"
+#include "librpc/gen_ndr/ndr_irpc.h"
+#include "librpc/gen_ndr/samr.h"
+#include "lib/messaging/irpc.h"
+#include "libcli/finddcs.h"
+#include "param/param.h"
+
+struct get_dom_info_state {
+ struct composite_context *ctx;
+ struct wb_dom_info *info;
+};
+
+static void get_dom_info_recv_addrs(struct composite_context *ctx);
+
+struct composite_context *wb_get_dom_info_send(TALLOC_CTX *mem_ctx,
+ struct wbsrv_service *service,
+ const char *domain_name,
+ const struct dom_sid *sid)
+{
+ struct composite_context *result, *ctx;
+ struct get_dom_info_state *state;
+ struct dom_sid *dom_sid;
+ result = composite_create(mem_ctx, service->task->event_ctx);
+ if (result == NULL) goto failed;
+
+ state = talloc(result, struct get_dom_info_state);
+ if (state == NULL) goto failed;
+ state->ctx = result;
+ result->private_data = state;
+
+ state->info = talloc_zero(state, struct wb_dom_info);
+ if (state->info == NULL) goto failed;
+
+ state->info->name = talloc_strdup(state->info, domain_name);
+ if (state->info->name == NULL) goto failed;
+
+ state->info->sid = dom_sid_dup(state->info, sid);
+ if (state->info->sid == NULL) goto failed;
+
+ dom_sid = dom_sid_dup(mem_ctx, sid);
+ if (dom_sid == NULL) goto failed;
+
+ ctx = finddcs_send(mem_ctx, lp_netbios_name(service->task->lp_ctx),
+ lp_nbt_port(service->task->lp_ctx),
+ domain_name, NBT_NAME_LOGON,
+ dom_sid,
+ lp_iconv_convenience(service->task->lp_ctx),
+ lp_resolve_context(service->task->lp_ctx),
+ service->task->event_ctx,
+ service->task->msg_ctx);
+ if (ctx == NULL) goto failed;
+
+ composite_continue(state->ctx, ctx, get_dom_info_recv_addrs, state);
+ return result;
+
+ failed:
+ talloc_free(result);
+ return NULL;
+}
+
+static void get_dom_info_recv_addrs(struct composite_context *ctx)
+{
+ struct get_dom_info_state *state =
+ talloc_get_type(ctx->async.private_data,
+ struct get_dom_info_state);
+
+ state->ctx->status = finddcs_recv(ctx, state->info,
+ &state->info->num_dcs,
+ &state->info->dcs);
+ if (!composite_is_ok(state->ctx)) return;
+
+ composite_done(state->ctx);
+}
+
+NTSTATUS wb_get_dom_info_recv(struct composite_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct wb_dom_info **result)
+{
+ NTSTATUS status = composite_wait(ctx);
+ if (NT_STATUS_IS_OK(status)) {
+ struct get_dom_info_state *state =
+ talloc_get_type(ctx->private_data,
+ struct get_dom_info_state);
+ *result = talloc_steal(mem_ctx, state->info);
+ }
+ talloc_free(ctx);
+ return status;
+}
+
+NTSTATUS wb_get_dom_info(TALLOC_CTX *mem_ctx,
+ struct wbsrv_service *service,
+ const char *domain_name,
+ const struct dom_sid *sid,
+ struct wb_dom_info **result)
+{
+ struct composite_context *ctx =
+ wb_get_dom_info_send(mem_ctx, service, domain_name, sid);
+ return wb_get_dom_info_recv(ctx, mem_ctx, result);
+}
diff --git a/source4/winbind/wb_dom_info_trusted.c b/source4/winbind/wb_dom_info_trusted.c
new file mode 100644
index 0000000000..5223b166aa
--- /dev/null
+++ b/source4/winbind/wb_dom_info_trusted.c
@@ -0,0 +1,242 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Get a struct wb_dom_info for a trusted domain, relying on "our" DC.
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/composite/composite.h"
+#include "libcli/resolve/resolve.h"
+#include "libcli/security/security.h"
+#include "winbind/wb_server.h"
+#include "smbd/service_task.h"
+#include "librpc/gen_ndr/ndr_netlogon_c.h"
+#include "libcli/libcli.h"
+#include "param/param.h"
+
+struct trusted_dom_info_state {
+ struct composite_context *ctx;
+ struct wbsrv_service *service;
+ struct wbsrv_domain *my_domain;
+
+ struct netr_DsRGetDCName d;
+ struct netr_GetAnyDCName g;
+
+ struct wb_dom_info *info;
+};
+
+static void trusted_dom_info_recv_domain(struct composite_context *ctx);
+static void trusted_dom_info_recv_dsr(struct rpc_request *req);
+static void trusted_dom_info_recv_dcname(struct rpc_request *req);
+static void trusted_dom_info_recv_dcaddr(struct composite_context *ctx);
+
+struct composite_context *wb_trusted_dom_info_send(TALLOC_CTX *mem_ctx,
+ struct wbsrv_service *service,
+ const char *domain_name,
+ const struct dom_sid *sid)
+{
+ struct composite_context *result, *ctx;
+ struct trusted_dom_info_state *state;
+
+ result = composite_create(mem_ctx, service->task->event_ctx);
+ if (result == NULL) goto failed;
+
+ state = talloc(result, struct trusted_dom_info_state);
+ if (state == NULL) goto failed;
+ state->ctx = result;
+ result->private_data = state;
+
+ state->info = talloc_zero(state, struct wb_dom_info);
+ if (state->info == NULL) goto failed;
+
+ state->service = service;
+
+ state->info->sid = dom_sid_dup(state->info, sid);
+ if (state->info->sid == NULL) goto failed;
+
+ state->info->name = talloc_strdup(state->info, domain_name);
+ if (state->info->name == NULL) goto failed;
+
+ ctx = wb_sid2domain_send(state, service, service->primary_sid);
+ if (ctx == NULL) goto failed;
+
+ ctx->async.fn = trusted_dom_info_recv_domain;
+ ctx->async.private_data = state;
+ return result;
+
+ failed:
+ talloc_free(result);
+ return NULL;
+}
+
+static void trusted_dom_info_recv_domain(struct composite_context *ctx)
+{
+ struct trusted_dom_info_state *state =
+ talloc_get_type(ctx->async.private_data,
+ struct trusted_dom_info_state);
+ struct rpc_request *req;
+
+ state->ctx->status = wb_sid2domain_recv(ctx, &state->my_domain);
+ if (!composite_is_ok(state->ctx)) return;
+
+ state->d.in.server_unc =
+ talloc_asprintf(state, "\\\\%s",
+ dcerpc_server_name(state->my_domain->netlogon_pipe));
+ if (composite_nomem(state->d.in.server_unc,
+ state->ctx)) return;
+
+ state->d.in.domain_name = state->info->name;
+ state->d.in.domain_guid = NULL;
+ state->d.in.site_guid = NULL;
+ state->d.in.flags = DS_RETURN_DNS_NAME;
+ state->d.out.info = talloc(state, struct netr_DsRGetDCNameInfo *);
+ if (composite_nomem(state->d.out.info, state->ctx)) return;
+
+ req = dcerpc_netr_DsRGetDCName_send(state->my_domain->netlogon_pipe,
+ state, &state->d);
+ composite_continue_rpc(state->ctx, req, trusted_dom_info_recv_dsr,
+ state);
+}
+
+/*
+ * dcerpc_netr_DsRGetDCName has replied
+ */
+
+static void trusted_dom_info_recv_dsr(struct rpc_request *req)
+{
+ struct trusted_dom_info_state *state =
+ talloc_get_type(req->async.private_data,
+ struct trusted_dom_info_state);
+
+ state->ctx->status = dcerpc_ndr_request_recv(req);
+ if (!NT_STATUS_IS_OK(state->ctx->status)) {
+ DEBUG(9, ("dcerpc_ndr_request_recv returned %s\n",
+ nt_errstr(state->ctx->status)));
+ goto fallback;
+ }
+
+ state->ctx->status =
+ werror_to_ntstatus(state->d.out.result);
+ if (!NT_STATUS_IS_OK(state->ctx->status)) {
+ DEBUG(9, ("dsrgetdcname returned %s\n",
+ nt_errstr(state->ctx->status)));
+ goto fallback;
+ }
+
+ /* Hey, that was easy! */
+ state->info->num_dcs = 1;
+ state->info->dcs = talloc(state->info, struct nbt_dc_name);
+ state->info->dcs[0].name = talloc_steal(state->info,
+ (*state->d.out.info)->dc_unc);
+ if (*state->info->dcs[0].name == '\\') state->info->dcs[0].name++;
+ if (*state->info->dcs[0].name == '\\') state->info->dcs[0].name++;
+
+ state->info->dcs[0].address = talloc_steal(state->info,
+ (*state->d.out.info)->dc_address);
+ if (*state->info->dcs[0].address == '\\') state->info->dcs[0].address++;
+ if (*state->info->dcs[0].address == '\\') state->info->dcs[0].address++;
+
+ state->info->dns_name = talloc_steal(state->info,
+ (*state->d.out.info)->domain_name);
+
+ composite_done(state->ctx);
+ return;
+
+ fallback:
+
+ state->g.in.logon_server = talloc_asprintf(
+ state, "\\\\%s",
+ dcerpc_server_name(state->my_domain->netlogon_pipe));
+ state->g.in.domainname = state->info->name;
+ state->g.out.dcname = talloc(state, const char *);
+
+ req = dcerpc_netr_GetAnyDCName_send(state->my_domain->netlogon_pipe,
+ state, &state->g);
+ if (composite_nomem(req, state->ctx)) return;
+
+ composite_continue_rpc(state->ctx, req, trusted_dom_info_recv_dcname,
+ state);
+}
+
+static void trusted_dom_info_recv_dcname(struct rpc_request *req)
+{
+ struct trusted_dom_info_state *state =
+ talloc_get_type(req->async.private_data,
+ struct trusted_dom_info_state);
+ struct composite_context *ctx;
+ struct nbt_name name;
+
+ state->ctx->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(state->ctx)) return;
+ state->ctx->status = werror_to_ntstatus(state->g.out.result);
+ if (!composite_is_ok(state->ctx)) return;
+
+ /* Hey, that was easy! */
+ state->info->num_dcs = 1;
+ state->info->dcs = talloc(state->info, struct nbt_dc_name);
+ state->info->dcs[0].name = talloc_steal(state->info,
+ *(state->g.out.dcname));
+ if (*state->info->dcs[0].name == '\\') state->info->dcs[0].name++;
+ if (*state->info->dcs[0].name == '\\') state->info->dcs[0].name++;
+
+ make_nbt_name(&name, state->info->dcs[0].name, 0x20);
+ ctx = resolve_name_send(lp_resolve_context(state->service->task->lp_ctx),
+ &name, state->service->task->event_ctx);
+
+ composite_continue(state->ctx, ctx, trusted_dom_info_recv_dcaddr,
+ state);
+}
+
+static void trusted_dom_info_recv_dcaddr(struct composite_context *ctx)
+{
+ struct trusted_dom_info_state *state =
+ talloc_get_type(ctx->async.private_data,
+ struct trusted_dom_info_state);
+
+ state->ctx->status = resolve_name_recv(ctx, state->info,
+ &state->info->dcs[0].address);
+ if (!composite_is_ok(state->ctx)) return;
+
+ composite_done(state->ctx);
+}
+
+NTSTATUS wb_trusted_dom_info_recv(struct composite_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct wb_dom_info **result)
+{
+ NTSTATUS status = composite_wait(ctx);
+ if (NT_STATUS_IS_OK(status)) {
+ struct trusted_dom_info_state *state =
+ talloc_get_type(ctx->private_data,
+ struct trusted_dom_info_state);
+ *result = talloc_steal(mem_ctx, state->info);
+ }
+ talloc_free(ctx);
+ return status;
+}
+
+NTSTATUS wb_trusted_dom_info(TALLOC_CTX *mem_ctx,
+ struct wbsrv_service *service,
+ const char *domain_name,
+ const struct dom_sid *sid,
+ struct wb_dom_info **result)
+{
+ struct composite_context *ctx =
+ wb_trusted_dom_info_send(mem_ctx, service, domain_name, sid);
+ return wb_trusted_dom_info_recv(ctx, mem_ctx, result);
+}
diff --git a/source4/winbind/wb_gid2sid.c b/source4/winbind/wb_gid2sid.c
new file mode 100644
index 0000000000..834d869845
--- /dev/null
+++ b/source4/winbind/wb_gid2sid.c
@@ -0,0 +1,108 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Command backend for wbinfo -G
+
+ Copyright (C) 2007-2008 Kai Blin
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/composite/composite.h"
+#include "winbind/wb_server.h"
+#include "smbd/service_task.h"
+#include "winbind/wb_helper.h"
+#include "libcli/security/proto.h"
+#include "winbind/idmap.h"
+
+struct gid2sid_state {
+ struct composite_context *ctx;
+ struct wbsrv_service *service;
+ struct dom_sid *sid;
+};
+
+static void gid2sid_recv_sid(struct composite_context *ctx);
+
+struct composite_context *wb_gid2sid_send(TALLOC_CTX *mem_ctx,
+ struct wbsrv_service *service, gid_t gid)
+{
+ struct composite_context *result, *ctx;
+ struct gid2sid_state *state;
+ struct unixid *unixid;
+ struct id_mapping *ids;
+
+ DEBUG(5, ("wb_gid2sid_send called\n"));
+
+ result = composite_create(mem_ctx, service->task->event_ctx);
+ if (!result) return NULL;
+
+ state = talloc(result, struct gid2sid_state);
+ if (composite_nomem(state, result)) return result;
+
+ state->ctx = result;
+ result->private_data = state;
+ state->service = service;
+
+ unixid = talloc(result, struct unixid);
+ if (composite_nomem(unixid, result)) return result;
+ unixid->id = gid;
+ unixid->type = ID_TYPE_GID;
+
+ ids = talloc(result, struct id_mapping);
+ if (composite_nomem(ids, result)) return result;
+ ids->unixid = unixid;
+ ids->sid = NULL;
+
+ ctx = wb_xids2sids_send(result, service, 1, ids);
+ if (composite_nomem(ctx, result)) return result;
+
+ composite_continue(result, ctx, gid2sid_recv_sid, state);
+ return result;
+}
+
+static void gid2sid_recv_sid(struct composite_context *ctx)
+{
+ struct gid2sid_state *state = talloc_get_type(ctx->async.private_data,
+ struct gid2sid_state);
+ struct id_mapping *ids = NULL;
+ state->ctx->status = wb_xids2sids_recv(ctx, &ids);
+ if (!composite_is_ok(state->ctx)) return;
+
+ if (!NT_STATUS_IS_OK(ids->status)) {
+ composite_error(state->ctx, ids->status);
+ return;
+ }
+
+ state->sid = ids->sid;
+ composite_done(state->ctx);
+}
+
+NTSTATUS wb_gid2sid_recv(struct composite_context *ctx, TALLOC_CTX *mem_ctx,
+ struct dom_sid **sid)
+{
+ NTSTATUS status = composite_wait(ctx);
+
+ DEBUG(5, ("wb_gid2sid_recv called.\n"));
+
+ if (NT_STATUS_IS_OK(status)) {
+ struct gid2sid_state *state =
+ talloc_get_type(ctx->private_data,
+ struct gid2sid_state);
+ *sid = talloc_steal(mem_ctx, state->sid);
+ }
+ talloc_free(ctx);
+ return status;
+}
+
diff --git a/source4/winbind/wb_init_domain.c b/source4/winbind/wb_init_domain.c
new file mode 100644
index 0000000000..676746681f
--- /dev/null
+++ b/source4/winbind/wb_init_domain.c
@@ -0,0 +1,434 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ A composite API for initializing a domain
+
+ Copyright (C) Volker Lendecke 2005
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/composite/composite.h"
+#include "libcli/smb_composite/smb_composite.h"
+#include "winbind/wb_server.h"
+#include "winbind/wb_async_helpers.h"
+#include "winbind/wb_helper.h"
+#include "smbd/service_task.h"
+#include "librpc/gen_ndr/ndr_netlogon.h"
+#include "librpc/gen_ndr/ndr_lsa_c.h"
+#include "librpc/gen_ndr/ndr_samr_c.h"
+#include "libcli/libcli.h"
+
+#include "libcli/auth/credentials.h"
+#include "libcli/security/security.h"
+
+#include "libcli/ldap/ldap_client.h"
+
+#include "auth/credentials/credentials.h"
+#include "param/param.h"
+
+/*
+ * Initialize a domain:
+ *
+ * - With schannel credentials, try to open the SMB connection and
+ * NETLOGON pipe with the machine creds. This works against W2k3SP1
+ * with an NTLMSSP session setup. Fall back to anonymous (for the CIFS level).
+ *
+ * - If we have schannel creds, do the auth2 and open the schannel'ed netlogon
+ * pipe.
+ *
+ * - Open LSA. If we have machine creds, try to open with SPNEGO or NTLMSSP. Fall back
+ * to schannel.
+ *
+ * - With queryinfopolicy, verify that we're talking to the right domain
+ *
+ * A bit complex, but with all the combinations I think it's the best we can
+ * get. NT4, W2k3 and W2k all have different combinations, but in the end we
+ * have a signed&sealed lsa connection on all of them.
+ *
+ * Not sure if it is overkill, but it seems to work.
+ */
+
+struct init_domain_state {
+ struct composite_context *ctx;
+ struct wbsrv_domain *domain;
+ struct wbsrv_service *service;
+
+ struct lsa_ObjectAttribute objectattr;
+ struct lsa_OpenPolicy2 lsa_openpolicy;
+ struct lsa_QueryInfoPolicy queryinfo;
+ union lsa_PolicyInformation *info;
+};
+
+static void init_domain_recv_netlogonpipe(struct composite_context *ctx);
+static void init_domain_recv_lsa_pipe(struct composite_context *ctx);
+static void init_domain_recv_lsa_policy(struct rpc_request *req);
+static void init_domain_recv_queryinfo(struct rpc_request *req);
+static void init_domain_recv_samr(struct composite_context *ctx);
+
+static struct dcerpc_binding *init_domain_binding(struct init_domain_state *state,
+ const struct ndr_interface_table *table)
+{
+ struct dcerpc_binding *binding;
+ NTSTATUS status;
+
+ /* Make a binding string */
+ {
+ char *s = talloc_asprintf(state, "ncacn_np:%s", state->domain->dc_name);
+ if (s == NULL) return NULL;
+ status = dcerpc_parse_binding(state, s, &binding);
+ talloc_free(s);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NULL;
+ }
+ }
+
+ /* Alter binding to contain hostname, but also address (so we don't look it up twice) */
+ binding->target_hostname = state->domain->dc_name;
+ binding->host = state->domain->dc_address;
+
+ /* This shouldn't make a network call, as the mappings for named pipes are well known */
+ status = dcerpc_epm_map_binding(binding, binding, table, state->service->task->event_ctx,
+ state->service->task->lp_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NULL;
+ }
+
+ return binding;
+}
+
+struct composite_context *wb_init_domain_send(TALLOC_CTX *mem_ctx,
+ struct wbsrv_service *service,
+ struct wb_dom_info *dom_info)
+{
+ struct composite_context *result, *ctx;
+ struct init_domain_state *state;
+
+ result = composite_create(mem_ctx, service->task->event_ctx);
+ if (result == NULL) goto failed;
+
+ state = talloc_zero(result, struct init_domain_state);
+ if (state == NULL) goto failed;
+ state->ctx = result;
+ result->private_data = state;
+
+ state->service = service;
+
+ state->domain = talloc(state, struct wbsrv_domain);
+ if (state->domain == NULL) goto failed;
+
+ state->domain->info = talloc_reference(state->domain, dom_info);
+ if (state->domain->info == NULL) goto failed;
+
+ /* Caller should check, but to be safe: */
+ if (dom_info->num_dcs < 1) {
+ goto failed;
+ }
+
+ /* For now, we just pick the first. The next step will be to
+ * walk the entire list. Also need to fix finddcs() to return
+ * the entire list */
+ state->domain->dc_name = dom_info->dcs[0].name;
+ state->domain->dc_address = dom_info->dcs[0].address;
+
+ state->domain->libnet_ctx = libnet_context_init(service->task->event_ctx,
+ service->task->lp_ctx);
+
+ /* Create a credentials structure */
+ state->domain->libnet_ctx->cred = cli_credentials_init(state->domain);
+ if (state->domain->libnet_ctx->cred == NULL) goto failed;
+
+ cli_credentials_set_conf(state->domain->libnet_ctx->cred, service->task->lp_ctx);
+
+ /* Connect the machine account to the credentials */
+ state->ctx->status =
+ cli_credentials_set_machine_account(state->domain->libnet_ctx->cred, state->domain->libnet_ctx->lp_ctx);
+ if (!NT_STATUS_IS_OK(state->ctx->status)) goto failed;
+
+ state->domain->netlogon_binding = init_domain_binding(state, &ndr_table_netlogon);
+
+ state->domain->netlogon_pipe = NULL;
+
+ if ((!cli_credentials_is_anonymous(state->domain->libnet_ctx->cred)) &&
+ ((lp_server_role(service->task->lp_ctx) == ROLE_DOMAIN_MEMBER) ||
+ (lp_server_role(service->task->lp_ctx) == ROLE_DOMAIN_CONTROLLER)) &&
+ (dom_sid_equal(state->domain->info->sid,
+ state->service->primary_sid))) {
+ state->domain->netlogon_binding->flags |= DCERPC_SCHANNEL;
+
+ /* For debugging, it can be a real pain if all the traffic is encrypted */
+ if (lp_winbind_sealed_pipes(service->task->lp_ctx)) {
+ state->domain->netlogon_binding->flags |= (DCERPC_SIGN | DCERPC_SEAL );
+ } else {
+ state->domain->netlogon_binding->flags |= (DCERPC_SIGN);
+ }
+ }
+
+ /* No encryption on anonymous pipes */
+
+ ctx = dcerpc_pipe_connect_b_send(state, state->domain->netlogon_binding,
+ &ndr_table_netlogon,
+ state->domain->libnet_ctx->cred,
+ service->task->event_ctx,
+ service->task->lp_ctx);
+
+ if (composite_nomem(ctx, state->ctx)) {
+ goto failed;
+ }
+
+ composite_continue(state->ctx, ctx, init_domain_recv_netlogonpipe,
+ state);
+ return result;
+ failed:
+ talloc_free(result);
+ return NULL;
+}
+
+/* Having make a netlogon connection (possibly secured with schannel),
+ * make an LSA connection to the same DC, on the same IPC$ share */
+static void init_domain_recv_netlogonpipe(struct composite_context *ctx)
+{
+ struct init_domain_state *state =
+ talloc_get_type(ctx->async.private_data,
+ struct init_domain_state);
+
+ state->ctx->status = dcerpc_pipe_connect_b_recv(ctx, state->domain,
+ &state->domain->netlogon_pipe);
+
+ if (!composite_is_ok(state->ctx)) {
+ return;
+ }
+ talloc_steal(state->domain->netlogon_pipe, state->domain->netlogon_binding);
+
+ state->domain->lsa_binding = init_domain_binding(state, &ndr_table_lsarpc);
+
+ /* For debugging, it can be a real pain if all the traffic is encrypted */
+ if (lp_winbind_sealed_pipes(state->service->task->lp_ctx)) {
+ state->domain->lsa_binding->flags |= (DCERPC_SIGN | DCERPC_SEAL );
+ } else {
+ state->domain->lsa_binding->flags |= (DCERPC_SIGN);
+ }
+
+ state->domain->libnet_ctx->lsa.pipe = NULL;
+
+ /* this will make the secondary connection on the same IPC$ share,
+ secured with SPNEGO or NTLMSSP */
+ ctx = dcerpc_secondary_auth_connection_send(state->domain->netlogon_pipe,
+ state->domain->lsa_binding,
+ &ndr_table_lsarpc,
+ state->domain->libnet_ctx->cred,
+ state->domain->libnet_ctx->lp_ctx
+ );
+ composite_continue(state->ctx, ctx, init_domain_recv_lsa_pipe, state);
+}
+
+static bool retry_with_schannel(struct init_domain_state *state,
+ struct dcerpc_binding *binding,
+ const struct ndr_interface_table *table,
+ void (*continuation)(struct composite_context *))
+{
+ struct composite_context *ctx;
+ state->ctx->status = NT_STATUS_OK;
+ if (state->domain->netlogon_binding->flags & DCERPC_SCHANNEL
+ && !(binding->flags & DCERPC_SCHANNEL)) {
+ /* Opening a policy handle failed, perhaps it was
+ * because we don't get a 'wrong password' error on
+ * NTLMSSP binds */
+
+ /* Try again with schannel */
+ binding->flags |= DCERPC_SCHANNEL;
+
+ /* Try again, likewise on the same IPC$ share,
+ secured with SCHANNEL */
+ ctx = dcerpc_secondary_auth_connection_send(state->domain->netlogon_pipe,
+ binding,
+ table,
+ state->domain->libnet_ctx->cred,
+ state->domain->libnet_ctx->lp_ctx);
+ composite_continue(state->ctx, ctx, continuation, state);
+ return true;
+ } else {
+ return false;
+ }
+}
+/* We should now have either an authenticated LSA pipe, or an error.
+ * On success, open a policy handle
+ */
+static void init_domain_recv_lsa_pipe(struct composite_context *ctx)
+{
+ struct rpc_request *req;
+ struct init_domain_state *state =
+ talloc_get_type(ctx->async.private_data,
+ struct init_domain_state);
+
+ state->ctx->status = dcerpc_secondary_auth_connection_recv(ctx, state->domain,
+ &state->domain->libnet_ctx->lsa.pipe);
+ if (NT_STATUS_EQUAL(state->ctx->status, NT_STATUS_LOGON_FAILURE)) {
+ if (retry_with_schannel(state, state->domain->lsa_binding,
+ &ndr_table_lsarpc,
+ init_domain_recv_lsa_pipe)) {
+ return;
+ }
+ }
+ if (!composite_is_ok(state->ctx)) return;
+
+ talloc_steal(state->domain->libnet_ctx, state->domain->libnet_ctx->lsa.pipe);
+ talloc_steal(state->domain->libnet_ctx->lsa.pipe, state->domain->lsa_binding);
+ state->domain->libnet_ctx->lsa.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ state->domain->libnet_ctx->lsa.name = state->domain->info->name;
+
+ ZERO_STRUCT(state->domain->libnet_ctx->lsa.handle);
+ state->lsa_openpolicy.in.system_name =
+ talloc_asprintf(state, "\\\\%s",
+ dcerpc_server_name(state->domain->libnet_ctx->lsa.pipe));
+ ZERO_STRUCT(state->objectattr);
+ state->lsa_openpolicy.in.attr = &state->objectattr;
+ state->lsa_openpolicy.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ state->lsa_openpolicy.out.handle = &state->domain->libnet_ctx->lsa.handle;
+
+ req = dcerpc_lsa_OpenPolicy2_send(state->domain->libnet_ctx->lsa.pipe, state,
+ &state->lsa_openpolicy);
+
+ composite_continue_rpc(state->ctx, req, init_domain_recv_lsa_policy, state);
+}
+
+/* Receive a policy handle (or not, and retry the authentication) and
+ * obtain some basic information about the domain */
+
+static void init_domain_recv_lsa_policy(struct rpc_request *req)
+{
+ struct init_domain_state *state =
+ talloc_get_type(req->async.private_data,
+ struct init_domain_state);
+
+ state->ctx->status = dcerpc_ndr_request_recv(req);
+ if ((!NT_STATUS_IS_OK(state->ctx->status)
+ || !NT_STATUS_IS_OK(state->lsa_openpolicy.out.result))) {
+ if (retry_with_schannel(state, state->domain->lsa_binding,
+ &ndr_table_lsarpc,
+ init_domain_recv_lsa_pipe)) {
+ return;
+ }
+ }
+ if (!composite_is_ok(state->ctx)) return;
+ state->ctx->status = state->lsa_openpolicy.out.result;
+ if (!composite_is_ok(state->ctx)) return;
+
+ state->info = talloc_zero(state->ctx, union lsa_PolicyInformation);
+ if (composite_nomem(state->info, state->ctx)) return;
+
+ state->queryinfo.in.handle = &state->domain->libnet_ctx->lsa.handle;
+ state->queryinfo.in.level = LSA_POLICY_INFO_ACCOUNT_DOMAIN;
+ state->queryinfo.out.info = &state->info;
+
+ req = dcerpc_lsa_QueryInfoPolicy_send(state->domain->libnet_ctx->lsa.pipe, state,
+ &state->queryinfo);
+ composite_continue_rpc(state->ctx, req,
+ init_domain_recv_queryinfo, state);
+}
+
+static void init_domain_recv_queryinfo(struct rpc_request *req)
+{
+ struct init_domain_state *state =
+ talloc_get_type(req->async.private_data, struct init_domain_state);
+ struct lsa_DomainInfo *dominfo;
+ struct composite_context *ctx;
+
+ state->ctx->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(state->ctx)) return;
+ state->ctx->status = state->queryinfo.out.result;
+ if (!composite_is_ok(state->ctx)) return;
+
+ dominfo = &(*state->queryinfo.out.info)->account_domain;
+
+ if (strcasecmp(state->domain->info->name, dominfo->name.string) != 0) {
+ DEBUG(2, ("Expected domain name %s, DC %s said %s\n",
+ state->domain->info->name,
+ dcerpc_server_name(state->domain->libnet_ctx->lsa.pipe),
+ dominfo->name.string));
+ composite_error(state->ctx, NT_STATUS_INVALID_DOMAIN_STATE);
+ return;
+ }
+
+ if (!dom_sid_equal(state->domain->info->sid, dominfo->sid)) {
+ DEBUG(2, ("Expected domain sid %s, DC %s said %s\n",
+ dom_sid_string(state, state->domain->info->sid),
+ dcerpc_server_name(state->domain->libnet_ctx->lsa.pipe),
+ dom_sid_string(state, dominfo->sid)));
+ composite_error(state->ctx, NT_STATUS_INVALID_DOMAIN_STATE);
+ return;
+ }
+
+ state->domain->samr_binding = init_domain_binding(state, &ndr_table_samr);
+
+ /* We want to use the same flags as the LSA pipe did (so, if
+ * it needed schannel, then we need that here too) */
+ state->domain->samr_binding->flags = state->domain->lsa_binding->flags;
+
+ state->domain->libnet_ctx->samr.pipe = NULL;
+
+ ctx = wb_connect_samr_send(state, state->domain);
+ composite_continue(state->ctx, ctx, init_domain_recv_samr, state);
+}
+
+/* Recv the SAMR details (SamrConnect and SamrOpenDomain handle) and
+ * open an LDAP connection */
+static void init_domain_recv_samr(struct composite_context *ctx)
+{
+ struct init_domain_state *state =
+ talloc_get_type(ctx->async.private_data,
+ struct init_domain_state);
+
+ state->ctx->status = wb_connect_samr_recv(
+ ctx, state->domain,
+ &state->domain->libnet_ctx->samr.pipe,
+ &state->domain->libnet_ctx->samr.connect_handle,
+ &state->domain->libnet_ctx->samr.handle);
+ if (!composite_is_ok(state->ctx)) return;
+
+ talloc_steal(state->domain->libnet_ctx->samr.pipe, state->domain->samr_binding);
+ state->domain->libnet_ctx->samr.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ state->domain->libnet_ctx->samr.name = state->domain->info->name;
+ state->domain->libnet_ctx->samr.sid = dom_sid_dup(
+ state->domain->libnet_ctx,
+ state->domain->info->sid);
+
+ composite_done(state->ctx);
+}
+
+NTSTATUS wb_init_domain_recv(struct composite_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct wbsrv_domain **result)
+{
+ NTSTATUS status = composite_wait(c);
+ if (NT_STATUS_IS_OK(status)) {
+ struct init_domain_state *state =
+ talloc_get_type(c->private_data,
+ struct init_domain_state);
+ *result = talloc_steal(mem_ctx, state->domain);
+ }
+ talloc_free(c);
+ return status;
+}
+
+NTSTATUS wb_init_domain(TALLOC_CTX *mem_ctx, struct wbsrv_service *service,
+ struct wb_dom_info *dom_info,
+ struct wbsrv_domain **result)
+{
+ struct composite_context *c =
+ wb_init_domain_send(mem_ctx, service, dom_info);
+ return wb_init_domain_recv(c, mem_ctx, result);
+}
diff --git a/source4/winbind/wb_irpc.c b/source4/winbind/wb_irpc.c
new file mode 100644
index 0000000000..42f4e7c94b
--- /dev/null
+++ b/source4/winbind/wb_irpc.c
@@ -0,0 +1,155 @@
+/*
+ Unix SMB/CIFS implementation.
+ Main winbindd irpc handlers
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbind/wb_server.h"
+#include "lib/messaging/irpc.h"
+#include "libcli/composite/composite.h"
+#include "libcli/security/proto.h"
+#include "librpc/gen_ndr/ndr_winbind.h"
+#include "smbd/service_task.h"
+
+struct wb_irpc_SamLogon_state {
+ struct irpc_message *msg;
+ struct winbind_SamLogon *req;
+};
+
+static void wb_irpc_SamLogon_callback(struct composite_context *ctx);
+
+static NTSTATUS wb_irpc_SamLogon(struct irpc_message *msg,
+ struct winbind_SamLogon *req)
+{
+ struct wbsrv_service *service = talloc_get_type(msg->private_data,
+ struct wbsrv_service);
+ struct wb_irpc_SamLogon_state *s;
+ struct composite_context *ctx;
+
+ DEBUG(5, ("wb_irpc_SamLogon called\n"));
+
+ s = talloc(msg, struct wb_irpc_SamLogon_state);
+ NT_STATUS_HAVE_NO_MEMORY(s);
+
+ s->msg = msg;
+ s->req = req;
+
+ ctx = wb_sam_logon_send(msg, service, req);
+ NT_STATUS_HAVE_NO_MEMORY(ctx);
+
+ ctx->async.fn = wb_irpc_SamLogon_callback;
+ ctx->async.private_data = s;
+
+ msg->defer_reply = true;
+ return NT_STATUS_OK;
+}
+
+static void wb_irpc_SamLogon_callback(struct composite_context *ctx)
+{
+ struct wb_irpc_SamLogon_state *s = talloc_get_type(ctx->async.private_data,
+ struct wb_irpc_SamLogon_state);
+ NTSTATUS status;
+
+ DEBUG(5, ("wb_irpc_SamLogon_callback called\n"));
+
+ status = wb_sam_logon_recv(ctx, s, s->req);
+
+ irpc_send_reply(s->msg, status);
+}
+
+struct wb_irpc_get_idmap_state {
+ struct irpc_message *msg;
+ struct winbind_get_idmap *req;
+ int level;
+};
+
+static void wb_irpc_get_idmap_callback(struct composite_context *ctx);
+
+static NTSTATUS wb_irpc_get_idmap(struct irpc_message *msg,
+ struct winbind_get_idmap *req)
+{
+ struct wbsrv_service *service = talloc_get_type(msg->private_data,
+ struct wbsrv_service);
+ struct wb_irpc_get_idmap_state *s;
+ struct composite_context *ctx;
+
+ DEBUG(5, ("wb_irpc_get_idmap called\n"));
+
+ s = talloc(msg, struct wb_irpc_get_idmap_state);
+ NT_STATUS_HAVE_NO_MEMORY(s);
+
+ s->msg = msg;
+ s->req = req;
+ s->level = req->in.level;
+
+ switch(s->level) {
+ case WINBIND_IDMAP_LEVEL_SIDS_TO_XIDS:
+ ctx = wb_sids2xids_send(msg, service, req->in.count,
+ req->in.ids);
+ break;
+ case WINBIND_IDMAP_LEVEL_XIDS_TO_SIDS:
+ ctx = wb_xids2sids_send(msg, service, req->in.count,
+ req->in.ids);
+ break;
+ }
+ NT_STATUS_HAVE_NO_MEMORY(ctx);
+
+ composite_continue(ctx, ctx, wb_irpc_get_idmap_callback, s);
+ msg->defer_reply = true;
+
+ return NT_STATUS_OK;
+}
+
+static void wb_irpc_get_idmap_callback(struct composite_context *ctx)
+{
+ struct wb_irpc_get_idmap_state *s;
+ NTSTATUS status;
+
+ DEBUG(5, ("wb_irpc_get_idmap_callback called\n"));
+
+ s = talloc_get_type(ctx->async.private_data,
+ struct wb_irpc_get_idmap_state);
+
+ switch(s->level) {
+ case WINBIND_IDMAP_LEVEL_SIDS_TO_XIDS:
+ status = wb_sids2xids_recv(ctx, &s->req->out.ids);
+ break;
+ case WINBIND_IDMAP_LEVEL_XIDS_TO_SIDS:
+ status = wb_xids2sids_recv(ctx, &s->req->out.ids);
+ break;
+ }
+
+ irpc_send_reply(s->msg, status);
+}
+
+NTSTATUS wbsrv_init_irpc(struct wbsrv_service *service)
+{
+ NTSTATUS status;
+
+ irpc_add_name(service->task->msg_ctx, "winbind_server");
+
+ status = IRPC_REGISTER(service->task->msg_ctx, winbind, WINBIND_SAMLOGON,
+ wb_irpc_SamLogon, service);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ status = IRPC_REGISTER(service->task->msg_ctx, winbind, WINBIND_GET_IDMAP,
+ wb_irpc_get_idmap, service);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/winbind/wb_name2domain.c b/source4/winbind/wb_name2domain.c
new file mode 100644
index 0000000000..e19703b1e5
--- /dev/null
+++ b/source4/winbind/wb_name2domain.c
@@ -0,0 +1,121 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Find and init a domain struct for a name
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/composite/composite.h"
+#include "winbind/wb_server.h"
+#include "smbd/service_task.h"
+#include "winbind/wb_helper.h"
+#include "param/param.h"
+
+struct name2domain_state {
+ struct composite_context *ctx;
+ struct wbsrv_service *service;
+
+ struct wbsrv_domain *domain;
+};
+
+static void name2domain_recv_sid(struct composite_context *ctx);
+static void name2domain_recv_domain(struct composite_context *ctx);
+
+struct composite_context *wb_name2domain_send(TALLOC_CTX *mem_ctx,
+ struct wbsrv_service *service, const char* name)
+{
+ struct composite_context *result, *ctx;
+ struct name2domain_state *state;
+ char *user_dom, *user_name;
+ bool ok;
+
+ DEBUG(5, ("wb_name2domain_send called\n"));
+
+ result = composite_create(mem_ctx, service->task->event_ctx);
+ if (!result) return NULL;
+
+ state = talloc(result, struct name2domain_state);
+ if (composite_nomem(state, result)) return result;
+ state->ctx = result;
+ result->private_data = state;
+ state->service = service;
+
+ ok = wb_samba3_split_username(state, service->task->lp_ctx, name, &user_dom, &user_name);
+ if(!ok) {
+ composite_error(state->ctx, NT_STATUS_OBJECT_NAME_INVALID);
+ return result;
+ }
+
+ ctx = wb_cmd_lookupname_send(state, service, user_dom, user_name);
+ if (composite_nomem(ctx, state->ctx)) return result;
+
+ composite_continue(result, ctx, name2domain_recv_sid, state);
+ return result;
+}
+
+static void name2domain_recv_sid(struct composite_context *ctx)
+{
+ struct name2domain_state *state =
+ talloc_get_type(ctx->async.private_data,
+ struct name2domain_state);
+ struct wb_sid_object *sid;
+
+ DEBUG(5, ("name2domain_recv_sid called\n"));
+
+ state->ctx->status = wb_cmd_lookupname_recv(ctx, state, &sid);
+ if(!composite_is_ok(state->ctx)) return;
+
+ ctx = wb_sid2domain_send(state, state->service, sid->sid);
+
+ composite_continue(state->ctx, ctx, name2domain_recv_domain, state);
+}
+
+static void name2domain_recv_domain(struct composite_context *ctx)
+{
+ struct name2domain_state *state =
+ talloc_get_type(ctx->async.private_data,
+ struct name2domain_state);
+ struct wbsrv_domain *domain;
+
+ DEBUG(5, ("name2domain_recv_domain called\n"));
+
+ state->ctx->status = wb_sid2domain_recv(ctx, &domain);
+ if(!composite_is_ok(state->ctx)) return;
+
+ state->domain = domain;
+
+ composite_done(state->ctx);
+}
+
+NTSTATUS wb_name2domain_recv(struct composite_context *ctx,
+ struct wbsrv_domain **result)
+{
+ NTSTATUS status = composite_wait(ctx);
+
+ DEBUG(5, ("wb_name2domain_recv called\n"));
+
+ if (NT_STATUS_IS_OK(status)) {
+ struct name2domain_state *state =
+ talloc_get_type(ctx->private_data,
+ struct name2domain_state);
+ *result = state->domain;
+ }
+ talloc_free(ctx);
+ return status;
+}
+
diff --git a/source4/winbind/wb_pam_auth.c b/source4/winbind/wb_pam_auth.c
new file mode 100644
index 0000000000..b2579fd6df
--- /dev/null
+++ b/source4/winbind/wb_pam_auth.c
@@ -0,0 +1,270 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Authenticate a user
+
+ Copyright (C) Volker Lendecke 2005
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/composite/composite.h"
+#include "winbind/wb_server.h"
+#include "smbd/service_task.h"
+#include "auth/credentials/credentials.h"
+#include "libcli/auth/libcli_auth.h"
+#include "librpc/gen_ndr/ndr_netlogon.h"
+#include "librpc/gen_ndr/ndr_netlogon_c.h"
+#include "librpc/gen_ndr/winbind.h"
+#include "param/param.h"
+
+/* Oh, there is so much to keep an eye on when authenticating a user. Oh my! */
+struct pam_auth_crap_state {
+ struct composite_context *ctx;
+ struct tevent_context *event_ctx;
+ struct loadparm_context *lp_ctx;
+
+ struct winbind_SamLogon *req;
+ char *unix_username;
+
+ struct netr_NetworkInfo ninfo;
+ struct netr_LogonSamLogon r;
+
+ const char *user_name;
+ const char *domain_name;
+
+ struct netr_UserSessionKey user_session_key;
+ struct netr_LMSessionKey lm_key;
+ DATA_BLOB info3;
+};
+
+/*
+ * NTLM authentication.
+*/
+
+static void pam_auth_crap_recv_logon(struct composite_context *ctx);
+
+struct composite_context *wb_cmd_pam_auth_crap_send(TALLOC_CTX *mem_ctx,
+ struct wbsrv_service *service,
+ uint32_t logon_parameters,
+ const char *domain,
+ const char *user,
+ const char *workstation,
+ DATA_BLOB chal,
+ DATA_BLOB nt_resp,
+ DATA_BLOB lm_resp)
+{
+ struct composite_context *result, *ctx;
+ struct pam_auth_crap_state *state;
+ struct netr_NetworkInfo *ninfo;
+ DATA_BLOB tmp_nt_resp, tmp_lm_resp;
+
+ result = composite_create(mem_ctx, service->task->event_ctx);
+ if (result == NULL) goto failed;
+
+ state = talloc(result, struct pam_auth_crap_state);
+ if (state == NULL) goto failed;
+ state->ctx = result;
+ state->lp_ctx = service->task->lp_ctx;
+ result->private_data = state;
+
+ state->req = talloc(state, struct winbind_SamLogon);
+
+ state->req->in.logon_level = 2;
+ state->req->in.validation_level = 3;
+ ninfo = state->req->in.logon.network = talloc(state, struct netr_NetworkInfo);
+ if (ninfo == NULL) goto failed;
+
+ ninfo->identity_info.account_name.string = talloc_strdup(state, user);
+ ninfo->identity_info.domain_name.string = talloc_strdup(state, domain);
+ ninfo->identity_info.parameter_control = logon_parameters;
+ ninfo->identity_info.logon_id_low = 0;
+ ninfo->identity_info.logon_id_high = 0;
+ ninfo->identity_info.workstation.string = talloc_strdup(state, workstation);
+
+ SMB_ASSERT(chal.length == sizeof(ninfo->challenge));
+ memcpy(ninfo->challenge, chal.data,
+ sizeof(ninfo->challenge));
+
+ tmp_nt_resp = data_blob_talloc(ninfo, nt_resp.data, nt_resp.length);
+ if ((nt_resp.data != NULL) &&
+ (tmp_nt_resp.data == NULL)) goto failed;
+
+ tmp_lm_resp = data_blob_talloc(ninfo, lm_resp.data, lm_resp.length);
+ if ((lm_resp.data != NULL) &&
+ (tmp_lm_resp.data == NULL)) goto failed;
+
+ ninfo->nt.length = tmp_nt_resp.length;
+ ninfo->nt.data = tmp_nt_resp.data;
+ ninfo->lm.length = tmp_lm_resp.length;
+ ninfo->lm.data = tmp_lm_resp.data;
+
+ state->unix_username = NULL;
+
+ ctx = wb_sam_logon_send(mem_ctx, service, state->req);
+ if (ctx == NULL) goto failed;
+
+ composite_continue(result, ctx, pam_auth_crap_recv_logon, state);
+ return result;
+
+ failed:
+ talloc_free(result);
+ return NULL;
+}
+
+/*
+ NTLM Authentication
+
+ Send of a SamLogon request to authenticate a user.
+*/
+static void pam_auth_crap_recv_logon(struct composite_context *ctx)
+{
+ DATA_BLOB tmp_blob;
+ enum ndr_err_code ndr_err;
+ struct netr_SamBaseInfo *base;
+ struct pam_auth_crap_state *state =
+ talloc_get_type(ctx->async.private_data,
+ struct pam_auth_crap_state);
+
+ state->ctx->status = wb_sam_logon_recv(ctx, state, state->req);
+ if (!composite_is_ok(state->ctx)) return;
+
+ ndr_err = ndr_push_struct_blob(
+ &tmp_blob, state, lp_iconv_convenience(state->lp_ctx),
+ state->req->out.validation.sam3,
+ (ndr_push_flags_fn_t)ndr_push_netr_SamInfo3);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ state->ctx->status = ndr_map_error2ntstatus(ndr_err);
+ if (!composite_is_ok(state->ctx)) return;
+ }
+
+ /* The Samba3 protocol is a bit broken (due to non-IDL
+ * heritage, so for compatability we must add a non-zero 4
+ * bytes to the info3 */
+ state->info3 = data_blob_talloc(state, NULL, tmp_blob.length+4);
+ if (composite_nomem(state->info3.data, state->ctx)) return;
+
+ SIVAL(state->info3.data, 0, 1);
+ memcpy(state->info3.data+4, tmp_blob.data, tmp_blob.length);
+
+ base = &state->req->out.validation.sam3->base;
+
+ state->user_session_key = base->key;
+ state->lm_key = base->LMSessKey;
+
+ /* Give the caller the most accurate username possible.
+ * Assists where case sensitive comparisons may be done by our
+ * ntlm_auth callers */
+ if (base->account_name.string) {
+ state->user_name = base->account_name.string;
+ talloc_steal(state, base->account_name.string);
+ }
+ if (base->domain.string) {
+ state->domain_name = base->domain.string;
+ talloc_steal(state, base->domain.string);
+ }
+
+ state->unix_username = talloc_asprintf(state, "%s%s%s",
+ state->domain_name,
+ lp_winbind_separator(state->lp_ctx),
+ state->user_name);
+ if (composite_nomem(state->unix_username, state->ctx)) return;
+
+ composite_done(state->ctx);
+}
+
+/* Having received a NTLM authentication reply, parse out the useful
+ * reply data for the caller */
+NTSTATUS wb_cmd_pam_auth_crap_recv(struct composite_context *c,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *info3,
+ struct netr_UserSessionKey *user_session_key,
+ struct netr_LMSessionKey *lm_key,
+ char **unix_username)
+{
+ struct pam_auth_crap_state *state =
+ talloc_get_type(c->private_data, struct pam_auth_crap_state);
+ NTSTATUS status = composite_wait(c);
+ if (NT_STATUS_IS_OK(status)) {
+ info3->length = state->info3.length;
+ info3->data = talloc_steal(mem_ctx, state->info3.data);
+ *user_session_key = state->user_session_key;
+ *lm_key = state->lm_key;
+ *unix_username = talloc_steal(mem_ctx, state->unix_username);
+ }
+ talloc_free(state);
+ return status;
+}
+
+/* Handle plaintext authentication, by encrypting the password and
+ * then sending via the NTLM calls */
+
+struct composite_context *wb_cmd_pam_auth_send(TALLOC_CTX *mem_ctx,
+ struct wbsrv_service *service,
+ struct cli_credentials *credentials)
+{
+ const char *workstation;
+ NTSTATUS status;
+ const char *user, *domain;
+ DATA_BLOB chal, nt_resp, lm_resp, names_blob;
+ int flags = CLI_CRED_NTLM_AUTH;
+ if (lp_client_lanman_auth(service->task->lp_ctx)) {
+ flags |= CLI_CRED_LANMAN_AUTH;
+ }
+
+ if (lp_client_ntlmv2_auth(service->task->lp_ctx)) {
+ flags |= CLI_CRED_NTLMv2_AUTH;
+ }
+
+ DEBUG(5, ("wbsrv_samba3_pam_auth called\n"));
+
+ chal = data_blob_talloc(mem_ctx, NULL, 8);
+ if (!chal.data) {
+ return NULL;
+ }
+ generate_random_buffer(chal.data, chal.length);
+ cli_credentials_get_ntlm_username_domain(credentials, mem_ctx,
+ &user, &domain);
+ /* for best compatability with multiple vitual netbios names
+ * on the host, this should be generated from the
+ * cli_credentials associated with the machine account */
+ workstation = cli_credentials_get_workstation(credentials);
+
+ names_blob = NTLMv2_generate_names_blob(
+ mem_ctx,
+ cli_credentials_get_workstation(credentials),
+ cli_credentials_get_domain(credentials));
+
+ status = cli_credentials_get_ntlm_response(
+ credentials, mem_ctx, &flags, chal, names_blob,
+ &lm_resp, &nt_resp, NULL, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NULL;
+ }
+ return wb_cmd_pam_auth_crap_send(mem_ctx, service,
+ MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT|MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT /* logon parameters */,
+ domain, user, workstation,
+ chal, nt_resp, lm_resp);
+}
+
+NTSTATUS wb_cmd_pam_auth_recv(struct composite_context *c)
+{
+ struct pam_auth_crap_state *state =
+ talloc_get_type(c->private_data, struct pam_auth_crap_state);
+ NTSTATUS status = composite_wait(c);
+ talloc_free(state);
+ return status;
+}
diff --git a/source4/winbind/wb_sam_logon.c b/source4/winbind/wb_sam_logon.c
new file mode 100644
index 0000000000..5ceb6e4af0
--- /dev/null
+++ b/source4/winbind/wb_sam_logon.c
@@ -0,0 +1,171 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Do a netr_LogonSamLogon to a remote DC
+
+ Copyright (C) Volker Lendecke 2005
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/composite/composite.h"
+#include "winbind/wb_server.h"
+#include "smbd/service_task.h"
+#include "auth/credentials/credentials.h"
+#include "libcli/auth/libcli_auth.h"
+#include "librpc/gen_ndr/ndr_netlogon.h"
+#include "librpc/gen_ndr/ndr_netlogon_c.h"
+#include "librpc/gen_ndr/winbind.h"
+
+struct wb_sam_logon_state {
+ struct composite_context *ctx;
+
+ struct winbind_SamLogon *req;
+
+ struct creds_CredentialState *creds_state;
+ struct netr_Authenticator auth1, auth2;
+
+ TALLOC_CTX *r_mem_ctx;
+ struct netr_LogonSamLogon r;
+};
+
+static void wb_sam_logon_recv_domain(struct composite_context *ctx);
+static void wb_sam_logon_recv_samlogon(struct rpc_request *req);
+
+/*
+ Find the connection to the DC (or find an existing connection)
+*/
+struct composite_context *wb_sam_logon_send(TALLOC_CTX *mem_ctx,
+ struct wbsrv_service *service,
+ struct winbind_SamLogon *req)
+{
+ struct composite_context *c, *creq;
+ struct wb_sam_logon_state *s;
+
+ c = composite_create(mem_ctx, service->task->event_ctx);
+ if (!c) return NULL;
+
+ s = talloc_zero(c, struct wb_sam_logon_state);
+ if (composite_nomem(s, c)) return c;
+ s->ctx = c;
+ s->req = req;
+
+ c->private_data = s;
+
+ creq = wb_sid2domain_send(s, service, service->primary_sid);
+ composite_continue(c, creq, wb_sam_logon_recv_domain, s);
+ return c;
+}
+
+/*
+ Having finished making the connection to the DC
+ Send of a SamLogon request to authenticate a user.
+*/
+static void wb_sam_logon_recv_domain(struct composite_context *creq)
+{
+ struct wb_sam_logon_state *s = talloc_get_type(creq->async.private_data,
+ struct wb_sam_logon_state);
+ struct rpc_request *req;
+ struct wbsrv_domain *domain;
+
+ s->ctx->status = wb_sid2domain_recv(creq, &domain);
+ if (!composite_is_ok(s->ctx)) return;
+
+ s->creds_state = cli_credentials_get_netlogon_creds(domain->libnet_ctx->cred);
+ creds_client_authenticator(s->creds_state, &s->auth1);
+
+ s->r.in.server_name = talloc_asprintf(s, "\\\\%s",
+ dcerpc_server_name(domain->netlogon_pipe));
+ if (composite_nomem(s->r.in.server_name, s->ctx)) return;
+
+ s->r.in.computer_name = cli_credentials_get_workstation(domain->libnet_ctx->cred);
+ s->r.in.credential = &s->auth1;
+ s->r.in.return_authenticator = &s->auth2;
+ s->r.in.logon_level = s->req->in.logon_level;
+ s->r.in.logon = &s->req->in.logon;
+ s->r.in.validation_level = s->req->in.validation_level;
+ s->r.out.return_authenticator = NULL;
+ s->r.out.validation = talloc(s, union netr_Validation);
+ if (composite_nomem(s->r.out.validation, s->ctx)) return;
+ s->r.out.authoritative = talloc(s, uint8_t);
+ if (composite_nomem(s->r.out.authoritative, s->ctx)) return;
+
+
+ /*
+ * use a new talloc context for the LogonSamLogon call
+ * because then we can just to a talloc_steal on this context
+ * in the final _recv() function to give the caller all the content of
+ * the s->r.out.validation
+ */
+ s->r_mem_ctx = talloc_new(s);
+ if (composite_nomem(s->r_mem_ctx, s->ctx)) return;
+
+ req = dcerpc_netr_LogonSamLogon_send(domain->netlogon_pipe, s->r_mem_ctx, &s->r);
+ composite_continue_rpc(s->ctx, req, wb_sam_logon_recv_samlogon, s);
+}
+
+/*
+ NTLM Authentication
+
+ Check the SamLogon reply and decrypt the session keys
+*/
+static void wb_sam_logon_recv_samlogon(struct rpc_request *req)
+{
+ struct wb_sam_logon_state *s = talloc_get_type(req->async.private_data,
+ struct wb_sam_logon_state);
+
+ s->ctx->status = dcerpc_ndr_request_recv(req);
+ if (!composite_is_ok(s->ctx)) return;
+
+ s->ctx->status = s->r.out.result;
+ if (!composite_is_ok(s->ctx)) return;
+
+ if ((s->r.out.return_authenticator == NULL) ||
+ (!creds_client_check(s->creds_state,
+ &s->r.out.return_authenticator->cred))) {
+ DEBUG(0, ("Credentials check failed!\n"));
+ composite_error(s->ctx, NT_STATUS_ACCESS_DENIED);
+ return;
+ }
+
+ /* Decrypt the session keys before we reform the info3, so the
+ * person on the other end of winbindd pipe doesn't have to.
+ * They won't have the encryption key anyway */
+ creds_decrypt_samlogon(s->creds_state,
+ s->r.in.validation_level,
+ s->r.out.validation);
+
+ composite_done(s->ctx);
+}
+
+NTSTATUS wb_sam_logon_recv(struct composite_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct winbind_SamLogon *req)
+{
+ struct wb_sam_logon_state *s = talloc_get_type(c->private_data,
+ struct wb_sam_logon_state);
+ NTSTATUS status = composite_wait(c);
+
+ if (NT_STATUS_IS_OK(status)) {
+ talloc_steal(mem_ctx, s->r_mem_ctx);
+ req->out.validation = *s->r.out.validation;
+ req->out.authoritative = 1;
+ }
+
+ talloc_free(s);
+ return status;
+}
diff --git a/source4/winbind/wb_samba3_cmd.c b/source4/winbind/wb_samba3_cmd.c
new file mode 100644
index 0000000000..2c273e5175
--- /dev/null
+++ b/source4/winbind/wb_samba3_cmd.c
@@ -0,0 +1,1219 @@
+/*
+ Unix SMB/CIFS implementation.
+ Main winbindd samba3 server routines
+
+ Copyright (C) Stefan Metzmacher 2005
+ Copyright (C) Volker Lendecke 2005
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
+ Copyright (C) Kai Blin 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbind/wb_server.h"
+#include "winbind/wb_async_helpers.h"
+#include "param/param.h"
+#include "winbind/wb_helper.h"
+#include "libcli/composite/composite.h"
+#include "version.h"
+#include "librpc/gen_ndr/netlogon.h"
+#include "libcli/security/security.h"
+#include "auth/ntlm/pam_errors.h"
+#include "auth/credentials/credentials.h"
+#include "smbd/service_task.h"
+
+/*
+ Send off the reply to an async Samba3 query, handling filling in the PAM, NTSTATUS and string errors.
+*/
+
+static void wbsrv_samba3_async_auth_epilogue(NTSTATUS status,
+ struct wbsrv_samba3_call *s3call)
+{
+ struct winbindd_response *resp = &s3call->response;
+ if (!NT_STATUS_IS_OK(status)) {
+ resp->result = WINBINDD_ERROR;
+ } else {
+ resp->result = WINBINDD_OK;
+ }
+
+ WBSRV_SAMBA3_SET_STRING(resp->data.auth.nt_status_string,
+ nt_errstr(status));
+ WBSRV_SAMBA3_SET_STRING(resp->data.auth.error_string,
+ get_friendly_nt_error_msg(status));
+
+ resp->data.auth.pam_error = nt_status_to_pam(status);
+ resp->data.auth.nt_status = NT_STATUS_V(status);
+
+ wbsrv_samba3_send_reply(s3call);
+}
+
+/*
+ Send of a generic reply to a Samba3 query
+*/
+
+static void wbsrv_samba3_async_epilogue(NTSTATUS status,
+ struct wbsrv_samba3_call *s3call)
+{
+ struct winbindd_response *resp = &s3call->response;
+ if (NT_STATUS_IS_OK(status)) {
+ resp->result = WINBINDD_OK;
+ } else {
+ resp->result = WINBINDD_ERROR;
+ }
+
+ wbsrv_samba3_send_reply(s3call);
+}
+
+/*
+ Boilerplate commands, simple queries without network traffic
+*/
+
+NTSTATUS wbsrv_samba3_interface_version(struct wbsrv_samba3_call *s3call)
+{
+ s3call->response.result = WINBINDD_OK;
+ s3call->response.data.interface_version = WINBIND_INTERFACE_VERSION;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS wbsrv_samba3_info(struct wbsrv_samba3_call *s3call)
+{
+ s3call->response.result = WINBINDD_OK;
+ s3call->response.data.info.winbind_separator = *lp_winbind_separator(s3call->wbconn->lp_ctx);
+ WBSRV_SAMBA3_SET_STRING(s3call->response.data.info.samba_version,
+ SAMBA_VERSION_STRING);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS wbsrv_samba3_domain_name(struct wbsrv_samba3_call *s3call)
+{
+ s3call->response.result = WINBINDD_OK;
+ WBSRV_SAMBA3_SET_STRING(s3call->response.data.domain_name,
+ lp_workgroup(s3call->wbconn->lp_ctx));
+ return NT_STATUS_OK;
+}
+
+NTSTATUS wbsrv_samba3_netbios_name(struct wbsrv_samba3_call *s3call)
+{
+ s3call->response.result = WINBINDD_OK;
+ WBSRV_SAMBA3_SET_STRING(s3call->response.data.netbios_name,
+ lp_netbios_name(s3call->wbconn->lp_ctx));
+ return NT_STATUS_OK;
+}
+
+NTSTATUS wbsrv_samba3_priv_pipe_dir(struct wbsrv_samba3_call *s3call)
+{
+ const char *path = s3call->wbconn->listen_socket->service->priv_socket_path;
+ s3call->response.result = WINBINDD_OK;
+ s3call->response.extra_data.data = discard_const(path);
+
+ s3call->response.length += strlen(path) + 1;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS wbsrv_samba3_ping(struct wbsrv_samba3_call *s3call)
+{
+ s3call->response.result = WINBINDD_OK;
+ return NT_STATUS_OK;
+}
+
+/* Plaintext authentication
+
+ This interface is used by ntlm_auth in it's 'basic' authentication
+ mode, as well as by pam_winbind to authenticate users where we are
+ given a plaintext password.
+*/
+
+static void check_machacc_recv(struct composite_context *ctx);
+
+NTSTATUS wbsrv_samba3_check_machacc(struct wbsrv_samba3_call *s3call)
+{
+ NTSTATUS status;
+ struct cli_credentials *creds;
+ struct composite_context *ctx;
+ struct wbsrv_service *service =
+ s3call->wbconn->listen_socket->service;
+
+ /* Create a credentials structure */
+ creds = cli_credentials_init(s3call);
+ if (creds == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ cli_credentials_set_conf(creds, service->task->lp_ctx);
+
+ /* Connect the machine account to the credentials */
+ status = cli_credentials_set_machine_account(creds, service->task->lp_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(creds);
+ return status;
+ }
+
+ ctx = wb_cmd_pam_auth_send(s3call, service, creds);
+
+ if (!ctx) {
+ talloc_free(creds);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ctx->async.fn = check_machacc_recv;
+ ctx->async.private_data = s3call;
+ s3call->flags |= WBSRV_CALL_FLAGS_REPLY_ASYNC;
+ return NT_STATUS_OK;
+}
+
+static void check_machacc_recv(struct composite_context *ctx)
+{
+ struct wbsrv_samba3_call *s3call =
+ talloc_get_type(ctx->async.private_data,
+ struct wbsrv_samba3_call);
+ NTSTATUS status;
+
+ status = wb_cmd_pam_auth_recv(ctx);
+
+ if (!NT_STATUS_IS_OK(status)) goto done;
+
+ done:
+ wbsrv_samba3_async_auth_epilogue(status, s3call);
+}
+
+/*
+ Find the name of a suitable domain controller, by query on the
+ netlogon pipe to the DC.
+*/
+
+static void getdcname_recv_dc(struct composite_context *ctx);
+
+NTSTATUS wbsrv_samba3_getdcname(struct wbsrv_samba3_call *s3call)
+{
+ struct composite_context *ctx;
+ struct wbsrv_service *service =
+ s3call->wbconn->listen_socket->service;
+
+ DEBUG(5, ("wbsrv_samba3_getdcname called\n"));
+
+ ctx = wb_cmd_getdcname_send(s3call, service,
+ s3call->request.domain_name);
+ NT_STATUS_HAVE_NO_MEMORY(ctx);
+
+ ctx->async.fn = getdcname_recv_dc;
+ ctx->async.private_data = s3call;
+ s3call->flags |= WBSRV_CALL_FLAGS_REPLY_ASYNC;
+ return NT_STATUS_OK;
+}
+
+static void getdcname_recv_dc(struct composite_context *ctx)
+{
+ struct wbsrv_samba3_call *s3call =
+ talloc_get_type(ctx->async.private_data,
+ struct wbsrv_samba3_call);
+ const char *dcname;
+ NTSTATUS status;
+
+ status = wb_cmd_getdcname_recv(ctx, s3call, &dcname);
+ if (!NT_STATUS_IS_OK(status)) goto done;
+
+ s3call->response.result = WINBINDD_OK;
+ WBSRV_SAMBA3_SET_STRING(s3call->response.data.dc_name, dcname);
+
+ done:
+ wbsrv_samba3_async_epilogue(status, s3call);
+}
+
+/*
+ Lookup a user's domain groups
+*/
+
+static void userdomgroups_recv_groups(struct composite_context *ctx);
+
+NTSTATUS wbsrv_samba3_userdomgroups(struct wbsrv_samba3_call *s3call)
+{
+ struct composite_context *ctx;
+ struct dom_sid *sid;
+
+ DEBUG(5, ("wbsrv_samba3_userdomgroups called\n"));
+
+ sid = dom_sid_parse_talloc(s3call, s3call->request.data.sid);
+ if (sid == NULL) {
+ DEBUG(5, ("Could not parse sid %s\n",
+ s3call->request.data.sid));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ctx = wb_cmd_userdomgroups_send(
+ s3call, s3call->wbconn->listen_socket->service, sid);
+ NT_STATUS_HAVE_NO_MEMORY(ctx);
+
+ ctx->async.fn = userdomgroups_recv_groups;
+ ctx->async.private_data = s3call;
+ s3call->flags |= WBSRV_CALL_FLAGS_REPLY_ASYNC;
+ return NT_STATUS_OK;
+}
+
+static void userdomgroups_recv_groups(struct composite_context *ctx)
+{
+ struct wbsrv_samba3_call *s3call =
+ talloc_get_type(ctx->async.private_data,
+ struct wbsrv_samba3_call);
+ int i, num_sids;
+ struct dom_sid **sids;
+ char *sids_string;
+ NTSTATUS status;
+
+ status = wb_cmd_userdomgroups_recv(ctx, s3call, &num_sids, &sids);
+ if (!NT_STATUS_IS_OK(status)) goto done;
+
+ sids_string = talloc_strdup(s3call, "");
+ if (sids_string == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ for (i=0; i<num_sids; i++) {
+ sids_string = talloc_asprintf_append_buffer(
+ sids_string, "%s\n", dom_sid_string(s3call, sids[i]));
+ }
+
+ if (sids_string == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ s3call->response.result = WINBINDD_OK;
+ s3call->response.extra_data.data = sids_string;
+ s3call->response.length += strlen(sids_string)+1;
+ s3call->response.data.num_entries = num_sids;
+
+ done:
+ wbsrv_samba3_async_epilogue(status, s3call);
+}
+
+/*
+ Lookup the list of SIDs for a user
+*/
+static void usersids_recv_sids(struct composite_context *ctx);
+
+NTSTATUS wbsrv_samba3_usersids(struct wbsrv_samba3_call *s3call)
+{
+ struct composite_context *ctx;
+ struct dom_sid *sid;
+
+ DEBUG(5, ("wbsrv_samba3_usersids called\n"));
+
+ sid = dom_sid_parse_talloc(s3call, s3call->request.data.sid);
+ if (sid == NULL) {
+ DEBUG(5, ("Could not parse sid %s\n",
+ s3call->request.data.sid));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ctx = wb_cmd_usersids_send(
+ s3call, s3call->wbconn->listen_socket->service, sid);
+ NT_STATUS_HAVE_NO_MEMORY(ctx);
+
+ ctx->async.fn = usersids_recv_sids;
+ ctx->async.private_data = s3call;
+ s3call->flags |= WBSRV_CALL_FLAGS_REPLY_ASYNC;
+ return NT_STATUS_OK;
+}
+
+static void usersids_recv_sids(struct composite_context *ctx)
+{
+ struct wbsrv_samba3_call *s3call =
+ talloc_get_type(ctx->async.private_data,
+ struct wbsrv_samba3_call);
+ int i, num_sids;
+ struct dom_sid **sids;
+ char *sids_string;
+ NTSTATUS status;
+
+ status = wb_cmd_usersids_recv(ctx, s3call, &num_sids, &sids);
+ if (!NT_STATUS_IS_OK(status)) goto done;
+
+ sids_string = talloc_strdup(s3call, "");
+ if (sids_string == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ for (i=0; i<num_sids; i++) {
+ sids_string = talloc_asprintf_append_buffer(
+ sids_string, "%s\n", dom_sid_string(s3call, sids[i]));
+ if (sids_string == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ }
+
+ s3call->response.result = WINBINDD_OK;
+ s3call->response.extra_data.data = sids_string;
+ s3call->response.length += strlen(sids_string);
+ s3call->response.data.num_entries = num_sids;
+
+ /* Hmmmm. Nasty protocol -- who invented the zeros between the
+ * SIDs? Hmmm. Could have been me -- vl */
+
+ while (*sids_string != '\0') {
+ if ((*sids_string) == '\n') {
+ *sids_string = '\0';
+ }
+ sids_string += 1;
+ }
+
+ done:
+ wbsrv_samba3_async_epilogue(status, s3call);
+}
+
+/*
+ Lookup a DOMAIN\\user style name, and return a SID
+*/
+
+static void lookupname_recv_sid(struct composite_context *ctx);
+
+NTSTATUS wbsrv_samba3_lookupname(struct wbsrv_samba3_call *s3call)
+{
+ struct composite_context *ctx;
+ struct wbsrv_service *service =
+ s3call->wbconn->listen_socket->service;
+
+ DEBUG(5, ("wbsrv_samba3_lookupname called\n"));
+
+ ctx = wb_cmd_lookupname_send(s3call, service,
+ s3call->request.data.name.dom_name,
+ s3call->request.data.name.name);
+ NT_STATUS_HAVE_NO_MEMORY(ctx);
+
+ /* setup the callbacks */
+ ctx->async.fn = lookupname_recv_sid;
+ ctx->async.private_data = s3call;
+ s3call->flags |= WBSRV_CALL_FLAGS_REPLY_ASYNC;
+ return NT_STATUS_OK;
+}
+
+static void lookupname_recv_sid(struct composite_context *ctx)
+{
+ struct wbsrv_samba3_call *s3call =
+ talloc_get_type(ctx->async.private_data,
+ struct wbsrv_samba3_call);
+ struct wb_sid_object *sid;
+ NTSTATUS status;
+
+ status = wb_cmd_lookupname_recv(ctx, s3call, &sid);
+ if (!NT_STATUS_IS_OK(status)) goto done;
+
+ s3call->response.result = WINBINDD_OK;
+ s3call->response.data.sid.type = sid->type;
+ WBSRV_SAMBA3_SET_STRING(s3call->response.data.sid.sid,
+ dom_sid_string(s3call, sid->sid));
+
+ done:
+ wbsrv_samba3_async_epilogue(status, s3call);
+}
+
+/*
+ Lookup a SID, and return a DOMAIN\\user style name
+*/
+
+static void lookupsid_recv_name(struct composite_context *ctx);
+
+NTSTATUS wbsrv_samba3_lookupsid(struct wbsrv_samba3_call *s3call)
+{
+ struct composite_context *ctx;
+ struct wbsrv_service *service =
+ s3call->wbconn->listen_socket->service;
+ struct dom_sid *sid;
+
+ DEBUG(5, ("wbsrv_samba3_lookupsid called\n"));
+
+ sid = dom_sid_parse_talloc(s3call, s3call->request.data.sid);
+ if (sid == NULL) {
+ DEBUG(5, ("Could not parse sid %s\n",
+ s3call->request.data.sid));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ctx = wb_cmd_lookupsid_send(s3call, service, sid);
+ NT_STATUS_HAVE_NO_MEMORY(ctx);
+
+ /* setup the callbacks */
+ ctx->async.fn = lookupsid_recv_name;
+ ctx->async.private_data = s3call;
+ s3call->flags |= WBSRV_CALL_FLAGS_REPLY_ASYNC;
+ return NT_STATUS_OK;
+}
+
+static void lookupsid_recv_name(struct composite_context *ctx)
+{
+ struct wbsrv_samba3_call *s3call =
+ talloc_get_type(ctx->async.private_data,
+ struct wbsrv_samba3_call);
+ struct wb_sid_object *sid;
+ NTSTATUS status;
+
+ status = wb_cmd_lookupsid_recv(ctx, s3call, &sid);
+ if (!NT_STATUS_IS_OK(status)) goto done;
+
+ s3call->response.result = WINBINDD_OK;
+ s3call->response.data.name.type = sid->type;
+ WBSRV_SAMBA3_SET_STRING(s3call->response.data.name.dom_name,
+ sid->domain);
+ WBSRV_SAMBA3_SET_STRING(s3call->response.data.name.name, sid->name);
+
+ done:
+ wbsrv_samba3_async_epilogue(status, s3call);
+}
+
+/*
+ Challenge-response authentication. This interface is used by
+ ntlm_auth and the smbd auth subsystem to pass NTLM authentication
+ requests along a common pipe to the domain controller.
+
+ The return value (in the async reply) may include the 'info3'
+ (effectivly most things you would want to know about the user), or
+ the NT and LM session keys seperated.
+*/
+
+static void pam_auth_crap_recv(struct composite_context *ctx);
+
+NTSTATUS wbsrv_samba3_pam_auth_crap(struct wbsrv_samba3_call *s3call)
+{
+ struct composite_context *ctx;
+ struct wbsrv_service *service =
+ s3call->wbconn->listen_socket->service;
+ DATA_BLOB chal, nt_resp, lm_resp;
+
+ DEBUG(5, ("wbsrv_samba3_pam_auth_crap called\n"));
+
+ chal.data = s3call->request.data.auth_crap.chal;
+ chal.length = sizeof(s3call->request.data.auth_crap.chal);
+ nt_resp.data = (uint8_t *)s3call->request.data.auth_crap.nt_resp;
+ nt_resp.length = s3call->request.data.auth_crap.nt_resp_len;
+ lm_resp.data = (uint8_t *)s3call->request.data.auth_crap.lm_resp;
+ lm_resp.length = s3call->request.data.auth_crap.lm_resp_len;
+
+ ctx = wb_cmd_pam_auth_crap_send(
+ s3call, service,
+ s3call->request.data.auth_crap.logon_parameters,
+ s3call->request.data.auth_crap.domain,
+ s3call->request.data.auth_crap.user,
+ s3call->request.data.auth_crap.workstation,
+ chal, nt_resp, lm_resp);
+ NT_STATUS_HAVE_NO_MEMORY(ctx);
+
+ ctx->async.fn = pam_auth_crap_recv;
+ ctx->async.private_data = s3call;
+ s3call->flags |= WBSRV_CALL_FLAGS_REPLY_ASYNC;
+ return NT_STATUS_OK;
+}
+
+static void pam_auth_crap_recv(struct composite_context *ctx)
+{
+ struct wbsrv_samba3_call *s3call =
+ talloc_get_type(ctx->async.private_data,
+ struct wbsrv_samba3_call);
+ NTSTATUS status;
+ DATA_BLOB info3;
+ struct netr_UserSessionKey user_session_key;
+ struct netr_LMSessionKey lm_key;
+ char *unix_username;
+
+ status = wb_cmd_pam_auth_crap_recv(ctx, s3call, &info3,
+ &user_session_key, &lm_key, &unix_username);
+ if (!NT_STATUS_IS_OK(status)) goto done;
+
+ if (s3call->request.flags & WBFLAG_PAM_USER_SESSION_KEY) {
+ memcpy(s3call->response.data.auth.user_session_key,
+ &user_session_key.key,
+ sizeof(s3call->response.data.auth.user_session_key));
+ }
+
+ if (s3call->request.flags & WBFLAG_PAM_INFO3_NDR) {
+ s3call->response.extra_data.data = info3.data;
+ s3call->response.length += info3.length;
+ }
+
+ if (s3call->request.flags & WBFLAG_PAM_LMKEY) {
+ memcpy(s3call->response.data.auth.first_8_lm_hash,
+ lm_key.key,
+ sizeof(s3call->response.data.auth.first_8_lm_hash));
+ }
+
+ if (s3call->request.flags & WBFLAG_PAM_UNIX_NAME) {
+ s3call->response.extra_data.data = unix_username;
+ s3call->response.length += strlen(unix_username)+1;
+ }
+
+ done:
+ wbsrv_samba3_async_auth_epilogue(status, s3call);
+}
+
+/* Plaintext authentication
+
+ This interface is used by ntlm_auth in it's 'basic' authentication
+ mode, as well as by pam_winbind to authenticate users where we are
+ given a plaintext password.
+*/
+
+static void pam_auth_recv(struct composite_context *ctx);
+
+NTSTATUS wbsrv_samba3_pam_auth(struct wbsrv_samba3_call *s3call)
+{
+ struct composite_context *ctx;
+ struct wbsrv_service *service =
+ s3call->wbconn->listen_socket->service;
+ struct cli_credentials *credentials;
+ char *user, *domain;
+
+ if (!wb_samba3_split_username(s3call, s3call->wbconn->lp_ctx,
+ s3call->request.data.auth.user,
+ &domain, &user)) {
+ return NT_STATUS_NO_SUCH_USER;
+ }
+
+ credentials = cli_credentials_init(s3call);
+ if (!credentials) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ cli_credentials_set_conf(credentials, service->task->lp_ctx);
+ cli_credentials_set_domain(credentials, domain, CRED_SPECIFIED);
+ cli_credentials_set_username(credentials, user, CRED_SPECIFIED);
+
+ cli_credentials_set_password(credentials, s3call->request.data.auth.pass, CRED_SPECIFIED);
+
+ ctx = wb_cmd_pam_auth_send(s3call, service, credentials);
+ NT_STATUS_HAVE_NO_MEMORY(ctx);
+
+ ctx->async.fn = pam_auth_recv;
+ ctx->async.private_data = s3call;
+ s3call->flags |= WBSRV_CALL_FLAGS_REPLY_ASYNC;
+ return NT_STATUS_OK;
+}
+
+static void pam_auth_recv(struct composite_context *ctx)
+{
+ struct wbsrv_samba3_call *s3call =
+ talloc_get_type(ctx->async.private_data,
+ struct wbsrv_samba3_call);
+ NTSTATUS status;
+
+ status = wb_cmd_pam_auth_recv(ctx);
+
+ if (!NT_STATUS_IS_OK(status)) goto done;
+
+ done:
+ wbsrv_samba3_async_auth_epilogue(status, s3call);
+}
+
+/*
+ List trusted domains
+*/
+
+static void list_trustdom_recv_doms(struct composite_context *ctx);
+
+NTSTATUS wbsrv_samba3_list_trustdom(struct wbsrv_samba3_call *s3call)
+{
+ struct composite_context *ctx;
+ struct wbsrv_service *service =
+ s3call->wbconn->listen_socket->service;
+
+ DEBUG(5, ("wbsrv_samba3_list_trustdom called\n"));
+
+ ctx = wb_cmd_list_trustdoms_send(s3call, service);
+ NT_STATUS_HAVE_NO_MEMORY(ctx);
+
+ ctx->async.fn = list_trustdom_recv_doms;
+ ctx->async.private_data = s3call;
+ s3call->flags |= WBSRV_CALL_FLAGS_REPLY_ASYNC;
+ return NT_STATUS_OK;
+}
+
+static void list_trustdom_recv_doms(struct composite_context *ctx)
+{
+ struct wbsrv_samba3_call *s3call =
+ talloc_get_type(ctx->async.private_data,
+ struct wbsrv_samba3_call);
+ int i, num_domains;
+ struct wb_dom_info **domains;
+ NTSTATUS status;
+ char *result;
+
+ status = wb_cmd_list_trustdoms_recv(ctx, s3call, &num_domains,
+ &domains);
+ if (!NT_STATUS_IS_OK(status)) goto done;
+
+ result = talloc_strdup(s3call, "");
+ if (result == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ for (i=0; i<num_domains; i++) {
+ result = talloc_asprintf_append_buffer(
+ result, "%s\\%s\\%s",
+ domains[i]->name, domains[i]->name,
+ dom_sid_string(s3call, domains[i]->sid));
+ }
+
+ if (result == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ s3call->response.result = WINBINDD_OK;
+ if (num_domains > 0) {
+ s3call->response.extra_data.data = result;
+ s3call->response.length += strlen(result)+1;
+ }
+
+ done:
+ wbsrv_samba3_async_epilogue(status, s3call);
+}
+
+/* list groups */
+static void list_groups_recv(struct composite_context *ctx);
+
+NTSTATUS wbsrv_samba3_list_groups(struct wbsrv_samba3_call *s3call)
+{
+ struct composite_context *ctx;
+ struct wbsrv_service *service = s3call->wbconn->listen_socket->service;
+
+ DEBUG(5, ("wbsrv_samba4_list_groups called\n"));
+
+ ctx = wb_cmd_list_groups_send(s3call, service,
+ s3call->request.domain_name);
+ NT_STATUS_HAVE_NO_MEMORY(ctx);
+
+ ctx->async.fn = list_groups_recv;
+ ctx->async.private_data = s3call;
+ s3call->flags |= WBSRV_CALL_FLAGS_REPLY_ASYNC;
+ return NT_STATUS_OK;
+}
+
+static void list_groups_recv(struct composite_context *ctx)
+{
+ struct wbsrv_samba3_call *s3call = talloc_get_type_abort(
+ ctx->async.private_data,
+ struct wbsrv_samba3_call);
+ uint32_t extra_data_len;
+ char *extra_data;
+ NTSTATUS status;
+
+ DEBUG(5, ("list_groups_recv called\n"));
+
+ status = wb_cmd_list_groups_recv(ctx, s3call, &extra_data_len,
+ &extra_data);
+
+ if (NT_STATUS_IS_OK(status)) {
+ s3call->response.extra_data.data = extra_data;
+ s3call->response.length += extra_data_len;
+ if (extra_data) {
+ s3call->response.length += 1;
+ }
+ }
+
+ wbsrv_samba3_async_epilogue(status, s3call);
+}
+
+/* List users */
+
+static void list_users_recv(struct composite_context *ctx);
+
+NTSTATUS wbsrv_samba3_list_users(struct wbsrv_samba3_call *s3call)
+{
+ struct composite_context *ctx;
+ struct wbsrv_service *service =
+ s3call->wbconn->listen_socket->service;
+
+ DEBUG(5, ("wbsrv_samba3_list_users called\n"));
+
+ ctx = wb_cmd_list_users_send(s3call, service,
+ s3call->request.domain_name);
+ NT_STATUS_HAVE_NO_MEMORY(ctx);
+
+ ctx->async.fn = list_users_recv;
+ ctx->async.private_data = s3call;
+ s3call->flags |= WBSRV_CALL_FLAGS_REPLY_ASYNC;
+ return NT_STATUS_OK;
+}
+
+static void list_users_recv(struct composite_context *ctx)
+{
+ struct wbsrv_samba3_call *s3call =
+ talloc_get_type(ctx->async.private_data,
+ struct wbsrv_samba3_call);
+ uint32_t extra_data_len;
+ char *extra_data;
+ NTSTATUS status;
+
+ DEBUG(5, ("list_users_recv called\n"));
+
+ status = wb_cmd_list_users_recv(ctx, s3call, &extra_data_len,
+ &extra_data);
+
+ if (NT_STATUS_IS_OK(status)) {
+ s3call->response.extra_data.data = extra_data;
+ s3call->response.length += extra_data_len;
+ if (extra_data) {
+ s3call->response.length += 1;
+ }
+ }
+
+ wbsrv_samba3_async_epilogue(status, s3call);
+}
+
+/* NSS calls */
+
+static void getpwnam_recv(struct composite_context *ctx);
+
+NTSTATUS wbsrv_samba3_getpwnam(struct wbsrv_samba3_call *s3call)
+{
+ struct composite_context *ctx;
+ struct wbsrv_service *service =
+ s3call->wbconn->listen_socket->service;
+
+ DEBUG(5, ("wbsrv_samba3_getpwnam called\n"));
+
+ ctx = wb_cmd_getpwnam_send(s3call, service,
+ s3call->request.data.username);
+ NT_STATUS_HAVE_NO_MEMORY(ctx);
+
+ ctx->async.fn = getpwnam_recv;
+ ctx->async.private_data = s3call;
+ s3call->flags |= WBSRV_CALL_FLAGS_REPLY_ASYNC;
+ return NT_STATUS_OK;
+}
+
+static void getpwnam_recv(struct composite_context *ctx)
+{
+ struct wbsrv_samba3_call *s3call =
+ talloc_get_type(ctx->async.private_data,
+ struct wbsrv_samba3_call);
+ NTSTATUS status;
+ struct winbindd_pw *pw;
+
+ DEBUG(5, ("getpwnam_recv called\n"));
+
+ status = wb_cmd_getpwnam_recv(ctx, s3call, &pw);
+ if(NT_STATUS_IS_OK(status))
+ s3call->response.data.pw = *pw;
+
+ wbsrv_samba3_async_epilogue(status, s3call);
+}
+
+static void getpwuid_recv(struct composite_context *ctx);
+
+NTSTATUS wbsrv_samba3_getpwuid(struct wbsrv_samba3_call *s3call)
+{
+ struct composite_context *ctx;
+ struct wbsrv_service *service = s3call->wbconn->listen_socket->service;
+
+ DEBUG(5, ("wbsrv_samba3_getpwuid called\n"));
+
+ ctx = wb_cmd_getpwuid_send(s3call, service,
+ s3call->request.data.uid);
+ NT_STATUS_HAVE_NO_MEMORY(ctx);
+
+ ctx->async.fn = getpwuid_recv;
+ ctx->async.private_data = s3call;
+ s3call->flags |= WBSRV_CALL_FLAGS_REPLY_ASYNC;
+ return NT_STATUS_OK;
+}
+
+static void getpwuid_recv(struct composite_context *ctx)
+{
+ struct wbsrv_samba3_call *s3call =
+ talloc_get_type(ctx->async.private_data,
+ struct wbsrv_samba3_call);
+ NTSTATUS status;
+ struct winbindd_pw *pw;
+
+ DEBUG(5, ("getpwuid_recv called\n"));
+
+ status = wb_cmd_getpwuid_recv(ctx, s3call, &pw);
+ if (NT_STATUS_IS_OK(status))
+ s3call->response.data.pw = *pw;
+
+ wbsrv_samba3_async_epilogue(status, s3call);
+}
+
+static void setpwent_recv(struct composite_context *ctx);
+
+NTSTATUS wbsrv_samba3_setpwent(struct wbsrv_samba3_call *s3call)
+{
+ struct composite_context *ctx;
+ struct wbsrv_service *service = s3call->wbconn->listen_socket->service;
+
+ DEBUG(5, ("wbsrv_samba3_setpwent called\n"));
+
+ ctx = wb_cmd_setpwent_send(s3call, service);
+ NT_STATUS_HAVE_NO_MEMORY(ctx);
+
+ ctx->async.fn = setpwent_recv;
+ ctx->async.private_data = s3call;
+ s3call->flags |= WBSRV_CALL_FLAGS_REPLY_ASYNC;
+ return NT_STATUS_OK;
+}
+
+static void setpwent_recv(struct composite_context *ctx)
+{
+ struct wbsrv_samba3_call *s3call =
+ talloc_get_type(ctx->async.private_data,
+ struct wbsrv_samba3_call);
+ NTSTATUS status;
+ struct wbsrv_pwent *pwent;
+
+ DEBUG(5, ("setpwent_recv called\n"));
+
+ status = wb_cmd_setpwent_recv(ctx, s3call->wbconn, &pwent);
+ if (NT_STATUS_IS_OK(status)) {
+ s3call->wbconn->protocol_private_data = pwent;
+ }
+
+ wbsrv_samba3_async_epilogue(status, s3call);
+}
+
+static void getpwent_recv(struct composite_context *ctx);
+
+NTSTATUS wbsrv_samba3_getpwent(struct wbsrv_samba3_call *s3call)
+{
+ struct composite_context *ctx;
+ struct wbsrv_service *service = s3call->wbconn->listen_socket->service;
+ struct wbsrv_pwent *pwent;
+
+ DEBUG(5, ("wbsrv_samba3_getpwent called\n"));
+
+ NT_STATUS_HAVE_NO_MEMORY(s3call->wbconn->protocol_private_data);
+
+ pwent = talloc_get_type(s3call->wbconn->protocol_private_data,
+ struct wbsrv_pwent);
+ NT_STATUS_HAVE_NO_MEMORY(pwent);
+
+ ctx = wb_cmd_getpwent_send(s3call, service, pwent,
+ s3call->request.data.num_entries);
+ NT_STATUS_HAVE_NO_MEMORY(ctx);
+
+ ctx->async.fn = getpwent_recv;
+ ctx->async.private_data = s3call;
+ s3call->flags |= WBSRV_CALL_FLAGS_REPLY_ASYNC;
+ return NT_STATUS_OK;
+}
+
+static void getpwent_recv(struct composite_context *ctx)
+{
+ struct wbsrv_samba3_call *s3call =
+ talloc_get_type(ctx->async.private_data,
+ struct wbsrv_samba3_call);
+ NTSTATUS status;
+ struct winbindd_pw *pw;
+ uint32_t num_users;
+
+ DEBUG(5, ("getpwent_recv called\n"));
+
+ status = wb_cmd_getpwent_recv(ctx, s3call, &pw, &num_users);
+ if (NT_STATUS_IS_OK(status)) {
+ uint32_t extra_len = sizeof(struct winbindd_pw) * num_users;
+
+ s3call->response.data.num_entries = num_users;
+ s3call->response.extra_data.data = pw;
+ s3call->response.length += extra_len;
+ }
+
+ wbsrv_samba3_async_epilogue(status, s3call);
+}
+
+NTSTATUS wbsrv_samba3_endpwent(struct wbsrv_samba3_call *s3call)
+{
+ struct wbsrv_pwent *pwent =
+ talloc_get_type(s3call->wbconn->protocol_private_data,
+ struct wbsrv_pwent);
+ DEBUG(5, ("wbsrv_samba3_endpwent called\n"));
+
+ talloc_free(pwent);
+
+ s3call->wbconn->protocol_private_data = NULL;
+ s3call->response.result = WINBINDD_OK;
+ return NT_STATUS_OK;
+}
+
+
+static void getgrnam_recv(struct composite_context *ctx);
+
+NTSTATUS wbsrv_samba3_getgrnam(struct wbsrv_samba3_call *s3call)
+{
+ struct composite_context *ctx;
+ struct wbsrv_service *service =
+ s3call->wbconn->listen_socket->service;
+
+ DEBUG(5, ("wbsrv_samba3_getgrnam called\n"));
+
+ ctx = wb_cmd_getgrnam_send(s3call, service,
+ s3call->request.data.groupname);
+ NT_STATUS_HAVE_NO_MEMORY(ctx);
+
+ ctx->async.fn = getgrnam_recv;
+ ctx->async.private_data = s3call;
+ s3call->flags |= WBSRV_CALL_FLAGS_REPLY_ASYNC;
+ return NT_STATUS_OK;
+}
+
+static void getgrnam_recv(struct composite_context *ctx)
+{
+ struct wbsrv_samba3_call *s3call =
+ talloc_get_type(ctx->async.private_data,
+ struct wbsrv_samba3_call);
+ NTSTATUS status;
+ struct winbindd_gr *gr;
+
+ DEBUG(5, ("getgrnam_recv called\n"));
+
+ status = wb_cmd_getgrnam_recv(ctx, s3call, &gr);
+ if(NT_STATUS_IS_OK(status))
+ s3call->response.data.gr = *gr;
+
+ wbsrv_samba3_async_epilogue(status, s3call);
+}
+
+static void getgrgid_recv(struct composite_context *ctx);
+
+NTSTATUS wbsrv_samba3_getgrgid(struct wbsrv_samba3_call *s3call)
+{
+ struct composite_context *ctx;
+ struct wbsrv_service *service = s3call->wbconn->listen_socket->service;
+
+ DEBUG(5, ("wbsrv_samba3_getgrgid called\n"));
+
+ ctx = wb_cmd_getgrgid_send(s3call, service,
+ s3call->request.data.gid);
+ NT_STATUS_HAVE_NO_MEMORY(ctx);
+
+ ctx->async.fn = getgrgid_recv;
+ ctx->async.private_data = s3call;
+ s3call->flags |= WBSRV_CALL_FLAGS_REPLY_ASYNC;
+ return NT_STATUS_OK;
+}
+
+static void getgrgid_recv(struct composite_context *ctx)
+{
+ struct wbsrv_samba3_call *s3call =
+ talloc_get_type(ctx->async.private_data,
+ struct wbsrv_samba3_call);
+ NTSTATUS status;
+ struct winbindd_gr *gr;
+
+ DEBUG(5, ("getgrgid_recv called\n"));
+
+ status = wb_cmd_getgrgid_recv(ctx, s3call, &gr);
+ if (NT_STATUS_IS_OK(status))
+ s3call->response.data.gr = *gr;
+
+ wbsrv_samba3_async_epilogue(status, s3call);
+}
+
+NTSTATUS wbsrv_samba3_getgroups(struct wbsrv_samba3_call *s3call)
+{
+ DEBUG(5, ("wbsrv_samba3_getgroups called\n"));
+ s3call->response.result = WINBINDD_ERROR;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS wbsrv_samba3_setgrent(struct wbsrv_samba3_call *s3call)
+{
+ DEBUG(5, ("wbsrv_samba3_setgrent called\n"));
+ s3call->response.result = WINBINDD_OK;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS wbsrv_samba3_getgrent(struct wbsrv_samba3_call *s3call)
+{
+ DEBUG(5, ("wbsrv_samba3_getgrent called\n"));
+ s3call->response.result = WINBINDD_ERROR;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS wbsrv_samba3_endgrent(struct wbsrv_samba3_call *s3call)
+{
+ DEBUG(5, ("wbsrv_samba3_endgrent called\n"));
+ s3call->response.result = WINBINDD_OK;
+ return NT_STATUS_OK;
+}
+
+static void sid2uid_recv(struct composite_context *ctx);
+
+NTSTATUS wbsrv_samba3_sid2uid(struct wbsrv_samba3_call *s3call)
+{
+ struct composite_context *ctx;
+ struct wbsrv_service *service =
+ s3call->wbconn->listen_socket->service;
+ struct dom_sid *sid;
+
+ DEBUG(5, ("wbsrv_samba3_sid2uid called\n"));
+
+ sid = dom_sid_parse_talloc(s3call, s3call->request.data.sid);
+ NT_STATUS_HAVE_NO_MEMORY(sid);
+
+ ctx = wb_sid2uid_send(s3call, service, sid);
+ NT_STATUS_HAVE_NO_MEMORY(ctx);
+
+ ctx->async.fn = sid2uid_recv;
+ ctx->async.private_data = s3call;
+ s3call->flags |= WBSRV_CALL_FLAGS_REPLY_ASYNC;
+ return NT_STATUS_OK;
+
+}
+
+static void sid2uid_recv(struct composite_context *ctx)
+{
+ struct wbsrv_samba3_call *s3call =
+ talloc_get_type(ctx->async.private_data,
+ struct wbsrv_samba3_call);
+ NTSTATUS status;
+
+ DEBUG(5, ("sid2uid_recv called\n"));
+
+ status = wb_sid2uid_recv(ctx, &s3call->response.data.uid);
+
+ wbsrv_samba3_async_epilogue(status, s3call);
+}
+
+static void sid2gid_recv(struct composite_context *ctx);
+
+NTSTATUS wbsrv_samba3_sid2gid(struct wbsrv_samba3_call *s3call)
+{
+ struct composite_context *ctx;
+ struct wbsrv_service *service =
+ s3call->wbconn->listen_socket->service;
+ struct dom_sid *sid;
+
+ DEBUG(5, ("wbsrv_samba3_sid2gid called\n"));
+
+ sid = dom_sid_parse_talloc(s3call, s3call->request.data.sid);
+ NT_STATUS_HAVE_NO_MEMORY(sid);
+
+ ctx = wb_sid2gid_send(s3call, service, sid);
+ NT_STATUS_HAVE_NO_MEMORY(ctx);
+
+ ctx->async.fn = sid2gid_recv;
+ ctx->async.private_data = s3call;
+ s3call->flags |= WBSRV_CALL_FLAGS_REPLY_ASYNC;
+ return NT_STATUS_OK;
+
+}
+
+static void sid2gid_recv(struct composite_context *ctx)
+{
+ struct wbsrv_samba3_call *s3call =
+ talloc_get_type(ctx->async.private_data,
+ struct wbsrv_samba3_call);
+ NTSTATUS status;
+
+ DEBUG(5, ("sid2gid_recv called\n"));
+
+ status = wb_sid2gid_recv(ctx, &s3call->response.data.gid);
+
+ wbsrv_samba3_async_epilogue(status, s3call);
+}
+
+static void uid2sid_recv(struct composite_context *ctx);
+
+NTSTATUS wbsrv_samba3_uid2sid(struct wbsrv_samba3_call *s3call)
+{
+ struct composite_context *ctx;
+ struct wbsrv_service *service =
+ s3call->wbconn->listen_socket->service;
+
+ DEBUG(5, ("wbsrv_samba3_uid2sid called\n"));
+
+ ctx = wb_uid2sid_send(s3call, service, s3call->request.data.uid);
+ NT_STATUS_HAVE_NO_MEMORY(ctx);
+
+ ctx->async.fn = uid2sid_recv;
+ ctx->async.private_data = s3call;
+ s3call->flags |= WBSRV_CALL_FLAGS_REPLY_ASYNC;
+ return NT_STATUS_OK;
+
+}
+
+static void uid2sid_recv(struct composite_context *ctx)
+{
+ struct wbsrv_samba3_call *s3call =
+ talloc_get_type(ctx->async.private_data,
+ struct wbsrv_samba3_call);
+ NTSTATUS status;
+ struct dom_sid *sid;
+ char *sid_str;
+
+ DEBUG(5, ("uid2sid_recv called\n"));
+
+ status = wb_uid2sid_recv(ctx, s3call, &sid);
+ if(NT_STATUS_IS_OK(status)) {
+ sid_str = dom_sid_string(s3call, sid);
+
+ /* If the conversion failed, bail out with a failure. */
+ if (sid_str == NULL)
+ wbsrv_samba3_async_epilogue(NT_STATUS_NO_MEMORY,s3call);
+
+ /* But we assume this worked, so we'll set the string. Work
+ * done. */
+ WBSRV_SAMBA3_SET_STRING(s3call->response.data.sid.sid, sid_str);
+ s3call->response.data.sid.type = SID_NAME_USER;
+ }
+
+ wbsrv_samba3_async_epilogue(status, s3call);
+}
+
+static void gid2sid_recv(struct composite_context *ctx);
+
+NTSTATUS wbsrv_samba3_gid2sid(struct wbsrv_samba3_call *s3call)
+{
+ struct composite_context *ctx;
+ struct wbsrv_service *service =
+ s3call->wbconn->listen_socket->service;
+
+ DEBUG(5, ("wbsrv_samba3_gid2sid called\n"));
+
+ ctx = wb_gid2sid_send(s3call, service, s3call->request.data.gid);
+ NT_STATUS_HAVE_NO_MEMORY(ctx);
+
+ ctx->async.fn = gid2sid_recv;
+ ctx->async.private_data = s3call;
+ s3call->flags |= WBSRV_CALL_FLAGS_REPLY_ASYNC;
+ return NT_STATUS_OK;
+
+}
+
+static void gid2sid_recv(struct composite_context *ctx)
+{
+ struct wbsrv_samba3_call *s3call =
+ talloc_get_type(ctx->async.private_data,
+ struct wbsrv_samba3_call);
+ NTSTATUS status;
+ struct dom_sid *sid;
+ char *sid_str;
+
+ DEBUG(5, ("gid2sid_recv called\n"));
+
+ status = wb_gid2sid_recv(ctx, s3call, &sid);
+ if(NT_STATUS_IS_OK(status)) {
+ sid_str = dom_sid_string(s3call, sid);
+
+ if (sid_str == NULL)
+ wbsrv_samba3_async_epilogue(NT_STATUS_NO_MEMORY,s3call);
+
+ WBSRV_SAMBA3_SET_STRING(s3call->response.data.sid.sid, sid_str);
+ s3call->response.data.sid.type = SID_NAME_DOMAIN;
+ }
+
+ wbsrv_samba3_async_epilogue(status, s3call);
+}
+
diff --git a/source4/winbind/wb_samba3_protocol.c b/source4/winbind/wb_samba3_protocol.c
new file mode 100644
index 0000000000..77c5bf3a1e
--- /dev/null
+++ b/source4/winbind/wb_samba3_protocol.c
@@ -0,0 +1,299 @@
+/*
+ Unix SMB/CIFS implementation.
+ Main winbindd samba3 server routines
+
+ Copyright (C) Stefan Metzmacher 2005
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbind/wb_server.h"
+#include "smbd/service_stream.h"
+#include "lib/stream/packet.h"
+
+/*
+ work out if a packet is complete for protocols that use a 32 bit host byte
+ order length
+*/
+NTSTATUS wbsrv_samba3_packet_full_request(void *private_data, DATA_BLOB blob, size_t *size)
+{
+ uint32_t *len;
+ if (blob.length < 4) {
+ return STATUS_MORE_ENTRIES;
+ }
+ len = (uint32_t *)blob.data;
+ *size = (*len);
+ if (*size > blob.length) {
+ return STATUS_MORE_ENTRIES;
+ }
+ return NT_STATUS_OK;
+}
+
+
+NTSTATUS wbsrv_samba3_pull_request(DATA_BLOB blob, struct wbsrv_connection *wbconn,
+ struct wbsrv_samba3_call **_call)
+{
+ struct wbsrv_samba3_call *call;
+
+ if (blob.length != sizeof(call->request)) {
+ DEBUG(0,("wbsrv_samba3_pull_request: invalid blob length %lu should be %lu\n"
+ " make sure you use the correct winbind client tools!\n",
+ (long)blob.length, (long)sizeof(call->request)));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ call = talloc_zero(wbconn, struct wbsrv_samba3_call);
+ NT_STATUS_HAVE_NO_MEMORY(call);
+
+ /* the packet layout is the same as the in memory layout of the request, so just copy it */
+ memcpy(&call->request, blob.data, sizeof(call->request));
+
+ call->wbconn = wbconn;
+ call->event_ctx = call->wbconn->conn->event.ctx;
+
+ *_call = call;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS wbsrv_samba3_handle_call(struct wbsrv_samba3_call *s3call)
+{
+ DEBUG(10, ("Got winbind samba3 request %d\n", s3call->request.cmd));
+
+ s3call->response.length = sizeof(s3call->response);
+
+ switch(s3call->request.cmd) {
+ case WINBINDD_INTERFACE_VERSION:
+ return wbsrv_samba3_interface_version(s3call);
+
+ case WINBINDD_CHECK_MACHACC:
+ return wbsrv_samba3_check_machacc(s3call);
+
+ case WINBINDD_PING:
+ return wbsrv_samba3_ping(s3call);
+
+ case WINBINDD_INFO:
+ return wbsrv_samba3_info(s3call);
+
+ case WINBINDD_DOMAIN_NAME:
+ return wbsrv_samba3_domain_name(s3call);
+
+ case WINBINDD_NETBIOS_NAME:
+ return wbsrv_samba3_netbios_name(s3call);
+
+ case WINBINDD_PRIV_PIPE_DIR:
+ return wbsrv_samba3_priv_pipe_dir(s3call);
+
+ case WINBINDD_LOOKUPNAME:
+ return wbsrv_samba3_lookupname(s3call);
+
+ case WINBINDD_LOOKUPSID:
+ return wbsrv_samba3_lookupsid(s3call);
+
+ case WINBINDD_PAM_AUTH:
+ return wbsrv_samba3_pam_auth(s3call);
+
+ case WINBINDD_PAM_AUTH_CRAP:
+ return wbsrv_samba3_pam_auth_crap(s3call);
+
+ case WINBINDD_GETDCNAME:
+ return wbsrv_samba3_getdcname(s3call);
+
+ case WINBINDD_GETUSERDOMGROUPS:
+ return wbsrv_samba3_userdomgroups(s3call);
+
+ case WINBINDD_GETUSERSIDS:
+ return wbsrv_samba3_usersids(s3call);
+
+ case WINBINDD_LIST_GROUPS:
+ return wbsrv_samba3_list_groups(s3call);
+
+ case WINBINDD_LIST_TRUSTDOM:
+ return wbsrv_samba3_list_trustdom(s3call);
+
+ case WINBINDD_LIST_USERS:
+ return wbsrv_samba3_list_users(s3call);
+
+ case WINBINDD_GETPWNAM:
+ return wbsrv_samba3_getpwnam(s3call);
+
+ case WINBINDD_GETPWUID:
+ return wbsrv_samba3_getpwuid(s3call);
+
+ case WINBINDD_SETPWENT:
+ return wbsrv_samba3_setpwent(s3call);
+
+ case WINBINDD_GETPWENT:
+ return wbsrv_samba3_getpwent(s3call);
+
+ case WINBINDD_ENDPWENT:
+ return wbsrv_samba3_endpwent(s3call);
+
+ case WINBINDD_GETGRNAM:
+ return wbsrv_samba3_getgrnam(s3call);
+
+ case WINBINDD_GETGRGID:
+ return wbsrv_samba3_getgrgid(s3call);
+
+ case WINBINDD_GETGROUPS:
+ return wbsrv_samba3_getgroups(s3call);
+
+ case WINBINDD_SETGRENT:
+ return wbsrv_samba3_setgrent(s3call);
+
+ case WINBINDD_GETGRENT:
+ return wbsrv_samba3_getgrent(s3call);
+
+ case WINBINDD_ENDGRENT:
+ return wbsrv_samba3_endgrent(s3call);
+
+ case WINBINDD_SID_TO_UID:
+ case WINBINDD_DUAL_SID2UID:
+ return wbsrv_samba3_sid2uid(s3call);
+
+ case WINBINDD_SID_TO_GID:
+ case WINBINDD_DUAL_SID2GID:
+ return wbsrv_samba3_sid2gid(s3call);
+
+ case WINBINDD_UID_TO_SID:
+ case WINBINDD_DUAL_UID2SID:
+ return wbsrv_samba3_uid2sid(s3call);
+
+ case WINBINDD_GID_TO_SID:
+ case WINBINDD_DUAL_GID2SID:
+ return wbsrv_samba3_gid2sid(s3call);
+
+ /* Unimplemented commands */
+
+ case WINBINDD_PAM_CHAUTHTOK:
+ case WINBINDD_PAM_LOGOFF:
+ case WINBINDD_PAM_CHNG_PSWD_AUTH_CRAP:
+ case WINBINDD_LOOKUPRIDS:
+ case WINBINDD_SIDS_TO_XIDS:
+ case WINBINDD_ALLOCATE_UID:
+ case WINBINDD_ALLOCATE_GID:
+ case WINBINDD_SET_MAPPING:
+ case WINBINDD_REMOVE_MAPPING:
+ case WINBINDD_SET_HWM:
+ case WINBINDD_DOMAIN_INFO:
+ case WINBINDD_SHOW_SEQUENCE:
+ case WINBINDD_WINS_BYIP:
+ case WINBINDD_WINS_BYNAME:
+ case WINBINDD_GETGRLST:
+ case WINBINDD_DSGETDCNAME:
+ case WINBINDD_INIT_CONNECTION:
+ case WINBINDD_DUAL_SIDS2XIDS:
+ case WINBINDD_DUAL_SET_MAPPING:
+ case WINBINDD_DUAL_REMOVE_MAPPING:
+ case WINBINDD_DUAL_SET_HWM:
+ case WINBINDD_DUAL_USERINFO:
+ case WINBINDD_DUAL_GETSIDALIASES:
+ case WINBINDD_CCACHE_NTLMAUTH:
+ case WINBINDD_NUM_CMDS:
+ DEBUG(10, ("Unimplemented winbind samba3 request %d\n",
+ s3call->request.cmd));
+ break;
+ }
+
+ s3call->response.result = WINBINDD_ERROR;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS wbsrv_samba3_push_reply(struct wbsrv_samba3_call *call, TALLOC_CTX *mem_ctx, DATA_BLOB *_blob)
+{
+ DATA_BLOB blob;
+ uint8_t *extra_data;
+ size_t extra_data_len = 0;
+
+ extra_data = (uint8_t *)call->response.extra_data.data;
+ if (extra_data != NULL) {
+ extra_data_len = call->response.length -
+ sizeof(call->response);
+ }
+
+ blob = data_blob_talloc(mem_ctx, NULL, call->response.length);
+ NT_STATUS_HAVE_NO_MEMORY(blob.data);
+
+ /* don't push real pointer values into sockets */
+ if (extra_data) {
+ call->response.extra_data.data = (void *)0xFFFFFFFF;
+ }
+ memcpy(blob.data, &call->response, sizeof(call->response));
+ /* set back the pointer */
+ call->response.extra_data.data = extra_data;
+
+ if (extra_data) {
+ memcpy(blob.data + sizeof(call->response), extra_data, extra_data_len);
+ }
+
+ *_blob = blob;
+ return NT_STATUS_OK;
+}
+
+/*
+ * queue a wbsrv_call reply on a wbsrv_connection
+ * NOTE: that this implies talloc_free(call),
+ * use talloc_reference(call) if you need it after
+ * calling wbsrv_queue_reply
+ */
+NTSTATUS wbsrv_samba3_send_reply(struct wbsrv_samba3_call *call)
+{
+ struct wbsrv_connection *wbconn = call->wbconn;
+ DATA_BLOB rep;
+ NTSTATUS status;
+
+ status = wbsrv_samba3_push_reply(call, call, &rep);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ status = packet_send(call->wbconn->packet, rep);
+
+ talloc_free(call);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ wbsrv_terminate_connection(wbconn,
+ "failed to packet_send winbindd reply");
+ return status;
+ }
+ /* the call isn't needed any more */
+ return status;
+}
+
+NTSTATUS wbsrv_samba3_process(void *private_data, DATA_BLOB blob)
+{
+ NTSTATUS status;
+ struct wbsrv_connection *wbconn = talloc_get_type(private_data,
+ struct wbsrv_connection);
+ struct wbsrv_samba3_call *call;
+ status = wbsrv_samba3_pull_request(blob, wbconn, &call);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = wbsrv_samba3_handle_call(call);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(call);
+ return status;
+ }
+
+ if (call->flags & WBSRV_CALL_FLAGS_REPLY_ASYNC) {
+ return NT_STATUS_OK;
+ }
+
+ status = wbsrv_samba3_send_reply(call);
+ return status;
+}
+
diff --git a/source4/winbind/wb_server.c b/source4/winbind/wb_server.c
new file mode 100644
index 0000000000..95be49d1e3
--- /dev/null
+++ b/source4/winbind/wb_server.c
@@ -0,0 +1,223 @@
+/*
+ Unix SMB/CIFS implementation.
+ Main winbindd server routines
+
+ Copyright (C) Stefan Metzmacher 2005
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/socket/socket.h"
+#include "../lib/util/dlinklist.h"
+#include "lib/events/events.h"
+#include "smbd/service_task.h"
+#include "smbd/process_model.h"
+#include "smbd/service_stream.h"
+#include "nsswitch/winbind_nss_config.h"
+#include "winbind/wb_server.h"
+#include "lib/stream/packet.h"
+#include "smbd/service.h"
+#include "param/secrets.h"
+#include "param/param.h"
+
+void wbsrv_terminate_connection(struct wbsrv_connection *wbconn, const char *reason)
+{
+ stream_terminate_connection(wbconn->conn, reason);
+}
+
+/*
+ called on a tcp recv error
+*/
+static void wbsrv_recv_error(void *private_data, NTSTATUS status)
+{
+ struct wbsrv_connection *wbconn = talloc_get_type(private_data, struct wbsrv_connection);
+ wbsrv_terminate_connection(wbconn, nt_errstr(status));
+}
+
+static void wbsrv_accept(struct stream_connection *conn)
+{
+ struct wbsrv_listen_socket *listen_socket = talloc_get_type(conn->private_data,
+ struct wbsrv_listen_socket);
+ struct wbsrv_connection *wbconn;
+
+ wbconn = talloc_zero(conn, struct wbsrv_connection);
+ if (!wbconn) {
+ stream_terminate_connection(conn, "wbsrv_accept: out of memory");
+ return;
+ }
+ wbconn->conn = conn;
+ wbconn->listen_socket = listen_socket;
+ wbconn->lp_ctx = listen_socket->service->task->lp_ctx;
+ conn->private_data = wbconn;
+
+ wbconn->packet = packet_init(wbconn);
+ if (wbconn->packet == NULL) {
+ wbsrv_terminate_connection(wbconn, "wbsrv_accept: out of memory");
+ return;
+ }
+ packet_set_private(wbconn->packet, wbconn);
+ packet_set_socket(wbconn->packet, conn->socket);
+ packet_set_callback(wbconn->packet, wbsrv_samba3_process);
+ packet_set_full_request(wbconn->packet, wbsrv_samba3_packet_full_request);
+ packet_set_error_handler(wbconn->packet, wbsrv_recv_error);
+ packet_set_event_context(wbconn->packet, conn->event.ctx);
+ packet_set_fde(wbconn->packet, conn->event.fde);
+ packet_set_serialise(wbconn->packet);
+}
+
+/*
+ receive some data on a winbind connection
+*/
+static void wbsrv_recv(struct stream_connection *conn, uint16_t flags)
+{
+ struct wbsrv_connection *wbconn = talloc_get_type(conn->private_data,
+ struct wbsrv_connection);
+ packet_recv(wbconn->packet);
+
+}
+
+/*
+ called when we can write to a connection
+*/
+static void wbsrv_send(struct stream_connection *conn, uint16_t flags)
+{
+ struct wbsrv_connection *wbconn = talloc_get_type(conn->private_data,
+ struct wbsrv_connection);
+ packet_queue_run(wbconn->packet);
+}
+
+static const struct stream_server_ops wbsrv_ops = {
+ .name = "winbind samba3 protocol",
+ .accept_connection = wbsrv_accept,
+ .recv_handler = wbsrv_recv,
+ .send_handler = wbsrv_send
+};
+
+/*
+ startup the winbind task
+*/
+static void winbind_task_init(struct task_server *task)
+{
+ uint16_t port = 1;
+ const struct model_ops *model_ops;
+ NTSTATUS status;
+ struct wbsrv_service *service;
+ struct wbsrv_listen_socket *listen_socket;
+
+ task_server_set_title(task, "task[winbind]");
+
+ /* within the winbind task we want to be a single process, so
+ ask for the single process model ops and pass these to the
+ stream_setup_socket() call. */
+ model_ops = process_model_startup(task->event_ctx, "single");
+ if (!model_ops) {
+ task_server_terminate(task,
+ "Can't find 'single' process model_ops");
+ return;
+ }
+
+ /* Make sure the directory for the Samba3 socket exists, and is of the correct permissions */
+ if (!directory_create_or_exist(lp_winbindd_socket_directory(task->lp_ctx), geteuid(), 0755)) {
+ task_server_terminate(task,
+ "Cannot create winbindd pipe directory");
+ return;
+ }
+
+ /* Make sure the directory for the Samba3 socket exists, and is of the correct permissions */
+ if (!directory_create_or_exist(lp_winbindd_privileged_socket_directory(task->lp_ctx), geteuid(), 0750)) {
+ task_server_terminate(task,
+ "Cannot create winbindd privileged pipe directory");
+ return;
+ }
+
+ service = talloc_zero(task, struct wbsrv_service);
+ if (!service) goto nomem;
+ service->task = task;
+
+ status = wbsrv_setup_domains(service);
+ if (!NT_STATUS_IS_OK(status)) {
+ task_server_terminate(task, nt_errstr(status));
+ return;
+ }
+
+ service->idmap_ctx = idmap_init(service, task->event_ctx, task->lp_ctx);
+ if (service->idmap_ctx == NULL) {
+ task_server_terminate(task, "Failed to load idmap database");
+ return;
+ }
+
+ /* setup the unprivileged samba3 socket */
+ listen_socket = talloc(service, struct wbsrv_listen_socket);
+ if (!listen_socket) goto nomem;
+ listen_socket->socket_path = talloc_asprintf(listen_socket, "%s/%s",
+ lp_winbindd_socket_directory(task->lp_ctx),
+ WINBINDD_SAMBA3_SOCKET);
+ if (!listen_socket->socket_path) goto nomem;
+ listen_socket->service = service;
+ listen_socket->privileged = false;
+ status = stream_setup_socket(task->event_ctx, task->lp_ctx, model_ops,
+ &wbsrv_ops, "unix",
+ listen_socket->socket_path, &port,
+ lp_socket_options(task->lp_ctx),
+ listen_socket);
+ if (!NT_STATUS_IS_OK(status)) goto listen_failed;
+
+ /* setup the privileged samba3 socket */
+ listen_socket = talloc(service, struct wbsrv_listen_socket);
+ if (!listen_socket) goto nomem;
+ listen_socket->socket_path
+ = service->priv_socket_path
+ = talloc_asprintf(listen_socket, "%s/%s",
+ lp_winbindd_privileged_socket_directory(task->lp_ctx),
+ WINBINDD_SAMBA3_SOCKET);
+ if (!listen_socket->socket_path) goto nomem;
+ if (!listen_socket->socket_path) goto nomem;
+ listen_socket->service = service;
+ listen_socket->privileged = true;
+ status = stream_setup_socket(task->event_ctx, task->lp_ctx, model_ops,
+ &wbsrv_ops, "unix",
+ listen_socket->socket_path, &port,
+ lp_socket_options(task->lp_ctx),
+ listen_socket);
+ if (!NT_STATUS_IS_OK(status)) goto listen_failed;
+
+ status = wbsrv_init_irpc(service);
+ if (!NT_STATUS_IS_OK(status)) goto irpc_failed;
+
+ return;
+
+listen_failed:
+ DEBUG(0,("stream_setup_socket(path=%s) failed - %s\n",
+ listen_socket->socket_path, nt_errstr(status)));
+ task_server_terminate(task, nt_errstr(status));
+ return;
+irpc_failed:
+ DEBUG(0,("wbsrv_init_irpc() failed - %s\n",
+ nt_errstr(status)));
+ task_server_terminate(task, nt_errstr(status));
+ return;
+nomem:
+ task_server_terminate(task, nt_errstr(NT_STATUS_NO_MEMORY));
+ return;
+}
+
+/*
+ register ourselves as a available server
+*/
+NTSTATUS server_service_winbind_init(void)
+{
+ return register_server_service("winbind", winbind_task_init);
+}
diff --git a/source4/winbind/wb_server.h b/source4/winbind/wb_server.h
new file mode 100644
index 0000000000..be79872e65
--- /dev/null
+++ b/source4/winbind/wb_server.h
@@ -0,0 +1,169 @@
+/*
+ Unix SMB/CIFS implementation.
+ Main winbindd server routines
+
+ Copyright (C) Stefan Metzmacher 2005
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "nsswitch/winbind_nss_config.h"
+#include "nsswitch/winbind_struct_protocol.h"
+#include "winbind/idmap.h"
+#include "libnet/libnet.h"
+
+#define WINBINDD_SAMBA3_SOCKET "pipe"
+/* the privileged socket is in smbd_tmp_dir() */
+#define WINBINDD_SAMBA3_PRIVILEGED_SOCKET "winbind_pipe"
+
+/* this struct stores global data for the winbind task */
+struct wbsrv_service {
+ struct task_server *task;
+
+ const struct dom_sid *primary_sid;
+ struct wbsrv_domain *domains;
+ struct idmap_context *idmap_ctx;
+
+ const char *priv_socket_path;
+};
+
+struct wbsrv_samconn {
+ struct wbsrv_domain *domain;
+ void *private_data;
+
+ struct composite_context (*seqnum_send)(struct wbsrv_samconn *);
+ NTSTATUS (*seqnum_recv)(struct composite_context *, uint64_t *);
+};
+
+struct wb_dom_info {
+ const char *name;
+ const char *dns_name;
+ const struct dom_sid *sid;
+
+ int num_dcs;
+ struct nbt_dc_name *dcs;
+};
+
+struct wbsrv_domain {
+ struct wbsrv_domain *next, *prev;
+
+ struct wb_dom_info *info;
+
+ /* Details for the server we are currently talking to */
+ const char *dc_address;
+ const char *dc_name;
+
+ struct libnet_context *libnet_ctx;
+
+ struct dcerpc_binding *lsa_binding;
+
+ struct dcerpc_binding *samr_binding;
+
+ struct dcerpc_pipe *netlogon_pipe;
+ struct dcerpc_binding *netlogon_binding;
+};
+
+/*
+ state of a listen socket and it's protocol information
+*/
+struct wbsrv_listen_socket {
+ const char *socket_path;
+ struct wbsrv_service *service;
+ bool privileged;
+};
+
+/*
+ state of an open winbind connection
+*/
+struct wbsrv_connection {
+ /* stream connection we belong to */
+ struct stream_connection *conn;
+
+ /* the listening socket we belong to, it holds protocol hooks */
+ struct wbsrv_listen_socket *listen_socket;
+
+ /* storage for protocol specific data */
+ void *protocol_private_data;
+
+ /* how many calls are pending */
+ uint32_t pending_calls;
+
+ struct packet_context *packet;
+
+ struct loadparm_context *lp_ctx;
+};
+
+#define WBSRV_SAMBA3_SET_STRING(dest, src) do { \
+ safe_strcpy(dest, src, sizeof(dest)-1);\
+} while(0)
+
+/*
+ state of a pwent query
+*/
+struct wbsrv_pwent {
+ /* Current UserList structure, contains 1+ user structs */
+ struct libnet_UserList *user_list;
+
+ /* Index of the next user struct in the current UserList struct */
+ uint32_t page_index;
+
+ /* The libnet_ctx to use for the libnet_UserList call */
+ struct libnet_context *libnet_ctx;
+};
+
+/*
+ state of one request
+
+ NOTE about async replies:
+ if the backend wants to reply later:
+
+ - it should set the WBSRV_CALL_FLAGS_REPLY_ASYNC flag, and may set a
+ talloc_destructor on the this structure or on the private_data (if it's a
+ talloc child of this structure), so that wbsrv_terminate_connection
+ called by another call clean up the whole connection correct.
+ - When the backend is ready to reply it should call wbsrv_send_reply(call),
+ wbsrv_send_reply implies talloc_free(call), so the backend should use
+ talloc_reference(call), if it needs it later.
+ - If wbsrv_send_reply doesn't return NT_STATUS_OK, the backend function
+ should call, wbsrv_terminate_connection(call->wbconn, nt_errstr(status));
+ return;
+
+*/
+struct wbsrv_samba3_call {
+#define WBSRV_CALL_FLAGS_REPLY_ASYNC 0x00000001
+ uint32_t flags;
+
+ /* the connection the call belongs to */
+ struct wbsrv_connection *wbconn;
+
+ /* the backend should use this event context */
+ struct tevent_context *event_ctx;
+
+ /* here the backend can store stuff like composite_context's ... */
+ void *private_data;
+
+ /* the request structure of the samba3 protocol */
+ struct winbindd_request request;
+
+ /* the response structure of the samba3 protocol*/
+ struct winbindd_response response;
+};
+
+struct netr_LMSessionKey;
+struct netr_UserSessionKey;
+struct winbind_SamLogon;
+
+#include "winbind/wb_async_helpers.h"
+#include "winbind/wb_proto.h"
diff --git a/source4/winbind/wb_setup_domains.c b/source4/winbind/wb_setup_domains.c
new file mode 100644
index 0000000000..92b91c182f
--- /dev/null
+++ b/source4/winbind/wb_setup_domains.c
@@ -0,0 +1,42 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbind/wb_server.h"
+#include "smbd/service_task.h"
+#include "auth/credentials/credentials.h"
+#include "param/secrets.h"
+#include "param/param.h"
+
+NTSTATUS wbsrv_setup_domains(struct wbsrv_service *service)
+{
+ const struct dom_sid *primary_sid;
+
+ primary_sid = secrets_get_domain_sid(service,
+ service->task->event_ctx,
+ service->task->lp_ctx,
+ lp_workgroup(service->task->lp_ctx));
+ if (!primary_sid) {
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ }
+
+ service->primary_sid = primary_sid;
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/winbind/wb_sid2domain.c b/source4/winbind/wb_sid2domain.c
new file mode 100644
index 0000000000..cb9fa3ada2
--- /dev/null
+++ b/source4/winbind/wb_sid2domain.c
@@ -0,0 +1,212 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Find and init a domain struct for a SID
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/composite/composite.h"
+#include "winbind/wb_server.h"
+#include "smbd/service_task.h"
+#include "winbind/wb_async_helpers.h"
+#include "libcli/security/security.h"
+#include "../lib/util/dlinklist.h"
+#include "param/param.h"
+
+static struct wbsrv_domain *find_domain_from_sid(struct wbsrv_service *service,
+ const struct dom_sid *sid)
+{
+ struct wbsrv_domain *domain;
+
+ for (domain = service->domains; domain!=NULL; domain = domain->next) {
+ if (dom_sid_equal(domain->info->sid, sid)) {
+ break;
+ }
+ if (dom_sid_in_domain(domain->info->sid, sid)) {
+ break;
+ }
+ }
+ return domain;
+}
+
+struct sid2domain_state {
+ struct composite_context *ctx;
+ struct wbsrv_service *service;
+ struct dom_sid *sid;
+
+ struct wbsrv_domain *domain;
+};
+
+static void sid2domain_recv_dom_info(struct composite_context *ctx);
+static void sid2domain_recv_name(struct composite_context *ctx);
+static void sid2domain_recv_trusted_dom_info(struct composite_context *ctx);
+static void sid2domain_recv_init(struct composite_context *ctx);
+
+struct composite_context *wb_sid2domain_send(TALLOC_CTX *mem_ctx,
+ struct wbsrv_service *service,
+ const struct dom_sid *sid)
+{
+ struct composite_context *result, *ctx;
+ struct sid2domain_state *state;
+
+ result = composite_create(mem_ctx, service->task->event_ctx);
+ if (result == NULL) goto failed;
+
+ state = talloc(result, struct sid2domain_state);
+ if (state == NULL) goto failed;
+ state->ctx = result;
+ result->private_data = state;
+
+ state->service = service;
+ state->sid = dom_sid_dup(state, sid);
+ if (state->sid == NULL) goto failed;
+
+ state->domain = find_domain_from_sid(service, sid);
+ if (state->domain != NULL) {
+ result->status = NT_STATUS_OK;
+ composite_done(result);
+ return result;
+ }
+
+ if (dom_sid_equal(service->primary_sid, sid) ||
+ dom_sid_in_domain(service->primary_sid, sid)) {
+ ctx = wb_get_dom_info_send(state, service, lp_workgroup(service->task->lp_ctx),
+ service->primary_sid);
+ if (ctx == NULL) goto failed;
+ ctx->async.fn = sid2domain_recv_dom_info;
+ ctx->async.private_data = state;
+ return result;
+ }
+
+ ctx = wb_cmd_lookupsid_send(state, service, state->sid);
+ if (ctx == NULL) goto failed;
+ composite_continue(result, ctx, sid2domain_recv_name, state);
+
+ return result;
+
+ failed:
+ talloc_free(result);
+ return NULL;
+
+}
+
+static void sid2domain_recv_dom_info(struct composite_context *ctx)
+{
+ struct sid2domain_state *state =
+ talloc_get_type(ctx->async.private_data,
+ struct sid2domain_state);
+ struct wb_dom_info *info;
+
+ state->ctx->status = wb_get_dom_info_recv(ctx, state, &info);
+ if (!composite_is_ok(state->ctx)) return;
+
+ ctx = wb_init_domain_send(state, state->service, info);
+
+ composite_continue(state->ctx, ctx, sid2domain_recv_init, state);
+}
+
+static void sid2domain_recv_name(struct composite_context *ctx)
+{
+ struct sid2domain_state *state =
+ talloc_get_type(ctx->async.private_data,
+ struct sid2domain_state);
+ struct wb_sid_object *name;
+
+ state->ctx->status = wb_cmd_lookupsid_recv(ctx, state, &name);
+ if (!composite_is_ok(state->ctx)) return;
+
+ if (name->type == SID_NAME_UNKNOWN) {
+ composite_error(state->ctx, NT_STATUS_NO_SUCH_DOMAIN);
+ return;
+ }
+
+ if (name->type != SID_NAME_DOMAIN) {
+ state->sid->num_auths -= 1;
+ }
+
+ ctx = wb_trusted_dom_info_send(state, state->service, name->domain,
+ state->sid);
+
+ composite_continue(state->ctx, ctx, sid2domain_recv_trusted_dom_info,
+ state);
+}
+
+static void sid2domain_recv_trusted_dom_info(struct composite_context *ctx)
+{
+ struct sid2domain_state *state =
+ talloc_get_type(ctx->async.private_data,
+ struct sid2domain_state);
+ struct wb_dom_info *info;
+
+ state->ctx->status = wb_trusted_dom_info_recv(ctx, state, &info);
+ if (!composite_is_ok(state->ctx)) return;
+
+ ctx = wb_init_domain_send(state, state->service, info);
+
+ composite_continue(state->ctx, ctx, sid2domain_recv_init, state);
+}
+
+static void sid2domain_recv_init(struct composite_context *ctx)
+{
+ struct sid2domain_state *state =
+ talloc_get_type(ctx->async.private_data,
+ struct sid2domain_state);
+ struct wbsrv_domain *existing;
+
+ state->ctx->status = wb_init_domain_recv(ctx, state,
+ &state->domain);
+ if (!composite_is_ok(state->ctx)) {
+ DEBUG(10, ("Could not init domain\n"));
+ return;
+ }
+
+ existing = find_domain_from_sid(state->service, state->sid);
+ if (existing != NULL) {
+ DEBUG(5, ("Initialized domain twice, dropping second one\n"));
+ talloc_free(state->domain);
+ state->domain = existing;
+ }
+
+ talloc_steal(state->service, state->domain);
+ DLIST_ADD(state->service->domains, state->domain);
+
+ composite_done(state->ctx);
+}
+
+NTSTATUS wb_sid2domain_recv(struct composite_context *ctx,
+ struct wbsrv_domain **result)
+{
+ NTSTATUS status = composite_wait(ctx);
+ if (NT_STATUS_IS_OK(status)) {
+ struct sid2domain_state *state =
+ talloc_get_type(ctx->private_data,
+ struct sid2domain_state);
+ *result = state->domain;
+ }
+ talloc_free(ctx);
+ return status;
+}
+
+NTSTATUS wb_sid2domain(TALLOC_CTX *mem_ctx, struct wbsrv_service *service,
+ const struct dom_sid *sid,
+ struct wbsrv_domain **result)
+{
+ struct composite_context *c = wb_sid2domain_send(mem_ctx, service,
+ sid);
+ return wb_sid2domain_recv(c, result);
+}
diff --git a/source4/winbind/wb_sid2gid.c b/source4/winbind/wb_sid2gid.c
new file mode 100644
index 0000000000..282d10c9c9
--- /dev/null
+++ b/source4/winbind/wb_sid2gid.c
@@ -0,0 +1,109 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Map a SID to a gid
+
+ Copyright (C) 2007-2008 Kai Blin
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/composite/composite.h"
+#include "winbind/wb_server.h"
+#include "smbd/service_task.h"
+#include "winbind/wb_helper.h"
+#include "libcli/security/security.h"
+#include "winbind/idmap.h"
+
+struct sid2gid_state {
+ struct composite_context *ctx;
+ struct wbsrv_service *service;
+ gid_t gid;
+};
+
+static void sid2gid_recv_gid(struct composite_context *ctx);
+
+struct composite_context *wb_sid2gid_send(TALLOC_CTX *mem_ctx,
+ struct wbsrv_service *service, const struct dom_sid *sid)
+{
+ struct composite_context *result, *ctx;
+ struct sid2gid_state *state;
+ struct id_mapping *ids;
+
+ DEBUG(5, ("wb_sid2gid_send called\n"));
+
+ result = composite_create(mem_ctx, service->task->event_ctx);
+ if (!result) return NULL;
+
+ state = talloc(result, struct sid2gid_state);
+ if(composite_nomem(state, result)) return result;
+
+ state->ctx = result;
+ result->private_data = state;
+ state->service = service;
+
+ ids = talloc(result, struct id_mapping);
+ if (composite_nomem(ids, result)) return result;
+
+ ids->sid = dom_sid_dup(result, sid);
+ if (composite_nomem(ids->sid, result)) return result;
+
+ ctx = wb_sids2xids_send(result, service, 1, ids);
+ if (composite_nomem(ctx, result)) return result;
+
+ composite_continue(result, ctx, sid2gid_recv_gid, state);
+ return result;
+}
+
+static void sid2gid_recv_gid(struct composite_context *ctx)
+{
+ struct sid2gid_state *state = talloc_get_type(ctx->async.private_data,
+ struct sid2gid_state);
+
+ struct id_mapping *ids = NULL;
+
+ state->ctx->status = wb_sids2xids_recv(ctx, &ids);
+ if (!composite_is_ok(state->ctx)) return;
+
+ if (!NT_STATUS_IS_OK(ids->status)) {
+ composite_error(state->ctx, ids->status);
+ return;
+ }
+
+ if (ids->unixid->type == ID_TYPE_BOTH ||
+ ids->unixid->type == ID_TYPE_GID) {
+ state->gid = ids->unixid->id;
+ composite_done(state->ctx);
+ } else {
+ composite_error(state->ctx, NT_STATUS_INVALID_SID);
+ }
+}
+
+NTSTATUS wb_sid2gid_recv(struct composite_context *ctx, gid_t *gid)
+{
+ NTSTATUS status = composite_wait(ctx);
+
+ DEBUG(5, ("wb_sid2gid_recv called\n"));
+
+ if (NT_STATUS_IS_OK(status)) {
+ struct sid2gid_state *state =
+ talloc_get_type(ctx->private_data,
+ struct sid2gid_state);
+ *gid = state->gid;
+ }
+ talloc_free(ctx);
+ return status;
+}
+
diff --git a/source4/winbind/wb_sid2uid.c b/source4/winbind/wb_sid2uid.c
new file mode 100644
index 0000000000..151f39906b
--- /dev/null
+++ b/source4/winbind/wb_sid2uid.c
@@ -0,0 +1,109 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Map a SID to a uid
+
+ Copyright (C) 2007-2008 Kai Blin
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/composite/composite.h"
+#include "winbind/wb_server.h"
+#include "smbd/service_task.h"
+#include "winbind/wb_helper.h"
+#include "libcli/security/security.h"
+#include "winbind/idmap.h"
+
+struct sid2uid_state {
+ struct composite_context *ctx;
+ struct wbsrv_service *service;
+ uid_t uid;
+};
+
+static void sid2uid_recv_uid(struct composite_context *ctx);
+
+struct composite_context *wb_sid2uid_send(TALLOC_CTX *mem_ctx,
+ struct wbsrv_service *service, const struct dom_sid *sid)
+{
+ struct composite_context *result, *ctx;
+ struct sid2uid_state *state;
+ struct id_mapping *ids;
+
+ DEBUG(5, ("wb_sid2uid_send called\n"));
+
+ result = composite_create(mem_ctx, service->task->event_ctx);
+ if (!result) return NULL;
+
+ state = talloc(result, struct sid2uid_state);
+ if (composite_nomem(state, result)) return result;
+
+ state->ctx = result;
+ result->private_data = state;
+ state->service = service;
+
+ ids = talloc(result, struct id_mapping);
+ if (composite_nomem(ids, result)) return result;
+
+ ids->sid = dom_sid_dup(result, sid);
+ if (composite_nomem(ids->sid, result)) return result;
+
+ ctx = wb_sids2xids_send(result, service, 1, ids);
+ if (composite_nomem(ctx, result)) return result;
+
+ composite_continue(result, ctx, sid2uid_recv_uid, state);
+ return result;
+}
+
+static void sid2uid_recv_uid(struct composite_context *ctx)
+{
+ struct sid2uid_state *state = talloc_get_type(ctx->async.private_data,
+ struct sid2uid_state);
+
+ struct id_mapping *ids = NULL;
+
+ state->ctx->status = wb_sids2xids_recv(ctx, &ids);
+ if (!composite_is_ok(state->ctx)) return;
+
+ if (!NT_STATUS_IS_OK(ids->status)) {
+ composite_error(state->ctx, ids->status);
+ return;
+ }
+
+ if (ids->unixid->type == ID_TYPE_BOTH ||
+ ids->unixid->type == ID_TYPE_UID) {
+ state->uid = ids->unixid->id;
+ composite_done(state->ctx);
+ } else {
+ composite_error(state->ctx, NT_STATUS_INVALID_SID);
+ }
+}
+
+NTSTATUS wb_sid2uid_recv(struct composite_context *ctx, uid_t *uid)
+{
+ NTSTATUS status = composite_wait(ctx);
+
+ DEBUG(5, ("wb_sid2uid_recv called\n"));
+
+ if (NT_STATUS_IS_OK(status)) {
+ struct sid2uid_state *state =
+ talloc_get_type(ctx->private_data,
+ struct sid2uid_state);
+ *uid = state->uid;
+ }
+ talloc_free(ctx);
+ return status;
+}
+
diff --git a/source4/winbind/wb_sids2xids.c b/source4/winbind/wb_sids2xids.c
new file mode 100644
index 0000000000..6b89caf465
--- /dev/null
+++ b/source4/winbind/wb_sids2xids.c
@@ -0,0 +1,80 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Map SIDs to unixids.
+
+ Copyright (C) 2008 Kai Blin
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/composite/composite.h"
+#include "winbind/wb_server.h"
+#include "smbd/service_task.h"
+#include "winbind/wb_helper.h"
+#include "libcli/security/proto.h"
+#include "winbind/idmap.h"
+
+struct sids2xids_state {
+ struct composite_context *ctx;
+ struct wbsrv_service *service;
+ struct id_mapping *ids;
+ int count;
+};
+
+struct composite_context *wb_sids2xids_send(TALLOC_CTX *mem_ctx,
+ struct wbsrv_service *service,
+ int count, struct id_mapping *ids)
+{
+ struct composite_context *result;
+ struct sids2xids_state *state;
+
+ DEBUG(5, ("wb_sids2xids_send called\n"));
+
+ result = composite_create(mem_ctx, service->task->event_ctx);
+ if (!result) return NULL;
+
+ state = talloc(result, struct sids2xids_state);
+ if (composite_nomem(state, result)) return result;
+
+ state->ctx = result;
+ result->private_data = state;
+ state->service = service;
+ state->count = count;
+ state->ids = ids;
+
+ state->ctx->status = idmap_sids_to_xids(service->idmap_ctx, mem_ctx,
+ count, state->ids);
+ if (!composite_is_ok(state->ctx)) return result;
+
+ composite_done(state->ctx);
+ return result;
+}
+
+NTSTATUS wb_sids2xids_recv(struct composite_context *ctx,
+ struct id_mapping **ids)
+{
+ NTSTATUS status = composite_wait(ctx);
+ struct sids2xids_state *state = talloc_get_type(ctx->private_data,
+ struct sids2xids_state);
+
+ DEBUG(5, ("wb_sids2xids_recv called\n"));
+
+ *ids = state->ids;
+
+ talloc_free(ctx);
+ return status;
+}
+
diff --git a/source4/winbind/wb_uid2sid.c b/source4/winbind/wb_uid2sid.c
new file mode 100644
index 0000000000..fd43dd64b9
--- /dev/null
+++ b/source4/winbind/wb_uid2sid.c
@@ -0,0 +1,110 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Command backend for wbinfo -U
+
+ Copyright (C) 2007-2008 Kai Blin
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/composite/composite.h"
+#include "winbind/wb_server.h"
+#include "smbd/service_task.h"
+#include "winbind/wb_helper.h"
+#include "libcli/security/proto.h"
+#include "winbind/idmap.h"
+
+struct uid2sid_state {
+ struct composite_context *ctx;
+ struct wbsrv_service *service;
+ struct dom_sid *sid;
+};
+
+static void uid2sid_recv_sid(struct composite_context *ctx);
+
+struct composite_context *wb_uid2sid_send(TALLOC_CTX *mem_ctx,
+ struct wbsrv_service *service, uid_t uid)
+{
+ struct composite_context *result, *ctx;
+ struct uid2sid_state *state;
+ struct unixid *unixid;
+ struct id_mapping *ids;
+
+ DEBUG(5, ("wb_uid2sid_send called\n"));
+
+ result = composite_create(mem_ctx, service->task->event_ctx);
+ if (!result) return NULL;
+
+ state = talloc(result, struct uid2sid_state);
+ if (composite_nomem(state, result)) return result;
+
+ state->ctx = result;
+ result->private_data = state;
+ state->service = service;
+
+ unixid = talloc(result, struct unixid);
+ if (composite_nomem(unixid, result)) return result;
+ unixid->id = uid;
+ unixid->type = ID_TYPE_UID;
+
+ ids = talloc(result, struct id_mapping);
+ if (composite_nomem(ids, result)) return result;
+ ids->unixid = unixid;
+ ids->sid = NULL;
+
+ ctx = wb_xids2sids_send(result, service, 1, ids);
+ if (composite_nomem(ctx, result)) return result;
+
+ composite_continue(result, ctx, uid2sid_recv_sid, state);
+ return result;
+}
+
+static void uid2sid_recv_sid(struct composite_context *ctx)
+{
+ struct uid2sid_state *state = talloc_get_type(ctx->async.private_data,
+ struct uid2sid_state);
+ struct id_mapping *ids = NULL;
+
+ state->ctx->status = wb_xids2sids_recv(ctx, &ids);
+ if (!composite_is_ok(state->ctx)) return;
+
+ if (!NT_STATUS_IS_OK(ids->status)) {
+ composite_error(state->ctx, ids->status);
+ return;
+ }
+
+ state->sid = ids->sid;
+
+ composite_done(state->ctx);
+}
+
+NTSTATUS wb_uid2sid_recv(struct composite_context *ctx, TALLOC_CTX *mem_ctx,
+ struct dom_sid **sid)
+{
+ NTSTATUS status = composite_wait(ctx);
+
+ DEBUG(5, ("wb_uid2sid_recv called.\n"));
+
+ if (NT_STATUS_IS_OK(status)) {
+ struct uid2sid_state *state =
+ talloc_get_type(ctx->private_data,
+ struct uid2sid_state);
+ *sid = talloc_steal(mem_ctx, state->sid);
+ }
+ talloc_free(ctx);
+ return status;
+}
+
diff --git a/source4/winbind/wb_utils.c b/source4/winbind/wb_utils.c
new file mode 100644
index 0000000000..43effc3028
--- /dev/null
+++ b/source4/winbind/wb_utils.c
@@ -0,0 +1,49 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Utility functions that are not related with async operations.
+
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "param/param.h"
+
+
+/* Split a domain\\user string into it's parts, because the client supplies it
+ * as one string.
+ * TODO: We probably will need to handle other formats later. */
+
+bool wb_samba3_split_username(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx,
+ const char *domuser,
+ char **domain, char **user)
+{
+ char *p = strchr(domuser, *lp_winbind_separator(lp_ctx));
+
+ if (p == NULL) {
+ *domain = talloc_strdup(mem_ctx, lp_workgroup(lp_ctx));
+ } else {
+ *domain = talloc_strndup(mem_ctx, domuser,
+ PTR_DIFF(p, domuser));
+ domuser = p+1;
+ }
+
+ *user = talloc_strdup(mem_ctx, domuser);
+
+ return ((*domain != NULL) && (*user != NULL));
+}
+
+
diff --git a/source4/winbind/wb_xids2sids.c b/source4/winbind/wb_xids2sids.c
new file mode 100644
index 0000000000..a1cf2667ff
--- /dev/null
+++ b/source4/winbind/wb_xids2sids.c
@@ -0,0 +1,80 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Convet an unixid struct to a SID
+
+ Copyright (C) 2008 Kai Blin
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/composite/composite.h"
+#include "winbind/wb_server.h"
+#include "smbd/service_task.h"
+#include "winbind/wb_helper.h"
+#include "libcli/security/proto.h"
+#include "winbind/idmap.h"
+
+struct xids2sids_state {
+ struct composite_context *ctx;
+ struct wbsrv_service *service;
+ struct id_mapping *ids;
+ int count;
+};
+
+struct composite_context *wb_xids2sids_send(TALLOC_CTX *mem_ctx,
+ struct wbsrv_service *service,
+ int count, struct id_mapping *ids)
+{
+ struct composite_context *result;
+ struct xids2sids_state *state;
+
+ DEBUG(5, ("wb_xids2sids_send called\n"));
+
+ result = composite_create(mem_ctx, service->task->event_ctx);
+ if (!result) return NULL;
+
+ state = talloc(mem_ctx, struct xids2sids_state);
+ if (composite_nomem(state, result)) return result;
+
+ state->ctx = result;
+ result->private_data = state;
+ state->service = service;
+ state->count = count;
+ state->ids = ids;
+
+ state->ctx->status = idmap_xids_to_sids(service->idmap_ctx, mem_ctx,
+ count, state->ids);
+ if (!composite_is_ok(state->ctx)) return result;
+
+ composite_done(state->ctx);
+ return result;
+}
+
+NTSTATUS wb_xids2sids_recv(struct composite_context *ctx,
+ struct id_mapping **ids)
+{
+ NTSTATUS status = composite_wait(ctx);
+ struct xids2sids_state *state = talloc_get_type(ctx->private_data,
+ struct xids2sids_state);
+
+ DEBUG(5, ("wb_xids2sids_recv called.\n"));
+
+ *ids = state->ids;
+
+ talloc_free(ctx);
+ return status;
+}
+
diff --git a/source4/wrepl_server/config.mk b/source4/wrepl_server/config.mk
new file mode 100644
index 0000000000..b8696868db
--- /dev/null
+++ b/source4/wrepl_server/config.mk
@@ -0,0 +1,24 @@
+# WREPL server subsystem
+
+#######################
+# Start SUBSYSTEM WREPL_SRV
+[MODULE::WREPL_SRV]
+INIT_FUNCTION = server_service_wrepl_init
+SUBSYSTEM = service
+PRIVATE_DEPENDENCIES = \
+ LIBCLI_WREPL WINSDB process_model
+# End SUBSYSTEM WREPL_SRV
+#######################
+
+WREPL_SRV_OBJ_FILES = $(addprefix $(wrepl_serversrcdir)/, \
+ wrepl_server.o \
+ wrepl_in_connection.o \
+ wrepl_in_call.o \
+ wrepl_apply_records.o \
+ wrepl_periodic.o \
+ wrepl_scavenging.o \
+ wrepl_out_pull.o \
+ wrepl_out_push.o \
+ wrepl_out_helpers.o)
+
+$(eval $(call proto_header_template,$(wrepl_serversrcdir)/wrepl_server_proto.h,$(WREPL_SRV_OBJ_FILES:.o=.c)))
diff --git a/source4/wrepl_server/wrepl_apply_records.c b/source4/wrepl_server/wrepl_apply_records.c
new file mode 100644
index 0000000000..e6ff9a03bf
--- /dev/null
+++ b/source4/wrepl_server/wrepl_apply_records.c
@@ -0,0 +1,1480 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ WINS Replication server
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "smbd/service_task.h"
+#include "lib/messaging/irpc.h"
+#include "librpc/gen_ndr/ndr_irpc.h"
+#include "librpc/gen_ndr/ndr_winsrepl.h"
+#include "wrepl_server/wrepl_server.h"
+#include "nbt_server/wins/winsdb.h"
+#include "libcli/wrepl/winsrepl.h"
+#include "system/time.h"
+#include "librpc/gen_ndr/ndr_nbt.h"
+#include "param/param.h"
+
+enum _R_ACTION {
+ R_INVALID,
+ R_DO_REPLACE,
+ R_NOT_REPLACE,
+ R_DO_PROPAGATE,
+ R_DO_CHALLENGE,
+ R_DO_RELEASE_DEMAND,
+ R_DO_SGROUP_MERGE
+};
+
+static const char *_R_ACTION_enum_string(enum _R_ACTION action)
+{
+ switch (action) {
+ case R_INVALID: return "INVALID";
+ case R_DO_REPLACE: return "REPLACE";
+ case R_NOT_REPLACE: return "NOT_REPLACE";
+ case R_DO_PROPAGATE: return "PROPAGATE";
+ case R_DO_CHALLENGE: return "CHALLEGNE";
+ case R_DO_RELEASE_DEMAND: return "RELEASE_DEMAND";
+ case R_DO_SGROUP_MERGE: return "SGROUP_MERGE";
+ }
+
+ return "enum _R_ACTION unknown";
+}
+
+#define R_IS_ACTIVE(r) ((r)->state == WREPL_STATE_ACTIVE)
+#if 0 /* unused */
+#define R_IS_RELEASED(r) ((r)->state == WREPL_STATE_RELEASED)
+#endif
+#define R_IS_TOMBSTONE(r) ((r)->state == WREPL_STATE_TOMBSTONE)
+
+#define R_IS_UNIQUE(r) ((r)->type == WREPL_TYPE_UNIQUE)
+#define R_IS_GROUP(r) ((r)->type == WREPL_TYPE_GROUP)
+#define R_IS_SGROUP(r) ((r)->type == WREPL_TYPE_SGROUP)
+#if 0 /* unused */
+#define R_IS_MHOMED(r) ((r)->type == WREPL_TYPE_MHOMED)
+#endif
+
+/* blindly overwrite records from the same owner in all cases */
+static enum _R_ACTION replace_same_owner(struct winsdb_record *r1, struct wrepl_name *r2)
+{
+ /* REPLACE */
+ return R_DO_REPLACE;
+}
+
+static bool r_1_is_subset_of_2_address_list(struct winsdb_record *r1, struct wrepl_name *r2, bool check_owners)
+{
+ uint32_t i,j;
+ size_t len = winsdb_addr_list_length(r1->addresses);
+
+ for (i=0; i < len; i++) {
+ bool found = false;
+ for (j=0; j < r2->num_addresses; j++) {
+ if (strcmp(r1->addresses[i]->address, r2->addresses[j].address) != 0) {
+ continue;
+ }
+
+ if (check_owners && strcmp(r1->addresses[i]->wins_owner, r2->addresses[j].owner) != 0) {
+ return false;
+ }
+ found = true;
+ break;
+ }
+ if (!found) return false;
+ }
+
+ return true;
+}
+
+static bool r_1_is_superset_of_2_address_list(struct winsdb_record *r1, struct wrepl_name *r2, bool check_owners)
+{
+ uint32_t i,j;
+ size_t len = winsdb_addr_list_length(r1->addresses);
+
+ for (i=0; i < r2->num_addresses; i++) {
+ bool found = false;
+ for (j=0; j < len; j++) {
+ if (strcmp(r2->addresses[i].address, r1->addresses[j]->address) != 0) {
+ continue;
+ }
+
+ if (check_owners && strcmp(r2->addresses[i].owner, r1->addresses[j]->wins_owner) != 0) {
+ return false;
+ }
+ found = true;
+ break;
+ }
+ if (!found) return false;
+ }
+
+ return true;
+}
+
+static bool r_1_is_same_as_2_address_list(struct winsdb_record *r1, struct wrepl_name *r2, bool check_owners)
+{
+ size_t len = winsdb_addr_list_length(r1->addresses);
+
+ if (len != r2->num_addresses) {
+ return false;
+ }
+
+ return r_1_is_superset_of_2_address_list(r1, r2, check_owners);
+}
+
+static bool r_contains_addrs_from_owner(struct winsdb_record *r1, const char *owner)
+{
+ uint32_t i;
+ size_t len = winsdb_addr_list_length(r1->addresses);
+
+ for (i=0; i < len; i++) {
+ if (strcmp(r1->addresses[i]->wins_owner, owner) == 0) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/*
+UNIQUE,ACTIVE vs. UNIQUE,ACTIVE with different ip(s) => REPLACE
+UNIQUE,ACTIVE vs. UNIQUE,TOMBSTONE with different ip(s) => NOT REPLACE
+UNIQUE,RELEASED vs. UNIQUE,ACTIVE with different ip(s) => REPLACE
+UNIQUE,RELEASED vs. UNIQUE,TOMBSTONE with different ip(s) => REPLACE
+UNIQUE,TOMBSTONE vs. UNIQUE,ACTIVE with different ip(s) => REPLACE
+UNIQUE,TOMBSTONE vs. UNIQUE,TOMBSTONE with different ip(s) => REPLACE
+UNIQUE,ACTIVE vs. GROUP,ACTIVE with different ip(s) => REPLACE
+UNIQUE,ACTIVE vs. GROUP,TOMBSTONE with same ip(s) => NOT REPLACE
+UNIQUE,RELEASED vs. GROUP,ACTIVE with different ip(s) => REPLACE
+UNIQUE,RELEASED vs. GROUP,TOMBSTONE with different ip(s) => REPLACE
+UNIQUE,TOMBSTONE vs. GROUP,ACTIVE with different ip(s) => REPLACE
+UNIQUE,TOMBSTONE vs. GROUP,TOMBSTONE with different ip(s) => REPLACE
+UNIQUE,ACTIVE vs. SGROUP,ACTIVE with same ip(s) => NOT REPLACE
+UNIQUE,ACTIVE vs. SGROUP,TOMBSTONE with same ip(s) => NOT REPLACE
+UNIQUE,RELEASED vs. SGROUP,ACTIVE with different ip(s) => REPLACE
+UNIQUE,RELEASED vs. SGROUP,TOMBSTONE with different ip(s) => REPLACE
+UNIQUE,TOMBSTONE vs. SGROUP,ACTIVE with different ip(s) => REPLACE
+UNIQUE,TOMBSTONE vs. SGROUP,TOMBSTONE with different ip(s) => REPLACE
+UNIQUE,ACTIVE vs. MHOMED,ACTIVE with different ip(s) => REPLACE
+UNIQUE,ACTIVE vs. MHOMED,TOMBSTONE with same ip(s) => NOT REPLACE
+UNIQUE,RELEASED vs. MHOMED,ACTIVE with different ip(s) => REPLACE
+UNIQUE,RELEASED vs. MHOMED,TOMBSTONE with different ip(s) => REPLACE
+UNIQUE,TOMBSTONE vs. MHOMED,ACTIVE with different ip(s) => REPLACE
+UNIQUE,TOMBSTONE vs. MHOMED,TOMBSTONE with different ip(s) => REPLACE
+*/
+static enum _R_ACTION replace_unique_replica_vs_X_replica(struct winsdb_record *r1, struct wrepl_name *r2)
+{
+ if (!R_IS_ACTIVE(r1)) {
+ /* REPLACE */
+ return R_DO_REPLACE;
+ }
+
+ if (!R_IS_SGROUP(r2) && R_IS_ACTIVE(r2)) {
+ /* REPLACE */
+ return R_DO_REPLACE;
+ }
+
+ /* NOT REPLACE */
+ return R_NOT_REPLACE;
+}
+
+/*
+GROUP,ACTIVE vs. UNIQUE,ACTIVE with same ip(s) => NOT REPLACE
+GROUP,ACTIVE vs. UNIQUE,TOMBSTONE with same ip(s) => NOT REPLACE
+GROUP,RELEASED vs. UNIQUE,ACTIVE with same ip(s) => NOT REPLACE
+GROUP,RELEASED vs. UNIQUE,TOMBSTONE with same ip(s) => NOT REPLACE
+GROUP,TOMBSTONE vs. UNIQUE,ACTIVE with same ip(s) => NOT REPLACE
+GROUP,TOMBSTONE vs. UNIQUE,TOMBSTONE with same ip(s) => NOT REPLACE
+GROUP,ACTIVE vs. GROUP,ACTIVE with same ip(s) => NOT REPLACE
+GROUP,ACTIVE vs. GROUP,TOMBSTONE with same ip(s) => NOT REPLACE
+GROUP,RELEASED vs. GROUP,ACTIVE with different ip(s) => REPLACE
+GROUP,RELEASED vs. GROUP,TOMBSTONE with different ip(s) => REPLACE
+GROUP,TOMBSTONE vs. GROUP,ACTIVE with different ip(s) => REPLACE
+GROUP,TOMBSTONE vs. GROUP,TOMBSTONE with different ip(s) => REPLACE
+GROUP,ACTIVE vs. SGROUP,ACTIVE with same ip(s) => NOT REPLACE
+GROUP,ACTIVE vs. SGROUP,TOMBSTONE with same ip(s) => NOT REPLACE
+GROUP,RELEASED vs. SGROUP,ACTIVE with different ip(s) => REPLACE
+GROUP,RELEASED vs. SGROUP,TOMBSTONE with same ip(s) => NOT REPLACE
+GROUP,TOMBSTONE vs. SGROUP,ACTIVE with different ip(s) => REPLACE
+GROUP,TOMBSTONE vs. SGROUP,TOMBSTONE with different ip(s) => REPLACE
+GROUP,ACTIVE vs. MHOMED,ACTIVE with same ip(s) => NOT REPLACE
+GROUP,ACTIVE vs. MHOMED,TOMBSTONE with same ip(s) => NOT REPLACE
+GROUP,RELEASED vs. MHOMED,ACTIVE with same ip(s) => NOT REPLACE
+GROUP,RELEASED vs. MHOMED,TOMBSTONE with same ip(s) => NOT REPLACE
+GROUP,TOMBSTONE vs. MHOMED,ACTIVE with different ip(s) => REPLACE
+GROUP,TOMBSTONE vs. MHOMED,TOMBSTONE with different ip(s) => REPLACE
+*/
+static enum _R_ACTION replace_group_replica_vs_X_replica(struct winsdb_record *r1, struct wrepl_name *r2)
+{
+ if (!R_IS_ACTIVE(r1) && R_IS_GROUP(r2)) {
+ /* REPLACE */
+ return R_DO_REPLACE;
+ }
+
+ if (R_IS_TOMBSTONE(r1) && !R_IS_UNIQUE(r2)) {
+ /* REPLACE */
+ return R_DO_REPLACE;
+ }
+
+ /* NOT REPLACE */
+ return R_NOT_REPLACE;
+}
+
+/*
+SGROUP,ACTIVE vs. UNIQUE,ACTIVE with same ip(s) => NOT REPLACE
+SGROUP,ACTIVE vs. UNIQUE,TOMBSTONE with same ip(s) => NOT REPLACE
+SGROUP,RELEASED vs. UNIQUE,ACTIVE with different ip(s) => REPLACE
+SGROUP,RELEASED vs. UNIQUE,TOMBSTONE with different ip(s) => REPLACE
+SGROUP,TOMBSTONE vs. UNIQUE,ACTIVE with different ip(s) => REPLACE
+SGROUP,TOMBSTONE vs. UNIQUE,TOMBSTONE with different ip(s) => REPLACE
+SGROUP,ACTIVE vs. GROUP,ACTIVE with same ip(s) => NOT REPLACE
+SGROUP,ACTIVE vs. GROUP,TOMBSTONE with same ip(s) => NOT REPLACE
+SGROUP,RELEASED vs. GROUP,ACTIVE with different ip(s) => REPLACE
+SGROUP,RELEASED vs. GROUP,TOMBSTONE with different ip(s) => REPLACE
+SGROUP,TOMBSTONE vs. GROUP,ACTIVE with different ip(s) => REPLACE
+SGROUP,TOMBSTONE vs. GROUP,TOMBSTONE with different ip(s) => REPLACE
+SGROUP,RELEASED vs. SGROUP,ACTIVE with different ip(s) => REPLACE
+SGROUP,RELEASED vs. SGROUP,TOMBSTONE with different ip(s) => REPLACE
+SGROUP,TOMBSTONE vs. SGROUP,ACTIVE with different ip(s) => REPLACE
+SGROUP,TOMBSTONE vs. SGROUP,TOMBSTONE with different ip(s) => REPLACE
+SGROUP,ACTIVE vs. MHOMED,ACTIVE with same ip(s) => NOT REPLACE
+SGROUP,ACTIVE vs. MHOMED,TOMBSTONE with same ip(s) => NOT REPLACE
+SGROUP,RELEASED vs. MHOMED,ACTIVE with different ip(s) => REPLACE
+SGROUP,RELEASED vs. MHOMED,TOMBSTONE with different ip(s) => REPLACE
+SGROUP,TOMBSTONE vs. MHOMED,ACTIVE with different ip(s) => REPLACE
+SGROUP,TOMBSTONE vs. MHOMED,TOMBSTONE with different ip(s) => REPLACE
+
+SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4 vs. B:A_3_4 => NOT REPLACE
+SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4 vs. B:NULL => NOT REPLACE
+SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4_X_3_4 vs. B:A_3_4 => NOT REPLACE
+SGROUP,ACTIVE vs. SGROUP,ACTIVE A:B_3_4 vs. B:A_3_4 => REPLACE
+SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4 vs. B:A_3_4_OWNER_B => REPLACE
+SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4_OWNER_B vs. B:A_3_4 => REPLACE
+
+SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4 vs. B:B_3_4 => C:A_3_4_B_3_4 => SGROUP_MERGE
+SGROUP,ACTIVE vs. SGROUP,ACTIVE A:B_3_4_X_3_4 vs. B:A_3_4 => B:A_3_4_X_3_4 => SGROUP_MERGE
+SGROUP,ACTIVE vs. SGROUP,ACTIVE A:X_3_4 vs. B:A_3_4 => C:A_3_4_X_3_4 => SGROUP_MERGE
+SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4_X_3_4 vs. B:A_3_4_OWNER_B => B:A_3_4_OWNER_B_X_3_4 => SGROUP_MERGE
+SGROUP,ACTIVE vs. SGROUP,ACTIVE A:B_3_4_X_3_4 vs. B:B_3_4_X_1_2 => C:B_3_4_X_1_2_3_4 => SGROUP_MERGE
+SGROUP,ACTIVE vs. SGROUP,ACTIVE A:B_3_4_X_3_4 vs. B:NULL => B:X_3_4 => SGROUP_MERGE
+
+
+this is a bit strange, incoming tombstone replicas always replace old replicas:
+
+SGROUP,ACTIVE vs. SGROUP,TOMBSTONE A:B_3_4_X_3_4 vs. B:NULL => B:NULL => REPLACE
+SGROUP,ACTIVE vs. SGROUP,TOMBSTONE A:B_3_4_X_3_4 vs. B:A_3_4 => B:A_3_4 => REPLACE
+SGROUP,ACTIVE vs. SGROUP,TOMBSTONE A:B_3_4_X_3_4 vs. B:B_3_4 => B:B_3_4 => REPLACE
+SGROUP,ACTIVE vs. SGROUP,TOMBSTONE A:B_3_4_X_3_4 vs. B:B_3_4_X_3_4 => B:B_3_4_X_3_4 => REPLACE
+*/
+static enum _R_ACTION replace_sgroup_replica_vs_X_replica(struct winsdb_record *r1, struct wrepl_name *r2)
+{
+ if (!R_IS_ACTIVE(r1)) {
+ /* REPLACE */
+ return R_DO_REPLACE;
+ }
+
+ if (!R_IS_SGROUP(r2)) {
+ /* NOT REPLACE */
+ return R_NOT_REPLACE;
+ }
+
+ /*
+ * this is strange, but correct
+ * the incoming tombstone replace the current active
+ * record
+ */
+ if (!R_IS_ACTIVE(r2)) {
+ /* REPLACE */
+ return R_DO_REPLACE;
+ }
+
+ if (r2->num_addresses == 0) {
+ if (r_contains_addrs_from_owner(r1, r2->owner)) {
+ /* not handled here: MERGE */
+ return R_DO_SGROUP_MERGE;
+ }
+
+ /* NOT REPLACE */
+ return R_NOT_REPLACE;
+ }
+
+ if (r_1_is_superset_of_2_address_list(r1, r2, true)) {
+ /* NOT REPLACE */
+ return R_NOT_REPLACE;
+ }
+
+ if (r_1_is_same_as_2_address_list(r1, r2, false)) {
+ /* REPLACE */
+ return R_DO_REPLACE;
+ }
+
+ /* not handled here: MERGE */
+ return R_DO_SGROUP_MERGE;
+}
+
+/*
+MHOMED,ACTIVE vs. UNIQUE,ACTIVE with different ip(s) => REPLACE
+MHOMED,ACTIVE vs. UNIQUE,TOMBSTONE with same ip(s) => NOT REPLACE
+MHOMED,RELEASED vs. UNIQUE,ACTIVE with different ip(s) => REPLACE
+MHOMED,RELEASED vs. UNIQUE,TOMBSTONE with different ip(s) => REPLACE
+MHOMED,TOMBSTONE vs. UNIQUE,ACTIVE with different ip(s) => REPLACE
+MHOMED,TOMBSTONE vs. UNIQUE,TOMBSTONE with different ip(s) => REPLACE
+MHOMED,ACTIVE vs. GROUP,ACTIVE with different ip(s) => REPLACE
+MHOMED,ACTIVE vs. GROUP,TOMBSTONE with same ip(s) => NOT REPLACE
+MHOMED,RELEASED vs. GROUP,ACTIVE with different ip(s) => REPLACE
+MHOMED,RELEASED vs. GROUP,TOMBSTONE with different ip(s) => REPLACE
+MHOMED,TOMBSTONE vs. GROUP,ACTIVE with different ip(s) => REPLACE
+MHOMED,TOMBSTONE vs. GROUP,TOMBSTONE with different ip(s) => REPLACE
+MHOMED,ACTIVE vs. SGROUP,ACTIVE with same ip(s) => NOT REPLACE
+MHOMED,ACTIVE vs. SGROUP,TOMBSTONE with same ip(s) => NOT REPLACE
+MHOMED,RELEASED vs. SGROUP,ACTIVE with different ip(s) => REPLACE
+MHOMED,RELEASED vs. SGROUP,TOMBSTONE with different ip(s) => REPLACE
+MHOMED,TOMBSTONE vs. SGROUP,ACTIVE with different ip(s) => REPLACE
+MHOMED,TOMBSTONE vs. SGROUP,TOMBSTONE with different ip(s) => REPLACE
+MHOMED,ACTIVE vs. MHOMED,ACTIVE with different ip(s) => REPLACE
+MHOMED,ACTIVE vs. MHOMED,TOMBSTONE with same ip(s) => NOT REPLACE
+MHOMED,RELEASED vs. MHOMED,ACTIVE with different ip(s) => REPLACE
+MHOMED,RELEASED vs. MHOMED,TOMBSTONE with different ip(s) => REPLACE
+MHOMED,TOMBSTONE vs. MHOMED,ACTIVE with different ip(s) => REPLACE
+MHOMED,TOMBSTONE vs. MHOMED,TOMBSTONE with different ip(s) => REPLACE
+*/
+static enum _R_ACTION replace_mhomed_replica_vs_X_replica(struct winsdb_record *r1, struct wrepl_name *r2)
+{
+ if (!R_IS_ACTIVE(r1)) {
+ /* REPLACE */
+ return R_DO_REPLACE;
+ }
+
+ if (!R_IS_SGROUP(r2) && R_IS_ACTIVE(r2)) {
+ /* REPLACE */
+ return R_DO_REPLACE;
+ }
+
+ /* NOT REPLACE */
+ return R_NOT_REPLACE;
+}
+
+/*
+active:
+_UA_UA_SI_U<00> => REPLACE
+_UA_UA_DI_P<00> => NOT REPLACE
+_UA_UA_DI_O<00> => NOT REPLACE
+_UA_UA_DI_N<00> => REPLACE
+_UA_UT_SI_U<00> => NOT REPLACE
+_UA_UT_DI_U<00> => NOT REPLACE
+_UA_GA_SI_R<00> => REPLACE
+_UA_GA_DI_R<00> => REPLACE
+_UA_GT_SI_U<00> => NOT REPLACE
+_UA_GT_DI_U<00> => NOT REPLACE
+_UA_SA_SI_R<00> => REPLACE
+_UA_SA_DI_R<00> => REPLACE
+_UA_ST_SI_U<00> => NOT REPLACE
+_UA_ST_DI_U<00> => NOT REPLACE
+_UA_MA_SI_U<00> => REPLACE
+_UA_MA_SP_U<00> => REPLACE
+_UA_MA_DI_P<00> => NOT REPLACE
+_UA_MA_DI_O<00> => NOT REPLACE
+_UA_MA_DI_N<00> => REPLACE
+_UA_MT_SI_U<00> => NOT REPLACE
+_UA_MT_DI_U<00> => NOT REPLACE
+Test Replica vs. owned active: some more UNIQUE,MHOMED combinations
+_UA_UA_DI_A<00> => MHOMED_MERGE
+_UA_MA_DI_A<00> => MHOMED_MERGE
+
+released:
+_UR_UA_SI<00> => REPLACE
+_UR_UA_DI<00> => REPLACE
+_UR_UT_SI<00> => REPLACE
+_UR_UT_DI<00> => REPLACE
+_UR_GA_SI<00> => REPLACE
+_UR_GA_DI<00> => REPLACE
+_UR_GT_SI<00> => REPLACE
+_UR_GT_DI<00> => REPLACE
+_UR_SA_SI<00> => REPLACE
+_UR_SA_DI<00> => REPLACE
+_UR_ST_SI<00> => REPLACE
+_UR_ST_DI<00> => REPLACE
+_UR_MA_SI<00> => REPLACE
+_UR_MA_DI<00> => REPLACE
+_UR_MT_SI<00> => REPLACE
+_UR_MT_DI<00> => REPLACE
+*/
+static enum _R_ACTION replace_unique_owned_vs_X_replica(struct winsdb_record *r1, struct wrepl_name *r2)
+{
+ if (!R_IS_ACTIVE(r1)) {
+ /* REPLACE */
+ return R_DO_REPLACE;
+ }
+
+ if (!R_IS_ACTIVE(r2)) {
+ /* NOT REPLACE, and PROPAGATE */
+ return R_DO_PROPAGATE;
+ }
+
+ if (R_IS_GROUP(r2) || R_IS_SGROUP(r2)) {
+ /* REPLACE and send a release demand to the old name owner */
+ return R_DO_RELEASE_DEMAND;
+ }
+
+ /*
+ * here we only have unique,active,owned vs.
+ * is unique,active,replica or mhomed,active,replica
+ */
+
+ if (r_1_is_subset_of_2_address_list(r1, r2, false)) {
+ /*
+ * if r1 has a subset(or same) of the addresses of r2
+ * <=>
+ * if r2 has a superset(or same) of the addresses of r1
+ *
+ * then replace the record
+ */
+ return R_DO_REPLACE;
+ }
+
+ /*
+ * in any other case, we need to do
+ * a name request to the old name holder
+ * to see if it's still there...
+ */
+ return R_DO_CHALLENGE;
+}
+
+/*
+active:
+_GA_UA_SI_U<00> => NOT REPLACE
+_GA_UA_DI_U<00> => NOT REPLACE
+_GA_UT_SI_U<00> => NOT REPLACE
+_GA_UT_DI_U<00> => NOT REPLACE
+_GA_GA_SI_U<00> => REPLACE
+_GA_GA_DI_U<00> => REPLACE
+_GA_GT_SI_U<00> => NOT REPLACE
+_GA_GT_DI_U<00> => NOT REPLACE
+_GA_SA_SI_U<00> => NOT REPLACE
+_GA_SA_DI_U<00> => NOT REPLACE
+_GA_ST_SI_U<00> => NOT REPLACE
+_GA_ST_DI_U<00> => NOT REPLACE
+_GA_MA_SI_U<00> => NOT REPLACE
+_GA_MA_DI_U<00> => NOT REPLACE
+_GA_MT_SI_U<00> => NOT REPLACE
+_GA_MT_DI_U<00> => NOT REPLACE
+
+released:
+_GR_UA_SI<00> => NOT REPLACE
+_GR_UA_DI<00> => NOT REPLACE
+_GR_UT_SI<00> => NOT REPLACE
+_GR_UT_DI<00> => NOT REPLACE
+_GR_GA_SI<00> => REPLACE
+_GR_GA_DI<00> => REPLACE
+_GR_GT_SI<00> => REPLACE
+_GR_GT_DI<00> => REPLACE
+_GR_SA_SI<00> => NOT REPLACE
+_GR_SA_DI<00> => NOT REPLACE
+_GR_ST_SI<00> => NOT REPLACE
+_GR_ST_DI<00> => NOT REPLACE
+_GR_MA_SI<00> => NOT REPLACE
+_GR_MA_DI<00> => NOT REPLACE
+_GR_MT_SI<00> => NOT REPLACE
+_GR_MT_DI<00> => NOT REPLACE
+*/
+static enum _R_ACTION replace_group_owned_vs_X_replica(struct winsdb_record *r1, struct wrepl_name *r2)
+{
+ if (R_IS_GROUP(r1) && R_IS_GROUP(r2)) {
+ if (!R_IS_ACTIVE(r1) || R_IS_ACTIVE(r2)) {
+ /* REPLACE */
+ return R_DO_REPLACE;
+ }
+ }
+
+ /* NOT REPLACE, but PROPAGATE */
+ return R_DO_PROPAGATE;
+}
+
+/*
+active (not sgroup vs. sgroup yet!):
+_SA_UA_SI_U<1c> => NOT REPLACE
+_SA_UA_DI_U<1c> => NOT REPLACE
+_SA_UT_SI_U<1c> => NOT REPLACE
+_SA_UT_DI_U<1c> => NOT REPLACE
+_SA_GA_SI_U<1c> => NOT REPLACE
+_SA_GA_DI_U<1c> => NOT REPLACE
+_SA_GT_SI_U<1c> => NOT REPLACE
+_SA_GT_DI_U<1c> => NOT REPLACE
+_SA_MA_SI_U<1c> => NOT REPLACE
+_SA_MA_DI_U<1c> => NOT REPLACE
+_SA_MT_SI_U<1c> => NOT REPLACE
+_SA_MT_DI_U<1c> => NOT REPLACE
+
+Test Replica vs. owned active: SGROUP vs. SGROUP tests
+_SA_SA_DI_U<1c> => SGROUP_MERGE
+_SA_SA_SI_U<1c> => SGROUP_MERGE
+_SA_SA_SP_U<1c> => SGROUP_MERGE
+_SA_SA_SB_U<1c> => SGROUP_MERGE
+_SA_ST_DI_U<1c> => NOT REPLACE
+_SA_ST_SI_U<1c> => NOT REPLACE
+_SA_ST_SP_U<1c> => NOT REPLACE
+_SA_ST_SB_U<1c> => NOT REPLACE
+
+SGROUP,ACTIVE vs. SGROUP,* is not handled here!
+
+released:
+_SR_UA_SI<1c> => REPLACE
+_SR_UA_DI<1c> => REPLACE
+_SR_UT_SI<1c> => REPLACE
+_SR_UT_DI<1c> => REPLACE
+_SR_GA_SI<1c> => REPLACE
+_SR_GA_DI<1c> => REPLACE
+_SR_GT_SI<1c> => REPLACE
+_SR_GT_DI<1c> => REPLACE
+_SR_SA_SI<1c> => REPLACE
+_SR_SA_DI<1c> => REPLACE
+_SR_ST_SI<1c> => REPLACE
+_SR_ST_DI<1c> => REPLACE
+_SR_MA_SI<1c> => REPLACE
+_SR_MA_DI<1c> => REPLACE
+_SR_MT_SI<1c> => REPLACE
+_SR_MT_DI<1c> => REPLACE
+*/
+static enum _R_ACTION replace_sgroup_owned_vs_X_replica(struct winsdb_record *r1, struct wrepl_name *r2)
+{
+ if (!R_IS_ACTIVE(r1)) {
+ /* REPLACE */
+ return R_DO_REPLACE;
+ }
+
+ if (!R_IS_SGROUP(r2) || !R_IS_ACTIVE(r2)) {
+ /* NOT REPLACE, but PROPAGATE */
+ return R_DO_PROPAGATE;
+ }
+
+ if (r_1_is_same_as_2_address_list(r1, r2, true)) {
+ /*
+ * as we're the old owner and the addresses and their
+ * owners are identical
+ */
+ return R_NOT_REPLACE;
+ }
+
+ /* not handled here: MERGE */
+ return R_DO_SGROUP_MERGE;
+}
+
+/*
+active:
+_MA_UA_SI_U<00> => REPLACE
+_MA_UA_DI_P<00> => NOT REPLACE
+_MA_UA_DI_O<00> => NOT REPLACE
+_MA_UA_DI_N<00> => REPLACE
+_MA_UT_SI_U<00> => NOT REPLACE
+_MA_UT_DI_U<00> => NOT REPLACE
+_MA_GA_SI_R<00> => REPLACE
+_MA_GA_DI_R<00> => REPLACE
+_MA_GT_SI_U<00> => NOT REPLACE
+_MA_GT_DI_U<00> => NOT REPLACE
+_MA_SA_SI_R<00> => REPLACE
+_MA_SA_DI_R<00> => REPLACE
+_MA_ST_SI_U<00> => NOT REPLACE
+_MA_ST_DI_U<00> => NOT REPLACE
+_MA_MA_SI_U<00> => REPLACE
+_MA_MA_SP_U<00> => REPLACE
+_MA_MA_DI_P<00> => NOT REPLACE
+_MA_MA_DI_O<00> => NOT REPLACE
+_MA_MA_DI_N<00> => REPLACE
+_MA_MT_SI_U<00> => NOT REPLACE
+_MA_MT_DI_U<00> => NOT REPLACE
+Test Replica vs. owned active: some more MHOMED combinations
+_MA_MA_SP_U<00> => REPLACE
+_MA_MA_SM_U<00> => REPLACE
+_MA_MA_SB_P<00> => MHOMED_MERGE
+_MA_MA_SB_A<00> => MHOMED_MERGE
+_MA_MA_SB_PRA<00> => NOT REPLACE
+_MA_MA_SB_O<00> => NOT REPLACE
+_MA_MA_SB_N<00> => REPLACE
+Test Replica vs. owned active: some more UNIQUE,MHOMED combinations
+_MA_UA_SB_P<00> => MHOMED_MERGE
+
+released:
+_MR_UA_SI<00> => REPLACE
+_MR_UA_DI<00> => REPLACE
+_MR_UT_SI<00> => REPLACE
+_MR_UT_DI<00> => REPLACE
+_MR_GA_SI<00> => REPLACE
+_MR_GA_DI<00> => REPLACE
+_MR_GT_SI<00> => REPLACE
+_MR_GT_DI<00> => REPLACE
+_MR_SA_SI<00> => REPLACE
+_MR_SA_DI<00> => REPLACE
+_MR_ST_SI<00> => REPLACE
+_MR_ST_DI<00> => REPLACE
+_MR_MA_SI<00> => REPLACE
+_MR_MA_DI<00> => REPLACE
+_MR_MT_SI<00> => REPLACE
+_MR_MT_DI<00> => REPLACE
+*/
+static enum _R_ACTION replace_mhomed_owned_vs_X_replica(struct winsdb_record *r1, struct wrepl_name *r2)
+{
+ if (!R_IS_ACTIVE(r1)) {
+ /* REPLACE */
+ return R_DO_REPLACE;
+ }
+
+ if (!R_IS_ACTIVE(r2)) {
+ /* NOT REPLACE, but PROPAGATE */
+ return R_DO_PROPAGATE;
+ }
+
+ if (R_IS_GROUP(r2) || R_IS_SGROUP(r2)) {
+ /* REPLACE and send a release demand to the old name owner */
+ return R_DO_RELEASE_DEMAND;
+ }
+
+ /*
+ * here we only have mhomed,active,owned vs.
+ * is unique,active,replica or mhomed,active,replica
+ */
+
+ if (r_1_is_subset_of_2_address_list(r1, r2, false)) {
+ /*
+ * if r1 has a subset(or same) of the addresses of r2
+ * <=>
+ * if r2 has a superset(or same) of the addresses of r1
+ *
+ * then replace the record
+ */
+ return R_DO_REPLACE;
+ }
+
+ /*
+ * in any other case, we need to do
+ * a name request to the old name holder
+ * to see if it's still there...
+ */
+ return R_DO_CHALLENGE;
+}
+
+static NTSTATUS r_do_add(struct wreplsrv_partner *partner,
+ TALLOC_CTX *mem_ctx,
+ struct wrepl_wins_owner *owner,
+ struct wrepl_name *replica)
+{
+ struct winsdb_record *rec;
+ uint32_t i;
+ uint8_t ret;
+
+ rec = talloc(mem_ctx, struct winsdb_record);
+ NT_STATUS_HAVE_NO_MEMORY(rec);
+
+ rec->name = &replica->name;
+ rec->type = replica->type;
+ rec->state = replica->state;
+ rec->node = replica->node;
+ rec->is_static = replica->is_static;
+ rec->expire_time= time(NULL) + partner->service->config.verify_interval;
+ rec->version = replica->version_id;
+ rec->wins_owner = replica->owner;
+ rec->addresses = winsdb_addr_list_make(rec);
+ NT_STATUS_HAVE_NO_MEMORY(rec->addresses);
+ rec->registered_by = NULL;
+
+ for (i=0; i < replica->num_addresses; i++) {
+ /* TODO: find out if rec->expire_time is correct here */
+ rec->addresses = winsdb_addr_list_add(partner->service->wins_db,
+ rec, rec->addresses,
+ replica->addresses[i].address,
+ replica->addresses[i].owner,
+ rec->expire_time,
+ false);
+ NT_STATUS_HAVE_NO_MEMORY(rec->addresses);
+ }
+
+ ret = winsdb_add(partner->service->wins_db, rec, 0);
+ if (ret != NBT_RCODE_OK) {
+ DEBUG(0,("Failed to add record %s: %u\n",
+ nbt_name_string(mem_ctx, &replica->name), ret));
+ return NT_STATUS_FOOBAR;
+ }
+
+ DEBUG(4,("added record %s\n",
+ nbt_name_string(mem_ctx, &replica->name)));
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS r_do_replace(struct wreplsrv_partner *partner,
+ TALLOC_CTX *mem_ctx,
+ struct winsdb_record *rec,
+ struct wrepl_wins_owner *owner,
+ struct wrepl_name *replica)
+{
+ uint32_t i;
+ uint8_t ret;
+
+ rec->name = &replica->name;
+ rec->type = replica->type;
+ rec->state = replica->state;
+ rec->node = replica->node;
+ rec->is_static = replica->is_static;
+ rec->expire_time= time(NULL) + partner->service->config.verify_interval;
+ rec->version = replica->version_id;
+ rec->wins_owner = replica->owner;
+ rec->addresses = winsdb_addr_list_make(rec);
+ NT_STATUS_HAVE_NO_MEMORY(rec->addresses);
+ rec->registered_by = NULL;
+
+ for (i=0; i < replica->num_addresses; i++) {
+ /* TODO: find out if rec->expire_time is correct here */
+ rec->addresses = winsdb_addr_list_add(partner->service->wins_db,
+ rec, rec->addresses,
+ replica->addresses[i].address,
+ replica->addresses[i].owner,
+ rec->expire_time,
+ false);
+ NT_STATUS_HAVE_NO_MEMORY(rec->addresses);
+ }
+
+ ret = winsdb_modify(partner->service->wins_db, rec, 0);
+ if (ret != NBT_RCODE_OK) {
+ DEBUG(0,("Failed to replace record %s: %u\n",
+ nbt_name_string(mem_ctx, &replica->name), ret));
+ return NT_STATUS_FOOBAR;
+ }
+
+ DEBUG(4,("replaced record %s\n",
+ nbt_name_string(mem_ctx, &replica->name)));
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS r_not_replace(struct wreplsrv_partner *partner,
+ TALLOC_CTX *mem_ctx,
+ struct winsdb_record *rec,
+ struct wrepl_wins_owner *owner,
+ struct wrepl_name *replica)
+{
+ DEBUG(4,("not replace record %s\n",
+ nbt_name_string(mem_ctx, &replica->name)));
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS r_do_propagate(struct wreplsrv_partner *partner,
+ TALLOC_CTX *mem_ctx,
+ struct winsdb_record *rec,
+ struct wrepl_wins_owner *owner,
+ struct wrepl_name *replica)
+{
+ uint8_t ret;
+ uint32_t modify_flags;
+
+ /*
+ * allocate a new version id for the record to that it'll be replicated
+ */
+ modify_flags = WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP;
+
+ ret = winsdb_modify(partner->service->wins_db, rec, modify_flags);
+ if (ret != NBT_RCODE_OK) {
+ DEBUG(0,("Failed to replace record %s: %u\n",
+ nbt_name_string(mem_ctx, &replica->name), ret));
+ return NT_STATUS_FOOBAR;
+ }
+
+ DEBUG(4,("propagated record %s\n",
+ nbt_name_string(mem_ctx, &replica->name)));
+
+ return NT_STATUS_OK;
+}
+
+/*
+Test Replica vs. owned active: some more MHOMED combinations
+_MA_MA_SP_U<00>: C:MHOMED vs. B:ALL => B:ALL => REPLACE
+_MA_MA_SM_U<00>: C:MHOMED vs. B:MHOMED => B:MHOMED => REPLACE
+_MA_MA_SB_P<00>: C:MHOMED vs. B:BEST (C:MHOMED) => B:MHOMED => MHOMED_MERGE
+_MA_MA_SB_A<00>: C:MHOMED vs. B:BEST (C:ALL) => B:MHOMED => MHOMED_MERGE
+_MA_MA_SB_PRA<00>: C:MHOMED vs. B:BEST (C:BEST) => C:MHOMED => NOT REPLACE
+_MA_MA_SB_O<00>: C:MHOMED vs. B:BEST (B:B_3_4) =>C:MHOMED => NOT REPLACE
+_MA_MA_SB_N<00>: C:MHOMED vs. B:BEST (NEGATIVE) => B:BEST => REPLACE
+Test Replica vs. owned active: some more UNIQUE,MHOMED combinations
+_MA_UA_SB_P<00>: C:MHOMED vs. B:UNIQUE,BEST (C:MHOMED) => B:MHOMED => MHOMED_MERGE
+_UA_UA_DI_PRA<00>: C:BEST vs. B:BEST2 (C:BEST2,LR:BEST2) => C:BEST => NOT REPLACE
+_UA_UA_DI_A<00>: C:BEST vs. B:BEST2 (C:ALL) => B:MHOMED => MHOMED_MERGE
+_UA_MA_DI_A<00>: C:BEST vs. B:BEST2 (C:ALL) => B:MHOMED => MHOMED_MERGE
+*/
+static NTSTATUS r_do_mhomed_merge(struct wreplsrv_partner *partner,
+ TALLOC_CTX *mem_ctx,
+ struct winsdb_record *rec,
+ struct wrepl_wins_owner *owner,
+ struct wrepl_name *replica)
+{
+ struct winsdb_record *merge;
+ uint32_t i,j;
+ uint8_t ret;
+ size_t len;
+
+ merge = talloc(mem_ctx, struct winsdb_record);
+ NT_STATUS_HAVE_NO_MEMORY(merge);
+
+ merge->name = &replica->name;
+ merge->type = WREPL_TYPE_MHOMED;
+ merge->state = replica->state;
+ merge->node = replica->node;
+ merge->is_static = replica->is_static;
+ merge->expire_time = time(NULL) + partner->service->config.verify_interval;
+ merge->version = replica->version_id;
+ merge->wins_owner = replica->owner;
+ merge->addresses = winsdb_addr_list_make(merge);
+ NT_STATUS_HAVE_NO_MEMORY(merge->addresses);
+ merge->registered_by = NULL;
+
+ for (i=0; i < replica->num_addresses; i++) {
+ merge->addresses = winsdb_addr_list_add(partner->service->wins_db,
+ merge, merge->addresses,
+ replica->addresses[i].address,
+ replica->addresses[i].owner,
+ merge->expire_time,
+ false);
+ NT_STATUS_HAVE_NO_MEMORY(merge->addresses);
+ }
+
+ len = winsdb_addr_list_length(rec->addresses);
+
+ for (i=0; i < len; i++) {
+ bool found = false;
+ for (j=0; j < replica->num_addresses; j++) {
+ if (strcmp(replica->addresses[j].address, rec->addresses[i]->address) == 0) {
+ found = true;
+ break;
+ }
+ }
+ if (found) continue;
+
+ merge->addresses = winsdb_addr_list_add(partner->service->wins_db,
+ merge, merge->addresses,
+ rec->addresses[i]->address,
+ rec->addresses[i]->wins_owner,
+ rec->addresses[i]->expire_time,
+ false);
+ NT_STATUS_HAVE_NO_MEMORY(merge->addresses);
+ }
+
+ ret = winsdb_modify(partner->service->wins_db, merge, 0);
+ if (ret != NBT_RCODE_OK) {
+ DEBUG(0,("Failed to modify mhomed merge record %s: %u\n",
+ nbt_name_string(mem_ctx, &replica->name), ret));
+ return NT_STATUS_FOOBAR;
+ }
+
+ DEBUG(4,("mhomed merge record %s\n",
+ nbt_name_string(mem_ctx, &replica->name)));
+
+ return NT_STATUS_OK;
+}
+
+struct r_do_challenge_state {
+ struct messaging_context *msg_ctx;
+ struct wreplsrv_partner *partner;
+ struct winsdb_record *rec;
+ struct wrepl_wins_owner owner;
+ struct wrepl_name replica;
+ struct nbtd_proxy_wins_challenge r;
+};
+
+static void r_do_late_release_demand_handler(struct irpc_request *ireq)
+{
+ NTSTATUS status;
+ struct r_do_challenge_state *state = talloc_get_type(ireq->async.private_data,
+ struct r_do_challenge_state);
+
+ status = irpc_call_recv(ireq);
+ /* don't care about the result */
+ talloc_free(state);
+}
+
+static NTSTATUS r_do_late_release_demand(struct r_do_challenge_state *state)
+{
+ struct irpc_request *ireq;
+ struct server_id *nbt_servers;
+ struct nbtd_proxy_wins_release_demand r;
+ uint32_t i;
+
+ DEBUG(4,("late release demand record %s\n",
+ nbt_name_string(state, &state->replica.name)));
+
+ nbt_servers = irpc_servers_byname(state->msg_ctx, state, "nbt_server");
+ if ((nbt_servers == NULL) || (nbt_servers[0].id == 0)) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ r.in.name = state->replica.name;
+ r.in.num_addrs = state->r.out.num_addrs;
+ r.in.addrs = talloc_array(state, struct nbtd_proxy_wins_addr, r.in.num_addrs);
+ NT_STATUS_HAVE_NO_MEMORY(r.in.addrs);
+ /* TODO: fix pidl to handle inline ipv4address arrays */
+ for (i=0; i < r.in.num_addrs; i++) {
+ r.in.addrs[i].addr = state->r.out.addrs[i].addr;
+ }
+
+ ireq = IRPC_CALL_SEND(state->msg_ctx, nbt_servers[0],
+ irpc, NBTD_PROXY_WINS_RELEASE_DEMAND,
+ &r, state);
+ NT_STATUS_HAVE_NO_MEMORY(ireq);
+
+ ireq->async.fn = r_do_late_release_demand_handler;
+ ireq->async.private_data= state;
+
+ return NT_STATUS_OK;
+}
+
+/*
+Test Replica vs. owned active: some more MHOMED combinations
+_MA_MA_SP_U<00>: C:MHOMED vs. B:ALL => B:ALL => REPLACE
+_MA_MA_SM_U<00>: C:MHOMED vs. B:MHOMED => B:MHOMED => REPLACE
+_MA_MA_SB_P<00>: C:MHOMED vs. B:BEST (C:MHOMED) => B:MHOMED => MHOMED_MERGE
+_MA_MA_SB_A<00>: C:MHOMED vs. B:BEST (C:ALL) => B:MHOMED => MHOMED_MERGE
+_MA_MA_SB_PRA<00>: C:MHOMED vs. B:BEST (C:BEST) => C:MHOMED => NOT REPLACE
+_MA_MA_SB_O<00>: C:MHOMED vs. B:BEST (B:B_3_4) =>C:MHOMED => NOT REPLACE
+_MA_MA_SB_N<00>: C:MHOMED vs. B:BEST (NEGATIVE) => B:BEST => REPLACE
+Test Replica vs. owned active: some more UNIQUE,MHOMED combinations
+_MA_UA_SB_P<00>: C:MHOMED vs. B:UNIQUE,BEST (C:MHOMED) => B:MHOMED => MHOMED_MERGE
+_UA_UA_DI_PRA<00>: C:BEST vs. B:BEST2 (C:BEST2,LR:BEST2) => C:BEST => NOT REPLACE
+_UA_UA_DI_A<00>: C:BEST vs. B:BEST2 (C:ALL) => B:MHOMED => MHOMED_MERGE
+_UA_MA_DI_A<00>: C:BEST vs. B:BEST2 (C:ALL) => B:MHOMED => MHOMED_MERGE
+*/
+static void r_do_challenge_handler(struct irpc_request *ireq)
+{
+ NTSTATUS status;
+ struct r_do_challenge_state *state = talloc_get_type(ireq->async.private_data,
+ struct r_do_challenge_state);
+ bool old_is_subset = false;
+ bool new_is_subset = false;
+ bool found = false;
+ uint32_t i,j;
+ uint32_t num_rec_addrs;
+
+ status = irpc_call_recv(ireq);
+
+ DEBUG(4,("r_do_challenge_handler: %s: %s\n",
+ nbt_name_string(state, &state->replica.name), nt_errstr(status)));
+
+ if (NT_STATUS_EQUAL(NT_STATUS_IO_TIMEOUT, status) ||
+ NT_STATUS_EQUAL(NT_STATUS_OBJECT_NAME_NOT_FOUND, status)) {
+ r_do_replace(state->partner, state, state->rec, &state->owner, &state->replica);
+ talloc_free(state);
+ return;
+ }
+
+ for (i=0; i < state->replica.num_addresses; i++) {
+ found = false;
+ new_is_subset = true;
+ for (j=0; j < state->r.out.num_addrs; j++) {
+ if (strcmp(state->replica.addresses[i].address, state->r.out.addrs[j].addr) == 0) {
+ found = true;
+ break;
+ }
+ }
+ if (found) continue;
+
+ new_is_subset = false;
+ break;
+ }
+
+ if (!new_is_subset) {
+ r_not_replace(state->partner, state, state->rec, &state->owner, &state->replica);
+ talloc_free(state);
+ return;
+ }
+
+ num_rec_addrs = winsdb_addr_list_length(state->rec->addresses);
+ for (i=0; i < num_rec_addrs; i++) {
+ found = false;
+ old_is_subset = true;
+ for (j=0; j < state->r.out.num_addrs; j++) {
+ if (strcmp(state->rec->addresses[i]->address, state->r.out.addrs[j].addr) == 0) {
+ found = true;
+ break;
+ }
+ }
+ if (found) continue;
+
+ old_is_subset = false;
+ break;
+ }
+
+ if (!old_is_subset) {
+ status = r_do_late_release_demand(state);
+ /*
+ * only free state on error, because we pass it down,
+ * and r_do_late_release_demand() will free it
+ */
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(state);
+ }
+ return;
+ }
+
+ r_do_mhomed_merge(state->partner, state, state->rec, &state->owner, &state->replica);
+ talloc_free(state);
+}
+
+static NTSTATUS r_do_challenge(struct wreplsrv_partner *partner,
+ TALLOC_CTX *mem_ctx,
+ struct winsdb_record *rec,
+ struct wrepl_wins_owner *owner,
+ struct wrepl_name *replica)
+{
+ struct irpc_request *ireq;
+ struct r_do_challenge_state *state;
+ struct server_id *nbt_servers;
+ const char **addrs;
+ uint32_t i;
+
+ DEBUG(4,("challenge record %s\n",
+ nbt_name_string(mem_ctx, &replica->name)));
+
+ state = talloc_zero(mem_ctx, struct r_do_challenge_state);
+ NT_STATUS_HAVE_NO_MEMORY(state);
+ state->msg_ctx = partner->service->task->msg_ctx;
+ state->partner = partner;
+ state->rec = talloc_steal(state, rec);
+ state->owner = *owner;
+ state->replica = *replica;
+ /* some stuff to have valid memory pointers in the async complete function */
+ state->replica.name = *state->rec->name;
+ talloc_steal(state, replica->owner);
+ talloc_steal(state, replica->addresses);
+
+ nbt_servers = irpc_servers_byname(state->msg_ctx, state, "nbt_server");
+ if ((nbt_servers == NULL) || (nbt_servers[0].id == 0)) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ state->r.in.name = *rec->name;
+ state->r.in.num_addrs = winsdb_addr_list_length(rec->addresses);
+ state->r.in.addrs = talloc_array(state, struct nbtd_proxy_wins_addr, state->r.in.num_addrs);
+ NT_STATUS_HAVE_NO_MEMORY(state->r.in.addrs);
+ /* TODO: fix pidl to handle inline ipv4address arrays */
+ addrs = winsdb_addr_string_list(state->r.in.addrs, rec->addresses);
+ NT_STATUS_HAVE_NO_MEMORY(addrs);
+ for (i=0; i < state->r.in.num_addrs; i++) {
+ state->r.in.addrs[i].addr = addrs[i];
+ }
+
+ ireq = IRPC_CALL_SEND(state->msg_ctx, nbt_servers[0],
+ irpc, NBTD_PROXY_WINS_CHALLENGE,
+ &state->r, state);
+ NT_STATUS_HAVE_NO_MEMORY(ireq);
+
+ ireq->async.fn = r_do_challenge_handler;
+ ireq->async.private_data= state;
+
+ talloc_steal(partner, state);
+ return NT_STATUS_OK;
+}
+
+struct r_do_release_demand_state {
+ struct messaging_context *msg_ctx;
+ struct nbtd_proxy_wins_release_demand r;
+};
+
+static void r_do_release_demand_handler(struct irpc_request *ireq)
+{
+ NTSTATUS status;
+ struct r_do_release_demand_state *state = talloc_get_type(ireq->async.private_data,
+ struct r_do_release_demand_state);
+
+ status = irpc_call_recv(ireq);
+ /* don't care about the result */
+ talloc_free(state);
+}
+
+static NTSTATUS r_do_release_demand(struct wreplsrv_partner *partner,
+ TALLOC_CTX *mem_ctx,
+ struct winsdb_record *rec,
+ struct wrepl_wins_owner *owner,
+ struct wrepl_name *replica)
+{
+ NTSTATUS status;
+ struct irpc_request *ireq;
+ struct server_id *nbt_servers;
+ const char **addrs;
+ struct winsdb_addr **addresses;
+ struct r_do_release_demand_state *state;
+ uint32_t i;
+
+ /*
+ * we need to get a reference to the old addresses,
+ * as we need to send a release demand to them after replacing the record
+ * and r_do_replace() will modify rec->addresses
+ */
+ addresses = rec->addresses;
+
+ status = r_do_replace(partner, mem_ctx, rec, owner, replica);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ DEBUG(4,("release demand record %s\n",
+ nbt_name_string(mem_ctx, &replica->name)));
+
+ state = talloc_zero(mem_ctx, struct r_do_release_demand_state);
+ NT_STATUS_HAVE_NO_MEMORY(state);
+ state->msg_ctx = partner->service->task->msg_ctx;
+
+ nbt_servers = irpc_servers_byname(state->msg_ctx, state, "nbt_server");
+ if ((nbt_servers == NULL) || (nbt_servers[0].id == 0)) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ state->r.in.name = *rec->name;
+ state->r.in.num_addrs = winsdb_addr_list_length(addresses);
+ state->r.in.addrs = talloc_array(state, struct nbtd_proxy_wins_addr,
+ state->r.in.num_addrs);
+ NT_STATUS_HAVE_NO_MEMORY(state->r.in.addrs);
+ /* TODO: fix pidl to handle inline ipv4address arrays */
+ addrs = winsdb_addr_string_list(state->r.in.addrs, addresses);
+ NT_STATUS_HAVE_NO_MEMORY(addrs);
+ for (i=0; i < state->r.in.num_addrs; i++) {
+ state->r.in.addrs[i].addr = addrs[i];
+ }
+
+ ireq = IRPC_CALL_SEND(state->msg_ctx, nbt_servers[0],
+ irpc, NBTD_PROXY_WINS_RELEASE_DEMAND,
+ &state->r, state);
+ NT_STATUS_HAVE_NO_MEMORY(ireq);
+
+ ireq->async.fn = r_do_release_demand_handler;
+ ireq->async.private_data= state;
+
+ talloc_steal(partner, state);
+ return NT_STATUS_OK;
+}
+
+/*
+SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4 vs. B:A_3_4 => NOT REPLACE
+SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4 vs. B:NULL => NOT REPLACE
+SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4_X_3_4 vs. B:A_3_4 => NOT REPLACE
+SGROUP,ACTIVE vs. SGROUP,ACTIVE A:B_3_4 vs. B:A_3_4 => REPLACE
+SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4 vs. B:A_3_4_OWNER_B => REPLACE
+SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4_OWNER_B vs. B:A_3_4 => REPLACE
+
+SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4 vs. B:B_3_4 => C:A_3_4_B_3_4 => SGROUP_MERGE
+SGROUP,ACTIVE vs. SGROUP,ACTIVE A:B_3_4_X_3_4 vs. B:A_3_4 => B:A_3_4_X_3_4 => SGROUP_MERGE
+SGROUP,ACTIVE vs. SGROUP,ACTIVE A:X_3_4 vs. B:A_3_4 => C:A_3_4_X_3_4 => SGROUP_MERGE
+SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4_X_3_4 vs. B:A_3_4_OWNER_B => B:A_3_4_OWNER_B_X_3_4 => SGROUP_MERGE
+SGROUP,ACTIVE vs. SGROUP,ACTIVE A:B_3_4_X_3_4 vs. B:B_3_4_X_1_2 => C:B_3_4_X_1_2_3_4 => SGROUP_MERGE
+SGROUP,ACTIVE vs. SGROUP,ACTIVE A:B_3_4_X_3_4 vs. B:NULL => B:X_3_4 => SGROUP_MERGE
+
+Test Replica vs. owned active: SGROUP vs. SGROUP tests
+_SA_SA_DI_U<1c> => SGROUP_MERGE
+_SA_SA_SI_U<1c> => SGROUP_MERGE
+_SA_SA_SP_U<1c> => SGROUP_MERGE
+_SA_SA_SB_U<1c> => SGROUP_MERGE
+*/
+static NTSTATUS r_do_sgroup_merge(struct wreplsrv_partner *partner,
+ TALLOC_CTX *mem_ctx,
+ struct winsdb_record *rec,
+ struct wrepl_wins_owner *owner,
+ struct wrepl_name *replica)
+{
+ struct winsdb_record *merge;
+ uint32_t modify_flags = 0;
+ uint32_t i,j;
+ uint8_t ret;
+ size_t len;
+ bool changed_old_addrs = false;
+ bool skip_replica_owned_by_us = false;
+ bool become_owner = true;
+ bool propagate = lp_parm_bool(partner->service->task->lp_ctx, NULL, "wreplsrv", "propagate name releases", false);
+ const char *local_owner = partner->service->wins_db->local_owner;
+
+ merge = talloc(mem_ctx, struct winsdb_record);
+ NT_STATUS_HAVE_NO_MEMORY(merge);
+
+ merge->name = &replica->name;
+ merge->type = replica->type;
+ merge->state = replica->state;
+ merge->node = replica->node;
+ merge->is_static = replica->is_static;
+ merge->expire_time = time(NULL) + partner->service->config.verify_interval;
+ merge->version = replica->version_id;
+ merge->wins_owner = replica->owner;
+ merge->addresses = winsdb_addr_list_make(merge);
+ NT_STATUS_HAVE_NO_MEMORY(merge->addresses);
+ merge->registered_by = NULL;
+
+ len = winsdb_addr_list_length(rec->addresses);
+
+ for (i=0; i < len; i++) {
+ bool found = false;
+
+ for (j=0; j < replica->num_addresses; j++) {
+ if (strcmp(rec->addresses[i]->address, replica->addresses[j].address) != 0) {
+ continue;
+ }
+
+ found = true;
+
+ if (strcmp(rec->addresses[i]->wins_owner, replica->addresses[j].owner) != 0) {
+ changed_old_addrs = true;
+ break;
+ }
+ break;
+ }
+
+ /*
+ * if the address isn't in the replica and is owned by replicas owner,
+ * it won't be added to the merged record
+ */
+ if (!found && strcmp(rec->addresses[i]->wins_owner, owner->address) == 0) {
+ changed_old_addrs = true;
+ continue;
+ }
+
+ /*
+ * add the address to the merge result, with the old owner and expire_time,
+ * the owner and expire_time will be overwritten later if the address is
+ * in the replica too
+ */
+ merge->addresses = winsdb_addr_list_add(partner->service->wins_db,
+ merge, merge->addresses,
+ rec->addresses[i]->address,
+ rec->addresses[i]->wins_owner,
+ rec->addresses[i]->expire_time,
+ false);
+ NT_STATUS_HAVE_NO_MEMORY(merge->addresses);
+ }
+
+ for (i=0; i < replica->num_addresses; i++) {
+ if (propagate &&
+ strcmp(replica->addresses[i].owner, local_owner) == 0) {
+ const struct winsdb_addr *a;
+
+ /*
+ * NOTE: this is different to the windows behavior
+ * and off by default, but it better propagated
+ * name releases
+ */
+ a = winsdb_addr_list_check(merge->addresses,
+ replica->addresses[i].address);
+ if (!a) {
+ /* don't add addresses owned by us */
+ skip_replica_owned_by_us = true;
+ }
+ continue;
+ }
+ merge->addresses = winsdb_addr_list_add(partner->service->wins_db,
+ merge, merge->addresses,
+ replica->addresses[i].address,
+ replica->addresses[i].owner,
+ merge->expire_time,
+ false);
+ NT_STATUS_HAVE_NO_MEMORY(merge->addresses);
+ }
+
+ /* we the old addresses change changed we don't become the owner */
+ if (changed_old_addrs) {
+ become_owner = false;
+ }
+
+ /*
+ * when we notice another server believes an address
+ * is owned by us and that's not the case
+ * we propagate the result
+ */
+ if (skip_replica_owned_by_us) {
+ become_owner = true;
+ }
+
+ /* if we're the owner of the old record, we'll be the owner of the new one too */
+ if (strcmp(rec->wins_owner, local_owner)==0) {
+ become_owner = true;
+ }
+
+ /*
+ * if the result has no addresses we take the ownership
+ */
+ len = winsdb_addr_list_length(merge->addresses);
+ if (len == 0) {
+ become_owner = true;
+ }
+
+ /*
+ * if addresses of the old record will be changed the replica owner
+ * will be owner of the merge result, otherwise we take the ownership
+ */
+ if (become_owner) {
+ time_t lh = 0;
+
+ modify_flags = WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP;
+
+ /*
+ * if we're the owner, the expire time becomes the highest
+ * expire time of owned addresses
+ */
+ len = winsdb_addr_list_length(merge->addresses);
+
+ for (i=0; i < len; i++) {
+ if (strcmp(merge->addresses[i]->wins_owner, local_owner)==0) {
+ lh = MAX(lh, merge->addresses[i]->expire_time);
+ }
+ }
+
+ if (lh != 0) {
+ merge->expire_time = lh;
+ }
+ }
+
+ ret = winsdb_modify(partner->service->wins_db, merge, modify_flags);
+ if (ret != NBT_RCODE_OK) {
+ DEBUG(0,("Failed to modify sgroup merge record %s: %u\n",
+ nbt_name_string(mem_ctx, &replica->name), ret));
+ return NT_STATUS_FOOBAR;
+ }
+
+ DEBUG(4,("sgroup merge record %s\n",
+ nbt_name_string(mem_ctx, &replica->name)));
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS wreplsrv_apply_one_record(struct wreplsrv_partner *partner,
+ TALLOC_CTX *mem_ctx,
+ struct wrepl_wins_owner *owner,
+ struct wrepl_name *replica)
+{
+ NTSTATUS status;
+ struct winsdb_record *rec = NULL;
+ enum _R_ACTION action = R_INVALID;
+ bool same_owner = false;
+ bool replica_vs_replica = false;
+ bool local_vs_replica = false;
+
+ status = winsdb_lookup(partner->service->wins_db,
+ &replica->name, mem_ctx, &rec);
+ if (NT_STATUS_EQUAL(NT_STATUS_OBJECT_NAME_NOT_FOUND, status)) {
+ return r_do_add(partner, mem_ctx, owner, replica);
+ }
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ if (strcmp(rec->wins_owner, partner->service->wins_db->local_owner)==0) {
+ local_vs_replica = true;
+ } else if (strcmp(rec->wins_owner, owner->address)==0) {
+ same_owner = true;
+ } else {
+ replica_vs_replica = true;
+ }
+
+ if (rec->is_static && !same_owner) {
+ action = R_NOT_REPLACE;
+
+ /*
+ * if we own the local record, then propagate it back to
+ * the other wins servers.
+ * to prevent ping-pong with other servers, we don't do this
+ * if the replica is static too.
+ *
+ * It seems that w2k3 doesn't do this, but I thing that's a bug
+ * and doing propagation helps to have consistent data on all servers
+ */
+ if (local_vs_replica && !replica->is_static) {
+ action = R_DO_PROPAGATE;
+ }
+ } else if (replica->is_static && !rec->is_static && !same_owner) {
+ action = R_DO_REPLACE;
+ } else if (same_owner) {
+ action = replace_same_owner(rec, replica);
+ } else if (replica_vs_replica) {
+ switch (rec->type) {
+ case WREPL_TYPE_UNIQUE:
+ action = replace_unique_replica_vs_X_replica(rec, replica);
+ break;
+ case WREPL_TYPE_GROUP:
+ action = replace_group_replica_vs_X_replica(rec, replica);
+ break;
+ case WREPL_TYPE_SGROUP:
+ action = replace_sgroup_replica_vs_X_replica(rec, replica);
+ break;
+ case WREPL_TYPE_MHOMED:
+ action = replace_mhomed_replica_vs_X_replica(rec, replica);
+ break;
+ }
+ } else if (local_vs_replica) {
+ switch (rec->type) {
+ case WREPL_TYPE_UNIQUE:
+ action = replace_unique_owned_vs_X_replica(rec, replica);
+ break;
+ case WREPL_TYPE_GROUP:
+ action = replace_group_owned_vs_X_replica(rec, replica);
+ break;
+ case WREPL_TYPE_SGROUP:
+ action = replace_sgroup_owned_vs_X_replica(rec, replica);
+ break;
+ case WREPL_TYPE_MHOMED:
+ action = replace_mhomed_owned_vs_X_replica(rec, replica);
+ break;
+ }
+ }
+
+ DEBUG(4,("apply record %s: %s\n",
+ nbt_name_string(mem_ctx, &replica->name), _R_ACTION_enum_string(action)));
+
+ switch (action) {
+ case R_INVALID: break;
+ case R_DO_REPLACE:
+ return r_do_replace(partner, mem_ctx, rec, owner, replica);
+ case R_NOT_REPLACE:
+ return r_not_replace(partner, mem_ctx, rec, owner, replica);
+ case R_DO_PROPAGATE:
+ return r_do_propagate(partner, mem_ctx, rec, owner, replica);
+ case R_DO_CHALLENGE:
+ return r_do_challenge(partner, mem_ctx, rec, owner, replica);
+ case R_DO_RELEASE_DEMAND:
+ return r_do_release_demand(partner, mem_ctx, rec, owner, replica);
+ case R_DO_SGROUP_MERGE:
+ return r_do_sgroup_merge(partner, mem_ctx, rec, owner, replica);
+ }
+
+ return NT_STATUS_INTERNAL_ERROR;
+}
+
+NTSTATUS wreplsrv_apply_records(struct wreplsrv_partner *partner,
+ struct wrepl_wins_owner *owner,
+ uint32_t num_names, struct wrepl_name *names)
+{
+ NTSTATUS status;
+ uint32_t i;
+
+ DEBUG(4,("apply records count[%u]:owner[%s]:min[%llu]:max[%llu]:partner[%s]\n",
+ num_names, owner->address,
+ (long long)owner->min_version,
+ (long long)owner->max_version,
+ partner->address));
+
+ for (i=0; i < num_names; i++) {
+ TALLOC_CTX *tmp_mem = talloc_new(partner);
+ NT_STATUS_HAVE_NO_MEMORY(tmp_mem);
+
+ status = wreplsrv_apply_one_record(partner, tmp_mem,
+ owner, &names[i]);
+ talloc_free(tmp_mem);
+ NT_STATUS_NOT_OK_RETURN(status);
+ }
+
+ status = wreplsrv_add_table(partner->service,
+ partner->service,
+ &partner->service->table,
+ owner->address,
+ owner->max_version);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/wrepl_server/wrepl_in_call.c b/source4/wrepl_server/wrepl_in_call.c
new file mode 100644
index 0000000000..a4f18ff2da
--- /dev/null
+++ b/source4/wrepl_server/wrepl_in_call.c
@@ -0,0 +1,575 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ WINS Replication server
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/events/events.h"
+#include "lib/socket/socket.h"
+#include "smbd/service_stream.h"
+#include "libcli/wrepl/winsrepl.h"
+#include "wrepl_server/wrepl_server.h"
+#include "libcli/composite/composite.h"
+#include "nbt_server/wins/winsdb.h"
+#include "lib/ldb/include/ldb.h"
+#include "lib/ldb/include/ldb_errors.h"
+#include "system/time.h"
+
+static NTSTATUS wreplsrv_in_start_association(struct wreplsrv_in_call *call)
+{
+ struct wrepl_start *start = &call->req_packet.message.start;
+ struct wrepl_start *start_reply = &call->rep_packet.message.start_reply;
+
+ if (call->req_packet.opcode & WREPL_OPCODE_BITS) {
+ /*
+ *if the assoc_ctx doesn't match ignore the packet
+ */
+ if ((call->req_packet.assoc_ctx != call->wreplconn->assoc_ctx.our_ctx)
+ && (call->req_packet.assoc_ctx != 0)) {
+ return ERROR_INVALID_PARAMETER;
+ }
+ } else {
+ call->wreplconn->assoc_ctx.our_ctx = WREPLSRV_INVALID_ASSOC_CTX;
+ return NT_STATUS_OK;
+ }
+
+/*
+ * it seems that we don't know all details about the start_association
+ * to support replication with NT4 (it sends 1.1 instead of 5.2)
+ * we ignore the version numbers until we know all details
+ */
+#if 0
+ if (start->minor_version != 2 || start->major_version != 5) {
+ /* w2k terminate the connection if the versions doesn't match */
+ return NT_STATUS_UNKNOWN_REVISION;
+ }
+#endif
+
+ call->wreplconn->assoc_ctx.stopped = false;
+ call->wreplconn->assoc_ctx.our_ctx = WREPLSRV_VALID_ASSOC_CTX;
+ call->wreplconn->assoc_ctx.peer_ctx = start->assoc_ctx;
+
+ call->rep_packet.mess_type = WREPL_START_ASSOCIATION_REPLY;
+ start_reply->assoc_ctx = call->wreplconn->assoc_ctx.our_ctx;
+ start_reply->minor_version = 2;
+ start_reply->major_version = 5;
+
+ /*
+ * nt4 uses 41 bytes for the start_association call
+ * so do it the same and as we don't know the meanings of this bytes
+ * we just send zeros and nt4, w2k and w2k3 seems to be happy with this
+ *
+ * if we don't do this nt4 uses an old version of the wins replication protocol
+ * and that would break nt4 <-> samba replication
+ */
+ call->rep_packet.padding = data_blob_talloc(call, NULL, 21);
+ NT_STATUS_HAVE_NO_MEMORY(call->rep_packet.padding.data);
+
+ memset(call->rep_packet.padding.data, 0, call->rep_packet.padding.length);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS wreplsrv_in_stop_assoc_ctx(struct wreplsrv_in_call *call)
+{
+ struct wrepl_stop *stop_out = &call->rep_packet.message.stop;
+
+ call->wreplconn->assoc_ctx.stopped = true;
+
+ call->rep_packet.mess_type = WREPL_STOP_ASSOCIATION;
+ stop_out->reason = 4;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS wreplsrv_in_stop_association(struct wreplsrv_in_call *call)
+{
+ /*
+ * w2k only check the assoc_ctx if the opcode has the 0x00007800 bits are set
+ */
+ if (call->req_packet.opcode & WREPL_OPCODE_BITS) {
+ /*
+ *if the assoc_ctx doesn't match ignore the packet
+ */
+ if (call->req_packet.assoc_ctx != call->wreplconn->assoc_ctx.our_ctx) {
+ return ERROR_INVALID_PARAMETER;
+ }
+ /* when the opcode bits are set the connection should be directly terminated */
+ return NT_STATUS_CONNECTION_RESET;
+ }
+
+ if (call->wreplconn->assoc_ctx.stopped) {
+ /* this causes the connection to be directly terminated */
+ return NT_STATUS_CONNECTION_RESET;
+ }
+
+ /* this will cause to not receive packets anymore and terminate the connection if the reply is send */
+ call->terminate_after_send = true;
+ return wreplsrv_in_stop_assoc_ctx(call);
+}
+
+static NTSTATUS wreplsrv_in_table_query(struct wreplsrv_in_call *call)
+{
+ struct wreplsrv_service *service = call->wreplconn->service;
+ struct wrepl_replication *repl_out = &call->rep_packet.message.replication;
+ struct wrepl_table *table_out = &call->rep_packet.message.replication.info.table;
+
+ repl_out->command = WREPL_REPL_TABLE_REPLY;
+
+ return wreplsrv_fill_wrepl_table(service, call, table_out,
+ service->wins_db->local_owner, true);
+}
+
+static int wreplsrv_in_sort_wins_name(struct wrepl_wins_name *n1,
+ struct wrepl_wins_name *n2)
+{
+ if (n1->id < n2->id) return -1;
+ if (n1->id > n2->id) return 1;
+ return 0;
+}
+
+static NTSTATUS wreplsrv_record2wins_name(TALLOC_CTX *mem_ctx,
+ struct wrepl_wins_name *name,
+ struct winsdb_record *rec)
+{
+ uint32_t num_ips, i;
+ struct wrepl_ip *ips;
+
+ name->name = rec->name;
+ talloc_steal(mem_ctx, rec->name);
+
+ name->id = rec->version;
+ name->unknown = "255.255.255.255";
+
+ name->flags = WREPL_NAME_FLAGS(rec->type, rec->state, rec->node, rec->is_static);
+
+ switch (name->flags & 2) {
+ case 0:
+ name->addresses.ip = rec->addresses[0]->address;
+ talloc_steal(mem_ctx, rec->addresses[0]->address);
+ break;
+ case 2:
+ num_ips = winsdb_addr_list_length(rec->addresses);
+ ips = talloc_array(mem_ctx, struct wrepl_ip, num_ips);
+ NT_STATUS_HAVE_NO_MEMORY(ips);
+
+ for (i = 0; i < num_ips; i++) {
+ ips[i].owner = rec->addresses[i]->wins_owner;
+ talloc_steal(ips, rec->addresses[i]->wins_owner);
+ ips[i].ip = rec->addresses[i]->address;
+ talloc_steal(ips, rec->addresses[i]->address);
+ }
+
+ name->addresses.addresses.num_ips = num_ips;
+ name->addresses.addresses.ips = ips;
+ break;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS wreplsrv_in_send_request(struct wreplsrv_in_call *call)
+{
+ struct wreplsrv_service *service = call->wreplconn->service;
+ struct wrepl_wins_owner *owner_in = &call->req_packet.message.replication.info.owner;
+ struct wrepl_replication *repl_out = &call->rep_packet.message.replication;
+ struct wrepl_send_reply *reply_out = &call->rep_packet.message.replication.info.reply;
+ struct wreplsrv_owner *owner;
+ const char *owner_filter;
+ const char *filter;
+ struct ldb_result *res = NULL;
+ int ret;
+ struct wrepl_wins_name *names;
+ struct winsdb_record *rec;
+ NTSTATUS status;
+ uint32_t i, j;
+ time_t now = time(NULL);
+
+ owner = wreplsrv_find_owner(service, service->table, owner_in->address);
+
+ repl_out->command = WREPL_REPL_SEND_REPLY;
+ reply_out->num_names = 0;
+ reply_out->names = NULL;
+
+ /*
+ * if we didn't know this owner, must be a bug in the partners client code...
+ * return an empty list.
+ */
+ if (!owner) {
+ DEBUG(2,("WINSREPL:reply [0] records unknown owner[%s] to partner[%s]\n",
+ owner_in->address, call->wreplconn->partner->address));
+ return NT_STATUS_OK;
+ }
+
+ /*
+ * the client sends a max_version of 0, interpret it as
+ * (uint64_t)-1
+ */
+ if (owner_in->max_version == 0) {
+ owner_in->max_version = (uint64_t)-1;
+ }
+
+ /*
+ * if the partner ask for nothing, or give invalid ranges,
+ * return an empty list.
+ */
+ if (owner_in->min_version > owner_in->max_version) {
+ DEBUG(2,("WINSREPL:reply [0] records owner[%s] min[%llu] max[%llu] to partner[%s]\n",
+ owner_in->address,
+ (long long)owner_in->min_version,
+ (long long)owner_in->max_version,
+ call->wreplconn->partner->address));
+ return NT_STATUS_OK;
+ }
+
+ /*
+ * if the partner has already all records for nothing, or give invalid ranges,
+ * return an empty list.
+ */
+ if (owner_in->min_version > owner->owner.max_version) {
+ DEBUG(2,("WINSREPL:reply [0] records owner[%s] min[%llu] max[%llu] to partner[%s]\n",
+ owner_in->address,
+ (long long)owner_in->min_version,
+ (long long)owner_in->max_version,
+ call->wreplconn->partner->address));
+ return NT_STATUS_OK;
+ }
+
+ owner_filter = wreplsrv_owner_filter(service, call, owner->owner.address);
+ NT_STATUS_HAVE_NO_MEMORY(owner_filter);
+ filter = talloc_asprintf(call,
+ "(&%s(objectClass=winsRecord)"
+ "(|(recordState=%u)(recordState=%u))"
+ "(versionID>=%llu)(versionID<=%llu))",
+ owner_filter,
+ WREPL_STATE_ACTIVE, WREPL_STATE_TOMBSTONE,
+ (long long)owner_in->min_version,
+ (long long)owner_in->max_version);
+ NT_STATUS_HAVE_NO_MEMORY(filter);
+ ret = ldb_search(service->wins_db->ldb, call, &res, NULL, LDB_SCOPE_SUBTREE, NULL, "%s", filter);
+ if (ret != LDB_SUCCESS) return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ DEBUG(10,("WINSREPL: filter '%s' count %d\n", filter, res->count));
+
+ if (res->count == 0) {
+ DEBUG(2,("WINSREPL:reply [%u] records owner[%s] min[%llu] max[%llu] to partner[%s]\n",
+ res->count, owner_in->address,
+ (long long)owner_in->min_version,
+ (long long)owner_in->max_version,
+ call->wreplconn->partner->address));
+ return NT_STATUS_OK;
+ }
+
+ names = talloc_array(call, struct wrepl_wins_name, res->count);
+ NT_STATUS_HAVE_NO_MEMORY(names);
+
+ for (i=0, j=0; i < res->count; i++) {
+ status = winsdb_record(service->wins_db, res->msgs[i], call, now, &rec);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ /*
+ * it's possible that winsdb_record() made the record RELEASED
+ * because it's expired, but in the database it's still stored
+ * as ACTIVE...
+ *
+ * make sure we really only replicate ACTIVE and TOMBSTONE records
+ */
+ if (rec->state == WREPL_STATE_ACTIVE || rec->state == WREPL_STATE_TOMBSTONE) {
+ status = wreplsrv_record2wins_name(names, &names[j], rec);
+ NT_STATUS_NOT_OK_RETURN(status);
+ j++;
+ }
+
+ talloc_free(rec);
+ talloc_free(res->msgs[i]);
+ }
+
+ /* sort the names before we send them */
+ qsort(names, j, sizeof(struct wrepl_wins_name), (comparison_fn_t)wreplsrv_in_sort_wins_name);
+
+ DEBUG(2,("WINSREPL:reply [%u] records owner[%s] min[%llu] max[%llu] to partner[%s]\n",
+ j, owner_in->address,
+ (long long)owner_in->min_version,
+ (long long)owner_in->max_version,
+ call->wreplconn->partner->address));
+
+ reply_out->num_names = j;
+ reply_out->names = names;
+
+ return NT_STATUS_OK;
+}
+
+struct wreplsrv_in_update_state {
+ struct wreplsrv_in_connection *wrepl_in;
+ struct wreplsrv_out_connection *wrepl_out;
+ struct composite_context *creq;
+ struct wreplsrv_pull_cycle_io cycle_io;
+};
+
+static void wreplsrv_in_update_handler(struct composite_context *creq)
+{
+ struct wreplsrv_in_update_state *update_state = talloc_get_type(creq->async.private_data,
+ struct wreplsrv_in_update_state);
+ NTSTATUS status;
+
+ status = wreplsrv_pull_cycle_recv(creq);
+
+ talloc_free(update_state->wrepl_out);
+
+ wreplsrv_terminate_in_connection(update_state->wrepl_in, nt_errstr(status));
+}
+
+static NTSTATUS wreplsrv_in_update(struct wreplsrv_in_call *call)
+{
+ struct wreplsrv_in_connection *wrepl_in = call->wreplconn;
+ struct wreplsrv_out_connection *wrepl_out;
+ struct wrepl_table *update_in = &call->req_packet.message.replication.info.table;
+ struct wreplsrv_in_update_state *update_state;
+ uint16_t fde_flags;
+
+ DEBUG(2,("WREPL_REPL_UPDATE: partner[%s] initiator[%s] num_owners[%u]\n",
+ call->wreplconn->partner->address,
+ update_in->initiator, update_in->partner_count));
+
+ /*
+ * we need to flip the connection into a client connection
+ * and do a WREPL_REPL_SEND_REQUEST's on the that connection
+ * and then stop this connection
+ */
+ fde_flags = event_get_fd_flags(wrepl_in->conn->event.fde);
+ talloc_free(wrepl_in->conn->event.fde);
+ wrepl_in->conn->event.fde = NULL;
+
+ update_state = talloc(wrepl_in, struct wreplsrv_in_update_state);
+ NT_STATUS_HAVE_NO_MEMORY(update_state);
+
+ wrepl_out = talloc(update_state, struct wreplsrv_out_connection);
+ NT_STATUS_HAVE_NO_MEMORY(wrepl_out);
+ wrepl_out->service = wrepl_in->service;
+ wrepl_out->partner = wrepl_in->partner;
+ wrepl_out->assoc_ctx.our_ctx = wrepl_in->assoc_ctx.our_ctx;
+ wrepl_out->assoc_ctx.peer_ctx = wrepl_in->assoc_ctx.peer_ctx;
+ wrepl_out->sock = wrepl_socket_merge(wrepl_out,
+ wrepl_in->conn->event.ctx,
+ wrepl_in->conn->socket,
+ wrepl_in->packet);
+ NT_STATUS_HAVE_NO_MEMORY(wrepl_out->sock);
+
+ event_set_fd_flags(wrepl_out->sock->event.fde, fde_flags);
+
+ update_state->wrepl_in = wrepl_in;
+ update_state->wrepl_out = wrepl_out;
+ update_state->cycle_io.in.partner = wrepl_out->partner;
+ update_state->cycle_io.in.num_owners = update_in->partner_count;
+ update_state->cycle_io.in.owners = update_in->partners;
+ talloc_steal(update_state, update_in->partners);
+ update_state->cycle_io.in.wreplconn = wrepl_out;
+ update_state->creq = wreplsrv_pull_cycle_send(update_state, &update_state->cycle_io);
+ if (!update_state->creq) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ update_state->creq->async.fn = wreplsrv_in_update_handler;
+ update_state->creq->async.private_data = update_state;
+
+ return ERROR_INVALID_PARAMETER;
+}
+
+static NTSTATUS wreplsrv_in_update2(struct wreplsrv_in_call *call)
+{
+ return wreplsrv_in_update(call);
+}
+
+static NTSTATUS wreplsrv_in_inform(struct wreplsrv_in_call *call)
+{
+ struct wrepl_table *inform_in = &call->req_packet.message.replication.info.table;
+
+ DEBUG(2,("WREPL_REPL_INFORM: partner[%s] initiator[%s] num_owners[%u]\n",
+ call->wreplconn->partner->address,
+ inform_in->initiator, inform_in->partner_count));
+
+ wreplsrv_out_partner_pull(call->wreplconn->partner, inform_in);
+
+ /* we don't reply to WREPL_REPL_INFORM messages */
+ return ERROR_INVALID_PARAMETER;
+}
+
+static NTSTATUS wreplsrv_in_inform2(struct wreplsrv_in_call *call)
+{
+ return wreplsrv_in_inform(call);
+}
+
+static NTSTATUS wreplsrv_in_replication(struct wreplsrv_in_call *call)
+{
+ struct wrepl_replication *repl_in = &call->req_packet.message.replication;
+ NTSTATUS status;
+
+ /*
+ * w2k only check the assoc_ctx if the opcode has the 0x00007800 bits are set
+ */
+ if (call->req_packet.opcode & WREPL_OPCODE_BITS) {
+ /*
+ *if the assoc_ctx doesn't match ignore the packet
+ */
+ if (call->req_packet.assoc_ctx != call->wreplconn->assoc_ctx.our_ctx) {
+ return ERROR_INVALID_PARAMETER;
+ }
+ }
+
+ if (!call->wreplconn->partner) {
+ struct socket_address *partner_ip = socket_get_peer_addr(call->wreplconn->conn->socket, call);
+
+ call->wreplconn->partner = wreplsrv_find_partner(call->wreplconn->service, partner_ip->addr);
+ if (!call->wreplconn->partner) {
+ DEBUG(1,("Failing WINS replication from non-partner %s\n",
+ partner_ip ? partner_ip->addr : NULL));
+ return wreplsrv_in_stop_assoc_ctx(call);
+ }
+ }
+
+ switch (repl_in->command) {
+ case WREPL_REPL_TABLE_QUERY:
+ if (!(call->wreplconn->partner->type & WINSREPL_PARTNER_PUSH)) {
+ DEBUG(0,("Failing WINS replication TABLE_QUERY from non-push-partner %s\n",
+ call->wreplconn->partner->address));
+ return wreplsrv_in_stop_assoc_ctx(call);
+ }
+ status = wreplsrv_in_table_query(call);
+ break;
+
+ case WREPL_REPL_TABLE_REPLY:
+ return ERROR_INVALID_PARAMETER;
+
+ case WREPL_REPL_SEND_REQUEST:
+ if (!(call->wreplconn->partner->type & WINSREPL_PARTNER_PUSH)) {
+ DEBUG(0,("Failing WINS replication SEND_REQUESET from non-push-partner %s\n",
+ call->wreplconn->partner->address));
+ return wreplsrv_in_stop_assoc_ctx(call);
+ }
+ status = wreplsrv_in_send_request(call);
+ break;
+
+ case WREPL_REPL_SEND_REPLY:
+ return ERROR_INVALID_PARAMETER;
+
+ case WREPL_REPL_UPDATE:
+ if (!(call->wreplconn->partner->type & WINSREPL_PARTNER_PULL)) {
+ DEBUG(0,("Failing WINS replication UPDATE from non-pull-partner %s\n",
+ call->wreplconn->partner->address));
+ return wreplsrv_in_stop_assoc_ctx(call);
+ }
+ status = wreplsrv_in_update(call);
+ break;
+
+ case WREPL_REPL_UPDATE2:
+ if (!(call->wreplconn->partner->type & WINSREPL_PARTNER_PULL)) {
+ DEBUG(0,("Failing WINS replication UPDATE2 from non-pull-partner %s\n",
+ call->wreplconn->partner->address));
+ return wreplsrv_in_stop_assoc_ctx(call);
+ }
+ status = wreplsrv_in_update2(call);
+ break;
+
+ case WREPL_REPL_INFORM:
+ if (!(call->wreplconn->partner->type & WINSREPL_PARTNER_PULL)) {
+ DEBUG(0,("Failing WINS replication INFORM from non-pull-partner %s\n",
+ call->wreplconn->partner->address));
+ return wreplsrv_in_stop_assoc_ctx(call);
+ }
+ status = wreplsrv_in_inform(call);
+ break;
+
+ case WREPL_REPL_INFORM2:
+ if (!(call->wreplconn->partner->type & WINSREPL_PARTNER_PULL)) {
+ DEBUG(0,("Failing WINS replication INFORM2 from non-pull-partner %s\n",
+ call->wreplconn->partner->address));
+ return wreplsrv_in_stop_assoc_ctx(call);
+ }
+ status = wreplsrv_in_inform2(call);
+ break;
+
+ default:
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ if (NT_STATUS_IS_OK(status)) {
+ call->rep_packet.mess_type = WREPL_REPLICATION;
+ }
+
+ return status;
+}
+
+static NTSTATUS wreplsrv_in_invalid_assoc_ctx(struct wreplsrv_in_call *call)
+{
+ struct wrepl_start *start = &call->rep_packet.message.start;
+
+ call->rep_packet.opcode = 0x00008583;
+ call->rep_packet.assoc_ctx = 0;
+ call->rep_packet.mess_type = WREPL_START_ASSOCIATION;
+
+ start->assoc_ctx = 0x0000000a;
+ start->minor_version = 0x0001;
+ start->major_version = 0x0000;
+
+ call->rep_packet.padding = data_blob_talloc(call, NULL, 4);
+ memset(call->rep_packet.padding.data, '\0', call->rep_packet.padding.length);
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS wreplsrv_in_call(struct wreplsrv_in_call *call)
+{
+ NTSTATUS status;
+
+ if (!(call->req_packet.opcode & WREPL_OPCODE_BITS)
+ && (call->wreplconn->assoc_ctx.our_ctx == WREPLSRV_INVALID_ASSOC_CTX)) {
+ return wreplsrv_in_invalid_assoc_ctx(call);
+ }
+
+ switch (call->req_packet.mess_type) {
+ case WREPL_START_ASSOCIATION:
+ status = wreplsrv_in_start_association(call);
+ break;
+ case WREPL_START_ASSOCIATION_REPLY:
+ /* this is not valid here, so we ignore it */
+ return ERROR_INVALID_PARAMETER;
+
+ case WREPL_STOP_ASSOCIATION:
+ status = wreplsrv_in_stop_association(call);
+ break;
+
+ case WREPL_REPLICATION:
+ status = wreplsrv_in_replication(call);
+ break;
+ default:
+ /* everythingelse is also not valid here, so we ignore it */
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ if (call->wreplconn->assoc_ctx.our_ctx == WREPLSRV_INVALID_ASSOC_CTX) {
+ return wreplsrv_in_invalid_assoc_ctx(call);
+ }
+
+ if (NT_STATUS_IS_OK(status)) {
+ /* let the backend to set some of the opcode bits, but always add the standards */
+ call->rep_packet.opcode |= WREPL_OPCODE_BITS;
+ call->rep_packet.assoc_ctx = call->wreplconn->assoc_ctx.peer_ctx;
+ }
+
+ return status;
+}
diff --git a/source4/wrepl_server/wrepl_in_connection.c b/source4/wrepl_server/wrepl_in_connection.c
new file mode 100644
index 0000000000..7c9c2b77bf
--- /dev/null
+++ b/source4/wrepl_server/wrepl_in_connection.c
@@ -0,0 +1,323 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ WINS Replication server
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/socket/socket.h"
+#include "lib/stream/packet.h"
+#include "smbd/service_task.h"
+#include "smbd/service_stream.h"
+#include "smbd/service.h"
+#include "lib/messaging/irpc.h"
+#include "librpc/gen_ndr/ndr_winsrepl.h"
+#include "wrepl_server/wrepl_server.h"
+#include "smbd/process_model.h"
+#include "system/network.h"
+#include "lib/socket/netif.h"
+#include "param/param.h"
+
+void wreplsrv_terminate_in_connection(struct wreplsrv_in_connection *wreplconn, const char *reason)
+{
+ stream_terminate_connection(wreplconn->conn, reason);
+}
+
+static int terminate_after_send_destructor(struct wreplsrv_in_connection **tas)
+{
+ wreplsrv_terminate_in_connection(*tas, "wreplsrv_in_connection: terminate_after_send");
+ return 0;
+}
+
+/*
+ receive some data on a WREPL connection
+*/
+static NTSTATUS wreplsrv_recv_request(void *private_data, DATA_BLOB blob)
+{
+ struct wreplsrv_in_connection *wreplconn = talloc_get_type(private_data, struct wreplsrv_in_connection);
+ struct wreplsrv_in_call *call;
+ DATA_BLOB packet_in_blob;
+ DATA_BLOB packet_out_blob;
+ struct wrepl_wrap packet_out_wrap;
+ NTSTATUS status;
+ enum ndr_err_code ndr_err;
+
+ call = talloc_zero(wreplconn, struct wreplsrv_in_call);
+ NT_STATUS_HAVE_NO_MEMORY(call);
+ call->wreplconn = wreplconn;
+ talloc_steal(call, blob.data);
+
+ packet_in_blob.data = blob.data + 4;
+ packet_in_blob.length = blob.length - 4;
+
+ ndr_err = ndr_pull_struct_blob(&packet_in_blob, call,
+ lp_iconv_convenience(wreplconn->service->task->lp_ctx),
+ &call->req_packet,
+ (ndr_pull_flags_fn_t)ndr_pull_wrepl_packet);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ if (DEBUGLVL(10)) {
+ DEBUG(10,("Received WINS-Replication packet of length %u\n",
+ (unsigned)packet_in_blob.length + 4));
+ NDR_PRINT_DEBUG(wrepl_packet, &call->req_packet);
+ }
+
+ status = wreplsrv_in_call(call);
+ NT_STATUS_IS_ERR_RETURN(status);
+ if (!NT_STATUS_IS_OK(status)) {
+ /* w2k just ignores invalid packets, so we do */
+ DEBUG(10,("Received WINS-Replication packet was invalid, we just ignore it\n"));
+ talloc_free(call);
+ return NT_STATUS_OK;
+ }
+
+ /* and now encode the reply */
+ packet_out_wrap.packet = call->rep_packet;
+ ndr_err = ndr_push_struct_blob(&packet_out_blob, call,
+ lp_iconv_convenience(wreplconn->service->task->lp_ctx),
+ &packet_out_wrap,
+ (ndr_push_flags_fn_t)ndr_push_wrepl_wrap);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ if (DEBUGLVL(10)) {
+ DEBUG(10,("Sending WINS-Replication packet of length %d\n", (int)packet_out_blob.length));
+ NDR_PRINT_DEBUG(wrepl_packet, &call->rep_packet);
+ }
+
+ if (call->terminate_after_send) {
+ struct wreplsrv_in_connection **tas;
+ tas = talloc(packet_out_blob.data, struct wreplsrv_in_connection *);
+ NT_STATUS_HAVE_NO_MEMORY(tas);
+ *tas = wreplconn;
+ talloc_set_destructor(tas, terminate_after_send_destructor);
+ }
+
+ status = packet_send(wreplconn->packet, packet_out_blob);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ talloc_free(call);
+ return NT_STATUS_OK;
+}
+
+/*
+ called when the socket becomes readable
+*/
+static void wreplsrv_recv(struct stream_connection *conn, uint16_t flags)
+{
+ struct wreplsrv_in_connection *wreplconn = talloc_get_type(conn->private_data,
+ struct wreplsrv_in_connection);
+
+ packet_recv(wreplconn->packet);
+}
+
+/*
+ called when the socket becomes writable
+*/
+static void wreplsrv_send(struct stream_connection *conn, uint16_t flags)
+{
+ struct wreplsrv_in_connection *wreplconn = talloc_get_type(conn->private_data,
+ struct wreplsrv_in_connection);
+ packet_queue_run(wreplconn->packet);
+}
+
+/*
+ handle socket recv errors
+*/
+static void wreplsrv_recv_error(void *private_data, NTSTATUS status)
+{
+ struct wreplsrv_in_connection *wreplconn = talloc_get_type(private_data,
+ struct wreplsrv_in_connection);
+ wreplsrv_terminate_in_connection(wreplconn, nt_errstr(status));
+}
+
+/*
+ called when we get a new connection
+*/
+static void wreplsrv_accept(struct stream_connection *conn)
+{
+ struct wreplsrv_service *service = talloc_get_type(conn->private_data, struct wreplsrv_service);
+ struct wreplsrv_in_connection *wreplconn;
+ struct socket_address *peer_ip;
+
+ wreplconn = talloc_zero(conn, struct wreplsrv_in_connection);
+ if (!wreplconn) {
+ stream_terminate_connection(conn, "wreplsrv_accept: out of memory");
+ return;
+ }
+
+ wreplconn->packet = packet_init(wreplconn);
+ if (!wreplconn->packet) {
+ wreplsrv_terminate_in_connection(wreplconn, "wreplsrv_accept: out of memory");
+ return;
+ }
+ packet_set_private(wreplconn->packet, wreplconn);
+ packet_set_socket(wreplconn->packet, conn->socket);
+ packet_set_callback(wreplconn->packet, wreplsrv_recv_request);
+ packet_set_full_request(wreplconn->packet, packet_full_request_u32);
+ packet_set_error_handler(wreplconn->packet, wreplsrv_recv_error);
+ packet_set_event_context(wreplconn->packet, conn->event.ctx);
+ packet_set_fde(wreplconn->packet, conn->event.fde);
+ packet_set_serialise(wreplconn->packet);
+
+ wreplconn->conn = conn;
+ wreplconn->service = service;
+
+ peer_ip = socket_get_peer_addr(conn->socket, wreplconn);
+ if (!peer_ip) {
+ wreplsrv_terminate_in_connection(wreplconn, "wreplsrv_accept: could not obtain peer IP from kernel");
+ return;
+ }
+
+ wreplconn->partner = wreplsrv_find_partner(service, peer_ip->addr);
+
+ conn->private_data = wreplconn;
+
+ irpc_add_name(conn->msg_ctx, "wreplsrv_connection");
+}
+
+static const struct stream_server_ops wreplsrv_stream_ops = {
+ .name = "wreplsrv",
+ .accept_connection = wreplsrv_accept,
+ .recv_handler = wreplsrv_recv,
+ .send_handler = wreplsrv_send,
+};
+
+/*
+ called when we get a new connection
+*/
+NTSTATUS wreplsrv_in_connection_merge(struct wreplsrv_partner *partner,
+ struct socket_context *sock,
+ struct packet_context *packet,
+ struct wreplsrv_in_connection **_wrepl_in)
+{
+ struct wreplsrv_service *service = partner->service;
+ struct wreplsrv_in_connection *wrepl_in;
+ const struct model_ops *model_ops;
+ struct stream_connection *conn;
+ NTSTATUS status;
+
+ /* within the wrepl task we want to be a single process, so
+ ask for the single process model ops and pass these to the
+ stream_setup_socket() call. */
+ model_ops = process_model_startup(service->task->event_ctx, "single");
+ if (!model_ops) {
+ DEBUG(0,("Can't find 'single' process model_ops"));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ wrepl_in = talloc_zero(partner, struct wreplsrv_in_connection);
+ NT_STATUS_HAVE_NO_MEMORY(wrepl_in);
+
+ wrepl_in->service = service;
+ wrepl_in->partner = partner;
+
+ status = stream_new_connection_merge(service->task->event_ctx, service->task->lp_ctx, model_ops,
+ sock, &wreplsrv_stream_ops, service->task->msg_ctx,
+ wrepl_in, &conn);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ /*
+ * make the wreplsrv_in_connection structure a child of the
+ * stream_connection, to match the hierarchy of wreplsrv_accept
+ */
+ wrepl_in->conn = conn;
+ talloc_steal(conn, wrepl_in);
+
+ /*
+ * now update the packet handling callback,...
+ */
+ wrepl_in->packet = talloc_steal(wrepl_in, packet);
+ packet_set_private(wrepl_in->packet, wrepl_in);
+ packet_set_socket(wrepl_in->packet, conn->socket);
+ packet_set_callback(wrepl_in->packet, wreplsrv_recv_request);
+ packet_set_full_request(wrepl_in->packet, packet_full_request_u32);
+ packet_set_error_handler(wrepl_in->packet, wreplsrv_recv_error);
+ packet_set_event_context(wrepl_in->packet, conn->event.ctx);
+ packet_set_fde(wrepl_in->packet, conn->event.fde);
+ packet_set_serialise(wrepl_in->packet);
+
+ *_wrepl_in = wrepl_in;
+ return NT_STATUS_OK;
+}
+
+/*
+ startup the wrepl port 42 server sockets
+*/
+NTSTATUS wreplsrv_setup_sockets(struct wreplsrv_service *service, struct loadparm_context *lp_ctx)
+{
+ NTSTATUS status;
+ struct task_server *task = service->task;
+ const struct model_ops *model_ops;
+ const char *address;
+ uint16_t port = WINS_REPLICATION_PORT;
+
+ /* within the wrepl task we want to be a single process, so
+ ask for the single process model ops and pass these to the
+ stream_setup_socket() call. */
+ model_ops = process_model_startup(task->event_ctx, "single");
+ if (!model_ops) {
+ DEBUG(0,("Can't find 'single' process model_ops"));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ if (lp_interfaces(lp_ctx) && lp_bind_interfaces_only(lp_ctx)) {
+ int num_interfaces;
+ int i;
+ struct interface *ifaces;
+
+ load_interfaces(task, lp_interfaces(lp_ctx), &ifaces);
+
+ num_interfaces = iface_count(ifaces);
+
+ /* We have been given an interfaces line, and been
+ told to only bind to those interfaces. Create a
+ socket per interface and bind to only these.
+ */
+ for(i = 0; i < num_interfaces; i++) {
+ address = iface_n_ip(ifaces, i);
+ status = stream_setup_socket(task->event_ctx,
+ task->lp_ctx, model_ops,
+ &wreplsrv_stream_ops,
+ "ipv4", address, &port,
+ lp_socket_options(task->lp_ctx),
+ service);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("stream_setup_socket(address=%s,port=%u) failed - %s\n",
+ address, port, nt_errstr(status)));
+ return status;
+ }
+ }
+ } else {
+ address = lp_socket_address(lp_ctx);
+ status = stream_setup_socket(task->event_ctx, task->lp_ctx,
+ model_ops, &wreplsrv_stream_ops,
+ "ipv4", address, &port, lp_socket_options(task->lp_ctx),
+ service);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("stream_setup_socket(address=%s,port=%u) failed - %s\n",
+ address, port, nt_errstr(status)));
+ return status;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/wrepl_server/wrepl_out_helpers.c b/source4/wrepl_server/wrepl_out_helpers.c
new file mode 100644
index 0000000000..6aff134072
--- /dev/null
+++ b/source4/wrepl_server/wrepl_out_helpers.c
@@ -0,0 +1,1123 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ WINS Replication server
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/events/events.h"
+#include "lib/socket/socket.h"
+#include "smbd/service_task.h"
+#include "smbd/service_stream.h"
+#include "librpc/gen_ndr/winsrepl.h"
+#include "wrepl_server/wrepl_server.h"
+#include "nbt_server/wins/winsdb.h"
+#include "libcli/composite/composite.h"
+#include "libcli/wrepl/winsrepl.h"
+#include "libcli/resolve/resolve.h"
+#include "param/param.h"
+
+enum wreplsrv_out_connect_stage {
+ WREPLSRV_OUT_CONNECT_STAGE_WAIT_SOCKET,
+ WREPLSRV_OUT_CONNECT_STAGE_WAIT_ASSOC_CTX,
+ WREPLSRV_OUT_CONNECT_STAGE_DONE
+};
+
+struct wreplsrv_out_connect_state {
+ enum wreplsrv_out_connect_stage stage;
+ struct composite_context *c;
+ struct wrepl_request *req;
+ struct composite_context *c_req;
+ struct wrepl_associate assoc_io;
+ enum winsrepl_partner_type type;
+ struct wreplsrv_out_connection *wreplconn;
+};
+
+static void wreplsrv_out_connect_handler_creq(struct composite_context *c_req);
+static void wreplsrv_out_connect_handler_req(struct wrepl_request *req);
+
+static NTSTATUS wreplsrv_out_connect_wait_socket(struct wreplsrv_out_connect_state *state)
+{
+ NTSTATUS status;
+
+ status = wrepl_connect_recv(state->c_req);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ state->req = wrepl_associate_send(state->wreplconn->sock, &state->assoc_io);
+ NT_STATUS_HAVE_NO_MEMORY(state->req);
+
+ state->req->async.fn = wreplsrv_out_connect_handler_req;
+ state->req->async.private_data = state;
+
+ state->stage = WREPLSRV_OUT_CONNECT_STAGE_WAIT_ASSOC_CTX;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS wreplsrv_out_connect_wait_assoc_ctx(struct wreplsrv_out_connect_state *state)
+{
+ NTSTATUS status;
+
+ status = wrepl_associate_recv(state->req, &state->assoc_io);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ state->wreplconn->assoc_ctx.peer_ctx = state->assoc_io.out.assoc_ctx;
+ state->wreplconn->assoc_ctx.peer_major = state->assoc_io.out.major_version;
+
+ if (state->type == WINSREPL_PARTNER_PUSH) {
+ if (state->wreplconn->assoc_ctx.peer_major >= 5) {
+ state->wreplconn->partner->push.wreplconn = state->wreplconn;
+ talloc_steal(state->wreplconn->partner, state->wreplconn);
+ } else {
+ state->type = WINSREPL_PARTNER_NONE;
+ }
+ } else if (state->type == WINSREPL_PARTNER_PULL) {
+ state->wreplconn->partner->pull.wreplconn = state->wreplconn;
+ talloc_steal(state->wreplconn->partner, state->wreplconn);
+ }
+
+ state->stage = WREPLSRV_OUT_CONNECT_STAGE_DONE;
+
+ return NT_STATUS_OK;
+}
+
+static void wreplsrv_out_connect_handler(struct wreplsrv_out_connect_state *state)
+{
+ struct composite_context *c = state->c;
+
+ switch (state->stage) {
+ case WREPLSRV_OUT_CONNECT_STAGE_WAIT_SOCKET:
+ c->status = wreplsrv_out_connect_wait_socket(state);
+ break;
+ case WREPLSRV_OUT_CONNECT_STAGE_WAIT_ASSOC_CTX:
+ c->status = wreplsrv_out_connect_wait_assoc_ctx(state);
+ c->state = COMPOSITE_STATE_DONE;
+ break;
+ case WREPLSRV_OUT_CONNECT_STAGE_DONE:
+ c->status = NT_STATUS_INTERNAL_ERROR;
+ }
+
+ if (!NT_STATUS_IS_OK(c->status)) {
+ c->state = COMPOSITE_STATE_ERROR;
+ }
+
+ if (c->state >= COMPOSITE_STATE_DONE && c->async.fn) {
+ c->async.fn(c);
+ }
+}
+
+static void wreplsrv_out_connect_handler_creq(struct composite_context *creq)
+{
+ struct wreplsrv_out_connect_state *state = talloc_get_type(creq->async.private_data,
+ struct wreplsrv_out_connect_state);
+ wreplsrv_out_connect_handler(state);
+ return;
+}
+
+static void wreplsrv_out_connect_handler_req(struct wrepl_request *req)
+{
+ struct wreplsrv_out_connect_state *state = talloc_get_type(req->async.private_data,
+ struct wreplsrv_out_connect_state);
+ wreplsrv_out_connect_handler(state);
+ return;
+}
+
+static struct composite_context *wreplsrv_out_connect_send(struct wreplsrv_partner *partner,
+ enum winsrepl_partner_type type,
+ struct wreplsrv_out_connection *wreplconn)
+{
+ struct composite_context *c = NULL;
+ struct wreplsrv_service *service = partner->service;
+ struct wreplsrv_out_connect_state *state = NULL;
+ struct wreplsrv_out_connection **wreplconnp = &wreplconn;
+ bool cached_connection = false;
+
+ c = talloc_zero(partner, struct composite_context);
+ if (!c) goto failed;
+
+ state = talloc_zero(c, struct wreplsrv_out_connect_state);
+ if (!state) goto failed;
+ state->c = c;
+ state->type = type;
+
+ c->state = COMPOSITE_STATE_IN_PROGRESS;
+ c->event_ctx = service->task->event_ctx;
+ c->private_data = state;
+
+ if (type == WINSREPL_PARTNER_PUSH) {
+ cached_connection = true;
+ wreplconn = partner->push.wreplconn;
+ wreplconnp = &partner->push.wreplconn;
+ } else if (type == WINSREPL_PARTNER_PULL) {
+ cached_connection = true;
+ wreplconn = partner->pull.wreplconn;
+ wreplconnp = &partner->pull.wreplconn;
+ }
+
+ /* we have a connection already, so use it */
+ if (wreplconn) {
+ if (!wreplconn->sock->dead) {
+ state->stage = WREPLSRV_OUT_CONNECT_STAGE_DONE;
+ state->wreplconn= wreplconn;
+ composite_done(c);
+ return c;
+ } else if (!cached_connection) {
+ state->stage = WREPLSRV_OUT_CONNECT_STAGE_DONE;
+ state->wreplconn= NULL;
+ composite_done(c);
+ return c;
+ } else {
+ talloc_free(wreplconn);
+ *wreplconnp = NULL;
+ }
+ }
+
+ wreplconn = talloc_zero(state, struct wreplsrv_out_connection);
+ if (!wreplconn) goto failed;
+
+ wreplconn->service = service;
+ wreplconn->partner = partner;
+ wreplconn->sock = wrepl_socket_init(wreplconn, service->task->event_ctx, lp_iconv_convenience(service->task->lp_ctx));
+ if (!wreplconn->sock) goto failed;
+
+ state->stage = WREPLSRV_OUT_CONNECT_STAGE_WAIT_SOCKET;
+ state->wreplconn= wreplconn;
+ state->c_req = wrepl_connect_send(wreplconn->sock,
+ partner->our_address?partner->our_address:wrepl_best_ip(service->task->lp_ctx, partner->address),
+ partner->address);
+ if (!state->c_req) goto failed;
+
+ state->c_req->async.fn = wreplsrv_out_connect_handler_creq;
+ state->c_req->async.private_data = state;
+
+ return c;
+failed:
+ talloc_free(c);
+ return NULL;
+}
+
+static NTSTATUS wreplsrv_out_connect_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
+ struct wreplsrv_out_connection **wreplconn)
+{
+ NTSTATUS status;
+
+ status = composite_wait(c);
+
+ if (NT_STATUS_IS_OK(status)) {
+ struct wreplsrv_out_connect_state *state = talloc_get_type(c->private_data,
+ struct wreplsrv_out_connect_state);
+ if (state->wreplconn) {
+ *wreplconn = talloc_reference(mem_ctx, state->wreplconn);
+ if (!*wreplconn) status = NT_STATUS_NO_MEMORY;
+ } else {
+ status = NT_STATUS_INVALID_CONNECTION;
+ }
+ }
+
+ talloc_free(c);
+ return status;
+
+}
+
+struct wreplsrv_pull_table_io {
+ struct {
+ struct wreplsrv_partner *partner;
+ uint32_t num_owners;
+ struct wrepl_wins_owner *owners;
+ } in;
+ struct {
+ uint32_t num_owners;
+ struct wrepl_wins_owner *owners;
+ } out;
+};
+
+enum wreplsrv_pull_table_stage {
+ WREPLSRV_PULL_TABLE_STAGE_WAIT_CONNECTION,
+ WREPLSRV_PULL_TABLE_STAGE_WAIT_TABLE_REPLY,
+ WREPLSRV_PULL_TABLE_STAGE_DONE
+};
+
+struct wreplsrv_pull_table_state {
+ enum wreplsrv_pull_table_stage stage;
+ struct composite_context *c;
+ struct wrepl_request *req;
+ struct wrepl_pull_table table_io;
+ struct wreplsrv_pull_table_io *io;
+ struct composite_context *creq;
+ struct wreplsrv_out_connection *wreplconn;
+};
+
+static void wreplsrv_pull_table_handler_req(struct wrepl_request *req);
+
+static NTSTATUS wreplsrv_pull_table_wait_connection(struct wreplsrv_pull_table_state *state)
+{
+ NTSTATUS status;
+
+ status = wreplsrv_out_connect_recv(state->creq, state, &state->wreplconn);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ state->table_io.in.assoc_ctx = state->wreplconn->assoc_ctx.peer_ctx;
+ state->req = wrepl_pull_table_send(state->wreplconn->sock, &state->table_io);
+ NT_STATUS_HAVE_NO_MEMORY(state->req);
+
+ state->req->async.fn = wreplsrv_pull_table_handler_req;
+ state->req->async.private_data = state;
+
+ state->stage = WREPLSRV_PULL_TABLE_STAGE_WAIT_TABLE_REPLY;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS wreplsrv_pull_table_wait_table_reply(struct wreplsrv_pull_table_state *state)
+{
+ NTSTATUS status;
+
+ status = wrepl_pull_table_recv(state->req, state, &state->table_io);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ state->stage = WREPLSRV_PULL_TABLE_STAGE_DONE;
+
+ return NT_STATUS_OK;
+}
+
+static void wreplsrv_pull_table_handler(struct wreplsrv_pull_table_state *state)
+{
+ struct composite_context *c = state->c;
+
+ switch (state->stage) {
+ case WREPLSRV_PULL_TABLE_STAGE_WAIT_CONNECTION:
+ c->status = wreplsrv_pull_table_wait_connection(state);
+ break;
+ case WREPLSRV_PULL_TABLE_STAGE_WAIT_TABLE_REPLY:
+ c->status = wreplsrv_pull_table_wait_table_reply(state);
+ c->state = COMPOSITE_STATE_DONE;
+ break;
+ case WREPLSRV_PULL_TABLE_STAGE_DONE:
+ c->status = NT_STATUS_INTERNAL_ERROR;
+ }
+
+ if (!NT_STATUS_IS_OK(c->status)) {
+ c->state = COMPOSITE_STATE_ERROR;
+ }
+
+ if (c->state >= COMPOSITE_STATE_DONE && c->async.fn) {
+ c->async.fn(c);
+ }
+}
+
+static void wreplsrv_pull_table_handler_creq(struct composite_context *creq)
+{
+ struct wreplsrv_pull_table_state *state = talloc_get_type(creq->async.private_data,
+ struct wreplsrv_pull_table_state);
+ wreplsrv_pull_table_handler(state);
+ return;
+}
+
+static void wreplsrv_pull_table_handler_req(struct wrepl_request *req)
+{
+ struct wreplsrv_pull_table_state *state = talloc_get_type(req->async.private_data,
+ struct wreplsrv_pull_table_state);
+ wreplsrv_pull_table_handler(state);
+ return;
+}
+
+static struct composite_context *wreplsrv_pull_table_send(TALLOC_CTX *mem_ctx, struct wreplsrv_pull_table_io *io)
+{
+ struct composite_context *c = NULL;
+ struct wreplsrv_service *service = io->in.partner->service;
+ struct wreplsrv_pull_table_state *state = NULL;
+
+ c = talloc_zero(mem_ctx, struct composite_context);
+ if (!c) goto failed;
+
+ state = talloc_zero(c, struct wreplsrv_pull_table_state);
+ if (!state) goto failed;
+ state->c = c;
+ state->io = io;
+
+ c->state = COMPOSITE_STATE_IN_PROGRESS;
+ c->event_ctx = service->task->event_ctx;
+ c->private_data = state;
+
+ if (io->in.num_owners) {
+ state->table_io.out.num_partners = io->in.num_owners;
+ state->table_io.out.partners = io->in.owners;
+ state->stage = WREPLSRV_PULL_TABLE_STAGE_DONE;
+ composite_done(c);
+ return c;
+ }
+
+ state->stage = WREPLSRV_PULL_TABLE_STAGE_WAIT_CONNECTION;
+ state->creq = wreplsrv_out_connect_send(io->in.partner, WINSREPL_PARTNER_PULL, NULL);
+ if (!state->creq) goto failed;
+
+ state->creq->async.fn = wreplsrv_pull_table_handler_creq;
+ state->creq->async.private_data = state;
+
+ return c;
+failed:
+ talloc_free(c);
+ return NULL;
+}
+
+static NTSTATUS wreplsrv_pull_table_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
+ struct wreplsrv_pull_table_io *io)
+{
+ NTSTATUS status;
+
+ status = composite_wait(c);
+
+ if (NT_STATUS_IS_OK(status)) {
+ struct wreplsrv_pull_table_state *state = talloc_get_type(c->private_data,
+ struct wreplsrv_pull_table_state);
+ io->out.num_owners = state->table_io.out.num_partners;
+ io->out.owners = talloc_reference(mem_ctx, state->table_io.out.partners);
+ }
+
+ talloc_free(c);
+ return status;
+}
+
+struct wreplsrv_pull_names_io {
+ struct {
+ struct wreplsrv_partner *partner;
+ struct wreplsrv_out_connection *wreplconn;
+ struct wrepl_wins_owner owner;
+ } in;
+ struct {
+ uint32_t num_names;
+ struct wrepl_name *names;
+ } out;
+};
+
+enum wreplsrv_pull_names_stage {
+ WREPLSRV_PULL_NAMES_STAGE_WAIT_CONNECTION,
+ WREPLSRV_PULL_NAMES_STAGE_WAIT_SEND_REPLY,
+ WREPLSRV_PULL_NAMES_STAGE_DONE
+};
+
+struct wreplsrv_pull_names_state {
+ enum wreplsrv_pull_names_stage stage;
+ struct composite_context *c;
+ struct wrepl_request *req;
+ struct wrepl_pull_names pull_io;
+ struct wreplsrv_pull_names_io *io;
+ struct composite_context *creq;
+ struct wreplsrv_out_connection *wreplconn;
+};
+
+static void wreplsrv_pull_names_handler_req(struct wrepl_request *req);
+
+static NTSTATUS wreplsrv_pull_names_wait_connection(struct wreplsrv_pull_names_state *state)
+{
+ NTSTATUS status;
+
+ status = wreplsrv_out_connect_recv(state->creq, state, &state->wreplconn);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ state->pull_io.in.assoc_ctx = state->wreplconn->assoc_ctx.peer_ctx;
+ state->pull_io.in.partner = state->io->in.owner;
+ state->req = wrepl_pull_names_send(state->wreplconn->sock, &state->pull_io);
+ NT_STATUS_HAVE_NO_MEMORY(state->req);
+
+ state->req->async.fn = wreplsrv_pull_names_handler_req;
+ state->req->async.private_data = state;
+
+ state->stage = WREPLSRV_PULL_NAMES_STAGE_WAIT_SEND_REPLY;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS wreplsrv_pull_names_wait_send_reply(struct wreplsrv_pull_names_state *state)
+{
+ NTSTATUS status;
+
+ status = wrepl_pull_names_recv(state->req, state, &state->pull_io);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ state->stage = WREPLSRV_PULL_NAMES_STAGE_DONE;
+
+ return NT_STATUS_OK;
+}
+
+static void wreplsrv_pull_names_handler(struct wreplsrv_pull_names_state *state)
+{
+ struct composite_context *c = state->c;
+
+ switch (state->stage) {
+ case WREPLSRV_PULL_NAMES_STAGE_WAIT_CONNECTION:
+ c->status = wreplsrv_pull_names_wait_connection(state);
+ break;
+ case WREPLSRV_PULL_NAMES_STAGE_WAIT_SEND_REPLY:
+ c->status = wreplsrv_pull_names_wait_send_reply(state);
+ c->state = COMPOSITE_STATE_DONE;
+ break;
+ case WREPLSRV_PULL_NAMES_STAGE_DONE:
+ c->status = NT_STATUS_INTERNAL_ERROR;
+ }
+
+ if (!NT_STATUS_IS_OK(c->status)) {
+ c->state = COMPOSITE_STATE_ERROR;
+ }
+
+ if (c->state >= COMPOSITE_STATE_DONE && c->async.fn) {
+ c->async.fn(c);
+ }
+}
+
+static void wreplsrv_pull_names_handler_creq(struct composite_context *creq)
+{
+ struct wreplsrv_pull_names_state *state = talloc_get_type(creq->async.private_data,
+ struct wreplsrv_pull_names_state);
+ wreplsrv_pull_names_handler(state);
+ return;
+}
+
+static void wreplsrv_pull_names_handler_req(struct wrepl_request *req)
+{
+ struct wreplsrv_pull_names_state *state = talloc_get_type(req->async.private_data,
+ struct wreplsrv_pull_names_state);
+ wreplsrv_pull_names_handler(state);
+ return;
+}
+
+static struct composite_context *wreplsrv_pull_names_send(TALLOC_CTX *mem_ctx, struct wreplsrv_pull_names_io *io)
+{
+ struct composite_context *c = NULL;
+ struct wreplsrv_service *service = io->in.partner->service;
+ struct wreplsrv_pull_names_state *state = NULL;
+ enum winsrepl_partner_type partner_type = WINSREPL_PARTNER_PULL;
+
+ if (io->in.wreplconn) partner_type = WINSREPL_PARTNER_NONE;
+
+ c = talloc_zero(mem_ctx, struct composite_context);
+ if (!c) goto failed;
+
+ state = talloc_zero(c, struct wreplsrv_pull_names_state);
+ if (!state) goto failed;
+ state->c = c;
+ state->io = io;
+
+ c->state = COMPOSITE_STATE_IN_PROGRESS;
+ c->event_ctx = service->task->event_ctx;
+ c->private_data = state;
+
+ state->stage = WREPLSRV_PULL_NAMES_STAGE_WAIT_CONNECTION;
+ state->creq = wreplsrv_out_connect_send(io->in.partner, partner_type, io->in.wreplconn);
+ if (!state->creq) goto failed;
+
+ state->creq->async.fn = wreplsrv_pull_names_handler_creq;
+ state->creq->async.private_data = state;
+
+ return c;
+failed:
+ talloc_free(c);
+ return NULL;
+}
+
+static NTSTATUS wreplsrv_pull_names_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
+ struct wreplsrv_pull_names_io *io)
+{
+ NTSTATUS status;
+
+ status = composite_wait(c);
+
+ if (NT_STATUS_IS_OK(status)) {
+ struct wreplsrv_pull_names_state *state = talloc_get_type(c->private_data,
+ struct wreplsrv_pull_names_state);
+ io->out.num_names = state->pull_io.out.num_names;
+ io->out.names = talloc_reference(mem_ctx, state->pull_io.out.names);
+ }
+
+ talloc_free(c);
+ return status;
+
+}
+
+enum wreplsrv_pull_cycle_stage {
+ WREPLSRV_PULL_CYCLE_STAGE_WAIT_TABLE_REPLY,
+ WREPLSRV_PULL_CYCLE_STAGE_WAIT_SEND_REPLIES,
+ WREPLSRV_PULL_CYCLE_STAGE_WAIT_STOP_ASSOC,
+ WREPLSRV_PULL_CYCLE_STAGE_DONE
+};
+
+struct wreplsrv_pull_cycle_state {
+ enum wreplsrv_pull_cycle_stage stage;
+ struct composite_context *c;
+ struct wreplsrv_pull_cycle_io *io;
+ struct wreplsrv_pull_table_io table_io;
+ uint32_t current;
+ struct wreplsrv_pull_names_io names_io;
+ struct composite_context *creq;
+ struct wrepl_associate_stop assoc_stop_io;
+ struct wrepl_request *req;
+};
+
+static void wreplsrv_pull_cycle_handler_creq(struct composite_context *creq);
+static void wreplsrv_pull_cycle_handler_req(struct wrepl_request *req);
+
+static NTSTATUS wreplsrv_pull_cycle_next_owner_do_work(struct wreplsrv_pull_cycle_state *state)
+{
+ struct wreplsrv_owner *current_owner=NULL;
+ struct wreplsrv_owner *local_owner;
+ uint32_t i;
+ uint64_t old_max_version = 0;
+ bool do_pull = false;
+
+ for (i=state->current; i < state->table_io.out.num_owners; i++) {
+ current_owner = wreplsrv_find_owner(state->io->in.partner->service,
+ state->io->in.partner->pull.table,
+ state->table_io.out.owners[i].address);
+
+ local_owner = wreplsrv_find_owner(state->io->in.partner->service,
+ state->io->in.partner->service->table,
+ state->table_io.out.owners[i].address);
+ /*
+ * this means we are ourself the current owner,
+ * and we don't want replicate ourself
+ */
+ if (!current_owner) continue;
+
+ /*
+ * this means we don't have any records of this owner
+ * so fetch them
+ */
+ if (!local_owner) {
+ do_pull = true;
+
+ break;
+ }
+
+ /*
+ * this means the remote partner has some new records of this owner
+ * fetch them
+ */
+ if (current_owner->owner.max_version > local_owner->owner.max_version) {
+ do_pull = true;
+ old_max_version = local_owner->owner.max_version;
+ break;
+ }
+ }
+ state->current = i;
+
+ if (do_pull) {
+ state->names_io.in.partner = state->io->in.partner;
+ state->names_io.in.wreplconn = state->io->in.wreplconn;
+ state->names_io.in.owner = current_owner->owner;
+ state->names_io.in.owner.min_version = old_max_version + 1;
+ state->creq = wreplsrv_pull_names_send(state, &state->names_io);
+ NT_STATUS_HAVE_NO_MEMORY(state->creq);
+
+ state->creq->async.fn = wreplsrv_pull_cycle_handler_creq;
+ state->creq->async.private_data = state;
+
+ return STATUS_MORE_ENTRIES;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS wreplsrv_pull_cycle_next_owner_wrapper(struct wreplsrv_pull_cycle_state *state)
+{
+ NTSTATUS status;
+
+ status = wreplsrv_pull_cycle_next_owner_do_work(state);
+ if (NT_STATUS_IS_OK(status)) {
+ state->stage = WREPLSRV_PULL_CYCLE_STAGE_DONE;
+ } else if (NT_STATUS_EQUAL(STATUS_MORE_ENTRIES, status)) {
+ state->stage = WREPLSRV_PULL_CYCLE_STAGE_WAIT_SEND_REPLIES;
+ status = NT_STATUS_OK;
+ }
+
+ if (state->stage == WREPLSRV_PULL_CYCLE_STAGE_DONE && state->io->in.wreplconn) {
+ state->assoc_stop_io.in.assoc_ctx = state->io->in.wreplconn->assoc_ctx.peer_ctx;
+ state->assoc_stop_io.in.reason = 0;
+ state->req = wrepl_associate_stop_send(state->io->in.wreplconn->sock, &state->assoc_stop_io);
+ NT_STATUS_HAVE_NO_MEMORY(state->req);
+
+ state->req->async.fn = wreplsrv_pull_cycle_handler_req;
+ state->req->async.private_data = state;
+
+ state->stage = WREPLSRV_PULL_CYCLE_STAGE_WAIT_STOP_ASSOC;
+ }
+
+ return status;
+}
+
+static NTSTATUS wreplsrv_pull_cycle_wait_table_reply(struct wreplsrv_pull_cycle_state *state)
+{
+ NTSTATUS status;
+ uint32_t i;
+
+ status = wreplsrv_pull_table_recv(state->creq, state, &state->table_io);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ /* update partner table */
+ for (i=0; i < state->table_io.out.num_owners; i++) {
+ status = wreplsrv_add_table(state->io->in.partner->service,
+ state->io->in.partner,
+ &state->io->in.partner->pull.table,
+ state->table_io.out.owners[i].address,
+ state->table_io.out.owners[i].max_version);
+ NT_STATUS_NOT_OK_RETURN(status);
+ }
+
+ status = wreplsrv_pull_cycle_next_owner_wrapper(state);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ return status;
+}
+
+static NTSTATUS wreplsrv_pull_cycle_apply_records(struct wreplsrv_pull_cycle_state *state)
+{
+ NTSTATUS status;
+
+ status = wreplsrv_apply_records(state->io->in.partner,
+ &state->names_io.in.owner,
+ state->names_io.out.num_names,
+ state->names_io.out.names);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ talloc_free(state->names_io.out.names);
+ ZERO_STRUCT(state->names_io);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS wreplsrv_pull_cycle_wait_send_replies(struct wreplsrv_pull_cycle_state *state)
+{
+ NTSTATUS status;
+
+ status = wreplsrv_pull_names_recv(state->creq, state, &state->names_io);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ /*
+ * TODO: this should maybe an async call,
+ * because we may need some network access
+ * for conflict resolving
+ */
+ status = wreplsrv_pull_cycle_apply_records(state);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ status = wreplsrv_pull_cycle_next_owner_wrapper(state);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ return status;
+}
+
+static NTSTATUS wreplsrv_pull_cycle_wait_stop_assoc(struct wreplsrv_pull_cycle_state *state)
+{
+ NTSTATUS status;
+
+ status = wrepl_associate_stop_recv(state->req, &state->assoc_stop_io);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ state->stage = WREPLSRV_PULL_CYCLE_STAGE_DONE;
+
+ return status;
+}
+
+static void wreplsrv_pull_cycle_handler(struct wreplsrv_pull_cycle_state *state)
+{
+ struct composite_context *c = state->c;
+
+ switch (state->stage) {
+ case WREPLSRV_PULL_CYCLE_STAGE_WAIT_TABLE_REPLY:
+ c->status = wreplsrv_pull_cycle_wait_table_reply(state);
+ break;
+ case WREPLSRV_PULL_CYCLE_STAGE_WAIT_SEND_REPLIES:
+ c->status = wreplsrv_pull_cycle_wait_send_replies(state);
+ break;
+ case WREPLSRV_PULL_CYCLE_STAGE_WAIT_STOP_ASSOC:
+ c->status = wreplsrv_pull_cycle_wait_stop_assoc(state);
+ break;
+ case WREPLSRV_PULL_CYCLE_STAGE_DONE:
+ c->status = NT_STATUS_INTERNAL_ERROR;
+ }
+
+ if (state->stage == WREPLSRV_PULL_CYCLE_STAGE_DONE) {
+ c->state = COMPOSITE_STATE_DONE;
+ }
+
+ if (!NT_STATUS_IS_OK(c->status)) {
+ c->state = COMPOSITE_STATE_ERROR;
+ }
+
+ if (c->state >= COMPOSITE_STATE_DONE && c->async.fn) {
+ c->async.fn(c);
+ }
+}
+
+static void wreplsrv_pull_cycle_handler_creq(struct composite_context *creq)
+{
+ struct wreplsrv_pull_cycle_state *state = talloc_get_type(creq->async.private_data,
+ struct wreplsrv_pull_cycle_state);
+ wreplsrv_pull_cycle_handler(state);
+ return;
+}
+
+static void wreplsrv_pull_cycle_handler_req(struct wrepl_request *req)
+{
+ struct wreplsrv_pull_cycle_state *state = talloc_get_type(req->async.private_data,
+ struct wreplsrv_pull_cycle_state);
+ wreplsrv_pull_cycle_handler(state);
+ return;
+}
+
+struct composite_context *wreplsrv_pull_cycle_send(TALLOC_CTX *mem_ctx, struct wreplsrv_pull_cycle_io *io)
+{
+ struct composite_context *c = NULL;
+ struct wreplsrv_service *service = io->in.partner->service;
+ struct wreplsrv_pull_cycle_state *state = NULL;
+
+ c = talloc_zero(mem_ctx, struct composite_context);
+ if (!c) goto failed;
+
+ state = talloc_zero(c, struct wreplsrv_pull_cycle_state);
+ if (!state) goto failed;
+ state->c = c;
+ state->io = io;
+
+ c->state = COMPOSITE_STATE_IN_PROGRESS;
+ c->event_ctx = service->task->event_ctx;
+ c->private_data = state;
+
+ state->stage = WREPLSRV_PULL_CYCLE_STAGE_WAIT_TABLE_REPLY;
+ state->table_io.in.partner = io->in.partner;
+ state->table_io.in.num_owners = io->in.num_owners;
+ state->table_io.in.owners = io->in.owners;
+ state->creq = wreplsrv_pull_table_send(state, &state->table_io);
+ if (!state->creq) goto failed;
+
+ state->creq->async.fn = wreplsrv_pull_cycle_handler_creq;
+ state->creq->async.private_data = state;
+
+ return c;
+failed:
+ talloc_free(c);
+ return NULL;
+}
+
+NTSTATUS wreplsrv_pull_cycle_recv(struct composite_context *c)
+{
+ NTSTATUS status;
+
+ status = composite_wait(c);
+
+ talloc_free(c);
+ return status;
+}
+
+enum wreplsrv_push_notify_stage {
+ WREPLSRV_PUSH_NOTIFY_STAGE_WAIT_CONNECT,
+ WREPLSRV_PUSH_NOTIFY_STAGE_WAIT_INFORM,
+ WREPLSRV_PUSH_NOTIFY_STAGE_DONE
+};
+
+struct wreplsrv_push_notify_state {
+ enum wreplsrv_push_notify_stage stage;
+ struct composite_context *c;
+ struct wreplsrv_push_notify_io *io;
+ enum wrepl_replication_cmd command;
+ bool full_table;
+ struct wrepl_send_ctrl ctrl;
+ struct wrepl_request *req;
+ struct wrepl_packet req_packet;
+ struct wrepl_packet *rep_packet;
+ struct composite_context *creq;
+ struct wreplsrv_out_connection *wreplconn;
+};
+
+static void wreplsrv_push_notify_handler_creq(struct composite_context *creq);
+static void wreplsrv_push_notify_handler_req(struct wrepl_request *req);
+
+static NTSTATUS wreplsrv_push_notify_update(struct wreplsrv_push_notify_state *state)
+{
+ struct wreplsrv_service *service = state->io->in.partner->service;
+ struct wrepl_packet *req = &state->req_packet;
+ struct wrepl_replication *repl_out = &state->req_packet.message.replication;
+ struct wrepl_table *table_out = &state->req_packet.message.replication.info.table;
+ struct wreplsrv_in_connection *wrepl_in;
+ NTSTATUS status;
+ struct socket_context *sock;
+ struct packet_context *packet;
+ uint16_t fde_flags;
+
+ /* prepare the outgoing request */
+ req->opcode = WREPL_OPCODE_BITS;
+ req->assoc_ctx = state->wreplconn->assoc_ctx.peer_ctx;
+ req->mess_type = WREPL_REPLICATION;
+
+ repl_out->command = state->command;
+
+ status = wreplsrv_fill_wrepl_table(service, state, table_out,
+ service->wins_db->local_owner, state->full_table);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ /* queue the request */
+ state->req = wrepl_request_send(state->wreplconn->sock, req, NULL);
+ NT_STATUS_HAVE_NO_MEMORY(state->req);
+
+ /*
+ * now we need to convert the wrepl_socket (client connection)
+ * into a wreplsrv_in_connection (server connection), because
+ * we'll act as a server on this connection after the WREPL_REPL_UPDATE*
+ * message is received by the peer.
+ */
+
+ /* steal the socket_context */
+ sock = state->wreplconn->sock->sock;
+ state->wreplconn->sock->sock = NULL;
+ talloc_steal(state, sock);
+
+ /*
+ * steal the packet_context
+ * note the request DATA_BLOB we just send on the
+ * wrepl_socket (client connection) is still unter the
+ * packet context and will be send to the wire
+ */
+ packet = state->wreplconn->sock->packet;
+ state->wreplconn->sock->packet = NULL;
+ talloc_steal(state, packet);
+
+ /*
+ * get the fde_flags of the old fde event,
+ * so that we can later set the same flags to the new one
+ */
+ fde_flags = event_get_fd_flags(state->wreplconn->sock->event.fde);
+
+ /*
+ * free the wrepl_socket (client connection)
+ */
+ talloc_free(state->wreplconn->sock);
+ state->wreplconn->sock = NULL;
+
+ /*
+ * now create a wreplsrv_in_connection,
+ * on which we act as server
+ *
+ * NOTE: sock and packet will be stolen by
+ * wreplsrv_in_connection_merge()
+ */
+ status = wreplsrv_in_connection_merge(state->io->in.partner,
+ sock, packet, &wrepl_in);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ event_set_fd_flags(wrepl_in->conn->event.fde, fde_flags);
+
+ wrepl_in->assoc_ctx.peer_ctx = state->wreplconn->assoc_ctx.peer_ctx;
+ wrepl_in->assoc_ctx.our_ctx = 0;
+
+ /* now we can free the wreplsrv_out_connection */
+ talloc_free(state->wreplconn);
+ state->wreplconn = NULL;
+
+ state->stage = WREPLSRV_PUSH_NOTIFY_STAGE_DONE;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS wreplsrv_push_notify_inform(struct wreplsrv_push_notify_state *state)
+{
+ struct wreplsrv_service *service = state->io->in.partner->service;
+ struct wrepl_packet *req = &state->req_packet;
+ struct wrepl_replication *repl_out = &state->req_packet.message.replication;
+ struct wrepl_table *table_out = &state->req_packet.message.replication.info.table;
+ NTSTATUS status;
+
+ req->opcode = WREPL_OPCODE_BITS;
+ req->assoc_ctx = state->wreplconn->assoc_ctx.peer_ctx;
+ req->mess_type = WREPL_REPLICATION;
+
+ repl_out->command = state->command;
+
+ status = wreplsrv_fill_wrepl_table(service, state, table_out,
+ service->wins_db->local_owner, state->full_table);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ /* we won't get a reply to a inform message */
+ state->ctrl.send_only = true;
+
+ state->req = wrepl_request_send(state->wreplconn->sock, req, &state->ctrl);
+ NT_STATUS_HAVE_NO_MEMORY(state->req);
+
+ state->req->async.fn = wreplsrv_push_notify_handler_req;
+ state->req->async.private_data = state;
+
+ state->stage = WREPLSRV_PUSH_NOTIFY_STAGE_WAIT_INFORM;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS wreplsrv_push_notify_wait_connect(struct wreplsrv_push_notify_state *state)
+{
+ NTSTATUS status;
+
+ status = wreplsrv_out_connect_recv(state->creq, state, &state->wreplconn);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ /* is the peer doesn't support inform fallback to update */
+ switch (state->command) {
+ case WREPL_REPL_INFORM:
+ if (state->wreplconn->assoc_ctx.peer_major < 5) {
+ state->command = WREPL_REPL_UPDATE;
+ }
+ break;
+ case WREPL_REPL_INFORM2:
+ if (state->wreplconn->assoc_ctx.peer_major < 5) {
+ state->command = WREPL_REPL_UPDATE2;
+ }
+ break;
+ default:
+ break;
+ }
+
+ switch (state->command) {
+ case WREPL_REPL_UPDATE:
+ state->full_table = true;
+ return wreplsrv_push_notify_update(state);
+ case WREPL_REPL_UPDATE2:
+ state->full_table = false;
+ return wreplsrv_push_notify_update(state);
+ case WREPL_REPL_INFORM:
+ state->full_table = true;
+ return wreplsrv_push_notify_inform(state);
+ case WREPL_REPL_INFORM2:
+ state->full_table = false;
+ return wreplsrv_push_notify_inform(state);
+ default:
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ return NT_STATUS_INTERNAL_ERROR;
+}
+
+static NTSTATUS wreplsrv_push_notify_wait_inform(struct wreplsrv_push_notify_state *state)
+{
+ NTSTATUS status;
+
+ status = wrepl_request_recv(state->req, state, NULL);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ state->stage = WREPLSRV_PUSH_NOTIFY_STAGE_DONE;
+ return status;
+}
+
+static void wreplsrv_push_notify_handler(struct wreplsrv_push_notify_state *state)
+{
+ struct composite_context *c = state->c;
+
+ switch (state->stage) {
+ case WREPLSRV_PUSH_NOTIFY_STAGE_WAIT_CONNECT:
+ c->status = wreplsrv_push_notify_wait_connect(state);
+ break;
+ case WREPLSRV_PUSH_NOTIFY_STAGE_WAIT_INFORM:
+ c->status = wreplsrv_push_notify_wait_inform(state);
+ break;
+ case WREPLSRV_PUSH_NOTIFY_STAGE_DONE:
+ c->status = NT_STATUS_INTERNAL_ERROR;
+ }
+
+ if (state->stage == WREPLSRV_PUSH_NOTIFY_STAGE_DONE) {
+ c->state = COMPOSITE_STATE_DONE;
+ }
+
+ if (!NT_STATUS_IS_OK(c->status)) {
+ c->state = COMPOSITE_STATE_ERROR;
+ }
+
+ if (c->state >= COMPOSITE_STATE_DONE && c->async.fn) {
+ c->async.fn(c);
+ }
+}
+
+static void wreplsrv_push_notify_handler_creq(struct composite_context *creq)
+{
+ struct wreplsrv_push_notify_state *state = talloc_get_type(creq->async.private_data,
+ struct wreplsrv_push_notify_state);
+ wreplsrv_push_notify_handler(state);
+ return;
+}
+
+static void wreplsrv_push_notify_handler_req(struct wrepl_request *req)
+{
+ struct wreplsrv_push_notify_state *state = talloc_get_type(req->async.private_data,
+ struct wreplsrv_push_notify_state);
+ wreplsrv_push_notify_handler(state);
+ return;
+}
+
+struct composite_context *wreplsrv_push_notify_send(TALLOC_CTX *mem_ctx, struct wreplsrv_push_notify_io *io)
+{
+ struct composite_context *c = NULL;
+ struct wreplsrv_service *service = io->in.partner->service;
+ struct wreplsrv_push_notify_state *state = NULL;
+ enum winsrepl_partner_type partner_type;
+
+ c = talloc_zero(mem_ctx, struct composite_context);
+ if (!c) goto failed;
+
+ state = talloc_zero(c, struct wreplsrv_push_notify_state);
+ if (!state) goto failed;
+ state->c = c;
+ state->io = io;
+
+ if (io->in.inform) {
+ /* we can cache the connection in partner->push->wreplconn */
+ partner_type = WINSREPL_PARTNER_PUSH;
+ if (io->in.propagate) {
+ state->command = WREPL_REPL_INFORM2;
+ } else {
+ state->command = WREPL_REPL_INFORM;
+ }
+ } else {
+ /* we can NOT cache the connection */
+ partner_type = WINSREPL_PARTNER_NONE;
+ if (io->in.propagate) {
+ state->command = WREPL_REPL_UPDATE2;
+ } else {
+ state->command = WREPL_REPL_UPDATE;
+ }
+ }
+
+ c->state = COMPOSITE_STATE_IN_PROGRESS;
+ c->event_ctx = service->task->event_ctx;
+ c->private_data = state;
+
+ state->stage = WREPLSRV_PUSH_NOTIFY_STAGE_WAIT_CONNECT;
+ state->creq = wreplsrv_out_connect_send(io->in.partner, partner_type, NULL);
+ if (!state->creq) goto failed;
+
+ state->creq->async.fn = wreplsrv_push_notify_handler_creq;
+ state->creq->async.private_data = state;
+
+ return c;
+failed:
+ talloc_free(c);
+ return NULL;
+}
+
+NTSTATUS wreplsrv_push_notify_recv(struct composite_context *c)
+{
+ NTSTATUS status;
+
+ status = composite_wait(c);
+
+ talloc_free(c);
+ return status;
+}
diff --git a/source4/wrepl_server/wrepl_out_helpers.h b/source4/wrepl_server/wrepl_out_helpers.h
new file mode 100644
index 0000000000..92bbe561af
--- /dev/null
+++ b/source4/wrepl_server/wrepl_out_helpers.h
@@ -0,0 +1,37 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ WINS Replication server
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+struct wreplsrv_pull_cycle_io {
+ struct {
+ struct wreplsrv_partner *partner;
+ uint32_t num_owners;
+ struct wrepl_wins_owner *owners;
+ struct wreplsrv_out_connection *wreplconn;
+ } in;
+};
+
+struct wreplsrv_push_notify_io {
+ struct {
+ struct wreplsrv_partner *partner;
+ bool inform;
+ bool propagate;
+ } in;
+};
diff --git a/source4/wrepl_server/wrepl_out_pull.c b/source4/wrepl_server/wrepl_out_pull.c
new file mode 100644
index 0000000000..557c553dd1
--- /dev/null
+++ b/source4/wrepl_server/wrepl_out_pull.c
@@ -0,0 +1,142 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ WINS Replication server
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "librpc/gen_ndr/winsrepl.h"
+#include "wrepl_server/wrepl_server.h"
+#include "libcli/composite/composite.h"
+
+static void wreplsrv_out_pull_reschedule(struct wreplsrv_partner *partner, uint32_t interval)
+{
+ NTSTATUS status;
+
+ partner->pull.next_run = timeval_current_ofs(interval, 0);
+ status = wreplsrv_periodic_schedule(partner->service, interval);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("wreplsrv_periodic_schedule() failed\n"));
+ }
+}
+
+static void wreplsrv_pull_handler_creq(struct composite_context *creq)
+{
+ struct wreplsrv_partner *partner = talloc_get_type(creq->async.private_data, struct wreplsrv_partner);
+ struct wreplsrv_pull_cycle_io *old_cycle_io;
+ struct wrepl_table inform_in;
+
+ partner->pull.last_status = wreplsrv_pull_cycle_recv(partner->pull.creq);
+ partner->pull.creq = NULL;
+
+ old_cycle_io = partner->pull.cycle_io;
+ partner->pull.cycle_io = NULL;
+
+ if (NT_STATUS_IS_OK(partner->pull.last_status)) {
+ partner->pull.error_count = 0;
+ DEBUG(1,("wreplsrv_pull_cycle(%s): %s\n",
+ partner->address, nt_errstr(partner->pull.last_status)));
+ goto done;
+ }
+
+ partner->pull.error_count++;
+
+ if (partner->pull.error_count > 1) {
+ uint32_t retry_interval;
+
+ retry_interval = partner->pull.error_count * partner->pull.retry_interval;
+ retry_interval = MIN(retry_interval, partner->pull.interval);
+
+ DEBUG(0,("wreplsrv_pull_cycle(%s): %s: error_count: %u: reschedule(%u)\n",
+ partner->address, nt_errstr(partner->pull.last_status),
+ partner->pull.error_count, retry_interval));
+
+ wreplsrv_out_pull_reschedule(partner, retry_interval);
+ goto done;
+ }
+
+ DEBUG(0,("wreplsrv_pull_cycle(%s): %s: error_count:%u retry\n",
+ partner->address, nt_errstr(partner->pull.last_status),
+ partner->pull.error_count));
+ inform_in.partner_count = old_cycle_io->in.num_owners;
+ inform_in.partners = old_cycle_io->in.owners;
+ wreplsrv_out_partner_pull(partner, &inform_in);
+done:
+ talloc_free(old_cycle_io);
+}
+
+void wreplsrv_out_partner_pull(struct wreplsrv_partner *partner, struct wrepl_table *inform_in)
+{
+ /* there's already a pull in progress, so we're done */
+ if (partner->pull.creq) return;
+
+ partner->pull.cycle_io = talloc(partner, struct wreplsrv_pull_cycle_io);
+ if (!partner->pull.cycle_io) {
+ goto nomem;
+ }
+
+ partner->pull.cycle_io->in.partner = partner;
+ partner->pull.cycle_io->in.wreplconn = NULL;
+ if (inform_in) {
+ partner->pull.cycle_io->in.num_owners = inform_in->partner_count;
+ partner->pull.cycle_io->in.owners = inform_in->partners;
+ talloc_steal(partner->pull.cycle_io, inform_in->partners);
+ } else {
+ partner->pull.cycle_io->in.num_owners = 0;
+ partner->pull.cycle_io->in.owners = NULL;
+ }
+ partner->pull.creq = wreplsrv_pull_cycle_send(partner->pull.cycle_io, partner->pull.cycle_io);
+ if (!partner->pull.creq) {
+ DEBUG(1,("wreplsrv_pull_cycle_send(%s) failed\n",
+ partner->address));
+ goto nomem;
+ }
+
+ partner->pull.creq->async.fn = wreplsrv_pull_handler_creq;
+ partner->pull.creq->async.private_data = partner;
+
+ return;
+nomem:
+ talloc_free(partner->pull.cycle_io);
+ partner->pull.cycle_io = NULL;
+ DEBUG(0,("wreplsrv_out_partner_pull(%s): no memory? (ignoring)\n",
+ partner->address));
+ return;
+}
+
+NTSTATUS wreplsrv_out_pull_run(struct wreplsrv_service *service)
+{
+ struct wreplsrv_partner *partner;
+
+ for (partner = service->partners; partner; partner = partner->next) {
+ /* if it's not a pull partner, go to the next partner */
+ if (!(partner->type & WINSREPL_PARTNER_PULL)) continue;
+
+ /* if pulling is disabled for the partner, go to the next one */
+ if (partner->pull.interval == 0) continue;
+
+ /* if the next timer isn't reached, go to the next partner */
+ if (!timeval_expired(&partner->pull.next_run)) continue;
+
+ wreplsrv_out_pull_reschedule(partner, partner->pull.interval);
+
+ wreplsrv_out_partner_pull(partner, NULL);
+ }
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/wrepl_server/wrepl_out_push.c b/source4/wrepl_server/wrepl_out_push.c
new file mode 100644
index 0000000000..8f0c409662
--- /dev/null
+++ b/source4/wrepl_server/wrepl_out_push.c
@@ -0,0 +1,144 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ WINS Replication server
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "librpc/gen_ndr/winsrepl.h"
+#include "wrepl_server/wrepl_server.h"
+#include "libcli/composite/composite.h"
+#include "nbt_server/wins/winsdb.h"
+
+static void wreplsrv_out_partner_push(struct wreplsrv_partner *partner, bool propagate);
+
+static void wreplsrv_push_handler_creq(struct composite_context *creq)
+{
+ struct wreplsrv_partner *partner = talloc_get_type(creq->async.private_data, struct wreplsrv_partner);
+ struct wreplsrv_push_notify_io *old_notify_io;
+
+ partner->push.last_status = wreplsrv_push_notify_recv(partner->push.creq);
+ partner->push.creq = NULL;
+
+ old_notify_io = partner->push.notify_io;
+ partner->push.notify_io = NULL;
+
+ if (NT_STATUS_IS_OK(partner->push.last_status)) {
+ partner->push.error_count = 0;
+ DEBUG(2,("wreplsrv_push_notify(%s): %s\n",
+ partner->address, nt_errstr(partner->push.last_status)));
+ goto done;
+ }
+
+ partner->push.error_count++;
+
+ if (partner->push.error_count > 1) {
+ DEBUG(1,("wreplsrv_push_notify(%s): %s: error_count: %u: giving up\n",
+ partner->address, nt_errstr(partner->push.last_status),
+ partner->push.error_count));
+ goto done;
+ }
+
+ DEBUG(1,("wreplsrv_push_notify(%s): %s: error_count: %u: retry\n",
+ partner->address, nt_errstr(partner->push.last_status),
+ partner->push.error_count));
+ wreplsrv_out_partner_push(partner, old_notify_io->in.propagate);
+done:
+ talloc_free(old_notify_io);
+}
+
+static void wreplsrv_out_partner_push(struct wreplsrv_partner *partner, bool propagate)
+{
+ /* a push for this partner is currently in progress, so we're done */
+ if (partner->push.creq) return;
+
+ /* now prepare the push notify */
+ partner->push.notify_io = talloc(partner, struct wreplsrv_push_notify_io);
+ if (!partner->push.notify_io) {
+ goto nomem;
+ }
+
+ partner->push.notify_io->in.partner = partner;
+ partner->push.notify_io->in.inform = partner->push.use_inform;
+ partner->push.notify_io->in.propagate = propagate;
+ partner->push.creq = wreplsrv_push_notify_send(partner->push.notify_io, partner->push.notify_io);
+ if (!partner->push.creq) {
+ DEBUG(1,("wreplsrv_push_notify_send(%s) failed nomem?\n",
+ partner->address));
+ goto nomem;
+ }
+
+ partner->push.creq->async.fn = wreplsrv_push_handler_creq;
+ partner->push.creq->async.private_data = partner;
+
+ return;
+nomem:
+ talloc_free(partner->push.notify_io);
+ partner->push.notify_io = NULL;
+ DEBUG(1,("wreplsrv_out_partner_push(%s,%u) failed nomem? (ignoring)\n",
+ partner->address, propagate));
+ return;
+}
+
+static uint32_t wreplsrv_calc_change_count(struct wreplsrv_partner *partner, uint64_t maxVersionID)
+{
+ uint64_t tmp_diff = UINT32_MAX;
+
+ /* catch an overflow */
+ if (partner->push.maxVersionID > maxVersionID) {
+ goto done;
+ }
+
+ tmp_diff = maxVersionID - partner->push.maxVersionID;
+
+ if (tmp_diff > UINT32_MAX) {
+ tmp_diff = UINT32_MAX;
+ goto done;
+ }
+
+done:
+ partner->push.maxVersionID = maxVersionID;
+ return (uint32_t)(tmp_diff & UINT32_MAX);
+}
+
+NTSTATUS wreplsrv_out_push_run(struct wreplsrv_service *service)
+{
+ struct wreplsrv_partner *partner;
+ uint64_t seqnumber;
+ uint32_t change_count;
+
+ seqnumber = winsdb_get_maxVersion(service->wins_db);
+
+ for (partner = service->partners; partner; partner = partner->next) {
+ /* if it's not a push partner, go to the next partner */
+ if (!(partner->type & WINSREPL_PARTNER_PUSH)) continue;
+
+ /* if push notifies are disabled for this partner, go to the next partner */
+ if (partner->push.change_count == 0) continue;
+
+ /* get the actual change count for the partner */
+ change_count = wreplsrv_calc_change_count(partner, seqnumber);
+
+ /* if the configured change count isn't reached, go to the next partner */
+ if (change_count < partner->push.change_count) continue;
+
+ wreplsrv_out_partner_push(partner, false);
+ }
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/wrepl_server/wrepl_periodic.c b/source4/wrepl_server/wrepl_periodic.c
new file mode 100644
index 0000000000..4771544428
--- /dev/null
+++ b/source4/wrepl_server/wrepl_periodic.c
@@ -0,0 +1,118 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ WINS Replication server
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/events/events.h"
+#include "smbd/service_task.h"
+#include "smbd/service.h"
+#include "librpc/gen_ndr/winsrepl.h"
+#include "wrepl_server/wrepl_server.h"
+
+static NTSTATUS wreplsrv_periodic_run(struct wreplsrv_service *service)
+{
+ NTSTATUS status;
+
+ status = wreplsrv_load_partners(service);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ status = wreplsrv_scavenging_run(service);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ status = wreplsrv_out_pull_run(service);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ status = wreplsrv_out_push_run(service);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ return NT_STATUS_OK;
+}
+
+static void wreplsrv_periodic_handler_te(struct tevent_context *ev, struct tevent_timer *te,
+ struct timeval t, void *ptr)
+{
+ struct wreplsrv_service *service = talloc_get_type(ptr, struct wreplsrv_service);
+ NTSTATUS status;
+
+ service->periodic.te = NULL;
+
+ status = wreplsrv_periodic_schedule(service, service->config.periodic_interval);
+ if (!NT_STATUS_IS_OK(status)) {
+ task_server_terminate(service->task, nt_errstr(status));
+ return;
+ }
+
+ status = wreplsrv_periodic_run(service);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("wresrv_periodic_run() failed: %s\n", nt_errstr(status)));
+ }
+}
+
+NTSTATUS wreplsrv_periodic_schedule(struct wreplsrv_service *service, uint32_t next_interval)
+{
+ TALLOC_CTX *tmp_mem;
+ struct tevent_timer *new_te;
+ struct timeval next_time;
+
+ /* prevent looping */
+ if (next_interval == 0) next_interval = 1;
+
+ next_time = timeval_current_ofs(next_interval, 5000);
+
+ if (service->periodic.te) {
+ /*
+ * if the timestamp of the new event is higher,
+ * as current next we don't need to reschedule
+ */
+ if (timeval_compare(&next_time, &service->periodic.next_event) > 0) {
+ return NT_STATUS_OK;
+ }
+ }
+
+ /* reset the next scheduled timestamp */
+ service->periodic.next_event = next_time;
+
+ new_te = event_add_timed(service->task->event_ctx, service,
+ service->periodic.next_event,
+ wreplsrv_periodic_handler_te, service);
+ NT_STATUS_HAVE_NO_MEMORY(new_te);
+
+ tmp_mem = talloc_new(service);
+ DEBUG(6,("wreplsrv_periodic_schedule(%u) %sscheduled for: %s\n",
+ next_interval,
+ (service->periodic.te?"re":""),
+ nt_time_string(tmp_mem, timeval_to_nttime(&next_time))));
+ talloc_free(tmp_mem);
+
+ talloc_free(service->periodic.te);
+ service->periodic.te = new_te;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS wreplsrv_setup_periodic(struct wreplsrv_service *service)
+{
+ NTSTATUS status;
+
+ status = wreplsrv_periodic_schedule(service, 0);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/wrepl_server/wrepl_scavenging.c b/source4/wrepl_server/wrepl_scavenging.c
new file mode 100644
index 0000000000..8fc7d0a6f0
--- /dev/null
+++ b/source4/wrepl_server/wrepl_scavenging.c
@@ -0,0 +1,565 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ WINS Replication server
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "librpc/gen_ndr/ndr_winsrepl.h"
+#include "wrepl_server/wrepl_server.h"
+#include "nbt_server/wins/winsdb.h"
+#include "ldb/include/ldb.h"
+#include "ldb/include/ldb_errors.h"
+#include "system/time.h"
+#include "smbd/service_task.h"
+#include "lib/messaging/irpc.h"
+#include "librpc/gen_ndr/ndr_irpc.h"
+#include "librpc/gen_ndr/ndr_nbt.h"
+#include "param/param.h"
+
+const char *wreplsrv_owner_filter(struct wreplsrv_service *service,
+ TALLOC_CTX *mem_ctx,
+ const char *wins_owner)
+{
+ if (strcmp(wins_owner, service->wins_db->local_owner) == 0) {
+ return talloc_asprintf(mem_ctx, "(|(winsOwner=%s)(winsOwner=0.0.0.0))",
+ wins_owner);
+ }
+
+ return talloc_asprintf(mem_ctx, "(&(winsOwner=%s)(!(winsOwner=0.0.0.0)))",
+ wins_owner);
+}
+
+static NTSTATUS wreplsrv_scavenging_owned_records(struct wreplsrv_service *service, TALLOC_CTX *tmp_mem)
+{
+ NTSTATUS status;
+ struct winsdb_record *rec = NULL;
+ struct ldb_result *res = NULL;
+ const char *owner_filter;
+ const char *filter;
+ uint32_t i;
+ int ret;
+ time_t now = time(NULL);
+ const char *now_timestr;
+ const char *action;
+ const char *old_state=NULL;
+ const char *new_state=NULL;
+ uint32_t modify_flags;
+ bool modify_record;
+ bool delete_record;
+ bool delete_tombstones;
+ struct timeval tombstone_extra_time;
+ const char *local_owner = service->wins_db->local_owner;
+ bool propagate = lp_parm_bool(service->task->lp_ctx, NULL, "wreplsrv", "propagate name releases", false);
+
+ now_timestr = ldb_timestring(tmp_mem, now);
+ NT_STATUS_HAVE_NO_MEMORY(now_timestr);
+ owner_filter = wreplsrv_owner_filter(service, tmp_mem, local_owner);
+ NT_STATUS_HAVE_NO_MEMORY(owner_filter);
+ filter = talloc_asprintf(tmp_mem,
+ "(&%s(objectClass=winsRecord)"
+ "(expireTime<=%s))",
+ owner_filter, now_timestr);
+ NT_STATUS_HAVE_NO_MEMORY(filter);
+ ret = ldb_search(service->wins_db->ldb, tmp_mem, &res, NULL, LDB_SCOPE_SUBTREE, NULL, "%s", filter);
+ if (ret != LDB_SUCCESS) return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ DEBUG(10,("WINS scavenging: filter '%s' count %d\n", filter, res->count));
+
+ tombstone_extra_time = timeval_add(&service->startup_time,
+ service->config.tombstone_extra_timeout,
+ 0);
+ delete_tombstones = timeval_expired(&tombstone_extra_time);
+
+ for (i=0; i < res->count; i++) {
+ bool has_replicas = false;
+
+ /*
+ * we pass '0' as 'now' here,
+ * because we want to get the raw timestamps which are in the DB
+ */
+ status = winsdb_record(service->wins_db, res->msgs[i], tmp_mem, 0, &rec);
+ NT_STATUS_NOT_OK_RETURN(status);
+ talloc_free(res->msgs[i]);
+
+ modify_flags = 0;
+ modify_record = false;
+ delete_record = false;
+
+ switch (rec->state) {
+ case WREPL_STATE_ACTIVE:
+ old_state = "active";
+ if (rec->is_static) {
+ /*
+ *we store it again, so that it won't appear
+ * in the scavenging the next time
+ */
+ old_state = "active(static)";
+ new_state = "active(static)";
+ modify_flags = 0;
+ modify_record = true;
+ break;
+ }
+ if (rec->type != WREPL_TYPE_SGROUP || !propagate) {
+ new_state = "released";
+ rec->state = WREPL_STATE_RELEASED;
+ rec->expire_time= service->config.tombstone_interval + now;
+ modify_flags = 0;
+ modify_record = true;
+ break;
+ }
+ /* check if there's any replica address */
+ for (i=0;rec->addresses[i];i++) {
+ if (strcmp(rec->addresses[i]->wins_owner, local_owner) != 0) {
+ has_replicas = true;
+ rec->addresses[i]->expire_time= service->config.renew_interval + now;
+ }
+ }
+ if (has_replicas) {
+ /* if it has replica addresses propagate them */
+ new_state = "active(propagated)";
+ rec->state = WREPL_STATE_ACTIVE;
+ rec->expire_time= service->config.renew_interval + now;
+ modify_flags = WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP;
+ modify_record = true;
+ break;
+ }
+ /*
+ * if it doesn't have replica addresses, make it a tombstone,
+ * so that the released owned addresses are propagated
+ */
+ new_state = "tombstone";
+ rec->state = WREPL_STATE_TOMBSTONE;
+ rec->expire_time= time(NULL) +
+ service->config.tombstone_interval +
+ service->config.tombstone_timeout;
+ modify_flags = WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP;
+ modify_record = true;
+ break;
+
+ case WREPL_STATE_RELEASED:
+ old_state = "released";
+ new_state = "tombstone";
+ rec->state = WREPL_STATE_TOMBSTONE;
+ rec->expire_time= service->config.tombstone_timeout + now;
+ modify_flags = WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP;
+ modify_record = true;
+ break;
+
+ case WREPL_STATE_TOMBSTONE:
+ old_state = "tombstone";
+ new_state = "tombstone";
+ if (!delete_tombstones) break;
+ new_state = "deleted";
+ delete_record = true;
+ break;
+
+ case WREPL_STATE_RESERVED:
+ DEBUG(0,("%s: corrupted record: %s\n",
+ __location__, nbt_name_string(rec, rec->name)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ if (modify_record) {
+ action = "modify";
+ ret = winsdb_modify(service->wins_db, rec, modify_flags);
+ } else if (delete_record) {
+ action = "delete";
+ ret = winsdb_delete(service->wins_db, rec);
+ } else {
+ action = "skip";
+ ret = NBT_RCODE_OK;
+ }
+
+ if (ret != NBT_RCODE_OK) {
+ DEBUG(2,("WINS scavenging: failed to %s name %s (owned:%s -> owned:%s): error:%u\n",
+ action, nbt_name_string(rec, rec->name), old_state, new_state, ret));
+ } else {
+ DEBUG(4,("WINS scavenging: %s name: %s (owned:%s -> owned:%s)\n",
+ action, nbt_name_string(rec, rec->name), old_state, new_state));
+ }
+
+ talloc_free(rec);
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS wreplsrv_scavenging_replica_non_active_records(struct wreplsrv_service *service, TALLOC_CTX *tmp_mem)
+{
+ NTSTATUS status;
+ struct winsdb_record *rec = NULL;
+ struct ldb_result *res = NULL;
+ const char *owner_filter;
+ const char *filter;
+ uint32_t i;
+ int ret;
+ time_t now = time(NULL);
+ const char *now_timestr;
+ const char *action;
+ const char *old_state=NULL;
+ const char *new_state=NULL;
+ uint32_t modify_flags;
+ bool modify_record;
+ bool delete_record;
+ bool delete_tombstones;
+ struct timeval tombstone_extra_time;
+
+ now_timestr = ldb_timestring(tmp_mem, now);
+ NT_STATUS_HAVE_NO_MEMORY(now_timestr);
+ owner_filter = wreplsrv_owner_filter(service, tmp_mem,
+ service->wins_db->local_owner);
+ NT_STATUS_HAVE_NO_MEMORY(owner_filter);
+ filter = talloc_asprintf(tmp_mem,
+ "(&(!%s)(objectClass=winsRecord)"
+ "(!(recordState=%u))(expireTime<=%s))",
+ owner_filter, WREPL_STATE_ACTIVE, now_timestr);
+ NT_STATUS_HAVE_NO_MEMORY(filter);
+ ret = ldb_search(service->wins_db->ldb, tmp_mem, &res, NULL, LDB_SCOPE_SUBTREE, NULL, "%s", filter);
+ if (ret != LDB_SUCCESS) return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ DEBUG(10,("WINS scavenging: filter '%s' count %d\n", filter, res->count));
+
+ tombstone_extra_time = timeval_add(&service->startup_time,
+ service->config.tombstone_extra_timeout,
+ 0);
+ delete_tombstones = timeval_expired(&tombstone_extra_time);
+
+ for (i=0; i < res->count; i++) {
+ /*
+ * we pass '0' as 'now' here,
+ * because we want to get the raw timestamps which are in the DB
+ */
+ status = winsdb_record(service->wins_db, res->msgs[i], tmp_mem, 0, &rec);
+ NT_STATUS_NOT_OK_RETURN(status);
+ talloc_free(res->msgs[i]);
+
+ modify_flags = 0;
+ modify_record = false;
+ delete_record = false;
+
+ switch (rec->state) {
+ case WREPL_STATE_ACTIVE:
+ DEBUG(0,("%s: corrupted record: %s\n",
+ __location__, nbt_name_string(rec, rec->name)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+
+ case WREPL_STATE_RELEASED:
+ old_state = "released";
+ new_state = "tombstone";
+ rec->state = WREPL_STATE_TOMBSTONE;
+ rec->expire_time= service->config.tombstone_timeout + now;
+ modify_flags = 0;
+ modify_record = true;
+ break;
+
+ case WREPL_STATE_TOMBSTONE:
+ old_state = "tombstone";
+ new_state = "tombstone";
+ if (!delete_tombstones) break;
+ new_state = "deleted";
+ delete_record = true;
+ break;
+
+ case WREPL_STATE_RESERVED:
+ DEBUG(0,("%s: corrupted record: %s\n",
+ __location__, nbt_name_string(rec, rec->name)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ if (modify_record) {
+ action = "modify";
+ ret = winsdb_modify(service->wins_db, rec, modify_flags);
+ } else if (delete_record) {
+ action = "delete";
+ ret = winsdb_delete(service->wins_db, rec);
+ } else {
+ action = "skip";
+ ret = NBT_RCODE_OK;
+ }
+
+ if (ret != NBT_RCODE_OK) {
+ DEBUG(2,("WINS scavenging: failed to %s name %s (replica:%s -> replica:%s): error:%u\n",
+ action, nbt_name_string(rec, rec->name), old_state, new_state, ret));
+ } else {
+ DEBUG(4,("WINS scavenging: %s name: %s (replica:%s -> replica:%s)\n",
+ action, nbt_name_string(rec, rec->name), old_state, new_state));
+ }
+
+ talloc_free(rec);
+ }
+
+ return NT_STATUS_OK;
+}
+
+struct verify_state {
+ struct messaging_context *msg_ctx;
+ struct wreplsrv_service *service;
+ struct winsdb_record *rec;
+ struct nbtd_proxy_wins_challenge r;
+};
+
+static void verify_handler(struct irpc_request *ireq)
+{
+ struct verify_state *s = talloc_get_type(ireq->async.private_data,
+ struct verify_state);
+ struct winsdb_record *rec = s->rec;
+ const char *action;
+ const char *old_state = "active";
+ const char *new_state = "active";
+ const char *new_owner = "replica";
+ uint32_t modify_flags = 0;
+ bool modify_record = false;
+ bool delete_record = false;
+ bool different = false;
+ int ret;
+ NTSTATUS status;
+ uint32_t i, j;
+
+ /*
+ * - if the name isn't present anymore remove our record
+ * - if the name is found and not a normal group check if the addresses match,
+ * - if they don't match remove the record
+ * - if they match do nothing
+ * - if an error happens do nothing
+ */
+ status = irpc_call_recv(ireq);
+ if (NT_STATUS_EQUAL(NT_STATUS_OBJECT_NAME_NOT_FOUND, status)) {
+ delete_record = true;
+ new_state = "deleted";
+ } else if (NT_STATUS_IS_OK(status) && rec->type != WREPL_TYPE_GROUP) {
+ for (i=0; i < s->r.out.num_addrs; i++) {
+ bool found = false;
+ for (j=0; rec->addresses[j]; j++) {
+ if (strcmp(s->r.out.addrs[i].addr, rec->addresses[j]->address) == 0) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ different = true;
+ break;
+ }
+ }
+ } else if (NT_STATUS_IS_OK(status) && rec->type == WREPL_TYPE_GROUP) {
+ if (s->r.out.num_addrs != 1 || strcmp(s->r.out.addrs[0].addr, "255.255.255.255") != 0) {
+ different = true;
+ }
+ }
+
+ if (different) {
+ /*
+ * if the reply from the owning wins server has different addresses
+ * then take the ownership of the record and make it a tombstone
+ * this will then hopefully replicated to the original owner of the record
+ * which will then propagate it's own record, so that the current record will
+ * be replicated to to us
+ */
+ DEBUG(2,("WINS scavenging: replica %s verify got different addresses from winsserver: %s: tombstoning record\n",
+ nbt_name_string(rec, rec->name), rec->wins_owner));
+
+ rec->state = WREPL_STATE_TOMBSTONE;
+ rec->expire_time= time(NULL) + s->service->config.tombstone_timeout;
+ for (i=0; rec->addresses[i]; i++) {
+ rec->addresses[i]->expire_time = rec->expire_time;
+ }
+ modify_record = true;
+ modify_flags = WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP;
+ new_state = "tombstone";
+ new_owner = "owned";
+ } else if (NT_STATUS_IS_OK(status)) {
+ /* if the addresses are the same, just update the timestamps */
+ rec->expire_time = time(NULL) + s->service->config.verify_interval;
+ for (i=0; rec->addresses[i]; i++) {
+ rec->addresses[i]->expire_time = rec->expire_time;
+ }
+ modify_record = true;
+ modify_flags = 0;
+ new_state = "active";
+ }
+
+ if (modify_record) {
+ action = "modify";
+ ret = winsdb_modify(s->service->wins_db, rec, modify_flags);
+ } else if (delete_record) {
+ action = "delete";
+ ret = winsdb_delete(s->service->wins_db, rec);
+ } else {
+ action = "skip";
+ ret = NBT_RCODE_OK;
+ }
+
+ if (ret != NBT_RCODE_OK) {
+ DEBUG(2,("WINS scavenging: failed to %s name %s (replica:%s -> %s:%s): error:%u\n",
+ action, nbt_name_string(rec, rec->name), old_state, new_owner, new_state, ret));
+ } else {
+ DEBUG(4,("WINS scavenging: %s name: %s (replica:%s -> %s:%s): %s: %s\n",
+ action, nbt_name_string(rec, rec->name), old_state, new_owner, new_state,
+ rec->wins_owner, nt_errstr(status)));
+ }
+
+ talloc_free(s);
+}
+
+static NTSTATUS wreplsrv_scavenging_replica_active_records(struct wreplsrv_service *service, TALLOC_CTX *tmp_mem)
+{
+ NTSTATUS status;
+ struct winsdb_record *rec = NULL;
+ struct ldb_result *res = NULL;
+ const char *owner_filter;
+ const char *filter;
+ uint32_t i;
+ int ret;
+ time_t now = time(NULL);
+ const char *now_timestr;
+ struct irpc_request *ireq;
+ struct verify_state *s;
+ struct server_id *nbt_servers;
+
+ nbt_servers = irpc_servers_byname(service->task->msg_ctx, tmp_mem, "nbt_server");
+ if ((nbt_servers == NULL) || (nbt_servers[0].id == 0)) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ now_timestr = ldb_timestring(tmp_mem, now);
+ NT_STATUS_HAVE_NO_MEMORY(now_timestr);
+ owner_filter = wreplsrv_owner_filter(service, tmp_mem,
+ service->wins_db->local_owner);
+ NT_STATUS_HAVE_NO_MEMORY(owner_filter);
+ filter = talloc_asprintf(tmp_mem,
+ "(&(!%s)(objectClass=winsRecord)"
+ "(recordState=%u)(expireTime<=%s))",
+ owner_filter, WREPL_STATE_ACTIVE, now_timestr);
+ NT_STATUS_HAVE_NO_MEMORY(filter);
+ ret = ldb_search(service->wins_db->ldb, tmp_mem, &res, NULL, LDB_SCOPE_SUBTREE, NULL, "%s", filter);
+ if (ret != LDB_SUCCESS) return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ DEBUG(10,("WINS scavenging: filter '%s' count %d\n", filter, res->count));
+
+ for (i=0; i < res->count; i++) {
+ /*
+ * we pass '0' as 'now' here,
+ * because we want to get the raw timestamps which are in the DB
+ */
+ status = winsdb_record(service->wins_db, res->msgs[i], tmp_mem, 0, &rec);
+ NT_STATUS_NOT_OK_RETURN(status);
+ talloc_free(res->msgs[i]);
+
+ if (rec->state != WREPL_STATE_ACTIVE) {
+ DEBUG(0,("%s: corrupted record: %s\n",
+ __location__, nbt_name_string(rec, rec->name)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ /*
+ * ask the owning wins server if the record still exists,
+ * if not delete the record
+ *
+ * TODO: NOTE: this is a simpliefied version, to verify that
+ * a record still exist, I assume that w2k3 uses
+ * DCERPC calls or some WINSREPL packets for this,
+ * but we use a wins name query
+ */
+ DEBUG(2,("ask wins server '%s' if '%s' with version_id:%llu still exists\n",
+ rec->wins_owner, nbt_name_string(rec, rec->name),
+ (unsigned long long)rec->version));
+
+ s = talloc_zero(tmp_mem, struct verify_state);
+ NT_STATUS_HAVE_NO_MEMORY(s);
+ s->msg_ctx = service->task->msg_ctx;
+ s->service = service;
+ s->rec = talloc_steal(s, rec);
+
+ s->r.in.name = *rec->name;
+ s->r.in.num_addrs = 1;
+ s->r.in.addrs = talloc_array(s, struct nbtd_proxy_wins_addr, s->r.in.num_addrs);
+ NT_STATUS_HAVE_NO_MEMORY(s->r.in.addrs);
+ /* TODO: fix pidl to handle inline ipv4address arrays */
+ s->r.in.addrs[0].addr = rec->wins_owner;
+
+ ireq = IRPC_CALL_SEND(s->msg_ctx, nbt_servers[0],
+ irpc, NBTD_PROXY_WINS_CHALLENGE,
+ &s->r, s);
+ NT_STATUS_HAVE_NO_MEMORY(ireq);
+
+ ireq->async.fn = verify_handler;
+ ireq->async.private_data= s;
+
+ talloc_steal(service, s);
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS wreplsrv_scavenging_run(struct wreplsrv_service *service)
+{
+ NTSTATUS status;
+ TALLOC_CTX *tmp_mem;
+ bool skip_first_run = false;
+
+ if (!timeval_expired(&service->scavenging.next_run)) {
+ return NT_STATUS_OK;
+ }
+
+ if (timeval_is_zero(&service->scavenging.next_run)) {
+ skip_first_run = true;
+ }
+
+ service->scavenging.next_run = timeval_current_ofs(service->config.scavenging_interval, 0);
+ status = wreplsrv_periodic_schedule(service, service->config.scavenging_interval);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ /*
+ * if it's the first time this functions is called (startup)
+ * the next_run is zero, in this case we should not do scavenging
+ */
+ if (skip_first_run) {
+ return NT_STATUS_OK;
+ }
+
+ if (service->scavenging.processing) {
+ return NT_STATUS_OK;
+ }
+
+ DEBUG(2,("wreplsrv_scavenging_run(): start\n"));
+
+ tmp_mem = talloc_new(service);
+ NT_STATUS_HAVE_NO_MEMORY(tmp_mem);
+ service->scavenging.processing = true;
+ status = wreplsrv_scavenging_owned_records(service,tmp_mem);
+ service->scavenging.processing = false;
+ talloc_free(tmp_mem);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ tmp_mem = talloc_new(service);
+ NT_STATUS_HAVE_NO_MEMORY(tmp_mem);
+ service->scavenging.processing = true;
+ status = wreplsrv_scavenging_replica_non_active_records(service, tmp_mem);
+ service->scavenging.processing = false;
+ talloc_free(tmp_mem);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ tmp_mem = talloc_new(service);
+ NT_STATUS_HAVE_NO_MEMORY(tmp_mem);
+ service->scavenging.processing = true;
+ status = wreplsrv_scavenging_replica_active_records(service, tmp_mem);
+ service->scavenging.processing = false;
+ talloc_free(tmp_mem);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ DEBUG(2,("wreplsrv_scavenging_run(): end\n"));
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/wrepl_server/wrepl_server.c b/source4/wrepl_server/wrepl_server.c
new file mode 100644
index 0000000000..c8316a5f4c
--- /dev/null
+++ b/source4/wrepl_server/wrepl_server.c
@@ -0,0 +1,512 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ WINS Replication server
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "../lib/util/dlinklist.h"
+#include "smbd/service_task.h"
+#include "smbd/service.h"
+#include "lib/messaging/irpc.h"
+#include "librpc/gen_ndr/winsrepl.h"
+#include "wrepl_server/wrepl_server.h"
+#include "nbt_server/wins/winsdb.h"
+#include "ldb/include/ldb.h"
+#include "ldb/include/ldb_errors.h"
+#include "auth/auth.h"
+#include "ldb_wrap.h"
+#include "param/param.h"
+#include "lib/socket/netif.h"
+
+static struct ldb_context *wins_config_db_connect(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx)
+{
+ return ldb_wrap_connect(mem_ctx, ev_ctx, lp_ctx, private_path(mem_ctx,
+ lp_ctx, lp_wins_config_url(lp_ctx)),
+ system_session(mem_ctx, lp_ctx), NULL, 0, NULL);
+}
+
+static uint64_t wins_config_db_get_seqnumber(struct ldb_context *ldb)
+{
+ int ret;
+ struct ldb_dn *dn;
+ struct ldb_result *res = NULL;
+ TALLOC_CTX *tmp_ctx = talloc_new(ldb);
+ uint64_t seqnumber = 0;
+
+ dn = ldb_dn_new(tmp_ctx, ldb, "@BASEINFO");
+ if (!dn) goto failed;
+
+ /* find the record in the WINS database */
+ ret = ldb_search(ldb, tmp_ctx, &res, dn, LDB_SCOPE_BASE, NULL, NULL);
+ if (ret != LDB_SUCCESS) goto failed;
+ if (res->count > 1) goto failed;
+
+ if (res->count == 1) {
+ seqnumber = ldb_msg_find_attr_as_uint64(res->msgs[0], "sequenceNumber", 0);
+ }
+
+failed:
+ talloc_free(tmp_ctx);
+ return seqnumber;
+}
+
+/*
+ open winsdb
+*/
+static NTSTATUS wreplsrv_open_winsdb(struct wreplsrv_service *service,
+ struct loadparm_context *lp_ctx)
+{
+ const char *owner = lp_parm_string(lp_ctx, NULL, "winsdb", "local_owner");
+
+ if (owner == NULL) {
+ struct interface *ifaces;
+ load_interfaces(service, lp_interfaces(lp_ctx), &ifaces);
+ owner = iface_n_ip(ifaces, 0);
+ }
+
+ service->wins_db = winsdb_connect(service, service->task->event_ctx, lp_ctx, owner, WINSDB_HANDLE_CALLER_WREPL);
+ if (!service->wins_db) {
+ return NT_STATUS_INTERNAL_DB_ERROR;
+ }
+
+ service->config.ldb = wins_config_db_connect(service, service->task->event_ctx, lp_ctx);
+ if (!service->config.ldb) {
+ return NT_STATUS_INTERNAL_DB_ERROR;
+ }
+
+ /* the default renew interval is 6 days */
+ service->config.renew_interval = lp_parm_int(lp_ctx, NULL,"wreplsrv","renew_interval", 6*24*60*60);
+
+ /* the default tombstone (extinction) interval is 6 days */
+ service->config.tombstone_interval= lp_parm_int(lp_ctx, NULL,"wreplsrv","tombstone_interval", 6*24*60*60);
+
+ /* the default tombstone (extinction) timeout is 1 day */
+ service->config.tombstone_timeout = lp_parm_int(lp_ctx, NULL,"wreplsrv","tombstone_timeout", 1*24*60*60);
+
+ /* the default tombstone extra timeout is 3 days */
+ service->config.tombstone_extra_timeout = lp_parm_int(lp_ctx, NULL,"wreplsrv","tombstone_extra_timeout", 3*24*60*60);
+
+ /* the default verify interval is 24 days */
+ service->config.verify_interval = lp_parm_int(lp_ctx, NULL,"wreplsrv","verify_interval", 24*24*60*60);
+
+ /* the default scavenging interval is 'renew_interval/2' */
+ service->config.scavenging_interval=lp_parm_int(lp_ctx, NULL,"wreplsrv","scavenging_interval",
+ service->config.renew_interval/2);
+
+ /* the maximun interval to the next periodic processing event */
+ service->config.periodic_interval = lp_parm_int(lp_ctx, NULL,"wreplsrv","periodic_interval", 15);
+
+ return NT_STATUS_OK;
+}
+
+struct wreplsrv_partner *wreplsrv_find_partner(struct wreplsrv_service *service, const char *peer_addr)
+{
+ struct wreplsrv_partner *cur;
+
+ for (cur = service->partners; cur; cur = cur->next) {
+ if (strcmp(cur->address, peer_addr) == 0) {
+ return cur;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ load our replication partners
+*/
+NTSTATUS wreplsrv_load_partners(struct wreplsrv_service *service)
+{
+ struct wreplsrv_partner *partner;
+ struct ldb_result *res = NULL;
+ int ret;
+ TALLOC_CTX *tmp_ctx;
+ int i;
+ uint64_t new_seqnumber;
+
+ new_seqnumber = wins_config_db_get_seqnumber(service->config.ldb);
+
+ /* if it's not the first run and nothing changed we're done */
+ if (service->config.seqnumber != 0 && service->config.seqnumber == new_seqnumber) {
+ return NT_STATUS_OK;
+ }
+
+ tmp_ctx = talloc_new(service);
+ NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
+
+ service->config.seqnumber = new_seqnumber;
+
+ /* find the record in the WINS database */
+ ret = ldb_search(service->config.ldb, tmp_ctx, &res,
+ ldb_dn_new(tmp_ctx, service->config.ldb, "CN=PARTNERS"),
+ LDB_SCOPE_SUBTREE, NULL, "(objectClass=wreplPartner)");
+ if (ret != LDB_SUCCESS) goto failed;
+
+ /* first disable all existing partners */
+ for (partner=service->partners; partner; partner = partner->next) {
+ partner->type = WINSREPL_PARTNER_NONE;
+ }
+
+ for (i=0; i < res->count; i++) {
+ const char *address;
+
+ address = ldb_msg_find_attr_as_string(res->msgs[i], "address", NULL);
+ if (!address) {
+ goto failed;
+ }
+
+ partner = wreplsrv_find_partner(service, address);
+ if (partner) {
+ if (partner->name != partner->address) {
+ talloc_free(discard_const(partner->name));
+ }
+ partner->name = NULL;
+ talloc_free(discard_const(partner->our_address));
+ partner->our_address = NULL;
+
+ /* force rescheduling of pulling */
+ partner->pull.next_run = timeval_zero();
+ } else {
+ partner = talloc_zero(service, struct wreplsrv_partner);
+ if (partner == NULL) goto failed;
+
+ partner->service = service;
+ partner->address = address;
+ talloc_steal(partner, partner->address);
+
+ DLIST_ADD_END(service->partners, partner, struct wreplsrv_partner *);
+ }
+
+ partner->name = ldb_msg_find_attr_as_string(res->msgs[i], "name", partner->address);
+ talloc_steal(partner, partner->name);
+ partner->our_address = ldb_msg_find_attr_as_string(res->msgs[i], "ourAddress", NULL);
+ talloc_steal(partner, partner->our_address);
+
+ partner->type = ldb_msg_find_attr_as_uint(res->msgs[i], "type", WINSREPL_PARTNER_BOTH);
+ partner->pull.interval = ldb_msg_find_attr_as_uint(res->msgs[i], "pullInterval",
+ WINSREPL_DEFAULT_PULL_INTERVAL);
+ partner->pull.retry_interval = ldb_msg_find_attr_as_uint(res->msgs[i], "pullRetryInterval",
+ WINSREPL_DEFAULT_PULL_RETRY_INTERVAL);
+ partner->push.change_count = ldb_msg_find_attr_as_uint(res->msgs[i], "pushChangeCount",
+ WINSREPL_DEFAULT_PUSH_CHANGE_COUNT);
+ partner->push.use_inform = ldb_msg_find_attr_as_uint(res->msgs[i], "pushUseInform", true);
+
+ DEBUG(3,("wreplsrv_load_partners: found partner: %s type: 0x%X\n",
+ partner->address, partner->type));
+ }
+
+ DEBUG(2,("wreplsrv_load_partners: %u partners found: wins_config_db seqnumber %llu\n",
+ res->count, (unsigned long long)service->config.seqnumber));
+
+ talloc_free(tmp_ctx);
+ return NT_STATUS_OK;
+failed:
+ talloc_free(tmp_ctx);
+ return NT_STATUS_FOOBAR;
+}
+
+NTSTATUS wreplsrv_fill_wrepl_table(struct wreplsrv_service *service,
+ TALLOC_CTX *mem_ctx,
+ struct wrepl_table *table_out,
+ const char *initiator,
+ bool full_table)
+{
+ struct wreplsrv_owner *cur;
+ uint32_t i = 0;
+
+ table_out->partner_count = 0;
+ table_out->partners = NULL;
+ table_out->initiator = initiator;
+
+ for (cur = service->table; cur; cur = cur->next) {
+ if (full_table) {
+ table_out->partner_count++;
+ continue;
+ }
+
+ if (strcmp(initiator, cur->owner.address) != 0) continue;
+
+ table_out->partner_count++;
+ break;
+ }
+
+ table_out->partners = talloc_array(mem_ctx, struct wrepl_wins_owner, table_out->partner_count);
+ NT_STATUS_HAVE_NO_MEMORY(table_out->partners);
+
+ for (cur = service->table; cur && i < table_out->partner_count; cur = cur->next) {
+ /*
+ * if it's our local entry
+ * update the max version
+ */
+ if (cur == service->owner) {
+ cur->owner.max_version = winsdb_get_maxVersion(service->wins_db);
+ }
+
+ if (full_table) {
+ table_out->partners[i] = cur->owner;
+ i++;
+ continue;
+ }
+
+ if (strcmp(initiator, cur->owner.address) != 0) continue;
+
+ table_out->partners[i] = cur->owner;
+ i++;
+ break;
+ }
+
+ return NT_STATUS_OK;
+}
+
+struct wreplsrv_owner *wreplsrv_find_owner(struct wreplsrv_service *service,
+ struct wreplsrv_owner *table,
+ const char *wins_owner)
+{
+ struct wreplsrv_owner *cur;
+
+ for (cur = table; cur; cur = cur->next) {
+ if (strcmp(cur->owner.address, wins_owner) == 0) {
+ /*
+ * if it's our local entry
+ * update the max version
+ */
+ if (cur == service->owner) {
+ cur->owner.max_version = winsdb_get_maxVersion(service->wins_db);
+ }
+ return cur;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ update the wins_owner_table max_version, if the given version is the highest version
+ if no entry for the wins_owner exists yet, create one
+*/
+NTSTATUS wreplsrv_add_table(struct wreplsrv_service *service,
+ TALLOC_CTX *mem_ctx, struct wreplsrv_owner **_table,
+ const char *wins_owner, uint64_t version)
+{
+ struct wreplsrv_owner *table = *_table;
+ struct wreplsrv_owner *cur;
+
+ if (!wins_owner || strcmp(wins_owner, "0.0.0.0") == 0) {
+ wins_owner = service->wins_db->local_owner;
+ }
+
+ cur = wreplsrv_find_owner(service, table, wins_owner);
+
+ /* if it doesn't exists yet, create one */
+ if (!cur) {
+ cur = talloc_zero(mem_ctx, struct wreplsrv_owner);
+ NT_STATUS_HAVE_NO_MEMORY(cur);
+
+ cur->owner.address = talloc_strdup(cur, wins_owner);
+ NT_STATUS_HAVE_NO_MEMORY(cur->owner.address);
+ cur->owner.min_version = 0;
+ cur->owner.max_version = 0;
+ cur->owner.type = 1; /* don't know why this is always 1 */
+
+ cur->partner = wreplsrv_find_partner(service, wins_owner);
+
+ DLIST_ADD_END(table, cur, struct wreplsrv_owner *);
+ *_table = table;
+ }
+
+ /* the min_version is always 0 here, and won't be updated */
+
+ /* if the given version is higher than the current max_version, update */
+ if (cur->owner.max_version < version) {
+ cur->owner.max_version = version;
+ /* if it's for our local db, we need to update the wins.ldb too */
+ if (cur == service->owner) {
+ uint64_t ret;
+ ret = winsdb_set_maxVersion(service->wins_db, cur->owner.max_version);
+ if (ret != cur->owner.max_version) {
+ DEBUG(0,("winsdb_set_maxVersion(%llu) failed: %llu\n",
+ (unsigned long long)cur->owner.max_version,
+ (unsigned long long)ret));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ load the partner table
+*/
+static NTSTATUS wreplsrv_load_table(struct wreplsrv_service *service)
+{
+ struct ldb_result *res = NULL;
+ int ret;
+ NTSTATUS status;
+ TALLOC_CTX *tmp_ctx = talloc_new(service);
+ struct ldb_context *ldb = service->wins_db->ldb;
+ int i;
+ struct wreplsrv_owner *local_owner;
+ const char *wins_owner;
+ uint64_t version;
+ const char * const attrs[] = {
+ "winsOwner",
+ "versionID",
+ NULL
+ };
+
+ /*
+ * make sure we have our local entry in the list,
+ * but we set service->owner when we're done
+ * to avoid to many calls to wreplsrv_local_max_version()
+ */
+ status = wreplsrv_add_table(service,
+ service, &service->table,
+ service->wins_db->local_owner, 0);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+ local_owner = wreplsrv_find_owner(service, service->table, service->wins_db->local_owner);
+ if (!local_owner) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ goto failed;
+ }
+
+ /* find the record in the WINS database */
+ ret = ldb_search(ldb, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE,
+ attrs, "(objectClass=winsRecord)");
+ status = NT_STATUS_INTERNAL_DB_CORRUPTION;
+ if (ret != LDB_SUCCESS) goto failed;
+
+ for (i=0; i < res->count; i++) {
+ wins_owner = ldb_msg_find_attr_as_string(res->msgs[i], "winsOwner", NULL);
+ version = ldb_msg_find_attr_as_uint64(res->msgs[i], "versionID", 0);
+
+ status = wreplsrv_add_table(service,
+ service, &service->table,
+ wins_owner, version);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+ talloc_free(res->msgs[i]);
+ }
+
+ /*
+ * this makes sure we call wreplsrv_local_max_version() before returning in
+ * wreplsrv_find_owner()
+ */
+ service->owner = local_owner;
+
+ /*
+ * this makes sure the maxVersion in the database is updated,
+ * with the highest version we found, if this is higher than the current stored one
+ */
+ status = wreplsrv_add_table(service,
+ service, &service->table,
+ service->wins_db->local_owner, local_owner->owner.max_version);
+ if (!NT_STATUS_IS_OK(status)) goto failed;
+
+ talloc_free(tmp_ctx);
+ return NT_STATUS_OK;
+failed:
+ talloc_free(tmp_ctx);
+ return status;
+}
+
+/*
+ setup our replication partners
+*/
+static NTSTATUS wreplsrv_setup_partners(struct wreplsrv_service *service)
+{
+ NTSTATUS status;
+
+ status = wreplsrv_load_partners(service);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ status = wreplsrv_load_table(service);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ startup the wrepl task
+*/
+static void wreplsrv_task_init(struct task_server *task)
+{
+ NTSTATUS status;
+ struct wreplsrv_service *service;
+
+ if (!lp_wins_support(task->lp_ctx)) {
+ return;
+ }
+
+ task_server_set_title(task, "task[wreplsrv]");
+
+ service = talloc_zero(task, struct wreplsrv_service);
+ if (!service) {
+ task_server_terminate(task, "wreplsrv_task_init: out of memory");
+ return;
+ }
+ service->task = task;
+ service->startup_time = timeval_current();
+ task->private_data = service;
+
+ /*
+ * setup up all partners, and open the winsdb
+ */
+ status = wreplsrv_open_winsdb(service, task->lp_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ task_server_terminate(task, "wreplsrv_task_init: wreplsrv_open_winsdb() failed");
+ return;
+ }
+
+ /*
+ * setup timed events for each partner we want to pull from
+ */
+ status = wreplsrv_setup_partners(service);
+ if (!NT_STATUS_IS_OK(status)) {
+ task_server_terminate(task, "wreplsrv_task_init: wreplsrv_setup_partners() failed");
+ return;
+ }
+
+ /*
+ * setup listen sockets, so we can anwser requests from our partners,
+ * which pull from us
+ */
+ status = wreplsrv_setup_sockets(service, task->lp_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ task_server_terminate(task, "wreplsrv_task_init: wreplsrv_setup_sockets() failed");
+ return;
+ }
+
+ status = wreplsrv_setup_periodic(service);
+ if (!NT_STATUS_IS_OK(status)) {
+ task_server_terminate(task, "wreplsrv_task_init: wreplsrv_setup_periodic() failed");
+ return;
+ }
+
+ irpc_add_name(task->msg_ctx, "wrepl_server");
+}
+
+/*
+ register ourselves as a available server
+*/
+NTSTATUS server_service_wrepl_init(void)
+{
+ return register_server_service("wrepl", wreplsrv_task_init);
+}
diff --git a/source4/wrepl_server/wrepl_server.h b/source4/wrepl_server/wrepl_server.h
new file mode 100644
index 0000000000..d92e524c35
--- /dev/null
+++ b/source4/wrepl_server/wrepl_server.h
@@ -0,0 +1,314 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ WINS Replication server
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+struct wreplsrv_service;
+struct wreplsrv_in_connection;
+struct wreplsrv_out_connection;
+struct wreplsrv_partner;
+
+#define WREPLSRV_VALID_ASSOC_CTX 0x12345678
+#define WREPLSRV_INVALID_ASSOC_CTX 0x0000000a
+
+/*
+ state of an incoming wrepl call
+*/
+struct wreplsrv_in_call {
+ struct wreplsrv_in_connection *wreplconn;
+ struct wrepl_packet req_packet;
+ struct wrepl_packet rep_packet;
+ bool terminate_after_send;
+};
+
+/*
+ state of an incoming wrepl connection
+*/
+struct wreplsrv_in_connection {
+ struct wreplsrv_in_connection *prev,*next;
+ struct stream_connection *conn;
+ struct packet_context *packet;
+
+ /* our global service context */
+ struct wreplsrv_service *service;
+
+ /*
+ * the partner that connects us,
+ * can be NULL, when we got a connection
+ * from an unknown address
+ */
+ struct wreplsrv_partner *partner;
+
+ /* keep track of the assoc_ctx's */
+ struct {
+ bool stopped;
+ uint32_t our_ctx;
+ uint32_t peer_ctx;
+ } assoc_ctx;
+};
+
+/*
+ state of an outgoing wrepl connection
+*/
+struct wreplsrv_out_connection {
+ /* our global service context */
+ struct wreplsrv_service *service;
+
+ /*
+ * the partner we connect
+ */
+ struct wreplsrv_partner *partner;
+
+ /* keep track of the assoc_ctx's */
+ struct {
+ uint32_t our_ctx;
+ uint32_t peer_ctx;
+ uint16_t peer_major;
+ } assoc_ctx;
+
+ /*
+ * the client socket to the partner,
+ * NULL if not yet connected
+ */
+ struct wrepl_socket *sock;
+};
+
+enum winsrepl_partner_type {
+ WINSREPL_PARTNER_NONE = 0x0,
+ WINSREPL_PARTNER_PULL = 0x1,
+ WINSREPL_PARTNER_PUSH = 0x2,
+ WINSREPL_PARTNER_BOTH = (WINSREPL_PARTNER_PULL | WINSREPL_PARTNER_PUSH)
+};
+
+#define WINSREPL_DEFAULT_PULL_INTERVAL (30*60)
+#define WINSREPL_DEFAULT_PULL_RETRY_INTERVAL (30)
+
+#define WINSREPL_DEFAULT_PUSH_CHANGE_COUNT (0)
+
+/*
+ this represents one of our configured partners
+*/
+struct wreplsrv_partner {
+ struct wreplsrv_partner *prev,*next;
+
+ /* our global service context */
+ struct wreplsrv_service *service;
+
+ /* the netbios name of the partner, mostly just for debugging */
+ const char *name;
+
+ /* the ip-address of the partner */
+ const char *address;
+
+ /*
+ * as wins partners identified by ip-address, we need to use a specific source-ip
+ * when we want to connect to the partner
+ */
+ const char *our_address;
+
+ /* the type of the partner, pull, push or both */
+ enum winsrepl_partner_type type;
+
+ /* pull specific options */
+ struct {
+ /* the interval between 2 pull replications to the partner */
+ uint32_t interval;
+
+ /* the retry_interval if a pull cycle failed to the partner */
+ uint32_t retry_interval;
+
+ /* the error count till the last success */
+ uint32_t error_count;
+
+ /* the status of the last pull cycle */
+ NTSTATUS last_status;
+
+ /* the timestamp of the next pull try */
+ struct timeval next_run;
+
+ /* this is a list of each wins_owner the partner knows about */
+ struct wreplsrv_owner *table;
+
+ /* the outgoing connection to the partner */
+ struct wreplsrv_out_connection *wreplconn;
+
+ /* the current pending pull cycle request */
+ struct composite_context *creq;
+
+ /* the pull cycle io params */
+ struct wreplsrv_pull_cycle_io *cycle_io;
+
+ /* the current timed_event to the next pull cycle */
+ struct tevent_timer *te;
+ } pull;
+
+ /* push specific options */
+ struct {
+ /* change count till push notification */
+ uint32_t change_count;
+
+ /* the last wins db maxVersion have reported to the partner */
+ uint64_t maxVersionID;
+
+ /* we should use WREPL_REPL_INFORM* messages to this partner */
+ bool use_inform;
+
+ /* the error count till the last success */
+ uint32_t error_count;
+
+ /* the status of the last push cycle */
+ NTSTATUS last_status;
+
+ /* the outgoing connection to the partner */
+ struct wreplsrv_out_connection *wreplconn;
+
+ /* the current push notification */
+ struct composite_context *creq;
+
+ /* the pull cycle io params */
+ struct wreplsrv_push_notify_io *notify_io;
+ } push;
+};
+
+struct wreplsrv_owner {
+ struct wreplsrv_owner *prev,*next;
+
+ /* this hold the owner_id (address), min_version, max_version and partner_type */
+ struct wrepl_wins_owner owner;
+
+ /* can be NULL if this owner isn't a configure partner */
+ struct wreplsrv_partner *partner;
+};
+
+/*
+ state of the whole wrepl service
+*/
+struct wreplsrv_service {
+ /* the whole wrepl service is in one task */
+ struct task_server *task;
+
+ /* the time the service was started */
+ struct timeval startup_time;
+
+ /* the winsdb handle */
+ struct winsdb_handle *wins_db;
+
+ /* some configuration */
+ struct {
+ /* the wins config db handle */
+ struct ldb_context *ldb;
+
+ /* the last wins config db seqnumber we know about */
+ uint64_t seqnumber;
+
+ /*
+ * the interval (in secs) till an active record will be marked as RELEASED
+ */
+ uint32_t renew_interval;
+
+ /*
+ * the interval (in secs) a record remains in RELEASED state,
+ * before it will be marked as TOMBSTONE
+ * (also known as extinction interval)
+ */
+ uint32_t tombstone_interval;
+
+ /*
+ * the interval (in secs) a record remains in TOMBSTONE state,
+ * before it will be removed from the database.
+ * See also 'tombstone_extra_timeout'.
+ * (also known as extinction timeout)
+ */
+ uint32_t tombstone_timeout;
+
+ /*
+ * the interval (in secs) a record remains in TOMBSTONE state,
+ * even after 'tombstone_timeout' passes the current timestamp.
+ * this is the minimum uptime of the wrepl service, before
+ * we start delete tombstones. This is to prevent deletion of
+ * tombstones, without replacte them.
+ */
+ uint32_t tombstone_extra_timeout;
+
+ /*
+ * the interval (in secs) till a replica record will be verified
+ * with the owning wins server
+ */
+ uint32_t verify_interval;
+
+ /*
+ * the interval (in secs) till a do a database cleanup
+ */
+ uint32_t scavenging_interval;
+
+ /*
+ * the interval (in secs) to the next periodic processing
+ * (this is the maximun interval)
+ */
+ uint32_t periodic_interval;
+ } config;
+
+ /* all incoming connections */
+ struct wreplsrv_in_connection *in_connections;
+
+ /* all partners (pull and push) */
+ struct wreplsrv_partner *partners;
+
+ /*
+ * this is our local wins_owner entry, this is also in the table list
+ * but we need a pointer to it, because we need to update it on each
+ * query to wreplsrv_find_owner(), as the local records can be added
+ * to the wins.ldb from external tools and the winsserver
+ */
+ struct wreplsrv_owner *owner;
+
+ /* this is a list of each wins_owner we know about in our database */
+ struct wreplsrv_owner *table;
+
+ /* some stuff for periodic processing */
+ struct {
+ /*
+ * the timestamp for the next event,
+ * this is the timstamp passed to event_add_timed()
+ */
+ struct timeval next_event;
+
+ /* here we have a reference to the timed event the schedules the periodic stuff */
+ struct tevent_timer *te;
+ } periodic;
+
+ /* some stuff for scavenging processing */
+ struct {
+ /*
+ * the timestamp for the next scavenging run,
+ * this is the timstamp passed to event_add_timed()
+ */
+ struct timeval next_run;
+
+ /*
+ * are we currently inside a scavenging run
+ */
+ bool processing;
+ } scavenging;
+};
+
+struct socket_context;
+struct wrepl_name;
+#include "wrepl_server/wrepl_out_helpers.h"
+#include "wrepl_server/wrepl_server_proto.h"